summaryrefslogtreecommitdiffstats
path: root/noatun
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commite2de64d6f1beb9e492daf5b886e19933c1fa41dd (patch)
tree9047cf9e6b5c43878d5bf82660adae77ceee097a /noatun
downloadtdemultimedia-e2de64d6f1beb9e492daf5b886e19933c1fa41dd.tar.gz
tdemultimedia-e2de64d6f1beb9e492daf5b886e19933c1fa41dd.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdemultimedia@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'noatun')
-rw-r--r--noatun/CHANGES4
-rw-r--r--noatun/COPYING26
-rw-r--r--noatun/FILES13
-rw-r--r--noatun/Makefile.am29
-rw-r--r--noatun/TODO15
-rw-r--r--noatun/app/Makefile.am11
-rw-r--r--noatun/app/main.cpp47
-rw-r--r--noatun/configure.in.in15
-rw-r--r--noatun/hi128-app-noatun.pngbin0 -> 17465 bytes
-rw-r--r--noatun/hi16-app-noatun.pngbin0 -> 964 bytes
-rw-r--r--noatun/hi22-app-noatun.pngbin0 -> 1633 bytes
-rw-r--r--noatun/hi32-app-noatun.pngbin0 -> 2767 bytes
-rw-r--r--noatun/hi48-app-noatun.pngbin0 -> 5527 bytes
-rw-r--r--noatun/hi64-app-noatun.pngbin0 -> 8005 bytes
-rw-r--r--noatun/library/Makefile.am57
-rw-r--r--noatun/library/app.cpp530
-rw-r--r--noatun/library/cmodule.cpp131
-rw-r--r--noatun/library/cmodule.h43
-rw-r--r--noatun/library/controls.cpp114
-rw-r--r--noatun/library/conversion.cpp149
-rw-r--r--noatun/library/downloader.cpp236
-rw-r--r--noatun/library/effects.cpp285
-rw-r--r--noatun/library/effectview.cpp284
-rw-r--r--noatun/library/effectview.h83
-rw-r--r--noatun/library/engine.cpp589
-rw-r--r--noatun/library/equalizer.cpp339
-rw-r--r--noatun/library/equalizerview.cpp325
-rw-r--r--noatun/library/equalizerview.h91
-rw-r--r--noatun/library/equalizerwidget.ui337
-rw-r--r--noatun/library/gentable.cpp1037
-rw-r--r--noatun/library/globalvideo.h26
-rw-r--r--noatun/library/ksaver.cpp184
-rw-r--r--noatun/library/ksaver.h102
-rw-r--r--noatun/library/mimetypetree.cpp64
-rw-r--r--noatun/library/mimetypetree.h35
-rw-r--r--noatun/library/noatun/Makefile.am10
-rw-r--r--noatun/library/noatun/app.h271
-rw-r--r--noatun/library/noatun/controls.h79
-rw-r--r--noatun/library/noatun/conversion.h117
-rw-r--r--noatun/library/noatun/downloader.h120
-rw-r--r--noatun/library/noatun/effects.h186
-rw-r--r--noatun/library/noatun/engine.h120
-rw-r--r--noatun/library/noatun/equalizer.h238
-rw-r--r--noatun/library/noatun/mocs.cpp37
-rw-r--r--noatun/library/noatun/player.h270
-rw-r--r--noatun/library/noatun/playlist.h531
-rw-r--r--noatun/library/noatun/playlistsaver.h102
-rw-r--r--noatun/library/noatun/plugin.h467
-rw-r--r--noatun/library/noatun/pluginloader.h99
-rw-r--r--noatun/library/noatun/pref.h91
-rw-r--r--noatun/library/noatun/scrollinglabel.h78
-rw-r--r--noatun/library/noatun/stdaction.h204
-rw-r--r--noatun/library/noatun/stereobuttonaction.h31
-rw-r--r--noatun/library/noatun/vequalizer.h455
-rw-r--r--noatun/library/noatun/video.h60
-rw-r--r--noatun/library/noatunarts/Equalizer.mcopclass4
-rw-r--r--noatun/library/noatunarts/EqualizerSSE.mcopclass4
-rw-r--r--noatun/library/noatunarts/Equalizer_impl.cpp472
-rw-r--r--noatun/library/noatunarts/FFTScope.mcopclass4
-rw-r--r--noatun/library/noatunarts/FFTScopeStereo.mcopclass4
-rw-r--r--noatun/library/noatunarts/FFTScopes.cpp422
-rw-r--r--noatun/library/noatunarts/Listener.mcopclass4
-rw-r--r--noatun/library/noatunarts/Makefile.am33
-rw-r--r--noatun/library/noatunarts/RawScope.mcopclass4
-rw-r--r--noatun/library/noatunarts/RawScopeStereo.mcopclass4
-rw-r--r--noatun/library/noatunarts/Session.mcopclass4
-rw-r--r--noatun/library/noatunarts/Session_impl.cpp82
-rw-r--r--noatun/library/noatunarts/StereoEffectStack.mcopclass4
-rw-r--r--noatun/library/noatunarts/StereoEffectStack_impl.cpp253
-rw-r--r--noatun/library/noatunarts/StereoVolumeControl.mcopclass4
-rw-r--r--noatun/library/noatunarts/StereoVolumeControlSSE.mcopclass4
-rw-r--r--noatun/library/noatunarts/StereoVolumeControl_impl.cpp181
-rw-r--r--noatun/library/noatunarts/fft.c384
-rw-r--r--noatun/library/noatunarts/fft.h88
-rw-r--r--noatun/library/noatunarts/noatunarts.idl91
-rw-r--r--noatun/library/noatunlistview.h5
-rw-r--r--noatun/library/noatunstdaction.cpp362
-rw-r--r--noatun/library/noatuntags/Makefile.am14
-rw-r--r--noatun/library/noatuntags/tags.cpp229
-rw-r--r--noatun/library/noatuntags/tags.h35
-rw-r--r--noatun/library/noatuntags/tagsgetter.h56
-rw-r--r--noatun/library/noatunui.cpp12
-rw-r--r--noatun/library/player.cpp379
-rw-r--r--noatun/library/playlist.cpp442
-rw-r--r--noatun/library/playlistsaver.cpp771
-rw-r--r--noatun/library/plugin.cpp578
-rw-r--r--noatun/library/plugin_deps.h44
-rw-r--r--noatun/library/pluginloader.cpp366
-rw-r--r--noatun/library/pluginmodule.cpp406
-rw-r--r--noatun/library/pluginmodule.h100
-rw-r--r--noatun/library/pref.cpp95
-rw-r--r--noatun/library/scrollinglabel.cpp196
-rw-r--r--noatun/library/spline.cpp194
-rw-r--r--noatun/library/spline.h74
-rw-r--r--noatun/library/stereobuttonaction.cpp47
-rw-r--r--noatun/library/titleproxy.cpp376
-rw-r--r--noatun/library/titleproxy.h129
-rw-r--r--noatun/library/vequalizer.cpp936
-rw-r--r--noatun/library/video.cpp162
-rw-r--r--noatun/modules/Makefile.am4
-rw-r--r--noatun/modules/artseffects/ExtraStereo.mcopclass5
-rw-r--r--noatun/modules/artseffects/ExtraStereoGuiFactory.mcopclass5
-rw-r--r--noatun/modules/artseffects/Makefile.am25
-rw-r--r--noatun/modules/artseffects/RawWriter.mcopclass4
-rw-r--r--noatun/modules/artseffects/VoiceRemoval.mcopclass5
-rw-r--r--noatun/modules/artseffects/artseffects.idl17
-rw-r--r--noatun/modules/artseffects/effect.cpp1
-rw-r--r--noatun/modules/artseffects/extrastereo_impl.cc153
-rw-r--r--noatun/modules/artseffects/extrastereogui_impl.cc28
-rw-r--r--noatun/modules/artseffects/extrastereogui_impl.h26
-rw-r--r--noatun/modules/dcopiface/Makefile.am16
-rw-r--r--noatun/modules/dcopiface/dcopiface.cpp250
-rw-r--r--noatun/modules/dcopiface/dcopiface.h104
-rw-r--r--noatun/modules/dcopiface/dcopiface.plugin137
-rw-r--r--noatun/modules/excellent/Makefile.am18
-rw-r--r--noatun/modules/excellent/excellent.plugin120
-rw-r--r--noatun/modules/excellent/excellentui.rc45
-rw-r--r--noatun/modules/excellent/noatunui.cpp32
-rw-r--r--noatun/modules/excellent/userinterface.cpp394
-rw-r--r--noatun/modules/excellent/userinterface.h101
-rw-r--r--noatun/modules/htmlexport/Makefile.am16
-rw-r--r--noatun/modules/htmlexport/TODO3
-rw-r--r--noatun/modules/htmlexport/htmlexport.cpp308
-rw-r--r--noatun/modules/htmlexport/htmlexport.h89
-rw-r--r--noatun/modules/htmlexport/htmlexport.plugin124
-rw-r--r--noatun/modules/infrared/Makefile.am16
-rw-r--r--noatun/modules/infrared/README3
-rw-r--r--noatun/modules/infrared/infrared.cpp120
-rw-r--r--noatun/modules/infrared/infrared.h30
-rw-r--r--noatun/modules/infrared/infrared.plugin120
-rw-r--r--noatun/modules/infrared/irprefs.cpp311
-rw-r--r--noatun/modules/infrared/irprefs.h62
-rw-r--r--noatun/modules/infrared/lirc.cpp173
-rw-r--r--noatun/modules/infrared/lirc.h75
-rw-r--r--noatun/modules/kaiman/Makefile.am23
-rw-r--r--noatun/modules/kaiman/README3
-rw-r--r--noatun/modules/kaiman/SKIN-SPECS518
-rw-r--r--noatun/modules/kaiman/kaiman.plugin132
-rw-r--r--noatun/modules/kaiman/kaimanui.rc45
-rw-r--r--noatun/modules/kaiman/noatunui.cpp9
-rw-r--r--noatun/modules/kaiman/pref.cpp122
-rw-r--r--noatun/modules/kaiman/pref.h48
-rw-r--r--noatun/modules/kaiman/skins/Makefile.am3
-rw-r--r--noatun/modules/kaiman/skins/car-preset/Makefile.am10
-rw-r--r--noatun/modules/kaiman/skins/car-preset/README22
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_exit.pngbin0 -> 674 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_iconify.pngbin0 -> 585 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_list.pngbin0 -> 762 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_next.pngbin0 -> 1055 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_p1.pngbin0 -> 691 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_p2.pngbin0 -> 679 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_p3.pngbin0 -> 689 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_p4.pngbin0 -> 641 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_p5.pngbin0 -> 668 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_p6.pngbin0 -> 741 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_play.pngbin0 -> 623 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_prev.pngbin0 -> 1006 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_sml.pngbin0 -> 454 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_stop.pngbin0 -> 603 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_voldn.pngbin0 -> 923 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/btn_volup.pngbin0 -> 1026 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/digbig.pngbin0 -> 359 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/digmed.pngbin0 -> 318 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/letters.pngbin0 -> 1240 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/main.pngbin0 -> 7191 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/monoster.pngbin0 -> 169 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/posbar.pngbin0 -> 519 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/random.pngbin0 -> 178 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/repeat.pngbin0 -> 172 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/skindata71
-rw-r--r--noatun/modules/kaiman/skins/car-preset/status.pngbin0 -> 133 bytes
-rw-r--r--noatun/modules/kaiman/skins/car-preset/volume.pngbin0 -> 664 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/Makefile.am13
-rw-r--r--noatun/modules/kaiman/skins/circle/README22
-rw-r--r--noatun/modules/kaiman/skins/circle/back.pngbin0 -> 20249 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/back_mask.pngbin0 -> 1519 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/back_sm.pngbin0 -> 2166 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/back_sm_mask.pngbin0 -> 217 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/bar_pos.pngbin0 -> 1188 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/bar_vol.pngbin0 -> 1156 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_exit.pngbin0 -> 2326 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_iconify.pngbin0 -> 1899 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_list.pngbin0 -> 2525 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_mode.pngbin0 -> 2026 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_next.pngbin0 -> 2527 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_play.pngbin0 -> 3081 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_pref.pngbin0 -> 2610 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_prev.pngbin0 -> 2411 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_repeat.pngbin0 -> 2488 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_shuffle.pngbin0 -> 2504 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_sm_exit.pngbin0 -> 680 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_sm_iconify.pngbin0 -> 627 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_sm_mode.pngbin0 -> 852 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_sm_next.pngbin0 -> 759 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_sm_play.pngbin0 -> 1004 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_sm_prev.pngbin0 -> 776 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_sm_stop.pngbin0 -> 1022 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/btn_stop.pngbin0 -> 3186 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/dig.pngbin0 -> 370 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/digsml.pngbin0 -> 201 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/letters.pngbin0 -> 1260 bytes
-rw-r--r--noatun/modules/kaiman/skins/circle/skindata58
-rw-r--r--noatun/modules/kaiman/skins/circle/skindata_alt44
-rw-r--r--noatun/modules/kaiman/skins/circle/status.pngbin0 -> 121 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/Makefile.am11
-rw-r--r--noatun/modules/kaiman/skins/k9/README24
-rw-r--r--noatun/modules/kaiman/skins/k9/conf.jpgbin0 -> 771 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/conf.pngbin0 -> 2436 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/eject.jpgbin0 -> 6065 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/icon.jpgbin0 -> 569 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/icon.pngbin0 -> 1427 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/kill.jpgbin0 -> 536 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/kill.pngbin0 -> 1221 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/knine-nfont.jpgbin0 -> 1747 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/knine-nfont.pngbin0 -> 1226 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/knine-nfont2.jpgbin0 -> 1721 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/knine-nfont2.pngbin0 -> 1320 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/knine-normal2.jpgbin0 -> 17471 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/knine-normal2.pngbin0 -> 72057 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/knine-vfont.jpgbin0 -> 730 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/knine-vfont.pngbin0 -> 351 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/long2.jpgbin0 -> 146555 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/mask.pngbin0 -> 3865 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/newtext.jpgbin0 -> 3196 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/newtext.pngbin0 -> 3135 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/next.jpgbin0 -> 6840 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/pause.jpgbin0 -> 7683 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/play.jpgbin0 -> 8850 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/pos_item.jpgbin0 -> 166210 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/repeat.jpgbin0 -> 756 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/repeat.pngbin0 -> 1299 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/reverse.jpgbin0 -> 6904 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/shuffle.jpgbin0 -> 719 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/shuffle.pngbin0 -> 1268 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/skindata73
-rw-r--r--noatun/modules/kaiman/skins/k9/small-k.jpgbin0 -> 516 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/small-k.pngbin0 -> 1009 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/square.jpgbin0 -> 720 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/square.pngbin0 -> 1894 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/status.jpgbin0 -> 461 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/status.pngbin0 -> 246 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/stop.jpgbin0 -> 7743 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/trans-pos.pngbin0 -> 99 bytes
-rw-r--r--noatun/modules/kaiman/skins/k9/trans-slide.pngbin0 -> 105 bytes
-rw-r--r--noatun/modules/kaiman/style.cpp1504
-rw-r--r--noatun/modules/kaiman/style.h356
-rw-r--r--noatun/modules/kaiman/userinterface.cpp562
-rw-r--r--noatun/modules/kaiman/userinterface.h85
-rw-r--r--noatun/modules/keyz/Makefile.am14
-rw-r--r--noatun/modules/keyz/keyz.cpp189
-rw-r--r--noatun/modules/keyz/keyz.h48
-rw-r--r--noatun/modules/keyz/keyz.plugin69
-rw-r--r--noatun/modules/kjofol-skin/ChangeLog111
-rw-r--r--noatun/modules/kjofol-skin/Makefile.am41
-rw-r--r--noatun/modules/kjofol-skin/helpers.cpp64
-rw-r--r--noatun/modules/kjofol-skin/kjbackground.cpp29
-rw-r--r--noatun/modules/kjofol-skin/kjbackground.h21
-rw-r--r--noatun/modules/kjofol-skin/kjbutton.cpp301
-rw-r--r--noatun/modules/kjofol-skin/kjbutton.h34
-rw-r--r--noatun/modules/kjofol-skin/kjequalizer.cpp129
-rw-r--r--noatun/modules/kjofol-skin/kjequalizer.h39
-rw-r--r--noatun/modules/kjofol-skin/kjfont.cpp290
-rw-r--r--noatun/modules/kjofol-skin/kjfont.h50
-rw-r--r--noatun/modules/kjofol-skin/kjguisettingswidget.ui465
-rw-r--r--noatun/modules/kjofol-skin/kjloader.cpp832
-rw-r--r--noatun/modules/kjofol-skin/kjloader.h129
-rw-r--r--noatun/modules/kjofol-skin/kjofolui.plugin64
-rw-r--r--noatun/modules/kjofol-skin/kjprefs.cpp658
-rw-r--r--noatun/modules/kjofol-skin/kjprefs.h96
-rw-r--r--noatun/modules/kjofol-skin/kjseeker.cpp210
-rw-r--r--noatun/modules/kjofol-skin/kjseeker.h37
-rw-r--r--noatun/modules/kjofol-skin/kjskinselectorwidget.ui227
-rw-r--r--noatun/modules/kjofol-skin/kjsliders.cpp336
-rw-r--r--noatun/modules/kjofol-skin/kjsliders.h88
-rw-r--r--noatun/modules/kjofol-skin/kjtextdisplay.cpp650
-rw-r--r--noatun/modules/kjofol-skin/kjtextdisplay.h139
-rw-r--r--noatun/modules/kjofol-skin/kjvis.cpp538
-rw-r--r--noatun/modules/kjofol-skin/kjvis.h102
-rw-r--r--noatun/modules/kjofol-skin/kjwidget.cpp70
-rw-r--r--noatun/modules/kjofol-skin/kjwidget.h53
-rw-r--r--noatun/modules/kjofol-skin/noatunui.cpp9
-rw-r--r--noatun/modules/kjofol-skin/parser.cpp132
-rw-r--r--noatun/modules/kjofol-skin/parser.h49
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/HexoBronx.rc77
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/Makefile.am7
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/README.txt79
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/active.pngbin0 -> 113726 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/eckig_font.pngbin0 -> 272 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/font.pngbin0 -> 1299 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/inactive.pngbin0 -> 109729 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/mask.pngbin0 -> 3416 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/pitch.pngbin0 -> 22516 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/splash.pngbin0 -> 94261 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/time_font.pngbin0 -> 395 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/volume.pngbin0 -> 24291 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/HexoBronx/volume_pitch_font.pngbin0 -> 355 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/Makefile.am3
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/Makefile.am11
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/kjofol.dck62
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/kjofol.pl39
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/kjofol.rc150
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/kjofol.wsh63
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sg.pngbin0 -> 29510 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sg_num.pngbin0 -> 1497 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sg_seek.bmpbin0 -> 218334 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sg_seek.pngbin0 -> 8170 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sg_text.pngbin0 -> 2093 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgdock.pngbin0 -> 5552 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgdock2.pngbin0 -> 5455 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgdocksk.pngbin0 -> 1236 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgdockvp.pngbin0 -> 1234 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgeq.pngbin0 -> 1236 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgpitch.pngbin0 -> 7552 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgpitchp.pngbin0 -> 5469 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgplist.pngbin0 -> 25049 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgplist2.pngbin0 -> 24538 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgpres1.pngbin0 -> 29852 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgpres2.pngbin0 -> 29568 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgpres3.pngbin0 -> 29962 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgvol.pngbin0 -> 17818 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgvolnum.pngbin0 -> 1290 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgvolpos.pngbin0 -> 4589 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgwshad.pngbin0 -> 7219 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgwshad2.pngbin0 -> 7270 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgwshdsk.pngbin0 -> 710 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgwshvol.pngbin0 -> 519 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/kjofol/sgwshvp.pngbin0 -> 709 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/Makefile.am8
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/p_eq.pngbin0 -> 131 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/p_main.pngbin0 -> 53187 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/p_mainback.pngbin0 -> 56216 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/p_numbers.pngbin0 -> 189 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/p_playback.pngbin0 -> 30106 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/p_playlist.pngbin0 -> 29702 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/p_propos.pngbin0 -> 3363 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/p_text.pngbin0 -> 477 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/p_volbar.pngbin0 -> 49460 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/p_volpos.pngbin0 -> 2325 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/phong.dck26
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/phong.rc104
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/phong.wsh26
-rw-r--r--noatun/modules/kjofol-skin/skins/phong/phong_readme.txt62
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/Makefile.am8
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/i_base.pngbin0 -> 65392 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/i_base2.pngbin0 -> 70854 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/i_eq.pngbin0 -> 165 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/i_font.pngbin0 -> 308 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/i_pl.pngbin0 -> 66544 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/i_pl2.pngbin0 -> 66435 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/i_pro.pngbin0 -> 676 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/i_text.pngbin0 -> 379 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/i_vol.pngbin0 -> 13273 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/i_volpos.pngbin0 -> 2550 bytes
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.dck26
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.rc105
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.wsh26
-rw-r--r--noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric_readme.txt19
-rw-r--r--noatun/modules/making_plugins23
-rw-r--r--noatun/modules/marquis/Makefile.am15
-rw-r--r--noatun/modules/marquis/marquis.cpp186
-rw-r--r--noatun/modules/marquis/marquis.h52
-rw-r--r--noatun/modules/marquis/marquis.plugin83
-rw-r--r--noatun/modules/marquis/plugin.cpp9
-rw-r--r--noatun/modules/metatag/HANDLED_ITEMS21
-rw-r--r--noatun/modules/metatag/Makefile.am15
-rw-r--r--noatun/modules/metatag/edit.cpp312
-rw-r--r--noatun/modules/metatag/edit.h55
-rw-r--r--noatun/modules/metatag/metatag.cpp124
-rw-r--r--noatun/modules/metatag/metatag.h30
-rw-r--r--noatun/modules/metatag/metatag.plugin125
-rw-r--r--noatun/modules/monoscope/Makefile.am16
-rw-r--r--noatun/modules/monoscope/monoscope.cpp112
-rw-r--r--noatun/modules/monoscope/monoscope.h34
-rw-r--r--noatun/modules/monoscope/monoscope.plugin99
-rw-r--r--noatun/modules/net/Makefile.am14
-rw-r--r--noatun/modules/net/net.cpp57
-rw-r--r--noatun/modules/net/net.h31
-rw-r--r--noatun/modules/net/net.plugin136
-rw-r--r--noatun/modules/noatunui/Makefile.am16
-rw-r--r--noatun/modules/noatunui/noatunui.cpp9
-rw-r--r--noatun/modules/noatunui/noatunui.plugin120
-rw-r--r--noatun/modules/noatunui/userinterface.cpp315
-rw-r--r--noatun/modules/noatunui/userinterface.h72
-rw-r--r--noatun/modules/simple/Makefile.am16
-rw-r--r--noatun/modules/simple/back.xpm21
-rw-r--r--noatun/modules/simple/forward.xpm21
-rw-r--r--noatun/modules/simple/noatunui.cpp17
-rw-r--r--noatun/modules/simple/pause.xpm21
-rw-r--r--noatun/modules/simple/play.xpm21
-rw-r--r--noatun/modules/simple/playlist.xpm21
-rw-r--r--noatun/modules/simple/propertiesdialog.ui348
-rw-r--r--noatun/modules/simple/propertiesdialog.ui.h58
-rw-r--r--noatun/modules/simple/simple.plugin126
-rw-r--r--noatun/modules/simple/simpleui.rc37
-rw-r--r--noatun/modules/simple/stop.xpm21
-rw-r--r--noatun/modules/simple/userinterface.cpp379
-rw-r--r--noatun/modules/simple/userinterface.h80
-rw-r--r--noatun/modules/simple/volume.xpm21
-rw-r--r--noatun/modules/splitplaylist/LICENSE124
-rw-r--r--noatun/modules/splitplaylist/Makefile.am17
-rw-r--r--noatun/modules/splitplaylist/find.cpp63
-rw-r--r--noatun/modules/splitplaylist/find.h33
-rw-r--r--noatun/modules/splitplaylist/playlist.cpp281
-rw-r--r--noatun/modules/splitplaylist/playlist.h98
-rw-r--r--noatun/modules/splitplaylist/splitplaylist.cpp13
-rw-r--r--noatun/modules/splitplaylist/splitplaylist.plugin118
-rw-r--r--noatun/modules/splitplaylist/splui.rc34
-rw-r--r--noatun/modules/splitplaylist/view.cpp1009
-rw-r--r--noatun/modules/splitplaylist/view.h165
-rw-r--r--noatun/modules/systray/Makefile.am16
-rw-r--r--noatun/modules/systray/cmodule.cpp192
-rw-r--r--noatun/modules/systray/cmodule.h55
-rw-r--r--noatun/modules/systray/kitsystemtray.cpp131
-rw-r--r--noatun/modules/systray/kitsystemtray.h54
-rw-r--r--noatun/modules/systray/noatunui.cpp9
-rw-r--r--noatun/modules/systray/systray.cpp467
-rw-r--r--noatun/modules/systray/systray.h80
-rw-r--r--noatun/modules/systray/systray.plugin100
-rw-r--r--noatun/modules/systray/systrayui.rc20
-rw-r--r--noatun/modules/systray/yhconfig.kcfg81
-rw-r--r--noatun/modules/systray/yhconfig.kcfgc7
-rw-r--r--noatun/modules/systray/yhconfigwidget.ui333
-rw-r--r--noatun/modules/voiceprint/Makefile.am14
-rw-r--r--noatun/modules/voiceprint/prefs.cpp67
-rw-r--r--noatun/modules/voiceprint/prefs.h22
-rw-r--r--noatun/modules/voiceprint/voiceprint.cpp126
-rw-r--r--noatun/modules/voiceprint/voiceprint.h33
-rw-r--r--noatun/modules/voiceprint/voiceprint.plugin92
-rw-r--r--noatun/modules/winskin/Makefile.am51
-rw-r--r--noatun/modules/winskin/fileInfo.cpp50
-rw-r--r--noatun/modules/winskin/fileInfo.h21
-rw-r--r--noatun/modules/winskin/guiSpectrumAnalyser.cpp224
-rw-r--r--noatun/modules/winskin/guiSpectrumAnalyser.h66
-rw-r--r--noatun/modules/winskin/mimetypes/Makefile.am2
-rw-r--r--noatun/modules/winskin/mimetypes/interface/Makefile.am6
-rw-r--r--noatun/modules/winskin/mimetypes/interface/x-winamp-skin.desktop58
-rw-r--r--noatun/modules/winskin/plugin.cpp13
-rw-r--r--noatun/modules/winskin/skinMap.h38
-rw-r--r--noatun/modules/winskin/skins/Makefile.am11
-rw-r--r--noatun/modules/winskin/skins/winamp/BALANCE.BMPbin0 -> 9654 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/CBUTTONS.BMPbin0 -> 5974 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/FONT.BMPbin0 -> 12622 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/MAIN.BMPbin0 -> 96102 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/MONOSTER.BMPbin0 -> 2518 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/Makefile.am8
-rw-r--r--noatun/modules/winskin/skins/winamp/NUMS_EX.BMPbin0 -> 2482 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/PLAYPAUS.BMPbin0 -> 1474 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/POSBAR.BMPbin0 -> 4158 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/SHUFREP.BMPbin0 -> 23514 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/SPEC.BMPbin0 -> 2166 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/TEXT.BMPbin0 -> 3886 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/TITLEBAR.BMPbin0 -> 89838 bytes
-rw-r--r--noatun/modules/winskin/skins/winamp/VISCOLOR.TXT24
-rw-r--r--noatun/modules/winskin/skins/winamp/VOLUME.BMPbin0 -> 11548 bytes
-rw-r--r--noatun/modules/winskin/vis/Makefile.am39
-rw-r--r--noatun/modules/winskin/vis/WinSkinFFT.mcopclass5
-rw-r--r--noatun/modules/winskin/vis/realFFT.cpp156
-rw-r--r--noatun/modules/winskin/vis/realFFT.h69
-rw-r--r--noatun/modules/winskin/vis/realFFTFilter.cpp88
-rw-r--r--noatun/modules/winskin/vis/realFFTFilter.h49
-rw-r--r--noatun/modules/winskin/vis/visQueue.cpp43
-rw-r--r--noatun/modules/winskin/vis/visQueue.h32
-rw-r--r--noatun/modules/winskin/vis/winSkinFFT_impl.cpp148
-rw-r--r--noatun/modules/winskin/vis/winSkinFFT_impl.h62
-rw-r--r--noatun/modules/winskin/vis/winskinvis.idl12
-rw-r--r--noatun/modules/winskin/waBalanceSlider.cpp56
-rw-r--r--noatun/modules/winskin/waBalanceSlider.h42
-rw-r--r--noatun/modules/winskin/waButton.cpp101
-rw-r--r--noatun/modules/winskin/waButton.h62
-rw-r--r--noatun/modules/winskin/waClutterbar.cpp11
-rw-r--r--noatun/modules/winskin/waClutterbar.h18
-rw-r--r--noatun/modules/winskin/waColor.cpp73
-rw-r--r--noatun/modules/winskin/waColor.h26
-rw-r--r--noatun/modules/winskin/waDigit.cpp89
-rw-r--r--noatun/modules/winskin/waDigit.h48
-rw-r--r--noatun/modules/winskin/waIndicator.cpp34
-rw-r--r--noatun/modules/winskin/waIndicator.h41
-rw-r--r--noatun/modules/winskin/waInfo.cpp173
-rw-r--r--noatun/modules/winskin/waInfo.h53
-rw-r--r--noatun/modules/winskin/waJumpSlider.cpp78
-rw-r--r--noatun/modules/winskin/waJumpSlider.h52
-rw-r--r--noatun/modules/winskin/waLabel.cpp65
-rw-r--r--noatun/modules/winskin/waLabel.h39
-rw-r--r--noatun/modules/winskin/waMain.cpp13
-rw-r--r--noatun/modules/winskin/waMain.h16
-rw-r--r--noatun/modules/winskin/waRegion.cpp126
-rw-r--r--noatun/modules/winskin/waRegion.h26
-rw-r--r--noatun/modules/winskin/waShadeMapping.h148
-rw-r--r--noatun/modules/winskin/waSkin.cpp800
-rw-r--r--noatun/modules/winskin/waSkin.h177
-rw-r--r--noatun/modules/winskin/waSkinManager.cpp127
-rw-r--r--noatun/modules/winskin/waSkinManager.h39
-rw-r--r--noatun/modules/winskin/waSkinMapping.h148
-rw-r--r--noatun/modules/winskin/waSkinModel.cpp458
-rw-r--r--noatun/modules/winskin/waSkinModel.h64
-rw-r--r--noatun/modules/winskin/waSkins.h161
-rw-r--r--noatun/modules/winskin/waSlider.cpp209
-rw-r--r--noatun/modules/winskin/waSlider.h82
-rw-r--r--noatun/modules/winskin/waStatus.cpp42
-rw-r--r--noatun/modules/winskin/waStatus.h40
-rw-r--r--noatun/modules/winskin/waTitleBar.cpp79
-rw-r--r--noatun/modules/winskin/waTitleBar.h51
-rw-r--r--noatun/modules/winskin/waVolumeSlider.cpp51
-rw-r--r--noatun/modules/winskin/waVolumeSlider.h41
-rw-r--r--noatun/modules/winskin/waWidget.cpp58
-rw-r--r--noatun/modules/winskin/waWidget.h31
-rw-r--r--noatun/modules/winskin/winSkinConfig.cpp174
-rw-r--r--noatun/modules/winskin/winSkinConfig.h35
-rw-r--r--noatun/modules/winskin/winSkinVis.cpp107
-rw-r--r--noatun/modules/winskin/winSkinVis.h54
-rw-r--r--noatun/modules/winskin/winskin.plugin122
-rw-r--r--noatun/noatun.api169
-rw-r--r--noatun/noatun.desktop83
-rw-r--r--noatun/noatun.upd5
-rw-r--r--noatun/noatun20update.cpp41
-rw-r--r--noatun/pics/Makefile.am2
-rw-r--r--noatun/pics/cr128-action-equalizer.pngbin0 -> 6419 bytes
-rw-r--r--noatun/pics/cr128-action-playlist.pngbin0 -> 8465 bytes
-rw-r--r--noatun/pics/cr16-action-effect.pngbin0 -> 964 bytes
-rw-r--r--noatun/pics/cr16-action-equalizer.pngbin0 -> 746 bytes
-rw-r--r--noatun/pics/cr16-action-noatunback.pngbin0 -> 140 bytes
-rw-r--r--noatun/pics/cr16-action-noatunforward.pngbin0 -> 139 bytes
-rw-r--r--noatun/pics/cr16-action-noatunpause.pngbin0 -> 116 bytes
-rw-r--r--noatun/pics/cr16-action-noatunplay.pngbin0 -> 133 bytes
-rw-r--r--noatun/pics/cr16-action-noatunplaylist.pngbin0 -> 145 bytes
-rw-r--r--noatun/pics/cr16-action-noatunstop.pngbin0 -> 117 bytes
-rw-r--r--noatun/pics/cr16-action-playlist.pngbin0 -> 836 bytes
-rw-r--r--noatun/pics/cr22-action-equalizer.pngbin0 -> 1070 bytes
-rw-r--r--noatun/pics/cr22-action-noatunback.pngbin0 -> 209 bytes
-rw-r--r--noatun/pics/cr22-action-noatunfback.pngbin0 -> 189 bytes
-rw-r--r--noatun/pics/cr22-action-noatunfforward.pngbin0 -> 188 bytes
-rw-r--r--noatun/pics/cr22-action-noatunforward.pngbin0 -> 210 bytes
-rw-r--r--noatun/pics/cr22-action-noatunloopnone.pngbin0 -> 233 bytes
-rw-r--r--noatun/pics/cr22-action-noatunloopplaylist.pngbin0 -> 270 bytes
-rw-r--r--noatun/pics/cr22-action-noatunlooprandom.pngbin0 -> 244 bytes
-rw-r--r--noatun/pics/cr22-action-noatunloopsong.pngbin0 -> 266 bytes
-rw-r--r--noatun/pics/cr22-action-noatunpause.pngbin0 -> 201 bytes
-rw-r--r--noatun/pics/cr22-action-noatunplay.pngbin0 -> 184 bytes
-rw-r--r--noatun/pics/cr22-action-noatunplaylist.pngbin0 -> 234 bytes
-rw-r--r--noatun/pics/cr22-action-noatunstop.pngbin0 -> 140 bytes
-rw-r--r--noatun/pics/cr22-action-noatuntiny.pngbin0 -> 191 bytes
-rw-r--r--noatun/pics/cr22-action-playlist.pngbin0 -> 1066 bytes
-rw-r--r--noatun/pics/cr32-action-effect.pngbin0 -> 1937 bytes
-rw-r--r--noatun/pics/cr32-action-equalizer.pngbin0 -> 1742 bytes
-rw-r--r--noatun/pics/cr32-action-playlist.pngbin0 -> 1673 bytes
-rw-r--r--noatun/pics/cr48-action-effect.pngbin0 -> 3073 bytes
-rw-r--r--noatun/pics/cr48-action-equalizer.pngbin0 -> 2582 bytes
-rw-r--r--noatun/pics/cr48-action-playlist.pngbin0 -> 2672 bytes
-rw-r--r--noatun/pics/cr64-action-equalizer.pngbin0 -> 3478 bytes
-rw-r--r--noatun/pics/cr64-action-playlist.pngbin0 -> 3462 bytes
-rw-r--r--noatun/preset.dance8
-rw-r--r--noatun/preset.eclecticguitar9
-rw-r--r--noatun/preset.jazz8
-rw-r--r--noatun/preset.metal8
-rw-r--r--noatun/preset.trance8
-rw-r--r--noatun/preset.zero9
556 files changed, 44045 insertions, 0 deletions
diff --git a/noatun/CHANGES b/noatun/CHANGES
new file mode 100644
index 00000000..eb2df008
--- /dev/null
+++ b/noatun/CHANGES
@@ -0,0 +1,4 @@
+* Added assert()'s to Itemlist interface. All client code must adhere
+with the reference semantics of Itemlist: never access it unless you make
+sure that the reference is valid.
+
diff --git a/noatun/COPYING b/noatun/COPYING
new file mode 100644
index 00000000..2c16ee94
--- /dev/null
+++ b/noatun/COPYING
@@ -0,0 +1,26 @@
+All files within Noatun are under the following license, unless otherwise
+stated. All plugins (within the modules directory) carry their own
+license, as read from the .plugin file.
+
+--
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/noatun/FILES b/noatun/FILES
new file mode 100644
index 00000000..b281f414
--- /dev/null
+++ b/noatun/FILES
@@ -0,0 +1,13 @@
+cmodule: Each panel in the settings window
+controls: those widgets that appear in the toolbars TODOLATER
+downloader: unused broken class for getting files
+engine: the only class that communicates to aRts
+klistview_t: like a KListView, but with more functionality, especially DnD TODOSLOWLY
+noatun: all the actions, makes toolbars, e.g., main window
+noatunpref: the main settings box (semi-unused) TODOLATER
+noatunview.cpp: The main view within the main view, which only contains the videoframe
+player: what happens when all the standard player buttons are fondled TODONOW
+playlist: abstraction of a playlist
+playlistview: the window with the playlist TODONOW
+setting: stores all the settings in KConfig
+videoframe: the little box that's supposed to get the video drawn in it
diff --git a/noatun/Makefile.am b/noatun/Makefile.am
new file mode 100644
index 00000000..bb024d36
--- /dev/null
+++ b/noatun/Makefile.am
@@ -0,0 +1,29 @@
+INCLUDES = -I$(kde_includes)/kio -I$(top_srcdir)/noatun/library -I$(kde_includes)/arts $(all_includes)
+
+SUBDIRS = pics library app . modules
+
+KDE_ICON = AUTO
+
+xdg_apps_DATA = noatun.desktop
+
+presetsdir = $(kde_datadir)/noatun/eq.preset
+presets_DATA = preset.dance preset.jazz preset.metal preset.trance preset.zero
+
+updatedir = $(kde_datadir)/kconf_update
+update_DATA = noatun.upd
+update_PROGRAMS = noatun20update
+
+noatun20update_SOURCES = noatun20update.cpp
+noatun20update_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+noatun20update_LDADD = $(LIB_QT)
+
+messages:
+ $(EXTRACTRC) `find . -name "*.rc" -o -name "*.ui"` > rc.cpp
+ $(XGETTEXT) rc.cpp `find . -name "*.cc" -o -name "*.cpp" -o -name "*.h"` -o $(podir)/noatun.pot
+
+api:
+ $(mkinstalldirs) $(top_builddir)/noatun/apidocs/libnoatun
+ if test ! -x $(top_builddir)/noatun/apidocs/common; then \
+ $(LN_S) $(kde_libs_htmldir)/en/common $(top_builddir)/noatun/apidocs/common; \
+ fi
+ doxygen noatun.api
diff --git a/noatun/TODO b/noatun/TODO
new file mode 100644
index 00000000..1bc55de0
--- /dev/null
+++ b/noatun/TODO
@@ -0,0 +1,15 @@
+1) get the command line stuff to work better
+2) variband equalizer:
+
+ y= log(x)/log(22000)*N
+ N : the amount of bands
+
+ perhaps you'll need to invert it
+
+Other CODECs for aRts:
+1) MIDI/timidity for transparent high-quality MID files
+2) SMPEG
+3) SPC
+4) NSF (requires multi-track files)
+5) libmpeg3
+6) rename addFile() to play() in class Playlist and the DCOP interface.
diff --git a/noatun/app/Makefile.am b/noatun/app/Makefile.am
new file mode 100644
index 00000000..59d8dbd2
--- /dev/null
+++ b/noatun/app/Makefile.am
@@ -0,0 +1,11 @@
+INCLUDES = -I$(top_srcdir)/noatun/library -I$(kde_includes)/arts $(all_includes)
+
+kdeinit_LTLIBRARIES = noatun.la
+noatun_la_SOURCES = main.cpp
+noatun_la_LDFLAGS = $(KDE_RPATH) $(all_libraries) -module -avoid-version
+noatun_la_LIBADD = -lqtmcop -lkmedia2_idl -lsoundserver_idl $(top_builddir)/noatun/library/libnoatun.la
+
+bin_PROGRAMS =
+lib_LTLIBRARIES =
+
+METASOURCES = AUTO
diff --git a/noatun/app/main.cpp b/noatun/app/main.cpp
new file mode 100644
index 00000000..86883461
--- /dev/null
+++ b/noatun/app/main.cpp
@@ -0,0 +1,47 @@
+#include <noatun/app.h>
+//#include "joinhandler.h"
+//#include <dcopclient.h>
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <klocale.h>
+
+static const char description[] = I18N_NOOP("The Fusion of Frequencies");
+static const char version[] = NOATUN_VERSION;
+
+static KCmdLineOptions options[] =
+{
+ { "+[URL]", I18N_NOOP("Files/URLs to open"), 0 },
+ KCmdLineLastOption
+};
+
+extern "C" KDE_EXPORT int kdemain(int argc, char **argv)
+{
+ KAboutData aboutData("noatun", I18N_NOOP("Noatun"),
+ version, description, KAboutData::License_BSD,
+ I18N_NOOP("(c) 2000-2004, The Noatun Developers"),
+ 0, "http://noatun.kde.org");
+ aboutData.addAuthor("Charles Samuels", I18N_NOOP("Noatun Developer"),
+ "charles@kde.org");
+ aboutData.addAuthor("Stefan Westerfeld", I18N_NOOP("Patron of the aRts"),
+ "stefan@space.twc.de");
+ aboutData.addAuthor("Martin Vogt", I18N_NOOP("MPEG Codec and OGG Vorbis Support"),
+ "mvogt@rhrk.uni-kl.de");
+ aboutData.addAuthor("Malte Starostik", I18N_NOOP("Infrared Control Support and HTML playlist export"),
+ "malte@kde.org");
+ aboutData.addAuthor("Nikolas Zimmermann", I18N_NOOP("HTML playlist export and Plugin System"),
+ "wildfox@kde.org");
+ aboutData.addAuthor("Stefan Schimanski", I18N_NOOP("Kaiman Skin Support"),
+ "1Stein@gmx.de");
+ aboutData.addAuthor("Stefan Gehn", I18N_NOOP("Extended K-Jöfol Skin Support, EXTM3U playlist loading"),
+ "metz@gehn.net");
+
+ aboutData.addCredit("Bill Huey", I18N_NOOP("Special help with the equalizer"));
+
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineArgs::addCmdLineOptions(options);
+ NoatunApp::addCmdLineOptions();
+
+ NoatunApp app;
+ return app.exec();
+}
+
diff --git a/noatun/configure.in.in b/noatun/configure.in.in
new file mode 100644
index 00000000..f8ab00da
--- /dev/null
+++ b/noatun/configure.in.in
@@ -0,0 +1,15 @@
+#MIN_CONFIG
+
+dnl These are common macros that you might or might not want to use
+
+dnl Checks for header files.
+AC_HEADER_DIRENT
+AC_HEADER_STDC
+AC_HEADER_TIME
+AC_CHECK_HEADERS(fcntl.h sys/time.h unistd.h stdlib.h paths.h)
+AC_CHECK_FUNCS(usleep)
+
+if test "x$build_arts" = "xno"; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE noatun"
+fi
+
diff --git a/noatun/hi128-app-noatun.png b/noatun/hi128-app-noatun.png
new file mode 100644
index 00000000..2eb53811
--- /dev/null
+++ b/noatun/hi128-app-noatun.png
Binary files differ
diff --git a/noatun/hi16-app-noatun.png b/noatun/hi16-app-noatun.png
new file mode 100644
index 00000000..c388b4ae
--- /dev/null
+++ b/noatun/hi16-app-noatun.png
Binary files differ
diff --git a/noatun/hi22-app-noatun.png b/noatun/hi22-app-noatun.png
new file mode 100644
index 00000000..d2f30ac4
--- /dev/null
+++ b/noatun/hi22-app-noatun.png
Binary files differ
diff --git a/noatun/hi32-app-noatun.png b/noatun/hi32-app-noatun.png
new file mode 100644
index 00000000..c440d092
--- /dev/null
+++ b/noatun/hi32-app-noatun.png
Binary files differ
diff --git a/noatun/hi48-app-noatun.png b/noatun/hi48-app-noatun.png
new file mode 100644
index 00000000..8ab19a28
--- /dev/null
+++ b/noatun/hi48-app-noatun.png
Binary files differ
diff --git a/noatun/hi64-app-noatun.png b/noatun/hi64-app-noatun.png
new file mode 100644
index 00000000..596ceaa7
--- /dev/null
+++ b/noatun/hi64-app-noatun.png
Binary files differ
diff --git a/noatun/library/Makefile.am b/noatun/library/Makefile.am
new file mode 100644
index 00000000..f3e1dfd9
--- /dev/null
+++ b/noatun/library/Makefile.am
@@ -0,0 +1,57 @@
+INCLUDES = -I$(top_srcdir)/noatun/library/noatun \
+ -I$(top_srcdir)/noatun/library \
+ -I$(top_srcdir)/arts/gui/kde -I$(top_builddir)/arts/gui/kde \
+ -I$(top_srcdir)/arts/gui/common -I$(top_builddir)/arts/gui/common \
+ -I$(kde_includes)/arts \
+ -I$(top_srcdir)/noatun/library/noatunarts \
+ -I$(top_builddir)/noatun/library/noatunarts \
+ $(all_includes)
+
+SUBDIRS= noatunarts . noatuntags noatun
+
+lib_LTLIBRARIES = libnoatun.la libnoatuncontrols.la
+
+libnoatun_la_SOURCES = pluginmodule.cpp cmodule.cpp downloader.cpp engine.cpp \
+ playlist.cpp pref.cpp \
+ player.cpp playlistsaver.cpp app.cpp \
+ pluginloader.cpp plugin.cpp \
+ noatunstdaction.cpp conversion.cpp\
+ noatunui.cpp effectview.cpp \
+ equalizerwidget.ui equalizerview.cpp equalizer.cpp \
+ effects.cpp mimetypetree.cpp stereobuttonaction.cpp ksaver.cpp \
+ video.cpp vequalizer.cpp spline.cpp titleproxy.cpp
+
+include_HEADERS = \
+ cmodule.h plugin_deps.h equalizerview.h effectview.h mimetypetree.h \
+ ksaver.h
+
+libnoatun_la_LDFLAGS = \
+ -version-info 3:0:2 $(KDE_RPATH) $(all_libraries)
+libnoatun_la_LIBADD = $(top_builddir)/arts/gui/common/libartsgui.la \
+ $(top_builddir)/arts/gui/kde/libartsgui_kde.la $(top_builddir)/arts/modules/libartsmodules.la \
+ -lkio -lqtmcop -lkmedia2_idl $(top_builddir)/noatun/library/noatunarts/libnoatunarts.la \
+ -lartsflow -lsoundserver_idl -lartskde $(LIBDL)
+
+libnoatuncontrols_la_SOURCES = controls.cpp scrollinglabel.cpp
+libnoatuncontrols_la_LDFLAGS = -version-info 3:0:2 $(KDE_RPATH) $(all_libraries) -no-undefined
+libnoatuncontrols_la_LIBADD = $(LIBDL) $(LIB_KDEUI)
+
+METASOURCES = AUTO
+META_INCLUDES = $(srcdir)/noatun
+
+noinst_PROGRAMS = gentable
+gentable_SOURCES = gentable.cpp
+gentable_LDFLAGS = $(KDE_EXTRA_RPATH)
+
+magictable: gentable
+ $(top_builddir)/noatun/library/gentable > magictable
+
+magictabledir = $(kde_datadir)/noatun
+magictable_DATA = magictable
+
+effects.lo: noatunarts/noatunarts.h ../../arts/gui/common/artsgui.h
+engine.lo: noatunarts/noatunarts.h
+equalizer.lo: noatunarts/noatunarts.h
+plugin.lo: noatunarts/noatunarts.h
+vequalizer.lo: noatunarts/noatunarts.h
+
diff --git a/noatun/library/app.cpp b/noatun/library/app.cpp
new file mode 100644
index 00000000..04167fe7
--- /dev/null
+++ b/noatun/library/app.cpp
@@ -0,0 +1,530 @@
+#include "cmodule.h"
+#include "downloader.h"
+#include "effects.h"
+#include "effectview.h"
+#include "engine.h"
+#include "equalizer.h"
+#include "equalizerview.h"
+#include "vequalizer.h"
+#include "app.h"
+#include "playlist.h"
+#include "pref.h"
+#include "player.h"
+#include "plugin.h"
+#include "pluginloader.h"
+#include "globalvideo.h"
+#include "stdaction.h"
+
+#include <common.h>
+#include <dcopobject.h>
+#include <dispatcher.h>
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kfiledialog.h>
+#include <kglobal.h>
+#include <klibloader.h>
+#include <klocale.h>
+#include <kmainwindow.h>
+#include <kmessagebox.h>
+#include <knotifyclient.h>
+#include <kpopupmenu.h>
+#include <qfile.h>
+#include <qimage.h>
+#include <qiomanager.h>
+#include <qsessionmanager.h>
+#include <qtextstream.h>
+#include <signal.h>
+#include <kmimetype.h>
+
+
+using std::string;
+using std::vector;
+
+// forgot the d pointer in Player TODO
+static GlobalVideo *globalVideo;
+struct NoatunApp::Private
+{
+ Effects *effects;
+ VEqualizer *vequalizer;
+};
+
+
+NoatunApp::NoatunApp()
+ : KUniqueApplication(true, true, true), mPluginMenu(0), mPluginActionMenu(0), mEqualizer(0)
+{
+ d = new Private;
+ d->vequalizer=0;
+ d->effects=0;
+
+ Visualization::internalVis=true;
+
+ mDownloader=new Downloader;
+
+ Visualization::initDispatcher(); // 316
+
+ showingInterfaces = true;
+
+ // set the default config data
+ // TODO: Maybe a first time wizard instead?
+ KConfig *config=KGlobal::config(); // +
+ config->setGroup(QString::null); // 1
+ if (!config->readEntry("Modules").length())
+ {
+ QStringList modules;
+ modules.append("excellent.plugin");
+ modules.append("splitplaylist.plugin");
+ modules.append("marquis.plugin");
+ modules.append("systray.plugin");
+ modules.append("metatag.plugin");
+ config->writeEntry("Modules", modules);
+ config->sync();
+ } // 1
+
+ mPref=new NoatunPreferences(0L); // 115
+ mPref->hide(); // 1
+ mLibraryLoader = new LibraryLoader; // 0
+
+ mLibraryLoader->add("dcopiface.plugin");
+
+ new General(this); // 25
+ new Plugins(this); // 149
+// new Types(this);
+
+ mPlayer=new Player; // 139
+ d->effects=new Effects; // 1
+ d->vequalizer = new VEqualizer;
+ d->vequalizer->init();
+ mEqualizer=new Equalizer; // 0
+ mEqualizer->init(); // 41
+ mEffectView=new EffectView; // 859
+ mEqualizerView=new EqualizerView; // 24
+
+ QTimer::singleShot(0, mDownloader, SLOT(start()));
+
+ ::globalVideo = new GlobalVideo;
+
+ if(isRestored())
+ {
+ mLibraryLoader->add("marquis.plugin");
+ static_cast<SessionManagement*>(mLibraryLoader->plugins().first())->restore();
+ }
+ else
+ {
+ loadPlugins(); // 1531
+ if (!playlist())
+ {
+ KMessageBox::error(0,i18n("No playlist plugin was found. " \
+ "Please make sure that Noatun was installed correctly."));
+ KApplication::quit();
+ delete this;
+ }
+ else
+ {
+ config->setGroup(QString::null); // 0
+ player()->setVolume(config->readNumEntry("Volume", 100)); // 10
+ player()->loop(config->readNumEntry("LoopStyle", (int)Player::None));
+ mPlayer->engine()->setInitialized(); // 0
+
+ switch (startupPlayMode())
+ {
+ case Restore:
+ restoreEngineState();
+ break;
+ case Play:
+ player()->play();
+ break;
+ case DontPlay:
+ default:
+ break;
+ }
+ }
+ }
+}
+
+NoatunApp::~NoatunApp()
+{
+ saveEngineState();
+ KConfig *config = KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("Volume", player()->volume());
+ config->writeEntry("LoopStyle", player()->loopStyle());
+ // for version continuity in the future
+ config->writeEntry("Version", QString(version())); // 1
+ config->sync(); // 40
+
+ mPlayer->stop();
+ delete ::globalVideo;
+ delete mLibraryLoader;
+ delete mEffectView;
+ delete mDownloader;
+ delete mEqualizer;
+ delete d->vequalizer;
+ delete mEqualizerView;
+ delete d->effects;
+
+ delete mPlayer;
+ delete mPref;
+ config->sync();
+
+ delete d;
+}
+
+QCString NoatunApp::version() const
+{
+ return aboutData()->version().ascii();
+}
+
+inline bool logicalXOR(bool A, bool B)
+{
+ return (A || B) && !(A && B);
+}
+
+#define READBOOLOPT_EX(name, string, def, group, reversal) \
+bool NoatunApp::name() const \
+{ \
+ KConfig *config=KGlobal::config(); \
+ config->setGroup(group); \
+ return logicalXOR(config->readBoolEntry(string, def), reversal); \
+}
+
+#define READBOOLOPT(name, string, def) \
+ READBOOLOPT_EX(name, string, def, "", false)
+
+
+READBOOLOPT(autoPlay, "AutoPlay", false)
+READBOOLOPT(loopList, "LoopList", true)
+READBOOLOPT(hackUpPlaylist, "HackUpPlaylist", true)
+READBOOLOPT(fastMixer, "FastMixer", false)
+READBOOLOPT(displayRemaining, "DisplayRemaining", false)
+READBOOLOPT(clearOnOpen, "ClearOnOpen", false)
+READBOOLOPT_EX(oneInstance, "MultipleInstances", true, "KDE", true)
+
+#undef READBOOLOPT
+#undef READBOOLOPT_EX
+
+bool NoatunApp::clearOnStart() const
+{
+ return clearOnOpen();
+}
+
+int NoatunApp::startupPlayMode() const
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ return config->readNumEntry("StartupPlayMode", autoPlay() ? Play : Restore);
+}
+
+void NoatunApp::setStartupPlayMode(int mode)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("StartupPlayMode", mode);
+ config->sync();
+}
+
+void NoatunApp::setHackUpPlaylist(bool b)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("HackUpPlaylist", b);
+ config->sync();
+}
+
+void NoatunApp::setFastMixer(bool b)
+{
+ bool whatBefore=fastMixer();
+ if (whatBefore!=b)
+ {
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("FastMixer", b);
+ config->sync();
+ player()->engine()->useHardwareMixer(b);
+ }
+}
+
+void NoatunApp::setOneInstance(bool b)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup("KDE");
+ config->writeEntry("MultipleInstances", !b);
+ config->sync();
+}
+
+void NoatunApp::setLoopList(bool b)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("LoopList", b);
+ KGlobal::config()->sync();
+}
+
+void NoatunApp::setAutoPlay(bool b)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("AutoPlay", b);
+ KGlobal::config()->sync();
+}
+
+void NoatunApp::setRememberPositions(bool b)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("RememberPositions", b);
+ KGlobal::config()->sync();
+}
+
+void NoatunApp::setSaveDirectory(const QString &s)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writePathEntry("SaveDirectory", s);
+ config->sync();
+}
+
+QString NoatunApp::saveDirectory() const
+{
+ KConfig *c=KGlobal::config();
+ c->setGroup(QString::null);
+ return c->readPathEntry("SaveDirectory", QString(getenv("HOME")));
+}
+
+QString NoatunApp::titleFormat() const
+{
+ KConfig *c=KGlobal::config();
+ c->setGroup(QString::null);
+ return c->readEntry("TitleFormat", "$(\"[\"author\"] - \")$(title)$(\" (\"bitrate\"kbps)\")");
+}
+
+void NoatunApp::setTitleFormat(const QString &format)
+{
+ KConfig *c=KGlobal::config();
+ c->setGroup(QString::null);
+ return c->writeEntry("TitleFormat", format);
+}
+
+void NoatunApp::setClearOnStart(bool b)
+{
+ setClearOnOpen(b);
+}
+
+void NoatunApp::setClearOnOpen(bool b)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("ClearOnOpen", b);
+ config->sync();
+}
+
+void NoatunApp::setDisplayRemaining(bool b)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("DisplayRemaining", b);
+ config->sync();
+}
+
+void NoatunApp::toggleInterfaces()
+{
+ showingInterfaces ^= true;
+
+ if(showingInterfaces)
+ emit showYourself();
+ else
+ emit hideYourself();
+}
+
+void NoatunApp::showInterfaces()
+{
+ showingInterfaces = true;
+ emit showYourself();
+}
+
+void NoatunApp::hideInterfaces()
+{
+ showingInterfaces = false;
+ emit hideYourself();
+}
+
+
+int NoatunApp::pluginMenuAdd(const QString &text, const QObject *receiver, const char *member)
+{
+ return pluginActionMenu()->menuAdd(text, receiver, member);
+}
+
+void NoatunApp::pluginMenuRemove(int id)
+{
+ pluginActionMenu()->menuRemove(id);
+}
+
+NoatunStdAction::PluginActionMenu *NoatunApp::pluginActionMenu()
+{
+ if (!mPluginActionMenu)
+ mPluginActionMenu = new NoatunStdAction::PluginActionMenu(this, "menu_actions");
+ return mPluginActionMenu;
+}
+
+KPopupMenu *NoatunApp::pluginMenu()
+{
+ if(!mPluginMenu)
+ mPluginMenu = pluginActionMenu()->popupMenu();
+ return mPluginMenu;
+}
+
+int NoatunApp::newInstance()
+{
+ // TODO, this should call playlist()->handleArguments();
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ bool clear = clearOnOpen();
+ bool playme=true;
+
+ for (int i=0; i<args->count(); i++)
+ {
+ player()->openFile(args->url(i), clear, playme);
+ playme=false;
+ clear=false;
+ }
+
+ args->clear();
+ return 0;
+}
+
+void NoatunApp::preferences()
+{
+ mPref->show();
+ mPref->raise();
+}
+
+void NoatunApp::quit()
+{
+ KApplication::quit();
+}
+
+void NoatunApp::fileOpen()
+{
+ KURL::List files = KFileDialog::getOpenURLs(":mediadir", mimeTypes(), 0,
+ i18n("Select File to Play"));
+
+ if (files.count())
+ mPlayer->openFile(files, clearOnOpen(), true);
+}
+
+void NoatunApp::effectView()
+{
+ mEffectView->show();
+}
+
+void NoatunApp::equalizerView()
+{
+ mEqualizerView->show();
+}
+
+VEqualizer * NoatunApp::vequalizer()
+{
+ return d->vequalizer;
+}
+
+Effects *NoatunApp::effects() const
+{
+ return d->effects;
+}
+
+QString NoatunApp::mimeTypes()
+{
+ QString mimeTypes;
+ Arts::TraderQuery q;
+ vector<Arts::TraderOffer> *results = q.query();
+ vector<Arts::TraderOffer>::iterator i;
+
+ for (i=results->begin(); i != results->end(); i++)
+ {
+ vector<string> *prop = (*i).getProperty("MimeType");
+
+ vector<string>::iterator istr;
+ for (istr = prop->begin(); istr != prop->end(); istr++)
+ {
+ if ( !(*istr).length())
+ continue;
+
+ const char *m = (*istr).c_str();
+ if ((KServiceType::serviceType(m)) && !mimeTypes.contains(m))
+ {
+ mimeTypes += m;
+ mimeTypes += ' ';
+ }
+ }
+ delete prop;
+ }
+ delete results;
+ return mimeTypes;
+}
+
+void NoatunApp::loadPlugins()
+{
+ mLibraryLoader->loadAll();
+ // backup in case of trouble
+ if(!mLibraryLoader->playlist())
+ {
+ kdDebug(66666) << k_funcinfo << "NO playlist plugin found!" << endl;
+ //mLibraryLoader->add("splitplaylist.plugin");
+ }
+}
+
+Playlist *NoatunApp::playlist() const
+{
+ return mLibraryLoader->playlist();
+}
+
+void NoatunApp::commitData(QSessionManager &)
+{
+}
+
+void NoatunApp::saveState(QSessionManager &sm)
+{
+ QStringList restartCommand = sm.restartCommand();
+ sm.setRestartCommand( restartCommand );
+
+ KApplication::saveState(sm);
+}
+
+// Deprecated
+QImage NoatunApp::readPNG(const QString &filename)
+{
+ QImageIO file(filename, "PNG");
+ file.setGamma(0.0);
+ file.read();
+ return file.image();
+}
+
+void NoatunApp::restoreEngineState()
+{
+ KConfig* config = KGlobal::config();
+ config->setGroup(QString::null);
+ int state = config->readNumEntry("EngineState", Arts::posPlaying);
+ switch (state)
+ {
+ case Arts::posPlaying:
+ player()->play();
+ break;
+ case Arts::posPaused:
+ if (player()->isPlaying())
+ player()->playpause();
+ break;
+ case Arts::posIdle:
+ default:
+ break;
+ }
+}
+
+void NoatunApp::saveEngineState()
+{
+ KConfig* config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("EngineState", player()->engine()->state());
+ // we don't sync here since it's done in the destructor afterwards anyway
+}
+
+#include "app.moc"
diff --git a/noatun/library/cmodule.cpp b/noatun/library/cmodule.cpp
new file mode 100644
index 00000000..a624fb64
--- /dev/null
+++ b/noatun/library/cmodule.cpp
@@ -0,0 +1,131 @@
+#include <cmodule.h>
+#include <noatun/pluginloader.h>
+#include <common.h>
+#include <noatun/app.h>
+
+#include <qpushbutton.h>
+#include <qbuttongroup.h>
+#include <qradiobutton.h>
+#include <qcheckbox.h>
+#include <qlayout.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <klistview.h>
+#include <qsplitter.h>
+#include <qlabel.h>
+#include <qdragobject.h>
+#include <kurlrequester.h>
+#include <kfiledialog.h>
+#include <kdialog.h>
+#include <klineedit.h>
+
+#include <qtextview.h>
+#include <qwhatsthis.h>
+
+#include "mimetypetree.h"
+
+/*****************************************************************
+ * General options
+ *****************************************************************/
+
+General::General(QObject *parent)
+ : CModule(i18n("General"), i18n("General Options"), "configure", parent)
+{
+ mLoopList=new QCheckBox(i18n("&Return to start of playlist on finish"), this);
+ mLoopList->setChecked(napp->loopList());
+ QWhatsThis::add(mLoopList, i18n("When the playlist is finished playing, return to the start, but do not start playing."));
+
+ mOneInstance=new QCheckBox(i18n("Allow only one &instance of Noatun"), this);
+ mOneInstance->setChecked(napp->oneInstance());
+ QWhatsThis::add(mOneInstance, i18n("Starting noatun a second time will cause it to just append items from the start to the current instance."));
+
+ mClearOnOpen = new QCheckBox(i18n("Clear playlist &when opening a file"), this);
+ mClearOnOpen->setChecked(napp->clearOnOpen());
+ QWhatsThis::add(mClearOnOpen, i18n("Opening a file with the global Open menu item will clear the playlist first."));
+
+ mFastVolume=new QCheckBox(i18n("&Use fast hardware volume control"), this);
+ mFastVolume->setChecked(napp->fastMixer());
+ QWhatsThis::add(mFastVolume, i18n("Use the hardware mixer instead of aRts'. It affects all streams, not just Noatun's, but is a little faster."));
+
+ mRemaining=new QCheckBox(i18n("Display &remaining play time"), this);
+ mRemaining->setChecked(napp->displayRemaining());
+ QWhatsThis::add(mRemaining, i18n("Counters count down towards zero, showing remaining time instead of elapsed time."));
+
+ QLabel *titleLabel=new QLabel(i18n("Title &format:"), this);
+ mTitleFormat=new KLineEdit(this);
+ titleLabel->setBuddy(mTitleFormat);
+ mTitleFormat->setText(napp->titleFormat());
+ QWhatsThis::add(mTitleFormat, i18n(
+ "Select a title to use for each file (in the playlist and user interface). "
+ "Each element such as $(title) is replaced with the property with the name "
+ "as given in the parentheses. The properties include, but are not limited to: "
+ "title, author, date, comments and album."));
+
+ QLabel *dlsaver=new QLabel(i18n("&Download folder:"), this);
+ mDlSaver=new KURLRequester(napp->saveDirectory(), this);
+ dlsaver->setBuddy(mDlSaver);
+ connect( mDlSaver, SIGNAL( openFileDialog( KURLRequester * )),
+ this, SLOT( slotRequesterClicked( KURLRequester * )));
+ QWhatsThis::add(mDlSaver, i18n("When opening a non-local file, download it to the selected folder."));
+
+ mPlayOnStartup = new QButtonGroup(1, Horizontal, i18n("Play Behavior on Startup"), this);
+ mPlayOnStartup->setExclusive(true);
+ mPlayOnStartup->insert(
+ new QRadioButton(i18n("Restore &play state"), mPlayOnStartup),
+ NoatunApp::Restore
+ );
+ mPlayOnStartup->insert(
+ new QRadioButton(i18n("Automatically play &first file"), mPlayOnStartup),
+ NoatunApp::Play
+ );
+ mPlayOnStartup->insert(
+ new QRadioButton(i18n("&Do not start playing"), mPlayOnStartup),
+ NoatunApp::DontPlay
+ );
+
+ if (QButton* b = mPlayOnStartup->find(napp->startupPlayMode()))
+ {
+ b->toggle();
+ }
+
+ QGridLayout *layout = new QGridLayout(this, 0, KDialog::spacingHint());
+ layout->setSpacing(KDialog::spacingHint());
+
+ layout->addMultiCellWidget(mLoopList, 0, 0, 0, 1);
+ layout->addMultiCellWidget(mOneInstance, 2, 2, 0, 1);
+ layout->addMultiCellWidget(mClearOnOpen, 4, 4, 0, 1);
+ layout->addMultiCellWidget(mFastVolume, 5, 5, 0, 1);
+ layout->addMultiCellWidget(mRemaining, 6, 6, 0, 1);
+
+ layout->addWidget(titleLabel, 7, 0);
+ layout->addWidget(mTitleFormat, 7, 1);
+
+ layout->addWidget(dlsaver, 8, 0);
+ layout->addWidget(mDlSaver, 8, 1);
+
+ layout->addMultiCellWidget(mPlayOnStartup, 9, 9, 0, 1);
+
+ layout->setRowStretch(10, 1);
+}
+
+
+void General::save()
+{
+ napp->setLoopList(mLoopList->isChecked());
+ napp->setOneInstance(mOneInstance->isChecked());
+ napp->setClearOnOpen(mClearOnOpen->isChecked());
+ napp->setSaveDirectory(mDlSaver->url());
+ napp->setFastMixer(mFastVolume->isChecked());
+ napp->setTitleFormat(mTitleFormat->text());
+ napp->setDisplayRemaining(mRemaining->isChecked());
+ napp->setStartupPlayMode(mPlayOnStartup->selectedId());
+}
+
+
+void General::slotRequesterClicked( KURLRequester * )
+{
+ mDlSaver->fileDialog()->setMode(
+ (KFile::Mode)(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly));
+}
+
+#include "cmodule.moc"
diff --git a/noatun/library/cmodule.h b/noatun/library/cmodule.h
new file mode 100644
index 00000000..6e0e1179
--- /dev/null
+++ b/noatun/library/cmodule.h
@@ -0,0 +1,43 @@
+#ifndef CMODULE_H
+#define CMODULE_H
+
+#include <qframe.h>
+#include <klistview.h>
+#include "noatun/pref.h"
+
+class KListView;
+class QSplitter;
+class QListViewItem;
+class NoatunLibraryInfo;
+class QTextView;
+class QButtonGroup;
+class MimeTypeTree;
+class KLineEdit;
+
+namespace Arts {class TraderOffer;}
+
+class QCheckBox;
+class KURLRequester;
+
+class General : public CModule
+{
+Q_OBJECT
+public:
+ General(QObject *parent=0);
+ virtual void save();
+
+private slots:
+ void slotRequesterClicked( KURLRequester * );
+
+private:
+ QCheckBox *mLoopList, *mOneInstance, *mRememberPositions,
+ *mClearOnOpen, *mFastVolume, *mRemaining;
+ QButtonGroup* mPlayOnStartup;
+ KURLRequester *mDlSaver;
+ KLineEdit *mTitleFormat;
+};
+
+// I'm too lazy to grep - Neil
+#include "pluginmodule.h"
+
+#endif
diff --git a/noatun/library/controls.cpp b/noatun/library/controls.cpp
new file mode 100644
index 00000000..1fb08269
--- /dev/null
+++ b/noatun/library/controls.cpp
@@ -0,0 +1,114 @@
+#include <noatun/controls.h>
+
+L33tSlider::L33tSlider(QWidget * parent, const char * name) :
+ QSlider(parent,name), pressed(false)
+{}
+L33tSlider::L33tSlider(Orientation o, QWidget * parent, const char * name) :
+ QSlider(o,parent,name), pressed(false)
+{}
+L33tSlider::L33tSlider(int minValue, int maxValue, int pageStep, int value,
+ Orientation o, QWidget * parent, const char * name) :
+ QSlider(minValue, maxValue, pageStep, value, o, parent,name), pressed(false)
+{}
+
+bool L33tSlider::currentlyPressed() const
+{
+ return pressed;
+}
+
+void L33tSlider::setValue(int i)
+{
+ if (!pressed)
+ QSlider::setValue(i);
+}
+
+void L33tSlider::mousePressEvent(QMouseEvent*e)
+{
+ if (e->button()!=RightButton)
+ {
+ pressed=true;
+ QSlider::mousePressEvent(e);
+ }
+}
+
+void L33tSlider::mouseReleaseEvent(QMouseEvent*e)
+{
+ pressed=false;
+ QSlider::mouseReleaseEvent(e);
+ emit userChanged(value());
+}
+
+void L33tSlider::wheelEvent(QWheelEvent *e)
+{
+ QSlider::wheelEvent(e);
+ int newValue=value() /* +e->delta()/120 */;
+ if (newValue<minValue())
+ newValue=minValue();
+ else if (newValue>maxValue())
+ newValue=maxValue();
+ setValue(newValue);
+ emit userChanged(newValue);
+}
+
+
+SliderAction::SliderAction(const QString& text, int accel, const QObject *receiver,
+ const char *member, QObject* parent, const char* name )
+ : KAction( text, accel, parent, name )
+{
+ m_receiver = receiver;
+ m_member = member;
+}
+
+int SliderAction::plug( QWidget *w, int index )
+{
+ if (!w->inherits("KToolBar")) return -1;
+
+ KToolBar *toolBar = (KToolBar *)w;
+ int id = KAction::getToolButtonID();
+
+ //Create it.
+ m_slider=new L33tSlider(0, 1000, 100, 0, Horizontal, toolBar);
+ m_slider->setMinimumWidth(10);
+ toolBar->insertWidget(id, 10, m_slider, index );
+
+
+ addContainer( toolBar, id );
+ connect( toolBar, SIGNAL( destroyed() ), this, SLOT( slotDestroyed() ) );
+ toolBar->setItemAutoSized( id, true );
+
+ if (w->inherits( "KToolBar" ))
+ connect(toolBar, SIGNAL(moved(KToolBar::BarPosition)), this, SLOT(toolbarMoved(KToolBar::BarPosition)));
+
+ emit plugged();
+
+ return containerCount() - 1;
+}
+
+void SliderAction::toolbarMoved(KToolBar::BarPosition)
+{
+// I wish this worked :)
+return;
+/*
+ if (pos == KToolBar::Left || pos == KToolBar::Right)
+ {
+ m_slider->setOrientation(Vertical);
+ m_slider->setFixedWidth(m_slider->height());
+ }
+ else
+ {
+ m_slider->setOrientation(Horizontal);
+ m_slider->resize(m_slider->height(), m_slider->height());
+ }
+*/
+}
+
+void SliderAction::unplug( QWidget *w )
+{
+ KToolBar *toolBar = (KToolBar *)w;
+ int idx = findContainer( w );
+
+ toolBar->removeItem( menuId( idx ) );
+ removeContainer( idx );
+}
+
+#include "controls.moc"
diff --git a/noatun/library/conversion.cpp b/noatun/library/conversion.cpp
new file mode 100644
index 00000000..791b5554
--- /dev/null
+++ b/noatun/library/conversion.cpp
@@ -0,0 +1,149 @@
+#include "noatun/conversion.h"
+
+// inherit the aRts routines
+#include <convert.h>
+
+#include <config.h>
+#include <limits.h>
+
+namespace Conversion
+{
+
+void convertMono8ToFloat(unsigned long samples, unsigned char *from, float *to)
+{
+ Arts::convert_mono_8_float(samples, from, to);
+}
+
+void interpolateMono8ToFloat(unsigned long samples, double start, double speed,
+ unsigned char *from, float *to)
+{
+ Arts::interpolate_mono_8_float(samples, start, speed, from, to);
+}
+
+void convertMono16leToFloat(unsigned long samples, unsigned char *from, float *to)
+{
+ Arts::convert_mono_16le_float(samples, from, to);
+}
+
+void interpolateMono16leToFloat(unsigned long samples, double startpos, double speed,
+ unsigned char *from, float *to)
+{
+ Arts::interpolate_mono_16le_float(samples, startpos, speed, from, to);
+}
+
+void convertStereoI8To2Float(unsigned long samples, unsigned char *from,
+ float *left, float *right)
+{
+ Arts::convert_stereo_i8_2float(samples, from, left, right);
+}
+
+
+void interpolateStereoI8To2Float(unsigned long samples, double startpos, double speed,
+ unsigned char *from, float *left, float *right)
+{
+ Arts::interpolate_stereo_i8_2float(samples, startpos, speed, from, left, right);
+}
+
+void convertStereoI16leTo2Float(unsigned long samples, unsigned char *from, float *left,
+ float *right)
+{
+ Arts::convert_stereo_i16le_2float(samples, from, left, right);
+}
+
+void interpolateStereoI16leTo2Float(unsigned long samples, double startpos, double speed,
+ unsigned char *from, float *left, float *right)
+{
+ Arts::interpolate_stereo_i16le_2float(samples, startpos, speed, from, left, right);
+}
+
+void interpolateMonoFloatToFloat(unsigned long samples, double startpos, double speed,
+ float *from, float *to)
+{
+ Arts::interpolate_mono_float_float( samples, startpos, speed, from, to);
+}
+
+void convertStereoIFloatTo2Float(unsigned long samples, float *from, float *left,
+ float *right)
+{
+ Arts::convert_stereo_ifloat_2float(samples, from, left, right);
+}
+
+void interpolateStereoIFloatTo2Float(unsigned long samples, double startpos,
+ double speed, float *from, float *left,
+ float *right)
+{
+ Arts::interpolate_stereo_ifloat_2float(samples, startpos, speed, from, left, right);
+}
+
+void convertMonoFloatTo16le(unsigned long samples, float *from, unsigned char *to)
+{
+ Arts::convert_mono_float_16le(samples, from, to);
+}
+
+void convertStereo2FloatToI16le(unsigned long samples, float *left, float *right,
+ unsigned char *to)
+{
+ Arts::convert_stereo_2float_i16le(samples, left, right, to);
+}
+
+void convertMonoFloatTo8(unsigned long samples, float *from, unsigned char *to)
+{
+ Arts::convert_mono_float_8(samples, from, to);
+}
+
+void convertStereo2FloatToI8(unsigned long samples, float *left, float *right,
+ unsigned char *to)
+{
+ Arts::convert_stereo_2float_i8(samples, left, right, to);
+}
+
+inline void toLittleEndian(unsigned long length, char *buffer)
+{
+#ifdef WORDS_BIGENDIAN
+ swapEndian(length, buffer);
+#else
+ (void)length;
+ (void)buffer;
+#endif
+}
+
+inline void toBigEndian(unsigned long length, char *buffer)
+{
+#ifndef WORDS_BIGENDIAN
+ swapEndian(length, buffer);
+#else
+ (void)length;
+ (void)buffer;
+#endif
+}
+
+void swapEndian(unsigned long length, char *buffer)
+{
+ // if you use a little-endian non intel box, and the ASM
+ // version doesn't work, it's safe to use the C version
+#ifdef __i386__
+ __asm__(
+ "shrl $1,%0\n"
+ "jz .l2\n"
+ ".l1:\n"
+ "rolw $8,(%1)\n"
+ "incl %1\n"
+ "incl %1\n"
+ "decl %0\n"
+ "jnz .l1\n"
+ ".l2:\n"
+ : : "r" (length), "r" (buffer));
+#else
+ while (length--)
+ {
+ register char c=*(buffer+1);
+ *(buffer+1)=*buffer;
+ *(buffer)=c;
+ buffer++; buffer++;
+ --length;
+ }
+#endif
+}
+
+}
+
diff --git a/noatun/library/downloader.cpp b/noatun/library/downloader.cpp
new file mode 100644
index 00000000..734f5e11
--- /dev/null
+++ b/noatun/library/downloader.cpp
@@ -0,0 +1,236 @@
+#include <noatun/downloader.h>
+#include <noatun/app.h>
+#include <assert.h>
+#include <qfile.h>
+#include <qtimer.h>
+#include <kio/job.h>
+#include <klocale.h>
+
+DownloadItem::DownloadItem()
+{
+
+}
+
+DownloadItem::~DownloadItem()
+{
+ dequeue();
+}
+
+bool DownloadItem::isDownloaded() const
+{
+ return true;
+}
+
+QString DownloadItem::localFilename() const
+{
+ return mLocalFilename;
+}
+
+void DownloadItem::setLocalFilename(const QString &filename)
+{
+ mLocalFilename=filename;
+}
+
+void DownloadItem::downloadFinished()
+{
+}
+
+void DownloadItem::downloaded(int )
+{
+}
+
+void DownloadItem::downloadTimeout()
+{
+}
+
+bool DownloadItem::enqueue(const KURL &url)
+{
+ if (url.isLocalFile())
+ {
+ setLocalFilename(url.path());
+ return false;
+ }
+ else
+ {
+ napp->downloader()->enqueue(this, url);
+ return true;
+ }
+}
+
+void DownloadItem::dequeue()
+{
+ napp->downloader()->dequeue(this);
+}
+
+
+
+
+Downloader::Downloader(QObject *parent)
+ : QObject(parent), localfile(0), current(0), mJob(0), mTimeout(0)
+{
+ mStarted=false;
+ mUnstartedQueue=new QPtrList<Downloader::QueueItem>;
+}
+
+Downloader::~Downloader()
+{
+
+}
+
+void Downloader::start()
+{
+ assert(!mStarted);
+ mStarted=true;
+ if (current)
+ emit enqueued(current->notifier, current->file);
+
+ for (QPtrListIterator<Downloader::QueueItem> i(*mUnstartedQueue); i.current(); ++i)
+ {
+ (*i)->notifier->mLocalFilename = (*i)->local;
+ mQueue.append(*i);
+ emit enqueued((*i)->notifier, (*i)->file);
+ }
+
+ delete mUnstartedQueue;
+ mUnstartedQueue=0;
+ QTimer::singleShot(0, this, SLOT(getNext()));
+}
+
+static QString nonExistantFile(const QString &file)
+{
+ if (file.right(1)=="/") return i18n("Unknown");
+ int i=0;
+ QString f(file);
+ while (QFile(f).exists())
+ {
+ i++;
+ f=file;
+ f.insert(f.findRev('.'), '_'+QString::number(i));
+ }
+ return f;
+}
+
+QString Downloader::enqueue(DownloadItem *notifier, const KURL &file)
+{
+ if (file.isLocalFile()) return 0;
+ QueueItem *i=new QueueItem;
+ i->notifier=notifier;
+ i->file=file;
+
+ if (!mStarted)
+ {
+ i->local=notifier->mLocalFilename;
+ if (!notifier->localFilename().length())
+ {
+ i->local =
+ nonExistantFile(napp->saveDirectory()+'/'+file.fileName());
+ }
+ mUnstartedQueue->append(i);
+ return i->local;
+ }
+
+
+ if (!notifier->localFilename().length())
+ {
+ notifier->mLocalFilename=
+ i->local =
+ nonExistantFile(napp->saveDirectory()+'/'+file.fileName());
+ }
+ else
+ {
+ i->local = notifier->mLocalFilename;
+ }
+
+ mQueue.append(i);
+ QTimer::singleShot(0, this, SLOT(getNext()));
+ emit enqueued(notifier, file);
+ return i->local;
+}
+
+void Downloader::dequeue(DownloadItem *notifier)
+{
+ if (current && notifier==current->notifier)
+ {
+ mJob->kill();
+ jobDone(mJob);
+ return;
+ }
+ for (QPtrListIterator<Downloader::QueueItem> i(mQueue); i.current(); ++i)
+ {
+ if ((*i)->notifier==notifier)
+ {
+ mQueue.removeRef(*i);
+ if (mStarted)
+ emit dequeued(notifier);
+ delete *i;
+ return;
+ }
+ }
+}
+
+void Downloader::getNext()
+{
+ if (current) return;
+ if (!mStarted) return;
+ if (mQueue.isEmpty()) return;
+ current=mQueue.take(0);
+
+ // open the QFile
+ localfile=new QFile(current->local);
+ localfile->open(IO_ReadWrite | IO_Append);
+
+ mJob= KIO::get(current->file, true, false);
+ connect(mJob, SIGNAL(data(KIO::Job*, const QByteArray&)), SLOT(data(KIO::Job*, const QByteArray&)));
+ connect(mJob, SIGNAL(result(KIO::Job*)), SLOT(jobDone(KIO::Job*)));
+ connect(mJob, SIGNAL(percent(KIO::Job*, unsigned long)), SLOT(percent(KIO::Job*, unsigned long)));
+
+ if (mTimeout)
+ delete mTimeout;
+ mTimeout=new QTimer(this);
+ mTimeout->start(30000, true);
+ connect(mTimeout, SIGNAL(timeout()), SLOT(giveUpWithThisDownloadServerIsRunningNT()));
+}
+
+void Downloader::data(KIO::Job *, const QByteArray &data)
+{
+ localfile->writeBlock(data);
+ localfile->flush();
+ delete mTimeout;
+ mTimeout=0;
+}
+
+void Downloader::jobDone(KIO::Job *)
+{
+ delete mTimeout;
+ mTimeout=0;
+ current->notifier->downloadFinished();
+ if (mStarted)
+ emit dequeued(current->notifier);
+ delete current;
+ current=0;
+ mJob=0;
+ getNext();
+}
+
+void Downloader::giveUpWithThisDownloadServerIsRunningNT()
+{
+ delete mTimeout;
+ mTimeout=0;
+ if (!current) return;
+ DownloadItem *old=current->notifier;
+ if (!old) return;
+ old->downloadTimeout();
+ current=0;
+ mJob=0;
+ delete old;
+ getNext();
+}
+
+void Downloader::percent( KIO::Job *, unsigned long percent)
+{
+ if (current && current->notifier)
+ current->notifier->downloaded((int)percent);
+}
+
+#include "downloader.moc"
+
diff --git a/noatun/library/effects.cpp b/noatun/library/effects.cpp
new file mode 100644
index 00000000..3c6c601e
--- /dev/null
+++ b/noatun/library/effects.cpp
@@ -0,0 +1,285 @@
+#include "effects.h"
+#include "engine.h"
+#include <common.h>
+#include <dynamicrequest.h>
+#include <artsflow.h>
+#include <app.h>
+#include <player.h>
+#include <soundserver.h>
+#include <noatunarts.h>
+#include <qlayout.h>
+
+#include <config.h>
+
+#define HAS_ARTSVERSION_H
+
+#ifdef HAS_ARTSVERSION_H
+#include <artsgui.h>
+#include <kartswidget.h>
+#endif
+
+#define engine napp->player()->engine()
+#define server (*(engine->server()))
+#define stack (*engine->effectStack())
+
+using namespace std;
+using namespace Arts;
+
+class EffectConfigWidget : public QWidget
+{
+public:
+ EffectConfigWidget(Effect *e, QWidget *parent=0)
+ : QWidget(parent), mEf(e)
+ {}
+
+ virtual ~EffectConfigWidget()
+ {
+ mEf->mConfig=0;
+ }
+
+private:
+ Effect *mEf;
+};
+
+
+Effect::Effect(const char *name)
+ : mId(0), mName(name), mConfig(0)
+{
+ mEffect=new StereoEffect;
+ *mEffect=DynamicCast(server.createObject(std::string(name)));
+ napp->effects()->mItems.append(this);
+}
+
+long Effect::id() const
+{
+ return mId;
+}
+
+StereoEffect *Effect::effect() const
+{
+ return mEffect;
+}
+
+Effect *Effect::after() const
+{
+ QPtrList<Effect> effects=napp->effects()->effects();
+ QPtrListIterator<Effect> i(effects);
+ for(; i.current(); ++i)
+ if ((*i)->id()==mId)
+ {
+ ++i;
+ if (*i)
+ return *i;
+ }
+
+ return 0;
+}
+
+Effect *Effect::before() const
+{
+ QPtrList<Effect> effects=napp->effects()->effects();
+ QPtrListIterator<Effect> i(effects);
+ for(; i.current(); ++i)
+ if ((*i)->id()==mId)
+ {
+ --i;
+ if (*i)
+ return *i;
+ }
+
+ return 0;
+}
+
+QCString Effect::name() const
+{
+ return mName;
+}
+
+QString Effect::title() const
+{
+ return clean(mName);
+}
+
+QString Effect::clean(const QCString &name)
+{
+ int pos=name.findRev("::");
+ if (pos>0)
+ return name.right(name.length()-pos-2);
+ return name;
+}
+
+bool Effect::isNull() const
+{
+ return effect()->isNull();
+}
+
+QWidget *Effect::configure(bool /*friendly*/)
+{
+#ifdef HAS_ARTSVERSION_H
+ if (mConfig) return mConfig;
+ if (!configurable()) return 0;
+
+ GenericGuiFactory factory;
+ Widget gui = factory.createGui(*mEffect);
+
+ if(!gui.isNull())
+ {
+ mConfig=new EffectConfigWidget(this);
+ mConfig->setCaption(title());
+
+ QBoxLayout *l=new QHBoxLayout(mConfig);
+ l->add(new KArtsWidget(gui, mConfig));
+ l->freeze();
+ }
+
+ return mConfig;
+#else
+ return 0;
+#endif
+}
+
+bool Effect::configurable() const
+{
+#ifdef HAS_ARTSVERSION_H
+ TraderQuery query;
+ query.supports("Interface","Arts::GuiFactory");
+ query.supports("CanCreate", mEffect->_interfaceName());
+
+ vector<TraderOffer> *queryResults = query.query();
+ bool yes= queryResults->size();
+ delete queryResults;
+
+ return yes;
+#else
+ return 0;
+#endif
+}
+
+Effect::~Effect()
+{
+ delete mConfig;
+ napp->effects()->remove(this, false);
+ emit napp->effects()->deleting(this);
+ delete mEffect;
+ napp->effects()->mItems.removeRef(this);
+}
+
+
+Effects::Effects()
+{
+ mItems.setAutoDelete(false);
+}
+
+bool Effects::insert(const Effect *after, Effect *item)
+{
+ if (!item) return false;
+ if (item->id()) return false;
+ if (item->isNull()) return false;
+ long i;
+ item->effect()->start();
+
+ if (!after)
+ i=stack.insertTop(*item->effect(), (const char*)item->name());
+ else
+ i=stack.insertAfter(after->id(), *item->effect(), (const char*)item->name());
+ if (!i)
+ {
+ item->effect()->stop();
+ return false;
+ }
+
+ item->mId=i;
+ emit added(item);
+ return true;
+}
+
+bool Effects::append(Effect *item)
+{
+ if (!item) return false;
+ if (item->id()) return false;
+ if (item->isNull()) return false;
+
+ item->effect()->start();
+ item->mId=stack.insertBottom(*item->effect(), (const char*)item->name());
+ if (!item->mId)
+ {
+ item->effect()->stop();
+ return false;
+ }
+ emit added(item);
+ return true;
+}
+
+void Effects::move(const Effect *after, Effect *item)
+{
+ if (!item) return;
+ if (!item->id()) return;
+ long id=after ? after->id() : 0;
+ stack.move(id, item->id());
+ emit moved(item);
+}
+
+void Effects::remove(Effect *item, bool del)
+{
+ if (!item) return;
+ if (!item->id()) return;
+
+ stack.remove(item->id());
+ item->effect()->stop();
+ item->mId=0;
+ removed(item);
+
+ if (del)
+ delete item;
+}
+
+void Effects::removeAll(bool del)
+{
+ for (QPtrListIterator<Effect> i(mItems); i.current(); ++i)
+ if ((*i)->id())
+ remove(*i, del);
+}
+
+QStrList Effects::available() const
+{
+ QStrList val;
+ Arts::TraderQuery query;
+ query.supports("Interface", "Arts::StereoEffect");
+ query.supports("Interface", "Arts::SynthModule");
+ query.supports("Use", "directly");
+ vector<Arts::TraderOffer> *offers = query.query();
+ for (vector<Arts::TraderOffer>::iterator i=offers->begin(); i!=offers->end(); i++)
+ {
+ Arts::TraderOffer &offer=*i;
+ QCString name = offer.interfaceName().c_str();
+ val.append(name);
+ }
+ delete offers;
+ return val;
+}
+
+Effect *Effects::findId(long id) const
+{
+ for (QPtrListIterator<Effect> i(mItems); i.current(); ++i)
+ if ((*i)->id()==id)
+ return *i;
+ return 0;
+}
+
+QPtrList<Effect> Effects::effects() const
+{
+ vector<long> *items=stack.effectList();
+ QPtrList<Effect> effects;
+ for (vector<long>::iterator i=items->begin();i!=items->end();i++)
+ if (Effect *e=findId(*i))
+ effects.append(e);
+
+ delete items;
+ return effects;
+}
+
+#undef server
+#undef stack
+#undef engine
+
+#include "effects.moc"
diff --git a/noatun/library/effectview.cpp b/noatun/library/effectview.cpp
new file mode 100644
index 00000000..73af1fc2
--- /dev/null
+++ b/noatun/library/effectview.cpp
@@ -0,0 +1,284 @@
+// Copyright (c) 2001 Charles Samuels <charles@kde.org>
+// Copyright (c) 2001 Neil Stevens <neil@qualityassistant.com>
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "effects.h"
+#include "effectview.h"
+#include "app.h"
+
+#include <kcombobox.h>
+#include <kdialog.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <qdragobject.h>
+#include <qheader.h>
+#include <qhgroupbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qtextedit.h>
+#include <qtoolbutton.h>
+#include <qvgroupbox.h>
+#include <qwhatsthis.h>
+
+class EffectListItem : public QListViewItem
+{
+public:
+ EffectListItem(QListView *parent, QListViewItem *after, Effect *e)
+ : QListViewItem(parent, after, e->title()), mEffect(e)
+ {
+ }
+
+ Effect *effect() const { return mEffect; }
+
+private:
+ Effect *mEffect;
+};
+
+EffectList::EffectList(QWidget *parent)
+ : KListView(parent)
+{
+}
+
+bool EffectList::acceptDrag(QDropEvent *event) const
+{
+ return QCString(event->format()) == "application/x-noatun-effectdrag";
+}
+
+QDragObject *EffectList::dragObject() const
+{
+ if (!currentItem()) return 0;
+ return new QStoredDrag("application/x-noatun-effectdrag", (QWidget*)this);
+}
+
+EffectView::EffectView()
+ : KDialogBase((QWidget*)0L, 0, false, i18n("Effects"), Help | Close, Close, true)
+ , initialized(false)
+{
+}
+
+void EffectView::show()
+{
+ init();
+ KDialogBase::show();
+}
+
+namespace
+{
+QToolButton *newButton(const QIconSet &iconSet, const QString &textLabel, QObject *receiver, const char * slot, QWidget *parent, const char *name = 0)
+{
+ QToolButton *button = new QToolButton(parent, name);
+ button->setIconSet(iconSet);
+ button->setTextLabel(textLabel, true);
+ QObject::connect(button, SIGNAL(clicked()), receiver, slot);
+ button->setFixedSize(QSize(22, 22));
+ return button;
+}
+}
+
+void EffectView::init(void)
+{
+ if(initialized) return;
+ initialized = true;
+
+ setCaption(i18n("Effects - Noatun"));
+ setIcon(SmallIcon("effect"));
+
+ // Create widgets and layouts
+ QFrame *box = makeMainWidget();
+ QVBoxLayout *boxLayout = new QVBoxLayout(box, 0, KDialog::spacingHint());
+
+ // Available
+ QVGroupBox *topBox = new QVGroupBox(i18n("Available Effects"), box);
+ topBox->setInsideSpacing(KDialog::spacingHint());
+
+ QFrame *topTopFrame = new QFrame(topBox);
+ QHBoxLayout *topTopLayout = new QHBoxLayout(topTopFrame, 0, KDialog::spacingHint());
+ topTopLayout->setAutoAdd(true);
+ available = new KComboBox(false, topTopFrame);
+ QToolButton *add = newButton(BarIconSet("down", KIcon::SizeSmall), i18n("Add"), this, SLOT(addEffect()), topTopFrame);
+
+ // Active
+ QHGroupBox *bottomBox = new QHGroupBox(i18n("Active Effects"), box);
+ bottomBox->setInsideSpacing(KDialog::spacingHint());
+
+ active = new EffectList(bottomBox);
+
+ boxLayout->addWidget(topBox);
+ boxLayout->addWidget(bottomBox);
+
+ // Fill and configure widgets
+ available->insertStrList(napp->effects()->available());
+
+ active->setAcceptDrops(true);
+ active->addColumn("");
+ active->header()->hide();
+ active->setSorting(-1);
+ active->setDropVisualizer(true);
+ active->setItemsMovable(true);
+ active->setSelectionMode(QListView::Single);
+ active->setDragEnabled(true);
+ connect(active, SIGNAL(dropped(QDropEvent *, QListViewItem *)), SLOT(activeDrop(QDropEvent *, QListViewItem *)));
+
+ // when a new effect is added
+ connect(napp->effects(), SIGNAL(added(Effect *)), SLOT(added(Effect *)));
+ connect(napp->effects(), SIGNAL(removed(Effect *)), SLOT(removed(Effect *)));
+ connect(napp->effects(), SIGNAL(moved(Effect *)), SLOT(moved(Effect *)));
+
+ available->setCurrentItem(0);
+
+ connect(active, SIGNAL(currentChanged(QListViewItem *)), SLOT(activeChanged(QListViewItem *)));
+ active->setCurrentItem(0);
+
+ // the buttons
+ QFrame *bottomLeftFrame = new QFrame(bottomBox);
+ QVBoxLayout *bottomLeftLayout = new QVBoxLayout(bottomLeftFrame, 0, KDialog::spacingHint());
+ up = newButton(BarIconSet("up", KIcon::SizeSmall), i18n("Up"), this, SLOT(moveUp()), bottomLeftFrame);
+ down = newButton(BarIconSet("down", KIcon::SizeSmall), i18n("Down"), this, SLOT(moveDown()), bottomLeftFrame);
+ configure = newButton(BarIconSet("configure", KIcon::SizeSmall), i18n("Configure"), this, SLOT(configureEffect()), bottomLeftFrame);
+ remove = newButton(BarIconSet("remove", KIcon::SizeSmall), i18n("Remove"), this, SLOT(removeEffect()), bottomLeftFrame);
+ bottomLeftLayout->addWidget(up);
+ bottomLeftLayout->addWidget(down);
+ bottomLeftLayout->addWidget(configure);
+ bottomLeftLayout->addWidget(remove);
+ bottomLeftLayout->addStretch();
+
+
+ activeChanged(active->currentItem());
+
+ // Inline documentation
+ QWhatsThis::add(available, i18n("This shows all available effects.\n\nTo activate a plugin, drag files from here to the active pane on the right."));
+ QWhatsThis::add(add, i18n("This will place the selected effect at the bottom of your chain."));
+ QWhatsThis::add(active, i18n("This shows your effect chain. Noatun supports an unlimited amount of effects in any order. You can even have the same effect twice.\n\nDrag the items to and from here to add and remove them, respectively. You may also reorder them with drag-and-drop. These actions can also be performed with the buttons to the right."));
+ QWhatsThis::add(up, i18n("Move the currently selected effect up in the chain."));
+ QWhatsThis::add(down, i18n("Move the currently selected effect down in the chain."));
+ QWhatsThis::add(configure, i18n("Configure the currently selected effect.\n\nYou can change things such as intensity from here."));
+ QWhatsThis::add(remove, i18n("This will remove the selected effect from your chain."));
+
+ resize(300, 400);
+}
+
+void EffectView::activeDrop(QDropEvent *, QListViewItem *pafter)
+{
+ EffectListItem *after(static_cast<EffectListItem*>(pafter));
+ napp->effects()->move(after ? after->effect() : 0,
+ static_cast<EffectListItem*>(active->currentItem())->effect());
+ activeChanged(active->currentItem());
+}
+
+QListViewItem *EffectView::toListItem(Effect *e) const
+{
+ for(QListViewItem *i = active->firstChild(); i; i = i->itemBelow())
+ if(static_cast<EffectListItem*>(i)->effect() == e)
+ return i;
+ return 0;
+}
+
+void EffectView::added(Effect *item)
+{
+ new EffectListItem(active, toListItem(item->before()), item);
+ activeChanged(active->currentItem());
+}
+
+void EffectView::moved(Effect *item)
+{
+ delete toListItem(item);
+ added(item);
+}
+
+void EffectView::removed(Effect *item)
+{
+ delete toListItem(item);
+ activeChanged(active->currentItem());
+}
+
+void EffectView::moveDown()
+{
+ Effect *e = static_cast<EffectListItem*>(active->currentItem())->effect();
+
+ if(e->after())
+ napp->effects()->move(e->after(), e);
+ active->setCurrentItem(toListItem(e));
+ active->setSelected(toListItem(e), true);
+ activeChanged(active->currentItem());
+}
+
+void EffectView::moveUp()
+{
+ Effect *e = static_cast<EffectListItem*>(active->currentItem())->effect();
+ if (e->before())
+ {
+ if (e->before()->before())
+ napp->effects()->move(e->before()->before(), e);
+ else
+ napp->effects()->move(0, e);
+ }
+ active->setCurrentItem(toListItem(e));
+ active->setSelected(toListItem(e), true);
+ activeChanged(active->currentItem());
+}
+
+void EffectView::removeEffect()
+{
+ EffectListItem *item = static_cast<EffectListItem*>(active->currentItem());
+ napp->effects()->remove(item->effect());
+ activeChanged(active->currentItem());
+}
+
+void EffectView::addEffect()
+{
+ // local8Bit() and arts makes me nervous
+ napp->effects()->append(new Effect(available->currentText().local8Bit()));
+ activeChanged(active->currentItem());
+}
+
+void EffectView::configureEffect()
+{
+ Effect *e = static_cast<EffectListItem*>(active->currentItem())->effect();
+ if(!e) return;
+
+ QWidget *c = e->configure();
+ if(c) c->show();
+}
+
+void EffectView::activeChanged(QListViewItem *i)
+{
+ if(i)
+ {
+ up->setEnabled(i->itemAbove());
+ down->setEnabled(i->itemBelow());
+ remove->setEnabled(true);
+
+ Effect *e = static_cast<EffectListItem*>(active->currentItem())->effect();
+ configure->setEnabled(e->configurable());
+ }
+ else
+ {
+ up->setEnabled(false);
+ down->setEnabled(false);
+ remove->setEnabled(false);
+ configure->setEnabled(false);
+ }
+}
+
+#include "effectview.moc"
diff --git a/noatun/library/effectview.h b/noatun/library/effectview.h
new file mode 100644
index 00000000..1cdb1907
--- /dev/null
+++ b/noatun/library/effectview.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2001 Charles Samuels <charles@kde.org>
+// Copyright (c) 2001 Neil Stevens <neil@qualityassistant.com>
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef EFFECTVIEW_H
+#define EFFECTVIEW_H
+
+#include <kdialogbase.h>
+#include <klistview.h>
+
+class EffectList;
+class Effect;
+class KComboBox;
+class QToolButton;
+
+class EffectView : public KDialogBase
+{
+Q_OBJECT
+public:
+ EffectView();
+
+ virtual void show();
+
+public slots:
+ void added(Effect *);
+ void removed(Effect *);
+ void moved(Effect *);
+
+ // buttons
+ void moveDown();
+ void moveUp();
+ void removeEffect();
+ void addEffect();
+ void configureEffect();
+
+ void activeChanged(QListViewItem *);
+
+protected slots:
+ void activeDrop(QDropEvent *, QListViewItem *);
+
+private:
+ QListViewItem *toListItem(Effect *) const;
+
+ void init(void);
+ bool initialized;
+
+ KComboBox *available;
+
+ QToolButton *up, *down, *configure, *remove;
+
+ EffectList *active;
+};
+
+class EffectList : public KListView
+{
+Q_OBJECT
+public:
+ EffectList(QWidget *parent);
+ virtual bool acceptDrag(QDropEvent *) const;
+ virtual QDragObject *dragObject() const;
+};
+
+#endif
diff --git a/noatun/library/engine.cpp b/noatun/library/engine.cpp
new file mode 100644
index 00000000..e937fa7f
--- /dev/null
+++ b/noatun/library/engine.cpp
@@ -0,0 +1,589 @@
+// $Id$
+
+#include <noatun/engine.h>
+#include <noatun/equalizer.h>
+#include <noatun/player.h>
+#include <noatun/plugin.h>
+#include <noatun/effects.h>
+#include "titleproxy.h"
+
+#include <string.h>
+
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <qfile.h>
+#include <qdir.h>
+#include <sys/wait.h>
+#include <kplayobject.h>
+#include <kplayobjectfactory.h>
+
+#include <dynamicrequest.h>
+#include <soundserver.h>
+#include <kmedia2.h>
+#include <flowsystem.h>
+#include <noatunarts.h>
+#include <connect.h>
+#include <cpuinfo.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <math.h>
+
+
+static Arts::PlayObject nullPO() { return Arts::PlayObject::null(); }
+
+#define HARDWARE_VOLUME
+
+#if defined(__osf__)
+#undef HARDWARE_VOLUME
+#elif defined(__linux__)
+#include <sys/soundcard.h>
+#elif defined(__FreeBSD__)
+#include <sys/soundcard.h>
+#elif defined(__NetBSD__)
+#include <soundcard.h>
+#elif defined(___SOMETHING_UNKNOWN__)
+#include <sys/soundcard.h>
+#elif defined(_UNIXWARE)
+#include <sys/soundcard.h>
+#else
+#undef HARDWARE_VOLUME
+#endif
+
+using namespace std;
+
+namespace VolumeControls
+{
+ struct VC
+ {
+ virtual ~VC() {}
+ virtual void setVolume(int percent)=0;
+ virtual int volume() const =0;
+ };
+
+#ifdef HARDWARE_VOLUME
+
+ struct Hardware : public VC
+ {
+ Hardware(Engine *)
+ {
+ if ((fd=::open( "/dev/mixer", O_RDWR)) < 0)
+ {
+ return;
+ }
+ else
+ {
+#define ERROR { fd=-1; return; }
+ int devmask, recmask, i_recsrc, stereodevs;
+ // Mixer is open. Now define properties
+ if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) ERROR
+ if (ioctl(fd, SOUND_MIXER_READ_RECMASK, &recmask) == -1) ERROR
+ if (ioctl(fd, SOUND_MIXER_READ_RECSRC, &i_recsrc) == -1) ERROR
+ if (ioctl(fd, SOUND_MIXER_READ_STEREODEVS, &stereodevs) == -1) ERROR
+ if (!devmask) ERROR
+#undef ERROR
+ }
+
+ }
+
+ virtual ~Hardware() { ::close(fd); }
+
+ virtual void setVolume(int percent)
+ {
+ percent=percent+(percent<<8);
+ ioctl(fd, MIXER_WRITE(4), &percent);
+ }
+
+ virtual int volume() const
+ {
+ int volume, left, right;
+ left=100;
+ if (ioctl(fd, MIXER_READ(4), &volume) != -1)
+ {
+ left=volume & 0x7f;
+ right=(volume>>8) & 0x7f;
+ left=(left+right)/2;
+ }
+ return left;
+ }
+ private:
+ int fd;
+ };
+#endif
+
+ struct SoftwareSSE : public VC
+ {
+ SoftwareSSE(Engine *e) : mVolume(100)
+ {
+ volumeControl = Arts::DynamicCast(e->server()->createObject("Noatun::StereoVolumeControlSSE"));
+
+ if(volumeControl.isNull())
+ volumeControl = Arts::DynamicCast(e->server()->createObject("Noatun::StereoVolumeControl"));
+
+ volumeControl.start();
+
+ id=e->globalEffectStack()->insertBottom(volumeControl,"Volume Control");
+ }
+
+ virtual void setVolume(int percent)
+ {
+ mVolume=percent;
+ float vol = pow(2.0, ((400 - (100-percent)*12)/200.0))/4.0;
+ if (percent == 0) vol = 0.0;
+ if (!volumeControl.isNull())
+ volumeControl.percent(vol);
+ }
+
+ virtual int volume() const
+ {
+ return mVolume;
+ }
+
+ private:
+ Noatun::StereoVolumeControlSSE volumeControl;
+ long id;
+ int mVolume;
+ };
+
+ struct Software : public VC
+ {
+ Software(Engine *e) : mVolume(100)
+ {
+ volumeControl = Arts::DynamicCast(e->server()->createObject("Noatun::StereoVolumeControl"));
+ volumeControl.start();
+
+ id=e->globalEffectStack()->insertBottom(volumeControl,"Volume Control");
+ }
+
+ virtual void setVolume(int percent)
+ {
+ mVolume=percent;
+ if (!volumeControl.isNull())
+ volumeControl.percent((float)percent/100.0);
+ }
+
+ virtual int volume() const
+ {
+ return mVolume;
+ }
+
+ private:
+ Noatun::StereoVolumeControl volumeControl;
+ long id;
+ int mVolume;
+ };
+
+ static VC *volumeControl(Engine *e)
+ {
+#ifdef HARDWARE_VOLUME
+ if (napp->fastMixer())
+ return new Hardware(e);
+ else
+ {
+#endif
+ if (!getenv("NO_SSE") && Arts::CpuInfo::flags()&Arts::CpuInfo::CpuSSE)
+ return new SoftwareSSE(e);
+ else
+ return new Software(e);
+
+#ifdef HARDWARE_VOLUME
+ }
+#endif
+ }
+}
+
+
+class Engine::EnginePrivate
+{
+public:
+ EnginePrivate()
+ : playobj(0),
+ server(Arts::SoundServerV2::null()),
+ globalEffectStack(Noatun::StereoEffectStack::null()),
+ effectsStack(Noatun::StereoEffectStack::null()),
+ visStack(Noatun::StereoEffectStack::null()),
+ volumeControl(0), session(Noatun::Session::null()),
+ pProxy(0)
+ {
+ }
+
+ ~EnginePrivate()
+ {
+ visStack=Noatun::StereoEffectStack::null();
+ }
+
+ KDE::PlayObject *playobj;
+ Arts::SoundServerV2 server;
+ Arts::Synth_AMAN_PLAY amanPlay;
+
+ // globalEffectStack
+ // |- effectsStack
+ // |- Effects...
+ // |- visStack
+ // |- Visualizations
+ // |- Volume Control
+ //
+ Noatun::StereoEffectStack globalEffectStack;
+ Noatun::StereoEffectStack effectsStack;
+ Noatun::StereoEffectStack visStack;
+ Noatun::Equalizer equalizer;
+
+ int volumeID;
+ VolumeControls::VC *volumeControl;
+ Noatun::Session session;
+ TitleProxy::Proxy *pProxy;
+};
+
+Arts::SoundServerV2 *Engine::server() const { return &d->server;}
+Arts::PlayObject Engine::playObject() const { return d->playobj ? d->playobj->object() : nullPO(); }
+Arts::SoundServerV2 *Engine::simpleSoundServer() const { return &d->server; }
+Noatun::StereoEffectStack *Engine::effectStack() const { return &d->effectsStack; }
+Noatun::StereoEffectStack *Engine::visualizationStack() const { return &d->visStack; }
+Noatun::StereoEffectStack *Engine::globalEffectStack() const { return &d->globalEffectStack; }
+Noatun::Equalizer *Engine::equalizer() const { return &d->equalizer; }
+Noatun::Session *Engine::session() const { return &d->session; }
+
+Engine::Engine(QObject *parent) : QObject(parent, "Engine"), mPlay(false)
+{
+ d=new EnginePrivate;
+ // Connect to aRts
+ if (!initArts())
+ {
+ KMessageBox::error(0, i18n("There was an error communicating to the aRts daemon."), i18n("aRts error"));
+ exit(0);
+ }
+
+}
+
+Engine::~Engine()
+{
+ stop();
+ delete d->volumeControl;
+ d->server=Arts::SoundServerV2::null();
+ delete d;
+}
+
+void Engine::setInitialized()
+{
+ mPlay=true;
+}
+
+bool Engine::initialized() const
+{
+ return mPlay;
+}
+
+bool Engine::open(const PlaylistItem &file)
+{
+ if(!initArts())
+ return false;
+
+ d->playobj = 0;
+
+ KDE::PlayObjectFactory factory(d->server);
+
+ if (file.isProperty("stream_") && file.url().protocol() == "http")
+ {
+ deleteProxy();
+ d->pProxy = new TitleProxy::Proxy(KURL(file.property("stream_")));
+ d->playobj = factory.createPlayObject(d->pProxy->proxyUrl(), false);
+
+ connect(d->playobj, SIGNAL(destroyed()), this, SLOT(deleteProxy()));
+ connect(
+ d->pProxy, SIGNAL(
+ metaData(
+ const QString &, const QString &,
+ const QString &, const QString &,
+ const QString &, const QString &)),
+ this, SIGNAL(
+ receivedStreamMeta(const QString &, const QString &,
+ const QString &, const QString &,
+ const QString &, const QString &))
+ );
+ connect(d->pProxy, SIGNAL(proxyError()), this, SLOT(slotProxyError()));
+ }
+ else
+ {
+ d->playobj = factory.createPlayObject(file.url(), false);
+ }
+
+ if (!d->playobj || d->playobj->isNull())
+ {
+ kdDebug(66666) << k_funcinfo <<
+ "No playobject for '" << file.url().prettyURL() << "'" << endl;
+ delete d->playobj;
+ d->playobj=0;
+ emit playingFailed();
+ return false;
+ }
+
+ if ( !d->playobj->object().isNull() )
+ {
+ connectPlayObject();
+ }
+ else
+ {
+ connect( d->playobj, SIGNAL( playObjectCreated() ), this, SLOT( connectPlayObject() ) );
+ }
+
+ if (mPlay)
+ d->playobj->play();
+
+ return true;
+}
+
+void Engine::slotProxyError()
+{
+ kdDebug(66666) << k_funcinfo << endl;
+ emit playingFailed();
+ deleteProxy();
+}
+
+void Engine::deleteProxy()
+{
+ delete d->pProxy;
+ d->pProxy = 0;
+}
+
+void Engine::connectPlayObject()
+{
+ if (d->playobj->object().isNull())
+ {
+ emit playingFailed();
+ return;
+ }
+ d->playobj->object()._node()->start();
+
+ // TODO: check for existence of left & right streams
+ Arts::connect(d->playobj->object(),"left",d->globalEffectStack,"inleft");
+ Arts::connect(d->playobj->object(),"right",d->globalEffectStack,"inright");
+
+ emit aboutToPlay();
+}
+
+bool Engine::play()
+{
+ if (!mPlay) return true;
+ if(!d->playobj)
+ return false;
+ d->playobj->play();
+ return true;
+}
+
+void Engine::pause()
+{
+ d->playobj->pause();
+}
+
+void Engine::stop()
+{
+ if(!d->playobj) return;
+
+ d->playobj->halt();
+ delete d->playobj;
+ d->playobj=0;
+}
+
+void Engine::seek(int msec) // pass time in msecs
+{
+ if(!d->playobj) return;
+
+ Arts::poTime t;
+
+ t.custom = 0.0;
+ t.ms = (long) msec % 1000;
+ t.seconds = (long) (msec - t.ms) / 1000;
+
+ if(d->playobj)
+ d->playobj->seek(t);
+}
+
+int Engine::position()
+{
+ if(!d->playobj) return -1;
+
+ Arts::poTime time(d->playobj->currentTime());
+ return (int)(time.ms + (time.seconds*1000)); // return position in milliseconds
+}
+
+int Engine::length()
+{
+ if(!d->playobj) return -1;
+ if (!(d->playobj->capabilities() & Arts::capSeek))
+ return -1;
+
+ Arts::poTime time(d->playobj->overallTime());
+ return (int)(time.ms + (time.seconds*1000)); // return track-length in milliseconds
+}
+
+int Engine::state()
+{
+ if(d->playobj)
+ return d->playobj->state();
+ else
+ return Arts::posIdle;
+}
+
+void Engine::setVolume(int percent)
+{
+ if (percent>100)
+ percent=100;
+ if (percent<0)
+ percent=0;
+ d->volumeControl->setVolume(percent);
+}
+
+int Engine::volume() const
+{
+ return d->volumeControl->volume();
+}
+
+void Engine::useHardwareMixer(bool)
+{
+ delete d->volumeControl;
+ d->volumeControl=VolumeControls::volumeControl(this);
+}
+
+bool Engine::initArts()
+{
+ if ( d->server.isNull() || d->server.error() )
+ {
+ d->server = Arts::Reference("global:Arts_SoundServerV2");
+ int volume = d->volumeControl ? d->volumeControl->volume() : -1;
+ delete d->volumeControl;
+ d->volumeControl=0;
+
+ if( d->server.isNull() || d->server.error() )
+ {
+ // aRts seems not to be running, let's try to run it
+ // First, let's read the configuration as in kcmarts
+ KConfig config("kcmartsrc");
+ QCString cmdline;
+
+ config.setGroup("Arts");
+
+ bool rt = config.readBoolEntry("StartRealtime",false);
+ bool x11Comm = config.readBoolEntry("X11GlobalComm",false);
+
+ // put the value of x11Comm into .mcoprc
+ {
+ KConfig X11CommConfig(QDir::homeDirPath()+"/.mcoprc");
+
+ if(x11Comm)
+ X11CommConfig.writeEntry("GlobalComm", "Arts::X11GlobalComm");
+ else
+ X11CommConfig.writeEntry("GlobalComm", "Arts::TmpGlobalComm");
+
+ X11CommConfig.sync();
+ }
+
+ cmdline = QFile::encodeName(KStandardDirs::findExe(QString::fromLatin1("kdeinit_wrapper")));
+ cmdline += " ";
+
+ if (rt)
+ cmdline += QFile::encodeName(KStandardDirs::findExe(
+ QString::fromLatin1("artswrapper")));
+ else
+ cmdline += QFile::encodeName(KStandardDirs::findExe(
+ QString::fromLatin1("artsd")));
+
+ cmdline += " ";
+ cmdline += config.readEntry("Arguments","-F 10 -S 4096 -s 60 -m artsmessage -l 3 -f").utf8();
+
+ int status=::system(cmdline);
+
+ if ( status!=-1 && WIFEXITED(status) )
+ {
+ // We could have a race-condition here.
+ // The correct way to do it is to make artsd fork-and-exit
+ // after starting to listen to connections (and running artsd
+ // directly instead of using kdeinit), but this is better
+ // than nothing.
+ int time = 0;
+ do
+ {
+ // every time it fails, we should wait a little longer
+ // between tries
+ ::sleep(1+time/2);
+ d->server = Arts::Reference("global:Arts_SoundServerV2");
+ } while(++time < 5 && (d->server.isNull()));
+ }
+ }
+
+ if ( !d->server.isNull() )
+ {
+ d->amanPlay = Arts::DynamicCast(
+ d->server.createObject("Arts::Synth_AMAN_PLAY")
+ );
+
+ if (d->amanPlay.isNull())
+ goto crapOut;
+
+ d->session = Arts::DynamicCast(
+ d->server.createObject("Noatun::Session"));
+ if (d->session.isNull())
+ {
+ kdWarning() << "Couldn't instanciate artsobject Noatun::Session. "
+ << "(This is normally caused by a broken package or "
+ << "compiling kdemultimedia in a --prefix different "
+ << "from arts. It may also be from two conflicting "
+ << "packages, so uninstall every arts/artsd package "
+ << "you have installed and try again." << endl;
+ goto crapOut;
+ }
+
+ d->amanPlay.title("noatun");
+ d->amanPlay.autoRestoreID("noatun");
+ d->amanPlay.start();
+
+ d->globalEffectStack=Arts::DynamicCast(
+ d->server.createObject("Noatun::StereoEffectStack"));;
+ d->globalEffectStack.start();
+ Arts::connect(d->globalEffectStack,d->amanPlay);
+
+ d->effectsStack=Arts::DynamicCast(
+ d->server.createObject("Noatun::StereoEffectStack"));
+ d->effectsStack.start();
+ d->globalEffectStack.insertBottom(d->effectsStack, "Effects Stack");
+
+ d->equalizer=Arts::DynamicCast(d->server.createObject("Noatun::Equalizer"));
+ d->equalizer.start();
+ d->globalEffectStack.insertBottom(d->equalizer, "Equalizer");
+
+ if (napp->equalizer())
+ {
+ napp->equalizer()->update(true);
+ napp->equalizer()->setPreamp(napp->equalizer()->preamp());
+ napp->equalizer()->setEnabled(napp->equalizer()->isEnabled());
+ }
+
+ d->visStack=Arts::DynamicCast(
+ d->server.createObject("Noatun::StereoEffectStack"));
+ d->visStack.start();
+
+ d->globalEffectStack.insertBottom(d->visStack, "Visualization Stack");
+ d->volumeControl=VolumeControls::volumeControl(this);
+ if (volume != -1)
+ d->volumeControl->setVolume(volume);
+ }
+ else
+ {
+ crapOut:
+ KMessageBox::error( 0, i18n("Connecting/starting aRts soundserver failed. Make sure that artsd is configured properly."));
+ exit(1);
+ }
+ }
+
+ d->playobj=0;
+ emit artsError();
+ return true;
+}
+
+
+#include "engine.moc"
+
diff --git a/noatun/library/equalizer.cpp b/noatun/library/equalizer.cpp
new file mode 100644
index 00000000..2ee208db
--- /dev/null
+++ b/noatun/library/equalizer.cpp
@@ -0,0 +1,339 @@
+#include "equalizer.h"
+#include "engine.h"
+#include <common.h>
+#include <dynamicrequest.h>
+#include <artsflow.h>
+#include <app.h>
+#include <player.h>
+#include <soundserver.h>
+#include <noatunarts.h>
+#include <ktempfile.h>
+#include <qdom.h>
+#include <kio/netaccess.h>
+#include <kstandarddirs.h>
+#include <qtextstream.h>
+#include <math.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include "ksaver.h"
+
+#define EQ napp->equalizer()
+#define VEQ napp->vequalizer()
+
+struct OldEqCruft
+{
+ VInterpolation *interpolated;
+
+};
+
+static OldEqCruft *eqCruft=0;
+
+Preset::Preset(const QString &)
+{ } // unused
+
+Preset::Preset(VPreset p)
+{
+ VPreset *copy = new VPreset(p);
+
+ // isn't this horrible? :)
+ mFile = QString::number((unsigned long)copy);
+}
+
+Preset::Preset()
+{ } // unused
+
+VPreset &Preset::vpreset() const
+{
+ unsigned long addr = mFile.toULong();
+ return *(VPreset*)addr;
+}
+
+QString Preset::name() const
+{
+ return vpreset().name();
+}
+
+bool Preset::setName(const QString &name)
+{
+ return vpreset().setName(name);
+}
+
+bool Preset::save() const
+{
+ vpreset().save();
+ return true;
+}
+
+bool Preset::load()
+{
+ vpreset().load();
+ return true;
+}
+
+void Preset::remove()
+{
+ vpreset().remove();
+}
+
+QString Preset::file() const
+{
+ return vpreset().file();
+}
+
+
+Band::Band(int, int)
+{
+ // Never used
+}
+
+Band::Band(int num)
+ : mNum(num)
+{
+
+}
+
+Band::~Band()
+{}
+
+int Band::level()
+{
+ return eqCruft->interpolated->band(mNum).level();
+}
+
+void Band::setLevel(int l)
+{
+ eqCruft->interpolated->band(mNum).setLevel(l);
+}
+
+int Band::start() const
+{
+ return eqCruft->interpolated->band(mNum).start();
+}
+
+int Band::end() const
+{
+ return eqCruft->interpolated->band(mNum).end();
+}
+
+int Band::center() const
+{
+ return eqCruft->interpolated->band(mNum).center();
+}
+
+QString Band::formatStart(bool withHz) const
+{
+ return eqCruft->interpolated->band(mNum).formatStart(withHz);
+}
+
+QString Band::formatEnd(bool withHz) const
+{
+ return eqCruft->interpolated->band(mNum).formatEnd(withHz);
+}
+
+QString Band::format(bool withHz) const
+{
+ return eqCruft->interpolated->band(mNum).format(withHz);
+}
+
+
+Equalizer::Equalizer()
+{
+}
+
+Equalizer::~Equalizer()
+{
+ delete eqCruft->interpolated;
+ delete eqCruft;
+
+// save(napp->dirs()->saveLocation("data", "noatun/") + "equalizer", "auto");
+ for (Band *i=mBands.first(); i!=0; i=mBands.next())
+ delete i;
+}
+
+
+void Equalizer::init()
+{
+ // must be called after VEqualizer::init
+ eqCruft = new OldEqCruft;
+ eqCruft->interpolated = new VInterpolation(6);
+
+ mBands.append(new Band(0));
+ mBands.append(new Band(1));
+ mBands.append(new Band(2));
+ mBands.append(new Band(3));
+ mBands.append(new Band(4));
+ mBands.append(new Band(5));
+
+ connect(VEQ, SIGNAL(changed()), SIGNAL(changed()));
+
+ connect(VEQ, SIGNAL(created(VPreset)), SLOT(created(VPreset)));
+ connect(VEQ, SIGNAL(selected(VPreset)), SLOT(selected(VPreset)));
+ connect(VEQ, SIGNAL(renamed(VPreset)), SLOT(renamed(VPreset)));
+ connect(VEQ, SIGNAL(removed(VPreset)), SLOT(removed(VPreset)));
+
+ connect(VEQ, SIGNAL(enabled()), SIGNAL(enabled()));
+ connect(VEQ, SIGNAL(disabled()), SIGNAL(disabled()));
+ connect(VEQ, SIGNAL(enabled(bool)), SIGNAL(enabled(bool)));
+
+ connect(VEQ, SIGNAL(preampChanged(int)), SIGNAL(preampChanged(int)));
+ connect(VEQ, SIGNAL(preampChanged(int)), SIGNAL(preampChanged(int)));
+}
+
+void Equalizer::created(VPreset preset)
+{
+ Preset *p = new Preset(preset);
+ emit created(p);
+ delete p;
+}
+
+void Equalizer::selected(VPreset preset)
+{
+ Preset *p = new Preset(preset);
+ emit changed(p);
+ delete p;
+}
+
+void Equalizer::renamed(VPreset preset)
+{
+ Preset *p = new Preset(preset);
+ emit renamed(p);
+ delete p;
+}
+
+void Equalizer::removed(VPreset preset)
+{
+ Preset *p = new Preset(preset);
+ emit removed(p);
+ delete p;
+}
+
+QPtrList<Preset> Equalizer::presets() const
+{
+ QValueList<VPreset> presets = VEQ->presets();
+ QPtrList<Preset> list;
+ for (
+ QValueList<VPreset>::Iterator i(presets.begin());
+ i != presets.end(); ++i
+ )
+ {
+ list.append(new Preset(*i));
+ }
+ return list;
+}
+
+Preset *Equalizer::preset(const QString &file)
+{
+ VPreset p = VEQ->presetByFile(file);
+ if (!p) return 0;
+ return new Preset(p);
+}
+
+bool Equalizer::presetExists(const QString &name) const
+{
+ return VEQ->presetExists(name);
+}
+
+Preset *Equalizer::createPreset(const QString &name, bool smart)
+{
+ VPreset p = VEQ->createPreset(name, smart);
+ if (!p) return 0;
+ return new Preset(p);
+}
+
+const QPtrList<Band> &Equalizer::bands() const
+{
+ return mBands;
+}
+
+Band *Equalizer::band(int num) const
+{
+ // can't use QPtrList::at since it sets current
+
+ QPtrListIterator<Band> item(mBands);
+ item+=(unsigned int)num;
+ return *item;
+}
+
+int Equalizer::bandCount() const
+{
+ return 6; // hmm ;)
+}
+
+int Equalizer::preamp() const
+{
+ return VEQ->preamp();
+}
+
+bool Equalizer::isEnabled() const
+{
+ return VEQ->isEnabled();
+
+}
+
+void Equalizer::setPreamp(int p)
+{
+ VEQ->setPreamp(p);
+}
+
+void Equalizer::enable()
+{
+ setEnabled(true);
+}
+
+void Equalizer::disable()
+{
+ setEnabled(false);
+}
+
+void Equalizer::setEnabled(bool e)
+{
+ VEQ->setEnabled(e);
+}
+
+QString Equalizer::toString(const QString &name) const
+{
+ return VEQ->toString(name);
+}
+
+bool Equalizer::fromString(const QString &str)
+{
+ return VEQ->fromString(str);
+}
+
+bool Equalizer::save(const KURL &filename, const QString &name) const
+{
+ return VEQ->save(filename, name);
+}
+
+
+
+bool Equalizer::load(const KURL &filename)
+{
+ return VEQ->load(filename);
+}
+
+void Equalizer::add(Band *)
+{
+ // should never be called
+}
+
+void Equalizer::remove(Band *)
+{
+ // should never be called
+}
+
+void Equalizer::update(bool)
+{
+ // should never be called
+}
+
+void Equalizer::enableUpdates(bool)
+{
+ // should never be called
+}
+
+#undef EQ
+#undef EQBACK
+
+#include "equalizer.moc"
+
diff --git a/noatun/library/equalizerview.cpp b/noatun/library/equalizerview.cpp
new file mode 100644
index 00000000..5e406e13
--- /dev/null
+++ b/noatun/library/equalizerview.cpp
@@ -0,0 +1,325 @@
+#include "vequalizer.h"
+#define EQVIEW_CPP
+#include "equalizerview.h"
+#undef EQVIEW_CPP
+#include "equalizerwidget.h"
+#include "app.h"
+
+#include <knuminput.h>
+#include <kdialog.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klocale.h>
+
+#include <qlayout.h>
+#include <qslider.h>
+#include <qcheckbox.h>
+#include <qlabel.h>
+#include <qtabwidget.h>
+#include <qpushbutton.h>
+#include <qheader.h>
+#include <qfileinfo.h>
+#include <qhbox.h>
+#include <qvbox.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+
+#define EQ (napp->vequalizer())
+
+
+////////////////////////////////////////////////
+// PresetList
+
+PresetList::PresetList(QWidget *parent, const char *name)
+ : KListView(parent, name)
+{
+ setItemsRenameable(true);
+ setRenameable(0, true);
+ addColumn(""); // first column is the visible one
+ addColumn("", 0); // create one column to store cruft in
+ setColumnWidthMode(0, QListView::Maximum);
+ header()->setStretchEnabled(true,0);
+ header()->hide();
+ // a try to set a sne minimum width. unfortuately the custom item
+ // still doesn't draw all text with that minimum width
+ setMinimumWidth(kapp->fontMetrics().boundingRect(i18n("Custom")).width()+2*itemMargin());
+}
+
+void PresetList::rename(QListViewItem *item, int c)
+{
+ // We can't rename the "Custom" metapreset
+ if (item->text(0)==i18n("Custom"))
+ return;
+
+ // Or presets we don't have write access to
+ if (!QFileInfo(item->text(1)).isWritable())
+ return;
+
+ KListView::rename(item, c);
+}
+
+////////////////////////////////////////////////
+// EqualizerLevel
+
+EqualizerLevel::EqualizerLevel(QWidget *parent, VBand band)
+ : QWidget(parent), mBand(band)
+{
+ QVBoxLayout *layout = new QVBoxLayout(this,
+ 0, 0, "EqualizerLevel::layout");
+
+ mSlider = new QSlider(-200, 200, 25, 0, Qt::Vertical, this, "EqualizerLevel::mSlider");
+ mSlider->setTickmarks(QSlider::Left);
+ mSlider->setTickInterval(25);
+ layout->addWidget(mSlider);
+ connect(mSlider, SIGNAL(valueChanged(int)), SLOT(changed(int)));
+ mLabel = new QLabel("", this, "EqualizerLevel::mLabel");
+ mLabel->setAlignment(AlignHCenter | AlignVCenter);
+ layout->addWidget(mLabel);
+
+ setMinimumHeight(200);
+// setMinimumWidth(kapp->fontMetrics().width("158kHz"));
+// setMinimumWidth(kapp->fontMetrics().width("549kHz"));
+
+ setBand(band);
+
+ connect(EQ, SIGNAL(modified()), SLOT(changed()));
+ connect(mSlider, SIGNAL(valueChanged(int)), SLOT(changed(int)));
+}
+
+void EqualizerLevel::setBand(VBand band)
+{
+ mBand = band;
+ mLabel->setText(band.format());
+ changed();
+}
+
+void EqualizerLevel::changed()
+{
+ mSlider->setValue(-mBand.level());
+}
+
+void EqualizerLevel::changed(int v)
+{
+ mBand.setLevel(-v);
+}
+
+
+///////////////////////////////////////////////
+// EqualizerView
+
+EqualizerView::EqualizerView()
+ : KDialogBase(0L, "EqualizerView", false, i18n("Equalizer"), Help | Close, Close, true),
+ first(true), mWidget(0), bandsLayout(0), mPresets(0), mGoingPreset(false)
+{
+ mBands.setAutoDelete(true);
+}
+
+void EqualizerView::show()
+{
+ if (first)
+ {
+ first = false;
+ setIcon(SmallIcon("noatun"));
+ mWidget = new EqualizerWidget(this, "mWidget");
+ setMainWidget(mWidget);
+
+ bandsLayout = new QHBoxLayout(mWidget->bandsFrame,
+ 0, KDialog::spacingHint(), "bandsLayout");
+
+ connect(mWidget->preampSlider, SIGNAL(valueChanged(int)),
+ this, SLOT(setPreamp(int)));
+ connect(EQ, SIGNAL(preampChanged(int)),
+ this, SLOT(changedPreamp(int)));
+
+ mWidget->bandCount->setRange(EQ->minBands(), EQ->maxBands());
+ connect(mWidget->bandCount, SIGNAL(valueChanged(int)),
+ EQ, SLOT(setBands(int)));
+
+ QVBoxLayout *l = new QVBoxLayout(mWidget->presetFrame);
+ mPresets = new PresetList(mWidget->presetFrame, "mPresets");
+ l->addWidget(mPresets);
+
+ connect(mWidget->removePresetButton, SIGNAL(clicked()), SLOT(remove()));
+ connect(mWidget->addPresetButton, SIGNAL(clicked()), SLOT(create()));
+ connect(mWidget->resetEqButton, SIGNAL(clicked()), SLOT(reset()));
+
+ new KListViewItem(mPresets, i18n("Custom"));
+
+ connect(mPresets, SIGNAL(currentChanged(QListViewItem*)),
+ this, SLOT(select(QListViewItem*)));
+
+ connect(mPresets, SIGNAL(itemRenamed(QListViewItem*)),
+ this, SLOT(rename(QListViewItem*)));
+
+ // populate the preset list
+ QValueList<VPreset> presets = EQ->presets();
+ QValueList<VPreset>::Iterator it;
+ for (it=presets.begin(); it!=presets.end(); ++it)
+ {
+ created(*it);
+ }
+
+ connect(EQ, SIGNAL(created(VPreset)), SLOT(created(VPreset)));
+ connect(EQ, SIGNAL(renamed(VPreset)), SLOT(renamed(VPreset)));
+ connect(EQ, SIGNAL(removed(VPreset)), SLOT(removed(VPreset)));
+
+ mWidget->enabledCheckBox->setChecked(EQ->isEnabled());
+ connect(mWidget->enabledCheckBox, SIGNAL(toggled(bool)),
+ EQ, SLOT(setEnabled(bool)));
+ connect(EQ, SIGNAL(enabled(bool)),
+ mWidget->enabledCheckBox, SLOT(setChecked(bool)));
+
+ connect(EQ, SIGNAL(changed()),
+ this, SLOT(changedEq()));
+ connect(EQ, SIGNAL(changedBands()),
+ this, SLOT(changedBands()));
+
+ changedBands();
+ changedEq();
+ } // END if(first)
+
+ if (isVisible())
+ raise();
+ else
+ KDialogBase::show();
+}
+
+QListViewItem *EqualizerView::itemFor(const QString &filename)
+{
+ for (QListViewItem *i=mPresets->firstChild(); i!=0; i=i->itemBelow())
+ {
+ QString t = i->text(1);
+ if ((t.length()==0 && filename.length()==0) || t==filename)
+ return i;
+ }
+ return 0;
+}
+
+QListViewItem *EqualizerView::itemFor(const VPreset &preset)
+{
+ return itemFor(preset.file());
+}
+
+// why is it that when you move a QSlider up, it goes down?
+void EqualizerView::setPreamp(int x)
+{
+ EQ->setPreamp(-x);
+}
+
+void EqualizerView::changedPreamp(int x)
+{
+ mWidget->preampSlider->setValue(-x);
+}
+
+
+void EqualizerView::changedBands()
+{
+ mBands.clear();
+
+ VEqualizer &eq = *EQ;
+ for (int i=0; i < eq.bands(); ++i)
+ {
+ EqualizerLevel *l = new EqualizerLevel(mWidget->bandsFrame, eq[i]);
+ bandsLayout->addWidget(l);
+ l->show();
+ mBands.append(l);
+ }
+
+ mWidget->bandCount->setValue(eq.bands());
+ changedEq();
+}
+
+void EqualizerView::changedEq()
+{
+ if (!mGoingPreset)
+ {
+ QListViewItem *customitem = itemFor("");
+ if (!customitem) // this should never happen!
+ return;
+ mPresets->setSelected(customitem, true);
+ }
+}
+
+void EqualizerView::removed(VPreset p)
+{
+ delete itemFor(p);
+}
+
+void EqualizerView::created(VPreset p)
+{
+ // store the filename in QListViewItem::text(0)
+ QString n = p.name();
+ QString f = p.file();
+ new KListViewItem(mPresets, n, f);
+}
+
+void EqualizerView::renamed(VPreset p)
+{
+ QListViewItem *renamed = itemFor(p);
+ if (!renamed) // WTF !
+ {
+ created(p);
+ return;
+ }
+ renamed->setText(0, p.name());
+}
+
+void EqualizerView::remove()
+{
+ QListViewItem *current=mPresets->currentItem();
+ if (current->text(0)==i18n("Custom"))
+ return;
+ QListViewItem *then=current->itemAbove();
+ if (!then) then=current->itemBelow();
+
+ if (then)
+ mPresets->setSelected(then, true);
+
+ VPreset p = EQ->presetByFile(current->text(1));
+ p.remove();
+}
+
+void EqualizerView::create()
+{
+ VPreset p = EQ->createPreset(i18n("New Preset"));
+
+ mGoingPreset = true;
+
+ // Load the new preset
+ p.load();
+
+ // We should have just made a list view item for this preset
+ // See EquailizerView::presetAdded()
+ QListViewItem *i = itemFor(p);
+
+ if (i)
+ mPresets->setSelected(i, true);
+
+ mGoingPreset = false;
+}
+
+void EqualizerView::reset()
+{
+ VEqualizer &eq = *EQ;
+ eq.setPreamp(0);
+ for (int i=0; i < eq.bands(); ++i)
+ eq.band(i).setLevel(0);
+
+}
+
+void EqualizerView::rename(QListViewItem *item)
+{
+ EQ->presetByFile(item->text(1)).setName(item->text(0));
+ item->setText(0, EQ->presetByFile(item->text(1)).name());
+}
+
+void EqualizerView::select(QListViewItem *item)
+{
+ mGoingPreset = true;
+ EQ->presetByFile(item->text(1)).load();
+ mGoingPreset = false;
+ mWidget->removePresetButton->setEnabled(item->text(1).length());
+}
+
+#undef EQ
+#include "equalizerview.moc"
diff --git a/noatun/library/equalizerview.h b/noatun/library/equalizerview.h
new file mode 100644
index 00000000..98778fcc
--- /dev/null
+++ b/noatun/library/equalizerview.h
@@ -0,0 +1,91 @@
+#ifndef EQUALIZERVIEW_H
+#define EQUALIZERVIEW_H
+
+#include <qwidget.h>
+#include <kdialogbase.h>
+#include <klistview.h>
+
+class VBand;
+class QSlider;
+class QLabel;
+class QListViewItem;
+class VPreset;
+class QHBoxLayout;
+class EqualizerWidget;
+
+
+class EqualizerLevel : public QWidget
+{
+Q_OBJECT
+public:
+ EqualizerLevel(QWidget *parent, VBand band);
+
+public slots:
+ void changed();
+ void changed(int);
+
+ void setBand(VBand band);
+
+private:
+ VBand mBand;
+ QSlider *mSlider;
+ QLabel *mLabel;
+};
+
+
+class PresetList : public KListView
+{
+Q_OBJECT
+public:
+ PresetList(QWidget *parent, const char *name=0);
+
+public:
+ void rename(QListViewItem *item, int c);
+};
+
+
+class EqualizerView : public KDialogBase
+{
+Q_OBJECT
+ QPtrList<EqualizerLevel> mBands;
+
+public:
+ EqualizerView();
+ virtual void show();
+
+ QListViewItem *itemFor(const QString &filename);
+ QListViewItem *itemFor(const VPreset &preset);
+
+public slots:
+ void setPreamp(int);
+ void changedPreamp(int);
+
+private slots:
+ void changedBands();
+ void changedEq();
+
+ void removed(VPreset p);
+ void created(VPreset p);
+ void renamed(VPreset p);
+
+ void remove();
+ void create();
+ void reset();
+ void rename(QListViewItem *);
+ void select(QListViewItem*);
+
+private:
+ bool first;
+ EqualizerWidget *mWidget;
+ QHBoxLayout *bandsLayout;
+// QCheckBox *mOn;
+// QSlider *mPreamp;
+ PresetList *mPresets;
+ bool mGoingPreset;
+// QPushButton *mRemovePreset, *mAddPreset;
+// QFrame *mSliders;
+// KIntNumInput *mBandCount;
+};
+
+#endif
+
diff --git a/noatun/library/equalizerwidget.ui b/noatun/library/equalizerwidget.ui
new file mode 100644
index 00000000..1432f59a
--- /dev/null
+++ b/noatun/library/equalizerwidget.ui
@@ -0,0 +1,337 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>EqualizerWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>EqualizerWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>518</width>
+ <height>283</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Equalizer</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>Pre&amp;amp:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>preampSlider</cstring>
+ </property>
+ </widget>
+ <spacer row="1" column="2">
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Minimum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>1</width>
+ <height>8</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="1" column="0">
+ <property name="name">
+ <cstring>spacer4_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Minimum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>1</width>
+ <height>8</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget" row="1" column="1">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QSlider">
+ <property name="name">
+ <cstring>preampSlider</cstring>
+ </property>
+ <property name="minValue">
+ <number>-200</number>
+ </property>
+ <property name="maxValue">
+ <number>200</number>
+ </property>
+ <property name="lineStep">
+ <number>0</number>
+ </property>
+ <property name="pageStep">
+ <number>25</number>
+ </property>
+ <property name="value">
+ <number>0</number>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="tickmarks">
+ <enum>Both</enum>
+ </property>
+ <property name="tickInterval">
+ <number>25</number>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>+/-</string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="1" column="1">
+ <property name="name">
+ <cstring>bandsGroupBox</cstring>
+ </property>
+ <property name="title">
+ <string>&amp;Bands</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>bandsFrame</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox" row="1" column="2">
+ <property name="name">
+ <cstring>presetsGroupBox</cstring>
+ </property>
+ <property name="title">
+ <string>&amp;Presets</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QFrame" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>presetFrame</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="0">
+ <property name="name">
+ <cstring>removePresetButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="2" column="0">
+ <property name="name">
+ <cstring>addPresetButton</cstring>
+ </property>
+ <property name="text">
+ <string>A&amp;dd</string>
+ </property>
+ </widget>
+ <spacer row="2" column="1">
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>8</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>enabledCheckBox</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Enabled</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Minimum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>8</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_3</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Number of bands:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>bandCount</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>bandCount</cstring>
+ </property>
+ <property name="maxValue">
+ <number>16</number>
+ </property>
+ <property name="value">
+ <number>6</number>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>8</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>resetEqButton</cstring>
+ </property>
+ <property name="text">
+ <string>Re&amp;set EQ</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<tabstops>
+ <tabstop>enabledCheckBox</tabstop>
+ <tabstop>bandCount</tabstop>
+ <tabstop>resetEqButton</tabstop>
+ <tabstop>preampSlider</tabstop>
+ <tabstop>removePresetButton</tabstop>
+ <tabstop>addPresetButton</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>knuminput.h</includehint>
+</includehints>
+</UI>
diff --git a/noatun/library/gentable.cpp b/noatun/library/gentable.cpp
new file mode 100644
index 00000000..68e04162
--- /dev/null
+++ b/noatun/library/gentable.cpp
@@ -0,0 +1,1037 @@
+const float data[] =
+{
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 2,
+ 1.93113195896148681640625,
+ 1.78310096263885498046875,
+ 1.70247924327850341796875,
+ 1.624516010284423828125,
+ 1.548387050628662109375,
+ 1.49999988079071044921875,
+ 1.4569394588470458984375,
+ 1.4230768680572509765625,
+ 1.388888835906982421875,
+ 1.3620688915252685546875,
+ 1.3333332538604736328125,
+ 1.31213867664337158203125,
+ 1.28985500335693359375,
+ 1.27981650829315185546875,
+ 1.26315784454345703125,
+ 1.24999988079071044921875,
+ 1.2345678806304931640625,
+ 1.2253520488739013671875,
+ 1.21578943729400634765625,
+ 1.20482635498046875,
+ 1.192531108856201171875,
+ 1.18749988079071044921875,
+ 1.1790392398834228515625,
+ 1.1704347133636474609375,
+ 1.16666662693023681640625,
+ 1.1615383625030517578125,
+ 1.153512477874755859375,
+ 1.14848482608795166015625,
+ 1.143036365509033203125,
+ 1.13935959339141845703125,
+ 1.13513505458831787109375,
+ 1.1296908855438232421875,
+ 1.12499988079071044921875,
+ 1.12320911884307861328125,
+ 1.11992251873016357421875,
+ 1.11627900600433349609375,
+ 1.1115043163299560546875,
+ 1.1090908050537109375,
+ 1.1054852008819580078125,
+ 1.1032257080078125,
+ 1.099999904632568359375,
+ 1.0987341403961181640625,
+ 1.09541976451873779296875,
+ 1.09374988079071044921875,
+ 1.09090900421142578125,
+ 1.088842868804931640625,
+ 1.08620679378509521484375,
+ 1.08459866046905517578125,
+ 1.0833332538604736328125,
+ 1.08128535747528076171875,
+ 1.0793650150299072265625,
+ 1.07757294178009033203125,
+ 1.07648181915283203125,
+ 1.0740740299224853515625,
+ 1.07282412052154541015625,
+ 1.07149398326873779296875,
+ 1.0701754093170166015625,
+ 1.06882584095001220703125,
+ 1.0675990581512451171875,
+ 1.065758228302001953125,
+ 1.0645160675048828125,
+ 1.06382977962493896484375,
+ 1.06249988079071044921875,
+ 1.0615384578704833984375,
+ 1.0606060028076171875,
+ 1.05952370166778564453125,
+ 1.0579373836517333984375,
+ 1.05714285373687744140625,
+ 1.05629765987396240234375,
+ 1.055452823638916015625,
+ 1.05461633205413818359375,
+ 1.05350315570831298828125,
+ 1.05263149738311767578125,
+ 1.05194795131683349609375,
+ 1.05086696147918701171875,
+ 1.0499999523162841796875,
+ 1.04951560497283935546875,
+ 1.04892957210540771484375,
+ 1.0478632450103759765625,
+ 1.0472972393035888671875,
+ 1.04651153087615966796875,
+ 1.04577457904815673828125,
+ 1.04522609710693359375,
+ 1.04464280605316162109375,
+ 1.0439865589141845703125,
+ 1.0434782505035400390625,
+ 1.0425531864166259765625,
+ 1.04188477993011474609375,
+ 1.04166662693023681640625,
+ 1.04085254669189453125,
+ 1.0404479503631591796875,
+ 1.03986012935638427734375,
+ 1.03934001922607421875,
+ 1.03876841068267822265625,
+ 1.0384614467620849609375,
+ 1.03795063495635986328125,
+ 1.03748404979705810546875,
+ 1.03688514232635498046875,
+ 1.03637897968292236328125,
+ 1.03602182865142822265625,
+ 1.03559863567352294921875,
+ 1.03508770465850830078125,
+ 1.0347592830657958984375,
+ 1.03414630889892578125,
+ 1.03389823436737060546875,
+ 1.033333301544189453125,
+ 1.03316318988800048828125,
+ 1.03260862827301025390625,
+ 1.03225803375244140625,
+ 1.0319488048553466796875,
+ 1.0315067768096923828125,
+ 1.03124988079071044921875,
+ 1.03100764751434326171875,
+ 1.0304877758026123046875,
+ 1.0302250385284423828125,
+ 1.0298507213592529296875,
+ 1.0294573307037353515625,
+ 1.029239654541015625,
+ 1.02892553806304931640625,
+ 1.02875816822052001953125,
+ 1.02830183506011962890625,
+ 1.02789247035980224609375,
+ 1.02767288684844970703125,
+ 1.02745664119720458984375,
+ 1.02711856365203857421875,
+ 1.0268414020538330078125,
+ 1.0265486240386962890625,
+ 1.0263156890869140625,
+ 1.02608692646026611328125,
+ 1.02590668201446533203125,
+ 1.0255353450775146484375,
+ 1.02531635761260986328125,
+ 1.02504265308380126953125,
+ 1.0247933864593505859375,
+ 1.02454984188079833984375,
+ 1.02436733245849609375,
+ 1.02406990528106689453125,
+ 1.0238094329833984375,
+ 1.0236685276031494140625,
+ 1.0234191417694091796875,
+ 1.02319896221160888671875,
+ 1.0230023860931396484375,
+ 1.0227272510528564453125,
+ 1.02263367176055908203125,
+ 1.0223712921142578125,
+ 1.0221674442291259765625,
+ 1.0219669342041015625,
+ 1.02173912525177001953125,
+ 1.02160274982452392578125,
+ 1.02142846584320068359375,
+ 1.0211639404296875,
+ 1.0209677219390869140625,
+ 1.0208332538604736328125,
+ 1.02064216136932373046875,
+ 1.02050113677978515625,
+ 1.02027022838592529296875,
+ 1.0201342105865478515625,
+ 1.019999980926513671875,
+ 1.01986753940582275390625,
+ 1.0196077823638916015625,
+ 1.01941740512847900390625,
+ 1.01928365230560302734375,
+ 1.01915180683135986328125,
+ 1.01902878284454345703125,
+ 1.0188140869140625,
+ 1.01866245269775390625,
+ 1.0185184478759765625,
+ 1.01840484142303466796875,
+ 1.01818180084228515625,
+ 1.01807224750518798828125,
+ 1.017937183380126953125,
+ 1.017857074737548828125,
+ 1.0176470279693603515625,
+ 1.01752567291259765625,
+ 1.01736104488372802734375,
+ 1.01724135875701904296875,
+ 1.01709401607513427734375,
+ 1.01700675487518310546875,
+ 1.01684439182281494140625,
+ 1.0166666507720947265625,
+ 1.01657450199127197265625,
+ 1.01646339893341064453125,
+ 1.016326427459716796875,
+ 1.0161879062652587890625,
+ 1.016129016876220703125,
+ 1.01601827144622802734375,
+ 1.01583111286163330078125,
+ 1.01574802398681640625,
+ 1.01562488079071044921875,
+ 1.01554048061370849609375,
+ 1.01538455486297607421875,
+ 1.0152838230133056640625,
+ 1.015151500701904296875,
+ 1.01507270336151123046875,
+ 1.01495325565338134765625,
+ 1.01484715938568115234375,
+ 1.01473677158355712890625,
+ 1.01467132568359375,
+ 1.01453483104705810546875,
+ 1.01440918445587158203125,
+ 1.014302730560302734375,
+ 1.01425659656524658203125,
+ 1.0141642093658447265625,
+ 1.01401865482330322265625,
+ 1.0139102935791015625,
+ 1.01386821269989013671875,
+ 1.01374042034149169921875,
+ 1.01367175579071044921875,
+ 1.01355922222137451171875,
+ 1.013473033905029296875,
+ 1.013388156890869140625,
+ 1.01326954364776611328125,
+ 1.01322114467620849609375,
+ 1.0131232738494873046875,
+ 1.01303780078887939453125,
+ 1.01293098926544189453125,
+ 1.012861728668212890625,
+ 1.01279580593109130859375,
+ 1.01268112659454345703125,
+ 1.0126049518585205078125,
+ 1.01249992847442626953125,
+ 1.012468814849853515625,
+ 1.01236855983734130859375,
+ 1.0122928619384765625,
+ 1.012195110321044921875,
+ 1.01213753223419189453125,
+ 1.01205670833587646484375,
+ 1.0119571685791015625,
+ 1.01190471649169921875,
+ 1.011820316314697265625,
+ 1.01176464557647705078125,
+ 1.01167309284210205078125,
+ 1.01161289215087890625,
+ 1.0115451812744140625,
+ 1.011450290679931640625,
+ 1.011395931243896484375,
+ 1.011334896087646484375,
+ 1.011273860931396484375,
+ 1.0111939907073974609375,
+ 1.0111110210418701171875,
+ 1.01105844974517822265625,
+ 1.0109889507293701171875,
+ 1.0109169483184814453125,
+ 1.010869503021240234375,
+ 1.01078164577484130859375,
+ 1.0107219219207763671875,
+ 1.01063823699951171875,
+ 1.01058197021484375,
+ 1.01052629947662353515625,
+ 1.01044380664825439453125,
+ 1.0103969573974609375,
+ 1.0103447437286376953125,
+ 1.0103092193603515625,
+ 1.01023006439208984375,
+ 1.01018321514129638671875,
+ 1.01010096073150634765625,
+ 1.01004636287689208984375,
+ 1.0099833011627197265625,
+ 1.00994026660919189453125,
+ 1.0098683834075927734375,
+ 1.00980389118194580078125,
+ 1.00976550579071044921875,
+ 1.0097086429595947265625,
+ 1.00965249538421630859375,
+ 1.00961530208587646484375,
+ 1.0095388889312744140625,
+ 1.0094835758209228515625,
+ 1.00943386554718017578125,
+ 1.0093896389007568359375,
+ 1.0093457698822021484375,
+ 1.00925922393798828125,
+ 1.00923073291778564453125,
+ 1.00917422771453857421875,
+ 1.00911843776702880859375,
+ 1.009090900421142578125,
+ 1.0090415477752685546875,
+ 1.0089685916900634765625,
+ 1.0089285373687744140625,
+ 1.00888884067535400390625,
+ 1.00882351398468017578125,
+ 1.00877702236175537109375,
+ 1.00875270366668701171875,
+ 1.0086956024169921875,
+ 1.00864541530609130859375,
+ 1.0086123943328857421875,
+ 1.00854694843292236328125,
+ 1.00850331783294677734375,
+ 1.00846016407012939453125,
+ 1.008419036865234375,
+ 1.00836813449859619140625,
+ 1.00833332538604736328125,
+ 1.00828397274017333984375,
+ 1.00823962688446044921875,
+ 1.00819671154022216796875,
+ 1.00815546512603759765625,
+ 1.00809705257415771484375,
+ 1.0080687999725341796875,
+ 1.0080320835113525390625,
+ 1.0079839229583740234375,
+ 1.0079364776611328125,
+ 1.00790059566497802734375,
+ 1.007874011993408203125,
+ 1.00781857967376708984375,
+ 1.0077903270721435546875,
+ 1.0077369213104248046875,
+ 1.00769221782684326171875,
+ 1.00766277313232421875,
+ 1.00761687755584716796875,
+ 1.0075757503509521484375,
+ 1.0075511932373046875,
+ 1.00750744342803955078125,
+ 1.00747382640838623046875,
+ 1.0074441432952880859375,
+ 1.007380008697509765625,
+ 1.0073528289794921875,
+ 1.00732898712158203125,
+ 1.00728595256805419921875,
+ 1.00724637508392333984375,
+ 1.00722301006317138671875,
+ 1.0071890354156494140625,
+ 1.0071427822113037109375,
+ 1.00710475444793701171875,
+ 1.00707542896270751953125,
+ 1.00703394412994384765625,
+ 1.0070092678070068359375,
+ 1.00696861743927001953125,
+ 1.0069444179534912109375,
+ 1.00691235065460205078125,
+ 1.00686490535736083984375,
+ 1.0068492889404296875,
+ 1.00680267810821533203125,
+ 1.00676810741424560546875,
+ 1.0067365169525146484375,
+ 1.00670230388641357421875,
+ 1.006666660308837890625,
+ 1.0066444873809814453125,
+ 1.00661051273345947265625,
+ 1.006578922271728515625,
+ 1.00654447078704833984375,
+ 1.00651454925537109375,
+ 1.00649344921112060546875,
+ 1.006465435028076171875,
+ 1.00642049312591552734375,
+ 1.00639998912811279296875,
+ 1.0063693523406982421875,
+ 1.006329059600830078125,
+ 1.006311893463134765625,
+ 1.0062713623046875,
+ 1.00624024868011474609375,
+ 1.00621116161346435546875,
+ 1.0061790943145751953125,
+ 1.00615751743316650390625,
+ 1.00613486766815185546875,
+ 1.0060975551605224609375,
+ 1.006077289581298828125,
+ 1.0060422420501708984375,
+ 1.00602400302886962890625,
+ 1.005992412567138671875,
+ 1.005952358245849609375,
+ 1.0059320926666259765625,
+ 1.00591278076171875,
+ 1.00588226318359375,
+ 1.00585925579071044921875,
+ 1.00583088397979736328125,
+ 1.005802631378173828125,
+ 1.00577557086944580078125,
+ 1.0057470798492431640625,
+ 1.0057141780853271484375,
+ 1.0056979656219482421875,
+ 1.0056817531585693359375,
+ 1.00564968585968017578125,
+ 1.00561797618865966796875,
+ 1.00560224056243896484375,
+ 1.00556433200836181640625,
+ 1.00554430484771728515625,
+ 1.00552475452423095703125,
+ 1.00550043582916259765625,
+ 1.0054776668548583984375,
+ 1.0054495334625244140625,
+ 1.00542485713958740234375,
+ 1.00540268421173095703125,
+ 1.005376338958740234375,
+ 1.0053546428680419921875,
+ 1.00533330440521240234375,
+ 1.005319118499755859375,
+ 1.0052816867828369140625,
+ 1.0052630901336669921875,
+ 1.005244731903076171875,
+ 1.0052082538604736328125,
+ 1.00519025325775146484375,
+ 1.00516521930694580078125,
+ 1.00515186786651611328125,
+ 1.00512027740478515625,
+ 1.00510203838348388671875,
+ 1.00508248805999755859375,
+ 1.005056858062744140625,
+ 1.00504410266876220703125,
+ 1.00502192974090576171875,
+ 1.00499999523162841796875,
+ 1.0049750804901123046875,
+ 1.0049545764923095703125,
+ 1.00493824481964111328125,
+ 1.00490796566009521484375,
+ 1.00489127635955810546875,
+ 1.0048732757568359375,
+ 1.00485432147979736328125,
+ 1.004830837249755859375,
+ 1.00480759143829345703125,
+ 1.00479614734649658203125,
+ 1.00476181507110595703125,
+ 1.00474488735198974609375,
+ 1.00472247600555419921875,
+ 1.0047113895416259765625,
+ 1.0046837329864501953125,
+ 1.0046679973602294921875,
+ 1.00465381145477294921875,
+ 1.0046253204345703125,
+ 1.00460827350616455078125,
+ 1.00458705425262451171875,
+ 1.0045731067657470703125,
+ 1.00455367565155029296875,
+ 1.00453507900238037109375,
+ 1.0045135021209716796875,
+ 1.0044963359832763671875,
+ 1.00448429584503173828125,
+ 1.00446426868438720703125,
+ 1.0044443607330322265625,
+ 1.00442469120025634765625,
+ 1.0044116973876953125,
+ 1.00438594818115234375,
+ 1.00437152385711669921875,
+ 1.004350185394287109375,
+ 1.00433647632598876953125,
+ 1.00431025028228759765625,
+ 1.0043010711669921875,
+ 1.004278659820556640625,
+ 1.00425755977630615234375,
+ 1.0042402744293212890625,
+ 1.00422823429107666015625,
+ 1.00420868396759033203125,
+ 1.004192829132080078125,
+ 1.0041687488555908203125,
+ 1.00415790081024169921875,
+ 1.00414359569549560546875,
+ 1.00412642955780029296875,
+ 1.0041067600250244140625,
+ 1.004092693328857421875,
+ 1.00407683849334716796875,
+ 1.0040628910064697265625,
+ 1.0040452480316162109375,
+ 1.00403225421905517578125,
+ 1.00400960445404052734375,
+ 1.0039999485015869140625,
+ 1.00398170948028564453125,
+ 1.00396823883056640625,
+ 1.003952503204345703125,
+ 1.0039370059967041015625,
+ 1.00391638278961181640625,
+ 1.00390613079071044921875,
+ 1.0038909912109375,
+ 1.00387585163116455078125,
+ 1.003860950469970703125,
+ 1.00384604930877685546875,
+ 1.0038239955902099609375,
+ 1.003810882568359375,
+ 1.00378787517547607421875,
+ 1.0037782192230224609375,
+ 1.0037634372711181640625,
+ 1.003753662109375,
+ 1.0037348270416259765625,
+ 1.0037243366241455078125,
+ 1.003703594207763671875,
+ 1.0036900043487548828125,
+ 1.00367641448974609375,
+ 1.00365960597991943359375,
+ 1.00364959239959716796875,
+ 1.00363194942474365234375,
+ 1.00362050533294677734375,
+ 1.003606319427490234375,
+ 1.0035927295684814453125,
+ 1.003577709197998046875,
+ 1.00356709957122802734375,
+ 1.0035459995269775390625,
+ 1.0035398006439208984375,
+ 1.00352108478546142578125,
+ 1.00350868701934814453125,
+ 1.0034964084625244140625,
+ 1.0034883022308349609375,
+ 1.00347220897674560546875,
+ 1.0034601688385009765625,
+ 1.0034482479095458984375,
+ 1.0034313201904296875,
+ 1.00342261791229248046875,
+ 1.00340712070465087890625,
+ 1.003391742706298828125,
+ 1.00337827205657958984375,
+ 1.003366947174072265625,
+ 1.00335562229156494140625,
+ 1.0033333301544189453125,
+ 1.00332772731781005859375,
+ 1.00331342220306396484375,
+ 1.003302097320556640625,
+ 1.0032875537872314453125,
+ 1.00327503681182861328125,
+ 1.003261566162109375,
+ 1.00324666500091552734375,
+ 1.00323617458343505859375,
+ 1.003225803375244140625,
+ 1.00321018695831298828125,
+ 1.00320339202880859375,
+ 1.00318801403045654296875,
+ 1.00317656993865966796875,
+ 1.0031645298004150390625,
+ 1.0031511783599853515625,
+ 1.0031425952911376953125,
+ 1.0031268596649169921875,
+ 1.00311768054962158203125,
+ 1.00310552120208740234375,
+ 1.0030958652496337890625,
+ 1.00308477878570556640625,
+ 1.00307214260101318359375,
+ 1.003063678741455078125,
+ 1.00304877758026123046875,
+ 1.00303637981414794921875,
+ 1.003030300140380859375,
+ 1.003017425537109375,
+ 1.00300967693328857421875,
+ 1.00299394130706787109375,
+ 1.00298798084259033203125,
+ 1.00297462940216064453125,
+ 1.00296294689178466796875,
+ 1.00295555591583251953125,
+ 1.00294458866119384765625,
+ 1.00293254852294921875,
+ 1.0029239654541015625,
+ 1.00290691852569580078125,
+ 1.0028984546661376953125,
+ 1.00288593769073486328125,
+ 1.00287353992462158203125,
+ 1.002865314483642578125,
+ 1.0028550624847412109375,
+ 1.00284087657928466796875,
+ 1.00283014774322509765625,
+ 1.0028247833251953125,
+ 1.00280892848968505859375,
+ 1.00279712677001953125,
+ 1.00278937816619873046875,
+ 1.00277769565582275390625,
+ 1.00276815891265869140625,
+ 1.00275981426239013671875,
+ 1.00274717807769775390625,
+ 1.00273716449737548828125,
+ 1.00272655487060546875,
+ 1.00271737575531005859375,
+ 1.0027062892913818359375,
+ 1.00269830226898193359375,
+ 1.0026881694793701171875,
+ 1.00267612934112548828125,
+ 1.00266802310943603515625,
+ 1.0026581287384033203125,
+ 1.0026471614837646484375,
+ 1.00263845920562744140625,
+ 1.00263154506683349609375,
+ 1.00261914730072021484375,
+ 1.0026109218597412109375,
+ 1.00260007381439208984375,
+ 1.002590656280517578125,
+ 1.00258123874664306640625,
+ 1.00257396697998046875,
+ 1.00256407260894775390625,
+ 1.0025575160980224609375,
+ 1.00254929065704345703125,
+ 1.0025379657745361328125,
+ 1.002526760101318359375,
+ 1.00252091884613037109375,
+ 1.002512454986572265625,
+ 1.00250303745269775390625,
+ 1.00249683856964111328125,
+ 1.00248754024505615234375,
+ 1.0024797916412353515625,
+ 1.00246906280517578125,
+ 1.00246298313140869140625,
+ 1.0024509429931640625,
+ 1.0024464130401611328125,
+ 1.00243747234344482421875,
+ 1.00242710113525390625,
+ 1.0024154186248779296875,
+ 1.00240957736968994140625,
+ 1.002398014068603515625,
+ 1.002389430999755859375,
+ 1.002380847930908203125,
+ 1.0023696422576904296875,
+ 1.00236117839813232421875,
+ 1.0023515224456787109375,
+ 1.00234317779541015625,
+ 1.00233638286590576171875,
+ 1.002325534820556640625,
+ 1.0023148059844970703125,
+ 1.00230944156646728515625,
+ 1.00230228900909423828125,
+ 1.00229179859161376953125,
+ 1.0022830963134765625,
+ 1.00227272510528564453125,
+ 1.00226497650146484375,
+ 1.0022590160369873046875,
+ 1.0022487640380859375,
+ 1.00224208831787109375,
+ 1.00223338603973388671875,
+ 1.00222551822662353515625,
+ 1.00221729278564453125,
+ 1.002211093902587890625,
+ 1.00220263004302978515625,
+ 1.002192974090576171875,
+ 1.00218498706817626953125,
+ 1.00217616558074951171875,
+ 1.00217151641845703125,
+ 1.00216448307037353515625,
+ 1.0021550655364990234375,
+ 1.0021469593048095703125,
+ 1.00214016437530517578125,
+ 1.00213325023651123046875,
+ 1.0021264553070068359375,
+ 1.002118587493896484375,
+ 1.00210964679718017578125,
+ 1.002105236053466796875,
+ 1.0020964145660400390625,
+ 1.00208985805511474609375,
+ 1.002083301544189453125,
+ 1.00207459926605224609375,
+ 1.00206816196441650390625,
+ 1.00206077098846435546875,
+ 1.00205433368682861328125,
+ 1.00204908847808837890625,
+ 1.00204074382781982421875,
+ 1.0020325183868408203125,
+ 1.00202834606170654296875,
+ 1.00202286243438720703125,
+ 1.00201475620269775390625,
+ 1.002007961273193359375,
+ 1.00199997425079345703125,
+ 1.00199401378631591796875,
+ 1.0019893646240234375,
+ 1.0019814968109130859375,
+ 1.0019762516021728515625,
+ 1.00196945667266845703125,
+ 1.00196325778961181640625,
+ 1.001956939697265625,
+ 1.001949310302734375,
+ 1.00194299221038818359375,
+ 1.00193417072296142578125,
+ 1.00192487239837646484375,
+ 1.0019180774688720703125,
+ 1.00191199779510498046875,
+ 1.00190293788909912109375,
+ 1.00189387798309326171875,
+ 1.00188791751861572265625,
+ 1.0018813610076904296875,
+ 1.00187265872955322265625,
+ 1.0018656253814697265625,
+ 1.00185871124267578125,
+ 1.0018517971038818359375,
+ 1.00184500217437744140625,
+ 1.001838207244873046875,
+ 1.00183141231536865234375,
+ 1.00182473659515380859375,
+ 1.001818180084228515625,
+ 1.001811504364013671875,
+ 1.00180494785308837890625,
+ 1.0017974376678466796875,
+ 1.00179207324981689453125,
+ 1.00178563594818115234375,
+ 1.00177776813507080078125,
+ 1.00177085399627685546875,
+ 1.001765727996826171875,
+ 1.00175893306732177734375,
+ 1.00175130367279052734375,
+ 1.00174510478973388671875,
+ 1.00174009799957275390625,
+ 1.0017330646514892578125,
+ 1.00172555446624755859375,
+ 1.00172007083892822265625,
+ 1.001715183258056640625,
+ 1.00170791149139404296875,
+ 1.0017006397247314453125,
+ 1.0016958713531494140625,
+ 1.00169050693511962890625,
+ 1.0016834735870361328125,
+ 1.0016777515411376953125,
+ 1.00167214870452880859375,
+ 1.00166666507720947265625,
+ 1.0016610622406005859375,
+ 1.00165557861328125,
+ 1.0016500949859619140625,
+ 1.00164473056793212890625,
+ 1.00163924694061279296875,
+ 1.0016338825225830078125,
+ 1.0016286373138427734375,
+ 1.0016224384307861328125,
+ 1.00161802768707275390625,
+ 1.0016129016876220703125,
+ 1.00160634517669677734375,
+ 1.001600742340087890625,
+ 1.00159657001495361328125,
+ 1.00159108638763427734375,
+ 1.0015847682952880859375,
+ 1.001579761505126953125,
+ 1.00157558917999267578125,
+ 1.0015697479248046875,
+ 1.00156366825103759765625,
+ 1.00155913829803466796875,
+ 1.0015552043914794921875,
+ 1.00154912471771240234375,
+ 1.00154316425323486328125,
+ 1.0015392303466796875,
+ 1.00153481960296630859375,
+ 1.0015289783477783203125,
+ 1.00152432918548583984375,
+ 1.001519680023193359375,
+ 1.0015151500701904296875,
+ 1.00151050090789794921875,
+ 1.00150597095489501953125,
+ 1.00150144100189208984375,
+ 1.00149691104888916015625,
+ 1.00149250030517578125,
+ 1.00148808956146240234375,
+ 1.0014836788177490234375,
+ 1.00147855281829833984375,
+ 1.001474857330322265625,
+ 1.0014705657958984375,
+ 1.00146520137786865234375,
+ 1.001457691192626953125,
+ 1.00145232677459716796875,
+ 1.0014450550079345703125,
+ 1.0014398097991943359375,
+ 1.0014326572418212890625,
+ 1.00142753124237060546875,
+ 1.00142037868499755859375,
+ 1.00141537189483642578125,
+ 1.0014083385467529296875,
+ 1.00140345096588134765625,
+ 1.00139653682708740234375,
+ 1.00139176845550537109375,
+ 1.0013849735260009765625,
+ 1.0013802051544189453125,
+ 1.0013735294342041015625,
+ 1.00136888027191162109375,
+ 1.001362323760986328125,
+ 1.00135767459869384765625,
+ 1.00135123729705810546875,
+ 1.00134670734405517578125,
+ 1.001340389251708984375,
+ 1.00133597850799560546875,
+ 1.00132977962493896484375,
+ 1.0013253688812255859375,
+ 1.0013191699981689453125,
+ 1.0013148784637451171875,
+ 1.00130879878997802734375,
+ 1.00130462646484375,
+ 1.0012986660003662109375,
+ 1.00129449367523193359375,
+ 1.0012886524200439453125,
+ 1.00128448009490966796875,
+ 1.00127875804901123046875,
+ 1.001274585723876953125,
+ 1.00126898288726806640625,
+ 1.00126492977142333984375,
+ 1.001259326934814453125,
+ 1.00125539302825927734375,
+ 1.00124990940093994140625,
+ 1.00124609470367431640625,
+ 1.00124061107635498046875,
+ 1.00123679637908935546875,
+ 1.0012314319610595703125,
+ 1.00122773647308349609375,
+ 1.00122249126434326171875,
+ 1.00121867656707763671875,
+ 1.001213550567626953125,
+ 1.00120985507965087890625,
+ 1.0012047290802001953125,
+ 1.001201152801513671875,
+ 1.0011961460113525390625,
+ 1.001192569732666015625,
+ 1.0011875629425048828125,
+ 1.00118410587310791015625,
+ 1.001179218292236328125,
+ 1.00117576122283935546875,
+ 1.0011708736419677734375,
+ 1.0011675357818603515625,
+ 1.0011627674102783203125,
+ 1.00115931034088134765625,
+ 1.0011546611785888671875,
+ 1.0011513233184814453125,
+ 1.00114667415618896484375,
+ 1.00114345550537109375,
+ 1.0011389255523681640625,
+ 1.00113570690155029296875,
+ 1.00113117694854736328125,
+ 1.0011279582977294921875,
+ 1.00112354755401611328125,
+ 1.00112044811248779296875,
+ 1.0011160373687744140625,
+ 1.00111293792724609375,
+ 1.001108646392822265625,
+ 1.0011055469512939453125,
+ 1.0011012554168701171875,
+ 1.00109827518463134765625,
+ 1.00109398365020751953125,
+ 1.00109100341796875,
+ 1.0010869503021240234375,
+ 1.00108397006988525390625,
+ 1.0010797977447509765625,
+ 1.0010769367218017578125,
+ 1.00107288360595703125,
+ 1.0010700225830078125,
+ 1.00106608867645263671875,
+ 1.00106322765350341796875,
+ 1.0010592937469482421875,
+ 1.0010564327239990234375,
+ 1.0010526180267333984375,
+ 1.0010497570037841796875,
+ 1.0010459423065185546875,
+ 1.00104320049285888671875,
+ 1.00103938579559326171875,
+ 1.00103676319122314453125,
+ 1.00103294849395751953125,
+ 1.00103032588958740234375,
+ 1.001026630401611328125,
+ 1.0010240077972412109375,
+ 1.00102031230926513671875,
+ 1.0010178089141845703125,
+ 1.00101411342620849609375,
+ 1.0010116100311279296875,
+ 1.00100803375244140625,
+ 1.00100553035736083984375,
+ 1.00100195407867431640625,
+ 1.00099945068359375,
+ 1.00099599361419677734375,
+ 1.0009934902191162109375,
+ 1.00099003314971923828125,
+ 1.00098764896392822265625,
+ 1.00098419189453125,
+ 1.000981807708740234375,
+ 1.0009784698486328125,
+ 1.0009746551513671875,
+ 1.00096893310546875,
+ 1.00096333026885986328125,
+ 1.00095784664154052734375,
+ 1.00095236301422119140625,
+ 1.00094687938690185546875,
+ 1.0009415149688720703125,
+ 1.0009362697601318359375,
+ 1.0009310245513916015625,
+ 1.00092589855194091796875,
+ 1.000920772552490234375,
+ 1.00091564655303955078125,
+ 1.00091063976287841796875,
+ 1.0009057521820068359375,
+ 1.00090086460113525390625,
+ 1.000895977020263671875,
+ 1.000891208648681640625,
+ 1.000886440277099609375,
+ 1.00088179111480712890625,
+ 1.0008771419525146484375,
+ 1.00087249279022216796875,
+ 1.00086796283721923828125,
+ 1.000863552093505859375,
+ 1.0008590221405029296875,
+ 1.00085461139678955078125,
+ 1.00085031986236572265625,
+ 1.00084590911865234375,
+ 1.00084173679351806640625,
+ 1.00083744525909423828125,
+ 1.0008332729339599609375,
+ 1.00082910060882568359375,
+ 1.00082504749298095703125,
+ 1.00082099437713623046875,
+ 1.00081694126129150390625,
+ 1.000813007354736328125,
+ 1.0008089542388916015625,
+ 1.0008051395416259765625,
+ 1.00080120563507080078125,
+ 1.00079739093780517578125,
+ 1.00079357624053955078125,
+ 1.0007898807525634765625,
+ 1.0007860660552978515625,
+ 1.00078237056732177734375,
+ 1.00077879428863525390625,
+ 1.0007750988006591796875,
+ 1.00077152252197265625,
+ 1.0007679462432861328125,
+ 1.00076448917388916015625,
+ 1.0007610321044921875,
+ 1.00075757503509521484375,
+ 1.0007541179656982421875,
+ 1.00075066089630126953125,
+ 1.00074732303619384765625,
+ 1.00074398517608642578125,
+ 1.00074064731597900390625,
+ 1.0007374286651611328125,
+ 1.00073421001434326171875,
+ 1.000730991363525390625,
+ 1.00072777271270751953125,
+ 1.0007245540618896484375,
+ 1.000721454620361328125,
+ 1.0007183551788330078125,
+ 1.0007152557373046875,
+ 1.0007121562957763671875,
+ 1.00070917606353759765625,
+ 1.000706195831298828125,
+ 1.00070321559906005859375,
+ 1.0007002353668212890625,
+ 1.00069725513458251953125,
+ 1.00069439411163330078125,
+ 1.00069153308868408203125,
+ 1.00068867206573486328125,
+ 1.00068581104278564453125,
+ 1.00068295001983642578125,
+ 1.0006802082061767578125,
+ 1.00067746639251708984375,
+ 1.000674724578857421875,
+ 1.00067198276519775390625,
+ 1.0006692409515380859375,
+ 1.00066661834716796875,
+ 1.0006639957427978515625,
+ 1.000661373138427734375,
+ 1.0006587505340576171875,
+ 1.0006561279296875,
+ 1.0006535053253173828125,
+ 1.00065100193023681640625,
+ 1.00064849853515625,
+ 1.0006458759307861328125,
+ 1.0006434917449951171875,
+ 1.00064098834991455078125,
+ 1.000638484954833984375,
+ 1.00063610076904296875,
+ 1.00063359737396240234375,
+ 1.00063121318817138671875,
+ 1.00062882900238037109375,
+ 1.00062656402587890625,
+ 1.000624179840087890625,
+ 1.000621795654296875,
+ 1.00061953067779541015625,
+ 1.0006172657012939453125,
+ 1.00061500072479248046875,
+ 1.000612735748291015625,
+ 1.00061047077178955078125,
+ 1.0006082057952880859375,
+ 1.000606060028076171875,
+ 1.00060379505157470703125,
+ 1.00060164928436279296875,
+ 1.00059950351715087890625,
+ 1.00059735774993896484375,
+ 1.00059521198272705078125,
+ 1.00059306621551513671875,
+ 1.00059092044830322265625,
+ 1.000588893890380859375,
+ 1.0005867481231689453125,
+ 1.00058472156524658203125,
+ 1.00058269500732421875,
+ 1.00058066844940185546875,
+ 1.0005786418914794921875,
+ 1.00057661533355712890625,
+ 1.00057470798492431640625,
+ 1.000572681427001953125,
+ 1.000570774078369140625,
+ 1.00056874752044677734375,
+ 1.00056684017181396484375,
+ 1.00056493282318115234375,
+ 1.00056302547454833984375,
+ 1.00056111812591552734375,
+ 1.00055921077728271484375,
+ 1.00055730342864990234375,
+ 1.000555515289306640625,
+ 1.000553607940673828125,
+ 1.00055181980133056640625,
+ 1.0005500316619873046875,
+ 1.00054824352264404296875,
+ 1.00054633617401123046875,
+ 1.00054454803466796875,
+ 1.0005428791046142578125,
+ 1.00054109096527099609375,
+ 1.000539302825927734375,
+ 1.0005376338958740234375,
+ 1.00053584575653076171875,
+ 1.00053417682647705078125,
+ 1.0005323886871337890625,
+ 1.000530719757080078125,
+ 1.0005290508270263671875,
+ 1.00052738189697265625,
+ 1.0005257129669189453125,
+ 1.000524044036865234375,
+ 1.0005223751068115234375,
+ 1.00052082538604736328125,
+ 1.00051915645599365234375,
+ 1.00051748752593994140625,
+ 1.00051593780517578125,
+ 1.00051438808441162109375,
+ 1.00051271915435791015625,
+ 1.00051116943359375,
+ 1.00050961971282958984375,
+ 1.0005080699920654296875,
+ 1.00050652027130126953125,
+ 1.000504970550537109375,
+ 1.00050342082977294921875,
+ 1.00050199031829833984375,
+ 1.0005004405975341796875,
+ 1.00049889087677001953125,
+ 1.00049746036529541015625,
+ 1.00049602985382080078125,
+ 1.000494480133056640625,
+ 1.00049304962158203125,
+ 1.000491619110107421875,
+ 1.0004901885986328125,
+ 1.000488758087158203125
+};
+
+
+#include <unistd.h>
+
+int main()
+{
+ ::write(1, data, sizeof(data));
+}
+
diff --git a/noatun/library/globalvideo.h b/noatun/library/globalvideo.h
new file mode 100644
index 00000000..e0f28cb0
--- /dev/null
+++ b/noatun/library/globalvideo.h
@@ -0,0 +1,26 @@
+#ifndef __GLOBALVIDEO_H
+#define __GLOBALVIDEO_H
+
+#include "noatun/video.h"
+
+
+class GlobalVideo : public QWidget
+{
+Q_OBJECT
+ QPopupMenu *menu;
+ VideoFrame *video;
+
+public:
+ GlobalVideo();
+
+public slots:
+ void appear();
+ void hide();
+ void slotAdaptSize(int w, int h);
+
+protected:
+ void mouseReleaseEvent(QMouseEvent *e);
+};
+
+
+#endif
diff --git a/noatun/library/ksaver.cpp b/noatun/library/ksaver.cpp
new file mode 100644
index 00000000..f1cc6a61
--- /dev/null
+++ b/noatun/library/ksaver.cpp
@@ -0,0 +1,184 @@
+// ksaver.cpp
+//
+// Copyright (C) 2001 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <klocale.h>
+#include <ktempfile.h>
+#include <kio/netaccess.h>
+
+#include "ksaver.h"
+
+class Noatun::KSaver::KSaverPrivate
+{
+public:
+ KSaverPrivate() : isLocal(true), tempFile(0), file(0), textStream(0), dataStream(0) {};
+ bool isLocal;
+ KTempFile *tempFile;
+ QFile *file;
+ KURL url;
+ QString error;
+ QTextStream *textStream;
+ QDataStream *dataStream;
+};
+
+Noatun::KSaver::KSaver(const KURL &_target)
+{
+ d = new KSaverPrivate;
+ d->url = _target;
+
+ if(d->url.protocol() == "file")
+ {
+ d->isLocal = true;
+ d->file = new QFile(d->url.path());
+ }
+ else
+ {
+ d->isLocal = false;
+ }
+}
+
+Noatun::KSaver::~KSaver()
+{
+ close();
+ delete d;
+}
+
+bool Noatun::KSaver::open(void)
+{
+ if(d->isLocal)
+ {
+ if(d->file->open(IO_WriteOnly))
+ {
+ return true;
+ }
+ else
+ {
+ d->error = i18n("Could not write to %1.").arg(d->url.prettyURL());
+ return false;
+ }
+ }
+ else
+ {
+ d->tempFile = new KTempFile;
+ return true;
+ }
+}
+
+bool Noatun::KSaver::close(void)
+{
+ if(!d->isLocal && d->tempFile)
+ delete d->textStream;
+ d->textStream = 0;
+
+ if(!d->isLocal && d->tempFile)
+ delete d->dataStream;
+ d->dataStream = 0;
+
+ if(d->isLocal)
+ {
+ if(!d->file) return true;
+
+ delete d->file;
+ d->file = 0;
+ return true;
+ }
+ else
+ {
+ if(!d->tempFile) return true;
+
+ d->tempFile->close();
+ d->textStream = 0;
+ d->dataStream = 0;
+
+ bool retval = KIO::NetAccess::upload(d->tempFile->name(), d->url);
+
+ delete d->tempFile;
+ d->tempFile = 0;
+
+ return retval;
+ }
+}
+
+QString Noatun::KSaver::error(void)
+{
+ return d->error;
+}
+
+QFile &Noatun::KSaver::file(void)
+{
+ if(d->isLocal && d->file)
+ return *d->file;
+ else if(!d->isLocal && d->tempFile)
+ return *d->tempFile->file();
+ else
+ return *static_cast<QFile *>(0);
+}
+
+QTextStream &Noatun::KSaver::textStream()
+{
+ if(d->textStream)
+ {
+ return *d->textStream;
+ }
+ else if(d->isLocal && d->file)
+ {
+ d->textStream = new QTextStream(d->file);
+ return *d->textStream;
+ }
+ else if(!d->isLocal && d->tempFile)
+ {
+ d->textStream = d->tempFile->textStream();
+ return *d->textStream;
+ }
+ else
+ {
+ return *static_cast<QTextStream *>(0);
+ }
+}
+
+QDataStream &Noatun::KSaver::dataStream()
+{
+ if(d->dataStream)
+ {
+ return *d->dataStream;
+ }
+ else if(d->isLocal && d->file)
+ {
+ d->dataStream = new QDataStream(d->file);
+ return *d->dataStream;
+ }
+ else if(!d->isLocal && d->tempFile)
+ {
+ d->dataStream = d->tempFile->dataStream();
+ return *d->dataStream;
+ }
+ else
+ {
+ return *static_cast<QDataStream *>(0);
+ }
+}
diff --git a/noatun/library/ksaver.h b/noatun/library/ksaver.h
new file mode 100644
index 00000000..e55b2c0d
--- /dev/null
+++ b/noatun/library/ksaver.h
@@ -0,0 +1,102 @@
+// Copyright (C) 2001 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifndef KSAVER_H
+#define KSAVER_H
+
+#include <kurl.h>
+#include <qfile.h>
+#include <qstring.h>
+#include <qtextstream.h>
+#include <qdatastream.h>
+
+namespace Noatun
+{
+
+/**
+ * KSaver provides a way to save files in the most efficient way for
+ * both local and remote URLs.
+ */
+class KSaver
+{
+public:
+ /**
+ * The constructor takes the url and decides the best way to save,
+ * which will mean using something like KIO::NetAccess or QFile.
+ */
+ KSaver(const KURL &_target);
+
+ /**
+ * The destructor closes if necessary.
+ */
+ ~KSaver();
+
+ /**
+ * open actually tries to open the file
+ *
+ * true on success, false on failure (get the error in @ref error)
+ */
+ bool open(void);
+
+ /**
+ * close closes the file, and uploads if necessary.
+ *
+ * true on success, false on failure (get the error in @ref error)
+ */
+ bool close(void);
+
+ /**
+ * If open or close returns false, there was an error, and error
+ * returns what the error was, when available.
+ */
+ QString error(void);
+
+ /**
+ * file returns a QFile open for writing, which may be for a temporary
+ * file on the local filesystem.
+ *
+ * If this is called before the file is opened, you will crash.
+ */
+ QFile &file(void);
+
+ /**
+ * You can use this to write out your data.
+ *
+ * If this is called before the file is opened, you will crash.
+ */
+ QTextStream &textStream(void);
+
+ /**
+ * You can use this to write out your data.
+ *
+ * If this is called before the file is opened, you will crash.
+ */
+ QDataStream &dataStream(void);
+private:
+ class KSaverPrivate;
+ KSaverPrivate *d;
+};
+
+}
+
+#endif
diff --git a/noatun/library/mimetypetree.cpp b/noatun/library/mimetypetree.cpp
new file mode 100644
index 00000000..c2b60c81
--- /dev/null
+++ b/noatun/library/mimetypetree.cpp
@@ -0,0 +1,64 @@
+#include "mimetypetree.h"
+#include <kmimetype.h>
+#include <qdict.h>
+#include <qheader.h>
+
+
+MimeTypeTree::MimeTypeTree(QWidget *parent)
+ : KListView(parent)
+{
+ KMimeType::List list=KMimeType::allMimeTypes();
+ QDict<QListViewItem> map;
+ setRootIsDecorated(true);
+
+ addColumn("-");
+ header()->hide();
+ QValueListIterator<KMimeType::Ptr> i(list.begin());
+ for (; i != list.end(); ++i)
+ {
+ QString mimetype = (*i)->name();
+ int slash = mimetype.find("/");
+ QString major = mimetype.left(slash);
+
+ // hide all and inode majors
+ if (major == "all" || major=="inode")
+ continue;
+
+ QString minor = mimetype.mid(slash+1);
+ QListViewItem *majorItem=map[major];
+ if (!majorItem)
+ {
+ majorItem=addMajor(major);
+ map.insert(major, majorItem);
+ }
+
+ new QListViewItem(majorItem, minor);
+ }
+}
+
+void MimeTypeTree::sel(QListViewItem *item)
+{
+ QListViewItem *p=item->parent();
+ if (!p) return;
+ QString major=p->text(0);
+ QString minor=item->text(0);
+
+ emit selected(major+'/'+minor);
+}
+
+QListViewItem* MimeTypeTree::addMajor(const QString &name)
+{
+ return new QListViewItem(this, name);
+}
+
+
+// GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666
+// 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL
+// GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666
+// 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL
+// GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666
+// 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL
+// GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666
+// 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL
+
+#include "mimetypetree.moc"
diff --git a/noatun/library/mimetypetree.h b/noatun/library/mimetypetree.h
new file mode 100644
index 00000000..1f315526
--- /dev/null
+++ b/noatun/library/mimetypetree.h
@@ -0,0 +1,35 @@
+/***
+ * Copyright (c) 2001 Charles Samuels <charles@kde.org>
+ * Standard BSD License. Second version.
+ * The added stipulation is that this cannot link
+ * to GPL code. Except in the explicit case
+ * of Noatun linking to this, and to a GPL plugin,
+ * where the GPL plugin does not use any code
+ * in this class. However, it may link directly
+ * to the Qt Library, where Qt may be under any license.
+ *
+ * Debian, Gnome, and GNU must ALL DIE.
+ * Especially GNU's stupid info pages.
+ **/
+#ifndef MIMETYPETREE_H
+#define MIMETYPETREE_H
+
+#include <klistview.h>
+
+class MimeTypeTree : public KListView
+{
+Q_OBJECT
+public:
+ MimeTypeTree(QWidget *parent);
+
+private:
+ QListViewItem *addMajor(const QString &name);
+private slots:
+ void sel(QListViewItem *item);
+
+signals:
+ void selected(const QString &mimetype);
+};
+
+#endif
+
diff --git a/noatun/library/noatun/Makefile.am b/noatun/library/noatun/Makefile.am
new file mode 100644
index 00000000..115760b0
--- /dev/null
+++ b/noatun/library/noatun/Makefile.am
@@ -0,0 +1,10 @@
+
+noatuninclude_HEADERS = \
+ effects.h playlist.h plugin.h \
+ controls.h engine.h pref.h pluginloader.h \
+ conversion.h equalizer.h stdaction.h scrollinglabel.h \
+ downloader.h app.h player.h stereobuttonaction.h \
+ playlistsaver.h video.h vequalizer.h
+
+noatunincludedir = $(includedir)/noatun
+
diff --git a/noatun/library/noatun/app.h b/noatun/library/noatun/app.h
new file mode 100644
index 00000000..c4effa4d
--- /dev/null
+++ b/noatun/library/noatun/app.h
@@ -0,0 +1,271 @@
+#ifndef NOATUN_H
+#define NOATUN_H
+
+#include <kuniqueapplication.h>
+#include <kdemacros.h>
+class Playlist;
+class Player;
+class LibraryLoader;
+class KPopupMenu;
+class NoatunPreferences;
+class Downloader;
+class Effects;
+class EffectView;
+class Equalizer;
+class KDialogBase;
+class VEqualizer;
+
+namespace NoatunStdAction
+{
+ class PluginActionMenu;
+}
+
+/**
+ * @class NoatunApp app.h noatun/app.h
+ * Can be accessed from every plugin by using "napp"
+ *
+ * @short Noatun Application class
+ * @author Charles Samuels
+ * @version 2.3
+ */
+class KDE_EXPORT NoatunApp : public KUniqueApplication
+{
+Q_OBJECT
+friend class Playlist;
+
+public:
+ NoatunApp();
+ ~NoatunApp();
+
+ /**
+ * Provides access to the central playlist object.
+ * Any plugin can access the noatun playlist with
+ * <pre>napp->playlist();</pre>
+ **/
+ Playlist *playlist() const;
+ /**
+ * access to the central player object
+ * Any plugin can access the noatun player backend with
+ * <pre>napp->playlist();</pre>
+ **/
+ Player *player() const { return mPlayer; }
+ /**
+ * access to NoatunPreferences
+ **/
+ NoatunPreferences *preferencesBox() const { return mPref; }
+
+ /**
+ * @return a list of mimetypes aRts (and thus Noatun) can play
+ * KFileDialog accepts this QString instead of the shell globs in
+ * its static functions, make use of it :)
+ **/
+ QString mimeTypes();
+
+ LibraryLoader *libraryLoader() const { return mLibraryLoader; }
+ Downloader *downloader() const { return mDownloader; }
+ static QImage readPNG(const QString &filename);
+ Effects *effects() const;
+ ::Equalizer *equalizer() const { return mEqualizer; }
+ ::VEqualizer *vequalizer();
+ KDialogBase *equalizerView() const { return mEqualizerView; }
+
+ QCString version() const;
+
+ virtual void commitData(QSessionManager &);
+ virtual void saveState(QSessionManager &);
+
+ /**
+ * The three startup modes how noatun should behave when it is
+ * restarted.
+ *
+ * Restore - it can restore the player's last state
+ *
+ * Play - it automatically starts playing the next file in the
+ * playlist
+ *
+ * DontPlay - it doesn't start playing
+ */
+ enum StartupPlayMode { Restore = 0, Play, DontPlay };
+
+signals:
+ /**
+ * Tells you (a UI plugin) to hide
+ */
+ void hideYourself();
+
+ /**
+ * Tells you (a UI plugin) to show again
+ */
+ void showYourself();
+
+public slots:
+ /**
+ * ask the UIs to hide or show
+ **/
+ void toggleInterfaces();
+ /**
+ * ask the UIs to show
+ **/
+ void showInterfaces();
+ /**
+ * ask the UIs to hide, then you have
+ * to look around for them, or you'll lose
+ **/
+ void hideInterfaces();
+
+public: //options
+ /**
+ * @deprecated Use startupPlayMode() instead
+ */
+ bool autoPlay() const;
+ int startupPlayMode() const;
+ bool loopList() const;
+ bool oneInstance() const;
+ QString saveDirectory() const;
+ /**
+ * @deprecated
+ * now merged with clearOnOpen()
+ **/
+ bool clearOnStart() const;
+ /**
+ * @return true if the playlist will be cleared when opening a
+ * new file via commandline or file open dialog, false otherwise
+ **/
+ bool clearOnOpen() const;
+ bool hackUpPlaylist() const;
+ /**
+ * @return true if hardware-mixing is being used, false in case
+ * software mixing is active
+ **/
+ bool fastMixer() const;
+ QString titleFormat() const;
+ /**
+ * @return true if remaining time is displayed to the user, false if
+ * played time is displayed
+ **/
+ bool displayRemaining() const;
+
+ void setOneInstance(bool);
+ void setLoopList(bool);
+ /**
+ * @deprecated Use setStartupPlayMode() instead
+ */
+ void setAutoPlay(bool);
+ void setStartupPlayMode(int mode);
+ void setSaveDirectory(const QString &);
+ void setRememberPositions(bool);
+ /**
+ * @deprecated
+ * now merged with setClearOnOpen()
+ **/
+ void setClearOnStart(bool);
+ /**
+ * Set if the playlist will be cleared when opening a
+ * new file via commandline or file open dialog
+ **/
+ void setClearOnOpen(bool);
+ void setHackUpPlaylist(bool);
+
+ /**
+ * Set if hardware-mixing ("fast") or software-mixing ("slow") should be used
+ **/
+ void setFastMixer(bool);
+
+ void setTitleFormat(const QString &);
+
+ /**
+ * Pass true if remaining time should be displayed to the user, false if
+ * played time should be displayed
+ **/
+ void setDisplayRemaining(bool);
+
+ /**
+ * To insert items use KActions and insert() them into pluginActionMenu().
+ * @return pointer to the actionmenu
+ */
+ NoatunStdAction::PluginActionMenu *pluginActionMenu();
+
+ /**
+ * @deprecated
+ * Adds an item to the plugin menu.
+ * You may use this value with pluginMenu() for greater control of your menu entry
+ *
+ * @return the ID associated with the menu item, for use in pluginMenuRemove()
+ **/
+ int pluginMenuAdd(const QString &text, const QObject *receiver, const char *member);
+
+ /**
+ * @deprecated
+ * Removes an item previously added to the plugin menu.
+ * @param id the ID of the "to be removed" menu item
+ **/
+ void pluginMenuRemove(int id);
+
+ /**
+ * @deprecated
+ * Use pluginActionMenu() instead
+ * @return pointer to the plugin menu
+ */
+ KPopupMenu *pluginMenu();
+
+protected:
+ virtual int newInstance();
+
+public slots:
+ // slots for the contextMenu
+ /**
+ * Opens the preferences dialog
+ * You can also use
+ * <pre>napp->preferencesBox()->show()</pre>
+ * @see NoatunPreferences
+ */
+ void preferences();
+ /**
+ * Exits Noatun
+ */
+ void quit();
+ /**
+ * Shows the standard file-open dialog
+ */
+ void fileOpen();
+ /**
+ * Shows the effects window
+ */
+ void effectView();
+ /**
+ * Shows the equalizer window
+ */
+ void equalizerView();
+
+private:
+ void loadPlugins();
+ void saveEngineState();
+ void restoreEngineState();
+
+private:
+ Player *mPlayer;
+ LibraryLoader *mLibraryLoader;
+ KPopupMenu *mPluginMenu;
+ NoatunStdAction::PluginActionMenu *mPluginActionMenu;
+ Downloader *mDownloader;
+ struct Private;
+ Private *d;
+ EffectView *mEffectView;
+ NoatunPreferences *mPref;
+ ::Equalizer *mEqualizer;
+ KDialogBase *mEqualizerView;
+ bool showingInterfaces;
+};
+
+#define napp (static_cast<NoatunApp*>(kapp))
+
+// version info for the plugins
+// this is MAJOR.MINOR.PATCHLEVEL
+// and you developers better ignore patchlevel :)
+#define NOATUN_MAJOR 2
+#define NOATUN_MINOR 10
+#define NOATUN_PATCHLEVEL 0
+
+#define NOATUN_VERSION "2.10.0"
+
+#endif
diff --git a/noatun/library/noatun/controls.h b/noatun/library/noatun/controls.h
new file mode 100644
index 00000000..6db8d1cd
--- /dev/null
+++ b/noatun/library/noatun/controls.h
@@ -0,0 +1,79 @@
+#ifndef __CONTROLS_H
+#define __CONTROLS_H
+
+#include <qguardedptr.h>
+
+#include <kaction.h>
+#include <ktoolbar.h>
+#include <qslider.h>
+#include <qstringlist.h>
+#include <kdemacros.h>
+
+class QComboBox;
+class QLabel;
+
+/**
+ * A slider that can be moved around while being
+ * changed internally
+ *
+ * @short Special QSlider based class suitable for time sliders
+ * @author Charles Samuels
+ * @version 2.3
+ **/
+class KDE_EXPORT L33tSlider : public QSlider
+{
+Q_OBJECT
+public:
+ L33tSlider(QWidget * parent, const char * name=0);
+ L33tSlider(Orientation, QWidget * parent, const char * name=0);
+ L33tSlider(int minValue, int maxValue, int pageStep, int value,
+ Orientation, QWidget * parent, const char * name=0);
+
+ bool currentlyPressed() const;
+signals:
+ /**
+ * emmited only when the user changes the value by hand
+ **/
+ void userChanged(int value);
+
+public slots:
+ virtual void setValue(int);
+
+protected:
+ virtual void mousePressEvent(QMouseEvent*);
+ virtual void mouseReleaseEvent(QMouseEvent*);
+ virtual void wheelEvent(QWheelEvent *);
+
+private:
+ bool pressed;
+};
+
+/**
+ * @short A slider for your toolbar
+ * @author Charles Samuels
+ * @version 2.3
+ **/
+class SliderAction : public KAction
+{
+Q_OBJECT
+public:
+ SliderAction(const QString& text, int accel, const QObject *receiver,
+ const char *member, QObject* parent, const char* name );
+ virtual int plug( QWidget *w, int index = -1 );
+ virtual void unplug( QWidget *w );
+ QSlider* slider() const { return m_slider; }
+
+signals:
+ void plugged();
+
+public slots:
+ void toolbarMoved(KToolBar::BarPosition pos);
+
+private:
+ QGuardedPtr<QSlider> m_slider;
+ QStringList m_items;
+ const QObject *m_receiver;
+ const char *m_member;
+};
+
+#endif
diff --git a/noatun/library/noatun/conversion.h b/noatun/library/noatun/conversion.h
new file mode 100644
index 00000000..841ed0f8
--- /dev/null
+++ b/noatun/library/noatun/conversion.h
@@ -0,0 +1,117 @@
+#ifndef NOATUN_CONVERT_H
+#define NOATUN_CONVERT_H
+
+/**
+ * Helper functions to convert between sample types
+ * @short sample type conversion
+ **/
+namespace Conversion
+{
+/**
+ * Convert a mono 8 bit group to float
+ **/
+void convertMono8ToFloat(unsigned long samples, unsigned char *from, float *to);
+
+/**
+ * convert a mono 8 bit group to float, at a different speed
+ **/
+void interpolateMono8ToFloat(unsigned long samples, double start, double speed,
+ unsigned char *from, float *to);
+
+/**
+ * convert a mono 16 bit little-endian stream to float
+ **/
+void convertMono16leToFloat(unsigned long samples, unsigned char *from, float *to);
+
+/**
+ * convert a mono 16 bit little-endian stream to float at a different speed
+ **/
+void interpolateMono16leToFloat(unsigned long samples, double startpos, double speed,
+ unsigned char *from, float *to);
+
+/**
+ * convert an stereo 8-bit interleaved (Alternating left/right channel) to floats
+ **/
+void convertStereoI8To2Float(unsigned long samples, unsigned char *from,
+ float *left, float *right);
+
+/**
+ * convert a stereo 8-bit interleaved (alternating left/right channel) to floats,
+ * at a different speed
+ **/
+void interpolateStereoI8To2Float(unsigned long samples, double startpos, double speed,
+ unsigned char *from, float *left, float *right);
+/**
+ * convert an interleaved 16 bit little endian stream to two floats
+ **/
+void convertStereoI16leTo2Float(unsigned long samples, unsigned char *from, float *left,
+ float *right);
+
+/**
+ * convert an interleaved 16 bit little endian stream to two floats at a different
+ * speed
+ **/
+void interpolateStereoI16leTo2Float(unsigned long samples, double startpos, double speed,
+ unsigned char *from, float *left, float *right);
+
+/**
+ * convert a float to a float, but at a different speed
+ **/
+void interpolateMonoFloatToFloat(unsigned long samples, double startpos, double speed,
+ float *from, float *to);
+
+/**
+ * convert a stereo interleaved float to two floats
+ **/
+void convertStereoIFloatTo2Float(unsigned long samples, float *from, float *left,
+ float *right);
+
+/**
+ * convert a stereo interleaved float to two floats at a different speed
+ **/
+void interpolateStereoIFloatTo2Float(unsigned long samples, double startpos,
+ double speed, float *from, float *left,
+ float *right);
+
+/**
+ * convert a mono float to a 16 bit little endian float
+ **/
+void convertMonoFloatTo16le(unsigned long samples, float *from, unsigned char *to);
+
+/**
+ * convert a two floats to a 16 bit little endian interleaved float
+ **/
+void convertStereo2FloatToI16le(unsigned long samples, float *left, float *right,
+ unsigned char *to);
+
+/**
+ * convert a mono float to an 8 bit stream
+ **/
+void convertMonoFloatTo8(unsigned long samples, float *from, unsigned char *to);
+
+/**
+ * convert two floats to an 8 bit interleaved stream
+ **/
+void convertStereo2FloatToI8(unsigned long samples, float *left, float *right,
+ unsigned char *to);
+
+/**
+ * to little endian (Intel) with swapEndian
+ * does nothing if this is an intel
+ **/
+inline void toLittleEndian(unsigned long len, char *buffer);
+
+/**
+ * to big endian with swapEndian
+ * does nothing if this isn't an intel
+ **/
+inline void toBigEndian(unsigned long len, char *buffer);
+
+/**
+ * swap the endian, does so on every platform
+ * operates on 16 bits at a time. so loads 16, swaps, and copies them
+ **/
+void swapEndian(unsigned long length, char *buffer);
+}
+
+#endif
diff --git a/noatun/library/noatun/downloader.h b/noatun/library/noatun/downloader.h
new file mode 100644
index 00000000..c281a006
--- /dev/null
+++ b/noatun/library/noatun/downloader.h
@@ -0,0 +1,120 @@
+#ifndef _DOWNLOADER_H
+#define _DOWNLOADER_H
+
+#include <kurl.h>
+#include <qobject.h>
+#include <qptrlist.h>
+
+class QFile;
+class QTimer;
+class Downloader;
+
+
+namespace KIO
+{
+ class TransferJob;
+ class Job;
+}
+
+/**
+ * Item to download, usually queued up in Downloader
+ **/
+class DownloadItem
+{
+ friend class Downloader;
+public:
+ DownloadItem();
+ virtual ~DownloadItem();
+
+ bool isDownloaded() const;
+
+ /**
+ * @return the local filename this item will be saved to
+ **/
+ QString localFilename() const;
+
+ virtual void setLocalFilename(const QString &filename);
+
+ /**
+ * Called if this item has been fully downloaded
+ **/
+ virtual void downloadFinished();
+ /**
+ * Called at regular intervals while downloading this item
+ **/
+ virtual void downloaded(int percent);
+ /**
+ * Called when downloading this item timed out
+ **/
+ virtual void downloadTimeout();
+ /**
+ * @return true if the download was scheduled, false if the file is local
+ **/
+ bool enqueue(const KURL &url);
+ /**
+ * Remove this item from Downloader queue
+ **/
+ void dequeue();
+
+private:
+ QString mLocalFilename;
+};
+
+/**
+ * download playlistitems, in a queue based fasion
+ **/
+class Downloader : public QObject
+{
+Q_OBJECT
+ struct QueueItem
+ {
+ DownloadItem *notifier;
+ KURL file;
+ QString local;
+ };
+
+public:
+ Downloader(QObject *parent=0);
+ virtual ~Downloader();
+
+public slots:
+ QString enqueue(DownloadItem *notifier, const KURL &file);
+ void dequeue(DownloadItem *notifier);
+
+ /**
+ * @internal
+ **/
+ void start();
+
+signals:
+ /**
+ * Emitted when a new DownloadItem was added to the queue
+ * @p notifier is the added item
+ **/
+ void enqueued(DownloadItem *notifier, const KURL &file);
+ /**
+ * Emitted when a DownloadItem was removed from the queue
+ * @p notifier is the removed item
+ **/
+ void dequeued(DownloadItem *notifier);
+
+private slots:
+ void getNext();
+
+ void data( KIO::Job *, const QByteArray &data);
+ void percent( KIO::Job *, unsigned long percent);
+ void jobDone( KIO::Job *);
+ void giveUpWithThisDownloadServerIsRunningNT();
+
+private:
+ QPtrList<Downloader::QueueItem> mQueue;
+ QPtrList<Downloader::QueueItem> *mUnstartedQueue;
+ QFile *localfile;
+ Downloader::QueueItem *current;
+ KIO::TransferJob *mJob;
+ QTimer *mTimeout;
+ bool mStarted;
+};
+
+#endif
+
diff --git a/noatun/library/noatun/effects.h b/noatun/library/noatun/effects.h
new file mode 100644
index 00000000..c39cb8aa
--- /dev/null
+++ b/noatun/library/noatun/effects.h
@@ -0,0 +1,186 @@
+#ifndef EFFECTS_H
+#define EFFECTS_H
+
+#include <qptrlist.h>
+#include <qcstring.h>
+#include <qstrlist.h>
+#include <qobject.h>
+
+namespace Arts { class StereoEffect; }
+class Engine;
+class EffectConfigWidget;
+
+/**
+ * @short Base class for a noatun effect
+ *
+ * Example:
+ * \code
+ * new Effect("Arts::SomeEffect");
+ * \endcode
+ * Then you can add, insert, etc. it using Effects
+ **/
+class Effect
+{
+friend class Effects;
+friend class EffectConfigWidget;
+public:
+ Effect(const char *name);
+ ~Effect();
+
+ /**
+ * return the effect processed
+ * directly before this one
+ **/
+ Effect *before() const;
+ /**
+ * return the effect processed
+ * directly after this one
+ **/
+ Effect *after() const;
+ long id() const;
+
+ /**
+ * get the Arts object.
+ * @internal
+ **/
+ Arts::StereoEffect *effect() const;
+
+ /**
+ * Get the name of the object.
+ **/
+ QCString name() const;
+
+ /**
+ * get the "clean" title of effect
+ **/
+ QString title() const;
+
+ /**
+ * @return true if this effect name is invalid, false otherwise
+ * <b>Note:</b> If you call StereoEffect::start() on an invalid Effect you
+ * will probably be punished with a segmentation fault.
+ **/
+ bool isNull() const;
+
+ /**
+ * show the configure dialog box for
+ * this effect. if friendly is true,
+ * then create a top-level window,
+ * set an icon and make it purdy. Otherwise
+ * create a plan widget that you can reparent.
+ **/
+ QWidget *configure(bool friendly=true);
+
+ /**
+ * Does this widget have a configurable
+ * dialog box. E.g., will configure
+ * return null?
+ **/
+ bool configurable() const;
+
+ /**
+ * Return an effect name that can be presented to a user
+ * i.e. Arts::FREEVERB will end up as FREEVERB
+ **/
+ static QString clean(const QCString &name);
+private:
+ long mId;
+ Arts::StereoEffect *mEffect;
+ QCString mName;
+ QWidget *mConfig;
+};
+
+/**
+ * Noatuns effect stack
+ * @author Charles Samuels
+ **/
+class Effects : public QObject
+{
+Q_OBJECT
+friend class Effect;
+public:
+ Effects();
+
+ bool insert(const Effect *after, Effect *item);
+
+ /**
+ * create the Effect, by getting the proper item
+ * from the list, then append it here.
+ *
+ * for example
+ * \code
+ * append(new Effect(available()[0]));
+ * \endcode
+ **/
+ bool append(Effect *item);
+
+ /**
+ * reorder the effect stack. If @p after is null,
+ * it'll be first
+ **/
+ void move(const Effect *after, Effect *item);
+ /**
+ * Removes an item from the effect stack
+ * @param item item to remove
+ * @param del delete the item from the effect stack as well (default true)
+ **/
+ void remove(Effect *item, bool del=true);
+
+ /**
+ * Removes all items from the effect stack.
+ * @param del delete the items from the effect stack as well (default true)
+ **/
+ void removeAll(bool del=true);
+
+ /**
+ * A list of all available effects, by name
+ * each of these can be given to the first
+ * argument of the Effect constructor
+ **/
+ QStrList available() const;
+
+ /**
+ * A list of all available effects objects
+ **/
+ QPtrList<Effect> effects() const;
+
+ /**
+ * Get the Effect that has the following id
+ **/
+ Effect *findId(long id) const;
+
+private:
+ QPtrListIterator<Effect> stackPosition() const;
+
+signals:
+ /**
+ * Emitted when an effect has been added to the effect stack
+ * @param effect the effect that got added
+ **/
+ void added(Effect *effect);
+ /**
+ * Emitted when an effect has been removed to the effect stack
+ * @param effect the effect that got removed
+ **/
+ void removed(Effect *effect);
+ /**
+ * Emitted when an effect has been moved
+ * @param effect the effect that got moved
+ **/
+ void moved(Effect *effect);
+ /**
+ * Emitted when an effect is about to be
+ * deleted (from memory)
+ * @param effect the effect to be deleted
+ **/
+ void deleting(Effect *effect);
+
+private:
+ // stored in no specific order
+ QPtrList<Effect> mItems;
+};
+
+
+
+#endif
+
diff --git a/noatun/library/noatun/engine.h b/noatun/library/noatun/engine.h
new file mode 100644
index 00000000..2244fa1e
--- /dev/null
+++ b/noatun/library/noatun/engine.h
@@ -0,0 +1,120 @@
+#ifndef _ENGINE_H
+#define _ENGINE_H
+
+#include <qobject.h>
+#include <kurl.h>
+#include <arts/kmedia2.h>
+#include <noatun/playlist.h>
+#include <kdemacros.h>
+class Visualization;
+
+namespace Arts
+{
+ class SoundServerV2;
+
+ class Synth_AMAN_PLAY;
+}
+
+namespace Noatun
+{
+ class StereoEffectStack;
+ class StereoVolumeControl;
+ class Equalizer;
+ class Session;
+}
+
+class NoatunApp;
+
+/**
+ * Handles all playing, connecting to aRts.
+ * Does almost everything related to multimedia.
+ * Most interfacing should be done with Player
+ **/
+class KDE_EXPORT Engine : public QObject
+{
+Q_OBJECT
+friend class NoatunApp;
+public:
+ Engine(QObject *parent=0);
+ ~Engine();
+ void setInitialized();
+ bool initialized() const;
+
+public slots:
+ /**
+ * opens the file, use play() to start playing
+ **/
+ bool open(const PlaylistItem &file);
+ /**
+ * Continues playing
+ **/
+ bool play();
+ /**
+ * Terminates playing, does not close the file
+ **/
+ void pause();
+ /**
+ * resets the engine
+ **/
+ void stop();
+ /**
+ * skips to a timecode
+ * unit is milliseconds
+ **/
+ void seek(int msec);
+
+ void setVolume(int percent);
+
+ void connectPlayObject();
+signals:
+ void done();
+ /**
+ * emitted when arts dies and noatun has to start
+ * it again. This is called when the new arts
+ * is already initialized
+ **/
+ void artsError();
+
+ void aboutToPlay();
+
+ void receivedStreamMeta(
+ const QString &streamName, const QString &streamGenre,
+ const QString &streamUrl, const QString &streamBitrate,
+ const QString &trackTitle, const QString &trackUrl
+ );
+
+ void playingFailed();
+
+ private slots:
+ void slotProxyError();
+ void deleteProxy();
+
+public:
+ int state();
+ int position(); // return position in milliseconds
+ int length(); // return track-length in milliseconds
+ int volume() const;
+
+private:
+ int openMixerFD();
+ void closeMixerFD(int);
+ void useHardwareMixer(bool);
+ bool initArts();
+
+public:
+ Arts::SoundServerV2 *server() const;
+ Arts::PlayObject playObject() const;
+ Arts::SoundServerV2 *simpleSoundServer() const;
+ Noatun::StereoEffectStack *effectStack() const;
+ Noatun::Equalizer *equalizer() const;
+ Noatun::StereoEffectStack *visualizationStack() const;
+ Noatun::StereoEffectStack *globalEffectStack() const;
+ Noatun::Session *session() const;
+
+private:
+ class EnginePrivate;
+ EnginePrivate *d;
+ bool mPlay;
+};
+
+#endif
diff --git a/noatun/library/noatun/equalizer.h b/noatun/library/noatun/equalizer.h
new file mode 100644
index 00000000..10de88c5
--- /dev/null
+++ b/noatun/library/noatun/equalizer.h
@@ -0,0 +1,238 @@
+#ifndef NOATUN_EQUALIZER_H
+#define NOATUN_EQUALIZER_H
+
+#include <qptrlist.h>
+#include <qobject.h>
+#include <kurl.h>
+#include <noatun/vequalizer.h>
+
+class Engine;
+class Equalizer;
+
+/**
+ * a preset stores the state of the equalizer
+ *
+ * @short EQ Preset
+ * @author Charles Samuels
+ * @version 2.3
+ **/
+class Preset
+{
+friend class Equalizer;
+
+ Preset(const QString &file);
+ Preset();
+ Preset(VPreset p);
+
+public:
+ QString name() const;
+ bool setName(const QString &name);
+ bool save() const;
+ bool load();
+
+ void remove();
+
+ QString file() const;
+
+ VPreset &vpreset() const;
+private:
+ QString mFile;
+};
+
+/**
+ * This represents a single band in Noatuns %Equalizer
+ *
+ * @short EQ Band
+ * @author Charles Samuels
+ * @version 2.3
+ **/
+class Band
+{
+friend class Equalizer;
+friend class QPtrList<Band>;
+
+private:
+ Band();
+ Band(int start, int end);
+ Band(int band);
+ virtual ~Band();
+public:
+ /**
+ * the intensity of the change.
+ * it's logarithmic. 0 is no change
+ * negative numbers are loss in intensity
+ * positive numbers are a gain
+ * And +-100 is the most you'd need to go
+ **/
+ int level();
+ void setLevel(int l);
+
+ int start() const;
+ int end() const;
+
+ /**
+ * the middle between start and end
+ **/
+ int center() const;
+
+ QString formatStart(bool withHz=true) const;
+ QString formatEnd(bool withHz=true) const;
+ /**
+ * return the format for center()
+ **/
+ QString format(bool withHz=true) const;
+
+private:
+ int mOffset;
+ int mNum, mEnd;
+};
+
+
+/**
+ * @deprecated
+ *
+ * this API is deprecated!!! Do not use it!
+ * it acts as a wrapper around the new VEqualizer API
+ * This only exists to keep compatibility in both
+ * source and binary forms. It will go away in the future.
+ * @short old Equalizer
+ * @author Charles Samuels
+ * @version 2.3
+ **/
+class Equalizer : public QObject
+{
+friend class Band;
+friend class Preset;
+friend class Engine;
+
+Q_OBJECT
+public:
+ Equalizer();
+ ~Equalizer();
+
+ const QPtrList<Band> &bands() const;
+ Band *band(int num) const;
+ int bandCount() const;
+
+ int preamp() const;
+ bool isEnabled() const;
+
+ void init();
+
+public slots:
+ /**
+ * set the preamplification
+ * it's logarithmic. 0 is no change
+ * negative numbers are loss in intensity
+ * positive numbers are a gain
+ * And +-100 is the most you'd need to go
+ **/
+ void setPreamp(int p);
+ void enable();
+ void disable();
+ void setEnabled(bool e);
+
+
+public:
+// saving eq stuff
+ /**
+ * save the current levels
+ * all noatun equalizer files have the "*.noatunequalizer"
+ * pattern. Nevertheless, the file can be identified
+ * by magic, so it's not required
+ **/
+ bool save(const KURL &file, const QString &friendly) const;
+
+ /**
+ * restore the EQ settings from this file
+ **/
+ bool load(const KURL &file);
+
+ /**
+ * convert the current EQ settings to string form,
+ * suitable for storage, the given string is the title
+ *
+ * @see fromString
+ **/
+ QString toString(const QString &name="stored") const;
+
+ /**
+ * undo a toString, restoring the EQ
+ * to the settings in the given string,
+ * emitting the changed signals
+ **/
+ bool fromString(const QString &str);
+
+ /**
+ * create a preset with such a name
+ * and remember that it'l return zero
+ * if the name already exists
+ *
+ * If smart is true, append a number to the end
+ * of the name, if one already exists by the given
+ **/
+ Preset *createPreset(const QString &name, bool smart=true);
+
+ /**
+ * return all the presets
+ * remember to setAutoDelete on this
+ **/
+ QPtrList<Preset> presets() const;
+
+ Preset *preset(const QString &file);
+ bool presetExists(const QString &name) const;
+
+signals:
+ void changed(Band *band);
+ void changed();
+ void enabled();
+ void disabled();
+ void enabled(bool e);
+
+ void preampChanged(int p);
+ void preampChanged();
+
+ /**
+ * the preset with the given name
+ * was selected
+ **/
+ void changed(Preset *);
+
+ /**
+ * when a new preset has been created
+ **/
+ void created(Preset*);
+
+ /**
+ * when @p preset has been renamed to @p newname
+ **/
+ void renamed(Preset *);
+
+ /**
+ * the given preset has been removed
+ **/
+ void removed(Preset *);
+
+private slots:
+ void created(VPreset preset);
+ void selected(VPreset preset);
+ void renamed(VPreset preset);
+ void removed(VPreset preset);
+
+private:
+ void add(Band*);
+ void remove(Band*);
+ // apply the data to artsd
+ void enableUpdates(bool on=true);
+ void update(bool full=false);
+
+private:
+ QPtrList<Band> mBands;
+ bool mUpdates;
+ int mPreamp;
+};
+
+
+
+#endif
+
diff --git a/noatun/library/noatun/mocs.cpp b/noatun/library/noatun/mocs.cpp
new file mode 100644
index 00000000..3d503bcc
--- /dev/null
+++ b/noatun/library/noatun/mocs.cpp
@@ -0,0 +1,37 @@
+#ifdef STUPID_KDE_AUTOMAKE
+
+#include "effects.h"
+#include "effects.moc"
+#include "playlistsaver.h"
+#include "playlistsaver.moc"
+#include "playlist.h"
+#include "playlist.moc"
+#include "plugin.h"
+#include "plugin.moc"
+#include "video.h"
+#include "video.moc"
+#include "controls.h"
+#include "controls.moc"
+#include "engine.h"
+#include "engine.moc"
+#include "pref.h"
+#include "pref.moc"
+#include "equalizer.h"
+#include "equalizer.moc"
+#include "stdaction.h"
+#include "stdaction.moc"
+#include "scrollinglabel.h"
+#include "scrollinglabel.moc"
+#include "downloader.h"
+#include "downloader.moc"
+#include "app.h"
+#include "app.moc"
+#include "player.h"
+#include "player.moc"
+#include "stereobuttonaction.h"
+#include "stereobuttonaction.moc"
+#include "vequalizer.h"
+#include "vequalizer.moc"
+
+#endif
+
diff --git a/noatun/library/noatun/player.h b/noatun/library/noatun/player.h
new file mode 100644
index 00000000..bccda62b
--- /dev/null
+++ b/noatun/library/noatun/player.h
@@ -0,0 +1,270 @@
+#ifndef PLAYER_H
+#define PLAYER_H
+
+#include <qobject.h>
+#include <qtimer.h>
+#include <kurl.h>
+#include <noatun/playlist.h>
+#include <kdemacros.h>
+class Engine;
+class Playlist;
+class KLibrary;
+
+/**
+ * This class has slots for all the common media player buttons
+ * The slots are called, and it queries the Playlist for the appropriate
+ * file.
+ *
+ * @short Noatun player backend
+ * @author Charles Samuels
+ * @version 2.4
+ **/
+class KDE_EXPORT Player : public QObject
+{
+Q_OBJECT
+friend class Effects;
+friend class PlaylistItemData;
+friend class PlaylistNotifier;
+
+public:
+ /**
+ * "None": Plays the playlist entries sequentially until the
+ * end of the playlist.
+ * "Song": Repeats the current playlist entry indefinitely.
+ * "Playlist": Plays the playlist entries sequentially, until
+ * end of the playlist. Noatun will then restart
+ * playback at the first song.
+ * "Random": Plays the entries of the playlist in a random,
+ * non-repeating order. Playback will continue
+ * indefinitely.
+ **/
+ enum LoopType { None=0, Song, Playlist, Random };
+
+public:
+ Player(QObject *parent=0);
+ ~Player();
+
+ /**
+ * @return a string with the time that can be used in the UI:
+ * CC:CC/LL:LL (mm:ss)
+ **/
+ QString lengthString(int _position=-1);
+ /**
+ * @return LoopType enum
+ **/
+ int loopStyle() const { return mLoopStyle; }
+ /**
+ * @return the volume from 0-100
+ * use volume() instead
+ **/
+ int volume() const;
+ /**
+ * @return the position in milliseconds
+ **/
+ int getTime() const { return position; }
+ /**
+ * @return the track-length in milliseconds
+ **/
+ int getLength();
+ /**
+ * @return true if we're playing
+ **/
+ bool isPlaying();
+ /**
+ * @return true if paused
+ **/
+ bool isPaused();
+ /**
+ * @return true if stopped
+ **/
+ bool isStopped();
+
+ /**
+ * get the current playlist
+ * this may be null
+ * And this may not be necessarily an item allocated
+ * by playlist()
+ **/
+ PlaylistItem current() const { return mCurrent;} // TODO: uninline
+
+ /**
+ * loads a file and optionally plays it
+ * @param file the file to load
+ * @param purge true to clear the playlist on open
+ * @param autoplay start playing that file after loading it
+ **/
+ void openFile(const KURL &file, bool purge=true, bool autoplay=false);
+
+ /**
+ * loads all given files
+ * @param files list of files to load
+ * @param purge true to clear the playlist on open
+ * @param autoplay if true, play the first added item
+ **/
+ void openFile(const KURL::List &files, bool purge=true, bool autoplay=false);
+
+ Engine *engine() const { return mEngine; }
+
+public slots:
+ /**
+ * show or hide the playlist
+ **/
+ void toggleListView();
+ /**
+ * force the playing/paused/stopped/playlist shown signals to
+ * be sent out, also, you can send this if you want to
+ * make all the UIs re-display the current item
+ **/
+ void handleButtons();
+ /**
+ * remove current from playlist
+ **/
+ void removeCurrent();
+
+ /**
+ * go back a track
+ **/
+ void back();
+ /**
+ * stop playing
+ **/
+ void stop();
+ /**
+ * start playing
+ **/
+ void play();
+
+ /**
+ * play the given file
+ **/
+ void play(const PlaylistItem &item);
+ /**
+ * start playing, or pause if we're currently playing
+ **/
+ void playpause();
+ /**
+ * go forward a track
+ **/
+ void forward(bool allowLoop = true);
+
+ /**
+ * skip to the position, unit is milliseconds
+ **/
+ void skipTo(int msec);
+
+ /**
+ * goes to the next type of looping
+ **/
+ void loop();
+
+ /**
+ * set the type of looping
+ **/
+ void loop(int i);
+
+ void setVolume(int);
+
+public slots:
+ /**
+ * @internal
+ * Play the current file
+ **/
+ void playCurrent();
+ /**
+ * @internal
+ * load the current file
+ **/
+ void newCurrent();
+
+private slots:
+ void posTimeout();
+ void aboutToPlay();
+ void slotUpdateStreamMeta(
+ const QString &streamName, const QString &streamGenre,
+ const QString &streamUrl, const QString &streamBitrate,
+ const QString &trackTitle, const QString &trackUrl
+ );
+
+signals:
+ /**
+ * Tells you to update the seekbar, volume
+ * and title.
+ **/
+ void timeout();
+
+ void stopped();
+
+ void playing();
+
+ void paused();
+
+ /**
+ * when the type of looping is
+ * changed
+ **/
+ void loopTypeChange(int t);
+
+ /**
+ * the playlist is made visible
+ **/
+ void playlistShown();
+
+ /**
+ * the playlist is hidden
+ **/
+ void playlistHidden();
+
+ /**
+ * called at the same time as newSong, but
+ * maybe easier to work with
+ **/
+ void newSongLen(int mins, int sec);
+
+ /**
+ * when a new song is currently playing
+ **/
+ void newSong();
+
+ /**
+ * Called when a new song is about to be played, but
+ * hasn't started. player->current() is the
+ * next song
+ **/
+ void changed();
+
+ /**
+ * called when we're about to load item, but it hasn't been yet
+ *
+ * this is used for implementing new protocols
+ **/
+ void aboutToOpen(PlaylistItem item);
+
+ void volumeChanged(int);
+
+ /**
+ * this signal is emitted when the user (or a plugin) seeks
+ * the song with @sa skipTo
+ **/
+ void skipped();
+
+ /**
+ * this signal is emitted when the user (or a plugin) seeks
+ * the song with @sa skipTo
+ *
+ * @param msec is the position in the song in milliseconds
+ **/
+ void skipped(int msec);
+
+private:
+ Engine *mEngine;
+ QTimer filePos;
+ int position;
+ int mLoopStyle;
+ bool firstTimeout;
+ PlaylistItem mCurrent; // TODO eliminate
+ QPtrList<PlaylistNotifier> mNotifiers;
+};
+
+
+#endif
+
diff --git a/noatun/library/noatun/playlist.h b/noatun/library/noatun/playlist.h
new file mode 100644
index 00000000..37eabb0b
--- /dev/null
+++ b/noatun/library/noatun/playlist.h
@@ -0,0 +1,531 @@
+#ifndef NOATUNPLAYLIST_H
+#define NOATUNPLAYLIST_H
+
+#include <qobject.h>
+#include <kurl.h>
+#include <qdict.h>
+#include <qstringlist.h>
+#include <cassert>
+#include <kdemacros.h>
+
+class PlaylistItem;
+
+/**
+ * If you're not coding a playlist, ignore this class.
+ *
+ * The backend. Since PlaylistItemData is refcounted,
+ * this contains the data, the PlaylistItem is the "reference"
+ * <pre>
+ * PlaylistItem m=new PlaylistItemData;
+ * </pre>
+ * Of course, you're supposed to inherit from PlaylistItemData
+ * in your Playlist, since there are pure virtuals.
+ *
+ * You can create these objects on demand.
+ *
+ * @short Playlist item data
+ * @author Charles Samuels
+ * @version 2.3
+ **/
+class KDE_EXPORT PlaylistItemData
+{
+public:
+ PlaylistItemData();
+ virtual ~PlaylistItemData();
+
+ /**
+ * Noatun asks your playlist for properties. It is your
+ * responsiblity to store the information. But usually a QMap<QString,QString>
+ * is enough.
+ *
+ * If you return the default value, the default should not
+ * be written.
+ *
+ * This returns the property, or def if such a property doesn't exist
+ **/
+ virtual QString property(const QString &key, const QString &def=0) const=0;
+
+ /**
+ * This sets the property with the given key and value.
+ *
+ * Important: If you use a QMap, you'll have to remove the current
+ * item before adding a new one
+ **/
+ virtual void setProperty(const QString &key, const QString &property)=0;
+
+ /**
+ * remove the item with given key
+ **/
+ virtual void clearProperty(const QString &key)=0;
+
+ /**
+ * return a list of property keys
+ **/
+ virtual QStringList properties() const=0;
+
+ /**
+ * return whether if the given key exists
+ **/
+ virtual bool isProperty(const QString &key) const=0;
+
+ /**
+ * return the title of the song. By default, this will
+ * use the following by default, in order of priority
+ *
+ * property("realtitle")
+ * property("title")
+ * url().filename()
+ *
+ * you shouldn't need to override this.
+ **/
+ virtual QString title() const;
+
+ /**
+ * the true filename of the song, remote or local
+ **/
+ virtual KURL url() const { return KURL(property("url")); }
+ /**
+ * set the true filename of the song, remote or local
+ **/
+ virtual void setUrl(const KURL &url) { setProperty("url", url.url()); }
+
+ /**
+ * first, this checks for the property "mimetype", else
+ * it'l ask KMimeType based on file()
+ **/
+ virtual QCString mimetype() const;
+
+ /**
+ * first, checks for the property "playObject", else,
+ * it'l ask aRts
+ **/
+ virtual QCString playObject() const;
+
+ /**
+ * return the filename to send to the playobject
+ **/
+ virtual QString file() const { return url().path(); }
+
+ /**
+ * what's the length of the song, in milliseconds?
+ **/
+ virtual int length() const;
+
+ /**
+ * sets the length of the song, in milliseconds
+ **/
+ virtual void setLength(int ms);
+
+ /**
+ * returns a friendly representation of the length
+ * of this file
+ **/
+ QString lengthString() const;
+
+ /**
+ * compare yourself with the given PlaylistItemData
+ * This is implemented in the slow fashion of
+ * comparing all the properties. You may
+ * have a much faster way of implementing this
+ * if this==&d, this will not be called, normally
+ **/
+ virtual bool operator == (const PlaylistItemData &d) const;
+
+ /**
+ * this is implemented as !(*this==d), you may have a
+ * faster way to implement this
+ **/
+ virtual bool operator != (const PlaylistItemData &d) const;
+
+ /**
+ * remove this item from the list
+ **/
+ virtual void remove() = 0;
+
+
+ /**
+ * Playlists should not download files if this is true
+ **/
+ bool streamable() const { return isProperty("stream_"); }
+
+public:
+ /**
+ * Call this when you want to signal
+ * the given item has been added to the list
+ **/
+ void added();
+
+ /**
+ * Your playlist must call this when the file
+ * is removed from the playlist
+ **/
+ void removed();
+
+ /**
+ * Your playlist must call this when the file
+ * is modified
+ **/
+ void modified();
+
+
+public: // reference counting
+ /**
+ * Have the reference counter never delete this
+ *
+ * This is useful for when you want to keep all
+ * your items hanging around
+ **/
+ void addRef() { mRefs++; }
+ void removeRef()
+ {
+ mRefs--;
+ if (!mRefs)
+ delete this;
+ }
+
+private:
+ mutable int mRefs;
+};
+
+
+/**
+ * a reference to a PlaylistItem(Data)
+ *
+ * All methods here should have the same behavior
+ * as they do for PlaylistItemData
+ *
+ * If you're a playlist, you should inherit
+ * from PlaylistItemData
+ *
+ * It's client code's responsibility to ensure that
+ * PlaylistItem is not null by using either the boolean
+ * conversion or isNull()
+ *
+ * @short Playlist items
+ * @author Charles Samuels
+ * @version 2.3
+ **/
+class KDE_EXPORT PlaylistItem
+{
+public:
+ PlaylistItem(const PlaylistItem &source);
+ PlaylistItem(PlaylistItemData *source);
+ PlaylistItem() : mData(0) {}
+ ~PlaylistItem();
+
+ PlaylistItem &operator =(const PlaylistItem &source);
+ PlaylistItem &operator =(PlaylistItemData *source);
+
+ PlaylistItemData *data() { return mData; }
+ const PlaylistItemData *data() const { return mData; }
+
+ const PlaylistItem &operator =(const PlaylistItem &source) const;
+ const PlaylistItem &operator =(const PlaylistItemData *source) const;
+
+ operator bool() const { return (bool)mData; }
+ bool isNull() const { return !(bool)mData; }
+
+ bool operator ==(const PlaylistItem &i) const
+ {
+ if (data()==i.data()) return true;
+ if (!data() || !i.data()) return false;
+ return *i.data()==*data();
+ }
+ bool operator ==(const PlaylistItemData *i) const
+ {
+ if (data()==i) return true;
+ if (!data() || !i) return false;
+ return *i==*data();
+ }
+
+ bool operator !=(const PlaylistItem &i) const
+ { return ! (*this==i); }
+ bool operator !=(const PlaylistItemData *i) const
+ { return ! (*this->data()==*i); }
+
+ QString property(const QString &key, const QString &def=0) const
+ {
+ assert(mData);
+ return mData->property(key, def);
+ }
+
+ void setProperty(const QString &key, const QString &property)
+ {
+ assert(mData);
+ const_cast<PlaylistItemData*>(mData)->setProperty(key, property);
+ }
+
+ void clearProperty(const QString &key)
+ {
+ assert(mData);
+ const_cast<PlaylistItemData*>(mData)->clearProperty(key);
+ }
+
+ QStringList properties() const
+ {
+ assert(mData);
+ return mData->properties();
+ }
+
+ bool isProperty(const QString &key) const
+ {
+ assert(mData);
+ return mData->isProperty(key);
+ }
+
+ KURL url() const { assert(mData); return mData->url(); }
+ void setUrl(const KURL &url)
+ {
+ assert(mData);
+ const_cast<PlaylistItemData*>(mData)->setUrl(url);
+ }
+
+ QCString mimetype() const { assert(mData); return mData->mimetype(); }
+ QCString playObject() const { assert(mData); return mData->playObject(); }
+ QString file() const { assert(mData); return mData->file(); }
+
+ QString title() const
+ {
+ assert(mData);
+ return mData->title();
+ }
+
+ int length() const
+ {
+ assert(mData);
+ return mData->length();
+ }
+
+ void setLength(int ms) const
+ {
+ assert(mData);
+ mData->setLength(ms);
+ }
+
+ QString lengthString() const { assert(mData); return mData->lengthString(); }
+
+ void remove() { assert(mData); mData->remove(); }
+
+ bool streamable() const { assert(mData); return mData->streamable(); }
+
+private:
+ // reference counting
+ void removeRef() const;
+ void addRef() const; // requires mData already has item
+
+private:
+ mutable PlaylistItemData *mData;
+ void *_bc1, *_bc2;
+};
+
+/**
+ * The playlist, which you derive from when creating
+ * your own playlist.
+ *
+ * Do not, under any circumstances, call a Playlist method
+ * when you can call a Player method, unless, of course, you
+ * ARE the playlist.
+ **/
+class Playlist : public QObject
+{
+Q_OBJECT
+ friend class PlaylistItemData;
+public:
+ Playlist(QObject *parent, const char *name);
+ /**
+ * on playlist unload, your playlist must
+ * have current()==0 and emit playCurrent
+ **/
+ virtual ~Playlist();
+
+ /**
+ * go to the front
+ **/
+ virtual void reset()=0;
+
+ /**
+ * empty the list
+ **/
+ virtual void clear()=0;
+
+ /**
+ * add a file
+ */
+ virtual void addFile(const KURL&, bool play=false)=0;
+
+ /**
+ * cycle forward, return that
+ **/
+ virtual PlaylistItem next()=0;
+
+ /**
+ * cycle to next section, return that
+ * defaults to return next()
+ */
+ virtual PlaylistItem nextSection();
+
+ /**
+ * cycle back, return that
+ **/
+ virtual PlaylistItem previous()=0;
+
+ /**
+ * cycle to previous section, return that
+ * defaults to return previous()
+ */
+ virtual PlaylistItem previousSection();
+
+ /**
+ * current item
+ **/
+ virtual PlaylistItem current()=0;
+ /**
+ * set the current item
+ **/
+ virtual void setCurrent(const PlaylistItem &)=0;
+
+ /**
+ * get the first item
+ **/
+ virtual PlaylistItem getFirst() const =0;
+
+ /**
+ * get the item after item, note that getFirst and getAfter do not
+ * have to follow play order since they are used solely to iterate
+ * over the entire collection in some order. Duplicating the play
+ * order (by looking into the future) is not necessary.
+ **/
+ virtual PlaylistItem getAfter(const PlaylistItem &item) const =0;
+
+ /**
+ * is the view visible?
+ **/
+
+ virtual bool listVisible() const =0;
+
+ /**
+ * do the KCmdLineArgs stuff
+ **/
+ int handleArguments();
+
+ /**
+ * return a list of songs in which at least one
+ * of the keys matches at least one of the values
+ *
+ * the default implementation will call getFirst()
+ * and getAfter() which could be potentially slow,
+ * depending how your playlist is designed. So
+ * you're free to reimplement this if you could
+ * do better
+ *
+ * A value of "" is equal to an unset value
+ *
+ * limit is the maximum amount of items to return,
+ * or -1 if you want as many as possible
+ *
+ * if exact is true, a match is only made if
+ * the string is identical to a value. if false
+ * a match is made if the string contains a value
+ *
+ * caseSensitive, if false, means that the given
+ * values are compared case insensitively to
+ * to the items in the playlist. The keys
+ * are always compared with case sensitivity
+ **/
+ virtual QValueList<PlaylistItem> select(
+ const QStringList &keys, const QStringList &values,
+ int limit=-1, bool exact=false, bool caseSensitive=false
+ );
+
+ /**
+ * The default implementation will just call
+ * the above select. Of course, you're free to implement
+ * both of these (with different mechanisms if need be)
+ * for speed
+ **/
+ virtual QValueList<PlaylistItem> select(
+ const QString &key, const QString &value,
+ int limit=-1, bool exact=false, bool caseSensitive=false
+ );
+ /**
+ * exactly the same as the above, except converts
+ * the const char* to QString (utf8)
+ **/
+ inline QValueList<PlaylistItem> select(
+ const char *key, const char *value,
+ int limit=-1, bool exact=false, bool caseSensitive=false
+ )
+ {
+ return select(
+ QString(key), QString(value),
+ limit, exact, caseSensitive
+ );
+ }
+
+public slots:
+ /**
+ * show the list!
+ **/
+ virtual void showList()=0;
+ /**
+ * hide it
+ **/
+ virtual void hideList()=0;
+ /**
+ * toggle visibility
+ **/
+ virtual void toggleList();
+
+signals:
+ /**
+ * when you want the engine to reload current()
+ * This is how your playlist forces noatun to
+ * play a new song
+ **/
+ void playCurrent();
+
+ /**
+ * when the list is hidden
+ **/
+ void listHidden();
+
+ /**
+ * when the list is shown
+ **/
+ void listShown();
+};
+
+/**
+ * this class's methods will be called whenever
+ * something happens to the playlist or its
+ * items.
+ *
+ * If the playlist plugin changes, you don't have to do
+ * anything.
+ **/
+class PlaylistNotifier
+{
+public:
+ PlaylistNotifier();
+ virtual ~PlaylistNotifier();
+
+ /**
+ * a new item is added to the list
+ **/
+ virtual void added(PlaylistItem &) {}
+
+ /**
+ * an item is removed from the list
+ **/
+ virtual void removed(PlaylistItem &) {}
+
+ /**
+ * this item was modified (via a changed
+ * or added property
+ **/
+ virtual void modified(PlaylistItem &) {}
+};
+
+
+#endif
+
diff --git a/noatun/library/noatun/playlistsaver.h b/noatun/library/noatun/playlistsaver.h
new file mode 100644
index 00000000..e83761ca
--- /dev/null
+++ b/noatun/library/noatun/playlistsaver.h
@@ -0,0 +1,102 @@
+#ifndef NOATUNPLAYLISTSAVER_H
+#define NOATUNPLAYLISTSAVER_H
+
+#include <kurl.h>
+#include <qmap.h>
+#include <noatun/playlist.h>
+
+class Structure;
+
+/**
+ * a simple one-group implementatio of
+ * the standard XML playlist format:
+ *
+ * http://noatun.kde.org/formats/xmlplaylist.phtml
+ *
+ * A custom implementation like yours should have a
+ * different "client" attribute for the playlist tag
+ **/
+class PlaylistSaver
+{
+ friend class NoatunXMLStructure;
+ friend class MSASXStructure;
+public:
+ enum Options
+ {
+ XMLPlaylist=1,
+ M3U=2,
+ PLS=4,
+ EXTM3U=8,
+ ASX=16
+ };
+
+ PlaylistSaver();
+ virtual ~PlaylistSaver();
+
+ bool save(const KURL &file, int options=0);
+ bool load(const KURL &file, int options=0);
+
+ /**
+ * guess the list's content between M3U or
+ * PLS, and give it to you
+ *
+ * this is also the way to make icecast work.
+ * you can also pass true icecast urls
+ * here and it'l do its magic.
+ *
+ * This calls readItem
+ **/
+ bool metalist(const KURL &url);
+
+ /**
+ * unused, for future expansion, do not use
+ * @internal
+ **/
+ virtual void setGroup(const QString &);
+
+protected:
+ /**
+ * read the item, and add it to your list
+ * Given is a list of properties which coincide
+ * with the standard noatun ones
+ **/
+ virtual void readItem(const QMap<QString,QString> &properties) = 0;
+
+ /**
+ * add this item to the XML file
+ * or a null item if we're at the end
+ **/
+ virtual PlaylistItem writeItem() = 0;
+
+ /**
+ * unused, for future expansion
+ * @internal
+ **/
+ virtual void changeGroup(const QString &) {}
+
+ /**
+ * this is called when you should either
+ * clear your list, or start writing from the start of the list
+ *
+ * You usually don't need to implement this, since it'l always
+ * be called immediately after load() or save()
+ **/
+ virtual void reset() {}
+
+private:
+ bool loadXML(const KURL &file, int x=0);
+ bool saveXML(const KURL &file, int x=0);
+
+ bool loadM3U(const KURL &file, int x=0);
+ bool saveM3U(const KURL &file, int x=0);
+
+ bool loadPLS(const KURL &file, int x=0);
+ bool savePLS(const KURL &file, int x=0);
+
+private:
+ class Private;
+ PlaylistSaver::Private *d; // unused
+
+};
+
+#endif
diff --git a/noatun/library/noatun/plugin.h b/noatun/library/noatun/plugin.h
new file mode 100644
index 00000000..f90dcc0d
--- /dev/null
+++ b/noatun/library/noatun/plugin.h
@@ -0,0 +1,467 @@
+#ifndef NPLUGIN_H
+#define NPLUGIN_H
+
+#include <noatun/pluginloader.h>
+#include <qmemarray.h>
+#include <vector>
+#include <kdemacros.h>
+
+namespace Noatun { class FFTScopeStereo; class FFTScope; class RawScope;
+ class RawScopeStereo; class StereoEffectStack;
+ class Listener;
+ }
+namespace Arts { class SoundServerV2; class Dispatcher; }
+
+//backwards compatibility
+
+#define NOATUNPLUGIND
+#define NOATUNPLUGINC(classname) { }
+
+class Playlist;
+class Player;
+class ExitNotifier;
+class NoatunApp;
+/**
+ * @short Base class for all plugins
+ **/
+class KDE_EXPORT Plugin
+{
+public:
+ Plugin();
+ virtual ~Plugin();
+
+ /**
+ * called directly after the plugin, just in case
+ * you want Noatun to be "ready" with your class
+ **/
+ virtual void init();
+
+ /**
+ * unload the plugin
+ * if it returns true, return from your function
+ * immediately and don't access members of this
+ * TODO
+ **/
+ virtual bool unload();
+
+ /**
+ * make multiple inheritence work
+ * only needed with playlist plugins
+ * generally "return this" in here
+ **/
+ virtual Playlist *playlist() { return 0; }
+};
+
+/**
+ * @short Base class for user-interface plugins
+ *
+ * Inherit from this one instead of Plugin if you are
+ * a user-interface
+ **/
+class KDE_EXPORT UserInterface : public Plugin
+{
+public:
+ UserInterface();
+ virtual ~UserInterface();
+};
+
+
+class TimerThingy;
+
+
+/**
+ * @short Base class for all sorts of visualizations
+ *
+ * all Noatun Visualizations can be in
+ * separate processes! You must fork, and
+ * then exec() to be able to use this.
+ * Its perfectly safe to create
+ * a visualization in a binary executed by
+ * KProcess.
+ **/
+class Visualization
+{
+friend class TimerThingy;
+friend class ThingThatTellsAVisualizationThatNoatunIsGone;
+friend class ExitNotifier;
+friend class NoatunApp;
+public:
+ /**
+ * interval is how frequently the rescope
+ * will occur
+ * 0 means disabled
+ *
+ * Pid, if not zero, can force the visualizaton
+ * to use another noatun's process's visualization stack,
+ * if it is zero, it'l try to guess what to use
+ **/
+ Visualization(int interval=125, int pid=0);
+ virtual ~Visualization();
+
+ /**
+ * start the timer, if it's 0, then this
+ * will do nothing.
+ **/
+ virtual void start();
+ /**
+ * stop the timer
+ **/
+ virtual void stop();
+
+ /**
+ * how long between each rescoping in milliseconds
+ **/
+ int interval() const;
+
+ /**
+ * set how long to wait
+ * the change takes effect immediately,
+ * unless it hadn't been start()ed beforehand
+ **/
+ virtual void setInterval(int msecs);
+
+ /**
+ * cause a rescoping to take effect immediately
+ **/
+ virtual void timeout()=0;
+
+ Noatun::StereoEffectStack visualizationStack();
+ Arts::SoundServerV2 *server();
+
+ /**
+ * return noatun's pid, useful if you're doing remote-visualization
+ *
+ * It returns the pid to use, or -1, if there was an error, or
+ * there isn't a noatun running. If there isn't a noatun running, the
+ * computer will crash, the trains will take the wrong tracks, time
+ * will start moving in the wrong direction, and the whole universe will melt
+ * down.
+ **/
+ static int noatunPid();
+
+ /**
+ * test if the server is still there
+ **/
+ bool connected();
+
+ /**
+ * create a dispatcher object , does nothing if
+ * this already has been called
+ **/
+ static void initDispatcher();
+
+private:
+ int mTimeout;
+ TimerThingy *mTimerThingy;
+ QCString mVisualizationStack;
+ Arts::SoundServerV2 *mServer;
+ static Arts::Dispatcher *mDispatcher;
+ static bool internalVis;
+};
+
+/**
+ * Base class for all kinds of FFT scopes
+ **/
+class KDE_EXPORT FFTScope : public Visualization
+{
+public:
+ FFTScope(int interval, int pid=0);
+
+ /**
+ * the "width" of each scopeEvent
+ **/
+ virtual int bands() const=0;
+
+ /**
+ * returns the magic number that you can
+ * give to "setBands". For example:
+ * <pre>
+ * setBands(magic(50));
+ * bands()==50
+ * </pre>
+ **/
+ static float magic(int bands);
+
+ /**
+ * set the width combiner value. in theory,
+ * a value of 1.0 should give you exactly
+ * 1024 samples, greater values will decrease
+ * the amount quite rapidly. by default,
+ * 1.1 is used, which gives 50. You'll have
+ * to tweak this manually.
+ *
+ * This will only return valid responses
+ * for values between 10 and 1024
+ *
+ * This function is a terrible hack, and we apologize
+ * for it. The values of these magic numbers
+ * do occasionally change, so you must use
+ * @ref magic
+ **/
+ virtual void setBands(float n)=0;
+};
+
+/**
+ * An easy to use FFT scope, stereo version.
+ * You certainly want to reimplement scopeEvent()
+ **/
+class KDE_EXPORT StereoFFTScope : public FFTScope
+{
+public:
+ StereoFFTScope(int timeout=250, int pid=0);
+ virtual ~StereoFFTScope();
+
+ /**
+ * called according to the timeout
+ * the two floats contain the data, with @p len items
+ * you override this yourself
+ **/
+ virtual void scopeEvent(float *left, float *right, int len)
+ { (void)left; (void)right; (void)len; }
+
+ /**
+ * get the current data
+ * pass two vector<float>*, this will do the rest.
+ * do not allocate it beforehand.
+ * you must then delete the vectors
+ * @returns the amount of elements for both left and right
+ **/
+ void scopeData(std::vector<float> *&left, std::vector<float> *&right);
+ virtual void timeout();
+
+ virtual int bands() const;
+ virtual void setBands(float f);
+
+private:
+ Noatun::FFTScopeStereo *mScope;
+ long mId;
+};
+
+
+/**
+ * An easy to use FFT scope, mono version.
+ * You certainly want to reimplement scopeEvent()
+ **/
+class KDE_EXPORT MonoFFTScope : public FFTScope
+{
+public:
+ MonoFFTScope(int timeout=250, int pid=0);
+ virtual ~MonoFFTScope();
+
+ /**
+ * called according to the timeout
+ * the float contains the data, with @p len items
+ * you override this yourself
+ **/
+ virtual void scopeEvent(float *data, int len)
+ { (void)data; (void)len; }
+
+ /**
+ * get the current data
+ * pass a vector<float>*, this will do the rest.
+ * do not allocate it beforehand.
+ * you must then delete the vectors
+ * @returns the amount of elements for both left and right
+ **/
+ void scopeData(std::vector<float> *&data);
+
+ /**
+ * reimplemented from class Visualization, you
+ * should never need to reimplement this yourself
+ **/
+ virtual void timeout();
+
+ virtual int bands() const;
+ virtual void setBands(float f);
+
+private:
+ Noatun::FFTScope *mScope;
+ long mId;
+};
+
+
+/**
+ * Base class for all kinds of scope visualizations, i.e. if you want to display
+ * the currently played waveform
+ **/
+class Scope : public Visualization
+{
+public:
+ Scope(int interval, int pid=0);
+
+ /**
+ * the "width" of each scopeEvent
+ **/
+ virtual int samples() const=0;
+
+ virtual void setSamples(int )=0;
+};
+
+/**
+ * An easy to use scope visualization, mono version.
+ * Note: Of course this one also works for audio with more than one channel
+ * You certainly want to reimplement scopeEvent()
+ */
+class KDE_EXPORT MonoScope : public Scope
+{
+public:
+ MonoScope(int timeout=250, int pid=0);
+ virtual ~MonoScope();
+
+ /**
+ * called according to the timeout
+ * the float contains the data, with @p len items
+ * you override this yourself, and process the data
+ **/
+ virtual void scopeEvent(float *data, int len)
+ {(void)data; (void)len; }
+
+ /**
+ * get the current data
+ * pass a vector<float>*, this will do the rest.
+ * do not allocate it beforehand.
+ * you must then delete the vectors
+ * @returns the amount of elements for both left and right
+ **/
+ void scopeData(std::vector<float> *&data);
+
+ /**
+ * reimplemented from class Visualization, you
+ * should never need to reimplement this yourself
+ **/
+ virtual void timeout();
+
+ virtual int samples() const;
+ virtual void setSamples(int);
+
+private:
+ Noatun::RawScope *mScope;
+ long mId;
+};
+
+/**
+ * An easy to use scope visualization, stereo version.
+ * You certainly want to reimplement scopeEvent()
+ **/
+class StereoScope : public Scope
+{
+public:
+ StereoScope(int timeout=250, int pid=0);
+ virtual ~StereoScope();
+
+ /**
+ * called according to the timeout
+ * the float contains the data, with @p len items
+ * you override this yourself, and process the data
+ **/
+ virtual void scopeEvent(float *left, float *right, int len)
+ { (void)left; (void)right; (void)len; }
+
+ /**
+ * get the current data
+ * pass a vector<float>*, this will do the rest.
+ * do not allocate it beforehand.
+ * you must then delete the vectors
+ * @returns the amount of elements for both left and right
+ **/
+ void scopeData(std::vector<float> *&left, std::vector<float> *&right);
+
+ /**
+ * reimplemented from class Visualization, you
+ * should never need to reimplement this yourself
+ **/
+ virtual void timeout();
+
+ virtual int samples() const;
+ virtual void setSamples(int);
+
+
+private:
+ Noatun::RawScopeStereo *mScope;
+ long mId;
+};
+
+/**
+ * @short Base class for session manager plugins
+ *
+ * Inherit from this one instead of Plugin if you are
+ * a session manager plugin
+ **/
+class SessionManagement : public Plugin
+{
+public:
+ SessionManagement();
+ virtual ~SessionManagement();
+
+ virtual void restore();
+};
+
+class NoatunListenerNotif;
+/**
+ * @short Abstract base class to monitor noatun
+ *
+ * So far only used for ExitNotifier.
+ **/
+class NoatunListener : public QObject
+{
+Q_OBJECT
+friend class NoatunListenerNotif;
+
+public:
+ NoatunListener(QObject *parent=0);
+ virtual ~NoatunListener();
+
+signals:
+ void event();
+
+protected:
+ virtual void message();
+
+ NoatunListenerNotif *mNotif;
+};
+
+/**
+ * @short Notifies you as soon as Noatun exits
+ *
+ * Link to libnoatun, and the signal event() will
+ * be emitted whenever noatun exits, and the best
+ * part is how it doesn't matter if noatun exits
+ * cleanly so you even get informed in case noatun
+ * crashes.
+ **/
+class ExitNotifier : public NoatunListener
+{
+public:
+ ExitNotifier(int pid, QObject *parent=0);
+ virtual ~ExitNotifier();
+
+private:
+ QCString appid;
+};
+
+/**
+ * @short Wrapper to set a bool variable as soon as Noatun exits
+ * This class can even be used when you cannot use signals/slots
+ * Example:
+ * <pre>
+ * bool noatunOk = false;
+ * new BoolNotifier(&noatunOk, new ExitNotifier(this), this);
+ * </pre>
+ *
+ * When noatunOk is false, then noatun has exited somehow.
+ **/
+class BoolNotifier : public QObject
+{
+Q_OBJECT
+public:
+ BoolNotifier(bool *value, NoatunListener *listener, QObject *parent=0);
+
+private slots:
+ void event() {*mValue=false;}
+
+private:
+ bool *mValue;
+};
+
+#endif
+
diff --git a/noatun/library/noatun/pluginloader.h b/noatun/library/noatun/pluginloader.h
new file mode 100644
index 00000000..611fe358
--- /dev/null
+++ b/noatun/library/noatun/pluginloader.h
@@ -0,0 +1,99 @@
+#ifndef PLUGIN_LOADER_H
+#define PLUGIN_LOADER_H
+
+#include <qstring.h>
+#include <qvaluelist.h>
+#include <qstringlist.h>
+#include <noatun/app.h>
+
+#include <klibloader.h>
+#include <qdict.h>
+#include <kdemacros.h>
+
+struct NoatunLibraryInfo
+{
+ QString specfile;
+ QString filename;
+ QString author;
+ QString license;
+ QString type;
+ QString site;
+ QString email;
+ QString name;
+ QString comment;
+ QStringList require;
+};
+
+bool operator ==(const NoatunLibraryInfo &, const NoatunLibraryInfo &);
+
+class Playlist;
+class Visualization;
+class Plugin;
+
+/**
+ * Used for loading plugins at runtime
+ **/
+class KDE_EXPORT LibraryLoader
+{
+ friend class Plugin;
+ struct PluginLibrary
+ {
+ Plugin *plugin;
+ KLibrary *library;
+ };
+
+public:
+ LibraryLoader();
+ ~LibraryLoader();
+
+ QValueList<NoatunLibraryInfo> available() const;
+ QValueList<NoatunLibraryInfo> loaded() const;
+ QValueList<NoatunLibraryInfo> loadedByType(const QString &type) const;
+
+ /**
+ * loads all the enabled plugins
+ **/
+ bool loadAll(void);
+ bool loadAll(const QStringList &);
+
+ bool isLoaded(const QString &spec) const;
+ void add(const QString &spec);
+ void setModules(const QStringList &mods);
+ /**
+ * unload the plugin specified by spec
+ **/
+ bool remove(const QString &spec);
+ /**
+ * Same as the above, but does not call kapp->exit() even
+ * when the last userinterface plugin is removed. Necessary
+ * during session management (see marquis plugin).
+ * ### BIC: merge with above with terminateOnLastUI = true
+ */
+ bool remove(const QString &spec, bool terminateOnLastUI);
+ /**
+ * unload the plugin that is referenced by @par plugin
+ **/
+ bool remove(const LibraryLoader::PluginLibrary *plugin);
+ bool remove(const Plugin *plugin);
+
+ Playlist *playlist() const;
+
+ /**
+ * This is needed for the Plugin-List-View
+ * to see what plugins are required to show
+ * (when required by another noatun-plugin)
+ **/
+ NoatunLibraryInfo getInfo(const QString &spec) const;
+ QPtrList<Plugin> plugins() const;
+
+private:
+ bool loadSO(const QString &spec);
+ void removeNow(const QString &spec);
+
+private:
+ QDict<LibraryLoader::PluginLibrary> mLibHash;
+ Playlist *mPlaylist;
+};
+
+#endif
+
diff --git a/noatun/library/noatun/pref.h b/noatun/library/noatun/pref.h
new file mode 100644
index 00000000..95be5e0e
--- /dev/null
+++ b/noatun/library/noatun/pref.h
@@ -0,0 +1,91 @@
+#ifndef NOATUNPREF_H
+#define NOATUNPREF_H
+
+#include <kdialogbase.h>
+#include <qptrlist.h>
+#include <kdemacros.h>
+
+class CModule;
+
+/**
+ * Noatun configuration dialog
+ **/
+class NoatunPreferences : public KDialogBase
+{
+Q_OBJECT
+friend class CModule;
+
+public:
+ /**
+ * @internal
+ **/
+ NoatunPreferences(QWidget *);
+
+public:
+ /**
+ * Display noatun preferences dialog
+ **/
+ virtual void show();
+ /**
+ * Display noatun preferences dialog showing @p module
+ * Useful if you want to display your own plugin configuration tab
+ **/
+ virtual void show(CModule *module);
+
+protected:
+ virtual void slotOk();
+ virtual void slotApply();
+
+private:
+ void add(CModule *);
+ void remove(CModule *);
+
+private:
+ class NoatunPreferencesPrivate;
+ NoatunPreferencesPrivate *d;
+
+ QPtrList<CModule> mModules;
+};
+
+/**
+ * @short Base class for a configuration sheet that is shown in preferences dialog
+ *
+ * Create your GUI in constructor, reimplement reopen() and save() and
+ * you're all set.
+ **/
+class KDE_EXPORT CModule : public QWidget
+{
+Q_OBJECT
+
+public:
+ /**
+ * arguments are short and long descriptions
+ * for this module, respectively
+ *
+ * parent is the object that is this modules virtual-parent.
+ * When that is deleted, this also will go away, automagically.
+ **/
+ CModule(const QString &name, const QString &description, const QString &icon, QObject *parent=0);
+
+ virtual ~CModule();
+
+public slots:
+ /**
+ * save all your options, and apply them
+ **/
+ virtual void save() {}
+ /**
+ * reload all options (e.g., read config files)
+ **/
+ virtual void reopen() {}
+
+private slots:
+ void ownerDeleted();
+
+private:
+ class CModulePrivate;
+ CModulePrivate *d;
+};
+
+
+#endif // NOATUNPREF_H
diff --git a/noatun/library/noatun/scrollinglabel.h b/noatun/library/noatun/scrollinglabel.h
new file mode 100644
index 00000000..1615a64f
--- /dev/null
+++ b/noatun/library/noatun/scrollinglabel.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2000 Rik Hemsley (rikkus) <rik@kde.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef SCROLLING_LABEL_H
+#define SCROLLING_LABEL_H
+
+#include <qwidget.h>
+/**
+ * A clever label that scrolls its contents as soon as there is not enough
+ * space to show everything at once.
+ **/
+class ScrollingLabel : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ ScrollingLabel(const QString &initialText,QWidget *parent,
+ const char * name = 0);
+ virtual ~ScrollingLabel();
+
+ /**
+ * Sets the label's text
+ * set @p time (ms) if you want to display a temporary message
+ **/
+ virtual void setText(const QString &text, int time = -1);
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ /**
+ * @return the label's text.
+ **/
+ virtual QString text() const;
+
+ /**
+ * Used to enable/disable scrolling manually
+ */
+ virtual void setScroll(bool on);
+
+ protected:
+
+ virtual void paintEvent(QPaintEvent *);
+ virtual void resizeEvent(QResizeEvent *);
+
+ protected slots:
+
+ virtual void scroll();
+ virtual void restoreText();
+
+ private:
+
+ void _update();
+
+ class Private;
+ Private * d;
+};
+
+#endif
+// vim:ts=2:sw=2:tw=78:noet
diff --git a/noatun/library/noatun/stdaction.h b/noatun/library/noatun/stdaction.h
new file mode 100644
index 00000000..7cac67fe
--- /dev/null
+++ b/noatun/library/noatun/stdaction.h
@@ -0,0 +1,204 @@
+#ifndef _NOATUNSTDACTION_H_
+#define _NOATUNSTDACTION_H_
+
+#include <kaction.h>
+#include <kactionclasses.h>
+#include <kdemacros.h>
+class KPopupMenu;
+
+/**
+ * Holds all noatun related actions
+ * @short noatun specific actions
+ * @author Charles Samuels
+ **/
+namespace NoatunStdAction
+{
+
+/**
+ * An action starting noatun playback
+ **/
+class PlayAction : public KAction
+{
+Q_OBJECT
+public:
+ PlayAction(QObject *parent, const char *name);
+private slots:
+ void playing();
+ void notplaying();
+};
+
+/**
+ * An action starting/stopping noatun playback
+ **/
+class PlaylistAction : public KToggleAction
+{
+Q_OBJECT
+public:
+ PlaylistAction(QObject *parent, const char *name);
+private slots:
+ void shown();
+ void hidden();
+};
+
+/**
+ * actionmenu that holds all plugin defined actions
+ * @author Stefan Gehn
+ */
+class PluginActionMenu : public KActionMenu
+{
+Q_OBJECT
+public:
+ PluginActionMenu(QObject *parent, const char *name);
+ /**
+ * inserts the given @p action into the action-menu
+ * @param action the action to insert
+ * @param index defines the place where the action gets displayed in
+ */
+ virtual void insert (KAction *action, int index=-1);
+ /**
+ * removes the given @p action into the action-menu
+ */
+ virtual void remove(KAction *action);
+ /**
+ * Wrapper method for old Noatun API
+ * <b>DON'T USE</b>
+ **/
+ int menuAdd(const QString &text, const QObject *receiver, const char *member);
+ /**
+ * Wrapper method for old Noatun API
+ * <b>DON'T USE</b>
+ **/
+ void menuRemove(int id);
+private:
+ int mCount;
+};
+
+/**
+ * actionmenu that holds all vis-plugins for easier enabling/disabling
+ * @author Stefan Gehn
+ */
+class VisActionMenu : public KActionMenu
+{
+Q_OBJECT
+public:
+ VisActionMenu(QObject *parent, const char *name);
+private slots:
+ void fillPopup();
+ void toggleVisPlugin(int);
+private:
+ QMap<int, QString>mSpecMap;
+};
+
+
+/**
+ * actionmenu that holds all looping modes
+ * @author Stefan Gehn
+ */
+class LoopActionMenu : public KActionMenu
+{
+Q_OBJECT
+public:
+ LoopActionMenu(QObject *parent, const char *name);
+private slots:
+ void updateLooping(int);
+ void loopNoneSelected();
+ void loopSongSelected();
+ void loopPlaylistSelected();
+ void loopRandomSelected();
+private:
+ KRadioAction *mLoopNone;
+ KRadioAction *mLoopSong;
+ KRadioAction *mLoopPlaylist;
+ KRadioAction *mLoopRandom;
+};
+
+
+/**
+ * @return pointer to a KAction which opens the effects dialog on activation
+ */
+KDE_EXPORT KAction *effects(QObject *parent = 0, const char *name = 0);
+
+/**
+ * @return pointer to a KAction which opens the equalizer dialog on activation
+ */
+KDE_EXPORT KAction *equalizer(QObject *parent = 0, const char *name = 0);
+
+/**
+ * @return pointer to a KAction which goes back one track on activation
+ */
+KDE_EXPORT KAction *back(QObject *parent = 0, const char *name = 0);
+/**
+ * @return pointer to a KAction which stops playback on activation
+ */
+KDE_EXPORT KAction *stop(QObject *parent = 0, const char *name = 0);
+/**
+ * @return pointer to a KAction which starts/pauses playback on activation
+ */
+KDE_EXPORT KAction *playpause(QObject *parent = 0, const char *name = 0);
+/**
+ * @return pointer to a KAction which advances one track on activation
+ */
+KDE_EXPORT KAction *forward(QObject *parent = 0, const char *name = 0);
+/**
+ * @return pointer to a KToggleAction which shows/hides the playlist
+ */
+KDE_EXPORT KToggleAction *playlist(QObject *parent = 0, const char *name = 0);
+
+/**
+ * loop action
+ **/
+KDE_EXPORT LoopActionMenu *loop(QObject *parent, const char *name);
+
+/**
+ * play action
+ */
+KDE_EXPORT KAction *play(QObject *parent = 0, const char *name = 0);
+
+/**
+ * pause action
+ */
+KDE_EXPORT KAction *pause(QObject *parent = 0, const char *name = 0);
+
+/**
+ * @return pointer to the global PluginActionMenu object (there is only one instance)
+ */
+KDE_EXPORT PluginActionMenu *actions();
+
+/**
+ * @return pointer to a VisActionMenu object
+ */
+KDE_EXPORT VisActionMenu *visualizations(QObject *parent = 0, const char *name = 0);
+
+/**
+ * The global popupmenu of noatun, there's not two or three but only one of these :)
+ * @author Charles Samuels
+ **/
+class KDE_EXPORT ContextMenu
+{
+public:
+ static KPopupMenu *createContextMenu(QWidget *p);
+
+ /**
+ * One menu to show them all, One menu to find them
+ * One menu to bring them all and in the darkness bind them
+ *
+ * In the land of Noatun where the oceans die
+ */
+ static KPopupMenu *contextMenu();
+
+ /**
+ * Show the context menu at point
+ **/
+ static void showContextMenu(const QPoint &);
+
+ /**
+ * show the context menu at the mouse's current position
+ **/
+ static void showContextMenu();
+private:
+ static KPopupMenu *mContextMenu;
+};
+
+}
+
+#endif
diff --git a/noatun/library/noatun/stereobuttonaction.h b/noatun/library/noatun/stereobuttonaction.h
new file mode 100644
index 00000000..ce423b64
--- /dev/null
+++ b/noatun/library/noatun/stereobuttonaction.h
@@ -0,0 +1,31 @@
+#ifndef STEREOBUTTONACTION_H
+#define STEREOBUTTONACTION_H
+
+#include <kaction.h>
+
+namespace NoatunStdAction
+{
+
+/**
+ * No, I never owned StereoButtonAction, but I'm tired
+ * and PlayAction is already taken.
+ */
+class StereoButtonAction : public KAction
+{
+Q_OBJECT
+public:
+ StereoButtonAction(const QString& text, int accel = 0, QObject* parent = 0, const char* name = 0 );
+ StereoButtonAction(const QString& text, int accel, const QObject* receiver, const char* slot, QObject* parent, const char* name = 0 );
+ StereoButtonAction(const QString& text, const QIconSet& pix, int accel = 0, QObject* parent = 0, const char* name = 0 );
+ StereoButtonAction(const QString& text, const QString& pix, int accel = 0, QObject* parent = 0, const char* name = 0 );
+ StereoButtonAction(const QString& text, const QIconSet& pix, int accel, const QObject* receiver, const char* slot, QObject* parent, const char* name = 0 );
+ StereoButtonAction(const QString& text, const QString& pix, int accel, const QObject* receiver, const char* slot, QObject* parent, const char* name = 0 );
+public slots:
+ void disable(void);
+ void enable(void);
+ void toggleEnabled(void);
+};
+
+}
+
+#endif
diff --git a/noatun/library/noatun/vequalizer.h b/noatun/library/noatun/vequalizer.h
new file mode 100644
index 00000000..73c421e1
--- /dev/null
+++ b/noatun/library/noatun/vequalizer.h
@@ -0,0 +1,455 @@
+/*
+ * Copyright (c) 2003 Charles Samuels <charles@kde.org>
+ *
+ * This file is hereby licensed under the GNU General Public License version
+ * 2 or later at your option.
+ *
+ * This file is licensed under the Qt Public License version 1 with the
+ * condition that the licensed will be governed under the Laws of California
+ * (USA) instead of Norway. Disputes will be settled in Santa Clara county
+ * courts.
+ *
+ * This file is licensed under the following additional license. Be aware
+ * that it is identical to the BSD license, except for the added clause 3:
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. By integrating this software into any other software codebase, you waive
+ all rights to any patents associated with the stated codebase.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef NOATUN_VEQUALIZER_H
+#define NOATUN_VEQUALIZER_H
+
+#include <qptrlist.h>
+#include <qobject.h>
+#include <kurl.h>
+#include <kdemacros.h>
+class VBand;
+
+/**
+ * Abstract base class for VInterpolation and VEqualizer
+ **/
+class VBandsInterface
+{
+ friend class VBand;
+
+public:
+ VBandsInterface();
+ virtual ~VBandsInterface();
+
+ virtual int bands() const=0;
+ virtual VBand band(int num)=0;
+ /**
+ * same as @ref band(num)
+ **/
+ VBand operator [] (int num);
+
+private:
+ virtual int level(int index) const=0;
+ virtual void setLevel(int index, int level)=0;
+};
+
+
+class VInterpolation;
+class VEqualizer;
+
+/**
+ * Represents a single band in a vequalizer
+ * @author Charles Samuels
+ **/
+class KDE_EXPORT VBand
+{
+ friend class VInterpolation;
+ friend class VEqualizer;
+
+ struct Private;
+ VBand::Private *d;
+
+private:
+ VBand(VBandsInterface *bands, int index, int start, int end);
+
+public:
+ ~VBand();
+
+ VBand(const VBand &copy);
+ VBand & operator =(const VBand &copy);
+
+ /**
+ * the intensity of the change.
+ * it's logarithmic. 0 is no change
+ * negative numbers are loss in intensity
+ * positive numbers are a gain
+ * And +-100 is the most you'd need to go
+ **/
+ int level() const;
+ void setLevel(int l);
+
+ int start() const;
+ int end() const;
+
+ /**
+ * the middle between start and end
+ **/
+ int center() const;
+
+ QString formatStart(bool withHz=true) const;
+ QString formatEnd(bool withHz=true) const;
+ /**
+ * return the format for center()
+ **/
+ QString format(bool withHz=true) const;
+};
+
+
+/**
+ * This class is an interpolated representation to the Eq data.
+ * This means that no matter how many bands
+ * the real equalizer has, your interpolated
+ * version appears like it has only as many
+ * bands as you asked for. You can continue
+ * to interact with all interpolations and
+ * the true equalizer as normal. They even
+ * modify eachother according to a Spline
+ * interpolation.
+ *
+ * @short interpolated representation of Eq data
+ * @author Charles Samuels
+ **/
+class KDE_EXPORT VInterpolation : public QObject, public VBandsInterface
+{
+ Q_OBJECT
+ struct Private;
+ Private *d;
+ friend class VBand;
+
+public:
+ /**
+ * create an interpolation with the one and only
+ * Noatun equalizer, and @p bands
+ **/
+ VInterpolation(int bands);
+ virtual ~VInterpolation();
+
+ virtual int bands() const;
+ virtual VBand band(int num);
+ virtual int level(int index) const;
+ virtual void setLevel(int index, int level);
+
+signals:
+ void changed();
+
+private:
+ void refresh();
+ void getFrequencies(int num, int *high, int *low) const;
+ /**
+ * where on the spline is my own interpolation's bandNum
+ * @returns an x value on the spline
+ **/
+ double onSpline(int bandNum) const;
+};
+
+class VPreset;
+
+/**
+ * @short Noatun EQ
+ * @author Charles Samuels
+ **/
+class KDE_EXPORT VEqualizer : public QObject, public VBandsInterface
+{
+ Q_OBJECT
+ friend class VBand;
+ friend class VPreset;
+ friend class Engine;
+ friend class NoatunApp;
+ friend class VInterpolation;
+
+ struct Private;
+ Private *d;
+
+ VEqualizer();
+ ~VEqualizer();
+ void init();
+
+public:
+
+ /**
+ * @return the frequencies to use with @p num many
+ * bands. This is a list of the upper frequency
+ * of each band, for example: { 40, 60, 100 } if
+ * you hear up to 40hz
+ **/
+ static QValueList<int> frequencies(int num);
+
+ /**
+ * @return number of bands I have, which may be different
+ * than what setBands was called with
+ * @sa setBands
+ **/
+ int bands() const;
+
+ /**
+ * @return first frequency I operate on, currently 20
+ *
+ * does not apply to an interpolation
+ **/
+ static int start();
+ /**
+ * @return last frequency I operate on, currently 20000
+ */
+ static int end();
+
+ /**
+ * @return maximum amount of bands I may have
+ **/
+ int maxBands() const;
+
+ /**
+ * the minimum number of bands I may have (2)
+ **/
+ int minBands() const;
+
+ VBand band(int num);
+
+ bool isEnabled() const;
+
+ /**
+ * returns the level of preamp (-100 to 100 normally)
+ **/
+ int preamp() const;
+
+public: // serialization
+ /**
+ * save the current levels
+ * all noatun equalizer files have the "*.noatunequalizer"
+ * pattern. Nevertheless, the file can be identified
+ * by magic, so it's not required
+ **/
+ bool save(const KURL &file, const QString &friendly) const;
+
+ /**
+ * restore the EQ settings from this file
+ **/
+ bool load(const KURL &file);
+
+ /**
+ * convert the current EQ settings to string form,
+ * suitable for storage, the given string is the title
+ *
+ * @see fromString
+ **/
+ QString toString(const QString &name="stored") const;
+
+ /**
+ * undo a toString, restoring the EQ
+ * to the settings in the given string,
+ * emitting the changed signals
+ **/
+ bool fromString(const QString &str);
+
+public: // presets
+ /**
+ * create a preset with such a name
+ * and remember that it'l return an invalied Preset
+ * if the name already exists
+ *
+ * If smart is true, append a number to the end
+ * of the name, if one already exists by the given
+ **/
+ VPreset createPreset(const QString &name, bool smart=true);
+
+ /**
+ * return all the presets
+ * remember to setAutoDelete on this
+ **/
+ QValueList<VPreset> presets() const;
+
+ /**
+ * @returns the preset with the given name
+ * or an invalid Preset if none exists
+ **/
+ VPreset presetByName(const QString &name);
+
+ /**
+ * @returns the preset in the given file
+ * or an invalid Preset if none exists
+ **/
+ VPreset presetByFile(const QString &file);
+
+ /**
+ * @returns whether a preset with the
+ * given name exists
+ **/
+ bool presetExists(const QString &name) const;
+
+signals:
+ /**
+ * emitted when the number of bands has changed (and hence
+ * all my associated Bands are useless
+ **/
+ void changedBands();
+
+ /**
+ * emitted when something changes. Preamplication, band level,
+ * number of bands, enabled/disabled
+ **/
+ void changed();
+ /**
+ * emitted when the value of one or more of the bands
+ * has changed, but not immediately after
+ * a changedBands
+ **/
+ void modified();
+
+ void preampChanged();
+ void preampChanged(int);
+
+ void enabled();
+ void disabled();
+
+ void enabled(bool e);
+
+ /**
+ * emitted when a preset is created
+ **/
+ void created(VPreset preset);
+
+ /**
+ * emitted when the given @p preset is
+ * selected
+ **/
+ void selected(VPreset preset);
+
+ /**
+ * when @p preset has been renamed
+ **/
+ void renamed(VPreset preset);
+
+ /**
+ * the given @p preset has been removed
+ **/
+ void removed(VPreset preset);
+public slots:
+ /**
+ * set the preamplification
+ * it's logarithmic. 0 is no change
+ * negative numbers are loss in intensity
+ * positive numbers are a gain
+ * And +-100 is the most you'd need to go
+ **/
+ void setPreamp(int p);
+ /**
+ * turn on EQ
+ */
+ void enable();
+ /**
+ * turn off EQ
+ */
+ void disable();
+ /**
+ * turn on/off EQ depending on @p e
+ */
+ void setEnabled(bool e);
+ void setBands(int bands);
+ void setBands(int bands, bool interpolate);
+
+private:
+ virtual int level(int index) const;
+ virtual void setLevel(int index, int level);
+ void setLevels(const QValueList<int> &levels);
+
+private:
+ /**
+ * tell the filter to start using the new settings
+ * if @p full is false, it doesn't take the full
+ * update, but just the values (not the number of
+ * bands
+ **/
+ void update(bool full=false);
+};
+
+/**
+ * a preset stores the state of the equalizer
+ *
+ * VEqualizer provides a way to get a list of these
+ * or access them by name
+ *
+ * this acts as a reference to the preset, so
+ * it might be invalid in which case
+ * isValid() is false, isNull() is true, and
+ * operator bool() return false
+ **/
+class VPreset
+{
+ friend class VEqualizer;
+
+ struct Private;
+ Private *d;
+
+ void *_bc;
+ VPreset(const QString &file);
+public:
+ VPreset();
+ VPreset(const VPreset &copy);
+ VPreset & operator=(const VPreset &copy);
+ ~VPreset();
+ bool operator ==(const VPreset &other) const;
+
+ /**
+ * @returns the name of the preset, which is user visible
+ **/
+ QString name() const;
+ /**
+ * set the user-presentable name of the preset
+ *
+ * Equalizer will emit renamed
+ *
+ * @returns success. If another preset is named
+ * this, it'l fail.
+ **/
+ bool setName(const QString &name);
+
+ /**
+ * @returns the file that this preset is in
+ **/
+ QString file() const;
+
+ bool isValid() const;
+ bool isNull() const { return !isValid(); }
+ operator bool() const { return isValid(); }
+
+ /**
+ * save the state of the equalizer into this preset
+ **/
+ void save();
+
+ /**
+ * load the preset into the equalizer
+ **/
+ void load() const;
+
+ /**
+ * obliterate this preset!
+ **/
+ void remove();
+};
+
+#endif
diff --git a/noatun/library/noatun/video.h b/noatun/library/noatun/video.h
new file mode 100644
index 00000000..3655c489
--- /dev/null
+++ b/noatun/library/noatun/video.h
@@ -0,0 +1,60 @@
+#ifndef NOATUN__VIDEO_H
+#define NOATUN__VIDEO_H
+
+#include <kvideowidget.h>
+#include <arts/kmedia2.h>
+
+class QPopupMenu;
+
+/**
+ * a widget that contains the video being played
+ **/
+class VideoFrame : public KVideoWidget
+{
+Q_OBJECT
+ struct Private;
+ VideoFrame::Private *d;
+
+ static QPtrList<VideoFrame> frames;
+ static VideoFrame *whose;
+
+public:
+ VideoFrame(KXMLGUIClient *clientParent, QWidget *parent=0, const char *name=0, WFlags f=0);
+ VideoFrame(QWidget *parent = 0, const char *name=0, WFlags f=0);
+ ~VideoFrame();
+
+ /**
+ * which one has the video (or will have it next, if no video is playing)
+ **/
+ static VideoFrame *playing();
+
+ QPopupMenu *popupMenu(QWidget *parent);
+ QPopupMenu *popupMenu() { return popupMenu(this); }
+
+public slots:
+ /**
+ * only one VideoFrame can be playing a video, make this the one
+ **/
+ void give();
+
+private slots:
+ void stopped();
+ void changed();
+
+signals:
+ /**
+ * signaled when video is playing in here, when
+ * (width*height) != 0
+ **/
+ void acquired();
+ /**
+ * signaled when video is no longer playing
+ * here, when (width*heoght) == 0
+ */
+ void lost();
+};
+
+
+
+#endif
+
diff --git a/noatun/library/noatunarts/Equalizer.mcopclass b/noatun/library/noatunarts/Equalizer.mcopclass
new file mode 100644
index 00000000..85dac1eb
--- /dev/null
+++ b/noatun/library/noatunarts/Equalizer.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::Equalizer,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/EqualizerSSE.mcopclass b/noatun/library/noatunarts/EqualizerSSE.mcopclass
new file mode 100644
index 00000000..9002a4ad
--- /dev/null
+++ b/noatun/library/noatunarts/EqualizerSSE.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::EqualizerSSE,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/Equalizer_impl.cpp b/noatun/library/noatunarts/Equalizer_impl.cpp
new file mode 100644
index 00000000..5d852b17
--- /dev/null
+++ b/noatun/library/noatunarts/Equalizer_impl.cpp
@@ -0,0 +1,472 @@
+/*
+Copyright (C) 2001 Charles Samuels <charles@kde.org>
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+
+*/
+
+#include "noatunarts.h"
+#include "artsflow.h"
+#include "fft.h"
+#include <stdsynthmodule.h>
+#include <math.h>
+#include <cstring>
+
+using namespace std;
+using namespace Arts;
+
+/**
+ * This class is _VERY_ picky
+ * which is why Noatun has it's own Equalizer class,
+ * that does all the error checking and sends it to here
+ **/
+
+namespace Noatun
+{
+
+void resize(vector<float> &vec, unsigned int newsize)
+{
+ while (newsize < vec.size())
+ vec.pop_back();
+ while (newsize > vec.size())
+ vec.push_back(0.0);
+}
+
+class Equalizer_impl : public Equalizer_skel, public StdSynthModule
+{
+ vector<float> mLevels;
+
+ vector<BandPassInfo> mBandLeft, mBandRight;
+
+ vector<float> mLevelWidths;
+ vector<float> mLevelCenters;
+
+ bool mEnabled;
+ float mPreamp;
+
+
+ float *mBuffer;
+ unsigned int mBufferLength;
+
+ void reinit()
+ {
+ mBandLeft.clear();
+ mBandRight.clear();
+ for (unsigned int i=0; i< mLevelWidths.size(); ++i)
+ {
+ BandPassInfo nfo;
+ BandPassInit(&nfo, mLevelCenters[i], mLevelWidths[i]);
+ mBandLeft.push_back(nfo);
+ mBandRight.push_back(nfo);
+ }
+
+ }
+
+public:
+ void set(const std::vector<float>& levels, const std::vector<float>& centers, const std::vector<float>& widths)
+ {
+ mLevelCenters=centers;
+ mLevelWidths=widths;
+
+ mLevels=levels;
+ reinit();
+ }
+
+ vector<float>* levelCenters()
+ {
+ return new vector<float>(mLevelCenters);
+ }
+
+ void levelCenters(const vector<float> &l)
+ {
+ mLevelCenters=l;
+ reinit();
+ }
+
+ vector<float>* levelWidths()
+ {
+ return new vector<float>(mLevelWidths);
+ }
+
+ void levelWidths(const vector<float> &l)
+ {
+ mLevelWidths=l;
+ reinit();
+ }
+
+ vector<float>* levels()
+ {
+ return new vector<float>(mLevels);
+ }
+
+ void levels(const vector<float> &l)
+ {
+ mLevels=l;
+ reinit();
+ }
+
+ long bands()
+ {
+ return mLevels.size();
+ }
+
+ void bands(long b)
+ {
+ resize(mLevels, (int)b);
+ resize(mLevelWidths, (int)b);
+ resize(mLevelCenters, (int)b);
+ reinit();
+ }
+
+ long enabled()
+ {
+ return (long)mEnabled;
+ }
+
+ void enabled(long enabled)
+ {
+ mEnabled=(bool)enabled;
+ }
+
+ float preamp()
+ {
+ return mPreamp;
+ }
+
+ void preamp(float a)
+ {
+ mPreamp=a;
+ }
+
+ void streamInit()
+ {
+
+ }
+
+ void streamStart()
+ {
+
+ }
+
+/* BandPassInit(&nfoLeft, 15000.0, 5000.0);
+ * BandPassInit(&nfoLeft, 15000.0, 5000.0);
+ */
+
+ void calculateBlock(unsigned long samples)
+ {
+ // works by separating the bands
+ // multiplying, then adding
+ if (mEnabled && samples && &mLevels.front())
+ {
+
+ { // preamp;
+
+ float *left=inleft;
+ float *right=inright;
+ float *end=left+samples;
+
+ float *oleft=outleft;
+ float *oright=outright;
+
+ while (left<end)
+ {
+ // see the _long_ comment in
+ // kdemultimedia/arts/modules/synth_std_equalizer_impl.cc
+ if (::fabs(*left) + ::fabs(*right) < 0.00000001)
+ goto copy; // if you apologize, it's becomes ok
+ *oleft=*left * mPreamp;
+ *oright=*right * mPreamp;
+ ++left;
+ ++right;
+ ++oleft;
+ ++oright;
+ }
+ }
+
+ BandPassInfo *leftBand=&mBandLeft.front();
+ BandPassInfo *rightBand=&mBandRight.front();
+ float *level=&mLevels.front();
+ float *end=&mLevels.back();
+ float intensity=1.0/(float)mLevels.size();
+
+ if (mBufferLength != samples)
+ {
+ delete mBuffer;
+ mBuffer = new float[samples];
+ mBufferLength = samples;
+ }
+
+ register float *buffer=mBuffer;
+ register float *bufferEnd=buffer+samples;
+ while (level<end)
+ {
+ register float *buffIter, *outIter;
+ float levelAndIntensity=*level * intensity;
+
+ BandPass(leftBand, outleft, buffer, samples);
+ for (buffIter=buffer, outIter=outleft; buffIter<bufferEnd; ++buffIter, ++outIter)
+ *outIter+=(*buffIter) * levelAndIntensity;
+
+ BandPass(rightBand, outright, buffer, samples);
+ for (buffIter=buffer, outIter=outright; buffIter<bufferEnd; ++buffIter, ++outIter)
+ *outIter+=(*buffIter) * levelAndIntensity;
+
+ ++level;
+ ++leftBand;
+ ++rightBand;
+ }
+ }
+ else
+ {
+ copy:
+ // ASM optimized, so much faster
+ memcpy(outleft, inleft, samples*sizeof(float));
+ memcpy(outright, inright, samples*sizeof(float));
+ }
+
+ }
+
+ Equalizer_impl() : mEnabled(false)
+ {
+ mBuffer=0;
+ mBufferLength=0;
+ }
+
+ ~Equalizer_impl()
+ {
+ delete [] mBuffer;
+ }
+
+ // speed hack! assume that someone else will
+ // suspend us
+ AutoSuspendState autoSuspend() { return asSuspend; }
+
+};
+
+
+
+
+
+
+class EqualizerSSE_impl : public EqualizerSSE_skel, public StdSynthModule
+{
+ vector<float> mLevels;
+
+ vector<BandPassInfo> mBandLeft, mBandRight;
+
+ vector<float> mLevelWidths;
+ vector<float> mLevelCenters;
+
+ bool mEnabled;
+ float mPreamp;
+
+
+
+ void reinit()
+ {
+ mBandLeft.clear();
+ mBandRight.clear();
+ for (unsigned int i=0; i< mLevelWidths.size(); ++i)
+ {
+ BandPassInfo nfo;
+ BandPassInit(&nfo, mLevelCenters[i], mLevelWidths[i]);
+ mBandLeft.push_back(nfo);
+ mBandRight.push_back(nfo);
+ }
+
+ }
+
+public:
+ void set(const std::vector<float>& levels, const std::vector<float>& centers, const std::vector<float>& widths)
+ {
+ mLevelCenters=centers;
+ mLevelWidths=widths;
+
+ mLevels=levels;
+ reinit();
+ }
+
+ vector<float>* levelCenters()
+ {
+ return new vector<float>(mLevelCenters);
+ }
+
+ void levelCenters(const vector<float> &l)
+ {
+ mLevelCenters=l;
+ reinit();
+ }
+
+ vector<float>* levelWidths()
+ {
+ return new vector<float>(mLevelWidths);
+ }
+
+ void levelWidths(const vector<float> &l)
+ {
+ mLevelWidths=l;
+ reinit();
+ }
+
+ vector<float>* levels()
+ {
+ return new vector<float>(mLevels);
+ }
+
+ void levels(const vector<float> &l)
+ {
+ mLevels=l;
+ reinit();
+ }
+
+ long bands()
+ {
+ return mLevels.size();
+ }
+
+ void bands(long b)
+ {
+ resize(mLevels, (int)b);
+ resize(mLevelWidths, (int)b);
+ resize(mLevelCenters, (int)b);
+ reinit();
+ }
+
+ long enabled()
+ {
+ return (long)mEnabled;
+ }
+
+ void enabled(long enabled)
+ {
+ mEnabled=(bool)enabled;
+ }
+
+ float preamp()
+ {
+ return mPreamp;
+ }
+
+ void preamp(float a)
+ {
+ mPreamp=a;
+ }
+
+ void streamInit()
+ {
+
+ }
+
+ void streamStart()
+ {
+
+ }
+
+/* BandPassInit(&nfoLeft, 15000.0, 5000.0);
+ * BandPassInit(&nfoLeft, 15000.0, 5000.0);
+ */
+
+ void calculateBlock(unsigned long samples)
+ {
+#ifdef __i386__
+ // works by separating the bands
+ // multiplying, then adding
+ if (mEnabled && samples)
+ {
+ if (*inleft + *inright == 0.0)
+ goto copy; // just shut up :)
+
+ { // preamp;
+
+ float *left=inleft;
+ float *right=inright;
+ float *end=left+samples;
+
+ float *oleft=outleft;
+ float *oright=outright;
+
+ while (left<end)
+ {
+ *oleft=*left * mPreamp;
+ *oright=*right * mPreamp;
+ ++left;
+ ++right;
+ ++oleft;
+ ++oright;
+ }
+ }
+
+ BandPassInfo *leftBand=&mBandLeft.front();
+ BandPassInfo *rightBand=&mBandRight.front();
+ float *level=&mLevels.front();
+ float *end=&mLevels.back();
+ float intensity=1.0/(float)mLevels.size();
+
+ register float *buffer=new float[samples];
+ register float *bufferEnd=buffer+samples;
+ while (level<end)
+ {
+ register float *buffIter, *outIter;
+ float levelAndIntensity=*level * intensity;
+
+ BandPassSSE(leftBand, outleft, buffer, samples);
+ for (buffIter=buffer, outIter=outleft; buffIter<bufferEnd; ++buffIter, ++outIter)
+ *outIter+=(*buffIter) * levelAndIntensity;
+
+ BandPassSSE(rightBand, outright, buffer, samples);
+ for (buffIter=buffer, outIter=outright; buffIter<bufferEnd; ++buffIter, ++outIter)
+ *outIter+=(*buffIter) * levelAndIntensity;
+
+ ++level;
+ ++leftBand;
+ ++rightBand;
+ }
+ delete [] buffer;
+ }
+ else
+ {
+ copy:
+ // ASM optimized, so much faster
+ memcpy(outleft, inleft, samples*sizeof(float));
+ memcpy(outright, inright, samples*sizeof(float));
+ }
+#else
+ (void)samples; // squelch warnings
+#endif
+ }
+
+ EqualizerSSE_impl() : mEnabled(false)
+ {
+
+ }
+
+ ~EqualizerSSE_impl()
+ {
+ }
+
+ // speed hack! assume that someone else will
+ // suspend us
+ AutoSuspendState autoSuspend() { return asSuspend; }
+};
+
+
+REGISTER_IMPLEMENTATION(Equalizer_impl);
+REGISTER_IMPLEMENTATION(EqualizerSSE_impl);
+
+}
+
+#undef SAMPLES
+
diff --git a/noatun/library/noatunarts/FFTScope.mcopclass b/noatun/library/noatunarts/FFTScope.mcopclass
new file mode 100644
index 00000000..65826259
--- /dev/null
+++ b/noatun/library/noatunarts/FFTScope.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::FFTScope,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/FFTScopeStereo.mcopclass b/noatun/library/noatunarts/FFTScopeStereo.mcopclass
new file mode 100644
index 00000000..f463a2ed
--- /dev/null
+++ b/noatun/library/noatunarts/FFTScopeStereo.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::FFTScopeStereo,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/FFTScopes.cpp b/noatun/library/noatunarts/FFTScopes.cpp
new file mode 100644
index 00000000..ab2a3bbb
--- /dev/null
+++ b/noatun/library/noatunarts/FFTScopes.cpp
@@ -0,0 +1,422 @@
+/*
+Copyright (C) 2000 Stefan Westerfeld <stefan@space.twc.de>
+ 2000 Charles Samuels <charles@kde.org>
+
+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., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+
+*/
+
+#include "noatunarts.h"
+#include "artsflow.h"
+#include "fft.h"
+#include <stdsynthmodule.h>
+#include <cmath>
+#include <cstring>
+
+#include <iostream>
+
+using namespace std;
+using namespace Arts;
+
+namespace Noatun
+{
+
+#define SAMPLES 4096
+
+static void doFft(float combine, float *inBuffer, vector<float> &scope)
+{
+ float out_real[SAMPLES],out_img[SAMPLES];
+ fft_float(SAMPLES,0,inBuffer,0,out_real,out_img);
+
+ scope.clear();
+
+ int previous=0;
+ int index=20;
+
+ while (previous < 2048 && index < 2048)
+ {
+ int end = int(std::exp(double(index)*combine));
+ float xrange = 0.0;
+
+ while (previous < end)
+ {
+ xrange += (std::fabs(out_img[previous]) + std::fabs(out_real[previous]));
+ previous++;
+ }
+
+ xrange /= float(SAMPLES);
+ scope.push_back(xrange);
+
+ index++;
+ }
+}
+
+
+class FFTScopeStereo_impl : public FFTScopeStereo_skel, public StdSynthModule
+{
+protected:
+ vector<float> mScopeLeft;
+ vector<float> mScopeRight;
+
+ float mCombine;
+
+ float *mWindow;
+
+ float *mInBufferLeft, *mInBufferRight;
+ unsigned long mInBufferPos;
+
+public:
+ void bandResolution(float res) { mCombine=res; }
+ float bandResolution() { return mCombine; }
+ void streamInit()
+ {
+ unsigned long i;
+ for(i=0;i<SAMPLES;i++)
+ {
+ float x = (float)i/(float)SAMPLES;
+ // double it, since we're at half intensity without
+ // adding both channels
+ mWindow[i] = sin(x*DDC_PI)*sin(x*DDC_PI)*2;
+
+ mInBufferLeft[i] = 0;
+ mInBufferRight[i] = 0;
+ }
+ doFft(mCombine, mInBufferLeft, mScopeLeft);
+ doFft(mCombine, mInBufferRight, mScopeRight);
+ }
+ void streamStart()
+ {
+ mInBufferPos = 0;
+ }
+ vector<float> *scopeLeft()
+ {
+ return new vector<float>(mScopeLeft);
+ }
+ vector<float> *scopeRight()
+ {
+ return new vector<float>(mScopeRight);
+ }
+ /*
+ in audio stream inleft, inright;
+ out audio stream outleft, outright;
+ */
+ void calculateBlock(unsigned long samples)
+ {
+ unsigned long i;
+ for(i=0;i<samples;i++)
+ {
+ mInBufferLeft[mInBufferPos] = inleft[i]*mWindow[mInBufferPos];
+ mInBufferRight[mInBufferPos] = inright[i]*mWindow[mInBufferPos];
+ if(++mInBufferPos == SAMPLES)
+ {
+ doFft(mCombine, mInBufferLeft, mScopeLeft);
+ doFft(mCombine, mInBufferRight, mScopeRight);
+ mInBufferPos = 0;
+ }
+ /*
+ monitoring only tasks can't be done with that StereoEffect
+ interface nicely - copy input to output until there is
+ something better
+ */
+ outleft[i] = inleft[i];
+ outright[i] = inright[i];
+ }
+ }
+
+ FFTScopeStereo_impl() : mCombine(0.152492)
+ {
+ mWindow = new float[SAMPLES];
+ mInBufferLeft = new float[SAMPLES];
+ mInBufferRight = new float[SAMPLES];
+
+ }
+ ~FFTScopeStereo_impl()
+ {
+ delete [] mWindow;
+ delete [] mInBufferLeft;
+ delete [] mInBufferRight;
+ }
+
+ AutoSuspendState autoSuspend() { return asSuspend; }
+
+};
+
+class FFTScope_impl : public FFTScope_skel, public StdSynthModule
+{
+protected:
+ vector<float> mScope;
+
+ float mCombine;
+
+ float *mWindow;
+ float *mInBuffer;
+ unsigned long mInBufferPos;
+
+public:
+ void bandResolution(float res) { mCombine=res; }
+ float bandResolution() { return mCombine; }
+ void streamInit()
+ {
+ unsigned long i;
+ for(i=0;i<SAMPLES;i++)
+ {
+ float x = (float)i/(float)SAMPLES;
+ mWindow[i] = sin(x*DDC_PI)*sin(x*DDC_PI);
+ mInBuffer[i] = 0;
+ }
+ doFft(mCombine, mInBuffer, mScope); // initialize so that we never return an empty scope
+ }
+ void streamStart()
+ {
+ mInBufferPos = 0;
+ }
+ vector<float> *scope()
+ {
+ return new vector<float>(mScope);
+ }
+
+ /*
+ in audio stream inleft, inright;
+ out audio stream outleft, outright;
+ */
+ void calculateBlock(unsigned long samples)
+ {
+ unsigned long i;
+
+ float *inBufferIt=mInBuffer+mInBufferPos;
+ float *inleftIt=inleft;
+ float *inrightIt=inright;
+ float *windowIt=mWindow+mInBufferPos;
+
+ for(i=0;i<samples;i++)
+ {
+ *inBufferIt = (*inleftIt + *inrightIt)* (*windowIt);
+ if(++mInBufferPos == SAMPLES)
+ {
+ doFft(mCombine, mInBuffer, mScope);
+ mInBufferPos = 0;
+ inBufferIt=mInBuffer;
+ }
+ inBufferIt++;
+ inleftIt++;
+ inrightIt++;
+ windowIt++;
+ }
+ /*
+ monitoring only tasks can't be done with that StereoEffect
+ interface nicely - copy input to output until there is
+ something better
+ */
+ memcpy(outleft, inleft, sizeof(float)*samples);
+ memcpy(outright, inright, sizeof(float)*samples);
+
+ }
+
+ FFTScope_impl() : mCombine(0.152492)
+ {
+ mWindow = new float[SAMPLES];
+ mInBuffer = new float[SAMPLES];
+ }
+
+ ~FFTScope_impl()
+ {
+ delete [] mWindow;
+ delete [] mInBuffer;
+ }
+
+ AutoSuspendState autoSuspend() { return asSuspend; }
+};
+
+class RawScope_impl : public RawScope_skel, public StdSynthModule
+{
+protected:
+ float *mScope;
+
+ int mScopeLength;
+ float *mScopeEnd;
+ float *mCurrent;
+
+public:
+ vector<float> *scope()
+ {
+ vector<float> *buf = new vector<float>;
+ buf->resize(mScopeLength);
+ char *front = (char *)(&buf->front());
+ memcpy(front, mCurrent, (mScopeEnd - mCurrent) * sizeof(float));
+ memcpy(front + (mScopeEnd - mCurrent)*sizeof(float), mScope,
+ (mCurrent - mScope) * sizeof(float));
+ return buf;
+ }
+
+ void buffer(long len)
+ {
+ delete [] mScope;
+
+ mScopeLength=len;
+ mScope=new float[len];
+ mScopeEnd=mScope+mScopeLength;
+ mCurrent=mScope;
+
+ memset(mScope, 0, mScopeLength);
+ }
+
+ long buffer()
+ {
+ return mScopeLength;
+ }
+
+ void calculateBlock(unsigned long samples)
+ {
+ for (unsigned long i=0; i<samples; ++i)
+ {
+ for (; mCurrent<mScopeEnd && i<samples; ++mCurrent, ++i)
+ {
+ *mCurrent = inleft[i] + inright[i];
+ }
+ if (mCurrent>=mScopeEnd)
+ mCurrent=mScope;
+ }
+
+ memcpy(outleft, inleft, sizeof(float)*samples);
+ memcpy(outright, inright, sizeof(float)*samples);
+
+ }
+
+ RawScope_impl()
+ {
+ mScope=0;
+ buffer(512);
+
+ }
+
+ ~RawScope_impl()
+ {
+ delete [] mScope;
+ }
+
+ AutoSuspendState autoSuspend() { return asSuspend; }
+};
+
+class RawScopeStereo_impl : public RawScopeStereo_skel, public StdSynthModule
+{
+protected:
+ int mScopeLength;
+
+ float *mScopeLeft;
+ float *mScopeEndLeft;
+ float *mCurrentLeft;
+
+ float *mScopeRight;
+ float *mScopeEndRight;
+ float *mCurrentRight;
+
+public:
+ vector<float> *scopeLeft()
+ {
+ vector<float> *buf = new vector<float>;
+ buf->resize(mScopeLength);
+ char *front = (char *)(&buf->front());
+ memcpy(front, mCurrentLeft, (mScopeEndLeft - mCurrentLeft) * sizeof(float));
+ memcpy(front + (mScopeEndLeft - mCurrentLeft)*sizeof(float), mScopeLeft,
+ (mCurrentLeft - mScopeLeft) * sizeof(float));
+ return buf;
+ }
+
+ vector<float> *scopeRight()
+ {
+ vector<float> *buf = new vector<float>;
+ buf->resize(mScopeLength);
+ char *front = (char *)(&buf->front());
+ memcpy(front, mCurrentRight, (mScopeEndRight - mCurrentRight) * sizeof(float));
+ memcpy(front + (mScopeEndRight - mCurrentRight)*sizeof(float), mScopeRight,
+ (mCurrentRight - mScopeRight) * sizeof(float));
+ return buf;
+ }
+
+ void buffer(long len)
+ {
+ delete [] mScopeRight;
+ delete [] mScopeLeft;
+
+ mScopeLength=len;
+ mScopeRight=new float[len];
+ mScopeLeft=new float[len];
+ mScopeEndRight=mScopeRight+mScopeLength;
+ mScopeEndLeft=mScopeLeft+mScopeLength;
+ mCurrentRight=mScopeRight;
+ mCurrentLeft=mScopeLeft;
+
+ memset(mScopeRight, 0, mScopeLength);
+ memset(mScopeLeft, 0, mScopeLength);
+ }
+
+ long buffer()
+ {
+ return mScopeLength;
+ }
+
+ void calculateBlock(unsigned long samples)
+ {
+ for (unsigned long i=0; i<samples; ++i)
+ {
+ for (; mCurrentLeft<mScopeEndLeft && i<samples; ++mCurrentLeft, ++i)
+ {
+ *mCurrentLeft = inleft[i];
+ }
+ if (mCurrentLeft>=mScopeEndLeft)
+ mCurrentLeft=mScopeLeft;
+ }
+
+ for (unsigned long i=0; i<samples; ++i)
+ {
+ for (; mCurrentRight<mScopeEndRight && i<samples; ++mCurrentRight, ++i)
+ {
+ *mCurrentRight = inright[i];
+ }
+ if (mCurrentRight>=mScopeEndRight)
+ mCurrentRight=mScopeRight;
+ }
+
+ memcpy(outleft, inleft, sizeof(float)*samples);
+ memcpy(outright, inright, sizeof(float)*samples);
+ }
+
+ RawScopeStereo_impl()
+ {
+ mScopeLeft=mScopeRight=0;
+ buffer(512);
+
+ }
+
+ ~RawScopeStereo_impl()
+ {
+ delete [] mScopeRight;
+ delete [] mScopeLeft;
+ }
+
+ AutoSuspendState autoSuspend() { return asSuspend; }
+};
+
+
+
+REGISTER_IMPLEMENTATION(FFTScope_impl);
+REGISTER_IMPLEMENTATION(FFTScopeStereo_impl);
+REGISTER_IMPLEMENTATION(RawScope_impl);
+REGISTER_IMPLEMENTATION(RawScopeStereo_impl);
+
+}
+
+#undef SAMPLES
diff --git a/noatun/library/noatunarts/Listener.mcopclass b/noatun/library/noatunarts/Listener.mcopclass
new file mode 100644
index 00000000..81f19d38
--- /dev/null
+++ b/noatun/library/noatunarts/Listener.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::Listener,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/Makefile.am b/noatun/library/noatunarts/Makefile.am
new file mode 100644
index 00000000..a08e279d
--- /dev/null
+++ b/noatun/library/noatunarts/Makefile.am
@@ -0,0 +1,33 @@
+INCLUDES= -I$(kde_includes)/arts $(all_includes)
+KDE_OPTIONS = nofinal
+
+lib_LTLIBRARIES = libnoatunarts.la
+libnoatunarts_la_SOURCES = noatunarts.cc fft.c Equalizer_impl.cpp \
+ FFTScopes.cpp StereoEffectStack_impl.cpp \
+ StereoVolumeControl_impl.cpp Session_impl.cpp
+libnoatunarts_la_COMPILE_FIRST = noatunarts.h
+libnoatunarts_la_LDFLAGS = $(all_libraries) -avoid-version -no-undefined
+libnoatunarts_la_LIBADD = -lkmedia2_idl -lsoundserver_idl -lartsflow
+libnoatunarts_la_METASOURCES = AUTO
+
+noatunarts.mcoptype: noatunarts.h
+noatunarts.mcopclass: noatunarts.h
+
+noatunarts.cc noatunarts.h: noatunarts.idl
+ $(MCOPIDL) -t -I$(kde_includes)/arts $(srcdir)/noatunarts.idl
+
+mcoptypedir = $(libdir)/mcop
+mcoptype_DATA = noatunarts.mcoptype noatunarts.mcopclass
+
+mcopclassdir = $(libdir)/mcop/Noatun
+mcopclass_DATA = Equalizer.mcopclass FFTScopeStereo.mcopclass StereoEffectStack.mcopclass \
+ EqualizerSSE.mcopclass RawScope.mcopclass StereoVolumeControl.mcopclass \
+ FFTScope.mcopclass RawScopeStereo.mcopclass StereoVolumeControlSSE.mcopclass \
+ Session.mcopclass Listener.mcopclass
+
+noatuninclude_HEADERS= noatunarts.h
+
+noatunincludedir = $(includedir)/noatun
+
+DISTCLEANFILES = noatunarts.cc noatunarts.h noatunarts.mcopclass noatunarts.mcoptype
+
diff --git a/noatun/library/noatunarts/RawScope.mcopclass b/noatun/library/noatunarts/RawScope.mcopclass
new file mode 100644
index 00000000..fb3d95be
--- /dev/null
+++ b/noatun/library/noatunarts/RawScope.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::RawScope,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/RawScopeStereo.mcopclass b/noatun/library/noatunarts/RawScopeStereo.mcopclass
new file mode 100644
index 00000000..cad987ee
--- /dev/null
+++ b/noatun/library/noatunarts/RawScopeStereo.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::RawScopeStereo,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/Session.mcopclass b/noatun/library/noatunarts/Session.mcopclass
new file mode 100644
index 00000000..036b8409
--- /dev/null
+++ b/noatun/library/noatunarts/Session.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::Session,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/Session_impl.cpp b/noatun/library/noatunarts/Session_impl.cpp
new file mode 100644
index 00000000..63912801
--- /dev/null
+++ b/noatun/library/noatunarts/Session_impl.cpp
@@ -0,0 +1,82 @@
+#include "noatunarts.h"
+#include <list>
+#include <algorithm>
+
+using namespace Arts;
+using namespace std;
+
+static bool compareArtsObjects(const Noatun::Listener &left, const Noatun::Listener &right)
+{
+ return left._isEqual(right);
+}
+
+list<Noatun::Listener>::iterator find(list<Noatun::Listener> &v, const Noatun::Listener &is,
+ bool (*compare)(const Noatun::Listener& left, const Noatun::Listener& right))
+{
+ for (list<Noatun::Listener>::iterator i=v.begin(); i!=v.end(); ++i)
+ {
+ if ((*compare)(is, *i))
+ return i;
+ }
+
+ return v.end();
+}
+
+static void sendMessage(Noatun::Listener l)
+{
+ l.message();
+}
+
+namespace Noatun
+{
+
+class Session_impl : public Session_skel
+{
+ list<Listener> listeners;
+
+ long mPid;
+
+public:
+
+ ~Session_impl()
+ {
+ for_each(listeners.begin(), listeners.end(), sendMessage);
+ }
+
+ long pid() { return mPid; }
+ void pid(long p) { mPid=p; }
+
+
+ void addListener(Noatun::Listener listener)
+ {
+ listeners.push_back(listener);
+ }
+
+ void removeListener(Noatun::Listener listener)
+ {
+ list<Listener>::iterator i=
+ find(listeners, listener, &compareArtsObjects);
+ if (i!=listeners.end())
+ listeners.erase(i);
+ }
+
+};
+
+class Listener_impl : public Listener_skel
+{
+
+private:
+ virtual void message()
+ {
+ // hmm
+ }
+
+};
+
+
+REGISTER_IMPLEMENTATION(Session_impl);
+REGISTER_IMPLEMENTATION(Listener_impl);
+
+
+} // namespace Noatun
+
diff --git a/noatun/library/noatunarts/StereoEffectStack.mcopclass b/noatun/library/noatunarts/StereoEffectStack.mcopclass
new file mode 100644
index 00000000..71c9a9cf
--- /dev/null
+++ b/noatun/library/noatunarts/StereoEffectStack.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::StereoEffectStack,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/StereoEffectStack_impl.cpp b/noatun/library/noatunarts/StereoEffectStack_impl.cpp
new file mode 100644
index 00000000..684d9694
--- /dev/null
+++ b/noatun/library/noatunarts/StereoEffectStack_impl.cpp
@@ -0,0 +1,253 @@
+#include "noatunarts.h"
+
+#include <artsflow.h>
+#include <flowsystem.h>
+#include <stdsynthmodule.h>
+#include <debug.h>
+
+using namespace std;
+using namespace Arts;
+
+namespace Noatun
+{
+class StereoEffectStack_impl : public StereoEffectStack_skel, public StdSynthModule
+{
+ public:
+ long nextID;
+
+ struct EffectEntry
+ {
+ StereoEffect effect;
+ string name;
+ long id;
+ };
+ list<EffectEntry *> fx;
+
+ void xconnect(bool connect, Object from, string fromP, Object to, string toP)
+ {
+ if(connect)
+ from._node()->connect(fromP,to._node(),toP);
+ else
+ from._node()->disconnect(fromP,to._node(),toP);
+ }
+
+ void xvirtualize(bool connect, string myPort, Object impl, string implPort)
+ {
+ if(connect)
+ _node()->virtualize(myPort,impl._node(),implPort);
+ else
+ _node()->devirtualize(myPort,impl._node(),implPort);
+ }
+
+ void internalconnect(bool c)
+ {
+ if(fx.empty())
+ {
+ /* no effects - forward input through to output */
+ xvirtualize(c,"outleft",Object::_from_base(this->_copy()),"inleft");
+ xvirtualize(c,"outright",Object::_from_base(this->_copy()),"inright");
+ }
+ else
+ {
+ list<EffectEntry *>::iterator ei;
+ EffectEntry *laste = 0;
+
+ long count = 0;
+ for(ei = fx.begin(); ei != fx.end(); ei++, count++)
+ {
+ EffectEntry *e = *ei;
+ if(count == 0) /* top of chain? virtualize to effect */
+ {
+ xvirtualize(c,"inleft",e->effect,"inleft");
+ xvirtualize(c,"inright",e->effect,"inright");
+ }
+ else /* not top? connect last effect to current effect */
+ {
+ xconnect(c,laste->effect,"outleft",e->effect,"inleft");
+ xconnect(c,laste->effect,"outright",e->effect,"inright");
+ }
+ laste = e;
+ }
+ /* end: virtualize effect output to our output */
+ xvirtualize(c,"outleft",laste->effect,"outleft");
+ xvirtualize(c,"outright",laste->effect,"outright");
+ }
+ }
+ void disconnect() { internalconnect(false); }
+ void reconnect() { internalconnect(true); }
+
+
+ long insertAfter(long after, StereoEffect effect, const string &name)
+ {
+ arts_return_val_if_fail(!effect.isNull(),0);
+ disconnect();
+
+ list<EffectEntry*>::iterator i = fx.begin();
+
+ bool found=false;
+ // seek through until we find 'after'
+ while(i != fx.end())
+ if((*i)->id == after)
+ {
+ found = true;
+ break;
+ }
+ else
+ i++;
+
+ long newId=0;
+ if (found)
+ {
+ i++;
+ EffectEntry *e = new EffectEntry;
+ e->effect=effect;
+ e->name=name;
+ e->id=nextID++;
+ fx.insert(i, e);
+ newId=e->id;
+ }
+ else
+ arts_warning("StereoEffectStack::insertAfter failed. "
+ "id %d not found?", after);
+
+ reconnect();
+ return newId;
+
+ }
+
+ void move(long after, long item)
+ {
+ arts_return_if_fail(item != 0);
+ disconnect();
+
+ list<EffectEntry*>::iterator iAfter=fx.begin();
+ bool found=false;
+ if (after)
+ while(iAfter != fx.end())
+ if((*iAfter)->id == after)
+ {
+ found = true;
+ iAfter++;
+ break;
+ }
+ else
+ iAfter++;
+ else
+ found=true;
+
+ list<EffectEntry*>::iterator iItem=fx.begin();
+ while (iItem != fx.end())
+ if((*iItem)->id == item)
+ {
+ found &= true;
+ break;
+ }
+ else
+ iItem++;
+ if (!found)
+ arts_warning("StereoEffectStack::move couldn't find items");
+ else
+ {
+ fx.insert(iAfter, *iItem);
+ fx.erase(iItem);
+ }
+
+ reconnect();
+
+ }
+
+ vector<long> *effectList()
+ {
+ vector<long> *items=new vector<long>;
+ for (list<EffectEntry*>::iterator i=fx.begin(); i!=fx.end();i++)
+ items->push_back((*i)->id);
+ return items;
+ }
+
+ // as stolen from stereoeffectstack_impl.cc
+ StereoEffectStack_impl() : nextID(1)
+ {
+ reconnect();
+ }
+
+ ~StereoEffectStack_impl()
+ {
+ // disconnect remaining effects
+ EffectEntry *laste = 0;
+ list<EffectEntry *>::iterator ei;
+
+ for(ei = fx.begin(); ei != fx.end(); ei++)
+ {
+ EffectEntry *e = *ei;
+ if(laste)
+ {
+ xconnect(false,laste->effect,"outleft",e->effect,"inleft");
+ xconnect(false,laste->effect,"outright",e->effect,"inright");
+ }
+ laste = e;
+ }
+ // delete remaining effect entries
+ for(ei = fx.begin(); ei != fx.end(); ei++)
+ delete *ei;
+ fx.clear();
+ }
+ long insertTop(StereoEffect effect, const string& name)
+ {
+ arts_return_val_if_fail(!effect.isNull(),0);
+
+ disconnect();
+ EffectEntry *e = new EffectEntry();
+ e->effect = effect;
+ e->name = name;
+ e->id = nextID++;
+ fx.push_front(e);
+ reconnect();
+ return e->id;
+ }
+ long insertBottom(StereoEffect effect, const string& name)
+ {
+ arts_return_val_if_fail(!effect.isNull(),0);
+
+ disconnect();
+ EffectEntry *e = new EffectEntry();
+ e->effect = effect;
+ e->name = name;
+ e->id = nextID++;
+ fx.push_back(e);
+ reconnect();
+ return e->id;
+ }
+
+ void remove(long ID)
+ {
+ arts_return_if_fail(ID != 0);
+
+ bool found = false;
+ disconnect();
+ list<EffectEntry *>::iterator ei = fx.begin();
+
+ while(ei != fx.end())
+ {
+ if((*ei)->id == ID) {
+ found = true;
+ delete (*ei);
+ fx.erase(ei);
+ ei = fx.begin();
+ }
+ else ei++;
+ }
+ if(!found) {
+ arts_warning("StereoEffectStack::remove failed. id %d not found?",
+ ID);
+ }
+ reconnect();
+ }
+
+ AutoSuspendState autoSuspend() { return asSuspend; }
+
+};
+
+REGISTER_IMPLEMENTATION(StereoEffectStack_impl);
+
+}
+
diff --git a/noatun/library/noatunarts/StereoVolumeControl.mcopclass b/noatun/library/noatunarts/StereoVolumeControl.mcopclass
new file mode 100644
index 00000000..c4aded7e
--- /dev/null
+++ b/noatun/library/noatunarts/StereoVolumeControl.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::StereoVolumeControl,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/StereoVolumeControlSSE.mcopclass b/noatun/library/noatunarts/StereoVolumeControlSSE.mcopclass
new file mode 100644
index 00000000..9a979f30
--- /dev/null
+++ b/noatun/library/noatunarts/StereoVolumeControlSSE.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::StereoVolumeControlSSE,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/StereoVolumeControl_impl.cpp b/noatun/library/noatunarts/StereoVolumeControl_impl.cpp
new file mode 100644
index 00000000..425704f6
--- /dev/null
+++ b/noatun/library/noatunarts/StereoVolumeControl_impl.cpp
@@ -0,0 +1,181 @@
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <artsflow.h>
+#include <stdsynthmodule.h>
+#include <flowsystem.h>
+#include "noatunarts.h"
+
+using namespace Arts;
+
+namespace Noatun
+{
+
+class StereoVolumeControl_impl : virtual public StereoVolumeControl_skel,
+ virtual public StdSynthModule
+{
+ float mPercent;
+ float level;
+public:
+ StereoVolumeControl_impl() : mPercent(1.0), level(0.0)
+ { }
+
+ /*attribute float scaleFactor;*/
+ void percent(float p) { mPercent=p; }
+ float percent() { return mPercent; }
+
+ void calculateBlock(unsigned long samples)
+ {
+ register float *left=inleft;
+ register float *right=inright;
+ register float *oleft=outleft;
+ register float *oright=outright;
+
+ level = *right + *left;
+
+ register float p=mPercent;
+
+ register float *end=left+samples;
+
+ while (left<end)
+ {
+ *oleft=*left * p;
+ *oright=*right * p;
+
+ ++left;
+ ++right;
+ ++oleft;
+ ++oright;
+ }
+ }
+
+ AutoSuspendState autoSuspend()
+ {
+ return (level < 0.001) ? asSuspend : asNoSuspend;
+ }
+};
+
+class StereoVolumeControlSSE_impl : virtual public Noatun::StereoVolumeControlSSE_skel,
+ virtual public StdSynthModule
+{
+ float mPercent;
+ float level;
+
+public:
+ StereoVolumeControlSSE_impl() : mPercent(1.0), level(0.0)
+ { }
+
+ /*attribute float scaleFactor;*/
+ void percent(float p) { mPercent=p; }
+ float percent() { return mPercent; }
+
+ void calculateBlock(unsigned long samples)
+ {
+#ifdef HAVE_X86_SSE
+ float *left=inleft;
+ float *right=inright;
+ float *oleft=outleft;
+ float *oright=outright;
+
+ level = *right + *left;
+
+ // need to copy the data members to locals to get enough
+ // spare registers (malte)
+
+ long p = (long)(mPercent*100.0);
+ __asm__ __volatile__(
+ "pushl $100 \n"
+ "fildl (%%esp) \n"
+#if defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)
+ "addl $4, %%esp \n"
+#endif
+ "fildl %5 \n"
+ "fdivp \n" // percent / 100.0
+#if defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)
+ "pushl $100 \n"
+#endif
+ "fstps (%%esp) \n"
+ "movss (%%esp), %%xmm1 \n"
+ "shufps $0x00, %%xmm1, %%xmm1 \n" // percentage in all of xmm1
+ "addl $4, %%esp \n"
+#if defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)
+ "subl $4, %4 \n"
+ "jl .l2 \n" // samples < 4
+#else
+ "pushl %4 \n" // save sample count
+ "shrl $2, %4 \n"
+ "jz .l2 \n" // samples < 4
+#endif
+ "xorl %%ecx, %%ecx \n"
+
+ ".l1: \n"
+ // left
+ "movups (%0, %%ecx, 8), %%xmm0 \n"
+ "mulps %%xmm1, %%xmm0 \n"
+ "movl %2, %%eax \n"
+ "movups %%xmm0, (%%eax, %%ecx, 8) \n"
+ // right
+ "movups (%1, %%ecx, 8), %%xmm0 \n"
+ "mulps %%xmm1, %%xmm0 \n"
+ "movl %3, %%eax \n"
+ "movups %%xmm0, (%%eax, %%ecx, 8) \n"
+
+ "incl %%ecx \n"
+ "incl %%ecx \n"
+#if defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)
+ "subl $4, %4 \n"
+ "jge .l1 \n"
+ ".l2: \n"
+ "addl $4, %4 \n"
+#else
+ "decl %4 \n"
+ "jnz .l1 \n"
+ ".l2: \n"
+ "popl %4 \n" // restore sample count
+ "andl $3, %4 \n"
+#endif
+ "jz .l4 \n"
+
+ // calculate remaining samples for samples % 4 != 0
+ "shll $1, %%ecx \n"
+ ".l3: \n"
+ "movss (%0, %%ecx, 4), %%xmm0 \n" // load left
+ "movss (%1, %%ecx, 4), %%xmm2 \n" // load right
+ "shufps $0x00, %%xmm2, %%xmm0 \n" // both channels in xmm0
+ "mulps %%xmm1, %%xmm0 \n"
+ "movl %2, %%eax \n"
+ "movss %%xmm0, (%%eax, %%ecx, 4) \n" // store left
+ "shufps $0x02, %%xmm0, %%xmm0 \n"
+ "movl %3, %%eax \n"
+ "movss %%xmm0, (%%eax, %%ecx, 4) \n" // store right
+ "incl %%ecx \n"
+ "decl %4 \n"
+ "jnz .l3 \n"
+
+ ".l4: \n"
+ "emms \n"
+ :
+ : "r" (left), // %0
+ "r" (right), // %1
+ "m" (oleft), // %2
+ "m" (oright), // %3
+ "r" (samples), // %4
+ "m" (p) // %5
+ : "eax", "ecx"
+ );
+#endif
+ }
+
+ AutoSuspendState autoSuspend()
+ {
+ return (level < 0.001) ? asSuspend : asNoSuspend;
+ }
+};
+
+REGISTER_IMPLEMENTATION(StereoVolumeControlSSE_impl);
+REGISTER_IMPLEMENTATION(StereoVolumeControl_impl);
+
+}
+
diff --git a/noatun/library/noatunarts/fft.c b/noatun/library/noatunarts/fft.c
new file mode 100644
index 00000000..86d647fb
--- /dev/null
+++ b/noatun/library/noatunarts/fft.c
@@ -0,0 +1,384 @@
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include "fft.h"
+
+#define TRUE 1
+#define FALSE 0
+
+/*
+ band pass filter for the Eq. This is a modification of Kai Lassfolk's work, as
+ removed from the Sound Processing Kit:
+
+ Sound Processing Kit - A C++ Class Library for Audio Signal Processing
+ Copyright (C) 1995-1998 Kai Lassfolk
+
+ 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
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#define SAMPLERATE 44100
+
+#ifndef M_PI
+#define M_PI DDC_PI
+#endif
+
+void BandPassInit(struct BandPassInfo *ip, float center, float bw)
+{
+ ip->center = center;
+ ip->bandwidth = bw;
+
+ ip->C = 1.0 / tan(M_PI * bw / SAMPLERATE);
+ ip->D = 2 * cos(2 * M_PI * center / SAMPLERATE);
+
+ ip->a[0] = 1.0 / (1.0 + ip->C);
+ ip->a[1] = 0.0;
+ ip->a[2] = -ip->a[0];
+
+ ip->b[0] = -ip->C * ip->D * ip->a[0];
+ ip->b[1] = (ip->C - 1.0) * ip->a[0];
+
+ ip->bufferX[0] = ip->bufferX[1] = 0.0;
+ ip->bufferY[0] = ip->bufferY[1] = 0.0;
+}
+void BandPassSSE(struct BandPassInfo *ip, float *inbuffer, float *outbuffer, unsigned long samples)
+{
+#ifdef HAVE_X86_SSE
+ __asm__(
+ "testl %0, %0 \n"
+ "jz .l5 \n" /* if (!samples) */
+
+ "movl %1, %%ecx \n"
+ "movups 0x10(%%ecx), %%xmm2 \n" /* ip->a[0] */
+ "shufps $0x00, %%xmm2, %%xmm2 \n" /* ip->a[0] all over xmm3 */
+ "movups 0x14(%%ecx), %%xmm4 \n" /* xmm4 = {ip->a[1], ip->a[2], ip->b} */
+ "movups 0x24(%%ecx), %%xmm5 \n" /* xmm5 = {ip->bufferX, ip->bufferY} */
+ "xorl %%ecx, %%ecx \n" /* i = 0 */
+ "movl $1, %%edx \n" /* j = 1 */
+ "prefetcht0 (%2) \n"
+ ".l1: \n"
+
+ "decl %%edx \n" /* --j */
+ "jnz .l4 \n" /* if (j) */
+
+ /* only load single values if less than four remain in inbuffer */
+ "testl $0xfffffffc, %0 \n"
+ "jnz .l2 \n"
+ "movss (%2, %%ecx, 4), %%xmm3\n"
+ "movl $1, %%edx \n"
+ "jmp .l3 \n"
+ ".l2: \n"
+ /* {inbuffer[i], inbuffer[i+1], inbuffer[i+2], inbuffer[i+3]} * ip->a[0] */
+ "movups (%2, %%ecx, 4), %%xmm3\n"
+ "movl $3, %%edx \n" /* j = 3 */
+ ".l3: \n"
+ "movaps %%xmm3, %%xmm6 \n"
+ "mulps %%xmm2, %%xmm3 \n"
+ ".l4: \n"
+
+ /* {ip->a[1], ip->a[2], ip->b} * {ip->bufferX, ip->bufferY} */
+ "movaps %%xmm4, %%xmm0 \n"
+ "mulps %%xmm5, %%xmm0 \n"
+ "movaps %%xmm0, %%xmm1 \n"
+ /* xmm0 = {xmm0[0] + xmm0[1], <unused>, xmm0[2] + xmm0[3], <unused>} */
+ "shufps $0xb1, %%xmm0, %%xmm1 \n"
+ "addps %%xmm1, %%xmm0 \n"
+ /* xmm0[0] -= xmm0[2] */
+ "movhlps %%xmm0, %%xmm1 \n"
+ "subss %%xmm1, %%xmm0 \n"
+ "addss %%xmm3, %%xmm0 \n" /* xmm0[0] += inbuffer[i] * ip->a[0] */
+ "movss %%xmm0, (%3, %%ecx, 4)\n" /* outbuffer[i] = xmm0[0] */
+
+ /* xmm5 = {inbuffer[i], xmm5[0], outbuffer[i], xmm5[2]} */
+ "shufps $0x24, %%xmm5, %%xmm0 \n"
+ "shufps $0x81, %%xmm0, %%xmm5 \n"
+ "movss %%xmm6, %%xmm5 \n"
+
+ /* right-shift xmm3 (inbuffer * ip->a[0]) and xmm6 (inbuffer) */
+ "shufps $0x39, %%xmm3, %%xmm3 \n"
+ "shufps $0x39, %%xmm6, %%xmm6 \n"
+
+ "incl %%ecx \n" /* ++i */
+ "decl %0 \n"
+ "jnz .l1 \n"
+
+ "movl %1,%%ecx \n"
+ "movups %%xmm5, 0x24(%%ecx) \n" /* {ip->bufferX, ip->bufferY} = xmm5 */
+ "emms \n"
+ ".l5: \n"
+ :
+ : "r" (samples), /* %0 */
+ "m" (ip), /* %1 */
+ "r" (inbuffer), /* %2 */
+ "r" (outbuffer) /* %3 */
+ : "ecx", "edx");
+#endif
+}
+
+void BandPass(struct BandPassInfo *ip, float *inbuffer, float *outbuffer, unsigned long samples)
+{
+ unsigned long i;
+ for (i=0; i<samples; ++i)
+ {
+ outbuffer[i] = ip->a[0] * inbuffer[i] + ip->a[1] * ip->bufferX[0] + ip->a[2]
+ * ip->bufferX[1] - ip->b[0] * ip->bufferY[0] - ip->b[1]
+ * ip->bufferY[1];
+
+ ip->bufferX[1] = ip->bufferX[0];
+ ip->bufferX[0] = inbuffer[i];
+ ip->bufferY[1] = ip->bufferY[0];
+ ip->bufferY[0] = outbuffer[i];
+ }
+}
+
+
+/*============================================================================
+
+ fftmisc.c - Don Cross <dcross@intersrv.com>
+
+ http://www.intersrv.com/~dcross/fft.html
+
+ Helper routines for Fast Fourier Transform implementation.
+ Contains common code for fft_float() and fft_double().
+
+ See also:
+ fourierf.c
+ fourierd.c
+ ..\include\fourier.h
+
+ Revision history:
+
+1998 September 19 [Don Cross]
+ Improved the efficiency of IsPowerOfTwo().
+ Updated coding standards.
+
+============================================================================*/
+
+
+#define BITS_PER_WORD (sizeof(unsigned) * 8)
+
+
+static int IsPowerOfTwo ( unsigned x )
+{
+ if ( x < 2 )
+ return FALSE;
+
+ if ( x & (x-1) ) /* Thanks to 'byang' for this cute trick! */
+ return FALSE;
+
+ return TRUE;
+}
+
+
+static unsigned NumberOfBitsNeeded ( unsigned PowerOfTwo )
+{
+ unsigned i;
+
+ if ( PowerOfTwo < 2 )
+ {
+ fprintf (
+ stderr,
+ ">>> Error in fftmisc.c: argument %d to NumberOfBitsNeeded is too small.\n",
+ PowerOfTwo );
+
+ exit(1);
+ }
+
+ for ( i=0; ; i++ )
+ {
+ if ( PowerOfTwo & (1 << i) )
+ return i;
+ }
+}
+
+
+
+static unsigned ReverseBits ( unsigned ind, unsigned NumBits )
+{
+ unsigned i, rev;
+
+ for ( i=rev=0; i < NumBits; i++ )
+ {
+ rev = (rev << 1) | (ind & 1);
+ ind >>= 1;
+ }
+
+ return rev;
+}
+
+/*
+static double Index_to_frequency ( unsigned NumSamples, unsigned Index )
+{
+ if ( Index >= NumSamples )
+ return 0.0;
+ else if ( Index <= NumSamples/2 )
+ return (double)Index / (double)NumSamples;
+
+ return -(double)(NumSamples-Index) / (double)NumSamples;
+}
+*/
+#undef TRUE
+#undef FALSE
+#undef BITS_PER_WORD
+/*============================================================================
+
+ fourierf.c - Don Cross <dcross@intersrv.com>
+
+ http://www.intersrv.com/~dcross/fft.html
+
+ Contains definitions for doing Fourier transforms
+ and inverse Fourier transforms.
+
+ This module performs operations on arrays of 'float'.
+
+ Revision history:
+
+1998 September 19 [Don Cross]
+ Updated coding standards.
+ Improved efficiency of trig calculations.
+
+============================================================================*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "fft.h"
+
+#define CHECKPOINTER(p) CheckPointer(p,#p)
+
+static void CheckPointer ( const void *p, const char *name )
+{
+ if ( p == NULL )
+ {
+ fprintf ( stderr, "Error in fft_float(): %s == NULL\n", name );
+ exit(1);
+ }
+}
+
+
+void fft_float (
+ unsigned NumSamples,
+ int InverseTransform,
+ float *RealIn,
+ float *ImagIn,
+ float *RealOut,
+ float *ImagOut )
+{
+ unsigned NumBits; /* Number of bits needed to store indices */
+ unsigned i, j, k, n;
+ unsigned BlockSize, BlockEnd;
+
+ double angle_numerator = 2.0 * DDC_PI;
+ double tr, ti; /* temp real, temp imaginary */
+
+ if ( !IsPowerOfTwo(NumSamples) )
+ {
+ fprintf (
+ stderr,
+ "Error in fft(): NumSamples=%u is not power of two\n",
+ NumSamples );
+
+ exit(1);
+ }
+
+ if ( InverseTransform )
+ angle_numerator = -angle_numerator;
+
+ CHECKPOINTER ( RealIn );
+ CHECKPOINTER ( RealOut );
+ CHECKPOINTER ( ImagOut );
+
+ NumBits = NumberOfBitsNeeded ( NumSamples );
+
+ /*
+ ** Do simultaneous data copy and bit-reversal ordering into outputs...
+ */
+
+ for ( i=0; i < NumSamples; i++ )
+ {
+ j = ReverseBits ( i, NumBits );
+ RealOut[j] = RealIn[i];
+ ImagOut[j] = (ImagIn == NULL) ? 0.0 : ImagIn[i];
+ }
+
+ /*
+ ** Do the FFT itself...
+ */
+
+ BlockEnd = 1;
+ for ( BlockSize = 2; BlockSize <= NumSamples; BlockSize <<= 1 )
+ {
+ double delta_angle = angle_numerator / (double)BlockSize;
+ double sm2 = sin ( -2 * delta_angle );
+ double sm1 = sin ( -delta_angle );
+ double cm2 = cos ( -2 * delta_angle );
+ double cm1 = cos ( -delta_angle );
+ double w = 2 * cm1;
+ double ar[3], ai[3];
+
+ for ( i=0; i < NumSamples; i += BlockSize )
+ {
+ ar[2] = cm2;
+ ar[1] = cm1;
+
+ ai[2] = sm2;
+ ai[1] = sm1;
+
+ for ( j=i, n=0; n < BlockEnd; j++, n++ )
+ {
+ ar[0] = w*ar[1] - ar[2];
+ ar[2] = ar[1];
+ ar[1] = ar[0];
+
+ ai[0] = w*ai[1] - ai[2];
+ ai[2] = ai[1];
+ ai[1] = ai[0];
+
+ k = j + BlockEnd;
+ tr = ar[0]*RealOut[k] - ai[0]*ImagOut[k];
+ ti = ar[0]*ImagOut[k] + ai[0]*RealOut[k];
+
+ RealOut[k] = RealOut[j] - tr;
+ ImagOut[k] = ImagOut[j] - ti;
+
+ RealOut[j] += tr;
+ ImagOut[j] += ti;
+ }
+ }
+
+ BlockEnd = BlockSize;
+ }
+
+ /*
+ ** Need to normalize if inverse transform...
+ */
+
+ if ( InverseTransform )
+ {
+ double denom = (double)NumSamples;
+
+ for ( i=0; i < NumSamples; i++ )
+ {
+ RealOut[i] /= denom;
+ ImagOut[i] /= denom;
+ }
+ }
+}
+
+
diff --git a/noatun/library/noatunarts/fft.h b/noatun/library/noatunarts/fft.h
new file mode 100644
index 00000000..e7f7804d
--- /dev/null
+++ b/noatun/library/noatunarts/fft.h
@@ -0,0 +1,88 @@
+#ifndef FFT_H
+#define FFT_H
+
+
+
+/* this is from ddcmath.h */
+
+#define DDC_PI (3.14159265358979323846)
+
+/*============================================================================
+
+ fourier.h - Don Cross <dcross@intersrv.com>
+
+ http://www.intersrv.com/~dcross/fft.html
+
+ Contains definitions for doing Fourier transforms
+ and inverse Fourier transforms.
+
+============================================================================*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+** If you change anything here, make sure to check if
+** the offsets used in the asm version of BandPass() are affected
+*/
+struct BandPassInfo
+{
+ float center;
+ float bandwidth;
+
+ float C, D;
+ float a[3], b[2];
+
+ float bufferX[2];
+ float bufferY[2];
+
+};
+
+void BandPassInit(struct BandPassInfo *i, float center, float bw);
+void BandPassSSE(struct BandPassInfo *ip, float *inbuffer, float *outbuffer, unsigned long samples);
+void BandPass(struct BandPassInfo *ip, float *inbuffer, float *outbuffer, unsigned long samples);
+
+/*
+** fft() computes the Fourier transform or inverse transform
+** of the complex inputs to produce the complex outputs.
+** The number of samples must be a power of two to do the
+** recursive decomposition of the FFT algorithm.
+** See Chapter 12 of "Numerical Recipes in FORTRAN" by
+** Press, Teukolsky, Vetterling, and Flannery,
+** Cambridge University Press.
+**
+** Notes: If you pass ImaginaryIn = NULL, this function will "pretend"
+** that it is an array of all zeroes. This is convenient for
+** transforming digital samples of real number data without
+** wasting memory.
+*/
+
+void fft_float (
+ unsigned NumSamples, /* must be a power of 2 */
+ int InverseTransform, /* 0=forward FFT, 1=inverse FFT */
+ float *RealIn, /* array of input's real samples */
+ float *ImaginaryIn, /* array of input's imag samples */
+ float *RealOut, /* array of output's reals */
+ float *ImaginaryOut ); /* array of output's imaginaries */
+
+
+/*
+int IsPowerOfTwo ( unsigned x );
+unsigned NumberOfBitsNeeded ( unsigned PowerOfTwo );
+unsigned ReverseBits ( unsigned index, unsigned NumBits );
+*/
+
+/*
+** The following function returns an "abstract frequency" of a
+** given index into a buffer with a given number of frequency samples.
+** Multiply return value by sampling rate to get frequency expressed in Hz.
+*/
+/*
+double Index_to_frequency ( unsigned NumSamples, unsigned Index );
+*/
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* FFT_H */
diff --git a/noatun/library/noatunarts/noatunarts.idl b/noatun/library/noatunarts/noatunarts.idl
new file mode 100644
index 00000000..809e2bb6
--- /dev/null
+++ b/noatun/library/noatunarts/noatunarts.idl
@@ -0,0 +1,91 @@
+#include <artsflow.idl>
+
+module Noatun
+{
+
+interface Equalizer : Arts::StereoEffect
+{
+ attribute sequence<float> levelCenters;
+ attribute sequence<float> levelWidths;
+ attribute sequence<float> levels;
+
+ attribute long bands;
+ attribute long enabled;
+ attribute float preamp;
+ void set(sequence<float> levels, sequence<float> centers, sequence<float> widths);
+};
+
+interface EqualizerSSE : Arts::StereoEffect
+{
+ attribute sequence<float> levelCenters;
+ attribute sequence<float> levelWidths;
+ attribute sequence<float> levels;
+
+ attribute long bands;
+ attribute long enabled;
+ attribute float preamp;
+ void set(sequence<float> levels, sequence<float> centers, sequence<float> widths);
+};
+
+interface FFTScope : Arts::StereoEffect
+{
+ attribute float bandResolution;
+ sequence<float> scope();
+};
+
+interface FFTScopeStereo : Arts::StereoEffect
+{
+ attribute float bandResolution;
+ sequence<float> scopeRight();
+ sequence<float> scopeLeft();
+};
+
+interface RawScope : Arts::StereoEffect
+{
+ attribute long buffer;
+ sequence<float> scope();
+};
+
+interface RawScopeStereo : Arts::StereoEffect
+{
+ attribute long buffer;
+ sequence<float> scopeLeft();
+ sequence<float> scopeRight();
+};
+
+interface StereoEffectStack : Arts::StereoEffect
+{
+ long insertAfter(long after, Arts::StereoEffect effect, string name);
+ void move(long after, long item);
+ sequence<long> effectList();
+ long insertTop(Arts::StereoEffect effect, string name);
+ long insertBottom(Arts::StereoEffect effect, string name);
+ void remove(long ID);
+};
+
+interface StereoVolumeControl : Arts::StereoEffect
+{
+ attribute float percent;
+};
+
+interface StereoVolumeControlSSE : Arts::StereoEffect
+{
+ attribute float percent;
+};
+
+interface Listener
+{
+ void message();
+};
+
+interface Session
+{
+ attribute long pid;
+ void addListener(Noatun::Listener listener);
+ void removeListener(Noatun::Listener listener);
+};
+
+
+};
+
+
diff --git a/noatun/library/noatunlistview.h b/noatun/library/noatunlistview.h
new file mode 100644
index 00000000..1571f64e
--- /dev/null
+++ b/noatun/library/noatunlistview.h
@@ -0,0 +1,5 @@
+// yeah yeah yeah.. fix qt and this can go away
+
+#define private protected
+#include <qlistview.h>
+#undef private
diff --git a/noatun/library/noatunstdaction.cpp b/noatun/library/noatunstdaction.cpp
new file mode 100644
index 00000000..f2837623
--- /dev/null
+++ b/noatun/library/noatunstdaction.cpp
@@ -0,0 +1,362 @@
+#include "stdaction.h"
+#include "app.h"
+#include "player.h"
+#include "stereobuttonaction.h"
+#include "pluginloader.h"
+
+#include <khelpmenu.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kstdaction.h>
+#include <qcursor.h>
+#include <qmap.h>
+#include <kdebug.h>
+#include <kstdguiitem.h>
+
+/**
+ * A namespace to have all of noatun's standard actions
+ * This is treated like KStdAction
+ **/
+namespace NoatunStdAction
+{
+/////////////////////////////////////////////////////
+PlayAction::PlayAction(QObject *parent, const char *name)
+ : KAction(i18n("Play"), 0, napp->player(), SLOT(playpause()), parent, name)
+{
+ connect(napp->player(), SIGNAL(playing()), SLOT(playing()));
+ connect(napp->player(), SIGNAL(paused()), SLOT(notplaying()));
+ connect(napp->player(), SIGNAL(stopped()), SLOT(notplaying()));
+ if (napp->player()->isPlaying())
+ playing();
+ else if (napp->player()->isPaused() || napp->player()->isStopped())
+ notplaying();
+}
+
+void PlayAction::playing()
+{
+ setIconSet(QIconSet(SmallIcon("player_pause")));
+ setText(i18n("Pause"));
+}
+
+void PlayAction::notplaying()
+{
+ setIconSet(QIconSet(SmallIcon("player_play")));
+ setText(i18n("Play"));
+}
+/////////////////////////////////////////////////////
+
+PlaylistAction::PlaylistAction(QObject *parent, const char *name)
+ : KToggleAction(i18n("Show Playlist"), "playlist", 0, napp->player(), SLOT(toggleListView()), parent, name)
+{
+ setCheckedState(i18n("Hide Playlist"));
+ connect(napp->player(), SIGNAL(playlistShown()), SLOT(shown()));
+ connect(napp->player(), SIGNAL(playlistHidden()), SLOT(hidden()));
+ setChecked(napp->playlist()->listVisible());
+}
+
+void PlaylistAction::shown()
+{
+ setChecked(true);
+}
+
+void PlaylistAction::hidden()
+{
+ setChecked(false);
+}
+
+////////////////////////////////////////////////////
+
+PluginActionMenu::PluginActionMenu(QObject *parent, const char *name)
+ : KActionMenu(i18n("&Actions"), parent, name)
+{
+// kdDebug(66666) << k_funcinfo << "called" << endl;
+ setEnabled(false);
+ mCount=0;
+}
+
+void PluginActionMenu::insert (KAction *action, int index)
+{
+// kdDebug(66666) << k_funcinfo << "called" << endl;
+ KActionMenu::insert(action,index);
+ setEnabled(true);
+ mCount++;
+}
+
+void PluginActionMenu::remove(KAction *action)
+{
+// kdDebug(66666) << k_funcinfo << "called" << endl;
+ KActionMenu::remove(action);
+ mCount--;
+ if(mCount==0)
+ setEnabled(false);
+}
+
+int PluginActionMenu::menuAdd(const QString &text, const QObject *receiver, const char *member)
+{
+// kdDebug(66666) << k_funcinfo << "called, mCount is currently at " << mCount << endl;
+ setEnabled(true);
+ mCount++;
+ return popupMenu()->insertItem(text, receiver, member);
+}
+
+void PluginActionMenu::menuRemove(int id)
+{
+// kdDebug(66666) << k_funcinfo << "called, mCount is currently at " << mCount << endl;
+ popupMenu()->removeItem(id);
+ mCount--;
+ if(mCount==0)
+ setEnabled(false);
+}
+
+////////////////////////////////////////////////////
+
+VisActionMenu::VisActionMenu(QObject *parent, const char *name)
+ : KActionMenu(i18n("&Visualizations"), parent, name)
+{
+ connect(popupMenu(), SIGNAL(aboutToShow()), this, SLOT(fillPopup()));
+ connect(popupMenu(), SIGNAL(activated(int)), this, SLOT(toggleVisPlugin(int)));
+}
+
+void VisActionMenu::fillPopup()
+{
+ int id;
+ popupMenu()->clear();
+ mSpecMap.clear();
+
+ QValueList<NoatunLibraryInfo> available = napp->libraryLoader()->available();
+ QValueList<NoatunLibraryInfo> loaded = napp->libraryLoader()->loaded();
+
+ for(QValueList<NoatunLibraryInfo>::Iterator i = available.begin(); i != available.end(); ++i)
+ {
+ if ((*i).type == "visualization")
+ {
+ id = popupMenu()->insertItem((*i).name);
+ mSpecMap[id] = (*i).specfile;
+ popupMenu()->setItemChecked(id, loaded.contains(*i));
+ }
+ }
+}
+
+void VisActionMenu::toggleVisPlugin(int id)
+{
+ if(!mSpecMap.contains(id))
+ return;
+
+ QString specfile = mSpecMap[id];
+
+ if(popupMenu()->isItemChecked(id))
+ {
+ napp->libraryLoader()->remove(specfile);
+ popupMenu()->setItemChecked(id, false);
+ }
+ else
+ {
+ napp->libraryLoader()->add(specfile);
+ popupMenu()->setItemChecked(id, true);
+ }
+}
+
+////////////////////////////////////////////////////
+
+LoopActionMenu::LoopActionMenu(QObject *parent, const char *name)
+ : KActionMenu(i18n("&Loop"), parent, name)
+{
+ mLoopNone = new KRadioAction(i18n("&None"), QString::fromLocal8Bit("noatunloopnone"),
+ 0, this, SLOT(loopNoneSelected()), this, "loop_none");
+ mLoopNone->setExclusiveGroup("loopType");
+ insert(mLoopNone);
+
+ mLoopSong = new KRadioAction(i18n("&Song"), QString::fromLocal8Bit("noatunloopsong"),
+ 0, this, SLOT(loopSongSelected()), this, "loop_song");
+ mLoopSong->setExclusiveGroup("loopType");
+ insert(mLoopSong);
+
+ mLoopPlaylist = new KRadioAction(i18n("&Playlist"), QString::fromLocal8Bit("noatunloopplaylist"),
+ 0, this, SLOT(loopPlaylistSelected()), this, "loop_playlist");
+ mLoopPlaylist->setExclusiveGroup("loopType");
+ insert(mLoopPlaylist);
+
+ mLoopRandom = new KRadioAction(i18n("&Random"), QString::fromLocal8Bit("noatunlooprandom"),
+ 0, this, SLOT(loopRandomSelected()), this, "loop_random");
+ mLoopRandom->setExclusiveGroup("loopType");
+ insert(mLoopRandom);
+
+ connect(napp->player(), SIGNAL(loopTypeChange(int)), this, SLOT(updateLooping(int)));
+
+ updateLooping(static_cast<int>(napp->player()->loopStyle()));
+}
+
+void LoopActionMenu::updateLooping(int loopType)
+{
+ switch(loopType)
+ {
+ case Player::None:
+ mLoopNone->setChecked(true);
+ setIcon("noatunloopnone");
+ break;
+ case Player::Song:
+ mLoopSong->setChecked(true);
+ setIcon("noatunloopsong");
+ break;
+ case Player::Playlist:
+ mLoopPlaylist->setChecked(true);
+ setIcon("noatunloopplaylist");
+ break;
+ case Player::Random:
+ mLoopRandom->setChecked(true);
+ setIcon("noatunlooprandom");
+ break;
+ }
+}
+
+void LoopActionMenu::loopNoneSelected()
+{
+ napp->player()->loop(Player::None);
+}
+
+void LoopActionMenu::loopSongSelected()
+{
+ napp->player()->loop(Player::Song);
+}
+
+void LoopActionMenu::loopPlaylistSelected()
+{
+ napp->player()->loop(Player::Playlist);
+}
+
+void LoopActionMenu::loopRandomSelected()
+{
+ napp->player()->loop(Player::Random);
+}
+
+////////////////////////////////////////////////////
+
+KAction *playpause(QObject *parent, const char *name)
+{
+ return new PlayAction(parent, name);
+}
+
+KAction *effects(QObject *parent, const char *name)
+{
+ return new KAction(i18n("&Effects..."), "effect", 0, napp, SLOT(effectView()), parent, name);
+}
+
+KAction *equalizer(QObject *parent, const char *name)
+{
+ return new KAction(i18n("E&qualizer..."), "equalizer", 0, napp, SLOT(equalizerView()), parent, name);
+}
+
+KAction *back(QObject *parent, const char *name)
+{
+ return new KAction(i18n("&Back"), "player_start", 0, napp->player(), SLOT(back()), parent, name);
+}
+
+KAction *stop(QObject *parent, const char *name)
+{
+ StereoButtonAction *action = new StereoButtonAction(i18n("Stop"), "player_stop", 0, napp->player(), SLOT(stop()), parent, name);
+ QObject::connect(napp->player(), SIGNAL(playing()), action, SLOT(enable()));
+ QObject::connect(napp->player(), SIGNAL(paused()), action, SLOT(enable()));
+ QObject::connect(napp->player(), SIGNAL(stopped()), action, SLOT(disable()));
+ if(napp->player()->isStopped())
+ action->disable();
+ else
+ action->enable();
+ return action;
+}
+
+KAction *forward(QObject *parent, const char *name)
+{
+ return new KAction(i18n("&Forward"), "player_end", 0, napp->player(), SLOT(forward()), parent, name);
+}
+
+KAction *play(QObject *parent, const char *name)
+{
+ StereoButtonAction *action = new StereoButtonAction(i18n("&Play"), "player_play", 0, napp->player(), SLOT(playpause()), parent, name);
+ QObject::connect(napp->player(), SIGNAL(playing()), action, SLOT(disable()));
+ QObject::connect(napp->player(), SIGNAL(paused()), action, SLOT(enable()));
+ QObject::connect(napp->player(), SIGNAL(stopped()), action, SLOT(enable()));
+ if(napp->player()->isPlaying())
+ action->disable();
+ else
+ action->enable();
+ return action;
+}
+
+KAction *pause(QObject *parent, const char *name)
+{
+ StereoButtonAction *action = new StereoButtonAction(i18n("&Pause"), "player_pause", 0, napp->player(), SLOT(playpause()), parent, name);
+ QObject::connect(napp->player(), SIGNAL(playing()), action, SLOT(enable()));
+ QObject::connect(napp->player(), SIGNAL(paused()), action, SLOT(disable()));
+ QObject::connect(napp->player(), SIGNAL(stopped()), action, SLOT(disable()));
+ if(napp->player()->isPlaying())
+ action->enable();
+ else
+ action->disable();
+ return action;
+}
+
+LoopActionMenu *loop(QObject *parent, const char *name)
+{
+ return new LoopActionMenu(parent, name);
+}
+
+PluginActionMenu *actions()
+{
+ // NoatunApp makes sure that we only have one ActionMenu around
+ return napp->pluginActionMenu();
+}
+
+VisActionMenu *visualizations(QObject *parent, const char *name)
+{
+ return new VisActionMenu(parent, name);
+}
+
+KToggleAction *playlist(QObject *parent, const char *name)
+{
+ return new PlaylistAction(parent, name);
+}
+
+KPopupMenu *ContextMenu::mContextMenu = 0;
+
+KPopupMenu *ContextMenu::contextMenu()
+{
+ if(!mContextMenu) mContextMenu = createContextMenu(0);
+
+ return mContextMenu;
+}
+
+KPopupMenu *ContextMenu::createContextMenu(QWidget *p)
+{
+ KPopupMenu *contextMenu = new KPopupMenu(p, "NoatunContextMenu");
+
+ KHelpMenu *helpmenu = new KHelpMenu(contextMenu, kapp->aboutData(), false);
+ KActionCollection* actions = new KActionCollection(helpmenu);
+
+ KStdAction::open(napp, SLOT(fileOpen()), actions)->plug(contextMenu);
+ KStdAction::quit(napp, SLOT(quit()), actions)->plug(contextMenu);
+ contextMenu->insertItem(SmallIcon("help"), KStdGuiItem::help().text(), helpmenu->menu());
+ contextMenu->insertSeparator();
+ KStdAction::preferences(napp, SLOT(preferences()), actions)->plug(contextMenu);
+ NoatunStdAction::playlist(contextMenu)->plug(contextMenu);
+ NoatunStdAction::effects(contextMenu)->plug(contextMenu);
+ NoatunStdAction::equalizer(napp)->plug(contextMenu);
+ NoatunStdAction::visualizations(napp)->plug(contextMenu);
+ napp->pluginActionMenu()->plug(contextMenu);
+
+ return contextMenu;
+}
+
+void ContextMenu::showContextMenu(const QPoint &p)
+{
+ contextMenu()->exec(p);
+}
+
+void ContextMenu::showContextMenu()
+{
+ showContextMenu(QCursor::pos());
+}
+
+} // END namespace NoatunStdAction
+
+#include "stdaction.moc"
diff --git a/noatun/library/noatuntags/Makefile.am b/noatun/library/noatuntags/Makefile.am
new file mode 100644
index 00000000..ea90faa1
--- /dev/null
+++ b/noatun/library/noatuntags/Makefile.am
@@ -0,0 +1,14 @@
+INCLUDES = -I$(top_srcdir)/noatun/library $(all_includes)
+
+lib_LTLIBRARIES = libnoatuntags.la
+
+libnoatuntags_la_SOURCES = tags.cpp
+libnoatuntags_la_LDFLAGS = -version-info 3:0:2 $(all_libraries)
+libnoatuntags_la_LIBADD = ../libnoatun.la
+
+libnoatuntags_la_METASOURCES = AUTO
+
+noatuntagsincludedir = $(includedir)/noatun
+noatuntagsinclude_HEADERS = tags.h
+
+
diff --git a/noatun/library/noatuntags/tags.cpp b/noatun/library/noatuntags/tags.cpp
new file mode 100644
index 00000000..38fd4abf
--- /dev/null
+++ b/noatun/library/noatuntags/tags.cpp
@@ -0,0 +1,229 @@
+#include "tags.h"
+#include "tagsgetter.h"
+#include <klocale.h>
+#include <qslider.h>
+#include <qspinbox.h>
+#include <kconfig.h>
+#include <qlayout.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qtimer.h>
+#include <noatun/player.h>
+#include <qcheckbox.h>
+
+TagsGetter *Tags::getter=0;
+
+TagsGetter::TagsGetter()
+{
+ new Control(this);
+ connect(napp->player(), SIGNAL(newSong()), SLOT(newSong()));
+}
+
+int TagsGetter::interval() const
+{
+ KGlobal::config()->setGroup("Tags");
+ return KGlobal::config()->readNumEntry("interval", 250);
+}
+
+bool TagsGetter::loadAuto() const
+{
+ KGlobal::config()->setGroup("Tags");
+ return KGlobal::config()->readBoolEntry("LoadAuto", true);
+}
+
+void TagsGetter::added(PlaylistItem &i)
+{
+ items += i;
+ killTimers();
+ startTimer(interval());
+}
+
+void TagsGetter::removed(PlaylistItem &i)
+{
+ items.remove(i);
+}
+
+void TagsGetter::getSongs()
+{
+ items = napp->playlist()->select("Tags::tagged_", "", -1, true, true);
+ killTimers();
+ startTimer(interval());
+}
+
+void TagsGetter::timerEvent(QTimerEvent *)
+{
+ if (!items.size())
+ {
+ killTimers();
+ return;
+ }
+
+ PlaylistItem item=items.first();
+ for (Tags *i=tags.first(); i; i=tags.next())
+ {
+ if (i->update(item))
+ {
+ item.setProperty("Tags::tagged_", "1");
+ if (item==napp->player()->current())
+ napp->player()->handleButtons();
+ }
+ }
+
+ items.remove(items.begin());
+}
+
+void TagsGetter::newSong()
+{
+ PlaylistItem item=napp->player()->current();
+ if (!item) return;
+
+ for (Tags *i=tags.first(); i; i=tags.next())
+ {
+ if (i->update(item))
+ {
+ item.setProperty("Tags::tagged_", "1");
+ napp->player()->handleButtons();
+ }
+ }
+ items.remove(item);
+}
+
+void TagsGetter::setInterval(int ms)
+{
+ killTimers();
+ startTimer(ms);
+
+ KGlobal::config()->setGroup("Tags");
+ KGlobal::config()->writeEntry("interval", ms);
+ KGlobal::config()->sync();
+}
+
+void TagsGetter::setLoadAuto(bool eh)
+{
+
+ KGlobal::config()->setGroup("Tags");
+ KGlobal::config()->writeEntry("LoadAuto", eh);
+ KGlobal::config()->sync();
+
+ killTimers();
+
+ if (eh) startTimer(interval());
+}
+
+void TagsGetter::associate(Tags *t)
+{
+ tags.append(t);
+ sortPriority();
+// getSongs();
+ QTimer::singleShot(interval(), this, SLOT(getSongs()));
+}
+
+void TagsGetter::sortPriority()
+{
+ // find the lowest one, since it comes first
+
+ int lowest=0;
+ for (Tags *i=tags.first(); i; i=tags.next())
+ {
+ if (lowest>i->mPriority)
+ lowest=i->mPriority;
+ }
+
+ QPtrList<Tags> sorted;
+ while (tags.count())
+ {
+ // find the one equal to myself
+ for (Tags *i=tags.first(); i;)
+ {
+ if (lowest==i->mPriority)
+ {
+ sorted.append(i);
+ tags.removeRef(i);
+ i=tags.first();
+ }
+ else
+ {
+ i=tags.next();
+ }
+ }
+ lowest++;
+ }
+
+ tags=sorted;
+}
+
+bool TagsGetter::unassociate(Tags *t)
+{
+ tags.removeRef(t);
+ if (tags.count()==0)
+ {
+ delete this;
+ return true;
+ }
+ return false;
+}
+
+Tags::Tags(int priority) : mPriority(priority)
+{
+ if (!getter)
+ getter=new TagsGetter;
+ getter->associate(this);
+}
+
+Tags::~Tags()
+{
+ if (getter->unassociate(this))
+ getter=0;
+}
+
+
+Control::Control(TagsGetter *parent)
+ : CModule(i18n("Tagging"), i18n("Settings for Tag Loaders"), "edit", parent)
+{
+ // todo
+ (void)I18N_NOOP("Rescan All Tags");
+
+ QVBoxLayout *l=new QVBoxLayout(this);
+ QCheckBox *onPlay;
+ {
+ onPlay=new QCheckBox(i18n("Load tags &automatically"), this);
+ l->addWidget(onPlay);
+ onPlay->show();
+ }
+
+ {
+ QHBox *intervalLine=new QHBox(this);
+ l->addWidget(intervalLine);
+ l->addStretch();
+
+ new QLabel(i18n(
+ "The time between each time noatun scans for a new file"
+ ", and updates tags (e.g., ID3)",
+ "Interval:"), intervalLine);
+ QSlider *slider=new QSlider(
+ 0, 2000, 100, 0, Horizontal, intervalLine
+ );
+ QSpinBox *spin=new QSpinBox(
+ 0, 2000, 10, intervalLine
+ );
+
+ spin->setSuffix(i18n("Milliseconds", " ms"));
+
+
+ connect(slider, SIGNAL(valueChanged(int)), spin, SLOT(setValue(int)));
+ connect(spin, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int)));
+ slider->setValue(parent->interval());
+ connect(slider, SIGNAL(valueChanged(int)), parent, SLOT(setInterval(int)));
+
+ connect(onPlay, SIGNAL(toggled(bool)), intervalLine, SLOT(setEnabled(bool)));
+ }
+ connect(onPlay, SIGNAL(toggled(bool)), parent, SLOT(setLoadAuto(bool)));
+
+ onPlay->setChecked(parent->loadAuto());
+}
+
+
+
+
+#include "tagsgetter.moc"
+
diff --git a/noatun/library/noatuntags/tags.h b/noatun/library/noatuntags/tags.h
new file mode 100644
index 00000000..ec98aef2
--- /dev/null
+++ b/noatun/library/noatuntags/tags.h
@@ -0,0 +1,35 @@
+#ifndef __NOATUN_TAGS___TACOS_ARE_YUMMY
+#define __NOATUN_TAGS___TACOS_ARE_YUMMY
+
+#include <noatun/playlist.h>
+
+class TagsGetter;
+
+class Tags
+{
+ friend class TagsGetter;
+public:
+ /**
+ * priority is how early this comes
+ * 0 means "normal"
+ * anything larger than zero means that this should come later,
+ * negative numbers mean it should come first
+ *
+ * I'm talking about the order which you're being processed
+ **/
+ Tags(int priority=0);
+ virtual ~Tags();
+
+ /**
+ * this will be called occasionally
+ * with an item you should fill up
+ **/
+ virtual bool update(PlaylistItem &item)=0;
+
+private:
+ static TagsGetter *getter;
+ int mPriority;
+};
+
+#endif
+
diff --git a/noatun/library/noatuntags/tagsgetter.h b/noatun/library/noatuntags/tagsgetter.h
new file mode 100644
index 00000000..269b7b90
--- /dev/null
+++ b/noatun/library/noatuntags/tagsgetter.h
@@ -0,0 +1,56 @@
+#ifndef TAGSGET_H
+#define TAGSGET_H
+
+#include <qobject.h>
+#include <cmodule.h>
+#include <qvaluelist.h>
+#include <qptrlist.h>
+#include "tags.h"
+
+class TagsGetter : public QObject, public PlaylistNotifier
+{
+Q_OBJECT
+public:
+ TagsGetter();
+ void associate(Tags *t);
+ // if returns true, I'm deleted
+ bool unassociate(Tags *t);
+
+ int interval() const;
+ bool loadAuto() const;
+
+public: //playlistnotifier
+ virtual void added(PlaylistItem &);
+ virtual void removed(PlaylistItem &);
+
+protected:
+ void timerEvent(QTimerEvent *);
+
+private slots:
+ // select the songs that need updating
+ void getSongs();
+
+ void newSong();
+
+public slots:
+ void setInterval(int ms);
+ void setLoadAuto(bool);
+
+private:
+ void sortPriority();
+
+private:
+ QPtrList<Tags> tags;
+ QValueList<PlaylistItem> items;
+};
+
+class Control : public CModule
+{
+Q_OBJECT
+public:
+ Control(TagsGetter* parent);
+};
+
+
+#endif
+
diff --git a/noatun/library/noatunui.cpp b/noatun/library/noatunui.cpp
new file mode 100644
index 00000000..9dbb1ff2
--- /dev/null
+++ b/noatun/library/noatunui.cpp
@@ -0,0 +1,12 @@
+#include "plugin.h"
+
+UserInterface::UserInterface()
+ : Plugin()
+{
+}
+
+UserInterface::~UserInterface()
+{
+
+}
+
diff --git a/noatun/library/player.cpp b/noatun/library/player.cpp
new file mode 100644
index 00000000..7760d9a8
--- /dev/null
+++ b/noatun/library/player.cpp
@@ -0,0 +1,379 @@
+#include "player.h"
+
+#include <noatun/playlist.h>
+#include "engine.h"
+#include "app.h"
+#include "titleproxy.h"
+
+#include <klibloader.h>
+#include <knotifyclient.h>
+#include <klocale.h>
+#include <qfile.h>
+
+enum ArtsPOS { posIdle=0, posPlaying, posPaused };
+
+
+
+Player::Player(QObject *parent) : QObject(parent, "Player"),
+ position(-1), mLoopStyle(None), firstTimeout(true)
+{
+ mEngine=new Engine;
+ connect(&filePos, SIGNAL(timeout()), SLOT(posTimeout()));
+ connect(mEngine, SIGNAL(aboutToPlay()), this, SLOT(aboutToPlay()));
+ connect(mEngine,
+ SIGNAL(receivedStreamMeta(const QString &, const QString &,
+ const QString &, const QString &,
+ const QString &, const QString &)),
+ this, SLOT(
+ slotUpdateStreamMeta(const QString &, const QString &,
+ const QString &, const QString &,
+ const QString &, const QString &))
+ );
+ connect(mEngine, SIGNAL(playingFailed()), this, SLOT(forward()));
+
+ handleButtons();
+}
+
+Player::~Player()
+{
+ delete mEngine;
+}
+
+bool Player::isPlaying()
+{
+ return mEngine->state()==posPlaying;
+}
+
+bool Player::isPaused()
+{
+ return mEngine->state()==posPaused;
+}
+
+bool Player::isStopped()
+{
+ return mEngine->state()==posIdle;
+}
+
+void Player::toggleListView()
+{
+ napp->playlist()->toggleList();
+}
+
+void Player::handleButtons()
+{
+ switch (mEngine->state())
+ {
+ case (posPlaying):
+ emit playing();
+ break;
+ case (posPaused):
+ emit paused();
+ break;
+ case (posIdle):
+ emit stopped();
+ }
+}
+
+void Player::back()
+{
+ if (napp->playlist()->previous())
+ {
+ stop();
+ play();
+ }
+}
+
+void Player::stop()
+{
+ filePos.stop();
+ position=0;
+ mEngine->stop();
+ emit stopped();
+ mCurrent=0;
+}
+
+void Player::play()
+{
+ napp->processEvents();
+ bool work=false;
+ firstTimeout=true;
+
+ if (mEngine->state()==posPlaying) // do nothing if already playing
+ return;
+
+ if (mEngine->state()==posPaused)
+ {
+ work=mEngine->play();
+ }
+ else
+ {
+ stop();
+ mCurrent = napp->playlist()->current();
+ if (!mCurrent)
+ {
+ work=false;
+ }
+ else
+ {
+ mEngine->blockSignals(true);
+ work=mEngine->open(mCurrent);
+ mEngine->blockSignals(false);
+ }
+ }
+
+ if (!work)
+ {
+ forward(false);
+ }
+ else
+ {
+ filePos.start(500);
+ emit changed();
+ mEngine->play();
+ }
+
+ handleButtons();
+}
+
+void Player::play(const PlaylistItem &item)
+{
+ napp->playlist()->setCurrent(item);
+}
+
+void Player::playpause()
+{
+ if (mEngine->state()==posPlaying)
+ {
+ filePos.stop();
+ mEngine->pause();
+// emit paused(); NOT necessary because emitted in handleButtons() (mETz)
+ handleButtons();
+ }
+ else
+ play();
+}
+
+void Player::forward(bool allowLoop)
+{
+ stop();
+ if (napp->playlist()->next())
+ play();
+ else if (allowLoop && napp->loopList())
+ if (napp->playlist()->reset(), napp->playlist()->current())
+ play();
+}
+
+void Player::skipTo(int msec) // skip to a certain time in the track
+{
+ if( (current()) && (msec>=0) )
+ {
+ mEngine->seek(msec);
+ position = mEngine->position(); // make sure position is recent
+ emit timeout(); // update the UI
+ emit skipped(msec);
+ emit skipped();
+ }
+}
+
+void Player::playCurrent()
+{
+ if (!mEngine->initialized()) return;
+ stop();
+ mCurrent=0;
+ if (napp->playlist()->current())
+ play();
+}
+
+void Player::newCurrent()
+{
+ // the second half of the following
+ if (!napp->playlist() || !mEngine->initialized())
+ return; // no playlist, or squelch playing as an optimization
+ if ((mEngine->state()!=posPlaying) && napp->autoPlay())
+ playCurrent();
+}
+
+void Player::posTimeout()
+{
+ if (mEngine->state()==posIdle)
+ {
+ stop();
+ handleButtons();
+ // If you're supposed to loop the song, don't go next
+ // otherwise, do go next
+ if (loopStyle()==Song || napp->playlist()->next())
+ play();
+ else if (loopStyle()==Playlist)
+ {
+ napp->playlist()->reset();
+ play();
+ }
+ else if (napp->loopList())
+ napp->playlist()->reset();
+
+ return;
+ }
+ position = mEngine->position();
+
+ if (current())
+ {
+ current().setLength(mEngine->length());
+ if (current().length() && firstTimeout)
+ {
+ int minutes = (int) ( current().length() / 60 );
+ int seconds = current().length() - minutes * 60;
+ emit newSongLen ( minutes, seconds );
+ firstTimeout = false;
+ emit newSong();
+ }
+ }
+
+ emit timeout();
+}
+
+QString Player::lengthString(int _position)
+{
+ if (!current())
+ return QString("--:--/--:--");
+
+ QString posString;
+ QString lenString;
+ int secs, seconds, minutes;
+
+ if (_position < 0)
+ _position = position;
+
+ if (_position<0)
+ {
+ posString="--:--/";
+ }
+ else
+ { // get the position
+ bool remain = napp->displayRemaining() && current();
+ if (remain && current().length()<0)
+ {
+ remain = false;
+ }
+
+ if (remain)
+ {
+ _position = current().length() - _position;
+ }
+
+ secs = _position / 1000; // convert milliseconds -> seconds
+ seconds = secs % 60;
+ minutes = (secs - seconds) / 60;
+
+ // the string has to look like '00:00/00:00'
+ posString.sprintf("%.2d:%.2d/", minutes, seconds);
+ if (remain) posString.prepend('-');
+ }
+
+ if (!current() || current().length()<0)
+ {
+ posString+="--:--";
+ }
+ else
+ { // get the length
+ secs = current().length() / 1000; // convert milliseconds -> seconds
+ seconds = secs % 60;
+ minutes = (secs - seconds) / 60;
+
+ // the string has to look like '00:00/00:00'
+ lenString.sprintf("%.2d:%.2d", minutes, seconds);
+ posString += lenString;
+ }
+
+ return posString;
+}
+
+int Player::getLength()
+{
+ if (!current())
+ return -1;
+ return current().length(); // return track-length in milliseconds
+}
+
+void Player::openFile(const KURL &f, bool purge, bool autoplay)
+{
+ if (purge)
+ napp->playlist()->clear();
+ napp->playlist()->addFile(f, autoplay);
+}
+
+void Player::openFile(const KURL::List &f, bool purge, bool autoplay)
+{
+ if (purge)
+ napp->playlist()->clear();
+ for (KURL::List::ConstIterator i(f.begin()); i != f.end(); ++i)
+ {
+ napp->playlist()->addFile(*i, autoplay);
+ autoplay=false;
+ }
+}
+
+
+void Player::loop()
+{
+ mLoopStyle++;
+ if (mLoopStyle>Random)
+ mLoopStyle=None;
+ emit loopTypeChange(mLoopStyle);
+}
+void Player::loop(int i)
+{
+ mLoopStyle=i;
+ emit loopTypeChange(mLoopStyle);
+}
+
+void Player::removeCurrent()
+{
+ if (napp->playlist()->current())
+ napp->playlist()->current().remove();
+}
+
+void Player::setVolume(int v)
+{
+ if (v<0) v=0;
+ if (v>100) v=100;
+ mEngine->setVolume(v);
+ emit timeout();
+ emit volumeChanged(v);
+}
+
+int Player::volume() const
+{
+ return mEngine->volume();
+}
+
+void Player::aboutToPlay()
+{
+ emit aboutToOpen( mCurrent );
+}
+
+void Player::slotUpdateStreamMeta(
+ const QString &streamName, const QString &streamGenre,
+ const QString &streamUrl, const QString &streamBitrate,
+ const QString &trackTitle, const QString &trackUrl)
+{
+ PlaylistItem currentItem = napp->playlist()->current();
+ if(currentItem)
+ {
+ currentItem.setProperty("title", trackTitle);
+ currentItem.setProperty("bitrate", streamBitrate);
+
+ if(!streamName.isEmpty())
+ currentItem.setProperty("author", streamName);
+ if(!streamGenre.isEmpty())
+ currentItem.setProperty("genre", streamGenre);
+ if(!trackUrl.isEmpty())
+ currentItem.setProperty("comment", trackUrl);
+ else if(!streamUrl.isEmpty())
+ currentItem.setProperty("comment", streamUrl);
+ else
+ currentItem.clearProperty("comment");
+ emit changed();
+ }
+}
+
+#include "player.moc"
diff --git a/noatun/library/playlist.cpp b/noatun/library/playlist.cpp
new file mode 100644
index 00000000..4e729924
--- /dev/null
+++ b/noatun/library/playlist.cpp
@@ -0,0 +1,442 @@
+#include <noatun/playlist.h>
+#include <noatun/app.h>
+#include <noatun/player.h>
+#include <noatun/downloader.h>
+#include <noatun/pluginloader.h>
+
+#include <kcmdlineargs.h>
+#include <kfile.h>
+#include <kmimetype.h>
+#include <qregexp.h>
+#include <assert.h>
+#include <qmetaobject.h>
+#include <kmedia2.h>
+#include <vector>
+
+PlaylistItemData::PlaylistItemData()
+{
+ mRefs=0;
+}
+
+PlaylistItemData::~PlaylistItemData()
+{
+}
+
+QCString PlaylistItemData::mimetype() const
+{
+ if (isProperty("mimetype"))
+ return property("mimetype").latin1();
+ KMimeType::Ptr mimetype = KMimeType::findByURL(url());
+
+ return mimetype->name().latin1();
+}
+
+QCString PlaylistItemData::playObject() const
+{
+ if (isProperty("playObject"))
+ return property("playObject").latin1();
+
+ std::string objectType;
+
+ Arts::TraderQuery query;
+ query.supports("Interface","Arts::PlayObject");
+ query.supports("MimeType", std::string(mimetype()));
+
+ std::vector<Arts::TraderOffer> *offers = query.query();
+ if (!offers) return "";
+
+ if(!offers->empty())
+ objectType = offers->front().interfaceName();
+
+ delete offers;
+
+ return QCString(objectType.c_str());
+}
+
+QString PlaylistItemData::title() const
+{
+ if (isProperty("realtitle"))
+ return property("realtitle");
+
+
+ // "$(property)"
+ QString format=napp->titleFormat();
+
+ QRegExp find("(?:(?:\\\\\\\\))*\\$\\((.*)");
+
+ int start=0;
+ while (start != -1)
+ {
+ start = find.search(format, start);
+ if (start == -1) break;
+
+ // test if there's an odd amount of backslashes
+ if (start>0 && format[start-1]=='\\')
+ {
+ // yes, so half the amount of backslashes
+
+ // count how many there are first
+ QRegExp counter("([\\\\]+)");
+ counter.search(format, start-1);
+ uint len=counter.cap(1).length()-1;
+
+ // and half them, and remove one more
+ format.replace(start-1, len/2+1, "");
+ start=start-1+len/2+find.cap(1).length()+3;
+ continue;
+ }
+
+ // now replace the backslashes with half as many
+
+ if (format[start]=='\\')
+ {
+ // count how many there are first
+ QRegExp counter("([\\\\]+)");
+ counter.search(format, start);
+ uint len=counter.cap(1).length();
+
+ // and half them
+ format.replace(start, len/2, "");
+ start=start+len/2;
+ }
+
+ // "sth"foo"sth"
+ QString cont(find.cap(1));
+ QString prefix,suffix,propname;
+ unsigned int i=0;
+ if (cont[i] == '"')
+ {
+ i++;
+ for (; i < cont.length(); i++)
+ {
+ if (cont[i] != '"')
+ prefix += cont[i];
+ else
+ break;
+ }
+ i++;
+ }
+
+
+ for (; i < cont.length(); ++i)
+ {
+ if (cont[i]!='"' && cont[i]!=')')
+ propname += cont[i];
+ else
+ break;
+ }
+
+ if (cont[i] == '"')
+ {
+ i++;
+ for (; i < cont.length(); i++)
+ {
+ if (cont[i] != '"')
+ suffix += cont[i];
+ else
+ break;
+ }
+ i++;
+ }
+ i++;
+
+
+ QString propval = property(propname);
+ if (propname == "title" && !propval.length())
+ {
+ propval = url().filename();
+ }
+ else if (propname == "comment")
+ {
+ // comments can contain newlines
+ // these are not wanted in a formatted title
+ propval.replace('\n', ' ');
+ }
+
+ if (propval.length())
+ {
+ propval = prefix+propval+suffix;
+ format.replace(start, i+2, propval);
+ start += propval.length();
+ }
+ else
+ {
+ format.replace(start, i+2, "");
+ }
+ }
+ return format;
+
+}
+
+int PlaylistItemData::length() const
+{
+ return property("length", "-1").toInt();
+}
+
+void PlaylistItemData::setLength(int ms)
+{
+ setProperty("length", QString::number(ms));
+}
+
+PlaylistItem::PlaylistItem(const PlaylistItem &source)
+{
+ mData=source.mData;
+ addRef();
+}
+
+PlaylistItem::PlaylistItem(PlaylistItemData *source)
+{
+ mData=source;
+ addRef();
+}
+
+PlaylistItem::~PlaylistItem()
+{
+ removeRef();
+}
+
+PlaylistItem &PlaylistItem::operator =(const PlaylistItem &source)
+{
+ if (source)
+ source.addRef();
+ removeRef();
+ mData=source.mData;
+ return *this;
+}
+
+PlaylistItem &PlaylistItem::operator =(PlaylistItemData *source)
+{
+ if (source)
+ source->addRef();
+ removeRef();
+ mData=source;
+ return *this;
+}
+
+const PlaylistItem &PlaylistItem::operator =(const PlaylistItem &source) const
+{
+ if (source)
+ source.addRef();
+ removeRef();
+ mData=source.mData;
+ return *this;
+}
+
+const PlaylistItem &PlaylistItem::operator =(const PlaylistItemData *source) const
+{
+ if (source)
+ const_cast<PlaylistItemData*>(source)->addRef();
+ removeRef();
+ mData=const_cast<PlaylistItemData*>(source);
+ return *this;
+}
+
+// reference counting
+void PlaylistItem::removeRef() const
+{
+ if (mData)
+ {
+ mData->removeRef();
+ }
+}
+
+void PlaylistItem::addRef() const
+{
+ if (mData)
+ mData->addRef();
+}
+
+QString PlaylistItemData::lengthString() const
+{
+ if ( length() == -1 ) // no file loaded
+ return QString("--:--");
+
+ int secs = length()/1000; // convert milliseconds -> seconds
+ int seconds = secs % 60;
+ return QString().sprintf("%.2d:%.2d", ((secs-seconds)/60), seconds);
+}
+
+
+bool PlaylistItemData::operator == (const PlaylistItemData &d) const
+{
+ return &d==this;
+}
+
+bool PlaylistItemData::operator != (const PlaylistItemData &d) const
+{
+ return ! (*this==d);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+Playlist::Playlist(QObject *parent, const char *name) : QObject(parent, name)
+{
+ napp->player()->connect(this, SIGNAL(playCurrent()), SLOT(playCurrent()));
+ napp->player()->connect(this, SIGNAL(listHidden()), SIGNAL(playlistHidden()));
+ napp->player()->connect(this, SIGNAL(listShown()), SIGNAL(playlistShown()));
+
+}
+
+Playlist::~Playlist()
+{
+
+}
+
+void Playlist::toggleList()
+{
+ if (listVisible())
+ hideList();
+ else
+ showList();
+}
+
+int Playlist::handleArguments()
+{
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ int i;
+ bool play= napp->autoPlay();
+
+ for (i=0; i < args->count(); i++)
+ {
+ KURL u(args->url(i));
+ if (u.isValid())
+ addFile(u, play);
+ play=false;
+ }
+ args->clear();
+ return i;
+}
+
+QValueList<PlaylistItem> Playlist::select(
+ const QStringList &keys, const QStringList &values,
+ int limit, bool exact, bool caseSensitive
+ )
+{
+ QValueList<PlaylistItem> list;
+ QString k;
+ QString v;
+ QStringList::ConstIterator key, val;
+ for (PlaylistItem i(getFirst()); i && limit; i=getAfter(i))
+ {
+ for (key = keys.begin(); key != keys.end() && limit ; ++key)
+ {
+ k=*key;
+ v=i.property(k);
+
+ for (val = values.begin(); val != values.end() && limit; ++val)
+ {
+ if ((*val).length()==0 && v.length()==0)
+ {
+ list.append(i);
+ limit--;
+ goto nextSong;
+ }
+ else if (exact)
+ {
+ if (
+ (!caseSensitive && (*val).lower()==v.lower())
+ ||
+ (caseSensitive && (*val)==v)
+ )
+
+ {
+ list.append(i);
+ limit--;
+ goto nextSong;
+ }
+ }
+ else
+ {
+ if ((*val).find(v, 0, caseSensitive)!=-1)
+ {
+ list.append(i);
+ limit--;
+ goto nextSong;
+ }
+ }
+ }
+ }
+ nextSong:
+ ;
+ }
+ return list;
+}
+
+QValueList<PlaylistItem> Playlist::select(
+ const QString &key, const QString &value,
+ int limit, bool exact, bool caseSensitive
+ )
+{
+ QStringList keys(key);
+ QStringList values(value);
+ return select(keys, values, limit, exact, caseSensitive);
+}
+
+void PlaylistItemData::added()
+{
+ PlaylistItem item(this);
+ for (
+ PlaylistNotifier *i=napp->player()->mNotifiers.first();
+ i; i=napp->player()->mNotifiers.next())
+ {
+ i->added(item);
+ }
+}
+
+void PlaylistItemData::removed()
+{
+ PlaylistItem item(this);
+ for (
+ PlaylistNotifier *i=napp->player()->mNotifiers.first();
+ i; i=napp->player()->mNotifiers.next())
+ {
+ i->removed(item);
+ }
+}
+
+void PlaylistItemData::modified()
+{
+ PlaylistItem item(this);
+ for (
+ PlaylistNotifier *i=napp->player()->mNotifiers.first();
+ i; i=napp->player()->mNotifiers.next())
+ {
+ i->modified(item);
+ }
+}
+
+
+PlaylistItem Playlist::nextSection()
+{
+ return next();
+}
+
+PlaylistItem Playlist::previousSection()
+{
+ return previous();
+}
+
+
+PlaylistNotifier::PlaylistNotifier()
+{
+ napp->player()->mNotifiers.append(this);
+}
+
+PlaylistNotifier::~PlaylistNotifier()
+{
+ napp->player()->mNotifiers.removeRef(this);
+}
+
+
+
+#include "playlist.moc"
+
diff --git a/noatun/library/playlistsaver.cpp b/noatun/library/playlistsaver.cpp
new file mode 100644
index 00000000..58f5d5d2
--- /dev/null
+++ b/noatun/library/playlistsaver.cpp
@@ -0,0 +1,771 @@
+#include <noatun/playlistsaver.h>
+#include <qdom.h>
+#include <kio/netaccess.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include <noatun/app.h>
+#include "ksaver.h"
+#include <ksimpleconfig.h>
+#include <kmimetype.h>
+#include <klocale.h>
+#include <qregexp.h>
+#include <qxml.h>
+#include <kdebug.h>
+
+PlaylistSaver::PlaylistSaver()
+{
+}
+
+PlaylistSaver::~PlaylistSaver()
+{
+}
+
+bool PlaylistSaver::save(const KURL &file, int opt)
+{
+// kdDebug(66666) << k_funcinfo << "opt=" << opt << endl;
+
+ if(file.isEmpty() || !file.isValid())
+ return false;
+
+ switch (opt)
+ {
+ default:
+ case 0:
+ case XMLPlaylist:
+ return saveXML(file, opt);
+
+ case M3U:
+ case EXTM3U:
+ return saveM3U(file, opt);
+
+ case PLS:
+ return savePLS(file, opt);
+
+ case ASX:
+ return false; // No, I won't code that! [mETz]
+ }
+}
+
+bool PlaylistSaver::load(const KURL &file, int opt)
+{
+// kdDebug(66666) << k_funcinfo << "opt=" << opt << endl;
+
+ switch (opt)
+ {
+ default:
+ case 0:
+ case XMLPlaylist:
+ case ASX:
+ return loadXML(file, opt);
+
+ case M3U:
+ case EXTM3U:
+ return loadM3U(file, opt);
+
+ case PLS:
+ return loadPLS(file, opt);
+ }
+}
+
+bool PlaylistSaver::metalist(const KURL &url)
+{
+ kdDebug(66666) << k_funcinfo << "url=" << url.url() << endl;
+
+ QString end=url.filename().right(3).lower();
+ /*
+ if (end=="mp3" || end=="ogg") // we want to download streams only
+ {
+ kdDebug(66666) << k_funcinfo << "I can only load playlists" << endl;
+ return false;
+ }
+ */
+
+/*
+.wax audio/x-ms-wax Metafiles that reference Windows Media files with the
+ .asf, .wma or .wax file extensions.
+
+.wvx video/x-ms-wvx Metafiles that reference Windows Media files with the
+ .wma, .wmv, .wvx or .wax file extensions.
+
+.asx video/x-ms-asf Metafiles that reference Windows Media files with the
+ .wma, .wax, .wmv, .wvx, .asf, or .asx file extensions.
+*/
+
+ // it's actually a stream!
+ if (end!="pls" &&
+ end!="m3u" &&
+ end!="wax" && // windows mediaplayer metafile
+ end!="wvx" && // windows mediaplayer metafile
+ end!="asx" && // windows mediaplayer metafile
+ url.protocol().lower()=="http")
+ {
+ KMimeType::Ptr mimetype = KMimeType::findByURL(url);
+ QString type=mimetype->name();
+
+ if (type!="application/octet-stream")
+ return false;
+
+ QMap<QString,QString> map;
+ map["playObject"]="Arts::StreamPlayObject";
+ map["title"] = i18n("Stream from %1").arg(url.host());
+
+ KURL u(url);
+ if (!u.hasPath())
+ u.setPath("/");
+
+ map["stream_"] = map["url"] = u.url();
+
+ reset();
+ readItem(map);
+ return true;
+ }
+
+ // it is a pls, m3u or ms-media-player file by now
+ if(loadXML(url, XMLPlaylist))
+ return true;
+
+ if(loadXML(url,ASX))
+ return true;
+
+ if(loadPLS(url))
+ return true;
+
+ if(loadM3U(url))
+ return true;
+
+ return false;
+}
+
+bool PlaylistSaver::saveXML(const KURL &file, int )
+{
+ QString localFile;
+ if (file.isLocalFile())
+ localFile = QFile::encodeName(file.path());
+ else
+ localFile = napp->tempSaveName(file.path());
+
+ // QDom is a pain :)
+ QDomDocument doc("playlist");
+ doc.setContent(QString("<!DOCTYPE XMLPlaylist><playlist version=\"1.0\" client=\"noatun\"/>"));
+
+ QDomElement docElem=doc.documentElement();
+
+ reset();
+ PlaylistItem i;
+ QStringList props;
+ while ((i=writeItem()))
+ {
+ // write all properties
+ props=i.properties();
+ QDomElement elem=doc.createElement("item");
+ for (QStringList::Iterator pi(props.begin()); pi!=props.end(); ++pi)
+ {
+ QString val=i.property(*pi);
+ elem.setAttribute(*pi, val);
+
+ if ((*pi)=="url")
+ {
+ KURL u(val);
+ if (u.isLocalFile())
+ {
+ elem.setAttribute("local", u.path());
+ }
+ }
+ }
+
+ docElem.appendChild(elem);
+ props.clear();
+ }
+ Noatun::KSaver saver(localFile);
+ if (!saver.open())
+ return false;
+ saver.textStream().setEncoding(QTextStream::UnicodeUTF8);
+ saver.textStream() << doc.toString();
+ saver.close();
+
+ return true;
+}
+
+class NoatunXMLStructure : public QXmlDefaultHandler
+{
+public:
+ PlaylistSaver *saver;
+ bool fresh;
+
+ NoatunXMLStructure(PlaylistSaver *s)
+ : saver(s), fresh(true)
+ {
+ }
+
+ bool startElement(
+ const QString&, const QString &,
+ const QString &name, const QXmlAttributes &a
+ )
+ {
+ if (fresh)
+ {
+ if (name=="playlist")
+ {
+ fresh=false;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ if (name != "item")
+ return true;
+
+ QMap<QString,QString> propMap;
+
+ for (int i=0; i<a.count(); i++)
+ {
+ propMap[a.qName(i)] = a.value(i);
+ }
+ saver->readItem(propMap);
+
+ return true;
+ }
+};
+
+
+class MSASXStructure : public QXmlDefaultHandler
+{
+public:
+ PlaylistSaver *saver;
+ bool fresh;
+ bool inEntry, inTitle;
+ QMap<QString,QString> propMap;
+ QString mAbsPath;
+
+ MSASXStructure(PlaylistSaver *s, const QString &absPath)
+ : saver(s), fresh(true), inEntry(false),
+ inTitle(false), mAbsPath(absPath)
+ {
+ //kdDebug(66666) << k_funcinfo << endl;
+ }
+
+ bool startElement(const QString&, const QString &,
+ const QString &name, const QXmlAttributes &a)
+ {
+ if (fresh)
+ {
+ if (name.lower()=="asx")
+ {
+ //kdDebug(66666) << "found ASX format" << endl;
+ fresh=false;
+ return true;
+ }
+ else
+ {
+ kdDebug(66666) << "This is NOT an ASX style playlist!" << endl;
+ return false;
+ }
+ }
+
+ if (name.lower()=="entry")
+ {
+ if(inEntry) // WHOOPS, we are already in an entry, this should NEVER happen
+ {
+ kdDebug(66666) << "STOP, ENTRY INSIDE ENTRY!" << endl;
+ return false;
+ }
+// kdDebug(66666) << "<ENTRY> =====================" << endl;
+ inEntry=true;
+ }
+ else
+ {
+ if (inEntry) // inside entry block
+ {
+ // known stuff inside an <entry> ... </entry> block:
+ // <title>blah</title>
+ // <param album="blub" />
+ // <param artist="blah" />
+ // <ref HREF="file:/something" />
+ if(name.lower()=="ref")
+ {
+ for (int i=0; i<a.count(); i++)
+ {
+ if(a.qName(i).lower()=="href")
+ {
+ QString filename=a.value(i);
+ if (filename.find(QRegExp("^[a-zA-Z0-9]+:/"))==0)
+ {
+ KURL url(filename);
+ KMimeType::Ptr mimetype = KMimeType::findByURL(url);
+ QString type=mimetype->name();
+ if (type != "application/octet-stream")
+ {
+ propMap["url"]=filename;
+ }
+ else
+ {
+ propMap["playObject"]="SplayPlayObject";
+ propMap["title"] = i18n("Stream from %1").arg(url.host());
+ if (!url.hasPath())
+ url.setPath("/");
+ propMap["url"] = url.url();
+ propMap["stream_"]=propMap["url"];
+// readItem(propMap);
+// continue;
+ }
+ }
+ else
+ {
+ KURL u1;
+ // we have to deal with a relative path
+ if (filename.find('/'))
+ {
+ u1.setPath(mAbsPath); //FIXME: how to get the path in this place?
+ u1.setFileName(filename);
+ }
+ else
+ {
+ u1.setPath(filename);
+ }
+ propMap["url"]=u1.url();
+ }
+// kdDebug(66666) << "adding property url, value='" << propMap["url"] << "'" << endl;
+ }
+ }
+ }
+ else if(name.lower()=="param")
+ {
+ QString keyName="", keyValue="";
+
+ for (int i=0; i<a.count(); i++)
+ {
+ if(a.value(i).lower()=="album")
+ keyName="album";
+ else if(a.value(i).lower()=="artist")
+ keyName="author";
+ else if(!keyName.isEmpty()) // successfully found a key, the next key=value pair has to be the value
+ {
+// kdDebug(66666) << "keyName=" << keyName << ", next value is '" << a.value(i) << "'" << endl;
+ keyValue=a.value(i);
+ }
+ }
+
+ if (!keyName.isEmpty() && !keyValue.isEmpty())
+ {
+// kdDebug(66666) << "adding property; key='" << keyName << "', value='" << keyValue << "'" << endl;
+ propMap[keyName]=keyValue;
+ }
+ }
+ else if(name.lower()=="title")
+ {
+ if(inTitle) // WHOOPS, we are already in an entry, this should NEVER happen
+ {
+ kdDebug(66666) << "STOP, TITLE INSIDE TITLE!" << endl;
+ return false;
+ }
+// kdDebug(66666) << "<TITLE> ======" << endl;
+ inTitle=true;
+ }
+/* else
+ {
+ kdDebug(66666) << "Unknown/unused element inside ENTRY block, NAME='" << name << "'" << endl;
+ for (int i=0; i<a.count(); i++)
+ kdDebug(66666) << " | " << a.qName(i) << " = '" << a.value(i) << "'" << endl;
+ }*/
+ }
+/* else
+ {
+ kdDebug(66666) << "Uninteresting element, NAME='" << name << "'" << endl;
+ for (int i=0; i<a.count(); i++)
+ kdDebug(66666) << " | " << a.qName(i) << " = '" << a.value(i) << "'" << endl;
+ }*/
+ }
+
+ return true;
+ }
+
+ bool endElement(const QString&,const QString&, const QString& name)
+ {
+// kdDebug(66666) << k_funcinfo << "name='" << name << "'" << endl;
+ if (name.lower()=="entry")
+ {
+ if(inEntry)
+ {
+/* kdDebug(66666) << "</ENTRY> =====================" << endl;
+ for (QMap<QString,QString>::ConstIterator it=propMap.begin(); it!=propMap.end(); ++it )
+ kdDebug(66666) << "key='" << it.key() << "', val='" << it.data() << "'" << endl;
+*/
+ saver->readItem(propMap);
+ propMap.clear();
+ inEntry=false;
+ }
+ else // found </entry> without a start
+ {
+ kdDebug(66666) << "STOP, </ENTRY> without a start" << endl;
+ return false;
+ }
+ }
+ else if (name.lower()=="title")
+ {
+ if(inTitle && inEntry)
+ {
+// kdDebug(66666) << "</TITLE> ======" << endl;
+ inTitle=false;
+ }
+ else if (inTitle) // found </title> without a start or not inside an <entry> ... </entry>
+ {
+ kdDebug(66666) << "STOP, </TITLE> without a start" << endl;
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool characters(const QString &ch)
+ {
+ if(inTitle)
+ {
+ if (!ch.isEmpty())
+ {
+ propMap["title"]=ch;
+// kdDebug(66666) << "adding property; key='title', value='" << ch << "'" << endl;
+ }
+ }
+ return true;
+ }
+};
+
+
+bool PlaylistSaver::loadXML(const KURL &url, int opt)
+{
+ kdDebug(66666) << k_funcinfo <<
+ "file='" << url.url() << "', opt=" << opt << endl;
+
+ QString dest;
+ if(KIO::NetAccess::download(url, dest, 0L))
+ {
+ QFile file(dest);
+ if (!file.open(IO_ReadOnly))
+ return false;
+
+ reset();
+
+ // QXml is horribly documented
+ QXmlInputSource source(&file);
+ QXmlSimpleReader reader;
+
+ if (opt == ASX ||
+ url.path().right(4).lower()==".wax" ||
+ url.path().right(4).lower()==".asx" ||
+ url.path().right(4).lower()==".wvx")
+ {
+ MSASXStructure ASXparser(this, url.path(0));
+ reader.setContentHandler(&ASXparser);
+ reader.parse(source);
+ return !ASXparser.fresh;
+ }
+ else
+ {
+ NoatunXMLStructure parser(this);
+ reader.setContentHandler(&parser);
+ reader.parse(source);
+ return !parser.fresh;
+ }
+ } // END download()
+
+ return false;
+}
+
+bool PlaylistSaver::loadM3U(const KURL &file, int /*opt*/)
+{
+ kdDebug(66666) << k_funcinfo << "file='" << file.path() << endl;
+
+ QString localFile;
+ if(!KIO::NetAccess::download(file, localFile, 0L))
+ return false;
+
+ // if it's a PLS, transfer control, again (KIO bug?)
+#if 0
+ {
+ KSimpleConfig list(local, true);
+ list.setGroup("playlist");
+
+ // some stupid Windows lusers like to be case insensitive
+ QStringList groups=list.groupList().grep(QRegExp("^playlist$", false));
+ if (groups.count())
+ {
+ KURL l;
+ l.setPath(local);
+ return loadPLS(l);
+ }
+ }
+#endif
+
+ QFile saver(localFile);
+ saver.open(IO_ReadOnly);
+ QTextStream t(&saver);
+
+ bool isExt = false; // flag telling if we load an EXTM3U file
+ QString filename;
+ QString extinf;
+ QMap<QString,QString> prop;
+ reset();
+
+ while (!t.eof())
+ {
+ if (isExt)
+ {
+ extinf = t.readLine();
+ if (!extinf.startsWith("#EXTINF:"))
+ {
+// kdDebug(66666) << "EXTM3U extinf line != extinf, assuming it's a filename." << endl;
+ filename = extinf;
+ extinf="";
+ }
+ else
+ {
+ filename = t.readLine(); // read in second line containing the filename
+ }
+ //kdDebug(66666) << "EXTM3U filename = '" << filename << "'" << endl;
+ }
+ else // old style m3u
+ {
+ filename = t.readLine();
+ }
+
+ if (filename == "#EXTM3U") // on first line
+ {
+// kdDebug(66666) << "FOUND '#EXTM3U' @ " << saver.at() << "." << endl;
+ isExt=true;
+ continue; // skip parsing the first (i.e. this) line
+ }
+
+ if (filename.isEmpty())
+ continue;
+
+ if (filename.find(QRegExp("^[a-zA-Z0-9]+:/"))==0)
+ {
+ //kdDebug(66666) << k_funcinfo << "url filename = " << filename << endl;
+
+ KURL protourl(filename);
+ KMimeType::Ptr mimetype = KMimeType::findByURL(protourl);
+
+ if (mimetype->name() != "application/octet-stream")
+ {
+ prop["url"] = filename;
+ }
+ else
+ {
+ prop["playObject"]="SplayPlayObject";
+ // Default title, might be overwritten by #EXTINF later
+ prop["title"] = i18n("Stream from %1").arg(protourl.host());
+
+ if (!protourl.hasPath())
+ protourl.setPath("/");
+
+ prop["url"] = protourl.url();
+ prop["stream_"] = prop["url"];
+ }
+ }
+ else // filename that is not of URL style (i.e. NOT "proto:/path/somefile")
+ {
+ KURL u1;
+ // we have to deal with a relative path
+ if (filename.find('/'))
+ {
+ u1.setPath(file.path(0));
+ u1.setFileName(filename);
+ }
+ else
+ {
+ u1.setPath(filename);
+ }
+ prop["url"] = u1.url();
+ }
+
+ // parse line of the following format:
+ //#EXTINF:length,displayed_title
+ if (isExt)
+ {
+ extinf.remove(0,8); // remove "#EXTINF:"
+ //kdDebug(66666) << "EXTM3U extinf = '" << extinf << "'" << endl;
+ int timeTitleSep = extinf.find(',', 0);
+
+ int length = (extinf.left(timeTitleSep)).toInt();
+ if (length>0)
+ prop["length"]=QString::number(length*1000);
+
+ QString displayTitle=extinf.mid(timeTitleSep+1);
+ if (!displayTitle.isEmpty())
+ {
+ int artistTitleSep = displayTitle.find(" - ",0);
+ if (artistTitleSep == -1) // no "artist - title" like format, just set it as title
+ {
+ prop["title"] = displayTitle;
+ }
+ else
+ {
+ prop["author"] = displayTitle.left(artistTitleSep);
+ prop["title"] = displayTitle.mid(artistTitleSep+3);
+ }
+ /*kdDebug(66666) << "EXTM3U author/artist='" << prop["author"] <<
+ "', title='" << prop["title"] << "'" << endl;*/
+ } // END !displayTitle.isEmpty()
+ } // END if(isExt)
+
+// kdDebug(66666) << k_funcinfo << "adding file '" << prop["url"] << "' to playlist" << endl;
+ readItem(prop);
+ prop.clear();
+ } // END while()
+
+ KIO::NetAccess::removeTempFile(localFile);
+
+// kdDebug(66666) << k_funcinfo << "END" << endl;
+ return true;
+}
+
+bool PlaylistSaver::saveM3U(const KURL &file, int opt)
+{
+// kdDebug(66666) << k_funcinfo << "file='" << file.path() << "', opt=" << opt << endl;
+
+ bool isExt=(opt==EXTM3U); // easier ;)
+
+ QString local(napp->tempSaveName(file.path()));
+ QFile saver(local);
+ saver.open(IO_ReadWrite | IO_Truncate);
+ QTextStream t(&saver);
+
+ reset();
+ PlaylistItem i;
+ QStringList props;
+
+ // this is more code but otoh faster than checking for isExt inside the loop
+ if(isExt)
+ {
+ t << "#EXTM3U" << '\n';
+
+ while ((i=writeItem()))
+ {
+ int length = static_cast<int>(((i.property("length")).toInt())/1000);
+ if(length==0) length=-1; // special value in an EXTM3U file, means "unknown"
+ KURL u(i.property("url"));
+ QString title;
+
+ // if a playlistitem is without a tag or ONLY title is set
+ if((i.property("author").isEmpty() && i.property("title").isEmpty())
+ || (i.property("author").isEmpty() && !i.property("title").isEmpty()) )
+ title = u.filename().left(u.filename().length()-4);
+ else
+ title = i.property("author") + " - " + i.property("title");
+
+// kdDebug(66666) << "#EXTINF:"<< QString::number(length) << "," << title << endl;
+ t << "#EXTINF:"<< QString::number(length) << "," << title << '\n';
+
+ if (u.isLocalFile())
+ t << u.path() << '\n';
+ else
+ t << u.url() << '\n';
+ }
+ }
+ else
+ {
+ while ((i=writeItem()))
+ {
+ KURL u(i.property("url"));
+ if (u.isLocalFile())
+ t << u.path() << '\n';
+ else
+ t << u.url() << '\n';
+ }
+ }
+
+ saver.close();
+ KIO::NetAccess::upload(local, file, 0L);
+ saver.remove();
+ return true;
+}
+
+static QString findNoCase(const QMap<QString,QString> &map, const QString &key)
+{
+ for (QMap<QString,QString>::ConstIterator i=map.begin(); i!=map.end(); ++i)
+ {
+ if (i.key().lower() == key.lower())
+ return i.data();
+ }
+ return 0;
+}
+
+bool PlaylistSaver::loadPLS(const KURL &file, int /*opt*/)
+{
+ kdDebug(66666) << k_funcinfo << "file='" << file.path() << endl;
+
+ QString localFile;
+ if(!KIO::NetAccess::download(file, localFile, 0L))
+ return false;
+
+ QFile checkFile(localFile);
+ checkFile.open(IO_ReadOnly);
+ QTextStream t(&checkFile);
+ QString firstLine = t.readLine();
+ if(firstLine.lower() != "[playlist]")
+ {
+ kdDebug(66666) << k_funcinfo << "PLS didn't start with '[playlist]', aborting" << endl;
+ return false;
+ }
+
+
+ KSimpleConfig list(localFile, true);
+ //list.setGroup("playlist");
+
+ // some stupid Windows lusers like to be case insensitive
+ QStringList groups = list.groupList().grep(QRegExp("^playlist$", false));
+ /*
+ if (!groups.count()) // didn't find "[playlist]", it's not a .pls file
+ return false;
+ */
+
+ QMap<QString,QString> group = list.entryMap(groups[0]);
+
+ QString numOfEntries = findNoCase(group, "numberofentries");
+ if(numOfEntries.isEmpty())
+ return false;
+
+ reset();
+
+ unsigned int nEntries = numOfEntries.toInt();
+ for(unsigned int entry = 1; entry <= nEntries; ++entry )
+ {
+ QString str;
+ str.sprintf("file%d", entry);
+ QString cast = findNoCase(group, str.utf8());
+ str.sprintf("title%d", entry);
+ QString title = findNoCase(group, str.utf8());
+
+ // assume that everything in a pls is a streamable file
+ QMap<QString,QString> map;
+
+ KURL url(cast);
+ if (!url.hasPath())
+ url.setPath("/");
+
+ map["playObject"]="SplayPlayObject";
+ if (title.isEmpty())
+ map["title"] = i18n("Stream from %1 (port: %2)").arg( url.host() ).arg( url.port() );
+ else
+ map["title"] = i18n("Stream from %1, (ip: %2, port: %3)").arg( title ).arg( url.host() ).arg(url.port() );
+
+ map["url"] = map["stream_"]= url.url();
+
+ readItem(map);
+ }
+ return true;
+}
+
+bool PlaylistSaver::savePLS(const KURL &, int)
+{
+ return false;
+}
+
+void PlaylistSaver::setGroup(const QString &)
+{
+}
+
+
diff --git a/noatun/library/plugin.cpp b/noatun/library/plugin.cpp
new file mode 100644
index 00000000..e9090609
--- /dev/null
+++ b/noatun/library/plugin.cpp
@@ -0,0 +1,578 @@
+#include <qtimer.h>
+#include <qfile.h>
+#include <artsflow.h>
+#include <vector>
+#include <artsflow.h>
+#include <soundserver.h>
+#include <noatunarts.h>
+#include <dcopclient.h>
+#include <dispatcher.h>
+#include <kdebug.h>
+#include <kstandarddirs.h>
+
+#include <cmath>
+
+#include "plugin_deps.h"
+#include "player.h"
+#include "plugin.h"
+#include "pluginloader.h"
+#include "app.h"
+#include "engine.h"
+
+using std::vector;
+
+Arts::Dispatcher *Visualization::mDispatcher=0;
+bool Visualization::internalVis=false;
+
+Plugin::Plugin()
+{
+}
+
+Plugin::~Plugin()
+{
+}
+
+void Plugin::init()
+{
+}
+
+bool Plugin::unload()
+{
+ return napp->libraryLoader()->remove(this);
+}
+
+TimerThingy::TimerThingy(Visualization *vis)
+ : mVis(vis), id(-1)
+{
+}
+
+void TimerThingy::timerEvent(QTimerEvent *)
+{
+ mVis->timeout();
+}
+
+void TimerThingy::setInterval(int msec)
+{
+ ms=msec;
+ if (id!=-1)
+ {
+ killTimer(id);
+ id=startTimer(msec);
+ }
+}
+
+void TimerThingy::start()
+{
+ if (id==-1)
+ id=startTimer(ms);
+}
+
+void TimerThingy::stop()
+{
+ if (id==-1)
+ {
+ killTimer(id);
+ id=-1;
+ }
+}
+
+
+Visualization::Visualization(int timeout, int pid)
+{
+ mTimerThingy=new TimerThingy(this);
+ setInterval(timeout);
+
+ // if this is a fork, do a cutesy arts thingy to get a remote
+ // stack, otherwise, get it from localhost :)
+ {
+ int parent=pid ? pid : getppid();
+
+ if (getenv("NOATUN_PID"))
+ parent = QString::fromLatin1(getenv("NOATUN_PID")).toInt();
+
+ DCOPClient c;
+ c.attach();
+
+ QCString appids[2];
+ appids[0]=QString("noatun-%1").arg(parent).local8Bit();
+ appids[1]="noatun";
+ QCString &appid=appids[0];
+
+ if (!internalVis && c.isApplicationRegistered(appids[0]))
+ {
+ appid=appids[0];
+ }
+ else if (!internalVis && c.isApplicationRegistered(appids[1]))
+ {
+ appid=appids[1];
+ }
+ else
+ {
+ kdDebug(66666) << "Internal Vis" <<endl;
+ mVisualizationStack=napp->player()->engine()->visualizationStack()->toString().c_str();
+ mServer=new Arts::SoundServerV2(*(napp->player()->engine()->server()));
+ return;
+ }
+
+ {
+ QByteArray replyData;
+ QCString replyType;
+
+ if (!c.call(appid, "Noatun", "visStack()", QByteArray(), replyType, replyData))
+ {
+ kdDebug(66666) << "Error communicating to parent noatun" << endl;
+ }
+ else
+ {
+ initDispatcher();
+ mServer=new Arts::SoundServerV2;
+ *mServer = Arts::Reference("global:Arts_SoundServerV2");
+ QDataStream reply(replyData, IO_ReadOnly);
+ QCString result;
+ reply >> result;
+ mVisualizationStack=result;
+ }
+ }
+ }
+}
+
+Visualization::~Visualization()
+{
+// napp->player()->engine()->visualizationStack()->remove(mId);
+
+ delete mServer;
+ delete mTimerThingy;
+}
+
+void Visualization::start()
+{
+ mTimerThingy->start();
+}
+
+void Visualization::stop()
+{
+ mTimerThingy->stop();
+}
+
+void Visualization::setInterval(int msec)
+{
+ mTimeout=msec;
+ if (!msec)
+ stop();
+ mTimerThingy->setInterval(msec);
+}
+
+int Visualization::interval() const
+{
+ return mTimeout;
+}
+
+Noatun::StereoEffectStack Visualization::visualizationStack()
+{
+ return Arts::Reference(mVisualizationStack);
+}
+
+Arts::SoundServerV2 *Visualization::server()
+{
+ return mServer;
+}
+
+int Visualization::noatunPid()
+{
+ DCOPClient dcop;
+ dcop.attach();
+ QCStringList apps = dcop.registeredApplications();
+ for (QCStringList::Iterator i = apps.begin(); i != apps.end(); ++i )
+ if ((*i).left(9) != "anonymous" )
+ {
+ if ((*i).left(6) != "noatun")
+ continue;
+ int pid=(*i).mid((*i).find('-')+1).toInt();
+ return pid;
+ }
+ return -1;
+}
+
+bool Visualization::connected()
+{
+ server()->_interfaceName(); // makes error() work
+ return !(server()->error() || server()->isNull());
+}
+
+void Visualization::initDispatcher()
+{
+ if (!mDispatcher)
+ {
+ mDispatcher=new Arts::Dispatcher;
+ }
+}
+
+FFTScope::FFTScope(int interval, int pid) : Visualization(interval, pid)
+{
+}
+
+float FFTScope::magic(int bands)
+{
+/* QString path=locate("data", "noatun/magictable");
+ QFile magic(path);
+ if (!magic.open(IO_ReadOnly | IO_Raw))
+ return 0;
+ if (!magic.at(bands*sizeof(float)))
+ return 0;
+
+ float value;
+ if (magic.readBlock((char*)&value, sizeof(float))==-1)
+ value=0;
+*/
+ bands += 20-1; // index-1 from FFTScopes.cpp
+ float value = std::log(std::pow(2046.0, 1.0/bands));
+
+ return value;
+}
+
+
+StereoFFTScope::StereoFFTScope(int timeout, int pid) : FFTScope(timeout, pid)
+{
+ mScope=new Noatun::FFTScopeStereo;
+ *mScope=Arts::DynamicCast(server()->createObject("Noatun::FFTScopeStereo"));
+
+ if ((*mScope).isNull())
+ {
+ delete mScope;
+ mScope=0;
+ }
+ else
+ {
+ mScope->start();
+ mId=visualizationStack().insertBottom(*mScope, "Noatun FFT");
+ }
+}
+
+StereoFFTScope::~StereoFFTScope()
+{
+ if (mScope)
+ {
+ if (connected())
+ visualizationStack().remove(mId);
+ mScope->stop();
+ delete mScope;
+ }
+}
+
+void StereoFFTScope::scopeData(vector<float> *&left, vector<float> *&right)
+{
+ left=mScope->scopeLeft();
+ right=mScope->scopeRight();
+}
+
+void StereoFFTScope::timeout()
+{
+ vector<float> *left, *right;
+ scopeData(left, right);
+
+ if (left->size())
+ scopeEvent(&left->front(), &right->front(), left->size());
+ delete left;
+ delete right;
+
+}
+
+int StereoFFTScope::bands() const
+{
+ vector<float> *d=mScope->scopeLeft();
+ int size=d->size();
+ delete d;
+ return size;
+}
+
+void StereoFFTScope::setBands(float f)
+{
+ mScope->bandResolution(f);
+}
+
+
+
+MonoFFTScope::MonoFFTScope(int timeout, int pid) : FFTScope(timeout, pid)
+{
+ mScope=new Noatun::FFTScope;
+ *mScope=Arts::DynamicCast(server()->createObject("Noatun::FFTScope"));
+
+ if ((*mScope).isNull())
+ {
+ delete mScope;
+ mScope=0;
+ }
+ else
+ {
+ mScope->start();
+ mId=visualizationStack().insertBottom(*mScope, "Noatun FFT");
+ }
+}
+
+MonoFFTScope::~MonoFFTScope()
+{
+ if (mScope)
+ {
+ if (connected())
+ visualizationStack().remove(mId);
+ mScope->stop();
+ delete mScope;
+ }
+}
+
+void MonoFFTScope::scopeData(vector<float> *&data)
+{
+ data=mScope->scope();
+}
+
+void MonoFFTScope::timeout()
+{
+ vector<float> *data(mScope->scope());
+
+ float *f=&data->front();
+ if (data->size()) scopeEvent(f, data->size());
+ delete data;
+}
+
+int MonoFFTScope::bands() const
+{
+ vector<float> *d=mScope->scope();
+ int size=d->size();
+ delete d;
+ return size;
+
+}
+
+void MonoFFTScope::setBands(float f)
+{
+ mScope->bandResolution(f);
+}
+
+
+
+
+
+
+Scope::Scope(int interval, int pid) : Visualization(interval, pid)
+{
+}
+
+
+
+MonoScope::MonoScope(int timeout, int pid) : Scope(timeout, pid)
+{
+ mScope=new Noatun::RawScope;
+ *mScope=Arts::DynamicCast(server()->createObject("Noatun::RawScope"));
+
+ if ((*mScope).isNull())
+ {
+ delete mScope;
+ mScope=0;
+ }
+ else
+ {
+ mScope->start();
+ mId=visualizationStack().insertBottom(*mScope, "Noatun Scope");
+ }
+}
+
+MonoScope::~MonoScope()
+{
+ if (mScope)
+ {
+ if (connected())
+ visualizationStack().remove(mId);
+ mScope->stop();
+ delete mScope;
+ }
+}
+
+void MonoScope::scopeData(vector<float> *&data)
+{
+ data=mScope->scope();
+}
+
+void MonoScope::timeout()
+{
+ vector<float> *data(mScope->scope());
+
+ float *f=&data->front();
+ if (data->size()) scopeEvent(f, data->size());
+ delete data;
+}
+
+int MonoScope::samples() const
+{
+ return (long)mScope->buffer();
+}
+
+void MonoScope::setSamples(int len)
+{
+ mScope->buffer((long)len);
+}
+
+
+
+StereoScope::StereoScope(int timeout, int pid) : Scope(timeout, pid)
+{
+ mScope=new Noatun::RawScopeStereo;
+ *mScope=Arts::DynamicCast(server()->createObject("Noatun::RawScopeStereo"));
+
+ if ((*mScope).isNull())
+ {
+ delete mScope;
+ mScope=0;
+ }
+ else
+ {
+ mScope->start();
+ mId=visualizationStack().insertBottom(*mScope, "Noatun Stereo Scope");
+ }
+}
+
+StereoScope::~StereoScope()
+{
+ if (mScope)
+ {
+ if (connected())
+ visualizationStack().remove(mId);
+ mScope->stop();
+ delete mScope;
+ }
+}
+
+void StereoScope::scopeData(vector<float> *&left, vector<float> *&right)
+{
+ left=mScope->scopeLeft();
+ right=mScope->scopeRight();
+}
+
+void StereoScope::timeout()
+{
+ vector<float> *left(mScope->scopeLeft());
+ vector<float> *right(mScope->scopeRight());
+
+ float *l=&left->front();
+ float *r=&right->front();
+
+ if (left->size()==right->size() && left->size())
+ scopeEvent(l, r, left->size());
+ delete left;
+ delete right;
+}
+
+int StereoScope::samples() const
+{
+ return (long)mScope->buffer();
+}
+
+void StereoScope::setSamples(int len)
+{
+ mScope->buffer((long)len);
+}
+
+
+
+NoatunListener::NoatunListener(QObject *parent) : QObject(parent)
+{ }
+
+NoatunListener::~NoatunListener()
+{ }
+
+void NoatunListener::message()
+{
+ emit event();
+}
+
+NoatunListenerNotif::NoatunListenerNotif(NoatunListener *l)
+{
+ mListener=l;
+}
+
+void NoatunListenerNotif::message()
+{
+ mListener->message();
+}
+
+
+ExitNotifier::ExitNotifier(int pid, QObject *parent) : NoatunListener(parent)
+{
+ mNotif=new NoatunListenerNotif(this);
+
+ DCOPClient c;
+ c.attach();
+
+
+ QCString appids[2];
+ appids[0]=QString("noatun-%1").arg(pid).local8Bit();
+ appids[1]="noatun";
+ appid=appids[0];
+
+ if (c.isApplicationRegistered(appids[0]))
+ {
+ appid=appids[0];
+ }
+ else if (c.isApplicationRegistered(appids[1]))
+ {
+ appid=appids[1];
+ }
+ else
+ {
+ return;
+ }
+
+ QByteArray replyData;
+ QCString replyType;
+
+ QCString sessionName;
+
+ if (!c.call(appid, "Noatun", "session()", QByteArray(), replyType, replyData))
+ {
+ kdDebug(66666) << "Error communicating to parent noatun" << endl;
+ }
+ else
+ {
+ QDataStream reply(replyData, IO_ReadOnly);
+ reply >> sessionName;
+ }
+
+ Visualization::initDispatcher();
+
+ Noatun::Session session=Arts::Reference(sessionName);
+ session.addListener(*mNotif);
+}
+
+ExitNotifier::~ExitNotifier()
+{
+ QByteArray replyData;
+ QCString replyType;
+
+ QCString sessionName;
+
+ DCOPClient c;
+ c.attach();
+
+ if (c.call(appid, "Noatun", "session()", QByteArray(), replyType, replyData))
+ {
+ QDataStream reply(replyData, IO_ReadOnly);
+ reply >> sessionName;
+
+ Noatun::Session session=Arts::Reference(sessionName);
+ session.removeListener(*mNotif);
+ }
+ delete mNotif;
+}
+
+BoolNotifier::BoolNotifier(bool *value, NoatunListener *listener, QObject *parent)
+ : QObject(parent)
+{
+ connect(listener, SIGNAL(event()), SLOT(event()));
+ mValue=value;
+}
+
+SessionManagement::SessionManagement() { }
+SessionManagement::~SessionManagement() { }
+void SessionManagement::restore() { }
+
+#include "plugin.moc"
+#include "plugin_deps.moc"
diff --git a/noatun/library/plugin_deps.h b/noatun/library/plugin_deps.h
new file mode 100644
index 00000000..a2f3f628
--- /dev/null
+++ b/noatun/library/plugin_deps.h
@@ -0,0 +1,44 @@
+#ifndef PLUGIN_DEPS_H
+#define PLUGIN_DEPS_H
+
+#include <qtimer.h>
+#include <qobject.h>
+#include <noatunarts.h>
+
+class Visualization;
+class NoatunListener;
+
+
+class TimerThingy : public QObject
+{
+Q_OBJECT
+public:
+ TimerThingy(Visualization*);
+
+ void setInterval(int msec);
+ void stop();
+ void start();
+
+public:
+ virtual void timerEvent(QTimerEvent *);
+
+private:
+ Visualization *mVis;
+ int id;
+ int ms;
+};
+
+class NoatunListenerNotif : public Noatun::Listener_skel
+{
+public:
+ NoatunListenerNotif(NoatunListener *);
+
+ virtual void message();
+
+ operator Noatun::Listener() { return Noatun::Listener::_from_base(_copy()); }
+private:
+ NoatunListener *mListener;
+};
+
+#endif
+
diff --git a/noatun/library/pluginloader.cpp b/noatun/library/pluginloader.cpp
new file mode 100644
index 00000000..c367228a
--- /dev/null
+++ b/noatun/library/pluginloader.cpp
@@ -0,0 +1,366 @@
+#include <qfile.h>
+#include <kglobal.h>
+#include <qdir.h>
+#include <ksimpleconfig.h>
+#include <kstandarddirs.h>
+#include <knotifyclient.h>
+#include <klocale.h>
+#include <kurl.h>
+#include <pluginloader.h>
+#include <plugin.h>
+#include <kdebug.h>
+
+bool operator ==(const NoatunLibraryInfo &a, const NoatunLibraryInfo &b)
+{
+ // Feels like cheating, doesn't it?
+ return a.specfile == b.specfile;
+}
+
+LibraryLoader::LibraryLoader() : mPlaylist(0)
+{
+}
+
+LibraryLoader::~LibraryLoader()
+{
+ QValueList<NoatunLibraryInfo> l;
+
+ l = loaded();
+ for(QValueList<NoatunLibraryInfo>::Iterator i = l.begin(); i != l.end(); ++i)
+ {
+ if((*i).type != "userinterface" && (*i).type != "playlist" && (*i).type != "systray")
+ {
+ removeNow((*i).specfile);
+ }
+ }
+
+ l = loaded();
+ for(QValueList<NoatunLibraryInfo>::Iterator i = l.begin(); i != l.end(); ++i)
+ {
+ if((*i).type == "userinterface")
+ {
+ removeNow((*i).specfile);
+ }
+ }
+
+ l = loaded();
+ for(QValueList<NoatunLibraryInfo>::Iterator i = l.begin(); i != l.end(); ++i)
+ {
+ removeNow((*i).specfile);
+ }
+}
+
+QValueList<NoatunLibraryInfo> LibraryLoader::available() const
+{
+ QValueList<NoatunLibraryInfo> items;
+ QStringList files=KGlobal::dirs()->findAllResources("appdata", "*.plugin", false, true);
+ for (QStringList::Iterator i=files.begin(); i!=files.end(); ++i)
+ items.append(getInfo(*i));
+
+ return items;
+}
+
+QPtrList<Plugin> LibraryLoader::plugins() const
+{
+ QPtrList<Plugin> list;
+ for (QDictIterator<LibraryLoader::PluginLibrary> i(mLibHash); i.current(); ++i)
+ list.append(i.current()->plugin);
+ return list;
+}
+
+bool LibraryLoader::loadAll()
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup("");
+ QStringList modules = config->readListEntry("Modules");
+ return loadAll(modules);
+}
+
+bool LibraryLoader::loadAll(const QStringList &modules)
+{
+ // Session management...
+ for(QStringList::ConstIterator i=modules.begin(); i!=modules.end(); ++i)
+ {
+ NoatunLibraryInfo info=getInfo(*i);
+ if (!info.type.contains("sm"))
+ continue;
+ loadSO(*i);
+ }
+
+ // load all the playlists in the first
+ for(QStringList::ConstIterator i=modules.begin(); i!=modules.end(); ++i)
+ {
+ NoatunLibraryInfo info=getInfo(*i);
+ if (!info.type.contains("playlist"))
+ continue;
+ loadSO(*i);
+ }
+
+ if (!mPlaylist)
+ {
+ kdWarning(66666) << "No playlist plugin loaded, defaulting to splitplaylist" << endl;
+ if (!loadSO("splitplaylist.plugin"))
+ return false;
+ }
+
+ // load all the user interfaces now
+ for(QStringList::ConstIterator i=modules.begin(); i!=modules.end(); ++i)
+ {
+ NoatunLibraryInfo info=getInfo(*i);
+ if (!info.type.contains("userinterface"))
+ continue;
+ loadSO(*i);
+ }
+
+ if (!loadedByType("userinterface").count())
+ {
+ kdWarning(66666) << "No userinterface plugin loaded, defaulting to excellent" << endl;
+ if (!loadSO("excellent.plugin"))
+ return false;
+ }
+
+ for(QStringList::ConstIterator i=modules.begin(); i!=modules.end(); ++i)
+ {
+ NoatunLibraryInfo info=getInfo(*i);
+ if((!info.type.contains("playlist"))
+ && (!info.type.contains("userinterface"))
+ && (!info.type.contains("sm")))
+ {
+ loadSO(*i);
+ }
+ }
+
+ return true;
+}
+
+NoatunLibraryInfo LibraryLoader::getInfo(const QString &spec) const
+{
+ NoatunLibraryInfo info;
+ QString specPath = (spec[0]=='/') ? spec : KGlobal::dirs()->findResource("appdata", spec);
+ if (!QFile::exists(specPath))
+ return info;
+ KSimpleConfig file(specPath);
+ if (spec.find('/')>=0)
+ info.specfile=KURL(spec).fileName();
+ else
+ info.specfile=spec;
+ info.filename=file.readPathEntry("Filename");
+ info.author=file.readEntry("Author");
+ info.site=file.readEntry("Site");
+ info.email=file.readEntry("Email");
+ info.type=file.readEntry("Type");
+ info.name=file.readEntry("Name");
+ info.comment=file.readEntry("Comment");
+ info.require=file.readListEntry("Require");
+ info.license=file.readEntry("License");
+ return info;
+}
+
+bool LibraryLoader::isLoaded(const QString &spec) const
+{
+ PluginLibrary *lib=mLibHash[spec];
+ if (!lib) return false;
+ return lib->plugin;
+}
+
+bool LibraryLoader::loadSO(const QString &spec)
+{
+ if(!isLoaded(spec))
+ {
+ NoatunLibraryInfo info = getInfo(spec);
+ if (info.specfile != spec)
+ return false;
+
+ for (QStringList::ConstIterator it = info.require.begin(); it != info.require.end(); ++it)
+ loadSO(*it);
+
+ // get the library loader instance
+ KLibLoader *loader = KLibLoader::self();
+
+ PluginLibrary *listitem=mLibHash[spec];
+
+ if (!listitem)
+ {
+ QString filename = KGlobal::dirs()->findResource("module", info.filename);
+ KLibrary *lib = loader->library(QFile::encodeName(filename));
+ if (!lib)
+ return false;
+ listitem=new PluginLibrary;
+ listitem->library=lib;
+ mLibHash.insert(spec, listitem);
+ }
+
+ void *create=listitem->library->symbol("create_plugin");
+ if (!create)
+ return false;
+
+ Plugin* (*plugInStart)();
+ plugInStart=(Plugin* (*)()) create;
+ listitem->plugin=plugInStart();
+
+ if (info.type.contains("playlist"))
+ {
+ //kdDebug(66666) << k_funcinfo << "Assigning mPlaylist to " << info.name << endl;
+ mPlaylist=listitem->plugin->playlist();
+ }
+ listitem->plugin->init();
+
+ return true;
+ }
+ else
+ return false;
+}
+
+void LibraryLoader::add(const QString &spec)
+{
+ PluginLibrary *lib=mLibHash[spec];
+ if (lib)
+ if (lib->plugin) return;
+
+ loadSO(spec);
+}
+
+void LibraryLoader::setModules(const QStringList &mods)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(0);
+ config->writeEntry("Modules", mods);
+ config->sync();
+}
+
+bool LibraryLoader::remove(const QString& spec, bool terminateOnLastUI)
+{
+ bool SystrayPluginEnabled=false;
+
+ NoatunLibraryInfo info = getInfo(spec);
+ // exit if this is the last UI
+ if (info.type=="userinterface" && terminateOnLastUI)
+ {
+ QValueList<NoatunLibraryInfo> l=loaded();
+
+ // Iterate over other plugins
+ for (QValueList<NoatunLibraryInfo>::Iterator i=l.begin(); i!=l.end(); ++i)
+ {
+ // Is this a UI plugin?
+ if ((*i).specfile!=spec && (*i).type=="userinterface")
+ {
+ // Good, we don't have to exit
+ removeNow(spec);
+ return true;
+ }
+ else if((*i).type=="systray") // Is there a Systray plugin?
+ {
+ SystrayPluginEnabled=true;
+ }
+ }
+
+ // Don't terminate, when there is a systray plugin.
+ if(SystrayPluginEnabled)
+ {
+ napp->toggleInterfaces();
+ return true;
+ }
+ else
+ {
+ // No other UIs, terminate
+ kapp->exit();
+ }
+ }
+ else if (info.type=="playlist")
+ {
+ //kdWarning(66666) << "Unloading playlist, this might be dangerous" << endl;
+ mPlaylist=0;
+ }
+
+ removeNow(spec);
+ return true;
+}
+
+bool LibraryLoader::remove(const QString &spec)
+{
+ remove(spec, true);
+ return true;
+}
+
+bool LibraryLoader::remove(const PluginLibrary *pl)
+{
+ for (QDictIterator<PluginLibrary> i(mLibHash); i.current(); ++i)
+ {
+ if (i.current()==pl)
+ return remove(i.currentKey());
+ }
+ return false;
+}
+
+bool LibraryLoader::remove(const Plugin *plugin)
+{
+ for (QDictIterator<PluginLibrary> i(mLibHash); i.current(); ++i)
+ {
+ if (i.current()->plugin==plugin)
+ return remove(i.currentKey());
+ }
+ return false;
+
+}
+
+Playlist *LibraryLoader::playlist() const
+{
+ // playlist should never be NULL when this method is called
+ Q_ASSERT(mPlaylist);
+ return mPlaylist;
+}
+
+QValueList<NoatunLibraryInfo> LibraryLoader::loaded() const
+{
+ QValueList<NoatunLibraryInfo> items;
+
+ for (QDictIterator<PluginLibrary> i(mLibHash); i.current(); ++i)
+ if (isLoaded(i.currentKey()))
+ items.append(getInfo(i.currentKey()));
+
+ return items;
+}
+
+QValueList<NoatunLibraryInfo> LibraryLoader::loadedByType(const QString &type) const
+{
+ QValueList<NoatunLibraryInfo> items;
+
+ for (QDictIterator<PluginLibrary> i(mLibHash); i.current(); ++i)
+ {
+ if (isLoaded(i.currentKey()))
+ {
+ NoatunLibraryInfo info = getInfo(i.currentKey());
+ if (info.type.contains(type))
+ items.append(info);
+ }
+ }
+
+ return items;
+}
+
+void LibraryLoader::removeNow(const QString &spec)
+{
+ NoatunLibraryInfo info = getInfo(spec);
+ if (info.specfile == spec)
+ {
+ QValueList<NoatunLibraryInfo> l = loaded();
+ for (QValueList<NoatunLibraryInfo>::Iterator i = l.begin(); i != l.end(); ++i)
+ {
+ for (QStringList::ConstIterator it = (*i).require.begin(); it != (*i).require.end(); ++it)
+ {
+ if (*it == spec)
+ removeNow((*i).specfile);
+ }
+ }
+ }
+
+ PluginLibrary *lib=mLibHash[spec];
+
+ if (!lib) return;
+
+ delete lib->plugin;
+ lib->plugin=0;
+ mLibHash.remove(spec);
+ delete lib;
+}
+
+
diff --git a/noatun/library/pluginmodule.cpp b/noatun/library/pluginmodule.cpp
new file mode 100644
index 00000000..aec76e44
--- /dev/null
+++ b/noatun/library/pluginmodule.cpp
@@ -0,0 +1,406 @@
+// Copyright (c) 2000-2001 Charles Samuels <charles@kde.org>
+// Copyright (c) 2000-2001 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIAB\ILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include <noatun/app.h>
+
+#include <kdebug.h>
+#include <kdialog.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <qtabwidget.h>
+#include <qheader.h>
+#include <qlabel.h>
+#include <qlayout.h>
+
+#include "noatunlistview.h"
+#include "pluginmodule.h"
+
+#include <qwhatsthis.h>
+
+#include "common.h"
+
+PluginListItem::PluginListItem(const bool _exclusive, bool _checked, const NoatunLibraryInfo &_info, QListView *_parent)
+ : QCheckListItem(_parent, _info.name, CheckBox)
+ , mInfo(_info)
+ , silentStateChange(false)
+ , exclusive(_exclusive)
+{
+ setChecked(_checked);
+ if(_checked) static_cast<PluginListView *>(listView())->count++;
+}
+
+
+void PluginListItem::setChecked(bool b)
+{
+ silentStateChange = true;
+ setOn(b);
+ silentStateChange = false;
+}
+
+void PluginListItem::stateChange(bool b)
+{
+ if(!silentStateChange)
+ static_cast<PluginListView *>(listView())->stateChanged(this, b);
+}
+
+void PluginListItem::paintCell(QPainter *p, const QColorGroup &cg, int a, int b, int c)
+{
+ if(exclusive) myType = RadioButton;
+ QCheckListItem::paintCell(p, cg, a, b, c);
+ if(exclusive) myType = CheckBox;
+}
+
+PluginListView::PluginListView(unsigned _min, unsigned _max, QWidget *_parent, const char *_name)
+ : KListView(_parent, _name)
+ , hasMaximum(true)
+ , max(_max)
+ , min(_min <= _max ? _min : _max)
+ , count(0)
+{
+}
+
+PluginListView::PluginListView(unsigned _min, QWidget *_parent, const char *_name)
+ : KListView(_parent, _name)
+ , hasMaximum(false)
+ , min(_min)
+ , count(0)
+{
+}
+
+PluginListView::PluginListView(QWidget *_parent, const char *_name)
+ : KListView(_parent, _name)
+ , hasMaximum(false)
+ , min(0)
+ , count(0)
+{
+}
+
+void PluginListView::clear()
+{
+ count = 0;
+ KListView::clear();
+}
+
+void PluginListView::stateChanged(PluginListItem *item, bool b)
+{
+ if(b)
+ {
+ count++;
+ emit stateChange(item, b);
+
+ if(hasMaximum && count > max)
+ {
+ // Find a different one and turn it off
+
+ QListViewItem *cur = firstChild();
+ PluginListItem *curItem = dynamic_cast<PluginListItem *>(cur);
+
+ while(cur == item || !curItem || !curItem->isOn())
+ {
+ cur = cur->nextSibling();
+ curItem = dynamic_cast<PluginListItem *>(cur);
+ }
+
+ curItem->setOn(false);
+ }
+ }
+ else
+ {
+ if(count == min)
+ {
+ item->setChecked(true);
+ }
+ else
+ {
+ count--;
+ emit stateChange(item, b);
+ }
+ }
+}
+
+Plugins::Plugins(QObject *_parent)
+ : CModule(i18n("Plugins"), i18n("Select Your Plugins"), "gear", _parent)
+ , shown(false)
+{
+ (new QVBoxLayout(this))->setAutoAdd(true);
+ QTabWidget *tabControl = new QTabWidget(this,"tabControl");
+
+ QFrame *interfaceTab = new QFrame(tabControl);
+ (new QVBoxLayout(interfaceTab, KDialog::marginHint(), KDialog::spacingHint()))->setAutoAdd(true);
+ (void)new QLabel(i18n("<b>Select one or more interfaces to use:</b>"), interfaceTab);
+ // At least one interface is required
+ interfaceList = new PluginListView(1, interfaceTab);
+ interfaceList->addColumn(i18n("Name"));
+ interfaceList->addColumn(i18n("Description"));
+ interfaceList->addColumn(i18n("Author"));
+ interfaceList->addColumn(i18n("License"));
+ connect(interfaceList, SIGNAL(stateChange(PluginListItem *, bool)), this, SLOT(stateChange(PluginListItem *, bool)));
+ tabControl->addTab(interfaceTab, i18n("&Interfaces"));
+
+ QFrame *playlistTab = new QFrame(tabControl);
+ (new QVBoxLayout(playlistTab, KDialog::marginHint(), KDialog::spacingHint()))->setAutoAdd(true);
+ (void)new QLabel(i18n("<b>Select one playlist to use:</b>"), playlistTab);
+ // Exactly one playlist is required
+ playlistList = new PluginListView(1, 1, playlistTab);
+ playlistList->addColumn(i18n("Name"));
+ playlistList->addColumn(i18n("Description"));
+ playlistList->addColumn(i18n("Author"));
+ playlistList->addColumn(i18n("License"));
+ connect(playlistList, SIGNAL(stateChange(PluginListItem *, bool)), this, SLOT(stateChange(PluginListItem *, bool)));
+ tabControl->addTab(playlistTab, i18n("&Playlist"));
+
+ QFrame *visTab = new QFrame(tabControl);
+ (new QVBoxLayout(visTab, KDialog::marginHint(), KDialog::spacingHint()))->setAutoAdd(true);
+ (void)new QLabel(i18n("<b>Select any visualizations to use:</b>"), visTab);
+ visList = new PluginListView(0, visTab);
+ visList->addColumn(i18n("Name"));
+ visList->addColumn(i18n("Description"));
+ visList->addColumn(i18n("Author"));
+ visList->addColumn(i18n("License"));
+ connect(visList, SIGNAL(stateChange(PluginListItem *, bool)), this, SLOT(stateChange(PluginListItem *, bool)));
+ tabControl->addTab(visTab, i18n("&Visualizations"));
+
+ // Other plugins are not restricted
+ QFrame *otherTab = new QFrame(tabControl);
+ (new QVBoxLayout(otherTab, KDialog::marginHint(), KDialog::spacingHint()))->setAutoAdd(true);
+ (void)new QLabel(i18n("<b>Select any other plugins to use:</b>"), otherTab);
+ otherList = new PluginListView(0, otherTab);
+ otherList->addColumn(i18n("Name"));
+ otherList->addColumn(i18n("Description"));
+ otherList->addColumn(i18n("Author"));
+ otherList->addColumn(i18n("License"));
+ connect(otherList, SIGNAL(stateChange(PluginListItem *, bool)), this, SLOT(stateChange(PluginListItem *, bool)));
+ tabControl->addTab(otherTab, i18n("O&ther Plugins"));
+}
+
+void Plugins::reopen()
+{
+ playlistList->clear();
+ interfaceList->clear();
+ otherList->clear();
+ visList->clear();
+
+ QValueList<NoatunLibraryInfo> available = napp->libraryLoader()->available();
+ QValueList<NoatunLibraryInfo> loaded = napp->libraryLoader()->loaded();
+
+ for(QValueList<NoatunLibraryInfo>::Iterator i = available.begin(); i != available.end(); ++i)
+ {
+ PluginListView *parent;
+ bool exclusive = false;
+
+ if((*i).type == "userinterface")
+ {
+ parent = interfaceList;
+ }
+ else if((*i).type == "playlist")
+ {
+ parent = playlistList;
+ exclusive = true;
+ }
+ else if((*i).type == "sm" || (*i).type=="hidden")
+ {
+ parent = 0;
+ }
+ else if ((*i).type == "visualization")
+ {
+ parent = visList;
+ }
+ else
+ {
+ parent = otherList;
+ }
+
+ if(parent)
+ {
+ PluginListItem *item = new PluginListItem(exclusive, loaded.contains(*i), *i, parent);
+ item->setText(0, (*i).name);
+ item->setText(1, (*i).comment);
+ item->setText(2, (*i).author);
+ item->setText(3, (*i).license);
+ }
+ }
+}
+
+void Plugins::stateChange(PluginListItem *item, bool b)
+{
+ if(b)
+ addPlugin(item->info());
+ else
+ removePlugin(item->info());
+}
+
+void Plugins::addPlugin(const NoatunLibraryInfo &info)
+{
+ // Load any that this one depends upon
+ for(QStringList::ConstIterator i = info.require.begin(); i != info.require.end(); ++i)
+ {
+ NoatunLibraryInfo requiredInfo = napp->libraryLoader()->getInfo(*i);
+ PluginListItem *item = findItem(requiredInfo);
+ if(item) item->setOn(true);
+ }
+
+ if(mDeleted.contains(info.specfile))
+ mDeleted.remove(info.specfile);
+ else if(!mAdded.contains(info.specfile))
+ mAdded.append(info.specfile);
+}
+
+void Plugins::removePlugin(const NoatunLibraryInfo &info)
+{
+ LibraryLoader &loader = *(napp->libraryLoader());
+
+ // Here are the ones loaded
+ QValueList<NoatunLibraryInfo> loaded = napp->libraryLoader()->loaded();
+
+ // Add the ones marked for loading
+ for(QStringList::ConstIterator i = mAdded.begin(); i != mAdded.end(); ++i)
+ loaded.append(loader.getInfo(*i));
+
+ // Subtract the ones marked for removal
+ for(QStringList::ConstIterator i = mDeleted.begin(); i != mDeleted.end(); ++i)
+ loaded.remove(loader.getInfo(*i));
+
+ // If any depend on this plugin, mark them for removal (or remove them from mAdded)
+ for(QValueList<NoatunLibraryInfo>::Iterator i = loaded.begin(); i != loaded.end(); ++i)
+ {
+ for(QStringList::ConstIterator j = (*i).require.begin(); j != (*i).require.end(); ++j)
+ {
+ if(*j == info.specfile)
+ {
+ PluginListItem *item = findItem(*i);
+ if(item) item->setOn(false);
+ }
+ }
+ }
+
+ if (mAdded.contains(info.specfile))
+ mAdded.remove(info.specfile);
+ else if(!mDeleted.contains(info.specfile))
+ mDeleted.append(info.specfile);
+}
+
+PluginListItem *Plugins::findItem(const NoatunLibraryInfo &info) const
+{
+ for(QListViewItem *cur = otherList->firstChild(); cur != 0; cur = cur->itemBelow())
+ {
+ PluginListItem *item = dynamic_cast<PluginListItem *>(cur);
+ if(item && item->info() == info)
+ return item;
+ }
+
+ // visualizations
+ for(QListViewItem *cur = visList->firstChild(); cur != 0; cur = cur->itemBelow())
+ {
+ PluginListItem *item = dynamic_cast<PluginListItem *>(cur);
+ if(item && item->info() == info)
+ return item;
+ }
+
+ // If our only interface has a dependency removed, that's a double dose of trouble
+ // We may as well have this here for completeness, though
+ for(QListViewItem *cur = interfaceList->firstChild(); cur != 0; cur = cur->itemBelow())
+ {
+ PluginListItem *item = dynamic_cast<PluginListItem *>(cur);
+ if(item && item->info() == info)
+ return item;
+ }
+
+ // If a playlist is added or removed due to a dependency, we're doom-diddly-oomed
+ // We may as well have this here for completeness, though
+ for(QListViewItem *cur = playlistList->firstChild(); cur != 0; cur = cur->itemBelow())
+ {
+ PluginListItem *item = dynamic_cast<PluginListItem *>(cur);
+ if(item && item->info() == info)
+ return item;
+ }
+
+ return 0;
+}
+
+void Plugins::save()
+{
+ LibraryLoader &loader = *(napp->libraryLoader());
+
+ // Load the plugins the user added
+ //loader.loadAll(mAdded);
+
+ QString oldPlaylist, newPlaylist;
+
+ // first load all non playlist things
+ for (QStringList::Iterator i = mAdded.begin(); i != mAdded.end(); ++i)
+ {
+ NoatunLibraryInfo info = loader.getInfo(*i);
+ if(info.type != "playlist")
+ loader.loadAll(QStringList(*i));
+ else
+ newPlaylist = (*i);
+ }
+
+ // Remove the plugins the user removed
+ for (QStringList::Iterator i = mDeleted.begin(); i != mDeleted.end(); ++i)
+ {
+ NoatunLibraryInfo info = loader.getInfo(*i);
+ if(info.type != "playlist")
+ loader.remove(*i);
+ else
+ oldPlaylist = *i;
+ }
+
+ // Loading normal plugins works the other way round!
+ // If you unload a playlist it sets the global playlist pointer to NULL,
+ // that also means you cannot first load the new and then unload the old one.
+ if(!newPlaylist.isEmpty() && !oldPlaylist.isEmpty())
+ {
+ kdDebug(66666) << k_funcinfo << "Unloading " << oldPlaylist << endl;
+ loader.remove(oldPlaylist);
+ kdDebug(66666) << k_funcinfo << "Loading " << oldPlaylist << endl;
+ loader.loadAll(QStringList(newPlaylist));
+ }
+
+
+ // Round up the ones that weren't loaded right now, for saving in the configuration
+ QStringList specList(mAdded);
+
+ QValueList<NoatunLibraryInfo> loaded = loader.loaded();
+ for(QValueList<NoatunLibraryInfo>::Iterator i = loaded.begin(); i != loaded.end(); ++i)
+ {
+ if(!specList.contains((*i).specfile) && loader.isLoaded((*i).specfile))
+ specList += (*i).specfile;
+ }
+
+ // Now we actually save
+ loader.setModules(specList);
+
+ mDeleted.clear();
+ mAdded.clear();
+}
+
+void Plugins::showEvent(QShowEvent *e)
+{
+ if(!shown)
+ {
+ shown = true;
+ KMessageBox::information(this, i18n("<qt>Changing your playlist plugin will stop playback. Different playlists may use different methods of storing information, so after changing playlists you may have to recreate your playlist.</qt>"), QString::null, "Plugin warning");
+ }
+ CModule::showEvent(e);
+}
+
+#include "pluginmodule.moc"
diff --git a/noatun/library/pluginmodule.h b/noatun/library/pluginmodule.h
new file mode 100644
index 00000000..75d3e04d
--- /dev/null
+++ b/noatun/library/pluginmodule.h
@@ -0,0 +1,100 @@
+// Copyright (c) 2000-2001 Charles Samuels <charles@kde.org>
+// Copyright (c) 2000-2001 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIAB\ILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef PLUGINMODULE_H
+#define PLUGINMODULE_H
+
+#include "noatun/pref.h"
+#include "noatun/pluginloader.h"
+
+#include <klistview.h>
+#include <qframe.h>
+#include <qstringlist.h>
+
+class KTabCtl;
+
+class PluginListItem : public QCheckListItem
+{
+public:
+ PluginListItem(const bool _exclusive, bool _checked, const NoatunLibraryInfo &_info, QListView *_parent);
+ const NoatunLibraryInfo &info() const { return mInfo; }
+
+ // This will toggle the state without "emitting" the stateChange
+ void setChecked(bool);
+
+protected:
+ virtual void stateChange(bool);
+ virtual void paintCell(QPainter *, const QColorGroup &, int, int, int);
+private:
+ NoatunLibraryInfo mInfo;
+ bool silentStateChange;
+ bool exclusive;
+};
+
+class PluginListView : public KListView
+{
+Q_OBJECT
+
+friend class PluginListItem;
+
+public:
+ PluginListView(QWidget *_parent = 0, const char *_name = 0);
+ PluginListView(unsigned _min, QWidget *_parent = 0, const char *_name = 0);
+ PluginListView(unsigned _min, unsigned _max, QWidget *_parent = 0, const char *_name = 0);
+
+ virtual void clear();
+
+signals:
+ void stateChange(PluginListItem *, bool);
+
+private:
+ void stateChanged(PluginListItem *, bool);
+
+ bool hasMaximum;
+ unsigned max, min;
+ unsigned count;
+};
+
+class Plugins : public CModule
+{
+Q_OBJECT
+public:
+ Plugins(QObject *_parent = 0);
+ virtual void save();
+ virtual void reopen();
+
+protected:
+ virtual void showEvent(QShowEvent *);
+
+private slots:
+ void stateChange(PluginListItem *, bool);
+
+private:
+ void addPlugin(const NoatunLibraryInfo &);
+ void removePlugin(const NoatunLibraryInfo &);
+ PluginListItem *findItem(const NoatunLibraryInfo &) const;
+
+ QStringList mAdded, mDeleted;
+ PluginListView *interfaceList, *playlistList, *otherList, *visList;
+
+ bool shown;
+};
+
+#endif
diff --git a/noatun/library/pref.cpp b/noatun/library/pref.cpp
new file mode 100644
index 00000000..98e71645
--- /dev/null
+++ b/noatun/library/pref.cpp
@@ -0,0 +1,95 @@
+#include "pref.h"
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <qlayout.h>
+//#include <qlabel.h>
+#include "cmodule.h"
+
+NoatunPreferences::NoatunPreferences(QWidget *parent)
+ : KDialogBase(TreeList, i18n("Preferences - Noatun"),
+ Ok|Apply|Cancel|Help, Ok, parent, "NoatunPreferences", false, true)
+{
+ resize(640, 480); // KDE is required to support 800x600 min.
+ setShowIconsInTreeList(true);
+ setRootIsDecorated(false);
+}
+
+void NoatunPreferences::slotOk()
+{
+ slotApply();
+ hide();
+}
+
+void NoatunPreferences::show()
+{
+ for (CModule *i=mModules.first(); i != 0; i=mModules.next())
+ i->reopen();
+ KDialogBase::show();
+}
+
+void NoatunPreferences::show(CModule *page)
+{
+ int index = pageIndex( static_cast<QWidget *>(page->parent()) );
+ if (index != -1)
+ showPage(index);
+ show();
+}
+
+void NoatunPreferences::slotApply()
+{
+ for (CModule *i=mModules.first(); i != 0; i=mModules.next())
+ i->save();
+}
+
+void NoatunPreferences::add(CModule *page)
+{
+ mModules.append(page);
+}
+
+void NoatunPreferences::remove(CModule *page)
+{
+ mModules.removeRef(page);
+}
+
+CModule::CModule(const QString &name, const QString &description, const QString &icon, QObject *owner)
+ : QWidget(napp->preferencesBox()->addPage(name, description, KGlobal::iconLoader()->loadIcon(
+ icon, KIcon::Small,0, KIcon::DefaultState,0, true)))
+{
+ if (owner)
+ connect(owner, SIGNAL(destroyed()), SLOT(ownerDeleted()));
+
+ //kdDebug(66666) << k_funcinfo << "name = " << name << endl;
+
+ napp->preferencesBox()->add(this);
+
+ QFrame *page=static_cast<QFrame*>(parent());
+ (new QHBoxLayout(page))->addWidget(this);
+}
+
+CModule::~CModule()
+{
+ //kdDebug(66666) << k_funcinfo << endl;
+#if QT_VERSION < 0x030102 && KDE_VERSION < KDE_MAKE_VERSION( 3, 1, 90 )
+ // Due to a bug in Qt 3.1 and 3.1.1 no close events are sent to hidden
+ // widgets, causing the KJanusWidget to crash. This workaround is
+ // rather intrusive and should be used only in the affected versions
+ // to avoid hard to track bugs in the future. KDE HEAD (to become 3.2)
+ // has a workaround for this problem, and additionally it's fixed in
+ // Qt 3.1.2.
+ napp->sendPostedEvents();
+#endif
+
+ napp->preferencesBox()->remove(this);
+}
+
+void CModule::ownerDeleted()
+{
+ QObject *p=parent();
+ delete this;
+ p->deleteLater();
+}
+
+#include "pref.moc"
diff --git a/noatun/library/scrollinglabel.cpp b/noatun/library/scrollinglabel.cpp
new file mode 100644
index 00000000..d7bdd643
--- /dev/null
+++ b/noatun/library/scrollinglabel.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2000 Rik Hemsley (rikkus) <rik@kde.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <noatun/scrollinglabel.h>
+
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qtimer.h>
+#include <qtooltip.h>
+
+class ScrollingLabel::Private
+{
+ public:
+
+ Private()
+ : scrollSize (0),
+ pos (0),
+ add (false),
+ scroll (true)
+ {
+ }
+
+ // Order dependency.
+ QString text;
+ QString originalText;
+ QPixmap buf;
+ QTimer scrollTimer;
+ QTimer resetTimer;
+ int scrollSize;
+ int pos;
+ bool add;
+ bool scroll;
+ // End order dependency.
+};
+
+ScrollingLabel::ScrollingLabel
+(
+ const QString & initialText,
+ QWidget * parent,
+ const char * name
+)
+ : QWidget(parent, name)
+{
+ d = new Private;
+
+ connect(&d->scrollTimer, SIGNAL(timeout()), this, SLOT(scroll()));
+ connect(&d->resetTimer, SIGNAL(timeout()), this, SLOT(restoreText()));
+
+ setText(initialText);
+}
+
+ScrollingLabel::~ScrollingLabel()
+{
+ delete d;
+}
+
+ void
+ScrollingLabel::setText(const QString & t, int time)
+{
+ d->resetTimer.stop();
+
+ if (-1 != time)
+ {
+ restoreText();
+ d->originalText = d->text;
+ d->text = t;
+ d->resetTimer.start(time, true);
+ _update();
+ }
+ else
+ {
+ d->text = d->originalText = t;
+ _update();
+ }
+
+ QToolTip::remove(this);
+ QToolTip::add(this, d->text);
+}
+
+ void
+ScrollingLabel::restoreText()
+{
+ d->text = d->originalText;
+ _update();
+}
+
+ void
+ScrollingLabel::_update()
+{
+ d->scrollTimer.stop();
+
+ d->pos = 0;
+ d->add = false;
+
+ int w = fontMetrics().width(d->text);
+ int h = fontMetrics().height();
+
+ setFixedHeight(h);
+
+ d->scrollSize = QMAX(0, w - width());
+
+ d->buf.resize(w, h);
+ d->buf.fill(colorGroup().background());
+
+ QPainter p(&d->buf);
+ p.setFont(font());
+ p.drawText(0, fontMetrics().ascent(), d->text);
+
+ if (d->scroll && (d->scrollSize > 0))
+ d->scrollTimer.start(100, true);
+
+ repaint(false);
+}
+
+ void
+ScrollingLabel::paintEvent(QPaintEvent *)
+{
+ bitBlt
+ (this, 0, 0, &d->buf, d->pos, 0, d->pos + width(), height(), Qt::CopyROP);
+}
+
+ void
+ScrollingLabel::resizeEvent(QResizeEvent *)
+{
+ _update();
+}
+
+ void
+ScrollingLabel::scroll()
+{
+ d->scrollTimer.stop();
+
+ repaint(false);
+
+ int scrollTime = 100;
+
+ if (d->pos == d->scrollSize || d->pos == 0)
+ {
+ d->add = !d->add;
+ scrollTime = 800;
+ }
+
+ d->pos += (d->add ? 1 : -1);
+
+ if (d->scroll)
+ d->scrollTimer.start(scrollTime, true);
+}
+
+ QSize
+ScrollingLabel::sizeHint() const
+{
+ return fontMetrics().boundingRect(d->text).size();
+}
+
+ QSize
+ScrollingLabel::minimumSizeHint() const
+{
+ return QSize(0, fontMetrics().height());
+}
+
+ QString
+ScrollingLabel::text() const
+{
+ return d->text;
+}
+
+ void
+ScrollingLabel::setScroll(bool b)
+{
+ d->scroll = b;
+ _update();
+}
+
+#include "scrollinglabel.moc"
+
+// vim:ts=2:sw=2:tw=78:noet
+
diff --git a/noatun/library/spline.cpp b/noatun/library/spline.cpp
new file mode 100644
index 00000000..d09c12dc
--- /dev/null
+++ b/noatun/library/spline.cpp
@@ -0,0 +1,194 @@
+/*
+ Copyright (C) 1998 Jrgen Hochwald <juergen.hochwald@privat.kkf.net>
+ Copyright (C) 2003 Charles Samuels <charles@kde.org>
+
+ 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 General Public License
+ along with this library; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+
+
+#include <stdio.h>
+#include "spline.h"
+
+const bool Spline::natural=false;
+
+Spline::Spline()
+{
+ yp1=0.0;
+ ypn=0.0;
+ mRecalc = true;
+}
+
+Spline::Spline(const Spline &copy)
+{
+ operator=(copy);
+}
+
+Spline &Spline::operator =(const Spline &copy)
+{
+ mPoints = copy.mPoints;
+ mRecalc = copy.mRecalc;
+ yp1 = copy.yp1;
+ ypn = copy.ypn;
+ return *this;
+}
+
+Spline::~Spline()
+{
+
+}
+
+void Spline::add(double x, double y)
+{
+ Group g = { x, y, 0.0 };
+ mPoints.push_back(g);
+ mRecalc=true;
+}
+
+std::vector<double> Spline::points(int count) const
+{
+ std::vector<double> points;
+ if (count == numPoints())
+ {
+ for (int i=0; i < count; ++i)
+ {
+ points.push_back(mPoints[i].y);
+ }
+ }
+ else
+ {
+ double min = mPoints[0].x;
+ double max = mPoints[numPoints()-1].x;
+ double dcount = double(count);
+
+ for (int i=0; i<count; ++i)
+ {
+ points.push_back(spline( (max-min)/dcount*i + min));
+ }
+ }
+
+ return points;
+}
+
+void Spline::calcSpline()
+{
+ const int np=numPoints();
+ double *u = new double[np];
+
+ if (natural)
+ {
+ mPoints[0].y2 = u[0] = 0.0;
+ }
+ else
+ {
+ mPoints[0].y2 = -0.5;
+ u[0] =
+ (3.0/(mPoints[1].x-mPoints[0].x))*((mPoints[1].y-mPoints[0].y)
+ / (mPoints[1].x-mPoints[0].x)-yp1);
+ }
+
+ double sig,p,qn,un;
+ for (int i=1; i<=np-2; i++)
+ {
+ sig=(mPoints[i].x-mPoints[i-1].x) / (mPoints[i+1].x-mPoints[i-1].x);
+ p = sig*mPoints[i-1].y2+2.0;
+ mPoints[i].y2 = (sig-1.0)/p;
+ u[i] =
+ (mPoints[i+1].y - mPoints[i].y) / (mPoints[i+1].x-mPoints[i].x)
+ - (mPoints[i].y - mPoints[i-1].y) / (mPoints[i].x - mPoints[i-1].x);
+ u[i] = (6.0*u[i] / (mPoints[i+1].x - mPoints[i-1].x) - sig *u[i-1])/p;
+ }
+
+ if (natural)
+ {
+ qn = un = 0.0;
+ }
+ else
+ {
+ qn = 0.5;
+ un =
+ (3.0 / (mPoints[np-1].x - mPoints[np-2].x))
+ * (ypn - (mPoints[np-1].y - mPoints[np-2].y)
+ / (mPoints[np-1].x - mPoints[np-2].x));
+ }
+ mPoints[np-1].y2 = (un - qn * u[np-2]) / (qn*mPoints[np-2].y2 +1.0 );
+
+ for (int i=np-2; i>=0; i--)
+ {
+ mPoints[i].y2 = mPoints[i].y2 * mPoints[i+1].y2+u[i];
+ }
+ mRecalc = false;
+ delete [] u;
+}
+
+double Spline::spline(double xarg) const
+{
+ if (numPoints()==0) return 0.0;
+ if (numPoints()==1) return mPoints[0].y;
+
+ if (mRecalc) calcSpline();
+
+ int klo=0;
+ int khi=numPoints()-1;
+ int k;
+ while (khi-klo > 1)
+ {
+ k = khi+klo;
+ if (k % 2)
+ k = (k+1) / 2;
+ else
+ k = k/2;
+
+ if (mPoints[k].x > xarg) khi=k;
+ else klo=k;
+ }
+
+ double h = mPoints[khi].x - mPoints[klo].x;
+ if (h==0)
+ {
+ // failed
+ return 0.0;
+ }
+
+ double a = (mPoints[khi].x - xarg) / h;
+ double b = (xarg - mPoints[klo].x) / h;
+ return
+ a * mPoints[klo].y + b*mPoints[khi].y
+ + ((a*a*a-a) * mPoints[klo].y2
+ + (b*b*b-b) * mPoints[khi].y2) * (h*h) / 6.0;
+}
+
+double Spline::x(int num) const
+{
+ if (numPoints()<num) return 0.0;
+ return mPoints[num].x;
+}
+
+double Spline::y(int num) const
+{
+ if (numPoints()<num) return 0.0;
+ return mPoints[num].y;
+}
+
+void Spline::clear()
+{
+ mPoints.resize(0);
+ ypn=0.0;
+ yp1=0.0;
+ mRecalc=true;
+}
+
+
diff --git a/noatun/library/spline.h b/noatun/library/spline.h
new file mode 100644
index 00000000..db1bb03b
--- /dev/null
+++ b/noatun/library/spline.h
@@ -0,0 +1,74 @@
+/*
+Copyright (C) 1998 Jrgen Hochwald <juergen.hochwald@privat.kkf.net>
+
+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 General Public License
+along with this library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+
+#ifndef SPLINE_H
+#define SPLINE_H
+
+#include <stdlib.h>
+#include <vector>
+
+class Spline
+{
+ struct Group
+ {
+ double x, y, y2;
+ };
+
+ std::vector<Spline::Group> mPoints;
+ bool mRecalc;
+ double yp1;
+ double ypn;
+
+ // stupid AIX[?] compiler won't let me give it a value here
+ static const bool natural;
+
+public:
+ Spline();
+ Spline(const Spline &copy);
+
+ Spline &operator =(const Spline &copy);
+ ~Spline();
+
+ /**
+ * if the curve had @p count points, return them
+ **/
+ std::vector<double> points(int count) const;
+
+ void add(double x, double y);
+ double spline(double xarg) const;
+ double operator[] (double xarg) const { return spline(xarg); }
+ int numPoints() const { return mPoints.size(); }
+ double x(int num) const;
+ double y(int num) const;
+
+ void clear();
+
+private:
+ void calcSpline() const
+ {
+ const_cast<Spline*>(this)->calcSpline();
+ }
+ void calcSpline();
+
+};
+
+#endif
+
diff --git a/noatun/library/stereobuttonaction.cpp b/noatun/library/stereobuttonaction.cpp
new file mode 100644
index 00000000..bfb688f7
--- /dev/null
+++ b/noatun/library/stereobuttonaction.cpp
@@ -0,0 +1,47 @@
+#include "stereobuttonaction.h"
+
+namespace NoatunStdAction
+{
+
+StereoButtonAction::StereoButtonAction(const QString& text, int accel, QObject* parent, const char* name )
+ : KAction(text, accel, parent, name)
+{}
+
+StereoButtonAction::StereoButtonAction(const QString& text, int accel, const QObject* receiver, const char* slot, QObject* parent, const char* name )
+ : KAction(text, accel, receiver, slot, parent, name)
+{}
+
+StereoButtonAction::StereoButtonAction(const QString& text, const QIconSet& pix, int accel, QObject* parent, const char* name )
+ : KAction(text, pix, accel, parent, name)
+{}
+
+StereoButtonAction::StereoButtonAction(const QString& text, const QString& pix, int accel, QObject* parent, const char* name )
+ : KAction(text, pix, accel, parent, name)
+{}
+
+StereoButtonAction::StereoButtonAction(const QString& text, const QIconSet& pix, int accel, const QObject* receiver, const char* slot, QObject* parent, const char* name )
+ : KAction(text, pix, accel, receiver, slot, parent, name)
+{}
+
+StereoButtonAction::StereoButtonAction(const QString& text, const QString& pix, int accel, const QObject* receiver, const char* slot, QObject* parent, const char* name )
+ : KAction(text, pix, accel, receiver, slot, parent, name)
+{}
+
+void StereoButtonAction::disable(void)
+{
+ setEnabled(false);
+}
+
+void StereoButtonAction::enable(void)
+{
+ setEnabled(true);
+}
+
+void StereoButtonAction::toggleEnabled(void)
+{
+ setEnabled(!isEnabled());
+}
+
+}
+
+#include "stereobuttonaction.moc"
diff --git a/noatun/library/titleproxy.cpp b/noatun/library/titleproxy.cpp
new file mode 100644
index 00000000..7515a35a
--- /dev/null
+++ b/noatun/library/titleproxy.cpp
@@ -0,0 +1,376 @@
+/***************************************************************************
+ Proxy.cpp - description
+ -------------------
+begin : Nov 20 14:35:18 CEST 2003
+copyright : (C) 2003 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 "titleproxy.h"
+
+#include <kdebug.h>
+#include <kprotocolmanager.h>
+#include <kmdcodec.h>
+
+#include <qstring.h>
+#include <qtimer.h>
+#include "noatun/app.h"
+
+using namespace TitleProxy;
+
+static const uint MIN_PROXYPORT = 6700;
+static const uint MAX_PROXYPORT = 7777;
+static const int BUFSIZE = 32768;
+
+Proxy::Proxy( KURL url )
+ : QObject()
+ , m_url( url )
+ , m_initSuccess( true )
+ , m_metaInt( 0 )
+ , m_byteCount( 0 )
+ , m_metaLen( 0 )
+ , m_usedPort( 0 )
+ , m_pBuf( 0 )
+{
+ kdDebug(66666) << k_funcinfo << endl;
+
+ m_pBuf = new char[ BUFSIZE ];
+ // Don't try to get metdata for ogg streams (different protocol)
+ m_icyMode = url.path().endsWith( ".ogg" ) ? false : true;
+ // If no port is specified, use default shoutcast port
+ if ( m_url.port() < 1 )
+ m_url.setPort( 80 );
+
+ connect( &m_sockRemote, SIGNAL( error( int ) ), this, SLOT( connectError() ) );
+ connect( &m_sockRemote, SIGNAL( connected() ), this, SLOT( sendRequest() ) );
+ connect( &m_sockRemote, SIGNAL( readyRead() ), this, SLOT( readRemote() ) );
+
+ uint i = 0;
+ Server* server = 0;
+ for ( i = MIN_PROXYPORT; i <= MAX_PROXYPORT; i++ )
+ {
+ server = new Server( i, this );
+ kdDebug(66666) << k_funcinfo <<
+ "Trying to bind to port: " << i << endl;
+ if ( server->ok() ) // found a free port
+ break;
+ delete server;
+ }
+
+ if ( i > MAX_PROXYPORT )
+ {
+ kdWarning(66666) << k_funcinfo <<
+ "Unable to find a free local port. Aborting." << endl;
+ m_initSuccess = false;
+ return;
+ }
+ m_usedPort = i;
+ connect( server, SIGNAL( connected( int ) ), this, SLOT( accept( int ) ) );
+}
+
+
+Proxy::~Proxy()
+{
+ kdDebug(66666) << k_funcinfo << endl;
+ delete[] m_pBuf;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC
+//////////////////////////////////////////////////////////////////////////////////////////
+
+KURL Proxy::proxyUrl()
+{
+ if ( m_initSuccess )
+ {
+ KURL url;
+ url.setPort( m_usedPort );
+ url.setHost( "localhost" );
+ url.setProtocol( "http" );
+ return url;
+ }
+ else
+ return m_url;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// PRIVATE SLOTS
+//////////////////////////////////////////////////////////////////////////////////////////
+
+void Proxy::accept( int socket ) //SLOT
+{
+ //kdDebug(66666) << "BEGIN " << k_funcinfo << endl;
+ m_sockProxy.setSocket( socket );
+ m_sockProxy.waitForMore( KProtocolManager::readTimeout() * 1000 );
+ connectToHost();
+ //kdDebug(66666) << "END " << k_funcinfo << endl;
+}
+
+
+void Proxy::connectToHost() //SLOT
+{
+ //kdDebug(66666) << "BEGIN " << k_funcinfo << endl;
+
+ { //initialisations
+ m_connectSuccess = false;
+ m_headerFinished = false;
+ m_headerStr = "";
+ }
+
+ { //connect to server
+ QTimer::singleShot( KProtocolManager::connectTimeout() * 1000,
+ this, SLOT( connectError() ) );
+
+ kdDebug(66666) << k_funcinfo << "Connecting to " <<
+ m_url.host() << ":" << m_url.port() << endl;
+
+ m_sockRemote.connectToHost( m_url.host(), m_url.port() );
+ }
+
+ //kdDebug(66666) << "END " << k_funcinfo << endl;
+}
+
+
+void Proxy::sendRequest() //SLOT
+{
+ //kdDebug(66666) << "BEGIN " << k_funcinfo << endl;
+
+ QCString username = m_url.user().utf8();
+ QCString password = m_url.pass().utf8();
+ QCString authString = KCodecs::base64Encode( username + ":" + password );
+ bool auth = !( username.isEmpty() && password.isEmpty() );
+
+ QString request = QString( "GET %1 HTTP/1.0\r\n"
+ "Host: %2\r\n"
+ "User-Agent: Noatun/%5\r\n"
+ "%3"
+ "%4"
+ "\r\n" )
+ .arg( m_url.path( -1 ).isEmpty() ? "/" : m_url.path( -1 ) )
+ .arg( m_url.host() )
+ .arg( m_icyMode ? QString( "Icy-MetaData:1\r\n" ) : QString::null )
+ .arg( auth ? QString( "Authorization: Basic " ).append( authString ) : QString::null )
+ .arg( NOATUN_VERSION );
+
+ m_sockRemote.writeBlock( request.latin1(), request.length() );
+
+ //kdDebug(66666) << "END " << k_funcinfo << endl;
+}
+
+
+void Proxy::readRemote() //SLOT
+{
+ m_connectSuccess = true;
+ Q_LONG index = 0;
+ Q_LONG bytesWrite = 0;
+ Q_LONG bytesRead = m_sockRemote.readBlock( m_pBuf, BUFSIZE );
+ if ( bytesRead == -1 )
+ {
+ kdDebug(66666) << k_funcinfo << "Could not read remote data from socket, aborting" << endl;
+ m_sockRemote.close();
+ emit proxyError();
+ return;
+ }
+
+ if ( !m_headerFinished )
+ {
+ if ( !processHeader( index, bytesRead ) )
+ return;
+ }
+
+ //This is the main loop which processes the stream data
+ while ( index < bytesRead )
+ {
+ if ( m_icyMode && m_metaInt && ( m_byteCount == m_metaInt ) )
+ {
+ m_byteCount = 0;
+ m_metaLen = m_pBuf[ index++ ] << 4;
+ }
+ else if ( m_icyMode && m_metaLen )
+ {
+ m_metaData.append( m_pBuf[ index++ ] );
+ --m_metaLen;
+
+ if ( !m_metaLen )
+ {
+ transmitData( m_metaData );
+ m_metaData = "";
+ }
+ }
+ else
+ {
+ bytesWrite = bytesRead - index;
+
+ if ( m_icyMode && bytesWrite > m_metaInt - m_byteCount )
+ bytesWrite = m_metaInt - m_byteCount;
+ bytesWrite = m_sockProxy.writeBlock( m_pBuf + index, bytesWrite );
+
+ if ( bytesWrite == -1 )
+ {
+ error();
+ return;
+ }
+
+ index += bytesWrite;
+ m_byteCount += bytesWrite;
+ }
+ }
+}
+
+
+void Proxy::connectError() //SLOT
+{
+ if ( !m_connectSuccess )
+ {
+ kdWarning(66666) <<
+ "TitleProxy error: Unable to connect to this stream " <<
+ "server. Can't play the stream!" << endl;
+
+ emit proxyError();
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// PRIVATE
+//////////////////////////////////////////////////////////////////////////////////////////
+
+bool Proxy::processHeader( Q_LONG &index, Q_LONG bytesRead )
+{
+ while ( index < bytesRead )
+ {
+ m_headerStr.append( m_pBuf[ index++ ] );
+ if ( m_headerStr.endsWith( "\r\n\r\n" ) )
+ {
+ /*kdDebug(66666) << k_funcinfo <<
+ "Got shoutcast header: '" << m_headerStr << "'" << endl;*/
+
+ // Handle redirection
+ QString loc( "Location: " );
+ int index = m_headerStr.find( loc );
+ if ( index >= 0 )
+ {
+ int start = index + loc.length();
+ int end = m_headerStr.find( "\n", index );
+ m_url = m_headerStr.mid( start, end - start - 1 );
+
+ kdDebug(66666) << k_funcinfo <<
+ "Stream redirected to: " << m_url << endl;
+
+ m_sockRemote.close();
+ connectToHost();
+ return false;
+ }
+
+
+ if (m_headerStr.startsWith("ICY"))
+ {
+ m_metaInt = m_headerStr.section( "icy-metaint:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 ).toInt();
+ m_bitRate = m_headerStr.section( "icy-br:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamName = m_headerStr.section( "icy-name:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamGenre = m_headerStr.section( "icy-genre:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamUrl = m_headerStr.section( "icy-url:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ }
+ else // not ShoutCast
+ {
+ QString serverName = m_headerStr.section( "Server:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ kdDebug(66666) << k_funcinfo << "Server name: " << serverName << endl;
+
+ if (serverName == "Icecast")
+ {
+ m_metaInt = 0;
+ m_streamName = m_headerStr.section( "ice-name:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamGenre = m_headerStr.section( "ice-genre:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamUrl = m_headerStr.section( "ice-url:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ }
+ else if (serverName.startsWith("icecast/1."))
+ {
+ m_metaInt = 0;
+ m_bitRate = m_headerStr.section( "x-audiocast-bitrate:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamName = m_headerStr.section( "x-audiocast-name:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamGenre = m_headerStr.section( "x-audiocast-genre:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamUrl = m_headerStr.section( "x-audiocast-url:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ }
+ }
+
+ if ( m_streamUrl.startsWith( "www.", true ) )
+ m_streamUrl.prepend( "http://" );
+
+ m_sockProxy.writeBlock( m_headerStr.latin1(), m_headerStr.length() );
+ m_headerFinished = true;
+
+ if ( m_icyMode && !m_metaInt )
+ {
+ error();
+ return false;
+ }
+
+ connect( &m_sockRemote, SIGNAL( connectionClosed() ),
+ this, SLOT( connectError() ) );
+ return true;
+ }
+ }
+ return false;
+}
+
+
+void Proxy::transmitData( const QString &data )
+{
+ /*kdDebug(66666) << k_funcinfo <<
+ " received new metadata: '" << data << "'" << endl;*/
+
+ //prevent metadata spam by ignoring repeated identical data
+ //(some servers repeat it every 10 seconds)
+ if ( data == m_lastMetadata )
+ return;
+
+ m_lastMetadata = data;
+
+ emit metaData(
+ m_streamName, m_streamGenre, m_streamUrl, m_bitRate,
+ extractStr(data, QString::fromLatin1("StreamTitle")),
+ extractStr(data, QString::fromLatin1("StreamUrl")));
+}
+
+
+void Proxy::error()
+{
+ kdDebug(66666) <<
+ "TitleProxy error: Stream does not support shoutcast metadata. " <<
+ "Restarting in non-metadata mode." << endl;
+
+ m_sockRemote.close();
+ m_icyMode = false;
+
+ //open stream again, but this time without metadata, please
+ connectToHost();
+}
+
+
+QString Proxy::extractStr( const QString &str, const QString &key )
+{
+ int index = str.find( key, 0, true );
+ if ( index == -1 )
+ {
+ return QString::null;
+ }
+ else
+ {
+ index = str.find( "'", index ) + 1;
+ int indexEnd = str.find( "'", index );
+ return str.mid( index, indexEnd - index );
+ }
+}
+
+#include "titleproxy.moc"
diff --git a/noatun/library/titleproxy.h b/noatun/library/titleproxy.h
new file mode 100644
index 00000000..058943d6
--- /dev/null
+++ b/noatun/library/titleproxy.h
@@ -0,0 +1,129 @@
+/***************************************************************************
+ titleproxy.h - description
+ -------------------
+begin : Nov 20 14:35:18 CEST 2003
+copyright : (C) 2003 by Mark Kretschmann
+email :
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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_TITLEPROXY_H
+#define AMAROK_TITLEPROXY_H
+
+#include <kurl.h> //stack allocated
+
+#include <qobject.h>
+#include <qserversocket.h> //baseclass
+#include <qsocket.h> //stack allocated
+
+class QString;
+
+namespace TitleProxy
+{
+ /**
+ * Proxy Concept:
+ * 1. Connect to streamserver
+ * 2. Listen on localhost, let aRts connect to proxyserver
+ * 3. Write GET request to streamserver, containing Icy-MetaData:1 token
+ * 4. Read MetaInt token from streamserver (==metadata offset)
+ *
+ * 5. Read stream data (mp3 + metadata) from streamserver
+ * 6. Filter out metadata, send to app
+ * 7. Write mp3 data to proxyserver
+ * 8. Goto 5
+ *
+ * Some info on the shoutcast metadata protocol can be found at:
+ * @see http://www.smackfu.com/stuff/programming/shoutcast.html
+ *
+ * @short A proxy server for extracting metadata from Shoutcast streams.
+ */
+
+ class Proxy : public QObject
+ {
+ Q_OBJECT
+ public:
+ Proxy( KURL url );
+ ~Proxy();
+
+ bool initSuccess() { return m_initSuccess; }
+ KURL proxyUrl();
+
+ signals:
+ void metaData(
+ const QString &streamName,
+ const QString &streamGenre,
+ const QString &streamUrl,
+ const QString &streamBitrate,
+ const QString &trackTitle,
+ const QString &trackUrl);
+ void proxyError();
+
+ private slots:
+ void accept( int socket );
+ void connectToHost();
+ void sendRequest();
+ void readRemote();
+ void connectError();
+
+ private:
+ bool processHeader( Q_LONG &index, Q_LONG bytesRead );
+ void transmitData( const QString &data );
+ void error();
+ QString extractStr( const QString &str, const QString &key );
+
+ //ATTRIBUTES:
+ KURL m_url;
+ int m_streamingMode;
+ bool m_initSuccess;
+ bool m_connectSuccess;
+
+ int m_metaInt;
+ QString m_bitRate;
+ int m_byteCount;
+ uint m_metaLen;
+
+ QString m_metaData;
+ bool m_headerFinished;
+ QString m_headerStr;
+ int m_usedPort;
+ QString m_lastMetadata;
+ bool m_icyMode;
+
+ QString m_streamName;
+ QString m_streamGenre;
+ QString m_streamUrl;
+
+ char *m_pBuf;
+
+ QSocket m_sockRemote;
+ QSocket m_sockProxy;
+ };
+
+
+ class Server : public QServerSocket
+ {
+ Q_OBJECT
+
+ public:
+ Server( Q_UINT16 port, QObject* parent )
+ : QServerSocket( port, 1, parent, "TitleProxyServer" ) {};
+
+ signals:
+ void connected( int socket );
+
+ private:
+ void newConnection( int socket ) { emit connected( socket ); }
+ };
+
+} //namespace TitleProxy
+
+#endif /*AMAROK_TITLEPROXY_H*/
+
diff --git a/noatun/library/vequalizer.cpp b/noatun/library/vequalizer.cpp
new file mode 100644
index 00000000..4b131e8a
--- /dev/null
+++ b/noatun/library/vequalizer.cpp
@@ -0,0 +1,936 @@
+/*
+ * Copyright (c) 2003 Charles Samuels <charles@kde.org>
+ *
+ * This file is hereby licensed under the GNU General Public License version
+ * 2 or later at your option.
+ *
+ * This file is licensed under the Qt Public License version 1 with the
+ * condition that the licensed will be governed under the Laws of California
+ * (USA) instead of Norway. Disputes will be settled in Santa Clara county
+ * courts.
+ *
+ * This file is licensed under the following additional license. Be aware
+ * that it is identical to the BSD license, except for the added clause 3:
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. By integrating this software into any other software codebase, you waive
+ all rights to any patents associated with the stated codebase.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "vequalizer.h"
+#include "engine.h"
+#include "spline.h"
+#include "ksaver.h"
+
+#include <noatunarts.h>
+#include <app.h>
+#include <player.h>
+
+#include <common.h>
+#include <dynamicrequest.h>
+#include <artsflow.h>
+#include <soundserver.h>
+
+#include <ktempfile.h>
+#include <kio/netaccess.h>
+#include <kstandarddirs.h>
+#include <kconfig.h>
+#include <klocale.h>
+
+#include <qdom.h>
+#include <qtextstream.h>
+#include <qfile.h>
+
+#include <math.h>
+
+#define EQ (napp->vequalizer())
+#define EQBACK (napp->player()->engine()->equalizer())
+
+using std::vector;
+
+static const double splineTension = 4.0;
+
+VBandsInterface::VBandsInterface()
+{
+
+}
+
+VBandsInterface::~VBandsInterface()
+{
+
+}
+
+
+VBand VBandsInterface::operator [] (int num)
+{
+ return band(num);
+}
+
+struct VBand::Private
+{
+ int refs;
+
+ int index;
+ int start, end;
+ VBandsInterface *bands;
+};
+
+VBand::VBand(VBandsInterface *bands, int index, int start, int end)
+{
+ d = new Private;
+ d->refs=1;
+ d->index = index;
+ d->start = start;
+ d->end = end;
+ d->bands = bands;
+}
+
+VBand::~VBand()
+{
+ if (--d->refs == 0)
+ {
+ delete d;
+ }
+}
+
+VBand::VBand(const VBand &copy)
+{
+ d=0;
+ operator=(copy);
+}
+
+VBand & VBand::operator =(const VBand &copy)
+{
+ if (d && --d->refs == 0)
+ {
+ delete d;
+ }
+
+ d= copy.d;
+ d->refs++;
+ return *this;
+}
+
+int VBand::level() const
+{
+ return d->bands->level(d->index);
+}
+
+void VBand::setLevel(int l)
+{
+ d->bands->setLevel(d->index, l);
+}
+
+int VBand::start() const
+{
+ return d->start;
+}
+
+int VBand::end() const
+{
+ return d->end;
+}
+
+int VBand::center() const
+{
+ return (d->start + d->end)/2;
+}
+
+static QString formatFreq(int f, bool withHz)
+{
+ QString format;
+ if (f<991)
+ format=QString::number(f);
+ else
+ format=QString::number((int)((f+500)/1000.0))+"k";
+
+ if (withHz)
+ format+="Hz";
+
+ return format;
+}
+
+QString VBand::formatStart(bool withHz) const
+{
+ return formatFreq(d->start, withHz);
+}
+
+QString VBand::formatEnd(bool withHz) const
+{
+ return formatFreq(d->end, withHz);
+}
+
+QString VBand::format(bool withHz) const
+{
+ return formatFreq(center(), withHz);
+}
+
+
+
+
+struct VInterpolation::Private
+{
+ int bands;
+ Spline spline;
+
+};
+
+VInterpolation::VInterpolation(int bands)
+{
+ d = new Private;
+ d->bands = bands;
+}
+
+VInterpolation::~VInterpolation()
+{
+ delete d;
+}
+
+int VInterpolation::bands() const
+{
+ return d->bands;
+}
+
+void VInterpolation::getFrequencies(int num, int *low, int *high) const
+{
+ QValueList<int> fs = VEqualizer::frequencies(bands());
+
+ if (num == 0) *low = 1;
+ else *low = fs[num-1]+1;
+ *high=fs[num];
+}
+
+
+VBand VInterpolation::band(int num)
+{
+ int low, high;
+ getFrequencies(num, &low, &high);
+ return VBand(this, num, low, high);
+}
+
+int VInterpolation::level(int index) const
+{
+ const_cast<VInterpolation*>(this)->refresh();
+ double x = onSpline(index);
+
+ return int(d->spline[x*splineTension]);
+}
+
+void VInterpolation::setLevel(int index, int level)
+{
+ refresh();
+
+ double numbands = double(bands());
+ Spline spline;
+
+ for (int i=0; i < numbands; ++i)
+ {
+ VBand b = band(i);
+ spline.add(i*splineTension, double(index == i ? level : b.level()));
+ }
+
+ int realbands = EQ->bands();
+ QValueList<int> values;
+ for (int i=0; i < realbands; ++i)
+ {
+ // i
+ // realbands numbands
+ double x = double(i)*numbands/double(realbands)*splineTension;
+ double value = spline[x];
+ values.append(int(value));
+ }
+ EQ->setLevels(values);
+}
+
+double VInterpolation::onSpline(int bandNum) const
+{
+ double maxReal(EQ->bands()); // 2
+ double maxInter(bands()); // 4
+ double offset(bandNum); // 4
+
+ return maxReal/maxInter*offset;
+}
+
+void VInterpolation::refresh()
+{
+ d->spline.clear();
+
+ VEqualizer *eq = EQ;
+
+ for (int i=0; i < eq->bands(); ++i)
+ {
+ VBand band = eq->band(i);
+ d->spline.add(double(i*splineTension), double(band.level()));
+ }
+
+}
+
+struct VEqualizer::Private
+{
+ struct BandInfo
+ {
+ int level;
+ int start;
+ int end;
+ };
+
+ std::vector<BandInfo> bands;
+ int preamp;
+};
+
+/* rate 4
+27 54 0-108 108
+81 163 109-217 108
+243 514 218-810 269
+729 1621 811-2431 1620
+2187 4661 2432-7290 4858
+6561 13645 7291+ 12708
+*/
+VEqualizer::VEqualizer()
+{
+ d = new Private;
+ d->preamp=1;
+ setBands(6, false);
+}
+
+void VEqualizer::init()
+{
+ KURL url;
+ url.setPath(kapp->dirs()->localkdedir()+"/share/apps/noatun/equalizer");
+ if(!load(url))
+ {
+ setPreamp(0);
+ disable();
+ }
+ else
+ {
+ KConfig *config=kapp->config();
+ config->setGroup("Equalizer");
+ setEnabled(config->readBoolEntry("enabled", false));
+ }
+}
+
+
+VEqualizer::~VEqualizer()
+{
+ KURL url;
+ url.setPath(kapp->dirs()->localkdedir()+"/share/apps/noatun/equalizer");
+ save(url, "auto");
+
+ delete d;
+}
+
+int VEqualizer::start()
+{
+ return 20;
+}
+
+int VEqualizer::end()
+{
+ return 20000;
+}
+
+int VEqualizer::maxBands() const
+{
+ return 14;
+}
+
+int VEqualizer::minBands() const
+{
+ return 2;
+}
+
+
+QValueList<int> VEqualizer::frequencies(int _num)
+{
+#if 0
+ QValueList<int> fs;
+ fs += 108;
+ fs += 217;
+ fs += 810;
+ fs += 2431;
+ fs += 7290;
+ fs += 19999;
+ return fs;
+
+#else
+
+
+
+ // we're looking for
+ // ?^_num = end()-start()
+ // so log[?] (end()-start()) = _num
+ // log(end()-start()) / log(?) = _num
+ // log(end()-start()) = _num * log(?)
+ // log(end()-start()) / _num = log(?)
+ // ? = 10^(log(end()-start()) / _num)
+
+ double num = double(_num);
+ double vstart = double(start());
+ double vend = double(end());
+ const double base = ::pow(10.0, ::log10(vend-vstart)/num);
+
+ QValueList<int> fs;
+
+ for (double i=1.0; i <= num; i++)
+ {
+ double f = ::pow(base, i);
+ f += vstart;
+ fs.append(int(f));
+ }
+
+ return fs;
+#endif
+}
+
+void VEqualizer::setBands(int num, bool interpolate)
+{
+ if (interpolate)
+ {
+ setBands(num);
+ return;
+ }
+
+ if (num > maxBands())
+ num=maxBands();
+ else if (num < minBands()) num = minBands();
+
+ if (num == bands()) return;
+
+ QValueList<int> fs = VEqualizer::frequencies(num);
+ std::vector<Private::BandInfo> modified;
+
+ int bstart=0;
+ for (QValueList<int>::Iterator i(fs.begin()); i != fs.end(); ++i)
+ {
+ Private::BandInfo info;
+ info.start = bstart+1;
+ info.level = 0;
+ info.end = *i;
+ bstart = info.end;
+
+ modified.push_back(info);
+ }
+ d->bands = modified;
+ update(true);
+
+ emit changedBands();
+ emit changed();
+}
+
+void VEqualizer::setPreamp(int preamp)
+{
+ d->preamp = preamp;
+ EQBACK->preamp(pow(2,float(preamp)/100.0));
+ emit changed();
+ emit preampChanged();
+ emit preampChanged(preamp);
+}
+
+
+void VEqualizer::setBands(int num)
+{
+ if (num == bands()) return;
+ VInterpolation ip(num);
+
+ std::vector<Private::BandInfo> modified;
+
+ for (int i=0; i < num; ++i)
+ {
+ Private::BandInfo info;
+ VBand b = ip[i];
+ info.level = b.level();
+ info.start = b.start();
+ info.end = b.end();
+
+ modified.push_back(info);
+ }
+ d->bands = modified;
+ update(true);
+
+ emit changedBands();
+ emit changed();
+}
+
+VBand VEqualizer::band(int num)
+{
+ return VBand(this, num, d->bands[num].start, d->bands[num].end);
+}
+
+int VEqualizer::bands() const
+{
+ return d->bands.size();
+}
+
+bool VEqualizer::isEnabled() const
+{
+ return bool(EQBACK->enabled());
+}
+
+int VEqualizer::preamp() const
+{
+ return d->preamp;
+}
+
+void VEqualizer::enable()
+{
+ setEnabled(true);
+}
+
+void VEqualizer::disable()
+{
+ setEnabled(false);
+}
+
+int VEqualizer::level(int index) const
+{
+ return d->bands[index].level;
+}
+
+void VEqualizer::setLevel(int index, int level)
+{
+ d->bands[index].level = level;
+ update();
+ emit changed();
+ emit modified();
+}
+
+void VEqualizer::setLevels(const QValueList<int> &levels)
+{
+ int index=0;
+ for (
+ QValueList<int>::ConstIterator i(levels.begin());
+ i != levels.end(); ++i
+ )
+ {
+ d->bands[index].level = *i;
+ index++;
+ }
+ update();
+ emit changed();
+ emit modified();
+}
+
+
+void VEqualizer::setEnabled(bool e)
+{
+ update(true); // just in case
+ EQBACK->enabled((long)e);
+ KConfig *config=kapp->config();
+ config->setGroup("Equalizer");
+ config->writeEntry("enabled", e);
+ config->sync();
+
+ emit enabled(e);
+ if (e)
+ emit enabled();
+ else
+ emit disabled();
+}
+
+void VEqualizer::update(bool full)
+{
+ std::vector<float> levels;
+ std::vector<float> mids;
+ std::vector<float> widths;
+
+ for (unsigned int i=0; i < d->bands.size(); ++i)
+ {
+ Private::BandInfo &info = d->bands[i];
+ levels.push_back(::pow(2.0, float(info.level)/50.0));
+ if (full)
+ {
+ int mid = info.start + info.end;
+ mids.push_back(float(mid)*0.5);
+ widths.push_back(float(info.end - info.start));
+ }
+ }
+ if (full)
+ EQBACK->set(levels, mids, widths);
+ else
+ EQBACK->levels(levels);
+}
+
+
+
+bool VEqualizer::save(const KURL &filename, const QString &friendly) const
+{
+ Noatun::KSaver saver(filename);
+ if(!saver.open()) return false;
+
+ saver.textStream() << toString(friendly);
+ saver.close();
+
+ return saver.close();
+}
+
+bool VEqualizer::load(const KURL &filename)
+{
+ QString dest;
+ if(KIO::NetAccess::download(filename, dest, 0L))
+ {
+ QFile file(dest);
+ if (!file.open(IO_ReadOnly))
+ return false;
+
+ QTextStream t(&file);
+ QString str = t.read();
+ fromString(str);
+ return true;
+ }
+ return false;
+}
+
+QString VEqualizer::toString(const QString &name) const
+{
+ QDomDocument doc("noatunequalizer");
+ doc.setContent(QString("<!DOCTYPE NoatunEqualizer><noatunequalizer/>"));
+ QDomElement docElem = doc.documentElement();
+
+ {
+ docElem.setAttribute("level", preamp());
+ docElem.setAttribute("name", name);
+ docElem.setAttribute("version", napp->version());
+ }
+
+ int bandc = bands();
+ for (int i=0; i < bandc; ++i)
+ {
+ VBand band = const_cast<VEqualizer*>(this)->operator[](i);
+ QDomElement elem = doc.createElement("band");
+ elem.setAttribute("start", band.start());
+ elem.setAttribute("end", band.end());
+ elem.setAttribute("level", band.level());
+
+ docElem.appendChild(elem);
+ }
+
+ return doc.toString();
+}
+
+bool VEqualizer::fromString(const QString &str)
+{
+ QDomDocument doc("noatunequalizer");
+ if (!doc.setContent(str))
+ return false;
+
+ QDomElement docElem = doc.documentElement();
+ if (docElem.tagName()!="noatunequalizer")
+ return false;
+
+ setPreamp(docElem.attribute("level", "0").toInt());
+
+ std::vector<Private::BandInfo> modified;
+ for (QDomNode n = docElem.firstChild(); !n.isNull(); n = n.nextSibling())
+ {
+ QDomElement e = n.toElement();
+ if(e.isNull()) continue;
+ if (e.tagName().lower() != "band") continue;
+
+ Private::BandInfo data;
+
+ data.level = e.attribute("level", "0").toInt();
+ data.start = e.attribute("start", "1").toInt();
+ data.end = e.attribute("end", "19999").toInt();
+
+ modified.push_back(data);
+ }
+ d->bands = modified;
+ update(true);
+
+ emit changedBands();
+ emit changed();
+ return true;
+}
+
+static QString makePresetFile()
+{
+ QString basedir=kapp->dirs()->localkdedir()+"/share/apps/noatun/eq.preset/";
+ // now append a filename that doesn't exist
+ KStandardDirs::makeDir(basedir);
+ QString fullpath;
+ int num=0;
+ do
+ {
+ if (num)
+ fullpath=basedir+"preset."+QString::number(num);
+ else
+ fullpath=basedir+"preset";
+ num++;
+ }
+ while (QFile(fullpath).exists());
+ return fullpath;
+}
+
+VPreset VEqualizer::createPreset(const QString &name, bool smart)
+{
+ if (presetExists(name) && !smart) return VPreset();
+ QString nameReal=name;
+ {
+ int number=1;
+ while (presetExists(nameReal))
+ {
+ nameReal=name+" ("+QString::number(number)+')';
+ number++;
+ }
+ }
+
+ VPreset preset(makePresetFile());
+ preset.setName(nameReal);
+
+ save(preset.file(), nameReal);
+ KConfig *config=kapp->config();
+ config->setGroup("Equalizer");
+ QStringList list = config->readListEntry("presets");
+ list += preset.file();
+ config->writeEntry("presets", list);
+ config->sync();
+
+ emit created(preset);
+ return preset;
+}
+
+
+QValueList<VPreset> VEqualizer::presets() const
+{
+ KConfig *conf=KGlobal::config();
+ conf->setGroup("Equalizer");
+
+ QStringList list;
+ if (conf->hasKey("presets"))
+ {
+ list=conf->readListEntry("presets");
+ }
+ else
+ {
+ list=kapp->dirs()->findAllResources("data", "noatun/eq.preset/*");
+ conf->writeEntry("presets", list);
+ conf->sync();
+ }
+
+ QValueList<VPreset> presets;
+
+ for (QStringList::Iterator i = list.begin(); i!=list.end(); ++i)
+ {
+ QFile file(*i);
+ if (!file.open(IO_ReadOnly)) continue;
+
+ QDomDocument doc("noatunequalizer");
+ if (!doc.setContent(&file)) continue;
+
+ QDomElement docElem = doc.documentElement();
+ if (docElem.tagName()!="noatunequalizer") continue;
+
+ presets.append(VPreset(*i));
+ }
+ return presets;
+}
+
+VPreset VEqualizer::presetByName(const QString &name)
+{
+ QValueList<VPreset> pr = presets();
+ for (
+ QValueList<VPreset>::Iterator i(pr.begin());
+ i != pr.end(); ++i
+ )
+ {
+ if ((*i).name() == name)
+ return *i;
+ }
+ return VPreset();
+}
+
+VPreset VEqualizer::presetByFile(const QString &file)
+{
+ KConfig *conf=KGlobal::config();
+ conf->setGroup("Equalizer");
+ QStringList list=kapp->config()->readListEntry("presets");
+ if (list.contains(file))
+ return VPreset(file);
+ return VPreset();
+}
+
+bool VEqualizer::presetExists(const QString &name) const
+{
+ QValueList<VPreset> list=presets();
+ for (
+ QValueList<VPreset>::Iterator i(list.begin());
+ i != list.end(); ++i
+ )
+ {
+ if ((*i).name() == name)
+ return true;
+ }
+ return false;
+}
+
+
+
+struct VPreset::Private
+{
+ QString file;
+};
+
+
+
+VPreset::VPreset(const QString &file)
+{
+ d = new Private;
+ d->file = file;
+
+}
+
+
+VPreset::VPreset()
+{
+ d = new Private;
+}
+
+VPreset::VPreset(const VPreset &copy)
+{
+ d = new Private;
+ operator =(copy);
+}
+
+VPreset::~VPreset()
+{
+ delete d;
+}
+
+bool VPreset::operator ==(const VPreset &other) const
+{
+ return name() == other.name();
+}
+
+VPreset & VPreset::operator=(const VPreset &copy)
+{
+ d->file = copy.file();
+ return *this;
+}
+
+QString VPreset::name() const
+{
+ QFile file(d->file);
+ if (!file.open(IO_ReadOnly)) return 0;
+
+ QDomDocument doc("noatunequalizer");
+ if (!doc.setContent(&file)) return 0;
+
+ QDomElement docElem = doc.documentElement();
+ if (docElem.tagName()!="noatunequalizer") return 0;
+ bool standard=docElem.attribute("default", "0")=="0";
+ QString n=docElem.attribute("name", 0);
+
+ { // All the translations for the presets
+# ifdef I18N_STUFF
+ I18N_NOOP("Trance");
+ I18N_NOOP("Dance");
+ I18N_NOOP("Metal");
+ I18N_NOOP("Jazz");
+ I18N_NOOP("Zero");
+ I18N_NOOP("Eclectic Guitar");
+# endif
+ }
+
+ if (standard)
+ n=i18n(n.local8Bit());
+
+ return n;
+}
+
+bool VPreset::setName(const QString &name)
+{
+ QFile file(d->file);
+ if (!file.open(IO_ReadOnly)) return false;
+
+ QDomDocument doc("noatunequalizer");
+ if (!doc.setContent(&file)) return false;
+
+ QDomElement docElem = doc.documentElement();
+ if (docElem.tagName()!="noatunequalizer") return false;
+
+ if (docElem.attribute("name") == name) return true;
+ if (EQ->presetByName(name)) return false;
+
+ docElem.setAttribute("name", name);
+ file.close();
+
+ if (!file.open(IO_ReadWrite | IO_Truncate)) return false;
+
+ QTextStream s(&file);
+ s << doc.toString();
+ file.close();
+
+ emit EQ->renamed(*this);
+
+ return true;
+}
+
+bool VPreset::isValid() const
+{
+ return d->file.length();
+}
+
+void VPreset::save()
+{
+ KURL url;
+ url.setPath(d->file);
+ EQ->load(url);
+}
+
+void VPreset::load() const
+{
+ KURL url;
+ url.setPath(d->file);
+ EQ->load(url);
+}
+
+QString VPreset::file() const
+{
+ return d->file;
+}
+
+void VPreset::remove()
+{
+ KConfig *config=kapp->config();
+ config->setGroup("Equalizer");
+ QStringList items=config->readListEntry("presets");
+ items.remove(file());
+ config->writeEntry("presets", items);
+ config->sync();
+
+ emit EQ->removed(*this);
+
+ if (file().find(kapp->dirs()->localkdedir())==0)
+ {
+ QFile f(file());
+ f.remove();
+ }
+ d->file = "";
+}
+
+
+#undef EQ
+#undef EQBACK
+
+#include "vequalizer.moc"
+
diff --git a/noatun/library/video.cpp b/noatun/library/video.cpp
new file mode 100644
index 00000000..c259d4ba
--- /dev/null
+++ b/noatun/library/video.cpp
@@ -0,0 +1,162 @@
+/****************************************************************************
+ Copyright (C) 2002 Charles Samuels
+ this file is on the BSD license, sans advertisement clause
+ *****************************************************************************/
+
+#include <noatun/video.h>
+#include <noatun/app.h>
+#include <noatun/player.h>
+#include <noatun/engine.h>
+
+#include <qpopupmenu.h>
+#include <kaction.h>
+#include <klocale.h>
+
+#include "globalvideo.h"
+
+// sorry :)
+QPtrList<VideoFrame> VideoFrame::frames;
+
+VideoFrame *VideoFrame::whose=0;
+
+struct VideoFrame::Private
+{
+};
+
+
+VideoFrame::VideoFrame(KXMLGUIClient *clientParent, QWidget *parent, const char*name, WFlags f)
+ : KVideoWidget(clientParent, parent, name, f)
+{
+ d = new Private;
+ connect(napp->player(), SIGNAL(newSong()), SLOT(changed()));
+ connect(napp->player(), SIGNAL(stopped()), SLOT(stopped()));
+ frames.append(this);
+}
+
+VideoFrame::VideoFrame(QWidget *parent, const char *name, WFlags f)
+ : KVideoWidget(parent, name, f)
+{
+ d = new Private;
+ connect(napp->player(), SIGNAL(newSong()), SLOT(changed()));
+ connect(napp->player(), SIGNAL(stopped()), SLOT(stopped()));
+ frames.append(this);
+}
+
+VideoFrame::~VideoFrame()
+{
+ if (whose == this)
+ {
+ embed(Arts::VideoPlayObject::null());
+ whose=0;
+ }
+
+ frames.removeRef(this);
+ VideoFrame *l = frames.last();
+ if (l) l->give();
+ else whose=0;
+ delete d;
+}
+
+VideoFrame *VideoFrame::playing()
+{
+ return whose;
+}
+
+QPopupMenu *VideoFrame::popupMenu(QWidget *parent)
+{
+ QPopupMenu *view = new QPopupMenu(parent);
+ action( "half_size" )->plug( view );
+ action( "normal_size" )->plug( view );
+ action( "double_size" )->plug( view );
+ view->insertSeparator();
+ action( "fullscreen_mode" )->plug( view );
+ return view;
+}
+
+void VideoFrame::give()
+{
+ VideoFrame *old=whose;
+ whose = this;
+
+ if (whose != old && old != 0)
+ {
+ old->embed(Arts::VideoPlayObject::null());
+ emit old->lost();
+ }
+
+ Arts::PlayObject po = napp->player()->engine()->playObject();
+ if (po.isNull()) return;
+
+ Arts::VideoPlayObject vpo = Arts::DynamicCast(po);
+ if (!vpo.isNull())
+ {
+ embed(vpo);
+ emit acquired();
+ }
+}
+
+void VideoFrame::changed()
+{
+ if (whose==this)
+ give();
+}
+
+void VideoFrame::stopped()
+{
+ if (whose==this)
+ {
+ embed(Arts::VideoPlayObject::null());
+ emit lost();
+ }
+}
+
+#include <qlayout.h>
+
+
+GlobalVideo::GlobalVideo()
+ : QWidget( 0, 0, WType_TopLevel | WStyle_Customize | WStyle_DialogBorder | WStyle_Title )
+{
+ setCaption(i18n("Video - Noatun"));
+ (new QVBoxLayout(this))->setAutoAdd(true);
+ video = new VideoFrame(this);
+ menu = video->popupMenu(this);
+
+ // FIXME: How to obtain minimum size for top-level widgets?
+// video->setMinimumSize(minimumSizeHint());
+// video->setMinimumSize(101,35);
+ video->setMinimumSize(128,96);
+
+ connect(video, SIGNAL(acquired()), SLOT(appear()));
+ connect(video, SIGNAL(lost()), SLOT(hide()));
+ connect(video, SIGNAL(adaptSize(int,int)), this, SLOT(slotAdaptSize(int,int)));
+
+ video->setNormalSize();
+ video->give();
+}
+
+void GlobalVideo::slotAdaptSize(int w, int h)
+{
+ resize(w, h);
+}
+
+void GlobalVideo::appear()
+{
+ QWidget::show();
+}
+
+void GlobalVideo::hide()
+{
+ QWidget::hide();
+}
+
+void GlobalVideo::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (e->button() == RightButton)
+ {
+ menu->exec(mapToGlobal(e->pos()));
+ }
+}
+
+#include "globalvideo.moc"
+#include "video.moc"
+
diff --git a/noatun/modules/Makefile.am b/noatun/modules/Makefile.am
new file mode 100644
index 00000000..90c298d3
--- /dev/null
+++ b/noatun/modules/Makefile.am
@@ -0,0 +1,4 @@
+SUBDIRS = artseffects \
+ dcopiface excellent htmlexport infrared kaiman keyz kjofol-skin \
+ marquis metatag monoscope net noatunui splitplaylist systray \
+ voiceprint winskin simple
diff --git a/noatun/modules/artseffects/ExtraStereo.mcopclass b/noatun/modules/artseffects/ExtraStereo.mcopclass
new file mode 100644
index 00000000..7fb466ec
--- /dev/null
+++ b/noatun/modules/artseffects/ExtraStereo.mcopclass
@@ -0,0 +1,5 @@
+Buildable=true
+Interface=ExtraStereo,Arts::StereoEffect,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartseffects.la
+Use=directly \ No newline at end of file
diff --git a/noatun/modules/artseffects/ExtraStereoGuiFactory.mcopclass b/noatun/modules/artseffects/ExtraStereoGuiFactory.mcopclass
new file mode 100644
index 00000000..5b5521ae
--- /dev/null
+++ b/noatun/modules/artseffects/ExtraStereoGuiFactory.mcopclass
@@ -0,0 +1,5 @@
+Buildable=true
+Interface=ExtraStereoGuiFactory,Arts::GuiFactory,Arts::Object
+Library=libartseffects.la
+Language=C++
+CanCreate=ExtraStereo
diff --git a/noatun/modules/artseffects/Makefile.am b/noatun/modules/artseffects/Makefile.am
new file mode 100644
index 00000000..49a5bb99
--- /dev/null
+++ b/noatun/modules/artseffects/Makefile.am
@@ -0,0 +1,25 @@
+INCLUDES= -I$(top_builddir)/arts/gui/common -I$(top_srcdir)/arts/gui/common -I$(kde_includes)/arts $(all_includes)
+lib_LTLIBRARIES = libartseffects.la
+#libartseffectsui.la
+
+libartseffects_la_SOURCES = artseffects.cc extrastereo_impl.cc
+libartseffects_la_COMPILE_FIRST = artseffects.cc
+libartseffects_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+libartseffects_la_LIBADD = $(top_builddir)/arts/gui/common/libartsgui_idl.la -lkmedia2_idl -lsoundserver_idl -lartsflow
+
+
+artseffects.mcopclass: artseffects.h
+artseffects.mcoptype: artseffects.h
+artseffects.cc artseffects.h: $(srcdir)/artseffects.idl $(MCOPIDL)
+ $(MCOPIDL) -t -I$(top_builddir)/arts/gui/common -I$(top_srcdir)/arts/gui/common -I$(kde_includes)/arts $(srcdir)/artseffects.idl
+
+CLEANFILES = artsmidi.cc artsmidi.h artsmidi.mcoptype artsmidi.mcopclass
+
+mcoptypedir = $(libdir)/mcop
+mcoptype_DATA = artseffects.mcoptype artseffects.mcopclass
+
+mcopclassdir = $(libdir)/mcop
+mcopclass_DATA = ExtraStereo.mcopclass VoiceRemoval.mcopclass RawWriter.mcopclass ExtraStereoGuiFactory.mcopclass
+
+artseffects.lo: artseffects.h ../../../arts/gui/common/artsgui.h
+extrastereo_impl.lo: ../../../arts/gui/common/artsgui.h artseffects.h
diff --git a/noatun/modules/artseffects/RawWriter.mcopclass b/noatun/modules/artseffects/RawWriter.mcopclass
new file mode 100644
index 00000000..535db20a
--- /dev/null
+++ b/noatun/modules/artseffects/RawWriter.mcopclass
@@ -0,0 +1,4 @@
+Buildable=true
+Interface=RawWriter,Arts::StereoEffect,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartseffects.la
diff --git a/noatun/modules/artseffects/VoiceRemoval.mcopclass b/noatun/modules/artseffects/VoiceRemoval.mcopclass
new file mode 100644
index 00000000..661029bd
--- /dev/null
+++ b/noatun/modules/artseffects/VoiceRemoval.mcopclass
@@ -0,0 +1,5 @@
+Buildable=true
+Interface=VoiceRemoval,Arts::StereoEffect,Arts::SynthModule,Arts::Object
+Language=C++
+Library=libartseffects.la
+Use=directly
diff --git a/noatun/modules/artseffects/artseffects.idl b/noatun/modules/artseffects/artseffects.idl
new file mode 100644
index 00000000..86fdea16
--- /dev/null
+++ b/noatun/modules/artseffects/artseffects.idl
@@ -0,0 +1,17 @@
+#include <artsflow.idl>
+#include <artsgui.idl>
+
+interface ExtraStereo : Arts::StereoEffect {
+ attribute float intensity;
+};
+
+interface VoiceRemoval : Arts::StereoEffect {
+};
+
+interface RawWriter : Arts::StereoEffect
+{
+};
+
+interface ExtraStereoGuiFactory : Arts::GuiFactory {
+};
+
diff --git a/noatun/modules/artseffects/effect.cpp b/noatun/modules/artseffects/effect.cpp
new file mode 100644
index 00000000..8d1c8b69
--- /dev/null
+++ b/noatun/modules/artseffects/effect.cpp
@@ -0,0 +1 @@
+
diff --git a/noatun/modules/artseffects/extrastereo_impl.cc b/noatun/modules/artseffects/extrastereo_impl.cc
new file mode 100644
index 00000000..3dc92705
--- /dev/null
+++ b/noatun/modules/artseffects/extrastereo_impl.cc
@@ -0,0 +1,153 @@
+#include "artsgui.h"
+#include "artseffects.h"
+#include "stdsynthmodule.h"
+#include <dynamicrequest.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <convert.h>
+#include "debug.h"
+#include <connect.h>
+#include <cstdlib>
+
+using namespace Arts;
+
+class ExtraStereo_impl : virtual public ExtraStereo_skel,
+ virtual public StdSynthModule
+{
+ float _intensity;
+// StereoEffectGUI mStereoEffectGUI;
+public:
+ float intensity() { return _intensity; }
+ void intensity(float newValue) {
+ _intensity = newValue;
+// cout << " **** INTENSITY = " << newValue << endl;
+ }
+
+ ExtraStereo_impl() : _intensity(2.0)
+ {
+// mStereoEffectGUI = DynamicCast(server.createObject("Arts::ExtraStereo"));
+// DynamicRequest(mStereoEffectGUI).method("_set_effect").param(*this).invoke();
+
+ }
+ void calculateBlock(unsigned long samples)
+ {
+ unsigned long i;
+
+ for(i = 0; i < samples; i++)
+ {
+ float average = (inleft[i] + inright[i]) / 2.0;
+
+ float outleftnew = average + (inleft[i] - average) * _intensity;
+ if(outleftnew > 1.0) outleft[i] = 1.0;
+ else if(outleftnew < -1.0) outleft[i] = -1.0;
+ else outleft[i] = outleftnew;
+
+ float outrightnew = average + (inright[i] - average) * _intensity;
+ if(outrightnew > 1.0) outright[i] = 1.0;
+ else if(outrightnew < -1.0) outright[i] = -1.0;
+ else outright[i] = outrightnew;
+ }
+ }
+};
+
+
+class VoiceRemoval_impl : virtual public VoiceRemoval_skel,
+ virtual public StdSynthModule
+{
+public:
+
+ VoiceRemoval_impl()
+ {
+
+ }
+ // This is based on the work of Anders Carlsson <anders.carlsson@tordata.se>
+ void calculateBlock(unsigned long samples)
+ {
+ for (unsigned i = 0; i < samples; i++)
+ {
+ float outleftnew=inleft[i]-inright[i];
+ float outrightnew=inright[i]-inleft[i];
+
+ if (inleft[i] < -1.0) outleftnew = -1.0;
+ else if (inleft[i] > 1.0) outleftnew = 1.0;
+
+ if (inright[i] < -1.0) outrightnew = -1.0;
+ else if (inright[i] > 1.0) outrightnew = 1.0;
+ outleft[i] = outleftnew;
+ outright[i] = outrightnew;
+ }
+
+ }
+};
+
+class RawWriter_impl : virtual public RawWriter_skel,
+ virtual public StdSynthModule
+{
+ int mFd;
+public:
+ RawWriter_impl()
+ {
+ std::string file=getenv("HOME");
+ file.append("/arts-write.raw");
+ mFd=::open(file.c_str(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ }
+ ~RawWriter_impl()
+ {
+ ::close(mFd);
+ }
+ // This is based on the work of Anders Carlsson <anders.carlsson@tordata.se>
+ void calculateBlock(unsigned long samples)
+ {
+ for (unsigned i = 0; i < samples; i++)
+ {
+ outleft[i]=inleft[i];
+ outright[i]=inright[i];
+ }
+ unsigned char *buffer=new unsigned char[samples*4];
+ convert_stereo_2float_i16le(samples,
+ inleft, inright, buffer);
+ ::write(mFd, (const void*)buffer, samples*4);
+ delete [] buffer;
+ }
+};
+
+class ExtraStereoGuiFactory_impl : public ExtraStereoGuiFactory_skel
+{
+public:
+ Widget createGui(Object object)
+ {
+ arts_return_val_if_fail(!object.isNull(), Arts::Widget::null());
+
+ ExtraStereo e = DynamicCast(object);
+ arts_return_val_if_fail(!e.isNull(), Arts::Widget::null());
+
+ HBox hbox;
+ hbox.width(80);
+ hbox.height(80);
+ hbox.show();
+
+
+ Poti intense;
+ intense.caption("Intensity");
+ intense.color("red");
+ intense.min(0);
+ intense.max(5);
+ intense.value(e.intensity());
+ intense.parent(hbox);
+ intense.show();
+ connect(intense,"value_changed", e, "intensity");
+
+ hbox._addChild(intense,"intensityWidget");
+
+ return hbox;
+ }
+};
+
+
+REGISTER_IMPLEMENTATION(ExtraStereo_impl);
+REGISTER_IMPLEMENTATION(VoiceRemoval_impl);
+REGISTER_IMPLEMENTATION(RawWriter_impl);
+REGISTER_IMPLEMENTATION(ExtraStereoGuiFactory_impl);
+
+
diff --git a/noatun/modules/artseffects/extrastereogui_impl.cc b/noatun/modules/artseffects/extrastereogui_impl.cc
new file mode 100644
index 00000000..5952c80e
--- /dev/null
+++ b/noatun/modules/artseffects/extrastereogui_impl.cc
@@ -0,0 +1,28 @@
+#include <qlayout.h>
+#include <qslider.h>
+#include "extrastereogui_impl.h"
+
+namespace Arts {
+
+ExtraStereoGUI_impl::ExtraStereoGUI_impl() : QWidget(0)
+{
+ (new QHBoxLayout(this))->setAutoAdd(true);
+ mSlider=new QSlider(0,100,10, 0, Horizontal, this);
+ mSlider->show();
+ show();
+}
+
+void ExtraStereoGUI_impl::changeSlider(int v)
+{
+ effect.intensity((float)v/100.0);
+}
+
+void ExtraStereoGUI_impl::setEffect(StereoEffect newEffect)
+{
+ effect = DynamicCast(newEffect);
+}
+
+REGISTER_IMPLEMENTATION(ExtraStereoGUI_impl);
+
+};
+
diff --git a/noatun/modules/artseffects/extrastereogui_impl.h b/noatun/modules/artseffects/extrastereogui_impl.h
new file mode 100644
index 00000000..56104ae8
--- /dev/null
+++ b/noatun/modules/artseffects/extrastereogui_impl.h
@@ -0,0 +1,26 @@
+#include "artseffects.h"
+#include "stdsynthmodule.h"
+#include <qwidget.h>
+
+class QSlider;
+
+namespace Arts {
+
+class ExtraStereoGUI_impl : public QWidget, virtual public ExtraStereoGUI_skel
+{
+public:
+ ExtraStereo effect;
+
+ ExtraStereoGUI_impl();
+
+ void setEffect(StereoEffect newEffect);
+
+public slots:
+ void changeSlider(int);
+
+private:
+ QSlider *mSlider;
+};
+
+
+};
diff --git a/noatun/modules/dcopiface/Makefile.am b/noatun/modules/dcopiface/Makefile.am
new file mode 100644
index 00000000..6d55fc72
--- /dev/null
+++ b/noatun/modules/dcopiface/Makefile.am
@@ -0,0 +1,16 @@
+INCLUDES= -I$(top_builddir)/noatun/library -I$(top_srcdir)/noatun/library -I$(kde_includes)/arts $(all_includes)
+kde_module_LTLIBRARIES = noatun_dcopiface.la
+
+noatun_dcopiface_la_SOURCES = dcopiface.cpp dcopiface.skel
+
+noatun_dcopiface_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_dcopiface_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la
+
+noatun_dcopiface_la_METASOURCES = AUTO
+
+noinst_HEADERS = dcopiface.h
+
+noatun_modules_dcopiface_DATA = dcopiface.plugin
+noatun_modules_dcopifacedir = $(kde_datadir)/noatun
+
+dcopiface.lo: ../../library/noatunarts/noatunarts.h
diff --git a/noatun/modules/dcopiface/dcopiface.cpp b/noatun/modules/dcopiface/dcopiface.cpp
new file mode 100644
index 00000000..93ef6160
--- /dev/null
+++ b/noatun/modules/dcopiface/dcopiface.cpp
@@ -0,0 +1,250 @@
+#include "dcopiface.h"
+
+#include <noatun/player.h>
+#include <noatun/app.h>
+#include <noatunarts/noatunarts.h>
+#include <noatun/engine.h>
+
+#include <dcopclient.h>
+
+extern "C"
+{
+ KDE_EXPORT NIF *create_plugin()
+ {
+ return new NIF();
+ }
+}
+
+
+NIF::NIF() : Plugin(), DCOPObject("Noatun")
+{
+ mLastVolume = 0;
+// connect(napp->player(), SIGNAL(newSong()), SLOT(newSongPlaying()));
+}
+
+NIF::~NIF()
+{
+ kapp->dcopClient()->emitDCOPSignal("exiting()", QByteArray());
+}
+
+void NIF::toggleListView()
+{
+ napp->player()->toggleListView();
+}
+
+void NIF::handleButtons()
+{
+ napp->player()->handleButtons();
+}
+
+void NIF::removeCurrent()
+{
+ napp->player()->removeCurrent();
+}
+
+void NIF::back()
+{
+ napp->player()->back();
+}
+
+void NIF::stop()
+{
+ napp->player()->stop();
+}
+
+void NIF::play()
+{
+ napp->player()->play();
+}
+
+void NIF::playpause()
+{
+ napp->player()->playpause();
+}
+
+void NIF::forward()
+{
+ napp->player()->forward();
+}
+
+void NIF::skipTo(int msec)
+{
+ napp->player()->skipTo(msec);
+}
+
+void NIF::loop()
+{
+ napp->player()->loop();
+}
+
+void NIF::setVolume(int i)
+{
+ napp->player()->setVolume(i);
+}
+
+int NIF::volume()
+{
+ return napp->player()->volume();
+}
+
+void NIF::volumeUp()
+{
+ napp->player()->setVolume(napp->player()->volume() + 5);
+}
+
+void NIF::volumeDown()
+{
+ napp->player()->setVolume(napp->player()->volume() - 5);
+}
+
+void NIF::toggleMute()
+{
+ int currVol = napp->player()->volume();
+ if (currVol == 0)
+ {
+ napp->player()->setVolume(mLastVolume);
+ }
+ else
+ {
+ mLastVolume = currVol;
+ napp->player()->setVolume(0);
+ }
+}
+
+int NIF::length() // returns -1 if there's no playobject
+{
+ return napp->player()->getLength();
+}
+
+int NIF::position() // returns -1 if there's no playobject
+{
+ return napp->player()->getTime();
+}
+
+int NIF::state()
+{
+ if (napp->player()->isPlaying())
+ return 2;
+ if (napp->player()->isPaused())
+ return 1;
+
+ return 0; // default to stopped
+}
+
+QString NIF::lengthString()
+{
+ return napp->player()->current() ? napp->player()->current().lengthString() : "";
+}
+
+QString NIF::timeString()
+{
+ return napp->player()->lengthString();
+}
+
+QString NIF::title()
+{
+ return napp->player()->current() ? napp->player()->current().title() : "";
+}
+
+void NIF::setCurrentProperty(const QString &key, const QString &value)
+{
+ if (!napp->player()->current()) return;
+
+ napp->player()->current().setProperty(key, value);
+}
+
+QString NIF::currentProperty(const QString &key)
+{
+ if (!napp->player()->current()) return "";
+
+ return napp->player()->current().property(key);
+}
+
+void NIF::clearCurrentProperty(const QString &key)
+{
+ if (!napp->player()->current()) return;
+
+ return napp->player()->current().clearProperty(key);
+}
+
+
+QCString NIF::visStack()
+{
+ return napp->player()->engine()->visualizationStack()->toString().c_str();
+}
+
+QCString NIF::session()
+{
+ return napp->player()->engine()->session()->toString().c_str();
+}
+
+// adds one file to the playlist
+void NIF::addFile(const QString& f, bool autoplay)
+{
+ napp->player()->openFile(f, false, autoplay);
+}
+
+// Adds a bunch of files to the playlist
+void NIF::addFile(const QStringList &f, bool autoplay)
+{
+ for (QStringList::ConstIterator it = f.begin(); it != f.end(); ++it )
+ napp->player()->openFile(*it, false, autoplay);
+}
+
+void NIF::loadPlugin(const QString &spec)
+{
+ napp->libraryLoader()->add(spec);
+}
+
+QStringList NIF::availablePlugins() {
+ QStringList available_spec_files;
+ QValueList<NoatunLibraryInfo> available;
+
+ available = napp->libraryLoader()->available();
+
+ QValueList<NoatunLibraryInfo>::iterator it;
+ for (it = available.begin();it != available.end();it++) {
+ available_spec_files += (*it).specfile;
+ }
+
+ return available_spec_files;
+}
+
+QStringList NIF::loadedPlugins() {
+ QStringList loaded_spec_files;
+ QValueList<NoatunLibraryInfo> loaded;
+
+ loaded = napp->libraryLoader()->loaded();
+
+ QValueList<NoatunLibraryInfo>::iterator it;
+ for (it = loaded.begin();it != loaded.end();it++) {
+ loaded_spec_files += (*it).specfile;
+ }
+
+ return loaded_spec_files;
+}
+
+bool NIF::unloadPlugin(const QString &spec)
+{
+ return napp->libraryLoader()->remove(spec);
+}
+
+QStringList NIF::mimeTypes()
+{
+ return napp->mimeTypes();
+}
+
+QCString NIF::version()
+{
+ return napp->version();
+}
+
+void NIF::newSongPlaying()
+{
+ kapp->dcopClient()->emitDCOPSignal("newFile()", QByteArray());
+}
+
+void NIF::clear()
+{
+ napp->playlist()->clear();
+}
diff --git a/noatun/modules/dcopiface/dcopiface.h b/noatun/modules/dcopiface/dcopiface.h
new file mode 100644
index 00000000..4b9fe5e7
--- /dev/null
+++ b/noatun/modules/dcopiface/dcopiface.h
@@ -0,0 +1,104 @@
+#ifndef DCOPIFACE_H
+#define DCOPIFACE_H
+
+#include <noatun/player.h>
+#include <noatun/plugin.h>
+
+#include <dcopobject.h>
+#include <kdemacros.h>
+
+class KDE_EXPORT NIF : public Plugin, public DCOPObject
+{
+K_DCOP
+
+public:
+ NIF();
+ ~NIF();
+
+private slots:
+ void newSongPlaying();
+
+k_dcop:
+ void toggleListView();
+ void handleButtons();
+ void removeCurrent();
+
+ void back();
+ void stop();
+ void play();
+ void playpause();
+ void forward();
+ void skipTo(int);
+ void loop();
+
+ void setVolume(int);
+ int volume();
+ void volumeUp();
+ void volumeDown();
+ void toggleMute();
+
+ /**
+ * length in milliseconds
+ **/
+ int length();
+ /**
+ * position in milliseconds
+ **/
+ int position();
+
+ /**
+ * 0 stopped
+ * 1 paused
+ * 2 playing
+ **/
+ int state();
+
+ QString lengthString();
+ QString timeString();
+
+ QString title();
+
+ /**
+ * set a property for the current song
+ **/
+ void setCurrentProperty(const QString &key, const QString &value);
+ /**
+ * get a property from the current song
+ **/
+ QString currentProperty(const QString &key);
+ /**
+ * clear a property from the current song
+ **/
+ void clearCurrentProperty(const QString &key);
+
+ QCString visStack();
+ QCString session();
+
+ void addFile(const QString& f, bool autoplay);
+ void addFile(const QStringList &f, bool autoplay);
+
+ void loadPlugin(const QString &specFile);
+ QStringList availablePlugins();
+ QStringList loadedPlugins();
+ bool unloadPlugin(const QString &specFile);
+
+ QStringList mimeTypes();
+ QCString version();
+
+ /**
+ * clear the playlist
+ **/
+ void clear();
+private:
+ int mLastVolume; // remember volume for mute/unmute
+
+#ifdef DOCUMENTATION_BLEH_BLEH_DONT_TRY_COMPILING_THIS
+signals:
+ void exiting();
+ void newFile();
+
+#endif
+};
+
+#endif
+
diff --git a/noatun/modules/dcopiface/dcopiface.plugin b/noatun/modules/dcopiface/dcopiface.plugin
new file mode 100644
index 00000000..d98a661f
--- /dev/null
+++ b/noatun/modules/dcopiface/dcopiface.plugin
@@ -0,0 +1,137 @@
+Filename=noatun_dcopiface.la
+Author=Charles Samuels
+Site=http://www.derkarl.org/noatun
+Email=charles@kde.org
+Type=hidden
+License=Artistic
+Name=DCOP Interface
+Name[af]=Dcop Koppelvlak
+Name[ar]=واجهة DCOP
+Name[az]=DCOP Ara üzü
+Name[bn]=ডিকপ ইন্টারফেস
+Name[br]=Etrefas DCOP
+Name[ca]=Interfície DCOP
+Name[cs]=Rozhraní DCOP
+Name[cy]=Rhyngwyneb DCOP
+Name[da]=DCOP-grænseflade
+Name[de]=DCOP-Schnittstelle
+Name[el]=Διασύνδεση DCOP
+Name[eo]=DCOP-interfaco
+Name[es]=Interfaz de DCOP
+Name[et]=DCOP liides
+Name[eu]=DCOP interfazea
+Name[fa]=واسط DCOP
+Name[fi]=DCOP-rajapinta
+Name[fr]=Interface DCOP
+Name[ga]=Comhéadan DCOP
+Name[gl]=Interface DCOP
+Name[he]=ממשק DCOP
+Name[hi]= डीकॉप इंटरफेस
+Name[hr]=DCOP sučelje
+Name[hu]=DCOP-felület
+Name[is]=DCOP aðgangur
+Name[it]=Interfaccia DCOP
+Name[ja]=DCOP インターフェース
+Name[kk]=DCOP интерфейсі
+Name[km]=ចំណុច​ប្រទាក់ DCOP
+Name[ko]=DCOP 인터페이스
+Name[lt]=DCOP sąsaja
+Name[lv]=DCOP Starpseja
+Name[mk]=Интерфејс DCOP
+Name[ms]=Antaramuka DCOP
+Name[mt]=Interfaċċja DCOP
+Name[nb]=DCOP-grensesnitt
+Name[nds]=DCOP-Koppelsteed
+Name[ne]=DCOP इन्टरफेस
+Name[nl]=DCOP-interface
+Name[nn]=DCOP-grensesnitt
+Name[pa]=DCOP ਇੰਟਰਫੇਸ
+Name[pl]=Interfejs DCOP
+Name[pt]=Interface do DCOP
+Name[pt_BR]=Interface DCOP
+Name[ro]=Interfaţă DCOP
+Name[ru]=Интерфейс DCOP
+Name[se]=DCOP-lakta
+Name[sk]=Rozhranie DCOP
+Name[sl]=Vmesnik DCOP
+Name[sr]=DCOP интерфејс
+Name[sr@Latn]=DCOP interfejs
+Name[sv]=DCOP-gränssnitt
+Name[ta]=DCOP இடைமுகம்
+Name[tg]=Интерфейси DCOP
+Name[th]=ส่วนติดต่อ DCOP
+Name[tr]=DCOP Arayüzü
+Name[uk]=Інтерфейс DCOP
+Name[uz]=DCOP interfeysi
+Name[uz@cyrillic]=DCOP интерфейси
+Name[xh]=Ujongano lwe DCOP
+Name[zh_CN]=DCOP 接口
+Name[zh_HK]=DCOP 介面
+Name[zh_TW]=DCOP 介面
+Name[zu]=Uxhumano olubhekeneyo lwe DCOP
+Comment=DCOP Interface for Inter-Process Communication
+Comment[af]=Dcop Koppelvlak vir Inter-Process Kommunikasie
+Comment[ar]=واجهة DCOP لإستعراض تواصل عمليات النظام مع بعضها
+Comment[az]=IPC üçün DCOP Ara üzü
+Comment[bg]=Интерфейс за комуникация между процесите
+Comment[bn]=আন্তঃপ্রক্রিয়া যোগাযোগের জন্য ডিকপ ইন্টারফেস
+Comment[bs]=DCOP interfejs za međuprocesnu komunikaciju
+Comment[ca]=Interfície DCOP per la comunicació entre processos
+Comment[cs]=Rozhraní DCOP pro mezi procesovou komunikaci
+Comment[cy]=Rhyngwyneb DCOP ar gyfer Cyfathrebu Rhyngbrosesol
+Comment[da]=DCOP-grænseflade for interproceskommunikation
+Comment[de]=DCOP-Schnittstelle für die Kommunikation zwischen Prozessen
+Comment[el]=Διασύνδεση DCOP για επικοινωνία μεταξύ διεργασιών (IPC)
+Comment[eo]=DCOP-interfaco por interproceza komunikado
+Comment[es]=Interfaz de DCOP para comunicaciones entre procesos (IPC)
+Comment[et]=DCOP liides protsessidevahelise ühenduse loomiseks
+Comment[eu]=DCOP interfazea prozesuen arteko komunikaziorako
+Comment[fa]=واسط DCOP برای ارتباط درون فرایند
+Comment[fi]=DCOP-rajapinta sisäiseen kommunikointiin
+Comment[fr]=Interface DCOP pour les communications entre processus
+Comment[ga]=Comhéadan DCOP le haghaidh cumarsáide idirphróiseas
+Comment[gl]=Interface DCOP para Comuicación Entre Procesos
+Comment[he]=ממשק DCOP לתקשורת בין-תהליכית
+Comment[hi]=इंटर-प्रोसेस कम्यूनिकेशन के लिए डीकॉप इंटरफेस
+Comment[hr]=DCOP sučelje za komunikaciju među procesima
+Comment[hu]=DCOP-felület IPC-hívásokhoz
+Comment[is]=DCOP aðgangur svo ferlin geti talað saman
+Comment[it]=Interfaccia DCOP per le comunicazioni tra processi (IPC)
+Comment[ja]=プロセス間通信に使われる DCOP インターフェース
+Comment[kk]=Процесаралық қатынаудың DCOP интерфейсі
+Comment[km]=ចំណុច​ប្រទាក់ DCOP សម្រាប់​ការ​ទំនាក់​ទំនង​ខាង​ក្នុង​ដំណើរការ
+Comment[ko]=프로세스 사이에 통신을 주고 받을 수 있게 해주는 DCOP 인터페이스
+Comment[lt]=DCOP sąsaja tarpprocesiniam ryšiui
+Comment[lv]=DCOP Starpseja Iekšējo-Procesu komunikācijām
+Comment[mk]=Интерфејс DCOP за комуникација помеѓу процесите
+Comment[ms]=Antaramuka DCOP untuk Komunikasi Antara Proses
+Comment[mt]=Interfaċċja DCOP għal komunikazzjoni bejn proċessi (IPC)
+Comment[nb]=DCOP grensesnitt for interprosesskommunikasjon
+Comment[nds]=DCOP-Koppelsteed för Kommunikatschoon twischen Perzessen
+Comment[ne]=आन्तरिक-प्रक्रिया सञ्चारका लागि DCOP इन्टरफेस
+Comment[nl]=DCOP-interface voor interprocescommunicatie
+Comment[nn]=DCOP-grensesnitt for interprosesskommunikasjon
+Comment[pl]=Interfejs DCOP dla Komunikacji Międzyprocesowej (IPC)
+Comment[pt]=Interface do DCOP para a comunicação entre os processos
+Comment[pt_BR]=Interface DCOP para comunicações inter-processos
+Comment[ro]=Interfaţă DCOP pentru comunicaţie interprocese
+Comment[ru]=Интерфейс с DCOP для межпроцессного взаимодействия
+Comment[se]=DCOP-lakta proseassaidgaskkasaš gulahallama dihte
+Comment[sk]=Rozhranie DCOP pre komunikáciu medzi procesmi
+Comment[sl]=Vmesnik DCOP za medprocesno komunikacijo
+Comment[sr]=DCOP интерфејс за међупроцесну комуникацју
+Comment[sr@Latn]=DCOP interfejs za međuprocesnu komunikacju
+Comment[sv]=DCOP-gränssnitt för kommunikation mellan processer
+Comment[ta]=செயல்களுக்கிடைப்பட்ட தகவல் பரிவர்த்தனைக்கான ODBC இடைமுகம்
+Comment[tg]=Интерфейси DCOP барои Алоқаи Ҷараёни Интернет
+Comment[th]=ส่วนติดต่อ DCOP สำหรับการสื่อสารระหว่างโปรเซส
+Comment[tr]=IPC için DCOP Arayüzü
+Comment[uk]=Інтерфейс DCOP для взаємодії процесів (IPC)
+Comment[uz]=Jarayonlararo bogʻlanish uchun DCOP interfeysi
+Comment[uz@cyrillic]=Жараёнлараро боғланиш учун DCOP интерфейси
+Comment[ven]=DCOP Interface ya tshitenwa tsha vhudavhudzani
+Comment[xh]=Ujongano lwe DCOP-lothungelwano lwangaphakathi oluqhubekayo
+Comment[zh_CN]=进程间通信的 DCOP 接口
+Comment[zh_HK]=用於行程間通訊的 DCOP 介面
+Comment[zh_TW]=程序程式間通訊的 DCOP 介面
+Comment[zu]=Uxhumano olubhekene lwe DCOP-lothungelwano lwangaphakathi oluqhubekayo
diff --git a/noatun/modules/excellent/Makefile.am b/noatun/modules/excellent/Makefile.am
new file mode 100644
index 00000000..a2558676
--- /dev/null
+++ b/noatun/modules/excellent/Makefile.am
@@ -0,0 +1,18 @@
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_excellent.la
+
+noatun_excellent_la_SOURCES = noatunui.cpp\
+ userinterface.cpp
+
+noatun_excellent_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_excellent_la_LIBADD = $(LIB_KFILE) \
+ $(top_builddir)/noatun/library/libnoatun.la \
+ $(top_builddir)/noatun/library/libnoatuncontrols.la \
+ -lqtmcop -lkmedia2_idl -lsoundserver_idl
+
+noatun_excellent_la_METASOURCES = AUTO
+
+noinst_HEADERS = userinterface.h
+
+noatun_modules_excellent_DATA = excellent.plugin excellentui.rc
+noatun_modules_excellentdir = $(kde_datadir)/noatun
diff --git a/noatun/modules/excellent/excellent.plugin b/noatun/modules/excellent/excellent.plugin
new file mode 100644
index 00000000..89f747c0
--- /dev/null
+++ b/noatun/modules/excellent/excellent.plugin
@@ -0,0 +1,120 @@
+Filename=noatun_excellent.la
+Author=Neil Stevens
+Site=http://noatun.kde.org/plugins/excellent/
+Email=neil@qualityassistant.com
+Type=userinterface
+License=X11-like
+Name=Excellent
+Name[af]=Uitstekende
+Name[ar]=ممتاز
+Name[az]=Mükəmməl
+Name[ca]=Excel·lent
+Name[cs]=Vynikající
+Name[cy]=Ardderchog
+Name[de]=Hervorragend
+Name[el]=Έξοχο
+Name[eo]=Brila
+Name[es]=Excelente
+Name[et]=Suurepärane
+Name[eu]=Aparta
+Name[fa]=عالی
+Name[fi]=Erinomainen
+Name[ga]=Sármhaith
+Name[gl]=Excelente
+Name[he]=מעולה
+Name[hi]=एक्सेलेंट
+Name[hr]=Odlično
+Name[is]=Frábært
+Name[it]=Eccellente
+Name[ja]=エクセレント
+Name[km]=ល្អ​ឥត​ខ្ចោះ
+Name[lt]=Puikus
+Name[lv]=Lielisks
+Name[mk]=Одличен
+Name[mt]=Eċċellenti
+Name[nb]=Fantastisk
+Name[nds]=Goot
+Name[ne]=दक्ष
+Name[nn]=Fantastisk
+Name[pa]=ਸਰਵੋਤਮ
+Name[pl]=Doskonały
+Name[pt]=Excelente
+Name[pt_BR]=Excelente
+Name[ro]=Excelent
+Name[ru]=Превосходный
+Name[sk]=Fantastický
+Name[sv]=Utmärkt
+Name[ta]=அற்புதம்
+Name[tg]=Олиҷаноб
+Name[th]=ยอดเยี่ยม
+Name[tr]=Mükemmel
+Name[uk]=Чудовий
+Name[ven]=Zwavhudi
+Name[xh]=Hle kwaphela
+Name[zh_CN]=优秀的
+Name[zh_HK]=極佳的
+Name[zh_TW]=完美的
+Name[zu]=Kuhle kakhulu
+Comment=A very ordinary, and therefore very usable, interface
+Comment[af]='n baie gewone, en daarom baie bruikbaar, koppelvlak
+Comment[ar]=واجهة استخدام عادية ، و بالتالي ممتازة
+Comment[az]=Olduqca bəsit və istifadəli bir axtar üz
+Comment[bg]=Обикновен и поради това много полезен интерфейс
+Comment[bs]=Vrlo uobičajen i zato vrlo upotrebljiv interfejs
+Comment[ca]=Una interfície molt normal, i per tant, molt usable
+Comment[cs]=Velmi obvyklé, and proto velmi použitelné rozhraní
+Comment[cy]=Rhyngwyneb Cyffredin iawn, ac felly defnyddiol iawn
+Comment[da]=En meget ordinær, og derfor meget nyttig, grænseflade
+Comment[de]=Eine ganz normale und folglich sehr brauchbare Oberfläche
+Comment[el]=Ένα πολύ συνηθισμένο, και έτσι πολύ εύχρηστο περιβάλλον
+Comment[eo]=Ordinara kaj do tre uzebla interfaco
+Comment[es]=Una interfaz muy común y por tanto muy fácil de usar
+Comment[et]=Väga tavaline ja lihtsasti kasutatav kasutajaliides
+Comment[eu]=Interfaze arrunta eta oso erabilgarria
+Comment[fa]=یک واسط معمولی، و بنابراین بسیار مفید
+Comment[fi]=Hyvin tavallinen ja käyttökelpoinen käyttöliittymä
+Comment[fr]=Une interface très ordinaire et donc très utilisable
+Comment[ga]=Comhéadan an-choitianta, agus dá bharr sin, an-inúsáidte
+Comment[gl]=Unha interface moi usual e polo tanto moi usábel
+Comment[he]=ממשק מאוד פשוט, ועל כן מאוד שימושי
+Comment[hi]=एक बहुत साधारण, अतः बहुत उपयोगी इंटरफेस
+Comment[hr]=Vrlo obično, i prema tome vrlo upotrebljivo, sučelje
+Comment[hu]=Egy jól ismert, ezért jól használható felület
+Comment[is]=Mjög venjulegt og notanlegt viðmót
+Comment[it]=Un'interfaccia molto ordinaria e molto usabile
+Comment[ja]=非常にありふれているが、だからこそ使いやすいインターフェース
+Comment[kk]=Ең кәдімгі, сондықтан жиі қолданылатын интерфейс
+Comment[km]=ចំណុច​ប្រទាក់​សាមញ្ញ​បំផុត ហើយ​ក៏​ងាយស្រួល​ប្រើ​ដែរ
+Comment[ko]=아주 평범하고 쓸만한 인터페이스
+Comment[lt]=Labai paprasta ir dėlto labai naudinga sąsaja
+Comment[lv]=Ļoti vienkārša, un tādēļ ļoti izmantojama, starpseja
+Comment[mk]=Мошне обичен, и според тоа мошне корисен интерфејс
+Comment[ms]=Antaramuka yang sangat biasa tetapi berguna
+Comment[mt]=Interfaċċja ordijarja ħafna u għalhekk utli ħafna
+Comment[nb]=Et svært vanlig og brukervennlig grensesnitt
+Comment[nds]=En bannig normaal un dorüm bannig eenfach to bruken Böversiet
+Comment[ne]=साधारण भएकोले धेरै उपयोगि इन्टरफेस
+Comment[nl]=Een heel gewone, en daarom erg bruikbare, interface
+Comment[nn]=Eit svært normalt og difor lettbruka grensesnitt
+Comment[pl]=Bardzo zwyczajny, ale przez to bardzo użyteczny motyw
+Comment[pt]=Uma interface básica e, por isso, muito fácil de usar
+Comment[pt_BR]=Uma interface bem ordinária e portanto muito utilizável
+Comment[ro]=O interfaţă foarte simplă şi foarte utilă
+Comment[ru]=Самый обычный, а потому и очень полезный интерфейс
+Comment[se]=Hui dábálaš ja dannege geavahahtti lakta
+Comment[sk]=Veľmi obyčajné, a preto použiteľné, rozhranie
+Comment[sl]=Zelo običajen in zato zelo uporaben vmesnik
+Comment[sr]=Веома обичан, стога и веома лак за коришћење, интерфејс
+Comment[sr@Latn]=Veoma običan, stoga i veoma lak za korišćenje, interfejs
+Comment[sv]=Ett väldigt vanligt och därför väldigt användbart gränssnitt
+Comment[ta]=மிக எளிய, பயனுள்ள இடைமுகம்
+Comment[tg]=Интерфейси хеле содда ва бинобар бисёр фоиданок
+Comment[th]=ส่วนติดต่อแบบง่ายๆ และด้วยเหตุนั้นก็เลยมีประโยชน์อย่างมาก
+Comment[tr]=Oldukça basit ve kullanışlı bir arayüz
+Comment[uk]=Дуже звичний, і тому дуже зручний, інтерфейс
+Comment[ven]=Zwithu zwazwo, zwo ralo zwa vhuthogwa, interface
+Comment[xh]=Eqheleke kakhulu, neluncedo kakhulu, injongano
+Comment[zh_CN]=非常普通,因而也非常有用的接口
+Comment[zh_HK]=非常普通但是很實用的介面
+Comment[zh_TW]=非常普通但是很實用的介面
+Comment[zu]=Ejwayelekile ngakhoke esebenzisekayo,uxhumano olubhekeneyo
diff --git a/noatun/modules/excellent/excellentui.rc b/noatun/modules/excellent/excellentui.rc
new file mode 100644
index 00000000..f9c43c70
--- /dev/null
+++ b/noatun/modules/excellent/excellentui.rc
@@ -0,0 +1,45 @@
+<!DOCTYPE kpartgui>
+<!--
+vim: syntax=xml
+-->
+<kpartgui name="noatunexcellent" version="4">
+<MenuBar>
+ <Menu name="file" noMerge="1"><text>&amp;File</text>
+ <Action name="file_open"/>
+ <Separator lineSeparator="true"/>
+ <Action name="file_quit"/>
+ </Menu>
+ <Menu name="go_music" noMerge="1"><text>&amp;Go</text>
+ <Action name="back"/>
+ <Action name="stop"/>
+ <Action name="play"/>
+ <Action name="pause"/>
+ <Action name="forward"/>
+ </Menu>
+ <Menu name="settings" noMerge="1"><text>&amp;Settings</text>
+ <Merge name="StandardToolBarMenuHandler" />
+ <Action name="options_show_toolbar"/>
+ <Action name="options_show_menubar"/>
+ <Action name="show_playlist"/>
+ <Action name="show_volumecontrol"/>
+ <Separator lineSeparator="true"/>
+ <Action name="effects"/>
+ <Action name="equalizer"/>
+ <Action name="loop_style"/>
+ <Separator lineSeparator="true"/>
+ <Action name="options_configure"/>
+ </Menu>
+</MenuBar>
+<Toolbar name="main"><text>Main Toolbar</text>
+ <Action name="back"/>
+ <Action name="stop"/>
+ <Action name="play"/>
+ <Action name="pause"/>
+ <Action name="forward"/>
+ <Separator lineSeparator="true"/>
+ <Action name="file_open"/>
+ <Action name="show_playlist"/>
+ <Separator lineSeparator="true"/>
+ <Action name="loop_style"/>
+</Toolbar>
+</kpartgui>
diff --git a/noatun/modules/excellent/noatunui.cpp b/noatun/modules/excellent/noatunui.cpp
new file mode 100644
index 00000000..a103268d
--- /dev/null
+++ b/noatun/modules/excellent/noatunui.cpp
@@ -0,0 +1,32 @@
+// noatunui.cpp
+//
+// Copyright (C) 2000 Neil Stevens <multivac@fcmail.com>
+// Copyright (C) 1999 Charles Samuels <charles@kde.org>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#include "userinterface.h"
+
+extern "C" KDE_EXPORT Plugin *create_plugin()
+{
+ return new Excellent();
+}
diff --git a/noatun/modules/excellent/userinterface.cpp b/noatun/modules/excellent/userinterface.cpp
new file mode 100644
index 00000000..7f218e98
--- /dev/null
+++ b/noatun/modules/excellent/userinterface.cpp
@@ -0,0 +1,394 @@
+// userinterface.cpp
+//
+// Copyright (C) 2001 Neil Stevens <neil@qualityassistant.com>
+// Copyright (C) 1999 Charles Samuels <charles@kde.org>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <noatun/effects.h>
+#include <noatun/app.h>
+#include <noatun/controls.h>
+#include <noatun/pref.h>
+#include <noatun/player.h>
+
+#include "userinterface.h"
+
+#include <kbuttonbox.h>
+#include <kconfig.h>
+#include <kfiledialog.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kmenubar.h>
+#include <kmessagebox.h>
+#include <kpixmapeffect.h>
+#include <kpopupmenu.h>
+#include <kstatusbar.h>
+#include <kstdaction.h>
+#include <kwin.h>
+#include <kurldrag.h>
+
+#include <qbitmap.h>
+#include <qdragobject.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qobjectlist.h>
+#include <qobjectdict.h>
+#include <qpushbutton.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+
+Excellent::Excellent()
+ : KMainWindow(0, "NoatunExcellent")
+ , UserInterface()
+{
+ setAcceptDrops(true);
+
+ KStdAction::quit(napp, SLOT(quit()), actionCollection());
+ KStdAction::open(napp, SLOT(fileOpen()), actionCollection());
+ setStandardToolBarMenuEnabled(true);
+
+ menubarAction = KStdAction::showMenubar(this, SLOT(showMenubar()), actionCollection());
+ KStdAction::preferences(napp, SLOT(preferences()), actionCollection());
+
+ // buttons
+ NoatunStdAction::back(actionCollection(), "back");
+ NoatunStdAction::stop(actionCollection(), "stop");
+ NoatunStdAction::play(actionCollection(), "play");
+ NoatunStdAction::pause(actionCollection(), "pause");
+ NoatunStdAction::forward(actionCollection(), "forward");
+ NoatunStdAction::playlist(actionCollection(), "show_playlist");
+
+ volumeAction = new KToggleAction(i18n("Show &Volume Control"), 0, this, SLOT(showVolumeControl()), actionCollection(), "show_volumecontrol");
+ volumeAction->setCheckedState(i18n("Hide &Volume Control"));
+ NoatunStdAction::effects(actionCollection(), "effects");
+ NoatunStdAction::equalizer(actionCollection(), "equalizer");
+
+ NoatunStdAction::loop(actionCollection(), "loop_style");
+
+ createGUI("excellentui.rc");
+
+ napp->pluginActionMenu()->plug(menuBar(),3);
+ toolBar("mainToolBar")->hide();
+
+ // Who needs Qt Designer?
+ mainFrame = new QHBox(this);
+ mainFrame->setSpacing(KDialog::spacingHint());
+ mainFrame->setMargin(0);
+ slider = new L33tSlider(0, 1000, 10, 0, L33tSlider::Horizontal, mainFrame);
+ slider->setTickmarks(QSlider::NoMarks);
+
+ elapsed = new QLabel(mainFrame);
+ QFont labelFont = elapsed->font();
+ labelFont.setPointSize(24);
+ labelFont.setBold(true);
+ QFontMetrics labelFontMetrics = labelFont;
+ elapsed->setFont(labelFont);
+ elapsed->setAlignment(AlignCenter | AlignVCenter | ExpandTabs);
+ elapsed->setText("--:--");
+ elapsed->setFixedHeight(labelFontMetrics.height());
+ elapsed->setMinimumWidth(elapsed->sizeHint().width());
+
+ // Doing this makes the slider the same heigh as the font. This is just plain wrong...
+ //slider->setFixedHeight(labelFontMetrics.height());
+
+ setCentralWidget(mainFrame);
+
+ total = new QLabel(statusBar());
+ labelFont = total->font();
+ labelFont.setBold(true);
+ total->setFont(labelFont);
+ total->setAlignment(AlignCenter | AlignVCenter | ExpandTabs);
+ total->setText("--:--");
+ total->setMinimumWidth(total->sizeHint().width());
+ total->setText("");
+
+ statusBar()->addWidget(total, 0, true);
+ statusBar()->show();
+
+ connect( napp, SIGNAL(hideYourself()), this, SLOT(hide()) );
+ connect( napp, SIGNAL(showYourself()), this, SLOT(show()) );
+
+ connect(napp->player(), SIGNAL(playing()), this, SLOT(slotPlaying()));
+ connect(napp->player(), SIGNAL(stopped()), this, SLOT(slotStopped()));
+ connect(napp->player(), SIGNAL(paused()), this, SLOT(slotPaused()));
+ napp->player()->handleButtons();
+
+ connect(napp->player(), SIGNAL(timeout()), this, SLOT(slotTimeout()));
+ connect(napp->player(), SIGNAL(loopTypeChange(int)), this, SLOT(slotLoopTypeChanged(int)));
+
+ /* This skipToWrapper is needed to pass milliseconds to Player() as everybody
+ * below the GUI is based on milliseconds instead of some unprecise thingy
+ * like seconds or mille */
+ connect(slider, SIGNAL(userChanged(int)), this, SLOT(skipToWrapper(int)));
+ connect(this, SIGNAL(skipTo(int)), napp->player(), SLOT(skipTo(int)));
+
+ connect(slider, SIGNAL(sliderMoved(int)), SLOT(sliderMoved(int)));
+
+ setCaption("Noatun");
+ setIcon(SmallIcon("noatun"));
+ //slotLoopTypeChanged(Player::None);
+ changeStatusbar();
+ handleLengthString("--:--/--:--");
+
+ setMinimumWidth(250);
+ resize(300, 75);
+
+ KConfig &config = *KGlobal::config();
+
+ toolBar("main")->applySettings(&config, "Excellent main");
+
+ config.setGroup("excellent");
+
+ volumeSlider = 0;
+ volumeAction->setChecked( config.readBoolEntry("volumeShown", false) );
+ showVolumeControl();
+
+ menubarAction->setChecked( config.readBoolEntry("menuShown", true) );
+ showMenubar();
+
+ applyMainWindowSettings(&config, "excellent");
+
+ switch((NET::MappingState)config.readNumEntry("mappingState", (int)NET::Visible))
+ {
+ case NET::Visible:
+ showNormal();
+ break;
+ case NET::Withdrawn:
+ if (napp->libraryLoader()->isLoaded("systray.plugin"))
+ hide();
+ break;
+ case NET::Iconic:
+ showMinimized();
+ break;
+ }
+
+ for (QPtrListIterator<QObject> i(*children()); i.current(); ++i)
+ (*i)->installEventFilter(this);
+}
+
+Excellent::~Excellent()
+{
+ KConfig &config = *KGlobal::config();
+ saveMainWindowSettings(&config, "excellent");
+ toolBar("main")->saveSettings(&config, "Excellent main");
+ config.setGroup("excellent");
+ config.writeEntry("volumeShown", volumeAction->isChecked());
+ config.writeEntry("menuShown", menubarAction->isChecked());
+ config.writeEntry("width", width());
+ config.writeEntry("height", height());
+ config.sync();
+}
+
+void Excellent::showEvent(QShowEvent *e)
+{
+ KConfig *config = KGlobal::config();
+ config->setGroup("excellent");
+ config->writeEntry("mappingState", NET::Visible);
+ config->sync();
+
+ KMainWindow::showEvent(e);
+}
+
+void Excellent::hideEvent(QHideEvent *e)
+{
+ KConfig *config = KGlobal::config();
+ config->setGroup("excellent");
+ config->writeEntry("mappingState", NET::Withdrawn);
+ config->sync();
+
+ KMainWindow::hideEvent(e);
+}
+
+void Excellent::closeEvent(QCloseEvent *)
+{
+ unload();
+}
+
+void Excellent::dragEnterEvent(QDragEnterEvent *event)
+{
+ // accept uri drops only
+ event->accept(KURLDrag::canDecode(event));
+}
+
+void Excellent::dropEvent(QDropEvent *event)
+{
+ KURL::List uri;
+ if (KURLDrag::decode(event, uri))
+ {
+ for (KURL::List::Iterator i = uri.begin(); i != uri.end(); ++i)
+ napp->player()->openFile(*i, false);
+ }
+}
+
+bool Excellent::eventFilter(QObject *o, QEvent *e)
+{
+ if (e->type() == QEvent::Wheel)
+ {
+ wheelEvent(static_cast<QWheelEvent*>(e));
+ return true;
+ }
+ return QWidget::eventFilter(o, e);
+}
+
+void Excellent::wheelEvent(QWheelEvent *e)
+{
+ int delta=e->delta();
+ napp->player()->setVolume(napp->player()->volume() + delta/120*2);
+}
+
+void Excellent::slotPlaying()
+{
+ slider->setEnabled(true);
+ changeStatusbar();
+}
+
+void Excellent::slotStopped()
+{
+ slider->setEnabled(false);
+ if(!napp->player()->current()) return;
+ changeStatusbar();
+ slider->setValue(0);
+ handleLengthString("--:--/--:--");
+}
+
+void Excellent::slotPaused()
+{
+ slider->setEnabled(true);
+ changeStatusbar();
+}
+
+void Excellent::slotTimeout()
+{
+ if(volumeSlider) volumeSlider->setValue(100 - napp->player()->volume());
+
+ if (!slider->currentlyPressed())
+ handleLengthString(napp->player()->lengthString());
+
+ if(!napp->player()->current()) return;
+ if(slider->currentlyPressed()) return;
+
+ slider->setRange(0, (int)napp->player()->getLength() / 1000 );
+ slider->setValue((int)napp->player()->getTime() / 1000 );
+
+ changeStatusbar();
+}
+
+void Excellent::sliderMoved(int seconds)
+{
+ if(napp->player()->current())
+ handleLengthString(napp->player()->lengthString(seconds * 1000));
+}
+
+void Excellent::skipToWrapper(int second) // wrap int to int _and_ seconds to mseconds
+{
+ emit skipTo((int)(second*1000));
+}
+
+void Excellent::slotLoopTypeChanged(int type)
+{
+ static const int time = 2000;
+ switch (type)
+ {
+ case Player::None:
+ statusBar()->message(i18n("No looping"), time);
+ break;
+ case Player::Song:
+ statusBar()->message(i18n("Song looping"), time);
+ break;
+ case Player::Playlist:
+ statusBar()->message(i18n("Playlist looping"), time);
+ break;
+ case Player::Random:
+ statusBar()->message(i18n("Random play"), time);
+ }
+}
+
+void Excellent::showMenubar(void)
+{
+ if(menubarAction->isChecked())
+ {
+ menuBar()->show();
+ }
+ else
+ {
+ KMessageBox::information(this, i18n("<qt>Press %1 to show the menubar.</qt>").arg(menubarAction->shortcut().toString()), QString::null, "Hide Menu warning");
+ menuBar()->hide();
+ }
+}
+
+void Excellent::showVolumeControl(void)
+{
+ if(volumeAction->isChecked())
+ growVolumeControl();
+ else
+ shrinkVolumeControl();
+}
+
+void Excellent::changeStatusbar(void)
+{
+ if(!napp->player()->current().isNull())
+ statusBar()->message(napp->player()->current().title());
+}
+
+void Excellent::handleLengthString(const QString &text)
+{
+ if(text.right(5) == "00:00" && text.left(5) == "00:00")
+ {
+ elapsed->setText("--:--");
+ total->setText("--:--");
+ }
+ else
+ {
+ // Split the length string in to "current" and "elapsed"
+ QStringList tokens = QStringList::split("/", text);
+
+ elapsed->setText(tokens[0]);
+ total->setText(tokens[1]);
+ }
+}
+
+void Excellent::growVolumeControl(void)
+{
+ volumeSlider = new L33tSlider(0, 100, 10, 0, Vertical, mainFrame);
+ volumeSlider->setValue(100 - napp->player()->volume());
+ volumeSlider->show();
+ connect(volumeSlider, SIGNAL(sliderMoved(int)), SLOT(changeVolume(int)));
+ connect(volumeSlider, SIGNAL(userChanged(int)), SLOT(changeVolume(int)));
+}
+
+void Excellent::shrinkVolumeControl(void)
+{
+ delete volumeSlider;
+ volumeSlider = 0;
+}
+
+void Excellent::changeVolume(int slider)
+{
+ napp->player()->setVolume(100 - slider);
+}
+
+#include "userinterface.moc"
diff --git a/noatun/modules/excellent/userinterface.h b/noatun/modules/excellent/userinterface.h
new file mode 100644
index 00000000..588db6e4
--- /dev/null
+++ b/noatun/modules/excellent/userinterface.h
@@ -0,0 +1,101 @@
+// userinterface.h
+//
+// Copyright (C) 2000, 2001 Neil Stevens <multivac@fcmail.com>
+// Copyright (C) 1999 Charles Samuels <charles@kde.org>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifndef USERINTERFACE_H
+#define USERINTERFACE_H
+
+#include <noatun/plugin.h>
+#include <noatun/app.h>
+#include <noatun/stdaction.h>
+#include <kmainwindow.h>
+
+class KAction;
+class KPopupMenu;
+class KStatusBar;
+class Player;
+class QHBox;
+class QLabel;
+class QSlider;
+class L33tSlider;
+class KToggleAction;
+
+/**
+ * @short Main window class
+ * @author Neil Stevens <multivac@fcmail.com>
+ * @author Charles Samuels <charles@kde.org>
+ */
+class Excellent : public KMainWindow, public UserInterface
+{
+Q_OBJECT
+
+public:
+ Excellent();
+ virtual ~Excellent();
+ void load(const QString& url);
+
+protected:
+ virtual void showEvent(QShowEvent *);
+ virtual void hideEvent(QHideEvent *);
+ virtual void closeEvent(QCloseEvent *);
+ virtual void dragEnterEvent(QDragEnterEvent *);
+ virtual void dropEvent(QDropEvent *);
+ void wheelEvent(QWheelEvent *e);
+ bool eventFilter(QObject *o, QEvent *e);
+
+public slots:
+ void slotPlaying();
+ void slotStopped();
+ void slotPaused();
+
+ void slotTimeout();
+ void sliderMoved(int seconds);
+ void slotLoopTypeChanged(int type);
+ void skipToWrapper(int second);
+
+signals:
+ // emitted by skipToWrapper()
+ void skipTo(int);
+
+private slots:
+ void showVolumeControl(void);
+ void showMenubar(void);
+ void changeStatusbar(void);
+ void changeVolume(int);
+
+ void handleLengthString(const QString &text);
+
+private:
+ void growVolumeControl(void);
+ void shrinkVolumeControl(void);
+
+ QHBox *mainFrame;
+
+ KToggleAction *volumeAction, *menubarAction;
+ L33tSlider *volumeSlider, *slider;
+ QLabel *elapsed, *total;
+};
+
+#endif
diff --git a/noatun/modules/htmlexport/Makefile.am b/noatun/modules/htmlexport/Makefile.am
new file mode 100644
index 00000000..bbea16ab
--- /dev/null
+++ b/noatun/modules/htmlexport/Makefile.am
@@ -0,0 +1,16 @@
+
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_htmlexport.la
+
+noatun_htmlexport_la_SOURCES = htmlexport.cpp
+noinst_HEADERS = htmlexport.h
+
+noatun_htmlexport_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_htmlexport_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la \
+ -lqtmcop -lkmedia2_idl -lsoundserver_idl
+
+noatun_htmlexport_la_METASOURCES = AUTO
+
+
+noatun_modules_htmlexport_DATA = htmlexport.plugin
+noatun_modules_htmlexportdir = $(kde_datadir)/noatun
diff --git a/noatun/modules/htmlexport/TODO b/noatun/modules/htmlexport/TODO
new file mode 100644
index 00000000..6f97ad78
--- /dev/null
+++ b/noatun/modules/htmlexport/TODO
@@ -0,0 +1,3 @@
+Most important TODO items:
+* Read tags before writing the file
+* Clean up the configure dialog and fix remaining issues
diff --git a/noatun/modules/htmlexport/htmlexport.cpp b/noatun/modules/htmlexport/htmlexport.cpp
new file mode 100644
index 00000000..dc48fd8f
--- /dev/null
+++ b/noatun/modules/htmlexport/htmlexport.cpp
@@ -0,0 +1,308 @@
+#include <klocale.h>
+#include <qregexp.h>
+#include <qtextcodec.h>
+#include <kaction.h>
+#include <noatun/stdaction.h>
+#include "htmlexport.h"
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new HTMLExport();
+ }
+}
+
+HTMLExport::HTMLExport(): QObject(0, "HTMLExport"), Plugin()
+{
+ NOATUNPLUGINC(HTMLExport);
+
+ mAction = new KAction(i18n("&Export Playlist..."), "filesaveas", 0,
+ this, SLOT(slotExport()), this, "exportlist");
+ napp->pluginActionMenu()->insert(mAction);
+
+ new Prefs(this);
+ config = KGlobal::config();
+}
+
+HTMLExport::~HTMLExport()
+{
+ napp->pluginActionMenu()->remove(mAction);
+}
+
+void HTMLExport::slotExport()
+{
+ // init readConfig
+ config->setGroup("HTMLExport");
+
+ // get output target
+ KURL url = KFileDialog::getSaveURL(QString::null,
+ "text/html",
+ 0,
+ i18n("Export Playlist"));
+
+ // write into tempfile
+ KTempFile temp;
+ temp.setAutoDelete(true);
+ QFile file(temp.name());
+ file.open(IO_WriteOnly);
+ QTextStream str(&file);
+ str.setCodec(QTextCodec::codecForName("utf8"));
+
+ str << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
+ str << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">" << endl;
+ str << "<!-- Generated by Noatun " << NOATUN_MAJOR << "." << NOATUN_MINOR << "." << NOATUN_PATCHLEVEL << " -->" << endl;
+
+ str << endl;
+
+ str << "<html>" << endl;
+ str << "<head>" << endl;
+ str << "\t<title>" << i18n("Noatun Playlist") << "</title>" << endl;
+
+ str << "\t<style type=\"text/css\">" << endl;
+ str << "\t\t<!--" << endl;
+ // Set the heading color
+ str << "\t\th1 { color:#"<< getColorByEntry("headColor")<<"; }" << endl;
+
+ // Optionally set the background image
+ if (!config->readPathEntry( "bgImgPath" ).stripWhiteSpace().isEmpty()) {
+ // Image
+ str << "\t\tp,li { color:#"<< getColorByEntry("txtColor") << "; }" << endl;
+ str << "\t\tbody { background-image: url(\"" << config->readPathEntry( "bgImgPath" ) << "\"); }" << endl;
+ }
+ else {
+ // Color
+ str << "\t\tp,li,body { color:#"<< getColorByEntry("txtColor");
+ str << "; background-color:#" << getColorByEntry( "bgColor" ) << "; }" << endl;
+ }
+
+ // Links are text colored, too
+ str << "\t\ta { color:#" << getColorByEntry("txtColor") << "; }" << endl;
+ if (getColorByEntry("hoverColor") != getColorByEntry("txtColor"))
+ str << "\t\ta:hover { color:#"<< getColorByEntry("hoverColor")<<"; }"<< endl;
+
+ str << "\t\t-->" << endl;
+ str << "\t</style>" << endl;
+
+ str << "</head>" << endl;
+ str << endl;
+ str << "<body>" << endl;
+ str << "\t<h1>" << i18n("Noatun Playlist") << "</h1>" << endl;
+
+ // Cache the config settings used in the big loop
+ bool link_entries = config->readBoolEntry( "linkEntries" );
+ bool number_entries = config->readBoolEntry( "numberEntries" );
+
+ if (number_entries)
+ str << "\t\t<ol>" << endl;
+ else
+ str << "\t\t<p>" << endl;
+
+
+ for (PlaylistItem item = napp->playlist()->getFirst();item;item = napp->playlist()->getAfter(item)) {
+ str << "\t\t\t";
+
+ if (number_entries)
+ str << "<li>";
+
+ if (link_entries)
+ str << "<a href=\"" << htmlEscape(item.file()) << "\">";
+
+ str << htmlEscape(item.title());
+
+ if (link_entries)
+ str << "</a>";
+
+ if (number_entries)
+ str << "</li>" << endl;
+ else
+ str << "<br />" << endl;
+ }
+
+ if (number_entries)
+ str << "\t\t</ol>" << endl;
+ else
+ str << "\t\t</p>" << endl;
+
+ str << "\t</body>" << endl;
+ str << "</html>" << endl;
+
+ file.close();
+ // tempfile -> userdefined file
+ KIO::NetAccess::upload(temp.name(), url, 0);
+}
+
+QString HTMLExport::htmlEscape(const QString &source) {
+ // Escape characters that need to be escaped
+ QString temp = source;
+
+ temp.replace( QRegExp("&"), "&amp;" );
+ temp.replace( QRegExp("<"), "&lt;" );
+ temp.replace( QRegExp(">"), "&gt;" );
+
+ return temp;
+}
+
+QString HTMLExport::getColorByEntry(QString s)
+{
+ QString res;
+ QString tmp;
+ QColor c;
+
+ // init readConfig
+
+ config->setGroup("HTMLExport");
+
+ c = config->readColorEntry( s );
+ tmp = QString::number( c.red(), 16);
+ if (tmp.length()==1) tmp="0"+tmp;
+ res = tmp;
+
+ tmp = QString::number( c.green(), 16);
+ if (tmp.length()==1) tmp="0"+tmp;
+ res += tmp;
+
+ tmp = QString::number( c.blue(), 16);
+ if (tmp.length()==1) tmp="0"+tmp;
+ res += tmp;
+
+ return res;
+
+}
+//////////////////////////////////// Settings ////////////////////////////////////
+
+Prefs::Prefs(QObject *parent)
+ : CModule(i18n("Playlist Export"), i18n("Colors & Settings for HTML Export"), "html", parent)
+{
+
+ // Init Config
+ KConfig *config = KGlobal::config();
+ config->setGroup("HTMLExport");
+
+ // Set default values
+ if ( !config->hasKey( "headColor" ) )
+ config->writeEntry( "headColor", QColor( black ) ) ;
+
+ if ( !config->hasKey( "hoverColor" ) )
+ config->writeEntry( "hoverColor", QColor( black ) );
+
+ if ( !config->hasKey( "bgColor" ) )
+ config->writeEntry( "bgColor", QColor( white ) ) ;
+
+ if ( !config->hasKey( "txtColor" ) )
+ config->writeEntry( "txtColor", QColor( black ) );
+
+ config->sync();
+
+ // Draw Stuff and insert Settings
+ QVBoxLayout *top = new QVBoxLayout(this, KDialog::marginHint(),
+ KDialog::spacingHint() );
+
+ colorBox = new QGroupBox( i18n( "HTML Color Settings" ), this, "colorBox" );
+
+ bgcolorLabel = new QGridLayout( colorBox, 2, 5,
+ KDialog::marginHint(), KDialog::spacingHint() );
+
+ headColorSelect = new KColorButton( colorBox, "headColorSelect" );
+
+ hoverColorSelect = new KColorButton( colorBox, "hoverColorSelect" );
+
+ bgColorSelect = new KColorButton( colorBox, "bgColorSelect" );
+
+ txtColorSelect = new KColorButton( colorBox, "txtColorSelect" );
+
+ txtColorLabel = new QLabel( colorBox, "txtColorLabel" );
+ txtColorLabel->setText( i18n( "Text:" ) );
+ txtColorLabel->setAlignment( int( QLabel::AlignVCenter | QLabel::AlignRight ) );
+
+ bgColorLabel = new QLabel( colorBox, "bgColorLabel" );
+ bgColorLabel->setText( i18n( "Background:" ) );
+ bgColorLabel->setAlignment( int( QLabel::AlignVCenter | QLabel::AlignRight ) );
+
+ headColorLabel = new QLabel( colorBox, "headColorLabel" );
+ headColorLabel->setText( i18n( "Heading:" ) );
+ headColorLabel->setAlignment( int( QLabel::AlignVCenter | QLabel::AlignRight ) );
+
+ hoverColorLabel = new QLabel( colorBox, "hoverColorLabel" );
+ hoverColorLabel->setText( i18n( "Link hover:" ) );
+ hoverColorLabel->setAlignment( int( QLabel::AlignVCenter | QLabel::AlignRight ) );
+
+ bgcolorLabel->setRowStretch(0, 1);
+
+ // Makes the spacing nice
+ bgcolorLabel->setColStretch(1, 2);
+ bgcolorLabel->setColStretch(2, 1);
+ bgcolorLabel->setColStretch(4, 2);
+
+ bgcolorLabel->addWidget( txtColorLabel, 0, 0 );
+ bgcolorLabel->addWidget( txtColorSelect, 0, 1 );
+ bgcolorLabel->addWidget( headColorLabel, 1, 0 );
+ bgcolorLabel->addWidget( headColorSelect, 1, 1 );
+ bgcolorLabel->addWidget( bgColorLabel, 0, 3 );
+ bgcolorLabel->addWidget( bgColorSelect, 0, 4 );
+ bgcolorLabel->addWidget( hoverColorLabel, 1, 3 );
+ bgcolorLabel->addWidget( hoverColorSelect, 1, 4 );
+
+
+ // Set up the Background Image frame
+ bgPicBox = new QHGroupBox( i18n( "Background Image"), this, "bgPicBox" );
+
+ // Set up the URL requestor
+ bgPicPath = new KURLRequester ( bgPicBox, "bgPicPath" );
+ bgPicPath->setShowLocalProtocol(true);
+
+ // Set up the URL requestor's file dialog
+ bgPicPath->setMode(KFile::File | KFile::ExistingOnly);
+ bgPicPath->setFilter("image/gif image/jpeg image/gif image/png image/svg+xml image/tiff");
+
+ linkEntries = new QCheckBox( this, "linkEntries" );
+ linkEntries->setText( i18n( "Hyper&link playlist entries to their URL" ) );
+ linkEntries->setTristate( false );
+
+ numberEntries = new QCheckBox( this, "numberEntries" );
+ numberEntries->setText( i18n( "&Number playlist entries" ) );
+ numberEntries->setTristate( false );
+
+ top->addWidget( colorBox );
+ top->addWidget( bgPicBox );
+ top->addWidget( linkEntries );
+ top->addWidget( numberEntries );
+
+ top->addStretch();
+
+ reopen();
+}
+
+void Prefs::save()
+{
+ KConfig *config = KGlobal::config();
+
+ QString bgRealURL = bgPicPath->url();
+
+ if (bgRealURL[0] == '/')
+ bgRealURL = "file:" + bgRealURL;
+
+ config->setGroup( "HTMLExport" );
+ config->writeEntry( "bgColor", bgColorSelect->color() );
+ config->writeEntry( "txtColor", txtColorSelect->color() );
+ config->writeEntry( "headColor", headColorSelect->color() );
+ config->writeEntry( "hoverColor", hoverColorSelect->color() );
+ config->writePathEntry( "bgImgPath", bgRealURL );
+ config->writeEntry( "linkEntries", linkEntries->isChecked() );
+ config->writeEntry( "numberEntries", numberEntries->isChecked() );
+ config->sync();
+}
+
+void Prefs::reopen()
+{
+ KConfig *config = KGlobal::config();
+ headColorSelect->setColor(config->readColorEntry( "headColor" ) );
+ hoverColorSelect->setColor( config->readColorEntry( "hoverColor" ) );
+ bgColorSelect->setColor( config->readColorEntry( "bgColor" ) );
+ txtColorSelect->setColor( config->readColorEntry( "txtColor" ) );
+ bgPicPath->setURL( config->readPathEntry( "bgImgPath" ) );
+ numberEntries->setChecked( config->readBoolEntry( "numberEntries" ) );
+ linkEntries->setChecked( config->readBoolEntry( "linkEntries" ) );
+}
+#include "htmlexport.moc"
+
diff --git a/noatun/modules/htmlexport/htmlexport.h b/noatun/modules/htmlexport/htmlexport.h
new file mode 100644
index 00000000..a909a5cb
--- /dev/null
+++ b/noatun/modules/htmlexport/htmlexport.h
@@ -0,0 +1,89 @@
+
+#ifndef _HTMLEXPORT_H_
+#define _HTMLEXPORT_H_
+
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qlabel.h>
+#include <qhgroupbox.h>
+#include <qlineedit.h>
+#include <qcheckbox.h>
+#include <qpushbutton.h>
+#include <qlayout.h>
+#include <qtooltip.h>
+#include <qwhatsthis.h>
+
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kfiledialog.h>
+#include <ktempfile.h>
+#include <kcolorbutton.h>
+#include <kio/netaccess.h>
+#include <kconfig.h>
+#include <kurlrequester.h>
+
+//#include <kdebug.h>
+
+#include <noatun/app.h>
+#include <noatun/playlist.h>
+#include <noatun/pref.h>
+#include <noatun/plugin.h>
+
+class KAction;
+
+class HTMLExport : public QObject, public Plugin
+{
+Q_OBJECT
+NOATUNPLUGIND
+public:
+ HTMLExport();
+ ~HTMLExport();
+
+
+private:
+ QString htmlEscape(const QString &source);
+ QString getColorByEntry(QString s);
+ KConfig *config;
+ KAction *mAction;
+
+private slots:
+ void slotExport();
+
+};
+
+class Prefs : public CModule
+{
+Q_OBJECT
+public:
+ Prefs(QObject *parent);
+ virtual void save();
+ virtual void reopen();
+
+ QGroupBox* colorBox;
+
+ KColorButton* headColorSelect;
+ KColorButton* hoverColorSelect;
+ KColorButton* bgColorSelect;
+ KColorButton* txtColorSelect;
+
+ QLabel* bgColorLabel;
+ QLabel* txtColorLabel;
+ QLabel* headColorLabel;
+ QLabel* hoverColorLabel;
+
+ QCheckBox* linkEntries;
+ QCheckBox* numberEntries;
+
+ QGroupBox* bgPicBox;
+ KURLRequester* bgPicPath;
+
+protected:
+ QGridLayout* bgcolorLabel;
+
+
+signals:
+ void saved();
+};
+
+#endif
+
diff --git a/noatun/modules/htmlexport/htmlexport.plugin b/noatun/modules/htmlexport/htmlexport.plugin
new file mode 100644
index 00000000..97a10fb9
--- /dev/null
+++ b/noatun/modules/htmlexport/htmlexport.plugin
@@ -0,0 +1,124 @@
+Filename=noatun_htmlexport.la
+Author=Malte Starostik
+Email=malte@kde.org
+Type=other
+License=Artistic
+Name=HTML Playlist Export
+Name[bn]=এইচ-টি-এম-এল সঙ্গীত-তালিকা রপ্তানি
+Name[br]=Ezporzh ar roll tonioù e HTML
+Name[bs]=Izvoz liste numera u HTML
+Name[ca]=Exportació del repertori en HTML
+Name[cs]=Export seznamu skladeb do HTML
+Name[cy]=Allforio Rhestr Chwarae HTML
+Name[da]=HTML-spilleliste-eksport
+Name[de]=Export von Wiedergabelisten in HTML
+Name[el]=Εξαγωγή λίστας αναπαραγωγής HTML
+Name[eo]=HTML-ludlisteksporto
+Name[es]=Exportador de listas de reproducción HTML
+Name[et]=Esitusnimekirja eksport HTML-i
+Name[eu]=HTML erreprodukzio-zerrendaren esportazioa
+Name[fa]=صادرات فهرست پخش زنگام
+Name[fi]=HTML-soittolistan vienti
+Name[fr]=Export en HTML des listes de lecture
+Name[ga]=Easpórtáil Seinmliosta mar HTML
+Name[gl]=Exportación de Lista de Reprodución a HTML
+Name[he]=ייצוא לרשימת ניגון בתבנית HTML
+Name[hu]=Lejátszási lista exportálása HTML-fájlba
+Name[is]=Útflutningur á HTML lagalista
+Name[it]=Esportazione HTML delle playlist
+Name[ja]=HTML プレイリストのエクスポート
+Name[kk]=Орындау тізімін HTML-экспорттау
+Name[km]=នាំចេញ​បញ្ជី​ចាក់ HTML
+Name[ko]=HTML 재생 목록 내보내기
+Name[lt]=HTML gaidaraščio eksportas
+Name[mk]=Испраќање на листа со нумери како HTML
+Name[nb]=Eksport av HTML-spilleliste
+Name[nds]=Afspeellist-Export na HTML
+Name[ne]=HTML बजाउने सूची निर्यात
+Name[nl]=HTML-afspeellijst-export
+Name[nn]=Eksport av HTML-speleliste
+Name[pa]=HTML ਸੰਗੀਤ-ਸੂਚੀ ਨਿਰਯਾਤ
+Name[pl]=Eksport listy utworów do formatu HTML
+Name[pt]=Exportação de Listas de Músicas em HTML
+Name[pt_BR]=Exportação de listas de reprodução em HTML
+Name[ro]=Exportare HTML listă de redare
+Name[ru]=Экспорт списка песен в HTML
+Name[sk]=Export playlistu do HTML
+Name[sl]=Izvoz predvajalnega seznama v HTML
+Name[sr]=Извожење листе нумера у HTML
+Name[sr@Latn]=Izvoženje liste numera u HTML
+Name[sv]=HTML-spellistexport
+Name[ta]=HTML பாடல் பட்டியல் ஏற்றுமதி
+Name[th]=ส่งออกรายการเล่นเป็น HTML
+Name[tr]=HTML Çalma Listesi Dışarı Aktarma
+Name[uk]=Експорт списку композицій до HTML
+Name[zh_CN]=HTML 播放列表导出
+Name[zh_HK]=HTML 播放清單匯出
+Name[zh_TW]=HTML 播放清單匯出
+Comment=Creates a HTML file from the playlist
+Comment[af]=Skep 'n Html lêer van die liedjielys
+Comment[ar]=ينشئ ملف HTML من لائحة التشغيل
+Comment[az]=Çalma siyahısından bir HTML faylı yarat
+Comment[bg]=Експортиране на списък за изпълнение във файл от тип HTML
+Comment[bn]=সঙ্গীত-তালিকা থেকে একটি এইচ-টি-এম-এল ফাইল তৈরি করে
+Comment[br]=Krouiñ ur restr HTML eus ar roll tonioù
+Comment[bs]=Pravi HTML dokument od liste numera
+Comment[ca]=Crea un fitxer HTML d'una selecció de peces
+Comment[cs]=Vytváří ze seznamu skladeb HTML soubor
+Comment[cy]=Creu ffeil HTML o'r rhestr chwarae
+Comment[da]=Opretter en HTML-fil fra spillelisten
+Comment[de]=Erstellt eine HTML-Datei aus einer Wiedergabeliste
+Comment[el]=Δημιουργεί ένα αρχείο HTML από τη λίστα αναπαραγωγής
+Comment[en_GB]=Creates an HTML file from the playlist
+Comment[eo]=Kreas HTML-dosieron el la ludlisto
+Comment[es]=Crea un archivo HTML a partir de la lista de reproducción
+Comment[et]=HTML-faili loomine nimekirjast
+Comment[eu]=HTML fitxategia erreprodukzio-zerrendatik sortzen du
+Comment[fa]=از فهرست پخش یک پروندۀ زنگام ایجاد می‌‌کند
+Comment[fi]=Luo HTML-tiedoston soittolistasta
+Comment[fr]=Crée un fichier HTML à partir de la liste de lecture
+Comment[ga]=Cruthaigh comhad HTML ón seinmliosta
+Comment[gl]=Crea un ficheiro HTML a partires da lista de reprodución
+Comment[he]=יצירת קובץ HTML מתוך רשימת הניגון
+Comment[hi]=प्लेलिस्ट से एचटीएमएल फ़ाइल बनाता है
+Comment[hr]=Stvara od liste pjesama HTML datoteku
+Comment[hu]=Lejátszási lista exportálása HTML-ben
+Comment[is]=Býr til HTML skrá úr lagalistanum
+Comment[it]=Crea un file HTML dalla playlist
+Comment[ja]=プレイリストから HTML を作成
+Comment[kk]=Орындау тізімін HTML-пішімді файлға жазу
+Comment[km]=បង្កើត​បញ្ជី​ឯកសារ HTML ពី​បញ្ជី​ចាក់
+Comment[ko]=재생 목록에서 HTML 파일 만들기
+Comment[lt]=Sukuria HTML failą iš gaidaraščio
+Comment[lv]=Izveido HTML failu no plejlista
+Comment[mk]=Креира датотека HTML од листата со нумери
+Comment[ms]=Bina fail HTML untuk senarai main
+Comment[mt]=Joħloq fajl HTML mill-playlist
+Comment[nb]=Lager en HTML-fil fra spillelisten
+Comment[nds]=Stellt ut de Afspeellist en HTML-Datei op
+Comment[ne]=बजाउने सूचीबाट HTML फाइल सिर्जना गर्दछ
+Comment[nl]=Maakt een HTML-bestand aan op basis van de speellijst
+Comment[nn]=Lagar ei HTML-fil frå spelelista
+Comment[pa]=ਸੰਗੀਤ-ਸੂਚੀ ਤੋਂ ਇੱਕ HTML ਫਾਇਲ ਬਣਾਓ
+Comment[pl]=Tworzy plik HTML z listy nagrań
+Comment[pt]=Cria um ficheiro HTML a partir de uma lista de músicas
+Comment[pt_BR]=Cria um arquivo HTML a partir da lista de reprodução
+Comment[ro]=Creează un fişier HTML dintr-o listă de redare
+Comment[ru]=Создание HTML-файла из списка песен
+Comment[se]=Ráhkada HTML-fiilla čuojahanlisttus
+Comment[sk]=Vytvorí HTML súbor z playlistu
+Comment[sl]=Ustvari datoteko HTML iz predvajalnega seznama
+Comment[sr]=Прави HTML фајл на основу листе нумера
+Comment[sr@Latn]=Pravi HTML fajl na osnovu liste numera
+Comment[sv]=Skapar en HTML-fil av en spellista
+Comment[ta]=பாடல் பட்டியலிலிருந்து HTML ஆவணமொன்றை உருவாக்கின்றது
+Comment[tg]=Аз рӯйхати бозикуниҳо файли HTML-ро офарид
+Comment[th]=สร้างแฟ้ม HTML สำหรับรายการเล่น
+Comment[tr]=Çalma listesinden bir HTML dosyası oluşturur
+Comment[uk]=Створює файл HTML зі списку композицій
+Comment[ven]=Itani faela ya HTML u bva kha mutevhe wa tshitambi
+Comment[xh]=Yenza ifayile ye HTML kuluhlu lomdlalo
+Comment[zh_CN]=从播放列表创建 HTML 文件
+Comment[zh_HK]=從播放清單建立 HTML 檔案
+Comment[zh_TW]=從播放清單建立 HTML 文件檔案
+Comment[zu]=Yenza ifayela ye HTML kuluhlu lomdlalo
diff --git a/noatun/modules/infrared/Makefile.am b/noatun/modules/infrared/Makefile.am
new file mode 100644
index 00000000..bf8a7212
--- /dev/null
+++ b/noatun/modules/infrared/Makefile.am
@@ -0,0 +1,16 @@
+
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_infrared.la
+
+noatun_infrared_la_SOURCES = infrared.cpp lirc.cpp irprefs.cpp
+noinst_HEADERS = infrared.h lirc.h irprefs.h
+
+noatun_infrared_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_infrared_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la \
+ -lqtmcop -lkmedia2_idl -lsoundserver_idl
+
+noatun_infrared_la_METASOURCES = AUTO
+
+
+noatun_modules_infrared_DATA = infrared.plugin
+noatun_modules_infrareddir = $(kde_datadir)/noatun
diff --git a/noatun/modules/infrared/README b/noatun/modules/infrared/README
new file mode 100644
index 00000000..b3fda334
--- /dev/null
+++ b/noatun/modules/infrared/README
@@ -0,0 +1,3 @@
+This plugin enables Noatun to be controlled with an IR remote control.
+You'll need lirc (http://fsinfo.cs.uni-sb.de/~columbus/lirc/) and
+an IR receiver supported by lirc.
diff --git a/noatun/modules/infrared/infrared.cpp b/noatun/modules/infrared/infrared.cpp
new file mode 100644
index 00000000..60ea145c
--- /dev/null
+++ b/noatun/modules/infrared/infrared.cpp
@@ -0,0 +1,120 @@
+
+#include <stdio.h>
+
+#include <unistd.h>
+#include <noatun/player.h>
+#include <noatun/app.h>
+
+#include <klocale.h>
+#include <qtimer.h>
+
+#include "infrared.h"
+#include "lirc.h"
+#include "irprefs.h"
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new InfraRed();
+ }
+}
+
+
+InfraRed::InfraRed()
+ : QObject(),
+ Plugin()
+{
+ NOATUNPLUGINC(InfraRed);
+ m_lirc = new Lirc(this);
+ connect(m_lirc,
+ SIGNAL(commandReceived(const QString &, const QString &, int)),
+ SLOT(slotCommand(const QString &, const QString &, int)));
+
+ IRPrefs::s_lirc = m_lirc;
+ volume=0;
+ QTimer::singleShot(0, this, SLOT(start()));
+}
+
+InfraRed::~InfraRed()
+{
+}
+
+void InfraRed::start()
+{
+ new IRPrefs(this);
+}
+
+void InfraRed::slotCommand(const QString &remote, const QString &button, int repeat)
+{
+ switch (IRPrefs::actionFor(remote, button, repeat))
+ {
+ case IRPrefs::None:
+ break;
+
+ case IRPrefs::Play:
+ napp->player()->play();
+ break;
+
+ case IRPrefs::Stop:
+ napp->player()->stop();
+ break;
+
+ case IRPrefs::Pause:
+ napp->player()->playpause();
+ break;
+
+ case IRPrefs::Mute:
+ if (napp->player()->volume())
+ {
+ volume=napp->player()->volume();
+ napp->player()->setVolume(0);
+ }
+ else
+ {
+ napp->player()->setVolume(volume);
+ }
+ break;
+
+ case IRPrefs::Previous:
+ napp->player()->back();
+ break;
+
+ case IRPrefs::Next:
+ napp->player()->forward();
+ break;
+
+ case IRPrefs::VolumeDown:
+ napp->player()->setVolume(napp->player()->volume() - 4);
+ break;
+
+ case IRPrefs::VolumeUp:
+ napp->player()->setVolume(napp->player()->volume() + 4);
+ break;
+
+ case IRPrefs::SeekBackward: // - 3 seconds
+ napp->player()->skipTo( QMAX(0, napp->player()->getTime() - 3000) );
+ break;
+
+ case IRPrefs::SeekForward: // + 3 seconds
+ napp->player()->skipTo( QMIN(napp->player()->getLength(),
+ napp->player()->getTime() + 3000) );
+ break;
+ case IRPrefs::ShowPlaylist:
+ napp->player()->toggleListView();
+ break;
+ case IRPrefs::NextSection:
+ // This and the next case theoretically shouldn't bypass player()
+ // but I'm making this change as inobtrusive as possible. That
+ // means restricting the change to infrared and not messing around
+ // in libnoatun -- Neil
+ napp->playlist()->nextSection();
+ break;
+ case IRPrefs::PreviousSection:
+ napp->playlist()->previousSection();
+ break;
+ }
+}
+
+#include "infrared.moc"
+
diff --git a/noatun/modules/infrared/infrared.h b/noatun/modules/infrared/infrared.h
new file mode 100644
index 00000000..37e97735
--- /dev/null
+++ b/noatun/modules/infrared/infrared.h
@@ -0,0 +1,30 @@
+
+#ifndef _INFRARED_H_
+#define _INFRARED_H_
+
+#include <noatun/player.h>
+#include <noatun/plugin.h>
+
+class NoatunPreferences;
+class Lirc;
+
+class InfraRed : public QObject, public Plugin
+{
+Q_OBJECT
+NOATUNPLUGIND
+public:
+ InfraRed();
+ ~InfraRed();
+
+private slots:
+ void slotCommand(const QString &, const QString &, int);
+
+ void start();
+
+private:
+ Lirc *m_lirc;
+ int volume;
+};
+
+#endif
+
diff --git a/noatun/modules/infrared/infrared.plugin b/noatun/modules/infrared/infrared.plugin
new file mode 100644
index 00000000..dab20149
--- /dev/null
+++ b/noatun/modules/infrared/infrared.plugin
@@ -0,0 +1,120 @@
+Filename=noatun_infrared.la
+Author=Malte Starostik
+Email=malte@kde.org
+Type=other
+License=Artistic
+Name=Infrared Control
+Name[af]=Infrared Kontrole
+Name[ar]=تحكم بالأشعة تحت الحمراء
+Name[az]=İnfra Qırmızı İdarə
+Name[bn]=ইনফ্রা-রেড নিয়ন্ত্রণ
+Name[bs]=Infrared kontrola
+Name[ca]=Control per infraroigs
+Name[cs]=Dálkové ovládání
+Name[cy]=Rheolaeth Isgoch
+Name[da]=Infrarød kontrol
+Name[de]=Infrarot-Fernbedienung
+Name[el]=Έλεγχος μέσω υπέρυθρων
+Name[eo]=INfraruĝstirilo
+Name[es]=Control por infrarrojos
+Name[et]=Infrapunaliidese kaudu juhtimine
+Name[eu]=Kontrol infragorria
+Name[fa]= کنترل مادون قرمز
+Name[fi]=Infrapunahallinta
+Name[fr]=Contrôleur infrarouge
+Name[ga]=Rialú Infridhearg
+Name[gl]=Control por Infravermellos
+Name[he]=שליטה בעזרת אינפרא־אדום
+Name[hi]=इन्फ्रारेड नियंत्रण
+Name[hr]=Infra-crvena kontrola
+Name[hu]=Távirányító
+Name[id]=Kontrol infra merah
+Name[is]=Innrauð stýring
+Name[it]=Controllo a infrarossi
+Name[ja]=赤外線コントロール
+Name[kk]=ИҚ тұтқасымен басқару
+Name[km]=ឧបករណ៍ Infrared
+Name[ko]=적외선 제어
+Name[lt]=Infraraudonųjų spindulių valdymas
+Name[lv]=Infrasarkanā Vadība
+Name[mk]=Инфрацрвена контрола
+Name[mt]=Kontroll Infrared
+Name[nb]=Infrarød kontroll
+Name[nds]=Infraroot-Feernbedenen
+Name[ne]=इन्फ्रारेड नियन्त्रण
+Name[nl]=Infraroodbediening
+Name[nn]=Infraraud kontroll
+Name[pa]=ਇੰਫਰਾਰੈੱਡ ਕੰਟਰੋਲ
+Name[pl]=Sterowanie przez podczerwień
+Name[pt]=Controlo de Infra-Vermelhos
+Name[pt_BR]=Controle de Infravermelho
+Name[ro]=Control în infraroşu
+Name[ru]=ИК пульт
+Name[se]=Infrarukses stivrran
+Name[sk]=Diaľkové ovládanie
+Name[sl]=Infrardeči nadzor
+Name[sr]=Инфрацрвена контрола
+Name[sr@Latn]=Infracrvena kontrola
+Name[sv]=Infraröd fjärrkontroll
+Name[ta]=செங்கீழ்க்கதிர்க் கட்டுப்பாடு
+Name[tg]=Идоракунии Инфрасурх
+Name[th]=ตัวควบคุมแบบอินฟราเรด
+Name[tr]=Kızılötesi Kontrol
+Name[uk]=Керуванням інфрачервоним зв'язком
+Name[ven]=Vhulanguli ha Infrared
+Name[xh]=Ulawulo olwakhelwe ngezantsi
+Name[zh_CN]=红外线控制
+Name[zh_HK]=紅外線控制
+Name[zh_TW]=紅外線控制
+Name[zu]=Ulawulo Olunokubomvu
+Comment=Control Noatun with your IR remote
+Comment[bg]=Управление на Noatun с помощта на инфрачервено, дистанционно управление
+Comment[bn]=আপনার ইনফ্রা-রেড রিমোট ব্যবহার করে নোটান নিয়ন্ত্রণ করুন
+Comment[bs]=Kontrolišite Noatun vašim infracrvenim daljinskim upravljačem
+Comment[ca]=Controla Noatun amb el comandament a distància
+Comment[cs]=Ovládejte Noatun svým dálkovým ovladačem
+Comment[cy]=Rheoli Noatun efo'ch Rheolydd Isgoch
+Comment[da]=Kontrollér Noatun med din fjernbetjening
+Comment[de]=Kontrollieren Sie Noatun mit Ihrer Infrarot-Fernbedienung
+Comment[el]=Έλεγχος του Noatun με το IR τηλεκοντρόλ σας
+Comment[eo]=Stiru Noatun per via infraruĝstirilo
+Comment[es]=Controle Noatun con su mando a distancia
+Comment[et]=Noatun juhtimine IR puldi abil
+Comment[eu]=Kontrolatu Noatun urruneko infragorriekin
+Comment[fa]=کنترل Noatun با IR دور شما
+Comment[fi]=Hallitse Noatunia infrapunakauko-ohjaimella
+Comment[fr]=Contrôlez Noatun avec votre télécommande infrarouge
+Comment[ga]=Rialaigh Noatun le do cianrialtán infridhearg
+Comment[gl]=Controlar Noatun co seu IR remoto
+Comment[he]=שליטה ב-Noatun באמצעות שלט אינפרא־אדום
+Comment[hu]=A Noatun kezelése távirányítóval
+Comment[is]=Stjórnaðu Nóatún með fjarstýringunni þinni
+Comment[it]=Controlla Noatun con il tuo telecomando
+Comment[ja]=赤外線リモコンで Noatun をコントロール
+Comment[kk]=Noatun-ды ИҚ тұтқасымен басқару
+Comment[km]=បញ្ជា Noatun ដោយ​ប្រើ IR របស់​អ្នក​ពី​ចម្ងាយ
+Comment[ko]=적외선 리모콘으로 Noatun 조정하기
+Comment[lt]=Valdykite Noatun pasitelkdami nuotolinį pultą
+Comment[mk]=Го контролира Noatun со вашиот инфрацрвен далечински управувач
+Comment[nb]=Styr Noatun med en infrerød fjernkontroll
+Comment[nds]=Noatun mit Dien Infraroot-Feernbedenen stüern
+Comment[ne]=तपाईँको IR टाढाकोसँग नियन्त्रण नोवटुन
+Comment[nl]=Bedien Noatun met uw infrarood afstandbediening
+Comment[nn]=Styr Noatun med ein infraraud fjernkontroll
+Comment[pl]=Sterowanie Noatun za pomocą pilota podczerwieni
+Comment[pt]=Controle o Noatun com um comando à distância
+Comment[pt_BR]=Controlar o Noatun com seu controle-remoto infravermelho
+Comment[ro]=Controlează Noatun cu telecomanda în infraroşu
+Comment[ru]=Управление Noatun с помощью инфракрасного пульта
+Comment[sk]=Ovládajte Noatun vaším diaľkovým ovládačom
+Comment[sl]=Nadzorujte Noatun z vašim infrardečim daljinskim upravljalcem
+Comment[sr]=Контролишите Noatun вашим ИЦ даљинским управљачем
+Comment[sr@Latn]=Kontrolišite Noatun vašim IC daljinskim upravljačem
+Comment[sv]=Styr Noatun med en infraröd fjärrkontroll
+Comment[ta]=உங்கள் IR கருவியால் Noatun இனைக் கட்டுப்படுத்தவும்
+Comment[th]=ควบคุม Noatun ด้วยตัวรีโมทอินฟราเรดของคุณ
+Comment[tr]=Kızılötesi Uzaktan Kumanda ile Noatun'u Kontrol Edin
+Comment[uk]=Керує Noatun з віддаленого інфрачервоного пульта керування
+Comment[zh_CN]=使用红外线遥控 Noatun
+Comment[zh_HK]=使用紅外線遙控控制 Noatun
+Comment[zh_TW]=使用 Noatun 透過紅外線遙控
diff --git a/noatun/modules/infrared/irprefs.cpp b/noatun/modules/infrared/irprefs.cpp
new file mode 100644
index 00000000..409fa94e
--- /dev/null
+++ b/noatun/modules/infrared/irprefs.cpp
@@ -0,0 +1,311 @@
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <noatun/app.h>
+
+#include <kdialog.h>
+#include <klocale.h>
+#include <kconfig.h>
+#include <klistview.h>
+#include <kcombobox.h>
+#include <knuminput.h>
+
+#include "irprefs.h"
+#include "lirc.h"
+
+class CommandItem : public QListViewItem
+{
+public:
+ CommandItem(QListViewItem *remote, const QString &name,
+ IRPrefs::Action action, int interval)
+ : QListViewItem(remote, name, IRPrefs::actionName(action),
+ interval ? QString().setNum(interval) : QString::null),
+ m_name(remote->text(0) + "::" + name),
+ m_action(action),
+ m_interval(interval)
+ {
+ }
+
+ const QString &name() const { return m_name; }
+ IRPrefs::Action action() const { return m_action; }
+ int interval() const { return m_interval; }
+ void setAction(IRPrefs::Action action)
+ {
+ setText(1, IRPrefs::actionName(action));
+ m_action = action;
+ }
+ void setInterval(int interval)
+ {
+ setText(2, interval ? QString().setNum(interval) : QString::null);
+ m_interval = interval;
+ }
+
+private:
+ QString m_name;
+ IRPrefs::Action m_action;
+ int m_interval;
+};
+
+Lirc *IRPrefs::s_lirc = 0;
+bool IRPrefs::s_configRead = false;
+QMap<QString, IRPrefs::Command> IRPrefs::s_commands;
+
+IRPrefs::IRPrefs(QObject *parent)
+ : CModule(i18n("Infrared Control"), i18n("Configure Infrared Commands"), "remote", parent)
+{
+ QGridLayout *layout = new QGridLayout(this, 3, 5, KDialog::marginHint(), KDialog::spacingHint());
+ layout->setColStretch(1, 1);
+
+ QLabel *label = new QLabel(i18n("Remote control &commands:"), this);
+ layout->addMultiCellWidget(label, 0, 0, 0, 4);
+
+ label->setBuddy(m_commands = new KListView(this));
+ layout->addMultiCellWidget(m_commands, 1, 1, 0, 4);
+
+ label = new QLabel(i18n("&Action:"), this);
+ layout->addWidget(label, 2, 0);
+
+ label->setBuddy(m_action = new KComboBox(this));
+ m_action->setEnabled(false);
+ layout->addWidget(m_action, 2, 1);
+
+ m_repeat = new QCheckBox(i18n("&Repeat"), this);
+ m_repeat->setEnabled(false);
+ layout->addWidget(m_repeat, 2, 2);
+
+ label = new QLabel(i18n("&Interval:"), this);
+ layout->addWidget(label, 2, 3);
+
+ label->setBuddy(m_interval = new KIntSpinBox(this));
+ m_interval->setMinValue(1);
+ m_interval->setMaxValue(0xff);
+ m_interval->setValue(10);
+ m_interval->setEnabled(false);
+ layout->addWidget(m_interval, 2, 4);
+
+ connect(s_lirc, SIGNAL(remotesRead()), SLOT(reopen()));
+ connect(m_commands,
+ SIGNAL(selectionChanged(QListViewItem *)),
+ SLOT(slotCommandSelected(QListViewItem *)));
+ connect(m_action,
+ SIGNAL(activated(int)),
+ SLOT(slotActionActivated(int)));
+ connect(m_repeat,
+ SIGNAL(toggled(bool)),
+ SLOT(slotRepeatToggled(bool)));
+ connect(m_interval,
+ SIGNAL(valueChanged(int)),
+ SLOT(slotIntervalChanged(int)));
+
+ reopen();
+}
+
+void IRPrefs::save()
+{
+ KConfig *c = kapp->config();
+ KConfigGroupSaver groupSaver(c, "Infrared");
+ c->writeEntry("CommandCount", s_commands.count());
+ int i = 1;
+ for (QMap<QString, Command>::ConstIterator it = s_commands.begin(); it != s_commands.end(); ++it)
+ {
+ c->writePathEntry(QString("Command_%1").arg(i), it.key());
+ c->writeEntry(QString("Action_%1").arg(i), (int)((*it).action));
+ c->writeEntry(QString("Interval_%1").arg(i), (*it).interval);
+ ++i;
+ }
+}
+
+void IRPrefs::reopen()
+{
+ readConfig();
+
+ QStringList remotes = s_lirc->remotes();
+ m_commands->clear();
+ while (m_commands->columns()) m_commands->removeColumn(0);
+ if (!remotes.count())
+ {
+ m_commands->addColumn(i18n("Sorry"));
+ m_commands->setSorting(-1);
+ if (s_lirc->isConnected())
+ {
+ new QListViewItem(m_commands, i18n("You do not have any remote control configured."));
+ new QListViewItem(m_commands, i18n("Please make sure lirc is setup correctly."));
+ }
+ else
+ {
+ new QListViewItem(m_commands, i18n("Connection could not be established."));
+ new QListViewItem(m_commands, i18n("Please make sure lirc is setup correctly and lircd is running."));
+ }
+ m_commands->setEnabled(false);
+ return;
+ }
+ m_commands->setEnabled(true);
+ m_commands->addColumn(i18n("Button"));
+ m_commands->addColumn(i18n("Action"));
+ m_commands->addColumn(i18n("Interval"));
+ m_commands->setSorting(0);
+ for (QStringList::ConstIterator it = remotes.begin(); it != remotes.end(); ++it)
+ {
+ QListViewItem *remote = new QListViewItem(m_commands, *it);
+ const QStringList &buttons = s_lirc->buttons(*it);
+ for (QStringList::ConstIterator btn = buttons.begin(); btn != buttons.end(); ++btn)
+ {
+ QString key = *it + "::" + *btn;
+ if (s_commands.contains(key))
+ new CommandItem(remote, *btn, s_commands[key].action, s_commands[key].interval);
+ else
+ new CommandItem(remote, *btn, None, 0);
+ }
+ remote->setOpen(true);
+ }
+
+ m_action->clear();
+ for (int i = 0; ; ++i)
+ {
+ QString action = actionName((Action)i);
+ if (action.isNull())
+ break;
+ if (action.isEmpty())
+ m_action->insertItem(i18n("None"));
+ else
+ m_action->insertItem(action);
+ }
+
+
+}
+
+void IRPrefs::slotCommandSelected(QListViewItem *item)
+{
+ CommandItem *cmd = dynamic_cast<CommandItem *>(item);
+ if (cmd)
+ {
+ m_action->setCurrentItem((int)(cmd->action()));
+ m_repeat->setChecked(cmd->interval());
+ if (cmd->interval())
+ m_interval->setValue(cmd->interval());
+ else
+ {
+ m_interval->setValue(10);
+ cmd->setInterval(0); // HACKHACKHACK
+ }
+ m_action->setEnabled(true);
+ m_repeat->setEnabled(cmd->action() != None);
+ m_interval->setEnabled(cmd->interval());
+ }
+ else
+ {
+ m_action->setEnabled(false);
+ m_repeat->setEnabled(false);
+ m_interval->setEnabled(false);
+ }
+}
+
+void IRPrefs::slotActionActivated(int action)
+{
+ CommandItem *cmd = dynamic_cast<CommandItem *>(m_commands->currentItem());
+ if (!cmd)
+ return; // Shouldn't happen
+ cmd->setAction((Action)action);
+ if (cmd->action() == None)
+ {
+ cmd->setInterval(0);
+ m_repeat->setChecked(false);
+ m_repeat->setEnabled(false);
+ m_interval->setEnabled(false);
+ }
+ else
+ {
+ m_repeat->setEnabled(true);
+ m_interval->setEnabled(cmd->interval());
+ }
+ s_commands[cmd->name()].action = cmd->action();
+ s_commands[cmd->name()].interval = 0;
+}
+
+void IRPrefs::slotRepeatToggled(bool value)
+{
+ CommandItem *cmd = dynamic_cast<CommandItem *>(m_commands->currentItem());
+ if (!cmd)
+ return; // Shouldn't happen
+ cmd->setInterval(value ? 10 : 0);
+ s_commands[cmd->name()].interval = cmd->interval();
+ m_interval->setEnabled(value);
+}
+
+void IRPrefs::slotIntervalChanged(int value)
+{
+ CommandItem *cmd = dynamic_cast<CommandItem *>(m_commands->currentItem());
+ if (!cmd)
+ return; // Shouldn't happen
+ cmd->setInterval(value);
+ s_commands[cmd->name()].interval = cmd->interval();
+}
+
+const QString IRPrefs::actionName(Action action)
+{
+ switch (action)
+ {
+ case None:
+ return QString("");
+ case Play:
+ return i18n("Play");
+ case Stop:
+ return i18n("Stop");
+ case Previous:
+ return i18n("Back");
+ case Next:
+ return i18n("Next");
+ case VolumeDown:
+ return i18n("Volume Down");
+ case VolumeUp:
+ return i18n("Volume Up");
+ case Mute:
+ return i18n("Mute");
+ case Pause:
+ return i18n("Pause");
+ case SeekBackward:
+ return i18n("Seek Backward");
+ case SeekForward:
+ return i18n("Seek Forward");
+ case ShowPlaylist:
+ return i18n("Show Playlist");
+ case NextSection:
+ return i18n("Next Section");
+ case PreviousSection:
+ return i18n("Previous Section");
+ }
+ return QString::null;
+}
+
+void IRPrefs::readConfig()
+{
+ if (s_configRead)
+ return;
+ KConfig *c = kapp->config();
+ KConfigGroupSaver groupSaver(c, "Infrared");
+ int count = c->readNumEntry("CommandCount");
+ for (int i = 1; i <= count; ++i)
+ {
+ Command cmd;
+ cmd.action = (Action)(c->readNumEntry(QString("Action_%1").arg(i)));
+ cmd.interval = c->readNumEntry(QString("Interval_%1").arg(i));
+ s_commands.insert(c->readPathEntry(QString("Command_%1").arg(i)), cmd);
+ }
+ s_configRead = true;
+}
+
+IRPrefs::Action IRPrefs::actionFor(const QString &remote, const QString &button, int repeat)
+{
+ readConfig();
+ Command cmd = s_commands[remote + "::" + button];
+ if ((cmd.interval == 0 && repeat == 0)
+ || (cmd.interval != 0 && repeat % cmd.interval == 0))
+ return cmd.action;
+ else
+ return None;
+}
+
+#include "irprefs.moc"
+
+
diff --git a/noatun/modules/infrared/irprefs.h b/noatun/modules/infrared/irprefs.h
new file mode 100644
index 00000000..7f813ac8
--- /dev/null
+++ b/noatun/modules/infrared/irprefs.h
@@ -0,0 +1,62 @@
+
+#ifndef _IRPREFS_H_
+#define _IRPREFS_H_
+
+#include <qmap.h>
+#include <noatun/pref.h>
+
+class QCheckBox;
+class QListViewItem;
+class KListView;
+class KComboBox;
+class KIntSpinBox;
+class Lirc;
+
+class IRPrefs : public CModule
+{
+Q_OBJECT
+public:
+ enum Action
+ {
+ None, Play, Stop, Pause, Mute,
+ Previous, Next, VolumeDown, VolumeUp,
+ SeekBackward, SeekForward, ShowPlaylist,
+ NextSection, PreviousSection
+ };
+
+ IRPrefs(QObject *parent);
+
+ virtual void save();
+
+ static const QString actionName(Action);
+ static Action actionFor(const QString &, const QString &, int);
+
+public slots:
+ static Lirc *s_lirc;
+
+private slots:
+ virtual void reopen();
+ void slotCommandSelected(QListViewItem *);
+ void slotActionActivated(int);
+ void slotRepeatToggled(bool);
+ void slotIntervalChanged(int);
+
+private:
+ static void readConfig();
+
+ KListView *m_commands;
+ KComboBox *m_action;
+ QCheckBox *m_repeat;
+ KIntSpinBox *m_interval;
+
+ struct Command
+ {
+ Action action;
+ int interval;
+ };
+ static bool s_configRead;
+ static QMap<QString, Command> s_commands;
+};
+
+#endif
+
diff --git a/noatun/modules/infrared/lirc.cpp b/noatun/modules/infrared/lirc.cpp
new file mode 100644
index 00000000..857c07e2
--- /dev/null
+++ b/noatun/modules/infrared/lirc.cpp
@@ -0,0 +1,173 @@
+
+#include <unistd.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+#include <qsocket.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include "lirc.h"
+
+Lirc::Lirc(QObject *parent)
+ : QObject(parent),
+ m_socket(0)
+{
+ int sock = ::socket(PF_UNIX, SOCK_STREAM, 0);
+ if (sock == -1)
+ {
+ KMessageBox::sorry(0, i18n("Could not create a socket to receive infrared signals. The error is:\n") + strerror(errno));
+ return;
+ }
+ sockaddr_un addr;
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, "/dev/lircd");
+ if (::connect(sock, (struct sockaddr *)(&addr), sizeof(addr)) == -1)
+ {
+ KMessageBox::sorry(0, i18n("Could not establish a connection to receive infrared signals. The error is:\n") + strerror(errno));
+ ::close(sock);
+ return;
+ }
+
+ m_socket = new QSocket;
+ m_socket->setSocket(sock);
+ connect(m_socket, SIGNAL(readyRead()), SLOT(slotRead()));
+ update();
+}
+
+Lirc::~Lirc()
+{
+ delete m_socket;
+}
+
+const QStringList Lirc::remotes() const
+{
+ QStringList result;
+ for (Remotes::ConstIterator it = m_remotes.begin(); it != m_remotes.end(); ++it)
+ result.append(it.key());
+ result.sort();
+ return result;
+}
+
+void Lirc::slotRead()
+{
+ while (m_socket->bytesAvailable())
+ {
+ QString line = readLine();
+ if (line == "BEGIN")
+ {
+ // BEGIN
+ // <command>
+ // [SUCCESS|ERROR]
+ // [DATA
+ // n
+ // n lines of data]
+ // END
+ line = readLine();
+ if (line == "SIGHUP")
+ {
+ // Configuration changed
+ do line = readLine();
+ while (!line.isEmpty() && line != "END");
+ update();
+ return;
+ }
+ else if (line == "LIST")
+ {
+ // remote control list
+ if (readLine() != "SUCCESS" || readLine() != "DATA")
+ {
+ do line = readLine();
+ while (!line.isEmpty() && line != "END");
+ return;
+ }
+ QStringList remotes;
+ int count = readLine().toInt();
+ for (int i = 0; i < count; ++i)
+ remotes.append(readLine());
+ do line = readLine();
+ while (!line.isEmpty() && line != "END");
+ if (line.isEmpty())
+ return; // abort on corrupt data
+ for (QStringList::ConstIterator it = remotes.begin(); it != remotes.end(); ++it)
+ sendCommand("LIST " + *it);
+ return;
+ }
+ else if (line.left(4) == "LIST")
+ {
+ // button list
+ if (readLine() != "SUCCESS" || readLine() != "DATA")
+ {
+ do line = readLine();
+ while (!line.isEmpty() && line != "END");
+ return;
+ }
+ QString remote = line.mid(5);
+ QStringList buttons;
+ int count = readLine().toInt();
+ for (int i = 0; i < count; ++i)
+ {
+ // <code> <name>
+ QString btn = readLine();
+ buttons.append(btn.mid(17));
+ }
+ m_remotes.insert(remote, buttons);
+ }
+ do line = readLine();
+ while (!line.isEmpty() && line != "END");
+ emit remotesRead();
+ }
+ else
+ {
+ // <code> <repeat> <button name> <remote control name>
+ line.remove(0, 17); // strip code
+ int pos = line.find(' ');
+ if (pos < 0)
+ return;
+ bool ok;
+ int repeat = line.left(pos).toInt(&ok, 16);
+ if (!ok)
+ return;
+ line.remove(0, pos + 1);
+
+ pos = line.find(' ');
+ if (pos < 0)
+ return;
+ QString btn = line.left(pos);
+ line.remove(0, pos + 1);
+
+ emit commandReceived(line, btn, repeat);
+ }
+ }
+}
+
+void Lirc::update()
+{
+ m_remotes.clear();
+ sendCommand("LIST");
+}
+
+const QString Lirc::readLine()
+{
+ if (!m_socket->bytesAvailable())
+ return QString::null;
+
+ QString line = m_socket->readLine();
+ if (line.isEmpty())
+ return QString::null;
+
+ line.remove(line.length() - 1, 1);
+ return line;
+}
+
+void Lirc::sendCommand(const QString &command)
+{
+ QString cmd = command + "\n";
+ m_socket->writeBlock(cmd.latin1(), cmd.length());
+}
+
+#include "lirc.moc"
+
diff --git a/noatun/modules/infrared/lirc.h b/noatun/modules/infrared/lirc.h
new file mode 100644
index 00000000..e5380c5c
--- /dev/null
+++ b/noatun/modules/infrared/lirc.h
@@ -0,0 +1,75 @@
+
+#ifndef _LIRC_H_
+#define _LIRC_H_
+
+#include <qobject.h>
+#include <qstringlist.h>
+#include <qmap.h>
+
+class QSocket;
+
+typedef QMap<QString, QStringList> Remotes;
+
+class Lirc : public QObject
+{
+Q_OBJECT
+public:
+ /**
+ * Constructor
+ */
+ Lirc(QObject *parent);
+ /**
+ * Destructor
+ */
+ virtual ~Lirc();
+
+ /**
+ * Returns true if the connection to lircd is operational
+ */
+ bool isConnected() const { return m_socket; }
+ /**
+ * The names of the remote configured controls
+ */
+ const QStringList remotes() const;
+ /**
+ * The names of the buttons for the specified
+ * remote control
+ */
+ const QStringList buttons(const QString &remote) const
+ {
+ return m_remotes[remote];
+ }
+
+signals:
+ /**
+ * Emitted when the list of controls / buttons was cmpletely read
+ */
+ void remotesRead();
+ /**
+ * Emitted when a IR command was received
+ *
+ * The arguments are the name of the remote control used,
+ * the name of the button pressed and the repeat counter.
+ *
+ * The signal is emitted repeatedly as long as the button
+ * on the remote control remains pressed.
+ * The repeat counter starts with 0 and increases
+ * every time this signal is emitted.
+ */
+ void commandReceived(const QString &, const QString &, int);
+
+private slots:
+ void slotRead();
+
+private:
+ void update();
+ const QString readLine();
+ void sendCommand(const QString &);
+
+private:
+ QSocket *m_socket;
+ Remotes m_remotes;
+};
+
+#endif
+
diff --git a/noatun/modules/kaiman/Makefile.am b/noatun/modules/kaiman/Makefile.am
new file mode 100644
index 00000000..a8205a59
--- /dev/null
+++ b/noatun/modules/kaiman/Makefile.am
@@ -0,0 +1,23 @@
+SUBDIRS = skins
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_kaiman.la
+
+noatun_kaiman_la_SOURCES = \
+ noatunui.cpp \
+ style.cpp \
+ userinterface.cpp \
+ pref.cpp
+
+noatun_kaiman_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_kaiman_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la \
+ -lqtmcop -lkmedia2_idl -lsoundserver_idl
+
+noatun_kaiman_la_METASOURCES = AUTO
+
+noinst_HEADERS = \
+ userinterface.h \
+ style.h \
+ pref.h
+
+noatun_modules_kaiman_DATA = kaiman.plugin kaimanui.rc
+noatun_modules_kaimandir = $(kde_datadir)/noatun
diff --git a/noatun/modules/kaiman/README b/noatun/modules/kaiman/README
new file mode 100644
index 00000000..4c24a3ae
--- /dev/null
+++ b/noatun/modules/kaiman/README
@@ -0,0 +1,3 @@
+kaiman - Media player for KDE2.0
+
+
diff --git a/noatun/modules/kaiman/SKIN-SPECS b/noatun/modules/kaiman/SKIN-SPECS
new file mode 100644
index 00000000..bc3533ad
--- /dev/null
+++ b/noatun/modules/kaiman/SKIN-SPECS
@@ -0,0 +1,518 @@
+ ############ GQmpeg skin specifications file. ############
+
+ (A quick reference of what is required in the image files
+ for each widget type is located at the end of this document )
+
+Skins are simply a directory which contain image files and
+a skindata file (named skindata).
+
+All skin features are configured in the skindata file.
+
+Note: when using an alternate skin, it's specs go into a file named
+skindata_alt, it uses the same format as the skindata file. Pressing
+the Alt_Skin_Button button switches between the two skins.
+(each skin must contain an Alt_Skin_Button if you want the alternate
+skin feature to work)
+
+Any line can be made into a comment by prefacing it with a '#' symbol.
+
+All image files can be any size, GQmpeg will calculate the drawing data
+for you. Skins can have any size buttons, display items, digits,
+fonts, etc. The files can be of any type supported by gdk-pixbuf (xpm, png,
+jpeg, gif, etc.) The recommended file format is png.
+
+Prelights are optional on all items that support them. A prelight is an
+alternate image that is displayed when the mouse is over a pressable
+item (button, slider, dial). For example, the default skin includes prelights
+for all buttons, notice the buttons 'brighten' when the mouse moves over
+them.
+
+Every image within a file must have the same width and height, for example
+if the play button was 30 by 20, the resulting image file would be
+180 by 20. (6 button states total, including status lights and prelights)
+
+ Addendum: If the above button was specified with the status light and
+ prelight options as false, the resulting image file would be
+ 60 by 20. (2 button states total, 1 for normal, 1 for pressed)
+
+Note that images for buttons and numbers contain the items horizontally,
+the images for items contain the items vertically, the image for a font
+contains 3 rows of 32 items, and the slider contains the background and
+handle horizontally or vertically (depending if a slider is horizontal
+or verticle, respectivley).
+
+Only the background image is required, all other elements are optional
+(although it would be nice to always have a play button :)
+If you do not want an item displayed, comment out the line with
+a '#' symbol.
+
+IMPORTANT:
+Slots enclosed in "[]" are optional, but are so only to retain backward
+compatibility of skins. Please specify all options for each type as in the
+future the options enclosed in "[]" may no longer be "optional". Please
+separate each option with a single space, and do not add extra characters
+at the end of the line, as the extra info may be mistaken for expanded
+options in the event that options are added to the skin spec in the future.
+
+PROPER TRANSPARENCY:
+The main background image uses a threshold of 1 (out of 256 levels) for the
+window shape, the rest is used for partially overwiting the background when
+the Transparency option is true.
+
+Portions of items, buttons, sliders, numbers, and text that never change and
+are the same as the background image should be set transparent so that the
+'force transparent' option works properly.
+
+ ############# skindata file format #################
+
+Note: For an example skindata file see the file skindata-template.
+
+x and y are always the position in the window (use the -skinhelp command line
+option to have GQmpeg print out the mouse coordinates as the mouse moves)
+
+And finally, to what is available:
+
+==========================
+ Main options
+==========================
+
+Background: filename
+
+ filename
+ The background image file, the window will be the same size as this image.
+ Add transparency to this image for shaped skins, the cutoff threshold for
+ transparency is 1 on images with muli-level alpha (like in png).
+
+Transparency: flag
+
+ flag
+ True or False, this specifies if the background image (above) has a multiple
+ level alpha channel (as in png files) to apply when overwriting the root
+ window's background.
+
+Mask: filename
+
+(this is DEPRECATED!, for transparent skins just add transparency to the
+ Background image, Mask remains merely for backwards compatibility)
+ filename
+ The mask image file, only needed for skins which are shaped windows (not
+ rectangular). Contains a transparency mask for the main window.
+
+==========================
+ Text display
+==========================
+
+Title/Album/Artist/Genre: filename length [extended] x y
+
+ filename
+ Should contain a fixed font. With 3 or 6 lines of 32 characters each,
+ these are the characters, they are listed in three rows so you can copy
+ and past them into you graphics program. (first character in the top line
+ is a space)
+
+ !"#$%&'()*+,-./0123456789:;<=>?
+ @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
+ `abcdefghijklmnopqrstuvwxyz{|}~
+
+ When extended is TRUE these are the 3 addition lines of international chars
+
+ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ XXXXXXX FIXME! TO DO! XXXXXXXXXX
+ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+ length
+ Maximum number of characters to display in window.
+
+ extended
+ (optional, absence defaults to FALSE)
+ When TRUE, the image contains 3 additional lines for internation
+ characters. The result is 6 lines of 32 characters each.
+
+==========================
+ Buttons (that optionally include an 'active' mode)
+==========================
+
+Play/Pause/Stop/Shuffle/Repeat/
+ Time_Remaining/Time_Total_Button: filename prelight status_light x y [clip_filename]
+
+ filename
+ Image file. Contains the buttons horizontally in this order:
+ normal, normal pressed, normal active, normal active pressed, prelit, prelit active
+ (the actual number of images may vary, see next two options)
+
+ prelight
+ TRUE or FALSE, specifies whether or not prelight buttons are available, If
+ FALSE, do not include the last two prelight buttons in the image file.
+
+ status_light
+ TRUE or FALSE, specifies whether or not button lights are available. If FALSE,
+ do not include the 'lit up', 'lit down', and 'prelight lit up' buttons in the
+ image file.
+
+ clip_filename (optional)
+ When specified, defines an image with transparency to be used as the button's
+ draw clip mask. The clip mask is placed at location x, y The transparency is used to
+ indicate the portions of the button that should be visible (drawn) and respond to
+ mouse clicks. If the image contains a multiple level alpha channel, the mask is reduced
+ such that levels above 50% are visible and those below 50% are not visible.
+
+==========================
+ Buttons (standard)
+==========================
+
+Next/Prev/FF/RW/Playlist/Config/Iconify/Mixer/Exit/Alt_Skin/Volume_Up/Volume_Down/
+ Balance_Left/Balance_Right_Button: filename prelight x y [clip_filename]
+Preset_1_/.../Preset_10_Button: filename prelight x y [clip_filename]
+
+ filename
+ Image file. Contains the buttons horizontally in this order:
+ normal, normal pressed, prelit
+ (the actual number of images may vary, see next option)
+
+ prelight
+ TRUE or FALSE, specifies whether or not prelight buttons are available. If
+ FALSE, do not include the last prelight button in the image file.
+
+ clip_filename (optional)
+ When specified, defines an image with transparency to be used as the button's
+ draw clip mask. The clip mask is placed at location x, y The transparency is used to
+ indicate the portions of the button that should be visible (drawn) and respond to
+ mouse clicks. If the image contains a multiple level alpha channel, the mask is reduced
+ such that levels above 50% are visible and those below 50% are not visible.
+
+
+==========================
+ Items (with fixed number sections)
+==========================
+
+Stereo/Shuffle/Repeat/Mpegversion/Mpeglayer/Mpegmode/Status/
+ Minus/Total_Item: filename x y
+
+ filename
+ Image file. Contains the items vertically in the order below:
+
+ Stereo_Item: blank, mono , stereo
+ Shuffle_Item: off, on
+ Repeat_Item: off, on
+ Mpegversion_Item: blank, 1, 2
+ Mpeglayer_Item: blank, 1, 2, 3
+ Mpegmode_Item: blank, stereo, joint-stereo, dual-channel, single-channel
+ Status_Item: stop, pause, play
+ Minus_Item: time counts up, time counts down
+ Total_Item: time refers to current song only, to total playlist, to live
+
+==========================
+ Items (animation oriented)
+==========================
+
+Load_Item: filename sections x y
+
+ filename
+ Image file. Contains animations for the following items:
+
+ Load_Item: Animation for loading playlist in background.
+
+ sections
+ The total number of sections in the image file. The first section is always
+ blank (animation is off), the subsequent images are cycled through to create
+ the animation. This number is a total count, so it will be 1 (first is always
+ blank) plus the number of animation frames.
+
+==========================
+ Items (value oriented)
+==========================
+
+Position/Volume/Balance_Item: filename sections x y
+
+ filename
+ Image file. Contains images vertically in the order representing the lowest to
+ highest values.
+
+ sections
+ The number of images within the file, recommended number of images is 16 to 32.
+ The most possible usable images is 101 for volume and blance (from volume=0%
+ to volume = 100%).
+
+ Note:
+ These items must be listed before their respective sliders:
+ (see Position/Volume/Balance_Slider).
+
+==========================
+ Digit placeholder (for convenience, less memory usage with many similar numbers)
+==========================
+
+Digit_Large/Digit_Small_Default: filename
+
+ filename
+ Image file. Contains digits horizontally from 0 to 9, and a blank space.
+
+ These two digits are a convenience function, if you want to use a digit more than
+ once it is quicker to load it into on of these two slots. Then when using the digit
+ in the number item type below, use the words 'Large' or 'Small' in place of the
+ filename.
+
+==========================
+ Numbers
+==========================
+
+Hour/Minute/Second/Song/Total/In_Rate/In_Hz/Out_Bits/Out_Hz/
+ Song_Minute/Song_Second/Frame/Frame_Total/CPU/
+ Hour_Total/Minute_Total/Second_Total_Number: filename [length center] x y
+
+ filename
+ Image file for the number's digit, or the word 'Large' or 'Small' (see above).
+ If a filename is specified, the image should contain the digits horizontally
+ from 0 to 9, and a blank space.
+
+ length (optional)
+ The number of digits to display, if not present the default is assumed.
+
+ center (optional, but if specified length is required too)
+ TRUE or FALSE, specify to center the number.
+
+==========================
+ Sliders
+==========================
+
+Position/Volume/Balance_Slider: filename prelight [verticle reversed] length x y
+
+ filename
+ Image file. Contains images horizontally in this order:
+ slider background, handle normal, handle pressed, handle prelit
+ (handles must have the same dimensions)
+
+ prelight
+ TRUE or FALSE, specifies whether or not a prelight handle is available, if FALSE,
+ do not include a 'handle prelit' in the image file.
+
+ verticle
+ TRUE or FALSE. If false the slider is horizontal, if true, verticle.
+
+ reversed
+ TRUE or FALSE. If true, the slider works opposite than normal. For example
+ when false the slider moves from left to right, when true the slider moves
+ from right to left. On a verticle slider and reversed is false, the slider
+ moves from top to bottom.
+
+ length
+ The width of the slider's background, this is the complete width the slider will
+ be in the window, and must match the length of the 'slider background' in the
+ image file.
+
+==========================
+ Dials (AKA knobs)
+==========================
+
+Position/Volume/Balance_Dial: filename has_press_image has_prelight_image reversed
+ angle_start angle_end handle_offset_x handle_offset_y center_x center_y
+ x y w h [clip_filename]
+
+ filename
+ Image file. Contains images for the dial's handle vertically in this order:
+ normal
+ pressed (being dragged with mouse, optional)
+ prelit (mouse over highlight, optional)
+
+ has_press_image
+ TRUE or FALSE, specifies whether or not handle has a pressed image (above)
+
+ has_prelight_image
+ TRUE or FALSE, specifies whether or not handle has a pressed image (above)
+
+ reversed
+ TRUE of FALSE, normally a dial works clockwise with angle_start being the
+ lowest (zero) value and angle_end being the highest value. When TRUE the dial
+ works counter-clockwise with angle_end being the lowest (zero) value to
+ angle_start being the highest position.
+
+ angle_start
+ angle_end
+ The start and end angles define the end points of the dial's rotation in integer
+ degrees, the degrees count from 0 located right of center axis increasing clockwise
+ to a value of 359. (360 is equivelent to 0, but the only accepted numbers are 0 - 359.
+ This (admittedly poor) figure might help:
+
+
+ 270 ____ center axis
+ _|_ /
+ / /
+ / / \
+ 180 -| + |- 0 (360)
+ \ /
+ \_ _/
+ |
+ 90
+
+ handle_offset_x
+ handle_offset_y
+ The x and y coordinates into the handle image that represents the handle center
+ of rotation (pivot point), this does not have to actually be within the image size.
+
+ center_x
+ center_y
+ The x and y coordinates on the skin image for the handle center of rotation.
+
+ x, y, width, height:
+ Marks the clipping region to draw the dial, basically the handle is not drawn
+ outside this region. (width and height will be ignored if a clip mask image
+ is specified (see next option).
+
+ clip_filename (optional)
+ When specified, defines an image with transparency to be used as the dial's
+ draw clip mask. The clip mask is placed at x, y (above) and the image's dimensions
+ are used in place of width, height (above). The transparency is used to indicate the
+ portions of the dial that should be visible (drawn) and respond to mouse clicks.
+ If the image contains a multiple level alpha channel, the mask is reduced such that
+ levels above 50% are visible and those below 50% are not visible.
+
+==============================================================================
+ ************ Quick reference tables **************
+==============================================================================
+
+Note: All example values below set (*)coordinates x=1 and y=1, and filename to fn.png.
+ (*) Except Dials.
+
+--------------------------
+ Buttons (all button images contained horizontally)
+--------------------------
+
+Play/Pause/Stop/Shuffle/Repeat_Button: filename prelight status_light x y
+Time_Remaining/Time_Total_Button: filename prelight status_light x y
+
+Option line: | # images | Normal | Pressed | Lit | Lit | Prelit | Prelit |
+ | total | | | Normal | Pressed | Normal | Lit Normal |
+-----------------------+----------+--------+---------+--------+---------+--------+------------+
+ fn.png TRUE TRUE 1 1 | 6 | X | X | X | X | X | X |
+ fn.png TRUE FALSE 1 1 | 3 | X | X | | | X | |
+ fn.png FALSE TRUE 1 1 | 4 | X | X | X | X | | |
+ fn.png FALSE FALSE 1 1| 2 | X | X | | | | |
+
+Next/Prev/FF/RW/Playlist/Config/Iconify/Mixer/Exit/Alt_Skin_Button: filename prelight x y
+Volume_Up/Volume_Down/Balance_Left/Balance_Right_Button: filename prelight x y
+
+Option line: | # images | Normal | Pressed | Prelit |
+ | total | | | Normal |
+-----------------------+----------+--------+---------+--------+
+ fn.png TRUE 1 1 | 3 | X | X | X |
+ fn.png FALSE 1 1 | 2 | X | X | |
+
+
+--------------------------
+ Items (all item images contained vertically)
+--------------------------
+
+Stereo/Shuffle/Repeat/Mpegversion/Mpeglayer/Mpegmode/Status/Minus/Total_Item: filename x y
+
+Item: | # images | Image 1 | Image 2 | Image 3 | Image 4 | Image 5 |
+ | total | | | | | |
+-----------------------+----------+---------+---------+---------+---------+---------+
+ Stereo_Item | 3 | blank | mono | stereo | | |
+ Shuffle_Item | 2 | off | on | | | |
+ Repeat_Item | 2 | off | on | | | |
+ Mpegversion_Item | 3 | blank | ver 1 | ver 2 | | |
+ Mpeglayer_Item | 4 | blank | layer 1 | layer 2 | layer 3 | |
+ Mpegmode_Item | 5 | blank | stereo | j-stereo| dual-ch |single-ch|
+ Status_Item | 3 | stop | pause | play | | |
+ Minus_Item | 2 | normal |remaining| | | |
+ Total_Item | 2 | normal | total | live | | |
+
+
+Load_Item: filename section_count x y
+
+(These are special Animation items, any number of sections can be included)
+
+Option line: | # images | Image 1 | Image 2 | ....... | Last Image |
+ | total | | | | |
+-------------------+----------+---------+---------+---------+------------+
+ fn.png 8 1 1 | 8 | blank | Frame 1 | F2...F6 | Frame 7 |
+ fn.png 4 1 1 | 4 | blank | Frame 1 | Frame 2 | Frame 3 |
+
+
+Position/Volume/Balance_Item: filename sections x y
+
+Option Line: (*) | # images | 1st Image | middle Image | Last Image |
+ | total | | | |
+----------------------------+----------+-----------+--------------+---------------+
+ Volume_Item fn.png 17 1 1 | 17 | 1 - 0% vol| 9 - 50 % vol | 17 - 100% vol |
+ Volume_Item fn.png 31 1 1 | 31 | 1 - 0% vol| 16 - 50 % vol| 31 - 100% vol |
+ Balance_Item fn.png 17 1 1 | 17 | 1 - Left | 9 - middle | 17 - Right |
+ Balance_Item fn.png 13 1 1 | 13 | 1 - Left | 6 - middle | 13 - Right |
+
+ (*)note: The values (17, 31, 17, 13) above are only examples, any number of images
+ can be specified.
+
+
+--------------------------
+ Sliders (all slider images contained horizontally)
+--------------------------
+
+Position/Volume/Balance_Slider: filename prelight [verticle reversed] length x y
+
+Option line: | # images | Background | Normal | Pressed | Prelit |
+ | total | width | | | Normal |
+-----------------------------------+----------+-------------+--------+---------+--------+
+ fn.png TRUE FALSE FALSE 32 1 1 | 4 | 32 pixels | X | X | X |
+ fn.png FALSE FALSE FALSE 250 1 1 | 3 | 250 pixels | X | X | |
+
+
+--------------------------
+ Dials (can be confusing, see detailed description, above)
+--------------------------
+
+Position/Volume/Balance_Dial: filename has_press_image has_prelight_image reversed
+ angle_start angle_end handle_offset_x handle_offset_y center_x center_y
+ x y w h [clip_filename]
+
+Option line: | # images | Normal | Pressed | Prelit |
+ | total | | | Normal |
+----------------------------------------------------------+----------+--------+---------+--------+
+ fn.png TRUE TRUE TRUE 0 180 16 16 200 100 140 40 80 80 | 3 | X | X | X |
+ fn.png FALSE TRUE TRUE 0 180 16 16 200 100 140 40 80 80 | 2 | X | | X |
+ fn.png FALSE FALSE TRUE 0 180 16 16 200 100 140 40 80 80 | 1 | X | | |
+
+--------------------------
+ Numbers (all number images contained horizontally)
+--------------------------
+
+*_Number: filename [length centered] x y
+
+Option line: | # images | Images in order (left to right) |
+ | total | |
+---------------------+----------+------------------------------------------------+
+ fn.png 1 1 | 11 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " "(blank space) |
+ fn.png 3 FALSE 1 1 | 11 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " "(blank space) |
+
+
+--------------------------
+ Fonts (all font images contained in 3 or 6 rows of 32 columns)
+--------------------------
+
+Title/Album/Artist/Genre: filename length [extended] x y
+
+Option line: | # images | Images in order |
+ | total | |
+------------------------+----------+--------------------+
+ fn.png 16 1 1 | 96 | see grid 1 (below) |
+ fn.png 16 FALSE 1 1 | 96 | see grid 1 (below) |
+ fn.png 16 TRUE 1 1 | 192 | see grid 2 (below) |
+
+ +------------------------------------+
+character grid 1: | |
+(standard) | !"#$%&'()*+,-./0123456789:;<=>? |
+ | @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ |
+ | `abcdefghijklmnopqrstuvwxyz{|}~ |
+ | |
+ +------------------------------------+
+
+ +------------------------------------+
+character grid 2: | |
+(international | !"#$%&'()*+,-./0123456789:;<=>? |
+ extended) | @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ |
+ | `abcdefghijklmnopqrstuvwxyz{|}~ |
+ | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
+ | XXXXXXX FIXME! TO DO! XXXXXXXXXX |
+ | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
+ | |
+ +------------------------------------+
+
+#################################
diff --git a/noatun/modules/kaiman/kaiman.plugin b/noatun/modules/kaiman/kaiman.plugin
new file mode 100644
index 00000000..1b345de3
--- /dev/null
+++ b/noatun/modules/kaiman/kaiman.plugin
@@ -0,0 +1,132 @@
+Filename=noatun_kaiman.la
+Author=Stefan Schimanski
+Site=http://www.derkarl.org/noatun
+Email=schimmi@kde.org
+Type=userinterface
+License=GPL
+Name=Kaiman Interface
+Name[af]=Kaiman Koppelvlak
+Name[ar]=واجهة Kaiman
+Name[az]=Kaiman Ara üzü
+Name[bn]=কাইমান ইন্টারফেস
+Name[br]=Etrefas Kaiman
+Name[ca]=Interfície Kaiman
+Name[cs]=Rozhraní Kaimana
+Name[cy]=Rhyngwyneb Kaiman
+Name[da]=Kaiman-grænseflade
+Name[de]=Kaiman-Oberfläche
+Name[el]=Περιβάλλον Kaiman
+Name[eo]=Kajmaninterfaco
+Name[es]=Interfaz de Kaiman
+Name[et]=Kaiman kasutajaliides
+Name[eu]=Kaiman interfazea
+Name[fa]=واسط Kaiman
+Name[fi]=Kaiman-käyttöliittymä
+Name[fr]=Interface de Kaiman
+Name[ga]=Comhéadan Kaiman
+Name[gl]=Interface Kayman
+Name[he]=ממשק Kaiman
+Name[hi]= काईमेन इंटरफेस
+Name[hr]=Kaiman sučelje
+Name[hu]=Kaiman felület
+Name[is]=Kaiman aðgangur
+Name[it]=Interfaccia Kaiman
+Name[ja]=Kaiman インターフェース
+Name[kk]=Kaiman интерфейсі
+Name[km]=ចំណុច​ប្រទាក់ Kaiman
+Name[ko]=Kaiman 인터페이스
+Name[lt]=Kaiman sąsaja
+Name[lv]=Kaiman Starpseja
+Name[mk]=Интерфејс Kaiman
+Name[mt]=Interfaċċja Kaiman
+Name[nb]=Kaiman grensesnitt
+Name[nds]=Kaiman-Böversiet
+Name[ne]=काइम्यान इन्टरफेस
+Name[nl]=Kaiman interface
+Name[nn]=Kaiman-grensesnitt
+Name[pl]=Motyw Kaimana
+Name[pt]=Interface do Kaiman
+Name[pt_BR]=Interface do Kaiman
+Name[ro]=Interfaţă Kaiman
+Name[ru]=Интерфейс Кайман
+Name[se]=Kaiman-lakta
+Name[sk]=Rozhranie Kaimana
+Name[sl]=Vmesnik Kaiman
+Name[sr]=Kaiman интерфејс
+Name[sr@Latn]=Kaiman interfejs
+Name[sv]=Kaiman-gränssnitt
+Name[ta]=Kaiman இடைமுகம்
+Name[tg]=Интерфейси Kaiman
+Name[th]=ส่วนติดต่อ Kaiman
+Name[tr]=Kaiman Arayüzü
+Name[uk]=Інтерфейс Kaiman
+Name[uz]=Kaiman interfeysi
+Name[uz@cyrillic]=Kaiman интерфейси
+Name[ven]=Interface ya Kaiman
+Name[xh]=Ujongano lwe Kaiman
+Name[zh_CN]=Kaiman 接口
+Name[zh_HK]=Kaiman 介面
+Name[zh_TW]=Kaiman 介面
+Name[zu]=Uxhumano olubhekeneyo lwe Kaiman
+Comment=A GQMpeg skin interface ported from Kaiman
+Comment[af]='n Gqmpeg vel koppelvlak oorgedra van Kaiman
+Comment[ar]=واجهة GQMpeg مأخوذة من Kaiman
+Comment[az]=Kaiman'dan alınan GQMpeg dekorsiya axtar üzü
+Comment[bg]=Интерфейс за GQMpeg прехвърлен за Kaiman
+Comment[bs]=GQMpeg skin interface prebačen sa Kaiman-a
+Comment[ca]=Una aparença d'interfície GQMpeg portada de Kaiman
+Comment[cs]=Motiv rozhraní GQMpegu přenesený z Kaimana
+Comment[cy]=Rhyngwyneb croen GQMpeg wedi'i droi o Kaiman
+Comment[da]=En GQMpeg-forsidegrænseflade overført fra Kaiman
+Comment[de]=Eine Schnittstelle zur GQMpeg-Optik, aus Kaiman übernommen
+Comment[el]=Μια διασύνδεση βασισμένη στο θέμα GQMpeg προσαρμοσμένο από το Kaiman
+Comment[eo]=GQMpeg-etosinterfaco portita de Kajmano
+Comment[es]=Una interfaz de pieles GQMpeg portado de Kaiman
+Comment[et]=Kaimanist porditud GQMpeg skinnide toetus
+Comment[eu]=GQMpeg azal interfazea Kaiman-etik ekarria
+Comment[fa]=یک واسط GQMpeg skin که از Kaiman آورده شده است
+Comment[fi]=GQMpeg-käyttöliittymärajapinta Kaimanille
+Comment[fr]=Un revêtement à la GQMpeg importé de Kaiman
+Comment[gl]=Unha pel para a interface GQMPeg importada de Kaiman
+Comment[he]=ממשק Skin של GQMpeg שיובא מתוך Kaiman
+Comment[hi]=काईमेन से पोर्टेड जीक्यू-एमपीईजी इंटरफेस
+Comment[hr]=GQMpeg sučelje za kože uvezeno iz Kaiman-a
+Comment[hu]=A Kaimanban használt GQMpeg kinézet átültetett változata
+Comment[is]=GQMpeg skinn frá Kaiman
+Comment[it]=Una skin per GQMpeg convertita da Kaiman
+Comment[ja]=Kaiman から移植した GQMpeg スキンインターフェース
+Comment[kk]=Kaiman-нан аударылған GQMpeg тыстарының интерфейсі
+Comment[km]=ចំណុច​ប្រទាក់​ស្បែក GQMpeg ដែល​បាន​បញ្ចូល​ពី Kaiman
+Comment[ko]=Kaiman에서 이식된 GQMpeg 스킨
+Comment[lt]=GQMpeg pavidalų sąsaja, pritaikyta nuo Kaiman
+Comment[lv]=GQMpeg ādu starpseja pārcelta no Kaimana
+Comment[mk]=Интерфејс GQMpeg за маски пренесен од Kaiman
+Comment[ms]=Kulit antaramuka GQMpeg dari Kaiman
+Comment[mt]=Interfaċċja għal faċċati GQMpeg portata minn Kaiman
+Comment[nb]=Et GQMpeg ham-grensesnitt tatt fra Kaiman
+Comment[nds]=En GQMpeg-Böversiet, vun Kaiman överdragen
+Comment[ne]=काइम्यानबाट परिमार्जन गरिएको GQMpeg स्किन इन्टरफेस
+Comment[nl]=Een GQMpeg-skin-interface, overgedragen van Kaiman
+Comment[nn]=Eit GQMpeg-skalgrensesnitt porta frå Kaiman
+Comment[pl]=Motyw skór GQMpeg przeniesiony z Kaimana
+Comment[pt]=A interface de aspectos do GQMpeg transposta para o Kaiman
+Comment[pt_BR]=Uma interface de aparência (skin) GQMpeg portada do Kaiman
+Comment[ro]=O interfaţă GQMpeg portată de la Kaiman
+Comment[ru]=Интерфейс образов GQMpeg, перенесенный из Каймана
+Comment[se]=GQMpeg-náhkkelakta portejuvvon Kaimanas
+Comment[sk]=Téma rozhrania GQMpeq prenesená z Kaimana
+Comment[sl]=Vmesnik preobleke GQMpeg, prenesen iz Kaimana
+Comment[sr]=GQMpeg интерфејс скинова пренесен са из Kaiman-а
+Comment[sr@Latn]=GQMpeg interfejs skinova prenesen sa iz Kaiman-a
+Comment[sv]=Gqmpeg-skalgränssnitt överfört från Kaiman
+Comment[ta]=GQMpeg தோல் இடைமுகம் காய்மானில் இருந்து இறக்கப்பட்டது
+Comment[tg]=Намуди интерфейси GQMpeg, ки аз Kaiman даргоҳбандӣ шудааст
+Comment[th]=ส่วนติดต่อหน้ากาก GQMpeg ที่ข้ามระบบมาให้ใช้กับ Kaiman
+Comment[tr]=Kaiman'dan alınan GQMpeg dekor arayüzü
+Comment[uk]=Інтерфейс жупанів GQMpeg, перенесено з Kaiman
+Comment[ven]=Lukanda lwa GQMpeg lu vhonwaho kha Kaiman
+Comment[xh]=GQMpeg wojongano nolusu olunezibuko olusuka kwi Kaiman
+Comment[zh_CN]=从 Kaiman 移植的 GQMpeg 外观
+Comment[zh_HK]=從 Kaiman 移植的 GQMpeg 外貌主題
+Comment[zh_TW]=從 Kaiman 移植的 GQMpeg 外表
+Comment[zu]=A GQMpeg uxhumano lwesikhumba ported from Kaiman
diff --git a/noatun/modules/kaiman/kaimanui.rc b/noatun/modules/kaiman/kaimanui.rc
new file mode 100644
index 00000000..fb2696ff
--- /dev/null
+++ b/noatun/modules/kaiman/kaimanui.rc
@@ -0,0 +1,45 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="noatunkaiman" version="1">
+<ActionProperties>
+ <Action name="play" icon="noatunplay"/>
+ <Action name="stop" icon="noatunstop"/>
+ <Action name="back" icon="noatunback"/>
+ <Action name="forward" icon="noatunforward"/>
+ <Action name="show_playlist" icon="noatunplaylist"/>
+</ActionProperties>
+<MenuBar>
+ <Menu name="file" noMerge="1"><text>&amp;File</text>
+ <Action name="file_open"/>
+ <Separator lineSeparator="true"/>
+ <Action name="file_quit"/>
+ </Menu>
+ <Menu name="go_music" noMerge="1"><text>&amp;Go</text>
+ <Action name="back"/>
+ <Action name="stop"/>
+ <Action name="play"/>
+ <Action name="forward"/>
+ </Menu>
+ <Menu name="settings" noMerge="1"><text>&amp;Settings</text>
+ <Action name="options_show_toolbar"/>
+ <Action name="show_playlist"/>
+ <Separator lineSeparator="true"/>
+ <Action name="options_configure"/>
+ <Action name="effects"/>
+ <Separator lineSeparator="true"/>
+ <Action name="loop_style"/>
+ </Menu>
+</MenuBar>
+<Toolbar name="main"><text>Main Toolbar</text>
+ <Action name="file_quit"/>
+ <Separator lineSeparator="true"/>
+ <Action name="back"/>
+ <Action name="stop"/>
+ <Action name="play"/>
+ <Action name="forward"/>
+ <Separator lineSeparator="true"/>
+ <Action name="file_open"/>
+ <Action name="show_playlist"/>
+ <Separator lineSeparator="true"/>
+ <Action name="loop_style"/>
+</Toolbar>
+</kpartgui>
diff --git a/noatun/modules/kaiman/noatunui.cpp b/noatun/modules/kaiman/noatunui.cpp
new file mode 100644
index 00000000..bc1bceb0
--- /dev/null
+++ b/noatun/modules/kaiman/noatunui.cpp
@@ -0,0 +1,9 @@
+#include "userinterface.h"
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new Kaiman();
+ }
+}
diff --git a/noatun/modules/kaiman/pref.cpp b/noatun/modules/kaiman/pref.cpp
new file mode 100644
index 00000000..892435fc
--- /dev/null
+++ b/noatun/modules/kaiman/pref.cpp
@@ -0,0 +1,122 @@
+/*
+ Copyright (c) 1999-2000 Stefan Schimanski <1Stein@gmx.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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <klocale.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <klistbox.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <kglobal.h>
+#include <kdebug.h>
+#include <kstandarddirs.h>
+#include <kconfig.h>
+
+#include "pref.h"
+#include "userinterface.h"
+
+
+KaimanPrefDlg::KaimanPrefDlg(QObject *parent )
+ : CModule(i18n("Kaiman Skins"), i18n("Skin Selection for the Kaiman Plugin"), "style", parent)
+{
+ // create widgets
+ QVBoxLayout *topLayout = new QVBoxLayout( this, 6, 11 );
+ QLabel *label = new QLabel( i18n("Kaiman Skins"), this, "label" );
+ topLayout->addWidget( label );
+
+ _skinList = new KListBox( this, "skinList" );
+ topLayout->addWidget( _skinList, 1 );
+ reopen();
+}
+
+
+KaimanPrefDlg::~KaimanPrefDlg()
+{
+}
+
+
+void KaimanPrefDlg::save()
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup("Kaiman");
+ config->writeEntry("SkinResource", skin() );
+ config->sync();
+
+ Kaiman *l=Kaiman::kaiman;
+ if ( l ) {
+ l->changeStyle( skin() );
+ }
+}
+
+void KaimanPrefDlg::reopen()
+{
+ _skinList->clear();
+ // fill with available skins
+ KGlobal::dirs()->addResourceType("skins", KStandardDirs::kde_default("data") + "noatun/skins/kaiman/");
+ QStringList list = KGlobal::dirs()->resourceDirs("skins");
+ for (QStringList::ConstIterator it = list.begin(); it != list.end(); it++)
+ readSkinDir(*it);
+
+ // load current config
+ KConfig *config=KGlobal::config();
+ config->setGroup("Kaiman");
+ QString skin = config->readEntry( "SkinResource", Kaiman::DEFAULT_SKIN );
+ QListBoxItem *item = _skinList->findItem( skin );
+ if ( item )
+ _skinList->setCurrentItem( item );
+ else
+ _skinList->setCurrentItem( 0 );
+}
+
+
+void KaimanPrefDlg::setSkin( QString skin )
+{
+ _skinList->setCurrentItem( _skinList->findItem( skin ) );
+}
+
+
+QString KaimanPrefDlg::skin()
+{
+ return _skinList->currentText();
+}
+
+
+void KaimanPrefDlg::readSkinDir( const QString &dir )
+{
+ kdDebug() << "readSkinDir " << dir << endl;
+
+ QDir directory( dir );
+ if (!directory.exists())
+ return;
+
+ const QFileInfoList *list = directory.entryInfoList();
+ QFileInfoListIterator it(*list);
+
+ while ( it.current() ) {
+ kdDebug() << it.current()->absFilePath() << endl;
+ QFileInfo skindata( it.current()->absFilePath()+"/skindata" );
+
+ if ( skindata.exists() ) {
+ _skinList->insertItem( it.current()->baseName() );
+ }
+
+ ++it;
+ }
+}
+
+#include "pref.moc"
diff --git a/noatun/modules/kaiman/pref.h b/noatun/modules/kaiman/pref.h
new file mode 100644
index 00000000..551dd16a
--- /dev/null
+++ b/noatun/modules/kaiman/pref.h
@@ -0,0 +1,48 @@
+/*
+ Copyright (c) 1999-2000 Stefan Schimanski <1Stein@gmx.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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef PREF_H_INCLUDED
+#define PREF_H_INCLUDED
+
+#include <noatun/pref.h>
+
+class KListBox;
+class QLabel;
+
+class KaimanPrefDlg : public CModule
+{
+ Q_OBJECT
+ public:
+ KaimanPrefDlg( QObject *parent );
+ virtual ~KaimanPrefDlg();
+
+ virtual void save();
+ virtual void reopen();
+
+ public slots:
+ void setSkin( QString skin );
+ QString skin();
+
+ private:
+ void readSkinDir( const QString &dir );
+
+ KListBox *_skinList;
+};
+
+#endif
+
diff --git a/noatun/modules/kaiman/skins/Makefile.am b/noatun/modules/kaiman/skins/Makefile.am
new file mode 100644
index 00000000..0d3d6687
--- /dev/null
+++ b/noatun/modules/kaiman/skins/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = k9 car-preset circle
+
+skinsdir = $(kde_datadir)/Skins
diff --git a/noatun/modules/kaiman/skins/car-preset/Makefile.am b/noatun/modules/kaiman/skins/car-preset/Makefile.am
new file mode 100644
index 00000000..646898d0
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/Makefile.am
@@ -0,0 +1,10 @@
+skin_DATA = btn_p1.png btn_play.png digbig.png random.png \
+ btn_p2.png btn_prev.png digmed.png repeat.png \
+btn_exit.png btn_p3.png btn_sml.png letters.png skindata \
+btn_iconify.png btn_p4.png btn_stop.png main.png status.png \
+btn_list.png btn_p5.png btn_voldn.png monoster.png volume.png \
+btn_next.png btn_p6.png btn_volup.png posbar.png
+
+skindir = $(kde_datadir)/noatun/skins/kaiman/car-preset
+
+EXTRA_DIST = $(skin_DATA)
diff --git a/noatun/modules/kaiman/skins/car-preset/README b/noatun/modules/kaiman/skins/car-preset/README
new file mode 100644
index 00000000..fedefaaa
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/README
@@ -0,0 +1,22 @@
+GQmpeg skin directory: car-preset
+Author: Johne Ellis <gqview@geocities.ocm>
+Released: October 25, 1998
+Version: 1.0
+URL:
+Comment: Skin similar to an automotive stereo, complete
+ with presets.
+Note: For the presets and volume controls to work, GQmpeg
+ 0.3.6 is required. Previous versions will work,
+ except for these functions.
+
+To use this skin with GQmpeg, use the command line:
+
+ gqmpeg -skin:car-preset
+
+Or when editing skin data, point GQmpeg to this skin with:
+
+ gqmpeg -skin:../car-preset
+
+or to allow the skin to be the default skin copy this directory
+to 'HOME/.gqmpeg/skins/car-preset' and specify 'car-preset' as the
+skin on the skin tab of the config dialog.
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_exit.png b/noatun/modules/kaiman/skins/car-preset/btn_exit.png
new file mode 100644
index 00000000..5bac9d23
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_exit.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_iconify.png b/noatun/modules/kaiman/skins/car-preset/btn_iconify.png
new file mode 100644
index 00000000..81b2859a
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_iconify.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_list.png b/noatun/modules/kaiman/skins/car-preset/btn_list.png
new file mode 100644
index 00000000..1bea110e
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_list.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_next.png b/noatun/modules/kaiman/skins/car-preset/btn_next.png
new file mode 100644
index 00000000..67a3db2a
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_next.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_p1.png b/noatun/modules/kaiman/skins/car-preset/btn_p1.png
new file mode 100644
index 00000000..4877b86e
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_p1.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_p2.png b/noatun/modules/kaiman/skins/car-preset/btn_p2.png
new file mode 100644
index 00000000..1f6b1f41
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_p2.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_p3.png b/noatun/modules/kaiman/skins/car-preset/btn_p3.png
new file mode 100644
index 00000000..d3ec7ab5
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_p3.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_p4.png b/noatun/modules/kaiman/skins/car-preset/btn_p4.png
new file mode 100644
index 00000000..16b57b7c
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_p4.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_p5.png b/noatun/modules/kaiman/skins/car-preset/btn_p5.png
new file mode 100644
index 00000000..18d65172
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_p5.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_p6.png b/noatun/modules/kaiman/skins/car-preset/btn_p6.png
new file mode 100644
index 00000000..2b0eba96
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_p6.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_play.png b/noatun/modules/kaiman/skins/car-preset/btn_play.png
new file mode 100644
index 00000000..814cbbf9
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_play.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_prev.png b/noatun/modules/kaiman/skins/car-preset/btn_prev.png
new file mode 100644
index 00000000..ffdc59a5
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_prev.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_sml.png b/noatun/modules/kaiman/skins/car-preset/btn_sml.png
new file mode 100644
index 00000000..a2acff28
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_sml.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_stop.png b/noatun/modules/kaiman/skins/car-preset/btn_stop.png
new file mode 100644
index 00000000..faca588f
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_stop.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_voldn.png b/noatun/modules/kaiman/skins/car-preset/btn_voldn.png
new file mode 100644
index 00000000..15e40697
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_voldn.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/btn_volup.png b/noatun/modules/kaiman/skins/car-preset/btn_volup.png
new file mode 100644
index 00000000..9e6c8964
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/btn_volup.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/digbig.png b/noatun/modules/kaiman/skins/car-preset/digbig.png
new file mode 100644
index 00000000..44ea6a9d
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/digbig.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/digmed.png b/noatun/modules/kaiman/skins/car-preset/digmed.png
new file mode 100644
index 00000000..55a43732
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/digmed.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/letters.png b/noatun/modules/kaiman/skins/car-preset/letters.png
new file mode 100644
index 00000000..7f59af69
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/letters.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/main.png b/noatun/modules/kaiman/skins/car-preset/main.png
new file mode 100644
index 00000000..2601e03b
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/main.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/monoster.png b/noatun/modules/kaiman/skins/car-preset/monoster.png
new file mode 100644
index 00000000..fe8129b7
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/monoster.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/posbar.png b/noatun/modules/kaiman/skins/car-preset/posbar.png
new file mode 100644
index 00000000..3d6eb8e8
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/posbar.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/random.png b/noatun/modules/kaiman/skins/car-preset/random.png
new file mode 100644
index 00000000..30b99726
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/random.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/repeat.png b/noatun/modules/kaiman/skins/car-preset/repeat.png
new file mode 100644
index 00000000..022648fd
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/repeat.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/skindata b/noatun/modules/kaiman/skins/car-preset/skindata
new file mode 100644
index 00000000..7a9651e0
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/skindata
@@ -0,0 +1,71 @@
+#GQmpeg skin data file
+
+#Title: Car faceplate with presets
+#Version: 1
+#Released: October 25, 1998
+#Author: John Ellis <gqview@geocities.com>
+#URL:
+#Comments: Skin similar to an automotive stereo, complete
+# with presets.
+
+#run 'gqmpeg -skinhelp' for help with coordinates.
+#simply comment out items you do not want to display
+#only Background is required.
+Background: main.png
+
+#Title: filename length x y
+Title: letters.png 16 60 18
+
+#Play/Pause/Stop/Shuffle/Repeat_Button: filename prelight status_light x y
+Play_Button: btn_play.png FALSE FALSE 25 32
+Stop_Button: btn_stop.png FALSE FALSE 4 32
+Shuffle_Button: btn_sml.png FALSE FALSE 265 41
+Repeat_Button: btn_sml.png FALSE FALSE 283 41
+
+#Next/Prev/FF/RW/Playlist/Config/
+# Iconify/Mixer/Exit/Alt_Skin_Button: prelight x y
+Next_Button: btn_next.png FALSE 7 18
+Prev_Button: btn_prev.png FALSE 7 51
+Playlist_Button: btn_list.png FALSE 11 66
+Config_Button: btn_sml.png FALSE 283 23
+Iconify_Button: btn_iconify.png FALSE 263 4
+Exit_Button: btn_exit.png FALSE 280 4
+
+#Stereo/Shuffle/Repeat/Mpegver/Mpeglayer/Mpegmode/Status_Item: filename x y
+Stereo_Item: monoster.png 161 49
+Shuffle_Item: random.png 161 38
+Repeat_Item: repeat.png 205 38
+Status_Item: status.png 57 39
+
+#you can define one or both of these first:
+Digit_Large_Default: digbig.png
+Digit_Small_Default: digmed.png
+#then use Large or Small as the filename in *_Number below (convenience feature)
+
+#Minute/Second/Song/Total/In_Rate/In_Hz/Out_Bits/Out_Hz/Frame/CPU_Number: filename x y
+Minute_Number: Large 65 38
+Second_Number: Large 97 38
+Song_Number: Small 129 44
+CPU_Number: Small 236 44
+
+#Volume/Balance_Item: filename sections x y
+# (these 2 items must be before their respective Volume/Balance_Sliders)
+Volume_Item: volume.png 17 181 48
+
+#Volume_Up/Volume_Down/Balance_Left/Balance_Right_Button: filename prelight x y
+Volume_Up_Button: btn_volup.png FALSE 261 60
+Volume_Down_Button: btn_voldn.png FALSE 279 60
+
+#Position/Volume/Balance_Slider: filename prelight length x y
+Position_Slider: posbar.png FALSE 195 58 5
+
+#Preset_1_ ... Preset_10_Button: filename prelight x y
+Preset_1_Button: btn_p1.png FALSE 58 66
+Preset_2_Button: btn_p2.png FALSE 91 66
+Preset_3_Button: btn_p3.png FALSE 124 66
+Preset_4_Button: btn_p4.png FALSE 157 66
+Preset_5_Button: btn_p5.png FALSE 190 66
+Preset_6_Button: btn_p6.png FALSE 223 66
+
+# end
+
diff --git a/noatun/modules/kaiman/skins/car-preset/status.png b/noatun/modules/kaiman/skins/car-preset/status.png
new file mode 100644
index 00000000..05ceed86
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/status.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/car-preset/volume.png b/noatun/modules/kaiman/skins/car-preset/volume.png
new file mode 100644
index 00000000..f690e79d
--- /dev/null
+++ b/noatun/modules/kaiman/skins/car-preset/volume.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/Makefile.am b/noatun/modules/kaiman/skins/circle/Makefile.am
new file mode 100644
index 00000000..6c8ae5f2
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/Makefile.am
@@ -0,0 +1,13 @@
+skin_DATA = btn_exit.png btn_repeat.png btn_sm_stop.png \
+ btn_iconify.png btn_shuffle.png btn_stop.png \
+back.png btn_list.png btn_sm_exit.png dig.png \
+back_mask.png btn_mode.png btn_sm_iconify.png digsml.png \
+back_sm.png btn_next.png btn_sm_mode.png letters.png \
+back_sm_mask.png btn_play.png btn_sm_next.png skindata \
+bar_pos.png btn_pref.png btn_sm_play.png \
+bar_vol.png btn_prev.png btn_sm_prev.png status.png
+
+
+skindir = $(kde_datadir)/noatun/skins/kaiman/circle
+
+EXTRA_DIST = $(skin_DATA)
diff --git a/noatun/modules/kaiman/skins/circle/README b/noatun/modules/kaiman/skins/circle/README
new file mode 100644
index 00000000..d80082af
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/README
@@ -0,0 +1,22 @@
+GQmpeg skin directory: circle
+Author: Johne Ellis <gqview@geocities.ocm>
+Released: November 25, 1998
+Version: 1.0
+URL: http://www.geocities.com/SiliconValley/Haven/5235
+Comments: Skin with a doughnut shape to test shaped windows.
+ (skins with a shape mask)
+
+Note: For transparency to work, GQmpeg 0.4.2 is required.
+ Previous versions will work, but will be _ugly_.
+
+To use this skin with GQmpeg, use the command line:
+
+ gqmpeg -skin:circle
+
+Or when editing skin data, point GQmpeg to this skin with:
+
+ gqmpeg -skin:../circle
+
+or to allow the skin to be the default skin copy this directory
+to 'HOME/.gqmpeg/skins/circle' and specify 'circle' as the
+skin on the skin tab of the config dialog.
diff --git a/noatun/modules/kaiman/skins/circle/back.png b/noatun/modules/kaiman/skins/circle/back.png
new file mode 100644
index 00000000..83758a14
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/back.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/back_mask.png b/noatun/modules/kaiman/skins/circle/back_mask.png
new file mode 100644
index 00000000..de54ddef
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/back_mask.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/back_sm.png b/noatun/modules/kaiman/skins/circle/back_sm.png
new file mode 100644
index 00000000..3263c2db
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/back_sm.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/back_sm_mask.png b/noatun/modules/kaiman/skins/circle/back_sm_mask.png
new file mode 100644
index 00000000..315d88bf
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/back_sm_mask.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/bar_pos.png b/noatun/modules/kaiman/skins/circle/bar_pos.png
new file mode 100644
index 00000000..e6ac85a3
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/bar_pos.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/bar_vol.png b/noatun/modules/kaiman/skins/circle/bar_vol.png
new file mode 100644
index 00000000..88f88651
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/bar_vol.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_exit.png b/noatun/modules/kaiman/skins/circle/btn_exit.png
new file mode 100644
index 00000000..cd36d2fd
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_exit.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_iconify.png b/noatun/modules/kaiman/skins/circle/btn_iconify.png
new file mode 100644
index 00000000..20647819
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_iconify.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_list.png b/noatun/modules/kaiman/skins/circle/btn_list.png
new file mode 100644
index 00000000..f51e322b
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_list.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_mode.png b/noatun/modules/kaiman/skins/circle/btn_mode.png
new file mode 100644
index 00000000..4c6db0e1
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_mode.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_next.png b/noatun/modules/kaiman/skins/circle/btn_next.png
new file mode 100644
index 00000000..4ae8ee71
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_next.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_play.png b/noatun/modules/kaiman/skins/circle/btn_play.png
new file mode 100644
index 00000000..f21d287e
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_play.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_pref.png b/noatun/modules/kaiman/skins/circle/btn_pref.png
new file mode 100644
index 00000000..9337e704
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_pref.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_prev.png b/noatun/modules/kaiman/skins/circle/btn_prev.png
new file mode 100644
index 00000000..c65a9298
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_prev.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_repeat.png b/noatun/modules/kaiman/skins/circle/btn_repeat.png
new file mode 100644
index 00000000..48b12a90
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_repeat.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_shuffle.png b/noatun/modules/kaiman/skins/circle/btn_shuffle.png
new file mode 100644
index 00000000..75ad39f9
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_shuffle.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_sm_exit.png b/noatun/modules/kaiman/skins/circle/btn_sm_exit.png
new file mode 100644
index 00000000..f14752f8
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_sm_exit.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_sm_iconify.png b/noatun/modules/kaiman/skins/circle/btn_sm_iconify.png
new file mode 100644
index 00000000..69b9332c
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_sm_iconify.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_sm_mode.png b/noatun/modules/kaiman/skins/circle/btn_sm_mode.png
new file mode 100644
index 00000000..79b4c5a0
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_sm_mode.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_sm_next.png b/noatun/modules/kaiman/skins/circle/btn_sm_next.png
new file mode 100644
index 00000000..66c01ddb
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_sm_next.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_sm_play.png b/noatun/modules/kaiman/skins/circle/btn_sm_play.png
new file mode 100644
index 00000000..191551eb
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_sm_play.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_sm_prev.png b/noatun/modules/kaiman/skins/circle/btn_sm_prev.png
new file mode 100644
index 00000000..d70dbb53
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_sm_prev.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_sm_stop.png b/noatun/modules/kaiman/skins/circle/btn_sm_stop.png
new file mode 100644
index 00000000..0d0841d7
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_sm_stop.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/btn_stop.png b/noatun/modules/kaiman/skins/circle/btn_stop.png
new file mode 100644
index 00000000..2563dbc0
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/btn_stop.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/dig.png b/noatun/modules/kaiman/skins/circle/dig.png
new file mode 100644
index 00000000..c417de15
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/dig.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/digsml.png b/noatun/modules/kaiman/skins/circle/digsml.png
new file mode 100644
index 00000000..29f2ed43
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/digsml.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/letters.png b/noatun/modules/kaiman/skins/circle/letters.png
new file mode 100644
index 00000000..b4322d62
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/letters.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/circle/skindata b/noatun/modules/kaiman/skins/circle/skindata
new file mode 100644
index 00000000..25268d22
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/skindata
@@ -0,0 +1,58 @@
+#GQmpeg skin data file
+#tested on version 0.4.2
+
+#Title: circle
+#Version: 1
+#Released: November 25, 1998
+#Author: John Ellis <gqview@geocities.com>
+#URL: http://www.geocities.com/SiliconValley/Haven/5235/
+#Comments: Skin with a doughnut shape to test shaped windows.
+# (skins with a shape mask)
+
+#run 'gqmpeg -skinhelp' for help with coordinates.
+#simply comment out items you do not want to display
+#only Background is required.
+Background: back.png
+
+#Mask is an image with transparency used to define a shaped window
+Mask: back_mask.png
+
+#Title: filename length x y
+Title: letters.png 23 32 86
+
+#Play/Pause/Stop/Shuffle/Repeat_Button: filename prelight status_light x y
+Play_Button: btn_play.png TRUE TRUE 125 160
+Stop_Button: btn_stop.png TRUE TRUE 44 160
+Shuffle_Button: btn_shuffle.png TRUE TRUE 151 29
+Repeat_Button: btn_repeat.png TRUE TRUE 164 49
+
+#Next/Prev/FF/RW/Playlist/Config/
+# Iconify/Mixer/Exit/Alt_Skin_Button: prelight x y
+Next_Button: btn_next.png TRUE 125 10
+Prev_Button: btn_prev.png TRUE 44 10
+Playlist_Button: btn_list.png TRUE 158 130
+Config_Button: btn_pref.png TRUE 13 130
+Iconify_Button: btn_iconify.png TRUE 3 69
+Exit_Button: btn_exit.png TRUE 13 40
+Alt_Skin_Button: btn_mode.png TRUE 3 103
+
+#Stereo/Shuffle/Repeat/Mpegver/Mpeglayer/Mpegmode/Status_Item: filename x y
+#Stereo_Item: stereo.png 280 26
+Status_Item: status.png 82 20
+
+#you can define one or both of these first:
+Digit_Large_Default: dig.png
+#then use Large or Small as the filename in *_Number below (convenience feature)
+
+#Minute/Second/Song/Total/In_Rate/In_Hz/Out_Bits/Out_Hz/Frame/CPU_Number: filename x y
+Minute_Number: Large 79 176
+Second_Number: Large 102 176
+Song_Number: Large 91 7
+CPU_Number: digsml.png 78 8
+
+#Position/Volume/Balance_Slider: filename prelight verticle reversed length x y
+Position_Slider: bar_pos.png TRUE FALSE FALSE 142 30 103
+Volume_Slider: bar_vol.png TRUE TRUE TRUE 46 175 77
+
+# end
+
diff --git a/noatun/modules/kaiman/skins/circle/skindata_alt b/noatun/modules/kaiman/skins/circle/skindata_alt
new file mode 100644
index 00000000..fb3e97f5
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/skindata_alt
@@ -0,0 +1,44 @@
+#GQmpeg skin data file
+#tested on version 0.4.2
+
+#Title: circle
+#Version: 1
+#Released: November 25, 1998
+#Author: John Ellis <gqview@geocities.com>
+#URL: http://www.geocities.com/SiliconValley/Haven/5235/
+#Comments: Skin with a doughnut shape to test shaped windows.
+# (skins with a shape mask)
+
+#run 'gqmpeg -skinhelp' for help with coordinates.
+#simply comment out items you do not want to display
+#only Background is required.
+Background: back_sm.png
+
+#Mask is an image with transparency used to define a shaped window
+Mask: back_sm_mask.png
+
+#Title: filename length x y
+Title: letters.png 21 8 4
+
+#Play/Pause/Stop/Shuffle/Repeat_Button: filename prelight status_light x y
+Play_Button: btn_sm_play.png TRUE TRUE 21 21
+Stop_Button: btn_sm_stop.png TRUE TRUE 4 21
+
+#Next/Prev/FF/RW/Playlist/Config/
+# Iconify/Mixer/Exit/Alt_Skin_Button: prelight x y
+Next_Button: btn_sm_next.png TRUE 87 21
+Prev_Button: btn_sm_prev.png TRUE 70 21
+Iconify_Button: btn_sm_iconify.png TRUE 120 29
+Exit_Button: btn_sm_exit.png TRUE 120 21
+Alt_Skin_Button: btn_sm_mode.png TRUE 104 21
+
+#you can define one or both of these first:
+Digit_Small_Default: digsml.png
+#then use Large or Small as the filename in *_Number below (convenience feature)
+
+#Minute/Second/Song/Total/In_Rate/In_Hz/Out_Bits/Out_Hz/Frame/CPU_Number: filename x y
+Minute_Number: Small 41 24
+Second_Number: Small 55 24
+
+# end
+
diff --git a/noatun/modules/kaiman/skins/circle/status.png b/noatun/modules/kaiman/skins/circle/status.png
new file mode 100644
index 00000000..b29e075c
--- /dev/null
+++ b/noatun/modules/kaiman/skins/circle/status.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/Makefile.am b/noatun/modules/kaiman/skins/k9/Makefile.am
new file mode 100644
index 00000000..bac3bd84
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/Makefile.am
@@ -0,0 +1,11 @@
+skin_DATA = README skindata conf.jpg conf.png eject.jpg icon.jpg icon.png \
+ kill.jpg kill.png knine-nfont.jpg knine-nfont.png knine-nfont2.jpg \
+ knine-nfont2.png knine-normal2.jpg knine-normal2.png knine-vfont.jpg \
+ knine-vfont.png long2.jpg mask.png newtext.jpg newtext.png next.jpg \
+ pause.jpg play.jpg pos_item.jpg repeat.jpg repeat.png reverse.jpg \
+ shuffle.jpg shuffle.png small-k.jpg small-k.png square.jpg square.png \
+ status.jpg status.png stop.jpg trans-pos.png trans-slide.png
+
+skindir = $(kde_datadir)/noatun/skins/kaiman/k9
+
+EXTRA_DIST = $(skin_DATA)
diff --git a/noatun/modules/kaiman/skins/k9/README b/noatun/modules/kaiman/skins/k9/README
new file mode 100644
index 00000000..8425d0c3
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/README
@@ -0,0 +1,24 @@
+
+This skin is totally (c) 1999 to Morgan aka."Splif" Thomas /
+the Aegis Corporation. Please don't rip, sell, use in public
+without the author's acceptation. Thanks. But you are
+totally free to distribute it all around the universe.
+_____________________________________________________________
+Ported by Me, 'cuz "A skin like this belongs under
+Enlightenment, not windows."
+ DNAspark99
+Contact: jedeye_one@hotmail.com
+check out: http://www3.bc.sympatico.ca/desperados
+
+Notes: I reversed the iris, so it "closes" as volume increases.
+If ya wanna try it the default way, edit the "Volume_Item" line in
+the skindata file and change it to "long" instead of "long2"
+
+ & Too bad GQmpeg doesn't have fancy playlist customization like those
+"other" mp3 players......this skin has a "kill" one of those too...
+----------------------------------------------------- --- --
+origional artist:
+Contact: Splif@Aegis-Corp.org
+Home: http://www.Aegis-Corp.org/Splif/
+
+____________________________________________________________
diff --git a/noatun/modules/kaiman/skins/k9/conf.jpg b/noatun/modules/kaiman/skins/k9/conf.jpg
new file mode 100644
index 00000000..91be54d1
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/conf.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/conf.png b/noatun/modules/kaiman/skins/k9/conf.png
new file mode 100644
index 00000000..56860e00
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/conf.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/eject.jpg b/noatun/modules/kaiman/skins/k9/eject.jpg
new file mode 100644
index 00000000..f1d00f09
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/eject.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/icon.jpg b/noatun/modules/kaiman/skins/k9/icon.jpg
new file mode 100644
index 00000000..3740549b
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/icon.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/icon.png b/noatun/modules/kaiman/skins/k9/icon.png
new file mode 100644
index 00000000..e9057671
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/icon.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/kill.jpg b/noatun/modules/kaiman/skins/k9/kill.jpg
new file mode 100644
index 00000000..8d52aa86
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/kill.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/kill.png b/noatun/modules/kaiman/skins/k9/kill.png
new file mode 100644
index 00000000..8ec851dd
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/kill.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/knine-nfont.jpg b/noatun/modules/kaiman/skins/k9/knine-nfont.jpg
new file mode 100644
index 00000000..5010b881
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/knine-nfont.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/knine-nfont.png b/noatun/modules/kaiman/skins/k9/knine-nfont.png
new file mode 100644
index 00000000..2e166ee1
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/knine-nfont.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/knine-nfont2.jpg b/noatun/modules/kaiman/skins/k9/knine-nfont2.jpg
new file mode 100644
index 00000000..7511230a
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/knine-nfont2.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/knine-nfont2.png b/noatun/modules/kaiman/skins/k9/knine-nfont2.png
new file mode 100644
index 00000000..63e45974
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/knine-nfont2.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/knine-normal2.jpg b/noatun/modules/kaiman/skins/k9/knine-normal2.jpg
new file mode 100644
index 00000000..b744ac82
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/knine-normal2.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/knine-normal2.png b/noatun/modules/kaiman/skins/k9/knine-normal2.png
new file mode 100644
index 00000000..e0d95dc8
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/knine-normal2.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/knine-vfont.jpg b/noatun/modules/kaiman/skins/k9/knine-vfont.jpg
new file mode 100644
index 00000000..a7c27bbf
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/knine-vfont.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/knine-vfont.png b/noatun/modules/kaiman/skins/k9/knine-vfont.png
new file mode 100644
index 00000000..e8692e80
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/knine-vfont.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/long2.jpg b/noatun/modules/kaiman/skins/k9/long2.jpg
new file mode 100644
index 00000000..321c121a
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/long2.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/mask.png b/noatun/modules/kaiman/skins/k9/mask.png
new file mode 100644
index 00000000..9ea57d2b
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/mask.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/newtext.jpg b/noatun/modules/kaiman/skins/k9/newtext.jpg
new file mode 100644
index 00000000..69c437f2
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/newtext.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/newtext.png b/noatun/modules/kaiman/skins/k9/newtext.png
new file mode 100644
index 00000000..e8f25356
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/newtext.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/next.jpg b/noatun/modules/kaiman/skins/k9/next.jpg
new file mode 100644
index 00000000..0a0e5267
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/next.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/pause.jpg b/noatun/modules/kaiman/skins/k9/pause.jpg
new file mode 100644
index 00000000..1144e70d
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/pause.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/play.jpg b/noatun/modules/kaiman/skins/k9/play.jpg
new file mode 100644
index 00000000..dbfe446f
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/play.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/pos_item.jpg b/noatun/modules/kaiman/skins/k9/pos_item.jpg
new file mode 100644
index 00000000..0ba2f333
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/pos_item.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/repeat.jpg b/noatun/modules/kaiman/skins/k9/repeat.jpg
new file mode 100644
index 00000000..774e0804
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/repeat.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/repeat.png b/noatun/modules/kaiman/skins/k9/repeat.png
new file mode 100644
index 00000000..84810e5b
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/repeat.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/reverse.jpg b/noatun/modules/kaiman/skins/k9/reverse.jpg
new file mode 100644
index 00000000..a9b74366
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/reverse.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/shuffle.jpg b/noatun/modules/kaiman/skins/k9/shuffle.jpg
new file mode 100644
index 00000000..1d52ac9b
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/shuffle.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/shuffle.png b/noatun/modules/kaiman/skins/k9/shuffle.png
new file mode 100644
index 00000000..a3e22250
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/shuffle.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/skindata b/noatun/modules/kaiman/skins/k9/skindata
new file mode 100644
index 00000000..1fdc4320
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/skindata
@@ -0,0 +1,73 @@
+#
+# port of K-Nine skin for K-Jofol
+# original art done by Morgan aka. "Splif" Thomas / the Aegis Corp.
+#
+# "'cuz a skin like this belongs under Enlightenment"
+#
+# DNAspark99
+#
+#
+######################################################
+
+Background: knine-normal2.jpg
+Mask: mask.png
+
+Play_Button: play.jpg FALSE FALSE 55 133 89 166
+Stop_Button: stop.jpg FALSE FALSE 28 124 52 145
+Pause_Button: pause.jpg FALSE 91 148 116 173
+Prev_Button: reverse.jpg FALSE 13 108 32 127
+Next_Button: next.jpg FALSE 122 154 141 173
+Repeat_Button: repeat.jpg FALSE TRUE 50 114 63 128
+Shuffle_Button: shuffle.jpg FALSE TRUE 102 134 118 147
+Playlist_Button: eject.jpg FALSE 78 106 104
+Mixer_Button: square.jpg FALSE 294 110 310 126
+Exit_Button: kill.jpg FALSE 282 57 295 69
+Iconify_Button: icon.jpg FALSE 292 85 304 96
+
+Config_Button: conf.jpg FALSE 222 163 242 181
+
+Alt_Skin_Button: small-k.jpg FALSE 287 138 299 153
+
+
+Minute_Number: knine-nfont2.jpg 69 64
+Second_Number: knine-nfont2.jpg 91 64
+
+In_Rate_Number: knine-vfont.jpg 123 59
+In_Hz_Number: knine-vfont.jpg 132 50
+
+Song_Number: knine-vfont.jpg 113 50
+
+Status_Item: status.jpg 113 68
+
+CPU_Number: knine-vfont.jpg 80 50
+
+Digit_Large: knine-vfont.jpg 113 40
+Digit_Small_Default: knine-vfont.jpg
+
+Title: newtext.jpg 26 19 87
+
+#RW_Button: back.jpg FALSE 35 192
+#FF_Button: ff.jpg FALSE 51 214
+
+#Preset_1_Button: list_1.jpg TRUE 70 70
+#Preset_2_Button: list_2.jpg TRUE 77 70
+#Preset_3_Button: list_3.jpg TRUE 84 70
+#Preset_4_Button: list_4.jpg TRUE 91 70
+#Preset_5_Button: list_5.jpg TRUE 98 70
+#Preset_6_Button: list_6.jpg TRUE 70 80
+#Preset_7_Button: list_7.jpg TRUE 77 80
+#Preset_8_Button: list_8.jpg TRUE 84 80
+#Preset_9_Button: list_9.jpg TRUE 91 80
+#Preset_10_Button: list_10.jpg TRUE 98 80
+
+
+Volume_Item: long2.jpg 27 199 76
+Volume_Slider: trans-slide.png FALSE FALSE FALSE 71 205 76 210
+
+Position_Item: pos_item.jpg 32 128 2 30
+Position_Slider: trans-pos.png FALSE FALSE FALSE 140 128 2 70
+
+#Balance_Item: balance.jpg FALSE FALSE FALSE 10 156 95
+#Balance_Slider: knine-pitchbtn.jpg 57 156 95
+
+# END
diff --git a/noatun/modules/kaiman/skins/k9/small-k.jpg b/noatun/modules/kaiman/skins/k9/small-k.jpg
new file mode 100644
index 00000000..219ebb05
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/small-k.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/small-k.png b/noatun/modules/kaiman/skins/k9/small-k.png
new file mode 100644
index 00000000..2a43a4a8
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/small-k.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/square.jpg b/noatun/modules/kaiman/skins/k9/square.jpg
new file mode 100644
index 00000000..c13b1644
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/square.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/square.png b/noatun/modules/kaiman/skins/k9/square.png
new file mode 100644
index 00000000..d77de4ef
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/square.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/status.jpg b/noatun/modules/kaiman/skins/k9/status.jpg
new file mode 100644
index 00000000..db6c61a1
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/status.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/status.png b/noatun/modules/kaiman/skins/k9/status.png
new file mode 100644
index 00000000..682d01b4
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/status.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/stop.jpg b/noatun/modules/kaiman/skins/k9/stop.jpg
new file mode 100644
index 00000000..0ca261e3
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/stop.jpg
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/trans-pos.png b/noatun/modules/kaiman/skins/k9/trans-pos.png
new file mode 100644
index 00000000..0eecf3bb
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/trans-pos.png
Binary files differ
diff --git a/noatun/modules/kaiman/skins/k9/trans-slide.png b/noatun/modules/kaiman/skins/k9/trans-slide.png
new file mode 100644
index 00000000..76aa00d7
--- /dev/null
+++ b/noatun/modules/kaiman/skins/k9/trans-slide.png
Binary files differ
diff --git a/noatun/modules/kaiman/style.cpp b/noatun/modules/kaiman/style.cpp
new file mode 100644
index 00000000..d42ed45f
--- /dev/null
+++ b/noatun/modules/kaiman/style.cpp
@@ -0,0 +1,1504 @@
+/*
+ Copyright (c) 2000 Stefan Schimanski (1Stein@gmx.de)
+ 1999-2000 Christian Esken (esken@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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+#include <kapplication.h>
+#include <kstandarddirs.h>
+#include <kmessagebox.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qpainter.h>
+#include <qdropsite.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kurldrag.h>
+#include <qtimer.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "style.h"
+#include "userinterface.h"
+#include <noatun/app.h>
+#include <noatun/stdaction.h>
+
+const bool KaimanStyleSlider::optionVertical = 1;
+const bool KaimanStyleSlider::optionReversed = 2;
+const bool KaimanStyleText::optionExtended = 1;
+
+KaimanStyleElement::KaimanStyleElement(QWidget *parent, const char *name)
+ : QWidget(parent, name)
+{
+ // Initialize everything to default values
+ filename = "";
+ element = "";
+ upperLeft.setX(0);
+ upperLeft.setY(0);
+ dimension.setWidth(0);
+ dimension.setHeight(0);
+ optionPrelight = optionStatuslight = false;
+ options[0] = options[1] = options[2] = false;
+ _currentPixmap = 0;
+ digits = -1;
+ pixmapLines = 1;
+ pixmapColumns = 1;
+
+ setAcceptDrops(true);
+ pixmaps.setAutoDelete(true);
+
+ setBackgroundMode( NoBackground );
+}
+
+
+KaimanStyleElement::~KaimanStyleElement()
+{
+}
+
+
+void KaimanStyleElement::loadPixmaps(QString &val_s_filename)
+{
+ QPixmap pixmap;
+
+ bool i_b_ret = pixmap.load(val_s_filename);
+ pixmapNum = pixmapLines*pixmapColumns;
+
+ pixmaps.resize(pixmapNum);
+
+ if ( i_b_ret ) {
+ if(pixmapNum) {
+ int firstWidth, firstHeight, width, height, sourcex = 0, sourcey = 0;
+
+ // first bitmap may be with different size
+ if ( dimension.width()!=0 )
+ {
+ firstWidth = dimension.width();
+ if ( pixmapColumns>1 )
+ width = (pixmap.width()-firstWidth) / (pixmapColumns-1);
+ else
+ width = 0;
+ } else
+ firstWidth = width = pixmap.width() / pixmapColumns;
+
+ if ( dimension.height()!=0 )
+ {
+ firstHeight = dimension.height();
+ if ( pixmapLines>1 )
+ height = (pixmap.height()-firstHeight) / (pixmapLines-1);
+ else
+ height = 0;
+ } else
+ firstHeight = height = pixmap.height() / pixmapLines;
+
+ // create single pixmaps
+ int i=0;
+ sourcey = 0;
+ for( int y=0; y<pixmapLines; y++ )
+ {
+ int h = (y==0) ? firstHeight : height;
+ sourcex = 0;
+
+ for( int x=0; x<pixmapColumns; x++ )
+ {
+ int w = (x==0) ? firstWidth : width;
+
+ QPixmap *part = new QPixmap(w,h,pixmap.depth());
+ part->fill(Qt::black);
+ bitBlt(part,0,0,&pixmap,sourcex,sourcey,w,h);
+ pixmaps.insert(i,part);
+
+ if(pixmap.mask())
+ {
+ QBitmap maskpart(w,h);
+ bitBlt(&maskpart,0,0,pixmap.mask(),sourcex,sourcey,w,h);
+ part->setMask(maskpart);
+ }
+
+ i++;
+ sourcex += w;
+ }
+
+ sourcey += h;
+ }
+ }
+ } else {
+ kdDebug() << "Cannot load pixmap " << val_s_filename << endl;
+
+ for ( int i=0; i<pixmapNum; i++ )
+ {
+ QPixmap *pm = new QPixmap(10, 10);
+ pm->fill(Qt::black);
+ pixmaps.insert( i, pm );
+ }
+ }
+
+ if ( dimension.width()==0 ) dimension.setWidth( pixmaps[0]->width() );
+ if ( dimension.height()==0 ) dimension.setHeight( pixmaps[0]->height() );
+ setGeometry( QRect(upperLeft, dimension) );
+}
+
+void KaimanStyleElement::setPixmap( int num )
+{
+ if ( num!=_currentPixmap )
+ {
+ if ( num>pixmapNum-1 ) num = pixmapNum-1;
+ if ( num<0 ) num = 0;
+
+ _currentPixmap = num;
+ repaint( FALSE );
+ }
+}
+
+void KaimanStyleElement::paintEvent ( QPaintEvent */*qpe*/ )
+{
+ QPixmap *pm = pixmaps[_currentPixmap];
+ if ( pm )
+ bitBlt(this, 0, 0, pm );
+ else
+ kdDebug() << "Invalid pixmap" << endl;
+
+/* QPainter p( this );
+ p.setBrush( NoBrush );
+ p.setPen( QColor(255,255,255) );
+ p.drawRect( 0, 0, width(), height() );
+ p.drawText( 2, 16, name() ); */
+}
+
+void KaimanStyleElement::dragEnterEvent( QDragEnterEvent *event )
+{
+ event->accept( KURLDrag::canDecode(event) );
+}
+
+void KaimanStyleElement::dropEvent( QDropEvent *event )
+{
+ ((Kaiman*)(parentWidget()->parentWidget()))->doDropEvent(event);
+}
+
+/***************************************************************************/
+
+
+KaimanStyleButton::KaimanStyleButton(QWidget *parent, const char *name)
+ : KaimanStyleMasked(parent, name)
+{
+ i_b_lit = i_b_prelit = i_b_down = false;
+ i_i_currentState = NormalUp;
+
+ I_pmIndex.resize( StateListEND );
+
+ for (int i=0; i<StateListEND; i++) {
+ // Set pixmap index of all states to 0 (the default pixmap)
+ I_pmIndex.insert( i, new int(0));
+ }
+}
+
+KaimanStyleButton::~KaimanStyleButton()
+{
+}
+
+void KaimanStyleButton::mousePressEvent(QMouseEvent *qme)
+{
+ // We deactivate prelight, because the user presses the button.
+ // So it is now down, but there is no PrelitDown icon (BTW: would
+ // make no real sense anyhow).
+ setPrelight(false);
+ setDown(true);
+ grabMouse();
+
+ KaimanStyleMasked::mousePressEvent( qme );
+}
+
+void KaimanStyleButton::mouseReleaseEvent(QMouseEvent *qme)
+{
+ releaseMouse ();
+
+ if (down())
+ {
+ setDown(false);
+ emit clicked();
+ }
+
+ KaimanStyleMasked::mouseReleaseEvent( qme );
+}
+
+
+/* paint prelight */
+void KaimanStyleButton::enterEvent ( QEvent * e )
+{
+ if ( !down() )
+ setPrelight(true);
+
+ KaimanStyleMasked::enterEvent( e );
+}
+
+/* unpaint prelight */
+void KaimanStyleButton::leaveEvent ( QEvent * e )
+{
+ if (!down())
+ setPrelight(false);
+
+ KaimanStyleMasked::leaveEvent( e );
+}
+
+bool KaimanStyleButton::lit()
+{
+ return i_b_lit;
+}
+
+void KaimanStyleButton::setLit(bool val_b_lit)
+{
+ i_b_lit = val_b_lit;
+ updateButtonState();
+}
+
+bool KaimanStyleButton::prelit()
+{
+ return i_b_prelit;
+}
+
+void KaimanStyleButton::setPrelight(bool val_b_prelit)
+{
+ i_b_prelit = val_b_prelit;
+ updateButtonState();
+}
+
+bool KaimanStyleButton::down()
+{
+ return i_b_down;
+}
+
+void KaimanStyleButton::setDown(bool val_b_down)
+{
+ i_b_down = val_b_down;
+ updateButtonState();
+}
+
+void KaimanStyleButton::updateButtonState() {
+
+ if ( i_b_prelit ) {
+ if ( i_b_lit ) {
+ // Prelit and Lit
+ i_i_currentState = PrelightLitUp;
+ }
+ else {
+ // Prelit and not Lit
+ i_i_currentState = PrelightUp;
+ }
+ }
+
+ else if ( i_b_lit ) {
+ if ( i_b_down ) {
+ // Lit and Down
+ i_i_currentState = LitDown;
+ } else {
+ // Lit and not Down
+ i_i_currentState = LitUp;
+ }
+ }
+ else {
+ if ( i_b_down ) {
+ // Normal and Down
+ i_i_currentState = NormalDown;
+ }
+ else {
+ // Normal and not Down
+ i_i_currentState = NormalUp;
+ }
+ }
+
+ setPixmap( *I_pmIndex[i_i_currentState] );
+ repaint();
+}
+
+
+/***********************************************************************/
+
+KaimanStyleSlider::KaimanStyleSlider(int min, int max, QWidget *parent, const char *name)
+ : KaimanStyleMasked( parent, name )
+{
+ _min = min;
+ _max = max;
+ _down = false;
+ _lit = false;
+
+ setValue( _min );
+}
+
+
+KaimanStyleSlider::~KaimanStyleSlider()
+{
+}
+
+
+void KaimanStyleSlider::setValue( int value )
+{
+ if (value>_max) value=_max;
+ if (value<_min) value=_min;
+ _value = value;
+ repaint();
+}
+
+
+void KaimanStyleSlider::setValue( int value, int min, int max )
+{
+ if ( value!=_value || min!=_min || max!=_max ) {
+ _min = min;
+ _max = max;
+ setValue( value );
+ repaint();
+ }
+}
+
+int KaimanStyleSlider::pos2value( int x, int y )
+{
+ int p;
+ int v;
+ if ( options[optionVertical] ) {
+ p = y;
+ v = p*(_max-_min)/height();
+ } else {
+ p = x;
+ v = p*(_max-_min)/width();
+ }
+
+ if ( options[optionReversed] ) v = (_max-_min) - v;
+ return _min + v;
+}
+
+
+void KaimanStyleSlider::mouseMoveEvent(QMouseEvent *qme)
+{
+ KaimanStyleMasked::mouseMoveEvent( qme );
+
+ if ( _down )
+ {
+ setValue( pos2value(qme->x(), qme->y()) );
+ emit newValue( value() );
+ }
+}
+
+void KaimanStyleSlider::mousePressEvent(QMouseEvent *qme)
+{
+ if ( !_down )
+ {
+ grabMouse();
+ _down = true;
+
+ setValue( pos2value(qme->x(), qme->y()) );
+ emit newValueDrag( value() );
+ emit newValue( value() );
+ }
+
+ KaimanStyleMasked::mousePressEvent( qme );
+}
+
+
+void KaimanStyleSlider::mouseReleaseEvent(QMouseEvent *qme)
+{
+ if ( _down )
+ {
+ _down = false;
+ releaseMouse();
+ repaint();
+
+ setValue( pos2value(qme->x(), qme->y()) );
+ emit newValue( value() );
+ emit newValueDrop( value() );
+ }
+
+ KaimanStyleMasked::mouseReleaseEvent( qme );
+}
+
+
+void KaimanStyleSlider::paintEvent(QPaintEvent */*qpe*/)
+{
+ // draw background
+ bitBlt( this, 0, 0, pixmaps[0] );
+
+ // draw optional handle
+ QPixmap *handle;
+ if ( _down )
+ handle = pixmaps[2];
+ else
+ {
+ if ( _lit && optionPrelight )
+ handle = pixmaps[3];
+ else
+ handle = pixmaps[1];
+ }
+
+ if ( handle && handle->width() )
+ {
+ int x = 0;
+ int y = 0;
+
+ if ( _max-_min ) {
+ int v = _value-_min;
+ if ( options[optionReversed] ) v = (_max-_min) - v;
+
+ if ( options[optionVertical] )
+ y = ( height()-handle->height() ) * v / (_max-_min);
+ else
+ x = ( width()-handle->width() ) * v / (_max-_min);
+ }
+
+ bitBlt( this, x, y, handle );
+ }
+}
+
+void KaimanStyleSlider::enterEvent ( QEvent * e )
+{
+ if ( !_lit && optionPrelight )
+ {
+ _lit = true;
+ repaint();
+ }
+
+ KaimanStyleMasked::enterEvent( e );
+}
+
+void KaimanStyleSlider::leaveEvent ( QEvent * e )
+{
+ if ( _lit )
+ {
+ _lit = false;
+ repaint();
+ }
+
+ KaimanStyleMasked::leaveEvent( e );
+}
+
+
+/***********************************************************************/
+
+
+KaimanStyleBackground::KaimanStyleBackground(QWidget *parent, const char *name)
+ : KaimanStyleMasked( parent, name )
+{
+ i_b_move = false;
+}
+
+KaimanStyleBackground::~KaimanStyleBackground()
+{
+}
+
+void KaimanStyleBackground::mouseReleaseEvent(QMouseEvent *qme)
+{
+ i_b_move = false;
+ KaimanStyleMasked::mouseReleaseEvent( qme );
+}
+
+void KaimanStyleBackground::mouseMoveEvent(QMouseEvent *qme)
+{
+ QPoint diff = qme->globalPos() - i_point_lastPos;
+ if ( abs(diff.x()) > 10 || abs(diff.y()) > 10) {
+ // Moving starts only, when passing a drag border
+ i_b_move = true;
+ }
+
+ if ( i_b_move ) {
+ QWidget *p = parentWidget()->parentWidget();
+ if ( !p ) p = parentWidget();
+
+ p->move( qme->globalPos() - i_point_dragStart );
+ }
+
+ KaimanStyleMasked::mouseMoveEvent( qme );
+}
+
+void KaimanStyleBackground::mousePressEvent(QMouseEvent *qme)
+{
+ // On the background we move the shaped toplevel around
+ if (!i_b_move) {
+ i_point_dragStart = qme->pos();
+ i_point_lastPos = qme->globalPos();
+ }
+
+ KaimanStyleMasked::mousePressEvent( qme );
+}
+
+/***********************************************************************/
+
+KaimanStyleValue::KaimanStyleValue(int min, int max, QWidget *parent, const char *name)
+ : KaimanStyleMasked( parent, name )
+{
+ _min = min;
+ _max = max;
+ _value = _min;
+
+ setPixmap( 0 );
+}
+
+KaimanStyleValue::~KaimanStyleValue()
+{
+}
+
+void KaimanStyleValue::setValue( int value )
+{
+ if (value>_max) value=_max;
+ if (value<_min) value=_min;
+ _value = value;
+
+ int len = _max-_min;
+ if ( len )
+ setPixmap( (_value-_min)*pixmapNum/len );
+ else
+ setPixmap( 0 );
+}
+
+void KaimanStyleValue::setValue( int value, int min, int max )
+{
+ _min = min;
+ _max = max;
+
+ setValue( value );
+}
+
+
+/***********************************************************************/
+
+KaimanStyleNumber::KaimanStyleNumber(QWidget *parent, const char *name)
+ : KaimanStyleElement( parent, name )
+{
+ //kdDebug(66666) << k_funcinfo << "name = '" << name << "'" << endl;
+ _value = 0;
+ if (QCString(name) == "In_Rate_Number")
+ digits = 3;
+ else
+ digits = 2;
+}
+
+KaimanStyleNumber::~KaimanStyleNumber()
+{
+}
+
+
+void KaimanStyleNumber::loadPixmaps(QString &val_s_filename)
+{
+ KaimanStyleElement::loadPixmaps( val_s_filename );
+ resize( digits*pixmaps[0]->width(),pixmaps[0]->height() );
+}
+
+
+void KaimanStyleNumber::setValue( int value )
+{
+ if ( _value!=value )
+ {
+ _value = value;
+ repaint();
+ }
+}
+
+void KaimanStyleNumber::paintEvent(QPaintEvent */*qpe*/)
+{
+ // check for overflow
+ int v = _value;
+ for ( int i=digits; i>0 && v>0; i-- )
+ v /= 10;
+
+ if ( v!=0 )
+ v = 999999999; // overflow
+ else
+ v = _value;
+
+ // draw number
+ int x = width();
+ do {
+ x -= pixmaps[0]->width();
+ bitBlt(this, x, 0, pixmaps[v%10] );
+
+ v /= 10;
+ } while ( v>0 );
+
+ // draw right free space
+ while ( x>0 )
+ {
+ x -= pixmaps[0]->width();
+ bitBlt(this, x, 0, pixmaps[0] );
+ }
+/*
+ QPainter p( this );
+ p.setBrush( NoBrush );
+ p.setPen( QColor(255,255,255) );
+ p.drawRect( 0, 0, width(), height() );
+ p.drawText( 2, 16, name() );*/
+}
+
+/***********************************************************************/
+
+KaimanStyleText::KaimanStyleText(QWidget *parent, const char *name)
+ : KaimanStyleElement( parent, name )
+{
+ _pos = 0;
+ _timer = new QTimer( this );
+ _delay = 500;
+ connect( _timer, SIGNAL(timeout()), this, SLOT(timeout()) );
+}
+
+KaimanStyleText::~KaimanStyleText()
+{
+}
+
+
+void KaimanStyleText::loadPixmaps(QString &val_s_filename)
+{
+ KaimanStyleElement::loadPixmaps( val_s_filename );
+ resize( digits*pixmaps[0]->width(), pixmaps[0]->height() );
+}
+
+
+void KaimanStyleText::setValue( QString value )
+{
+ if ( value!=_value ) {
+ _pos = 0;
+ _direction = 1;
+ _value = value;
+ repaint();
+ }
+}
+
+
+void KaimanStyleText::startAnimation( int delay )
+{
+ _pos = 0;
+ _direction = 1;
+ _delay = delay;
+ _timer->start( _delay, TRUE );
+}
+
+
+void KaimanStyleText::stopAnimation()
+{
+ _pos = 0;
+ _timer->stop();
+}
+
+
+void KaimanStyleText::timeout()
+{
+ // reflect
+ if ( _pos+_direction<0 || (int)_value.length()-(_pos+_direction)<digits ) {
+ _direction = -_direction;
+ _timer->start( _delay*5, TRUE );
+ } else {
+ // check new position
+ if ( _pos+_direction>=0 && (int)_value.length()-(_pos+_direction)>=digits ) {
+ _pos += _direction;
+ repaint();
+ }
+
+ _timer->start( _delay, TRUE );
+ }
+
+
+}
+
+
+void KaimanStyleText::paintEvent(QPaintEvent */*qpe*/)
+{
+ // draw number
+ int p;
+ for (p=0; p<digits && p<(int)_value.length()-_pos; p++ )
+ {
+ int pmNum = _value[p+_pos].latin1() - ' ' ;
+ if ( pmNum>=96 ) pmNum = '?' - ' ';
+ if ( pmNum<0 ) pmNum = '?' - ' ';
+
+ QPixmap *pm = pixmaps[pmNum];
+ if ( pm ) bitBlt(this, p*pixmaps[0]->width(), 0, pm );
+ }
+
+ QPixmap *pm = pixmaps[0];
+ for ( ; p<digits; p++ )
+ bitBlt(this, p*pixmaps[0]->width(), 0, pm );
+
+/* QPainter pnt( this );
+ pnt.setBrush( NoBrush );
+ pnt.setPen( QColor(255,255,255) );
+ pnt.drawRect( 0, 0, width(), height() );
+ pnt.drawText( 2, 16, name() );*/
+}
+
+/***********************************************************************/
+
+KaimanStyleAnimation::KaimanStyleAnimation(int delay, QWidget *parent, const char *name)
+ : KaimanStyleMasked( parent, name )
+{
+ _delay = delay;
+ _frame = 0;
+ _timer = new QTimer( this );
+ connect( _timer, SIGNAL(timeout()), this, SLOT(timeout()) );
+}
+
+KaimanStyleAnimation::~KaimanStyleAnimation()
+{
+}
+
+void KaimanStyleAnimation::start()
+{
+ _timer->start( _delay, FALSE );
+}
+
+void KaimanStyleAnimation::pause()
+{
+ _timer->stop();
+}
+
+void KaimanStyleAnimation::stop()
+{
+ _timer->stop();
+ _frame = 0;
+ setPixmap( _frame );
+}
+
+void KaimanStyleAnimation::timeout()
+{
+ _frame++;
+ if ( _frame>=pixmapNum ) _frame = 1;
+ setPixmap( _frame );
+}
+
+/***********************************************************************/
+
+KaimanStyleState::KaimanStyleState(QWidget *parent, const char *name)
+ : KaimanStyleMasked( parent, name )
+{
+ _value = 0;
+}
+
+KaimanStyleState::~KaimanStyleState()
+{
+}
+
+void KaimanStyleState::setValue( int value )
+{
+ _value = value;
+ setPixmap( _value );
+}
+
+void KaimanStyleState::mousePressEvent(QMouseEvent *qme)
+{
+ emit clicked();
+ KaimanStyleMasked::mouseReleaseEvent( qme );
+}
+
+/***********************************************************************/
+
+
+KaimanStyle::KaimanStyle( QWidget *parent, const char *name )
+ : QWidget( parent, name )
+{
+ i_qw_parent = parent;
+ i_eventSemaphore = false;
+}
+
+
+KaimanStyle::~KaimanStyle()
+{
+}
+
+
+int KaimanStyle::parseStyleFile(QString &l_s_tmpName)
+{
+ int l_i_ret = false;
+
+ QStringList l_s_tokens;
+
+ QFile l_fd(l_s_tmpName);
+ if ( l_fd.open(IO_ReadOnly) ) {
+ // file opened successfully
+ QTextStream l_ts_line( &l_fd );
+ QString l_s_textLine, l_s_token;
+ while ( !l_ts_line.eof() ) {
+ // Clear list of tokens (we are going to fill them now)
+ l_s_tokens.clear();
+
+ // Read a line
+ l_s_textLine = l_ts_line.readLine();
+ l_s_textLine = l_s_textLine.simplifyWhiteSpace();
+
+ if ( l_s_textLine.left(1) != "#" ) {
+ // OK, this is not a comment line
+ if ( l_s_textLine.isNull())
+ l_s_textLine = "";
+ while ( !l_s_textLine.isEmpty() ) {
+ l_s_token = getToken(l_s_textLine, ' ');
+ if ( ! l_s_token.isEmpty() ) {
+ // OK. There is a useful token. It is not NULL
+ if ( l_s_token.right(1) == ":" )
+ l_s_tokens.append(l_s_token.left(l_s_token.length() -1 ));
+ else
+ l_s_tokens.append(l_s_token);
+ } // -<- if it is a not-empty token
+ } // -<- while there are tokens available
+
+ interpretTokens(l_s_tokens);
+
+ } // -<- if is not comment line
+ } // -<- While not EOF on file
+
+ l_i_ret = 0;
+ } // -<- if file could be opened
+
+ else {
+ l_i_ret = KaimanStyle::FileNotFound;
+ }
+ return l_i_ret;
+}
+
+
+
+/*
+ This function gets a list of tokens and inserts a new
+ KaimanStyleElement in I_styleElem. */
+void KaimanStyle::interpretTokens(QStringList& ref_s_tokens)
+{
+ if ( ref_s_tokens.count() < 1 ) {
+ // A list with less than 1 item is useless to us
+ return;
+ }
+
+ QString l_s_tokenTypes;
+ const QString &l_s_elem = ref_s_tokens.first();
+ bool l_vertPixmaps = false;
+ int l_i_pmIndex[KaimanStyleButton::StateListEND];
+ for (int i=0; i<KaimanStyleButton::StateListEND; i++) l_i_pmIndex[i]=0;
+
+ enum { UnknownElement, BackgroundElement, MaskElement, ButtonElement, SliderElement,
+ ValueElement, AnimationElement, StateElement, DigitElement, NumberElement,
+ TextElement } l_elementType = UnknownElement;
+
+ // Now determine the meaning of the following tokens
+ // l_s_tokenTypes stores the meaning (e.g. x-Position, filename, ...)
+ if ( l_s_elem == "Background" ) {
+ l_s_tokenTypes = "f";
+ l_elementType = BackgroundElement;
+ }
+ // ---
+ else if ( l_s_elem == "Mask" ) {
+ l_s_tokenTypes = "f";
+ l_elementType = MaskElement;
+ } else if ( l_s_elem=="Digit_Small" || l_s_elem=="Digit_Small_Default" ) {
+ i_smallFont = ref_s_tokens[1];
+ return;
+ } else if ( l_s_elem=="Digit_Large" || l_s_elem=="Digit_Large_Default" ) {
+ i_largeFont = ref_s_tokens[1];
+ return;
+ }
+ // ---
+ else if ( l_s_elem == "Title" ||
+ l_s_elem == "Album" ||
+ l_s_elem == "Artist" ||
+ l_s_elem == "Genre" ) {
+ // You can have an OPTIONAL argument, so lets see if it is there.
+ if (ref_s_tokens.count() == 6 )
+ l_s_tokenTypes = "fd1xy";
+ else
+ l_s_tokenTypes = "fdxy";
+ l_elementType = TextElement;
+ }
+ // ---
+ else if ( l_s_elem == "Play_Button" ||
+ l_s_elem == "Pause_Button" ||
+ l_s_elem == "Stop_Button" ||
+ l_s_elem == "Shuffle_Button" ||
+ l_s_elem == "Repeat_Button" ) {
+ l_s_tokenTypes = "fPSxy";
+ l_i_pmIndex[KaimanStyleButton::NormalUp] = 0;
+ l_i_pmIndex[KaimanStyleButton::NormalDown] = 1;
+ l_i_pmIndex[KaimanStyleButton::LitUp] = 2;
+ l_i_pmIndex[KaimanStyleButton::LitDown] = 3;
+ l_i_pmIndex[KaimanStyleButton::PrelightUp] = 4;
+ l_i_pmIndex[KaimanStyleButton::PrelightLitUp] = 5;
+ l_elementType = ButtonElement;
+ }
+ // ---
+ else if ( l_s_elem == "Next_Button" ||
+ l_s_elem == "Prev_Button" ||
+ l_s_elem == "FF" ||
+ l_s_elem == "RW" ||
+ l_s_elem == "Playlist_Button" ||
+ l_s_elem == "Config_Button" ||
+ l_s_elem == "Iconify_Button" ||
+ l_s_elem == "Mixer_Button" ||
+ l_s_elem == "Exit_Button" ||
+ l_s_elem == "Alt_Skin_Button" ||
+ l_s_elem == "Volume_Up_Button" ||
+ l_s_elem == "Volume_Down_Button" ||
+ l_s_elem == "Balance_Left_Button" ||
+ l_s_elem == "Balance_Right_Button" ) {
+ l_i_pmIndex[KaimanStyleButton::NormalUp] = 0;
+ l_i_pmIndex[KaimanStyleButton::NormalDown] = 1;
+ l_i_pmIndex[KaimanStyleButton::PrelightUp] = 2;
+ l_s_tokenTypes = "fPxy";
+ l_elementType = ButtonElement;
+ }
+ // ---
+ else if ( l_s_elem == "Stereo_Item" ||
+ l_s_elem == "Shuffle_Item" ||
+ l_s_elem == "Repeat_Item" ||
+ l_s_elem == "Mpegversion_Item" ||
+ l_s_elem == "Mpeglayer_Item" ||
+ l_s_elem == "Mpegmode_Item" ||
+ l_s_elem == "Status_Item" ) {
+ l_s_tokenTypes = "Vfxy";
+ l_elementType = StateElement;
+ }
+ // ---
+ else if ( l_s_elem == "Hour_Number" ||
+ l_s_elem == "Minute_Number" ||
+ l_s_elem == "Second_Number" ||
+ l_s_elem == "Song_Number" ||
+ l_s_elem == "Total_Number" ||
+ l_s_elem == "In_Rate_Number" ||
+ l_s_elem == "In_Hz_Number" ||
+ l_s_elem == "Out_Bits" ||
+ l_s_elem == "Out_Hz" ||
+ l_s_elem == "Song_Minute" ||
+ l_s_elem == "Song_Second" ||
+ l_s_elem == "Frame" ||
+ l_s_elem == "Frame_Total" ||
+ l_s_elem == "CPU_Number" ||
+ l_s_elem == "Hour_Total" ||
+ l_s_elem == "Minute_Total" ||
+ l_s_elem == "Second_Total_Number" ) {
+ // You can have an OPTIONAL argument, so lets see if it is there.
+ if (ref_s_tokens.count() == 6 )
+ l_s_tokenTypes = "fd1xy";
+ else if (ref_s_tokens.count() == 5 )
+ l_s_tokenTypes = "fdxy";
+ else
+ l_s_tokenTypes = "fxy";
+
+ l_elementType = NumberElement;
+ }
+ // ---
+ else if ( l_s_elem == "Position_Item" ||
+ l_s_elem == "Volume_Item" ||
+ l_s_elem == "Balance_Item" ) {
+ l_s_tokenTypes = "Vflxy";
+ l_elementType = ValueElement;
+ }
+ // ---
+ else if ( l_s_elem == "Load_Item" ) {
+ l_s_tokenTypes = "Vflxy";
+ l_elementType = AnimationElement;
+ }
+ // ---
+ else if ( l_s_elem == "Position_Slider" ||
+ l_s_elem == "Volume_Slider" ||
+ l_s_elem == "Balance_Slider" ) {
+
+ if ( ref_s_tokens.count()==10 )
+ l_s_tokenTypes = "VfP12wxyh";
+ else
+ l_s_tokenTypes = "VfP12sxy";
+ l_elementType = SliderElement;
+ }
+ else {
+ kdDebug() << l_s_elem << " not handled yet." << endl;
+ l_s_tokenTypes = "f";
+ }
+
+ /* The above lines decode the meanings of the tokens. The rules for
+ this are the SKIN-SPECS. So the decoder implements a syntactic
+ analyser (parser).
+
+ I now do know the type of each tokens, whether it represents a
+ filename, the prelight parameter, the x position or what else. This
+ information resides in l_s_tokenTypes, and will help in creating
+ the kaiman style elements.
+
+ I will now do two things:
+ 1) Create a new KaimanStyle*, that is inserted into I_styleElem.
+ 2) Fill the KaimanStyleElement structure, by interpreting the tokens.
+ */
+ QStringList::Iterator li_s_tokens = ref_s_tokens.begin();
+ ++ li_s_tokens; // Skip the name of the element
+
+ // 1) Create a new KaimanStyleElement, that is inserted into I_styleElem.
+ KaimanStyleElement *l_kse_elem = 0;
+ KaimanStyleButton *but = 0;
+
+ switch ( l_elementType )
+ {
+ case BackgroundElement:
+ l_kse_elem = new KaimanStyleBackground(this, l_s_elem.latin1());
+ l_kse_elem->show();
+ break;
+
+ case MaskElement:
+ l_kse_elem = new KaimanStyleElement(this, l_s_elem.latin1());
+ l_kse_elem->hide();
+ break;
+
+ case ButtonElement:
+ l_kse_elem = new KaimanStyleButton(this, l_s_elem.latin1());
+ but = static_cast<KaimanStyleButton*>(l_kse_elem);
+ l_kse_elem->show();
+ break;
+
+ case SliderElement:
+ l_kse_elem = new KaimanStyleSlider(0, 100, this, l_s_elem.latin1());
+ i_sliders.append(l_kse_elem);
+ l_kse_elem->show();
+ break;
+
+ case ValueElement:
+ l_kse_elem = new KaimanStyleValue(0, 100, this, l_s_elem.latin1());
+ break;
+
+ case AnimationElement:
+ l_kse_elem = new KaimanStyleAnimation(30, this, l_s_elem.latin1());
+ break;
+
+ case StateElement:
+ l_kse_elem = new KaimanStyleState(this, l_s_elem.latin1());
+ break;
+
+ case NumberElement:
+ l_kse_elem = new KaimanStyleNumber(this, l_s_elem.latin1());
+ break;
+
+ case TextElement:
+ l_kse_elem = new KaimanStyleText(this, l_s_elem.latin1());
+ break;
+
+ default:
+ break;
+ }
+
+ if ( !l_kse_elem )
+ {
+ kdDebug() << "Ignoring style element " << l_s_elem << endl;
+ return;
+ }
+
+ // insert element into element list
+ uint l_i_size = I_styleElem.size();
+ I_styleElem.resize(l_i_size + 1);
+ I_styleElem.insert(l_i_size, l_kse_elem);
+ l_kse_elem->installEventFilter( this );
+
+ // initialize element parameters
+ l_kse_elem->element = l_s_elem;
+
+ if ( l_s_tokenTypes.left(1) == "V" ) {
+ // Vertical flag
+ l_vertPixmaps = true;
+ l_s_tokenTypes = l_s_tokenTypes.mid(1);
+ }
+
+ // initialize button parameters
+ if ( but )
+ {
+ for (int i=0; i<KaimanStyleButton::StateListEND; i++)
+ but->I_pmIndex.insert(i, new int(l_i_pmIndex[i]) );
+ }
+
+ // 2) Fill the KaimanStyleElement structure, by interpreting the tokens.
+ while ( l_s_tokenTypes.length() != 0 ) {
+ /* The skindata format allows omitting arguments if the parser
+ can reconstruct without problems what you mean. This is taken
+ into account when writing the l_s_tokenTypes sting.
+
+ Unfortunately, several skins do ship with a broken skindata file.
+ Most common problem is that width and height is also given.
+
+ Even worse examples leave out specified parametes and add others.
+ For instance, the pause line is specified as "fPSxy". But in k9
+
+
+ the line looks like
+
+ Pause_Button: pause.jpg FALSE 91 148 116 173
+
+ So the actual parameters are fPxywh. The parser has to be pretty
+ smart now. It should "see" that S (status light) is not present,
+ since that should be either TRUE or FALSE, and distribute the
+ others accordingly. */
+
+ bool skipOne;
+
+ do {
+ skipOne = false;
+
+ // Take the first item from the l_s_tokenTypes;
+ char l_c_type = (l_s_tokenTypes[0]).latin1();
+ l_s_tokenTypes = l_s_tokenTypes.mid(1);
+ QString l_s_token = *li_s_tokens;
+ switch(l_c_type) {
+ case 'f':
+ // filename
+ l_kse_elem->filename = l_s_token;
+ break;
+ case 'P':
+ // Prelight
+ if ( l_s_token.upper() == "TRUE" )
+ l_kse_elem->optionPrelight = true;
+ else
+ {
+ l_kse_elem->optionPrelight = false;
+
+ // was that token really there?
+ skipOne = ( l_s_token.upper() != "FALSE" );
+ }
+ break;
+ case 'S':
+ // Statuslight
+ if ( l_s_token.upper() == "TRUE" )
+ l_kse_elem->optionStatuslight = true;
+ else
+ {
+ l_kse_elem->optionStatuslight = false;
+
+ // was that token really there?
+ skipOne = ( l_s_token.upper() != "FALSE" );
+ }
+ break;
+ case '1':
+ // parameter 1
+ if ( l_s_token.upper() == "TRUE" )
+ l_kse_elem->options[0] = true;
+ else
+ {
+ l_kse_elem->options[0] = false;
+
+ // was that token really there?
+ skipOne = ( l_s_token.upper() != "FALSE" );
+ }
+ break;
+ case '2':
+ // parameter 2
+ if ( l_s_token.upper() == "TRUE" )
+ l_kse_elem->options[1] = true;
+ else
+ {
+ l_kse_elem->options[1] = false;
+
+ // was that token really there?
+ skipOne = ( l_s_token.upper() != "FALSE" );
+ }
+ break;
+ case '3':
+ // parameter 3
+ if ( l_s_token.upper() == "TRUE" )
+ l_kse_elem->options[2] = true;
+ else
+ {
+ l_kse_elem->options[2] = false;
+
+ // was that token really there?
+ skipOne = ( l_s_token.upper() != "FALSE" );
+ }
+ break;
+ case 'l':
+ // length
+ if ( l_vertPixmaps )
+ l_kse_elem->pixmapLines = l_s_token.toInt();
+ else
+ l_kse_elem->pixmapColumns = l_s_token.toInt();
+ break;
+ case 'x':
+ // x Position
+ l_kse_elem->upperLeft.setX(l_s_token.toInt());
+ break;
+ case 'y':
+ // y Position
+ l_kse_elem->upperLeft.setY(l_s_token.toInt());
+ break;
+ case 's':
+ // dimension
+ if ( l_kse_elem->options[KaimanStyleSlider::optionVertical] )
+ l_kse_elem->dimension.setHeight(l_s_token.toInt());
+ else
+ l_kse_elem->dimension.setWidth(l_s_token.toInt());
+ break;
+ case 'w':
+ // width
+ l_kse_elem->dimension.setWidth(l_s_token.toInt());
+ break;
+ case 'h':
+ // height
+ l_kse_elem->dimension.setHeight(l_s_token.toInt());
+ break;
+ case 'd':
+ // number of digits
+ l_kse_elem->digits = l_s_token.toInt();
+ break;
+
+ default:
+ kdDebug() << "Element type '" << l_c_type << "' unknown" << endl;
+ }
+
+ if(skipOne) {
+ kdDebug() << "Skipped one element '" << l_c_type << "'" << endl;
+ }
+ } while(skipOne && l_s_tokenTypes.length() != 0);
+
+ // Next token.
+ ++li_s_tokens;
+ if (li_s_tokens == ref_s_tokens.end() ) {
+ // End of token list
+ break;
+ }
+ }
+
+ /* Do some post-processing */
+
+ if( l_elementType==ButtonElement ) {
+ // <Normal button>
+ if(but->optionPrelight) {
+ // --- Has Prelight ---
+ if(but->optionStatuslight)
+ but->pixmapColumns = 6;
+ else {
+ but->pixmapColumns = 3;
+ but->I_pmIndex.insert( KaimanStyleButton::LitUp,
+ new int( *(but->I_pmIndex[KaimanStyleButton::NormalUp])));
+ but->I_pmIndex.insert( KaimanStyleButton::LitDown,
+ new int( *(but->I_pmIndex[KaimanStyleButton::NormalDown])));
+ }
+ } else {
+ // --- Has No Prelight ---
+ but->I_pmIndex.insert( KaimanStyleButton::PrelightUp,
+ new int( *(but->I_pmIndex[KaimanStyleButton::NormalUp])));
+ but->I_pmIndex.insert( KaimanStyleButton::PrelightLitUp,
+ new int( *(but->I_pmIndex[KaimanStyleButton::LitUp])));
+ if(l_kse_elem->optionStatuslight)
+ but->pixmapColumns = 4;
+ else {
+ but->pixmapColumns = 2;
+ but->I_pmIndex.insert( KaimanStyleButton::LitUp,
+ new int( *(but->I_pmIndex[KaimanStyleButton::NormalUp])));
+ but->I_pmIndex.insert( KaimanStyleButton::LitDown,
+ new int( *(but->I_pmIndex[KaimanStyleButton::NormalDown])));
+ but->I_pmIndex.insert( KaimanStyleButton::PrelightLitUp,
+ new int( *(but->I_pmIndex[KaimanStyleButton::NormalUp])));
+ }
+ }
+ } else if( l_elementType==NumberElement ) {
+ // number items
+ l_kse_elem->pixmapColumns = 11;
+ if ( l_kse_elem->filename=="Small" ) l_kse_elem->filename = i_smallFont;
+ else if ( l_kse_elem->filename=="Large" ) l_kse_elem->filename = i_largeFont;
+ } else if( l_elementType==SliderElement ) {
+ // slider items
+ if ( l_kse_elem->options[KaimanStyleSlider::optionVertical] )
+ l_kse_elem->pixmapLines = l_kse_elem->optionPrelight ? 4 : 3;
+ else
+ l_kse_elem->pixmapColumns = l_kse_elem->optionPrelight ? 4 : 3;
+ } else if( l_elementType==TextElement ) {
+ // text items
+ l_kse_elem->pixmapColumns = 32;
+ l_kse_elem->pixmapLines = l_kse_elem->options[KaimanStyleText::optionExtended] ? 6 : 3;
+ } else {
+ // <Not standard element>
+ if(l_s_elem == "Stereo_Item")
+ l_kse_elem->pixmapLines = 3;
+ else if(l_s_elem == "Shuffle_Item")
+ l_kse_elem->pixmapLines = 2;
+ else if(l_s_elem == "Repeat_Item")
+ l_kse_elem->pixmapLines = 2;
+ else if(l_s_elem == "Mpegversion_Item")
+ l_kse_elem->pixmapLines = 3;
+ else if(l_s_elem == "Mpegversion_Item")
+ l_kse_elem->pixmapLines = 4;
+ else if(l_s_elem == "Mpegmode_Item")
+ l_kse_elem->pixmapLines = 5;
+ else if(l_s_elem == "Status_Item")
+ l_kse_elem->pixmapLines = 3;
+ } // </Not normal button>
+}
+
+
+QString KaimanStyle::getToken(QString &val_s_string, char val_c_separator)
+{
+ int l_i_pos;
+ QString l_s_token;
+
+ // Find the first occurrence of the separator
+ l_i_pos = val_s_string.find(val_c_separator, 0, false);
+ if ( l_i_pos == -1 ) {
+ // No sparator! Then the whole string is the token
+ l_s_token = val_s_string;
+ val_s_string = "";
+ }
+ else {
+ // Separator found: Split the string at the separator position
+ l_s_token = val_s_string.left(l_i_pos);
+ val_s_string.remove(0,l_i_pos);
+ }
+ val_s_string = val_s_string.simplifyWhiteSpace();
+
+ // Return the first token
+ return l_s_token;
+}
+
+
+
+bool KaimanStyle::loadStyle(const QString &styleName, const QString &descFile)
+{
+ bool l_b_ret = true;
+ int l_i_ret = 0;
+ QString l_s_tmpName;
+
+ i_skinName = styleName;
+ this->i_s_styleName = styleName;
+ i_s_styleBase = QString("skins/kaiman/") + i_s_styleName + QString("/");
+
+ l_s_tmpName = locate("appdata", i_s_styleBase + descFile );
+ if ( l_s_tmpName.isNull() ) {
+ l_b_ret = false;
+ }
+
+ if ( l_b_ret) {
+ // Skin description found. Now parse StyleFile.
+ l_i_ret = parseStyleFile(l_s_tmpName);
+ if (l_i_ret == 0) {
+ // If parsiing OK, load the referenced pixmaps
+ l_b_ret = loadPixmaps();
+
+ kdDebug(66666) << "Found " << I_styleElem.count() << " elements." << endl;
+ }
+ else {
+ if ( l_i_ret == KaimanStyle::FileNotFound ) {
+ // File not found
+ KMessageBox::error( 0, i18n("Cannot load style. Style not installed.") );
+ }
+ else {
+ // Parsing not OK: Notify user
+ KMessageBox::error( 0, i18n("Cannot load style. Unsupported or faulty style description.") );
+ }
+ }
+ }
+
+ return l_b_ret;
+}
+
+bool KaimanStyle::eventFilter( QObject *o, QEvent *e )
+{
+ /* HACK! HACK! HACK! */
+ if ( !i_eventSemaphore )
+ if ( e->type()==QEvent::MouseMove || e->type()==QEvent::MouseButtonPress ||
+ e->type()==QEvent::MouseButtonRelease )
+ {
+ QMouseEvent *m = (QMouseEvent*)e;
+
+ // handle noatun context menu
+ if (m->button()==RightButton) {
+ NoatunStdAction::ContextMenu::showContextMenu();
+ return true;
+ }
+
+ QPoint mousePos( m->x()+static_cast<QWidget *>(o)->x(),
+ m->y()+static_cast<QWidget *>(o)->y() );
+ QWidget *slider = 0;
+
+ /* find slider that is under the mouse position */
+ for ( QWidget *s = i_sliders.first(); s!=0; s=i_sliders.next() )
+ {
+ QRect sliderRect( s->pos(), s->size() );
+ if ( sliderRect.contains(mousePos) ) slider = s;
+ }
+
+ /* the slider the mouse events instead of the visible widget */
+ if ( slider )
+ {
+ QMouseEvent newMouseEvent( m->type(), mousePos-slider->pos(),
+ m->globalPos(), m->button(), m->state() );
+
+ i_eventSemaphore = true;
+ bool ret = QApplication::sendEvent( slider, &newMouseEvent );
+ i_eventSemaphore = false;
+ return ret;
+ }
+ }
+
+ return QWidget::eventFilter( o, e ); // standard event processing
+}
+
+bool KaimanStyle::loadPixmaps()
+{
+ QString l_s_tmpName;
+ QPixmap *l_pixmap_Background = 0, *l_pixmap_Mask = 0;
+ KaimanStyleElement *l_kse_elem;
+
+ for ( uint l_i_elem = 0; l_i_elem<I_styleElem.count(); l_i_elem++) {
+ l_kse_elem = I_styleElem[l_i_elem];
+
+ l_s_tmpName = locate("appdata", i_s_styleBase + l_kse_elem->filename );
+ l_kse_elem->loadPixmaps(l_s_tmpName);
+ }
+
+ l_kse_elem = this->find("Background");
+ if ( l_kse_elem != 0 ) {
+ l_pixmap_Background = l_kse_elem->pixmaps[0];
+ }
+ l_kse_elem = this->find("Mask");
+ if ( l_kse_elem != 0 ) {
+ l_pixmap_Mask = l_kse_elem->pixmaps[0];
+ }
+
+ if ( (l_pixmap_Background != 0) && (l_pixmap_Mask != 0) ) {
+
+ // OK, background and mask are defined. So now I can calculate the shape
+ int l_i_width_Mask = l_pixmap_Mask->width();
+ int l_i_height_Mask = l_pixmap_Mask->height();
+
+ QImage l_image_MaskOrig = l_pixmap_Mask->convertToImage();
+
+ QImage l_image_Mask(l_i_width_Mask,l_i_height_Mask, 1, 2, QImage::LittleEndian);
+ l_image_Mask.setColor( 0, 0x00ffffff );
+ l_image_Mask.setColor( 1, 0 );
+ l_image_Mask.fill( 0xff );
+
+ uchar *l_c_pixel;
+ uint l_qcol_white = qRgb(255,255,255);
+
+ for (int l_i_x=0; l_i_x<l_i_width_Mask; l_i_x++) {
+ for (int l_i_y=0; l_i_y < l_i_height_Mask; l_i_y++) {
+ if ( ((*((QRgb*) l_image_MaskOrig.scanLine(l_i_y)+l_i_x) & 0x00ffffff)) != (l_qcol_white & 0x00ffffff) ) {
+ l_c_pixel = (uchar *)l_image_Mask.scanLine(l_i_y);
+ *(l_c_pixel + (l_i_x>>3) ) &= ~(1 << (l_i_x & 7));
+ }
+ }
+ }
+
+ i_bitmap_Mask.convertFromImage(l_image_Mask);
+ // l_pixmap_Background.setMask(i_bitmap_Mask);
+ }
+
+ return true;
+}
+
+
+QBitmap* KaimanStyle::Mask()
+{
+ return &i_bitmap_Mask;
+}
+
+KaimanStyleElement* KaimanStyle::find(const char *val_s_elemName)
+{
+ for (uint i=0; i< I_styleElem.count(); i++) {
+ if ( I_styleElem[i]->element == QString(val_s_elemName)) {
+ return I_styleElem[i];
+ }
+ }
+ return 0;
+}
+#include "style.moc"
diff --git a/noatun/modules/kaiman/style.h b/noatun/modules/kaiman/style.h
new file mode 100644
index 00000000..0c1a6d81
--- /dev/null
+++ b/noatun/modules/kaiman/style.h
@@ -0,0 +1,356 @@
+// -*- C++ -*-
+/*
+ Copyright (c) 2000 Stefan Schimanski (1Stein@gmx.de)
+ 1999-2000 Christian Esken (esken@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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KaimanStyle_H
+#define KaimanStyle_H
+
+#include <qstring.h>
+#include <qwidget.h>
+#include <qpixmap.h>
+#include <qbitmap.h>
+#include <qimage.h>
+#include <qevent.h>
+#include <qptrvector.h>
+
+class KaimanStyleElement : public QWidget
+{
+ Q_OBJECT
+public:
+ KaimanStyleElement(QWidget *parent, const char *name=0);
+ ~KaimanStyleElement();
+
+ virtual void loadPixmaps(QString &val_s_filename);
+
+ QString element;
+ QString filename;
+ QPoint upperLeft;
+ QSize dimension;
+
+ bool options[3];
+ int digits;
+
+ bool optionPrelight;
+ bool optionStatuslight;
+
+ int pixmapLines;
+ int pixmapColumns;
+
+ QPtrVector<QPixmap> pixmaps;
+
+public slots:
+ void setPixmap( int num );
+
+protected:
+ void paintEvent(QPaintEvent *qpe);
+ void dropEvent( QDropEvent *event );
+ void dragEnterEvent( QDragEnterEvent *event );
+
+ int pixmapNum;
+
+private:
+ int _currentPixmap;
+};
+
+
+class KaimanStyleMasked : public KaimanStyleElement
+{
+ Q_OBJECT
+public:
+ KaimanStyleMasked(QWidget *parent, const char *name=0)
+ : KaimanStyleElement( parent, name ) {};
+
+ virtual void loadPixmaps(QString &val_s_filename)
+ {
+ KaimanStyleElement::loadPixmaps( val_s_filename );
+ if(pixmaps[0]->mask())
+ setMask(*pixmaps[0]->mask());
+ };
+};
+
+
+class KaimanStyleButton : public KaimanStyleMasked
+{
+ Q_OBJECT
+public:
+ KaimanStyleButton(QWidget *parent, const char *name=0);
+ ~KaimanStyleButton();
+
+ // Button states.
+ enum { NormalUp=0, NormalDown, LitUp, LitDown, PrelightUp, PrelightLitUp, StateListEND };
+
+ QPtrVector<int> I_pmIndex;
+
+ void setLit(bool);
+ void setPrelight(bool);
+ void setDown(bool);
+ bool lit();
+ bool prelit();
+ bool down();
+ void updateButtonState();
+
+signals:
+ void clicked();
+
+protected:
+ void mousePressEvent(QMouseEvent *qme);
+ void mouseReleaseEvent(QMouseEvent *qme);
+ void enterEvent(QEvent * );
+ void leaveEvent ( QEvent * );
+
+private:
+ int i_i_currentState;
+ bool i_b_lit;
+ bool i_b_prelit;
+ bool i_b_down;
+};
+
+
+class KaimanStyleSlider : public KaimanStyleMasked
+{
+ Q_OBJECT
+public:
+ KaimanStyleSlider(int min, int max, QWidget *parent, const char *name=0);
+ ~KaimanStyleSlider();
+
+ int value() { return _value; };
+
+ static const bool optionVertical;
+ static const bool optionReversed;
+
+public slots:
+ void setValue( int value );
+ void setValue( int value, int min, int max );
+
+signals:
+ void newValue( int value );
+ void newValueDrag( int value );
+ void newValueDrop( int value );
+
+protected:
+ void mouseMoveEvent(QMouseEvent *qme);
+ void mousePressEvent(QMouseEvent *qme);
+ void mouseReleaseEvent(QMouseEvent *qme);
+ void paintEvent(QPaintEvent *qpe);
+ void enterEvent(QEvent * );
+ void leaveEvent ( QEvent * );
+
+ int pos2value( int x, int y );
+
+ bool _down;
+ bool _lit;
+ int _value;
+ int _min, _max;
+};
+
+
+class KaimanStyleBackground : public KaimanStyleMasked
+{
+ Q_OBJECT
+public:
+ KaimanStyleBackground(QWidget *parent, const char *name=0);
+ ~KaimanStyleBackground();
+
+protected:
+ void mousePressEvent(QMouseEvent *qme);
+ void mouseReleaseEvent(QMouseEvent *qme);
+ void mouseMoveEvent(QMouseEvent *qme);
+
+private:
+ bool i_b_move;
+ QPoint i_point_dragStart;
+ QPoint i_point_lastPos;
+};
+
+
+class KaimanStyleValue : public KaimanStyleMasked
+{
+ Q_OBJECT
+public:
+ KaimanStyleValue(int min, int max, QWidget *parent, const char *name=0);
+ ~KaimanStyleValue();
+
+ int value() { return _value; };
+
+public slots:
+ void setValue( int value );
+ void setValue( int value, int min, int max );
+
+private:
+ int _min, _max, _value;
+};
+
+
+class KaimanStyleState : public KaimanStyleMasked
+{
+ Q_OBJECT
+public:
+ KaimanStyleState(QWidget *parent, const char *name=0);
+ ~KaimanStyleState();
+
+ int value() { return _value; };
+
+public slots:
+ void setValue( int value );
+
+signals:
+ void clicked();
+
+protected:
+ void mousePressEvent(QMouseEvent *qme);
+
+private:
+ int _value;
+};
+
+
+class KaimanStyleNumber : public KaimanStyleElement
+{
+ Q_OBJECT
+public:
+ KaimanStyleNumber(QWidget *parent, const char *name=0);
+ ~KaimanStyleNumber();
+
+ virtual void loadPixmaps(QString &val_s_filename);
+
+ static const bool optionCentered = 1;
+
+ int value() { return _value; };
+
+public slots:
+ void setValue( int value );
+
+protected:
+ void paintEvent(QPaintEvent *qpe);
+
+private:
+ int _value;
+};
+
+
+class KaimanStyleText : public KaimanStyleElement
+{
+ Q_OBJECT
+public:
+ KaimanStyleText(QWidget *parent, const char *name=0);
+ ~KaimanStyleText();
+
+ virtual void loadPixmaps(QString &val_s_filename);
+
+ static const bool optionExtended;
+
+ QString value() { return _value; };
+
+ void startAnimation( int delay );
+ void stopAnimation();
+
+public slots:
+ void setValue( QString value );
+
+protected:
+ void paintEvent(QPaintEvent *qpe);
+
+protected slots:
+ void timeout();
+
+private:
+ QString _value;
+ int _pos;
+ int _direction;
+ int _delay;
+ QTimer *_timer;
+};
+
+
+class KaimanStyleAnimation : public KaimanStyleMasked
+{
+ Q_OBJECT
+public:
+ KaimanStyleAnimation(int delay, QWidget *parent, const char *name=0);
+ ~KaimanStyleAnimation();
+
+public slots:
+ void start();
+ void pause();
+ void stop();
+
+protected:
+ void timeout();
+
+private:
+ int _delay,_frame;
+ QTimer *_timer;
+};
+
+class KaimanStyle : public QWidget
+{
+ Q_OBJECT
+public:
+ KaimanStyle(QWidget *parent, const char *name=0);
+ ~KaimanStyle();
+
+ enum { background, mask, play_Button, stop_Button, pause_Button, prev_Button, next_Button, repeat_Button, shuffle_Button, playlist_Button, mixer_Button, exit_Button, Iconify_Button, Config_Button, Alt_Skin_Button, Minute_Number, Second_Number, in_Rate_Number, in_Hz_Number, song_Number, status_Item, cPU_Number, digit_Large, digit_Small_Default, title, volume_Item, volume_Slider, position_Item, position_Slider };
+
+ enum { ParsingError=1, FileNotFound };
+
+ /// Finds a style element, according to it's name. Returns 0 when element is not available.
+ KaimanStyleElement* find(const char* val_s_elemName);
+
+ /// Tries to load the given style and returns success (true) or failure (false)
+ bool loadStyle(const QString &styleName, const QString &descFile="skindata" );
+ QString skinName() { return i_skinName; };
+
+ /// Returns the mask
+ QBitmap* Mask();
+
+ virtual bool eventFilter( QObject *o, QEvent *e );
+
+private:
+ // Parses the "skindata" file and returns success (true) or failure (false)
+ int parseStyleFile(QString &l_s_tmpName);
+ QString getToken(QString &val_s_string, char val_c_separator);
+ void interpretTokens(QStringList& ref_s_tokens);
+ bool loadPixmaps();
+
+ /// The name of the style, e.g. "k9"
+ QString i_s_styleName;
+ /// The base directory, where the style is found. For example
+ /// "/opt/kde/share/apps/kaiman/Skins/k9/" or "/opt/kde/share/apps/kaiman/Skins/k9.tgz"
+ QString i_s_styleBase;
+
+
+ // The mask of the complete style. Used for doing shaped windows
+ QBitmap i_bitmap_Mask;
+
+ /// All style elements are stored here.
+ QPtrVector<KaimanStyleElement> I_styleElem;
+
+ // The parent window. In other words: The container that holds all the KaimanStyleElement's
+ QWidget* i_qw_parent;
+
+ QPtrList<QWidget> i_sliders;
+ bool i_eventSemaphore;
+
+ QString i_smallFont;
+ QString i_largeFont;
+ QString i_skinName;
+};
+
+
+#endif
diff --git a/noatun/modules/kaiman/userinterface.cpp b/noatun/modules/kaiman/userinterface.cpp
new file mode 100644
index 00000000..31db0e09
--- /dev/null
+++ b/noatun/modules/kaiman/userinterface.cpp
@@ -0,0 +1,562 @@
+/*
+ Copyright (c) 2000 Stefan Schimanski <1Stein@gmx.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, 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
+ aint with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <noatun/effects.h>
+#include <noatun/app.h>
+#include <noatun/player.h>
+#include <noatun/pref.h>
+#include "userinterface.h"
+#include "pref.h"
+
+#include <qcursor.h>
+#include <qpixmap.h>
+#include <qbitmap.h>
+#include <qimage.h>
+#include <qdropsite.h>
+#include <qdragobject.h>
+#include <qtimer.h>
+
+#include <kfiledialog.h>
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kwin.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kconfig.h>
+#include <kpopupmenu.h>
+#include <kstandarddirs.h>
+#include <kmessagebox.h>
+#include <kio/netaccess.h>
+#include <kurldrag.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+#include <X11/X.h>
+#include <X11/Xos.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/shape.h>
+
+Kaiman* Kaiman::kaiman=0;
+const char Kaiman::DEFAULT_SKIN[]="car-preset";
+
+Kaiman::Kaiman()
+ : KMainWindow(0, "NoatunKaiman"), UserInterface()
+{
+ NOATUNPLUGINC(Kaiman);
+ kaiman=this;
+
+ //setCaption( i18n("Kaiman") );
+ KWin::setType( this->winId(), NET::Override );
+ setBackgroundMode( NoBackground );
+ setAcceptDrops(true);
+
+ _style = 0;
+ _seeking = false;
+ _altSkin = false;
+
+ // init config
+ KConfig *config=KGlobal::config();
+ config->setGroup("Kaiman");
+
+ // load skin
+ QString skinName = config->readEntry( "SkinResource", DEFAULT_SKIN );
+
+ if ( !changeStyle(skinName, "skindata") )
+ {
+ KMessageBox::sorry( this, i18n("Cannot load skin %1. Switching to default skin.").arg(skinName) );
+ if ( !changeStyle( DEFAULT_SKIN, "skindata" ) )
+ {
+ KMessageBox::error( this, i18n("Cannot load default skin %1.").arg(DEFAULT_SKIN) );
+ QTimer::singleShot( 0, this, SLOT(close()) );
+ return;
+ }
+ }
+
+ // global connects
+ connect( napp, SIGNAL(hideYourself()), this, SLOT(hide()) );
+ connect( napp, SIGNAL(showYourself()), this, SLOT(show()) );
+
+ connect( napp->player(), SIGNAL(playing()), this, SLOT(updateMode()));
+ connect( napp->player(), SIGNAL(stopped()), this, SLOT(updateMode()));
+ connect( napp->player(), SIGNAL(paused()), this, SLOT(updateMode()));
+ connect( napp->player(), SIGNAL(timeout()), this, SLOT(timeout()));
+ connect( napp->player(), SIGNAL(loopTypeChange(int)), this, SLOT(loopTypeChange(int)));
+ connect( napp->player(), SIGNAL(newSongLen(int,int)), this, SLOT(newSongLen(int,int)));
+ connect( napp->player(), SIGNAL(newSong()), this, SLOT(newSong()));
+
+ if( napp->player()->isPlaying() )
+ newSong();
+
+ new KaimanPrefDlg(this);
+
+ show();
+}
+
+
+Kaiman::~Kaiman()
+{
+}
+
+
+bool Kaiman::changeStyle( const QString &style, const QString &desc )
+{
+ QString ldesc = desc;
+ if ( ldesc.isEmpty() )
+ ldesc = _altSkin ? "alt_skindata" : "skindata";
+
+ bool vis = isVisible();
+ if ( vis )
+ hide();
+
+ bool ret = loadStyle( style, ldesc );
+
+ newSongLen(0,0);
+ timeout();
+ loopTypeChange(0);
+ updateMode();
+
+ if ( vis )
+ show();
+ return ret;
+}
+
+
+bool Kaiman::loadStyle( const QString &style, const QString &desc )
+{
+ if ( _style ) delete _style;
+ _style = new KaimanStyle(this);
+ if ( !_style->loadStyle( style, desc ) )
+ {
+ delete _style;
+ _style = 0;
+ return false;
+ }
+
+ if ( _style->Mask() != 0 )
+ {
+ // Set the shaped window form
+ XShapeCombineMask( qt_xdisplay(), winId(), ShapeBounding, 0,0,
+ _style->Mask()->handle(), ShapeSet );
+ }
+
+ KaimanStyleElement* item = _style->find("Background");
+ setBackgroundMode(QWidget::NoBackground);
+ if ( item!=0 )
+ {
+ _style->resize( item->width(), item->height());
+ resize( item->width(), item->height());
+ setFixedSize( item->width(), item->height());
+ }
+
+ item = _style->find("Playlist_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), napp->player(), SLOT(toggleListView()) );
+
+ item = _style->find("Play_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), napp->player(), SLOT(playpause()) );
+
+ item = _style->find("Pause_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), napp->player(), SLOT(playpause()) );
+
+ item = _style->find("Stop_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), napp->player(), SLOT(stop()) );
+
+ item = _style->find("Next_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), napp->player(), SLOT(forward()) );
+
+ item = _style->find("Prev_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), napp->player(), SLOT(back()) );
+
+ item = _style->find("Exit_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), this, SLOT(close()) );
+
+ item = _style->find("Mixer_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), this, SLOT(execMixer()) );
+
+ item = _style->find("Iconify_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), this, SLOT(showMinimized()) );
+
+ item = _style->find("Alt_Skin_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), this, SLOT(toggleSkin()) );
+
+ item = _style->find("Repeat_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), this, SLOT(toggleLoop()) );
+
+ item = _style->find("Shuffle_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), this, SLOT(toggleShuffle()) );
+
+ item = _style->find("Config_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), napp, SLOT(preferences()) );
+
+ item = _style->find("Volume_Up_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), this,SLOT(volumeUp()));
+
+ item = _style->find("Volume_Down_Button");
+ if( item!=0 ) connect( item, SIGNAL(clicked()), this,SLOT(volumeDown()));
+
+ KaimanStyleSlider* slider =
+ static_cast<KaimanStyleSlider*>(_style->find("Position_Slider"));
+ if( slider!=0 )
+ {
+ connect( slider, SIGNAL(newValueDrag(int)), this, SLOT(seekStart(int)) );
+ connect( slider, SIGNAL(newValue(int)), this, SLOT(seekDrag(int)) );
+ connect( slider, SIGNAL(newValueDrop(int)), this, SLOT(seekStop(int)) );
+ slider->setValue( 0, 0, 1000 );
+ }
+
+ slider = static_cast<KaimanStyleSlider*>(_style->find("Volume_Slider"));
+ if ( slider!=0 )
+ {
+ connect(slider, SIGNAL(newValue(int)), this, SLOT(setVolume(int)));
+ slider->setValue( napp->player()->volume(), 0, 100 );
+ }
+
+ KaimanStyleValue* volItem = static_cast<KaimanStyleValue*>(_style->find("Volume_Item"));
+ if ( volItem )
+ volItem->setValue( napp->player()->volume(), 0, 100 );
+
+ KaimanStyleText* titleItem = static_cast<KaimanStyleText*>(_style->find("Title"));
+ if ( titleItem )
+ titleItem->startAnimation( 300 );
+
+ return true;
+}
+
+
+void Kaiman::closeEvent(QCloseEvent*)
+{
+ unload();
+}
+
+
+void Kaiman::dragEnterEvent( QDragEnterEvent *event )
+{
+ event->accept( KURLDrag::canDecode(event) );
+}
+
+
+void Kaiman::dropEvent( QDropEvent *event )
+{
+ doDropEvent(event);
+}
+
+
+void Kaiman::doDropEvent(QDropEvent *event)
+{
+ KURL::List uri;
+ if (KURLDrag::decode(event, uri))
+ {
+ for (KURL::List::Iterator i = uri.begin(); i != uri.end(); ++i)
+ napp->player()->openFile(*i, false);
+ }
+}
+
+
+void Kaiman::seekStart( int )
+{
+ _seeking = true;
+}
+
+
+void Kaiman::seekDrag( int value )
+{
+ int length = napp->player()->getLength()/1000;
+ if ( length < 0)
+ length = 0;
+
+ if ( !_style ) return;
+
+ KaimanStyleValue* posItem =
+ static_cast<KaimanStyleValue*>(_style->find("Position_Item"));
+ if ( posItem )
+ posItem->setValue( value, 0, length );
+
+ KaimanStyleSlider* posSlider =
+ static_cast<KaimanStyleSlider*>(_style->find("Position_Slider"));
+ if ( posSlider )
+ posSlider->setValue( value, 0, length );
+
+ // update time
+
+ KaimanStyleNumber* numItem =
+ static_cast<KaimanStyleNumber*>(_style->find("Minute_Number"));
+ if ( numItem )
+ numItem->setValue( value/60%60 );
+
+ numItem = static_cast<KaimanStyleNumber*>(_style->find("Second_Number"));
+ if ( numItem )
+ numItem->setValue( value % 60 );
+}
+
+
+void Kaiman::seekStop( int value )
+{
+ seek( value );
+ _seeking = false;
+}
+
+
+void Kaiman::seek( int value )
+{
+ napp->player()->skipTo( value*1000 ); // skipTo() takes milliseconds
+}
+
+
+void Kaiman::toggleSkin()
+{
+ _altSkin = !_altSkin;
+
+ QString skinName = _style->skinName();
+
+ QString oldDesc, newDesc;
+ if ( _altSkin )
+ {
+ oldDesc = QString::fromLatin1("skindata");
+ newDesc = QString::fromLatin1("alt_skindata");
+ }
+ else
+ {
+ newDesc = QString::fromLatin1("skindata");
+ oldDesc = QString::fromLatin1("alt_skindata");
+ }
+
+ if ( !changeStyle(skinName, newDesc) )
+ changeStyle(skinName, oldDesc);
+}
+
+
+void Kaiman::setVolume( int vol )
+{
+ if ( vol<0 ) vol=0;
+ if ( vol>=100 ) vol=100;
+
+ napp->player()->setVolume( vol );
+}
+
+
+void Kaiman::volumeUp()
+{
+ setVolume( napp->player()->volume()+10 );
+}
+
+
+void Kaiman::volumeDown()
+{
+ setVolume( napp->player()->volume()-10 );
+}
+
+
+void Kaiman::execMixer()
+{
+ kapp->startServiceByDesktopName ( QString::fromLatin1("kmix"), QString::null );
+}
+
+
+void Kaiman::timeout()
+{
+ if ( !_style ) return;
+
+ if (!napp->player()->current())
+ return;
+
+ // update volume
+ KaimanStyleSlider* l_elem_volslider = static_cast<KaimanStyleSlider*>(_style->find("Volume_Slider"));
+ KaimanStyleValue* l_elem_volitem = static_cast<KaimanStyleValue*>(_style->find("Volume_Item"));
+ if ( l_elem_volslider!=0 )
+ l_elem_volslider->setValue( napp->player()->volume(), 0, 100 );
+ if ( l_elem_volitem!=0 )
+ l_elem_volitem->setValue( napp->player()->volume(), 0, 100 );
+
+ // update position
+ if ( !_seeking )
+ {
+ int sec = napp->player()->getTime()/1000;
+ if ( sec < 0 )
+ sec = 0;
+
+ KaimanStyleValue* posItem =
+ static_cast<KaimanStyleValue*>(_style->find("Position_Item"));
+ if ( posItem ) posItem->setValue( sec, 0, napp->player()->getLength()/1000 );
+
+ KaimanStyleSlider* posSlider =
+ static_cast<KaimanStyleSlider*>(_style->find("Position_Slider"));
+ if ( posSlider ) posSlider->setValue( sec, 0, napp->player()->getLength()/1000 );
+
+ // update time
+ KaimanStyleNumber* numItem =
+ static_cast<KaimanStyleNumber*>(_style->find("Minute_Number"));
+ if ( numItem )
+ numItem->setValue( sec/60%60 );
+
+ numItem = static_cast<KaimanStyleNumber*>(_style->find("Second_Number"));
+ if ( numItem )
+ numItem->setValue( sec%60 );
+ }
+
+ const PlaylistItem &item = napp->playlist()->current();
+ KaimanStyleNumber* numItem = 0;
+
+ numItem = static_cast<KaimanStyleNumber*>(_style->find("In_Rate_Number"));
+ if ( numItem )
+ numItem->setValue(item.property("bitrate").toInt());
+
+ QString hzString = item.property("samplerate");
+ hzString.truncate(2);
+
+ numItem = static_cast<KaimanStyleNumber*>(_style->find("In_Hz_Number"));
+ if ( numItem )
+ numItem->setValue(hzString.toInt());
+}
+
+
+void Kaiman::updateMode()
+{
+ if ( !_style ) return;
+
+ KaimanStyleButton* pause =
+ static_cast<KaimanStyleButton*>(_style->find("Pause_Button"));
+ KaimanStyleButton* play =
+ static_cast<KaimanStyleButton*>(_style->find("Play_Button"));
+ KaimanStyleState* status =
+ static_cast<KaimanStyleState*>(_style->find("Status_Item"));
+
+ if (napp->player()->isStopped() )
+ {
+ if ( pause ) pause->setLit( false );
+ if ( play ) play->setLit( false );
+ if ( status ) status->setValue( 0 );
+ }
+ else if ( napp->player()->isPlaying() )
+ {
+ if ( pause ) pause->setLit( false );
+ if ( play ) play->setLit( true );
+ if ( status ) status->setValue( 2 );
+ }
+ else if ( napp->player()->isPaused() )
+ {
+ if ( pause ) pause->setLit( true );
+ if ( play ) play->setLit( false );
+ if ( status ) status->setValue( 1 );
+ }
+}
+
+
+void Kaiman::loopTypeChange( int )
+{
+ if ( !_style ) return;
+
+ KaimanStyleState* rep = static_cast<KaimanStyleState*>(_style->find("Repeat_Item"));
+ if ( rep )
+ rep->setValue( napp->player()->loopStyle() );
+}
+
+
+void Kaiman::newSongLen( int, int )
+{
+ if ( !_style )
+ return;
+
+ int len = napp->player()->getLength()/1000; // convert milliseconds -> seconds
+ if ( len < 0 ) // getLength returns -1 if there's no Playobject
+ len = 0;
+
+ // update time
+ KaimanStyleNumber* numItem =
+ static_cast<KaimanStyleNumber*>(_style->find("Minute_Total_Number"));
+ if ( numItem )
+ numItem->setValue( len/60%60 );
+
+ numItem = static_cast<KaimanStyleNumber*>(_style->find("Second_Total_Number"));
+ if ( numItem )
+ numItem->setValue( len%60 );
+
+ numItem = static_cast<KaimanStyleNumber*>(_style->find("Song_Minute_Number"));
+ if ( numItem )
+ numItem->setValue( len/60%60 );
+
+ numItem = static_cast<KaimanStyleNumber*>(_style->find("Second_Minute_Number"));
+ if ( numItem )
+ numItem->setValue( len%60 );
+
+ newSong();
+}
+
+
+void Kaiman::newSong()
+{
+ if ( !_style ) return;
+
+ KaimanStyleText* titleItem = static_cast<KaimanStyleText*>(_style->find("Title"));
+ if ( titleItem )
+ {
+ QString title = i18n("Noatun");
+ if ( napp->player()->current() )
+ {
+ title = napp->player()->current().title();
+ if ( title.isEmpty() )
+ title = napp->player()->current().file();
+
+ title = i18n("TITLE (LENGTH)", "%1 (%2)").arg(title,
+ napp->player()->current().lengthString());
+ }
+ titleItem->setValue( title );
+ }
+}
+
+
+#undef None
+void Kaiman::toggleLoop()
+{
+ KPopupMenu *loopMenu = new KPopupMenu(this, "loopMenu");
+ int selectedItem = 0;
+
+ loopMenu->setCheckable(true);
+ loopMenu->insertTitle(i18n("Loop Style"));
+ loopMenu->insertItem(i18n("&None"),
+ (int)Player::None);
+ loopMenu->insertItem(i18n("&Song"),
+ (int)Player::Song);
+ loopMenu->insertItem(i18n("&Playlist"),
+ (int)(Player::Playlist));
+ loopMenu->insertItem(i18n("&Random"),
+ (int)(Player::Random));
+
+ loopMenu->setItemChecked((int)napp->player()->loopStyle(), true); // select current loopstyle in menu
+ selectedItem = loopMenu->exec(QCursor::pos());
+ if (selectedItem != -1)
+ napp->player()->loop(selectedItem); // set new loopstyle
+
+ delete loopMenu;
+}
+
+
+void Kaiman::toggleShuffle()
+{
+ napp->player()->loop(Player::Random); // set loopstyle to be random
+}
+
+#include "userinterface.moc"
diff --git a/noatun/modules/kaiman/userinterface.h b/noatun/modules/kaiman/userinterface.h
new file mode 100644
index 00000000..2fb46184
--- /dev/null
+++ b/noatun/modules/kaiman/userinterface.h
@@ -0,0 +1,85 @@
+/*
+ Copyright (c) 2000 Stefan Schimanski <1Stein@gmx.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, 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
+ aint with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef USERINTERFACE_H
+#define USERINTERFACE_H
+
+#include <noatun/plugin.h>
+#include <noatun/app.h>
+#include <kmainwindow.h>
+#include "style.h"
+
+
+class Player;
+
+/**
+ * @short Main window class
+ * @author Stefan Schimanski <1Stein@gmx.de>
+ * @version 0.1
+ */
+class Kaiman : public KMainWindow, public UserInterface
+{
+Q_OBJECT
+ public:
+ Kaiman();
+ virtual ~Kaiman();
+
+ bool changeStyle( const QString &style, const QString &desc=QString::null );
+ public slots:
+ void dropEvent( QDropEvent * );
+ void doDropEvent( QDropEvent * );
+ void dragEnterEvent( QDragEnterEvent * );
+ void closeEvent(QCloseEvent*);
+
+ protected slots:
+ void seekStart( int );
+ void seekDrag( int );
+ void seekStop( int );
+ void seek( int );
+ void toggleSkin();
+
+ void setVolume( int vol );
+ void volumeUp();
+ void volumeDown();
+
+ void execMixer();
+
+
+ void timeout();
+ void loopTypeChange( int t );
+ void newSongLen( int mins, int sec );
+ void newSong();
+ void updateMode();
+ void toggleLoop();
+ void toggleShuffle();
+
+ public:
+ static const char DEFAULT_SKIN[];
+ static Kaiman *kaiman;
+
+ protected:
+ bool loadStyle( const QString &style, const QString &desc );
+
+
+ bool _seeking;
+ bool _altSkin;
+ KaimanStyle *_style;
+};
+
+
+#endif
diff --git a/noatun/modules/keyz/Makefile.am b/noatun/modules/keyz/Makefile.am
new file mode 100644
index 00000000..61c9f1bd
--- /dev/null
+++ b/noatun/modules/keyz/Makefile.am
@@ -0,0 +1,14 @@
+INCLUDES= -I$(top_srcdir)/noatun/library -I$(kde_includes)/arts $(all_includes)
+kde_module_LTLIBRARIES = noatun_keyz.la
+
+noatun_keyz_la_SOURCES = keyz.cpp
+
+noatun_keyz_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_keyz_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la
+
+noatun_keyz_la_METASOURCES = AUTO
+
+noinst_HEADERS = keyz.h
+
+noatun_modules_keyz_DATA = keyz.plugin
+noatun_modules_keyzdir = $(kde_datadir)/noatun
diff --git a/noatun/modules/keyz/keyz.cpp b/noatun/modules/keyz/keyz.cpp
new file mode 100644
index 00000000..7b32cf98
--- /dev/null
+++ b/noatun/modules/keyz/keyz.cpp
@@ -0,0 +1,189 @@
+#include <noatun/engine.h>
+#include <noatun/player.h>
+#include <noatun/app.h>
+#include <noatun/playlist.h>
+
+#include <qlayout.h>
+
+#include <kglobalaccel.h>
+#include <kkeydialog.h>
+#include <klocale.h>
+#include <qclipboard.h>
+
+#include "keyz.h"
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new Keyz();
+ }
+}
+
+KGlobalAccel * Keyz::s_accel = 0L;
+
+Keyz::Keyz() : QObject( 0L, "Keyz" ), Plugin(), preMuteVol(0)
+{
+ NOATUNPLUGINC(Keyz);
+ Player *player = napp->player();
+
+ if ( !s_accel )
+ {
+ s_accel = new KGlobalAccel( this, "noatunglobalaccel" );
+ s_accel->insert( "PlayPause", i18n("Play/Pause"), QString::null,
+ CTRL+ALT+Key_P, KKey::QtWIN+CTRL+Key_P,
+ player, SLOT( playpause() ));
+ s_accel->insert( "Stop", i18n("Stop Playing"), QString::null,
+ CTRL+ALT+Key_S, KKey::QtWIN+CTRL+Key_S,
+ player, SLOT( stop() ));
+ s_accel->insert( "Back", i18n("Back"), QString::null,
+ CTRL+ALT+Key_Left, KKey::QtWIN+CTRL+Key_Left,
+ player, SLOT( back() ));
+ s_accel->insert( "Forward", i18n("Forward"), QString::null,
+ CTRL+ALT+Key_Right, KKey::QtWIN+CTRL+Key_Right,
+ player, SLOT( forward() ));
+ s_accel->insert( "Playlist", i18n("Show/Hide Playlist"), QString::null,
+ CTRL+ALT+Key_L, KKey::QtWIN+CTRL+Key_L,
+ player, SLOT( toggleListView() ));
+ s_accel->insert( "OpenFile", i18n("Open File to Play"), QString::null,
+ CTRL+ALT+Key_O, KKey::QtWIN+CTRL+Key_O,
+ napp, SLOT( fileOpen() ));
+ s_accel->insert( "Effects", i18n("Effects Configuration"), QString::null,
+ CTRL+ALT+Key_E, KKey::QtWIN+CTRL+Key_E,
+ napp, SLOT( effectView() ));
+ s_accel->insert( "Preferences", i18n("Preferences"), QString::null,
+ CTRL+ALT+Key_F, KKey::QtWIN+CTRL+Key_F,
+ napp, SLOT( preferences() ));
+ s_accel->insert( "VolumeUp", i18n("Volume Up"), QString::null,
+ CTRL+ALT+SHIFT+Key_Up, KKey::QtWIN+CTRL+SHIFT+Key_Up,
+ this, SLOT( slotVolumeUp() ));
+ s_accel->insert( "VolumeDown", i18n("Volume Down"), QString::null,
+ CTRL+ALT+SHIFT+Key_Down, KKey::QtWIN+CTRL+SHIFT+Key_Down,
+ this, SLOT( slotVolumeDown() ));
+ s_accel->insert( "Mute", i18n("Mute"), QString::null,
+ CTRL+ALT+Key_M, KKey::QtWIN+CTRL+Key_M,
+ this, SLOT( slotMute() ));
+ s_accel->insert( "SeekForward", i18n("Seek Forward"), QString::null,
+ CTRL+ALT+SHIFT+Key_Right, KKey::QtWIN+CTRL+SHIFT+Key_Right,
+ this, SLOT( slotForward() ));
+ s_accel->insert( "SeekBackward", i18n("Seek Backward"), QString::null,
+ CTRL+ALT+SHIFT+Key_Left, KKey::QtWIN+CTRL+SHIFT+Key_Left,
+ this, SLOT( slotBackward() ));
+ s_accel->insert( "NextSection", i18n("Next Section"), QString::null,
+ 0, 0,
+ this, SLOT( slotNextSection() ));
+ s_accel->insert( "PrevSection", i18n("Previous Section"), QString::null,
+ 0, 0,
+ this, SLOT( slotPrevSection() ));
+ s_accel->insert( "CopyTitle", i18n("Copy Song Title to Clipboard"), QString::null,
+ CTRL+ALT+Key_C, KKey::QtWIN+CTRL+Key_C,
+ this, SLOT( slotCopyTitle() ));
+
+ s_accel->insert( "ToggleGUI", i18n("Show/Hide Main Window"), QString::null,
+ CTRL+ALT+Key_W, KKey::QtWIN+CTRL+Key_W,
+ napp, SLOT( toggleInterfaces() ));
+
+ s_accel->readSettings();
+ s_accel->updateConnections();
+ }
+
+ new KeyzPrefs(this);
+}
+
+Keyz::~Keyz()
+{
+ delete s_accel;
+ s_accel = 0L;
+}
+
+void Keyz::slotVolumeUp()
+{
+ int vol = napp->player()->volume();
+ if ( vol >= 100 )
+ return;
+
+ napp->player()->setVolume( vol + 4 );
+}
+
+void Keyz::slotVolumeDown()
+{
+ int vol = napp->player()->volume();
+ if ( vol <= 0 )
+ return;
+
+ napp->player()->setVolume( vol - 4 );
+}
+
+void Keyz::slotForward()
+{
+ napp->player()->skipTo( QMIN(napp->player()->getLength(), napp->player()->getTime() + 3000) ); // + 3 seconds
+}
+
+void Keyz::slotBackward()
+{
+ napp->player()->skipTo( QMAX( 0, napp->player()->getTime() - 3000 )); // - 3 seconds
+}
+
+void Keyz::slotNextSection()
+{
+ Playlist *list = napp->playlist();
+ if ( list ) {
+ PlaylistItem item = list->nextSection();
+ if ( !item.isNull() )
+ napp->player()->play( item );
+ }
+}
+
+void Keyz::slotPrevSection()
+{
+ Playlist *list = napp->playlist();
+ if ( list ) {
+ PlaylistItem item = list->previousSection();
+ if ( !item.isNull() )
+ napp->player()->play( item );
+ }
+}
+
+void Keyz::slotCopyTitle()
+{
+ if (napp->player()->current())
+ KApplication::kApplication()->clipboard()->setText(napp->player()->current().title());
+}
+
+
+void Keyz::slotMute()
+{
+ int vol = napp->player()->volume();
+
+ if ( vol <= 0 ) // already muted
+ {
+ vol = preMuteVol;
+ }
+ else // we should mute
+ {
+ preMuteVol = vol;
+ vol = 0;
+ }
+
+ napp->player()->setVolume( vol );
+}
+
+///////////////////////////////////////////////////////////////////
+
+KeyzPrefs::KeyzPrefs( QObject *parent ) :
+ CModule( i18n("Keyz"), i18n("Shortcut Configuration"), "key_bindings",
+ parent )
+{
+ QVBoxLayout *layout = new QVBoxLayout( this );
+ m_chooser = new KKeyChooser( Keyz::accel(), this );
+ layout->addWidget( m_chooser );
+}
+
+void KeyzPrefs::save()
+{
+ m_chooser->commitChanges();
+ Keyz::accel()->updateConnections();
+ Keyz::accel()->writeSettings();
+}
+
+#include "keyz.moc"
diff --git a/noatun/modules/keyz/keyz.h b/noatun/modules/keyz/keyz.h
new file mode 100644
index 00000000..929e6c67
--- /dev/null
+++ b/noatun/modules/keyz/keyz.h
@@ -0,0 +1,48 @@
+#ifndef KEYZ_H
+#define KEYZ_H
+
+#include <noatun/pref.h>
+#include <noatun/plugin.h>
+
+class Keyz : public QObject, public Plugin
+{
+ Q_OBJECT
+ NOATUNPLUGIND
+
+public:
+ Keyz();
+ ~Keyz();
+
+ static KGlobalAccel *accel() { return s_accel; }
+
+public slots:
+void slotVolumeUp();
+ void slotVolumeDown();
+ void slotMute();
+
+ void slotForward();
+ void slotBackward();
+ void slotNextSection();
+ void slotPrevSection();
+ void slotCopyTitle();
+
+private:
+ static KGlobalAccel *s_accel;
+ int preMuteVol;
+};
+
+
+class KeyzPrefs : public CModule
+{
+ Q_OBJECT
+
+public:
+ KeyzPrefs( QObject *parent );
+ virtual void save();
+
+private:
+ class KKeyChooser* m_chooser;
+
+};
+
+#endif // KEYZ_H
diff --git a/noatun/modules/keyz/keyz.plugin b/noatun/modules/keyz/keyz.plugin
new file mode 100644
index 00000000..41560e86
--- /dev/null
+++ b/noatun/modules/keyz/keyz.plugin
@@ -0,0 +1,69 @@
+Filename=noatun_keyz.la
+Author=Carsten Pfeiffer
+Site=http://noatun.kde.org/
+Email=pfeiffer@kde.org
+Type=other
+License=Artistic
+Name=Keyz
+Name[af]=Keys
+Name[cs]=Klávesy
+Name[de]=Tasten
+Name[eo]=Klavkombinoj
+Name[he]=מקשים
+Name[hi]=के-ईवायज़ेड
+Name[nds]=Tasten
+Name[ru]=Клавишки
+Name[sv]=Tangentisar
+Name[ta]=விசைகள்
+Name[xh]=Izitshixo
+Name[zu]=Izikhiye
+Comment=Global shortcuts for most operations
+Comment[bg]=Управление чрез бързи клавиши на повечето операции
+Comment[bs]=Globalne kratice za većinu operacija
+Comment[ca]=Dreceres globals per a la majoria d'operacions
+Comment[cs]=Globální zkratky pro většinu operací
+Comment[cy]=Byrlwybrau Eang i'r rhan fwyaf o weithredoedd
+Comment[da]=Globale genveje til de fleste operationer
+Comment[de]=Globale Kurzbefehle für die meisten Vorgänge
+Comment[el]=Καθολικές συντομεύσεις για τις περισσότερες λειτουργίες
+Comment[eo]=Mallokaj klavkombinoj por multaj operacioj
+Comment[es]=Accesos directos globales para la mayoría de las operaciones
+Comment[et]=Enamiku tegevuste globaalsed kiirklahvid
+Comment[eu]=Laisterbide globalak eragiketa gehienetarako
+Comment[fa]=میان‌برهای سراسری برای اغلب عملیاتها
+Comment[fi]=Yleiset pikavalinnat lähes kaikille toiminnoille
+Comment[fr]=Raccourcis pour l'essentiel des opérations
+Comment[ga]=Aicearraí comhchoiteanna d'fhormhór na n-oibríochtaí
+Comment[gl]=Atallos globais para a maioría das operacións
+Comment[he]=קיצורי דרך גלובליים לרוב הפעולות
+Comment[hu]=Globális billentyűparancsok a legtöbb művelethez
+Comment[is]=Altækir flýtilyklar fyrir flest allt
+Comment[it]=Scorciatoie globali per le operazioni comuni
+Comment[ja]=ほとんどの操作に使われるグローバルショートカット
+Comment[kk]=Көп операциялардың жалпы-жүйелік перне тірсесімдері
+Comment[km]=ផ្លូវកាត់​សកល​សម្រាប់​ប្រតិបត្តិការ​ភាគ​ច្រើន
+Comment[ko]=대부분 작업에 대한 전역 단축키
+Comment[lt]=Bendri spartieji klavišai daugumai operacijų
+Comment[mk]=Глобални кратенки за повеќето операции
+Comment[nb]=Globale snarveier for de fleste operasjoner
+Comment[nds]=Globaal Tastkombinatschonen för de mehrsten Akschonen
+Comment[ne]=धेरै सञ्चालनका लागि विश्वव्यापी सर्टकट
+Comment[nl]=Globale snelkoppelingen voor de meeste handelingen
+Comment[nn]=Globale snøggtastar for dei fleste operasjonar
+Comment[pl]=Globalne skróty dla większości operacji
+Comment[pt]=Atalhos globais para a maioria das operações
+Comment[pt_BR]=Atalhos globais para a maioria das operações
+Comment[ro]=Acceleratori globali pentru operaţii uzuale
+Comment[ru]=Привязки клавиш для большинства действий
+Comment[sk]=Globálne skratky pre väčšinu operácií
+Comment[sl]=Globalne bližnjice za večino operacij
+Comment[sr]=Глобалне пречице за већину операција
+Comment[sr@Latn]=Globalne prečice za većinu operacija
+Comment[sv]=Globala snabbtangenter för de flesta operationerna
+Comment[ta]=பல செயலுகளுக்கான உலகளாவிய குறுக்குவழிகள்
+Comment[th]=ปุ่มพิมพ์ลัดส่วนกลางสำหรับปฏิบัติการส่วนใหญ่
+Comment[tr]=Pek çok İşlemler için Genel Kısayollar
+Comment[uk]=Глобальні скорочення для більшості дій
+Comment[zh_CN]=大多数操作的全局快捷键
+Comment[zh_HK]=大部分操作的通用捷徑
+Comment[zh_TW]=給一般大部分操作使用的全域快速鍵
diff --git a/noatun/modules/kjofol-skin/ChangeLog b/noatun/modules/kjofol-skin/ChangeLog
new file mode 100644
index 00000000..537de012
--- /dev/null
+++ b/noatun/modules/kjofol-skin/ChangeLog
@@ -0,0 +1,111 @@
+ChangeLog (only lists MY changes)
+-----------------------------------------------------------------------------------
+
+2003-01-09 Stefan Gehn <sgehn@gmx.net>
+* Allow use of system fonts instead of skin-supplied pixmap-fonts, user
+ has to take care for choosing an appropriate font and font-color
+
+2003-01-19 Stefan Gehn <sgehn@gmx.net>
+* Equalizer support
+* Ignore unsupported buttons for spectrum/oscilloscope, won't work with default skin anyway
+* Display about-lines in skinselector
+
+2002-04-04 Stefan Gehn <sgehn@gmx.net>
+* Started work on config-dialog, this will unhide most things you can now only
+ set by clicking on widgets (i.e. time counting mode)
+
+2002-01-21 Stefan Gehn <sgehn@gmx.net>
+* Actually read counting-mode of time-display from config
+
+2002-01-20 Stefan Gehn <sgehn@gmx.net>
+* Fix ugly crash on Startup related to Time-Display
+* less debug
+
+2002-01-06 Stefan Gehn <sgehn@gmx.net>
+* Made Playlist-button show if playlist-window is open even after loading a new skin
+* Added Tooltips for text-displays
+* usual removal of old debug-messages and adding new ones :)
+
+2002-01-04 Stefan Gehn <sgehn@gmx.net>
+* changed scrolling-text behaviour (scrolls 1/2 char every 400ms now)
+* playlist-button is a state-button, i.e. shows if playlist-window is open or not
+* commented or removed some more unused debug-messages
+
+2001-12-14 Stefan Gehn <sgehn@gmx.net>
+* slightly changed skin-installer (creates dirs without archive extension)
+* code cleanups, removed debug-messages
+
+2001-12-12 Stefan Gehn <sgehn@gmx.net>
+* finally made font-spacing between characters working (no garbage inside space)
+* textfields with more space than needed for the string to display now get centered text
+ (take a look at volumetext or pitchtext to see the centere-effect)
+* Noatun is not shown in taskbar if in dockmode as it's no normal window in that mode
+* take care if Playlist is shown/hidden and update the playlistbutton
+* using kdDebug(66666) for debugmessages instead of stderr-output
+
+2001-10-04 Stefan Gehn <sgehn@gmx.net>
+* Still fighting with analyzer and osci visualizations,
+ now both are buffered and flicker-free
+* fixed stupid bug in isGray(qRgb), now RGB(0,0,0) is treated as gray again :)
+* commented out (hopefully) unneded debug-messages
+* added splash-screen for skins supporting it (only skin I know of: K-Nine)
+
+2001-09-30 Stefan Gehn <sgehn@gmx.net>
+* ignore alpha-channel of loaded files, they break applying masks to QPixmaps,
+ result of this is text without transparency
+ (this happened for two skins: adagio and xbs)
+
+2001-09-28 Stefan Gehn <sgehn@gmx.net>
+* analyzer-like scope is now drawn using a gradient instead of exactly one boring color
+* removed KJLoader::fixSkin() as Njaard deleted the script doing all the work, I assume
+ that this is not needed anymore (guess: maybe QT3 now handles
+ most fileformats in a proper way?)
+
+2001-09-22 Stefan Gehn <sgehn@gmx.net>
+* isGray(qRgb) is more tolerant, colors like rgb(162,163,162) are treated as gray too
+ This fixes some volume/pitch-sliders and makes using them a lot easier
+* made titletext move again (works with and without transparent text)
+
+2001-09-16 Stefan Gehn <sgehn@gmx.net>
+* Made it compile with QT3 and stopped using Njaard's hack to load PNGs
+
+2001-09-04 Stefan Gehn <sgehn@gmx.net>
+* added support for transparent fonts (a skin named steelforged still got problems though)
+ for the moment this disables moving of titletext,
+ I have to find a new way to move it (bitblt on a masked QPixmap is tricky)
+
+2001-09-03 Stefan Gehn <sgehn@gmx.net>
+* added support for pitch-slider and its textlabel
+ kjfol now depends on libartsmodules because of pitch-support
+* finished splitting up kjloader.cpp into several files.
+ Now almost every class has its own file
+ also took care that compiling with --enable-final works
+
+2001-09-02 Stefan Gehn <sgehn@gmx.net>
+* started splitting up kjloader.cpp into several files
+
+Before 2001-09-02 Stefan Gehn <sgehn@gmx.net>
+
+I should have started a ChangeLog earlier :/
+Never thought I would change THAT much
+(it all started with small noatun-bugfixes)
+
+I'll now try to list the tons of changes done before (totally unordered)
+
+* fixed seeker to work with long files
+ (I've got a 330min mp3, seeking did not work behind about 120min)
+* made the repeat-button work, it switches between no-looping and song-looping
+* made the forward/backward-buttons work, they will skip 10 seconds
+* added dockmode-support, in this mode kjfol will dock to the currently active window
+ the window's behaviour looks like this mode needs some more work
+* added support for textlabels showing current volume
+* added a skininstaller, it still needs proper widgets showing
+ the actions going on (unpacking, moving files, deleting files on uninstall)
+* tweaked builtin visualizations, both now try to fit into any possible skin
+ last activated vis is remembered on restart
+* vis honors "AnalyzerColor" and defaults to white if that key is missing in the skin.rc
+* support for buttons with "DARKEN"-flag (normally "BMPsomenumber" is used)
+* made kjofol parse the skin.rc in lowercase only, now it's fully case-insensitve
+* many many tests for existence of keys, 99% of former crashes were caused by not making
+ sure a certain key really exists. Many places now assume "default"-values if keys are
+ missing, this leads to MUCH better compatibility
diff --git a/noatun/modules/kjofol-skin/Makefile.am b/noatun/modules/kjofol-skin/Makefile.am
new file mode 100644
index 00000000..17ff1162
--- /dev/null
+++ b/noatun/modules/kjofol-skin/Makefile.am
@@ -0,0 +1,41 @@
+INCLUDES = -I$(top_srcdir)/noatun/library \
+ -I$(top_builddir)/noatun/library \
+ -I$(top_builddir)/arts/modules \
+ -I$(top_builddir)/arts/midi \
+ -I$(top_builddir)/arts/modules/synth \
+ -I$(top_builddir)/arts/modules/common \
+ -I$(top_builddir)/arts/modules/effects \
+ -I$(top_builddir)/arts/modules/mixers \
+ -I$(top_builddir)/arts/gui/common \
+ -I$(kde_includes)/kio \
+ -I$(kde_includes)/arts \
+ $(all_includes)
+
+kde_module_LTLIBRARIES = noatun_kjofol.la
+SUBDIRS= . skins
+METASOURCES = AUTO
+
+noatun_kjofol_la_SOURCES = kjloader.cpp kjwidget.cpp kjbutton.cpp kjseeker.cpp \
+ kjsliders.cpp kjfont.cpp kjtextdisplay.cpp \
+ kjvis.cpp kjequalizer.cpp kjbackground.cpp \
+ noatunui.cpp parser.cpp \
+ kjprefs.cpp \
+ kjskinselectorwidget.ui kjguisettingswidget.ui
+
+noatun_kjofol_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_kjofol_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la \
+ $(LIB_KIO) -lm \
+ $(top_builddir)/arts/modules/libartsmodules.la
+
+noinst_HEADERS = kjloader.h kjwidget.h kjbutton.h kjseeker.h \
+ kjsliders.h kjfont.h kjtextdisplay.h \
+ kjvis.h kjequalizer.h kjbackground.h \
+ parser.h kjprefs.h \
+ kjskinselectorwidget.h kjguisettingswidget.h
+
+noatun_modules_kjofol_DATA = kjofolui.plugin
+noatun_modules_kjofoldir = $(kde_datadir)/noatun
+
+kjloader.lo: ../../../arts/modules/artsmodules.h ../../../arts/midi/artsmidi.h ../../../arts/gui/common/artsgui.h ../../../arts/modules/common/artsmodulescommon.h ../../../arts/modules/synth/artsmodulessynth.h ../../../arts/modules/effects/artsmoduleseffects.h ../../../arts/modules/mixers/artsmodulesmixers.h ../../library/noatunarts/noatunarts.h
+kjsliders.lo: ../../../arts/modules/artsmodules.h ../../../arts/midi/artsmidi.h ../../../arts/gui/common/artsgui.h ../../../arts/modules/common/artsmodulescommon.h ../../../arts/modules/synth/artsmodulessynth.h ../../../arts/modules/effects/artsmoduleseffects.h ../../../arts/modules/mixers/artsmodulesmixers.h
+kjtextdisplay.lo: ../../../arts/modules/artsmodules.h ../../../arts/midi/artsmidi.h ../../../arts/gui/common/artsgui.h ../../../arts/modules/common/artsmodulescommon.h ../../../arts/modules/synth/artsmodulessynth.h ../../../arts/modules/effects/artsmoduleseffects.h ../../../arts/modules/mixers/artsmodulesmixers.h
diff --git a/noatun/modules/kjofol-skin/helpers.cpp b/noatun/modules/kjofol-skin/helpers.cpp
new file mode 100644
index 00000000..b38822a6
--- /dev/null
+++ b/noatun/modules/kjofol-skin/helpers.cpp
@@ -0,0 +1,64 @@
+/***************************************************************************
+ helpers.cpp
+ Just a few functions needed in several Kjofol-classes
+ ---------------------------------------------
+ Maintainer: Charles Samuels <charles@kde.org>
+
+ ***************************************************************************/
+
+#ifndef KJHELPERS_CPP
+#define KJHELPERS_CPP
+
+static int grayRgb(QRgb r)
+{
+ return qGray(qRed(r), qGreen(r), qBlue(r));
+}
+
+static int isGray(QRgb r)
+{
+// this is more tolerant than the old version
+// i.e. RGB 162 163 162 is treated as gray too
+// too many broken skins around having such colors
+
+// cerr << "r("<<qRed(r)<<","<<qGreen(r)<<","<<qBlue(r)<<")"<<endl;
+
+ if ( (qRed(r)==qGreen(r)) || (qRed(r)+1==qGreen(r)) || (qRed(r)-1==qGreen(r)))
+ {
+ if ( (qRed(r)==qBlue(r)) || (qRed(r)+1==qBlue(r)) || (qRed(r)-1==qBlue(r)))
+ {
+ // looks a bit like gray, so return true
+ return (1);
+ }
+ }
+ // well, it's not gray
+ return(0);
+
+/*
+ // mETz: wrong braces in the code below ??
+ return (qRed(r)==qGreen(r)) && (qRed(r) == qBlue(r));
+*/
+}
+
+// only works little endian
+// UPDATE: should work on both little and big endian now (haven't tested that!)
+// this code is taken from the QT-docu and I hope that this example
+// is one of the working ones ;)
+inline void setPixel1BPP(QImage &image, int x, int y, bool value)
+{
+ if ( image.bitOrder() == QImage::LittleEndian )
+ {
+ if (value)
+ *(image.scanLine(y) + (x >> 3)) |= 1 << (x & 7);
+ else
+ *(image.scanLine(y) + (x >> 3)) &= ~(1 << (x & 7));
+ }
+ else
+ {
+ if (value)
+ *(image.scanLine(y) + (x >> 3)) |= 1 << (7-(x & 7));
+ else
+ *(image.scanLine(y) + (x >> 3)) &= ~(1 << (7-(x & 7)));
+ }
+}
+
+#endif
diff --git a/noatun/modules/kjofol-skin/kjbackground.cpp b/noatun/modules/kjofol-skin/kjbackground.cpp
new file mode 100644
index 00000000..83c19ace
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjbackground.cpp
@@ -0,0 +1,29 @@
+/***************************************************************************
+ kjbackground.cpp
+ --------------------------------------
+ Just draws the main-pixmap of a KJfol-Skin
+ --------------------------------------
+ Maintainer: Stefan Gehn <sgehn@gmx.net>
+
+ ***************************************************************************/
+
+#include "kjbackground.h"
+
+KJBackground::KJBackground(KJLoader *parent)
+ : KJWidget(parent)
+{
+ QImage ibackground;
+
+ mBackground = parent->pixmap(parser()["backgroundimage"][1]);
+ ibackground = parent->image(parser()["backgroundimage"][1]);
+
+ parent->setMask( getMask(ibackground) );
+ parent->setFixedSize ( QSize(mBackground.width(), mBackground.height()) );
+
+ setRect(0,0,parent->width(),parent->height());
+}
+
+void KJBackground::paint(QPainter *painter, const QRect &rect)
+{
+ bitBlt(painter->device(), rect.topLeft(), &mBackground, rect, Qt::CopyROP);
+}
diff --git a/noatun/modules/kjofol-skin/kjbackground.h b/noatun/modules/kjofol-skin/kjbackground.h
new file mode 100644
index 00000000..502611c5
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjbackground.h
@@ -0,0 +1,21 @@
+#ifndef KJBACKGROUND_H
+#define KJBACKGROUND_H
+
+#include "kjwidget.h"
+//#include "kjloader.h"
+class KJLoader;
+
+#include <qpainter.h>
+
+class KJBackground : public KJWidget
+{
+public:
+ KJBackground(KJLoader *);
+ virtual void paint(QPainter *, const QRect &rect);
+ virtual bool mousePress(const QPoint &) {return false;}
+ virtual void mouseRelease(const QPoint &, bool) {}
+
+private:
+ QPixmap mBackground;
+};
+#endif
diff --git a/noatun/modules/kjofol-skin/kjbutton.cpp b/noatun/modules/kjofol-skin/kjbutton.cpp
new file mode 100644
index 00000000..403ea61e
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjbutton.cpp
@@ -0,0 +1,301 @@
+/***************************************************************************
+ kjbutton.cpp
+ --------------------------------------
+ Handles all ordinary Buttons like stop, play, pause, etc.
+ --------------------------------------
+ Maintainer: Stefan Gehn <sgehn@gmx.net>
+
+ ***************************************************************************/
+
+#include "kjbutton.h"
+#include "kjbutton.moc"
+#include "kjloader.h"
+#include "kjprefs.h"
+
+#include <noatun/pref.h>
+#include <noatun/player.h>
+#include <noatun/vequalizer.h>
+
+#include <qcursor.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kpixmap.h>
+#include <kpixmapeffect.h>
+#include <kurl.h>
+#include <kfiledialog.h>
+#include <khelpmenu.h>
+#include <kpopupmenu.h>
+
+/*******************************************
+ * KJButton
+ *******************************************/
+
+KJButton::KJButton(const QStringList &i, KJLoader *parent)
+ : QObject(0), KJWidget(parent), mTitle(i[0]), mShowPressed(false)
+{
+// kdDebug(66666) << k_funcinfo << "new button: " << i[0].latin1() << endl;
+ mPushedPixmap = (i.count() >= 7);
+
+ // get the rectangle
+ int x, y, xs, ys;
+ x=i[1].toInt();
+ y=i[2].toInt();
+ xs=i[3].toInt()-x; // width
+ ys=i[4].toInt()-y; // height
+ setRect ( x, y, xs, ys );
+
+ QStringList temp = i;
+
+ // search for selected button-type
+ // can be either BMPx where x is a number representing one
+ // of the background-images
+ // or darken which means just darken the button on click
+ bool gotBack = false; // in case any of these keys is duplicated
+ for(QStringList::Iterator it = temp.begin(); it != temp.end(); ++it)
+ {
+ if((*it).contains("bmp"))
+ {
+ QString pressedTmp = backgroundPressed((*it));
+ if(!pressedTmp.isEmpty())
+ {
+ mPressed = parent->pixmap(pressedTmp);
+ gotBack = true;
+ }
+ }
+ else if((*it) == "darken")
+ {
+ // take background and darken the buttons rectangle
+ // FIXME: what KPixmapEffect causes the desired effect?
+ // intensity is the wrong one
+ KPixmap temp = parent->pixmap(parser()["backgroundimage"][1]);
+ mPressed = (QPixmap)KPixmapEffect::intensity ( temp, 1.2f );
+ gotBack = true;
+ }
+ if(gotBack)
+ break;
+ }
+
+ if(!gotBack)
+ {
+ kdDebug(66666) << k_funcinfo << "Couldn't find valid background for button '" <<
+ mTitle << "', dafulting to backgroundimage" << endl;
+ mPressed = parent->pixmap(parser()["backgroundimage"][1]);
+ }
+
+ // playlistbutton has to show if playlistwindow is open
+ // right after the button appears on screen
+ if (mTitle=="playlistbutton")
+ {
+ mShowPressed = napp->playlist()->listVisible();
+ connect( napp->player(), SIGNAL(playlistShown()), this, SLOT(slotPlaylistShown()) );
+ connect( napp->player(), SIGNAL(playlistHidden()), this, SLOT(slotPlaylistHidden()) );
+ }
+ else if ( mTitle=="equalizeroffbutton") // same goes for EQ buttons
+ {
+ mShowPressed = (!napp->vequalizer()->isEnabled());
+ connect( napp->vequalizer(), SIGNAL(enabled(bool)), SLOT(slotEqEnabled(bool)));
+ }
+ else if (mTitle=="equalizeronbutton")
+ {
+ mShowPressed = napp->vequalizer()->isEnabled();
+ connect( napp->vequalizer(), SIGNAL(enabled(bool)), SLOT(slotEqEnabled(bool)));
+ }
+}
+
+QString KJButton::tip()
+{
+ QString str;
+ if (mTitle=="closebutton")
+ str=i18n("Close");
+ else if (mTitle=="minimizebutton")
+ str=i18n("Minimize");
+ else if (mTitle=="aboutbutton")
+ str=i18n("About");
+ else if (mTitle=="stopbutton")
+ str=i18n("Stop");
+ else if (mTitle=="playbutton")
+ str=i18n("Play");
+ else if (mTitle=="pausebutton")
+ str=i18n("Pause");
+ else if (mTitle=="openfilebutton")
+ str=i18n("Open");
+ else if (mTitle=="playlistbutton")
+ str=i18n("Playlist");
+ else if (mTitle=="repeatbutton")
+ str=i18n("Loop");
+ else if (mTitle=="equalizerbutton")
+ str=i18n("Show Equalizer Window");
+ else if (mTitle=="equalizeronbutton")
+ str=i18n("Turn on Equalizer");
+ else if (mTitle=="equalizeroffbutton")
+ str=i18n("Turn off Equalizer");
+ else if (mTitle=="equalizerresetbutton")
+ str=i18n("Reset Equalizer");
+ else if (mTitle=="nextsongbutton")
+ str=i18n("Next");
+ else if (mTitle=="previoussongbutton")
+ str=i18n("Previous");
+ else if (mTitle=="forwardbutton")
+ str=i18n("Forward");
+ else if (mTitle=="rewindbutton")
+ str=i18n("Rewind");
+ else if (mTitle=="preferencesbutton")
+ str=i18n("K-Jöfol Preferences");
+ else if (mTitle=="dockmodebutton")
+ str=i18n("Switch to dockmode");
+ else if (mTitle=="undockmodebutton")
+ str=i18n("Return from dockmode");
+
+ return str;
+}
+
+void KJButton::paint(QPainter *, const QRect &)
+{
+ if (mShowPressed)
+ bitBlt(KJWidget::parent(), rect().topLeft(), &mPressed, rect(), Qt::CopyROP);
+}
+
+bool KJButton::mousePress(const QPoint &)
+{
+ bitBlt(KJWidget::parent(), rect().topLeft(), &mPressed, rect(), Qt::CopyROP);
+ return true;
+}
+
+void KJButton::showPressed(bool b)
+{
+ mShowPressed = b;
+ if ( mShowPressed )
+ repaint(true); // repaint with selected image
+ else
+ repaint(false); // repaint with default image (player-background)
+}
+
+void KJButton::slotPlaylistShown(void)
+{
+// kdDebug(66666) << "KJButton::slotPlaylistShown()" << endl;
+ showPressed(true);
+}
+
+void KJButton::slotPlaylistHidden(void)
+{
+// kdDebug(66666) << "KJButton::slotPlaylistHidden()" << endl;
+ showPressed(false);
+}
+
+void KJButton::slotEqEnabled(bool on)
+{
+// kdDebug(66666) << "KJButton::slotEqEnabled(" << on << ") for " << mTitle << endl;
+ if (mTitle=="equalizeronbutton")
+ showPressed(on);
+ else if (mTitle=="equalizeroffbutton")
+ showPressed(!on);
+}
+
+void KJButton::mouseRelease(const QPoint &, bool in)
+{
+ // repaint with default image (player-background)
+ repaint(false);
+
+ if (!in) // only do something if users is still inside the button
+ return;
+
+ // now, find what widget I am and do the proper action
+ if (mTitle=="closebutton")
+ KJWidget::parent()->close();
+ else if (mTitle=="minimizebutton")
+ KJWidget::parent()->minimize();
+ else if (mTitle=="aboutbutton")
+ KJWidget::parent()->helpMenu()->aboutApplication();
+ else if (mTitle=="stopbutton")
+ napp->player()->stop();
+ else if (mTitle=="playbutton")
+ napp->player()->play();
+ else if (mTitle=="pausebutton")
+ napp->player()->playpause();
+ else if (mTitle=="openfilebutton")
+ {
+ KURL file(KFileDialog::getOpenURL(0, napp->mimeTypes(), KJWidget::parent(), i18n("Select File to Play")));
+ if (file.isValid())
+ napp->player()->openFile(file);
+ }
+ else if (mTitle=="playlistbutton")
+ napp->player()->toggleListView();
+ else if (mTitle=="repeatbutton")
+ {
+ KPopupMenu *loopMenu = new KPopupMenu(KJWidget::parent(),"loopMenu");
+ int selectedItem = 0;
+
+ loopMenu->setCheckable(true);
+ loopMenu->insertTitle(i18n("Loop Style"));
+ loopMenu->insertItem(i18n("&None"), static_cast<int>(Player::None));
+ loopMenu->insertItem(i18n("&Song"), static_cast<int>(Player::Song));
+ loopMenu->insertItem(i18n("&Playlist"), static_cast<int>(Player::Playlist));
+ loopMenu->insertItem(i18n("&Random"), static_cast<int>(Player::Random));
+
+ loopMenu->setItemChecked(static_cast<int>(napp->player()->loopStyle()), true); // select current loopstyle in menu
+ selectedItem = loopMenu->exec(QCursor::pos());
+ if (selectedItem != -1)
+ napp->player()->loop(selectedItem); // set new loopstyle
+
+ delete loopMenu;
+
+/*
+ if ( napp->player()->loopStyle() == 1)
+ {
+// kdDebug(66666) << "loop song is OFF" << endl;
+
+// bah, xlib.h already defined None
+#undef None
+ napp->player()->loop( Player::None );
+ showPressed( false );
+ }
+ else
+ {
+// kdDebug(66666) << "loop song is ON" << endl;
+ napp->player()->loop( Player::Song );
+ showPressed ( true );
+ }
+*/
+ }
+ else if (mTitle=="equalizerbutton")
+ {
+ napp->equalizerView();
+ }
+ else if (mTitle=="equalizeronbutton")
+ {
+ if (!napp->vequalizer()->isEnabled())
+ napp->vequalizer()->enable();
+ }
+ else if (mTitle=="equalizeroffbutton")
+ {
+ if (napp->vequalizer()->isEnabled())
+ napp->vequalizer()->disable();
+ }
+ else if (mTitle=="equalizerresetbutton")
+ {
+ for (int band=0; band<napp->vequalizer()->bands(); band++)
+ napp->vequalizer()->band(band).setLevel(0);
+ /*
+ // That preset resets to 6 bands, that's not what we want
+ VPreset set = napp->vequalizer()->presetByName("Zero");
+ if (set) // tests if that preset is valid
+ set.load();
+ */
+ }
+ else if (mTitle=="nextsongbutton")
+ napp->player()->forward();
+ else if (mTitle=="previoussongbutton")
+ napp->player()->back();
+ else if (mTitle=="forwardbutton")
+ napp->player()->skipTo(napp->player()->getTime()+10000); // TODO: make +- 10 secs configurable
+ else if (mTitle=="rewindbutton")
+ napp->player()->skipTo(napp->player()->getTime()-10000);
+ else if (mTitle=="preferencesbutton")
+ napp->preferencesBox()->show(static_cast<CModule *>(KJWidget::parent()->prefs()));
+ else if (mTitle=="dockmodebutton")
+ KJWidget::parent()->switchToDockmode();
+ else if (mTitle=="undockmodebutton")
+ KJWidget::parent()->returnFromDockmode();
+ else
+ kdDebug(66666) << "unknown buttontype: " << mTitle.latin1() << endl;
+}
diff --git a/noatun/modules/kjofol-skin/kjbutton.h b/noatun/modules/kjofol-skin/kjbutton.h
new file mode 100644
index 00000000..4af4fcb0
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjbutton.h
@@ -0,0 +1,34 @@
+#ifndef KJBUTTON_H
+#define KJBUTTON_H
+
+#include "kjwidget.h"
+#include <qobject.h>
+class KJLoader;
+
+class KJButton : public QObject, public KJWidget
+{
+Q_OBJECT
+public:
+ KJButton(const QStringList&, KJLoader *);
+
+ virtual void paint(QPainter *, const QRect &rect);
+ virtual bool mousePress(const QPoint &pos);
+ virtual void mouseRelease(const QPoint &pos, bool);
+ virtual void showPressed(bool b=true);
+
+ virtual QString tip();
+
+private slots:
+ void slotPlaylistShown(void);
+ void slotPlaylistHidden(void);
+ void slotEqEnabled(bool on);
+
+private:
+ QPixmap mBackground;
+ bool mPushedPixmap;
+ QPixmap mPressed;
+ QString mTitle;
+ bool mShowPressed;
+};
+
+#endif
diff --git a/noatun/modules/kjofol-skin/kjequalizer.cpp b/noatun/modules/kjofol-skin/kjequalizer.cpp
new file mode 100644
index 00000000..3f0716e4
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjequalizer.cpp
@@ -0,0 +1,129 @@
+/***************************************************************************
+ kjequalizer.cpp - links noatun VEqualizer and KJofol
+ --------------------------------------
+ Maintainer: Stefan Gehn <sgehn@gmx.net>
+
+ ***************************************************************************/
+
+#include "kjequalizer.h"
+#include "kjequalizer.moc"
+
+#include <qpainter.h>
+#include <qtimer.h>
+
+#include <kdebug.h>
+#include <kpixmap.h>
+
+#include <noatun/vequalizer.h>
+
+KJEqualizer::KJEqualizer(const QStringList &l, KJLoader *p)
+ : QObject(0), KJWidget(p), mBack(0), mView(0), mInterpEq(0)
+{
+ int x=l[1].toInt();
+ int y=l[2].toInt();
+ int xs=l[3].toInt()-x;
+ int ys=l[4].toInt()-y;
+ setRect(x,y,xs,ys);
+
+ mBars = p->pixmap(parser()["equalizerbmp"][3]);
+
+ mBands = l[6].toInt();
+ mXSpace = l[7].toInt();
+
+ // background under equalizer
+ // needed to only blit onto screen ONCE and not for every band
+ QPixmap tmp = p->pixmap(p->item("backgroundimage")[1]);
+ mBack = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mBack, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ // buffer for view
+ mView = new QPixmap ( xs, ys );
+
+ mBandWidth=parser()["EqualizerBmp"][1].toInt();
+ mBandHalfHeight=parser()["EqualizerBmp"][2].toInt();
+
+ kdDebug(66666) << "[KJEqualizer] mBands=" << mBands << ", mXSpace=" << mXSpace << ", mBandWidth=" << mBandWidth << ", mBandHalfHeight=" << mBandHalfHeight << "." << endl;
+
+ kdDebug(66666) << "[KJEqualizer] creating VInterpolation for " << mBands << " bands..." << endl;
+ mInterpEq = new VInterpolation(mBands);
+// napp->vequalizer()->setBands(mBands); // FIXME: hack because spline sucks :P
+ connect(napp->vequalizer(), SIGNAL(changed()), this, SLOT(slotUpdateBuffer()));
+
+ slotUpdateBuffer(); // fill mView pixmap with valid data
+}
+
+KJEqualizer::~KJEqualizer(void)
+{
+ delete mInterpEq;
+ delete mView;
+ delete mBack;
+}
+
+int KJEqualizer::barNum(const QPoint &pos) const
+{
+ int x = pos.x();
+ x = x / mXSpace;
+ return mInterpEq->bands() * x / mBands;
+}
+
+int KJEqualizer::level(const QPoint &pos) const
+{
+ int y = ((-pos.y()) + mBandHalfHeight+1) * (200/mBandHalfHeight);
+ return y;
+}
+
+void KJEqualizer::paint(QPainter *p, const QRect &)
+{
+ QPixmap temp(rect().width(), rect().height());
+ // draw background into buffer
+ bitBlt ( &temp, 0, 0, mBack, 0, 0, -1, -1, Qt::CopyROP );
+ // draw band sliders into buffer
+ bitBlt( &temp, 0, 0, mView, 0, 0, rect().width(), rect().height(), Qt::CopyROP);
+ // and draw it on screen
+ bitBlt(p->device(), rect().topLeft(), &temp, QRect(0,0,-1,-1), Qt::CopyROP);
+}
+
+void KJEqualizer::slotUpdateBuffer()
+{
+// kdDebug(66666) << "[KJEqualizer] slotUpdateBuffer() called." << endl;
+
+ QBitmap regionMask( rect().width(), rect().height(), true); // fully transparent mask
+ QPainter mask( &regionMask );
+
+ QPoint destX = QPoint(0, 0);
+
+ for (int band=0; band<mBands; band++)
+ {
+ int level = mInterpEq->level(band);
+ if (level>200) level=200;
+ if (level<-200) level=-200;
+ int picNum = ((int)(level+200)*(mBandHalfHeight-1) / 400) + 1;
+ int xPos = (picNum * mBandWidth) - mBandWidth;
+
+// kdDebug(66666) << "[KJEqualizer] band=" << band << ", level=" << level << ", picNum=" << picNum << " @ xpos=" << xPos << "." << endl;
+
+ bitBlt(mView, destX, &mBars, QRect(xPos,0,mBandWidth,rect().height()), Qt::CopyROP);
+ // make slider opaque in mask so you see something on screen
+ mask.fillRect ( destX.x(), 0, mBandWidth, rect().height(), Qt::color1 );
+ destX += QPoint(mXSpace,0);
+
+ } // for()
+ // whole thingy has been drawn, now set the mask
+ mView->setMask( regionMask );
+ repaint();
+}
+
+void KJEqualizer::mouseMove(const QPoint &p, bool in)
+{
+ if (!in) return;
+ mousePress(p);
+}
+
+bool KJEqualizer::mousePress(const QPoint &p)
+{
+ kdDebug(66666) << "[KJEqualizer] setting band " << mBands << "/" << barNum(p)+1 << " to level " << level(p) << endl;
+ VBand b = mInterpEq->band( barNum(p) );
+ b.setLevel( level(p) );
+// mouseMove(p, true);
+ return true;
+}
diff --git a/noatun/modules/kjofol-skin/kjequalizer.h b/noatun/modules/kjofol-skin/kjequalizer.h
new file mode 100644
index 00000000..f3f13bd5
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjequalizer.h
@@ -0,0 +1,39 @@
+#ifndef KJEQUALIZER_H
+#define KJEQUALIZER_H
+
+#include "kjwidget.h"
+//#include "kjloader.h"
+class KJLoader;
+class VInterpolation;
+
+#include <qobject.h>
+
+class KJEqualizer : public QObject, public KJWidget
+{
+Q_OBJECT
+public:
+ KJEqualizer(const QStringList &, KJLoader *parent);
+ ~KJEqualizer(void);
+
+ virtual void mouseMove(const QPoint &pos, bool);
+ virtual bool mousePress(const QPoint&);
+ virtual void paint(QPainter *p, const QRect &rect);
+ int barNum(const QPoint &pos) const;
+ int level(const QPoint &pos) const;
+
+public slots:
+ void slotUpdateBuffer();
+
+private:
+ int mBands;
+ int mXSpace;
+
+ int mBandWidth;
+ int mBandHalfHeight;
+ QPixmap mBars; // holds all slider images
+ QPixmap *mBack; // holds background of EQ for easy repaint
+ QPixmap *mView; // holds prepared img of all sliders
+ VInterpolation *mInterpEq;
+};
+
+#endif
diff --git a/noatun/modules/kjofol-skin/kjfont.cpp b/noatun/modules/kjofol-skin/kjfont.cpp
new file mode 100644
index 00000000..df2abed3
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjfont.cpp
@@ -0,0 +1,290 @@
+/***************************************************************************
+ kjfont.cpp
+ --------------------------------------
+ Font-Handling of KJfol
+ Creates pixmaps of strings using the supplied font-pixmap
+ --------------------------------------
+ Maintainer: Stefan Gehn <sgehn@gmx.net>
+
+ ***************************************************************************/
+
+#include "kjfont.h"
+#include "kjloader.h"
+#include "kjwidget.h"
+#include "kjprefs.h"
+
+#include <kdebug.h>
+#include <kglobalsettings.h>
+
+#include <qimage.h>
+#include <qpainter.h>
+
+/*******************************************
+ * KJFont
+ *******************************************/
+
+KJFont::KJFont(const QString &prefix, KJLoader *parent) : mTextMask(0), mTransparentRGB(0)
+{
+// kdDebug(66666) << "KJFont::KJFont(const QString &prefix, KJLoader *parent)" << prefix.latin1() << endl;
+
+ if (prefix=="timefont")
+ {
+ mString[0]="0123456789: ";
+ mString[1]=mString[2]="";
+ mNullChar=' ';
+ }
+ else if ( (prefix=="volumefont") || (prefix=="pitchfont") )
+ {
+ mString[0]="0123456789% ";
+ mString[1]=mString[2]="";
+ mNullChar=' ';
+ }
+ else
+ {
+ mString[0]="abcdefghijklmnopqrstuvwxyz\"@";
+ mString[1]="0123456789;_:()-'!_+\\/[]*&%.=$#";
+ mString[2]="?*, ";
+ mNullChar=' ';
+ }
+
+ mText = parent->pixmap(parent->item(prefix+"image")[1]);
+
+ if ( parent->exist(prefix+"size") )
+ {
+ mWidth = parent->item(prefix+"size")[1].toInt();
+ mHeight = parent->item(prefix+"size")[2].toInt();
+ }
+ else // try to load the font even we are missing important settings
+ { // this still can cause crashes!
+ kdDebug(66666) << "font height/width missing, this skin might crash noatun!" << endl;
+
+ mWidth = mText.width() / strlen(mString[0]);
+
+ if ( (prefix=="timefont") || (prefix=="volumefont") || (prefix=="pitchfont") )
+ mHeight = mText.height();
+ else
+ mHeight = mText.height()/3;
+ }
+
+// kdDebug(66666) << prefix << " h: " << mHeight << " w: " << mWidth << endl;
+
+ // fix for wrong numbers in rc (a skin named steelforged needs that)
+ if ( mHeight > mText.height() )
+ mHeight = mText.height();
+
+ // Stupid Skin authors tend to forget keys :/
+ if ( parent->exist(prefix+"spacing") )
+ mSpacing = parent->item(prefix+"spacing")[1].toInt();
+ else
+ mSpacing = 0; // FIXME: What's default for this in kjfol???
+
+ if ( parent->exist(prefix+"transparent") )
+ mTransparent = (bool)parent->item(prefix+"transparent")[1].toInt();
+ else
+ mTransparent = true; // transparency seems to be default in kjfol
+
+ // define color in font that will be transparent later on
+ if ( mTransparent )
+ {
+ QImage ibackground = mText.convertToImage();
+ mTransparentRGB = ibackground.pixel( ibackground.width()-1, ibackground.height()-1 );
+// kdDebug(66666) << "color (" << qRed(mTransparentRGB) << "," << qGreen(mTransparentRGB) << "," << qBlue(mTransparentRGB) << ") will be transparent for " << prefix.latin1() << endl;
+ mTextMask = KJWidget::getMask(ibackground,mTransparentRGB);
+ }
+
+ mUseSysFont = KJLoader::kjofol->prefs()->useSysFont();
+ sysFontMetrics = 0L;
+ if (mUseSysFont)
+ recalcSysFont();
+}
+
+void KJFont::recalcSysFont(void)
+{
+// kdDebug(66666) << k_funcinfo << "called." << endl;
+
+ mUseSysFont = KJLoader::kjofol->prefs()->useSysFont();
+ if (!mUseSysFont)
+ return;
+ sysFont = QFont(KJLoader::kjofol->prefs()->sysFont());
+ sysFont.setStyleStrategy( QFont::NoAntialias );
+ if ( sysFontMetrics )
+ delete sysFontMetrics;
+ sysFontColor = KJLoader::kjofol->prefs()->sysFontColor();
+
+// kdDebug(66666) << "mHeight=" << mHeight << endl;
+
+ int fSize;
+ for(fSize = mHeight; fSize>=4; fSize--)
+ {
+ sysFont.setPixelSize ( fSize );
+ sysFontMetrics = new QFontMetrics(sysFont);
+// kdDebug(66666) << "wanted fSize=" << fSize << ", metric h=" << sysFontMetrics->height() << endl;
+ // either found a small enough font or found no proper font at all
+ if ( sysFontMetrics->height() <= mHeight || fSize==4 )
+ {
+// kdDebug(66666) << "stopping @ fSize=" << fSize << ", metric h=" << sysFontMetrics->height() << endl;
+ return;
+ }
+ delete sysFontMetrics;
+ }
+}
+
+QPixmap KJFont::draw(const QCString &str, int wide, const QPoint &pos) const
+{
+ if ( mUseSysFont )
+ return drawSysFont(str,wide,pos);
+ else
+ return drawPixmapFont(str,wide,pos);
+}
+
+QPixmap KJFont::drawSysFont(const QCString &s, int wide, const QPoint &pos) const
+{
+// kdDebug(66666) << k_funcinfo << "BEGIN, s='" << s << "'" << endl;
+ QPoint to(pos);
+ QString string(s);
+
+ int stringWidth = sysFontMetrics->width( string );
+// kdDebug(66666) << "final metrics; w=" << stringWidth << ", h=" << sysFontMetrics->height() << endl;
+
+ QPixmap region(
+ (stringWidth > wide ? stringWidth : wide),
+ mHeight);
+ QPainter rp(&region); // region painter
+
+ QBitmap regionMask(
+ (stringWidth > wide ? stringWidth : wide),
+ mHeight, true); // fully transparent mask
+ QPainter mp(&regionMask); // mask painter
+
+// kdDebug(66666) << "region; w=" << region.width() << ", h=" << region.height() << endl;
+
+ int freeSpace=0;
+ // center string into pixmap if its chars won't fill the whole pixmap
+ if ( stringWidth < wide )
+ {
+ freeSpace = wide - stringWidth;
+ mp.fillRect ( to.x(), 0, (freeSpace/2), mHeight, Qt::color0 );
+ to += QPoint ( (freeSpace/2), 0 );
+// kdDebug(66666) << "centering text, freeSpace=" << freeSpace << endl;
+ }
+
+ rp.setFont(sysFont);
+ rp.setPen(sysFontColor);
+ rp.drawText(to.x(), to.y(), region.width()-freeSpace, mHeight, Qt::AlignLeft|Qt::AlignTop, string);
+
+ mp.setFont(sysFont);
+ mp.setPen(Qt::color1);
+ mp.drawText(to.x(), to.y(), region.width()-freeSpace, mHeight, Qt::AlignLeft|Qt::AlignTop, string);
+
+ to += QPoint(region.width()-freeSpace,0);
+// kdDebug(66666) << "text width=" << region.width()-freeSpace << endl;
+
+ if (freeSpace > 0)
+ {
+ mp.fillRect ( to.x(), 0, (freeSpace/2), mHeight, Qt::color0 );
+ to += QPoint ( (freeSpace/2), 0 );
+// kdDebug(66666) << "centering text, freeSpace=" << freeSpace << endl;
+ }
+
+ region.setMask( regionMask );
+// kdDebug(66666) << "width: " << wide << "|end after drawing: " << to.x() << endl;
+// kdDebug(66666) << k_funcinfo << "END" << endl << endl;
+ return region;
+}
+
+QPixmap KJFont::drawPixmapFont(const QCString &str, int wide, const QPoint &pos) const
+{
+// kdDebug(66666) << k_funcinfo << "BEGIN" << endl;
+ QPoint to(pos);
+
+ QCString string = str.lower();
+ QPixmap region(
+ (string.length()*mWidth+string.length()*mSpacing > (unsigned int)wide
+ ? string.length()*mWidth+string.length()*mSpacing : wide),
+ mHeight);
+
+ QBitmap regionMask(
+ (string.length()*mWidth+string.length()*mSpacing > (unsigned int)wide
+ ? string.length()*mWidth+string.length()*mSpacing : wide),
+ mHeight, true); // fully transparent mask
+ QPainter mask( &regionMask );
+
+// kdDebug(66666) << "draw: {" << str << "}" << endl;
+
+ int freeSpace=0;
+ // center string into pixmap if its chars won't fill the whole pixmap
+ if ( string.length()*mWidth+string.length()*mSpacing < (unsigned int)wide )
+ {
+ freeSpace = wide - string.length()*mWidth+string.length()*mSpacing;
+ mask.fillRect ( to.x(), 0, (freeSpace/2), mHeight, Qt::color0 );
+ to += QPoint ( (freeSpace/2), 0 );
+ }
+
+// kdDebug(66666) << k_funcinfo << "pixmap width=" << region.width() << endl;
+
+ // draw every char and add spacing in between these chars if defined
+ unsigned int stringLength(string.length());
+ for ( unsigned int charPos=0; charPos < stringLength; charPos++ )
+ {
+ char c = string[charPos]; // the character to be drawn next
+
+ drawCharacter(&region, &regionMask, to, c);
+ to += QPoint(mWidth, 0);
+
+ // draw according to "spacing"
+ if ( (charPos < string.length()-1) && mSpacing > 0 )
+ { // make the spacing-area transparent
+ mask.fillRect ( to.x(), 0, mSpacing, mHeight, Qt::color0 );
+ to += QPoint ( mSpacing, 0 );
+ }
+ }
+
+ if (freeSpace > 0)
+ {
+ mask.fillRect ( to.x(), 0, (freeSpace/2), mHeight, Qt::color0 );
+ to += QPoint ( (freeSpace/2), 0 );
+ }
+
+ region.setMask( regionMask );
+// kdDebug(66666) << "width: " << wide << "|end after drawing: " << to.x() << endl;
+ return region;
+}
+
+void KJFont::drawCharacter(QPixmap *dev, QBitmap *devMask, const QPoint &to, char c) const
+{
+ QPoint src=charSource(c);
+ int x=src.x();
+ int y=src.y();
+ int xs=mWidth;
+ int ys=mHeight;
+
+ bitBlt(dev, to, &mText, QRect(x,y,xs,ys), Qt::CopyROP);
+
+ // bitBlt mask for transparency
+ if ( mTransparent )
+ {
+ bitBlt(devMask, to, &mTextMask, QRect(x,y,xs,ys), Qt::OrROP);
+ }
+ else // fill mask
+ {
+ QPainter tempPainter (devMask);
+ tempPainter.fillRect ( to.x(), 0, xs,ys, Qt::color1 );
+ }
+}
+
+// needed for strchr
+#include <string.h>
+
+// searches for top/left coordinate of a given character inside the font-pixmap
+QPoint KJFont::charSource(char c) const
+{
+ for (int i=0; i<3; i++)
+ {
+ const char *pos = strchr(mString[i], c);
+
+ if (!pos) continue;
+ return QPoint(mWidth*((int)(pos-mString[i])), mHeight*i);
+ }
+
+ return charSource(mNullChar);
+}
diff --git a/noatun/modules/kjofol-skin/kjfont.h b/noatun/modules/kjofol-skin/kjfont.h
new file mode 100644
index 00000000..4ea5319b
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjfont.h
@@ -0,0 +1,50 @@
+#ifndef KJFONT_H
+#define KJFONT_H
+
+#include <qstring.h>
+#include <qpixmap.h>
+#include <qbitmap.h>
+#include <qfont.h>
+
+class KJLoader;
+
+class KJFont
+{
+public:
+ KJFont(const QString &prefix, KJLoader *parent);
+ // draw the string str to dev at position pos, within rectangle limit in relation to pos
+ QPixmap draw(const QCString &str, int wide, const QPoint &pt=QPoint(0,0)) const;
+ QPixmap draw(const QString &str, int wide, const QPoint &pt=QPoint(0,0)) const
+ { return draw(QCString(str.latin1()), wide, pt); }
+
+ int fontHeight() const {return mHeight;}
+ int fontWidth() const {return mWidth;}
+ int fontSpacing() const {return mSpacing;}
+ bool isTransparent() const {return mTransparent;}
+
+ // !!! Call if you changed the systemfont !!!
+ void recalcSysFont(void);
+
+protected:
+ QPixmap drawSysFont(const QCString &s, int wide, const QPoint &pos=QPoint(0,0)) const;
+ QPixmap drawPixmapFont(const QCString &, int, const QPoint &pos=QPoint(0,0)) const;
+
+ void drawCharacter(QPixmap *dev, QBitmap *devMask, const QPoint &to, char c) const;
+ QPoint charSource(char c) const;
+
+private:
+ QPixmap mText;
+ QBitmap mTextMask;
+ QRgb mTransparentRGB; // this color will be transparent
+ int mSpacing;
+ int mWidth, mHeight;
+ bool mTransparent; // indicates wether there's transparency
+ const char *mString[3];
+ char mNullChar;
+ QFontMetrics *sysFontMetrics;
+ QFont sysFont;
+ QColor sysFontColor;
+ bool mUseSysFont;
+};
+
+#endif
diff --git a/noatun/modules/kjofol-skin/kjguisettingswidget.ui b/noatun/modules/kjofol-skin/kjguisettingswidget.ui
new file mode 100644
index 00000000..e2b4f784
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjguisettingswidget.ui
@@ -0,0 +1,465 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>KJGuiSettings</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KJGuiSettings</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>690</width>
+ <height>454</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>Layout7</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>ButtonGroup1</cstring>
+ </property>
+ <property name="title">
+ <string>Visualization</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>visScope</cstring>
+ </property>
+ <property name="text">
+ <string>Oscillo&amp;scope</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>visAnalyzer</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Analyzer</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>visNone</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;None</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1_3</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>U&amp;pdate every:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>visTimerValue</cstring>
+ </property>
+ </widget>
+ <widget class="KIntNumInput">
+ <property name="name">
+ <cstring>visTimerValue</cstring>
+ </property>
+ <property name="value">
+ <number>30</number>
+ </property>
+ <property name="minValue">
+ <number>20</number>
+ </property>
+ <property name="maxValue">
+ <number>1000</number>
+ </property>
+ <property name="suffix">
+ <string>ms</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>GroupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Pitch</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Lower limit:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>minPitch</cstring>
+ </property>
+ </widget>
+ <widget class="KIntNumInput">
+ <property name="name">
+ <cstring>minPitch</cstring>
+ </property>
+ <property name="value">
+ <number>50</number>
+ </property>
+ <property name="minValue">
+ <number>50</number>
+ </property>
+ <property name="maxValue">
+ <number>98</number>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1_2_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Upper limit:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>maxPitch</cstring>
+ </property>
+ </widget>
+ <widget class="KIntNumInput">
+ <property name="name">
+ <cstring>maxPitch</cstring>
+ </property>
+ <property name="value">
+ <number>200</number>
+ </property>
+ <property name="minValue">
+ <number>102</number>
+ </property>
+ <property name="maxValue">
+ <number>200</number>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>displayTooltips</cstring>
+ </property>
+ <property name="text">
+ <string>Display &amp;tooltips</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>displaySplash</cstring>
+ </property>
+ <property name="text">
+ <string>Display splash sc&amp;reen</string>
+ </property>
+ </widget>
+ <spacer row="5" column="0">
+ <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>60</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget" row="4" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>Layout2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>T&amp;itle display scrolling speed:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>titleScrollSpeed</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Slow</string>
+ </property>
+ </widget>
+ <widget class="QSlider">
+ <property name="name">
+ <cstring>titleScrollSpeed</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="maxValue">
+ <number>3</number>
+ </property>
+ <property name="pageStep">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>2</number>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="tickmarks">
+ <enum>NoMarks</enum>
+ </property>
+ <property name="tickInterval">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Fast</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox" row="3" column="0">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>System Font</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KColorCombo" row="2" column="1">
+ <property name="name">
+ <cstring>cmbSysFontColor</cstring>
+ </property>
+ <property name="color">
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>txtFontColor</cstring>
+ </property>
+ <property name="text">
+ <string>Color:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>txtFont</cstring>
+ </property>
+ <property name="text">
+ <string>Font:</string>
+ </property>
+ </widget>
+ <widget class="KFontCombo" row="1" column="1">
+ <property name="name">
+ <cstring>cmbSysFont</cstring>
+ </property>
+ <property name="urlDropsEnabled" stdset="0">
+ <bool>false</bool>
+ </property>
+ <property name="editable" stdset="0">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>useSysFont</cstring>
+ </property>
+ <property name="text">
+ <string>Use system font</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer row="3" column="1">
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>140</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<tabstops>
+ <tabstop>visScope</tabstop>
+ <tabstop>visAnalyzer</tabstop>
+ <tabstop>visNone</tabstop>
+ <tabstop>visTimerValue</tabstop>
+ <tabstop>minPitch</tabstop>
+ <tabstop>maxPitch</tabstop>
+ <tabstop>displayTooltips</tabstop>
+ <tabstop>displaySplash</tabstop>
+ <tabstop>useSysFont</tabstop>
+ <tabstop>cmbSysFont</tabstop>
+ <tabstop>cmbSysFontColor</tabstop>
+ <tabstop>titleScrollSpeed</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>kcolorcombo.h</includehint>
+ <includehint>kfontcombo.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/noatun/modules/kjofol-skin/kjloader.cpp b/noatun/modules/kjofol-skin/kjloader.cpp
new file mode 100644
index 00000000..11f96b7a
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjloader.cpp
@@ -0,0 +1,832 @@
+/***************************************************************************
+ kjloader.cpp - The KJfol-GUI itself
+ --------------------------------------
+ Maintainer: Stefan Gehn <sgehn@gmx.net>
+
+ ***************************************************************************/
+
+// local includes
+#include "kjloader.h"
+#include "kjloader.moc"
+#include "kjwidget.h"
+#include "kjbackground.h"
+#include "kjbutton.h"
+#include "kjfont.h"
+#include "kjseeker.h"
+#include "kjsliders.h"
+#include "kjtextdisplay.h"
+#include "kjvis.h"
+#include "kjprefs.h"
+#include "kjequalizer.h"
+
+#include "helpers.cpp"
+
+// arts-includes, needed for pitch
+#include <artsmodules.h>
+#include <reference.h>
+#include <soundserver.h>
+#include <kmedia2.h>
+
+// noatun-specific includes
+#include <noatun/engine.h>
+#include <noatunarts/noatunarts.h>
+#include <noatun/stdaction.h>
+#include <noatun/app.h>
+#include <noatun/player.h>
+#include <noatun/vequalizer.h>
+
+// system includes
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <qdragobject.h>
+#include <qimage.h>
+#include <qbitmap.h>
+#include <qpixmap.h>
+#include <qcursor.h>
+#include <qpainter.h>
+#include <qtooltip.h>
+#include <qptrvector.h>
+#include <qvbox.h>
+#include <qlabel.h>
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <kfiledialog.h>
+#include <khelpmenu.h>
+#include <kstdaction.h>
+#include <kpopupmenu.h>
+#include <klocale.h>
+#include <kglobalsettings.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <knotifyclient.h>
+#include <kpixmapeffect.h>
+#include <kurldrag.h>
+
+#include <kwin.h>
+#include <kiconloader.h>
+
+class KJToolTip : public QToolTip
+{
+public:
+ KJToolTip(KJLoader *parent)
+ : QToolTip(parent), mParent(parent)
+ {}
+
+protected:
+ virtual void maybeTip(const QPoint &p)
+ {
+ if ( !mParent->prefs()->displayTooltips() )
+ return;
+
+ QPtrList<KJWidget> things=mParent->widgetsAt(p);
+ for (KJWidget *i=things.first(); i!=0; i=things.next())
+ {
+ QString string=i->tip();
+ if (string.length())
+ {
+ tip(i->rect(), string);
+ return;
+ }
+ }
+ }
+
+private:
+ KJLoader *mParent;
+};
+
+
+KJLoader *KJLoader::kjofol=0;
+
+
+KJLoader::KJLoader()
+ : QWidget(0, "NoatunKJLoader",
+ WType_TopLevel | WStyle_NoBorder | WRepaintNoErase ),
+ UserInterface(),
+ moving(false),
+ mClickedIn(0),
+ mText(0),
+ mNumbers(0),
+ mVolumeFont(0),
+ mPitchFont(0),
+ splashScreen(0)
+{
+ kjofol = this;
+
+ mTooltips = new KJToolTip(this);
+
+ // Windowname and Icon
+ setCaption(i18n("Noatun"));
+ setIcon(SmallIcon("noatun"));
+ setAcceptDrops(true);
+
+ // We're going to draw over everything, there is no point in drawing the grey background first
+ setBackgroundMode(NoBackground);
+
+ // used for dockmode
+ mWin = new KWinModule();
+
+ subwidgets.setAutoDelete(true);
+
+ mPrefs = new KJPrefs(this);
+ connect ( mPrefs, SIGNAL(configChanged()), this, SLOT(readConfig()) );
+
+ QString skin = mPrefs->skin();
+ if ( QFile(skin).exists() )
+ {
+ loadSkin(skin);
+ }
+ else
+ {
+ KNotifyClient::event(winId(), "warning",
+ i18n("There was trouble loading skin %1. Please select another skin file.").arg(skin));
+ napp->preferences();
+ }
+
+ mHelpMenu = new KHelpMenu(this, kapp->aboutData());
+ connect(napp->player(), SIGNAL(timeout()), SLOT(timeUpdate()));
+ connect(napp->player(), SIGNAL(stopped()), SLOT(timeUpdate()));
+ connect(napp->player(), SIGNAL(newSong()), SLOT(newSong()));
+
+ connect(napp, SIGNAL(hideYourself()), SLOT(hide()));
+ connect(napp, SIGNAL(showYourself()), SLOT(show()));
+// KStdAction::quit(napp, SLOT(quit()), actionCollection());
+
+ QApplication::restoreOverrideCursor();
+// newSong();
+}
+
+QPtrList<KJWidget> KJLoader::widgetsAt(const QPoint &pt) const
+{
+ QPtrList<KJWidget> things;
+ for ( QPtrListIterator<KJWidget> i(subwidgets); i.current(); ++i )
+ if ( (*i)->rect().contains(pt) )
+ things.append((*i));
+
+ return things;
+}
+
+void KJLoader::removeChild(KJWidget *c)
+{
+ if ( mClickedIn == c )
+ mClickedIn = 0;
+ if (subwidgets.findRef(c) != -1)
+ subwidgets.take();
+}
+
+void KJLoader::addChild(KJWidget *c)
+{
+ subwidgets.append(c);
+}
+
+// The BIG one ;)
+// this methode does all the hard work on loading these weird skins
+void KJLoader::loadSkin(const QString &file)
+{
+// kdDebug(66666) << "<KJLoader::loadSkin(const QString &file)>" << endl;
+// kdDebug(66666) << " file = " << file.latin1() << endl;
+
+ if ( file == mCurrentSkin ) // we don't load the same skin again
+ return;
+
+ mCurrentSkin = file;
+
+ // don't overwrite path to *.rc when we are loading dock- or winshade-mode
+ if ( (file != mCurrentWinshadeModeSkin) && (file != mCurrentDockModeSkin) )
+ {
+ mCurrentDefaultSkin = file;
+// kdDebug(66666) << " setting mCurrentDefaultSkin: '" << file.latin1() << "'" << endl;
+ }
+ unloadSkin();
+
+ Parser::open( filenameNoCase(file) );
+
+ KJPitchText *pitchText=0;
+ KJVolumeText *volumeText=0;
+ mText = 0;
+ mNumbers = 0;
+ mVolumeFont = 0;
+ mPitchFont = 0;
+
+ if ( exist("splashscreen") && mPrefs->displaySplash() )
+ showSplash();
+
+ if ( (file != mCurrentWinshadeModeSkin) && (file != mCurrentDockModeSkin) )
+ {
+ if ( exist("dockmodercfile") )
+ {
+ // set path to dockmode rc-file (its not always skinname.dck)
+ mCurrentDockModeSkin = file.left(file.findRev("/")+1) + (item("dockmodercfile")[1]);
+ mDockPosition = item("dockmodeposition")[1].toInt();
+ mDockPositionX = item("dockmodepositionxy")[1].toInt();
+ mDockPositionY = item("dockmodepositionxy")[2].toInt();
+ }
+ else // NO DockMode
+ mCurrentDockModeSkin="";
+
+ if ( exist("winshademodercfile") )
+ mCurrentWinshadeModeSkin = file.left(file.findRev("/")+1) + (item("winshademodercfile")[1]);
+ else // no WinshadeMode
+ mCurrentWinshadeModeSkin="";
+ }
+
+ // Font-loading
+ if ( exist("fontimage") )
+ mText = new KJFont("font", this);
+
+ if ( exist("timefontimage") )
+ mNumbers = new KJFont("timefont", this);
+
+ if (exist("volumefontimage"))
+ mVolumeFont = new KJFont("volumefont", this);
+
+ // our skin-background, There has to be one so no check with exist()
+ subwidgets.append( new KJBackground(this) );
+
+ if ( exist("pitchtext") )
+ {
+ if (exist("pitchfontimage"))
+ {
+ mPitchFont = new KJFont("pitchfont", this);
+ }
+ else
+ {
+ mPitchFont = mNumbers;
+ kdDebug(66666) << "no pitchfont but pitchtext!" << endl;
+ kdDebug(66666) << "replacing pitchfont with timefont" << endl;
+ }
+ subwidgets.append( pitchText=new KJPitchText(item("pitchtext"), this) );
+ }
+
+ if (exist("volumetext"))
+ subwidgets.append(volumeText=new KJVolumeText(item("volumetext"), this));
+
+ if ( exist("volumecontroltype") )
+ {
+ if ( item("volumecontroltype")[1] == "bmp" )
+ {
+ KJVolumeBMP *b;
+ subwidgets.append(b=new KJVolumeBMP(item("volumecontrolbutton"), this));
+ b->setText(volumeText);
+ }
+ else if ( item("volumecontroltype")[1] == "bar" )
+ {
+ KJVolumeBar *b;
+ subwidgets.append(b=new KJVolumeBar(item("volumecontrolbutton"), this));
+ b->setText(volumeText);
+ }
+/* else
+ {
+ kdDebug(66666) << "unknown volumecontrol: " << item("volumecontroltype")[1].latin1() << endl;
+ } */
+ }
+ else
+ {
+ kdDebug(66666) << "guessing type of volumecontrol" << endl;
+ if (exist("volumecontrolbutton") &&
+ exist("volumecontrolimage") &&
+ exist("volumecontrolimagexsize") &&
+ exist("volumecontrolimageposition") &&
+ exist("volumecontrolimagenb") )
+ {
+ KJVolumeBMP *b;
+ subwidgets.append(b=new KJVolumeBMP(item("volumecontrolbutton"), this));
+ b->setText(volumeText);
+ }
+ else if (exist("volumecontrolimage") &&
+ exist("volumecontrolbutton") )
+ {
+ KJVolumeBar *b;
+ subwidgets.append(b=new KJVolumeBar(item("volumecontrolbutton"), this));
+ b->setText(volumeText);
+ }
+/* else
+ {
+ kdDebug(66666) << " no volumecontrol" << endl;
+ } */
+ }
+
+ if (exist("pitchcontrolbutton") &&
+ exist("pitchcontrolimage") &&
+ exist("pitchcontrolimagexsize") &&
+ exist("pitchcontrolimageposition") &&
+ exist("pitchcontrolimagenb") )
+ {
+// kdDebug(66666) << "added KJPitchBMP" << endl;
+ KJPitchBMP *b;
+ subwidgets.append(b=new KJPitchBMP(item("pitchcontrolbutton"), this));
+ b->setText(pitchText);
+ }
+ else
+ {
+ // make sure we reset speed to 100% as the user won't be able
+ // to reset it without a pitchcontrol
+ Arts::PlayObject playobject = napp->player()->engine()->playObject();
+ Arts::PitchablePlayObject pitchable = Arts::DynamicCast(playobject);
+
+ if ( !pitchable.isNull() )
+ {
+ if ( pitchable.speed() > 1.0f )
+ pitchable.speed(1.0f);
+ }
+ }
+
+ if (exist("filenamewindow"))
+ subwidgets.append(new KJFilename(item("filenamewindow"), this));
+
+ if (exist("mp3timewindow"))
+ subwidgets.append(new KJTime(item("mp3timewindow"), this));
+
+ if (exist("mp3kbpswindow"))
+ subwidgets.append(new KJFileInfo(item("mp3kbpswindow"), this));
+
+ if (exist("mp3khzwindow"))
+ subwidgets.append(new KJFileInfo(item("mp3khzwindow"), this));
+
+ if (exist("analyzerwindow"))
+ {
+ int vistype = mPrefs->visType();
+ switch ( vistype )
+ {
+ case KJVisScope::Null:
+ subwidgets.append(new KJNullScope(item("analyzerwindow"), this));
+ break;
+ case KJVisScope::FFT:
+ subwidgets.append(new KJFFT(item("analyzerwindow"), this));
+ break;
+ case KJVisScope::StereoFFT:
+ subwidgets.append(new KJStereoFFT(item("analyzerwindow"), this));
+ break;
+ case KJVisScope::Mono:
+ subwidgets.append(new KJScope(item("analyzerwindow"), this));
+ break;
+ }
+ }
+
+ if (exist("EqualizerWindow"))
+ subwidgets.append(new KJEqualizer(item("EqualizerWindow"), this));
+
+ // I cant believe it, there are skins without a seeker, now THATS stupid :)
+ if (exist("seekregion"))
+ QTimer::singleShot(0, this, SLOT(loadSeeker()));
+
+ // all the regular buttons
+ for (QDictIterator<QStringList> i(*this); i.current(); ++i)
+ {
+ QString d=i.currentKey();
+ if(d.contains("button") &&
+ !d.startsWith("playlistwindow") && // don't add buttons that belong to the playlistwindow
+ d != "pitchcontrolbutton" && // both already handled above as they aren't buttons but sliders
+ d != "volumecontrolbutton" &&
+ d != "spectrumanalyzerbutton" && // FIXME: unsupported button
+ d != "oscilloscopebutton" && // FIXME: unsupported button
+ i.count() >= 7 )
+ {
+ subwidgets.append(new KJButton(*(*i), this));
+ }
+ }
+
+ show();
+ conserveMemory();
+
+ repaint();
+
+ // update displays if we are already playing
+ // This happens while changing skins
+ if (napp->player()->isPlaying())
+ newSong();
+
+// kdDebug(66666) << "</KJLoader::loadSkin(const QString &file)>" << endl;
+}
+
+void KJLoader::loadSeeker()
+{
+ subwidgets.append(new KJSeeker(item("seekregion"), this));
+}
+
+void KJLoader::unloadSkin()
+{
+// kdDebug(66666) << "<KJLoader::unloadSkin()>" << endl;
+
+ KWin::clearState(winId(), NET::SkipTaskbar);
+
+// kdDebug(66666) << " freeing subwidgets" << endl;
+ subwidgets.clear();
+
+ // This is special because mPitchfont can also point to mNumbers
+ // as some skins use the NumberFont for pitchtext
+ if ( mPitchFont && mPitchFont != mNumbers )
+ {
+// kdDebug(66666) << " freeing mPitchFont" << endl;
+ delete mPitchFont;
+ }
+
+ if ( mText )
+ {
+// kdDebug(66666) << " freeing mText" << endl;
+ delete mText;
+ }
+
+ if ( mNumbers )
+ {
+// kdDebug(66666) << " freeing mNumbers" << endl;
+ delete mNumbers;
+ }
+
+ if ( mVolumeFont )
+ {
+// kdDebug(66666) << " freeing mVolumeFont" << endl;
+ delete mVolumeFont;
+ }
+
+// kdDebug(66666) << "</KJLoader::unloadSkin()>" << endl;
+}
+
+void KJLoader::minimize()
+{
+// kdDebug(66666) << "KJLoader::minimize()" << endl;
+ showMinimized();
+}
+
+void KJLoader::closeEvent(QCloseEvent*)
+{
+// kdDebug(66666) << "KJLoader::closeEvent(QCloseEvent*)" << endl;
+ unload();
+}
+
+void KJLoader::dragEnterEvent(QDragEnterEvent *event)
+{
+ // accept uri drops only
+ event->accept(KURLDrag::canDecode(event));
+}
+
+void KJLoader::dropEvent(QDropEvent *event)
+{
+ KURL::List urls;
+ if ( KURLDrag::decode(event,urls) )
+ {
+ for ( KURL::List::iterator it = urls.begin(); it != urls.end(); ++it )
+ napp->player()->openFile((*it), false);
+ }
+}
+
+void KJLoader::wheelEvent(QWheelEvent *e)
+{ // from QT-Docu: delta() is 120 for one step
+ if (e->state() & ControlButton)
+ napp->player()->setVolume ( napp->player()->volume() + (e->delta()/8) ); // 15% volumechange
+ else
+ napp->player()->setVolume ( napp->player()->volume() + (e->delta()/24) ); // 5% volumechange
+}
+
+// now for some dockmode stuff
+void KJLoader::switchToDockmode()
+{
+// kdDebug(66666) << "KJLoader::switchToDockmode()" << endl;
+ loadSkin( mCurrentDockModeSkin );
+
+ connect(mWin, SIGNAL(activeWindowChanged(WId)), this, SLOT(slotWindowActivate(WId)));
+ connect(mWin, SIGNAL(windowRemoved(WId)), this, SLOT(slotWindowRemove(WId)));
+ connect(mWin, SIGNAL(stackingOrderChanged()), this, SLOT(slotStackingChanged()));
+ connect(mWin, SIGNAL(windowChanged(WId)), this, SLOT(slotWindowChange(WId)));
+ connect(mWin, SIGNAL(currentDesktopChanged(int)), this, SLOT(slotDesktopChange(int)));
+
+ WId activeWin = mWin->activeWindow();
+ if (activeWin && (activeWin != winId()))
+ {
+ KWin::WindowInfo winInf = KWin::windowInfo(activeWin, NET::WMKDEFrameStrut);
+ if(winInf.valid())
+ {
+ mDockToWin = activeWin;
+ mDockWindowRect = winInf.frameGeometry();
+ slotWindowActivate(mDockToWin);
+ hide();
+ restack();
+ }
+ }
+}
+
+void KJLoader::returnFromDockmode()
+{
+// kdDebug(66666) << "KJLoader::returnFromDockmode()" << endl;
+ mWin->disconnect();
+ loadSkin(mCurrentDefaultSkin);
+}
+
+void KJLoader::slotWindowActivate(WId win)
+{
+ if(mCurrentSkin != mCurrentDockModeSkin)
+ return;
+
+ KWin::WindowInfo winInf = KWin::windowInfo(
+ win, NET::WMWindowType);
+ if((win != winId()) && winInf.valid())
+ {
+ // ensure we dock to the active window _except_ our own
+ // and stick to the last window if the NEW current one is a desktop
+ NET::WindowType winType = winInf.windowType(
+ NET::NormalMask|NET::DesktopMask|NET::DockMask|
+ NET::ToolbarMask|NET::MenuMask|NET::DialogMask|
+ NET::OverrideMask|NET::TopMenuMask|NET::UtilityMask|
+ NET::SplashMask);
+
+ if(winType == NET::Unknown || winType == NET::Normal || winType == NET::Dialog)
+ {
+ //kdDebug(66666) << k_funcinfo << "Now docking to window: " << win << endl;
+ mDockToWin = win;
+ }
+
+ }
+
+ if(mDockToWin != 0)
+ {
+ mDockWindowRect = KWin::windowInfo(mDockToWin, NET::WMKDEFrameStrut).frameGeometry();
+ /*kdDebug(66666) << k_funcinfo << "winrect: " << mDockWindowRect.x() << ", " <<
+ mDockWindowRect.y() << endl;*/
+ switch ( mDockPosition )
+ {
+ case 0:
+ move( mDockWindowRect.x() + mDockPositionX, mDockWindowRect.y() + mDockPositionY );
+ break;
+ case 2:
+ move( mDockWindowRect.x() + mDockPositionX, mDockWindowRect.y() + mDockWindowRect.height() + mDockPositionY );
+ break;
+ }
+
+ if(!isVisible())
+ {
+ show();
+ KWin::setState(winId(), NET::SkipTaskbar);
+ }
+ restack();
+ }
+ else
+ {
+ // We don't want to do anything until a window comes into
+ // focus.
+ //kdDebug(66666) << "No window having focus, hiding" << endl;
+ hide();
+ }
+
+// kdDebug(66666) << "END slotWindowActivate()" << endl;
+}
+
+void KJLoader::slotWindowRemove(WId win)
+{
+// kdDebug(66666) << "START slotWindowRemove()" << endl;
+ if ( mCurrentSkin != mCurrentDockModeSkin )
+ return;
+
+ if (win == mDockToWin)
+ {
+// kdDebug(66666) << "our window removed: " << win << endl;
+ hide();
+ mDockToWin = 0;
+ }
+// kdDebug(66666) << "END slotWindowRemove()" << endl;
+}
+
+void KJLoader::slotWindowChange(WId win)
+{
+// kdDebug(66666) << "START slotWindowChange()" << endl;
+ if ( mCurrentSkin != mCurrentDockModeSkin )
+ return;
+
+ if ( win == mDockToWin )
+ {
+// kdDebug(66666) << "changed our window:" << win << endl;
+ KWin::WindowInfo winInf = KWin::windowInfo(
+ mDockToWin, NET::WMKDEFrameStrut|NET::WMWindowType|
+ NET::WMState|NET::XAWMState|NET::WMDesktop);
+
+ if(!winInf.valid())
+ {
+ /*kdDebug(66666) << k_funcinfo <<
+ "No valid WindowInfo for tracked window: " << win << endl;*/
+ hide();
+ mDockToWin = 0;
+ return;
+ }
+
+ NET::WindowType winType = winInf.windowType(
+ NET::NormalMask|NET::DesktopMask|NET::DockMask|
+ NET::ToolbarMask|NET::MenuMask|NET::DialogMask|
+ NET::OverrideMask|NET::TopMenuMask|NET::UtilityMask|
+ NET::SplashMask);
+
+ if (
+ (winInf.state() & NET::Hidden) ||
+ (winInf.state() & NET::FullScreen) ||
+ (winType != NET::Unknown && winType != NET::Normal && winType != NET::Dialog)
+ )
+ {
+ /*kdDebug(66666) << k_funcinfo <<
+ "Our window changed: " << win <<
+ ". Either iconified or special window" << endl;*/
+ // target-window has been iconified or window is desktop
+ hide();
+ mDockToWin = 0;
+ return;
+ }
+
+ // Size or position of target-window changed.
+ mDockWindowRect = winInf.frameGeometry();
+ /*kdDebug(66666) << k_funcinfo << "winrect: " << mDockWindowRect.x() << ", " <<
+ mDockWindowRect.y() << endl;*/
+ // Ensure we are still on the window.
+ switch(mDockPosition)
+ {
+ case 0:
+ {
+ move(
+ mDockWindowRect.x() + mDockPositionX,
+ mDockWindowRect.y() + mDockPositionY);
+ break;
+ }
+ case 2:
+ {
+ move(
+ mDockWindowRect.x() + mDockPositionX,
+ mDockWindowRect.y() + mDockWindowRect.height() + mDockPositionY);
+ break;
+ }
+ }
+ restack();
+ }
+}
+
+void KJLoader::slotDesktopChange(int)
+{
+// kdDebug(66666) << "START slotDesktopChange()" << endl;
+ if ( mCurrentSkin != mCurrentDockModeSkin )
+ return;
+
+ hide();
+ mDockToWin = 0L;
+// kdDebug(66666) << "END slotDesktopChange()" << endl;
+}
+
+void KJLoader::slotStackingChanged()
+{
+// kdDebug(66666) << "START slotStackingChanged()" << endl;
+ if ( mCurrentSkin != mCurrentDockModeSkin )
+ return;
+
+ // We seem to get this signal before the window has been restacked,
+ // so we just schedule a restack.
+ QTimer::singleShot ( 10, this, SLOT(restack()) );
+
+// kdDebug(66666) << "END slotStackingChanged()" << endl;
+}
+
+// Set the animation's stacking order to be just above the target window's
+// window decoration, or on top.
+void KJLoader::restack()
+{
+// kdDebug(66666) << "START restack()" << endl;
+
+ if ( !mDockToWin )
+ {
+// kdDebug(66666) << "No window to dock to, no restacking" << endl;
+ hide();
+ return;
+ }
+
+ // simply raise ourselves to the top
+ raise();
+ // and then ensure our target-window gets focus
+// NET::setActiveWindow (mDockToWin);
+
+// kdDebug(66666) << "END restack()" << endl;
+}
+
+KJLoader::~KJLoader()
+{
+// kdDebug(66666) << "KJLoader::~KJLoader()" << endl;
+ delete mWin;
+}
+
+void KJLoader::paintEvent(QPaintEvent *e)
+{
+ QPainter p(this);
+ for (KJWidget* i=subwidgets.first(); i!=0; i=subwidgets.next())
+ if (i->rect().intersects(e->rect()))
+ i->paint(&p, e->rect().intersect(i->rect()));
+// QWidget::paintEvent(e);
+}
+
+void KJLoader::mouseMoveEvent(QMouseEvent *e)
+{
+ if (moving)
+ {
+ move ( QCursor::pos()-mMousePoint );
+ return;
+ }
+
+
+// QWidget::mouseMoveEvent(e);
+ // not on background but on a widget: pass event to subwidget
+ if ( !moving && mClickedIn && subwidgets.findRef(mClickedIn) != -1 )
+ {
+ mClickedIn->mouseMove (
+ e->pos()-mClickedIn->rect().topLeft(),
+ mClickedIn->rect().contains(mapFromGlobal(QCursor::pos())) );
+ }
+}
+
+void KJLoader::mousePressEvent(QMouseEvent *e)
+{
+// kdDebug(66666) << "KJLoader::mousePressEvent(QMouseEvent *e)" << endl;
+
+// QWidget::mousePressEvent(e);
+
+ if ( e->button()==RightButton )
+ NoatunStdAction::ContextMenu::showContextMenu();
+ else /* if ( e->button()==LeftButton ) */
+ {
+ mMousePoint = mapFromGlobal(QCursor::pos());
+ // try to find a KJWidget that is here
+ for (KJWidget* i=subwidgets.first(); i!=0; i=subwidgets.next())
+ if (i->rect().contains(mMousePoint))
+ {
+ if (i->mousePress(mMousePoint-i->rect().topLeft()))
+ {
+ mClickedIn=i;
+ return;
+ }
+ }
+ // can't find a widget, so move the window
+ if ( mCurrentSkin != mCurrentDockModeSkin)
+ moving = true;
+ }
+}
+
+void KJLoader::mouseReleaseEvent(QMouseEvent */*e*/)
+{
+// kdDebug(66666) << "KJLoader::mouseReleaseEvent(QMouseEvent *e)" << endl;
+
+// QWidget::mouseReleaseEvent(e);
+
+ if (!moving && mClickedIn && subwidgets.findRef(mClickedIn)!=-1)
+ {
+ mClickedIn->mouseRelease(mapFromGlobal(QCursor::pos())-
+ mClickedIn->rect().topLeft(),
+ mClickedIn->rect().contains(
+ mapFromGlobal(QCursor::pos())));
+ mClickedIn=0;
+ }
+
+ moving = false;
+}
+
+void KJLoader::timeUpdate()
+{
+ for (KJWidget* widget=subwidgets.first(); widget; widget=subwidgets.next())
+ widget->timeUpdate(napp->player()->getTime()/1000); // pass seconds to all Widgets
+}
+
+void KJLoader::newSong()
+{
+ if (!napp->player()->current())
+ return;
+ for ( KJWidget* i=subwidgets.first(); i!=0; i=subwidgets.next() )
+ i->newFile();
+}
+
+void KJLoader::readConfig()
+{
+// kdDebug(66666) << "KJLoader::readConfig()" << endl;
+ for (KJWidget* i=subwidgets.first(); i!=0; i=subwidgets.next())
+ i->readConfig();
+}
+
+void KJLoader::showSplash()
+{
+ splashScreen = new QLabel( 0L, "SplashScreen",
+ WType_TopLevel | WStyle_NoBorder | WRepaintNoErase | WX11BypassWM );
+
+ QPixmap splashPix = pixmap(item("splashscreen")[1]);
+ splashScreen->setPixmap( splashPix );
+ splashScreen->setBackgroundMode ( NoBackground );
+ splashScreen->setMask( KJWidget::getMask(image(item("splashscreen")[1])) );
+
+ QSize sh = splashScreen->sizeHint();
+
+ QRect desk = KGlobalSettings::splashScreenDesktopGeometry();
+ splashScreen->move (desk.x() + (desk.width() - sh.width())/2,
+ desk.y() + (desk.height() - sh.height())/2 );
+
+ splashScreen->setFixedSize(sh);
+ splashScreen->show();
+ napp->processEvents(); // we want this one time to get the splash actually displayed ASAP
+
+ QTimer::singleShot(3000, this, SLOT(hideSplash()) );
+}
+
+void KJLoader::hideSplash()
+{
+ splashScreen->hide();
+ delete splashScreen;
+}
diff --git a/noatun/modules/kjofol-skin/kjloader.h b/noatun/modules/kjofol-skin/kjloader.h
new file mode 100644
index 00000000..44b507d6
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjloader.h
@@ -0,0 +1,129 @@
+#ifndef KJLOADER_H
+#define KJLOADER_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+// local includes
+#include "parser.h"
+
+// noatun-specific includes
+#include <noatun/plugin.h>
+#include <noatun/app.h>
+
+// system includes
+#include <qwidget.h>
+#include <qbitmap.h>
+#include <qptrlist.h>
+#include <qcstring.h>
+
+#include <kwinmodule.h>
+
+class QLabel;
+class KJWidget;
+class KHelpMenu;
+class KJSeeker;
+class NoatunPreferences;
+class KJToolTip;
+class KJFont;
+class KJPrefs;
+
+
+class KJLoader : public QWidget, public UserInterface, public Parser
+{
+Q_OBJECT
+NOATUNPLUGIND
+
+ friend class KJWidget;
+public:
+ KJLoader();
+ ~KJLoader();
+
+public:
+ void minimize();
+ KHelpMenu *helpMenu() const { return mHelpMenu; }
+ QStringList &item(const QString &key) { return *Parser::find(key); }
+
+ // returns path to currently loaded configfile
+ // can be either a newly loaded one or one of the three below
+ QString currentSkin() { return mCurrentSkin; }
+
+ // returns path to mainskin-configfile
+ QString currentDefaultSkin() { return mCurrentDefaultSkin; }
+
+ //returns path to dockmode-configfile if present
+ QString currentDockModeSkin() { return mCurrentDockModeSkin; }
+
+ //returns path to winshademode-configfile if present (not supported yet)
+ QString currentWinshadeModeSkin() { return mCurrentWinshadeModeSkin; }
+
+ KJPrefs *prefs() const { return mPrefs; }
+
+ QPtrList<KJWidget> widgetsAt(const QPoint &pt) const;
+
+ void removeChild(KJWidget *c);
+ void addChild(KJWidget *c);
+
+public slots:
+ void loadSkin(const QString &file);
+ void readConfig();
+ void switchToDockmode();
+ void returnFromDockmode();
+
+protected:
+ void unloadSkin();
+ void showSplash();
+
+public slots:
+ void timeUpdate();
+ void newSong();
+
+private slots:
+ void loadSeeker();
+ void slotWindowActivate(WId win);
+ void slotWindowRemove(WId win);
+ void slotWindowChange(WId win);
+ void slotDesktopChange(int);
+ void slotStackingChanged();
+ void restack();
+ void hideSplash();
+
+protected:
+ virtual void mouseMoveEvent(QMouseEvent *e);
+ virtual void mousePressEvent(QMouseEvent *e);
+ virtual void mouseReleaseEvent(QMouseEvent *e);
+ virtual void paintEvent(QPaintEvent *e);
+ virtual void closeEvent(QCloseEvent*e);
+ virtual void wheelEvent(QWheelEvent *e);
+
+ virtual void dragEnterEvent(QDragEnterEvent *event);
+ virtual void dropEvent(QDropEvent *event);
+
+public:
+ static KJLoader* kjofol;
+
+private:
+ // ==== docking stuff ====
+ KWinModule *mWin;
+ WId mDockToWin;
+ int mDockPositionX, mDockPositionY, mDockPosition;
+ QRect mDockWindowRect;
+ // ==== end of docking stuff ====
+ bool moving;
+ QPoint mMousePoint;
+ QPtrList<KJWidget> subwidgets;
+ KJWidget *mClickedIn;
+ KHelpMenu *mHelpMenu;
+ KJFont *mText, *mNumbers, *mVolumeFont, *mPitchFont;
+ QLabel *splashScreen;
+ KJToolTip *mTooltips;
+ QString mCurrentSkin;
+ QString mCurrentDefaultSkin;
+ QString mCurrentDockModeSkin;
+ QString mCurrentWinshadeModeSkin;
+
+ KJPrefs *mPrefs;
+};
+
+#endif // KJLOADER_H
diff --git a/noatun/modules/kjofol-skin/kjofolui.plugin b/noatun/modules/kjofol-skin/kjofolui.plugin
new file mode 100644
index 00000000..f6037cdb
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjofolui.plugin
@@ -0,0 +1,64 @@
+Filename=noatun_kjofol.la
+Author=Charles Samuels, Stefan Gehn
+Site=http://noatun.kde.org/
+Email=charles@kde.org
+Type=userinterface
+License=Artistic
+Name=K-Jöfol
+Name[eo]=K-Jofol
+Name[hi]=के-जॉफ़ॉल
+Name[is]=K-Jofol
+Name[ne]=के-जोफोल
+Name[pt_BR]=K-Jofol
+Name[ta]=K-Jofol
+Name[xh]=K-Jofol
+Comment=Skin loader for K-Jofol skins
+Comment[bg]=Зареждане на теми за K-Jofol
+Comment[br]=Ur c'harger a groc'hen evit K-Jofol
+Comment[bs]=Skin Loader za K-Jofol skinove
+Comment[ca]=Carregador d'aparences per les aparences K-Jofol
+Comment[cs]=Nahrávání motivů pro motivy K-Jofolu
+Comment[cy]=Llwythydd croen ar gyfer crwyn K-Jofol
+Comment[da]=Forsideindlæser for K-Jöfol-forsider
+Comment[de]=Importprogramm für K-Jöfol-Designs
+Comment[el]=Φόρτωση θέματος για θέματα K-Jofol
+Comment[eo]=Ŝargilo por K-Jofol-etosoj
+Comment[es]=Cargador de pieles de K-Jofol
+Comment[et]=K-Jöfoli rüüde laadija
+Comment[eu]=Azal kargatzailea K-Jöfol azalentzat
+Comment[fa]=بارکننده Skin برای K-Jofol skins
+Comment[fi]=K-Jofol-nahkojen latausohjelma
+Comment[fr]=Chargeur de revêtements K-Jöfol
+Comment[gl]=Cargador de peles para as peles de K-Jofol
+Comment[he]=טוען Skins של K-Jofol
+Comment[hu]=Betöltőprogram a K-Jofol-os kinézetekhez
+Comment[is]=Hleður inn K-Jofol skinn
+Comment[it]=Caricatore di skin per K-Jöfol
+Comment[ja]=K-Jöfol スキンのローダ
+Comment[kk]=K-Jofol тыстарының жүктегіші
+Comment[km]=កម្មវិធី​ផ្ទុក​ស្បែក K-Jofol
+Comment[ko]=K-Jofol 스킨 로더
+Comment[lt]=K-Jofol apvalkalų pakrovėjas
+Comment[mk]=Вчитувач на маски за маски K-Jofol
+Comment[nb]=Drakthenter for K-Jöfol-drakt
+Comment[nds]=Böversietlader för "K-Jöfol"-Böversieden
+Comment[ne]=के-जोफोल स्किनका लागि स्किन लोडर
+Comment[nl]=Skinlader voor K-Jofol-skins
+Comment[nn]=Skal-lastar for K-Jofol-skal
+Comment[pl]=Ładowarka skór dla skór K-Jofol
+Comment[pt]=Leitor de aspectos do K-Jofol
+Comment[pt_BR]=Carregador de aparências (skins) para o K-Jofol
+Comment[ro]=Încărcător de interfeţe pentru tematici K-Jofol
+Comment[ru]=Загрузчик образов K-Jofol
+Comment[sk]=Nahrávanie tém K-Jofol
+Comment[sl]=Nalagalnik za preobleke K-Jofol
+Comment[sr]=Учитавач кошуљица за К-Jofol кошуљице
+Comment[sr@Latn]=Učitavač košuljica za K-Jofol košuljice
+Comment[sv]=Skalladdare för K-Jofol-skal
+Comment[ta]=கே-ஜோபோல் அலங்கார அமைப்புக்கான ஏற்றி
+Comment[th]=ตัวโหลดหน้ากากสำหรับ K-Jofol
+Comment[tr]=K-Jofol arayüzleri için yükleyici
+Comment[uk]=Завантажувач жупанів для K-Jofol
+Comment[zh_CN]=K-Jofol 外观载入器
+Comment[zh_HK]=用於 K-Jofol 外貌的外貌載入器
+Comment[zh_TW]= K-Jofol 面板載入器
diff --git a/noatun/modules/kjofol-skin/kjprefs.cpp b/noatun/modules/kjofol-skin/kjprefs.cpp
new file mode 100644
index 00000000..0cadc5ac
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjprefs.cpp
@@ -0,0 +1,658 @@
+/***************************************************************************
+ kjprefs.cpp - Preferences-Dialog for KJ�ol-Skinloader
+ --------------------------------------------------------
+ Maintainer: Stefan Gehn <sgehn@gmx.net>
+
+ ***************************************************************************/
+
+// local includes
+#include "kjprefs.h"
+#include "kjprefs.moc"
+#include "kjloader.h"
+#include "kjwidget.h"
+#include "kjvis.h"
+#include "parser.h"
+
+// system includes
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qradiobutton.h>
+#include <qslider.h>
+#include <qpixmap.h>
+#include <qtabwidget.h>
+#include <qtextbrowser.h>
+#include <qfileinfo.h>
+#include <qstringlist.h>
+#include <qregexp.h>
+
+#include <knuminput.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kio/job.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kmimemagic.h>
+#include <knotifyclient.h>
+#include <kprocess.h>
+#include <kstandarddirs.h>
+#include <kglobalsettings.h>
+#include <kfontcombo.h>
+#include <kcolorcombo.h>
+
+static QString expand(QString s);
+
+KJPrefs::KJPrefs(QObject* parent)
+ : CModule(i18n("K-Jöfol Skins"), i18n("Skin Selection For the K-Jöfol Plugin"), "style", parent)
+{
+ cfg = KGlobal::config();
+
+ QVBoxLayout *vbox = new QVBoxLayout(this);
+ vbox->setAutoAdd(true);
+ vbox->setSpacing( 0 );
+ vbox->setMargin( 0 );
+
+ mTabWidget = new QTabWidget( this, "mTabWidget" );
+
+ mSkinselectorWidget = new KJSkinselector ( mTabWidget, "mSkinselectorWidget" );
+ mGuiSettingsWidget = new KJGuiSettings ( mTabWidget, "mGuiSettingsWidget" );
+
+ mTabWidget->insertTab( mSkinselectorWidget, i18n("&Skin Selector") );
+ mTabWidget->insertTab( mGuiSettingsWidget, i18n("O&ther Settings") );
+
+ connect ( mSkinselectorWidget->mSkins, SIGNAL(activated(const QString&)), SLOT(showPreview(const QString&)) );
+ connect ( mSkinselectorWidget->installButton, SIGNAL(clicked()), this, SLOT(installNewSkin()) );
+ connect ( mSkinselectorWidget->mRemoveButton, SIGNAL(clicked()), this, SLOT(removeSelectedSkin()) );
+
+ reopen(); // fill the skinlist and draw a preview
+}
+
+
+void KJPrefs::reopen() // reload config and set stuff in dialog
+{
+// kdDebug(66666) << "[KJPrefs] reopen()" << endl;
+
+ cfg->setGroup("KJofol-Skins");
+
+// mGuiSettingsWidget->timeCountdown->setChecked( cfg->readBoolEntry("TimeCountMode", false) );
+ mGuiSettingsWidget->displayTooltips->setChecked( cfg->readBoolEntry("DisplayTooltips", true) );
+ mGuiSettingsWidget->displaySplash->setChecked( cfg->readBoolEntry("DisplaySplashScreen", true) );
+
+ mGuiSettingsWidget->minPitch->setValue( cfg->readNumEntry("minimumPitch", 50) );
+ mGuiSettingsWidget->maxPitch->setValue( cfg->readNumEntry("maximumPitch", 200) );
+ mGuiSettingsWidget->visTimerValue->setValue( cfg->readNumEntry("VisualizationSpeed", 30) );
+
+ mGuiSettingsWidget->useSysFont->setChecked( cfg->readBoolEntry("Use SysFont", false) );
+ mGuiSettingsWidget->cmbSysFont->setCurrentFont(
+ cfg->readEntry("SysFont Family", KGlobalSettings::generalFont().family()) );
+ QColor tmpColor = QColor(255,255,255);
+ mGuiSettingsWidget->cmbSysFontColor->setColor(
+ cfg->readColorEntry("SysFont Color", &tmpColor));
+
+ // TODO somehow honor both config-entries, I want a custom mode
+ switch ( cfg->readNumEntry("TitleScrollSpeed", 400 ) )
+ {
+ case 800:
+ mGuiSettingsWidget->titleScrollSpeed->setValue(1);
+ break;
+ case 400:
+ mGuiSettingsWidget->titleScrollSpeed->setValue(2);
+ break;
+ case 200:
+ mGuiSettingsWidget->titleScrollSpeed->setValue(3);
+ break;
+ }
+
+ switch ( cfg->readNumEntry("AnalyzerType", KJVisScope::FFT ) )
+ {
+ case KJVisScope::Null:
+ mGuiSettingsWidget->visNone->setChecked(true);
+ mGuiSettingsWidget->visScope->setChecked(false);
+ mGuiSettingsWidget->visAnalyzer->setChecked(false);
+ break;
+
+ case KJVisScope::FFT:
+ mGuiSettingsWidget->visNone->setChecked(false);
+ mGuiSettingsWidget->visScope->setChecked(false);
+ mGuiSettingsWidget->visAnalyzer->setChecked(true);
+ break;
+
+ case KJVisScope::Mono:
+ mGuiSettingsWidget->visNone->setChecked(false);
+ mGuiSettingsWidget->visScope->setChecked(true);
+ mGuiSettingsWidget->visAnalyzer->setChecked(false);
+ break;
+ }
+
+ QStringList skins;
+ QStringList skinLocations = KGlobal::dirs()->findDirs("data", "noatun/skins/kjofol");
+ // iterate through all paths where Noatun is searching for kjofol-skins
+ for (uint i = 0; i < skinLocations.count(); ++i )
+ {
+ QStringList skinDirs = QDir(skinLocations[i]).entryList();
+ // iterate trough all dirs (normally, users can fsck every dir-struct *g*) containing a skin
+ for (uint k = 2; k < skinDirs.count(); ++k )
+ {
+ QDir skinDirCnt = QDir ( skinLocations[i]+skinDirs[k], "*.rc", QDir::Name|QDir::IgnoreCase, QDir::Files );
+ // make a list of all .rc-files in a skindir
+ QStringList rcFiles = skinDirCnt.entryList();
+ // iterate trough all those rc.-files in a skindir
+ for (uint j = 0; j < rcFiles.count(); j++ )
+ {
+// kdDebug(66666) << "found: " << rcFiles[j].latin1() << endl;
+ skins += ( rcFiles[j] );
+ }
+ }
+ }
+
+ skins.sort();
+
+ QString loaded = cfg->readEntry("SkinResource", locate("data", "noatun/skins/kjofol/kjofol/kjofol.rc") );
+ loaded = loaded.mid(loaded.findRev("/")+1); // remove path
+ loaded = loaded.left( loaded.length() - 3 ); // remove ".rc"
+
+ mSkinselectorWidget->mSkins->clear();
+
+ int index = 0;
+ for (QStringList::Iterator i=skins.begin(); i!=skins.end(); ++i)
+ {
+ *i = (*i).left( (*i).length() - 3 );
+ mSkinselectorWidget->mSkins->insertItem(*i);
+ if ( (*i) == loaded )
+ index = mSkinselectorWidget->mSkins->count()-1; // save index no. to set active item later on
+ }
+
+ mSkinselectorWidget->mSkins->setCurrentItem(index);
+
+ showPreview( mSkinselectorWidget->mSkins->currentText() );
+}
+
+
+void KJPrefs::save()
+{
+// kdDebug(66666) << k_funcinfo << "called." << endl;
+ QString skin=::expand ( mSkinselectorWidget->mSkins->currentText() );
+
+ // first load skin and then save config to prevent
+ // reloading a broken skin after a crash
+ KJLoader *l=KJLoader::kjofol;
+ if (l)
+ l->loadSkin(skin);
+
+ cfg->setGroup("KJofol-Skins");
+
+ cfg->writeEntry("SkinResource", skin);
+// cfg->writeEntry("TimeCountMode", timeCountMode() );
+ cfg->writeEntry("DisplayTooltips", displayTooltips() );
+ cfg->writeEntry("DisplaySplashScreen", displaySplash() );
+
+ cfg->writeEntry("TitleScrollSpeed", titleMovingUpdates() );
+ cfg->writeEntry("TitleScrollAmount", titleMovingDistance() );
+ cfg->writeEntry("AnalyzerType", (int)visType() );
+ cfg->writeEntry("minimumPitch", minimumPitch() );
+ cfg->writeEntry("maximumPitch", maximumPitch() );
+ cfg->writeEntry("VisualizationSpeed", visTimerValue() );
+
+ cfg->writeEntry("Use SysFont", mGuiSettingsWidget->useSysFont->isChecked());
+ cfg->writeEntry("SysFont Family", mGuiSettingsWidget->cmbSysFont->currentFont());
+// kdDebug(66666) << k_funcinfo << "currentfont=" << mGuiSettingsWidget->cmbSysFont->currentFont() << endl;
+ cfg->writeEntry("SysFont Color", mGuiSettingsWidget->cmbSysFontColor->color());
+
+ cfg->sync();
+
+ emit configChanged();
+}
+
+QString KJPrefs::skin( void ) const
+{
+ // return full path to currently loaded skin
+ return ::expand( mSkinselectorWidget->mSkins->currentText() );
+}
+
+int KJPrefs::minimumPitch( void ) const
+{
+ return mGuiSettingsWidget->minPitch->value();
+}
+
+int KJPrefs::maximumPitch( void ) const
+{
+ return mGuiSettingsWidget->maxPitch->value();
+}
+
+int KJPrefs::visTimerValue ( void ) const
+{
+ return mGuiSettingsWidget->visTimerValue->value();
+}
+
+int KJPrefs::titleMovingUpdates ( void ) const
+{
+ switch ( mGuiSettingsWidget->titleScrollSpeed->value() )
+ {
+ case 1:
+ return 800;
+ case 2:
+ return 400;
+ case 3:
+ return 200;
+ default:
+ return 400; // emergency exit :)
+ }
+}
+
+float KJPrefs::titleMovingDistance ( void ) const
+{
+ switch ( mGuiSettingsWidget->titleScrollSpeed->value() )
+ {
+ case 1:
+ return 0.2f;
+ case 2:
+ return 0.5f;
+ case 3:
+ return 1.0f;
+ default:
+ return 0.5f; // emergency exit :)
+ }
+}
+
+int KJPrefs::visType ( void ) const
+{
+ if ( mGuiSettingsWidget->visNone->isChecked() ) // No Vis
+ return KJVisScope::Null;
+ else if ( mGuiSettingsWidget->visScope->isChecked() ) // MonoScope
+ return KJVisScope::Mono;
+ else if ( mGuiSettingsWidget->visAnalyzer->isChecked() ) // FFT Analyzer
+ return KJVisScope::FFT;
+ else
+ return KJVisScope::StereoFFT; //Null; // emergency exit :)
+}
+
+void KJPrefs::setVisType ( int vis )
+{
+ switch ( vis )
+ {
+ case KJVisScope::Null:
+ mGuiSettingsWidget->visNone->setChecked(true);
+ mGuiSettingsWidget->visScope->setChecked(false);
+ mGuiSettingsWidget->visAnalyzer->setChecked(false);
+ break;
+
+ case KJVisScope::FFT:
+ mGuiSettingsWidget->visNone->setChecked(false);
+ mGuiSettingsWidget->visScope->setChecked(false);
+ mGuiSettingsWidget->visAnalyzer->setChecked(true);
+ break;
+
+ case KJVisScope::StereoFFT:
+ mGuiSettingsWidget->visNone->setChecked(false);
+ mGuiSettingsWidget->visScope->setChecked(false);
+ mGuiSettingsWidget->visAnalyzer->setChecked(false);
+ break;
+
+ case KJVisScope::Mono:
+ mGuiSettingsWidget->visNone->setChecked(false);
+ mGuiSettingsWidget->visScope->setChecked(true);
+ mGuiSettingsWidget->visAnalyzer->setChecked(false);
+ break;
+ }
+ save(); // not sure if that's a good idea or doing saving by hand in here
+}
+
+
+bool KJPrefs::useSysFont( void ) const
+{
+ return mGuiSettingsWidget->useSysFont->isChecked();
+}
+
+void KJPrefs::setUseSysFont( bool mode )
+{
+ mGuiSettingsWidget->useSysFont->setChecked( mode );
+ save(); // not sure if that's a good idea or doing saving by hand in here
+}
+
+QFont KJPrefs::sysFont(void) const
+{
+ QString family = mGuiSettingsWidget->cmbSysFont->currentFont();
+// kdDebug(66666) << k_funcinfo << "family=" << family << endl;
+ return QFont( family );
+}
+
+void KJPrefs::setSysFont(QFont &fnt)
+{
+ mGuiSettingsWidget->cmbSysFont->setCurrentFont( fnt.family() );
+}
+
+QColor KJPrefs::sysFontColor(void) const
+{
+ return mGuiSettingsWidget->cmbSysFontColor->color();
+}
+
+void KJPrefs::sysFontColor(QColor &c)
+{
+ mGuiSettingsWidget->cmbSysFontColor->setColor( c );
+}
+
+bool KJPrefs::displayTooltips( void ) const
+{
+ return mGuiSettingsWidget->displayTooltips->isChecked();
+}
+
+bool KJPrefs::displaySplash( void ) const
+{
+ return mGuiSettingsWidget->displaySplash->isChecked();
+}
+
+
+void KJPrefs::showPreview(const QString &_skin)
+{
+ Parser p;
+ p.open( ::expand(_skin) );
+
+ QImage image = p.image(p["BackgroundImage"][1]);
+ if (!image.isNull())
+ {
+ mPixmap.convertFromImage(image);
+ mPixmap.setMask( KJWidget::getMask(image) );
+ }
+ else
+ mPixmap=QPixmap();
+
+ mSkinselectorWidget->mPreview->setPixmap(mPixmap);
+ mSkinselectorWidget->mAboutText->setText(p.about());
+ mSkinselectorWidget->updateGeometry();
+}
+
+
+/* =================================================================================== */
+
+
+void KJPrefs::installNewSkin( void )
+{
+ bool skinInstalled = false; // flag showing wether a skindir got installed
+ KURL src, dst; // sourcedir and destinationdir for skin-installation
+
+ KURL srcFile ( mSkinselectorWidget->mSkinRequester->url() );
+
+ //kdDebug(66666) << "file to work on: " << srcFile.path().latin1() << endl;
+
+ if ( !srcFile.isValid() || srcFile.isEmpty() ) // stop working on broken URLs
+ {
+ kdDebug(66666) << "srcFile is malformed or empty !!!" << endl;
+ return;
+ }
+
+ if ( !srcFile.isLocalFile() ) // TODO: Download file into tmp dir + unpack afterwards
+ {
+ KMessageBox::sorry ( this, i18n("Non-Local files are not supported yet") );
+ return;
+ }
+
+ // Determine file-format trough mimetype (no stupid .ext test)
+ KMimeMagicResult * result = KMimeMagic::self()->findFileType( srcFile.path() );
+
+ if ( !result->isValid() )
+ {
+ kdDebug(66666) << "Could not determine filetype of srcFile !!!" << endl;
+ return;
+ }
+
+ if ( result->mimeType() != "application/x-zip" )
+ {
+ KMessageBox::error ( this, i18n("The selected file does not appear to be a valid zip-archive") );
+ return;
+ }
+
+ // create a dir with name of the skinarchive
+ // path to unpack to: pathToTmp/filename.ext/
+ QString tmpUnpackPath = locateLocal("tmp", srcFile.fileName()+"/" );
+ kdDebug(66666) << "tmpUnpackPath: " << tmpUnpackPath.latin1() << endl;
+
+ // Our extract-process, TODO: wanna have kio_(un)zip instead :)
+ KShellProcess proc;
+
+ // "unzip -d whereToUnpack whatToUnpack"
+ proc << "unzip -d " << proc.quote(tmpUnpackPath) << " " << proc.quote(srcFile.path());
+ kdDebug(66666) << "unzip -d " << tmpUnpackPath.latin1() << " " << srcFile.path().latin1() << endl;
+
+ proc.start( KProcess::Block, KProcess::NoCommunication );
+
+ // "unzip" spits out errorcodes > 0 only, 0 on success
+ if ( proc.exitStatus() != 0 )
+ {
+ KMessageBox::error ( this, i18n("Extracting skin-archive failed") );
+ // FIXME: Do I have to wait for the job to finish?
+ // I'd say no because I don't care about the temp-dir
+ // anyway after leaving this method :)
+ KIO::del( tmpUnpackPath );
+ return;
+ }
+
+ QDir tmpCnt = QDir ( tmpUnpackPath );
+ tmpCnt.setFilter ( QDir::Dirs );
+
+ QStringList dirList = tmpCnt.entryList();
+ // Iterate trough all subdirs of tmpUnpackPath (including "."!)
+ for ( unsigned int i = 0; i < dirList.count(); i++ )
+ {
+ // FIXME: is the following portable?
+ if ( dirList[i] == ".." )
+ continue;
+
+ QDir tmpSubCnt = QDir( tmpUnpackPath + dirList[i], "*.rc;*.RC;*.Rc;*.rC", QDir::Name|QDir::IgnoreCase, QDir::Files );
+ kdDebug(66666) << "Searching for *.rc in " << QString(tmpUnpackPath+dirList[i]).latin1() << endl;
+
+ // oh, no .rc file in current dir, let's go to next dir in list
+ if ( tmpSubCnt.count() == 0 )
+ continue;
+
+ src = KURL::encode_string(tmpUnpackPath+dirList[i]);
+ dst = KURL::encode_string(locateLocal("data","noatun/skins/kjofol/")); // destination to copy skindir into
+
+ if ( dirList[i] == "." ) // zip did not contain a subdir, we have to create one
+ {
+ // skindir is named like the archive without extension (FIXME: extension is not stripped from name)
+
+ int dotPos = srcFile.fileName().findRev('.');
+ if ( dotPos > 0 ) // found a dot -> (hopefully) strip the extension
+ {
+ dst.addPath( srcFile.fileName().left(dotPos) );
+ }
+ else // we don't seem to have any extension, just append the archivename
+ {
+ dst.addPath( srcFile.fileName() );
+ }
+
+ kdDebug(66666) << "want to create: " << dst.path().latin1() << endl;
+
+ if ( !dst.isValid() )
+ {
+ KMessageBox::error ( this,
+ i18n("Installing new skin failed: Destination path is invalid.\n"
+ "Please report a bug to the K-Jöfol maintainer") );
+ KIO::del( tmpUnpackPath );
+ return;
+ }
+ KIO::mkdir( dst );
+ }
+
+ if ( !src.isValid() || !dst.isValid() )
+ {
+ KMessageBox::error ( this,
+ i18n("Installing new skin failed: Either source or destination path is invalid.\n"
+ "Please report a bug to the K-Jöfol maintainer") );
+ }
+ else
+ {
+ kdDebug(66666) << "src: " << src.path().latin1() << endl;
+ kdDebug(66666) << "dst: " << dst.path().latin1() << endl;
+ KIO::Job *job = KIO::copy(src,dst);
+ connect ( job, SIGNAL(result(KIO::Job*)), this, SLOT(slotResult(KIO::Job*)) );
+ skinInstalled = true;
+ }
+ } // END iterate trough dirList
+
+ if ( !skinInstalled )
+ {
+ KMessageBox::sorry ( this, i18n("No new skin has been installed.\nMake sure the archive contains a valid K-Jöfol skin") );
+ }
+ else
+ {
+ KMessageBox::information ( this, i18n("The new skin has been successfully installed") );
+ }
+
+ KIO::del( tmpUnpackPath );
+}
+
+
+void KJPrefs::removeSelectedSkin( void )
+{
+ QString question = i18n("Are you sure you want to remove %1?\n"
+ "This will delete the files installed by this skin ").
+ arg ( mSkinselectorWidget->mSkins->currentText() );
+
+ cfg->setGroup("KJofol-Skins");
+ QString loadedSkin = cfg->readEntry("SkinResource", "kjofol");
+// kdDebug(66666) << "loaded Skin Name: " << QFileInfo(loadedSkin).baseName().latin1() << endl;
+
+ int r = KMessageBox::warningContinueCancel ( this, question, i18n("Confirmation"), KStdGuiItem::del() );
+ if ( r != KMessageBox::Continue )
+ return;
+
+ bool deletingCurrentSkin = ( mSkinselectorWidget->mSkins->currentText() == QFileInfo(loadedSkin).baseName() );
+
+ // Now find the dir to delete !!!
+
+ QString dirToDelete = QString ("");
+ QStringList skinLocations = KGlobal::dirs()->findDirs("data", "noatun/skins/kjofol");
+
+ // iterate through all paths where Noatun is searching for kjofol-skins
+ for (uint i = 0; i < skinLocations.count(); ++i )
+ {
+ QStringList skinDirs = QDir(skinLocations[i]).entryList();
+
+ // iterate trough all dirs containing a skin
+ for (uint k = 0; k < skinDirs.count(); ++k )
+ {
+ QDir skinDirCnt = QDir ( skinLocations[i]+skinDirs[k], "*.rc", QDir::Name|QDir::IgnoreCase, QDir::Files );
+ // make a list of all .rc-files in a skindir
+ QStringList rcFiles = skinDirCnt.entryList();
+
+ // iterate trough all those rc.-files in a skindir
+ for (uint j = 0; j < rcFiles.count(); j++ )
+ {
+ if ( rcFiles[j].left(rcFiles[j].length()-3) == mSkinselectorWidget->mSkins->currentText() ) // found skinname.rc :)
+ {
+ dirToDelete = QString ( skinLocations[i]+skinDirs[k] );
+ kdDebug(66666) << "FOUND SKIN @ " << dirToDelete.latin1() << endl;
+ }
+ }
+ }
+ }
+
+ if ( dirToDelete.length() != 0 )
+ {
+ kdDebug(66666) << "Deleting Skindir: " << dirToDelete.latin1() << endl;
+ KIO::Job *job = KIO::del( dirToDelete, false, true );
+ connect ( job, SIGNAL(result(KIO::Job*)), this, SLOT(slotResult(KIO::Job*)) );
+ }
+
+ int item = -1;
+ // Fallback to kjofol-skin (the default one) if we've deleted the current skin
+ if ( deletingCurrentSkin )
+ {
+ for ( int i = 0; i < mSkinselectorWidget->mSkins->count(); i++ )
+ { // FIXME: no check wether "kjofol" is ever found, well, it HAS to be in the list
+ if ( mSkinselectorWidget->mSkins->text(i) == "kjofol" )
+ item = i;
+ }
+ }
+ else
+ item = mSkinselectorWidget->mSkins->currentItem();
+
+ if ( item != -1 )
+ mSkinselectorWidget->mSkins->setCurrentItem( item );
+
+ // update configuration
+ if ( deletingCurrentSkin )
+ save();
+}
+
+void KJPrefs::slotResult(KIO::Job *job )
+{
+ if ( job->error() )
+ {
+ job->showErrorDialog(this);
+ }
+ else
+ {
+ // Reload Skinlist
+ reopen();
+ }
+}
+
+
+/* =================================================================================== */
+
+
+// takes name of rc-file without .rc at the end and returns full path to rc-file
+static QString expand(QString s)
+{
+// kdDebug(66666) << "expand( "<< s.latin1() << " )" << endl;
+
+ QStringList skinLocations = KGlobal::dirs()->findDirs("data", "noatun/skins/kjofol");
+
+ // iterate through all paths where Noatun is searching for kjofol-skins
+ for (uint i = 0; i < skinLocations.count(); ++i )
+ {
+ QStringList skinDirs = QDir(skinLocations[i]).entryList();
+
+ // iterate trough all dirs containing a skin
+ for (uint k = 0; k < skinDirs.count(); ++k )
+ {
+ QDir skinDirCnt = QDir ( skinLocations[i]+skinDirs[k], "*.rc", QDir::Name|QDir::IgnoreCase, QDir::Files );
+ // make a list of all .rc-files in a skindir
+ QStringList rcFiles = skinDirCnt.entryList();
+
+ // iterate trough all those rc.-files in a skindir
+ for (uint j = 0; j < rcFiles.count(); j++ )
+ {
+ if ( rcFiles[j].left(rcFiles[j].length()-3) == s ) // found $s.rc :)
+ {
+// kdDebug(66666) << "expand() found: " << QString(skinLocations[i]+skinDirs[k]+"/"+rcFiles[j]).latin1() << endl;
+ return (skinLocations[i]+skinDirs[k]+"/"+rcFiles[j]);
+ }
+ }
+ }
+ }
+ return QString();
+}
+
+QString filenameNoCase(const QString &filename, int badNodes)
+{
+ QStringList names=QStringList::split('/', filename);
+ QString full;
+ int number=(int)names.count();
+ for (QStringList::Iterator i=names.begin(); i!=names.end(); ++i)
+ {
+ full+="/";
+ if (number<=badNodes)
+ {
+ QDir d(full);
+ QStringList files=d.entryList();
+ files=files.grep(QRegExp("^"+ (*i) + "$", false));
+ if (!files.count())
+ return "";
+ *i=files.grep(*i, false)[0];
+ }
+
+ full+=*i;
+
+ number--;
+ }
+
+ if (filename.right(1)=="/")
+ full+="/";
+ return full;
+}
diff --git a/noatun/modules/kjofol-skin/kjprefs.h b/noatun/modules/kjofol-skin/kjprefs.h
new file mode 100644
index 00000000..ce1725d5
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjprefs.h
@@ -0,0 +1,96 @@
+#ifndef KJPREFS_H
+#define KJPREFS_H
+
+//#include "kjprefswidget.h"
+#include "kjskinselectorwidget.h"
+#include "kjguisettingswidget.h"
+
+// system includes
+#include <qwidget.h>
+#include <noatun/pref.h>
+
+#include <kio/job.h>
+#include <kurlrequester.h>
+
+class QVBoxLayout;
+class QHBoxLayout;
+class QGridLayout;
+class QComboBox;
+class QLabel;
+class QPushButton;
+class QTabWidget;
+class KConfig;
+class KJLoader;
+
+class KJPrefs : public CModule
+{
+Q_OBJECT
+public:
+ KJPrefs(QObject* parent);
+
+ // Save which Skin is currently selected
+ virtual void save();
+
+ // Rebuild the Skinlist
+ virtual void reopen();
+
+ QString skin( void ) const;
+
+ int minimumPitch( void ) const;
+ int maximumPitch( void ) const;
+
+ int visTimerValue ( void ) const;
+
+ int titleMovingUpdates ( void ) const;
+ float titleMovingDistance ( void ) const;
+
+ int visType ( void ) const;
+ void setVisType ( int vis );
+
+ bool useSysFont( void ) const;
+ void setUseSysFont( bool );
+
+ QFont sysFont(void) const;
+ void setSysFont(QFont&);
+
+ QColor sysFontColor(void) const;
+ void sysFontColor(QColor &);
+
+ bool displayTooltips( void ) const;
+ bool displaySplash( void ) const;
+
+public slots:
+ // Installs a skin defined by the URL in mSkinRequester
+ void installNewSkin( void );
+
+ // Delete the currently selected Skin (does not work for systemwide skins!)
+ void removeSelectedSkin ( void );
+
+ // Show a preview of "skin" in mPixmap
+ void showPreview(const QString &skin);
+
+ // gets called after a KIO-action has finished
+ // KIO is used for installing/removing skins
+ void slotResult(KIO::Job *job);
+
+signals:
+ void configChanged();
+
+private:
+ QPixmap mPixmap; // preview Pixmap
+ KConfig *cfg;
+
+ // Dialog-Widgets
+ QTabWidget *mTabWidget;
+ KJSkinselector *mSkinselectorWidget;
+ KJGuiSettings *mGuiSettingsWidget;
+};
+
+/**
+ * resolve a filename to its correct case.
+ * badNodes is the amount of directories/files (at the end)
+ * that aren't known)
+ **/
+QString filenameNoCase(const QString &filename, int badNodes=1);
+
+#endif // KJPREFS_H
diff --git a/noatun/modules/kjofol-skin/kjseeker.cpp b/noatun/modules/kjofol-skin/kjseeker.cpp
new file mode 100644
index 00000000..41e4db13
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjseeker.cpp
@@ -0,0 +1,210 @@
+/***************************************************************************
+ kjseeker.cpp
+ ---------------------------------------------
+ slider that lets the user jump inside the currently played file
+ ---------------------------------------------
+ Maintainer: Stefan Gehn <sgehn@gmx.net>
+
+ ***************************************************************************/
+
+#include "kjseeker.h"
+#include "kjloader.h"
+
+#include "helpers.cpp"
+#include <noatun/player.h>
+
+#include <kdebug.h>
+
+KJSeeker::KJSeeker(const QStringList &i, KJLoader *l) : KJWidget(l), g(0)
+{
+ QString activeBg = backgroundPressed("bmp1");
+ if(activeBg.isEmpty())
+ {
+ kdDebug(66666) << k_funcinfo << "No pressed background found for seeker," <<
+ " using default background!" << endl;
+ parent()->image(parser()["backgroundimage"][1]);
+ }
+ else
+ mActive = parent()->image(activeBg);
+
+ mScale = parent()->image(parser()["seekimage"][1]);
+ QImage pixmapNoPress = parent()->image(parser()["backgroundimage"][1]);
+
+ // generate transparent mask
+ int x, y, xs, ys;
+ x=i[1].toInt();
+ y=i[2].toInt();
+ xs=i[3].toInt()-x;
+ ys=i[4].toInt()-y;
+ setRect(x,y,xs,ys);
+ QImage transmask(xs, ys, 1, 2, QImage::LittleEndian);
+#if QT_VERSION < 0x030300
+ transmask.setColor(0, qRgb(0,0,0));
+ transmask.setColor(1, qRgb(255,255,255));
+#else
+ transmask.setColor(1, qRgb(0,0,0));
+ transmask.setColor(0, qRgb(255,255,255));
+#endif
+
+ // clear the pointers
+ memset(barmodeImages, 0, 256*sizeof(QImage*));
+ memset(barmode, 0, 256*sizeof(QPixmap*));
+
+ // Now do the pixel fking
+// kdDebug(66666) << "creating Pixmaps for Seeker" << endl;
+ for (int iy=y;iy<y+ys; iy++)
+ {
+ for (int ix=x;ix<x+xs; ix++)
+ {
+ QRgb checkmScale = mScale.pixel(ix, iy);
+ // am I transparent?
+ if (!isGray(checkmScale))
+ {
+ setPixel1BPP(transmask, ix-x, iy-y, 0);
+ continue;
+ }
+ setPixel1BPP(transmask, ix-x, iy-y, 1);
+
+ // what is the level
+ int level=grayRgb(checkmScale)+1;
+ if (level>255) level=255;
+ // allocate the pixmap of the level proper
+ // copy the color to the surface proper
+ QRgb activeColor=mActive.pixel(ix,iy);
+ QRgb inactiveColor=pixmapNoPress.pixel(ix,iy);
+ // set this pixel and everything before it
+ for(int i=0; i<level; i++)
+ {
+ if (!barmodeImages[i])
+ barmodeImages[i]=new QImage(xs,ys, 32);
+ QRgb *l=(QRgb*)barmodeImages[i]->scanLine(iy-y);
+ l[ix-x]=inactiveColor;
+ }
+
+ do
+ {
+ if (!barmodeImages[level])
+ barmodeImages[level]=new QImage(xs,ys, 32);
+ QRgb *l=(QRgb*)barmodeImages[level]->scanLine(iy-y);
+ l[ix-x]=activeColor;
+ } while (level++<255);
+ }
+ }
+// kdDebug(66666) << "finished creating Pixmaps" << endl;
+
+ // create the blank one
+ barmode[0]=new QPixmap(xs, ys);
+ QPixmap px=parent()->pixmap(parser()["backgroundimage"][1]);
+ bitBlt(barmode[0], 0, 0, &px, x, y, xs, ys, Qt::CopyROP);
+ px.convertFromImage(transmask);
+ barModeMask=px;
+
+// kdDebug(66666) << "END KJSeeker constructor" << endl;
+}
+
+QPixmap *KJSeeker::toPixmap(int n)
+{
+ if (!barmodeImages[n]) return barmode[n];
+
+ barmode[n]=new QPixmap(
+ barmodeImages[n]->width(),
+ barmodeImages[n]->height()
+ );
+ barmode[n]->convertFromImage(*barmodeImages[n]);
+
+ delete barmodeImages[n];
+ barmodeImages[n]=0;
+ return barmode[n];
+}
+
+
+KJSeeker::~KJSeeker()
+{
+ for (uint i=0; i<256; i++)
+ {
+ if (barmode[i])
+ delete barmode[i];
+ if (barmodeImages[i])
+ delete barmodeImages[i];
+ }
+}
+
+void KJSeeker::paint(QPainter *p, const QRect &)
+{
+ closest();
+ QPixmap *pixmap = toPixmap(g);
+ pixmap->setMask(barModeMask);
+ bitBlt(p->device(), rect().topLeft().x(), rect().topLeft().y(),
+ pixmap, 0, 0, rect().width(), rect().height(), Qt::CopyROP);
+}
+
+bool KJSeeker::mousePress(const QPoint &pos)
+{
+ return (isGray(mScale.pixel(rect().topLeft().x()+pos.x(), rect().topLeft().y()+pos.y())));
+}
+
+void KJSeeker::mouseRelease(const QPoint &pos, bool in)
+{
+ int x = rect().topLeft().x()+pos.x();
+ int y = rect().topLeft().y()+pos.y();
+
+ if(napp->player()->isStopped())
+ return;
+
+ if(!mScale.valid(x, y))
+ return;
+
+ QRgb color=mScale.pixel(x, y);
+
+ // user released mousebutton outside of the seeker-area (which is gray)
+ if ( (!isGray(color)) || (!in) )
+ return;
+
+ g = grayRgb(color);
+ repaint();
+
+// kdDebug(66666) << "length : " << napp->player()->getLength() << endl;
+// kdDebug(66666) << "skip to: " << ((long long)g*(long long)napp->player()->getLength())/255 << endl;
+
+ // g * titlelength can get REALLY HUGE, that's why I used (long long)
+ napp->player()->skipTo( ((long long)g*(long long)napp->player()->getLength())/255 );
+
+ return;
+}
+
+void KJSeeker::timeUpdate(int sec)
+{
+ int length = napp->player()->getLength() / 1000;
+ if (length<1)
+ length=1;
+
+ if (sec > length)
+ sec = length;
+ else if ( sec < 0 )
+ sec=0;
+
+ g = sec * 255 / length;
+ //kdDebug(66666) << "sec: " << sec << " len: " << length << " g: " << g << endl;
+ QPainter p(parent());
+ paint(&p, rect());
+}
+
+void KJSeeker::closest()
+{
+ int south=g, north=g;
+ bool southtried=false, northtried=false;
+ while (
+ !barmode[south] && !barmodeImages[south]
+ && !barmode[north] && !barmodeImages[north])
+ {
+ if (southtried && northtried) { g=0; return; }
+ south--;
+ north++;
+ if (north>255) {northtried=true; north=g;}
+ if (south<0) {southtried=true; south=g;}
+ }
+ if (barmode[south] || barmodeImages[south])
+ g=south;
+ else if (barmode[north] || barmodeImages[north])
+ g=north;
+}
diff --git a/noatun/modules/kjofol-skin/kjseeker.h b/noatun/modules/kjofol-skin/kjseeker.h
new file mode 100644
index 00000000..78fea6fb
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjseeker.h
@@ -0,0 +1,37 @@
+#ifndef KJSEEKER_H
+#define KJSEEKER_H
+
+#include "kjwidget.h"
+//#include "kjloader.h"
+class KJLoader;
+
+#include <qpainter.h>
+
+class KJSeeker : public KJWidget
+{
+public:
+ KJSeeker(const QStringList &i, KJLoader *);
+ ~KJSeeker();
+
+
+ virtual void paint(QPainter *, const QRect &rect);
+ virtual bool mousePress(const QPoint &pos);
+ virtual void mouseRelease(const QPoint &pos, bool);
+
+ void timeUpdate(int mille);
+
+ void closest();
+
+private:
+ QPixmap *toPixmap(int n);
+
+private:
+ QImage mScale;
+ QImage mActive;
+ QPixmap *barmode[256];
+ QImage *barmodeImages[256];
+ QBitmap barModeMask;
+ int g;
+};
+
+#endif
diff --git a/noatun/modules/kjofol-skin/kjskinselectorwidget.ui b/noatun/modules/kjofol-skin/kjskinselectorwidget.ui
new file mode 100644
index 00000000..1540ad5e
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjskinselectorwidget.ui
@@ -0,0 +1,227 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>KJSkinselector</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KJSkinselector</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>461</width>
+ <height>345</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>mSkins</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>previewGroup</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Preview</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer row="0" column="1">
+ <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>31</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="2" column="2">
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>51</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="3" column="1">
+ <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>41</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="1" column="0">
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>31</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="1" column="1" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>mPreview</cstring>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ <property name="hAlign" stdset="0">
+ </property>
+ <property name="vAlign" stdset="0">
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>About skin:</string>
+ </property>
+ </widget>
+ <widget class="QTextBrowser">
+ <property name="name">
+ <cstring>mAboutText</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Here you will see all the comments people wrote about their skins.
+It can be several lines and usually does not contain anything interesting but still this will be shown.</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>mSkinRequester</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>installButton</cstring>
+ </property>
+ <property name="text">
+ <string>Install Skin</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>mRemoveButton</cstring>
+ </property>
+ <property name="text">
+ <string>Remove Skin</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/noatun/modules/kjofol-skin/kjsliders.cpp b/noatun/modules/kjofol-skin/kjsliders.cpp
new file mode 100644
index 00000000..8cadd04f
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjsliders.cpp
@@ -0,0 +1,336 @@
+/***************************************************************************
+ kjsliders.cpp
+ ---------------------------------------------
+ Sliders for Volume and Pitch
+ ---------------------------------------------
+ Maintainer: Stefan Gehn <sgehn@gmx.net>
+
+ ***************************************************************************/
+
+// local includes
+#include "kjsliders.h"
+#include "kjtextdisplay.h"
+#include "kjprefs.h"
+
+#include "helpers.cpp"
+
+// kde includes
+#include <klocale.h>
+#include <kdebug.h>
+
+// arts-includes, needed for pitch
+#include <artsmodules.h>
+#include <reference.h>
+#include <soundserver.h>
+#include <kmedia2.h>
+
+// noatun includes
+#include <noatun/player.h>
+#include <noatun/engine.h>
+
+/*******************************************
+ * KJVolumeBar
+ *******************************************/
+
+KJVolumeBar::KJVolumeBar(const QStringList &i, KJLoader *p)
+ : KJWidget(p), mVolume(0), mText(0)
+{
+ int x, y, xs, ys;
+ x=i[1].toInt();
+ y=i[2].toInt();
+ xs=i[3].toInt()-x;
+ ys=i[4].toInt()-y;
+ setRect ( x, y, xs, ys );
+
+// kdDebug(66666) << "x: " << x << " y: " << y << " w: " << xs << " h: " << ys << endl;
+
+ mBack = parent()->pixmap(parser()["backgroundimage"][1]);
+ mSlider = parent()->pixmap(parser()["volumecontrolimage"][1]);
+}
+
+QString KJVolumeBar::tip()
+{
+ return i18n("Volume");
+}
+
+void KJVolumeBar::paint(QPainter *p, const QRect &)
+{
+// kdDebug(66666) << "x: " << rect().x() << " y: " << rect().y() << endl;
+// kdDebug(66666) << "vol x: " << rect().x()+(mVolume*rect().width())/100 << endl;
+
+ // center of that slider-pixmap
+// QPoint hotSpot = QPoint( mSlider.width()/2, mSlider.height()/2 );
+
+ // draw our background
+ bitBlt(
+ p->device(),
+ rect().x() /*- hotSpot.x()*/,
+ rect().y() /*- hotSpot.y()*/,
+ &mBack,
+ rect().x() /*- hotSpot.x()*/,
+ rect().y() /*- hotSpot.y()*/,
+ rect().width() /*+ (2*hotSpot.x())*/,
+ rect().height() /*+ (2*hotSpot.y())*/,
+ Qt::CopyROP);
+
+ // draw our slider
+ bitBlt(
+ p->device(),
+ rect().x() + ((mVolume*rect().width())/100) /*- hotSpot.x()*/,
+ rect().y() /*- hotSpot.y()*/,
+ &mSlider,
+ 0,
+ 0,
+ mSlider.width(),
+ mSlider.height(),
+ Qt::CopyROP);
+
+ if (mText)
+ mText->repaint();
+}
+
+bool KJVolumeBar::mousePress(const QPoint &pos)
+{
+ mVolume = (pos.x()*100) / rect().width();
+// kdDebug(66666) << "volume: " << mVolume << endl;
+ repaint();
+ napp->player()->setVolume(mVolume);
+ return true;
+}
+
+void KJVolumeBar::mouseRelease(const QPoint &, bool)
+{
+}
+
+void KJVolumeBar::mouseMove(const QPoint &pos, bool in)
+{
+ if (!in)
+ return;
+ mousePress(pos);
+}
+
+void KJVolumeBar::timeUpdate(int)
+{
+ mVolume = napp->player()->volume();
+ repaint();
+}
+
+
+/*******************************************
+ * KJVolumeBMP
+ *******************************************/
+
+KJVolumeBMP::KJVolumeBMP(const QStringList &i, KJLoader *p)
+ : KJWidget(p), mVolume(0), mOldVolume(0), mText(0)
+{
+ int x, y, xs, ys;
+ x=i[1].toInt();
+ y=i[2].toInt();
+ xs=i[3].toInt()-x;
+ ys=i[4].toInt()-y;
+ setRect ( x, y, xs, ys );
+
+ mWidth = parser()["volumecontrolimagexsize"][1].toInt();
+ mCount = parser()["volumecontrolimagenb"][1].toInt()-1;
+
+ mImages = parent()->pixmap(parser()["volumecontrolimage"][1]);
+ mPos = parent()->image(parser()["volumecontrolimageposition"][1]);
+ timeUpdate(0);
+}
+
+QString KJVolumeBMP::tip()
+{
+ return i18n("Volume");
+}
+
+void KJVolumeBMP::paint(QPainter *p, const QRect &)
+{
+ QRect from(mVolume*mCount/100*mWidth, 0, mWidth, mImages.height());
+ bitBlt(p->device(), rect().topLeft(), &mImages, from, Qt::CopyROP);
+ if (mText)
+ mText->repaint();
+}
+
+bool KJVolumeBMP::mousePress(const QPoint &pos)
+{
+ QRgb color = mPos.pixel ( rect().topLeft().x()+pos.x(), rect().topLeft().y()+pos.y() );
+
+ if (!isGray(color))
+ return false;
+
+ mVolume = grayRgb(color)*100/255;
+// kdDebug(66666) << "gray : " << grayRgb(color) << endl;
+// kdDebug(66666) << "volume: " << mVolume << endl;
+
+ repaint();
+
+ napp->player()->setVolume(mVolume);
+
+ return true;
+}
+
+void KJVolumeBMP::mouseRelease(const QPoint &, bool)
+{}
+
+void KJVolumeBMP::mouseMove(const QPoint &pos, bool in)
+{
+ if (!in) return;
+ mousePress(pos);
+}
+
+void KJVolumeBMP::timeUpdate(int)
+{
+ mVolume = napp->player()->volume();
+
+ if ( mVolume == mOldVolume ) // dont redraw if nothing changed
+ return;
+
+ mOldVolume = mVolume;
+
+ repaint();
+}
+
+
+/*******************************************
+ * KJPitchBMP
+ *******************************************/
+
+KJPitchBMP::KJPitchBMP(const QStringList &i, KJLoader *p)
+ : KJWidget(p), mText(0)
+{
+ int x = i[1].toInt();
+ int y = i[2].toInt();
+ int xs = i[3].toInt() - x;
+ int ys = i[4].toInt() - y;
+
+ setRect ( x, y, xs, ys );
+
+ mWidth = parser()["pitchcontrolimagexsize"][1].toInt();
+ mCount = parser()["pitchcontrolimagenb"][1].toInt()-1;
+
+ mImages = parent()->pixmap(parser()["pitchcontrolimage"][1]);
+ mPos = parent()->image(parser()["pitchcontrolimageposition"][1]);
+
+ // makes all pixels with rgb(255,0,255) transparent
+ QImage ibackground;
+ ibackground = parent()->image(parser()["pitchcontrolimage"][1]);
+ mImages.setMask( getMask(ibackground) );
+
+ Arts::PlayObject playobject = napp->player()->engine()->playObject();
+ Arts::PitchablePlayObject pitchable = Arts::DynamicCast(playobject);
+
+ if ( pitchable.isNull() )
+ mCurrentPitch = 1.0;
+ else
+ mCurrentPitch = pitchable.speed();
+
+// kdDebug() << "[KJPitchBMP] starting with pitch: " << mCurrentPitch << endl;
+/*
+ mMinPitch = 0.5;
+ mMaxPitch = 2.0;
+
+ mMinPitch = 0.8;
+ mMaxPitch = 1.2;
+*/
+
+ readConfig();
+
+ if (mText)
+ mText->repaint();
+}
+
+QString KJPitchBMP::tip()
+{
+ return i18n("Pitch");
+}
+
+void KJPitchBMP::paint(QPainter *p, const QRect &)
+{
+ float xPos = (int)((mCurrentPitch-mMinPitch)*100.0) * mCount / (int)((mMaxPitch-mMinPitch)*100.0) * mWidth;
+
+ QRect from( (int)xPos, 0, mWidth, mImages.height());
+
+ bitBlt(p->device(), rect().topLeft(), &mImages, from, Qt::CopyROP);
+
+ if (mText)
+ mText->repaint();
+}
+
+bool KJPitchBMP::mousePress(const QPoint &pos)
+{
+ QRgb color = mPos.pixel ( rect().topLeft().x()+pos.x(), rect().topLeft().y()+pos.y() );
+
+ if (!isGray(color))
+ return false;
+
+ mCurrentPitch = mMinPitch + ( (grayRgb(color)*(mMaxPitch-mMinPitch)) / 255 );
+// kdDebug(66666) << "[KJPitchBMP] mousePress() mCurrentPitch: " << mCurrentPitch << endl;
+
+ repaint();
+
+ newFile(); // wrong naming, in fact it just sets pitch
+
+ return true;
+}
+
+void KJPitchBMP::mouseRelease(const QPoint &, bool)
+{}
+
+void KJPitchBMP::mouseMove(const QPoint &pos, bool in)
+{
+ if (!in) return;
+ mousePress(pos);
+}
+
+void KJPitchBMP::timeUpdate(int)
+{
+// kdDebug(66666) << "[KJPitchBMP] :timeUpdate(int)" << endl;
+
+ Arts::PlayObject playobject = napp->player()->engine()->playObject();
+ Arts::PitchablePlayObject pitchable = Arts::DynamicCast(playobject);
+
+ if ( !pitchable.isNull() )
+ {
+ mCurrentPitch = pitchable.speed();
+// kdDebug(66666) << "[KJPitchBMP] mCurrentPitch: " << mCurrentPitch << endl;
+ }
+
+ if ( mCurrentPitch == mOldPitch ) // dont redraw if nothing changed
+ return;
+
+ mOldPitch = mCurrentPitch;
+
+ repaint();
+}
+
+void KJPitchBMP::newFile()
+{
+// kdDebug(66666) << "[KJPitchBMP] newFile()" << endl;
+
+ Arts::PlayObject playobject = napp->player()->engine()->playObject();
+ Arts::PitchablePlayObject pitchable = Arts::DynamicCast(playobject);
+
+ if (!pitchable.isNull())
+ {
+// kdDebug(66666) << "[KJPitchBMP] new speed: " << mCurrentPitch << endl;
+ pitchable.speed( mCurrentPitch );
+ }
+}
+
+void KJPitchBMP::readConfig()
+{
+// kdDebug(66666) << "KJPitchBMP::readConfig()" << endl;
+
+ mMinPitch = KJLoader::kjofol->prefs()->minimumPitch() / 100.0;
+ mMaxPitch = KJLoader::kjofol->prefs()->maximumPitch() / 100.0;
+
+ // Now comes the range checking if the user changed the setting :)
+ if ( mCurrentPitch < mMinPitch || mCurrentPitch > mMaxPitch )
+ {
+ if ( mCurrentPitch < mMinPitch )
+ mCurrentPitch = mMinPitch;
+ if ( mCurrentPitch > mMaxPitch )
+ mCurrentPitch = mMaxPitch;
+ newFile(); // wrong naming, in fact it just sets pitch
+ }
+}
diff --git a/noatun/modules/kjofol-skin/kjsliders.h b/noatun/modules/kjofol-skin/kjsliders.h
new file mode 100644
index 00000000..94f10934
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjsliders.h
@@ -0,0 +1,88 @@
+#ifndef KJSLIDERS_H
+#define KJSLIDERS_H
+
+#include "kjwidget.h"
+#include <qpainter.h>
+
+class KJLoader;
+class KJPitchText;
+class KJVolumeText;
+
+
+class KJVolumeBMP : public KJWidget
+{
+public:
+ KJVolumeBMP(const QStringList &, KJLoader *parent);
+
+ virtual void paint(QPainter *, const QRect &rect);
+ virtual bool mousePress(const QPoint &pos);
+ virtual void mouseRelease(const QPoint &pos, bool);
+ virtual void timeUpdate(int);
+ virtual void mouseMove(const QPoint &pos, bool);
+
+ virtual QString tip();
+
+ void setText(KJVolumeText *t) { mText=t; }
+
+private:
+ QPixmap mImages;
+ QImage mPos;
+ int mVolume, mOldVolume;
+ int mWidth, mCount;
+ KJVolumeText *mText;
+};
+
+
+class KJVolumeBar : public KJWidget
+{
+public:
+ KJVolumeBar(const QStringList &, KJLoader *parent);
+
+ virtual void paint(QPainter *, const QRect &rect);
+ virtual bool mousePress(const QPoint &pos);
+ virtual void mouseRelease(const QPoint &pos, bool);
+ virtual void timeUpdate(int);
+ virtual void mouseMove(const QPoint &pos, bool);
+
+ virtual QString tip();
+
+ void setText(KJVolumeText *t) { mText=t; }
+
+private:
+ QPixmap mSlider;
+ QPixmap mBack;
+ int mVolume;
+ KJVolumeText *mText;
+};
+
+
+class KJPitchBMP : public KJWidget
+{
+public:
+ KJPitchBMP(const QStringList &, KJLoader *parent);
+
+ virtual void paint(QPainter *, const QRect &rect);
+ virtual bool mousePress(const QPoint &pos);
+ virtual void mouseRelease(const QPoint &pos, bool);
+ virtual void timeUpdate(int);
+ virtual void newFile();
+ virtual void mouseMove(const QPoint &pos, bool);
+ virtual void readConfig();
+
+ virtual QString tip();
+
+ void setText(KJPitchText *t) { mText=t; }
+
+private:
+ QPixmap mImages;
+ QImage mPos;
+ int mWidth, mCount;
+ float mCurrentPitch;
+ float mOldPitch;
+ float mMinPitch;
+ float mMaxPitch;
+
+ KJPitchText *mText;
+};
+
+#endif
diff --git a/noatun/modules/kjofol-skin/kjtextdisplay.cpp b/noatun/modules/kjofol-skin/kjtextdisplay.cpp
new file mode 100644
index 00000000..89f92526
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjtextdisplay.cpp
@@ -0,0 +1,650 @@
+/***************************************************************************
+ kjtexdisplay.cpp
+ ---------------------------------------------
+ Displays for time and other things
+ using fonts provided by KJFont
+ ---------------------------------------------
+ Maintainer: Stefan Gehn <sgehn@gmx.net>
+
+ ***************************************************************************/
+
+// local includes
+#include "kjtextdisplay.h"
+#include "kjfont.h"
+#include "kjprefs.h"
+
+// kde includes
+#include <klocale.h>
+#include <kdebug.h>
+#include <kpixmap.h>
+#include <kurl.h>
+#include <krun.h>
+#include <kmimemagic.h>
+
+// arts-includes, needed for pitch
+#include <artsmodules.h>
+#include <arts/reference.h>
+#include <arts/soundserver.h>
+#include <arts/kmedia2.h>
+
+// noatun includes
+#include <noatun/player.h>
+#include <noatun/engine.h>
+
+/*******************************************
+ * KJFilename
+ *******************************************/
+
+KJFilename::KJFilename(const QStringList &l, KJLoader *p)
+ : QObject(0), KJWidget(p), mBack(0)
+{
+ int x = l[1].toInt();
+ int y = l[2].toInt();
+ int xs = l[3].toInt() - x;
+ int ys = l[4].toInt() - y;
+
+ // fix for all those weird skins where the filenamewindow has more
+ // height than needed for the font
+ // ( ... usually resulting in garbage on-screen )
+ if ( ys > (textFont().fontHeight()) )
+ ys = textFont().fontHeight();
+
+ // background under filename-scroller
+ QPixmap tmp = p->pixmap(p->item("backgroundimage")[1]);
+ mBack = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mBack, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ setRect(x,y,xs,ys);
+
+ // how far it moves per cycle
+ // TODO: make that configurable for the user
+
+ //mDistance = 1;
+// mDistance = (int)(textFont().fontWidth()/2);
+ readConfig();
+
+ prepareString(i18n("Welcome to Noatun").local8Bit());
+ killTimers();
+}
+
+KJFilename::~KJFilename()
+{
+ delete mBack;
+}
+
+void KJFilename::paint(QPainter *p, const QRect &)
+{
+ QPixmap temp ( rect().width(), rect().height() );
+
+ // draw background into buffer
+ bitBlt ( &temp, 0, 0, mBack, 0, 0, -1, -1, Qt::CopyROP );
+ // draw font into buffer
+ bitBlt( &temp, 0, 0, &mView, 0, 0, rect().width(), rect().height(), Qt::CopyROP);
+ // and draw it on screen
+ bitBlt(p->device(), rect().topLeft(), &temp,
+ QRect(0,0,-1,-1), Qt::CopyROP);
+}
+
+void KJFilename::timerEvent(QTimerEvent *)
+{
+ int height = mView.height();
+ int width = mView.width();
+
+ QBitmap cycleMask ( mDistance, height ); // temporary-space for moving parts of the mask
+ QPixmap cycle ( mDistance, height ); // temporary-space for moving parts of the pixmap
+ QBitmap newMask ( *mView.mask() ); // save old mask
+
+ // copy mask like the same way we're doing it with the pixmap
+ // a mask does not get copied on a bitblt automatically, we have to do
+ // it "by hand"
+ bitBlt(&cycleMask, 0,0, &newMask, 0,0, mDistance, height, Qt::CopyROP);
+ bitBlt(&newMask, 0,0, &newMask, mDistance, 0, width-mDistance, height, Qt::CopyROP);
+ bitBlt(&newMask, width-mDistance, 0, &cycleMask, 0,0, mDistance, height, Qt::CopyROP);
+
+ bitBlt(&cycle, 0,0, &mView, 0,0, mDistance, height, Qt::CopyROP);
+ bitBlt(&mView, 0,0, &mView, mDistance, 0, width-mDistance, height, Qt::CopyROP);
+ bitBlt(&mView, width-mDistance, 0, &cycle, 0,0, mDistance, height, Qt::CopyROP);
+
+ // apply the newly created mask
+ mView.setMask(newMask);
+
+ repaint();
+}
+
+bool KJFilename::mousePress(const QPoint &)
+{
+ return true;
+}
+
+void KJFilename::mouseRelease(const QPoint &, bool in)
+{
+ if (!in) // only do something if users is still inside the button
+ return;
+
+ if ( !napp->player()->current() )
+ return;
+
+ KURL dirURL = napp->player()->current().url().upURL();
+
+ KMimeMagicResult *result = KMimeMagic::self()->findFileType( dirURL.path() );
+
+ // TODO: Maybe test for protocol type?
+// if ( napp->player()->current().url().protocol() == "file" )
+ if ( result->isValid() )
+ KRun::runURL ( dirURL, result->mimeType() );
+}
+
+void KJFilename::readConfig()
+{
+ kdDebug(66666) << "KJFilename::readConfig()" << endl;
+ mDistance = (int)( textFont().fontWidth() * KJLoader::kjofol->prefs()->titleMovingDistance() );
+ if ( mDistance <= 0 )
+ mDistance = 1;
+ mTimerUpdates = KJLoader::kjofol->prefs()->titleMovingUpdates();
+ textFont().recalcSysFont();
+ mLastTitle=""; // invalidate title so it gets repainted on next timeUpdate()
+}
+
+void KJFilename::prepareString(const QCString &str)
+{
+ killTimers(); // i.e. stop timers
+
+ mView = textFont().draw(str, rect().width());
+
+ startTimer(mTimerUpdates);
+}
+
+void KJFilename::timeUpdate(int)
+{
+ if ( !napp->player()->current() ) // just for safety
+ return;
+
+ QCString title = QCString( napp->player()->current().title().local8Bit() );
+
+ if ( title == mLastTitle )
+ return;
+
+ mLastTitle = title;
+
+ QCString timestring = napp->player()->lengthString().local8Bit();
+ timestring = timestring.mid(timestring.find('/')+1);
+ prepareString ( title + " (" + timestring + ") ");
+}
+
+QString KJFilename::tip()
+{
+ if ( !napp->player()->current() ) // just for safety
+ return i18n("Filename");
+ else
+ return napp->player()->current().url().prettyURL();
+}
+
+
+/*******************************************
+ * KJTime
+ *******************************************/
+
+KJTime::KJTime(const QStringList &l, KJLoader *p)
+ : KJWidget(p), mBack(0)
+{
+ int x = l[1].toInt();
+ int y = l[2].toInt();
+ int xs = l[3].toInt() - x;
+ int ys = l[4].toInt() - y;
+
+ // fix for all those weird skins where the timewindow
+ // has more space than needed for the font
+ int maxNeededHeight = timeFont().fontHeight();
+ if ( ys > maxNeededHeight )
+ ys = maxNeededHeight;
+
+ // five digits + spacing between them
+ int maxNeededWidth = ( 5 *timeFont().fontWidth() ) + ( 4 * timeFont().fontSpacing() );
+ if ( xs > maxNeededWidth )
+ xs = maxNeededWidth;
+
+ // background under time-display
+ QPixmap tmp = p->pixmap(p->item("backgroundimage")[1]);
+ mBack = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mBack, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ setRect(x,y,xs,ys);
+
+ readConfig();
+
+ prepareString("00:00");
+}
+
+KJTime::~KJTime()
+{
+ delete mBack;
+}
+
+void KJTime::paint(QPainter *p, const QRect &)
+{
+// kdDebug(66666) << "KJTime::paint(QPainter *p, const QRect &)" << endl;
+ QPixmap temp ( rect().width(), rect().height() );
+
+ // draw background into buffer
+ bitBlt ( &temp, 0, 0, mBack, 0, 0, -1, -1, Qt::CopyROP );
+ // draw time-display into buffer (that's a pixmap with a mask applied)
+ bitBlt( &temp, 0, 0, &mTime, 0, 0, rect().width(), rect().height(), Qt::CopyROP);
+
+ // and draw it on screen
+ bitBlt(p->device(), rect().topLeft(), &temp,
+ QRect(0,0, rect().width(), rect().height()), Qt::CopyROP);
+}
+
+bool KJTime::mousePress(const QPoint &)
+{
+ return true;
+}
+
+void KJTime::mouseRelease(const QPoint &, bool in)
+{
+ if (!in) // only do something if users is still inside the button
+ return;
+
+ countDown = !countDown;
+ napp->setDisplayRemaining( countDown );
+// KJLoader::kjofol->prefs()->setTimeCountMode( countDown );
+}
+
+void KJTime::readConfig()
+{
+// kdDebug(66666) << "KJTime::readConfig()" << endl;
+ countDown = napp->displayRemaining();
+ timeFont().recalcSysFont();
+ mLastTime=""; // invalidate time so it gets repainted on next timeUpdate()
+}
+
+QString KJTime::lengthString ( void )
+{
+ int pos = 0;
+ QString posString;
+ int secs = 0,
+ seconds = 0,
+ minutes = 0,
+ hours = 0;
+
+ if ( countDown )
+ { // current remaining time
+ pos = napp->player()->getLength() - napp->player()->getTime();
+ }
+ else
+ { // current time
+ pos = napp->player()->getTime();
+ }
+
+ if ( pos < 0 )
+ {
+ posString = "00:00";
+ }
+ else
+ { // get the position
+ secs = pos / 1000; // convert milliseconds -> seconds
+
+ seconds = secs % 60;
+ minutes = (secs - seconds) / 60;
+ hours = minutes / 60;
+ minutes %= 60; // remove the hours from minutes ;)
+
+// cerr << " " << hours << ":" << minutes << ":" << seconds << endl;
+
+// if ( hours > 0 ) // looks ugly :)
+ if ( (napp->player()->getLength()/1000) >= 3600 ) // displays hh:mm if file is long
+ {
+ posString.sprintf("%d:%.2d", hours, minutes);
+ }
+ else // displays mm:ss
+ {
+ posString.sprintf("%.2d:%.2d", minutes, seconds);
+ }
+ }
+
+ return posString;
+}
+
+void KJTime::timeUpdate(int)
+{
+// kdDebug(66666) << "START KJTime::timeUpdate(int)" << endl;
+ if (!napp->player()->current())
+ return;
+
+ prepareString( (lengthString()).latin1() );
+
+// kdDebug(66666) << "END KJTime::timeUpdate(int)" << endl;
+}
+
+void KJTime::prepareString(const QCString &str)
+{
+// kdDebug(66666) << "START KJTime::prepareString(const QCString &str)" << endl;
+ if ( str == mLastTime )
+ return;
+
+ mLastTime = str;
+ mTime = timeFont().draw(str, rect().width());
+
+ repaint();
+// kdDebug(66666) << "END KJTime::prepareString(const QCString &str)" << endl;
+}
+
+QString KJTime::tip()
+{
+ if ( countDown )
+ return i18n("Play time left");
+ else
+ return i18n("Current play time");
+}
+
+
+/*******************************************
+ * KJVolumeText
+ *******************************************/
+
+KJVolumeText::KJVolumeText(const QStringList &l, KJLoader *p)
+ : KJWidget(p), mBack(0)
+{
+ int x=l[1].toInt();
+ int y=l[2].toInt();
+ int xs=l[3].toInt()-x;
+ int ys=l[4].toInt()-y;
+
+ // fix for all those weird skins where the timewindow has more space than needed for the font
+ if ( ys > (volumeFont().fontHeight()) )
+ ys = volumeFont().fontHeight();
+
+ // 3 digits for volume (1-100)
+ // + spaces according to spacing
+ // + percentage letter (seems to be 1px wider than a normal char)
+ int tempWidth = (3*volumeFont().fontWidth()) + (2*volumeFont().fontSpacing()) + ((volumeFont().fontWidth()+1));
+ if ( xs > ( tempWidth ) )
+ xs = tempWidth;
+
+ // background under volumetext-display
+ QPixmap tmp = p->pixmap(p->item("backgroundimage")[1]);
+ mBack = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mBack, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ setRect(x,y,xs,ys);
+
+ prepareString("100%");
+}
+
+KJVolumeText::~KJVolumeText()
+{
+ delete mBack;
+}
+
+void KJVolumeText::paint(QPainter *p, const QRect &)
+{
+ QPixmap temp ( rect().width(), rect().height() );
+
+ // draw background into buffer
+ bitBlt ( &temp, 0, 0, mBack, 0, 0, -1, -1, Qt::CopyROP );
+ // draw time-display into buffer
+ bitBlt( &temp, 0, 0, &mVolume, 0, 0, rect().width(), rect().height(), Qt::CopyROP);
+
+ // and draw it on screen
+ bitBlt(p->device(), rect().topLeft(), &temp,
+ QRect(0,0,-1,-1), Qt::CopyROP);
+}
+
+bool KJVolumeText::mousePress(const QPoint &)
+{
+ return false;
+}
+
+void KJVolumeText::readConfig()
+{
+ volumeFont().recalcSysFont();
+ mLastVolume=""; // invalidate value so it gets repainted on next timeUpdate()
+}
+
+void KJVolumeText::timeUpdate(int)
+{
+ QCString volume;
+
+ if (!napp->player()->current())
+ return;
+
+ volume.sprintf("%d%%", napp->player()->volume() ); // FIXME: is sprintf safe to use?
+
+ prepareString(volume);
+}
+
+void KJVolumeText::prepareString(const QCString &str)
+{
+ if ( str == mLastVolume )
+ return;
+
+ mLastVolume = str;
+ mVolume = volumeFont().draw(str, rect().width());
+
+ repaint();
+}
+
+QString KJVolumeText::tip()
+{
+ return i18n("Volume");
+}
+
+
+/*******************************************
+ * KJPitchText
+ *******************************************/
+
+KJPitchText::KJPitchText(const QStringList &l, KJLoader *p)
+ : KJWidget(p), mBack(0)
+{
+ int x = l[1].toInt();
+ int y = l[2].toInt();
+ int xs = l[3].toInt() - x;
+ int ys = l[4].toInt() - y;
+
+ // fix for all those weird skins where the timewindow has more space than needed for the font
+ if ( ys > (pitchFont().fontHeight()) )
+ ys = pitchFont().fontHeight();
+
+ // 3 digits for volume (1-100), spaces according to spacing and percentage letter
+ int tempWidth = (3*pitchFont().fontWidth()) + (2*pitchFont().fontSpacing());
+ if ( xs > tempWidth )
+ xs = tempWidth;
+
+ // background under time-display
+ QPixmap tmp = p->pixmap(p->item("backgroundimage")[1]);
+ mBack = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mBack, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ setRect(x,y,xs,ys);
+
+ prepareString("100");
+}
+
+KJPitchText::~KJPitchText()
+{
+ delete mBack;
+}
+
+
+void KJPitchText::paint(QPainter *p, const QRect &)
+{
+ QPixmap temp ( rect().width(), rect().height() );
+
+ // draw background into buffer
+ bitBlt ( &temp, 0, 0, mBack, 0, 0, -1, -1, Qt::CopyROP );
+ // draw time-display into buffer
+ bitBlt( &temp, 0, 0, &mSpeed, 0, 0, rect().width(), rect().height(), Qt::CopyROP);
+ // and draw it on screen
+ bitBlt(p->device(), rect().topLeft(), &temp, QRect(0,0,-1,-1), Qt::CopyROP);
+}
+
+bool KJPitchText::mousePress(const QPoint &)
+{
+ return true;
+}
+
+void KJPitchText::mouseRelease(const QPoint &, bool in)
+{
+ if (!in)
+ return;
+
+ Arts::PlayObject playobject = napp->player()->engine()->playObject();
+ Arts::PitchablePlayObject pitchable = Arts::DynamicCast(playobject);
+
+ if (pitchable.isNull())
+ return;
+
+ pitchable.speed( 1.00f ); // reset pitch
+}
+
+void KJPitchText::readConfig()
+{
+ pitchFont().recalcSysFont();
+ mLastPitch=""; // invalidate value so it gets repainted on next timeUpdate()
+}
+
+void KJPitchText::timeUpdate(int)
+{
+ QCString speed;
+
+ if (!napp->player()->current())
+ return;
+
+ Arts::PlayObject playobject = napp->player()->engine()->playObject();
+ Arts::PitchablePlayObject pitchable = Arts::DynamicCast(playobject);
+
+ if (pitchable.isNull())
+ return;
+
+ speed.setNum ( (int) ((float)pitchable.speed()*(float)100) );
+ prepareString ( speed );
+}
+
+void KJPitchText::prepareString(const QCString &str)
+{
+ if ( str == mLastPitch )
+ return;
+
+ mLastPitch = str;
+ mSpeed = pitchFont().draw(str, rect().width());
+
+ repaint();
+}
+
+QString KJPitchText::tip()
+{
+ return i18n("Pitch");
+}
+
+
+/*******************************************
+ * KJFileInfo
+ *******************************************/
+
+KJFileInfo::KJFileInfo(const QStringList &l, KJLoader *p)
+ : KJWidget(p), mBack(0)
+{
+ mInfoType = l[0]; // type of info-display
+
+ int x = l[1].toInt();
+ int y = l[2].toInt();
+ int xs = l[3].toInt() - x;
+ int ys = l[4].toInt() - y;
+
+ // fix for all those weird skins where the timewindow
+ // has more space than needed for the font
+ int maxNeededHeight = timeFont().fontHeight();
+ if ( ys > maxNeededHeight )
+ ys = maxNeededHeight;
+
+ // five digits + spacing between them
+ int maxNeededWidth = ( 3 *timeFont().fontWidth() ) + ( 2 * timeFont().fontSpacing() );
+ if ( xs > maxNeededWidth )
+ xs = maxNeededWidth;
+
+ // background under info-display
+ QPixmap tmp = p->pixmap(p->item("backgroundimage")[1]);
+ mBack = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mBack, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ setRect(x,y,xs,ys);
+
+ prepareString("");
+}
+
+KJFileInfo::~KJFileInfo()
+{
+ delete mBack;
+}
+
+void KJFileInfo::paint(QPainter *p, const QRect &)
+{
+ QPixmap temp ( rect().width(), rect().height() );
+
+ // draw background into buffer
+ bitBlt ( &temp, 0, 0, mBack, 0, 0, -1, -1, Qt::CopyROP );
+ // draw time-display into buffer (that's a pixmap with a mask applied)
+ bitBlt( &temp, 0, 0, &mTime, 0, 0, rect().width(), rect().height(), Qt::CopyROP);
+
+ // and draw it on screen
+ bitBlt(p->device(), rect().topLeft(), &temp,
+ QRect(0,0, rect().width(), rect().height()), Qt::CopyROP);
+}
+
+bool KJFileInfo::mousePress(const QPoint &)
+{
+ return false;
+}
+
+void KJFileInfo::readConfig()
+{
+ textFont().recalcSysFont();
+ mLastTime=""; // invalidate value so it gets repainted on next timeUpdate()
+}
+
+void KJFileInfo::timeUpdate(int)
+{
+ if (!napp->player()->current())
+ return;
+
+ const PlaylistItem &item = napp->player()->current();
+ QString prop;
+
+ if ( mInfoType == "mp3khzwindow" )
+ {
+ prop = item.property("samplerate");
+ prop.truncate(2); // we just want 44 instead of 44100
+ }
+ else if ( mInfoType == "mp3kbpswindow" )
+ {
+ prop = item.property("bitrate");
+ }
+ else // for safety: no infoType we know of
+ return;
+
+ if (prop.isNull())
+ prop="";
+ prepareString( prop.latin1() );
+}
+
+void KJFileInfo::prepareString(const QCString &str)
+{
+ if ( str == mLastTime )
+ return;
+ mLastTime = str;
+ mTime = textFont().draw(str, rect().width());
+ repaint();
+}
+
+QString KJFileInfo::tip()
+{
+ if ( mInfoType == "mp3khzwindow" )
+ return i18n("Sample rate in kHz");
+ else if ( mInfoType == "mp3kbpswindow" )
+ return i18n("Bitrate in kbps");
+
+ return QString();
+}
+
+#include "kjtextdisplay.moc"
diff --git a/noatun/modules/kjofol-skin/kjtextdisplay.h b/noatun/modules/kjofol-skin/kjtextdisplay.h
new file mode 100644
index 00000000..11098b0c
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjtextdisplay.h
@@ -0,0 +1,139 @@
+#ifndef KJTEXTDISPLAY_H
+#define KJTEXTDISPLAY_H
+
+#include "kjwidget.h"
+class KJLoader;
+class KPixmap;
+//#include "kjloader.h"
+
+#include <qobject.h>
+#include <qpainter.h>
+
+class KJFilename : public QObject, public KJWidget
+{
+Q_OBJECT
+public:
+ KJFilename(const QStringList &, KJLoader *parent);
+ ~KJFilename();
+
+ virtual void paint(QPainter *, const QRect &rect);
+ virtual bool mousePress(const QPoint &pos);
+ virtual void mouseRelease(const QPoint &, bool in);
+// virtual void newFile();
+ virtual void timeUpdate(int);
+ virtual void readConfig();
+
+ void prepareString(const QCString &str);
+ virtual QString tip();
+
+ virtual void timerEvent(QTimerEvent *);
+
+private:
+ QCString mLastTitle;
+ int mDistance;
+ int mTimerUpdates;
+ int mWidth;
+ int mTickerPos;
+ QPixmap mView;
+ KPixmap *mBack;
+};
+
+
+class KJTime : public KJWidget
+{
+public:
+ KJTime(const QStringList &, KJLoader *parent);
+ ~KJTime();
+
+ virtual void paint(QPainter *, const QRect &rect);
+ virtual bool mousePress(const QPoint &pos);
+ virtual void mouseRelease(const QPoint &, bool in);
+ virtual void timeUpdate(int);
+ virtual void readConfig();
+
+ void prepareString(const QCString &time);
+ virtual QString tip();
+
+// enum countModes { Up=0, Down };
+
+private:
+ QCString mLastTime;
+ int mWidth;
+ bool countDown;
+ QPixmap mTime;
+ KPixmap *mBack;
+
+private:
+ QString lengthString ( void );
+
+};
+
+
+class KJVolumeText : public KJWidget
+{
+public:
+ KJVolumeText(const QStringList &, KJLoader *parent);
+ ~KJVolumeText();
+
+ virtual void paint(QPainter *, const QRect &rect);
+ virtual bool mousePress(const QPoint &pos);
+ virtual void timeUpdate(int);
+ virtual void readConfig();
+
+ void prepareString(const QCString &time);
+ virtual QString tip();
+
+private:
+ QCString mLastVolume;
+ int mWidth;
+ QPixmap mVolume;
+ KPixmap *mBack;
+};
+
+
+class KJPitchText : public KJWidget
+{
+public:
+ KJPitchText(const QStringList &, KJLoader *parent);
+ ~KJPitchText();
+
+ virtual void paint(QPainter *, const QRect &rect);
+ virtual bool mousePress(const QPoint &pos);
+ virtual void mouseRelease(const QPoint &, bool in);
+ virtual void timeUpdate(int);
+ virtual void readConfig();
+
+ void prepareString(const QCString &time);
+ virtual QString tip();
+
+private:
+ QCString mLastPitch;
+ int mWidth;
+ QPixmap mSpeed;
+ KPixmap *mBack;
+};
+
+
+class KJFileInfo : public KJWidget
+{
+public:
+ KJFileInfo(const QStringList &, KJLoader *parent);
+ ~KJFileInfo();
+
+ virtual void paint(QPainter *, const QRect &rect);
+ virtual bool mousePress(const QPoint &pos);
+ virtual void timeUpdate(int);
+ virtual void readConfig();
+
+ void prepareString(const QCString &time);
+ virtual QString tip();
+
+private:
+ QCString mLastTime;
+ QString mInfoType;
+ int mWidth;
+ QPixmap mTime;
+ KPixmap *mBack;
+};
+
+#endif
diff --git a/noatun/modules/kjofol-skin/kjvis.cpp b/noatun/modules/kjofol-skin/kjvis.cpp
new file mode 100644
index 00000000..71246089
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjvis.cpp
@@ -0,0 +1,538 @@
+/***************************************************************************
+ kjvis.cpp - Visualizations used in the KJfol-GUI
+ --------------------------------------
+ Maintainer: Stefan Gehn <metz AT gehn.net>
+
+ ***************************************************************************/
+
+// local includes
+#include "kjvis.h"
+#include "kjprefs.h"
+
+// system includes
+#include <math.h>
+
+//qt includes
+#include <qpainter.h>
+#include <qsize.h>
+
+//kde includes
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kconfig.h>
+#include <kpixmapeffect.h>
+#include <kpixmap.h>
+
+// noatun includes
+#include <noatun/player.h>
+
+#define _KJ_GRADIENT_DIFF 130
+
+/*******************************************
+ * KJFFTScope
+ *******************************************/
+
+void KJVisScope::swapScope(Visuals newOne)
+{
+ //kdDebug(66666) << k_funcinfo << endl;
+ QStringList line = parent()->item("analyzerwindow");
+ KJLoader *p=parent();
+ p->removeChild(this);
+ delete this;
+
+ KJLoader::kjofol->prefs()->setVisType ( newOne );
+
+ KJWidget *w = 0;
+ switch (newOne)
+ {
+ case Null:
+ w = new KJNullScope(line, p);
+ break;
+ case FFT:
+ w = new KJFFT(line, p);
+ break;
+ case StereoFFT:
+ w = new KJStereoFFT(line, p);
+ break;
+ case Mono:
+ w = new KJScope(line, p);
+ break;
+ };
+
+ p->addChild(w);
+}
+
+/*******************************************
+ * KJNullScope
+ *******************************************/
+
+KJNullScope::KJNullScope(const QStringList &l, KJLoader *parent)
+ : KJVisScope(parent)
+{
+ int x = l[1].toInt();
+ int y = l[2].toInt();
+ int xs = l[3].toInt() - x;
+ int ys = l[4].toInt() - y;
+
+ // background under vis
+ QPixmap tmp = parent->pixmap(parent->item("backgroundimage")[1]);
+ mBack = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mBack, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+ setRect ( x, y, xs, ys );
+ repaint();
+}
+
+void KJNullScope::paint(QPainter *p, const QRect &)
+{
+ // just redraw the background
+ bitBlt ( p->device(), rect().topLeft(), mBack, QRect(0,0,-1,-1), Qt::CopyROP );
+}
+
+bool KJNullScope::mousePress(const QPoint &)
+{
+ return true;
+}
+
+void KJNullScope::mouseRelease(const QPoint &, bool in)
+{
+ if (!in) // only do something if users is still inside the button
+ return;
+
+ parent()->repaint(rect(), false);
+ swapScope(FFT);
+}
+
+void KJNullScope::readConfig()
+{
+// kdDebug(66666) << "[KJNullScope] readConfig()" << endl;
+ Visuals v = (Visuals) KJLoader::kjofol->prefs()->visType();
+ if ( v != Null )
+ {
+ parent()->repaint(rect(), false);
+ swapScope ( v );
+ }
+}
+
+
+/*************************************************
+ * KJFFT - Analyzer like visualization, mono
+ *************************************************/
+
+KJFFT::KJFFT(const QStringList &l, KJLoader *parent)
+ : KJVisScope(parent), MonoFFTScope(50), mGradient(0)
+{
+ int x = l[1].toInt();
+ int y = l[2].toInt();
+ int xs = l[3].toInt()-x;
+ int ys = l[4].toInt()-y;
+
+ // each bar will be 1px wide
+ mMultiples=1;
+
+ if ( parent->exist("analyzercolor") )
+ {
+ QStringList &col = parser()["analyzercolor"];
+ mColor.setRgb ( col[1].toInt(), col[2].toInt(), col[3].toInt() );
+ }
+ else // TODO: what should be default colors for Vis?
+ {
+ mColor.setRgb ( 255, 255, 255 ); // white is default
+ }
+
+ // background under vis
+ QPixmap tmp = parent->pixmap(parent->item("backgroundimage")[1]);
+ mBack = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mBack, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ mAnalyzer = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mAnalyzer, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ // create a gradient for the bars going from 30% lighter to 30% darker than mColor
+ mGradient = new KPixmap ( QSize(xs,ys) );
+ KPixmapEffect::gradient ( *mGradient, mColor.light(_KJ_GRADIENT_DIFF),
+ mColor.dark(_KJ_GRADIENT_DIFF), KPixmapEffect::VerticalGradient );
+
+ setRect (x,y,xs,ys);
+ setBands(magic(xs/mMultiples));
+ readConfig(); // read our config settings
+ start();
+}
+
+void KJFFT::scopeEvent(float *d, int size)
+{
+ if ( !napp->player()->isPlaying() ) // don't draw if we aren't playing (either paused or stopped)
+ {
+ if ( napp->player()->isStopped() ) // clear vis-window if playing has been stopped
+ parent()->repaint(rect(), false);
+ return;
+ }
+
+ int x = 0;
+ int h = rect().height();
+
+ QBitmap mGradientMask ( rect().width(), h, true );
+ QPainter mask( &mGradientMask );
+
+ float *start = d ;
+ float *end = d + size /*- 1*/;
+
+ // loop creating the mask for vis-gradient
+ for ( ; start < end; ++start )
+ {
+ // 5 has been 8 before and I have no idea how this scaling works :/
+ // FIXME: somebody please make it scale to 100% of height,
+ // I guess that would be nicer to look at
+// float n = log((*start)+1) * (float)h * 5;
+ float n = log((*start)+1) * (float)h * 5;
+ int amp=(int)n;
+
+ // range check
+ if ( amp < 0 ) amp = 0;
+ else if ( amp > h ) amp = h;
+
+ // make a part of the analyzer-gradient visible
+ mask.fillRect ( x, (h-amp), mMultiples, amp, Qt::color1 );
+ x += mMultiples;
+ }
+ // done creating our mask
+
+ // draw background of vis into it
+ bitBlt ( mAnalyzer, 0, 0, mBack, 0, 0, -1, -1, Qt::CopyROP );
+
+ // draw the analyzer
+ mGradient->setMask(mGradientMask);
+ bitBlt ( mAnalyzer, 0, 0, mGradient, 0, 0, -1, -1, Qt::CopyROP );
+
+ repaint();
+}
+
+void KJFFT::paint(QPainter *p, const QRect &)
+{
+ // put that thing on screen
+ if ( !napp->player()->isStopped() )
+ bitBlt ( p->device(), rect().topLeft(), mAnalyzer, QRect(0,0,-1,-1), Qt::CopyROP );
+}
+
+
+bool KJFFT::mousePress(const QPoint &)
+{
+ return true;
+}
+
+void KJFFT::mouseRelease(const QPoint &, bool in)
+{
+ if (!in) // only do something if users is still inside the button
+ return;
+
+ stop();
+ parent()->repaint(rect(), false);
+ swapScope(Mono);
+}
+
+void KJFFT::readConfig()
+{
+// kdDebug(66666) << "[KJFFT] readConfig()" << endl;
+ Visuals v = (Visuals) KJLoader::kjofol->prefs()->visType();
+ if ( v != FFT )
+ {
+ stop();
+ parent()->repaint(rect(), false);
+ swapScope ( v );
+ return;
+ }
+
+ mTimerValue = KJLoader::kjofol->prefs()->visTimerValue();
+ setInterval( mTimerValue );
+}
+
+
+/*************************************************
+ * KJStereoFFT - Analyzer like visualization, stereo
+ *************************************************/
+
+KJStereoFFT::KJStereoFFT(const QStringList &l, KJLoader *parent)
+ : KJVisScope(parent), StereoFFTScope(50), mGradient(0)
+{
+ //kdDebug(66666) << k_funcinfo << endl;
+
+ int x = l[1].toInt();
+ int y = l[2].toInt();
+ int xs = l[3].toInt()-x;
+ int ys = l[4].toInt()-y;
+
+ // each bar will be 1px wide
+ mMultiples=1;
+
+ if ( parent->exist("analyzercolor") )
+ {
+ QStringList &col = parser()["analyzercolor"];
+ mColor.setRgb ( col[1].toInt(), col[2].toInt(), col[3].toInt() );
+ }
+ else // TODO: what should be default colors for Vis?
+ {
+ mColor.setRgb ( 255, 255, 255 ); // white is default
+ }
+
+ // background under vis
+ QPixmap tmp = parent->pixmap(parent->item("backgroundimage")[1]);
+ mBack = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mBack, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ mAnalyzer = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mAnalyzer, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ // create a gradient for the bars going from 30% lighter to 30% darker than mColor
+ mGradient = new KPixmap ( QSize(xs,ys) );
+ KPixmapEffect::gradient ( *mGradient, mColor.light(_KJ_GRADIENT_DIFF),
+ mColor.dark(_KJ_GRADIENT_DIFF), KPixmapEffect::VerticalGradient );
+
+ setRect (x,y,xs,ys);
+ setBands(magic(xs/mMultiples));
+ readConfig(); // read our config settings
+ start();
+}
+
+void KJStereoFFT::scopeEvent(float *left, float *right, int len)
+{
+ if ( !napp->player()->isPlaying() ) // don't draw if we aren't playing (either paused or stopped)
+ {
+ if ( napp->player()->isStopped() ) // clear vis-window if playing has been stopped
+ parent()->repaint(rect(), false);
+ return;
+ }
+
+ unsigned int h = rect().height();
+ int hh = (int)(rect().height()/2);
+
+ QBitmap mGradientMask ( rect().width(), h, true );
+ QPainter mask( &mGradientMask );
+
+ float *start = left;
+ float *end = left + len;
+ float n = 0.0;
+ int amp = 0;
+ int x = 0;
+
+ // loop creating the mask for vis-gradient
+ for ( ; start < end; ++start )
+ {
+ n = log((*start)+1) * (float)hh * 5;
+ amp = (int)n;
+
+ // range check
+ if ( amp < 0 ) amp = 0;
+ else if ( amp > hh ) amp = hh;
+
+ // make a part of the analyzer-gradient visible
+ mask.fillRect ( x, (h-amp), mMultiples, amp, Qt::color1 );
+ x += mMultiples;
+ }
+ // done creating our mask
+
+
+ start = right;
+ end = right + len;
+ x = 0;
+ // loop creating the mask for vis-gradient
+ for ( ; start < end; ++start )
+ {
+ n = log((*start)+1) * (float)hh * 5;
+ amp = (int)n;
+
+ // range check
+ if ( amp < 0 ) amp = 0;
+ else if ( amp > hh ) amp = hh;
+
+ // make a part of the analyzer-gradient visible
+ mask.fillRect ( x, 0, mMultiples, amp, Qt::color1 );
+ x += mMultiples;
+ }
+
+
+ // draw background of vis into it
+ bitBlt ( mAnalyzer, 0, 0, mBack, 0, 0, -1, -1, Qt::CopyROP );
+
+ // draw the analyzer
+ mGradient->setMask(mGradientMask);
+ bitBlt ( mAnalyzer, 0, 0, mGradient, 0, 0, -1, -1, Qt::CopyROP );
+
+ repaint();
+}
+
+void KJStereoFFT::paint(QPainter *p, const QRect &)
+{
+ // put that thing on screen
+ if ( !napp->player()->isStopped() )
+ bitBlt ( p->device(), rect().topLeft(), mAnalyzer, QRect(0,0,-1,-1), Qt::CopyROP );
+}
+
+bool KJStereoFFT::mousePress(const QPoint &)
+{
+ return true;
+}
+
+void KJStereoFFT::mouseRelease(const QPoint &, bool in)
+{
+ if (!in) // only do something if users is still inside the button
+ return;
+ stop();
+ parent()->repaint(rect(), false);
+ swapScope(Null);
+}
+
+void KJStereoFFT::readConfig()
+{
+ //kdDebug(66666) << k_funcinfo << endl;
+ Visuals v = (Visuals) KJLoader::kjofol->prefs()->visType();
+ if ( v != StereoFFT )
+ {
+ stop();
+ parent()->repaint(rect(), false);
+ swapScope ( v );
+ return;
+ }
+ setInterval(KJLoader::kjofol->prefs()->visTimerValue());
+}
+
+
+/*************************************************
+ * KJScope - oscilloscope like visualization
+ *************************************************/
+
+KJScope::KJScope(const QStringList &l, KJLoader *parent)
+ : KJVisScope(parent), MonoScope(50)/*, blurnum(0), mOsci(0)*/
+{
+ int x=l[1].toInt();
+ int y=l[2].toInt();
+ int xs = mWidth = l[3].toInt()-x;
+ int ys = mHeight = l[4].toInt()-y;
+
+ blurnum = 0;
+
+// kdDebug(66666) << "Analyzer Window " << x << "," << y << " " << mWidth << "," << mHeight << endl;
+
+ if ( parent->exist("analyzercolor") )
+ {
+ QStringList &col = parser()["analyzercolor"];
+ mColor.setRgb ( col[1].toInt(), col[2].toInt(), col[3].toInt() );
+ }
+ else // FIXME: what should be default colors for Vis?
+ mColor.setRgb ( 255, 255, 255 );
+
+ // background under vis
+ QPixmap tmp = parent->pixmap(parent->item("backgroundimage")[1]);
+ mBack = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mBack, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ mOsci = new KPixmap ( QSize(xs,ys) );
+ bitBlt( mOsci, 0, 0, &tmp, x, y, xs, ys, Qt::CopyROP );
+
+ // create a gradient
+ mGradient = new KPixmap ( QSize(xs,ys) );
+ KPixmapEffect::gradient ( *mGradient, mColor.light(_KJ_GRADIENT_DIFF),
+ mColor.dark(_KJ_GRADIENT_DIFF), KPixmapEffect::VerticalGradient );
+
+ setRect ( x, y, xs, ys );
+
+ // set the samplewidth to the largest integer divisible by mWidth
+ setSamples ( xs );
+
+ readConfig();
+ start();
+}
+
+void KJScope::scopeEvent(float *d, int size)
+{
+ if ( !napp->player()->isPlaying() )
+ {
+ if ( napp->player()->isStopped() )
+ {
+ bitBlt ( mOsci, 0, 0, mBack, 0, 0, -1, -1, Qt::CopyROP );
+ repaint();
+ }
+ return;
+ }
+
+ float *start = d;
+ float *end = d + size;
+
+ int heightHalf = rect().height()/2 /* -1 */;
+ int x = 0;
+
+ QPainter tempP( mOsci );
+
+ if ( blurnum == 3 )
+ { // clear whole Vis
+ bitBlt ( mOsci, 0, 0, mBack, 0, 0, -1, -1, Qt::CopyROP );
+ tempP.setPen( mColor.light(110) ); // 10% lighter than mColor
+ blurnum=0;
+ }
+ else
+ {
+ blurnum++;
+ // reduce color for blur-effect
+ tempP.setPen( mColor.dark(90+(10*blurnum)) ); // darken color
+ }
+
+ for ( ; start < end; ++start )
+ {
+ float n = (*start) * (float)heightHalf;
+ int amp = (int)n;
+
+ // range check
+ if ( amp > heightHalf ) amp = heightHalf;
+ else if ( amp < -heightHalf) amp = -heightHalf;
+
+ // draw
+// tempP.drawLine(x, heightHalf, x, heightHalf+amp);
+ if ( amp > 0 )
+ {
+ bitBlt ( tempP.device(), QPoint(x,heightHalf), mGradient, QRect(x,heightHalf,1,amp), Qt::CopyROP );
+ }
+ else
+ {
+ amp = -amp;
+ bitBlt ( tempP.device(), QPoint(x,heightHalf-amp), mGradient, QRect(x,(heightHalf-amp),1,amp), Qt::CopyROP );
+ }
+ x++;
+ }
+
+ repaint();
+}
+
+void KJScope::paint(QPainter *p, const QRect &)
+{
+ // put that thing on screen
+ bitBlt ( p->device(), rect().topLeft(), mOsci, QRect(0,0,-1,-1), Qt::CopyROP );
+}
+
+bool KJScope::mousePress(const QPoint &)
+{
+ return true;
+}
+
+void KJScope::mouseRelease(const QPoint &, bool in)
+{
+ if (!in) // only do something if users is still inside the button
+ return;
+
+ stop();
+ parent()->repaint(rect(), false);
+ swapScope(/*Null*/ StereoFFT);
+}
+
+void KJScope::readConfig()
+{
+// kdDebug(66666) << "[KJScope] readConfig()" << endl;
+ Visuals v = (Visuals) KJLoader::kjofol->prefs()->visType();
+ if ( v != Mono )
+ {
+ stop();
+ parent()->repaint(rect(), false);
+ swapScope ( v );
+ return;
+ }
+
+ mTimerValue = KJLoader::kjofol->prefs()->visTimerValue();
+ setInterval( mTimerValue );
+}
diff --git a/noatun/modules/kjofol-skin/kjvis.h b/noatun/modules/kjofol-skin/kjvis.h
new file mode 100644
index 00000000..d2a43700
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjvis.h
@@ -0,0 +1,102 @@
+#ifndef KJVIS_H
+#define KJVIS_H
+
+#include "kjwidget.h"
+class KJLoader;
+class KPixmap;
+
+class KJVisScope : public KJWidget
+{
+public:
+ KJVisScope(KJLoader *parent) : KJWidget(parent) {};
+ enum Visuals { Null=0, FFT, Mono, StereoFFT };
+ void swapScope(Visuals newOne);
+// virtual void readConfig();
+};
+
+
+// dummy-scope displaying nothing
+class KJNullScope : public KJVisScope
+{
+public:
+ KJNullScope(const QStringList &, KJLoader *parent);
+ virtual void paint(QPainter *p, const QRect &);
+ virtual bool mousePress(const QPoint&);
+ virtual void mouseRelease(const QPoint &, bool in);
+ virtual void readConfig(void);
+
+private:
+ KPixmap *mBack;
+
+};
+
+
+// analyzer-like scope
+class KJFFT : public KJVisScope, public MonoFFTScope
+{
+public:
+ KJFFT(const QStringList &, KJLoader *parent);
+ virtual void paint(QPainter *p, const QRect &);
+ virtual void scopeEvent(float *d, int size);
+
+ virtual bool mousePress(const QPoint&);
+ virtual void mouseRelease(const QPoint &, bool in);
+ virtual void readConfig(void);
+
+private:
+ QColor mColor;
+ KPixmap *mGradient;
+ KPixmap *mBack;
+ KPixmap *mAnalyzer;
+ int mMultiples;
+ int mTimerValue;
+};
+
+
+// analyzer-like scope, stereo version
+class KJStereoFFT : public KJVisScope, public StereoFFTScope
+{
+public:
+ KJStereoFFT(const QStringList &, KJLoader *parent);
+ virtual void paint(QPainter *p, const QRect &);
+ virtual void scopeEvent(float *left, float *right, int len);
+
+ virtual bool mousePress(const QPoint&);
+ virtual void mouseRelease(const QPoint &, bool in);
+ virtual void readConfig(void);
+
+private:
+ QColor mColor;
+ KPixmap *mGradient;
+ KPixmap *mBack;
+ KPixmap *mAnalyzer;
+ int mMultiples;
+ int mTimerValue;
+};
+
+
+// oscilloscope showing waveform
+class KJScope : public KJVisScope, public MonoScope
+{
+public:
+ KJScope ( const QStringList &, KJLoader *parent);
+ virtual void paint(QPainter *p, const QRect &);
+ virtual void scopeEvent(float *d, int size);
+
+ virtual bool mousePress(const QPoint&);
+ virtual void mouseRelease(const QPoint &, bool in);
+ virtual void readConfig(void);
+
+private:
+ QColor mColor;
+ KPixmap *mGradient;
+ KPixmap *mBack;
+ KPixmap *mOsci;
+ int mMultiples;
+ int mWidth;
+ int mHeight;
+ unsigned int blurnum;
+ int mTimerValue;
+};
+
+#endif
diff --git a/noatun/modules/kjofol-skin/kjwidget.cpp b/noatun/modules/kjofol-skin/kjwidget.cpp
new file mode 100644
index 00000000..e7b6a4d1
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjwidget.cpp
@@ -0,0 +1,70 @@
+/***************************************************************************
+ kjwidget.cpp - Base Class for all widgets
+ --------------------------------------
+ Maintainer: Stefan Gehn <sgehn@gmx.net>
+
+ ***************************************************************************/
+
+// local includes
+#include "kjwidget.h"
+//#include <kdebug.h>
+
+// ugly static functions and stuff
+#include "helpers.cpp"
+
+#include <qpainter.h>
+
+KJWidget::KJWidget(KJLoader *p) : mParent(p)
+{
+}
+
+QBitmap KJWidget::getMask(const QImage &_rect, register QRgb transparent)
+{
+ QImage result(_rect.width(), _rect.height(), 1,2, QImage::LittleEndian);
+#if QT_VERSION < 0x030300
+ result.setColor(0, qRgb(0,0,0)); //TODO: maybe use Qt::color0 and Qt::color1
+ result.setColor(1, qRgb(255,255,255));
+#else
+ result.setColor(1, qRgb(0,0,0));
+ result.setColor(0, qRgb(255,255,255));
+#endif
+
+ for(int height=0;height<_rect.height(); height++)
+ {
+ for(int width=0; width<_rect.width(); width++)
+ setPixel1BPP(result, width, height, _rect.pixel(width, height)!=transparent);
+ }
+ QBitmap bm;
+ bm.convertFromImage(result);
+ return bm;
+}
+
+void KJWidget::repaint(bool me, const QRect &r, bool clear)
+{
+ QPainter p(parent());
+ if (me)
+ paint(&p, r.isValid() ? r : rect());
+ else
+ parent()->repaint(r.isValid() ? r : rect(), clear);
+}
+
+const QString &KJWidget::backgroundPressed(const QString &bmp) const
+{
+ if(bmp.isEmpty()) // play safe
+ {
+// kdDebug(66666) << k_funcinfo << "empty argument 'bmp', returning QString::null!" << endl;
+ return QString::null;
+ }
+
+// kdDebug(66666) << k_funcinfo << "Returning pressed pixmap for '" << bmp.latin1() << "'" << endl;
+
+ // make absolutely sure the wanted backgroundimagepressedX line is there
+ QStringList item = parser()["backgroundimagepressed"+QString::number(bmp.mid(3).toInt())];
+ if(item.count() < 2)
+ {
+// kdDebug(66666) << k_funcinfo << "backgroundimagepressed doesn't have enough keys in its line!" << endl;
+ return QString::null;
+ }
+ else
+ return item[1];
+}
diff --git a/noatun/modules/kjofol-skin/kjwidget.h b/noatun/modules/kjofol-skin/kjwidget.h
new file mode 100644
index 00000000..cc7ddf53
--- /dev/null
+++ b/noatun/modules/kjofol-skin/kjwidget.h
@@ -0,0 +1,53 @@
+#ifndef KJWIDGET_H
+#define KJWIDGET_H
+
+#include "kjloader.h"
+
+class KJWidget
+{
+public:
+ KJWidget(KJLoader *);
+ virtual ~KJWidget() {};
+ // called when the widget should paint
+ virtual void paint(QPainter *, const QRect &) {};
+ // called to receive the rect this widget is in
+ virtual QRect rect() const { return mRect; }
+ // called when pressed in this widget
+ virtual bool mousePress(const QPoint &) {return false; }
+ // called when the mouse is released after clicked in this widget
+ virtual void mouseRelease(const QPoint &, bool){}
+ virtual void mouseMove(const QPoint &, bool) {}
+ // called with the current time (mille)
+ virtual void timeUpdate(int) {}
+ // called when a new song is playing, player() is ready with it too
+ virtual void newFile() {}
+ // called when config-entries have to be read, is a TODO for most widgets
+ virtual void readConfig() {}
+
+ // called when the mouse is moved while clicked in this widget
+ // repaint myself
+ virtual void repaint(bool me=true, const QRect &rect=QRect(), bool clear=false);
+
+ virtual QString tip() { return 0; }
+
+public:
+ static QBitmap getMask(const QImage &color, register QRgb=qRgb(255,0,255));
+
+protected:
+ const QString &backgroundPressed(const QString &bmp) const;
+ KJLoader *parent() const {return mParent;}
+ KJLoader &parser() const {return *mParent;}
+
+ KJFont &textFont() const {return *mParent->mText;}
+ KJFont &timeFont() const {return *mParent->mNumbers;}
+ KJFont &volumeFont() const {return *mParent->mVolumeFont;}
+ KJFont &pitchFont() const {return *mParent->mPitchFont;}
+
+ void setRect(const QRect& rect) {mRect=rect;}
+ void setRect(int x, int y, int xs, int ys) {mRect=QRect(x,y,xs,ys);}
+private:
+ KJLoader *mParent;
+ QRect mRect;
+};
+
+#endif
diff --git a/noatun/modules/kjofol-skin/noatunui.cpp b/noatun/modules/kjofol-skin/noatunui.cpp
new file mode 100644
index 00000000..b5be87fd
--- /dev/null
+++ b/noatun/modules/kjofol-skin/noatunui.cpp
@@ -0,0 +1,9 @@
+#include "kjloader.h"
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new KJLoader();
+ }
+}
diff --git a/noatun/modules/kjofol-skin/parser.cpp b/noatun/modules/kjofol-skin/parser.cpp
new file mode 100644
index 00000000..df5fdc40
--- /dev/null
+++ b/noatun/modules/kjofol-skin/parser.cpp
@@ -0,0 +1,132 @@
+/***************************************************************************
+ parser.cpp - Reads *.rc files in kjfol-config-format into a QDict
+ --------------------------------------
+ Maintainer: Stefan Gehn <sgehn@gmx.net>
+
+ ***************************************************************************/
+
+// local includes
+#include "parser.h"
+#include "kjprefs.h"
+
+// system includes
+#include <qtextstream.h>
+#include <qimage.h>
+#include <qfile.h>
+#include <kdebug.h>
+#include <kmimemagic.h>
+#include <kurl.h>
+
+Parser::Parser() : QDict<QStringList>(17,false)
+{
+ mSkinAbout="";
+ mImageCache.setAutoDelete(true);
+ setAutoDelete(true);
+}
+
+void Parser::conserveMemory()
+{
+ mImageCache.clear();
+}
+
+void Parser::open(const QString &file)
+{
+ clear();
+ mImageCache.clear();
+ mSkinAbout="";
+ mDir=KURL(file).directory();
+ QFile f(file);
+ if ( !f.exists() )
+ return;
+ f.open(IO_ReadOnly);
+
+ f.at(0);
+ QTextStream stream(&f);
+ while (!stream.eof())
+ {
+ QString line=stream.readLine();
+ line=line.simplifyWhiteSpace();
+ if ((!line.length()) || line[0]=='#')
+ continue;
+ QStringList *l=new QStringList(QStringList::split(" ", (line.lower())));
+ QString first=l->first();
+
+ // special handling for about-texts as the key "about" can appear multiple
+ // times and thus does not fit into qdict.
+ if (first=="about")
+ {
+ if (!mSkinAbout.isEmpty())
+ mSkinAbout+="\n";
+
+ mSkinAbout += line.mid(6);
+// kdDebug(66666) << "found About-line, mSkinAbout is now '" << mSkinAbout << "'" << endl;
+ delete l; // don't need the stringlist anymore
+ }
+ else
+ insert(first, l);
+ }
+}
+
+QString Parser::fileItem(const QString &i) const
+{
+ return dir()+'/'+i;
+}
+
+QString Parser::dir() const
+{
+ return mDir;
+}
+
+Parser::ImagePixmap* Parser::getPair(const QString &filenameOld) const
+{
+ // is it in there?
+ ImagePixmap *pair;
+ {
+ pair=mImageCache.find(filenameOld);
+ if (pair)
+ return pair;
+ }
+
+ QString filename=fileItem(filenameOld);
+
+ QImage image;
+
+ // Determine file-format trough mimetype (no stupid .ext test)
+ KMimeMagicResult * result = KMimeMagic::self()->findFileType( filename );
+
+ if ( result->mimeType() == "image/png" )
+ {
+// image = NoatunApp::readPNG(filenameNoCase(filename));
+ QImageIO iio;
+ iio.setFileName( filenameNoCase(filename) );
+ // forget about gamma-value, fix for broken PNGs
+ iio.setGamma( 0.00000001 );
+ if ( iio.read() )
+ {
+ image = iio.image();
+ image.setAlphaBuffer(false); // we don't want/support alpha-channels
+ }
+ else
+ {
+ kdDebug(66666) << "Could not load file: " << filename.latin1() << endl;
+ }
+ }
+ else
+ {
+ image = QImage(filenameNoCase(filename));
+ }
+
+ //add to the cache
+ QPixmap pixmap;
+ pixmap.convertFromImage(image, QPixmap::AutoColor|QPixmap::ThresholdDither|QPixmap::AvoidDither);
+ pair = new Parser::ImagePixmap;
+ pair->mImage = image;
+ pair->mPixmap = pixmap;
+ mImageCache.insert(filenameOld, pair);
+ return pair;
+}
+
+bool Parser::exist(const QString &i) const
+{
+ return (bool)find(i);
+}
diff --git a/noatun/modules/kjofol-skin/parser.h b/noatun/modules/kjofol-skin/parser.h
new file mode 100644
index 00000000..97e20d99
--- /dev/null
+++ b/noatun/modules/kjofol-skin/parser.h
@@ -0,0 +1,49 @@
+#ifndef PARSER_H
+#define PARSER_H
+
+// system includes
+#include <qstringlist.h>
+#include <qpixmap.h>
+#include <qimage.h>
+#include <qdict.h>
+
+class Parser : public QDict<QStringList>
+{
+ class ImagePixmap
+ {
+ public:
+ ImagePixmap() : mImage(0), mPixmap(0) {}
+ ~ImagePixmap() {}
+ QImage mImage;
+ QPixmap mPixmap;
+ };
+
+ public:
+ Parser();
+
+ void conserveMemory();
+ void open(const QString &file);
+
+ QString dir() const;
+ QPixmap pixmap(const QString &pixmap) const
+ { return getPair(pixmap)->mPixmap; }
+ QImage image(const QString &image) const
+ { return getPair(image)->mImage; }
+ QString about() const { return mSkinAbout; };
+
+ QString fileItem(const QString &file) const;
+
+ bool exist(const QString &i) const;
+
+ public:
+ QStringList& operator[](const QString &l) { return *find(l);}
+
+ private:
+ ImagePixmap *getPair(const QString &i) const;
+
+ private:
+ mutable QDict<ImagePixmap> mImageCache;
+ QString mDir;
+ QString mSkinAbout;
+};
+#endif // PARSER_H
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/HexoBronx.rc b/noatun/modules/kjofol-skin/skins/HexoBronx/HexoBronx.rc
new file mode 100644
index 00000000..76e9d10c
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/HexoBronx.rc
@@ -0,0 +1,77 @@
+About .: HexoBronx :.
+
+BackgroundImage inactive.png
+BackgroundImagePressed1 active.png
+BackgroundImageInactive inactive.png
+
+SplashScreen splash.png
+
+
+FontImage font.png
+FontSize 5 7
+FontSpacing 0
+FontTransparent 1
+
+TimeFontImage eckig_font.png
+TimeFontSize 7 7
+TimeFontSpacing 0
+TimeFontTransparent 1
+
+PitchFontImage volume_pitch_font.png
+PitchFontSize 5 7
+PitchFontSpacing 0
+PitchFontTransparent 1
+
+VolumeFontImage volume_pitch_font.png
+VolumeFontSize 5 7
+VolumeFontSpacing 0
+VolumeFontTransparent 1
+
+
+SeekRegion 100 138 222 178
+SeekImage mask.png
+
+VolumeControlType BMP
+VolumeControlImage volume.png
+VolumeControlImagePosition mask.png
+VolumeControlImageXSize 33
+VolumeControlImageNb 39
+VolumeControlButton 16 108 48 146 vol
+
+PitchControlImage pitch.png
+PitchControlImagePosition mask.png
+PitchControlImageXSize 33
+PitchControlImageNb 39
+PitchControlButton 272 108 304 146 pitch
+
+CloseButton 233 31 245 42 close BMP1
+MinimizeButton 77 32 89 41 min BMP1
+
+PlayButton 124 21 160 59 play BMP1
+PauseButton 161 21 197 59 pause BMP1
+
+RewindButton 52 62 111 99 rwd BMP1
+ForwardButton 211 62 271 99 fwd BMP1
+
+AboutButton 144 76 176 86 about BMP1
+PlaylistButton 137 89 185 102 pl BMP1
+
+RepeatButton 85 119 103 136 repeat BMP1
+PreferencesButton 220 118 236 136 pref BMP1
+
+PreviousSongButton 52 156 109 192 prevsong BMP1
+NextSongButton 212 156 271 192 nextsong BMP1
+
+StopButton 123 197 197 233 stop BMP1
+
+
+MP3TimeWindow 144 104 179 111
+
+VolumeText 118 115 138 122
+
+PitchText 190 115 205 122
+
+FilenameWindow 108 125 215 132
+
+AnalyzerWindow 130 134 192 153 analyzer
+AnalyzerColor 136 217 144
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/Makefile.am b/noatun/modules/kjofol-skin/skins/HexoBronx/Makefile.am
new file mode 100644
index 00000000..5be0735d
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/Makefile.am
@@ -0,0 +1,7 @@
+
+skin_DATA = HexoBronx.rc README.txt active.png eckig_font.png \
+ font.png inactive.png mask.png pitch.png splash.png time_font.png volume.png \
+ volume_pitch_font.png
+
+skindir = $(kde_datadir)/noatun/skins/kjofol/HexoBronx
+EXTRA_DIST = $(skin_DATA)
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/README.txt b/noatun/modules/kjofol-skin/skins/HexoBronx/README.txt
new file mode 100644
index 00000000..640ca023
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/README.txt
@@ -0,0 +1,79 @@
+HeXoBronX - Desktop Media Control Interface
+ for Noatun's K-Jfol Skin Loader
+
+ ...brought to you by mETz, Crix and Sush
+---------------------------------------------------------------
+
+We would like to thank you for downloading/installing this little piece
+of art. This is our first try with K-Jfol skinning and we hope you
+enjoy the result.
+
+Features
+--------
+The skin features the following buttons/functions
+
+ - play, pause, stop
+ - fastforward, rewind
+ - previous and next track
+ - volume and pitch
+ - seeker
+ - playlist button
+ - repeat
+ - preferences
+ - about
+ - window controls: minimize, close
+ - works with Noatun and XMMS
+
+What you could possibly miss
+----------------------------
+Things this skin doesn't support:
+
+ - There is no skinned playlist because playlists suck with K-Jfol skins.
+
+ - There is no skinned dockmode.
+
+ - It lacks support for original K-Jfol player. Sounds odd but it's true.
+ This skin does not work with that player. During design we forgot to
+ make sizes divisible by four which seems to be a quite important skin
+ spec. When we actually realized that it was already too late to make
+ the appropriate changes :/
+ However it works very well with Noatun's K-Jfol skin loader.
+ If there is anyone out there who want's to use this skin for
+ the original K-Jfol player he might drop us a line and we'll see.
+
+
+Credits
+-------
+ - Design concept, scripting and idea:
+ Stefan "mETz" Gehn, <sgehn@gmx.net>
+
+ - Executive artist:
+ Christian "Crix" Hoffmann
+ be sure to visit http://www.crixensgfxcorner.de.vu
+
+ - Assistance and suggestions:
+ Sascha "Sush" Hoffmann
+
+Special thanks to
+-----------------
+ - Lars "die.viper" Kluge for improving the first scribbles of this skin
+ be sure to visit http://www.die-viper.de
+
+ - You for reading this file and trying this skin
+
+Licensing stuff
+---------------
+This skin is copyright 2002 by the people mentioned in the Credits above
+and distributed under the Clarified Artistic Licence.
+
+The most important things to mention:
+
+ - you are allowed to redistribute it
+
+ - you are allowed to alter it but please give credits and give it a new name
+
+
+Hey! Why are you wasting your time with this readme? Just go ahead and try the skin!
+Have fun!
+
+ -mETz, Crix and Sush, 01/25/2002
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/active.png b/noatun/modules/kjofol-skin/skins/HexoBronx/active.png
new file mode 100644
index 00000000..f82388e8
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/active.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/eckig_font.png b/noatun/modules/kjofol-skin/skins/HexoBronx/eckig_font.png
new file mode 100644
index 00000000..ad137023
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/eckig_font.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/font.png b/noatun/modules/kjofol-skin/skins/HexoBronx/font.png
new file mode 100644
index 00000000..ba1c2f7e
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/font.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/inactive.png b/noatun/modules/kjofol-skin/skins/HexoBronx/inactive.png
new file mode 100644
index 00000000..2c19d06a
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/inactive.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/mask.png b/noatun/modules/kjofol-skin/skins/HexoBronx/mask.png
new file mode 100644
index 00000000..d9e376c1
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/mask.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/pitch.png b/noatun/modules/kjofol-skin/skins/HexoBronx/pitch.png
new file mode 100644
index 00000000..97c9c464
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/pitch.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/splash.png b/noatun/modules/kjofol-skin/skins/HexoBronx/splash.png
new file mode 100644
index 00000000..d7433a5f
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/splash.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/time_font.png b/noatun/modules/kjofol-skin/skins/HexoBronx/time_font.png
new file mode 100644
index 00000000..bd409d7f
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/time_font.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/volume.png b/noatun/modules/kjofol-skin/skins/HexoBronx/volume.png
new file mode 100644
index 00000000..1db32364
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/volume.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/HexoBronx/volume_pitch_font.png b/noatun/modules/kjofol-skin/skins/HexoBronx/volume_pitch_font.png
new file mode 100644
index 00000000..df0160f9
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/HexoBronx/volume_pitch_font.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/Makefile.am b/noatun/modules/kjofol-skin/skins/Makefile.am
new file mode 100644
index 00000000..47e5721d
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/Makefile.am
@@ -0,0 +1,3 @@
+
+SUBDIRS=kjofol phong vibrocentric HexoBronx
+
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/Makefile.am b/noatun/modules/kjofol-skin/skins/kjofol/Makefile.am
new file mode 100644
index 00000000..f549bccc
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/Makefile.am
@@ -0,0 +1,11 @@
+
+skin_DATA = kjofol.dck kjofol.pl kjofol.rc kjofol.wsh sgdock2.png \
+ sgdock.png sgdocksk.png sgdockvp.png sgeq.png sg_num.png \
+ sgpitch.png sgpitchp.png sgplist2.png sgplist.png sg.png \
+ sgpres1.png sgpres2.png sgpres3.png sg_seek.bmp sg_seek.png \
+ sg_text.png sgvolnum.png sgvol.png sgvolpos.png sgwshad2.png \
+ sgwshad.png sgwshdsk.png sgwshvol.png sgwshvp.png
+
+skindir = $(kde_datadir)/noatun/skins/kjofol/kjofol
+EXTRA_DIST = $(skin_DATA)
+
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/kjofol.dck b/noatun/modules/kjofol-skin/skins/kjofol/kjofol.dck
new file mode 100644
index 00000000..7af3b666
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/kjofol.dck
@@ -0,0 +1,62 @@
+# Kjofol Default resource file - DOCKING MODE
+
+# Command : BackgroundImage <Name of .BMP file>
+# Desc. : This is the back image.
+BackgroundImage sgdock.png
+
+# Command : BackgroundImageInactive <Name of .BMP file>
+# Desc. : This is the back image when the window is not selected.
+# : If you do not want this feature, just put the same name as
+# : BackgroundImage
+BackgroundImageInactive sgdock.png
+
+# Command : BackgroundImagePressed[1-3] <Name of .BMP file>
+# Desc. : This is the back images when all the buttons are pressed
+# : Used if you use the BMP option in the buttons options.
+# : You can have 3 backimages so you can do nifty things for the
+# : buttons =)
+BackgroundImagePressed1 sgdock2.png
+
+FontImage sg_text.png
+FontSize 5 9
+FontSpacing 0
+FontTransparent 0
+
+# Command : VolumeControlType <BAR/BMP>
+# Desc. : Put BAR if you want a bar style volume control, BMP if you want
+# : to customize it by a BMP animation file ...
+VolumeControlType BMP
+VolumeControlImage sgwshvol.png
+VolumeControlImagePosition sgdockvp.png
+VolumeControlImageXSize 30
+VolumeControlImageNb 28
+VolumeControlButton 100 17 129 23 vOLUME
+
+# Command : [Option]Button <Position X> <Position Y> <End X> <End Y> <Tooltip Text> <DARKEN/BMP[?]>
+# The DARKEN option just dark the button when the user click on it.
+# The BMP[?] option use the BackgroundImagePressed[?] defined above.
+# Be sure to define a BackgroundImagePressed if you use the BMP option !!
+AboutButton 10 12 27 32 About BMP1
+OpenFileButton 14 73 32 91 Open BMP1
+StopButton 14 56 32 73 Stop BMP1
+PlayButton 14 37 32 55 Play BMP1
+PreviousSongButton 14 92 33 109 PreviousSong BMP1
+NextSongButton 14 110 33 127 NextSong BMP1
+UnDockModeButton 14 127 33 140 UnDock BMP1
+
+# Command : FilenameWindow
+# Desc. : This is the window where the file name appears
+FilenameWindow 65 23 141 32
+
+SeekRegion 56 18 84 21
+SeekImage sgdocksk.png
+
+# Command : AnalyzerWindow <X> <Y> <MaxX> <MaxY> <TipTool>
+# Desc. : Spectrum Analyzer area. If you doesn't want one, just comment the
+# line ...
+AnalyzerWindow 41 23 62 32 Analyzer
+# Command : AnalyzerColor <Red> <Green> <Blue>
+# Desc. : Spectrum Analyzer color. Colors range are 0-255.
+AnalyzerColor 81 94 81
+
+IncludeRCFile kjofol.pl \ No newline at end of file
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/kjofol.pl b/noatun/modules/kjofol-skin/skins/kjofol/kjofol.pl
new file mode 100644
index 00000000..e7eaf45a
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/kjofol.pl
@@ -0,0 +1,39 @@
+# Playlist screen section
+PlaylistBmp sgplist.png
+PlaylistBmpPressed sgplist2.png
+
+# Command : PlaylistWindowText <X> <Y> <MaxX> <MaxY>
+# Desc. : Where all the playlist files will be placed on the playlist
+# screen
+PlaylistWindowText 43 42 170 211
+PlaylistWindowFontName Arial
+PlaylistWindowFontSize 12
+PlaylistWindowFontYSpacing 10
+PlaylistWindowNbSelectedTrack 79 215 93 225
+PlaylistWindowNbTotalTracks 79 230 93 240
+
+PlaylistWindowLinkButton 180 33 194 48
+PlaylistWindowCloseButton 174 21 187 35
+PlaylistWindowUpButton 146 226 179 242
+PlaylistWindowDownButton 146 243 179 259
+PlaylistWindowShuffleButton 189 175 233 185
+PlaylistWindowSortButton 188 144 234 155
+PlaylistWindowSortInverseButton 187 159 234 170
+PlaylistWindowMinimizeButton 184 50 193 59
+PlaylistWindowAddButton 188 74 234 84
+PlaylistWindowDelButton 189 87 234 99
+PlaylistWindowResetButton 192 102 232 113
+PlaylistWindowLoadPlaylistButton 191 129 231 141
+PlaylistWindowSavePlaylistButton 189 115 232 127
+PlaylistWindowSelectionUpButton 27 42 41 55
+PlaylistWindowSelectionDownButton 27 198 41 211
+PlaylistWindowAboutButton 194 29 217 59
+PlaylistWindowPlayButton 113 285 147 311
+PlaylistWindowPreviousButton 76 285 109 310
+PlaylistWindowNextButton 153 283 185 308
+PlaylistWindowPauseButton 187 266 217 290
+PlaylistWindowStopButton 198 239 234 262
+PlaylistWindowOpenButton 202 210 236 233
+PlaylistWindowColor 0 0 0
+PlaylistWindowCurrentTrackColor 0 0 136
+PlaylistWindowCurrentSelectionColor 153 207 181
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/kjofol.rc b/noatun/modules/kjofol-skin/skins/kjofol/kjofol.rc
new file mode 100644
index 00000000..daf99638
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/kjofol.rc
@@ -0,0 +1,150 @@
+# If you want to know all secrets about K-Jofol's resources files,
+# Just check out the great tutorial at : http://www.angelfire.com/mo/nequiem/
+
+# Command : About <text>
+# Desc. : This text will appear when the users selects "About ..."
+# : You have 5 lines of text ...
+About
+About Made by Steve Gedikian for K-Jofol.
+
+# Command : BackgroundImage <Name of .BMP file>
+# Desc. : This is the back image.
+BackgroundImage sg.png
+
+# Command : BackgroundImageInactive <Name of .BMP file>
+# Desc. : This is the back image when the window is not selected.
+# : If you do not want this feature, just put the same name as
+# : BackgroundImage
+BackgroundImageInactive sg.png
+
+# Command : BackgroundImagePressed[1-3] <Name of .BMP file>
+# Desc. : This is the back images when all the buttons are pressed
+# : Used if you use the BMP option in the buttons options.
+# : You can have 3 backimages so you can do nifty things for the
+# : buttons =)
+BackgroundImagePressed1 sgpres1.png
+BackgroundImagePressed2 sgpres2.png
+BackgroundImagePressed3 sgpres3.png
+
+FontImage sg_text.png
+FontSize 5 9
+FontSpacing 0
+FontTransparent 0
+TimeFontImage sg_num.png
+TimeFontSize 8 9
+TimeFontSpacing 0
+TimeFontTransparent 0
+
+# Command : VolumeControlType <BAR/BMP>
+# Desc. : Put BAR if you want a bar style volume control, BMP if you want
+# : to customize it by a BMP animation file ...
+# VolumeControlType BAR
+# VolumeControlImage btn_vol.bmp
+# VolumeControlButton 235 166 275 174 vOLUME
+VolumeControlType BMP
+VolumeControlImage sgvol.png
+VolumeControlImagePosition sgvolpos.png
+VolumeControlImageXSize 86
+VolumeControlImageNb 83
+VolumeControlButton 211 133 296 209 vOLUME
+
+SeekImage sg_seek.png
+SeekRegion 91 22 300 127
+
+# Pitch control definition
+PitchText 243 93 260 102
+PitchControlImage sgpitch.png
+PitchControlImageXSize 58
+PitchControlImageNb 36
+PitchControlButton 222 68 280 125 pITCH
+PitchControlImagePosition sgpitchp.png
+
+PitchFontImage sgvolnum.png
+PitchFontSize 5 10
+PitchFontSpacing 0
+PitchFontTransparent 0
+
+# Command : [Option]Button <Position X> <Position Y> <End X> <End Y> <Tooltip Text> <DARKEN/BMP[?]>
+# The DARKEN option just dark the button when the user click on it.
+# The BMP[?] option use the BackgroundImagePressed[?] defined above.
+# Be sure to define a BackgroundImagePressed if you use the BMP option !!
+CloseButton 310 116 322 128 Close BMP1
+MinimizeButton 306 132 319 140 Minimize BMP1
+AboutButton 306 83 334 116 About BMP1
+OpenFileButton 25 8 61 26 Open BMP1
+StopButton 54 16 81 51 Stop BMP2
+PlayButton 28 27 57 56 Play BMP3
+#RewindButton 11 49 43 78 FastRewind BMP1
+#ForwardButton 45 49 73 77 FastForward BMP1
+PreviousSongButton 11 49 43 78 PreviousSong BMP1
+NextSongButton 45 49 73 77 NextSong BMP1
+PauseButton 9 18 31 51 Pause BMP2
+PreferencesButton 267 43 300 51 Options BMP1
+EqualizerButton 125 152 135 159 Equalizer BMP1
+EqualizerResetButton 175 104 200 112 Reset BMP1
+EqualizerOnButton 141 150 152 157 On BMP1
+EqualizerOffButton 156 150 167 157 Off BMP1
+RepeatButton 278 51 308 59 Repeat BMP1
+PlaylistButton 292 67 325 77 Playlist BMP1
+
+# Command : EqualizerWindow <X> <Y> <MaxX> <MaxY> <TipTool> <# of bands> <X-Space between bands>
+EqualizerWindow 111 115 180 148 Equalizer 12 6
+# Command : EqualizerBmp <X-Size> <Nb Equalizer> <BMP File>
+EqualizerBmp 4 17 sgeq.png
+
+# Inactive Zone
+# This put a button that does NOTHING :)
+# In fact, this option is very useful for designing the "Spectrum Analyzer" and
+# the "Oscilliscope" buttons because they are not rectangulars.
+# Inactive zones for the Spectrum Analyzer button
+InactiveZone 91 124 159 136
+InactiveZone 99 136 159 152
+InactiveZone 113 153 163 166
+# Inactive zones for the Oscilloscope butoon
+InactiveZone 159 123 198 137
+InactiveZone 159 138 190 149
+InactiveZone 163 151 177 160
+# Remember to put first your inactive zones BEFORE the buttons
+
+# Now that we have put our inactive zones, we could safely put the buttons
+SpectrumAnalyzerButton 68 123 167 185 SpectrumAnalyzer
+OscilloscopeButton 165 122 211 172 Oscilloscope
+
+# Dock Mode
+DockModeButton 243 27 268 36 DockMode BMP1
+DockModeRCFile kjofol.dck
+# DockModePosition : 0 - Upper Left 1 - Upper Right
+# 2 - Bottom Left 3 - Bottom Right
+DockModePosition 0
+DockModePositionXY -33 -38
+WinshadeModeRCFile kjofol.wsh
+WinshadeModePosition 1
+WinshadeModePositionXY -405 -9
+
+# Command : FilenameWindow
+# Desc. : This is the window where the file name appears
+FilenameWindow 96 80 200 89
+
+MP3KbpsWindow 93 90 110 97
+MP3KbpsString
+MP3KhzWindow 135 90 146 97
+Mp3KhzString
+
+MP3TimeWindow 124 50 165 59
+CurrentTrackWindow 191 90 204 97
+
+# Command : AnalyzerWindow <X> <Y> <MaxX> <MaxY> <TipTool>
+# Desc. : Spectrum Analyzer area. If you doesn't want one, just comment the
+# line ...
+AnalyzerWindow 106 61 184 78 Analyzer
+# Command : AnalyzerColor <Red> <Green> <Blue>
+# Desc. : Spectrum Analyzer color. Colors range are 0-255.
+AnalyzerColor 81 94 81
+
+VolumeFontImage sgvolnum.png
+VolumeFontSize 5 10
+VolumeFontSpacing 0
+VolumeFontTransparent 0
+VolumeText 247 168 265 176
+
+IncludeRCFile kjofol.pl
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/kjofol.wsh b/noatun/modules/kjofol-skin/skins/kjofol/kjofol.wsh
new file mode 100644
index 00000000..2910f636
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/kjofol.wsh
@@ -0,0 +1,63 @@
+# Kjofol Default resource file - WINDOW SHADE MODE
+
+# Command : BackgroundImage <Name of .BMP file>
+# Desc. : This is the back image.
+BackgroundImage sgwshad.png
+
+# Command : BackgroundImageInactive <Name of .BMP file>
+# Desc. : This is the back image when the window is not selected.
+# : If you do not want this feature, just put the same name as
+# : BackgroundImage
+BackgroundImageInactive sgwshad.png
+
+# Command : BackgroundImagePressed[1-3] <Name of .BMP file>
+# Desc. : This is the back images when all the buttons are pressed
+# : Used if you use the BMP option in the buttons options.
+# : You can have 3 backimages so you can do nifty things for the
+# : buttons =)
+BackgroundImagePressed1 sgwshad2.png
+
+FontImage sg_text.png
+FontSize 5 9
+FontSpacing 0
+FontTransparent 0
+
+# Command : VolumeControlType <BAR/BMP>
+# Desc. : Put BAR if you want a bar style volume control, BMP if you want
+# : to customize it by a BMP animation file ...
+VolumeControlType BMP
+VolumeControlImage sgwshvol.png
+VolumeControlImagePosition sgwshvp.png
+VolumeControlImageXSize 30
+VolumeControlImageNb 28
+VolumeControlButton 192 18 221 22 vOLUME
+
+
+# Command : [Option]Button <Position X> <Position Y> <End X> <End Y> <Tooltip Text> <DARKEN/BMP[?]>
+# The DARKEN option just dark the button when the user click on it.
+# The BMP[?] option use the BackgroundImagePressed[?] defined above.
+# Be sure to define a BackgroundImagePressed if you use the BMP option !!
+AboutButton 10 13 26 29 About BMP1
+PlayButton 230 13 247 29 Play BMP1
+StopButton 249 13 265 30 Stop BMP1
+OpenFileButton 267 14 284 28 Open BMP1
+PreviousSongButton 285 14 302 30 PreviousSong BMP1
+NextSongButton 303 13 319 29 NextSong BMP1
+UnDockModeButton 322 14 336 28 UnDock BMP1
+
+# Command : FilenameWindow
+# Desc. : This is the window where the file name appears
+FilenameWindow 38 16 114 25
+
+SeekRegion 148 19 176 22
+SeekImage sgwshdsk.png
+
+# Command : AnalyzerWindow <X> <Y> <MaxX> <MaxY> <TipTool>
+# Desc. : Spectrum Analyzer area. If you doesn't want one, just comment the
+# line ...
+AnalyzerWindow 117 16 130 25 Analyzer
+# Command : AnalyzerColor <Red> <Green> <Blue>
+# Desc. : Spectrum Analyzer color. Colors range are 0-255.
+AnalyzerColor 81 94 81
+
+IncludeRCFile kjofol.pl \ No newline at end of file
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sg.png b/noatun/modules/kjofol-skin/skins/kjofol/sg.png
new file mode 100644
index 00000000..6195fbe2
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sg.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sg_num.png b/noatun/modules/kjofol-skin/skins/kjofol/sg_num.png
new file mode 100644
index 00000000..ec1b6df8
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sg_num.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sg_seek.bmp b/noatun/modules/kjofol-skin/skins/kjofol/sg_seek.bmp
new file mode 100644
index 00000000..209014fa
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sg_seek.bmp
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sg_seek.png b/noatun/modules/kjofol-skin/skins/kjofol/sg_seek.png
new file mode 100644
index 00000000..92145805
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sg_seek.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sg_text.png b/noatun/modules/kjofol-skin/skins/kjofol/sg_text.png
new file mode 100644
index 00000000..a867da1b
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sg_text.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgdock.png b/noatun/modules/kjofol-skin/skins/kjofol/sgdock.png
new file mode 100644
index 00000000..49663245
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgdock.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgdock2.png b/noatun/modules/kjofol-skin/skins/kjofol/sgdock2.png
new file mode 100644
index 00000000..c932db01
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgdock2.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgdocksk.png b/noatun/modules/kjofol-skin/skins/kjofol/sgdocksk.png
new file mode 100644
index 00000000..9c6e5209
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgdocksk.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgdockvp.png b/noatun/modules/kjofol-skin/skins/kjofol/sgdockvp.png
new file mode 100644
index 00000000..056f29e4
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgdockvp.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgeq.png b/noatun/modules/kjofol-skin/skins/kjofol/sgeq.png
new file mode 100644
index 00000000..5d2ffeb6
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgeq.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgpitch.png b/noatun/modules/kjofol-skin/skins/kjofol/sgpitch.png
new file mode 100644
index 00000000..e0fff521
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgpitch.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgpitchp.png b/noatun/modules/kjofol-skin/skins/kjofol/sgpitchp.png
new file mode 100644
index 00000000..fa9b3121
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgpitchp.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgplist.png b/noatun/modules/kjofol-skin/skins/kjofol/sgplist.png
new file mode 100644
index 00000000..de165370
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgplist.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgplist2.png b/noatun/modules/kjofol-skin/skins/kjofol/sgplist2.png
new file mode 100644
index 00000000..2516a029
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgplist2.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgpres1.png b/noatun/modules/kjofol-skin/skins/kjofol/sgpres1.png
new file mode 100644
index 00000000..555ffa90
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgpres1.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgpres2.png b/noatun/modules/kjofol-skin/skins/kjofol/sgpres2.png
new file mode 100644
index 00000000..70bae784
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgpres2.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgpres3.png b/noatun/modules/kjofol-skin/skins/kjofol/sgpres3.png
new file mode 100644
index 00000000..f440f739
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgpres3.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgvol.png b/noatun/modules/kjofol-skin/skins/kjofol/sgvol.png
new file mode 100644
index 00000000..85d2a5dd
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgvol.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgvolnum.png b/noatun/modules/kjofol-skin/skins/kjofol/sgvolnum.png
new file mode 100644
index 00000000..ecb5aa1c
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgvolnum.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgvolpos.png b/noatun/modules/kjofol-skin/skins/kjofol/sgvolpos.png
new file mode 100644
index 00000000..b6e08a9f
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgvolpos.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgwshad.png b/noatun/modules/kjofol-skin/skins/kjofol/sgwshad.png
new file mode 100644
index 00000000..ab603003
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgwshad.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgwshad2.png b/noatun/modules/kjofol-skin/skins/kjofol/sgwshad2.png
new file mode 100644
index 00000000..0a456cea
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgwshad2.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgwshdsk.png b/noatun/modules/kjofol-skin/skins/kjofol/sgwshdsk.png
new file mode 100644
index 00000000..b63f09e2
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgwshdsk.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgwshvol.png b/noatun/modules/kjofol-skin/skins/kjofol/sgwshvol.png
new file mode 100644
index 00000000..394134fd
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgwshvol.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/kjofol/sgwshvp.png b/noatun/modules/kjofol-skin/skins/kjofol/sgwshvp.png
new file mode 100644
index 00000000..e072545a
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/kjofol/sgwshvp.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/phong/Makefile.am b/noatun/modules/kjofol-skin/skins/phong/Makefile.am
new file mode 100644
index 00000000..fb299a01
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/Makefile.am
@@ -0,0 +1,8 @@
+
+skin_DATA = p_eq.png p_numbers.png p_propos.png p_volpos.png phong.wsh \
+ p_main.png p_playback.png p_text.png phong.dck phong_readme.txt \
+ p_mainback.png p_playlist.png p_volbar.png phong.rc
+
+skindir = $(kde_datadir)/noatun/skins/kjofol/phong
+EXTRA_DIST = $(skin_DATA)
+
diff --git a/noatun/modules/kjofol-skin/skins/phong/p_eq.png b/noatun/modules/kjofol-skin/skins/phong/p_eq.png
new file mode 100644
index 00000000..7b5390a0
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/p_eq.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/phong/p_main.png b/noatun/modules/kjofol-skin/skins/phong/p_main.png
new file mode 100644
index 00000000..1e1b5110
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/p_main.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/phong/p_mainback.png b/noatun/modules/kjofol-skin/skins/phong/p_mainback.png
new file mode 100644
index 00000000..1f12b323
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/p_mainback.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/phong/p_numbers.png b/noatun/modules/kjofol-skin/skins/phong/p_numbers.png
new file mode 100644
index 00000000..51865529
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/p_numbers.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/phong/p_playback.png b/noatun/modules/kjofol-skin/skins/phong/p_playback.png
new file mode 100644
index 00000000..233b681d
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/p_playback.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/phong/p_playlist.png b/noatun/modules/kjofol-skin/skins/phong/p_playlist.png
new file mode 100644
index 00000000..b98b0a9b
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/p_playlist.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/phong/p_propos.png b/noatun/modules/kjofol-skin/skins/phong/p_propos.png
new file mode 100644
index 00000000..e5ed17e3
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/p_propos.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/phong/p_text.png b/noatun/modules/kjofol-skin/skins/phong/p_text.png
new file mode 100644
index 00000000..8254b6d2
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/p_text.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/phong/p_volbar.png b/noatun/modules/kjofol-skin/skins/phong/p_volbar.png
new file mode 100644
index 00000000..61c4ea0e
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/p_volbar.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/phong/p_volpos.png b/noatun/modules/kjofol-skin/skins/phong/p_volpos.png
new file mode 100644
index 00000000..0052097f
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/p_volpos.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/phong/phong.dck b/noatun/modules/kjofol-skin/skins/phong/phong.dck
new file mode 100644
index 00000000..f4fadf52
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/phong.dck
@@ -0,0 +1,26 @@
+BackgroundImage ../kjofol/sgdock.png
+BackgroundImageInactive ../kjofol/sgdock.png
+BackgroundImagePressed1 ../kjofol/sgdock2.png
+FontImage ../kjofol/sg_text.png
+FontSize 5 9
+FontSpacing 0
+FontTransparent 0
+VolumeControlType BMP
+VolumeControlImage ../kjofol/sgwshvol.png
+VolumeControlImagePosition ../kjofol/sgdockvp.png
+VolumeControlImageXSize 30
+VolumeControlImageNb 28
+VolumeControlButton 100 17 129 23 vOLUME
+AboutButton 10 12 27 32 About BMP1
+OpenFileButton 14 73 32 91 Open BMP1
+StopButton 14 56 32 73 Stop BMP1
+PlayButton 14 37 32 55 Play BMP1
+PreviousSongButton 14 92 33 109 PreviousSong BMP1
+NextSongButton 14 110 33 127 NextSong BMP1
+UnDockModeButton 14 127 33 140 UnDock BMP1
+FilenameWindow 65 23 141 32
+SeekRegion 56 18 84 21
+SeekImage ../kjofol/sgdocksk.png
+AnalyzerWindow 41 23 62 32 Analyzer
+AnalyzerColor 81 94 81
+IncludeRCFile noirotic.pl \ No newline at end of file
diff --git a/noatun/modules/kjofol-skin/skins/phong/phong.rc b/noatun/modules/kjofol-skin/skins/phong/phong.rc
new file mode 100644
index 00000000..ee90005c
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/phong.rc
@@ -0,0 +1,104 @@
+About : : : phong : : :
+About by misery in motion 1999
+About misery_in_motion@hotmail.com
+About http://misery.mp3s.com.au
+
+BackgroundImage p_main.png
+BackgroundImageInactive p_main.png
+BackgroundImagePressed1 p_mainback.png
+
+
+FontImage p_text.png
+FontSize 5 9
+FontSpacing 1
+FontTransparent 0
+TimeFontImage p_numbers.png
+TimeFontSize 8 11
+TimeFontSpacing 1
+TimeFontTransparent 0
+
+VolumeControlType BMP
+VolumeControlImage p_volbar.png
+VolumeControlImagePosition p_volpos.png
+VolumeControlImageXSize 37
+VolumeControlImageNb 20
+VolumeControlButton 190 53 227 181 vOLUME
+
+SeekRegion 68 55 193 173
+SeekImage p_propos.png
+
+CloseButton 185 41 196 51 Close BMP1
+MinimizeButton 174 32 183 42 Minimize BMP1
+AboutButton 107 194 147 229 About BMP1
+OpenFileButton 237 95 257 130 Open BMP1
+StopButton 28 137 53 171 Stop BMP1
+PlayButton 24 96 45 130 Play BMP1
+#RewindButton 16 35 32 48 FastRewind BMP1
+#ForwardButton 33 11 49 25 FastForward BMP1
+PreviousSongButton 49 31 76 56 PreviousSong BMP1
+NextSongButton 49 172 77 202 NextSong BMP1
+PauseButton 32 60 53 91 Pause BMP1
+PreferencesButton 198 2 205 11 Options BMP1
+#EqualizerButton 76 103 82 107 Equalizer BMP1
+EqualizerResetButton 121 150 153 161 Reset BMP1
+EqualizerOnButton 98 130 107 142 On BMP1
+EqualizerOffButton 146 130 159 141 Off BMP1
+RepeatButton 221 66 253 92 Repeat BMP1
+PlaylistButton 227 131 252 162 Playlist BMP1
+
+FilenameWindow 86 119 168 129
+MP3KbpsWindow 150 108 170 116
+MP3KbpsString
+MP3KhzWindow 88 108 103 116
+Mp3KhzString
+MP3TimeWindow 105 106 153 122
+
+AnalyzerWindow 106 89 147 104 Analyzer
+AnalyzerColor 88 88 88
+EqualizerWindow 109 131 144 145 Equalizer 12 3
+EqualizerBmp 2 12 p_eq.png
+
+DockModeButton 158 23 171 36 DockMode BMP1
+DockModeRCFile phong.dck
+DockModePosition 0
+DockModePositionXY -33 -38
+WinshadeModeRCFile phong.wsh
+WinshadeModePosition 1
+WinshadeModePositionXY -405 -9
+
+PlaylistBmp p_playlist.png
+PlaylistBmpPressed p_playback.png
+PlaylistWindowText 45 88 177 198
+PlaylistWindowFontName Arial
+PlaylistWindowFontSize 12
+PlaylistWindowFontYSpacing 10
+PlaylistWindowNbSelectedTrack 58 211 68 221
+PlaylistWindowNbTotalTracks 83 211 93 221
+
+PlaylistWindowLinkButton 148 15 160 25
+PlaylistWindowCloseButton 174 31 184 41
+PlaylistWindowUpButton 98 67 122 81
+PlaylistWindowDownButton 98 207 122 220
+PlaylistWindowShuffleButton 157 232 174 248
+PlaylistWindowSortButton 115 232 132 248
+PlaylistWindowSortInverseButton 137 232 153 248
+PlaylistWindowMinimizeButton 161 21 172 32
+PlaylistWindowAddButton 48 232 64 248
+PlaylistWindowDelButton 70 232 86 248
+PlaylistWindowResetButton 91 232 107 248
+PlaylistWindowLoadPlaylistButton 41 71 74 78
+PlaylistWindowSavePlaylistButton 127 71 164 78
+PlaylistWindowSelectionUpButton 185 83 195 90
+PlaylistWindowSelectionDownButton 185 199 195 206
+PlaylistWindowAboutButton 140 210 193 220
+PlaylistWindowPlayButton 84 37 100 53
+PlaylistWindowPreviousButton 66 37 82 53
+PlaylistWindowNextButton 138 37 153 53
+PlaylistWindowPauseButton 101 37 118 53
+PlaylistWindowStopButton 120 37 136 53
+PlaylistWindowOpenButton 202 210 236 233
+PlaylistWindowColor 97 97 97
+PlaylistWindowCurrentTrackColor 60 60 60
+PlaylistWindowCurrentSelectionColor 150 150 150
+
+
diff --git a/noatun/modules/kjofol-skin/skins/phong/phong.wsh b/noatun/modules/kjofol-skin/skins/phong/phong.wsh
new file mode 100644
index 00000000..0fc338e7
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/phong.wsh
@@ -0,0 +1,26 @@
+BackgroundImage ../kjofol/sgwshad.png
+BackgroundImageInactive ../kjofol/sgwshad.png
+BackgroundImagePressed1 ../kjofol/sgwshad2.png
+FontImage ../kjofol/sg_text.png
+FontSize 5 9
+FontSpacing 0
+FontTransparent 0
+VolumeControlType BMP
+VolumeControlImage ../kjofol/sgwshvol.png
+VolumeControlImagePosition ../kjofol/sgwshvp.png
+VolumeControlImageXSize 30
+VolumeControlImageNb 28
+VolumeControlButton 192 18 221 22 vOLUME
+AboutButton 10 13 26 29 About BMP1
+PlayButton 230 13 247 29 Play BMP1
+StopButton 249 13 265 30 Stop BMP1
+OpenFileButton 267 14 284 28 Open BMP1
+PreviousSongButton 285 14 302 30 PreviousSong BMP1
+NextSongButton 303 13 319 29 NextSong BMP1
+UnDockModeButton 322 14 336 28 UnDock BMP1
+FilenameWindow 38 16 114 25
+SeekRegion 148 19 176 22
+SeekImage ../kjofol/sgwshdsk.png
+AnalyzerWindow 117 16 130 25 Analyzer
+AnalyzerColor 81 94 81
+IncludeRCFile noirotic.pl \ No newline at end of file
diff --git a/noatun/modules/kjofol-skin/skins/phong/phong_readme.txt b/noatun/modules/kjofol-skin/skins/phong/phong_readme.txt
new file mode 100644
index 00000000..a6030dc9
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/phong/phong_readme.txt
@@ -0,0 +1,62 @@
+This is the original phong skin by misery
+
+Included is his little readme
+
+
+p h o n g 1 . 5
+-------------------------------------------------
+
+well, err... yes, its phong... created in
+photoshop 4 & 5 during the last two months
+
+does not include an individual dock/shade mode,
+but the wsh, rc and dk are set properly so you
+can use the default.
+
+i've also included a rather decent phong wallpaper
+(phong_wallpaper.jpg)
+its in 1024 x 768, but you can use it in other
+resolutions too. for 800x600 just be sure that
+you do not stretch the file, for bigger resolutions
+than 1024 x 768 make also sure to pick a 50% grey
+as background color.
+
+
+other skins by misery
+--------------------------------------------------
+
+winamp 1.x/2.x
+
+miseryamp 1.0 (-)
+phreak 0.667
+xtended 1.0
+stainless 2.0 (*)
+metropolis 2.01 (*)
+pandemonium 1.0
+coldbringer 2.01 (*)
+boost VIII 2.0 (*)
+schubduese 2.0 (*)
+asmith.net 2.0 (*)
+crystal bastard 2.0 (*)
+zorg 2.0 (*)
+
+k-jofol
+
+wintermute 1.0
+wintermute 2.0
+phong 1.5
+
+* winamp 2.0x support
+- shitty skin, removed
+
+distibute this skin freely, but don't delete the
+readme. manipulators and modifiers of this skin
+will be eaten by myself personally :-0.
+
+-------------------------------------------------
+misery, 08.04.99
+
+web: http://misery.mp3s.com.au
+mail: misery_in_motion@hotmail.com
+
+(c) m i s e r y i n m o t i o n ( 9 9 )
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/Makefile.am b/noatun/modules/kjofol-skin/skins/vibrocentric/Makefile.am
new file mode 100644
index 00000000..37461998
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/Makefile.am
@@ -0,0 +1,8 @@
+
+skin_DATA = i_base2.png i_pl.png i_text.png vibrocentric.dck vibrocentric_readme.txt \
+ i_eq.png i_pl2.png i_vol.png vibrocentric.rc i_base.png \
+ i_font.png i_pro.png i_volpos.png vibrocentric.wsh
+
+skindir = $(kde_datadir)/noatun/skins/kjofol/vibrocentric
+EXTRA_DIST = $(skin_DATA)
+
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/i_base.png b/noatun/modules/kjofol-skin/skins/vibrocentric/i_base.png
new file mode 100644
index 00000000..331dc169
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/i_base.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/i_base2.png b/noatun/modules/kjofol-skin/skins/vibrocentric/i_base2.png
new file mode 100644
index 00000000..d32f5460
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/i_base2.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/i_eq.png b/noatun/modules/kjofol-skin/skins/vibrocentric/i_eq.png
new file mode 100644
index 00000000..0174afa7
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/i_eq.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/i_font.png b/noatun/modules/kjofol-skin/skins/vibrocentric/i_font.png
new file mode 100644
index 00000000..7b37dc22
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/i_font.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/i_pl.png b/noatun/modules/kjofol-skin/skins/vibrocentric/i_pl.png
new file mode 100644
index 00000000..1e203bd9
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/i_pl.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/i_pl2.png b/noatun/modules/kjofol-skin/skins/vibrocentric/i_pl2.png
new file mode 100644
index 00000000..748d55cf
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/i_pl2.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/i_pro.png b/noatun/modules/kjofol-skin/skins/vibrocentric/i_pro.png
new file mode 100644
index 00000000..37e30816
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/i_pro.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/i_text.png b/noatun/modules/kjofol-skin/skins/vibrocentric/i_text.png
new file mode 100644
index 00000000..28ddfd39
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/i_text.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/i_vol.png b/noatun/modules/kjofol-skin/skins/vibrocentric/i_vol.png
new file mode 100644
index 00000000..53170e92
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/i_vol.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/i_volpos.png b/noatun/modules/kjofol-skin/skins/vibrocentric/i_volpos.png
new file mode 100644
index 00000000..b40ec693
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/i_volpos.png
Binary files differ
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.dck b/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.dck
new file mode 100644
index 00000000..f4fadf52
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.dck
@@ -0,0 +1,26 @@
+BackgroundImage ../kjofol/sgdock.png
+BackgroundImageInactive ../kjofol/sgdock.png
+BackgroundImagePressed1 ../kjofol/sgdock2.png
+FontImage ../kjofol/sg_text.png
+FontSize 5 9
+FontSpacing 0
+FontTransparent 0
+VolumeControlType BMP
+VolumeControlImage ../kjofol/sgwshvol.png
+VolumeControlImagePosition ../kjofol/sgdockvp.png
+VolumeControlImageXSize 30
+VolumeControlImageNb 28
+VolumeControlButton 100 17 129 23 vOLUME
+AboutButton 10 12 27 32 About BMP1
+OpenFileButton 14 73 32 91 Open BMP1
+StopButton 14 56 32 73 Stop BMP1
+PlayButton 14 37 32 55 Play BMP1
+PreviousSongButton 14 92 33 109 PreviousSong BMP1
+NextSongButton 14 110 33 127 NextSong BMP1
+UnDockModeButton 14 127 33 140 UnDock BMP1
+FilenameWindow 65 23 141 32
+SeekRegion 56 18 84 21
+SeekImage ../kjofol/sgdocksk.png
+AnalyzerWindow 41 23 62 32 Analyzer
+AnalyzerColor 81 94 81
+IncludeRCFile noirotic.pl \ No newline at end of file
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.rc b/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.rc
new file mode 100644
index 00000000..cd2840d2
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.rc
@@ -0,0 +1,105 @@
+
+
+About idoru xt 1 : vibrocentric
+About part of the idoru litestep theme
+About by misery in motion 1999
+About misery_in_motion@mindless.com
+About http://misery.subnet.at
+
+BackgroundImage i_base.png
+BackgroundImageInactive i_base.png
+BackgroundImagePressed1 i_base2.png
+
+
+FontImage i_text.png
+FontSize 5 6
+FontSpacing 0
+FontTransparent 0
+TimeFontImage i_font.png
+TimeFontSize 9 8
+TimeFontSpacing 1
+TimeFontTransparent 0
+
+VolumeControlType BMP
+VolumeControlImage i_vol.png
+VolumeControlImagePosition i_volpos.png
+VolumeControlImageXSize 51
+VolumeControlImageNb 16
+VolumeControlButton 41 19 91 69 vOLUME
+
+SeekRegion 58 65 180 186
+SeekImage i_pro.png
+
+CloseButton 123 49 136 61 Close BMP1
+MinimizeButton 101 191 114 204 Minimize BMP1
+AboutButton 32 79 50 97 About BMP1
+OpenFileButton 144 51 161 69 Open BMP1
+StopButton 183 88 201 105 Stop BMP1
+PlayButton 159 181 186 206 Play BMP1
+#RewindButton 16 35 32 48 FastRewind BMP1
+#ForwardButton 33 11 49 25 FastForward BMP1
+PreviousSongButton 166 63 185 80 PreviousSong BMP1
+NextSongButton 185 149 204 166 NextSong BMP1
+PauseButton 190 117 208 135 Pause BMP1
+PreferencesButton 29 113 47 131 Options BMP1
+#EqualizerButton 76 103 82 107 Equalizer BMP1
+EqualizerResetButton 116 166 123 172 Reset BMP1
+EqualizerOnButton 105 166 114 172 On BMP1
+EqualizerOffButton 125 166 137 172 Off BMP1
+RepeatButton 35 144 54 160 Repeat BMP1
+PlaylistButton 75 185 93 201 Playlist BMP1
+
+FilenameWindow 72 131 169 137
+MP3KbpsWindow 74 117 90 122
+#MP3KbpsString
+MP3KhzWindow 79 123 90 129
+#Mp3KhzString
+MP3TimeWindow 111 118 166 128
+
+AnalyzerWindow 90 90 149 112 Analyzer
+AnalyzerColor 206 208 210
+EqualizerWindow 92 139 150 162 Equalizer 12 5
+EqualizerBmp 4 14 i_eq.png
+
+DockModeButton 53 168 71 186 DockMode BMP1
+DockModeRCFile vibrocentric.dck
+DockModePosition 0
+DockModePositionXY -33 -38
+WinshadeModeRCFile vibrocentric.wsh
+WinshadeModePosition 1
+WinshadeModePositionXY -405 -9
+
+PlaylistBmp i_pl.png
+PlaylistBmpPressed i_pl2.png
+PlaylistWindowText 60 108 176 181
+PlaylistWindowFontName Arial
+PlaylistWindowFontSize 12
+PlaylistWindowFontYSpacing 8
+PlaylistWindowNbSelectedTrack 106 186 120 195
+PlaylistWindowNbTotalTracks 106 198 120 207
+
+PlaylistWindowLinkButton 148 46 161 59
+PlaylistWindowCloseButton 189 82 202 95
+PlaylistWindowUpButton 148 86 158 93
+PlaylistWindowDownButton 148 96 158 103
+PlaylistWindowShuffleButton 136 186 146 196
+PlaylistWindowSortButton 124 186 134 196
+PlaylistWindowSortInverseButton 148 186 159 196
+PlaylistWindowMinimizeButton 124 42 136 55
+PlaylistWindowAddButton 124 198 134 208
+PlaylistWindowDelButton 136 198 146 208
+PlaylistWindowResetButton 148 198 158 208
+PlaylistWindowLoadPlaylistButton 88 210 116 219
+PlaylistWindowSavePlaylistButton 118 210 147 219
+PlaylistWindowSelectionUpButton 44 19 84 38
+PlaylistWindowSelectionDownButton 42 39 84 57
+PlaylistWindowAboutButton 169 59 183 73
+PlaylistWindowPlayButton 157 234 182 257
+PlaylistWindowPreviousButton 31 196 44 209
+PlaylistWindowNextButton 100 238 112 252
+PlaylistWindowPauseButton 75 234 88 247
+PlaylistWindowStopButton 50 221 63 234
+PlaylistWindowOpenButton 21 165 35 178
+PlaylistWindowColor 130 135 139
+PlaylistWindowCurrentTrackColor 164 168 171
+PlaylistWindowCurrentSelectionColor 81 89 94
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.wsh b/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.wsh
new file mode 100644
index 00000000..0fc338e7
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric.wsh
@@ -0,0 +1,26 @@
+BackgroundImage ../kjofol/sgwshad.png
+BackgroundImageInactive ../kjofol/sgwshad.png
+BackgroundImagePressed1 ../kjofol/sgwshad2.png
+FontImage ../kjofol/sg_text.png
+FontSize 5 9
+FontSpacing 0
+FontTransparent 0
+VolumeControlType BMP
+VolumeControlImage ../kjofol/sgwshvol.png
+VolumeControlImagePosition ../kjofol/sgwshvp.png
+VolumeControlImageXSize 30
+VolumeControlImageNb 28
+VolumeControlButton 192 18 221 22 vOLUME
+AboutButton 10 13 26 29 About BMP1
+PlayButton 230 13 247 29 Play BMP1
+StopButton 249 13 265 30 Stop BMP1
+OpenFileButton 267 14 284 28 Open BMP1
+PreviousSongButton 285 14 302 30 PreviousSong BMP1
+NextSongButton 303 13 319 29 NextSong BMP1
+UnDockModeButton 322 14 336 28 UnDock BMP1
+FilenameWindow 38 16 114 25
+SeekRegion 148 19 176 22
+SeekImage ../kjofol/sgwshdsk.png
+AnalyzerWindow 117 16 130 25 Analyzer
+AnalyzerColor 81 94 81
+IncludeRCFile noirotic.pl \ No newline at end of file
diff --git a/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric_readme.txt b/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric_readme.txt
new file mode 100644
index 00000000..c2180678
--- /dev/null
+++ b/noatun/modules/kjofol-skin/skins/vibrocentric/vibrocentric_readme.txt
@@ -0,0 +1,19 @@
+This is the original vibrocentric skin by misery
+
+I couldn't find any readmes....
+but i hope this sentence from the phong skin
+is also okay for this skin
+
+<<
+distibute this skin freely, but don't delete the
+readme. manipulators and modifiers of this skin
+will be eaten by myself personally :-0.
+
+-------------------------------------------------
+misery, 08.04.99
+
+web: http://misery.mp3s.com.au
+mail: misery_in_motion@hotmail.com
+
+(c) m i s e r y i n m o t i o n ( 9 9 )
+>> \ No newline at end of file
diff --git a/noatun/modules/making_plugins b/noatun/modules/making_plugins
new file mode 100644
index 00000000..cb921cc2
--- /dev/null
+++ b/noatun/modules/making_plugins
@@ -0,0 +1,23 @@
+Welcome to the wonderful world of noatun plugin development!
+
+This is just a "notes" file. It won't show you _how_ to do it.
+
+You're free to use the net plugin as a base, I've licensed it under the
+public domain, so you can relicense it however you wish (preferably not
+GPL, though :)
+
+Remember that QObject must derive first.
+
+Do a "return this;" in the PlayList *playlist() const; function, if your class
+is a playlist, otherwise, don't even override that function.
+
+If your playlist can't seem to get activated, you might have left out the
+"const".
+
+For your create_plugin class, it should return Plugin*, not ClassName *:
+Plugin *create_plugin() {...} // good
+MyClass *create_plugin() {...{ // bad
+(this is important)
+
+A lot should be available to you with the 'napp' variable. It returns
+the global NoatunApp* type.
diff --git a/noatun/modules/marquis/Makefile.am b/noatun/modules/marquis/Makefile.am
new file mode 100644
index 00000000..a2fc3e3f
--- /dev/null
+++ b/noatun/modules/marquis/Makefile.am
@@ -0,0 +1,15 @@
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_marquis.la
+
+noatun_marquis_la_SOURCES = marquis.cpp plugin.cpp
+
+noatun_marquis_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_marquis_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la \
+ -lqtmcop -lkmedia2_idl -lsoundserver_idl
+
+noatun_marquis_la_METASOURCES = AUTO
+
+noinst_HEADERS = marquis.h
+
+noatun_modules_marquis_DATA = marquis.plugin
+noatun_modules_marquisdir = $(kde_datadir)/noatun
diff --git a/noatun/modules/marquis/marquis.cpp b/noatun/modules/marquis/marquis.cpp
new file mode 100644
index 00000000..29ca7364
--- /dev/null
+++ b/noatun/modules/marquis/marquis.cpp
@@ -0,0 +1,186 @@
+// marquis.cpp
+//
+// Copyright (C) 2000 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <noatun/pluginloader.h>
+#include "marquis.h"
+#include <noatun/player.h>
+#include <noatun/engine.h>
+#include <kaction.h>
+
+static int getPlayStatus( Player *player )
+{
+ if ( player->isPlaying() )
+ return 1;
+ if ( player->isPaused() )
+ return 2;
+ return 0;
+}
+
+static void setPlayStatus( Player *player, int status )
+{
+ switch( status )
+ {
+ default:
+ case 0:
+ player->stop();
+ break;
+ case 1:
+ player->play();
+ break;
+ case 2:
+ if ( !player->isPaused() )
+ {
+ player->play();
+ player->playpause();
+ }
+ break;
+ }
+}
+
+
+Marquis::Marquis()
+ : KMainWindow(0, "Marquis")
+ , SessionManagement()
+{
+ hide();
+ kdDebug(66666) << k_funcinfo << "number of members == " << memberList->count() << endl;
+
+// for testing: uncomment this and use
+// dcop `dcop | grep noatun` Marquis activateAction dynamicRestore
+// and dynamicSave accordingly.
+// (void) new KAction("Restore", 0, this, SLOT( dynamicRestore() ), actionCollection(), "dynamicRestore" );
+// (void) new KAction("Save", 0, this, SLOT( dynamicSave() ), actionCollection(), "dynamicSave" );
+ connect( napp, SIGNAL( saveYourself() ), SLOT( dynamicSave() ));
+}
+
+Marquis::~Marquis()
+{
+}
+
+void Marquis::restore(void)
+{
+ kdDebug(66666) << k_funcinfo << endl;
+ dynamicRestore();
+}
+
+// unload every window, and save the config as QStringList of those loaded
+void Marquis::saveSessionConfig(KConfig *c)
+{
+ kdDebug(66666) << k_funcinfo << endl;
+
+ Player *player = napp->player();
+ c->writeEntry("Volume", player->volume());
+ c->writeEntry("Loop Style", player->loopStyle());
+ if ( napp->playlist() )
+ c->writeEntry("Playlist Visible", napp->playlist()->listVisible());
+
+ if ( !player->current().isNull() )
+ {
+ KURL songURL = player->current().url();
+ songURL.setPass( QString::null ); // don't save passwords
+ c->writePathEntry("Current Song", songURL.url());
+ }
+ else
+ c->writePathEntry("Current Song", QString::null);
+
+ c->writeEntry("Current Position", player->getTime());
+ c->writeEntry("PlayStatus", getPlayStatus( player ));
+
+ // borrowed from Plugin config dialog
+ QStringList specList;
+
+ QValueList<NoatunLibraryInfo> loaded = napp->libraryLoader()->loaded();
+ for( QValueList<NoatunLibraryInfo>::Iterator i = loaded.begin(); i != loaded.end(); ++i)
+ {
+ if(!specList.contains((*i).specfile)
+ && napp->libraryLoader()->isLoaded((*i).specfile)
+ && (*i).specfile != "marquis.plugin")
+ {
+ specList += (*i).specfile;
+ napp->libraryLoader()->remove((*i).specfile, false);
+ }
+ }
+
+ c->writeEntry("Loaded Plugins", specList);
+}
+
+// get the list of loaded plugins from the config, and load them
+void Marquis::readSessionConfig(KConfig *c)
+{
+ Player *player = napp->player();
+
+ c->setGroup(QString::null);
+
+ // First set volume, then load modules, some module could start
+ // playback and that would be with 100% volume!
+ player->setVolume( c->readNumEntry("Volume", 100) );
+ //player->setVolume( 0 );
+ player->loop( c->readNumEntry("Loop Style", (int) Player::None) );
+
+ QStringList list = c->readListEntry("Loaded Plugins");
+ /*
+ kdDebug(66666) << "Marquis::readSessionConfig()" << endl;
+ for(QStringList::ConstIterator i=list.begin(); i!=list.end(); ++i)
+ kdDebug(66666) << *i << endl;
+ kdDebug(66666) << "Marquis::readSessionConfig() there we go" << endl;
+ */
+ napp->libraryLoader()->loadAll(list);
+
+ if (!napp->playlist())
+ {
+ KMessageBox::error(0,i18n("No playlist plugin was found. " \
+ "Please make sure that Noatun was installed correctly."));
+ napp->quit();
+ return;
+ }
+
+ if ( c->readBoolEntry( "Playlist Visible", false ))
+ napp->playlist()->showList();
+
+ napp->player()->engine()->setInitialized();
+ // TODO: restore the playback state (also save it ;)
+}
+
+void Marquis::dynamicSave()
+{
+ KConfig config( "marquisrc" );
+ saveSessionConfig( &config );
+}
+
+void Marquis::dynamicRestore()
+{
+ KConfig config( "marquisrc" );
+ readSessionConfig( &config );
+}
+
+#include "marquis.moc"
diff --git a/noatun/modules/marquis/marquis.h b/noatun/modules/marquis/marquis.h
new file mode 100644
index 00000000..5b166d57
--- /dev/null
+++ b/noatun/modules/marquis/marquis.h
@@ -0,0 +1,52 @@
+// marquis.h
+//
+// Copyright (C) 2000 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifndef MARQUIS_H
+#define MARQUIS_H
+
+#include <noatun/plugin.h>
+#include <noatun/app.h>
+#include <kmainwindow.h>
+
+class Player;
+
+class Marquis : public KMainWindow, public SessionManagement
+{
+Q_OBJECT
+NOATUNPLUGIND
+public:
+ Marquis();
+ virtual ~Marquis();
+
+ virtual void restore(void);
+private:
+ void saveSessionConfig(KConfig *);
+ void readSessionConfig(KConfig *);
+private slots:
+ void dynamicSave();
+ void dynamicRestore();
+};
+
+#endif
diff --git a/noatun/modules/marquis/marquis.plugin b/noatun/modules/marquis/marquis.plugin
new file mode 100644
index 00000000..7987d7b2
--- /dev/null
+++ b/noatun/modules/marquis/marquis.plugin
@@ -0,0 +1,83 @@
+Filename=noatun_marquis.la
+Author=Neil Stevens
+Site=http://www.derkarl.org/noatun
+Email=neil@qualityassistant.com
+Type=sm
+License=X11-like
+Name=Marquis
+Name[af]=Markies
+Name[ar]=ماركيس
+Name[cs]=Markýz
+Name[eo]=Markizo
+Name[es]=Marqués
+Name[hi]=मरक्यू
+Name[lt]=Markizas
+Name[lv]=Marķīzs
+Name[mk]=Маркиз
+Name[mt]=Markis
+Name[ne]=मार्किज
+Name[ru]=Маркиз
+Name[zh_CN]=侯爵
+Name[zh_HK]=侯爵
+Name[zh_TW]=侯爵
+Comment=Plugin to interact with the Session Manager
+Comment[af]=Inplak na interaksie het met die Sessie Bestuurder
+Comment[az]=İclas idarəcisi ilə interaksiya əlavəsi
+Comment[bg]=Взаимодействие с мениджъра на сесии
+Comment[bs]=Dodatak za interakciju sa upraviteljem sesija
+Comment[ca]=Connector per interactuar amb el gestor de sessió
+Comment[cs]=Modul pro spolupráci se správcem relace
+Comment[cy]=Atodyn i ryngweithredu efo'r Rheolwr Sesiynau
+Comment[da]=Plugin til at fungere sammen med sessionshåndteringen
+Comment[de]=Modul zur Kommunikation mit dem Sitzungsmanager
+Comment[el]=Πρόσθετο για αλληλεπίδραση με το διαχειριστή συνεδρίας
+Comment[eo]=Kromaĵo por interago kun la seancokonservilo
+Comment[es]=Plugin para interactuar con el Administrador de sesiones
+Comment[et]=Seansihalduriga suhtlemise plugin
+Comment[eu]=Sesio kudeatzailearekin komunikatzeko plugin-a
+Comment[fa]=وصله، برای کنش با مدیر نشست
+Comment[fi]=Sovelma istunnonhallinnan ohjaamiseen
+Comment[fr]=Un module pour interagir avec le gestionnaire de session
+Comment[gl]=Extensión para interaccionar co Xestor de Sesións
+Comment[he]=תוסף לתקשורת עם מנהל ההפעלה
+Comment[hi]=सत्र प्रबंधक से इंटरेक्ट करने का प्लगइन
+Comment[hr]=Dodatak za interakciju sa upravljačem seansa
+Comment[hu]=Beépülő modul a munkafolyamat-kezelő beállításához
+Comment[is]=Viðbót til að hafa samband við setustjórann
+Comment[it]=Plugin per interagire con la gestione delle sessioni
+Comment[ja]=セッションマネージャと情報をやり取りするためのプラグイン
+Comment[kk]=Сеанс менеджерімен әрекеттесу плагин модулі
+Comment[km]=កម្មវិធី​ជំនួយ​ដើម្បី​ធ្វើ​អន្តរកម្ម​ជាមួយ​កម្មវិធី​គ្រប់គ្រង​សម័យ
+Comment[ko]=세션 관리자와 작동하는 플러그인
+Comment[lt]=Sąveikos su sesijos tvarkykle priemonė
+Comment[lv]=Iespraudnis sadarbībai ar Sesiju Menedžeri
+Comment[mk]=Приклучок за содејство со менаџерот на сесијата
+Comment[ms]=Plug masuk untuk berinteraksi dengan Pengurus Sesi
+Comment[mt]=Plugin biex jaqbad mal-Manager tas-Sessjoni
+Comment[nb]=Programtillegg for å kommunisere med øktbehandleren
+Comment[nds]=Moduul för't Tosamenwerken mit den Törnpleger
+Comment[ne]=सत्र प्रबन्धकसँग अन्तरक्रिया गर्न प्लगइन
+Comment[nl]=Plugin om te communiceren met de sessiebeheerder
+Comment[nn]=Tilleggsmodul for å kommunisera med økthandsamaren
+Comment[pl]=Wtyczka do współpracy z menedżerem sesji
+Comment[pt]=Um 'plugin' para interagir com o gestor de sessões
+Comment[pt_BR]=Plug-in para interagir com o gerenciador de sessão
+Comment[ro]=Modul de interacţiune cu managerul de sesiune
+Comment[ru]=Модуль взаимодействия с менеджером сеанса
+Comment[se]=Lassemoduvla mainna gulahalla bargovuorrugieđahalliin
+Comment[sk]=Modul pre spoluprácu so Správcom sedenia
+Comment[sl]=Vstavek za delovanje z Upravljalnikom sej
+Comment[sr]=Прикључак за интеракцију са менаџером сесије
+Comment[sr@Latn]=Priključak za interakciju sa menadžerom sesije
+Comment[sv]=Insticksprogram för att samverka med sessionshanteraren
+Comment[ta]=அமர்வு மேலாளருடன் தொடர்பாடும் சொருகுப்பொருள்
+Comment[tg]=Модул барои амали мутақобила бо Мудири Сеанс
+Comment[th]=ปลั๊กอินสำหรับโต้ตอบกับตัวจัดการโปรเซส
+Comment[tr]=Oturum yöneticisi ile etkileşim eklentisi
+Comment[uk]=Втулок для взаємодії з Менеджером сеансів
+Comment[ven]=U plaga u itela u tangana na mulanguli wa tshitenwa
+Comment[xh]=Faka iplagi ukusebenza ngaphakathi noMphathi weNtlanganiso
+Comment[zh_CN]=和会话管理器交互的插件
+Comment[zh_HK]=用於和工作階段管理員互動的插件
+Comment[zh_TW]=用來給工作階段管理程式使用的外掛程式
+Comment[zu]=Faka iplagi ukusebenza ngaphakathi noMphathi weNhlanganiso
diff --git a/noatun/modules/marquis/plugin.cpp b/noatun/modules/marquis/plugin.cpp
new file mode 100644
index 00000000..146b0979
--- /dev/null
+++ b/noatun/modules/marquis/plugin.cpp
@@ -0,0 +1,9 @@
+#include "marquis.h"
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new Marquis();
+ }
+}
diff --git a/noatun/modules/metatag/HANDLED_ITEMS b/noatun/modules/metatag/HANDLED_ITEMS
new file mode 100644
index 00000000..3c369a9e
--- /dev/null
+++ b/noatun/modules/metatag/HANDLED_ITEMS
@@ -0,0 +1,21 @@
+ Here are the mappings between the items returned from KFileMetaInfo, and the
+properties Metatag sets based on those items. These properties may or may be
+set depending on the file's type and contents, and will not be set for
+non-local files or streams.
+
+|=============================================================================|
+| KFileMetaInfo Key | Noatun Property | Description |
+|===================|=================|=======================================|
+| Artist | author | Author |
+| Album | album | Album from which the file originates |
+| Bitrate | bitrate | Average bitrate (kilobits per second) |
+| Channels | channels | Number of audio channels |
+| Comment | comment | Free-form text comment |
+| Date | date | Date the file was released |
+| Genre | genre | Genre of file |
+| Location | location | Location of recording |
+| Organization | organization | |
+| Sample Rate | samplerate | File's audio sample rate (hertz) |
+| Title | title | Title of file |
+| Tracknumber | track | Track number of file on it's album |
+|=============================================================================|
diff --git a/noatun/modules/metatag/Makefile.am b/noatun/modules/metatag/Makefile.am
new file mode 100644
index 00000000..330e9f34
--- /dev/null
+++ b/noatun/modules/metatag/Makefile.am
@@ -0,0 +1,15 @@
+INCLUDES= -I$(top_srcdir)/noatun/library -I$(kde_includes)/arts $(all_includes)
+kde_module_LTLIBRARIES = noatun_metatag.la
+
+noatun_metatag_la_SOURCES = metatag.cpp edit.cpp
+
+noatun_metatag_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_metatag_la_LIBADD = $(LIB_KIO) $(top_builddir)/noatun/library/libnoatun.la \
+ $(top_builddir)/noatun/library/noatuntags/libnoatuntags.la
+
+noatun_metatag_la_METASOURCES = AUTO
+
+noinst_HEADERS = metatag.h edit.h
+
+noatun_modules_metatag_DATA = metatag.plugin
+noatun_modules_metatagdir = $(kde_datadir)/noatun
diff --git a/noatun/modules/metatag/edit.cpp b/noatun/modules/metatag/edit.cpp
new file mode 100644
index 00000000..d5ccb0c4
--- /dev/null
+++ b/noatun/modules/metatag/edit.cpp
@@ -0,0 +1,312 @@
+#include <klocale.h>
+#include <qlayout.h>
+#include <klineedit.h>
+#include <qlabel.h>
+#include <qptrlist.h>
+
+#include <kmimetype.h>
+#include <kicontheme.h>
+#include <qtooltip.h>
+#include <kstringvalidator.h>
+#include <qspinbox.h>
+#include <qhbox.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qcombobox.h>
+#include <qpushbutton.h>
+#include <kdialogbase.h>
+#include <kdebug.h>
+#include <kseparator.h>
+#include <kfilemetainfo.h>
+#include <qvalidator.h>
+
+#include "metatag.h"
+#include "edit.h"
+
+Editor::Editor()
+: KDialogBase((QWidget*)0L, 0, false, i18n("Tag Editor"), Ok | Cancel)
+{
+ mMainWidget = makeMainWidget();
+// mMainWidget->setMinimumWidth(325);
+
+ mGrid = new QGridLayout(mMainWidget, 1, 1, 0, spacingHint(), "Editor::mGrid");
+
+ mGrid->setColStretch(1, 1);
+ mGrid->setColStretch(2, 1);
+
+ QHBox *heading = new QHBox(mMainWidget, "Editor::heading");
+ heading->setSpacing(4);
+ mFileIcon = new QLabel(heading);
+ mFileIcon->setAlignment(AlignVCenter | AlignLeft);
+ mFile = new QLabel(heading);
+ mFile->setAlignment(AlignVCenter | AlignLeft);
+ heading->setStretchFactor(mFile, 2);
+ mGrid->addMultiCellWidget(heading, 0, 0, 0, 2);
+
+ KSeparator *sep = new KSeparator(KSeparator::HLine, mMainWidget);
+ mGrid->addMultiCellWidget(sep, 1, 1, 0, 2);
+
+ mControls.setAutoDelete(true);
+ mNextRow = 2;
+
+ connect(this, SIGNAL(closeClicked()), SLOT(delayedDestruct()));
+ connect(this, SIGNAL(okClicked()), SLOT(save()));
+
+ enableButtonSeparator(true);
+ setFixedHeight(sizeHint().height());
+}
+
+void Editor::open(const PlaylistItem & file)
+{
+ KFileMetaInfo file_meta_info(file.file(), file.mimetype());
+ KFileMetaInfoItem info_item;
+
+ item = file;
+ mDirty = false;
+
+ mFile->setText("<nobr><b>" + file.url().fileName(false) + "</b></nobr>");
+ QToolTip::add(mFile, file.url().prettyURL());
+ mFileIcon->
+ setPixmap(KMimeType::pixmapForURL(file.url(), 0, KIcon::Small));
+
+ if (file.url().isLocalFile()) {
+ QFileInfo file_info(file.file());
+ mFileWritable = file_info.isWritable();
+ }
+ else {
+ // KFileMetaInfo doesn't work on remote files
+ mFileWritable = false;
+ }
+
+ if ( !file_meta_info.isValid() ) // go ahead people, nothing to see here
+ return;
+
+ mControls.append(createControl(file_meta_info, i18n("&Title"), "Title", QVariant::String, false, mMainWidget));
+ mControls.append(createControl(file_meta_info, i18n("&Artist"), "Artist", QVariant::String, false, mMainWidget));
+ mControls.append(createControl(file_meta_info, i18n("A&lbum"), "Album", QVariant::String, false, mMainWidget));
+ mControls.append(createControl(file_meta_info, i18n("&Date"), "Date", QVariant::String, false, mMainWidget));
+ mControls.append(createControl(file_meta_info, i18n("T&rack"), "Tracknumber", QVariant::UInt, false, mMainWidget));
+ mControls.append(createControl(file_meta_info, i18n("&Genre"), "Genre", QVariant::String, false, mMainWidget));
+ mControls.append(createControl(file_meta_info, i18n("Co&mment"), "Comment", QVariant::String, false, mMainWidget));
+}
+
+void Editor::save()
+{
+ // Only write to disk if something actually changed
+ if (!mDirty)
+ {
+ delayedDestruct();
+ return;
+ }
+
+ KFileMetaInfo meta_info(item.file(), item.mimetype());
+ if ( !meta_info.isValid() )
+ {
+ delayedDestruct();
+ return;
+ }
+
+ for (MetaWidget *meta_widget = mControls.first(); meta_widget; meta_widget = mControls.next() )
+ saveControl(meta_info, *meta_widget);
+
+ meta_info.applyChanges();
+
+ emit(saved(item));
+ delayedDestruct();
+}
+
+void Editor::saveControl(KFileMetaInfo& meta_info, const MetaWidget &meta_widget) {
+ QVariant value;
+ const KFileMimeTypeInfo *info = KFileMetaInfoProvider::self()->mimeTypeInfo( meta_info.mimeType() );
+
+ if (!meta_widget.widget->isEnabled())
+ return;
+
+ if (meta_widget.widget->inherits("QSpinBox"))
+ value = static_cast<QSpinBox *>(meta_widget.widget)->value();
+ else if (meta_widget.widget->inherits("QComboBox"))
+ value = static_cast<QComboBox *>(meta_widget.widget)->currentText();
+ else if (meta_widget.widget->inherits("QLineEdit"))
+ value = static_cast<QLineEdit *>(meta_widget.widget)->text();
+
+ QString group = keyGroup(meta_info, meta_widget.key);
+
+ if (group.isNull()) {
+ kdWarning() << "Cannot find group for " << meta_widget.key << endl;
+ return;
+ }
+
+ if (info->groupInfo(group)->itemInfo(meta_widget.key)) {
+ if (info->groupInfo(group)->attributes() & KFileMimeTypeInfo::Addable) {
+ kdDebug(66666) << "Adding group " << group << endl;
+ if (!meta_info.addGroup(group))
+ kdWarning() << "Adding group \"" << group << "\" failed!" << endl;
+ }
+
+ if (info->groupInfo(group)->itemInfo(meta_widget.key)->attributes() & KFileMimeTypeInfo::Addable) {
+ kdDebug(66666) << "Adding key " << meta_widget.key << endl;
+ if (!meta_info.group(group).addItem(meta_widget.key).isValid())
+ kdWarning() << "Adding key \"" << meta_widget.key << "\" failed!" << endl;
+ }
+ }
+
+ if (value.cast(meta_info.item(meta_widget.key).type())) {
+ if (!meta_info.item(meta_widget.key).setValue(value))
+ kdWarning() << "setValue() failed on " << group << "/" << meta_widget.key << endl;
+ }
+ else
+ kdWarning() << "Cannot save " << meta_widget.key << " as required type." << endl;
+}
+
+MetaWidget* Editor::createControl(KFileMetaInfo& meta_info, const QString &label, const QString &key, QVariant::Type default_type, bool optional, QWidget *parent) {
+ QLabel *tmp_label = 0L;
+ KFileMetaInfoItem info_item = meta_info.item(key);
+ QVariant::Type type;
+ MetaWidget *meta_widget = 0L;
+ QValidator *validator = 0L;
+ QString groupName = keyGroup(meta_info, key);
+ bool known_key = ((!groupName.isNull()) && meta_info.group(groupName).contains(key));
+ bool addable = keyAddable(meta_info, key);
+ const KFileMimeTypeInfo *info = KFileMetaInfoProvider::self()->mimeTypeInfo( meta_info.mimeType() );
+
+ // This key isn't a real thing, and we can't even create it
+ if ((!info_item.isEditable()) && (!addable) && optional)
+ return 0;
+
+ if (!groupName.isNull()) {
+ type = info->groupInfo(groupName)->itemInfo(key)->type();
+ }
+ else {
+ type = default_type;
+ }
+
+ // Get the correct validator
+ if ( info && !groupName.isNull() )
+ validator = info->createValidator( groupName, key, parent );
+
+ // meta_widget is used for book-keeping internally
+ meta_widget = new MetaWidget;
+ meta_widget->key = key;
+
+ if ((type == QVariant::Int) || (type == QVariant::UInt)) {
+ // We're an int, make a spin box
+ QSpinBox *box = new QSpinBox(parent);
+
+ // Well, normally metatag doesn't care that much about suffixes
+ // and prefixes, but this is just too easy.
+ box->setPrefix(info_item.prefix());
+ box->setSuffix(info_item.suffix());
+ // Kinda a hack... display " " instead of "0" (think track numbers)
+ box->setSpecialValueText(" ");
+
+ // Do we have a validator?
+ if (validator) {
+ box->setValidator(validator);
+
+ // Is it an integer validator
+ if (validator->inherits("QIntValidator")) {
+ QIntValidator *int_validator = static_cast<QIntValidator *>(validator);
+
+ // FIXME: Why the -hell- doesn't QSpinBox::setValidator() do this??
+ box->setMinValue(int_validator->bottom());
+ box->setMaxValue(int_validator->top());
+ }
+ }
+
+ box->setValue(info_item.value().toInt());
+
+ connect(box, SIGNAL(valueChanged(int)), this, SLOT(modified()));
+ meta_widget->widget = box;
+ }
+ else {
+ // We're not an int, make a KLineEdit/QComboBox, depending on our validator
+ bool combo_box = false;
+
+ if (validator)
+ combo_box = validator->isA("KStringListValidator");
+
+ if (combo_box) {
+ QComboBox *combo = new QComboBox(parent);
+
+ combo->clear();
+ combo->insertStringList(static_cast<
+ KStringListValidator *
+ >(validator)->stringList());
+
+ combo->setCurrentText(info_item.value().toString());
+ connect(combo, SIGNAL(activated(int)), this, SLOT(modified()));
+
+ meta_widget->widget = combo;
+ }
+ else {
+ KLineEdit *edit;
+
+ edit = new KLineEdit(parent);
+ edit->setText(info_item.value().toString());
+ edit->setValidator(validator);
+ connect(edit, SIGNAL(textChanged(const QString &)), this, SLOT(modified()));
+
+ meta_widget->widget = edit;
+ }
+ }
+
+ if (known_key)
+ meta_widget->widget->setEnabled(info_item.isEditable() && mFileWritable);
+ else
+ meta_widget->widget->setEnabled(addable && mFileWritable);
+
+ mGrid->addMultiCellWidget(meta_widget->widget, mNextRow, mNextRow, 1, 2);
+
+ // Add our label. This is the easy part
+ tmp_label = new QLabel(meta_widget->widget, label + ":", parent);
+ mGrid->addWidget(tmp_label, mNextRow, 0);
+
+
+ mNextRow++;
+
+ return meta_widget;
+}
+
+QString Editor::keyGroup(const KFileMetaInfo& i, QString key) {
+ const KFileMimeTypeInfo *info = KFileMetaInfoProvider::self()->mimeTypeInfo( i.mimeType() );
+ QStringList groups = info->supportedGroups();
+
+ for (QStringList::Iterator it = groups.begin();it != groups.end();++it) {
+ if (info->groupInfo(*it)->itemInfo(key)) {
+ return *it;
+ }
+ }
+
+ return QString::null;
+}
+
+bool Editor::keyAddable(const KFileMetaInfo &i, QString key) {
+ const KFileMimeTypeInfo *info = KFileMetaInfoProvider::self()->mimeTypeInfo( i.mimeType() );
+ QStringList groups = info->supportedGroups();
+
+ kdDebug(66666) << "Testing if " << key << " is addable" << endl;
+ for (QStringList::Iterator it = groups.begin();it != groups.end();++it) {
+ if (info->groupInfo(*it)->supportsVariableKeys()) {
+ kdDebug(66666) << "Group " << *it << " supports variable keys" << endl;
+ return true;
+ }
+
+ if (info->groupInfo(*it)->itemInfo(key)) {
+ if (info->groupInfo(*it)->attributes() & KFileMimeTypeInfo::Addable) {
+ kdDebug(66666) << "Group " << *it << " is addable" << endl;
+ return true;
+ }
+
+ if (info->groupInfo(*it)->itemInfo(key)->attributes() & KFileMimeTypeInfo::Addable)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Editor::modified() {
+ mDirty = true;
+}
+
+#include "edit.moc"
diff --git a/noatun/modules/metatag/edit.h b/noatun/modules/metatag/edit.h
new file mode 100644
index 00000000..d6b0ab9c
--- /dev/null
+++ b/noatun/modules/metatag/edit.h
@@ -0,0 +1,55 @@
+#ifndef EDITOR_H
+#define EDITOR_H
+
+#include <kdialogbase.h>
+#include <noatun/playlist.h>
+#include <qptrlist.h>
+#include <qvariant.h>
+
+class KFileMetaInfo;
+class KFileMetaInfoItem;
+class QGridLayout;
+
+struct MetaWidget {
+ QWidget *widget;
+ QString key;
+};
+
+class Editor:public KDialogBase {
+ Q_OBJECT
+ public:
+ Editor();
+
+ signals:
+ void saved(PlaylistItem &);
+
+ public slots:
+ void open(const PlaylistItem & i);
+
+ protected slots:
+ void save();
+ void modified();
+
+ protected:
+ bool keyAddable(const KFileMetaInfo &, QString);
+ QString keyGroup(const KFileMetaInfo &, QString);
+
+ void saveControl(KFileMetaInfo& meta_info, const MetaWidget&);
+ MetaWidget *createControl(KFileMetaInfo& meta_info, const QString &label, const QString &key, QVariant::Type default_type, bool optional, QWidget *parent);
+
+ QPtrList<MetaWidget> mControls;
+
+ QWidget *mMainWidget;
+ QGridLayout *mGrid;
+ int mNextRow;
+
+ bool mFileWritable;
+ bool mDirty;
+
+ QLabel *mFile;
+ QLabel *mFileIcon;
+ const char *filename;
+ PlaylistItem item;
+};
+
+#endif
diff --git a/noatun/modules/metatag/metatag.cpp b/noatun/modules/metatag/metatag.cpp
new file mode 100644
index 00000000..d0531092
--- /dev/null
+++ b/noatun/modules/metatag/metatag.cpp
@@ -0,0 +1,124 @@
+
+#include "metatag.h"
+#include "edit.h"
+
+#include <string.h>
+
+#include <noatun/app.h>
+#include <noatun/stdaction.h>
+
+#include <qfile.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qregexp.h>
+
+#include <klocale.h>
+#include <kaction.h>
+#include <kglobal.h>
+#include <klineedit.h>
+#include <kconfig.h>
+#include <kaction.h>
+#include <kiconloader.h>
+#include <kpopupmenu.h>
+#include <kfilemetainfo.h>
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new MetaTagLoader;
+ }
+}
+
+MetaTagLoader::MetaTagLoader():Plugin()
+{
+ mAction = new KAction(i18n("&Tag Editor..."), "edit", 0, this, SLOT(editTag()), this, "edittag");
+ napp->pluginActionMenu()->insert(mAction);
+}
+
+MetaTagLoader::~MetaTagLoader()
+{
+ napp->pluginActionMenu()->remove(mAction);
+}
+
+void MetaTagLoader::editTag()
+{
+ PlaylistItem i = napp->player()->current();
+
+ if(!i)
+ return;
+
+ Editor *e = new Editor();
+ e->open(i);
+ e->show();
+
+ connect(e, SIGNAL(saved(PlaylistItem &)),
+ SLOT(update(PlaylistItem &)));
+}
+
+bool MetaTagLoader::update(PlaylistItem & item)
+{
+ KFileMetaInfo file_info(item.file(),item.mimetype());
+
+ // Ack, no file info :(
+ if ( !file_info.isValid() )
+ return false;
+
+ if(item.length() == -1) // no value set, set almost correct id3tag time
+ {
+ KFileMetaInfoItem length_item = file_info.item("Length");
+ if(length_item.isValid())
+ {
+ int numVal = length_item.value().toInt();
+ if (numVal != 0)
+ item.setLength(numVal * 1000);
+ }
+ }
+
+ // Yes, this is icky. It maps KFileMetaInfo property names to Noatun's
+ setProperty(file_info, item, "Title", "title");
+ setProperty(file_info, item, "Artist", "author");
+ setProperty(file_info, item, "Album", "album");
+ setProperty(file_info, item, "Genre", "genre");
+ setProperty(file_info, item, "Tracknumber", "track");
+ setProperty(file_info, item, "Date", "date");
+ setProperty(file_info, item, "Comment", "comment");
+ setProperty(file_info, item, "Location", "location");
+ setProperty(file_info, item, "Organization", "organization");
+
+ // Now map the audio properties over
+ setProperty(file_info, item, "Bitrate", "bitrate");
+ setProperty(file_info, item, "Sample Rate", "samplerate");
+ setProperty(file_info, item, "Channels", "channels");
+
+ return true;
+}
+
+bool MetaTagLoader::setProperty(KFileMetaInfo &info, PlaylistItem &item, const QString &key, const QString &property)
+{
+ KFileMetaInfoItem info_item = info.item(key);
+
+ if ( info_item.isValid() )
+ {
+ if (!info_item.value().toString().stripWhiteSpace().isEmpty())
+ {
+ // The item is valid and non-empty, add it
+ item.setProperty(property, info_item.value().toString());
+ }
+ else
+ {
+ // If the info_item is valid, but empty.
+ // This means we know for a fact that this
+ // property has no value. Blow it away.
+ item.clearProperty(property);
+ }
+ return true;
+ }
+
+ // The item isn't valid, so we don't know that it has
+ // no value. Don't remove the property, so we can work
+ // well with other tag readers, like Lucky
+ return false;
+}
+
+#include "metatag.moc"
diff --git a/noatun/modules/metatag/metatag.h b/noatun/modules/metatag/metatag.h
new file mode 100644
index 00000000..ff5c277f
--- /dev/null
+++ b/noatun/modules/metatag/metatag.h
@@ -0,0 +1,30 @@
+#ifndef NID3_H
+#define NID3_H
+
+#include <noatun/player.h>
+#include <noatun/plugin.h>
+#include <noatun/pref.h>
+#include <noatuntags/tags.h>
+
+#include <qobject.h>
+
+class KFileMetaInfo;
+class KAction;
+
+class MetaTagLoader:public QObject, public Tags, public Plugin {
+ Q_OBJECT
+ public:
+ MetaTagLoader();
+ ~MetaTagLoader();
+
+ public slots:
+ bool update(PlaylistItem & item);
+ void editTag();
+
+ private:
+ bool setProperty(KFileMetaInfo &info, PlaylistItem &item, const QString &key, const QString &property);
+ int menuID;
+ KAction *mAction;
+};
+
+#endif
diff --git a/noatun/modules/metatag/metatag.plugin b/noatun/modules/metatag/metatag.plugin
new file mode 100644
index 00000000..0e359350
--- /dev/null
+++ b/noatun/modules/metatag/metatag.plugin
@@ -0,0 +1,125 @@
+Filename=noatun_metatag.la
+Author=Ryan Cumming
+Site=http://noatun.derkarl.org/
+Email=bodnar42@phalynx.dhs.org
+Type=other
+License=Artistic
+Name=Tag Reader
+Name[af]=Etiket Leser
+Name[ar]=قارئ معلومات الرقعة
+Name[br]=Lenner al liketennoù
+Name[bs]=Čitač tagova
+Name[ca]=Lector de marques
+Name[cs]=Čtení tagů
+Name[cy]=Darllenydd Tag
+Name[da]=Mærkelæser
+Name[de]=Meta-Info-Leser
+Name[el]=Αναγνώστης ετικετών
+Name[eo]=Indiklegilo
+Name[es]=Lector de etiquetas
+Name[et]=Tagide lugeja
+Name[eu]=Etiketa irakurlea
+Name[fa]=خوانندۀ برچسب
+Name[fi]=Tagilukija
+Name[fr]=Lecteur de balise
+Name[ga]=Léitheoir Clibe
+Name[gl]=Lector de Etiquetas
+Name[he]=קורא תגיות
+Name[hi]=टैग रीडर
+Name[hr]=Čitač tag-ova
+Name[hu]=Azonosító-olvasó
+Name[is]=Tagabirtir
+Name[it]=Lettore tag
+Name[ja]=タグリーダー
+Name[kk]=Тегтерді оқу
+Name[km]=កម្មវិធី​អាន​ស្លាក
+Name[ko]=태그 리더
+Name[lt]=Etikečių skaityklė
+Name[lv]=Tagu Lasītājs
+Name[mk]=Читач на ознаки
+Name[nb]=Taggleser
+Name[nds]=Beteker-Kieker
+Name[ne]=ट्याग रिडर
+Name[nl]=Tag-lezer
+Name[nn]=Tagglesar
+Name[pa]=ਟੈਗ ਰੀਡਰ
+Name[pl]=Czytnik znaczników
+Name[pt]=Leitor de Marcas
+Name[pt_BR]=Leitor de símbolos
+Name[ro]=Cititor taguri
+Name[ru]=Чтение тегов
+Name[se]=Sárggislohkki
+Name[sk]=Čítačka tagov
+Name[sl]=Bralnik oznak
+Name[sr]=Читач ознака
+Name[sr@Latn]=Čitač oznaka
+Name[sv]=Taggläsare
+Name[ta]=ஒட்டு வாசிப்பாளர்
+Name[tg]=Хонандаи Борчасп
+Name[th]=โปรแกรมอ่านแท็ก
+Name[tr]=İm Okuyucu
+Name[uk]=Читач етикеток
+Name[ven]=Muvhali wa Tag
+Name[zh_CN]=标签读取程序
+Name[zh_HK]=標籤閱讀器
+Name[zh_TW]=標籤閱讀器
+Name[zu]=Umfundi we Tag
+Comment=Support for reading and writing to tags in media files
+Comment[af]=Ondersteun vir lees en om te skryf na etiket in media lêers
+Comment[ar]=دعم قراءة و كتابة الرقع في ملفات الوسائط
+Comment[bg]=Поддръжка на четене и запис на информацията в мултимедийни файловете
+Comment[bs]=Podrška za čitanje i pisanje tagova u multimedijalnim datotekama
+Comment[ca]=Suport per llegir i escriure etiquetes a fitxers multimèdia
+Comment[cs]=Podpora čtení a zápisu tagů v multimediálních souborech
+Comment[cy]=Cynhaliaeth ar gyfer darllen ac ysgrifennu i dagiau mewn ffeiliau cyfryngau
+Comment[da]=Understøttelse af læsning fra og skrivning til mærker i mediefiler
+Comment[de]=Lesen und Schreiben von Meta-Infos (Tags) in Media-Dateien
+Comment[el]=Υποστήριξη για ανάγνωση και εγγραφή στις ετικέτες των αρχείων μέσων
+Comment[eo]=Subteno por legado kaj skribado de indikoj en sonordosieroj
+Comment[es]=Soporte para lectura y escritura de etiquetas en archivos multimedia
+Comment[et]=Multimeediafailide tagide lugemise ja kirjutamise toetus
+Comment[eu]=Euskarri fitxategien etiketak irakurtzeko eta idazteko euskarria
+Comment[fa]=پشتیبانی برای خواندن و نوشتن در برچسبهای پرونده‌های رسانه
+Comment[fi]=Tuki mediatiedostojen tagien lukuun ja kirjoittamiseen
+Comment[fr]=Lecture et écriture des balises ID3 dans les fichiers multimedia
+Comment[gl]=Soporte para a lectura e escritura de etiquetas en ficheiros multimedia
+Comment[he]=תמיכה בקריאה וכתיבה של תגיות בקבצי מדיה
+Comment[hi]=मीडिया फ़ाइलों के टैग पढ़ने तथा लिखने का समर्थन
+Comment[hr]=Podrška za čitanje i pisanje dodatnih informacija (tag-ova) u medijske datoteke
+Comment[hu]=Támogatás médiafájlok azonosítóinak olvasásához és írásához
+Comment[is]=Stuðningur til að lesa og skrifa ID3 tög í MP3 skrám
+Comment[it]=Supporto per la lettura e la scrittura dei tag nei file multimediali
+Comment[ja]=メディアファイル内のタグの読み書きをサポート
+Comment[kk]=Медиа файлдарының тегтерін оқу-жазу құралы
+Comment[km]=ការ​គាំទ្រ​ដើម្បី​អាន និង​សរសេរ​ស្លាក​ក្នុង​ឯកសារ​មេឌៀ
+Comment[ko]=미디어 파일의 태그 읽고 쓰기 지원
+Comment[lt]=Palaiko etikečių skaitymą ir rašymą media bylose
+Comment[lv]=Tagu lasīšanas un rakstīšanas uzturēšana mēdiju failos
+Comment[mk]=Поддршка за читање и запишување на ознаките од мултимедијални датотеки
+Comment[ms]=Sokongan untuk baca dan tulis tag pada fail media
+Comment[nb]=Støtte for lesing og skriving til merkelapper i mediafiler
+Comment[nds]=Ünnerstütten för't Lesen un Schrieven vun Betekers binnen Mediendateien
+Comment[ne]=मिडिया फाइलको ट्यागमा लेख्न र पढ्नका लागि समर्थन
+Comment[nl]=Ondersteuning voor het lezen van en schrijven naar tags in mediabestanden
+Comment[nn]=Støtte for lesing og skriving av taggar i mediefiler
+Comment[pl]=Obsługa odczytu i zapisu znaczników w plikach medialnych
+Comment[pt]=O suporte de leitura/escrita de marcas em ficheiros multimédia
+Comment[pt_BR]=Suporte a leitura e gravação de símbolos em arquivos de mídia
+Comment[ro]=Suport pentru citirea şi scrierea tagurilor în fişiere multimedia
+Comment[ru]=Поддержка чтения и записи тегов в медиафайлах
+Comment[se]=Doarju sárggislohkama ja -čállima mediefiillain
+Comment[sk]=Podpora pre čítanie a zápis do tagov v multimediálnych súboroch
+Comment[sl]=Podpora branju in pisanju oznak za večpredstavnostne datoteke
+Comment[sr]=Подршка за читање и писање ID3 ознака у MP3 фајловима
+Comment[sr@Latn]=Podrška za čitanje i pisanje ID3 oznaka u MP3 fajlovima
+Comment[sv]=Stöd för att läsa och skriva taggar i mediafiler
+Comment[ta]=ஊடகக் கோப்புகளில் குறியொட்டுக்களை எழுத வாசிக்கப்பதற்கு ஆதரவு
+Comment[tg]=Пуштибонии хондан ва навиштани борчаспҳо дар файлҳои расона
+Comment[th]=รองรับการอ่านและเขียนแท็กในแฟ้มสื่อ
+Comment[tr]=Medya dosyalarındaki imleri okumak ve yazmak için destek
+Comment[uk]=Підтримка для читання та запису етикеток в медіафайлах
+Comment[ven]=Thikhedzo ya u vhala na u nwala u itela u tag kha dzifaela dza media
+Comment[zh_CN]=支持对媒体文件的标签进行读取和写入
+Comment[zh_HK]=讀取或寫入媒體檔案標籤的支援
+Comment[zh_TW]=支援讀取或寫入媒體檔案的標籤
+Comment[zu]=Inxaso yokufunda nokubhala amathegi kumafayela ezezindaba
diff --git a/noatun/modules/monoscope/Makefile.am b/noatun/modules/monoscope/Makefile.am
new file mode 100644
index 00000000..55c58a05
--- /dev/null
+++ b/noatun/modules/monoscope/Makefile.am
@@ -0,0 +1,16 @@
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_monoscope.la
+
+noatun_monoscope_la_SOURCES = monoscope.cpp
+#prefs.cpp
+
+noatun_monoscope_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_monoscope_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la -lm
+
+noatun_monoscope_la_METASOURCES = AUTO
+
+noinst_HEADERS = monoscope.h
+#prefs.h
+
+noatun_modules_monoscope_DATA = monoscope.plugin
+noatun_modules_monoscopedir = $(kde_datadir)/noatun
diff --git a/noatun/modules/monoscope/monoscope.cpp b/noatun/modules/monoscope/monoscope.cpp
new file mode 100644
index 00000000..5ab63ecc
--- /dev/null
+++ b/noatun/modules/monoscope/monoscope.cpp
@@ -0,0 +1,112 @@
+#include "monoscope.h"
+#include <noatun/player.h>
+#include <noatun/app.h>
+#include <math.h>
+#include <qpainter.h>
+#include <kactionclasses.h>
+#include <noatun/stdaction.h>
+#include <klocale.h>
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new Monoscope();
+ }
+}
+
+Monoscope::Monoscope() : QWidget(0,0,WRepaintNoErase), MonoScope(30), Plugin()
+{
+ NOATUNPLUGINC(Monoscope);
+
+ mAction=0L;
+ mLowColor=qRgb(0,0,0);
+ mHighColor=qRgb(238,238,238);
+ resize(320, 240);
+ MonoScope::start();
+ setCaption(i18n("Monoscope"));
+ show();
+ resizeEvent(0);
+ repaint(0,0, QWidget::width(), height(), false);
+ resizeEvent(0);
+ setBackgroundColor(mLowColor);
+}
+
+Monoscope::~Monoscope()
+{
+ if(mAction)
+ napp->pluginActionMenu()->remove(mAction);
+}
+
+void Monoscope::init()
+{
+ mAction = new KToggleAction(i18n("Toggle Monoscope"), 0, 0,
+ this, SLOT(toggle()), this, "togglemonoscope");
+ mAction->setChecked(!isHidden());
+ napp->pluginActionMenu()->insert(mAction);
+}
+
+void Monoscope::toggle(void)
+{
+ if(isHidden())
+ show();
+ else
+ hide();
+}
+
+void Monoscope::closeEvent(QCloseEvent *)
+{
+ hide();
+}
+
+void Monoscope::resizeEvent(QResizeEvent *)
+{
+ setSamples(width());
+}
+
+void Monoscope::scopeEvent(float *d, int size)
+{
+ // save cpu
+ if(isHidden()) return;
+
+ const bool line=false;
+
+ int viewWidth =width();
+ int viewHeight=height();
+
+ float *end=d+size;
+ int x=0;
+ int heightHalf=viewHeight/4;
+ int y=viewHeight/2;
+ // reduce flicker
+ QPixmap buffer(viewWidth, viewHeight, -1, QPixmap::BestOptim);
+ buffer.fill(mLowColor);
+ QPainter p(&buffer);
+ p.setPen(mHighColor);
+ repaint(rect());
+
+ if (line)
+ p.moveTo(0, y);
+
+ while (d<=end)
+ {
+ float &n=*d;
+
+ n *= heightHalf;
+ int amp=(int)n;
+
+ if (line) // line
+ p.lineTo(x, y+amp);
+ else // fill
+ p.drawLine(x, y, x, y+amp);
+ d++;
+ x++;
+
+ }
+ if (line)
+ p.drawLine(0, y, size, y);
+ bitBlt(this, 0, 0, &buffer, 0, 0, viewWidth, viewHeight, Qt::CopyROP);
+}
+
+#include "monoscope.moc"
+
diff --git a/noatun/modules/monoscope/monoscope.h b/noatun/modules/monoscope/monoscope.h
new file mode 100644
index 00000000..37031a4b
--- /dev/null
+++ b/noatun/modules/monoscope/monoscope.h
@@ -0,0 +1,34 @@
+#ifndef MONOSCOPE_H
+#define MONOSCOPE_H
+
+#include <noatun/plugin.h>
+
+class KToggleAction;
+
+class Monoscope : public QWidget, public MonoScope, public Plugin
+{
+Q_OBJECT
+NOATUNPLUGIND
+
+public:
+ Monoscope();
+ virtual ~Monoscope();
+
+ void init();
+
+public slots:
+ void toggle(void);
+
+protected:
+ virtual void closeEvent(QCloseEvent *);
+ virtual void scopeEvent(float *data, int bands);
+
+ virtual void resizeEvent(QResizeEvent *);
+
+private:
+ QRgb mHighColor, mLowColor;
+ KToggleAction *mAction;
+};
+
+#endif
+
diff --git a/noatun/modules/monoscope/monoscope.plugin b/noatun/modules/monoscope/monoscope.plugin
new file mode 100644
index 00000000..b4533c0f
--- /dev/null
+++ b/noatun/modules/monoscope/monoscope.plugin
@@ -0,0 +1,99 @@
+Filename=noatun_monoscope.la
+Author=Charles Samuels
+Site=http://noatun.kde.org/
+Email=charles@kde.org
+Type=visualization
+License=BSD
+Name=Monoscope
+Name[af]=Monoskoop
+Name[ar]=مونوسكوب
+Name[az]=Monoskop
+Name[ca]=Monoscopi
+Name[cs]=Monoskop
+Name[da]=Monoskop
+Name[eo]=Monoskopo
+Name[es]=Monoscopio
+Name[fa]=تک دامنه‌ای
+Name[gl]=Monoscopio
+Name[hi]=मोनोस्कोप
+Name[hu]=Monoszkóp
+Name[it]=Monoscopio
+Name[ja]=モノスコープ
+Name[kk]=Осцилограф
+Name[lv]=Monoskops
+Name[mt]=Monoskopju
+Name[ne]=मोमोस्कोप
+Name[pa]=ਮੋਨੋਸਕੋਪ
+Name[pl]=Monoskop
+Name[ro]=Monoscop
+Name[ru]=Моноскоп
+Name[sk]=Monoskop
+Name[sv]=Monoskop
+Name[ta]=ஓரெல்லைப் பரப்பு
+Name[tr]=Monoskop
+Name[uk]=Моноскоп
+Name[xh]=Isithuba esinye
+Name[zh_CN]=单像管
+Name[zh_HK]=單像管
+Name[zh_TW]=單像管
+Name[zu]=Isithuba esinye
+Comment=A neat waveform scope analyzer
+Comment[af]='n netjiese golfvorm skoop analiseerder
+Comment[az]=Dalğa şəklində analiz proqramı
+Comment[bg]=Изящен осцилограф
+Comment[bs]=Zgodan analizator talasne forme
+Comment[ca]=Un analitzador pulcre de camps d'ones
+Comment[cs]=Vkusný analyzátor vln
+Comment[cy]=Dadansoddydd Twt ar gyfer Cwmpas Tonffurf
+Comment[da]=En smart bølgeformsscope-analysator
+Comment[de]=Ein Programm zur "Waveform/Scope"-Analyse
+Comment[el]=Ένας θαυμάσιος αναλυτής φάσματος κυματομορφών
+Comment[en_GB]=A neat waveform scope analyser
+Comment[eo]=Uzinda ondo-analizilo
+Comment[es]=Un analizador elegante de formas de onda
+Comment[et]=Nunnu signaalikuju analüsaator
+Comment[eu]=uhinforma esparruko analizatzailea
+Comment[fa]=تحلیل‌کنندۀ دامنۀ موجی شکل ساده
+Comment[fi]=Nätti oskilloskooppi
+Comment[fr]=Un bel analyseur de spectre
+Comment[gl]=Analizador suave da forma da onda
+Comment[he]=מאבחן תחומי גל
+Comment[hi]=एक साफ वेवफार्म स्कोप एनॉलाइजर
+Comment[hr]=Simpatičan analizator zvuka
+Comment[hu]=Egy kellemes hullámforma-analizáló program
+Comment[is]=Flottur bylgjugreinir
+Comment[it]=Un puro analizzatore di forme d'onda
+Comment[ja]=かっこいい波形スコープアナライザ
+Comment[kk]=Керемет бір осциллограф секілді аспап
+Comment[km]=កម្មវិធី​វិភាគ​វិសាលភាព​ទម្រង់ wave ហ្មត់ចត់
+Comment[ko]=파형을 관찰하고 분석하는 도구
+Comment[lt]=Gražus bangų formos vaizdo analizatorius
+Comment[lv]=Vienkāršs viļņaformas analizators
+Comment[mk]=Симпатичен осцилоскоп за анализа на бранови форми
+Comment[ms]=Penganalisis skop bentuk gelombang
+Comment[nb]=Et nett lite program for analysering av «waveform scope»
+Comment[nds]=En smuck Bülgenformrebeetkieker
+Comment[ne]=क्षेत्र विश्लेषकबाट निट तरङ
+Comment[nl]=Een goed waveform scope-analyseprogramma
+Comment[nn]=Analysering av «waveform scope»
+Comment[pl]=Analizator fal dźwiękowych
+Comment[pt]=Um analisador de formas de onda bonito
+Comment[pt_BR]=Um analisador de formas de onda
+Comment[ro]=Un analizor de formă de undă foarte bun
+Comment[ru]=Симпатичный осциллограф
+Comment[sk]=Milý analyzátor vĺn
+Comment[sl]=Lep analizator zvoka
+Comment[sr]=Уредан анализатор облика таласа
+Comment[sr@Latn]=Uredan analizator oblika talasa
+Comment[sv]=Elegant oscilloskop
+Comment[ta]=ஒரு சீரான அலைவடிவ ஆய்வாளர்
+Comment[tg]=Таҳлилгари намудсози шакли мавҷии шабакавӣ
+Comment[th]=โปรแกรมวิเคราะห์รูปแบบคลื่นที่ปราณีต
+Comment[tr]=Dalga formu analiz programı
+Comment[uk]=Гарний аналізатор хвильових меж
+Comment[ven]=Mugudi neat waveform scope
+Comment[xh]=Umhlahleli wesithuba wendlela ecocekileyo yamaza
+Comment[zh_CN]=一个小巧的波形范围分析器
+Comment[zh_HK]=小巧的波形範圍分析器
+Comment[zh_TW]=一個小巧的波形分析器
+Comment[zu]=Umhaziyi wesithuba wendlela ecehlanzekile
diff --git a/noatun/modules/net/Makefile.am b/noatun/modules/net/Makefile.am
new file mode 100644
index 00000000..9398a602
--- /dev/null
+++ b/noatun/modules/net/Makefile.am
@@ -0,0 +1,14 @@
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_net.la
+
+noatun_net_la_SOURCES = net.cpp
+
+noatun_net_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_net_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la
+
+noatun_net_la_METASOURCES = AUTO
+
+noinst_HEADERS = net.h
+
+noatun_modules_net_DATA = net.plugin
+noatun_modules_netdir = $(kde_datadir)/noatun
diff --git a/noatun/modules/net/net.cpp b/noatun/modules/net/net.cpp
new file mode 100644
index 00000000..5e3ba707
--- /dev/null
+++ b/noatun/modules/net/net.cpp
@@ -0,0 +1,57 @@
+#include "net.h"
+#include <noatun/player.h>
+#include <noatun/app.h>
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new Net();
+ }
+}
+
+
+Net::Net() : QServerSocket(7539, 10), Plugin()
+{
+ mFDs.setAutoDelete(true);
+ connect(napp->player(), SIGNAL(newSong()), SLOT(newSong()));
+}
+
+Net::~Net()
+{
+}
+
+
+void Net::newConnection(int fd)
+{
+ QSocket *s=new QSocket;
+ s->setSocket(fd);
+ mFDs.append(s);
+}
+
+void Net::newSong()
+{
+ if (!napp->player()->current())
+ return;
+
+ for (QSocket *i=mFDs.first(); i!=0; i=mFDs.next())
+ {
+ QCString line;
+ line=napp->player()->current().title().latin1();
+ line+='\n';
+ ::write(i->socket(), (const void*)line.data(), line.length());
+ }
+}
+
+void Net::closed()
+{
+ for (QSocket *i=mFDs.first(); i!=0; i=mFDs.next())
+ {
+ if (sender()==i)
+ mFDs.removeRef(i);
+ }
+}
+
+
+
+#include "net.moc"
diff --git a/noatun/modules/net/net.h b/noatun/modules/net/net.h
new file mode 100644
index 00000000..7d0fcb11
--- /dev/null
+++ b/noatun/modules/net/net.h
@@ -0,0 +1,31 @@
+#ifndef DCOPIFACE_H
+#define DCOPIFACE_H
+
+#include <noatun/player.h>
+#include <noatun/plugin.h>
+#include <qserversocket.h>
+#include <qsocket.h>
+#include <unistd.h>
+
+class Net : public QServerSocket, public Plugin
+{
+Q_OBJECT
+
+public:
+ Net();
+ ~Net();
+
+public slots:
+ void newSong();
+
+private slots:
+ void closed();
+protected:
+ virtual void newConnection(int socket);
+
+private:
+ QPtrList<QSocket> mFDs;
+};
+
+#endif
+
diff --git a/noatun/modules/net/net.plugin b/noatun/modules/net/net.plugin
new file mode 100644
index 00000000..21683b5d
--- /dev/null
+++ b/noatun/modules/net/net.plugin
@@ -0,0 +1,136 @@
+Filename=noatun_net.la
+Author=Charles Samuels
+Site=http://noatun.derkarl.org/
+Email=charles@kde.org
+Type=other
+License=Public Domain
+Name=Network Interface
+Name[af]=Netwerk Koppelvlak
+Name[ar]=واجهة الشبكة
+Name[az]=Şəbəkə Ara üzü
+Name[bn]=নেটওয়ার্ক ইন্টারফেস
+Name[br]=Etrefas Rouedad
+Name[bs]=Mrežni interfejs
+Name[ca]=Interfície de xarxa
+Name[cs]=Síťové rozhraní
+Name[cy]=Rhyngwyneb Rhwydwaith
+Name[da]=Netværksgrænseflade
+Name[de]=Netzwerk-Schnittstelle
+Name[el]=Διασύνδεση δικτύου
+Name[eo]=Retinterfaco
+Name[es]=Interfaz de red
+Name[et]=Võrguliides
+Name[eu]=Sareko interfazea
+Name[fa]=واسط شبکه
+Name[fi]=Verkkokäyttöliittymä
+Name[fr]=Interface réseau
+Name[ga]=Comhéadan Gréasáin
+Name[gl]=Interface de rede
+Name[he]=ממשק רשת
+Name[hi]= नेटवर्क इंटरफेस
+Name[hr]=Mrežno sučelje
+Name[hu]=Hálózati felület
+Name[id]=Interface jaringan
+Name[is]=Nettengi
+Name[it]=Interfaccia di rete
+Name[ja]=ネットワークインターフェース
+Name[kk]=Желілік интерфейсі
+Name[km]=ចំណុច​ប្រទាក់​បណ្ដាញ
+Name[ko]=네트워크 인터페이스
+Name[lt]=Tinklo sąsaja
+Name[lv]=Tīkla Starpseja
+Name[mk]=Мрежен интерфејс
+Name[mt]=Interfaċċja tan-Network
+Name[nb]=Nettverksgrensesnitt
+Name[nds]=Nettwark-Koppelsteed
+Name[ne]=सञ्जाल इन्टरफेस
+Name[nl]=Netwerk-interface
+Name[nn]=Nettverksgrensesnitt
+Name[pa]=ਨੈਟਵਰਕ ਇੰਟਰਫੇਸ
+Name[pl]=Interfejs sieciowy
+Name[pt]=Interface de Rede
+Name[pt_BR]=Interface de Rede
+Name[ro]=Interfată reţea
+Name[ru]=Сетевой интерфейс
+Name[se]=Fierpmádatlakta
+Name[sk]=Sieťové rozhranie
+Name[sl]=Omrežni vmesnik
+Name[sr]=Мрежни интерфејс
+Name[sr@Latn]=Mrežni interfejs
+Name[sv]=Nätverksgränssnitt
+Name[ta]=வலைப்பின்னல் இடைமுகம்
+Name[tg]=Интерфейси Шабакавӣ
+Name[th]=แผงวงจรเครือข่าย
+Name[tr]=Ağ Arayüzü
+Name[uk]=Інтерфейс мережі
+Name[uz]=Tarmoq interfeysi
+Name[uz@cyrillic]=Тармоқ интерфейси
+Name[ven]=Vhukwamani
+Name[xh]=Ujongano nomsebenzi womnatha
+Name[zh_CN]=网络接口
+Name[zh_HK]=網絡介面
+Name[zh_TW]=網路介面
+Name[zu]=Uxhumano Olubhekene loxhumaniso
+Comment=A very simple read-only network interface on port 7539
+Comment[af]='n baie eenvoudige lees-alleen netwerk koppelvlak op poort 7539
+Comment[ar]=واجهة بسيطة للشبكة قابلة للقراءة فقط على المنفذ رقم 7539
+Comment[az]=Çox bəsit bir sırf-oxuma şəbəkə axtar üzü (7539.port)
+Comment[bg]=Елементарен мрежов интерфейс само за четене на порт 7539
+Comment[bs]=Veoma jednostavan read-only mrežni interfejs na portu 7539
+Comment[ca]=Una interfície de xarxa molt simple de només lectura al port 7539
+Comment[cs]=Velmi jednoduché síťové rozhraní určené jen pro čtení na portu 7539
+Comment[cy]=Rhyngwyneb rhwydwaith syml darllen-yn-unig ar borth 7539
+Comment[da]=En meget simpel skrivebeskyttet netværksgrænseflade på port 7539
+Comment[de]=Einfache Netzwerkschnittstelle an Port 7539
+Comment[el]=Μια πολύ απλή διασύνδεση δικτύου, μόνο-ανάγνωσης, στη θύρα 7539
+Comment[eo]=Simpla nurlega retinterfaco por a pordo 7539
+Comment[es]=Una interfaz de red simple de sólo lectura en el puerto 7539
+Comment[et]=Väga lihtne pordil 7539 töötav võrguliides (ainult lugemiseks)
+Comment[eu]=Irakurketarako bakarrik den 7539 atakako sare interfaze oso sinplea
+Comment[fa]=یک واسط شبکه فقط خواندنی بسیار ساده روی درگاه ۷۵۳۹
+Comment[fi]=Yksinkertainen vain-luku-verkkokäyttöliittymä portissa 7539
+Comment[fr]=Une interface réseau très simple sur le port 7539
+Comment[ga]=Comhéadan an-simplí líonra ar phort 7539
+Comment[gl]=Unha interface de rede moi sinxela de só lectura no porto 7539
+Comment[he]=ממשק רשת פשוט מאוד לקריאה בלבד ביציאה 7539
+Comment[hi]=एक अत्यंत साधारण सिर्फ पढ़ने लायक नेटवर्क इंटरफेल पोर्ट 7539 पर
+Comment[hr]=Vrlo jednostavno mrežno sučelje za čitanje s porta 7539
+Comment[hu]=Egy nagyon egyszerű, csak olvasható hálózati felület a 7539-es porton
+Comment[is]=Mjög einfalt ritvarið nettengi á porti 7539
+Comment[it]=Un'interfaccia su porta 7539 di sola lettura molto semplice
+Comment[ja]=ポート 7539 を使う非常にシンプルな読み込み専用のネットワークインターフェース
+Comment[kk]=Өте қарапайым, 7539 портынан тек оқу үшін желілік интерфейсі
+Comment[km]=ចំណុច​ប្រទាក់​បណ្ដាញ​សាមញ្ញ​បំផុត ហើយ​បាន​តែ​អាន ដែល​រត់​លើ​ច្រក 7539
+Comment[ko]=7539 포트에서 작동하는 간단한 읽기 전용 네트워크 인터페이스
+Comment[lt]=Labai paprasta tik skaitoma tinklo sąsaja 7539'am portui
+Comment[lv]=Ļoti vienkārša tikai-lasīšana tīkla starpseja uz porta 7539
+Comment[mk]=Многу едноставен мрежен интерфејс, само за читање, на порта 7539
+Comment[ms]=Antaramuka jaringan baca sahaja yang ringkas untuk liang 7539
+Comment[mt]=Interfaċċja sempiliċi ħafna tinqara biss fuq port 7539
+Comment[nb]=Et veldig enkelt skrivebeskyttet nettverksgrensesnitt på port 7539
+Comment[nds]=En bannig eenfach "Bloots-lesen"-Nettwarkkoppelsteed op Port 7539
+Comment[ne]=पोर्ट ७५३९ मा धेरै साधारण पढ्ने-मात्र सञ्जाल
+Comment[nl]=Een heel eenvoudige alleen-lezen netwerkinterface op poort 7539
+Comment[nn]=Eit svært enkelt nettverksgrensesnitt for lesing på port 7539
+Comment[pl]=Bardzo prosty interfejs sieciowy tylko do odczytu, na porcie 7539
+Comment[pt]=Uma interface de rede muito simples no porto 7539
+Comment[pt_BR]=Uma interface muito simples de rede, porta 7539, somente leitura
+Comment[ro]=O interfaţă de reţea foarte simplă pe portul 7539
+Comment[ru]=Простейший сетевой интерфейс на 7539 порту только для чтения
+Comment[se]=Hui oktageardanis fierpmádatlakta mii lohká dieđuid 7539 verráhis
+Comment[sk]=Veľmi jednoduché sieťové rozhranie iba pre čítanie na porte 7539
+Comment[sl]=Zelo preprost bralni omrežni vmesnik na vratih 7539
+Comment[sr]=Једноставан, само-за-читање мрежни интерфејс на порту 7539
+Comment[sr@Latn]=Jednostavan, samo-za-čitanje mrežni interfejs na portu 7539
+Comment[sv]=Väldigt enkelt skrivskyddat nätverksgränssnitt på port 7539
+Comment[ta]=துறை 7539இலுள்ள மிக எளிய, வாசிக்க-மட்டும் வலைத்தள இடைமுடம்
+Comment[tg]=Интерфейси хеле содда ва танҳо барои хониши шабакавӣ дар даргоҳи 7539
+Comment[th]=แผงวงจรเครือข่ายบนพอร์ต 7539
+Comment[tr]=Çok basit bir salt-oku ağ arayüzü (7539.port)
+Comment[uk]=Дуже простий інтерфейс мережі для зчитування на порту 7539
+Comment[ven]=Vhukwamani ho leluwaho hau vhala kha port ya 7539
+Comment[xh]=Ufundo olulula kakhulu-lujongano lomsebenzi womnatha kuphela kwizibuko 7539
+Comment[zh_CN]=简单的位于 7539 端口的只读网络接口
+Comment[zh_HK]=工作於 7539 連接埠的唯讀簡易型網絡介面
+Comment[zh_TW]=一個位於 7539 Port 上唯讀存取的簡易型網路介面
+Comment[zu]=Okulula ukufunda kuphela kuxhumaniso olubhekeneyo kwi port 7539
diff --git a/noatun/modules/noatunui/Makefile.am b/noatun/modules/noatunui/Makefile.am
new file mode 100644
index 00000000..443afd6c
--- /dev/null
+++ b/noatun/modules/noatunui/Makefile.am
@@ -0,0 +1,16 @@
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_ui.la
+
+noatun_ui_la_SOURCES = noatunui.cpp userinterface.cpp
+
+noatun_ui_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_ui_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la \
+ $(top_builddir)/noatun/library/libnoatuncontrols.la \
+ -lqtmcop -lkmedia2_idl -lsoundserver_idl
+
+noatun_ui_la_METASOURCES = AUTO
+
+noinst_HEADERS = userinterface.h
+
+noatun_modules_ui_DATA = noatunui.plugin
+noatun_modules_uidir = $(kde_datadir)/noatun
diff --git a/noatun/modules/noatunui/noatunui.cpp b/noatun/modules/noatunui/noatunui.cpp
new file mode 100644
index 00000000..adb9534b
--- /dev/null
+++ b/noatun/modules/noatunui/noatunui.cpp
@@ -0,0 +1,9 @@
+#include "userinterface.h"
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new MilkChocolate;
+ }
+}
diff --git a/noatun/modules/noatunui/noatunui.plugin b/noatun/modules/noatunui/noatunui.plugin
new file mode 100644
index 00000000..e8413cd8
--- /dev/null
+++ b/noatun/modules/noatunui/noatunui.plugin
@@ -0,0 +1,120 @@
+Filename=noatun_ui.la
+Author=Charles Samuels
+Site=http://noatun.kde.org/plugins/milkchocolate/
+Email=charles@kde.org
+Type=userinterface
+License=Artistic
+Name=Milk-Chocolate
+Name[ar]=حليب-شوكولا
+Name[az]=Şokaladlı Süd
+Name[br]=Chokolad leizh
+Name[bs]=Mlijeko-čokolada
+Name[ca]=Xocolata amb llet
+Name[cs]=Noatun mléčná čokoláda
+Name[cy]=Sioclat Llefrith
+Name[da]=Mælkechokolade
+Name[de]=Milchschokolade
+Name[eo]=Laktoĉokolado
+Name[es]=Chocolate con leche
+Name[fi]=Maitosuklaa
+Name[fr]=Chocolat au lait
+Name[gl]=Leite-Chocolate
+Name[hi]=मिल्क-चॉकलेट
+Name[hr]=Mliječna čokolada
+Name[hu]=Tejcsoki
+Name[is]=Mjólkursúkkulaði
+Name[it]=Caffellatte
+Name[km]=ទឹកដោះគោ-សូកូឡា
+Name[ko]=밀크 초콜릿
+Name[lt]=Pieniškas šokoladas
+Name[lv]=Piena-Šokolāde
+Name[mk]=Млечна чоколада
+Name[mt]=Ċikkulata
+Name[nb]=Melkesjokolade
+Name[nds]=Melkschokolaad
+Name[ne]=दुध-मिठाइ
+Name[nl]=Melkchocolade
+Name[nn]=Mjølkesjokolade
+Name[pl]=Czekolada mleczna
+Name[pt]=Leite com Chocolate
+Name[pt_BR]=Noatun Leite-Chocolate
+Name[ro]=Ciocolată cu lapte
+Name[ru]=Молочный шоколад
+Name[se]=Mielkešokolada
+Name[sk]=Mliečna čokoláda
+Name[sl]=Mlečna čokolada
+Name[sv]=Mjölkchoklad
+Name[ta]=மில்க்-சாக்லேட்
+Name[tg]=Шоколади Ширӣ
+Name[tr]=Çikolatalı Süt
+Name[uk]=Молочний шоколад
+Name[ven]=Tshokholeiti ya mafhi
+Name[xh]=Ubisi-ichocolate
+Name[zh_CN]=牛奶巧克力
+Name[zh_HK]=牛奶巧克力
+Name[zh_TW]=牛奶巧克力
+Name[zu]=Ushokolade Wobisi
+Comment=Noatun's simple GUI
+Comment[ar]=واجهة بسيطة جدا لNoatun
+Comment[az]=Noatun bəsit axtar üzü
+Comment[bg]=Семпъл графичен интерфейс за Noatun
+Comment[bs]=Jednostavan Noatunov GUI
+Comment[ca]=IGU senzill pel Noatun
+Comment[cs]=Jednoduché rozhraní pro Noatun
+Comment[cy]=GUI syml Noatun
+Comment[da]=Noatuns enkle GUI
+Comment[de]=Noatuns einfache Oberfläche
+Comment[el]=Το απλό περιβάλλον χρήσης του Noatun
+Comment[eo]=La simpla grafika etoso de Noatun
+Comment[es]=Interfaz simple de Noatun
+Comment[et]=Lihtne Noatuni kasutajaliides
+Comment[eu]=Noatun-en GUI simplea
+Comment[fa]=ونک ساده Noatun
+Comment[fi]=Noatunin yksinkertainen käyttöliittymä
+Comment[fr]=Interface graphique simple de Noatun
+Comment[ga]=Comhéadan simplí grafach ar Noatun
+Comment[gl]=GUI sinxela de Noatun
+Comment[he]=ממשק המשתמש הגרפי הפשוט של Noatun
+Comment[hi]=नोआट्यून का साधारण जीयूआई
+Comment[hr]=Noatunovo jednostavno sučelje
+Comment[hu]=A Noatun egyszerű grafikus kezelői felülete
+Comment[id]=Contoh Noatun'GUI
+Comment[is]=Hið einfalda viðmót Nóatúns forritsins
+Comment[it]=Interfaccia grafica semplice di Noatun
+Comment[ja]=Noatun のシンプルな GUI
+Comment[kk]=Noatun-ның қарапайым графикалық интерфейсі
+Comment[km]=ចំណុច​ប្រទាក់​ក្រាហ្វិក​សាមញ្ញ​របស់ Noatun
+Comment[ko]=Noatun의 간단한 GUI
+Comment[lt]=Noatun paprastas GUI
+Comment[lv]=Vienkāršs Noatuna GUI
+Comment[mk]=Едноставен графички интерфејс за Noatun
+Comment[ms]=GUI Noatun yang ringkas
+Comment[mt]=GUI sempliċi għal Noatun
+Comment[nb]=Noatuns enkle GUI
+Comment[nds]=Eenfach Böversiet vun Noatun
+Comment[ne]=नोवटुनको साधारण GUI
+Comment[nl]=Noatun's eenvoudige GUI
+Comment[nn]=Det enkle Noatun-GUI-et
+Comment[pa]=ਨੋਟੌਮ ਦਾ ਸਧਾਰਨ GUI
+Comment[pl]=Prosty motyw Noatun
+Comment[pt]=Uma interface simples do Noatun
+Comment[pt_BR]=Interface simplificada do Noatun
+Comment[ro]=Interfaţă grafică Noatun simplă
+Comment[ru]=Простой графический интерфейс Noatun
+Comment[se]=Noatuna oktageardanis grafihkkalaš lakta
+Comment[sk]=Jednoduché rozhranie pre Noatun
+Comment[sl]=Noatunov preprost uporabniški vmesnik
+Comment[sr]=Noatun-ов једноставан GUI
+Comment[sr@Latn]=Noatun-ov jednostavan GUI
+Comment[sv]=Noatuns enkla gränssnitt
+Comment[ta]=Noatun'இன் எளிய முகப்பொன்று
+Comment[tg]=Интерфейси Графикии соддаи Noatun
+Comment[th]=ส่วนติดต่อผู้ใช้แบบกราฟิกแบบเรียบง่ายของ Noatun
+Comment[tr]=Noatun basit arayüzü
+Comment[uk]=Простий графічний інтерфейс Noatun
+Comment[ven]=Noatun yo leluwaho ya GUI
+Comment[xh]=iGUI elula ye Noatun
+Comment[zh_CN]=Noatun 的简单界面
+Comment[zh_HK]=Noatun 的簡單圖形界面
+Comment[zh_TW]=Noatun 簡單圖形界面
+Comment[zu]=I GUI elula ye Noatun
diff --git a/noatun/modules/noatunui/userinterface.cpp b/noatun/modules/noatunui/userinterface.cpp
new file mode 100644
index 00000000..c13d4d6d
--- /dev/null
+++ b/noatun/modules/noatunui/userinterface.cpp
@@ -0,0 +1,315 @@
+/*
+ * noatun.cpp
+ *
+ * Copyright (C) 1999 Charles Samuels <charles@kde.org>
+ */
+
+#include "userinterface.h"
+#include <noatun/playlist.h>
+#include <noatun/stdaction.h>
+#include <noatun/app.h>
+#include <noatun/player.h>
+#include <noatun/controls.h>
+#include <noatun/effects.h>
+
+#include <qpushbutton.h>
+#include <qdragobject.h>
+#include <qlayout.h>
+#include <qtooltip.h>
+#include <qobjectlist.h>
+#include <qobjectdict.h>
+
+#include <kpopupmenu.h>
+#include <kstatusbar.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kurldrag.h>
+#include <kfiledialog.h>
+#include <kconfig.h>
+
+MilkChocolate::MilkChocolate() : QWidget(0,"NoatunUI"), UserInterface()
+{
+ setAcceptDrops(true);
+ static const int buttonSize=32;
+
+ { // The buttons
+ mBack=new QPushButton(this);
+ mBack->setFixedSize(buttonSize,buttonSize);
+ mBack->setPixmap(BarIcon("noatunback"));
+ connect(mBack, SIGNAL(clicked()), napp->player(), SLOT(back()));
+ QToolTip::add(mBack,i18n("Back"));
+
+ mStop=new QPushButton(this);
+ mStop->setFixedSize(buttonSize,buttonSize);
+ mStop->setPixmap(BarIcon("noatunstop"));
+ connect(mStop, SIGNAL(clicked()), napp->player(), SLOT(stop()));
+ QToolTip::add(mStop, i18n("Stop"));
+
+ mPlay=new QPushButton(this);
+ mPlay->setToggleButton(true);
+ mPlay->setFixedSize(buttonSize,buttonSize);
+ mPlay->setPixmap(BarIcon("noatunplay"));
+ connect(mPlay, SIGNAL(clicked()), napp->player(), SLOT(playpause()));
+ QToolTip::add(mPlay, i18n("Play"));
+
+ mForward=new QPushButton(this);
+ mForward->setFixedSize(buttonSize,buttonSize);
+ mForward->setPixmap(BarIcon("noatunforward"));
+ connect(mForward, SIGNAL(clicked()), napp->player(), SLOT(forward()));
+ QToolTip::add(mForward, i18n("Forward"));
+
+ mPlaylist=new QPushButton(this);
+ mPlaylist->setToggleButton(true);
+ mPlaylist->setFixedSize(buttonSize,buttonSize);
+ mPlaylist->setPixmap(BarIcon("noatunplaylist"));
+ connect(mPlaylist, SIGNAL(clicked()), napp->player(), SLOT(toggleListView()));
+ QToolTip::add(mPlaylist, i18n("Playlist"));
+
+ mLoop=new QPushButton(this);
+ mLoop->setFixedSize(buttonSize,buttonSize);
+ mLoop->setPixmap(BarIcon("noatunloopnone"));
+ connect(mLoop, SIGNAL(clicked()), napp->player(), SLOT(loop()));
+ QToolTip::add(mLoop, i18n("Change loop style"));
+
+ mPopup=new QPushButton(this);
+ mPopup->setFixedSize(buttonSize,buttonSize);
+ mPopup->setPixmap(BarIcon("noatun"));
+ connect(mPopup, SIGNAL(clicked()), SLOT(popup()));
+// QToolTip::add(mRemoveCurrent, i18n("Remove current file from playlist"));
+
+ }
+
+ mVolume=new L33tSlider(0,100,10,0, Horizontal, this);
+ mVolume->setValue(napp->player()->volume());
+ mSeeker=new L33tSlider(0,1000,10,0, Horizontal, this);
+
+ mStatusBar=new KStatusBar(this);
+
+
+ QGridLayout *l=new QGridLayout(this);
+ l->addWidget(mBack,0,0);
+ l->addWidget(mStop,0,1);
+ l->addWidget(mPlay,0,2);
+ l->addWidget(mForward,0,3);
+ l->addWidget(mPlaylist,0,4, Qt::AlignLeft);
+ l->addWidget(mLoop,0,5);
+ l->addWidget(mPopup,0,6);
+ l->addColSpacing(4, buttonSize+8);
+
+ l->addMultiCellWidget(mVolume,1,1,0,6);
+ l->addMultiCellWidget(mSeeker,2,2,0,6);
+ l->addMultiCellWidget(mStatusBar,3,3,0,6);
+
+ statusBar()->message(i18n("No File Loaded"));
+ statusBar()->insertItem("--:--/--:--", 1, 0, true);
+
+ connect(napp, SIGNAL(hideYourself()), this, SLOT(hide()) );
+ connect(napp, SIGNAL(showYourself()), this, SLOT(show()) );
+
+ connect(napp->player(), SIGNAL(playing()), this, SLOT(slotPlaying()));
+ connect(napp->player(), SIGNAL(stopped()), this, SLOT(slotStopped()));
+ connect(napp->player(), SIGNAL(paused()), this, SLOT(slotPaused()));
+ napp->player()->handleButtons();
+
+ connect(napp->player(), SIGNAL(timeout()), this, SLOT(slotTimeout()));
+ connect(napp->player(), SIGNAL(loopTypeChange(int)), this, SLOT(changeLoopType(int)));
+
+// if(seeker())
+ {
+ /* This skipToWrapper is needed to pass milliseconds to Player() as everybody
+ * below the GUI is based on milliseconds instead of some unprecise thingy
+ * like seconds or mille */
+ connect(seeker(), SIGNAL(userChanged(int)), this, SLOT(skipToWrapper(int)));
+ connect(this, SIGNAL(skipTo(int)), napp->player(), SLOT(skipTo(int)));
+ connect(seeker(), SIGNAL(sliderMoved(int)), SLOT(sliderMoved(int)));
+ }
+ connect(mVolume, SIGNAL(sliderMoved(int)), napp->player(), SLOT(setVolume(int)));
+ connect(mVolume, SIGNAL(userChanged(int)), napp->player(), SLOT(setVolume(int)));
+
+
+ connect(napp->player(), SIGNAL(playlistShown()), SLOT(playlistShown()));
+ connect(napp->player(), SIGNAL(playlistHidden()), SLOT(playlistHidden()));
+
+ // Event Filter for the RMB
+ for (QPtrListIterator<QObject> i(*children()); i.current(); ++i)
+ (*i)->installEventFilter(this);
+
+ setCaption("Noatun");
+ setIcon(BarIcon("noatun"));
+ show();
+
+ // What it is now, stay, stay.. roll over, good boy!
+ setFixedSize(minimumSize());
+}
+
+MilkChocolate::~MilkChocolate()
+{
+ // If cfg dialog is still open, delete it so it saves it's position
+// if(prefDlgExist)
+// delete prefDlg;
+}
+
+void MilkChocolate::closeEvent(QCloseEvent*)
+{
+ unload();
+}
+
+void MilkChocolate::showEvent(QShowEvent*e)
+{
+ QWidget::showEvent(e);
+}
+
+void MilkChocolate::dragEnterEvent(QDragEnterEvent *event)
+{
+ // accept uri drops only
+ event->accept(KURLDrag::canDecode(event));
+}
+
+void MilkChocolate::dropEvent(QDropEvent *event)
+{
+ KURL::List uri;
+ if (KURLDrag::decode(event, uri))
+ {
+ for (KURL::List::Iterator i = uri.begin(); i != uri.end(); ++i)
+ napp->player()->openFile(*i, false);
+ }
+}
+
+void MilkChocolate::mouseReleaseEvent(QMouseEvent *e)
+{
+ QWidget::mouseReleaseEvent(e);
+ if (e->button()!=RightButton) return;
+ NoatunStdAction::ContextMenu::showContextMenu();
+}
+
+void MilkChocolate::changeStatusbar(const QString& text, const QString &text2)
+{
+ if (!text2.isNull())
+ statusBar()->changeItem(text2, 1);
+
+ statusBar()->message(!text.isNull() ? text : napp->player()->current().title());
+}
+
+void MilkChocolate::changeCaption(const QString& text)
+{
+ setCaption(text);
+}
+
+void MilkChocolate::popup()
+{
+ NoatunStdAction::ContextMenu::showContextMenu(
+ mapToGlobal(mPopup->geometry().bottomLeft())
+ );
+}
+
+void MilkChocolate::slotPlaying()
+{
+// connect(kwinmodule, SIGNAL(windowAdded(WId)), view, SLOT(attemptReparent(WId)));
+ changeStatusbar(napp->player()->current().title(), napp->player()->lengthString());
+ mPlay->setOn(true);
+ mStop->setEnabled(true);
+ mPlay->setPixmap(BarIcon("noatunpause"));
+}
+
+void MilkChocolate::slotStopped()
+{
+ if (!napp->player()->current()) return;
+ changeStatusbar(i18n("No File Loaded"), napp->player()->lengthString());
+ mStop->setEnabled(false);
+ mPlay->setOn(false);
+ seeker()->setValue(0);
+ mPlay->setPixmap(BarIcon("noatunplay"));
+}
+
+void MilkChocolate::slotPaused()
+{
+ mStop->setEnabled(true);
+ mPlay->setOn(false);
+ mPlay->setPixmap(BarIcon("noatunplay"));
+}
+
+void MilkChocolate::slotTimeout()
+{
+ mVolume->setValue(napp->player()->volume());
+
+ if (!napp->player()->current()) return;
+ if (static_cast<L33tSlider*>(seeker())->currentlyPressed()) return;
+ if (seeker())
+ {
+ seeker()->setRange ( 0, (int)napp->player()->getLength()/1000 );
+ seeker()->setValue ( (int)napp->player()->getTime()/1000 );
+ }
+ changeStatusbar(0, napp->player()->lengthString());
+}
+
+void MilkChocolate::sliderMoved(int seconds)
+{
+ if (napp->player()->current())
+ changeStatusbar(0, napp->player()->lengthString(seconds*1000));
+}
+
+void MilkChocolate::skipToWrapper(int second)
+{
+ emit skipTo((long)(second*1000));
+}
+
+void MilkChocolate::changeLoopType(int t)
+{
+ static const int time=2000;
+ switch (t)
+ {
+ case(Player::None):
+ statusBar()->message(i18n("No looping"), time);
+ mLoop->setPixmap(BarIcon("noatunloopnone"));
+ break;
+ case(Player::Song):
+ statusBar()->message(i18n("Song looping"), time);
+ mLoop->setPixmap(BarIcon("noatunloopsong"));
+ break;
+ case(Player::Playlist):
+ statusBar()->message(i18n("Playlist looping"), time);
+ mLoop->setPixmap(BarIcon("noatunloopplaylist"));
+ break;
+ case(Player::Random):
+ statusBar()->message(i18n("Random play"), time);
+ mLoop->setPixmap(BarIcon("noatunlooprandom"));
+ }
+
+}
+
+bool MilkChocolate::eventFilter(QObject *o, QEvent *e)
+{
+ if ((e->type() == QEvent::MouseButtonRelease)
+ && ((static_cast<QMouseEvent*>(e))->button()==RightButton))
+ {
+ mouseReleaseEvent(static_cast<QMouseEvent*>(e));
+ return true;
+ }
+
+ if (e->type() == QEvent::Wheel)
+ {
+ wheelEvent(static_cast<QWheelEvent*>(e));
+ return true;
+ }
+ return QWidget::eventFilter(o, e);
+}
+
+void MilkChocolate::playlistShown()
+{
+ mPlaylist->setOn(true);
+}
+
+void MilkChocolate::playlistHidden()
+{
+ mPlaylist->setOn(false);
+}
+
+void MilkChocolate::wheelEvent(QWheelEvent *e)
+{
+ int delta=e->delta();
+ mVolume->setValue(mVolume->value()+(delta/120));
+ napp->player()->setVolume(mVolume->value()+(delta/120));
+}
+
+#include "userinterface.moc"
diff --git a/noatun/modules/noatunui/userinterface.h b/noatun/modules/noatunui/userinterface.h
new file mode 100644
index 00000000..94dc9c44
--- /dev/null
+++ b/noatun/modules/noatunui/userinterface.h
@@ -0,0 +1,72 @@
+#ifndef USERINTERFACE_H
+#define USERINTERFACE_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <noatun/plugin.h>
+#include <noatun/app.h>
+
+// Pref dialog pointer global now for position saving
+#include <noatun/pref.h>
+
+class Player;
+class QSlider;
+class QPushButton;
+class KStatusBar;
+
+/**
+ * @short Main window class
+ * @author Charles Samuels <charles@kde.org>
+ * @version 0.1
+ */
+class MilkChocolate : public QWidget, public UserInterface
+{
+Q_OBJECT
+public:
+ MilkChocolate();
+ virtual ~MilkChocolate();
+ void load(const QString& url);
+
+protected:
+ virtual void dragEnterEvent(QDragEnterEvent *event);
+ virtual void dropEvent(QDropEvent *event);
+ virtual void closeEvent(QCloseEvent*);
+ virtual void showEvent(QShowEvent*e);
+ virtual void mouseReleaseEvent(QMouseEvent *);
+ virtual bool eventFilter(QObject*, QEvent*);
+ virtual void wheelEvent(QWheelEvent *e);
+
+protected:
+ QSlider *seeker() const { return mSeeker; }
+ KStatusBar *statusBar() const { return mStatusBar; }
+
+public slots:
+ void slotPlaying();
+ void slotStopped();
+ void slotPaused();
+
+ void playlistShown();
+ void playlistHidden();
+
+ void slotTimeout();
+ void sliderMoved(int seconds);
+ void changeLoopType(int t);
+ void skipToWrapper(int second);
+
+signals:
+ void skipTo( int ); // emitted by skipToWrapper()
+
+private slots:
+ void changeStatusbar(const QString& text, const QString &text2=0);
+ void changeCaption(const QString& text);
+ void popup();
+
+private:
+ QPushButton *mBack, *mStop, *mPlay, *mForward, *mPlaylist, *mPopup, *mLoop;
+ QSlider *mSeeker, *mVolume;
+ KStatusBar *mStatusBar;
+};
+
+#endif
diff --git a/noatun/modules/simple/Makefile.am b/noatun/modules/simple/Makefile.am
new file mode 100644
index 00000000..de5a7aa8
--- /dev/null
+++ b/noatun/modules/simple/Makefile.am
@@ -0,0 +1,16 @@
+INCLUDES= -I$(top_srcdir)/noatun/library -I$(kde_includes)/arts $(all_includes)
+
+kde_module_LTLIBRARIES = noatunsimple.la
+
+noatunsimple_la_SOURCES = propertiesdialog.ui noatunui.cpp userinterface.cpp
+noatunsimple_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module
+noatunsimple_la_LIBADD = $(top_builddir)/noatun/library/libnoatun.la \
+ $(top_builddir)/noatun/library/libnoatuncontrols.la \
+ -lkmedia2_idl -lsoundserver_idl -lartskde
+
+noatunsimple_la_METASOURCES = AUTO
+
+noinst_HEADERS = propertiesdialog.h userinterface.h
+
+noatun_modules_simple_DATA = simple.plugin simpleui.rc
+noatun_modules_simpledir = $(kde_datadir)/noatun
diff --git a/noatun/modules/simple/back.xpm b/noatun/modules/simple/back.xpm
new file mode 100644
index 00000000..bd433e5e
--- /dev/null
+++ b/noatun/modules/simple/back.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+const char * back_xpm[] = {
+"16 16 2 1",
+" c None",
+". c #000000",
+" ",
+" ",
+" ",
+" .... .. ",
+" .... ... ",
+" .... .... ",
+" .... ..... ",
+" .... ...... ",
+" .... ...... ",
+" .... ..... ",
+" .... .... ",
+" .... ... ",
+" .... .. ",
+" ",
+" ",
+" "};
diff --git a/noatun/modules/simple/forward.xpm b/noatun/modules/simple/forward.xpm
new file mode 100644
index 00000000..c4e95ccc
--- /dev/null
+++ b/noatun/modules/simple/forward.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+const char * forward_xpm[] = {
+"16 16 2 1",
+" c None",
+". c #000000",
+" ",
+" ",
+" ",
+" .. .... ",
+" ... .... ",
+" .... .... ",
+" ..... .... ",
+" ...... .... ",
+" ...... .... ",
+" ..... .... ",
+" .... .... ",
+" ... .... ",
+" .. .... ",
+" ",
+" ",
+" "};
diff --git a/noatun/modules/simple/noatunui.cpp b/noatun/modules/simple/noatunui.cpp
new file mode 100644
index 00000000..3b647e42
--- /dev/null
+++ b/noatun/modules/simple/noatunui.cpp
@@ -0,0 +1,17 @@
+/*
+ This file is part of KDE/aRts (Noatun) - xine integration
+ Copyright (C) 2002 Ewald Snel <ewald@rambo.its.tudelft.nl>
+
+ 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.
+*/
+
+#include "userinterface.h"
+
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin() { return new SimpleUI(); }
+}
diff --git a/noatun/modules/simple/pause.xpm b/noatun/modules/simple/pause.xpm
new file mode 100644
index 00000000..48d37568
--- /dev/null
+++ b/noatun/modules/simple/pause.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+const char * pause_xpm[] = {
+"16 16 2 1",
+" c None",
+". c #000000",
+" ",
+" ",
+" ",
+" .... .... ",
+" .... .... ",
+" .... .... ",
+" .... .... ",
+" .... .... ",
+" .... .... ",
+" .... .... ",
+" .... .... ",
+" .... .... ",
+" .... .... ",
+" ",
+" ",
+" "};
diff --git a/noatun/modules/simple/play.xpm b/noatun/modules/simple/play.xpm
new file mode 100644
index 00000000..7cb55a88
--- /dev/null
+++ b/noatun/modules/simple/play.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+const char * play_xpm[] = {
+"16 16 2 1",
+" c None",
+". c #000000",
+" ",
+" ",
+" ",
+" .. ",
+" ... ",
+" .... ",
+" ..... ",
+" ...... ",
+" ...... ",
+" ..... ",
+" .... ",
+" ... ",
+" .. ",
+" ",
+" ",
+" "};
diff --git a/noatun/modules/simple/playlist.xpm b/noatun/modules/simple/playlist.xpm
new file mode 100644
index 00000000..80b622ac
--- /dev/null
+++ b/noatun/modules/simple/playlist.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+const char * playlist_xpm[] = {
+"16 16 2 1",
+" c None",
+". c #000000",
+" ",
+" ",
+" .. ",
+" .... ",
+" ...... ",
+" ........ ",
+" .......... ",
+" .......... ",
+" ",
+" ",
+" .......... ",
+" .......... ",
+" .......... ",
+" .......... ",
+" ",
+" "};
diff --git a/noatun/modules/simple/propertiesdialog.ui b/noatun/modules/simple/propertiesdialog.ui
new file mode 100644
index 00000000..08f4f71c
--- /dev/null
+++ b/noatun/modules/simple/propertiesdialog.ui
@@ -0,0 +1,348 @@
+<!DOCTYPE UI><UI version="3.0" stdsetdef="1">
+<class>PropertiesDialog</class>
+<author>Ewald Snel</author>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>PropertiesDialog</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>320</width>
+ <height>261</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Properties</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>TabWidget2</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>details</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Details</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>16</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLineEdit" row="0" column="2">
+ <property name="name">
+ <cstring>nameField</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>180</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="Line" row="1" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>Line1</cstring>
+ </property>
+ <property name="frameShape" stdset="0">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>iconLabel</cstring>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignLeft</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="2">
+ <property name="name">
+ <cstring>typeLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <spacer row="7" column="2">
+ <property name="name" stdset="0">
+ <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>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="Line" row="6" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>Line1_2</cstring>
+ </property>
+ <property name="frameShape" stdset="0">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel7_2</cstring>
+ </property>
+ <property name="text">
+ <string>Type:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="1" rowspan="4" colspan="1">
+ <property name="name">
+ <cstring>TextLabel2_2</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>25</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>25</width>
+ <height>32767</height>
+ </size>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Length:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="2">
+ <property name="name">
+ <cstring>lengthLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>TextLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>Audio:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="2">
+ <property name="name">
+ <cstring>audioLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>TextLabel7</cstring>
+ </property>
+ <property name="text">
+ <string>Video:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="2">
+ <property name="name">
+ <cstring>videoLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>playobject</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;PlayObject</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>16</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel2</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>80</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Description:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>descriptionLabel</cstring>
+ </property>
+ </widget>
+ <widget class="QListView" row="1" column="0" rowspan="1" colspan="2">
+ <column>
+ <property name="text">
+ <string>Capabilities</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizeable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>capsList</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>buttonLayout</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer>
+ <property name="name" stdset="0">
+ <cstring>Spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>okButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;OK</string>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>okButton</sender>
+ <signal>clicked()</signal>
+ <receiver>PropertiesDialog</receiver>
+ <slot>accept()</slot>
+ </connection>
+</connections>
+<includes>
+ <include location="global" impldecl="in declaration">noatun/playlist.h</include>
+ <include location="global" impldecl="in declaration">kmedia2.h</include>
+ <include location="global" impldecl="in declaration">kmimetype.h</include>
+ <include location="local" impldecl="in implementation">propertiesdialog.ui.h</include>
+</includes>
+<slots>
+ <slot specifier="non virtual">setPlayObject( PlaylistItem pi, Arts::PlayObject po )</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/noatun/modules/simple/propertiesdialog.ui.h b/noatun/modules/simple/propertiesdialog.ui.h
new file mode 100644
index 00000000..a712d776
--- /dev/null
+++ b/noatun/modules/simple/propertiesdialog.ui.h
@@ -0,0 +1,58 @@
+/*
+ This file is part of KDE/aRts (Noatun) - xine integration
+ Copyright (C) 2002 Ewald Snel <ewald@rambo.its.tudelft.nl>
+
+ 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.
+*/
+
+#include <klocale.h>
+
+void PropertiesDialog::setPlayObject( PlaylistItem pi, Arts::PlayObject po )
+{
+ // Arts::PlayObject properties
+ if (!po.isNull())
+ {
+ Arts::poCapabilities ncaps = po.capabilities();
+ QCheckListItem *item;
+
+ descriptionLabel->setText( po.description().c_str() );
+
+ // Determine capabilities
+ if (!(item = (QCheckListItem *)capsList->findItem( "capSeek", 0 )))
+ {
+ item = new QCheckListItem( capsList, "capSeek",
+ QCheckListItem::CheckBox );
+ }
+ item->setOn( (ncaps & Arts::capSeek) );
+
+ if (!(item = (QCheckListItem *)capsList->findItem( "capPause", 0 )))
+ {
+ item = new QCheckListItem( capsList, "capPause",
+ QCheckListItem::CheckBox );
+ }
+ item->setOn( (ncaps & Arts::capPause) );
+
+ // Defaults
+ nameField->setText( i18n("unknown") );
+ typeLabel->setText( i18n("unknown") );
+ lengthLabel->setText( i18n("unknown") );
+ audioLabel->setText( i18n("unknown") );
+ videoLabel->setText( i18n("unknown") );
+ }
+
+ // PlaylistItem properties (name and mimetype)
+ if (!pi.isNull())
+ {
+ setCaption( i18n("Properties for %1").arg(pi.url().fileName()) );
+
+ KSharedPtr<KMimeType> mime = KMimeType::mimeType( pi.mimetype() );
+ iconLabel->setPixmap( mime->pixmap( KIcon::Desktop, KIcon::SizeMedium ) );
+
+ nameField->setText( pi.url().fileName() );
+ typeLabel->setText( pi.mimetype() );
+ lengthLabel->setText( pi.lengthString() );
+ }
+}
diff --git a/noatun/modules/simple/simple.plugin b/noatun/modules/simple/simple.plugin
new file mode 100644
index 00000000..9d7e4beb
--- /dev/null
+++ b/noatun/modules/simple/simple.plugin
@@ -0,0 +1,126 @@
+Filename=noatunsimple.la
+Author=Ewald Snel
+Site=http://rambo.its.tudelft.nl/xine/
+Email=ewald@rambo.its.tudelft.nl
+Type=userinterface
+License=LGPL
+Name=Simple
+Name[af]=Eenvoudige
+Name[ar]=بسيط
+Name[az]=Bəsit
+Name[bn]=সরল
+Name[br]=Eeun
+Name[bs]=Jednostavno
+Name[ca]=Senzill
+Name[cs]=Jednoduchý
+Name[cy]=Syml
+Name[da]=Simpel
+Name[de]=Einfach
+Name[el]=Απλό
+Name[eo]=Simpla
+Name[et]=Lihtne
+Name[eu]=Sinplea
+Name[fa]=ساده
+Name[fi]=Yksinkertainen
+Name[ga]=Simplí
+Name[he]=פשוט
+Name[hi]=साधारण
+Name[hr]=Jednostavno
+Name[hu]=Egyszerű
+Name[id]=Sederhana
+Name[is]=Einfalt
+Name[it]=Semplice
+Name[km]=សាមញ្ញ
+Name[ko]=간단
+Name[lt]=Paprastas
+Name[lv]=Vienkāršs
+Name[mk]=Едноставен
+Name[mt]=Sempliċi
+Name[nb]=Enkel
+Name[nds]=Eenfach
+Name[ne]=साधारण
+Name[nl]=Eenvoudig
+Name[nn]=Enkel
+Name[pa]=ਸਧਾਰਨ
+Name[pl]=Prosty
+Name[pt]=Simples
+Name[pt_BR]=Simples
+Name[ro]=Simplu
+Name[ru]=Простой
+Name[se]=Oktageardanis
+Name[sk]=Jednoduchý
+Name[sl]=Preprosto
+Name[sr]=Једноставан
+Name[sr@Latn]=Jednostavan
+Name[sv]=Enkel
+Name[ta]=எளிய
+Name[tg]=Содда
+Name[th]=เรียบง่าย
+Name[tr]=Basit
+Name[uk]=Простий
+Name[uz]=Oddiy
+Name[uz@cyrillic]=Оддий
+Name[ven]=Zwoleluwa
+Name[wa]=Simpe
+Name[xh]=Lula
+Name[zh_CN]=简单
+Name[zh_HK]=簡單
+Name[zh_TW]=簡單
+Name[zu]=Okulula
+Comment=Simple GUI (embedded video)
+Comment[af]=Eenvoudige Gui (ingebedde video)
+Comment[bg]=Семпъл графичен интерфейс за Noatun
+Comment[bs]=Jednostavan GUI (uključen video)
+Comment[ca]=IGU senzill (vídeo incrustat)
+Comment[cs]=Jednoduché GUI (vnořené video)
+Comment[cy]=GUI Syml (fideo mewnol)
+Comment[da]=Simpel GUI (indlejret video)
+Comment[de]=Einfache Oberfläche (eingebettetes Video)
+Comment[el]=Απλό περιβάλλον χρήσης (ενσωματωμένο βίντεο)
+Comment[es]=Interfaz gráfica simple (vídeo empotrado)
+Comment[et]=Lihtne kasutajaliides (põimitud video)
+Comment[eu]=GUI sinplea (bideo kapsulatua)
+Comment[fa]=ونک ساده )ویدیوی نهفته(
+Comment[fi]=Yksinkertainen käyttöliittymä (upotettu video)
+Comment[fr]=Interface graphique simple (video incorporée)
+Comment[ga]=Comhéadan simplí (fís leabaithe)
+Comment[gl]=GUI Sinxela (video incrustado)
+Comment[he]=ממשק משתמש גרפי פשוט (וידאו מוטבע)
+Comment[hi]=साधारण जीयूआई (एम्बेडेड वीडियो)
+Comment[hu]=Egyszerű kezelőfelület (beágyazott videó)
+Comment[is]=Einföld myndræn skil (innbyggt)
+Comment[it]=Semplice GUI (video integrato)
+Comment[ja]=シンプルな GUI (埋め込みビデオ)
+Comment[kk]=Қарапайым инерфейсі (ендірілген бейне)
+Comment[km]=ចំណុច​ប្រទាក់​ក្រាហ្វិក​សាមញ្ញ (វីដេអូ​បង្កប់)
+Comment[ko]=간단한 GUI (내장된 비디오)
+Comment[lt]=Paprastas GUI (įdedamas video)
+Comment[mk]=Едноставен графички интерфејс (вградено видео)
+Comment[ms]=GUI Ringkas (video diselitkan)
+Comment[nb]=Enkel GUI (innebygget video)
+Comment[nds]=Eenfach Böversiet (inbett Video)
+Comment[ne]=साधारण GUI (सम्मिलित भिडियो)
+Comment[nl]=Eenvoudige GUI (inbedbare video)
+Comment[nn]=Enkelt grensesnitt (innebygd video)
+Comment[pa]=ਸਧਾਰਨ GUI (ਸ਼ਾਮਲ ਵੀਡਿਓ)
+Comment[pl]=Zwykły motyw (osadzone wideo)
+Comment[pt]=Interface Simples (vídeo embebido)
+Comment[pt_BR]=Interface Simples (vídeo integrado)
+Comment[ro]=Interfaţă grafică simplă (video integrat)
+Comment[ru]=Простой интерфейс (встроенное видео)
+Comment[se]=Oktageardánis lakta (vuojuhuvvon video)
+Comment[sk]=Jednoduché rozhranie (vložené video)
+Comment[sl]=Preprost uporabniški vmesnik (vključen video)
+Comment[sr]=Једноставан GUI (уграђен видео)
+Comment[sr@Latn]=Jednostavan GUI (ugrađen video)
+Comment[sv]=Enkelt grafiskt gränssnitt (inbäddad video)
+Comment[ta]=எளிய GUI (உட்பொதிந்த தாரை)
+Comment[tg]=Интерфейси Графикии содда (видеои дарунгузошта)
+Comment[th]=ส่วนติดต่อผู้ใช้แบบกราฟิกแบบเรียบง่าย (วิดีโอแบบฝัง)
+Comment[tr]=Basit arayüz (gömülü video)
+Comment[uk]=Простий інтерфейс (вмонтоване відео)
+Comment[xh]=GUI Elula (video ebekiweyo)
+Comment[zh_CN]=简单图形用户界面 (嵌入式视频)
+Comment[zh_HK]=簡單圖形用戶(嵌入式視訊)
+Comment[zh_TW]=簡單圖形使用者界面(嵌入式視訊)
+Comment[zu]=I-GUI Elula (ividiyo ehlanganiselwe)
diff --git a/noatun/modules/simple/simpleui.rc b/noatun/modules/simple/simpleui.rc
new file mode 100644
index 00000000..6e5fb86a
--- /dev/null
+++ b/noatun/modules/simple/simpleui.rc
@@ -0,0 +1,37 @@
+<!DOCTYPE kpartgui>
+<!--
+vim: syntax=xml
+-->
+<kpartgui name="simpleui" version="1">
+<MenuBar>
+ <Menu name="file"><text>&amp;File</text>
+ <Action name="_file_open"/>
+ <Separator lineSeparator="true"/>
+ <Action name="_file_properties"/>
+ <Separator lineSeparator="true"/>
+ <Action name="_file_quit"/>
+ </Menu>
+ <Menu name="view">
+ <Action name="view_playlist"/>
+ <Separator lineSeparator="true"/>
+ <Action name="half_size" />
+ <Action name="normal_size" />
+ <Action name="double_size" />
+ <Separator lineSeparator="true"/>
+ <Action name="fullscreen_mode" />
+ </Menu>
+ <Menu name="settings" noMerge="1"><text>&amp;Settings</text>
+ <Action name="options_show_menubar"/>
+ <Action name="options_show_statusbar"/>
+ <Separator lineSeparator="true"/>
+ <Action name="effects"/>
+ <Action name="equalizer"/>
+ <Action name="loop_style"/>
+ <Separator lineSeparator="true"/>
+ <Action name="options_configure"/>
+ </Menu>
+
+ <Action name="menu_actions" />
+</MenuBar>
+
+</kpartgui>
diff --git a/noatun/modules/simple/stop.xpm b/noatun/modules/simple/stop.xpm
new file mode 100644
index 00000000..6b080e94
--- /dev/null
+++ b/noatun/modules/simple/stop.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+const char * stop_xpm[] = {
+"16 16 2 1",
+" c None",
+". c #000000",
+" ",
+" ",
+" ",
+" .......... ",
+" .......... ",
+" .......... ",
+" .......... ",
+" .......... ",
+" .......... ",
+" .......... ",
+" .......... ",
+" .......... ",
+" .......... ",
+" ",
+" ",
+" "};
diff --git a/noatun/modules/simple/userinterface.cpp b/noatun/modules/simple/userinterface.cpp
new file mode 100644
index 00000000..6e7b486f
--- /dev/null
+++ b/noatun/modules/simple/userinterface.cpp
@@ -0,0 +1,379 @@
+/*
+ This file is part of KDE/aRts (Noatun) - xine integration
+ Copyright (C) 2002 Ewald Snel <ewald@rambo.its.tudelft.nl>
+
+ 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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <noatun/app.h>
+#include <noatun/engine.h>
+#include <noatun/player.h>
+#include <noatun/stdaction.h>
+
+#include <kconfig.h>
+#include <kglobal.h>
+#include <kglobalsettings.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kurldrag.h>
+#include <kmenubar.h>
+#include <kpopupmenu.h>
+#include <kstatusbar.h>
+#include <kstdaction.h>
+#include <kstdguiitem.h>
+#include <kurl.h>
+
+#include <qaccel.h>
+#include <qdragobject.h>
+#include <qhbox.h>
+#include <qimage.h>
+#include <qpixmap.h>
+#include <qstrlist.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+
+#include "userinterface.h"
+
+#include "back.xpm"
+#include "forward.xpm"
+#include "pause.xpm"
+#include "play.xpm"
+#include "playlist.xpm"
+#include "stop.xpm"
+#include "volume.xpm"
+
+SimpleUI::SimpleUI()
+ : KMainWindow(0, "NoatunSimpleUI"), UserInterface()
+{
+ setAcceptDrops( true );
+ setCaption( i18n("Noatun") );
+ setIcon( SmallIcon( "noatun" ) );
+
+ setupCentralWidget();
+ setupActions();
+
+ contextMenu = video->popupMenu( this );
+
+ setupGUI( StatusBar|Create, "simpleui.rc" );
+
+ connect( napp->player(), SIGNAL(playing()), SLOT(slotPlaying()) );
+ connect( napp->player(), SIGNAL(stopped()), SLOT(slotStopped()) );
+ connect( napp->player(), SIGNAL(paused()), SLOT(slotPaused()) );
+ connect( napp->player(), SIGNAL(timeout()), SLOT(slotTimeout()) );
+ connect( napp->player(), SIGNAL(newSong()), SLOT(slotChanged()) );
+ connect( napp->player(), SIGNAL(volumeChanged(int)), SLOT(slotVolumeChanged(int)) );
+ connect( napp, SIGNAL(hideYourself()), SLOT(hide()) );
+ connect( napp, SIGNAL(showYourself()), SLOT(show()) );
+
+ napp->player()->handleButtons();
+
+ resize( minimumSize() );
+
+ // Show UI and calculate video widget frame
+ show();
+
+ extra_width = (width() - video->width());
+ extra_height = (height() - video->height());
+
+ // Load configuration
+ KConfig &config = *KGlobal::config();
+ config.setGroup( "Simple" );
+ QString str = config.readEntry( "View", "NormalSize" );
+
+ if (str == "HalfSize")
+ video->setHalfSize();
+ else if (str == "NormalSize")
+ video->setNormalSize();
+ else if (str == "DoubleSize")
+ video->setDoubleSize();
+ else
+ applyMainWindowSettings( &config, "Simple" );
+
+ // PlayObject could be running, update video widget
+ slotChanged();
+
+ video->give();
+}
+
+SimpleUI::~SimpleUI()
+{
+ KConfig &config = *KGlobal::config();
+ saveMainWindowSettings( &config, "Simple" );
+ config.setGroup( "Simple" );
+ QString str;
+
+ if (video->isHalfSize())
+ str = "HalfSize";
+ else if (video->isNormalSize())
+ str = "NormalSize";
+ else if (video->isDoubleSize())
+ str = "DoubleSize";
+ else
+ str = "CustomSize";
+
+ config.writeEntry( "View", str );
+ config.sync();
+}
+
+
+
+void SimpleUI::setupActions()
+{
+ KStdAction::open( napp, SLOT(fileOpen()), actionCollection(), "_file_open" );
+ new KAction( i18n("&Properties"), 0, propertiesDialog, SLOT(show()),
+ actionCollection(), "_file_properties" );
+ KStdAction::quit( napp, SLOT(quit()), actionCollection(), "_file_quit");
+
+ NoatunStdAction::playlist( actionCollection(), "view_playlist" );
+ actionCollection()->insert(video->action( "half_size" ));
+ actionCollection()->insert(video->action( "normal_size" ));
+ actionCollection()->insert(video->action( "double_size" ));
+ actionCollection()->insert(video->action( "fullscreen_mode" ));
+
+ actionCollection()->insert(napp->pluginActionMenu());
+
+ menubarAction = KStdAction::showMenubar(this, SLOT(showMenubar()),
+ actionCollection());
+ statusbarAction = KStdAction::showStatusbar(this, SLOT(showStatusbar()),
+ actionCollection());
+ NoatunStdAction::effects( actionCollection(), "effects" );
+ NoatunStdAction::equalizer( actionCollection(), "equalizer" );
+ NoatunStdAction::loop( actionCollection(), "loop_style" );
+ KStdAction::preferences( napp, SLOT(preferences()), actionCollection() );
+}
+
+void SimpleUI::showMenubar()
+{
+ if(menubarAction->isChecked())
+ menuBar()->show();
+ else
+ menuBar()->hide();
+}
+
+void SimpleUI::showStatusbar()
+{
+ if(statusbarAction->isChecked())
+ statusBar()->show();
+ else
+ statusBar()->hide();
+}
+
+
+void SimpleUI::setupCentralWidget()
+{
+ QVBox *npWidget = new QVBox( this );
+ npWidget->setMargin( 0 );
+ npWidget->setSpacing( 0 );
+
+ positionLabel = new QLabel( statusBar() );
+ positionLabel->setAlignment( AlignVCenter | AlignCenter );
+ positionLabel->setFixedSize( fontMetrics().size( 0, " 00:00/00:00 " ) );
+ statusBar()->addWidget( positionLabel, 0, true );
+
+ video = new VideoFrame( npWidget );
+ connect( video, SIGNAL(adaptSize(int,int)),
+ SLOT(slotAdaptSize(int,int)) );
+ connect( video, SIGNAL(rightButtonPressed(const QPoint &)),
+ SLOT(slotContextMenu(const QPoint &)) );
+
+ QHBox *ctlFrame = new QHBox( npWidget );
+ ctlFrame->setFixedHeight( 38 );
+ ctlFrame->setFrameShape( QFrame::StyledPanel );
+ ctlFrame->setFrameShadow( QFrame::Raised );
+ ctlFrame->setMargin( 6 );
+ ctlFrame->setSpacing( 6 );
+
+ QPushButton *backButton = new QPushButton( ctlFrame );
+ backButton->setFixedSize( 24, 24 );
+ backButton->setPixmap( QPixmap( back_xpm ) );
+ QToolTip::add( backButton, i18n("Back") );
+ connect( backButton, SIGNAL(clicked()), napp->player(), SLOT(back()) );
+
+ stopButton = new QPushButton( ctlFrame );
+ stopButton->setFixedSize( 24, 24 );
+ stopButton->setPixmap( QPixmap( stop_xpm ) );
+ QToolTip::add( stopButton, i18n("Stop") );
+ connect( stopButton, SIGNAL(clicked()), napp->player(), SLOT(stop()) );
+
+ playButton = new QPushButton( ctlFrame );
+ playButton->setFixedSize( 24, 24 );
+ playButton->setPixmap( QPixmap( play_xpm ) );
+ QToolTip::add( playButton, i18n("Play / Pause") );
+ connect( playButton, SIGNAL(clicked()), napp->player(), SLOT(playpause()) );
+
+ QPushButton *forwButton = new QPushButton( ctlFrame );
+ forwButton->setFixedSize( 24, 24 );
+ forwButton->setPixmap( QPixmap( forward_xpm ) );
+ QToolTip::add( forwButton, i18n("Forward") );
+ connect( forwButton, SIGNAL(clicked()), napp->player(), SLOT(forward()) );
+
+ slider = new L33tSlider( 0, 1000, 10, 0, L33tSlider::Horizontal, ctlFrame );
+ slider->setFixedHeight( 24 );
+ slider->setMinimumWidth( 100 );
+ slider->setTickmarks( QSlider::NoMarks );
+ connect( slider, SIGNAL(userChanged(int)), SLOT(slotSkipTo(int)) );
+ connect( slider, SIGNAL(sliderMoved(int)), SLOT(slotSliderMoved(int)) );
+
+ QPushButton *playlistButton = new QPushButton( ctlFrame );
+ playlistButton->setFixedSize( 24, 24 );
+ playlistButton->setPixmap( QPixmap( playlist_xpm ) );
+ QToolTip::add( playlistButton, i18n("Playlist") );
+ connect( playlistButton, SIGNAL(clicked()), napp->player(), SLOT(toggleListView()) );
+
+ volumeButton = new QPushButton( ctlFrame );
+ volumeButton->setFixedSize( 24, 24 );
+ volumeButton->setPixmap( QPixmap( volume_xpm ) );
+ QToolTip::add( volumeButton, i18n("Volume") );
+
+ volumeFrame = new QVBox( this, "Volume", WStyle_Customize | WType_Popup );
+ volumeFrame->setFrameStyle( QFrame::PopupPanel );
+ volumeFrame->setMargin( 4 );
+
+ volumeLabel = new QLabel( volumeFrame );
+ volumeLabel->setText( "100%" );
+ volumeLabel->setAlignment( AlignCenter );
+ volumeLabel->setFixedSize( volumeLabel->sizeHint() );
+
+ QHBox *volumeSubFrame = new QHBox( volumeFrame );
+ volumeSlider = new L33tSlider( 0, 100, 10, 0, Vertical, volumeSubFrame );
+ volumeSlider->setValue( 100 - napp->player()->volume() );
+ volumeSlider->setFixedSize( volumeSlider->sizeHint() );
+
+ volumeFrame->resize( volumeFrame->sizeHint() );
+
+ connect( volumeSlider, SIGNAL(sliderMoved(int)), SLOT(slotVolumeSliderMoved(int)) );
+ connect( volumeSlider, SIGNAL(userChanged(int)), SLOT(slotVolumeSliderMoved(int)) );
+ connect( volumeButton, SIGNAL(clicked()), SLOT(slotVolumeFrame()) );
+
+ setCentralWidget( npWidget );
+
+ video->setMinimumSize( minimumSizeHint().width(), 1 );
+
+ // Create properties dialog
+ propertiesDialog = new PropertiesDialog( this );
+ propertiesDialog->resize( 375, 285 );
+}
+
+void SimpleUI::closeEvent( QCloseEvent * )
+{
+ unload();
+}
+
+void SimpleUI::dragEnterEvent( QDragEnterEvent *event )
+{
+ event->accept( KURLDrag::canDecode( event ) );
+}
+
+void SimpleUI::dropEvent( QDropEvent *event )
+{
+ KURL::List uri;
+ if (KURLDrag::decode( event, uri ))
+ napp->player()->openFile( uri, false );
+}
+
+void SimpleUI::slotAdaptSize( int width, int height )
+{
+ resize( width + extra_width, height + extra_height );
+}
+
+void SimpleUI::slotPlaying()
+{
+ playButton->setPixmap( QPixmap( pause_xpm ) );
+ stopButton->setEnabled( true );
+ slider->setEnabled( true );
+
+ if (napp->player()->current())
+ statusBar()->message( napp->player()->current().title() );
+}
+
+void SimpleUI::slotStopped()
+{
+ playButton->setPixmap( QPixmap( play_xpm ) );
+ stopButton->setEnabled( false );
+ slider->setEnabled( false );
+ slider->setValue( 0 );
+ positionLabel->setText( "" );
+ statusBar()->message( "" );
+}
+
+void SimpleUI::slotPaused()
+{
+ playButton->setPixmap( QPixmap( play_xpm ) );
+ slider->setEnabled( true );
+}
+
+void SimpleUI::slotTimeout()
+{
+ if (napp->player()->current() && !slider->currentlyPressed())
+ {
+ positionLabel->setText( napp->player()->lengthString() );
+ slider->setRange( 0, (int)napp->player()->getLength() / 1000 );
+ slider->setValue( (int)napp->player()->getTime() / 1000 );
+ }
+}
+
+void SimpleUI::slotSkipTo( int sec )
+{
+ napp->player()->skipTo( sec * 1000 );
+}
+
+void SimpleUI::slotChanged()
+{
+ propertiesDialog->setPlayObject( napp->player()->current(),
+ napp->player()->engine()->playObject() );
+}
+
+void SimpleUI::slotContextMenu( const QPoint &pos )
+{
+ contextMenu->exec( pos );
+}
+
+void SimpleUI::slotSliderMoved( int sec )
+{
+ if (napp->player()->current())
+ positionLabel->setText( napp->player()->lengthString( sec * 1000 ) );
+}
+
+void SimpleUI::slotVolumeChanged( int volume )
+{
+ volumeLabel->setText( QString::number( volume ) + "%" );
+ volumeSlider->setValue( 100 - volume );
+}
+
+void SimpleUI::slotVolumeFrame()
+{
+ if (volumeFrame->isVisible())
+ {
+ volumeFrame->hide();
+ }
+ else
+ {
+ int x = (volumeButton->width() - volumeFrame->width()) / 2;
+ int y = -(volumeFrame->height() + 5);
+
+ QPoint point( volumeButton->mapToGlobal( QPoint( x, y ) ) );
+ QRect deskRect = KGlobalSettings::desktopGeometry( point );
+
+ bool bottom = (point.y() + volumeFrame->height()) > (deskRect.y() + deskRect.height());
+ bool right = (point.x() + volumeFrame->width()) > (deskRect.x() + deskRect.width());
+
+ volumeFrame->move(
+ right ? (deskRect.x() + deskRect.width()) - volumeFrame->width() : ( point.x() < 0 ? 0 : point.x() ),
+ bottom ? (deskRect.y() + deskRect.height()) - volumeFrame->height() : ( point.y() < 0 ? 0 : point.y() ) );
+ volumeFrame->show();
+ }
+}
+
+void SimpleUI::slotVolumeSliderMoved( int slider )
+{
+ napp->player()->setVolume( 100 - slider );
+}
+
+#include "userinterface.moc"
diff --git a/noatun/modules/simple/userinterface.h b/noatun/modules/simple/userinterface.h
new file mode 100644
index 00000000..c545fd2c
--- /dev/null
+++ b/noatun/modules/simple/userinterface.h
@@ -0,0 +1,80 @@
+/*
+ This file is part of KDE/aRts (Noatun) - xine integration
+ Copyright (C) 2002 Ewald Snel <ewald@rambo.its.tudelft.nl>
+
+ 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.
+*/
+
+#ifndef __USERINTERFACE_H
+#define __USERINTERFACE_H
+
+#include <noatun/controls.h>
+#include <noatun/plugin.h>
+#include <noatun/video.h>
+#include <kaction.h>
+#include <kmainwindow.h>
+#include <kmedia2.h>
+#include <qevent.h>
+#include <qlabel.h>
+#include <qpopupmenu.h>
+#include <qpushbutton.h>
+#include <qvbox.h>
+#include "propertiesdialog.h"
+
+
+class SimpleUI : public KMainWindow, public UserInterface
+{
+ Q_OBJECT
+
+public:
+ SimpleUI();
+ ~SimpleUI();
+
+protected:
+ void setupActions();
+ void setupCentralWidget();
+
+ virtual void closeEvent( QCloseEvent * );
+ virtual void dragEnterEvent( QDragEnterEvent *event );
+ virtual void dropEvent( QDropEvent *event );
+
+public slots:
+ void slotAdaptSize( int width, int height );
+ void slotPlaying();
+ void slotStopped();
+ void slotPaused();
+ void slotTimeout();
+ void slotSkipTo( int sec );
+ void slotChanged();
+ void slotContextMenu( const QPoint &pos );
+
+private slots:
+ void slotSliderMoved( int sec );
+ void slotVolumeSliderMoved( int volume );
+ void slotVolumeFrame();
+ void slotVolumeChanged( int volume );
+ void showMenubar();
+ void showStatusbar();
+
+private:
+ PropertiesDialog *propertiesDialog;
+ QPopupMenu *contextMenu;
+ QPushButton *stopButton;
+ QPushButton *playButton;
+ QPushButton *volumeButton;
+ QVBox *volumeFrame;
+ QLabel *volumeLabel;
+ QLabel *positionLabel;
+ VideoFrame *video;
+ L33tSlider *volumeSlider;
+ L33tSlider *slider;
+ int extra_width;
+ int extra_height;
+ KToggleAction *menubarAction;
+ KToggleAction *statusbarAction;
+};
+
+#endif
diff --git a/noatun/modules/simple/volume.xpm b/noatun/modules/simple/volume.xpm
new file mode 100644
index 00000000..fa240d42
--- /dev/null
+++ b/noatun/modules/simple/volume.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+const char * volume_xpm[] = {
+"16 16 2 1",
+" c None",
+". c #000000",
+" ",
+" ",
+" . .. ",
+" .. . . ",
+" .... . . ",
+" .... . . ",
+" .. .. . ",
+" . ... . ",
+" .... . ",
+" ..... . ",
+" ...... . ",
+" ....... . ",
+" ........ . ",
+" ............ ",
+" ",
+" "};
diff --git a/noatun/modules/splitplaylist/LICENSE b/noatun/modules/splitplaylist/LICENSE
new file mode 100644
index 00000000..8f9bdefc
--- /dev/null
+++ b/noatun/modules/splitplaylist/LICENSE
@@ -0,0 +1,124 @@
+The "Artistic License"
+
+ Preamble
+
+ The intent of this document is to state the conditions under which a
+ Package may be copied, such that the Copyright Holder maintains some
+ semblance of artistic control over the development of the package,
+ while giving the users of the package the right to use and distribute
+ the Package in a more-or-less customary fashion, plus the right to
+ make reasonable modifications.
+
+ Definitions
+
+ "Package" refers to the collection of files distributed by the
+ Copyright Holder, and derivatives of that collection of files
+ created through textual modification.
+
+ "Standard Version" refers to such a Package if it has not been
+ modified, or has been modified in accordance with the wishes of the
+ Copyright Holder as specified below.
+
+ "Copyright Holder" is whoever is named in the copyright or
+ copyrights for the package.
+
+ "You" is you, if you're thinking about copying or distributing this
+ Package.
+
+ "Reasonable copying fee" is whatever you can justify on the basis
+ of media cost, duplication charges, time of people involved, and so
+ on. (You will not be required to justify it to the Copyright
+ Holder, but only to the computing community at large as a market
+ that must bear the fee.)
+
+ "Freely Available" means that no fee is charged for the item
+ itself, though there may be fees involved in handling the item. It
+ also means that recipients of the item may redistribute it under
+ the same conditions they received it.
+
+ 1. You may make and give away verbatim copies of the source form of
+ the Standard Version of this Package without restriction, provided
+ that you duplicate all of the original copyright notices and
+ associated disclaimers.
+ 2. You may apply bug fixes, portability fixes and other modifications
+ derived from the Public Domain or from the Copyright Holder. A
+ Package modified in such a way shall still be considered the
+ Standard Version.
+ 3. You may otherwise modify your copy of this Package in any way,
+ provided that you insert a prominent notice in each changed file
+ stating how and when you changed that file, and provided that you
+ do at least ONE of the following:
+
+ a. place your modifications in the Public Domain or otherwise make
+ them Freely Available, such as by posting said modifications to
+ Usenet or an equivalent medium, or placing the modifications on a
+ major archive site such as uunet.uu.net, or by allowing the
+ Copyright Holder to include your modifications in the Standard
+ Version of the Package.
+ b. use the modified Package only within your corporation or
+ organization.
+ c. rename any non-standard executables so the names do not conflict
+ with standard executables, which must also be provided, and
+ provide a separate manual page for each non-standard executable
+ that clearly documents how it differs from the Standard Version.
+ d. make other distribution arrangements with the Copyright Holder.
+
+ You may distribute the programs of this Package in object code or
+ executable form, provided that you do at least ONE of the following:
+
+ a. distribute a Standard Version of the executables and library
+ files, together with instructions (in the manual page or
+ equivalent) on where to get the Standard Version.
+ b. accompany the distribution with the machine-readable source of the
+ Package with your modifications.
+ c. give non-standard executables non-standard names, and clearly
+ document the differences in manual pages (or equivalent), together
+ with instructions on where to get the Standard Version.
+ d. make other distribution arrangements with the Copyright Holder.
+
+ You may charge a reasonable copying fee for any distribution of this
+ Package. You may charge any fee you choose for support of this
+ Package. You may not charge a fee for this Package itself. However,
+ you may distribute this Package in aggregate with other (possibly
+ commercial) programs as part of a larger (possibly commercial)
+ software distribution provided that you do not advertise this Package
+ as a product of your own. You may embed this Package's interpreter
+ within an executable of yours (by linking); this shall be construed as
+ a mere form of aggregation, provided that the complete Standard
+ Version of the interpreter is so embedded.
+
+ The scripts and library files supplied as input to or produced as
+ output from the programs of this Package do not automatically fall
+ under the copyright of this Package, but belong to whomever generated
+ them, and may be sold commercially, and may be aggregated with this
+ Package. If such scripts or library files are aggregated with this
+ Package via the so-called "undump" or "unexec" methods of producing a
+ binary executable image, then distribution of such an image shall
+ neither be construed as a distribution of this Package nor shall it
+ fall under the restrictions of Paragraphs 3 and 4, provided that you
+ do not represent such an executable image as a Standard Version of
+ this Package.
+
+ C subroutines (or comparably compiled subroutines in other
+ languages) supplied by you and linked into this Package in order to
+ emulate subroutines and variables of the language defined by this
+ Package shall not be considered part of this Package, but are the
+ equivalent of input as in Paragraph 6, provided these subroutines do
+ not change the language in any way that would cause it to fail the
+ regression tests for the language.
+
+ Aggregation of this Package with a commercial distribution is always
+ permitted provided that the use of this Package is embedded; that is,
+ when no overt attempt is made to make this Package's interfaces
+ visible to the end user of the commercial distribution. Such use shall
+ not be construed as a distribution of this Package.
+
+ The name of the Copyright Holder may not be used to endorse or
+ promote products derived from this software without specific prior
+ written permission.
+
+ THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+ The End
diff --git a/noatun/modules/splitplaylist/Makefile.am b/noatun/modules/splitplaylist/Makefile.am
new file mode 100644
index 00000000..4ea511ac
--- /dev/null
+++ b/noatun/modules/splitplaylist/Makefile.am
@@ -0,0 +1,17 @@
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_splitplaylist.la
+
+noatun_splitplaylist_la_SOURCES = splitplaylist.cpp playlist.cpp view.cpp find.cpp
+
+noatun_splitplaylist_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_splitplaylist_la_LIBADD = $(LIB_KIO) $(top_builddir)/noatun/library/libnoatun.la
+
+noatun_splitplaylist_la_METASOURCES = AUTO
+
+noinst_HEADERS = playlist.h view.h find.h
+
+noatun_modules_splitplaylist_DATA = splitplaylist.plugin
+noatun_modules_splitplaylistdir = $(kde_datadir)/noatun
+
+rc_DATA = splui.rc
+rcdir = $(kde_datadir)/noatun
diff --git a/noatun/modules/splitplaylist/find.cpp b/noatun/modules/splitplaylist/find.cpp
new file mode 100644
index 00000000..b6e196d9
--- /dev/null
+++ b/noatun/modules/splitplaylist/find.cpp
@@ -0,0 +1,63 @@
+#include "find.h"
+#include <qlayout.h>
+#include <kcombobox.h>
+#include <qpushbutton.h>
+#include <qcheckbox.h>
+#include <klocale.h>
+
+Finder::Finder(QWidget *parent) : KDialogBase(parent, 0, false, i18n("Find"), Close | User1, User1, false, KGuiItem(i18n("&Find"),"find"))
+{
+ QWidget *mainWidget = new QWidget(this);
+ mainWidget->setMinimumWidth(320);
+ setMainWidget(mainWidget);
+
+ QGridLayout *layout=new QGridLayout(mainWidget);
+ layout->setSpacing(KDialog::spacingHint());
+
+ mText=new KHistoryCombo(mainWidget);
+ mText->setMaxCount(10);
+
+ mText->setFocus();
+
+ mRegexp=new QCheckBox(i18n("&Regular expression"), mainWidget);
+ mBackwards=new QCheckBox(i18n("Find &backwards"), mainWidget);
+
+ layout->addMultiCellWidget(mText, 0, 0, 0, 1);
+ layout->addWidget(mRegexp, 1, 0);
+ layout->addWidget(mBackwards, 1, 1);
+
+ connect(this, SIGNAL(user1Clicked()), SLOT(clicked()));
+
+ connect(mText, SIGNAL(activated(int)), SLOT(clicked()));
+ connect(mText, SIGNAL(textChanged(const QString &)), SLOT(textChanged(const QString &)));
+
+ enableButton(User1, false);
+}
+
+void Finder::textChanged(const QString &text) {
+ enableButton(User1, !text.isEmpty());
+}
+
+bool Finder::regexp() const
+{
+ return mRegexp->isChecked();
+}
+
+bool Finder::isForward() const
+{
+ return !mBackwards->isChecked();
+}
+
+void Finder::clicked()
+{
+ mText->addToHistory( mText->currentText() );
+ emit search(this);
+}
+
+QString Finder::string() const
+{
+ return mText->currentText();
+}
+
+
+#include "find.moc"
diff --git a/noatun/modules/splitplaylist/find.h b/noatun/modules/splitplaylist/find.h
new file mode 100644
index 00000000..a4791339
--- /dev/null
+++ b/noatun/modules/splitplaylist/find.h
@@ -0,0 +1,33 @@
+#ifndef FIND_H
+#define FIND_H
+
+#include <kdialogbase.h>
+
+class KHistoryCombo;
+class QCheckBox;
+class QPushButton;
+
+class Finder : public KDialogBase
+{
+Q_OBJECT
+public:
+ Finder(QWidget *parent);
+
+ bool regexp() const;
+ bool isForward() const;
+
+ QString string() const;
+signals:
+ void search(Finder *);
+
+public slots:
+ void textChanged(const QString &);
+ void clicked();
+
+private:
+ KHistoryCombo *mText;
+ QCheckBox *mRegexp, *mBackwards;
+};
+
+#endif
+
diff --git a/noatun/modules/splitplaylist/playlist.cpp b/noatun/modules/splitplaylist/playlist.cpp
new file mode 100644
index 00000000..57d6fb48
--- /dev/null
+++ b/noatun/modules/splitplaylist/playlist.cpp
@@ -0,0 +1,281 @@
+#include "playlist.h"
+#include "view.h"
+#include <noatun/player.h>
+
+#include <kapplication.h>
+#include <krandomsequence.h>
+#include <kdebug.h>
+#include <kwin.h>
+
+#include <kiconloader.h>
+
+SplitPlaylist *SplitPlaylist::Self=0;
+
+SplitPlaylist::SplitPlaylist()
+ : Playlist(0, "SplitPlaylist"), Plugin(), mExiting(false)
+{
+ Self=this;
+}
+
+void SplitPlaylist::init()
+{
+ view=new View(this); // 195
+ connect(view->listView(), SIGNAL(executed(QListViewItem*)), SLOT(listItemSelected(QListViewItem*)));
+ connect(view, SIGNAL(shown()), SIGNAL(listShown()));
+ connect(view, SIGNAL(hidden()), SIGNAL(listHidden()));
+
+ view->init(); // 1000
+}
+
+SplitPlaylist::~SplitPlaylist()
+{
+ mExiting=true;
+ delete view;
+}
+
+void SplitPlaylist::reset()
+{
+ SafeListViewItem *i;
+ setCurrent(i=static_cast<SafeListViewItem*>(view->listView()->firstChild()), false);
+ if (i && !i->isOn())
+ next(false);
+}
+
+PlaylistItem SplitPlaylist::next()
+{
+ return next(true);
+}
+
+PlaylistItem SplitPlaylist::next(bool play)
+{
+ PlaylistItem nextItem;
+
+ if (napp->player()->loopStyle() == Player::Random)
+ {
+ // Ignore all this order stuff and select a random item
+ List *lview = view->listView();
+
+ if (lview->childCount())
+ {
+ SafeListViewItem *slvi = static_cast<SafeListViewItem*>(
+ lview->itemAtIndex(KApplication::random() % lview->childCount())
+ );
+ nextItem = PlaylistItem(slvi);
+ }
+ else
+ {
+ nextItem = 0;
+ }
+ }
+ else
+ {
+ if(!current())
+ {
+ nextItem = static_cast<SafeListViewItem*>(static_cast<SafeListViewItem*>(getFirst().data()));
+ }
+ else
+ {
+ nextItem = static_cast<SafeListViewItem*>(
+ static_cast<SafeListViewItem*>(current().data())->itemBelow());
+ }
+ }
+
+ if (!nextItem) // don't set a null-item as current item
+ {
+ return 0;
+// nextItem = static_cast<SafeListViewItem*>(static_cast<SafeListViewItem*>(getFirst().data()));
+ }
+
+ PlaylistItem oldCurrent = currentItem;
+ setCurrent(nextItem, play);
+
+ // Hack for back button on randomized play
+ if (oldCurrent)
+ randomPrevious = oldCurrent;
+
+ if (currentItem)
+ if (!static_cast<SafeListViewItem*>(currentItem.data())->isOn())
+ return next(play);
+
+ return currentItem;
+}
+
+PlaylistItem SplitPlaylist::current()
+{
+ return currentItem;
+}
+
+PlaylistItem SplitPlaylist::previous()
+{
+ if (napp->player()->loopStyle() == Player::Random && randomPrevious)
+ {
+ List *list = view->listView();
+ // check if the item still exists (hackitude: 50%)
+ bool found=false;
+ for (QListViewItem *i = list->firstChild(); i; i = i->nextSibling())
+ {
+ if (i == static_cast<SafeListViewItem*>(randomPrevious.data()))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (found)
+ {
+ // setCurrent modified randomPrevious, and setCurrent is pass-by-reference
+ PlaylistItem prev = randomPrevious;
+
+ setCurrent(prev);
+ return currentItem;
+ }
+ }
+ // there's a possibility that I will fall out to here
+ // from the above branch
+
+ PlaylistItem nextItem;
+ if(!current())
+ {
+ nextItem = static_cast<SafeListViewItem*>(static_cast<SafeListViewItem*>(getFirst().data()));
+ }
+ else
+ {
+ nextItem = static_cast<SafeListViewItem*>(
+ static_cast<SafeListViewItem*>(current().data())->itemAbove());
+ }
+ if (!nextItem) // don't set a null-item as current item
+ return 0;
+
+ setCurrent(nextItem);
+
+ if (currentItem)
+ if (!static_cast<SafeListViewItem*>(currentItem.data())->isOn())
+ return previous();
+
+ return currentItem;
+}
+
+PlaylistItem SplitPlaylist::getFirst() const
+{
+ return static_cast<SafeListViewItem*>(view->listView()->firstChild());
+}
+
+PlaylistItem SplitPlaylist::getAfter(const PlaylistItem &item) const
+{
+ if (item)
+ return static_cast<SafeListViewItem*>(static_cast<const SafeListViewItem*>(item.data())->nextSibling());
+ return 0;
+}
+
+bool SplitPlaylist::listVisible() const
+{
+ KWin::WindowInfo info = KWin::windowInfo(view->winId());
+ return !(info.hasState(NET::Shaded) || info.hasState(NET::Hidden) || !info.valid() || !info.isOnCurrentDesktop());
+}
+
+void SplitPlaylist::showList()
+{
+ KWin::setOnDesktop(view->winId(), KWin::currentDesktop());
+ view->show();
+ if (view->isMinimized())
+ view->showNormal();
+ view->raise();
+}
+
+void SplitPlaylist::hideList()
+{
+ view->hide();
+}
+
+void SplitPlaylist::clear()
+{
+ view->listView()->clear();
+}
+
+void SplitPlaylist::addFile(const KURL &file, bool play)
+{
+ view->addFile(file, play);
+}
+
+void SplitPlaylist::setCurrent(const PlaylistItem &i)
+{
+ setCurrent(i, true);
+}
+
+void SplitPlaylist::setCurrent(const PlaylistItem &i, bool emitC)
+{
+ randomPrevious = PlaylistItem();
+ emitC = emitC && currentItem;
+ if (!i)
+ {
+ currentItem=0;
+ }
+ else
+ {
+ // remove the old icon
+ SafeListViewItem *now=static_cast<SafeListViewItem*>(current().data());
+ if (now)
+ now->setPixmap(0, QPixmap());
+
+ QRect rect(view->listView()->itemRect(static_cast<SafeListViewItem*>(current().data())));
+ rect.setWidth(view->listView()->viewport()->width());
+ currentItem=i;
+ view->listView()->viewport()->repaint(rect,true);
+
+ view->listView()->ensureItemVisible(static_cast<SafeListViewItem*>(current().data()));
+ QRect currentRect= view->listView()->itemRect(static_cast<SafeListViewItem*>(current().data()));
+ view->listView()->viewport()->repaint(currentRect);
+
+ now=static_cast<SafeListViewItem*>(current().data());
+ if(now)
+ now->setPixmap(0, ::SmallIcon("noatunplay"));
+ }
+
+ if (emitC && !exiting())
+ emit playCurrent();
+}
+
+void SplitPlaylist::remove(const PlaylistItem &)
+{
+// delete i;
+}
+
+void SplitPlaylist::listItemSelected(QListViewItem *i)
+{
+ setCurrent(PlaylistItem(static_cast<SafeListViewItem*>(i)), false);
+ emit playCurrent();
+}
+
+void SplitPlaylist::randomize()
+{
+ // turning off sorting is necessary
+ // otherwise, the list will get randomized and promptly sorted again
+ view->setSorting(false);
+ List *lview = view->listView();
+ // eeeeevil :)
+ QPtrList<void> list;
+ QPtrList<QListViewItem> items;
+ for(int i = 0; i < lview->childCount(); i++)
+ {
+ list.append( (void*) i );
+ items.append( lview->itemAtIndex( i ) );
+ }
+
+ KRandomSequence seq;
+ seq.randomize( &list );
+
+ for(int i = 0; i < lview->childCount(); i++)
+ {
+ items.take()->moveItem(lview->itemAtIndex((long) list.take()));
+ }
+
+ setCurrent(currentItem, false);
+}
+
+void SplitPlaylist::sort()
+{
+ view->setSorting(true);
+ setCurrent(currentItem, false);
+}
+
+#include "playlist.moc"
diff --git a/noatun/modules/splitplaylist/playlist.h b/noatun/modules/splitplaylist/playlist.h
new file mode 100644
index 00000000..04cb648d
--- /dev/null
+++ b/noatun/modules/splitplaylist/playlist.h
@@ -0,0 +1,98 @@
+#ifndef PLAYLIST_H
+#define PLAYLIST_H
+
+#include <noatun/playlist.h>
+#include <noatun/plugin.h>
+
+/*
+class PlaylistItem
+{
+ PlaylistItem(const KURL &u=0);
+ virtual ~PlaylistItem();
+
+ QString title() const;
+ virtual void setTitle(const QString &t);
+
+ KURL url() const;
+ virtual void setUrl(const KURL &u);
+
+ int length() const;
+ virtual void setLength(int l);
+};
+*/
+class SafeListViewItem;
+class View;
+class List;
+class QListViewItem;
+
+class SplitPlaylist : public Playlist, public Plugin
+{
+Q_OBJECT
+friend class SafeListViewItem;
+friend class List;
+public:
+ SplitPlaylist();
+ ~SplitPlaylist();
+
+ /**
+ * go to the front
+ **/
+ virtual void reset();
+
+ virtual void clear();
+ virtual void addFile(const KURL&, bool play=false);
+ /**
+ * Cycle everthing through forward
+ **/
+ virtual PlaylistItem next();
+ PlaylistItem next(bool play);
+ /**
+ * return the one that might/should be playing now
+ **/
+ virtual PlaylistItem current();
+ /**
+ * Cycle through backwards
+ **/
+ virtual PlaylistItem previous();
+
+ virtual PlaylistItem getFirst() const;
+ virtual PlaylistItem getAfter(const PlaylistItem &item) const;
+
+ virtual bool listVisible() const;
+ virtual void init();
+
+ virtual Playlist *playlist()
+ { return this; }
+
+ static SplitPlaylist *SPL() { return Self; }
+ inline bool exiting() const { return mExiting; }
+public slots:
+ virtual void showList();
+ virtual void hideList();
+ virtual void remove(const PlaylistItem&);
+ virtual void sort();
+
+
+public slots:
+ void setCurrent(const PlaylistItem &, bool emitC);
+ void setCurrent(const PlaylistItem &);
+
+ void listItemSelected(QListViewItem*);
+
+ void randomize();
+
+private:
+ PlaylistItem currentItem, randomPrevious;
+
+signals:
+ void play(PlaylistItem*);
+
+private:
+ bool mExiting; // HACK HACK HACK HACK!!!
+ View *view;
+// QRect currentRect;
+ static SplitPlaylist *Self;
+};
+
+
+#endif
diff --git a/noatun/modules/splitplaylist/splitplaylist.cpp b/noatun/modules/splitplaylist/splitplaylist.cpp
new file mode 100644
index 00000000..e86a3921
--- /dev/null
+++ b/noatun/modules/splitplaylist/splitplaylist.cpp
@@ -0,0 +1,13 @@
+#include <kcmodule.h>
+
+#include "playlist.h"
+
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new SplitPlaylist();
+ }
+}
+
diff --git a/noatun/modules/splitplaylist/splitplaylist.plugin b/noatun/modules/splitplaylist/splitplaylist.plugin
new file mode 100644
index 00000000..3474dbed
--- /dev/null
+++ b/noatun/modules/splitplaylist/splitplaylist.plugin
@@ -0,0 +1,118 @@
+Filename=noatun_splitplaylist.la
+Author=Charles Samuels
+Site=http://www.derkarl.org/noatun
+Email=charles@kde.org
+Type=playlist
+License=Artistic
+Name=Split Playlist
+Name[af]=Skei Liedjielys
+Name[az]=Çalma Siyahısını Ayır
+Name[bn]=বিভাজিত সঙ্গীত-তালিকা
+Name[br]=Didrochañ ar roll tonioù
+Name[bs]=Podijeli playlistu
+Name[ca]=Dividir la selecció de peces
+Name[cs]=Oddělovací seznam skladeb
+Name[cy]=Hollti Rhestr Chwarae
+Name[da]=Opdelt spilleliste
+Name[de]=Aufgeteilte Wiedergabeliste
+Name[el]=Split λίστα αναπαραγωγής
+Name[eo]=Dividu ludliston
+Name[es]=Lista de reproducción dividida
+Name[et]=Poolitatud nimekiri
+Name[eu]=Erreprodukzio-zerrenda zatitu
+Name[fa]=شکافتن فهرست پخش
+Name[fi]=Jaettu soittolista
+Name[fr]=Liste de lecture découpée
+Name[ga]=Roinn Seinmliosta
+Name[gl]=Dividir Lista de Reproducións
+Name[hi]=स्प्लिट प्लेलिस्ट
+Name[hr]=Razdvoji listu pjesama
+Name[hu]=Osztott lejátszási lista
+Name[is]=Tvískiptur lagalisti
+Name[it]=Split playlist
+Name[km]=ពុះ​បញ្ជី​ចាក់
+Name[ko]=재생 목록 나누기
+Name[lt]=Suskaidytas gaidaraštis
+Name[lv]=Dalīt Plejlistu
+Name[mk]=Раздели листа со нумери
+Name[mt]=Playlist Maqsum
+Name[nb]=Splittet spilleliste
+Name[nds]=Opdeelt Afspeellist
+Name[ne]=बजाउने सूची विभाजन गर्नुहोस्
+Name[nl]=Gesplitste speellijst
+Name[nn]=Delt speleliste
+Name[pa]=ਸੰਗੀਤ-ਸੂਚੀ ਵੰਡੋ
+Name[pl]=Zwykła lista odtwarzania
+Name[pt]=Lista de Músicas Split
+Name[pt_BR]=A lista de reprodução dividida
+Name[ro]=Împarte lista de redare
+Name[ru]=Разбить список произведений
+Name[se]=Juhkkojuvvon čuojahanlistu
+Name[sk]=Rozdeľovací playlist
+Name[sl]=Razdeli predvajalni seznam
+Name[sr]=Split листа нумера
+Name[sr@Latn]=Split lista numera
+Name[sv]=Delad spellista
+Name[ta]=பாடல் பட்டியலை பிரி
+Name[tg]=Рӯйхати бозикуниҳои Пора
+Name[th]=แยกรายการเล่น
+Name[tr]=Ayrılmış Parça Listesi
+Name[uk]=Розбитий список композицій
+Name[ven]=Mutevhe wa tshitambi tshau phadalala
+Name[xh]=Chaka uluhlu lomdlalo
+Name[zh_CN]=分割播放列表
+Name[zh_HK]=分割播放清單
+Name[zh_TW]=分割播放清單
+Name[zu]=Qhekeza uluhlu lomdlalo
+Comment=The inaccurately titled playlist
+Comment[bg]=Неподреден списък за поддръжка на файлове за изпълнение
+Comment[bs]=Neispravno naslovljena playlista
+Comment[ca]=Llista de reproducció titulada inexactament
+Comment[cs]=Nesprávně pojmenovaný seznam skladeb
+Comment[cy]=Y rhestr chwarae efo'r teitl gwallus
+Comment[da]=Den upræcist benævnte spilleliste
+Comment[de]=Die ungenaue Wiedergabeliste
+Comment[el]=Η ανακριβώς ονομαζόμενη λίστα αναπαραγωγής
+Comment[eo]=La neĝuste titolita ludlisto
+Comment[es]=La lista de reproducción con título inexacto
+Comment[et]=Ebaadekvaatselt nimetatud nimekiri
+Comment[eu]=Zehatza ez den izenburudun erreprodukzio-zerrenda
+Comment[fa]=فهرست پخش که با بی‌دقتی عنوان‌بندی شده است
+Comment[fi]=Epätarkasti nimetty soittolista
+Comment[fr]=La liste de lecture mal nommée
+Comment[ga]=An seinmliosta le teideal neamhchruinn
+Comment[gl]=A lista de reprodución con títulos inexactos
+Comment[he]=רשימת הניגון עם השם הלא מדוייק
+Comment[hi]=गलत शीर्षक युक्त गीत-सूची
+Comment[hu]=Nem pontosan feliratozott lejátszási lista
+Comment[is]=Lagalistinn með ónákvæma nafnið
+Comment[it]=Playlist con i titoli non accurati
+Comment[ja]=不正確なタイトルのプレイリスト (実際に分割はしません)
+Comment[kk]=Дұрыс аталмаған орындау тізімі
+Comment[km]=បញ្ជី​ចាក់​ដែល​មាន​ចំណងជើង​មិន​សុក្រឹត
+Comment[ko]=정확하지 않게 제목이 붙은 재생 목록
+Comment[lt]=Netiksliai pavadintas gaidaraštis
+Comment[mk]=Неточно насловена листа со нумери
+Comment[nb]=Den unøyaktig navngitte spillelisten
+Comment[nds]=De nich nau nöömte Afspeellist
+Comment[ne]=अशुद्ध तरिकाले शीर्षक दिइएको बजाउने सूची
+Comment[nl]=De inaccuraat getitelde afspeellijst
+Comment[nn]=Spelelista med unøyaktig namn
+Comment[pl]=Zwykła, prosta lista odtwarzania
+Comment[pt]=A lista mal intitulada
+Comment[pt_BR]=A lista de reprodução erroneamente intitulada
+Comment[ro]=Listă de redare incorect denumită
+Comment[ru]=Список песен с неточными названиями
+Comment[sk]=Nesprávne pomenovaný playlist
+Comment[sl]=Nenatančno naslovljen predvajalni seznam
+Comment[sr]=Непрецизно названа листа нумера
+Comment[sr@Latn]=Neprecizno nazvana lista numera
+Comment[sv]=Den felaktigt benämnda spellistan
+Comment[ta]=பிழையாக தலைப்பிட்ட பாடல் பட்டியல்
+Comment[tg]=Рӯйхати бозикуниҳои бетартибона номгузошташуда
+Comment[th]=รายการเล่นที่มีชื่อไม่ถูกต้อง
+Comment[tr]=Düzensiz başlıklı çalma listesi
+Comment[uk]=Неохайно підписаний список композицій
+Comment[zh_CN]=命名不确切的播放列表
+Comment[zh_HK]=未有正確命名的播放清單
+Comment[zh_TW]=未精確命名的撥放清單
diff --git a/noatun/modules/splitplaylist/splui.rc b/noatun/modules/splitplaylist/splui.rc
new file mode 100644
index 00000000..2b0b0a32
--- /dev/null
+++ b/noatun/modules/splitplaylist/splui.rc
@@ -0,0 +1,34 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<kpartgui name="SPL" version="1">
+
+<MenuBar>
+
+<Menu name="file">
+ <Action name="add_files" />
+ <Action name="add_dir" />
+</Menu>
+
+<Menu name="edit">
+ <Action name="delete" />
+ <Action name="clear" />
+ <Separator />
+ <Action name="shuffle" />
+</Menu>
+
+</MenuBar>
+
+<ToolBar noMerge="1" name="mainToolBar">
+ <text>Main Toolbar</text>
+ <Action name="add_files" />
+ <Action name="add_dir" />
+ <Separator />
+ <Action name="delete" />
+ <Separator />
+ <Action name="file_open" />
+ <Action name="file_save" />
+ <Action name="file_save_as" />
+ <Separator />
+ <Action name="edit_find" />
+</ToolBar>
+
+</kpartgui>
diff --git a/noatun/modules/splitplaylist/view.cpp b/noatun/modules/splitplaylist/view.cpp
new file mode 100644
index 00000000..7f5584f9
--- /dev/null
+++ b/noatun/modules/splitplaylist/view.cpp
@@ -0,0 +1,1009 @@
+/**
+ * Copyright (c) 2000-2004 Charles Samuels <charles@kde.org>
+ * 2000-2001 Neil Stevens <neil@qualityassistant.com>
+ *
+ * Copyright (c) from the patches of:
+ * 2001 Klas Kalass <klas.kalass@gmx.de>
+ * 2001 Anno v. Heimburg <doktor.dos@gmx.de>
+ **/
+
+
+// Abandon All Hope, Ye Who Enter Here
+
+#include <qdragobject.h>
+#include <qheader.h>
+#include <qlayout.h>
+#include <qmap.h>
+#include <qregexp.h>
+#include <qtextstream.h>
+#include <qpainter.h>
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <kfiledialog.h>
+#include <kfileitem.h>
+#include <kio/job.h>
+#include <kio/netaccess.h>
+#include <klocale.h>
+#include <kmenubar.h>
+#include <ksimpleconfig.h>
+#include <kstandarddirs.h>
+#include <kstdaction.h>
+#include <kedittoolbar.h>
+#include <kurldrag.h>
+#include <kmessagebox.h>
+
+#include <noatun/app.h>
+#include <noatun/player.h>
+#include <noatun/playlistsaver.h>
+
+#include "playlist.h"
+#include "view.h"
+#include "find.h"
+
+#define SPL SplitPlaylist::SPL()
+
+SafeListViewItem::SafeListViewItem(QListView *parent, QListViewItem *after, const KURL &text)
+ : QCheckListItem(parent,0, QCheckListItem::CheckBox), PlaylistItemData(), removed(false)
+{
+ addRef();
+ setUrl(text);
+
+ static_cast<KListView*>(parent)->moveItem(this, 0, after);
+ setOn(true);
+
+ // is this really needed, it makes the listview too wide for me :(
+// setText(0,text.filename());
+
+ // if (!isDownloaded()) setText(1, "0%");
+
+// mProperties.setAutoDelete(true);
+
+ if (!streamable() && enqueue(url()))
+ setUrl(KURL(localFilename()));
+
+ PlaylistItemData::added();
+}
+
+SafeListViewItem::SafeListViewItem(QListView *parent, QListViewItem *after, const QMap<QString,QString> &props)
+ : QCheckListItem(parent, 0, QCheckListItem::CheckBox), removed(false)
+{
+ addRef();
+
+ setOn(true);
+
+ // A version of setProperty that assumes a key is unique,
+ // and doesn't call modified for every new key.
+ // Ugly, but this function is a very hot path on playlist loading
+ for (QMap<QString,QString>::ConstIterator i=props.begin(); i!=props.end(); ++i )
+ {
+ QString n = i.key();
+ QString val = i.data();
+
+ if (n=="enabled")
+ {
+ setOn(val!="false" && val!="0");
+ }
+ else
+ {
+ Property p={n,val};
+ mProperties += p;
+ }
+ }
+
+ static_cast<KListView*>(parent)->moveItem(this, 0, after);
+ modified();
+
+ if (!streamable() && enqueue(url()))
+ {
+ KURL u;
+ u.setPath(localFilename());
+
+ setUrl(u);
+ }
+ PlaylistItemData::added();
+}
+
+SafeListViewItem::~SafeListViewItem()
+{
+ remove();
+}
+
+QString SafeListViewItem::file() const
+{
+ return localFilename();
+}
+
+static void pad(QString &str)
+{
+ int len=str.length();
+ int at = 0;
+ int blocklen=0;
+
+ static const int paddingsize=12;
+
+ // not static for reason
+ const QChar chars[paddingsize] =
+ {
+ QChar('0'), QChar('0'), QChar('0'), QChar('0'),
+ QChar('0'), QChar('0'), QChar('0'), QChar('0'),
+ QChar('0'), QChar('0'), QChar('0'), QChar('0')
+ };
+
+ for (int i=0; i < len; i++)
+ {
+ if (str[i].isNumber())
+ {
+ if (!blocklen)
+ at = i;
+ blocklen++;
+ }
+ else if (blocklen)
+ {
+ int pads=paddingsize;
+ pads -= blocklen;
+ str.insert(at, chars, pads);
+ i += pads;
+ blocklen = 0;
+ }
+ }
+ if (blocklen)
+ {
+ int pads=paddingsize;
+ pads -= blocklen;
+ str.insert(at, chars, pads);
+ }
+}
+
+int SafeListViewItem::compare(QListViewItem * i, int col, bool) const
+{
+ QString text1 = text(col);
+ QString text2 = i->text(col);
+
+ pad(text1);
+ pad(text2);
+ return text1.compare(text2);
+}
+
+QString SafeListViewItem::property(const QString &n, const QString &def) const
+{
+ for (QValueList<Property>::ConstIterator i=mProperties.begin(); i != mProperties.end(); ++i)
+ {
+ if ((*i).key==n)
+ return (*i).value;
+ }
+ if (n=="enabled")
+ {
+ if (isOn()) return "true";
+ else return "false";
+ }
+ return def;
+}
+
+void SafeListViewItem::setProperty(const QString &n, const QString &val)
+{
+ if (n=="enabled")
+ {
+ setOn(val!="false" && val!="0");
+ }
+ else
+ {
+ if ( property(n,"") == val )
+ {
+// kdDebug(66666) << "SafeListViewItem::setProperty(), property unchanged!" << endl;
+ return;
+ }
+
+ clearProperty(n);
+ Property p={n,val};
+ mProperties += p;
+ }
+ modified();
+}
+
+void SafeListViewItem::clearProperty(const QString &n)
+{
+ if (n=="enabled")
+ {
+ setOn(true);
+ modified();
+ return;
+ }
+
+ for (QValueList<Property>::Iterator i=mProperties.begin(); i != mProperties.end(); ++i)
+ {
+ if ((*i).key==n)
+ {
+ mProperties.remove(i);
+ modified();
+ break;
+ }
+ }
+}
+
+QStringList SafeListViewItem::properties() const
+{
+ QStringList list;
+ for (QValueList<Property>::ConstIterator i=mProperties.begin(); i != mProperties.end(); ++i)
+ list += (*i).key;
+ list += "enabled";
+ return list;
+}
+
+bool SafeListViewItem::isProperty(const QString &n) const
+{
+ for (QValueList<Property>::ConstIterator i=mProperties.begin(); i != mProperties.end(); ++i)
+ {
+ if ((*i).key==n)
+ return true;
+ }
+ return n=="enabled";
+}
+
+void SafeListViewItem::downloaded(int percent)
+{
+ if (!removed)
+ setText(1, QString::number(percent)+'%');
+}
+
+void SafeListViewItem::downloadTimeout()
+{
+ if (!removed)
+ setText(1, "-");
+}
+
+void SafeListViewItem::downloadFinished()
+{
+ if (!removed)
+ setText(1, "");
+}
+
+void SafeListViewItem::modified()
+{
+ bool widthChangeNeeded = false;
+
+ if (text(0)!=title())
+ {
+ setText(0, title());
+ widthChangeNeeded = true;
+ }
+
+ if (isDownloaded() && length()!=-1 && text(1)!=lengthString())
+ {
+ setText(1, lengthString());
+ widthChangeNeeded = true;
+ }
+
+ if (widthChangeNeeded)
+ widthChanged(-1);
+
+ PlaylistItemData::modified();
+}
+
+void SafeListViewItem::stateChange(bool s)
+{
+ // if you uncheck this, uncheck thet others that
+ // are selected too
+
+ QPtrList<QListViewItem> list=SPL->view->listView()->selectedItems();
+
+ // but not if I'm not selected
+ if (list.containsRef(this))
+ for (QListViewItem *i=list.first(); i != 0; i=list.next())
+ static_cast<QCheckListItem*>(i)->setOn(s);
+ else
+ QCheckListItem::stateChange(s);
+}
+
+void SafeListViewItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align)
+{
+ QCheckListItem::paintCell(p, cg, column, width, align);
+
+ if (SPL->current() == this)
+ {
+ p->save();
+ p->setRasterOp(XorROP);
+ p->fillRect(0, 0, width, height(), QColor(255,255,255));
+
+ p->restore();
+ }
+}
+
+void SafeListViewItem::remove()
+{
+ removed=true;
+ if (napp->player()->current()==this && !itemAbove() && !itemBelow())
+ {
+ napp->player()->stop();
+ SPL->setCurrent(0);
+ }
+ else if (napp->player()->current()==this)
+ {
+// SPL->setCurrent(0);
+// napp->player()->playCurrent();
+
+ if (napp->player()->isPlaying() && !SPL->exiting())
+ napp->player()->forward();
+ else
+ SPL->setCurrent(0);
+ }
+
+ if (listView())
+ {
+ if (SPL->currentItem==this) // just optimizing for least unreadably
+ SPL->setCurrent(static_cast<SafeListViewItem*>(itemBelow()));
+
+ listView()->takeItem(this);
+ }
+ else if (SPL->currentItem==this)
+ {
+ SPL->setCurrent(0);
+ }
+
+ dequeue();
+ PlaylistItemData::removed();
+}
+
+List::List(View *parent)
+ : KListView(parent), recursiveAddAfter(0), listJob(0)
+{
+ addColumn(i18n("File"));
+ addColumn(i18n("Time"));
+ setAcceptDrops(true);
+ setSorting(-1);
+ setDropVisualizer(true);
+ setDragEnabled(true);
+ setItemsMovable(true);
+ setSelectionMode(QListView::Extended);
+ connect(this, SIGNAL(dropped(QDropEvent*, QListViewItem*)), SLOT(dropEvent(QDropEvent*, QListViewItem*)));
+ connect(this, SIGNAL(moved()), SLOT(move()));
+ connect(this, SIGNAL(aboutToMove()), parent, SLOT(setNoSorting()));
+ connect(this, SIGNAL(deleteCurrentItem()), parent, SLOT(deleteSelected()));
+}
+
+List::~List()
+{
+}
+
+void List::move()
+{
+ emit modified();
+}
+
+bool List::acceptDrag(QDropEvent *event) const
+{
+ return KURLDrag::canDecode(event) || KListView::acceptDrag(event);
+}
+
+void List::dropEvent(QDropEvent *event, QListViewItem *after)
+{
+ static_cast<View*>(parent())->setNoSorting();
+ KURL::List textlist;
+ if (!KURLDrag::decode(event, textlist)) return;
+ event->acceptAction();
+
+ for (KURL::List::Iterator i=textlist.begin(); i != textlist.end(); ++i)
+ {
+ after= addFile(*i, false, after);
+ }
+
+ emit modified();
+}
+
+void List::keyPressEvent(QKeyEvent *e)
+{
+ if (e->key()==Key_Enter || e->key()==Key_Return)
+ {
+ if (currentItem())
+ {
+ emit KListView::executed(currentItem());
+ }
+
+ return;
+ }
+
+ if (e->key()==Key_Delete)
+ {
+ if (currentItem())
+ {
+ emit deleteCurrentItem();
+ }
+
+ return;
+ }
+
+ KListView::keyPressEvent(e);
+
+
+}
+
+/**
+ * use this only once!!!
+ **/
+class NoatunSaver : public PlaylistSaver
+{
+ List *mList;
+ SafeListViewItem *after, *mFirst;
+public:
+ NoatunSaver(List *l, QListViewItem *after=0)
+ : mList(l)
+ {
+ this->after = static_cast<SafeListViewItem*>(after);
+ mFirst = 0;
+ }
+
+ QListViewItem *getAfter() { return after; }
+ QListViewItem *getFirst() { return mFirst; }
+
+protected:
+ virtual void readItem(const QMap<QString,QString> &properties)
+ {
+ after = new SafeListViewItem(mList, after, properties);
+ if (mFirst==0)
+ mFirst = after;
+ }
+
+ virtual PlaylistItem writeItem()
+ {
+ if (!after)
+ {
+ after=static_cast<SafeListViewItem*>(mList->firstChild());
+ }
+ else
+ {
+ after=static_cast<SafeListViewItem*>(static_cast<SafeListViewItem*>(after)->nextSibling());
+ }
+ return PlaylistItem(after);
+ }
+};
+
+
+bool View::saveToURL(const KURL &url)
+{
+ NoatunSaver saver(list);
+ if(saver.save(url))
+ {
+ return true;
+ }
+ else
+ {
+ KMessageBox::error( this, i18n("Could not write to %1.").arg(url.prettyURL()) );
+ return false;
+ }
+}
+
+void View::exportTo(const KURL &url)
+{
+ QString local(napp->tempSaveName(url.path()));
+ QFile saver(local);
+ saver.open(IO_ReadWrite | IO_Truncate);
+ QTextStream t(&saver);
+ // navigate the list
+ for (SafeListViewItem *i=static_cast<SafeListViewItem*>(listView()->firstChild());
+ i != 0; i=static_cast<SafeListViewItem*>(i->itemBelow()))
+ {
+ KURL u=i->url();
+ if (u.isLocalFile())
+ t<< u.path() << '\n';
+ else
+ t << u.url() << '\n';
+ }
+ saver.close();
+
+ KIO::NetAccess::upload(local, url, this);
+
+ saver.remove();
+}
+
+QListViewItem *List::openGlobal(const KURL &u, QListViewItem *after)
+{
+ clear();
+ NoatunSaver saver(this, after);
+ saver.metalist(u);
+
+ return saver.getAfter();
+}
+
+// for m3u files
+QListViewItem *List::importGlobal(const KURL &u, QListViewItem *after)
+{
+ NoatunSaver saver(this, after);
+ if (!saver.metalist(u))
+ {
+ after=new SafeListViewItem(this, after, u);
+// SPL->listItemSelected(after);
+ return after;
+ }
+
+ // return the first item added from this playlist
+ // that way noatun can start playing the first item
+ if (saver.getFirst())
+ return saver.getFirst();
+
+ // failsafe in case nothing was added, getFirst() may return 0
+ return saver.getAfter();
+}
+
+QListViewItem *List::addFile(const KURL& url, bool play, QListViewItem *after)
+{
+ // when a new item is added, we don't want to sort anymore
+ SPL->view->setNoSorting();
+
+ if (
+ url.path().right(4).lower()==".m3u"
+ || url.path().right(4).lower()==".pls"
+ || url.protocol().lower()=="http"
+ )
+ {
+ // a playlist is requested
+ QListViewItem *i = importGlobal(url, after);
+ if (play)
+ SPL->listItemSelected(i);
+ return i;
+ }
+ else
+ {
+ if (!after) after=lastItem();
+ KFileItem fileItem(KFileItem::Unknown,KFileItem::Unknown,url);
+ if (fileItem.isDir())
+ {
+ addDirectoryRecursive(url, after);
+ return after; // don't (and can't) know better!?
+ }
+ else
+ {
+ QListViewItem *i = new SafeListViewItem(this, after, url);
+ if (play)
+ SPL->listItemSelected(i);
+ return i;
+ }
+ }
+}
+
+// starts a new listJob if there is no active but work to do
+void List::addNextPendingDirectory()
+{
+ KURL::List::Iterator pendingIt= pendingAddDirectories.begin();
+ if (!listJob && (pendingIt!= pendingAddDirectories.end()))
+ {
+ currentJobURL= *pendingIt;
+ listJob= KIO::listRecursive(currentJobURL, false,false);
+ connect(
+ listJob, SIGNAL(entries(KIO::Job*, const KIO::UDSEntryList&)),
+ SLOT(slotEntries(KIO::Job*, const KIO::UDSEntryList&))
+ );
+ connect(
+ listJob, SIGNAL(result(KIO::Job *)),
+ SLOT(slotResult(KIO::Job *))
+ );
+ connect(
+ listJob, SIGNAL(redirection(KIO::Job *, const KURL &)),
+ SLOT(slotRedirection(KIO::Job *, const KURL &))
+ );
+ pendingAddDirectories.remove(pendingIt);
+ }
+}
+
+void List::addDirectoryRecursive(const KURL &dir, QListViewItem *after)
+{
+ if (!after) after=lastItem();
+ recursiveAddAfter= after;
+ pendingAddDirectories.append(dir);
+ addNextPendingDirectory();
+}
+
+void List::slotResult(KIO::Job *job)
+{
+ listJob= 0;
+ if (job && job->error())
+ job->showErrorDialog();
+ addNextPendingDirectory();
+}
+
+void List::slotEntries(KIO::Job *, const KIO::UDSEntryList &entries)
+{
+ QMap<QString,KURL> __list; // temp list to sort entries
+
+ KIO::UDSEntryListConstIterator it = entries.begin();
+ KIO::UDSEntryListConstIterator end = entries.end();
+
+ for (; it != end; ++it)
+ {
+ KFileItem file(*it, currentJobURL, false /* no mimetype detection */, true);
+ // "prudhomm:
+ // insert the path + url in the map to sort automatically by path
+ // note also that you use audiocd to rip your CDs then it will be sorted the right way
+ // now it is an easy fix to have a nice sort BUT it is not the best
+ // we should sort based on the tracknumber"
+ // - copied over from old kdirlister hack <hans_meine@gmx.de>
+ if (!file.isDir())
+ __list.insert(file.url().path(), file.url());
+ }
+ QMap<QString,KURL>::Iterator __it;
+ for( __it = __list.begin(); __it != __list.end(); ++__it )
+ {
+ recursiveAddAfter= addFile(__it.data(), false, recursiveAddAfter);
+ }
+}
+
+void List::slotRedirection(KIO::Job *, const KURL & url)
+{
+ currentJobURL= url;
+}
+
+/////////////////////////////////
+
+View::View(SplitPlaylist *)
+ : KMainWindow(0, "NoatunSplitplaylistView")
+{
+ list=new List(this);
+ setCentralWidget(list);
+ connect(list, SIGNAL(modified(void)), this, SLOT(setModified(void)) );
+ // connect the click on the header with sorting
+ connect(list->header(),SIGNAL(clicked(int)),this,SLOT(headerClicked(int)) );
+
+ mOpen=new KAction(i18n("Add &Files..."), "queue", 0, this, SLOT(addFiles()), actionCollection(), "add_files");
+ (void) new KAction(i18n("Add Fol&ders..."), "folder", 0, this, SLOT(addDirectory()), actionCollection(), "add_dir");
+ mDelete=new KAction(i18n("Delete"), "editdelete", Key_Delete, this, SLOT(deleteSelected()), actionCollection(), "delete");
+
+ mClose=KStdAction::close(this, SLOT(close()), actionCollection());
+ mFind=KStdAction::find(this, SLOT(find()), actionCollection());
+
+ (void) KStdAction::configureToolbars(this, SLOT(configureToolBars()), actionCollection());
+ mOpenNew=KStdAction::openNew(this, SLOT(openNew()), actionCollection());
+ mOpenpl=KStdAction::open(this, SLOT(open()), actionCollection());
+ mSave=KStdAction::save(this, SLOT(save()), actionCollection());
+ mSaveAs=KStdAction::saveAs(this, SLOT(saveAs()), actionCollection());
+
+ (void) new KAction(i18n("Shuffle"), "misc", 0, SPL, SLOT( randomize() ), actionCollection(), "shuffle");
+ (void) new KAction(i18n("Clear"), "editclear", 0, list, SLOT( clear() ), actionCollection(), "clear");
+
+ createGUI("splui.rc");
+
+ mFinder = new Finder(this);
+
+ applyMainWindowSettings(KGlobal::config(), "SPL Window");
+ list->setFocus();
+}
+
+void View::find()
+{
+ mFinder->show();
+ connect(mFinder, SIGNAL(search(Finder*)), SLOT(findIt(Finder*)));
+}
+
+static bool testWord(QListViewItem *i, const QString &finder)
+{
+ PlaylistItemData *item=static_cast<SafeListViewItem*>(i);
+ if (item->title().find(finder, 0, false) >=0)
+ return true;
+ if (item->file().find(finder, 0, false) >=0)
+ return true;
+ if (item->url().path().find(finder.local8Bit(), 0, false) >=0)
+ return true;
+ if (item->lengthString().find(finder, 0, false) >=0)
+ return true;
+ if (item->mimetype().find(finder.local8Bit(), 0, false) >=0)
+ return true;
+ return false;
+}
+
+static bool testWord(QListViewItem *i, const QRegExp &finder)
+{
+ PlaylistItemData *item=static_cast<SafeListViewItem*>(i);
+ if (item->title().find(finder) >=0)
+ return true;
+ if (item->file().find(finder) >=0)
+ return true;
+ if (item->url().path().find(finder) >=0)
+ return true;
+ if (item->lengthString().find(finder) >=0)
+ return true;
+ if (item->mimetype().find(finder) >=0)
+ return true;
+ return false;
+}
+
+void View::findIt(Finder *f)
+{
+ QListViewItem *search=list->currentItem();
+
+ if (list->currentItem())
+ {
+ if (f->isForward())
+ search=list->currentItem()->itemBelow();
+ else
+ search=list->currentItem()->itemAbove();
+ }
+ else
+ {
+ if (f->isForward())
+ search=list->firstChild();
+ else
+ search=list->lastChild();
+ }
+
+
+ while (search)
+ {
+ if (f->regexp())
+ {
+ if (testWord(search, QRegExp(f->string(), false)))
+ break;
+ }
+ else
+ {
+ if (testWord(search, f->string()))
+ break;
+ }
+
+ if (f->isForward())
+ search=search->itemBelow();
+ else
+ search=search->itemAbove();
+
+ if (!search)
+ {
+ if (f->isForward())
+ {
+ if (KMessageBox::questionYesNo(this, i18n("End of playlist reached. Continue searching from beginning?"),QString::null,KStdGuiItem::cont(),KStdGuiItem::cancel()) == KMessageBox::Yes)
+ search=list->firstChild();
+ }
+ else
+ {
+ if (KMessageBox::questionYesNo(this, i18n("Beginning of playlist reached. Continue searching from end?"),QString::null,KStdGuiItem::cont(),KStdGuiItem::cancel()) == KMessageBox::Yes)
+ search=list->lastChild();
+ }
+ }
+ }
+
+ if (search)
+ {
+ { // select none
+ QPtrList<QListViewItem> sel=list->selectedItems();
+ for (QListViewItem *i=sel.first(); i!=0; i=sel.next())
+ list->setSelected(i, false);
+ }
+ list->setSelected(search, true);
+ list->setCurrentItem(search);
+ list->ensureItemVisible(search);
+ }
+}
+
+View::~View()
+{
+ napp->player()->stop();
+ hide();
+ saveState();
+ delete list;
+}
+
+void View::init()
+{
+ // see if we are importing an old-style list
+ bool importing= ! QFile(napp->dirs()->saveLocation("data", "noatun/") + "splitplaylist.xml").exists();
+
+ if (importing)
+ {
+ KURL internalURL;
+ internalURL.setPath(napp->dirs()->saveLocation("data", "noatun/") + "splitplaylistdata");
+ NoatunSaver saver(list, 0);
+ saver.load(internalURL, PlaylistSaver::M3U);
+ }
+ else
+ {
+ KURL internalURL;
+ internalURL.setPath(napp->dirs()->saveLocation("data", "noatun/") + "splitplaylist.xml");
+ list->openGlobal(internalURL);
+ }
+
+ KConfig &config = *KGlobal::config();
+ config.setGroup("splitplaylist");
+
+ // this has to come after openGlobal, since openGlobal emits modified()
+ setModified(config.readBoolEntry("modified", false));
+ QString path = config.readPathEntry("file");
+ // don't call setPath with an empty path, that would make the url "valid"
+ if ( !path.isEmpty() )
+ mPlaylistFile.setPath(path);
+
+ SPL->reset();
+ int saved = config.readNumEntry("current", 0);
+
+ PlaylistItem item=SPL->getFirst();
+ for(int i = 0 ; i < saved ; i++)
+ {
+ item=SPL->getAfter(item);
+ }
+ if (item)
+ SPL->setCurrent(item);
+}
+
+void View::save()
+{
+ if(mPlaylistFile.isEmpty() || !mPlaylistFile.isValid())
+ {
+ saveAs();
+ return;
+ }
+
+ if(saveToURL(mPlaylistFile))
+ setModified(false);
+}
+
+void View::saveAs()
+{
+ KURL u=KFileDialog::getSaveURL(0, "*.xml splitplaylistdata *.pls *.m3u\n*", this, i18n("Save Playlist"));
+ if(!u.isValid())
+ return;
+ mPlaylistFile = u;
+ save();
+}
+
+void View::open()
+{
+ KURL u=KFileDialog::getOpenURL(0, "*.xml splitplaylistdata *.pls *.m3u\n*", this, i18n("Open Playlist"));
+ if(!u.isValid())
+ return;
+ mPlaylistFile = u;
+ list->openGlobal(u);
+ setModified(false);
+}
+
+void View::openNew()
+{
+ mPlaylistFile = "";
+ listView()->clear();
+}
+
+void List::clear()
+{
+ SPL->setCurrent(0);
+
+ QListView::clear();
+}
+
+void View::deleteSelected()
+{
+ QPtrList<QListViewItem> items(list->selectedItems());
+
+ bool stopped=false;
+ // noatun shouldn't play files for now
+ QListViewItem *afterLast=0;
+
+ for (QPtrListIterator<QListViewItem> it(items); it.current(); ++it)
+ {
+ SafeListViewItem *i = static_cast<SafeListViewItem*>(*it);
+ if (!stopped && SPL->current() == i)
+ {
+ napp->player()->stop();
+ SPL->setCurrent(0);
+ stopped = true;
+ }
+ i->remove();
+
+ afterLast = i->itemBelow();
+ }
+
+ if (stopped)
+ SPL->setCurrent(static_cast<SafeListViewItem*>(afterLast));
+
+ setModified(true);
+}
+
+void View::addFiles()
+{
+ KURL::List files=KFileDialog::getOpenURLs(":mediadir", napp->mimeTypes(), this, i18n("Select File to Play"));
+
+ QListViewItem *last = list->lastItem();
+ for(KURL::List::Iterator it=files.begin(); it!=files.end(); ++it)
+ last = addFile(KURL(*it), false);
+
+ setModified(true);
+}
+
+void View::addDirectory()
+{
+ QString file=KFileDialog::getExistingDirectory(0, this, i18n("Select Folder"));
+
+ if (!file) return;
+ KURL url;
+ url.setPath(file);
+ list->addDirectoryRecursive(url);
+
+ setModified(true);
+}
+
+void View::closeEvent(QCloseEvent*)
+{
+ hide();
+}
+
+void View::showEvent(QShowEvent *)
+{
+ emit shown();
+}
+
+void View::hideEvent(QHideEvent *)
+{
+ emit hidden();
+}
+
+void View::setModified(bool b)
+{
+ modified = b;
+ setCaption(i18n("Playlist"), modified);
+}
+
+void View::setModified(void)
+{
+ setModified(true);
+}
+
+void View::saveState(void)
+{
+ KConfig &config = *KGlobal::config();
+ config.setGroup("splitplaylist");
+
+ config.writeEntry("modified", modified);
+ config.writePathEntry("file", mPlaylistFile.path());
+ saveToURL(napp->dirs()->saveLocation("data", "noatun/") + "splitplaylist.xml");
+
+ unsigned int i;
+ PlaylistItem item=SPL->getFirst();
+ for(i = 0; item && item != SPL->current(); )
+ item=SPL->getAfter(item), i++;
+
+ config.writeEntry("current", i);
+ saveMainWindowSettings(KGlobal::config(), "SPL Window");
+
+ config.sync();
+}
+
+void View::configureToolBars()
+{
+ saveMainWindowSettings(KGlobal::config(), "SPL Window");
+ KEditToolbar dlg(actionCollection(), "splui.rc");
+ connect(&dlg, SIGNAL(newToolbarConfig()), SLOT(newToolBarConfig()));
+ dlg.exec();
+}
+
+void View::newToolBarConfig()
+{
+ createGUI("splui.rc");
+ applyMainWindowSettings(KGlobal::config(), "SPL Window");
+}
+
+// turns the sorting on or off
+void View::setSorting(bool on, int column)
+{
+ if (on)
+ {
+ list->setSorting(column,true);
+ list->setShowSortIndicator(true);
+ }
+ else
+ {
+ list->setShowSortIndicator(false);
+ list->setSorting(-1);
+ }
+}
+
+void View::headerClicked(int column)
+{
+ // this is to avoid that if we already have it sorted,
+ // we sort it again ascendingly this way, clicking on
+ // the header a second time will correctly toggle
+ // ascending/descending sort
+ if (list->showSortIndicator())
+ {
+ return;
+ }
+ else
+ {
+ setSorting(true,column);
+ }
+}
+
+#include "view.moc"
+
diff --git a/noatun/modules/splitplaylist/view.h b/noatun/modules/splitplaylist/view.h
new file mode 100644
index 00000000..18dc917a
--- /dev/null
+++ b/noatun/modules/splitplaylist/view.h
@@ -0,0 +1,165 @@
+#ifndef VIEW_H
+#define VIEW_H
+
+#include <qevent.h>
+#include <qptrlist.h>
+#include <klistview.h>
+#include <kmainwindow.h>
+#include <qrect.h>
+#include <qdict.h>
+#include <kio/global.h>
+#include <noatun/downloader.h>
+
+class Finder;
+class View;
+namespace KIO { class ListJob; }
+
+
+class SafeListViewItem
+ : public QCheckListItem
+ , public PlaylistItemData
+ , public DownloadItem
+{
+public:
+ SafeListViewItem(QListView *parent, QListViewItem *after, const KURL &text);
+ SafeListViewItem(QListView *parent, QListViewItem *after, const QMap<QString,QString> &properties);
+ virtual ~SafeListViewItem();
+
+ virtual QString property(const QString &, const QString & = 0) const;
+ virtual void setProperty(const QString &, const QString &);
+ virtual void clearProperty(const QString &);
+ virtual QStringList properties() const;
+ virtual bool isProperty(const QString &) const;
+
+ virtual QString file() const;
+
+ int compare(QListViewItem * i, int col, bool ascending) const;
+ virtual void remove();
+
+protected:
+ virtual void downloaded(int percent);
+ virtual void downloadTimeout();
+ virtual void downloadFinished();
+ virtual void modified();
+ virtual void stateChange(bool s);
+
+ void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align);
+
+private:
+ struct Property
+ {
+ QString key;
+ QString value;
+ };
+ QValueList<Property> mProperties;
+ bool removed;
+};
+
+class List : public KListView
+{
+Q_OBJECT
+friend class View;
+public:
+ List(View *parent);
+ virtual ~List();
+ QListViewItem *openGlobal(const KURL&, QListViewItem * =0);
+ QListViewItem *importGlobal(const KURL&, QListViewItem * =0);
+ QListViewItem *addFile(const KURL&, bool play=false, QListViewItem * =0);
+ void addDirectoryRecursive(const KURL &dir, QListViewItem *after= 0);
+
+public slots:
+ virtual void clear();
+
+signals:
+ void modified(void);
+ void deleteCurrentItem();
+
+protected:
+ virtual bool acceptDrag(QDropEvent *event) const;
+ virtual void keyPressEvent(QKeyEvent *e);
+
+protected slots:
+ virtual void dropEvent(QDropEvent *event, QListViewItem *after);
+ void move();
+
+protected:
+ QListViewItem *recursiveAddAfter;
+
+protected slots:
+ // used when adding directories via KIO::listRecursive
+ void slotResult(KIO::Job *job);
+ void slotEntries(KIO::Job *job, const KIO::UDSEntryList &entries);
+ void slotRedirection(KIO::Job *, const KURL & url);
+
+protected:
+ void addNextPendingDirectory();
+ KURL::List pendingAddDirectories;
+ KIO::ListJob *listJob;
+ KURL currentJobURL;
+};
+
+class KFileDialog;
+class KToggleAction;
+class KToolBar;
+
+class View : public KMainWindow
+{
+Q_OBJECT
+public:
+ View(SplitPlaylist *mother);
+ // load the SM playlist
+ void init();
+ virtual ~View();
+ List *listView() const { return list; }
+ QListViewItem *addFile(const KURL &u, bool play=false)
+ { return list->addFile(u, play, list->lastItem()); }
+
+
+public slots:
+ void deleteSelected();
+ void addFiles();
+ void addDirectory();
+ void save();
+ void saveAs();
+ void open();
+ void openNew();
+ void setSorting(bool on, int column = 0);
+ void setNoSorting() { setSorting(false); }
+ void headerClicked(int column);void find();
+ void findIt(Finder *);
+
+
+private slots:
+ void setModified();
+ void saveState();
+
+ void configureToolBars();
+ void newToolBarConfig();
+
+protected:
+ void setupActions();
+
+ bool saveToURL(const KURL &);
+ void exportTo(const KURL &);
+
+ void setModified(bool);
+ virtual void closeEvent(QCloseEvent*e);
+ virtual void showEvent(QShowEvent *);
+ virtual void hideEvent(QHideEvent *);
+
+signals:
+ void hidden();
+ void shown();
+
+private:
+ List *list;
+ KAction *mOpen, *mDelete, *mSave, *mSaveAs, *mOpenpl, *mOpenNew;
+ KAction *mClose;
+ KAction *mFind;
+ Finder *mFinder;
+
+ KURL mPlaylistFile;
+ bool modified;
+};
+
+#endif
diff --git a/noatun/modules/systray/Makefile.am b/noatun/modules/systray/Makefile.am
new file mode 100644
index 00000000..1dcfdd25
--- /dev/null
+++ b/noatun/modules/systray/Makefile.am
@@ -0,0 +1,16 @@
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_systray.la
+
+noatun_systray_la_SOURCES = systray.cpp noatunui.cpp kitsystemtray.cpp cmodule.cpp \
+ yhconfig.kcfgc yhconfigwidget.ui
+
+
+noatun_systray_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_systray_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la
+
+noatun_systray_la_METASOURCES = AUTO
+
+noinst_HEADERS = systray.h kitsystemtray.h cmodule.h
+
+noatun_modules_systray_DATA = systray.plugin systrayui.rc yhconfig.kcfg
+noatun_modules_systraydir = $(kde_datadir)/noatun
diff --git a/noatun/modules/systray/cmodule.cpp b/noatun/modules/systray/cmodule.cpp
new file mode 100644
index 00000000..4adf7e44
--- /dev/null
+++ b/noatun/modules/systray/cmodule.cpp
@@ -0,0 +1,192 @@
+// cmodule.cpp
+//
+// Copyright (C) 2001 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#include "cmodule.h"
+#include "yhconfig.h"
+#include "yhconfigwidget.h"
+
+#include <kdebug.h>
+//#include <kglobal.h>
+#include <klocale.h>
+#include <qbuttongroup.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qradiobutton.h>
+#include <qspinbox.h>
+
+#include <noatun/app.h>
+#include <noatun/pluginloader.h>
+
+#include <fixx11h.h>
+
+YHModule::YHModule(QObject *_parent)
+ : CModule(i18n("System Tray Icon"), i18n("Configure System Tray Icon"),
+ "bottom", _parent)
+{
+ QVBoxLayout *top = new QVBoxLayout(this);
+ mWidget = new YHConfigWidget(this);
+ top->addWidget(mWidget);
+
+ mWidget->cmbModifier->insertItem(i18n("None"), YHConfig::None);
+ mWidget->cmbModifier->insertItem(i18n("Shift"), YHConfig::Shift);
+ mWidget->cmbModifier->insertItem(i18n("Alt"), YHConfig::Alt);
+ mWidget->cmbModifier->insertItem(i18n("Ctrl"), YHConfig::Ctrl);
+ mWidget->cmbModifier->setCurrentItem(YHConfig::None);
+
+ connect(mWidget->chkUsePopup, SIGNAL(toggled(bool)), this, SLOT(slotUsePopupToggled(bool)));
+ connect(mWidget->cmbModifier, SIGNAL(activated(int)), this, SLOT(slotModifierActivated(int)));
+ connect(mWidget->grpMwheel, SIGNAL(clicked(int)), this, SLOT(slotMwheelClicked(int)));
+
+ reopen();
+}
+
+
+void YHModule::reopen()
+{
+ kdDebug(66666) << k_funcinfo << endl;
+ YHConfig *c = YHConfig::self();
+
+ /** General TAB **/
+
+ mWidget->chkUseTooltip->setChecked(c->tip());
+ mWidget->chkUseCovers->setChecked(c->passivePopupCovers());
+
+ mWidget->chkUsePopup->setChecked(c->passivePopup());
+ mWidget->spinPopupTimeout->setValue(c->passivePopupTimeout());
+ mWidget->chkPopupButtons->setChecked(c->passivePopupButtons());
+
+ switch(c->stateIconDisplay())
+ {
+ case (YHConfig::Animation):
+ mWidget->rbStateAnim->setChecked(true);
+ break;
+ case (YHConfig::FlashingIcon):
+ mWidget->rbStateFlashing->setChecked(true);
+ break;
+ case (YHConfig::StaticIcon):
+ mWidget->rbStateStatic->setChecked(true);
+ break;
+ case (YHConfig::NoIcon):
+ mWidget->rbStateNone->setChecked(true);
+ break;
+ }
+
+ /** Advanced TAB **/
+
+ if (c->middleMouseAction() == YHConfig::PlayPause)
+ mWidget->rbPlayPause->setChecked(true);
+ else
+ mWidget->rbHideShowPlaylist->setChecked(true);
+
+ mActionMap[YHConfig::None] = YHConfig::self()->mouseWheelAction(YHConfig::None);
+ mActionMap[YHConfig::Shift] = YHConfig::self()->mouseWheelAction(YHConfig::Shift);
+ mActionMap[YHConfig::Alt] = YHConfig::self()->mouseWheelAction(YHConfig::Alt);
+ mActionMap[YHConfig::Ctrl] = YHConfig::self()->mouseWheelAction(YHConfig::Ctrl);
+
+ slotModifierActivated(mWidget->cmbModifier->currentItem());
+}
+
+
+void YHModule::save()
+{
+ kdDebug(66666) << k_funcinfo << endl;
+
+ YHConfig *c = YHConfig::self();
+
+ /** General TAB **/
+
+ c->setTip(mWidget->chkUseTooltip->isChecked());
+ c->setPassivePopupCovers(mWidget->chkUseCovers->isChecked());
+
+ c->setPassivePopup(mWidget->chkUsePopup->isChecked());
+ c->setPassivePopupTimeout(mWidget->spinPopupTimeout->value());
+ c->setPassivePopupButtons(mWidget->chkPopupButtons->isChecked());
+
+ if (mWidget->rbStateAnim->isChecked())
+ c->setStateIconDisplay(YHConfig::Animation);
+ else if (mWidget->rbStateFlashing->isChecked())
+ c->setStateIconDisplay(YHConfig::FlashingIcon);
+ else if (mWidget->rbStateStatic->isChecked())
+ c->setStateIconDisplay(YHConfig::StaticIcon);
+ else
+ c->setStateIconDisplay(YHConfig::NoIcon);
+
+ /** Advanced TAB **/
+
+ if (mWidget->rbPlayPause->isChecked())
+ c->setMiddleMouseAction(YHConfig::PlayPause);
+ else
+ c->setMiddleMouseAction(YHConfig::HideShowPlaylist);
+
+ c->setMouseWheelAction(YHConfig::None, mActionMap[YHConfig::None]);
+ c->setMouseWheelAction(YHConfig::Shift, mActionMap[YHConfig::Shift]);
+ c->setMouseWheelAction(YHConfig::Alt, mActionMap[YHConfig::Alt]);
+ c->setMouseWheelAction(YHConfig::Ctrl, mActionMap[YHConfig::Ctrl]);
+
+ c->writeConfig();
+ emit saved();
+}
+
+
+void YHModule::slotUsePopupToggled(bool on)
+{
+ mWidget->lblPopupTimeout->setEnabled(on);
+ mWidget->spinPopupTimeout->setEnabled(on);
+ mWidget->chkPopupButtons->setEnabled(on);
+}
+
+
+void YHModule::slotModifierActivated(int index)
+{
+ kdDebug(66666) << k_funcinfo << endl;
+ switch(mActionMap[index])
+ {
+ case (YHConfig::Nothing):
+ mWidget->rbActNothing->setChecked(true);
+ break;
+ case (YHConfig::ChangeVolume):
+ mWidget->rbActVolume->setChecked(true);
+ break;
+ case (YHConfig::ChangeTrack):
+ mWidget->rbActTrack->setChecked(true);
+ break;
+ }
+}
+
+void YHModule::slotMwheelClicked(int index)
+{
+ kdDebug(66666) << k_funcinfo << endl;
+ if (index == 0)
+ mActionMap[mWidget->cmbModifier->currentItem()] = YHConfig::Nothing;
+ else if (index == 1)
+ mActionMap[mWidget->cmbModifier->currentItem()] = YHConfig::ChangeVolume;
+ else
+ mActionMap[mWidget->cmbModifier->currentItem()] = YHConfig::ChangeTrack;
+}
+
+#include "cmodule.moc"
diff --git a/noatun/modules/systray/cmodule.h b/noatun/modules/systray/cmodule.h
new file mode 100644
index 00000000..fecc70e3
--- /dev/null
+++ b/noatun/modules/systray/cmodule.h
@@ -0,0 +1,55 @@
+// cmodule.h
+//
+// Copyright (C) 2001 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifndef CMODULE_H
+#define CMODULE_H
+
+#include <qmap.h>
+#include <noatun/pref.h>
+
+class YHConfigWidget;
+
+class YHModule : public CModule
+{
+Q_OBJECT
+ public:
+ YHModule(QObject *_parent);
+ virtual void save();
+ virtual void reopen();
+
+ signals:
+ void saved();
+
+ private:
+ YHConfigWidget *mWidget;
+ QMap<int, int> mActionMap;
+
+ private slots:
+ void slotUsePopupToggled(bool on);
+ void slotModifierActivated(int index);
+ void slotMwheelClicked(int index);
+};
+
+#endif
diff --git a/noatun/modules/systray/kitsystemtray.cpp b/noatun/modules/systray/kitsystemtray.cpp
new file mode 100644
index 00000000..5847d7da
--- /dev/null
+++ b/noatun/modules/systray/kitsystemtray.cpp
@@ -0,0 +1,131 @@
+// $Id$
+//
+// Kit
+//
+// Copyright (C) 1999 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "yhconfig.h"
+
+#include <noatun/app.h>
+#include <noatun/player.h>
+#include <qdragobject.h>
+
+#include "kitsystemtray.h"
+#include <kmainwindow.h>
+#include <kpopupmenu.h>
+#include <kxmlguifactory.h>
+#include <kiconloader.h>
+#include <kurldrag.h>
+
+#include <fixx11h.h>
+
+KitSystemTray::KitSystemTray(const QString &contextMenu, KMainWindow *parent, const char *name)
+ : KSystemTray(parent, name)
+{
+ setAlignment(AlignHCenter | AlignVCenter);
+ menu = (KPopupMenu *)parent->guiFactory()->container(contextMenu, parent);
+ menu->insertTitle(SmallIcon("noatun"), QString::null, 0, 0);
+ setAcceptDrops(true);
+}
+
+void KitSystemTray::changeTitle(const QPixmap &pixmap, const QString &title)
+{
+ menu->changeTitle(0, pixmap, title);
+}
+
+void KitSystemTray::showEvent(QShowEvent *)
+{
+ // empty
+}
+
+void KitSystemTray::mousePressEvent(QMouseEvent *event)
+{
+ switch(event->button())
+ {
+ case LeftButton:
+ napp->toggleInterfaces();
+ break;
+ case MidButton:
+ if (YHConfig::self()->middleMouseAction() == YHConfig::HideShowPlaylist)
+ napp->playlist()->toggleList();
+ else // play or pause
+ napp->player()->playpause();
+ break;
+ default:
+ menu->popup(event->globalPos());
+ break;
+ }
+}
+
+void KitSystemTray::dragEnterEvent(QDragEnterEvent * event)
+{
+ event->accept(KURLDrag::canDecode(event)); // accept uri drops only
+}
+
+void KitSystemTray::dropEvent(QDropEvent * event)
+{
+ KURL::List uris;
+ if (KURLDrag::decode(event, uris))
+ {
+ KURL::List::ConstIterator it;
+ for (it = uris.begin(); it != uris.end(); ++it)
+ napp->player()->openFile(*it, false);
+ }
+}
+
+void KitSystemTray::wheelEvent(QWheelEvent *event)
+{
+ YHConfig *c = YHConfig::self();
+
+ int action = 0;
+ if (event->state() & Qt::ShiftButton)
+ action = c->mouseWheelAction(YHConfig::Shift);
+ else if (event->state() & Qt::ControlButton)
+ action = c->mouseWheelAction(YHConfig::Ctrl);
+ else if (event->state() & Qt::AltButton)
+ action = c->mouseWheelAction(YHConfig::Alt);
+ else
+ action = c->mouseWheelAction(YHConfig::None);
+
+ switch(action)
+ {
+ case (YHConfig::ChangeVolume):
+ napp->player()->setVolume(napp->player()->volume()+event->delta()/24);
+ break;
+ case (YHConfig::ChangeTrack):
+ if (event->delta() > 0)
+ napp->player()->forward(true);
+ else
+ napp->player()->back();
+ break;
+ default:
+ break;
+ }
+}
+
+#include "kitsystemtray.moc"
diff --git a/noatun/modules/systray/kitsystemtray.h b/noatun/modules/systray/kitsystemtray.h
new file mode 100644
index 00000000..2f640adb
--- /dev/null
+++ b/noatun/modules/systray/kitsystemtray.h
@@ -0,0 +1,54 @@
+// $Id$
+//
+// Kit
+//
+// Copyright (C) 1999 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifndef KITSYSTEMTRAY_H
+#define KITSYSTEMTRAY_H
+
+#include <ksystemtray.h>
+
+class KPopupMenu;
+class KMainWindow;
+class QPixmap;
+
+class KitSystemTray : public KSystemTray
+{
+Q_OBJECT
+
+public:
+ KitSystemTray(const QString &contextMenu, KMainWindow *parent, const char *name = 0);
+ void changeTitle(const QPixmap &, const QString &);
+protected:
+ virtual void showEvent(QShowEvent *);
+ virtual void mousePressEvent(QMouseEvent *);
+ virtual void dragEnterEvent(QDragEnterEvent *);
+ virtual void dropEvent(QDropEvent *);
+ virtual void wheelEvent(QWheelEvent *e);
+
+ KPopupMenu *menu;
+};
+
+#endif
diff --git a/noatun/modules/systray/noatunui.cpp b/noatun/modules/systray/noatunui.cpp
new file mode 100644
index 00000000..76f9af25
--- /dev/null
+++ b/noatun/modules/systray/noatunui.cpp
@@ -0,0 +1,9 @@
+#include "systray.h"
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new NoatunSystray();
+ }
+}
diff --git a/noatun/modules/systray/systray.cpp b/noatun/modules/systray/systray.cpp
new file mode 100644
index 00000000..c93080ca
--- /dev/null
+++ b/noatun/modules/systray/systray.cpp
@@ -0,0 +1,467 @@
+// systray.h
+//
+// Copyright (C) 2000 Neil Stevens <multivac@fcmail.com>
+// Copyright (C) 1999 Charles Samuels <charles@kde.org>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "systray.h"
+#include "kitsystemtray.h"
+#include "cmodule.h"
+#include "yhconfig.h"
+
+#include <noatun/app.h>
+#include <noatun/pref.h>
+#include <noatun/player.h>
+#include <noatun/stdaction.h>
+
+#include <kaction.h>
+#include <kconfig.h>
+#include <qfile.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kpassivepopup.h>
+#include <kpixmapeffect.h>
+#include <kstdaction.h>
+#include <qbitmap.h>
+#include <qhbox.h>
+#include <qpainter.h>
+#include <qpushbutton.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+
+#include <qimage.h>
+#include <kurl.h>
+#include <kio/netaccess.h>
+#include <kdebug.h>
+#include <kstandarddirs.h>
+
+#include <netwm.h>
+#include <kglobalsettings.h>
+
+// TODO: Maybe make this value configurable?
+const int COVER_MAXW = 128;
+const int COVER_MAXH = 128;
+#define BASEICON "noatun"
+
+// From JuK
+class PassivePopup : public KPassivePopup
+{
+public:
+ PassivePopup(QWidget *parent = 0, const char *name = 0) : KPassivePopup(parent, name) {}
+
+protected:
+ virtual void enterEvent(QEvent *)
+ {
+ setTimeout(3000000); // Make timeout damn near infinite
+ }
+
+ virtual void leaveEvent(QEvent *)
+ {
+ setTimeout(250); // Close quickly
+ }
+};
+
+
+//NoatunSystray *NoatunSystray::self = 0;
+
+
+NoatunSystray::NoatunSystray() : KMainWindow(0, "NoatunSystray"), Plugin(),
+ mTray(0), trayStatus(0), trayBase(0), mPassivePopup(0L)
+{
+ //self = this;
+ hide();
+
+ tmpCoverPath = locateLocal("tmp", "youngHickoryCover.png");
+
+ removeCover(); // make sure any old temp cover is gone
+
+ KStdAction::quit(napp, SLOT(quit()), actionCollection());
+ KStdAction::open(napp, SLOT(fileOpen()), actionCollection());
+ KStdAction::preferences(napp, SLOT(preferences()), actionCollection());
+ NoatunStdAction::back(actionCollection(), "back");
+ NoatunStdAction::stop(actionCollection(), "stop");
+ NoatunStdAction::playpause(actionCollection(), "play");
+ NoatunStdAction::forward(actionCollection(), "forward");
+ NoatunStdAction::playlist(actionCollection(), "show_playlist");
+ NoatunStdAction::loop(actionCollection(), "loop_style");
+ NoatunStdAction::effects(actionCollection(), "effects");
+ NoatunStdAction::equalizer(actionCollection(), "equalizer");
+
+ createGUI("systrayui.rc");
+
+ mTray = new KitSystemTray("tray", this);
+ mTray->show();
+
+ trayBase = renderIcon(BASEICON, QString::null);
+ trayStatus = renderIcon(BASEICON, "player_stop");
+
+ mTray->changeTitle(*trayBase, i18n("Noatun"));
+ showingTrayStatus = false;
+
+ mBlinkTimer = new QTimer(this);
+ connect(mBlinkTimer, SIGNAL(timeout()), this, SLOT(slotBlinkTimer()));
+
+ connect(napp->player(), SIGNAL(playing()), this, SLOT(slotPlayPause()));
+ connect(napp->player(), SIGNAL(paused()), this, SLOT(slotPlayPause()));
+ connect(napp->player(), SIGNAL(stopped()), this, SLOT(slotStopped()));
+ //napp->player()->handleButtons();
+}
+
+
+NoatunSystray::~NoatunSystray()
+{
+ //kdDebug(66666) << k_funcinfo << "Called." << endl;
+ removeCover();
+ delete trayBase;
+ delete trayStatus;
+ napp->showInterfaces();
+}
+
+
+void NoatunSystray::init()
+{
+ YHModule *cmod = new YHModule(this);
+ connect(cmod, SIGNAL(saved()), this, SLOT(slotLoadSettings()));
+ slotLoadSettings();
+}
+
+
+void NoatunSystray::slotLoadSettings()
+{
+ kdDebug(66666) << k_funcinfo << endl;
+
+ YHConfig *c = YHConfig::self();
+
+ if(c->stateIconDisplay() == YHConfig::FlashingIcon)
+ mBlinkTimer->start(1000);
+ else
+ mBlinkTimer->stop();
+ slotBlinkTimer();
+
+
+ if(c->tip())
+ QToolTip::add(mTray, tipText);
+ else
+ QToolTip::remove(mTray);
+
+ if (!c->passivePopupCovers())
+ removeCover();
+
+ if(c->passivePopup())
+ {
+ mPassivePopup = new PassivePopup(mTray, "NoatunPassivePopup");
+ }
+ else
+ {
+ delete mPassivePopup;
+ mPassivePopup = 0L;
+ }
+}
+
+
+void NoatunSystray::closeEvent(QCloseEvent*)
+{
+ //kdDebug(66666) << k_funcinfo << "Called." << endl;
+ disconnect(napp->player(), 0, 0, 0);
+ unload();
+}
+
+
+void NoatunSystray::slotPlayPause()
+{
+ QString status;
+
+ if(napp->player()->isPaused())
+ {
+ changeTray("player_pause");
+ status = i18n("Noatun - Paused");
+ }
+ else
+ {
+ changeTray("player_play");
+ status = i18n("Noatun - Playing");
+ }
+
+ const PlaylistItem item = napp->player()->current();
+ QString s;
+
+ if(!item.isProperty("title"))
+ {
+ // No metadata
+ s = QString("<nobr>%1</nobr>").arg(item.title());
+ }
+ else
+ {
+ s = QString("<h2><nobr>%1</nobr></h2>").arg(item.property("title"));
+
+ if(item.isProperty("author"))
+ s += QString("<nobr>%1</nobr><br>").arg(item.property("author"));
+
+ if(item.isProperty("album"))
+ {
+ if(item.isProperty("date"))
+ s += QString("<nobr>%1 (%2)</nobr><br>").arg(item.property("album")).arg(item.property("date"));
+ else
+ s += QString("<nobr>%1</nobr><br>").arg(item.property("album"));
+ }
+ }
+
+ // prepare cover image for display
+ if (YHConfig::self()->passivePopupCovers())
+ updateCover();
+
+ if(YHConfig::self()->passivePopupCovers() && QFile::exists(tmpCoverPath))
+ {
+ // QT always adds an empty line after the table so we add en empty line before the
+ // table to get equal spacing on top and bottom
+ setTipText(QString("<qt><br><table cellspacing=0 cellpadding=0><tr>" \
+ "<td align=center valign=center><h4><nobr>%1</nobr></h4>%2</td>" \
+ "<td valign=center><img src='%3'></td>" \
+ "</qt></tr></table>").arg(status).arg(s).arg(tmpCoverPath));
+ }
+ else
+ {
+ setTipText(QString("<qt><center><h4><nobr>%1</nobr></h4>%2</center></qt>").arg(status).arg(s));
+ }
+}
+
+
+void NoatunSystray::slotStopped()
+{
+ if(!napp->player()->current())
+ return;
+ changeTray("player_stop");
+ setTipText(QString("<qt><nobr><h4>%1</h4></nobr></qt>").arg(i18n("Noatun - Stopped")));
+}
+
+
+
+void NoatunSystray::changeTray(const QString &pm)
+{
+ delete trayStatus;
+ trayStatus = renderIcon(BASEICON, pm);
+ if(showingTrayStatus)
+ slotBlinkTimer();
+}
+
+
+void NoatunSystray::slotBlinkTimer()
+{
+ switch(YHConfig::self()->stateIconDisplay())
+ {
+ case (YHConfig::FlashingIcon):
+ showingTrayStatus ^= true;
+ break;
+ case (YHConfig::StaticIcon):
+ showingTrayStatus = true;
+ break;
+ case (YHConfig::NoIcon):
+ showingTrayStatus = false;
+ break;
+ }
+
+ if(showingTrayStatus)
+ mTray->setPixmap(*trayStatus);
+ else
+ mTray->setPixmap(*trayBase);
+}
+
+
+// taken from patched karamba xmmssensor
+// modified heavily to work in this place
+void NoatunSystray::updateCover()
+{
+ //kdDebug(66666) << k_funcinfo << endl;
+ QString dir = napp->player()->current().url().directory();
+ QString cover;
+
+ // TODO: Maybe make these filenames configurable?
+ if(QFile::exists(dir + "/folder.png"))
+ cover = dir + "/folder.png";
+ else if(QFile::exists(dir + "/.folder.png"))
+ cover = dir + "/.folder.png";
+ else if(QFile::exists(dir + "/cover.png"))
+ cover = dir + "/cover.png";
+ else if(QFile::exists(dir + "/cover.jpg"))
+ cover = dir + "/cover.jpg";
+ else if(QFile::exists(dir + "/cover.jpeg"))
+ cover = dir + "/cover.jpeg";
+ else // no cover
+ {
+ //kdDebug(66666) << k_funcinfo << "NO COVER" << endl;
+ removeCover();
+ return;
+ }
+
+ QString title = napp->player()->current().title();
+
+ QImage previmg;
+ previmg.load(tmpCoverPath);
+
+ if(previmg.text("Title") != title)
+ { //Verify song change to limit CPU usage
+ /*kdDebug(66666) << k_funcinfo << "Creating new temp cover for '" <<
+ cover << "'" << endl;*/
+
+ QImage src;
+ QImage tmpimg;
+
+ if(src.load(cover))
+ {
+ if(src.width() >= COVER_MAXW || src.height() >= COVER_MAXH)
+ tmpimg = src.scale(COVER_MAXW, COVER_MAXH, QImage::ScaleMin);
+ else
+ tmpimg = src;
+
+ tmpimg.setText("Title", 0, title); //add Title in the image text for cache usage
+ tmpimg.save(tmpCoverPath, "PNG", 0);
+ }
+ else
+ {
+ removeCover();
+ }
+ }
+}
+
+
+void NoatunSystray::removeCover()
+{
+ if(QFile::exists(tmpCoverPath))
+ KIO::NetAccess::del(KURL(tmpCoverPath), this);
+}
+
+
+void NoatunSystray::setTipText(const QString& text)
+{
+ if(text == tipText) // save the planet, save cpu cycles ;)
+ return;
+ tipText = text;
+
+ YHConfig *c = YHConfig::self();
+ if(c->passivePopup())
+ QTimer::singleShot(0, this, SLOT(showPassivePopup()));
+
+ if(c->tip())
+ QToolTip::add(mTray, tipText);
+}
+
+
+void NoatunSystray::showPassivePopup()
+{
+ if (!mPassivePopup)
+ {
+ kdDebug(66666) << k_funcinfo << "Called but no KPassivePopup created yet!" << endl;
+ return;
+ }
+
+ mPassivePopup->reparent(0L, QPoint(0,0));
+
+ if (YHConfig::self()->passivePopupButtons() && !napp->player()->isStopped())
+ {
+ QVBox *widget = mPassivePopup->standardView(QString::null, tipText, QPixmap());
+ QHBox *box = new QHBox(mPassivePopup, "popupbox");
+
+ box->setSpacing(8);
+
+ // Algorithm for determining popup location from kpassivepopup.cpp via JuK
+ NETWinInfo ni(qt_xdisplay(), mTray->winId(), qt_xrootwin(),
+ NET::WMIconGeometry | NET::WMKDESystemTrayWinFor);
+ NETRect frame, win;
+ ni.kdeGeometry(frame, win);
+
+ QRect bounds = KGlobalSettings::desktopGeometry(QPoint(win.pos.x, win.pos.y));
+
+ if(win.pos.x < bounds.center().x())
+ {
+ // Buttons to the left
+
+ QVBox *buttonBox = new QVBox(box);
+ buttonBox->setSpacing(3);
+
+ QPushButton *forwardButton = new QPushButton(action("forward")->iconSet(), 0, buttonBox, "popup_forward");
+ forwardButton->setFlat(true);
+ connect(forwardButton, SIGNAL(clicked()), action("forward"), SLOT(activate()));
+
+ QPushButton *backButton = new QPushButton(action("back")->iconSet(), 0, buttonBox, "popup_back");
+ backButton->setFlat(true);
+ connect(backButton, SIGNAL(clicked()), action("back"), SLOT(activate()));
+
+ QFrame *line = new QFrame(box);
+ line->setFrameShape(QFrame::VLine);
+
+ widget->reparent(box, QPoint(0, 0));
+ }
+ else
+ {
+ // Buttons to the right
+ widget->reparent(box, QPoint(0, 0));
+
+ QFrame *line = new QFrame(box);
+ line->setFrameShape(QFrame::VLine);
+
+ QVBox *buttonBox = new QVBox(box);
+ buttonBox->setSpacing(3);
+
+ QPushButton *forwardButton = new QPushButton(action("forward")->iconSet(), 0, buttonBox, "popup_forward");
+ forwardButton->setFlat(true);
+ connect(forwardButton, SIGNAL(clicked()), action("forward"), SLOT(activate()));
+
+ QPushButton *backButton = new QPushButton(action("back")->iconSet(), 0, buttonBox, "popup_back");
+ backButton->setFlat(true);
+ connect(backButton, SIGNAL(clicked()), action("back"), SLOT(activate()));
+ }
+ mPassivePopup->setView(box);
+ }
+ else
+ {
+ mPassivePopup->setView(QString::null, tipText);
+ }
+
+ mPassivePopup->setTimeout(YHConfig::self()->passivePopupTimeout()*1000);
+ mPassivePopup->show();
+}
+
+
+QPixmap *NoatunSystray::renderIcon(const QString& baseIcon, const QString &overlayIcon) const
+{
+ QPixmap *base = new QPixmap(KSystemTray::loadIcon(baseIcon));
+
+ if(!(overlayIcon.isNull())) // otherwise leave the base as-is
+ {
+ QPixmap overlay = KSystemTray::loadIcon(overlayIcon);
+ if(!overlay.isNull())
+ {
+ // draw the overlay on top of it
+ QPainter p(base);
+ p.drawPixmap(0, 0, overlay);
+ }
+ }
+ return base;
+}
+
+#include "systray.moc"
diff --git a/noatun/modules/systray/systray.h b/noatun/modules/systray/systray.h
new file mode 100644
index 00000000..f602c4e3
--- /dev/null
+++ b/noatun/modules/systray/systray.h
@@ -0,0 +1,80 @@
+// systray.h
+//
+// Copyright (C) 2000 Neil Stevens <multivac@fcmail.com>
+// Copyright (C) 1999 Charles Samuels <charles@kde.org>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifndef SYSTRAY_H
+#define SYSTRAY_H
+
+#include <noatun/plugin.h>
+#include <noatun/app.h>
+
+#include <qtimer.h>
+
+#include <kmainwindow.h>
+
+class KitSystemTray;
+class QTimer;
+class PassivePopup;
+
+class NoatunSystray : public KMainWindow, public Plugin
+{
+Q_OBJECT
+public:
+ NoatunSystray();
+ virtual ~NoatunSystray();
+ virtual void init();
+
+protected:
+ virtual void closeEvent(QCloseEvent *);
+
+public slots:
+ void slotPlayPause();
+ void slotStopped();
+
+private slots:
+ void changeTray(const QString &);
+ void slotLoadSettings();
+ void slotBlinkTimer();
+ void showPassivePopup();
+ QPixmap *renderIcon(const QString &, const QString &) const;
+
+private:
+ void setTipText(const QString&);
+ void updateCover();
+ void removeCover();
+
+private:
+ KitSystemTray *mTray;
+ QTimer *mBlinkTimer;
+ QPixmap *trayStatus;
+ QPixmap *trayBase;
+ PassivePopup *mPassivePopup;
+
+ bool showingTrayStatus;
+ QString tipText;
+ QString tmpCoverPath;
+};
+
+#endif
diff --git a/noatun/modules/systray/systray.plugin b/noatun/modules/systray/systray.plugin
new file mode 100644
index 00000000..24af57f5
--- /dev/null
+++ b/noatun/modules/systray/systray.plugin
@@ -0,0 +1,100 @@
+Filename=noatun_systray.la
+Author=Neil Stevens
+Site=http://noatun.kde.org/plugins/yh/
+Email=neil@qualityassistant.com
+Type=systray
+License=X11-like
+Name=Young Hickory
+Name[af]=Jong Hickory
+Name[ar]=شجرة الجوز الصغيرة
+Name[az]=Gənc Hickory
+Name[cs]=Mladý ořech
+Name[eo]=Juna hikorio
+Name[es]=Joven Nogal
+Name[et]=Noor hikkor
+Name[fa]=یانگ هیکوری
+Name[fi]=Nuori Hickory
+Name[fr]=Jeune Hickory
+Name[hi]=यंग हिकॉरी
+Name[is]=Ungi Hickory
+Name[it]=Giovane nocciolina
+Name[ko]=젊은 히코리
+Name[lt]=Jaunas Hickory
+Name[lv]=Jaunais Hickorijs
+Name[ne]=युवा हिक्कोरी
+Name[pl]=Tacka systemowa
+Name[ru]=Ветка гикори
+Name[sk]=Mladý Hickory
+Name[sl]=Mladi hikori
+Name[sv]=Ung valnöt
+Name[ta]=சின்ன ஒருவகை மரம்
+Name[tr]=Genç Hickory
+Name[ven]=Hickory mutuku
+Name[xh]=iHickory encinane
+Name[zh_CN]=小胡桃木
+Name[zh_HK]=小山胡桃
+Name[zh_TW]=小山胡桃
+Comment=A system tray interface
+Comment[af]='n stelsel laai koppelvlak
+Comment[ar]=واجهة للوحة النظام
+Comment[az]=Sistem rəf paneli
+Comment[bg]=Икона за управление от системния панел
+Comment[bn]=একটি সিস্টেম ট্রে ইন্টারফেস
+Comment[bs]=Interfejs za sistemski tray
+Comment[ca]=Una interfície de la safata del sistema
+Comment[cs]=Rozhraní systémové části panelu
+Comment[cy]=Rhyngwyneb hambwrdd cysawd
+Comment[da]=En statusfelt-grænseflade
+Comment[de]=Oberfläche für den Systembereich der Kontrollleiste
+Comment[el]=Μια διασύνδεση για το πλαίσιο συστήματος
+Comment[eo]=Interfaco por la dokejo
+Comment[es]=Una interfaz para la bandeja del sistema
+Comment[et]=Süsteemse doki liides
+Comment[eu]=Interfazea sistemaren bandejarentzat
+Comment[fa]=یک واسط سینی سیستم
+Comment[fi]=Järjestelmäikkunan rajapinta
+Comment[fr]=Une interface intégrée dans la boîte à miniatures
+Comment[gl]=Unha interface para a bandexa do sistema
+Comment[he]=ממשק מגש מערכת
+Comment[hi]=एक तंत्र तश्तरी इंटरफेस
+Comment[hr]=Sučelje za sustavsku ladicu
+Comment[hu]=A rendszertálca kezelői felülete
+Comment[id]=interface tray sistem
+Comment[is]=Aðgangur að kerfisbakka
+Comment[it]=Un'interfaccia per il vassoio di sistema
+Comment[ja]=システムトレイインターフェース
+Comment[kk]=Жүйелік сөре интерфейсі
+Comment[km]=ចំណុច​ប្រទាក់​ថាស​ប្រព័ន្ធ
+Comment[ko]=시스템 트레이 인터페이스
+Comment[lt]=Sistemos dėklo sąsaja
+Comment[lv]=Sistēmas teknes starpseja
+Comment[mk]=Интерфејс за системската лента
+Comment[ms]=Antaramuka dulang sistem
+Comment[mt]=Interfaċċja għat-tray tas-sistema
+Comment[nb]=Systemkurv grensesnitt
+Comment[nds]=Systeemafsnitt-Koppelsteed
+Comment[ne]=प्रणाली ट्रे इन्टरफेस
+Comment[nl]=Een systeemvakinterface
+Comment[nn]=Systemtrau-grensesnitt
+Comment[pa]=ਇੱਕ ਸਿਸਟਮ ਟਰੇ ਇੰਟਰਫੇਸ
+Comment[pl]=Interfejs tacki systemowej
+Comment[pt]=Uma interface para a bandeja do sistema
+Comment[pt_BR]=Uma interface para os ícones de sistema
+Comment[ro]=O interfaţă pentru tava de sistem
+Comment[ru]=Интерфейс панели системного лотка
+Comment[sk]=Rozhranie pre systémovú lištu
+Comment[sl]=Vmesnik za sistemsko vrstico v pultu
+Comment[sr]=Интерфејс за системску касету
+Comment[sr@Latn]=Interfejs za sistemsku kasetu
+Comment[sv]=Gränssnitt för aktivitetsfältet
+Comment[ta]=அமைப்பு தொகுதித் தட்டு இடைமுகம்
+Comment[tg]=Интерфейси сабади системавӣ
+Comment[th]=ส่วนติดต่อกับถาดระบบ
+Comment[tr]=Sistem çekmecesi arayüzü
+Comment[uk]=Інтерфейс системного лотка
+Comment[ven]=Thirei ya sisitemu
+Comment[xh]=Ujongano lwendlela yetreyi
+Comment[zh_CN]=一个系统托盘界面
+Comment[zh_HK]=系統匣介面
+Comment[zh_TW]=System Tray的介面
+Comment[zu]=Isistimu yetreyi loxhumano olubhekene
diff --git a/noatun/modules/systray/systrayui.rc b/noatun/modules/systray/systrayui.rc
new file mode 100644
index 00000000..2b50654b
--- /dev/null
+++ b/noatun/modules/systray/systrayui.rc
@@ -0,0 +1,20 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="noatunsystray" version="12">
+<Menu name="tray">
+ <Action name="file_open"/>
+ <Action name="show_playlist"/>
+ <Separator lineSeparator="true"/>
+ <Action name="back"/>
+ <Action name="stop"/>
+ <Action name="play"/>
+ <Action name="forward"/>
+ <Separator lineSeparator="true"/>
+ <Action name="loop_style"/>
+ <Action name="effects"/>
+ <Action name="equalizer"/>
+ <Separator lineSeparator="true"/>
+ <Action name="options_configure"/>
+ <Separator lineSeparator="true"/>
+ <Action name="file_quit"/>
+</Menu>
+</kpartgui>
diff --git a/noatun/modules/systray/yhconfig.kcfg b/noatun/modules/systray/yhconfig.kcfg
new file mode 100644
index 00000000..9b017aec
--- /dev/null
+++ b/noatun/modules/systray/yhconfig.kcfg
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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="noatunrc"/>
+ <group name="Young Hickory">
+
+ <entry key="State Icon Display" type="Enum">
+ <label>State Icon Display</label>
+ <choices>
+ <choice name="Animation"/>
+ <choice name="FlashingIcon"/>
+ <choice name="StaticIcon"/>
+ <choice name="NoIcon"/>
+ </choices>
+ <default>FlashingIcon</default>
+ </entry>
+
+ <entry name="icon" type="Int">
+ <label>Icon</label>
+ <default>1</default>
+ </entry>
+
+ <entry name="tip" type="Bool">
+ <label>Show a tooltip for the current track</label>
+ <default>true</default>
+ </entry>
+
+ <entry name="passivePopup" type="Bool">
+ <label>Announce tracks with a popup window</label>
+ <default>true</default>
+ </entry>
+
+ <entry name="passivePopupTimeout" type="Int">
+ <label>Display popup window for x seconds</label>
+ <default>5</default>
+ </entry>
+
+ <entry name="passivePopupCovers" type="Bool">
+ <label>Show covers in popup window and tooltip</label>
+ <default>true</default>
+ </entry>
+
+ <entry name="passivePopupButtons" type="Bool">
+ <label>Show buttons in popup window</label>
+ <default>true</default>
+ </entry>
+
+
+ <entry key="MiddleMouse Action" type="Enum">
+ <label>Mode</label>
+ <choices>
+ <choice name="PlayPause"/>
+ <choice name="HideShowPlaylist"/>
+ </choices>
+ <default>HideShowPlaylist</default>
+ </entry>
+
+ <entry name="MouseWheelAction$(Modifier)" type="Enum" key="mousewheelaction_$(Modifier)">
+ <parameter name="Modifier" type="Enum">
+ <values>
+ <value>None</value>
+ <value>Shift</value>
+ <value>Alt</value>
+ <value>Ctrl</value>
+ </values>
+ </parameter>
+ <choices>
+ <choice name="Nothing"/>
+ <choice name="ChangeVolume"/>
+ <choice name="ChangeTrack"/>
+ </choices>
+ <default param="None">ChangeVolume</default>
+ <default param="Shift">Nothing</default>
+ <default param="Alt">Nothing</default>
+ <default param="Ctrl">ChangeTrack</default>
+ </entry>
+
+ </group>
+</kcfg>
diff --git a/noatun/modules/systray/yhconfig.kcfgc b/noatun/modules/systray/yhconfig.kcfgc
new file mode 100644
index 00000000..5b4f99e4
--- /dev/null
+++ b/noatun/modules/systray/yhconfig.kcfgc
@@ -0,0 +1,7 @@
+# Code generation options for kconfig_compiler
+File=yhconfig.kcfg
+ClassName=YHConfig
+Singleton=true
+Mutators=true
+MemberVariables=private
+GlobalEnums=true
diff --git a/noatun/modules/systray/yhconfigwidget.ui b/noatun/modules/systray/yhconfigwidget.ui
new file mode 100644
index 00000000..48267c28
--- /dev/null
+++ b/noatun/modules/systray/yhconfigwidget.ui
@@ -0,0 +1,333 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>YHConfigWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>YHConfigWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>371</width>
+ <height>379</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>tabWidget2</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;General</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>chkUseTooltip</cstring>
+ </property>
+ <property name="text">
+ <string>Show a &amp;tooltip for the current track</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>chkUseCovers</cstring>
+ </property>
+ <property name="text">
+ <string>Show &amp;covers in popup window and tooltip</string>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Popup Window</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>lblPopupTimeout</cstring>
+ </property>
+ <property name="text">
+ <string>Display popup window t&amp;ime:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>spinPopupTimeout</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>chkPopupButtons</cstring>
+ </property>
+ <property name="text">
+ <string>Show &amp;buttons in popup window</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="1" column="1">
+ <property name="name">
+ <cstring>spinPopupTimeout</cstring>
+ </property>
+ <property name="suffix">
+ <string>s</string>
+ <comment>Seconds</comment>
+ </property>
+ <property name="maxValue">
+ <number>600</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>5</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>chkUsePopup</cstring>
+ </property>
+ <property name="text">
+ <string>Announce tracks with a &amp;popup window</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="title">
+ <string>State Icon Display</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton" row="0" column="0">
+ <property name="name">
+ <cstring>rbStateAnim</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Animated</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="0" column="1">
+ <property name="name">
+ <cstring>rbStateFlashing</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Flashing</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="1" column="0">
+ <property name="name">
+ <cstring>rbStateStatic</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Static</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="1" column="1">
+ <property name="name">
+ <cstring>rbStateNone</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;None</string>
+ </property>
+ </widget>
+ </grid>
+ </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>81</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Ad&amp;vanced</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>grpMiddleMouse</cstring>
+ </property>
+ <property name="title">
+ <string>Middle Mouse Button Action</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>rbHideShowPlaylist</cstring>
+ </property>
+ <property name="text">
+ <string>Hide / Show play&amp;list</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>rbPlayPause</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Play / Pause</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>grpMwheel</cstring>
+ </property>
+ <property name="title">
+ <string>Mouse &amp;Wheel</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblModifier</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Keyboard modifier:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>cmbModifier</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>cmbModifier</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblAction</cstring>
+ </property>
+ <property name="text">
+ <string>Action:</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>rbActNothing</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Nothing</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>rbActVolume</cstring>
+ </property>
+ <property name="text">
+ <string>Change v&amp;olume</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>rbActTrack</cstring>
+ </property>
+ <property name="text">
+ <string>Switch &amp;track</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </widget>
+ </vbox>
+</widget>
+<tabstops>
+ <tabstop>tabWidget2</tabstop>
+ <tabstop>chkUseTooltip</tabstop>
+ <tabstop>chkUseCovers</tabstop>
+ <tabstop>chkUsePopup</tabstop>
+ <tabstop>spinPopupTimeout</tabstop>
+ <tabstop>chkPopupButtons</tabstop>
+ <tabstop>rbStateAnim</tabstop>
+ <tabstop>rbStateFlashing</tabstop>
+ <tabstop>rbStateStatic</tabstop>
+ <tabstop>rbStateNone</tabstop>
+ <tabstop>rbHideShowPlaylist</tabstop>
+ <tabstop>rbPlayPause</tabstop>
+ <tabstop>cmbModifier</tabstop>
+ <tabstop>rbActNothing</tabstop>
+ <tabstop>rbActVolume</tabstop>
+ <tabstop>rbActTrack</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/noatun/modules/voiceprint/Makefile.am b/noatun/modules/voiceprint/Makefile.am
new file mode 100644
index 00000000..22c3f65c
--- /dev/null
+++ b/noatun/modules/voiceprint/Makefile.am
@@ -0,0 +1,14 @@
+INCLUDES= -I$(top_srcdir)/noatun/library $(all_includes)
+kde_module_LTLIBRARIES = noatun_voiceprint.la
+
+noatun_voiceprint_la_SOURCES = voiceprint.cpp prefs.cpp
+
+noatun_voiceprint_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatun_voiceprint_la_LIBADD = $(LIB_KFILE) $(top_builddir)/noatun/library/libnoatun.la -lm
+
+noatun_voiceprint_la_METASOURCES = AUTO
+
+noinst_HEADERS = voiceprint.h prefs.h
+
+noatun_modules_voiceprint_DATA = voiceprint.plugin
+noatun_modules_voiceprintdir = $(kde_datadir)/noatun
diff --git a/noatun/modules/voiceprint/prefs.cpp b/noatun/modules/voiceprint/prefs.cpp
new file mode 100644
index 00000000..48998680
--- /dev/null
+++ b/noatun/modules/voiceprint/prefs.cpp
@@ -0,0 +1,67 @@
+#include "prefs.h"
+#include "voiceprint.h"
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <kcolorbutton.h>
+#include <kconfig.h>
+
+Prefs::Prefs(QObject* parent)
+ : CModule(i18n("Voiceprint"), i18n("Options for the Voiceprint Visualization"), "xapp", parent)
+{
+ QVBoxLayout *king=new QVBoxLayout(this);
+ QHBoxLayout *minor;
+
+ QLabel *label;
+ mForeground=new KColorButton(this);
+ label=new QLabel(mForeground, i18n("&Foreground color:"), this);
+ minor=new QHBoxLayout(king);
+ minor->addWidget(label);
+ minor->addWidget(mForeground);
+
+ mBackground=new KColorButton(this);
+ label=new QLabel(mBackground, i18n("&Background color:"), this);
+ minor=new QHBoxLayout(king);
+ minor->addWidget(label);
+ minor->addWidget(mBackground);
+
+ mLine=new KColorButton(this);
+ label=new QLabel(mForeground, i18n("&Sweep color:"), this);
+ minor=new QHBoxLayout(king);
+ minor->addWidget(label);
+ minor->addWidget(mLine);
+
+ king->addStretch();
+}
+
+void Prefs::reopen()
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup("VoicePrint");
+ QColor black(0, 0, 0);
+ QColor blue(0, 0, 222);
+ mBackground->setColor(config->readColorEntry("Background", &black));
+ mForeground->setColor(config->readColorEntry("Foreground", &blue));
+ mLine->setColor(config->readColorEntry("Line", &black));
+}
+
+void Prefs::save()
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup("VoicePrint");
+ config->writeEntry("Background", mBackground->color());
+ config->writeEntry("Foreground", mForeground->color());
+ config->writeEntry("Line", mLine->color());
+
+ config->sync();
+
+ VoicePrint *l=VoicePrint::voicePrint;
+ if (l)
+ l->setColors(mBackground->color(), mForeground->color(), mLine->color());
+
+}
+
+#include "prefs.moc"
+
diff --git a/noatun/modules/voiceprint/prefs.h b/noatun/modules/voiceprint/prefs.h
new file mode 100644
index 00000000..6541b4de
--- /dev/null
+++ b/noatun/modules/voiceprint/prefs.h
@@ -0,0 +1,22 @@
+#ifndef PREFS_H
+#define PREFS_H
+
+#include <qwidget.h>
+#include <noatun/pref.h>
+
+class KColorButton;
+
+class Prefs : public CModule
+{
+Q_OBJECT
+public:
+ Prefs(QObject* parent);
+ virtual void save();
+ virtual void reopen();
+
+private:
+ KColorButton *mForeground, *mBackground, *mLine;
+};
+
+#endif
+
diff --git a/noatun/modules/voiceprint/voiceprint.cpp b/noatun/modules/voiceprint/voiceprint.cpp
new file mode 100644
index 00000000..4f6c7aea
--- /dev/null
+++ b/noatun/modules/voiceprint/voiceprint.cpp
@@ -0,0 +1,126 @@
+#include "voiceprint.h"
+#include <noatun/player.h>
+#include <noatun/app.h>
+#include <math.h>
+#include <qpainter.h>
+#include "prefs.h"
+#include <klocale.h>
+#include <stdio.h>
+
+extern "C"
+{
+ KDE_EXPORT Plugin *create_plugin()
+ {
+ return new VoicePrint();
+ }
+}
+
+VoicePrint *VoicePrint::voicePrint=0;
+
+VoicePrint::VoicePrint() : QWidget(0,0,WRepaintNoErase), MonoFFTScope(50), Plugin()
+{
+ voicePrint=this;
+ mOffset=0;
+ mSegmentWidth=2;
+ setCaption(i18n("Voiceprint"));
+ resize(320, 240);
+ MonoFFTScope::start();
+ show();
+ setMaximumHeight(1024);
+}
+
+VoicePrint::~VoicePrint()
+{
+}
+
+void VoicePrint::init()
+{
+ Prefs *p=new Prefs(this);
+ p->reopen();
+ p->save();
+ resizeEvent(0);
+}
+
+void VoicePrint::setColors(const QColor &bg, const QColor &fg, const QColor &l)
+{
+ mProgress=l;
+ mLowColor=bg.rgb();
+ mHighColor=fg.rgb();
+ setBackgroundColor(mLowColor);
+}
+
+void VoicePrint::closeEvent(QCloseEvent *)
+{
+ unload();
+}
+
+void VoicePrint::resizeEvent(QResizeEvent *)
+{
+ mOffset=0;
+ mBuffer.resize(size());
+ QPainter paint(&mBuffer);
+ paint.fillRect(QRect(0,0, QWidget::width(), height()), QColor(mLowColor));
+ setBands(magic(height()/mSegmentWidth));
+}
+
+#define COLOR(color, bgcolor, fgcolor, foctet) \
+ (int)( color(bgcolor) + (foctet) * (color(fgcolor) - color(bgcolor)) )
+
+inline static QRgb averageByIntensity(QRgb bgcolor, QRgb fgcolor, int octet)
+{
+ float foctet = octet / 255.0;
+
+ return qRgb(COLOR(qRed, bgcolor, fgcolor, foctet),
+ COLOR(qGreen, bgcolor, fgcolor, foctet),
+ COLOR(qBlue, bgcolor, fgcolor, foctet)
+ );
+}
+
+#undef COLOR
+
+void VoicePrint::paintEvent(QPaintEvent *e)
+{
+ bitBlt(this, e->rect().topLeft(), &mBuffer, e->rect(), Qt::CopyROP);
+}
+
+void VoicePrint::scopeEvent(float *data, int bands)
+{
+ // save cpu
+ if(isHidden()) return;
+
+ QPainter paint(&mBuffer);
+ // each square has a width of mSegmentWidth
+ float brightness = float(bands * mSegmentWidth);
+ for (int i=0; i<bands ; i++)
+ {
+ float b=data[bands-i-1]+1.0;
+ // the more bands there are, the dimmer each becomes
+ b=log10(b)/log(2) * 16 * brightness;
+ int band=int(b);
+ if (band>255) band=255;
+ else if (band<0) band=0;
+
+ QColor area(averageByIntensity(mLowColor, mHighColor, band));
+
+ int bandTop=i*height()/bands, bandBottom=(i+1)*height()/bands;
+ paint.fillRect(mOffset, bandTop, mSegmentWidth,bandBottom-bandTop,area);
+ }
+
+ int newOffset = mOffset+mSegmentWidth;
+ if (newOffset>QWidget::width()) newOffset=0;
+ paint.fillRect(newOffset, 0, mSegmentWidth, height(), mProgress);
+
+ // redraw changes with the minimum amount of work
+ if(newOffset != 0)
+ {
+ repaint(mOffset,0,mSegmentWidth*2,height(),false);
+ }
+ else
+ {
+ repaint(mOffset,0,mSegmentWidth,height(),false);
+ repaint(newOffset,0,mSegmentWidth,height(),false);
+ }
+ mOffset = newOffset;
+}
+
+#include "voiceprint.moc"
diff --git a/noatun/modules/voiceprint/voiceprint.h b/noatun/modules/voiceprint/voiceprint.h
new file mode 100644
index 00000000..ab5af69d
--- /dev/null
+++ b/noatun/modules/voiceprint/voiceprint.h
@@ -0,0 +1,33 @@
+#ifndef VOICEPRINT_H
+#define VOICEPRINT_H
+
+#include <noatun/plugin.h>
+
+class VoicePrint : public QWidget, public MonoFFTScope, public Plugin
+{
+Q_OBJECT
+
+public:
+ VoicePrint();
+ virtual ~VoicePrint();
+
+ void setColors(const QColor &bg, const QColor &fg, const QColor &l);
+ void init();
+
+protected:
+ virtual void closeEvent(QCloseEvent *);
+ virtual void scopeEvent(float *data, int bands);
+ virtual void resizeEvent(QResizeEvent *);
+ virtual void paintEvent(QPaintEvent *);
+
+public:
+ static VoicePrint* voicePrint;
+
+private:
+ QColor mProgress;
+ QPixmap mBuffer;
+ QRgb mLowColor, mHighColor;
+ int mOffset, mSegmentWidth;
+};
+#endif
+
diff --git a/noatun/modules/voiceprint/voiceprint.plugin b/noatun/modules/voiceprint/voiceprint.plugin
new file mode 100644
index 00000000..b7bca4f4
--- /dev/null
+++ b/noatun/modules/voiceprint/voiceprint.plugin
@@ -0,0 +1,92 @@
+Filename=noatun_voiceprint.la
+Author=Charles Samuels
+Site=http://noatun.derkarl.org/
+Email=charles@kde.org
+Type=visualization
+License=Artistic
+Name=Voiceprint
+Name[af]=Stem afdruk
+Name[az]=Səs gözləyici
+Name[ca]=Empremta de veu
+Name[cy]=Argraff Lais
+Name[el]=Φωνητικό αποτύπωμα
+Name[eo]=Voĉvidigilo
+Name[es]=Huella de voz
+Name[et]=Visuaalne signaal
+Name[fi]=Äänikuva
+Name[ga]=Guthlorg
+Name[hi]=वाइसप्रिंट
+Name[hr]=Otisak glasa
+Name[hu]=Hanglenyomat
+Name[is]=Raddrit
+Name[it]=Impronta vocale
+Name[ja]=ボイスプリント
+Name[km]=បង្ហាញ​សំឡេង
+Name[ko]=성문
+Name[lt]=Balso antspaudas
+Name[lv]=Balssdruka
+Name[mt]=Stampavuċi
+Name[ne]=आवाज मुद्रण
+Name[nl]=Stemafdruk
+Name[pl]=Zapis głosu
+Name[pt]=Impressão Vocal
+Name[ro]=Amprentă vocală
+Name[sl]=Galsovni zapis
+Name[sv]=Röstavtryck
+Name[ta]=குரல் அச்சு
+Name[tr]=Ses Gözlemcisi
+Name[ven]=U phirintha ha Mukosi
+Name[xh]=Ushicilelo lelizwi
+Name[zh_CN]=声波纹
+Name[zh_HK]=聲波紋
+Name[zh_TW]=聲波紋
+Name[zu]=Ushicilelo lelizwi
+Comment=A voiceprint visualizer
+Comment[bg]=Визуализатор Voiceprint
+Comment[bs]=Vizualizacija sa ispisom glasa
+Comment[ca]=Un visualitzador d'empremta de veu
+Comment[cs]=Vizualizátor hlasu
+Comment[cy]=Gweledydd argraff lais
+Comment[da]=En voiceprint-visualisering
+Comment[de]=Klangvisualisierung
+Comment[el]=Μια αναπαράσταση αποτυπώματος φωνής
+Comment[en_GB]=A voiceprint visualiser
+Comment[eo]=Voĉo-vidigilo
+Comment[es]=Un visualizador de huellas de voz
+Comment[et]=Signaali visualiseerimise plugin
+Comment[eu]=Voiceprint ikustailea
+Comment[fa]=یک تجسم‌کننده voiceprint
+Comment[fi]=Äänikuvavisualisoija
+Comment[fr]=Un afficheur Voiceprint
+Comment[gl]=Visualizador voiceprint
+Comment[he]=ממחיש טביעות קול
+Comment[hu]=Hanglenyomat-analizáló
+Comment[is]=Raddritsskjár
+Comment[it]=Visualizzatore di impronta vocale
+Comment[ja]=声紋の視覚効果
+Comment[kk]=Дыбыс таңба бейнекөрінісі
+Comment[km]=ម៉ាកែត​សម្រាប់​បង្ហាញ​សំឡេង
+Comment[ko]=성문 비주얼라이저
+Comment[lt]=Balso antspaudo vaizdavimo priemonė
+Comment[nb]=Voiceprint-fremvisar
+Comment[nds]=Stimmafdruck-Filmmaker
+Comment[ne]=आवाज मुद्रण भिजुलाइजर
+Comment[nl]=Stemafdruk visualisatie
+Comment[nn]=Voiceprint-framvisar
+Comment[pl]=Wizualizacja śladu głosu
+Comment[pt]=Um visualizador do comportamento vocal
+Comment[pt_BR]=Um visualizador voiceprint
+Comment[ro]=Vizualizor de amprentă vocală
+Comment[ru]=визуализация voiceprint
+Comment[sk]=Vizualizátor voiceprint
+Comment[sl]=Predstavitelj glasovnega zapisa
+Comment[sr]=Визуализатор гласовног отиска
+Comment[sr@Latn]=Vizualizator glasovnog otiska
+Comment[sv]=En röstavtrycksvisare
+Comment[ta]=குரல் அச்சு படக்காட்சி
+Comment[th]=โปรแกรมสร้างวิฌวลไลเซอร์ voiceprint
+Comment[tr]=Ses Dökümü İzleme Programı
+Comment[uk]=Візуаліатор voiseprint
+Comment[zh_CN]=声波纹视觉效果
+Comment[zh_HK]=聲波紋顯示程式
+Comment[zh_TW]=聲波紋顯示程式
diff --git a/noatun/modules/winskin/Makefile.am b/noatun/modules/winskin/Makefile.am
new file mode 100644
index 00000000..5c0423e6
--- /dev/null
+++ b/noatun/modules/winskin/Makefile.am
@@ -0,0 +1,51 @@
+SUBDIRS = vis skins mimetypes
+
+noatun_modules_winskin_DATA = winskin.plugin
+noatun_modules_winskindir = $(kde_datadir)/noatun
+
+INCLUDES = -I$(top_srcdir)/noatun/library \
+ -I$(top_builddir)/noatun/library \
+ -I$(kde_includes)/arts \
+ $(all_includes)
+
+kde_module_LTLIBRARIES = noatun_winskin.la
+
+noatun_winskin_la_SOURCES = fileInfo.cpp \
+ guiSpectrumAnalyser.cpp \
+ plugin.cpp \
+ waBalanceSlider.cpp \
+ waButton.cpp \
+ waClutterbar.cpp \
+ waColor.cpp \
+ waDigit.cpp \
+ waInfo.cpp \
+ waIndicator.cpp \
+ waJumpSlider.cpp \
+ waLabel.cpp \
+ waMain.cpp \
+ waRegion.cpp \
+ waSkin.cpp \
+ waSkinModel.cpp \
+ waSlider.cpp \
+ waStatus.cpp \
+ waTitleBar.cpp \
+ waVolumeSlider.cpp \
+ waWidget.cpp \
+ winSkinConfig.cpp \
+ winSkinVis.cpp \
+ waSkinManager.cpp \
+ waSkinManager.skel
+
+noatun_winskin_la_LDFLAGS = $(all_libraries) \
+ -module -avoid-version -no-undefined
+
+noatun_winskin_la_LIBADD = $(top_builddir)/noatun/library/libnoatun.la \
+ $(top_builddir)/noatun/modules/winskin/vis/libwinskinvis.la
+
+noatun_winskin_la_METASOURCES = AUTO
+
+
+
+waSkin.lo: ../../library/noatunarts/noatunarts.h vis/winskinvis.h
+winSkinVis.lo: ../../library/noatunarts/noatunarts.h vis/winskinvis.h
+guiSpectrumAnalyser.lo: ../../library/noatunarts/noatunarts.h vis/winskinvis.h
diff --git a/noatun/modules/winskin/fileInfo.cpp b/noatun/modules/winskin/fileInfo.cpp
new file mode 100644
index 00000000..69f93215
--- /dev/null
+++ b/noatun/modules/winskin/fileInfo.cpp
@@ -0,0 +1,50 @@
+#include <noatun/app.h>
+#include <noatun/playlist.h>
+
+#include <qstring.h>
+#include <kfilemetainfo.h>
+
+#include "fileInfo.h"
+
+fileInfo::fileInfo(const PlaylistItem &item)
+{
+ QString prop;
+
+ prop = item.property("bitrate");
+ if (prop.isNull())
+ _bps = 0;
+ else
+ _bps = prop.toInt();
+
+ prop = item.property("samplerate");
+ if (prop.isNull())
+ _KHz = 44100;
+ else
+ _KHz = prop.toInt();
+
+ prop = item.property("channels");
+ if (prop.isNull())
+ _channelCount = 2;
+ else
+ _channelCount = prop.toInt();
+}
+
+fileInfo::~fileInfo()
+{
+}
+
+unsigned int fileInfo::bps()
+{
+ return _bps;
+}
+
+unsigned int fileInfo::KHz()
+{
+ return _KHz;
+}
+
+unsigned int fileInfo::channelCount()
+{
+ return _channelCount;
+}
+
diff --git a/noatun/modules/winskin/fileInfo.h b/noatun/modules/winskin/fileInfo.h
new file mode 100644
index 00000000..203af087
--- /dev/null
+++ b/noatun/modules/winskin/fileInfo.h
@@ -0,0 +1,21 @@
+#ifndef _FILEINFO_H
+#define _FILEINFO_H
+
+#include <noatun/playlist.h>
+
+class fileInfo {
+ public:
+ fileInfo(const PlaylistItem &);
+ ~fileInfo();
+
+ unsigned int bps();
+ unsigned int KHz();
+ unsigned int channelCount();
+
+ private:
+ int _KHz;
+ int _bps;
+ int _channelCount;
+};
+
+#endif
diff --git a/noatun/modules/winskin/guiSpectrumAnalyser.cpp b/noatun/modules/winskin/guiSpectrumAnalyser.cpp
new file mode 100644
index 00000000..d015e5da
--- /dev/null
+++ b/noatun/modules/winskin/guiSpectrumAnalyser.cpp
@@ -0,0 +1,224 @@
+/*
+ winamp visualisation plugin.
+ Copyright (C) 2001 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include <klocale.h>
+#include <qcolor.h>
+#include <qpopupmenu.h>
+#include <qpainter.h>
+#include <kconfig.h>
+
+#include "waColor.h"
+#include "waSkinModel.h"
+
+#include "guiSpectrumAnalyser.h"
+#include "vis/winskinvis.h"
+
+#define __BANDS 75
+
+GuiSpectrumAnalyser::GuiSpectrumAnalyser()
+ : WaWidget(_WA_MAPPING_ANALYSER)
+{
+ connect(WaSkinModel::instance(), SIGNAL(skinChanged()), this, SLOT(pixmapChange()));
+
+ contextMenu = new QPopupMenu(this);
+ visualizationMenu = new QPopupMenu();
+ analyserMenu = new QPopupMenu();
+
+ contextMenu->insertItem(i18n("Visualization Mode"), visualizationMenu);
+ contextMenu->insertItem(i18n("Analyzer Mode"), analyserMenu);
+
+ visualizationMenu->insertItem(i18n("Analyzer"), (int)MODE_ANALYSER);
+ visualizationMenu->insertItem(i18n("Disabled"), (int)MODE_DISABLED);
+ visualizationMenu->setCheckable(true);
+ connect(visualizationMenu, SIGNAL(activated(int)), this, SLOT(setVisualizationMode(int)));
+
+ analyserMenu->insertItem(i18n("Normal"), (int)MODE_NORMAL);
+ analyserMenu->insertItem(i18n("Fire"), (int)MODE_FIRE);
+ analyserMenu->insertItem(i18n("Vertical Lines"), (int)MODE_VERTICAL_LINES);
+ analyserMenu->setCheckable(true);
+ connect(analyserMenu, SIGNAL(activated(int)), this, SLOT(setAnalyserMode(int)));
+
+ analyserCache = NULL;
+ winSkinVis = NULL;
+
+ KConfig *config = KGlobal::config();
+ config->setGroup("Winskin");
+
+ setVisualizationMode(config->readNumEntry("visualizationMode", MODE_ANALYSER));
+ setAnalyserMode(config->readNumEntry("analyserMode", MODE_NORMAL));
+}
+
+
+GuiSpectrumAnalyser::~GuiSpectrumAnalyser()
+{
+ KConfig *config = KGlobal::config();
+ config->setGroup("Winskin");
+
+ config->writeEntry("visualizationMode", visualization_mode);
+ config->writeEntry("analyserMode", analyser_mode);
+
+ delete analyserCache;
+}
+
+void GuiSpectrumAnalyser::mousePressEvent ( QMouseEvent *e )
+{
+ if (e->button() == LeftButton) {
+ if (visualization_mode == MODE_DISABLED)
+ setVisualizationMode(MODE_ANALYSER);
+ else
+ setVisualizationMode(MODE_DISABLED);
+ }
+ else if (e->button() == RightButton) {
+ contextMenu->popup(mapToGlobal(QPoint(e->x(), e->y())));
+ }
+}
+
+void GuiSpectrumAnalyser::setAnalyserMode(int mode)
+{
+ analyser_mode = mode;
+
+ analyserMenu->setItemChecked(MODE_NORMAL, (mode == MODE_NORMAL));
+ analyserMenu->setItemChecked(MODE_FIRE, (mode == MODE_FIRE));
+ analyserMenu->setItemChecked(MODE_VERTICAL_LINES, (mode == MODE_VERTICAL_LINES));
+
+ delete analyserCache;
+ analyserCache = NULL;
+}
+
+void GuiSpectrumAnalyser::pauseVisualization()
+{
+ hide();
+}
+
+void GuiSpectrumAnalyser::resumeVisualization()
+{
+ show();
+}
+
+void GuiSpectrumAnalyser::updatePeaks()
+{
+ if ((visualization_mode == MODE_DISABLED) || (!isVisible()))
+ return;
+
+ float* currentPeaks = winSkinVis->currentPeaks();
+
+ if (!analyserCache)
+ freshenAnalyserCache();
+
+ for (int x = 0;x < __BANDS;x++) {
+ int amp = int(currentPeaks[x]);
+
+ if (amp < 0)
+ amp = 0;
+ else if (amp > 16)
+ amp = 16;
+
+ bitBlt(this, x, 0, analyserCache, (amp * 2) + (x % 2), 0, 1, 16);
+ }
+}
+
+void GuiSpectrumAnalyser::setVisualizationMode(int mode)
+{
+ visualization_mode = mode;
+
+ visualizationMenu->setItemChecked(MODE_ANALYSER, (mode == MODE_ANALYSER));
+ visualizationMenu->setItemChecked(MODE_DISABLED, (mode == MODE_DISABLED));
+
+ if (mode == MODE_ANALYSER)
+ {
+ if (!winSkinVis)
+ {
+ winSkinVis=new WinSkinVis(this,"WinSkinVis");
+ connect(winSkinVis,SIGNAL(doRepaint()),this,SLOT(updatePeaks()));
+ }
+ }
+ else
+ {
+ delete winSkinVis;
+ winSkinVis = NULL;
+ }
+
+ update();
+}
+
+
+void GuiSpectrumAnalyser::freshenAnalyserCache()
+{
+ // We need a color scheme
+ if (!colorScheme)
+ return;
+
+ // The analyser cache is a 34x16 pixmap containing all the bits needed
+ // to quickly draw the spectrum analyser
+ analyserCache = new QPixmap(34, 16);
+ QPainter p(analyserCache);
+
+ for (unsigned int x = 0;x < 17;x++) {
+ if (x != 16) {
+ p.setPen(QPen(colorScheme->skinColors[INDEX_BACKGROUND_COLOR]));
+ p.drawLine(x * 2, 0, x * 2, 16 - x - 1);
+ }
+
+ for (unsigned int y = 0; y < (16 - x);y++) {
+ if (y % 2)
+ p.setPen(QPen(colorScheme->skinColors[INDEX_GRID_COLOR]));
+ else
+ p.setPen(QPen(colorScheme->skinColors[INDEX_BACKGROUND_COLOR]));
+
+ p.drawPoint((x * 2) + 1, y);
+ }
+
+ if (!x)
+ continue;
+
+ switch (analyser_mode) {
+ case MODE_FIRE:
+ for (unsigned int y = (16 - x); y < 16; y++) {
+ p.setPen(QPen(colorScheme->skinColors[INDEX_SPEC_BASE + (y - (16 - x))]));
+ p.drawPoint((x * 2), y);
+ p.drawPoint((x * 2) + 1, y);
+ }
+ break;
+ case MODE_VERTICAL_LINES:
+ p.setPen(QPen(colorScheme->skinColors[INDEX_SPEC_BASE + (16 - x)]));
+ p.drawLine((x * 2), (15 - x), (x * 2), 15);
+ p.drawLine((x * 2) + 1, (15 - x), (x * 2) + 1, 15);
+ break;
+ case MODE_NORMAL:
+ // Fall through
+ default:
+ for (unsigned int y = (16 - x); y < 16; y++) {
+ p.setPen(QPen(colorScheme->skinColors[INDEX_SPEC_BASE + y]));
+ p.drawPoint((x * 2), y);
+ p.drawPoint((x * 2) + 1, y);
+ }
+ break;
+ }
+ }
+}
+
+void GuiSpectrumAnalyser::paintEvent (QPaintEvent *)
+{
+ if (visualization_mode == MODE_DISABLED)
+ paintBackground();
+}
+
+void GuiSpectrumAnalyser::pixmapChange()
+{
+ delete analyserCache;
+ analyserCache = NULL;
+}
+
+
+#include "guiSpectrumAnalyser.moc"
+
diff --git a/noatun/modules/winskin/guiSpectrumAnalyser.h b/noatun/modules/winskin/guiSpectrumAnalyser.h
new file mode 100644
index 00000000..ecef8d37
--- /dev/null
+++ b/noatun/modules/winskin/guiSpectrumAnalyser.h
@@ -0,0 +1,66 @@
+/*
+ a GUI for a spectrum analyser
+ Copyright (C) 1998 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef _GUISPECTRUMANALYSER_H
+#define _GUISPECTRUMANALYSER_H
+
+#include <math.h>
+
+#include "winSkinVis.h"
+#include "waWidget.h"
+#include "waColor.h"
+
+
+#define MAX_MODE 1
+
+enum visualizationMode {MODE_DISABLED = 0, MODE_ANALYSER = 1};
+enum analyserMode {MODE_NORMAL = 0, MODE_FIRE = 1, MODE_VERTICAL_LINES = 2};
+
+class GuiSpectrumAnalyser : public WaWidget {
+ Q_OBJECT
+
+ public:
+ GuiSpectrumAnalyser();
+ ~GuiSpectrumAnalyser();
+
+ void mousePressEvent(QMouseEvent * mouseEvent);
+
+ public slots:
+ void pauseVisualization();
+ void resumeVisualization();
+
+ private slots:
+ void pixmapChange();
+ void updatePeaks();
+
+ void setVisualizationMode(int);
+ void setAnalyserMode(int);
+
+ private:
+ void paintEvent(QPaintEvent *);
+
+ QPopupMenu *contextMenu;
+ QPopupMenu *visualizationMenu;
+ QPopupMenu *analyserMenu;
+
+ void freshenAnalyserCache();
+
+ int visualization_mode;
+ int analyser_mode;
+
+ QPixmap *analyserCache;
+ WinSkinVis *winSkinVis;
+};
+#endif
+
diff --git a/noatun/modules/winskin/mimetypes/Makefile.am b/noatun/modules/winskin/mimetypes/Makefile.am
new file mode 100644
index 00000000..c28d0e8e
--- /dev/null
+++ b/noatun/modules/winskin/mimetypes/Makefile.am
@@ -0,0 +1,2 @@
+
+SUBDIRS = interface
diff --git a/noatun/modules/winskin/mimetypes/interface/Makefile.am b/noatun/modules/winskin/mimetypes/interface/Makefile.am
new file mode 100644
index 00000000..9ca52284
--- /dev/null
+++ b/noatun/modules/winskin/mimetypes/interface/Makefile.am
@@ -0,0 +1,6 @@
+noatun_modules_winskin_mimetypes_interface_DATA = x-winamp-skin.desktop
+
+noatun_modules_winskin_mimetypes_interfacedir = $(kde_mimedir)/interface
+
+EXTRA_DIST = $(noatun_modules_winskin_mimetypes_interface_DATA)
+
diff --git a/noatun/modules/winskin/mimetypes/interface/x-winamp-skin.desktop b/noatun/modules/winskin/mimetypes/interface/x-winamp-skin.desktop
new file mode 100644
index 00000000..34686e98
--- /dev/null
+++ b/noatun/modules/winskin/mimetypes/interface/x-winamp-skin.desktop
@@ -0,0 +1,58 @@
+[Desktop Entry]
+Type=MimeType
+MimeType=interface/x-winamp-skin
+Icon=colorscm
+Patterns=*.wsz;*.WSZ
+Comment=Compressed Winamp Skin
+Comment[bg]=Компресирана тема за Winamp
+Comment[bn]=কম্প্রেস করা উইন-অ্যাম্প স্কিন
+Comment[br]=Kroc'hen Winamp gwasket
+Comment[bs]=Komprimirani Winamp skin
+Comment[ca]=Aparença Winamp comprimida
+Comment[cs]=Komprimovaný Winamp skin
+Comment[cy]=Croen Winamp cywasgedig
+Comment[da]=Komprimeret Winamp-forside
+Comment[de]=Komprimierte Winamp-Oberfläche
+Comment[el]=Συμπιεσμένο θέμα Winamp
+Comment[eo]=Kunpremita Winamp-etoso
+Comment[es]=Piel comprimida de Winamp
+Comment[et]=Winampi pakitud kest (skin)
+Comment[eu]=Winamp azal konprimitua
+Comment[fa]=Winamp Skin فشرده
+Comment[fi]=Pakattu Winamp-nahka
+Comment[fr]=Revêtement Winamp compacté
+Comment[gl]=Pel de Winamp Comprimida
+Comment[he]=Winamp דחוס של Skin
+Comment[hi]=संपीडित विनएम्प स्किन
+Comment[hu]=Tömörített Winamp-kinézet
+Comment[is]=Þjappað Winamp-skin
+Comment[it]=Skin di Winamp compressa
+Comment[ja]=圧縮された Winamp のスキン
+Comment[kk]=Сығылған Winamp тысы
+Comment[km]=ស្បែក Winamp បាន​បង្ហាប់
+Comment[ko]=압축된 Winamp 스킨
+Comment[lt]=Suglaudintas Winamp pavidalas
+Comment[mk]=Компресирана маска Winamp
+Comment[nb]=Komprimert Winamp-ham
+Comment[nds]=Komprimeert Winamp-Böversiet
+Comment[ne]=सङ्कुचित विन्याप स्किन
+Comment[nl]=Gecomprimeerde Winamp-skin
+Comment[nn]=Komprimert Winamp-drakt
+Comment[pl]=Skompresowana skóra Winampa
+Comment[pt]=Aspecto Comprimido do Winamp
+Comment[pt_BR]=Skin do Winamp comprimido
+Comment[ro]=Interfaţă Winamp comprimată
+Comment[ru]=Сжатая тема Winamp
+Comment[sk]=Komprimované rozhranie pre Winamp
+Comment[sl]=Stisnjena preobleka za Winamp
+Comment[sr]=Компресован Winamp-ов скин
+Comment[sr@Latn]=Kompresovan Winamp-ov skin
+Comment[sv]=Komprimerat Winamp-skal
+Comment[ta]=அழுத்தப்பட்ட வின் ஆம்ப் அலங்கார அமைப்பு
+Comment[tg]=Намуди Фишурдашудаи Winamp
+Comment[th]=หน้ากากวินแอมป์บีบอัด Compress
+Comment[tr]=Sıkıştırılmış Winamp Teması
+Comment[uk]=Стиснутий жупан Winamp
+Comment[zh_CN]=压缩的 Winamp 皮肤
+Comment[zh_HK]=已壓縮的 Winamp skin
+Comment[zh_TW]=壓縮的 Winamp 面板
diff --git a/noatun/modules/winskin/plugin.cpp b/noatun/modules/winskin/plugin.cpp
new file mode 100644
index 00000000..8890c515
--- /dev/null
+++ b/noatun/modules/winskin/plugin.cpp
@@ -0,0 +1,13 @@
+#include <kglobal.h>
+#include <klocale.h>
+
+#include "winSkinConfig.h"
+#include "waSkin.h"
+
+extern "C" {
+ KDE_EXPORT Plugin *create_plugin() {
+ WaSkin *new_skin = new WaSkin();
+ new WinSkinConfig(new_skin, new_skin->skinManager());
+ return new_skin;
+ }
+}
diff --git a/noatun/modules/winskin/skinMap.h b/noatun/modules/winskin/skinMap.h
new file mode 100644
index 00000000..f67f1557
--- /dev/null
+++ b/noatun/modules/winskin/skinMap.h
@@ -0,0 +1,38 @@
+/*
+ generic type for describing skins.
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef _SKINMAP_H
+#define _SKINMAP_H
+
+#include <qpixmap.h>
+
+typedef struct {
+ int x;
+ int y;
+ int width;
+ int height;
+} SkinMap;
+
+typedef struct {
+ int fileId;
+ int x;
+ int y;
+ int width;
+ int height;
+} SkinDesc;
+
+
+
+#endif
diff --git a/noatun/modules/winskin/skins/Makefile.am b/noatun/modules/winskin/skins/Makefile.am
new file mode 100644
index 00000000..8f8e17ab
--- /dev/null
+++ b/noatun/modules/winskin/skins/Makefile.am
@@ -0,0 +1,11 @@
+# widgetlib - Makefile.am
+
+SUBDIRS = winamp
+
+
+
+
+
+
+
+
diff --git a/noatun/modules/winskin/skins/winamp/BALANCE.BMP b/noatun/modules/winskin/skins/winamp/BALANCE.BMP
new file mode 100644
index 00000000..3ce036e9
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/BALANCE.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/CBUTTONS.BMP b/noatun/modules/winskin/skins/winamp/CBUTTONS.BMP
new file mode 100644
index 00000000..8b335e55
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/CBUTTONS.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/FONT.BMP b/noatun/modules/winskin/skins/winamp/FONT.BMP
new file mode 100644
index 00000000..57caa947
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/FONT.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/MAIN.BMP b/noatun/modules/winskin/skins/winamp/MAIN.BMP
new file mode 100644
index 00000000..5e8a2cec
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/MAIN.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/MONOSTER.BMP b/noatun/modules/winskin/skins/winamp/MONOSTER.BMP
new file mode 100644
index 00000000..35fee70c
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/MONOSTER.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/Makefile.am b/noatun/modules/winskin/skins/winamp/Makefile.am
new file mode 100644
index 00000000..169e1bc5
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/Makefile.am
@@ -0,0 +1,8 @@
+
+EXTRA_DIST = BALANCE.BMP CBUTTONS.BMP FONT.BMP MAIN.BMP \
+ MONOSTER.BMP NUMS_EX.BMP PLAYPAUS.BMP \
+ POSBAR.BMP SHUFREP.BMP SPEC.BMP \
+ TEXT.BMP TITLEBAR.BMP VISCOLOR.TXT VOLUME.BMP
+
+skin_DATA = $(EXTRA_DIST)
+skindir = $(kde_datadir)/noatun/skins/winamp/Winamp
diff --git a/noatun/modules/winskin/skins/winamp/NUMS_EX.BMP b/noatun/modules/winskin/skins/winamp/NUMS_EX.BMP
new file mode 100644
index 00000000..72c3215d
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/NUMS_EX.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/PLAYPAUS.BMP b/noatun/modules/winskin/skins/winamp/PLAYPAUS.BMP
new file mode 100644
index 00000000..90319a0e
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/PLAYPAUS.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/POSBAR.BMP b/noatun/modules/winskin/skins/winamp/POSBAR.BMP
new file mode 100644
index 00000000..be6636ca
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/POSBAR.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/SHUFREP.BMP b/noatun/modules/winskin/skins/winamp/SHUFREP.BMP
new file mode 100644
index 00000000..12f5e406
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/SHUFREP.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/SPEC.BMP b/noatun/modules/winskin/skins/winamp/SPEC.BMP
new file mode 100644
index 00000000..38df7813
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/SPEC.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/TEXT.BMP b/noatun/modules/winskin/skins/winamp/TEXT.BMP
new file mode 100644
index 00000000..5b0b3a77
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/TEXT.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/TITLEBAR.BMP b/noatun/modules/winskin/skins/winamp/TITLEBAR.BMP
new file mode 100644
index 00000000..b3109235
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/TITLEBAR.BMP
Binary files differ
diff --git a/noatun/modules/winskin/skins/winamp/VISCOLOR.TXT b/noatun/modules/winskin/skins/winamp/VISCOLOR.TXT
new file mode 100644
index 00000000..5871ad89
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/VISCOLOR.TXT
@@ -0,0 +1,24 @@
+0,0,0, // color 0 = black
+24,33,41, // color 1 = grey for dots
+239,49,16, // color 2 = top of spec
+206,41,16, // 3
+214,90,0, // 4
+214,102,0, // 5
+214,115,0, // 6
+198,123,8, // 7
+222,165,24, // 8
+214,181,33, // 9
+189,222,41, // 10
+148,222,33, // 11
+41,206,16, // 12
+50,190,16, // 13
+57,181,16, // 14
+49,156,8, // 15
+41,148,0, // 16
+24,132,8, // 17 = bottom of spec
+255,255,255, // 18 = osc 1
+214,214,222, // 19 = osc 2 (slightly dimmer)
+181,189,189, // 20 = osc 3
+160,170,175, // 21 = osc 4
+148,156,165, // 22 = osc 4
+150, 150, 150, // 23 = analyzer peak dots
diff --git a/noatun/modules/winskin/skins/winamp/VOLUME.BMP b/noatun/modules/winskin/skins/winamp/VOLUME.BMP
new file mode 100644
index 00000000..deb7e924
--- /dev/null
+++ b/noatun/modules/winskin/skins/winamp/VOLUME.BMP
Binary files differ
diff --git a/noatun/modules/winskin/vis/Makefile.am b/noatun/modules/winskin/vis/Makefile.am
new file mode 100644
index 00000000..6935d860
--- /dev/null
+++ b/noatun/modules/winskin/vis/Makefile.am
@@ -0,0 +1,39 @@
+INCLUDES= -I$(kde_includes)/arts $(all_includes)
+KDE_OPTIONS = nofinal
+
+DISTCLEANFILES = winskinvis.h winskinvis.cc
+
+winskinvis.mcopclass: winskinvis.h
+winskinvis.mcoptype: winskinvis.h
+winskinvis.cc winskinvis.h : $(srcdir)/winskinvis.idl
+ $(MCOPIDL) -t -I$(kde_includes)/arts $(srcdir)/winskinvis.idl
+
+lib_LTLIBRARIES = libwinskinvis.la
+libwinskinvis_la_SOURCES= winskinvis.cc \
+ winSkinFFT_impl.cpp realFFT.cpp \
+ realFFTFilter.cpp visQueue.cpp
+
+
+libwinskinvis_la_LDFLAGS= $(all_libraries) -avoid-version \
+ -no-undefined
+
+libwinskinvis_la_LIBADD = -lkmedia2_idl -lsoundserver_idl -lartsflow
+libwinskinvis_la_COMPILE_FIRST = winskinvis.cc
+libwinskinvis_la_METASOURCES = AUTO
+
+
+
+mcoptypedir = $(libdir)/mcop
+mcoptype_DATA = winskinvis.mcoptype winskinvis.mcopclass
+
+mcopclassdir = $(libdir)/mcop/Noatun
+mcopclass_DATA = WinSkinFFT.mcopclass
+
+noatuninclude_HEADERS = winskinvis.h
+
+noatunincludedir = $(includedir)/noatun
+
+
+winSkinFFT_impl.lo: winskinvis.h
+winskinvis.lo: winskinvis.h
+
diff --git a/noatun/modules/winskin/vis/WinSkinFFT.mcopclass b/noatun/modules/winskin/vis/WinSkinFFT.mcopclass
new file mode 100644
index 00000000..90d21e61
--- /dev/null
+++ b/noatun/modules/winskin/vis/WinSkinFFT.mcopclass
@@ -0,0 +1,5 @@
+Interface=Noatun::WinSkinFFT,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libwinskinvis.la
+
+
diff --git a/noatun/modules/winskin/vis/realFFT.cpp b/noatun/modules/winskin/vis/realFFT.cpp
new file mode 100644
index 00000000..330280ea
--- /dev/null
+++ b/noatun/modules/winskin/vis/realFFT.cpp
@@ -0,0 +1,156 @@
+/*
+ a FFT class
+ Copyright (C) 1998 Martin Vogt;Philip VanBaren
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "realFFT.h"
+
+/*
+ * Initialize the Sine table and Twiddle pointers (bit-reversed pointers)
+ * for the FFT routine.
+ */
+RealFFT::RealFFT(int fftlen) {
+ int i;
+ int temp;
+ int mask;
+
+ /*
+ * FFT size is only half the number of data points
+ * The full FFT output can be reconstructed from this FFT's output.
+ * (This optimization can be made since the data is real.)
+ */
+ Points = fftlen;
+
+ if((SinTable=(short *)malloc(Points*sizeof(short)))==NULL)
+ {
+ puts("Error allocating memory for Sine table.");
+ exit(1);
+ }
+ if((BitReversed=(int *)malloc(Points/2*sizeof(int)))==NULL)
+ {
+ puts("Error allocating memory for BitReversed.");
+ exit(1);
+ }
+
+ for(i=0;i<Points/2;i++)
+ {
+ temp=0;
+ for(mask=Points/4;mask>0;mask >>= 1)
+ temp=(temp >> 1) + (i&mask ? Points/2 : 0);
+
+ BitReversed[i]=temp;
+ }
+
+ for(i=0;i<Points/2;i++)
+ {
+ register double s,c;
+ s=floor(-32768.0*sin(2*M_PI*i/(Points))+0.5);
+ c=floor(-32768.0*cos(2*M_PI*i/(Points))+0.5);
+ if(s>32767.5) s=32767;
+ if(c>32767.5) c=32767;
+ SinTable[BitReversed[i] ]=(short)s;
+ SinTable[BitReversed[i]+1]=(short)c;
+ }
+
+}
+
+/*
+ * Free up the memory allotted for Sin table and Twiddle Pointers
+ */
+RealFFT::~RealFFT() {
+ free(BitReversed);
+ free(SinTable);
+ Points=0;
+}
+
+
+/*
+ * Actual FFT routine. Must call InitializeFFT(fftlen) first!
+ * This routine has another parameter list than the other fft's
+ * But because we want a fast fft on pcm data this routine
+ * is better than the other two.
+ * The other two can be useful for inverse FFT.
+ * The format is an array of floats. (only real parts the img
+ * part does not exists)
+ */
+void RealFFT::fft(short* buffer) {
+ int ButterfliesPerGroup=Points/4;
+
+ endptr1=buffer+Points;
+
+ /*
+ * Butterfly:
+ * Ain-----Aout
+ * \ /
+ * / \
+ * Bin-----Bout
+ */
+
+ while(ButterfliesPerGroup>0)
+ {
+ A=buffer;
+ B=buffer+ButterfliesPerGroup*2;
+ sptr=SinTable;
+
+ while(A<endptr1)
+ {
+ register short sin=*sptr;
+ register short cos=*(sptr+1);
+ endptr2=B;
+ while(A<endptr2)
+ {
+ long v1=((long)*B*cos + (long)*(B+1)*sin) >> 15;
+ long v2=((long)*B*sin - (long)*(B+1)*cos) >> 15;
+ *B=(*A+v1)>>1;
+ *(A++)=*(B++)-v1;
+ *B=(*A-v2)>>1;
+ *(A++)=*(B++)+v2;
+ }
+ A=B;
+ B+=ButterfliesPerGroup*2;
+ sptr+=2;
+ }
+ ButterfliesPerGroup >>= 1;
+ }
+ /*
+ * Massage output to get the output for a real input sequence.
+ */
+ br1=BitReversed+1;
+ br2=BitReversed+Points/2-1;
+
+ while(br1<=br2)
+ {
+ register long temp1,temp2;
+ short sin=SinTable[*br1];
+ short cos=SinTable[*br1+1];
+ A=buffer+*br1;
+ B=buffer+*br2;
+ HRplus = (HRminus = *A - *B ) + (*B << 1);
+ HIplus = (HIminus = *(A+1) - *(B+1)) + (*(B+1) << 1);
+ temp1 = ((long)sin*HRminus - (long)cos*HIplus) >> 15;
+ temp2 = ((long)cos*HRminus + (long)sin*HIplus) >> 15;
+ *B = (*A = (HRplus + temp1) >> 1) - temp1;
+ *(B+1) = (*(A+1) = (HIminus + temp2) >> 1) - HIminus;
+
+ br1++;
+ br2--;
+ }
+ /*
+ * Handle DC bin separately
+ */
+ buffer[0]+=buffer[1];
+ buffer[1]=0;
+}
+
+
+int* RealFFT::getBitReversed() {
+ return BitReversed;
+}
diff --git a/noatun/modules/winskin/vis/realFFT.h b/noatun/modules/winskin/vis/realFFT.h
new file mode 100644
index 00000000..39c6dbfd
--- /dev/null
+++ b/noatun/modules/winskin/vis/realFFT.h
@@ -0,0 +1,69 @@
+/*
+ a FFT class
+ Copyright (C) 1998 Martin Vogt;Philip VanBaren
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __REALFFT_H
+#define __REALFFT_H
+
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/**
+ <pre>
+ * Program: REALFFTF.C
+ * Author: Philip VanBaren
+ * Date: 2 September 1993
+ *
+ * Description: These routines perform an FFT on real data.
+ * On a 486/33 compiled using Borland C++ 3.1 with full
+ * speed optimization and a small memory model, a 1024 point
+ * FFT takes about 16ms.
+ * This code is for floating point data.
+ *
+ * Note: Output is BIT-REVERSED! so you must use the BitReversed to
+ * get legible output, (i.e. Real_i = buffer[ BitReversed[i] ]
+ * Imag_i = buffer[ BitReversed[i]+1 ] )
+ * Input is in normal order.
+ </pre>
+ */
+
+
+
+class RealFFT {
+
+ int* BitReversed;
+ short* SinTable;
+ int Points;
+
+ public:
+ RealFFT(int fftlen);
+ ~RealFFT();
+
+ void fft(short* buffer);
+ int* getBitReversed();
+
+ private:
+
+ short *A,*B;
+ short *sptr;
+ short *endptr1,*endptr2;
+ int *br1,*br2;
+ long HRplus,HRminus,HIplus,HIminus;
+
+
+};
+
+
+#endif
diff --git a/noatun/modules/winskin/vis/realFFTFilter.cpp b/noatun/modules/winskin/vis/realFFTFilter.cpp
new file mode 100644
index 00000000..13343bce
--- /dev/null
+++ b/noatun/modules/winskin/vis/realFFTFilter.cpp
@@ -0,0 +1,88 @@
+/*
+ a FFT filter
+ Copyright (C) 1998 Martin Vogt;Philip VanBaren, 2 September 1993
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "realFFTFilter.h"
+
+RealFFTFilter::RealFFTFilter(int fftPoints) {
+ this->fftPoints = fftPoints;
+ data=new short[fftPoints*4];
+ realFFT= new RealFFT(fftPoints*2);
+}
+
+
+RealFFTFilter::~RealFFTFilter() {
+ delete data;
+ delete realFFT;
+}
+
+
+int RealFFTFilter::getPoints() {
+ return fftPoints;
+}
+
+
+short* RealFFTFilter::getPointPtr() {
+ return data;
+}
+
+
+/*
+ the array is expected to be a PCM stream (real data)
+*/
+int RealFFTFilter::fft16(float* left,float* right,int len) {
+ int i;
+
+ len=len/4;
+
+
+ int mixTmp;
+
+ // take care for no array overflows:
+ int n=min(len,fftPoints);
+
+ // copy things into fftArray.
+
+ for (i = 0 ; i < n; i++) {
+ mixTmp=(int) (16384.0*(left[i]+right[i]));
+
+ if (mixTmp < SHRT_MIN) {
+ data[i]= SHRT_MIN;
+ } else {
+ if (mixTmp > SHRT_MAX) {
+ data[i] = SHRT_MAX;
+ } else {
+ data[i]=(short)mixTmp;
+ }
+ }
+ }
+
+ realFFT->fft(data);
+ return true;
+}
+
+
+
+int* RealFFTFilter::getBitReversed() {
+ return realFFT->getBitReversed();
+}
+
+
+
+int RealFFTFilter::min(int x1,int x2) {
+ if (x1 < x2) {
+ return x1;
+ }
+ return x2;
+}
+
+
diff --git a/noatun/modules/winskin/vis/realFFTFilter.h b/noatun/modules/winskin/vis/realFFTFilter.h
new file mode 100644
index 00000000..255e5191
--- /dev/null
+++ b/noatun/modules/winskin/vis/realFFTFilter.h
@@ -0,0 +1,49 @@
+/*
+ a FFT filter
+ Copyright (C) 1998 Martin Vogt;Philip VanBaren, 2 September 1993
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __REALFFTFILTER_H
+#define __REALFFTFILTER_H
+
+
+
+#include "realFFT.h"
+#include <limits.h>
+
+
+
+class RealFFTFilter {
+
+ int fftPoints;
+ RealFFT* realFFT;
+
+ short* data;
+
+
+ public:
+ RealFFTFilter(int points);
+ ~RealFFTFilter();
+ int fft16(float* left,float* right,int len);
+
+ int* getBitReversed();
+ int getPoints();
+ short* getPointPtr();
+
+ private:
+ int min(int x1,int x2);
+
+};
+
+
+#endif
diff --git a/noatun/modules/winskin/vis/visQueue.cpp b/noatun/modules/winskin/vis/visQueue.cpp
new file mode 100644
index 00000000..370930d2
--- /dev/null
+++ b/noatun/modules/winskin/vis/visQueue.cpp
@@ -0,0 +1,43 @@
+/*
+ queue fft samples
+ Copyright (C) 2001 Martin Vogt
+
+ This program 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "visQueue.h"
+
+
+VISQueue::VISQueue(int elements) {
+ this->elements=elements;
+
+ visArrayQueue = new std::vector<float>*[elements];
+ for(int i=0;i<elements;i++)
+ {
+ visArrayQueue[i]=new std::vector<float>;
+ }
+
+}
+
+VISQueue::~VISQueue() {
+ for(int i=0;i<elements;i++) {
+ delete visArrayQueue[i];
+ }
+ delete [] visArrayQueue;
+}
+
+std::vector<float>* VISQueue::getElement(int i)
+{
+ if ( (i < 0) || (i>elements) ) {
+ return visArrayQueue[0];
+ }
+ return visArrayQueue[i];
+}
+
+
diff --git a/noatun/modules/winskin/vis/visQueue.h b/noatun/modules/winskin/vis/visQueue.h
new file mode 100644
index 00000000..2f737fd1
--- /dev/null
+++ b/noatun/modules/winskin/vis/visQueue.h
@@ -0,0 +1,32 @@
+/*
+ queue fft samples
+ Copyright (C) 2001 Martin Vogt
+
+ This program 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __VISQUEUE_H
+#define __VISQUEUE_H
+
+
+#include <vector>
+
+class VISQueue {
+
+ int elements;
+ std::vector<float> **visArrayQueue;
+
+ public:
+ VISQueue(int elements);
+ ~VISQueue();
+
+ std::vector<float>* getElement(int i);
+
+};
+#endif
diff --git a/noatun/modules/winskin/vis/winSkinFFT_impl.cpp b/noatun/modules/winskin/vis/winSkinFFT_impl.cpp
new file mode 100644
index 00000000..5396ac3c
--- /dev/null
+++ b/noatun/modules/winskin/vis/winSkinFFT_impl.cpp
@@ -0,0 +1,148 @@
+/*
+ implementation for winskin fft
+ Copyright (C) 2000 Martin Vogt
+
+ This program 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "winSkinFFT_impl.h"
+#include <audiosubsys.h>
+#include <cstring>
+
+#define __BANDS 75
+
+namespace Noatun {
+
+WinSkinFFT_impl::WinSkinFFT_impl() {
+ fftBands_short=256;
+ realFFTFilter= new RealFFTFilter(fftBands_short);
+ fftArray=new int[fftBands_short];
+ bands=0;
+
+ fragCnt=(int)(AudioSubSystem::the()->fragmentCount());
+ visQueue=new VISQueue(fragCnt);
+ writePos=0;
+
+}
+
+WinSkinFFT_impl::~WinSkinFFT_impl(){
+ delete realFFTFilter;
+ delete fftArray;
+ delete visQueue;
+}
+
+void WinSkinFFT_impl::streamInit() {
+}
+
+
+void WinSkinFFT_impl::streamStart() {
+}
+
+
+void WinSkinFFT_impl::calculateBlock(unsigned long samples) {
+
+
+ unsigned long i;
+
+ // monitoring only tasks can't be done with that StereoEffect
+ // interface nicely - copy input to output until there is
+ // something better
+ // (when?)
+ int n=sizeof(float)*samples;
+ memcpy(outleft,inleft,n);
+ memcpy(outright,inright,n);
+
+
+ if (realFFTFilter->fft16(inleft,inright,samples) == false) {
+ return;
+ }
+
+
+
+ //
+ // The following modifications have nothing to do
+ // with an fft, they only make the output look nice.
+ // (mostly scaling)
+
+ short* fftPtr;
+ int* bitReversed;
+
+ fftPtr=realFFTFilter->getPointPtr();
+ bitReversed=realFFTFilter->getBitReversed();
+
+ int pos=0;
+ int step=realFFTFilter->getPoints()/__BANDS;
+
+
+ int re;
+ int im;
+ int tmp;
+
+ float max=0.0;
+ float avg=0.0;
+
+
+
+ for (i=0;i<__BANDS;i++) {
+ re=(int)fftPtr[bitReversed[pos]];
+ im=(int)fftPtr[bitReversed[pos]+1];
+
+ tmp=re*re+im*im;
+ // Here I check a new idea. We remove all low values
+ // and all values over xyz to xyz.
+ fftArray[pos]=(int)(::sqrt(::sqrt(tmp)));
+
+ if (fftArray[pos]<=15) {
+ max+=fftArray[pos];
+ } else {
+ max+=15+fftArray[pos]/2;
+ }
+ pos=pos+step;
+ }
+ avg=0.65*max/(float)__BANDS;
+
+ pos=0;
+ vector<float>* visAnalyserArray=visQueue->getElement(writePos);
+ visAnalyserArray->clear();
+ visAnalyserArray->reserve(__BANDS);
+ for (i=0;i<__BANDS;i++) {
+ float val=(float)(fftArray[pos]-avg);
+ visAnalyserArray->push_back(val);
+ pos=pos+step;
+ }
+ writePos++;
+ if (writePos >= fragCnt) writePos=0;
+
+}
+
+
+void WinSkinFFT_impl::bandResolution(float res) {
+ bands=(int)res;
+}
+
+float WinSkinFFT_impl::bandResolution() {
+ return (float)bands;
+}
+
+
+vector<float>* WinSkinFFT_impl::scope() {
+ int delay=writePos+1;
+ if (delay >= fragCnt) delay=0;
+
+
+ vector<float>* visAnalyserArray=visQueue->getElement(delay);
+
+ return new vector<float>(*visAnalyserArray);
+}
+
+
+REGISTER_IMPLEMENTATION(WinSkinFFT_impl);
+
+}
diff --git a/noatun/modules/winskin/vis/winSkinFFT_impl.h b/noatun/modules/winskin/vis/winSkinFFT_impl.h
new file mode 100644
index 00000000..c1a77e45
--- /dev/null
+++ b/noatun/modules/winskin/vis/winSkinFFT_impl.h
@@ -0,0 +1,62 @@
+/*
+ implementation for winskin fft
+ Copyright (C) 2000 Martin Vogt
+
+ This program 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __WINSKINFFT_IMPL_H
+#define __WINSKINFFT_IMPL_H
+
+#include <artsflow.h>
+#include <stdsynthmodule.h>
+#include "winskinvis.h"
+#include "realFFTFilter.h"
+#include "visQueue.h"
+
+
+using namespace std;
+using namespace Arts;
+
+namespace Noatun {
+
+class WinSkinFFT_impl : public WinSkinFFT_skel, public StdSynthModule {
+
+ public:
+
+ WinSkinFFT_impl();
+ ~WinSkinFFT_impl();
+
+ void streamInit();
+ void streamStart();
+
+ // in: audio stream inleft, inright;
+ // out: audio stream outleft, outright;
+ void calculateBlock(unsigned long samples);
+
+ void bandResolution(float res);
+ float bandResolution();
+ vector<float> *scope();
+
+ private:
+ RealFFTFilter* realFFTFilter;
+ int fftBands_short;
+ int* fftArray;
+ VISQueue* visQueue;
+ int bands;
+
+ int fragCnt;
+ int writePos;
+ int readPos;
+};
+
+
+}
+
+#endif
diff --git a/noatun/modules/winskin/vis/winskinvis.idl b/noatun/modules/winskin/vis/winskinvis.idl
new file mode 100644
index 00000000..9b6564f4
--- /dev/null
+++ b/noatun/modules/winskin/vis/winskinvis.idl
@@ -0,0 +1,12 @@
+#include <artsflow.idl>
+
+module Noatun
+{
+
+interface WinSkinFFT : Arts::StereoEffect
+{
+ attribute float bandResolution;
+ sequence<float> scope();
+};
+
+}; \ No newline at end of file
diff --git a/noatun/modules/winskin/waBalanceSlider.cpp b/noatun/modules/winskin/waBalanceSlider.cpp
new file mode 100644
index 00000000..1ac1b562
--- /dev/null
+++ b/noatun/modules/winskin/waBalanceSlider.cpp
@@ -0,0 +1,56 @@
+/*
+ balanceslider for winamp skins
+ Copyright (C) 1998 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include <waBalanceSlider.h>
+
+
+WaBalanceSlider::WaBalanceSlider() : WaWidget(_WA_MAPPING_BALANCE_BAR)
+{
+}
+
+
+WaBalanceSlider::~WaBalanceSlider()
+{
+}
+
+
+void WaBalanceSlider::buildGui()
+{
+ ws = new WaSlider(_WA_MAPPING_BALANCE_BAR,
+ _WA_MAPPING_BALANCE_SLIDER, true);
+
+ ws->setRange(-100, 100);
+
+
+ ws->setPixmapSliderButtonUp(_WA_SKIN_BALANCE_SLIDER_NORM);
+ ws->setPixmapSliderButtonDown(_WA_SKIN_BALANCE_SLIDER_PRES);
+ ws->setPixmapSliderBar(_WA_SKIN_BALANCE_BAR);
+
+ ws->setValue(0);
+
+ connect(ws, SIGNAL(valueChanged(int)), this,
+ SIGNAL(balanceSetValue(int)));
+ connect(ws, SIGNAL(sliderPressed()), SIGNAL(sliderPressed()));
+ connect(ws, SIGNAL(sliderReleased()), SIGNAL(sliderReleased()));
+}
+
+
+void WaBalanceSlider::setBalanceValue(int val)
+{
+ int currVal = ws->value();
+ if (currVal != val) {
+ ws->setValue(val);
+ }
+}
+
+#include "waBalanceSlider.moc"
diff --git a/noatun/modules/winskin/waBalanceSlider.h b/noatun/modules/winskin/waBalanceSlider.h
new file mode 100644
index 00000000..d120bcc7
--- /dev/null
+++ b/noatun/modules/winskin/waBalanceSlider.h
@@ -0,0 +1,42 @@
+/*
+ balanceslider for winamp skins
+ Copyright (C) 1999 Martin Vogt
+ Copyright (C) 2001 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __WABALANCESLIDER_H
+#define __WABALANCESLIDER_H
+
+#include <waSlider.h>
+#include "waWidget.h"
+
+class WaBalanceSlider : public WaWidget {
+ Q_OBJECT
+
+ public:
+ WaBalanceSlider();
+ ~WaBalanceSlider();
+ void buildGui();
+
+ void setBalanceValue(int val);
+
+ private:
+ WaSlider *ws;
+
+ signals:
+ void balanceSetValue(int val);
+ void sliderPressed();
+ void sliderReleased();
+};
+
+
+#endif
diff --git a/noatun/modules/winskin/waButton.cpp b/noatun/modules/winskin/waButton.cpp
new file mode 100644
index 00000000..cac0275a
--- /dev/null
+++ b/noatun/modules/winskin/waButton.cpp
@@ -0,0 +1,101 @@
+/*
+ standard Button fo winamp Skin
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include <qbitmap.h>
+
+#include "waButton.h"
+
+WaButton::WaButton(int mapId) : WaWidget(mapId)
+{
+ _togglable = false;
+ _toggled = false;
+ pressed = false;
+}
+
+
+WaButton::~WaButton() {
+}
+
+void WaButton::setPixmapUp(int pixId) {
+ nUpId=pixId;
+}
+
+
+void WaButton::setPixmapDown(int pixId) {
+ nDownId=pixId;
+}
+
+
+void WaButton::setPixmapUpSelected(int pixId) {
+ nUpIdSelected=pixId;
+}
+
+
+void WaButton::setPixmapDownSelected(int pixId) {
+ nDownIdSelected=pixId;
+}
+
+void WaButton::paintEvent(QPaintEvent *) {
+ paintPixmap(getPixmapId());
+}
+
+
+void WaButton::mousePressEvent(QMouseEvent* e) {
+ if (e->button() != LeftButton) {
+ // We can't deal with it, but maybe the widget can do something clever
+ WaWidget::mousePressEvent(e);
+ }
+ else {
+ pressed = true;
+ update();
+ }
+}
+
+void WaButton::mouseReleaseEvent(QMouseEvent* e) {
+ if (!pressed) {
+ // We're not pressed, so just pass along the mouse event, it's not ours
+ WaWidget::mouseReleaseEvent(e);
+ }
+ else {
+ pressed = false;
+
+ if (this->rect().contains(e->pos())){
+ if (_togglable) {
+ _toggled = !_toggled;
+ emit(toggleEvent(_toggled));
+ }
+
+ emit(clicked());
+ }
+ }
+
+ update();
+}
+
+int WaButton::getPixmapId() {
+ if (_toggled == true) {
+ if (pressed)
+ return nDownIdSelected;
+ else
+ return nUpIdSelected;
+ }
+ else {
+ if (pressed)
+ return nDownId;
+ else
+ return nUpId;
+ }
+
+ return -1;
+}
+
+#include "waButton.moc"
diff --git a/noatun/modules/winskin/waButton.h b/noatun/modules/winskin/waButton.h
new file mode 100644
index 00000000..50947216
--- /dev/null
+++ b/noatun/modules/winskin/waButton.h
@@ -0,0 +1,62 @@
+/*
+ standard Button for winamp Skin
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __WABUTTON_H
+#define __WABUTTON_H
+
+#include <qpainter.h>
+#include "waWidget.h"
+
+class WaButton : public WaWidget {
+ Q_OBJECT
+ public:
+ WaButton(int mapId);
+ ~WaButton();
+
+ void setPixmapDown(int pixId);
+ void setPixmapUp(int pixId);
+ void setPixmapUpSelected(int pixId);
+ void setPixmapDownSelected(int pixId);
+
+ void setToggled(bool toggled_flag) { _toggled = toggled_flag; update(); }
+ bool toggled() const { return _toggled; }
+
+ void setTogglable(bool togglable_flag) { _togglable = togglable_flag; update(); }
+ bool togglable() const { return _togglable; }
+
+ int getPixmapId();
+ void paintEvent(QPaintEvent*);
+
+ private:
+ void mousePressEvent (QMouseEvent* e);
+ void mouseReleaseEvent (QMouseEvent* e);
+
+ int nUpId;
+ int nDownId;
+ int nDownIdSelected;
+ int nUpIdSelected;
+
+ QPoint currentLocation;
+
+ bool _toggled;
+ bool _togglable;
+
+ bool pressed;
+
+ signals:
+ void toggleEvent(bool val);
+ void clicked();
+};
+#endif
diff --git a/noatun/modules/winskin/waClutterbar.cpp b/noatun/modules/winskin/waClutterbar.cpp
new file mode 100644
index 00000000..6ceb7d04
--- /dev/null
+++ b/noatun/modules/winskin/waClutterbar.cpp
@@ -0,0 +1,11 @@
+#include "waClutterbar.h"
+#include "waClutterbar.moc"
+WaClutterbar::WaClutterbar() : WaWidget(_WA_MAPPING_CLUTTERBAR) {
+}
+
+WaClutterbar::~WaClutterbar() {
+}
+
+void WaClutterbar::paintEvent(QPaintEvent *) {
+ paintPixmap(_WA_SKIN_CLUTTERBAR_DISABLED);
+}
diff --git a/noatun/modules/winskin/waClutterbar.h b/noatun/modules/winskin/waClutterbar.h
new file mode 100644
index 00000000..69339c37
--- /dev/null
+++ b/noatun/modules/winskin/waClutterbar.h
@@ -0,0 +1,18 @@
+#ifndef __WACLUTTERBAR_H
+#define __WACLUTTERBAR_H
+
+#include <qpainter.h>
+#include "waWidget.h"
+
+class WaClutterbar : public WaWidget {
+ Q_OBJECT
+
+ public:
+ WaClutterbar();
+ ~WaClutterbar();
+
+ public slots:
+ void paintEvent(QPaintEvent *);
+};
+
+#endif
diff --git a/noatun/modules/winskin/waColor.cpp b/noatun/modules/winskin/waColor.cpp
new file mode 100644
index 00000000..e06b38bc
--- /dev/null
+++ b/noatun/modules/winskin/waColor.cpp
@@ -0,0 +1,73 @@
+#include <fstream>
+#include <qfile.h>
+
+#include "waColor.h"
+
+WaColor *colorScheme = NULL;
+
+WaColor::WaColor(QString filename) {
+ int r, g, b;
+ char comma;
+
+ skinColors[0].setRgb(0, 0, 0);
+ skinColors[1].setRgb(24, 33, 41);
+ skinColors[2].setRgb(239, 49, 16);
+ skinColors[3].setRgb(206, 41, 16);
+ skinColors[4].setRgb(214, 90, 0);
+ skinColors[5].setRgb(214, 102, 0);
+ skinColors[6].setRgb(214, 115, 0);
+ skinColors[7].setRgb(198, 123, 8);
+ skinColors[8].setRgb(222, 165, 24);
+ skinColors[9].setRgb(214, 181, 33);
+ skinColors[10].setRgb(189, 222, 41);
+ skinColors[11].setRgb(148, 222, 33);
+ skinColors[12].setRgb(41, 206, 16);
+ skinColors[13].setRgb(50, 190, 16);
+ skinColors[14].setRgb(57, 181, 16);
+ skinColors[15].setRgb(49, 156, 8);
+ skinColors[16].setRgb(41, 148, 0);
+ skinColors[17].setRgb(24, 132, 8);
+ skinColors[18].setRgb(255, 255, 255);
+ skinColors[19].setRgb(214, 214, 222);
+ skinColors[20].setRgb(181, 189, 189);
+ skinColors[21].setRgb(160, 170, 175);
+ skinColors[22].setRgb(148, 156, 165);
+ skinColors[23].setRgb(150, 150, 150);
+
+ if (filename.isEmpty()) {
+ return;
+ }
+
+ std::ifstream viscolor(QFile::encodeName(filename));
+
+ if (!viscolor)
+ return;
+
+ for (int index = 0;index < 24;index++) {
+ viscolor >> r;
+ viscolor >> std::ws;
+ viscolor >> comma;
+ viscolor >> std::ws;
+ viscolor >> g;
+ viscolor >> std::ws;
+ viscolor >> comma;
+ viscolor >> std::ws;
+ viscolor >> b;
+
+ while(1) {
+ char c;
+
+ if (!viscolor.get(c))
+ return;
+
+ if (c == '\n')
+ break;
+ }
+
+ skinColors[index].setRgb(r, g, b);
+ }
+
+}
+
+WaColor::~WaColor() {
+}
diff --git a/noatun/modules/winskin/waColor.h b/noatun/modules/winskin/waColor.h
new file mode 100644
index 00000000..a0e18484
--- /dev/null
+++ b/noatun/modules/winskin/waColor.h
@@ -0,0 +1,26 @@
+
+
+#ifndef WACOLOR_H
+#define WACOLOR_H
+
+#include <qcolor.h>
+#include <qstring.h>
+
+#define INDEX_BACKGROUND_COLOR 0
+#define INDEX_GRID_COLOR 1
+#define INDEX_SPEC_BASE 2
+#define INDEX_OSC_BASE 18
+#define INDEX_PEAKS 23
+
+class WaColor {
+public:
+ WaColor(QString filename);
+ ~WaColor();
+
+ QColor skinColors[24];
+};
+
+extern WaColor *colorScheme;
+
+#endif
+
diff --git a/noatun/modules/winskin/waDigit.cpp b/noatun/modules/winskin/waDigit.cpp
new file mode 100644
index 00000000..b775d7cf
--- /dev/null
+++ b/noatun/modules/winskin/waDigit.cpp
@@ -0,0 +1,89 @@
+/*
+ The digit for the time
+ Copyright (C) 1999 Martin Vogt
+ Copyright (C) 2002 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "waDigit.h"
+#include "waSkinModel.h"
+
+#include <kconfig.h>
+#include <kglobal.h>
+
+WaDigit::WaDigit() : WaWidget(_WA_MAPPING_DIGITS)
+{
+ KConfig *config = KGlobal::config();
+ config->setGroup("Winskin");
+
+ reverse_time = config->readNumEntry("timeReversed", false);
+}
+
+
+WaDigit::~WaDigit()
+{
+ KConfig *config = KGlobal::config();
+ config->setGroup("Winskin");
+ config->writeEntry("timeReversed", reverse_time);
+}
+
+void WaDigit::paintEvent(QPaintEvent *)
+{
+ paintBackground();
+
+ const char *time = timeString.latin1();
+ int len = strlen(time);
+ if (len == 0)
+ return;
+
+ // Declare all these variables after we check for zero-length
+ WaSkinModel *waSkinModel = WaSkinModel::instance();
+
+ int x = waSkinModel->getMapGeometry(mapping).x();
+ int y = waSkinModel->getMapGeometry(mapping).y();
+
+ QRect mapRect;
+
+ // We expect strings either in the form "xx:yy", or "-xx:yy"
+ // If the string length is 6, we have it in the form of "-xx:yy"
+ // Remove the -, move the string forward one character, and
+ // continue parsing
+ mapRect = waSkinModel->getMapGeometry(_WA_MAPPING_MINUS);
+ if (len == 6) {
+ waSkinModel->getDigit('-', this, mapRect.x() - x, mapRect.y() - y);
+ time++;
+ }
+ else {
+ waSkinModel->getDigit(' ', this, mapRect.x() - x, mapRect.y() - y);
+ }
+
+ mapRect = waSkinModel->getMapGeometry(_WA_MAPPING_DIGIT_1);
+ waSkinModel->getDigit(time[0], this, mapRect.x() - x, mapRect.y() - y);
+
+ mapRect = waSkinModel->getMapGeometry(_WA_MAPPING_DIGIT_2);
+ waSkinModel->getDigit(time[1], this, mapRect.x() - x, mapRect.y() - y);
+
+ mapRect = waSkinModel->getMapGeometry(_WA_MAPPING_DIGIT_3);
+ waSkinModel->getDigit(time[3], this, mapRect.x() - x, mapRect.y() - y);
+
+ mapRect = waSkinModel->getMapGeometry(_WA_MAPPING_DIGIT_4);
+ waSkinModel->getDigit(time[4], this, mapRect.x() - x, mapRect.y() - y);
+}
+
+void WaDigit::mousePressEvent(QMouseEvent* e) {
+ if (e->button() == LeftButton) {
+ reverse_time = !reverse_time;
+ emit digitsClicked();
+ }
+ else
+ WaWidget::mousePressEvent(e);
+}
+
+#include "waDigit.moc"
diff --git a/noatun/modules/winskin/waDigit.h b/noatun/modules/winskin/waDigit.h
new file mode 100644
index 00000000..cefbfeb3
--- /dev/null
+++ b/noatun/modules/winskin/waDigit.h
@@ -0,0 +1,48 @@
+/*
+ The digit for the time
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __WADIGIT_H
+#define __WADIGIT_H
+
+#include <qpainter.h>
+
+#include "waWidget.h"
+
+class WaDigit : public WaWidget {
+ Q_OBJECT
+
+ public:
+ WaDigit();
+ ~WaDigit();
+
+ void setTime(QString time) { timeString = time; update(); }
+ QString time() const { return timeString; }
+
+ bool timeReversed() const { return reverse_time; }
+
+ public slots:
+ void paintEvent(QPaintEvent * paintEvent);
+
+ private:
+ void mousePressEvent(QMouseEvent* e);
+ bool reverse_time;
+
+ WaSkinModel *waSkinModel;
+ QString timeString;
+
+ signals:
+ void digitsClicked();
+};
+#endif
diff --git a/noatun/modules/winskin/waIndicator.cpp b/noatun/modules/winskin/waIndicator.cpp
new file mode 100644
index 00000000..9c5efa84
--- /dev/null
+++ b/noatun/modules/winskin/waIndicator.cpp
@@ -0,0 +1,34 @@
+/*
+ State indicator for Winamp Skin
+ Copyright (C) 2002 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include <waIndicator.h>
+
+WaIndicator::WaIndicator(int widget_mapping, int enabled_mapping, int disabled_mapping)
+ : WaWidget(widget_mapping)
+{
+ _state = false;
+ _enabled_mapping = enabled_mapping;
+ _disabled_mapping = disabled_mapping;
+}
+
+WaIndicator::~WaIndicator()
+{
+}
+
+void WaIndicator::paintEvent(QPaintEvent *)
+{
+ paintPixmap( _state ? _enabled_mapping : _disabled_mapping );
+}
+
+
+#include "waIndicator.moc"
diff --git a/noatun/modules/winskin/waIndicator.h b/noatun/modules/winskin/waIndicator.h
new file mode 100644
index 00000000..f367fe84
--- /dev/null
+++ b/noatun/modules/winskin/waIndicator.h
@@ -0,0 +1,41 @@
+/*
+ Standard state indicator for Winamp skin
+ Copyright (C) 2002 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __WAINDICATOR_H
+#define __WAINDICATOR_H
+
+#include <qpainter.h>
+
+#include "waWidget.h"
+
+class WaIndicator : public WaWidget {
+ Q_OBJECT
+
+ public:
+ WaIndicator(int widget_mapping, int enabled_mapping, int disabled_mapping);
+ ~WaIndicator();
+
+ void setState(bool state) { _state = state; update(); }
+ bool state() const { return _state; }
+
+ public slots:
+ void paintEvent(QPaintEvent *);
+
+ private:
+ int _enabled_mapping;
+ int _disabled_mapping;
+ bool _state;
+};
+#endif
diff --git a/noatun/modules/winskin/waInfo.cpp b/noatun/modules/winskin/waInfo.cpp
new file mode 100644
index 00000000..c735a8e4
--- /dev/null
+++ b/noatun/modules/winskin/waInfo.cpp
@@ -0,0 +1,173 @@
+/*
+ Scrolling song title for winamp Skin
+ Copyright (C) 1999 Martin Vogt
+ Copyright (C) 2001 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include <qpainter.h>
+#include <qpixmap.h>
+
+#include <stdlib.h>
+
+#include <kconfig.h>
+#include <kglobal.h>
+
+#include "waInfo.h"
+#include "waSkinModel.h"
+
+WaInfo::WaInfo() : WaWidget(_WA_MAPPING_INFO)
+{
+ connect(WaSkinModel::instance(), SIGNAL(skinChanged()),
+ this, SLOT(pixmapChange()));
+
+ completePixmap = new QPixmap();
+
+ QSize size = sizeHint();
+ completePixmap->resize(size.width(), size.height());
+
+ xGrabbedPos = -1;
+
+ timer = new QTimer(this);
+ connect(timer, SIGNAL(timeout()), this, SLOT(timeEvent()));
+}
+
+WaInfo::~WaInfo()
+{
+ delete completePixmap;
+}
+
+
+void WaInfo::timeEvent()
+{
+ if ((xGrabbedPos == -1) && (xScrollDirection)) {
+ xScrollPos += xScrollDirection;
+
+ if (abs(xScrollPos) > completePixmap->width()) {
+ xScrollPos = 0;
+ }
+
+ if (isVisible())
+ repaint(false);
+ }
+}
+
+
+void WaInfo::scrollerSetup()
+{
+ xScrollPos = 0;
+ xScrollDirection = 0;
+ timer->stop();
+ QSize size = sizeHint();
+ if (completePixmap->width() > size.width()) {
+ xScrollDirection = 1;
+
+ KConfig *config=KGlobal::config();
+ config->setGroup("Winskin");
+ int s = config->readNumEntry("ScrollDelay", 15);
+ if (s!=0)
+ timer->start(50-s);
+ }
+}
+
+void WaInfo::paintEvent(QPaintEvent *)
+{
+ QSize size = sizeHint();
+
+ if (completePixmap->width() <= size.width()) {
+ bitBlt(this, 0, 0, completePixmap);
+ return;
+ }
+
+ // pixmap widther than window:
+ int xDrawWidth;
+ int xRestWidth;
+
+ xDrawWidth = completePixmap->width() - xScrollPos;
+ if (xDrawWidth > size.width()) {
+ xDrawWidth = size.width();
+ }
+
+
+ bitBlt(this, 0, 0, completePixmap, xScrollPos, 0, xDrawWidth);
+
+ if (xDrawWidth < size.width()) {
+ xRestWidth = size.width() - xDrawWidth;
+ bitBlt(this, xDrawWidth, 0, completePixmap, 0, 0, xRestWidth);
+ }
+}
+
+
+void WaInfo::setText(QString song)
+{
+ if (_text != song) {
+ _text = song;
+ pixmapChange();
+ }
+}
+
+QString WaInfo::text() const
+{
+ return _text;
+}
+
+
+void WaInfo::pixmapChange()
+{
+ int i;
+ const char *infoString = _text.latin1();
+
+ int x = 0;
+ int n=infoString ? strlen(infoString) : 0;
+
+ QSize size = sizeHint();
+
+ completePixmap->resize(QMAX(n * _WA_TEXT_WIDTH, size.width()), _WA_TEXT_HEIGHT);
+
+ for (i = 0; i < n; i++) {
+ WaSkinModel::instance()->getText(infoString[i], completePixmap, x, 0);
+ x += _WA_TEXT_WIDTH;
+ }
+
+ // if the size is now smaller than the with of this widget, we
+ // fill the pixmap with spaces
+ if (x < size.width()) {
+ while (x < size.width()) {
+ WaSkinModel::instance()->getText(' ', completePixmap, x, 0);
+ x += _WA_TEXT_WIDTH;
+ }
+ }
+
+ scrollerSetup();
+ update();
+}
+
+void WaInfo::mousePressEvent (QMouseEvent *e) {
+ if (e->button() == LeftButton)
+ xGrabbedPos = (e->x() + xScrollPos) % completePixmap->width();
+}
+
+void WaInfo::mouseMoveEvent (QMouseEvent * e) {
+ xScrollPos = -e->x() + xGrabbedPos;
+
+ if (xScrollPos < 0)
+ xScrollPos = completePixmap->width() - (-xScrollPos % completePixmap->width());
+ else
+ xScrollPos %= completePixmap->width();
+
+ update();
+}
+
+void WaInfo::mouseReleaseEvent (QMouseEvent *) {
+ xGrabbedPos = -1;
+}
+
+
+#include "waInfo.moc"
diff --git a/noatun/modules/winskin/waInfo.h b/noatun/modules/winskin/waInfo.h
new file mode 100644
index 00000000..513ff3cc
--- /dev/null
+++ b/noatun/modules/winskin/waInfo.h
@@ -0,0 +1,53 @@
+/*
+ standard Button for winamp Skin
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __WAINFO_H
+#define __WAINFO_H
+
+#include <qpainter.h>
+#include <qtimer.h>
+
+#include "waWidget.h"
+
+class WaInfo : public WaWidget {
+ Q_OBJECT
+
+ public:
+ WaInfo();
+ ~WaInfo();
+
+ void setText(QString song);
+ QString text() const;
+ void scrollerSetup();
+
+ protected:
+ void paintEvent(QPaintEvent * paintEvent);
+
+ void mousePressEvent (QMouseEvent * e);
+ void mouseMoveEvent (QMouseEvent * e);
+ void mouseReleaseEvent (QMouseEvent * e);
+
+ QPixmap *completePixmap;
+ QString _text;
+ QTimer *timer;
+ int xScrollPos;
+ int xScrollDirection;
+ int xGrabbedPos;
+
+ protected slots:
+ void pixmapChange();
+ void timeEvent();
+};
+#endif
diff --git a/noatun/modules/winskin/waJumpSlider.cpp b/noatun/modules/winskin/waJumpSlider.cpp
new file mode 100644
index 00000000..51633300
--- /dev/null
+++ b/noatun/modules/winskin/waJumpSlider.cpp
@@ -0,0 +1,78 @@
+/*
+ jumpslider for winamp skins
+ Copyright (C) 1998 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "waJumpSlider.h"
+
+WaJumpSlider::WaJumpSlider() : WaWidget(_WA_MAPPING_POS_BAR)
+{
+}
+
+WaJumpSlider::~WaJumpSlider()
+{
+}
+
+void WaJumpSlider::buildGui()
+{
+ ws = new WaSlider(_WA_MAPPING_POS_BAR, _WA_MAPPING_POS_BAR_SLIDER);
+ ws->setPixmapSliderButtonUp(_WA_SKIN_POS_BAR_SLIDER_NORM);
+ ws->setPixmapSliderButtonDown(_WA_SKIN_POS_BAR_SLIDER_PRES);
+ ws->setPixmapSliderBar(_WA_SKIN_POS_BAR);
+ ws->setRange(0, 100);
+ ws->setValue(0);
+
+ connect(ws, SIGNAL(sliderPressed()), this, SIGNAL(sliderPressed()));
+ connect(ws, SIGNAL(sliderReleased()), this, SLOT(releasedSlider()));
+ connect(ws, SIGNAL(valueChanged(int)), this, SIGNAL(valueChanged(int)));
+}
+
+void WaJumpSlider::setJumpRange(int val)
+{
+ if (val == -1)
+ ws->hideButton();
+ else {
+ ws->showButton();
+ ws->setRange(0, val);
+ }
+}
+
+int WaJumpSlider::jumpValue() {
+ return ws->value();
+}
+
+void WaJumpSlider::setJumpValue(int val)
+{
+ int currVal = ws->value();
+
+ if (currVal != val) {
+ ws->setValue(val);
+ }
+}
+
+void WaJumpSlider::releasedSlider() {
+ emit(jump(ws->value()));
+ emit(sliderReleased());
+}
+
+void WaJumpSlider::showEvent (QShowEvent *) {
+ ws->show();
+}
+
+void WaJumpSlider::hideEvent (QHideEvent *) {
+ ws->hide();
+}
+
+void WaJumpSlider::cancelDrag() {
+ ws->cancelDrag();
+}
+
+#include "waJumpSlider.moc"
diff --git a/noatun/modules/winskin/waJumpSlider.h b/noatun/modules/winskin/waJumpSlider.h
new file mode 100644
index 00000000..12c07808
--- /dev/null
+++ b/noatun/modules/winskin/waJumpSlider.h
@@ -0,0 +1,52 @@
+/*
+ jumpslider for winamp skins
+ Copyright (C) 1998 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __WAJUMPSLIDER_H
+#define __WAJUMPSLIDER_H
+
+#include "waSlider.h"
+#include "waWidget.h"
+
+class WaJumpSlider : public WaWidget {
+ Q_OBJECT
+
+ public:
+ WaJumpSlider();
+ ~WaJumpSlider();
+ void buildGui();
+
+ void setJumpRange(int val);
+
+ void setJumpValue(int val);
+ int jumpValue();
+
+ void cancelDrag();
+
+ protected:
+ WaSlider *ws;
+ void showEvent (QShowEvent *);
+ void hideEvent (QHideEvent *);
+
+ private slots:
+ void releasedSlider();
+
+ signals:
+ void jump(int seconds);
+ void sliderPressed();
+ void sliderReleased();
+ void valueChanged(int);
+};
+
+
+#endif
diff --git a/noatun/modules/winskin/waLabel.cpp b/noatun/modules/winskin/waLabel.cpp
new file mode 100644
index 00000000..8f3ddb60
--- /dev/null
+++ b/noatun/modules/winskin/waLabel.cpp
@@ -0,0 +1,65 @@
+/*
+ Standard label for Winskin
+ Copyright (C) 1999 Martin Vogt
+ Copyright (C) 2001 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include <qpainter.h>
+#include <qpixmap.h>
+
+#include "waLabel.h"
+#include "waSkinModel.h"
+
+WaLabel::WaLabel(int mapping) : WaWidget(mapping)
+{
+ connect(WaSkinModel::instance(), SIGNAL(skinChanged()),
+ this, SLOT(pixmapChange()));
+
+ completePixmap = new QPixmap();
+
+ QSize size = sizeHint();
+
+ completePixmap->resize(size.width(), size.height());
+}
+
+WaLabel::~WaLabel()
+{
+ delete completePixmap;
+}
+
+void WaLabel::paintEvent(QPaintEvent *)
+{
+ bitBlt(this, 0, 0, completePixmap);
+}
+
+void WaLabel::setText(const QString &new_text)
+{
+ int width = WaSkinModel::instance()->getMapGeometry(mapping).width();
+
+ // Fit the text to the widget
+ // This should always be three characters, but we generate its value anyway
+ _text = new_text.rightJustify(width / _WA_TEXT_WIDTH, ' ');
+
+ pixmapChange();
+
+ update();
+}
+
+void WaLabel::pixmapChange()
+{
+ const char *label_text = _text.latin1();
+ int n = label_text ? strlen(label_text) : 0;
+
+ for (int i = 0; i < n; i++)
+ WaSkinModel::instance()->getText(label_text[i], completePixmap,
+ i * _WA_TEXT_WIDTH, 0);
+}
+
+#include "waLabel.moc"
diff --git a/noatun/modules/winskin/waLabel.h b/noatun/modules/winskin/waLabel.h
new file mode 100644
index 00000000..f1470902
--- /dev/null
+++ b/noatun/modules/winskin/waLabel.h
@@ -0,0 +1,39 @@
+/*
+ standard Button for winamp Skin
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#ifndef __WABPS_H
+#define __WABPS_H
+
+#include <qpainter.h>
+#include "waWidget.h"
+
+class WaLabel : public WaWidget {
+ Q_OBJECT
+
+ public:
+ WaLabel(int mapping);
+ ~WaLabel();
+
+ void setText(const QString &text);
+ QString text() const { return _text; }
+
+ private:
+ void paintEvent(QPaintEvent *);
+
+ QPixmap *completePixmap;
+ QString _text;
+
+
+ private slots:
+ void pixmapChange();
+};
+#endif
diff --git a/noatun/modules/winskin/waMain.cpp b/noatun/modules/winskin/waMain.cpp
new file mode 100644
index 00000000..a5bb7fdc
--- /dev/null
+++ b/noatun/modules/winskin/waMain.cpp
@@ -0,0 +1,13 @@
+#include "waMain.h"
+
+WaMain::WaMain() : WaWidget(_WA_MAPPING_MAIN) {
+}
+
+WaMain::~WaMain() {
+}
+
+void WaMain::paintEvent(QPaintEvent *) {
+ paintPixmap(_WA_SKIN_MAIN);
+}
+
+#include "waMain.moc"
diff --git a/noatun/modules/winskin/waMain.h b/noatun/modules/winskin/waMain.h
new file mode 100644
index 00000000..06bcfe5d
--- /dev/null
+++ b/noatun/modules/winskin/waMain.h
@@ -0,0 +1,16 @@
+#ifndef __WAMAIN_H
+#define __WAMAIN_H
+
+#include "waWidget.h"
+
+class WaMain : WaWidget {
+ Q_OBJECT
+public:
+ WaMain();
+ ~WaMain();
+
+protected:
+ void paintEvent(QPaintEvent *);
+};
+
+#endif
diff --git a/noatun/modules/winskin/waRegion.cpp b/noatun/modules/winskin/waRegion.cpp
new file mode 100644
index 00000000..ab4d2e57
--- /dev/null
+++ b/noatun/modules/winskin/waRegion.cpp
@@ -0,0 +1,126 @@
+/*
+ Winamp Skin
+ Copyright (C) 2002 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include <ksimpleconfig.h>
+#include <qbitmap.h>
+#include <qpainter.h>
+#include <qregexp.h>
+
+#include "waSkinModel.h"
+#include "waSkins.h"
+#include "waRegion.h"
+
+WaRegion *windowRegion = NULL;
+
+// Hack around case-insensitivity in Winamp INI files by searching for common capitializations
+// Needs to be replaced with an all-custom loader after 3.0
+const char *pointListNames[] = {"PointList", "pointlist", "Pointlist", "pointList", "POINTLIST", 0};
+const char *numPointsNames[] = {"NumPoints", "numpoints", "Numpoints", "numPoints", "NUMPOINTS", 0};
+
+WaRegion::WaRegion(QString filename) {
+ // Load the region file, which happens to be in KConfig format
+ KSimpleConfig regionFile(filename, true);
+
+ // Clear our variables by default
+ window_mask = 0;
+ shade_mask = 0;
+
+ // Make the new bitmaps, default window size
+ window_mask = new QBitmap(WaSkinModel::instance()->getMapGeometry(_WA_MAPPING_MAIN).size(), true);
+ shade_mask = new QBitmap(WaSkinModel::instance()->getMapGeometry(_WA_MAPPING_TITLE).size(), true);
+
+ // Load the normal window mask data
+ regionFile.setGroup("Normal");
+
+ QValueList<int> num_points;
+ for (int x = 0;numPointsNames[x];x++) {
+ if (regionFile.hasKey(numPointsNames[x]))
+ num_points = parseList(regionFile.readEntry(numPointsNames[x]));
+ }
+
+ QValueList<int> point_list;
+ for (int x = 0;pointListNames[x];x++) {
+ if (regionFile.hasKey(pointListNames[x]))
+ point_list = parseList(regionFile.readEntry(pointListNames[x]));
+ }
+
+ // Now build the mask
+ buildPixmap(num_points, point_list, window_mask);
+
+ // Load the windowshade mask data
+ regionFile.setGroup("WindowShade");
+
+ num_points = parseList(regionFile.readEntry("NumPoints"));
+ point_list = parseList(regionFile.readEntry("PointList"));
+
+ // Now build the mask
+ buildPixmap(num_points, point_list, shade_mask);
+}
+
+WaRegion::~WaRegion() {
+ delete window_mask;
+ delete shade_mask;
+}
+
+void WaRegion::buildPixmap(const QValueList<int> &num_points_list, const QValueList<int> &points_list, QBitmap *dest) {
+ if (!num_points_list.count()) {
+ dest->fill(Qt::color1);
+ return;
+ }
+
+ QValueList<int>::const_iterator points = points_list.begin();
+
+ QPainter p(dest);
+
+ // Coordinates in REGION.TXT can go one pixel beyond the window size
+ QBitmap bm(dest->width()+1,dest->height()+1,true);
+ QPainter bmp(&bm);
+
+ bmp.setBrush(Qt::color1);
+ bmp.setPen(Qt::NoPen); // The polygon border itself should not be part of the visible window
+
+ // Go over each "region" in the file
+ for (QValueList<int>::const_iterator num_points = num_points_list.begin();num_points != num_points_list.end();num_points++) {
+ // Make a new point array
+ QPointArray point_array(*num_points);
+
+ // Populate it
+ for (int i = 0;i < *num_points;i++) {
+ int x = (*points++);
+ int y = (*points++);
+
+ point_array.setPoint(i, x, y);
+ }
+
+ // Now draw it as a filled polygon on the mask
+ bmp.drawPolygon(point_array);
+ }
+
+ p.drawPixmap(0,0,bm,0,0,dest->width(),dest->height());
+}
+
+
+// The winamp list format is absolutely insane, it will accept either
+// commas or whitespace as the delimiter. This function deals with
+// that.
+QValueList<int> WaRegion::parseList(const QString &list) const {
+ QValueList<int> temp_list;
+
+ if (list.isEmpty())
+ return temp_list;
+
+ QStringList open=QStringList::split(QRegExp("[,\\s]+"), list);
+ for (QStringList::Iterator i=open.begin(); i != open.end(); ++i)
+ temp_list.append((*i).toInt());
+
+ return temp_list;
+}
diff --git a/noatun/modules/winskin/waRegion.h b/noatun/modules/winskin/waRegion.h
new file mode 100644
index 00000000..853909c1
--- /dev/null
+++ b/noatun/modules/winskin/waRegion.h
@@ -0,0 +1,26 @@
+#ifndef WAREGION_H
+#define WAREGION_H
+
+#include <qcolor.h>
+#include <qstring.h>
+
+class WaRegion {
+public:
+ WaRegion(QString filename);
+ ~WaRegion();
+
+ const QBitmap *mainWindowMask() const { return window_mask; }
+ const QBitmap *mainWindowShadeMask() const { return shade_mask; }
+
+private:
+ QValueList<int> parseList(const QString &list) const;
+ void buildPixmap(const QValueList<int> &num_points, const QValueList<int> &point_list, QBitmap *dest);
+
+ QBitmap *window_mask;
+ QBitmap *shade_mask;
+};
+
+extern WaRegion *windowRegion;
+
+#endif
+
diff --git a/noatun/modules/winskin/waShadeMapping.h b/noatun/modules/winskin/waShadeMapping.h
new file mode 100644
index 00000000..d28d4ca8
--- /dev/null
+++ b/noatun/modules/winskin/waShadeMapping.h
@@ -0,0 +1,148 @@
+/*
+ mapping from file and id to pixmap, and global player map
+ Copyright (C) 1999 Martin Vogt
+ Copyright (C) 2002 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __WASHADEMAPPING_H
+#define __WASHADEMAPPING_H
+
+
+/**
+ This file is not a header file in the normal sense.
+ It define _directly_ variables, which are used in the
+ WaSkinLoader class.
+ Its a bit black voodoo magic.
+*/
+
+/**
+ The Skin file format was downloaded from the web.
+ http://www.algonet.et.se/~daniel7
+
+ Author: unknown
+*/
+
+#include "skinMap.h"
+#include "waSkins.h"
+
+static const SkinMap shadeMapToGui[] = {
+ {0, 0, 275, 14}, // _WA_MAPPING_MAIN
+ {168, 3, 9, 9}, // _WA_MAPPING_CBUTTONS_PREV
+ {177, 3, 9, 9}, // _WA_MAPPING_CBUTTONS_PLAY
+ {186, 3, 9, 9}, // _WA_MAPPING_CBUTTONS_PAUSE
+ {195, 3, 9, 9}, // _WA_MAPPING_CBUTTONS_STOP
+ {204, 3, 9, 9}, // _WA_MAPPING_CBUTTONS_NEXT
+ {215, 3, 9, 9}, // _WA_MAPPING_CBUTTONS_EJECT
+ {239, 41, 29, 12}, // _WA_MAPPING_MONOSTER_STEREO
+ {212, 41, 27, 12}, // _WA_MAPPING_MONOSTER_MONO
+ {210, 89, 28, 15}, // _WA_MAPPING_REPEAT
+ {164, 89, 46, 15}, // _WA_MAPPING_SHUFFLE
+ {242, 58, 23, 12}, // _WA_MAPPING_PLAYLIST
+ {219, 58, 23, 12}, // _WA_MAPPING_EQ
+ {107, 57, 68, 13}, // _WA_MAPPING_VOLUME_BAR
+ {0, 0, 13, 11}, // _WA_MAPPING_VOLUME_SLIDER
+ {177, 57, 38, 13}, // _WA_MAPPING_BALANCE_BAR
+ {0, 0, 13, 11}, // _WA_MAPPING_BALANCE_SLIDER
+ {24, 28, 11, 9}, // _WA_MAPPING_PLAYPAUS
+ {16, 72, 248, 10}, // _WA_MAPPING_POS_BAR
+ {0, 0, 29, 10}, // _WA_MAPPING_POS_BAR_SLIDER
+ {130, 4, 27, 6}, // _WA_MAPPING_DIGITS
+ {130, 4, 5, 6}, // _WA_MAPPING_MINUS
+ {135, 4, 5, 6}, // _WA_MAPPING_DIGIT_1
+ {140, 4, 5, 6}, // _WA_MAPPING_DIGIT_2
+ {147, 4, 5, 6}, // _WA_MAPPING_DIGIT_3
+ {152, 4, 5, 6}, // _WA_MAPPING_DIGIT_4
+ {24, 43, 75, 16}, // _WA_MAPPING_ANALYSER
+ {111, 43, 15, 6}, // _WA_MAPPING_BPS
+ {156, 43, 10, 6}, // _WA_MAPPING_FREQ
+ {112, 27, 152, 6}, // _WA_MAPPING_INFO
+ {0, 0, 274, 14}, // _WA_MAPPING_TITLE
+ {6, 3, 9, 9}, // _WA_MAPPING_TITLE_MENU
+ {244, 3, 9, 9}, // _WA_MAPPING_TITLE_MIN
+ {254, 3, 9, 9}, // _WA_MAPPING_TITLE_SHADE
+ {264, 3, 9, 9}, // _WA_MAPPING_TITLE_CLOSE
+ {10, 22, 8, 43} // _WA_MAPPING_CLUTTERBAR
+};
+
+
+static const SkinDesc shadeMapFromFile[] = {
+ {_WA_FILE_TITLEBAR, 27, 29, 275, 14}, // _WA_SKIN_MAIN
+ {_WA_FILE_TITLEBAR, 195, 32, 9, 9}, // _WA_SKIN_CBUTTONS_PREV_NORM
+ {_WA_FILE_TITLEBAR, 195, 32, 9, 9}, // _WA_SKIN_CBUTTONS_PREV_PRES
+ {_WA_FILE_TITLEBAR, 204, 32, 9, 9}, //_WA_SKIN_CBUTTONS_PLAY_NORM,
+ {_WA_FILE_TITLEBAR, 204, 32, 9, 9}, // _WA_SKIN_CBUTTONS_PLAY_PRES
+ {_WA_FILE_TITLEBAR, 213, 32, 9, 9}, // _WA_SKIN_CBUTTONS_PAUSE_NORM
+ {_WA_FILE_TITLEBAR, 213, 32, 9, 9}, // _WA_SKIN_CBUTTONS_PAUSE_PRES
+ {_WA_FILE_TITLEBAR, 222, 32, 9, 9}, // _WA_SKIN_CBUTTONS_STOP_NORM
+ {_WA_FILE_TITLEBAR, 222, 32, 9, 9}, // _WA_SKIN_CBUTTONS_STOP_PRES
+ {_WA_FILE_TITLEBAR, 231, 32, 9, 9}, // _WA_SKIN_CBUTTONS_NEXT_NORM
+ {_WA_FILE_TITLEBAR, 231, 32, 9, 9}, // _WA_SKIN_CBUTTONS_NEXT_PRES
+ {_WA_FILE_TITLEBAR, 242, 32, 9, 9}, // _WA_SKIN_CBUTTONS_EJECT_NORM
+ {_WA_FILE_TITLEBAR, 242, 32, 9, 9}, // _WA_SKIN_CBUTTONS_EJECT_PRESS
+ {_WA_FILE_MONOSTER, 0, 0, 29, 12}, // _WA_SKIN_MONOSTER_STEREO_TRUE
+ {_WA_FILE_MONOSTER, 0, 12, 29, 12}, // _WA_SKIN_MONOSTER_STEREO_FALSE
+ {_WA_FILE_MONOSTER, 29, 0, 27, 12}, // _WA_SKIN_MONOSTER_MONO_TRUE
+ {_WA_FILE_MONOSTER, 29, 12, 27, 12}, // _WA_SKIN_MONOSTER_MONO_FALSE
+ {_WA_FILE_TEXT, 0, 6, 50, 6}, // _WA_SKIN_NUMBERS
+ {_WA_FILE_TEXT, 75, 6, 5, 6}, // _WA_SKIN_NUMBERS_MINUS
+ {_WA_FILE_TEXT, 50, 12, 5, 6}, // _WA_SKIN_NUMBERS_BLANK
+ {_WA_FILE_SHUFREP, 0, 0, 28, 15}, // _WA_SKIN_SHUFREP_REPEAT_NOT_SET_NORM
+ {_WA_FILE_SHUFREP, 0, 15, 28, 15}, // _WA_SKIN_SHUFREP_REPEAT_NOT_SET_PRES
+ {_WA_FILE_SHUFREP, 0, 30, 28, 15}, // _WA_SKIN_SHUFREP_REPEAT_SET_NORM
+ {_WA_FILE_SHUFREP, 0, 45, 28, 15}, // _WA_SKIN_SHUFREP_REPEAT_SET_PRES
+ {_WA_FILE_SHUFREP, 28, 0, 47, 15}, // _WA_SKIN_SHUFREP_SHUFFLE_NOT_SET_NORM
+ {_WA_FILE_SHUFREP, 28, 15, 47, 15}, // _WA_SKIN_SHUFREP_SHUFFLE_NOT_SET_PRES
+ {_WA_FILE_SHUFREP, 28, 30, 47, 15}, // _WA_SKIN_SHUFREP_SHUFFLE_SET_NORM
+ {_WA_FILE_SHUFREP, 28, 45, 47, 15}, // _WA_SKIN_SHUFREP_SHUFFLE_SET_PRES
+ {_WA_FILE_SHUFREP, 23, 61, 23, 12}, // _WA_SKIN_SHUFREP_PLAYLIST_NOT_SET_NORM
+ {_WA_FILE_SHUFREP, 69, 61, 23, 12}, // _WA_SKIN_SHUFREP_PLAYLIST_NOT_SET_PRES
+ {_WA_FILE_SHUFREP, 23, 73, 23, 12}, // _WA_SKIN_SHUFREP_PLAYLIST_SET_NORM
+ {_WA_FILE_SHUFREP, 73, 23, 12}, // _WA_SKIN_SHUFREP_PLAYLIST_SET_PRES
+ {_WA_FILE_SHUFREP, 0, 61, 23, 12}, // _WA_SKIN_SHUFREP_EQ_NOT_SET_NORM
+ {_WA_FILE_SHUFREP, 46, 61, 23, 12}, // _WA_SKIN_SHUFREP_EQ_NOT_SET_PRES
+ {_WA_FILE_SHUFREP, 0, 73, 23, 12}, // _WA_SKIN_SHUFREP_EQ_SET_NORM
+ {_WA_FILE_SHUFREP, 46, 73, 23, 12}, // _WA_SKIN_SHUFREP_EQ_SET_PRES
+ {_WA_FILE_TEXT, 0, 0, 155, 18}, // _WA_SKIN_TEXT
+ {_WA_FILE_VOLUME, 0, 0, 68, 421}, // _WA_SKIN_VOLUME_BAR_ALL_BARS
+ {_WA_FILE_VOLUME, 0, 0, 68, 13}, // _WA_SKIN_VOLUME_BAR
+ {_WA_FILE_VOLUME, 0, 422, 14, 11}, // _WA_SKIN_VOLUME_SLIDER_NORM
+ {_WA_FILE_VOLUME, 15, 422, 14, 11}, // _WA_SKIN_VOLUME_SLIDER_PRES
+ {_WA_FILE_BALANCE, 9, 0, 38, 421}, // _WA_SKIN_BALANCE_BAR_ALL_BARS
+ {_WA_FILE_BALANCE, 9, 0, 38, 13}, // _WA_SKIN_BALANCE_BAR
+ {_WA_FILE_BALANCE, 0, 422, 14, 11}, // _WA_SKIN_BALANCE_SLIDER_NORM
+ {_WA_FILE_BALANCE, 15, 422, 14, 11}, // _WA_SKIN_BALANCE_SLIDER_PRES
+ {_WA_FILE_POSBAR, 0, 0, 248, 10}, // _WA_SKIN_POS_BAR
+ {_WA_FILE_POSBAR, 278, 0, 29, 10}, // _WA_SKIN_POS_BAR_SLIDER_NORM
+ {_WA_FILE_POSBAR, 248, 0, 29, 10}, // _WA_SKIN_POS_BAR_SLIDER_PRES
+ {_WA_FILE_PLAYPAUS, 1, 0, 8, 9}, // _WA_SKIN_PLAYPAUS_PLAY
+ {_WA_FILE_PLAYPAUS, 9, 0, 9, 9}, // _WA_SKIN_PLAYPAUS_PAUSE
+ {_WA_FILE_PLAYPAUS, 18, 0, 9, 9}, // _WA_SKIN_PLAYPAUS_STOP
+ {_WA_FILE_PLAYPAUS, 27, 0, 2, 9}, // _WA_SKIN_PLAYPAUS_FILLER
+ {_WA_FILE_PLAYPAUS, 36, 0, 3, 9}, // _WA_SKIN_PLAYPAUS_WORK_INDICATOR
+ {_WA_FILE_TITLEBAR, 27, 29, 275, 14}, // _WA_SKIN_TITLE_ACTIVE
+ {_WA_FILE_TITLEBAR, 27, 42, 275, 14}, // _WA_SKIN_TITLE_INACTIVE
+ {_WA_FILE_TITLEBAR, 0, 9, 9, 9}, // _WA_SKIN_TITLE_MENU_PRES
+ {_WA_FILE_TITLEBAR, 33, 32, 9, 9}, // _WA_SKIN_TITLE_MENU_NORM
+ {_WA_FILE_TITLEBAR, 33, 45, 9, 9}, // _WA_SKIN_TITLE_MENU_INACTIVE
+ {_WA_FILE_TITLEBAR, 9, 9, 9, 9}, // _WA_SKIN_TITLE_MIN_PRES
+ {_WA_FILE_TITLEBAR, 271, 32, 9, 9}, // _WA_SKIN_TITLE_MIN_NORM
+ {_WA_FILE_TITLEBAR, 271, 45, 9, 9}, // _WA_SKIN_TITLE_MIN_INACTIVE
+ {_WA_FILE_TITLEBAR, 9, 27, 9, 9}, // _WA_SKIN_TITLE_SHADE_PRES
+ {_WA_FILE_TITLEBAR, 281, 32, 9, 9}, // _WA_SKIN_TITLE_SHADE_NORM
+ {_WA_FILE_TITLEBAR, 281, 45, 9, 9}, // _WA_SKIN_TITLE_SHADE_INACTIVE
+ {_WA_FILE_TITLEBAR, 18, 9, 9, 9}, // _WA_SKIN_TITLE_CLOSE_PRES
+ {_WA_FILE_TITLEBAR, 291, 32, 9, 9}, // _WA_SKIN_TITLE_CLOSE_NORM
+ {_WA_FILE_TITLEBAR, 291, 45, 9, 9}, // _WA_SKIN_TITLE_CLOSE_INACTIVE
+ {_WA_FILE_TITLEBAR, 304, 0, 8, 43} // _WA_SKIN_CLUTTERBAR_DISABLED
+};
+
+#endif
diff --git a/noatun/modules/winskin/waSkin.cpp b/noatun/modules/winskin/waSkin.cpp
new file mode 100644
index 00000000..7d95047d
--- /dev/null
+++ b/noatun/modules/winskin/waSkin.cpp
@@ -0,0 +1,800 @@
+/*
+ Winamp Skin
+ Copyright (C) 1999 Martin Vogt
+ Copyright (C) 2001-2002 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include <kconfig.h>
+#include <qstringlist.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <khelpmenu.h>
+#include <kpopupmenu.h>
+#include <kurldrag.h>
+#include <kstdaction.h>
+#include <kaction.h>
+#include <qdragobject.h>
+#include <kstandarddirs.h>
+#include <kwin.h>
+#include <time.h>
+
+#include <noatun/playlist.h>
+#include <noatun/app.h>
+#include <noatun/stdaction.h>
+
+#include "waSkin.h"
+
+#include "waRegion.h"
+
+#include "waMain.h"
+#include "waClutterbar.h"
+#include "waDigit.h"
+#include "waInfo.h"
+#include "waButton.h"
+#include "waLabel.h"
+#include "waIndicator.h"
+#include "waTitleBar.h"
+
+#include "waJumpSlider.h"
+#include "waBalanceSlider.h"
+#include "waVolumeSlider.h"
+
+#include "waStatus.h"
+
+#include "waSkinManager.h"
+#include "waSkinModel.h"
+
+#include "guiSpectrumAnalyser.h"
+#include "fileInfo.h"
+
+WaSkin *_waskin_instance = NULL;
+
+WaSkin::WaSkin() : QWidget(0, "NoatunWinampSkin"), UserInterface()
+{
+ mJumpPressed = false;
+ mVolumePressed = false;
+ mBalancePressed = false;
+
+ _waskin_instance = this;
+
+ // Our really low-level render-y sort of stuff
+ waSkinModel = new WaSkinModel();
+ // Skin management stuff
+ waSkinManager = new WaSkinManager();
+
+ createHighLevelElements();
+ createButtons();
+ setMinimumSize(sizeHint());
+ setMaximumSize(sizeHint());
+
+ KWin::setType(this->winId(), NET::Override);
+ setBackgroundMode(NoBackground);
+
+ setAcceptDrops(true);
+
+ title_shaded = false;
+
+ // These slots make Young Hickory love us
+ connect( napp, SIGNAL(hideYourself()), this, SLOT(hide()) );
+ connect( napp, SIGNAL(showYourself()), this, SLOT(show()) );
+
+ connect(napp->player(), SIGNAL(playlistShown()), this,
+ SLOT(playlistShown()));
+ connect(napp->player(), SIGNAL(playlistHidden()), this,
+ SLOT(playlistHidden()));
+ connect(napp->player(), SIGNAL(loopTypeChange(int)), this,
+ SLOT(loopChange(int)));
+ connect(napp->player(), SIGNAL(newSong()), this, SLOT(newSong()));
+
+ connect(napp->player(), SIGNAL(timeout()), this, SLOT(timetick()));
+ KConfig *config=KGlobal::config();
+
+ config->setGroup("Winskin");
+ QString skin = config->readEntry("CurrentSkin", WaSkin::defaultSkin());
+
+ loadSkin(skin);
+
+ setCaption(i18n("Noatun"));
+ setIcon(SmallIcon("noatun"));
+
+
+ QObject::connect(waTitleBar, SIGNAL(shaded()), this, SLOT(shadeEvent()));
+ // connect to players signals - so we can update our display if someone else
+ // changes settings...
+
+ connect(napp->player(), SIGNAL(stopped()), this, SLOT(slotStopped()));
+ connect(napp->player(), SIGNAL(playing()), this, SLOT(slotPlaying()));
+ connect(napp->player(), SIGNAL(paused()), this, SLOT(slotPaused()));
+
+ napp->player()->handleButtons();
+
+ playlist->setToggled(napp->playlist()->listVisible());
+
+ shuffle->setToggled(false);
+ repeat->setToggled(false);
+ waBalanceSlider->setBalanceValue(0);
+ waVolumeSlider->setVolumeValue(napp->player()->volume());
+
+ newSong();
+
+ // HACK: We won't get focus events otherwise
+ setFocusPolicy(QWidget::ClickFocus);
+
+ show();
+}
+
+
+WaSkin::~WaSkin()
+{
+ delete waSkinManager;
+ waSkinManager = 0L;
+}
+
+void WaSkin::loadSkin(QString newSkinDir)
+{
+ waSkinManager->loadSkin(newSkinDir);
+
+ setMinimumSize(sizeHint());
+
+ if (title_shaded) {
+ // waSkinModel::load() resets our skin model :(
+ waSkinModel->setSkinModel(WA_MODEL_WINDOWSHADE);
+ setMask(*windowRegion->mainWindowShadeMask());
+ }
+ else {
+ setMask(*windowRegion->mainWindowMask());
+ }
+}
+
+QSize WaSkin::sizeHint() const
+{
+ QRect temp_rect;
+
+ temp_rect = waSkinModel->getGeometry(_WA_SKIN_MAIN);
+
+ return temp_rect.size();
+}
+
+
+void WaSkin::createButtons()
+{
+ prev = new WaButton(_WA_MAPPING_CBUTTONS_PREV);
+ play = new WaButton(_WA_MAPPING_CBUTTONS_PLAY);
+ pause = new WaButton(_WA_MAPPING_CBUTTONS_PAUSE);
+ stop = new WaButton(_WA_MAPPING_CBUTTONS_STOP);
+ next = new WaButton(_WA_MAPPING_CBUTTONS_NEXT);
+ eject = new WaButton(_WA_MAPPING_CBUTTONS_EJECT);
+ shuffle = new WaButton(_WA_MAPPING_SHUFFLE);
+ repeat = new WaButton(_WA_MAPPING_REPEAT);
+ playlist = new WaButton(_WA_MAPPING_PLAYLIST);
+ eq = new WaButton(_WA_MAPPING_EQ);
+
+ menu = new WaButton(_WA_MAPPING_TITLE_MENU);
+ menu->setPixmapUp(_WA_SKIN_TITLE_MENU_NORM);
+ menu->setPixmapDown(_WA_SKIN_TITLE_MENU_PRES);
+ connect(menu, SIGNAL(clicked()), this, SLOT(menuEvent()));
+
+ minimize = new WaButton(_WA_MAPPING_TITLE_MIN);
+ minimize->setPixmapUp(_WA_SKIN_TITLE_MIN_NORM);
+ minimize->setPixmapDown(_WA_SKIN_TITLE_MIN_PRES);
+ connect(minimize, SIGNAL(clicked()), this, SLOT(minimizeEvent()));
+
+ titleshade = new WaButton(_WA_MAPPING_TITLE_SHADE);
+ titleshade->setPixmapUp(_WA_SKIN_TITLE_SHADE_NORM);
+ titleshade->setPixmapDown(_WA_SKIN_TITLE_SHADE_PRES);
+ connect(titleshade, SIGNAL(clicked()), this, SLOT(shadeEvent()));
+
+ close = new WaButton(_WA_MAPPING_TITLE_CLOSE);
+ close->setPixmapUp(_WA_SKIN_TITLE_CLOSE_NORM);
+ close->setPixmapDown(_WA_SKIN_TITLE_CLOSE_PRES);
+ connect(close, SIGNAL(clicked()), this, SLOT(doClose()));
+
+ shuffle->setTogglable(true);
+ shuffle->show();
+
+ repeat->setTogglable(true);
+ playlist->setTogglable(true);
+
+ connect(shuffle, SIGNAL(toggleEvent(bool)),
+ this, SLOT(shuffleClickedEvent(bool)));
+
+ connect(repeat, SIGNAL(toggleEvent(bool)),
+ this, SLOT(repeatClickedEvent(bool)));
+
+ connect(playlist, SIGNAL(toggleEvent(bool)),
+ this, SLOT(playlistClickedEvent(bool)));
+
+ connect(eq, SIGNAL(clicked()),
+ this, SLOT(eqClickedEvent()));
+
+ prev->setPixmapUp(_WA_SKIN_CBUTTONS_PREV_NORM);
+ prev->setPixmapDown(_WA_SKIN_CBUTTONS_PREV_PRES);
+ connect(prev, SIGNAL(clicked()), napp->player(), SLOT(back()));
+
+ play->setPixmapUp(_WA_SKIN_CBUTTONS_PLAY_NORM);
+ play->setPixmapDown(_WA_SKIN_CBUTTONS_PLAY_PRES);
+ connect(play, SIGNAL(clicked()), this, SLOT(playCurrentEvent()));
+
+ pause->setPixmapUp(_WA_SKIN_CBUTTONS_PAUSE_NORM);
+ pause->setPixmapDown(_WA_SKIN_CBUTTONS_PAUSE_PRES);
+ connect(pause, SIGNAL(clicked()), this, SLOT(playPauseEvent()));
+
+
+ stop->setPixmapUp(_WA_SKIN_CBUTTONS_STOP_NORM);
+ stop->setPixmapDown(_WA_SKIN_CBUTTONS_STOP_PRES);
+ connect(stop, SIGNAL(clicked()), napp->player(), SLOT(stop()));
+
+
+ next->setPixmapUp(_WA_SKIN_CBUTTONS_NEXT_NORM);
+ next->setPixmapDown(_WA_SKIN_CBUTTONS_NEXT_PRES);
+ connect(next, SIGNAL(clicked()), napp->player(), SLOT(forward()));
+
+
+ eject->setPixmapUp(_WA_SKIN_CBUTTONS_EJECT_NORM);
+ eject->setPixmapDown(_WA_SKIN_CBUTTONS_EJECT_PRESS);
+ connect(eject, SIGNAL(clicked()), napp, SLOT(fileOpen()));
+
+ shuffle->setPixmapUp(_WA_SKIN_SHUFREP_SHUFFLE_NOT_SET_NORM);
+ shuffle->setPixmapDown(_WA_SKIN_SHUFREP_SHUFFLE_NOT_SET_PRES);
+ shuffle->setPixmapUpSelected(_WA_SKIN_SHUFREP_SHUFFLE_SET_NORM);
+ shuffle->setPixmapDownSelected(_WA_SKIN_SHUFREP_SHUFFLE_SET_PRES);
+ shuffle->setToggled(true);
+
+ repeat->setPixmapUp(_WA_SKIN_SHUFREP_REPEAT_NOT_SET_NORM);
+ repeat->setPixmapDown(_WA_SKIN_SHUFREP_REPEAT_NOT_SET_PRES);
+ repeat->setPixmapUpSelected(_WA_SKIN_SHUFREP_REPEAT_SET_NORM);
+ repeat->setPixmapDownSelected(_WA_SKIN_SHUFREP_REPEAT_SET_PRES);
+
+
+ eq->setPixmapUp(_WA_SKIN_SHUFREP_EQ_NOT_SET_NORM);
+ eq->setPixmapDown(_WA_SKIN_SHUFREP_EQ_NOT_SET_PRES);
+
+ playlist->setPixmapUp(_WA_SKIN_SHUFREP_PLAYLIST_NOT_SET_NORM);
+ playlist->setPixmapDown( _WA_SKIN_SHUFREP_PLAYLIST_NOT_SET_PRES);
+
+ playlist->setPixmapUpSelected(_WA_SKIN_SHUFREP_PLAYLIST_SET_NORM);
+ playlist->setPixmapDownSelected(_WA_SKIN_SHUFREP_PLAYLIST_SET_PRES);
+
+ waClutterbar = new WaClutterbar();
+}
+
+void WaSkin::createHighLevelElements()
+{
+ // Two most top-level elements
+ main = new WaMain();
+ waTitleBar = new WaTitleBar();
+
+ guiSpectrumAnalyser = new GuiSpectrumAnalyser();
+
+ waJumpSlider = new WaJumpSlider();
+ waJumpSlider->buildGui();
+
+ waVolumeSlider = new WaVolumeSlider();
+ waVolumeSlider->buildGui();
+
+ connect(waVolumeSlider, SIGNAL(volumeSetValue(int)),
+ this, SLOT(volumeSetValue(int)));
+ connect(waVolumeSlider, SIGNAL(sliderPressed()),
+ this, SLOT(volumeSliderPressed()));
+ connect(waVolumeSlider, SIGNAL(sliderReleased()),
+ this, SLOT(volumeSliderReleased()));
+
+ waBalanceSlider = new WaBalanceSlider();
+ waBalanceSlider->buildGui();
+
+ connect(waBalanceSlider, SIGNAL(balanceSetValue(int)),
+ this, SLOT(balanceSetValue(int)));
+ connect(waBalanceSlider, SIGNAL(sliderPressed()),
+ this, SLOT(balanceSliderPressed()));
+ connect(waBalanceSlider, SIGNAL(sliderReleased()),
+ this, SLOT(balanceSliderReleased()));
+
+ waDigit = new WaDigit();
+ connect(waDigit, SIGNAL(digitsClicked()), this, SLOT(digitsClicked()));
+
+ waBPS = new WaLabel(_WA_MAPPING_BPS);
+ waFreq = new WaLabel(_WA_MAPPING_FREQ);
+
+ waInfo = new WaInfo();
+
+ waStatus = new WaStatus();
+
+ waStereo = new WaIndicator(_WA_MAPPING_MONOSTER_STEREO, _WA_SKIN_MONOSTER_STEREO_TRUE, _WA_SKIN_MONOSTER_STEREO_FALSE);
+ waMono = new WaIndicator(_WA_MAPPING_MONOSTER_MONO, _WA_SKIN_MONOSTER_MONO_TRUE, _WA_SKIN_MONOSTER_MONO_FALSE);
+
+ connect(waJumpSlider, SIGNAL(jump(int)), this, SLOT(jump(int)));
+ connect(waJumpSlider, SIGNAL(sliderPressed()),
+ this, SLOT(jumpSliderPressed()));
+ connect(waJumpSlider, SIGNAL(sliderReleased()),
+ this, SLOT(jumpSliderReleased()));
+ connect(waJumpSlider, SIGNAL(valueChanged(int)),
+ this, SLOT(jumpValueChanged(int)));
+}
+
+
+void WaSkin::setChannels(int val)
+{
+ if (val <= 0) {
+ waStereo->setState(false);
+ waMono->setState(false);
+ } else if (val == 1) {
+ waStereo->setState(false);
+ waMono->setState(true);
+ } else {
+ waStereo->setState(true);
+ waMono->setState(false);
+ }
+}
+
+void WaSkin::shade() {
+ waSkinModel->setSkinModel(WA_MODEL_WINDOWSHADE);
+
+ setMinimumSize(sizeHint());
+ setMask(*windowRegion->mainWindowShadeMask());
+
+ title_shaded = true;
+}
+
+void WaSkin::unshade() {
+ waSkinModel->setSkinModel(WA_MODEL_NORMAL);
+
+ setMinimumSize(sizeHint());
+ setMask(*windowRegion->mainWindowMask());
+
+ title_shaded = false;
+}
+
+void WaSkin::focusOutEvent( QFocusEvent * ) {
+ menu->setPixmapUp(_WA_SKIN_TITLE_MENU_INACTIVE);
+ menu->update();
+
+ minimize->setPixmapUp(_WA_SKIN_TITLE_MIN_INACTIVE);
+ minimize->update();
+
+ titleshade->setPixmapUp(_WA_SKIN_TITLE_SHADE_INACTIVE);
+ titleshade->update();
+
+ close->setPixmapUp(_WA_SKIN_TITLE_CLOSE_INACTIVE);
+ close->update();
+
+ waTitleBar->setState(false);
+}
+
+void WaSkin::focusInEvent( QFocusEvent * ) {
+ menu->setPixmapUp(_WA_SKIN_TITLE_MENU_NORM);
+ menu->update();
+
+ minimize->setPixmapUp(_WA_SKIN_TITLE_MIN_NORM);
+ minimize->update();
+
+ titleshade->setPixmapUp(_WA_SKIN_TITLE_SHADE_NORM);
+ titleshade->update();
+
+ close->setPixmapUp(_WA_SKIN_TITLE_CLOSE_NORM);
+ close->update();
+
+ waTitleBar->setState(true);
+}
+
+void WaSkin::wheelEvent(QWheelEvent *e) {
+ // Get the current volume
+ int newVolume = napp->player()->volume();
+
+ // Wheel events return needlessly large "deltas", normalize it a bit
+ newVolume += e->delta() / 24;
+ napp->player()->setVolume(newVolume);
+}
+
+void WaSkin::repeatClickedEvent(bool)
+{
+ updateLoopStyle();
+}
+
+void WaSkin::shuffleClickedEvent(bool)
+{
+ updateLoopStyle();
+}
+
+void WaSkin::updateLoopStyle() {
+ if (shuffle->toggled()) {
+ napp->player()->loop(Player::Random);
+ }
+ else {
+ int loopVal = repeat->toggled() ? Player::Playlist : Player::None;
+ napp->player()->loop(loopVal);
+ }
+}
+
+void WaSkin::playlistClickedEvent(bool)
+{
+ napp->playlist()->toggleList();
+}
+
+void WaSkin::eqClickedEvent()
+{
+ napp->equalizerView();
+}
+
+void WaSkin::jump(int val)
+{
+ if (napp->player()->isStopped()) {
+ waJumpSlider->setJumpValue(0);
+ } else {
+ napp->player()->skipTo((int) (val * 1000));
+ }
+}
+
+void WaSkin::jumpSliderPressed()
+{
+ mJumpPressed = true;
+ jumpValueChanged(waJumpSlider->jumpValue());
+}
+
+
+void WaSkin::jumpSliderReleased()
+{
+ mJumpPressed = false;
+ waInfo->setText(getTitleString());
+}
+
+void WaSkin::jumpValueChanged(int val)
+{
+ if (mJumpPressed && !napp->player()->isStopped()) {
+ QString timeStr = i18n("Seek to: %1/%2 (%3%)").
+ arg(getTimeString(val * 1000)).
+ arg(getTimeString(napp->player()->getLength())).
+ arg((val * 1000 * 100) / napp->player()->getLength());
+ waInfo->setText(timeStr);
+ }
+}
+
+QString WaSkin::getTitleString() {
+ int length;
+ QString title = "";
+
+ if (!napp->playlist()->current()) {
+ title = "Noatun ";
+ title += QString::number(NOATUN_MAJOR) + ".";
+ title += QString::number(NOATUN_MINOR) + ".";
+ title += QString::number(NOATUN_PATCHLEVEL);
+ }
+ else {
+ length = napp->playlist()->current().length();
+
+ title = napp->playlist()->current().title();
+
+ if (length >= 0)
+ title += " (" + getTimeString(length) + ")";
+
+ if (title.length() > 30) {
+ // It's scrolling; provide the nice, friendly seperator.
+ title += " *** ";
+ }
+ }
+
+ return title;
+}
+
+QString WaSkin::getTimeString(int milliseconds, bool truncate) {
+ int seconds = abs(milliseconds / 1000);
+ QString ret = "";
+
+ // Do we need to convert to hours:minutes instead of minutes:seconds?
+ if (truncate && (abs(seconds) >= (100 * 60)))
+ seconds /= 60;
+
+ // Print the optional minus sign, hours/minutes, a colon, and then minutes/seconds.
+ ret.sprintf("%s%.2d:%.2d", ((milliseconds < 0) ? "-" : ""), seconds / 60, seconds % 60);
+
+ return ret;
+}
+
+void WaSkin::menuEvent() {
+ NoatunStdAction::ContextMenu::showContextMenu(mapToGlobal(QPoint(0, 14)));
+}
+
+void WaSkin::minimizeEvent() {
+ showMinimized();
+}
+
+void WaSkin::shadeEvent()
+{
+ if (!title_shaded)
+ shade();
+ else
+ unshade();
+}
+
+void WaSkin::doUnload() {
+ unload();
+}
+
+void WaSkin::doClose() {
+ QTimer::singleShot(0, this, SLOT(doUnload()));
+}
+
+void WaSkin::dragEnterEvent(QDragEnterEvent * event)
+{
+ // accept uri drops only
+ event->accept(KURLDrag::canDecode(event));
+}
+
+void WaSkin::dropEvent(QDropEvent * event)
+{
+ KURL::List uri;
+ if (KURLDrag::decode(event, uri))
+ {
+ for (KURL::List::Iterator i = uri.begin(); i != uri.end(); ++i)
+ napp->player()->openFile(*i, false);
+ }
+}
+
+void WaSkin::balanceSliderPressed()
+{
+ mBalancePressed = true;
+ balanceSetValue(0);
+}
+
+void WaSkin::balanceSliderReleased()
+{
+
+ mBalancePressed = false;
+ waBalanceSlider->setBalanceValue(0);
+
+ waInfo->setText(getTitleString());
+}
+
+void WaSkin::balanceSetValue(int val)
+{
+ if (val == 0) {
+ waInfo->setText(i18n("Balance: Center"));
+ }
+ else if (val < 0) {
+ waInfo->setText(i18n("Balance: %1% Left").arg(-val));
+ } else {
+ waInfo->setText(i18n("Balance: %1% Right").arg(val));
+ }
+}
+
+void WaSkin::playCurrentEvent()
+{
+ if (napp->player()->isPaused())
+ napp->player()->playpause();
+ else
+ napp->player()->playCurrent();
+}
+
+void WaSkin::playPauseEvent()
+{
+ if (!napp->player()->isStopped())
+ napp->player()->playpause();
+}
+
+
+void WaSkin::loopChange(int loopType)
+{
+ shuffle->setToggled(loopType == Player::Random);
+
+ if (loopType != Player::Random)
+ repeat->setToggled(loopType != Player::None);
+}
+
+void WaSkin::playlistShown()
+{
+ playlist->setToggled(true);
+}
+
+void WaSkin::playlistHidden()
+{
+ playlist->setToggled(false);
+}
+
+void WaSkin::newSong()
+{
+ if (napp->player()->getLength() == -1)
+ waJumpSlider->hide();
+ else
+ waJumpSlider->show();
+
+ mJumpPressed = false;
+ waJumpSlider->cancelDrag();
+
+ timetick();
+}
+
+void WaSkin::timetick()
+{
+ int mLength;
+
+ if (!mVolumePressed && !mBalancePressed && !mJumpPressed)
+ waInfo->setText(getTitleString());
+
+ if (!napp->player()->current())
+ return;
+
+ mLength = (int) napp->player()->getLength() / 1000;
+ if (mLength < 0)
+ mLength = 0;
+
+ waJumpSlider->setJumpRange(mLength);
+
+ digitsClicked();
+
+ int time = 0;
+ if (napp->player()->current())
+ time = (int) napp->player()->getTime() / 1000;
+
+ if (!mJumpPressed)
+ waJumpSlider->setJumpValue(time);
+
+ waVolumeSlider->setVolumeValue(napp->player()->volume());
+}
+
+void WaSkin::digitsClicked() {
+ if (!waDigit->timeReversed() || (napp->player()->getLength() == -1)) {
+ // Setting truncate=true means we want setTime to return
+ // no more than 6 digits (-xx:yy)
+
+ if (napp->player()->getTime() != -1)
+ waDigit->setTime(getTimeString(napp->player()->getTime(), true));
+ else
+ waDigit->setTime(getTimeString(0, true));
+ }
+ else {
+ int rem_time = napp->player()->getTime() - napp->player()->getLength();
+
+ // Setting truncate=true means we want setTime to return
+ // no more than 6 digits (-xx:yy)
+ waDigit->setTime(getTimeString(rem_time, true));
+ }
+}
+
+void WaSkin::volumeSliderPressed()
+{
+ mVolumePressed = true;
+ volumeSetValue(napp->player()->volume());
+}
+
+void WaSkin::volumeSliderReleased()
+{
+ mVolumePressed = false;
+ waInfo->setText(getTitleString());
+}
+
+void WaSkin::volumeSetValue(int val)
+{
+ if (mVolumePressed)
+ waInfo->setText(i18n("Volume: %1%").arg(val));
+
+ napp->player()->setVolume(val);
+}
+
+void WaSkin::slotPlaying()
+{
+ waStatus->setStatus(STATUS_PLAYING);
+
+ if (!napp->playlist()->current()) {
+ return;
+ }
+
+ fileInfo info(napp->playlist()->current());
+
+ if (!info.bps())
+ waBPS->setText("");
+ else
+ waBPS->setText(QString::number(info.bps()));
+
+ if (!info.KHz())
+ waFreq->setText("");
+ else
+ waFreq->setText(QString::number(info.KHz() / 1000));
+
+ setChannels(info.channelCount());
+ guiSpectrumAnalyser->resumeVisualization();
+
+ if (napp->player()->getLength() == -1)
+ waJumpSlider->hide();
+ else
+ waJumpSlider->show();
+
+ timetick();
+}
+
+void WaSkin::slotStopped()
+{
+ waStatus->setStatus(STATUS_STOPPED);
+
+ waDigit->setTime("");
+
+ waBPS->setText("");
+ waFreq->setText("");
+ setChannels(0);
+
+ waJumpSlider->setJumpValue(0);
+ // -1 == disable jump bar
+ waJumpSlider->setJumpRange(-1);
+
+
+ mJumpPressed = false;
+ waJumpSlider->cancelDrag();
+
+ waJumpSlider->hide();
+
+ guiSpectrumAnalyser->pauseVisualization();
+}
+
+void WaSkin::slotPaused()
+{
+ waStatus->setStatus(STATUS_PAUSED);
+}
+
+void WaSkin::keyPressEvent(QKeyEvent *e) {
+ switch(e->key()) {
+ case Key_Up:
+ napp->player()->setVolume(napp->player()->volume() + 5);
+ break;
+ case Key_Down:
+ napp->player()->setVolume(napp->player()->volume() - 5);
+ break;
+ case Key_Left:
+ if (napp->player()->current())
+ napp->player()->skipTo(napp->player()->getTime() - 5000);
+
+ break;
+ case Key_Right:
+ if (napp->player()->current())
+ napp->player()->skipTo(napp->player()->getTime() + 5000);
+
+ break;
+
+ case Key_Z:
+ napp->player()->back();
+ break;
+
+ case Key_X:
+ if (napp->player()->isPaused())
+ napp->player()->playpause();
+ else
+ napp->player()->playCurrent();
+
+ break;
+
+ case Key_C:
+ if (!napp->player()->isStopped())
+ napp->player()->playpause();
+
+ break;
+
+ case Key_V:
+ napp->player()->stop();
+ break;
+
+ case Key_B:
+ napp->player()->forward();
+ break;
+
+ case Key_R:
+ repeat->setToggled(!repeat->toggled());
+ updateLoopStyle();
+ break;
+
+ case Key_S:
+ shuffle->setToggled(!shuffle->toggled());
+ updateLoopStyle();
+ break;
+ }
+}
+
+QString WaSkin::defaultSkin() {
+ return "Winamp";
+}
+
+#include "waSkin.moc"
diff --git a/noatun/modules/winskin/waSkin.h b/noatun/modules/winskin/waSkin.h
new file mode 100644
index 00000000..7a2db9e1
--- /dev/null
+++ b/noatun/modules/winskin/waSkin.h
@@ -0,0 +1,177 @@
+/*
+ Winamp Skin
+ Copyright (C) 1999 Martin Vogt
+ Copyright (C) 2001 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __WASKIN_H
+#define __WASKIN_H
+
+#include <noatun/plugin.h>
+#include <noatun/app.h>
+#include <noatun/player.h>
+#include <qwidget.h>
+
+// Use forward declarations so we compile in a finite time
+class WaSkinModel;
+class WaSkinManager;
+
+class WaButton;
+class WaDigit;
+class WaLabel;
+class WaInfo;
+class WaMain;
+class WaIndicator;
+class WaTitleBar;
+class WaClutterbar;
+class WaStatus;
+
+class WaJumpSlider;
+class WaVolumeSlider;
+class WaBalanceSlider;
+
+class GuiSpectrumAnalyser;
+
+class WaSkin;
+extern WaSkin *_waskin_instance;
+
+class WaSkin : public QWidget, public UserInterface {
+ Q_OBJECT
+ NOATUNPLUGIND
+ WaSkinModel *waSkinModel;
+ WaSkinManager *waSkinManager;
+
+ public:
+ WaSkin();
+ ~WaSkin();
+
+ int getSkinId();
+
+ void loadSkin(QString skinDir);
+ void setChannels(int val);
+
+ QSize sizeHint() const;
+
+ static QString defaultSkin();
+ static WaSkin *instance() { return _waskin_instance; }
+
+ public slots:
+ void repeatClickedEvent(bool);
+ void shuffleClickedEvent(bool);
+ void playlistClickedEvent(bool);
+ void eqClickedEvent();
+
+ // seek bar
+ void jump(int second);
+ void jumpSliderPressed();
+ void jumpSliderReleased();
+ void jumpValueChanged(int);
+
+ void menuEvent();
+ void minimizeEvent();
+ void shadeEvent();
+ void doUnload();
+ void doClose();
+
+ // balance
+ void balanceSliderPressed();
+ void balanceSetValue(int val);
+ void balanceSliderReleased();
+
+ // volume
+ void volumeSliderPressed();
+ void volumeSetValue(int val);
+ void volumeSliderReleased();
+
+ void playCurrentEvent();
+ void playPauseEvent();
+
+ void loopChange(int loopType);
+ void playlistShown();
+ void playlistHidden();
+ void newSong();
+
+ void timetick();
+ void digitsClicked();
+
+ void slotPlaying();
+ void slotStopped();
+ void slotPaused();
+
+ WaSkinModel *skinModel() { return waSkinModel; }
+ WaSkinManager *skinManager() { return waSkinManager; }
+ WaInfo *skinInfo() { return waInfo; }
+
+ protected:
+ void updateLoopStyle();
+
+ void createButtons();
+ void createHighLevelElements();
+
+ void keyPressEvent(QKeyEvent *);
+
+ void shade();
+ void unshade();
+
+ QString getTitleString();
+ QString getTimeString(int milliseconds, bool truncate = false);
+
+ void focusOutEvent ( QFocusEvent * );
+ void focusInEvent ( QFocusEvent * );
+
+ void dragEnterEvent(QDragEnterEvent * event);
+ void dropEvent(QDropEvent * event);
+
+ void wheelEvent(QWheelEvent *e);
+
+ WaButton *prev;
+ WaButton *play;
+ WaButton *pause;
+ WaButton *stop;
+ WaButton *next;
+ WaButton *eject;
+ WaButton *shuffle;
+ WaButton *repeat;
+ WaButton *playlist;
+ WaButton *eq;
+
+ WaButton *menu;
+ WaButton *minimize;
+ WaButton *titleshade;
+ WaButton *close;
+
+ WaJumpSlider *waJumpSlider;
+ WaVolumeSlider *waVolumeSlider;
+ WaBalanceSlider *waBalanceSlider;
+ WaDigit *waDigit;
+
+ WaLabel *waBPS;
+ WaLabel *waFreq;
+
+ WaInfo *waInfo;
+ WaStatus *waStatus;
+ WaIndicator *waStereo;
+ WaIndicator *waMono;
+
+ WaMain *main;
+ WaTitleBar *waTitleBar;
+ WaClutterbar *waClutterbar;
+
+ GuiSpectrumAnalyser *guiSpectrumAnalyser;
+
+ bool title_shaded;
+
+ bool mJumpPressed;
+ bool mBalancePressed;
+ bool mVolumePressed;
+};
+#endif
diff --git a/noatun/modules/winskin/waSkinManager.cpp b/noatun/modules/winskin/waSkinManager.cpp
new file mode 100644
index 00000000..5cde117b
--- /dev/null
+++ b/noatun/modules/winskin/waSkinManager.cpp
@@ -0,0 +1,127 @@
+#include <kglobal.h>
+#include <qfileinfo.h>
+#include <qregexp.h>
+#include <kstandarddirs.h>
+#include <qdir.h>
+#include <kdebug.h>
+#include <kmimetype.h>
+#include <kio/job.h>
+#include <kurl.h>
+#include <kio/netaccess.h>
+#include <kzip.h>
+
+#include "waSkinManager.h"
+#include "waSkinModel.h"
+
+WaSkinManager::WaSkinManager() : DCOPObject("WaSkinManager") {
+}
+
+WaSkinManager::~WaSkinManager() {
+}
+
+QStringList WaSkinManager::availableSkins() {
+ QStringList skinDirs = KGlobal::dirs()->findDirs("data", "noatun/skins/winamp");
+ QStringList skin_list;
+
+ // This loop adds them all to our skin list
+ for(unsigned int x = 0;x < skinDirs.count();x++) {
+ QDir skinQDir(skinDirs[x]);
+
+ // We only want directories, although there shouldn't be anything else
+ skinQDir.setFilter( QDir::Dirs );
+ // I guess name is as good as any
+ skinQDir.setSorting( QDir::Name );
+
+ for (unsigned int y = 0;y < skinQDir.count();y++) {
+ QStringList skins = skinQDir.entryList(QDir::Dirs, QDir::Name);
+
+ // We really don't care for '.' and '..'
+ if (skinQDir[y][0] != (char)'.') {
+ // Add ourselves to the list, using our directory name
+ skin_list += skinQDir[y];
+ }
+ }
+ }
+
+ return skin_list;
+}
+
+QString WaSkinManager::currentSkin() {
+ return mCurrentSkin;
+}
+
+QString WaSkinManager::defaultSkin() {
+ return "Winamp";
+}
+
+bool WaSkinManager::loadSkin(QString skinName) {
+ QStringList skins = KGlobal::dirs()->findDirs("data", "noatun/skins/winamp/" + skinName);
+
+ if (!skins.count())
+ mCurrentSkin = defaultSkin();
+ else
+ mCurrentSkin = skinName;
+
+ return _waskinmodel_instance->load(skins[0]);
+}
+
+bool WaSkinManager::installSkin(QString _url) {
+ QString location = KGlobal::dirs()->saveLocation("data", "noatun/skins/winamp");
+ KURL url(_url);
+ QString mimetype = KMimeType::findByURL(_url)->name();
+
+ if (mimetype == "inode/directory")
+ {
+ KIO::Job *job = KIO::copy(url, location, !url.isLocalFile());
+ connect(job, SIGNAL(result(KIO::Job *)), this, SIGNAL(updateSkinList()));
+ return true;
+ }
+ else if ((mimetype == "interface/x-winamp-skin") || (mimetype == "application/x-zip"))
+ {
+ if (!url.isLocalFile())
+ return false;
+
+ QString base_path;
+ base_path = location + "/" + QFileInfo(url.path()).baseName().replace(QRegExp("_"), " ");
+ KIO::Job *job = KIO::copy("zip:" + url.path(), base_path);
+ connect(job, SIGNAL(result(KIO::Job *)), this, SIGNAL(updateSkinList()));
+
+ return true;
+ }
+
+ return false;
+}
+
+bool WaSkinManager::removeSkin(QString skinName) {
+ if (!skinRemovable(skinName))
+ return false;
+
+ QStringList skins = KGlobal::dirs()->findDirs("data", "noatun/skins/winamp/" + skinName);
+
+ KIO::Job *job = KIO::del(KURL(skins[0]), false, false);
+ connect(job, SIGNAL(result(KIO::Job *)), this, SIGNAL(updateSkinList()));
+
+ return true;
+}
+
+bool WaSkinManager::skinRemovable(QString skinName) {
+ QStringList skins = KGlobal::dirs()->findDirs("data", "noatun/skins/winamp/" + skinName);
+
+ if (!skins.count())
+ return false;
+
+ QFileInfo info(skins[0]);
+ return info.isWritable();
+}
+
+QStringList WaSkinManager::skinMimeTypes() {
+ QStringList temp;
+
+ temp.append("interface/x-winamp-skin");
+ temp.append("application/x-zip");
+ temp.append("inode/directory");
+
+ return temp;
+}
+
+#include "waSkinManager.moc"
diff --git a/noatun/modules/winskin/waSkinManager.h b/noatun/modules/winskin/waSkinManager.h
new file mode 100644
index 00000000..17b21daf
--- /dev/null
+++ b/noatun/modules/winskin/waSkinManager.h
@@ -0,0 +1,39 @@
+#ifndef _WASKINMANAGER_H
+#define _WASKINMANAGER_H
+
+#include <dcopobject.h>
+#include <qobject.h>
+#include <qstringlist.h>
+#include <qstring.h>
+#include <qmap.h>
+
+class WaSkinManager : public QObject, public DCOPObject {
+Q_OBJECT
+K_DCOP
+
+public:
+ WaSkinManager();
+ ~WaSkinManager();
+
+k_dcop:
+ QStringList availableSkins();
+ QString currentSkin();
+ bool loadSkin(QString skinName);
+
+ QString defaultSkin();
+
+ bool installSkin(QString url);
+
+ bool skinRemovable(QString skinName);
+ bool removeSkin(QString skinName);
+
+ QStringList skinMimeTypes();
+
+signals:
+ void updateSkinList();
+
+private:
+ QString mCurrentSkin;
+};
+
+#endif
diff --git a/noatun/modules/winskin/waSkinMapping.h b/noatun/modules/winskin/waSkinMapping.h
new file mode 100644
index 00000000..a3dfc9c5
--- /dev/null
+++ b/noatun/modules/winskin/waSkinMapping.h
@@ -0,0 +1,148 @@
+/*
+ mapping from file and id to pixmap, and global player map
+ Copyright (C) 1999 Martin Vogt
+ Copyright (C) 2002 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __WASKINMAPPING_H
+#define __WASKINMAPPING_H
+
+
+/**
+ This file is not a header file in the normal sense.
+ It define _directly_ variables, which are used in the
+ WaSkinLoader class.
+ Its a bit black voodoo magic.
+*/
+
+/**
+ The Skin file format was downloaded from the web.
+ http://www.algonet.et.se/~daniel7
+
+ Author: unknown
+*/
+
+#include "skinMap.h"
+#include "waSkins.h"
+
+static const SkinMap normalMapToGui[] = {
+ {0, 0, 275, 116}, // _WA_MAPPING_MAIN
+ {16, 88, 23, 18}, // _WA_MAPPING_CBUTTONS_PREV
+ {16 + 1 * 23, 88, 23, 18}, // _WA_MAPPING_CBUTTONS_PLAY
+ {16 + 2 * 23, 88, 23, 18}, // _WA_MAPPING_CBUTTONS_PAUSE
+ {16 + 3 * 23, 88, 23, 18}, // _WA_MAPPING_CBUTTONS_STOP
+ {16 + 4 * 23, 88, 22, 18}, // _WA_MAPPING_CBUTTONS_NEXT
+ {136, 89, 22, 16}, // _WA_MAPPING_CBUTTONS_EJECT
+ {239, 41, 29, 12}, // _WA_MAPPING_MONOSTER_STEREO
+ {212, 41, 27, 12}, // _WA_MAPPING_MONOSTER_MONO
+ {210, 89, 28, 15}, // _WA_MAPPING_REPEAT
+ {164, 89, 46, 15}, // _WA_MAPPING_SHUFFLE
+ {242, 58, 23, 12}, // _WA_MAPPING_PLAYLIST
+ {219, 58, 23, 12}, // _WA_MAPPING_EQ
+ {107, 57, 68, 13}, // _WA_MAPPING_VOLUME_BAR
+ {0, 0, 14, 11}, // _WA_MAPPING_VOLUME_SLIDER
+ {177, 57, 38, 13}, // _WA_MAPPING_BALANCE_BAR
+ {0, 0, 14, 11}, // _WA_MAPPING_BALANCE_SLIDER
+ {24, 28, 11, 9}, // _WA_MAPPING_PLAYPAUS
+ {16, 72, 248, 10}, // _WA_MAPPING_POS_BAR
+ {0, 0, 29, 10}, // _WA_MAPPING_POS_BAR_SLIDER
+ {40, 26, 63, 13}, // _WA_MAPPING_DIGITS
+ {40, 32, 5, 1}, // _WA_MAPPING_MINUS
+ {48, 26, 9, 13}, // _WA_MAPPING_DIGIT_1
+ {60, 26, 9, 13}, // _WA_MAPPING_DIGIT_2
+ {78, 26, 9, 13}, // _WA_MAPPING_DIGIT_3
+ {90, 26, 9, 13}, // _WA_MAPPING_DIGIT_4
+ {24, 43, 75, 16}, // _WA_MAPPING_ANALYSER
+ {111, 43, 15, 6}, // _WA_MAPPING_BPS
+ {156, 43, 10, 6}, // _WA_MAPPING_FREQ
+ {112, 27, 152, 6}, // _WA_MAPPING_INFO
+ {0, 0, 274, 14}, // _WA_MAPPING_TITLE
+ {6, 3, 9, 9}, // _WA_MAPPING_TITLE_MENU
+ {244, 3, 9, 9}, // _WA_MAPPING_TITLE_MIN
+ {254, 3, 9, 9}, // _WA_MAPPING_TITLE_SHADE
+ {264, 3, 9, 9}, // _WA_MAPPING_TITLE_CLOSE
+ {10, 22, 8, 43} // _WA_MAPPING_CLUTTERBAR
+};
+
+
+static const SkinDesc normalMapFromFile[] = {
+ {_WA_FILE_MAIN, 0, 0, 275, 116}, // _WA_SKIN_MAIN
+ {_WA_FILE_CBUTTONS, 0, 0, 23, 18}, // _WA_SKIN_CBUTTONS_PREV_NORM
+ {_WA_FILE_CBUTTONS, 0, 18, 23, 18}, // _WA_SKIN_CBUTTONS_PREV_PRES
+ {_WA_FILE_CBUTTONS, 23, 0, 23, 18}, //_WA_SKIN_CBUTTONS_PLAY_NORM,
+ {_WA_FILE_CBUTTONS, 23, 18, 23, 18}, // _WA_SKIN_CBUTTONS_PLAY_PRES
+ {_WA_FILE_CBUTTONS, 46, 0, 23, 18}, // _WA_SKIN_CBUTTONS_PAUSE_NORM
+ {_WA_FILE_CBUTTONS, 46, 18, 23, 18}, // _WA_SKIN_CBUTTONS_PAUSE_PRES
+ {_WA_FILE_CBUTTONS, 69, 0, 23, 18}, // _WA_SKIN_CBUTTONS_STOP_NORM
+ {_WA_FILE_CBUTTONS, 69, 18, 23, 18}, // _WA_SKIN_CBUTTONS_STOP_PRES
+ {_WA_FILE_CBUTTONS, 92, 0, 22, 18}, // _WA_SKIN_CBUTTONS_NEXT_NORM
+ {_WA_FILE_CBUTTONS, 92, 18, 22, 18}, // _WA_SKIN_CBUTTONS_NEXT_PRES
+ {_WA_FILE_CBUTTONS, 114, 0, 22, 16}, // _WA_SKIN_CBUTTONS_EJECT_NORM
+ {_WA_FILE_CBUTTONS, 114, 16, 22, 16}, // _WA_SKIN_CBUTTONS_EJECT_PRESS
+ {_WA_FILE_MONOSTER, 0, 0, 29, 12}, // _WA_SKIN_MONOSTER_STEREO_TRUE
+ {_WA_FILE_MONOSTER, 0, 12, 29, 12}, // _WA_SKIN_MONOSTER_STEREO_FALSE
+ {_WA_FILE_MONOSTER, 29, 0, 27, 12}, // _WA_SKIN_MONOSTER_MONO_TRUE
+ {_WA_FILE_MONOSTER, 29, 12, 27, 12}, // _WA_SKIN_MONOSTER_MONO_FALSE
+ {_WA_FILE_NUMBERS, 0, 0, 99, 13}, // _WA_SKIN_NUMBERS
+ {_WA_FILE_NUMBERS, 20, 6, 5, 1}, // _WA_SKIN_NUMBERS_MINUS
+ {_WA_FILE_NUMBERS, 90, 6, 5, 1}, // _WA_SKIN_NUMBERS_BLANK
+ {_WA_FILE_SHUFREP, 0, 0, 28, 15}, // _WA_SKIN_SHUFREP_REPEAT_NOT_SET_NORM
+ {_WA_FILE_SHUFREP, 0, 15, 28, 15}, // _WA_SKIN_SHUFREP_REPEAT_NOT_SET_PRES
+ {_WA_FILE_SHUFREP, 0, 30, 28, 15}, // _WA_SKIN_SHUFREP_REPEAT_SET_NORM
+ {_WA_FILE_SHUFREP, 0, 45, 28, 15}, // _WA_SKIN_SHUFREP_REPEAT_SET_PRES
+ {_WA_FILE_SHUFREP, 28, 0, 47, 15}, // _WA_SKIN_SHUFREP_SHUFFLE_NOT_SET_NORM
+ {_WA_FILE_SHUFREP, 28, 15, 47, 15}, // _WA_SKIN_SHUFREP_SHUFFLE_NOT_SET_PRES
+ {_WA_FILE_SHUFREP, 28, 30, 47, 15}, // _WA_SKIN_SHUFREP_SHUFFLE_SET_NORM
+ {_WA_FILE_SHUFREP, 28, 45, 47, 15}, // _WA_SKIN_SHUFREP_SHUFFLE_SET_PRES
+ {_WA_FILE_SHUFREP, 23, 61, 23, 12}, // _WA_SKIN_SHUFREP_PLAYLIST_NOT_SET_NORM
+ {_WA_FILE_SHUFREP, 69, 61, 23, 12}, // _WA_SKIN_SHUFREP_PLAYLIST_NOT_SET_PRES
+ {_WA_FILE_SHUFREP, 23, 73, 23, 12}, // _WA_SKIN_SHUFREP_PLAYLIST_SET_NORM
+ {_WA_FILE_SHUFREP, 73, 23, 12}, // _WA_SKIN_SHUFREP_PLAYLIST_SET_PRES
+ {_WA_FILE_SHUFREP, 0, 61, 23, 12}, // _WA_SKIN_SHUFREP_EQ_NOT_SET_NORM
+ {_WA_FILE_SHUFREP, 46, 61, 23, 12}, // _WA_SKIN_SHUFREP_EQ_NOT_SET_PRES
+ {_WA_FILE_SHUFREP, 0, 73, 23, 12}, // _WA_SKIN_SHUFREP_EQ_SET_NORM
+ {_WA_FILE_SHUFREP, 46, 73, 23, 12}, // _WA_SKIN_SHUFREP_EQ_SET_PRES
+ {_WA_FILE_TEXT, 0, 0, 155, 18}, // _WA_SKIN_TEXT
+ {_WA_FILE_VOLUME, 0, 0, 68, 421}, // _WA_SKIN_VOLUME_BAR_ALL_BARS
+ {_WA_FILE_VOLUME, 0, 0, 68, 13}, // _WA_SKIN_VOLUME_BAR
+ {_WA_FILE_VOLUME, 0, 422, 14, 11}, // _WA_SKIN_VOLUME_SLIDER_NORM
+ {_WA_FILE_VOLUME, 15, 422, 14, 11}, // _WA_SKIN_VOLUME_SLIDER_PRES
+ {_WA_FILE_BALANCE, 9, 0, 38, 421}, // _WA_SKIN_BALANCE_BAR_ALL_BARS
+ {_WA_FILE_BALANCE, 9, 0, 38, 13}, // _WA_SKIN_BALANCE_BAR
+ {_WA_FILE_BALANCE, 0, 422, 14, 11}, // _WA_SKIN_BALANCE_SLIDER_NORM
+ {_WA_FILE_BALANCE, 15, 422, 14, 11}, // _WA_SKIN_BALANCE_SLIDER_PRES
+ {_WA_FILE_POSBAR, 0, 0, 248, 10}, // _WA_SKIN_POS_BAR
+ {_WA_FILE_POSBAR, 278, 0, 29, 10}, // _WA_SKIN_POS_BAR_SLIDER_NORM
+ {_WA_FILE_POSBAR, 248, 0, 29, 10}, // _WA_SKIN_POS_BAR_SLIDER_PRES
+ {_WA_FILE_PLAYPAUS, 1, 0, 8, 9}, // _WA_SKIN_PLAYPAUS_PLAY
+ {_WA_FILE_PLAYPAUS, 9, 0, 9, 9}, // _WA_SKIN_PLAYPAUS_PAUSE
+ {_WA_FILE_PLAYPAUS, 18, 0, 9, 9}, // _WA_SKIN_PLAYPAUS_STOP
+ {_WA_FILE_PLAYPAUS, 27, 0, 2, 9}, // _WA_SKIN_PLAYPAUS_FILLER
+ {_WA_FILE_PLAYPAUS, 36, 0, 3, 9}, // _WA_SKIN_PLAYPAUS_WORK_INDICATOR
+ {_WA_FILE_TITLEBAR, 27, 0, 275, 14}, // _WA_SKIN_TITLE_ACTIVE
+ {_WA_FILE_TITLEBAR, 27, 15, 275, 14}, // _WA_SKIN_TITLE_INACTIVE
+ {_WA_FILE_TITLEBAR, 0, 9, 9, 9}, // _WA_SKIN_TITLE_MENU_PRES
+ {_WA_FILE_TITLEBAR, 0, 0, 9, 9}, // _WA_SKIN_TITLE_MENU_NORM
+ {_WA_FILE_TITLEBAR, 33, 18, 9, 9}, // _WA_SKIN_TITLE_MENU_INACTIVE
+ {_WA_FILE_TITLEBAR, 9, 9, 9, 9}, // _WA_SKIN_TITLE_MIN_PRES
+ {_WA_FILE_TITLEBAR, 9, 0, 9, 9}, // _WA_SKIN_TITLE_MIN_NORM
+ {_WA_FILE_TITLEBAR, 271, 18, 9, 9}, // _WA_SKIN_TITLE_MIN_INACTIVE
+ {_WA_FILE_TITLEBAR, 9, 18, 9, 9}, // _WA_SKIN_TITLE_SHADE_PRES
+ {_WA_FILE_TITLEBAR, 0, 18, 9, 9}, // _WA_SKIN_TITLE_SHADE_NORM
+ {_WA_FILE_TITLEBAR, 281, 18, 9, 9}, // _WA_SKIN_TITLE_SHADE_INACTIVE
+ {_WA_FILE_TITLEBAR, 18, 9, 9, 9}, // _WA_SKIN_TITLE_CLOSE_PRES
+ {_WA_FILE_TITLEBAR, 18, 0, 9, 9}, // _WA_SKIN_TITLE_CLOSE_NORM
+ {_WA_FILE_TITLEBAR, 291, 18, 9, 9}, // _WA_SKIN_TITLE_CLOSE_INACTIVE
+ {_WA_FILE_TITLEBAR, 312, 0, 8, 43} // _WA_SKIN_CLUTTERBAR_DISABLED
+};
+
+#endif
diff --git a/noatun/modules/winskin/waSkinModel.cpp b/noatun/modules/winskin/waSkinModel.cpp
new file mode 100644
index 00000000..ff53b4e6
--- /dev/null
+++ b/noatun/modules/winskin/waSkinModel.cpp
@@ -0,0 +1,458 @@
+/*
+ operations with skinset.
+ Copyright (C) 1999 Martin Vogt
+ Copyright (C) 2001 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include <config.h>
+#include <qdir.h>
+#include <qstringlist.h>
+#include <qbitmap.h>
+#include <kdebug.h>
+#include <kstandarddirs.h>
+
+#include "waSkinMapping.h"
+#include "waShadeMapping.h"
+
+#include "waSkinModel.h"
+#include "waColor.h"
+#include "waRegion.h"
+#include "waSkin.h"
+
+WaSkinModel *_waskinmodel_instance = NULL;
+
+// Our current skin map
+// Can switch between normal and windowshade maps
+const SkinMap *mapToGui;
+const SkinDesc *mapFromFile;
+int digit_width;
+int digit_height;
+
+
+struct WaPixmapEntry {
+ const char *filename;
+ QPixmap *pixmap;
+};
+
+WaPixmapEntry waPixmapEntries[11] = {
+ {"main.bmp", NULL},
+ {"cbuttons.bmp", NULL},
+ {"monoster.bmp", NULL},
+ {"numbers.bmp", NULL},
+ {"shufrep.bmp", NULL},
+ {"text.bmp", NULL},
+ {"volume.bmp", NULL},
+ {"balance.bmp", NULL},
+ {"posbar.bmp", NULL},
+ {"playpaus.bmp", NULL},
+ {"titlebar.bmp", NULL}
+ };
+
+WaSkinModel::WaSkinModel()
+{
+ for (int x = 0;x < 11;x++)
+ waPixmapEntries[x].pixmap = new QPixmap;
+
+ resetSkinModel();
+ _waskinmodel_instance = this;
+}
+
+WaSkinModel::~WaSkinModel()
+{
+ for (int x = 0;x < 11;x++)
+ delete waPixmapEntries[x].pixmap;
+
+ delete windowRegion;
+ delete colorScheme;
+}
+
+bool WaSkinModel::load(QString skinDir)
+{
+ bool success = true;
+
+ QDir dir(skinDir);
+
+ if (findFile(dir, "main.bmp").isEmpty()) {
+ // Ack, our skin dir doesn't exist, fall back to the default
+ dir = QDir(KGlobal::dirs()->findDirs("data", "noatun/skins/winamp/" + WaSkin::defaultSkin())[0]);
+ success = false;
+ }
+
+ for (int x = 0;x < 11;x++) {
+ getPixmap(dir, waPixmapEntries[x].filename,
+ waPixmapEntries[x].pixmap);
+ }
+
+ resetSkinModel();
+
+ loadColors(dir);
+ loadRegion(dir);
+
+ emit(skinChanged());
+
+ return success;
+}
+
+// Does a case-insenstive file search (like DOS/Windows)
+// Filename -must- be lowercase, which is an nice optimization, since
+// this is a private API, and all our filenames are internally lowercase
+// anyway
+QString WaSkinModel::findFile(const QDir &dir, const QString &filename) {
+ QFileInfo fileInfo;
+ QString ret = "";
+
+ QStringList strList = dir.entryList();
+
+ for (QStringList::iterator file = strList.begin(); file != strList.end(); file++) {
+ QFileInfo fileInfo(*file);
+
+ if (fileInfo.isDir())
+ continue;
+
+ if (fileInfo.filePath().lower() == filename)
+ return dir.absPath() + "/" + QString(fileInfo.filePath());
+ }
+
+ return "";
+}
+
+void WaSkinModel::loadColors(const QDir &dir) {
+ QString colorFile = findFile(dir, "viscolor.txt");
+
+ if (colorScheme) {
+ delete colorScheme;
+ }
+
+ colorScheme = new WaColor(colorFile);
+}
+
+void WaSkinModel::loadRegion(const QDir &dir) {
+ QString regionFile = findFile(dir, "region.txt");
+
+ if (windowRegion) {
+ delete windowRegion;
+ windowRegion = 0;
+ }
+
+ windowRegion = new WaRegion(regionFile);
+}
+
+int WaSkinModel::getPixmap(const QDir &dir, QString fname,
+ QPixmap *target)
+{
+ QFileInfo fileInfo;
+ QStringList strList = dir.entryList();
+ QString abspath;
+
+ abspath = findFile(dir, fname);
+
+ if (!abspath.isEmpty()) {
+ target->load(abspath);
+ return true;
+ }
+
+ // now the filename mapping 1.8x -> 2.0
+ if (fname == "volume.bmp")
+ return WaSkinModel::getPixmap(dir, QString("volbar.bmp"), target);
+
+ if (fname == "numbers.bmp")
+ return WaSkinModel::getPixmap(dir, QString("nums_ex.bmp"), target);
+
+ // Even 2.x themes can omit BALANCE, in which case we use VOLUME
+ if (fname == "balance.bmp")
+ return WaSkinModel::getPixmap(dir, QString("volume.bmp"), target);
+
+ return false;
+}
+
+
+QRect WaSkinModel::getGeometry(int id) {
+ if ( (id < 0) || (id >= _WA_SKIN_ENTRIES) ) {
+ kdDebug() << "Array index out of range. WaSkinModel::getGeometry"<<endl;
+ exit(-1);
+ }
+ return QRect(mapFromFile[id].x, mapFromFile[id].y,
+ mapFromFile[id].width, mapFromFile[id].height);
+}
+
+QRect WaSkinModel::getMapGeometry(int id) {
+ if ( (id < 0) || (id >= _WA_MAPPING_ENTRIES) ) {
+ kdDebug() << "Array index out of range. WaSkinModel::getMapGeometry"<<endl;
+ exit(-1);
+ }
+ return QRect(mapToGui[id].x, mapToGui[id].y,
+ mapToGui[id].width, mapToGui[id].height);
+}
+
+void WaSkinModel::bltTo(int id, QPaintDevice *dest, int x, int y) {
+ bitBlt(dest, x, y, waPixmapEntries[mapFromFile[id].fileId].pixmap,
+ mapFromFile[id].x, mapFromFile[id].y,
+ mapFromFile[id].width, mapFromFile[id].height);
+}
+
+void WaSkinModel::bltTo(int id, QPaintDevice *dest, int x, int y, int argument) {
+ if (id == _WA_SKIN_VOLUME_BAR) {
+ QPixmap *pix = waPixmapEntries[_WA_FILE_VOLUME].pixmap;
+
+ int nBar = int((float)argument * 27.0 / 100.0);
+ bitBlt(dest, x, y, pix, 0, 15 * nBar, 68, 13);
+
+ return;
+ }
+
+ if (id == _WA_SKIN_BALANCE_BAR) {
+ QPixmap *pix = waPixmapEntries[_WA_FILE_BALANCE].pixmap;
+
+ argument = abs(argument);
+
+ int nBar = int((float)argument * 27.0 / 100.0);
+ bitBlt(dest, x, y, pix, 9, 15 * nBar, 38, 13);
+
+ return;
+ }
+
+ bltTo(id, dest, x, y);
+}
+
+void WaSkinModel::getDigit(char number, QPaintDevice *dest, int x, int y) {
+ if (number=='-') {
+ bltTo(_WA_SKIN_NUMBERS_MINUS, dest, x, y);
+ return;
+ }
+
+ // empty number ?
+ if (number == ' ') {
+ bltTo(_WA_SKIN_NUMBERS_BLANK, dest, x, y);
+ return;
+ }
+
+ // number
+ QPixmap *pix = waPixmapEntries[mapFromFile[_WA_SKIN_NUMBERS].fileId].pixmap;
+
+ // ordinary number:
+ int index = number - '0';
+ if ((index < 0) || (index > 9))
+ return;
+
+ bitBlt(dest, x, y, pix , (index * digit_width) + mapFromFile[_WA_SKIN_NUMBERS].x, mapFromFile[_WA_SKIN_NUMBERS].y, digit_width, digit_height);
+
+ return;
+}
+
+void WaSkinModel::getText(char text, QPaintDevice * dest, int x, int y) {
+ QPixmap *pix = waPixmapEntries[_WA_FILE_TEXT].pixmap;
+
+ text = deaccent(text);
+
+ if (('A' <= text) && (text <= 'Z')) {
+ bitBlt(dest, x, y,pix,(text-'A')*5,0,5,6);
+ return;
+ }
+ if (('a' <= text) && (text <= 'z')) {
+ bitBlt(dest, x, y,pix,(text-'a')*5,0,5,6);
+ return;
+ }
+ if (('0' <= text) && (text <= '9')) {
+ bitBlt(dest, x, y,pix,(text-'0')*5,6,5,6);
+ return;
+ }
+ if ('"' == text) {
+ bitBlt(dest, x, y,pix,27*5,0,5,6);
+ return;
+ }
+ if ('@' == text) {
+ bitBlt(dest, x, y,pix,28*5,0,5,6);
+ return;
+ }
+
+
+ if ('.' == text) {
+ bitBlt(dest, x, y,pix,11*5,6,5,6);
+ return;
+ }
+ if (':' == text) {
+ bitBlt(dest, x, y,pix,12*5,6,5,6);
+ return;
+ }
+ if (('(' == text) || ('<' == text) || ('{' == text)) {
+ bitBlt(dest, x, y,pix,13*5,6,5,6);
+ return;
+ }
+ if ((')' == text) || ('>' == text) || ('}' == text)) {
+ bitBlt(dest, x, y,pix,14*5,6,5,6);
+ return;
+ }
+ if ('-' == text) {
+ bitBlt(dest, x, y,pix,15*5,6,5,6);
+ return;
+ }
+ if (('`' == text) || ('\'' == text)) {
+ bitBlt(dest, x, y,pix,16*5,6,5,6);
+ return;
+ }
+ if ('!' == text) {
+ bitBlt(dest, x, y,pix,17*5,6,5,6);
+ return;
+ }
+ if ('_' == text) {
+ bitBlt(dest, x, y,pix,18*5,6,5,6);
+ return;
+ }
+ if ('+' == text) {
+ bitBlt(dest, x, y,pix,19*5,6,5,6);
+ return;
+ }
+ if ('\\' == text) {
+ bitBlt(dest, x, y,pix,20*5,6,5,6);
+ return;
+ }
+ if ('/' == text) {
+ bitBlt(dest, x, y,pix,21*5,6,5,6);
+ return;
+ }
+ if ('[' == text) {
+ bitBlt(dest, x, y,pix,22*5,6,5,6);
+ return;
+ }
+ if (']' == text) {
+ bitBlt(dest, x, y,pix,23*5,6,5,6);
+ return;
+ }
+ if ('^' == text) {
+ bitBlt(dest, x, y,pix,24*5,6,5,6);
+ return;
+ }
+ if ('&' == text) {
+ bitBlt(dest, x, y,pix,25*5,6,5,6);
+ return;
+ }
+ if ('%' == text) {
+ bitBlt(dest, x, y,pix,26*5,6,5,6);
+ return;
+ }
+ if (',' == text) {
+ bitBlt(dest, x, y,pix,27*5,6,5,6);
+ return;
+ }
+ if ('=' == text) {
+ bitBlt(dest, x, y,pix,28*5,6,5,6);
+ return;
+ }
+ if ('$' == text) {
+ bitBlt(dest, x, y,pix,29*5,6,5,6);
+ return;
+ }
+ if ('#' == text) {
+ bitBlt(dest, x, y,pix,30*5,6,5,6);
+ return;
+ }
+ if (('' == text) || ('' == text)) {
+ bitBlt(dest, x, y,pix,0*5,12,5,6);
+ return;
+ }
+ if (('' == text) || ('' == text)) {
+ bitBlt(dest, x, y,pix,1*5,12,5,6);
+ return;
+ }
+ if (('' == text) || ('' == text)) {
+ bitBlt(dest, x, y,pix,2*5,12,5,6);
+ return;
+ }
+ if ('?' == text) {
+ bitBlt(dest, x, y,pix,3*5,12,5,6);
+ return;
+ }
+ if ('*' == text) {
+ bitBlt(dest, x, y,pix,4*5,12,5,6);
+ return;
+ }
+ // default back is space char
+ bitBlt(dest, x, y,pix,(10*5),12,5,6);
+}
+
+void WaSkinModel::paintBackgroundTo(int mapping, QPaintDevice *dest, int x, int y)
+{
+ QPixmap *pix = waPixmapEntries[mapFromFile[_WA_SKIN_MAIN].fileId].pixmap;
+ QRect main_rect = getGeometry(_WA_SKIN_MAIN);
+ QRect dest_rect = getMapGeometry(mapping);
+
+ int source_x = main_rect.x() + dest_rect.x() + x;
+ int source_y = main_rect.y() + dest_rect.y() + y;
+
+ int width = dest_rect.width() - x;
+ int height = dest_rect.height() - y;
+
+ bitBlt(dest, x, y, pix, source_x, source_y, width, height);
+}
+
+void WaSkinModel::setSkinModel(skin_models new_model) {
+ if (new_model == WA_MODEL_NORMAL) {
+ mapToGui = normalMapToGui;
+ mapFromFile = normalMapFromFile;
+ digit_width = 9;
+ digit_height = 13;
+ }
+ else if (new_model == WA_MODEL_WINDOWSHADE) {
+ mapToGui = shadeMapToGui;
+ mapFromFile = shadeMapFromFile;
+ digit_width = 5;
+ digit_height = 6;
+ }
+
+ emit(skinChanged());
+}
+
+void WaSkinModel::resetSkinModel() {
+ mapToGui = normalMapToGui;
+ mapFromFile = normalMapFromFile;
+ digit_width = 9;
+ digit_height = 13;
+}
+
+
+QChar WaSkinModel::deaccent(QChar input) {
+ if (QString("").contains(input))
+ return 'A';
+
+ if (QString("").contains(input))
+ return 'E';
+
+ if (QString("").contains(input))
+ return 'I';
+
+ if (QString("").contains(input))
+ return 'O';
+
+ if (QString("").contains(input))
+ return 'U';
+
+ if (input == '')
+ return 'Y';
+
+ if (QString("").contains(input))
+ return 'a';
+
+ if (QString("").contains(input))
+ return 'e';
+
+ if (QString("").contains(input))
+ return 'i';
+
+ if (QString("").contains(input))
+ return 'o';
+
+ if (QString("").contains(input))
+ return 'u';
+
+ return input;
+}
+
+#include "waSkinModel.moc"
diff --git a/noatun/modules/winskin/waSkinModel.h b/noatun/modules/winskin/waSkinModel.h
new file mode 100644
index 00000000..9447f8ae
--- /dev/null
+++ b/noatun/modules/winskin/waSkinModel.h
@@ -0,0 +1,64 @@
+/*
+ Model for winamp skins
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#ifndef __WASKINMODEL_H
+#define __WASKINMODEL_H
+
+#include <qstring.h>
+#include <qobject.h>
+#include <qdir.h>
+
+#include "waSkins.h"
+
+enum skin_models { WA_MODEL_NORMAL, WA_MODEL_WINDOWSHADE };
+
+class QChar;
+class WaSkinModel;
+extern WaSkinModel *_waskinmodel_instance;
+
+class WaSkinModel : public QObject {
+ Q_OBJECT
+
+ public:
+ WaSkinModel();
+ ~WaSkinModel();
+
+ void getDigit(char number, QPaintDevice *dest, int x, int y);
+ void getText(char text, QPaintDevice *dest, int x, int y);
+
+ void bltTo(int id, QPaintDevice *dest, int x, int y);
+ void bltTo(int id, QPaintDevice *dest, int x, int y, int argument);
+
+ void paintBackgroundTo(int mapping, QPaintDevice *dest, int x, int y);
+
+ QRect getGeometry(int id);
+ QRect getMapGeometry(int id);
+
+ bool load(QString skinDir);
+
+ void setSkinModel(skin_models new_model);
+ static WaSkinModel *instance() { return _waskinmodel_instance; }
+
+ private:
+ void resetSkinModel();
+
+ QString findFile(const QDir &dir, const QString &filename);
+ QChar deaccent(QChar input);
+
+ int getPixmap(const QDir &dir, QString fname, QPixmap * target);
+ void loadColors(const QDir &dir);
+ void loadRegion(const QDir &dir);
+
+ signals:
+ void skinChanged();
+};
+#endif
diff --git a/noatun/modules/winskin/waSkins.h b/noatun/modules/winskin/waSkins.h
new file mode 100644
index 00000000..f8d5f623
--- /dev/null
+++ b/noatun/modules/winskin/waSkins.h
@@ -0,0 +1,161 @@
+/*
+ names for the different skins in winamp
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef _WASKINS_H
+#define _WASKINS_H
+
+
+
+/**
+ The Skin file format was downloaded from the web.
+ http://www.algonet.et.se/~daniel7
+
+ Author: unknown
+*/
+
+
+#define _WA_SKIN_MAIN 0
+#define _WA_SKIN_CBUTTONS_PREV_NORM 1
+#define _WA_SKIN_CBUTTONS_PREV_PRES 2
+#define _WA_SKIN_CBUTTONS_PLAY_NORM 3
+#define _WA_SKIN_CBUTTONS_PLAY_PRES 4
+#define _WA_SKIN_CBUTTONS_PAUSE_NORM 5
+#define _WA_SKIN_CBUTTONS_PAUSE_PRES 6
+#define _WA_SKIN_CBUTTONS_STOP_NORM 7
+#define _WA_SKIN_CBUTTONS_STOP_PRES 8
+#define _WA_SKIN_CBUTTONS_NEXT_NORM 9
+#define _WA_SKIN_CBUTTONS_NEXT_PRES 10
+#define _WA_SKIN_CBUTTONS_EJECT_NORM 11
+#define _WA_SKIN_CBUTTONS_EJECT_PRESS 12
+#define _WA_SKIN_MONOSTER_STEREO_TRUE 13
+#define _WA_SKIN_MONOSTER_STEREO_FALSE 14
+#define _WA_SKIN_MONOSTER_MONO_TRUE 15
+#define _WA_SKIN_MONOSTER_MONO_FALSE 16
+
+#define _WA_SKIN_NUMBERS 17
+#define _WA_SKIN_NUMBERS_MINUS 18
+#define _WA_SKIN_NUMBERS_BLANK 19
+
+#define _WA_SKIN_SHUFREP_REPEAT_NOT_SET_NORM 20
+#define _WA_SKIN_SHUFREP_REPEAT_NOT_SET_PRES 21
+#define _WA_SKIN_SHUFREP_REPEAT_SET_NORM 22
+#define _WA_SKIN_SHUFREP_REPEAT_SET_PRES 23
+#define _WA_SKIN_SHUFREP_SHUFFLE_NOT_SET_NORM 24
+#define _WA_SKIN_SHUFREP_SHUFFLE_NOT_SET_PRES 25
+#define _WA_SKIN_SHUFREP_SHUFFLE_SET_NORM 26
+#define _WA_SKIN_SHUFREP_SHUFFLE_SET_PRES 27
+#define _WA_SKIN_SHUFREP_PLAYLIST_NOT_SET_NORM 28
+#define _WA_SKIN_SHUFREP_PLAYLIST_NOT_SET_PRES 29
+#define _WA_SKIN_SHUFREP_PLAYLIST_SET_NORM 30
+#define _WA_SKIN_SHUFREP_PLAYLIST_SET_PRES 31
+#define _WA_SKIN_SHUFREP_EQ_NOT_SET_NORM 32
+#define _WA_SKIN_SHUFREP_EQ_NOT_SET_PRES 33
+#define _WA_SKIN_SHUFREP_EQ_SET_NORM 34
+#define _WA_SKIN_SHUFREP_EQ_SET_PRES 35
+
+#define _WA_SKIN_TEXT 36
+
+#define _WA_SKIN_VOLUME_BAR_ALL_BARS 37
+#define _WA_SKIN_VOLUME_BAR 38
+#define _WA_SKIN_VOLUME_SLIDER_NORM 39
+#define _WA_SKIN_VOLUME_SLIDER_PRES 40
+
+#define _WA_SKIN_BALANCE_BAR_ALL_BARS 41
+#define _WA_SKIN_BALANCE_BAR 42
+#define _WA_SKIN_BALANCE_SLIDER_NORM 43
+#define _WA_SKIN_BALANCE_SLIDER_PRES 44
+
+#define _WA_SKIN_POS_BAR 45
+#define _WA_SKIN_POS_BAR_SLIDER_NORM 46
+#define _WA_SKIN_POS_BAR_SLIDER_PRES 47
+
+#define _WA_SKIN_PLAYPAUS_PLAY 48
+#define _WA_SKIN_PLAYPAUS_PAUSE 49
+#define _WA_SKIN_PLAYPAUS_STOP 50
+#define _WA_SKIN_PLAYPAUS_FILLER 51
+#define _WA_SKIN_PLAYPAUS_WORK_INDICATOR 52
+
+#define _WA_SKIN_TITLE_ACTIVE 53
+#define _WA_SKIN_TITLE_INACTIVE 54
+
+#define _WA_SKIN_TITLE_MENU_PRES 55
+#define _WA_SKIN_TITLE_MENU_NORM 56
+#define _WA_SKIN_TITLE_MENU_INACTIVE 57
+#define _WA_SKIN_TITLE_MIN_PRES 58
+#define _WA_SKIN_TITLE_MIN_NORM 59
+#define _WA_SKIN_TITLE_MIN_INACTIVE 60
+#define _WA_SKIN_TITLE_SHADE_PRES 61
+#define _WA_SKIN_TITLE_SHADE_NORM 62
+#define _WA_SKIN_TITLE_SHADE_INACTIVE 63
+#define _WA_SKIN_TITLE_CLOSE_PRES 64
+#define _WA_SKIN_TITLE_CLOSE_NORM 65
+#define _WA_SKIN_TITLE_CLOSE_INACTIVE 66
+
+#define _WA_SKIN_CLUTTERBAR_DISABLED 67
+
+#define _WA_MAPPING_MAIN 0
+#define _WA_MAPPING_CBUTTONS_PREV 1
+#define _WA_MAPPING_CBUTTONS_PLAY 2
+#define _WA_MAPPING_CBUTTONS_PAUSE 3
+#define _WA_MAPPING_CBUTTONS_STOP 4
+#define _WA_MAPPING_CBUTTONS_NEXT 5
+#define _WA_MAPPING_CBUTTONS_EJECT 6
+#define _WA_MAPPING_MONOSTER_STEREO 7
+#define _WA_MAPPING_MONOSTER_MONO 8
+#define _WA_MAPPING_REPEAT 9
+#define _WA_MAPPING_SHUFFLE 10
+#define _WA_MAPPING_PLAYLIST 11
+#define _WA_MAPPING_EQ 12
+#define _WA_MAPPING_VOLUME_BAR 13
+#define _WA_MAPPING_VOLUME_SLIDER 14
+#define _WA_MAPPING_BALANCE_BAR 15
+#define _WA_MAPPING_BALANCE_SLIDER 16
+#define _WA_MAPPING_PLAYPAUS 17
+#define _WA_MAPPING_POS_BAR 18
+#define _WA_MAPPING_POS_BAR_SLIDER 19
+#define _WA_MAPPING_DIGITS 20
+#define _WA_MAPPING_MINUS 21
+#define _WA_MAPPING_DIGIT_1 22
+#define _WA_MAPPING_DIGIT_2 23
+#define _WA_MAPPING_DIGIT_3 24
+#define _WA_MAPPING_DIGIT_4 25
+#define _WA_MAPPING_ANALYSER 26
+#define _WA_MAPPING_BPS 27
+#define _WA_MAPPING_FREQ 28
+#define _WA_MAPPING_INFO 29
+#define _WA_MAPPING_TITLE 30
+#define _WA_MAPPING_TITLE_MENU 31
+#define _WA_MAPPING_TITLE_MIN 32
+#define _WA_MAPPING_TITLE_SHADE 33
+#define _WA_MAPPING_TITLE_CLOSE 34
+#define _WA_MAPPING_CLUTTERBAR 35
+
+#define _WA_FILE_MAIN 0
+#define _WA_FILE_CBUTTONS 1
+#define _WA_FILE_MONOSTER 2
+#define _WA_FILE_NUMBERS 3
+#define _WA_FILE_SHUFREP 4
+#define _WA_FILE_TEXT 5
+#define _WA_FILE_VOLUME 6
+#define _WA_FILE_BALANCE 7
+#define _WA_FILE_POSBAR 8
+#define _WA_FILE_PLAYPAUS 9
+#define _WA_FILE_TITLEBAR 10
+
+#define _WA_MAPPING_ENTRIES 36
+#define _WA_SKIN_ENTRIES 68
+
+#define _WA_TEXT_WIDTH 5
+#define _WA_TEXT_HEIGHT 6
+
+#endif
diff --git a/noatun/modules/winskin/waSlider.cpp b/noatun/modules/winskin/waSlider.cpp
new file mode 100644
index 00000000..4fff1b0d
--- /dev/null
+++ b/noatun/modules/winskin/waSlider.cpp
@@ -0,0 +1,209 @@
+/*
+ Standard slider for Winskin
+ Copyright (C) 1999 Martin Vogt
+ Copyright (C) 2002 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+#include <stdlib.h>
+
+#include "waSlider.h"
+#include "waSkinModel.h"
+
+WaSlider::WaSlider(int sliderBarMapId, int sliderId, bool is_volume) : WaWidget(sliderBarMapId)
+{
+ this->sliderBarMapId = sliderBarMapId;
+
+ lDragging = false;
+
+ currentValue = 0;
+ setRange(0, 100);
+
+ slider_y = is_volume ? 1 : 0;
+ slider_x = 0;
+
+ slider_width = WaSkinModel::instance()->getMapGeometry(sliderId).width();
+
+ slider_visible = true;
+}
+
+
+WaSlider::~WaSlider()
+{
+}
+
+void WaSlider::setPixmapSliderBar(int pixId)
+{
+ this->sliderBarId = pixId;
+ update();
+}
+
+void WaSlider::paintEvent(QPaintEvent *)
+{
+ // POSBAR.BMP does not have full height in all Winamp skins
+ // Paint background before painting slider to be on the safe side
+ if(sliderBarId == _WA_SKIN_POS_BAR)
+ paintPixmap(-1);
+
+ paintPixmap(sliderBarId, value());
+
+ if (slider_visible)
+ paintPixmap(lDragging ? up_pixmap : down_pixmap, slider_x, slider_y);
+}
+
+
+void WaSlider::mouseMoveEvent(QMouseEvent * e)
+{
+ if (lDragging == false) {
+ WaWidget::mouseMoveEvent(e);
+ return;
+ }
+
+ int newX = e->x() - pressPoint.x();
+
+ if (newX < 0)
+ newX = 0;
+
+ QSize size = sizeHint();
+
+ int maxX = size.width() - slider_width;
+
+ if(mapping == _WA_MAPPING_VOLUME_BAR)
+ maxX -= 3;
+
+ if (newX > maxX)
+ newX = maxX;
+
+ int value = pixel2Value(newX);
+
+ setValue(value);
+}
+
+
+void WaSlider::updateSliderPos(int value)
+{
+ if (value > maxValue) {
+ value = maxValue;
+ }
+ if (value < minValue) {
+ value = minValue;
+ }
+
+ int pixelPos = value2Pixel(value);
+ slider_x = (pixelPos);
+
+ update();
+}
+
+void WaSlider::mousePressEvent(QMouseEvent *e) {
+ if (e->button() != LeftButton && e->button() != MidButton) {
+ WaWidget::mousePressEvent(e);
+ return;
+ }
+
+ int maxX = slider_x - slider_width;
+
+ if(mapping == _WA_MAPPING_VOLUME_BAR)
+ maxX -= 3;
+
+ if ((e->x() < slider_x) || (e->x() > (maxX))) {
+ int newX = e->x();
+ newX -= (slider_width / 2);
+ setValue(pixel2Value(newX));
+ }
+
+
+ pressPoint.setX(e->x() - slider_x);
+ lDragging = true;
+
+ update();
+
+ emit(sliderPressed());
+}
+
+void WaSlider::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (!lDragging) {
+ WaWidget::mouseReleaseEvent(e);
+ return;
+ }
+
+ lDragging = false;
+ update();
+
+ emit(sliderReleased());
+}
+
+
+int WaSlider::pixel2Value(int xpos)
+{
+ QSize size = sizeHint();
+ int min = abs(minValue);
+ int max = abs(maxValue);
+
+ int valuerange = min + max;
+ int pixelrange = size.width() - slider_width;
+
+ if(mapping == _WA_MAPPING_VOLUME_BAR)
+ pixelrange -= 3;
+
+ return ((xpos * valuerange) / pixelrange) + minValue;
+}
+
+int WaSlider::value2Pixel(int value)
+{
+ QSize size = sizeHint();
+ float min = (float) minValue;
+ float max = (float) maxValue;
+ float fmin = min;
+ float fmax = max;
+ if (min < 0) {
+ fmin = -1 * fmin;
+ }
+ if (max < 0) {
+ fmax = -1 * fmax;
+ }
+ float valuerange = fmin + fmax;
+ float verhaeltnis = fmin / valuerange;
+ float pixelrange = (float) (size.width() - slider_width);
+
+ if(mapping == _WA_MAPPING_VOLUME_BAR)
+ pixelrange -= 3;
+
+ float zeropoint = verhaeltnis * pixelrange;
+ float anstieg = pixelrange / valuerange;
+
+ float pixel = (float) value * anstieg + zeropoint;
+ return (int) (pixel+0.5); // add 0.5 to round upwards if larger than x.5
+}
+
+void WaSlider::setRange(int min, int max) {
+ minValue = min;
+ maxValue = max;
+
+ if (currentValue < min)
+ currentValue = min;
+
+ if (currentValue > max)
+ currentValue = max;
+}
+
+void WaSlider::setValue(int value) {
+ currentValue = value;
+
+ updateSliderPos(currentValue);
+ emit(valueChanged(value));
+}
+
+void WaSlider::cancelDrag() {
+ lDragging = false;
+ update();
+}
+
+#include "waSlider.moc"
diff --git a/noatun/modules/winskin/waSlider.h b/noatun/modules/winskin/waSlider.h
new file mode 100644
index 00000000..0003afd2
--- /dev/null
+++ b/noatun/modules/winskin/waSlider.h
@@ -0,0 +1,82 @@
+/*
+ standard Slider for winamp Skin
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __WASLIDER_H
+#define __WASLIDER_H
+
+#include <qpainter.h>
+
+#include "waWidget.h"
+#include "waButton.h"
+
+class WaSlider : public WaWidget {
+ Q_OBJECT
+
+ public:
+ WaSlider(int sliderBarMapId, int sliderId, bool is_volume = false);
+ ~WaSlider();
+
+ void setRange(int min, int max);
+
+ void setValue(int value);
+ int value() const { return currentValue; }
+
+ void hideButton() { slider_visible = false; }
+ void showButton() { slider_visible = true; }
+
+ void cancelDrag();
+
+ public slots:
+ void setPixmapSliderButtonDown(int pixId) { down_pixmap = pixId; }
+ void setPixmapSliderButtonUp(int pixId) { up_pixmap = pixId; }
+ void setPixmapSliderBar(int pixId);
+
+ private:
+ void paintEvent(QPaintEvent *);
+ int pixel2Value(int xpos);
+ int value2Pixel(int value);
+
+ int slider_x;
+ int slider_y;
+ int slider_width;
+ bool slider_visible;
+
+ int up_pixmap;
+ int down_pixmap;
+
+ int sliderBarId;
+ int sliderBarMapId;
+
+ bool lDragging;
+ QPoint pressPoint;
+
+ int currentValue;
+
+ int minValue;
+ int maxValue;
+
+ void mousePressEvent(QMouseEvent *e);
+ void mouseMoveEvent(QMouseEvent *e);
+ void mouseReleaseEvent(QMouseEvent *e);
+
+ private slots:
+ void updateSliderPos(int value);
+
+ signals:
+ void sliderPressed();
+ void sliderReleased();
+ void valueChanged(int);
+};
+#endif
diff --git a/noatun/modules/winskin/waStatus.cpp b/noatun/modules/winskin/waStatus.cpp
new file mode 100644
index 00000000..e028b7f0
--- /dev/null
+++ b/noatun/modules/winskin/waStatus.cpp
@@ -0,0 +1,42 @@
+/*
+ standard Button for winamp Skin
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include "waStatus.h"
+#include "waSkinModel.h"
+
+WaStatus::WaStatus() : WaWidget(_WA_MAPPING_PLAYPAUS)
+{
+ _status = STATUS_STOPPED;
+}
+
+WaStatus::~WaStatus()
+{
+}
+
+void WaStatus::paintEvent(QPaintEvent *)
+{
+ if (_status == STATUS_PLAYING) {
+ paintPixmap(_WA_SKIN_PLAYPAUS_WORK_INDICATOR);
+ paintPixmap(_WA_SKIN_PLAYPAUS_PLAY, 3, 0);
+ }
+ else if (_status == STATUS_STOPPED) {
+ paintPixmap(_WA_SKIN_PLAYPAUS_FILLER);
+ paintPixmap(_WA_SKIN_PLAYPAUS_STOP, 2 ,0);
+ }
+ else if (_status == STATUS_PAUSED) {
+ paintPixmap(_WA_SKIN_PLAYPAUS_FILLER);
+ paintPixmap(_WA_SKIN_PLAYPAUS_PAUSE, 2, 0);
+ }
+}
+
+#include "waStatus.moc"
diff --git a/noatun/modules/winskin/waStatus.h b/noatun/modules/winskin/waStatus.h
new file mode 100644
index 00000000..a3fe1bc8
--- /dev/null
+++ b/noatun/modules/winskin/waStatus.h
@@ -0,0 +1,40 @@
+/*
+ standard Button for winamp Skin
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __WASTATUS_H
+#define __WASTATUS_H
+
+#include <qpainter.h>
+
+#include "waWidget.h"
+
+enum status_enum {STATUS_PLAYING, STATUS_STOPPED, STATUS_PAUSED};
+
+class WaStatus : public WaWidget {
+ Q_OBJECT
+
+ public:
+ WaStatus();
+ ~WaStatus();
+
+ void setStatus(status_enum status) { _status = status; update(); }
+ status_enum status() const { return _status; }
+
+ private:
+ void paintEvent(QPaintEvent * paintEvent);
+
+ status_enum _status;
+};
+#endif
diff --git a/noatun/modules/winskin/waTitleBar.cpp b/noatun/modules/winskin/waTitleBar.cpp
new file mode 100644
index 00000000..82b603f5
--- /dev/null
+++ b/noatun/modules/winskin/waTitleBar.cpp
@@ -0,0 +1,79 @@
+/*
+ Titlebar for winamp Skin
+ Copyright (C) 1999 Martin Vogt
+ Copyright (C) 2001 Ryan Cumming
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include <qwidget.h>
+#include <qpixmap.h>
+
+#include "waTitleBar.h"
+#include "waSkinModel.h"
+
+#include <stdlib.h>
+
+#include <iostream>
+
+WaTitleBar::WaTitleBar() : WaIndicator(_WA_MAPPING_TITLE, _WA_SKIN_TITLE_ACTIVE, _WA_SKIN_TITLE_INACTIVE)
+{
+ moving = false;
+ setState(parentWidget()->isActiveWindow());
+}
+
+WaTitleBar::~WaTitleBar()
+{
+}
+
+void WaTitleBar::mousePressEvent(QMouseEvent * e)
+{
+ if (e->button() != RightButton) {
+ if (!moving) {
+ moving = true;
+ mDragStart = e->pos();
+ mLastPos = e->globalPos();
+ }
+
+ setState(true);
+ update();
+ return;
+ }
+ else
+ WaWidget::mousePressEvent(e);
+}
+
+void WaTitleBar::mouseDoubleClickEvent(QMouseEvent *) {
+ emit(shaded());
+}
+
+void WaTitleBar::mouseReleaseEvent(QMouseEvent * e)
+{
+ if (e->button() != RightButton) {
+ moving = false;
+ update();
+ return;
+ }
+ else
+ WaWidget::mouseReleaseEvent(e);
+}
+
+void WaTitleBar::mouseMoveEvent(QMouseEvent * e)
+{
+ QPoint diff = e->globalPos() - mLastPos;
+ if (abs(diff.x()) > 10 || abs(diff.y()) > 10) {
+ // Moving starts only, when passing a drag border
+ moving = true;
+ }
+
+ if (moving)
+ parentWidget()->move(e->globalPos() - mDragStart);
+}
+
+#include <waTitleBar.moc>
diff --git a/noatun/modules/winskin/waTitleBar.h b/noatun/modules/winskin/waTitleBar.h
new file mode 100644
index 00000000..4e001394
--- /dev/null
+++ b/noatun/modules/winskin/waTitleBar.h
@@ -0,0 +1,51 @@
+/*
+ standard Button for winamp Skin
+ Copyright (C) 1999 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+
+#ifndef __WATITLEBAR_H
+#define __WATITLEBAR_H
+
+#include <qpainter.h>
+
+#include "waIndicator.h"
+
+
+/**
+ A titlebar is similar to a button only that we move
+ the whole widget, when pressed
+*/
+
+
+class WaTitleBar : public WaIndicator {
+ Q_OBJECT
+
+ public:
+ WaTitleBar();
+ ~WaTitleBar();
+
+ private:
+ void mouseDoubleClickEvent (QMouseEvent * e);
+ void mousePressEvent(QMouseEvent * e);
+ void mouseReleaseEvent(QMouseEvent * e);
+ void mouseMoveEvent(QMouseEvent * e);
+
+ bool moving;
+ QPoint mLastPos;
+ QPoint mDragStart;
+
+ signals:
+ void shaded();
+
+};
+#endif
diff --git a/noatun/modules/winskin/waVolumeSlider.cpp b/noatun/modules/winskin/waVolumeSlider.cpp
new file mode 100644
index 00000000..d0a934d7
--- /dev/null
+++ b/noatun/modules/winskin/waVolumeSlider.cpp
@@ -0,0 +1,51 @@
+/*
+ jumpslider for winamp skins
+ Copyright (C) 1998 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#include <waVolumeSlider.h>
+
+
+WaVolumeSlider::WaVolumeSlider() : WaWidget(_WA_MAPPING_VOLUME_BAR)
+{
+
+}
+
+
+WaVolumeSlider::~WaVolumeSlider()
+{
+}
+
+
+void WaVolumeSlider::buildGui()
+{
+ ws = new WaSlider(_WA_MAPPING_VOLUME_BAR, _WA_MAPPING_VOLUME_SLIDER, true);
+
+ ws->setPixmapSliderButtonUp(_WA_SKIN_VOLUME_SLIDER_NORM);
+ ws->setPixmapSliderButtonDown(_WA_SKIN_VOLUME_SLIDER_PRES);
+ ws->setPixmapSliderBar(_WA_SKIN_VOLUME_BAR);
+
+ connect(ws, SIGNAL(valueChanged(int)), this,
+ SIGNAL(volumeSetValue(int)));
+ connect(ws, SIGNAL(sliderPressed()), SIGNAL(sliderPressed()));
+ connect(ws, SIGNAL(sliderReleased()), SIGNAL(sliderReleased()));
+}
+
+void WaVolumeSlider::setVolumeValue(int val)
+{
+ int currVal = ws->value();
+ if (currVal != val) {
+ ws->setValue(val);
+ }
+}
+
+
+#include "waVolumeSlider.moc"
diff --git a/noatun/modules/winskin/waVolumeSlider.h b/noatun/modules/winskin/waVolumeSlider.h
new file mode 100644
index 00000000..29030c65
--- /dev/null
+++ b/noatun/modules/winskin/waVolumeSlider.h
@@ -0,0 +1,41 @@
+/*
+ jumpslider for winamp skins
+ Copyright (C) 1998 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#ifndef __WAVOLUMESLIDER_H
+#define __WAVOLUMESLIDER_H
+
+#include "waSlider.h"
+#include "waWidget.h"
+
+class WaVolumeSlider:public WaWidget {
+ Q_OBJECT
+
+ public:
+ WaVolumeSlider();
+ ~WaVolumeSlider();
+ void buildGui();
+
+ void setVolumeValue(int val);
+
+ private:
+ WaSlider *ws;
+
+ signals:
+ void volumeSetValue(int val);
+ void sliderPressed();
+ void sliderReleased();
+};
+
+
+#endif
diff --git a/noatun/modules/winskin/waWidget.cpp b/noatun/modules/winskin/waWidget.cpp
new file mode 100644
index 00000000..ffe2d3b4
--- /dev/null
+++ b/noatun/modules/winskin/waWidget.cpp
@@ -0,0 +1,58 @@
+#include "waWidget.h"
+#include "waSkinModel.h"
+#include "noatun/stdaction.h"
+#include "waSkin.h"
+
+WaWidget::WaWidget(int _mapping) : QWidget(WaSkin::instance()) {
+ mapping = _mapping;
+ setBackgroundMode(NoBackground);
+ connect (WaSkinModel::instance(), SIGNAL(skinChanged()), this, SLOT(skinChanged()));
+}
+
+WaWidget::~WaWidget() {
+}
+
+void WaWidget::paintPixmap(int pixmap_mapping) {
+ if (pixmap_mapping != -1)
+ WaSkinModel::instance()->bltTo(pixmap_mapping, this, 0, 0);
+ else
+ WaSkinModel::instance()->paintBackgroundTo(mapping, this, 0, 0);
+}
+
+void WaWidget::paintPixmap(int pixmap_mapping, int x, int y) {
+ if (pixmap_mapping != -1)
+ WaSkinModel::instance()->bltTo(pixmap_mapping, this, x, y);
+ else
+ WaSkinModel::instance()->paintBackgroundTo(mapping, this, x, y);
+}
+
+void WaWidget::paintPixmap(int pixmap_mapping, int argument) {
+ if (pixmap_mapping != -1)
+ WaSkinModel::instance()->bltTo(pixmap_mapping, this, 0, 0, argument);
+ else
+ WaSkinModel::instance()->paintBackgroundTo(mapping, this, 0, 0);
+
+}
+
+void WaWidget::paintPixmap(int pixmap_mapping, int argument, int x, int y) {
+ if (pixmap_mapping != -1)
+ WaSkinModel::instance()->bltTo(pixmap_mapping, this, x, y, argument);
+ else
+ WaSkinModel::instance()->paintBackgroundTo(mapping, this, x, y);
+}
+
+QSize WaWidget::sizeHint() {
+ return WaSkinModel::instance()->getMapGeometry(mapping).size();
+}
+
+void WaWidget::mousePressEvent(QMouseEvent *e) {
+ if (e->button() == RightButton)
+ NoatunStdAction::ContextMenu::showContextMenu();
+}
+
+void WaWidget::skinChanged() {
+ setGeometry(WaSkinModel::instance()->getMapGeometry(mapping));
+ update();
+}
+
+#include <waWidget.moc>
diff --git a/noatun/modules/winskin/waWidget.h b/noatun/modules/winskin/waWidget.h
new file mode 100644
index 00000000..f2a41c82
--- /dev/null
+++ b/noatun/modules/winskin/waWidget.h
@@ -0,0 +1,31 @@
+#ifndef _WAWIDGET_H
+#define _WAWIDGET_H
+
+#include <qwidget.h>
+#include "waSkinModel.h"
+
+class WaWidget : public QWidget {
+ Q_OBJECT
+public:
+ WaWidget(int mapping);
+ virtual ~WaWidget();
+
+ QSize sizeHint();
+
+ void paintPixmap(int wa_mapping);
+ void paintPixmap(int wa_mapping, int number);
+
+ void paintPixmap(int wa_mapping, int x, int y);
+ void paintPixmap(int wa_mapping, int number, int x, int y);
+
+ void paintBackground() { WaSkinModel::instance()->paintBackgroundTo(mapping, this, 0, 0); }
+
+protected:
+ void mousePressEvent(QMouseEvent *);
+ int mapping;
+
+private slots:
+ void skinChanged();
+};
+
+#endif
diff --git a/noatun/modules/winskin/winSkinConfig.cpp b/noatun/modules/winskin/winSkinConfig.cpp
new file mode 100644
index 00000000..649fd1fe
--- /dev/null
+++ b/noatun/modules/winskin/winSkinConfig.cpp
@@ -0,0 +1,174 @@
+#include <noatun/pref.h>
+
+#include <klocale.h>
+#include <qpushbutton.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qpixmap.h>
+#include <kglobal.h>
+#include <kconfig.h>
+#include <qslider.h>
+#include <qframe.h>
+#include <qstringlist.h>
+#include <kfile.h>
+#include <kfiledialog.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <kurlrequester.h>
+#include <kurlrequesterdlg.h>
+#include <qdir.h>
+
+#include "waSkin.h"
+#include "waInfo.h"
+#include "waSkinManager.h"
+#include "winSkinConfig.h"
+
+WinSkinConfig::WinSkinConfig(QWidget * parent, WaSkinManager *waSkinManager) :
+ CModule(i18n("Winskin"),
+ i18n("Skin Selection for the Winskin Plugin"),
+ "style",
+ parent)
+{
+ // Make a token horizontal layout box
+ vbox = new QVBoxLayout(this);
+ vbox->setSpacing( 6 );
+ vbox->setMargin( 0 );
+
+ // Add a simple list of skins, populated in WinSkinConfig::reopen()
+ skin_list = new QListBox(this, "skin_list");
+ vbox->addWidget(skin_list);
+
+ QHBoxLayout* hbox = new QHBoxLayout( 0, 6, 6 );
+
+ QPushButton* buttonInstall = new QPushButton( i18n("&Install New Skin..."), this );
+ hbox->addWidget(buttonInstall);
+
+ buttonRemove = new QPushButton( i18n("&Remove Skin"), this );
+ buttonRemove->setEnabled(false);
+ hbox->addWidget(buttonRemove);
+ vbox->addLayout(hbox);
+
+ connect( skin_list, SIGNAL(highlighted(const QString &)), this, SLOT(selected()));
+ connect( buttonInstall, SIGNAL(clicked()), this, SLOT(install()));
+ connect( buttonRemove, SIGNAL(clicked()), this, SLOT(remove()));
+ connect(waSkinManager, SIGNAL(updateSkinList()), this, SLOT(reopen()));
+
+ mWaSkinManager = waSkinManager;
+
+ QGroupBox *settingsBox = new QGroupBox( 1, Vertical, i18n("Settings"), this );
+ vbox->addWidget(settingsBox);
+
+ QHBox *box = new QHBox(settingsBox);
+ QLabel *label = new QLabel(i18n("T&itle scrolling speed:"), box);
+ new QLabel(i18n("None"), box);
+
+ scrollSpeed = new QSlider(box);
+ label->setBuddy(scrollSpeed);
+ scrollSpeed->setMinimumSize( QSize( 80, 0 ) );
+ scrollSpeed->setMinValue( 0 );
+ scrollSpeed->setMaxValue( 50 );
+ scrollSpeed->setPageStep( 1 );
+ scrollSpeed->setOrientation( QSlider::Horizontal );
+ scrollSpeed->setTickmarks( QSlider::NoMarks );
+
+ label = new QLabel(i18n("Fast"), box);
+
+ reopen();
+}
+
+void WinSkinConfig::save()
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup("Winskin");
+ config->writeEntry("CurrentSkin", skin_list->currentText());
+ config->writeEntry("ScrollDelay", scrollSpeed->value());
+ config->sync();
+
+ if (skin_list->currentText() != orig_skin) {
+ _waskin_instance->loadSkin(skin_list->currentText());
+ orig_skin = skin_list->currentText();
+ }
+ else
+ {
+ _waskin_instance->skinInfo()->scrollerSetup();
+ }
+}
+
+void WinSkinConfig::reopen() {
+ // Wipe out the old list
+ skin_list->clear();
+
+ // Get a list of skins
+ QStringList skins = mWaSkinManager->availableSkins();
+
+ // This loop adds them all to our skin list
+ for(unsigned int x = 0;x < skins.count();x++) {
+ // Add ourselves to the list
+ skin_list->insertItem(skins[x]);
+ }
+
+ // Figure out our current skin
+ QString orig_skin = mWaSkinManager->currentSkin();
+
+ // Where is that skin in our big-list-o-skins?
+ QListBoxItem *item = skin_list->findItem(orig_skin);
+
+ if (item) {
+ // Aha, found it... make it the currently selected skin
+ skin_list->setCurrentItem( item );
+ }
+ else {
+ // Er, it's not there... select the current item
+ // Maybe this should emit a warning? Oh well, it's not harmful
+ skin_list->setCurrentItem( 0 );
+ }
+
+ KConfig *config=KGlobal::config();
+ config->setGroup("Winskin");
+ scrollSpeed->setValue(config->readNumEntry("ScrollDelay", 15));
+}
+
+void WinSkinConfig::selected()
+{
+ buttonRemove->setEnabled(mWaSkinManager->skinRemovable( skin_list->currentText() ));
+}
+
+void WinSkinConfig::install()
+{
+ QString url;
+
+ // Ask the user for directory containing a skin
+ KURLRequesterDlg* udlg = new KURLRequesterDlg( QString::null, this, "udlg", true );
+ udlg->urlRequester()->setFilter(mWaSkinManager->skinMimeTypes().join(" "));
+ udlg->urlRequester()->setMode( KFile::File | KFile::Directory | KFile::ExistingOnly );
+
+ if( udlg->exec() == QDialog::Accepted ) {
+ url = udlg->urlRequester()->url();
+ mWaSkinManager->installSkin( url );
+ }
+}
+
+void WinSkinConfig::remove()
+{
+ // Is there any item selected ??
+ if( skin_list->currentText().isEmpty() )
+ return;
+
+ // We can't remove every skin
+ if( !mWaSkinManager->skinRemovable( skin_list->currentText() ) ) {
+ KMessageBox::information( this, i18n("You cannot remove this skin.") );
+ // Reload skin list, perhaps the skin is already removed!
+ return;
+ }
+
+ // Ask the user first
+ if( KMessageBox::warningContinueCancel( this,
+ i18n("<qt>Are you sure you want to remove the <b>%1</b> skin?</qt>").arg( skin_list->currentText() ), QString::null, KStdGuiItem::del() )
+ == KMessageBox::Continue ) {
+
+ mWaSkinManager->removeSkin( skin_list->currentText() );
+ reopen();
+ }
+}
+
+#include <winSkinConfig.moc>
diff --git a/noatun/modules/winskin/winSkinConfig.h b/noatun/modules/winskin/winSkinConfig.h
new file mode 100644
index 00000000..22dca884
--- /dev/null
+++ b/noatun/modules/winskin/winSkinConfig.h
@@ -0,0 +1,35 @@
+#ifndef __WINSKINCONFIG_H
+#define __WINSKINCONFIG_H
+
+#include <noatun/pref.h>
+#include <qwidget.h>
+
+class QVBoxLayout;
+class WaSkinManager;
+class QSlider;
+
+class WinSkinConfig:public CModule {
+ Q_OBJECT
+ public:
+ WinSkinConfig(QWidget * parent, WaSkinManager *waManager);
+
+ void save();
+
+ public slots:
+ void reopen();
+
+ private slots:
+ void selected();
+ void install();
+ void remove();
+
+ private:
+ WaSkinManager *mWaSkinManager;
+ QListBox *skin_list;
+ QString orig_skin;
+ QVBoxLayout *vbox;
+ QPushButton *buttonRemove;
+ QSlider *scrollSpeed;
+};
+
+#endif
diff --git a/noatun/modules/winskin/winSkinVis.cpp b/noatun/modules/winskin/winSkinVis.cpp
new file mode 100644
index 00000000..f9937981
--- /dev/null
+++ b/noatun/modules/winskin/winSkinVis.cpp
@@ -0,0 +1,107 @@
+/*
+ noatun visualisation interface for winskin
+ Copyright (C) 2001 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+
+#include "winSkinVis.h"
+
+#define __BANDS 75
+#define __SPAHEIGHT 15
+
+WinSkinVis::WinSkinVis(QObject *parent, const char *name):
+ QObject(parent,name),Visualization(50) {
+
+ m_currentPeaks=new float[__BANDS];
+
+ for(unsigned int i=0;i<__BANDS;i++)
+ m_currentPeaks[i]=0.0;
+
+ // If we can create our server-side object, visualize away
+ if (initServerObject())
+ {
+ start();
+ }
+}
+
+
+WinSkinVis::~WinSkinVis()
+{
+ if (m_winSkinFFT != NULL) {
+ if (connected())
+ {
+ visualizationStack().remove(m_id);
+ m_winSkinFFT->stop();
+ delete m_winSkinFFT;
+ }
+ }
+
+ delete[] m_currentPeaks;
+}
+
+
+bool WinSkinVis::initServerObject()
+{
+ // Create FFT on server
+ m_winSkinFFT = new Noatun::WinSkinFFT();
+ *m_winSkinFFT = Arts::DynamicCast(server()->createObject("Noatun::WinSkinFFT"));
+
+ if ( (*m_winSkinFFT).isNull() ) {
+ delete m_winSkinFFT;
+ m_winSkinFFT=NULL;
+ }
+ else
+ {
+ m_winSkinFFT->bandResolution(__BANDS);
+ m_winSkinFFT->start();
+ m_id=visualizationStack().insertBottom(*m_winSkinFFT, "WinSkin FFT");
+ }
+
+ return (m_winSkinFFT != NULL);
+}
+
+void WinSkinVis::timeout()
+{
+ std::vector<float> *data(m_winSkinFFT->scope());
+
+ float *f=&data->front();
+ if (data->size())
+ scopeEvent(f, data->size());
+
+ delete data;
+}
+
+float* WinSkinVis::currentPeaks()
+{
+ return m_currentPeaks;
+}
+
+void WinSkinVis::scopeEvent(float* bandPtr, unsigned int bands)
+{
+ for (unsigned int i = 0;i < bands;i++) {
+ float value=bandPtr[i];
+ // if the peak is less we prefer the higher one
+ if (m_currentPeaks[i] < value)
+ m_currentPeaks[i] = value;
+ else
+ m_currentPeaks[i] = m_currentPeaks[i]-1.3;
+
+ if (m_currentPeaks[i] < 0.0)
+ m_currentPeaks[i] = 0.0;
+
+ if (m_currentPeaks[i] > __SPAHEIGHT)
+ m_currentPeaks[i]=__SPAHEIGHT;
+ }
+ emit(doRepaint());
+}
+
+
+#include "winSkinVis.moc"
diff --git a/noatun/modules/winskin/winSkinVis.h b/noatun/modules/winskin/winSkinVis.h
new file mode 100644
index 00000000..7d139d0c
--- /dev/null
+++ b/noatun/modules/winskin/winSkinVis.h
@@ -0,0 +1,54 @@
+/*
+ noatun visualisation interface for winskin
+ Copyright (C) 2001 Martin Vogt
+
+ 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.
+
+ For more information look at the file COPYRIGHT in this package
+
+ */
+
+
+#ifndef __WINSKINVIS_H
+#define __WINSKINVIS_H
+
+#include <qobject.h>
+
+// These are needed for the Visualisation class
+#include <soundserver.h>
+#include "noatunarts/noatunarts.h"
+#include "noatun/plugin.h"
+
+#include "vis/winskinvis.h"
+
+class WinSkinVis : public QObject, public Visualization {
+ Q_OBJECT
+
+ public:
+ WinSkinVis(QObject* parent,const char* name);
+ ~WinSkinVis();
+
+ /**
+ * reimplemented from class Visualization, you
+ * should never need to reimplement this yourself
+ **/
+ void timeout();
+ float* currentPeaks();
+
+ signals:
+ void doRepaint();
+
+ private:
+ bool initServerObject();
+ void scopeEvent(float* spectrum, unsigned int size);
+
+ Noatun::WinSkinFFT* m_winSkinFFT;
+ long m_id;
+ float* m_currentPeaks;
+
+
+};
+
+#endif
diff --git a/noatun/modules/winskin/winskin.plugin b/noatun/modules/winskin/winskin.plugin
new file mode 100644
index 00000000..0f8b1074
--- /dev/null
+++ b/noatun/modules/winskin/winskin.plugin
@@ -0,0 +1,122 @@
+Filename=noatun_winskin.la
+Author=Ryan Cumming, Martin Vogt
+Site=http://mpeglib.sf.net
+Email=bodnar42@phalynx.dhs.org
+Type=userinterface
+License=LGPL
+Name=WinAmp Interface
+Name[af]=Winamp Koppelvlak
+Name[ar]=واجهة WinAmp
+Name[az]=WinAMP Ara üzü
+Name[bn]=উইন-অ্যাম্প ইন্টারফেস
+Name[br]=Talbenn evit WinAmp
+Name[ca]=Interfície WinAmp
+Name[cs]=Rozhraní WinAmpu
+Name[cy]=Rhyngwyneb Winamp
+Name[da]=Winamp-grænseflade
+Name[de]=Winamp-Oberfläche
+Name[el]=Περιβάλλον WinAmp
+Name[eo]=WinAmp-interfaco
+Name[es]=Interfaz de Winamp
+Name[et]=WinAmp liides
+Name[eu]=WinAmp interfazea
+Name[fa]=واسط WinAmp
+Name[fi]=WinAmp-käyttöliittymä
+Name[fr]=Interface Winamp
+Name[ga]=Comhéadan WinAmp
+Name[gl]=Interface de WinAmp
+Name[he]=ממשק WinAmp
+Name[hi]= विनएम्प इंटरफेस
+Name[hr]=WinAmp sučelje
+Name[hu]=Winamp-felület
+Name[id]=Interface winamp
+Name[is]=WinAmp andlit
+Name[it]=Interfaccia WinAmp
+Name[ja]=WinAmp インターフェース
+Name[kk]=WinAmp интерфейсі
+Name[km]=ចំណុច​ប្រទាក់ WinAmp
+Name[ko]=WinAmp 인터페이스
+Name[lt]=WinAmp sąsaja
+Name[lv]=WinAmp Starpseja
+Name[mk]=Интерфејс WinAmp
+Name[ms]=Antaramuka WinAmp
+Name[mt]=Interfaċċja WinAmp
+Name[nb]=WinAmp-grensesnitt
+Name[nds]=Winamp-Böversiet
+Name[ne]=WinAmp इन्टरफेस
+Name[nl]=WinAmp-interface
+Name[nn]=WinAmp-grensesnitt
+Name[pa]=WinAmp ਇੰਟਰਫੇਸ
+Name[pl]=Motyw WinAmpa
+Name[pt]=Interface do WinAmp
+Name[pt_BR]=Interface WinAmp
+Name[ro]=Interfaţă WinAmp
+Name[ru]=Интерфейс WinAmp
+Name[sk]=Rozhranie WinAmp
+Name[sl]=Vmesnik WinAmp
+Name[sr]=WinAmp интерфејс
+Name[sr@Latn]=WinAmp interfejs
+Name[sv]=Winamp-gränssnitt
+Name[ta]=வின் ஆம்ப் முகப்பு
+Name[tg]=Интерфейси WinAmp
+Name[th]=ส่วนติดต่อแบบ WinAmp
+Name[tr]=WinAmp Arayüzü
+Name[uk]=Інтерфейс WinAmp
+Name[ven]=Interface ya WinAmp
+Name[wa]=Eterface po WinAmp
+Name[xh]=Ujongano lwe WinAmp
+Name[zh_CN]=WinAmp 界面
+Name[zh_HK]=WinAmp 界面
+Name[zh_TW]=WinAmp 界面
+Name[zu]=Uxhumano olubhekeneyo lwe WinAmp
+Comment=A Winamp skin loader
+Comment[bg]=Зареждане на теми за Winamp
+Comment[bn]=একটি উইন-অ্যাম্প স্কিন লোডার
+Comment[br]=Ur c'harger a groc'hen Winamp
+Comment[bs]=Učitavanje Winamp skinova
+Comment[ca]=Carregador d'aparences de Winamp
+Comment[cs]=Zavaděč motivů WinAmpu
+Comment[cy]=Llwythydd Crwyn Winamp
+Comment[da]=En Winamp-forsideindlæser
+Comment[de]=Import von Winamp-Designs
+Comment[el]=Φόρτωση θεμάτων Winamp
+Comment[eo]=WinAmp-etosŝargilo
+Comment[es]=Un cargador de pieles de Winamp
+Comment[et]=Winampi rüüde laadija
+Comment[eu]=Winamp azal kargatzailea
+Comment[fa]=یک بارکنندۀ Winamp skin
+Comment[fi]=Winamp-nahkojen lataaja
+Comment[fr]=Un chargeur de revêtement Winamp
+Comment[gl]=Un cargador de peles de Winamp
+Comment[he]=טוען Skins של Winamp
+Comment[hu]=Betöltőprogram Winamp-kinézetekhez
+Comment[is]=Les skinn fyrir WinAmp
+Comment[it]=Caricatore di skin di Winamp
+Comment[ja]=Winamp のスキンローダ
+Comment[kk]=Winamp тысының жүктегіші
+Comment[km]=កម្មវិធី​ផ្ទុក​ស្បែក Winamp
+Comment[ko]=Winamp 스킨 로더
+Comment[lt]=Winamp pavidalų įkėliklis
+Comment[mk]=Вчитувач на маски за Winamp
+Comment[nb]=Laster WinAmp-drakt
+Comment[nds]=En Winamp-Böversietlader
+Comment[ne]=विन्याप स्किन लोडर
+Comment[nl]=Een Winamp-skinlader
+Comment[nn]=Lastar WinAmp-skal
+Comment[pl]=Ładowarka skór Winampa
+Comment[pt]=Um leitor de aspectos do WinAmp
+Comment[pt_BR]=Um carregador de aparências (skins) do Winamp
+Comment[ro]=Un încărcător de tematici WinAmp
+Comment[ru]=Загрузчик тем Winamp
+Comment[sk]=Načítavač tém WinAmp
+Comment[sl]=Nalagalnik preoblek Winamp
+Comment[sr]=Учитавач Winamp-ових кошуљица
+Comment[sr@Latn]=Učitavač Winamp-ovih košuljica
+Comment[sv]=Skalladdare för Winamp
+Comment[ta]=வின் ஆம்ப் அலங்கார அமைப்பு ஏற்றி
+Comment[th]=ตัวโหลดหน้ากากแบบวินแอมป์
+Comment[tr]=Winamp arayüz yükleyici
+Comment[uk]=Завантажувач жупанів Winamp
+Comment[zh_CN]=Winamp 外观载入器
+Comment[zh_HK]=Winamp Skin 載入器
+Comment[zh_TW]=Winamp 面板載入器
diff --git a/noatun/noatun.api b/noatun/noatun.api
new file mode 100644
index 00000000..abf3bf87
--- /dev/null
+++ b/noatun/noatun.api
@@ -0,0 +1,169 @@
+# Doxygen configuration generated by Doxywizard version 0.1
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME = Noatun
+PROJECT_NUMBER = 2.7.0
+OUTPUT_DIRECTORY = apidocs
+OUTPUT_LANGUAGE = English
+EXTRACT_ALL = NO
+EXTRACT_PRIVATE = NO
+EXTRACT_STATIC = YES
+EXTRACT_LOCAL_CLASSES = YES
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+HIDE_FRIEND_COMPOUNDS = YES
+HIDE_IN_BODY_DOCS = NO
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ALWAYS_DETAILED_SEC = YES
+FULL_PATH_NAMES = NO
+STRIP_FROM_PATH =
+INTERNAL_DOCS = NO
+SOURCE_BROWSER = YES
+INLINE_SOURCES = NO
+STRIP_CODE_COMMENTS = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION = YES
+CASE_SENSE_NAMES = YES
+HIDE_SCOPE_NAMES = NO
+VERBATIM_HEADERS = YES
+SHOW_INCLUDE_FILES = YES
+JAVADOC_AUTOBRIEF = NO
+INHERIT_DOCS = YES
+INLINE_INFO = YES
+SORT_MEMBER_DOCS = YES
+DISTRIBUTE_GROUP_DOC = NO
+TAB_SIZE = 8
+ENABLED_SECTIONS =
+GENERATE_TODOLIST = NO
+GENERATE_TESTLIST = NO
+GENERATE_BUGLIST = NO
+GENERATE_DEPRECATEDLIST= YES
+ALIASES =
+MAX_INITIALIZER_LINES = 30
+OPTIMIZE_OUTPUT_FOR_C = NO
+SHOW_USED_FILES = YES
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET = YES
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = YES
+WARN_IF_DOC_ERROR = YES
+WARN_FORMAT =
+WARN_LOGFILE =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT = library/noatun
+FILE_PATTERNS = *.h
+RECURSIVE = NO
+EXCLUDE_PATTERNS = *.moc.* \
+ moc* \
+ *.all_cpp.* \
+ *unload.* \
+EXAMPLE_PATH =
+EXAMPLE_PATTERNS =
+EXAMPLE_RECURSIVE = NO
+IMAGE_PATH =
+INPUT_FILTER =
+FILTER_SOURCE_FILES = NO
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX = YES
+COLS_IN_ALPHA_INDEX = 3
+IGNORE_PREFIX =
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML = YES
+HTML_OUTPUT = libnoatun/html
+HTML_HEADER = apidocs/common/header.html
+HTML_FOOTER = apidocs/common/footer.html
+HTML_STYLESHEET = apidocs/common/doxygen.css
+HTML_ALIGN_MEMBERS = YES
+GENERATE_HTMLHELP = NO
+CHM_FILE =
+HHC_LOCATION =
+GENERATE_CHI = NO
+BINARY_TOC = NO
+TOC_EXPAND = NO
+DISABLE_INDEX = YES
+ENUM_VALUES_PER_LINE = 4
+GENERATE_TREEVIEW = NO
+TREEVIEW_WIDTH = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX = NO
+LATEX_OUTPUT = latex
+COMPACT_LATEX = NO
+PAPER_TYPE = a4wide
+EXTRA_PACKAGES =
+LATEX_HEADER =
+PDF_HYPERLINKS = NO
+USE_PDFLATEX = NO
+LATEX_BATCHMODE = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF = NO
+RTF_OUTPUT = rtf
+COMPACT_RTF = NO
+RTF_HYPERLINKS = NO
+RTF_STYLESHEET_FILE =
+RTF_EXTENSIONS_FILE =
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN = NO
+MAN_OUTPUT = man
+MAN_EXTENSION = .3
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = NO
+EXPAND_ONLY_PREDEF = NO
+SEARCH_INCLUDES = YES
+INCLUDE_PATH =
+INCLUDE_FILE_PATTERNS =
+PREDEFINED =
+EXPAND_AS_DEFINED =
+SKIP_FUNCTION_MACROS = YES
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references
+#---------------------------------------------------------------------------
+TAGFILES =
+GENERATE_TAGFILE = noatun.tag
+ALLEXTERNALS = NO
+EXTERNAL_GROUPS = NO
+PERL_PATH = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS = YES
+HIDE_UNDOC_RELATIONS = NO
+HAVE_DOT = YES
+CLASS_GRAPH = YES
+COLLABORATION_GRAPH = NO
+UML_LOOK = NO
+TEMPLATE_RELATIONS = YES
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
+CALL_GRAPH = NO
+GRAPHICAL_HIERARCHY = YES
+DOT_IMAGE_FORMAT = png
+DOT_PATH =
+DOTFILE_DIRS =
+MAX_DOT_GRAPH_WIDTH = 1024
+MAX_DOT_GRAPH_HEIGHT = 1024
+MAX_DOT_GRAPH_DEPTH = 0
+GENERATE_LEGEND = YES
+DOT_CLEANUP = YES
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE = NO
diff --git a/noatun/noatun.desktop b/noatun/noatun.desktop
new file mode 100644
index 00000000..2643d967
--- /dev/null
+++ b/noatun/noatun.desktop
@@ -0,0 +1,83 @@
+[Desktop Entry]
+Name=Noatun
+Name[bn]=নোটান
+Name[eo]=Sonorludilo
+Name[hi]=नोआट्यून
+Name[is]=Nóatún
+Name[ne]=नोवटुन
+Name[pa]=ਨੌਟੋਮ
+Name[ta]=நோடன்
+Exec=noatun %i %m -caption "%c" %U
+Icon=noatun
+Type=Application
+DocPath=noatun/index.html
+MimeType=audio/x-mp3;audio/x-mp2;audio/mpeg;audio/vorbis;application/ogg;audio/x-mpegurl;audio/x-wav;audio/x-mod;video/mpeg;video/x-msvideo;video/quicktime;video/mp4;video/x-flic;video/x-theora;video/x-ogm;audio/basic;audio/mp4;audio/ac3;audio/aac;audio/x-flac;audio/x-oggflac;audio/x-speex;audio/x-musepack;audio/x-ms-wma;
+GenericName=Media Player
+GenericName[af]=Media Speler
+GenericName[ar]=مشغل وسائط
+GenericName[bg]=Мултимедиен плеър
+GenericName[bn]=মিডিয়া প্লেয়ার
+GenericName[br]=Soner liesvedia
+GenericName[ca]=Reproductor multimèdia
+GenericName[cs]=Přehrávač médií
+GenericName[cy]=Chwaraeydd Cyfryngau
+GenericName[da]=Medieafspiller
+GenericName[de]=Multimedia-Wiedergabe
+GenericName[el]=Αναπαραγωγέας μέσων
+GenericName[eo]=Ludilo por sonor-dosieroj
+GenericName[es]=Reproductor multimedia
+GenericName[et]=Multimeedia mängija
+GenericName[eu]=Euskarri erreproduzigailua
+GenericName[fa]=پخش‌کنندۀ رسانه
+GenericName[fi]=Mediasoitin
+GenericName[fr]=Lecteur multimédia
+GenericName[ga]=Seinnteoir Meán
+GenericName[gl]=Reproductor Multimedia
+GenericName[he]=נגן מדיה
+GenericName[hi]=मीडिया प्लेयर
+GenericName[hu]=Médialejátszó
+GenericName[is]=Margmiðlunarforrit
+GenericName[it]=Lettore multimediale
+GenericName[ja]=メディアプレーヤ
+GenericName[kk]=Media ойнатқышы
+GenericName[km]=កម្មវិធី​ចាក់​មេ​ឌៀ
+GenericName[ko]=미디어 재생기
+GenericName[lt]=Media grotuvas
+GenericName[lv]=Mēdiju Atskaņotājs
+GenericName[mk]=Медијaплеер
+GenericName[nb]=Mediaspiller
+GenericName[nds]=Medienafspeler
+GenericName[ne]=मिडिया प्लेयर
+GenericName[nl]=Mediaspeler
+GenericName[nn]=Mediespelar
+GenericName[pa]=ਸੰਗੀਤ ਵਾਜਾ
+GenericName[pl]=Odtwarzacz multimedialny
+GenericName[pt]=Leitor Multimédia
+GenericName[pt_BR]=Reprodutor de Mídia
+GenericName[ro]=Program de redare multimedia
+GenericName[ru]=Медиаплеер
+GenericName[se]=Mediačuojaheaddji
+GenericName[sk]=Prehrávač médií
+GenericName[sl]=Večpredstavnostni predvajalnik
+GenericName[sr]=Медија плејер
+GenericName[sr@Latn]=Medija plejer
+GenericName[sv]=Mediaspelare
+GenericName[ta]=ஊடக இயக்கி
+GenericName[tg]=Бозингари Расона
+GenericName[th]=โปรแกรมเล่นแฟ้มสื่อ
+GenericName[tr]=Medya Yürütücüsü
+GenericName[uk]=Програвач медіа-матеріалів
+GenericName[uz]=Media pleyer
+GenericName[uz@cyrillic]=Медиа плейер
+GenericName[ven]=Tshitambi tsha Media
+GenericName[wa]=Djouweu multimedia
+GenericName[xh]=Umdlali we Midia
+GenericName[zh_CN]=媒体播放器
+GenericName[zh_HK]=媒體播放器
+GenericName[zh_TW]=媒體播放器
+GenericName[zu]=Umdlali Womculo
+Terminal=false
+InitialPreference=6
+X-KDE-StartupNotify=true
+X-DCOP-ServiceType=Unique
+Categories=Qt;KDE;AudioVideo;
diff --git a/noatun/noatun.upd b/noatun/noatun.upd
new file mode 100644
index 00000000..c58d08cd
--- /dev/null
+++ b/noatun/noatun.upd
@@ -0,0 +1,5 @@
+Id=noatun20
+File=noatunrc
+Script=noatun20update
+Group=KDE
+RemoveKey=MultipleInstances
diff --git a/noatun/noatun20update.cpp b/noatun/noatun20update.cpp
new file mode 100644
index 00000000..e6a611f0
--- /dev/null
+++ b/noatun/noatun20update.cpp
@@ -0,0 +1,41 @@
+// Convert Noatun 1.2 plugins that no longer exist to their closest 2.0
+// counterparts.
+
+#include <qfile.h>
+#include <qregexp.h>
+
+#ifndef stdin
+extern "C" FILE *stdin;
+#endif
+
+#ifndef stdout
+extern "C" FILE *stdout;
+#endif
+
+int main(int, char **)
+{
+ QFile qin, qout;
+ qin.open(IO_ReadOnly, stdin);
+ qout.open(IO_WriteOnly, stdout);
+
+ QString text = qin.readAll();
+
+ // tag loaders
+ bool tagloaders = text.contains("id3tag.plugin") ||
+ text.contains("oggtag.plugin") ||
+ text.contains("luckytag.plugin");
+
+ text.replace(QRegExp("id3tag\\.plugin"), "");
+ text.replace(QRegExp("oggtag\\.plugin"), "");
+ text.replace(QRegExp("luckytag\\.plugin"), "");
+
+ if(tagloaders) text.replace(QRegExp("Modules="), "Modules=metatag.plugin,");
+
+ // playlists
+ text.replace(QRegExp("tron\\.plugin"), "splitplaylist.plugin");
+ text.replace(QRegExp("liszt\\.plugin"), "splitplaylist.plugin");
+
+ qout.writeBlock(text.local8Bit());
+
+ return 0;
+}
diff --git a/noatun/pics/Makefile.am b/noatun/pics/Makefile.am
new file mode 100644
index 00000000..07e86dbf
--- /dev/null
+++ b/noatun/pics/Makefile.am
@@ -0,0 +1,2 @@
+noatuniconsdir = $(kde_datadir)/noatun/icons
+noatunicons_ICON = AUTO
diff --git a/noatun/pics/cr128-action-equalizer.png b/noatun/pics/cr128-action-equalizer.png
new file mode 100644
index 00000000..b526abef
--- /dev/null
+++ b/noatun/pics/cr128-action-equalizer.png
Binary files differ
diff --git a/noatun/pics/cr128-action-playlist.png b/noatun/pics/cr128-action-playlist.png
new file mode 100644
index 00000000..f825459c
--- /dev/null
+++ b/noatun/pics/cr128-action-playlist.png
Binary files differ
diff --git a/noatun/pics/cr16-action-effect.png b/noatun/pics/cr16-action-effect.png
new file mode 100644
index 00000000..c388b4ae
--- /dev/null
+++ b/noatun/pics/cr16-action-effect.png
Binary files differ
diff --git a/noatun/pics/cr16-action-equalizer.png b/noatun/pics/cr16-action-equalizer.png
new file mode 100644
index 00000000..001826c4
--- /dev/null
+++ b/noatun/pics/cr16-action-equalizer.png
Binary files differ
diff --git a/noatun/pics/cr16-action-noatunback.png b/noatun/pics/cr16-action-noatunback.png
new file mode 100644
index 00000000..13db3289
--- /dev/null
+++ b/noatun/pics/cr16-action-noatunback.png
Binary files differ
diff --git a/noatun/pics/cr16-action-noatunforward.png b/noatun/pics/cr16-action-noatunforward.png
new file mode 100644
index 00000000..0ff89c3e
--- /dev/null
+++ b/noatun/pics/cr16-action-noatunforward.png
Binary files differ
diff --git a/noatun/pics/cr16-action-noatunpause.png b/noatun/pics/cr16-action-noatunpause.png
new file mode 100644
index 00000000..1b88b6fe
--- /dev/null
+++ b/noatun/pics/cr16-action-noatunpause.png
Binary files differ
diff --git a/noatun/pics/cr16-action-noatunplay.png b/noatun/pics/cr16-action-noatunplay.png
new file mode 100644
index 00000000..abd8f18a
--- /dev/null
+++ b/noatun/pics/cr16-action-noatunplay.png
Binary files differ
diff --git a/noatun/pics/cr16-action-noatunplaylist.png b/noatun/pics/cr16-action-noatunplaylist.png
new file mode 100644
index 00000000..77e0e9b9
--- /dev/null
+++ b/noatun/pics/cr16-action-noatunplaylist.png
Binary files differ
diff --git a/noatun/pics/cr16-action-noatunstop.png b/noatun/pics/cr16-action-noatunstop.png
new file mode 100644
index 00000000..734184ec
--- /dev/null
+++ b/noatun/pics/cr16-action-noatunstop.png
Binary files differ
diff --git a/noatun/pics/cr16-action-playlist.png b/noatun/pics/cr16-action-playlist.png
new file mode 100644
index 00000000..ecaa54be
--- /dev/null
+++ b/noatun/pics/cr16-action-playlist.png
Binary files differ
diff --git a/noatun/pics/cr22-action-equalizer.png b/noatun/pics/cr22-action-equalizer.png
new file mode 100644
index 00000000..09d698e4
--- /dev/null
+++ b/noatun/pics/cr22-action-equalizer.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunback.png b/noatun/pics/cr22-action-noatunback.png
new file mode 100644
index 00000000..698f22df
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunback.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunfback.png b/noatun/pics/cr22-action-noatunfback.png
new file mode 100644
index 00000000..4634a633
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunfback.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunfforward.png b/noatun/pics/cr22-action-noatunfforward.png
new file mode 100644
index 00000000..2df91709
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunfforward.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunforward.png b/noatun/pics/cr22-action-noatunforward.png
new file mode 100644
index 00000000..d6eed5f0
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunforward.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunloopnone.png b/noatun/pics/cr22-action-noatunloopnone.png
new file mode 100644
index 00000000..d44109c3
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunloopnone.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunloopplaylist.png b/noatun/pics/cr22-action-noatunloopplaylist.png
new file mode 100644
index 00000000..142bfe5d
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunloopplaylist.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunlooprandom.png b/noatun/pics/cr22-action-noatunlooprandom.png
new file mode 100644
index 00000000..59916d74
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunlooprandom.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunloopsong.png b/noatun/pics/cr22-action-noatunloopsong.png
new file mode 100644
index 00000000..d18f40a5
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunloopsong.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunpause.png b/noatun/pics/cr22-action-noatunpause.png
new file mode 100644
index 00000000..1dde8554
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunpause.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunplay.png b/noatun/pics/cr22-action-noatunplay.png
new file mode 100644
index 00000000..d4fbdae4
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunplay.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunplaylist.png b/noatun/pics/cr22-action-noatunplaylist.png
new file mode 100644
index 00000000..008197fe
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunplaylist.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatunstop.png b/noatun/pics/cr22-action-noatunstop.png
new file mode 100644
index 00000000..5881503a
--- /dev/null
+++ b/noatun/pics/cr22-action-noatunstop.png
Binary files differ
diff --git a/noatun/pics/cr22-action-noatuntiny.png b/noatun/pics/cr22-action-noatuntiny.png
new file mode 100644
index 00000000..74bc5e94
--- /dev/null
+++ b/noatun/pics/cr22-action-noatuntiny.png
Binary files differ
diff --git a/noatun/pics/cr22-action-playlist.png b/noatun/pics/cr22-action-playlist.png
new file mode 100644
index 00000000..ec3006cf
--- /dev/null
+++ b/noatun/pics/cr22-action-playlist.png
Binary files differ
diff --git a/noatun/pics/cr32-action-effect.png b/noatun/pics/cr32-action-effect.png
new file mode 100644
index 00000000..e983d952
--- /dev/null
+++ b/noatun/pics/cr32-action-effect.png
Binary files differ
diff --git a/noatun/pics/cr32-action-equalizer.png b/noatun/pics/cr32-action-equalizer.png
new file mode 100644
index 00000000..383f7ce5
--- /dev/null
+++ b/noatun/pics/cr32-action-equalizer.png
Binary files differ
diff --git a/noatun/pics/cr32-action-playlist.png b/noatun/pics/cr32-action-playlist.png
new file mode 100644
index 00000000..a0d02f56
--- /dev/null
+++ b/noatun/pics/cr32-action-playlist.png
Binary files differ
diff --git a/noatun/pics/cr48-action-effect.png b/noatun/pics/cr48-action-effect.png
new file mode 100644
index 00000000..1394861e
--- /dev/null
+++ b/noatun/pics/cr48-action-effect.png
Binary files differ
diff --git a/noatun/pics/cr48-action-equalizer.png b/noatun/pics/cr48-action-equalizer.png
new file mode 100644
index 00000000..c0d75a68
--- /dev/null
+++ b/noatun/pics/cr48-action-equalizer.png
Binary files differ
diff --git a/noatun/pics/cr48-action-playlist.png b/noatun/pics/cr48-action-playlist.png
new file mode 100644
index 00000000..e9b51c89
--- /dev/null
+++ b/noatun/pics/cr48-action-playlist.png
Binary files differ
diff --git a/noatun/pics/cr64-action-equalizer.png b/noatun/pics/cr64-action-equalizer.png
new file mode 100644
index 00000000..39999c34
--- /dev/null
+++ b/noatun/pics/cr64-action-equalizer.png
Binary files differ
diff --git a/noatun/pics/cr64-action-playlist.png b/noatun/pics/cr64-action-playlist.png
new file mode 100644
index 00000000..4277fdaa
--- /dev/null
+++ b/noatun/pics/cr64-action-playlist.png
Binary files differ
diff --git a/noatun/preset.dance b/noatun/preset.dance
new file mode 100644
index 00000000..89ba2557
--- /dev/null
+++ b/noatun/preset.dance
@@ -0,0 +1,8 @@
+<!DOCTYPE NoatunEqualizer ><noatunequalizer version="1.1.354" name="Dance" level="-100" default="1" >
+ <band end="108" level="149" start="0" />
+ <band end="217" level="122" start="109" />
+ <band end="810" level="105" start="218" />
+ <band end="2431" level="109" start="811" />
+ <band end="7290" level="135" start="2432" />
+ <band end="19999" level="164" start="7291" />
+</noatunequalizer>
diff --git a/noatun/preset.eclecticguitar b/noatun/preset.eclecticguitar
new file mode 100644
index 00000000..bc41ce8d
--- /dev/null
+++ b/noatun/preset.eclecticguitar
@@ -0,0 +1,9 @@
+<!DOCTYPE NoatunEqualizer>
+<noatunequalizer version="1.1.354" level="-150" name="Eclectic Guitar" default="1">
+ <band end="108" start="0" level="-66" />
+ <band end="217" start="109" level="-56" />
+ <band end="810" start="218" level="38" />
+ <band end="2431" start="811" level="65" />
+ <band end="7290" start="2432" level="84" />
+ <band end="19999" start="7291" level="70" />
+</noatunequalizer>
diff --git a/noatun/preset.jazz b/noatun/preset.jazz
new file mode 100644
index 00000000..a20ed11e
--- /dev/null
+++ b/noatun/preset.jazz
@@ -0,0 +1,8 @@
+<!DOCTYPE NoatunEqualizer ><noatunequalizer version="1.1.354" name="Jazz" level="0" default="1" >
+ <band end="108" start="0" level="181" />
+ <band end="217" start="109" level="154" />
+ <band end="810" start="218" level="93" />
+ <band end="2431" start="811" level="130" />
+ <band end="7290" start="2432" level="160" />
+ <band end="19999" start="7291" level="177" />
+</noatunequalizer>
diff --git a/noatun/preset.metal b/noatun/preset.metal
new file mode 100644
index 00000000..747fe9eb
--- /dev/null
+++ b/noatun/preset.metal
@@ -0,0 +1,8 @@
+<!DOCTYPE NoatunEqualizer ><noatunequalizer version="1.1.354" name="Metal" level="-100" default="1" >
+ <band end="108" start="0" level="162" />
+ <band end="217" start="109" level="135" />
+ <band end="810" start="218" level="110" />
+ <band end="2431" start="811" level="124" />
+ <band end="7290" start="2432" level="149" />
+ <band end="19999" start="7291" level="175" />
+</noatunequalizer>
diff --git a/noatun/preset.trance b/noatun/preset.trance
new file mode 100644
index 00000000..78a38221
--- /dev/null
+++ b/noatun/preset.trance
@@ -0,0 +1,8 @@
+<!DOCTYPE NoatunEqualizer ><noatunequalizer version="1.1.354" name="Trance" level="-100" default="1" >
+ <band end="108" start="0" level="162" />
+ <band end="217" start="109" level="103" />
+ <band end="810" start="218" level="162" />
+ <band end="2431" start="811" level="112" />
+ <band end="7290" start="2432" level="177" />
+ <band end="19999" start="7291" level="112" />
+</noatunequalizer>
diff --git a/noatun/preset.zero b/noatun/preset.zero
new file mode 100644
index 00000000..82380e9a
--- /dev/null
+++ b/noatun/preset.zero
@@ -0,0 +1,9 @@
+<!DOCTYPE NoatunEqualizer>
+<noatunequalizer version="2.4.0" name="Zero" level="0" default="1" >
+ <band end="5" start="1" level="0" />
+ <band end="27" start="6" level="0" />
+ <band end="141" start="28" level="0" />
+ <band end="736" start="142" level="0" />
+ <band end="3835" start="737" level="0" />
+ <band end="19979" start="3836" level="0" />
+</noatunequalizer>