summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Dashevskiy <watcherhd@gmail.com>2012-10-08 18:43:29 +0000
committerVadim Dashevskiy <watcherhd@gmail.com>2012-10-08 18:43:29 +0000
commit864081102a5f252415f41950b3039a896b4ae9c5 (patch)
treec6b764651e9dd1f8f53b98eab05f16ba4a492a79
parentdb5149b48346c417e18add5702a9dfe7f6e28dd0 (diff)
Awkwars's plugins - welcome to our trunk
git-svn-id: http://svn.miranda-ng.org/main/trunk@1822 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
-rw-r--r--plugins/Actman/actman.dpr189
-rw-r--r--plugins/Actman/global.pas21
-rw-r--r--plugins/Actman/hooks/hooks.pas73
-rw-r--r--plugins/Actman/hooks/hooks.rc28
-rw-r--r--plugins/Actman/hooks/hooks.resbin0 -> 688 bytes
-rw-r--r--plugins/Actman/hooks/i_hconst.inc20
-rw-r--r--plugins/Actman/hooks/i_hook.inc154
-rw-r--r--plugins/Actman/hooks/i_opt_dlg.inc410
-rw-r--r--plugins/Actman/hooks/i_options.inc71
-rw-r--r--plugins/Actman/i_action.inc952
-rw-r--r--plugins/Actman/i_actlow.inc836
-rw-r--r--plugins/Actman/i_const.inc219
-rw-r--r--plugins/Actman/i_contact.inc113
-rw-r--r--plugins/Actman/i_dlglists.inc75
-rw-r--r--plugins/Actman/i_inoutxm.inc1180
-rw-r--r--plugins/Actman/i_opt_dlg.inc215
-rw-r--r--plugins/Actman/i_opt_dlg2.inc2109
-rw-r--r--plugins/Actman/i_options.inc459
-rw-r--r--plugins/Actman/i_services.inc131
-rw-r--r--plugins/Actman/i_vars.inc31
-rw-r--r--plugins/Actman/i_visual.inc1073
-rw-r--r--plugins/Actman/ico/advance.icobin0 -> 2550 bytes
-rw-r--r--plugins/Actman/ico/apply.icobin0 -> 1406 bytes
-rw-r--r--plugins/Actman/ico/chain.icobin0 -> 2550 bytes
-rw-r--r--plugins/Actman/ico/contact.icobin0 -> 2550 bytes
-rw-r--r--plugins/Actman/ico/delete.icobin0 -> 2550 bytes
-rw-r--r--plugins/Actman/ico/down.icobin0 -> 2550 bytes
-rw-r--r--plugins/Actman/ico/export.icobin0 -> 2550 bytes
-rw-r--r--plugins/Actman/ico/format.icobin0 -> 2550 bytes
-rw-r--r--plugins/Actman/ico/import.icobin0 -> 2550 bytes
-rw-r--r--plugins/Actman/ico/insert.icobin0 -> 2550 bytes
-rw-r--r--plugins/Actman/ico/message.icobin0 -> 2550 bytes
-rw-r--r--plugins/Actman/ico/new.icobin0 -> 2550 bytes
-rw-r--r--plugins/Actman/ico/program.icobin0 -> 2550 bytes
-rw-r--r--plugins/Actman/ico/reload.icobin0 -> 2550 bytes
-rw-r--r--plugins/Actman/ico/rw.icobin0 -> 2550 bytes
-rw-r--r--plugins/Actman/ico/service.icobin0 -> 2550 bytes
-rw-r--r--plugins/Actman/ico/test.icobin0 -> 2550 bytes
-rw-r--r--plugins/Actman/ico/up.icobin0 -> 2550 bytes
-rw-r--r--plugins/Actman/ico/vcheck.icobin0 -> 350 bytes
-rw-r--r--plugins/Actman/ico/vuncheck.icobin0 -> 350 bytes
-rw-r--r--plugins/Actman/m_actions.inc193
-rw-r--r--plugins/Actman/m_actman.h96
-rw-r--r--plugins/Actman/m_actman.inc158
-rw-r--r--plugins/Actman/make.bat20
-rw-r--r--plugins/Actman/options.rc328
-rw-r--r--plugins/Actman/options.resbin0 -> 55904 bytes
-rw-r--r--plugins/Actman/question.pas51
-rw-r--r--plugins/Actman/readme.txt126
-rw-r--r--plugins/Actman/services.ini497
-rw-r--r--plugins/Actman/tasks/i_opt_dlg.inc536
-rw-r--r--plugins/Actman/tasks/i_options.inc99
-rw-r--r--plugins/Actman/tasks/i_service.inc87
-rw-r--r--plugins/Actman/tasks/i_task.inc242
-rw-r--r--plugins/Actman/tasks/i_tconst.inc27
-rw-r--r--plugins/Actman/tasks/scheduler.pas86
-rw-r--r--plugins/Actman/tasks/tasks.rc47
-rw-r--r--plugins/Actman/tasks/tasks.resbin0 -> 1288 bytes
-rw-r--r--plugins/Actman/ua/action.icobin0 -> 2550 bytes
-rw-r--r--plugins/Actman/ua/i_inoutxm.inc357
-rw-r--r--plugins/Actman/ua/i_opt_dlg.inc571
-rw-r--r--plugins/Actman/ua/i_options.inc337
-rw-r--r--plugins/Actman/ua/i_ua.inc155
-rw-r--r--plugins/Actman/ua/i_uaplaces.inc831
-rw-r--r--plugins/Actman/ua/i_uavars.inc124
-rw-r--r--plugins/Actman/ua/i_uconst.inc34
-rw-r--r--plugins/Actman/ua/ua.pas124
-rw-r--r--plugins/Actman/ua/ua.rc51
-rw-r--r--plugins/Actman/ua/ua.resbin0 -> 3944 bytes
-rw-r--r--plugins/Libs/ActiveKOL.pas2649
-rw-r--r--plugins/Libs/BASS_DSHOW.pas275
-rw-r--r--plugins/Libs/Dynamic_Bass.pas1298
-rw-r--r--plugins/Libs/FastMM4.pas11698
-rw-r--r--plugins/Libs/FastMM4Messages.pas135
-rw-r--r--plugins/Libs/FastMM4Options.inc426
-rw-r--r--plugins/Libs/KOLCCtrls.pas1780
-rw-r--r--plugins/Libs/KOLDEF.inc308
-rw-r--r--plugins/Libs/KOL_ASM.inc15855
-rw-r--r--plugins/Libs/KOL_ASM_NOUNICODE.inc4351
-rw-r--r--plugins/Libs/KOL_ansi.inc2316
-rw-r--r--plugins/Libs/KOL_unicode.inc1277
-rw-r--r--plugins/Libs/KolZLibBzip.pas1940
-rw-r--r--plugins/Libs/MCKfakeClasses.inc79
-rw-r--r--plugins/Libs/MCKfakeClasses200x.inc51
-rw-r--r--plugins/Libs/MsgDecode.pas4957
-rw-r--r--plugins/Libs/PsAPI.pas399
-rw-r--r--plugins/Libs/bz2/BLOCKS~1.OBJbin0 -> 11771 bytes
-rw-r--r--plugins/Libs/bz2/BZLIB.OBJbin0 -> 11596 bytes
-rw-r--r--plugins/Libs/bz2/COMPRESS.OBJbin0 -> 14175 bytes
-rw-r--r--plugins/Libs/bz2/DECOMP~1.OBJbin0 -> 15653 bytes
-rw-r--r--plugins/Libs/bz2/HUFFMAN.OBJbin0 -> 6463 bytes
-rw-r--r--plugins/Libs/delphicommctrl.inc1594
-rw-r--r--plugins/Libs/dynbasswma.pas249
-rw-r--r--plugins/Libs/err.pas1199
-rw-r--r--plugins/Libs/kol.pas61873
-rw-r--r--plugins/Libs/kolcomobj.pas2352
-rw-r--r--plugins/Libs/kolmath.pas1845
-rw-r--r--plugins/Libs/make.bat14
-rw-r--r--plugins/Libs/visual_xp_styles.inc1448
-rw-r--r--plugins/Libs/zlib/Infblock.objbin0 -> 5570 bytes
-rw-r--r--plugins/Libs/zlib/Infcodes.objbin0 -> 3817 bytes
-rw-r--r--plugins/Libs/zlib/Infutil.objbin0 -> 1554 bytes
-rw-r--r--plugins/Libs/zlib/adler32.objbin0 -> 529 bytes
-rw-r--r--plugins/Libs/zlib/compress.objbin0 -> 559 bytes
-rw-r--r--plugins/Libs/zlib/crc32.objbin0 -> 11189 bytes
-rw-r--r--plugins/Libs/zlib/deflate.objbin0 -> 8175 bytes
-rw-r--r--plugins/Libs/zlib/infback.objbin0 -> 7736 bytes
-rw-r--r--plugins/Libs/zlib/inffast.objbin0 -> 2394 bytes
-rw-r--r--plugins/Libs/zlib/inflate.objbin0 -> 10775 bytes
-rw-r--r--plugins/Libs/zlib/inftrees.objbin0 -> 2408 bytes
-rw-r--r--plugins/Libs/zlib/trees.objbin0 -> 11757 bytes
-rw-r--r--plugins/Libs/zlib/uncompr.objbin0 -> 496 bytes
-rw-r--r--plugins/QuickSearch/ico/default.icobin0 -> 2550 bytes
-rw-r--r--plugins/QuickSearch/ico/delete.icobin0 -> 1406 bytes
-rw-r--r--plugins/QuickSearch/ico/down.icobin0 -> 2550 bytes
-rw-r--r--plugins/QuickSearch/ico/female.icobin0 -> 2550 bytes
-rw-r--r--plugins/QuickSearch/ico/item.icobin0 -> 1406 bytes
-rw-r--r--plugins/QuickSearch/ico/male.icobin0 -> 2550 bytes
-rw-r--r--plugins/QuickSearch/ico/new.icobin0 -> 1406 bytes
-rw-r--r--plugins/QuickSearch/ico/qs.icobin0 -> 1406 bytes
-rw-r--r--plugins/QuickSearch/ico/reload.icobin0 -> 2550 bytes
-rw-r--r--plugins/QuickSearch/ico/up.icobin0 -> 2550 bytes
-rw-r--r--plugins/QuickSearch/make.bat17
-rw-r--r--plugins/QuickSearch/qs.rc178
-rw-r--r--plugins/QuickSearch/qs.resbin0 -> 26276 bytes
-rw-r--r--plugins/QuickSearch/quicksearch.dpr244
-rw-r--r--plugins/QuickSearch/quicksearch_history.txt110
-rw-r--r--plugins/QuickSearch/resource.inc76
-rw-r--r--plugins/QuickSearch/sr_frame.pas343
-rw-r--r--plugins/QuickSearch/sr_global.pas750
-rw-r--r--plugins/QuickSearch/sr_optdialog.pas1041
-rw-r--r--plugins/QuickSearch/sr_window.pas2943
-rw-r--r--plugins/Utils.pas/TextBlock.pas335
-rw-r--r--plugins/Utils.pas/appcmdapi.pas97
-rw-r--r--plugins/Utils.pas/base64.pas108
-rw-r--r--plugins/Utils.pas/cbex.pas79
-rw-r--r--plugins/Utils.pas/common.pas2409
-rw-r--r--plugins/Utils.pas/compilers.inc778
-rw-r--r--plugins/Utils.pas/dbsettings.pas481
-rw-r--r--plugins/Utils.pas/i_card_const.inc16
-rw-r--r--plugins/Utils.pas/i_struct_const.inc44
-rw-r--r--plugins/Utils.pas/icobuttons.pas392
-rw-r--r--plugins/Utils.pas/io.pas249
-rw-r--r--plugins/Utils.pas/kolsizer.pas538
-rw-r--r--plugins/Utils.pas/mApiCardM.pas404
-rw-r--r--plugins/Utils.pas/mApicard.rc39
-rw-r--r--plugins/Utils.pas/mApicard.resbin0 -> 1052 bytes
-rw-r--r--plugins/Utils.pas/make.bat14
-rw-r--r--plugins/Utils.pas/memini.pas514
-rw-r--r--plugins/Utils.pas/mirutils.pas1163
-rw-r--r--plugins/Utils.pas/msninfo.pas182
-rw-r--r--plugins/Utils.pas/old/hotkeys.pas574
-rw-r--r--plugins/Utils.pas/old/ini.pas857
-rw-r--r--plugins/Utils.pas/old/mApiCardC.pas399
-rw-r--r--plugins/Utils.pas/playlist.pas480
-rw-r--r--plugins/Utils.pas/protocols.pas610
-rw-r--r--plugins/Utils.pas/sedit.pas1331
-rw-r--r--plugins/Utils.pas/strans.pas828
-rw-r--r--plugins/Utils.pas/structopts.rc83
-rw-r--r--plugins/Utils.pas/structopts.resbin0 -> 12348 bytes
-rw-r--r--plugins/Utils.pas/syswin.pas725
-rw-r--r--plugins/Utils.pas/tb_chunk.inc640
-rw-r--r--plugins/Utils.pas/utils.pas44
-rw-r--r--plugins/Utils.pas/wrapdlgs.pas130
-rw-r--r--plugins/Utils.pas/wrapper.pas513
-rw-r--r--plugins/Utils.pas/zwrapper.pas58
-rw-r--r--plugins/Watrack/HlpDlg.pas83
-rw-r--r--plugins/Watrack/docs/const.php157
-rw-r--r--plugins/Watrack/docs/m_music.h386
-rw-r--r--plugins/Watrack/docs/m_music.inc404
-rw-r--r--plugins/Watrack/docs/sampledll.dpr139
-rw-r--r--plugins/Watrack/docs/wat.php28
-rw-r--r--plugins/Watrack/docs/watrack_history.txt519
-rw-r--r--plugins/Watrack/docs/watrack_readme.txt110
-rw-r--r--plugins/Watrack/formats/fmt_aac.pas93
-rw-r--r--plugins/Watrack/formats/fmt_ape.pas137
-rw-r--r--plugins/Watrack/formats/fmt_avi.pas295
-rw-r--r--plugins/Watrack/formats/fmt_dummy.pas46
-rw-r--r--plugins/Watrack/formats/fmt_flv.pas334
-rw-r--r--plugins/Watrack/formats/fmt_m4a.pas378
-rw-r--r--plugins/Watrack/formats/fmt_mkv.pas235
-rw-r--r--plugins/Watrack/formats/fmt_mp3.pas460
-rw-r--r--plugins/Watrack/formats/fmt_mpc.pas90
-rw-r--r--plugins/Watrack/formats/fmt_ofr.pas74
-rw-r--r--plugins/Watrack/formats/fmt_ogg.pas522
-rw-r--r--plugins/Watrack/formats/fmt_real.pas335
-rw-r--r--plugins/Watrack/formats/fmt_tta.pas65
-rw-r--r--plugins/Watrack/formats/fmt_wav.pas146
-rw-r--r--plugins/Watrack/formats/fmt_wma.pas438
-rw-r--r--plugins/Watrack/formats/tag_apev2.inc124
-rw-r--r--plugins/Watrack/formats/tag_id3v1.inc175
-rw-r--r--plugins/Watrack/formats/tag_id3v2.inc545
-rw-r--r--plugins/Watrack/formats/tags.pas21
-rw-r--r--plugins/Watrack/global.pas86
-rw-r--r--plugins/Watrack/i_cover.inc90
-rw-r--r--plugins/Watrack/i_gui.inc114
-rw-r--r--plugins/Watrack/i_opt_0.inc91
-rw-r--r--plugins/Watrack/i_opt_1.inc256
-rw-r--r--plugins/Watrack/i_opt_dlg.inc57
-rw-r--r--plugins/Watrack/i_options.inc171
-rw-r--r--plugins/Watrack/i_timer.inc26
-rw-r--r--plugins/Watrack/i_vars.inc37
-rw-r--r--plugins/Watrack/icons/GO/GoAsm.Exebin0 -> 124416 bytes
-rw-r--r--plugins/Watrack/icons/GO/GoLink.exebin0 -> 48640 bytes
-rw-r--r--plugins/Watrack/icons/GO/GoRC.exebin0 -> 54784 bytes
-rw-r--r--plugins/Watrack/icons/GO/icons.bat8
-rw-r--r--plugins/Watrack/icons/GO/icons.rc58
-rw-r--r--plugins/Watrack/icons/GO/waticons.h35
-rw-r--r--plugins/Watrack/icons/GO/watrack_buttons.asm5
-rw-r--r--plugins/Watrack/icons/MASM/icons.bat8
-rw-r--r--plugins/Watrack/icons/MASM/icons.rc58
-rw-r--r--plugins/Watrack/icons/MASM/iconspl.rc83
-rw-r--r--plugins/Watrack/icons/MASM/poasm.exebin0 -> 653488 bytes
-rw-r--r--plugins/Watrack/icons/MASM/polink.exebin0 -> 164528 bytes
-rw-r--r--plugins/Watrack/icons/MASM/porc.dllbin0 -> 146096 bytes
-rw-r--r--plugins/Watrack/icons/MASM/porc.exebin0 -> 30208 bytes
-rw-r--r--plugins/Watrack/icons/MASM/waticons.h35
-rw-r--r--plugins/Watrack/icons/MASM/watrack.asm5
-rw-r--r--plugins/Watrack/icons/TASM/RLINK32.DLLbin0 -> 59904 bytes
-rw-r--r--plugins/Watrack/icons/TASM/TASM32.EXEbin0 -> 188416 bytes
-rw-r--r--plugins/Watrack/icons/TASM/TLINK32.EXEbin0 -> 208896 bytes
-rw-r--r--plugins/Watrack/icons/TASM/brcc32.exebin0 -> 169008 bytes
-rw-r--r--plugins/Watrack/icons/TASM/icons.bat9
-rw-r--r--plugins/Watrack/icons/TASM/icons.rc58
-rw-r--r--plugins/Watrack/icons/TASM/iconspl.rc61
-rw-r--r--plugins/Watrack/icons/TASM/rw32core.dllbin0 -> 812576 bytes
-rw-r--r--plugins/Watrack/icons/TASM/waticons.h35
-rw-r--r--plugins/Watrack/icons/TASM/watrack.asm8
-rw-r--r--plugins/Watrack/icons/iconsets/players/1by1.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/AIMP.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/Ashampoo Media Player.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/BeholdTV.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/Billy.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/Core Media Player.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/Crystal Player.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/Cyberlink PowerDVD.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/Evil Player.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/GOMPlayer.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/Helium Music Manager.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/J.River Media Center.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/JetAudio.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/KMPlayer.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/LastFM.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/MediaMonkey.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/MoreAmp.icobin0 -> 2862 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/MusikCube.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/QCDPlayer.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/Quicktime Player.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/RadLight.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/Real Player.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/Spider Player.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/Ultra player.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/VUPlayer.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/WMP 9.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/WinDVD.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/XMPlay.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/Zoom Player.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/alshow.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/alsong.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/apollo.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/audio.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/bsplayer.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/cms.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/flv.icobin0 -> 1406 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/foobar2000.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/itunes.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/la.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/mcone.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/mcx.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/mmatch.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/mpc.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/mplayer.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/pluton.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/saps.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/songbird.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/vlc.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/vp3.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/wany.icobin0 -> 1406 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/wifi.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/players/winamp.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/disable.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/enable.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/next.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/next_hovered.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/next_pressed.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/pause.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/pause_hovered.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/pause_pressed.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/play.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/play_hovered.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/play_pressed.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/previous.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/previous_hovered.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/previous_pressed.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/slider.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/slider_hovered.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/slider_pressed.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/stop.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/stop_hovered.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/stop_pressed.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/volume_down.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/volume_down_hovered.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/volume_down_pressed.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/volume_up.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/volume_up_hovered.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true+256-solid/volume_up_pressed.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/next.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/next_hovered.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/next_pressed.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/pause.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/pause_hovered.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/pause_pressed.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/play.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/play_hovered.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/play_pressed.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/previous.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/previous_hovered.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/previous_pressed.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/slider.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/stop.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/stop_hovered.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/stop_pressed.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/ver.resbin0 -> 744 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/volume_down.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/volume_down_hovered.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/volume_down_pressed.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/volume_up.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/volume_up_hovered.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/icons/iconsets/true-solid-faith/volume_up_pressed.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/icons/make-buttons.bat4
-rw-r--r--plugins/Watrack/icons/make-players.bat2
-rw-r--r--plugins/Watrack/icons/make.bat16
-rw-r--r--plugins/Watrack/icons/waticons.inc35
-rw-r--r--plugins/Watrack/kolframe/frm.rc84
-rw-r--r--plugins/Watrack/kolframe/frm.resbin0 -> 3032 bytes
-rw-r--r--plugins/Watrack/kolframe/frm_data.inc37
-rw-r--r--plugins/Watrack/kolframe/frm_designer.inc164
-rw-r--r--plugins/Watrack/kolframe/frm_dlg1.inc283
-rw-r--r--plugins/Watrack/kolframe/frm_dlg2.inc172
-rw-r--r--plugins/Watrack/kolframe/frm_frame.inc497
-rw-r--r--plugins/Watrack/kolframe/frm_icogroup.inc115
-rw-r--r--plugins/Watrack/kolframe/frm_rc.inc56
-rw-r--r--plugins/Watrack/kolframe/frm_text.inc90
-rw-r--r--plugins/Watrack/kolframe/frm_trackbar.inc229
-rw-r--r--plugins/Watrack/kolframe/frm_vars.inc80
-rw-r--r--plugins/Watrack/kolframe/i_bitmap.inc290
-rw-r--r--plugins/Watrack/kolframe/kolframe.pas327
-rw-r--r--plugins/Watrack/lastfm/i_const.inc17
-rw-r--r--plugins/Watrack/lastfm/i_last_api.inc599
-rw-r--r--plugins/Watrack/lastfm/i_last_dlg.inc120
-rw-r--r--plugins/Watrack/lastfm/i_last_opt.inc44
-rw-r--r--plugins/Watrack/lastfm/lastfm.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/lastfm/lastfm.pas300
-rw-r--r--plugins/Watrack/lastfm/lastfm.rc38
-rw-r--r--plugins/Watrack/lastfm/lastfm.resbin0 -> 3720 bytes
-rw-r--r--plugins/Watrack/lst_formats.inc16
-rw-r--r--plugins/Watrack/lst_players.inc17
-rw-r--r--plugins/Watrack/m_music.inc419
-rw-r--r--plugins/Watrack/macros.pas93
-rw-r--r--plugins/Watrack/make.bat26
-rw-r--r--plugins/Watrack/myrtf.pas219
-rw-r--r--plugins/Watrack/myshows/i_const.inc14
-rw-r--r--plugins/Watrack/myshows/i_cookies.inc91
-rw-r--r--plugins/Watrack/myshows/i_myshows_api.inc247
-rw-r--r--plugins/Watrack/myshows/i_myshows_dlg.inc111
-rw-r--r--plugins/Watrack/myshows/i_myshows_opt.inc47
-rw-r--r--plugins/Watrack/myshows/myshows.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/myshows/myshows.pas333
-rw-r--r--plugins/Watrack/myshows/myshows.rc41
-rw-r--r--plugins/Watrack/myshows/myshows.resbin0 -> 1728 bytes
-rw-r--r--plugins/Watrack/player.ini283
-rw-r--r--plugins/Watrack/players/mradio.icobin0 -> 1150 bytes
-rw-r--r--plugins/Watrack/players/mradio.rc3
-rw-r--r--plugins/Watrack/players/mradio.resbin0 -> 1264 bytes
-rw-r--r--plugins/Watrack/players/pl_1by1.pas84
-rw-r--r--plugins/Watrack/players/pl_aimp.pas376
-rw-r--r--plugins/Watrack/players/pl_apollo.pas263
-rw-r--r--plugins/Watrack/players/pl_behold.pas175
-rw-r--r--plugins/Watrack/players/pl_bs.pas252
-rw-r--r--plugins/Watrack/players/pl_cowon.pas392
-rw-r--r--plugins/Watrack/players/pl_foobar.pas534
-rw-r--r--plugins/Watrack/players/pl_itunes.pas392
-rw-r--r--plugins/Watrack/players/pl_la.pas141
-rw-r--r--plugins/Watrack/players/pl_lastfm.pas129
-rw-r--r--plugins/Watrack/players/pl_mmonkey.pas181
-rw-r--r--plugins/Watrack/players/pl_mpc.pas117
-rw-r--r--plugins/Watrack/players/pl_mradio.pas345
-rw-r--r--plugins/Watrack/players/pl_vlc.pas380
-rw-r--r--plugins/Watrack/players/pl_winamp.pas170
-rw-r--r--plugins/Watrack/players/pl_wmp.pas128
-rw-r--r--plugins/Watrack/plugins/watrack_mpd/Makefile12
-rw-r--r--plugins/Watrack/plugins/watrack_mpd/res/watrack_mpd.rc112
-rw-r--r--plugins/Watrack/plugins/watrack_mpd/src/commonheaders.h43
-rw-r--r--plugins/Watrack/plugins/watrack_mpd/src/constants.h4
-rw-r--r--plugins/Watrack/plugins/watrack_mpd/src/globals.h11
-rw-r--r--plugins/Watrack/plugins/watrack_mpd/src/init.c112
-rw-r--r--plugins/Watrack/plugins/watrack_mpd/src/m_music.h355
-rw-r--r--plugins/Watrack/plugins/watrack_mpd/src/main.c433
-rw-r--r--plugins/Watrack/plugins/watrack_mpd/src/main.h24
-rw-r--r--plugins/Watrack/plugins/watrack_mpd/src/options.c91
-rw-r--r--plugins/Watrack/plugins/watrack_mpd/src/resource.h32
-rw-r--r--plugins/Watrack/plugins/watrack_mpd/src/utilities.c126
-rw-r--r--plugins/Watrack/plugins/watrack_mpd/src/utilities.h6
-rw-r--r--plugins/Watrack/plugins/watrack_mpd/watrack_mpd.vcxproj299
-rw-r--r--plugins/Watrack/plugins/watrack_mpd/watrack_mpd.vcxproj.filters56
-rw-r--r--plugins/Watrack/popup/pop_dlg.inc179
-rw-r--r--plugins/Watrack/popup/pop_opt.inc81
-rw-r--r--plugins/Watrack/popup/pop_rc.inc34
-rw-r--r--plugins/Watrack/popup/pop_vars.inc27
-rw-r--r--plugins/Watrack/popup/popup.rc55
-rw-r--r--plugins/Watrack/popup/popup.resbin0 -> 3352 bytes
-rw-r--r--plugins/Watrack/popup/popups.pas542
-rw-r--r--plugins/Watrack/popup/wat_info.icobin0 -> 1406 bytes
-rw-r--r--plugins/Watrack/proto/i_proto_dlg.inc144
-rw-r--r--plugins/Watrack/proto/i_proto_opt.inc35
-rw-r--r--plugins/Watrack/proto/i_proto_rc.inc17
-rw-r--r--plugins/Watrack/proto/proto.pas564
-rw-r--r--plugins/Watrack/proto/proto.rc36
-rw-r--r--plugins/Watrack/proto/proto.resbin0 -> 2624 bytes
-rw-r--r--plugins/Watrack/proto/wat_context.icobin0 -> 1406 bytes
-rw-r--r--plugins/Watrack/res/i_const.inc27
-rw-r--r--plugins/Watrack/res/wat_disable.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/res/wat_enable.icobin0 -> 2550 bytes
-rw-r--r--plugins/Watrack/res/watrack.rc104
-rw-r--r--plugins/Watrack/res/watrack.resbin0 -> 8520 bytes
-rw-r--r--plugins/Watrack/srv_format.pas394
-rw-r--r--plugins/Watrack/srv_player.pas1220
-rw-r--r--plugins/Watrack/stat/default.tmpl89
-rw-r--r--plugins/Watrack/stat/report.inc315
-rw-r--r--plugins/Watrack/stat/stat.rc50
-rw-r--r--plugins/Watrack/stat/stat.resbin0 -> 3320 bytes
-rw-r--r--plugins/Watrack/stat/stat_data.inc16
-rw-r--r--plugins/Watrack/stat/stat_dlg.inc223
-rw-r--r--plugins/Watrack/stat/stat_opt.inc62
-rw-r--r--plugins/Watrack/stat/stat_rc.inc29
-rw-r--r--plugins/Watrack/stat/stat_vars.inc21
-rw-r--r--plugins/Watrack/stat/statlog.pas650
-rw-r--r--plugins/Watrack/stat/wat_report.icobin0 -> 1406 bytes
-rw-r--r--plugins/Watrack/status/i_hotkey.inc62
-rw-r--r--plugins/Watrack/status/i_opt_11.inc459
-rw-r--r--plugins/Watrack/status/i_opt_12.inc108
-rw-r--r--plugins/Watrack/status/i_opt_3.inc106
-rw-r--r--plugins/Watrack/status/i_opt_status.inc49
-rw-r--r--plugins/Watrack/status/i_opt_tmpl.inc244
-rw-r--r--plugins/Watrack/status/i_st_rc.inc45
-rw-r--r--plugins/Watrack/status/i_st_vars.inc26
-rw-r--r--plugins/Watrack/status/i_status.inc223
-rw-r--r--plugins/Watrack/status/status.pas142
-rw-r--r--plugins/Watrack/status/status.rc88
-rw-r--r--plugins/Watrack/status/status.resbin0 -> 3092 bytes
-rw-r--r--plugins/Watrack/status/tmpl.pas304
-rw-r--r--plugins/Watrack/templates/i_expkey.inc34
-rw-r--r--plugins/Watrack/templates/i_macro.inc149
-rw-r--r--plugins/Watrack/templates/i_opt_it.inc50
-rw-r--r--plugins/Watrack/templates/i_text.inc135
-rw-r--r--plugins/Watrack/templates/i_tmpl_dlg.inc117
-rw-r--r--plugins/Watrack/templates/i_tmpl_rc.inc21
-rw-r--r--plugins/Watrack/templates/i_variables.inc185
-rw-r--r--plugins/Watrack/templates/templates.pas113
-rw-r--r--plugins/Watrack/templates/templates.rc51
-rw-r--r--plugins/Watrack/templates/templates.resbin0 -> 1648 bytes
-rw-r--r--plugins/Watrack/wat_api.pas183
-rw-r--r--plugins/Watrack/waticons.inc35
-rw-r--r--plugins/Watrack/waticons.pas202
-rw-r--r--plugins/Watrack/watrack.dpr675
-rw-r--r--plugins/Watrack/winampapi.pas277
-rw-r--r--plugins/mRadio/activex.pp4173
-rw-r--r--plugins/mRadio/i_bass.inc940
-rw-r--r--plugins/mRadio/i_cc.inc433
-rw-r--r--plugins/mRadio/i_frame.inc242
-rw-r--r--plugins/mRadio/i_frameapi.inc234
-rw-r--r--plugins/mRadio/i_hotkey.inc61
-rw-r--r--plugins/mRadio/i_myservice.inc178
-rw-r--r--plugins/mRadio/i_optdlg.inc962
-rw-r--r--plugins/mRadio/i_search.inc444
-rw-r--r--plugins/mRadio/i_service.inc241
-rw-r--r--plugins/mRadio/i_tray.inc228
-rw-r--r--plugins/mRadio/i_tray_api.inc125
-rw-r--r--plugins/mRadio/i_variables.inc82
-rw-r--r--plugins/mRadio/i_vars.inc184
-rw-r--r--plugins/mRadio/i_visual.inc115
-rw-r--r--plugins/mRadio/ico/delete.icobin0 -> 2550 bytes
-rw-r--r--plugins/mRadio/ico/mradio.icobin0 -> 318 bytes
-rw-r--r--plugins/mRadio/ico/new.icobin0 -> 2550 bytes
-rw-r--r--plugins/mRadio/ico/off.icobin0 -> 2038 bytes
-rw-r--r--plugins/mRadio/ico/on.icobin0 -> 2038 bytes
-rw-r--r--plugins/mRadio/ico/recoff.icobin0 -> 1406 bytes
-rw-r--r--plugins/mRadio/ico/recon.icobin0 -> 1406 bytes
-rw-r--r--plugins/mRadio/m_radio.h131
-rw-r--r--plugins/mRadio/m_radio.inc126
-rw-r--r--plugins/mRadio/make.bat17
-rw-r--r--plugins/mRadio/mr_rc.inc87
-rw-r--r--plugins/mRadio/mradio.dpr350
-rw-r--r--plugins/mRadio/mradio.rc182
-rw-r--r--plugins/mRadio/mradio.resbin0 -> 18720 bytes
-rw-r--r--plugins/mRadio/readme.txt147
-rw-r--r--plugins/mRadio/variants.pas7
497 files changed, 192473 insertions, 0 deletions
diff --git a/plugins/Actman/actman.dpr b/plugins/Actman/actman.dpr
new file mode 100644
index 0000000000..caf0319c6a
--- /dev/null
+++ b/plugins/Actman/actman.dpr
@@ -0,0 +1,189 @@
+{$include compilers.inc}
+{$IFDEF COMPILER_16_UP}
+ {$WEAKLINKRTTI ON}
+ {.$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([])}
+{$ENDIF}
+{$IMAGEBASE $13200000}
+library actman;
+{%ToDo 'actman.todo'}
+{%File 'i_actlow.inc'}
+{%File 'm_actions.inc'}
+{%File 'm_actman.inc'}
+{%File 'i_action.inc'}
+{%File 'i_const.inc'}
+{%File 'i_contact.inc'}
+{%File 'i_opt_dlg2.inc'}
+{%File 'i_opt_dlg.inc'}
+{%File 'i_visual.inc'}
+{%File 'i_options.inc'}
+{%File 'i_services.inc'}
+{%File 'i_vars.inc'}
+{%File 'i_inoutxm.inc'}
+{%File 'tasks\i_opt_dlg.inc'}
+{%File 'tasks\i_options.inc'}
+{%File 'tasks\i_task.inc'}
+{%File 'hooks\i_options.inc'}
+{%File 'hooks\i_hook.inc'}
+{%File 'hooks\i_opt_dlg.inc'}
+{%File 'ua\i_opt_dlg.inc'}
+{%File 'ua\i_inoutxm.inc'}
+{%File 'ua\i_options.inc'}
+{%File 'ua\i_ua.inc'}
+{%File 'ua\i_uaplaces.inc'}
+{%File 'ua\i_uconst.inc'}
+
+uses
+ m_api,
+ Windows,
+ messages,
+ commctrl,
+ common,
+ wrapper,
+ io,
+ dbsettings,
+ mirutils,
+ syswin,
+ base64,
+ question,
+ mApiCardM,
+ global,
+ sedit,
+ strans,
+ ua in 'ua\ua.pas',
+ hooks in 'hooks\hooks.pas',
+ scheduler in 'tasks\scheduler.pas';
+
+{$r options.res}
+
+const
+ PluginName = 'Action Manager';
+var
+ hHookShutdown,
+ onloadhook,
+ opthook:cardinal;
+ hevaction,hHookChanged,hevinout:cardinal;
+ hsel,hinout,hfree,hget,hrun,hrung,hrunp:cardinal;
+
+{$include m_actions.inc}
+{$include m_actman.inc}
+
+
+function MirandaPluginInfoEx(mirandaVersion:DWORD):PPLUGININFOEX; cdecl;
+begin
+ result:=@PluginInfo;
+ PluginInfo.cbSize :=SizeOf(TPLUGININFOEX);
+ PluginInfo.shortName :='Action manager';
+ PluginInfo.version :=$00020001;
+ PluginInfo.description:='Plugin for manage hotkeys to open contact window, insert text, '+
+ 'run program and call services';
+ PluginInfo.author :='Awkward';
+ PluginInfo.authorEmail:='panda75@bk.ru; awk1975@ya.ru';
+ PluginInfo.copyright :='(c) 2007-2012 Awkward';
+ PluginInfo.homepage :='http://code.google.com/p/delphi-miranda-plugins/';
+ PluginInfo.flags :=UNICODE_AWARE;
+ PluginInfo.uuid :=MIID_ACTMAN;
+end;
+
+{$include i_const.inc}
+{$include i_vars.inc}
+
+{$include i_action.inc}
+{$include i_actlow.inc}
+{$include i_options.inc}
+{$include i_contact.inc}
+{$include i_opt_dlg.inc}
+{$include i_inoutxm.inc}
+{$include i_services.inc}
+
+function PreShutdown(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ ptr:pActionLink;
+begin
+ result:=0;
+
+ ptr:=ActionLink;
+ while ptr<>nil do
+ begin
+ if @ptr^.DeInit<>nil then
+ ptr^.DeInit;
+ ptr:=ptr^.Next;
+ end;
+
+ FreeGroups;
+
+ UnhookEvent(hHookShutdown);
+ UnhookEvent(opthook);
+
+ DestroyHookableEvent(hHookChanged);
+ DestroyHookableEvent(hevinout);
+ DestroyHookableEvent(hevaction);
+
+ DestroyServiceFunction(hfree);
+ DestroyServiceFunction(hget);
+ DestroyServiceFunction(hrun);
+ DestroyServiceFunction(hrung);
+ DestroyServiceFunction(hrunp);
+ DestroyServiceFunction(hinout);
+ DestroyServiceFunction(hsel);
+end;
+
+function OnModulesLoaded(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ ptr:pActionLink;
+begin
+ Result:=0;
+ UnhookEvent(onloadhook);
+
+ LoadGroups;
+ RegisterIcons;
+
+ opthook :=HookEvent(ME_OPT_INITIALISE ,@OnOptInitialise);
+ hHookShutdown:=HookEvent(ME_SYSTEM_SHUTDOWN{ME_SYSTEM_OKTOEXIT},@PreShutdown);
+ NotifyEventHooks(hHookChanged,twparam(ACTM_LOADED),0);
+
+ //----- DBEDITOR support -----
+// CallService('DBEditorpp/RegisterSingleModule',dword(PluginShort),0);
+
+ IsMultiThread:=true;
+ // Load additional modules
+ ptr:=ActionLink;
+ while ptr<>nil do
+ begin
+ if @ptr^.Init<>nil then
+ ptr^.Init;
+ ptr:=ptr^.Next;
+ end;
+
+ CallService(MS_ACT_RUNBYNAME,TWPARAM(AutoStartName),0);
+end;
+
+function Load():int; cdecl;
+begin
+ Result:=0;
+ Langpack_register;
+
+ hHookChanged:=CreateHookableEvent(ME_ACT_CHANGED);
+ hevinout :=CreateHookableEvent(ME_ACT_INOUT);
+ hevaction :=CreateHookableEvent(ME_ACT_ACTION);
+
+ hfree :=CreateServiceFunction(MS_ACT_FREELIST ,@ActFreeList);
+ hget :=CreateServiceFunction(MS_ACT_GETLIST ,@ActGetList);
+ hrun :=CreateServiceFunction(MS_ACT_RUNBYID ,@ActRun);
+ hrung :=CreateServiceFunction(MS_ACT_RUNBYNAME,@ActRunGroup);
+ hrunp :=CreateServiceFunction(MS_ACT_RUNPARAMS,@ActRunParam);
+ hinout:=CreateServiceFunction(MS_ACT_INOUT ,@ActInOut);
+ hsel :=CreateServiceFunction(MS_ACT_SELECT ,@ActSelect);
+
+ onloadhook:=HookEvent(ME_SYSTEM_MODULESLOADED,@OnModulesLoaded);
+end;
+
+function Unload: int; cdecl;
+begin
+ Result:=0;
+end;
+
+exports
+ Load, Unload,
+ MirandaPluginInfoEx;
+
+end.
diff --git a/plugins/Actman/global.pas b/plugins/Actman/global.pas
new file mode 100644
index 0000000000..50254b383e
--- /dev/null
+++ b/plugins/Actman/global.pas
@@ -0,0 +1,21 @@
+unit global;
+
+interface
+
+type
+ tAddOption = function(var tmpl:pAnsiChar;var proc:pointer;var name:PAnsiChar):integer;
+type
+ pActionLink=^tActionLink;
+ tActionLink=record
+ Next :pActionLink;
+ Init :procedure;
+ DeInit :procedure;
+ AddOption:tAddOption;
+ end;
+
+const
+ ActionLink:pActionLink=nil;
+
+implementation
+
+end. \ No newline at end of file
diff --git a/plugins/Actman/hooks/hooks.pas b/plugins/Actman/hooks/hooks.pas
new file mode 100644
index 0000000000..b3309c327a
--- /dev/null
+++ b/plugins/Actman/hooks/hooks.pas
@@ -0,0 +1,73 @@
+unit hooks;
+
+interface
+
+procedure Init;
+procedure DeInit;
+function AddOptionPage(var tmpl:pAnsiChar;var proc:pointer;var name:PAnsiChar):integer;
+
+implementation
+
+uses
+ windows, commctrl, messages,
+ mirutils, common, dbsettings, io, m_api, wrapper,
+ global, mApiCardM;
+
+{$R hooks.res}
+
+{$include m_actman.inc}
+
+{$include i_hook.inc}
+{$include i_hconst.inc}
+{$include i_options.inc}
+{$include i_opt_dlg.inc}
+
+// ------------ base interface functions -------------
+
+procedure Init;
+begin
+
+ MessageWindow:=CreateWindowExW(0,'STATIC',nil,0,1,1,1,1,HWND_MESSAGE,0,hInstance,nil);
+ if MessageWindow<>0 then
+ SetWindowLongPtrW(MessageWindow,GWL_WNDPROC,LONG_PTR(@HookWndProc));
+
+ if LoadHooks=0 then
+ begin
+ MaxHooks:=8;
+ GetMem (HookList ,MaxHooks*SizeOf(tHookRec));
+ FillChar(HookList^,MaxHooks*SizeOf(tHookRec),0);
+ end
+ else
+ SetAllHooks;
+end;
+
+procedure DeInit;
+begin
+ ClearHooks;
+ if MessageWindow<>0 then
+ DestroyWindow(MessageWindow);
+end;
+
+function AddOptionPage(var tmpl:pAnsiChar;var proc:pointer;var name:PAnsiChar):integer;
+begin
+ result:=0;
+ tmpl:=PAnsiChar(IDD_HOOKS);
+ proc:=@DlgProcOpt;
+ name:='Hooks';
+end;
+
+var
+ amLink:tActionLink;
+
+procedure InitLink;
+begin
+ amLink.Next :=ActionLink;
+ amLink.Init :=@Init;
+ amLink.DeInit :=@DeInit;
+ amLink.AddOption:=@AddOptionPage;
+ ActionLink :=@amLink;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Actman/hooks/hooks.rc b/plugins/Actman/hooks/hooks.rc
new file mode 100644
index 0000000000..ff351cc94d
--- /dev/null
+++ b/plugins/Actman/hooks/hooks.rc
@@ -0,0 +1,28 @@
+#include "i_hconst.inc"
+
+LANGUAGE 0,0
+
+IDD_HOOKS DIALOGEX 0, 0, 304, 226, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ CONTROL "", IDC_HOOKLIST, "SysListView32",
+ WS_BORDER | WS_TABSTOP |
+ LVS_SHOWSELALWAYS| LVS_REPORT | LVS_EDITLABELS,// | LVS_SINGLESEL
+ 0, 2, 280, 160, WS_EX_CONTROLPARENT
+
+ CONTROL "Help" ,IDC_EVENT_HELP ,"MButtonClass",WS_TABSTOP,284, 2,16,16,$18000000
+ CONTROL "Delete",IDC_HOOK_DELETE,"MButtonClass",WS_TABSTOP,284, 96,16,16,$18000000
+
+ CONTROL "New" ,IDC_HOOK_NEW ,"MButtonClass",WS_TABSTOP,284,126,16,16,$18000000
+ CONTROL "Apply" ,IDC_HOOK_APPLY ,"MButtonClass",WS_TABSTOP,284,146,16,16,$18000000
+
+ CONTROL "Help" ,IDC_EVENT_CHELP,"MButtonClass",WS_TABSTOP,2 ,162,16,16,$18000000
+ CTEXT "Event" ,-1 ,18, 165, 121, 11, SS_CENTERIMAGE
+ COMBOBOX IDC_EVENTLIST , 0, 178, 157, 128, CBS_DROPDOWN | CBS_SORT | WS_VSCROLL
+ CTEXT "Action",-1 , 0, 195, 157, 11, SS_CENTERIMAGE
+ COMBOBOX IDC_ACTIONLIST, 0, 208, 157, 128, CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL
+
+ LTEXT "",IDC_DESCR, 160, 165, 138, 57
+}
diff --git a/plugins/Actman/hooks/hooks.res b/plugins/Actman/hooks/hooks.res
new file mode 100644
index 0000000000..1cf202f414
--- /dev/null
+++ b/plugins/Actman/hooks/hooks.res
Binary files differ
diff --git a/plugins/Actman/hooks/i_hconst.inc b/plugins/Actman/hooks/i_hconst.inc
new file mode 100644
index 0000000000..d011278b7e
--- /dev/null
+++ b/plugins/Actman/hooks/i_hconst.inc
@@ -0,0 +1,20 @@
+{resource constants}
+const
+ // dialogs
+ IDD_HOOKS = 1029;
+
+ // icons
+ IDI_NEW = 1025;
+ IDI_DELETE = 1028;
+
+ // Hook editor
+ IDC_HOOKLIST = 1025;
+ IDC_ACTIONLIST = 1026;
+ IDC_EVENTLIST = 1027;
+ IDC_EVENT_HELP = 1028;
+ IDC_HOOK_NEW = 1029;
+ IDC_HOOK_DELETE = 1030;
+ IDC_HOOK_APPLY = 1031;
+ IDC_EVENT_CHELP = 1032;
+
+ IDC_DESCR = 1040;
diff --git a/plugins/Actman/hooks/i_hook.inc b/plugins/Actman/hooks/i_hook.inc
new file mode 100644
index 0000000000..8b7b487d98
--- /dev/null
+++ b/plugins/Actman/hooks/i_hook.inc
@@ -0,0 +1,154 @@
+{}
+
+const
+ HWND_MESSAGE = HWND(-3);
+const
+ ACF_ASSIGNED = $80000000; // hook assigned
+ ACF_DISABLED = $10000000; // hook disabled
+const
+ WM_RESETHOOKS = WM_USER+1312;
+ WM_FIRSTHOOK = WM_USER+1313;
+ WM_LASTHOOK = WM_FIRSTHOOK+1000;
+
+type
+ pHookRec = ^tHookRec;
+ tHookRec = record
+ flags :dword;
+ name :PAnsiChar; // name for hook
+ handle :THANDLE; // handle of hook
+ descr :PWideChar; // name for list
+ action :dword; // assigned action
+ message:uint; // window message for hook
+ end;
+ pHookList = ^tHookList;
+ tHookList = array [0..1023] of tHookRec;
+
+var
+ HookList:pHookList = nil;
+ MaxHooks:integer = 0;
+ MessageWindow:HWND = 0;
+
+function GetNextMessage:uint;
+var
+ i:uint;
+ j:integer;
+begin
+ result:=0;
+ for i:=WM_FIRSTHOOK to WM_LASTHOOK do
+ begin
+ for j:=0 to MaxHooks-1 do
+ begin
+ with HookList^[j] do
+ begin
+ if ((flags and ACF_ASSIGNED)<>0) and (i=message) then
+ begin
+ inc(result);
+ break;
+ end;
+ end;
+ end;
+ if result=0 then
+ begin
+ result:=i;
+ break;
+ end
+ else
+ result:=0;
+ end;
+end;
+
+procedure SetAllHooks;
+var
+ i:integer;
+ msg:cardinal;
+begin
+ msg:=WM_FIRSTHOOK;
+ for i:=0 to MaxHooks-1 do
+ begin
+ with HookList[i] do
+ begin
+ message:=msg;
+ if (flags and ACF_ASSIGNED)<>0 then
+ begin
+ if (flags and ACF_DISABLED)<>0 then
+ begin
+ if handle<>0 then
+ begin
+ UnhookEvent(handle);
+ handle:=0;
+ end;
+ end
+ else
+ begin
+ if handle<>0 then
+ UnhookEvent(handle);
+ handle:=HookEventMessage(name,MessageWindow,message);
+ end;
+ end;
+ end;
+ inc(msg);
+ end;
+end;
+
+function GetHookByMessage(msg:uint):pHookRec;
+var
+ i:integer;
+begin
+ result:=nil;
+ for i:=0 to MaxHooks-1 do
+ begin
+ with HookList[i] do
+ begin
+ if ((flags and ACF_ASSIGNED)<>0) and (msg=message) then
+ begin
+ result:=@HookList[i];
+ break;
+ end;
+ end;
+ end;
+end;
+
+function HookWndProc(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+var
+ p:pHookRec;
+ ap:tAct_Param;
+begin
+ result:=0;
+ case hMessage of
+ WM_FIRSTHOOK..WM_LASTHOOK: begin
+ p:=GetHookByMessage(hMessage);
+ if p<>nil then
+ begin
+ ap.flags :=ACTP_WAIT;
+ ap.Id :=p^.action;
+ ap.wParam:=wParam;
+ ap.lParam:=lParam;
+ result:=CallService(MS_ACT_RUNPARAMS,0,TLPARAM(@ap));
+ end;
+ end;
+ else
+ result:=DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+end;
+
+procedure ClearHooks;
+var
+ i:integer;
+begin
+ for i:=0 to MaxHooks-1 do
+ begin
+ with HookList[i] do
+ begin
+ if (flags and ACF_ASSIGNED)<>0 then
+ begin
+ mFreeMem(descr);
+ mFreeMem(name);
+ if handle<>0 then
+ UnhookEvent(handle);
+ end;
+ end;
+ end;
+ FreeMem(HookList);
+ MaxHooks:=0;
+end;
+
diff --git a/plugins/Actman/hooks/i_opt_dlg.inc b/plugins/Actman/hooks/i_opt_dlg.inc
new file mode 100644
index 0000000000..d208ec4384
--- /dev/null
+++ b/plugins/Actman/hooks/i_opt_dlg.inc
@@ -0,0 +1,410 @@
+{}
+const
+ settings:HWND = 0;
+var
+ OldTableProc:pointer;
+ onactchanged:THANDLE;
+ ApiCard:tmApiCard;
+
+const
+ ACI_NEW :PAnsiChar = 'ACI_New';
+ ACI_APPLY :PAnsiChar = 'ACI_Apply';
+ ACI_DELETE :PAnsiChar = 'ACI_Delete';
+
+procedure CheckHookList(wnd:HWND);
+var
+ i:integer;
+ li:LV_ITEMW;
+ arr:array [0..127] of WideChar;
+begin
+ ClearHooks;
+
+ li.mask :=LVIF_TEXT or LVIF_PARAM;
+ li.pszText :=@arr;
+ li.cchTextMax:=SizeOf(arr) div SizeOf(WideChar);
+
+ MaxHooks:=SendMessage(wnd,LVM_GETITEMCOUNT,0,0);
+
+ GetMem (HookList ,MaxHooks*SizeOf(tHookRec));
+ FillChar(HookList^,MaxHooks*SizeOf(tHookRec),0);
+ for i:=0 to MaxHooks-1 do
+ begin
+ with HookList[i] do
+ begin
+ flags:=ACF_ASSIGNED;
+ li.iItem :=i;
+ li.iSubItem:=0;
+ SendMessageW(wnd,LVM_GETITEMW,0,LPARAM(@li));
+ StrDupW(descr,arr);
+ action:=li.lParam;
+ li.iSubItem:=1;
+ SendMessageA(wnd,LVM_GETITEMA,0,LPARAM(@li));
+ StrDup(name,pAnsiChar(@arr));
+
+ if ListView_GetCheckState(wnd,i)=0 then // disabled
+ flags:=flags or ACF_DISABLED;
+ end;
+ end;
+end;
+
+procedure FillHookList(wnd:HWND);
+var
+ i:integer;
+ li:LV_ITEMW;
+begin
+ SendMessage(wnd,LVM_DELETEALLITEMS,0,0);
+ for i:=0 to MaxHooks-1 do
+ begin
+ with HookList[i] do
+ begin
+ if (flags and ACF_ASSIGNED)<>0 then
+ begin
+ li.mask :=LVIF_TEXT+LVIF_PARAM;
+ li.iSubItem:=0;
+ li.iItem :=i;
+ li.lParam :=action;
+ li.pszText :=descr;
+ li.iItem :=SendMessageW(wnd,LVM_INSERTITEMW,0,LPARAM(@li));
+ li.mask :=LVIF_TEXT;
+ li.iSubItem:=1;
+ li.pszText :=pWideChar(name);
+ SendMessageA(wnd,LVM_SETITEMA,0,LPARAM(@li));
+ ListView_SetCheckState(wnd,li.iItem,(flags and ACF_DISABLED)=0);
+ end;
+ end;
+ end;
+ ListView_SetItemState(wnd,0,
+ LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+end;
+
+procedure FillActionList(wnd:HWND);
+var
+ ptr,ptr1:pChain;
+ i,cnt:integer;
+begin
+ cnt:=CallService(MS_ACT_GETLIST,0,LPARAM(@ptr));
+ SendMessage(wnd,CB_RESETCONTENT,0,0);
+ if cnt>0 then
+ begin
+ ptr1:=ptr;
+ inc(pbyte(ptr),4);
+ for i:=0 to cnt-1 do
+ begin
+ CB_AddStrDataW(wnd,ptr^.descr,ptr^.id);
+ inc(ptr);
+ end;
+
+ CallService(MS_ACT_FREELIST,0,LPARAM(ptr1));
+ SendMessage(wnd,CB_SETCURSEL,0,0);
+ end;
+end;
+
+function ActListChange(wParam:WPARAM;lParam:LPARAM):integer; cdecl;
+begin
+ result:=0;
+ if settings<>0 then
+ FillActionList(GetDlgItem(settings,IDC_ACTIONLIST));
+end;
+
+procedure ShowHookData(Dialog:HWND; item:integer=-1);
+var
+ li:LV_ITEM;
+ arr:array [0..127] of WideChar;
+ wnd:HWND;
+begin
+ wnd:=GetDlgItem(Dialog,IDC_HOOKLIST);
+ if item<0 then
+ li.iItem:=SendMessage(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED)
+ else
+ li.iItem:=item;
+
+ li.mask :=LVIF_TEXT+LVIF_PARAM;
+ li.iSubItem :=1;
+ li.pszText :=@arr;
+ li.cchTextMax:=SizeOf(arr) div SizeOf(WideChar);
+ arr[0]:=#0;
+ SendMessageW(wnd,LVM_GETITEMW,0,LPARAM(@li));
+ if arr[0]<>#0 then
+ SetDlgItemTextW(Dialog,IDC_EVENTLIST,arr);
+ CB_SelectData(GetDlgItem(Dialog,IDC_ACTIONLIST),li.lParam);
+end;
+
+procedure SaveHookData(Dialog:HWND; item:integer=-1);
+var
+ wnd:HWND;
+ li:LV_ITEM;
+begin
+ wnd:=GetDlgItem(Dialog,IDC_HOOKLIST);
+ if item<0 then
+ li.iItem:=SendMessage(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED)
+ else
+ li.iItem:=item;
+ li.mask :=LVIF_PARAM;
+ li.lParam :=CB_GetData(GetDlgItem(Dialog,IDC_ACTIONLIST));
+ li.iSubItem :=0;
+ SendMessageW(wnd,LVM_SETITEMW,0,LPARAM(@li));
+ li.mask :=LVIF_TEXT;
+ li.iSubItem :=1;
+ li.pszText :=GetDlgText(Dialog,IDC_EVENTLIST);
+ SendMessageW(wnd,LVM_SETITEMW,0,LPARAM(@li));
+ mFreeMem(li.pszText);
+end;
+
+function NewHook(Dialog:HWND;item:integer=-1):integer;
+var
+ wnd:HWND;
+ li:LV_ITEMW;
+begin
+ wnd:=GetDlgItem(Dialog,IDC_HOOKLIST);
+ li.mask :=LVIF_TEXT;
+ if item<0 then
+ li.iItem :=SendMessage(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED)+1
+ else
+ li.iItem :=item;
+ li.iSubItem:=0;
+ li.pszText :=TranslateW('hook sample');
+ result:=SendMessageW(wnd,LVM_INSERTITEMW,0,LPARAM(@li));
+end;
+
+function DeleteHook(Dialog:HWND):integer;
+var
+ wnd:HWND;
+ i:integer;
+begin
+ result:=0;
+ wnd:=GetDlgItem(Dialog,IDC_HOOKLIST);
+ for i:=ListView_GetItemCount(wnd)-1 downto 0 do
+ begin
+ if ListView_GetItemState(wnd,i,LVIS_SELECTED)<>0 then
+ SendMessage(wnd,LVM_DELETEITEM,i,0);
+ end;
+ Listview_SetItemState(wnd,0,LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+end;
+
+function NewHKTableProc(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+var
+ i:integer;
+begin
+ result:=0;
+ case hMessage of
+ WM_KEYDOWN: begin
+ if (lParam and (1 shl 30))=0 then
+ begin
+ case wParam of
+ VK_F2: begin
+ i:=SendMessage(Dialog,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
+ if i>=0 then
+ PostMessageW(Dialog,LVM_EDITLABELW,i,0);
+ exit;
+ end;
+ VK_INSERT: begin
+ PostMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDC_HOOK_NEW,0);
+ exit;
+ end;
+ VK_DELETE: begin
+ PostMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDC_HOOK_DELETE,0);
+ exit;
+ end;
+ end;
+ end;
+ end;
+ end;
+ result:=CallWindowProc(OldTableProc,Dialog,hMessage,wParam,lParam);
+end;
+
+procedure SetIcons(Dialog:HWND);
+var
+ ti:TTOOLINFOW;
+ hwndTooltip:HWND;
+begin
+ hwndTooltip:=CreateWindowW(TOOLTIPS_CLASS,nil,TTS_ALWAYSTIP,
+ integer(CW_USEDEFAULT),integer(CW_USEDEFAULT),
+ integer(CW_USEDEFAULT),integer(CW_USEDEFAULT),
+ Dialog,0,hInstance,nil);
+
+ FillChar(ti,SizeOf(ti),0);
+ ti.cbSize :=sizeof(TOOLINFO);
+ ti.uFlags :=TTF_IDISHWND or TTF_SUBCLASS;
+ ti.hwnd :=dialog;
+ ti.hinst :=hInstance;
+
+ ti.lpszText:=TranslateW('Help');
+ ti.uId :=GetDlgItem(Dialog,IDC_EVENT_HELP);
+ SendMessage(ti.uId,BM_SETIMAGE,IMAGE_ICON,
+ CallService(MS_SKIN_LOADICON,SKINICON_OTHER_HELP,0));
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,LPARAM(@ti));
+ ti.uId :=GetDlgItem(Dialog,IDC_EVENT_CHELP);
+ SendMessage(ti.uId,BM_SETIMAGE,IMAGE_ICON,
+ CallService(MS_SKIN_LOADICON,SKINICON_OTHER_HELP,0));
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,LPARAM(@ti));
+
+ ti.uId :=GetDlgItem(Dialog,IDC_HOOK_NEW);
+ ti.lpszText:=TranslateW('New');
+ SetButtonIcon(ti.uId,ACI_NEW);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,LPARAM(@ti));
+ ti.uId :=GetDlgItem(Dialog,IDC_HOOK_APPLY);
+ ti.lpszText:=TranslateW('Apply');
+ SetButtonIcon(ti.uId,ACI_APPLY);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,LPARAM(@ti));
+ ti.uId :=GetDlgItem(Dialog,IDC_HOOK_DELETE);
+ ti.lpszText:=TranslateW('Delete');
+ SetButtonIcon(ti.uId,ACI_DELETE);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,LPARAM(@ti));
+end;
+
+function DlgProcOpt(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+var
+ wnd:HWND;
+ lv:LV_COLUMNW;
+ i:integer;
+ tmp:pAnsiChar;
+ buf:array [0..255] of AnsiChar;
+begin
+ result:=0;
+ case hMessage of
+ WM_CLOSE: begin
+ ApiCard.Free;
+
+ UnhookEvent(onactchanged);
+ settings:=0;
+ end;
+
+ WM_INITDIALOG: begin
+ ApiCard:=CreateEventCard(Dialog);
+
+ wnd:=GetDlgItem(Dialog,IDC_HOOKLIST);
+ SendMessage(wnd,LVM_SETUNICODEFORMAT,1,0);
+ FillChar(lv,SizeOf(lv),0);
+ lv.mask :=LVCF_TEXT or LVCF_WIDTH;
+ lv.pszText:=TranslateW('Description');
+ lv.cx :=110;
+ SendMessageW(wnd,LVM_INSERTCOLUMNW ,0,TLPARAM(@lv));
+ lv.pszText:=TranslateW('Name');
+ lv.cx :=110;
+ SendMessageW(wnd,LVM_INSERTCOLUMNW ,1,TLPARAM(@lv));
+ SendMessageW(wnd,LVM_SETCOLUMNWIDTH,1,LVSCW_AUTOSIZE_USEHEADER);
+// SendMessage (wnd,LVM_SETEXTENDEDLISTVIEWSTYLE,LVS_EX_CHECKBOXES,LVS_EX_CHECKBOXES);
+ SendMessage (wnd,LVM_SETEXTENDEDLISTVIEWSTYLE,0,
+ LVS_EX_FULLROWSELECT or LVS_EX_CHECKBOXES or LVS_EX_GRIDLINES);
+ SendMessage(wnd,LVM_SETUNICODEFORMAT,1,0);
+ OldTableProc:=pointer(SetWindowLongPtrW(wnd,GWL_WNDPROC,LONG_PTR(@NewHKTableProc)));
+ TranslateDialogDefault(Dialog);
+
+ SetIcons(Dialog);
+
+ ApiCard.FillList(GetDlgItem(Dialog,IDC_EVENTLIST));
+ FillActionList(GetDlgItem(Dialog,IDC_ACTIONLIST));
+ FillHookList(wnd);
+ ShowHookData(Dialog);
+
+ onactchanged:=HookEvent(ME_ACT_CHANGED,@ActListChange);
+ settings:=Dialog;
+ end;
+
+ WM_HELP: begin
+ ApiCard.Show;
+ end;
+
+ WM_RESETHOOKS:begin
+ FillHookList(GetDlgItem(Dialog,IDC_HOOKLIST));
+ end;
+
+ WM_COMMAND: begin
+ case wParam shr 16 of
+ EN_CHANGE,
+ CBN_EDITCHANGE,
+ CBN_SELCHANGE: begin
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+ end;
+
+ case wParam shr 16 of
+ CBN_EDITCHANGE: begin
+ case loword(wParam) of
+ IDC_EVENTLIST: begin
+ tmp :=GetDlgText(Dialog,IDC_EVENTLIST,true);
+ ApiCard.Event:=tmp;
+ mFreeMem(tmp);
+ tmp:=ApiCard.Description;
+ SetDlgItemTextA(Dialog,IDC_DESCR,Translate(tmp));
+ mFreeMem(tmp);
+ end;
+ end;
+ end;
+
+ CBN_SELENDOK: begin
+ case loword(wParam) of
+ IDC_EVENTLIST: begin
+ i:=SendMessage(LOWORD(lParam),CB_GETCURSEL,0,0);
+ SendMessageA(LOWORD(lParam),CB_GETLBTEXT,i,TLPARAM(@buf));
+ ApiCard.Event:=@buf;
+ tmp:=ApiCard.Description;
+ SetDlgItemTextA(Dialog,IDC_DESCR,Translate(tmp));
+ mFreeMem(tmp);
+ end;
+ end;
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+
+ BN_CLICKED: begin
+ case loword(wParam) of
+ IDC_EVENT_CHELP: ;
+ IDC_EVENT_HELP : ;
+ IDC_HOOK_NEW : NewHook(Dialog);
+ IDC_HOOK_DELETE: DeleteHook(Dialog);
+ IDC_HOOK_APPLY : SaveHookData(Dialog);
+ end;
+ end;
+ end;
+ end;
+
+ WM_NOTIFY: begin
+ case integer(PNMHdr(lParam)^.code) of
+ PSN_APPLY: begin
+ SaveHookData(Dialog);
+ CheckHookList(GetDlgItem(Dialog,IDC_HOOKLIST));
+ SetAllHooks;
+ SaveHooks;
+ end;
+
+ NM_DBLCLK: begin
+ if PNMListView(lParam)^.iItem>=0 then
+ PostMessageW(PNMHdr(lParam)^.hWndFrom,LVM_EDITLABELW,
+ PNMListView(lParam)^.iItem,0);
+ end;
+
+ LVN_ENDLABELEDITW: begin
+ with PLVDISPINFOW(lParam)^ do
+ begin
+ if item.pszText<>nil then
+ begin
+ item.mask:=LVIF_TEXT;
+ SendMessageW(hdr.hWndFrom,LVM_SETITEMW,0,TLPARAM(@item));
+ end;
+ end;
+ result:=1;
+ end;
+
+ LVN_ITEMCHANGED: begin
+ if PNMLISTVIEW(lParam)^.uChanged=LVIF_STATE then
+ begin
+ i:=(PNMLISTVIEW(lParam)^.uOldState and LVNI_FOCUSED)-
+ (PNMLISTVIEW(lParam)^.uNewState and LVNI_FOCUSED);
+
+ if i>0 then // old focus
+ SaveHookData(Dialog,PNMLISTVIEW(lParam)^.iItem)
+ else if i<0 then // new focus
+ begin
+ ShowHookData(Dialog,PNMLISTVIEW(lParam)^.iItem);
+ end
+ else if (settings<>0) and
+ ((PNMLISTVIEW(lParam)^.uOldState or PNMLISTVIEW(lParam)^.uNewState)=$3000) then
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+ end;
+ end;
+ end;
+ end;
+end;
diff --git a/plugins/Actman/hooks/i_options.inc b/plugins/Actman/hooks/i_options.inc
new file mode 100644
index 0000000000..4404cfbde6
--- /dev/null
+++ b/plugins/Actman/hooks/i_options.inc
@@ -0,0 +1,71 @@
+{}
+const
+ opt_hook :PAnsiChar = 'Hook';
+ opt_hooks :PAnsiChar = 'Hooks';
+ opt_count :PAnsiChar = 'numhooks';
+ opt_flags :PAnsiChar = 'flags';
+ opt_descr :PAnsiChar = 'descr';
+ opt_name :PAnsiChar = 'name';
+ opt_action:PAnsiChar = 'action';
+
+procedure SaveHooks;
+var
+ section:array [0..63] of AnsiChar;
+ p,p1:PAnsiChar;
+ i,amount:integer;
+begin
+ DBDeleteGroup(0,DBBranch,opt_hooks);
+ amount:=0;
+ p1:=StrCopyE(section,opt_hooks);
+ p1^:='/'; inc(p1);
+ p1:=StrCopyE(p1,opt_hook);
+ for i:=0 to MaxHooks-1 do
+ begin
+ if (HookList[i].flags and ACF_ASSIGNED)=0 then
+ continue;
+
+ p:=StrEnd(IntToStr(p1,amount));
+ p^:='/'; inc(p);
+
+ with HookList[i] do
+ begin
+ StrCopy(p,opt_flags ); DBWriteDWord (0,DBBranch,section,flags);
+ StrCopy(p,opt_descr ); DBWriteUnicode(0,DBBranch,section,descr);
+ StrCopy(p,opt_name ); DBWriteString (0,DBBranch,section,name);
+ StrCopy(p,opt_action); DBWriteDWord (0,DBBranch,section,action);
+ end;
+ inc(amount);
+ end;
+ DBWriteByte(0,DBBranch,opt_count,amount);
+end;
+
+function LoadHooks:integer;
+var
+ section:array [0..63] of AnsiChar;
+ p,p1:PAnsiChar;
+ i:integer;
+begin
+ MaxHooks:=DBReadByte(0,DBBranch,opt_count);
+ result:=MaxHooks;
+ if MaxHooks>0 then
+ begin
+ GetMem (HookList ,MaxHooks*SizeOf(tHookRec));
+ FillChar(HookList^,MaxHooks*SizeOf(tHookRec),0);
+ p1:=StrCopyE(section,opt_hooks);
+ p1^:='/'; inc(p1);
+ p1:=StrCopyE(p1,opt_hook);
+ for i:=0 to MaxHooks-1 do
+ begin
+ p:=StrEnd(IntToStr(p1,i));
+ p^:='/'; inc(p);
+
+ with HookList[i] do
+ begin
+ StrCopy(p,opt_flags ); flags :=DBReadDWord (0,DBBranch,section);
+ StrCopy(p,opt_descr ); descr :=DBReadUnicode(0,DBBranch,section);
+ StrCopy(p,opt_name ); name :=DBReadString (0,DBBranch,section);
+ StrCopy(p,opt_action); action:=DBReadDWord (0,DBBranch,section);
+ end;
+ end;
+ end;
+end;
diff --git a/plugins/Actman/i_action.inc b/plugins/Actman/i_action.inc
new file mode 100644
index 0000000000..1dd9719d35
--- /dev/null
+++ b/plugins/Actman/i_action.inc
@@ -0,0 +1,952 @@
+{Action code}
+const
+ rtInt = 1;
+ rtWide = 2;
+
+const
+ SST_BYTE = 0;
+ SST_WORD = 1;
+ SST_DWORD = 2;
+ SST_QWORD = 3;
+ SST_NATIVE = 4;
+ SST_BARR = 5;
+ SST_WARR = 6;
+ SST_BPTR = 7;
+ SST_WPTR = 8;
+ SST_LAST = 9;
+ SST_PARAM = 10;
+
+const
+ protostr = '<proto>';
+
+const
+ BufferSize = 8192;
+
+function DBRW(act:pHKAction;hContact:THANDLE;avalue:uint_ptr;
+ last,restype:uint_ptr):uint_ptr;
+var
+ buf ,buf1 :array [0..127] of AnsiChar;
+ sbuf:array [0..127] of AnsiChar;
+ module,setting:pAnsiChar;
+ tmp:pWideChar;
+ tmpa,tmpa1:pAnsichar;
+begin
+ module :=act^.dbmodule;
+ setting:=act^.dbsetting;
+
+ with act^ do
+ begin
+ if restype=rtWide then
+ FastWideToAnsiBuf(pWideChar(last),@sbuf)
+ else
+ IntToStr(sbuf,last);
+
+ if (flags2 and ACF2_RW_MVAR)<>0 then module :=ParseVarString(module ,hContact,sbuf);
+ if (flags2 and ACF2_RW_SVAR)<>0 then setting:=ParseVarString(setting,hContact,sbuf);
+ StrCopy(buf,module);
+ StrReplace(buf,protostr,GetContactProtoAcc(hContact));
+
+ StrReplace(buf,'<last>',sbuf);
+ StrCopy(buf1,setting);
+ StrReplace(buf1,'<last>',sbuf);
+
+ if (flags2 and ACF2_RW_TVAR)<>0 then
+ pWideChar(avalue):=ParseVarString(pWideChar(avalue),hContact,@sbuf);
+
+ if ((flags and ACF_DBUTEXT)=0) and
+ ((flags2 and ACF2_RW_TVAR)<>0) then
+ begin
+ tmp:=pWideChar(avalue);
+ avalue:=StrToInt(tmp);
+ mFreeMem(tmp);
+ end;
+
+ if (flags and ACF_DBDELETE)<>0 then
+ begin
+ result:=DBDeleteSetting(hContact,buf,setting);
+ end
+ else if (flags and ACF_DBWRITE)<>0 then
+ begin
+ if (flags and ACF_DBANSI)=ACF_DBANSI then
+ begin
+ WideToAnsi(pWideChar(avalue),tmpa,MirandaCP);
+ DBWriteString(hContact,buf,buf1,tmpa);
+ mFreeMem(tmpa);
+ if (flags2 and ACF2_RW_TVAR)=0 then
+ StrDupW(pWideChar(avalue),pWideChar(avalue));
+ end
+ else if (flags and ACF_DBBYTE )=ACF_DBBYTE then DBWriteByte(hContact,buf,setting,avalue)
+ else if (flags and ACF_DBWORD )=ACF_DBWORD then DBWriteWord(hContact,buf,setting,avalue)
+ else if (flags and ACF_DBUTEXT)=ACF_DBUTEXT then
+ begin
+ DBWriteUnicode(hContact,buf,buf1,pWideChar(avalue));
+ if (flags2 and ACF2_RW_TVAR)=0 then
+ StrDupW(pWideChar(avalue),pWideChar(avalue));
+ end
+ else DBWriteDWord(hContact,buf,setting,avalue);
+
+ result:=avalue;
+ end
+ else
+ begin
+ if (flags and ACF_DBANSI)=ACF_DBANSI then
+ begin
+ WideToAnsi(pWideChar(avalue),tmpa1,MirandaCP);
+ tmpa:=DBReadString(hContact,buf,buf1,tmpa1);
+ AnsiToWide(tmpa,PWideChar(result),MirandaCP);
+ mFreeMem(tmpa1);
+ mFreeMem(tmpa);
+
+ if (flags2 and ACF2_RW_TVAR)<>0 then
+ mFreeMem(avalue);
+ end
+ else if (flags and ACF_DBBYTE )=ACF_DBBYTE then result:=DBReadByte(hContact,buf,setting,avalue)
+ else if (flags and ACF_DBWORD )=ACF_DBWORD then result:=DBReadWord(hContact,buf,setting,avalue)
+ else if (flags and ACF_DBUTEXT)=ACF_DBUTEXT then
+ begin
+ result:=uint_ptr(DBReadUnicode(hContact,buf,buf1,pWideChar(avalue)));
+ if (flags2 and ACF2_RW_TVAR)<>0 then
+ mFreeMem(avalue);
+ end
+ else result:=DBReadDWord(hContact,buf,setting,avalue);
+
+ end;
+ if (flags2 and ACF2_RW_MVAR)<>0 then mFreeMem(module);
+ if (flags2 and ACF2_RW_SVAR)<>0 then mFreeMem(setting);
+ end;
+end;
+
+function OpenContact(hContact:THANDLE):THANDLE;
+begin
+ ShowContactDialog(hContact);
+{
+ if CallService(MS_DB_CONTACT_IS,hContact,0)<>0 then
+ begin
+ if ServiceExists(MS_MSG_CONVERS)<>0 then
+ begin
+ CallService(MS_MSG_CONVERS,hContact,0)
+ end
+ else
+ CallService(MS_MSG_SENDMESSAGE,hContact,0)
+ end;
+}
+ result:=hContact;
+end;
+
+function replany(var str:pWideChar;aparam:LPARAM;alast:pWideChar):boolean;
+var
+ buf:array [0..31] of WideChar;
+ tmp:pWideChar;
+begin
+ if StrScanW(str,'<')<>nil then
+ begin
+ result:=true;
+ mGetMem(tmp,2048);
+ StrCopyW(tmp,str);
+ StrReplaceW(tmp,'<param>',IntToStr(buf,aparam));
+ StrReplaceW(tmp,'<last>' ,alast);
+
+ str:=tmp;
+ end
+ else
+ result:=false;
+end;
+
+function RunProgram(act:pHKAction;aparam:LPARAM;alast:pWideChar):dword;
+var
+ tmp,tmpp,lpath:PWideChar;
+ replPrg ,replArg :PWideChar;
+ replPrg1,replArg1:PWideChar;
+ pd:LPARAM;
+ vars1,vars2,prgs,argss:boolean;
+begin
+ with act^ do
+ begin
+
+ replPrg:=prgname;
+ prgs :=replany(replPrg,aparam,alast);
+
+ replArg:=args;
+ argss :=replany(replArg,aparam,alast);
+
+ if ((flags2 and ACF2_PRG_PRG)<>0) or
+ ((flags2 and ACF2_PRG_ARG)<>0) then
+ begin
+ pd:=WndToContact(WaitFocusedWndChild(GetForegroundwindow){GetFocus});
+ if (pd=0) and (CallService(MS_DB_CONTACT_IS,aparam,0)<>0) then
+ pd:=aparam;
+ end;
+
+ if (flags2 and ACF2_PRG_ARG)<>0 then
+ begin
+ vars2:=true;
+ tmp :=ParseVarString(replArg,pd,alast);
+ end
+ else
+ begin
+ vars2:=false;
+ tmp :=replArg;
+ end;
+
+ if (flags2 and ACF2_PRG_PRG)<>0 then
+ begin
+ vars1:=true;
+ tmpp :=ParseVarString(replPrg,pd,alast);
+ end
+ else
+ begin
+ vars1:=false;
+ tmpp:=replPrg;
+ end;
+
+ if StrScanW(tmpp,'%')<>nil then
+ begin
+ mGetMem(replPrg1,8192*SizeOf(WideChar));
+ ExpandEnvironmentStringsW(tmpp,replPrg1,8191);
+ if vars1 then mFreeMem(tmpp);
+ if prgs then mFreeMem(replPrg);
+ tmpp :=replPrg1;
+ prgs :=false;
+ vars1:=true;
+ end;
+ if StrScanW(tmp,'%')<>nil then
+ begin
+ mGetMem(replArg1,8192*SizeOf(WideChar));
+ ExpandEnvironmentStringsW(tmp,replArg1,8191);
+ if vars2 then mFreeMem(tmp);
+ if argss then mFreeMem(replArg);
+ tmp :=replArg1;
+ argss:=false;
+ vars2:=true;
+ end;
+
+ if (flags and ACF_CURPATH)=0 then
+ lpath:=ExtractW(tmpp,false)
+ else
+ lpath:=nil;
+
+ if (flags and ACF_PRTHREAD)<>0 then
+ time:=0
+ else if time=0 then
+ time:=INFINITE;
+ result:=ExecuteWaitW(tmpp,tmp,lpath,show,time,@pd);
+
+ if vars2 then mFreeMem(tmp);
+ if vars1 then mFreeMem(tmpp);
+
+ if prgs then mFreeMem(replPrg);
+ if argss then mFreeMem(replArg);
+ end;
+ mFreeMem(lpath);
+end;
+{
+function MakeStructure(txt:pWideChar;aparam,alast:LPARAM;
+ var code,alen:integer;var ofs:int_ptr; restype:integer=rtInt):pointer; forward;
+procedure FreeStructure(var struct;descr:pWideChar); forward;
+}
+function RunService(act:pHKAction;LastResult,Param:LPARAM;var restype:dword):uint_ptr;
+var
+ res:int_ptr;
+ buf:array [0..255] of AnsiChar;
+ cc:integer;
+ lservice:pAnsiChar;
+ lwparam,llparam:LPARAM;
+ tmp1,tmp2:pWideChar;
+ code,len:integer;
+begin
+ result:=uint_ptr(-1);
+
+ lservice:=act^.service;
+ lwparam :=act^.wparam;
+ llparam :=act^.lparam;
+ with act^ do
+ begin
+ if (flags2 and ACF2_SRV_SRVC)<>0 then
+ lservice:=ParseVarString(lservice,Param);
+
+ StrCopy(buf,lservice);
+ if StrPos(lservice,protostr)<>nil then
+ if CallService(MS_DB_CONTACT_IS,Param,0)=0 then
+ begin
+ if (flags2 and ACF2_SRV_SRVC)<>0 then
+ mFreeMem(lservice);
+ exit
+ end
+ else
+ StrReplace(buf,protostr,GetContactProtoAcc(Param));
+
+ if ServiceExists(buf)<>0 then
+ begin
+
+ cc:=-1;
+
+ tmp1:=nil;
+ tmp2:=nil;
+ code:=-1;
+ if (flags and ACF_WSTRUCT)<>0 then
+ begin
+ lwparam:=twParam(MakeStructure(pAnsiChar(lwparam),Param,LastResult,restype))
+ end
+ else if (flags and ACF_WPARAM)<>0 then
+ begin
+ lwparam:=Param;
+ end
+ else if (flags and ACF_WRESULT)<>0 then
+ begin
+ lwparam:=LastResult;
+ end
+ else if (flags and ACF_WCURRENT)<>0 then
+ begin
+ cc:=WndToContact(WaitFocusedWndChild(GetForegroundwindow){GetFocus});
+ lwparam:=cc;
+ end
+ else if (flags2 and ACF2_SRV_WPAR)<>0 then
+ begin
+ if (flags and ACF_WPARNUM)=0 then
+ begin
+ if (flags and ACF_WUNICODE)=0 then
+ lwparam:=uint_ptr(ParseVarString(pAnsiChar(lwparam),Param))
+ else
+ lwparam:=uint_ptr(ParseVarString(pWideChar(lwparam),Param))
+ end
+ else
+ begin
+ tmp1:=ParseVarString(pWideChar(lwparam),Param);
+ lwparam:=StrToInt(tmp1);
+ end;
+ end;
+
+ if (flags and ACF_LSTRUCT)<>0 then
+ begin
+ llparam:=tlParam(MakeStructure(pAnsiChar(llparam),Param,LastResult,restype))
+ end
+ else if (flags and ACF_LPARAM)<>0 then
+ begin
+ llparam:=Param;
+ end
+ else if (flags and ACF_LRESULT)<>0 then
+ begin
+ llparam:=LastResult;
+ end
+ else if (flags and ACF_LCURRENT)<>0 then
+ begin
+ if cc<>-1 then
+ llparam:=cc
+ else
+ llparam:=WndToContact(WaitFocusedWndChild(GetForegroundwindow){GetFocus});
+ end
+ else if (flags2 and ACF2_SRV_LPAR)<>0 then
+ begin
+ if (flags and ACF_LPARNUM)=0 then
+ begin
+ if (flags and ACF_LUNICODE)=0 then
+ llparam:=uint_ptr(ParseVarString(pAnsiChar(llparam),Param))
+ else
+ llparam:=uint_ptr(ParseVarString(pWideChar(llparam),Param))
+ end
+ else
+ begin
+ tmp2:=ParseVarString(pWideChar(llparam),Param);
+ llparam:=StrToInt(tmp2);
+ end;
+ end;
+
+ res:=CallServiceSync(buf,lwparam,llparam);
+ result:=res;
+ if (flags and ACF_STRING)<>0 then
+ begin
+//!! delete old or not?
+ if (flags and ACF_UNICODE)=0 then
+ begin
+ AnsiToWide(pAnsiChar(res),pWideChar(result),MirandaCP);
+ if (flags2 and ACF2_FREEMEM)<>0 then
+ mFreeMem(pAnsiChar(res));
+ end
+ else if (flags2 and ACF2_FREEMEM)=0 then
+ StrDupW(pWideChar(result),pWideChar(res));
+ restype:=rtWide;
+ end
+ else if (flags and ACF_STRUCT)=0 then
+ restype:=rtInt;
+
+ if (flags and ACF_WSTRUCT)<>0 then
+ begin
+ if (flags and ACF_STRUCT)<>0 then
+ begin
+ result:=GetStructureResult(lwparam,@code,@len);
+ case code of
+ SST_LAST: begin
+ result:=LastResult;
+ end;
+ SST_PARAM: begin
+ result:=Param;
+ restype:=rtInt;
+ end;
+ SST_BYTE,SST_WORD,SST_DWORD,
+ SST_QWORD,SST_NATIVE: begin
+ restype:=rtInt;
+ end;
+ SST_BARR: begin
+{
+ mGetMem(pAnsiChar(res),len+1);
+ StrCopy(pAnsiChar(res),pAnsiChar(ofs),len);
+}
+ StrDup(pAnsiChar(res),pAnsiChar(result),len);
+ AnsiToWide(pAnsiChar(res),PWideChar(result),MirandaCP);
+ mFreeMem(pAnsiChar(res));
+ restype:=rtWide;
+ end;
+ SST_WARR: begin
+{
+ mGetMem(pWideChar(result),len+2);
+ len:= len div 2;
+ StrCopyW(pWideChar(result),pWideChar(ofs),len);
+}
+ StrDupW(pWideChar(result),pWideChar(result),len);
+ restype:=rtWide;
+ end;
+ SST_BPTR: begin
+ AnsiToWide(pAnsiChar(result),pWideChar(result),MirandaCP);
+ restype:=rtWide;
+ end;
+ SST_WPTR: begin
+ StrDupW(pWideChar(result),pWideChar(result));
+ restype:=rtWide;
+ end;
+ end;
+ end;
+ code:=SST_UNKNOWN;
+ FreeStructure(lwparam);
+ res:=result;
+ end
+ else if (flags2 and ACF2_SRV_WPAR)<>0 then
+ begin
+ if (flags and ACF_LPARNUM)=0 then
+ mFreeMem(pAnsiChar(lwparam))
+ else
+ mFreeMem(tmp1);
+ end;
+
+ if (flags and ACF_LSTRUCT)<>0 then
+ begin
+ if (flags and ACF_STRUCT)<>0 then
+ begin
+ result:=GetStructureResult(llparam,@code,@len);
+ case code of
+ SST_LAST: begin
+ result:=LastResult;
+ end;
+ SST_PARAM: begin
+ result:=Param;
+ restype:=rtInt;
+ end;
+ SST_BYTE,SST_WORD,SST_DWORD,
+ SST_QWORD,SST_NATIVE: begin
+ restype:=rtInt;
+ end;
+ SST_BARR: begin
+{
+ mGetMem(pAnsiChar(res),len+1);
+ StrCopy(pAnsiChar(res),pAnsiChar(ofs),len);
+}
+ StrDup(pAnsiChar(res),pAnsiChar(result),len);
+ AnsiToWide(pAnsiChar(res),PWideChar(result),MirandaCP);
+ mFreeMem(pAnsiChar(res));
+ restype:=rtWide;
+ end;
+ SST_WARR: begin
+{
+ mGetMem(pWideChar(result),len+2);
+ len:= len div 2;
+ StrCopyW(pWideChar(result),pWideChar(ofs),len);
+}
+ StrDupW(pWideChar(result),pWideChar(result),len);
+ restype:=rtWide;
+ end;
+ SST_BPTR: begin
+ AnsiToWide(pAnsiChar(result),pWideChar(result),MirandaCP);
+ restype:=rtWide;
+ end;
+ SST_WPTR: begin
+ StrDupW(pWideChar(result),pWideChar(result));
+ restype:=rtWide;
+ end;
+ end;
+ end;
+ code:=SST_UNKNOWN;
+ FreeStructure(llparam);
+ res:=result;
+ end
+ else if (flags2 and ACF2_SRV_LPAR)<>0 then
+ begin
+ if (flags and ACF_LPARNUM)=0 then
+ mFreeMem(pAnsiChar(llparam))
+ else
+ mFreeMem(tmp2);
+ end;
+
+ if (flags and (ACF_INSERT or ACF_MESSAGE or ACF_POPUP))<>0 then
+ begin
+ if restype=rtInt then
+ begin
+ if (flags and ACF_HEX)<>0 then
+ IntToHex(pWideChar(@buf),result)
+ else if ((flags and ACF_SIGNED)<>0) and (res<0) then
+ begin
+ pWideChar(@buf)[0]:='-';
+ IntToStr(PWideChar(@buf)+1,-result);
+ end
+ else
+ IntToStr(pWideChar(@buf),result);
+ res:=int_ptr(@buf);
+ end
+ else
+ res:=result;
+ end;
+
+ if (flags and ACF_INSERT )<>0 then SendMessageW(WaitFocusedWndChild(GetForegroundwindow){GetFocus},EM_REPLACESEL,0,res);
+ if (flags and ACF_POPUP )<>0 then ShowPopupW(PWideChar(res));
+ if (flags and ACF_MESSAGE)<>0 then MessageBoxW(0,PWideChar(res),'',0);
+
+ end;
+ if (flags2 and ACF2_SRV_SRVC)<>0 then
+ mFreeMem(lservice);
+ end;
+end;
+
+procedure PasteClipboard(dst:pWideChar);
+var
+ p:pWideChar;
+ fh:tHandle;
+begin
+ if StrPosW(dst,'^v')<>nil then
+ begin
+{
+ p:=PasteFromClipboard(false);
+ StrReplaceW(dst,'^v',p);
+ mFreeMem(p);
+}
+ if OpenClipboard(0) then
+ begin
+ fh:=GetClipboardData(cf_UnicodeText);
+ p:=GlobalLock(fh);
+ StrReplaceW(dst,'^v',p);
+ GlobalUnlock(fh);
+ CloseClipboard;
+ end
+ end
+end;
+
+type
+ trec = record
+ text:PAnsiChar;
+ one, two:integer;
+ end;
+
+function GetFileString(fname:PAnsiChar;linenum:integer):pWideChar;
+var
+ pc,FileBuf,CurLine:PAnsiChar;
+ f:THANDLE;
+ NumLines, j:integer;
+begin
+ f:=Reset(fname);
+ if f<>INVALID_HANDLE_VALUE then
+ begin
+ j:=FileSize(f);
+ mGetMem(FileBuf,j+1);
+ BlockRead(f,FileBuf^,j);
+ while (FileBuf+j)^<' ' do dec(j);
+ (FileBuf+j+1)^:=#0;
+ CloseHandle(f);
+ pc:=FileBuf;
+ CurLine:=pc;
+ NumLines:=1;
+ while pc^<>#0 do // count number of lines
+ begin
+ if pc^=#13 then
+ begin
+ if linenum=NumLines then
+ break;
+ inc(pc);
+ if pc^=#10 then
+ inc(pc);
+ inc(NumLines);
+ CurLine:=pc;
+ end
+ else
+ inc(pc);
+ end;
+ if (linenum>NumLines) or (linenum=0) then //ls - lastline
+ else if linenum<0 then
+ begin
+ randomize;
+ linenum:=random(NumLines)+1;
+ pc:=FileBuf;
+ NumLines:=1;
+ CurLine:=pc;
+ repeat
+ if (pc^=#13) or (pc^=#0) then
+ begin
+ if linenum=NumLines then
+ break;
+ if pc^<>#0 then
+ begin
+ inc(pc);
+ if pc^=#10 then
+ inc(pc);
+ end;
+ inc(NumLines);
+ CurLine:=pc;
+ end
+ else
+ inc(pc);
+ until false;
+ end;
+ pc^:=#0;
+ StrReplace(CurLine,'\n',#13#10);
+ StrReplace(CurLine,'\t',#09);
+ AnsiToWide(CurLine,result,CP_ACP);
+ mFreeMem(FileBuf);
+ end
+ else
+ result:=nil;
+end;
+
+function Split(buf:PWideChar;macro:PWideChar;var r:trec):integer;
+type
+ tconv = packed record
+ case boolean of
+ false: (res:int);
+ true: (lo,hi:word);
+ end;
+var
+ i:integer;
+ p,pp,lp:pWideChar;
+ ls:array [0..511] of WideChar;
+begin
+ result:=0;
+ i:=StrIndexW(buf,macro);
+ if i>0 then
+ begin
+ dec(i);
+ p:=buf+i+StrLenW(macro);
+ pp:=p;
+ while (p^<>#0) and (p^<>')') do
+ inc(p);
+ ls[0]:=#0;
+ if p^<>#0 then // correct syntax
+ begin
+ lp:=ls;
+ while (pp<>p) and (pp^<>',') do // filename
+ begin
+ lp^:=pp^;
+ inc(lp);
+ inc(pp);
+ end;
+ lp^:=#0;
+ WideToAnsi(ls,r.text,MirandaCP);
+ r.one:=-1;
+ r.two:=-1;
+ if pp^=',' then
+ begin
+ inc(pp);
+ r.one:=StrToInt(pp);
+ while (pp<>p) and (pp^<>',') do inc(pp);
+ if pp^=',' then
+ begin
+ inc(pp);
+ r.two:=StrToInt(pp);
+ end;
+ end;
+ tconv(result).lo:=p-buf-i+1; // length
+ tconv(result).hi:=i; // position
+ end;
+ end;
+end;
+
+procedure PasteFileString(dst:pWideChar);
+var
+ i:integer;
+ lp:pWideChar;
+ buf:array [0..511] of AnsiChar;
+ r:trec;
+begin
+ repeat
+ i:=Split(dst,'^f(',r);
+ if i>0 then
+ begin
+ StrDeleteW(dst,i shr 16,loword(i));
+ ConvertFileName(r.text,buf);
+// CallService(MS_UTILS_PATHTOABSOLUTE,dword(r.text),dword(@buf));
+ lp:=GetFileString(@buf,r.one);
+ if lp<>nil then
+ begin
+ StrInsertW(lp,dst,i shr 16);
+ mFreeMem(lp);
+ end;
+ end
+ else
+ break;
+ until false;
+end;
+
+procedure PasteSelectedText(wnd:hwnd;dst:pWideChar);
+var
+ sel:integer;
+ buf:pWideChar;
+begin
+ if (StrPosW(dst,'^s')<>nil) and (wnd<>0) then
+ begin
+ sel:=SendMessageW(wnd,EM_GETSEL,0,0);
+ if loword(sel)=(sel shr 16) then
+ StrReplaceW(dst,'^s',nil)
+ else
+ begin
+ buf:=GetDlgText(wnd,false);
+//!! next line was active. why?
+// SendMessageW(wnd,WM_GETTEXT,4095,dword(@buf));
+ buf[sel shr 16]:=#0;
+ StrReplaceW(dst,'^s',buf+loword(sel));
+ mFreeMem(buf);
+ end;
+ end;
+end;
+
+function CheckAuto(dst:pWideChar):bool;
+var
+ p:PWideChar;
+begin
+ result:=false;
+ if dst<>nil then
+ begin
+ p:=StrEndW(dst);
+ if (p-dst)>2 then
+ begin
+ dec(p,2);
+ if (p^='^') and ((p+1)^='a') then
+ begin
+ result:=true;
+ p^:=#0;
+ end;
+ end;
+ end;
+end;
+
+function InsertText(act:pHKAction;param:LPARAM;last:pWideChar):uint_ptr;
+var
+ tmp:PWideChar;
+ blob,p:PAnsiChar;
+ w:PWideChar;
+ hContact:THANDLE;
+ wnd:HWND;
+ fexist,autosend:bool;
+ dbei:TDBEVENTINFO;
+ i:cardinal;
+ cp:integer;
+ fh:THANDLE;
+ lstr:pWideChar;
+ llen:integer;
+ buf:array [0..31] of WideChar;
+ b,b1:array [0..MAX_PATH] of AnsiChar;
+begin
+ result:=uint_ptr(last);
+ with act^ do
+ begin
+ if (flags and ACF_CLIPBRD)<>0 then
+ begin
+ if (flags and ACF_COPYTO)<>0 then
+ CopyToClipboard(last,false)
+ else
+ result:=uint_ptr(PasteFromClipboard(false));
+ exit;
+ end;
+
+ hContact:=0;
+ if (flags and ACF_FILE)=0 then
+ begin
+ wnd:=WaitFocusedWndChild(GetForegroundWindow){GetFocus};
+ if wnd<>0 then
+ hContact:=WndToContact(wnd);
+ end
+ else
+ wnd:=0;
+
+ if hContact=0 then
+ begin
+ if CallService(MS_DB_CONTACT_IS,param,0)<>0 then
+ hContact:=param;
+ end;
+
+ if (flags and (ACF_FILE or ACF_FAPPEND or ACF_FWRITE))<>ACF_FILE then
+ begin
+ mGetMem (w ,BufferSize*SizeOf(WideChar));
+ FillChar(w^,BufferSize*SizeOf(WideChar),0);
+ StrCopyW(w,text);
+ PasteClipboard(w); // ^v
+ PasteFileString(w); // ^f
+ PasteSelectedText(wnd,w); // ^s
+ autosend:=CheckAuto(w); // ^a
+ StrReplaceW(w,'^l',last); // ^l
+ StrReplaceW(w,'^h',IntToHex(buf,StrToInt(last))); // ^h
+ StrReplaceW(w,'^t',#9); // ^t
+ StrReplaceW(w,'^e',nil); // ^e
+ end
+ else
+ autosend:=false;
+
+ if (flags2 and ACF2_TXT_TEXT)<>0 then
+ begin
+ tmp:=ParseVarString(w,hContact,last);
+ mFreeMem(w);
+ w:=tmp;
+ end;
+
+ if (flags and ACF_FILE)<>0 then
+ begin
+ cp:=0;
+ if (flags and ACF_ANSI)=ACF_ANSI then cp:=1
+ else if (flags and (ACF_UTF8 or ACF_SIGN))=ACF_UTF8 then cp:=2
+ else if (flags and (ACF_UTF8 or ACF_SIGN))=ACF_SIGN then cp:=4
+ else if (flags and (ACF_UTF8 or ACF_SIGN))=(ACF_UTF8 or ACF_SIGN) then cp:=3;
+
+ if (flags2 and ACF2_TXT_FILE)<>0 then
+ tmp:=ParseVarString(tfile,hContact,last)
+ else
+ tmp:=tfile;
+
+ if (flags and (ACF_FAPPEND or ACF_FWRITE))<>0 then
+ begin
+ case cp of
+ 1: begin
+ llen:=StrLen(WideToAnsi(w,pAnsiChar(lstr),MirandaCP));
+ end;
+ 2,3: begin
+ llen:=StrLen(WideToUTF8(w,pAnsiChar(lstr)));
+ end;
+ else
+ lstr:=w;
+ llen:=StrLenW(lstr)*SizeOf(WideChar);
+ end;
+ end
+ else
+ llen:=0;
+
+ fexist:=FileExists(tmp);
+ if fexist and ((flags and ACF_FAPPEND)<>0) then
+ begin
+ fh:=Append(tmp);
+ if fh<>THANDLE(INVALID_HANDLE_VALUE) then
+ begin
+ BlockWrite(fh,lstr^,llen);
+ end;
+ if (cp<>0) and (cp<>4) then
+ mFreeMem(lstr);
+ end
+ else if ((flags and ACF_FWRITE)<>0) or
+ (not fexist and ((flags and ACF_FAPPEND)<>0)) then
+ begin
+ fh:=ReWrite(tmp);
+ if fh<>THANDLE(INVALID_HANDLE_VALUE) then
+ begin
+ if cp=3 then
+ begin
+ i:=SIGN_UTF8;
+ BlockWrite(fh,i,3); // UTF8 sign
+ end
+ else if cp=4 then
+ begin
+ i:=SIGN_UNICODE;
+ BlockWrite(fh,i,2); // UTF16 sign
+ end;
+
+ BlockWrite(fh,lstr^,llen);
+ if (cp<>0) and (cp<>4) then
+ mFreeMem(lstr);
+ end;
+ end
+ else
+ begin
+ if StrPosW(tmp,'://')<>nil then // remote
+ begin
+ GetTempPathA(MAX_PATH,b);
+ GetTempFileNameA(b,'wat',GetCurrentTime,b1);
+ GetFile(FastWideToAnsiBuf(tmp,b),b1);
+ if tmp<>tfile then
+ mFreeMem(tmp);
+ FastAnsiToWide(b1,tmp);
+ end
+ else
+ b1[0]:=#0;
+ fh:=Reset(tmp);
+ if fh<>THANDLE(INVALID_HANDLE_VALUE) then
+ begin
+ i:=GetFSize(tmp);
+ mGetMem (w ,i+SizeOf(WideChar));
+ FillChar(w^,i+SizeOf(WideChar),0);
+ BlockRead(fh,w^,i);
+ if (flags and ACF_ANSI)<>0 then
+ begin
+ AnsiToWide(pAnsiChar(w),lstr,MirandaCP);
+ mFreeMem(w);
+ w:=lstr;
+ end
+ else if (flags and ACF_UTF8)<>0 then
+ begin
+ if (pdword(w)^ and $FFFFFF)=SIGN_UTF8 then
+ p:=pAnsiChar(w)+3
+ else
+ p:=pAnsiChar(w);
+ mFreeMem(w);
+ UTF8ToWide(p,w);
+ end
+ else
+ ChangeUnicode(w);
+ end;
+ if b1[0]<>#0 then
+ DeleteFileA(b1);
+ end;
+ if fh<>THANDLE(INVALID_HANDLE_VALUE) then
+ CloseHandle(fh);
+ if tmp<>tfile then
+ mFreeMem(tmp);
+ end;
+
+ result:=uint_ptr(w);
+
+ if (flags and ACF_FILE)=0 then
+ begin
+ if autosend then
+ begin
+ if hContact=0 then exit;
+ p:=GetContactProtoAcc(hContact);
+ cp:=DBReadDWord(hContact,'Tab_SRMsg','ANSIcodepage',MirandaCP);
+ if DBReadByte(hContact,p,'ChatRoom',0)<>1 then
+ begin
+ i:=WideToCombo(w,blob,cp);
+ // if CallContactService(hContact,PSS_MESSAGEW,0,dword(blob))=
+ // ACKRESULT_FAILED then
+ CallContactService(hContact,PSS_MESSAGE,PREF_UNICODE,tlparam(blob));
+ dbei.cbSize :=sizeof(dbei);
+ dbei.cbBlob :=i;
+ dbei.pBlob :=pByte(blob);
+ dbei.eventType:=EVENTTYPE_MESSAGE;
+ dbei.timestamp:=GetCurrentTime;
+ dbei.szModule :=p;
+ dbei.flags :=DBEF_SENT;
+ CallService(MS_DB_EVENT_ADD,hContact,tlparam(@dbei));
+ mFreeMem(blob);
+ end
+ else
+ SendToChat(hContact,w);
+ end
+ else
+ begin
+ GetWindowThreadProcessId(GetForegroundWindow,@i);
+ if (i=GetCurrentProcessId) and (wnd<>0) then
+ SendMessageW(wnd,EM_REPLACESEL,1,tlparam(w))
+ else
+ SendString(0,w);
+ end;
+ end;
+ end;
+end;
diff --git a/plugins/Actman/i_actlow.inc b/plugins/Actman/i_actlow.inc
new file mode 100644
index 0000000000..a749aef27f
--- /dev/null
+++ b/plugins/Actman/i_actlow.inc
@@ -0,0 +1,836 @@
+{Lowlevel actions work: clone, create, delete, execute}
+
+type
+ tAdvExpr = (aeNot,aeAdd,aeSub,aeMul,aeDiv,aeMod,aeAnd,aeOr,aeXor,aeSet);
+
+function GetActNameById(id:dword):PWideChar;
+var
+ i:integer;
+begin
+ for i:=0 to MaxGroups-1 do
+ begin
+ if ((GroupList^[i].flags and ACF_ASSIGNED)<>0) and
+ (id=GroupList^[i].id) then
+ begin
+ result:=GroupList^[i].descr;
+ exit;
+ end;
+ end;
+ result:=nil;
+end;
+
+function GetActIdByName(name:PWideChar):integer;
+var
+ i:integer;
+begin
+ for i:=0 to MaxGroups-1 do
+ begin
+ if ((GroupList^[i].flags and ACF_ASSIGNED)<>0) and
+ (StrCmpW(name,GroupList^[i].descr)=0) then
+ begin
+ result:=GroupList^[i].id;
+ exit;
+ end;
+ end;
+ result:=0;
+end;
+
+function GetActIdxByName(name:PWideChar):integer;
+var
+ i:integer;
+begin
+ for i:=0 to MaxGroups-1 do
+ begin
+ if ((GroupList^[i].flags and ACF_ASSIGNED)<>0) and
+ (StrCmpW(name,GroupList^[i].descr)=0) then
+ begin
+ result:=i;
+ exit;
+ end;
+ end;
+ result:=-1;
+end;
+
+function GetActIdxById(id:dword):integer;
+var
+ i:integer;
+begin
+ for i:=0 to MaxGroups-1 do
+ begin
+ if ((GroupList^[i].flags and ACF_ASSIGNED)<>0) and
+ (id=GroupList^[i].id) then
+ begin
+ result:=i;
+ exit;
+ end;
+ end;
+ result:=-1;
+end;
+
+function FreeAction(act:pHKAction):dword;
+begin
+ result:=act^.next;
+ with act^ do
+ begin
+ if (flags and ACF_ASSIGNED)<>0 then
+ begin
+ mFreeMem(descr);
+ case actionType of
+ ACT_SERVICE: begin
+ mFreeMem(service);
+ if (flags and (ACF_WPARNUM or ACF_WRESULT or ACF_WPARAM))=0 then
+ mFreeMem(pointer(wparam));
+ if ((flags and ACF_WPARNUM)<>0) and ((flags2 and ACF2_SRV_WPAR)<>0) then
+ mFreeMem(pointer(wparam));
+ if (flags and (ACF_LPARNUM or ACF_LRESULT or ACF_LPARAM))=0 then
+ mFreeMem(pointer(lparam));
+ if ((flags and ACF_LPARNUM)<>0) and ((flags2 and ACF2_SRV_LPAR)<>0) then
+ mFreeMem(pointer(lparam));
+ end;
+ ACT_PROGRAM: begin
+ mFreeMem(prgname);
+ mFreeMem(args);
+ end;
+ ACT_TEXT: begin
+ if (flags and ACF_CLIPBRD)=0 then
+ begin
+ mFreeMem(text);
+ if (flags and ACF_FILE)<>0 then
+ mFreeMem(tfile);
+ end;
+ end;
+ ACT_ADVANCE: begin
+ mFreeMem(varval);
+ if (action and ADV_ACT_POST)=ADV_ACT_JUMP then
+ mFreeMem(operval);
+ end;
+ ACT_CHAIN: begin
+ if (flags and ACF_BYNAME)<>0 then
+ mFreeMem(actname);
+ end;
+ ACT_RW: begin
+ mFreeMem(dbmodule);
+ mFreeMem(dbsetting);
+ if (flags and ACF_DBUTEXT)<>0 then
+ mFreeMem(dbvalue)
+ else if (flags2 and ACF2_RW_TVAR)<>0 then
+ mFreeMem(dbvalue);
+ end;
+ ACT_MESSAGE: begin
+ mFreeMem(msgtitle);
+ mFreeMem(msgtext);
+ end;
+ end;
+ end;
+ end;
+ FillChar(act^,SizeOf(act^),0);
+end;
+
+procedure FreeActions(list:pActList;idx:cardinal);
+begin
+ while idx<>0 do
+ idx:=FreeAction(@list^[idx]);
+end;
+
+procedure FreeActionsContinued(act:pHKAction);
+var
+ act_org:pHKAction;
+begin
+ act_org:=act;
+ repeat
+ FreeAction(act);
+ if act^.next<>0 then
+ inc(act)
+ else
+ break;
+ until false;
+ FreeMem(act_org);
+end;
+
+procedure DestroyActions(act:pActList;count:integer);
+var
+ pact:pHKAction;
+begin
+ pact:=@act^;
+ while count>0 do
+ begin
+ FreeAction(pact);
+ inc(pact);
+ dec(count);
+ end;
+ FreeMem(act);
+end;
+
+procedure CloneAction(dst,src:pHKAction);
+begin
+ move(src^,dst^,SizeOf(tHKAction));
+ with dst^ do
+ begin
+ if (flags and ACF_ASSIGNED)<>0 then
+ begin
+ StrDupW(descr,descr);
+ case actionType of
+ ACT_SERVICE: begin
+ StrDup(service,service);
+ if (flags and ACF_WPARNUM)=0 then
+ begin
+ if (flags and ACF_WSTRUCT)<>0 then
+ StrDupW(pWideChar(wparam),pWideChar(wparam))
+ else if (flags and ACF_WUNICODE)<>0 then
+ StrDupW(pWideChar(wparam),pWideChar(wparam))
+ else
+ StrDup(PAnsiChar(wparam),PAnsiChar(wparam));
+ end
+ else if (flags2 and ACF2_SRV_WPAR)<>0 then
+ StrDupW(pWideChar(wparam),pWideChar(wparam));
+
+ if (flags and ACF_LPARNUM)=0 then
+ begin
+ if (flags and ACF_LSTRUCT)<>0 then
+ StrDupW(pWideChar(lparam),pWideChar(lparam))
+ else if (flags and ACF_LUNICODE)<>0 then
+ StrDupW(pWideChar(lparam),pWideChar(lparam))
+ else
+ StrDup(PAnsiChar(lparam),PAnsiChar(lparam));
+ end
+ else if (flags2 and ACF2_SRV_LPAR)<>0 then
+ StrDupW(pWideChar(lparam),pWideChar(lparam));
+ end;
+
+ ACT_PROGRAM: begin
+ StrDupW(prgname,prgname);
+ StrDupW(args,args);
+ end;
+
+ ACT_TEXT: begin
+ if (flags and ACF_CLIPBRD)=0 then
+ begin
+ StrDupW(text,text);
+ if (flags and ACF_FILE)<>0 then
+ StrDupW(tfile,tfile);
+ end;
+ end;
+
+ ACT_ADVANCE: begin
+ StrDupW(varval,varval);
+ if (action and ADV_ACT_POST)=ADV_ACT_JUMP then
+ StrDupW(operval,operval);
+ end;
+
+ ACT_CHAIN: begin
+ if (flags or ACF_BYNAME)<>0 then
+ StrDupW(actname,actname);
+ end;
+
+ ACT_RW: begin
+ StrDup(dbmodule,dbmodule);
+ StrDup(dbsetting,dbsetting);
+ if (flags and ACF_DBUTEXT)<>0 then
+ StrDupW(pWideChar(dbvalue),pWideChar(dbvalue))
+ else if (flags2 and ACF2_RW_TVAR)<>0 then
+ StrDupW(pWideChar(dbvalue),pWideChar(dbvalue));
+ end;
+
+ ACT_MESSAGE: begin
+ StrDupW(msgtitle,msgtitle);
+ StrDupW(msgtext,msgtext);
+ end;
+ end;
+ end;
+ end;
+end;
+
+function CloneActions(idx:cardinal):pointer;
+var
+ i,count:integer;
+ aList:pHKAction;
+begin
+ count:=0;
+ i:=idx;
+ while i<>0 do
+ begin
+ inc(count);
+ i:=ActionList^[i].next;
+ end;
+ if count>0 then
+ begin
+ GetMem(result,count*SizeOf(tHKAction));
+ aList:=result;
+ i:=idx;
+ while i<>0 do
+ begin
+ CloneAction(aList,@ActionList^[i]);
+ i:=ActionList^[i].next;
+ inc(aList);
+ end;
+ end
+ else
+ result:=nil;
+end;
+
+function DoAction(action:dword;aparam:LPARAM;var last:uint_ptr;restype:dword):integer;
+var
+ tmpact,act,act_org:pHKAction;
+ val,prelast:uint_ptr;
+ b:boolean;
+ i:integer;
+ lContact:THANDLE;
+ buf:array [0..31] of WideChar;
+ tmpc,tmpc1,tmpc2,tmpcv1,tmpcv2:pWideChar;
+ oldrestype:integer;
+begin
+ if action<>0 then
+ begin
+ act:=CloneActions(action);
+ // if act=nil then exit;
+ act_org :=act;
+ oldrestype:=restype;
+ prelast :=0;
+ repeat
+ if (act^.flags and ACF_DISABLED)=0 then
+ begin
+
+ if (oldrestype=rtWide) and (last<>prelast) then
+ mFreeMem(prelast);
+ oldrestype:=restype;
+ prelast:=last;
+
+ with act^ do
+ begin
+ case actionType of
+
+ ACT_CONTACT: begin
+ if (flags and ACF_KEEPONLY)=0 then
+ last:=OpenContact(contact)
+ else
+ last:=contact;
+
+ restype:=rtInt;
+ end;
+
+ ACT_SERVICE: begin
+ last:=RunService(act,last,aparam,restype);
+{
+ if (flags and ACF_STRING)<>0 then
+ begin
+ if (flags and ACF_UNICODE)=0 then
+ begin
+ val:=last;
+ AnsiToWide(pAnsiChar(val),pWideChar(last),MirandaCP);
+ mFreeMem(val);
+ end
+ else
+ StrDupW(pWideChar(last),pWideChar(last));
+ restype:=rtWide;
+ end
+ else
+ restype:=rtInt;
+}
+ end;
+
+ ACT_PROGRAM: begin
+ if restype=rtInt then
+ last:=uint_ptr(IntToStr(buf,last));
+
+ last:=RunProgram(act,aparam,pWideChar(last));
+
+ restype:=rtInt;
+ end;
+
+ ACT_TEXT: begin
+ if restype=rtInt then
+ last:=uint_ptr(IntToStr(buf,last));
+
+ last:=InsertText(act,aparam,pWideChar(last));
+
+ restype:=rtWide;
+ end;
+
+ ACT_ADVANCE: begin
+ if restype=rtWide then
+ val:=StrToInt(pWideChar(last))
+ else
+ val:=last;
+
+ case condition and not ADV_COND_NOT of
+ ADV_COND_EQ: b:=val=value;
+ ADV_COND_GT: b:=integer(val)>integer(value);
+ ADV_COND_LT: b:=integer(val)<integer(value);
+ else
+ b:=true;
+ end;
+ if ((condition and ADV_COND_NOT)<>0) and (condition<>ADV_COND_NOP) then
+ b:=not b;
+ if b then
+ begin
+ case action and ADV_ACTION of
+
+ ADV_ACT_MATH: begin
+ case tAdvExpr(oper) of
+ aeNot: last:= not val;
+ aeAdd: last:= integer(val) + integer(mathval);
+ aeSub: last:= integer(val) - integer(mathval);
+ aeMul: last:= integer(val) * integer(mathval);
+ aeDiv: last:= integer(val) div integer(mathval);
+ aeMod: last:= val mod mathval;
+ aeAnd: last:= val and mathval;
+ aeOr : last:= val or mathval;
+ aeXor: last:= val xor mathval;
+ aeSet: last:= mathval;
+ end;
+ restype:=rtInt;
+ end;
+
+ ADV_ACT_VARS: begin
+//!! need to clear 'Last' if was string?
+ if (varval<>NIL) and (varval^<>#0) then
+ begin
+ if CallService(MS_DB_CONTACT_IS,aparam,0)<>0 then
+ lContact:=aparam
+ else
+ lContact:=0;
+ if restype=rtInt then
+ last:=uint_ptr(IntToStr(buf,last));
+
+ pWideChar(last):=ParseVarString(varval,lContact,pWideChar(last));
+
+ if (flags and ACF_VARASINT)<>0 then
+ begin
+ tmpc:=pWideChar(last);
+ last:=StrToInt(tmpc);
+ mFreeMem(tmpc);
+ restype:=rtInt;
+ end
+ else
+ restype:=rtWide;
+ end;
+ end;
+
+ end;
+ case action and ADV_ACT_POST of
+
+ ADV_ACT_JUMP : begin
+ tmpact:=act_org;
+ repeat
+ if StrCmpW(tmpact^.descr,operval)=0 then
+ begin
+ act:=tmpact;
+ tmpact:=nil;
+ break;
+ end;
+ if tmpact^.next=0 then
+ break;
+ inc(tmpact);
+ until false;
+ if tmpact=nil then continue;
+ end;
+
+ ADV_ACT_BREAK: break;
+ end;
+ end;
+ end;//last:=MakeAdvanced(act,last);
+
+ ACT_CHAIN: begin
+ if (flags and ACF_BYNAME)<>0 then
+ i:=GetActIdxByName(actname)
+ else
+ i:=GetActIdxById(id);
+
+ if i>=0 then
+ begin
+ restype:=DoAction(GroupList^[i].firstAction,aparam,last,restype);
+ // cleared in called Action
+ oldrestype:=rtInt;
+ prelast:=0;
+ end
+ else
+ begin
+ restype:=rtInt;
+ last:=0;
+ end;
+ end;
+
+ ACT_RW: begin
+ if (flags and ACF_CURRENT)<>0 then i:=0
+ else if (flags and ACF_PARAM )<>0 then i:=aparam
+ else if (flags and ACF_RESULT )<>0 then i:=last
+ else
+ i:=dbcontact;
+ if (flags and ACF_LAST)=0 then
+ val:=dbvalue
+ else
+ begin
+ val:=last;
+ if (flags and ACF_DBUTEXT)<>0 then
+ begin
+ if restype=rtInt then
+ val:=uint_ptr(IntToStr(buf,val));
+ end
+ else
+ begin
+ if restype=rtWide then
+ val:=StrToInt(pWideChar(val));
+ end;
+ end;
+
+ last:=DBRW(act,i,val,last,restype);
+
+ if (flags and ACF_DBUTEXT)<>0 then
+ restype:=rtWide
+ else
+ restype:=rtInt;
+ end;
+
+ ACT_MESSAGE: begin
+ if restype=rtWide then
+ tmpc:=PWideChar(last)
+ else
+ begin
+ IntToStr(buf,last);
+ tmpc:=@buf;
+ end;
+
+ if StrPosW(msgtitle,'<last>')<>nil then
+ begin
+ mGetMem(tmpc1,8192);
+ StrCopyW(tmpc1,msgtitle);
+ StrReplaceW(tmpc1,'<last>',tmpc);
+ end
+ else
+ tmpc1:=msgtitle;
+ if StrPosW(msgtext,'<last>')<>nil then
+ begin
+ mGetMem(tmpc2,8192);
+ StrCopyW(tmpc2,msgtext);
+ StrReplaceW(tmpc2,'<last>',tmpc);
+ end
+ else
+ tmpc2:=msgtext;
+
+ if (flags2 and ACF2_MSG_TTL)<>0 then
+ tmpcv1:=ParseVarString(tmpc1,aparam,tmpc)
+ else
+ tmpcv1:=tmpc1;
+ if (flags2 and ACF2_MSG_TXT)<>0 then
+ tmpcv2:=ParseVarString(tmpc2,aparam,tmpc)
+ else
+ tmpcv2:=tmpc2;
+
+ i:=MessageBoxW(0,tmpcv2,tmpcv1,boxopts);
+
+ if (flags and ACF_MSG_KEEP)=0 then
+ begin
+ restype:=rtInt;
+ last:=i
+ end;
+
+ if tmpcv1<>tmpc1 then mFreeMem(tmpcv1);
+ if tmpcv2<>tmpc2 then mFreeMem(tmpcv2);
+ if tmpc1 <>msgtitle then mFreeMem(tmpc1);
+ if tmpc2 <>msgtext then mFreeMem(tmpc2);
+ end;
+
+ else
+ last:=0;
+ end;
+ end;
+ end;
+ if (act_org^.flags and ACF_DOBREAK)<>0 then
+ break;
+ if act^.next=0 then
+ break;
+ inc(act);
+ until false;
+ FreeActionsContinued(act_org);
+ if (oldrestype=rtWide) and (last<>prelast) then
+ mFreeMem(prelast);
+ end;
+ result:=restype;
+end;
+
+type
+ pActStartData = ^tActStartData;
+ tActStartData = record
+ event :THANDLE;
+ action:dword;
+ param :LPARAM;
+ group :pHKRecord;
+ last :LPARAM;
+ end;
+
+procedure ThDoAction(arg:pActStartData); cdecl;
+var
+ ltmp:uint_ptr;
+ res:integer;
+begin
+ ltmp:=arg^.last;
+
+ if arg^.group<>nil then
+ begin
+ NotifyEventHooks(hevaction,arg^.group.id,0); // started
+ arg^.group.flags:=arg^.group.flags or ACF_USEDNOW;
+ end;
+
+ res:=DoAction(arg^.action,arg^.param,ltmp,rtInt);
+
+ if arg^.group<>nil then
+ begin
+ arg^.group.flags:=arg^.group.flags and not ACF_USEDNOW;
+ NotifyEventHooks(hevaction,arg^.group.id,1); // finished
+ end;
+
+ if arg^.event<>0 then
+ begin
+ arg^.last:=ltmp;
+ SetEvent(arg^.event);
+ end
+ else if res=rtWide then
+ begin
+ mFreeMem(ltmp);
+ end;
+end;
+
+function ActionStarterWait(action:dword;aparam:LPARAM=0;group:pHKRecord=nil;alast:LPARAM=0):LPARAM;
+var
+ tmp:pActStartData;
+begin
+ mGetMem(tmp,SizeOf(tActStartData));
+ tmp^.action:=action;
+ tmp^.param :=aparam;
+ tmp^.group :=group;
+ tmp^.last :=alast;
+ tmp^.event :=CreateEvent(nil,FALSE,FALSE,nil);
+ CloseHandle(mir_forkthread(@ThDoAction,tmp));
+ WaitForSingleObjectEx(tmp.event,INFINITE,true);
+ CloseHandle(tmp^.event);
+ result:=tmp^.last;
+end;
+
+function ActionStarter(action:dword;aparam:dword=0;group:pHKRecord=nil;alast:dword=0):integer;
+var
+ tmp:pActStartData;
+begin
+ result:=0;
+ mGetMem(tmp,SizeOf(tActStartData));
+ tmp^.action:=action;
+ tmp^.param :=aparam;
+ tmp^.group :=group;
+ tmp^.last :=alast;
+ tmp^.event :=0;
+ CloseHandle(mir_forkthread(@ThDoAction,tmp));
+end;
+
+procedure ReallocActionList(var ActList:pActList;var MaxAct:cardinal);
+var
+ i:cardinal;
+ tmp:pActList;
+begin
+ i:=(MaxAct+ActListPage)*SizeOf(tHKAction);
+ GetMem(tmp,i);
+ FillChar(tmp^,i,0);
+ if MaxAct>0 then
+ begin
+ move(ActList^,tmp^,MaxAct*SizeOf(tHKAction));
+ FreeMem(ActList);
+ end;
+ ActList:=tmp;
+ inc(MaxAct,ActListPage);
+end;
+
+function NewAction(var ActList:pActList;var MaxAct:cardinal):cardinal;
+var
+ i:cardinal;
+ pAct:pHKAction;
+begin
+ i:=1;
+ pAct:=@ActList^;
+ inc(pAct); // skip zero
+ while i<MaxAct do
+ begin
+ if (pAct^.flags and ACF_ASSIGNED)=0 then
+ begin
+ result:=i;
+ FillChar(pAct^,SizeOf(tHKAction),0);
+ pAct^.actionType:=ACT_CONTACT;
+ pAct^.flags :=ACF_ASSIGNED;
+ exit;
+ end;
+ inc(i);
+ inc(pAct);
+ end;
+
+ if MaxAct=0 then
+ result:=1
+ else
+ result:=MaxAct;
+
+ ReallocActionList(ActList,MaxAct);
+
+ ActList^[result].actionType:=ACT_CONTACT;
+ ActList^[result].flags :=ACF_ASSIGNED;
+end;
+
+procedure ReallocHKList(var HKList:pHKList;var MaxHK:cardinal);
+var
+ i:cardinal;
+ tmp:pHKList;
+begin
+ i:=(MaxHK+HKListPage)*SizeOf(tHKRecord);
+ GetMem(tmp,i);
+ FillChar(tmp^,i,0);
+ if MaxHK>0 then
+ begin
+ move(HKList^,tmp^,MaxHK*SizeOf(tHKRecord));
+ FreeMem(HKList);
+ end;
+ HKList:=tmp;
+ inc(MaxHK,HKListPage);
+end;
+
+procedure InitGroupValue(pHK:pHKRecord);
+var
+// time:TSYSTEMTIME;
+ tmp:int64;
+begin
+ with pHK^ do
+ begin
+ StrDupW(descr,NoDescription);
+{
+ GetSystemTime(time);
+ id :=time.wSecond+time.wMinute*60+time.wHour*3600+time.wMilliseconds*86400;
+}
+ QueryPerformanceCounter(tmp);
+ id :=tmp and $FFFFFFFF;
+ firstAction:=0;
+ active :=nil;
+ flags :=ACF_ASSIGNED;
+ end;
+end;
+
+// Root,Size,MaxCount(Page,flag)
+function NewGroup(var HKList:pHKList;var MaxHK:cardinal):cardinal;
+var
+ i:cardinal;
+ pHK:pHKRecord;
+begin
+ i:=0;
+ pHK:=@HKList^;
+ while i<MaxHK do
+ begin
+ if (pHK^.flags and ACF_ASSIGNED)=0 then
+ begin
+ result:=i;
+ InitGroupValue(pHK);
+ exit;
+ end;
+ inc(i);
+ inc(pHK);
+ end;
+ // realloc
+ result:=MaxHK;
+ ReallocHKList(HKList,MaxHK);
+ InitGroupValue(@HKList^[result]);
+end;
+
+procedure FreeGroup(num:cardinal);
+begin
+ with GroupList^[num] do
+ begin
+ if (flags and ACF_ASSIGNED)<>0 then
+ begin
+ flags:=0;
+ mFreeMem(descr);
+ FreeActions(ActionList,firstAction);
+ end;
+ end;
+end;
+
+procedure FreeGroups;
+var
+ i:integer;
+begin
+ for i:=0 to MaxGroups-1 do
+ begin
+ FreeGroup(i);
+ end;
+ MaxGroups:=0;
+ FreeMem(GroupList);
+ FreeMem(ActionList);
+ GroupList:=nil;
+ ActionList:=nil;
+end;
+
+procedure DestroyGroups(HKList:pHKList;count:integer);
+var
+ pHK:pHKRecord;
+begin
+ pHK:=@HKList^;
+ while count>0 do
+ begin
+ if (pHK^.flags and ACF_ASSIGNED)<>0 then
+ mFreeMem(pHK^.descr);
+ inc(pHK);
+ dec(count);
+ end;
+ FreeMem(HKList);
+end;
+
+function CloneActionList:pActList;
+var
+ src,dst:pHKAction;
+ i:integer;
+begin
+ i:=MaxActions;
+ GetMem(result,i*SizeOf(tHKAction));
+ src:=@ActionList^;
+ dst:=@result^;
+ while i>0 do
+ begin
+ CloneAction(dst,src);
+ inc(src);
+ inc(dst);
+ dec(i);
+ end;
+end;
+
+procedure CloneGroup(dst,src:pHKRecord);
+begin
+ move(src^,dst^,SizeOf(tHKRecord));
+ if (src^.flags and ACF_ASSIGNED)<>0 then
+ StrDupW(dst^.descr,src^.descr);
+end;
+
+function CloneGroupList:pHKList;
+var
+ src,dst:pHKRecord;
+ i:integer;
+begin
+ i:=MaxGroups;
+ GetMem(result,i*SizeOf(tHKRecord));
+ src:=@GroupList^;
+ dst:=@result^;
+ while i>0 do
+ begin
+ CloneGroup(dst,src);
+ inc(src);
+ inc(dst);
+ dec(i);
+ end;
+end;
+
+function ActSelect(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+begin
+ if (wParam and ACCF_ID)<>0 then
+ result:=GetActIdxById(lParam)
+ else
+ result:=GetActIdxByName(pWideChar(lParam));
+ if result=-1 then
+ exit;
+ with GroupList^[result] do
+ begin
+ if (wParam and ACCF_CLEAR)<>0 then
+ flags:=flags and not (uint_ptr(wParam) and ACCF_FLAGS)
+ else
+ flags:=flags or (uint_ptr(wParam) and ACCF_FLAGS);
+ end;
+end;
diff --git a/plugins/Actman/i_const.inc b/plugins/Actman/i_const.inc
new file mode 100644
index 0000000000..3e9950414f
--- /dev/null
+++ b/plugins/Actman/i_const.inc
@@ -0,0 +1,219 @@
+{resource constants}
+const
+ // dialogs
+ IDD_ACTION = 1025;
+ IDD_STRUCTURE = 1027;
+ IDD_ASK = 1028;
+
+ // icons
+ IDI_NEW = 1025;
+ IDI_UP = 1026;
+ IDI_DOWN = 1027;
+ IDI_DELETE = 1028;
+ IDI_RELOAD = 1029;
+ IDI_CONTACT = 1030;
+ IDI_SERVICE = 1031;
+ IDI_PROGRAM = 1032;
+ IDI_INSERT = 1033;
+ IDI_ADVANCE = 1034;
+ IDI_CHAIN = 1035;
+ IDI_RW = 1036;
+ IDI_TEST = 1037;
+ IDI_EXPORT = 1038;
+ IDI_IMPORT = 1039;
+ IDI_MESSAGE = 1040;
+ IDI_VAR_CHECKED = 1041;
+ IDI_VAR_UNCHECKED = 1042;
+ IDI_FORMAT = 1043;
+ IDI_APPLY = 1044;
+
+ // Structure editor
+ IDC_DATA_FULL = 2001;
+ IDC_DATA_TYPE = 2002;
+ IDC_DATA_EDIT = 2005;
+ IDC_DATA_LEN = 2006;
+ IDC_DATA_HELP = 2007;
+
+ IDC_DATA_NEW = 2008;
+ IDC_DATA_DELETE = 2009;
+ IDC_DATA_UP = 2010;
+ IDC_DATA_DOWN = 2011;
+ IDC_DATA_CHANGE = 2012;
+ IDC_DATA_VARS = 2013;
+ IDC_DATA_PACKED = 2014;
+
+ // Action page
+// IDC_RESET = 2001;
+
+ IDC_ACTION_TYPE = 2005;
+ IDC_STAT_ACTION = 2006;
+
+ IDC_ACTION_LIST = 2007;
+ IDC_ACTION_NEW = 2008;
+ IDC_ACTION_DELETE = 2009;
+ IDC_ACTION_UP = 2010;
+ IDC_ACTION_DOWN = 2011;
+
+ IDC_ACTION_GROUP = 2012;
+ IDC_GROUP_NEW = 2015;
+ IDC_GROUP_RELOAD = 2016;
+ IDC_GROUP_DELETE = 2017;
+ IDC_GROUP_TEST = 2018;
+ IDC_GROUP_UP = 2019;
+ IDC_GROUP_DOWN = 2020;
+ IDC_GROUP_EXPORT = 2021;
+ IDC_GROUP_IMPORT = 2022;
+ IDC_ACTION_HELP = 2023;
+
+ IDC_STAT_CONTACT = 2140;
+ IDC_CONTACTLIST = 2141;
+ IDC_CNT_KEEP = 2142;
+ IDC_STAT_FORMAT = 2143;
+ IDC_EDIT_FORMAT = 2144;
+ IDC_CNT_FILTER = 2145;
+ IDC_CNT_APPLY = 2146;
+ IDC_STAT_FHELP = 2147;
+
+ IDC_STAT_WPAR1 = 2150;
+ IDC_STAT_LPAR1 = 2151;
+ IDC_STAT_WPAR = 2152;
+ IDC_STAT_LPAR = 2153;
+ IDC_FLAG_WPAR = 2154;
+ IDC_FLAG_LPAR = 2155;
+ IDC_EDIT_WPAR = 2156;
+ IDC_EDIT_LPAR = 2157;
+ IDC_STAT_SERVICE = 2158;
+ IDC_EDIT_SERVICE = 2159;
+ IDC_WSTRUCT = 2160;
+ IDC_LSTRUCT = 2161;
+
+ IDC_RES_POPUP = 2251;
+ IDC_RES_MESSAGE = 2252;
+ IDC_RES_INSERT = 2253;
+ IDC_SRV_RESSTAT = 2254;
+ IDC_SRV_RESULT = 2255;
+ IDC_RES_FREEMEM = 2256;
+ IDC_RES_UNICODE = 2257;
+ IDC_RES_SIGNED = 2258;
+ IDC_RES_GROUP = 2259;
+
+ IDC_FLAG_MINIMIZE = 2350;
+ IDC_STAT_PRGPATH = 2351;
+ IDC_EDIT_PRGPATH = 2352;
+ IDC_PROGRAM = 2353;
+ IDC_STAT_PRGARGS = 2354;
+ IDC_EDIT_PRGARGS = 2355;
+ IDC_EDIT_PROCTIME = 2356;
+ IDC_PROCESS_GROUP = 2357;
+ IDC_STAT_PROCTIME = 2358;
+ IDC_PRSTART_GROUP = 2359;
+ IDC_FLAG_NORMAL = 2360;
+ IDC_FLAG_HIDDEN = 2361;
+ IDC_FLAG_MAXIMIZE = 2362;
+ IDC_FLAG_CURPATH = 2363;
+ IDC_FLAG_PARALLEL = 2364;
+ IDC_FLAG_CONTINUE = 2365;
+ IDC_HLP_FVARS = 2366;
+
+ IDC_HLP_VARS = 2451;
+ IDC_STAT_INSERT = 2452;
+ IDC_EDIT_INSERT = 2453;
+
+ IDC_FLAG_CLIP = 2454;
+ IDC_FLAG_MESSAGE = 2455;
+ IDC_CLIP_COPYTO = 2456;
+ IDC_CLIP_PASTE = 2457;
+ IDC_CLIP_GROUP = 2458;
+ IDC_FILE_ENC = 2459;
+// IDC_CLIP_ANSI = 2459;
+// IDC_CLIP_WIDE = 2460;
+ IDC_FLAG_FILE = 2461;
+ IDC_FILE_PATH = 2462;
+ IDC_FILE_FILEBTN = 2463;
+ IDC_FILE_READ = 2464;
+ IDC_FILE_WRITE = 2465;
+ IDC_FILE_APPEND = 2466;
+ IDC_FILE_GROUP = 2467;
+
+ IDC_STAT_GROUPS = 2500;
+ IDC_GROUP_LIST = 2501;
+
+ IDC_CONDITION = 2505;
+ IDC_FLAG_GT = 2506;
+ IDC_FLAG_LT = 2507;
+ IDC_FLAG_EQ = 2508;
+ IDC_FLAG_NOP = 2509;
+ IDC_FLAG_NOT = 2510;
+
+ IDC_OPERATION = 2511;
+ IDC_FLAG_BREAK = 2512;
+ IDC_FLAG_JUMP = 2513;
+ IDC_FLAG_ANOP = 2514;
+ IDC_ADV_VALUE = 2515;
+ IDC_STAT_VAL = 2516;
+
+ IDC_FLAG_MATH = 2519;
+ IDC_ADV_OPER = 2520;
+ IDC_ADV_VAL1 = 2521;
+ IDC_ADV_VAL2 = 2522;
+ IDC_FLAG_VARS = 2523;
+ IDC_ADV_VARS = 2524;
+ IDC_ADV_HVARS = 2526;
+ IDC_ADV_ASINT = 2527;
+
+ IDC_RW_READ = 2601;
+ IDC_RW_WRITE = 2602;
+ IDC_RW_DELETE = 2603;
+ IDC_RW_STATM = 2605;
+ IDC_RW_MODULE = 2606;
+ IDC_RW_STATS = 2607;
+ IDC_RW_SETTING = 2608;
+ IDC_RW_TEXT = 2609;
+ IDC_RW_VALUE = 2610;
+ IDC_RW_DATATYPE = 2611;
+ IDC_RW_CURRENT = 2614;
+ IDC_RW_PARAM = 2615;
+ IDC_RW_MANUAL = 2616;
+ IDC_RW_OPER = 2617;
+ IDC_RW_VAL = 2618;
+ IDC_RW_RESULT = 2619;
+ IDC_RW_LAST = 2620;
+
+ IDC_MSG_STAT1 = 2701;
+ IDC_MSG_STAT2 = 2702;
+ IDC_MSG_TITLE = 2703;
+ IDC_MSG_TEXT = 2704;
+ IDC_MSG_BTNS = 2705;
+ IDC_MSGB_ARI = 2706;
+ IDC_MSGB_OK = 2707;
+ IDC_MSGB_OC = 2708;
+ IDC_MSGB_RC = 2709;
+ IDC_MSGB_YN = 2710;
+ IDC_MSGB_YNC = 2711;
+ IDC_MSG_ICONS = 2712;
+ IDC_MSGI_NONE = 2713;
+ IDC_MSGI_WARN = 2714;
+ IDC_MSGI_INFO = 2715;
+ IDC_MSGI_QUEST = 2716;
+ IDC_MSGI_ERROR = 2717;
+ IDC_MSG_KEEP = 2718;
+
+// Variables buttons
+ IDC_SRV_WPAR = 3000;
+ IDC_SRV_LPAR = 3001;
+ IDC_SRV_SRVC = 3002;
+ IDC_PRG_PRG = 3003;
+ IDC_PRG_ARG = 3004;
+ IDC_TXT_FILE = 3005;
+ IDC_TXT_TEXT = 3006;
+ IDC_RW_MVAR = 3008;
+ IDC_RW_SVAR = 3009;
+ IDC_RW_TVAR = 3010;
+ IDC_MSG_TTL = 3011;
+ IDC_MSG_TXT = 3012;
+
+// Question
+ IDC_ASK = 1025;
+ IDC_YESALL = 1026;
+ IDC_NOALL = 1027;
+ IDC_APPEND = 1028;
diff --git a/plugins/Actman/i_contact.inc b/plugins/Actman/i_contact.inc
new file mode 100644
index 0000000000..40cd6f18f7
--- /dev/null
+++ b/plugins/Actman/i_contact.inc
@@ -0,0 +1,113 @@
+{hkContact}
+const
+ defformat = '%name% - %uid% (%account%:%group%)';
+
+procedure FillContactList(list:hwnd; filter:boolean=true;format:pWideChar=nil);
+var
+ hContact:THANDLE;
+ buf:array [0..511] of WideChar;
+ buf1:array [0..63] of WideChar;
+ p:PWideChar;
+ uid:pAnsiChar;
+ ldbv:TDBVARIANT;
+ acc:pAnsiChar;
+ lName,
+ lGroup,
+ lAccount,
+ lUID:boolean;
+begin
+ if format=nil then format:=defformat;
+
+ SendMessage(list,CB_RESETCONTENT,0,0);
+ hContact:=CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+
+ lName :=StrPosW(format,'%name%')<>nil;
+ lGroup :=StrPosW(format,'%group%')<>nil;
+ lAccount:=StrPosW(format,'%account%')<>nil;
+ lUID :=StrPosW(format,'%uid%')<>nil;
+
+ while hContact<>0 do
+ begin
+ if ((not filter) and ((IsContactActive(hContact)+1)>=0)) or // + disabled (not deleted)
+ (filter and (IsContactActive(hContact) >=0)) then
+ begin
+ StrCopyW(buf,format);
+ if lName then
+ StrReplaceW(buf,'%name%',
+ PWideChar(CallService(MS_CLIST_GETCONTACTDISPLAYNAME,hContact,GCDNF_UNICODE)));
+
+ if lGroup then
+ begin
+ p:=DBReadUnicode(hContact,strCList,'Group',nil);
+ StrReplaceW(buf,'%group%',p);
+ mFreeMem(p);
+ end;
+
+ if lAccount then
+ begin
+ acc:=GetContactProtoAcc(hContact);
+ StrReplaceW(buf,'%account%',FastAnsiToWideBuf(acc,buf1));
+ end
+ else
+ acc:=nil;
+
+ if lUID then
+ begin
+ if acc=nil then
+ acc:=GetContactProtoAcc(hContact);
+ if IsChat(hContact) then
+ begin
+ p:=DBReadUnicode(hContact,acc,'ChatRoomID');
+ StrReplaceW(buf,'%uid%',p);
+ mFreeMem(p);
+ end
+ else
+ begin
+ uid:=pAnsiChar(CallProtoService(acc,PS_GETCAPS,PFLAG_UNIQUEIDSETTING,0));
+ if uint_ptr(uid)<>CALLSERVICE_NOTFOUND then
+ begin
+ if DBReadSetting(hContact,acc,uid,@ldbv)=0 then
+ begin
+ case ldbv._type of
+ DBVT_DELETED: p:='[deleted]';
+ DBVT_BYTE : p:=IntToStr(buf1,ldbv.bVal);
+ DBVT_WORD : p:=IntToStr(buf1,ldbv.wVal);
+ DBVT_DWORD : p:=IntToStr(buf1,ldbv.dVal);
+ DBVT_UTF8 : UTF8ToWide(ldbv.szVal.A,p);
+ DBVT_ASCIIZ : AnsiToWide(ldbv.szVal.A,p,MirandaCP);
+ DBVT_WCHAR : p:=ldbv.szVal.W;
+ DBVT_BLOB : p:='blob';
+ end;
+ StrReplaceW(buf,'%uid%',p);
+ if ldbv._type in [DBVT_UTF8,DBVT_ASCIIZ] then
+ mFreeMem(p);
+ DBFreeVariant(@ldbv);
+ end;
+ end;
+ StrReplaceW(buf,'%uid%',nil);
+ end;
+ end;
+
+ SendMessage(list,CB_SETITEMDATA,
+ SendMessageW(list,CB_ADDSTRING,0,tlparam(@buf)),
+ hContact);
+ end;
+ hContact:=CallService(MS_DB_CONTACT_FINDNEXT,hContact,0);
+ end;
+end;
+
+function FindContact(list:hwnd;contact:THANDLE):integer;
+var
+ i,j:integer;
+begin
+ result:=0;
+ j:=SendMessage(list,CB_GETCOUNT,0,0);
+ for i:=0 to j-1 do
+ begin
+ if THANDLE(SendMessage(list,CB_GETITEMDATA,i,0))=contact then
+ begin
+ result:=i;
+ break;
+ end;
+ end;
+end;
diff --git a/plugins/Actman/i_dlglists.inc b/plugins/Actman/i_dlglists.inc
new file mode 100644
index 0000000000..ce2cc2138d
--- /dev/null
+++ b/plugins/Actman/i_dlglists.inc
@@ -0,0 +1,75 @@
+{Dialog list filling}
+ procedure InsertString(wnd:HWND;num:dword;str:PAnsiChar);
+ var
+ buf:array [0..127] of WideChar;
+ begin
+ SendMessageW(wnd,CB_SETITEMDATA,
+ SendMessageW(wnd,CB_ADDSTRING,0,
+ lparam(TranslateW(FastAnsiToWideBuf(str,buf)))),
+ num);
+ {
+ SendMessageW(wnd,CB_INSERTSTRING,num,
+ dword(TranslateW(FastAnsiToWideBuf(str,buf))));
+ }
+ end;
+
+ procedure MakeMathOperList(wnd:HWND);
+ begin
+ SendMessage(wnd,CB_RESETCONTENT,0,0);
+ InsertString(wnd,cardinal(aeNot),'! not');
+ InsertString(wnd,cardinal(aeAdd),'+ add');
+ InsertString(wnd,cardinal(aeSub),'- sub');
+ InsertString(wnd,cardinal(aeMul),'* mul');
+ InsertString(wnd,cardinal(aeDiv),'/ div');
+ InsertString(wnd,cardinal(aeMod),'% mod');
+ InsertString(wnd,cardinal(aeAnd),'& and');
+ InsertString(wnd,cardinal(aeOr ),'| or');
+ InsertString(wnd,cardinal(aeXor),'^ xor');
+ InsertString(wnd,cardinal(aeSet),'= set');
+ SendMessage(wnd,CB_SETCURSEL,0,0);
+ end;
+
+ procedure MakeParamTypeList(wnd:HWND);
+ begin
+ SendMessage(wnd,CB_RESETCONTENT,0,0);
+ InsertString(wnd,ptNumber ,'number value');
+ InsertString(wnd,ptString ,'ANSI string');
+ InsertString(wnd,ptUnicode,'Unicode string');
+ InsertString(wnd,ptCurrent,'current contact');
+ InsertString(wnd,ptResult ,'last result');
+ InsertString(wnd,ptParam ,'parameter');
+ InsertString(wnd,ptStruct ,'structure');
+ SendMessage(wnd,CB_SETCURSEL,0,0);
+ end;
+
+ procedure MakeResultTypeList(wnd:HWND);
+ begin
+ SendMessage(wnd,CB_RESETCONTENT,0,0);
+ InsertString(wnd,sresInt ,'Integer');
+ InsertString(wnd,sresHex ,'Hexadecimal');
+ InsertString(wnd,sresString,'String');
+ InsertString(wnd,sresStruct,'Structure');
+ SendMessage(wnd,CB_SETCURSEL,0,0);
+ end;
+
+ procedure MakeFileEncList(wnd:HWND);
+ begin
+ SendMessage(wnd,CB_RESETCONTENT,0,0);
+ InsertString(wnd,0,'Ansi');
+ InsertString(wnd,1,'UTF8');
+ InsertString(wnd,2,'UTF8+sign');
+ InsertString(wnd,3,'UTF16');
+ InsertString(wnd,4,'UTF16+sign');
+ SendMessage(wnd,CB_SETCURSEL,0,0);
+ end;
+
+ procedure MakeDataTypeList(wnd:HWND);
+ begin
+ SendMessage(wnd,CB_RESETCONTENT,0,0);
+ InsertString(wnd,0,'Byte');
+ InsertString(wnd,1,'Word');
+ InsertString(wnd,2,'DWord');
+ InsertString(wnd,3,'Ansi');
+ InsertString(wnd,4,'Unicode');
+ SendMessage(wnd,CB_SETCURSEL,0,0);
+ end;
diff --git a/plugins/Actman/i_inoutxm.inc b/plugins/Actman/i_inoutxm.inc
new file mode 100644
index 0000000000..8795d566e7
--- /dev/null
+++ b/plugins/Actman/i_inoutxm.inc
@@ -0,0 +1,1180 @@
+{}
+var
+ xmlparser:XML_API_W;
+const
+ // Nodes
+ ioRoot :PWideChar = 'ActMan_Export';
+ ioAction :PWideChar = 'Action';
+ ioSubAction :PWideChar = 'SubAction';
+ ioContactWindow:PWideChar = 'ContactWindow';
+ ioCallService :PWideChar = 'CallService';
+ ioRunProgram :PWideChar = 'RunProgram';
+ ioInsertText :PWideChar = 'InsertText';
+ ioAdvanced :PWideChar = 'Advanced';
+ ioLinkAction :PWideChar = 'LinkAction';
+ ioProfile :PWideChar = 'Profile';
+ ioMessageBox :PWideChar = 'MessageBox';
+ ioWParam :PWideChar = 'WPARAM';
+ ioLParam :PWideChar = 'LPARAM';
+ ioItem :PWideChar = 'ITEM';
+ ioPost :PWideChar = 'POST';
+ ioIf :PWideChar = 'IF';
+ ioAct :PWideChar = 'ACT';
+ ioOutput :PWideChar = 'OUTPUT';
+ // Attributes
+ ioType :PWideChar = 'type';
+ ioInverse :PWideChar = 'inverse';
+ ioName :PWideChar = 'name';
+ ioDisabled :PWideChar = 'disabled';
+ ioVolatile :PWideChar = 'volatile';
+ ioLast :PWideChar = 'last';
+ ioWindow :PWideChar = 'window';
+ ioParallel :PWideChar = 'parallel';
+ ioArgs :PWideChar = 'args';
+ ioWait :PWideChar = 'wait';
+ ioObject :PWideChar = 'object';
+ ioOper :PWideChar = 'oper';
+ ioMessage :PWideChar = 'message';
+// ioDest :PWideChar = 'dest';
+ ioEnc :PWideChar = 'enc';
+ ioValue :PWideChar = 'value';
+ ioService :PWideChar = 'service';
+ ioNot :PWideChar = 'not';
+ ioCond :PWideChar = 'cond';
+ ioVariables :PWideChar = 'variables';
+ ioFileVariable :PWideChar = 'modvariables';
+ ioArgVariable :PWideChar = 'argvariables';
+ ioModule :PWideChar = 'module';
+ ioSetting :PWideChar = 'setting';
+ ioCProto :PWideChar = 'cproto';
+ ioCUIDType :PWideChar = 'cuidtype';
+ ioCUID :PWideChar = 'cuid';
+ ioIsChat :pWideChar = 'ischat';
+ ioTitle :PWideChar = 'title';
+ ioFile :PWideChar = 'file';
+ ioAsInt :PWideChar = 'asint';
+ ioKeepOnly :PWideChar = 'keeponly';
+ ioKeepLast :PWideChar = 'keeplast';
+ ioReturn :PWideChar = 'return';
+ ioLength :PWideChar = 'length';
+ ioFree :PWideChar = 'free';
+ ioPacked :PWideChar = 'packed';
+
+ // Values
+ ioNumber :PWideChar = 'number';
+ ioCurrent :PWideChar = 'current';
+ ioContact :PWideChar = 'contact';
+ ioStruct :PWideChar = 'struct';
+ ioResult :PWideChar = 'result';
+ ioParam :PWideChar = 'param';
+ ioByte :PWideChar = 'byte';
+ ioWord :PWideChar = 'word';
+ ioDword :PWideChar = 'dword';
+ ioAnsi :PWideChar = 'ansi';
+ ioUnicode :PWideChar = 'unicode';
+// ioWStruct :PWideChar = 'wordstruct';
+// ioBStruct :PWideChar = 'bytestruct';
+ ioHex :PWideChar = 'hex';
+ ioInt :PWideChar = 'int';
+ ioSigned :PWideChar = 'signed';
+ ioPopup :PWideChar = 'popup';
+ ioMsgBox :PWideChar = 'msgbox';
+ ioHidden :PWideChar = 'hidden';
+ ioMinimized :PWideChar = 'minimized';
+ ioMaximized :PWideChar = 'maximized';
+ ioNormal :PWideChar = 'normal';
+ ioClipboard :PWideChar = 'clipboard';
+ ioCopy :PWideChar = 'copy';
+ ioPaste :PWideChar = 'paste';
+ ioBreak :PWideChar = 'break';
+ ioJump :PWideChar = 'jump';
+ ioNop :PWideChar = 'nop';
+// ioArray :PWideChar = 'array';
+ ioScript :PWideChar = 'script';
+ ioWrite :PWideChar = 'write';
+ ioRead :PWideChar = 'read';
+ ioAppend :PWideChar = 'append';
+ ioDelete :PWideChar = 'delete';
+
+const
+ imp_yes = 1;
+ imp_yesall = 2;
+ imp_no = 3;
+ imp_noall = 4;
+ imp_append = 5;
+
+function ReadStruct(act:HXML):int_ptr;
+{
+var
+ child,i:integer;
+ tmp:pWideChar;
+ sub:HXML;
+ typ:pWideChar;
+}
+begin
+ result:=0;
+(*
+ mGetMem (tmp ,32768);
+ FillChar(tmp^,32768,0);
+ result:=int_ptr(tmp);
+ with xmlparser do
+ begin
+{
+ typ:=getAttrValue(act,ioPacked);
+ if (typ<>nil) and (typ^<>#0) and (typ^<>'0') then
+ begin
+ tmp^:=char_packed; inc(tmp);
+ end;
+}
+ child:=0;
+ repeat
+ sub:=getNextChild(act,ioItem,@child);
+ if sub=0 then break;
+
+ typ:=getAttrValue(sub,ioType);
+ for i:=0 to MaxStructTypes-1 do
+ begin
+ if lstrcmpiw(typ,StructElems[i].short)=0 then break;
+ end;
+ if StrToInt(getAttrValue(sub,ioReturn))=1 then
+ begin
+ tmp^:=char_return; inc(tmp);
+ end;
+ if StrToInt(getAttrValue(sub,ioScript))=1 then
+ begin
+ tmp^:=char_script; inc(tmp);
+ end;
+ tmp:=StrCopyEW(tmp,typ);
+ tmp^:=' '; inc(tmp);
+ case StructElems[i].typ of
+ SST_LAST, SST_PARAM: ;
+ SST_BYTE,
+ SST_WORD,
+ SST_DWORD,
+ SST_QWORD,
+ SST_NATIVE: begin
+ tmp:=StrCopyEW(tmp,getAttrValue(sub,ioValue));
+ end;
+ SST_BARR, SST_WARR,
+ SST_BPTR, SST_WPTR: begin
+ tmp:=StrCopyEW(tmp,getAttrValue(sub,ioLength));
+ tmp^:=' '; inc(tmp);
+ tmp:=StrCopyEW(tmp,getAttrValue(sub,ioValue));
+ end;
+ end;
+ tmp^:='|'; inc(tmp);
+ until false;
+ dec(tmp); tmp^:=#0;
+ end;
+*)
+end;
+
+function ReadParam(act:HXML; var param:int_ptr;isvar:boolean):dword;
+var
+ tmp:pWideChar;
+begin
+ result:=0;
+ if act=0 then
+ exit;
+ with xmlparser do
+ begin
+ tmp:=getAttrValue(act,ioType);
+ if lstrcmpiw(tmp,ioCurrent)=0 then result:=result or ACF_WCURRENT
+ else if lstrcmpiw(tmp,ioResult )=0 then result:=result or ACF_WRESULT
+ else if lstrcmpiw(tmp,ioParam )=0 then result:=result or ACF_WPARAM
+ else if lstrcmpiw(tmp,ioNumber )=0 then
+ begin
+ result:=result or ACF_WPARNUM;
+ tmp:=getAttrValue(act,ioValue);
+ if isvar then
+ StrDupW(pWideChar(param),tmp)
+ else
+ param:=StrToInt(tmp);
+ end
+ else if lstrcmpiw(tmp,ioStruct)=0 then
+ begin
+ result:=result or ACF_WSTRUCT;
+ param:=ReadStruct(act);
+ end
+ else if lstrcmpiw(tmp,ioUnicode)=0 then
+ begin
+ result:=result or ACF_WUNICODE;
+ StrDupW(pWideChar(param),getAttrValue(act,ioValue));
+ end
+ else if lstrcmpiw(tmp,ioAnsi)=0 then
+ begin
+ WideToAnsi(getAttrValue(act,ioValue),pAnsiChar(param),MirandaCP);
+ end;
+ end;
+end;
+
+function ImportContact(node:HXML):THANDLE;
+var
+ proto:pAnsiChar;
+ tmpbuf:array [0..63] of AnsiChar;
+ dbv:TDBVARIANT;
+ is_chat:boolean;
+begin
+ with xmlparser do
+ begin
+ proto:=FastWideToAnsiBuf(getAttrValue(node,ioCProto),tmpbuf);
+ if (proto=nil) or (proto^=#0) then
+ begin
+ result:=0;
+ exit;
+ end;
+ is_chat:=StrToInt(getAttrValue(node,ioIsChat))<>0;
+
+ if is_chat then
+ begin
+ dbv.szVal.W:=getAttrValue(node,ioCUID);
+ end
+ else
+ begin
+ FillChar(dbv,SizeOf(TDBVARIANT),0);
+ dbv._type:=StrToInt(getAttrValue(node,ioCUIDType));
+ case dbv._type of
+ DBVT_BYTE : dbv.bVal:=StrToInt(getAttrValue(node,ioCUID));
+ DBVT_WORD : dbv.wVal:=StrToInt(getAttrValue(node,ioCUID));
+ DBVT_DWORD : dbv.dVal:=StrToInt(getAttrValue(node,ioCUID));
+ DBVT_ASCIIZ: FastWideToAnsi(getAttrValue(node,ioCUID),dbv.szVal.A);
+ DBVT_UTF8 : WideToUTF8(getAttrValue(node,ioCUID),dbv.szVal.A);
+ DBVT_WCHAR : StrDupW(dbv.szVal.W,getAttrValue(node,ioCUID));
+ DBVT_BLOB : begin
+ Base64Decode(FastWideToAnsi(getAttrValue(node,ioCUID),pAnsiChar(dbv.pbVal)),dbv.pbVal);
+ end;
+ end;
+ end;
+ end;
+ result:=FindContactHandle(proto,dbv,is_chat);
+ if not is_chat then
+ case dbv._type of
+ DBVT_WCHAR,
+ DBVT_ASCIIZ,
+ DBVT_UTF8 : mFreeMem(dbv.szVal.A);
+ DBVT_BLOB : mFreeMem(dbv.pbVal);
+ end;
+end;
+
+function ImportAction(actnode:HXML):integer;
+var
+ tmp:pWideChar;
+ act:tHKAction;
+ sub:HXML;
+begin
+ FillChar(act,SizeOf(act),0);
+ with xmlparser,act do
+ begin
+ flags:=ACF_ASSIGNED;
+ if StrToInt(getAttrValue(actnode,ioDisabled))=1 then
+ flags:=flags or ACF_DISABLED;
+
+ StrDupW(descr,getAttrValue(actnode,ioName));
+
+ actnode:=getChild(actnode,0);
+ tmp:=getName(actnode);
+MessageBoxW(0,tmp,'node',0);
+
+ // CONTACT
+ if StrCmpW(tmp,ioContactWindow)=0 then
+ begin
+ actionType:=ACT_CONTACT;
+ contact:=ImportContact(actnode);
+// contact:=StrToInt(getAttrValue(actnode,ioNumber));
+ if StrToInt(getAttrValue(actnode,ioKeepOnly))=1 then
+ flags:=flags or ACF_KEEPONLY;
+ end
+
+ // SERVICE
+ else if StrCmpW(tmp,ioCallService)=0 then
+ begin
+ actionType:=ACT_SERVICE;
+ FastWideToAnsi(getAttrValue(actnode,ioService),service);
+MessageBoxA(0,service,'service',0);
+ if StrToInt(getAttrValue(actnode,ioVariables))=1 then
+ flags2:=flags2 or ACF2_SRV_SRVC;
+
+ sub:=getNthChild(actnode,ioWParam,0);
+ if StrToInt(getAttrValue(sub,ioVariables))=1 then
+ flags2:=flags2 or ACF2_SRV_WPAR;
+ if StrToInt(getAttrValue(sub,ioHex))=1 then
+ flags2:=flags2 or ACF2_SRV_WHEX;
+ flags:=flags or ReadParam(sub,int_ptr(wparam),(flags2 and ACF2_SRV_WPAR)<>0);
+
+ sub:=getNthChild(actnode,ioLParam,0);
+ if StrToInt(getAttrValue(sub,ioVariables))=1 then
+ flags2:=flags2 or ACF2_SRV_LPAR;
+ if StrToInt(getAttrValue(sub,ioHex))=1 then
+ flags2:=flags2 or ACF2_SRV_LHEX;
+ flags:=flags or (ReadParam(sub,lparam,(flags2 and ACF2_SRV_LPAR)<>0) shl 1);
+
+ sub:=getNthChild(actnode,ioOutput,0);
+ if StrToInt(getAttrValue(sub,ioMessage))=1 then flags:=flags or ACF_INSERT;
+ if StrToInt(getAttrValue(sub,ioPopup ))=1 then flags:=flags or ACF_POPUP;
+ if StrToInt(getAttrValue(sub,ioMsgBox ))=1 then flags:=flags or ACF_MESSAGE;
+
+ if StrToInt(getAttrValue(sub,ioFree))=1 then flags2:=flags2 or ACF2_FREEMEM;
+
+ tmp:=getAttrValue(sub,ioType);
+ if lstrcmpiw(tmp,ioUnicode)=0 then flags:=flags or ACF_UNICODE+ACF_STRING
+ else if lstrcmpiw(tmp,ioAnsi )=0 then flags:=flags or ACF_STRING
+ else if lstrcmpiw(tmp,ioSigned )=0 then flags:=flags or ACF_SIGNED
+ else if lstrcmpiw(tmp,ioHex )=0 then flags:=flags or ACF_HEX
+ else if lstrcmpiw(tmp,ioStruct )=0 then flags:=flags or ACF_STRUCT
+ else if lstrcmpiw(tmp,ioInt )=0 then ;
+MessageBoxW(0,'','end',0);
+ end
+
+ // PROGRAM
+ else if StrCmpW(tmp,ioRunProgram)=0 then
+ begin
+ actionType:=ACT_PROGRAM;
+ StrDupW(prgname,getText(actnode));
+ StrDupW(args,getAttrValue(actnode,ioArgs));
+ if StrToInt(getAttrValue(actnode,ioCurrent))=1 then
+ flags:=flags or ACF_CURPATH;
+
+ if StrToInt(getAttrValue(actnode,ioParallel))=1 then
+ flags:=flags or ACF_PRTHREAD
+ else
+ time:=StrToInt(getAttrValue(actnode,ioWait));
+
+ if StrToInt(getAttrValue(actnode,ioFileVariable))=1 then
+ flags2:=flags2 or ACF2_PRG_PRG;
+
+ if StrToInt(getAttrValue(actnode,ioArgVariable))=1 then
+ flags2:=flags2 or ACF2_PRG_ARG;
+
+ tmp:=getAttrValue(actnode,ioWindow);
+ if lstrcmpiw(tmp,ioHidden )=0 then show:=SW_HIDE
+ else if lstrcmpiw(tmp,ioMinimized)=0 then show:=SW_SHOWMINIMIZED
+ else if lstrcmpiw(tmp,ioMaximized)=0 then show:=SW_SHOWMAXIMIZED
+ else show:=SW_SHOWNORMAL;
+ end
+
+ // INSERT TEXT
+ else if StrCmpW(tmp,ioInsertText)=0 then
+ begin
+ actionType:=ACT_TEXT;
+ tmp:=getAttrValue(actnode,ioObject);
+ if lstrcmpiw(tmp,ioClipboard)=0 then
+ begin
+ flags:=flags or ACF_CLIPBRD;
+ tmp:=getAttrValue(actnode,ioOper);
+ if lstrcmpiw(tmp,ioCopy)=0 then flags:=flags or ACF_COPYTO;
+// else if lstrcmpiw(tmp,'paste')=0 then ;
+ tmp:=getAttrValue(actnode,ioEnc);
+ if lstrcmpiw(tmp,ioAnsi)=0 then flags:=flags or ACF_ANSI;
+// else if lstrcmpiw(tmp,'unicode')=0 then ;
+ end
+ else
+ begin
+ StrDupW(text,getText(actnode));
+
+ if StrToInt(getAttrValue(actnode,ioVariables))=1 then
+ flags2:=flags2 or ACF2_TXT_TEXT;
+
+ if lstrcmpiw(tmp,ioFile)=0 then
+ begin
+
+ if StrToInt(getAttrValue(actnode,ioFileVariable))=1 then
+ flags2:=flags2 or ACF2_TXT_FILE;
+
+ flags:=flags or ACF_FILE;
+ StrDupW(tfile,getAttrValue(actnode,ioFile));
+ tmp:=getAttrValue(actnode,ioOper);
+ if lstrcmpiw(tmp,ioWrite )=0 then flags:=flags or ACF_FWRITE
+ else if lstrcmpiw(tmp,ioAppend)=0 then flags:=flags or ACF_FAPPEND;
+ case StrToInt(getAttrValue(actnode,ioEnc)) of
+ 0: flags:=flags or ACF_ANSI;
+ 1: flags:=flags or ACF_UTF8;
+ 2: flags:=flags or ACF_UTF8 or ACF_SIGN;
+ 3: flags:=flags or 0;
+ 4: flags:=flags or ACF_SIGN;
+ end;
+ end;
+ end;
+ end
+
+ // ADVANCED
+ else if StrCmpW(tmp,ioAdvanced)=0 then
+ begin
+ actionType:=ACT_ADVANCE;
+ sub:=getNthChild(actnode,ioIf,0);
+ if sub<>0 then
+ begin
+ tmp:=getAttrValue(sub,ioCond);
+ if lstrcmpiw(tmp,'gt' )=0 then condition:=condition or ADV_COND_GT
+ else if lstrcmpiw(tmp,'lt' )=0 then condition:=condition or ADV_COND_LT
+ else if lstrcmpiw(tmp,'eq' )=0 then condition:=condition or ADV_COND_EQ
+ else if lstrcmpiw(tmp,ioNop)=0 then ;
+
+ if StrToInt(getAttrValue(sub,ioNot))=1 then
+ condition:=condition or ADV_COND_NOT;
+
+ value:=StrToInt(getAttrValue(sub,ioValue));
+ end;
+
+ sub:=getNthChild(actnode,ioAct,0);
+ tmp:=getAttrValue(sub,ioType);
+ if lstrcmpiw(tmp,ioValue)=0 then
+ begin
+ action:=action or ADV_ACT_MATH;
+ tmp:=getAttrValue(sub,ioOper);
+ if lstrcmpiw(tmp,ioInverse)=0 then
+ oper:=Cardinal(aeNot)
+ else
+ begin
+ case tmp^ of
+ '+': oper:=Cardinal(aeAdd);
+ '-': oper:=Cardinal(aeSub);
+ '*': oper:=Cardinal(aeMul);
+ '\': oper:=Cardinal(aeDiv);
+ '%': oper:=Cardinal(aeMod);
+ '&': oper:=Cardinal(aeAnd);
+ '|': oper:=Cardinal(aeOr );
+ '^': oper:=Cardinal(aeXor);
+ '=': oper:=Cardinal(aeSet);
+ end;
+ mathval:=StrToInt(getAttrValue(sub,ioValue));
+ end;
+ end
+ else if lstrcmpiw(tmp,ioScript)=0 then
+ begin
+ if StrToInt(getAttrValue(sub,ioAsInt))<>0 then
+ flags:=flags or ACF_VARASINT;
+ action:=action or ADV_ACT_VARS;
+ StrDupW(varval,getText(sub));
+ end;
+
+ sub:=getNthChild(actnode,ioPost,0);
+ if sub<>0 then
+ begin
+ tmp:=getAttrValue(sub,ioOper);
+ if lstrcmpiw(tmp,ioBreak)=0 then action:=action or ADV_ACT_BREAK
+ else if lstrcmpiw(tmp,ioJump )=0 then action:=action or ADV_ACT_JUMP
+ else if lstrcmpiw(tmp,ioNop )=0 then ;
+
+ tmp:=getAttrValue(sub,ioValue);
+ case action and ADV_ACT_POST of
+ ADV_ACT_JUMP: StrDupW(operval,tmp);
+ end;
+ end;
+ end
+
+ // CHAIN
+ else if StrCmpW(tmp,ioLinkAction)=0 then
+ begin
+ actionType:=ACT_CHAIN;
+ StrDupW(actname,getText(actnode));
+ flags:=flags or ACF_BYNAME;
+ end
+
+ // DBRW
+ else if StrCmpW(tmp,ioProfile)=0 then
+ begin
+ actionType:=ACT_RW;
+ tmp:=getAttrValue(actnode,ioOper);
+ if lstrcmpiw(tmp,ioDelete)=0 then flags:=flags or ACF_DBDELETE
+ else if lstrcmpiw(tmp,ioWrite )=0 then flags:=flags or ACF_DBWRITE;
+// else if lstrcmpiw(tmp,ioRead)=0 then ;
+ tmp:=getAttrValue(actnode,ioContact);
+ if lstrcmpiw(tmp,ioCurrent)=0 then flags:=flags or ACF_CURRENT
+ else if lstrcmpiw(tmp,ioResult )=0 then flags:=flags or ACF_RESULT
+ else if lstrcmpiw(tmp,ioParam )=0 then flags:=flags or ACF_PARAM
+ else if lstrcmpiw(tmp,ioContact)=0 then
+ begin
+ contact:=ImportContact(actnode);
+ end;
+
+ FastWideToAnsi(getAttrValue(actnode,ioModule ),dbmodule);
+ FastWideToAnsi(getAttrValue(actnode,ioSetting),dbsetting);
+
+ if StrToInt(getAttrValue(actnode,ioFileVariable))=1 then flags2:=flags2 or ACF2_RW_MVAR;
+ if StrToInt(getAttrValue(actnode,ioArgVariable ))=1 then flags2:=flags2 or ACF2_RW_SVAR;
+ if StrToInt(getAttrValue(actnode,ioVariables ))=1 then flags2:=flags2 or ACF2_RW_TVAR;
+ if StrToInt(getAttrValue(actnode,ioHex ))=1 then flags2:=flags2 or ACF2_RW_HEX;
+
+ tmp:=getAttrValue(actnode,ioType);
+ if lstrcmpiw(tmp,ioByte )=0 then flags:=flags or ACF_DBBYTE
+ else if lstrcmpiw(tmp,ioWord )=0 then flags:=flags or ACF_DBWORD
+ else if lstrcmpiw(tmp,ioDword)=0 then
+ else // if lstrcmpiw(tmp,ioUnicode)=0 then
+ begin
+ if lstrcmpiw(tmp,ioAnsi)=0 then
+ flags:=flags or ACF_DBANSI
+ else
+ flags:=flags or ACF_DBUTEXT;
+ StrDupW(pWideChar(dbvalue),getText(actnode));
+ end;
+
+ if StrToInt(getAttrValue(actnode,ioLast))=1 then
+ flags:=flags or ACF_LAST
+ else if (flags and ACF_DBUTEXT)=0 then
+ begin
+ if (flags2 and ACF2_RW_TVAR)<>0 then
+ StrDupW(pWideChar(dbvalue),getText(actnode))
+ else
+ dbvalue:=StrToInt(getAttrValue(actnode,ioValue));
+ end;
+ end
+
+ // MessageBox
+ else if StrCmpW(tmp,ioMessageBox)=0 then
+ begin
+ actionType:=ACT_MESSAGE;
+ StrDupW(msgtitle,getAttrValue(actnode,ioTitle));
+ StrDupW(msgtext,getText(actnode));
+ boxopts:=StrToInt(getAttrValue(actnode,ioType));
+ if StrToInt(getAttrValue(actnode,ioArgVariable))=1 then flags2:=flags2 or ACF2_MSG_TXT;
+ if StrToInt(getAttrValue(actnode,ioVariables ))=1 then flags2:=flags2 or ACF2_MSG_TTL;
+ if StrToInt(getAttrValue(actnode,ioKeepLast ))=1 then flags :=flags or ACF_MSG_KEEP;
+ end
+
+ else
+ begin
+ actionType:=ACT_UNKNOWN;
+ result:=0;
+ Exit;
+ end;
+ end;
+ result:=NewAction(ActionList,MaxActions);
+ move(act,ActionList^[result],SizeOf(tHKAction));
+end;
+
+function Import(fname:PWideChar;aflags:dword):integer;
+var
+ f:THANDLE;
+ i,j:integer;
+ tmp,res:pWideChar;
+ root,actnode:HXML;
+ last,next:integer;
+ impact:integer;
+ buf:array [0..511] of WideChar;
+ oldid:dword;
+begin
+ result:=0;
+ for i:=0 to MaxGroups-1 do
+ with GroupList[i] do
+ if (flags and (ACF_IMPORT or ACF_ASSIGNED))=
+ (ACF_IMPORT or ACF_ASSIGNED) then
+ flags:=flags and not (ACF_IMPORT or ACF_OVERLOAD);
+
+ if (fname=nil) or (fname^=#0) then
+ exit;
+ i:=GetFSize(fname);
+ if i=0 then
+ exit;
+ mGetMem (res ,i+SizeOf(WideChar));
+ FillChar(res^,i+SizeOf(WideChar),0);
+ f:=Reset(fname);
+ BlockRead(f,res^,i);
+ CloseHandle(f);
+
+MessageBoxW(0,res,'SRC',0);
+ xmlparser.cbSize:={XML_API_SIZEOF_V1;//}SizeOf(XML_API_W);
+ CallService(MS_SYSTEM_GET_XI,0,lparam(@xmlparser));
+ with xmlparser do
+ begin
+ root:=parseString(ChangeUnicode(res),@i,nil);
+ j:=0;
+ impact:=imp_yes;
+ repeat
+ actnode:=getNthChild(root,ioAction,j);
+ if actnode=0 then break;
+MessageBoxW(0,'not zero','actnode',0);
+//?? if StrCmpW(getName(actnode),ioAction)<>0 then break;
+ tmp:=getAttrValue(actnode,ioName);
+MessageBoxW(0,tmp,'nodename',0);
+ if tmp<>nil then //!!
+ begin
+ i:=GetActIdxByName(tmp);
+ oldid:=$FFFFFFFF;
+ if i>=0 then
+ begin
+ if (impact<>imp_yesall) and (impact<>imp_noall) then
+ begin
+ StrCopyW(buf,TranslateW('Action "$" exists, do you want to rewrite it?'));
+ impact:=DialogBoxParam(hInstance,MAKEINTRESOURCE(IDD_ASK),0,
+ @QuestionDlg,TLPARAM(StrReplaceW(buf,'$',tmp)));
+ end;
+ if (impact=imp_yesall) or (impact=imp_yes) then
+ begin
+ oldid:=GroupList^[i].id;
+ FreeGroup(i);
+ end;
+ end;
+ if (i<0) or (impact=imp_yesall) or (impact=imp_yes) or (impact=imp_append) then
+ begin
+ with GroupList^[NewGroup(GroupList,MaxGroups)] do
+ begin
+ if (i>=0) and (oldid<>$FFFFFFFF) then // set old id to keep UseAction setting
+ begin
+ flags:=flags or ACF_IMPORT or ACF_OVERLOAD;
+ id:=oldid;
+ end;
+ flags:=flags or ACF_IMPORT;
+ if StrToInt(getAttrValue(actnode,ioDisabled))=1 then flags:=flags or ACF_DISABLED;
+ if StrToInt(getAttrValue(actnode,ioVolatile))=1 then flags:=flags or ACF_VOLATILE;
+ mFreeMem(descr);
+ StrDupW(descr,tmp);
+ i:=0;
+ last:=-1;
+MessageBoxW(0,descr,'descr',0);
+ repeat
+ next:=ImportAction(getChild(actnode,i));
+ if next=0 then
+ break;
+ if last<0 then
+ firstAction:=next
+ else
+ ActionList[last].next:=next;
+ last:=next;
+ inc(i);
+ until false;
+ inc(result);
+ end;
+ end;
+ end;
+ inc(j);
+ until false;
+ DestroyNode(root);
+ end;
+ mFreeMem(res);
+end;
+
+//--------------------------
+
+procedure WriteStruct(node:HXML;struct:PWideChar);
+{var
+ sub:HXML;
+ ppc,value,lsrc,p,pc:pWideChar;
+ i,len:integer;
+ typ:integer;
+}
+begin
+(*
+ if struct=nil then exit;
+
+ mGetMem(pc,4096);
+ lsrc:=pWideChar(struct);
+ with xmlparser do
+ begin
+{
+ if lsrc^=char_packed then
+ begin
+ AddAttrInt(node,ioPacked,1);
+ inc(lsrc);
+ end;
+}
+ while lsrc^<>#0 do
+ begin
+ sub:=AddChild(node,ioItem,nil);
+ p:=StrScanW(lsrc,'|');
+ StrCopyW(pc,lsrc,p-lsrc);
+
+ ppc:=pc;
+ if ppc^=char_return then
+ begin
+ AddAttrInt(sub,ioReturn,1);
+ inc(ppc);
+ end;
+
+ if ppc^=char_script then
+ begin
+ AddAttrInt(sub,ioScript,1);
+ inc(ppc);
+ end;
+
+ typ:=GetOneElement(ppc,len,value);
+
+ i:=0;
+ while i<MaxStructTypes do
+ begin
+ if StructElems[i].typ=typ then //!!
+ break;
+ inc(i);
+ end;
+ AddAttr(sub,ioType,StructElems[i].short);
+
+
+ case typ of
+ SST_LAST,SST_PARAM: ;
+ SST_BYTE,SST_WORD,SST_DWORD,SST_QWORD,SST_NATIVE: begin
+ AddAttr(sub,ioValue,value);
+ end;
+ SST_BARR,SST_WARR,SST_BPTR,SST_WPTR: begin
+ AddAttrInt(sub,ioLength,len);
+ AddAttr (sub,ioValue ,value);
+ end;
+ end;
+
+ if p=nil then break;
+ lsrc:=p+1;
+ end;
+ end;
+ mFreeMem(pc);
+*)
+end;
+
+procedure WriteParam(node:HXML;flags,param:int_ptr;flags2:integer);
+var
+ s:pWideChar;
+ tmp:pWideChar;
+begin
+ with xmlparser do
+ begin
+ if (flags and ACF_WPARNUM)<>0 then
+ begin
+ s:=ioNumber;
+ if (flags2 and ACF2_SRV_WPAR)<>0 then
+ AddAttr(node,ioValue,PWideChar(param))
+ else
+ AddAttrInt(node,ioValue,param);
+ end
+ else if (flags and ACF_WCURRENT)<>0 then
+ begin
+ s:=ioCurrent;
+ end
+ else if (flags and ACF_WRESULT)<>0 then
+ begin
+ s:=ioResult;
+ end
+ else if (flags and ACF_WPARAM)<>0 then
+ begin
+ s:=ioParam;
+ end
+ else if (flags and ACF_WSTRUCT)<>0 then
+ begin
+ s:=ioStruct;
+ WriteStruct(node,pointer(param));
+ end
+ else if (flags and ACF_WUNICODE)<>0 then
+ begin
+ s:=ioUnicode;
+ AddAttr(node,ioValue,PWideChar(param));
+ end
+ else
+ begin
+ s:=ioAnsi;
+ AddAttr(node,ioValue,AnsiToWide(PAnsiChar(param),tmp));
+ mFreeMem(tmp);
+ end;
+ AddAttr(node,ioType,s);
+ end;
+end;
+
+function ExportContact(node:HXML;hContact:THANDLE):integer;
+var
+ proto,uid:pAnsiChar;
+ cws:TDBVARIANT;
+ p1:pAnsiChar;
+ p:pWideChar;
+ tmpbuf:array [0..63] of WideChar;
+ is_chat:boolean;
+begin
+ result:=0;
+ proto:=GetContactProtoAcc(hContact);
+ if proto<>nil then
+ begin
+ is_chat:=IsChat(hContact);
+ if is_chat then
+ begin
+ with xmlparser do
+ begin
+ p:=DBReadUnicode(hContact,proto,'ChatRoomID');
+ addAttr(node,ioCUID,p);
+ mFreeMem(p);
+ end;
+ result:=1;
+ end
+ else
+ begin
+ uid:=pAnsiChar(CallProtoService(proto,PS_GETCAPS,PFLAG_UNIQUEIDSETTING,0));
+ if DBReadSetting(hContact,proto,uid,@cws)=0 then
+ begin
+ result:=1;
+ with xmlparser do
+ begin
+ addAttrInt(node,ioCUIDType,cws._type);
+ case cws._type of
+ DBVT_BYTE : AddAttrInt(node,ioCUID,cws.bVal);
+ DBVT_WORD : AddAttrInt(node,ioCUID,cws.wVal);
+ DBVT_DWORD : AddAttrInt(node,ioCUID,cws.dVal);
+ DBVT_ASCIIZ: begin
+ AddAttr(node,ioCUID,FastAnsiToWide(cws.szVal.A,p));
+ mFreeMem(p);
+ end;
+ DBVT_UTF8 : begin
+ AddAttr(node,ioCUID,UTF8ToWide(cws.szVal.A,p));
+ // AddAttr(node,'cuid',FastAnsiToWide(cws.szVal.A,p));
+ mFreeMem(p);
+ end;
+ DBVT_WCHAR : AddAttr(node,ioCUID,cws.szVal.W);
+ DBVT_BLOB : begin
+ p1:=Base64Encode(cws.pbVal,cws.cpbVal);
+ AddAttr(node,ioCUID,FastAnsiToWide(p1,p));
+ mFreeMem(p1);
+ mFreeMem(p);
+ end;
+ end;
+ end;
+ end;
+ DBFreeVariant(@cws);
+ end;
+ if result<>0 then
+ begin
+ with xmlparser do
+ begin
+ addAttr (node,ioCProto,FastAnsiToWideBuf(proto,tmpbuf));
+ addAttrInt(node,ioIsChat,ord(is_chat));
+ end;
+ end;
+ end;
+end;
+
+procedure WriteAction(actnode:HXML;idx:integer);
+var
+ sub, act: HXML;
+ s:PWideChar;
+ i:integer;
+ tmp:pWideChar;
+begin
+ with xmlparser,ActionList[idx] do
+ begin
+ actnode:=AddChild(actnode,ioSubAction,nil);
+ if descr<>nil then
+ AddAttr(actnode,ioName,descr);
+ if (flags and ACF_DISABLED)<>0 then
+ AddAttrInt(actnode,ioDisabled,1);
+
+ case actionType of
+// ----- CONTACT -----
+ ACT_CONTACT: begin
+ sub:=AddChild(actnode,ioContactWindow,nil);
+ ExportContact(sub,contact);
+// AddAttrInt(sub,ioNumber,0); // contact
+ if (flags and ACF_KEEPONLY)<>0 then AddAttrInt(sub,ioKeepOnly,1);
+ end;
+// ----- SERVICE -----
+ ACT_SERVICE: begin
+ sub:=AddChild(actnode,ioCallService,nil);
+ AddAttr(sub,ioService,FastAnsiToWide(service,tmp));
+ if (flags2 and ACF2_SRV_SRVC)<>0 then AddAttrInt(sub,ioVariables,1);
+ mFreeMem(tmp);
+ act:=AddChild(sub,ioWParam,nil); WriteParam(act,flags ,wparam,flags2);
+ if (flags2 and ACF2_SRV_WPAR)<>0 then AddAttrInt(act,ioVariables,1);
+ if (flags2 and ACF2_SRV_WHEX)<>0 then AddAttrInt(act,ioHex ,1);
+ act:=AddChild(sub,ioLParam,nil); WriteParam(act,flags shr 1,lparam,flags2 shr 1);
+ if (flags2 and ACF2_SRV_LPAR)<>0 then AddAttrInt(act,ioVariables,1);
+ if (flags2 and ACF2_SRV_LHEX)<>0 then AddAttrInt(act,ioHex ,1);
+
+ act:=AddChild(sub,ioOutput,nil);
+ if (flags and (ACF_MESSAGE+ACF_POPUP+ACF_INSERT))<>0 then
+ begin
+ if (flags and ACF_INSERT )<>0 then AddAttrInt(act,ioMessage,1);
+ if (flags and ACF_POPUP )<>0 then AddAttrInt(act,ioPopup ,1);
+ if (flags and ACF_MESSAGE)<>0 then AddAttrInt(act,ioMsgBox ,1);
+ end;
+
+ if (flags2 and ACF2_FREEMEM)<>0 then AddAttrInt(sub,ioFree,1);
+
+ if (flags and ACF_STRUCT)<>0 then
+ s:=ioStruct
+ else if (flags and ACF_STRING)<>0 then
+ begin
+ if (flags and ACF_UNICODE)<>0 then
+ s:=ioUnicode
+ else
+ s:=ioAnsi;
+ end
+ else
+ begin
+ if (flags and ACF_SIGNED)<>0 then s:=ioSigned
+ else if (flags and ACF_HEX )<>0 then s:=ioHex
+ else s:=ioInt;
+ end;
+ AddAttr(act,ioType,s);
+ end;
+// ----- PROGRAM -----
+ ACT_PROGRAM: begin
+ sub:=AddChild(actnode,ioRunProgram,prgname);
+ if args<>nil then
+ AddAttr(sub,ioArgs,args);
+ if (flags and ACF_CURPATH)<>0 then AddAttrInt(sub,ioCurrent,1);
+ if (flags and ACF_PRTHREAD)=0 then AddAttrInt(sub,ioWait,time)
+ else AddAttrInt(sub,ioParallel,1);
+
+ if (flags2 and ACF2_PRG_PRG)<>0 then AddAttrInt(sub,ioFileVariable,1);
+ if (flags2 and ACF2_PRG_ARG)<>0 then AddAttrInt(sub,ioArgVariable ,1);
+
+ case show of
+ SW_HIDE : s:=ioHidden;
+ SW_SHOWMINIMIZED: s:=ioMinimized;
+ SW_SHOWMAXIMIZED: s:=ioMaximized;
+ else
+ s:=ioNormal;
+ end;
+ AddAttr(sub,ioWindow,s);
+ end;
+// ----- TEXT -----
+ ACT_TEXT: begin
+ if (flags and ACF_CLIPBRD)<>0 then
+ tmp:=nil
+ else
+ tmp:=text;
+ sub:=AddChild(actnode,ioInsertText,tmp);
+ if (flags and ACF_CLIPBRD)<>0 then
+ begin
+ AddAttr(sub,ioObject,ioClipboard);
+ if (flags and ACF_COPYTO)<>0 then
+ s:=ioCopy
+ else
+ s:=ioPaste;
+ AddAttr(sub,ioOper,s);
+ if (flags and ACF_ANSI)=0 then
+ s:=ioUnicode
+ else
+ s:=ioAnsi;
+ AddAttr(sub,ioEnc,s);
+ end
+ else
+ begin
+ if (flags and ACF_FILE)<>0 then
+ begin
+ if (flags2 and ACF2_TXT_FILE)<>0 then
+ AddAttrInt(sub,ioFileVariable,1);
+ AddAttr(sub,ioObject,ioFile);
+ AddAttr(sub,ioFile,tfile);
+ if (flags and ACF_FWRITE )<>0 then AddAttr(sub,ioOper,ioWrite)
+ else if (flags and ACF_FAPPEND)<>0 then AddAttr(sub,ioOper,ioAppend);
+
+ if (flags and ACF_ANSI)<>0 then
+ i:=0
+ else if (flags and ACF_UTF8)<>0 then
+ begin
+ if (flags and ACF_SIGN)<>0 then
+ i:=2
+ else
+ i:=1;
+ end
+ else if (flags and ACF_SIGN)<>0 then
+ i:=4
+ else
+ i:=3;
+ AddAttrInt(sub,ioEnc,i);
+ end
+ else
+ AddAttr(sub,ioObject,ioWindow);
+
+ if (flags2 and ACF2_TXT_TEXT)<>0 then
+ AddAttrInt(sub,ioVariables,1);
+ end;
+ end;
+// ----- ADVANCED -----
+ ACT_ADVANCE: begin
+ sub:=AddChild(actnode,ioAdvanced,nil);
+ if lobyte(condition)<>0 then
+ begin
+ act:=AddChild(sub,ioIf,nil);
+ if (lobyte(condition) and ADV_COND_NOT)<>0 then
+ AddAttrInt(act,ioNot,1);
+ case lobyte(condition) and not ADV_COND_NOT of
+ ADV_COND_GT: s:='gt';
+ ADV_COND_LT: s:='lt';
+ ADV_COND_EQ: s:='eq';
+ else
+ s:=ioNop;
+ end;
+ AddAttr(act,ioCond,s);
+ AddAttrInt(act,ioValue,value);
+ end;
+
+ if (action and not 3)<>ADV_ACT_VARS then
+ tmp:=nil
+ else
+ tmp:=varval;
+ act:=AddChild(sub,ioAct,tmp);
+ case action and ADV_ACTION of
+ ADV_ACT_MATH: begin
+ AddAttr(act,ioType,ioValue);
+ if tAdvExpr(oper)=aeNot then
+ AddAttr(act,ioOper,ioInverse)
+ else
+ begin
+ case tAdvExpr(oper) of
+ aeAdd: s:='+';
+ aeSub: s:='-';
+ aeMul: s:='*';
+ aeDiv: s:='\';
+ aeMod: s:='%';
+ aeAnd: s:='&';
+ aeOr : s:='|';
+ aeXor: s:='^';
+ aeSet: s:='=';
+ else
+ s:=nil;
+ end;
+ AddAttr(act,ioOper,s);
+ AddAttrInt(act,ioValue,mathval);
+ end;
+ end;
+ ADV_ACT_VARS: begin
+ if (flags and ACF_VARASINT)<>0 then
+ AddAttrInt(act,ioAsInt,1);
+ AddAttr(act,ioType,ioScript);
+ end;
+ end;
+
+ if (action and ADV_ACT_POST)<>0 then
+ begin
+ act:=AddChild(sub,ioPost,nil);
+ case action and ADV_ACT_POST of
+ ADV_ACT_BREAK: s:=ioBreak;
+ ADV_ACT_JUMP : begin
+ s:=ioJump;
+ AddAttr(act,ioValue,operval);
+ end;
+ else
+ s:=ioNop
+ end;
+ AddAttr(act,ioOper,s);
+ end;
+ //!!
+ end;
+// ----- LINK -----
+ ACT_CHAIN: begin
+ if (flags and ACF_BYNAME)<>0 then
+ s:=actname
+ else
+ s:=GetActNameById(id);
+ AddChild(actnode,ioLinkAction,s);
+ end;
+// ----- DATABASE -----
+ ACT_RW: begin
+ if ((flags and ACF_DBUTEXT)=0) and ((flags2 and ACF2_RW_TVAR)=0) then
+ tmp:=nil
+ else
+ tmp:=pWideChar(dbvalue);
+ sub:=AddChild(actnode,ioProfile,tmp);
+ if (flags and ACF_DBDELETE)<>0 then s:=ioDelete
+ else if (flags and ACF_DBWRITE )<>0 then s:=ioWrite
+ else s:=ioRead;
+ AddAttr(sub,ioOper,s);
+ if (flags and ACF_CURRENT)<>0 then s:=ioCurrent
+ else if (flags and ACF_RESULT )<>0 then s:=ioResult
+ else if (flags and ACF_PARAM )<>0 then s:=ioParam
+ else
+ begin
+ s:=ioContact;
+ ExportContact(sub,dbcontact);
+ end;
+ AddAttr(sub,ioContact,s);
+ AddAttr(sub,ioModule ,FastAnsiToWide(dbmodule ,tmp)); mFreeMem(tmp);
+ AddAttr(sub,ioSetting,FastAnsiToWide(dbsetting,tmp)); mFreeMem(tmp);
+
+ if (flags and ACF_DBANSI )=ACF_DBANSI then s:=ioAnsi
+ else if (flags and ACF_DBBYTE )=ACF_DBBYTE then s:=ioByte
+ else if (flags and ACF_DBWORD )=ACF_DBWORD then s:=ioWord
+ else if (flags and ACF_DBUTEXT)= 0 then s:=ioDword
+ else s:=ioUnicode;
+ AddAttr(sub,ioType,s);
+
+ if (flags2 and ACF2_RW_MVAR)<>0 then AddAttrInt(sub,ioFileVariable,1);
+ if (flags2 and ACF2_RW_SVAR)<>0 then AddAttrInt(sub,ioArgVariable ,1);
+ if (flags2 and ACF2_RW_TVAR)<>0 then AddAttrInt(sub,ioVariables ,1);
+ if (flags2 and ACF2_RW_HEX )<>0 then AddAttrInt(sub,ioHex ,1);
+
+ if ( flags and ACF_LAST )<>0 then AddAttrInt(sub,ioLast ,1)
+ else if ((flags and ACF_DBUTEXT )=0) and
+ ((flags2 and ACF2_RW_TVAR)=0) then AddAttrInt(sub,ioValue,dbvalue);
+ end;
+// ----- MESSAGEBOX -----
+ ACT_MESSAGE: begin
+ sub:=AddChild(actnode,ioMessageBox,msgtext);
+ if (flags2 and ACF2_MSG_TTL)<>0 then AddAttrInt(sub,ioVariables ,1);
+ if (flags2 and ACF2_MSG_TXT)<>0 then AddAttrInt(sub,ioArgVariable,1);
+ if (flags and ACF_MSG_KEEP)<>0 then AddAttrInt(sub,ioKeepLast ,1);
+ AddAttr (sub,ioTitle,msgtitle);
+ AddAttrInt(sub,ioType ,boxopts);
+ end;
+ end;
+ end;
+end;
+
+procedure Export({act:integer;}fname:pWideChar;aflags:dword);
+var
+ i:integer;
+ f:THANDLE;
+ root,actnode:HXML;
+ res:pWideChar;
+ act:integer;
+begin
+ xmlparser.cbSize:={XML_API_SIZEOF_V1;//}SizeOf(XML_API_W);
+ CallService(MS_SYSTEM_GET_XI,0,lparam(@xmlparser));
+ root:=0;
+ with xmlparser do
+ begin
+ i:=0;
+ if (aflags and ACIO_APPEND)<>0 then
+ begin
+ i:=GetFSize(fname);
+ if i<>0 then
+ begin
+ mGetMem (res ,i+SizeOf(WideChar));
+ FillChar(res^,i+SizeOf(WideChar),0);
+ f:=Reset(fname);
+ BlockRead(f,res^,i);
+ CloseHandle(f);
+ root:=parseString(res,@i,nil);
+ mFreeMem(res);
+ i:=1;
+ end;
+ end;
+ if i=0 then // new file
+ root:=CreateNode(ioRoot,nil,false);
+
+ for act:=0 to MaxGroups-1 do
+ if ((aflags and ACIO_SELECTED)=0) or
+ ((GroupList[act].flags and (ACF_EXPORT or ACF_ASSIGNED))=
+ (ACF_EXPORT or ACF_ASSIGNED)) then
+ begin
+// GroupList[act].flags:=GroupList[act].flags and not ACF_EXPORT;
+ actnode:=addChild(root,ioAction,nil);
+ AddAttr(actnode,ioName,GroupList[act].descr);
+ if (GroupList[act].flags and ACF_DISABLED)<>0 then
+ AddAttrInt(actnode,ioDisabled,1);
+
+ i:=GroupList[act].firstAction;
+ if i<>0 then
+ repeat
+ WriteAction(actnode,i);
+ i:=ActionList[i].next;
+ until i=0;
+ end;
+
+ res:=toString(root,@i);
+ if i>0 then
+ begin
+ f:=Rewrite(fname);
+ BlockWrite(f,res^,i*SizeOf(WideChar));
+ CloseHandle(f);
+ end;
+ xmlparser.FreeMem(res);
+ DestroyNode(root);
+ end;
+end;
+
+function ActInOut(wParam:WPARAM;lParam:LPARAM):int_ptr; cdecl;
+begin
+ if (wParam and ACIO_EXPORT)=0 then
+ begin
+ result:=Import(pWideChar(lParam),wParam);
+ end
+ else
+ begin
+ result:=1;
+ Export(pWideChar(lParam),wParam);
+ end;
+ NotifyEventHooks(hevinout,wParam,lParam);
+end;
diff --git a/plugins/Actman/i_opt_dlg.inc b/plugins/Actman/i_opt_dlg.inc
new file mode 100644
index 0000000000..8b9e3fb32b
--- /dev/null
+++ b/plugins/Actman/i_opt_dlg.inc
@@ -0,0 +1,215 @@
+{}
+const
+ etHK = 1; // Groups changed
+ etACT = 2; // Actions changed
+const
+ ACI_APPLY :PAnsiChar = 'ACI_Apply';
+ ACI_NEW :PAnsiChar = 'ACI_New';
+ ACI_UP :PAnsiChar = 'ACI_Up';
+ ACI_DOWN :PAnsiChar = 'ACI_Down';
+ ACI_DELETE :PAnsiChar = 'ACI_Delete';
+ ACI_RELOAD :PAnsiChar = 'ACI_Reload';
+ ACI_TEST :PAnsiChar = 'ACI_Test';
+ ACI_IMPORT :PAnsiChar = 'ACI_Import';
+ ACI_EXPORT :PAnsiChar = 'ACI_Export';
+const
+ ACI_CONTACT = 'ACI_Contact';
+ ACI_SERVICE = 'ACI_Service';
+ ACI_PROGRAM = 'ACI_Program';
+ ACI_INSERT = 'ACI_Insert';
+ ACI_ADVANCE = 'ACI_Advanced';
+ ACI_CHAIN = 'ACI_Chain';
+ ACI_RW = 'ACI_Database';
+ ACI_MESSAGE = 'ACI_Message';
+
+ ACI_FORMAT = 'ACI_Format';
+
+ ACI_VAR_UNCHECKED = 'ACI_VarUnChecked';
+ ACI_VAR_CHECKED = 'ACI_VarChecked';
+
+const
+ sresInt = 0;
+ sresHex = 1;
+ sresString = 2;
+ sresStruct = 3;
+type
+ tActId = record
+ code:dword;
+ id :dword;
+ icon:PAnsiChar;
+ text:PAnsiChar;
+ end;
+const
+ ActIds:array [0..ACT_MAXTYPE-1] of tActId = (
+ (code:ACT_CONTACT; id:IDI_CONTACT; icon:ACI_CONTACT; text:'Open contact window'),
+ (code:ACT_SERVICE; id:IDI_SERVICE; icon:ACI_SERVICE; text:'Call service'),
+ (code:ACT_PROGRAM; id:IDI_PROGRAM; icon:ACI_PROGRAM; text:'Execute program'),
+ (code:ACT_TEXT ; id:IDI_INSERT ; icon:ACI_INSERT ; text:'Insert text'),
+ (code:ACT_ADVANCE; id:IDI_ADVANCE; icon:ACI_ADVANCE; text:'Advanced'),
+ (code:ACT_CHAIN ; id:IDI_CHAIN ; icon:ACI_CHAIN ; text:'Link to action'),
+ (code:ACT_RW ; id:IDI_RW ; icon:ACI_RW ; text:'Profile'),
+ (code:ACT_MESSAGE; id:IDI_MESSAGE; icon:ACI_MESSAGE; text:'MessageBox'));
+
+procedure RegisterIcon(var sid:TSKINICONDESC;id:uint_ptr;name:PAnsiChar;descr:PAnsiChar);
+var
+ buf:array [0..63] of WideChar;
+begin
+ sid.hDefaultIcon :=LoadImage(hInstance,MAKEINTRESOURCE(id),IMAGE_ICON,16,16,0);
+ sid.pszName :=name;
+ sid.szDescription.w:=FastAnsiToWideBuf(descr,buf);
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+end;
+
+procedure RegisterIcons;
+var
+ sid:TSKINICONDESC;
+ i:integer;
+begin
+ FillChar(sid,SizeOf(TSKINICONDESC),0);
+ sid.cbSize :=SizeOf(TSKINICONDESC);
+ sid.cx :=16;
+ sid.cy :=16;
+ sid.flags :=SIDF_UNICODE;
+ sid.szSection.w:='Actions';
+
+ RegisterIcon(sid,IDI_APPLY ,ACI_APPLY ,'Apply');
+ RegisterIcon(sid,IDI_NEW ,ACI_NEW ,'New');
+ RegisterIcon(sid,IDI_DELETE ,ACI_DELETE ,'Delete');
+ RegisterIcon(sid,IDI_UP ,ACI_UP ,'Up');
+ RegisterIcon(sid,IDI_DOWN ,ACI_DOWN ,'Down');
+ RegisterIcon(sid,IDI_RELOAD ,ACI_RELOAD ,'Reload');
+ RegisterIcon(sid,IDI_TEST ,ACI_TEST ,'Test');
+ RegisterIcon(sid,IDI_IMPORT ,ACI_IMPORT ,'Import');
+ RegisterIcon(sid,IDI_EXPORT ,ACI_EXPORT ,'Export');
+
+ RegisterIcon(sid,IDI_FORMAT ,ACI_FORMAT ,'Contact list format');
+
+ for i:=0 to ACT_MAXTYPE-1 do
+ with ActIds[i] do
+ RegisterIcon(sid,id,icon,text);
+
+ sid.cx:=8;
+ sid.cy:=8;
+ RegisterIcon(sid,IDI_VAR_CHECKED ,ACI_VAR_CHECKED ,'Use Variables');
+ RegisterIcon(sid,IDI_VAR_UNCHECKED,ACI_VAR_UNCHECKED,'Don''t use Variables');
+end;
+
+procedure SetStart;
+begin
+ if NewGroupList=nil then
+ begin
+ NewGroupList:=GroupList;
+ NewMaxGroups:=MaxGroups;
+ end;
+ if NewActionList=nil then
+ begin
+ NewActionList:=ActionList;
+ NewMaxActions:=MaxActions;
+ end;
+end;
+
+procedure SetChanged(wnd:HWND;atype:integer);
+begin
+ SendMessage(GetParent(wnd),PSM_CHANGED,0,0);
+ if ((atype and etHK )<>0) and (NewGroupList =GroupList ) then NewGroupList :=CloneGroupList;
+ if ((atype and etACT)<>0) and (NewActionList=ActionList) then NewActionList:=CloneActionList;
+end;
+
+procedure SetSave(Dialog:HWND;curIdx:integer);
+var
+ i,j:integer;
+ li:LV_ITEMW;
+ wnd:HWND;
+begin
+ if NewGroupList<>GroupList then
+ begin
+
+ DestroyGroups(GroupList,MaxGroups);
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_GROUP);
+ j:=SendMessageW(wnd,LVM_GETITEMCOUNT,0,0);
+ MaxGroups:=j;
+ if j>0 then
+ begin
+ GetMem (GroupList ,MaxGroups*SizeOf(tHKRecord));
+ FillChar(GroupList^,MaxGroups*SizeOf(tHKRecord),0);
+ li.mask :=LVIF_PARAM;
+ li.iSubItem:=0;
+ for i:=0 to j-1 do
+ begin
+ li.iItem:=i;
+ SendMessageW(wnd,LVM_GETITEMW,0,lparam(@li)); // GetLParam(wnd,i);
+ move(NewGroupList^[li.lParam],GroupList^[i],SizeOf(tHKRecord));
+ li.lParam:=i;
+ SendMessageW(wnd,LVM_SETITEMW,0,lparam(@li));
+ end;
+ end
+ else
+ GroupList:=nil;
+
+ FreeMem(NewGroupList);
+ NewGroupList:=GroupList;
+ NewMaxGroups:=MaxGroups;
+ end;
+end;
+
+procedure SetCancel;
+begin
+ if NewActionList<>nil then
+ begin
+ if (NewActionList<>ActionList) then
+ DestroyActions(NewActionList,NewMaxActions);
+ NewActionList:=nil;
+ end;
+ if NewGroupList<>nil then
+ begin
+ if (NewGroupList<>GroupList) then
+ DestroyGroups(NewGroupList,NewMaxGroups);
+ NewGroupList:=nil;
+ end;
+end;
+
+{$include i_opt_dlg2.inc}
+
+function OnOptInitialise(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ odp:TOPTIONSDIALOGPAGE;
+ ptr:pActionLink;
+ tmpl:pAnsiChar;
+ name:pansiChar;
+ proc:pointer;
+ i:integer;
+begin
+ result:=0;
+ NoDescription :=TranslateW('No Description');
+ StrCopyW(xmlfilename,'c:\export.xml');
+
+ DoInitCommonControls(ICC_USEREX_CLASSES);
+
+ FillChar(odp,SizeOf(odp),0);
+ odp.cbSize :=SizeOf(odp);
+ odp.flags :=ODPF_BOLDGROUPS or ODPF_EXPERTONLY;
+ odp.Position :=900003000;
+ odp.hInstance :=hInstance;
+ odp.szGroup.a :='Services';
+ odp.szTitle.a :='Actions';
+ odp.szTab.a :='Actions';
+ odp.pfnDlgProc :=@DlgProcOpt2;
+ odp.pszTemplate:=PAnsiChar(IDD_ACTION);
+ Options_AddPage(wParam,@odp);
+
+ ptr:=ActionLink;
+ while ptr<>nil do
+ begin
+ if @ptr^.AddOption<>nil then
+ begin
+ i:=ptr^.AddOption(tmpl,proc,name);
+ odp.pszTemplate:=tmpl;
+ odp.pfnDlgProc :=proc;
+ odp.szTab.a :=name;
+ Options_AddPage(wParam,@odp);
+ if i>0 then continue;
+ end;
+ ptr:=ptr^.Next;
+ end;
+end;
diff --git a/plugins/Actman/i_opt_dlg2.inc b/plugins/Actman/i_opt_dlg2.inc
new file mode 100644
index 0000000000..3c6b4e974b
--- /dev/null
+++ b/plugins/Actman/i_opt_dlg2.inc
@@ -0,0 +1,2109 @@
+{}
+
+const
+ inoutfilter:pWideChar = 'XML files'#0'*.xml'#0'All files'#0'*.*'#0#0;
+const
+ NoChainText:PWideChar = 'not defined';
+const
+ ActionNames:array [0..ACT_MAXTYPE] of pWideChar=(
+ 'Unknown','Contact','Service','Program','Text','Advanced','Action','Profile','Message');
+const
+ checknames:array [BST_UNCHECKED..BST_CHECKED] of PAnsiChar=(
+ ACI_VAR_UNCHECKED,ACI_VAR_CHECKED);
+const
+ MaxDescrLen = 128;
+const
+ hlpContact = 0;
+ hlpService = 1;
+ hlpProgram = 2;
+ hlpText = 3;
+ hlpAdvance = 4;
+ hlpChain = 5;
+ hlpDBRW = 6;
+ hlpMessage = 7;
+ hlpVariables = 30;
+ hlpAdvVariables = 31;
+const
+ ptNumber = 0;
+ ptString = 1;
+ ptUnicode = 2;
+ ptCurrent = 3;
+ ptResult = 4;
+ ptParam = 5;
+ ptStruct = 6;
+
+var
+ wstruct,lstruct:pAnsiChar;
+ DontReact:bool;
+ OldGroupTableProc,
+ OldActTableProc:pointer;
+ ChMask:dword;
+
+function GetNumValue(wnd:HWND;usevar:boolean;var dst):boolean;
+var
+ tmp:pWideChar;
+begin
+ result:=false;
+ pWideChar(dst):=GetDlgText(wnd);
+ if Pointer(dst)=nil then exit;
+ if not usevar then
+ begin
+ tmp:=PWideChar(dst);
+ if pWideChar(dst)^='$' then
+ begin
+ integer(dst):=HexToInt(pWideChar(dst)+1);
+ result:=true;
+ end
+ else
+ integer(dst):=StrToInt(pWideChar(dst));
+ mFreeMem(tmp);
+ end;
+end;
+
+function GetGroupName(id:dword):pWideChar;
+var
+ i:integer;
+begin
+ for i:=0 to NewMaxGroups-1 do
+ begin
+ if ((NewGroupList^[i].flags and ACF_ASSIGNED)<>0) and (id=NewGroupList^[i].id) then
+ begin
+ result:=NewGroupList^[i].descr;
+ exit;
+ end;
+ end;
+ result:=NoChainText;
+end;
+
+function AddGroup(Dialog:HWND;HKnum:dword):integer;
+var
+ li:LV_ITEMW;
+ list:HWND;
+begin
+ with NewGroupList^[HKnum] do
+ begin
+ if (flags and ACF_ASSIGNED)<>0 then
+ begin
+ list:=GetDlgItem(Dialog,IDC_ACTION_GROUP);
+ li.mask :=LVIF_PARAM+LVIF_TEXT;
+ li.iItem :=SendMessage(list,LVM_GETNEXTITEM,-1,LVNI_FOCUSED)+1;
+ li.iSubItem :=0;
+ li.lParam :=HKnum;
+ if descr=nil then
+ li.pszText:=NoDescription
+ else
+ li.pszText:=descr;
+ li.iItem :=SendMessageW(list,LVM_INSERTITEMW,0,lparam(@li));
+ if li.iItem>0 then
+ dec(li.iItem);
+ ListView_SetItemState(list,li.iItem,LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+ result:=li.iItem;
+ end
+ else
+ result:=-1;
+ end;
+end;
+
+// Fill action type combobox
+procedure FillActTypeList(list:hwnd);
+var
+ cbei:TCOMBOBOXEXITEMW;
+ il:HIMAGELIST;
+ i:integer;
+ buf:array [0..127] of WideChar;
+begin
+ SendMessage(list,CB_RESETCONTENT,0,0);
+ il:=ImageList_Create(16,16,ILC_COLOR32 or ILC_MASK,0,1);
+
+ cbei.mask:=CBEIF_IMAGE or CBEIF_SELECTEDIMAGE or CBEIF_TEXT; //!!
+ for i:=0 to ACT_MAXTYPE-1 do
+ begin
+ ImageList_AddIcon(il,CallService(MS_SKIN2_GETICON,0,lparam(ActIds[i].icon)));
+
+ cbei.pszText :=TranslateW(FastAnsiToWideBuf(ActIds[i].text,buf));
+ cbei.iItem :=i;
+ cbei.iImage :=i;
+ cbei.iSelectedImage:=i;
+ if SendMessageW(list,CBEM_INSERTITEMW,0,lparam(@cbei))=-1 then break;
+ end;
+ ImageList_Destroy(SendMessage(list,CBEM_SETIMAGELIST,0,il));
+ SendMessage(list,CB_SETCURSEL,0,0);
+end;
+
+procedure FillSubList(Dialog:hwnd);
+var
+ list,wnd:HWND;
+ i,act:integer;
+ arr:array [0..127] of WideChar;
+ li:LV_ITEMW;
+begin
+ wnd:=GetDlgItem(Dialog,IDC_ADV_VAL2);
+
+ SendMessage(wnd,CB_RESETCONTENT,0,0);
+
+ list:=GetDlgItem(Dialog,IDC_ACTION_LIST);
+ act:=SendMessageW(list,LVM_GETITEMCOUNT,0,0);
+ i:=0;
+ li.mask :=LVIF_TEXT;
+ li.iSubItem :=0;
+ li.pszText :=@arr;
+ li.cchTextMax:=SizeOf(arr) div SizeOf(WideChar);
+ while i<act do
+ begin
+ li.iItem:=i;
+ SendMessageW(list,LVM_GETITEMW,0,lparam(@li));
+ SendMessageW(wnd,CB_ADDSTRING,0,lparam(PWideChar(@arr)));
+ inc(i);
+ end;
+ SendMessage(wnd,CB_SETCURSEL,0,0);
+end;
+
+procedure FillChainList(Dialog:hwnd);
+var
+ wnd:HWND;
+ i:integer;
+// num:integer;
+begin
+ wnd:=GetDlgItem(Dialog,IDC_GROUP_LIST);
+// for current chain exclude
+// num:=SendDlgItemMessage(Dialog,IDC_ACTION_GROUP,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
+
+ SendMessage(wnd,CB_RESETCONTENT,0,0);
+ SendMessage(wnd,CB_SETITEMDATA,
+ SendMessageW(wnd,CB_ADDSTRING,0,lparam(TranslateW(NoChainText))),0);
+ for i:=0 to NewMaxGroups-1 do
+ begin
+ if (NewGroupList^[i].flags and (ACF_ASSIGNED or ACF_VOLATILE))=ACF_ASSIGNED then
+ begin
+ SendMessage(wnd,CB_SETITEMDATA,
+ SendMessageW(wnd,CB_ADDSTRING,0,lparam(NewGroupList^[i].descr)),
+ NewGroupList^[i].id);
+ end;
+ end;
+end;
+
+// action group table procedure (key hook)
+function NewGroupTableProc(Dialog:HWnd;hMessage:uint;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+var
+ i:integer;
+begin
+ result:=0;
+ case hMessage of
+ WM_KEYDOWN: begin
+ if (lParam and (1 shl 30))=0 then
+ begin
+ case wParam of
+ VK_F2: begin
+ i:=SendMessage(Dialog,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
+ if i>=0 then
+ PostMessageW(Dialog,LVM_EDITLABELW,i,0);
+ exit;
+ end;
+ VK_INSERT: begin
+ PostMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDC_GROUP_NEW,0);
+ exit;
+ end;
+ VK_DELETE: begin
+ PostMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDC_GROUP_DELETE,0);
+ exit;
+ end;
+ VK_UP: begin
+ if (GetKeyState(VK_CONTROL) and $8000)<>0 then
+ begin
+ PostMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDC_GROUP_UP,0);
+ exit;
+ end;
+ end;
+ VK_DOWN: begin
+ if (GetKeyState(VK_CONTROL) and $8000)<>0 then
+ begin
+ PostMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDC_GROUP_DOWN,0);
+ exit;
+ end;
+ end;
+ end;
+ end;
+ end;
+ end;
+ result:=CallWindowProc(OldGroupTableProc,Dialog,hMessage,wParam,lParam);
+end;
+
+// action (chain) table procedure (key hook)
+function NewActTableProc(Dialog:HWnd;hMessage:uint;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+var
+ i:integer;
+begin
+ result:=0;
+ case hMessage of
+ WM_KEYDOWN: begin
+ if (lParam and (1 shl 30))=0 then
+ begin
+ case wParam of
+ VK_F2: begin
+ i:=SendMessage(Dialog,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
+ if i>=0 then
+ PostMessageW(Dialog,LVM_EDITLABELW,i,0);
+ exit;
+ end;
+ VK_UP: begin
+ if (GetKeyState(VK_CONTROL) and $8000)<>0 then
+ begin
+ PostMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDC_ACTION_UP,0);
+ exit;
+ end;
+ end;
+ VK_DOWN: begin
+ if (GetKeyState(VK_CONTROL) and $8000)<>0 then
+ begin
+ PostMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDC_ACTION_DOWN,0);
+ exit;
+ end;
+ end;
+ VK_INSERT: begin
+ PostMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDC_ACTION_NEW,0);
+ exit;
+ end;
+ VK_DELETE: begin
+ PostMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDC_ACTION_DELETE,0);
+ exit;
+ end;
+ end;
+ end;
+ end;
+ end;
+ result:=CallWindowProc(OldActTableProc,Dialog,hMessage,wParam,lParam);
+end;
+
+// miranda button icon paint
+procedure SetButtonIcons2(Dialog:HWND);
+var
+ ti:TTOOLINFOW;
+ hwndTooltip:HWND;
+begin
+ hwndTooltip:=CreateWindowW(TOOLTIPS_CLASS,nil,TTS_ALWAYSTIP,
+ integer(CW_USEDEFAULT),integer(CW_USEDEFAULT),
+ integer(CW_USEDEFAULT),integer(CW_USEDEFAULT),
+ Dialog,0,hInstance,nil);
+
+ FillChar(ti,SizeOf(ti),0);
+ ti.cbSize :=sizeof(TOOLINFO);
+ ti.uFlags :=TTF_IDISHWND or TTF_SUBCLASS;
+ ti.hwnd :=Dialog;
+ ti.hinst :=hInstance;
+
+ ti.uId :=GetDlgItem(Dialog,IDC_ACTION_HELP);
+ ti.lpszText:=TranslateW('Help');
+ SendMessage(ti.uId,BM_SETIMAGE,IMAGE_ICON,
+ CallService(MS_SKIN_LOADICON,SKINICON_OTHER_HELP,0));
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId :=GetDlgItem(Dialog,IDC_ACTION_NEW);
+ ti.lpszText:=TranslateW('New');
+ SetButtonIcon(ti.uId,ACI_NEW);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId :=GetDlgItem(Dialog,IDC_GROUP_NEW);
+ SetButtonIcon(ti.uId,ACI_NEW);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId :=GetDlgItem(Dialog,IDC_ACTION_UP);
+ ti.lpszText:=TranslateW('Up');
+ SetButtonIcon(ti.uId,ACI_UP);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId :=GetDlgItem(Dialog,IDC_GROUP_UP);
+ SetButtonIcon(ti.uId,ACI_UP);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId :=GetDlgItem(Dialog,IDC_ACTION_DOWN);
+ ti.lpszText:=TranslateW('Down');
+ SetButtonIcon(ti.uId,ACI_DOWN);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId :=GetDlgItem(Dialog,IDC_GROUP_DOWN);
+ SetButtonIcon(ti.uId,ACI_DOWN);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId :=GetDlgItem(Dialog,IDC_ACTION_DELETE);
+ ti.lpszText:=TranslateW('Delete');
+ SetButtonIcon(ti.uId,ACI_DELETE);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId :=GetDlgItem(Dialog,IDC_GROUP_DELETE);
+ SetButtonIcon(ti.uId,ACI_DELETE);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId :=GetDlgItem(Dialog,IDC_GROUP_RELOAD);
+ ti.lpszText:=TranslateW('Reload');
+ SetButtonIcon(ti.uId,ACI_RELOAD);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId :=GetDlgItem(Dialog,IDC_GROUP_TEST);
+ ti.lpszText:=TranslateW('Test');
+ SetButtonIcon(ti.uId,ACI_TEST);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId :=GetDlgItem(Dialog,IDC_CNT_APPLY);
+ ti.lpszText:=TranslateW('Apply format');
+ SetButtonIcon(ti.uId,ACI_FORMAT);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId :=GetDlgItem(Dialog,IDC_GROUP_IMPORT);
+ ti.lpszText:=TranslateW('Import');
+ SetButtonIcon(ti.uId,ACI_IMPORT);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId :=GetDlgItem(Dialog,IDC_GROUP_EXPORT);
+ ti.lpszText:=TranslateW('Export');
+ SetButtonIcon(ti.uId,ACI_EXPORT);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.lpszText:=TranslateW('Use Variables');
+ ti.uId:=GetDlgItem(Dialog,IDC_SRV_WPAR);
+ SetButtonIcon(ti.uId,ACI_VAR_UNCHECKED);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+{
+ pc:=TranslateW('');
+ wnd:=GetDlgItem(Dialog,IDC_NEW);
+ SendMessage(hNew,BUTTONADDTOOLTIP,TWPARAM(pc),BATF_UNICODE);
+ SetButtonIcon(wnd,QS_NEW);
+}
+ ti.uId:=GetDlgItem(Dialog,IDC_SRV_LPAR);
+ SetButtonIcon(ti.uId,ACI_VAR_UNCHECKED);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId:=GetDlgItem(Dialog,IDC_SRV_SRVC);
+ SetButtonIcon(ti.uId,ACI_VAR_UNCHECKED);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId:=GetDlgItem(Dialog,IDC_PRG_PRG);
+ SetButtonIcon(ti.uId,ACI_VAR_UNCHECKED);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId:=GetDlgItem(Dialog,IDC_PRG_ARG);
+ SetButtonIcon(ti.uId,ACI_VAR_UNCHECKED);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId:=GetDlgItem(Dialog,IDC_TXT_FILE);
+ SetButtonIcon(ti.uId,ACI_VAR_UNCHECKED);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId:=GetDlgItem(Dialog,IDC_TXT_TEXT);
+ SetButtonIcon(ti.uId,ACI_VAR_UNCHECKED);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId:=GetDlgItem(Dialog,IDC_RW_MVAR);
+ SetButtonIcon(ti.uId,ACI_VAR_UNCHECKED);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId:=GetDlgItem(Dialog,IDC_RW_SVAR);
+ SetButtonIcon(ti.uId,ACI_VAR_UNCHECKED);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId:=GetDlgItem(Dialog,IDC_RW_TVAR);
+ SetButtonIcon(ti.uId,ACI_VAR_UNCHECKED);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId:=GetDlgItem(Dialog,IDC_MSG_TTL);
+ SetButtonIcon(ti.uId,ACI_VAR_UNCHECKED);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId:=GetDlgItem(Dialog,IDC_MSG_TXT);
+ SetButtonIcon(ti.uId,ACI_VAR_UNCHECKED);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+end;
+
+function MoveLVItem(list:HWND;num:integer;incr:integer):integer;
+var
+ li:LV_ITEM;
+ buf:array [0..127] of WideChar;
+begin
+ li.mask :=LVIF_PARAM+LVIF_STATE+LVIF_TEXT;
+ li.iItem :=num;
+ li.iSubItem :=0;
+ li.StateMask :=dword(-1);
+ li.pszText :=@buf;
+ li.cchTextMax:=127;
+ SendMessageW(list,LVM_GETITEMW,0,lparam(@li));
+ SendMessageW(list,LVM_DELETEITEM,li.iItem,0);
+ inc(li.iItem,incr);
+
+ SendMessageW(list,LVM_INSERTITEMW,0,lparam(@li));
+ SendMessageW(list,LVM_SETITEMSTATE,li.iItem,lparam(@li));
+ result:=li.iItem;
+end;
+
+function MoveGroup(list:HWND;num:integer=-1;incr:integer=0):integer;
+var
+ i,j:integer;
+begin
+ if num<0 then
+ begin
+ result:=-1;
+ j:=SendMessage(list,LVM_GETITEMCOUNT,0,0)-1;
+ if incr<0 then // up, from beginning
+ begin
+ for i:=0 to j do
+ begin
+ if SendMessage(list,LVM_GETITEMSTATE,i,LVIS_SELECTED)<>0 then
+ begin
+ if i=0 then break;
+ LV_MoveItem(list,incr,i);
+// MoveLVItem(list,i,incr);
+ if result<0 then result:=i+incr;
+ end;
+ end;
+ end
+ else // down, from the end
+ begin
+ for i:=j downto 0 do
+ begin
+ if SendMessage(list,LVM_GETITEMSTATE,i,LVIS_SELECTED)<>0 then
+ begin
+ if i=j then break;
+ LV_MoveItem(list,incr,i);
+// MoveLVItem(list,i,incr);
+ if result<0 then result:=i+incr;
+ end;
+ end;
+ end;
+ end
+ else
+ begin
+ MoveLVItem(list,num,incr);
+ result:=num;
+ end;
+end;
+
+{$include i_dlglists.inc}
+
+procedure SetNumValue(wnd:HWND;value:dword;isvar:boolean;ishex:boolean);
+var
+ buf:array [0..31] of WideChar;
+begin
+ if isvar then
+ SendMessageW(wnd,WM_SETTEXT,0,value)
+ else if ishex then
+ begin
+ buf[0]:='$';
+ IntToHex(PWideChar(@buf[1]),value);
+ SendMessageW(wnd,WM_SETTEXT,0,tlparam(@buf));
+ end
+ else
+ SendMessageW(wnd,WM_SETTEXT,0,tlparam(IntToStr(buf,value)));
+end;
+
+function DlgProcOpt2(Dialog:HWnd;hMessage:uint;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+
+ {$include i_visual.inc}
+
+ procedure ShowHelp(code:integer);
+ var
+ buf:PAnsiChar;
+ vhi:TVARHELPINFO;
+ begin
+ case code of
+ hlpVariables: begin
+ FillChar(vhi,SizeOf(vhi),0);
+ with vhi do
+ begin
+ cbSize:=SizeOf(vhi);
+ flags:=VHF_NOINPUTDLG;
+ end;
+ CallService(MS_VARS_SHOWHELPEX,Dialog,tlparam(@vhi));
+ end;
+ hlpAdvVariables: begin
+ FillChar(vhi,SizeOf(vhi),0);
+ with vhi do
+ begin
+ cbSize :=SizeOf(vhi);
+ flags :=VHF_FULLDLG or VHF_SETLASTSUBJECT;
+ hwndCtrl :=GetDlgItem(Dialog,IDC_ADV_VARS);
+ szSubjectDesc:='test your variables';
+ end;
+ CallService(MS_VARS_SHOWHELPEX,Dialog,tlparam(@vhi));
+ end;
+ hlpContact: begin
+{
+ MessageBoxW(0,
+ TranslateW('Select contact to open it''s window'),
+ TranslateW('Contacts'),0);
+}
+ end;
+ hlpService: begin
+ buf:=GetDlgText(Dialog,IDC_EDIT_SERVICE,true);
+//!! if buf<>nil then
+ begin
+ ApiCard.Service:=buf;
+ mFreeMem(buf);
+ ApiCard.Show;
+ end;
+ end;
+ hlpProgram: begin
+ MessageBoxW(0,
+ TranslateW('Text <last> replacing'#13#10+
+ 'by last result'#13#10#13#10+
+ 'Text <param> replacing'#13#10+
+ 'by parameter'),
+ TranslateW('Text'),0);
+ end;
+ hlpText: begin
+ MessageBoxW(0,
+ TranslateW('^s - selected (and replaced) part'#13#10+
+ '^e - replaced by empty string'#13#10+
+ '^v - paste text from Clipboard'#13#10+
+ '^t - replaced by tabulation'#13#10+
+ '^l - replaced by last result as unicode'#13#10+
+ '^h - replaced by last result as hex'#13#10+
+ '^a - in the end: autosend'#13#10+
+ '^f(name[,str])'#13#10+
+ ' paste line from text file.'#13#10+
+ ' brackets contents must be w/o spaces'),
+ TranslateW('Text'),0);
+ end;
+ hlpAdvance: begin
+ end;
+ hlpChain: begin
+{
+ MessageBoxW(0,
+ TranslateW('You can select another group from combobox, '+
+ 'then it will be executed, after that current '+
+ 'action group will be continued.'),
+ TranslateW('Macros'),0);
+}
+ end;
+ hlpDBRW: begin
+ end;
+ hlpMessage: begin
+ MessageBoxW(0,
+ TranslateW(
+ 'Text <last> replacing'#13#10+
+ 'by last result'#13#10#13#10+
+ 'Returns:'#13#10+
+ '--------'#13#10+
+ 'OK'#9'= 1'#13#10+
+ 'CANCEL'#9'= 2'#13#10+
+ 'ABORT'#9'= 3'#13#10+
+ 'RETRY'#9'= 4'#13#10+
+ 'IGNORE'#9'= 5'#13#10+
+ 'YES'#9'= 6'#13#10+
+ 'NO'#9'= 7'#13#10+
+ 'CLOSE'#9'= 8'),
+ TranslateW('MessageBox'),0);
+ end;
+ end;
+ end;
+
+ // enable/disable navigation chain buttons
+ procedure CheckGrpList(num:integer=-1);
+ var
+ wnd:HWND;
+ dir:integer;
+ okup,okdown:boolean;
+ begin
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_GROUP);
+// if num<0 then
+ begin
+ dir:=LV_CheckDirection(wnd);
+ okup :=odd(loword(dir));
+ okdown:=(loword(dir) and 2)<>0;
+ end;
+{
+ else
+ begin
+ okup :=num>0;
+ okdown:=(num+1)<SendMessage(wnd,LVM_GETITEMCOUNT,0,0);
+ SendMessage(wnd,LVM_ENSUREVISIBLE,num,0);
+ end;
+}
+ EnableWindow(GetDlgItem(Dialog,IDC_GROUP_UP ),okup);
+ EnableWindow(GetDlgItem(Dialog,IDC_GROUP_DOWN),okdown);
+ SendMessage(wnd,LVM_ENSUREVISIBLE,hiword(dir)-1,0);
+(*
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_GROUP);
+ if num<0 then
+ num:=SendMessage(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
+ EnableWindow(GetDlgItem(Dialog,IDC_GROUP_UP),num>0);
+ EnableWindow(GetDlgItem(Dialog,IDC_GROUP_DOWN),
+ (num+1)<SendMessage(wnd,LVM_GETITEMCOUNT,0,0));
+ SendMessage(wnd,LVM_ENSUREVISIBLE,num,0);
+ result:=num;
+*)
+ end;
+ procedure CheckActList(num:integer=-1);
+ var
+ wnd:HWND;
+ dir:integer;
+ okup,okdown:boolean;
+ begin
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_LIST);
+// if num<0 then
+ begin
+ dir:=LV_CheckDirection(wnd);
+ okup :=odd(loword(dir));
+ okdown:=(loword(dir) and 2)<>0;
+ end;
+{
+ else
+ begin
+ okup :=num>0;
+ okdown:=(num+1)<SendMessage(wnd,LVM_GETITEMCOUNT,0,0);
+ SendMessage(wnd,LVM_ENSUREVISIBLE,num,0);
+ end;
+}
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_UP ),okup);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_DOWN),okdown);
+ SendMessage(wnd,LVM_ENSUREVISIBLE,hiword(dir)-1,0);
+(*
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_LIST);
+ if num<0 then
+ num:=SendMessage(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_UP),num>0);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_DOWN),
+ (num+1)<SendMessage(wnd,LVM_GETITEMCOUNT,0,0));
+ SendMessage(wnd,LVM_ENSUREVISIBLE,num,0);
+*)
+ end;
+
+ // change current action name (by type)
+ procedure ChangeActionName(num:integer=-1;acttype:integer=0;str:PWideChar=nil);
+ var
+ li:LV_ITEMW;
+ wnd:HWND;
+ str1:pWideChar;
+ begin
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_LIST);
+ if num<0 then
+ li.iItem:=SendMessage(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED)
+ else
+ li.iItem:=num;
+ if li.iItem>=0 then
+ begin
+ li.iSubItem:=0;
+ // getting SubAction number
+ li.mask:=LVIF_PARAM;
+ SendMessage(wnd,LVM_GETITEM,0,tlparam(@li));
+
+ // changing to default name
+ if str=nil then
+ begin
+ if num>=0 then // new item - screen only
+ str1:=TranslateW(ActionNames[ACT_CONTACT])
+ else // change action type
+ begin
+ str1:=NewActionList[li.lParam].descr;
+ if str1=nil then // not in memory yet
+ str1:=TranslateW(ActionNames[acttype])
+ else
+ exit;
+ end;
+ end
+ else // rename
+ begin
+ str1:=str;
+ mFreeMem(NewActionList[li.lParam].descr);
+ StrDupW (NewActionList[li.lParam].descr,str);
+ end;
+
+// screen
+ li.mask :=LVIF_TEXT;
+ li.pszText:=str1;
+ SendMessageW(wnd,LVM_SETITEMW,0,tlparam(@li));
+ end;
+ end;
+
+ // Fill Chain list
+ procedure MakeActionList(HK:pHKRecord);
+ var
+ i,idx:integer;
+ wnd:HWND;
+ li:LV_ITEMW;
+ p:pWideChar;
+ begin
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_LIST);
+ SendMessage(wnd,LVM_DELETEALLITEMS,0,0);
+
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_UP),false);
+ if (HK=nil) or (HK^.firstAction=0) then
+ begin
+ SHWindows;
+ SHActButtons(SW_HIDE);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_LIST ),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_GROUP_TEST ),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_HELP ),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_DELETE),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_DOWN ),false);
+ exit;
+ end;
+ SHActButtons(SW_SHOW);
+
+ li.mask :=LVIF_TEXT or LVIF_PARAM;
+ li.iSubitem:=0;
+ i:=HK^.firstAction;
+ idx:=0;
+ repeat
+ p:=NewActionList^[i].descr;
+ if p=nil then
+ p:=TranslateW(ActionNames[NewActionList^[i].actionType]);
+ li.pszText:=p;
+ li.iItem :=idx;
+ li.lParam :=i;
+ SendMessageW(wnd,LVM_INSERTITEMW,0,tlparam(@li));
+ ListView_SetCheckState(wnd,idx,(NewActionList^[i].flags and ACF_DISABLED)=0);
+
+ i:=NewActionList^[i].next;
+ inc(idx);
+ until i=0;
+
+ Listview_SetItemState(wnd,0,
+ LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_LIST ),true);
+ EnableWindow(GetDlgItem(Dialog,IDC_GROUP_TEST ),true);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_HELP ),true);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_DELETE),true);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_DOWN),idx>1);
+// FillSubList(Dialog);
+ FillAction(HK^.firstAction);
+ CheckActList(0);
+ end;
+
+ procedure SaveAction(listnum,actnum:integer);
+ var
+ wnd:HWND;
+ i:integer;
+ tmp:pWideChar;
+ arr: array [0..255] of WideChar;
+ li:LV_ITEMW;
+ begin
+ if (ChMask and ACTM_ACT)=0 then exit;
+ ChMask:=ChMask and not ACTM_ACT;
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_LIST);
+ if listnum<0 then
+ begin
+ listnum:=SendMessageW(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
+ if listnum<0 then
+ exit;
+ end;
+ if actnum<0 then
+ actnum:=LV_GetLParam(wnd,listnum);
+ StrDupW(tmp,NewActionList^[actnum].descr); // keeping old name
+ FreeAction(@NewActionList^[actnum]);
+ with NewActionList^[actnum] do
+ begin
+ flags :=ACF_ASSIGNED;
+ flags2:=0;
+
+ descr:=tmp;
+ if descr=nil then
+ begin
+ li.iItem :=listnum;
+ li.mask :=LVIF_TEXT;
+ li.iSubItem :=0;
+ li.pszText :=@arr;
+ li.cchTextMax:=SizeOf(arr) div SizeOf(WideChar);
+ SendDlgItemMessageW(Dialog,IDC_ACTION_LIST,LVM_GETITEMW,0,tlparam(@li));
+ StrDupW(descr,arr);
+ end;
+
+ if ListView_GetCheckState(wnd,listnum)=0 then
+ flags:=flags or ACF_DISABLED;
+
+ actionType:=ActIds[SendDlgItemMessage(Dialog,IDC_ACTION_TYPE,CB_GETCURSEL,0,0)].code;
+ case actionType of
+ ACT_CONTACT: begin
+ contact:=SendDlgItemMessage(Dialog,IDC_CONTACTLIST,CB_GETITEMDATA,
+ SendDlgItemMessage(Dialog,IDC_CONTACTLIST,CB_GETCURSEL,0,0),0);
+ if IsDlgButtonChecked(Dialog,IDC_CNT_KEEP)=BST_CHECKED then
+ flags:=flags or ACF_KEEPONLY;
+ end;
+
+ ACT_SERVICE: begin
+ if IsDlgButtonChecked(Dialog,IDC_SRV_WPAR)=BST_CHECKED then
+ flags2:=flags2 or ACF2_SRV_WPAR;
+ if IsDlgButtonChecked(Dialog,IDC_SRV_LPAR)=BST_CHECKED then
+ flags2:=flags2 or ACF2_SRV_LPAR;
+ if IsDlgButtonChecked(Dialog,IDC_SRV_SRVC)=BST_CHECKED then
+ flags2:=flags2 or ACF2_SRV_SRVC;
+
+ case CB_GetData(GetDlgItem(Dialog,IDC_FLAG_WPAR)) of
+ ptParam: begin
+ flags:=flags or ACF_WPARAM
+ end;
+ ptResult: begin
+ flags:=flags or ACF_WRESULT
+ end;
+ ptCurrent: begin
+ flags:=flags or ACF_WPARNUM or ACF_WCURRENT
+ end;
+ ptNumber: begin
+ flags:=flags or ACF_WPARNUM;
+ if GetNumValue(GetDlgItem(Dialog,IDC_EDIT_WPAR),
+ (flags2 and ACF2_SRV_WPAR)<>0,wparam) then
+ flags2:=flags2 or ACF2_SRV_WHEX;
+// wparam:=GetDlgItemInt(Dialog,IDC_EDIT_WPAR,pbool(nil)^,true);
+ end;
+ ptStruct: begin
+ flags:=flags or ACF_WSTRUCT;
+ StrDup(pAnsiChar(wparam),wstruct);
+ end;
+ ptUnicode: begin
+ flags:=flags or ACF_WUNICODE;
+ pointer(wparam):=GetDlgText(Dialog,IDC_EDIT_WPAR);
+ end;
+ ptString: pointer(wparam):=GetDlgText(Dialog,IDC_EDIT_WPAR,true);
+ end;
+
+ case CB_GetData(GetDlgItem(Dialog,IDC_FLAG_LPAR)) of
+ ptParam: begin
+ flags:=flags or ACF_LPARAM
+ end;
+ ptResult: begin
+ flags:=flags or ACF_LRESULT
+ end;
+ ptCurrent: begin
+ flags:=flags or ACF_LPARNUM or ACF_LCURRENT
+ end;
+ ptNumber: begin
+ flags:=flags or ACF_LPARNUM;
+ if GetNumValue(GetDlgItem(Dialog,IDC_EDIT_LPAR),
+ (flags2 and ACF2_SRV_LPAR)<>0,lparam) then
+ flags2:=flags2 or ACF2_SRV_LHEX;
+// lparam:=GetDlgItemInt(Dialog,IDC_EDIT_LPAR,pbool(nil)^,true);
+ end;
+ ptStruct: begin
+ flags:=flags or ACF_LSTRUCT;
+ StrDup(pAnsiChar(lparam),lstruct);
+ end;
+ ptUnicode: begin
+ flags:=flags or ACF_LUNICODE;
+ pointer(lparam):=GetDlgText(Dialog,IDC_EDIT_LPAR);
+ end;
+ ptString: pointer(lparam):=GetDlgText(Dialog,IDC_EDIT_LPAR,true);
+ end;
+
+ if IsDlgButtonChecked(Dialog,IDC_RES_INSERT)=BST_CHECKED then
+ flags:=flags or ACF_INSERT;
+ if IsDlgButtonChecked(Dialog,IDC_RES_MESSAGE)=BST_CHECKED then
+ flags:=flags or ACF_MESSAGE;
+ if IsDlgButtonChecked(Dialog,IDC_RES_POPUP)=BST_CHECKED then
+ flags:=flags or ACF_POPUP;
+
+ case CB_GetData(GetDlgItem(Dialog,IDC_SRV_RESULT)) of
+ sresHex: flags:=flags or ACF_HEX;
+ sresInt: begin
+ if IsDlgButtonChecked(Dialog,IDC_RES_SIGNED)=BST_CHECKED then
+ flags:=flags or ACF_SIGNED;
+ end;
+ sresString: begin
+ flags:=flags or ACF_STRING;
+ if IsDlgButtonChecked(Dialog,IDC_RES_UNICODE)=BST_CHECKED then
+ flags:=flags or ACF_UNICODE;
+ if IsDlgButtonChecked(Dialog,IDC_RES_FREEMEM)=BST_CHECKED then
+ flags2:=flags2 or ACF2_FREEMEM;
+ end;
+ sresStruct: flags:=flags or ACF_STRUCT;
+ end;
+
+ service:=GetDlgText(Dialog,IDC_EDIT_SERVICE,true);
+
+ end;
+
+ ACT_PROGRAM: begin
+ prgname:=GetDlgText(Dialog,IDC_EDIT_PRGPATH);
+ {
+ p:=GetDlgText(IDC_EDIT_PRGPATH);
+ if p<>nil then
+ begin
+ CallService(MS_UTILS_PATHTORELATIVE,dword(p),dword(@buf));
+ StrDupW(prgname,@buf);
+ mFreeMem(p);
+ end;
+ }
+ args:=GetDlgText(Dialog,IDC_EDIT_PRGARGS);
+ if IsDlgButtonChecked(Dialog,IDC_FLAG_PARALLEL)=BST_CHECKED then
+ flags:=flags or ACF_PRTHREAD;
+ if IsDlgButtonChecked(Dialog,IDC_FLAG_CURPATH)=BST_CHECKED then
+ flags:=flags or ACF_CURPATH;
+ time:=GetDlgItemInt(Dialog,IDC_EDIT_PROCTIME,pbool(nil)^,false);
+ if IsDlgButtonChecked(Dialog,IDC_FLAG_MINIMIZE)=BST_CHECKED then
+ show:=SW_SHOWMINIMIZED
+ else if IsDlgButtonChecked(Dialog,IDC_FLAG_MAXIMIZE)=BST_CHECKED then
+ show:=SW_SHOWMAXIMIZED
+ else if IsDlgButtonChecked(Dialog,IDC_FLAG_HIDDEN)=BST_CHECKED then
+ show:=SW_HIDE
+ else //if IsDlgButtonChecked(Dialog,IDC_FLAG_NORMAL)=BST_CHECKED then
+ show:=SW_SHOWNORMAL;
+
+ if IsDlgButtonChecked(Dialog,IDC_PRG_PRG)=BST_CHECKED then
+ flags2:=flags2 or ACF2_PRG_PRG;
+ if IsDlgButtonChecked(Dialog,IDC_PRG_ARG)=BST_CHECKED then
+ flags2:=flags2 or ACF2_PRG_ARG;
+ end;
+
+ ACT_TEXT: begin
+ if IsDlgButtonChecked(Dialog,IDC_FLAG_CLIP)<>BST_UNCHECKED then
+ begin
+ flags:=flags or ACF_CLIPBRD;
+ if IsDlgButtonChecked(Dialog,IDC_CLIP_COPYTO)<>BST_UNCHECKED then
+ flags:=flags or ACF_COPYTO;
+ end
+ else
+ begin
+ if IsDlgButtonChecked(Dialog,IDC_TXT_TEXT)=BST_CHECKED then
+ flags2:=flags2 or ACF2_TXT_TEXT;
+ text:=GetDlgText(Dialog,IDC_EDIT_INSERT);
+ if IsDlgButtonChecked(Dialog,IDC_FLAG_FILE)<>BST_UNCHECKED then
+ begin
+ flags:=flags or ACF_FILE;
+ case CB_GetData(GetDlgItem(Dialog,IDC_FILE_ENC)) of
+ 0: flags:=flags or ACF_ANSI;
+ 1: flags:=flags or ACF_UTF8;
+ 2: flags:=flags or ACF_UTF8 or ACF_SIGN;
+ 3: flags:=flags or 0;
+ 4: flags:=flags or ACF_SIGN;
+ end;
+
+ if IsDlgButtonChecked(Dialog,IDC_TXT_FILE)=BST_CHECKED then
+ flags2:=flags2 or ACF2_TXT_FILE;
+ tfile:=GetDlgText(Dialog,IDC_FILE_PATH);
+ if IsDlgButtonChecked(Dialog,IDC_FILE_APPEND)<>BST_UNCHECKED then
+ flags:=flags or ACF_FAPPEND
+ else if IsDlgButtonChecked(Dialog,IDC_FILE_WRITE)<>BST_UNCHECKED then
+ flags:=flags or ACF_FWRITE;
+ end;
+ end;
+ end;
+
+ ACT_ADVANCE: begin
+ condition:=ADV_COND_NOP;
+ if IsDlgButtonChecked(Dialog,IDC_FLAG_GT )=BST_CHECKED then
+ condition:=ADV_COND_GT
+ else if IsDlgButtonChecked(Dialog,IDC_FLAG_LT )=BST_CHECKED then
+ condition:=ADV_COND_LT
+ else if IsDlgButtonChecked(Dialog,IDC_FLAG_EQ )=BST_CHECKED then
+ condition:=ADV_COND_EQ;
+ value:=GetDlgItemInt(Dialog,IDC_ADV_VALUE,pbool(nil)^,false);
+
+ if IsDlgButtonChecked(Dialog,IDC_FLAG_NOT)=BST_CHECKED then
+ condition:=condition or ADV_COND_NOT;
+
+ if IsDlgButtonChecked(Dialog,IDC_FLAG_BREAK)=BST_CHECKED then
+ action:=ADV_ACT_BREAK
+ else if IsDlgButtonChecked(Dialog,IDC_FLAG_JUMP )=BST_CHECKED then
+ action:=ADV_ACT_JUMP
+ else
+ action:=ADV_ACT_NOP;
+
+ case action of
+ ADV_ACT_JUMP: operval:=GetDlgText(Dialog,IDC_ADV_VAL2);
+ end;
+
+ if IsDlgButtonChecked(Dialog,IDC_FLAG_VARS)<>BST_UNCHECKED then
+ begin
+ varval:=GetDlgText(Dialog,IDC_ADV_VARS);
+ action:=action or ADV_ACT_VARS;
+ if IsDlgButtonChecked(Dialog,IDC_ADV_ASINT)<>BST_UNCHECKED then
+ flags:=flags or ACF_VARASINT;
+ end
+;{//!!executively!!
+ else }if IsDlgButtonChecked(Dialog,IDC_FLAG_MATH)=BST_CHECKED then
+ begin
+ mathval:=GetDlgItemInt(Dialog,IDC_ADV_VAL1,pbool(nil)^,true);
+ action :=action or ADV_ACT_MATH;
+ oper :=CB_GetData(GetDlgItem(Dialog,IDC_ADV_OPER));
+// oper :=SendDlgItemMessage(Dialog,IDC_ADV_OPER,CB_GETCURSEL,0,0);
+ end;
+ end;
+
+ ACT_CHAIN: begin
+ wnd:=GetDlgItem(Dialog,IDC_GROUP_LIST);
+ i:=SendMessage(wnd,CB_GETCURSEL,0,0);
+ if i>0 then
+ id:=SendMessage(wnd,CB_GETITEMDATA,i,0)
+ else
+ id:=0;
+ end;
+
+ ACT_RW: begin
+ if IsDlgButtonChecked(Dialog,IDC_RW_CURRENT)=BST_CHECKED then
+ flags:=flags or ACF_CURRENT
+ else if IsDlgButtonChecked(Dialog,IDC_RW_RESULT)=BST_CHECKED then
+ flags:=flags or ACF_RESULT
+ else if IsDlgButtonChecked(Dialog,IDC_RW_PARAM)=BST_CHECKED then
+ flags:=flags or ACF_PARAM
+ else
+ dbcontact:=SendDlgItemMessage(Dialog,IDC_CONTACTLIST,CB_GETITEMDATA,
+ SendDlgItemMessage(Dialog,IDC_CONTACTLIST,CB_GETCURSEL,0,0),0);
+ dbmodule :=GetDlgText(Dialog,IDC_RW_MODULE ,true);
+ dbsetting:=GetDlgText(Dialog,IDC_RW_SETTING,true);
+ if IsDlgButtonChecked(Dialog,IDC_RW_MVAR)=BST_CHECKED then
+ flags2:=flags2 or ACF2_RW_MVAR;
+ if IsDlgButtonChecked(Dialog,IDC_RW_SVAR)=BST_CHECKED then
+ flags2:=flags2 or ACF2_RW_SVAR;
+
+ if IsDlgButtonChecked(Dialog,IDC_RW_WRITE)=BST_CHECKED then
+ flags:=flags or ACF_DBWRITE
+ else if IsDlgButtonChecked(Dialog,IDC_RW_DELETE)=BST_CHECKED then
+ flags:=flags or ACF_DBDELETE;
+
+ if IsDlgButtonChecked(Dialog,IDC_RW_LAST)=BST_CHECKED then
+ flags:=flags or ACF_LAST;
+
+ if IsDlgButtonChecked(Dialog,IDC_RW_TVAR)=BST_CHECKED then
+ flags2:=flags2 or ACF2_RW_TVAR;
+
+ i:=CB_GetData(GetDlgItem(Dialog,IDC_RW_DATATYPE));
+ case i of
+ 0: flags:=flags or ACF_DBBYTE;
+ 1: flags:=flags or ACF_DBWORD;
+ 2: flags:=flags or 0;
+ 3: flags:=flags or ACF_DBANSI;
+ 4: flags:=flags or ACF_DBUTEXT;
+ end;
+ if i<3 then
+ begin
+ if (flags and ACF_LAST)=0 then
+ begin
+ if GetNumValue(GetDlgItem(Dialog,IDC_RW_VALUE),
+ (flags2 and ACF2_RW_TVAR)<>0,dbvalue) then
+ flags2:=flags2 or ACF2_RW_HEX;
+ end;
+ end
+ else
+ begin
+ if (flags and ACF_LAST)=0 then
+ pWideChar(dbvalue):=GetDlgText(Dialog,IDC_RW_TEXT);
+ end;
+
+ if (IsDlgButtonChecked(Dialog,IDC_RW_LAST)=BST_CHECKED) or
+ ((flags and ACF_LAST)<>0) then
+ flags:=flags or ACF_LAST;
+ end;
+
+ ACT_MESSAGE: begin
+ pWideChar(msgtitle):=GetDlgText(Dialog,IDC_MSG_TITLE);
+ pWideChar(msgtext ):=GetDlgText(Dialog,IDC_MSG_TEXT);
+ if IsDlgButtonChecked(Dialog,IDC_MSG_KEEP)=BST_CHECKED then
+ flags:=flags or ACF_MSG_KEEP;
+ if IsDlgButtonChecked(Dialog,IDC_MSG_TTL)=BST_CHECKED then
+ flags2:=flags2 or ACF2_MSG_TTL;
+ if IsDlgButtonChecked(Dialog,IDC_MSG_TXT)=BST_CHECKED then
+ flags2:=flags2 or ACF2_MSG_TXT;
+
+ if IsDlgButtonChecked(Dialog,IDC_MSGB_OC )=BST_CHECKED then boxopts:=MB_OKCANCEL
+ else if IsDlgButtonChecked(Dialog,IDC_MSGB_ARI)=BST_CHECKED then boxopts:=MB_ABORTRETRYIGNORE
+ else if IsDlgButtonChecked(Dialog,IDC_MSGB_YNC)=BST_CHECKED then boxopts:=MB_YESNOCANCEL
+ else if IsDlgButtonChecked(Dialog,IDC_MSGB_YN )=BST_CHECKED then boxopts:=MB_YESNO
+ else if IsDlgButtonChecked(Dialog,IDC_MSGB_RC )=BST_CHECKED then boxopts:=MB_RETRYCANCEL
+ else{if IsDlgButtonChecked(Dialog,IDC_MSGB_OK )=BST_CHECKED then}boxopts:=MB_OK;
+
+ if IsDlgButtonChecked(Dialog,IDC_MSGI_ERROR)=BST_CHECKED then boxopts:=boxopts or MB_ICONHAND
+ else if IsDlgButtonChecked(Dialog,IDC_MSGI_QUEST)=BST_CHECKED then boxopts:=boxopts or MB_ICONQUESTION
+ else if IsDlgButtonChecked(Dialog,IDC_MSGI_WARN )=BST_CHECKED then boxopts:=boxopts or MB_ICONWARNING
+ else if IsDlgButtonChecked(Dialog,IDC_MSGI_INFO )=BST_CHECKED then boxopts:=boxopts or MB_ICONINFORMATION
+ ;//else if IsDlgButtonChecked(Dialog,IDC_MSGI_NONE)=BST_CHECKED then ;
+ end;
+
+ end;
+ end;
+ end;
+
+ procedure BuildActionChain(group:integer=-1);
+ var
+ i,j,item:integer;
+ wnd:HWND;
+ act:pHKAction;
+ idx:integer;
+ begin
+ if (ChMask and ACTM_ACTS)=0 then exit;
+ ChMask:=ChMask and not ACTM_ACTS;
+ SaveAction(-1,-1);
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_LIST);
+ i:=SendMessageW(wnd,LVM_GETITEMCOUNT,0,0);
+ idx:=LV_GetLParam(GetDlgItem(Dialog,IDC_ACTION_GROUP),group);
+ if i>0 then
+ begin
+ j:=LV_GetLParam(wnd,0);
+ NewGroupList^[idx].firstAction:=j;
+ act:=@NewActionList^[j];
+ for item:=1 to i-1 do
+ begin
+ j:=LV_GetLParam(wnd,item);
+ act^.next:=j;
+ act:=@NewActionList^[j];
+ end;
+ act^.next:=0;
+ end
+ else
+ begin
+ if idx>=0 then
+ NewGroupList^[idx].firstAction:=0;
+ end;
+ end;
+
+ procedure CheckActionList(next:integer);
+ var
+ i:integer;
+ wnd:HWND;
+ begin
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_LIST);
+ i:=SendMessage(wnd,LVM_GETITEMCOUNT,0,0);
+ if i>0 then
+ begin
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_HELP),true);
+ if next=i then
+ dec(next);
+ ListView_SetItemState(wnd,next,
+ LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+ end
+ else
+ begin
+ SHWindows;
+ SHActButtons(SW_HIDE);
+ EnableWindow(wnd,false);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_HELP ),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_GROUP_TEST ),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_DELETE),false);
+ next:=0;
+ end;
+ CheckActList(next);
+ end;
+
+ procedure CheckGroupList(next:integer);
+ var
+ i:integer;
+ wnd:HWND;
+ li:LV_ITEMW;
+ begin
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_GROUP);
+ i:=SendMessage(wnd,LVM_GETITEMCOUNT,0,0);
+ if i>0 then
+ begin
+ if next=i then
+ dec(next);
+ ListView_SetItemState(wnd,next,LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+ li.iItem :=next;
+ li.iSubItem:=0;
+ li.mask :=LVIF_PARAM;
+ SendMessage(wnd,LVM_GETITEM,0,tlparam(@li));
+ end
+ else
+ begin
+ EnableWindow(wnd,false);
+ EnableWindow(GetDlgItem(Dialog,IDC_GROUP_DELETE),false);
+ CheckActionList(0);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_NEW),false);
+ end;
+ CheckGrpList(next);
+ end;
+
+ // Fill action group list and disable chain controls
+ function FillGroupList{(Dialog:hwnd)}:integer;
+ var
+ CurGroup:pHKRecord;
+ i:integer;
+ list:HWND;
+ lvi:TLVITEMW;
+ begin
+ SendDlgItemMessage(Dialog,IDC_ACTION_LIST,LVM_DELETEALLITEMS,0,0);
+ CheckActionList(-1);
+ list:=GetDlgItem(Dialog,IDC_ACTION_GROUP);
+
+ SendMessage(list,LVM_DELETEALLITEMS,0,0);
+ CurGroup:=@NewGroupList^;
+ result:=-1;
+ lvi.mask:=LVIF_TEXT+LVIF_PARAM;
+ lvi.iSubItem:=0;
+ for i:=0 to NewMaxGroups-1 do
+ begin
+ if (CurGroup^.flags and ACF_ASSIGNED)<>0 then
+ begin
+ lvi.iItem :=i;
+ lvi.lParam:=i;
+ if CurGroup^.descr=nil then
+ lvi.pszText:=NoDescription
+ else
+ lvi.pszText:=CurGroup^.descr;
+ SendMessageW(list,LVM_INSERTITEMW,0,tlparam(@lvi));
+ inc(result);
+ end;
+ inc(CurGroup);
+ end;
+
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_GROUP ),result>=0);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_LIST ),result>=0);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_NEW ),result>=0);
+ EnableWindow(GetDlgItem(Dialog,IDC_GROUP_DELETE ),result>=0);
+ if result<0 then
+ begin
+ EnableWindow(GetDlgItem(Dialog,IDC_GROUP_TEST ),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_HELP ),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_DELETE),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_UP ),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_DOWN ),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_STAT_GROUPS ),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_TYPE ),false);
+ end
+ else
+ result:=0;
+ SendMessage(list,CB_SETCURSEL,0,0);
+ SendMessage(list,LVM_SETCOLUMNWIDTH,0,LVSCW_AUTOSIZE_USEHEADER);
+
+ ListView_SetItemState(list,0,
+ LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+ end;
+
+ procedure SHMath(show:boolean);
+ var
+ wnd:HWND;
+ begin
+ EnableWindow(GetDlgItem(Dialog,IDC_ADV_ASINT),not show);
+ EnableWindow(GetDlgItem(Dialog,IDC_ADV_VARS ),not show);
+
+ wnd:=GetDlgItem(Dialog,IDC_ADV_OPER);
+ EnableWindow(wnd,show);
+ EnableWindow(GetDlgItem(Dialog,IDC_ADV_VAL1),
+ show and (CB_GetData(wnd)<>lresult(aeNot)));//(SendMessage(wnd,CB_GETCURSEL,0,0)<>0));
+ end;
+
+ procedure SetMBRadioIcon(h:THANDLE;id:dword;icon:uint_ptr);
+ begin
+ SendDlgItemMessage(Dialog,id,BM_SETIMAGE,IMAGE_ICON,
+ LoadImage(h,MAKEINTRESOURCE(icon),IMAGE_ICON,16,16,0{LR_SHARED}));
+ // SendDlgItemMessage(Dialog,id,BM_SETIMAGE,IMAGE_ICON,LoadIcon(0,icon));
+ end;
+
+ procedure SetMBRadioIcons;
+ var
+ h:THANDLE;
+ begin
+ h:=LoadLibrary('user32.dll');
+ // SetMBRadioIcon(IDC_MSGI_NONE,IDI_); //?
+ SetMBRadioIcon(h,IDC_MSGI_ERROR,103{IDI_HAND});
+ SetMBRadioIcon(h,IDC_MSGI_QUEST,102{IDI_QUESTION});
+ SetMBRadioIcon(h,IDC_MSGI_WARN ,101{IDI_EXCLAMATION});
+ SetMBRadioIcon(h,IDC_MSGI_INFO ,104{IDI_ASTERISK});
+ FreeLibrary(h);
+ end;
+
+ procedure FillFileName(idc:integer);
+ var
+ pw,ppw:pWideChar;
+ begin
+ mGetMem(pw,1024*SizeOf(WideChar));
+ ppw:=GetDlgText(Dialog,idc);
+ if ShowDlgW(pw,ppw) then
+ SetDlgItemTextW(Dialog,idc,pw);
+ mFreeMem(ppw);
+ mFreeMem(pw);
+ end;
+
+var
+ wnd,wnd1,wnd2:HWND;
+ i,j:int_ptr;
+ lvflag:integer;
+ pc:pAnsiChar;
+ li:LV_ITEMW;
+ lv:LV_COLUMNW;
+ b:boolean;
+ ico:HICON;
+begin
+ result:=0;
+ case hMessage of
+ WM_DESTROY: begin
+ ApiCard.Free;
+ SetCancel;
+ mFreeMem(wstruct);
+ mFreeMem(lstruct);
+ end;
+
+ WM_INITDIALOG: begin
+ ApiCard:=CreateServiceCard(Dialog);
+ wstruct:=nil;
+ lstruct:=nil;
+ SetStart;
+ DontReact :=true;
+ SHWindows;
+ TranslateDialogDefault(Dialog);
+ SetButtonIcons2(Dialog);
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_LIST);
+ SendMessage(wnd,LVM_SETEXTENDEDLISTVIEWSTYLE,LVS_EX_CHECKBOXES,LVS_EX_CHECKBOXES);
+ SendMessage(wnd,LVM_SETUNICODEFORMAT,1,0);
+ zeromemory(@lv,sizeof(lv));
+ lv.mask:=LVCF_WIDTH;
+ lv.cx :=110;
+ SendMessageW(wnd,LVM_INSERTCOLUMNW ,0,tlparam(@lv));
+ SendMessageW(wnd,LVM_SETCOLUMNWIDTH,0,LVSCW_AUTOSIZE_USEHEADER);
+
+ MakeResultTypeList(GetDlgItem(Dialog,IDC_SRV_RESULT));
+ MakeParamTypeList (GetDlgItem(Dialog,IDC_FLAG_WPAR));
+ MakeParamTypeList (GetDlgItem(Dialog,IDC_FLAG_LPAR));
+ MakeMathOperList (GetDlgItem(Dialog,IDC_ADV_OPER));
+ MakeFileEncList (GetDlgItem(Dialog,IDC_FILE_ENC));
+ MakeDataTypeList (GetDlgItem(Dialog,IDC_RW_DATATYPE));
+
+ // service list for RunService
+ ApiCard.FillList(GetDlgItem(Dialog,IDC_EDIT_SERVICE));
+ // contact list for ContactMessage
+ FillContactList(GetDlgItem(Dialog,IDC_CONTACTLIST),fCLfilter,fCLformat);
+ // action type combobox
+ FillActTypeList(GetDlgItem(Dialog,IDC_ACTION_TYPE));
+
+ if isVarsInstalled then
+ begin
+ ico:=CallService(MS_VARS_GETSKINITEM,0,VSI_HELPICON);
+ SendDlgItemMessage(Dialog,IDC_HLP_FVARS,BM_SETIMAGE,IMAGE_ICON,ico);
+ SendDlgItemMessage(Dialog,IDC_HLP_VARS ,BM_SETIMAGE,IMAGE_ICON,ico);
+ SendDlgItemMessage(Dialog,IDC_ADV_HVARS,BM_SETIMAGE,IMAGE_ICON,ico);
+ end;
+
+ if ServiceExists(MS_SYSTEM_GET_XI)=0 then
+ begin
+ EnableWindow(GetDlgItem(Dialog,IDC_GROUP_EXPORT),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_GROUP_IMPORT),false);
+ end;
+
+ OldActTableProc :=pointer(SetWindowLongPtrW(wnd,GWL_WNDPROC,long_ptr(@NewActTableProc)));
+ OldGroupTableProc:=pointer(SetWindowLongPtrW(GetDlgItem(Dialog,IDC_ACTION_GROUP),
+ GWL_WNDPROC,long_ptr(@NewGroupTableProc)));
+
+ // fill group list
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_GROUP);
+ SendMessage(wnd,LVM_SETUNICODEFORMAT,1,0);
+ lv.mask:=LVCF_WIDTH;
+ lv.cx :=110;
+ SendMessageW(wnd,LVM_INSERTCOLUMNW,0,tlparam(@lv));
+ FillGroupList{(Dialog)};
+ FillChainList(Dialog);
+
+ SetMBRadioIcons;
+
+ // fill current group
+ MakeActionList(@NewGroupList^);
+ ChMask:=0;
+ CheckGrpList(-1);
+ DontReact:=false;
+ end;
+
+ WM_COMMAND: begin
+ if DontReact then exit;
+ case wParam shr 16 of
+ CBN_EDITCHANGE: begin
+ ChMask:=ChMask or ACTM_ACT or ACTM_ACTS;
+ SetChanged(Dialog,etACT);
+ end;
+ EN_CHANGE: begin
+// check for group renaming
+ if loword(wParam)<>IDC_EDIT_FORMAT then
+ begin
+ ChMask:=ChMask or ACTM_ACT or ACTM_ACTS;
+ SetChanged(Dialog,etACT);
+ end;
+ end;
+ CBN_SELCHANGE: begin
+ ChMask:=ChMask or ACTM_ACT or ACTM_ACTS;
+ SetChanged(Dialog,etACT);
+ case loword(wParam) of
+ IDC_SRV_RESULT: begin
+ i:=CB_GetData(lParam);
+ case i of
+ sresHex,sresInt,sresStruct: begin
+ SHControl(IDC_RES_FREEMEM,SW_HIDE);
+ SHControl(IDC_RES_UNICODE,SW_HIDE);
+ if i=sresInt then
+ SHControl(IDC_RES_SIGNED,SW_SHOW)
+ else
+ SHControl(IDC_RES_SIGNED,SW_HIDE);
+ end;
+ sresString: begin
+ SHControl(IDC_RES_FREEMEM,SW_SHOW);
+ SHControl(IDC_RES_UNICODE,SW_SHOW);
+ SHControl(IDC_RES_SIGNED ,SW_HIDE);
+ end;
+ end;
+ end;
+
+ IDC_ADV_OPER: begin
+ EnableWindow(GetDlgItem(Dialog,IDC_ADV_VAL1),
+ (CB_GetData(lParam)<>lresult(aeNot)));
+// SendMessage(lParam,CB_GETCURSEL,0,0)<>0);
+ end;
+
+ IDC_RW_DATATYPE: begin
+ if CB_GetData(GetDlgItem(Dialog,IDC_RW_DATATYPE))>2 then
+ begin
+ SHControl(IDC_RW_TEXT ,SW_SHOW);
+ SHControl(IDC_RW_VALUE,SW_HIDE);
+ end
+ else
+ begin
+ SHControl(IDC_RW_TEXT ,SW_HIDE);
+ SHControl(IDC_RW_VALUE,SW_SHOW);
+ end;
+ end;
+
+ IDC_ACTION_TYPE: begin
+ i:=SendMessage(lParam,CB_GETCURSEL,0,0);
+ SHWindows(ActIds[i].code);
+ ChangeActionName(-1,i+1);
+ case ActIds[i].code of
+ ACT_CONTACT: EnableWindow(GetDlgItem(Dialog,IDC_CONTACTLIST),true);
+ ACT_RW: begin
+ EnableWindow(GetDlgItem(Dialog,IDC_CONTACTLIST),
+ (IsDlgButtonChecked(Dialog,IDC_RW_MANUAL)=BST_CHECKED));
+
+ if CB_GetData(GetDlgItem(Dialog,IDC_RW_DATATYPE))>2 then
+ begin
+ SHControl(IDC_RW_TEXT ,SW_SHOW);
+ SHControl(IDC_RW_VALUE,SW_HIDE);
+ end
+ else
+ begin
+ SHControl(IDC_RW_TEXT ,SW_HIDE);
+ SHControl(IDC_RW_VALUE,SW_SHOW);
+ end;
+
+ end;
+ ACT_ADVANCE: FillSubList(Dialog);
+ end;
+ end;
+{
+ IDC_EDIT_WPAR,IDC_EDIT_LPAR: begin
+ SendMessage(lParam,CB_GETLBTEXT,
+ SendMessage(lParam,CB_GETCURSEL,0,0),
+ dword(@buf));
+ if loword(wParam)=IDC_EDIT_WPAR then
+ FixParam(buf,IDC_EDIT_WPAR,IDC_FLAG_WPAR)
+ else
+ FixParam(buf,IDC_EDIT_LPAR,IDC_FLAG_LPAR)
+ end;
+}
+ IDC_FLAG_WPAR,IDC_FLAG_LPAR: begin
+ if loword(wParam)=IDC_FLAG_WPAR then
+ begin
+ wnd :=GetDlgItem(Dialog,IDC_EDIT_WPAR);
+ wnd1:=GetDlgItem(Dialog,IDC_WSTRUCT);
+ wnd2:=GetDlgItem(Dialog,IDC_SRV_WPAR);
+ end
+ else
+ begin
+ wnd :=GetDlgItem(Dialog,IDC_EDIT_LPAR);
+ wnd1:=GetDlgItem(Dialog,IDC_LSTRUCT);
+ wnd2:=GetDlgItem(Dialog,IDC_SRV_LPAR);
+ end;
+ i:=CB_GetData(GetDlgItem(Dialog,loword(wParam)));
+
+ if i=ptStruct then
+ begin
+ ShowWindow(wnd ,SW_HIDE);
+ ShowWindow(wnd2,SW_HIDE);
+ ShowWindow(wnd1,SW_SHOW);
+ end
+ else
+ begin
+ ShowWindow(wnd ,SW_SHOW);
+ ShowWindow(wnd2,SW_SHOW);
+ ShowWindow(wnd1,SW_HIDE);
+ if i in [ptCurrent,ptResult,ptParam] then
+ EnableWindow(wnd,false)
+ else
+ begin
+ EnableWindow(wnd,true);
+{
+ flag:=GetWindowLongPtr(wnd,GWL_STYLE);
+ if i=ptNumber then
+ flag:=flag or ES_NUMBER
+ else
+ flag:=flag and not ES_NUMBER;
+ SetWindowLongPtr(wnd,GWL_STYLE,flag);
+}
+ end;
+ end;
+ end;
+ IDC_EDIT_SERVICE: ReloadService;
+ end;
+ end;
+ BN_CLICKED: begin
+ case loword(wParam) of
+ IDC_GROUP_RELOAD, // don't affect to saved (DB) datas
+ IDC_WSTRUCT, // 'Changed' on process
+ IDC_LSTRUCT, // 'Changed' on process
+ IDC_HLP_FVARS,
+ IDC_HLP_VARS,
+ IDC_ADV_HVARS,
+ IDC_ACTION_HELP,
+ IDC_GROUP_EXPORT,
+ IDC_CNT_APPLY,
+ IDC_CNT_FILTER,
+ IDC_GROUP_TEST: ;
+
+ IDC_GROUP_UP,
+ IDC_GROUP_DOWN,
+ IDC_GROUP_NEW : SetChanged(Dialog,etHK);
+
+ IDC_GROUP_DELETE, // action deleting with subactions
+ IDC_GROUP_IMPORT,
+ IDC_ACTION_NEW,
+ IDC_ACTION_DELETE,
+ IDC_ACTION_UP,
+ IDC_ACTION_DOWN: begin
+ ChMask:=ChMask or ACTM_ACT or ACTM_ACTS;
+ SetChanged(Dialog,etHK+etACT);
+ end;
+ else
+ ChMask:=ChMask or ACTM_ACT or ACTM_ACTS;
+ SetChanged(Dialog,etACT);
+ end;
+
+ case loword(wParam) of
+ IDC_SRV_WPAR,
+ IDC_SRV_LPAR,
+ IDC_SRV_SRVC,
+ IDC_PRG_PRG ,
+ IDC_PRG_ARG ,
+ IDC_TXT_FILE,
+ IDC_TXT_TEXT,
+ IDC_RW_MVAR ,
+ IDC_RW_SVAR ,
+ IDC_RW_TVAR ,
+ IDC_MSG_TTL ,
+ IDC_MSG_TXT : SetButtonIcon(lParam,checknames[IsDlgButtonChecked(Dialog,loword(wParam))]);
+ end;
+
+ case loword(wParam) of
+ IDC_CNT_FILTER,
+ IDC_CNT_APPLY: begin
+ if loword(wParam)=IDC_CNT_APPLY then
+ begin
+ mFreeMem(fCLformat);
+ fCLformat:=GetDlgText(Dialog,IDC_EDIT_FORMAT);
+ DBWriteUnicode(0,DBBranch,'CLformat',fCLformat);
+ end
+ else
+ begin
+ fCLfilter:=IsDlgButtonChecked(Dialog,IDC_CNT_FILTER)<>BST_UNCHECKED;
+ DBWriteByte(0,DBBranch,'CLfilter',ord(fCLfilter));
+ end;
+// Saving and restoring contact after list rebuild
+ wnd:=GetDlgItem(Dialog,IDC_CONTACTLIST);
+ i:=SendMessage(wnd,CB_GETITEMDATA,SendMessage(wnd,CB_GETCURSEL,0,0),0);
+
+ FillContactList(wnd,fCLfilter,fCLformat);
+
+ SendMessage(wnd,CB_SETCURSEL,FindContact(wnd,i),0);
+ end;
+ IDC_HLP_FVARS,
+ IDC_HLP_VARS : ShowHelp(hlpVariables);
+ IDC_ADV_HVARS: ShowHelp(hlpAdvVariables);
+
+ IDC_GROUP_EXPORT: begin
+ if ShowDlgW(xmlfilename,xmlfilename,inoutfilter,false) then
+ begin
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_GROUP);
+ for i:=0 to ListView_GetItemCount(wnd)-1 do
+ begin
+ if ListView_GetItemState(wnd,i,LVIS_SELECTED)<>0 then
+ begin
+ with GroupList[LV_GetLParam(wnd,i)] do
+ if (flags and ACF_ASSIGNED)<>0 then // must be always true
+ flags:=flags or ACF_EXPORT;
+ end
+ else
+ with GroupList[i] do
+ if (flags and (ACF_EXPORT or ACF_ASSIGNED))=
+ (ACF_EXPORT or ACF_ASSIGNED) then
+ flags:=flags and not ACF_EXPORT;
+ end;
+ i:=ACIO_EXPORT or ACIO_SELECTED;
+ if GetFSize(xmlfilename)>0 then
+ if MessageBoxW(Dialog,TranslateW('Append data to file'),
+ PluginName,MB_YESNO+MB_ICONWARNING)=IDYES then
+ i:=i or ACIO_APPEND;
+ CallService(MS_ACT_INOUT,i,TLPARAM(@xmlfilename));
+// Export({GetLParam(GetDlgItem(Dialog,IDC_ACTION_GROUP)),}xmlfilename,i);
+ for i:=0 to MaxGroups-1 do
+ with GroupList[i] do
+ if (flags and (ACF_EXPORT or ACF_ASSIGNED))=
+ (ACF_EXPORT or ACF_ASSIGNED) then
+ flags:=flags and not ACF_EXPORT;
+ end;
+ end;
+
+ IDC_GROUP_IMPORT: begin
+ if ShowDlgW(xmlfilename,xmlfilename,inoutfilter) then
+ begin
+// if Import(xmlfilename) then
+ if CallService(MS_ACT_INOUT,0,TLPARAM(@xmlfilename))<>0 then
+ begin
+ ChMask:=ChMask or ACTM_NEW;
+ SendMessage(Dialog,WM_COMMAND,IDC_GROUP_RELOAD+(BN_CLICKED shl 16),
+ GetDlgItem(Dialog,IDC_GROUP_RELOAD));
+//(in reload) FillChainList(Dialog);
+ end;
+ end;
+ end;
+
+ IDC_WSTRUCT, IDC_LSTRUCT: begin
+ if loword(wParam)=IDC_WSTRUCT then
+ pc:=wstruct
+ else
+ pc:=lstruct;
+//!!!!
+ pAnsiChar(j):=EditStructure(pAnsiChar(pc),Dialog);
+ if j<>0 then
+ begin
+ ChMask:=ChMask or ACTM_ACT or ACTM_ACTS;
+ SetChanged(Dialog,etACT);
+ mFreeMem(pAnsiChar(pc));
+ pc:=pAnsiChar(j);
+
+ if loword(wParam)=IDC_WSTRUCT then
+ wstruct:=pc
+ else
+ lstruct:=pc;
+ end;
+ end;
+
+ IDC_PROGRAM: begin
+ FillFileName(IDC_EDIT_PRGPATH);
+ end;
+ IDC_FILE_FILEBTN: begin
+ FillFileName(IDC_FILE_PATH);
+ end;
+
+ IDC_GROUP_TEST: begin
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_LIST);
+ li.mask :=LVIF_PARAM;
+ li.iSubItem:=0;
+ li.iItem :=0;
+ SendMessageW(wnd,LVM_GETITEMW,0,tlparam(@li));
+ j:=SendMessage(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
+
+ ActionStarterWait(li.lParam);
+ // doubling from "reload" button
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_GROUP);
+ i:=SendMessage(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
+ NewGroupList :=GroupList;
+ NewMaxGroups :=MaxGroups;
+ NewActionList:=ActionList;
+ NewMaxActions:=MaxActions;
+ FillGroupList{(Dialog)};
+ FillChainList(Dialog);
+
+ Listview_SetItemState(wnd,0,0,LVIS_FOCUSED or LVIS_SELECTED);
+ Listview_SetItemState(wnd,i,
+ LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+ SendMessage(wnd,LVM_ENSUREVISIBLE,i,0);
+
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_LIST);
+ Listview_SetItemState(wnd,0,0,LVIS_FOCUSED or LVIS_SELECTED);
+ Listview_SetItemState(wnd,j,
+ LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+ SendMessage(wnd,LVM_ENSUREVISIBLE,j,0);
+
+ end;
+ IDC_GROUP_NEW: begin
+ i:=AddGroup(Dialog,NewGroup(NewGroupList,NewMaxGroups));
+ if i>=0 then
+ begin
+ ChMask:=ChMask or ACTM_NEW or ACTM_SORT;
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_GROUP),true);
+ EnableWindow(GetDlgItem(Dialog,IDC_GROUP_DELETE),true);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_NEW ),true);
+ FillChainList(Dialog);
+ CheckGrpList(i);
+ end;
+ end;
+ IDC_GROUP_DELETE: begin
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_GROUP);
+ for i:=ListView_GetItemCount(wnd)-1 downto 0 do
+ begin
+ if ListView_GetItemState(wnd,i,LVIS_SELECTED)<>0 then
+ begin
+ ChMask:=ChMask or ACTM_DELETE or ACTM_SORT;
+
+ with NewGroupList^[LV_GetLParam(wnd,i)] do
+ begin
+ flags:=0;
+ mFreeMem(descr);
+ FreeActions(NewActionList,firstAction);
+ end;
+
+ SendMessage(wnd,LVM_DELETEITEM,i,0);
+ end;
+ end;
+ SendDlgItemMessage(Dialog,IDC_ACTION_LIST,LVM_DELETEALLITEMS,0,0);
+ FillChainList(Dialog);
+ Listview_SetItemState(wnd,0,LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+ CheckGroupList(-1);
+{
+ i:=SendMessageW(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED); //??
+ if i>=0 then
+ begin
+ ChMask:=ChMask or ACTM_DELETE;
+ j:=GetLParam(wnd,i);
+
+ with NewGroupList^[j] do
+ begin
+ flags:=0;
+ mFreeMem(descr);
+ FreeActions(NewActionList,firstAction);
+ end;
+ SendDlgItemMessage(Dialog,IDC_ACTION_LIST,LVM_DELETEALLITEMS,0,0);
+
+//?? i:=ListView_GetNextItem(wnd,-1,LVNI_FOCUSED);
+ SendMessage(wnd,LVM_DELETEITEM,i,0);
+ FillChainList(Dialog);
+ CheckGroupList(i);
+ end;
+}
+ end;
+ IDC_GROUP_RELOAD: begin
+ ChMask:=ChMask or ACTM_RELOAD;
+ SetCancel;
+ NewGroupList :=GroupList;
+ NewMaxGroups :=MaxGroups;
+ NewActionList:=ActionList;
+ NewMaxActions:=MaxActions;
+ FillGroupList{(Dialog)};
+ FillChainList(Dialog);
+ end;
+
+ IDC_ACTION_HELP: begin
+ ShowHelp(SendDlgItemMessage(Dialog,IDC_ACTION_TYPE,CB_GETCURSEL,0,0));
+ end;
+
+ IDC_ACTION_NEW: begin
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_LIST);
+ li.mask :=LVIF_PARAM;
+ i :=SendMessage(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
+ li.iItem :=i+1;
+ li.iSubItem:=0;
+ li.lParam :=NewAction(NewActionList,NewMaxActions);
+ SendMessageW(wnd,LVM_INSERTITEMW,0,tlparam(@li));
+ ListView_SetCheckState(wnd,li.iItem,true);
+ if li.iItem=0 then
+ begin
+ ListView_SetItemState(wnd,0,LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+ SHActButtons(SW_SHOW);
+ end;
+ ChangeActionName(li.iItem);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_LIST ),true);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_HELP ),true);
+ EnableWindow(GetDlgItem(Dialog,IDC_GROUP_TEST ),true);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_DELETE),true);
+ EnableWindow(GetDlgItem(Dialog,IDC_ACTION_TYPE ),true);
+ CheckActList(i);
+
+ wnd:=GetDlgItem(Dialog,IDC_ADV_VAL2);
+ i:=SendMessage(wnd,CB_GETCURSEL,0,0);
+ FillSubList(Dialog);
+ SendMessage(wnd,CB_SETCURSEL,i,0);
+ end;
+ IDC_ACTION_DELETE: begin
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_LIST);
+ for i:=ListView_GetItemCount(wnd)-1 downto 0 do
+ begin
+ if ListView_GetItemState(wnd,i,LVIS_SELECTED)<>0 then
+ begin
+ FreeAction(@NewActionList^[LV_GetLParam(wnd,i)]);
+ SendMessage(wnd,LVM_DELETEITEM,i,0);
+ end;
+ end;
+ Listview_SetItemState(wnd,0,LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+ CheckActionList(0);
+{
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_LIST);
+ li.iItem:=SendMessage(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED); //??
+ if li.iItem>=0 then
+ begin
+ li.mask :=LVIF_PARAM;
+ li.iSubItem:=0;
+ SendMessageW(wnd,LVM_GETITEM,0,dword(@li));
+ FreeAction(@NewActionList^[li.lParam]);
+
+ SendMessage(wnd,LVM_DELETEITEM,li.iItem,0);
+ CheckActionList(li.iItem);
+ end;
+}
+ end;
+
+ IDC_GROUP_UP: begin
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_GROUP);
+// i:=SendMessage(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
+// if i>0 then
+ begin
+ CheckGrpList(MoveGroup(wnd,-1,-1));
+ ChMask:=ChMask or ACTM_SORT;
+ end;
+ end;
+ IDC_GROUP_DOWN: begin
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_GROUP);
+// i:=SendMessage(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
+// if i<(SendMessage(wnd,LVM_GETITEMCOUNT,0,0)-1) then
+ begin
+ CheckGrpList(MoveGroup(wnd,-1,1));
+ ChMask:=ChMask or ACTM_SORT;
+ end;
+ end;
+
+ IDC_ACTION_UP: begin
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_LIST);
+// i:=SendMessage(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
+// if i>0 then
+ CheckActList(MoveGroup(wnd,-1,-1));
+ end;
+ IDC_ACTION_DOWN: begin
+ wnd:=GetDlgItem(Dialog,IDC_ACTION_LIST);
+// i:=SendMessage(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
+// if i<(SendMessage(wnd,LVM_GETITEMCOUNT,0,0)-1) then
+ CheckActList(MoveGroup(wnd,-1,1));
+ end;
+
+ IDC_FILE_WRITE, IDC_FILE_READ, IDC_FILE_APPEND:
+ EnableWindow(GetDlgItem(Dialog,IDC_EDIT_INSERT),
+ loword(wParam)<>IDC_FILE_APPEND);
+
+ IDC_RW_DELETE,
+ IDC_RW_READ,
+ IDC_RW_WRITE: begin
+ b:=loword(wParam)<>IDC_RW_DELETE;
+ EnableWindow(GetDlgItem(Dialog,IDC_RW_DATATYPE),b);
+ EnableWindow(GetDlgItem(Dialog,IDC_RW_LAST ),b);
+ EnableWindow(GetDlgItem(Dialog,IDC_RW_VALUE ),b);
+ EnableWindow(GetDlgItem(Dialog,IDC_RW_TEXT ),b);
+ end;
+
+ IDC_FLAG_CLIP,IDC_FLAG_FILE,IDC_FLAG_MESSAGE: begin
+ b:=loword(wParam)=IDC_FLAG_CLIP;
+//!! +text read = disabled
+ EnableWindow(GetDlgItem(Dialog,IDC_CLIP_COPYTO),b);
+ EnableWindow(GetDlgItem(Dialog,IDC_CLIP_PASTE ),b);
+ b:=b or ((loword(wParam)=IDC_FLAG_FILE) and
+ (IsDlgButtonChecked(Dialog,IDC_FILE_READ)<>BST_UNCHECKED));
+ EnableWindow(GetDlgItem(Dialog,IDC_EDIT_INSERT),not b);
+
+ b:=loword(wParam)=IDC_FLAG_FILE;
+ EnableWindow(GetDlgItem(Dialog,IDC_FILE_ENC ),b);
+ EnableWindow(GetDlgItem(Dialog,IDC_FILE_PATH ),b);
+ EnableWindow(GetDlgItem(Dialog,IDC_FILE_FILEBTN),b);
+ EnableWindow(GetDlgItem(Dialog,IDC_FILE_READ ),b);
+ EnableWindow(GetDlgItem(Dialog,IDC_FILE_WRITE ),b);
+ EnableWindow(GetDlgItem(Dialog,IDC_FILE_APPEND ),b);
+ end;
+
+ IDC_FLAG_JUMP: begin
+ EnableWindow(GetDlgItem(Dialog,IDC_ADV_VAL2),true);
+ end;
+
+ IDC_FLAG_BREAK,IDC_FLAG_ANOP:
+ begin
+ EnableWindow(GetDlgItem(Dialog,IDC_ADV_VAL2),false);
+ end;
+
+ IDC_FLAG_VARS: begin
+ if IsDlgButtonChecked(Dialog,IDC_FLAG_VARS)<>BST_UNCHECKED then
+ begin
+ SHMath(false);
+ CheckDlgButton(Dialog,IDC_FLAG_MATH,BST_UNCHECKED);
+ end
+ else
+ begin
+ EnableWindow(GetDlgItem(Dialog,IDC_ADV_ASINT),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_ADV_VARS ),false);
+ end;
+ end;
+ IDC_FLAG_MATH: begin
+ if IsDlgButtonChecked(Dialog,IDC_FLAG_MATH)<>BST_UNCHECKED then
+ begin
+ SHMath(true);
+ CheckDlgButton(Dialog,IDC_FLAG_VARS,BST_UNCHECKED);
+ end
+ else
+ begin
+ EnableWindow(GetDlgItem(Dialog,IDC_ADV_OPER),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_ADV_VAL1),false);
+ end;
+ end;
+
+ IDC_RW_CURRENT, IDC_RW_RESULT, IDC_RW_PARAM: begin
+ EnableWindow(GetDlgItem(Dialog,IDC_CONTACTLIST),false);
+ end;
+ IDC_RW_MANUAL: EnableWindow(GetDlgItem(Dialog,IDC_CONTACTLIST),true);
+
+ IDC_RW_LAST: begin
+ b :=IsDlgButtonChecked(Dialog,IDC_RW_LAST )=BST_UNCHECKED;
+ EnableWindow(GetDlgItem(Dialog,IDC_RW_VALUE), b);
+ EnableWindow(GetDlgItem(Dialog,IDC_RW_TEXT ), b);
+ end;
+ end;
+ end;
+ end;
+ end;
+
+ WM_HELP: begin
+ if (PHELPINFO(lParam)^.iContextType=HELPINFO_WINDOW) then
+ ShowHelp(SendDlgItemMessage(Dialog,IDC_ACTION_TYPE,CB_GETCURSEL,0,0));
+ end;
+
+ WM_NOTIFY: begin
+ case integer(PNMHdr(lParam)^.code) of
+ PSN_APPLY: begin
+ BuildActionChain;
+ SetSave(Dialog,LV_GetLParam(GetDlgItem(Dialog,IDC_ACTION_GROUP)));
+
+ if ActionList<>NewActionList then
+ begin
+ DestroyActions(ActionList,MaxActions);
+ ActionList:=NewActionList;
+ MaxActions:=NewMaxActions;
+ end;
+
+ SaveGroups;
+
+ if ChMask<>0 then //??
+ begin
+ NotifyEventHooks(hHookChanged,ChMask,0);
+ ChMask:=0;
+ end;
+
+ end;
+
+ NM_DBLCLK: begin
+ if PNMListView(lParam)^.iItem>=0 then
+ PostMessageW(PNMHdr(lParam)^.hWndFrom,LVM_EDITLABELW,
+ PNMListView(lParam)^.iItem,0);
+ end;
+
+ LVN_ITEMCHANGED: begin
+ if DontReact then exit; // bug when group moved avoid
+
+ if wParam=IDC_ACTION_GROUP then
+ begin
+ if PNMLISTVIEW(lParam)^.uChanged=LVIF_STATE then
+ begin
+ lvflag:=(PNMLISTVIEW(lParam)^.uOldState and LVNI_FOCUSED)-
+ (PNMLISTVIEW(lParam)^.uNewState and LVNI_FOCUSED);
+
+ if lvflag>0 then // old focus
+ BuildActionChain(PNMLISTVIEW(lParam)^.iItem)
+ else if lvflag<0 then // new focus
+ begin
+ DontReact:=true;
+
+ ClearDialogData;
+ MakeActionList(@NewGroupList^[PNMLISTVIEW(lParam)^.lParam]);
+ CheckGrpList(PNMLISTVIEW(lParam)^.iItem);
+
+ DontReact:=false;
+ end
+ else
+ begin
+ lvflag:=(PNMLISTVIEW(lParam)^.uOldState and LVNI_SELECTED)-
+ (PNMLISTVIEW(lParam)^.uNewState and LVNI_SELECTED);
+ if lvflag<>0 then
+ CheckGrpList(PNMLISTVIEW(lParam)^.iItem);
+ end;
+ end;
+ end
+ else if wParam=IDC_ACTION_LIST then
+ begin
+ lvflag:=(PNMLISTVIEW(lParam)^.uOldState and LVNI_FOCUSED)-
+ (PNMLISTVIEW(lParam)^.uNewState and LVNI_FOCUSED);
+ if lvflag>0 then // old focus
+ SaveAction(PNMLISTVIEW(lParam)^.iItem,
+ PNMLISTVIEW(lParam)^.lParam)
+ else if lvflag<0 then // new focus
+ begin
+ DontReact:=true;
+
+ ClearDialogData;
+ j:=PNMLISTVIEW(lParam)^.lParam;
+ FillAction(j);
+ CheckActList(PNMLISTVIEW(lParam)^.iItem);
+
+ DontReact:=false;
+ end
+ else
+ begin // checkboxes
+ lvflag:=(PNMLISTVIEW(lParam)^.uOldState and LVNI_SELECTED)-
+ (PNMLISTVIEW(lParam)^.uNewState and LVNI_SELECTED);
+ if lvflag<>0 then
+ CheckGrpList(PNMLISTVIEW(lParam)^.iItem);
+
+ if (PNMLISTVIEW(lParam)^.uOldState or PNMLISTVIEW(lParam)^.uNewState)=$3000 then
+ begin
+ if PNMLISTVIEW(lParam)^.uOldState=$1000 then
+ i:=0
+ else
+ i:=ACF_DISABLED;
+ j:=PNMLISTVIEW(lParam)^.lParam;
+ NewActionList^[j].flags:=(NewActionList^[j].flags and not ACF_DISABLED) or dword(i);
+ ChMask:=ChMask or ACTM_ACT or ACTM_ACTS;
+ SetChanged(Dialog,etACT);
+ end;
+ end;
+ end;
+ end;
+
+ LVN_ENDLABELEDITW: begin
+ if DontReact then exit;
+ if wParam=IDC_ACTION_GROUP then
+ begin
+ with PLVDISPINFOW(lParam)^ do
+ begin
+ if item.pszText<>nil then
+ begin
+ ChMask:=ChMask or ACTM_RENAME;
+ SetChanged(Dialog,etHK);
+ item.mask:=LVIF_TEXT;
+ if pWideChar(item.pszText)^=#0 then
+ pWideChar(item.pszText):=NoDescription;
+ SendMessageW(hdr.hWndFrom,LVM_SETITEMW,0,tlparam(@item));
+
+ with NewGroupList^[item.lParam] do
+ begin
+ mFreeMem(descr);
+ StrDupW(descr,item.pszText);
+ end;
+
+ FillChainList(Dialog);
+ result:=1;
+ end;
+ end;
+ end
+ else if wParam=IDC_ACTION_LIST then
+ begin
+ with PLVDISPINFOW(lParam)^ do
+ begin
+ if item.pszText<>nil then
+ begin
+ ChMask:=ChMask or ACTM_ACT or ACTM_ACTS;
+ SetChanged(Dialog,etACT);
+ ChangeActionName(item.iItem,0,pWideChar(item.pszText));
+ result:=1;
+ end;
+//??
+ end;
+ end;
+ end;
+
+ end;
+ end;
+ else
+// {result:=}DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+// {result:=}DefWindowProc(Dialog,hMessage,wParam,lParam);
+end;
diff --git a/plugins/Actman/i_options.inc b/plugins/Actman/i_options.inc
new file mode 100644
index 0000000000..64a6b856cc
--- /dev/null
+++ b/plugins/Actman/i_options.inc
@@ -0,0 +1,459 @@
+{Save/load options}
+
+const
+ opt_group = 'Group';
+ opt_actions = 'Action';
+ opt_numacts = 'numactions';
+ opt_numhk = 'numgroups';
+ opt_firstAction = 'firstaction';
+
+ opt_cproto = 'cproto';
+ opt_cuid = 'cuid';
+ opt_ischat = 'ischat';
+
+ opt_descr = 'descr';
+ opt_id = 'id';
+ opt_flags = 'flags';
+ opt_flags2 = 'flags2';
+ opt_time = 'time';
+ opt_show = 'show';
+ opt_action = 'action';
+ opt_value = 'value';
+ opt_file = 'file';
+
+ opt_next = 'next';
+ opt_type = 'type';
+ opt_contact = 'contact';
+ opt_text = 'text';
+ opt_prg = 'program';
+ opt_args = 'arguments';
+ opt_service = 'service';
+ opt_wparam = 'wparam';
+ opt_wlparam = 'wparamlen';
+ opt_llparam = 'lparamlen';
+ opt_lparam = 'lparam';
+ opt_chain = 'chain';
+ opt_cond = 'condition';
+ opt_count = 'count';
+ opt_module = 'module';
+ opt_setting = 'setting';
+ opt_oper = 'operation';
+ opt_mathval = 'mathval';
+ opt_operval = 'operval';
+ opt_varval = 'varval';
+ opt_msgtitle = 'msgtitle';
+ opt_msgtext = 'msgtext';
+ opt_boxopts = 'boxopts';
+
+
+//----- Save settings -----
+
+procedure SaveNumValue(setting:pAnsiChar;value:uint_ptr;isvar:boolean);
+begin
+ if isvar then
+ DBWriteUnicode(0,DBBranch,setting,pWideChar(value))
+ else
+ DBWriteDWord (0,DBBranch,setting,value);
+end;
+
+function SaveActions(section:pAnsiChar;first:integer):integer;
+var
+ p,p1:PAnsiChar;
+ act:pHKAction;
+ i:integer;
+begin
+ result:=0;
+ // in: section = "Group#/"
+ p1:=StrCopyE(StrEnd(section),opt_actions); // "Group#/Action"
+ DBDeleteGroup(0,DBBranch,section);
+ i:=1;
+ while first<>0 do
+ begin
+ act:=@ActionList[first];
+ p:=StrEnd(IntToStr(p1,i)); //!!!
+ p^:='/'; inc(p); // "Group#/Action#/"
+
+ StrCopy(p,opt_flags ); DBWriteDWord(0,DBBranch,section,act^.flags);
+ StrCopy(p,opt_flags2); DBWriteDWord(0,DBBranch,section,act^.flags2);
+ StrCopy(p,opt_type ); DBWriteByte (0,DBBranch,section,act^.actionType);
+ if act^.descr<>nil then
+ begin
+ StrCopy(p,opt_descr); DBWriteUnicode(0,DBBranch,section,act^.descr);
+ end;
+
+ case act^.actionType of
+ ACT_CONTACT: begin
+ p^:=#0;
+ SaveContact(act^.contact,DBBranch,section);
+ end;
+
+ ACT_SERVICE: begin
+ StrCopy(p,opt_service); DBWriteString(0,DBBranch,section,act^.service);
+
+ if (act^.flags and (ACF_WCURRENT or ACF_WRESULT or ACF_WPARAM))=0 then
+ begin
+ StrCopy(p,opt_wparam);
+ if (act^.flags and ACF_WPARNUM)<>0 then
+ SaveNumValue(section,act^.wparam,(act^.flags2 and ACF2_SRV_WPAR)<>0)
+// DBWriteDWord(0,DBBranch,section,act^.wparam)
+ else if act^.wparam<>0 then
+ begin
+ if (act^.flags and ACF_WSTRUCT)<>0 then
+ DBWriteUTF8(0,DBBranch,section,pAnsiChar(act^.wparam))
+ else if (act^.flags and ACF_WUNICODE)<>0 then
+ DBWriteUnicode(0,DBBranch,section,pWideChar(act^.wparam))
+ else
+ DBWriteString(0,DBBranch,section,PAnsiChar(act^.wparam));
+ end;
+ end;
+
+ if (act^.flags and (ACF_LCURRENT or ACF_LRESULT or ACF_LPARAM))=0 then
+ begin
+ StrCopy(p,opt_lparam);
+ if (act^.flags and ACF_LPARNUM)<>0 then
+ SaveNumValue(section,act^.lparam,(act^.flags2 and ACF2_SRV_LPAR)<>0)
+// DBWriteDWord(0,DBBranch,section,act^.lparam)
+ else if act^.lparam<>0 then
+ begin
+ if (act^.flags and ACF_LSTRUCT)<>0 then
+ DBWriteUTF8(0,DBBranch,section,pAnsiChar(act^.lparam))
+ else if (act^.flags and ACF_LUNICODE)<>0 then
+ DBWriteUnicode(0,DBBranch,section,pWideChar(act^.lparam))
+ else
+ DBWriteString(0,DBBranch,section,PAnsiChar(act^.lparam));
+ end;
+ end;
+
+ end;
+
+ ACT_PROGRAM: begin
+ StrCopy(p,opt_prg ); DBWriteUnicode(0,DBBranch,section,act^.prgname);
+ StrCopy(p,opt_args); DBWriteUnicode(0,DBBranch,section,act^.args);
+ StrCopy(p,opt_time); DBWriteDWord (0,DBBranch,section,act^.time);
+ StrCopy(p,opt_show); DBWriteDWord (0,DBBranch,section,act^.show);
+ end;
+
+ ACT_TEXT: begin
+ if (act^.flags and ACF_CLIPBRD)=0 then
+ begin
+ StrCopy(p,opt_text); DBWriteUnicode(0,DBBranch,section,act^.text);
+ if (act^.flags and ACF_FILE)<>0 then
+ begin
+ StrCopy(p,opt_file); DBWriteUnicode(0,DBBranch,section,act^.tfile);
+ end;
+ end;
+ end;
+
+ ACT_ADVANCE: begin
+ StrCopy(p,opt_cond ); DBWriteByte (0,DBBranch,section,act^.condition);
+ StrCopy(p,opt_value ); DBWriteDWord (0,DBBranch,section,act^.value);
+ StrCopy(p,opt_action ); DBWriteByte (0,DBBranch,section,act^.action);
+ StrCopy(p,opt_operval); DBWriteUnicode(0,DBBranch,section,act^.operval);
+ StrCopy(p,opt_oper ); DBWriteByte (0,DBBranch,section,act^.oper);
+ StrCopy(p,opt_mathval); DBWriteDWord (0,DBBranch,section,act^.mathval);
+ StrCopy(p,opt_varval ); DBWriteUnicode(0,DBBranch,section,act^.varval);
+ end;
+
+ ACT_CHAIN: begin
+ StrCopy(p,opt_text); DBWriteDWord(0,DBBranch,section,act^.id);
+ end;
+
+ ACT_RW: begin
+ if (act^.flags and ACF_NOCNTCT)=0 then
+ begin
+ p^:=#0;
+ SaveContact(act^.dbcontact,DBBranch,section);
+ end;
+ StrCopy(p,opt_module ); DBWriteString(0,DBBranch,section,act^.dbmodule);
+ StrCopy(p,opt_setting); DBWriteString(0,DBBranch,section,act^.dbsetting);
+ StrCopy(p,opt_value );
+ if (act^.flags and ACF_DBUTEXT)=0 then
+ begin
+ SaveNumValue(section,act^.dbvalue,(act^.flags2 and ACF2_RW_TVAR)<>0);
+// DBWriteDWord(0,DBBranch,section,act^.dbvalue)
+ end
+ else
+ DBWriteUnicode(0,DBBranch,section,pWideChar(act^.dbvalue));
+ end;
+
+ ACT_MESSAGE: begin
+ StrCopy(p,opt_msgtitle); DBWriteUnicode(0,DBBranch,section,act^.msgtitle);
+ StrCopy(p,opt_msgtext ); DBWriteUnicode(0,DBBranch,section,act^.msgtext);
+ StrCopy(p,opt_boxopts ); DBWriteByte (0,DBBranch,section,act^.boxopts); //!!
+ end;
+
+ end;
+ inc(result);
+ inc(i);
+ first:=ActionList^[first].next;
+ end;
+end;
+
+procedure SaveGroups;
+var
+ HK:pHKRecord;
+ NumHK:integer;
+ i,num:integer;
+ section:array [0..127] of AnsiChar;
+ p,p1:PAnsiChar;
+ Actions:integer;
+begin
+// even if crap in settings, skip on read
+// DBDeleteGroup(0,DBBranch,opt_group);
+ HK:=@GroupList^;
+ i:=MaxGroups;
+ NumHK:=0;
+ Actions:=1;
+ DBWriteUnicode(0,DBBranch,'CLformat',fCLformat);
+ DBWriteByte (0,DBBranch,'CLfilter',ord(fCLfilter));
+
+ p1:=StrCopyE(section,opt_group);
+ while i>0 do
+ begin
+ with HK^ do
+ begin
+ if (flags and (ACF_ASSIGNED or ACF_VOLATILE))=ACF_ASSIGNED then
+ begin
+ p:=StrEnd(IntToStr(p1,NumHK));
+ p^:='/'; inc(p);
+
+ StrCopy(p,opt_id ); DBWriteDWord(0,DBBranch,section,id);
+ StrCopy(p,opt_flags); DBWriteDWord(0,DBBranch,section,flags);
+ StrCopy(p,opt_descr);
+ if descr<>nil then
+ DBWriteUnicode (0,DBBranch,section,descr)
+ else
+ DBDeleteSetting(0,DBBranch,section);
+
+ p^:=#0;
+ //??
+ num:=SaveActions(section,firstAction);
+ StrCopy(p,opt_numacts); DBWriteWord(0,DBBranch,section,num);
+
+ inc(Actions,num);
+ inc(NumHK);
+ end;
+ end;
+ inc(HK);
+ dec(i);
+ end;
+ DBWriteWord(0,DBBranch,opt_numhk ,NumHK);
+ DBWriteWord(0,DBBranch,opt_numacts,Actions-1);
+end;
+
+//----- Load settings -----
+
+function LoadNumValue(setting:pAnsiChar;isvar:boolean):uint_ptr;
+begin
+ if isvar then
+ result:=uint_ptr(DBReadUnicode(0,DBBranch,setting,nil))
+ else
+ result:=DBReadDWord(0,DBBranch,setting);
+end;
+
+function LoadActions(section:pAnsiChar;count:integer):integer;
+var
+ p,p1:PAnsiChar;
+ act:tHKAction;
+ i,num,oldnum:integer;
+begin
+ result:=0;
+ p1:=StrCopyE(StrEnd(section),opt_actions); // "Group#/Action"
+
+ oldnum:=0;
+ for i:=1 to count do
+ begin
+ p:=StrEnd(IntToStr(p1,i));
+ p^:='/'; inc(p); // "Group#/Action#/"
+ FillChar(act,SizeOf(act),0);
+
+ StrCopy(p,opt_flags ); act.flags :=DBReadDWord (0,DBBranch,section,0);
+ if (act.flags and ACF_ASSIGNED)<>0 then
+ begin
+ StrCopy(p,opt_flags2); act.flags2 :=DBReadDWord (0,DBBranch,section,0);
+ StrCopy(p,opt_descr ); act.descr :=DBReadUnicode(0,DBBranch,section,nil);
+ StrCopy(p,opt_type ); act.actionType:=DBReadByte (0,DBBranch,section,ACT_CONTACT);
+
+ case act.actionType of
+ ACT_CONTACT: begin
+ p^:=#0;
+ act.contact:=LoadContact(DBBranch,section);
+ end;
+
+ ACT_SERVICE: begin
+ StrCopy(p,opt_service);
+ act.service:=DBReadString(0,DBBranch,section,nil);
+
+ if (act.flags and (ACF_WCURRENT or ACF_WRESULT or ACF_WPARAM))=0 then
+ begin
+ StrCopy(p,opt_wparam);
+ if (act.flags and ACF_WPARNUM)<>0 then
+ act.wparam:=LoadNumValue(section,(act.flags2 and ACF2_SRV_WPAR)<>0)
+ else if (act.flags and ACF_WSTRUCT)<>0 then
+ act.wparam:=wparam(DBReadUTF8(0,DBBranch,section,nil))
+ else if (act.flags and ACF_WUNICODE)<>0 then
+ act.wparam:=wparam(DBReadUnicode(0,DBBranch,section,nil))
+ else
+ act.wparam:=wparam(DBReadString (0,DBBranch,section,nil));
+ end;
+
+ if (act.flags and (ACF_LCURRENT or ACF_LRESULT or ACF_LPARAM))=0 then
+ begin
+ StrCopy(p,opt_lparam);
+ if (act.flags and ACF_LPARNUM)<>0 then
+ act.lparam:=LoadNumValue(section,(act.flags2 and ACF2_SRV_LPAR)<>0)
+ // act.lparam:=DBReadDWord(0,DBBranch,section,0)
+ else if (act.flags and ACF_LSTRUCT)<>0 then
+ act.lparam:=lparam(DBReadUTF8(0,DBBranch,section,nil))
+ else if (act.flags and ACF_LUNICODE)<>0 then
+ act.lparam:=lparam(DBReadUnicode(0,DBBranch,section,nil))
+ else
+ act.lparam:=lparam(DBReadString(0,DBBranch,section,nil));
+ end;
+
+ end;
+
+ ACT_PROGRAM: begin
+ StrCopy(p,opt_prg ); act.prgname:=DBReadUnicode(0,DBBranch,section,nil);
+ StrCopy(p,opt_args); act.args :=DBReadUnicode(0,DBBranch,section,nil);
+ StrCopy(p,opt_time); act.time :=DBReadDWord (0,DBBranch,section,0);
+ StrCopy(p,opt_show); act.show :=DBReadDWord (0,DBBranch,section,SW_SHOW);
+ end;
+
+ ACT_TEXT: begin
+ if (act.flags and ACF_CLIPBRD)=0 then
+ begin
+ StrCopy(p,opt_text); act.text:=DBReadUnicode(0,DBBranch,section,nil);
+ if (act.flags and ACF_FILE)<>0 then
+ begin
+ StrCopy(p,opt_file); act.tfile:=DBReadUnicode(0,DBBranch,section,nil);
+ end;
+ end;
+ end;
+
+ ACT_ADVANCE: begin
+ StrCopy(p,opt_cond ); act.condition:=DBReadByte (0,DBBranch,section);
+ StrCopy(p,opt_value ); act.value :=DBReadDWord (0,DBBranch,section);
+ StrCopy(p,opt_action ); act.action :=DBReadByte (0,DBBranch,section);
+ StrCopy(p,opt_oper ); act.oper :=DBReadByte (0,DBBranch,section);
+ StrCopy(p,opt_mathval); act.mathval :=DBReadDWord (0,DBBranch,section);
+ StrCopy(p,opt_operval); act.operval :=DBReadUnicode(0,DBBranch,section);
+ StrCopy(p,opt_varval ); act.varval :=DBReadUnicode(0,DBBranch,section);
+ end;
+
+ ACT_CHAIN: begin
+ StrCopy(p,opt_text); act.id:=DBReadDWord(0,DBBranch,section);
+ end;
+
+ ACT_RW: begin
+ if (act.flags and ACF_NOCNTCT)=0 then
+ begin
+ p^:=#0;
+ act.dbcontact:=LoadContact(DBBranch,section);
+ end;
+ StrCopy(p,opt_module ); act.dbmodule :=DBReadString(0,DBBranch,section);
+ StrCopy(p,opt_setting); act.dbsetting:=DBReadString(0,DBBranch,section);
+ StrCopy(p,opt_value );
+
+ if (act.flags and ACF_DBUTEXT)=0 then
+ act.dbvalue:=LoadNumValue(section,(act.flags2 and ACF2_RW_TVAR)<>0)
+ else
+ act.dbvalue:=uint_ptr(DBReadUnicode(0,DBBranch,section));
+ end;
+
+ ACT_MESSAGE: begin
+ StrCopy(p,opt_msgtitle); act.msgtitle:=DBReadUnicode(0,DBBranch,section);
+ StrCopy(p,opt_msgtext ); act.msgtext :=DBReadUnicode(0,DBBranch,section);
+ StrCopy(p,opt_boxopts ); act.boxopts :=DBReadByte (0,DBBranch,section);
+ end;
+
+ end;
+ num:=NewAction(ActionList,MaxActions);
+ move(act,ActionList^[num],SizeOf(tHKAction));
+ if i=1 then
+ result:=num
+ else
+ ActionList^[oldnum].next:=num;
+ oldnum:=num;
+ end;
+ end;
+end;
+
+procedure LoadGroups;
+var
+ HK:pHKRecord;
+ i,num:cardinal;
+ p,p1:PAnsiChar;
+ section:array [0..127] of AnsiChar;
+ NumGroups,NumActions:cardinal;
+begin
+{ remove doubling - no need? (called just once)
+ if MaxGroups>0 then
+ begin
+ while MaxGroups>0 do
+ begin
+ FreeGroup(MaxGroups);
+ dec(MaxGroups);
+ end;
+ FreeMem(GroupList);
+ end;
+}
+ NumGroups:=DBReadWord(0,DBBranch,opt_numhk,HKListPage);
+ if NumGroups<HKListPage then
+ MaxGroups:=HKListPage
+ else
+ MaxGroups:=NumGroups;
+ GetMem (GroupList ,MaxGroups*SizeOf(tHKRecord));
+ FillChar(GroupList^,MaxGroups*SizeOf(tHKRecord),0);
+
+{ remove doubling - no need? (called just once)
+ if MaxActions<>0 then
+ begin
+ act:=@ActionList[1];
+ while MaxActions>0 do
+ begin
+ FreeAction(act);
+ inc(act);
+ dec(MaxActions);
+ end;
+ FreeMem(ActionList);
+ end;
+}
+ NumActions:=DBReadWord(0,DBBranch,opt_numacts,ActListPage);
+ if NumActions<ActListPage then
+ MaxActions:=ActListPage
+ else
+ MaxActions:=NumActions+1;
+ GetMem (ActionList ,MaxActions*SizeOf(tHKAction));
+ FillChar(ActionList^,MaxActions*SizeOf(tHKAction),0);
+
+ HK:=@GroupList^; //??
+ i:=0;
+ p1:=StrCopyE(section,opt_group);
+ while i<NumGroups do
+ begin
+ p:=StrEnd(IntToStr(p1,i));
+ p^:='/'; inc(p);
+
+ StrCopy(p,opt_flags);
+ with HK^ do
+ begin
+ flags:=DBReadDWord(0,DBBranch,section,0{integer(ACF_ASSIGNED or ACF_DISABLED)});
+ if (flags and ACF_ASSIGNED)<>0 then // not needed in normal cases
+ begin
+ StrCopy(p,opt_id ); id :=DBReadDWord (0,DBBranch,section);
+ StrCopy(p,opt_descr); descr:=DBReadUnicode(0,DBBranch,section,nil);
+ if descr=nil then
+ StrDupW(descr,TranslateW('No Description'));
+
+ StrCopy(p,opt_numacts); num:=DBReadWord(0,DBBranch,section);
+ p^:=#0;
+ firstAction:=LoadActions(section,num);
+ end;
+ end;
+ inc(HK);
+ inc(i);
+ end;
+ fCLfilter:=DBReadByte (0,DBBranch,'CLfilter',0)<>0;
+ fCLformat:=DBReadUnicode(0,DBBranch,'CLformat');
+end;
diff --git a/plugins/Actman/i_services.inc b/plugins/Actman/i_services.inc
new file mode 100644
index 0000000000..d835c9225a
--- /dev/null
+++ b/plugins/Actman/i_services.inc
@@ -0,0 +1,131 @@
+{Basic ActMan services}
+
+function ActFreeList(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+begin
+ result:=0;
+ mFreeMem(PAnsiChar(lParam));
+end;
+
+function ActGetList(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+var
+ pc:^tChain;
+ p:pHKRecord;
+ i,cnt:integer;
+begin
+ p:=@GroupList[0];
+ cnt:=0;
+ for i:=0 to MaxGroups-1 do
+ begin
+ if (p^.flags and (ACF_ASSIGNED or ACF_VOLATILE))=ACF_ASSIGNED then inc(cnt);
+ inc(p);
+ end;
+ result:=cnt;
+ if lParam=0 then exit;
+ if cnt>0 then
+ begin
+ mGetMem(pc,cnt*SizeOf(tChain)+4);
+ puint_ptr(lParam)^:=uint_ptr(pc);
+// {$IFDEF WIN64}pqword{$ELSE}pdword{$ENDIF}(lParam)^:=uint_ptr(pc);
+ pdword(pc)^:=SizeOf(tChain);
+ inc(PByte(pc),4);
+
+ p:=@GroupList[0];
+ for i:=0 to MaxGroups-1 do
+ begin
+ if (p^.flags and (ACF_ASSIGNED or ACF_VOLATILE))=ACF_ASSIGNED then
+ begin
+ pc^.descr:=p^.descr;
+ pc^.id :=p^.id;
+ pc^.flags:=p^.flags;
+ inc(pc);
+ end;
+ inc(p);
+ end;
+ end
+ else
+ puint_ptr(lParam)^:=0;
+// {$IFDEF WIN64}pqword{$ELSE}pdword{$ENDIF}(lParam)^:=0;
+end;
+
+function ActRun(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+var
+ i:integer;
+ p:pHKRecord;
+begin
+ result:=-1;
+ p:=@GroupList[0];
+ for i:=0 to MaxGroups-1 do
+ begin
+ if ((p^.flags and ACF_ASSIGNED)<>0) and (p^.id=dword(wParam)) then
+ begin
+ result:=p^.firstAction;
+ break;
+ end;
+ inc(p);
+ end;
+ if result>0 then
+ result:=ActionStarter(result,lParam,p);
+end;
+
+function ActRunGroup(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+var
+ i:integer;
+ p:pHKRecord;
+begin
+ result:=-1;
+ p:=@GroupList[0];
+ for i:=0 to MaxGroups-1 do
+ begin
+ if ((p^.flags and ACF_ASSIGNED)<>0) and (StrCmpW(p^.descr,pWideChar(wParam))=0) then
+ begin
+ result:=p^.firstAction;
+ break;
+ end;
+ inc(p);
+ end;
+ if result>0 then
+ result:=ActionStarter(result,lParam,p);
+end;
+
+function ActRunParam(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+var
+ i:integer;
+ p:pHKRecord;
+begin
+ result:=-1;
+ p:=@GroupList[0];
+
+ if (pAct_Param(lParam)^.flags and ACTP_BYNAME)=0 then
+ begin
+ for i:=0 to MaxGroups-1 do
+ begin
+ if ((p^.flags and ACF_ASSIGNED)<>0) and (p^.id=pAct_Param(lParam)^.Id) then
+ begin
+ result:=p^.firstAction;
+ break;
+ end;
+ inc(p);
+ end;
+ end
+ else
+ begin
+ for i:=0 to MaxGroups-1 do
+ begin
+ if ((p^.flags and ACF_ASSIGNED)<>0) and
+ (StrCmpW(p^.descr,pWideChar(pAct_Param(lParam)^.Id))=0) then
+ begin
+ result:=p^.firstAction;
+ break;
+ end;
+ inc(p);
+ end;
+ end;
+
+ if result>0 then
+ begin
+ if (pAct_Param(lParam)^.flags and ACTP_WAIT)=0 then
+ result:=ActionStarter (result,pAct_Param(lParam)^.wParam,p,pAct_Param(lParam)^.lParam)
+ else
+ result:=ActionStarterWait(result,pAct_Param(lParam)^.wParam,p,pAct_Param(lParam)^.lParam);
+ end;
+end;
diff --git a/plugins/Actman/i_vars.inc b/plugins/Actman/i_vars.inc
new file mode 100644
index 0000000000..4ca6764191
--- /dev/null
+++ b/plugins/Actman/i_vars.inc
@@ -0,0 +1,31 @@
+{variables}
+type
+ pHKList = ^tHKList;
+ tHKList = array [0..1023] of tHKRecord;
+ pActList = ^tActList;
+ tActList = array [0..1023] of tHKAction;
+const
+ StructDelim = #7;
+const
+ HKListPage = 8;
+ ActListPage = 16;
+var
+ MaxGroups :cardinal=0; // current array size
+ MaxActions:cardinal=0; // current array size
+var
+ GroupList :pHKList =nil;
+ ActionList:pActList=nil;
+var
+ NoDescription:PWideChar;
+var
+ NewGroupList :pHKList = nil;
+ NewActionList:pActList = nil;
+ NewMaxGroups :cardinal;
+ NewMaxActions:cardinal;
+var
+ xmlfilename:array [0..511] of WideChar;
+var
+ fCLfilter:boolean = true;
+ fCLformat:pWideChar = nil;
+var
+ ApiCard:tmApiCard; \ No newline at end of file
diff --git a/plugins/Actman/i_visual.inc b/plugins/Actman/i_visual.inc
new file mode 100644
index 0000000000..ab32488165
--- /dev/null
+++ b/plugins/Actman/i_visual.inc
@@ -0,0 +1,1073 @@
+{Dialog visual part, depends of Dialog window}
+ procedure SHControl(ctrl,mode:dword);
+ begin
+ ShowWindow(GetDlgItem(Dialog,ctrl),mode);
+ end;
+
+ procedure SetupControls(group,mode:dword);
+ begin
+ case group of
+ ACT_CONTACT: begin
+ SHControl(IDC_STAT_CONTACT,mode);
+ SHControl(IDC_CONTACTLIST ,mode);
+ SHControl(IDC_CNT_KEEP ,mode);
+ SHControl(IDC_CNT_FILTER ,mode);
+ SHControl(IDC_STAT_FORMAT ,mode);
+ SHControl(IDC_EDIT_FORMAT ,mode);
+ SHControl(IDC_CNT_APPLY ,mode);
+ SHControl(IDC_STAT_FHELP ,mode);
+ end;
+ ACT_SERVICE: begin
+ SHControl(IDC_STAT_WPAR ,mode);
+ SHControl(IDC_STAT_LPAR ,mode);
+ SHControl(IDC_STAT_WPAR1 ,mode);
+ SHControl(IDC_STAT_LPAR1 ,mode);
+ SHControl(IDC_STAT_SERVICE,mode);
+ SHControl(IDC_EDIT_SERVICE,mode);
+ SHControl(IDC_SRV_SRVC ,mode);
+ SHControl(IDC_FLAG_WPAR ,mode);
+ SHControl(IDC_FLAG_LPAR ,mode);
+
+ SHControl(IDC_RES_GROUP ,mode);
+ SHControl(IDC_RES_POPUP ,mode);
+ SHControl(IDC_RES_MESSAGE ,mode);
+ SHControl(IDC_RES_INSERT ,mode);
+
+ SHControl(IDC_SRV_RESULT ,mode);
+ SHControl(IDC_SRV_RESSTAT ,mode);
+
+ if mode=SW_SHOW then
+ begin
+//!! Check parameter type
+ if CB_GetData(GetDlgItem(Dialog,IDC_FLAG_WPAR))=ptStruct then
+// if SendDlgItemMessage(Dialog,IDC_FLAG_WPAR,CB_GETCURSEL,0,0)=ptStruct then
+ begin
+ SHControl(IDC_WSTRUCT ,SW_SHOW);
+ SHControl(IDC_EDIT_WPAR,SW_HIDE);
+ SHControl(IDC_SRV_WPAR ,SW_HIDE);
+ end
+ else
+ begin
+ SHControl(IDC_WSTRUCT ,SW_HIDE);
+ SHControl(IDC_EDIT_WPAR,SW_SHOW);
+ SHControl(IDC_SRV_WPAR ,SW_SHOW);
+ end;
+ if CB_GetData(GetDlgItem(Dialog,IDC_FLAG_LPAR))=ptStruct then
+// if SendDlgItemMessage(Dialog,IDC_FLAG_LPAR,CB_GETCURSEL,0,0)=ptStruct then
+ begin
+ SHControl(IDC_LSTRUCT ,SW_SHOW);
+ SHControl(IDC_EDIT_LPAR,SW_HIDE);
+ SHControl(IDC_SRV_LPAR ,SW_HIDE);
+ end
+ else
+ begin
+ SHControl(IDC_LSTRUCT ,SW_HIDE);
+ SHControl(IDC_EDIT_LPAR,SW_SHOW);
+ SHControl(IDC_SRV_LPAR ,SW_SHOW);
+ end;
+
+ case CB_GetData(GetDlgItem(Dialog,IDC_SRV_RESULT)) of
+ sresHex: ;
+ sresInt: SHControl(IDC_RES_SIGNED,SW_SHOW);
+ sresString: begin
+ SHControl(IDC_RES_UNICODE,SW_SHOW);
+ SHControl(IDC_RES_FREEMEM,SW_SHOW);
+ end;
+ sresStruct: ;
+ end;
+
+ end
+ else
+ begin
+ SHControl(IDC_WSTRUCT ,SW_HIDE);
+ SHControl(IDC_LSTRUCT ,SW_HIDE);
+ SHControl(IDC_SRV_WPAR ,SW_HIDE);
+ SHControl(IDC_SRV_LPAR ,SW_HIDE);
+ SHControl(IDC_EDIT_WPAR ,SW_HIDE);
+ SHControl(IDC_EDIT_LPAR ,SW_HIDE);
+ SHControl(IDC_RES_FREEMEM,SW_HIDE);
+ SHControl(IDC_RES_UNICODE,SW_HIDE);
+ SHControl(IDC_RES_SIGNED ,SW_HIDE);
+ end;
+ end;
+ ACT_PROGRAM: begin
+ SHControl(IDC_PROCESS_GROUP,mode);
+ SHControl(IDC_PROGRAM ,mode);
+ SHControl(IDC_EDIT_PROCTIME,mode);
+ SHControl(IDC_EDIT_PRGPATH ,mode);
+ SHControl(IDC_PRG_PRG ,mode);
+ SHControl(IDC_EDIT_PRGARGS ,mode);
+ SHControl(IDC_PRG_ARG ,mode);
+ SHControl(IDC_STAT_PROCTIME,mode);
+ SHControl(IDC_STAT_PRGPATH ,mode);
+ SHControl(IDC_STAT_PRGARGS ,mode);
+
+ SHControl(IDC_PRSTART_GROUP,mode);
+ SHControl(IDC_FLAG_NORMAL ,mode);
+ SHControl(IDC_FLAG_HIDDEN ,mode);
+ SHControl(IDC_FLAG_MAXIMIZE,mode);
+ SHControl(IDC_FLAG_MINIMIZE,mode);
+
+ SHControl(IDC_FLAG_CURPATH,mode);
+ SHControl(IDC_FLAG_CONTINUE,mode);
+ SHControl(IDC_FLAG_PARALLEL,mode);
+
+ if mode=SW_SHOW then
+ begin
+ if isVarsInstalled then
+ SHControl(IDC_HLP_FVARS,SW_SHOW);
+ end
+ else
+ SHControl(IDC_HLP_FVARS,SW_HIDE);
+ end;
+ ACT_TEXT: begin
+ SHControl(IDC_FLAG_CLIP ,mode);
+ SHControl(IDC_FLAG_MESSAGE,mode);
+ SHControl(IDC_CLIP_COPYTO ,mode);
+ SHControl(IDC_CLIP_PASTE ,mode);
+ SHControl(IDC_CLIP_GROUP ,mode);
+ SHControl(IDC_FILE_ENC ,mode);
+
+ SHControl(IDC_FLAG_FILE ,mode);
+ SHControl(IDC_FILE_PATH ,mode);
+ SHControl(IDC_TXT_FILE ,mode);
+ SHControl(IDC_FILE_FILEBTN,mode);
+ SHControl(IDC_FILE_READ ,mode);
+ SHControl(IDC_FILE_WRITE ,mode);
+ SHControl(IDC_FILE_APPEND ,mode);
+ SHControl(IDC_FILE_GROUP ,mode);
+
+ SHControl(IDC_TXT_TEXT ,mode);
+ SHControl(IDC_EDIT_INSERT ,mode);
+ SHControl(IDC_STAT_INSERT ,mode);
+
+ if mode=SW_SHOW then
+ begin
+ if isVarsInstalled then
+ SHControl(IDC_HLP_VARS,SW_SHOW);
+ end
+ else
+ SHControl(IDC_HLP_VARS,SW_HIDE);
+ end;
+ ACT_ADVANCE: begin
+ SHControl(IDC_CONDITION,mode);
+ SHControl(IDC_FLAG_GT ,mode);
+ SHControl(IDC_FLAG_LT ,mode);
+ SHControl(IDC_FLAG_EQ ,mode);
+ SHControl(IDC_FLAG_NOP ,mode);
+ SHControl(IDC_FLAG_NOT ,mode);
+ SHControl(IDC_ADV_VALUE,mode);
+ SHControl(IDC_STAT_VAL ,mode);
+ SHControl(IDC_ADV_ASINT,mode);
+
+ SHControl(IDC_OPERATION ,mode);
+ SHControl(IDC_FLAG_BREAK,mode);
+ SHControl(IDC_FLAG_JUMP ,mode);
+ SHControl(IDC_FLAG_ANOP ,mode);
+
+ SHControl(IDC_FLAG_MATH,mode);
+ SHControl(IDC_ADV_OPER ,mode);
+ SHControl(IDC_ADV_VAL2 ,mode);
+ SHControl(IDC_ADV_VAL1 ,mode);
+
+ if mode=SW_SHOW then
+ begin
+ if isVarsInstalled then
+ SHControl(IDC_ADV_HVARS,SW_SHOW);
+ end
+ else
+ SHControl(IDC_ADV_HVARS,SW_HIDE);
+ SHControl(IDC_FLAG_VARS,mode);
+ SHControl(IDC_ADV_VARS ,mode);
+ end;
+ ACT_CHAIN: begin
+ SHControl(IDC_STAT_GROUPS,mode);
+ SHControl(IDC_GROUP_LIST ,mode);
+ end;
+ ACT_RW: begin
+ SHControl(IDC_STAT_CONTACT,mode);
+ SHControl(IDC_CONTACTLIST ,mode);
+
+ SHControl(IDC_RW_OPER ,mode);
+ SHControl(IDC_RW_VAL ,mode);
+ SHControl(IDC_RW_READ ,mode);
+ SHControl(IDC_RW_WRITE ,mode);
+ SHControl(IDC_RW_DELETE ,mode);
+ SHControl(IDC_RW_STATM ,mode);
+ SHControl(IDC_RW_MODULE ,mode);
+ SHControl(IDC_RW_STATS ,mode);
+ SHControl(IDC_RW_SETTING,mode);
+ SHControl(IDC_RW_TVAR ,mode);
+
+ if mode=SW_SHOW then
+ begin
+ if CB_GetData(GetDlgItem(Dialog,IDC_RW_DATATYPE))>2 then
+ begin
+ SHControl(IDC_RW_VALUE,SW_HIDE);
+ SHControl(IDC_RW_TEXT ,SW_SHOW);
+ end
+ else
+ begin
+ SHControl(IDC_RW_VALUE,SW_SHOW);
+ SHControl(IDC_RW_TEXT ,SW_HIDE);
+ end
+ end
+ else
+ begin
+ SHControl(IDC_RW_VALUE,SW_HIDE);
+ SHControl(IDC_RW_TEXT ,SW_HIDE);
+ end;
+ SHControl(IDC_RW_DATATYPE,mode);
+
+ SHControl(IDC_RW_CURRENT,mode);
+ SHControl(IDC_RW_PARAM ,mode);
+ SHControl(IDC_RW_RESULT ,mode);
+ SHControl(IDC_RW_MANUAL ,mode);
+ SHControl(IDC_RW_LAST ,mode);
+
+ SHControl(IDC_RW_MVAR ,mode);
+ SHControl(IDC_RW_SVAR ,mode);
+ end;
+ ACT_MESSAGE: begin
+ SHControl(IDC_MSG_KEEP ,mode);
+ SHControl(IDC_MSG_STAT1 ,mode);
+ SHControl(IDC_MSG_STAT2 ,mode);
+ SHControl(IDC_MSG_TITLE ,mode);
+ SHControl(IDC_MSG_TEXT ,mode);
+ SHControl(IDC_MSG_BTNS ,mode);
+ SHControl(IDC_MSGB_ARI ,mode);
+ SHControl(IDC_MSGB_OK ,mode);
+ SHControl(IDC_MSGB_OC ,mode);
+ SHControl(IDC_MSGB_RC ,mode);
+ SHControl(IDC_MSGB_YN ,mode);
+ SHControl(IDC_MSGB_YNC ,mode);
+ SHControl(IDC_MSG_ICONS ,mode);
+ SHControl(IDC_MSGI_NONE ,mode);
+ SHControl(IDC_MSGI_WARN ,mode);
+ SHControl(IDC_MSGI_INFO ,mode);
+ SHControl(IDC_MSGI_QUEST,mode);
+ SHControl(IDC_MSGI_ERROR,mode);
+
+ SHControl(IDC_MSG_TTL ,mode);
+ SHControl(IDC_MSG_TXT ,mode);
+ end;
+ end;
+ end;
+
+ procedure SetButtonOnOff(ctrl,state:dword);
+ begin
+ CheckDlgButton(Dialog,ctrl,state);
+ case ctrl of
+ IDC_SRV_WPAR,
+ IDC_SRV_LPAR,
+ IDC_SRV_SRVC,
+ IDC_PRG_PRG ,
+ IDC_PRG_ARG ,
+ IDC_TXT_FILE,
+ IDC_TXT_TEXT,
+ IDC_RW_MVAR ,
+ IDC_RW_SVAR ,
+ IDC_RW_TVAR ,
+ IDC_MSG_TTL ,
+ IDC_MSG_TXT : SetButtonIcon(GetDlgItem(Dialog,ctrl),checknames[state]);
+ end;
+ end;
+
+ procedure ButtonOff(ctrl:dword); {$IFDEF MSWINDOWS}inline;{$ENDIF}
+ begin
+ SetButtonOnOff(ctrl,BST_UNCHECKED);
+ end;
+
+ procedure ButtonOn(ctrl:dword); {$IFDEF MSWINDOWS}inline;{$ENDIF}
+ begin
+ SetButtonOnOff(ctrl,BST_CHECKED);
+ end;
+
+ procedure TextClear(ctrl:dword);
+ begin
+ SetDlgItemTextW(Dialog,ctrl,nil);
+ end;
+
+ function FixParam(buf:PAnsiChar;flag:integer):integer;
+ begin
+ if lstrcmpia(buf,Translate('hContact' ))=0 then result:=ptCurrent
+ else if lstrcmpia(buf,Translate('parameter'))=0 then result:=ptParam
+ else if lstrcmpia(buf,Translate('result' ))=0 then result:=ptResult
+ else if lstrcmpia(buf,Translate('structure'))=0 then result:=ptStruct
+ else
+ begin
+ if (buf[0] in ['0'..'9']) or ((buf[0]='-') and (buf[1] in ['0'..'9'])) or
+ ((buf[0]='$') and (buf[1] in sHexNum)) then
+ result:=ptNumber
+ else
+ result:=ptString;
+ end;
+
+ CB_SelectData(Dialog,flag,result);
+// SendDlgItemMessage(Dialog,flag,CB_SETCURSEL,result,0);
+ SendMessage(Dialog,WM_COMMAND,(CBN_SELCHANGE shl 16) or flag,GetDlgItem(Dialog,flag));
+ end;
+
+ procedure ReloadService;
+ var
+ pc:pAnsiChar;
+ buf,buf1:array [0..MaxDescrLen] of AnsiChar;
+ wnd:hwnd;
+ i:integer;
+// bufw:array [0..MaxDescrLen] of WideChar;
+ begin
+ wnd:=GetDlgItem(Dialog,IDC_EDIT_SERVICE);
+ SendMessageA(wnd,CB_GETLBTEXT,SendMessage(wnd,CB_GETCURSEL,0,0),tlparam(@buf));
+ ApiCard.Service:=@buf;
+
+ pc:=ApiCard.FillParams(GetDlgItem(Dialog,IDC_EDIT_WPAR),true);
+ if pc<>nil then
+ begin
+ if GetDlgItemTextA(Dialog,IDC_EDIT_WPAR,buf1,SizeOf(buf1))>0 then
+ case FixParam(@buf1,IDC_FLAG_WPAR) of
+ ptStruct: begin
+ mFreeMem(wstruct);
+ StrDup(wstruct,StrScan(pc,'|')+1);
+// AnsiToWide(StrScan(pc,'|')+1,wstruct,MirandaCP);
+ end;
+ end;
+ mFreeMem(pc);
+ end;
+
+ pc:=ApiCard.FillParams(GetDlgItem(Dialog,IDC_EDIT_LPAR),false);
+ if pc<>nil then
+ begin
+ if GetDlgItemTextA(Dialog,IDC_EDIT_LPAR,buf1,SizeOf(buf1))>0 then
+ case FixParam(@buf1,IDC_FLAG_LPAR) of
+ ptStruct: begin
+ mFreeMem(lstruct);
+ StrDup(lstruct,StrScan(pc,'|')+1);
+// AnsiToWide(StrScan(pc,'|')+1,lstruct,MirandaCP);
+ end;
+ end;
+ mFreeMem(pc);
+ end;
+
+ pc:=ApiCard.ResultType;
+ i:=sresInt;
+ if pc<>nil then
+ begin
+ if lstrcmpia(pc,'struct')=0 then i:=sresStruct
+ else if lstrcmpia(pc,'hex' )=0 then i:=sresHex
+ else if lstrcmpia(pc,'int')=0 then
+ begin
+ i:=sresInt;
+ ButtonOff(IDC_RES_SIGNED);
+ end
+ else if lstrcmpia(pc,'signed')=0 then
+ begin
+ i:=sresInt;
+ ButtonOn(IDC_RES_SIGNED);
+ end
+ else if lstrcmpia(pc,'str')=0 then
+ begin
+ i:=sresString;
+ ButtonOff(IDC_RES_UNICODE);
+ end
+ else if lstrcmpia(pc,'wide')=0 then
+ begin
+ i:=sresString;
+ ButtonOn(IDC_RES_UNICODE);
+ end;
+ mFreeMem(pc);
+ end;
+ CB_SelectData(Dialog,IDC_SRV_RESULT,i);
+
+// ApiCard.Show;
+ end;
+
+ procedure SelectActionType(group:dword);
+ var
+ i:integer;
+ begin
+ for i:=0 to ACT_MAXTYPE-1 do
+ if ActIds[i].code=group then break;
+ SendDlgItemMessage(Dialog,IDC_ACTION_TYPE,CB_SETCURSEL,i,0);
+ end;
+
+ procedure ClearControls(group:dword);
+ begin
+//! SelectActionType(group);
+ case group of
+ ACT_CONTACT: begin
+ EnableWindow(GetDlgItem(Dialog,IDC_CONTACTLIST),true);
+ SendDlgItemMessage(Dialog,IDC_CONTACTLIST,CB_SETCURSEL,0,0);
+ ButtonOff(IDC_CNT_KEEP);
+ ButtonOff(IDC_CNT_FILTER);
+ TextClear(IDC_EDIT_FORMAT);
+ end;
+ ACT_SERVICE: begin
+ TextClear(IDC_EDIT_SERVICE);
+ TextClear(IDC_EDIT_WPAR);
+ TextClear(IDC_EDIT_LPAR);
+ EnableWindow(GetDlgItem(Dialog,IDC_EDIT_WPAR),true);
+ EnableWindow(GetDlgItem(Dialog,IDC_EDIT_LPAR),true);
+// SendDlgItemMessage(Dialog,IDC_FLAG_WPAR,CB_SETCURSEL,0,0);
+// SendDlgItemMessage(Dialog,IDC_FLAG_LPAR,CB_SETCURSEL,0,0);
+ CB_SelectData(GetDlgItem(Dialog,IDC_FLAG_WPAR),ptNumber);
+ CB_SelectData(GetDlgItem(Dialog,IDC_FLAG_LPAR),ptNumber);
+
+ CB_SelectData(GetDlgItem(Dialog,IDC_SRV_RESULT),sresInt);
+
+ SendMessage(GetDlgItem(Dialog,IDC_EDIT_WPAR),CB_RESETCONTENT,0,0);
+ SendMessage(GetDlgItem(Dialog,IDC_EDIT_LPAR),CB_RESETCONTENT,0,0);
+
+ ButtonOff(IDC_RES_POPUP);
+ ButtonOff(IDC_RES_MESSAGE);
+ ButtonOff(IDC_RES_INSERT);
+
+ ButtonOff(IDC_RES_FREEMEM);
+ ButtonOff(IDC_RES_UNICODE);
+ ButtonOff(IDC_RES_SIGNED);
+
+ ButtonOff(IDC_SRV_WPAR);
+ ButtonOff(IDC_SRV_LPAR);
+ ButtonOff(IDC_SRV_SRVC);
+ end;
+ ACT_PROGRAM: begin
+ TextClear(IDC_EDIT_PROCTIME);
+ TextClear(IDC_EDIT_PRGPATH);
+ TextClear(IDC_EDIT_PRGARGS);
+
+ ButtonOff(IDC_FLAG_NORMAL);
+ ButtonOff(IDC_FLAG_HIDDEN);
+ ButtonOff(IDC_FLAG_MINIMIZE);
+ ButtonOff(IDC_FLAG_MAXIMIZE);
+
+ ButtonOff(IDC_FLAG_CURPATH);
+ ButtonOff(IDC_FLAG_CONTINUE);
+ ButtonOff(IDC_FLAG_PARALLEL);
+
+ ButtonOff(IDC_PRG_PRG);
+ ButtonOff(IDC_PRG_ARG);
+ end;
+ ACT_TEXT: begin
+ ButtonOff(IDC_FLAG_CLIP);
+ ButtonOff(IDC_FLAG_MESSAGE);
+ ButtonOff(IDC_CLIP_COPYTO);
+ ButtonOff(IDC_CLIP_PASTE);
+
+ ButtonOff(IDC_FLAG_FILE);
+ TextClear(IDC_FILE_PATH);
+ ButtonOff(IDC_FILE_READ);
+ ButtonOff(IDC_FILE_WRITE);
+ ButtonOff(IDC_FILE_APPEND);
+
+ EnableWindow(GetDlgItem(Dialog,IDC_EDIT_INSERT ),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_CLIP_COPYTO ),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_CLIP_PASTE ),false);
+
+ EnableWindow(GetDlgItem(Dialog,IDC_FILE_ENC ),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_FILE_PATH ),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_FILE_FILEBTN),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_FILE_READ ),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_FILE_WRITE ),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_FILE_APPEND ),false);
+
+ TextClear(IDC_EDIT_INSERT);
+
+ ButtonOff(IDC_TXT_FILE);
+ ButtonOff(IDC_TXT_TEXT);
+ end;
+ ACT_ADVANCE: begin
+ ButtonOff(IDC_FLAG_GT);
+ ButtonOff(IDC_FLAG_LT);
+ ButtonOff(IDC_FLAG_EQ);
+ ButtonOff(IDC_FLAG_NOP);
+ ButtonOff(IDC_FLAG_NOT);
+ TextClear(IDC_ADV_VALUE);
+
+ ButtonOff(IDC_FLAG_BREAK);
+ ButtonOff(IDC_FLAG_JUMP);
+ ButtonOff(IDC_FLAG_ANOP);
+ EnableWindow(GetDlgItem(Dialog,IDC_ADV_VAL2),false);
+
+ ButtonOff(IDC_FLAG_MATH);
+ SendDlgItemMessage(Dialog,IDC_ADV_OPER,CB_SETCURSEL,0,0);
+ TextClear(IDC_ADV_VAL1);
+ ButtonOff(IDC_ADV_ASINT);
+
+ EnableWindow(GetDlgItem(Dialog,IDC_ADV_OPER),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_ADV_VAL1),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_ADV_VARS),false);
+
+ ButtonOff(IDC_FLAG_VARS);
+ TextClear(IDC_ADV_VARS);
+ end;
+ ACT_CHAIN: begin
+// FillChainList(Dialog);
+ SendDlgItemMessage(Dialog,IDC_GROUP_LIST,CB_SETCURSEL,0,0);
+ end;
+ ACT_RW: begin
+ ButtonOff(IDC_RW_READ);
+ ButtonOff(IDC_RW_WRITE);
+ ButtonOff(IDC_RW_DELETE);
+ SendDlgItemMessage(Dialog,IDC_CONTACTLIST,CB_SETCURSEL,0,0);
+ EnableWindow(GetDlgItem(Dialog,IDC_CONTACTLIST),true);
+ TextClear(IDC_CONTACTLIST);
+ TextClear(IDC_RW_MODULE);
+ TextClear(IDC_RW_SETTING);
+ TextClear(IDC_RW_VALUE);
+ TextClear(IDC_RW_TEXT);
+
+ ButtonOff(IDC_RW_CURRENT);
+ ButtonOff(IDC_RW_PARAM);
+ ButtonOff(IDC_RW_RESULT);
+ ButtonOff(IDC_RW_MANUAL);
+ ButtonOff(IDC_RW_LAST);
+
+ ButtonOff(IDC_RW_MVAR);
+ ButtonOff(IDC_RW_SVAR);
+ ButtonOff(IDC_RW_TVAR);
+ end;
+ ACT_MESSAGE: begin
+ TextClear(IDC_MSG_TITLE);
+ TextClear(IDC_MSG_TEXT);
+ ButtonOff(IDC_MSG_KEEP);
+ ButtonOff(IDC_MSGB_OK);
+ ButtonOff(IDC_MSGB_OC);
+ ButtonOff(IDC_MSGB_ARI);
+ ButtonOff(IDC_MSGB_YNC);
+ ButtonOff(IDC_MSGB_YN);
+ ButtonOff(IDC_MSGB_RC);
+ ButtonOff(IDC_MSGI_NONE);
+ ButtonOff(IDC_MSGI_ERROR);
+ ButtonOff(IDC_MSGI_QUEST);
+ ButtonOff(IDC_MSGI_WARN);
+ ButtonOff(IDC_MSGI_INFO);
+
+ ButtonOff(IDC_MSG_TTL);
+ ButtonOff(IDC_MSG_TXT);
+ end;
+ end;
+ end;
+
+ procedure ClearDialogData;
+ begin
+ ClearControls(ACT_CONTACT);
+ ClearControls(ACT_SERVICE);
+ ClearControls(ACT_PROGRAM);
+ ClearControls(ACT_TEXT);
+ ClearControls(ACT_ADVANCE);
+ ClearControls(ACT_CHAIN);
+ ClearControls(ACT_RW);
+ ClearControls(ACT_MESSAGE);
+ mFreeMem(wstruct);
+ mFreeMem(lstruct);
+ end;
+
+ procedure SHWindows(exclude:dword=0);
+ begin
+ SetupControls(ACT_CONTACT,SW_HIDE);
+ SetupControls(ACT_SERVICE,SW_HIDE);
+ SetupControls(ACT_PROGRAM,SW_HIDE);
+ SetupControls(ACT_TEXT ,SW_HIDE);
+ SetupControls(ACT_ADVANCE,SW_HIDE);
+ SetupControls(ACT_CHAIN ,SW_HIDE);
+ SetupControls(ACT_RW ,SW_HIDE);
+ SetupControls(ACT_MESSAGE,SW_HIDE);
+{
+ if exclude<>ACT_CONTACT then SetupControls(ACT_CONTACT,SW_HIDE);
+ if exclude<>ACT_SERVICE then SetupControls(ACT_SERVICE,SW_HIDE);
+ if exclude<>ACT_PROGRAM then SetupControls(ACT_PROGRAM,SW_HIDE);
+ if exclude<>ACT_TEXT then SetupControls(ACT_TEXT ,SW_HIDE);
+ if exclude<>ACT_ADVANCE then SetupControls(ACT_ADVANCE,SW_HIDE);
+ if exclude<>ACT_CHAIN then SetupControls(ACT_CHAIN ,SW_HIDE);
+ if exclude<>ACT_RW then SetupControls(ACT_RW ,SW_HIDE);
+}
+ case exclude of
+ ACT_CONTACT,
+ ACT_SERVICE,
+ ACT_PROGRAM,
+ ACT_TEXT ,
+ ACT_ADVANCE,
+ ACT_CHAIN ,
+ ACT_RW ,
+ ACT_MESSAGE: begin
+ SetupControls(exclude,SW_SHOW);
+ end;
+ end;
+ end;
+
+ procedure SHActButtons(mode:integer);
+ begin
+ ShowWindow(GetDlgItem(Dialog,IDC_STAT_ACTION),mode);
+ ShowWindow(GetDlgItem(Dialog,IDC_ACTION_TYPE),mode);
+ end;
+
+ procedure InitDef(exclude:dword=0);
+ begin
+ if exclude<>ACT_CONTACT then
+ begin
+ ButtonOn(IDC_CNT_FILTER);
+ // do nothing
+ end;
+ if exclude<>ACT_SERVICE then
+ begin
+ CB_SelectData(Dialog,IDC_SRV_RESULT,sresInt);
+ end;
+ if exclude<>ACT_PROGRAM then
+ begin
+ ButtonOn(IDC_FLAG_PARALLEL);
+ ButtonOn(IDC_FLAG_NORMAL);
+ SetDlgItemInt(Dialog,IDC_EDIT_PROCTIME,0,false);
+ end;
+ if exclude<>ACT_TEXT then
+ begin
+ ButtonOn(IDC_FLAG_CLIP);
+ EnableWindow(GetDlgItem(Dialog,IDC_CLIP_COPYTO),true);
+ EnableWindow(GetDlgItem(Dialog,IDC_CLIP_PASTE ),true);
+ ButtonOn(IDC_CLIP_COPYTO);
+
+ ButtonOn(IDC_FILE_READ);
+ // do nothing
+ end;
+ if exclude<>ACT_ADVANCE then
+ begin
+ ButtonOn(IDC_FLAG_NOP);
+ ButtonOn(IDC_FLAG_ANOP);
+ SetDlgItemInt(Dialog,IDC_ADV_VALUE,0,false);
+ SetDlgItemInt(Dialog,IDC_ADV_VAL1 ,0,false);
+ EnableWindow(GetDlgItem(Dialog,IDC_ADV_VAL1),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_ADV_VAL2),false);
+ EnableWindow(GetDlgItem(Dialog,IDC_ADV_OPER),false);
+// SendDlgItemMessage(Dialog,IDC_ADV_OPER,CB_SETCURSEL,0,0);
+ CB_SelectData(Dialog,IDC_ADV_OPER,ORD(aeNot));
+ end;
+ if exclude<>ACT_CHAIN then
+ begin
+// FillChainList(Dialog);
+ SendDlgItemMessage(Dialog,IDC_GROUP_LIST,CB_SETCURSEL,0,0);
+ end;
+ if exclude<>ACT_RW then
+ begin
+ ButtonOn(IDC_RW_READ);
+ SetDlgItemInt(Dialog,IDC_RW_VALUE,0,false);
+ ButtonOn(IDC_RW_MANUAL);
+ EnableWindow(GetDlgItem(Dialog,IDC_RW_VALUE),true);
+ EnableWindow(GetDlgItem(Dialog,IDC_RW_TEXT ),true);
+ end;
+ if exclude<>ACT_MESSAGE then
+ begin
+ ButtonOn(IDC_MSGB_OK);
+ ButtonOn(IDC_MSGI_NONE);
+ end;
+ end;
+
+ // set buttons by options
+ procedure FillAction(CurAction:integer);
+ var
+ i:integer;
+ bb:boolean;
+ wnd:HWND;
+ begin
+ with NewActionList^[CurAction] do
+ begin
+ if (flags and ACF_ASSIGNED)=0 then
+ exit;
+ if actionType=0 then
+ actionType:=ACT_CONTACT;
+
+ InitDef(actionType);
+ SelectActionType(actionType);
+ case actionType of
+
+ ACT_CONTACT: begin
+ if (flags and ACF_KEEPONLY)<>0 then
+ ButtonOn(IDC_CNT_KEEP);
+ if fCLfilter then
+ ButtonOn(IDC_CNT_FILTER);
+ SetDlgItemTextW(Dialog,IDC_EDIT_FORMAT,fCLformat);
+
+ SendDlgItemMessage(Dialog,IDC_CONTACTLIST,CB_SETCURSEL,
+ FindContact(GetDlgItem(Dialog,IDC_CONTACTLIST),contact),0);
+ end;
+
+ ACT_SERVICE: begin
+ if SendDlgItemMessageA(Dialog,IDC_EDIT_SERVICE,CB_SELECTSTRING,twparam(-1),tlparam(service))<>CB_ERR then
+ ReloadService
+ else
+ SetDlgItemTextA(Dialog,IDC_EDIT_SERVICE,service);
+
+ if (flags2 and ACF2_SRV_WPAR)<>0 then ButtonOn(IDC_SRV_WPAR);
+ if (flags2 and ACF2_SRV_LPAR)<>0 then ButtonOn(IDC_SRV_LPAR);
+ if (flags2 and ACF2_SRV_SRVC)<>0 then ButtonOn(IDC_SRV_SRVC);
+
+ if (flags and ACF_MESSAGE)<>0 then ButtonOn(IDC_RES_MESSAGE);
+ if (flags and ACF_POPUP )<>0 then ButtonOn(IDC_RES_POPUP);
+ if (flags and ACF_INSERT )<>0 then ButtonOn(IDC_RES_INSERT);
+
+ if (flags and ACF_HEX)<>0 then
+ i:=sresHex
+ else if (flags and ACF_STRUCT)<>0 then
+ i:=sresStruct
+ else if (flags and ACF_STRING)<>0 then
+ begin
+ i:=sresString;
+ if (flags and ACF_UNICODE )<>0 then ButtonOn(IDC_RES_UNICODE);
+ if (flags2 and ACF2_FREEMEM)<>0 then ButtonOn(IDC_RES_FREEMEM);
+ end
+ else
+ begin
+ i:=sresInt;
+ if (flags and ACF_SIGNED)<>0 then
+ ButtonOn(IDC_RES_SIGNED);
+ end;
+ CB_SelectData(Dialog,IDC_SRV_RESULT,i);
+
+ if (flags and ACF_WPARAM)<>0 then
+ begin
+ EnableWindow(GetDlgItem(Dialog,IDC_EDIT_WPAR),false);
+ i:=ptParam;
+ end
+ else if (flags and ACF_WRESULT)<>0 then
+ begin
+ EnableWindow(GetDlgItem(Dialog,IDC_EDIT_WPAR),false);
+ i:=ptResult;
+ end
+ else if (flags and ACF_WPARNUM)<>0 then
+ begin
+ if (flags and ACF_WCURRENT)<>0 then
+ begin
+ EnableWindow(GetDlgItem(Dialog,IDC_EDIT_WPAR),false);
+ i:=ptCurrent
+ end
+ else
+ begin
+ i:=ptNumber;
+ SetNumValue(GetDlgItem(Dialog,IDC_EDIT_WPAR),wparam,
+ (flags2 and ACF2_SRV_WPAR)<>0,
+ (flags2 and ACF2_SRV_WHEX)<>0);
+// SetDlgItemInt(Dialog,IDC_EDIT_WPAR,wparam,true)
+ end;
+ end
+ else if (flags and ACF_WSTRUCT)<>0 then
+ begin
+ i:=ptStruct;
+ SHControl(IDC_EDIT_WPAR,SW_HIDE);
+ SHControl(IDC_WSTRUCT ,SW_SHOW);
+ mFreeMem(wstruct);
+ StrDup(wstruct,PAnsiChar(wparam));
+ end
+ else if (flags and ACF_WUNICODE)<>0 then
+ begin
+ i:=ptUnicode;
+ SetDlgItemTextW(Dialog,IDC_EDIT_WPAR,pWideChar(wparam));
+ end
+ else
+ begin
+ i:=ptString;
+ SetDlgItemTextA(Dialog,IDC_EDIT_WPAR,PAnsiChar(wparam));
+ end;
+ CB_SelectData(GetDlgItem(Dialog,IDC_FLAG_WPAR),i);
+ SendDlgItemMessage(Dialog,IDC_FLAG_WPAR,CB_SETCURSEL,i,0);
+
+ if (flags and ACF_LPARAM)<>0 then
+ begin
+ EnableWindow(GetDlgItem(Dialog,IDC_EDIT_LPAR),false);
+ i:=ptParam;
+ end
+ else if (flags and ACF_LRESULT)<>0 then
+ begin
+ EnableWindow(GetDlgItem(Dialog,IDC_EDIT_LPAR),false);
+ i:=ptResult;
+ end
+ else if (flags and ACF_LPARNUM)<>0 then
+ begin
+ if (flags and ACF_LCURRENT)<>0 then
+ begin
+ EnableWindow(GetDlgItem(Dialog,IDC_EDIT_LPAR),false);
+ i:=ptCurrent;
+ end
+ else
+ begin
+ i:=ptNumber;
+ SetNumValue(GetDlgItem(Dialog,IDC_EDIT_LPAR),lparam,
+ (flags2 and ACF2_SRV_LPAR)<>0,
+ (flags2 and ACF2_SRV_LHEX)<>0);
+// SetDlgItemInt(Dialog,IDC_EDIT_LPAR,lparam,true)
+ end;
+ end
+ else if (flags and ACF_LSTRUCT)<>0 then
+ begin
+ i:=ptStruct;
+ SHControl(IDC_EDIT_LPAR,SW_HIDE);
+ SHControl(IDC_LSTRUCT ,SW_SHOW);
+ mFreeMem(lstruct);
+ StrDup(lstruct,PAnsiChar(lparam));
+ end
+ else if (flags and ACF_LUNICODE)<>0 then
+ begin
+ i:=ptUnicode;
+ SetDlgItemTextW(Dialog,IDC_EDIT_LPAR,pWideChar(lparam));
+ end
+ else
+ begin
+ i:=ptString;
+ SetDlgItemTextA(Dialog,IDC_EDIT_LPAR,PAnsiChar(lparam));
+ end;
+ CB_SelectData(GetDlgItem(Dialog,IDC_FLAG_LPAR),i);
+
+ end;
+
+ ACT_PROGRAM: begin
+ if (flags2 and ACF2_PRG_PRG)<>0 then
+ ButtonOn(IDC_PRG_PRG);
+ if (flags2 and ACF2_PRG_ARG)<>0 then
+ ButtonOn(IDC_PRG_ARG);
+
+ SetDlgItemTextW(Dialog,IDC_EDIT_PRGPATH ,prgname);
+ SetDlgItemTextW(Dialog,IDC_EDIT_PRGARGS ,args);
+ SetDlgItemInt (Dialog,IDC_EDIT_PROCTIME,time,false);
+ case show of
+ SW_HIDE : ButtonOn(IDC_FLAG_HIDDEN);
+ SW_SHOWMINIMIZED: ButtonOn(IDC_FLAG_MINIMIZE);
+ SW_SHOWMAXIMIZED: ButtonOn(IDC_FLAG_MAXIMIZE);
+ else
+ {SW_SHOWNORMAL :} ButtonOn(IDC_FLAG_NORMAL);
+ end;
+ if (flags and ACF_CURPATH)<>0 then
+ ButtonOn(IDC_FLAG_CURPATH);
+ if (flags and ACF_PRTHREAD)<>0 then
+ ButtonOn(IDC_FLAG_PARALLEL)
+ else
+ ButtonOn(IDC_FLAG_CONTINUE);
+
+ end;
+
+ ACT_TEXT: begin
+ if (flags and ACF_CLIPBRD)<>0 then
+ begin
+ ButtonOn(IDC_FLAG_CLIP);
+ EnableWindow(GetDlgItem(Dialog,IDC_CLIP_COPYTO),true);
+ EnableWindow(GetDlgItem(Dialog,IDC_CLIP_PASTE ),true);
+ if (flags and ACF_COPYTO)<>0 then
+ ButtonOn(IDC_CLIP_COPYTO)
+ else
+ ButtonOn(IDC_CLIP_PASTE);
+// for switches
+ ButtonOn(IDC_FILE_READ);
+ end
+
+ else
+ begin
+ if (flags and (ACF_FILE or ACF_FAPPEND or ACF_FWRITE))<>ACF_FILE then
+ EnableWindow(GetDlgItem(Dialog,IDC_EDIT_INSERT),true);
+ SetDlgItemTextW(Dialog,IDC_EDIT_INSERT,text);
+// for switches
+ ButtonOn(IDC_CLIP_COPYTO);
+
+ if (flags2 and ACF2_TXT_TEXT)<>0 then
+ ButtonOn(IDC_TXT_TEXT);
+
+ if (flags and ACF_FILE)<>0 then
+ begin
+ if (flags2 and ACF2_TXT_FILE)<>0 then
+ ButtonOn(IDC_TXT_FILE);
+ ButtonOn(IDC_FLAG_FILE);
+ EnableWindow(GetDlgItem(Dialog,IDC_FILE_PATH ),true);
+ EnableWindow(GetDlgItem(Dialog,IDC_FILE_FILEBTN),true);
+ EnableWindow(GetDlgItem(Dialog,IDC_FILE_READ ),true);
+ EnableWindow(GetDlgItem(Dialog,IDC_FILE_WRITE ),true);
+ EnableWindow(GetDlgItem(Dialog,IDC_FILE_APPEND ),true);
+
+ wnd:=GetDlgItem(Dialog,IDC_FILE_ENC);
+ EnableWindow(wnd,true);
+ if (flags and ACF_ANSI)<>0 then
+ CB_SelectData(wnd,0)
+ else if (flags and ACF_UTF8)<>0 then
+ begin
+ if (flags and ACF_SIGN)<>0 then
+ CB_SelectData(wnd,2)
+ else
+ CB_SelectData(wnd,1);
+ end
+ else if (flags and ACF_SIGN)<>0 then
+ CB_SelectData(wnd,4)
+ else
+ CB_SelectData(wnd,3);
+
+ if (flags and ACF_FAPPEND)<>0 then ButtonOn(IDC_FILE_APPEND)
+ else if (flags and ACF_FWRITE )<>0 then ButtonOn(IDC_FILE_WRITE)
+ else ButtonOn(IDC_FILE_READ);
+ SetDlgItemTextW(Dialog,IDC_FILE_PATH,tfile);
+ end
+ else
+ begin
+ ButtonOn(IDC_FLAG_MESSAGE);
+// for switches
+ ButtonOn(IDC_FILE_READ);
+ end;
+ end;
+
+ end;
+
+ ACT_ADVANCE: begin
+ FillSubList(Dialog);
+// SendDlgItemMessage(Dialog,IDC_ADV_VAL2,CB_SETCURSEL,0,0);
+
+ case condition and not ADV_COND_NOT of
+ ADV_COND_GT: ButtonOn(IDC_FLAG_GT);
+ ADV_COND_LT: ButtonOn(IDC_FLAG_LT);
+ ADV_COND_EQ: ButtonOn(IDC_FLAG_EQ);
+ else // ADV_COND_NOP
+ ButtonOn(IDC_FLAG_NOP);
+ end;
+ SetDlgItemInt(Dialog,IDC_ADV_VALUE,value,false);
+ if (condition and ADV_COND_NOT)<>0 then
+ ButtonOn(IDC_FLAG_NOT);
+
+ SetDlgItemInt(Dialog,IDC_ADV_VAL1,mathval,true);
+ case action and ADV_ACTION of
+ ADV_ACT_MATH: begin
+ ButtonOn(IDC_FLAG_MATH);
+ CB_SelectData(GetDlgItem(Dialog,IDC_ADV_OPER),oper);
+// SendDlgItemMessage(Dialog,IDC_ADV_OPER,CB_SETCURSEL,oper,0);
+ EnableWindow(GetDlgItem(Dialog,IDC_ADV_VAL1),true);
+ EnableWindow(GetDlgItem(Dialog,IDC_ADV_OPER),true);
+ end;
+ ADV_ACT_VARS: begin
+ ButtonOn(IDC_FLAG_VARS);
+ if (flags and ACF_VARASINT)<>0 then
+ ButtonOn(IDC_ADV_ASINT);
+ EnableWindow(GetDlgItem(Dialog,IDC_ADV_VARS),true);
+ SetDlgItemTextW(Dialog,IDC_ADV_VARS,varval);
+ end;
+ end;
+ bb:=false;
+ case action and ADV_ACT_POST of
+ ADV_ACT_BREAK: ButtonOn(IDC_FLAG_BREAK);
+ ADV_ACT_JUMP : begin
+ SendDlgItemMessageW(Dialog,IDC_ADV_VAL2,CB_SELECTSTRING,twparam(-1),tlparam(operval));
+ ButtonOn(IDC_FLAG_JUMP);
+ bb:=true;
+ end;
+ else // ADV_ACT_NOP
+ ButtonOn(IDC_FLAG_ANOP);
+ end;
+ EnableWindow(GetDlgItem(Dialog,IDC_ADV_VAL2),bb);
+ end;
+
+ ACT_CHAIN: begin
+// FillChainList(Dialog);
+ if (flags and ACF_BYNAME)<>0 then
+ SendDlgItemMessageW(Dialog,IDC_GROUP_LIST,CB_SELECTSTRING,twparam(-1),tlparam(actname))
+ else
+ SendDlgItemMessageW(Dialog,IDC_GROUP_LIST,CB_SELECTSTRING,twparam(-1),tlparam(GetGroupName(id)));
+ end;
+
+ ACT_RW: begin
+ if (flags and ACF_DBDELETE)<>0 then ButtonOn(IDC_RW_DELETE)
+ else if (flags and ACF_DBWRITE )= 0 then ButtonOn(IDC_RW_READ)
+ else ButtonOn(IDC_RW_WRITE);
+
+ bb:=false;
+ if (flags and ACF_CURRENT)<>0 then
+ begin
+ ButtonOn(IDC_RW_CURRENT);
+ EnableWindow(GetDlgItem(Dialog,IDC_CONTACTLIST),false);
+ end
+ else if (flags and ACF_PARAM)<>0 then
+ begin
+ ButtonOn(IDC_RW_PARAM);
+ EnableWindow(GetDlgItem(Dialog,IDC_CONTACTLIST),false);
+ end
+ else if (flags and ACF_RESULT)<>0 then
+ begin
+ ButtonOn(IDC_RW_RESULT);
+ EnableWindow(GetDlgItem(Dialog,IDC_CONTACTLIST),false);
+ end
+ else
+ begin
+ ButtonOn(IDC_RW_MANUAL);
+ bb:=true;
+ EnableWindow(GetDlgItem(Dialog,IDC_CONTACTLIST),true);
+ SendDlgItemMessage(Dialog,IDC_CONTACTLIST,CB_SETCURSEL,
+ FindContact(GetDlgItem(Dialog,IDC_CONTACTLIST),dbcontact),0);
+ end;
+ EnableWindow(GetDlgItem(Dialog,IDC_CONTACTLIST),bb);
+
+ if (flags and ACF_LAST)<>0 then
+ begin
+ ButtonOn(IDC_RW_LAST);
+ bb:=false;
+ end
+ else
+ bb:=true;
+ if (flags and ACF_DBDELETE)<>0 then
+ bb:=false;
+
+ EnableWindow(GetDlgItem(Dialog,IDC_RW_VALUE),bb);
+ EnableWindow(GetDlgItem(Dialog,IDC_RW_TEXT ),bb);
+
+ SetDlgItemTextA(Dialog,IDC_RW_MODULE ,dbmodule);
+ SetDlgItemTextA(Dialog,IDC_RW_SETTING,dbsetting);
+ if (flags2 and ACF2_RW_MVAR)<>0 then ButtonOn(IDC_RW_MVAR);
+ if (flags2 and ACF2_RW_SVAR)<>0 then ButtonOn(IDC_RW_SVAR);
+ if (flags2 and ACF2_RW_TVAR)<>0 then ButtonOn(IDC_RW_TVAR);
+
+
+ wnd:=GetDlgItem(Dialog,IDC_RW_DATATYPE);
+ if ((flags and ACF_DBANSI )=ACF_DBANSI) or
+ ((flags and ACF_DBUTEXT)=ACF_DBUTEXT) then
+ begin
+ SHControl(IDC_RW_TEXT ,SW_SHOW);
+ SHControl(IDC_RW_VALUE,SW_HIDE);
+ if (flags and ACF_DBANSI)=ACF_DBANSI then
+ CB_SelectData(wnd,3)
+ else
+ CB_SelectData(wnd,4);
+ SetDlgItemTextW(Dialog,IDC_RW_TEXT,pWideChar(dbvalue));
+ end
+ else
+ begin
+ if (flags and ACF_DBBYTE)=ACF_DBBYTE then
+ CB_SelectData(wnd,0)
+ else if (flags and ACF_DBWORD)=ACF_DBWORD then
+ CB_SelectData(wnd,1)
+ else
+ CB_SelectData(wnd,2);
+ SHControl(IDC_RW_TEXT ,SW_HIDE);
+ SHControl(IDC_RW_VALUE,SW_SHOW);
+
+ SetNumValue(GetDlgItem(Dialog,IDC_RW_VALUE),dbvalue,
+ (flags2 and ACF2_RW_TVAR)<>0,
+ (flags2 and ACF2_RW_HEX )<>0);
+ end;
+
+ end;
+
+ ACT_MESSAGE: begin
+ SetDlgItemTextW(Dialog,IDC_MSG_TITLE,msgtitle);
+ SetDlgItemTextW(Dialog,IDC_MSG_TEXT ,msgtext);
+ if (flags2 and ACF2_MSG_TTL)<>0 then ButtonOn(IDC_MSG_TTL);
+ if (flags2 and ACF2_MSG_TXT)<>0 then ButtonOn(IDC_MSG_TXT);
+ if (flags and ACF_MSG_KEEP)<>0 then ButtonOn(IDC_MSG_KEEP);
+
+ case boxopts and $0F of
+ 1: ButtonOn(IDC_MSGB_OC);
+ 2: ButtonOn(IDC_MSGB_ARI);
+ 3: ButtonOn(IDC_MSGB_YNC);
+ 4: ButtonOn(IDC_MSGB_YN);
+ 5: ButtonOn(IDC_MSGB_RC);
+ else
+ ButtonOn(IDC_MSGB_OK);
+ end;
+ case boxopts and $F0 of
+ $10: ButtonOn(IDC_MSGI_ERROR);
+ $20: ButtonOn(IDC_MSGI_QUEST);
+ $30: ButtonOn(IDC_MSGI_WARN);
+ $40: ButtonOn(IDC_MSGI_INFO);
+ else
+ ButtonOn(IDC_MSGI_NONE);
+ end;
+ end;
+ end;
+ SHWindows(actionType);
+ end;
+ end;
diff --git a/plugins/Actman/ico/advance.ico b/plugins/Actman/ico/advance.ico
new file mode 100644
index 0000000000..fa6c604542
--- /dev/null
+++ b/plugins/Actman/ico/advance.ico
Binary files differ
diff --git a/plugins/Actman/ico/apply.ico b/plugins/Actman/ico/apply.ico
new file mode 100644
index 0000000000..80c3802c09
--- /dev/null
+++ b/plugins/Actman/ico/apply.ico
Binary files differ
diff --git a/plugins/Actman/ico/chain.ico b/plugins/Actman/ico/chain.ico
new file mode 100644
index 0000000000..3a98b8ac30
--- /dev/null
+++ b/plugins/Actman/ico/chain.ico
Binary files differ
diff --git a/plugins/Actman/ico/contact.ico b/plugins/Actman/ico/contact.ico
new file mode 100644
index 0000000000..8174fa221a
--- /dev/null
+++ b/plugins/Actman/ico/contact.ico
Binary files differ
diff --git a/plugins/Actman/ico/delete.ico b/plugins/Actman/ico/delete.ico
new file mode 100644
index 0000000000..eea851da19
--- /dev/null
+++ b/plugins/Actman/ico/delete.ico
Binary files differ
diff --git a/plugins/Actman/ico/down.ico b/plugins/Actman/ico/down.ico
new file mode 100644
index 0000000000..d4fdb83bbf
--- /dev/null
+++ b/plugins/Actman/ico/down.ico
Binary files differ
diff --git a/plugins/Actman/ico/export.ico b/plugins/Actman/ico/export.ico
new file mode 100644
index 0000000000..ddddb59074
--- /dev/null
+++ b/plugins/Actman/ico/export.ico
Binary files differ
diff --git a/plugins/Actman/ico/format.ico b/plugins/Actman/ico/format.ico
new file mode 100644
index 0000000000..ddddb59074
--- /dev/null
+++ b/plugins/Actman/ico/format.ico
Binary files differ
diff --git a/plugins/Actman/ico/import.ico b/plugins/Actman/ico/import.ico
new file mode 100644
index 0000000000..481da4dbaf
--- /dev/null
+++ b/plugins/Actman/ico/import.ico
Binary files differ
diff --git a/plugins/Actman/ico/insert.ico b/plugins/Actman/ico/insert.ico
new file mode 100644
index 0000000000..481da4dbaf
--- /dev/null
+++ b/plugins/Actman/ico/insert.ico
Binary files differ
diff --git a/plugins/Actman/ico/message.ico b/plugins/Actman/ico/message.ico
new file mode 100644
index 0000000000..fa6c604542
--- /dev/null
+++ b/plugins/Actman/ico/message.ico
Binary files differ
diff --git a/plugins/Actman/ico/new.ico b/plugins/Actman/ico/new.ico
new file mode 100644
index 0000000000..73937210e0
--- /dev/null
+++ b/plugins/Actman/ico/new.ico
Binary files differ
diff --git a/plugins/Actman/ico/program.ico b/plugins/Actman/ico/program.ico
new file mode 100644
index 0000000000..30c7df1875
--- /dev/null
+++ b/plugins/Actman/ico/program.ico
Binary files differ
diff --git a/plugins/Actman/ico/reload.ico b/plugins/Actman/ico/reload.ico
new file mode 100644
index 0000000000..dc070c5083
--- /dev/null
+++ b/plugins/Actman/ico/reload.ico
Binary files differ
diff --git a/plugins/Actman/ico/rw.ico b/plugins/Actman/ico/rw.ico
new file mode 100644
index 0000000000..d5927ebb08
--- /dev/null
+++ b/plugins/Actman/ico/rw.ico
Binary files differ
diff --git a/plugins/Actman/ico/service.ico b/plugins/Actman/ico/service.ico
new file mode 100644
index 0000000000..ddddb59074
--- /dev/null
+++ b/plugins/Actman/ico/service.ico
Binary files differ
diff --git a/plugins/Actman/ico/test.ico b/plugins/Actman/ico/test.ico
new file mode 100644
index 0000000000..345530ba76
--- /dev/null
+++ b/plugins/Actman/ico/test.ico
Binary files differ
diff --git a/plugins/Actman/ico/up.ico b/plugins/Actman/ico/up.ico
new file mode 100644
index 0000000000..56fde31eda
--- /dev/null
+++ b/plugins/Actman/ico/up.ico
Binary files differ
diff --git a/plugins/Actman/ico/vcheck.ico b/plugins/Actman/ico/vcheck.ico
new file mode 100644
index 0000000000..3f4afbbb01
--- /dev/null
+++ b/plugins/Actman/ico/vcheck.ico
Binary files differ
diff --git a/plugins/Actman/ico/vuncheck.ico b/plugins/Actman/ico/vuncheck.ico
new file mode 100644
index 0000000000..9587919f5b
--- /dev/null
+++ b/plugins/Actman/ico/vuncheck.ico
Binary files differ
diff --git a/plugins/Actman/m_actions.inc b/plugins/Actman/m_actions.inc
new file mode 100644
index 0000000000..7600d0dcdf
--- /dev/null
+++ b/plugins/Actman/m_actions.inc
@@ -0,0 +1,193 @@
+// defined in interfaces.inc
+//const MIID_ACTMANAGER:MUUID='{9584DA04-FB4F-40c1-9325-E4F9CAAFCB5D}';
+
+// hotkey and action (common) flags
+const
+ ACF_DISABLED = $10000000; // action disabled
+ ACF_USEDNOW = $20000000; // action in use (reserved)
+ ACF_DOBREAK = $40000000; // special, make break;
+ ACF_ASSIGNED = $80000000; // action assigned
+ ACF_EXPORT = $08000000; // action to export
+ ACF_IMPORT = ACF_EXPORT; // imported
+ ACF_VOLATILE = $04000000; // don't save in DB
+ ACF_PARALLEL = $02000000; // parallel action work (no wait thread)
+ ACF_OVERLOAD = $01000000; // imported action overwrite old
+
+// action flags
+const
+ // ACT_CONTACT
+ ACF_KEEPONLY = $00000001; // keep contact handle in Last, don't show window
+
+ // ACT_SERVICE
+ ACF_WPARNUM = $00000001; // wParam is number
+ ACF_LPARNUM = $00000002; // lParam is number
+ ACF_WUNICODE = $00000004; // wParam is Unicode string
+ ACF_LUNICODE = $00000008; // lParam is Unicode string
+ ACF_WCURRENT = $00000010; // wParam is ignored, used current user handle
+ // from current message window
+ ACF_LCURRENT = $00000020; // lParam is ignored, used current user handle
+ // from current message window
+ ACF_WPARHEX = $00000040; //!! Show as hex
+ ACF_LPARHEX = $00000080; //!! Show as hex
+
+ ACF_WRESULT = $00010000; // wParam is previous action result
+ ACF_LRESULT = $00020000; // lParam is previous action result
+ ACF_WPARAM = $00040000; // wParam is Call parameter
+ ACF_LPARAM = $00080000; // lParam is Call parameter
+ ACF_WSTRUCT = $00100000;
+ ACF_LSTRUCT = $00200000;
+ ACF_WPARTYPE = ACF_WPARNUM or ACF_WUNICODE or ACF_WCURRENT or ACF_WPARAM or ACF_WSTRUCT;
+ ACF_LPARTYPE = ACF_LPARNUM or ACF_LUNICODE or ACF_LCURRENT or ACF_LPARAM or ACF_LSTRUCT;
+
+ ACF_INSERT = $00000100; // Insert result in message
+ ACF_MESSAGE = $00000200; // Show service result as message
+ ACF_POPUP = $00000400; // Show service result as popup
+ ACF_STRING = $00000800; // Service result is string
+ ACF_UNICODE = $00001000; // Service result is Widestring
+ ACF_HEX = $00002000; // Result as Hex
+ ACF_SIGNED = $00004000; // Result as signed value
+//!!
+ ACF_STRUCT = $00008000; // Service result in structure
+
+ // ACT_TEXT
+ ACF_CLIPBRD = $00000002; // Clipboard operations, not window
+ ACF_ANSI = $00000004; // File: ANSI or Unicode (UTF8/UTF16) text
+ ACF_COPYTO = $00000008; // Clipboard operations: 'copy to' or 'paste from'
+
+ ACF_FILE = $00000010; // File operations
+ ACF_FWRITE = $00000020; // read/write file
+ ACF_FAPPEND = $00000040; // append file
+
+ ACF_UTF8 = $00000080; // File: UTF8 or UTF16
+ ACF_SIGN = $00000100; // File: with signature or not
+
+ // ACT_PROGRAM
+ ACF_CURPATH = $00000002; // Current (not program) path
+ ACF_PRTHREAD = $00000004; // parallel Program
+
+ // ACT_ADVANCED
+ ACF_VARASINT = $00000001; // if variables script, translate to int
+
+ // ACT_DBRW
+ ACF_DBWRITE = $00000001; // write to (not read from) DB
+ ACF_DBBYTE = $00000002; // read/write byte (def. dword)
+ ACF_DBWORD = $00000004; // read/write word (def. dword)
+ ACF_PARAM = $00000008; // hContact from parameter
+ ACF_CURRENT = $00000010; // hContact is 0 (user settings)
+ ACF_RESULT = $00000020; // hContact is last result value
+ ACF_LAST = $00000040; // use last result for DB writing
+ ACF_DBUTEXT = $00000080; // read/write Unicode string
+ ACF_DBANSI = $00000082; // read/write ANSI string
+ ACF_DBDELETE = $00000100; // delete setting
+ ACF_NOCNTCT = ACF_PARAM or ACF_CURRENT or ACF_RESULT;
+
+ // ACT_CHAIN
+ ACF_BYNAME = $00000001; // Address action link by name, not Id
+
+ // ACT_MESSAGE
+ ACF_MSG_KEEP = $00000001; // Keep past 'last result'
+
+const
+ // Variables use
+ ACF2_SRV_WPAR = $00000001;
+ ACF2_SRV_LPAR = $00000002;
+ ACF2_SRV_SRVC = $00000004;
+ ACF2_SRV_WHEX = $00000008;
+ ACF2_SRV_LHEX = $00000010;
+ ACF2_PRG_PRG = $00000001;
+ ACF2_PRG_ARG = $00000002;
+ ACF2_TXT_FILE = $00000001;
+ ACF2_TXT_TEXT = $00000002;
+ ACF2_RW_MVAR = $00000001;
+ ACF2_RW_SVAR = $00000002;
+ ACF2_RW_TVAR = $00000004;
+ ACF2_RW_HEX = $00000008;
+ ACF2_MSG_TTL = $00000001;
+ ACF2_MSG_TXT = $00000002;
+
+ ACF2_FREEMEM = $00000100;
+
+const
+ ADV_COND_NOP = 0;
+ ADV_COND_GT = 1;
+ ADV_COND_LT = 2;
+ ADV_COND_EQ = 3;
+ ADV_COND_NOT = $80;
+
+ ADV_ACT_NOP = 0; // two lower bits $03 mask
+ ADV_ACT_BREAK = 1;
+ ADV_ACT_JUMP = 2;
+
+ ADV_ACT_POST = $0F;
+
+ ADV_ACT_MATH = $10; // bit masks
+ ADV_ACT_VARS = $20;
+
+ ADV_ACTION = $F0;
+
+const
+ ACT_UNKNOWN = 0;
+ ACT_CONTACT = 1;
+ ACT_SERVICE = 2;
+ ACT_PROGRAM = 3;
+ ACT_TEXT = 4;
+ ACT_ADVANCE = 5;
+ ACT_CHAIN = 6;
+ ACT_RW = 7;
+ ACT_MESSAGE = 8;
+ ACT_MAXTYPE = 8;
+ ACT_SPECIAL = ACT_MAXTYPE+1;
+ ACT_FINISH = ACT_SPECIAL+1;
+
+type
+ pHKRecord = ^tHKRecord;
+ tHKRecord = record
+ descr :pWideChar; // like name
+ id :dword;
+ flags :dword; // Assigned or not
+ firstAction:dword; // array [0..0] of dword
+ active :pointer;
+ end;
+
+ pHKAction = ^tHKAction;
+ tHKAction = record
+ flags :dword; // See ACF_* constants
+ flags2:dword; // See ACF2_* constants (Variables use etc.)
+ next :dword;
+ descr :PWideChar;
+ case actionType:dword of
+ ACT_CONTACT:
+ (contact:THANDLE);
+ ACT_SERVICE:
+ (service:PAnsiChar;
+ wparam :WPARAM;
+ lparam :LPARAM);
+ ACT_PROGRAM:
+ (prgname:pWideChar;
+ args :pWideChar;
+ show :dword;
+ time :dword);
+ ACT_TEXT:
+ (text :pWideChar;
+ tfile :pWideChar);
+ ACT_ADVANCE:
+ (condition:dword;
+ value :uint_ptr;
+ action :dword;
+ operval :pWideChar;
+ oper :dword;
+ mathval :dword;
+ varval :pWideChar);
+ ACT_CHAIN:
+ (id :dword;
+ actname:pWideChar);
+ ACT_RW:
+ (dbcontact:THANDLE;
+ dbmodule :PAnsiChar;
+ dbsetting :PAnsiChar;
+ dbvalue :uint_ptr);
+ ACT_MESSAGE:
+ (boxopts:dword;
+ msgtitle:pWideChar;
+ msgtext :pWideChar);
+ end;
diff --git a/plugins/Actman/m_actman.h b/plugins/Actman/m_actman.h
new file mode 100644
index 0000000000..c900ba9c6c
--- /dev/null
+++ b/plugins/Actman/m_actman.h
@@ -0,0 +1,96 @@
+#ifndef M_ACTMAN
+#define M_ACTMAN
+
+#define ACCF_DISABLED 0x10000000 // action disabled
+#define ACCF_EXPORT 0x08000000 // action to export
+#define ACCF_VOLATILE 0x04000000 // don't save in DB
+#define ACCF_IMPORTED ACF_EXPORT
+#define ACCF_FLAGS (ACCF_DISABLED | ACCF_EXPORT | ACCF_IMPORTED | ACCF_VOLATILE)
+#define ACCF_ID 0x02000000 // for MS_ACT_SELECT, lParam is ID (else name)
+#define ACCF_CLEAR 0x01000000 // clear other flags, else - set
+
+
+typedef struct{
+ WCHAR* Descr;
+ DWORD ID;
+ DWORD flags; // ACCF_* flags
+ } TChain, *PChain;
+
+// Service to get list of all configured actions;
+// wParam : 0
+// lParam : address of destination list variable (address of pointer to TChain)
+// Notes: first 4 bytes of list = size of TChain structure (to add new fields in future)
+// Return value: count of elements;
+#define MS_ACT_GETLIST "Actions/GetList"
+
+// Service to free list of all configured actions got with MS_ACT_GETLIST service call;
+// wParam : 0
+// lParam : list address (pointer to ACTION returned by MS_ACT_GETLIST)
+#define MS_ACT_FREELIST "Actions/FreeList"
+
+// Service to call action defined in wParam;
+// wParam: ID of an action (see ACTION.ActID) when calling MS_ACT_RUN
+// or description of an action (see ACTION.ActDescr) when calling MS_ACT_RUNGROUP
+// lParam: parameter (will be passed to action called)
+#define MS_ACT_RUNBYID "Actions/RunById"
+#define MS_ACT_RUNBYNAME "Actions/RunByName"
+
+// Event: action group list was changed: something was added or deleted
+// wParam: set of ACTM_* flags
+// lParam : 0
+#define ME_ACT_CHANGED "Actions/Changed"
+
+// Starts action with 2 parameters
+// wParam: 0
+// lParam: pointer to TAct_Param
+
+#define MS_ACT_RUNPARAMS "Actions/RunWithParams"
+typedef struct TAct_Param
+ {
+ DWORD flags; // 0 - ID, 1 - Name
+ DWORD ID; // Id or name
+ WPARAM wParam;
+ LPARAM lParam;
+ } TAct_Param, *PAct_Param;
+
+#define ACTM_NEW 0x00000001
+#define ACTM_DELETE 0x00000002
+#define ACTM_RELOAD 0x00000004
+#define ACTM_RENAME 0x00000008
+#define ACTM_SORT 0x00000010
+#define ACTM_ACT 0x10000000 // do not check, internal
+#define ACTM_ACTS 0x20000000 // do not check, internal
+#define ACTM_LOADED 0x80000000
+
+
+#define ACIO_EXPORT 0x00000001 // export, else - import
+#define ACIO_APPEND 0x00000002 // append file on export
+#define ACIO_ASKEXIST 0x00000004 // ask, if action exists on import
+#define ACIO_SELECTED 0x00000008 // export selected actions only
+
+// wParam: ACIO_* flags
+// lParam: Unicode file name
+// Return - true, if totally succesful
+#define MS_ACT_INOUT "Actions/ImpExp"
+
+
+//Event: Export actions
+// wParam - ACIO_* flags
+// lParam - unicode filename
+#define ME_ACT_INOUT "Actions/InOut"
+
+
+// Select/unselect specified action
+// wParam: set of ACCF_* consts
+// lParam: unicode action name / number
+// Return - -1 if unsuccesful
+#define MS_ACT_SELECT "Actions/Select"
+
+
+// Event: Action started/finished
+// wParam - Action status: 0 - started, 1 - finished
+// lParam - action id
+
+#define ME_ACT_ACTION "Actions/Action"
+
+#endif
diff --git a/plugins/Actman/m_actman.inc b/plugins/Actman/m_actman.inc
new file mode 100644
index 0000000000..53344e2990
--- /dev/null
+++ b/plugins/Actman/m_actman.inc
@@ -0,0 +1,158 @@
+{$IFNDEF M_ACTMAN}
+{$DEFINE M_ACTMAN}
+
+// defined in interfaces.inc
+//const MIID_ACTMANAGER:MUUID='{9584DA04-FB4F-40c1-9325-E4F9CAAFCB5D}';
+
+const
+ AutoStartName:PWideChar = '#Autostart';
+const
+ DBBranch = 'ActMan';
+const
+ ACCF_DISABLED = $10000000; // action disabled
+ ACCF_EXPORT = $08000000; // action to export
+ ACCF_VOLATILE = $04000000; // don't save in DB
+ ACCF_IMPORTED = ACCF_EXPORT;
+ ACCF_FLAGS = ACCF_DISABLED or ACCF_EXPORT or ACCF_IMPORTED or ACCF_VOLATILE;
+ ACCF_OVERLOAD = $01000000; // imported action overwrite old
+
+ ACCF_ID = $02000000; // for MS_ACT_SELECT, lParam is ID (else name)
+ ACCF_CLEAR = $01000000; // clear other flags, else - set
+type
+ pChain = ^tChain;
+ tChain = record
+ descr:pWideChar;
+ id :dword;
+ flags:dword; // ACCF_* flags
+ order:dword;
+ end;
+
+const
+ {
+ wParam - 0
+ lParam - address of destination list variable (address of pointer to tChain)
+ if lParam=0, return just count of elements
+ Return - count of elements
+ Notes: first 4 bytes = size of TChain structure (to add new fields in future)
+ }
+ MS_ACT_GETLIST:PAnsiChar = 'Actions/GetList';
+ {
+ wParam - 0
+ lParam - list address (pointer to data returned by MS_ACT_GETLIST)
+ }
+ MS_ACT_FREELIST:PAnsiChar = 'Actions/FreeList';
+ {
+ wParam - id: dword
+ lParam - parameter
+ }
+ MS_ACT_RUNBYID :PAnsiChar = 'Actions/RunById';
+ {
+ wParam - unicode action name
+ lParam - parameter
+ }
+ MS_ACT_RUNBYNAME:PAnsiChar = 'Actions/RunByName';
+
+{ Starts action with 2 parameters
+ wParam: 0
+ lParam: pointer to TAct_Param
+}
+ MS_ACT_RUNPARAMS:PAnsiChar = 'Actions/RunWithParams';
+const
+ ACTP_BYNAME = 1;
+ ACTP_WAIT = 2;
+type
+ pAct_Param = ^tAct_Param;
+ tAct_Param = record
+ flags :dword; // ACTP_*
+ Id :uint_ptr; // Id or name
+ wParam:WPARAM;
+ lParam:LPARAM;
+ end;
+
+const
+ ACTM_NEW = $00000001;
+ ACTM_DELETE = $00000002;
+ ACTM_RELOAD = $00000004;
+ ACTM_RENAME = $00000008;
+ ACTM_SORT = $00000010;
+ ACTM_ACT = $10000000; // do not check, internal
+ ACTM_ACTS = $20000000; // do not check, internal
+ ACTM_LOADED = $80000000;
+
+ {
+ Event: action group list was changed: some was added or deleted
+ wParam - set of ACTM_* flags
+ lParam - 0
+ }
+ ME_ACT_CHANGED:PAnsiChar = 'Actions/Changed';
+
+ ACIO_EXPORT = $00000001; // export, else - import
+ ACIO_APPEND = $00000002; // append file on export
+ ACIO_ASKEXIST = $00000004; // ask, if action exists on import
+ ACIO_SELECTED = $00000008; // export selected actions only
+
+ {
+ wParam - ACIO_* flags
+ lParam - Unicode file name
+ Return - true, if totally succesful
+ }
+ MS_ACT_INOUT:PAnsiChar = 'Actions/ImpExp';
+
+ {
+ Event: Export actions
+ wParam - ACIO_* flags
+ lParam - unicode filename
+ }
+ ME_ACT_INOUT:PAnsiChar = 'Actions/InOut';
+
+ {
+ Select/unselect specified action
+ wParam - set of ACCF_* consts
+ lParam - unicode action name / number
+ Return - -1 if unsuccesful
+ }
+ MS_ACT_SELECT:PAnsiChar = 'Actions/Select';
+
+ {
+ Event: Action started/finished
+ wParam - Action status: 0 - started, 1 - finished
+ lParam - action id
+ }
+ ME_ACT_ACTION:PAnsiChar = 'Actions/Action';
+
+//----- Scheduling part services -----
+
+const
+ {
+ Enable or disable tasks
+ wParam - 1/0 (enable/disable)
+ lParam - unicode task name
+ Note - works for all tasks with same started name
+ }
+ MS_ACT_TASKENABLE:PAnsiChar = 'Actions/TaskEnable';
+
+ {
+ Delete task
+ wParam - 0
+ lParam - unicode task name
+ Note - works for all tasks with same started name
+ }
+ MS_ACT_TASKDELETE:PAnsiChar = 'Actions/TaskDelete';
+
+ {
+ Set task repeat count
+ wParam - repeat count
+ lParam - unicode task name
+ Return - old repeat count value
+ Note - works for all tasks with same started name
+ }
+ MS_ACT_TASKCOUNT:PAnsiChar = 'Actions/TaskCount';
+
+ {
+ Event for task start
+ wParam - counter of call (from 0 to repeat count)
+ lParam - unicode task name
+ }
+ ME_ACT_BELL:PAnsiChar = 'Actions/Bell';
+
+{$ENDIF}
diff --git a/plugins/Actman/make.bat b/plugins/Actman/make.bat
new file mode 100644
index 0000000000..3e448f046c
--- /dev/null
+++ b/plugins/Actman/make.bat
@@ -0,0 +1,20 @@
+@echo off
+set myopts=-dMiranda
+set dprname=actman.dpr
+
+..\delphi\brcc32.exe %myopts% options.rc -fooptions.res
+..\delphi\brcc32.exe %myopts% hooks\hooks.rc -fohooks\hooks.res
+..\delphi\brcc32.exe %myopts% tasks\tasks.rc -fotasks\tasks.res
+..\delphi\brcc32.exe %myopts% ua\ua.rc -foua\ua.res
+
+if /i '%1' == 'fpc' (
+ ..\FPC\bin\fpc.exe %myopts% %dprname% %2 %3 %4 %5 %6 %7 %8 %9
+) else if /i '%1' == 'fpc64' (
+ ..\FPC\bin64\ppcrossx64.exe %myopts% %dprname% %2 %3 %4 %5 %6 %7 %8 %9
+) else if /i '%1' == 'xe2' (
+ ..\XE2\BIN\dcc32.exe %myopts% %dprname% %2 %3 %4 %5 %6 %7 %8 %9
+) else if /i '%1' == 'xe64' (
+ ..\XE2\BIN\dcc64.exe %myopts% %dprname% %2 %3 %4 %5 %6 %7 %8 %9
+) else (
+ ..\delphi\dcc32 %myopts% %dprname% %1 %2 %3 %4 %5 %6 %7 %8 %9
+)
diff --git a/plugins/Actman/options.rc b/plugins/Actman/options.rc
new file mode 100644
index 0000000000..cc7558f392
--- /dev/null
+++ b/plugins/Actman/options.rc
@@ -0,0 +1,328 @@
+#include "i_const.inc"
+
+LANGUAGE 0,0
+/*
+IDD_STRUCTURE DIALOGEX 0, 0, 332,184, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_VISIBLE | WS_THICKFRAME
+CAPTION "Structure Editor"
+//EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ CONTROL "" , IDC_DATA_FULL, "SysListView32",
+ WS_BORDER | WS_TABSTOP |
+ LVS_NOCOLUMNHEADER | LVS_SHOWSELALWAYS |
+ LVS_SINGLESEL | LVS_REPORT,
+ 2, 2, 160, 180, WS_EX_CONTROLPARENT
+
+ CONTROL "New" ,IDC_DATA_NEW ,"MButtonClass",WS_TABSTOP,166, 2,16,16,$18000000// | WS_GROUP
+ CONTROL "Up" ,IDC_DATA_UP ,"MButtonClass",WS_TABSTOP,166,22,16,16,$18000000
+ CONTROL "Down" ,IDC_DATA_DOWN ,"MButtonClass",WS_TABSTOP,166,40,16,16,$18000000
+ CONTROL "Delete",IDC_DATA_DELETE,"MButtonClass",WS_TABSTOP,166,60,16,16,$18000000
+
+ COMBOBOX IDC_DATA_TYPE , 186, 2, 142, 96, CBS_DROPDOWNLIST | WS_VSCROLL
+ EDITTEXT IDC_DATA_LEN , 186, 18, 32, 11
+ LTEXT "Data length" ,-1 , 222, 18, 106, 11, SS_CENTERIMAGE
+ EDITTEXT IDC_DATA_EDIT, 186, 32, 142, 11, ES_AUTOHSCROLL
+ AUTOCHECKBOX "Use Variables", IDC_DATA_VARS, 186, 45, 142, 14
+
+ DEFPUSHBUTTON "&Change", IDC_DATA_CHANGE, 186, 62, 46, 14//, WS_GROUP
+ PUSHBUTTON "&OK" , IDOK , 234, 62, 46, 14
+ PUSHBUTTON "C&ancel", IDCANCEL , 282, 62, 46, 14
+
+ AUTOCHECKBOX "Packed structure", IDC_DATA_PACKED, 166, 78, 162, 14
+
+ CTEXT "Use Byte array/pointer for ANSI strings\n"\
+ "Use Word array/pointer for Unicode strings\n\n"\
+ "$## replaces by byte with hex value ##\n"\
+ "$#### replaces by word with hex value #### (for Unicode strings only)\n\n"\
+ "All data length calculating in bytes",
+ IDC_DATA_HELP,166,94,162,88
+
+}
+*/
+IDD_ACTION DIALOGEX 0, 0, 304, 226, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ CONTROL "New" ,IDC_GROUP_NEW ,"MButtonClass",WS_TABSTOP,114, 1,16,16,$18000000 | WS_GROUP
+ CONTROL "Up" ,IDC_GROUP_UP ,"MButtonClass",WS_TABSTOP,114, 18,16,16,$18000000
+ CONTROL "Down" ,IDC_GROUP_DOWN ,"MButtonClass",WS_TABSTOP,114, 34,16,16,$18000000
+ CONTROL "Reload",IDC_GROUP_RELOAD,"MButtonClass",WS_TABSTOP,114, 51,16,16,$18000000
+ CONTROL "Delete",IDC_GROUP_DELETE,"MButtonClass",WS_TABSTOP,114, 68,16,16,$18000000
+ CONTROL "Test" ,IDC_GROUP_TEST ,"MButtonClass",WS_TABSTOP,114, 85,16,16,$18000000
+ CONTROL "Export",IDC_GROUP_EXPORT,"MButtonClass",WS_TABSTOP,114,102,16,16,$18000000
+ CONTROL "Import",IDC_GROUP_IMPORT,"MButtonClass",WS_TABSTOP,114,119,16,16,$18000000
+
+ CONTROL "", IDC_ACTION_GROUP, "SysListView32",
+ WS_BORDER | WS_TABSTOP |
+ LVS_NOCOLUMNHEADER | LVS_SHOWSELALWAYS | LVS_REPORT | LVS_EDITLABELS,// | LVS_SINGLESEL,
+ 0, 2, 110, 132, WS_EX_CONTROLPARENT
+
+ CONTROL "", IDC_ACTION_LIST, "SysListView32",
+ WS_BORDER | WS_TABSTOP |
+ LVS_NOCOLUMNHEADER | LVS_SHOWSELALWAYS| LVS_REPORT | LVS_EDITLABELS,// | LVS_SINGLESEL
+ 0, 138, 110, 86, WS_EX_CONTROLPARENT
+
+ CONTROL "Help" ,IDC_ACTION_HELP ,"MButtonClass",WS_TABSTOP,114,138,16,16,$18000000 | WS_GROUP
+ CONTROL "New" ,IDC_ACTION_NEW ,"MButtonClass",WS_TABSTOP,114,156,16,16,$18000000
+ CONTROL "Up" ,IDC_ACTION_UP ,"MButtonClass",WS_TABSTOP,114,174,16,16,$18000000
+ CONTROL "Down" ,IDC_ACTION_DOWN ,"MButtonClass",WS_TABSTOP,114,190,16,16,$18000000
+ CONTROL "Delete",IDC_ACTION_DELETE,"MButtonClass",WS_TABSTOP,114,208,16,16,$18000000
+
+// PUSHBUTTON "Reset", IDC_RESET, 264, 2, 40, 12
+ GROUPBOX "" , -1, 132, 0, 172, 226
+
+ RTEXT "Action",IDC_STAT_ACTION, 135, 6, 63, 12, SS_CENTERIMAGE
+ CONTROL "", IDC_ACTION_TYPE, "ComboBoxEx32",
+ WS_TABSTOP | WS_VSCROLL | CBS_AUTOHSCROLL | CBS_DROPDOWNLIST, 200, 6, 101, 96
+
+// Contact
+ RTEXT "Choose Contact", IDC_STAT_CONTACT , 135, 24, 160, 10
+ COMBOBOX IDC_CONTACTLIST, 135, 35, 166, 128, CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL
+ AUTOCHECKBOX "Keep handle only" , IDC_CNT_KEEP , 135, 53, 160, 11
+ AUTOCHECKBOX "Active accounts only",IDC_CNT_FILTER, 135, 65, 160, 11
+ RTEXT "Dropdown list format",IDC_STAT_FORMAT, 135, 78, 160, 11
+ EDITTEXT IDC_EDIT_FORMAT , 153, 92, 142, 12, ES_AUTOHSCROLL
+ CONTROL "Apply",IDC_CNT_APPLY,"MButtonClass",WS_TABSTOP,135,90,16,16,$18000000
+ CTEXT "You can use %name%, %uid%, %account% and %group% macros",IDC_STAT_FHELP, 135, 107, 160, 24
+
+// Service
+ RTEXT "wParam type", IDC_STAT_WPAR1, 135, 125, 63, 14, SS_CENTERIMAGE
+ COMBOBOX IDC_FLAG_WPAR , 200, 125, 102, 56, CBS_DROPDOWNLIST | WS_VSCROLL
+ RTEXT "lParam type", IDC_STAT_LPAR1, 135, 140, 63, 14, SS_CENTERIMAGE
+ COMBOBOX IDC_FLAG_LPAR , 200, 140, 102, 56, CBS_DROPDOWNLIST | WS_VSCROLL
+
+ RTEXT "Service name", IDC_STAT_SERVICE, 135, 153, 160, 8
+ AUTOCHECKBOX "", IDC_SRV_SRVC,135,162,8,8,BS_ICON | BS_PUSHLIKE
+ COMBOBOX IDC_EDIT_SERVICE, 144, 162, 157, 96, CBS_DROPDOWN | WS_VSCROLL | CBS_AUTOHSCROLL | CBS_SORT
+ RTEXT "wParam" , IDC_STAT_WPAR , 135, 177, 160, 8
+ AUTOCHECKBOX "", IDC_SRV_WPAR,135,186,8,8,BS_ICON | BS_PUSHLIKE
+ COMBOBOX IDC_EDIT_WPAR , 144, 186, 157, 76, CBS_DROPDOWN | WS_VSCROLL | CBS_AUTOHSCROLL
+ RTEXT "lParam" , IDC_STAT_LPAR , 135, 201, 160, 8
+ AUTOCHECKBOX "", IDC_SRV_LPAR,135,210,8,8,BS_ICON | BS_PUSHLIKE
+ COMBOBOX IDC_EDIT_LPAR , 144, 210, 157, 76, CBS_DROPDOWN | WS_VSCROLL | CBS_AUTOHSCROLL
+ PUSHBUTTON "Structure" , IDC_WSTRUCT , 135, 186, 166, 14
+ PUSHBUTTON "Structure" , IDC_LSTRUCT , 135, 210, 166, 14
+
+ GROUPBOX "Result action" , IDC_RES_GROUP , 135, 24, 166, 99, WS_GROUP
+ AUTOCHECKBOX "Show in popup" , IDC_RES_POPUP , 138, 34, 159, 11
+ AUTOCHECKBOX "Show in messagebox" , IDC_RES_MESSAGE, 138, 45, 159, 11
+ AUTOCHECKBOX "Insert into message", IDC_RES_INSERT , 138, 56, 159, 11
+
+ LTEXT "Service result" , IDC_SRV_RESSTAT, 138, 72, 159, 11
+ COMBOBOX IDC_SRV_RESULT , 138, 83, 159, 76, CBS_DROPDOWN | WS_VSCROLL | CBS_AUTOHSCROLL
+ AUTOCHECKBOX "Free memory" , IDC_RES_FREEMEM, 138, 99, 159, 11
+ AUTOCHECKBOX "Unicode string" , IDC_RES_UNICODE, 138, 110, 159, 11
+ AUTOCHECKBOX "Signed value" , IDC_RES_SIGNED , 138, 99, 159, 11
+
+// Program
+ GROUPBOX "Process options" , IDC_PROCESS_GROUP, 135, 24, 166, 46, WS_GROUP
+ AUTORADIOBUTTON "Parallel" , IDC_FLAG_PARALLEL, 138, 33, 161, 11
+ AUTORADIOBUTTON "Continued" , IDC_FLAG_CONTINUE, 138, 44, 161, 11
+ EDITTEXT IDC_EDIT_PROCTIME, 138, 56, 31, 11, ES_RIGHT | ES_NUMBER
+ LTEXT "Process time, ms", IDC_STAT_PROCTIME, 171, 56, 128, 11, SS_CENTERIMAGE
+
+ AUTOCHECKBOX "Current path" , IDC_FLAG_CURPATH, 138, 72, 161, 11
+
+ GROUPBOX "Window option" , IDC_PRSTART_GROUP, 135, 83, 166, 55, WS_GROUP
+ AUTORADIOBUTTON "Start normal" , IDC_FLAG_NORMAL , 138, 92, 162, 11
+ AUTORADIOBUTTON "Start hidden" , IDC_FLAG_HIDDEN , 138, 103, 162, 11
+ AUTORADIOBUTTON "Start minimized", IDC_FLAG_MINIMIZE, 138, 114, 162, 11
+ AUTORADIOBUTTON "Start maximized", IDC_FLAG_MAXIMIZE, 138, 125, 162, 11
+
+ CONTROL "V", IDC_HLP_FVARS, "MButtonClass",WS_TABSTOP, 285,139,16,16,$18000000
+ RTEXT "Program path", IDC_STAT_PRGPATH, 135, 155, 160, 8
+ AUTOCHECKBOX "", IDC_PRG_PRG,135,164,8,8,BS_ICON | BS_PUSHLIKE
+ EDITTEXT IDC_EDIT_PRGPATH, 144, 164, 139, 12, ES_AUTOHSCROLL
+ PUSHBUTTON "..." , IDC_PROGRAM , 285, 164, 16, 12
+ RTEXT "Program args", IDC_STAT_PRGARGS, 135, 179, 160, 8
+ AUTOCHECKBOX "", IDC_PRG_ARG,135,186,8,8,BS_ICON | BS_PUSHLIKE
+ EDITTEXT IDC_EDIT_PRGARGS, 144, 188, 157, 12, ES_AUTOHSCROLL
+
+// Text
+ AUTORADIOBUTTON "Clipboard" , IDC_FLAG_CLIP , 135, 20, 166, 11, WS_GROUP
+ AUTORADIOBUTTON "File" , IDC_FLAG_FILE , 135, 66, 166, 11
+ AUTORADIOBUTTON "Message window", IDC_FLAG_MESSAGE, 135, 128, 166, 11
+
+ GROUPBOX "" , IDC_CLIP_GROUP , 135, 30, 166, 33
+ AUTORADIOBUTTON "Copy to" , IDC_CLIP_COPYTO, 140, 36, 160, 11, WS_GROUP
+ AUTORADIOBUTTON "Paste from", IDC_CLIP_PASTE , 140, 47, 160, 11
+
+ GROUPBOX "" , IDC_FILE_GROUP , 135, 75, 166, 52
+ AUTORADIOBUTTON "Read" , IDC_FILE_READ , 138, 83, 52, 11, WS_GROUP
+ AUTORADIOBUTTON "Write" , IDC_FILE_WRITE , 191, 83, 52, 11
+ AUTORADIOBUTTON "Append" , IDC_FILE_APPEND , 244, 83, 52, 11
+ AUTOCHECKBOX "", IDC_TXT_FILE,138,96,8,8,BS_ICON | BS_PUSHLIKE
+ EDITTEXT IDC_FILE_PATH , 147, 96, 131, 12, ES_AUTOHSCROLL
+ PUSHBUTTON "..." , IDC_FILE_FILEBTN, 281, 96, 16, 12
+ COMBOBOX IDC_FILE_ENC , 138, 111, 160, 76, CBS_DROPDOWNLIST | WS_VSCROLL
+
+ CONTROL "V", IDC_HLP_VARS, "MButtonClass",WS_TABSTOP, 285,139,16,16,$18000000
+// PUSHBUTTON "vars" , IDC_HLP_VARS , 264, 140, 37, 14
+ RTEXT "Text to insert", IDC_STAT_INSERT , 135, 155, 160, 9
+ AUTOCHECKBOX "", IDC_TXT_TEXT,135,164,8,8,BS_ICON | BS_PUSHLIKE
+ EDITTEXT IDC_EDIT_INSERT , 144, 164, 157, 59,
+ ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
+
+// Advanced
+ GROUPBOX "Condition" , IDC_CONDITION , 135, 24, 166, 34, WS_GROUP
+ AUTORADIOBUTTON ">" , IDC_FLAG_GT , 138, 33, 32, 11
+ AUTORADIOBUTTON "<" , IDC_FLAG_LT , 170, 33, 32, 11
+ AUTORADIOBUTTON "=" , IDC_FLAG_EQ , 202, 33, 32, 11
+ AUTORADIOBUTTON "NOP" , IDC_FLAG_NOP , 234, 33, 32, 11
+ AUTOCHECKBOX "NOT" , IDC_FLAG_NOT , 138, 44, 32, 11
+ RTEXT "Value" , IDC_STAT_VAL , 170, 44, 78, 11, SS_CENTERIMAGE
+ EDITTEXT IDC_ADV_VALUE , 250, 44, 48, 11, ES_RIGHT //| ES_NUMBER
+
+ AUTOCHECKBOX "Math" , IDC_FLAG_MATH , 138, 61, 64, 13, WS_GROUP
+ COMBOBOX IDC_ADV_OPER , 204, 61, 44, 96,
+ CBS_DROPDOWNLIST | WS_VSCROLL
+ EDITTEXT IDC_ADV_VAL1 , 250, 61, 48, 13, ES_RIGHT //| ES_NUMBER
+
+ AUTOCHECKBOX "Variables" , IDC_FLAG_VARS , 138, 78, 124, 12
+ CONTROL "V", IDC_ADV_HVARS, "MButtonClass",WS_TABSTOP, 285,75,16,16,$18000000
+// PUSHBUTTON "vars" , IDC_ADV_HVARS , 264, 78, 37, 12
+ EDITTEXT IDC_ADV_VARS , 144, 92, 157, 68,
+ ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
+ AUTOCHECKBOX "Result as integer", IDC_ADV_ASINT , 135, 161, 164, 13, BS_RIGHT | BS_LEFTTEXT
+
+ GROUPBOX "Operation" , IDC_OPERATION , 135, 174, 166, 49, WS_GROUP
+ AUTORADIOBUTTON "JUMP" , IDC_FLAG_JUMP , 138, 183, 62, 12
+ AUTORADIOBUTTON "BREAK" , IDC_FLAG_BREAK, 138, 196, 62, 12
+ AUTORADIOBUTTON "NOP" , IDC_FLAG_ANOP , 138, 209, 62, 12
+ COMBOBOX IDC_ADV_VAL2 , 200, 183, 99, 96, CBS_DROPDOWNLIST | WS_VSCROLL | CBS_AUTOHSCROLL
+
+// Chain
+ RTEXT "Other Action groups",IDC_STAT_GROUPS,135, 24, 160, 10
+ COMBOBOX IDC_GROUP_LIST, 135, 35, 166, 128,
+ CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | WS_VSCROLL
+
+// Read / write setting
+
+ AUTORADIOBUTTON "Own settings", IDC_RW_CURRENT, 135, 50, 80, 11, BS_RIGHT | BS_LEFTTEXT | WS_GROUP
+ AUTORADIOBUTTON "Manual" , IDC_RW_MANUAL , 135, 62, 80, 11, BS_RIGHT | BS_LEFTTEXT
+ AUTORADIOBUTTON "Parameter" , IDC_RW_PARAM , 217, 50, 80, 11
+ AUTORADIOBUTTON "Last result" , IDC_RW_RESULT , 217, 62, 80, 11
+
+ RTEXT "Module" , IDC_RW_STATM , 135, 75, 160, 8, SS_CENTERIMAGE
+ AUTOCHECKBOX "", IDC_RW_MVAR,135,84,8,8,BS_ICON | BS_PUSHLIKE
+ EDITTEXT IDC_RW_MODULE , 144, 84, 157, 12, ES_AUTOHSCROLL
+ RTEXT "Setting" , IDC_RW_STATS , 135, 99, 160, 8, SS_CENTERIMAGE
+ AUTOCHECKBOX "", IDC_RW_SVAR,135,108,8,8,BS_ICON | BS_PUSHLIKE
+ EDITTEXT IDC_RW_SETTING, 144, 108, 157, 12, ES_AUTOHSCROLL
+
+ GROUPBOX "Operation" , IDC_RW_OPER , 135, 124, 166, 21, WS_GROUP
+ AUTORADIOBUTTON "Read" , IDC_RW_READ , 138, 133, 52, 11
+ AUTORADIOBUTTON "Write" , IDC_RW_WRITE , 191, 133, 52, 11
+ AUTORADIOBUTTON "Delete" , IDC_RW_DELETE , 244, 133, 52, 11
+
+ GROUPBOX "Value" , IDC_RW_VAL , 135, 146, 166, 76, WS_GROUP
+
+ COMBOBOX IDC_RW_DATATYPE, 220, 155, 79, 96,
+ CBS_DROPDOWNLIST | WS_VSCROLL
+
+ AUTOCHECKBOX "Last result" , IDC_RW_LAST , 140, 179, 156, 11, BS_RIGHT | BS_LEFTTEXT
+ EDITTEXT IDC_RW_VALUE , 149, 191, 147, 11, ES_AUTOHSCROLL | ES_RIGHT// | ES_NUMBER
+ AUTOCHECKBOX "", IDC_RW_TVAR,140,191,8,8,BS_ICON | BS_PUSHLIKE
+ EDITTEXT IDC_RW_TEXT , 149, 191, 147, 29,
+ ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
+
+// MessageBox
+
+ AUTOCHECKBOX "", IDC_MSG_TTL,135,32,8,8,BS_ICON | BS_PUSHLIKE
+ EDITTEXT IDC_MSG_TITLE, 144, 32, 157, 12,
+ ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
+ RTEXT "Message text" ,IDC_MSG_STAT2, 137, 46, 164, 11, SS_CENTERIMAGE
+ AUTOCHECKBOX "", IDC_MSG_TXT,135,58,8,8,BS_ICON | BS_PUSHLIKE
+ EDITTEXT IDC_MSG_TEXT , 144, 58, 157, 49,
+ ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
+ AUTOCHECKBOX "Keep Last Result" , IDC_MSG_KEEP, 138, 109, 163, 11, BS_RIGHT | BS_LEFTTEXT
+
+ GROUPBOX "Icons" , IDC_MSG_ICONS , 135, 123, 166, 28, WS_GROUP
+ AUTORADIOBUTTON "Error" , IDC_MSGI_ERROR, 140, 130, 24, 20, BS_ICON
+ AUTORADIOBUTTON "Question", IDC_MSGI_QUEST, 166, 130, 24, 20, BS_ICON
+ AUTORADIOBUTTON "Warning" , IDC_MSGI_WARN , 192, 130, 24, 20, BS_ICON
+ AUTORADIOBUTTON "Info" , IDC_MSGI_INFO , 218, 130, 24, 20, BS_ICON
+ AUTORADIOBUTTON "None" , IDC_MSGI_NONE , 246, 130, 53, 20//, BS_ICON
+
+ GROUPBOX "Buttons" , IDC_MSG_BTNS, 135, 152, 166, 71, WS_GROUP
+ AUTORADIOBUTTON "OK" , IDC_MSGB_OK , 140, 161, 156, 10
+ AUTORADIOBUTTON "OK, Cancel" , IDC_MSGB_OC , 140, 171, 156, 10
+ AUTORADIOBUTTON "Abort, Retry, Ignore", IDC_MSGB_ARI, 140, 181, 156, 10
+ AUTORADIOBUTTON "Yes, No, Cancel" , IDC_MSGB_YNC, 140, 191, 156, 10
+ AUTORADIOBUTTON "Yes, No" , IDC_MSGB_YN , 140, 201, 156, 10
+ AUTORADIOBUTTON "Retry, Cancel" , IDC_MSGB_RC , 140, 211, 156, 10
+
+ RTEXT "Message title",IDC_MSG_STAT1, 137, 20, 164, 11, SS_CENTERIMAGE
+}
+
+IDD_ASK DIALOGEX 0, 0, 276, 72, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_CONTROLPARENT
+CAPTION "Choose action"
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ CTEXT "", IDC_ASK,4,4,268,42,SS_CENTERIMAGE
+
+ CONTROL "", -1, "STATIC", SS_ETCHEDHORZ, 2, 50, 272, 2
+
+ DEFPUSHBUTTON "&Yes" , IDOK , 4, 54, 40, 16
+ PUSHBUTTON "&No" , IDCANCEL , 52, 54, 40, 16
+ PUSHBUTTON "A&ppend" , IDC_APPEND, 100, 54, 52, 16
+ PUSHBUTTON "Yes to &All", IDC_YESALL, 160, 54, 52, 16
+ PUSHBUTTON "N&o to All" , IDC_NOALL , 220, 54, 52, 16
+}
+
+IDI_NEW ICON "ico\new.ico"
+IDI_UP ICON "ico\up.ico"
+IDI_DOWN ICON "ico\down.ico"
+IDI_DELETE ICON "ico\delete.ico"
+IDI_RELOAD ICON "ico\reload.ico"
+IDI_TEST ICON "ico\test.ico"
+IDI_EXPORT ICON "ico\export.ico"
+IDI_IMPORT ICON "ico\import.ico"
+
+IDI_CONTACT ICON "ico\contact.ico"
+IDI_SERVICE ICON "ico\service.ico"
+IDI_PROGRAM ICON "ico\program.ico"
+IDI_INSERT ICON "ico\insert.ico"
+IDI_ADVANCE ICON "ico\advance.ico"
+IDI_CHAIN ICON "ico\chain.ico"
+IDI_RW ICON "ico\rw.ico"
+IDI_MESSAGE ICON "ico\message.ico"
+IDI_FORMAT ICON "ico\format.ico"
+
+IDI_APPLY ICON "ico\apply.ico"
+
+IDI_VAR_CHECKED ICON "ico\vcheck.ico"
+IDI_VAR_UNCHECKED ICON "ico\vuncheck.ico"
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 0,2,0,1
+ PRODUCTVERSION 0,9,0,0
+ FILEFLAGSMASK $3F
+ FILEOS 4
+ FILETYPE 2
+ FILESUBTYPE 0
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "CompanyName",""
+ VALUE "Comments", "Plugin for managing different Miranda actions "0
+ VALUE "FileDescription", "Action manager for Miranda NG"0
+ VALUE "FileVersion", "0, 2, 0, 1 "0
+ VALUE "InternalName", "ActManager"0
+ VALUE "OriginalFilename", "actman.dll"0
+ VALUE "ProductName", "Action Manager Dynamic Link Library (DLL)"0
+ VALUE "ProductVersion", "0, 9, 0, 0 "0
+ VALUE "SpecialBuild", "18.08.2011 "0
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation",0,1200
+ END
+END
diff --git a/plugins/Actman/options.res b/plugins/Actman/options.res
new file mode 100644
index 0000000000..5c280b96d4
--- /dev/null
+++ b/plugins/Actman/options.res
Binary files differ
diff --git a/plugins/Actman/question.pas b/plugins/Actman/question.pas
new file mode 100644
index 0000000000..84bbd60604
--- /dev/null
+++ b/plugins/Actman/question.pas
@@ -0,0 +1,51 @@
+unit question;
+
+interface
+uses windows,messages;
+
+function QuestionDlg(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):LRESULT; stdcall;
+
+implementation
+
+uses m_api;
+
+{$include i_const.inc}
+
+const
+ imp_yes = 1;
+ imp_yesall = 2;
+ imp_no = 3;
+ imp_noall = 4;
+ imp_append = 5;
+
+function QuestionDlg(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):LRESULT; stdcall;
+var
+ i:integer;
+begin
+ result:=0;
+ case hMessage of
+ WM_INITDIALOG: begin
+ TranslateDialogDefault(Dialog);
+ SetDlgItemTextW(Dialog, IDC_ASK,pWideChar(lParam));
+ result:=1;
+ end;
+ WM_COMMAND: begin
+ case loword(wParam) of
+ IDOK : i:=imp_yes;
+ IDCANCEL : i:=imp_no;
+ IDC_YESALL: i:=imp_yesall;
+ IDC_NOALL : i:=imp_noall;
+ IDC_APPEND: i:=imp_append;
+ else
+ i:=0;
+ end;
+ if i<>0 then
+ begin
+ EndDialog(Dialog,i);
+ result:=1;
+ end;
+ end;
+ end;
+end;
+
+end.
diff --git a/plugins/Actman/readme.txt b/plugins/Actman/readme.txt
new file mode 100644
index 0000000000..0d1c6108b4
--- /dev/null
+++ b/plugins/Actman/readme.txt
@@ -0,0 +1,126 @@
+Note
+----
+'Actions' is action groups wich can be executed through services
+'Subactions' is single simple action like 'Call service', ' rum program' etc.
+
+Description
+-----------
+This service plugin can be used for defining, management and executing some
+actions.
+Action can be executed later through miranda service.
+
+Settings
+--------
+Main window consist of two parts. Left part is for action list and subaction list
+for selected action. Navigation buttons for action list and subaction list positioned near
+these lists.
+'New' - to add new list item
+'Delete' - to delete selected item
+'Reload' - to reload settings from DB
+'Test' - executes currently selected action
+'Up','Down' - for place selected subaction upper or lower current position
+With DblClick on ag list item you can rename this item
+
+When action list is not empty, in right side you can see combobox for subaction type selecting.
+At this moment only next types supported:
+ Open contact window
+ Call service
+ Execute program
+ Insert text
+ Advanced
+ Link to action
+ Profile
+ MessageBox
+
+-- Open contact window
+Just select needed contact from combobox.
+
+-- Call service
+This is one of hard to understand action type.
+wParam and lParam comboboxes in upper part are for parameter type.
+ 'number' - just integer number (decimal or hex started from $ sign)
+ 'ANSI string' - single-byte character string
+ 'Unicode string' - double-byte character string
+ 'Current contact' - mean what parameter is current user handle.
+ Current user is active window owner
+ 'Last result' - result from previous action or calling parameter
+ 'Parameter' - parameter from calling service
+ 'Structure' - parameter is structure which can be edited in options dialog
+ but will not saving changes in runtime.
+
+wParam and lParam in lower part are for values, passed to service.
+If service.ini file presents in plugins directory, you can press F1 to see short help
+notes for selected from combobox service name.
+Most upper part, 'result action' is for action, what we must to do with result:
+show in popup, in message window or paste into text.
+Option to translate result are: integer decimal value (signed or not), hex value or
+string value (Unicode or ANSI)
+'<proto>' in service name will be replaced by protocol name for contact handle in paremeter
+
+-- Execute program
+More hard for undestand are upper settings.
+Process settings: 'parallel' means executing next subaction or return from executing action
+immediately, 'continued' mean what next subaction will be only after finishing this program.
+Process time option is for waiting time after that runned process will be shutdowned.
+'Current path' mean what program will run with current directory as start directory.
+Window options are for starting window view: minimized, normal, maximized etc.
+Severl macros can be used in program name:
+<param> - parameter
+<last> - last result
+
+-- Insert text
+You can work with Clipboard or file/mesage window here.
+In field below action type combobox you can write some text which will be inserted
+into text field of miranda and other program.
+Or, that text can be writed to file or some text can be readed.
+If you want help for text formatting, you can press F1.
+You can use Variables plugin. If you want it, just mark checkbox. help button can
+get help notes for available variables and functions.
+
+-- Advanced
+Most ununderstable block. Upper group is conditions for operation executing.
+'Value' is value for comparing last subaction result with it.
+Next actions will be if condition is true:
+'Math' - mathematic operations with last result and presented in 'value' field value.
+After math calculations (or w/o it) can be used next operations:
+'Operation' block consist of several operations:
+ 'BREAK' - break action executing (and return for 'parent' action executing - if exist)
+ 'JUMP' - jump to subaction with selected name
+ 'NOP' - not operation
+
+As 'Math' alternative, you can use Variables plugin scripts.
+Script can be writed in text field below and checked by pressing 'Vars' button.
+Variables %subject% and %extratext% can be used in the script.
+
+-- Call chain
+Select action from combobox below. This action will be executed. After that next subaction
+will be executed.
+
+-- Database
+This subaction is for database reading/writing (see Operation block).
+Value and value type is for type of data, reading or writing from/to database.
+'Last result' mean that value is result of previous action.
+Group of radioboxes is for contact type.
+ 'Own settings' - all settings only for our contact (our profle settings)
+ 'Parameter' - settings for contact with handle, passed from start parameter
+ 'Last result' - contact handle is result of last action
+ 'Manual' - contact handle selected from combobox
+'<proto>' in module name will be replaced with contact protocol name
+
+-- MessageBox
+ Subaction uses for standard windows MessageBox showing. text <last> will be replaced
+by last result value.
+
+Using
+-----
+Executing action from option dialog is possible but not recommended.
+First way is assign action to button for toolbar (maybe through modern clist skin engine)
+Second way is assign action to hotkey (miranda core not so good for this now)
+Third way is to insert action into menu by other plugin.
+Pluginmaker Vasilich wrote his plugin (UserActions) which works with ActMan and gives
+access to ability to assign actions to some controls (menu, toolbar, hotkey).
+
+Byte/Word/DWord - numeric integer data
+byte/word pointers - pointers to some data (really, like ANSI/Unicode strings)
+byte/word array - array of mixed data (really, like ANSI/Unicode strings)
+last result/param - current working data = dword
diff --git a/plugins/Actman/services.ini b/plugins/Actman/services.ini
new file mode 100644
index 0000000000..1de3148fdd
--- /dev/null
+++ b/plugins/Actman/services.ini
@@ -0,0 +1,497 @@
+;Small Service list
+;if wparam or lparam consists of list, "|" is separator
+;in list: if translation not needed, just add space before help
+;numeric parameter format: number<space>help
+;hContact will setup "Current contact" feature
+;structure will setup "structure" feature
+;if "return" starts from int/hex/str/struct then separator, result type will set
+
+
+;[Event:Event (name or constant]
+;alias=constant name
+;descr=text
+;plugin=placement (including "core" and empty = "unknown"
+;wparam=
+;lparam=
+
+;full: full structure, with aliases
+;short: smallest needed structure
+;descr: structure description
+;plugin: where defined
+
+[Service:CListFrames/HideALLFramesTB]
+alias=MS_CLIST_FRAMES_HIDEALLFRAMESTB
+wparam=0
+lparam=0
+return=int 0, if successful
+descr=Hide All Titlebars
+
+[Service:CListFrames/ShowALLFrames]
+alias=MS_CLIST_FRAMES_SHOWALLFRAMES
+wparam=0
+lparam=0
+return=int 0, if successful
+descr=Show All Frames
+
+[Service:CListFrames/ShowALLFramesTB]
+alias=MS_CLIST_FRAMES_SHOWALLFRAMESTB
+wparam=0
+lparam=0
+return=int 0, if successful
+descr=Show All Titlebars
+
+[Service:CList/MenuBuildContact]
+alias=MS_CLIST_MENUBUILDCONTACT
+wparam=hContact
+lparam=0
+return=int hMenu handle
+descr=Built the context menu for a specific contact. Menu should be DestroyMenu()ed after done
+
+[Service:CList/SetHideOffline]
+alias=MS_CLIST_SETHIDEOFFLINE
+wparam=0 Show All Users|1 Show only Online Users|-1 Toggle status
+lparam=0
+return=int 0, if successful
+descr=Change 'hide offline contacts' option value
+
+[Service:CList/SetStatusMode]
+alias=MS_CLIST_SETSTATUSMODE
+wparam=40071 Offline|40072 Online|40073 Away|40074 DND|40075 NA|40076 Occupied|40077 Free for Chat|40078 Invisible|40079 On the Phone|40080 Out to Lunch
+lparam=0
+return=int 0, if successful
+descr=Set global status
+
+[Service:CList/ShowHide]
+alias=MS_CLIST_SHOWHIDE
+wparam=0
+lparam=0
+return=int 0, if successful
+descr=Switch contactlist status
+
+[Service:CloseAction]
+wparam=0
+lparam=0
+descr=Closes Miranda
+
+[Service:Console/Show/Hide]
+wparam=0
+lparam=0
+plugin=Console (console.dll)
+Show or hide netlog console window
+
+[Service:DB/Contact/GetCount]
+alias=MS_DB_CONTACT_GETCOUNT
+wparam=0
+lparam=0
+return=int Value
+descr=Returns contact amount, excluding user account
+
+[Service:DB/Module/Delete]
+alias=MS_DB_MODULE_DELETE
+wparam=0
+lparam=Ansi Text
+descr=Removes all settings for the specified module
+
+[Service:DBEditorpp/Import]
+alias=MS_DBEDIT_IMPORT
+wparam=hContact
+lparam=Ansi Text
+return=int 0
+descr=Import settings\contacts from file
+plugin=Database Editor++ (dbeditorpp.dll)
+
+[Service:DBEditorpp/MenuCommand]
+alias=MS_DBEDIT_MENUCOMMAND
+wparam=0
+lparam=0
+return=int 0
+plugin=Database Editor++ (dbeditorpp.dll)
+descr=Opens or activate database editor
+
+[Service:FindAdd/FindAddCommand]
+alias=MS_FINDADDFINDADD
+wparam=0
+lparam=0
+return=int 0
+descr=Opens or activate user search dialog
+
+[Service:FtMgr/Show]
+wparam=0
+lparam=0
+return=int 0
+descr=displays File Transfer window
+
+[Service:Help/AboutCommand]
+wparam=0 on Desktop|parent window handle
+lparam=0
+descr=Show window "About..."
+
+[Service:Help/IndexCommand]
+wparam=0
+lparam=0
+descr=Open support (originaly - Miranda wiki) page
+
+[Service:Help/WebsiteCommand]
+wparam=0
+lparam=0
+descr=Go to Miranda IM Homepage
+
+[Service:Help/BugCommand]
+wparam=0
+lparam=0
+descr=Open bug report page
+
+[Service:History/ShowContactHistory]
+alias=MS_HISTORY_SHOWCONTACTHISTORY
+wparam=0 System|hContact
+lparam=0
+descr=Shows contact history or (wParam=0) system history
+
+[Service:History++/EmptyHistory]
+alias=MS_HPP_EMPTYHISTORY
+wparam=hContact
+lparam=0
+plugin=History++ (historypp.dll)
+descr=Erases contact's history. hContact can be NULL(0) to empty system history
+
+[Service:History++/ShowGlobalSearch]
+alias=MS_HPP_SHOWGLOBALSEARCH
+wparam=0
+lparam=0
+plugin=History++ (historypp.dll)
+descr=Show Global history search window. If already opened, bring it to front.
+
+[Service:Ignore/Ignore]
+alias=MS_IGNORE_IGNORE
+wparam=hContact
+lparam=-1 Ignore all|1 Ignore messages|2 Ignore URLs|3 Ignore files|4 Ignore User Online|5 Ignore requests|6 Ignore 'You were added'
+return=int 0, if successful
+descr=Ignore Contact
+
+[Service:Ignore/Unignore]
+alias=MS_IGNORE_UNIGNORE
+wparam=hContact
+lparam=-1 Ignore all|1 Ignore messages|2 Ignore URLs|3 Ignore files|4 Ignore User Online|5 Ignore requests|6 Ignore 'You were added'
+return=int 0, if successful
+descr=Unignore Contact
+
+[Service:mDynDNS/GetIP]
+wparam=0 auto|1 mDynDNS-checkip|2 DNS querry
+lparam=structure|*b.arr 16|
+return=struct
+descr=Returns the IP (emty string on failure)
+
+[Service:MIMLocker/Lock]
+wparam=0
+wparam=0
+plugin=MIMLocker (MIMLocker.dll)
+descr=Locks & hides Miranda's contact list and message sessions until password is entered
+
+[Service:Miranda/System/Restart]
+alias=MS_SYSTEM_RESTART
+wparam=0
+lparam=0
+descr=Restarts Miranda (try to use together with CloseAction service) ver.0.8+
+
+[Service:mRadio/PlayStop]
+alias=MS_RADIO_PLAYSTOP
+wparam=hContact|Station name
+lparam=0 wParam is Handle|1 wParam is Ansi station name|2 wParam is Unicode station name
+descr=Starting or stopping radio station
+
+[Service:MyDetails/CicleThroughtProtocols]
+alias=MS_MYDETAILS_CICLE_THROUGHT_PROTOCOLS
+wparam=0 Stop cycle|1 Start cycle
+lparam=0
+return=int 0, if successful
+plugin=My Details (mydetails.dll)
+descr=Start/stops the cycling throught protocols
+
+[Service:MyDetails/SetMyAvatarUI]
+alias=MS_MYDETAILS_SETMYAVATARUI
+wparam=0
+lparam=0 All protocols|Protocol
+return=signed -2 if proto can't set this, -1 on protocol not found, else 0
+plugin=My Details (mydetails.dll)
+descr=Shows a dialog to set the avatar for all possible protocols
+
+[Service:MyDetails/ShowNextProtocol]
+alias=MS_MYDETAILS_SHOWNEXTPROTOCOL
+wparam=0
+lparam=0
+return=int 0, if successful
+plugin=My Details (mydetails.dll)
+descr=Shows the next protocol in the frame
+
+[Service:MyDetails/ShowPreviousProtocol]
+alias=MS_MYDETAILS_SHOWPREVIOUSPROTOCOL
+wparam=0
+lparam=0
+return=int 0, if successful
+plugin=My Details (mydetails.dll)
+descr=Shows the previous protocol in the frame
+
+[Service:Options/OptionsCommand]
+wparam=0
+lparam=0
+descr=Open Options dialog
+
+[Service:Opt/OpenOptions]
+alias=MS_OPT_OPENOPTIONS
+wparam=0
+lparam=structure|0|native|bptr|bptr|bptr|
+return=int 0, if successful
+descr=Opens the options dialog, optionally at the specified page
+
+[Service:PopUp/EnableDisableMenuCommand]
+wparam=0
+lparam=0
+plugin=Popup Plus (popup.dll)
+descr=Enables or disables PopUp windows
+
+[Service:PopUp/ShowMessage]
+alias=MS_POPUP_SHOWMESSAGE
+wparam=Ansi Text
+lparam=1 Warning|2 Notify|3 Error
+return=int 0, if successful
+plugin=YAPP or PopUp
+descr=Popup window
+
+[Service:PopUp/ShowMessageW]
+alias=MS_POPUP_SHOWMESSAGEW
+wparam=Unicode Text
+lparam=1 Warning|2 Notify|3 Error
+return=int 0, if successful
+plugin=YAPP only
+descr=Popup window
+
+[Service:PopUp/ToggleEnabled]
+wparam=0
+lparam=0
+plugin=YAPP (yapp.dll)
+descr=Enables or disables PopUp windows
+
+[Service:Proto/CallContactService]
+alias=MS_PROTO_CALLCONTACTSERVICE
+wparam=0
+lparam=structure|0|native|b.ptr|native|native|
+return=result of protocol service call
+descr=send a general request through the protocol chain for a contact
+
+[Service:Protos/ShowAccountManager]
+alias=MS_PROTO_SHOWACCMGR
+wparam=0
+lparam=0
+descr=displays the Account Manager
+
+[Service:QuickContacts/ShowDialog]
+alias=MS_QC_SHOW_DIALOG
+wparam=0
+lparam=0
+plugin=Quick Contacts (quickcontacts.dll)
+descr=Show the dialog to select the contact
+
+[Service:QuickSearch_PLUGIN/Show]
+wparam=0|filter text
+lparam=0 wparam is unicode|1 wparam is Ansi|2 reserved
+plugin=Quick Search (Mod) (quicksearch.dll)
+descr=
+
+[Service:Skin/Sounds/Play]
+alias=MS_SKIN_PLAYSOUND
+wparam=0
+lparam=Name
+descr=Plays sound added through Skin/Sounds/AddNew. If sound not found, standard Windows sound plays
+
+[Service:SREMail/SendCommand]
+alias=MS_EMAIL_SENDEMAIL
+wparam=hContact
+lparam=0
+return=int 0, if successful
+descr=Send Email to contact
+
+[Service:SRFile/GetReceivedFilesFolder]
+alias=MS_FILE_GETRECEIVEDFILESFOLDER
+wparam=hContact
+lparam=structure|*b.arr 300|
+return=struct
+descr=Returns the received files folder for a contact
+
+[Service:SRFile/OpenContRecDir]
+wparam=hContact
+lparam=0
+descr=Open contact received file directory
+
+[Service:SRFile/SendCommand]
+alias=MS_FILE_SENDFILE
+wparam=hContact
+lparam=0
+return=int 0, if successful
+descr=Send file to contact.
+
+[Service:SRMsg/SendCommand]
+alias=MS_MSG_SENDMESSAGE
+wparam=hContact;parameter
+lparam=0
+descr=Opens message window for contact with handle in wparam
+
+[Service:StopSpam/RemoveTempContacts]
+alias=MS_STOPSPAM_REMTEMPCONTACTS
+wparam=0
+lparam=0
+return=int 0
+plugin=StopSpam (stopspam.dll)
+descr=remove all temporary contacts from db
+
+[Service:SV_Avatars/ContactOptions]
+alias=MS_AV_CONTACTOPTIONS
+wparam=hContact
+lparam=0
+plugin=Avatar service (loadavatars.dll)
+descr=Call avatar option dialog for contact
+
+[Service:SV_Avatars/SetAvatar]
+alias=MS_AV_SETAVATAR
+wparam=hContact
+lparam=0|Filename
+plugin=Avatar service (loadavatars.dll)
+descr=Set (and optionally protect) a local contact picture for the given hContact. If lParam = NIL, the service will open a file selection dialog.
+
+[Service:Update/CheckForUpdates]
+wparam=0
+lparam=0
+return=int 0
+plugin=Updater (updater.dll)
+descr=Check for plugin updates
+
+[Service:UserInfo/ShowDialog]
+alias=MS_USERINFO_SHOWDIALOG
+wparam=0 System|hContact
+lparam=0
+plugin=Extended UserInfo (uinfoex.dll)
+descr=Shows contact property window.
+
+[Service:Utils/OpenURL]
+alias=MS_UTILS_OPENURL
+wparam=0 Open URL in current window
+lparam=URL
+return=int 0
+descr=Open URL in default browser
+
+[Service:Versioninfo/GetInfo]
+alias=MS_VERSIONINFO_GETINFO
+wparam=0 With formating|1 Don't use formating
+lparam=structure|*b.ptr 0|
+plugin=VersionInfo
+return=int 0, if succesful
+descr=Returns a string containing the versioninfo post
+
+[Service:VersionInfo/MenuCommand]
+alias=MS_VERSIONINFO_MENU_COMMAND
+wparam=0
+lparam=0
+plugin=VersionInfo
+descr=Show or save (call default action) Modules version Info
+
+[Service:WATrack/ShowMusicInfo]
+alias=MS_WAT_SHOWMUSICINFO
+wparam=0
+lparam=0
+plugin=Winamp Track (watrack.dll)
+descr=Show popup or Info window with current music information.
+
+[Service:WATrack/MakeReport]
+alias=MS_WAT_MAKEREPORT
+wparam=log filename|
+lparam=report filename|
+return=int 0, if unsuccessful
+plugin=Winamp Track (watrack.dll)
+descr=Create report from log and run it (if option is set). If wParam or lParam is empty then file names from options are used.
+
+[Service:WhenWasIt/List/Show]
+alias=MS_WWI_LIST_SHOW
+wparam=0
+lparam=0
+plugin=WhenWasIt Birthday Reminder (whenwasit.dll)
+descr=display birthdays window
+
+[Service:<proto>/Bookmarks]
+wparam=0
+lparam=0
+plugin=Jabber
+descr=Manage Jabber Bookmarks
+
+[Service:<proto>/SetAwayMsg]
+alias=PS_SETAWAYMSG
+wparam=40071 Offline|40072 Online|40073 Away|40074 DND|40075 NA|40076 Occupied|40077 Free for Chat|40078 Invisible|40079 On the Phone|40080 Out to Lunch
+lparam=text
+return=int 0, if successful
+descr=Set status message
+
+[Service:<proto>/SetStatus]
+alias=PS_SETSTATUS
+wparam=40071 Offline|40072 Online|40073 Away|40074 DND|40075 NA|40076 Occupied|40077 Free for Chat|40078 Invisible|40079 On the Phone|40080 Out to Lunch
+lparam=0
+return=int 0, if successful
+descr=Set protocol status
+
+[Service:<proto>/SetXStatus]
+alias=PS_ICQ_SETCUSTOMSTATUS
+;alias=JS_SETXSTATUSEX
+wparam=0 None|1 Angry|2 Taking a bath|3 Tired|4 Party|5 Drinking beer|6 Thinking|7 Eating|8 Watching TV|9 Meeting|10 Coffee|11 Listening to music|12 Business|13 Shooting|14 Having fun|15 On the phone|16 Gaming|17 Studying|18 Shopping|19 Feeling sick|20 Sleeping|21 Surfing|22 Browsing|23 Working|24 Typing|25 Picnic|26 Cooking|27 Smoking|28 I'm high|29 On WC|30 To be or not to be|31 Watching pro7 on TV|32 Love
+lparam=0
+plugin=ICQ
+descr=Sets owner current custom status
+
+[Service:<proto>/ShowXStatusDetails]
+alias=MS_XSTATUS_SHOWDETAILS
+wparam=0|hContact
+lparam=0
+plugin=ICQ
+descr=Display XStatus detail
+
+[Event:Actions/Changed]
+alias=ME_ACT_CHANGED
+plugin=ActMan
+descr='action group list was changed: some was added or deleted'
+wparam=ACTM_NEW|ACTM_DELETE|ACTM_RELOAD|ACTM_RENAME|ACTM_SORT|ACTM_LOADED
+lparam=0
+
+[Event:CList/PreBuildContactMenu]
+alias=ME_CLIST_PREBUILDCONTACTMENU
+plugin=contact list
+descr='the context menu for a contact is about to be built'
+wparam=hContact
+lparam=0
+
+[Event:CList/DoubleClicked]
+alias=ME_CLIST_DOUBLECLICKED
+plugin=contact list
+descr='double click on the CList'
+wparam=hContact
+lparam=0
+
+[Event:DB/Contact/Added]
+alias=ME_DB_CONTACT_ADDED
+plugin=database driver
+descr='New contact added to database'
+wparam=hContact
+lparam=0
+
+[Event:DB/Contact/Deleted]
+alias=ME_DB_CONTACT_DELETED
+plugin=database driver
+descr='Contact deleting'
+wparam=hContact
+lparam=0
+
+[Structure:CCSDATA]
+; variant: Handle -> param
+full=0| \
+param (HANDLE) hContact| \
+b.ptr (const char *) szProtoService| \
+native (WPARAM) wParam| \
+native (LPARAM) lParam|
+short=0|param|b.ptr|native|native|
+descr=
+plugin=
diff --git a/plugins/Actman/tasks/i_opt_dlg.inc b/plugins/Actman/tasks/i_opt_dlg.inc
new file mode 100644
index 0000000000..c8025c278d
--- /dev/null
+++ b/plugins/Actman/tasks/i_opt_dlg.inc
@@ -0,0 +1,536 @@
+{}
+const
+ settings:HWND = 0;
+
+var
+ OldTableProc:pointer;
+ onactchanged:THANDLE;
+
+const
+ ACI_NEW :PAnsiChar = 'ACI_New';
+ ACI_DELETE :PAnsiChar = 'ACI_Delete';
+
+procedure CheckTaskList(Dialog:HWND;enable:boolean);
+begin
+ if not enable then
+ enable:=SendMessage(GetDlgItem(Dialog,IDC_TASK_NAME),LVM_GETITEMCOUNT,0,0)>0;
+
+ EnableWindow(GetDlgItem(Dialog,IDC_TASK_ABSOLUTE ),enable);
+ EnableWindow(GetDlgItem(Dialog,IDC_TASK_DATEV ),enable);
+ EnableWindow(GetDlgItem(Dialog,IDC_TASK_DAYSV ),enable);
+ EnableWindow(GetDlgItem(Dialog,IDC_TASK_TIMEV ),enable);
+ EnableWindow(GetDlgItem(Dialog,IDC_TASK_REPEAT ),enable);
+ EnableWindow(GetDlgItem(Dialog,IDC_TASK_INTERVAL ),enable);
+ EnableWindow(GetDlgItem(Dialog,IDC_TASK_BREAK ),enable);
+ EnableWindow(GetDlgItem(Dialog,IDC_TASK_EVENT ),enable);
+ EnableWindow(GetDlgItem(Dialog,IDC_TASK_ONCE ),enable);
+ if not enable then
+ begin
+ ShowWindow(GetDlgItem(Dialog,IDC_TASK_DAYST),SW_HIDE);
+ ShowWindow(GetDlgItem(Dialog,IDC_TASK_DAYSV),SW_HIDE);
+ end;
+end;
+
+procedure FillTaskList(wnd:HWND);
+var
+ i:integer;
+ li:LV_ITEMW;
+begin
+ SendMessage(wnd,LVM_DELETEALLITEMS,0,0);
+ for i:=0 to MaxTasks-1 do
+ begin
+ with TaskList[i] do
+ begin
+ if (flags and ACF_ASSIGNED)<>0 then
+ begin
+ li.mask :=LVIF_TEXT+LVIF_PARAM;
+ li.iSubItem:=0;
+ li.iItem :=i;
+ li.lParam :=i;
+ li.pszText :=name;
+ li.iItem :=SendMessageW(wnd,LVM_INSERTITEMW,0,LPARAM(@li));
+ ListView_SetCheckState(wnd,li.iItem,(flags and ACF_DISABLED)=0);
+ end;
+ end;
+ end;
+ ListView_SetItemState(wnd,0,
+ LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+end;
+
+procedure ClearTaskData(Dialog:HWND);
+var
+ st:TSystemTime;
+begin
+ SendMessage(GetDlgItem(Dialog,IDC_TASK_ABSOLUTE),CB_SETCURSEL,0,0);
+ CheckDlgButton(Dialog,IDC_TASK_BREAK ,BST_UNCHECKED);
+ CheckDlgButton(Dialog,IDC_TASK_EVENT ,BST_UNCHECKED);
+ CheckDlgButton(Dialog,IDC_TASK_ONCE ,BST_UNCHECKED);
+ EnableWindow(GetDlgItem(Dialog,IDC_TASK_ONCE),false);
+
+ SetDlgItemInt(Dialog,IDC_TASK_DAYSV ,1,false);
+ SetDlgItemInt(Dialog,IDC_TASK_REPEAT,0,false);
+
+ FillChar(st,SizeOf(st),0);
+ SendDlgItemMessage(Dialog,IDC_TASK_TIMEV ,DTM_SETSYSTEMTIME,GDT_VALID,lParam(@st));
+ SendDlgItemMessage(Dialog,IDC_TASK_INTERVAL,DTM_SETSYSTEMTIME,GDT_VALID,lParam(@st));
+{
+ ShowWindow(GetDlgItem(Dialog,IDC_TASK_DATET),SW_SHOW);
+ ShowWindow(GetDlgItem(Dialog,IDC_TASK_DATEV),SW_SHOW);
+ ShowWindow(GetDlgItem(Dialog,IDC_TASK_DAYST),SW_HIDE);
+ ShowWindow(GetDlgItem(Dialog,IDC_TASK_DAYSV),SW_HIDE);
+}
+end;
+
+procedure ShowDateType(Dialog:HWND;start:integer);
+var
+ sh1,sh2,sh3:integer;
+begin
+ case start of
+ 1: begin // start after
+ sh1:=SW_HIDE;
+ sh2:=SW_SHOW;
+ sh3:=SW_SHOW;
+ end;
+ 2: begin // start from
+ sh1:=SW_SHOW;
+ sh2:=SW_HIDE;
+ sh3:=SW_SHOW;
+ end;
+ else
+ begin
+// 3: begin // start immediately
+ sh1:=SW_HIDE;
+ sh2:=SW_HIDE;
+ sh3:=SW_HIDE;
+ end;
+ end;
+
+ ShowWindow(GetDlgItem(Dialog,IDC_TASK_DATET),sh1);
+ ShowWindow(GetDlgItem(Dialog,IDC_TASK_DATEV),sh1);
+ ShowWindow(GetDlgItem(Dialog,IDC_TASK_DAYST),sh2);
+ ShowWindow(GetDlgItem(Dialog,IDC_TASK_DAYSV),sh2);
+ ShowWindow(GetDlgItem(Dialog,IDC_TASK_TIMET),sh3);
+ ShowWindow(GetDlgItem(Dialog,IDC_TASK_TIMEV),sh3);
+end;
+
+procedure ShowTaskData(Dialog:HWND; item:integer=-1);
+var
+ st:TSystemTime;
+ lwnd:HWND;
+ start:integer;
+begin
+ lwnd:=settings;
+ settings:=0;
+
+ ClearTaskData(Dialog);
+
+ with TaskList[LV_GetLParam(GetDlgItem(Dialog,IDC_TASK_NAME),item)] do
+ begin
+ // flags
+
+ if (flags and TCF_NONZEROBREAK)<>0 then
+ CheckDlgButton(Dialog,IDC_TASK_BREAK,BST_CHECKED);
+ if (flags and TCF_MAKEEVENT)<>0 then
+ CheckDlgButton(Dialog,IDC_TASK_EVENT,BST_CHECKED);
+ if (flags and TCF_EVENTONCE)<>0 then
+ CheckDlgButton(Dialog,IDC_TASK_ONCE,BST_CHECKED);
+
+ EnableWindow(GetDlgItem(Dialog,IDC_TASK_ONCE),
+ IsDlgButtonChecked(Dialog,IDC_TASK_EVENT)<>BST_UNCHECKED);
+
+ // action
+ CB_SelectData(GetDlgItem(Dialog,IDC_TASK_ACTION),action);
+ // times
+ FileTimeToSystemTime(starttime,st);
+
+ if (flags and TCF_IMMEDIATELY)<>0 then
+ begin
+ start:=3;
+ end
+ else if (flags and TCF_ABSOLUTE)<>0 then
+ begin
+ start:=2;
+ SendDlgItemMessage(Dialog,IDC_TASK_DATEV,DTM_SETSYSTEMTIME,GDT_VALID,lParam(@st))
+ end
+ else
+ begin
+ start:=1;
+ SetDlgItemInt(Dialog,IDC_TASK_DAYSV,dayoffset,false);
+ end;
+ CB_SelectData(GetDlgItem(Dialog,IDC_TASK_ABSOLUTE),start);
+
+ SendDlgItemMessage(Dialog,IDC_TASK_TIMEV,DTM_SETSYSTEMTIME,GDT_VALID,lParam(@st));
+
+ SetDlgItemInt(Dialog,IDC_TASK_REPEAT,count,true);
+
+ FileTimeToSystemTime(interval,st);
+ SendDlgItemMessage(Dialog,IDC_TASK_INTERVAL,DTM_SETSYSTEMTIME,GDT_VALID,lParam(@st));
+ SetDlgItemInt(Dialog,IDC_TASK_INTDAYS,intdays,false);
+ end;
+
+ ShowDateType(Dialog,start);
+
+ settings:=lwnd;
+end;
+
+procedure SaveTaskData(Dialog:HWND; item:integer=-1);
+var
+ wnd:HWND;
+ li:LV_ITEM;
+ st,st1:TSystemTime;
+ tmp:longbool;
+begin
+ wnd:=GetDlgItem(Dialog,IDC_TASK_NAME);
+
+ if item<0 then
+ li.iItem:=SendMessage(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED) // LVNI_SELECTED
+ else
+ li.iItem:=item;
+
+ if li.iItem>=0 then
+ begin
+ li.mask :=LVIF_PARAM;
+ li.iSubItem :=0;
+ SendMessageW(wnd,LVM_GETITEMW,0,LPARAM(@li));
+
+ with TaskList[li.lParam] do
+ begin
+ if (flags and ACF_ASSIGNED)<>0 then
+ begin
+ flags:=ACF_ASSIGNED;
+ // flags
+ if ListView_GetCheckState(wnd,li.iItem)=0 then
+ flags:=flags or ACF_DISABLED;
+
+ if IsDlgButtonChecked(Dialog,IDC_TASK_BREAK)<>BST_UNCHECKED then
+ flags:=flags or TCF_NONZEROBREAK;
+ if IsDlgButtonChecked(Dialog,IDC_TASK_EVENT)<>BST_UNCHECKED then
+ begin
+ flags:=flags or TCF_MAKEEVENT;
+ if IsDlgButtonChecked(Dialog,IDC_TASK_ONCE )<>BST_UNCHECKED then
+ flags:=flags or TCF_EVENTONCE;
+ end;
+ // action
+ action:=CB_GetData(GetDlgItem(Dialog,IDC_TASK_ACTION));
+ // times
+ SendDlgItemMessage(Dialog,IDC_TASK_TIMEV,DTM_GETSYSTEMTIME,0,lParam(@st));
+
+ case CB_GetData(GetDlgItem(Dialog,IDC_TASK_ABSOLUTE)) of
+ 1: begin
+ dayoffset:=GetDlgItemInt(Dialog,IDC_TASK_DAYSV,tmp,false);
+ end;
+ 2: begin
+ flags:=flags or TCF_ABSOLUTE;
+ SendDlgItemMessage(Dialog,IDC_TASK_DATEV,DTM_GETSYSTEMTIME,0,lParam(@st1));
+ st.wYear :=st1.wYear;
+ st.wMonth :=st1.wMonth;
+ st.wDayOfWeek:=st1.wDayOfWeek;
+ st.wDay :=st1.wDay;
+ end;
+ 3: begin
+ flags:=flags or TCF_IMMEDIATELY;
+ end;
+ end;
+ SystemTimeToFileTime(st,starttime);
+
+ count:=GetDlgItemInt(Dialog,IDC_TASK_REPEAT,tmp,true);
+
+ SendDlgItemMessage(Dialog,IDC_TASK_INTERVAL,DTM_GETSYSTEMTIME,0,lParam(@st));
+ SystemTimeToFileTime(st,interval);
+ intdays:=GetDlgItemInt(Dialog,IDC_TASK_INTDAYS,tmp,false);
+ end;
+ end;
+ end;
+end;
+
+function NewTask(Dialog:HWND;item:integer=-1):integer;
+var
+ wnd:HWND;
+ li:LV_ITEMW;
+begin
+ wnd:=GetDlgItem(Dialog,IDC_TASK_NAME);
+ if item<0 then
+ li.iItem :=SendMessage(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED)+1
+ else
+ li.iItem :=item;
+ li.iSubItem:=0;
+ li.mask :=LVIF_TEXT + LVIF_PARAM;
+ li.lParam :=CreateNewTask;
+ li.pszText :=TranslateW('Task sample');
+ result:=SendMessageW(wnd,LVM_INSERTITEMW,0,LPARAM(@li));
+
+ ListView_SetCheckState(wnd,li.iItem,
+ (TaskList[li.lParam].flags and ACF_DISABLED)=0);
+ StrDupW(TaskList[li.lParam].name,li.pszText);
+
+ CheckTaskList(Dialog,true);
+
+ if li.iItem=0 then
+ Listview_SetItemState(wnd,0,LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+end;
+
+function DeleteTask(Dialog:HWND):integer;
+var
+ li:LV_ITEM;
+ wnd:HWND;
+ i:integer;
+begin
+ result:=0;
+ wnd:=GetDlgItem(Dialog,IDC_TASK_NAME);
+ for i:=ListView_GetItemCount(wnd)-1 downto 0 do
+ begin
+ if ListView_GetItemState(wnd,i,LVIS_SELECTED)<>0 then
+ begin
+ li.iItem :=i;
+ li.mask :=LVIF_PARAM;
+ li.iSubItem :=0;
+ SendMessageW(wnd,LVM_GETITEMW,0,LPARAM(@li));
+
+ TaskList[li.lParam].flags:=TaskList[li.lParam].flags and not ACF_ASSIGNED;
+
+ SendMessage(wnd,LVM_DELETEITEM,i,0);
+ end;
+ end;
+ Listview_SetItemState(wnd,0,LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+
+ CheckTaskList(Dialog,false);
+end;
+
+function NewHKTableProc(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+var
+ i:integer;
+begin
+ result:=0;
+ case hMessage of
+ WM_KEYDOWN: begin
+ if (lParam and (1 shl 30))=0 then
+ begin
+ case wParam of
+ VK_F2: begin
+ i:=SendMessage(Dialog,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
+ if i>=0 then
+ PostMessageW(Dialog,LVM_EDITLABELW,i,0);
+ exit;
+ end;
+
+ VK_INSERT: begin
+ PostMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDC_TASK_NEW,0);
+ exit;
+ end;
+
+ VK_DELETE: begin
+ PostMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDC_TASK_DELETE,0);
+ exit;
+ end;
+ end;
+ end;
+ end;
+ end;
+ result:=CallWindowProc(OldTableProc,Dialog,hMessage,wParam,lParam);
+end;
+
+procedure FillStartTimeList(wnd:HWND);
+begin
+ SendMessage(wnd,CB_RESETCONTENT,0,0);
+ CB_AddStrDataW(wnd,TranslateW('Starting after' ),1);
+ CB_AddStrDataW(wnd,TranslateW('Starting from' ),2);
+ CB_AddStrDataW(wnd,TranslateW('Start immediately'),3);
+ SendMessage(wnd,CB_SETCURSEL,0,0);
+end;
+
+procedure FillActionList(wnd:HWND);
+var
+ ptr,ptr1:pChain;
+ i,cnt:integer;
+begin
+ cnt:=CallService(MS_ACT_GETLIST,0,LPARAM(@ptr));
+ SendMessage(wnd,CB_RESETCONTENT,0,0);
+ if cnt>0 then
+ begin
+ ptr1:=ptr;
+ inc(pbyte(ptr),4);
+ for i:=0 to cnt-1 do
+ begin
+ CB_AddStrDataW(wnd,ptr^.descr,ptr^.id);
+ inc(ptr);
+ end;
+
+ CallService(MS_ACT_FREELIST,0,LPARAM(ptr1));
+ SendMessage(wnd,CB_SETCURSEL,0,0);
+ end;
+end;
+
+function ActListChange(wParam:WPARAM;lParam:LPARAM):integer; cdecl;
+begin
+ result:=0;
+ if settings<>0 then
+ FillActionList(GetDlgItem(settings,IDC_TASK_ACTION));
+end;
+
+procedure SetIcons(Dialog:HWND);
+var
+ ti:TTOOLINFOW;
+ hwndTooltip:HWND;
+begin
+ hwndTooltip:=CreateWindowW(TOOLTIPS_CLASS,nil,TTS_ALWAYSTIP,
+ integer(CW_USEDEFAULT),integer(CW_USEDEFAULT),
+ integer(CW_USEDEFAULT),integer(CW_USEDEFAULT),
+ Dialog,0,hInstance,nil);
+
+ FillChar(ti,SizeOf(ti),0);
+ ti.cbSize :=sizeof(TOOLINFO);
+ ti.uFlags :=TTF_IDISHWND or TTF_SUBCLASS;
+ ti.hwnd :=dialog;
+ ti.hinst :=hInstance;
+{
+ ti.uId :=GetDlgItem(Dialog,IDC_EVENT_HELP);
+ ti.lpszText:=TranslateW('Help');
+ SendMessage(ti.uId,BM_SETIMAGE,IMAGE_ICON,
+ CallService(MS_SKIN_LOADICON,SKINICON_OTHER_HELP,0));
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,LPARAM(@ti));
+}
+ ti.uId :=GetDlgItem(Dialog,IDC_TASK_NEW);
+ ti.lpszText:=TranslateW('New');
+ SetButtonIcon(ti.uId,ACI_NEW);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,LPARAM(@ti));
+
+ ti.uId :=GetDlgItem(Dialog,IDC_TASK_DELETE);
+ ti.lpszText:=TranslateW('Delete');
+ SetButtonIcon(ti.uId,ACI_DELETE);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,LPARAM(@ti));
+end;
+
+function DlgProcOpt(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+var
+ wnd:HWND;
+ lv:LV_COLUMNW;
+ li:LV_ITEMW;
+ i:integer;
+begin
+ result:=0;
+ case hMessage of
+ WM_CLOSE: begin
+ UnhookEvent(onactchanged);
+ settings:=0;
+ end;
+
+ WM_INITDIALOG: begin
+ settings:=0;
+ wnd:=GetDlgItem(Dialog,IDC_TASK_NAME);
+ SendMessage(wnd,LVM_SETEXTENDEDLISTVIEWSTYLE,LVS_EX_CHECKBOXES,LVS_EX_CHECKBOXES);
+ SendMessage(wnd,LVM_SETUNICODEFORMAT,1,0);
+ zeromemory(@lv,sizeof(lv));
+ lv.mask:=LVCF_WIDTH;
+ lv.cx :=110;
+ SendMessageW(wnd,LVM_INSERTCOLUMNW ,0,tlparam(@lv));
+ SendMessageW(wnd,LVM_SETCOLUMNWIDTH,0,LVSCW_AUTOSIZE_USEHEADER);
+
+ CreateUpDownControl(
+ WS_CHILD+WS_BORDER+WS_VISIBLE+UDS_ARROWKEYS+UDS_SETBUDDYINT+UDS_ALIGNRIGHT,
+ 190,112,14,18,
+ Dialog, IDC_TASK_UPDOWN, hInstance, GetDlgItem(Dialog,IDC_TASK_REPEAT),
+ 10000, -1, 0);
+
+ OldTableProc:=pointer(SetWindowLongPtrW(wnd,GWL_WNDPROC,LONG_PTR(@NewHKTableProc)));
+ TranslateDialogDefault(Dialog);
+
+ SetIcons(Dialog);
+
+ FillActionList(GetDlgItem(Dialog,IDC_TASK_ACTION));
+ FillStartTimeList(GetDlgItem(Dialog,IDC_TASK_ABSOLUTE));
+ FillTaskList(wnd);
+ CheckTaskList(Dialog,false);
+ onactchanged:=HookEvent(ME_ACT_CHANGED,@ActListChange);
+ settings:=Dialog;
+ end;
+
+ WM_COMMAND: begin
+ case wParam shr 16 of
+ CBN_SELCHANGE: begin
+ ShowDateType(Dialog,CB_GetData(lParam));
+ end;
+
+ EN_CHANGE: begin
+ end;
+
+ BN_CLICKED: begin
+ case loword(wParam) of
+ IDC_TASK_NEW : NewTask(Dialog);
+ IDC_TASK_DELETE: DeleteTask(Dialog);
+
+ IDC_TASK_EVENT: begin
+ EnableWindow(GetDlgItem(Dialog,IDC_TASK_ONCE),
+ IsDlgButtonChecked(Dialog,IDC_TASK_EVENT)<>BST_UNCHECKED);
+ end;
+ end;
+ end;
+ end;
+ if settings<>0 then
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+
+ WM_NOTIFY: begin
+ case integer(PNMHdr(lParam)^.code) of
+ PSN_APPLY: begin
+ SaveTaskData(Dialog);
+ SaveTasks;
+ SetAllTasks;
+ end;
+
+ DTN_DATETIMECHANGE: begin
+ if settings<>0 then
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+
+ NM_DBLCLK: begin
+ if PNMListView(lParam)^.iItem>=0 then
+ PostMessageW(PNMHdr(lParam)^.hWndFrom,LVM_EDITLABELW,
+ PNMListView(lParam)^.iItem,0);
+ end;
+
+ LVN_ITEMCHANGED: begin
+ if PNMLISTVIEW(lParam)^.uChanged=LVIF_STATE then
+ begin
+ i:=(PNMLISTVIEW(lParam)^.uOldState and LVNI_FOCUSED)-
+ (PNMLISTVIEW(lParam)^.uNewState and LVNI_FOCUSED);
+
+ if i>0 then // old focus
+ SaveTaskData(Dialog,PNMLISTVIEW(lParam)^.iItem)
+ else if i<0 then // new focus
+ begin
+ ShowTaskData(Dialog,PNMLISTVIEW(lParam)^.iItem);
+ end
+ else if (settings<>0) and
+ ((PNMLISTVIEW(lParam)^.uOldState or PNMLISTVIEW(lParam)^.uNewState)=$3000) then
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+ end;
+
+ LVN_ENDLABELEDITW: begin
+ with PLVDISPINFOW(lParam)^ do
+ begin
+ if item.pszText<>nil then
+ begin
+ item.mask:=LVIF_TEXT;
+ SendMessageW(hdr.hWndFrom,LVM_SETITEMW,0,TLPARAM(@item));
+
+ li.iItem :=item.iItem;
+ li.mask :=LVIF_PARAM;
+ li.iSubItem :=0;
+ SendMessageW(hdr.hWndFrom,LVM_GETITEMW,0,TLPARAM(@li));
+ with TaskList[li.lParam] do
+ begin
+ mFreeMem(name);
+ StrDupW (name,item.pszText);
+ end;
+ end;
+ end;
+ result:=1;
+ end;
+ end;
+ end;
+ end;
+end;
diff --git a/plugins/Actman/tasks/i_options.inc b/plugins/Actman/tasks/i_options.inc
new file mode 100644
index 0000000000..527e8d0c88
--- /dev/null
+++ b/plugins/Actman/tasks/i_options.inc
@@ -0,0 +1,99 @@
+{}
+const
+ opt_task :PAnsiChar = 'Task';
+ opt_tasks :PAnsiChar = 'Tasks';
+ opt_count :PAnsiChar = 'numtasks';
+
+ opt_name :PAnsiChar = 'name';
+ opt_flags :PAnsiChar = 'flags';
+ opt_action :PAnsiChar = 'action';
+ opt_repeat :PAnsiChar = 'repeat';
+ opt_days :PAnsiChar = 'dayoffset';
+ opt_intdays :PAnsiChar = 'intdays';
+
+ opt_time_lo :PAnsiChar = 'starttime_lo';
+ opt_time_hi :PAnsiChar = 'starttime_hi';
+ opt_interval_lo:PAnsiChar = 'interval_lo';
+ opt_interval_hi:PAnsiChar = 'interval_hi';
+ opt_lastcall_lo:PAnsiChar = 'lastcall_lo';
+ opt_lastcall_hi:PAnsiChar = 'lastcall_hi';
+
+procedure SaveTasks;
+var
+ section:array [0..63] of AnsiChar;
+ p,p1:PAnsiChar;
+ i,amount:integer;
+begin
+ DBDeleteGroup(0,DBBranch,opt_tasks);
+ amount:=0;
+ p1:=StrCopyE(section,opt_tasks);
+ p1^:='/'; inc(p1);
+ p1:=StrCopyE(p1,opt_task);
+ for i:=0 to MaxTasks-1 do
+ begin
+ if (TaskList[i].flags and ACF_ASSIGNED)=0 then
+ continue;
+
+ p:=StrEnd(IntToStr(p1,amount));
+ p^:='/'; inc(p);
+ with TaskList[i] do
+ begin
+ StrCopy(p,opt_flags ); DBWriteDWord (0,DBBranch,section,flags);
+ StrCopy(p,opt_name ); DBWriteUnicode(0,DBBranch,section,name);
+ StrCopy(p,opt_action); DBWriteDWord (0,DBBranch,section,action);
+ StrCopy(p,opt_repeat); DBWriteWord (0,DBBranch,section,count);
+ StrCopy(p,opt_days ); DBWriteByte (0,DBBranch,section,dayoffset);
+ //systemtime to filetime if needs
+ StrCopy(p,opt_time_lo ); DBWriteDWord(0,DBBranch,section,starttime.dwLowDateTime);
+ StrCopy(p,opt_time_hi ); DBWriteDWord(0,DBBranch,section,starttime.dwHighDateTime);
+ StrCopy(p,opt_interval_lo); DBWriteDWord(0,DBBranch,section,interval .dwLowDateTime);
+ StrCopy(p,opt_interval_hi); DBWriteDWord(0,DBBranch,section,interval .dwHighDateTime);
+ StrCopy(p,opt_intdays ); DBWriteByte (0,DBBranch,section,intdays);
+ StrCopy(p,opt_lastcall_lo); DBWriteDWord(0,DBBranch,section,lastcall .dwLowDateTime);
+ StrCopy(p,opt_lastcall_hi); DBWriteDWord(0,DBBranch,section,lastcall .dwHighDateTime);
+ end;
+ inc(amount);
+ end;
+ DBWriteByte(0,DBBranch,opt_count,amount);
+end;
+
+function LoadTasks:integer;
+var
+ section:array [0..63] of AnsiChar;
+ p,p1:PAnsiChar;
+ i:integer;
+begin
+ MaxTasks:=DBReadByte(0,DBBranch,opt_count);
+ result:=MaxTasks;
+ if MaxTasks>0 then
+ begin
+ GetMem (TaskList ,MaxTasks*SizeOf(tTaskRec));
+ FillChar(TaskList^,MaxTasks*SizeOf(tTaskRec),0);
+ p1:=StrCopyE(section,opt_tasks);
+ p1^:='/'; inc(p1);
+ p1:=StrCopyE(p1,opt_task);
+ for i:=0 to MaxTasks-1 do
+ begin
+ p:=StrEnd(IntToStr(p1,i));
+ p^:='/'; inc(p);
+
+ with TaskList[i] do
+ begin
+ StrCopy(p,opt_flags ); flags :=DBReadDWord (0,DBBranch,section);
+ StrCopy(p,opt_name ); name :=DBReadUnicode(0,DBBranch,section);
+ StrCopy(p,opt_action); action :=DBReadDWord (0,DBBranch,section);
+ StrCopy(p,opt_days ); dayoffset:=DBReadByte (0,DBBranch,section);
+ StrCopy(p,opt_repeat); count :=Shortint(DBReadWord(0,DBBranch,section));
+
+ StrCopy(p,opt_time_lo ); starttime.dwLowDateTime :=DBReadDWord(0,DBBranch,section);
+ StrCopy(p,opt_time_hi ); starttime.dwHighDateTime:=DBReadDWord(0,DBBranch,section);
+ StrCopy(p,opt_interval_lo); interval .dwLowDateTime :=DBReadDWord(0,DBBranch,section);
+ StrCopy(p,opt_interval_hi); interval .dwHighDateTime:=DBReadDWord(0,DBBranch,section);
+ StrCopy(p,opt_intdays ); intdays:=DBReadByte(0,DBBranch,section);
+ StrCopy(p,opt_lastcall_lo); lastcall .dwLowDateTime :=DBReadDWord(0,DBBranch,section);
+ StrCopy(p,opt_lastcall_hi); lastcall .dwHighDateTime:=DBReadDWord(0,DBBranch,section);
+ // filetime to systemtime if needs
+ end;
+ end;
+ end;
+end;
diff --git a/plugins/Actman/tasks/i_service.inc b/plugins/Actman/tasks/i_service.inc
new file mode 100644
index 0000000000..376e75cba0
--- /dev/null
+++ b/plugins/Actman/tasks/i_service.inc
@@ -0,0 +1,87 @@
+{}
+// wParam: 1/0 (enable/disable), lParam = task name
+// works for all tasks with same started name
+function TaskEnable(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+var
+ i,j:integer;
+begin
+ result:=0;
+ if lParam=0 then exit;
+ j:=StrLenW(pWideChar(lParam));
+
+ for i:=0 to MaxTasks-1 do
+ begin
+ if (TaskList[i].flags and ACF_ASSIGNED)<>0 then
+ begin
+ if StrCmpW(TaskList[i].name,pWideChar(lParam),j)=0 then
+ begin
+ if wParam=0 then // disable
+ begin
+ if (TaskList[i].flags and ACF_DISABLED)=0 then
+ begin
+ inc(result);
+ TaskList[i].flags:=TaskList[i].flags or ACF_DISABLED;
+ if TaskList[i].timer<>0 then
+ begin
+ KillTimer(0,TaskList[i].timer);
+ TaskList[i].timer:=0;
+ end;
+ end;
+ end
+ else
+ begin
+ if (TaskList[i].flags and ACF_DISABLED)<>0 then
+ begin
+ inc(result);
+ TaskList[i].flags:=TaskList[i].flags and not ACF_DISABLED;
+ SetTask(TaskList[i]);
+ end;
+ end;
+ end;
+ end;
+ end;
+end;
+
+function TaskDelete(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+var
+ i,j:integer;
+begin
+ result:=0;
+ if lParam=0 then exit;
+ j:=StrLenW(pWideChar(lParam));
+
+ for i:=0 to MaxTasks-1 do
+ begin
+ if (TaskList[i].flags and ACF_ASSIGNED)<>0 then
+ begin
+ if StrCmpW(TaskList[i].name,pWideChar(lParam),j)=0 then
+ begin
+ TaskList[i].flags:=TaskList[i].flags and not ACF_ASSIGNED;
+ end;
+ end;
+ end;
+end;
+
+function TaskCount(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+var
+ i,j:integer;
+begin
+ result:=0;
+ if lParam=0 then exit;
+ j:=StrLenW(pWideChar(lParam));
+
+ for i:=0 to MaxTasks-1 do
+ begin
+ with TaskList[i] do
+ begin
+ if (flags and ACF_ASSIGNED)<>0 then
+ begin
+ if StrCmpW(name,pWideChar(lParam),j)=0 then
+ begin
+ result:=count;
+ count:=wParam;
+ end;
+ end;
+ end;
+ end;
+end;
diff --git a/plugins/Actman/tasks/i_task.inc b/plugins/Actman/tasks/i_task.inc
new file mode 100644
index 0000000000..2c860db85f
--- /dev/null
+++ b/plugins/Actman/tasks/i_task.inc
@@ -0,0 +1,242 @@
+{}
+const
+ ACF_ASSIGNED = $80000000; // Task assigned
+ ACF_DISABLED = $10000000; // Task disabled
+
+ TCF_ABSOLUTE = $00000001;
+ TCF_IMMEDIATELY = $00000002;
+ TCF_NONZEROBREAK = $00000004;
+ TCF_MAKEEVENT = $00000008;
+ TCF_EVENTONCE = $00000010;
+
+const
+ WM_RESETTASKS = WM_USER+1312;
+ WM_FIRSTTASK = WM_USER+1313;
+ WM_LASTTASK = WM_FIRSTTASK+1000;
+
+type
+ pTaskRec = ^tTaskRec;
+ tTaskRec = record
+ // option values
+ flags :dword;
+ name :PWideChar; // name for task
+ action :dword; // assigned action
+ intdays, // interval,days
+ dayoffset :integer; //!! offset, days
+ starttime, // task starttime
+ interval :TFileTime; // interval for repeat
+ count :integer; // repeat count
+ // support values
+ lastcall :TFileTime; // last timer event time
+ nextcall :TFileTime; // ?? next start time?
+ // runtime values
+ timer :uint_ptr; // timer handle
+ curcount :integer; // repeat count
+ inprocess :bool; // starting processing
+ inaction :bool; // timer event processing
+ end;
+ pTaskList = ^tTaskList;
+ tTaskList = array [0..1023] of tTaskRec;
+
+var
+ TaskList:pTaskList = nil;
+ MaxTasks:integer = 0;
+
+procedure TimerProc(wnd:HWND;uMsg:uint;idEvent:uint_ptr;dwTime:dword); stdcall;
+var
+ ltime:uint;
+ i:integer;
+ res:int_ptr;
+ st:tSystemTime;
+begin
+ for i:=0 to MaxTasks-1 do
+ begin
+ with TaskList[i] do
+ begin
+ if (flags and (ACF_ASSIGNED or ACF_DISABLED))=ACF_ASSIGNED then
+ if timer=idEvent then
+ begin
+ inaction:=true;
+ if ((flags and TCF_MAKEEVENT)<>0) and
+ (((flags and TCF_EVENTONCE) =0) or (curcount=count)) then
+ NotifyEventHooks(hevent,count-curcount,lParam(name));
+
+ GetLocalTime(st);
+ SystemTimeToFileTime(st,lastcall);
+
+ res:=CallService(MS_ACT_RUNBYID,action,0);
+
+ if ((res<>0) and ((flags and TCF_NONZEROBREAK)<>0)) or // non-zero result
+ (count=0) or (curcount=0) then // no need to repeat or all repeats done
+ begin
+ KillTimer(0,idEvent);
+ timer:=0;
+ flags:=flags or ACF_DISABLED;
+ end
+ else
+ begin
+ if (count<>0) and (count=curcount) then // next timer - repeat interval
+ begin
+ KillTimer(0,idEvent);
+ FileTimeToSystemTime(interval,st);
+ ltime:={st.wMilliseconds+}st.wSecond*1000+st.wMinute*1000*60+st.wHour*60*60*1000;
+ timer:=SetTimer(0,0,ltime,@TimerProc);
+ if count=-1 then
+ curcount:=1;
+ end;
+ if count>0 then
+ dec(curcount);
+ end;
+ inaction:=false;
+ break;
+ end;
+ end;
+ end;
+end;
+
+procedure SetTask(var task:tTaskRec);
+var
+ ltime:uint;
+ uli1,uli2:ULARGE_INTEGER;
+ sft:tFileTime;
+ st:tSystemTime;
+ dif:int64;
+begin
+ task.inprocess:=true;
+ // Check task time
+ if (task.flags and TCF_IMMEDIATELY)<>0 then
+ begin
+ FileTimeToSystemTime(task.interval,st);
+ ltime:={st.wMilliseconds+}st.wSecond*1000+st.wMinute*1000*60+
+ st.wHour*60*60*1000;
+ end
+ else if (task.flags and TCF_ABSOLUTE)<>0 then
+ begin
+ uli1.LowPart :=task.starttime.dwLowDateTime;
+ uli1.HighPart:=task.starttime.dwHighDateTime;
+ GetLocalTime(st);
+ SystemTimeToFileTime(st,sft);
+ uli2.LowPart :=sft.dwLowDateTime;
+ uli2.HighPart:=sft.dwHighDateTime;
+ dif:=uli1.QuadPart-uli2.QuadPart;
+ if dif>0 then // time in future
+ ltime:=dif div 10000 // 100ns to 1 ms
+ else // was in past
+ begin
+ task.flags:=task.flags or ACF_DISABLED;
+ exit;
+ end;
+ end
+ else
+ begin
+ // days+hours+minutes+seconds+millseconds
+ FileTimeToSystemTime(task.starttime,st);
+ ltime:={st.wMilliseconds+}st.wSecond*1000+st.wMinute*1000*60+
+ st.wHour*60*60*1000+task.dayoffset*24*60*60*1000;
+ end;
+ // set timer
+ task.curcount:=task.count;
+ task.timer :=SetTimer(0,0,ltime,@TimerProc);
+
+ if (task.flags and TCF_IMMEDIATELY)<>0 then
+ TimerProc(0,WM_TIMER,task.timer,0);
+ task.inprocess:=false;
+end;
+
+procedure SetAllTasks;
+var
+ i:integer;
+begin
+ for i:=0 to MaxTasks-1 do
+ begin
+ if (TaskList[i].flags and ACF_ASSIGNED)<>0 then
+ begin
+ if (TaskList[i].flags and ACF_DISABLED)=0 then
+ SetTask(TaskList[i])
+ else if TaskList[i].timer<>0 then
+ begin
+ KillTimer(0,TaskList[i].timer);
+ TaskList[i].timer:=0;
+ end;
+ end;
+ end;
+end;
+
+procedure StopAllTasks;
+var
+ i:integer;
+begin
+ for i:=0 to MaxTasks-1 do
+ begin
+ if (TaskList[i].flags and (ACF_ASSIGNED or ACF_DISABLED))=ACF_ASSIGNED then
+ if TaskList[i].timer<>0 then
+ begin
+ KillTimer(0,TaskList[i].timer);
+ TaskList[i].timer:=0;
+ end;
+ end;
+end;
+
+procedure ClearTasks;
+var
+ i:integer;
+begin
+ for i:=0 to MaxTasks-1 do
+ begin
+ with TaskList[i] do
+ begin
+//!! if (flags and ACF_ASSIGNED)<>0 then
+ mFreeMem(name);
+ end;
+ end;
+ FreeMem(TaskList);
+ MaxTasks:=0;
+end;
+
+function CreateNewTask:integer;
+var
+ i:integer;
+ tmp:pTaskList;
+ st:tSystemTime;
+begin
+ result:=-1;
+ // if list is not empty, search for hole
+ if MaxTasks>0 then
+ begin
+ for i:=0 to MaxTasks-1 do
+ begin
+ if (TaskList[i].flags and ACF_ASSIGNED)=0 then
+ begin
+ FillChar(TaskList[i],SizeOf(tTaskRec),0);
+ result:=i;
+ break;
+ end;
+ end;
+ end;
+ if result<0 then
+ begin
+ // not found or empty list
+ i:=(MaxTasks+16)*SizeOf(tTaskRec);
+ GetMem (tmp ,i);
+ FillChar(tmp^,i,0);
+ if MaxTasks>0 then
+ begin
+ move(TaskList^,tmp^,MaxTasks*SizeOf(tTaskRec));
+ FreeMem(TaskList);
+ end;
+ TaskList:=tmp;
+ result:=MaxTasks;
+ inc(MaxTasks,16);
+ end;
+ with TaskList^[result] do
+ begin
+ flags:=flags or ACF_ASSIGNED or ACF_DISABLED or TCF_ABSOLUTE;
+ GetLocalTime(st);
+ SystemTimeToFileTime(st,starttime);
+ //!!! CHEAT
+ st.wHour :=0;
+ st.wMinute:=0;
+ st.wSecond:=1;
+ SystemTimeToFileTime(st,interval);
+ end;
+end;
diff --git a/plugins/Actman/tasks/i_tconst.inc b/plugins/Actman/tasks/i_tconst.inc
new file mode 100644
index 0000000000..f4df810d32
--- /dev/null
+++ b/plugins/Actman/tasks/i_tconst.inc
@@ -0,0 +1,27 @@
+{resource constants}
+const
+ IDD_TASKS = 2030;
+
+ IDC_TASK_NAME = 1025;
+
+ IDC_TASK_DATET = 1026;
+ IDC_TASK_DATEV = 1027;
+ IDC_TASK_DAYST = 1028;
+ IDC_TASK_DAYSV = 1029;
+ IDC_TASK_TIMET = 1030;
+ IDC_TASK_TIMEV = 1031;
+
+ IDC_TASK_REPEAT = 1032;
+ IDC_TASK_BREAK = 1034;
+ IDC_TASK_INTERVAL = 1035;
+ IDC_TASK_EVENT = 1036;
+ IDC_TASK_ONCE = 1037;
+ IDC_TASK_UPDOWN = 1038;
+ IDC_TASK_ABSOLUTE = 1039;
+
+ IDC_TASK_ACTION = 1040;
+
+ IDC_TASK_INTDAYS = 1041;
+
+ IDC_TASK_NEW = 1050;
+ IDC_TASK_DELETE = 1051;
diff --git a/plugins/Actman/tasks/scheduler.pas b/plugins/Actman/tasks/scheduler.pas
new file mode 100644
index 0000000000..05e9cb6a58
--- /dev/null
+++ b/plugins/Actman/tasks/scheduler.pas
@@ -0,0 +1,86 @@
+unit scheduler;
+
+interface
+
+procedure Init;
+procedure DeInit;
+function AddOptionPage(var tmpl:pAnsiChar;var proc:pointer;var name:PAnsiChar):integer;
+
+implementation
+
+uses
+ windows, commctrl, messages,
+ mirutils, common, dbsettings, io, m_api, wrapper,
+ global;
+
+{$R tasks.res}
+
+{$include m_actman.inc}
+
+var
+ hevent: THANDLE;
+
+{$include i_task.inc}
+{$include i_tconst.inc}
+{$include i_options.inc}
+{$include i_opt_dlg.inc}
+{$include i_service.inc}
+
+// ------------ base interface functions -------------
+
+var
+ hendis,
+ hcount,
+ hdel: THANDLE;
+
+procedure Init;
+begin
+
+ if LoadTasks=0 then
+ begin
+ MaxTasks:=8;
+ GetMem (TaskList ,MaxTasks*SizeOf(tTaskRec));
+ FillChar(TaskList^,MaxTasks*SizeOf(tTaskRec),0);
+ end
+ else
+ SetAllTasks;
+
+ hcount:=CreateServiceFunction(MS_ACT_TASKCOUNT ,@TaskCount);
+ hendis:=CreateServiceFunction(MS_ACT_TASKENABLE,@TaskEnable);
+ hdel :=CreateServiceFunction(MS_ACT_TASKDELETE,@TaskDelete);
+ hevent:=CreateHookableEvent(ME_ACT_BELL);
+
+end;
+
+procedure DeInit;
+begin
+ StopAllTasks;
+ DestroyServiceFunction(hendis);
+ DestroyServiceFunction(hdel);
+ DestroyServiceFunction(hcount);
+ ClearTasks;
+end;
+
+function AddOptionPage(var tmpl:pAnsiChar;var proc:pointer;var name:PAnsiChar):integer;
+begin
+ result:=0;
+ tmpl:=PAnsiChar(IDD_TASKS);
+ proc:=@DlgProcOpt;
+ name:='Scheduler';
+end;
+
+var
+ amLink:tActionLink;
+
+procedure InitLink;
+begin
+ amLink.Next :=ActionLink;
+ amLink.Init :=@Init;
+ amLink.DeInit :=@DeInit;
+ amLink.AddOption:=@AddOptionPage;
+ ActionLink :=@amLink;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Actman/tasks/tasks.rc b/plugins/Actman/tasks/tasks.rc
new file mode 100644
index 0000000000..2bc558fbc3
--- /dev/null
+++ b/plugins/Actman/tasks/tasks.rc
@@ -0,0 +1,47 @@
+#include "i_tconst.inc"
+
+LANGUAGE 0,0
+
+IDD_TASKS DIALOGEX 0, 0, 304, 226, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ CONTROL "", IDC_TASK_NAME, "SysListView32",
+ WS_BORDER | WS_TABSTOP |
+ LVS_NOCOLUMNHEADER | LVS_SHOWSELALWAYS | LVS_REPORT | LVS_EDITLABELS,// | LVS_SINGLESEL,
+ 2, 2, 130, 174, WS_EX_CONTROLPARENT
+
+ CTEXT "Action",-1 , 140, 2, 160, 12, SS_CENTERIMAGE
+ COMBOBOX IDC_TASK_ACTION, 140, 14, 160, 128, CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL
+
+ GROUPBOX "Start" , -1, 138, 30, 164, 54
+
+ COMBOBOX IDC_TASK_ABSOLUTE, 142, 40, 156, 60, CBS_DROPDOWNLIST | WS_VSCROLL
+
+ CTEXT "Date", IDC_TASK_DATET, 140, 54, 76, 12, SS_CENTERIMAGE
+ CONTROL "Date", IDC_TASK_DATEV, "SysDateTimePick32", WS_TABSTOP, 150, 66, 56, 14
+
+ CTEXT "Days", IDC_TASK_DAYST, 140, 54, 76, 12, SS_CENTERIMAGE
+ EDITTEXT IDC_TASK_DAYSV, 162, 66, 32, 14
+
+ CTEXT "Time", IDC_TASK_TIMET, 220, 54, 76, 12, SS_CENTERIMAGE
+ CONTROL "Time", IDC_TASK_TIMEV, "SysDateTimePick32", WS_TABSTOP|$09, 230, 66, 56, 14
+
+ GROUPBOX "Repeat" , -1, 138, 88, 164, 88
+
+ CTEXT "Repeat, times", -1, 140, 98, 70, 14, SS_CENTERIMAGE
+ EDITTEXT IDC_TASK_REPEAT, 155, 114, 40, 14
+
+ CTEXT "Interval", -1, 212, 98, 84, 14, SS_CENTERIMAGE
+ EDITTEXT IDC_TASK_INTDAYS, 212, 114, 24, 14
+ CONTROL "Interval", IDC_TASK_INTERVAL, "SysDateTimePick32", WS_TABSTOP|$09, 240, 114, 56, 14
+
+ AUTOCHECKBOX "Break on non-zero result", IDC_TASK_BREAK, 142, 132, 156, 14
+ AUTOCHECKBOX "Send event on start time", IDC_TASK_EVENT, 142, 146, 156, 14
+ AUTOCHECKBOX "Send event just once" , IDC_TASK_ONCE , 142, 160, 156, 14
+
+ CONTROL "New" ,IDC_TASK_NEW ,"MButtonClass",WS_TABSTOP, 2,178,16,16,$18000000
+ CONTROL "Delete",IDC_TASK_DELETE,"MButtonClass",WS_TABSTOP,22,178,16,16,$18000000
+// CONTROL "Help" ,IDC_EVENT_HELP ,"MButtonClass",WS_TABSTOP,42,164,16,16,$18000000
+}
diff --git a/plugins/Actman/tasks/tasks.res b/plugins/Actman/tasks/tasks.res
new file mode 100644
index 0000000000..bc72f06406
--- /dev/null
+++ b/plugins/Actman/tasks/tasks.res
Binary files differ
diff --git a/plugins/Actman/ua/action.ico b/plugins/Actman/ua/action.ico
new file mode 100644
index 0000000000..9e4c60d9d3
--- /dev/null
+++ b/plugins/Actman/ua/action.ico
Binary files differ
diff --git a/plugins/Actman/ua/i_inoutxm.inc b/plugins/Actman/ua/i_inoutxm.inc
new file mode 100644
index 0000000000..68ad22d694
--- /dev/null
+++ b/plugins/Actman/ua/i_inoutxm.inc
@@ -0,0 +1,357 @@
+{}
+var
+ xmlparser:XML_API_W;
+
+const
+ ioAction :PWideChar = 'Action';
+ ioUA :PWideChar = 'UA';
+
+ ioName :PWideChar = 'name';
+
+ ioTwoState :PWideChar = 'twostate';
+ ioSaveState :PWideChar = 'savestate';
+
+ ioHotkey :PWideChar = 'Hotkey';
+ ioToolbar :PWideChar = 'Toolbar';
+ ioTabSRMM :PWideChar = 'TabSRMM';
+ ioMenuItem :PWideChar = 'Menu';
+
+ ioTooltip :PWideChar = 'tooltip';
+ ioTooltipPressed :PWideChar = 'tt_pressed';
+
+ ioType :PWideChar = 'type';
+ ioMenuPopup :PWideChar = 'Popup';
+ ioMenuName :PWideChar = 'Name';
+ ioMenuShow :PWideChar = 'Show';
+ ioMenuUsed :PWideChar = 'Used';
+ ioMenuSeparated :PWideChar = 'Separated';
+
+
+function ImportMenuItems(node:HXML;var MenuItem:tUAMenuItem):integer;
+begin
+ result:=0;
+
+ with xmlparser do
+ begin
+ with MenuItem do
+ begin
+ menu_opt:=0;
+ // popup
+ StrDupW(szMenuPopup,getAttrValue(node,ioMenuPopup));
+ // name
+ StrDupW(szMenuNameVars,getAttrValue(node,ioMenuName));
+ // show
+ StrDupW(szMenuShowWhenVars,getAttrValue(node,ioMenuShow));
+ // used
+ if StrToInt(getAttrValue(node,ioMenuUsed))<>0 then
+ menu_opt:=menu_opt or UAF_MENUUSE;
+ // separated
+ if StrToInt(getAttrValue(node,ioMenuSeparated))<>0 then
+ menu_opt:=menu_opt or UAF_MENUSEP;
+ end;
+ end;
+end;
+
+function ImportUAction(actnode:HXML;var UA:tMyActionItem):integer;
+var
+ num,i:integer;
+ sub:HXML;
+begin
+ result:=0;
+ if actnode=0 then exit;
+
+ with xmlparser do
+ begin
+ // we don't need that node as is, just it's child for UA
+// actnode:=GetNthChild(actnode,ioUA,0);
+
+ UA.flags:=0;
+ // ----- Common -----
+ if StrToInt(getAttrValue(actnode,ioTwoState))<>0 then
+ UA.flags:=UA.flags or UAF_2STATE;
+
+ if StrToInt(getAttrValue(actnode,ioSaveState))<>0 then
+ UA.flags:=UA.flags or UAF_SAVESTATE;
+
+ // sub:=AddChild(actnode,ioRegister,nil);
+ if StrToInt(getAttrValue(actnode,ioHotkey))<>0 then
+ UA.flags:=UA.flags or UAF_REGHOTKEY;
+ if StrToInt(getAttrValue(actnode,ioToolbar))<>0 then
+ UA.flags:=UA.flags or UAF_REGTTBB;
+ if StrToInt(getAttrValue(actnode,ioTabSRMM))<>0 then
+ UA.flags:=UA.flags or UAF_REGTABB;
+
+ // ----- Hotkey -----
+ // nothing
+
+ // ----- Modern CList toolbar -----
+ // source - ANSI text
+ sub:=GetNthChild(actnode,ioToolbar,0);
+ WideToAnsi(GetAttrValue(sub,ioTooltip ),UA.szTTBTooltip ,MirandaCP);
+ WideToAnsi(GetAttrValue(sub,ioTooltipPressed),UA.szTTBTooltipPressed,MirandaCP);
+
+ // ----- TabSRMM toolbar -----
+ sub:=GetNthChild(actnode,ioTabSRMM,0);
+ StrDupW(UA.szTabBTooltip ,getAttrValue(sub,ioTooltip));
+ StrDupW(UA.szTabBTooltipPressed,getAttrValue(sub,ioTooltipPressed));
+
+ // ----- Menus -----
+ num:=0;
+ repeat
+ sub:=getNextChild(actnode,ioMenuItem,@num);
+ if sub=0 then break;
+
+ i:=StrToInt(getAttrValue(sub,ioType));
+ ImportMenuItems(sub,
+ UA.UAMenuItem[tMenuType(i)]);
+ until false;
+ end;
+end;
+
+function Import(fname:PWideChar;aflags:dword):integer;
+var
+ i,j,act:integer;
+ root,actnode:HXML;
+ pcw,res:pWideChar;
+ f:THANDLE;
+ num,num1:integer;
+ ptr,ptr1:pChain;
+begin
+ result:=0;
+
+ if (fname=nil) or (fname^=#0) then
+ exit;
+ i:=GetFSize(fname);
+ if i=0 then
+ exit;
+
+ num:=CallService(MS_ACT_GETLIST,0,LPARAM(@ptr));
+ if num=0 then exit;
+ ptr1:=ptr;
+
+ mGetMem (res ,i+SizeOf(WideChar));
+ FillChar(res^,i+SizeOf(WideChar),0);
+ f:=Reset(fname);
+ BlockRead(f,res^,i);
+ CloseHandle(f);
+
+ xmlparser.cbSize:={XML_API_SIZEOF_V1;//}SizeOf(XML_API_W);
+ CallService(MS_SYSTEM_GET_XI,0,lparam(@xmlparser));
+ with xmlparser do
+ begin
+ root:=parseString(ChangeUnicode(res),@i,nil);
+ j:=0;
+ repeat
+ actnode:=getNthChild(root,ioAction,j);
+ if actnode=0 then break;
+ // search id by name?
+ pcw:=GetAttrValue(actnode,ioName);
+ ptr:=ptr1;
+ inc(pbyte(ptr),4);
+ for i:=0 to num-1 do
+ begin
+ if (ptr.flags and ACCF_IMPORTED)<>0 then
+ begin
+ if StrCmpw(pcw,ptr.descr)=0 then
+ begin
+ // delete old UA for overwrited actions
+ if (ptr.flags and ACCF_OVERLOAD)<>0 then
+ begin
+ for act:=0 to HIGH(UActionList) do
+ begin
+ if ptr.id=UActionList[act].dwActID then
+ begin
+ DeleteUAction(act);
+ break;
+ end;
+ end;
+ end;
+ num1:=AddUAction(-1,ptr);
+ ImportUAction(getNthChild(actnode,ioUA,0),UActionList[num1]);
+ break;
+ end;
+ end;
+ inc(ptr);
+ end;
+
+ inc(j);
+ until false;
+
+ DestroyNode(root);
+ end;
+ CallService(MS_ACT_FREELIST,0,LPARAM(ptr1));
+ mFreeMem(res);
+ result:=1;
+ if settings<>0 then
+ begin
+ FillActionList(settings);
+ ShowAction(settings,-1);
+ end;
+end;
+
+//--------------------------
+
+function ExportMenuItems(node:HXML;MenuItem:tUAMenuItem):HXML;
+begin
+ with xmlparser do
+ begin
+ result:=AddChild(node,ioMenuItem,nil);
+ with MenuItem do
+ begin
+ // popup
+ if (szMenuPopup<>nil) and (szMenuPopup^<>#0) then
+ AddAttr(result,ioMenuPopup,szMenuPopup);
+ // name
+ if (szMenuNameVars<>nil) and (szMenuNameVars^<>#0) then
+ AddAttr(result,ioMenuName,szMenuNameVars);
+ // show
+ if (szMenuShowWhenVars<>nil) and (szMenuShowWhenVars^<>#0) then
+ AddAttr(result,ioMenuShow,szMenuShowWhenVars);
+ // used
+ AddAttrInt(result,ioMenuUsed,ord((menu_opt AND UAF_MENUUSE)<>0));
+ // separated
+ AddAttrInt(result,ioMenuSeparated,ord((menu_opt AND UAF_MENUSEP)<>0));
+ end;
+ end;
+end;
+
+procedure WriteUAction(root:HXML;id:dword;name:pWideChar);
+var
+ i:integer;
+ lmenu:tMenuType;
+ pc:pWideChar;
+ actnode,sub:HXML;
+ UA:pMyActionItem;
+begin
+ with xmlparser do
+ begin
+ for i:=0 to HIGH(UActionList) do
+ begin
+ if UActionList[i].dwActID=id then
+ begin
+ UA:=@UActionList[i];
+ actnode:=getChildByAttrValue(root,ioAction,ioName,name);
+ if actnode=0 then break;
+ // we don't need that node as is, just it's child for UA
+ actnode:=addChild(actnode,ioUA,nil);
+
+ // ----- Common -----
+ AddAttrInt(actnode,ioTwoState ,ORD((UA.flags and UAF_2STATE )<>0));
+ AddAttrInt(actnode,ioSaveState,ORD((UA.flags and UAF_SAVESTATE)<>0));
+
+ // sub:=AddChild(actnode,ioRegister,nil);
+ AddAttrInt(actnode,ioHotkey ,ORD((UA.flags and UAF_REGHOTKEY)<>0));
+ AddAttrInt(actnode,ioToolbar,ORD((UA.flags and UAF_REGTTBB )<>0));
+ AddAttrInt(actnode,ioTabSRMM,ORD((UA.flags and UAF_REGTABB )<>0));
+
+ // ----- Hotkey -----
+ // nothing
+
+ // ----- Modern CList toolbar -----
+ // source - ANSI text
+ if ((UA.szTTBTooltip <>nil) and (UA.szTTBTooltip^ <>#0)) or
+ ((UA.szTTBTooltipPressed<>nil) and (UA.szTTBTooltipPressed^<>#0)) then
+ begin
+ sub:=AddChild(actnode,ioToolbar,nil);
+ if (UA.szTTBTooltip<>nil) and (UA.szTTBTooltip^<>#0) then
+ begin
+ AnsiToWide(UA.szTTBTooltip,pc,MirandaCP);
+ AddAttr(sub,ioTooltip,pc);
+ mFreeMem(pc);
+ end;
+ if (UA.szTTBTooltipPressed<>nil) and (UA.szTTBTooltipPressed^<>#0) then
+ begin
+ AnsiToWide(UA.szTTBTooltipPressed,pc,MirandaCP);
+ AddAttr(sub,ioTooltipPressed,pc);
+ mFreeMem(pc);
+ end;
+ end;
+
+ // ----- TabSRMM toolbar -----
+ if ((UA.szTabBTooltip <>nil) and (UA.szTabBTooltip^ <>#0)) or
+ ((UA.szTabBTooltipPressed<>nil) and (UA.szTabBTooltipPressed^<>#0)) then
+ begin
+ sub:=AddChild(actnode,ioTabSRMM,nil);
+ if (UA.szTabBTooltip<>nil) and (UA.szTabBTooltip^<>#0) then
+ AddAttr(sub,ioTooltip,UA.szTabBTooltip);
+ if (UA.szTabBTooltipPressed<>nil) and (UA.szTabBTooltipPressed^<>#0) then
+ AddAttr(sub,ioTooltipPressed,UA.szTabBTooltipPressed);
+ end;
+
+ // ----- Menus -----
+ for lmenu:=main_menu to HIGH(tMenuType) do
+ begin
+ sub:=ExportMenuItems(actnode,UA.UAMenuItem[lmenu]);
+ AddAttrInt(sub,ioType,ORD(lmenu));
+ end;
+
+ break;
+ end;
+ end;
+ end;
+end;
+
+function Export(fname:pWideChar;aflags:dword):integer;
+var
+ i,num:integer;
+ f:THANDLE;
+ root:HXML;
+ res:pWideChar;
+ ptr,ptr1:pChain;
+begin
+ result:=0;
+ xmlparser.cbSize:={XML_API_SIZEOF_V1;//}SizeOf(XML_API_W);
+ CallService(MS_SYSTEM_GET_XI,0,lparam(@xmlparser));
+ with xmlparser do
+ begin
+ // we need append file, not rewrite
+ i:=GetFSize(fname);
+ if i=0 then exit;
+
+ mGetMem (res ,i+SizeOf(WideChar));
+ FillChar(res^,i+SizeOf(WideChar),0);
+ f:=Reset(fname);
+ BlockRead(f,res^,i);
+ CloseHandle(f);
+ root:=parseString(res,@i,nil);
+ mFreeMem(res);
+
+ num:=CallService(MS_ACT_GETLIST,0,LPARAM(@ptr));
+ if num>0 then
+ begin
+ ptr1:=ptr;
+ inc(pbyte(ptr),4);
+ for i:=0 to num-1 do
+ begin
+ if ((aflags and ACIO_SELECTED)=0) or
+ ((ptr.flags and ACCF_EXPORT)<>0) then
+ begin
+ WriteUAction(root,ptr.id,ptr.descr);
+ end;
+ inc(ptr);
+ end;
+ CallService(MS_ACT_FREELIST,0,LPARAM(ptr1));
+ end;
+
+ res:=toString(root,@i);
+
+ f:=Rewrite(fname);
+ BlockWrite(f,res^,i*SizeOf(WideChar));
+ CloseHandle(f);
+ xmlparser.FreeMem(res);
+ DestroyNode(root);
+ end;
+ result:=1;
+end;
+
+function ActInOut(wParam:WPARAM;lParam:LPARAM):int_ptr; cdecl;
+begin
+ if (wParam and ACIO_EXPORT)=0 then
+ begin
+ result:=Import(pWideChar(lParam),wParam);
+ end
+ else
+ begin
+ result:=Export(pWideChar(lParam),wParam);
+ end;
+end;
diff --git a/plugins/Actman/ua/i_opt_dlg.inc b/plugins/Actman/ua/i_opt_dlg.inc
new file mode 100644
index 0000000000..01f01dceb7
--- /dev/null
+++ b/plugins/Actman/ua/i_opt_dlg.inc
@@ -0,0 +1,571 @@
+{}
+const
+ settings:HWND = 0;
+const
+ NumControls = 17;
+
+ IDsArray:array [0..NumControls-1] of integer =(
+ // Menu settings controls
+ IDC_UA_SEPARATE ,IDC_UA_POPUPT ,IDC_UA_POPUPV,
+ IDC_UA_VARNAMEST,IDC_UA_VARNAMESV,IDC_UA_VARNAMESH,
+ IDC_UA_SHOWVART ,IDC_UA_SHOWVARV ,IDC_UA_SHOWVARH,
+ IDC_UA_TWOSTATE ,IDC_UA_SAVSTATE ,IDC_UA_COMMON,
+ // toolbar settings controls
+ IDC_UA_TTNORMALT,IDC_UA_TTNORMALV,IDC_UA_TTPRESSEDT,IDC_UA_TTPRESSEDV,
+ IDC_UA_GLOBAL
+ );
+
+ // Show-hide controls by place type
+ SHArray:array [0..NumTypes-1, 0..NumControls-1] of integer = (
+ // CList Modern toolbar
+ (SW_HIDE,SW_HIDE,SW_HIDE, SW_HIDE,SW_HIDE,SW_HIDE, SW_SHOW,SW_SHOW,SW_SHOW,
+ SW_SHOW,SW_SHOW,SW_SHOW, SW_SHOW,SW_SHOW,SW_SHOW,SW_SHOW, SW_HIDE),
+ // TabSRMM toolbar
+ (SW_HIDE,SW_HIDE,SW_HIDE, SW_HIDE,SW_HIDE,SW_HIDE, SW_HIDE,SW_HIDE,SW_HIDE,
+ SW_SHOW,SW_SHOW,SW_SHOW, SW_SHOW,SW_SHOW,SW_SHOW,SW_SHOW, SW_SHOW),
+ // Core Hotkey
+ (SW_HIDE,SW_HIDE,SW_HIDE, SW_HIDE,SW_HIDE,SW_HIDE, SW_HIDE,SW_HIDE,SW_HIDE,
+ SW_HIDE,SW_HIDE,SW_HIDE, SW_HIDE,SW_HIDE,SW_HIDE,SW_HIDE, SW_HIDE),
+ // Main menu
+ (SW_SHOW,SW_SHOW,SW_SHOW, SW_SHOW,SW_SHOW,SW_SHOW, SW_SHOW,SW_SHOW,SW_SHOW,
+ SW_SHOW,SW_SHOW,SW_SHOW, SW_HIDE,SW_HIDE,SW_HIDE,SW_HIDE, SW_HIDE),
+ // Contact menu
+ (SW_SHOW,SW_SHOW,SW_SHOW, SW_SHOW,SW_SHOW,SW_SHOW, SW_SHOW,SW_SHOW,SW_SHOW,
+ SW_SHOW,SW_SHOW,SW_SHOW, SW_HIDE,SW_HIDE,SW_HIDE,SW_HIDE, SW_SHOW),
+ // Tray menu
+ (SW_SHOW,SW_SHOW,SW_SHOW, SW_SHOW,SW_SHOW,SW_SHOW, SW_SHOW,SW_SHOW,SW_SHOW,
+ SW_SHOW,SW_SHOW,SW_SHOW, SW_HIDE,SW_HIDE,SW_HIDE,SW_HIDE, SW_HIDE),
+ // Protocol menu
+ (SW_SHOW,SW_SHOW,SW_SHOW, SW_SHOW,SW_SHOW,SW_SHOW, SW_SHOW,SW_SHOW,SW_SHOW,
+ SW_SHOW,SW_SHOW,SW_SHOW, SW_HIDE,SW_HIDE,SW_HIDE,SW_HIDE, SW_HIDE),
+ // Status menu
+ (SW_SHOW,SW_SHOW,SW_SHOW, SW_SHOW,SW_SHOW,SW_SHOW, SW_SHOW,SW_SHOW,SW_SHOW,
+ SW_SHOW,SW_SHOW,SW_SHOW, SW_HIDE,SW_HIDE,SW_HIDE,SW_HIDE, SW_HIDE)
+ );
+ // additional show/hide controls check by Variables installings (1 - need to check)
+ SHVarArray:array [0..NumControls-1] of byte = (
+ 0,0,0, 0,0,1, 1,1,1,
+ 0,0,0,0,0,0,0, 0);
+ // additional enable/disable controls check (1 - always enable)
+ EnDisArray:array [0..NumControls-1] of byte = (
+ 0,0,0, 0,0,0, 0,0,0,
+ 1,1,1,0,0,0,0, 1);
+
+var
+ hIC:THANDLE;
+
+procedure CheckPlacesAbility;
+var
+ i:integer;
+begin
+ for i:=0 to NumTypes-1 do
+ begin
+ with NamesArray[i] do
+ begin
+ enable:=(service=nil) or (ServiceExists(service)<>0);
+ end;
+ end;
+end;
+
+function CompareItem(lParam1,lParam2:LPARAM;SortType:LPARAM):int; stdcall;
+begin
+ result:=UActionList[lParam1].wSortIndex-UActionList[lParam2].wSortIndex;
+end;
+
+// Show or hide option items
+procedure SetupControls(Dialog:HWND;atype:integer;item:integer=-1);
+var
+ i: cardinal;
+ typ:integer;
+ wnd,wnd1:HWND;
+ enable:boolean;
+begin
+ if atype<0 then
+ begin
+ for i:=0 to NumControls-1 do
+ begin
+ ShowWindow(GetDlgItem(Dialog,IDsArray[i]),SW_HIDE);
+ end;
+ end
+ else
+ begin
+ wnd1:=GetDlgItem(Dialog,IDC_UA_PLACELIST);
+ if item<0 then
+ item:=SendMessage(wnd1,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
+ enable:=ListView_GetCheckState(wnd1,item)<>0;
+
+ for i:=0 to NumControls-1 do
+ begin
+ typ:=SHArray[LoByte(atype)+HiByte(atype)][i];
+ if typ=SW_SHOW then
+ if (SHVarArray[i]<>0) and (not IsVarsInstalled) then
+ typ:=SW_HIDE;
+ wnd:=GetDlgItem(Dialog,IDsArray[i]);
+ ShowWindow(wnd,typ);
+ EnableWindow(wnd,enable or (EnDisArray[i]<>0));
+ end;
+
+ // common settings
+ EnableWindow(GetDlgItem(Dialog,IDC_UA_SAVSTATE),
+ IsDlgButtonChecked(Dialog,IDC_UA_TWOSTATE)<>BST_UNCHECKED);
+
+ // personal settings
+ case LoByte(atype) of
+ uaTTB, uaTAB: begin
+ enable:=false;
+ if IsDlgButtonChecked(Dialog,IDC_UA_TWOSTATE)<>BST_UNCHECKED then
+ if IsWindowEnabled(GetDlgItem(Dialog,IDC_UA_TTNORMALV)) then
+ enable:=true;
+ EnableWindow(GetDlgItem(Dialog,IDC_UA_TTPRESSEDV),enable);
+ end;
+ end;
+ end;
+end;
+
+// Clear all screen buttons/text fields (reset)
+procedure ClearControls(Dialog:HWND);
+var
+ s:HWND;
+begin
+ s:=settings;
+ settings:=0;
+ CheckDlgButton (Dialog,IDC_UA_TWOSTATE ,BST_UNCHECKED);
+ CheckDlgButton (Dialog,IDC_UA_SAVSTATE ,BST_UNCHECKED);
+
+ CheckDlgButton (Dialog,IDC_UA_SEPARATE ,BST_UNCHECKED);
+ SetDlgItemTextW(Dialog,IDC_UA_POPUPV ,nil);
+ SetDlgItemTextW(Dialog,IDC_UA_VARNAMESV,nil);
+ SetDlgItemTextW(Dialog,IDC_UA_SHOWVARV ,nil);
+ settings:=s;
+end;
+
+procedure ShowSubAction(Dialog:HWND;aType:integer;item:integer=-1);
+var
+ UA:pMyActionItem;
+ s:HWND;
+begin
+ s:=settings;
+ settings:=0;
+ ClearControls(Dialog);
+
+ // get UAction number
+ item:=LV_GetLParam(GetDlgItem(Dialog,IDC_UA_ACTIONLIST),item);
+
+ UA:=@UActionList[item];
+
+ // common settings
+ if (UA.flags and UAF_2STATE)<>0 then
+ CheckDlgButton(Dialog,IDC_UA_TWOSTATE,BST_CHECKED);
+
+ if (UA.flags and UAF_SAVESTATE)<>0 then
+ CheckDlgButton(Dialog,IDC_UA_SAVSTATE,BST_CHECKED);
+
+ if (UA.flags and UAF_GLOBAL)=0 then
+ CheckDlgButton(Dialog,IDC_UA_GLOBAL,BST_CHECKED);
+
+ // Show real UA settings
+ case LoByte(aType) of
+ uaTTB: begin // CList modern toolbar
+ SetDlgItemTextA(Dialog,IDC_UA_TTNORMALV ,UA.szTTBTooltip);
+ SetDlgItemTextA(Dialog,IDC_UA_TTPRESSEDV,UA.szTTBTooltipPressed);
+ SetDlgItemTextW(Dialog,IDC_UA_SHOWVARV ,UA.szTTBShowWhenVars);
+ end;
+
+ uaTAB: begin // TabSRMM toolbar
+ SetDlgItemTextW(Dialog,IDC_UA_TTNORMALV ,UA.szTabBTooltip);
+ SetDlgItemTextW(Dialog,IDC_UA_TTPRESSEDV,UA.szTabBTooltipPressed);
+ end;
+
+ uaMenu: begin
+ with UA.UAMenuItem[tMenuType(HiByte(aType))] do
+ begin
+ if (menu_opt and UAF_MENUSEP)<>0 then
+ CheckDlgButton(Dialog,IDC_UA_SEPARATE,BST_CHECKED);
+ SetDlgItemTextW(Dialog,IDC_UA_POPUPV ,szMenuPopup);
+ SetDlgItemTextW(Dialog,IDC_UA_VARNAMESV,szMenuNameVars);
+ SetDlgItemTextW(Dialog,IDC_UA_SHOWVARV ,szMenuShowWhenVars);
+ end;
+ end;
+
+ uaHotkey: begin // Hotkey
+ // Settings in Customize/Hotkeys
+ end;
+ end;
+ SetupControls(Dialog,aType,-1);
+ settings:=s;
+end;
+
+function isPlaceActive(idx,place:integer):boolean;
+begin
+ result:=false;
+ with UActionList[idx] do
+ case LoByte(place) of
+ uaTTB : result:=(flags and UAF_REGTTBB)<>0;
+ uaTAB : result:=(flags and UAF_REGTABB)<>0;
+ uaHotkey : result:=(flags and UAF_REGHOTKEY)<>0;
+ uaMenu: begin
+ result:=(UAMenuItem[tMenuType(HiByte(place))].menu_opt and UAF_MENUUSE)<>0
+ end;
+ end;
+end;
+
+procedure ShowAction(Dialog:HWND;item:integer=-1);
+var
+ i,j:integer;
+ wnd:HWND;
+ li:LV_ITEMW;
+ buf:array [0..255] of WideChar;
+ lset:HWND;
+begin
+ wnd:=GetDlgItem(Dialog,IDC_UA_PLACELIST);
+ SendMessage(wnd,LVM_DELETEALLITEMS,0,0);
+ j:=LV_GetLParam(GetDlgItem(Dialog,IDC_UA_ACTIONLIST),item);
+ if j>=0 then
+ begin
+ with UActionList[j] do
+ begin
+ lset:=settings;
+ settings:=0;
+ // make "places" list
+ for i:=0 to NumTypes-1 do
+ begin
+ with NamesArray[i] do
+ begin
+ if enable then // cached ability flag
+ begin
+ li.mask :=LVIF_TEXT+LVIF_PARAM;
+ li.iSubItem:=0;
+ li.iItem :=i;
+ li.lParam :=atype; //!!!!!! need to add subtype
+ li.pszText :=TranslateW(FastAnsiToWideBuf(name,buf));
+ li.iItem :=SendMessageW(wnd,LVM_INSERTITEMW,0,LPARAM(@li));
+
+ ListView_SetCheckState(wnd,li.iItem,isPlaceActive(j,atype));
+ end;
+ end;
+ end;
+ ListView_SetItemState(wnd,0,
+ LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+ // show first selected "place"
+ ShowSubAction(Dialog,LV_GetLParam(wnd));
+ settings:=lset;
+ end;
+ end
+ else
+ begin
+ ClearControls(Dialog);
+ SetupControls(Dialog,-1,-1);
+ end;
+end;
+
+procedure SetChangedFlag(Dialog:HWND);
+var
+ num,atype:integer;
+begin
+ num :=LV_GetLParam(GetDlgItem(Dialog,IDC_UA_ACTIONLIST),-1);
+ atype:=LV_GetLParam(GetDlgItem(Dialog,IDC_UA_PLACELIST ),-1);
+ UActionList[num].UAMenuItem[tMenuType(HiByte(atype))].changed:=true;
+end;
+
+procedure SaveMenuSubAction(Dialog:HWND;var MenuItem:tUAMenuItem);
+begin
+ with MenuItem do
+ begin
+ mFreeMem(szMenuPopup ); szMenuPopup :=GetDlgText(Dialog,IDC_UA_POPUPV);
+ mFreeMem(szMenuNameVars ); szMenuNameVars :=GetDlgText(Dialog,IDC_UA_VARNAMESV);
+ mFreeMem(szMenuShowWhenVars); szMenuShowWhenVars:=GetDlgText(Dialog,IDC_UA_SHOWVARV);
+ menu_opt:=0;
+ if IsDlgButtonchecked(Dialog,IDC_UA_SEPARATE)<>BST_UNCHECKED then
+ menu_opt:=menu_opt or UAF_MENUSEP;
+ end;
+end;
+
+procedure SetPlaceActive(idx,place:integer;active:boolean);
+begin
+ with UActionList[idx] do
+ case LoByte(place) of
+ uaTTB : if active then flags:=flags or UAF_REGTTBB else flags:=flags and not UAF_REGTTBB;
+ uaTAB : if active then flags:=flags or UAF_REGTABB else flags:=flags and not UAF_REGTABB;
+ uaHotkey: if active then flags:=flags or UAF_REGHOTKEY else flags:=flags and not UAF_REGHOTKEY;
+ uaMenu :
+ with UAMenuItem[tMenuType(HiByte(place))] do
+ if active then menu_opt:=menu_opt or UAF_MENUUSE
+ else menu_opt:=menu_opt and not UAF_MENUUSE;
+ end;
+end;
+
+procedure SaveAction(Dialog:HWND;item:integer=-1;atype:integer=-1);
+var
+ i,num:integer;
+ wnd:HWND;
+begin
+ num:=LV_GetLParam(GetDlgItem(Dialog,IDC_UA_ACTIONLIST),item);
+ if num<0 then exit;
+
+ wnd:=GetDlgItem(Dialog,IDC_UA_PLACELIST);
+ atype:=LV_GetLParam(wnd,atype);
+ with UActionList[num] do
+ begin
+ // main flags
+ flags:=flags and not UAF_USING;
+ // common section
+ if IsDlgButtonChecked(Dialog,IDC_UA_TWOSTATE)<>BST_UNCHECKED then
+ flags:=flags or UAF_2STATE
+ else
+ flags:=flags and not UAF_2STATE;
+
+ if IsDlgButtonChecked(Dialog,IDC_UA_SAVSTATE)<>BST_UNCHECKED then
+ flags:=flags or UAF_SAVESTATE
+ else
+ flags:=flags and not UAF_SAVESTATE;
+
+ if IsDlgButtonChecked(Dialog,IDC_UA_GLOBAL)=BST_UNCHECKED then
+ flags:=flags or UAF_GLOBAL
+ else
+ flags:=flags and not UAF_GLOBAL;
+
+ // custom data
+ case LoByte(atype) of
+ uaTTB: begin // CList modern toolbar
+ mFreeMem(szTTBTooltip ); szTTBTooltip :=GetDlgText(Dialog,IDC_UA_TTNORMALV ,true);
+ mFreeMem(szTTBTooltipPressed); szTTBTooltipPressed:=GetDlgText(Dialog,IDC_UA_TTPRESSEDV,true);
+ mFreeMem(szTTBShowWhenVars ); szTTBShowWhenVars :=GetDlgText(Dialog,IDC_UA_SHOWVARV);
+ end;
+
+ uaTAB: begin // TabSRMM toolbar
+ mFreeMem(szTabBTooltip ); szTabBTooltip :=GetDlgText(Dialog,IDC_UA_TTNORMALV);
+ mFreeMem(szTabBTooltipPressed); szTabBTooltipPressed:=GetDlgText(Dialog,IDC_UA_TTPRESSEDV);
+ end;
+
+ uaMenu: SaveMenuSubAction(Dialog,UAMenuItem[tMenuType(HiByte(atype))]);
+
+ uaHotkey: begin // Hotkey
+ // Settings in Customize/Hotkeys
+ end;
+ end;
+ for i:=0 to SendMessage(wnd,LVM_GETITEMCOUNT,0,0)-1 do
+ begin
+ SetPlaceActive(num,LV_GetLParam(wnd,i),ListView_GetCheckState(wnd,i)<>0);
+ end;
+ //just after Action (not place) changes
+ if item<0 then
+ SaveUA(num);
+ end;
+end;
+
+procedure FillActionList(wnd:HWND);
+var
+ i:integer;
+ li:LV_ITEMW;
+ il:HIMAGELIST;
+ lmenu:tMenuType;
+begin
+ wnd:=GetDlgItem(wnd,IDC_UA_ACTIONLIST);
+ SendMessage(wnd,LVM_DELETEALLITEMS,0,0);
+
+ il:=ImageList_Create(16,16,ILC_COLOR32 or ILC_MASK,0,1);
+ for i:=0 to HIGH(UActionList) do
+ begin
+ li.mask :=LVIF_TEXT+LVIF_PARAM+LVIF_IMAGE;
+ li.iSubItem:=0;
+ li.iItem :=i;
+ li.lParam :=i;
+ li.pszText :=UActionList[i].szActDescr;
+ li.iImage:=ImageList_AddIcon(il,
+ HICON(CallService(MS_SKIN2_GETICONBYHANDLE,0,LPARAM(UActionList[i].hIcolibIcon))));
+ li.iItem :=SendMessageW(wnd,LVM_INSERTITEMW,0,LPARAM(@li));
+
+ for lmenu:=main_menu to HIGH(tMenuType) do
+ UActionList[i].UAMenuItem[lmenu].changed:=false;
+ end;
+ ImageList_Destroy(SendMessage(wnd,LVM_SETIMAGELIST,LVSIL_SMALL,il));
+
+ SendMessage(wnd,LVM_SORTITEMS,0,LPARAM(@CompareItem));
+
+ ListView_SetItemState(wnd,0,
+ LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+end;
+
+// refresh icons in UA list (at least)
+function IconChanged(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ i:integer;
+ li:LV_ITEMW;
+ il:HIMAGELIST;
+ wnd:HWND;
+begin
+ result:=0;
+ wnd:=GetDlgItem(settings,IDC_UA_ACTIONLIST);
+
+ il:=ImageList_Create(16,16,ILC_COLOR32 or ILC_MASK,0,1);
+ for i:=0 to HIGH(UActionList) do
+ begin
+ li.mask :=LVIF_IMAGE;
+ li.iSubItem:=0;
+ li.iItem :=i;
+ li.iImage:=ImageList_AddIcon(il,
+ HICON(CallService(MS_SKIN2_GETICONBYHANDLE,0,TLPARAM(UActionList[i].hIcolibIcon))));
+ SendMessageW(wnd,LVM_SETITEM,0,TLPARAM(@li));
+ end;
+ ImageList_Destroy(SendMessage(wnd,LVM_SETIMAGELIST,LVSIL_SMALL,il));
+//!!refresh?
+end;
+
+function DlgProcOpt(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+var
+ wnd:HWND;
+ lv:LV_COLUMNW;
+ i:integer;
+begin
+ result:=0;
+ case hMessage of
+ WM_CLOSE: begin
+ settings:=0;
+ UnhookEvent(hIC);
+ end;
+
+ WM_INITDIALOG: begin
+ settings:=0;
+ TranslateDialogDefault(Dialog);
+
+ wnd:=GetDlgItem(Dialog,IDC_UA_PLACELIST);
+ SendMessage(wnd,LVM_SETEXTENDEDLISTVIEWSTYLE,LVS_EX_CHECKBOXES,LVS_EX_CHECKBOXES);
+ SendMessage(wnd,LVM_SETUNICODEFORMAT,1,0);
+ zeromemory(@lv,sizeof(lv));
+ lv.mask:=LVCF_WIDTH;
+ lv.cx :=110;
+ SendMessageW(wnd,LVM_INSERTCOLUMNW ,0,tlparam(@lv));
+ SendMessageW(wnd,LVM_SETCOLUMNWIDTH,0,LVSCW_AUTOSIZE_USEHEADER);
+
+ wnd:=GetDlgItem(Dialog,IDC_UA_ACTIONLIST);
+// SendMessage(wnd,LVM_SETEXTENDEDLISTVIEWSTYLE,LVS_EX_CHECKBOXES,LVS_EX_CHECKBOXES);
+ SendMessage(wnd,LVM_SETUNICODEFORMAT,1,0);
+ zeromemory(@lv,sizeof(lv));
+ lv.mask:=LVCF_WIDTH;
+ lv.cx :=110;
+ SendMessageW(wnd,LVM_INSERTCOLUMNW ,0,tlparam(@lv));
+ SendMessageW(wnd,LVM_SETCOLUMNWIDTH,0,LVSCW_AUTOSIZE_USEHEADER);
+ FillActionList(Dialog);
+ ShowAction(Dialog,-1);
+
+// if isVarsInstalled then
+ begin
+ SendDlgItemMessage(Dialog,IDC_UA_VARNAMESH,BM_SETIMAGE,IMAGE_ICON,
+ CallService(MS_VARS_GETSKINITEM,0,VSI_HELPICON));
+ SendDlgItemMessage(Dialog,IDC_UA_SHOWVARH,BM_SETIMAGE,IMAGE_ICON,
+ CallService(MS_VARS_GETSKINITEM,0,VSI_HELPICON));
+// SendDlgItemMessage(Dialog,IDC_UA_VARNAMESH,BUTTONSETASFLATBTN,0,0);
+// SendDlgItemMessage(Dialog,IDC_UA_SHOWVARH ,BUTTONSETASFLATBTN,0,0);
+ end;
+
+ settings:=Dialog;
+ hIC:=HookEvent(ME_SKIN2_ICONSCHANGED,@IconChanged);
+ end;
+
+ WM_COMMAND: begin
+ case wParam shr 16 of
+ EN_CHANGE: begin
+ if settings<>0 then
+ begin
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ if loword(wParam)=IDC_UA_POPUPV then
+ SetChangedFlag(Dialog);
+ end;
+ end;
+
+ BN_CLICKED: begin
+ if settings<>0 then
+ begin
+ case loword(wParam) of
+ IDC_UA_TWOSTATE: begin
+ SetupControls(Dialog,
+ LV_GetLParam(GetDlgItem(Dialog,IDC_UA_PLACELIST)),-1);
+ {
+ EnableWindow(GetDlgItem(Dialog,IDC_UA_TTPRESSEDV),
+ IsDlgButtonChecked(Dialog,IDC_UA_TWOSTATE)<>BST_UNCHECKED);
+ }
+ if IsDlgButtonChecked(Dialog,IDC_UA_TWOSTATE)=BST_UNCHECKED then
+ DeleteIcolibIconP(
+ UActionList[LV_GetLParam(GetDlgItem(Dialog,IDC_UA_ACTIONLIST),-1)])
+ else
+ AddIcolibIconP(
+ UActionList[LV_GetLParam(GetDlgItem(Dialog,IDC_UA_ACTIONLIST),-1)]);
+
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+
+ IDC_UA_VARNAMESH: ShowVarHelp(Dialog,IDC_UA_VARNAMESV);
+ IDC_UA_SHOWVARH : ShowVarHelp(Dialog,IDC_UA_SHOWVARV);
+
+//??? IDC_UA_SAVSTATE,
+ IDC_UA_GLOBAL: SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+
+ IDC_UA_SEPARATE: begin
+ SetChangedFlag(Dialog);
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+ end;
+ end;
+ end;
+ end;
+ end;
+
+ WM_NOTIFY: begin
+ case integer(PNMHdr(lParam)^.code) of
+ PSN_APPLY: begin
+ SaveAction(Dialog);
+ SaveUAs;
+ FillChar(arMenuRec[0],Length(arMenuRec)*SizeOf(tuaMenuRecA),0);
+ for i:=0 to HIGH(UActionList) do
+ begin
+ SetAllActionUsers(UActionList[i],false);
+ end;
+ end;
+
+ LVN_ITEMCHANGED: begin
+ if settings=0 then exit;
+ if PNMLISTVIEW(lParam)^.uChanged=LVIF_STATE then
+ begin
+ i:=(PNMLISTVIEW(lParam)^.uOldState and LVNI_FOCUSED)-
+ (PNMLISTVIEW(lParam)^.uNewState and LVNI_FOCUSED);
+
+ if i>0 then // old focus
+ begin
+ if wParam=IDC_UA_ACTIONLIST then
+ SaveAction(Dialog,PNMLISTVIEW(lParam)^.iItem)
+ else //if wParam=IDC_UA_PLACELIST then
+ SaveAction(Dialog,-1,PNMLISTVIEW(lParam)^.iItem);
+ end
+ else if i<0 then // new focus
+ begin
+ if wParam=IDC_UA_ACTIONLIST then
+ ShowAction(Dialog,PNMLISTVIEW(lParam)^.iItem)
+ else//if wParam=IDC_UA_PLACELIST then
+ ShowSubAction(Dialog,
+ LV_GetLParam(GetDlgItem(Dialog,IDC_UA_PLACELIST),
+ PNMLISTVIEW(lParam)^.iItem));
+ end
+ else if (settings<>0) and
+ ((PNMLISTVIEW(lParam)^.uOldState or PNMLISTVIEW(lParam)^.uNewState)=$3000) then
+ begin
+ // which type
+ wnd:=GetDlgItem(Dialog,IDC_UA_PLACELIST);
+ if PNMLISTVIEW(lParam)^.iItem<>
+ SendMessage(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED) then
+ ListView_SetItemState(wnd,PNMLISTVIEW(lParam)^.iItem,
+ LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED)
+ else
+ SetupControls(Dialog,LV_GetLParam(wnd,PNMLISTVIEW(lParam)^.iItem),
+ PNMLISTVIEW(lParam)^.iItem);
+
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+ end;
+ end;
+ end;
+ end;
+ end;
+end;
diff --git a/plugins/Actman/ua/i_options.inc b/plugins/Actman/ua/i_options.inc
new file mode 100644
index 0000000000..91f54e89d8
--- /dev/null
+++ b/plugins/Actman/ua/i_options.inc
@@ -0,0 +1,337 @@
+{}
+const
+ opt_TTBTooltip :pAnsiChar = 'MTBTooltip';
+ opt_TTBTooltipPressed :pAnsiChar = 'MTBTooltipPressed';
+ opt_TTBShowWhenVars :pAnsiChar = 'MTBVarStr';
+
+ opt_TabBTooltip :pAnsiChar = 'TabBTooltip';
+ opt_TabBTooltipPressed:pAnsiChar = 'TabBTooltipPressed';
+
+ opt_MenuPopup :pAnsiChar = 'MenuPopup';
+ opt_MenuNameVars :pAnsiChar = 'MenuName';
+ opt_MenuShowWhenVars:pAnsiChar = 'MenuVarStr';
+ opt_MenuOptions :pAnsiChar = 'MenuOptions';
+
+{}
+procedure DeleteUASettings(idx:integer);
+var
+ setting:array [0..63] of AnsiChar;
+ p,pm:pAnsiChar;
+ lmenu:tMenuType;
+begin
+ with UActionList[idx] do
+ begin
+ p:=GetUABranch(setting,dwActID);
+ if p<>nil then
+ begin
+ p:=StrCopyE(p,opt_UA);
+ p^:='/'; inc(p);
+
+ StrCopy(p,opt_Flags); DBDeleteSetting(0,DBBranch,setting);
+
+ StrCopy(p,opt_TTBTooltip ); DBDeleteSetting(0,DBBranch,setting);
+ StrCopy(p,opt_TTBTooltipPressed ); DBDeleteSetting(0,DBBranch,setting);
+ StrCopy(p,opt_TTBShowWhenVars ); DBDeleteSetting(0,DBBranch,setting);
+
+ StrCopy(p,opt_TabBTooltip ); DBDeleteSetting(0,DBBranch,setting);
+ StrCopy(p,opt_TabBTooltipPressed); DBDeleteSetting(0,DBBranch,setting);
+
+ for lmenu:=main_menu to HIGH(tMenuType) do
+ begin
+ pm:=p;
+ pm^:=AnsiChar(ORD(lmenu)+ORD('0')); inc(pm);
+ pm^:='_'; inc(pm);
+ StrCopy(pm,opt_MenuPopup ); DBDeleteSetting(0,DBBranch,setting);
+ StrCopy(pm,opt_MenuNameVars ); DBDeleteSetting(0,DBBranch,setting);
+ StrCopy(pm,opt_MenuShowWhenVars); DBDeleteSetting(0,DBBranch,setting);
+ StrCopy(pm,opt_MenuOptions ); DBDeleteSetting(0,DBBranch,setting);
+ end;
+ end;
+ end;
+end;
+
+procedure addSaveUA(setting:pAnsiChar;txt:pWideChar); overload;
+begin
+ if (txt=nil) or (txt^=#0) then DBDeleteSetting(0,DBBranch,setting)
+ else DBWriteUnicode(0,DBBranch,setting,txt);
+end;
+
+procedure addSaveUA(setting:pAnsiChar;txt:pAnsiChar); overload;
+begin
+ if (txt=nil) or (txt^=#0) then DBDeleteSetting(0,DBBranch,setting)
+ else DBWriteString(0,DBBranch,setting,txt);
+end;
+
+procedure SaveUA(idx:integer);
+var
+ setting:array [0..63] of AnsiChar;
+ p,pm:pAnsiChar;
+ lmenu:tMenuType;
+begin
+ with UActionList[idx] do
+ begin
+ p:=GetUABranch(setting,dwActID);
+ if p<>nil then
+ begin
+ p:=StrCopyE(p,opt_UA);
+ p^:='/'; inc(p);
+
+ StrCopy(p,opt_Flags); DBWriteDWord(0,DBBranch,setting,
+ flags and not (UAF_REALTIME OR UAF_SPECIAL));
+
+ StrCopy(p,opt_TTBTooltip ); addSaveUA(setting,szTTBTooltip);
+ StrCopy(p,opt_TTBTooltipPressed); addSaveUA(setting,szTTBTooltipPressed);
+ StrCopy(p,opt_TTBShowWhenVars ); addSaveUA(setting,szTTBShowWhenVars);
+
+ StrCopy(p,opt_TabBTooltip ); addSaveUA(setting,szTabBTooltip);
+ StrCopy(p,opt_TabBTooltipPressed); addSaveUA(setting,szTabBTooltipPressed);
+
+ for lmenu:=main_menu to HIGH(tMenuType) do
+ begin
+ pm:=p;
+ pm^:=AnsiChar(ORD(lmenu)+ORD('0')); inc(pm);
+ pm^:='_'; inc(pm);
+ with UAMenuItem[lmenu] do
+ begin
+ StrCopy(pm,opt_MenuPopup ); addSaveUA(setting,szMenuPopup);
+ StrCopy(pm,opt_MenuNameVars ); addSaveUA(setting,szMenuNameVars);
+ StrCopy(pm,opt_MenuShowWhenVars); addSaveUA(setting,szMenuShowWhenVars);
+ StrCopy(pm,opt_MenuOptions ); DBWriteWord(0,DBBranch,setting,menu_opt);
+ end;
+ end;
+ end;
+ end;
+end;
+
+procedure SaveUAs;
+var
+ i:integer;
+begin
+ for i:=0 to HIGH(UActionList) do
+ SaveUA(i);
+end;
+
+function LoadUA(idx:integer):integer;
+var
+ setting:array [0..63] of AnsiChar;
+ p,pm:pAnsiChar;
+ lmenu:tMenuType;
+begin
+ result:=0;
+ with UActionList[idx] do
+ begin
+ p:=GetUABranch(setting,dwActID);
+ if p<>nil then
+ begin
+ p:=StrCopyE(p,opt_UA);
+ p^:='/'; inc(p);
+
+ StrCopy(p,opt_Flags);
+ flags:=DBReadDWord(0,DBBranch,setting,dword(UAF_SPECIAL));
+ if flags=dword(UAF_SPECIAL) then // no settings
+ begin
+ flags:=0;
+ exit;
+ end;
+ // no need to use previous "pressed" state
+ if (flags and UAF_SAVESTATE)=0 then
+ flags:=flags and not UAF_PRESSED;
+
+ flags:=flags and not UAF_REALTIME;
+ result:=1;
+
+ StrCopy(p,opt_TTBTooltip ); szTTBTooltip :=DBReadString (0,DBBranch,setting);
+ StrCopy(p,opt_TTBTooltipPressed); szTTBTooltipPressed:=DBReadString (0,DBBranch,setting);
+ StrCopy(p,opt_TTBShowWhenVars ); szTTBShowWhenVars :=DBReadUnicode(0,DBBranch,setting);
+
+ StrCopy(p,opt_TabBTooltip ); szTabBTooltip :=DBReadUnicode(0,DBBranch,setting);
+ StrCopy(p,opt_TabBTooltipPressed); szTabBTooltipPressed:=DBReadUnicode(0,DBBranch,setting);
+
+ for lmenu:=main_menu to HIGH(tMenuType) do
+ begin
+ pm:=p;
+ pm^:=AnsiChar(ORD(lmenu)+ORD('0')); inc(pm);
+ pm^:='_'; inc(pm);
+ with UAMenuItem[lmenu] do
+ begin
+ StrCopy(pm,opt_MenuPopup ); szMenuPopup :=DBReadUnicode(0,DBBranch,setting);
+ StrCopy(pm,opt_MenuNameVars ); szMenuNameVars :=DBReadUnicode(0,DBBranch,setting);
+ StrCopy(pm,opt_MenuShowWhenVars); szMenuShowWhenVars:=DBReadUnicode(0,DBBranch,setting);
+ StrCopy(pm,opt_MenuOptions ); menu_opt :=DBReadWord (0,DBBranch,setting);
+ end;
+ end;
+ end;
+ end;
+end;
+(*
+function LoadUAs:integer;
+{
+var
+ section:array [0..63] of AnsiChar;
+ p:PAnsiChar;
+ i:integer;
+}
+begin
+ result:=0;
+{
+ MaxTasks:=DBReadByte(0,opt_tasks,opt_count);
+ result:=MaxTasks;
+ if MaxTasks>0 then
+ begin
+ GetMem (TaskList ,MaxTasks*SizeOf(tTaskRec));
+ FillChar(TaskList^,MaxTasks*SizeOf(tTaskRec),0);
+ for i:=0 to MaxTasks-1 do
+ begin
+ p:=StrEnd(IntToStr(section,i));
+ with TaskList[i] do
+ begin
+ StrCopy(p,opt_flags ); flags :=DBReadDWord (0,opt_tasks,section);
+ StrCopy(p,opt_name ); name :=DBReadUnicode(0,opt_tasks,section);
+ StrCopy(p,opt_action); action :=DBReadDWord (0,opt_tasks,section);
+ end;
+ end;
+ end;
+}
+end;
+*)
+procedure SetAllActionUsers(var ActionItem:tMyActionItem; initial:boolean);
+var
+ setting:array [0..63] of AnsiChar;
+ p:pAnsiChar;
+ luse:boolean;
+ lmenu:tMenuType;
+begin
+ if NamesArray[uaHotkey].enable then
+ begin
+ if (ActionItem.flags and UAF_REGHOTKEY)<>0 then
+ AddCoreHotkey(ActionItem)
+ else
+ DeleteCoreHotkey(ActionItem);
+ end;
+ if not initial then
+ begin
+ if NamesArray[uaTTB].enable then
+ begin
+ DeleteTTBButton(ActionItem); // no modify command there, just delete, then insert back
+ if (ActionItem.flags and UAF_REGTTBB)<>0 then
+ AddTTBButton(ActionItem);
+ end;
+
+ if NamesArray[uaTAB].enable then
+ begin
+ if (ActionItem.flags and UAF_REGTABB)<>0 then
+ AddTabBBButton(ActionItem)
+ else
+ DeleteTabBBButton(ActionItem);
+ end;
+ end;
+
+ luse:=false;
+ for lmenu:=main_menu to HIGH(tMenuType) do
+ begin
+ if NamesArray[uaMenu+ORD(lmenu)].enable then
+ begin
+ if (ActionItem.UAMenuItem[lmenu].menu_opt and UAF_MENUUSE)<>0 then
+ begin
+ luse:=true;
+ if ActionItem.UAMenuItem[lmenu].changed then
+ DeleteMenuItem(ActionItem,lmenu);
+ CreateMenuItem(ActionItem,lmenu);
+ end
+ else
+ DeleteMenuItem(ActionItem,lmenu);
+ end;
+ end;
+
+ if (not luse) and (ActionItem.hMenuService<>0) then
+ begin
+ DestroyServiceFunction(ActionItem.hMenuService);
+ ActionItem.hMenuService:=0;
+ end;
+
+ // First run (ok ok, if ppl ask for it....)
+ p:=GetUABranch(setting,ActionItem.dwActID);
+ if p<>nil then
+ begin
+ p:=StrCopyE(p,opt_UA);
+ p^:='/'; inc(p);
+ StrCopy(p,'_FirstRun');
+ if DBReadByte(0,DBBranch,setting,0)<>0 then
+ begin
+ CAllService(MS_ACT_RUNBYID,ActionItem.dwActID,0);
+ DBDeleteSetting(0,DBBranch,setting);
+ end;
+ end;
+
+end;
+
+procedure DeleteUAction(num:integer);
+var
+ ActionItem:pMyActionItem;
+ setting:array [0..63] of AnsiChar;
+ p:pAnsiChar;
+ luse:boolean;
+ lmenu:tMenuType;
+begin
+ DeleteUASettings(num);
+
+ ActionItem:=@UActionList[num];
+
+ DeleteIcolibIcon(ActionItem^);
+
+ if (ActionItem.flags and UAF_REGHOTKEY)<>0 then
+ DeleteCoreHotkey(ActionItem^);
+
+ if (ActionItem.flags and UAF_REGTTBB)<>0 then
+ DeleteTTBButton(ActionItem^);
+ mFreeMem(ActionItem.szTTBTooltip);
+ mFreeMem(ActionItem.szTTBTooltipPressed);
+ mFreeMem(ActionItem.szTTBShowWhenVars);
+
+ if (ActionItem.flags and UAF_REGTABB)<>0 then
+ DeleteTabBBButton(ActionItem^);
+ mFreeMem(ActionItem.szTabBTooltip);
+ mFreeMem(ActionItem.szTabBTooltipPressed);
+
+ luse:=false;
+ for lmenu:=main_menu to HIGH(tMenuType) do
+ begin
+ with ActionItem.UAMenuItem[lmenu] do
+ begin
+ if (menu_opt and UAF_MENUUSE)<>0 then
+ begin
+ luse:=true;
+ DeleteMenuItem(ActionItem^,lmenu);
+ end;
+ mFreeMem(szMenuPopup);
+ mFreeMem(szMenuNameVars);
+ mFreeMem(szMenuShowWhenVars);
+ end;
+ end;
+
+ if (not luse) and (ActionItem.hMenuService<>0) then
+ begin
+ DestroyServiceFunction(ActionItem.hMenuService);
+ ActionItem.hMenuService:=0;
+ end;
+
+ p:=GetUABranch(setting,ActionItem.dwActID);
+ if p<>nil then
+ begin
+ p:=StrCopyE(p,opt_UA);
+ p^:='/'; inc(p);
+ StrCopy(p,'_FirstRun');
+ DBDeleteSetting(0,DBBranch,setting);
+ end;
+
+ // Free Memory
+ mFreeMem(ActionItem.szNameID);
+ mFreeMem(ActionItem.szActDescr);
+
+ // compact list
+ if num<HIGH(UActionList) then
+ begin
+ move(UActionList[num+1],UActionList[num],(HIGH(UACtionList)-num)*SizeOf(tMyActionItem));
+ end;
+ SetLength(UActionList,Length(UActionList)-1);
+end;
diff --git a/plugins/Actman/ua/i_ua.inc b/plugins/Actman/ua/i_ua.inc
new file mode 100644
index 0000000000..61641a7447
--- /dev/null
+++ b/plugins/Actman/ua/i_ua.inc
@@ -0,0 +1,155 @@
+{}
+function AddUAction(idx:integer; ptr:pChain):integer;
+var
+ buf:array [0..127] of AnsiChar;
+begin
+ if idx<0 then idx:=Length(UActionList);
+ if idx=Length(UActionList) then
+ SetLength(UActionList,Length(UActionList)+1);
+
+ FillChar(UActionList[idx],SizeOf(tMyActionItem),0);
+
+ with UActionList[idx] do
+ begin
+ // get Action settings
+ dwActID:=ptr^.id;
+ if (ptr^.flags and ACCF_DISABLED)<>0 then
+ flags:=UAF_DISABLED;
+ StrDupW(szActDescr,ptr^.descr);
+ wSortIndex:=idx;
+
+ // prepare for work
+ IntToStr(StrCopyE(buf,'Actions/Action_'),ptr^.id);
+ StrDup(szNameID,@buf);
+ AddIcolibIcon (UActionList[idx]);
+ end;
+
+ SetLength(arMenuRec,Length(UActionList)+1);
+ FillChar (arMenuRec[HIGH(arMenuRec)],SizeOf(tuaMenuRecA),0);
+ result:=idx;
+end;
+
+function CreateUActionList:integer;
+var
+ ptr,ptr1:pChain;
+ i:integer;
+begin
+ result:=CallService(MS_ACT_GETLIST,0,LPARAM(@ptr));
+ SetLength(UActionList,result);
+
+ SetLength(arMenuRec, result+1);
+ FillChar (arMenuRec[0],(result+1)*SizeOf(tuaMenuRecA),0);
+
+ if result>0 then
+ begin
+ ptr1:=ptr;
+ inc(pbyte(ptr),4);
+ for i:=0 to result-1 do
+ begin
+ AddUAction(i,ptr);
+ LoadUA(i); // just here coz at list changes for new we don't have settings
+ if (UActionList[i].flags and UAF_2STATE)<>0 then
+ AddIcolibIconP(UActionList[i]);
+ SetAllActionUsers(UActionList[i],true);
+ inc(ptr);
+ end;
+ CallService(MS_ACT_FREELIST,0,LPARAM(ptr1));
+ end;
+end;
+
+function ActListChange(wParam:WPARAM;lParam:LPARAM):integer; cdecl;
+var
+ ptr,ptr1:pChain;
+ idx,i,j,count:integer;
+ bFound:boolean;
+begin
+ result:=0;
+
+ count:=CallService(MS_ACT_GETLIST,0,TLPARAM(@ptr));
+
+ if count>0 then
+ begin
+ ptr1:=ptr;
+ inc(pbyte(ptr),4);
+ // maybe add ACTM_RELOAD (as NEW and DELETE) here too?
+ if (wParam and (ACTM_NEW or ACTM_RENAME or ACTM_SORT or ACTM_DELETE))<>0 then
+ for i:=0 to count-1 do
+ begin
+ // search corresponding element
+ idx:=-1;
+ for j:=0 to HIGH(UActionList) do
+ begin
+ if UActionList[j].dwActID=ptr^.id then
+ begin
+ idx:=j;
+ break;
+ end;
+ end;
+ // if we have no item in list for this action - then add new one
+ if idx<0 then
+ AddUAction(-1,ptr)
+ else
+ begin
+ if (wParam and ACTM_RENAME)<>0 then
+ begin
+ // check for time economy - no need to change ALL items
+ if StrCmpW(UActionList[idx].szActDescr,ptr^.descr)<>0 then
+ begin
+ mFreeMem(UActionList[idx].szActDescr);
+ StrDupW (UActionList[idx].szActDescr,ptr^.descr);
+ end;
+ end;
+
+ if (wParam and (ACTM_SORT or ACTM_DELETE or ACTM_NEW))<>0 then
+ UActionList[idx].wSortIndex:=i;
+ end;
+ inc(ptr);
+ end;
+ end
+ else
+ ptr1:=nil;
+
+ // now search deleted items
+ if (wParam and ACTM_DELETE)<>0 then
+ begin
+ for j:=HIGH(UActionList) downto 0 do
+ begin
+ bFound:=false;
+ if count>0 then
+ begin
+ ptr:=ptr1;
+ inc(pbyte(ptr),4);
+ for i:=0 to count-1 do
+ begin
+ if UActionList[j].dwActID=ptr^.id then
+ begin
+ bFound:=true;
+ break;
+ end;
+ inc(ptr);
+ end;
+ end;
+ if not bFound then
+ DeleteUAction(j);
+ end;
+ end;
+
+ if count>0 then
+ CallService(MS_ACT_FREELIST,0,TLPARAM(ptr1));
+
+ if settings<>0 then
+ begin
+ FillActionList(settings);
+ ShowAction(settings,-1);
+ SendMessage(GetParent(settings),PSM_CHANGED,0,0);
+ end
+ else
+ begin
+ SaveUAs;
+ FillChar(arMenuRec[0],Length(arMenuRec)*SizeOf(tuaMenuRecA),0);
+ for i:=0 to HIGH(UActionList) do
+ begin
+ SetAllActionUsers(UActionList[i],false);
+ end;
+ end;
+end;
diff --git a/plugins/Actman/ua/i_uaplaces.inc b/plugins/Actman/ua/i_uaplaces.inc
new file mode 100644
index 0000000000..1d923e25a3
--- /dev/null
+++ b/plugins/Actman/ua/i_uaplaces.inc
@@ -0,0 +1,831 @@
+{}
+
+const
+ MenuServices:array [tMenuType] of pAnsiChar = (
+ 'CList/AddMainMenuItem' {MS_CLIST_ADDMAINMENUITEM },
+ 'CList/AddContactMenuItem'{MS_CLIST_ADDCONTACTMENUITEM},
+ 'CList/AddTrayMenuItem' {MS_CLIST_ADDTRAYMENUITEM },
+ 'CList/AddProtoMenuItem' {MS_CLIST_ADDPROTOMENUITEM },
+ 'CList/AddStatusMenuItem' {MS_CLIST_ADDSTATUSMENUITEM }
+ );
+type
+ tuaMenuRec = record
+ hMenuRoot:HMENU;
+ position :integer;
+ end;
+ tuaMenuRecA = array [tMenuType] of tuaMenuRec;
+
+var
+ arMenuRec: array of tuaMenuRecA;
+
+//===== Support =====
+
+function ServiceCallWithLParam(wParam:WPARAM; lParam:LPARAM):int_ptr; cdecl;
+begin
+ result:=CallService(MS_ACT_RUNBYID, lParam, wParam);
+end;
+
+procedure SetTTBState(var ActionItem:tMyActionItem);
+var
+ lflag:integer;
+begin
+ if ActionItem.hTTBButton=0 then exit;
+ if (ActionItem.flags and UAF_2STATE)=0 then exit;
+
+ lflag:=CallService(MS_TTB_GETBUTTONSTATE,ActionItem.hTTBButton,0);
+ if lflag=TTBST_PUSHED then
+ begin
+ if (ActionItem.flags and (UAF_2STATE+UAF_PRESSED))<>(UAF_2STATE+UAF_PRESSED) then exit;
+ lflag:=TTBST_RELEASED;
+ end
+ else
+ begin
+ if (ActionItem.flags and (UAF_2STATE+UAF_PRESSED))=(UAF_2STATE+UAF_PRESSED) then exit;
+ if (ActionItem.flags and UAF_PRESSED)=0 then exit;
+ lflag:=TTBST_PUSHED;
+ end;
+ CallService(MS_TTB_SETBUTTONSTATE,ActionItem.hTTBButton,lflag);
+end;
+
+procedure SetTABState(hContact:THANDLE;var ActionItem:tMyActionItem;pressed:integer);
+var
+ tabb:BBButton;
+ pc:pWideChar;
+begin
+ FillChar(tabb,SizeOf(tabb),0);
+ tabb.cbSize :=SizeOf(tabb);
+ tabb.dwButtonID :=ActionItem.dwActID;
+ tabb.pszModuleName:=MODULE_NAME;
+
+ if pressed<>0 then
+ begin
+ pc:=ActionItem.szTabBTooltipPressed;
+ if pc=nil then pc:=ActionItem.szTabBTooltip;
+ tabb.hIcon:=ActionItem.hIcolibIconPressed;
+ tabb.bbbFlags:=BBSF_PUSHED;
+ end
+ else
+ begin
+ pc:=ActionItem.szTabBTooltip;
+ tabb.hIcon:=ActionItem.hIcolibIcon;
+ tabb.bbbFlags:=BBSF_RELEASED;
+ end;
+ if pc=nil then pc:=ActionItem.szActDescr;
+ tabb.szTooltip.w:=pc;
+ CallService(MS_BB_SETBUTTONSTATE,hContact,TLPARAM(@tabb));
+end;
+
+function IsLocalItem(const UAItem:tMyActionItem):boolean;
+begin
+ result:=((UAItem.flags and UAF_GLOBAL)=0) and
+ (UAItem.UAMenuItem[main_menu ].hMenuItem=0) and
+ (UAItem.UAMenuItem[tray_menu ].hMenuItem=0) and
+ (UAItem.UAMenuItem[proto_menu ].hMenuItem=0) and
+ (UAItem.UAMenuItem[status_menu].hMenuItem=0) and
+ (UAItem.hTTBButton=0);
+end;
+
+function ServiceCallWithFParam(wParam:WPARAM; lParam:LPARAM; fParam:LPARAM):int_ptr; cdecl;
+var
+ i:integer;
+ setting:array [0..63] of AnsiChar;
+ p:pAnsiChar;
+ cnt:THANDLE;
+ state:integer;
+begin
+ for i:=0 to HIGH(UActionList) do
+ begin
+ with UActionList[i] do
+ if dwActID=cardinal(fParam) then
+ if (flags and UAF_2STATE)<>0 then
+ begin
+ // sync buttons/menus
+ if IsLocalItem(UActionList[i]) then
+ begin
+ // if (flags and UAF_SAVESTATE)<>0 then
+ begin
+ state:=DBReadByte(lastContact,opt_ua,szNameID);
+ state:=state xor 1;
+ DBWriteByte(lastContact,opt_ua,szNameID,state);
+ cnt:=lastContact;
+ end;
+ end
+ else
+ begin
+ flags:=flags xor UAF_PRESSED;
+ // save "pressed" state
+ if (flags and UAF_SAVESTATE)<>0 then
+ begin
+ p:=GetUABranch(setting,dwActID);
+ if p<>nil then
+ begin
+ p:=StrCopyE(p,opt_ua);
+ p^:='/'; inc(p);
+ StrCopy(p,opt_flags);
+ DBWriteDWord(0,DBBranch,setting,flags and not UAF_REALTIME);
+ end;
+ end;
+
+ if hTTBButton<>0 then
+ SetTTBState(UActionList[i]);
+
+ cnt:=0;
+ state:=ORD(flags and UAF_PRESSED);
+ end;
+ if (flags and UAF_REGTABB)<>0 then
+ SetTABState(cnt,UActionList[i],state);
+
+ break;
+ end;
+ end;
+
+ result:=CallService(MS_ACT_RUNBYID, fParam, wParam);
+end;
+
+function AddIcolibIconP(var ActionItem:tMyActionItem):THANDLE;
+var
+ sid:TSKINICONDESC;
+ buf,buf1:array [0..63] of WideChar;
+begin
+ if (ActionItem.hIcolibIconPressed=0) or
+ (ActionItem.hIcolibIconPressed=ActionItem.hIcolibIcon) then
+ begin
+ // add icon for action to icolib
+ fillChar(sid,SizeOf(sid),0);
+ sid.cbSize :=sizeof(sid);
+ sid.szSection .w:=ICOLIB_ACTSECTION;
+ sid.szDefaultFile.w:=szMyPath;
+ sid.iDefaultIndex :=-IDI_ACTION;
+ sid.cx :=16;
+ sid.cy :=16;
+ sid.flags :=SIDF_ALL_UNICODE;
+ // icon "off"
+ StrCopyW(StrCopyEW(buf,ActionItem.szActDescr),' (pressed)');
+ sid.szDescription.w:=@buf;
+ StrCopy(StrCopyE(@buf1,ActionItem.szNameID),'_pressed');
+ sid.pszName :=@buf1;
+ ActionItem.hIcolibIconPressed:=Skin_AddIcon(@sid);
+ end;
+ result:=ActionItem.hIcolibIconPressed;
+end;
+
+function AddIcolibIcon(var ActionItem:tMyActionItem):THANDLE;
+var
+ sid:TSKINICONDESC;
+begin
+ if ActionItem.hIcolibIcon=0 then
+ begin
+ // add icon for action to icolib
+ fillChar(sid,SizeOf(sid),0);
+ sid.cbSize :=sizeof(sid);
+ sid.szSection .w:=ICOLIB_ACTSECTION;
+ sid.szDefaultFile.w:=szMyPath;
+ sid.iDefaultIndex :=-IDI_ACTION;
+ sid.cx :=16;
+ sid.cy :=16;
+ sid.flags :=SIDF_ALL_UNICODE;
+ // icon "on"
+ sid.szDescription.w:=ActionItem.szActDescr;
+ sid.pszName :=ActionItem.szNameID;
+ ActionItem.hIcolibIcon:=Skin_AddIcon(@sid);
+ end;
+ result:=ActionItem.hIcolibIcon;
+end;
+
+procedure DeleteIcolibIconP(var ActionItem:tMyActionItem);
+var
+ buf1:array [0..63] of WideChar;
+begin
+ if (ActionItem.hIcolibIconPressed<>0) and
+ (ActionItem.hIcolibIconPressed<>ActionItem.hIcolibIcon) then
+ begin
+ StrCopy(StrCopyE(@buf1,ActionItem.szNameID),'_pressed');
+ CallService(MS_SKIN2_REMOVEICON,0,LPARAM(@buf1));
+ ActionItem.hIcolibIconPressed:=ActionItem.hIcolibIcon;
+ end;
+end;
+
+procedure DeleteIcolibIcon(var ActionItem:tMyActionItem);
+begin
+ DeleteIcolibIconP(ActionItem);
+ CallService(MS_SKIN2_REMOVEICON,0,LPARAM(ActionItem.szNameID));
+ ActionItem.hIcolibIcon :=0;
+ ActionItem.hIcolibIconPressed:=0;
+end;
+
+//===== Really places =====
+
+//----- Hotkeys -----
+
+function AddCoreHotkey(var ActionItem:tMyActionItem):boolean;
+var
+ hkd:THOTKEYDESC;
+begin
+ if (ActionItem.flags and UAF_HKREGGED)=0 then
+ begin
+ FillChar(hkd,SizeOf(hkd),0);
+ hkd.cbSize := SizeOf(hkd); // HOTKEYDESC_SIZE_V1 for pre-0.9
+ hkd.dwFlags := HKD_UNICODE; // since 0.9 only
+ hkd.pszName := ActionItem.szNameID;
+ hkd.pszDescription.w:= ActionItem.szActDescr;
+ hkd.pszSection .w:= MODULE_NAME;
+ hkd.pszService := SERVICE_WITH_LPARAM_NAME;
+ hkd.lParam := ActionItem.dwActID;
+ result:=Hotkey_Register(@hkd)<>0;
+ if result then
+ ActionItem.flags:=ActionItem.flags or UAF_HKREGGED;
+ end
+ else
+ result:=true; //!!
+end;
+
+procedure DeleteCoreHotkey(var ActionItem:tMyActionItem);
+begin
+ if // bCoreHotkeyPresents and
+ // (ServiceExists(MS_HOTKEY_UNREGISTER)<>0) and
+ ((ActionItem.flags and UAF_HKREGGED)<>0) then
+ begin
+ CallService(MS_HOTKEY_UNREGISTER,0,LParam(ActionItem.szNameID));
+ ActionItem.flags:=ActionItem.flags and not UAF_HKREGGED;
+ end;
+end;
+
+//----- Common menu functions -----
+
+function AddRootMenuIcon(szPopupName:pWideChar):THANDLE;
+var
+ sid:TSKINICONDESC;
+begin
+ FillChar(sid,SizeOf(sid),0);
+ //first - register icon for root popup
+ sid.cbSize := sizeof(sid);
+ sid.szSection.w := ICOLIB_MNUSECTION;
+ sid.flags := SIDF_ALL_UNICODE;
+ sid.cx := 16;
+ sid.cy := 16;
+ sid.szDescription.w:= szPopupName;
+ sid.szDefaultFile.w:= szMyPath;
+ sid.iDefaultIndex := -IDI_ACTION;
+ WideToAnsi(szPopupName,sid.pszName);
+ result:=Skin_AddIcon(@sid);
+ mFreeMem(sid.pszName);
+end;
+
+procedure DeleteMenuItem(var ActionItem:tMyActionItem;mtype:tMenuType);
+var
+ i:integer;
+ hMenuRoot:THANDLE;
+ p:pMyActionItem;
+begin
+ with ActionItem.UAMenuItem[mtype] do
+ begin
+ if hMenuItem=0 then exit;
+ CallService(MO_REMOVEMENUITEM,hMenuItem,0);
+ hMenuItem:=0;
+ end;
+
+ hMenuRoot:=ActionItem.UAMenuItem[mtype].hMenuRoot;
+ if hMenuRoot<>0 then
+ begin
+ for i:=0 to HIGH(UActionList) do
+ begin
+ p:=@UActionList[i];
+ // presents somethere else
+ if (p<>@ActionItem) and (p.UAMenuItem[mtype].hMenuRoot=hMenuRoot) then
+ exit;
+ end;
+ // menu array cleanup now?
+ for i:=0 to HIGH(arMenuRec) do
+ begin
+ if arMenuRec[i][mtype].hMenuRoot=hMenuRoot then
+ begin
+ FillChar(arMenuRec[i][mtype],SizeOf(tuaMenuRec),0);
+// arMenuRec[i][mtype].hMenuRoot:=0;
+ break;
+ end;
+ end;
+ CallService(MO_REMOVEMENUITEM,hMenuRoot,0);
+ ActionItem.UAMenuItem[mtype].hMenuRoot:=0;
+ end;
+end;
+
+function GetMenuPosition(hMenu:HMENU;mtype:tMenuType;toset:boolean):integer;
+var
+ i:integer;
+begin
+ result:=0;
+ for i:=0 to HIGH(arMenuRec) do
+ begin
+ if arMenuRec[i][mtype].hMenuRoot=hMenu then
+ begin
+ if toset then
+ inc(arMenuRec[i][mtype].position,100000);
+ result:=arMenuRec[i][mtype].position;
+ break;
+ end;
+ end;
+end;
+
+function MakeMenuItem(mtype:tMenuType;clmi:PCListMenuItem):THANDLE;
+begin
+ case mtype of
+ main_menu : result:=Menu_AddMainMenuItem(clmi);
+ contact_menu: result:=Menu_AddContactMenuItem(clmi);
+ tray_menu : result:=Menu_AddTrayMenuItem(clmi);
+ proto_menu : result:=Menu_AddProtoMenuItem(clmi);
+ status_menu : result:=Menu_AddStatusMenuItem(clmi);
+ else
+ result:=0;
+ end;
+end;
+
+procedure CreateMenuItem(var ActionItem:tMyActionItem;mtype:tMenuType);
+var
+ i:integer;
+ ActItem:pMyActionItem;
+ ActMItem,UAMenuItem:pUAMenuItem;
+ clmi:TCListMenuItem;
+ res:boolean;
+ extra:pWideChar;
+begin
+{}
+ UAMenuItem:=@ActionItem.UAMenuItem[mtype];
+ if UAMenuItem.hMenuItem<>0 then exit;
+
+ // create popup menu
+{}{}
+ res:=true;
+ if (UAMenuItem.szMenuPopup<>nil) and (UAMenuItem.szMenuPopup^<>#0) then
+ begin
+ res:=false;
+ for i:=0 to HIGH(UActionList) do
+ begin
+ // try to find root popup with same name (if we already created one)
+ ActItem :=@UActionList[i];
+ ActMItem:=@ActItem.UAMenuItem[mtype];
+
+ if (ActMItem.szMenuPopup<>nil) and
+ (ActMItem.hMenuRoot<>0) and
+ ( (ActItem<>@ActionItem) and
+ (StrCmpW(ActMItem.szMenuPopup,UAMenuItem.szMenuPopup)=0) ) then
+ begin
+ UAMenuItem.hMenuRoot:=ActMItem.hMenuRoot;
+ res:=true;
+ break;
+ end;
+ end;
+ end;
+ // popup menu not found
+ if not res then
+ begin
+ FillChar(clmi,SizeOf(clmi),0);
+ clmi.cbSize:=SizeOf(clmi);
+ clmi.flags :=CMIF_UNICODE or CMIF_ICONFROMICOLIB;
+
+ if (UAMenuItem.szMenuPopup<>nil) and (UAMenuItem.szMenuPopup^<>#0) then
+ clmi.szName.w:=ParseVarString(UAMenuItem.szMenuPopup)
+ else
+ clmi.szName.w:=ActionItem.szActDescr;
+
+ clmi.hIcon :=AddRootMenuIcon(clmi.szName.w);
+ clmi.position:=ActionItem.wSortIndex*10;
+
+ // position in Root Menu
+ inc(clmi.position,GetMenuPosition(0,mtype,
+ (UAMenuItem.menu_opt and UAF_MENUSEP)<>0));
+
+ UAMenuItem.hMenuRoot:=MakeMenuItem(mtype,@clmi);
+ //CallService(MenuServices[mtype],0,LPARAM(@clmi));
+ if clmi.szName.w<>ActionItem.szActDescr then
+ mFreeMem(clmi.szName.w);
+
+ for i:=1 to HIGH(arMenuRec) do
+ begin
+ with arMenuRec[i][mtype] do
+ if hMenuRoot=0 then
+ begin
+// MenuName :=ActionItem.szActDescr;
+ hMenuRoot:=UAMenuItem.hMenuRoot;
+ break;
+ end;
+ end;
+
+ end;
+{}{}
+
+ // Now Menu Item preparing
+{}{}
+ FillChar(clmi,SizeOf(clmi),0);
+ clmi.cbSize:=SizeOf(clmi);
+ clmi.flags:=CMIF_UNICODE or CMIF_ICONFROMICOLIB;
+ if (ActionItem.flags and (UAF_2STATE+UAF_PRESSED))<>(UAF_2STATE+UAF_PRESSED) then
+ begin
+ clmi.hIcon:=ActionItem.hIcolibIcon;
+ extra:='0';
+ end
+ else
+ begin
+ clmi.hIcon:=ActionItem.hIcolibIconPressed;
+ clmi.flags:=CMIF_UNICODE or CMIF_ICONFROMICOLIB or CMIF_CHECKED;
+ extra:='1';
+ end;
+
+ with ActionItem.UAMenuItem[mtype] do
+ begin
+ if (szMenuNameVars<>nil) and (szMenuNameVars^<>#0) then
+ clmi.szName.w:=ParseVarString(szMenuNameVars,0,extra)
+ else
+ clmi.szName.w:=ActionItem.szActDescr;
+
+ if hMenuRoot<>0 then
+ begin
+ clmi.flags:=clmi.flags or CMIF_ROOTHANDLE;
+ clmi.szPopupName.w:=pWideChar(hMenuRoot);
+ end;
+ end;
+
+ clmi.pszService:=ActionItem.szNameID;
+ if ActionItem.hMenuService=0 then
+ ActionItem.hMenuService:=CreateServiceFunctionParam(
+ clmi.pszService,@ServiceCallWithFParam,ActionItem.dwActID);
+
+ clmi.position:=ActionItem.wSortIndex*10;
+{}{}
+ inc(clmi.position,GetMenuPosition(UAMenuItem.hMenuRoot,mtype,
+ (UAMenuItem.menu_opt and UAF_MENUSEP)<>0));
+
+ UAMenuItem.hMenuItem:=MakeMenuItem(mtype,@clmi);
+ //CallService(MenuServices[mtype],0,LPARAM(@clmi));
+ if clmi.szName.w<>ActionItem.szActDescr then
+ mFreeMem(clmi.szName.w);
+{}
+
+end;
+
+function PreBuildMenu(mtype:tMenuType;hContact:THANDLE=0):int;
+var
+ i:integer;
+ mi:TCListMenuItem;
+ p,extra:pWideChar;
+begin
+ result:=0;
+
+ FillChar(mi,SizeOf(mi),0);
+ mi.cbSize:=SizeOf(mi);
+
+ for i:=0 to HIGH(UActionList) do
+ begin
+ mi.flags:=CMIM_FLAGS;
+ p:=nil;
+ with UActionList[i] do
+ begin
+ with UAMenuItem[mtype] do
+ begin
+ if hMenuItem<>0 then // it means, we process that item here
+ begin
+ mi.szName.w:=nil;
+ // Show / hide
+ if isVarsInstalled then
+ begin
+ if (szMenuShowWhenVars<>nil) and (szMenuShowWhenVars^<>#0) then
+ begin
+ p:=ParseVarString(szMenuShowWhenVars,hContact);
+ if p<>nil then
+ begin
+ if StrCmpW(p,'1')<>0 then
+ mi.flags:=CMIM_FLAGS or CMIF_HIDDEN;
+ mFreeMem(p);
+ end;
+ end;
+ end;
+
+ // change if need to show only
+ // (popup can be used by many items, keep unchanged)
+ if (mi.flags and CMIF_HIDDEN)=0 then
+ begin
+ //!!!! icon (check for contact menu)
+ mi.flags:=mi.flags or CMIM_ICON or CMIM_FLAGS or CMIF_ICONFROMICOLIB;
+
+ if (mtype=contact_menu) and IsLocalItem(UActionList[i]) then
+ begin
+ lastContact:=hContact;
+ if ((flags and UAF_2STATE)<>0) and
+ (DBReadByte(hContact,opt_ua,szNameID)<>0) then
+ begin
+ mi.flags:=mi.flags or CMIF_CHECKED;
+ mi.hIcon:=hIcolibIconPressed;
+ extra:='1';
+ flags:=flags or UAF_PRESSED;
+ end
+ else
+ begin
+ mi.hIcon:=hIcolibIcon;
+ flags:=flags and not UAF_PRESSED;
+ extra:='0';
+ end;
+
+ end
+ else
+ begin
+ if (flags and (UAF_2STATE+UAF_PRESSED))=(UAF_2STATE+UAF_PRESSED) then
+ begin
+ mi.flags:=mi.flags or CMIF_CHECKED;
+ mi.hIcon:=hIcolibIconPressed;
+ extra:='1';
+ end
+ else
+ begin
+ mi.hIcon:=hIcolibIcon;
+ extra:='0';
+ end;
+ end;
+
+ // new name
+ mi.flags:=mi.flags or CMIM_NAME or CMIF_UNICODE;
+ if (szMenuNameVars<>nil) and (szMenuNameVars^<>#0) then
+ mi.szName.w:=ParseVarString(szMenuNameVars,hContact,extra);
+
+ if mi.szName.w=nil then
+ mi.szName.w:=szActDescr;
+ end;
+
+ CallService(MS_CLIST_MODIFYMENUITEM,hMenuItem,LPARAM(@mi));
+ if mi.szName.w<>szActDescr then
+ mFreeMem(mi.szName.w);
+ end;
+ end;
+ end;
+
+ end;
+end;
+
+function PreBuildMainMenu(wParam:WPARAM;lParam:LPARAM):int; cdecl;
+begin
+ result:=PreBuildMenu(main_menu,wParam);
+end;
+
+function PreBuildContactMenu(wParam:WPARAM;lParam:LPARAM):int; cdecl;
+begin
+ result:=PreBuildMenu(contact_menu,wParam);
+end;
+
+function PreBuildTrayMenu(wParam:WPARAM;lParam:LPARAM):int; cdecl;
+begin
+ result:=PreBuildMenu(tray_menu,wParam);
+end;
+
+//----- TopToolbar -----
+
+procedure AddTTBButton(var ActionItem:tMyActionItem);
+var
+ mtButton:TTBButton;
+ pc,pc1,pc2:pAnsiChar;
+ res:boolean;
+ p:pWideChar;
+begin
+ if not NamesArray[uaTTB].enable then exit;
+
+ if ActionItem.hTTBButton=0 then
+ begin
+ // Add or not
+ if isVarsInstalled then
+ begin
+ if (ActionItem.szTTBShowWhenVars<>nil) and (ActionItem.szTTBShowWhenVars^<>#0) then
+ begin
+ p:=ParseVarString(ActionItem.szTTBShowWhenVars);
+ if p<>nil then
+ begin
+ res:=StrCmpW(p,'1')<>0;
+ mFreeMem(p);
+ end
+ else
+ res:=true;
+ if res then
+ exit;
+ end;
+ end;
+
+ FillChar(mtButton,SizeOf(mtButton),0);
+ mtButton.cbSize :=SizeOf(mtButton);
+
+ mtButton.pszService:=TTB_SERVICE_NAME;//SERVICE_WITH_LPARAM_NAME;
+ mtButton.lParamUp :=ActionItem.dwActID;
+ mtButton.lParamDown:=ActionItem.dwActID;
+
+ mtButton.hIconUp:=ActionItem.hIcolibIcon;
+ mtButton.hIconDn:=ActionItem.hIcolibIconPressed;
+
+ WideToAnsi(ActionItem.szActDescr,pc);
+
+ if (ActionItem.flags and UAF_2STATE)<>0 then
+ mtButton.dwFlags:=TTBBF_VISIBLE or TTBBF_SHOWTOOLTIP{ or TTBBF_ASPUSHBUTTON}
+ else
+ mtButton.dwFlags:=TTBBF_VISIBLE or TTBBF_SHOWTOOLTIP;
+
+ if ActionItem.szTTBTooltip =nil then
+ pc1:=pc
+ else pc1:=ActionItem.szTTBTooltip;
+
+ if ((ActionItem.flags and UAF_2STATE)=0) or
+ (ActionItem.szTTBTooltipPressed=nil) then
+ pc2:=pc1
+ else
+ pc2:=ActionItem.szTTBTooltipPressed;
+
+ mtButton.Name :=pc;
+ mtButton.pszTooltipUp :=pc1;
+ mtButton.pszTooltipDn :=pc2;
+
+ ActionItem.hTTBButton:=TopToolbar_AddButton(@mtButton);
+ if ActionItem.hTTBButton=THANDLE(-1) then
+ ActionItem.hTTBButton:=0;
+ mFreeMem(pc);
+ end;
+end;
+
+procedure DeleteTTBButton(var ActionItem:tMyActionItem);
+begin
+ if ActionItem.hTTBButton<>0 then
+ begin
+ CallService(MS_TTB_REMOVEBUTTON,ActionItem.hTTBButton,0);
+ ActionItem.hTTBButton:=0;
+ end;
+end;
+
+function TTBServiceCall(wParam:WPARAM; lParam:LPARAM):int_ptr; cdecl;
+var
+ i,lflag:integer;
+begin
+ result:=0;
+ for i:=0 to HIGH(UActionList) do
+ begin
+ if TLPARAM(UActionList[i].dwActID)=lParam then
+ begin
+ with UActionList[i] do
+ begin
+ if (flags and UAF_2STATE)<>0 then
+ begin
+ if CallService(MS_TTB_GETBUTTONSTATE,hTTBButton,0)=TTBST_PUSHED then
+ begin
+ lflag:=TTBST_RELEASED;
+ end
+ else
+ begin
+ lflag:=TTBST_PUSHED;
+ end;
+ CallService(MS_TTB_SETBUTTONSTATE,hTTBButton,lflag);
+ end;
+ end;
+
+ result:=ServiceCallWithFParam(0,0,lParam);
+ break;
+ end;
+ end;
+end;
+
+function OnTTBLoaded(wParam:WPARAM;lParam:LPARAM):int; cdecl;
+var
+ i:integer;
+begin
+ result:=0;
+ for i:=HIGH(UActionList) downto 0 do
+ begin
+ if (UActionList[i].flags and UAF_REGTTBB)<>0 then
+ AddTTBButton(UActionList[i]);
+ end;
+end;
+
+//----- TabSRMM Toolbar -----
+
+const
+ TABTOOLBAR_INITPOS = 350;
+
+procedure AddTabBBButton(var ActionItem:tMyActionItem);
+var
+ tabb:BBButton;
+begin
+ if not NamesArray[uaTAB].enable then exit;
+
+ if (ActionItem.flags and UAF_TBREGGED)=0 then
+ begin
+ FillChar(tabb,SizeOf(tabb),0);
+ // register Tab ButtonBar button
+ tabb.cbSize :=SizeOf(tabb);
+ tabb.dwButtonID :=ActionItem.dwActID;
+ tabb.pszModuleName:=MODULE_NAME;
+ tabb.dwDefPos :=(TABTOOLBAR_INITPOS+ActionItem.wSortIndex*10) and $7FFF;
+ tabb.iButtonWidth :=0;
+ tabb.hIcon :=ActionItem.hIcolibIcon;
+ if (ActionItem.flags and UAF_2STATE)<>0 then
+ tabb.bbbFlags:=BBBF_ISIMBUTTON or BBBF_ISLSIDEBUTTON or
+ BBBF_ISCHATBUTTON or BBBF_ISPUSHBUTTON
+ else
+ tabb.bbbFlags:=BBBF_ISIMBUTTON or BBBF_ISLSIDEBUTTON or
+ BBBF_ISCHATBUTTON;
+
+ if ActionItem.szTabBTooltip<>nil then
+ tabb.szTooltip.w:=ActionItem.szTabBTooltip
+ else
+ tabb.szTooltip.w:=ActionItem.szActDescr;
+
+ if CallService(MS_BB_ADDBUTTON,0,LPARAM(@tabb))=0 then
+ ActionItem.flags:=ActionItem.flags or UAF_TBREGGED;
+ end;
+end;
+
+procedure DeleteTabBBButton(var ActionItem:tMyActionItem);
+var
+ tabb:BBButton;
+begin
+ if (ActionItem.flags and UAF_TBREGGED)<>0 then
+ begin
+ FillChar(tabb,SizeOf(tabb),0);
+ tabb.cbSize :=SizeOf(tabb);
+ tabb.dwButtonID :=ActionItem.dwActID;
+ tabb.pszModuleName:=MODULE_NAME;
+ CallService(MS_BB_REMOVEBUTTON,0,LPARAM(@tabb));
+ ActionItem.flags:=ActionItem.flags and not UAF_TBREGGED;
+ end;
+end;
+
+function OnTabButtonPressed(wParam:WPARAM;lParam:LPARAM):int; cdecl;
+var
+ cbcd:pCustomButtonClickData;
+// tabb:BBButton;
+// pc:pWideChar;
+ i:integer;
+begin
+ result:=0;
+ cbcd:=pointer(lParam);
+ if StrCmp(cbcd.pszModule,MODULE_NAME)<>0 then
+ exit;
+
+ for i:=0 to HIGH(UActionList) do
+ begin
+ with UActionList[i] do
+ begin
+ if cbcd.dwButtonId=dwActID then
+ begin
+{
+ FillChar(tabb,SizeOf(tabb),0);
+ tabb.cbSize :=SizeOf(tabb);
+ tabb.dwButtonID :=cbcd.dwButtonId;
+ tabb.pszModuleName:=MODULE_NAME;
+ if (flags and UAF_2STATE)<>0 then
+ begin
+ CallService(MS_BB_GETBUTTONSTATE,cbcd.hContact,TLPARAM(@tabb));
+ if IsLocalItem(UActionList[i]) then
+ begin
+ if DBReadByte(hContact,opt_ua,szNameID)<>0 then
+ end
+ else
+ begin
+ if (tabb.bbbFlags and BBSF_PUSHED)<>0 then
+ begin
+ pc:=szTabBTooltipPressed;
+ if pc=nil then pc:=szTabBTooltip;
+ tabb.hIcon:=hIcolibIconPressed;
+ end
+ else
+ begin
+ pc:=szTabBTooltip;
+ tabb.hIcon:=hIcolibIcon;
+ end;
+ if pc=nil then pc:=szActDescr;
+ tabb.szTooltip.w:=pc;
+ tabb.bbbFlags :=BBBF_ISIMBUTTON or BBBF_ISLSIDEBUTTON or
+ BBBF_ISCHATBUTTON or BBBF_ISPUSHBUTTON;
+ end
+ else
+ begin
+ tabb.hIcon:=hIcolibIcon;
+ tabb.szTooltip.w:=szTabBTooltip;
+ if tabb.szTooltip.w=nil then tabb.szTooltip.w:=szActDescr;
+ tabb.bbbFlags :=BBBF_ISIMBUTTON or BBBF_ISLSIDEBUTTON or
+ BBBF_ISCHATBUTTON;
+ end;
+
+ tabb.iButtonWidth:=0;
+ tabb.dwDefPos :=(TABTOOLBAR_INITPOS+wSortIndex*10) and $7FFF;
+ CallService(MS_BB_MODIFYBUTTON,0,TLPARAM(@tabb));
+}
+ ServiceCallWithFParam(cbcd.hContact,0,cbcd.dwButtonId);
+ result:=1;
+ break;
+ end;
+ end;
+ end;
+
+end;
+
+function OnTabBBLoaded(wParam:WPARAM;lParam:LPARAM):int; cdecl;
+var
+ i:integer;
+begin
+ result:=0;
+ for i:=HIGH(UActionList) downto 0 do
+ begin
+ if (UActionList[i].flags and UAF_REGTABB)<>0 then
+ AddTabBBButton(UActionList[i]);
+ end;
+end;
diff --git a/plugins/Actman/ua/i_uavars.inc b/plugins/Actman/ua/i_uavars.inc
new file mode 100644
index 0000000000..bab2ac12a1
--- /dev/null
+++ b/plugins/Actman/ua/i_uavars.inc
@@ -0,0 +1,124 @@
+{}
+const
+ MODULE_NAME = 'Actions';
+const
+ opt_groups:PAnsiChar = 'Group';
+ opt_ua :PAnsiChar = 'UA';
+ opt_id :PAnsiChar = 'id';
+ opt_flags :PAnsiChar = 'Flags';
+
+const
+ ICOLIB_ACTSECTION = MODULE_NAME+'/Registered actions';
+ ICOLIB_MNUSECTION = MODULE_NAME+'/Menu icons';
+
+ SERVICE_WITH_LPARAM_NAME = MODULE_NAME+'/CallAction';
+ TTB_SERVICE_NAME = MODULE_NAME+'/TTBAction';
+
+type
+ tMenuType = (main_menu,contact_menu,tray_menu,proto_menu,status_menu);
+ pUAMenuItem = ^tUAMenuItem;
+ tUAMenuItem = record
+ hMenuItem :THANDLE;
+ szMenuPopup :pWideChar;
+ szMenuNameVars :pWideChar;
+ szMenuShowWhenVars:pWideChar;
+ hMenuRoot :THANDLE;
+ menu_opt :dword;
+ changed :boolean;
+ end;
+type
+ pMyActionItem = ^tMyActionItem;
+ tMyActionItem = record
+ flags :dword;
+ dwActID :dword; // action ID
+ wSortIndex :word; // list/menu/toolbar order
+ // UseActions/Action_ID
+ szNameID :pAnsiChar; // uaction ID
+ szActDescr :pWideChar; // action name
+
+ hIcolibIcon,
+ hIcolibIconPressed :THANDLE;
+
+ hTTBButton :THANDLE; // TopToolbar button
+ szTTBTooltip :PAnsiChar;
+ szTTBTooltipPressed :PAnsiChar;
+ szTTBShowWhenVars :pWideChar;
+
+ szTabBTooltip :PWideChar; // TabSRMM toolbar button
+ szTabBTooltipPressed:PWideChar;
+
+ lastContact :THANDLE; // for contact menu
+ hMenuService :THANDLE; // common menu service
+ UAMenuItem :array [tMenuType] of tUAMenuItem;
+ end;
+
+const
+ UAF_NONE = 0;
+
+ UAF_REGHOTKEY = 1 shl 0; // hotkey
+ UAF_REGTTBB = 1 shl 1; // modern toolbar
+
+ UAF_REGTABB = 1 shl 5; // TabSRMM toolbar
+ UAF_USING = UAF_REGHOTKEY or UAF_REGTTBB or UAF_REGTABB;
+
+ UAF_2STATE = 1 shl 11; // Buttons/menu items are 2-state
+ UAF_PRESSED = 1 shl 12; // Button pressed/menu item selected
+ UAF_SAVESTATE = 1 shl 13; // Save or not "pressed" state
+ UAF_GLOBAL = 1 shl 14; // not contact related even if in contact menu only
+
+ // realtime, no save
+ UAF_HKREGGED = 1 shl 16; // hotkey registered
+ UAF_TBREGGED = 1 shl 17; // TabSRMM button registered
+ UAF_DISABLED = 1 shl 30; // action disabled atm
+ UAF_REALTIME = UAF_HKREGGED or UAF_TBREGGED or UAF_DISABLED;
+
+ UAF_SPECIAL = 1 shl 31; // for settings read
+
+ // menu options
+ UAF_MENUSEP = 1 shl 1; // menu item separated
+ UAF_MENUUSE = 1 shl 8; // use this menu
+
+type
+ tNameRec = record
+ name :PAnsiChar;
+ service:PAnsiChar;
+ mask :dword;
+ atype :word;
+ enable :boolean;
+ end;
+
+const
+ NumTypes = 8;
+const
+ uaTTB = 0;
+ uaTAB = 1;
+ uaHotkey = 2;
+ uaMenu = 3;
+
+const
+ NamesArray: array [0..NumTypes-1] of tNameRec = (
+ (name:'TopToolbar'; service:'TopToolBar/AddButton';
+ mask:UAF_REGTTBB ; atype:uaTTB; enable:false),
+ (name:'TabSRMM toolbar' ; service:'TabSRMM/ButtonsBar/AddButton';
+ mask:UAF_REGTABB ; atype:uaTAB; enable:false),
+ (name:'Core Hotkey' ; service:nil{MS_HOTKEY_REGISTER};
+ mask:UAF_REGHOTKEY; atype:uaHotkey; enable:false),
+ (name:'Main menu' ; service:nil;
+ mask:0; atype:uaMenu+(ORD(main_menu ) shl 8); enable:false),
+ (name:'Contact menu' ; service:nil;
+ mask:0; atype:uaMenu+(ORD(contact_menu) shl 8); enable:false),
+ (name:'Tray menu' ; service:'CList/AddTrayMenuItem';
+ mask:0; atype:uaMenu+(ORD(tray_menu ) shl 8); enable:false),
+ (name:'Protocol menus' ; service:'CList/AddProtoMenuItem';
+ mask:0; atype:uaMenu+(ORD(proto_menu ) shl 8); enable:false),
+ (name:'Status menu' ; service:'CList/AddStatusMenuItem';
+ mask:0; atype:uaMenu+(ORD(status_menu ) shl 8); enable:false)
+ );
+
+var
+ UActionList:array of tMyActionItem;
+var
+ szMyPath:array [0..MAX_PATH] of WideChar;
+var
+ hServiceWithLParam:THANDLE;
+ hTTBService:THANDLE;
diff --git a/plugins/Actman/ua/i_uconst.inc b/plugins/Actman/ua/i_uconst.inc
new file mode 100644
index 0000000000..34dde3ee9e
--- /dev/null
+++ b/plugins/Actman/ua/i_uconst.inc
@@ -0,0 +1,34 @@
+{resource constants}
+const
+ IDD_UA = 1031;
+
+ IDC_UA_ACTIONLIST = 1025;
+ IDC_UA_PLACELIST = 1026;
+
+ // menu settings
+ IDC_UA_SEPARATE = 1027;
+ IDC_UA_POPUPT = 1028;
+ IDC_UA_POPUPV = 1029;
+
+ IDC_UA_VARNAMEST = 1030;
+ IDC_UA_VARNAMESV = 1031;
+ IDC_UA_VARNAMESH = 1032;
+
+ IDC_UA_SHOWVART = 1033;
+ IDC_UA_SHOWVARV = 1034;
+ IDC_UA_SHOWVARH = 1035;
+
+ // toolbar settings
+ IDC_UA_TTNORMALT = 2028;
+ IDC_UA_TTNORMALV = 2029;
+ IDC_UA_TTPRESSEDT = 2030;
+ IDC_UA_TTPRESSEDV = 2031;
+
+ // common
+ IDC_UA_COMMON = 2000;
+ IDC_UA_TWOSTATE = 2001;
+ IDC_UA_SAVSTATE = 2002;
+
+ IDC_UA_GLOBAL = 2003;
+
+ IDI_ACTION = 101;
diff --git a/plugins/Actman/ua/ua.pas b/plugins/Actman/ua/ua.pas
new file mode 100644
index 0000000000..62b9604286
--- /dev/null
+++ b/plugins/Actman/ua/ua.pas
@@ -0,0 +1,124 @@
+unit ua;
+
+interface
+
+procedure Init;
+procedure DeInit;
+function AddOptionPage(var tmpl:pAnsiChar;var proc:pointer;var name:PAnsiChar):integer;
+
+implementation
+
+uses
+ windows, commctrl, messages,
+ mirutils, common, dbsettings, io, m_api, wrapper,
+ global;
+
+{$R ua.res}
+
+{$include m_actman.inc}
+
+{$include i_uconst.inc}
+{$include i_uavars.inc}
+
+// in - Action ID, out - action (group) number
+function GetUABranch(setting:pAnsiChar;id:cardinal):pAnsiChar;
+var
+ i:integer;
+ p,p1:pAnsiChar;
+begin
+ result:=nil;
+ p1:=StrCopyE(setting,opt_groups);
+ for i:=0 to CallService(MS_ACT_GETLIST,0,0)-1 do
+ begin
+ p:=StrEnd(IntToStr(p1,i));
+ p^:='/'; inc(p);
+ StrCopy(p,opt_id);
+ if DBReadDWord(0,DBBranch,setting)=id then
+ begin
+ p^:=#0;
+ result:=p;
+ break;
+ end;
+ end;
+end;
+
+var
+ amLink:tActionLink;
+
+{$include i_uaplaces.inc}
+{$include i_options.inc}
+{$include i_opt_dlg.inc}
+{$include i_ua.inc}
+{$include i_inoutxm.inc}
+
+// ------------ base interface functions -------------
+
+var
+ iohook:THANDLE;
+ hontabloaded,
+ honttbloaded,
+ ontabbtnpressed,
+ onactchanged:THANDLE;
+ hPreBuildMMenu,
+ hPreBuildCMenu,
+ hPreBuildTMenu:THANDLE;
+
+procedure Init;
+begin
+ GetModuleFileNameW(hInstance,szMyPath,MAX_PATH);
+
+ hServiceWithLParam:=CreateServiceFunction(SERVICE_WITH_LPARAM_NAME,@ServiceCallWithLParam);
+ hTTBService :=CreateServiceFunction(TTB_SERVICE_NAME ,@TTBServiceCall);
+ CheckPlacesAbility;
+
+ CreateUActionList;
+
+ honttbloaded :=HookEvent(ME_TTB_MODULELOADED ,@OnTTBLoaded);
+ hontabloaded :=HookEvent(ME_MSG_TOOLBARLOADED,@OnTabBBLoaded);
+ ontabbtnpressed:=HookEvent(ME_MSG_BUTTONPRESSED,@OnTabButtonPressed);
+ onactchanged :=HookEvent(ME_ACT_CHANGED ,@ActListChange);
+
+ hPreBuildMMenu:=HookEvent(ME_CLIST_PREBUILDMAINMENU , PreBuildMainMenu);
+ hPreBuildCMenu:=HookEvent(ME_CLIST_PREBUILDCONTACTMENU, PreBuildContactMenu);
+ hPreBuildTMenu:=HookEvent(ME_CLIST_PREBUILDTRAYMENU , PreBuildTrayMenu);
+
+ iohook:=HookEvent(ME_ACT_INOUT,@ActInOut);
+end;
+
+procedure DeInit;
+begin
+ SetLength(arMenuRec,0);
+
+ UnhookEvent(hPreBuildMMenu);
+ UnhookEvent(hPreBuildCMenu);
+ UnhookEvent(hPreBuildTMenu);
+
+ UnhookEvent(honttbloaded);
+ UnhookEvent(hontabloaded);
+ UnhookEvent(ontabbtnpressed);
+ UnhookEvent(onactchanged);
+ UnhookEvent(iohook);
+ DestroyServiceFunction(hServiceWithLParam);
+ DestroyServiceFunction(hTTBService);
+end;
+
+function AddOptionPage(var tmpl:pAnsiChar;var proc:pointer;var name:PAnsiChar):integer;
+begin
+ result:=0;
+ tmpl:=PAnsiChar(IDD_UA);
+ proc:=@DlgProcOpt;
+ name:='Use Actions';
+end;
+
+procedure InitLink;
+begin
+ amLink.Next :=ActionLink;
+ amLink.Init :=@Init;
+ amLink.DeInit :=@DeInit;
+ amLink.AddOption:=@AddOptionPage;
+ ActionLink :=@amLink;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Actman/ua/ua.rc b/plugins/Actman/ua/ua.rc
new file mode 100644
index 0000000000..76d8c0ad77
--- /dev/null
+++ b/plugins/Actman/ua/ua.rc
@@ -0,0 +1,51 @@
+#include "i_uconst.inc"
+
+LANGUAGE 0,0
+
+IDD_UA DIALOGEX 0, 0, 304, 226, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ CTEXT "Action list",-1, 2,2,132,10, SS_CENTERIMAGE
+ CONTROL "", IDC_UA_ACTIONLIST, "SysListView32",
+ WS_BORDER | WS_TABSTOP |
+ LVS_NOCOLUMNHEADER | LVS_SHOWSELALWAYS | LVS_REPORT | LVS_SINGLESEL,
+ 2, 12, 132, 212, WS_EX_CONTROLPARENT
+
+ CTEXT "Where to use",-1, 138,2,160,10, SS_CENTERIMAGE
+ CONTROL "", IDC_UA_PLACELIST, "SysListView32",
+ WS_BORDER | WS_TABSTOP |
+ LVS_NOCOLUMNHEADER | LVS_SHOWSELALWAYS | LVS_REPORT | LVS_SINGLESEL,
+ 138, 12, 160, 74, WS_EX_CONTROLPARENT
+
+ GROUPBOX "Common" , IDC_UA_COMMON , 138, 88,160,38
+ AUTOCHECKBOX "2 state button/item" , IDC_UA_TWOSTATE, 140, 96,156,14, BS_LEFTTEXT | BS_RIGHT
+ AUTOCHECKBOX "Save button/item state", IDC_UA_SAVSTATE, 140,110,156,14, BS_LEFTTEXT | BS_RIGHT
+
+ AUTOCHECKBOX "Contact related", IDC_UA_GLOBAL, 138,128,160,14, BS_LEFTTEXT | BS_RIGHT
+
+ // Buttons settings block
+ RTEXT "Normal button tooltip",IDC_UA_TTNORMALT, 138,148,160,8
+ EDITTEXT IDC_UA_TTNORMALV, 138,158,160,12, ES_AUTOHSCROLL
+
+ RTEXT "Pressed button tooltip",IDC_UA_TTPRESSEDT, 138,172,160,8
+ EDITTEXT IDC_UA_TTPRESSEDV, 138,182,160,12, ES_AUTOHSCROLL
+
+ // Menu settings block
+ AUTOCHECKBOX "Separated",IDC_UA_SEPARATE, 138,142,160,14, BS_LEFTTEXT | BS_RIGHT
+
+ RTEXT "Root popup:",IDC_UA_POPUPT,138,158,80,12, SS_CENTERIMAGE
+ EDITTEXT IDC_UA_POPUPV, 218,158,80,12, ES_AUTOHSCROLL
+
+ RTEXT "Menu item name:",IDC_UA_VARNAMEST, 138,172,140,8
+ EDITTEXT IDC_UA_VARNAMESV, 138,182,140,12, ES_AUTOHSCROLL
+ CONTROL "V",IDC_UA_VARNAMESH,"MButtonClass",WS_TABSTOP, 282,180,16,16, $18000000
+
+ RTEXT "Show only if variables return 1",IDC_UA_SHOWVART, 138,200,140,8
+ EDITTEXT IDC_UA_SHOWVARV,138,210,140,12,ES_AUTOHSCROLL
+ CONTROL "V",IDC_UA_SHOWVARH,"MButtonClass",WS_TABSTOP, 282,208,16,16, $18000000
+
+}
+
+IDI_ACTION ICON "action.ico"
diff --git a/plugins/Actman/ua/ua.res b/plugins/Actman/ua/ua.res
new file mode 100644
index 0000000000..8316b81f51
--- /dev/null
+++ b/plugins/Actman/ua/ua.res
Binary files differ
diff --git a/plugins/Libs/ActiveKOL.pas b/plugins/Libs/ActiveKOL.pas
new file mode 100644
index 0000000000..6f62f0c50d
--- /dev/null
+++ b/plugins/Libs/ActiveKOL.pas
@@ -0,0 +1,2649 @@
+{This version is compatible with KOL 3.00+ -- VK}
+
+unit ActiveKOL;
+
+interface
+
+uses
+ windows, messages, KOL, ActiveX, KOLComObj, err;
+
+{$I KOLDEF.INC}
+{$IFDEF _D6orHigher}
+ //{$WARN SYMBOL_DEPRECATED OFF}
+ {$WARN SYMBOL_PLATFORM OFF}
+ {$IFDEF _D7orHigher}
+ {$WARN UNSAFE_TYPE OFF}
+ {$WARN UNSAFE_CAST OFF}
+ {$WARN UNSAFE_CODE OFF}
+ {$ENDIF}
+{$ENDIF}
+
+{$IFNDEF _D5orHigher}
+const
+ sNoRunningObject = 'Unable to retrieve a pointer to a running object registered with OLE for %s/%s';
+{$ENDIF}
+
+type
+ POleCtl = ^TOleCtl;
+
+ TEventDispatch = class(TObject, IUnknown, IDispatch)
+ private
+ FControl: POleCtl;
+ protected
+ { IUnknown }
+ function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
+ function _AddRef: Integer; stdcall;
+ function _Release: Integer; stdcall;
+ { IDispatch }
+ function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
+ function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
+ function GetIDsOfNames(const IID: TGUID; Names: Pointer;
+ NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
+ function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
+ Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
+ property Control: POleCtl read FControl;
+ public
+ constructor Create(Control: POleCtl);
+ end;
+
+ {$IFNDEF _D5orHigher}
+ TOleEnum = type Integer;
+ //{$NODEFINE TOleEnum}
+ {$ENDIF}
+
+ TGetStrProc = procedure(const S: string) of object;
+
+ TEnumValue = record
+ Value: Longint;
+ Ident: string;
+ end;
+
+ PEnumValueList = ^TEnumValueList;
+ TEnumValueList = array[0..32767] of TEnumValue;
+
+ PEnumPropDesc = ^TEnumPropDesc;
+ TEnumPropDesc = object(TObj)
+ private
+ FDispID: Integer;
+ FValueCount: Integer;
+ FValues: PEnumValueList;
+ public
+ constructor Create(DispID, ValueCount: Integer;
+ const TypeInfo: ITypeInfo);
+ destructor Destroy; virtual;
+ procedure GetStrings(Proc: TGetStrProc);
+ function StringToValue(const S: string): Integer;
+ function ValueToString(V: Integer): string;
+ end;
+
+ PControlData = ^TControlData;
+ TControlData = record
+ ClassID: TGUID;
+ EventIID: TGUID;
+ EventCount: Longint;
+ EventDispIDs: Pointer;
+ LicenseKey: Pointer;
+ Flags: DWORD;
+ Version: Integer;
+ FontCount: Integer;
+ FontIDs: PDispIDList;
+ PictureCount: Integer;
+ PictureIDs: PDispIDList;
+ Reserved: Integer;
+ InstanceCount: Integer;
+ EnumPropDescs: PList;
+ end;
+
+ PControlData2 = ^TControlData2;
+ TControlData2 = record
+ ClassID: TGUID;
+ EventIID: TGUID;
+ EventCount: Longint;
+ EventDispIDs: Pointer;
+ LicenseKey: Pointer;
+ Flags: DWORD;
+ Version: Integer;
+ FontCount: Integer;
+ FontIDs: PDispIDList;
+ PictureCount: Integer;
+ PictureIDs: PDispIDList;
+ Reserved: Integer;
+ InstanceCount: Integer;
+ EnumPropDescs: PList;
+ FirstEventOfs: Cardinal;
+ end;
+
+ TOleCtlIntfClass = class of TOleCtlIntf;
+ TOleCtlIntf = class( TObject, IUnknown, IOleClientSite,
+ IOleControlSite, IOleInPlaceSite, IOleInPlaceFrame, IDispatch,
+ IPropertyNotifySink, ISimpleFrameSite)
+ private
+ FRefCount: Integer;
+ fOleCtl: POleCtl;
+ procedure GetEventMethod(DispID: TDispID; var Method: TMethod);
+ protected
+ { IUnknown }
+ function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; //override;
+ function _AddRef: Integer; stdcall;
+ function _Release: Integer; stdcall;
+ { IOleClientSite }
+ function SaveObject: HResult; stdcall;
+ function GetMoniker(dwAssign: Longint; dwWhichMoniker: Longint;
+ out mk: IMoniker): HResult; stdcall;
+ function GetContainer(out container: IOleContainer): HResult; stdcall;
+ function ShowObject: HResult; stdcall;
+ function OnShowWindow(fShow: BOOL): HResult; stdcall;
+ function RequestNewObjectLayout: HResult; stdcall;
+ { IOleControlSite }
+ function OnControlInfoChanged: HResult; stdcall;
+ function LockInPlaceActive(fLock: BOOL): HResult; stdcall;
+ function GetExtendedControl(out disp: IDispatch): HResult; stdcall;
+ function TransformCoords(var ptlHimetric: TPoint; var ptfContainer: TPointF;
+ flags: Longint): HResult; stdcall;
+ function IOleControlSite.TranslateAccelerator = OleControlSite_TranslateAccelerator;
+ function OleControlSite_TranslateAccelerator(msg: PMsg;
+ grfModifiers: Longint): HResult; stdcall;
+ function OnFocus(fGotFocus: BOOL): HResult; stdcall;
+ function ShowPropertyFrame: HResult; stdcall;
+ { IOleWindow }
+ function ContextSensitiveHelp(fEnterMode: BOOL): HResult; stdcall;
+ { IOleInPlaceSite }
+ function IOleInPlaceSite.GetWindow = OleInPlaceSite_GetWindow;
+ function OleInPlaceSite_GetWindow(out wnd: HWnd): HResult; stdcall;
+ function CanInPlaceActivate: HResult; stdcall;
+ function OnInPlaceActivate: HResult; stdcall;
+ function OnUIActivate: HResult; stdcall;
+ function GetWindowContext(out frame: IOleInPlaceFrame;
+ out doc: IOleInPlaceUIWindow; out rcPosRect: TRect;
+ out rcClipRect: TRect; out frameInfo: TOleInPlaceFrameInfo): HResult;
+ stdcall;
+ function Scroll(scrollExtent: TPoint): HResult; stdcall;
+ function OnUIDeactivate(fUndoable: BOOL): HResult; stdcall;
+ function OnInPlaceDeactivate: HResult; stdcall;
+ function DiscardUndoState: HResult; stdcall;
+ function DeactivateAndUndo: HResult; stdcall;
+ function OnPosRectChange(const rcPosRect: TRect): HResult; stdcall;
+ { IOleInPlaceUIWindow }
+ function GetBorder(out rectBorder: TRect): HResult; stdcall;
+ function RequestBorderSpace(const borderwidths: TRect): HResult; stdcall;
+ function SetBorderSpace(pborderwidths: PRect): HResult; stdcall;
+ function SetActiveObject(const activeObject: IOleInPlaceActiveObject;
+ pszObjName: POleStr): HResult; stdcall;
+ { IOleInPlaceFrame }
+ function IOleInPlaceFrame.GetWindow = OleInPlaceFrame_GetWindow;
+ function OleInPlaceFrame_GetWindow(out wnd: HWnd): HResult; stdcall;
+ function InsertMenus(hmenuShared: HMenu;
+ var menuWidths: TOleMenuGroupWidths): HResult; stdcall;
+ function SetMenu(hmenuShared: HMenu; holemenu: HMenu;
+ hwndActiveObject: HWnd): HResult; stdcall;
+ function RemoveMenus(hmenuShared: HMenu): HResult; stdcall;
+ function SetStatusText(pszStatusText: POleStr): HResult; stdcall;
+ function EnableModeless(fEnable: BOOL): HResult; stdcall;
+ function IOleInPlaceFrame.TranslateAccelerator = OleInPlaceFrame_TranslateAccelerator;
+ function OleInPlaceFrame_TranslateAccelerator(var msg: Windows.TMsg;
+ wID: Word): HResult; stdcall;
+ { IDispatch }
+ function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
+ function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
+ function GetIDsOfNames(const IID: TGUID; Names: Pointer;
+ NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
+ function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
+ Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
+ { ISimpleFrameSite }
+ function PreMessageFilter(wnd: HWnd; msg, wp, lp: Integer;
+ out res: Integer; out Cookie: Longint): HResult; stdcall;
+ function PostMessageFilter(wnd: HWnd; msg, wp, lp: Integer;
+ out res: Integer; Cookie: Longint): HResult; stdcall;
+ { IPropertyNotifySink }
+ function OnChanged(dispid: TDispID): HResult; virtual; stdcall;
+ function OnRequestEdit(dispid: TDispID): HResult; virtual; stdcall;
+ public
+ property OleCtl: POleCtl read fOleCtl;
+ constructor Create; virtual;
+ end;
+
+
+ TOnGetIntfClass = function(): TOleCtlIntfClass of object;
+
+
+ TOleCtl = object( TControl )
+ private
+ FOnGetIntfClass: TOnGetIntfClass;
+ function GetOleObject: Variant;
+ procedure CreateInstance;
+ function GetOnLeave: TOnEvent;
+ procedure SetOnLeave(const Value: TOnEvent);
+ procedure HookControlWndProc;
+ procedure SetUIActive(Active: Boolean);
+ procedure CreateControl;
+ procedure DestroyStorage;
+ procedure DestroyControl;
+ procedure StandardEvent(DispID: TDispID; var Params: TDispParams);
+ //procedure SetMouseDblClk(const Value: TOnMouse);
+ procedure SetOnChar(const Value: TOnChar);
+ protected
+ //{$IFDEF DELPHI_CODECOMPLETION_BUG}
+ fNotAvailable: Boolean;
+ //{$ENDIF}
+ {$IFNDEF USE_NAMES}
+ fName: String;
+ {$ENDIF}
+ FControlData: PControlData;
+ FOleObject: IOleObject;
+ FMiscStatus: Longint;
+ FFonts: PList;
+ FPictures: PList;
+ FEventDispatch: TEventDispatch;
+ fOleCtlIntf: TOleCtlIntf;
+ FPersistStream: IPersistStreamInit;
+ FOleInPlaceObject: IOleInPlaceObject;
+ FOleInPlaceActiveObject: IOleInPlaceActiveObject;
+ FOleControl: IOleControl;
+ FUpdatingColor: Boolean;
+ FUpdatingFont: Boolean;
+ FUpdatingEnabled: Boolean;
+ FObjectData: HGlobal;
+ FControlDispatch: IDispatch;
+ FPropBrowsing: IPerPropertyBrowsing;
+ FPropConnection: Longint;
+ FEventsConnection: Longint;
+ fCreatingWnd: Boolean;
+ procedure Init; virtual;
+ procedure InitControlData; virtual;
+ procedure InitControlInterface(const Obj: IUnknown); virtual;
+ property ControlData: PControlData read FControlData write FControlData;
+ function GetMainMenu: HMenu;
+ procedure InvokeEvent(DispID: TDispID; var Params: TDispParams);
+ procedure D2InvokeEvent(DispID: TDispID; var Params: TDispParams);
+ procedure DoHandleException;
+ procedure CreateEnumPropDescs;
+ procedure DestroyEnumPropDescs;
+
+ property OnGetIntfClass: TOnGetIntfClass read FOnGetIntfClass write FOnGetIntfClass;
+ public
+ function GetByteProp(Index: Integer): Byte;
+ function GetColorProp(Index: Integer): TColor;
+ function GetTColorProp(Index: Integer): TColor;
+ function GetCompProp(Index: Integer): Comp;
+ function GetCurrencyProp(Index: Integer): Currency;
+ function GetDoubleProp(Index: Integer): Double;
+ function GetIDispatchProp(Index: Integer): IDispatch;
+ function GetIntegerProp(Index: Integer): Integer;
+ function GetIUnknownProp(Index: Integer): IUnknown;
+ function GetWordBoolProp(Index: Integer): WordBool;
+ function GetTDateTimeProp(Index: Integer): TDateTime;
+ function GetTFontProp(Index: Integer): PGraphicTool;
+ function GetOleBoolProp(Index: Integer): TOleBool;
+ function GetOleDateProp(Index: Integer): TOleDate;
+ function GetOleEnumProp(Index: Integer): TOleEnum;
+ function GetTOleEnumProp(Index: Integer): TOleEnum;
+ function GetOleVariantProp(Index: Integer): OleVariant;
+ //function GetTPictureProp(Index: Integer): TPicture;
+ procedure GetProperty(Index: Integer; var Value: TVarData);
+ function GetShortIntProp(Index: Integer): ShortInt;
+ function GetSingleProp(Index: Integer): Single;
+ function GetSmallintProp(Index: Integer): Smallint;
+ function GetStringProp(Index: Integer): string;
+ function GetVariantProp(Index: Integer): Variant;
+ function GetWideStringProp(Index: Integer): WideString;
+ function GetWordProp(Index: Integer): Word;
+ procedure SetByteProp(Index: Integer; Value: Byte);
+ procedure SetColorProp(Index: Integer; Value: TColor);
+ procedure SetTColorProp(Index: Integer; Value: TColor);
+ procedure SetCompProp(Index: Integer; const Value: Comp);
+ procedure SetCurrencyProp(Index: Integer; const Value: Currency);
+ procedure SetDoubleProp(Index: Integer; const Value: Double);
+ procedure SetIDispatchProp(Index: Integer; const Value: IDispatch);
+ procedure SetIntegerProp(Index: Integer; Value: Integer);
+ procedure SetIUnknownProp(Index: Integer; const Value: IUnknown);
+ procedure SetName(const Value: String); virtual;
+ procedure SetWordBoolProp(Index: Integer; Value: WordBool);
+ procedure SetTDateTimeProp(Index: Integer; const Value: TDateTime);
+ procedure SetTFontProp(Index: Integer; Value:PGraphicTool);
+ procedure SetOleBoolProp(Index: Integer; Value: TOleBool);
+ procedure SetOleDateProp(Index: Integer; const Value: TOleDate);
+ procedure SetOleEnumProp(Index: Integer; Value: TOleEnum);
+ procedure SetTOleEnumProp(Index: Integer; Value: TOleEnum);
+ procedure SetOleVariantProp(Index: Integer; const Value: OleVariant);
+ procedure SetParent(AParent: PControl); virtual;
+ //procedure SetTPictureProp(Index: Integer; Value: TPicture);
+ procedure SetProperty(Index: Integer; const Value: TVarData);
+ procedure SetShortIntProp(Index: Integer; Value: Shortint);
+ procedure SetSingleProp(Index: Integer; const Value: Single);
+ procedure SetSmallintProp(Index: Integer; Value: Smallint);
+ procedure SetStringProp(Index: Integer; const Value: string);
+ procedure SetVariantProp(Index: Integer; const Value: Variant);
+ procedure SetWideStringProp(Index: Integer; const Value: WideString);
+ procedure SetWordProp(Index: Integer; Value: Word);
+
+ function GetEnumPropDesc(DispID: Integer): PEnumPropDesc;
+
+ property DragCursor: Boolean read fNotAvailable;
+ property DragMode : Boolean read fNotAvailable;
+ property ParentShowHint: Boolean read fNotAvailable;
+ property PopupMenu: Boolean read fNotAvailable;
+ property ShowHint: Boolean read fNotAvailable;
+ property OnDragDrop: Boolean read fNotAvailable;
+ property OnDragOver: Boolean read fNotAvailable;
+ property OnEndDrag: Boolean read fNotAvailable;
+ property OnStartDrag: Boolean read fNotAvailable;
+
+ property OnExit: TOnEvent read GetOnLeave write SetOnLeave;
+ property OleObject: Variant read GetOleObject;
+
+ property Name: String read fName write fName;
+ function CreateWindow: Boolean; virtual;
+ procedure DblClk;
+ procedure KeyDown(var Key: Longint; AShift: DWORD);
+ procedure KeyUp(var Key: Longint; AShift: DWORD);
+ procedure KeyPress(var Key: KOLChar);
+ procedure MouseDown(Button: TMouseButton; AShift: DWORD;
+ X, Y: Integer);
+ procedure MouseMove(AShift: DWORD; X, Y: Integer);
+ procedure MouseUp(Button: TMouseButton; AShift: DWORD;
+ X, Y: Integer);
+
+ property OnKeyPress: TOnChar
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnChar {$ELSE} EV.fOnChar {$ENDIF}
+ write SetOnChar;
+ property OnDblClick: TOnMouse index idx_fOnMouseDblClk
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnMouseEvent {$ELSE} EV.fOnMouseDblClk {$ENDIF}
+ write SetOnMouseEvent; // SetMouseDblClk;
+
+ destructor Destroy; virtual;
+
+ end;
+
+{$IFNDEF _D2orD3}
+type
+ TVariantArray = Array of OleVariant;
+ TOleServer = class;
+ TConnectKind = (ckRunningOrNew, // Attach to a running or create a new instance of the server
+ ckNewInstance, // Create a new instance of the server
+ ckRunningInstance, // Attach to a running instance of the server
+ ckRemote, // Bind to a remote instance of the server
+ ckAttachToInterface); // Don't bind to server, user will provide interface via 'CpnnectTo'
+
+ TServerEventDispatch = class(TObject, IUnknown, IDispatch)
+ private
+ FServer: TOleServer;
+ InternalRefCount : Integer;
+ protected
+ { IUnknown }
+ function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
+ function _AddRef: Integer; stdcall;
+ function _Release: Integer; stdcall;
+ { IDispatch }
+ function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
+ function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
+ function GetIDsOfNames(const IID: TGUID; Names: Pointer;
+ NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
+ function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
+ Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
+ property Server: TOleServer read FServer;
+ function ServerDisconnect :Boolean;
+ public
+ constructor Create(Server: TOleServer);
+ end;
+
+ PServerData = ^TServerData;
+ TServerData = record
+ ClassID: TGUID; // CLSID of CoClass
+ IntfIID: TGUID; // IID of default interface
+ EventIID: TGUID; // IID of default source interface
+ LicenseKey: Pointer; // Pointer to license string (not implemented)
+ Version: Integer; // Version of this structure
+ InstanceCount: Integer; // Instance of the Server running
+ end;
+
+ TOleServer = class(TObject, IUnknown)
+ private
+ FServerData: PServerData;
+ FRefCount: Longint;
+ FEventDispatch: TServerEventDispatch;
+ FEventsConnection: Longint;
+ FAutoConnect: Boolean;
+ FRemoteMachineName: string;
+ FConnectKind: TConnectKind;
+
+ protected
+ { IUnknown }
+ function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; //override;
+ function _AddRef: Integer; stdcall;
+ function _Release: Integer; stdcall;
+
+ procedure Loaded; //override;
+ procedure InitServerData; virtual; abstract;
+
+ function GetServer: IUnknown; virtual;
+
+ procedure ConnectEvents(const Obj: IUnknown);
+ procedure DisconnectEvents(const Obj: Iunknown);
+ procedure InvokeEvent(DispID: TDispID; var Params: TVariantArray); virtual;
+
+ function GetConnectKind: TConnectKind;
+ procedure SetConnectKind(ck: TConnectKind);
+
+ function GetAutoConnect: Boolean;
+ procedure SetAutoConnect(flag: Boolean);
+
+ property ServerData: PServerData read FServerData write FServerData;
+ property EventDispatch: TServerEventDispatch read FEventDispatch write FEventDispatch;
+
+ public
+ constructor Create; //(AOwner: TComponent); override;
+ destructor Destroy; override;
+
+ // NOTE: If derived class is generated by TLIBIMP or ImportTypeLibraryCodeGenerator,
+ // the derived class will also expose a 'ConnectTo(interface)' function.
+ // You must invoke that method if you're using 'ckAttachToInterface' connection
+ // kind.
+ procedure Connect; virtual; abstract;
+ procedure Disconnect; virtual; abstract;
+
+ published
+ property AutoConnect: Boolean read GetAutoConnect write SetAutoConnect;
+ property ConnectKind: TConnectKind read GetConnectKind write SetConnectKind;
+ property RemoteMachineName: string read FRemoteMachineName write FRemoteMachineName;
+ end;
+{$ENDIF}
+
+var
+ EmptyParam: OleVariant; { "Empty parameter" standard constant which can be
+ passed as an optional parameter on a dual interface. }
+
+
+implementation
+
+uses
+ OleConst;
+
+const
+ // The following flags may be or'd into the TControlData.Reserved field to override
+ // default behaviors.
+
+ // cdForceSetClientSite:
+ // Call SetClientSite early (in constructor) regardless of misc status flags
+ cdForceSetClientSite = 1;
+
+ // cdDeferSetClientSite:
+ // Don't call SetClientSite early. Takes precedence over cdForceSetClientSite and misc status flags
+ cdDeferSetClientSite = 2;
+
+const
+ cfBackColor = $00000001;
+ cfForeColor = $00000002;
+ cfFont = $00000004;
+ cfEnabled = $00000008;
+ cfCaption = $00000010;
+ cfText = $00000020;
+
+const
+ MaxDispArgs = 32;
+
+type
+
+ PDispInfo = ^TDispInfo;
+ TDispInfo = packed record
+ DispID: TDispID;
+ ResType: Byte;
+ CallDesc: TCallDesc;
+ end;
+
+ TArgKind = (akDWord, akSingle, akDouble);
+
+ PEventArg = ^TEventArg;
+ TEventArg = record
+ Kind: TArgKind;
+ Data: array[0..1] of Integer;
+ end;
+
+ TEventInfo = record
+ Method: TMethod;
+ Sender: TObject;
+ ArgCount: Integer;
+ Args: array[0..MaxDispArgs - 1] of TEventArg;
+ end;
+
+function StringToVarOleStr(const S: string): Variant;
+begin
+ VarClear(Result);
+ TVarData(Result).VOleStr := StringToOleStr(S);
+ TVarData(Result).VType := varOleStr;
+end;
+
+{ TEnumPropDesc }
+
+constructor TEnumPropDesc.Create(DispID, ValueCount: Integer;
+ const TypeInfo: ITypeInfo);
+var
+ I: Integer;
+ VarDesc: PVarDesc;
+ XName: WideString;
+begin
+ FDispID := DispID;
+ FValueCount := ValueCount;
+ FValues := AllocMem(ValueCount * SizeOf(TEnumValue));
+ for I := 0 to ValueCount - 1 do
+ begin
+ OleCheck(TypeInfo.GetVarDesc(I, VarDesc));
+ try
+ OleCheck(TypeInfo.GetDocumentation(VarDesc^.memid, @XName,
+ nil, nil, nil));
+ with FValues^[I] do
+ begin
+ Value := TVarData(VarDesc^.lpVarValue^).VInteger;
+ Ident := XName;
+ while (Length(Ident) > 1) and (Ident[1] = '_') do
+ Delete(Ident, 1, 1);
+ end;
+ finally
+ TypeInfo.ReleaseVarDesc(VarDesc);
+ end;
+ end;
+end;
+
+destructor TEnumPropDesc.Destroy;
+begin
+ if FValues <> nil then
+ begin
+ Finalize(FValues^[0], FValueCount);
+ FreeMem(FValues, FValueCount * SizeOf(TEnumValue));
+ end;
+ inherited;
+end;
+
+procedure TEnumPropDesc.GetStrings(Proc: TGetStrProc);
+var
+ I: Integer;
+begin
+ for I := 0 to FValueCount - 1 do
+ with FValues^[I] do Proc(Format('%d - %s', [Value, Ident]));
+end;
+
+function TEnumPropDesc.StringToValue(const S: string): Integer;
+var
+ I: Integer;
+begin
+ I := 1;
+ while (I <= Length(S)) and (S[I] in ['0'..'9', '-']) do Inc(I);
+ if I > 1 then
+ begin
+ Result := Str2Int(Copy(S, 1, I - 1));
+ for I := 0 to FValueCount - 1 do
+ if Result = FValues^[I].Value then Exit;
+ end else
+ for I := 0 to FValueCount - 1 do
+ with FValues^[I] do
+ if AnsiCompareText(S, Ident) = 0 then
+ begin
+ Result := Value;
+ Exit;
+ end;
+ raise EOleError.CreateResFmt(e_Ole, Integer( @SBadPropValue ), [S]);
+end;
+
+function TEnumPropDesc.ValueToString(V: Integer): string;
+var
+ I: Integer;
+begin
+ for I := 0 to FValueCount - 1 do
+ with FValues^[I] do
+ if V = Value then
+ begin
+ Result := Format('%d - %s', [Value, Ident]);
+ Exit;
+ end;
+ Result := Int2Str(V);
+end;
+
+{ TOleCtl }
+
+procedure TOleCtl.CreateControl;
+var
+ Stream: IStream;
+ CS: IOleClientSite;
+ X: Integer;
+begin
+ if FOleControl = nil then
+ try
+ try // work around ATL bug
+ X := FOleObject.GetClientSite(CS);
+ except
+ X := -1;
+ end;
+ if (X <> 0) or (CS = nil) then
+ OleCheck(FOleObject.SetClientSite(fOleCtlIntf));
+ if FObjectData = 0 then OleCheck(FPersistStream.InitNew) else
+ begin
+ OleCheck(CreateStreamOnHGlobal(FObjectData, False, Stream));
+ OleCheck(FPersistStream.Load(Stream));
+ DestroyStorage;
+ end;
+ OleCheck(FOleObject.QueryInterface(IOleControl, FOleControl));
+ OleCheck(FOleObject.QueryInterface(IDispatch, FControlDispatch));
+ FOleObject.QueryInterface(IPerPropertyBrowsing, FPropBrowsing);
+ InterfaceConnect(FOleObject, IPropertyNotifySink,
+ fOleCtlIntf, FPropConnection);
+ InterfaceConnect(FOleObject, FControlData^.EventIID,
+ FEventDispatch, FEventsConnection);
+ if FControlData^.Flags and cfBackColor <> 0 then
+ fOleCtlIntf.OnChanged(DISPID_BACKCOLOR);
+ if FControlData^.Flags and cfEnabled <> 0 then
+ fOleCtlIntf.OnChanged(DISPID_ENABLED);
+ if FControlData^.Flags and cfFont <> 0 then
+ fOleCtlIntf.OnChanged(DISPID_FONT);
+ if FControlData^.Flags and cfForeColor <> 0 then
+ fOleCtlIntf.OnChanged(DISPID_FORECOLOR);
+ FOleControl.OnAmbientPropertyChange(DISPID_UNKNOWN);
+ fOleCtlIntf.RequestNewObjectLayout;
+ except
+ DestroyControl;
+ raise;
+ end;
+end;
+
+procedure TOleCtl.CreateEnumPropDescs;
+
+ function FindMember(DispId: Integer): Boolean;
+ begin
+ Result := GetEnumPropDesc(DispId) <> nil;
+ end;
+ {var
+ I: Integer;
+ begin
+ for I := 0 to FControlData^.EnumPropDescs.Count - 1 do
+ if TEnumPropDesc(FControlData^.EnumPropDescs).FDispID = DispID then
+ begin
+ Result := True;
+ Exit;
+ end;
+ Result := False;
+ end;}
+
+ procedure CreateEnum(TypeDesc: TTypeDesc; const TypeInfo: ITypeInfo;
+ DispId: Integer);
+ var
+ RefInfo: ITypeInfo;
+ RefAttr: PTypeAttr;
+ epd: PEnumPropDesc;
+ begin
+ if TypeDesc.vt <> VT_USERDEFINED then Exit;
+ OleCheck(TypeInfo.GetRefTypeInfo(TypeDesc.hreftype, RefInfo));
+ OleCheck(RefInfo.GetTypeAttr(RefAttr));
+ try
+ if RefAttr^.typekind = TKIND_ENUM then
+ begin
+ new( epd, Create(Dispid, RefAttr^.cVars, RefInfo) );
+ FControlData^.EnumPropDescs.Add( epd );
+ end;
+ finally
+ RefInfo.ReleaseTypeAttr(RefAttr);
+ end;
+ end;
+
+ procedure ProcessTypeInfo(const TypeInfo: ITypeInfo);
+ var
+ I: Integer;
+ RefInfo: ITypeInfo;
+ TypeAttr: PTypeAttr;
+ VarDesc: PVarDesc;
+ FuncDesc: PFuncDesc;
+ RefType: HRefType;
+ begin
+ OleCheck(TypeInfo.GetTypeAttr(TypeAttr));
+ try
+ if IsEqualGUID(TypeAttr^.guid, IDispatch) then Exit;
+ if ((TypeAttr.typekind = TKIND_INTERFACE) or
+ (TypeAttr.wTypeFlags and TYPEFLAG_FDUAL <> 0)) and
+ (TypeAttr.wTypeFlags and TYPEFLAG_FNONEXTENSIBLE <> 0) then
+ begin
+ OleCheck(TypeInfo.GetRefTypeOfImplType(0, RefType));
+ OleCheck(TypeInfo.GetRefTypeInfo(RefType, RefInfo));
+ ProcessTypeInfo(RefInfo);
+ end;
+ for I := 0 to TypeAttr^.cVars - 1 do
+ begin
+ OleCheck(TypeInfo.GetVarDesc(I, VarDesc));
+ try
+ CreateEnum(VarDesc^.elemdescVar.tdesc, TypeInfo, VarDesc^.memid);
+ finally
+ TypeInfo.ReleaseVarDesc(VarDesc);
+ end;
+ end;
+ for I := 0 to TypeAttr^.cFuncs - 1 do
+ begin
+ OleCheck(TypeInfo.GetFuncDesc(I, FuncDesc));
+ try
+ if not FindMember(FuncDesc^.memid) then
+ case FuncDesc^.invkind of
+ INVOKE_PROPERTYGET:
+ CreateEnum(FuncDesc^.elemdescFunc.tdesc, TypeInfo, FuncDesc^.memid);
+ INVOKE_PROPERTYPUT:
+ CreateEnum(FuncDesc^.lprgelemdescParam[FuncDesc.cParams - 1].tdesc,
+ TypeInfo, FuncDesc^.memid);
+ INVOKE_PROPERTYPUTREF:
+ if FuncDesc^.lprgelemdescParam[FuncDesc.cParams - 1].tdesc.vt = VT_PTR then
+ CreateEnum(FuncDesc^.lprgelemdescParam[FuncDesc.cParams - 1].tdesc.ptdesc^,
+ TypeInfo, FuncDesc^.memid);
+ end;
+ finally
+ TypeInfo.ReleaseFuncDesc(FuncDesc);
+ end;
+ end;
+ finally
+ TypeInfo.ReleaseTypeAttr(TypeAttr);
+ end;
+ end;
+
+var
+ TypeInfo: ITypeInfo;
+begin
+ CreateControl;
+ FControlData^.EnumPropDescs := NewList;
+ try
+ OleCheck(FControlDispatch.GetTypeInfo(0, 0, TypeInfo));
+ ProcessTypeInfo(TypeInfo);
+ except
+ DestroyEnumPropDescs;
+ raise;
+ end;
+end;
+
+procedure TOleCtl.CreateInstance;
+var
+ ClassFactory2: IClassFactory2;
+ LicKeyStr: WideString;
+
+ procedure LicenseCheck(Status: HResult; const Ident: string);
+ begin
+ if Status = CLASS_E_NOTLICENSED then
+ raise EOleError.CreateFmt(e_Ole, Ident, [SubClassName]);
+ OleCheck(Status);
+ end;
+
+begin
+ if (FControlData^.LicenseKey <> nil) then
+ begin
+ OleCheck(CoGetClassObject(FControlData^.ClassID, CLSCTX_INPROC_SERVER or
+ CLSCTX_LOCAL_SERVER, nil, IClassFactory2, ClassFactory2));
+ LicKeyStr := PWideChar(FControlData^.LicenseKey);
+ LicenseCheck(ClassFactory2.CreateInstanceLic(nil, nil, IOleObject,
+ LicKeyStr, FOleObject), SInvalidLicense);
+ end else
+ LicenseCheck(CoCreateInstance(FControlData^.ClassID, nil,
+ CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER, IOleObject,
+ FOleObject), SNotLicensed);
+end;
+
+procedure CallEventMethod(const EventInfo: TEventInfo);
+asm
+ PUSH EBX
+ PUSH ESI
+ PUSH EBP
+ MOV EBP,ESP
+ MOV EBX,EAX
+ MOV EDX,[EBX].TEventInfo.ArgCount
+ TEST EDX,EDX
+ JE @@5
+ XOR EAX,EAX
+ LEA ESI,[EBX].TEventInfo.Args
+@@1: MOV AL,[ESI].TEventArg.Kind
+ CMP AL,1
+ JA @@2
+ JE @@3
+ TEST AH,AH
+ JNE @@3
+ MOV ECX,[ESI].Integer[4]
+ MOV AH,1
+ JMP @@4
+@@2: PUSH [ESI].Integer[8]
+@@3: PUSH [ESI].Integer[4]
+@@4: ADD ESI,12
+ DEC EDX
+ JNE @@1
+@@5: MOV EDX,[EBX].TEventInfo.Sender
+ MOV EAX,[EBX].TEventInfo.Method.Data
+ CALL [EBX].TEventInfo.Method.Code
+ MOV ESP,EBP
+ POP EBP
+ POP ESI
+ POP EBX
+end;
+
+type
+ PVarArg = ^TVarArg;
+ TVarArg = array[0..3] of DWORD;
+
+function TOleCtl.CreateWindow: Boolean;
+begin
+ Result := FALSE;
+ if fHandle <> 0 then
+ begin
+ Result := TRUE;
+ Exit;
+ end;
+ if fCreatingWnd then
+ Exit;
+ fCreatingWnd := TRUE;
+ try
+ CreateControl;
+ if FMiscStatus and OLEMISC_INVISIBLEATRUNTIME = 0 then
+ begin
+ FOleObject.DoVerb(OLEIVERB_INPLACEACTIVATE, nil, fOleCtlIntf, 0,
+ ParentWindow, BoundsRect);
+ if FOleInPlaceObject = nil then
+ raise EOleError.CreateResFmt(e_Ole, Integer( @SCannotActivate ), [nil]);
+ HookControlWndProc;
+ if {$IFDEF USE_FLAGS} not(F3_Visible in fStyle.f3_Style)
+ {$ELSE} not fVisible {$ENDIF}
+ and IsWindowVisible(fHandle) then
+ ShowWindow(fHandle, SW_HIDE);
+ Result := TRUE;
+ end
+ else
+ Result := inherited CreateWindow;
+ finally
+ fCreatingWnd := FALSE;
+ end;
+end;
+
+procedure TOleCtl.D2InvokeEvent(DispID: TDispID; var Params: TDispParams);
+type
+ TStringDesc = record
+ PStr: Pointer;
+ BStr: PBStr;
+ end;
+var
+ I, J, K, ArgType, ArgCount, StrCount: Integer;
+ ArgPtr: PEventArg;
+ ParamPtr: PVarArg;
+ Strings: array[0..MaxDispArgs - 1] of TStringDesc;
+ EventInfo: TEventInfo;
+begin
+ fOleCtlIntf.GetEventMethod(DispID, EventInfo.Method);
+ if Integer(EventInfo.Method.Code) >= $10000 then
+ begin
+ StrCount := 0;
+ try
+ ArgCount := Params.cArgs;
+ EventInfo.Sender := fOleCtlIntf;
+ EventInfo.ArgCount := ArgCount;
+ if ArgCount <> 0 then
+ begin
+ ParamPtr := @Params.rgvarg^[EventInfo.ArgCount];
+ ArgPtr := @EventInfo.Args;
+ I := 0;
+ repeat
+ Dec(Integer(ParamPtr), SizeOf(TVarArg));
+ ArgType := ParamPtr^[0] and $0000FFFF;
+ if ArgType and varTypeMask = varOleStr then
+ begin
+ ArgPtr^.Kind := akDWord;
+ with Strings[StrCount] do
+ begin
+ PStr := nil;
+ if ArgType and varByRef <> 0 then
+ begin
+ OleStrToStrVar(PBStr(ParamPtr^[2])^, string(PStr));
+ BStr := PBStr(ParamPtr^[2]);
+ ArgPtr^.Data[0] := Integer(@PStr);
+ end else
+ begin
+ OleStrToStrVar(TBStr(ParamPtr^[2]), string(PStr));
+ BStr := nil;
+ ArgPtr^.Data[0] := Integer(PStr);
+ end;
+ end;
+ Inc(StrCount);
+ end else
+ begin
+ case ArgType of
+ varSingle:
+ begin
+ ArgPtr^.Kind := akSingle;
+ ArgPtr^.Data[0] := ParamPtr^[2];
+ end;
+ varDouble..varDate:
+ begin
+ ArgPtr^.Kind := akDouble;
+ ArgPtr^.Data[0] := ParamPtr^[2];
+ ArgPtr^.Data[1] := ParamPtr^[3];
+ end;
+ varDispatch:
+ begin
+ ArgPtr^.Kind := akDWord;
+ ArgPtr^.Data[0] := Integer(ParamPtr)
+ end;
+ else
+ ArgPtr^.Kind := akDWord;
+ if (ArgType and varArray) <> 0 then
+ ArgPtr^.Data[0] := Integer(ParamPtr)
+ else
+ ArgPtr^.Data[0] := ParamPtr^[2];
+ end;
+ end;
+ Inc(Integer(ArgPtr), SizeOf(TEventArg));
+ Inc(I);
+ until I = EventInfo.ArgCount;
+ end;
+ CallEventMethod(EventInfo);
+ J := StrCount;
+ while J <> 0 do
+ begin
+ Dec(J);
+ with Strings[J] do
+ if BStr <> nil then BStr^ := StringToOleStr(string(PStr));
+ end;
+ except
+ DoHandleException;
+ end;
+ K := StrCount;
+ while K <> 0 do
+ begin
+ Dec(K);
+ string(Strings[K].PStr) := '';
+ end;
+ end;
+end;
+
+procedure TOleCtl.DblClk;
+var MouseData: TMouseEventData;
+ P: TPoint;
+begin
+ {$IFDEF NIL_EVENTS}
+ if Assigned(EV.fOnMouseDblClk) then
+ {$ENDIF}
+ begin
+ MouseData.Button := mbLeft;
+ MouseData.Shift := 0;
+ GetCursorPos( P );
+ P := Screen2Client( P );
+ MouseData.X := P.x;
+ MouseData.Y := P.y;
+ EV.fOnMouseDblClk(@Self, MouseData);
+ end;
+end;
+
+destructor TOleCtl.Destroy;
+
+ procedure FreeList(var L: PList);
+ begin
+ if L = nil then Exit;
+ L.Release;
+ L := nil;
+ end;
+
+begin
+ SetUIActive(False);
+ if FOleObject <> nil then FOleObject.Close(OLECLOSE_NOSAVE);
+ DestroyControl;
+ DestroyStorage;
+ FPersistStream := nil;
+ if FOleObject <> nil then FOleObject.SetClientSite(nil);
+ FOleObject := nil;
+ FEventDispatch.Free;
+ FreeList(FFonts);
+ FreeList(FPictures);
+ Dec(FControlData^.InstanceCount);
+ if FControlData^.InstanceCount = 0 then
+ DestroyEnumPropDescs;
+ fOleCtlIntf.Free;
+ inherited Destroy;
+end;
+
+procedure TOleCtl.DestroyControl;
+begin
+ InterfaceDisconnect(FOleObject, FControlData^.EventIID, FEventsConnection);
+ InterfaceDisconnect(FOleObject, IPropertyNotifySink, FPropConnection);
+ FPropBrowsing := nil;
+ FControlDispatch := nil;
+ FOleControl := nil;
+end;
+
+procedure TOleCtl.DestroyEnumPropDescs;
+var
+ I: Integer;
+begin
+ with FControlData^ do
+ if EnumPropDescs <> nil then
+ begin
+ for I := 0 to EnumPropDescs.Count - 1 do
+ PEnumPropDesc(EnumPropDescs.Items[I]).Free;
+ EnumPropDescs.Free;
+ EnumPropDescs := nil;
+ end;
+end;
+
+procedure TOleCtl.DestroyStorage;
+begin
+ if FObjectData <> 0 then
+ begin
+ GlobalFree(FObjectData);
+ FObjectData := 0;
+ end;
+end;
+
+procedure TOleCtl.DoHandleException;
+begin
+ //Application.HandleException(Self);
+ //TODO: replace Application.HandleException with something
+end;
+
+function TOleCtl.GetByteProp(Index: Integer): Byte;
+begin
+ Result := GetIntegerProp(Index);
+end;
+
+function TOleCtl.GetColorProp(Index: Integer): TColor;
+begin
+ Result := GetIntegerProp(Index);
+end;
+
+function TOleCtl.GetCompProp(Index: Integer): Comp;
+begin
+ Result := GetDoubleProp(Index);
+end;
+
+function TOleCtl.GetCurrencyProp(Index: Integer): Currency;
+var
+ Temp: TVarData;
+begin
+ GetProperty(Index, Temp);
+ Result := Temp.VCurrency;
+end;
+
+function TOleCtl.GetDoubleProp(Index: Integer): Double;
+var
+ Temp: TVarData;
+begin
+ GetProperty(Index, Temp);
+ Result := Temp.VDouble;
+end;
+
+procedure TOleCtlIntf.GetEventMethod(DispID: TDispID; var Method: TMethod);
+{begin // test for D4 - it works...
+ Method.Code := nil;
+ Method.Data := nil;
+end;}
+const
+ szOleCtl = sizeof( TOleCtl );
+asm
+ PUSH EBX
+ PUSH ESI
+ PUSH EDI
+ PUSH ECX
+ MOV EBX,EAX
+ MOV ECX,[EBX].fOleCtl
+ ///////////////////////// fix of events handling
+ MOV EBX, ECX // by Alexey Izyumov
+ ///////////////////////// Octouber, 2001
+ MOV ECX,[ECX].TOleCtl.FControlData
+ MOV EDI,[ECX].TControlData.EventCount
+ MOV ESI,[ECX].TControlData.EventDispIDs
+ XOR EAX,EAX
+ JMP @@1
+@@0: CMP EDX,[ESI].Integer[EAX*4]
+ JE @@2
+ INC EAX
+@@1: CMP EAX,EDI
+ JNE @@0
+ XOR EAX,EAX
+ XOR EDX,EDX
+ JMP @@3
+@@2: PUSH EAX
+ CMP [ECX].TControlData.Version, 401
+ JB @@2a
+ MOV EAX, [ECX].TControlData2.FirstEventOfs
+ TEST EAX, EAX
+ JNE @@2b
+@@2a: {MOV EAX, [EBX]
+ CALL TObject.ClassParent
+ CALL TObject.InstanceSize}
+ MOV EAX, szOleCtl
+ ADD EAX, 7
+ AND EAX, not 7 // 8 byte alignment
+@@2b: ADD EBX, EAX
+ POP EAX
+ MOV EDX,[EBX][EAX*8].TMethod.Data
+ MOV EAX,[EBX][EAX*8].TMethod.Code
+@@3: POP ECX
+ MOV [ECX].TMethod.Code,EAX
+ MOV [ECX].TMethod.Data,EDX
+ POP EDI
+ POP ESI
+ POP EBX
+end;
+
+function TOleCtl.GetEnumPropDesc(DispID: Integer): PEnumPropDesc;
+var
+ I: Integer;
+begin
+ with FControlData^ do
+ begin
+ if EnumPropDescs = nil then CreateEnumPropDescs;
+ for I := 0 to EnumPropDescs.Count - 1 do
+ begin
+ Result := EnumPropDescs.Items[I];
+ if Result.FDispID = DispID then Exit;
+ end;
+ Result := nil;
+ end;
+end;
+
+function TOleCtl.GetIDispatchProp(Index: Integer): IDispatch;
+var
+ Temp: TVarData;
+begin
+ GetProperty(Index, Temp);
+ Result := IDispatch(Temp.VDispatch);
+end;
+
+function TOleCtl.GetIntegerProp(Index: Integer): Integer;
+var
+ Temp: TVarData;
+begin
+ GetProperty(Index, Temp);
+ Result := Temp.VInteger;
+end;
+
+function TOleCtl.GetIUnknownProp(Index: Integer): IUnknown;
+var
+ Temp: TVarData;
+begin
+ GetProperty(Index, Temp);
+ Result := IUnknown(Temp.VUnknown);
+end;
+
+function TOleCtl.GetMainMenu: HMenu;
+var
+ Form: PControl;
+begin
+ Result := 0;
+ Form := ParentForm;
+ if Form <> nil then
+ //if Form.FormStyle <> fsMDIChild then
+ Result := Form.Menu
+ {else
+ if Application.MainForm <> nil then
+ Result := Application.MainForm.Menu};
+end;
+
+function TOleCtl.GetOleBoolProp(Index: Integer): TOleBool;
+var
+ Temp: TVarData;
+begin
+ GetProperty(Index, Temp);
+ Result := Temp.VBoolean;
+end;
+
+function TOleCtl.GetOleDateProp(Index: Integer): TOleDate;
+var
+ Temp: TVarData;
+begin
+ GetProperty(Index, Temp);
+ Result := Temp.VDate;
+end;
+
+function TOleCtl.GetOleEnumProp(Index: Integer): TOleEnum;
+begin
+ Result := GetIntegerProp(Index);
+end;
+
+function TOleCtl.GetOleObject: Variant;
+begin
+ CreateControl;
+ Result := Variant(FOleObject as IDispatch);
+end;
+
+function TOleCtl.GetOleVariantProp(Index: Integer): OleVariant;
+begin
+ VarClear(Result);
+ GetProperty(Index, TVarData(Result));
+end;
+
+function TOleCtl.GetOnLeave: TOnEvent;
+begin
+ Result := OnExit;
+end;
+
+var // init to zero, never written to
+ DispParams: TDispParams = ();
+
+procedure TOleCtl.GetProperty(Index: Integer; var Value: TVarData);
+var
+ Status: HResult;
+ ExcepInfo: TExcepInfo;
+begin
+ CreateControl;
+ Value.VType := varEmpty;
+ Status := FControlDispatch.Invoke(Index, GUID_NULL, 0,
+ DISPATCH_PROPERTYGET, DispParams, @Value, @ExcepInfo, nil);
+ if Status <> 0 then DispatchInvokeError(Status, ExcepInfo);
+end;
+
+function TOleCtl.GetShortIntProp(Index: Integer): ShortInt;
+begin
+ Result := GetIntegerProp(Index);
+end;
+
+function TOleCtl.GetSingleProp(Index: Integer): Single;
+var
+ Temp: TVarData;
+begin
+ GetProperty(Index, Temp);
+ Result := Temp.VSingle;
+end;
+
+function TOleCtl.GetSmallintProp(Index: Integer): Smallint;
+var
+ Temp: TVarData;
+begin
+ GetProperty(Index, Temp);
+ Result := Temp.VSmallint;
+end;
+
+function TOleCtl.GetStringProp(Index: Integer): string;
+begin
+ Result := GetVariantProp(Index);
+end;
+
+function TOleCtl.GetTColorProp(Index: Integer): TColor;
+begin
+ Result := GetIntegerProp(Index);
+end;
+
+function TOleCtl.GetTDateTimeProp(Index: Integer): TDateTime;
+var
+ Temp: TVarData;
+begin
+ GetProperty(Index, Temp);
+ Result := Temp.VDate;
+end;
+
+function TOleCtl.GetTFontProp(Index: Integer): PGraphicTool;
+{var
+ I: Integer;}
+begin
+ Result := nil;
+ {for I := 0 to FFonts.Count-1 do
+ if FControlData^.FontIDs^[I] = Index then
+ begin
+ Result := TFont(FFonts[I]);
+ if Result.FontAdapter = nil then
+ SetOleFont(Result, GetIDispatchProp(Index) as IFontDisp);
+ end;}
+ //TODO: implement TFont later
+end;
+
+function TOleCtl.GetTOleEnumProp(Index: Integer): TOleEnum;
+begin
+ Result := GetIntegerProp(Index);
+end;
+
+function TOleCtl.GetVariantProp(Index: Integer): Variant;
+begin
+ Result := GetOleVariantProp(Index);
+end;
+
+function TOleCtl.GetWideStringProp(Index: Integer): WideString;
+var
+ Temp: TVarData;
+begin
+ Result := '';
+ GetProperty(Index, Temp);
+ Pointer(Result) := Temp.VOleStr;
+end;
+
+function TOleCtl.GetWordBoolProp(Index: Integer): WordBool;
+var
+ Temp: TVarData;
+begin
+ GetProperty(Index, Temp);
+ Result := Temp.VBoolean;
+end;
+
+function TOleCtl.GetWordProp(Index: Integer): Word;
+begin
+ Result := GetIntegerProp(Index);
+end;
+
+procedure TOleCtl.HookControlWndProc;
+var
+ WndHandle: HWnd;
+begin
+ if (FOleInPlaceObject <> nil) and (fHandle = 0) then
+ begin
+ WndHandle := 0;
+ FOleInPlaceObject.GetWindow(WndHandle);
+ if WndHandle = 0 then
+ raise EOleError.CreateResFmt(e_Ole, Integer(@SNoWindowHandle), [nil]);
+ fHandle := WndHandle;
+ fDefWndProc := Pointer(GetWindowLong(fHandle, GWL_WNDPROC));
+ CreatingWindow := @Self;
+ SetWindowLong(fHandle, GWL_WNDPROC, Longint(@WndFunc));
+ SendMessage(fHandle, WM_NULL, 0, 0);
+ end;
+end;
+
+procedure TOleCtl.Init;
+var
+ I: Integer;
+ intfClass: TOleCtlIntfClass;
+begin
+ OleInit;
+ inherited;
+ // overriding this method, we allow for constructor to initialize
+ // the object.
+ fControlClassName := 'OleCtl'; // ClassName
+ {$IFDEF USE_FLAGS} include( fFlagsG3, G3_IsControl );
+ {$ELSE} fIsControl := TRUE; {$ENDIF}
+ fStyle.Value := WS_VISIBLE or WS_CLIPCHILDREN or WS_CLIPSIBLINGS or
+ WS_CHILD; // or WS_BORDER or WS_THICKFRAME;
+
+ //AttachProc( WndProcCtrl ); for test only
+
+ // The rest of initialization -- moved from OleCtrls
+ InitControlData;
+ Inc(FControlData^.InstanceCount);
+ if FControlData^.FontCount > 0 then
+ begin
+ FFonts := NewList;
+ //FFonts.Count := FControlData^.FontCount;
+ for I := 0 to FControlData^.FontCount-1 do
+ FFonts.Add( NewFont );
+ end;
+ {if FControlData^.PictureCount > 0 then
+ begin
+ FPictures := NewList;
+ //FPictures.Count := FControlData^.PictureCount;
+ for I := 0 to FControlData^.PictureCount-1 do
+ begin
+ FPictures.Add( NewPicture );
+ TPicture(FPictures[I]).OnChange := PictureChanged;
+ end;
+ end;}
+ FEventDispatch := TEventDispatch.Create(@Self);
+ CreateInstance;
+ InitControlInterface(FOleObject);
+ OleCheck(FOleObject.GetMiscStatus(DVASPECT_CONTENT, FMiscStatus));
+
+ if (Assigned(OnGetIntfClass)) then
+ intfClass := OnGetIntfClass()
+ else
+ intfClass := TOleCtlIntf;
+ fOleCtlIntf := intfClass.Create;
+ fOleCtlIntf.fOleCtl := @Self;
+
+ if (FControlData^.Reserved and cdDeferSetClientSite) = 0 then
+ if ((FMiscStatus and OLEMISC_SETCLIENTSITEFIRST) <> 0) or
+ ((FControlData^.Reserved and cdForceSetClientSite) <> 0) then
+ OleCheck(FOleObject.SetClientSite(fOleCtlIntf));
+ OleCheck(FOleObject.QueryInterface(IPersistStreamInit, FPersistStream));
+ if FMiscStatus and OLEMISC_INVISIBLEATRUNTIME <> 0 then
+ {$IFDEF USE_FLAGS} exclude( fStyle.f3_Style, F3_Visible );
+ {$ELSE} fVisible := False; {$ENDIF}
+ {if FMiscStatus and OLEMISC_SIMPLEFRAME <> 0 then
+ ControlStyle := [csAcceptsControls, csDoubleClicks, csNoStdEvents] else
+ ControlStyle := [csDoubleClicks, csNoStdEvents];}
+ if FMiscStatus and OLEMISC_SIMPLEFRAME = 0 then
+ fExStyle := 0; // clear WS_EX_CONTROLPARENT
+ TabStop := FMiscStatus and (OLEMISC_ACTSLIKELABEL or
+ OLEMISC_NOUIACTIVATE) = 0;
+ OleCheck(fOleCtlIntf.RequestNewObjectLayout);
+end;
+
+procedure TOleCtl.InitControlData;
+begin
+ // nothing here. Originally, this method was abstract.
+ // Since TOleControl class became TOleCtl object, abstract methods
+ // are not available. So, make this method empty to override it
+ // in descendant objects, which represent Active-X controls.
+end;
+
+procedure TOleCtl.InitControlInterface(const Obj: IUnknown);
+begin
+ // This method is to override it in derived Active-X control holder.
+end;
+
+procedure TOleCtl.InvokeEvent(DispID: TDispID; var Params: TDispParams);
+var
+ EventMethod: TMethod;
+begin
+ if ControlData.Version < 300 then
+ D2InvokeEvent(DispID, Params)
+ else
+ begin
+ fOleCtlIntf.GetEventMethod(DispID, EventMethod);
+ if Integer(EventMethod.Code) < $10000 then Exit;
+
+ try
+ asm
+ PUSH EBX
+ PUSH ESI
+ MOV ESI, Params
+ MOV EBX, [ESI].TDispParams.cArgs
+ TEST EBX, EBX
+ JZ @@7
+ MOV ESI, [ESI].TDispParams.rgvarg
+ MOV EAX, EBX
+ SHL EAX, 4 // count * sizeof(TVarArg)
+ XOR EDX, EDX
+ ADD ESI, EAX // EDI = Params.rgvarg^[ArgCount]
+ @@1: SUB ESI, 16 // Sizeof(TVarArg)
+ MOV EAX, dword ptr [ESI]
+ CMP AX, varSingle
+ JA @@3
+ JE @@4
+ @@2: TEST DL,DL
+ JNE @@2a
+ MOV ECX, ESI
+ INC DL
+ TEST EAX, varArray
+ JNZ @@6
+ MOV ECX, dword ptr [ESI+8]
+ JMP @@6
+ @@2a: TEST EAX, varArray
+ JZ @@5
+ PUSH ESI
+ JMP @@6
+ @@3: CMP AX, varDate
+ JA @@2
+ @@4: PUSH dword ptr [ESI+12]
+ @@5: PUSH dword ptr [ESI+8]
+ @@6: DEC EBX
+ JNE @@1
+ @@7: MOV EDX, Self
+ MOV EAX, EventMethod.Data
+ CALL EventMethod.Code
+ POP ESI
+ POP EBX
+ end;
+ except
+ DoHandleException;
+ end;
+ end;
+end;
+
+procedure TOleCtl.KeyDown(var Key: Longint; AShift: DWORD);
+begin
+ if Assigned(EV.fOnKeyDown) then EV.fOnKeyDown(@Self, Key, AShift);
+end;
+
+procedure TOleCtl.KeyPress(var Key: KOLChar);
+begin
+ if Assigned(EV.fOnChar) then EV.fOnChar(@Self, Key, 0);
+end;
+
+procedure TOleCtl.KeyUp(var Key: Longint; AShift: DWORD);
+begin
+ if Assigned(EV.fOnKeyUp) then EV.fOnKeyUp(@Self, Key, AShift);
+end;
+
+procedure TOleCtl.MouseDown(Button: TMouseButton; AShift: DWORD; X,
+ Y: Integer);
+begin
+ //TODO: mouse
+end;
+
+procedure TOleCtl.MouseMove(AShift: DWORD; X, Y: Integer);
+begin
+ //TODO: mouse
+end;
+
+procedure TOleCtl.MouseUp(Button: TMouseButton; AShift: DWORD; X,
+ Y: Integer);
+begin
+ //TODO: mouse
+end;
+
+procedure TOleCtl.SetByteProp(Index: Integer; Value: Byte);
+begin
+ SetIntegerProp(Index, Value);
+end;
+
+procedure TOleCtl.SetColorProp(Index: Integer; Value: TColor);
+begin
+ SetIntegerProp(Index, Value);
+end;
+
+procedure TOleCtl.SetCompProp(Index: Integer; const Value: Comp);
+var
+ Temp: TVarData;
+begin
+ Temp.VType := VT_I8;
+ Temp.VDouble := Value;
+ SetProperty(Index, Temp);
+end;
+
+procedure TOleCtl.SetCurrencyProp(Index: Integer; const Value: Currency);
+var
+ Temp: TVarData;
+begin
+ Temp.VType := varCurrency;
+ Temp.VCurrency := Value;
+ SetProperty(Index, Temp);
+end;
+
+procedure TOleCtl.SetDoubleProp(Index: Integer; const Value: Double);
+var
+ Temp: TVarData;
+begin
+ Temp.VType := varDouble;
+ Temp.VDouble := Value;
+ SetProperty(Index, Temp);
+end;
+
+procedure TOleCtl.SetIDispatchProp(Index: Integer; const Value: IDispatch);
+var
+ Temp: TVarData;
+begin
+ Temp.VType := varDispatch;
+ Temp.VDispatch := Pointer(Value);
+ SetProperty(Index, Temp);
+end;
+
+procedure TOleCtl.SetIntegerProp(Index, Value: Integer);
+var
+ Temp: TVarData;
+begin
+ Temp.VType := varInteger;
+ Temp.VInteger := Value;
+ SetProperty(Index, Temp);
+end;
+
+procedure TOleCtl.SetIUnknownProp(Index: Integer; const Value: IUnknown);
+var
+ Temp: TVarData;
+begin
+ Temp.VType := VT_UNKNOWN;
+ Temp.VUnknown := Pointer(Value);
+ SetProperty(Index, Temp);
+end;
+
+(*procedure TOleCtl.SetMouseDblClk(const Value: TOnMouse);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnMouseDblClk := Value;
+end;*)
+
+procedure TOleCtl.SetName(const Value: String);
+var
+ OldName: string;
+ DispID: Integer;
+begin
+ OldName := Name;
+ Name := Value; //inherited SetName(Value);
+ if FOleControl <> nil then
+ begin
+ FOleControl.OnAmbientPropertyChange(DISPID_AMBIENT_DISPLAYNAME);
+ if FControlData^.Flags and (cfCaption or cfText) <> 0 then
+ begin
+ if FControlData^.Flags and cfCaption <> 0 then
+ DispID := DISPID_CAPTION else
+ DispID := DISPID_TEXT;
+ if OldName = GetStringProp(DispID) then SetStringProp(DispID, Value);
+ end;
+ end;
+end;
+
+procedure TOleCtl.SetOleBoolProp(Index: Integer; Value: TOleBool);
+var
+ Temp: TVarData;
+begin
+ Temp.VType := varBoolean;
+ if Value then
+ Temp.VBoolean := WordBool(-1) else
+ Temp.VBoolean := WordBool(0);
+ SetProperty(Index, Temp);
+end;
+
+procedure TOleCtl.SetOleDateProp(Index: Integer; const Value: TOleDate);
+var
+ Temp: TVarData;
+begin
+ Temp.VType := varDate;
+ Temp.VDate := Value;
+ SetProperty(Index, Temp);
+end;
+
+procedure TOleCtl.SetOleEnumProp(Index: Integer; Value: TOleEnum);
+begin
+ SetIntegerProp(Index, Value);
+end;
+
+procedure TOleCtl.SetOleVariantProp(Index: Integer;
+ const Value: OleVariant);
+begin
+ SetProperty(Index, TVarData(Value));
+end;
+
+procedure TOleCtl.SetOnChar(const Value: TOnChar);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnChar := Value;
+end;
+
+procedure TOleCtl.SetOnLeave(const Value: TOnEvent);
+begin
+ OnExit := Value;
+end;
+
+procedure TOleCtl.SetParent(AParent: PControl);
+var
+ CS: IOleClientSite;
+ X: Integer;
+begin
+ inherited Parent := AParent;
+ if (AParent <> nil) then
+ begin
+ try // work around ATL bug
+ X := FOleObject.GetClientSite(CS);
+ except
+ X := -1;
+ end;
+ if (X <> 0) or (CS = nil) then
+ OleCheck(FOleObject.SetClientSite(fOleCtlIntf));
+ if FOleControl <> nil then
+ FOleControl.OnAmbientPropertyChange(DISPID_UNKNOWN);
+ end;
+end;
+
+procedure TOleCtl.SetProperty(Index: Integer; const Value: TVarData);
+const
+ DispIDArgs: Longint = DISPID_PROPERTYPUT;
+var
+ Status, InvKind: Integer;
+ DispParams: TDispParams;
+ ExcepInfo: TExcepInfo;
+begin
+ CreateControl;
+ DispParams.rgvarg := @Value;
+ DispParams.rgdispidNamedArgs := @DispIDArgs;
+ DispParams.cArgs := 1;
+ DispParams.cNamedArgs := 1;
+ if Value.VType <> varDispatch then
+ InvKind := DISPATCH_PROPERTYPUT else
+ InvKind := DISPATCH_PROPERTYPUTREF;
+ Status := FControlDispatch.Invoke(Index, GUID_NULL, 0,
+ InvKind, DispParams, nil, @ExcepInfo, nil);
+ if Status <> 0 then DispatchInvokeError(Status, ExcepInfo);
+end;
+
+procedure TOleCtl.SetShortIntProp(Index: Integer; Value: Shortint);
+begin
+ SetIntegerProp(Index, Value);
+end;
+
+procedure TOleCtl.SetSingleProp(Index: Integer; const Value: Single);
+var
+ Temp: TVarData;
+begin
+ Temp.VType := varSingle;
+ Temp.VSingle := Value;
+ SetProperty(Index, Temp);
+end;
+
+procedure TOleCtl.SetSmallintProp(Index: Integer; Value: Smallint);
+var
+ Temp: TVarData;
+begin
+ Temp.VType := varSmallint;
+ Temp.VSmallint := Value;
+ SetProperty(Index, Temp);
+end;
+
+procedure TOleCtl.SetStringProp(Index: Integer; const Value: string);
+var
+ Temp: TVarData;
+begin
+ Temp.VType := varOleStr;
+ Temp.VOleStr := StringToOleStr(Value);
+ try
+ SetProperty(Index, Temp);
+ finally
+ SysFreeString(Temp.VOleStr);
+ end;
+end;
+
+procedure TOleCtl.SetTColorProp(Index: Integer; Value: TColor);
+begin
+ SetIntegerProp(Index, Value);
+end;
+
+procedure TOleCtl.SetTDateTimeProp(Index: Integer; const Value: TDateTime);
+var
+ Temp: TVarData;
+begin
+ Temp.VType := varDate;
+ Temp.VDate := Value;
+ SetProperty(Index, Temp);
+end;
+
+procedure TOleCtl.SetTFontProp(Index: Integer; Value: PGraphicTool);
+{var
+ I: Integer;
+ F: TFont;
+ Temp: IFontDisp;}
+begin
+ {for I := 0 to FFonts.Count-1 do
+ if FControlData^.FontIDs^[I] = Index then
+ begin
+ F := TFont(FFonts[I]);
+ F.Assign(Value);
+ if F.FontAdapter = nil then
+ begin
+ GetOleFont(F, Temp);
+ SetIDispatchProp(Index, Temp);
+ end;
+ end;}
+ //TODO: implement TFont property later
+end;
+
+procedure TOleCtl.SetTOleEnumProp(Index: Integer; Value: TOleEnum);
+begin
+ SetIntegerProp(Index, Value);
+end;
+
+procedure TOleCtl.SetUIActive(Active: Boolean);
+var
+ Form: POleCtl; // declare it as POleCtl, though it is only PControl
+ // - to access its protected fields
+begin
+ Form := POleCtl( ParentForm );
+ if Form <> nil then
+ if Active then
+ begin
+ {if (Form.ActiveOleControl <> nil) and
+ (Form.ActiveOleControl <> Self) then
+ Form.ActiveOleControl.Perform(CM_UIDEACTIVATE, 0, 0);
+ Form.ActiveOleControl := Self;}
+ if (Form.DF.fCurrentControl <> nil) and
+ (Form.DF.fCurrentControl <> @Self) then
+ Form.DF.fCurrentControl.Perform(CM_UIDEACTIVATE, 0, 0);
+ Form.DF.fCurrentControl := @Self;
+ end else
+ if Form.DF.fCurrentControl = @Self then
+ Form.DF.fCurrentControl := nil;
+end;
+
+procedure TOleCtl.SetVariantProp(Index: Integer; const Value: Variant);
+begin
+ SetOleVariantProp(Index, Value);
+end;
+
+procedure TOleCtl.SetWideStringProp(Index: Integer;
+ const Value: WideString);
+var
+ Temp: TVarData;
+begin
+ Temp.VType := varOleStr;
+ if Value <> '' then
+ Temp.VOleStr := PWideChar(Value)
+ else
+ Temp.VOleStr := nil;
+ SetProperty(Index, Temp);
+end;
+
+procedure TOleCtl.SetWordBoolProp(Index: Integer; Value: WordBool);
+var
+ Temp: TVarData;
+begin
+ Temp.VType := varBoolean;
+ if Value then
+ Temp.VBoolean := WordBool(-1) else
+ Temp.VBoolean := WordBool(0);
+ SetProperty(Index, Temp);
+end;
+
+procedure TOleCtl.SetWordProp(Index: Integer; Value: Word);
+begin
+ SetIntegerProp(Index, Value);
+end;
+
+procedure TOleCtl.StandardEvent(DispID: TDispID; var Params: TDispParams);
+type
+ PVarDataList = ^TVarDataList;
+ TVarDataList = array[0..3] of TVarData;
+const
+ {ShiftMap: array[0..7] of TShiftState = (
+ [],
+ [ssShift],
+ [ssCtrl],
+ [ssShift, ssCtrl],
+ [ssAlt],
+ [ssShift, ssAlt],
+ [ssCtrl, ssAlt],
+ [ssShift, ssCtrl, ssAlt]);
+ MouseMap: array[0..7] of TShiftState = (
+ [],
+ [ssLeft],
+ [ssRight],
+ [ssLeft, ssRight],
+ [ssMiddle],
+ [ssLeft, ssMiddle],
+ [ssRight, ssMiddle],
+ [ssLeft, ssRight, ssMiddle]);}
+ ShiftMap: array[0..7] of DWord = (
+ 0,
+ MK_SHIFT,
+ MK_CONTROL,
+ MK_SHIFT or MK_CONTROL,
+ MK_ALT,
+ MK_SHIFT or MK_ALT,
+ MK_CONTROL or MK_ALT,
+ MK_SHIFT or MK_CONTROL or MK_ALT);
+ MouseMap: array[0..7] of DWORD = (
+ 0,
+ MK_LBUTTON,
+ MK_RBUTTON,
+ MK_LBUTTON or MK_RBUTTON,
+ MK_MBUTTON,
+ MK_LBUTTON or MK_MBUTTON,
+ MK_RBUTTON or MK_MBUTTON,
+ MK_LBUTTON or MK_RBUTTON or MK_MBUTTON);
+ ButtonMap: array[0..7] of TMouseButton = (
+ mbLeft, mbLeft, mbRight, mbLeft, mbMiddle, mbLeft, mbRight, mbLeft);
+var
+ Args: PVarDataList;
+ AShift: DWORD;
+ Button: TMouseButton;
+ X, Y: Integer;
+ Key: Longint;
+ Ch: KOLChar;
+begin
+ Args := PVarDataList(Params.rgvarg);
+ try
+ case DispID of
+ DISPID_CLICK:
+ Click;
+ DISPID_DBLCLICK:
+ DblClk;
+ DISPID_KEYDOWN, DISPID_KEYUP:
+ if Params.cArgs >= 2 then
+ begin
+ Key := Variant(Args^[1]);
+ X := Variant(Args^[0]);
+ case DispID of
+ DISPID_KEYDOWN: KeyDown(Key, X);
+ DISPID_KEYUP: KeyUp(Key, X);
+ end;
+ if ((Args^[1].vType and varByRef) <> 0) then
+ Word(Args^[1].VPointer^) := Key;
+ end;
+ DISPID_KEYPRESS:
+ if Params.cArgs > 0 then
+ begin
+ Ch := KOLChar(Integer(Variant(Args^[0])));
+ KeyPress(Ch);
+ if ((Args^[0].vType and varByRef) <> 0) then
+ KOLChar(Args^[0].VPointer^) := Ch;
+ end;
+ {DISPID_KEYPRESS:
+ if Params.cArgs > 0 then
+ begin
+ Ch := KOLChar(Integer(Variant(Args^[0])));
+ KeyPress(Ch);
+ if ((Args^[0].vType and varByRef) <> 0) then
+ KOLChar(Args^[0].VPointer^) := Ch;
+ end;}
+ DISPID_MOUSEDOWN, DISPID_MOUSEMOVE, DISPID_MOUSEUP:
+ if Params.cArgs >= 4 then
+ begin
+ X := Integer(Variant(Args^[3])) and 7;
+ Y := Integer(Variant(Args^[2])) and 7;
+ Button := ButtonMap[X];
+ AShift := ShiftMap[Y] + MouseMap[X];
+ X := Variant(Args^[1]);
+ Y := Variant(Args^[0]);
+ case DispID of
+ DISPID_MOUSEDOWN:
+ MouseDown(Button, AShift, X, Y);
+ DISPID_MOUSEMOVE:
+ MouseMove(AShift, X, Y);
+ DISPID_MOUSEUP:
+ MouseUp(Button, AShift, X, Y);
+ end;
+ end;
+ end;
+ except
+ DoHandleException;
+ end;
+end;
+
+{$IFNDEF _D2orD3}
+{ TServerEventDispatch }
+constructor TServerEventDispatch.Create(Server: TOleServer);
+begin
+ FServer := Server;
+ InternalRefCount := 1;
+end;
+
+{ TServerEventDispatch.IUnknown }
+function TServerEventDispatch.QueryInterface(const IID: TGUID; out Obj): HResult;
+begin
+ if GetInterface(IID, Obj) then
+ begin
+ Result := S_OK;
+ Exit;
+ end;
+ if IsEqualIID(IID, FServer.FServerData^.EventIID) then
+ begin
+ GetInterface(IDispatch, Obj);
+ Result := S_OK;
+ Exit;
+ end;
+ Result := E_NOINTERFACE;
+end;
+
+function TServerEventDispatch._AddRef: Integer;
+begin
+ if FServer <> nil then FServer._AddRef;
+ InternalRefCount := InternalRefCount + 1;
+ Result := InternalRefCount;
+end;
+
+function TServerEventDispatch._Release: Integer;
+begin
+ if FServer <> nil then FServer._Release;
+ InternalRefCount := InternalRefCount -1;
+ Result := InternalRefCount;
+end;
+
+{ TServerEventDispatch.IDispatch }
+function TServerEventDispatch.GetTypeInfoCount(out Count: Integer): HResult;
+begin
+ Count := 0;
+ Result:= S_OK;
+end;
+
+function TServerEventDispatch.GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult;
+begin
+ Pointer(TypeInfo) := nil;
+ Result := E_NOTIMPL;
+end;
+
+function TServerEventDispatch.GetIDsOfNames(const IID: TGUID; Names: Pointer;
+ NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
+begin
+ Result := E_NOTIMPL;
+end;
+
+function TServerEventDispatch.Invoke(DispID: Integer; const IID: TGUID;
+ LocaleID: Integer; Flags: Word; var Params;
+ VarResult, ExcepInfo, ArgErr: Pointer): HResult;
+var
+ ParamCount, I: integer;
+ VarArray : TVariantArray;
+begin
+ // Get parameter count
+ ParamCount := TDispParams(Params).cArgs;
+ // Set our array to appropriate length
+ SetLength(VarArray, ParamCount);
+ // Copy over data
+ for I := Low(VarArray) to High(VarArray) do
+ VarArray[High(VarArray)-I] := OleVariant(TDispParams(Params).rgvarg^[I]);
+ // Invoke Server proxy class
+ if FServer <> nil then FServer.InvokeEvent(DispID, VarArray);
+ // Clean array
+ SetLength(VarArray, 0);
+ // Pascal Events return 'void' - so assume success!
+ Result := S_OK;
+end;
+
+function TServerEventDispatch.ServerDisconnect : Boolean;
+begin
+ FServer := nil;
+ if FServer <> nil then
+ Result := false
+ else Result := true;
+end;
+
+{TOleServer}
+constructor TOleServer.Create; //(AOwner: TComponent);
+begin
+ inherited; // Create(AOwner);
+ // Allow derived class to initialize ServerData structure pointer
+ InitServerData;
+ // Make sure derived class set ServerData pointer to some valid structure
+ Assert(FServerData <> nil);
+ // Increment instance count (not used currently)
+ Inc(FServerData^.InstanceCount);
+ // Create Event Dispatch Handler
+ FEventDispatch := TServerEventDispatch.Create(Self);
+end;
+
+destructor TOleServer.Destroy;
+begin
+ // Disconnect from the Server (NOTE: Disconnect must handle case when we're no longer connected)
+ Disconnect;
+ // Free Events dispatcher
+ FEventDispatch.ServerDisconnect;
+ if (FEventDispatch._Release = 0) then FEventDispatch.Free;
+ // Decrement refcount
+ Dec(FServerData^.InstanceCount);
+ inherited Destroy;
+end;
+
+procedure TOleServer.Loaded;
+begin
+ {inherited Loaded;}
+
+ // Load Server if user requested 'AutoConnect' and we're not in Design mode
+ {if not (csDesigning in ComponentState) then}
+ if AutoConnect then
+ Connect;
+end;
+
+procedure TOleServer.InvokeEvent(DispID: TDispID; var Params: TVariantArray);
+begin
+ // To be overriden in derived classes to do dispatching
+end;
+
+function TOleServer.GetServer: IUnknown;
+var
+ HR: HResult;
+ ErrorStr: string;
+begin
+ case ConnectKind of
+ ckNewInstance:
+ Result := CreateComObject(FServerData^.ClassId);
+
+ ckRunningInstance:
+ begin
+ HR := GetActiveObject(FServerData^.ClassId, nil, Result);
+ if not Succeeded(HR) then
+ begin
+ ErrorStr := Format(sNoRunningObject, [ClassIDToProgID(FServerData^.ClassId),
+ GuidToString(FServerData^.ClassId)]);
+ raise EOleSysError.Create( e_Ole, ErrorStr {, HR, 0} );
+ end;
+ end;
+
+ ckRunningOrNew:
+ if not Succeeded(GetActiveObject(FServerData^.ClassId, nil, Result)) then
+ Result := CreateComObject(FServerData^.ClassId);
+
+ ckRemote:
+ {Highly inefficient: requires at least two round trips - GetClassObject + QI}
+ Result := CreateRemoteComObject(RemoteMachineName, FServerData^.ClassID);
+ end;
+end;
+
+procedure TOleServer.ConnectEvents(const Obj: IUnknown);
+begin
+ KOLComObj.InterfaceConnect(Obj, FServerData^.EventIID, FEventDispatch, FEventsConnection);
+end;
+
+procedure TOleServer.DisconnectEvents(const Obj: Iunknown);
+begin
+ KOLComObj.InterfaceDisconnect(Obj, FServerData^.EventIID, FEventsConnection);
+end;
+
+function TOleServer.GetConnectKind: TConnectKind;
+begin
+ // Should the setting of a RemoteMachine name override the Connection Kind ??
+ if RemoteMachineName <> '' then
+ Result := ckRemote
+ else
+ Result := FConnectKind;
+end;
+
+procedure TOleServer.SetConnectKind(cK: TConnectKind);
+begin
+ // Should we validate that we have a RemoteMachineName for ckRemote ??
+ FConnectKind := cK;
+end;
+
+function TOleServer.GetAutoConnect: Boolean;
+begin
+ // If user wants to provide the interface to connect to, then we won't
+ // 'automatically' connect to a server.
+ if ConnectKind = ckAttachToInterface then
+ Result := False
+ else
+ Result := FAutoConnect;
+end;
+
+procedure TOleServer.SetAutoConnect(flag: Boolean);
+begin
+ FAutoConnect := flag;
+end;
+
+{ TOleServer.IUnknown }
+function TOleServer.QueryInterface(const IID: TGUID; out Obj): HResult;
+begin
+ if GetInterface(IID, Obj) then
+ Result := S_OK
+ else
+ Result := E_NOINTERFACE;
+end;
+
+function TOleServer._AddRef: Integer;
+begin
+ Inc(FRefCount);
+ Result := FRefCount;
+end;
+
+function TOleServer._Release: Integer;
+begin
+ Dec(FRefCount);
+ Result := FRefCount;
+end;
+{$ENDIF _D2orD3}
+
+{ TEventDispatch }
+
+constructor TEventDispatch.Create(Control: POleCtl);
+begin
+ FControl := Control;
+end;
+
+{ TEventDispatch.IUnknown }
+
+function TEventDispatch.QueryInterface(const IID: TGUID; out Obj): HResult;
+begin
+ if GetInterface(IID, Obj) then
+ begin
+ Result := S_OK;
+ Exit;
+ end;
+ if IsEqualIID(IID, FControl.FControlData^.EventIID) then
+ begin
+ GetInterface(IDispatch, Obj);
+ Result := S_OK;
+ Exit;
+ end;
+ Result := E_NOINTERFACE;
+end;
+
+function TEventDispatch._AddRef: Integer;
+begin
+ Result := FControl.fOleCtlIntf._AddRef;
+end;
+
+function TEventDispatch._Release: Integer;
+begin
+ Result := FControl.fOleCtlIntf._Release;
+end;
+
+{ TEventDispatch.IDispatch }
+
+function TEventDispatch.GetTypeInfoCount(out Count: Integer): HResult;
+begin
+ Count := 0;
+ Result := S_OK;
+end;
+
+function TEventDispatch.GetTypeInfo(Index, LocaleID: Integer;
+ out TypeInfo): HResult;
+begin
+ Pointer(TypeInfo) := nil;
+ Result := E_NOTIMPL;
+end;
+
+function TEventDispatch.GetIDsOfNames(const IID: TGUID; Names: Pointer;
+ NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
+begin
+ Result := E_NOTIMPL;
+end;
+
+function TEventDispatch.Invoke(DispID: Integer; const IID: TGUID;
+ LocaleID: Integer; Flags: Word; var Params;
+ VarResult, ExcepInfo, ArgErr: Pointer): HResult;
+begin
+ if (DispID >= DISPID_MOUSEUP) and (DispID <= DISPID_CLICK) then
+ FControl.StandardEvent(DispID, TDispParams(Params)) else
+ FControl.InvokeEvent(DispID, TDispParams(Params));
+ Result := S_OK;
+end;
+
+{ TOleCtlIntf }
+
+function TOleCtlIntf._AddRef: Integer;
+begin
+ //{$IFDEF _D2orD3}
+ //Result := inherited _AddRef;
+ //{$ELSE}
+ Inc(FRefCount);
+ Result := FRefCount;
+ //{$ENDIF}
+end;
+
+function TOleCtlIntf._Release: Integer;
+begin
+ //{$IFDEF _D2orD3}
+ //Result := inherited _Release;
+ //{$ELSE}
+ Dec(FRefCount);
+ Result := FRefCount;
+ //{$ENDIF}
+end;
+
+function TOleCtlIntf.CanInPlaceActivate: HResult;
+begin
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.ContextSensitiveHelp(fEnterMode: BOOL): HResult;
+begin
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.DeactivateAndUndo: HResult;
+begin
+ fOleCtl.FOleInPlaceObject.UIDeactivate;
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.DiscardUndoState: HResult;
+begin
+ Result := E_NOTIMPL;
+end;
+
+function TOleCtlIntf.EnableModeless(fEnable: BOOL): HResult;
+begin
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.GetBorder(out rectBorder: TRect): HResult;
+begin
+ Result := INPLACE_E_NOTOOLSPACE;
+end;
+
+function TOleCtlIntf.GetContainer(out container: IOleContainer): HResult;
+begin
+ Result := E_NOINTERFACE;
+end;
+
+function TOleCtlIntf.GetExtendedControl(out disp: IDispatch): HResult;
+begin
+ Result := E_NOTIMPL;
+end;
+
+function TOleCtlIntf.GetIDsOfNames(const IID: TGUID; Names: Pointer;
+ NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
+begin
+ Result := E_NOTIMPL;
+end;
+
+function TOleCtlIntf.GetMoniker(dwAssign, dwWhichMoniker: Integer;
+ out mk: IMoniker): HResult;
+begin
+ Result := E_NOTIMPL;
+end;
+
+function TOleCtlIntf.GetTypeInfo(Index, LocaleID: Integer;
+ out TypeInfo): HResult;
+begin
+ Pointer(TypeInfo) := nil;
+ Result := E_NOTIMPL;
+end;
+
+function TOleCtlIntf.GetTypeInfoCount(out Count: Integer): HResult;
+begin
+ Count := 0;
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.GetWindowContext(out frame: IOleInPlaceFrame;
+ out doc: IOleInPlaceUIWindow; out rcPosRect, rcClipRect: TRect;
+ out frameInfo: TOleInPlaceFrameInfo): HResult;
+begin
+ frame := Self;
+ doc := nil;
+ rcPosRect := fOleCtl.BoundsRect;
+ rcClipRect := MakeRect( 0, 0, 32767, 32767 );
+ with frameInfo do
+ begin
+ fMDIApp := False;
+ hWndFrame := fOleCtl.ParentForm.GetWindowHandle;
+ //GetTopParentHandle;
+ // now it is not possible to make alien window to be parent for KOL window
+ hAccel := 0;
+ cAccelEntries := 0;
+ end;
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.InsertMenus(hmenuShared: HMenu;
+ var menuWidths: TOleMenuGroupWidths): HResult;
+{var
+ Menu: TMainMenu;}
+begin
+ {Menu := GetMainMenu;
+ if Menu <> nil then
+ Menu.PopulateOle2Menu(hmenuShared, [0, 2, 4], menuWidths.width);}
+ //TODO: implement menu populate
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.Invoke(DispID: Integer; const IID: TGUID;
+ LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,
+ ArgErr: Pointer): HResult;
+{var
+ F: PGraphicTool;}
+begin
+ if (Flags and DISPATCH_PROPERTYGET <> 0) and (VarResult <> nil) then
+ begin
+ Result := S_OK;
+ case DispID of
+ DISPID_AMBIENT_BACKCOLOR:
+ PVariant(VarResult)^ := fOleCtl.Color;
+ DISPID_AMBIENT_DISPLAYNAME:
+ PVariant(VarResult)^ := StringToVarOleStr( fOleCtl.Name );
+ DISPID_AMBIENT_FONT:
+ begin
+ {if (fOleCtl.Parent <> nil) and fOleCtl.ParentFont then
+ F := Parent.Font // TOleControl(Parent).Font
+ else
+ F := Font;
+ PVariant(VarResult)^ := FontToOleFont(F);}
+ //TODO: implement Font later
+ end;
+ DISPID_AMBIENT_FORECOLOR:
+ PVariant(VarResult)^ := fOleCtl.fTextColor; // Font.Color;
+ DISPID_AMBIENT_LOCALEID:
+ PVariant(VarResult)^ := Integer(GetUserDefaultLCID);
+ DISPID_AMBIENT_MESSAGEREFLECT:
+ PVariant(VarResult)^ := True;
+ DISPID_AMBIENT_USERMODE:
+ PVariant(VarResult)^ := TRUE; // not (csDesigning in ComponentState);
+ DISPID_AMBIENT_UIDEAD:
+ PVariant(VarResult)^ := FALSE; // csDesigning in ComponentState;
+ DISPID_AMBIENT_SHOWGRABHANDLES:
+ PVariant(VarResult)^ := False;
+ DISPID_AMBIENT_SHOWHATCHING:
+ PVariant(VarResult)^ := False;
+ DISPID_AMBIENT_SUPPORTSMNEMONICS:
+ PVariant(VarResult)^ := True;
+ DISPID_AMBIENT_AUTOCLIP:
+ PVariant(VarResult)^ := True;
+ else
+ Result := DISP_E_MEMBERNOTFOUND;
+ end;
+ end else
+ Result := DISP_E_MEMBERNOTFOUND;
+end;
+
+function TOleCtlIntf.LockInPlaceActive(fLock: BOOL): HResult;
+begin
+ Result := E_NOTIMPL;
+end;
+
+function TOleCtlIntf.OleControlSite_TranslateAccelerator(msg: PMsg;
+ grfModifiers: Integer): HResult;
+begin
+ Result := E_NOTIMPL;
+end;
+
+function TOleCtlIntf.OleInPlaceFrame_GetWindow(out wnd: HWnd): HResult;
+begin
+ wnd := fOleCtl.ParentForm.GetWindowHandle; // GetTopParentHandle;
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.OleInPlaceFrame_TranslateAccelerator(var msg: Windows.TMsg;
+ wID: Word): HResult;
+begin
+ Result := S_FALSE;
+end;
+
+function TOleCtlIntf.OleInPlaceSite_GetWindow(out wnd: HWnd): HResult;
+begin
+ Result := S_OK;
+ wnd := fOleCtl.ParentWindow;
+ if wnd = 0 then Result := E_FAIL;
+end;
+
+function TOleCtlIntf.OnChanged(dispid: TDispID): HResult;
+begin
+ try
+ case dispid of
+ DISPID_BACKCOLOR:
+ if not fOleCtl.FUpdatingColor then
+ begin
+ fOleCtl.FUpdatingColor := True;
+ try
+ fOleCtl.fColor := fOleCtl.GetIntegerProp(DISPID_BACKCOLOR);
+ finally
+ fOleCtl.FUpdatingColor := False;
+ end;
+ end;
+ DISPID_ENABLED:
+ if not fOleCtl.FUpdatingEnabled then
+ begin
+ fOleCtl.FUpdatingEnabled := True;
+ try
+ fOleCtl.Enabled := fOleCtl.GetWordBoolProp(DISPID_ENABLED);
+ finally
+ fOleCtl.FUpdatingEnabled := False;
+ end;
+ end;
+ DISPID_FONT:
+ if not fOleCtl.FUpdatingFont then
+ begin
+ fOleCtl.FUpdatingFont := True;
+ try
+ //OleFontToFont(GetVariantProp(DISPID_FONT), Font);
+ // font - implement later
+ finally
+ fOleCtl.FUpdatingFont := False;
+ end;
+ end;
+ DISPID_FORECOLOR:
+ if not fOleCtl.FUpdatingFont then
+ begin
+ fOleCtl.FUpdatingFont := True;
+ try
+ fOleCtl.fTextColor := fOleCtl.GetIntegerProp(DISPID_FORECOLOR);
+ //Font.Color := GetIntegerProp(DISPID_FORECOLOR);
+ finally
+ fOleCtl.FUpdatingFont := False;
+ end;
+ end;
+ end;
+ except // control sent us a notification for a dispid it doesn't have.
+ //on EOleError do ;
+ end;
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.OnControlInfoChanged: HResult;
+begin
+ Result := E_NOTIMPL;
+end;
+
+function TOleCtlIntf.OnFocus(fGotFocus: BOOL): HResult;
+begin
+ Result := E_NOTIMPL;
+end;
+
+function TOleCtlIntf.OnInPlaceActivate: HResult;
+begin
+ fOleCtl.FOleObject.QueryInterface( IOleInPlaceObject,
+ fOleCtl.FOleInPlaceObject);
+ fOleCtl.FOleObject.QueryInterface( IOleInPlaceActiveObject,
+ fOleCtl.FOleInPlaceActiveObject);
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.OnInPlaceDeactivate: HResult;
+begin
+ fOleCtl.FOleInPlaceActiveObject := nil;
+ fOleCtl.FOleInPlaceObject := nil;
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.OnPosRectChange(const rcPosRect: TRect): HResult;
+begin
+ fOleCtl.FOleInPlaceObject.SetObjectRects(rcPosRect, MakeRect(0, 0, 32767, 32767));
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.OnRequestEdit(dispid: TDispID): HResult;
+begin
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.OnShowWindow(fShow: BOOL): HResult;
+begin
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.OnUIActivate: HResult;
+begin
+ fOleCtl.SetUIActive(True);
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.OnUIDeactivate(fUndoable: BOOL): HResult;
+begin
+ SetMenu(0, 0, 0);
+ fOleCtl.SetUIActive(False);
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.PostMessageFilter(wnd: HWnd; msg, wp, lp: Integer;
+ out res: Integer; Cookie: Integer): HResult;
+begin
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.PreMessageFilter(wnd: HWnd; msg, wp, lp: Integer;
+ out res, Cookie: Integer): HResult;
+begin
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.QueryInterface(const IID: TGUID; out Obj): HResult;
+begin
+ if GetInterface(IID, Obj) then Result := S_OK else Result := E_NOINTERFACE;
+end;
+
+function TOleCtlIntf.RemoveMenus(hmenuShared: HMenu): HResult;
+begin
+ while GetMenuItemCount(hmenuShared) > 0 do
+ RemoveMenu(hmenuShared, 0, MF_BYPOSITION);
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.RequestBorderSpace(
+ const borderwidths: TRect): HResult;
+begin
+ Result := INPLACE_E_NOTOOLSPACE;
+end;
+
+function TOleCtlIntf.RequestNewObjectLayout: HResult;
+var
+ Extent: TPoint;
+ W, H: Integer;
+ DC: HDC;
+ PixelsPerInch: Integer;
+begin
+ Result := fOleCtl.FOleObject.GetExtent(DVASPECT_CONTENT, Extent);
+ if Result <> S_OK then Exit;
+
+ W := fOleCtl.Width;
+ H := fOleCtl.Height;
+ if (W = 0) or (H = 0) then
+ begin
+ DC := GetDC(0);
+ PixelsPerInch := GetDeviceCaps(DC, LOGPIXELSY);
+ ReleaseDC(0, DC);
+
+ W := MulDiv(Extent.X, PixelsPerInch, 2540);
+ H := MulDiv(Extent.Y, PixelsPerInch, 2540);
+ if (fOleCtl.FMiscStatus and OLEMISC_INVISIBLEATRUNTIME <> 0) and
+ (fOleCtl.FOleControl = nil) then
+ begin
+ if W > 32 then W := 32;
+ if H > 32 then H := 32;
+ end;
+ end;
+ fOleCtl.SetBoundsRect( MakeRect( fOleCtl.Left, fOleCtl.Top,
+ fOleCtl.Left + W, fOleCtl.Top + H ) );
+end;
+
+function TOleCtlIntf.SaveObject: HResult;
+begin
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.Scroll(scrollExtent: TPoint): HResult;
+begin
+ Result := E_NOTIMPL;
+end;
+
+function TOleCtlIntf.SetActiveObject(
+ const activeObject: IOleInPlaceActiveObject;
+ pszObjName: POleStr): HResult;
+begin
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.SetBorderSpace(pborderwidths: PRect): HResult;
+begin
+ Result := E_NOTIMPL;
+end;
+
+function TOleCtlIntf.SetMenu(hmenuShared, holemenu: HMenu;
+ hwndActiveObject: HWnd): HResult;
+var
+ Menu: HMenu;
+begin
+ Menu := fOleCtl.GetMainMenu;
+ Result := S_OK;
+ if Menu <> 0 then
+ begin
+ //Menu.SetOle2MenuHandle(hmenuShared);
+ Result := OleSetMenuDescriptor( holemenu,
+ fOleCtl.ParentForm.GetWindowHandle,
+ hwndActiveObject, nil, nil);
+ end;
+end;
+
+function TOleCtlIntf.SetStatusText(pszStatusText: POleStr): HResult;
+begin
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.ShowObject: HResult;
+begin
+ fOleCtl.HookControlWndProc;
+ Result := S_OK;
+end;
+
+function TOleCtlIntf.ShowPropertyFrame: HResult;
+begin
+ Result := E_NOTIMPL;
+end;
+
+function TOleCtlIntf.TransformCoords(var ptlHimetric: TPoint;
+ var ptfContainer: TPointF; flags: Integer): HResult;
+var DC: HDC;
+ PixelsPerInch: Integer;
+begin
+ DC := GetDC(0);
+ PixelsPerInch := GetDeviceCaps(DC, LOGPIXELSY);
+ ReleaseDC(0, DC);
+
+ if flags and XFORMCOORDS_HIMETRICTOCONTAINER <> 0 then
+ begin
+ ptfContainer.X := MulDiv(ptlHimetric.X, PixelsPerInch, 2540);
+ ptfContainer.Y := MulDiv(ptlHimetric.Y, PixelsPerInch, 2540);
+ end else
+ begin
+ ptlHimetric.X := Integer(Round(ptfContainer.X * 2540 / PixelsPerInch));
+ ptlHimetric.Y := Integer(Round(ptfContainer.Y * 2540 / PixelsPerInch));
+ end;
+ Result := S_OK;
+end;
+
+constructor TOleCtlIntf.Create;
+begin
+ inherited;
+end;
+
+
+end.
diff --git a/plugins/Libs/BASS_DSHOW.pas b/plugins/Libs/BASS_DSHOW.pas
new file mode 100644
index 0000000000..e8778f6f66
--- /dev/null
+++ b/plugins/Libs/BASS_DSHOW.pas
@@ -0,0 +1,275 @@
+unit BASS_DSHOW;
+{
+ BASS_DSHOW 2.4 Delphi unit
+ Copyright (c) 2009-2010 Cristea Aurel Ionut.
+}
+
+interface
+
+uses
+ Windows,dynamic_bass;
+
+const
+ {BASS_DSHOW Plugin CLSID}
+ CLSID_DSHOWPLUGIN: TGUID = '{00000000-0000-0000-0000-000000000000}';
+ BASS_DSHOW_VERSION = $20401; // API version
+ BASS_DSHOW_VERSIONTEXT = '2.4.1'; //TEXT version
+
+type
+ HENCODE = DWORD;
+ HWINDOW = DWORD;
+ HRECORD = DWORD;
+
+//for Mix_StreamCreate function
+ TMixingFiles = array[0..15] of PCHAR;
+
+/////////////CALLBACKS///////////////////
+///
+ TCallBackEnumEncoderFilter = function(Filter : Pointer; FilterName: PChar) : BOOL; stdcall;
+ TCallBackConnectedFilters = function(Filter : Pointer; FilterName: PChar;pp:BOOL;user:pointer) : BOOL; stdcall;
+ TCallBackEnumDevices = function(device: PChar;user:Pointer) : BOOL; stdcall;
+ /////////////////////////////////////////
+
+//for BASS_DSHOW_ChannelGetInfo function
+ PBASS_DSVIDEOINFO= ^TBASS_DSVIDEOINFO;
+ TBASS_DSVIDEOINFO = record
+ AvgTimePerFrame : Double;
+ Height, Width : integer;
+ end;
+//for BASS_DSHOW_ChannelSetConfig function
+ PTTextOverlayStruct = ^TTextOverlayStruct;
+ TTextOverlayStruct = record
+ x: integer; //x position
+ y: integer; //y position
+ red: integer;
+ green: integer;
+ blue : integer;
+ end;
+
+ PTVideoColors = ^TVideoColors;
+ TVideoColors=record
+ HUE: integer; //-180...180
+ Contrast: integer; //0...128
+ Brightness: integer; //-128...128.
+ Saturation: integer; //0...128
+ end;
+
+const /////flags
+ DLLNAME = 'BASS_DSHOW.DLL';
+ BASS_DSHOW_DECODE = BASS_STREAM_DECODE;
+
+//for BASS_DSHOW_SetConfig function
+ DSHOW_VMRWINDOW = 95; //VMR need an initial window so set a HWND to use properly VMR
+ BASS_DSHOW_VideoRenderer = 96;
+ BASS_DSHOW_USEDefault = 97; //pass this to select default video render
+ BASS_DSHOW_USEOverlay = 98; //pass this to select overlay video render
+ BASS_DSHOW_USEVMR = 99; //pass this to setconfig option to turn on/off VMR
+//for BASS_DSHOW_DVDSetOption
+ DVD_TITLE = 100;
+ DVD_ROOT = 101; //go to DVD root
+ DVD_NEXTCHAPTER = 102; //go to dvd next chapter
+ DVD_PREVCHAPTER = 103; //go to dvd previous chapter
+ BD_ShowVideoWindow = 1001; //set this to show/hide video
+
+// BASS_DSHOW_ChannelSetOption function flags
+ DSHOW_Overlay = 1002;
+ DSHOW_OverlayText = 1003;
+ DSHOW_OverlayProp = 1004;
+ DSHOW_AVSync = 1005;
+ DSHOW_CONFIG_PITCH = 1007;
+ DSHOW_CheckChannel = 1009; //for sync with a channel when first is a decoded one
+ DSHOW_4p3 = 1010;
+ DSHOW_16p9 = 1011;
+ DSHOW_AspectRatio = 1012;
+ DSHOW_GetBitmap = 1013;
+ DSHOW_VideoColors = 1014;
+ DSHOW_EnablePitch = 1015; //2.4.1
+////////MIX FLAGS//////////////////////
+ BASS_DSHOW_MixRect = 2000;
+ BASS_DSHOW_MixAlpha = 2001;
+
+//ERROR CODES
+
+ BASS_DSHOW_OK = 104; //all is ok
+ BASS_DSHOW_INVALIDCHAN = 113; //invalid channel
+ BASS_DSHOW_BADFILENAME = 105;
+ BASS_DSHOW_Unknown = 106;
+ BASS_DSHOW_ERROR1 = 107; //this is returned by set dvd menu function
+ BASS_DSHOW_ERROR2 = 108; // next chapter failed
+ BASS_DSHOW_ERROR3 = 109; //prev chapter failed
+ BASS_DSHOW_ERROR4 = 110; // title menu failed
+ BASS_DSHOW_ERROR5 = 111; //graph creation failed
+ BASS_DSHOW_ERROR6 = 112; //DVD Graph creation failed
+ BASS_DSHOW_ERROR7 = 114;
+ BASS_DSHOW_ERROR8 = 115; //NO DVD Decoder found
+
+//Converter Flags///
+ Convert_EncoderVideo = 3000;
+ Convert_EncoderAudio = 3001;
+ Convert_AudioCompressor = 3002;
+ Convert_VideoCompressor = 3003;
+
+ Convert_DisableAudio = 3005; //convert only audio. Disables video
+//Profiles
+ Convert_ToAvi = 3007; //convert to avi
+ Convert_ToWMV = 3008; //convert to WMV
+ Convert_ToWAV = 3009; //convert to WAV
+
+///Recorder Flags///
+ Record_AudioDevice = 5000;
+ Record_VideoDevice = 5001;
+
+///
+///
+var BASS_DSHOW_StreamCreateURL :function(str: PCHAR;flags: DWORD): HSTREAM; stdcall;
+var BASS_DSHOW_StreamCreateFile:function(str: PCHAR;flags: DWORD): HSTREAM; stdcall;
+var BASS_DSHOW_StreamFree :function(chan: HStream): bool; stdcall;
+var BASS_DSHOW_StreamCreateDVD :function():HSTREAM; stdcall;
+
+var BASS_DSHOW_Init:function(handle: HWND):bool; stdcall;
+var BASS_DSHOW_Free:function(): BOOL; stdcall;
+
+var BASS_DSHOW_ChannelSetPosition :procedure(chan: HSTREAM;pos: QWORD); stdcall;
+var BASS_DSHOW_ChannelGetLength :function (chan: HSTREAM): QWORD; stdcall;
+var BASS_DSHOW_ChannelGetPosition :function (chan: HSTREAM): QWORD; stdcall;
+var BASS_DSHOW_ChannelSetWindow :procedure(chan: HSTREAM;handle: HWND); stdcall;
+var BASS_DSHOW_ChannelResizeWindow :procedure(chan: HSTREAM;left,top,right,bottom: integer); stdcall;
+var BASS_DSHOW_ChannelSetFullscreen :procedure(chan: HSTREAM;value: boolean); stdcall;
+var BASS_DSHOW_ChannelPlay :function (chan: HSTREAM):bool; stdcall;
+var BASS_DSHOW_ChannelPause :function (chan: HSTREAM):bool; stdcall;
+var BASS_DSHOW_ChannelStop :function (chan: HStream): bool; stdcall;
+var BASS_DSHOW_ChannelGetInfo :procedure(chan: HSTREAM;value: PBASS_DSVIDEOINFO);stdcall;
+var BASS_DSHOW_ChannelSetOption :procedure(chan:HSTREAM;option:DWORD;value:DWORD;value2: pointer); stdcall;
+var BASS_DSHOW_ChannelGetConnectedFilters:procedure(chan: HSTREAM;callback :Pointer;user:Pointer); stdcall; //2.4.1
+var BASS_DSHOW_ChannelSetTextOverlay :procedure(chan: HSTREAM ;text:PCHAR;x, y, red, green, blue: integer); stdcall;
+var BASS_DSHOW_ChannelAddWindow :function(chan:HSTREAM;win:HWND): HWINDOW; stdcall;
+
+var BASS_DSHOW_DVDSetOption:function(chan: HStream;option: DWORD): bool; stdcall;
+var BASS_DSHOW_SetConfig :procedure(config: integer;value: integer); stdcall;
+var BASS_DSHOW_ErrorGetCode:function(): DWORD; stdcall;
+var BASS_DSHOW_LoadPlugin :procedure(str: pchar;guid :TGUID;name: PCHAR); stdcall;
+var BASS_DSHOW_LoadPlugin2 :procedure(str: Pointer;guid :Pointer;name: Pointer;flags: DWORD); stdcall;
+var BASS_DSHOW_GetVersion :function(): DWORD; stdcall;
+
+var BASS_DSHOW_ShowFilterPropertyPage:procedure(chan:HSTREAM;filter:DWORD;hndparent: HWND); stdcall; //2.4.1
+var BASS_DSHOW_MIX_StreamCreateFile:function(files: TMixingFiles;fileno:integer;flags: DWORD): HSTREAM; stdcall;
+var BASS_DSHOW_MIX_ChanOptions :function(chan: HSTREAM;option:DWORD;value: DWORD;value2: DWORD;rect: TRECT): BOOL; stdcall;
+
+//////// STILL TEsting encoding//////
+var BASS_DSHOW_Encode_GetCodecs :function(CodecsType:DWORD;callback: Pointer):integer; stdcall;
+var BASS_DSHOW_Encode_GetProfiles :function(CodecsType:DWORD;callback: Pointer):integer; stdcall;
+var BASS_DSHOW_Encode_StreamCreate:function(inFile: PCHAR;outFile:PChar): HENCODE; stdcall;
+var BASS_DSHOW_Encode_Start :function(hnd: HENCODE;profile:DWORD;flags: DWORD): BOOL; stdcall;
+var BASS_DSHOW_Encode_Stop :function(hnd: HENCODE): BOOL; stdcall;
+var BASS_DSHOW_Encode_GetPosition :function(hnd: HENCODE): DWORD; stdcall;
+var BASS_DSHOW_Encode_SetEncoder :function(hnd:HENCODE;enctype: DWORD;encoder: DWORD): BOOL; stdcall;
+
+///////
+var BASS_DSHOW_Record_GetDevices:function(devicetype: DWORD;callback: Pointer;user: Pointer): integer; stdcall;
+var BASS_DSHOW_RecordStart :function(audiodevice: Integer;videodevice: Integer;devicetype: DWORD;flags: DWORD): HRECORD; stdcall;
+var BASS_DSHOW_RecordFree :function(rec: HRECORD): BOOL; stdcall;
+
+implementation
+// END OF FILE /////////////////////////////////////////////////////////////////
+
+procedure SetProcs(handle:THANDLE);
+begin
+ @BASS_DSHOW_StreamCreateURL :=GetProcAddress(handle, 'BASS_DSHOW_StreamCreateURL');
+ @BASS_DSHOW_StreamCreateFile:=GetProcAddress(handle, 'BASS_DSHOW_StreamCreateFile');
+ @BASS_DSHOW_StreamFree :=GetProcAddress(handle, 'BASS_DSHOW_StreamFree');
+ @BASS_DSHOW_StreamCreateDVD :=GetProcAddress(handle, 'BASS_DSHOW_StreamCreateDVD');
+
+ @BASS_DSHOW_Init:=GetProcAddress(handle, 'BASS_DSHOW_Init');
+ @BASS_DSHOW_Free:=GetProcAddress(handle, 'BASS_DSHOW_Free');
+
+ @BASS_DSHOW_ChannelSetPosition :=GetProcAddress(handle, 'BASS_DSHOW_ChannelSetPosition');
+ @BASS_DSHOW_ChannelGetLength :=GetProcAddress(handle, 'BASS_DSHOW_ChannelGetLength');
+ @BASS_DSHOW_ChannelGetPosition :=GetProcAddress(handle, 'BASS_DSHOW_ChannelGetPosition');
+ @BASS_DSHOW_ChannelSetWindow :=GetProcAddress(handle, 'BASS_DSHOW_ChannelSetWindow');
+ @BASS_DSHOW_ChannelResizeWindow :=GetProcAddress(handle, 'BASS_DSHOW_ChannelResizeWindow');
+ @BASS_DSHOW_ChannelSetFullscreen :=GetProcAddress(handle, 'BASS_DSHOW_ChannelSetFullscreen');
+ @BASS_DSHOW_ChannelPlay :=GetProcAddress(handle, 'BASS_DSHOW_ChannelPlay');
+ @BASS_DSHOW_ChannelPause :=GetProcAddress(handle, 'BASS_DSHOW_ChannelPause');
+ @BASS_DSHOW_ChannelStop :=GetProcAddress(handle, 'BASS_DSHOW_ChannelStop');
+ @BASS_DSHOW_ChannelGetInfo :=GetProcAddress(handle, 'BASS_DSHOW_ChannelGetInfo');
+ @BASS_DSHOW_ChannelSetOption :=GetProcAddress(handle, 'BASS_DSHOW_ChannelSetOption');
+ @BASS_DSHOW_ChannelGetConnectedFilters:=GetProcAddress(handle, 'BASS_DSHOW_ChannelGetConnectedFilters');
+ @BASS_DSHOW_ChannelSetTextOverlay :=GetProcAddress(handle, 'BASS_DSHOW_ChannelSetTextOverlay');
+ @BASS_DSHOW_ChannelAddWindow :=GetProcAddress(handle, 'BASS_DSHOW_ChannelAddWindow');
+
+ @BASS_DSHOW_DVDSetOption:=GetProcAddress(handle, 'BASS_DSHOW_DVDSetOption');
+ @BASS_DSHOW_SetConfig :=GetProcAddress(handle, 'BASS_DSHOW_SetConfig');
+ @BASS_DSHOW_ErrorGetCode:=GetProcAddress(handle, 'BASS_DSHOW_ErrorGetCode');
+ @BASS_DSHOW_LoadPlugin :=GetProcAddress(handle, 'BASS_DSHOW_LoadPlugin');
+ @BASS_DSHOW_LoadPlugin2 :=GetProcAddress(handle, 'BASS_DSHOW_LoadPlugin2');
+ @BASS_DSHOW_GetVersion :=GetProcAddress(handle, 'BASS_DSHOW_GetVersion');
+
+ @BASS_DSHOW_ShowFilterPropertyPage:=GetProcAddress(handle, 'BASS_DSHOW_ShowFilterPropertyPage');
+ @BASS_DSHOW_MIX_StreamCreateFile:=GetProcAddress(handle, 'BASS_DSHOW_MIX_StreamCreateFile');
+ @BASS_DSHOW_MIX_ChanOptions :=GetProcAddress(handle, 'BASS_DSHOW_MIX_ChanOptions');
+
+ @BASS_DSHOW_Encode_GetCodecs :=GetProcAddress(handle, 'BASS_DSHOW_Encode_GetCodecs');
+ @BASS_DSHOW_Encode_GetProfiles :=GetProcAddress(handle, 'BASS_DSHOW_Encode_GetProfiles');
+ @BASS_DSHOW_Encode_StreamCreate:=GetProcAddress(handle, 'BASS_DSHOW_Encode_StreamCreate');
+ @BASS_DSHOW_Encode_Start :=GetProcAddress(handle, 'BASS_DSHOW_Encode_Start');
+ @BASS_DSHOW_Encode_Stop :=GetProcAddress(handle, 'BASS_DSHOW_Encode_Stop');
+ @BASS_DSHOW_Encode_GetPosition :=GetProcAddress(handle, 'BASS_DSHOW_Encode_GetPosition');
+ @BASS_DSHOW_Encode_SetEncoder :=GetProcAddress(handle, 'BASS_DSHOW_Encode_SetEncoder');
+
+ @BASS_DSHOW_Record_GetDevices:=GetProcAddress(handle, 'BASS_DSHOW_Record_GetDevices');
+ @BASS_DSHOW_RecordStart :=GetProcAddress(handle, 'BASS_DSHOW_RecordStart');
+ @BASS_DSHOW_RecordFree :=GetProcAddress(handle, 'BASS_DSHOW_RecordFree');
+
+end;
+
+const
+ DSHOW_Handle:THANDLE = 0;
+ from:integer = 0;
+
+function InitDSHOW:bool;
+var
+ info:PBASS_PLUGININFO;
+ i:dword;
+ pHPlugin:^HPLUGIN;
+begin
+ if DSHOW_Handle<>0 then
+ begin
+ result:=true;
+ exit;
+ end;
+ result:=false;
+ pHPlugin:=pointer(BASS_PluginGetInfo(0));
+ if pHPlugin=nil then exit;
+ while pHPlugin^<>0 do
+ begin
+ info:=BASS_PluginGetInfo(pHPlugin^);
+ i:=0;
+ while i<info^.formatc do
+ begin
+//!! if info^.formats^[i].ctype=BASS_CTYPE_STREAM_WMA then
+ begin
+ DSHOW_Handle:=pHPlugin^;
+ SetProcs(pHPlugin^);
+ from:=2;
+ result:=true;
+ exit;
+ end;
+ inc(i);
+ end;
+ inc(pHPlugin);
+ end;
+end;
+
+var
+ mDSHOW:tBASSRegRec;
+
+procedure Init;
+begin
+ mDSHOW.Next:=BASSRegRec;
+ mDSHOW.Init:=@InitDSHOW;
+ BASSRegRec:=@mDSHOW;
+end;
+
+begin
+ Init;
+end.
diff --git a/plugins/Libs/Dynamic_Bass.pas b/plugins/Libs/Dynamic_Bass.pas
new file mode 100644
index 0000000000..235ced05e6
--- /dev/null
+++ b/plugins/Libs/Dynamic_Bass.pas
@@ -0,0 +1,1298 @@
+{.$DEFINE CHECK_PROC}
+{
+ BASS 2.4 Delphi unit (dynamic)
+ Copyright (c) 1999-2008 Un4seen Developments Ltd.
+
+ See the BASS.CHM file for more detailed documentation
+
+ How to install
+ ----------------
+ Copy DYNAMIC_BASS.PAS to the \LIB subdirectory of your Delphi path or your project dir
+
+ Call Load_BASSDLL (eg. in FormCreate) to load BASS before using any functions, and
+ Unload_BASSDLL (eg. in FormDestory) to unload it when you're done.
+}
+
+unit Dynamic_Bass;
+
+interface
+
+uses
+ Windows;
+
+type
+ pBASSRegRec = ^tBASSRegRec;
+ tBASSRegRec = record
+ next: pBASSRegRec;
+ Init: function: bool;
+ end;
+const
+ BASSRegRec:pBASSRegRec = nil;
+
+const
+ BASSVERSION = $204; // API version
+ BASSVERSIONTEXT = '2.4';
+
+ // Use these to test for error from functions that return a DWORD or QWORD
+ DW_ERROR = Cardinal(-1); // -1 (DWORD)
+ QW_ERROR = Int64(-1); // -1 (QWORD)
+
+ // Error codes returned by BASS_ErrorGetCode()
+ BASS_OK = 0; // all is OK
+ BASS_ERROR_MEM = 1; // memory error
+ BASS_ERROR_FILEOPEN = 2; // can't open the file
+ BASS_ERROR_DRIVER = 3; // can't find a free sound driver
+ BASS_ERROR_BUFLOST = 4; // the sample buffer was lost
+ BASS_ERROR_HANDLE = 5; // invalid handle
+ BASS_ERROR_FORMAT = 6; // unsupported sample format
+ BASS_ERROR_POSITION = 7; // invalid position
+ BASS_ERROR_INIT = 8; // BASS_Init has not been successfully called
+ BASS_ERROR_START = 9; // BASS_Start has not been successfully called
+
+ BASS_ERROR_ALREADY = 14; // already initialized/paused/whatever
+
+ BASS_ERROR_NOCHAN = 18; // can't get a free channel
+ BASS_ERROR_ILLTYPE = 19; // an illegal type was specified
+ BASS_ERROR_ILLPARAM = 20; // an illegal parameter was specified
+ BASS_ERROR_NO3D = 21; // no 3D support
+ BASS_ERROR_NOEAX = 22; // no EAX support
+ BASS_ERROR_DEVICE = 23; // illegal device number
+ BASS_ERROR_NOPLAY = 24; // not playing
+ BASS_ERROR_FREQ = 25; // illegal sample rate
+
+ BASS_ERROR_NOTFILE = 27; // the stream is not a file stream
+
+ BASS_ERROR_NOHW = 29; // no hardware voices available
+
+ BASS_ERROR_EMPTY = 31; // the MOD music has no sequence data
+ BASS_ERROR_NONET = 32; // no internet connection could be opened
+ BASS_ERROR_CREATE = 33; // couldn't create the file
+ BASS_ERROR_NOFX = 34; // effects are not enabled
+
+ BASS_ERROR_NOTAVAIL = 37; // requested data is not available
+ BASS_ERROR_DECODE = 38; // the channel is a "decoding channel"
+ BASS_ERROR_DX = 39; // a sufficient DirectX version is not installed
+ BASS_ERROR_TIMEOUT = 40; // connection timedout
+ BASS_ERROR_FILEFORM = 41; // unsupported file format
+ BASS_ERROR_SPEAKER = 42; // unavailable speaker
+ BASS_ERROR_VERSION = 43; // invalid BASS version (used by add-ons)
+ BASS_ERROR_CODEC = 44; // codec is not available/supported
+ BASS_ERROR_ENDED = 45; // the channel/file has ended
+ BASS_ERROR_BUSY = 46; // the device is busy
+ BASS_ERROR_UNKNOWN = -1; // some other mystery problem
+
+ BASS_ERROR_MAXNUMBER = 46; // custom, just to have max error number
+
+const
+ BASS_ERRORS: array [0..46] of pAnsiChar = (
+ {BASS_OK } 'all is OK',
+ {BASS_ERROR_MEM } 'memory error',
+ {BASS_ERROR_FILEOPEN} 'can''t open the file',
+ {BASS_ERROR_DRIVER } 'can''t find a free sound driver',
+ {BASS_ERROR_BUFLOST } 'the sample buffer was lost',
+ {BASS_ERROR_HANDLE } 'invalid handle',
+ {BASS_ERROR_FORMAT } 'unsupported sample format',
+ {BASS_ERROR_POSITION} 'invalid position',
+ {BASS_ERROR_INIT } 'BASS_Init has not been successfully called',
+ {BASS_ERROR_START } 'BASS_Start has not been successfully called',
+ nil,
+ nil,
+ nil,
+ nil,
+ {BASS_ERROR_ALREADY } 'already initialized/paused/whatever',
+ nil,
+ nil,
+ nil,
+ {BASS_ERROR_NOCHAN } 'can''t get a free channel',
+ {BASS_ERROR_ILLTYPE } 'an illegal type was specified',
+ {BASS_ERROR_ILLPARAM} 'an illegal parameter was specified',
+ {BASS_ERROR_NO3D } 'no 3D support',
+ {BASS_ERROR_NOEAX } 'no EAX support',
+ {BASS_ERROR_DEVICE } 'illegal device number',
+ {BASS_ERROR_NOPLAY } 'not playing',
+ {BASS_ERROR_FREQ } 'illegal sample rate',
+ nil,
+ {BASS_ERROR_NOTFILE } 'the stream is not a file stream',
+ nil,
+ {BASS_ERROR_NOHW } 'no hardware voices available',
+ nil,
+ {BASS_ERROR_EMPTY } 'the MOD music has no sequence data',
+ {BASS_ERROR_NONET } 'no internet connection could be opened',
+ {BASS_ERROR_CREATE } 'couldn''t create the file',
+ {BASS_ERROR_NOFX } 'effects are not enabled',
+ nil,
+ nil,
+ {BASS_ERROR_NOTAVAIL} 'requested data is not available',
+ {BASS_ERROR_DECODE } 'the channel is a "decoding channel"',
+ {BASS_ERROR_DX } 'a sufficient DirectX version is not installed',
+ {BASS_ERROR_TIMEOUT } 'connection timedout',
+ {BASS_ERROR_FILEFORM} 'unsupported file format',
+ {BASS_ERROR_SPEAKER } 'unavailable speaker',
+ {BASS_ERROR_VERSION } 'invalid BASS version (used by add-ons)',
+ {BASS_ERROR_CODEC } 'codec is not available/supported',
+ {BASS_ERROR_ENDED } 'the channel/file has ended',
+ {BASS_ERROR_BUSY } 'the device is busy');
+
+ // BASS_SetConfig options
+ BASS_CONFIG_BUFFER = 0;
+ BASS_CONFIG_UPDATEPERIOD = 1;
+ BASS_CONFIG_GVOL_SAMPLE = 4;
+ BASS_CONFIG_GVOL_STREAM = 5;
+ BASS_CONFIG_GVOL_MUSIC = 6;
+ BASS_CONFIG_CURVE_VOL = 7;
+ BASS_CONFIG_CURVE_PAN = 8;
+ BASS_CONFIG_FLOATDSP = 9;
+ BASS_CONFIG_3DALGORITHM = 10;
+ BASS_CONFIG_NET_TIMEOUT = 11;
+ BASS_CONFIG_NET_BUFFER = 12;
+ BASS_CONFIG_PAUSE_NOPLAY = 13;
+ BASS_CONFIG_NET_PREBUF = 15;
+ BASS_CONFIG_NET_PASSIVE = 18;
+ BASS_CONFIG_REC_BUFFER = 19;
+ BASS_CONFIG_NET_PLAYLIST = 21;
+ BASS_CONFIG_MUSIC_VIRTUAL = 22;
+ BASS_CONFIG_VERIFY = 23;
+ BASS_CONFIG_UPDATETHREADS = 24;
+ BASS_CONFIG_DEV_BUFFER = 27;
+ BASS_CONFIG_DEV_DEFAULT = 36;
+ BASS_CONFIG_NET_READTIMEOUT = 37;
+ BASS_CONFIG_VISTA_SPEAKERS = 38;
+ BASS_CONFIG_IOS_SPEAKER = 39;
+ BASS_CONFIG_HANDLES = 41;
+ BASS_CONFIG_UNICODE = 42;
+ BASS_CONFIG_SRC = 43;
+ BASS_CONFIG_SRC_SAMPLE = 44;
+
+ // BASS_SetConfigPtr options
+ BASS_CONFIG_NET_AGENT = 16;
+ BASS_CONFIG_NET_PROXY = 17;
+
+ // Initialization flags
+ BASS_DEVICE_8BITS = 1; // use 8 bit resolution, else 16 bit
+ BASS_DEVICE_MONO = 2; // use mono, else stereo
+ BASS_DEVICE_3D = 4; // enable 3D functionality
+ BASS_DEVICE_LATENCY = $100; // calculate device latency (BASS_INFO struct)
+ BASS_DEVICE_CPSPEAKERS = $400; // detect speakers via Windows control panel
+ BASS_DEVICE_SPEAKERS = $800; // force enabling of speaker assignment
+ BASS_DEVICE_NOSPEAKER = $1000; // ignore speaker arrangement
+ BASS_DEVICE_DMIX = $2000; // use ALSA "dmix" plugin
+ BASS_DEVICE_FREQ = $4000; // set device sample rate
+
+ // DirectSound interfaces (for use with BASS_GetDSoundObject)
+ BASS_OBJECT_DS = 1; // IDirectSound
+ BASS_OBJECT_DS3DL = 2; // IDirectSound3DListener
+
+ // BASS_DEVICEINFO flags
+ BASS_DEVICE_ENABLED = 1;
+ BASS_DEVICE_DEFAULT = 2;
+ BASS_DEVICE_INIT = 4;
+
+ // BASS_INFO flags (from DSOUND.H)
+ DSCAPS_CONTINUOUSRATE = $00000010; // supports all sample rates between min/maxrate
+ DSCAPS_EMULDRIVER = $00000020; // device does NOT have hardware DirectSound support
+ DSCAPS_CERTIFIED = $00000040; // device driver has been certified by Microsoft
+ DSCAPS_SECONDARYMONO = $00000100; // mono
+ DSCAPS_SECONDARYSTEREO = $00000200; // stereo
+ DSCAPS_SECONDARY8BIT = $00000400; // 8 bit
+ DSCAPS_SECONDARY16BIT = $00000800; // 16 bit
+
+ // BASS_RECORDINFO flags (from DSOUND.H)
+ DSCCAPS_EMULDRIVER = DSCAPS_EMULDRIVER; // device does NOT have hardware DirectSound recording support
+ DSCCAPS_CERTIFIED = DSCAPS_CERTIFIED; // device driver has been certified by Microsoft
+
+ // defines for formats field of BASS_RECORDINFO (from MMSYSTEM.H)
+ WAVE_FORMAT_1M08 = $00000001; // 11.025 kHz, Mono, 8-bit
+ WAVE_FORMAT_1S08 = $00000002; // 11.025 kHz, Stereo, 8-bit
+ WAVE_FORMAT_1M16 = $00000004; // 11.025 kHz, Mono, 16-bit
+ WAVE_FORMAT_1S16 = $00000008; // 11.025 kHz, Stereo, 16-bit
+ WAVE_FORMAT_2M08 = $00000010; // 22.05 kHz, Mono, 8-bit
+ WAVE_FORMAT_2S08 = $00000020; // 22.05 kHz, Stereo, 8-bit
+ WAVE_FORMAT_2M16 = $00000040; // 22.05 kHz, Mono, 16-bit
+ WAVE_FORMAT_2S16 = $00000080; // 22.05 kHz, Stereo, 16-bit
+ WAVE_FORMAT_4M08 = $00000100; // 44.1 kHz, Mono, 8-bit
+ WAVE_FORMAT_4S08 = $00000200; // 44.1 kHz, Stereo, 8-bit
+ WAVE_FORMAT_4M16 = $00000400; // 44.1 kHz, Mono, 16-bit
+ WAVE_FORMAT_4S16 = $00000800; // 44.1 kHz, Stereo, 16-bit
+
+ BASS_SAMPLE_8BITS = 1; // 8 bit
+ BASS_SAMPLE_FLOAT = 256; // 32-bit floating-point
+ BASS_SAMPLE_MONO = 2; // mono
+ BASS_SAMPLE_LOOP = 4; // looped
+ BASS_SAMPLE_3D = 8; // 3D functionality
+ BASS_SAMPLE_SOFTWARE = 16; // not using hardware mixing
+ BASS_SAMPLE_MUTEMAX = 32; // mute at max distance (3D only)
+ BASS_SAMPLE_VAM = 64; // DX7 voice allocation & management
+ BASS_SAMPLE_FX = 128; // old implementation of DX8 effects
+ BASS_SAMPLE_OVER_VOL = $10000; // override lowest volume
+ BASS_SAMPLE_OVER_POS = $20000; // override longest playing
+ BASS_SAMPLE_OVER_DIST = $30000; // override furthest from listener (3D only)
+
+ BASS_STREAM_PRESCAN = $20000; // enable pin-point seeking/length (MP3/MP2/MP1)
+ BASS_MP3_SETPOS = BASS_STREAM_PRESCAN;
+ BASS_STREAM_AUTOFREE = $40000; // automatically free the stream when it stop/ends
+ BASS_STREAM_RESTRATE = $80000; // restrict the download rate of internet file streams
+ BASS_STREAM_BLOCK = $100000;// download/play internet file stream in small blocks
+ BASS_STREAM_DECODE = $200000;// don't play the stream, only decode (BASS_ChannelGetData)
+ BASS_STREAM_STATUS = $800000;// give server status info (HTTP/ICY tags) in DOWNLOADPROC
+
+ BASS_MUSIC_FLOAT = BASS_SAMPLE_FLOAT;
+ BASS_MUSIC_MONO = BASS_SAMPLE_MONO;
+ BASS_MUSIC_LOOP = BASS_SAMPLE_LOOP;
+ BASS_MUSIC_3D = BASS_SAMPLE_3D;
+ BASS_MUSIC_FX = BASS_SAMPLE_FX;
+ BASS_MUSIC_AUTOFREE = BASS_STREAM_AUTOFREE;
+ BASS_MUSIC_DECODE = BASS_STREAM_DECODE;
+ BASS_MUSIC_PRESCAN = BASS_STREAM_PRESCAN; // calculate playback length
+ BASS_MUSIC_CALCLEN = BASS_MUSIC_PRESCAN;
+ BASS_MUSIC_RAMP = $200; // normal ramping
+ BASS_MUSIC_RAMPS = $400; // sensitive ramping
+ BASS_MUSIC_SURROUND = $800; // surround sound
+ BASS_MUSIC_SURROUND2 = $1000; // surround sound (mode 2)
+ BASS_MUSIC_FT2MOD = $2000; // play .MOD as FastTracker 2 does
+ BASS_MUSIC_PT1MOD = $4000; // play .MOD as ProTracker 1 does
+ BASS_MUSIC_NONINTER = $10000; // non-interpolated sample mixing
+ BASS_MUSIC_SINCINTER = $800000; // sinc interpolated sample mixing
+ BASS_MUSIC_POSRESET = $8000; // stop all notes when moving position
+ BASS_MUSIC_POSRESETEX = $400000; // stop all notes and reset bmp/etc when moving position
+ BASS_MUSIC_STOPBACK = $80000; // stop the music on a backwards jump effect
+ BASS_MUSIC_NOSAMPLE = $100000; // don't load the samples
+
+ // Speaker assignment flags
+ BASS_SPEAKER_FRONT = $1000000; // front speakers
+ BASS_SPEAKER_REAR = $2000000; // rear/side speakers
+ BASS_SPEAKER_CENLFE = $3000000; // center & LFE speakers (5.1)
+ BASS_SPEAKER_REAR2 = $4000000; // rear center speakers (7.1)
+ BASS_SPEAKER_LEFT = $10000000; // modifier: left
+ BASS_SPEAKER_RIGHT = $20000000; // modifier: right
+ BASS_SPEAKER_FRONTLEFT = BASS_SPEAKER_FRONT or BASS_SPEAKER_LEFT;
+ BASS_SPEAKER_FRONTRIGHT = BASS_SPEAKER_FRONT or BASS_SPEAKER_RIGHT;
+ BASS_SPEAKER_REARLEFT = BASS_SPEAKER_REAR or BASS_SPEAKER_LEFT;
+ BASS_SPEAKER_REARRIGHT = BASS_SPEAKER_REAR or BASS_SPEAKER_RIGHT;
+ BASS_SPEAKER_CENTER = BASS_SPEAKER_CENLFE or BASS_SPEAKER_LEFT;
+ BASS_SPEAKER_LFE = BASS_SPEAKER_CENLFE or BASS_SPEAKER_RIGHT;
+ BASS_SPEAKER_REAR2LEFT = BASS_SPEAKER_REAR2 or BASS_SPEAKER_LEFT;
+ BASS_SPEAKER_REAR2RIGHT = BASS_SPEAKER_REAR2 or BASS_SPEAKER_RIGHT;
+
+ BASS_UNICODE = $80000000;
+
+ BASS_RECORD_PAUSE = $8000; // start recording paused
+
+ // DX7 voice allocation & management flags
+ BASS_VAM_HARDWARE = 1;
+ BASS_VAM_SOFTWARE = 2;
+ BASS_VAM_TERM_TIME = 4;
+ BASS_VAM_TERM_DIST = 8;
+ BASS_VAM_TERM_PRIO = 16;
+
+ // BASS_CHANNELINFO types
+ BASS_CTYPE_SAMPLE = 1;
+ BASS_CTYPE_RECORD = 2;
+ BASS_CTYPE_STREAM = $10000;
+ BASS_CTYPE_STREAM_OGG = $10002;
+ BASS_CTYPE_STREAM_MP1 = $10003;
+ BASS_CTYPE_STREAM_MP2 = $10004;
+ BASS_CTYPE_STREAM_MP3 = $10005;
+ BASS_CTYPE_STREAM_AIFF = $10006;
+ BASS_CTYPE_STREAM_WAV = $40000; // WAVE flag, LOWORD=codec
+ BASS_CTYPE_STREAM_WAV_PCM = $50001;
+ BASS_CTYPE_STREAM_WAV_FLOAT = $50003;
+ BASS_CTYPE_MUSIC_MOD = $20000;
+ BASS_CTYPE_MUSIC_MTM = $20001;
+ BASS_CTYPE_MUSIC_S3M = $20002;
+ BASS_CTYPE_MUSIC_XM = $20003;
+ BASS_CTYPE_MUSIC_IT = $20004;
+ BASS_CTYPE_MUSIC_MO3 = $00100; // MO3 flag
+
+ // 3D channel modes
+ BASS_3DMODE_NORMAL = 0; // normal 3D processing
+ BASS_3DMODE_RELATIVE = 1; // position is relative to the listener
+ BASS_3DMODE_OFF = 2; // no 3D processing
+
+ // software 3D mixing algorithms (used with BASS_CONFIG_3DALGORITHM)
+ BASS_3DALG_DEFAULT = 0;
+ BASS_3DALG_OFF = 1;
+ BASS_3DALG_FULL = 2;
+ BASS_3DALG_LIGHT = 3;
+
+ // EAX environments, use with BASS_SetEAXParameters
+ EAX_ENVIRONMENT_GENERIC = 0;
+ EAX_ENVIRONMENT_PADDEDCELL = 1;
+ EAX_ENVIRONMENT_ROOM = 2;
+ EAX_ENVIRONMENT_BATHROOM = 3;
+ EAX_ENVIRONMENT_LIVINGROOM = 4;
+ EAX_ENVIRONMENT_STONEROOM = 5;
+ EAX_ENVIRONMENT_AUDITORIUM = 6;
+ EAX_ENVIRONMENT_CONCERTHALL = 7;
+ EAX_ENVIRONMENT_CAVE = 8;
+ EAX_ENVIRONMENT_ARENA = 9;
+ EAX_ENVIRONMENT_HANGAR = 10;
+ EAX_ENVIRONMENT_CARPETEDHALLWAY = 11;
+ EAX_ENVIRONMENT_HALLWAY = 12;
+ EAX_ENVIRONMENT_STONECORRIDOR = 13;
+ EAX_ENVIRONMENT_ALLEY = 14;
+ EAX_ENVIRONMENT_FOREST = 15;
+ EAX_ENVIRONMENT_CITY = 16;
+ EAX_ENVIRONMENT_MOUNTAINS = 17;
+ EAX_ENVIRONMENT_QUARRY = 18;
+ EAX_ENVIRONMENT_PLAIN = 19;
+ EAX_ENVIRONMENT_PARKINGLOT = 20;
+ EAX_ENVIRONMENT_SEWERPIPE = 21;
+ EAX_ENVIRONMENT_UNDERWATER = 22;
+ EAX_ENVIRONMENT_DRUGGED = 23;
+ EAX_ENVIRONMENT_DIZZY = 24;
+ EAX_ENVIRONMENT_PSYCHOTIC = 25;
+ // total number of environments
+ EAX_ENVIRONMENT_COUNT = 26;
+
+ BASS_STREAMPROC_END = $80000000; // end of user stream flag
+
+
+ // BASS_StreamCreateFileUser file systems
+ STREAMFILE_NOBUFFER = 0;
+ STREAMFILE_BUFFER = 1;
+ STREAMFILE_BUFFERPUSH = 2;
+
+ // BASS_StreamPutFileData options
+ BASS_FILEDATA_END = 0; // end & close the file
+
+ // BASS_StreamGetFilePosition modes
+ BASS_FILEPOS_CURRENT = 0;
+ BASS_FILEPOS_DECODE = BASS_FILEPOS_CURRENT;
+ BASS_FILEPOS_DOWNLOAD = 1;
+ BASS_FILEPOS_END = 2;
+ BASS_FILEPOS_START = 3;
+ BASS_FILEPOS_CONNECTED = 4;
+ BASS_FILEPOS_BUFFER = 5;
+
+ // BASS_ChannelSetSync types
+ BASS_SYNC_POS = 0;
+ BASS_SYNC_END = 2;
+ BASS_SYNC_META = 4;
+ BASS_SYNC_SLIDE = 5;
+ BASS_SYNC_STALL = 6;
+ BASS_SYNC_DOWNLOAD = 7;
+ BASS_SYNC_FREE = 8;
+ BASS_SYNC_SETPOS = 11;
+ BASS_SYNC_MUSICPOS = 10;
+ BASS_SYNC_MUSICINST = 1;
+ BASS_SYNC_MUSICFX = 3;
+ BASS_SYNC_OGG_CHANGE = 12;
+ BASS_SYNC_MIXTIME = $40000000; // FLAG: sync at mixtime, else at playtime
+ BASS_SYNC_ONETIME = $80000000; // FLAG: sync only once, else continuously
+
+ // BASS_ChannelIsActive return values
+ BASS_ACTIVE_STOPPED = 0;
+ BASS_ACTIVE_PLAYING = 1;
+ BASS_ACTIVE_STALLED = 2;
+ BASS_ACTIVE_PAUSED = 3;
+
+ // Channel attributes
+ BASS_ATTRIB_FREQ = 1;
+ BASS_ATTRIB_VOL = 2;
+ BASS_ATTRIB_PAN = 3;
+ BASS_ATTRIB_EAXMIX = 4;
+ BASS_ATTRIB_NOBUFFER = 5;
+ BASS_ATTRIB_CPU = 7;
+ BASS_ATTRIB_SRC = 8;
+ BASS_ATTRIB_MUSIC_AMPLIFY = $100;
+ BASS_ATTRIB_MUSIC_PANSEP = $101;
+ BASS_ATTRIB_MUSIC_PSCALER = $102;
+ BASS_ATTRIB_MUSIC_BPM = $103;
+ BASS_ATTRIB_MUSIC_SPEED = $104;
+ BASS_ATTRIB_MUSIC_VOL_GLOBAL = $105;
+ BASS_ATTRIB_MUSIC_VOL_CHAN = $200; // + channel #
+ BASS_ATTRIB_MUSIC_VOL_INST = $300; // + instrument #
+
+ // BASS_ChannelGetData flags
+ BASS_DATA_AVAILABLE = 0; // query how much data is buffered
+ BASS_DATA_FLOAT = $40000000; // flag: return floating-point sample data
+ BASS_DATA_FFT256 = $80000000; // 256 sample FFT
+ BASS_DATA_FFT512 = $80000001; // 512 FFT
+ BASS_DATA_FFT1024 = $80000002; // 1024 FFT
+ BASS_DATA_FFT2048 = $80000003; // 2048 FFT
+ BASS_DATA_FFT4096 = $80000004; // 4096 FFT
+ BASS_DATA_FFT8192 = $80000005; // 8192 FFT
+ BASS_DATA_FFT16384 = $80000006; // 16384 FFT
+ BASS_DATA_FFT_INDIVIDUAL = $10; // FFT flag: FFT for each channel, else all combined
+ BASS_DATA_FFT_NOWINDOW = $20; // FFT flag: no Hanning window
+ BASS_DATA_FFT_REMOVEDC = $40; // FFT flag: pre-remove DC bias
+
+
+ // BASS_ChannelGetTags types : what's returned
+ BASS_TAG_ID3 = 0; // ID3v1 tags : TAG_ID3 structure
+ BASS_TAG_ID3V2 = 1; // ID3v2 tags : variable length block
+ BASS_TAG_OGG = 2; // OGG comments : series of null-terminated UTF-8 strings
+ BASS_TAG_HTTP = 3; // HTTP headers : series of null-terminated ANSI strings
+ BASS_TAG_ICY = 4; // ICY headers : series of null-terminated ANSI strings
+ BASS_TAG_META = 5; // ICY metadata : ANSI string
+ BASS_TAG_APE = 6; // APEv2 tags : series of null-terminated UTF-8 strings
+ BASS_TAG_MP4 = 7; // MP4/iTunes metadata : series of null-terminated UTF-8 strings
+ BASS_TAG_VENDOR = 9; // OGG encoder : UTF-8 string
+ BASS_TAG_LYRICS3 = 10; // Lyric3v2 tag : ASCII string
+ BASS_TAG_CA_CODEC = 11; // CoreAudio codec info : TAG_CA_CODEC structure
+ BASS_TAG_MF = 13; // Media Foundation tags : series of null-terminated UTF-8 strings
+ BASS_TAG_WAVEFORMAT = 14; // WAVE format : WAVEFORMATEEX structure
+ BASS_TAG_RIFF_INFO = $100; // RIFF "INFO" tags : series of null-terminated ANSI strings
+ BASS_TAG_RIFF_BEXT = $101; // RIFF/BWF "bext" tags : TAG_BEXT structure
+ BASS_TAG_RIFF_CART = $102; // RIFF/BWF "cart" tags : TAG_CART structure
+ BASS_TAG_RIFF_DISP = $103; // RIFF "DISP" text tag : ANSI string
+ BASS_TAG_APE_BINARY = $1000; // + index #, binary APEv2 tag : TAG_APE_BINARY structure
+ BASS_TAG_MUSIC_NAME = $10000; // MOD music name : ANSI string
+ BASS_TAG_MUSIC_MESSAGE = $10001; // MOD message : ANSI string
+ BASS_TAG_MUSIC_INST = $10100; // + instrument #, MOD instrument name : ANSI string
+ BASS_TAG_MUSIC_SAMPLE = $10300; // + sample #, MOD sample name : ANSI string
+
+ // BASS_ChannelGetLength/GetPosition/SetPosition modes
+ BASS_POS_BYTE = 0; // byte position
+ BASS_POS_MUSIC_ORDER = 1; // order.row position, MAKELONG(order,row)
+ BASS_POS_DECODE = $10000000; // flag: get the decoding (not playing) position
+ BASS_POS_DECODETO = $20000000; // flag: decode to the position instead of seeking
+
+ // BASS_RecordSetInput flags
+ BASS_INPUT_OFF = $10000;
+ BASS_INPUT_ON = $20000;
+
+ BASS_INPUT_TYPE_MASK = $FF000000;
+ BASS_INPUT_TYPE_UNDEF = $00000000;
+ BASS_INPUT_TYPE_DIGITAL = $01000000;
+ BASS_INPUT_TYPE_LINE = $02000000;
+ BASS_INPUT_TYPE_MIC = $03000000;
+ BASS_INPUT_TYPE_SYNTH = $04000000;
+ BASS_INPUT_TYPE_CD = $05000000;
+ BASS_INPUT_TYPE_PHONE = $06000000;
+ BASS_INPUT_TYPE_SPEAKER = $07000000;
+ BASS_INPUT_TYPE_WAVE = $08000000;
+ BASS_INPUT_TYPE_AUX = $09000000;
+ BASS_INPUT_TYPE_ANALOG = $0A000000;
+
+ BASS_FX_DX8_CHORUS = 0;
+ BASS_FX_DX8_COMPRESSOR = 1;
+ BASS_FX_DX8_DISTORTION = 2;
+ BASS_FX_DX8_ECHO = 3;
+ BASS_FX_DX8_FLANGER = 4;
+ BASS_FX_DX8_GARGLE = 5;
+ BASS_FX_DX8_I3DL2REVERB = 6;
+ BASS_FX_DX8_PARAMEQ = 7;
+ BASS_FX_DX8_REVERB = 8;
+
+ BASS_DX8_PHASE_NEG_180 = 0;
+ BASS_DX8_PHASE_NEG_90 = 1;
+ BASS_DX8_PHASE_ZERO = 2;
+ BASS_DX8_PHASE_90 = 3;
+ BASS_DX8_PHASE_180 = 4;
+
+type
+ DWORD = cardinal;
+ BOOL = LongBool;
+ FLOAT = Single;
+ QWORD = int64; // 64-bit (replace "int64" with "comp" if using Delphi 3)
+
+ HMUSIC = DWORD; // MOD music handle
+ HSAMPLE = DWORD; // sample handle
+ HCHANNEL = DWORD; // playing sample's channel handle
+ HSTREAM = DWORD; // sample stream handle
+ HRECORD = DWORD; // recording handle
+ HSYNC = DWORD; // synchronizer handle
+ HDSP = DWORD; // DSP handle
+ HFX = DWORD; // DX8 effect handle
+ HPLUGIN = DWORD; // Plugin handle
+
+ // Device info structure
+ BASS_DEVICEINFO = record
+ name : PAnsiChar; // description
+ driver: PAnsiChar; // driver
+ flags : DWORD;
+ end;
+
+ BASS_INFO = record
+ flags : DWORD; // device capabilities (DSCAPS_xxx flags)
+ hwsize : DWORD; // size of total device hardware memory
+ hwfree : DWORD; // size of free device hardware memory
+ freesam : DWORD; // number of free sample slots in the hardware
+ free3d : DWORD; // number of free 3D sample slots in the hardware
+ minrate : DWORD; // min sample rate supported by the hardware
+ maxrate : DWORD; // max sample rate supported by the hardware
+ eax : BOOL; // device supports EAX? (always FALSE if BASS_DEVICE_3D was not used)
+ minbuf : DWORD; // recommended minimum buffer length in ms (requires BASS_DEVICE_LATENCY)
+ dsver : DWORD; // DirectSound version
+ latency : DWORD; // delay (in ms) before start of playback (requires BASS_DEVICE_LATENCY)
+ initflags: DWORD; // BASS_Init "flags" parameter
+ speakers : DWORD; // number of speakers available
+ freq : DWORD; // current output rate
+ end;
+
+ // Recording device info structure
+ BASS_RECORDINFO = record
+ flags : DWORD; // device capabilities (DSCCAPS_xxx flags)
+ formats : DWORD; // supported standard formats (WAVE_FORMAT_xxx flags)
+ inputs : DWORD; // number of inputs
+ singlein: BOOL; // only 1 input can be set at a time
+ freq : DWORD; // current input rate
+ end;
+
+ // Sample info structure
+ BASS_SAMPLE = record
+ freq : DWORD; // default playback rate
+ volume : FLOAT; // default volume (0-100)
+ pan : FLOAT; // default pan (-100=left, 0=middle, 100=right)
+ flags : DWORD; // BASS_SAMPLE_xxx flags
+ length : DWORD; // length (in samples, not bytes)
+ max : DWORD; // maximum simultaneous playbacks
+ origres : DWORD; // original resolution
+ chans : DWORD; // number of channels
+ mingap : DWORD; // minimum gap (ms) between creating channels
+ mode3d : DWORD; // BASS_3DMODE_xxx mode
+ mindist : FLOAT; // minimum distance
+ maxdist : FLOAT; // maximum distance
+ iangle : DWORD; // angle of inside projection cone
+ oangle : DWORD; // angle of outside projection cone
+ outvol : FLOAT; // delta-volume outside the projection cone
+ vam : DWORD; // voice allocation/management flags (BASS_VAM_xxx)
+ priority: DWORD; // priority (0=lowest, $ffffffff=highest)
+ end;
+
+ // Channel info structure
+ BASS_CHANNELINFO = record
+ freq : DWORD; // default playback rate
+ chans : DWORD; // channels
+ flags : DWORD; // BASS_SAMPLE/STREAM/MUSIC/SPEAKER flags
+ ctype : DWORD; // type of channel
+ origres : DWORD; // original resolution
+ plugin : HPLUGIN; // plugin
+ sample : HSAMPLE; // sample
+ {$IFDEF CPUX64}
+ padding: DWORD;
+ {$ENDIF}
+ filename: PAnsiChar; // filename
+ end;
+
+ BASS_PLUGINFORM = record
+ ctype: DWORD; // channel type
+ {$IFDEF CPUX64}
+ padding: DWORD;
+ {$ENDIF}
+ name : PAnsiChar; // format description
+ exts : PAnsiChar; // file extension filter (*.ext1;*.ext2;etc...)
+ end;
+ PBASS_PLUGINFORMS = ^TBASS_PLUGINFORMS;
+ TBASS_PLUGINFORMS = array[0..maxInt div sizeOf(BASS_PLUGINFORM) - 1] of BASS_PLUGINFORM;
+
+ BASS_PLUGININFO = record
+ version: DWORD; // version (same form as BASS_GetVersion)
+ formatc: DWORD; // number of formats
+ formats: PBASS_PLUGINFORMS; // the array of formats
+ end;
+ PBASS_PLUGININFO = ^BASS_PLUGININFO;
+
+ // 3D vector (for 3D positions/velocities/orientations)
+ BASS_3DVECTOR = record
+ x: FLOAT; // +=right, -=left
+ y: FLOAT; // +=up , -=down
+ z: FLOAT; // +=front, -=behind
+ end;
+
+ // User file stream callback functions
+ FILECLOSEPROC = procedure(user: Pointer); stdcall;
+ FILELENPROC = function(user: Pointer): QWORD; stdcall;
+ FILEREADPROC = function(buffer: Pointer; length: DWORD; user: Pointer): DWORD; stdcall;
+ FILESEEKPROC = function(offset: QWORD; user: Pointer): BOOL; stdcall;
+
+ BASS_FILEPROCS = record
+ close : FILECLOSEPROC;
+ length: FILELENPROC;
+ read : FILEREADPROC;
+ seek : FILESEEKPROC;
+ end;
+
+ // ID3v1 tag structure
+ TAG_ID3 = record
+ id : Array[0.. 2] of AnsiChar;
+ title : Array[0..29] of AnsiChar;
+ artist : Array[0..29] of AnsiChar;
+ album : Array[0..29] of AnsiChar;
+ year : Array[0.. 3] of AnsiChar;
+ comment: Array[0..29] of AnsiChar;
+ genre : Byte;
+ end;
+
+ // Binary APEv2 tag structure
+ TAG_APE_BINARY = record
+ key : PAnsiChar;
+ data : PAnsiChar;
+ length: DWORD;
+ end;
+
+ // BWF "bext" tag structure
+ TAG_BEXT = packed record
+ Description : Array[0..255] of AnsiChar; // description
+ Originator : Array[0.. 31] of AnsiChar; // name of the originator
+ OriginatorReference: Array[0.. 31] of AnsiChar; // reference of the originator
+ OriginationDate : Array[0.. 9] of AnsiChar; // date of creation (yyyy-mm-dd)
+ OriginationTime : Array[0.. 7] of AnsiChar; // time of creation (hh-mm-ss)
+ TimeReference : QWORD; // first sample count since midnight (little-endian)
+ Version : Word; // BWF version (little-endian)
+ UMID : Array[0.. 63] of Byte; // SMPTE UMID
+ Reserved : Array[0..189] of Byte;
+ CodingHistory : Array of AnsiChar; // history
+ end;
+
+ BASS_DX8_CHORUS = record
+ fWetDryMix: FLOAT;
+ fDepth : FLOAT;
+ fFeedback : FLOAT;
+ fFrequency: FLOAT;
+ lWaveform : DWORD; // 0=triangle, 1=sine
+ fDelay : FLOAT;
+ lPhase : DWORD; // BASS_DX8_PHASE_xxx
+ end;
+
+ BASS_DX8_COMPRESSOR = record
+ fGain : FLOAT;
+ fAttack : FLOAT;
+ fRelease : FLOAT;
+ fThreshold: FLOAT;
+ fRatio : FLOAT;
+ fPredelay : FLOAT;
+ end;
+
+ BASS_DX8_DISTORTION = record
+ fGain : FLOAT;
+ fEdge : FLOAT;
+ fPostEQCenterFrequency: FLOAT;
+ fPostEQBandwidth : FLOAT;
+ fPreLowpassCutoff : FLOAT;
+ end;
+
+ BASS_DX8_ECHO = record
+ fWetDryMix : FLOAT;
+ fFeedback : FLOAT;
+ fLeftDelay : FLOAT;
+ fRightDelay: FLOAT;
+ lPanDelay : BOOL;
+ end;
+
+ BASS_DX8_FLANGER = record
+ fWetDryMix: FLOAT;
+ fDepth : FLOAT;
+ fFeedback : FLOAT;
+ fFrequency: FLOAT;
+ lWaveform : DWORD; // 0=triangle, 1=sine
+ fDelay : FLOAT;
+ lPhase : DWORD; // BASS_DX8_PHASE_xxx
+ end;
+
+ BASS_DX8_GARGLE = record
+ dwRateHz : DWORD; // Rate of modulation in hz
+ dwWaveShape: DWORD; // 0=triangle, 1=square
+ end;
+
+ BASS_DX8_I3DL2REVERB = record
+ lRoom : Longint; // [-10000, 0 ] default: -1000 mB
+ lRoomHF : Longint; // [-10000, 0 ] default: 0 mB
+ flRoomRolloffFactor: FLOAT; // [0.0 , 10.0 ] default: 0.0
+ flDecayTime : FLOAT; // [0.1 , 20.0 ] default: 1.49s
+ flDecayHFRatio : FLOAT; // [0.1 , 2.0 ] default: 0.83
+ lReflections : Longint; // [-10000, 1000 ] default: -2602 mB
+ flReflectionsDelay : FLOAT; // [0.0 , 0.3 ] default: 0.007 s
+ lReverb : Longint; // [-10000, 2000 ] default: 200 mB
+ flReverbDelay : FLOAT; // [0.0 , 0.1 ] default: 0.011 s
+ flDiffusion : FLOAT; // [0.0 , 100.0 ] default: 100.0 %
+ flDensity : FLOAT; // [0.0 , 100.0 ] default: 100.0 %
+ flHFReference : FLOAT; // [20.0 , 20000.0] default: 5000.0 Hz
+ end;
+
+ BASS_DX8_PARAMEQ = record
+ fCenter : FLOAT;
+ fBandwidth: FLOAT;
+ fGain : FLOAT;
+ end;
+
+ BASS_DX8_REVERB = record
+ fInGain : FLOAT; // [-96.0,0.0 ] default: 0.0 dB
+ fReverbMix : FLOAT; // [-96.0,0.0 ] default: 0.0 db
+ fReverbTime : FLOAT; // [0.001,3000.0] default: 1000.0 ms
+ fHighFreqRTRatio: FLOAT; // [0.001,0.999 ] default: 0.001
+ end;
+
+ // callback function types
+ STREAMPROC = function(handle: HSTREAM; buffer: Pointer; length: DWORD; user: Pointer): DWORD; stdcall;
+ {
+ User stream callback function. NOTE: A stream function should obviously be as
+ quick as possible, other streams (and MOD musics) can't be mixed until
+ it's finished.
+ handle : The stream that needs writing
+ buffer : Buffer to write the samples in
+ length : Number of bytes to write
+ user : The 'user' parameter value given when calling BASS_StreamCreate
+ RETURN : Number of bytes written. Set the BASS_STREAMPROC_END flag to end
+ the stream.
+ }
+
+const
+ // special STREAMPROCs
+ STREAMPROC_DUMMY : pointer = pointer(0); // "dummy" stream
+ STREAMPROC_PUSH : pointer = pointer(-1); // push stream
+
+type
+
+ DOWNLOADPROC = procedure(buffer: Pointer; length: DWORD; user: Pointer); stdcall;
+ {
+ Internet stream download callback function.
+ buffer : Buffer containing the downloaded data... NULL=end of download
+ length : Number of bytes in the buffer
+ user : The 'user' parameter value given when calling BASS_StreamCreateURL
+ }
+
+ SYNCPROC = procedure(handle: HSYNC; channel, data: DWORD; user: Pointer); stdcall;
+ {
+ Sync callback function. NOTE: a sync callback function should be very
+ quick as other syncs cannot be processed until it has finished. If the
+ sync is a "mixtime" sync, then other streams and MOD musics can not be
+ mixed until it's finished either.
+ handle : The sync that has occured
+ channel: Channel that the sync occured in
+ data : Additional data associated with the sync's occurance
+ user : The 'user' parameter given when calling BASS_ChannelSetSync
+ }
+
+ DSPPROC = procedure(handle: HDSP; channel: DWORD; buffer: Pointer; length: DWORD; user: Pointer); stdcall;
+ {
+ DSP callback function. NOTE: A DSP function should obviously be as quick
+ as possible... other DSP functions, streams and MOD musics can not be
+ processed until it's finished.
+ handle : The DSP handle
+ channel: Channel that the DSP is being applied to
+ buffer : Buffer to apply the DSP to
+ length : Number of bytes in the buffer
+ user : The 'user' parameter given when calling BASS_ChannelSetDSP
+ }
+
+ RECORDPROC = function(handle: HRECORD; buffer: Pointer; length: DWORD; user: Pointer): BOOL; stdcall;
+ {
+ Recording callback function.
+ handle : The recording handle
+ buffer : Buffer containing the recorded sample data
+ length : Number of bytes
+ user : The 'user' parameter value given when calling BASS_RecordStart
+ RETURN : TRUE = continue recording, FALSE = stop
+ }
+
+
+// Vars that will hold our dynamically loaded functions...
+var BASS_SetConfig :function(option, value: DWORD): BOOL; stdcall;
+var BASS_GetConfig :function(option: DWORD): DWORD; stdcall;
+var BASS_SetConfigPtr :function(option: DWORD; value: Pointer): BOOL; stdcall;
+var BASS_GetConfigPtr :function(option: DWORD): Pointer; stdcall;
+var BASS_GetVersion :function: DWORD; stdcall;
+var BASS_ErrorGetCode :function: Integer; stdcall;
+var BASS_GetDeviceInfo :function(device: DWORD; var info: BASS_DEVICEINFO): BOOL; stdcall;
+var BASS_Init :function(device: Integer; freq, flags: DWORD; win: HWND; clsid: PGUID): BOOL; stdcall;
+var BASS_SetDevice :function(device: DWORD): BOOL; stdcall;
+var BASS_GetDevice :function: DWORD; stdcall;
+var BASS_Free :function: BOOL; stdcall;
+var BASS_GetDSoundObject:function(obj: DWORD): Pointer; stdcall;
+var BASS_GetInfo :function(var info: BASS_INFO): BOOL; stdcall;
+var BASS_Update :function(length: DWORD): BOOL; stdcall;
+var BASS_GetCPU :function: FLOAT; stdcall;
+var BASS_Start :function: BOOL; stdcall;
+var BASS_Stop :function: BOOL; stdcall;
+var BASS_Pause :function: BOOL; stdcall;
+var BASS_SetVolume :function(volume: FLOAT): BOOL; stdcall;
+var BASS_GetVolume :function: FLOAT; stdcall;
+
+function BASS_PluginLoad (filename: PAnsiChar; flags: DWORD): HPLUGIN; stdcall;
+function BASS_PluginFree (handle: HPLUGIN): BOOL; stdcall;
+function BASS_PluginGetInfo(handle: HPLUGIN): PBASS_PLUGININFO; stdcall;
+
+var BASS_Set3DFactors :function(distf, rollf, doppf: FLOAT): BOOL; stdcall;
+var BASS_Get3DFactors :function(var distf, rollf, doppf: FLOAT): BOOL; stdcall;
+var BASS_Set3DPosition :function(var pos, vel, front, top: BASS_3DVECTOR): BOOL; stdcall;
+var BASS_Get3DPosition :function(var pos, vel, front, top: BASS_3DVECTOR): BOOL; stdcall;
+var BASS_Apply3D :procedure; stdcall;
+var BASS_SetEAXParameters:function(env: Integer; vol, decay, damp: FLOAT): BOOL; stdcall;
+var BASS_GetEAXParameters:function(var env: DWORD; var vol, decay, damp: FLOAT): BOOL; stdcall;
+
+var BASS_MusicLoad:function(mem: BOOL; f: Pointer; offset: QWORD; length, flags, freq: DWORD): HMUSIC; stdcall;
+var BASS_MusicFree:function(handle: HMUSIC): BOOL; stdcall;
+
+var BASS_SampleLoad :function(mem: BOOL; f: Pointer; offset: QWORD; length, max, flags: DWORD): HSAMPLE; stdcall;
+var BASS_SampleCreate :function(length, freq, chans, max, flags: DWORD): HSAMPLE; stdcall;
+var BASS_SampleFree :function(handle: HSAMPLE): BOOL; stdcall;
+var BASS_SampleSetData :function(handle: HSAMPLE; buffer: Pointer): BOOL; stdcall;
+var BASS_SampleGetData :function(handle: HSAMPLE; buffer: Pointer): BOOL; stdcall;
+var BASS_SampleGetInfo :function(handle: HSAMPLE; var info: BASS_SAMPLE): BOOL; stdcall;
+var BASS_SampleSetInfo :function(handle: HSAMPLE; var info: BASS_SAMPLE): BOOL; stdcall;
+var BASS_SampleGetChannel :function(handle: HSAMPLE; onlynew: BOOL): HCHANNEL; stdcall;
+var BASS_SampleGetChannels:function(handle: HSAMPLE; channels: Pointer): DWORD; stdcall;
+var BASS_SampleStop :function(handle: HSAMPLE): BOOL; stdcall;
+
+var BASS_StreamCreate :function(freq, chans, flags: DWORD; proc: STREAMPROC; user: Pointer): HSTREAM; stdcall;
+var BASS_StreamCreateFile :function(mem: BOOL; f: Pointer; offset, length: QWORD; flags: DWORD): HSTREAM; stdcall;
+var BASS_StreamCreateURL :function(url: Pointer; offset: DWORD; flags: DWORD; proc: DOWNLOADPROC; user: Pointer):HSTREAM; stdcall;
+var BASS_StreamCreateFileUser :function(system, flags: DWORD; var procs: BASS_FILEPROCS; user: Pointer): HSTREAM; stdcall;
+var BASS_StreamFree :function(handle: HSTREAM): BOOL; stdcall;
+var BASS_StreamGetFilePosition:function(handle: HSTREAM; mode: DWORD): QWORD; stdcall;
+var BASS_StreamPutData :function(handle: HSTREAM; buffer: Pointer; length: DWORD): DWORD; stdcall;
+var BASS_StreamPutFileData :function(handle: HSTREAM; buffer: Pointer; length: DWORD): DWORD; stdcall;
+
+var BASS_RecordGetDeviceInfo:function(device: DWORD; var info: BASS_DEVICEINFO): BOOL; stdcall;
+var BASS_RecordInit :function(device: Integer): BOOL; stdcall;
+var BASS_RecordSetDevice :function(device: DWORD): BOOL; stdcall;
+var BASS_RecordGetDevice :function: DWORD; stdcall;
+var BASS_RecordFree :function: BOOL; stdcall;
+var BASS_RecordGetInfo :function(var info: BASS_RECORDINFO): BOOL; stdcall;
+var BASS_RecordGetInputName :function(input: Integer): PAnsiChar; stdcall;
+var BASS_RecordSetInput :function(input: Integer; flags: DWORD; volume: FLOAT): BOOL; stdcall;
+var BASS_RecordGetInput :function(input: Integer; var volume: FLOAT): DWORD; stdcall;
+var BASS_RecordStart :function(freq, chans, flags: DWORD; proc: RECORDPROC; user: Pointer): HRECORD; stdcall;
+
+var BASS_ChannelBytes2Seconds :function(handle: DWORD; pos: QWORD): Double; stdcall;
+var BASS_ChannelSeconds2Bytes :function(handle: DWORD; pos: Double): QWORD; stdcall;
+var BASS_ChannelGetDevice :function(handle: DWORD): DWORD; stdcall;
+var BASS_ChannelSetDevice :function(handle, device: DWORD): BOOL; stdcall;
+var BASS_ChannelIsActive :function(handle: DWORD): DWORD; stdcall;
+var BASS_ChannelGetInfo :function(handle: DWORD; var info: BASS_CHANNELINFO):BOOL;stdcall;
+var BASS_ChannelGetTags :function(handle: HSTREAM; tags: DWORD): PAnsiChar; stdcall;
+var BASS_ChannelFlags :function(handle, flags, mask: DWORD): DWORD; stdcall;
+var BASS_ChannelUpdate :function(handle, length: DWORD): BOOL; stdcall;
+var BASS_ChannelLock :function(handle: DWORD; lock: BOOL): BOOL; stdcall;
+var BASS_ChannelPlay :function(handle: DWORD; restart: BOOL): BOOL; stdcall;
+var BASS_ChannelStop :function(handle: DWORD): BOOL; stdcall;
+var BASS_ChannelPause :function(handle: DWORD): BOOL; stdcall;
+var BASS_ChannelSetAttribute :function(handle, attrib: DWORD; value: FLOAT): BOOL; stdcall;
+var BASS_ChannelGetAttribute :function(handle, attrib: DWORD; var value: FLOAT): BOOL; stdcall;
+var BASS_ChannelSlideAttribute :function(handle, attrib: DWORD; value: FLOAT; time: DWORD): BOOL; stdcall;
+var BASS_ChannelIsSliding :function(handle, attrib: DWORD): BOOL; stdcall;
+var BASS_ChannelSet3DAttributes:function(handle: DWORD; mode: Integer; min, max: FLOAT; iangle, oangle, outvol: Integer): BOOL; stdcall;
+var BASS_ChannelGet3DAttributes:function(handle: DWORD; var mode: DWORD; var min, max: FLOAT; var iangle, oangle, outvol: DWORD): BOOL; stdcall;
+var BASS_ChannelSet3DPosition :function(handle: DWORD; var pos, orient, vel: BASS_3DVECTOR): BOOL; stdcall;
+var BASS_ChannelGet3DPosition :function(handle: DWORD; var pos, orient, vel: BASS_3DVECTOR): BOOL; stdcall;
+var BASS_ChannelGetLength :function(handle, mode: DWORD): QWORD; stdcall;
+var BASS_ChannelSetPosition :function(handle: DWORD; pos: QWORD; mode: DWORD): BOOL; stdcall;
+var BASS_ChannelGetPosition :function(handle, mode: DWORD): QWORD; stdcall;
+var BASS_ChannelGetLevel :function(handle: DWORD): DWORD; stdcall;
+var BASS_ChannelGetData :function(handle: DWORD; buffer: Pointer; length: DWORD): DWORD; stdcall;
+var BASS_ChannelSetSync :function(handle: DWORD; type_: DWORD; param: QWORD; proc: SYNCPROC; user: Pointer): HSYNC; stdcall;
+var BASS_ChannelRemoveSync :function(handle: DWORD; sync: HSYNC): BOOL; stdcall;
+var BASS_ChannelSetDSP :function(handle: DWORD; proc: DSPPROC; user: Pointer; priority: Integer): HDSP; stdcall;
+var BASS_ChannelRemoveDSP :function(handle: DWORD; dsp: HDSP): BOOL; stdcall;
+var BASS_ChannelSetLink :function(handle, chan: DWORD): BOOL; stdcall;
+var BASS_ChannelRemoveLink :function(handle, chan: DWORD): BOOL; stdcall;
+var BASS_ChannelSetFX :function(handle, type_: DWORD; priority: Integer): HFX; stdcall;
+var BASS_ChannelRemoveFX :function(handle: DWORD; fx: HFX): BOOL; stdcall;
+
+var BASS_FXSetParameters:function(handle: HFX; par: Pointer): BOOL; stdcall;
+var BASS_FXGetParameters:function(handle: HFX; par: Pointer): BOOL; stdcall;
+var BASS_FXReset :function(handle: HFX): BOOL; stdcall;
+
+{ok, now we need something that loads our DLL and gets rid of it as well...}
+
+var BASS_Handle:Thandle=0; // this will hold our handle for the dll; it functions nicely as a mutli-dll prevention unit as well...
+
+Function Load_BASSDLL(dllfilename:PAnsiChar):boolean; overload;
+Function Load_BASSDLL(dllfilename:PWideChar):boolean; overload;
+
+Procedure Unload_BASSDLL; // another mystery function ???
+{
+ This function frees the dynamically linked-in functions from memory...don't forget to call it once you're done !
+ Best place to put this is probably the OnDestroy of your Main-Form;
+ suggested use in OnDestroy :
+ - Call BASS_Free to get rid of everything that's eating memory (automatically called, but just to be on the safe-side !),
+ - Then call this function.
+}
+
+
+function BASS_SPEAKER_N(n: DWORD): DWORD;
+function BASS_SetEAXPreset(env: Integer): BOOL;
+{
+ This function is defined in the implementation part of this unit.
+ It is not part of BASS.DLL but an extra function which makes it easier
+ to set the predefined EAX environments.
+ env : a EAX_ENVIRONMENT_xxx constant
+}
+
+implementation
+
+var BASS_PluginLoad_ :function(f: PAnsiChar; flags: DWORD): HPLUGIN; stdcall;
+var BASS_PluginFree_ :function(handle: HPLUGIN): BOOL; stdcall;
+var BASS_PluginGetInfo_:function(handle: HPLUGIN): PBASS_PLUGININFO; stdcall;
+
+Function CheckBASSHandle:boolean;
+{$IFDEF CHECK_PROC}label L_Exit;{$ENDIF}
+begin
+ if BASS_Handle<>0 then
+ begin {now we tie the functions to the VARs from above}
+
+ @BASS_SetConfig :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_SetConfig'));
+ {$IFDEF CHECK_PROC}if @BASS_SetConfig=nil then goto L_Exit;{$ENDIF}
+ @BASS_GetConfig :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_GetConfig'));
+ {$IFDEF CHECK_PROC}if @BASS_GetConfig=nil then goto L_Exit;{$ENDIF}
+ @BASS_SetConfigPtr :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_SetConfigPtr'));
+ {$IFDEF CHECK_PROC}if @BASS_SetConfigPtr=nil then goto L_Exit;{$ENDIF}
+ @BASS_GetConfigPtr :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_GetConfigPtr'));
+ {$IFDEF CHECK_PROC}if @BASS_GetConfigPtr=nil then goto L_Exit;{$ENDIF}
+ @BASS_GetVersion :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_GetVersion'));
+ {$IFDEF CHECK_PROC}if @BASS_GetVersion=nil then goto L_Exit;{$ENDIF}
+ @BASS_ErrorGetCode :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ErrorGetCode'));
+ {$IFDEF CHECK_PROC}if @BASS_ErrorGetCode=nil then goto L_Exit;{$ENDIF}
+ @BASS_GetDeviceInfo :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_GetDeviceInfo'));
+ {$IFDEF CHECK_PROC}if @BASS_GetDeviceInfo=nil then goto L_Exit;{$ENDIF}
+ @BASS_Init :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_Init'));
+ {$IFDEF CHECK_PROC}if @BASS_Init=nil then goto L_Exit;{$ENDIF}
+ @BASS_SetDevice :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_SetDevice'));
+ {$IFDEF CHECK_PROC}if @BASS_SetDevice=nil then goto L_Exit;{$ENDIF}
+ @BASS_GetDevice :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_GetDevice'));
+ {$IFDEF CHECK_PROC}if @BASS_GetDevice=nil then goto L_Exit;{$ENDIF}
+ @BASS_Free :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_Free'));
+ {$IFDEF CHECK_PROC}if @BASS_Free=nil then goto L_Exit;{$ENDIF}
+ @BASS_GetDSoundObject:=GetProcAddress(BASS_Handle,PAnsiChar('BASS_GetDSoundObject'));
+ {$IFDEF CHECK_PROC}if @BASS_GetDSoundObject=nil then goto L_Exit;{$ENDIF}
+ @BASS_GetInfo :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_GetInfo'));
+ {$IFDEF CHECK_PROC}if @BASS_GetInfo=nil then goto L_Exit;{$ENDIF}
+ @BASS_Update :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_Update'));
+ {$IFDEF CHECK_PROC}if @BASS_Update=nil then goto L_Exit;{$ENDIF}
+ @BASS_GetCPU :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_GetCPU'));
+ {$IFDEF CHECK_PROC}if @BASS_GetCPU=nil then goto L_Exit;{$ENDIF}
+ @BASS_Start :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_Start'));
+ {$IFDEF CHECK_PROC}if @BASS_Start=nil then goto L_Exit;{$ENDIF}
+ @BASS_Stop :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_Stop'));
+ {$IFDEF CHECK_PROC}if @BASS_Stop=nil then goto L_Exit;{$ENDIF}
+ @BASS_Pause :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_Pause'));
+ {$IFDEF CHECK_PROC}if @BASS_Pause=nil then goto L_Exit;{$ENDIF}
+ @BASS_SetVolume :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_SetVolume'));
+ {$IFDEF CHECK_PROC}if @BASS_SetVolume=nil then goto L_Exit;{$ENDIF}
+ @BASS_GetVolume :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_GetVolume'));
+ {$IFDEF CHECK_PROC}if @BASS_GetVolume=nil then goto L_Exit;{$ENDIF}
+
+ @BASS_PluginLoad_ :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_PluginLoad'));
+ {$IFDEF CHECK_PROC}if @BASS_PluginLoad_=nil then goto L_Exit;{$ENDIF}
+ @BASS_PluginFree_ :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_PluginFree'));
+ {$IFDEF CHECK_PROC}if @BASS_PluginFree_=nil then goto L_Exit;{$ENDIF}
+ @BASS_PluginGetInfo_:=GetProcAddress(BASS_Handle,PAnsiChar('BASS_PluginGetInfo'));
+ {$IFDEF CHECK_PROC}if @BASS_PluginGetInfo_=nil then goto L_Exit;{$ENDIF}
+
+ @BASS_Set3DFactors :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_Set3DFactors'));
+ {$IFDEF CHECK_PROC}if @BASS_Set3DFactors=nil then goto L_Exit;{$ENDIF}
+ @BASS_Get3DFactors :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_Get3DFactors'));
+ {$IFDEF CHECK_PROC}if @BASS_Get3DFactors=nil then goto L_Exit;{$ENDIF}
+ @BASS_Set3DPosition :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_Set3DPosition'));
+ {$IFDEF CHECK_PROC}if @BASS_Set3DPosition=nil then goto L_Exit;{$ENDIF}
+ @BASS_Get3DPosition :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_Get3DPosition'));
+ {$IFDEF CHECK_PROC}if @BASS_Get3DPosition=nil then goto L_Exit;{$ENDIF}
+ @BASS_Apply3D :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_Apply3D'));
+ {$IFDEF CHECK_PROC}if @BASS_Apply3D=nil then goto L_Exit;{$ENDIF}
+ @BASS_SetEAXParameters:=GetProcAddress(BASS_Handle,PAnsiChar('BASS_SetEAXParameters'));
+ {$IFDEF CHECK_PROC}if @BASS_SetEAXParameters=nil then goto L_Exit;{$ENDIF}
+ @BASS_GetEAXParameters:=GetProcAddress(BASS_Handle,PAnsiChar('BASS_GetEAXParameters'));
+ {$IFDEF CHECK_PROC}if @BASS_GetEAXParameters=nil then goto L_Exit;{$ENDIF}
+
+ @BASS_MusicLoad:=GetProcAddress(BASS_Handle,PAnsiChar('BASS_MusicLoad'));
+ {$IFDEF CHECK_PROC}if @BASS_MusicLoad=nil then goto L_Exit;{$ENDIF}
+ @BASS_MusicFree:=GetProcAddress(BASS_Handle,PAnsiChar('BASS_MusicFree'));
+ {$IFDEF CHECK_PROC}if @BASS_MusicFree=nil then goto L_Exit;{$ENDIF}
+
+ @BASS_SampleLoad :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_SampleLoad'));
+ {$IFDEF CHECK_PROC}if @BASS_SampleLoad=nil then goto L_Exit;{$ENDIF}
+ @BASS_SampleCreate :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_SampleCreate'));
+ {$IFDEF CHECK_PROC}if @BASS_SampleCreate=nil then goto L_Exit;{$ENDIF}
+ @BASS_SampleFree :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_SampleFree'));
+ {$IFDEF CHECK_PROC}if @BASS_SampleFree=nil then goto L_Exit;{$ENDIF}
+ @BASS_SampleSetData :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_SampleSetData'));
+ {$IFDEF CHECK_PROC}if @BASS_SampleSetData=nil then goto L_Exit;{$ENDIF}
+ @BASS_SampleGetData :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_SampleGetData'));
+ {$IFDEF CHECK_PROC}if @BASS_SampleGetData=nil then goto L_Exit;{$ENDIF}
+ @BASS_SampleGetInfo :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_SampleGetInfo'));
+ {$IFDEF CHECK_PROC}if @BASS_SampleGetInfo=nil then goto L_Exit;{$ENDIF}
+ @BASS_SampleSetInfo :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_SampleSetInfo'));
+ {$IFDEF CHECK_PROC}if @BASS_SampleSetInfo=nil then goto L_Exit;{$ENDIF}
+ @BASS_SampleGetChannel :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_SampleGetChannel'));
+ {$IFDEF CHECK_PROC}if @BASS_SampleGetChannel=nil then goto L_Exit;{$ENDIF}
+ @BASS_SampleGetChannels:=GetProcAddress(BASS_Handle,PAnsiChar('BASS_SampleGetChannels'));
+ {$IFDEF CHECK_PROC}if @BASS_SampleGetChannels=nil then goto L_Exit;{$ENDIF}
+ @BASS_SampleStop :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_SampleStop'));
+ {$IFDEF CHECK_PROC}if @BASS_SampleStop=nil then goto L_Exit;{$ENDIF}
+
+ @BASS_StreamCreate :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_StreamCreate'));
+ {$IFDEF CHECK_PROC}if @BASS_StreamCreate=nil then goto L_Exit;{$ENDIF}
+ @BASS_StreamCreateFile :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_StreamCreateFile'));
+ {$IFDEF CHECK_PROC}if @BASS_StreamCreateFile=nil then goto L_Exit;{$ENDIF}
+ @BASS_StreamCreateURL :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_StreamCreateURL'));
+ {$IFDEF CHECK_PROC}if @BASS_StreamCreateURL=nil then goto L_Exit;{$ENDIF}
+ @BASS_StreamCreateFileUser :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_StreamCreateFileUser'));
+ {$IFDEF CHECK_PROC}if @BASS_StreamCreateFileUser=nil then goto L_Exit;{$ENDIF}
+ @BASS_StreamFree :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_StreamFree'));
+ {$IFDEF CHECK_PROC}if @BASS_StreamFree=nil then goto L_Exit;{$ENDIF}
+ @BASS_StreamGetFilePosition:=GetProcAddress(BASS_Handle,PAnsiChar('BASS_StreamGetFilePosition'));
+ {$IFDEF CHECK_PROC}if @BASS_StreamGetFilePosition=nil then goto L_Exit;{$ENDIF}
+ @BASS_StreamPutData :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_StreamPutData'));
+ {$IFDEF CHECK_PROC}if @BASS_StreamPutData=nil then goto L_Exit;{$ENDIF}
+ @BASS_StreamPutFileData :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_StreamPutFileData'));
+ {$IFDEF CHECK_PROC}if @BASS_StreamPutFileData=nil then goto L_Exit;{$ENDIF}
+
+ @BASS_RecordGetDeviceInfo:=GetProcAddress(BASS_Handle,PAnsiChar('BASS_RecordGetDeviceInfo'));
+ {$IFDEF CHECK_PROC}if @BASS_RecordGetDeviceInfo=nil then goto L_Exit;{$ENDIF}
+ @BASS_RecordInit :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_RecordInit'));
+ {$IFDEF CHECK_PROC}if @BASS_RecordInit=nil then goto L_Exit;{$ENDIF}
+ @BASS_RecordSetDevice :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_RecordSetDevice'));
+ {$IFDEF CHECK_PROC}if @BASS_RecordSetDevice=nil then goto L_Exit;{$ENDIF}
+ @BASS_RecordGetDevice :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_RecordGetDevice'));
+ {$IFDEF CHECK_PROC}if @BASS_RecordGetDevice=nil then goto L_Exit;{$ENDIF}
+ @BASS_RecordFree :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_RecordFree'));
+ {$IFDEF CHECK_PROC}if @BASS_RecordFree=nil then goto L_Exit;{$ENDIF}
+ @BASS_RecordGetInfo :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_RecordGetInfo'));
+ {$IFDEF CHECK_PROC}if @BASS_RecordGetInfo=nil then goto L_Exit;{$ENDIF}
+ @BASS_RecordGetInputName :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_RecordGetInputName'));
+ {$IFDEF CHECK_PROC}if @BASS_RecordGetInputName=nil then goto L_Exit;{$ENDIF}
+ @BASS_RecordSetInput :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_RecordSetInput'));
+ {$IFDEF CHECK_PROC}if @BASS_RecordSetInput=nil then goto L_Exit;{$ENDIF}
+ @BASS_RecordGetInput :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_RecordGetInput'));
+ {$IFDEF CHECK_PROC}if @BASS_RecordGetInput=nil then goto L_Exit;{$ENDIF}
+ @BASS_RecordStart :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_RecordStart'));
+ {$IFDEF CHECK_PROC}if @BASS_RecordStart=nil then goto L_Exit;{$ENDIF}
+
+ @BASS_ChannelBytes2Seconds :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelBytes2Seconds'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelBytes2Seconds=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelSeconds2Bytes :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelSeconds2Bytes'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelSeconds2Bytes=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelGetDevice :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelGetDevice'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelGetDevice=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelSetDevice :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelSetDevice'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelSetDevice=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelIsActive :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelIsActive'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelIsActive=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelGetInfo :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelGetInfo'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelGetInfo=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelGetTags :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelGetTags'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelGetTags=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelFlags :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelFlags'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelFlags=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelUpdate :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelUpdate'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelUpdate=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelLock :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelLock'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelLock=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelPlay :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelPlay'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelPlay=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelStop :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelStop'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelStop=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelPause :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelPause'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelPause=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelSetAttribute :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelSetAttribute'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelSetAttribute=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelGetAttribute :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelGetAttribute'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelGetAttribute=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelSlideAttribute :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelSlideAttribute'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelSlideAttribute=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelIsSliding :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelIsSliding'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelIsSliding=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelSet3DAttributes:=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelSet3DAttributes'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelSet3DAttributes=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelGet3DAttributes:=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelGet3DAttributes'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelGet3DAttributes=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelSet3DPosition :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelSet3DPosition'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelSet3DPosition=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelGet3DPosition :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelGet3DPosition'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelGet3DPosition=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelGetLength :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelGetLength'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelGetLength=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelSetPosition :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelSetPosition'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelSetPosition=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelGetPosition :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelGetPosition'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelGetPosition=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelGetLevel :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelGetLevel'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelGetLevel=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelGetData :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelGetData'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelGetData=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelSetSync :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelSetSync'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelSetSync=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelRemoveSync :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelRemoveSync'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelRemoveSync=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelSetDSP :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelSetDSP'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelSetDSP=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelRemoveDSP :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelRemoveDSP'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelRemoveDSP=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelSetLink :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelSetLink'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelSetLink=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelRemoveLink :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelRemoveLink'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelRemoveLink=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelSetFX :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelSetFX'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelSetFX=nil then goto L_Exit;{$ENDIF}
+ @BASS_ChannelRemoveFX :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_ChannelRemoveFX'));
+ {$IFDEF CHECK_PROC}if @BASS_ChannelRemoveFX=nil then goto L_Exit;{$ENDIF}
+
+ @BASS_FXSetParameters:=GetProcAddress(BASS_Handle,PAnsiChar('BASS_FXSetParameters'));
+ {$IFDEF CHECK_PROC}if @BASS_FXSetParameters=nil then goto L_Exit;{$ENDIF}
+ @BASS_FXGetParameters:=GetProcAddress(BASS_Handle,PAnsiChar('BASS_FXGetParameters'));
+ {$IFDEF CHECK_PROC}if @BASS_FXGetParameters=nil then goto L_Exit;{$ENDIF}
+ @BASS_FXReset :=GetProcAddress(BASS_Handle,PAnsiChar('BASS_FXReset'));
+ {$IFDEF CHECK_PROC}if @BASS_FXReset=nil then goto L_Exit;{$ENDIF}
+
+ result:=true;
+ exit;
+{$IFDEF CHECK_PROC}
+L_Exit:
+ FreeLibrary(BASS_Handle);
+ BASS_Handle:=0;
+{$ENDIF}
+ end;
+ result:=false;
+end;
+
+Function Load_BASSDLL(dllfilename:PAnsiChar):boolean;
+var
+ oldmode:integer;
+begin
+ if BASS_Handle<>0 then result:=true
+ else
+ begin
+ oldmode:=SetErrorMode($8001);
+ BASS_Handle:=LoadLibraryA(dllfilename);
+ SetErrorMode(oldmode);
+ result:=CheckBASSHandle;
+ end;
+end;
+
+Function Load_BASSDLL(dllfilename:PWideChar):boolean;
+var
+ oldmode:integer;
+begin
+ if BASS_Handle<>0 then result:=true
+ else
+ begin
+ oldmode:=SetErrorMode($8001);
+ BASS_Handle:=LoadLibraryW(dllfilename);
+ SetErrorMode(oldmode);
+ result:=CheckBASSHandle;
+ end;
+end;
+
+Procedure Unload_BASSDLL;
+begin
+ if BASS_Handle<>0 then
+ begin
+ BASS_Free; // make sure we release everything
+ FreeLibrary(BASS_Handle);
+ end;
+ BASS_Handle:=0;
+end;
+
+function BASS_SPEAKER_N(n: DWORD): DWORD;
+begin
+ Result := n shl 24;
+end;
+
+type
+ tEAXrec = record
+ vol ,
+ decay,
+ damp : FLOAT;
+ end;
+
+const
+ EAXTable : array [0..EAX_ENVIRONMENT_COUNT-1] of tEAXRec = (
+ (vol:0.5 ; decay: 1.493; damp:0.5 ),
+ (vol:0.25 ; decay: 0.1 ; damp:0 ),
+ (vol:0.417; decay: 0.4 ; damp:0.666),
+ (vol:0.653; decay: 1.499; damp:0.166),
+ (vol:0.208; decay: 0.478; damp:0 ),
+ (vol:0.5 ; decay: 2.309; damp:0.888),
+ (vol:0.403; decay: 4.279; damp:0.5 ),
+ (vol:0.5 ; decay: 3.961; damp:0.5 ),
+ (vol:0.5 ; decay: 2.886; damp:1.304),
+ (vol:0.361; decay: 7.284; damp:0.332),
+ (vol:0.5 ; decay:10.0 ; damp:0.3 ),
+ (vol:0.153; decay: 0.259; damp:2.0 ),
+ (vol:0.361; decay: 1.493; damp:0 ),
+ (vol:0.444; decay: 2.697; damp:0.638),
+ (vol:0.25 ; decay: 1.752; damp:0.776),
+ (vol:0.111; decay: 3.145; damp:0.472),
+ (vol:0.111; decay: 2.767; damp:0.224),
+ (vol:0.194; decay: 7.841; damp:0.472),
+ (vol:1 ; decay: 1.499; damp:0.5 ),
+ (vol:0.097; decay: 2.767; damp:0.224),
+ (vol:0.208; decay: 1.652; damp:1.5 ),
+ (vol:0.652; decay: 2.886; damp:0.25 ),
+ (vol:1 ; decay: 1.499; damp:0 ),
+ (vol:0.875; decay: 8.392; damp:1.388),
+ (vol:0.139; decay:17.234; damp:0.666),
+ (vol:0.486; decay: 7.563; damp:0.806));
+
+function BASS_SetEAXPreset(env: Integer): BOOL;
+begin
+ if env<EAX_ENVIRONMENT_COUNT then
+ begin
+ with EAXTable[env] do
+ result:=BASS_SetEAXParameters(env,vol,decay,damp);
+ end
+ else
+ result:=false;
+end;
+
+const
+ PluginsMax = 50;
+const
+ NumPlugin:cardinal=0;
+var
+ arrPlugins:array [0..PluginsMax] of HPLUGIN;
+
+// Plugin functions
+function BASS_PluginLoad(filename:PAnsiChar;flags:DWORD):HPLUGIN; stdcall;
+var
+ i:cardinal;
+begin
+ result:=BASS_PluginLoad_(filename,flags);
+ if result<>0 then // 0 - not plugin?
+ begin
+ i:=0;
+ while i<NumPlugin do
+ begin
+ if arrPlugins[i]=result then // if present already
+ exit;
+ inc(i);
+ end;
+ // Remember for future
+ arrPlugins[i]:=result;
+ inc(NumPlugin);
+ arrPlugins[NumPlugin]:=0; //last - zero
+ end;
+end;
+
+function BASS_PluginFree(handle:HPLUGIN):BOOL; stdcall;
+var
+ i:cardinal;
+begin
+ result:=BASS_PluginFree_(handle);
+ if result then
+ begin
+ if handle=0 then
+ NumPlugin:=0
+ else
+ begin
+ i:=0;
+ while i<NumPlugin do
+ begin
+ if arrPlugins[i]=handle then
+ begin
+ move(arrPlugins[i+1],arrPlugins[i],SizeOf(HPLUGIN)*(NumPlugin-i));
+ dec(NumPlugin);
+ exit;
+ end;
+ inc(i);
+ end;
+ end;
+ end;
+end;
+
+function BASS_PluginGetInfo(handle:HPLUGIN):PBASS_PLUGININFO; stdcall;
+begin
+ if handle=0 then
+ begin
+ if NumPlugin=0 then
+ result:=nil
+ else
+ result:=@arrPlugins;
+ end
+ else
+ result:=BASS_PluginGetInfo_(handle);
+end;
+
+end.
diff --git a/plugins/Libs/FastMM4.pas b/plugins/Libs/FastMM4.pas
new file mode 100644
index 0000000000..8e02a3a1c9
--- /dev/null
+++ b/plugins/Libs/FastMM4.pas
@@ -0,0 +1,11698 @@
+(*
+
+Fast Memory Manager 4.99
+
+Description:
+ A fast replacement memory manager for Embarcadero Delphi Win32 applications
+ that scales well under multi-threaded usage, is not prone to memory
+ fragmentation, and supports shared memory without the use of external .DLL
+ files.
+
+Homepage:
+ http://fastmm.sourceforge.net
+
+Advantages:
+ - Fast
+ - Low overhead. FastMM is designed for an average of 5% and maximum of 10%
+ overhead per block.
+ - Supports up to 3GB of user mode address space under Windows 32-bit and 4GB
+ under Windows 64-bit. Add the "$SetPEFlags $20" option (in curly braces)
+ to your .dpr to enable this.
+ - Highly aligned memory blocks. Can be configured for either 8-byte or 16-byte
+ alignment.
+ - Good scaling under multi-threaded applications
+ - Intelligent reallocations. Avoids slow memory move operations through
+ not performing unneccesary downsizes and by having a minimum percentage
+ block size growth factor when an in-place block upsize is not possible.
+ - Resistant to address space fragmentation
+ - No external DLL required when sharing memory between the application and
+ external libraries (provided both use this memory manager)
+ - Optionally reports memory leaks on program shutdown. (This check can be set
+ to be performed only if Delphi is currently running on the machine, so end
+ users won't be bothered by the error message.)
+ - Supports Delphi 4 (or later), C++ Builder 4 (or later), Kylix 3.
+
+Usage:
+ Delphi:
+ Place this unit as the very first unit under the "uses" section in your
+ project's .dpr file. When sharing memory between an application and a DLL
+ (e.g. when passing a long string or dynamic array to a DLL function), both the
+ main application and the DLL must be compiled using this memory manager (with
+ the required conditional defines set). There are some conditional defines
+ (inside FastMM4Options.inc) that may be used to tweak the memory manager. To
+ enable support for a user mode address space greater than 2GB you will have to
+ use the EditBin* tool to set the LARGE_ADDRESS_AWARE flag in the EXE header.
+ This informs Windows x64 or Windows 32-bit (with the /3GB option set) that the
+ application supports an address space larger than 2GB (up to 4GB). In Delphi 6
+ and later you can also specify this flag through the compiler directive
+ {$SetPEFlags $20}
+ *The EditBin tool ships with the MS Visual C compiler.
+ C++ Builder 6:
+ Refer to the instructions inside FastMM4BCB.cpp.
+
+License:
+ This work is copyright Professional Software Development / Pierre le Riche. It
+ is released under a dual license, and you may choose to use it under either the
+ Mozilla Public License 1.1 (MPL 1.1, available from
+ http://www.mozilla.org/MPL/MPL-1.1.html) or the GNU Lesser General Public
+ License 2.1 (LGPL 2.1, available from
+ http://www.opensource.org/licenses/lgpl-license.php). If you find FastMM useful
+ or you would like to support further development, a donation would be much
+ appreciated. My banking details are:
+ Country: South Africa
+ Bank: ABSA Bank Ltd
+ Branch: Somerset West
+ Branch Code: 334-712
+ Account Name: PSD (Distribution)
+ Account No.: 4041827693
+ Swift Code: ABSAZAJJ
+ My PayPal account is:
+ bof@psd.co.za
+
+Contact Details:
+ My contact details are shown below if you would like to get in touch with me.
+ If you use this memory manager I would like to hear from you: please e-mail me
+ your comments - good and bad.
+ Snailmail:
+ PO Box 2514
+ Somerset West
+ 7129
+ South Africa
+ E-mail:
+ plr@psd.co.za
+
+Support:
+ If you have trouble using FastMM, you are welcome to drop me an e-mail at the
+ address above, or you may post your questions in the BASM newsgroup on the
+ Embarcadero news server (which is where I hang out quite frequently).
+
+Disclaimer:
+ FastMM has been tested extensively with both single and multithreaded
+ applications on various hardware platforms, but unfortunately I am not in a
+ position to make any guarantees. Use it at your own risk.
+
+Acknowledgements (for version 4):
+ - Eric Grange for his RecyclerMM on which the earlier versions of FastMM were
+ based. RecyclerMM was what inspired me to try and write my own memory
+ manager back in early 2004.
+ - Primoz Gabrijelcic for helping to track down various bugs.
+ - Dennis Christensen for his tireless efforts with the Fastcode project:
+ helping to develop, optimize and debug the growing Fastcode library.
+ - JiYuan Xie for implementing the leak reporting code for C++ Builder.
+ - Sebastian Zierer for implementing the OS X support.
+ - Pierre Y. for his suggestions regarding the extension of the memory leak
+ checking options.
+ - Hanspeter Widmer for his suggestion to have an option to display install and
+ uninstall debug messages and moving options to a separate file, as well as
+ the new usage tracker.
+ - Anders Isaksson and Greg for finding and identifying the "DelphiIsRunning"
+ bug under Delphi 5.
+ - Francois Malan for various suggestions and bug reports.
+ - Craig Peterson for helping me identify the cache associativity issues that
+ could arise due to medium blocks always being an exact multiple of 256 bytes.
+ Also for various other bug reports and enhancement suggestions.
+ - Jarek Karciarz, Vladimir Ulchenko (Vavan) and Bob Gonder for their help in
+ implementing the BCB support.
+ - Ben Taylor for his suggestion to display the object class of all memory
+ leaks.
+ - Jean Marc Eber and Vincent Mahon (the Memcheck guys) for the call stack
+ trace code and also the method used to catch virtual method calls on freed
+ objects.
+ - Nahan Hyn for the suggestion to be able to enable or disable memory leak
+ reporting through a global variable (the "ManualLeakReportingControl"
+ option.)
+ - Leonel Togniolli for various suggestions with regard to enhancing the bug
+ tracking features of FastMM and other helpful advice.
+ - Joe Bain and Leonel Togniolli for the workaround to QC#10922 affecting
+ compilation under Delphi 2005.
+ - Robert Marquardt for the suggestion to make localisation of FastMM easier by
+ having all string constants together.
+ - Simon Kissel and Fikret Hasovic for their help in implementing Kylix support.
+ - Matthias Thoma, Petr Vones, Robert Rossmair and the rest of the JCL team for
+ their debug info library used in the debug info support DLL and also the
+ code used to check for a valid call site in the "raw" stack trace code.
+ - Andreas Hausladen for the suggestion to use an external DLL to enable the
+ reporting of debug information.
+ - Alexander Tabakov for various good suggestions regarding the debugging
+ facilities of FastMM.
+ - M. Skloff for some useful suggestions and bringing to my attention some
+ compiler warnings.
+ - Martin Aignesberger for the code to use madExcept instead of the JCL library
+ inside the debug info support DLL.
+ - Diederik and Dennis Passmore for the suggestion to be able to register
+ expected leaks.
+ - Dario Tiraboschi and Mark Gebauer for pointing out the problems that occur
+ when range checking and complete boolean evaluation is turned on.
+ - Arthur Hoornweg for notifying me of the image base being incorrect for
+ borlndmm.dll.
+ - Theo Carr-Brion and Hanspeter Widmer for finding the false alarm error
+ message "Block Header Has Been Corrupted" bug in FullDebugMode.
+ - Danny Heijl for reporting the compiler error in "release" mode.
+ - Omar Zelaya for reporting the BCB support regression bug.
+ - Dan Miser for various good suggestions, e.g. not logging expected leaks to
+ file, enhancements the stack trace and messagebox functionality, etc.
+ - Arjen de Ruijter for fixing the bug in GetMemoryLeakType that caused it
+ to not properly detect expected leaks registered by class when in
+ "FullDebugMode".
+ - Aleksander Oven for reporting the installation problem when trying to use
+ FastMM in an application together with libraries that all use runtime
+ packages.
+ - Kristofer Skaug for reporting the bug that sometimes causes the leak report
+ to be shown, even when all the leaks have been registered as expected leaks.
+ Also for some useful enhancement suggestions.
+ - Günther Schoch for the "RequireDebuggerPresenceForLeakReporting" option.
+ - Jan Schlüter for the "ForceMMX" option.
+ - Hallvard Vassbotn for various good enhancement suggestions.
+ - Mark Edington for some good suggestions and bug reports.
+ - Paul Ishenin for reporting the compilation error when the NoMessageBoxes
+ option is set and also the missing call stack entries issue when "raw" stack
+ traces are enabled, as well as for the Russian translation.
+ - Cristian Nicola for reporting the compilation bug when the
+ CatchUseOfFreedInterfaces option was enabled (4.40).
+ - Mathias Rauen (madshi) for improving the support for madExcept in the debug
+ info support DLL.
+ - Roddy Pratt for the BCB5 support code.
+ - Rene Mihula for the Czech translation and the suggestion to have dynamic
+ loading of the FullDebugMode DLL as an option.
+ - Artur Redzko for the Polish translation.
+ - Bart van der Werf for helping me solve the DLL unload order problem when
+ using the debug mode borlndmm.dll library, as well as various other
+ suggestions.
+ - JRG ("The Delphi Guy") for the Spanish translation.
+ - Justus Janssen for Delphi 4 support.
+ - Vadim Lopushansky and Charles Vinal for reporting the Delphi 5 compiler
+ error in version 4.50.
+ - Johni Jeferson Capeletto for the Brazilian Portuguese translation.
+ - Kurt Fitzner for reporting the BCB6 compiler error in 4.52.
+ - Michal Niklas for reporting the Kylix compiler error in 4.54.
+ - Thomas Speck and Uwe Queisser for German translations.
+ - Zaenal Mutaqin for the Indonesian translation.
+ - Carlos Macao for the Portuguese translation.
+ - Michael Winter for catching the performance issue when reallocating certain
+ block sizes.
+ - dzmitry[li] for the Belarussian translation.
+ - Marcelo Montenegro for the updated Spanish translation.
+ - Jud Cole for finding and reporting the bug which may trigger a read access
+ violation when upsizing certain small block sizes together with the
+ "UseCustomVariableSizeMoveRoutines" option.
+ - Zdenek Vasku for reporting and fixing the memory manager sharing bug
+ affecting Windows 95/98/Me.
+ - RB Winston for suggesting the improvement to GExperts "backup" support.
+ - Thomas Schulz for reporting the bug affecting large address space support
+ under FullDebugMode, as well as the recursive call bug when attempting to
+ report memory leaks when EnableMemoryLeakReporting is disabled.
+ - Luigi Sandon for the Italian translation.
+ - Werner Bochtler for various suggestions and bug reports.
+ - Markus Beth for suggesting the "NeverSleepOnThreadContention" option.
+ - JiYuan Xie for the Simplified Chinese translation.
+ - Andrey Shtukaturov for the updated Russian translation, as well as the
+ Ukrainian translation.
+ - Dimitry Timokhov for finding two elusive bugs in the memory leak class
+ detection code.
+ - Paulo Moreno for fixing the AllocMem bug in FullDebugMode that prevented
+ large blocks from being cleared.
+ - Vladimir Bochkarev for the suggestion to remove some unnecessary code if the
+ MM sharing mechanism is disabled.
+ - Loris Luise for the version constant suggestion.
+ - J.W. de Bokx for the MessageBox bugfix.
+ - Igor Lindunen for reporting the bug that caused the Align16Bytes option to
+ not work in FullDebugMode.
+ - Ionut Muntean for the Romanian translation.
+ - Florent Ouchet for the French translation.
+ - Marcus Mönnig for the ScanMemoryPoolForCorruptions suggestion and the
+ suggestion to have the option to scan the memory pool before every
+ operation when in FullDebugMode.
+ - Francois Piette for bringing under my attention that
+ ScanMemoryPoolForCorruption was not thread safe.
+ - Michael Rabatscher for reporting some compiler warnings.
+ - QianYuan Wang for the Simplified Chinese translation of FastMM4Options.inc.
+ - Maurizio Lotauro and Christian-W. Budde for reporting some Delphi 5
+ compiler errors.
+ - Patrick van Logchem for the DisableLoggingOfMemoryDumps option.
+ - Norbert Spiegel for the BCB4 support code.
+ - Uwe Schuster for the improved string leak detection code.
+ - Murray McGowan for improvements to the usage tracker.
+ - Michael Hieke for the SuppressFreeMemErrorsInsideException option as well
+ as a bugfix to GetMemoryMap.
+ - Richard Bradbrook for fixing the Windows 95 FullDebugMode support that was
+ broken in version 4.94.
+ - Zach Saw for the suggestion to (optionally) use SwitchToThread when
+ waiting for a lock on a shared resource to be released.
+ - Everyone who have made donations. Thanks!
+ - Any other Fastcoders or supporters that I have forgotten, and also everyone
+ that helped with the older versions.
+
+Change log:
+ Version 1.00 (28 June 2004):
+ - First version (called PSDMemoryManager). Based on RecyclerMM (free block
+ stack approach) by Eric Grange.
+ Version 2.00 (3 November 2004):
+ - Complete redesign and rewrite from scratch. Name changed to FastMM to
+ reflect this fact. Uses a linked-list approach. Is faster, has less memory
+ overhead, and will now catch most bad pointers on FreeMem calls.
+ Version 3.00 (1 March 2005):
+ - Another rewrite. Reduced the memory overhead by: (a) not having a separate
+ memory area for the linked list of free blocks (uses space inside free
+ blocks themselves) (b) batch managers are allocated as part of chunks (c)
+ block size lookup table size reduced. This should make FastMM more CPU
+ cache friendly.
+ Version 4.00 (7 June 2005):
+ - Yet another rewrite. FastMM4 is in fact three memory managers in one: Small
+ blocks (up to a few KB) are managed through the binning model in the same
+ way as previous versions, medium blocks (from a few KB up to approximately
+ 256K) are allocated in a linked-list fashion, and large blocks are grabbed
+ directly from the system through VirtualAlloc. This 3-layered design allows
+ very fast operation with the most frequently used block sizes (small
+ blocks), while also minimizing fragmentation and imparting significant
+ overhead savings with blocks larger than a few KB.
+ Version 4.01 (8 June 2005):
+ - Added the options "RequireDebugInfoForLeakReporting" and
+ "RequireIDEPresenceForLeakReporting" as suggested by Pierre Y.
+ - Fixed the "DelphiIsRunning" function not working under Delphi 5, and
+ consequently no leak checking. (Reported by Anders Isaksson and Greg.)
+ Version 4.02 (8 June 2005):
+ - Fixed the compilation error when both the "AssumeMultiThreaded" and
+ "CheckHeapForCorruption options were set. (Reported by Francois Malan.)
+ Version 4.03 (9 June 2005):
+ - Added descriptive error messages when FastMM4 cannot be installed because
+ another MM has already been installed or memory has already been allocated.
+ Version 4.04 (13 June 2005):
+ - Added a small fixed offset to the size of medium blocks (previously always
+ exact multiples of 256 bytes). This makes performance problems due to CPU
+ cache associativity limitations much less likely. (Reported by Craig
+ Peterson.)
+ Version 4.05 (17 June 2005):
+ - Added the Align16Bytes option. Disable this option to drop the 16 byte
+ alignment restriction and reduce alignment to 8 bytes for the smallest
+ block sizes. Disabling Align16Bytes should lower memory consumption at the
+ cost of complicating the use of aligned SSE move instructions. (Suggested
+ by Craig Peterson.)
+ - Added a support unit for C++ Builder 6 - Add FastMM4BCB.cpp and
+ FastMM4.pas to your BCB project to use FastMM instead of the RTL MM. Memory
+ leak checking is not supported because (unfortunately) once an MM is
+ installed under BCB you cannot uninstall it... at least not without
+ modifying the RTL code in exit.c or patching the RTL code runtime. (Thanks
+ to Jarek Karciarz, Vladimir Ulchenko and Bob Gonder.)
+ Version 4.06 (22 June 2005):
+ - Displays the class of all leaked objects on the memory leak report and also
+ tries to identify leaked long strings. Previously it only displayed the
+ sizes of all leaked blocks. (Suggested by Ben Taylor.)
+ - Added support for displaying the sizes of medium and large block memory
+ leaks. Previously it only displayed details for small block leaks.
+ Version 4.07 (22 June 2005):
+ - Fixed the detection of the class of leaked objects not working under
+ Windows 98/Me.
+ Version 4.08 (27 June 2005):
+ - Added a BorlndMM.dpr project to allow you to build a borlndmm.dll that uses
+ FastMM4 instead of the default memory manager. You may replace the old
+ DLL in the Delphi \Bin directory to make the IDE use this memory manager
+ instead.
+ Version 4.09 (30 June 2005):
+ - Included a patch fix for the bug affecting replacement borlndmm.dll files
+ with Delphi 2005 (QC#14007). Compile the patch, close Delphi, and run it
+ once to patch your vclide90.bpl. You will now be able to use the
+ replacement borlndmm.dll to speed up the Delphi 2005 IDE as well.
+ Version 4.10 (7 July 2005):
+ - Due to QC#14070 ("Delphi IDE attempts to free memory after the shutdown
+ code of borlndmm.dll has been called"), FastMM cannot be uninstalled
+ safely when used inside a replacement borlndmm.dll for the IDE. Added a
+ conditional define "NeverUninstall" for this purpose.
+ - Added the "FullDebugMode" option to pad all blocks with a header and footer
+ to help you catch memory overwrite bugs in your applications. All blocks
+ returned to freemem are also zeroed out to help catch bugs involving the
+ use of previously freed blocks. Also catches attempts at calling virtual
+ methods of freed objects provided the block in question has not been reused
+ since the object was freed. Displays stack traces on error to aid debugging.
+ - Added the "LogErrorsToFile" option to log all errors to a text file in the
+ same folder as the application.
+ - Added the "ManualLeakReportingControl" option (suggested by Nahan Hyn) to
+ enable control over whether the memory leak report should be done or not
+ via a global variable.
+ Version 4.11 (7 July 2005):
+ - Fixed a compilation error under Delphi 2005 due to QC#10922. (Thanks to Joe
+ Bain and Leonel Togniolli.)
+ - Fixed leaked object classes not displaying in the leak report in
+ "FullDebugMode".
+ Version 4.12 (8 July 2005):
+ - Moved all the string constants to one place to make it easier to do
+ translations into other languages. (Thanks to Robert Marquardt.)
+ - Added support for Kylix. Some functionality is currently missing: No
+ support for detecting the object class on leaks and also no MM sharing.
+ (Thanks to Simon Kissel and Fikret Hasovic).
+ Version 4.13 (11 July 2005):
+ - Added the FastMM_DebugInfo.dll support library to display debug info for
+ stack traces.
+ - Stack traces for the memory leak report is now logged to the log file in
+ "FullDebugMode".
+ Version 4.14 (14 July 2005):
+ - Fixed string leaks not being detected as such in "FullDebugMode". (Thanks
+ to Leonel Togniolli.)
+ - Fixed the compilation error in "FullDebugMode" when "LogErrorsToFile" is
+ not set. (Thanks to Leonel Togniolli.)
+ - Added a "Release" option to allow the grouping of various options and to
+ make it easier to make debug and release builds. (Thanks to Alexander
+ Tabakov.)
+ - Added a "HideMemoryLeakHintMessage" option to not display the hint below
+ the memory leak message. (Thanks to Alexander Tabakov.)
+ - Changed the fill character for "FullDebugMode" from zero to $80 to be able
+ to differentiate between invalid memory accesses using nil pointers to
+ invalid memory accesses using fields of freed objects. FastMM tries to
+ reserve the 64K block starting at $80800000 at startup to ensure that an
+ A/V will occur when this block is accessed. (Thanks to Alexander Tabakov.)
+ - Fixed some compiler warnings. (Thanks to M. Skloff)
+ - Fixed some display bugs in the memory leak report. (Thanks to Leonel
+ Togniolli.)
+ - Added a "LogMemoryLeakDetailToFile" option. Some applications leak a lot of
+ memory and can make the log file grow very large very quickly.
+ - Added the option to use madExcept instead of the JCL Debug library in the
+ debug info support DLL. (Thanks to Martin Aignesberger.)
+ - Added procedures "GetMemoryManagerState" and "GetMemoryMap" to retrieve
+ statistics about the current state of the memory manager and memory pool.
+ (A usage tracker form together with a demo is also available.)
+ Version 4.15 (14 July 2005):
+ - Fixed a false 4GB(!) memory leak reported in some instances.
+ Version 4.16 (15 July 2005):
+ - Added the "CatchUseOfFreedInterfaces" option to catch the use of interfaces
+ of freed objects. This option is not compatible with checking that a freed
+ block has not been modified, so enable this option only when hunting an
+ invalid interface reference. (Only relevant if "FullDebugMode" is set.)
+ - During shutdown FastMM now checks that all free blocks have not been
+ modified since being freed. (Only when "FullDebugMode" is set and
+ "CatchUseOfFreedInterfaces" is disabled.)
+ Version 4.17 (15 July 2005):
+ - Added the AddExpectedMemoryLeaks and RemoveExpectedMemoryLeaks procedures to
+ register/unregister expected leaks, thus preventing the leak report from
+ displaying if only expected leaks occurred. (Thanks to Diederik and Dennis
+ Passmore for the suggestion.) (Note: these functions were renamed in later
+ versions.)
+ - Fixed the "LogMemoryLeakDetailToFile" not logging memory leak detail to file
+ as it is supposed to. (Thanks to Leonel Togniolli.)
+ Version 4.18 (18 July 2005):
+ - Fixed some issues when range checking or complete boolean evaluation is
+ switched on. (Thanks to Dario Tiraboschi and Mark Gebauer.)
+ - Added the "OutputInstallUninstallDebugString" option to display a message when
+ FastMM is installed or uninstalled. (Thanks to Hanspeter Widmer.)
+ - Moved the options to a separate include file. (Thanks to Hanspeter Widmer.)
+ - Moved message strings to a separate file for easy translation.
+ Version 4.19 (19 July 2005):
+ - Fixed Kylix support that was broken in 4.14.
+ Version 4.20 (20 July 2005):
+ - Fixed a false memory overwrite report at shutdown in "FullDebugMode". If you
+ consistently got a "Block Header Has Been Corrupted" error message during
+ shutdown at address $xxxx0070 then it was probably a false alarm. (Thanks to
+ Theo Carr-Brion and Hanspeter Widmer.}
+ Version 4.21 (27 July 2005):
+ - Minor change to the block header flags to make it possible to immediately
+ tell whether a medium block is being used as a small block pool or not.
+ (Simplifies the leak checking and status reporting code.)
+ - Expanded the functionality around the management of expected memory leaks.
+ - Added the "ClearLogFileOnStartup" option. Deletes the log file during
+ initialization. (Thanks to M. Skloff.)
+ - Changed "OutputInstallUninstallDebugString" to use OutputDebugString instead
+ of MessageBox. (Thanks to Hanspeter Widmer.)
+ Version 4.22 (1 August 2005):
+ - Added a FastAllocMem function that avoids an unnecessary FillChar call with
+ large blocks.
+ - Changed large block resizing behavior to be a bit more conservative. Large
+ blocks will be downsized if the new size is less than half of the old size
+ (the threshold was a quarter previously).
+ Version 4.23 (6 August 2005):
+ - Fixed BCB6 support (Thanks to Omar Zelaya).
+ - Renamed "OutputInstallUninstallDebugString" to "UseOutputDebugString", and
+ added debug string output on memory leak or error detection.
+ Version 4.24 (11 August 2005):
+ - Added the "NoMessageBoxes" option to suppress the display of message boxes,
+ which is useful for services that should not be interrupted. (Thanks to Dan
+ Miser).
+ - Changed the stack trace code to return the line number of the caller and not
+ the line number of the return address. (Thanks to Dan Miser).
+ Version 4.25 (15 August 2005):
+ - Fixed GetMemoryLeakType not detecting expected leaks registered by class
+ when in "FullDebugMode". (Thanks to Arjen de Ruijter).
+ Version 4.26 (18 August 2005):
+ - Added a "UseRuntimePackages" option that allows FastMM to be used in a main
+ application together with DLLs that all use runtime packages. (Thanks to
+ Aleksander Oven.)
+ Version 4.27 (24 August 2005):
+ - Fixed a bug that sometimes caused the leak report to be shown even though all
+ leaks were registered as expected leaks. (Thanks to Kristofer Skaug.)
+ Version 4.29 (30 September 2005):
+ - Added the "RequireDebuggerPresenceForLeakReporting" option to only display
+ the leak report if the application is run inside the IDE. (Thanks to Günther
+ Schoch.)
+ - Added the "ForceMMX" option, which when disabled will check the CPU for
+ MMX compatibility before using MMX. (Thanks to Jan Schlüter.)
+ - Added the module name to the title of error dialogs to more easily identify
+ which application caused the error. (Thanks to Kristofer Skaug.)
+ - Added an ASCII dump to the "FullDebugMode" memory dumps. (Thanks to Hallvard
+ Vassbotn.)
+ - Added the option "HideExpectedLeaksRegisteredByPointer" to suppress the
+ display and logging of expected memory leaks that were registered by pointer.
+ (Thanks to Dan Miser.) Leaks registered by size or class are often ambiguous,
+ so these expected leaks are always logged to file (in FullDebugMode) and are
+ never hidden from the leak display (only displayed if there is at least one
+ unexpected leak).
+ - Added a procedure "GetRegisteredMemoryLeaks" to return a list of all
+ registered memory leaks. (Thanks to Dan Miser.)
+ - Added the "RawStackTraces" option to perform "raw" stack traces, negating
+ the need for stack frames. This will usually result in more complete stack
+ traces in FullDebugMode error reports, but it is significantly slower.
+ (Thanks to Hallvard Vassbotn, Dan Miser and the JCL team.)
+ Version 4.31 (2 October 2005):
+ - Fixed the crash bug when both "RawStackTraces" and "FullDebugMode" were
+ enabled. (Thanks to Dan Miser and Mark Edington.)
+ Version 4.33 (6 October 2005):
+ - Added a header corruption check to all memory blocks that are identified as
+ leaks in FullDebugMode. This allows better differentiation between memory
+ pool corruption bugs and actual memory leaks.
+ - Fixed the stack overflow bug when using "RawStackTraces".
+ Version 4.35 (6 October 2005):
+ - Fixed a compilation error when the "NoMessageBoxes" option is set. (Thanks
+ to Paul Ishenin.)
+ - Before performing a "raw" stack trace, FastMM now checks whether exception
+ handling is in place. If exception handling is not in place FastMM falls
+ back to stack frame tracing. (Exception handling is required to handle the
+ possible A/Vs when reading invalid call addresses. Exception handling is
+ usually always available except when SysUtils hasn't been initialized yet or
+ after SysUtils has been finalized.)
+ Version 4.37 (8 October 2005):
+ - Fixed the missing call stack trace entry issue when dynamically loading DLLs.
+ (Thanks to Paul Ishenin.)
+ Version 4.39 (12 October 2005):
+ - Restored the performance with "RawStackTraces" enabled back to the level it
+ was in 4.35.
+ - Fixed the stack overflow error when using "RawStackTraces" that I thought I
+ had fixed in 4.31, but unfortunately didn't. (Thanks to Craig Peterson.)
+ Version 4.40 (13 October 2005):
+ - Improved "RawStackTraces" to have less incorrect extra entries. (Thanks to
+ Craig Peterson.)
+ - Added the Russian (by Paul Ishenin) and Afrikaans translations of
+ FastMM4Messages.pas.
+ Version 4.42 (13 October 2005):
+ - Fixed the compilation error when "CatchUseOfFreedInterfaces" is enabled.
+ (Thanks to Cristian Nicola.)
+ Version 4.44 (25 October 2005):
+ - Implemented a FastGetHeapStatus function in analogy with GetHeapStatus.
+ (Suggested by Cristian Nicola.)
+ - Shifted more of the stack trace code over to the support dll to allow third
+ party vendors to make available their own stack tracing and stack trace
+ logging facilities.
+ - Mathias Rauen (madshi) improved the support for madExcept in the debug info
+ support DLL. Thanks!
+ - Added support for BCB5. (Thanks to Roddy Pratt.)
+ - Added the Czech translation by Rene Mihula.
+ - Added the "DetectMMOperationsAfterUninstall" option. This will catch
+ attempts to use the MM after FastMM has been uninstalled, and is useful for
+ debugging.
+ Version 4.46 (26 October 2005):
+ - Renamed FastMM_DebugInfo.dll to FastMM_FullDebugMode.dll and made the
+ dependency on this library a static one. This solves a DLL unload order
+ problem when using FullDebugMode together with the replacement
+ borlndmm.dll. (Thanks to Bart van der Werf.)
+ - Added the Polish translation by Artur Redzko.
+ Version 4.48 (10 November 2005):
+ - Fixed class detection for objects leaked in dynamically loaded DLLs that
+ were relocated.
+ - Fabio Dell'Aria implemented support for EurekaLog in the FullDebugMode
+ support DLL. Thanks!
+ - Added the Spanish translation by JRG ("The Delphi Guy").
+ Version 4.49 (10 November 2005):
+ - Implemented support for installing replacement AllocMem and leak
+ registration mechanisms for Delphi/BCB versions that support it.
+ - Added support for Delphi 4. (Thanks to Justus Janssen.)
+ Version 4.50 (5 December 2005):
+ - Renamed the ReportMemoryLeaks global variable to ReportMemoryLeaksOnShutdown
+ to be more consistent with the Delphi 2006 memory manager.
+ - Improved the handling of large blocks. Large blocks can now consist of
+ several consecutive segments allocated through VirtualAlloc. This
+ significantly improves speed when frequently resizing large blocks, since
+ these blocks can now often be upsized in-place.
+ Version 4.52 (7 December 2005):
+ - Fixed the compilation error with Delphi 5. (Thanks to Vadim Lopushansky and
+ Charles Vinal for reporting the error.)
+ Version 4.54 (15 December 2005):
+ - Added the Brazilian Portuguese translation by Johni Jeferson Capeletto.
+ - Fixed the compilation error with BCB6. (Thanks to Kurt Fitzner.)
+ Version 4.56 (20 December 2005):
+ - Fixed the Kylix compilation problem. (Thanks to Michal Niklas.)
+ Version 4.58 (1 February 2006):
+ - Added the German translations by Thomas Speck and Uwe Queisser.
+ - Added the Indonesian translation by Zaenal Mutaqin.
+ - Added the Portuguese translation by Carlos Macao.
+ Version 4.60 (21 February 2006):
+ - Fixed a performance issue due to an unnecessary block move operation when
+ allocating a block in the range 1261-1372 bytes and then reallocating it in
+ the range 1373-1429 bytes twice. (Thanks to Michael Winter.)
+ - Added the Belarussian translation by dzmitry[li].
+ - Added the updated Spanish translation by Marcelo Montenegro.
+ - Added a new option "EnableSharingWithDefaultMM". This option allows FastMM
+ to be shared with the default MM of Delphi 2006. It is on by default, but
+ MM sharing has to be enabled otherwise it has no effect (refer to the
+ documentation for the "ShareMM" and "AttemptToUseSharedMM" options).
+ Version 4.62 (22 February 2006):
+ - Fixed a possible read access violation in the MoveX16LP routine when the
+ UseCustomVariableSizeMoveRoutines option is enabled. (Thanks to Jud Cole for
+ some great detective work in finding this bug.)
+ - Improved the downsizing behaviour of medium blocks to better correlate with
+ the reallocation behaviour of small blocks. This change reduces the number
+ of transitions between small and medium block types when reallocating blocks
+ in the 0.7K to 2.6K range. It cuts down on the number of memory move
+ operations and improves performance.
+ Version 4.64 (31 March 2006):
+ - Added the following functions for use with FullDebugMode (and added the
+ exports to the replacement BorlndMM.dll): SetMMLogFileName,
+ GetCurrentAllocationGroup, PushAllocationGroup, PopAllocationGroup and
+ LogAllocatedBlocksToFile. The purpose of these functions are to allow you to
+ identify and log related memory leaks while your application is still
+ running.
+ - Fixed a bug in the memory manager sharing mechanism affecting Windows
+ 95/98/ME. (Thanks to Zdenek Vasku.)
+ Version 4.66 (9 May 2006):
+ - Added a hint comment in this file so that FastMM4Messages.pas will also be
+ backed up by GExperts. (Thanks to RB Winston.)
+ - Fixed a bug affecting large address space (> 2GB) support under
+ FullDebugMode. (Thanks to Thomas Schulz.)
+ Version 4.68 (3 July 2006):
+ - Added the Italian translation by Luigi Sandon.
+ - If FastMM is used inside a DLL it will now use the name of the DLL as base
+ for the log file name. (Previously it always used the name of the main
+ application executable file.)
+ - Fixed a rare A/V when both the FullDebugMode and RawStackTraces options were
+ enabled. (Thanks to Primoz Gabrijelcic.)
+ - Added the "NeverSleepOnThreadContention" option. This option may improve
+ performance if the ratio of the the number of active threads to the number
+ of CPU cores is low (typically < 2). This option is only useful for 4+ CPU
+ systems, it almost always hurts performance on single and dual CPU systems.
+ (Thanks to Werner Bochtler and Markus Beth.)
+ Version 4.70 (4 August 2006):
+ - Added the Simplified Chinese translation by JiYuan Xie.
+ - Added the updated Russian as well as the Ukrainian translation by Andrey
+ Shtukaturov.
+ - Fixed two bugs in the leak class detection code that would sometimes fail
+ to detect the class of leaked objects and strings, and report them as
+ 'unknown'. (Thanks to Dimitry Timokhov)
+ Version 4.72 (24 September 2006):
+ - Fixed a bug that caused AllocMem to not clear blocks > 256K in
+ FullDebugMode. (Thanks to Paulo Moreno.)
+ Version 4.74 (9 November 2006):
+ - Fixed a bug in the segmented large block functionality that could lead to
+ an application freeze when upsizing blocks greater than 256K in a
+ multithreaded application (one of those "what the heck was I thinking?"
+ type bugs).
+ Version 4.76 (12 January 2007):
+ - Changed the RawStackTraces code in the FullDebugMode DLL
+ to prevent it from modifying the Windows "GetLastError" error code.
+ (Thanks to Primoz Gabrijelcic.)
+ - Fixed a threading issue when the "CheckHeapForCorruption" option was
+ enabled, but the "FullDebugMode" option was disabled. (Thanks to Primoz
+ Gabrijelcic.)
+ - Removed some unnecessary startup code when the MM sharing mechanism is
+ disabled. (Thanks to Vladimir Bochkarev.)
+ - In FullDebugMode leaked blocks would sometimes be reported as belonging to
+ the class "TFreedObject" if they were allocated but never used. Such blocks
+ will now be reported as "unknown". (Thanks to Francois Malan.)
+ - In recent versions the replacement borlndmm.dll created a log file (when
+ enabled) that used the "borlndmm" prefix instead of the application name.
+ It is now fixed to use the application name, however if FastMM is used
+ inside other DLLs the name of those DLLs will be used. (Thanks to Bart van
+ der Werf.)
+ - Added a "FastMMVersion" constant. (Suggested by Loris Luise.)
+ - Fixed an issue with error message boxes not displaying under certain
+ configurations. (Thanks to J.W. de Bokx.)
+ - FastMM will now display only one error message at a time. If many errors
+ occur in quick succession, only the first error will be shown (but all will
+ be logged). This avoids a stack overflow with badly misbehaved programs.
+ (Thanks to Bart van der Werf.)
+ - Added a LoadDebugDLLDynamically option to be used in conjunction with
+ FullDebugMode. In this mode FastMM_FullDebugMode.dll is loaded dynamically.
+ If the DLL cannot be found, stack traces will not be available. (Thanks to
+ Rene Mihula.)
+ Version 4.78 (1 March 2007):
+ - The MB_DEFAULT_DESKTOP_ONLY constant that is used when displaying messages
+ boxes since 4.76 is not defined under Kylix, and the source would thus not
+ compile. That constant is now defined. (Thanks to Werner Bochtler.)
+ - Moved the medium block locking code that was duplicated in several places
+ to a subroutine to reduce code size. (Thanks to Hallvard Vassbotn.)
+ - Fixed a bug in the leak registration code that sometimes caused registered
+ leaks to be reported erroneously. (Thanks to Primoz Gabrijelcic.)
+ - Added the NoDebugInfo option (on by default) that suppresses the generation
+ of debug info for the FastMM4.pas unit. This will prevent the integrated
+ debugger from stepping into the memory manager. (Thanks to Primoz
+ Gabrijelcic.)
+ - Increased the default stack trace depth in FullDebugMode from 9 to 10 to
+ ensure that the Align16Bytes setting works in FullDebugMode. (Thanks to
+ Igor Lindunen.)
+ - Updated the Czech translation. (Thanks to Rene Mihula.)
+ Version 4.84 (7 July 2008):
+ - Added the Romanian translation. (Thanks to Ionut Muntean.)
+ - Optimized the GetMemoryMap procedure to improve speed.
+ - Added the GetMemoryManagerUsageSummary function that returns a summary of
+ the GetMemoryManagerState call. (Thanks to Hallvard Vassbotn.)
+ - Added the French translation. (Thanks to Florent Ouchet.)
+ - Added the "AlwaysAllocateTopDown" FullDebugMode option to help with
+ catching bad pointer arithmetic code in an address space > 2GB. This option
+ is enabled by default.
+ - Added the "InstallOnlyIfRunningInIDE" option. Enable this option to
+ only install FastMM as the memory manager when the application is run
+ inside the Delphi IDE. This is useful when you want to deploy the same EXE
+ that you use for testing, but only want the debugging features active on
+ development machines. When this option is enabled and the application is
+ not being run inside the IDE, then the default Delphi memory manager will
+ be used (which, since Delphi 2006, is FastMM without FullDebugMode.) This
+ option is off by default.
+ - Added the "FullDebugModeInIDE" option. This is a convenient shorthand for
+ enabling FullDebugMode, InstallOnlyIfRunningInIDE and
+ LoadDebugDLLDynamically. This causes FastMM to be used in FullDebugMode
+ when the application is being debugged on development machines, and the
+ default memory manager when the same executable is deployed. This allows
+ the debugging and deployment of an application without having to compile
+ separate executables. This option is off by default.
+ - Added a ScanMemoryPoolForCorruptions procedure that checks the entire
+ memory pool for corruptions and raises an exception if one is found. It can
+ be called at any time, but is only available in FullDebugMode. (Thanks to
+ Marcus Mönnig.)
+ - Added a global variable "FullDebugModeScanMemoryPoolBeforeEveryOperation".
+ When this variable is set to true and FullDebugMode is enabled, then the
+ entire memory pool is checked for consistency before every GetMem, FreeMem
+ and ReallocMem operation. An "Out of Memory" error is raised if a
+ corruption is found (and this variable is set to false to prevent recursive
+ errors). This obviously incurs a massive performance hit, so enable it only
+ when hunting for elusive memory corruption bugs. (Thanks to Marcus Mönnig.)
+ - Fixed a bug in AllocMem that caused the FPU stack to be shifted by one
+ position.
+ - Changed the default for option "EnableMMX" to false, since using MMX may
+ cause unexpected behaviour in code that passes parameters on the FPU stack
+ (like some "compiler magic" routines, e.g. VarFromReal).
+ - Removed the "EnableSharingWithDefaultMM" option. This is now the default
+ behaviour and cannot be disabled. (FastMM will always try to share memory
+ managers between itself and the default memory manager when memory manager
+ sharing is enabled.)
+ - Introduced a new memory manager sharing mechanism based on memory mapped
+ files. This solves compatibility issues with console and service
+ applications. This sharing mechanism currently runs in parallel with the
+ old mechanism, but the old mechanism can be disabled by undefining
+ "EnableBackwardCompatibleMMSharing" in FastMM4Options.inc.
+ - Fixed the recursive call error when the EnableMemoryLeakReporting option
+ is disabled and an attempt is made to register a memory leak under Delphi
+ 2006 or later. (Thanks to Thomas Schulz.)
+ - Added a global variable "SuppressMessageBoxes" to enable or disable
+ messageboxes at runtime. (Thanks to Craig Peterson.)
+ - Added the leak reporting code for C++ Builder, as well as various other
+ C++ Builder bits written by JiYuan Xie. (Thank you!)
+ - Added the new Usage Tracker written by Hanspeter Widmer. (Thank you!)
+ Version 4.86 (31 July 2008):
+ - Tweaked the string detection algorithm somewhat to be less strict, and
+ allow non-class leaks to be more often categorized as strings.
+ - Fixed a compilation error under Delphi 5.
+ - Made LogAllocatedBlocksToFile and ScanMemoryPoolForCorruptions thread
+ safe. (Thanks to Francois Piette.)
+ Version 4.88 (13 August 2008):
+ - Fixed compiler warnings in NoOpRegisterExpectedMemoryLeak and
+ NoOpUnRegisterExpectedMemoryLeak. (Thanks to Michael Rabatscher.)
+ - Added the Simplified Chinese translation of FastMM4Options.inc by
+ QianYuan Wang. (Thank you!)
+ - Included the updated C++ Builder files with support for BCB6 without
+ update 4 applied. (Submitted by JiYuan Xie. Thanks!)
+ - Fixed a compilation error under Delphi 5.
+ - Made LogAllocatedBlocksToFile and ScanMemoryPoolForCorruptions thread
+ safe - for real this time. (Thanks to Francois Piette.)
+ Version 4.90 (9 September 2008):
+ - Added logging of the thread ID when capturing and displaying stack
+ traces. (Suggested by Allen Bauer and Mark Edington.)
+ - Fixed a Delphi 5 compiler error under FullDebugMode. (Thanks to Maurizio
+ Lotauro and Christian-W. Budde.)
+ - Changed a default setting in FastMM4Options.inc: RawStackTraces is now
+ off by default due to the high number of support requests I receive with
+ regards to the false postives it may cause. I recommend compiling debug
+ builds of applications with the "Stack Frames" option enabled.
+ - Fixed a compilation error under Kylix. (Thanks to Werner Bochtler.)
+ - Official support for Delphi 2009.
+ Version 4.92 (25 November 2008):
+ - Added the DisableLoggingOfMemoryDumps option under FullDebugMode. When
+ this option is set, memory dumps will not be logged for memory leaks or
+ errors. (Thanks to Patrick van Logchem.)
+ - Exposed the class and string type detection code in the interface section
+ for use in application code (if required). (Requested by Patrick van
+ Logchem.)
+ - Fixed a bug in SetMMLogFileName that could cause the log file name to be
+ set incorrectly.
+ - Added BCB4 support. (Thanks to Norbert Spiegel.)
+ - Included the updated Czech translation by Rene Mihula.
+ - When FastMM raises an error due to a freed block being modified, it now
+ logs detail about which bytes in the block were modified.
+ Version 4.94 (28 August 2009):
+ - Added the DoNotInstallIfDLLMissing option that prevents FastMM from
+ installing itself if the FastMM_FullDebugMode.dll library is not
+ available. (Only applicable when FullDebugMode and LoadDebugDLLDynamically
+ are both enabled.) This is useful when the same executable will be used for
+ both debugging and deployment - when the debug support DLL is available
+ FastMM will be installed in FullDebugMode, and otherwise the default memory
+ manager will be used.
+ - Added the FullDebugModeWhenDLLAvailable option that combines the
+ FullDebugMode, LoadDebugDLLDynamically and DoNotInstallIfDLLMissing options.
+ - Re-enabled RawStackTraces by default. The frame based stack traces (even
+ when compiling with stack frames enabled) are generally too incomplete.
+ - Improved the speed of large block operations under FullDebugMode: Since
+ large blocks are never reused, there is no point in clearing them before
+ and after use (so it does not do that anymore).
+ - If an error occurs in FullDebugMode and FastMM is unable to append to the
+ log file, it will attempt to write to a log file of the same name in the
+ "My Documents" folder. This feature is helpful when the executable resides
+ in a read-only location and the default log file, which is derived from the
+ executable name, would thus not be writeable.
+ - Added support for controlling the error log file location through an
+ environment variable. If the 'FastMMLogFilePath' environment variable is
+ set then any generated error logs will be written to the specified folder
+ instead of the default location (which is the same folder as the
+ application).
+ - Improved the call instruction detection code in the FastMM_FullDebugMode
+ library. (Thanks to the JCL team.)
+ - Improved the string leak detection and reporting code. (Thanks to Uwe
+ Schuster.)
+ - New FullDebugMode feature: Whenever FreeMem or ReallocMem is called, FastMM
+ will check that the block was actually allocated through the same FastMM
+ instance. This is useful for tracking down memory manager sharing issues.
+ - Compatible with Delphi 2010.
+ Version 4.96 (31 August 2010):
+ - Reduced the minimum block size to 4 bytes from the previous value of 12
+ bytes (only applicable to 8 byte alignment). This reduces memory usage if
+ the application allocates many blocks <= 4 bytes in size.
+ - Added colour-coded change indication to the FastMM usage tracker, making
+ it easier to spot changes in the memory usage grid. (Thanks to Murray
+ McGowan.)
+ - Added the SuppressFreeMemErrorsInsideException FullDebugMode option: If
+ FastMM encounters a problem with a memory block inside the FullDebugMode
+ FreeMem handler then an "invalid pointer operation" exception will usually
+ be raised. If the FreeMem occurs while another exception is being handled
+ (perhaps in the try.. finally code) then the original exception will be
+ lost. With this option set FastMM will ignore errors inside FreeMem when an
+ exception is being handled, thus allowing the original exception to
+ propagate. This option is on by default. (Thanks to Michael Hieke.)
+ - Fixed Windows 95 FullDebugMode support that was broken in 4.94. (Thanks to
+ Richard Bradbrook.)
+ - Fixed a bug affecting GetMemoryMap performance and accuracy of measurements
+ above 2GB if a large address space is not enabled for the project. (Thanks
+ to Michael Hieke.)
+ - Added the FullDebugModeRegisterAllAllocsAsExpectedMemoryLeak boolean flag.
+ When set, all allocations are automatically registered as expected memory
+ leaks. Only available in FullDebugMode. (Thanks to Brian Cook.)
+ - Compatible with Delphi XE.
+ Version 4.97 (30 September 2010):
+ - Fixed a crash bug (that crept in in 4.96) that may manifest itself when
+ resizing a block to 4 bytes or less.
+ - Added the UseSwitchToThread option. Set this option to call SwitchToThread
+ instead of sitting in a "busy waiting" loop when a thread contention
+ occurs. This is used in conjunction with the NeverSleepOnThreadContention
+ option, and has no effect unless NeverSleepOnThreadContention is also
+ defined. This option may improve performance with many CPU cores and/or
+ threads of different priorities. Note that the SwitchToThread API call is
+ only available on Windows 2000 and later. (Thanks to Zach Saw.)
+ Version 4.98 (23 September 2011):
+ - Added the FullDebugModeCallBacks define which adds support for memory
+ manager event callbacks. This allows the application to be notified of
+ memory allocations, frees and reallocations as they occur. (Thanks to
+ Jeroen Pluimers.)
+ - Added security options ClearMemoryBeforeReturningToOS and
+ AlwaysClearFreedMemory to force the clearing of memory blocks after being
+ freed. This could possibly provide some protection against information
+ theft, but at a significant performance penalty. (Thanks to Andrey
+ Sozonov.)
+ - Shifted the code in the initialization section to a procedure
+ RunInitializationCode. This allows the startup code to be called before
+ InitUnits, which is required by some software protection tools.
+ - Added support for Delphi XE2 (Windows 32-bit and Windows 64-bit platforms
+ only).
+ Version 4.99 (6 November 2011):
+ - Fixed crashes in the 64-bit BASM codepath when more than 4GB of memory is
+ allocated.
+ - Fixed bad record alignment under 64-bit that affected performance.
+ - Fixed compilation errors with some older compilers.
+ Version 4.??? (? ??? 2012)
+ - Added the LogMemoryManagerStateToFile call. This call logs a summary of
+ the memory manager state to file: The total allocated memory, overhead,
+ efficiency, and a breakdown of allocated memory by class and string type.
+ This call may be useful to catch objects that do not necessarily leak, but
+ do linger longer than they should.
+ - OS X support added by Sebastian Zierer
+
+*)
+
+unit FastMM4;
+
+interface
+
+{$Include FastMM4Options.inc}
+
+{$RANGECHECKS OFF}
+{$BOOLEVAL OFF}
+{$OVERFLOWCHECKS OFF}
+{$OPTIMIZATION ON}
+{$TYPEDADDRESS OFF}
+{$LONGSTRINGS ON}
+
+{Compiler version defines}
+{$ifndef BCB}
+ {$ifdef ver120}
+ {$define Delphi4or5}
+ {$endif}
+ {$ifdef ver130}
+ {$define Delphi4or5}
+ {$endif}
+ {$ifdef ver140}
+ {$define Delphi6}
+ {$endif}
+ {$ifdef ver150}
+ {$define Delphi7}
+ {$endif}
+ {$ifdef ver170}
+ {$define Delphi2005}
+ {$endif}
+{$else}
+ {for BCB4, use the Delphi 5 codepath}
+ {$ifdef ver120}
+ {$define Delphi4or5}
+ {$define BCB4}
+ {$endif}
+ {for BCB5, use the Delphi 5 codepath}
+ {$ifdef ver130}
+ {$define Delphi4or5}
+ {$endif}
+{$endif}
+{$ifdef ver180}
+ {$define BDS2006}
+{$endif}
+{$define 32Bit}
+{$ifndef Delphi4or5}
+ {$if SizeOf(Pointer) = 8}
+ {$define 64Bit}
+ {$undef 32Bit}
+ {$ifend}
+ {$if CompilerVersion >= 23}
+ {$define XE2AndUp}
+ {$ifend}
+ {$define BCB6OrDelphi6AndUp}
+ {$ifndef BCB}
+ {$define Delphi6AndUp}
+ {$endif}
+ {$ifndef Delphi6}
+ {$define BCB6OrDelphi7AndUp}
+ {$ifndef BCB}
+ {$define Delphi7AndUp}
+ {$endif}
+ {$ifndef BCB}
+ {$ifndef Delphi7}
+ {$ifndef Delphi2005}
+ {$define BDS2006AndUp}
+ {$endif}
+ {$endif}
+ {$endif}
+ {$endif}
+{$endif}
+
+{$ifdef 64Bit}
+ {Under 64 bit memory blocks must always be 16-byte aligned}
+ {$define Align16Bytes}
+ {No need for MMX under 64-bit, since SSE2 is available}
+ {$undef EnableMMX}
+ {There is little need for raw stack traces under 64-bit, since frame based
+ stack traces are much more accurate than under 32-bit. (And frame based
+ stack tracing is much faster.)}
+ {$undef RawStackTraces}
+{$endif}
+
+{IDE debug mode always enables FullDebugMode and dynamic loading of the FullDebugMode DLL.}
+{$ifdef FullDebugModeInIDE}
+ {$define InstallOnlyIfRunningInIDE}
+ {$define FullDebugMode}
+ {$define LoadDebugDLLDynamically}
+{$endif}
+
+{Install in FullDebugMode only when the DLL is available?}
+{$ifdef FullDebugModeWhenDLLAvailable}
+ {$define FullDebugMode}
+ {$define LoadDebugDLLDynamically}
+ {$define DoNotInstallIfDLLMissing}
+{$endif}
+
+{$ifdef Linux}
+ {$define POSIX}
+{$endif}
+
+{Some features not currently supported under Kylix / OS X}
+{$ifdef POSIX}
+ {$undef FullDebugMode}
+ {$undef LogErrorsToFile}
+ {$undef LogMemoryLeakDetailToFile}
+ {$undef ShareMM}
+ {$undef AttemptToUseSharedMM}
+ {$undef RequireIDEPresenceForLeakReporting}
+ {$undef UseOutputDebugString}
+ {$ifdef PIC}
+ {BASM version does not support position independent code}
+ {$undef ASMVersion}
+ {$endif}
+{$endif}
+
+{Do we require debug info for leak checking?}
+{$ifdef RequireDebugInfoForLeakReporting}
+ {$ifopt D-}
+ {$undef EnableMemoryLeakReporting}
+ {$endif}
+{$endif}
+
+{Enable heap checking and leak reporting in full debug mode}
+{$ifdef FullDebugMode}
+ {$STACKFRAMES ON}
+ {$define CheckHeapForCorruption}
+ {$ifndef CatchUseOfFreedInterfaces}
+ {$define CheckUseOfFreedBlocksOnShutdown}
+ {$endif}
+{$else}
+ {Error logging requires FullDebugMode}
+ {$undef LogErrorsToFile}
+ {$undef CatchUseOfFreedInterfaces}
+ {$undef RawStackTraces}
+ {$undef AlwaysAllocateTopDown}
+{$endif}
+
+{Set defines for security options}
+{$ifdef FullDebugMode}
+ {In FullDebugMode small and medium blocks are always cleared when calling
+ FreeMem. Large blocks are always returned to the OS immediately.}
+ {$ifdef ClearMemoryBeforeReturningToOS}
+ {$define ClearLargeBlocksBeforeReturningToOS}
+ {$endif}
+ {$ifdef AlwaysClearFreedMemory}
+ {$define ClearLargeBlocksBeforeReturningToOS}
+ {$endif}
+{$else}
+ {If memory blocks are cleared in FreeMem then they do not need to be cleared
+ before returning the memory to the OS.}
+ {$ifdef AlwaysClearFreedMemory}
+ {$define ClearSmallAndMediumBlocksInFreeMem}
+ {$define ClearLargeBlocksBeforeReturningToOS}
+ {$else}
+ {$ifdef ClearMemoryBeforeReturningToOS}
+ {$define ClearMediumBlockPoolsBeforeReturningToOS}
+ {$define ClearLargeBlocksBeforeReturningToOS}
+ {$endif}
+ {$endif}
+{$endif}
+
+{Only the Pascal version supports extended heap corruption checking.}
+{$ifdef CheckHeapForCorruption}
+ {$undef ASMVersion}
+{$endif}
+
+{For BASM bits that are not implemented in 64-bit.}
+{$ifdef 32Bit}
+ {$ifdef ASMVersion}
+ {$define Use32BitAsm}
+ {$endif}
+{$endif}
+
+{$ifdef UseRuntimePackages}
+ {$define AssumeMultiThreaded}
+{$endif}
+
+{$ifdef BCB6OrDelphi6AndUp}
+ {$WARN SYMBOL_PLATFORM OFF}
+ {$WARN SYMBOL_DEPRECATED OFF}
+{$endif}
+
+{Leak detail logging requires error logging}
+{$ifndef LogErrorsToFile}
+ {$undef LogMemoryLeakDetailToFile}
+ {$undef ClearLogFileOnStartup}
+{$endif}
+
+{$ifndef EnableMemoryLeakReporting}
+ {Manual leak reporting control requires leak reporting to be enabled}
+ {$undef ManualLeakReportingControl}
+{$endif}
+
+{$ifndef EnableMMX}
+ {$undef ForceMMX}
+{$endif}
+
+{Are any of the MM sharing options enabled?}
+{$ifdef ShareMM}
+ {$define MMSharingEnabled}
+{$endif}
+{$ifdef AttemptToUseSharedMM}
+ {$define MMSharingEnabled}
+{$endif}
+
+{Instruct GExperts to back up the messages file as well.}
+{#BACKUP FastMM4Messages.pas}
+
+{Should debug info be disabled?}
+{$ifdef NoDebugInfo}
+ {$DEBUGINFO OFF}
+{$endif}
+
+{$ifdef BCB}
+ {$ifdef borlndmmdll}
+ {$OBJEXPORTALL OFF}
+ {$endif}
+ {$ifndef PatchBCBTerminate}
+ {Cannot uninstall safely under BCB}
+ {$define NeverUninstall}
+ {Disable memory leak reporting}
+ {$undef EnableMemoryLeakReporting}
+ {$endif}
+{$endif}
+
+{-------------------------Public constants-----------------------------}
+const
+ {The current version of FastMM}
+ FastMMVersion = '4.99';
+ {The number of small block types}
+{$ifdef Align16Bytes}
+ NumSmallBlockTypes = 46;
+{$else}
+ NumSmallBlockTypes = 56;
+{$endif}
+
+{----------------------------Public types------------------------------}
+type
+
+ {Make sure all the required types are available}
+{$ifdef BCB6OrDelphi6AndUp}
+ {$if CompilerVersion < 20}
+ PByte = PAnsiChar;
+ {NativeInt didn't exist or was broken before Delphi 2009.}
+ NativeInt = Integer;
+ {$ifend}
+ {$if CompilerVersion < 21}
+ {NativeUInt didn't exist or was broken before Delphi 2010.}
+ NativeUInt = Cardinal;
+ {$ifend}
+ {$if CompilerVersion < 22}
+ {PNativeUInt didn't exist before Delphi XE.}
+ PNativeUInt = ^Cardinal;
+ {$ifend}
+ {$if CompilerVersion < 23}
+ {IntPtr and UIntPtr didn't exist before Delphi XE2.}
+ IntPtr = Integer;
+ UIntPtr = Cardinal;
+ {$ifend}
+{$else}
+ PByte = PAnsiChar;
+ NativeInt = Integer;
+ NativeUInt = Cardinal;
+ PNativeUInt = ^Cardinal;
+ IntPtr = Integer;
+ UIntPtr = Cardinal;
+{$endif}
+
+ TSmallBlockTypeState = record
+ {The internal size of the block type}
+ InternalBlockSize: Cardinal;
+ {Useable block size: The number of non-reserved bytes inside the block.}
+ UseableBlockSize: Cardinal;
+ {The number of allocated blocks}
+ AllocatedBlockCount: NativeUInt;
+ {The total address space reserved for this block type (both allocated and
+ free blocks)}
+ ReservedAddressSpace: NativeUInt;
+ end;
+ TSmallBlockTypeStates = array[0..NumSmallBlockTypes - 1] of TSmallBlockTypeState;
+
+ TMemoryManagerState = record
+ {Small block type states}
+ SmallBlockTypeStates: TSmallBlockTypeStates;
+ {Medium block stats}
+ AllocatedMediumBlockCount: Cardinal;
+ TotalAllocatedMediumBlockSize: NativeUInt;
+ ReservedMediumBlockAddressSpace: NativeUInt;
+ {Large block stats}
+ AllocatedLargeBlockCount: Cardinal;
+ TotalAllocatedLargeBlockSize: NativeUInt;
+ ReservedLargeBlockAddressSpace: NativeUInt;
+ end;
+
+ TMemoryManagerUsageSummary = record
+ {The total number of bytes allocated by the application.}
+ AllocatedBytes: NativeUInt;
+ {The total number of address space bytes used by control structures, or
+ lost due to fragmentation and other overhead.}
+ OverheadBytes: NativeUInt;
+ {The efficiency of the memory manager expressed as a percentage. This is
+ 100 * AllocatedBytes / (AllocatedBytes + OverheadBytes).}
+ EfficiencyPercentage: Double;
+ end;
+
+ {Memory map}
+ TChunkStatus = (csUnallocated, csAllocated, csReserved, csSysAllocated,
+ csSysReserved);
+ TMemoryMap = array[0..65535] of TChunkStatus;
+
+{$ifdef EnableMemoryLeakReporting}
+ {List of registered leaks}
+ TRegisteredMemoryLeak = record
+ LeakAddress: Pointer;
+ LeakedClass: TClass;
+ {$ifdef CheckCppObjectTypeEnabled}
+ LeakedCppTypeIdPtr: Pointer;
+ {$endif}
+ LeakSize: NativeInt;
+ LeakCount: Integer;
+ end;
+ TRegisteredMemoryLeaks = array of TRegisteredMemoryLeak;
+{$endif}
+
+ {Used by the DetectStringData routine to detect whether a leaked block
+ contains string data.}
+ TStringDataType = (stUnknown, stAnsiString, stUnicodeString);
+
+ {The callback procedure for WalkAllocatedBlocks.}
+ TWalkAllocatedBlocksCallback = procedure(APBlock: Pointer; ABlockSize: NativeInt; AUserData: Pointer);
+
+{--------------------------Public variables----------------------------}
+var
+ {If this variable is set to true and FullDebugMode is enabled, then the
+ entire memory pool is checked for consistency before every memory
+ operation. Note that this incurs a massive performance hit on top of
+ the already significant FullDebugMode overhead, so enable this option
+ only when absolutely necessary.}
+ FullDebugModeScanMemoryPoolBeforeEveryOperation: Boolean = False;
+ FullDebugModeRegisterAllAllocsAsExpectedMemoryLeak: Boolean = False;
+{$ifdef ManualLeakReportingControl}
+ {Variable is declared in system.pas in newer Delphi versions.}
+ {$ifndef BDS2006AndUp}
+ ReportMemoryLeaksOnShutdown: Boolean;
+ {$endif}
+{$endif}
+ {If set to True, disables the display of all messageboxes}
+ SuppressMessageBoxes: Boolean;
+
+{-------------------------Public procedures----------------------------}
+{Executes the code normally run in the initialization section. Running it
+ earlier may be required with e.g. some software protection tools.}
+procedure RunInitializationCode;
+{Installation procedures must be exposed for the BCB helper unit FastMM4BCB.cpp}
+{$ifdef BCB}
+procedure InitializeMemoryManager;
+function CheckCanInstallMemoryManager: Boolean;
+procedure InstallMemoryManager;
+
+{$ifdef FullDebugMode}
+(*$HPPEMIT '#define FullDebugMode' *)
+
+{$ifdef ClearLogFileOnStartup}
+(*$HPPEMIT ' #define ClearLogFileOnStartup' *)
+procedure DeleteEventLog;
+{$endif}
+
+{$ifdef LoadDebugDLLDynamically}
+(*$HPPEMIT ' #define LoadDebugDLLDynamically' *)
+{$endif}
+
+{$ifdef RawStackTraces}
+(*$HPPEMIT ' #define RawStackTraces' *)
+{$endif}
+
+{$endif}
+
+{$ifdef PatchBCBTerminate}
+(*$HPPEMIT ''#13#10 *)
+(*$HPPEMIT '#define PatchBCBTerminate' *)
+
+{$ifdef EnableMemoryLeakReporting}
+(*$HPPEMIT ''#13#10 *)
+(*$HPPEMIT '#define EnableMemoryLeakReporting' *)
+{$endif}
+
+{$ifdef DetectMMOperationsAfterUninstall}
+(*$HPPEMIT ''#13#10 *)
+(*$HPPEMIT '#define DetectMMOperationsAfterUninstall' *)
+{$endif}
+
+{Called in FastMM4BCB.cpp, should contain codes of original "finalization" section}
+procedure FinalizeMemoryManager;
+
+{For completion of "RequireDebuggerPresenceForLeakReporting" checking in "FinalizeMemoryManager"}
+var
+ pCppDebugHook: ^Integer = nil; //PInteger not defined in BCB5
+
+{$ifdef CheckCppObjectTypeEnabled}
+(*$HPPEMIT ''#13#10 *)
+(*$HPPEMIT '#define CheckCppObjectTypeEnabled' *)
+
+type
+ TGetCppVirtObjSizeByTypeIdPtrFunc = function(APointer: Pointer): Cardinal;
+ TGetCppVirtObjTypeIdPtrFunc = function(APointer: Pointer; ASize: Cardinal): Pointer;
+ TGetCppVirtObjTypeNameFunc = function(APointer: Pointer; ASize: Cardinal): PAnsiChar;
+ TGetCppVirtObjTypeNameByTypeIdPtrFunc = function (APointer: Pointer): PAnsiChar;
+ TGetCppVirtObjTypeNameByVTablePtrFunc = function(AVTablePtr: Pointer; AVTablePtrOffset: Cardinal): PAnsiChar;
+var
+ {Return virtual object's size from typeId pointer}
+ GetCppVirtObjSizeByTypeIdPtrFunc: TGetCppVirtObjSizeByTypeIdPtrFunc = nil;
+ {Retrieve virtual object's typeId pointer}
+ GetCppVirtObjTypeIdPtrFunc: TGetCppVirtObjTypeIdPtrFunc = nil;
+ {Retrieve virtual object's type name}
+ GetCppVirtObjTypeNameFunc: TGetCppVirtObjTypeNameFunc = nil;
+ {Return virtual object's type name from typeId pointer}
+ GetCppVirtObjTypeNameByTypeIdPtrFunc: TGetCppVirtObjTypeNameByTypeIdPtrFunc = nil;
+ {Retrieve virtual object's typeId pointer from it's virtual table pointer}
+ GetCppVirtObjTypeNameByVTablePtrFunc: TGetCppVirtObjTypeNameByVTablePtrFunc = nil;
+{$endif}
+{$endif}
+{$endif}
+
+{$ifndef FullDebugMode}
+{The standard memory manager functions}
+function FastGetMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
+function FastFreeMem(APointer: Pointer): Integer;
+function FastReallocMem(APointer: Pointer; ANewSize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
+function FastAllocMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Cardinal{$endif}): Pointer;
+{$else}
+{The FullDebugMode memory manager functions}
+function DebugGetMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
+function DebugFreeMem(APointer: Pointer): Integer;
+function DebugReallocMem(APointer: Pointer; ANewSize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
+function DebugAllocMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Cardinal{$endif}): Pointer;
+{Scans the memory pool for any corruptions. If a corruption is encountered an "Out of Memory" exception is
+ raised.}
+procedure ScanMemoryPoolForCorruptions;
+{Specify the full path and name for the filename to be used for logging memory
+ errors, etc. If ALogFileName is nil or points to an empty string it will
+ revert to the default log file name.}
+procedure SetMMLogFileName(ALogFileName: PAnsiChar = nil);
+{Returns the current "allocation group". Whenever a GetMem request is serviced
+ in FullDebugMode, the current "allocation group" is stored in the block header.
+ This may help with debugging. Note that if a block is subsequently reallocated
+ that it keeps its original "allocation group" and "allocation number" (all
+ allocations are also numbered sequentially).}
+function GetCurrentAllocationGroup: Cardinal;
+{Allocation groups work in a stack like fashion. Group numbers are pushed onto
+ and popped off the stack. Note that the stack size is limited, so every push
+ should have a matching pop.}
+procedure PushAllocationGroup(ANewCurrentAllocationGroup: Cardinal);
+procedure PopAllocationGroup;
+{Logs detail about currently allocated memory blocks for the specified range of
+ allocation groups. if ALastAllocationGroupToLog is less than
+ AFirstAllocationGroupToLog or it is zero, then all allocation groups are
+ logged. This routine also checks the memory pool for consistency at the same
+ time, raising an "Out of Memory" error if the check fails.}
+procedure LogAllocatedBlocksToFile(AFirstAllocationGroupToLog, ALastAllocationGroupToLog: Cardinal);
+{$endif}
+
+{Releases all allocated memory (use with extreme care)}
+procedure FreeAllMemory;
+
+{Returns summarised information about the state of the memory manager. (For
+ backward compatibility.)}
+function FastGetHeapStatus: THeapStatus;
+{Returns statistics about the current state of the memory manager}
+procedure GetMemoryManagerState(var AMemoryManagerState: TMemoryManagerState);
+{Returns a summary of the information returned by GetMemoryManagerState}
+procedure GetMemoryManagerUsageSummary(
+ var AMemoryManagerUsageSummary: TMemoryManagerUsageSummary);
+{$ifndef POSIX}
+{Gets the state of every 64K block in the 4GB address space}
+procedure GetMemoryMap(var AMemoryMap: TMemoryMap);
+{$endif}
+
+{$ifdef EnableMemoryLeakReporting}
+{Registers expected memory leaks. Returns true on success. The list of leaked
+ blocks is limited, so failure is possible if the list is full.}
+function RegisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean; overload;
+function RegisterExpectedMemoryLeak(ALeakedObjectClass: TClass; ACount: Integer = 1): Boolean; overload;
+function RegisterExpectedMemoryLeak(ALeakedBlockSize: NativeInt; ACount: Integer = 1): Boolean; overload;
+{$ifdef CheckCppObjectTypeEnabled}
+{Registers expected memory leaks by virtual object's typeId pointer.
+ Usage: RegisterExpectedMemoryLeak(typeid(ACppObject).tpp, Count);}
+function RegisterExpectedMemoryLeak(ALeakedCppVirtObjTypeIdPtr: Pointer; ACount: Integer): boolean; overload;
+{$endif}
+{Removes expected memory leaks. Returns true on success.}
+function UnregisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean; overload;
+function UnregisterExpectedMemoryLeak(ALeakedObjectClass: TClass; ACount: Integer = 1): Boolean; overload;
+function UnregisterExpectedMemoryLeak(ALeakedBlockSize: NativeInt; ACount: Integer = 1): Boolean; overload;
+{$ifdef CheckCppObjectTypeEnabled}
+{Usage: UnregisterExpectedMemoryLeak(typeid(ACppObject).tpp, Count);}
+function UnregisterExpectedMemoryLeak(ALeakedCppVirtObjTypeIdPtr: Pointer; ACount: Integer): boolean; overload;
+{$endif}
+{Returns a list of all expected memory leaks}
+function GetRegisteredMemoryLeaks: TRegisteredMemoryLeaks;
+{$endif}
+
+{Returns the class for a memory block. Returns nil if it is not a valid class.
+ Used by the leak detection code.}
+function DetectClassInstance(APointer: Pointer): TClass;
+{Detects the probable string data type for a memory block. Used by the leak
+ classification code when a block cannot be identified as a known class
+ instance.}
+function DetectStringData(APMemoryBlock: Pointer;
+ AAvailableSpaceInBlock: NativeInt): TStringDataType;
+{Walks all allocated blocks, calling ACallBack for each. Passes the user block size and AUserData to the callback.
+ Important note: All block types will be locked during the callback, so the memory manager cannot be used inside it.}
+procedure WalkAllocatedBlocks(ACallBack: TWalkAllocatedBlocksCallback; AUserData: Pointer);
+{Writes a log file containing a summary of the memory mananger state and a summary of allocated blocks grouped by
+ class. The file will be saved in UTF-8 encoding (in supported Delphi versions). Returns True on success. }
+function LogMemoryManagerStateToFile(const AFileName: string; const AAdditionalDetails: string = ''): Boolean;
+
+{$ifdef FullDebugMode}
+{-------------FullDebugMode constants---------------}
+const
+ {The stack trace depth. (Must be an *uneven* number to ensure that the
+ Align16Bytes option works in FullDebugMode.)}
+ StackTraceDepth = 11;
+ {The number of entries in the allocation group stack}
+ AllocationGroupStackSize = 1000;
+ {The number of fake VMT entries - used to track virtual method calls on
+ freed objects. Do not change this value without also updating TFreedObject.GetVirtualMethodIndex}
+ MaxFakeVMTEntries = 200;
+ {The pattern used to fill unused memory}
+ DebugFillByte = $80;
+{$ifdef 32Bit}
+ DebugFillPattern = $01010101 * Cardinal(DebugFillByte);
+ {The address that is reserved so that accesses to the address of the fill
+ pattern will result in an A/V. (Not used under 64-bit, since the upper half
+ of the address space is always reserved by the OS.)}
+ DebugReservedAddress = $01010000 * Cardinal(DebugFillByte);
+{$else}
+ DebugFillPattern = $8080808080808080;
+{$endif}
+
+{-------------------------FullDebugMode structures--------------------}
+type
+ PStackTrace = ^TStackTrace;
+ TStackTrace = array[0..StackTraceDepth - 1] of NativeUInt;
+
+ TBlockOperation = (boBlockCheck, boGetMem, boFreeMem, boReallocMem);
+
+ {The header placed in front of blocks in FullDebugMode (just after the
+ standard header). Must be a multiple of 16 bytes in size otherwise the
+ Align16Bytes option will not work. Current size = 128 bytes under 32-bit,
+ and 240 bytes under 64-bit.}
+ PFullDebugBlockHeader = ^TFullDebugBlockHeader;
+ TFullDebugBlockHeader = record
+ {Space used by the medium block manager for previous/next block management.
+ If a medium block is binned then these two fields will be modified.}
+ Reserved1: Pointer;
+ Reserved2: Pointer;
+ {Is the block currently allocated? If it is allocated this will be the
+ address of the getmem routine through which it was allocated, otherwise it
+ will be nil.}
+ AllocatedByRoutine: Pointer;
+ {The allocation group: Can be used in the debugging process to group
+ related memory leaks together}
+ AllocationGroup: Cardinal;
+ {The allocation number: All new allocations are numbered sequentially. This
+ number may be useful in memory leak analysis. If it reaches 4G it wraps
+ back to 0.}
+ AllocationNumber: Cardinal;
+ {The call stack when the block was allocated}
+ AllocationStackTrace: TStackTrace;
+ {The thread that allocated the block}
+ AllocatedByThread: Cardinal;
+ {The thread that freed the block}
+ FreedByThread: Cardinal;
+ {The call stack when the block was freed}
+ FreeStackTrace: TStackTrace;
+ {The user requested size for the block. 0 if this is the first time the
+ block is used.}
+ UserSize: NativeUInt;
+ {The object class this block was used for the previous time it was
+ allocated. When a block is freed, the pointer that would normally be in the
+ space of the class pointer is copied here, so if it is detected that
+ the block was used after being freed we have an idea what class it is.}
+ PreviouslyUsedByClass: NativeUInt;
+ {The sum of all the dwords(32-bit)/qwords(64-bit) in this structure
+ excluding the initial two reserved fields and this field.}
+ HeaderCheckSum: NativeUInt;
+ end;
+ {The NativeUInt following the user area of the block is the inverse of
+ HeaderCheckSum. This is used to catch buffer overrun errors.}
+
+ {The class used to catch attempts to execute a virtual method of a freed
+ object}
+ TFreedObject = class
+ public
+ procedure GetVirtualMethodIndex;
+ procedure VirtualMethodError;
+{$ifdef CatchUseOfFreedInterfaces}
+ procedure InterfaceError;
+{$endif}
+ end;
+
+{$ifdef FullDebugModeCallBacks}
+ {FullDebugMode memory manager event callbacks. Note that APHeaderFreedBlock in the TOnDebugFreeMemFinish
+ will not be valid for large (>260K) blocks.}
+ TOnDebugGetMemFinish = procedure(APHeaderNewBlock: PFullDebugBlockHeader; ASize: NativeInt);
+ TOnDebugFreeMemStart = procedure(APHeaderBlockToFree: PFullDebugBlockHeader);
+ TOnDebugFreeMemFinish = procedure(APHeaderFreedBlock: PFullDebugBlockHeader; AResult: Integer);
+ TOnDebugReallocMemStart = procedure(APHeaderBlockToReallocate: PFullDebugBlockHeader; ANewSize: NativeInt);
+ TOnDebugReallocMemFinish = procedure(APHeaderReallocatedBlock: PFullDebugBlockHeader; ANewSize: NativeInt);
+
+var
+ {Note: FastMM will not catch exceptions inside these hooks, so make sure your hook code runs without
+ exceptions.}
+ OnDebugGetMemFinish: TOnDebugGetMemFinish = nil;
+ OnDebugFreeMemStart: TOnDebugFreeMemStart = nil;
+ OnDebugFreeMemFinish: TOnDebugFreeMemFinish = nil;
+ OnDebugReallocMemStart: TOnDebugReallocMemStart = nil;
+ OnDebugReallocMemFinish: TOnDebugReallocMemFinish = nil;
+{$endif}
+{$endif}
+
+implementation
+
+uses
+{$ifndef POSIX}
+ Windows,
+ {$ifdef FullDebugMode}
+ {$ifdef Delphi4or5}
+ ShlObj,
+ {$else}
+ SHFolder,
+ {$endif}
+ {$endif}
+{$else}
+ {$ifdef MACOS}
+ Posix.Stdlib, Posix.Unistd, Posix.Fcntl,
+ {$ELSE}
+ Libc,
+ {$endif}
+{$endif}
+ FastMM4Messages;
+
+{Fixed size move procedures. The 64-bit versions assume 16-byte alignment.}
+procedure Move4(const ASource; var ADest; ACount: NativeInt); forward;
+procedure Move12(const ASource; var ADest; ACount: NativeInt); forward;
+procedure Move20(const ASource; var ADest; ACount: NativeInt); forward;
+procedure Move28(const ASource; var ADest; ACount: NativeInt); forward;
+procedure Move36(const ASource; var ADest; ACount: NativeInt); forward;
+procedure Move44(const ASource; var ADest; ACount: NativeInt); forward;
+procedure Move52(const ASource; var ADest; ACount: NativeInt); forward;
+procedure Move60(const ASource; var ADest; ACount: NativeInt); forward;
+procedure Move68(const ASource; var ADest; ACount: NativeInt); forward;
+{$ifdef 64Bit}
+{These are not needed and thus unimplemented under 32-bit}
+procedure Move8(const ASource; var ADest; ACount: NativeInt); forward;
+procedure Move24(const ASource; var ADest; ACount: NativeInt); forward;
+procedure Move40(const ASource; var ADest; ACount: NativeInt); forward;
+procedure Move56(const ASource; var ADest; ACount: NativeInt); forward;
+{$endif}
+
+{$ifdef DetectMMOperationsAfterUninstall}
+{Invalid handlers to catch MM operations after uninstall}
+function InvalidFreeMem(APointer: Pointer): Integer; forward;
+function InvalidGetMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer; forward;
+function InvalidReallocMem(APointer: Pointer; ANewSize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer; forward;
+function InvalidAllocMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Cardinal{$endif}): Pointer; forward;
+function InvalidRegisterAndUnRegisterMemoryLeak(APointer: Pointer): Boolean; forward;
+{$endif}
+
+{-------------------------Private constants----------------------------}
+const
+ {The size of a medium block pool. This is allocated through VirtualAlloc and
+ is used to serve medium blocks. The size must be a multiple of 16 and at
+ least 4 bytes less than a multiple of 4K (the page size) to prevent a
+ possible read access violation when reading past the end of a memory block
+ in the optimized move routine (MoveX16LP). In Full Debug mode we leave a
+ trailing 256 bytes to be able to safely do a memory dump.}
+ MediumBlockPoolSize = 20 * 64 * 1024{$ifndef FullDebugMode} - 16{$else} - 256{$endif};
+ {The granularity of small blocks}
+{$ifdef Align16Bytes}
+ SmallBlockGranularity = 16;
+{$else}
+ SmallBlockGranularity = 8;
+{$endif}
+ {The granularity of medium blocks. Newly allocated medium blocks are
+ a multiple of this size plus MediumBlockSizeOffset, to avoid cache line
+ conflicts}
+ MediumBlockGranularity = 256;
+ MediumBlockSizeOffset = 48;
+ {The granularity of large blocks}
+ LargeBlockGranularity = 65536;
+ {The maximum size of a small block. Blocks Larger than this are either
+ medium or large blocks.}
+ MaximumSmallBlockSize = 2608;
+ {The smallest medium block size. (Medium blocks are rounded up to the nearest
+ multiple of MediumBlockGranularity plus MediumBlockSizeOffset)}
+ MinimumMediumBlockSize = 11 * 256 + MediumBlockSizeOffset;
+ {The number of bins reserved for medium blocks}
+ MediumBlockBinsPerGroup = 32;
+ MediumBlockBinGroupCount = 32;
+ MediumBlockBinCount = MediumBlockBinGroupCount * MediumBlockBinsPerGroup;
+ {The maximum size allocatable through medium blocks. Blocks larger than this
+ fall through to VirtualAlloc ( = large blocks).}
+ MaximumMediumBlockSize = MinimumMediumBlockSize + (MediumBlockBinCount - 1) * MediumBlockGranularity;
+ {The target number of small blocks per pool. The actual number of blocks per
+ pool may be much greater for very small sizes and less for larger sizes. The
+ cost of allocating the small block pool is amortized across all the small
+ blocks in the pool, however the blocks may not all end up being used so they
+ may be lying idle.}
+ TargetSmallBlocksPerPool = 48;
+ {The minimum number of small blocks per pool. Any available medium block must
+ have space for roughly this many small blocks (or more) to be useable as a
+ small block pool.}
+ MinimumSmallBlocksPerPool = 12;
+ {The lower and upper limits for the optimal small block pool size}
+ OptimalSmallBlockPoolSizeLowerLimit = 29 * 1024 - MediumBlockGranularity + MediumBlockSizeOffset;
+ OptimalSmallBlockPoolSizeUpperLimit = 64 * 1024 - MediumBlockGranularity + MediumBlockSizeOffset;
+ {The maximum small block pool size. If a free block is this size or larger
+ then it will be split.}
+ MaximumSmallBlockPoolSize = OptimalSmallBlockPoolSizeUpperLimit + MinimumMediumBlockSize;
+ {-------------Block type flags--------------}
+ {The lower 3 bits in the dword header of small blocks (4 bits in medium and
+ large blocks) are used as flags to indicate the state of the block}
+ {Set if the block is not in use}
+ IsFreeBlockFlag = 1;
+ {Set if this is a medium block}
+ IsMediumBlockFlag = 2;
+ {Set if it is a medium block being used as a small block pool. Only valid if
+ IsMediumBlockFlag is set.}
+ IsSmallBlockPoolInUseFlag = 4;
+ {Set if it is a large block. Only valid if IsMediumBlockFlag is not set.}
+ IsLargeBlockFlag = 4;
+ {Is the medium block preceding this block available? (Only used by medium
+ blocks)}
+ PreviousMediumBlockIsFreeFlag = 8;
+ {Is this large block segmented? I.e. is it actually built up from more than
+ one chunk allocated through VirtualAlloc? (Only used by large blocks.)}
+ LargeBlockIsSegmented = 8;
+ {The flags masks for small blocks}
+ DropSmallFlagsMask = -8;
+ ExtractSmallFlagsMask = 7;
+ {The flags masks for medium and large blocks}
+ DropMediumAndLargeFlagsMask = -16;
+ ExtractMediumAndLargeFlagsMask = 15;
+ {-------------Block resizing constants---------------}
+ SmallBlockDownsizeCheckAdder = 64;
+ SmallBlockUpsizeAdder = 32;
+ {When a medium block is reallocated to a size smaller than this, then it must
+ be reallocated to a small block and the data moved. If not, then it is
+ shrunk in place down to MinimumMediumBlockSize. Currently the limit is set
+ at a quarter of the minimum medium block size.}
+ MediumInPlaceDownsizeLimit = MinimumMediumBlockSize div 4;
+ {-------------Memory leak reporting constants---------------}
+ ExpectedMemoryLeaksListSize = 64 * 1024;
+ {-------------Other constants---------------}
+{$ifndef NeverSleepOnThreadContention}
+ {Sleep time when a resource (small/medium/large block manager) is in use}
+ InitialSleepTime = 0;
+ {Used when the resource is still in use after the first sleep}
+ AdditionalSleepTime = 1;
+{$endif}
+ {Hexadecimal characters}
+ HexTable: array[0..15] of AnsiChar = ('0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');
+ {Copyright message - not used anywhere in the code}
+ Copyright: AnsiString = 'FastMM4 (c) 2004 - 2011 Pierre le Riche / Professional Software Development';
+{$ifdef FullDebugMode}
+ {Virtual Method Called On Freed Object Errors}
+ StandardVirtualMethodNames: array[1 + vmtParent div SizeOf(Pointer) .. vmtDestroy div SizeOf(Pointer)] of PAnsiChar = (
+{$ifdef BCB6OrDelphi6AndUp}
+ {$if RTLVersion >= 20}
+ 'Equals',
+ 'GetHashCode',
+ 'ToString',
+ {$ifend}
+{$endif}
+ 'SafeCallException',
+ 'AfterConstruction',
+ 'BeforeDestruction',
+ 'Dispatch',
+ 'DefaultHandler',
+ 'NewInstance',
+ 'FreeInstance',
+ 'Destroy');
+ {The name of the FullDebugMode support DLL. The support DLL implements stack
+ tracing and the conversion of addresses to unit and line number information.}
+{$ifdef 32Bit}
+ FullDebugModeLibraryName = FullDebugModeLibraryName32Bit;
+{$else}
+ FullDebugModeLibraryName = FullDebugModeLibraryName64Bit;
+{$endif}
+{$endif}
+
+{-------------------------Private types----------------------------}
+type
+
+{$ifdef Delphi4or5}
+ {Delphi 5 Compatibility}
+ PCardinal = ^Cardinal;
+ PPointer = ^Pointer;
+{$endif}
+{$ifdef BCB4}
+ {Define some additional types for BCB4}
+ PInteger = ^Integer;
+{$endif}
+
+ {Move procedure type}
+ TMoveProc = procedure(const ASource; var ADest; ACount: NativeInt);
+
+ {Registers structure (for GetCPUID)}
+ TRegisters = record
+ RegEAX, RegEBX, RegECX, RegEDX: Integer;
+ end;
+
+ {The layout of a string allocation. Used to detect string leaks.}
+ PStrRec = ^StrRec;
+ StrRec = packed record
+{$ifdef 64Bit}
+ _Padding: Integer;
+{$endif}
+{$ifdef BCB6OrDelphi6AndUp}
+ {$if RTLVersion >= 20}
+ codePage: Word;
+ elemSize: Word;
+ {$ifend}
+{$endif}
+ refCnt: Integer;
+ length: Integer;
+ end;
+
+{$ifdef EnableMemoryLeakReporting}
+ {Different kinds of memory leaks}
+ TMemoryLeakType = (mltUnexpectedLeak, mltExpectedLeakRegisteredByPointer,
+ mltExpectedLeakRegisteredByClass, mltExpectedLeakRegisteredBySize);
+{$endif}
+
+ {---------------Small block structures-------------}
+
+ {Pointer to the header of a small block pool}
+ PSmallBlockPoolHeader = ^TSmallBlockPoolHeader;
+
+ {Small block type (Size = 32 bytes for 32-bit, 64 bytes for 64-bit).}
+ PSmallBlockType = ^TSmallBlockType;
+ TSmallBlockType = record
+ {True = Block type is locked}
+ BlockTypeLocked: Boolean;
+ {Bitmap indicating which of the first 8 medium block groups contain blocks
+ of a suitable size for a block pool.}
+ AllowedGroupsForBlockPoolBitmap: Byte;
+ {The block size for this block type}
+ BlockSize: Word;
+ {The minimum and optimal size of a small block pool for this block type}
+ MinimumBlockPoolSize: Word;
+ OptimalBlockPoolSize: Word;
+ {The first partially free pool for the given small block. This field must
+ be at the same offset as TSmallBlockPoolHeader.NextPartiallyFreePool.}
+ NextPartiallyFreePool: PSmallBlockPoolHeader;
+ {The last partially free pool for the small block type. This field must
+ be at the same offset as TSmallBlockPoolHeader.PreviousPartiallyFreePool.}
+ PreviousPartiallyFreePool: PSmallBlockPoolHeader;
+ {The offset of the last block that was served sequentially. The field must
+ be at the same offset as TSmallBlockPoolHeader.FirstFreeBlock.}
+ NextSequentialFeedBlockAddress: Pointer;
+ {The last block that can be served sequentially.}
+ MaxSequentialFeedBlockAddress: Pointer;
+ {The pool that is current being used to serve blocks in sequential order}
+ CurrentSequentialFeedPool: PSmallBlockPoolHeader;
+{$ifdef UseCustomFixedSizeMoveRoutines}
+ {The fixed size move procedure used to move data for this block size when
+ it is upsized. When a block is downsized (which usually does not occur
+ that often) the variable size move routine is used.}
+ UpsizeMoveProcedure: TMoveProc;
+{$else}
+ Reserved1: Pointer;
+{$endif}
+{$ifdef 64Bit}
+ {Pad to 64 bytes for 64-bit}
+ Reserved2: Pointer;
+{$endif}
+ end;
+
+ {Small block pool (Size = 32 bytes for 32-bit, 48 bytes for 64-bit).}
+ TSmallBlockPoolHeader = record
+ {BlockType}
+ BlockType: PSmallBlockType;
+{$ifdef 32Bit}
+ {Align the next fields to the same fields in TSmallBlockType and pad this
+ structure to 32 bytes for 32-bit}
+ Reserved1: Cardinal;
+{$endif}
+ {The next and previous pool that has free blocks of this size. Do not
+ change the position of these two fields: They must be at the same offsets
+ as the fields in TSmallBlockType of the same name.}
+ NextPartiallyFreePool: PSmallBlockPoolHeader;
+ PreviousPartiallyFreePool: PSmallBlockPoolHeader;
+ {Pointer to the first free block inside this pool. This field must be at
+ the same offset as TSmallBlockType.NextSequentialFeedBlockAddress.}
+ FirstFreeBlock: Pointer;
+ {The number of blocks allocated in this pool.}
+ BlocksInUse: Cardinal;
+ {Padding}
+ Reserved2: Cardinal;
+ {The pool pointer and flags of the first block}
+ FirstBlockPoolPointerAndFlags: NativeUInt;
+ end;
+
+ {Small block layout:
+ At offset -SizeOf(Pointer) = Flags + address of the small block pool.
+ At offset BlockSize - SizeOf(Pointer) = Flags + address of the small block
+ pool for the next small block.
+ }
+
+ {------------------------Medium block structures------------------------}
+
+ {The medium block pool from which medium blocks are drawn. Size = 16 bytes
+ for 32-bit and 32 bytes for 64-bit.}
+ PMediumBlockPoolHeader = ^TMediumBlockPoolHeader;
+ TMediumBlockPoolHeader = record
+ {Points to the previous and next medium block pools. This circular linked
+ list is used to track memory leaks on program shutdown.}
+ PreviousMediumBlockPoolHeader: PMediumBlockPoolHeader;
+ NextMediumBlockPoolHeader: PMediumBlockPoolHeader;
+ {Padding}
+ Reserved1: NativeUInt;
+ {The block size and flags of the first medium block in the block pool}
+ FirstMediumBlockSizeAndFlags: NativeUInt;
+ end;
+
+ {Medium block layout:
+ Offset: -2 * SizeOf(Pointer) = Previous Block Size (only if the previous block is free)
+ Offset: -SizeOf(Pointer) = This block size and flags
+ Offset: 0 = User data / Previous Free Block (if this block is free)
+ Offset: SizeOf(Pointer) = Next Free Block (if this block is free)
+ Offset: BlockSize - 2*SizeOf(Pointer) = Size of this block (if this block is free)
+ Offset: BlockSize - SizeOf(Pointer) = Size of the next block and flags
+
+ {A medium block that is unused}
+ PMediumFreeBlock = ^TMediumFreeBlock;
+ TMediumFreeBlock = record
+ PreviousFreeBlock: PMediumFreeBlock;
+ NextFreeBlock: PMediumFreeBlock;
+ end;
+
+ {-------------------------Large block structures------------------------}
+
+ {Large block header record (Size = 16 for 32-bit, 32 for 64-bit)}
+ PLargeBlockHeader = ^TLargeBlockHeader;
+ TLargeBlockHeader = record
+ {Points to the previous and next large blocks. This circular linked
+ list is used to track memory leaks on program shutdown.}
+ PreviousLargeBlockHeader: PLargeBlockHeader;
+ NextLargeBlockHeader: PLargeBlockHeader;
+ {The user allocated size of the Large block}
+ UserAllocatedSize: NativeUInt;
+ {The size of this block plus the flags}
+ BlockSizeAndFlags: NativeUInt;
+ end;
+
+ {-------------------------Expected Memory Leak Structures--------------------}
+{$ifdef EnableMemoryLeakReporting}
+
+ {The layout of an expected leak. All fields may not be specified, in which
+ case it may be harder to determine which leaks are expected and which are
+ not.}
+ PExpectedMemoryLeak = ^TExpectedMemoryLeak;
+ PPExpectedMemoryLeak = ^PExpectedMemoryLeak;
+ TExpectedMemoryLeak = record
+ {Linked list pointers}
+ PreviousLeak, NextLeak: PExpectedMemoryLeak;
+ {Information about the expected leak}
+ LeakAddress: Pointer;
+ LeakedClass: TClass;
+ {$ifdef CheckCppObjectTypeEnabled}
+ LeakedCppTypeIdPtr: Pointer;
+ {$endif}
+ LeakSize: NativeInt;
+ LeakCount: Integer;
+ end;
+
+ TExpectedMemoryLeaks = record
+ {The number of entries used in the expected leaks buffer}
+ EntriesUsed: Integer;
+ {Freed entries}
+ FirstFreeSlot: PExpectedMemoryLeak;
+ {Entries with the address specified}
+ FirstEntryByAddress: PExpectedMemoryLeak;
+ {Entries with no address specified, but with the class specified}
+ FirstEntryByClass: PExpectedMemoryLeak;
+ {Entries with only size specified}
+ FirstEntryBySizeOnly: PExpectedMemoryLeak;
+ {The expected leaks buffer (Need to leave space for this header)}
+ ExpectedLeaks: array[0..(ExpectedMemoryLeaksListSize - 64) div SizeOf(TExpectedMemoryLeak) - 1] of TExpectedMemoryLeak;
+ end;
+ PExpectedMemoryLeaks = ^TExpectedMemoryLeaks;
+
+{$endif}
+
+{-------------------------Private constants----------------------------}
+const
+{$ifndef BCB6OrDelphi7AndUp}
+ reOutOfMemory = 1;
+ reInvalidPtr = 2;
+{$endif}
+ {The size of the block header in front of small and medium blocks}
+ BlockHeaderSize = SizeOf(Pointer);
+ {The size of a small block pool header}
+ SmallBlockPoolHeaderSize = SizeOf(TSmallBlockPoolHeader);
+ {The size of a medium block pool header}
+ MediumBlockPoolHeaderSize = SizeOf(TMediumBlockPoolHeader);
+ {The size of the header in front of Large blocks}
+ LargeBlockHeaderSize = SizeOf(TLargeBlockHeader);
+{$ifdef FullDebugMode}
+ {We need space for the header, the trailer checksum and the trailing block
+ size (only used by freed medium blocks).}
+ FullDebugBlockOverhead = SizeOf(TFullDebugBlockHeader) + SizeOf(NativeUInt) + SizeOf(Pointer);
+{$endif}
+
+{-------------------------Private variables----------------------------}
+var
+ {-----------------Small block management------------------}
+ {The small block types. Sizes include the leading header. Sizes are
+ picked to limit maximum wastage to about 10% or 256 bytes (whichever is
+ less) where possible.}
+ SmallBlockTypes: array[0..NumSmallBlockTypes - 1] of TSmallBlockType =(
+ {8/16 byte jumps}
+{$ifndef Align16Bytes}
+ (BlockSize: 8 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move4{$endif}),
+{$endif}
+ (BlockSize: 16 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: {$ifdef 32Bit}Move12{$else}Move8{$endif}{$endif}),
+{$ifndef Align16Bytes}
+ (BlockSize: 24 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move20{$endif}),
+{$endif}
+ (BlockSize: 32 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: {$ifdef 32Bit}Move28{$else}Move24{$endif}{$endif}),
+{$ifndef Align16Bytes}
+ (BlockSize: 40 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move36{$endif}),
+{$endif}
+ (BlockSize: 48 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: {$ifdef 32Bit}Move44{$else}Move40{$endif}{$endif}),
+{$ifndef Align16Bytes}
+ (BlockSize: 56 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move52{$endif}),
+{$endif}
+ (BlockSize: 64 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: {$ifdef 32Bit}Move60{$else}Move56{$endif}{$endif}),
+{$ifndef Align16Bytes}
+ (BlockSize: 72 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move68{$endif}),
+{$endif}
+ (BlockSize: 80),
+{$ifndef Align16Bytes}
+ (BlockSize: 88),
+{$endif}
+ (BlockSize: 96),
+{$ifndef Align16Bytes}
+ (BlockSize: 104),
+{$endif}
+ (BlockSize: 112),
+{$ifndef Align16Bytes}
+ (BlockSize: 120),
+{$endif}
+ (BlockSize: 128),
+{$ifndef Align16Bytes}
+ (BlockSize: 136),
+{$endif}
+ (BlockSize: 144),
+{$ifndef Align16Bytes}
+ (BlockSize: 152),
+{$endif}
+ (BlockSize: 160),
+ {16 byte jumps}
+ (BlockSize: 176),
+ (BlockSize: 192),
+ (BlockSize: 208),
+ (BlockSize: 224),
+ (BlockSize: 240),
+ (BlockSize: 256),
+ (BlockSize: 272),
+ (BlockSize: 288),
+ (BlockSize: 304),
+ (BlockSize: 320),
+ {32 byte jumps}
+ (BlockSize: 352),
+ (BlockSize: 384),
+ (BlockSize: 416),
+ (BlockSize: 448),
+ (BlockSize: 480),
+ {48 byte jumps}
+ (BlockSize: 528),
+ (BlockSize: 576),
+ (BlockSize: 624),
+ (BlockSize: 672),
+ {64 byte jumps}
+ (BlockSize: 736),
+ (BlockSize: 800),
+ {80 byte jumps}
+ (BlockSize: 880),
+ (BlockSize: 960),
+ {96 byte jumps}
+ (BlockSize: 1056),
+ (BlockSize: 1152),
+ {112 byte jumps}
+ (BlockSize: 1264),
+ (BlockSize: 1376),
+ {128 byte jumps}
+ (BlockSize: 1504),
+ {144 byte jumps}
+ (BlockSize: 1648),
+ {160 byte jumps}
+ (BlockSize: 1808),
+ {176 byte jumps}
+ (BlockSize: 1984),
+ {192 byte jumps}
+ (BlockSize: 2176),
+ {208 byte jumps}
+ (BlockSize: 2384),
+ {224 byte jumps}
+ (BlockSize: MaximumSmallBlockSize),
+ {The last block size occurs three times. If, during a GetMem call, the
+ requested block size is already locked by another thread then up to two
+ larger block sizes may be used instead. Having the last block size occur
+ three times avoids the need to have a size overflow check.}
+ (BlockSize: MaximumSmallBlockSize),
+ (BlockSize: MaximumSmallBlockSize));
+ {Size to small block type translation table}
+ AllocSize2SmallBlockTypeIndX4: array[0..(MaximumSmallBlockSize - 1) div SmallBlockGranularity] of Byte;
+ {-----------------Medium block management------------------}
+ {A dummy medium block pool header: Maintains a circular list of all medium
+ block pools to enable memory leak detection on program shutdown.}
+ MediumBlockPoolsCircularList: TMediumBlockPoolHeader;
+ {Are medium blocks locked?}
+ MediumBlocksLocked: Boolean;
+ {The sequential feed medium block pool.}
+ LastSequentiallyFedMediumBlock: Pointer;
+ MediumSequentialFeedBytesLeft: Cardinal;
+ {The medium block bins are divided into groups of 32 bins. If a bit
+ is set in this group bitmap, then at least one bin in the group has free
+ blocks.}
+ MediumBlockBinGroupBitmap: Cardinal;
+ {The medium block bins: total of 32 * 32 = 1024 bins of a certain
+ minimum size.}
+ MediumBlockBinBitmaps: array[0..MediumBlockBinGroupCount - 1] of Cardinal;
+ {The medium block bins. There are 1024 LIFO circular linked lists each
+ holding blocks of a specified minimum size. The sizes vary in size from
+ MinimumMediumBlockSize to MaximumMediumBlockSize. The bins are treated as
+ type TMediumFreeBlock to avoid pointer checks.}
+ MediumBlockBins: array[0..MediumBlockBinCount - 1] of TMediumFreeBlock;
+ {-----------------Large block management------------------}
+ {Are large blocks locked?}
+ LargeBlocksLocked: Boolean;
+ {A dummy large block header: Maintains a list of all allocated large blocks
+ to enable memory leak detection on program shutdown.}
+ LargeBlocksCircularList: TLargeBlockHeader;
+ {-------------------------Expected Memory Leak Structures--------------------}
+{$ifdef EnableMemoryLeakReporting}
+ {The expected memory leaks}
+ ExpectedMemoryLeaks: PExpectedMemoryLeaks;
+ ExpectedMemoryLeaksListLocked: Boolean;
+{$endif}
+ {---------------------Full Debug Mode structures--------------------}
+{$ifdef FullDebugMode}
+ {The allocation group stack}
+ AllocationGroupStack: array[0..AllocationGroupStackSize - 1] of Cardinal;
+ {The allocation group stack top (it is an index into AllocationGroupStack)}
+ AllocationGroupStackTop: Cardinal;
+ {The last allocation number used}
+ CurrentAllocationNumber: Cardinal;
+ {This is a count of the number of threads currently inside any of the
+ FullDebugMode GetMem, Freemem or ReallocMem handlers. If this value
+ is negative then a block scan is in progress and no thread may
+ allocate, free or reallocate any block or modify any FullDebugMode
+ block header or footer.}
+ ThreadsInFullDebugModeRoutine: Integer;
+ {The current log file name}
+ MMLogFileName: array[0..1023] of AnsiChar;
+ {The 64K block of reserved memory used to trap invalid memory accesses using
+ fields in a freed object.}
+ ReservedBlock: Pointer;
+ {The virtual method index count - used to get the virtual method index for a
+ virtual method call on a freed object.}
+ VMIndex: Integer;
+ {The fake VMT used to catch virtual method calls on freed objects.}
+ FreedObjectVMT: packed record
+ VMTData: array[vmtSelfPtr .. vmtParent + SizeOf(Pointer) - 1] of byte;
+ VMTMethods: array[SizeOf(Pointer) + vmtParent .. vmtParent + MaxFakeVMTEntries * SizeOf(Pointer) + SizeOf(Pointer) - 1] of Byte;
+ end;
+ {$ifdef CatchUseOfFreedInterfaces}
+ VMTBadInterface: array[0..MaxFakeVMTEntries - 1] of Pointer;
+ {$endif}
+{$endif}
+ {--------------Other info--------------}
+ {The memory manager that was replaced}
+ OldMemoryManager: {$ifndef BDS2006AndUp}TMemoryManager{$else}TMemoryManagerEx{$endif};
+ {The replacement memory manager}
+ NewMemoryManager: {$ifndef BDS2006AndUp}TMemoryManager{$else}TMemoryManagerEx{$endif};
+{$ifdef DetectMMOperationsAfterUninstall}
+ {Invalid handlers to catch MM operations after uninstall}
+ InvalidMemoryManager: {$ifndef BDS2006AndUp}TMemoryManager{$else}TMemoryManagerEx{$endif} = (
+ GetMem: InvalidGetMem;
+ FreeMem: InvalidFreeMem;
+ ReallocMem: InvalidReallocMem
+ {$ifdef BDS2006AndUp};
+ AllocMem: InvalidAllocMem;
+ RegisterExpectedMemoryLeak: InvalidRegisterAndUnRegisterMemoryLeak;
+ UnRegisterExpectedMemoryLeak: InvalidRegisterAndUnRegisterMemoryLeak;
+ {$endif}
+ );
+{$endif}
+
+{$ifdef MMSharingEnabled}
+ {A string uniquely identifying the current process (for sharing the memory
+ manager between DLLs and the main application)}
+ MappingObjectName: array[0..25] of AnsiChar = ('L', 'o', 'c', 'a', 'l', '\',
+ 'F', 'a', 's', 't', 'M', 'M', '_', 'P', 'I', 'D', '_', '?', '?', '?', '?',
+ '?', '?', '?', '?', #0);
+{$ifdef EnableBackwardCompatibleMMSharing}
+ UniqueProcessIDString: array[1..20] of AnsiChar = ('?', '?', '?', '?', '?',
+ '?', '?', '?', '_', 'P', 'I', 'D', '_', 'F', 'a', 's', 't', 'M', 'M', #0);
+ UniqueProcessIDStringBE: array[1..23] of AnsiChar = ('?', '?', '?', '?', '?',
+ '?', '?', '?', '_', 'P', 'I', 'D', '_', 'F', 'a', 's', 't', 'M', 'M', '_',
+ 'B', 'E', #0);
+ {The handle of the MM window}
+ MMWindow: HWND;
+ {The handle of the MM window (for default MM of Delphi 2006 compatibility)}
+ MMWindowBE: HWND;
+{$endif}
+ {The handle of the memory mapped file}
+ MappingObjectHandle: NativeUInt;
+{$endif}
+ {Has FastMM been installed?}
+ FastMMIsInstalled: Boolean;
+ {Is the MM in place a shared memory manager?}
+ IsMemoryManagerOwner: Boolean;
+ {Must MMX be used for move operations?}
+{$ifdef EnableMMX}
+ {$ifndef ForceMMX}
+ UseMMX: Boolean;
+ {$endif}
+{$endif}
+ {Is a MessageBox currently showing? If so, do not show another one.}
+ ShowingMessageBox: Boolean;
+ {True if RunInitializationCode has been called already.}
+ InitializationCodeHasRun: Boolean = False;
+
+{----------------Utility Functions------------------}
+
+{A copy of StrLen in order to avoid the SysUtils unit, which would have
+ introduced overhead like exception handling code.}
+function StrLen(const AStr: PAnsiChar): NativeUInt;
+{$ifndef Use32BitAsm}
+begin
+ Result := 0;
+ while AStr[Result] <> #0 do
+ Inc(Result);
+end;
+{$else}
+asm
+ {Check the first byte}
+ cmp byte ptr [eax], 0
+ je @ZeroLength
+ {Get the negative of the string start in edx}
+ mov edx, eax
+ neg edx
+ {Word align}
+ add eax, 1
+ and eax, -2
+@ScanLoop:
+ mov cx, [eax]
+ add eax, 2
+ test cl, ch
+ jnz @ScanLoop
+ test cl, cl
+ jz @ReturnLess2
+ test ch, ch
+ jnz @ScanLoop
+ lea eax, [eax + edx - 1]
+ ret
+@ReturnLess2:
+ lea eax, [eax + edx - 2]
+ ret
+@ZeroLength:
+ xor eax, eax
+end;
+{$endif}
+
+{$ifdef EnableMMX}
+{$ifndef ForceMMX}
+{Returns true if the CPUID instruction is supported}
+function CPUID_Supported: Boolean;
+asm
+ pushfd
+ pop eax
+ mov edx, eax
+ xor eax, $200000
+ push eax
+ popfd
+ pushfd
+ pop eax
+ xor eax, edx
+ setnz al
+end;
+
+{Gets the CPUID}
+function GetCPUID(AInfoRequired: Integer): TRegisters;
+asm
+ push ebx
+ push esi
+ mov esi, edx
+ {cpuid instruction}
+{$ifdef Delphi4or5}
+ db $0f, $a2
+{$else}
+ cpuid
+{$endif}
+ {Save registers}
+ mov TRegisters[esi].RegEAX, eax
+ mov TRegisters[esi].RegEBX, ebx
+ mov TRegisters[esi].RegECX, ecx
+ mov TRegisters[esi].RegEDX, edx
+ pop esi
+ pop ebx
+end;
+
+{Returns true if the CPU supports MMX}
+function MMX_Supported: Boolean;
+var
+ LReg: TRegisters;
+begin
+ if CPUID_Supported then
+ begin
+ {Get the CPUID}
+ LReg := GetCPUID(1);
+ {Bit 23 must be set for MMX support}
+ Result := LReg.RegEDX and $800000 <> 0;
+ end
+ else
+ Result := False;
+end;
+{$endif}
+{$endif}
+
+{Compare [AAddress], CompareVal:
+ If Equal: [AAddress] := NewVal and result = CompareVal
+ If Unequal: Result := [AAddress]}
+function LockCmpxchg(CompareVal, NewVal: Byte; AAddress: PByte): Byte;
+asm
+{$ifdef 32Bit}
+ {On entry:
+ al = CompareVal,
+ dl = NewVal,
+ ecx = AAddress}
+ {$ifndef LINUX}
+ lock cmpxchg [ecx], dl
+ {$else}
+ {Workaround for Kylix compiler bug}
+ db $F0, $0F, $B0, $11
+ {$endif}
+{$else}
+ {On entry:
+ cl = CompareVal
+ dl = NewVal
+ r8 = AAddress}
+ .noframe
+ mov rax, rcx
+ lock cmpxchg [r8], dl
+{$endif}
+end;
+
+{$ifndef ASMVersion}
+{Gets the first set bit in the 32-bit number, returning the bit index}
+function FindFirstSetBit(ACardinal: Cardinal): Cardinal;
+asm
+{$ifdef 64Bit}
+ .noframe
+ mov rax, rcx
+{$endif}
+ bsf eax, eax
+end;
+{$endif}
+
+{$ifdef MACOS}
+
+function StrLCopy(Dest: PAnsiChar; const Source: PAnsiChar; MaxLen: Cardinal): PAnsiChar;
+var
+ Len: Cardinal;
+begin
+ Result := Dest;
+ Len := StrLen(Source);
+ if Len > MaxLen then
+ Len := MaxLen;
+ Move(Source^, Dest^, Len * SizeOf(AnsiChar));
+ Dest[Len] := #0;
+end;
+
+function GetModuleFileName(Module: HMODULE; Buffer: PAnsiChar; BufLen: Integer): Integer;
+const
+ CUnknown: AnsiString = 'unknown';
+var
+ tmp: array[0..512] of Char;
+begin
+ if FastMMIsInstalled then
+ begin
+ Result := System.GetModuleFileName(Module, tmp, BufLen);
+ StrLCopy(Buffer, PAnsiChar(AnsiString(tmp)), BufLen);
+ end
+ else
+ begin
+ Result := Length(CUnknown);
+ StrLCopy(Buffer, Pointer(CUnknown), Result + 1);
+ end;
+end;
+
+const
+ INVALID_HANDLE_VALUE = THandle(-1);
+
+function FileCreate(const FileName: string): THandle;
+begin
+ Result := THandle(__open(PAnsiChar(UTF8String(FileName)), O_RDWR or O_CREAT or O_TRUNC or O_EXCL, FileAccessRights));
+end;
+
+{$endif}
+
+{Writes the module filename to the specified buffer and returns the number of
+ characters written.}
+function AppendModuleFileName(ABuffer: PAnsiChar): Integer;
+var
+ LModuleHandle: HModule;
+begin
+ {Get the module handle}
+{$ifndef borlndmmdll}
+ if IsLibrary then
+ LModuleHandle := HInstance
+ else
+{$endif}
+ LModuleHandle := 0;
+ {Get the module name}
+{$ifndef POSIX}
+ Result := GetModuleFileNameA(LModuleHandle, ABuffer, 512);
+{$else}
+ Result := GetModuleFileName(LModuleHandle, ABuffer, 512);
+{$endif}
+end;
+
+{Copies the name of the module followed by the given string to the buffer,
+ returning the pointer following the buffer.}
+function AppendStringToModuleName(AString, ABuffer: PAnsiChar): PAnsiChar;
+var
+ LModuleNameLength: Cardinal;
+ LCopyStart: PAnsiChar;
+begin
+ {Get the name of the application}
+ LModuleNameLength := AppendModuleFileName(ABuffer);
+ {Replace the last few characters}
+ if LModuleNameLength > 0 then
+ begin
+ {Find the last backslash}
+ LCopyStart := PAnsiChar(PByte(ABuffer) + LModuleNameLength - 1);
+ LModuleNameLength := 0;
+ while (UIntPtr(LCopyStart) >= UIntPtr(ABuffer))
+ and (LCopyStart^ <> '\') do
+ begin
+ Inc(LModuleNameLength);
+ Dec(LCopyStart);
+ end;
+ {Copy the name to the start of the buffer}
+ Inc(LCopyStart);
+ System.Move(LCopyStart^, ABuffer^, LModuleNameLength);
+ Inc(ABuffer, LModuleNameLength);
+ ABuffer^ := ':';
+ Inc(ABuffer);
+ ABuffer^ := ' ';
+ Inc(ABuffer);
+ end;
+ {Append the string}
+ while AString^ <> #0 do
+ begin
+ ABuffer^ := AString^;
+ Inc(ABuffer);
+ {Next char}
+ Inc(AString);
+ end;
+ ABuffer^ := #0;
+ Result := ABuffer;
+end;
+
+{----------------Faster Move Procedures-------------------}
+
+{Fixed size move operations ignore the size parameter. All moves are assumed to
+ be non-overlapping.}
+
+procedure Move4(const ASource; var ADest; ACount: NativeInt);
+asm
+{$ifdef 32Bit}
+ mov eax, [eax]
+ mov [edx], eax
+{$else}
+.noframe
+ mov eax, [rcx]
+ mov [rdx], eax
+{$endif}
+end;
+
+{$ifdef 64Bit}
+procedure Move8(const ASource; var ADest; ACount: NativeInt);
+asm
+ mov rax, [rcx]
+ mov [rdx], rax
+end;
+{$endif}
+
+procedure Move12(const ASource; var ADest; ACount: NativeInt);
+asm
+{$ifdef 32Bit}
+ mov ecx, [eax]
+ mov [edx], ecx
+ mov ecx, [eax + 4]
+ mov eax, [eax + 8]
+ mov [edx + 4], ecx
+ mov [edx + 8], eax
+{$else}
+.noframe
+ mov rax, [rcx]
+ mov ecx, [rcx + 8]
+ mov [rdx], rax
+ mov [rdx + 8], ecx
+{$endif}
+end;
+
+procedure Move20(const ASource; var ADest; ACount: NativeInt);
+asm
+{$ifdef 32Bit}
+ mov ecx, [eax]
+ mov [edx], ecx
+ mov ecx, [eax + 4]
+ mov [edx + 4], ecx
+ mov ecx, [eax + 8]
+ mov [edx + 8], ecx
+ mov ecx, [eax + 12]
+ mov eax, [eax + 16]
+ mov [edx + 12], ecx
+ mov [edx + 16], eax
+{$else}
+.noframe
+ movdqa xmm0, [rcx]
+ mov ecx, [rcx + 16]
+ movdqa [rdx], xmm0
+ mov [rdx + 16], ecx
+{$endif}
+end;
+
+{$ifdef 64Bit}
+procedure Move24(const ASource; var ADest; ACount: NativeInt);
+asm
+ movdqa xmm0, [rcx]
+ mov r8, [rcx + 16]
+ movdqa [rdx], xmm0
+ mov [rdx + 16], r8
+end;
+{$endif}
+
+procedure Move28(const ASource; var ADest; ACount: NativeInt);
+asm
+{$ifdef 32Bit}
+ mov ecx, [eax]
+ mov [edx], ecx
+ mov ecx, [eax + 4]
+ mov [edx + 4], ecx
+ mov ecx, [eax + 8]
+ mov [edx + 8], ecx
+ mov ecx, [eax + 12]
+ mov [edx + 12], ecx
+ mov ecx, [eax + 16]
+ mov [edx + 16], ecx
+ mov ecx, [eax + 20]
+ mov eax, [eax + 24]
+ mov [edx + 20], ecx
+ mov [edx + 24], eax
+{$else}
+.noframe
+ movdqa xmm0, [rcx]
+ mov r8, [rcx + 16]
+ mov ecx, [rcx + 24]
+ movdqa [rdx], xmm0
+ mov [rdx + 16], r8
+ mov [rdx + 24], ecx
+{$endif}
+end;
+
+procedure Move36(const ASource; var ADest; ACount: NativeInt);
+asm
+{$ifdef 32Bit}
+ fild qword ptr [eax]
+ fild qword ptr [eax + 8]
+ fild qword ptr [eax + 16]
+ fild qword ptr [eax + 24]
+ mov ecx, [eax + 32]
+ mov [edx + 32], ecx
+ fistp qword ptr [edx + 24]
+ fistp qword ptr [edx + 16]
+ fistp qword ptr [edx + 8]
+ fistp qword ptr [edx]
+{$else}
+.noframe
+ movdqa xmm0, [rcx]
+ movdqa xmm1, [rcx + 16]
+ mov ecx, [rcx + 32]
+ movdqa [rdx], xmm0
+ movdqa [rdx + 16], xmm1
+ mov [rdx + 32], ecx
+{$endif}
+end;
+
+{$ifdef 64Bit}
+procedure Move40(const ASource; var ADest; ACount: NativeInt);
+asm
+ movdqa xmm0, [rcx]
+ movdqa xmm1, [rcx + 16]
+ mov r8, [rcx + 32]
+ movdqa [rdx], xmm0
+ movdqa [rdx + 16], xmm1
+ mov [rdx + 32], r8
+end;
+{$endif}
+
+procedure Move44(const ASource; var ADest; ACount: NativeInt);
+asm
+{$ifdef 32Bit}
+ fild qword ptr [eax]
+ fild qword ptr [eax + 8]
+ fild qword ptr [eax + 16]
+ fild qword ptr [eax + 24]
+ fild qword ptr [eax + 32]
+ mov ecx, [eax + 40]
+ mov [edx + 40], ecx
+ fistp qword ptr [edx + 32]
+ fistp qword ptr [edx + 24]
+ fistp qword ptr [edx + 16]
+ fistp qword ptr [edx + 8]
+ fistp qword ptr [edx]
+{$else}
+.noframe
+ movdqa xmm0, [rcx]
+ movdqa xmm1, [rcx + 16]
+ mov r8, [rcx + 32]
+ mov ecx, [rcx + 40]
+ movdqa [rdx], xmm0
+ movdqa [rdx + 16], xmm1
+ mov [rdx + 32], r8
+ mov [rdx + 40], ecx
+{$endif}
+end;
+
+procedure Move52(const ASource; var ADest; ACount: NativeInt);
+asm
+{$ifdef 32Bit}
+ fild qword ptr [eax]
+ fild qword ptr [eax + 8]
+ fild qword ptr [eax + 16]
+ fild qword ptr [eax + 24]
+ fild qword ptr [eax + 32]
+ fild qword ptr [eax + 40]
+ mov ecx, [eax + 48]
+ mov [edx + 48], ecx
+ fistp qword ptr [edx + 40]
+ fistp qword ptr [edx + 32]
+ fistp qword ptr [edx + 24]
+ fistp qword ptr [edx + 16]
+ fistp qword ptr [edx + 8]
+ fistp qword ptr [edx]
+{$else}
+.noframe
+ movdqa xmm0, [rcx]
+ movdqa xmm1, [rcx + 16]
+ movdqa xmm2, [rcx + 32]
+ mov ecx, [rcx + 48]
+ movdqa [rdx], xmm0
+ movdqa [rdx + 16], xmm1
+ movdqa [rdx + 32], xmm2
+ mov [rdx + 48], ecx
+{$endif}
+end;
+
+{$ifdef 64Bit}
+procedure Move56(const ASource; var ADest; ACount: NativeInt);
+asm
+ movdqa xmm0, [rcx]
+ movdqa xmm1, [rcx + 16]
+ movdqa xmm2, [rcx + 32]
+ mov r8, [rcx + 48]
+ movdqa [rdx], xmm0
+ movdqa [rdx + 16], xmm1
+ movdqa [rdx + 32], xmm2
+ mov [rdx + 48], r8
+end;
+{$endif}
+
+procedure Move60(const ASource; var ADest; ACount: NativeInt);
+asm
+{$ifdef 32Bit}
+ fild qword ptr [eax]
+ fild qword ptr [eax + 8]
+ fild qword ptr [eax + 16]
+ fild qword ptr [eax + 24]
+ fild qword ptr [eax + 32]
+ fild qword ptr [eax + 40]
+ fild qword ptr [eax + 48]
+ mov ecx, [eax + 56]
+ mov [edx + 56], ecx
+ fistp qword ptr [edx + 48]
+ fistp qword ptr [edx + 40]
+ fistp qword ptr [edx + 32]
+ fistp qword ptr [edx + 24]
+ fistp qword ptr [edx + 16]
+ fistp qword ptr [edx + 8]
+ fistp qword ptr [edx]
+{$else}
+.noframe
+ movdqa xmm0, [rcx]
+ movdqa xmm1, [rcx + 16]
+ movdqa xmm2, [rcx + 32]
+ mov r8, [rcx + 48]
+ mov ecx, [rcx + 56]
+ movdqa [rdx], xmm0
+ movdqa [rdx + 16], xmm1
+ movdqa [rdx + 32], xmm2
+ mov [rdx + 48], r8
+ mov [rdx + 56], ecx
+{$endif}
+end;
+
+procedure Move68(const ASource; var ADest; ACount: NativeInt);
+asm
+{$ifdef 32Bit}
+ fild qword ptr [eax]
+ fild qword ptr [eax + 8]
+ fild qword ptr [eax + 16]
+ fild qword ptr [eax + 24]
+ fild qword ptr [eax + 32]
+ fild qword ptr [eax + 40]
+ fild qword ptr [eax + 48]
+ fild qword ptr [eax + 56]
+ mov ecx, [eax + 64]
+ mov [edx + 64], ecx
+ fistp qword ptr [edx + 56]
+ fistp qword ptr [edx + 48]
+ fistp qword ptr [edx + 40]
+ fistp qword ptr [edx + 32]
+ fistp qword ptr [edx + 24]
+ fistp qword ptr [edx + 16]
+ fistp qword ptr [edx + 8]
+ fistp qword ptr [edx]
+{$else}
+.noframe
+ movdqa xmm0, [rcx]
+ movdqa xmm1, [rcx + 16]
+ movdqa xmm2, [rcx + 32]
+ movdqa xmm3, [rcx + 48]
+ mov ecx, [rcx + 64]
+ movdqa [rdx], xmm0
+ movdqa [rdx + 16], xmm1
+ movdqa [rdx + 32], xmm2
+ movdqa [rdx + 48], xmm3
+ mov [rdx + 64], ecx
+{$endif}
+end;
+
+{Variable size move procedure: Rounds ACount up to the next multiple of 16 less
+ SizeOf(Pointer). Important note: Always moves at least 16 - SizeOf(Pointer)
+ bytes (the minimum small block size with 16 byte alignment), irrespective of
+ ACount.}
+procedure MoveX16LP(const ASource; var ADest; ACount: NativeInt);
+asm
+{$ifdef 32Bit}
+ {Make the counter negative based: The last 12 bytes are moved separately}
+ sub ecx, 12
+ add eax, ecx
+ add edx, ecx
+{$ifdef EnableMMX}
+ {$ifndef ForceMMX}
+ cmp UseMMX, True
+ jne @FPUMove
+ {$endif}
+ {Make the counter negative based: The last 12 bytes are moved separately}
+ neg ecx
+ jns @MMXMoveLast12
+@MMXMoveLoop:
+ {Move a 16 byte block}
+ {$ifdef Delphi4or5}
+ {Delphi 5 compatibility}
+ db $0f, $6f, $04, $01
+ db $0f, $6f, $4c, $01, $08
+ db $0f, $7f, $04, $11
+ db $0f, $7f, $4c, $11, $08
+ {$else}
+ movq mm0, [eax + ecx]
+ movq mm1, [eax + ecx + 8]
+ movq [edx + ecx], mm0
+ movq [edx + ecx + 8], mm1
+ {$endif}
+ {Are there another 16 bytes to move?}
+ add ecx, 16
+ js @MMXMoveLoop
+@MMXMoveLast12:
+ {Do the last 12 bytes}
+ {$ifdef Delphi4or5}
+ {Delphi 5 compatibility}
+ db $0f, $6f, $04, $01
+ {$else}
+ movq mm0, [eax + ecx]
+ {$endif}
+ mov eax, [eax + ecx + 8]
+ {$ifdef Delphi4or5}
+ {Delphi 5 compatibility}
+ db $0f, $7f, $04, $11
+ {$else}
+ movq [edx + ecx], mm0
+ {$endif}
+ mov [edx + ecx + 8], eax
+ {Exit MMX state}
+ {$ifdef Delphi4or5}
+ {Delphi 5 compatibility}
+ db $0f, $77
+ {$else}
+ emms
+ {$endif}
+ {$ifndef ForceMMX}
+ ret
+ {$endif}
+{$endif}
+{FPU code is only used if MMX is not forced}
+{$ifndef ForceMMX}
+@FPUMove:
+ neg ecx
+ jns @FPUMoveLast12
+@FPUMoveLoop:
+ {Move a 16 byte block}
+ fild qword ptr [eax + ecx]
+ fild qword ptr [eax + ecx + 8]
+ fistp qword ptr [edx + ecx + 8]
+ fistp qword ptr [edx + ecx]
+ {Are there another 16 bytes to move?}
+ add ecx, 16
+ js @FPUMoveLoop
+@FPUMoveLast12:
+ {Do the last 12 bytes}
+ fild qword ptr [eax + ecx]
+ fistp qword ptr [edx + ecx]
+ mov eax, [eax + ecx + 8]
+ mov [edx + ecx + 8], eax
+{$endif}
+{$else}
+.noframe
+ {Make the counter negative based: The last 8 bytes are moved separately}
+ sub r8, 8
+ add rcx, r8
+ add rdx, r8
+ neg r8
+ jns @MoveLast12
+@MoveLoop:
+ {Move a 16 byte block}
+ movdqa xmm0, [rcx + r8]
+ movdqa [rdx + r8], xmm0
+ {Are there another 16 bytes to move?}
+ add r8, 16
+ js @MoveLoop
+@MoveLast12:
+ {Do the last 8 bytes}
+ mov r9, [rcx + r8]
+ mov [rdx + r8], r9
+{$endif}
+end;
+
+{Variable size move procedure: Rounds ACount up to the next multiple of 8 less
+ SizeOf(Pointer). Important note: Always moves at least 8 - SizeOf(Pointer)
+ bytes (the minimum small block size with 8 byte alignment), irrespective of
+ ACount.}
+procedure MoveX8LP(const ASource; var ADest; ACount: NativeInt);
+asm
+{$ifdef 32Bit}
+ {Make the counter negative based: The last 4 bytes are moved separately}
+ sub ecx, 4
+ {4 bytes or less? -> Use the Move4 routine.}
+ jle @FourBytesOrLess
+ add eax, ecx
+ add edx, ecx
+ neg ecx
+{$ifdef EnableMMX}
+ {$ifndef ForceMMX}
+ cmp UseMMX, True
+ jne @FPUMoveLoop
+ {$endif}
+@MMXMoveLoop:
+ {Move an 8 byte block}
+{$ifdef Delphi4or5}
+ {Delphi 5 compatibility}
+ db $0f, $6f, $04, $01
+ db $0f, $7f, $04, $11
+{$else}
+ movq mm0, [eax + ecx]
+ movq [edx + ecx], mm0
+{$endif}
+ {Are there another 8 bytes to move?}
+ add ecx, 8
+ js @MMXMoveLoop
+ {Exit MMX state}
+{$ifdef Delphi4or5}
+ {Delphi 5 compatibility}
+ db $0f, $77
+{$else}
+ emms
+{$endif}
+ {Do the last 4 bytes}
+ mov eax, [eax + ecx]
+ mov [edx + ecx], eax
+ ret
+{$endif}
+{FPU code is only used if MMX is not forced}
+{$ifndef ForceMMX}
+@FPUMoveLoop:
+ {Move an 8 byte block}
+ fild qword ptr [eax + ecx]
+ fistp qword ptr [edx + ecx]
+ {Are there another 8 bytes to move?}
+ add ecx, 8
+ js @FPUMoveLoop
+ {Do the last 4 bytes}
+ mov eax, [eax + ecx]
+ mov [edx + ecx], eax
+ ret
+{$endif}
+@FourBytesOrLess:
+ {Four or less bytes to move}
+ mov eax, [eax]
+ mov [edx], eax
+{$else}
+.noframe
+ {Make the counter negative based}
+ add rcx, r8
+ add rdx, r8
+ neg r8
+@MoveLoop:
+ {Move an 8 byte block}
+ mov r9, [rcx + r8]
+ mov [rdx + r8], r9
+ {Are there another 8 bytes to move?}
+ add r8, 8
+ js @MoveLoop
+{$endif}
+end;
+
+{----------------Windows Emulation Functions for Kylix / OS X Support-----------------}
+
+{$ifdef POSIX}
+
+const
+ {Messagebox constants}
+ MB_OK = 0;
+ MB_ICONERROR = $10;
+ MB_TASKMODAL = $2000;
+ MB_DEFAULT_DESKTOP_ONLY = $20000;
+ {Virtual memory constants}
+ MEM_COMMIT = $1000;
+ MEM_RELEASE = $8000;
+ MEM_TOP_DOWN = $100000;
+ PAGE_READWRITE = 4;
+
+procedure MessageBoxA(hWnd: Cardinal; AMessageText, AMessageTitle: PAnsiChar; uType: Cardinal); stdcall;
+begin
+ if FastMMIsInstalled then
+ writeln(AMessageText)
+ else
+ __write(STDERR_FILENO, AMessageText, StrLen(AMessageText));
+end;
+
+function VirtualAlloc(lpvAddress: Pointer; dwSize, flAllocationType, flProtect: Cardinal): Pointer; stdcall;
+begin
+ Result := valloc(dwSize);
+end;
+
+function VirtualFree(lpAddress: Pointer; dwSize, dwFreeType: Cardinal): LongBool; stdcall;
+begin
+ free(lpAddress);
+ Result := True;
+end;
+
+function WriteFile(hFile: THandle; const Buffer; nNumberOfBytesToWrite: Cardinal;
+ var lpNumberOfBytesWritten: Cardinal; lpOverlapped: Pointer): Boolean; stdcall;
+begin
+ lpNumberOfBytesWritten := __write(hFile, @Buffer, nNumberOfBytesToWrite);
+ if lpNumberOfBytesWritten = Cardinal(-1) then
+ begin
+ lpNumberOfBytesWritten := 0;
+ Result := False;
+ end
+ else
+ Result := True;
+end;
+
+{$ifndef NeverSleepOnThreadContention}
+procedure Sleep(dwMilliseconds: Cardinal); stdcall;
+begin
+ {Convert to microseconds (more or less)}
+ usleep(dwMilliseconds shl 10);
+end;
+{$endif}
+{$endif}
+
+{-----------------Debugging Support Functions and Procedures------------------}
+
+{$ifdef FullDebugMode}
+
+{Returns the current thread ID}
+function GetThreadID: Cardinal;
+{$ifdef 32Bit}
+asm
+ mov eax, FS:[$24]
+end;
+{$else}
+begin
+ Result := GetCurrentThreadId;
+end;
+{$endif}
+
+{Fills a block of memory with the given dword (32-bit) or qword (64-bit).
+ Always fills a multiple of SizeOf(Pointer) bytes}
+procedure DebugFillMem(var AAddress; AByteCount: NativeInt; AFillValue: NativeUInt);
+asm
+{$ifdef 32Bit}
+ {On Entry:
+ eax = AAddress
+ edx = AByteCount
+ ecx = AFillValue}
+ add eax, edx
+ neg edx
+ jns @Done
+@FillLoop:
+ mov [eax + edx], ecx
+ add edx, 4
+ js @FillLoop
+@Done:
+{$else}
+ {On Entry:
+ rcx = AAddress
+ rdx = AByteCount
+ r8 = AFillValue}
+ add rcx, rdx
+ neg rdx
+ jns @Done
+@FillLoop:
+ mov [rcx + rdx], r8
+ add rdx, 8
+ js @FillLoop
+@Done:
+{$endif}
+end;
+
+ {$ifndef LoadDebugDLLDynamically}
+
+{The stack trace procedure. The stack trace module is external since it may
+ raise handled access violations that result in the creation of exception
+ objects and the stack trace code is not re-entrant.}
+procedure GetStackTrace(AReturnAddresses: PNativeUInt;
+ AMaxDepth, ASkipFrames: Cardinal); external FullDebugModeLibraryName
+ name {$ifdef RawStackTraces}'GetRawStackTrace'{$else}'GetFrameBasedStackTrace'{$endif};
+
+{The exported procedure in the FastMM_FullDebugMode.dll library used to convert
+ the return addresses of a stack trace to a text string.}
+function LogStackTrace(AReturnAddresses: PNativeUInt;
+ AMaxDepth: Cardinal; ABuffer: PAnsiChar): PAnsiChar; external FullDebugModeLibraryName
+ name 'LogStackTrace';
+
+ {$else}
+
+ {Default no-op stack trace and logging handlers}
+ procedure NoOpGetStackTrace(AReturnAddresses: PNativeUInt;
+ AMaxDepth, ASkipFrames: Cardinal);
+ begin
+ DebugFillMem(AReturnAddresses^, AMaxDepth * SizeOf(Pointer), 0);
+ end;
+
+ function NoOpLogStackTrace(AReturnAddresses: PNativeUInt;
+ AMaxDepth: Cardinal; ABuffer: PAnsiChar): PAnsiChar;
+ begin
+ Result := ABuffer;
+ end;
+
+var
+
+ {Handle to the FullDebugMode DLL}
+ FullDebugModeDLL: HMODULE;
+
+ GetStackTrace: procedure (AReturnAddresses: PNativeUInt;
+ AMaxDepth, ASkipFrames: Cardinal) = NoOpGetStackTrace;
+
+ LogStackTrace: function (AReturnAddresses: PNativeUInt;
+ AMaxDepth: Cardinal; ABuffer: PAnsiChar): PAnsiChar = NoOpLogStackTrace;
+
+ {$endif}
+
+{$endif}
+
+{$ifndef POSIX}
+function DelphiIsRunning: Boolean;
+begin
+ Result := FindWindowA('TAppBuilder', nil) <> 0;
+end;
+{$endif}
+
+{Converts an unsigned integer to string at the buffer location, returning the
+ new buffer position. Note: The 32-bit asm version only supports numbers up to
+ 2^31 - 1.}
+function NativeUIntToStrBuf(ANum: NativeUInt; APBuffer: PAnsiChar): PAnsiChar;
+{$ifndef Use32BitAsm}
+const
+ MaxDigits = 20;
+var
+ LDigitBuffer: array[0..MaxDigits - 1] of AnsiChar;
+ LCount: Cardinal;
+ LDigit: NativeUInt;
+begin
+ {Generate the digits in the local buffer}
+ LCount := 0;
+ repeat
+ LDigit := ANum;
+ ANum := ANum div 10;
+ LDigit := LDigit - ANum * 10;
+ Inc(LCount);
+ LDigitBuffer[MaxDigits - LCount] := AnsiChar(Ord('0') + LDigit);
+ until ANum = 0;
+ {Copy the digits to the output buffer and advance it}
+ System.Move(LDigitBuffer[MaxDigits - LCount], APBuffer^, LCount);
+ Result := APBuffer + LCount;
+end;
+{$else}
+asm
+ {On entry: eax = ANum, edx = ABuffer}
+ push edi
+ mov edi, edx //Pointer to the first character in edi
+ {Calculate leading digit: divide the number by 1e9}
+ add eax, 1 //Increment the number
+ mov edx, $89705F41 //1e9 reciprocal
+ mul edx //Multplying with reciprocal
+ shr eax, 30 //Save fraction bits
+ mov ecx, edx //First digit in bits <31:29>
+ and edx, $1FFFFFFF //Filter fraction part edx<28:0>
+ shr ecx, 29 //Get leading digit into accumulator
+ lea edx, [edx + 4 * edx] //Calculate ...
+ add edx, eax //... 5*fraction
+ mov eax, ecx //Copy leading digit
+ or eax, '0' //Convert digit to ASCII
+ mov [edi], al //Store digit out to memory
+ {Calculate digit #2}
+ mov eax, edx //Point format such that 1.0 = 2^28
+ cmp ecx, 1 //Any non-zero digit yet ?
+ sbb edi, -1 //Yes->increment ptr, No->keep old ptr
+ shr eax, 28 //Next digit
+ and edx, $0fffffff //Fraction part edx<27:0>
+ or ecx, eax //Accumulate next digit
+ or eax, '0' //Convert digit to ASCII
+ mov [edi], al //Store digit out to memory
+ {Calculate digit #3}
+ lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:27>
+ lea edx, [edx * 4 + edx] //5*fraction, new fraction edx<26:0>
+ cmp ecx, 1 //Any non-zero digit yet ?
+ sbb edi, -1 //Yes->increment ptr, No->keep old ptr
+ shr eax, 27 //Next digit
+ and edx, $07ffffff //Fraction part
+ or ecx, eax //Accumulate next digit
+ or eax, '0' //Convert digit to ASCII
+ mov [edi], al //Store digit out to memory
+ {Calculate digit #4}
+ lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:26>
+ lea edx, [edx * 4 + edx] //5*fraction, new fraction edx<25:0>
+ cmp ecx, 1 //Any non-zero digit yet ?
+ sbb edi, -1 //Yes->increment ptr, No->keep old ptr
+ shr eax, 26 //Next digit
+ and edx, $03ffffff //Fraction part
+ or ecx, eax //Accumulate next digit
+ or eax, '0' //Convert digit to ASCII
+ mov [edi], al //Store digit out to memory
+ {Calculate digit #5}
+ lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:25>
+ lea edx, [edx * 4 + edx] //5*fraction, new fraction edx<24:0>
+ cmp ecx, 1 //Any non-zero digit yet ?
+ sbb edi, -1 //Yes->increment ptr, No->keep old ptr
+ shr eax, 25 //Next digit
+ and edx, $01ffffff //Fraction part
+ or ecx, eax //Accumulate next digit
+ or eax, '0' //Convert digit to ASCII
+ mov [edi], al //Store digit out to memory
+ {Calculate digit #6}
+ lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:24>
+ lea edx, [edx * 4 + edx] //5*fraction, new fraction edx<23:0>
+ cmp ecx, 1 //Any non-zero digit yet ?
+ sbb edi, -1 //Yes->increment ptr, No->keep old ptr
+ shr eax, 24 //Next digit
+ and edx, $00ffffff //Fraction part
+ or ecx, eax //Accumulate next digit
+ or eax, '0' //Convert digit to ASCII
+ mov [edi], al //Store digit out to memory
+ {Calculate digit #7}
+ lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:23>
+ lea edx, [edx * 4 + edx] //5*fraction, new fraction edx<31:23>
+ cmp ecx, 1 //Any non-zero digit yet ?
+ sbb edi, -1 //Yes->increment ptr, No->keep old ptr
+ shr eax, 23 //Next digit
+ and edx, $007fffff //Fraction part
+ or ecx, eax //Accumulate next digit
+ or eax, '0' //Convert digit to ASCII
+ mov [edi], al //Store digit out to memory
+ {Calculate digit #8}
+ lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:22>
+ lea edx, [edx * 4 + edx] //5*fraction, new fraction edx<22:0>
+ cmp ecx, 1 //Any non-zero digit yet ?
+ sbb edi, -1 //Yes->increment ptr, No->keep old ptr
+ shr eax, 22 //Next digit
+ and edx, $003fffff //Fraction part
+ or ecx, eax //Accumulate next digit
+ or eax, '0' //Convert digit to ASCII
+ mov [edi], al //Store digit out to memory
+ {Calculate digit #9}
+ lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:21>
+ lea edx, [edx * 4 + edx] //5*fraction, new fraction edx<21:0>
+ cmp ecx, 1 //Any non-zero digit yet ?
+ sbb edi, -1 //Yes->increment ptr, No->keep old ptr
+ shr eax, 21 //Next digit
+ and edx, $001fffff //Fraction part
+ or ecx, eax //Accumulate next digit
+ or eax, '0' //Convert digit to ASCII
+ mov [edi], al //Store digit out to memory
+ {Calculate digit #10}
+ lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:20>
+ cmp ecx, 1 //Any-non-zero digit yet ?
+ sbb edi, -1 //Yes->increment ptr, No->keep old ptr
+ shr eax, 20 //Next digit
+ or eax, '0' //Convert digit to ASCII
+ mov [edi], al //Store last digit and end marker out to memory
+ {Return a pointer to the next character}
+ lea eax, [edi + 1]
+ {Restore edi}
+ pop edi
+end;
+{$endif}
+
+{Converts an unsigned integer to a hexadecimal string at the buffer location,
+ returning the new buffer position.}
+function NativeUIntToHexBuf(ANum: NativeUInt; APBuffer: PAnsiChar): PAnsiChar;
+{$ifndef Use32BitAsm}
+const
+ MaxDigits = 16;
+var
+ LDigitBuffer: array[0..MaxDigits - 1] of AnsiChar;
+ LCount: Cardinal;
+ LDigit: NativeUInt;
+begin
+ {Generate the digits in the local buffer}
+ LCount := 0;
+ repeat
+ LDigit := ANum;
+ ANum := ANum div 16;
+ LDigit := LDigit - ANum * 16;
+ Inc(LCount);
+ LDigitBuffer[MaxDigits - LCount] := HexTable[LDigit];
+ until ANum = 0;
+ {Copy the digits to the output buffer and advance it}
+ System.Move(LDigitBuffer[MaxDigits - LCount], APBuffer^, LCount);
+ Result := APBuffer + LCount;
+end;
+{$else}
+asm
+ {On entry:
+ eax = ANum
+ edx = ABuffer}
+ push ebx
+ push edi
+ {Save ANum in ebx}
+ mov ebx, eax
+ {Get a pointer to the first character in edi}
+ mov edi, edx
+ {Get the number in ecx as well}
+ mov ecx, eax
+ {Keep the low nibbles in ebx and the high nibbles in ecx}
+ and ebx, $0f0f0f0f
+ and ecx, $f0f0f0f0
+ {Swap the bytes into the right order}
+ ror ebx, 16
+ ror ecx, 20
+ {Get nibble 7}
+ movzx eax, ch
+ mov dl, ch
+ mov al, byte ptr HexTable[eax]
+ mov [edi], al
+ cmp dl, 1
+ sbb edi, -1
+ {Get nibble 6}
+ movzx eax, bh
+ or dl, bh
+ mov al, byte ptr HexTable[eax]
+ mov [edi], al
+ cmp dl, 1
+ sbb edi, -1
+ {Get nibble 5}
+ movzx eax, cl
+ or dl, cl
+ mov al, byte ptr HexTable[eax]
+ mov [edi], al
+ cmp dl, 1
+ sbb edi, -1
+ {Get nibble 4}
+ movzx eax, bl
+ or dl, bl
+ mov al, byte ptr HexTable[eax]
+ mov [edi], al
+ cmp dl, 1
+ sbb edi, -1
+ {Rotate ecx and ebx so we get access to the rest}
+ shr ebx, 16
+ shr ecx, 16
+ {Get nibble 3}
+ movzx eax, ch
+ or dl, ch
+ mov al, byte ptr HexTable[eax]
+ mov [edi], al
+ cmp dl, 1
+ sbb edi, -1
+ {Get nibble 2}
+ movzx eax, bh
+ or dl, bh
+ mov al, byte ptr HexTable[eax]
+ mov [edi], al
+ cmp dl, 1
+ sbb edi, -1
+ {Get nibble 1}
+ movzx eax, cl
+ or dl, cl
+ mov al, byte ptr HexTable[eax]
+ mov [edi], al
+ cmp dl, 1
+ sbb edi, -1
+ {Get nibble 0}
+ movzx eax, bl
+ mov al, byte ptr HexTable[eax]
+ mov [edi], al
+ {Return a pointer to the end of the string}
+ lea eax, [edi + 1]
+ {Restore registers}
+ pop edi
+ pop ebx
+end;
+{$endif}
+
+{Appends the source text to the destination and returns the new destination
+ position}
+function AppendStringToBuffer(const ASource, ADestination: PAnsiChar; ACount: Cardinal): PAnsiChar;
+begin
+ System.Move(ASource^, ADestination^, ACount);
+ Result := Pointer(PByte(ADestination) + ACount);
+end;
+
+{Appends the name of the class to the destination buffer and returns the new
+ destination position}
+function AppendClassNameToBuffer(AClass: TClass; ADestination: PAnsiChar): PAnsiChar;
+var
+ LPClassName: PShortString;
+begin
+ {Get a pointer to the class name}
+ if AClass <> nil then
+ begin
+ LPClassName := PShortString(PPointer(PByte(AClass) + vmtClassName)^);
+ {Append the class name}
+ Result := AppendStringToBuffer(@LPClassName^[1], ADestination, Length(LPClassName^));
+ end
+ else
+ begin
+ Result := AppendStringToBuffer(UnknownClassNameMsg, ADestination, Length(UnknownClassNameMsg));
+ end;
+end;
+
+{Shows a message box if the program is not showing one already.}
+procedure ShowMessageBox(AText, ACaption: PAnsiChar);
+begin
+ if (not ShowingMessageBox) and (not SuppressMessageBoxes) then
+ begin
+ ShowingMessageBox := True;
+ MessageBoxA(0, AText, ACaption,
+ MB_OK or MB_ICONERROR or MB_TASKMODAL or MB_DEFAULT_DESKTOP_ONLY);
+ ShowingMessageBox := False;
+ end;
+end;
+
+{Returns the class for a memory block. Returns nil if it is not a valid class}
+function DetectClassInstance(APointer: Pointer): TClass;
+{$ifndef POSIX}
+var
+ LMemInfo: TMemoryBasicInformation;
+
+ {Checks whether the given address is a valid address for a VMT entry.}
+ function IsValidVMTAddress(APAddress: Pointer): Boolean;
+ begin
+ {Do some basic pointer checks: Must be dword aligned and beyond 64K}
+ if (UIntPtr(APAddress) > 65535)
+ and (UIntPtr(APAddress) and 3 = 0) then
+ begin
+ {Do we need to recheck the virtual memory?}
+ if (UIntPtr(LMemInfo.BaseAddress) > UIntPtr(APAddress))
+ or ((UIntPtr(LMemInfo.BaseAddress) + LMemInfo.RegionSize) < (UIntPtr(APAddress) + 4)) then
+ begin
+ {Get the VM status for the pointer}
+ LMemInfo.RegionSize := 0;
+ VirtualQuery(APAddress, LMemInfo, SizeOf(LMemInfo));
+ end;
+ {Check the readability of the memory address}
+ Result := (LMemInfo.RegionSize >= 4)
+ and (LMemInfo.State = MEM_COMMIT)
+ and (LMemInfo.Protect and (PAGE_READONLY or PAGE_READWRITE or PAGE_EXECUTE or PAGE_EXECUTE_READ or PAGE_EXECUTE_READWRITE or PAGE_EXECUTE_WRITECOPY) <> 0)
+ and (LMemInfo.Protect and PAGE_GUARD = 0);
+ end
+ else
+ Result := False;
+ end;
+
+ {Returns true if AClassPointer points to a class VMT}
+ function InternalIsValidClass(AClassPointer: Pointer; ADepth: Integer = 0): Boolean;
+ var
+ LParentClassSelfPointer: PPointer;
+ begin
+ {Check that the self pointer as well as parent class self pointer addresses
+ are valid}
+ if (ADepth < 1000)
+ and IsValidVMTAddress(Pointer(PByte(AClassPointer) + vmtSelfPtr))
+ and IsValidVMTAddress(Pointer(PByte(AClassPointer) + vmtParent)) then
+ begin
+ {Get a pointer to the parent class' self pointer}
+ LParentClassSelfPointer := PPointer(PByte(AClassPointer) + vmtParent)^;
+ {Check that the self pointer as well as the parent class is valid}
+ Result := (PPointer(PByte(AClassPointer) + vmtSelfPtr)^ = AClassPointer)
+ and ((LParentClassSelfPointer = nil)
+ or (IsValidVMTAddress(LParentClassSelfPointer)
+ and InternalIsValidClass(LParentClassSelfPointer^, ADepth + 1)));
+ end
+ else
+ Result := False;
+ end;
+
+begin
+ {Get the class pointer from the (suspected) object}
+ Result := TClass(PPointer(APointer)^);
+ {No VM info yet}
+ LMemInfo.RegionSize := 0;
+ {Check the block}
+ if (not InternalIsValidClass(Pointer(Result), 0))
+{$ifdef FullDebugMode}
+ or (Result = @FreedObjectVMT.VMTMethods[0])
+{$endif}
+ then
+ Result := nil;
+end;
+{$else}
+begin
+ {Not currently supported under Linux / OS X}
+ Result := nil;
+end;
+{$endif}
+
+{Gets the available size inside a block}
+function GetAvailableSpaceInBlock(APointer: Pointer): NativeUInt;
+var
+ LBlockHeader: NativeUInt;
+ LPSmallBlockPool: PSmallBlockPoolHeader;
+begin
+ LBlockHeader := PNativeUInt(PByte(APointer) - BlockHeaderSize)^;
+ if LBlockHeader and (IsMediumBlockFlag or IsLargeBlockFlag) = 0 then
+ begin
+ LPSmallBlockPool := PSmallBlockPoolHeader(LBlockHeader and DropSmallFlagsMask);
+ Result := LPSmallBlockPool.BlockType.BlockSize - BlockHeaderSize;
+ end
+ else
+ begin
+ Result := (LBlockHeader and DropMediumAndLargeFlagsMask) - BlockHeaderSize;
+ if (LBlockHeader and IsMediumBlockFlag) = 0 then
+ Dec(Result, LargeBlockHeaderSize);
+ end;
+end;
+
+{-----------------Small Block Management------------------}
+
+{Locks all small block types}
+procedure LockAllSmallBlockTypes;
+var
+ LInd: Cardinal;
+begin
+ {Lock the medium blocks}
+{$ifndef AssumeMultiThreaded}
+ if IsMultiThread then
+{$endif}
+ begin
+ for LInd := 0 to NumSmallBlockTypes - 1 do
+ begin
+ while LockCmpxchg(0, 1, @SmallBlockTypes[LInd].BlockTypeLocked) <> 0 do
+ begin
+{$ifdef NeverSleepOnThreadContention}
+ {$ifdef UseSwitchToThread}
+ SwitchToThread;
+ {$endif}
+{$else}
+ Sleep(InitialSleepTime);
+ if LockCmpxchg(0, 1, @SmallBlockTypes[LInd].BlockTypeLocked) = 0 then
+ Break;
+ Sleep(AdditionalSleepTime);
+{$endif}
+ end;
+ end;
+ end;
+end;
+
+{Gets the first and last block pointer for a small block pool}
+procedure GetFirstAndLastSmallBlockInPool(APSmallBlockPool: PSmallBlockPoolHeader;
+ var AFirstPtr, ALastPtr: Pointer);
+var
+ LBlockSize: NativeUInt;
+begin
+ {Get the pointer to the first block}
+ AFirstPtr := Pointer(PByte(APSmallBlockPool) + SmallBlockPoolHeaderSize);
+ {Get a pointer to the last block}
+ if (APSmallBlockPool.BlockType.CurrentSequentialFeedPool <> APSmallBlockPool)
+ or (UIntPtr(APSmallBlockPool.BlockType.NextSequentialFeedBlockAddress) > UIntPtr(APSmallBlockPool.BlockType.MaxSequentialFeedBlockAddress)) then
+ begin
+ {Not the sequential feed - point to the end of the block}
+ LBlockSize := PNativeUInt(PByte(APSmallBlockPool) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask;
+ ALastPtr := Pointer(PByte(APSmallBlockPool) + LBlockSize - APSmallBlockPool.BlockType.BlockSize);
+ end
+ else
+ begin
+ {The sequential feed pool - point to before the next sequential feed block}
+ ALastPtr := Pointer(PByte(APSmallBlockPool.BlockType.NextSequentialFeedBlockAddress) - 1);
+ end;
+end;
+
+{-----------------Medium Block Management------------------}
+
+{Advances to the next medium block. Returns nil if the end of the medium block
+ pool has been reached}
+function NextMediumBlock(APMediumBlock: Pointer): Pointer;
+var
+ LBlockSize: NativeUInt;
+begin
+ {Get the size of this block}
+ LBlockSize := PNativeUInt(PByte(APMediumBlock) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask;
+ {Advance the pointer}
+ Result := Pointer(PByte(APMediumBlock) + LBlockSize);
+ {Is the next block the end of medium pool marker?}
+ LBlockSize := PNativeUInt(PByte(Result) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask;
+ if LBlockSize = 0 then
+ Result := nil;
+end;
+
+{Gets the first medium block in the medium block pool}
+function GetFirstMediumBlockInPool(APMediumBlockPoolHeader: PMediumBlockPoolHeader): Pointer;
+begin
+ if (MediumSequentialFeedBytesLeft = 0)
+ or (UIntPtr(LastSequentiallyFedMediumBlock) < UIntPtr(APMediumBlockPoolHeader))
+ or (UIntPtr(LastSequentiallyFedMediumBlock) > UIntPtr(APMediumBlockPoolHeader) + MediumBlockPoolSize) then
+ begin
+ Result := Pointer(PByte(APMediumBlockPoolHeader) + MediumBlockPoolHeaderSize);
+ end
+ else
+ begin
+ {Is the sequential feed pool empty?}
+ if MediumSequentialFeedBytesLeft <> MediumBlockPoolSize - MediumBlockPoolHeaderSize then
+ Result := LastSequentiallyFedMediumBlock
+ else
+ Result := nil;
+ end;
+end;
+
+{Locks the medium blocks. Note that the 32-bit asm version is assumed to
+ preserve all registers except eax.}
+{$ifndef Use32BitAsm}
+procedure LockMediumBlocks;
+begin
+ {Lock the medium blocks}
+{$ifndef AssumeMultiThreaded}
+ if IsMultiThread then
+{$endif}
+ begin
+ while LockCmpxchg(0, 1, @MediumBlocksLocked) <> 0 do
+ begin
+{$ifdef NeverSleepOnThreadContention}
+ {$ifdef UseSwitchToThread}
+ SwitchToThread;
+ {$endif}
+{$else}
+ Sleep(InitialSleepTime);
+ if LockCmpxchg(0, 1, @MediumBlocksLocked) = 0 then
+ Break;
+ Sleep(AdditionalSleepTime);
+{$endif}
+ end;
+ end;
+end;
+{$else}
+procedure LockMediumBlocks;
+asm
+ {Note: This routine is assumed to preserve all registers except eax}
+@MediumBlockLockLoop:
+ mov eax, $100
+ {Attempt to lock the medium blocks}
+ lock cmpxchg MediumBlocksLocked, ah
+ je @Done
+{$ifdef NeverSleepOnThreadContention}
+ {Pause instruction (improves performance on P4)}
+ rep nop
+ {$ifdef UseSwitchToThread}
+ push ecx
+ push edx
+ call SwitchToThread
+ pop edx
+ pop ecx
+ {$endif}
+ {Try again}
+ jmp @MediumBlockLockLoop
+{$else}
+ {Couldn't lock the medium blocks - sleep and try again}
+ push ecx
+ push edx
+ push InitialSleepTime
+ call Sleep
+ pop edx
+ pop ecx
+ {Try again}
+ mov eax, $100
+ {Attempt to grab the block type}
+ lock cmpxchg MediumBlocksLocked, ah
+ je @Done
+ {Couldn't lock the medium blocks - sleep and try again}
+ push ecx
+ push edx
+ push AdditionalSleepTime
+ call Sleep
+ pop edx
+ pop ecx
+ {Try again}
+ jmp @MediumBlockLockLoop
+{$endif}
+@Done:
+end;
+{$endif}
+
+{Removes a medium block from the circular linked list of free blocks.
+ Does not change any header flags. Medium blocks should be locked
+ before calling this procedure.}
+procedure RemoveMediumFreeBlock(APMediumFreeBlock: PMediumFreeBlock);
+{$ifndef ASMVersion}
+var
+ LPreviousFreeBlock, LNextFreeBlock: PMediumFreeBlock;
+ LBinNumber, LBinGroupNumber: Cardinal;
+begin
+ {Get the current previous and next blocks}
+ LNextFreeBlock := APMediumFreeBlock.NextFreeBlock;
+ LPreviousFreeBlock := APMediumFreeBlock.PreviousFreeBlock;
+ {Remove this block from the linked list}
+ LPreviousFreeBlock.NextFreeBlock := LNextFreeBlock;
+ LNextFreeBlock.PreviousFreeBlock := LPreviousFreeBlock;
+ {Is this bin now empty? If the previous and next free block pointers are
+ equal, they must point to the bin.}
+ if LPreviousFreeBlock = LNextFreeBlock then
+ begin
+ {Get the bin number for this block size}
+ LBinNumber := (UIntPtr(LNextFreeBlock) - UIntPtr(@MediumBlockBins)) div SizeOf(TMediumFreeBlock);
+ LBinGroupNumber := LBinNumber div 32;
+ {Flag this bin as empty}
+ MediumBlockBinBitmaps[LBinGroupNumber] := MediumBlockBinBitmaps[LBinGroupNumber]
+ and (not (1 shl (LBinNumber and 31)));
+ {Is the group now entirely empty?}
+ if MediumBlockBinBitmaps[LBinGroupNumber] = 0 then
+ begin
+ {Flag this group as empty}
+ MediumBlockBinGroupBitmap := MediumBlockBinGroupBitmap
+ and (not (1 shl LBinGroupNumber));
+ end;
+ end;
+end;
+{$else}
+{$ifdef 32Bit}
+asm
+ {On entry: eax = APMediumFreeBlock}
+ {Get the current previous and next blocks}
+ mov ecx, TMediumFreeBlock[eax].NextFreeBlock
+ mov edx, TMediumFreeBlock[eax].PreviousFreeBlock
+ {Is this bin now empty? If the previous and next free block pointers are
+ equal, they must point to the bin.}
+ cmp ecx, edx
+ {Remove this block from the linked list}
+ mov TMediumFreeBlock[ecx].PreviousFreeBlock, edx
+ mov TMediumFreeBlock[edx].NextFreeBlock, ecx
+ {Is this bin now empty? If the previous and next free block pointers are
+ equal, they must point to the bin.}
+ je @BinIsNowEmpty
+@Done:
+ ret
+ {Align branch target}
+ nop
+@BinIsNowEmpty:
+ {Get the bin number for this block size in ecx}
+ sub ecx, offset MediumBlockBins
+ mov edx, ecx
+ shr ecx, 3
+ {Get the group number in edx}
+ movzx edx, dh
+ {Flag this bin as empty}
+ mov eax, -2
+ rol eax, cl
+ and dword ptr [MediumBlockBinBitmaps + edx * 4], eax
+ jnz @Done
+ {Flag this group as empty}
+ mov eax, -2
+ mov ecx, edx
+ rol eax, cl
+ and MediumBlockBinGroupBitmap, eax
+end;
+{$else}
+asm
+ {On entry: rcx = APMediumFreeBlock}
+ mov rax, rcx
+ {Get the current previous and next blocks}
+ mov rcx, TMediumFreeBlock[rax].NextFreeBlock
+ mov rdx, TMediumFreeBlock[rax].PreviousFreeBlock
+ {Is this bin now empty? If the previous and next free block pointers are
+ equal, they must point to the bin.}
+ cmp rcx, rdx
+ {Remove this block from the linked list}
+ mov TMediumFreeBlock[rcx].PreviousFreeBlock, rdx
+ mov TMediumFreeBlock[rdx].NextFreeBlock, rcx
+ {Is this bin now empty? If the previous and next free block pointers are
+ equal, they must point to the bin.}
+ jne @Done
+ {Get the bin number for this block size in rcx}
+ lea r8, MediumBlockBins
+ sub rcx, r8
+ mov edx, ecx
+ shr ecx, 4
+ {Get the group number in edx}
+ shr edx, 9
+ {Flag this bin as empty}
+ mov eax, -2
+ rol eax, cl
+ lea r8, MediumBlockBinBitmaps
+ and dword ptr [r8 + rdx * 4], eax
+ jnz @Done
+ {Flag this group as empty}
+ mov eax, -2
+ mov ecx, edx
+ rol eax, cl
+ and MediumBlockBinGroupBitmap, eax
+@Done:
+end;
+{$endif}
+{$endif}
+
+{Inserts a medium block into the appropriate medium block bin.}
+procedure InsertMediumBlockIntoBin(APMediumFreeBlock: PMediumFreeBlock; AMediumBlockSize: Cardinal);
+{$ifndef ASMVersion}
+var
+ LBinNumber, LBinGroupNumber: Cardinal;
+ LPBin, LPFirstFreeBlock: PMediumFreeBlock;
+begin
+ {Get the bin number for this block size. Get the bin that holds blocks of at
+ least this size.}
+ LBinNumber := (AMediumBlockSize - MinimumMediumBlockSize) div MediumBlockGranularity;
+ if LBinNumber >= MediumBlockBinCount then
+ LBinNumber := MediumBlockBinCount - 1;
+ {Get the bin}
+ LPBin := @MediumBlockBins[LBinNumber];
+ {Bins are LIFO, se we insert this block as the first free block in the bin}
+ LPFirstFreeBlock := LPBin.NextFreeBlock;
+ APMediumFreeBlock.PreviousFreeBlock := LPBin;
+ APMediumFreeBlock.NextFreeBlock := LPFirstFreeBlock;
+ LPFirstFreeBlock.PreviousFreeBlock := APMediumFreeBlock;
+ LPBin.NextFreeBlock := APMediumFreeBlock;
+ {Was this bin empty?}
+ if LPFirstFreeBlock = LPBin then
+ begin
+ {Get the group number}
+ LBinGroupNumber := LBinNumber div 32;
+ {Flag this bin as used}
+ MediumBlockBinBitmaps[LBinGroupNumber] := MediumBlockBinBitmaps[LBinGroupNumber]
+ or (1 shl (LBinNumber and 31));
+ {Flag the group as used}
+ MediumBlockBinGroupBitmap := MediumBlockBinGroupBitmap
+ or (1 shl LBinGroupNumber);
+ end;
+end;
+{$else}
+{$ifdef 32Bit}
+asm
+ {On entry: eax = APMediumFreeBlock, edx = AMediumBlockSize}
+ {Get the bin number for this block size. Get the bin that holds blocks of at
+ least this size.}
+ sub edx, MinimumMediumBlockSize
+ shr edx, 8
+ {Validate the bin number}
+ sub edx, MediumBlockBinCount - 1
+ sbb ecx, ecx
+ and edx, ecx
+ add edx, MediumBlockBinCount - 1
+ {Get the bin in ecx}
+ lea ecx, [MediumBlockBins + edx * 8]
+ {Bins are LIFO, se we insert this block as the first free block in the bin}
+ mov edx, TMediumFreeBlock[ecx].NextFreeBlock
+ {Was this bin empty?}
+ cmp edx, ecx
+ mov TMediumFreeBlock[eax].PreviousFreeBlock, ecx
+ mov TMediumFreeBlock[eax].NextFreeBlock, edx
+ mov TMediumFreeBlock[edx].PreviousFreeBlock, eax
+ mov TMediumFreeBlock[ecx].NextFreeBlock, eax
+ {Was this bin empty?}
+ je @BinWasEmpty
+ ret
+ {Align branch target}
+ nop
+ nop
+@BinWasEmpty:
+ {Get the bin number in ecx}
+ sub ecx, offset MediumBlockBins
+ mov edx, ecx
+ shr ecx, 3
+ {Get the group number in edx}
+ movzx edx, dh
+ {Flag this bin as not empty}
+ mov eax, 1
+ shl eax, cl
+ or dword ptr [MediumBlockBinBitmaps + edx * 4], eax
+ {Flag the group as not empty}
+ mov eax, 1
+ mov ecx, edx
+ shl eax, cl
+ or MediumBlockBinGroupBitmap, eax
+end;
+{$else}
+asm
+ {On entry: rax = APMediumFreeBlock, edx = AMediumBlockSize}
+ mov rax, rcx
+ {Get the bin number for this block size. Get the bin that holds blocks of at
+ least this size.}
+ sub edx, MinimumMediumBlockSize
+ shr edx, 8
+ {Validate the bin number}
+ sub edx, MediumBlockBinCount - 1
+ sbb ecx, ecx
+ and edx, ecx
+ add edx, MediumBlockBinCount - 1
+ mov r9, rdx
+ {Get the bin address in rcx}
+ lea rcx, MediumBlockBins
+ shl edx, 4
+ add rcx, rdx
+ {Bins are LIFO, se we insert this block as the first free block in the bin}
+ mov rdx, TMediumFreeBlock[rcx].NextFreeBlock
+ {Was this bin empty?}
+ cmp rdx, rcx
+ mov TMediumFreeBlock[rax].PreviousFreeBlock, rcx
+ mov TMediumFreeBlock[rax].NextFreeBlock, rdx
+ mov TMediumFreeBlock[rdx].PreviousFreeBlock, rax
+ mov TMediumFreeBlock[rcx].NextFreeBlock, rax
+ {Was this bin empty?}
+ jne @Done
+ {Get the bin number in ecx}
+ mov rcx, r9
+ {Get the group number in edx}
+ mov rdx, r9
+ shr edx, 5
+ {Flag this bin as not empty}
+ mov eax, 1
+ shl eax, cl
+ lea r8, MediumBlockBinBitmaps
+ or dword ptr [r8 + rdx * 4], eax
+ {Flag the group as not empty}
+ mov eax, 1
+ mov ecx, edx
+ shl eax, cl
+ or MediumBlockBinGroupBitmap, eax
+@Done:
+end;
+{$endif}
+{$endif}
+
+{Bins what remains in the current sequential feed medium block pool. Medium
+ blocks must be locked.}
+procedure BinMediumSequentialFeedRemainder;
+{$ifndef ASMVersion}
+var
+ LSequentialFeedFreeSize, LNextBlockSizeAndFlags: NativeUInt;
+ LPRemainderBlock, LNextMediumBlock: Pointer;
+begin
+ LSequentialFeedFreeSize := MediumSequentialFeedBytesLeft;
+ if LSequentialFeedFreeSize > 0 then
+ begin
+ {Get the block after the open space}
+ LNextMediumBlock := LastSequentiallyFedMediumBlock;
+ LNextBlockSizeAndFlags := PNativeUInt(PByte(LNextMediumBlock) - BlockHeaderSize)^;
+ {Point to the remainder}
+ LPRemainderBlock := Pointer(PByte(LNextMediumBlock) - LSequentialFeedFreeSize);
+{$ifndef FullDebugMode}
+ {Can the next block be combined with the remainder?}
+ if (LNextBlockSizeAndFlags and IsFreeBlockFlag) <> 0 then
+ begin
+ {Increase the size of this block}
+ Inc(LSequentialFeedFreeSize, LNextBlockSizeAndFlags and DropMediumAndLargeFlagsMask);
+ {Remove the next block as well}
+ if (LNextBlockSizeAndFlags and DropMediumAndLargeFlagsMask) >= MinimumMediumBlockSize then
+ RemoveMediumFreeBlock(LNextMediumBlock);
+ end
+ else
+ begin
+{$endif}
+ {Set the "previous block is free" flag of the next block}
+ PNativeUInt(PByte(LNextMediumBlock) - BlockHeaderSize)^ := LNextBlockSizeAndFlags or PreviousMediumBlockIsFreeFlag;
+{$ifndef FullDebugMode}
+ end;
+{$endif}
+ {Store the size of the block as well as the flags}
+ PNativeUInt(PByte(LPRemainderBlock) - BlockHeaderSize)^ := LSequentialFeedFreeSize or IsMediumBlockFlag or IsFreeBlockFlag;
+ {Store the trailing size marker}
+ PNativeUInt(PByte(LPRemainderBlock) + LSequentialFeedFreeSize - BlockHeaderSize * 2)^ := LSequentialFeedFreeSize;
+{$ifdef FullDebugMode}
+ {In full debug mode the sequential feed remainder will never be too small to
+ fit a full debug header.}
+ {Clear the user area of the block}
+ DebugFillMem(Pointer(PByte(LPRemainderBlock) + SizeOf(TFullDebugBlockHeader) + SizeOf(NativeUInt))^,
+ LSequentialFeedFreeSize - FullDebugBlockOverhead - SizeOf(NativeUInt),
+ {$ifndef CatchUseOfFreedInterfaces}DebugFillPattern{$else}NativeUInt(@VMTBadInterface){$endif});
+ {We need to set a valid debug header and footer in the remainder}
+ PFullDebugBlockHeader(LPRemainderBlock).HeaderCheckSum := NativeUInt(LPRemainderBlock);
+ PNativeUInt(PByte(LPRemainderBlock) + SizeOf(TFullDebugBlockHeader))^ := not NativeUInt(LPRemainderBlock);
+{$endif}
+ {Bin this medium block}
+ if LSequentialFeedFreeSize >= MinimumMediumBlockSize then
+ InsertMediumBlockIntoBin(LPRemainderBlock, LSequentialFeedFreeSize);
+ end;
+end;
+{$else}
+{$ifdef 32Bit}
+asm
+ cmp MediumSequentialFeedBytesLeft, 0
+ jne @MustBinMedium
+ {Nothing to bin}
+ ret
+ {Align branch target}
+ nop
+ nop
+@MustBinMedium:
+ {Get a pointer to the last sequentially allocated medium block}
+ mov eax, LastSequentiallyFedMediumBlock
+ {Is the block that was last fed sequentially free?}
+ test byte ptr [eax - 4], IsFreeBlockFlag
+ jnz @LastBlockFedIsFree
+ {Set the "previous block is free" flag in the last block fed}
+ or dword ptr [eax - 4], PreviousMediumBlockIsFreeFlag
+ {Get the remainder in edx}
+ mov edx, MediumSequentialFeedBytesLeft
+ {Point eax to the start of the remainder}
+ sub eax, edx
+@BinTheRemainder:
+ {Status: eax = start of remainder, edx = size of remainder}
+ {Store the size of the block as well as the flags}
+ lea ecx, [edx + IsMediumBlockFlag + IsFreeBlockFlag]
+ mov [eax - 4], ecx
+ {Store the trailing size marker}
+ mov [eax + edx - 8], edx
+ {Bin this medium block}
+ cmp edx, MinimumMediumBlockSize
+ jnb InsertMediumBlockIntoBin
+ ret
+ {Align branch target}
+ nop
+ nop
+@LastBlockFedIsFree:
+ {Drop the flags}
+ mov edx, DropMediumAndLargeFlagsMask
+ and edx, [eax - 4]
+ {Free the last block fed}
+ cmp edx, MinimumMediumBlockSize
+ jb @DontRemoveLastFed
+ {Last fed block is free - remove it from its size bin}
+ call RemoveMediumFreeBlock
+ {Re-read eax and edx}
+ mov eax, LastSequentiallyFedMediumBlock
+ mov edx, DropMediumAndLargeFlagsMask
+ and edx, [eax - 4]
+@DontRemoveLastFed:
+ {Get the number of bytes left in ecx}
+ mov ecx, MediumSequentialFeedBytesLeft
+ {Point eax to the start of the remainder}
+ sub eax, ecx
+ {edx = total size of the remainder}
+ add edx, ecx
+ jmp @BinTheRemainder
+@Done:
+end;
+{$else}
+asm
+ .params 2
+ xor eax, eax
+ cmp MediumSequentialFeedBytesLeft, eax
+ je @Done
+ {Get a pointer to the last sequentially allocated medium block}
+ mov rax, LastSequentiallyFedMediumBlock
+ {Is the block that was last fed sequentially free?}
+ test byte ptr [rax - BlockHeaderSize], IsFreeBlockFlag
+ jnz @LastBlockFedIsFree
+ {Set the "previous block is free" flag in the last block fed}
+ or qword ptr [rax - BlockHeaderSize], PreviousMediumBlockIsFreeFlag
+ {Get the remainder in edx}
+ mov edx, MediumSequentialFeedBytesLeft
+ {Point eax to the start of the remainder}
+ sub rax, rdx
+@BinTheRemainder:
+ {Status: rax = start of remainder, edx = size of remainder}
+ {Store the size of the block as well as the flags}
+ lea rcx, [rdx + IsMediumBlockFlag + IsFreeBlockFlag]
+ mov [rax - BlockHeaderSize], rcx
+ {Store the trailing size marker}
+ mov [rax + rdx - 2 * BlockHeaderSize], rdx
+ {Bin this medium block}
+ cmp edx, MinimumMediumBlockSize
+ jb @Done
+ mov rcx, rax
+ call InsertMediumBlockIntoBin
+ jmp @Done
+@LastBlockFedIsFree:
+ {Drop the flags}
+ mov rdx, DropMediumAndLargeFlagsMask
+ and rdx, [rax - BlockHeaderSize]
+ {Free the last block fed}
+ cmp edx, MinimumMediumBlockSize
+ jb @DontRemoveLastFed
+ {Last fed block is free - remove it from its size bin}
+ mov rcx, rax
+ call RemoveMediumFreeBlock
+ {Re-read rax and rdx}
+ mov rax, LastSequentiallyFedMediumBlock
+ mov rdx, DropMediumAndLargeFlagsMask
+ and rdx, [rax - BlockHeaderSize]
+@DontRemoveLastFed:
+ {Get the number of bytes left in ecx}
+ mov ecx, MediumSequentialFeedBytesLeft
+ {Point rax to the start of the remainder}
+ sub rax, rcx
+ {edx = total size of the remainder}
+ add edx, ecx
+ jmp @BinTheRemainder
+@Done:
+end;
+{$endif}
+{$endif}
+
+{Allocates a new sequential feed medium block pool and immediately splits off a
+ block of the requested size. The block size must be a multiple of 16 and
+ medium blocks must be locked.}
+function AllocNewSequentialFeedMediumPool(AFirstBlockSize: Cardinal): Pointer;
+var
+ LOldFirstMediumBlockPool: PMediumBlockPoolHeader;
+ LNewPool: Pointer;
+begin
+ {Bin the current sequential feed remainder}
+ BinMediumSequentialFeedRemainder;
+ {Allocate a new sequential feed block pool}
+ LNewPool := VirtualAlloc(nil, MediumBlockPoolSize,
+ MEM_COMMIT{$ifdef AlwaysAllocateTopDown} or MEM_TOP_DOWN{$endif}, PAGE_READWRITE);
+ if LNewPool <> nil then
+ begin
+ {Insert this block pool into the list of block pools}
+ LOldFirstMediumBlockPool := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
+ PMediumBlockPoolHeader(LNewPool).PreviousMediumBlockPoolHeader := @MediumBlockPoolsCircularList;
+ MediumBlockPoolsCircularList.NextMediumBlockPoolHeader := LNewPool;
+ PMediumBlockPoolHeader(LNewPool).NextMediumBlockPoolHeader := LOldFirstMediumBlockPool;
+ LOldFirstMediumBlockPool.PreviousMediumBlockPoolHeader := LNewPool;
+ {Store the sequential feed pool trailer}
+ PNativeUInt(PByte(LNewPool) + MediumBlockPoolSize - BlockHeaderSize)^ := IsMediumBlockFlag;
+ {Get the number of bytes still available}
+ MediumSequentialFeedBytesLeft := (MediumBlockPoolSize - MediumBlockPoolHeaderSize) - AFirstBlockSize;
+ {Get the result}
+ Result := Pointer(PByte(LNewPool) + MediumBlockPoolSize - AFirstBlockSize);
+ LastSequentiallyFedMediumBlock := Result;
+ {Store the block header}
+ PNativeUInt(PByte(Result) - BlockHeaderSize)^ := AFirstBlockSize or IsMediumBlockFlag;
+ end
+ else
+ begin
+ {Out of memory}
+ MediumSequentialFeedBytesLeft := 0;
+ Result := nil;
+ end;
+end;
+
+{-----------------Large Block Management------------------}
+
+{Locks the large blocks}
+procedure LockLargeBlocks;
+begin
+ {Lock the large blocks}
+{$ifndef AssumeMultiThreaded}
+ if IsMultiThread then
+{$endif}
+ begin
+ while LockCmpxchg(0, 1, @LargeBlocksLocked) <> 0 do
+ begin
+{$ifdef NeverSleepOnThreadContention}
+ {$ifdef UseSwitchToThread}
+ SwitchToThread;
+ {$endif}
+{$else}
+ Sleep(InitialSleepTime);
+ if LockCmpxchg(0, 1, @LargeBlocksLocked) = 0 then
+ Break;
+ Sleep(AdditionalSleepTime);
+{$endif}
+ end;
+ end;
+end;
+
+{Allocates a Large block of at least ASize (actual size may be larger to
+ allow for alignment etc.). ASize must be the actual user requested size. This
+ procedure will pad it to the appropriate page boundary and also add the space
+ required by the header.}
+function AllocateLargeBlock(ASize: NativeUInt): Pointer;
+var
+ LLargeUsedBlockSize: NativeUInt;
+ LOldFirstLargeBlock: PLargeBlockHeader;
+begin
+ {Pad the block size to include the header and granularity. We also add a
+ SizeOf(Pointer) overhead so a huge block size is a multiple of 16 bytes less
+ SizeOf(Pointer) (so we can use a single move function for reallocating all
+ block types)}
+ LLargeUsedBlockSize := (ASize + LargeBlockHeaderSize + LargeBlockGranularity - 1 + BlockHeaderSize)
+ and -LargeBlockGranularity;
+ {Get the Large block}
+ Result := VirtualAlloc(nil, LLargeUsedBlockSize, MEM_COMMIT or MEM_TOP_DOWN,
+ PAGE_READWRITE);
+ {Set the Large block fields}
+ if Result <> nil then
+ begin
+ {Set the large block size and flags}
+ PLargeBlockHeader(Result).UserAllocatedSize := ASize;
+ PLargeBlockHeader(Result).BlockSizeAndFlags := LLargeUsedBlockSize or IsLargeBlockFlag;
+ {Insert the large block into the linked list of large blocks}
+ LockLargeBlocks;
+ LOldFirstLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
+ PLargeBlockHeader(Result).PreviousLargeBlockHeader := @LargeBlocksCircularList;
+ LargeBlocksCircularList.NextLargeBlockHeader := Result;
+ PLargeBlockHeader(Result).NextLargeBlockHeader := LOldFirstLargeBlock;
+ LOldFirstLargeBlock.PreviousLargeBlockHeader := Result;
+ LargeBlocksLocked := False;
+ {Add the size of the header}
+ Inc(PByte(Result), LargeBlockHeaderSize);
+{$ifdef FullDebugMode}
+ {Since large blocks are never reused, the user area is not initialized to
+ the debug fill pattern, but the debug header and footer must be set.}
+ PFullDebugBlockHeader(Result).HeaderCheckSum := NativeUInt(Result);
+ PNativeUInt(PByte(Result) + SizeOf(TFullDebugBlockHeader))^ := not NativeUInt(Result);
+{$endif}
+ end;
+end;
+
+{Frees a large block, returning 0 on success, -1 otherwise}
+function FreeLargeBlock(APointer: Pointer): Integer;
+var
+ LPreviousLargeBlockHeader, LNextLargeBlockHeader: PLargeBlockHeader;
+{$ifndef POSIX}
+ LRemainingSize: NativeUInt;
+ LCurrentSegment: Pointer;
+ LMemInfo: TMemoryBasicInformation;
+{$endif}
+begin
+{$ifdef ClearLargeBlocksBeforeReturningToOS}
+ FillChar(APointer^,
+ (PLargeBlockHeader(PByte(APointer) - LargeBlockHeaderSize).BlockSizeAndFlags
+ and DropMediumAndLargeFlagsMask) - LargeBlockHeaderSize, 0);
+{$endif}
+ {Point to the start of the large block}
+ APointer := Pointer(PByte(APointer) - LargeBlockHeaderSize);
+ {Get the previous and next large blocks}
+ LockLargeBlocks;
+ LPreviousLargeBlockHeader := PLargeBlockHeader(APointer).PreviousLargeBlockHeader;
+ LNextLargeBlockHeader := PLargeBlockHeader(APointer).NextLargeBlockHeader;
+{$ifndef POSIX}
+ {Is the large block segmented?}
+ if PLargeBlockHeader(APointer).BlockSizeAndFlags and LargeBlockIsSegmented = 0 then
+ begin
+{$endif}
+ {Single segment large block: Try to free it}
+ if VirtualFree(APointer, 0, MEM_RELEASE) then
+ Result := 0
+ else
+ Result := -1;
+{$ifndef POSIX}
+ end
+ else
+ begin
+ {The large block is segmented - free all segments}
+ LCurrentSegment := APointer;
+ LRemainingSize := PLargeBlockHeader(APointer).BlockSizeAndFlags and DropMediumAndLargeFlagsMask;
+ Result := 0;
+ while True do
+ begin
+ {Get the size of the current segment}
+ VirtualQuery(LCurrentSegment, LMemInfo, SizeOf(LMemInfo));
+ {Free the segment}
+ if not VirtualFree(LCurrentSegment, 0, MEM_RELEASE) then
+ begin
+ Result := -1;
+ Break;
+ end;
+ {Done?}
+ if NativeUInt(LMemInfo.RegionSize) >= LRemainingSize then
+ Break;
+ {Decrement the remaining size}
+ Dec(LRemainingSize, NativeUInt(LMemInfo.RegionSize));
+ Inc(PByte(LCurrentSegment), NativeUInt(LMemInfo.RegionSize));
+ end;
+ end;
+{$endif}
+ {Success?}
+ if Result = 0 then
+ begin
+ {Remove the large block from the linked list}
+ LNextLargeBlockHeader.PreviousLargeBlockHeader := LPreviousLargeBlockHeader;
+ LPreviousLargeBlockHeader.NextLargeBlockHeader := LNextLargeBlockHeader;
+ end;
+ {Unlock the large blocks}
+ LargeBlocksLocked := False;
+end;
+
+{$ifndef FullDebugMode}
+{Reallocates a large block to at least the requested size. Returns the new
+ pointer, or nil on error}
+function ReallocateLargeBlock(APointer: Pointer; ANewSize: NativeUInt): Pointer;
+var
+ LOldAvailableSize, LBlockHeader, LOldUserSize, LMinimumUpsize,
+ LNewAllocSize: NativeUInt;
+{$ifndef POSIX}
+ LNewSegmentSize: NativeUInt;
+ LNextSegmentPointer: Pointer;
+ LMemInfo: TMemoryBasicInformation;
+{$endif}
+begin
+ {Get the block header}
+ LBlockHeader := PNativeUInt(PByte(APointer) - BlockHeaderSize)^;
+ {Large block - size is (16 + 4) less than the allocated size}
+ LOldAvailableSize := (LBlockHeader and DropMediumAndLargeFlagsMask) - (LargeBlockHeaderSize + BlockHeaderSize);
+ {Is it an upsize or a downsize?}
+ if ANewSize > LOldAvailableSize then
+ begin
+ {This pointer is being reallocated to a larger block and therefore it is
+ logical to assume that it may be enlarged again. Since reallocations are
+ expensive, there is a minimum upsize percentage to avoid unnecessary
+ future move operations.}
+ {Add 25% for large block upsizes}
+ LMinimumUpsize := LOldAvailableSize + (LOldAvailableSize shr 2);
+ if ANewSize < LMinimumUpsize then
+ LNewAllocSize := LMinimumUpsize
+ else
+ LNewAllocSize := ANewSize;
+{$ifndef POSIX}
+ {Can another large block segment be allocated directly after this segment,
+ thus negating the need to move the data?}
+ LNextSegmentPointer := Pointer(PByte(APointer) - LargeBlockHeaderSize + (LBlockHeader and DropMediumAndLargeFlagsMask));
+ VirtualQuery(LNextSegmentPointer, LMemInfo, SizeOf(LMemInfo));
+ if LMemInfo.State = MEM_FREE then
+ begin
+ {Round the region size to the previous 64K}
+ LMemInfo.RegionSize := LMemInfo.RegionSize and -LargeBlockGranularity;
+ {Enough space to grow in place?}
+ if NativeUInt(LMemInfo.RegionSize) > (ANewSize - LOldAvailableSize) then
+ begin
+ {There is enough space after the block to extend it - determine by how
+ much}
+ LNewSegmentSize := (LNewAllocSize - LOldAvailableSize + LargeBlockGranularity - 1) and -LargeBlockGranularity;
+ if LNewSegmentSize > LMemInfo.RegionSize then
+ LNewSegmentSize := LMemInfo.RegionSize;
+ {Attempy to reserve the address range (which will fail if another
+ thread has just reserved it) and commit it immediately afterwards.}
+ if (VirtualAlloc(LNextSegmentPointer, LNewSegmentSize, MEM_RESERVE, PAGE_READWRITE) <> nil)
+ and (VirtualAlloc(LNextSegmentPointer, LNewSegmentSize, MEM_COMMIT, PAGE_READWRITE) <> nil) then
+ begin
+ {Update the requested size}
+ PLargeBlockHeader(PByte(APointer) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
+ PLargeBlockHeader(PByte(APointer) - LargeBlockHeaderSize).BlockSizeAndFlags :=
+ (PLargeBlockHeader(PByte(APointer) - LargeBlockHeaderSize).BlockSizeAndFlags + LNewSegmentSize)
+ or LargeBlockIsSegmented;
+ {Success}
+ Result := APointer;
+ Exit;
+ end;
+ end;
+ end;
+{$endif}
+ {Could not resize in place: Allocate the new block}
+ Result := FastGetMem(LNewAllocSize);
+ if Result <> nil then
+ begin
+ {If it's a large block - store the actual user requested size (it may
+ not be if the block that is being reallocated from was previously
+ downsized)}
+ if LNewAllocSize > (MaximumMediumBlockSize - BlockHeaderSize) then
+ PLargeBlockHeader(PByte(Result) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
+ {The user allocated size is stored for large blocks}
+ LOldUserSize := PLargeBlockHeader(PByte(APointer) - LargeBlockHeaderSize).UserAllocatedSize;
+ {The number of bytes to move is the old user size.}
+{$ifdef UseCustomVariableSizeMoveRoutines}
+ MoveX16LP(APointer^, Result^, LOldUserSize);
+{$else}
+ System.Move(APointer^, Result^, LOldUserSize);
+{$endif}
+ {Free the old block}
+ FastFreeMem(APointer);
+ end;
+ end
+ else
+ begin
+ {It's a downsize: do we need to reallocate? Only if the new size is less
+ than half the old size}
+ if ANewSize >= (LOldAvailableSize shr 1) then
+ begin
+ {No need to reallocate}
+ Result := APointer;
+ {Update the requested size}
+ PLargeBlockHeader(PByte(APointer) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
+ end
+ else
+ begin
+ {The block is less than half the old size, and the current size is
+ greater than the minimum block size allowing a downsize: reallocate}
+ Result := FastGetMem(ANewSize);
+ if Result <> nil then
+ begin
+ {Still a large block? -> Set the user size}
+ if ANewSize > (MaximumMediumBlockSize - BlockHeaderSize) then
+ PLargeBlockHeader(PByte(APointer) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
+ {Move the data across}
+{$ifdef UseCustomVariableSizeMoveRoutines}
+{$ifdef Align16Bytes}
+ MoveX16LP(APointer^, Result^, ANewSize);
+{$else}
+ MoveX8LP(APointer^, Result^, ANewSize);
+{$endif}
+{$else}
+ System.Move(APointer^, Result^, ANewSize);
+{$endif}
+ {Free the old block}
+ FastFreeMem(APointer);
+ end;
+ end;
+ end;
+end;
+{$endif}
+
+{---------------------Replacement Memory Manager Interface---------------------}
+
+{Replacement for SysGetMem}
+
+function FastGetMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
+{$ifndef ASMVersion}
+var
+ LMediumBlock{$ifndef FullDebugMode}, LNextFreeBlock, LSecondSplit{$endif}: PMediumFreeBlock;
+ LNextMediumBlockHeader: PNativeUInt;
+ LBlockSize, LAvailableBlockSize{$ifndef FullDebugMode}, LSecondSplitSize{$endif},
+ LSequentialFeedFreeSize: NativeUInt;
+ LPSmallBlockType: PSmallBlockType;
+ LPSmallBlockPool, LPNewFirstPool: PSmallBlockPoolHeader;
+ LNewFirstFreeBlock: Pointer;
+ LPMediumBin: PMediumFreeBlock;
+ LBinNumber, {$ifndef FullDebugMode}LBinGroupsMasked, {$endif}LBinGroupMasked,
+ LBinGroupNumber: Cardinal;
+begin
+ {Is it a small block? -> Take the header size into account when
+ determining the required block size}
+ if NativeUInt(ASize) <= (MaximumSmallBlockSize - BlockHeaderSize) then
+ begin
+ {-------------------------Allocate a small block---------------------------}
+ {Get the block type from the size}
+ LPSmallBlockType := PSmallBlockType(AllocSize2SmallBlockTypeIndX4[
+ (NativeUInt(ASize) + (BlockHeaderSize - 1)) div SmallBlockGranularity]
+ * (SizeOf(TSmallBlockType) div 4)
+ + UIntPtr(@SmallBlockTypes));
+ {Lock the block type}
+{$ifndef AssumeMultiThreaded}
+ if IsMultiThread then
+{$endif}
+ begin
+ while True do
+ begin
+ {Try to lock the small block type}
+ if LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) = 0 then
+ Break;
+ {Try the next block type}
+ Inc(PByte(LPSmallBlockType), SizeOf(TSmallBlockType));
+ if LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) = 0 then
+ Break;
+ {Try up to two sizes past the requested size}
+ Inc(PByte(LPSmallBlockType), SizeOf(TSmallBlockType));
+ if LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) = 0 then
+ Break;
+ {All three sizes locked - given up and sleep}
+ Dec(PByte(LPSmallBlockType), 2 * SizeOf(TSmallBlockType));
+{$ifdef NeverSleepOnThreadContention}
+ {$ifdef UseSwitchToThread}
+ SwitchToThread;
+ {$endif}
+{$else}
+ {Both this block type and the next is in use: sleep}
+ Sleep(InitialSleepTime);
+ {Try the lock again}
+ if LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) = 0 then
+ Break;
+ {Sleep longer}
+ Sleep(AdditionalSleepTime);
+{$endif}
+ end;
+ end;
+ {Get the first pool with free blocks}
+ LPSmallBlockPool := LPSmallBlockType.NextPartiallyFreePool;
+ {Is the pool valid?}
+ if UIntPtr(LPSmallBlockPool) <> UIntPtr(LPSmallBlockType) then
+ begin
+ {Get the first free offset}
+ Result := LPSmallBlockPool.FirstFreeBlock;
+ {Get the new first free block}
+ LNewFirstFreeBlock := PPointer(PByte(Result) - BlockHeaderSize)^;
+{$ifdef CheckHeapForCorruption}
+ {The block should be free}
+ if (NativeUInt(LNewFirstFreeBlock) and ExtractSmallFlagsMask) <> IsFreeBlockFlag then
+ {$ifdef BCB6OrDelphi7AndUp}
+ System.Error(reInvalidPtr);
+ {$else}
+ System.RunError(reInvalidPtr);
+ {$endif}
+{$endif}
+ LNewFirstFreeBlock := Pointer(UIntPtr(LNewFirstFreeBlock) and DropSmallFlagsMask);
+ {Increment the number of used blocks}
+ Inc(LPSmallBlockPool.BlocksInUse);
+ {Set the new first free block}
+ LPSmallBlockPool.FirstFreeBlock := LNewFirstFreeBlock;
+ {Is the pool now full?}
+ if LNewFirstFreeBlock = nil then
+ begin
+ {Pool is full - remove it from the partially free list}
+ LPNewFirstPool := LPSmallBlockPool.NextPartiallyFreePool;
+ LPSmallBlockType.NextPartiallyFreePool := LPNewFirstPool;
+ LPNewFirstPool.PreviousPartiallyFreePool := PSmallBlockPoolHeader(LPSmallBlockType);
+ end;
+ end
+ else
+ begin
+ {Try to feed a small block sequentially}
+ Result := LPSmallBlockType.NextSequentialFeedBlockAddress;
+ {Can another block fit?}
+ if UIntPtr(Result) <= UIntPtr(LPSmallBlockType.MaxSequentialFeedBlockAddress) then
+ begin
+ {Get the sequential feed block pool}
+ LPSmallBlockPool := LPSmallBlockType.CurrentSequentialFeedPool;
+ {Increment the number of used blocks in the sequential feed pool}
+ Inc(LPSmallBlockPool.BlocksInUse);
+ {Store the next sequential feed block address}
+ LPSmallBlockType.NextSequentialFeedBlockAddress := Pointer(PByte(Result) + LPSmallBlockType.BlockSize);
+ end
+ else
+ begin
+ {Need to allocate a pool: Lock the medium blocks}
+ LockMediumBlocks;
+{$ifndef FullDebugMode}
+ {Are there any available blocks of a suitable size?}
+ LBinGroupsMasked := MediumBlockBinGroupBitmap and ($ffffff00 or LPSmallBlockType.AllowedGroupsForBlockPoolBitmap);
+ if LBinGroupsMasked <> 0 then
+ begin
+ {Get the bin group with free blocks}
+ LBinGroupNumber := FindFirstSetBit(LBinGroupsMasked);
+ {Get the bin in the group with free blocks}
+ LBinNumber := FindFirstSetBit(MediumBlockBinBitmaps[LBinGroupNumber])
+ + LBinGroupNumber * 32;
+ LPMediumBin := @MediumBlockBins[LBinNumber];
+ {Get the first block in the bin}
+ LMediumBlock := LPMediumBin.NextFreeBlock;
+ {Remove the first block from the linked list (LIFO)}
+ LNextFreeBlock := LMediumBlock.NextFreeBlock;
+ LPMediumBin.NextFreeBlock := LNextFreeBlock;
+ LNextFreeBlock.PreviousFreeBlock := LPMediumBin;
+ {Is this bin now empty?}
+ if LNextFreeBlock = LPMediumBin then
+ begin
+ {Flag this bin as empty}
+ MediumBlockBinBitmaps[LBinGroupNumber] := MediumBlockBinBitmaps[LBinGroupNumber]
+ and (not (1 shl (LBinNumber and 31)));
+ {Is the group now entirely empty?}
+ if MediumBlockBinBitmaps[LBinGroupNumber] = 0 then
+ begin
+ {Flag this group as empty}
+ MediumBlockBinGroupBitmap := MediumBlockBinGroupBitmap
+ and (not (1 shl LBinGroupNumber));
+ end;
+ end;
+ {Get the size of the available medium block}
+ LBlockSize := PNativeUInt(PByte(LMediumBlock) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask;
+ {$ifdef CheckHeapForCorruption}
+ {Check that this block is actually free and the next and previous blocks
+ are both in use.}
+ if ((PNativeUInt(PByte(LMediumBlock) - BlockHeaderSize)^ and ExtractMediumAndLargeFlagsMask) <> (IsMediumBlockFlag or IsFreeBlockFlag))
+ or ((PNativeUInt(PByte(LMediumBlock) + (PNativeUInt(PByte(LMediumBlock) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask) - BlockHeaderSize)^ and IsFreeBlockFlag) <> 0)
+ then
+ begin
+ {$ifdef BCB6OrDelphi7AndUp}
+ System.Error(reInvalidPtr);
+ {$else}
+ System.RunError(reInvalidPtr);
+ {$endif}
+ end;
+ {$endif}
+ {Should the block be split?}
+ if LBlockSize >= MaximumSmallBlockPoolSize then
+ begin
+ {Get the size of the second split}
+ LSecondSplitSize := LBlockSize - LPSmallBlockType.OptimalBlockPoolSize;
+ {Adjust the block size}
+ LBlockSize := LPSmallBlockType.OptimalBlockPoolSize;
+ {Split the block in two}
+ LSecondSplit := PMediumFreeBlock(PByte(LMediumBlock) + LBlockSize);
+ PNativeUInt(PByte(LSecondSplit) - BlockHeaderSize)^ := LSecondSplitSize or (IsMediumBlockFlag or IsFreeBlockFlag);
+ {Store the size of the second split as the second last dword/qword}
+ PNativeUInt(PByte(LSecondSplit) + LSecondSplitSize - 2 * BlockHeaderSize)^ := LSecondSplitSize;
+ {Put the remainder in a bin (it will be big enough)}
+ InsertMediumBlockIntoBin(LSecondSplit, LSecondSplitSize);
+ end
+ else
+ begin
+ {Mark this block as used in the block following it}
+ LNextMediumBlockHeader := PNativeUInt(PByte(LMediumBlock) + LBlockSize - BlockHeaderSize);
+ LNextMediumBlockHeader^ := LNextMediumBlockHeader^ and (not PreviousMediumBlockIsFreeFlag);
+ end;
+ end
+ else
+ begin
+{$endif}
+ {Check the sequential feed medium block pool for space}
+ LSequentialFeedFreeSize := MediumSequentialFeedBytesLeft;
+ if LSequentialFeedFreeSize >= LPSmallBlockType.MinimumBlockPoolSize then
+ begin
+ {Enough sequential feed space: Will the remainder be usable?}
+ if LSequentialFeedFreeSize >= (LPSmallBlockType.OptimalBlockPoolSize + MinimumMediumBlockSize) then
+ begin
+ LBlockSize := LPSmallBlockType.OptimalBlockPoolSize;
+ end
+ else
+ LBlockSize := LSequentialFeedFreeSize;
+ {Get the block}
+ LMediumBlock := Pointer(PByte(LastSequentiallyFedMediumBlock) - LBlockSize);
+ {Update the sequential feed parameters}
+ LastSequentiallyFedMediumBlock := LMediumBlock;
+ MediumSequentialFeedBytesLeft := LSequentialFeedFreeSize - LBlockSize;
+ end
+ else
+ begin
+ {Need to allocate a new sequential feed medium block pool: use the
+ optimal size for this small block pool}
+ LBlockSize := LPSmallBlockType.OptimalBlockPoolSize;
+ {Allocate the medium block pool}
+ LMediumBlock := AllocNewSequentialFeedMediumPool(LBlockSize);
+ if LMediumBlock = nil then
+ begin
+ {Out of memory}
+ {Unlock the medium blocks}
+ MediumBlocksLocked := False;
+ {Unlock the block type}
+ LPSmallBlockType.BlockTypeLocked := False;
+ {Failed}
+ Result := nil;
+ {done}
+ Exit;
+ end;
+ end;
+{$ifndef FullDebugMode}
+ end;
+{$endif}
+ {Mark this block as in use}
+ {Set the size and flags for this block}
+ PNativeUInt(PByte(LMediumBlock) - BlockHeaderSize)^ := LBlockSize or IsMediumBlockFlag or IsSmallBlockPoolInUseFlag;
+ {Unlock medium blocks}
+ MediumBlocksLocked := False;
+ {Set up the block pool}
+ LPSmallBlockPool := PSmallBlockPoolHeader(LMediumBlock);
+ LPSmallBlockPool.BlockType := LPSmallBlockType;
+ LPSmallBlockPool.FirstFreeBlock := nil;
+ LPSmallBlockPool.BlocksInUse := 1;
+ {Set it up for sequential block serving}
+ LPSmallBlockType.CurrentSequentialFeedPool := LPSmallBlockPool;
+ Result := Pointer(PByte(LPSmallBlockPool) + SmallBlockPoolHeaderSize);
+ LPSmallBlockType.NextSequentialFeedBlockAddress := Pointer(PByte(Result) + LPSmallBlockType.BlockSize);
+ LPSmallBlockType.MaxSequentialFeedBlockAddress := Pointer(PByte(LPSmallBlockPool) + LBlockSize - LPSmallBlockType.BlockSize);
+ end;
+{$ifdef FullDebugMode}
+ {Clear the user area of the block}
+ DebugFillMem(Pointer(PByte(Result) + (SizeOf(TFullDebugBlockHeader) + SizeOf(NativeUInt)))^,
+ LPSmallBlockType.BlockSize - FullDebugBlockOverhead - SizeOf(NativeUInt),
+ {$ifndef CatchUseOfFreedInterfaces}DebugFillPattern{$else}NativeUInt(@VMTBadInterface){$endif});
+ {Block was fed sequentially - we need to set a valid debug header. Use
+ the block address.}
+ PFullDebugBlockHeader(Result).HeaderCheckSum := NativeUInt(Result);
+ PNativeUInt(PByte(Result) + SizeOf(TFullDebugBlockHeader))^ := not NativeUInt(Result);
+{$endif}
+ end;
+ {Unlock the block type}
+ LPSmallBlockType.BlockTypeLocked := False;
+ {Set the block header}
+ PNativeUInt(PByte(Result) - BlockHeaderSize)^ := UIntPtr(LPSmallBlockPool);
+ end
+ else
+ begin
+ {Medium block or Large block?}
+ if NativeUInt(ASize) <= (MaximumMediumBlockSize - BlockHeaderSize) then
+ begin
+ {------------------------Allocate a medium block--------------------------}
+ {Get the block size and bin number for this block size. Block sizes are
+ rounded up to the next bin size.}
+ LBlockSize := ((NativeUInt(ASize) + (MediumBlockGranularity - 1 + BlockHeaderSize - MediumBlockSizeOffset))
+ and -MediumBlockGranularity) + MediumBlockSizeOffset;
+ {Get the bin number}
+ LBinNumber := (LBlockSize - MinimumMediumBlockSize) div MediumBlockGranularity;
+ {Lock the medium blocks}
+ LockMediumBlocks;
+ {Calculate the bin group}
+ LBinGroupNumber := LBinNumber div 32;
+ {Is there a suitable block inside this group?}
+ LBinGroupMasked := MediumBlockBinBitmaps[LBinGroupNumber] and -(1 shl (LBinNumber and 31));
+ if LBinGroupMasked <> 0 then
+ begin
+ {Get the actual bin number}
+ LBinNumber := FindFirstSetBit(LBinGroupMasked) + LBinGroupNumber * 32;
+ end
+ else
+ begin
+{$ifndef FullDebugMode}
+ {Try all groups greater than this group}
+ LBinGroupsMasked := MediumBlockBinGroupBitmap and -(2 shl LBinGroupNumber);
+ if LBinGroupsMasked <> 0 then
+ begin
+ {There is a suitable group with space: get the bin number}
+ LBinGroupNumber := FindFirstSetBit(LBinGroupsMasked);
+ {Get the bin in the group with free blocks}
+ LBinNumber := FindFirstSetBit(MediumBlockBinBitmaps[LBinGroupNumber])
+ + LBinGroupNumber * 32;
+ end
+ else
+ begin
+{$endif}
+ {There are no bins with a suitable block: Sequentially feed the required block}
+ LSequentialFeedFreeSize := MediumSequentialFeedBytesLeft;
+ if LSequentialFeedFreeSize >= LBlockSize then
+ begin
+{$ifdef FullDebugMode}
+ {In full debug mode a medium block must have enough bytes to fit
+ all the debug info, so we must make sure there are no tiny medium
+ blocks at the start of the pool.}
+ if LSequentialFeedFreeSize - LBlockSize < (FullDebugBlockOverhead + BlockHeaderSize) then
+ LBlockSize := LSequentialFeedFreeSize;
+{$endif}
+ {Block can be fed sequentially}
+ Result := Pointer(PByte(LastSequentiallyFedMediumBlock) - LBlockSize);
+ {Store the last sequentially fed block}
+ LastSequentiallyFedMediumBlock := Result;
+ {Store the remaining bytes}
+ MediumSequentialFeedBytesLeft := LSequentialFeedFreeSize - LBlockSize;
+ {Set the flags for the block}
+ PNativeUInt(PByte(Result) - BlockHeaderSize)^ := LBlockSize or IsMediumBlockFlag;
+ end
+ else
+ begin
+ {Need to allocate a new sequential feed block}
+ Result := AllocNewSequentialFeedMediumPool(LBlockSize);
+ end;
+{$ifdef FullDebugMode}
+ {Block was fed sequentially - we need to set a valid debug header}
+ if Result <> nil then
+ begin
+ PFullDebugBlockHeader(Result).HeaderCheckSum := NativeUInt(Result);
+ PNativeUInt(PByte(Result) + SizeOf(TFullDebugBlockHeader))^ := not NativeUInt(Result);
+ {Clear the user area of the block}
+ DebugFillMem(Pointer(PByte(Result) + SizeOf(TFullDebugBlockHeader) + SizeOf(NativeUInt))^,
+ LBlockSize - FullDebugBlockOverhead - SizeOf(NativeUInt),
+ {$ifndef CatchUseOfFreedInterfaces}DebugFillPattern{$else}NativeUInt(@VMTBadInterface){$endif});
+ end;
+{$endif}
+ {Done}
+ MediumBlocksLocked := False;
+ Exit;
+{$ifndef FullDebugMode}
+ end;
+{$endif}
+ end;
+ {If we get here we have a valid LBinGroupNumber and LBinNumber:
+ Use the first block in the bin, splitting it if necessary}
+ {Get a pointer to the bin}
+ LPMediumBin := @MediumBlockBins[LBinNumber];
+ {Get the result}
+ Result := LPMediumBin.NextFreeBlock;
+{$ifdef CheckHeapForCorruption}
+ {Check that this block is actually free and the next and previous blocks
+ are both in use (except in full debug mode).}
+ if ((PNativeUInt(PByte(Result) - BlockHeaderSize)^ and {$ifndef FullDebugMode}ExtractMediumAndLargeFlagsMask{$else}(IsMediumBlockFlag or IsFreeBlockFlag){$endif}) <> (IsFreeBlockFlag or IsMediumBlockFlag))
+ {$ifndef FullDebugMode}
+ or ((PNativeUInt(PByte(Result) + (PNativeUInt(PByte(Result) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask) - BlockHeaderSize)^ and (ExtractMediumAndLargeFlagsMask - IsSmallBlockPoolInUseFlag)) <> (IsMediumBlockFlag or PreviousMediumBlockIsFreeFlag))
+ {$endif}
+ then
+ begin
+ {$ifdef BCB6OrDelphi7AndUp}
+ System.Error(reInvalidPtr);
+ {$else}
+ System.RunError(reInvalidPtr);
+ {$endif}
+ end;
+{$endif}
+ {Remove the block from the bin containing it}
+ RemoveMediumFreeBlock(Result);
+ {Get the block size}
+ LAvailableBlockSize := PNativeUInt(PByte(Result) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask;
+{$ifndef FullDebugMode}
+ {Is it an exact fit or not?}
+ LSecondSplitSize := LAvailableBlockSize - LBlockSize;
+ if LSecondSplitSize <> 0 then
+ begin
+ {Split the block in two}
+ LSecondSplit := PMediumFreeBlock(PByte(Result) + LBlockSize);
+ {Set the size of the second split}
+ PNativeUInt(PByte(LSecondSplit) - BlockHeaderSize)^ := LSecondSplitSize or (IsMediumBlockFlag or IsFreeBlockFlag);
+ {Store the size of the second split}
+ PNativeUInt(PByte(LSecondSplit) + LSecondSplitSize - 2 * BlockHeaderSize)^ := LSecondSplitSize;
+ {Put the remainder in a bin if it is big enough}
+ if LSecondSplitSize >= MinimumMediumBlockSize then
+ InsertMediumBlockIntoBin(LSecondSplit, LSecondSplitSize);
+ end
+ else
+ begin
+{$else}
+ {In full debug mode blocks are never split or coalesced}
+ LBlockSize := LAvailableBlockSize;
+{$endif}
+ {Mark this block as used in the block following it}
+ LNextMediumBlockHeader := Pointer(PByte(Result) + LBlockSize - BlockHeaderSize);
+{$ifndef FullDebugMode}
+ {$ifdef CheckHeapForCorruption}
+ {The next block must be in use}
+ if (LNextMediumBlockHeader^ and (ExtractMediumAndLargeFlagsMask - IsSmallBlockPoolInUseFlag)) <> (IsMediumBlockFlag or PreviousMediumBlockIsFreeFlag) then
+ {$ifdef BCB6OrDelphi7AndUp}
+ System.Error(reInvalidPtr);
+ {$else}
+ System.RunError(reInvalidPtr);
+ {$endif}
+ {$endif}
+{$endif}
+ LNextMediumBlockHeader^ :=
+ LNextMediumBlockHeader^ and (not PreviousMediumBlockIsFreeFlag);
+{$ifndef FullDebugMode}
+ end;
+ {Set the size and flags for this block}
+ PNativeUInt(PByte(Result) - BlockHeaderSize)^ := LBlockSize or IsMediumBlockFlag;
+{$else}
+ {In full debug mode blocks are never split or coalesced}
+ Dec(PNativeUInt(PByte(Result) - BlockHeaderSize)^, IsFreeBlockFlag);
+{$endif}
+ {Unlock the medium blocks}
+ MediumBlocksLocked := False;
+ end
+ else
+ begin
+ {Allocate a Large block}
+ if ASize > 0 then
+ Result := AllocateLargeBlock(ASize)
+ else
+ Result := nil;
+ end;
+ end;
+end;
+{$else}
+{$ifdef 32Bit}
+asm
+ {On entry:
+ eax = ASize}
+ {Since most allocations are for small blocks, determine the small block type
+ index so long}
+ lea edx, [eax + BlockHeaderSize - 1]
+{$ifdef Align16Bytes}
+ shr edx, 4
+{$else}
+ shr edx, 3
+{$endif}
+ {Is it a small block?}
+ cmp eax, (MaximumSmallBlockSize - BlockHeaderSize)
+ {Save ebx}
+ push ebx
+ {Get the IsMultiThread variable so long}
+{$ifndef AssumeMultiThreaded}
+ mov cl, IsMultiThread
+{$endif}
+ {Is it a small block?}
+ ja @NotASmallBlock
+ {Do we need to lock the block type?}
+{$ifndef AssumeMultiThreaded}
+ test cl, cl
+{$endif}
+ {Get the small block type in ebx}
+ movzx eax, byte ptr [AllocSize2SmallBlockTypeIndX4 + edx]
+ lea ebx, [SmallBlockTypes + eax * 8]
+ {Do we need to lock the block type?}
+{$ifndef AssumeMultiThreaded}
+ jnz @LockBlockTypeLoop
+{$else}
+ jmp @LockBlockTypeLoop
+ {Align branch target}
+ nop
+ nop
+{$endif}
+@GotLockOnSmallBlockType:
+ {Find the next free block: Get the first pool with free blocks in edx}
+ mov edx, TSmallBlockType[ebx].NextPartiallyFreePool
+ {Get the first free block (or the next sequential feed address if edx = ebx)}
+ mov eax, TSmallBlockPoolHeader[edx].FirstFreeBlock
+ {Get the drop flags mask in ecx so long}
+ mov ecx, DropSmallFlagsMask
+ {Is there a pool with free blocks?}
+ cmp edx, ebx
+ je @TrySmallSequentialFeed
+ {Increment the number of used blocks}
+ add TSmallBlockPoolHeader[edx].BlocksInUse, 1
+ {Get the new first free block}
+ and ecx, [eax - 4]
+ {Set the new first free block}
+ mov TSmallBlockPoolHeader[edx].FirstFreeBlock, ecx
+ {Set the block header}
+ mov [eax - 4], edx
+ {Is the chunk now full?}
+ jz @RemoveSmallPool
+ {Unlock the block type}
+ mov TSmallBlockType[ebx].BlockTypeLocked, False
+ {Restore ebx}
+ pop ebx
+ {All done}
+ ret
+ {Align branch target}
+{$ifndef AssumeMultiThreaded}
+ nop
+ nop
+{$endif}
+ nop
+@TrySmallSequentialFeed:
+ {Try to feed a small block sequentially: Get the sequential feed block pool}
+ mov edx, TSmallBlockType[ebx].CurrentSequentialFeedPool
+ {Get the next sequential feed address so long}
+ movzx ecx, TSmallBlockType[ebx].BlockSize
+ add ecx, eax
+ {Can another block fit?}
+ cmp eax, TSmallBlockType[ebx].MaxSequentialFeedBlockAddress
+ ja @AllocateSmallBlockPool
+ {Increment the number of used blocks in the sequential feed pool}
+ add TSmallBlockPoolHeader[edx].BlocksInUse, 1
+ {Store the next sequential feed block address}
+ mov TSmallBlockType[ebx].NextSequentialFeedBlockAddress, ecx
+ {Unlock the block type}
+ mov TSmallBlockType[ebx].BlockTypeLocked, False
+ {Set the block header}
+ mov [eax - 4], edx
+ {Restore ebx}
+ pop ebx
+ {All done}
+ ret
+ {Align branch target}
+ nop
+ nop
+ nop
+@RemoveSmallPool:
+ {Pool is full - remove it from the partially free list}
+ mov ecx, TSmallBlockPoolHeader[edx].NextPartiallyFreePool
+ mov TSmallBlockPoolHeader[ecx].PreviousPartiallyFreePool, ebx
+ mov TSmallBlockType[ebx].NextPartiallyFreePool, ecx
+ {Unlock the block type}
+ mov TSmallBlockType[ebx].BlockTypeLocked, False
+ {Restore ebx}
+ pop ebx
+ {All done}
+ ret
+ {Align branch target}
+ nop
+ nop
+@LockBlockTypeLoop:
+ mov eax, $100
+ {Attempt to grab the block type}
+ lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
+ je @GotLockOnSmallBlockType
+ {Try the next size}
+ add ebx, Type(TSmallBlockType)
+ mov eax, $100
+ lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
+ je @GotLockOnSmallBlockType
+ {Try the next size (up to two sizes larger)}
+ add ebx, Type(TSmallBlockType)
+ mov eax, $100
+ lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
+ je @GotLockOnSmallBlockType
+ {Block type and two sizes larger are all locked - give up and sleep}
+ sub ebx, 2 * Type(TSmallBlockType)
+{$ifdef NeverSleepOnThreadContention}
+ {Pause instruction (improves performance on P4)}
+ rep nop
+ {$ifdef UseSwitchToThread}
+ call SwitchToThread
+ {$endif}
+ {Try again}
+ jmp @LockBlockTypeLoop
+ {Align branch target}
+ nop
+ {$ifndef UseSwitchToThread}
+ nop
+ {$endif}
+{$else}
+ {Couldn't grab the block type - sleep and try again}
+ push InitialSleepTime
+ call Sleep
+ {Try again}
+ mov eax, $100
+ {Attempt to grab the block type}
+ lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
+ je @GotLockOnSmallBlockType
+ {Couldn't grab the block type - sleep and try again}
+ push AdditionalSleepTime
+ call Sleep
+ {Try again}
+ jmp @LockBlockTypeLoop
+ {Align branch target}
+ nop
+ nop
+ nop
+{$endif}
+@AllocateSmallBlockPool:
+ {save additional registers}
+ push esi
+ push edi
+ {Do we need to lock the medium blocks?}
+{$ifndef AssumeMultiThreaded}
+ cmp IsMultiThread, False
+ je @MediumBlocksLockedForPool
+{$endif}
+ call LockMediumBlocks
+@MediumBlocksLockedForPool:
+ {Are there any available blocks of a suitable size?}
+ movsx esi, TSmallBlockType[ebx].AllowedGroupsForBlockPoolBitmap
+ and esi, MediumBlockBinGroupBitmap
+ jz @NoSuitableMediumBlocks
+ {Get the bin group number with free blocks in eax}
+ bsf eax, esi
+ {Get the bin number in ecx}
+ lea esi, [eax * 8]
+ mov ecx, dword ptr [MediumBlockBinBitmaps + eax * 4]
+ bsf ecx, ecx
+ lea ecx, [ecx + esi * 4]
+ {Get a pointer to the bin in edi}
+ lea edi, [MediumBlockBins + ecx * 8]
+ {Get the free block in esi}
+ mov esi, TMediumFreeBlock[edi].NextFreeBlock
+ {Remove the first block from the linked list (LIFO)}
+ mov edx, TMediumFreeBlock[esi].NextFreeBlock
+ mov TMediumFreeBlock[edi].NextFreeBlock, edx
+ mov TMediumFreeBlock[edx].PreviousFreeBlock, edi
+ {Is this bin now empty?}
+ cmp edi, edx
+ jne @MediumBinNotEmpty
+ {eax = bin group number, ecx = bin number, edi = @bin, esi = free block, ebx = block type}
+ {Flag this bin as empty}
+ mov edx, -2
+ rol edx, cl
+ and dword ptr [MediumBlockBinBitmaps + eax * 4], edx
+ jnz @MediumBinNotEmpty
+ {Flag the group as empty}
+ btr MediumBlockBinGroupBitmap, eax
+@MediumBinNotEmpty:
+ {esi = free block, ebx = block type}
+ {Get the size of the available medium block in edi}
+ mov edi, DropMediumAndLargeFlagsMask
+ and edi, [esi - 4]
+ cmp edi, MaximumSmallBlockPoolSize
+ jb @UseWholeBlock
+ {Split the block: get the size of the second part, new block size is the
+ optimal size}
+ mov edx, edi
+ movzx edi, TSmallBlockType[ebx].OptimalBlockPoolSize
+ sub edx, edi
+ {Split the block in two}
+ lea eax, [esi + edi]
+ lea ecx, [edx + IsMediumBlockFlag + IsFreeBlockFlag]
+ mov [eax - 4], ecx
+ {Store the size of the second split as the second last dword}
+ mov [eax + edx - 8], edx
+ {Put the remainder in a bin (it will be big enough)}
+ call InsertMediumBlockIntoBin
+ jmp @GotMediumBlock
+ {Align branch target}
+{$ifdef AssumeMultiThreaded}
+ nop
+{$endif}
+@NoSuitableMediumBlocks:
+ {Check the sequential feed medium block pool for space}
+ movzx ecx, TSmallBlockType[ebx].MinimumBlockPoolSize
+ mov edi, MediumSequentialFeedBytesLeft
+ cmp edi, ecx
+ jb @AllocateNewSequentialFeed
+ {Get the address of the last block that was fed}
+ mov esi, LastSequentiallyFedMediumBlock
+ {Enough sequential feed space: Will the remainder be usable?}
+ movzx ecx, TSmallBlockType[ebx].OptimalBlockPoolSize
+ lea edx, [ecx + MinimumMediumBlockSize]
+ cmp edi, edx
+ jb @NotMuchSpace
+ mov edi, ecx
+@NotMuchSpace:
+ sub esi, edi
+ {Update the sequential feed parameters}
+ sub MediumSequentialFeedBytesLeft, edi
+ mov LastSequentiallyFedMediumBlock, esi
+ {Get the block pointer}
+ jmp @GotMediumBlock
+ {Align branch target}
+@AllocateNewSequentialFeed:
+ {Need to allocate a new sequential feed medium block pool: use the
+ optimal size for this small block pool}
+ movzx eax, TSmallBlockType[ebx].OptimalBlockPoolSize
+ mov edi, eax
+ {Allocate the medium block pool}
+ call AllocNewSequentialFeedMediumPool
+ mov esi, eax
+ test eax, eax
+ jnz @GotMediumBlock
+ mov MediumBlocksLocked, al
+ mov TSmallBlockType[ebx].BlockTypeLocked, al
+ pop edi
+ pop esi
+ pop ebx
+ ret
+ {Align branch target}
+@UseWholeBlock:
+ {esi = free block, ebx = block type, edi = block size}
+ {Mark this block as used in the block following it}
+ and byte ptr [esi + edi - 4], not PreviousMediumBlockIsFreeFlag
+@GotMediumBlock:
+ {esi = free block, ebx = block type, edi = block size}
+ {Set the size and flags for this block}
+ lea ecx, [edi + IsMediumBlockFlag + IsSmallBlockPoolInUseFlag]
+ mov [esi - 4], ecx
+ {Unlock medium blocks}
+ xor eax, eax
+ mov MediumBlocksLocked, al
+ {Set up the block pool}
+ mov TSmallBlockPoolHeader[esi].BlockType, ebx
+ mov TSmallBlockPoolHeader[esi].FirstFreeBlock, eax
+ mov TSmallBlockPoolHeader[esi].BlocksInUse, 1
+ {Set it up for sequential block serving}
+ mov TSmallBlockType[ebx].CurrentSequentialFeedPool, esi
+ {Return the pointer to the first block}
+ lea eax, [esi + SmallBlockPoolHeaderSize]
+ movzx ecx, TSmallBlockType[ebx].BlockSize
+ lea edx, [eax + ecx]
+ mov TSmallBlockType[ebx].NextSequentialFeedBlockAddress, edx
+ add edi, esi
+ sub edi, ecx
+ mov TSmallBlockType[ebx].MaxSequentialFeedBlockAddress, edi
+ {Unlock the small block type}
+ mov TSmallBlockType[ebx].BlockTypeLocked, False
+ {Set the small block header}
+ mov [eax - 4], esi
+ {Restore registers}
+ pop edi
+ pop esi
+ pop ebx
+ {Done}
+ ret
+{-------------------Medium block allocation-------------------}
+ {Align branch target}
+ nop
+@NotASmallBlock:
+ cmp eax, (MaximumMediumBlockSize - BlockHeaderSize)
+ ja @IsALargeBlockRequest
+ {Get the bin size for this block size. Block sizes are
+ rounded up to the next bin size.}
+ lea ebx, [eax + MediumBlockGranularity - 1 + BlockHeaderSize - MediumBlockSizeOffset]
+ and ebx, -MediumBlockGranularity
+ add ebx, MediumBlockSizeOffset
+ {Do we need to lock the medium blocks?}
+{$ifndef AssumeMultiThreaded}
+ test cl, cl
+ jz @MediumBlocksLocked
+{$endif}
+ call LockMediumBlocks
+@MediumBlocksLocked:
+ {Get the bin number in ecx and the group number in edx}
+ lea edx, [ebx - MinimumMediumBlockSize]
+ mov ecx, edx
+ shr edx, 8 + 5
+ shr ecx, 8
+ {Is there a suitable block inside this group?}
+ mov eax, -1
+ shl eax, cl
+ and eax, dword ptr [MediumBlockBinBitmaps + edx * 4]
+ jz @GroupIsEmpty
+ {Get the actual bin number}
+ and ecx, -32
+ bsf eax, eax
+ or ecx, eax
+ jmp @GotBinAndGroup
+ {Align branch target}
+ nop
+@GroupIsEmpty:
+ {Try all groups greater than this group}
+ mov eax, -2
+ mov ecx, edx
+ shl eax, cl
+ and eax, MediumBlockBinGroupBitmap
+ jz @TrySequentialFeedMedium
+ {There is a suitable group with space: get the bin number}
+ bsf edx, eax
+ {Get the bin in the group with free blocks}
+ mov eax, dword ptr [MediumBlockBinBitmaps + edx * 4]
+ bsf ecx, eax
+ mov eax, edx
+ shl eax, 5
+ or ecx, eax
+ jmp @GotBinAndGroup
+ {Align branch target}
+ nop
+@TrySequentialFeedMedium:
+ mov ecx, MediumSequentialFeedBytesLeft
+ {Block can be fed sequentially?}
+ sub ecx, ebx
+ jc @AllocateNewSequentialFeedForMedium
+ {Get the block address}
+ mov eax, LastSequentiallyFedMediumBlock
+ sub eax, ebx
+ mov LastSequentiallyFedMediumBlock, eax
+ {Store the remaining bytes}
+ mov MediumSequentialFeedBytesLeft, ecx
+ {Set the flags for the block}
+ or ebx, IsMediumBlockFlag
+ mov [eax - 4], ebx
+ jmp @MediumBlockGetDone
+ {Align branch target}
+@AllocateNewSequentialFeedForMedium:
+ mov eax, ebx
+ call AllocNewSequentialFeedMediumPool
+@MediumBlockGetDone:
+ mov MediumBlocksLocked, False
+ pop ebx
+ ret
+ {Align branch target}
+@GotBinAndGroup:
+ {ebx = block size, ecx = bin number, edx = group number}
+ push esi
+ push edi
+ {Get a pointer to the bin in edi}
+ lea edi, [MediumBlockBins + ecx * 8]
+ {Get the free block in esi}
+ mov esi, TMediumFreeBlock[edi].NextFreeBlock
+ {Remove the first block from the linked list (LIFO)}
+ mov eax, TMediumFreeBlock[esi].NextFreeBlock
+ mov TMediumFreeBlock[edi].NextFreeBlock, eax
+ mov TMediumFreeBlock[eax].PreviousFreeBlock, edi
+ {Is this bin now empty?}
+ cmp edi, eax
+ jne @MediumBinNotEmptyForMedium
+ {eax = bin group number, ecx = bin number, edi = @bin, esi = free block, ebx = block size}
+ {Flag this bin as empty}
+ mov eax, -2
+ rol eax, cl
+ and dword ptr [MediumBlockBinBitmaps + edx * 4], eax
+ jnz @MediumBinNotEmptyForMedium
+ {Flag the group as empty}
+ btr MediumBlockBinGroupBitmap, edx
+@MediumBinNotEmptyForMedium:
+ {esi = free block, ebx = block size}
+ {Get the size of the available medium block in edi}
+ mov edi, DropMediumAndLargeFlagsMask
+ and edi, [esi - 4]
+ {Get the size of the second split in edx}
+ mov edx, edi
+ sub edx, ebx
+ jz @UseWholeBlockForMedium
+ {Split the block in two}
+ lea eax, [esi + ebx]
+ lea ecx, [edx + IsMediumBlockFlag + IsFreeBlockFlag]
+ mov [eax - 4], ecx
+ {Store the size of the second split as the second last dword}
+ mov [eax + edx - 8], edx
+ {Put the remainder in a bin}
+ cmp edx, MinimumMediumBlockSize
+ jb @GotMediumBlockForMedium
+ call InsertMediumBlockIntoBin
+ jmp @GotMediumBlockForMedium
+ {Align branch target}
+ nop
+ nop
+ nop
+@UseWholeBlockForMedium:
+ {Mark this block as used in the block following it}
+ and byte ptr [esi + edi - 4], not PreviousMediumBlockIsFreeFlag
+@GotMediumBlockForMedium:
+ {Set the size and flags for this block}
+ lea ecx, [ebx + IsMediumBlockFlag]
+ mov [esi - 4], ecx
+ {Unlock medium blocks}
+ mov MediumBlocksLocked, False
+ mov eax, esi
+ pop edi
+ pop esi
+ pop ebx
+ ret
+{-------------------Large block allocation-------------------}
+ {Align branch target}
+@IsALargeBlockRequest:
+ pop ebx
+ test eax, eax
+ jns AllocateLargeBlock
+ xor eax, eax
+end;
+{$else}
+{64-bit BASM implementation}
+asm
+ {On entry:
+ rcx = ASize}
+ .params 2
+ .pushnv rbx
+ .pushnv rsi
+ .pushnv rdi
+ {Since most allocations are for small blocks, determine the small block type
+ index so long}
+ lea edx, [ecx + BlockHeaderSize - 1]
+{$ifdef Align16Bytes}
+ shr edx, 4
+{$else}
+ shr edx, 3
+{$endif}
+ {Preload the addresses of some small block structures}
+ lea r8, AllocSize2SmallBlockTypeIndX4
+ lea rbx, SmallBlockTypes
+{$ifndef AssumeMultiThreaded}
+ {Get the IsMultiThread variable so long}
+ movzx esi, IsMultiThread
+{$endif}
+ {Is it a small block?}
+ cmp rcx, (MaximumSmallBlockSize - BlockHeaderSize)
+ ja @NotASmallBlock
+ {Get the small block type pointer in rbx}
+ movzx ecx, byte ptr [r8 + rdx]
+ shl ecx, 4 //SizeOf(TSmallBlockType) = 64
+ add rbx, rcx
+ {Do we need to lock the block type?}
+{$ifndef AssumeMultiThreaded}
+ test esi, esi
+ jnz @LockBlockTypeLoop
+{$else}
+ jmp @LockBlockTypeLoop
+{$endif}
+@GotLockOnSmallBlockType:
+ {Find the next free block: Get the first pool with free blocks in rdx}
+ mov rdx, TSmallBlockType[rbx].NextPartiallyFreePool
+ {Get the first free block (or the next sequential feed address if rdx = rbx)}
+ mov rax, TSmallBlockPoolHeader[rdx].FirstFreeBlock
+ {Get the drop flags mask in rcx so long}
+ mov rcx, DropSmallFlagsMask
+ {Is there a pool with free blocks?}
+ cmp rdx, rbx
+ je @TrySmallSequentialFeed
+ {Increment the number of used blocks}
+ add TSmallBlockPoolHeader[rdx].BlocksInUse, 1
+ {Get the new first free block}
+ and rcx, [rax - BlockHeaderSize]
+ {Set the new first free block}
+ mov TSmallBlockPoolHeader[rdx].FirstFreeBlock, rcx
+ {Set the block header}
+ mov [rax - BlockHeaderSize], rdx
+ {Is the chunk now full?}
+ jz @RemoveSmallPool
+ {Unlock the block type}
+ mov TSmallBlockType[rbx].BlockTypeLocked, False
+ jmp @Done
+@TrySmallSequentialFeed:
+ {Try to feed a small block sequentially: Get the sequential feed block pool}
+ mov rdx, TSmallBlockType[rbx].CurrentSequentialFeedPool
+ {Get the next sequential feed address so long}
+ movzx ecx, TSmallBlockType[rbx].BlockSize
+ add rcx, rax
+ {Can another block fit?}
+ cmp rax, TSmallBlockType[rbx].MaxSequentialFeedBlockAddress
+ ja @AllocateSmallBlockPool
+ {Increment the number of used blocks in the sequential feed pool}
+ add TSmallBlockPoolHeader[rdx].BlocksInUse, 1
+ {Store the next sequential feed block address}
+ mov TSmallBlockType[rbx].NextSequentialFeedBlockAddress, rcx
+ {Unlock the block type}
+ mov TSmallBlockType[rbx].BlockTypeLocked, False
+ {Set the block header}
+ mov [rax - BlockHeaderSize], rdx
+ jmp @Done
+@RemoveSmallPool:
+ {Pool is full - remove it from the partially free list}
+ mov rcx, TSmallBlockPoolHeader[rdx].NextPartiallyFreePool
+ mov TSmallBlockPoolHeader[rcx].PreviousPartiallyFreePool, rbx
+ mov TSmallBlockType[rbx].NextPartiallyFreePool, rcx
+ {Unlock the block type}
+ mov TSmallBlockType[rbx].BlockTypeLocked, False
+ jmp @Done
+@LockBlockTypeLoop:
+ mov eax, $100
+ {Attempt to grab the block type}
+ lock cmpxchg TSmallBlockType([rbx]).BlockTypeLocked, ah
+ je @GotLockOnSmallBlockType
+ {Try the next size}
+ add rbx, Type(TSmallBlockType)
+ mov eax, $100
+ lock cmpxchg TSmallBlockType([rbx]).BlockTypeLocked, ah
+ je @GotLockOnSmallBlockType
+ {Try the next size (up to two sizes larger)}
+ add rbx, Type(TSmallBlockType)
+ mov eax, $100
+ lock cmpxchg TSmallBlockType([rbx]).BlockTypeLocked, ah
+ je @GotLockOnSmallBlockType
+ {Block type and two sizes larger are all locked - give up and sleep}
+ sub rbx, 2 * Type(TSmallBlockType)
+{$ifdef NeverSleepOnThreadContention}
+ {Pause instruction (improves performance on P4)}
+ pause
+ {$ifdef UseSwitchToThread}
+ call SwitchToThread
+ {$endif}
+ {Try again}
+ jmp @LockBlockTypeLoop
+{$else}
+ {Couldn't grab the block type - sleep and try again}
+ mov ecx, InitialSleepTime
+ call Sleep
+ {Try again}
+ mov eax, $100
+ {Attempt to grab the block type}
+ lock cmpxchg TSmallBlockType([rbx]).BlockTypeLocked, ah
+ je @GotLockOnSmallBlockType
+ {Couldn't grab the block type - sleep and try again}
+ mov ecx, AdditionalSleepTime
+ call Sleep
+ {Try again}
+ jmp @LockBlockTypeLoop
+{$endif}
+@AllocateSmallBlockPool:
+ {Do we need to lock the medium blocks?}
+{$ifndef AssumeMultiThreaded}
+ test esi, esi
+ jz @MediumBlocksLockedForPool
+{$endif}
+ call LockMediumBlocks
+@MediumBlocksLockedForPool:
+ {Are there any available blocks of a suitable size?}
+ movsx esi, TSmallBlockType[rbx].AllowedGroupsForBlockPoolBitmap
+ and esi, MediumBlockBinGroupBitmap
+ jz @NoSuitableMediumBlocks
+ {Get the bin group number with free blocks in eax}
+ bsf eax, esi
+ {Get the bin number in ecx}
+ lea r8, MediumBlockBinBitmaps
+ lea r9, [rax * 4]
+ mov ecx, [r8 + r9]
+ bsf ecx, ecx
+ lea ecx, [ecx + r9d * 8]
+ {Get a pointer to the bin in edi}
+ lea rdi, MediumBlockBins
+ lea esi, [ecx * 8]
+ lea rdi, [rdi + rsi * 2] //SizeOf(TMediumBlockBin) = 16
+ {Get the free block in rsi}
+ mov rsi, TMediumFreeBlock[rdi].NextFreeBlock
+ {Remove the first block from the linked list (LIFO)}
+ mov rdx, TMediumFreeBlock[rsi].NextFreeBlock
+ mov TMediumFreeBlock[rdi].NextFreeBlock, rdx
+ mov TMediumFreeBlock[rdx].PreviousFreeBlock, rdi
+ {Is this bin now empty?}
+ cmp rdi, rdx
+ jne @MediumBinNotEmpty
+ {r8 = @MediumBlockBinBitmaps, eax = bin group number,
+ r9 = bin group number * 4, ecx = bin number, edi = @bin, esi = free block,
+ ebx = block type}
+ {Flag this bin as empty}
+ mov edx, -2
+ rol edx, cl
+ and [r8 + r9], edx
+ jnz @MediumBinNotEmpty
+ {Flag the group as empty}
+ btr MediumBlockBinGroupBitmap, eax
+@MediumBinNotEmpty:
+ {esi = free block, ebx = block type}
+ {Get the size of the available medium block in edi}
+ mov rdi, DropMediumAndLargeFlagsMask
+ and rdi, [rsi - BlockHeaderSize]
+ cmp edi, MaximumSmallBlockPoolSize
+ jb @UseWholeBlock
+ {Split the block: get the size of the second part, new block size is the
+ optimal size}
+ mov edx, edi
+ movzx edi, TSmallBlockType[rbx].OptimalBlockPoolSize
+ sub edx, edi
+ {Split the block in two}
+ lea rcx, [rsi + rdi]
+ lea rax, [rdx + IsMediumBlockFlag + IsFreeBlockFlag]
+ mov [rcx - BlockHeaderSize], rax
+ {Store the size of the second split as the second last qword}
+ mov [rcx + rdx - BlockHeaderSize * 2], rdx
+ {Put the remainder in a bin (it will be big enough)}
+ call InsertMediumBlockIntoBin
+ jmp @GotMediumBlock
+@NoSuitableMediumBlocks:
+ {Check the sequential feed medium block pool for space}
+ movzx ecx, TSmallBlockType[rbx].MinimumBlockPoolSize
+ mov edi, MediumSequentialFeedBytesLeft
+ cmp edi, ecx
+ jb @AllocateNewSequentialFeed
+ {Get the address of the last block that was fed}
+ mov rsi, LastSequentiallyFedMediumBlock
+ {Enough sequential feed space: Will the remainder be usable?}
+ movzx ecx, TSmallBlockType[rbx].OptimalBlockPoolSize
+ lea edx, [ecx + MinimumMediumBlockSize]
+ cmp edi, edx
+ jb @NotMuchSpace
+ mov edi, ecx
+@NotMuchSpace:
+ sub rsi, rdi
+ {Update the sequential feed parameters}
+ sub MediumSequentialFeedBytesLeft, edi
+ mov LastSequentiallyFedMediumBlock, rsi
+ {Get the block pointer}
+ jmp @GotMediumBlock
+ {Align branch target}
+@AllocateNewSequentialFeed:
+ {Need to allocate a new sequential feed medium block pool: use the
+ optimal size for this small block pool}
+ movzx ecx, TSmallBlockType[rbx].OptimalBlockPoolSize
+ mov edi, ecx
+ {Allocate the medium block pool}
+ call AllocNewSequentialFeedMediumPool
+ mov rsi, rax
+ test rax, rax
+ jnz @GotMediumBlock
+ mov MediumBlocksLocked, al
+ mov TSmallBlockType[rbx].BlockTypeLocked, al
+ jmp @Done
+@UseWholeBlock:
+ {rsi = free block, rbx = block type, edi = block size}
+ {Mark this block as used in the block following it}
+ and byte ptr [rsi + rdi - BlockHeaderSize], not PreviousMediumBlockIsFreeFlag
+@GotMediumBlock:
+ {rsi = free block, rbx = block type, edi = block size}
+ {Set the size and flags for this block}
+ lea ecx, [edi + IsMediumBlockFlag + IsSmallBlockPoolInUseFlag]
+ mov [rsi - BlockHeaderSize], rcx
+ {Unlock medium blocks}
+ xor eax, eax
+ mov MediumBlocksLocked, al
+ {Set up the block pool}
+ mov TSmallBlockPoolHeader[rsi].BlockType, rbx
+ mov TSmallBlockPoolHeader[rsi].FirstFreeBlock, rax
+ mov TSmallBlockPoolHeader[rsi].BlocksInUse, 1
+ {Set it up for sequential block serving}
+ mov TSmallBlockType[rbx].CurrentSequentialFeedPool, rsi
+ {Return the pointer to the first block}
+ lea rax, [rsi + SmallBlockPoolHeaderSize]
+ movzx ecx, TSmallBlockType[rbx].BlockSize
+ lea rdx, [rax + rcx]
+ mov TSmallBlockType[rbx].NextSequentialFeedBlockAddress, rdx
+ add rdi, rsi
+ sub rdi, rcx
+ mov TSmallBlockType[rbx].MaxSequentialFeedBlockAddress, rdi
+ {Unlock the small block type}
+ mov TSmallBlockType[rbx].BlockTypeLocked, False
+ {Set the small block header}
+ mov [rax - BlockHeaderSize], rsi
+ jmp @Done
+{-------------------Medium block allocation-------------------}
+@NotASmallBlock:
+ cmp rcx, (MaximumMediumBlockSize - BlockHeaderSize)
+ ja @IsALargeBlockRequest
+ {Get the bin size for this block size. Block sizes are
+ rounded up to the next bin size.}
+ lea ebx, [ecx + MediumBlockGranularity - 1 + BlockHeaderSize - MediumBlockSizeOffset]
+ and ebx, -MediumBlockGranularity
+ add ebx, MediumBlockSizeOffset
+ {Do we need to lock the medium blocks?}
+{$ifndef AssumeMultiThreaded}
+ test esi, esi
+ jz @MediumBlocksLocked
+{$endif}
+ call LockMediumBlocks
+@MediumBlocksLocked:
+ {Get the bin number in ecx and the group number in edx}
+ lea edx, [ebx - MinimumMediumBlockSize]
+ mov ecx, edx
+ shr edx, 8 + 5
+ shr ecx, 8
+ {Is there a suitable block inside this group?}
+ mov eax, -1
+ shl eax, cl
+ lea r8, MediumBlockBinBitmaps
+ and eax, [r8 + rdx * 4]
+ jz @GroupIsEmpty
+ {Get the actual bin number}
+ and ecx, -32
+ bsf eax, eax
+ or ecx, eax
+ jmp @GotBinAndGroup
+@GroupIsEmpty:
+ {Try all groups greater than this group}
+ mov eax, -2
+ mov ecx, edx
+ shl eax, cl
+ and eax, MediumBlockBinGroupBitmap
+ jz @TrySequentialFeedMedium
+ {There is a suitable group with space: get the bin number}
+ bsf edx, eax
+ {Get the bin in the group with free blocks}
+ mov eax, [r8 + rdx * 4]
+ bsf ecx, eax
+ mov eax, edx
+ shl eax, 5
+ or ecx, eax
+ jmp @GotBinAndGroup
+@TrySequentialFeedMedium:
+ mov ecx, MediumSequentialFeedBytesLeft
+ {Block can be fed sequentially?}
+ sub ecx, ebx
+ jc @AllocateNewSequentialFeedForMedium
+ {Get the block address}
+ mov rax, LastSequentiallyFedMediumBlock
+ sub rax, rbx
+ mov LastSequentiallyFedMediumBlock, rax
+ {Store the remaining bytes}
+ mov MediumSequentialFeedBytesLeft, ecx
+ {Set the flags for the block}
+ or rbx, IsMediumBlockFlag
+ mov [rax - BlockHeaderSize], rbx
+ jmp @MediumBlockGetDone
+@AllocateNewSequentialFeedForMedium:
+ mov ecx, ebx
+ call AllocNewSequentialFeedMediumPool
+@MediumBlockGetDone:
+ xor cl, cl
+ mov MediumBlocksLocked, cl //workaround for QC99023
+ jmp @Done
+@GotBinAndGroup:
+ {ebx = block size, ecx = bin number, edx = group number}
+ {Get a pointer to the bin in edi}
+ lea rdi, MediumBlockBins
+ lea eax, [ecx + ecx]
+ lea rdi, [rdi + rax * 8]
+ {Get the free block in esi}
+ mov rsi, TMediumFreeBlock[rdi].NextFreeBlock
+ {Remove the first block from the linked list (LIFO)}
+ mov rax, TMediumFreeBlock[rsi].NextFreeBlock
+ mov TMediumFreeBlock[rdi].NextFreeBlock, rax
+ mov TMediumFreeBlock[rax].PreviousFreeBlock, rdi
+ {Is this bin now empty?}
+ cmp rdi, rax
+ jne @MediumBinNotEmptyForMedium
+ {edx = bin group number, ecx = bin number, rdi = @bin, rsi = free block, ebx = block size}
+ {Flag this bin as empty}
+ mov eax, -2
+ rol eax, cl
+ lea r8, MediumBlockBinBitmaps
+ and [r8 + rdx * 4], eax
+ jnz @MediumBinNotEmptyForMedium
+ {Flag the group as empty}
+ btr MediumBlockBinGroupBitmap, edx
+@MediumBinNotEmptyForMedium:
+ {rsi = free block, ebx = block size}
+ {Get the size of the available medium block in edi}
+ mov rdi, DropMediumAndLargeFlagsMask
+ and rdi, [rsi - BlockHeaderSize]
+ {Get the size of the second split in edx}
+ mov edx, edi
+ sub edx, ebx
+ jz @UseWholeBlockForMedium
+ {Split the block in two}
+ lea rcx, [rsi + rbx]
+ lea rax, [rdx + IsMediumBlockFlag + IsFreeBlockFlag]
+ mov [rcx - BlockHeaderSize], rax
+ {Store the size of the second split as the second last dword}
+ mov [rcx + rdx - BlockHeaderSize * 2], rdx
+ {Put the remainder in a bin}
+ cmp edx, MinimumMediumBlockSize
+ jb @GotMediumBlockForMedium
+ call InsertMediumBlockIntoBin
+ jmp @GotMediumBlockForMedium
+@UseWholeBlockForMedium:
+ {Mark this block as used in the block following it}
+ and byte ptr [rsi + rdi - BlockHeaderSize], not PreviousMediumBlockIsFreeFlag
+@GotMediumBlockForMedium:
+ {Set the size and flags for this block}
+ lea rcx, [rbx + IsMediumBlockFlag]
+ mov [rsi - BlockHeaderSize], rcx
+ {Unlock medium blocks}
+ xor cl, cl
+ mov MediumBlocksLocked, cl //workaround for QC99023
+ mov rax, rsi
+ jmp @Done
+{-------------------Large block allocation-------------------}
+@IsALargeBlockRequest:
+ xor rax, rax
+ test rcx, rcx
+ js @Done
+ call AllocateLargeBlock
+@Done:
+end;
+{$endif}
+{$endif}
+
+{$ifndef ASMVersion}
+{Frees a medium block, returning 0 on success, -1 otherwise}
+function FreeMediumBlock(APointer: Pointer): Integer;
+var
+ LNextMediumBlock{$ifndef FullDebugMode}, LPreviousMediumBlock{$endif}: PMediumFreeBlock;
+ LNextMediumBlockSizeAndFlags: NativeUInt;
+ LBlockSize{$ifndef FullDebugMode}, LPreviousMediumBlockSize{$endif}: Cardinal;
+{$ifndef FullDebugMode}
+ LPPreviousMediumBlockPoolHeader, LPNextMediumBlockPoolHeader: PMediumBlockPoolHeader;
+{$endif}
+ LBlockHeader: NativeUInt;
+begin
+ {Get the block header}
+ LBlockHeader := PNativeUInt(PByte(APointer) - BlockHeaderSize)^;
+ {Get the medium block size}
+ LBlockSize := LBlockHeader and DropMediumAndLargeFlagsMask;
+ {Lock the medium blocks}
+ LockMediumBlocks;
+ {Can we combine this block with the next free block?}
+ LNextMediumBlock := PMediumFreeBlock(PByte(APointer) + LBlockSize);
+ LNextMediumBlockSizeAndFlags := PNativeUInt(PByte(LNextMediumBlock) - BlockHeaderSize)^;
+{$ifndef FullDebugMode}
+{$ifdef CheckHeapForCorruption}
+ {Check that this block was flagged as in use in the next block}
+ if (LNextMediumBlockSizeAndFlags and PreviousMediumBlockIsFreeFlag) <> 0 then
+{$ifdef BCB6OrDelphi7AndUp}
+ System.Error(reInvalidPtr);
+{$else}
+ System.RunError(reInvalidPtr);
+{$endif}
+{$endif}
+ if (LNextMediumBlockSizeAndFlags and IsFreeBlockFlag) <> 0 then
+ begin
+ {Increase the size of this block}
+ Inc(LBlockSize, LNextMediumBlockSizeAndFlags and DropMediumAndLargeFlagsMask);
+ {Remove the next block as well}
+ if LNextMediumBlockSizeAndFlags >= MinimumMediumBlockSize then
+ RemoveMediumFreeBlock(LNextMediumBlock);
+ end
+ else
+ begin
+{$endif}
+ {Reset the "previous in use" flag of the next block}
+ PNativeUInt(PByte(LNextMediumBlock) - BlockHeaderSize)^ := LNextMediumBlockSizeAndFlags or PreviousMediumBlockIsFreeFlag;
+{$ifndef FullDebugMode}
+ end;
+ {Can we combine this block with the previous free block? We need to
+ re-read the flags since it could have changed before we could lock the
+ medium blocks.}
+ if (PNativeUInt(PByte(APointer) - BlockHeaderSize)^ and PreviousMediumBlockIsFreeFlag) <> 0 then
+ begin
+ {Get the size of the free block just before this one}
+ LPreviousMediumBlockSize := PNativeUInt(PByte(APointer) - 2 * BlockHeaderSize)^;
+ {Get the start of the previous block}
+ LPreviousMediumBlock := PMediumFreeBlock(PByte(APointer) - LPreviousMediumBlockSize);
+{$ifdef CheckHeapForCorruption}
+ {Check that the previous block is actually free}
+ if (PNativeUInt(PByte(LPreviousMediumBlock) - BlockHeaderSize)^ and ExtractMediumAndLargeFlagsMask) <> (IsMediumBlockFlag or IsFreeBlockFlag) then
+{$ifdef BCB6OrDelphi7AndUp}
+ System.Error(reInvalidPtr);
+{$else}
+ System.RunError(reInvalidPtr);
+{$endif}
+{$endif}
+ {Set the new block size}
+ Inc(LBlockSize, LPreviousMediumBlockSize);
+ {This is the new current block}
+ APointer := LPreviousMediumBlock;
+ {Remove the previous block from the linked list}
+ if LPreviousMediumBlockSize >= MinimumMediumBlockSize then
+ RemoveMediumFreeBlock(LPreviousMediumBlock);
+ end;
+{$ifdef CheckHeapForCorruption}
+ {Check that the previous block is currently flagged as in use}
+ if (PNativeUInt(PByte(APointer) - BlockHeaderSize)^ and PreviousMediumBlockIsFreeFlag) <> 0 then
+{$ifdef BCB6OrDelphi7AndUp}
+ System.Error(reInvalidPtr);
+{$else}
+ System.RunError(reInvalidPtr);
+{$endif}
+{$endif}
+ {Is the entire medium block pool free, and there are other free blocks
+ that can fit the largest possible medium block? -> free it. (Except in
+ full debug mode where medium pools are never freed.)}
+ if (LBlockSize <> (MediumBlockPoolSize - MediumBlockPoolHeaderSize)) then
+ begin
+ {Store the size of the block as well as the flags}
+ PNativeUInt(PByte(APointer) - BlockHeaderSize)^ := LBlockSize or (IsMediumBlockFlag or IsFreeBlockFlag);
+{$else}
+ {Mark the block as free}
+ Inc(PNativeUInt(PByte(APointer) - BlockHeaderSize)^, IsFreeBlockFlag);
+{$endif}
+ {Store the trailing size marker}
+ PNativeUInt(PByte(APointer) + LBlockSize - 2 * BlockHeaderSize)^ := LBlockSize;
+ {Insert this block back into the bins: Size check not required here,
+ since medium blocks that are in use are not allowed to be
+ shrunk smaller than MinimumMediumBlockSize}
+ InsertMediumBlockIntoBin(APointer, LBlockSize);
+{$ifndef FullDebugMode}
+{$ifdef CheckHeapForCorruption}
+ {Check that this block is actually free and the next and previous blocks are both in use.}
+ if ((PNativeUInt(PByte(APointer) - BlockHeaderSize)^ and ExtractMediumAndLargeFlagsMask) <> (IsMediumBlockFlag or IsFreeBlockFlag))
+ or ((PNativeUInt(PByte(APointer) + (PNativeUInt(PByte(APointer) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask) - BlockHeaderSize)^ and IsFreeBlockFlag) <> 0) then
+ begin
+{$ifdef BCB6OrDelphi7AndUp}
+ System.Error(reInvalidPtr);
+{$else}
+ System.RunError(reInvalidPtr);
+{$endif}
+ end;
+{$endif}
+{$endif}
+ {Unlock medium blocks}
+ MediumBlocksLocked := False;
+ {All OK}
+ Result := 0;
+{$ifndef FullDebugMode}
+ end
+ else
+ begin
+ {Should this become the new sequential feed?}
+ if MediumSequentialFeedBytesLeft <> MediumBlockPoolSize - MediumBlockPoolHeaderSize then
+ begin
+ {Bin the current sequential feed}
+ BinMediumSequentialFeedRemainder;
+ {Set this medium pool up as the new sequential feed pool:
+ Store the sequential feed pool trailer}
+ PNativeUInt(PByte(APointer) + LBlockSize - BlockHeaderSize)^ := IsMediumBlockFlag;
+ {Store the number of bytes available in the sequential feed chunk}
+ MediumSequentialFeedBytesLeft := MediumBlockPoolSize - MediumBlockPoolHeaderSize;
+ {Set the last sequentially fed block}
+ LastSequentiallyFedMediumBlock := Pointer(PByte(APointer) + LBlockSize);
+ {Unlock medium blocks}
+ MediumBlocksLocked := False;
+ {Success}
+ Result := 0;
+ end
+ else
+ begin
+ {Remove this medium block pool from the linked list}
+ Dec(PByte(APointer), MediumBlockPoolHeaderSize);
+ LPPreviousMediumBlockPoolHeader := PMediumBlockPoolHeader(APointer).PreviousMediumBlockPoolHeader;
+ LPNextMediumBlockPoolHeader := PMediumBlockPoolHeader(APointer).NextMediumBlockPoolHeader;
+ LPPreviousMediumBlockPoolHeader.NextMediumBlockPoolHeader := LPNextMediumBlockPoolHeader;
+ LPNextMediumBlockPoolHeader.PreviousMediumBlockPoolHeader := LPPreviousMediumBlockPoolHeader;
+ {Unlock medium blocks}
+ MediumBlocksLocked := False;
+{$ifdef ClearMediumBlockPoolsBeforeReturningToOS}
+ FillChar(APointer^, MediumBlockPoolSize, 0);
+{$endif}
+ {Free the medium block pool}
+ if VirtualFree(APointer, 0, MEM_RELEASE) then
+ Result := 0
+ else
+ Result := -1;
+ end;
+ end;
+{$endif}
+end;
+{$endif}
+
+{Replacement for SysFreeMem}
+function FastFreeMem(APointer: Pointer): Integer;
+{$ifndef ASMVersion}
+var
+ LPSmallBlockPool{$ifndef FullDebugMode}, LPPreviousPool, LPNextPool{$endif},
+ LPOldFirstPool: PSmallBlockPoolHeader;
+ LPSmallBlockType: PSmallBlockType;
+ LOldFirstFreeBlock: Pointer;
+ LBlockHeader: NativeUInt;
+begin
+ {Get the small block header: Is it actually a small block?}
+ LBlockHeader := PNativeUInt(PByte(APointer) - BlockHeaderSize)^;
+ {Is it a small block that is in use?}
+ if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag or IsLargeBlockFlag) = 0 then
+ begin
+ {Get a pointer to the block pool}
+ LPSmallBlockPool := PSmallBlockPoolHeader(LBlockHeader);
+ {Get the block type}
+ LPSmallBlockType := LPSmallBlockPool.BlockType;
+{$ifdef ClearSmallAndMediumBlocksInFreeMem}
+ FillChar(APointer^, LPSmallBlockType.BlockSize - BlockHeaderSize, 0);
+{$endif}
+ {Lock the block type}
+{$ifndef AssumeMultiThreaded}
+ if IsMultiThread then
+{$endif}
+ begin
+ while (LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) <> 0) do
+ begin
+{$ifdef NeverSleepOnThreadContention}
+ {$ifdef UseSwitchToThread}
+ SwitchToThread;
+ {$endif}
+{$else}
+ Sleep(InitialSleepTime);
+ if LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) = 0 then
+ Break;
+ Sleep(AdditionalSleepTime);
+{$endif}
+ end;
+ end;
+ {Get the old first free block}
+ LOldFirstFreeBlock := LPSmallBlockPool.FirstFreeBlock;
+ {Was the pool manager previously full?}
+ if LOldFirstFreeBlock = nil then
+ begin
+ {Insert this as the first partially free pool for the block size}
+ LPOldFirstPool := LPSmallBlockType.NextPartiallyFreePool;
+ LPSmallBlockPool.NextPartiallyFreePool := LPOldFirstPool;
+ LPOldFirstPool.PreviousPartiallyFreePool := LPSmallBlockPool;
+ LPSmallBlockPool.PreviousPartiallyFreePool := PSmallBlockPoolHeader(LPSmallBlockType);
+ LPSmallBlockType.NextPartiallyFreePool := LPSmallBlockPool;
+ end;
+ {Store the old first free block}
+ PNativeUInt(PByte(APointer) - BlockHeaderSize)^ := UIntPtr(LOldFirstFreeBlock) or IsFreeBlockFlag;
+ {Store this as the new first free block}
+ LPSmallBlockPool.FirstFreeBlock := APointer;
+ {Decrement the number of allocated blocks}
+ Dec(LPSmallBlockPool.BlocksInUse);
+ {Small block pools are never freed in full debug mode. This increases the
+ likehood of success in catching objects still being used after being
+ destroyed.}
+{$ifndef FullDebugMode}
+ {Is the entire pool now free? -> Free it.}
+ if LPSmallBlockPool.BlocksInUse = 0 then
+ begin
+ {Get the previous and next chunk managers}
+ LPPreviousPool := LPSmallBlockPool.PreviousPartiallyFreePool;
+ LPNextPool := LPSmallBlockPool.NextPartiallyFreePool;
+ {Remove this manager}
+ LPPreviousPool.NextPartiallyFreePool := LPNextPool;
+ LPNextPool.PreviousPartiallyFreePool := LPPreviousPool;
+ {Is this the sequential feed pool? If so, stop sequential feeding}
+ if (LPSmallBlockType.CurrentSequentialFeedPool = LPSmallBlockPool) then
+ LPSmallBlockType.MaxSequentialFeedBlockAddress := nil;
+ {Unlock this block type}
+ LPSmallBlockType.BlockTypeLocked := False;
+ {Free the block pool}
+ FreeMediumBlock(LPSmallBlockPool);
+ end
+ else
+ begin
+{$endif}
+ {Unlock this block type}
+ LPSmallBlockType.BlockTypeLocked := False;
+{$ifndef FullDebugMode}
+ end;
+{$endif}
+ {No error}
+ Result := 0;
+ end
+ else
+ begin
+ {Is this a medium block or a large block?}
+ if LBlockHeader and (IsFreeBlockFlag or IsLargeBlockFlag) = 0 then
+ begin
+{$ifdef ClearSmallAndMediumBlocksInFreeMem}
+ {Get the block header, extract the block size and clear the block it.}
+ LBlockHeader := PNativeUInt(PByte(APointer) - BlockHeaderSize)^;
+ FillChar(APointer^,
+ (LBlockHeader and DropMediumAndLargeFlagsMask) - BlockHeaderSize, 0);
+{$endif}
+ Result := FreeMediumBlock(APointer);
+ end
+ else
+ begin
+ {Validate: Is this actually a Large block, or is it an attempt to free an
+ already freed small block?}
+ if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag) = 0 then
+ Result := FreeLargeBlock(APointer)
+ else
+ Result := -1;
+ end;
+ end;
+end;
+{$else}
+{$ifdef 32Bit}
+asm
+ {Get the block header in edx}
+ mov edx, [eax - 4]
+ {Is it a small block in use?}
+ test dl, IsFreeBlockFlag + IsMediumBlockFlag + IsLargeBlockFlag
+ {Save the pointer in ecx}
+ mov ecx, eax
+ {Save ebx}
+ push ebx
+ {Get the IsMultiThread variable in bl}
+{$ifndef AssumeMultiThreaded}
+ mov bl, IsMultiThread
+{$endif}
+ {Is it a small block that is in use?}
+ jnz @NotSmallBlockInUse
+{$ifdef ClearSmallAndMediumBlocksInFreeMem}
+ push edx
+ push ecx
+ mov edx, TSmallBlockPoolHeader[edx].BlockType
+ movzx edx, TSmallBlockType(edx).BlockSize
+ sub edx, BlockHeaderSize
+ xor ecx, ecx
+ call System.@FillChar
+ pop ecx
+ pop edx
+{$endif}
+ {Do we need to lock the block type?}
+{$ifndef AssumeMultiThreaded}
+ test bl, bl
+{$endif}
+ {Get the small block type in ebx}
+ mov ebx, TSmallBlockPoolHeader[edx].BlockType
+ {Do we need to lock the block type?}
+{$ifndef AssumeMultiThreaded}
+ jnz @LockBlockTypeLoop
+{$else}
+ jmp @LockBlockTypeLoop
+ {Align branch target}
+ nop
+{$endif}
+@GotLockOnSmallBlockType:
+ {Current state: edx = @SmallBlockPoolHeader, ecx = APointer, ebx = @SmallBlockType}
+ {Decrement the number of blocks in use}
+ sub TSmallBlockPoolHeader[edx].BlocksInUse, 1
+ {Get the old first free block}
+ mov eax, TSmallBlockPoolHeader[edx].FirstFreeBlock
+ {Is the pool now empty?}
+ jz @PoolIsNowEmpty
+ {Was the pool full?}
+ test eax, eax
+ {Store this as the new first free block}
+ mov TSmallBlockPoolHeader[edx].FirstFreeBlock, ecx
+ {Store the previous first free block as the block header}
+ lea eax, [eax + IsFreeBlockFlag]
+ mov [ecx - 4], eax
+ {Insert the pool back into the linked list if it was full}
+ jz @SmallPoolWasFull
+ {All ok}
+ xor eax, eax
+ {Unlock the block type}
+ mov TSmallBlockType[ebx].BlockTypeLocked, al
+ {Restore registers}
+ pop ebx
+ {Done}
+ ret
+ {Align branch target}
+{$ifndef AssumeMultiThreaded}
+ nop
+{$endif}
+@SmallPoolWasFull:
+ {Insert this as the first partially free pool for the block size}
+ mov ecx, TSmallBlockType[ebx].NextPartiallyFreePool
+ mov TSmallBlockPoolHeader[edx].PreviousPartiallyFreePool, ebx
+ mov TSmallBlockPoolHeader[edx].NextPartiallyFreePool, ecx
+ mov TSmallBlockPoolHeader[ecx].PreviousPartiallyFreePool, edx
+ mov TSmallBlockType[ebx].NextPartiallyFreePool, edx
+ {Unlock the block type}
+ mov TSmallBlockType[ebx].BlockTypeLocked, False
+ {All ok}
+ xor eax, eax
+ {Restore registers}
+ pop ebx
+ {Done}
+ ret
+ {Align branch target}
+ nop
+ nop
+@PoolIsNowEmpty:
+ {Was this pool actually in the linked list of pools with space? If not, it
+ can only be the sequential feed pool (it is the only pool that may contain
+ only one block, i.e. other blocks have not been split off yet)}
+ test eax, eax
+ jz @IsSequentialFeedPool
+ {Pool is now empty: Remove it from the linked list and free it}
+ mov eax, TSmallBlockPoolHeader[edx].PreviousPartiallyFreePool
+ mov ecx, TSmallBlockPoolHeader[edx].NextPartiallyFreePool
+ {Remove this manager}
+ mov TSmallBlockPoolHeader[eax].NextPartiallyFreePool, ecx
+ mov TSmallBlockPoolHeader[ecx].PreviousPartiallyFreePool, eax
+ {Zero out eax}
+ xor eax, eax
+ {Is this the sequential feed pool? If so, stop sequential feeding}
+ cmp TSmallBlockType[ebx].CurrentSequentialFeedPool, edx
+ jne @NotSequentialFeedPool
+@IsSequentialFeedPool:
+ mov TSmallBlockType[ebx].MaxSequentialFeedBlockAddress, eax
+@NotSequentialFeedPool:
+ {Unlock the block type}
+ mov TSmallBlockType[ebx].BlockTypeLocked, al
+ {Release this pool}
+ mov eax, edx
+ mov edx, [edx - 4]
+{$ifndef AssumeMultiThreaded}
+ mov bl, IsMultiThread
+{$endif}
+ jmp @FreeMediumBlock
+ {Align branch target}
+{$ifndef AssumeMultiThreaded}
+ nop
+ nop
+{$endif}
+ nop
+@LockBlockTypeLoop:
+ mov eax, $100
+ {Attempt to grab the block type}
+ lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
+ je @GotLockOnSmallBlockType
+{$ifdef NeverSleepOnThreadContention}
+ {Pause instruction (improves performance on P4)}
+ rep nop
+ {$ifdef UseSwitchToThread}
+ push ecx
+ push edx
+ call SwitchToThread
+ pop edx
+ pop ecx
+ {$endif}
+ {Try again}
+ jmp @LockBlockTypeLoop
+ {Align branch target}
+ {$ifndef UseSwitchToThread}
+ nop
+ {$endif}
+{$else}
+ {Couldn't grab the block type - sleep and try again}
+ push ecx
+ push edx
+ push InitialSleepTime
+ call Sleep
+ pop edx
+ pop ecx
+ {Try again}
+ mov eax, $100
+ {Attempt to grab the block type}
+ lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
+ je @GotLockOnSmallBlockType
+ {Couldn't grab the block type - sleep and try again}
+ push ecx
+ push edx
+ push AdditionalSleepTime
+ call Sleep
+ pop edx
+ pop ecx
+ {Try again}
+ jmp @LockBlockTypeLoop
+ {Align branch target}
+ nop
+ nop
+{$endif}
+ {---------------------Medium blocks------------------------------}
+ {Align branch target}
+@NotSmallBlockInUse:
+ {Not a small block in use: is it a medium or large block?}
+ test dl, IsFreeBlockFlag + IsLargeBlockFlag
+ jnz @NotASmallOrMediumBlock
+@FreeMediumBlock:
+{$ifdef ClearSmallAndMediumBlocksInFreeMem}
+ push eax
+ push edx
+ and edx, DropMediumAndLargeFlagsMask
+ sub edx, BlockHeaderSize
+ xor ecx, ecx
+ call System.@FillChar
+ pop edx
+ pop eax
+{$endif}
+ {Drop the flags}
+ and edx, DropMediumAndLargeFlagsMask
+ {Free the medium block pointed to by eax, header in edx, bl = IsMultiThread}
+{$ifndef AssumeMultiThreaded}
+ {Do we need to lock the medium blocks?}
+ test bl, bl
+{$endif}
+ {Block size in ebx}
+ mov ebx, edx
+ {Save registers}
+ push esi
+ {Pointer in esi}
+ mov esi, eax
+ {Do we need to lock the medium blocks?}
+{$ifndef AssumeMultiThreaded}
+ jz @MediumBlocksLocked
+{$endif}
+ call LockMediumBlocks
+@MediumBlocksLocked:
+ {Can we combine this block with the next free block?}
+ test dword ptr [esi + ebx - 4], IsFreeBlockFlag
+ {Get the next block size and flags in ecx}
+ mov ecx, [esi + ebx - 4]
+ jnz @NextBlockIsFree
+ {Set the "PreviousIsFree" flag in the next block}
+ or ecx, PreviousMediumBlockIsFreeFlag
+ mov [esi + ebx - 4], ecx
+@NextBlockChecked:
+ {Can we combine this block with the previous free block? We need to
+ re-read the flags since it could have changed before we could lock the
+ medium blocks.}
+ test byte ptr [esi - 4], PreviousMediumBlockIsFreeFlag
+ jnz @PreviousBlockIsFree
+@PreviousBlockChecked:
+ {Is the entire medium block pool free, and there are other free blocks
+ that can fit the largest possible medium block -> free it.}
+ cmp ebx, (MediumBlockPoolSize - MediumBlockPoolHeaderSize)
+ je @EntireMediumPoolFree
+@BinFreeMediumBlock:
+ {Store the size of the block as well as the flags}
+ lea eax, [ebx + IsMediumBlockFlag + IsFreeBlockFlag]
+ mov [esi - 4], eax
+ {Store the trailing size marker}
+ mov [esi + ebx - 8], ebx
+ {Insert this block back into the bins: Size check not required here,
+ since medium blocks that are in use are not allowed to be
+ shrunk smaller than MinimumMediumBlockSize}
+ mov eax, esi
+ mov edx, ebx
+ {Insert into bin}
+ call InsertMediumBlockIntoBin
+ {Unlock medium blocks}
+ mov MediumBlocksLocked, False;
+ {All OK}
+ xor eax, eax
+ {Restore registers}
+ pop esi
+ pop ebx
+ {Return}
+ ret
+ {Align branch target}
+@NextBlockIsFree:
+ {Get the next block address in eax}
+ lea eax, [esi + ebx]
+ {Increase the size of this block}
+ and ecx, DropMediumAndLargeFlagsMask
+ add ebx, ecx
+ {Was the block binned?}
+ cmp ecx, MinimumMediumBlockSize
+ jb @NextBlockChecked
+ call RemoveMediumFreeBlock
+ jmp @NextBlockChecked
+ {Align branch target}
+ nop
+@PreviousBlockIsFree:
+ {Get the size of the free block just before this one}
+ mov ecx, [esi - 8]
+ {Include the previous block}
+ sub esi, ecx
+ {Set the new block size}
+ add ebx, ecx
+ {Remove the previous block from the linked list}
+ cmp ecx, MinimumMediumBlockSize
+ jb @PreviousBlockChecked
+ mov eax, esi
+ call RemoveMediumFreeBlock
+ jmp @PreviousBlockChecked
+ {Align branch target}
+@EntireMediumPoolFree:
+ {Should we make this the new sequential feed medium block pool? If the
+ current sequential feed pool is not entirely free, we make this the new
+ sequential feed pool.}
+ cmp MediumSequentialFeedBytesLeft, MediumBlockPoolSize - MediumBlockPoolHeaderSize
+ jne @MakeEmptyMediumPoolSequentialFeed
+ {Point esi to the medium block pool header}
+ sub esi, MediumBlockPoolHeaderSize
+ {Remove this medium block pool from the linked list}
+ mov eax, TMediumBlockPoolHeader[esi].PreviousMediumBlockPoolHeader
+ mov edx, TMediumBlockPoolHeader[esi].NextMediumBlockPoolHeader
+ mov TMediumBlockPoolHeader[eax].NextMediumBlockPoolHeader, edx
+ mov TMediumBlockPoolHeader[edx].PreviousMediumBlockPoolHeader, eax
+ {Unlock medium blocks}
+ mov MediumBlocksLocked, False;
+{$ifdef ClearMediumBlockPoolsBeforeReturningToOS}
+ mov eax, esi
+ mov edx, MediumBlockPoolSize
+ xor ecx, ecx
+ call System.@FillChar
+{$endif}
+ {Free the medium block pool}
+ push MEM_RELEASE
+ push 0
+ push esi
+ call VirtualFree
+ {VirtualFree returns >0 if all is ok}
+ cmp eax, 1
+ {Return 0 on all ok}
+ sbb eax, eax
+ {Restore registers}
+ pop esi
+ pop ebx
+ ret
+ {Align branch target}
+ nop
+ nop
+ nop
+@MakeEmptyMediumPoolSequentialFeed:
+ {Get a pointer to the end-marker block}
+ lea ebx, [esi + MediumBlockPoolSize - MediumBlockPoolHeaderSize]
+ {Bin the current sequential feed pool}
+ call BinMediumSequentialFeedRemainder
+ {Set this medium pool up as the new sequential feed pool:
+ Store the sequential feed pool trailer}
+ mov dword ptr [ebx - BlockHeaderSize], IsMediumBlockFlag
+ {Store the number of bytes available in the sequential feed chunk}
+ mov MediumSequentialFeedBytesLeft, MediumBlockPoolSize - MediumBlockPoolHeaderSize
+ {Set the last sequentially fed block}
+ mov LastSequentiallyFedMediumBlock, ebx
+ {Unlock medium blocks}
+ mov MediumBlocksLocked, False;
+ {Success}
+ xor eax, eax
+ {Restore registers}
+ pop esi
+ pop ebx
+ ret
+ {Align branch target}
+ nop
+ nop
+@NotASmallOrMediumBlock:
+ {Restore ebx}
+ pop ebx
+ {Is it in fact a large block?}
+ test dl, IsFreeBlockFlag + IsMediumBlockFlag
+ jz FreeLargeBlock
+ {Attempt to free an already free block}
+ mov eax, -1
+end;
+
+{$else}
+
+{---------------64-bit BASM FastFreeMem---------------}
+asm
+ .params 3
+ .pushnv rbx
+ .pushnv rsi
+ {Get the block header in rdx}
+ mov rdx, [rcx - BlockHeaderSize]
+ {Is it a small block in use?}
+ test dl, IsFreeBlockFlag + IsMediumBlockFlag + IsLargeBlockFlag
+ {Get the IsMultiThread variable in bl}
+{$ifndef AssumeMultiThreaded}
+ mov bl, IsMultiThread
+{$endif}
+ {Is it a small block that is in use?}
+ jnz @NotSmallBlockInUse
+{$ifdef ClearSmallAndMediumBlocksInFreeMem}
+ mov rsi, rcx
+ mov rdx, TSmallBlockPoolHeader[rdx].BlockType
+ movzx edx, TSmallBlockType(rdx).BlockSize
+ sub edx, BlockHeaderSize
+ xor r8, r8
+ call System.@FillChar
+ mov rcx, rsi
+ mov rdx, [rcx - BlockHeaderSize]
+{$endif}
+ {Do we need to lock the block type?}
+{$ifndef AssumeMultiThreaded}
+ test bl, bl
+{$endif}
+ {Get the small block type in rbx}
+ mov rbx, TSmallBlockPoolHeader[rdx].BlockType
+ {Do we need to lock the block type?}
+{$ifndef AssumeMultiThreaded}
+ jnz @LockBlockTypeLoop
+{$else}
+ jmp @LockBlockTypeLoop
+{$endif}
+@GotLockOnSmallBlockType:
+ {Current state: rdx = @SmallBlockPoolHeader, rcx = APointer, rbx = @SmallBlockType}
+ {Decrement the number of blocks in use}
+ sub TSmallBlockPoolHeader[rdx].BlocksInUse, 1
+ {Get the old first free block}
+ mov rax, TSmallBlockPoolHeader[rdx].FirstFreeBlock
+ {Is the pool now empty?}
+ jz @PoolIsNowEmpty
+ {Was the pool full?}
+ test rax, rax
+ {Store this as the new first free block}
+ mov TSmallBlockPoolHeader[rdx].FirstFreeBlock, rcx
+ {Store the previous first free block as the block header}
+ lea rax, [rax + IsFreeBlockFlag]
+ mov [rcx - BlockHeaderSize], rax
+ {Insert the pool back into the linked list if it was full}
+ jz @SmallPoolWasFull
+ {All ok}
+ xor eax, eax
+ {Unlock the block type}
+ mov TSmallBlockType[rbx].BlockTypeLocked, al
+ jmp @Done
+@SmallPoolWasFull:
+ {Insert this as the first partially free pool for the block size}
+ mov rcx, TSmallBlockType[rbx].NextPartiallyFreePool
+ mov TSmallBlockPoolHeader[rdx].PreviousPartiallyFreePool, rbx
+ mov TSmallBlockPoolHeader[rdx].NextPartiallyFreePool, rcx
+ mov TSmallBlockPoolHeader[rcx].PreviousPartiallyFreePool, rdx
+ mov TSmallBlockType[rbx].NextPartiallyFreePool, rdx
+ {Unlock the block type}
+ mov TSmallBlockType[rbx].BlockTypeLocked, False
+ {All ok}
+ xor eax, eax
+ jmp @Done
+@PoolIsNowEmpty:
+ {Was this pool actually in the linked list of pools with space? If not, it
+ can only be the sequential feed pool (it is the only pool that may contain
+ only one block, i.e. other blocks have not been split off yet)}
+ test rax, rax
+ jz @IsSequentialFeedPool
+ {Pool is now empty: Remove it from the linked list and free it}
+ mov rax, TSmallBlockPoolHeader[rdx].PreviousPartiallyFreePool
+ mov rcx, TSmallBlockPoolHeader[rdx].NextPartiallyFreePool
+ {Remove this manager}
+ mov TSmallBlockPoolHeader[rax].NextPartiallyFreePool, rcx
+ mov TSmallBlockPoolHeader[rcx].PreviousPartiallyFreePool, rax
+ {Zero out eax}
+ xor rax, rax
+ {Is this the sequential feed pool? If so, stop sequential feeding}
+ cmp TSmallBlockType[rbx].CurrentSequentialFeedPool, rdx
+ jne @NotSequentialFeedPool
+@IsSequentialFeedPool:
+ mov TSmallBlockType[rbx].MaxSequentialFeedBlockAddress, rax
+@NotSequentialFeedPool:
+ {Unlock the block type}
+ mov TSmallBlockType[rbx].BlockTypeLocked, al
+ {Release this pool}
+ mov rcx, rdx
+ mov rdx, [rdx - BlockHeaderSize]
+{$ifndef AssumeMultiThreaded}
+ mov bl, IsMultiThread
+{$endif}
+ jmp @FreeMediumBlock
+@LockBlockTypeLoop:
+ mov eax, $100
+ {Attempt to grab the block type}
+ lock cmpxchg TSmallBlockType([rbx]).BlockTypeLocked, ah
+ je @GotLockOnSmallBlockType
+{$ifdef NeverSleepOnThreadContention}
+ {Pause instruction (improves performance on P4)}
+ pause
+ {$ifdef UseSwitchToThread}
+ mov rsi, rcx
+ call SwitchToThread
+ mov rcx, rsi
+ mov rdx, [rcx - BlockHeaderSize]
+ {$endif}
+ {Try again}
+ jmp @LockBlockTypeLoop
+{$else}
+ {Couldn't grab the block type - sleep and try again}
+ mov rsi, rcx
+ mov ecx, InitialSleepTime
+ call Sleep
+ mov rcx, rsi
+ mov rdx, [rcx - BlockHeaderSize]
+ {Try again}
+ mov eax, $100
+ {Attempt to grab the block type}
+ lock cmpxchg TSmallBlockType([rbx]).BlockTypeLocked, ah
+ je @GotLockOnSmallBlockType
+ {Couldn't grab the block type - sleep and try again}
+ mov rsi, rcx
+ mov ecx, AdditionalSleepTime
+ call Sleep
+ mov rcx, rsi
+ mov rdx, [rcx - BlockHeaderSize]
+ {Try again}
+ jmp @LockBlockTypeLoop
+{$endif}
+ {---------------------Medium blocks------------------------------}
+@NotSmallBlockInUse:
+ {Not a small block in use: is it a medium or large block?}
+ test dl, IsFreeBlockFlag + IsLargeBlockFlag
+ jnz @NotASmallOrMediumBlock
+@FreeMediumBlock:
+{$ifdef ClearSmallAndMediumBlocksInFreeMem}
+ mov rsi, rcx
+ and rdx, DropMediumAndLargeFlagsMask
+ sub rdx, BlockHeaderSize
+ xor r8, r8
+ call System.@FillChar
+ mov rcx, rsi
+ mov rdx, [rcx - BlockHeaderSize]
+{$endif}
+ {Drop the flags}
+ and rdx, DropMediumAndLargeFlagsMask
+ {Free the medium block pointed to by eax, header in edx, bl = IsMultiThread}
+{$ifndef AssumeMultiThreaded}
+ {Do we need to lock the medium blocks?}
+ test bl, bl
+{$endif}
+ {Block size in rbx}
+ mov rbx, rdx
+ {Pointer in rsi}
+ mov rsi, rcx
+ {Do we need to lock the medium blocks?}
+{$ifndef AssumeMultiThreaded}
+ jz @MediumBlocksLocked
+{$endif}
+ call LockMediumBlocks
+@MediumBlocksLocked:
+ {Can we combine this block with the next free block?}
+ test qword ptr [rsi + rbx - BlockHeaderSize], IsFreeBlockFlag
+ {Get the next block size and flags in rcx}
+ mov rcx, [rsi + rbx - BlockHeaderSize]
+ jnz @NextBlockIsFree
+ {Set the "PreviousIsFree" flag in the next block}
+ or rcx, PreviousMediumBlockIsFreeFlag
+ mov [rsi + rbx - BlockHeaderSize], rcx
+@NextBlockChecked:
+ {Can we combine this block with the previous free block? We need to
+ re-read the flags since it could have changed before we could lock the
+ medium blocks.}
+ test byte ptr [rsi - BlockHeaderSize], PreviousMediumBlockIsFreeFlag
+ jnz @PreviousBlockIsFree
+@PreviousBlockChecked:
+ {Is the entire medium block pool free, and there are other free blocks
+ that can fit the largest possible medium block -> free it.}
+ cmp ebx, (MediumBlockPoolSize - MediumBlockPoolHeaderSize)
+ je @EntireMediumPoolFree
+@BinFreeMediumBlock:
+ {Store the size of the block as well as the flags}
+ lea rax, [rbx + IsMediumBlockFlag + IsFreeBlockFlag]
+ mov [rsi - BlockHeaderSize], rax
+ {Store the trailing size marker}
+ mov [rsi + rbx - 2 * BlockHeaderSize], rbx
+ {Insert this block back into the bins: Size check not required here,
+ since medium blocks that are in use are not allowed to be
+ shrunk smaller than MinimumMediumBlockSize}
+ mov rcx, rsi
+ mov rdx, rbx
+ {Insert into bin}
+ call InsertMediumBlockIntoBin
+ {All OK}
+ xor eax, eax
+ {Unlock medium blocks}
+ mov MediumBlocksLocked, al
+ jmp @Done
+@NextBlockIsFree:
+ {Get the next block address in rax}
+ lea rax, [rsi + rbx]
+ {Increase the size of this block}
+ and rcx, DropMediumAndLargeFlagsMask
+ add rbx, rcx
+ {Was the block binned?}
+ cmp rcx, MinimumMediumBlockSize
+ jb @NextBlockChecked
+ mov rcx, rax
+ call RemoveMediumFreeBlock
+ jmp @NextBlockChecked
+@PreviousBlockIsFree:
+ {Get the size of the free block just before this one}
+ mov rcx, [rsi - 2 * BlockHeaderSize]
+ {Include the previous block}
+ sub rsi, rcx
+ {Set the new block size}
+ add rbx, rcx
+ {Remove the previous block from the linked list}
+ cmp ecx, MinimumMediumBlockSize
+ jb @PreviousBlockChecked
+ mov rcx, rsi
+ call RemoveMediumFreeBlock
+ jmp @PreviousBlockChecked
+@EntireMediumPoolFree:
+ {Should we make this the new sequential feed medium block pool? If the
+ current sequential feed pool is not entirely free, we make this the new
+ sequential feed pool.}
+ lea r8, MediumSequentialFeedBytesLeft
+ cmp dword ptr [r8], MediumBlockPoolSize - MediumBlockPoolHeaderSize //workaround for QC99023
+ jne @MakeEmptyMediumPoolSequentialFeed
+ {Point esi to the medium block pool header}
+ sub rsi, MediumBlockPoolHeaderSize
+ {Remove this medium block pool from the linked list}
+ mov rax, TMediumBlockPoolHeader[rsi].PreviousMediumBlockPoolHeader
+ mov rdx, TMediumBlockPoolHeader[rsi].NextMediumBlockPoolHeader
+ mov TMediumBlockPoolHeader[rax].NextMediumBlockPoolHeader, rdx
+ mov TMediumBlockPoolHeader[rdx].PreviousMediumBlockPoolHeader, rax
+ {Unlock medium blocks}
+ xor eax, eax
+ mov MediumBlocksLocked, al
+{$ifdef ClearMediumBlockPoolsBeforeReturningToOS}
+ mov rcx, rsi
+ mov edx, MediumBlockPoolSize
+ xor r8, r8
+ call System.@FillChar
+{$endif}
+ {Free the medium block pool}
+ mov rcx, rsi
+ xor edx, edx
+ mov r8d, MEM_RELEASE
+ call VirtualFree
+ {VirtualFree returns >0 if all is ok}
+ cmp eax, 1
+ {Return 0 on all ok}
+ sbb eax, eax
+ jmp @Done
+@MakeEmptyMediumPoolSequentialFeed:
+ {Get a pointer to the end-marker block}
+ lea rbx, [rsi + MediumBlockPoolSize - MediumBlockPoolHeaderSize]
+ {Bin the current sequential feed pool}
+ call BinMediumSequentialFeedRemainder
+ {Set this medium pool up as the new sequential feed pool:
+ Store the sequential feed pool trailer}
+ mov qword ptr [rbx - BlockHeaderSize], IsMediumBlockFlag
+ {Store the number of bytes available in the sequential feed chunk}
+ lea rax, MediumSequentialFeedBytesLeft
+ mov dword ptr [rax], MediumBlockPoolSize - MediumBlockPoolHeaderSize //QC99023 workaround
+ {Set the last sequentially fed block}
+ mov LastSequentiallyFedMediumBlock, rbx
+ {Success}
+ xor eax, eax
+ {Unlock medium blocks}
+ mov MediumBlocksLocked, al
+ jmp @Done
+@NotASmallOrMediumBlock:
+ {Attempt to free an already free block?}
+ mov eax, -1
+ {Is it in fact a large block?}
+ test dl, IsFreeBlockFlag + IsMediumBlockFlag
+ jnz @Done
+ call FreeLargeBlock
+@Done:
+end;
+{$endif}
+{$endif}
+
+{$ifndef FullDebugMode}
+{Replacement for SysReallocMem}
+function FastReallocMem(APointer: Pointer; ANewSize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
+{$ifndef ASMVersion}
+var
+ LBlockHeader, LNextBlockSizeAndFlags, LNewAllocSize, LBlockFlags,
+ LOldAvailableSize, LNextBlockSize, LNewAvailableSize, LMinimumUpsize,
+ LSecondSplitSize, LNewBlockSize: NativeUInt;
+ LPSmallBlockType: PSmallBlockType;
+ LPNextBlock, LPNextBlockHeader: Pointer;
+
+ {Upsizes a large block in-place. The following variables are assumed correct:
+ LBlockFlags, LOldAvailableSize, LPNextBlock, LNextBlockSizeAndFlags,
+ LNextBlockSize, LNewAvailableSize. Medium blocks must be locked on entry if
+ required.}
+ procedure MediumBlockInPlaceUpsize;
+ begin
+ {Remove the next block}
+ if LNextBlockSizeAndFlags >= MinimumMediumBlockSize then
+ RemoveMediumFreeBlock(LPNextBlock);
+ {Add 25% for medium block in-place upsizes}
+ LMinimumUpsize := LOldAvailableSize + (LOldAvailableSize shr 2);
+ if NativeUInt(ANewSize) < LMinimumUpsize then
+ LNewAllocSize := LMinimumUpsize
+ else
+ LNewAllocSize := NativeUInt(ANewSize);
+ {Round up to the nearest block size granularity}
+ LNewBlockSize := ((LNewAllocSize + (BlockHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset))
+ and -MediumBlockGranularity) + MediumBlockSizeOffset;
+ {Calculate the size of the second split}
+ LSecondSplitSize := LNewAvailableSize + BlockHeaderSize - LNewBlockSize;
+ {Does it fit?}
+ if NativeInt(LSecondSplitSize) <= 0 then
+ begin
+ {The block size is the full available size plus header}
+ LNewBlockSize := LNewAvailableSize + BlockHeaderSize;
+ {Grab the whole block: Mark it as used in the block following it}
+ LPNextBlockHeader := Pointer(PByte(APointer) + LNewAvailableSize);
+ PNativeUInt(LPNextBlockHeader)^ :=
+ PNativeUInt(LPNextBlockHeader)^ and (not PreviousMediumBlockIsFreeFlag);
+ end
+ else
+ begin
+ {Split the block in two}
+ LPNextBlock := PMediumFreeBlock(PByte(APointer) + LNewBlockSize);
+ {Set the size of the second split}
+ PNativeUInt(PByte(LPNextBlock) - BlockHeaderSize)^ := LSecondSplitSize or (IsMediumBlockFlag or IsFreeBlockFlag);
+ {Store the size of the second split before the header of the next block}
+ PNativeUInt(PByte(LPNextBlock) + LSecondSplitSize - 2 * BlockHeaderSize)^ := LSecondSplitSize;
+ {Put the remainder in a bin if it is big enough}
+ if LSecondSplitSize >= MinimumMediumBlockSize then
+ InsertMediumBlockIntoBin(LPNextBlock, LSecondSplitSize);
+ end;
+ {Set the size and flags for this block}
+ PNativeUInt(PByte(APointer) - BlockHeaderSize)^ := LNewBlockSize or LBlockFlags;
+ end;
+
+ {In-place downsize of a medium block. On entry Size must be less than half of
+ LOldAvailableSize.}
+ procedure MediumBlockInPlaceDownsize;
+ begin
+ {Round up to the next medium block size}
+ LNewBlockSize := ((ANewSize + (BlockHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset))
+ and -MediumBlockGranularity) + MediumBlockSizeOffset;
+ {Get the size of the second split}
+ LSecondSplitSize := (LOldAvailableSize + BlockHeaderSize) - LNewBlockSize;
+ {Lock the medium blocks}
+ LockMediumBlocks;
+ {Set the new size}
+ PNativeUInt(PByte(APointer) - BlockHeaderSize)^ :=
+ (PNativeUInt(PByte(APointer) - BlockHeaderSize)^ and ExtractMediumAndLargeFlagsMask)
+ or LNewBlockSize;
+ {Is the next block in use?}
+ LPNextBlock := PNativeUInt(PByte(APointer) + LOldAvailableSize + BlockHeaderSize);
+ LNextBlockSizeAndFlags := PNativeUInt(PByte(LPNextBlock) - BlockHeaderSize)^;
+ if LNextBlockSizeAndFlags and IsFreeBlockFlag = 0 then
+ begin
+ {The next block is in use: flag its previous block as free}
+ PNativeUInt(PByte(LPNextBlock) - BlockHeaderSize)^ :=
+ LNextBlockSizeAndFlags or PreviousMediumBlockIsFreeFlag;
+ end
+ else
+ begin
+ {The next block is free: combine it}
+ LNextBlockSizeAndFlags := LNextBlockSizeAndFlags and DropMediumAndLargeFlagsMask;
+ Inc(LSecondSplitSize, LNextBlockSizeAndFlags);
+ if LNextBlockSizeAndFlags >= MinimumMediumBlockSize then
+ RemoveMediumFreeBlock(LPNextBlock);
+ end;
+ {Set the split}
+ LPNextBlock := PNativeUInt(PByte(APointer) + LNewBlockSize);
+ {Store the free part's header}
+ PNativeUInt(PByte(LPNextBlock) - BlockHeaderSize)^ := LSecondSplitSize or (IsMediumBlockFlag or IsFreeBlockFlag);
+ {Store the trailing size field}
+ PNativeUInt(PByte(LPNextBlock) + LSecondSplitSize - 2 * BlockHeaderSize)^ := LSecondSplitSize;
+ {Bin this free block}
+ if LSecondSplitSize >= MinimumMediumBlockSize then
+ InsertMediumBlockIntoBin(LPNextBlock, LSecondSplitSize);
+ {Unlock the medium blocks}
+ MediumBlocksLocked := False;
+ end;
+
+begin
+ {Get the block header: Is it actually a small block?}
+ LBlockHeader := PNativeUInt(PByte(APointer) - BlockHeaderSize)^;
+ {Is it a small block that is in use?}
+ if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag or IsLargeBlockFlag) = 0 then
+ begin
+ {-----------------------------------Small block-------------------------------------}
+ {The block header is a pointer to the block pool: Get the block type}
+ LPSmallBlockType := PSmallBlockPoolHeader(LBlockHeader).BlockType;
+ {Get the available size inside blocks of this type.}
+ LOldAvailableSize := LPSmallBlockType.BlockSize - BlockHeaderSize;
+ {Is it an upsize or a downsize?}
+ if LOldAvailableSize >= NativeUInt(ANewSize) then
+ begin
+ {It's a downsize. Do we need to allocate a smaller block? Only if the new
+ block size is less than a quarter of the available size less
+ SmallBlockDownsizeCheckAdder bytes}
+ if (NativeUInt(ANewSize) * 4 + SmallBlockDownsizeCheckAdder) >= LOldAvailableSize then
+ begin
+ {In-place downsize - return the pointer}
+ Result := APointer;
+ Exit;
+ end
+ else
+ begin
+ {Allocate a smaller block}
+ Result := FastGetMem(ANewSize);
+ {Allocated OK?}
+ if Result <> nil then
+ begin
+ {Move the data across}
+{$ifdef UseCustomVariableSizeMoveRoutines}
+ {$ifdef Align16Bytes}
+ MoveX16LP(APointer^, Result^, ANewSize);
+ {$else}
+ MoveX8LP(APointer^, Result^, ANewSize);
+ {$endif}
+{$else}
+ System.Move(APointer^, Result^, ANewSize);
+{$endif}
+ {Free the old pointer}
+ FastFreeMem(APointer);
+ end;
+ end;
+ end
+ else
+ begin
+ {This pointer is being reallocated to a larger block and therefore it is
+ logical to assume that it may be enlarged again. Since reallocations are
+ expensive, there is a minimum upsize percentage to avoid unnecessary
+ future move operations.}
+ {Must grow with at least 100% + x bytes}
+ LNewAllocSize := LOldAvailableSize * 2 + SmallBlockUpsizeAdder;
+ {Still not large enough?}
+ if LNewAllocSize < NativeUInt(ANewSize) then
+ LNewAllocSize := NativeUInt(ANewSize);
+ {Allocate the new block}
+ Result := FastGetMem(LNewAllocSize);
+ {Allocated OK?}
+ if Result <> nil then
+ begin
+ {Do we need to store the requested size? Only large blocks store the
+ requested size.}
+ if LNewAllocSize > (MaximumMediumBlockSize - BlockHeaderSize) then
+ PLargeBlockHeader(PByte(Result) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
+ {Move the data across}
+{$ifdef UseCustomFixedSizeMoveRoutines}
+ LPSmallBlockType.UpsizeMoveProcedure(APointer^, Result^, LOldAvailableSize);
+{$else}
+ System.Move(APointer^, Result^, LOldAvailableSize);
+{$endif}
+ {Free the old pointer}
+ FastFreeMem(APointer);
+ end;
+ end;
+ end
+ else
+ begin
+ {Is this a medium block or a large block?}
+ if LBlockHeader and (IsFreeBlockFlag or IsLargeBlockFlag) = 0 then
+ begin
+ {-------------------------------Medium block--------------------------------------}
+ {What is the available size in the block being reallocated?}
+ LOldAvailableSize := (LBlockHeader and DropMediumAndLargeFlagsMask);
+ {Get a pointer to the next block}
+ LPNextBlock := PNativeUInt(PByte(APointer) + LOldAvailableSize);
+ {Subtract the block header size from the old available size}
+ Dec(LOldAvailableSize, BlockHeaderSize);
+ {Is it an upsize or a downsize?}
+ if NativeUInt(ANewSize) > LOldAvailableSize then
+ begin
+ {Can we do an in-place upsize?}
+ LNextBlockSizeAndFlags := PNativeUInt(PByte(LPNextBlock) - BlockHeaderSize)^;
+ {Is the next block free?}
+ if LNextBlockSizeAndFlags and IsFreeBlockFlag <> 0 then
+ begin
+ LNextBlockSize := LNextBlockSizeAndFlags and DropMediumAndLargeFlagsMask;
+ {The available size including the next block}
+ LNewAvailableSize := LOldAvailableSize + LNextBlockSize;
+ {Can the block fit?}
+ if NativeUInt(ANewSize) <= LNewAvailableSize then
+ begin
+ {The next block is free and there is enough space to grow this
+ block in place.}
+{$ifndef AssumeMultiThreaded}
+ if IsMultiThread then
+ begin
+{$endif}
+ {Multi-threaded application - lock medium blocks and re-read the
+ information on the blocks.}
+ LockMediumBlocks;
+ {Re-read the info for this block}
+ LBlockFlags := PNativeUInt(PByte(APointer) - BlockHeaderSize)^ and ExtractMediumAndLargeFlagsMask;
+ {Re-read the info for the next block}
+ LNextBlockSizeAndFlags := PNativeUInt(PByte(LPNextBlock) - BlockHeaderSize)^;
+ {Recalculate the next block size}
+ LNextBlockSize := LNextBlockSizeAndFlags and DropMediumAndLargeFlagsMask;
+ {The available size including the next block}
+ LNewAvailableSize := LOldAvailableSize + LNextBlockSize;
+ {Is the next block still free and the size still sufficient?}
+ if (LNextBlockSizeAndFlags and IsFreeBlockFlag <> 0)
+ and (NativeUInt(ANewSize) <= LNewAvailableSize) then
+ begin
+ {Upsize the block in-place}
+ MediumBlockInPlaceUpsize;
+ {Unlock the medium blocks}
+ MediumBlocksLocked := False;
+ {Return the result}
+ Result := APointer;
+ {Done}
+ Exit;
+ end;
+ {Couldn't use the block: Unlock the medium blocks}
+ MediumBlocksLocked := False;
+{$ifndef AssumeMultiThreaded}
+ end
+ else
+ begin
+ {Extract the block flags}
+ LBlockFlags := ExtractMediumAndLargeFlagsMask and LBlockHeader;
+ {Upsize the block in-place}
+ MediumBlockInPlaceUpsize;
+ {Return the result}
+ Result := APointer;
+ {Done}
+ Exit;
+ end;
+{$endif}
+ end;
+ end;
+ {Couldn't upsize in place. Grab a new block and move the data across:
+ If we have to reallocate and move medium blocks, we grow by at
+ least 25%}
+ LMinimumUpsize := LOldAvailableSize + (LOldAvailableSize shr 2);
+ if NativeUInt(ANewSize) < LMinimumUpsize then
+ LNewAllocSize := LMinimumUpsize
+ else
+ LNewAllocSize := NativeUInt(ANewSize);
+ {Allocate the new block}
+ Result := FastGetMem(LNewAllocSize);
+ if Result <> nil then
+ begin
+ {If it's a large block - store the actual user requested size}
+ if LNewAllocSize > (MaximumMediumBlockSize - BlockHeaderSize) then
+ PLargeBlockHeader(PByte(Result) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
+ {Move the data across}
+{$ifdef UseCustomVariableSizeMoveRoutines}
+ MoveX16LP(APointer^, Result^, LOldAvailableSize);
+{$else}
+ System.Move(APointer^, Result^, LOldAvailableSize);
+{$endif}
+ {Free the old block}
+ FastFreeMem(APointer);
+ end;
+ end
+ else
+ begin
+ {Must be less than half the current size or we don't bother resizing.}
+ if NativeUInt(ANewSize * 2) >= LOldAvailableSize then
+ begin
+ Result := APointer;
+ end
+ else
+ begin
+ {In-place downsize? Balance the cost of moving the data vs. the cost
+ of fragmenting the memory pool. Medium blocks in use may never be
+ smaller than MinimumMediumBlockSize.}
+ if NativeUInt(ANewSize) >= (MinimumMediumBlockSize - BlockHeaderSize) then
+ begin
+ MediumBlockInPlaceDownsize;
+ Result := APointer;
+ end
+ else
+ begin
+ {The requested size is less than the minimum medium block size. If
+ the requested size is less than the threshold value (currently a
+ quarter of the minimum medium block size), move the data to a small
+ block, otherwise shrink the medium block to the minimum allowable
+ medium block size.}
+ if NativeUInt(ANewSize) >= MediumInPlaceDownsizeLimit then
+ begin
+ {The request is for a size smaller than the minimum medium block
+ size, but not small enough to justify moving data: Reduce the
+ block size to the minimum medium block size}
+ ANewSize := MinimumMediumBlockSize - BlockHeaderSize;
+ {Is it already at the minimum medium block size?}
+ if LOldAvailableSize > NativeUInt(ANewSize) then
+ MediumBlockInPlaceDownsize;
+ Result := APointer;
+ end
+ else
+ begin
+ {Allocate the new block}
+ Result := FastGetMem(ANewSize);
+ if Result <> nil then
+ begin
+ {Move the data across}
+{$ifdef UseCustomVariableSizeMoveRoutines}
+ {$ifdef Align16Bytes}
+ MoveX16LP(APointer^, Result^, ANewSize);
+ {$else}
+ MoveX8LP(APointer^, Result^, ANewSize);
+ {$endif}
+{$else}
+ System.Move(APointer^, Result^, ANewSize);
+{$endif}
+ {Free the old block}
+ FastFreeMem(APointer);
+ end;
+ end;
+ end;
+ end;
+ end;
+ end
+ else
+ begin
+ {Is this a valid large block?}
+ if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag) = 0 then
+ begin
+ {-----------------------Large block------------------------------}
+ Result := ReallocateLargeBlock(APointer, ANewSize);
+ end
+ else
+ begin
+ {-----------------------Invalid block------------------------------}
+ {Bad pointer: probably an attempt to reallocate a free memory block.}
+ Result := nil;
+ end;
+ end;
+ end;
+end;
+{$else}
+{$ifdef 32Bit}
+asm
+ {On entry: eax = APointer; edx = ANewSize}
+ {Get the block header: Is it actually a small block?}
+ mov ecx, [eax - 4]
+ {Is it a small block?}
+ test cl, IsFreeBlockFlag + IsMediumBlockFlag + IsLargeBlockFlag
+ {Save ebx}
+ push ebx
+ {Save esi}
+ push esi
+ {Save the original pointer in esi}
+ mov esi, eax
+ {Is it a small block?}
+ jnz @NotASmallBlock
+ {-----------------------------------Small block-------------------------------------}
+ {Get the block type in ebx}
+ mov ebx, TSmallBlockPoolHeader[ecx].BlockType
+ {Get the available size inside blocks of this type.}
+ movzx ecx, TSmallBlockType[ebx].BlockSize
+ sub ecx, 4
+ {Is it an upsize or a downsize?}
+ cmp ecx, edx
+ jb @SmallUpsize
+ {It's a downsize. Do we need to allocate a smaller block? Only if the new
+ size is less than a quarter of the available size less
+ SmallBlockDownsizeCheckAdder bytes}
+ lea ebx, [edx * 4 + SmallBlockDownsizeCheckAdder]
+ cmp ebx, ecx
+ jb @NotSmallInPlaceDownsize
+ {In-place downsize - return the original pointer}
+ pop esi
+ pop ebx
+ ret
+ {Align branch target}
+ nop
+@NotSmallInPlaceDownsize:
+ {Save the requested size}
+ mov ebx, edx
+ {Allocate a smaller block}
+ mov eax, edx
+ call FastGetMem
+ {Allocated OK?}
+ test eax, eax
+ jz @SmallDownsizeDone
+ {Move data across: count in ecx}
+ mov ecx, ebx
+ {Destination in edx}
+ mov edx, eax
+ {Save the result in ebx}
+ mov ebx, eax
+ {Original pointer in eax}
+ mov eax, esi
+ {Move the data across}
+{$ifdef UseCustomVariableSizeMoveRoutines}
+ {$ifdef Align16Bytes}
+ call MoveX16LP
+ {$else}
+ call MoveX8LP
+ {$endif}
+{$else}
+ call System.Move
+{$endif}
+ {Free the original pointer}
+ mov eax, esi
+ call FastFreeMem
+ {Return the pointer}
+ mov eax, ebx
+@SmallDownsizeDone:
+ pop esi
+ pop ebx
+ ret
+ {Align branch target}
+ nop
+ nop
+@SmallUpsize:
+ {State: esi = APointer, edx = ANewSize, ecx = Current Block Size, ebx = Current Block Type}
+ {This pointer is being reallocated to a larger block and therefore it is
+ logical to assume that it may be enlarged again. Since reallocations are
+ expensive, there is a minimum upsize percentage to avoid unnecessary
+ future move operations.}
+ {Small blocks always grow with at least 100% + SmallBlockUpsizeAdder bytes}
+ lea ecx, [ecx + ecx + SmallBlockUpsizeAdder]
+ {save edi}
+ push edi
+ {Save the requested size in edi}
+ mov edi, edx
+ {New allocated size is the maximum of the requested size and the minimum
+ upsize}
+ xor eax, eax
+ sub ecx, edx
+ adc eax, -1
+ and eax, ecx
+ add eax, edx
+ {Allocate the new block}
+ call FastGetMem
+ {Allocated OK?}
+ test eax, eax
+ jz @SmallUpsizeDone
+ {Do we need to store the requested size? Only large blocks store the
+ requested size.}
+ cmp edi, MaximumMediumBlockSize - BlockHeaderSize
+ jbe @NotSmallUpsizeToLargeBlock
+ {Store the user requested size}
+ mov [eax - 8], edi
+@NotSmallUpsizeToLargeBlock:
+ {Get the size to move across}
+ movzx ecx, TSmallBlockType[ebx].BlockSize
+ sub ecx, BlockHeaderSize
+ {Move to the new block}
+ mov edx, eax
+ {Save the result in edi}
+ mov edi, eax
+ {Move from the old block}
+ mov eax, esi
+ {Move the data across}
+{$ifdef UseCustomFixedSizeMoveRoutines}
+ call TSmallBlockType[ebx].UpsizeMoveProcedure
+{$else}
+ call System.Move
+{$endif}
+ {Free the old pointer}
+ mov eax, esi
+ call FastFreeMem
+ {Done}
+ mov eax, edi
+@SmallUpsizeDone:
+ pop edi
+ pop esi
+ pop ebx
+ ret
+ {Align branch target}
+ nop
+@NotASmallBlock:
+ {Is this a medium block or a large block?}
+ test cl, IsFreeBlockFlag + IsLargeBlockFlag
+ jnz @PossibleLargeBlock
+ {-------------------------------Medium block--------------------------------------}
+ {Status: ecx = Current Block Size + Flags, eax/esi = APointer,
+ edx = Requested Size}
+ mov ebx, ecx
+ {Drop the flags from the header}
+ and ecx, DropMediumAndLargeFlagsMask
+ {Save edi}
+ push edi
+ {Get a pointer to the next block in edi}
+ lea edi, [eax + ecx]
+ {Subtract the block header size from the old available size}
+ sub ecx, BlockHeaderSize
+ {Get the complete flags in ebx}
+ and ebx, ExtractMediumAndLargeFlagsMask
+ {Is it an upsize or a downsize?}
+ cmp edx, ecx
+ {Save ebp}
+ push ebp
+ {Is it an upsize or a downsize?}
+ ja @MediumBlockUpsize
+ {Status: ecx = Current Block Size - 4, bl = Current Block Flags,
+ edi = @Next Block, eax/esi = APointer, edx = Requested Size}
+ {Must be less than half the current size or we don't bother resizing.}
+ lea ebp, [edx + edx]
+ cmp ebp, ecx
+ jb @MediumMustDownsize
+@MediumNoResize:
+ {Restore registers}
+ pop ebp
+ pop edi
+ pop esi
+ pop ebx
+ {Return}
+ ret
+ {Align branch target}
+ nop
+ nop
+ nop
+@MediumMustDownsize:
+ {In-place downsize? Balance the cost of moving the data vs. the cost of
+ fragmenting the memory pool. Medium blocks in use may never be smaller
+ than MinimumMediumBlockSize.}
+ cmp edx, MinimumMediumBlockSize - BlockHeaderSize
+ jae @MediumBlockInPlaceDownsize
+ {The requested size is less than the minimum medium block size. If the
+ requested size is less than the threshold value (currently a quarter of the
+ minimum medium block size), move the data to a small block, otherwise shrink
+ the medium block to the minimum allowable medium block size.}
+ cmp edx, MediumInPlaceDownsizeLimit
+ jb @MediumDownsizeRealloc
+ {The request is for a size smaller than the minimum medium block size, but
+ not small enough to justify moving data: Reduce the block size to the
+ minimum medium block size}
+ mov edx, MinimumMediumBlockSize - BlockHeaderSize
+ {Is it already at the minimum medium block size?}
+ cmp ecx, edx
+ jna @MediumNoResize
+@MediumBlockInPlaceDownsize:
+ {Round up to the next medium block size}
+ lea ebp, [edx + BlockHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset]
+ and ebp, -MediumBlockGranularity;
+ add ebp, MediumBlockSizeOffset
+ {Get the size of the second split}
+ add ecx, BlockHeaderSize
+ sub ecx, ebp
+ {Lock the medium blocks}
+{$ifndef AssumeMultiThreaded}
+ cmp IsMultiThread, False
+ je @DoMediumInPlaceDownsize
+{$endif}
+@DoMediumLockForDownsize:
+ {Lock the medium blocks (ecx *must* be preserved)}
+ call LockMediumBlocks
+ {Reread the flags - they may have changed before medium blocks could be
+ locked.}
+ mov ebx, ExtractMediumAndLargeFlagsMask
+ and ebx, [esi - 4]
+@DoMediumInPlaceDownsize:
+ {Set the new size}
+ or ebx, ebp
+ mov [esi - 4], ebx
+ {Get the second split size in ebx}
+ mov ebx, ecx
+ {Is the next block in use?}
+ mov edx, [edi - 4]
+ test dl, IsFreeBlockFlag
+ jnz @MediumDownsizeNextBlockFree
+ {The next block is in use: flag its previous block as free}
+ or edx, PreviousMediumBlockIsFreeFlag
+ mov [edi - 4], edx
+ jmp @MediumDownsizeDoSplit
+ {Align branch target}
+ nop
+ nop
+{$ifdef AssumeMultiThreaded}
+ nop
+{$endif}
+@MediumDownsizeNextBlockFree:
+ {The next block is free: combine it}
+ mov eax, edi
+ and edx, DropMediumAndLargeFlagsMask
+ add ebx, edx
+ add edi, edx
+ cmp edx, MinimumMediumBlockSize
+ jb @MediumDownsizeDoSplit
+ call RemoveMediumFreeBlock
+@MediumDownsizeDoSplit:
+ {Store the trailing size field}
+ mov [edi - 8], ebx
+ {Store the free part's header}
+ lea eax, [ebx + IsMediumBlockFlag + IsFreeBlockFlag];
+ mov [esi + ebp - 4], eax
+ {Bin this free block}
+ cmp ebx, MinimumMediumBlockSize
+ jb @MediumBlockDownsizeDone
+ lea eax, [esi + ebp]
+ mov edx, ebx
+ call InsertMediumBlockIntoBin
+@MediumBlockDownsizeDone:
+ {Unlock the medium blocks}
+ mov MediumBlocksLocked, False
+ {Result = old pointer}
+ mov eax, esi
+ {Restore registers}
+ pop ebp
+ pop edi
+ pop esi
+ pop ebx
+ {Return}
+ ret
+ {Align branch target}
+@MediumDownsizeRealloc:
+ {Save the requested size}
+ mov edi, edx
+ mov eax, edx
+ {Allocate the new block}
+ call FastGetMem
+ test eax, eax
+ jz @MediumBlockDownsizeExit
+ {Save the result}
+ mov ebp, eax
+ mov edx, eax
+ mov eax, esi
+ mov ecx, edi
+ {Move the data across}
+{$ifdef UseCustomVariableSizeMoveRoutines}
+ {$ifdef Align16Bytes}
+ call MoveX16LP
+ {$else}
+ call MoveX8LP
+ {$endif}
+{$else}
+ call System.Move
+{$endif}
+ mov eax, esi
+ call FastFreeMem
+ {Return the result}
+ mov eax, ebp
+@MediumBlockDownsizeExit:
+ pop ebp
+ pop edi
+ pop esi
+ pop ebx
+ ret
+ {Align branch target}
+@MediumBlockUpsize:
+ {Status: ecx = Current Block Size - 4, bl = Current Block Flags,
+ edi = @Next Block, eax/esi = APointer, edx = Requested Size}
+ {Can we do an in-place upsize?}
+ mov eax, [edi - 4]
+ test al, IsFreeBlockFlag
+ jz @CannotUpsizeMediumBlockInPlace
+ {Get the total available size including the next block}
+ and eax, DropMediumAndLargeFlagsMask
+ {ebp = total available size including the next block (excluding the header)}
+ lea ebp, [eax + ecx]
+ {Can the block fit?}
+ cmp edx, ebp
+ ja @CannotUpsizeMediumBlockInPlace
+ {The next block is free and there is enough space to grow this
+ block in place.}
+{$ifndef AssumeMultiThreaded}
+ cmp IsMultiThread, False
+ je @DoMediumInPlaceUpsize
+{$endif}
+@DoMediumLockForUpsize:
+ {Lock the medium blocks (ecx and edx *must* be preserved}
+ call LockMediumBlocks
+ {Re-read the info for this block (since it may have changed before the medium
+ blocks could be locked)}
+ mov ebx, ExtractMediumAndLargeFlagsMask
+ and ebx, [esi - 4]
+ {Re-read the info for the next block}
+ mov eax, [edi - 4]
+ {Next block still free?}
+ test al, IsFreeBlockFlag
+ jz @NextMediumBlockChanged
+ {Recalculate the next block size}
+ and eax, DropMediumAndLargeFlagsMask
+ {The available size including the next block}
+ lea ebp, [eax + ecx]
+ {Can the block still fit?}
+ cmp edx, ebp
+ ja @NextMediumBlockChanged
+@DoMediumInPlaceUpsize:
+ {Is the next block binnable?}
+ cmp eax, MinimumMediumBlockSize
+ {Remove the next block}
+ jb @MediumInPlaceNoNextRemove
+ mov eax, edi
+ push ecx
+ push edx
+ call RemoveMediumFreeBlock
+ pop edx
+ pop ecx
+@MediumInPlaceNoNextRemove:
+ {Medium blocks grow a minimum of 25% in in-place upsizes}
+ mov eax, ecx
+ shr eax, 2
+ add eax, ecx
+ {Get the maximum of the requested size and the minimum growth size}
+ xor edi, edi
+ sub eax, edx
+ adc edi, -1
+ and eax, edi
+ {Round up to the nearest block size granularity}
+ lea eax, [eax + edx + BlockHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset]
+ and eax, -MediumBlockGranularity
+ add eax, MediumBlockSizeOffset
+ {Calculate the size of the second split}
+ lea edx, [ebp + BlockHeaderSize]
+ sub edx, eax
+ {Does it fit?}
+ ja @MediumInPlaceUpsizeSplit
+ {Grab the whole block: Mark it as used in the block following it}
+ and dword ptr [esi + ebp], not PreviousMediumBlockIsFreeFlag
+ {The block size is the full available size plus header}
+ add ebp, 4
+ {Upsize done}
+ jmp @MediumUpsizeInPlaceDone
+ {Align branch target}
+{$ifndef AssumeMultiThreaded}
+ nop
+ nop
+ nop
+{$endif}
+@MediumInPlaceUpsizeSplit:
+ {Store the size of the second split as the second last dword}
+ mov [esi + ebp - 4], edx
+ {Set the second split header}
+ lea edi, [edx + IsMediumBlockFlag + IsFreeBlockFlag]
+ mov [esi + eax - 4], edi
+ mov ebp, eax
+ cmp edx, MinimumMediumBlockSize
+ jb @MediumUpsizeInPlaceDone
+ add eax, esi
+ call InsertMediumBlockIntoBin
+@MediumUpsizeInPlaceDone:
+ {Set the size and flags for this block}
+ or ebp, ebx
+ mov [esi - 4], ebp
+ {Unlock the medium blocks}
+ mov MediumBlocksLocked, False
+ {Result = old pointer}
+ mov eax, esi
+@MediumBlockResizeDone2:
+ {Restore registers}
+ pop ebp
+ pop edi
+ pop esi
+ pop ebx
+ {Return}
+ ret
+ {Align branch target for "@CannotUpsizeMediumBlockInPlace"}
+ nop
+ nop
+@NextMediumBlockChanged:
+ {The next medium block changed while the medium blocks were being locked}
+ mov MediumBlocksLocked, False
+@CannotUpsizeMediumBlockInPlace:
+ {Couldn't upsize in place. Grab a new block and move the data across:
+ If we have to reallocate and move medium blocks, we grow by at
+ least 25%}
+ mov eax, ecx
+ shr eax, 2
+ add eax, ecx
+ {Get the maximum of the requested size and the minimum growth size}
+ xor edi, edi
+ sub eax, edx
+ adc edi, -1
+ and eax, edi
+ add eax, edx
+ {Save the size to allocate}
+ mov ebp, eax
+ {Save the size to move across}
+ mov edi, ecx
+ {Get the block}
+ push edx
+ call FastGetMem
+ pop edx
+ {Success?}
+ test eax, eax
+ jz @MediumBlockResizeDone2
+ {If it's a Large block - store the actual user requested size}
+ cmp ebp, MaximumMediumBlockSize - BlockHeaderSize
+ jbe @MediumUpsizeNotLarge
+ mov [eax - 8], edx
+@MediumUpsizeNotLarge:
+ {Save the result}
+ mov ebp, eax
+ {Move the data across}
+ mov edx, eax
+ mov eax, esi
+ mov ecx, edi
+{$ifdef UseCustomVariableSizeMoveRoutines}
+ call MoveX16LP
+{$else}
+ call System.Move
+{$endif}
+ {Free the old block}
+ mov eax, esi
+ call FastFreeMem
+ {Restore the result}
+ mov eax, ebp
+ {Restore registers}
+ pop ebp
+ pop edi
+ pop esi
+ pop ebx
+ {Return}
+ ret
+ {Align branch target}
+ nop
+@PossibleLargeBlock:
+ {-----------------------Large block------------------------------}
+ {Restore registers}
+ pop esi
+ pop ebx
+ {Is this a valid large block?}
+ test cl, IsFreeBlockFlag + IsMediumBlockFlag
+ jz ReallocateLargeBlock
+ {-----------------------Invalid block------------------------------}
+ xor eax, eax
+end;
+
+{$else}
+
+{-----------------64-bit BASM FastReallocMem-----------------}
+asm
+ .params 3
+ .pushnv rbx
+ .pushnv rsi
+ .pushnv rdi
+ .pushnv r14
+ .pushnv r15
+ {On entry: rcx = APointer; rdx = ANewSize}
+ {Save the original pointer in rsi}
+ mov rsi, rcx
+ {Get the block header}
+ mov rcx, [rcx - BlockHeaderSize]
+ {Is it a small block?}
+ test cl, IsFreeBlockFlag + IsMediumBlockFlag + IsLargeBlockFlag
+ jnz @NotASmallBlock
+ {-----------------------------------Small block-------------------------------------}
+ {Get the block type in rbx}
+ mov rbx, TSmallBlockPoolHeader[rcx].BlockType
+ {Get the available size inside blocks of this type.}
+ movzx ecx, TSmallBlockType[rbx].BlockSize
+ sub ecx, BlockHeaderSize
+ {Is it an upsize or a downsize?}
+ cmp rcx, rdx
+ jb @SmallUpsize
+ {It's a downsize. Do we need to allocate a smaller block? Only if the new
+ size is less than a quarter of the available size less
+ SmallBlockDownsizeCheckAdder bytes}
+ lea ebx, [edx * 4 + SmallBlockDownsizeCheckAdder]
+ cmp ebx, ecx
+ jb @NotSmallInPlaceDownsize
+ {In-place downsize - return the original pointer}
+ mov rax, rsi
+ jmp @Done
+@NotSmallInPlaceDownsize:
+ {Save the requested size}
+ mov rbx, rdx
+ {Allocate a smaller block}
+ mov rcx, rdx
+ call FastGetMem
+ {Allocated OK?}
+ test rax, rax
+ jz @Done
+ {Move data across: count in r8}
+ mov r8, rbx
+ {Destination in edx}
+ mov rdx, rax
+ {Save the result in ebx}
+ mov rbx, rax
+ {Original pointer in ecx}
+ mov rcx, rsi
+ {Move the data across}
+{$ifdef UseCustomVariableSizeMoveRoutines}
+ {$ifdef Align16Bytes}
+ call MoveX16LP
+ {$else}
+ call MoveX8LP
+ {$endif}
+{$else}
+ call System.Move
+{$endif}
+ {Free the original pointer}
+ mov rcx, rsi
+ call FastFreeMem
+ {Return the pointer}
+ mov rax, rbx
+ jmp @Done
+@SmallUpsize:
+ {State: rsi = APointer, rdx = ANewSize, rcx = Current Block Size, rbx = Current Block Type}
+ {This pointer is being reallocated to a larger block and therefore it is
+ logical to assume that it may be enlarged again. Since reallocations are
+ expensive, there is a minimum upsize percentage to avoid unnecessary
+ future move operations.}
+ {Small blocks always grow with at least 100% + SmallBlockUpsizeAdder bytes}
+ lea ecx, [ecx + ecx + SmallBlockUpsizeAdder]
+ {Save the requested size in rdi}
+ mov rdi, rdx
+ {New allocated size is the maximum of the requested size and the minimum
+ upsize}
+ xor rax, rax
+ sub rcx, rdx
+ adc rax, -1
+ and rcx, rax
+ add rcx, rdx
+ {Allocate the new block}
+ call FastGetMem
+ {Allocated OK?}
+ test rax, rax
+ jz @Done
+ {Do we need to store the requested size? Only large blocks store the
+ requested size.}
+ cmp rdi, MaximumMediumBlockSize - BlockHeaderSize
+ jbe @NotSmallUpsizeToLargeBlock
+ {Store the user requested size}
+ mov [rax - 2 * BlockHeaderSize], rdi
+@NotSmallUpsizeToLargeBlock:
+ {Get the size to move across}
+ movzx r8d, TSmallBlockType[rbx].BlockSize
+ sub r8d, BlockHeaderSize
+ {Move to the new block}
+ mov rdx, rax
+ {Save the result in edi}
+ mov rdi, rax
+ {Move from the old block}
+ mov rcx, rsi
+ {Move the data across}
+{$ifdef UseCustomFixedSizeMoveRoutines}
+ call TSmallBlockType[rbx].UpsizeMoveProcedure
+{$else}
+ call System.Move
+{$endif}
+ {Free the old pointer}
+ mov rcx, rsi
+ call FastFreeMem
+ {Done}
+ mov rax, rdi
+ jmp @Done
+@NotASmallBlock:
+ {Is this a medium block or a large block?}
+ test cl, IsFreeBlockFlag + IsLargeBlockFlag
+ jnz @PossibleLargeBlock
+ {-------------------------------Medium block--------------------------------------}
+ {Status: rcx = Current Block Size + Flags, rsi = APointer,
+ rdx = Requested Size}
+ mov rbx, rcx
+ {Drop the flags from the header}
+ and ecx, DropMediumAndLargeFlagsMask
+ {Get a pointer to the next block in rdi}
+ lea rdi, [rsi + rcx]
+ {Subtract the block header size from the old available size}
+ sub ecx, BlockHeaderSize
+ {Get the complete flags in ebx}
+ and ebx, ExtractMediumAndLargeFlagsMask
+ {Is it an upsize or a downsize?}
+ cmp rdx, rcx
+ ja @MediumBlockUpsize
+ {Status: ecx = Current Block Size - BlockHeaderSize, bl = Current Block Flags,
+ rdi = @Next Block, rsi = APointer, rdx = Requested Size}
+ {Must be less than half the current size or we don't bother resizing.}
+ lea r15, [rdx + rdx]
+ cmp r15, rcx
+ jb @MediumMustDownsize
+@MediumNoResize:
+ mov rax, rsi
+ jmp @Done
+@MediumMustDownsize:
+ {In-place downsize? Balance the cost of moving the data vs. the cost of
+ fragmenting the memory pool. Medium blocks in use may never be smaller
+ than MinimumMediumBlockSize.}
+ cmp edx, MinimumMediumBlockSize - BlockHeaderSize
+ jae @MediumBlockInPlaceDownsize
+ {The requested size is less than the minimum medium block size. If the
+ requested size is less than the threshold value (currently a quarter of the
+ minimum medium block size), move the data to a small block, otherwise shrink
+ the medium block to the minimum allowable medium block size.}
+ cmp edx, MediumInPlaceDownsizeLimit
+ jb @MediumDownsizeRealloc
+ {The request is for a size smaller than the minimum medium block size, but
+ not small enough to justify moving data: Reduce the block size to the
+ minimum medium block size}
+ mov edx, MinimumMediumBlockSize - BlockHeaderSize
+ {Is it already at the minimum medium block size?}
+ cmp ecx, edx
+ jna @MediumNoResize
+@MediumBlockInPlaceDownsize:
+ {Round up to the next medium block size}
+ lea r15, [rdx + BlockHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset]
+ and r15, -MediumBlockGranularity
+ add r15, MediumBlockSizeOffset
+ {Get the size of the second split}
+ add ecx, BlockHeaderSize
+ sub ecx, r15d
+ {Lock the medium blocks}
+{$ifndef AssumeMultiThreaded}
+ lea r8, IsMultiThread
+ cmp byte ptr [r8], False
+ je @DoMediumInPlaceDownsize
+{$endif}
+@DoMediumLockForDownsize:
+ {Lock the medium blocks}
+ mov ebx, ecx
+ call LockMediumBlocks
+ mov ecx, ebx
+ {Reread the flags - they may have changed before medium blocks could be
+ locked.}
+ mov rbx, ExtractMediumAndLargeFlagsMask
+ and rbx, [rsi - BlockHeaderSize]
+@DoMediumInPlaceDownsize:
+ {Set the new size}
+ or rbx, r15
+ mov [rsi - BlockHeaderSize], rbx
+ {Get the second split size in ebx}
+ mov ebx, ecx
+ {Is the next block in use?}
+ mov rdx, [rdi - BlockHeaderSize]
+ test dl, IsFreeBlockFlag
+ jnz @MediumDownsizeNextBlockFree
+ {The next block is in use: flag its previous block as free}
+ or rdx, PreviousMediumBlockIsFreeFlag
+ mov [rdi - BlockHeaderSize], rdx
+ jmp @MediumDownsizeDoSplit
+@MediumDownsizeNextBlockFree:
+ {The next block is free: combine it}
+ mov rcx, rdi
+ and rdx, DropMediumAndLargeFlagsMask
+ add rbx, rdx
+ add rdi, rdx
+ cmp edx, MinimumMediumBlockSize
+ jb @MediumDownsizeDoSplit
+ call RemoveMediumFreeBlock
+@MediumDownsizeDoSplit:
+ {Store the trailing size field}
+ mov [rdi - 2 * BlockHeaderSize], rbx
+ {Store the free part's header}
+ lea rcx, [rbx + IsMediumBlockFlag + IsFreeBlockFlag];
+ mov [rsi + r15 - BlockHeaderSize], rcx
+ {Bin this free block}
+ cmp rbx, MinimumMediumBlockSize
+ jb @MediumBlockDownsizeDone
+ lea rcx, [rsi + r15]
+ mov rdx, rbx
+ call InsertMediumBlockIntoBin
+@MediumBlockDownsizeDone:
+ {Unlock the medium blocks}
+ lea rax, MediumBlocksLocked
+ mov byte ptr [rax], False
+ {Result = old pointer}
+ mov rax, rsi
+ jmp @Done
+@MediumDownsizeRealloc:
+ {Save the requested size}
+ mov rdi, rdx
+ mov rcx, rdx
+ {Allocate the new block}
+ call FastGetMem
+ test rax, rax
+ jz @Done
+ {Save the result}
+ mov r15, rax
+ mov rdx, rax
+ mov rcx, rsi
+ mov r8, rdi
+ {Move the data across}
+{$ifdef UseCustomVariableSizeMoveRoutines}
+ {$ifdef Align16Bytes}
+ call MoveX16LP
+ {$else}
+ call MoveX8LP
+ {$endif}
+{$else}
+ call System.Move
+{$endif}
+ mov rcx, rsi
+ call FastFreeMem
+ {Return the result}
+ mov rax, r15
+ jmp @Done
+@MediumBlockUpsize:
+ {Status: ecx = Current Block Size - BlockHeaderSize, bl = Current Block Flags,
+ rdi = @Next Block, rsi = APointer, rdx = Requested Size}
+ {Can we do an in-place upsize?}
+ mov rax, [rdi - BlockHeaderSize]
+ test al, IsFreeBlockFlag
+ jz @CannotUpsizeMediumBlockInPlace
+ {Get the total available size including the next block}
+ and rax, DropMediumAndLargeFlagsMask
+ {r15 = total available size including the next block (excluding the header)}
+ lea r15, [rax + rcx]
+ {Can the block fit?}
+ cmp rdx, r15
+ ja @CannotUpsizeMediumBlockInPlace
+ {The next block is free and there is enough space to grow this
+ block in place.}
+{$ifndef AssumeMultiThreaded}
+ lea r8, IsMultiThread
+ cmp byte ptr [r8], False
+ je @DoMediumInPlaceUpsize
+{$endif}
+@DoMediumLockForUpsize:
+ {Lock the medium blocks.}
+ mov rbx, rcx
+ mov r15, rdx
+ call LockMediumBlocks
+ mov rcx, rbx
+ mov rdx, r15
+ {Re-read the info for this block (since it may have changed before the medium
+ blocks could be locked)}
+ mov rbx, ExtractMediumAndLargeFlagsMask
+ and rbx, [rsi - BlockHeaderSize]
+ {Re-read the info for the next block}
+ mov rax, [rdi - BlockheaderSize]
+ {Next block still free?}
+ test al, IsFreeBlockFlag
+ jz @NextMediumBlockChanged
+ {Recalculate the next block size}
+ and eax, DropMediumAndLargeFlagsMask
+ {The available size including the next block}
+ lea r15, [rax + rcx]
+ {Can the block still fit?}
+ cmp rdx, r15
+ ja @NextMediumBlockChanged
+@DoMediumInPlaceUpsize:
+ {Is the next block binnable?}
+ cmp eax, MinimumMediumBlockSize
+ {Remove the next block}
+ jb @MediumInPlaceNoNextRemove
+ mov r14, rcx
+ mov rcx, rdi
+ mov rdi, rdx
+ call RemoveMediumFreeBlock
+ mov rcx, r14
+ mov rdx, rdi
+@MediumInPlaceNoNextRemove:
+ {Medium blocks grow a minimum of 25% in in-place upsizes}
+ mov eax, ecx
+ shr eax, 2
+ add eax, ecx
+ {Get the maximum of the requested size and the minimum growth size}
+ xor edi, edi
+ sub eax, edx
+ adc edi, -1
+ and eax, edi
+ {Round up to the nearest block size granularity}
+ lea eax, [eax + edx + BlockHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset]
+ and eax, -MediumBlockGranularity
+ add eax, MediumBlockSizeOffset
+ {Calculate the size of the second split}
+ lea rdx, [r15 + BlockHeaderSize]
+ sub edx, eax
+ {Does it fit?}
+ ja @MediumInPlaceUpsizeSplit
+ {Grab the whole block: Mark it as used in the block following it}
+ and qword ptr [rsi + r15], not PreviousMediumBlockIsFreeFlag
+ {The block size is the full available size plus header}
+ add r15, BlockHeaderSize
+ {Upsize done}
+ jmp @MediumUpsizeInPlaceDone
+@MediumInPlaceUpsizeSplit:
+ {Store the size of the second split as the second last dword}
+ mov [rsi + r15 - BlockHeaderSize], rdx
+ {Set the second split header}
+ lea edi, [edx + IsMediumBlockFlag + IsFreeBlockFlag]
+ mov [rsi + rax - BlockHeaderSize], rdi
+ mov r15, rax
+ cmp edx, MinimumMediumBlockSize
+ jb @MediumUpsizeInPlaceDone
+ lea rcx, [rsi + rax]
+ call InsertMediumBlockIntoBin
+@MediumUpsizeInPlaceDone:
+ {Set the size and flags for this block}
+ or r15, rbx
+ mov [rsi - BlockHeaderSize], r15
+ {Unlock the medium blocks}
+ lea rax, MediumBlocksLocked
+ mov byte ptr [rax], False
+ {Result = old pointer}
+ mov rax, rsi
+ jmp @Done
+@NextMediumBlockChanged:
+ {The next medium block changed while the medium blocks were being locked}
+ lea rax, MediumBlocksLocked
+ mov byte ptr [rax], False
+@CannotUpsizeMediumBlockInPlace:
+ {Couldn't upsize in place. Grab a new block and move the data across:
+ If we have to reallocate and move medium blocks, we grow by at
+ least 25%}
+ mov eax, ecx
+ shr eax, 2
+ add eax, ecx
+ {Get the maximum of the requested size and the minimum growth size}
+ xor rdi, rdi
+ sub rax, rdx
+ adc rdi, -1
+ and rax, rdi
+ add rax, rdx
+ {Save the size to allocate}
+ mov r15, rax
+ {Save the size to move across}
+ mov edi, ecx
+ {Save the requested size}
+ mov rbx, rdx
+ {Get the block}
+ mov rcx, rax
+ call FastGetMem
+ mov rdx, rbx
+ {Success?}
+ test eax, eax
+ jz @Done
+ {If it's a Large block - store the actual user requested size}
+ cmp r15, MaximumMediumBlockSize - BlockHeaderSize
+ jbe @MediumUpsizeNotLarge
+ mov [rax - 2 * BlockHeaderSize], rdx
+@MediumUpsizeNotLarge:
+ {Save the result}
+ mov r15, rax
+ {Move the data across}
+ mov rdx, rax
+ mov rcx, rsi
+ mov r8, rdi
+{$ifdef UseCustomVariableSizeMoveRoutines}
+ call MoveX16LP
+{$else}
+ call System.Move
+{$endif}
+ {Free the old block}
+ mov rcx, rsi
+ call FastFreeMem
+ {Restore the result}
+ mov rax, r15
+ jmp @Done
+@PossibleLargeBlock:
+ {-----------------------Large block------------------------------}
+ {Is this a valid large block?}
+ test cl, IsFreeBlockFlag + IsMediumBlockFlag
+ jnz @Error
+ mov rcx, rsi
+ call ReallocateLargeBlock
+ jmp @Done
+ {-----------------------Invalid block------------------------------}
+@Error:
+ xor eax, eax
+@Done:
+end;
+{$endif}
+{$endif}
+{$endif}
+
+{Allocates a block and fills it with zeroes}
+function FastAllocMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Cardinal{$endif}): Pointer;
+{$ifndef ASMVersion}
+begin
+ Result := FastGetMem(ASize);
+ {Large blocks are already zero filled}
+ if (Result <> nil) and (ASize <= (MaximumMediumBlockSize - BlockHeaderSize)) then
+ FillChar(Result^, ASize, 0);
+end;
+{$else}
+{$ifdef 32Bit}
+asm
+ push ebx
+ {Get the size rounded down to the previous multiple of 4 into ebx}
+ lea ebx, [eax - 1]
+ and ebx, -4
+ {Get the block}
+ call FastGetMem
+ {Could a block be allocated? ecx = 0 if yes, $ffffffff if no}
+ cmp eax, 1
+ sbb ecx, ecx
+ {Point edx to the last dword}
+ lea edx, [eax + ebx]
+ {ebx = $ffffffff if no block could be allocated, otherwise size rounded down
+ to previous multiple of 4. If ebx = 0 then the block size is 1..4 bytes and
+ the FPU based clearing loop should not be used (since it clears 8 bytes per
+ iteration).}
+ or ebx, ecx
+ jz @ClearLastDWord
+ {Large blocks are already zero filled}
+ cmp ebx, MaximumMediumBlockSize - BlockHeaderSize
+ jae @Done
+ {Make the counter negative based}
+ neg ebx
+ {Load zero into st(0)}
+ fldz
+ {Clear groups of 8 bytes. Block sizes are always four less than a multiple
+ of 8.}
+@FillLoop:
+ fst qword ptr [edx + ebx]
+ add ebx, 8
+ js @FillLoop
+ {Clear st(0)}
+ ffree st(0)
+ {Correct the stack top}
+ fincstp
+ {Clear the last four bytes}
+@ClearLastDWord:
+ mov [edx], ecx
+@Done:
+ pop ebx
+end;
+
+{$else}
+
+{---------------64-bit BASM FastAllocMem---------------}
+asm
+ .params 1
+ .pushnv rbx
+ {Get the size rounded down to the previous multiple of SizeOf(Pointer) into
+ ebx}
+ lea rbx, [rcx - 1]
+ and rbx, -8
+ {Get the block}
+ call FastGetMem
+ {Could a block be allocated? rcx = 0 if yes, -1 if no}
+ cmp rax, 1
+ sbb rcx, rcx
+ {Point rdx to the last dword}
+ lea rdx, [rax + rbx]
+ {rbx = -1 if no block could be allocated, otherwise size rounded down
+ to previous multiple of 8. If rbx = 0 then the block size is 1..8 bytes and
+ the SSE2 based clearing loop should not be used (since it clears 16 bytes per
+ iteration).}
+ or rbx, rcx
+ jz @ClearLastQWord
+ {Large blocks are already zero filled}
+ cmp rbx, MaximumMediumBlockSize - BlockHeaderSize
+ jae @Done
+ {Make the counter negative based}
+ neg rbx
+ {Load zero into xmm0}
+ pxor xmm0, xmm0
+ {Clear groups of 16 bytes. Block sizes are always 8 less than a multiple of
+ 16.}
+@FillLoop:
+ movdqa [rdx + rbx], xmm0
+ add rbx, 16
+ js @FillLoop
+ {Clear the last 8 bytes}
+@ClearLastQWord:
+ xor rcx, rcx
+ mov [rdx], rcx
+@Done:
+end;
+{$endif}
+{$endif}
+
+{-----------------Post Uninstall GetMem/FreeMem/ReallocMem-------------------}
+
+{$ifdef DetectMMOperationsAfterUninstall}
+
+function InvalidGetMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
+{$ifndef NoMessageBoxes}
+var
+ LErrorMessageTitle: array[0..1023] of AnsiChar;
+{$endif}
+begin
+{$ifdef UseOutputDebugString}
+ OutputDebugStringA(InvalidGetMemMsg);
+{$endif}
+{$ifndef NoMessageBoxes}
+ AppendStringToModuleName(InvalidOperationTitle, LErrorMessageTitle);
+ ShowMessageBox(InvalidGetMemMsg, LErrorMessageTitle);
+{$endif}
+ Result := nil;
+end;
+
+function InvalidFreeMem(APointer: Pointer): Integer;
+{$ifndef NoMessageBoxes}
+var
+ LErrorMessageTitle: array[0..1023] of AnsiChar;
+{$endif}
+begin
+{$ifdef UseOutputDebugString}
+ OutputDebugStringA(InvalidFreeMemMsg);
+{$endif}
+{$ifndef NoMessageBoxes}
+ AppendStringToModuleName(InvalidOperationTitle, LErrorMessageTitle);
+ ShowMessageBox(InvalidFreeMemMsg, LErrorMessageTitle);
+{$endif}
+ Result := -1;
+end;
+
+function InvalidReallocMem(APointer: Pointer; ANewSize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
+{$ifndef NoMessageBoxes}
+var
+ LErrorMessageTitle: array[0..1023] of AnsiChar;
+{$endif}
+begin
+{$ifdef UseOutputDebugString}
+ OutputDebugStringA(InvalidReallocMemMsg);
+{$endif}
+{$ifndef NoMessageBoxes}
+ AppendStringToModuleName(InvalidOperationTitle, LErrorMessageTitle);
+ ShowMessageBox(InvalidReallocMemMsg, LErrorMessageTitle);
+{$endif}
+ Result := nil;
+end;
+
+function InvalidAllocMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Cardinal{$endif}): Pointer;
+{$ifndef NoMessageBoxes}
+var
+ LErrorMessageTitle: array[0..1023] of AnsiChar;
+{$endif}
+begin
+{$ifdef UseOutputDebugString}
+ OutputDebugStringA(InvalidAllocMemMsg);
+{$endif}
+{$ifndef NoMessageBoxes}
+ AppendStringToModuleName(InvalidOperationTitle, LErrorMessageTitle);
+ ShowMessageBox(InvalidAllocMemMsg, LErrorMessageTitle);
+{$endif}
+ Result := nil;
+end;
+
+function InvalidRegisterAndUnRegisterMemoryLeak(APointer: Pointer): Boolean;
+begin
+ Result := False;
+end;
+
+{$endif}
+
+{-----------------Full Debug Mode Memory Manager Interface--------------------}
+
+{$ifdef FullDebugMode}
+
+{Compare [AAddress], CompareVal:
+ If Equal: [AAddress] := NewVal and result = CompareVal
+ If Unequal: Result := [AAddress]}
+function LockCmpxchg32(CompareVal, NewVal: Integer; AAddress: PInteger): Integer;
+asm
+{$ifdef 32Bit}
+ {On entry:
+ eax = CompareVal,
+ edx = NewVal,
+ ecx = AAddress}
+ lock cmpxchg [ecx], edx
+{$else}
+.noframe
+ {On entry:
+ ecx = CompareVal,
+ edx = NewVal,
+ r8 = AAddress}
+ mov eax, ecx
+ lock cmpxchg [r8], edx
+{$endif}
+end;
+
+{Called by DebugGetMem, DebugFreeMem and DebugReallocMem in order to block a
+ free block scan operation while the memory pool is being modified.}
+procedure StartChangingFullDebugModeBlock;
+var
+ LOldCount: Integer;
+begin
+ while True do
+ begin
+ {Get the old thread count}
+ LOldCount := ThreadsInFullDebugModeRoutine;
+ if (LOldCount >= 0)
+ and (LockCmpxchg32(LOldCount, LOldCount + 1, @ThreadsInFullDebugModeRoutine) = LOldCount) then
+ begin
+ Break;
+ end;
+ {$ifdef NeverSleepOnThreadContention}
+ {$ifdef UseSwitchToThread}
+ SwitchToThread;
+ {$endif}
+ {$else}
+ Sleep(InitialSleepTime);
+ {Try again}
+ LOldCount := ThreadsInFullDebugModeRoutine;
+ if (LOldCount >= 0)
+ and (LockCmpxchg32(LOldCount, LOldCount + 1, @ThreadsInFullDebugModeRoutine) = LOldCount) then
+ begin
+ Break;
+ end;
+ Sleep(AdditionalSleepTime);
+ {$endif}
+ end;
+end;
+
+procedure DoneChangingFullDebugModeBlock;
+asm
+{$ifdef 32Bit}
+ lock dec ThreadsInFullDebugModeRoutine
+{$else}
+.noframe
+ lea rax, ThreadsInFullDebugModeRoutine
+ lock dec dword ptr [rax]
+{$endif}
+end;
+
+{Increments the allocation number}
+procedure IncrementAllocationNumber;
+asm
+{$ifdef 32Bit}
+ lock inc CurrentAllocationNumber
+{$else}
+.noframe
+ lea rax, CurrentAllocationNumber
+ lock inc dword ptr [rax]
+{$endif}
+end;
+
+{Called by a routine wanting to lock the entire memory pool in FullDebugMode, e.g. before scanning the memory
+ pool for corruptions.}
+procedure BlockFullDebugModeMMRoutines;
+begin
+ while True do
+ begin
+ {Get the old thread count}
+ if LockCmpxchg32(0, -1, @ThreadsInFullDebugModeRoutine) = 0 then
+ Break;
+{$ifdef NeverSleepOnThreadContention}
+ {$ifdef UseSwitchToThread}
+ SwitchToThread;
+ {$endif}
+{$else}
+ Sleep(InitialSleepTime);
+ {Try again}
+ if LockCmpxchg32(0, -1, @ThreadsInFullDebugModeRoutine) = 0 then
+ Break;
+ Sleep(AdditionalSleepTime);
+{$endif}
+ end;
+end;
+
+procedure UnblockFullDebugModeMMRoutines;
+begin
+ {Currently blocked? If so, unblock the FullDebugMode routines.}
+ if ThreadsInFullDebugModeRoutine = -1 then
+ ThreadsInFullDebugModeRoutine := 0;
+end;
+
+procedure DeleteEventLog;
+begin
+ {Delete the file}
+ DeleteFileA(MMLogFileName);
+end;
+
+{Finds the start and length of the file name given a full path.}
+procedure ExtractFileName(APFullPath: PAnsiChar; var APFileNameStart: PAnsiChar; var AFileNameLength: Integer);
+var
+ LChar: AnsiChar;
+begin
+ {Initialize}
+ APFileNameStart := APFullPath;
+ AFileNameLength := 0;
+ {Find the file }
+ while True do
+ begin
+ {Get the next character}
+ LChar := APFullPath^;
+ {End of the path string?}
+ if LChar = #0 then
+ Break;
+ {Advance the buffer position}
+ Inc(APFullPath);
+ {Found a backslash? -> May be the start of the file name}
+ if LChar = '\' then
+ APFileNameStart := APFullPath;
+ end;
+ {Calculate the length of the file name}
+ AFileNameLength := IntPtr(APFullPath) - IntPtr(APFileNameStart);
+end;
+
+procedure AppendEventLog(ABuffer: Pointer; ACount: Cardinal);
+const
+ {Declared here, because it is not declared in the SHFolder.pas unit of some older Delphi versions.}
+ SHGFP_TYPE_CURRENT = 0;
+var
+ LFileHandle, LBytesWritten: Cardinal;
+ LEventHeader: array[0..1023] of AnsiChar;
+ LAlternateLogFileName: array[0..2047] of AnsiChar;
+ LPathLen, LNameLength: Integer;
+ LMsgPtr, LPFileName: PAnsiChar;
+ LSystemTime: TSystemTime;
+begin
+ {Try to open the log file in read/write mode.}
+ LFileHandle := CreateFileA(MMLogFileName, GENERIC_READ or GENERIC_WRITE,
+ 0, nil, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
+ {Did log file creation fail? If so, the destination folder is perhaps read-only:
+ Try to redirect logging to a file in the user's "My Documents" folder.}
+ if (LFileHandle = INVALID_HANDLE_VALUE)
+{$ifdef Delphi4or5}
+ and SHGetSpecialFolderPathA(0, @LAlternateLogFileName, CSIDL_PERSONAL, True) then
+{$else}
+ and (SHGetFolderPathA(0, CSIDL_PERSONAL or CSIDL_FLAG_CREATE, 0,
+ SHGFP_TYPE_CURRENT, @LAlternateLogFileName) = S_OK) then
+{$endif}
+ begin
+ {Extract the filename part from MMLogFileName and append it to the path of
+ the "My Documents" folder.}
+ LPathLen := StrLen(LAlternateLogFileName);
+ {Ensure that there is a trailing backslash in the path}
+ if (LPathLen = 0) or (LAlternateLogFileName[LPathLen - 1] <> '\') then
+ begin
+ LAlternateLogFileName[LPathLen] := '\';
+ Inc(LPathLen);
+ end;
+ {Add the filename to the path}
+ ExtractFileName(@MMLogFileName, LPFileName, LNameLength);
+ System.Move(LPFileName^, LAlternateLogFileName[LPathLen], LNameLength + 1);
+ {Try to open the alternate log file}
+ LFileHandle := CreateFileA(LAlternateLogFileName, GENERIC_READ or GENERIC_WRITE,
+ 0, nil, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
+ end;
+ {Was the log file opened/created successfully?}
+ if LFileHandle <> INVALID_HANDLE_VALUE then
+ begin
+ {Seek to the end of the file}
+ SetFilePointer(LFileHandle, 0, nil, FILE_END);
+ {Set the separator}
+ LMsgPtr := AppendStringToBuffer(CRLF, @LEventHeader[0], Length(CRLF));
+ LMsgPtr := AppendStringToBuffer(EventSeparator, LMsgPtr, Length(EventSeparator));
+ {Set the date & time}
+ GetLocalTime(LSystemTime);
+ LMsgPtr := NativeUIntToStrBuf(LSystemTime.wYear, LMsgPtr);
+ LMsgPtr^ := '/';
+ Inc(LMsgPtr);
+ LMsgPtr := NativeUIntToStrBuf(LSystemTime.wMonth, LMsgPtr);
+ LMsgPtr^ := '/';
+ Inc(LMsgPtr);
+ LMsgPtr := NativeUIntToStrBuf(LSystemTime.wDay, LMsgPtr);
+ LMsgPtr^ := ' ';
+ Inc(LMsgPtr);
+ LMsgPtr := NativeUIntToStrBuf(LSystemTime.wHour, LMsgPtr);
+ LMsgPtr^ := ':';
+ Inc(LMsgPtr);
+ if LSystemTime.wMinute < 10 then
+ begin
+ LMsgPtr^ := '0';
+ Inc(LMsgPtr);
+ end;
+ LMsgPtr := NativeUIntToStrBuf(LSystemTime.wMinute, LMsgPtr);
+ LMsgPtr^ := ':';
+ Inc(LMsgPtr);
+ if LSystemTime.wSecond < 10 then
+ begin
+ LMsgPtr^ := '0';
+ Inc(LMsgPtr);
+ end;
+ LMsgPtr := NativeUIntToStrBuf(LSystemTime.WSecond, LMsgPtr);
+ {Write the header}
+ LMsgPtr := AppendStringToBuffer(EventSeparator, LMsgPtr, Length(EventSeparator));
+ LMsgPtr := AppendStringToBuffer(CRLF, LMsgPtr, Length(CRLF));
+ WriteFile(LFileHandle, LEventHeader[0], NativeUInt(LMsgPtr) - NativeUInt(@LEventHeader[0]), LBytesWritten, nil);
+ {Write the data}
+ WriteFile(LFileHandle, ABuffer^, ACount, LBytesWritten, nil);
+ {Close the file}
+ CloseHandle(LFileHandle);
+ end;
+end;
+
+{Sets the default log filename}
+procedure SetDefaultMMLogFileName;
+const
+ LogFileExtAnsi: PAnsiChar = LogFileExtension;
+var
+ LEnvVarLength, LModuleNameLength: Cardinal;
+ LPathOverride: array[0..2047] of AnsiChar;
+ LPFileName: PAnsiChar;
+ LFileNameLength: Integer;
+begin
+ {Get the name of the application}
+ LModuleNameLength := AppendModuleFileName(@MMLogFileName[0]);
+ {Replace the last few characters of the module name, and optionally override
+ the path.}
+ if LModuleNameLength > 0 then
+ begin
+ {Change the filename}
+ System.Move(LogFileExtAnsi^, MMLogFileName[LModuleNameLength - 4],
+ StrLen(LogFileExtAnsi) + 1);
+ {Try to read the FastMMLogFilePath environment variable}
+ LEnvVarLength := GetEnvironmentVariableA(PAnsiChar('FastMMLogFilePath'),
+ @LPathOverride, 1023);
+ {Does the environment variable exist? If so, override the log file path.}
+ if LEnvVarLength > 0 then
+ begin
+ {Ensure that there's a trailing backslash.}
+ if LPathOverride[LEnvVarLength - 1] <> '\' then
+ begin
+ LPathOverride[LEnvVarLength] := '\';
+ Inc(LEnvVarLength);
+ end;
+ {Add the filename to the path override}
+ ExtractFileName(@MMLogFileName[0], LPFileName, LFileNameLength);
+ System.Move(LPFileName^, LPathOverride[LEnvVarLength], LFileNameLength + 1);
+ {Copy the override path back to the filename buffer}
+ System.Move(LPathOverride, MMLogFileName, SizeOf(MMLogFileName) - 1);
+ end;
+ end;
+end;
+
+{Specify the full path and name for the filename to be used for logging memory
+ errors, etc. If ALogFileName is nil or points to an empty string it will
+ revert to the default log file name.}
+procedure SetMMLogFileName(ALogFileName: PAnsiChar = nil);
+var
+ LLogFileNameLen: Integer;
+begin
+ {Is ALogFileName valid?}
+ if (ALogFileName <> nil) and (ALogFileName^ <> #0) then
+ begin
+ LLogFileNameLen := StrLen(ALogFileName);
+ if LLogFileNameLen < Length(MMLogFileName) then
+ begin
+ {Set the log file name}
+ System.Move(ALogFileName^, MMLogFileName, LLogFileNameLen + 1);
+ Exit;
+ end;
+ end;
+ {Invalid log file name}
+ SetDefaultMMLogFileName;
+end;
+
+{Returns the current "allocation group". Whenever a GetMem request is serviced
+ in FullDebugMode, the current "allocation group" is stored in the block header.
+ This may help with debugging. Note that if a block is subsequently reallocated
+ that it keeps its original "allocation group" and "allocation number" (all
+ allocations are also numbered sequentially).}
+function GetCurrentAllocationGroup: Cardinal;
+begin
+ Result := AllocationGroupStack[AllocationGroupStackTop];
+end;
+
+{Allocation groups work in a stack like fashion. Group numbers are pushed onto
+ and popped off the stack. Note that the stack size is limited, so every push
+ should have a matching pop.}
+procedure PushAllocationGroup(ANewCurrentAllocationGroup: Cardinal);
+begin
+ if AllocationGroupStackTop < AllocationGroupStackSize - 1 then
+ begin
+ Inc(AllocationGroupStackTop);
+ AllocationGroupStack[AllocationGroupStackTop] := ANewCurrentAllocationGroup;
+ end
+ else
+ begin
+ {Raise a runtime error if the stack overflows}
+ {$ifdef BCB6OrDelphi7AndUp}
+ System.Error(reInvalidPtr);
+ {$else}
+ System.RunError(reInvalidPtr);
+ {$endif}
+ end;
+end;
+
+procedure PopAllocationGroup;
+begin
+ if AllocationGroupStackTop > 0 then
+ begin
+ Dec(AllocationGroupStackTop);
+ end
+ else
+ begin
+ {Raise a runtime error if the stack underflows}
+ {$ifdef BCB6OrDelphi7AndUp}
+ System.Error(reInvalidPtr);
+ {$else}
+ System.RunError(reInvalidPtr);
+ {$endif}
+ end;
+end;
+
+{Sums all the dwords starting at the given address. ACount must be > 0 and a
+ multiple of SizeOf(Pointer).}
+function SumNativeUInts(AStartValue: NativeUInt; APointer: PNativeUInt;
+ ACount: NativeUInt): NativeUInt;
+asm
+{$ifdef 32Bit}
+ {On entry: eax = AStartValue, edx = APointer; ecx = ACount}
+ add edx, ecx
+ neg ecx
+@AddLoop:
+ add eax, [edx + ecx]
+ add ecx, 4
+ js @AddLoop
+{$else}
+ {On entry: rcx = AStartValue, rdx = APointer; r8 = ACount}
+ add rdx, r8
+ neg r8
+ mov rax, rcx
+@AddLoop:
+ add rax, [rdx + r8]
+ add r8, 8
+ js @AddLoop
+{$endif}
+end;
+
+{Checks the memory starting at the given address for the fill pattern.
+ Returns True if all bytes are all valid. ACount must be >0 and a multiple of
+ SizeOf(Pointer).}
+function CheckFillPattern(APointer: Pointer; ACount: NativeUInt;
+ AFillPattern: NativeUInt): Boolean;
+asm
+{$ifdef 32Bit}
+ {On entry: eax = APointer; edx = ACount; ecx = AFillPattern}
+ add eax, edx
+ neg edx
+@CheckLoop:
+ cmp [eax + edx], ecx
+ jne @Done
+ add edx, 4
+ js @CheckLoop
+@Done:
+ sete al
+{$else}
+ {On entry: rcx = APointer; rdx = ACount; r8 = AFillPattern}
+ add rcx, rdx
+ neg rdx
+@CheckLoop:
+ cmp [rcx + rdx], r8
+ jne @Done
+ add rdx, 8
+ js @CheckLoop
+@Done:
+ sete al
+{$endif}
+end;
+
+{Calculates the checksum for the debug header. Adds all dwords in the debug
+ header to the start address of the block.}
+function CalculateHeaderCheckSum(APointer: PFullDebugBlockHeader): NativeUInt;
+begin
+ Result := SumNativeUInts(
+ NativeUInt(APointer),
+ PNativeUInt(PByte(APointer) + 2 * SizeOf(Pointer)),
+ SizeOf(TFullDebugBlockHeader) - 2 * SizeOf(Pointer) - SizeOf(NativeUInt));
+end;
+
+procedure UpdateHeaderAndFooterCheckSums(APointer: PFullDebugBlockHeader);
+var
+ LHeaderCheckSum: NativeUInt;
+begin
+ LHeaderCheckSum := CalculateHeaderCheckSum(APointer);
+ APointer.HeaderCheckSum := LHeaderCheckSum;
+ PNativeUInt(PByte(APointer) + SizeOf(TFullDebugBlockHeader) + APointer.UserSize)^ := not LHeaderCheckSum;
+end;
+
+function LogCurrentThreadAndStackTrace(ASkipFrames: Cardinal; ABuffer: PAnsiChar): PAnsiChar;
+var
+ LCurrentStackTrace: TStackTrace;
+begin
+ {Get the current call stack}
+ GetStackTrace(@LCurrentStackTrace[0], StackTraceDepth, ASkipFrames);
+ {Log the thread ID}
+ Result := AppendStringToBuffer(CurrentThreadIDMsg, ABuffer, Length(CurrentThreadIDMsg));
+ Result := NativeUIntToHexBuf(GetThreadID, Result);
+ {List the stack trace}
+ Result := AppendStringToBuffer(CurrentStackTraceMsg, Result, Length(CurrentStackTraceMsg));
+ Result := LogStackTrace(@LCurrentStackTrace, StackTraceDepth, Result);
+end;
+
+{$ifndef DisableLoggingOfMemoryDumps}
+function LogMemoryDump(APointer: PFullDebugBlockHeader; ABuffer: PAnsiChar): PAnsiChar;
+var
+ LByteNum, LVal: Cardinal;
+ LDataPtr: PByte;
+begin
+ Result := AppendStringToBuffer(MemoryDumpMsg, ABuffer, Length(MemoryDumpMsg));
+ Result := NativeUIntToHexBuf(NativeUInt(APointer) + SizeOf(TFullDebugBlockHeader), Result);
+ Result^ := ':';
+ Inc(Result);
+ {Add the bytes}
+ LDataPtr := PByte(PByte(APointer) + SizeOf(TFullDebugBlockHeader));
+ for LByteNum := 0 to 255 do
+ begin
+ if LByteNum and 31 = 0 then
+ begin
+ Result^ := #13;
+ Inc(Result);
+ Result^ := #10;
+ Inc(Result);
+ end
+ else
+ begin
+ Result^ := ' ';
+ Inc(Result);
+ end;
+ {Set the hex data}
+ LVal := Byte(LDataPtr^);
+ Result^ := HexTable[LVal shr 4];
+ Inc(Result);
+ Result^ := HexTable[LVal and $f];
+ Inc(Result);
+ {Next byte}
+ Inc(LDataPtr);
+ end;
+ {Dump ASCII}
+ LDataPtr := PByte(PByte(APointer) + SizeOf(TFullDebugBlockHeader));
+ for LByteNum := 0 to 255 do
+ begin
+ if LByteNum and 31 = 0 then
+ begin
+ Result^ := #13;
+ Inc(Result);
+ Result^ := #10;
+ Inc(Result);
+ end
+ else
+ begin
+ Result^ := ' ';
+ Inc(Result);
+ Result^ := ' ';
+ Inc(Result);
+ end;
+ {Set the hex data}
+ LVal := Byte(LDataPtr^);
+ if LVal < 32 then
+ Result^ := '.'
+ else
+ Result^ := AnsiChar(LVal);
+ Inc(Result);
+ {Next byte}
+ Inc(LDataPtr);
+ end;
+end;
+{$endif}
+
+{Rotates AValue ABitCount bits to the right}
+function RotateRight(AValue, ABitCount: NativeUInt): NativeUInt;
+asm
+{$ifdef 32Bit}
+ mov ecx, edx
+ ror eax, cl
+{$else}
+ mov rax, rcx
+ mov rcx, rdx
+ ror rax, cl
+{$endif}
+end;
+
+{Determines whether a byte in the user portion of the freed block has been modified. Does not work beyond
+ the end of the user portion (i.e. footer and beyond).}
+function FreeBlockByteWasModified(APointer: PFullDebugBlockHeader; AUserOffset: NativeUInt): Boolean;
+var
+ LFillPattern: NativeUInt;
+begin
+ {Get the expected fill pattern}
+ if AUserOffset < SizeOf(Pointer) then
+ begin
+ LFillPattern := NativeUInt(@FreedObjectVMT.VMTMethods[0]);
+ end
+ else
+ begin
+{$ifndef CatchUseOfFreedInterfaces}
+ LFillPattern := DebugFillPattern;
+{$else}
+ LFillPattern := NativeUInt(@VMTBadInterface);
+{$endif}
+ end;
+ {Compare the byte value}
+ Result := Byte(PByte(PByte(APointer) + SizeOf(TFullDebugBlockHeader) + AUserOffset)^) <>
+ Byte(RotateRight(LFillPattern, (AUserOffset and (SizeOf(Pointer) - 1)) * 8));
+end;
+
+function LogBlockChanges(APointer: PFullDebugBlockHeader; ABuffer: PAnsiChar): PAnsiChar;
+var
+ LOffset, LChangeStart, LCount: NativeUInt;
+ LLogCount: Integer;
+begin
+ {No errors logged so far}
+ LLogCount := 0;
+ {Log a maximum of 32 changes}
+ LOffset := 0;
+ while (LOffset < APointer.UserSize) and (LLogCount < 32) do
+ begin
+ {Has the byte been modified?}
+ if FreeBlockByteWasModified(APointer, LOffset) then
+ begin
+ {Found the start of a changed block, now find the length}
+ LChangeStart := LOffset;
+ LCount := 0;
+ while True do
+ begin
+ Inc(LCount);
+ Inc(LOffset);
+ if (LOffset >= APointer.UserSize)
+ or (not FreeBlockByteWasModified(APointer, LOffset)) then
+ begin
+ Break;
+ end;
+ end;
+ {Got the offset and length, now log it.}
+ if LLogCount = 0 then
+ begin
+ ABuffer := AppendStringToBuffer(FreeModifiedDetailMsg, ABuffer, Length(FreeModifiedDetailMsg));
+ end
+ else
+ begin
+ ABuffer^ := ',';
+ Inc(ABuffer);
+ ABuffer^ := ' ';
+ Inc(ABuffer);
+ end;
+ ABuffer := NativeUIntToStrBuf(LChangeStart, ABuffer);
+ ABuffer^ := '(';
+ Inc(ABuffer);
+ ABuffer := NativeUIntToStrBuf(LCount, ABuffer);
+ ABuffer^ := ')';
+ Inc(ABuffer);
+ {Increment the log count}
+ Inc(LLogCount);
+ end;
+ {Next byte}
+ Inc(LOffset);
+ end;
+ {Return the current buffer position}
+ Result := ABuffer;
+end;
+
+procedure LogBlockError(APointer: PFullDebugBlockHeader; AOperation: TBlockOperation; LHeaderValid, LFooterValid: Boolean);
+var
+ LMsgPtr: PAnsiChar;
+ LErrorMessage: array[0..32767] of AnsiChar;
+{$ifndef NoMessageBoxes}
+ LErrorMessageTitle: array[0..1023] of AnsiChar;
+{$endif}
+ LClass: TClass;
+ {$ifdef CheckCppObjectTypeEnabled}
+ LCppObjectTypeName: PAnsiChar;
+ {$endif}
+begin
+ {Display the error header and the operation type.}
+ LMsgPtr := AppendStringToBuffer(ErrorMsgHeader, @LErrorMessage[0], Length(ErrorMsgHeader));
+ case AOperation of
+ boGetMem: LMsgPtr := AppendStringToBuffer(GetMemMsg, LMsgPtr, Length(GetMemMsg));
+ boFreeMem: LMsgPtr := AppendStringToBuffer(FreeMemMsg, LMsgPtr, Length(FreeMemMsg));
+ boReallocMem: LMsgPtr := AppendStringToBuffer(ReallocMemMsg, LMsgPtr, Length(ReallocMemMsg));
+ boBlockCheck: LMsgPtr := AppendStringToBuffer(BlockCheckMsg, LMsgPtr, Length(BlockCheckMsg));
+ end;
+ LMsgPtr := AppendStringToBuffer(OperationMsg, LMsgPtr, Length(OperationMsg));
+ {Is the header still intact?}
+ if LHeaderValid then
+ begin
+ {Is the footer still valid?}
+ if LFooterValid then
+ begin
+ {A freed block has been modified, a double free has occurred, or an
+ attempt was made to free a memory block allocated by a different
+ instance of FastMM.}
+ if AOperation <= boGetMem then
+ begin
+ LMsgPtr := AppendStringToBuffer(FreeModifiedErrorMsg, LMsgPtr, Length(FreeModifiedErrorMsg));
+ {Log the exact changes that caused the error.}
+ LMsgPtr := LogBlockChanges(APointer, LMsgPtr);
+ end
+ else
+ begin
+ {It is either a double free, or an attempt was made to free a block
+ that was allocated via a different memory manager.}
+ if APointer.AllocatedByRoutine = nil then
+ LMsgPtr := AppendStringToBuffer(DoubleFreeErrorMsg, LMsgPtr, Length(DoubleFreeErrorMsg))
+ else
+ LMsgPtr := AppendStringToBuffer(WrongMMFreeErrorMsg, LMsgPtr, Length(WrongMMFreeErrorMsg));
+ end;
+ end
+ else
+ begin
+ LMsgPtr := AppendStringToBuffer(BlockFooterCorruptedMsg, LMsgPtr, Length(BlockFooterCorruptedMsg))
+ end;
+ {Set the block size message}
+ if AOperation <= boGetMem then
+ LMsgPtr := AppendStringToBuffer(PreviousBlockSizeMsg, LMsgPtr, Length(PreviousBlockSizeMsg))
+ else
+ LMsgPtr := AppendStringToBuffer(CurrentBlockSizeMsg, LMsgPtr, Length(CurrentBlockSizeMsg));
+ LMsgPtr := NativeUIntToStrBuf(APointer.UserSize, LMsgPtr);
+ {The header is still intact - display info about the this/previous allocation}
+ if APointer.AllocationStackTrace[0] <> 0 then
+ begin
+ if AOperation <= boGetMem then
+ LMsgPtr := AppendStringToBuffer(ThreadIDPrevAllocMsg, LMsgPtr, Length(ThreadIDPrevAllocMsg))
+ else
+ LMsgPtr := AppendStringToBuffer(ThreadIDAtAllocMsg, LMsgPtr, Length(ThreadIDAtAllocMsg));
+ LMsgPtr := NativeUIntToHexBuf(APointer.AllocatedByThread, LMsgPtr);
+ LMsgPtr := AppendStringToBuffer(StackTraceMsg, LMsgPtr, Length(StackTraceMsg));
+ LMsgPtr := LogStackTrace(@APointer.AllocationStackTrace, StackTraceDepth, LMsgPtr);
+ end;
+ {Get the class this block was used for previously}
+ LClass := DetectClassInstance(@APointer.PreviouslyUsedByClass);
+ if (LClass <> nil) and (IntPtr(LClass) <> IntPtr(@FreedObjectVMT.VMTMethods[0])) then
+ begin
+ LMsgPtr := AppendStringToBuffer(PreviousObjectClassMsg, LMsgPtr, Length(PreviousObjectClassMsg));
+ LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
+ end;
+ {$ifdef CheckCppObjectTypeEnabled}
+ if (LClass = nil) and Assigned(GetCppVirtObjTypeNameByVTablePtrFunc) then
+ begin
+ LCppObjectTypeName := GetCppVirtObjTypeNameByVTablePtrFunc(Pointer(APointer.PreviouslyUsedByClass), 0);
+ if Assigned(LCppObjectTypeName) then
+ begin
+ LMsgPtr := AppendStringToBuffer(PreviousObjectClassMsg, LMsgPtr, Length(PreviousObjectClassMsg));
+ LMsgPtr := AppendStringToBuffer(LCppObjectTypeName, LMsgPtr, StrLen(LCppObjectTypeName));
+ end;
+ end;
+ {$endif}
+ {Get the current class for this block}
+ if (AOperation > boGetMem) and (APointer.AllocatedByRoutine <> nil) then
+ begin
+ LMsgPtr := AppendStringToBuffer(CurrentObjectClassMsg, LMsgPtr, Length(CurrentObjectClassMsg));
+ LClass := DetectClassInstance(Pointer(PByte(APointer) + SizeOf(TFullDebugBlockHeader)));
+ if IntPtr(LClass) = IntPtr(@FreedObjectVMT.VMTMethods[0]) then
+ LClass := nil;
+ {$ifndef CheckCppObjectTypeEnabled}
+ LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
+ {$else}
+ if (LClass = nil) and Assigned(GetCppVirtObjTypeNameFunc) then
+ begin
+ LCppObjectTypeName := GetCppVirtObjTypeNameFunc(Pointer(PByte(APointer) + SizeOf(TFullDebugBlockHeader)),
+ APointer.UserSize);
+ if LCppObjectTypeName <> nil then
+ LMsgPtr := AppendStringToBuffer(LCppObjectTypeName, LMsgPtr, StrLen(LCppObjectTypeName))
+ else
+ LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
+ end
+ else
+ begin
+ LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
+ end;
+ {$endif}
+ {Log the allocation group}
+ if APointer.AllocationGroup > 0 then
+ begin
+ LMsgPtr := AppendStringToBuffer(CurrentAllocationGroupMsg, LMsgPtr, Length(CurrentAllocationGroupMsg));
+ LMsgPtr := NativeUIntToStrBuf(APointer.AllocationGroup, LMsgPtr);
+ end;
+ {Log the allocation number}
+ LMsgPtr := AppendStringToBuffer(CurrentAllocationNumberMsg, LMsgPtr, Length(CurrentAllocationNumberMsg));
+ LMsgPtr := NativeUIntToStrBuf(APointer.AllocationNumber, LMsgPtr);
+ end
+ else
+ begin
+ {Log the allocation group}
+ if APointer.AllocationGroup > 0 then
+ begin
+ LMsgPtr := AppendStringToBuffer(PreviousAllocationGroupMsg, LMsgPtr, Length(PreviousAllocationGroupMsg));
+ LMsgPtr := NativeUIntToStrBuf(APointer.AllocationGroup, LMsgPtr);
+ end;
+ {Log the allocation number}
+ LMsgPtr := AppendStringToBuffer(PreviousAllocationNumberMsg, LMsgPtr, Length(PreviousAllocationNumberMsg));
+ LMsgPtr := NativeUIntToStrBuf(APointer.AllocationNumber, LMsgPtr);
+ end;
+ {Get the call stack for the previous free}
+ if APointer.FreeStackTrace[0] <> 0 then
+ begin
+ LMsgPtr := AppendStringToBuffer(ThreadIDAtFreeMsg, LMsgPtr, Length(ThreadIDAtFreeMsg));
+ LMsgPtr := NativeUIntToHexBuf(APointer.FreedByThread, LMsgPtr);
+ LMsgPtr := AppendStringToBuffer(StackTraceMsg, LMsgPtr, Length(StackTraceMsg));
+ LMsgPtr := LogStackTrace(@APointer.FreeStackTrace, StackTraceDepth, LMsgPtr);
+ end;
+ end
+ else
+ begin
+ {Header has been corrupted}
+ LMsgPtr := AppendStringToBuffer(BlockHeaderCorruptedMsg, LMsgPtr, Length(BlockHeaderCorruptedMsg));
+ end;
+ {Add the current stack trace}
+ LMsgPtr := LogCurrentThreadAndStackTrace(3 + Ord(AOperation <> boGetMem) + Ord(AOperation = boReallocMem), LMsgPtr);
+{$ifndef DisableLoggingOfMemoryDumps}
+ {Add the memory dump}
+ LMsgPtr := LogMemoryDump(APointer, LMsgPtr);
+{$endif}
+ {Trailing CRLF}
+ LMsgPtr^ := #13;
+ Inc(LMsgPtr);
+ LMsgPtr^ := #10;
+ Inc(LMsgPtr);
+ {Trailing #0}
+ LMsgPtr^ := #0;
+{$ifdef LogErrorsToFile}
+ {Log the error}
+ AppendEventLog(@LErrorMessage[0], NativeUInt(LMsgPtr) - NativeUInt(@LErrorMessage[0]));
+{$endif}
+{$ifdef UseOutputDebugString}
+ OutputDebugStringA(LErrorMessage);
+{$endif}
+ {Show the message}
+{$ifndef NoMessageBoxes}
+ AppendStringToModuleName(BlockErrorMsgTitle, LErrorMessageTitle);
+ ShowMessageBox(LErrorMessage, LErrorMessageTitle);
+{$endif}
+end;
+
+{Logs the stack traces for a memory leak to file}
+procedure LogMemoryLeakOrAllocatedBlock(APointer: PFullDebugBlockHeader; IsALeak: Boolean);
+var
+ LHeaderValid: Boolean;
+ LMsgPtr: PAnsiChar;
+ LErrorMessage: array[0..32767] of AnsiChar;
+ LClass: TClass;
+ {$ifdef CheckCppObjectTypeEnabled}
+ LCppObjectTypeName: PAnsiChar;
+ {$endif}
+begin
+ {Display the error header and the operation type.}
+ if IsALeak then
+ LMsgPtr := AppendStringToBuffer(LeakLogHeader, @LErrorMessage[0], Length(LeakLogHeader))
+ else
+ LMsgPtr := AppendStringToBuffer(BlockScanLogHeader, @LErrorMessage[0], Length(BlockScanLogHeader));
+ LMsgPtr := NativeUIntToStrBuf(GetAvailableSpaceInBlock(APointer) - FullDebugBlockOverhead, LMsgPtr);
+ {Is the debug info surrounding the block valid?}
+ LHeaderValid := CalculateHeaderCheckSum(APointer) = APointer.HeaderCheckSum;
+ {Is the header still intact?}
+ if LHeaderValid then
+ begin
+ {The header is still intact - display info about this/previous allocation}
+ if APointer.AllocationStackTrace[0] <> 0 then
+ begin
+ LMsgPtr := AppendStringToBuffer(ThreadIDAtAllocMsg, LMsgPtr, Length(ThreadIDAtAllocMsg));
+ LMsgPtr := NativeUIntToHexBuf(APointer.AllocatedByThread, LMsgPtr);
+ LMsgPtr := AppendStringToBuffer(StackTraceMsg, LMsgPtr, Length(StackTraceMsg));
+ LMsgPtr := LogStackTrace(@APointer.AllocationStackTrace, StackTraceDepth, LMsgPtr);
+ end;
+ LMsgPtr := AppendStringToBuffer(CurrentObjectClassMsg, LMsgPtr, Length(CurrentObjectClassMsg));
+ {Get the current class for this block}
+ LClass := DetectClassInstance(Pointer(PByte(APointer) + SizeOf(TFullDebugBlockHeader)));
+ if IntPtr(LClass) = IntPtr(@FreedObjectVMT.VMTMethods[0]) then
+ LClass := nil;
+ {$ifndef CheckCppObjectTypeEnabled}
+ if LClass <> nil then
+ begin
+ LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
+ end
+ else
+ begin
+ case DetectStringData(Pointer(PByte(APointer) + SizeOf(TFullDebugBlockHeader)), APointer.UserSize) of
+ stUnknown: LMsgPtr := AppendClassNameToBuffer(nil, LMsgPtr);
+ stAnsiString: LMsgPtr := AppendStringToBuffer(AnsiStringBlockMessage, LMsgPtr, Length(AnsiStringBlockMessage));
+ stUnicodeString: LMsgPtr := AppendStringToBuffer(UnicodeStringBlockMessage, LMsgPtr, Length(UnicodeStringBlockMessage));
+ end;
+ end;
+ {$else}
+ if (LClass = nil) and Assigned(GetCppVirtObjTypeNameFunc) then
+ begin
+ LCppObjectTypeName := GetCppVirtObjTypeNameFunc(Pointer(PByte(APointer) + SizeOf(TFullDebugBlockHeader)),
+ APointer.UserSize);
+ if LCppObjectTypeName <> nil then
+ LMsgPtr := AppendStringToBuffer(LCppObjectTypeName, LMsgPtr, StrLen(LCppObjectTypeName))
+ else
+ begin
+ case DetectStringData(Pointer(PByte(APointer) + SizeOf(TFullDebugBlockHeader)), APointer.UserSize) of
+ stUnknown: LMsgPtr := AppendClassNameToBuffer(nil, LMsgPtr);
+ stAnsiString: LMsgPtr := AppendStringToBuffer(AnsiStringBlockMessage, LMsgPtr, Length(AnsiStringBlockMessage));
+ stUnicodeString: LMsgPtr := AppendStringToBuffer(UnicodeStringBlockMessage, LMsgPtr, Length(UnicodeStringBlockMessage));
+ end;
+ end;
+ end
+ else
+ LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
+ {$endif}
+ {Log the allocation group}
+ if APointer.AllocationGroup > 0 then
+ begin
+ LMsgPtr := AppendStringToBuffer(CurrentAllocationGroupMsg, LMsgPtr, Length(CurrentAllocationGroupMsg));
+ LMsgPtr := NativeUIntToStrBuf(APointer.AllocationGroup, LMsgPtr);
+ end;
+ {Log the allocation number}
+ LMsgPtr := AppendStringToBuffer(CurrentAllocationNumberMsg, LMsgPtr, Length(CurrentAllocationNumberMsg));
+ LMsgPtr := NativeUIntToStrBuf(APointer.AllocationNumber, LMsgPtr);
+ end
+ else
+ begin
+ {Header has been corrupted}
+ LMsgPtr^ := '.';
+ Inc(LMsgPtr);
+ LMsgPtr^ := ' ';
+ Inc(LMsgPtr);
+ LMsgPtr := AppendStringToBuffer(BlockHeaderCorruptedMsg, LMsgPtr, Length(BlockHeaderCorruptedMsg));
+ end;
+{$ifndef DisableLoggingOfMemoryDumps}
+ {Add the memory dump}
+ LMsgPtr := LogMemoryDump(APointer, LMsgPtr);
+{$endif}
+ {Trailing CRLF}
+ LMsgPtr^ := #13;
+ Inc(LMsgPtr);
+ LMsgPtr^ := #10;
+ Inc(LMsgPtr);
+ {Trailing #0}
+ LMsgPtr^ := #0;
+ {Log the error}
+ AppendEventLog(@LErrorMessage[0], NativeUInt(LMsgPtr) - NativeUInt(@LErrorMessage[0]));
+end;
+
+{Checks that a free block is unmodified}
+function CheckFreeBlockUnmodified(APBlock: PFullDebugBlockHeader; ABlockSize: NativeUInt;
+ AOperation: TBlockOperation): Boolean;
+var
+ LHeaderCheckSum: NativeUInt;
+ LHeaderValid, LFooterValid, LBlockUnmodified: Boolean;
+begin
+ LHeaderCheckSum := CalculateHeaderCheckSum(APBlock);
+ LHeaderValid := LHeaderCheckSum = APBlock.HeaderCheckSum;
+ {Is the footer itself still in place}
+ LFooterValid := LHeaderValid
+ and (PNativeUInt(PByte(APBlock) + SizeOf(TFullDebugBlockHeader) + APBlock.UserSize)^ = (not LHeaderCheckSum));
+ {Is the footer and debug VMT in place? The debug VMT is only valid if the user size is greater than the size of a pointer.}
+ if LFooterValid
+ and (APBlock.UserSize < SizeOf(Pointer)) or (PNativeUInt(PByte(APBlock) + SizeOf(TFullDebugBlockHeader))^ = NativeUInt(@FreedObjectVMT.VMTMethods[0])) then
+ begin
+ {Store the debug fill pattern in place of the footer in order to simplify
+ checking for block modifications.}
+ PNativeUInt(PByte(APBlock) + SizeOf(TFullDebugBlockHeader) + APBlock.UserSize)^ :=
+ {$ifndef CatchUseOfFreedInterfaces}
+ DebugFillPattern;
+ {$else}
+ RotateRight(NativeUInt(@VMTBadInterface), (APBlock.UserSize and (SizeOf(Pointer) - 1)) * 8);
+ {$endif}
+ {Check that all the filler bytes are valid inside the block, except for
+ the "dummy" class header}
+ LBlockUnmodified := CheckFillPattern(PNativeUInt(PByte(APBlock) + (SizeOf(TFullDebugBlockHeader) + SizeOf(Pointer))),
+ ABlockSize - (FullDebugBlockOverhead + SizeOf(Pointer)),
+ {$ifndef CatchUseOfFreedInterfaces}DebugFillPattern{$else}NativeUInt(@VMTBadInterface){$endif});
+ {Reset the old footer}
+ PNativeUInt(PByte(APBlock) + SizeOf(TFullDebugBlockHeader) + APBlock.UserSize)^ := not LHeaderCheckSum;
+ end
+ else
+ LBlockUnmodified := False;
+ if (not LHeaderValid) or (not LFooterValid) or (not LBlockUnmodified) then
+ begin
+ LogBlockError(APBlock, AOperation, LHeaderValid, LFooterValid);
+ Result := False;
+ end
+ else
+ Result := True;
+end;
+
+function DebugGetMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
+begin
+ {Scan the entire memory pool first?}
+ if FullDebugModeScanMemoryPoolBeforeEveryOperation then
+ ScanMemoryPoolForCorruptions;
+ {Enter the memory manager: block scans may not be performed now}
+ StartChangingFullDebugModeBlock;
+ try
+ {We need extra space for (a) The debug header, (b) the block debug trailer
+ and (c) the trailing block size pointer for free blocks}
+ Result := FastGetMem(ASize + FullDebugBlockOverhead);
+ if Result <> nil then
+ begin
+ {Large blocks are always newly allocated (and never reused), so checking
+ for a modify-after-free is not necessary.}
+ if (ASize > (MaximumMediumBlockSize - BlockHeaderSize - FullDebugBlockOverhead))
+ or CheckFreeBlockUnmodified(Result, GetAvailableSpaceInBlock(Result) + BlockHeaderSize, boGetMem) then
+ begin
+ {Set the allocation call stack}
+ GetStackTrace(@PFullDebugBlockHeader(Result).AllocationStackTrace, StackTraceDepth, 1);
+ {Set the thread ID of the thread that allocated the block}
+ PFullDebugBlockHeader(Result).AllocatedByThread := GetThreadID;
+ {Block is now in use: It was allocated by this routine}
+ PFullDebugBlockHeader(Result).AllocatedByRoutine := @DebugGetMem;
+ {Set the group number}
+ PFullDebugBlockHeader(Result).AllocationGroup := AllocationGroupStack[AllocationGroupStackTop];
+ {Set the allocation number}
+ IncrementAllocationNumber;
+ PFullDebugBlockHeader(Result).AllocationNumber := CurrentAllocationNumber;
+ {Clear the previous block trailer}
+ PNativeUInt(PByte(Result) + SizeOf(TFullDebugBlockHeader) + PFullDebugBlockHeader(Result).UserSize)^ :=
+ {$ifndef CatchUseOfFreedInterfaces}
+ DebugFillPattern;
+ {$else}
+ RotateRight(NativeUInt(@VMTBadInterface), (PFullDebugBlockHeader(Result).UserSize and (SizeOf(Pointer) - 1)) * 8);
+ {$endif}
+ {Set the user size for the block}
+ PFullDebugBlockHeader(Result).UserSize := ASize;
+ {Set the checksums}
+ UpdateHeaderAndFooterCheckSums(Result);
+ {$ifdef FullDebugModeCallBacks}
+ if Assigned(OnDebugGetMemFinish) then
+ OnDebugGetMemFinish(PFullDebugBlockHeader(Result), ASize);
+ {$endif}
+ {Return the start of the actual block}
+ Result := Pointer(PByte(Result) + SizeOf(TFullDebugBlockHeader));
+{$ifdef EnableMemoryLeakReporting}
+ {Should this block be marked as an expected leak automatically?}
+ if FullDebugModeRegisterAllAllocsAsExpectedMemoryLeak then
+ RegisterExpectedMemoryLeak(Result);
+{$endif}
+ end
+ else
+ begin
+ Result := nil;
+ end;
+ end;
+ finally
+ {Leaving the memory manager routine: Block scans may be performed again.}
+ DoneChangingFullDebugModeBlock;
+ end;
+end;
+
+function CheckBlockBeforeFreeOrRealloc(APBlock: PFullDebugBlockHeader;
+ AOperation: TBlockOperation): Boolean;
+var
+ LHeaderValid, LFooterValid: Boolean;
+ LPFooter: PNativeUInt;
+{$ifndef CatchUseOfFreedInterfaces}
+ LBlockSize: NativeUInt;
+ LPTrailingByte, LPFillPatternEnd: PByte;
+{$endif}
+begin
+ {Is the checksum for the block header valid?}
+ LHeaderValid := CalculateHeaderCheckSum(APBlock) = APBlock.HeaderCheckSum;
+ {If the header is corrupted then the footer is assumed to be corrupt too.}
+ if LHeaderValid then
+ begin
+ {Check the footer checksum: The footer checksum should equal the header
+ checksum with all bits inverted.}
+ LPFooter := PNativeUInt(PByte(APBlock) + SizeOf(TFullDebugBlockHeader) + PFullDebugBlockHeader(APBlock).UserSize);
+ if APBlock.HeaderCheckSum = (not (LPFooter^)) then
+ begin
+ LFooterValid := True;
+{$ifndef CatchUseOfFreedInterfaces}
+ {Large blocks do not have the debug fill pattern, since they are never reused.}
+ if PNativeUInt(PByte(APBlock) - BlockHeaderSize)^ and (IsMediumBlockFlag or IsLargeBlockFlag) <> IsLargeBlockFlag then
+ begin
+ {Check that the application has not modified bytes beyond the block
+ footer. The $80 fill pattern should extend up to 2 nativeints before
+ the start of the next block (leaving space for the free block size and
+ next block header.)}
+ LBlockSize := GetAvailableSpaceInBlock(APBlock);
+ LPFillPatternEnd := PByte(PByte(APBlock) + LBlockSize - SizeOf(Pointer));
+ LPTrailingByte := PByte(PByte(LPFooter) + SizeOf(NativeUInt));
+ while UIntPtr(LPTrailingByte) < UIntPtr(LPFillPatternEnd) do
+ begin
+ if Byte(LPTrailingByte^) <> DebugFillByte then
+ begin
+ LFooterValid := False;
+ Break;
+ end;
+ Inc(LPTrailingByte);
+ end;
+ end;
+{$endif}
+ end
+ else
+ LFooterValid := False;
+ end
+ else
+ LFooterValid := False;
+ {The header and footer must be intact and the block must have been allocated
+ by this memory manager instance.}
+ if LFooterValid and (APBlock.AllocatedByRoutine = @DebugGetMem) then
+ begin
+ Result := True;
+ end
+ else
+ begin
+ {Log the error}
+ LogBlockError(APBlock, AOperation, LHeaderValid, LFooterValid);
+ {Return an error}
+ Result := False;
+ end;
+end;
+
+function DebugFreeMem(APointer: Pointer): Integer;
+var
+ LActualBlock: PFullDebugBlockHeader;
+ LBlockHeader: NativeUInt;
+begin
+ {Scan the entire memory pool first?}
+ if FullDebugModeScanMemoryPoolBeforeEveryOperation then
+ ScanMemoryPoolForCorruptions;
+ {Get a pointer to the start of the actual block}
+ LActualBlock := PFullDebugBlockHeader(PByte(APointer)
+ - SizeOf(TFullDebugBlockHeader));
+ {Is the debug info surrounding the block valid?}
+ if CheckBlockBeforeFreeOrRealloc(LActualBlock, boFreeMem) then
+ begin
+ {Enter the memory manager: block scans may not be performed now}
+ StartChangingFullDebugModeBlock;
+ try
+ {$ifdef FullDebugModeCallBacks}
+ if Assigned(OnDebugFreeMemStart) then
+ OnDebugFreeMemStart(LActualBlock);
+ {$endif}
+ {Large blocks are never reused, so there is no point in updating their
+ headers and fill pattern.}
+ LBlockHeader := PNativeUInt(PByte(LActualBlock) - BlockHeaderSize)^;
+ if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag or IsLargeBlockFlag) <> IsLargeBlockFlag then
+ begin
+ {Get the class the block was used for}
+ LActualBlock.PreviouslyUsedByClass := PNativeUInt(APointer)^;
+ {Set the free call stack}
+ GetStackTrace(@LActualBlock.FreeStackTrace, StackTraceDepth, 1);
+ {Set the thread ID of the thread that freed the block}
+ LActualBlock.FreedByThread := GetThreadID;
+ {Block is now free}
+ LActualBlock.AllocatedByRoutine := nil;
+ {Clear the user area of the block}
+ DebugFillMem(APointer^, LActualBlock.UserSize,
+ {$ifndef CatchUseOfFreedInterfaces}DebugFillPattern{$else}NativeUInt(@VMTBadInterface){$endif});
+ {Set a pointer to the dummy VMT}
+ PNativeUInt(APointer)^ := NativeUInt(@FreedObjectVMT.VMTMethods[0]);
+ {Recalculate the checksums}
+ UpdateHeaderAndFooterCheckSums(LActualBlock);
+ end;
+{$ifdef EnableMemoryLeakReporting}
+ {Automatically deregister the expected memory leak?}
+ if FullDebugModeRegisterAllAllocsAsExpectedMemoryLeak then
+ UnregisterExpectedMemoryLeak(APointer);
+{$endif}
+ {Free the actual block}
+ Result := FastFreeMem(LActualBlock);
+ {$ifdef FullDebugModeCallBacks}
+ if Assigned(OnDebugFreeMemFinish) then
+ OnDebugFreeMemFinish(LActualBlock, Result);
+ {$endif}
+ finally
+ {Leaving the memory manager routine: Block scans may be performed again.}
+ DoneChangingFullDebugModeBlock;
+ end;
+ end
+ else
+ begin
+{$ifdef SuppressFreeMemErrorsInsideException}
+ if {$ifdef BDS2006AndUp}ExceptObject{$else}RaiseList{$endif} <> nil then
+ Result := 0
+ else
+{$endif}
+ Result := -1;
+ end;
+end;
+
+function DebugReallocMem(APointer: Pointer; ANewSize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
+var
+ LMoveSize, LBlockSpace: NativeUInt;
+ LActualBlock, LNewActualBlock: PFullDebugBlockHeader;
+begin
+ {Scan the entire memory pool first?}
+ if FullDebugModeScanMemoryPoolBeforeEveryOperation then
+ ScanMemoryPoolForCorruptions;
+ {Get a pointer to the start of the actual block}
+ LActualBlock := PFullDebugBlockHeader(PByte(APointer)
+ - SizeOf(TFullDebugBlockHeader));
+ {Is the debug info surrounding the block valid?}
+ if CheckBlockBeforeFreeOrRealloc(LActualBlock, boReallocMem) then
+ begin
+ {Get the current block size}
+ LBlockSpace := GetAvailableSpaceInBlock(LActualBlock);
+ {Can the block fit? We need space for the debug overhead and the block header
+ of the next block}
+ if LBlockSpace < (NativeUInt(ANewSize) + FullDebugBlockOverhead) then
+ begin
+ {Get a new block of the requested size.}
+ Result := DebugGetMem(ANewSize);
+ if Result <> nil then
+ begin
+ {Block scans may not be performed now}
+ StartChangingFullDebugModeBlock;
+ try
+ {$ifdef FullDebugModeCallBacks}
+ if Assigned(OnDebugReallocMemStart) then
+ OnDebugReallocMemStart(LActualBlock, ANewSize);
+ {$endif}
+ {We reuse the old allocation number. Since DebugGetMem always bumps
+ CurrentAllocationGroup, there may be gaps in the sequence of
+ allocation numbers.}
+ LNewActualBlock := PFullDebugBlockHeader(PByte(Result)
+ - SizeOf(TFullDebugBlockHeader));
+ LNewActualBlock.AllocationGroup := LActualBlock.AllocationGroup;
+ LNewActualBlock.AllocationNumber := LActualBlock.AllocationNumber;
+ {Recalculate the header and footer checksums}
+ UpdateHeaderAndFooterCheckSums(LNewActualBlock);
+ {$ifdef FullDebugModeCallBacks}
+ if Assigned(OnDebugReallocMemFinish) then
+ OnDebugReallocMemFinish(LNewActualBlock, ANewSize);
+ {$endif}
+ finally
+ {Block scans can again be performed safely}
+ DoneChangingFullDebugModeBlock;
+ end;
+ {How many bytes to move?}
+ LMoveSize := LActualBlock.UserSize;
+ if LMoveSize > NativeUInt(ANewSize) then
+ LMoveSize := ANewSize;
+ {Move the data across}
+ System.Move(APointer^, Result^, LMoveSize);
+ {Free the old block}
+ DebugFreeMem(APointer);
+ end
+ else
+ begin
+ Result := nil;
+ end;
+ end
+ else
+ begin
+ {Block scans may not be performed now}
+ StartChangingFullDebugModeBlock;
+ try
+ {$ifdef FullDebugModeCallBacks}
+ if Assigned(OnDebugReallocMemStart) then
+ OnDebugReallocMemStart(LActualBlock, ANewSize);
+ {$endif}
+ {Clear all data after the new end of the block up to the old end of the
+ block, including the trailer.}
+ DebugFillMem(Pointer(PByte(APointer) + NativeUInt(ANewSize) + SizeOf(NativeUInt))^,
+ NativeInt(LActualBlock.UserSize) - ANewSize,
+{$ifndef CatchUseOfFreedInterfaces}
+ DebugFillPattern);
+{$else}
+ RotateRight(NativeUInt(@VMTBadInterface), (ANewSize and (SizeOf(Pointer) - 1)) * 8));
+{$endif}
+ {Update the user size}
+ LActualBlock.UserSize := ANewSize;
+ {Set the new checksums}
+ UpdateHeaderAndFooterCheckSums(LActualBlock);
+ {$ifdef FullDebugModeCallBacks}
+ if Assigned(OnDebugReallocMemFinish) then
+ OnDebugReallocMemFinish(LActualBlock, ANewSize);
+ {$endif}
+ finally
+ {Block scans can again be performed safely}
+ DoneChangingFullDebugModeBlock;
+ end;
+ {Return the old pointer}
+ Result := APointer;
+ end;
+ end
+ else
+ begin
+ Result := nil;
+ end;
+end;
+
+{Allocates a block and fills it with zeroes}
+function DebugAllocMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Cardinal{$endif}): Pointer;
+begin
+ Result := DebugGetMem(ASize);
+ {Clear the block}
+ if Result <> nil then
+ FillChar(Result^, ASize, 0);
+end;
+
+{Raises a runtime error if a memory corruption was encountered. Subroutine for
+ InternalScanMemoryPool and InternalScanSmallBlockPool.}
+procedure RaiseMemoryCorruptionError;
+begin
+ {Disable exhaustive checking in order to prevent recursive exceptions.}
+ FullDebugModeScanMemoryPoolBeforeEveryOperation := False;
+ {Unblock the memory manager in case the creation of the exception below
+ causes an attempt to be made to allocate memory.}
+ UnblockFullDebugModeMMRoutines;
+ {Raise the runtime error}
+{$ifdef BCB6OrDelphi7AndUp}
+ System.Error(reOutOfMemory);
+{$else}
+ System.RunError(reOutOfMemory);
+{$endif}
+end;
+
+{Subroutine for InternalScanMemoryPool: Checks the given small block pool for
+ allocated blocks}
+procedure InternalScanSmallBlockPool(APSmallBlockPool: PSmallBlockPoolHeader;
+ AFirstAllocationGroupToLog, ALastAllocationGroupToLog: Cardinal);
+var
+ LCurPtr, LEndPtr: Pointer;
+begin
+ {Get the first and last pointer for the pool}
+ GetFirstAndLastSmallBlockInPool(APSmallBlockPool, LCurPtr, LEndPtr);
+ {Step through all blocks}
+ while UIntPtr(LCurPtr) <= UIntPtr(LEndPtr) do
+ begin
+ {Is this block in use? If so, is the debug info intact?}
+ if ((PNativeUInt(PByte(LCurPtr) - BlockHeaderSize)^ and IsFreeBlockFlag) = 0) then
+ begin
+ if CheckBlockBeforeFreeOrRealloc(LCurPtr, boBlockCheck) then
+ begin
+ if (PFullDebugBlockHeader(LCurPtr).AllocationGroup >= AFirstAllocationGroupToLog)
+ and (PFullDebugBlockHeader(LCurPtr).AllocationGroup <= ALastAllocationGroupToLog) then
+ begin
+ LogMemoryLeakOrAllocatedBlock(LCurPtr, False);
+ end;
+ end
+ else
+ RaiseMemoryCorruptionError;
+ end
+ else
+ begin
+ {Check that the block has not been modified since being freed}
+ if not CheckFreeBlockUnmodified(LCurPtr, APSmallBlockPool.BlockType.BlockSize, boBlockCheck) then
+ RaiseMemoryCorruptionError;
+ end;
+ {Next block}
+ Inc(PByte(LCurPtr), APSmallBlockPool.BlockType.BlockSize);
+ end;
+end;
+
+{Subroutine for LogAllocatedBlocksToFile and ScanMemoryPoolForCorruptions:
+ Scans the memory pool for corruptions and optionally logs allocated blocks
+ in the allocation group range.}
+procedure InternalScanMemoryPool(AFirstAllocationGroupToLog, ALastAllocationGroupToLog: Cardinal);
+var
+ LPLargeBlock: PLargeBlockHeader;
+ LPMediumBlock: Pointer;
+ LPMediumBlockPoolHeader: PMediumBlockPoolHeader;
+ LMediumBlockHeader: NativeUInt;
+begin
+ {Block all the memory manager routines while performing the scan. No memory
+ block may be allocated or freed, and no FullDebugMode block header or
+ footer may be modified, while the scan is in progress.}
+ BlockFullDebugModeMMRoutines;
+ try
+ {Step through all the medium block pools}
+ LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
+ while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
+ begin
+ LPMediumBlock := GetFirstMediumBlockInPool(LPMediumBlockPoolHeader);
+ while LPMediumBlock <> nil do
+ begin
+ LMediumBlockHeader := PNativeUInt(PByte(LPMediumBlock) - BlockHeaderSize)^;
+ {Is the block in use?}
+ if LMediumBlockHeader and IsFreeBlockFlag = 0 then
+ begin
+ {Block is in use: Is it a medium block or small block pool?}
+ if (LMediumBlockHeader and IsSmallBlockPoolInUseFlag) <> 0 then
+ begin
+ {Get all the leaks for the small block pool}
+ InternalScanSmallBlockPool(LPMediumBlock, AFirstAllocationGroupToLog, ALastAllocationGroupToLog);
+ end
+ else
+ begin
+ if CheckBlockBeforeFreeOrRealloc(LPMediumBlock, boBlockCheck) then
+ begin
+ if (PFullDebugBlockHeader(LPMediumBlock).AllocationGroup >= AFirstAllocationGroupToLog)
+ and (PFullDebugBlockHeader(LPMediumBlock).AllocationGroup <= ALastAllocationGroupToLog) then
+ begin
+ LogMemoryLeakOrAllocatedBlock(LPMediumBlock, False);
+ end;
+ end
+ else
+ RaiseMemoryCorruptionError;
+ end;
+ end
+ else
+ begin
+ {Check that the block has not been modified since being freed}
+ if not CheckFreeBlockUnmodified(LPMediumBlock, LMediumBlockHeader and DropMediumAndLargeFlagsMask, boBlockCheck) then
+ RaiseMemoryCorruptionError;
+ end;
+ {Next medium block}
+ LPMediumBlock := NextMediumBlock(LPMediumBlock);
+ end;
+ {Get the next medium block pool}
+ LPMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
+ end;
+ {Scan large blocks}
+ LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
+ while LPLargeBlock <> @LargeBlocksCircularList do
+ begin
+ if CheckBlockBeforeFreeOrRealloc(Pointer(PByte(LPLargeBlock) + LargeBlockHeaderSize), boBlockCheck) then
+ begin
+ if (PFullDebugBlockHeader(PByte(LPLargeBlock) + LargeBlockHeaderSize).AllocationGroup >= AFirstAllocationGroupToLog)
+ and (PFullDebugBlockHeader(PByte(LPLargeBlock) + LargeBlockHeaderSize).AllocationGroup <= ALastAllocationGroupToLog) then
+ begin
+ LogMemoryLeakOrAllocatedBlock(Pointer(PByte(LPLargeBlock) + LargeBlockHeaderSize), False);
+ end;
+ end
+ else
+ RaiseMemoryCorruptionError;
+ {Get the next large block}
+ LPLargeBlock := LPLargeBlock.NextLargeBlockHeader;
+ end;
+ finally
+ {Unblock the FullDebugMode memory manager routines.}
+ UnblockFullDebugModeMMRoutines;
+ end;
+end;
+
+{Logs detail about currently allocated memory blocks for the specified range of
+ allocation groups. if ALastAllocationGroupToLog is less than
+ AFirstAllocationGroupToLog or it is zero, then all allocation groups are
+ logged. This routine also checks the memory pool for consistency at the same
+ time, raising an "Out of Memory" error if the check fails.}
+procedure LogAllocatedBlocksToFile(AFirstAllocationGroupToLog, ALastAllocationGroupToLog: Cardinal);
+begin
+ {Validate input}
+ if (ALastAllocationGroupToLog = 0) or (ALastAllocationGroupToLog < AFirstAllocationGroupToLog) then
+ begin
+ {Bad input: log all groups}
+ AFirstAllocationGroupToLog := 0;
+ ALastAllocationGroupToLog := $ffffffff;
+ end;
+ {Scan the memory pool, logging allocated blocks in the requested range.}
+ InternalScanMemoryPool(AFirstAllocationGroupToLog, ALastAllocationGroupToLog);
+end;
+
+{Scans the memory pool for any corruptions. If a corruption is encountered an "Out of Memory" exception is
+ raised.}
+procedure ScanMemoryPoolForCorruptions;
+begin
+ {Scan the memory pool for corruptions, but don't log any allocated blocks}
+ InternalScanMemoryPool($ffffffff, 0);
+end;
+
+{-----------------------Invalid Virtual Method Calls-------------------------}
+
+{ TFreedObject }
+
+{Used to determine the index of the virtual method call on the freed object.
+ Do not change this without updating MaxFakeVMTEntries. Currently 200.}
+procedure TFreedObject.GetVirtualMethodIndex;
+asm
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+ Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
+
+ jmp TFreedObject.VirtualMethodError
+end;
+
+procedure TFreedObject.VirtualMethodError;
+var
+ LVMOffset: Integer;
+ LMsgPtr: PAnsiChar;
+ LErrorMessage: array[0..32767] of AnsiChar;
+{$ifndef NoMessageBoxes}
+ LErrorMessageTitle: array[0..1023] of AnsiChar;
+{$endif}
+ LClass: TClass;
+ LActualBlock: PFullDebugBlockHeader;
+begin
+ {Get the offset of the virtual method}
+ LVMOffset := (MaxFakeVMTEntries - VMIndex) * SizeOf(Pointer) + vmtParent + SizeOf(Pointer);
+ {Reset the index for the next error}
+ VMIndex := 0;
+ {Get the address of the actual block}
+ LActualBlock := PFullDebugBlockHeader(PByte(Self) - SizeOf(TFullDebugBlockHeader));
+ {Display the error header}
+ LMsgPtr := AppendStringToBuffer(VirtualMethodErrorHeader, @LErrorMessage[0], Length(VirtualMethodErrorHeader));
+ {Is the debug info surrounding the block valid?}
+ if CalculateHeaderCheckSum(LActualBlock) = LActualBlock.HeaderCheckSum then
+ begin
+ {Get the class this block was used for previously}
+ LClass := DetectClassInstance(@LActualBlock.PreviouslyUsedByClass);
+ if (LClass <> nil) and (IntPtr(LClass) <> IntPtr(@FreedObjectVMT.VMTMethods[0])) then
+ begin
+ LMsgPtr := AppendStringToBuffer(FreedObjectClassMsg, LMsgPtr, Length(FreedObjectClassMsg));
+ LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
+ end;
+ {Get the virtual method name}
+ LMsgPtr := AppendStringToBuffer(VirtualMethodName, LMsgPtr, Length(VirtualMethodName));
+ if LVMOffset < 0 then
+ begin
+ LMsgPtr := AppendStringToBuffer(StandardVirtualMethodNames[LVMOffset div SizeOf(Pointer)], LMsgPtr, Length(StandardVirtualMethodNames[LVMOffset div SizeOf(Pointer)]));
+ end
+ else
+ begin
+ LMsgPtr := AppendStringToBuffer(VirtualMethodOffset, LMsgPtr, Length(VirtualMethodOffset));
+ LMsgPtr := NativeUIntToStrBuf(LVMOffset, LMsgPtr);
+ end;
+ {Virtual method address}
+ if (LClass <> nil) and (IntPtr(LClass) <> IntPtr(@FreedObjectVMT.VMTMethods[0])) then
+ begin
+ LMsgPtr := AppendStringToBuffer(VirtualMethodAddress, LMsgPtr, Length(VirtualMethodAddress));
+ LMsgPtr := NativeUIntToHexBuf(PNativeUInt(PByte(LClass) + LVMOffset)^, LMsgPtr);
+ end;
+ {Log the allocation group}
+ if LActualBlock.AllocationGroup > 0 then
+ begin
+ LMsgPtr := AppendStringToBuffer(PreviousAllocationGroupMsg, LMsgPtr, Length(PreviousAllocationGroupMsg));
+ LMsgPtr := NativeUIntToStrBuf(LActualBlock.AllocationGroup, LMsgPtr);
+ end;
+ {Log the allocation number}
+ LMsgPtr := AppendStringToBuffer(PreviousAllocationNumberMsg, LMsgPtr, Length(PreviousAllocationNumberMsg));
+ LMsgPtr := NativeUIntToStrBuf(LActualBlock.AllocationNumber, LMsgPtr);
+ {The header is still intact - display info about the this/previous allocation}
+ if LActualBlock.AllocationStackTrace[0] <> 0 then
+ begin
+ LMsgPtr := AppendStringToBuffer(ThreadIDAtObjectAllocMsg, LMsgPtr, Length(ThreadIDAtObjectAllocMsg));
+ LMsgPtr := NativeUIntToHexBuf(LActualBlock.AllocatedByThread, LMsgPtr);
+ LMsgPtr := AppendStringToBuffer(StackTraceMsg, LMsgPtr, Length(StackTraceMsg));
+ LMsgPtr := LogStackTrace(@LActualBlock.AllocationStackTrace, StackTraceDepth, LMsgPtr);
+ end;
+ {Get the call stack for the previous free}
+ if LActualBlock.FreeStackTrace[0] <> 0 then
+ begin
+ LMsgPtr := AppendStringToBuffer(ThreadIDAtObjectFreeMsg, LMsgPtr, Length(ThreadIDAtObjectFreeMsg));
+ LMsgPtr := NativeUIntToHexBuf(LActualBlock.FreedByThread, LMsgPtr);
+ LMsgPtr := AppendStringToBuffer(StackTraceMsg, LMsgPtr, Length(StackTraceMsg));
+ LMsgPtr := LogStackTrace(@LActualBlock.FreeStackTrace, StackTraceDepth, LMsgPtr);
+ end;
+ end
+ else
+ begin
+ {Header has been corrupted}
+ LMsgPtr := AppendStringToBuffer(BlockHeaderCorruptedNoHistoryMsg, LMsgPtr, Length(BlockHeaderCorruptedNoHistoryMsg));
+ end;
+ {Add the current stack trace}
+ LMsgPtr := LogCurrentThreadAndStackTrace(2, LMsgPtr);
+{$ifndef DisableLoggingOfMemoryDumps}
+ {Add the pointer address}
+ LMsgPtr := LogMemoryDump(LActualBlock, LMsgPtr);
+{$endif}
+ {Trailing CRLF}
+ LMsgPtr^ := #13;
+ Inc(LMsgPtr);
+ LMsgPtr^ := #10;
+ Inc(LMsgPtr);
+ {Trailing #0}
+ LMsgPtr^ := #0;
+{$ifdef LogErrorsToFile}
+ {Log the error}
+ AppendEventLog(@LErrorMessage[0], NativeUInt(LMsgPtr) - NativeUInt(@LErrorMessage[0]));
+{$endif}
+{$ifdef UseOutputDebugString}
+ OutputDebugStringA(LErrorMessage);
+{$endif}
+{$ifndef NoMessageBoxes}
+ {Show the message}
+ AppendStringToModuleName(BlockErrorMsgTitle, LErrorMessageTitle);
+ ShowMessageBox(LErrorMessage, LErrorMessageTitle);
+{$endif}
+ {Raise an access violation}
+ RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, nil);
+end;
+
+{$ifdef CatchUseOfFreedInterfaces}
+procedure TFreedObject.InterfaceError;
+var
+ LMsgPtr: PAnsiChar;
+{$ifndef NoMessageBoxes}
+ LErrorMessageTitle: array[0..1023] of AnsiChar;
+{$endif}
+ LErrorMessage: array[0..4000] of AnsiChar;
+begin
+ {Display the error header}
+ LMsgPtr := AppendStringToBuffer(InterfaceErrorHeader, @LErrorMessage[0], Length(InterfaceErrorHeader));
+ {Add the current stack trace}
+ LMsgPtr := LogCurrentThreadAndStackTrace(2, LMsgPtr);
+ {Trailing CRLF}
+ LMsgPtr^ := #13;
+ Inc(LMsgPtr);
+ LMsgPtr^ := #10;
+ Inc(LMsgPtr);
+ {Trailing #0}
+ LMsgPtr^ := #0;
+{$ifdef LogErrorsToFile}
+ {Log the error}
+ AppendEventLog(@LErrorMessage[0], NativeUInt(LMsgPtr) - NativeUInt(@LErrorMessage[0]));
+{$endif}
+{$ifdef UseOutputDebugString}
+ OutputDebugStringA(LErrorMessage);
+{$endif}
+{$ifndef NoMessageBoxes}
+ {Show the message}
+ AppendStringToModuleName(BlockErrorMsgTitle, LErrorMessageTitle);
+ ShowMessageBox(LErrorMessage, LErrorMessageTitle);
+{$endif}
+ {Raise an access violation}
+ RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, nil);
+end;
+{$endif}
+
+{$endif}
+
+{----------------------------Memory Leak Checking-----------------------------}
+
+{$ifdef EnableMemoryLeakReporting}
+
+{Adds a leak to the specified list}
+function UpdateExpectedLeakList(APLeakList: PPExpectedMemoryLeak;
+ APNewEntry: PExpectedMemoryLeak; AExactSizeMatch: Boolean = True): Boolean;
+var
+ LPInsertAfter, LPNewEntry: PExpectedMemoryLeak;
+begin
+ {Default to error}
+ Result := False;
+ {Find the insertion spot}
+ LPInsertAfter := APLeakList^;
+ while LPInsertAfter <> nil do
+ begin
+ {Too big?}
+ if LPInsertAfter.LeakSize > APNewEntry.LeakSize then
+ begin
+ LPInsertAfter := LPInsertAfter.PreviousLeak;
+ Break;
+ end;
+ {Find a matching entry. If an exact size match is not required and the leak
+ is larger than the current entry, use it if the expected size of the next
+ entry is too large.}
+ if (IntPtr(LPInsertAfter.LeakAddress) = IntPtr(APNewEntry.LeakAddress))
+ and ((IntPtr(LPInsertAfter.LeakedClass) = IntPtr(APNewEntry.LeakedClass))
+ {$ifdef CheckCppObjectTypeEnabled}
+ or (LPInsertAfter.LeakedCppTypeIdPtr = APNewEntry.LeakedCppTypeIdPtr)
+ {$endif}
+ )
+ and ((LPInsertAfter.LeakSize = APNewEntry.LeakSize)
+ or ((not AExactSizeMatch)
+ and (LPInsertAfter.LeakSize < APNewEntry.LeakSize)
+ and ((LPInsertAfter.NextLeak = nil)
+ or (LPInsertAfter.NextLeak.LeakSize > APNewEntry.LeakSize))
+ )) then
+ begin
+ if (LPInsertAfter.LeakCount + APNewEntry.LeakCount) >= 0 then
+ begin
+ Inc(LPInsertAfter.LeakCount, APNewEntry.LeakCount);
+ {Is the count now 0?}
+ if LPInsertAfter.LeakCount = 0 then
+ begin
+ {Delete the entry}
+ if LPInsertAfter.NextLeak <> nil then
+ LPInsertAfter.NextLeak.PreviousLeak := LPInsertAfter.PreviousLeak;
+ if LPInsertAfter.PreviousLeak <> nil then
+ LPInsertAfter.PreviousLeak.NextLeak := LPInsertAfter.NextLeak
+ else
+ APLeakList^ := LPInsertAfter.NextLeak;
+ {Insert it as the first free slot}
+ LPInsertAfter.NextLeak := ExpectedMemoryLeaks.FirstFreeSlot;
+ ExpectedMemoryLeaks.FirstFreeSlot := LPInsertAfter;
+ end;
+ Result := True;
+ end;
+ Exit;
+ end;
+ {Next entry}
+ if LPInsertAfter.NextLeak <> nil then
+ LPInsertAfter := LPInsertAfter.NextLeak
+ else
+ Break;
+ end;
+ if APNewEntry.LeakCount > 0 then
+ begin
+ {Get a position for the entry}
+ LPNewEntry := ExpectedMemoryLeaks.FirstFreeSlot;
+ if LPNewEntry <> nil then
+ begin
+ ExpectedMemoryLeaks.FirstFreeSlot := LPNewEntry.NextLeak;
+ end
+ else
+ begin
+ if ExpectedMemoryLeaks.EntriesUsed < Length(ExpectedMemoryLeaks.ExpectedLeaks) then
+ begin
+ LPNewEntry := @ExpectedMemoryLeaks.ExpectedLeaks[ExpectedMemoryLeaks.EntriesUsed];
+ Inc(ExpectedMemoryLeaks.EntriesUsed);
+ end
+ else
+ begin
+ {No more space}
+ Exit;
+ end;
+ end;
+ {Set the entry}
+ LPNewEntry^ := APNewEntry^;
+ {Insert it into the list}
+ LPNewEntry.PreviousLeak := LPInsertAfter;
+ if LPInsertAfter <> nil then
+ begin
+ LPNewEntry.NextLeak := LPInsertAfter.NextLeak;
+ if LPNewEntry.NextLeak <> nil then
+ LPNewEntry.NextLeak.PreviousLeak := LPNewEntry;
+ LPInsertAfter.NextLeak := LPNewEntry;
+ end
+ else
+ begin
+ LPNewEntry.NextLeak := APLeakList^;
+ if LPNewEntry.NextLeak <> nil then
+ LPNewEntry.NextLeak.PreviousLeak := LPNewEntry;
+ APLeakList^ := LPNewEntry;
+ end;
+ Result := True;
+ end;
+end;
+
+{Locks the expected leaks. Returns false if the list could not be allocated.}
+function LockExpectedMemoryLeaksList: Boolean;
+begin
+ {Lock the expected leaks list}
+{$ifndef AssumeMultiThreaded}
+ if IsMultiThread then
+{$endif}
+ begin
+ while LockCmpxchg(0, 1, @ExpectedMemoryLeaksListLocked) <> 0 do
+ begin
+{$ifdef NeverSleepOnThreadContention}
+ {$ifdef UseSwitchToThread}
+ SwitchToThread;
+ {$endif}
+{$else}
+ Sleep(InitialSleepTime);
+ if LockCmpxchg(0, 1, @ExpectedMemoryLeaksListLocked) = 0 then
+ Break;
+ Sleep(AdditionalSleepTime);
+{$endif}
+ end;
+ end;
+ {Allocate the list if it does not exist}
+ if ExpectedMemoryLeaks = nil then
+ ExpectedMemoryLeaks := VirtualAlloc(nil, ExpectedMemoryLeaksListSize, MEM_COMMIT, PAGE_READWRITE);
+ {Done}
+ Result := ExpectedMemoryLeaks <> nil;
+end;
+
+{Registers expected memory leaks. Returns true on success. The list of leaked
+ blocks is limited, so failure is possible if the list is full.}
+function RegisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean; overload;
+var
+ LNewEntry: TExpectedMemoryLeak;
+begin
+ {Fill out the structure}
+{$ifndef FullDebugMode}
+ LNewEntry.LeakAddress := ALeakedPointer;
+{$else}
+ LNewEntry.LeakAddress := Pointer(PByte(ALeakedPointer) - SizeOf(TFullDebugBlockHeader));
+{$endif}
+ LNewEntry.LeakedClass := nil;
+ {$ifdef CheckCppObjectTypeEnabled}
+ LNewEntry.LeakedCppTypeIdPtr := nil;
+ {$endif}
+ LNewEntry.LeakSize := 0;
+ LNewEntry.LeakCount := 1;
+ {Add it to the correct list}
+ Result := LockExpectedMemoryLeaksList
+ and UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByAddress, @LNewEntry);
+ ExpectedMemoryLeaksListLocked := False;
+end;
+
+function RegisterExpectedMemoryLeak(ALeakedObjectClass: TClass; ACount: Integer = 1): Boolean; overload;
+var
+ LNewEntry: TExpectedMemoryLeak;
+begin
+ {Fill out the structure}
+ LNewEntry.LeakAddress := nil;
+ LNewEntry.LeakedClass := ALeakedObjectClass;
+ {$ifdef CheckCppObjectTypeEnabled}
+ LNewEntry.LeakedCppTypeIdPtr := nil;
+ {$endif}
+ LNewEntry.LeakSize := ALeakedObjectClass.InstanceSize;
+ LNewEntry.LeakCount := ACount;
+ {Add it to the correct list}
+ Result := LockExpectedMemoryLeaksList
+ and UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByClass, @LNewEntry);
+ ExpectedMemoryLeaksListLocked := False;
+end;
+
+{$ifdef CheckCppObjectTypeEnabled}
+function RegisterExpectedMemoryLeak(ALeakedCppVirtObjTypeIdPtr: Pointer; ACount: Integer): Boolean; overload;
+var
+ LNewEntry: TExpectedMemoryLeak;
+begin
+ {Fill out the structure}
+ if Assigned(GetCppVirtObjSizeByTypeIdPtrFunc) then
+ begin
+ //Return 0 if not a proper type
+ LNewEntry.LeakSize := GetCppVirtObjSizeByTypeIdPtrFunc(ALeakedCppVirtObjTypeIdPtr);
+ if LNewEntry.LeakSize > 0 then
+ begin
+ LNewEntry.LeakAddress := nil;
+ LNewEntry.LeakedClass := nil;
+ LNewEntry.LeakedCppTypeIdPtr := ALeakedCppVirtObjTypeIdPtr;
+ LNewEntry.LeakCount := ACount;
+ {Add it to the correct list}
+ Result := LockExpectedMemoryLeaksList
+ and UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByClass, @LNewEntry);
+ ExpectedMemoryLeaksListLocked := False;
+ end
+ else
+ begin
+ Result := False;
+ end;
+ end
+ else
+ begin
+ Result := False;
+ end;
+end;
+{$endif}
+
+function RegisterExpectedMemoryLeak(ALeakedBlockSize: NativeInt; ACount: Integer = 1): Boolean; overload;
+var
+ LNewEntry: TExpectedMemoryLeak;
+begin
+ {Fill out the structure}
+ LNewEntry.LeakAddress := nil;
+ LNewEntry.LeakedClass := nil;
+ {$ifdef CheckCppObjectTypeEnabled}
+ LNewEntry.LeakedCppTypeIdPtr := nil;
+ {$endif}
+ LNewEntry.LeakSize := ALeakedBlockSize;
+ LNewEntry.LeakCount := ACount;
+ {Add it to the correct list}
+ Result := LockExpectedMemoryLeaksList
+ and UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryBySizeOnly, @LNewEntry);
+ ExpectedMemoryLeaksListLocked := False;
+end;
+
+function UnregisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean; overload;
+var
+ LNewEntry: TExpectedMemoryLeak;
+begin
+ {Fill out the structure}
+{$ifndef FullDebugMode}
+ LNewEntry.LeakAddress := ALeakedPointer;
+{$else}
+ LNewEntry.LeakAddress := Pointer(PByte(ALeakedPointer) - SizeOf(TFullDebugBlockHeader));
+{$endif}
+ LNewEntry.LeakedClass := nil;
+ {$ifdef CheckCppObjectTypeEnabled}
+ LNewEntry.LeakedCppTypeIdPtr := nil;
+ {$endif}
+ LNewEntry.LeakSize := 0;
+ LNewEntry.LeakCount := -1;
+ {Remove it from the list}
+ Result := LockExpectedMemoryLeaksList
+ and UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByAddress, @LNewEntry);
+ ExpectedMemoryLeaksListLocked := False;
+end;
+
+function UnregisterExpectedMemoryLeak(ALeakedObjectClass: TClass; ACount: Integer = 1): Boolean; overload;
+begin
+ Result := RegisterExpectedMemoryLeak(ALeakedObjectClass, - ACount);
+end;
+
+{$ifdef CheckCppObjectTypeEnabled}
+function UnregisterExpectedMemoryLeak(ALeakedCppVirtObjTypeIdPtr: Pointer; ACount: Integer): Boolean; overload;
+begin
+ Result := RegisterExpectedMemoryLeak(ALeakedCppVirtObjTypeIdPtr, - ACount);
+end;
+{$endif}
+
+function UnregisterExpectedMemoryLeak(ALeakedBlockSize: NativeInt; ACount: Integer = 1): Boolean; overload;
+begin
+ Result := RegisterExpectedMemoryLeak(ALeakedBlockSize, - ACount);
+end;
+
+{Returns a list of all expected memory leaks}
+function GetRegisteredMemoryLeaks: TRegisteredMemoryLeaks;
+
+ procedure AddEntries(AEntry: PExpectedMemoryLeak);
+ var
+ LInd: Integer;
+ begin
+ while AEntry <> nil do
+ begin
+ LInd := Length(Result);
+ SetLength(Result, LInd + 1);
+ {Add the entry}
+{$ifndef FullDebugMode}
+ Result[LInd].LeakAddress := AEntry.LeakAddress;
+{$else}
+ Result[LInd].LeakAddress := Pointer(PByte(AEntry.LeakAddress) + SizeOf(TFullDebugBlockHeader));
+{$endif}
+ Result[LInd].LeakedClass := AEntry.LeakedClass;
+{$ifdef CheckCppObjectTypeEnabled}
+ Result[LInd].LeakedCppTypeIdPtr := AEntry.LeakedCppTypeIdPtr;
+{$endif}
+ Result[LInd].LeakSize := AEntry.LeakSize;
+ Result[LInd].LeakCount := AEntry.LeakCount;
+ {Next entry}
+ AEntry := AEntry.NextLeak;
+ end;
+ end;
+
+begin
+ SetLength(Result, 0);
+ if (ExpectedMemoryLeaks <> nil) and LockExpectedMemoryLeaksList then
+ begin
+ {Add all entries}
+ AddEntries(ExpectedMemoryLeaks.FirstEntryByAddress);
+ AddEntries(ExpectedMemoryLeaks.FirstEntryByClass);
+ AddEntries(ExpectedMemoryLeaks.FirstEntryBySizeOnly);
+ {Unlock the list}
+ ExpectedMemoryLeaksListLocked := False;
+ end;
+end;
+
+{$else}
+ {$ifdef BDS2006AndUp}
+function NoOpRegisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean;
+begin
+ {Do nothing. Used when memory leak reporting is disabled under Delphi 2006 and later.}
+ Result := False;
+end;
+
+function NoOpUnregisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean;
+begin
+ {Do nothing. Used when memory leak reporting is disabled under Delphi 2006 and later.}
+ Result := False;
+end;
+ {$endif}
+{$endif}
+
+{Detects the probable string data type for a memory block.}
+function DetectStringData(APMemoryBlock: Pointer;
+ AAvailableSpaceInBlock: NativeInt): TStringDataType;
+const
+ {If the string reference count field contains a value greater than this,
+ then it is assumed that the block is not a string.}
+ MaxRefCount = 255;
+ {The lowest ASCII character code considered valid string data. If there are
+ any characters below this code point then the data is assumed not to be a
+ string. #9 = Tab.}
+ MinCharCode = #9;
+var
+ LStringLength, LElemSize, LCharInd: Integer;
+ LPAnsiStr: PAnsiChar;
+ LPUniStr: PWideChar;
+begin
+ {Check that the reference count is within a reasonable range}
+ if PStrRec(APMemoryBlock).refCnt > MaxRefCount then
+ begin
+ Result := stUnknown;
+ Exit;
+ end;
+{$ifdef BCB6OrDelphi6AndUp}
+ {$if RTLVersion >= 20}
+ LElemSize := PStrRec(APMemoryBlock).elemSize;
+ {Element size must be either 1 (Ansi) or 2 (Unicode)}
+ if (LElemSize <> 1) and (LElemSize <> 2) then
+ begin
+ Result := stUnknown;
+ Exit;
+ end;
+ {$ifend}
+ {$if RTLVersion < 20}
+ LElemSize := 1;
+ {$ifend}
+{$else}
+ LElemSize := 1;
+{$endif}
+ {Get the string length}
+ LStringLength := PStrRec(APMemoryBlock).length;
+ {Does the string fit?}
+ if (LStringLength <= 0)
+ or (LStringLength >= (AAvailableSpaceInBlock - SizeOf(StrRec)) div LElemSize) then
+ begin
+ Result := stUnknown;
+ Exit;
+ end;
+ {Check for no characters outside the expected range. If there are,
+ then it is probably not a string.}
+ if LElemSize = 1 then
+ begin
+ {Check that all characters are in the range considered valid.}
+ LPAnsiStr := PAnsiChar(PByte(APMemoryBlock) + SizeOf(StrRec));
+ for LCharInd := 1 to LStringLength do
+ begin
+ if LPAnsiStr^ < MinCharCode then
+ begin
+ Result := stUnknown;
+ Exit;
+ end;
+ Inc(LPAnsiStr);
+ end;
+ {Must have a trailing #0}
+ if LPAnsiStr^ = #0 then
+ Result := stAnsiString
+ else
+ Result := stUnknown;
+ end
+ else
+ begin
+ {Check that all characters are in the range considered valid.}
+ LPUniStr := PWideChar(PByte(APMemoryBlock) + SizeOf(StrRec));
+ for LCharInd := 1 to LStringLength do
+ begin
+ if LPUniStr^ < MinCharCode then
+ begin
+ Result := stUnknown;
+ Exit;
+ end;
+ Inc(LPUniStr);
+ end;
+ {Must have a trailing #0}
+ if LPUniStr^ = #0 then
+ Result := stUnicodeString
+ else
+ Result := stUnknown;
+ end;
+end;
+
+{Walks all allocated blocks, calling ACallBack for each. Passes the user block size and AUserData to the callback.
+ Important note: All block types will be locked during the callback, so the memory manager cannot be used inside it.}
+procedure WalkAllocatedBlocks(ACallBack: TWalkAllocatedBlocksCallback; AUserData: Pointer);
+const
+ DebugHeaderSize = {$ifdef FullDebugMode}SizeOf(TFullDebugBlockHeader){$else}0{$endif};
+ TotalDebugOverhead = {$ifdef FullDebugMode}FullDebugBlockOverhead{$else}0{$endif};
+var
+ LPMediumBlock: Pointer;
+ LPMediumBlockPoolHeader: PMediumBlockPoolHeader;
+ LMediumBlockHeader: NativeUInt;
+ LPLargeBlock: PLargeBlockHeader;
+ LBlockSize: NativeInt;
+ LPSmallBlockPool: PSmallBlockPoolHeader;
+ LCurPtr, LEndPtr: Pointer;
+ LInd: Integer;
+begin
+ {Lock all small block types}
+ LockAllSmallBlockTypes;
+ {Lock the medium blocks}
+ LockMediumBlocks;
+ try
+ {Step through all the medium block pools}
+ LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
+ while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
+ begin
+ LPMediumBlock := GetFirstMediumBlockInPool(LPMediumBlockPoolHeader);
+ while LPMediumBlock <> nil do
+ begin
+ LMediumBlockHeader := PNativeUInt(PByte(LPMediumBlock) - BlockHeaderSize)^;
+ {Is the block in use?}
+ if LMediumBlockHeader and IsFreeBlockFlag = 0 then
+ begin
+ if (LMediumBlockHeader and IsSmallBlockPoolInUseFlag) <> 0 then
+ begin
+ {Step through all the blocks in the small block pool}
+ LPSmallBlockPool := LPMediumBlock;
+ {Get the useable size inside a block}
+ LBlockSize := LPSmallBlockPool.BlockType.BlockSize - BlockHeaderSize - TotalDebugOverhead;
+ {Get the first and last pointer for the pool}
+ GetFirstAndLastSmallBlockInPool(LPSmallBlockPool, LCurPtr, LEndPtr);
+ {Step through all blocks}
+ while UIntPtr(LCurPtr) <= UIntPtr(LEndPtr) do
+ begin
+ {Is this block in use?}
+ if (PNativeUInt(PByte(LCurPtr) - BlockHeaderSize)^ and IsFreeBlockFlag) = 0 then
+ begin
+ ACallBack(PByte(LCurPtr) + DebugHeaderSize, LBlockSize, AUserData);
+ end;
+ {Next block}
+ Inc(PByte(LCurPtr), LPSmallBlockPool.BlockType.BlockSize);
+ end;
+ end
+ else
+ begin
+ LBlockSize := (LMediumBlockHeader and DropMediumAndLargeFlagsMask) - BlockHeaderSize - TotalDebugOverhead;
+ ACallBack(PByte(LPMediumBlock) + DebugHeaderSize, LBlockSize, AUserData);
+ end;
+ end;
+ {Next medium block}
+ LPMediumBlock := NextMediumBlock(LPMediumBlock);
+ end;
+ {Get the next medium block pool}
+ LPMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
+ end;
+ finally
+ {Unlock medium blocks}
+ MediumBlocksLocked := False;
+ {Unlock all the small block types}
+ for LInd := 0 to NumSmallBlockTypes - 1 do
+ SmallBlockTypes[LInd].BlockTypeLocked := False;
+ end;
+ {Step through all the large blocks}
+ LockLargeBlocks;
+ try
+ {Get all leaked large blocks}
+ LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
+ while LPLargeBlock <> @LargeBlocksCircularList do
+ begin
+ LBlockSize := (LPLargeBlock.BlockSizeAndFlags and DropMediumAndLargeFlagsMask) - BlockHeaderSize - LargeBlockHeaderSize - TotalDebugOverhead;
+ ACallBack(PByte(LPLargeBlock) + LargeBlockHeaderSize + DebugHeaderSize, LBlockSize, AUserData);
+ {Get the next large block}
+ LPLargeBlock := LPLargeBlock.NextLargeBlockHeader;
+ end;
+ finally
+ LargeBlocksLocked := False;
+ end;
+end;
+
+{-----------LogMemoryManagerStateToFile implementation------------}
+const
+ MaxMemoryLogNodes = 100000;
+ QuickSortMinimumItemsInPartition = 4;
+
+type
+ {While scanning the memory pool the list of classes is built up in a binary search tree.}
+ PMemoryLogNode = ^TMemoryLogNode;
+ TMemoryLogNode = record
+ {The left and right child nodes}
+ LeftAndRightNodePointers: array[Boolean] of PMemoryLogNode;
+ {The class this node belongs to}
+ ClassPtr: Pointer;
+ {The number of instances of the class}
+ InstanceCount: NativeInt;
+ {The total memory usage for this class}
+ TotalMemoryUsage: NativeInt;
+ end;
+ TMemoryLogNodes = array[0..MaxMemoryLogNodes - 1] of TMemoryLogNode;
+ PMemoryLogNodes = ^TMemoryLogNodes;
+
+ TMemoryLogInfo = record
+ {The number of nodes in "Nodes" that are used.}
+ NodeCount: Integer;
+ {The root node of the binary search tree. The content of this node is not actually used, it just simplifies the
+ binary search code.}
+ RootNode: TMemoryLogNode;
+ Nodes: TMemoryLogNodes;
+ end;
+ PMemoryLogInfo = ^TMemoryLogInfo;
+
+{LogMemoryManagerStateToFile callback subroutine}
+procedure LogMemoryManagerStateCallBack(APBlock: Pointer; ABlockSize: NativeInt; AUserData: Pointer);
+var
+ LClass, LClassHashBits: NativeUInt;
+ LPLogInfo: PMemoryLogInfo;
+ LPParentNode, LPClassNode: PMemoryLogNode;
+ LChildNodeDirection: Boolean;
+begin
+ LPLogInfo := AUserData;
+ {Detecting an object is very expensive (due to the VirtualQuery call), so we do some basic checks and try to find
+ the "class" in the tree first.}
+ LClass := PNativeUInt(APBlock)^;
+ {Do some basic pointer checks: The "class" must be dword aligned and beyond 64K}
+ if (LClass > 65535)
+ and (LClass and 3 = 0) then
+ begin
+ LPParentNode := @LPLogInfo.RootNode;
+ LClassHashBits := LClass;
+ repeat
+ LChildNodeDirection := Boolean(LClassHashBits and 1);
+ {Split off the next bit of the class pointer and traverse in the appropriate direction.}
+ LPClassNode := LPParentNode.LeftAndRightNodePointers[LChildNodeDirection];
+ {Is this child node the node the class we're looking for?}
+ if (LPClassNode = nil) or (NativeUInt(LPClassNode.ClassPtr) = LClass) then
+ Break;
+ {The node was not found: Keep on traversing the tree.}
+ LClassHashBits := LClassHashBits shr 1;
+ LPParentNode := LPClassNode;
+ until False;
+ end
+ else
+ LPClassNode := nil;
+ {Was the "class" found?}
+ if LPClassNode = nil then
+ begin
+ {The "class" is not yet in the tree: Determine if it is actually a class.}
+ LClass := NativeUInt(DetectClassInstance(APBlock));
+ {If it is not a class, try to detect the string type.}
+ if LClass = 0 then
+ LClass := Ord(DetectStringData(APBlock, ABlockSize));
+ {Is this class already in the tree?}
+ LPParentNode := @LPLogInfo.RootNode;
+ LClassHashBits := LClass;
+ repeat
+ LChildNodeDirection := Boolean(LClassHashBits and 1);
+ {Split off the next bit of the class pointer and traverse in the appropriate direction.}
+ LPClassNode := LPParentNode.LeftAndRightNodePointers[LChildNodeDirection];
+ {Is this child node the node the class we're looking for?}
+ if LPClassNode = nil then
+ begin
+ {The end of the tree was reached: Add a new child node.}
+ LPClassNode := @LPLogInfo.Nodes[LPLogInfo.NodeCount];
+ Inc(LPLogInfo.NodeCount);
+ LPParentNode.LeftAndRightNodePointers[LChildNodeDirection] := LPClassNode;
+ LPClassNode.ClassPtr := Pointer(LClass);
+ Break;
+ end
+ else
+ begin
+ if NativeUInt(LPClassNode.ClassPtr) = LClass then
+ Break;
+ end;
+ {The node was not found: Keep on traversing the tree.}
+ LClassHashBits := LClassHashBits shr 1;
+ LPParentNode := LPClassNode;
+ until False;
+ end;
+ {Update the statistics for the class}
+ Inc(LPClassNode.InstanceCount);
+ Inc(LPClassNode.TotalMemoryUsage, ABlockSize);
+end;
+
+{LogMemoryManagerStateToFile subroutine: A median-of-3 quicksort routine for sorting a TMemoryLogNodes array.}
+procedure QuickSortLogNodes(APLeftItem: PMemoryLogNodes; ARightIndex: Integer);
+var
+ M, I, J: Integer;
+ LPivot, LTempItem: TMemoryLogNode;
+begin
+ while True do
+ begin
+ {Order the left, middle and right items in ascending order}
+ M := ARightIndex shr 1;
+ {Is the middle item larger than the left item?}
+ if APLeftItem[0].TotalMemoryUsage > APLeftItem[M].TotalMemoryUsage then
+ begin
+ {Swap items 0 and M}
+ LTempItem := APLeftItem[0];
+ APLeftItem[0] := APLeftItem[M];
+ APLeftItem[M] := LTempItem;
+ end;
+ {Is the middle item larger than the right?}
+ if APLeftItem[M].TotalMemoryUsage > APLeftItem[ARightIndex].TotalMemoryUsage then
+ begin
+ {The right-hand item is not larger - swap it with the middle}
+ LTempItem := APLeftItem[ARightIndex];
+ APLeftItem[ARightIndex] := APLeftItem[M];
+ APLeftItem[M] := LTempItem;
+ {Is the left larger than the new middle?}
+ if APLeftItem[0].TotalMemoryUsage > APLeftItem[M].TotalMemoryUsage then
+ begin
+ {Swap items 0 and M}
+ LTempItem := APLeftItem[0];
+ APLeftItem[0] := APLeftItem[M];
+ APLeftItem[M] := LTempItem;
+ end;
+ end;
+ {Move the pivot item out of the way by swapping M with R - 1}
+ LPivot := APLeftItem[M];
+ APLeftItem[M] := APLeftItem[ARightIndex - 1];
+ APLeftItem[ARightIndex - 1] := LPivot;
+ {Set up the loop counters}
+ I := 0;
+ J := ARightIndex - 1;
+ while true do
+ begin
+ {Find the first item from the left that is not smaller than the pivot}
+ repeat
+ Inc(I);
+ until APLeftItem[I].TotalMemoryUsage >= LPivot.TotalMemoryUsage;
+ {Find the first item from the right that is not larger than the pivot}
+ repeat
+ Dec(J);
+ until APLeftItem[J].TotalMemoryUsage <= LPivot.TotalMemoryUsage;
+ {Stop the loop when the two indexes cross}
+ if J < I then
+ Break;
+ {Swap item I and J}
+ LTempItem := APLeftItem[I];
+ APLeftItem[I] := APLeftItem[J];
+ APLeftItem[J] := LTempItem;
+ end;
+ {Put the pivot item back in the correct position by swapping I with R - 1}
+ APLeftItem[ARightIndex - 1] := APLeftItem[I];
+ APLeftItem[I] := LPivot;
+ {Sort the left-hand partition}
+ if J >= (QuickSortMinimumItemsInPartition - 1) then
+ QuickSortLogNodes(APLeftItem, J);
+ {Sort the right-hand partition}
+ APLeftItem := @APLeftItem[I + 1];
+ ARightIndex := ARightIndex - I - 1;
+ if ARightIndex < (QuickSortMinimumItemsInPartition - 1) then
+ Break;
+ end;
+end;
+
+{LogMemoryManagerStateToFile subroutine: An InsertionSort routine for sorting a TMemoryLogNodes array.}
+procedure InsertionSortLogNodes(APLeftItem: PMemoryLogNodes; ARightIndex: Integer);
+var
+ I, J: Integer;
+ LCurNode: TMemoryLogNode;
+begin
+ for I := 1 to ARightIndex do
+ begin
+ LCurNode := APLeftItem[I];
+ {Scan backwards to find the best insertion spot}
+ J := I;
+ while (J > 0) and (APLeftItem[J - 1].TotalMemoryUsage > LCurNode.TotalMemoryUsage) do
+ begin
+ APLeftItem[J] := APLeftItem[J - 1];
+ Dec(J);
+ end;
+ APLeftItem[J] := LCurNode;
+ end;
+end;
+
+{Writes a log file containing a summary of the memory mananger state and a summary of allocated blocks grouped by
+ class. The file will be saved in UTF-8 encoding (in supported Delphi versions). Returns True on success. }
+function LogMemoryManagerStateToFile(const AFileName: string; const AAdditionalDetails: string): Boolean;
+const
+ MsgBufferSize = 65536;
+ MaxLineLength = 512;
+ {Write the UTF-8 BOM in Delphi versions that support UTF-8 conversion.}
+ LogStateHeaderMsg = {$ifdef BCB6OrDelphi7AndUp}#$EF#$BB#$BF + {$endif}
+ 'FastMM State Capture:'#13#10'---------------------'#13#10#13#10;
+ LogStateAllocatedMsg = 'K Allocated'#13#10;
+ LogStateOverheadMsg = 'K Overhead'#13#10;
+ LogStateEfficiencyMsg = '% Efficiency'#13#10#13#10'Usage Detail:'#13#10;
+ LogStateAdditionalInfoMsg = #13#10'Additional Information:'#13#10'-----------------------'#13#10;
+var
+ LPLogInfo: PMemoryLogInfo;
+ LInd: Integer;
+ LPNode: PMemoryLogNode;
+ LMsgBuffer: array[0..MsgBufferSize - 1] of AnsiChar;
+ LPMsg: PAnsiChar;
+ LBufferSpaceUsed, LBytesWritten: Cardinal;
+ LFileHandle: NativeUInt;
+ LMemoryManagerUsageSummary: TMemoryManagerUsageSummary;
+ LUTF8Str: AnsiString;
+begin
+ {Get the current memory manager usage summary.}
+ GetMemoryManagerUsageSummary(LMemoryManagerUsageSummary);
+ {Allocate the memory required to capture detailed allocation information.}
+ LPLogInfo := VirtualAlloc(nil, SizeOf(TMemoryLogInfo), MEM_COMMIT or MEM_TOP_DOWN, PAGE_READWRITE);
+ if LPLogInfo <> nil then
+ begin
+ try
+ {Log all allocated blocks by class.}
+ WalkAllocatedBlocks(LogMemoryManagerStateCallBack, LPLogInfo);
+ {Sort the classes by total memory usage: Do the initial QuickSort pass over the list to sort the list in groups
+ of QuickSortMinimumItemsInPartition size.}
+ if LPLogInfo.NodeCount >= QuickSortMinimumItemsInPartition then
+ QuickSortLogNodes(@LPLogInfo.Nodes[0], LPLogInfo.NodeCount - 1);
+ {Do the final InsertionSort pass.}
+ InsertionSortLogNodes(@LPLogInfo.Nodes[0], LPLogInfo.NodeCount - 1);
+ {Create the output file}
+ {$ifdef POSIX}
+ lFileHandle := FileCreate(AFilename);
+ {$else}
+ LFileHandle := CreateFileA(PAnsiChar(AFilename), GENERIC_READ or GENERIC_WRITE, 0,
+ nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
+ {$endif}
+ if LFileHandle <> INVALID_HANDLE_VALUE then
+ begin
+ try
+ {Log the usage summary}
+ LPMsg := @LMsgBuffer;
+ LPMsg := AppendStringToBuffer(LogStateHeaderMsg, LPMsg, Length(LogStateHeaderMsg));
+ LPMsg := NativeUIntToStrBuf(LMemoryManagerUsageSummary.AllocatedBytes shr 10, LPMsg);
+ LPMsg := AppendStringToBuffer(LogStateAllocatedMsg, LPMsg, Length(LogStateAllocatedMsg));
+ LPMsg := NativeUIntToStrBuf(LMemoryManagerUsageSummary.OverheadBytes shr 10, LPMsg);
+ LPMsg := AppendStringToBuffer(LogStateOverheadMsg, LPMsg, Length(LogStateOverheadMsg));
+ LPMsg := NativeUIntToStrBuf(Round(LMemoryManagerUsageSummary.EfficiencyPercentage), LPMsg);
+ LPMsg := AppendStringToBuffer(LogStateEfficiencyMsg, LPMsg, Length(LogStateEfficiencyMsg));
+ {Log the allocation detail}
+ for LInd := LPLogInfo.NodeCount - 1 downto 0 do
+ begin
+ LPNode := @LPLogInfo.Nodes[LInd];
+ {Add the allocated size}
+ LPMsg^ := ' ';
+ Inc(LPMsg);
+ LPMsg := NativeUIntToStrBuf(LPNode.TotalMemoryUsage, LPMsg);
+ LPMsg := AppendStringToBuffer(BytesMessage, LPMsg, Length(BytesMessage));
+ {Add the class type}
+ case NativeInt(LPNode.ClassPtr) of
+ {Unknown}
+ 0:
+ begin
+ LPMsg := AppendStringToBuffer(UnknownClassNameMsg, LPMsg, Length(UnknownClassNameMsg));
+ end;
+ {AnsiString}
+ 1:
+ begin
+ LPMsg := AppendStringToBuffer(AnsiStringBlockMessage, LPMsg, Length(AnsiStringBlockMessage));
+ end;
+ {UnicodeString}
+ 2:
+ begin
+ LPMsg := AppendStringToBuffer(UnicodeStringBlockMessage, LPMsg, Length(UnicodeStringBlockMessage));
+ end;
+ {Classes}
+ else
+ begin
+ LPMsg := AppendClassNameToBuffer(LPNode.ClassPtr, LPMsg);
+ end;
+ end;
+ {Add the count}
+ LPMsg^ := ' ';
+ Inc(LPMsg);
+ LPMsg^ := 'x';
+ Inc(LPMsg);
+ LPMsg^ := ' ';
+ Inc(LPMsg);
+ LPMsg := NativeUIntToStrBuf(LPNode.InstanceCount, LPMsg);
+ LPMsg^ := #13;
+ Inc(LPMsg);
+ LPMsg^ := #10;
+ Inc(LPMsg);
+ {Flush the buffer?}
+ LBufferSpaceUsed := NativeInt(LPMsg) - NativeInt(@LMsgBuffer);
+ if LBufferSpaceUsed > (MsgBufferSize - MaxLineLength) then
+ begin
+ WriteFile(LFileHandle, LMsgBuffer, LBufferSpaceUsed, LBytesWritten, nil);
+ LPMsg := @LMsgBuffer;
+ end;
+ end;
+ if AAdditionalDetails <> '' then
+ LPMsg := AppendStringToBuffer(LogStateAdditionalInfoMsg, LPMsg, Length(LogStateAdditionalInfoMsg));
+ {Flush any remaining bytes}
+ LBufferSpaceUsed := NativeInt(LPMsg) - NativeInt(@LMsgBuffer);
+ if LBufferSpaceUsed > 0 then
+ WriteFile(LFileHandle, LMsgBuffer, LBufferSpaceUsed, LBytesWritten, nil);
+ {Write the additional info}
+ if AAdditionalDetails <> '' then
+ begin
+ {$ifdef BCB6OrDelphi7AndUp}
+ LUTF8Str := UTF8Encode(AAdditionalDetails);
+ {$else}
+ LUTF8Str := AAdditionalDetails;
+ {$endif}
+ WriteFile(LFileHandle, LUTF8Str[1], Length(LUTF8Str), LBytesWritten, nil);
+ end;
+ {Success}
+ Result := True;
+ finally
+ {Close the file}
+ {$ifdef POSIX}
+ __close(LFileHandle)
+ {$else}
+ CloseHandle(LFileHandle);
+ {$endif}
+ end;
+ end
+ else
+ Result := False;
+ finally
+ VirtualFree(LPLogInfo, 0, MEM_RELEASE);
+ end;
+ end
+ else
+ Result := False;
+end;
+
+{-----------CheckBlocksOnShutdown implementation------------}
+
+{Checks blocks for modification after free and also for memory leaks}
+procedure CheckBlocksOnShutdown(ACheckForLeakedBlocks: Boolean);
+{$ifdef EnableMemoryLeakReporting}
+type
+ {Leaked class type}
+ TLeakedClass = record
+ ClassPointer: TClass;
+ {$ifdef CheckCppObjectTypeEnabled}
+ CppTypeIdPtr: Pointer;
+ {$endif}
+ NumLeaks: Cardinal;
+ end;
+ TLeakedClasses = array[0..255] of TLeakedClass;
+ PLeakedClasses = ^TLeakedClasses;
+ {Leak statistics for a small block type}
+ TSmallBlockLeaks = array[0..NumSmallBlockTypes - 1] of TLeakedClasses;
+ {A leaked medium or large block}
+ TMediumAndLargeBlockLeaks = array[0..4095] of NativeUInt;
+{$endif}
+var
+{$ifdef EnableMemoryLeakReporting}
+ {The leaked classes for small blocks}
+ LSmallBlockLeaks: TSmallBlockLeaks;
+ LLeakType: TMemoryLeakType;
+ {$ifdef CheckCppObjectTypeEnabled}
+ LLeakedCppTypeIdPtr: Pointer;
+ LCppTypeName: PAnsiChar;
+ {$endif}
+ LMediumAndLargeBlockLeaks: TMediumAndLargeBlockLeaks;
+ LNumMediumAndLargeLeaks: Integer;
+ LPLargeBlock: PLargeBlockHeader;
+ LLeakMessage: array[0..32767] of AnsiChar;
+ {$ifndef NoMessageBoxes}
+ LMessageTitleBuffer: array[0..1023] of AnsiChar;
+ {$endif}
+ LMsgPtr: PAnsiChar;
+ LExpectedLeaksOnly, LSmallLeakHeaderAdded, LBlockSizeHeaderAdded: Boolean;
+ LBlockTypeInd, LClassInd, LBlockInd: Cardinal;
+ LMediumBlockSize, LPreviousBlockSize, LLargeBlockSize, LThisBlockSize: NativeUInt;
+{$endif}
+ LPMediumBlock: Pointer;
+ LPMediumBlockPoolHeader: PMediumBlockPoolHeader;
+ LMediumBlockHeader: NativeUInt;
+
+{$ifdef EnableMemoryLeakReporting}
+ {Tries to account for a memory leak. Returns true if the leak is expected and
+ removes the leak from the list}
+ function GetMemoryLeakType(AAddress: Pointer; ASpaceInsideBlock: NativeUInt): TMemoryLeakType;
+ var
+ LLeak: TExpectedMemoryLeak;
+ begin
+ {Default to not found}
+ Result := mltUnexpectedLeak;
+ if ExpectedMemoryLeaks <> nil then
+ begin
+ {Check by pointer address}
+ LLeak.LeakAddress := AAddress;
+ LLeak.LeakedClass := nil;
+ {$ifdef CheckCppObjectTypeEnabled}
+ LLeak.LeakedCppTypeIdPtr := nil;
+ {$endif}
+ LLeak.LeakSize := 0;
+ LLeak.LeakCount := -1;
+ if UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByAddress, @LLeak, False) then
+ begin
+ Result := mltExpectedLeakRegisteredByPointer;
+ Exit;
+ end;
+ {Check by class}
+ LLeak.LeakAddress := nil;
+ {$ifdef FullDebugMode}
+ LLeak.LeakedClass := TClass(PNativeUInt(PByte(AAddress)+ SizeOf(TFullDebugBlockHeader))^);
+ {$else}
+ LLeak.LeakedClass := TClass(PNativeUInt(AAddress)^);
+ {$endif}
+ {$ifdef CheckCppObjectTypeEnabled}
+ if Assigned(GetCppVirtObjTypeIdPtrFunc) then
+ begin
+ {$ifdef FullDebugMode}
+ LLeak.LeakedCppTypeIdPtr := GetCppVirtObjTypeIdPtrFunc(Pointer(PByte(AAddress)
+ + SizeOf(TFullDebugBlockHeader)), ASpaceInsideBlock);
+ {$else}
+ LLeak.LeakedCppTypeIdPtr := GetCppVirtObjTypeIdPtrFunc(AAddress, ASpaceInsideBlock);
+ {$endif}
+ end;
+ LLeakedCppTypeIdPtr := LLeak.LeakedCppTypeIdPtr;
+ {$endif}
+ LLeak.LeakSize := ASpaceInsideBlock;
+ if UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByClass, @LLeak, False) then
+ begin
+ Result := mltExpectedLeakRegisteredByClass;
+ Exit;
+ end;
+ {Check by size: the block must be large enough to hold the leak}
+ LLeak.LeakedClass := nil;
+ if UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryBySizeOnly, @LLeak, False) then
+ Result := mltExpectedLeakRegisteredBySize;
+ end;
+ end;
+
+ {Checks the small block pool for leaks.}
+ procedure CheckSmallBlockPoolForLeaks(APSmallBlockPool: PSmallBlockPoolHeader);
+ var
+ LLeakedClass: TClass;
+ {$ifdef CheckCppObjectTypeEnabled}
+ LLeakedCppObjectTypeId: Pointer;
+ {$endif}
+ LSmallBlockLeakType: TMemoryLeakType;
+ LClassIndex: Integer;
+ LCurPtr, LEndPtr, LDataPtr: Pointer;
+ LBlockTypeIndex: Cardinal;
+ LPLeakedClasses: PLeakedClasses;
+ LSmallBlockSize: Cardinal;
+ begin
+ {Get the useable size inside a block}
+ LSmallBlockSize := APSmallBlockPool.BlockType.BlockSize - BlockHeaderSize;
+ {$ifdef FullDebugMode}
+ Dec(LSmallBlockSize, FullDebugBlockOverhead);
+ {$endif}
+ {Get the block type index}
+ LBlockTypeIndex := (UIntPtr(APSmallBlockPool.BlockType) - UIntPtr(@SmallBlockTypes[0])) div SizeOf(TSmallBlockType);
+ LPLeakedClasses := @LSmallBlockLeaks[LBlockTypeIndex];
+ {Get the first and last pointer for the pool}
+ GetFirstAndLastSmallBlockInPool(APSmallBlockPool, LCurPtr, LEndPtr);
+ {Step through all blocks}
+ while UIntPtr(LCurPtr) <= UIntPtr(LEndPtr) do
+ begin
+ {Is this block in use? If so, is the debug info intact?}
+ if ((PNativeUInt(PByte(LCurPtr) - BlockHeaderSize)^ and IsFreeBlockFlag) = 0) then
+ begin
+ {$ifdef FullDebugMode}
+ if CheckBlockBeforeFreeOrRealloc(LCurPtr, boBlockCheck) then
+ {$endif}
+ begin
+ {$ifdef CheckCppObjectTypeEnabled}
+ LLeakedCppTypeIdPtr := nil;
+ {$endif}
+ {Get the leak type}
+ LSmallBlockLeakType := GetMemoryLeakType(LCurPtr, LSmallBlockSize);
+ {$ifdef LogMemoryLeakDetailToFile}
+ {$ifdef HideExpectedLeaksRegisteredByPointer}
+ if LSmallBlockLeakType <> mltExpectedLeakRegisteredByPointer then
+ {$endif}
+ LogMemoryLeakOrAllocatedBlock(LCurPtr, True);
+ {$endif}
+ {Only expected leaks?}
+ LExpectedLeaksOnly := LExpectedLeaksOnly and (LSmallBlockLeakType <> mltUnexpectedLeak);
+ {$ifdef HideExpectedLeaksRegisteredByPointer}
+ if LSmallBlockLeakType <> mltExpectedLeakRegisteredByPointer then
+ {$endif}
+ begin
+ {Get a pointer to the user data}
+ {$ifndef FullDebugMode}
+ LDataPtr := LCurPtr;
+ {$else}
+ LDataPtr := Pointer(PByte(LCurPtr) + SizeOf(TFullDebugBlockHeader));
+ {$endif}
+ {Default to an unknown block}
+ LClassIndex := 0;
+ {Get the class contained by the block}
+ LLeakedClass := DetectClassInstance(LDataPtr);
+ {Not a Delphi class? -> is it perhaps a string or C++ object type?}
+ if LLeakedClass = nil then
+ begin
+ {$ifdef CheckCppObjectTypeEnabled}
+ LLeakedCppObjectTypeId := LLeakedCppTypeIdPtr;
+ if (LLeakedCppObjectTypeId = nil) and (ExpectedMemoryLeaks = nil) then
+ begin
+ if Assigned(GetCppVirtObjTypeIdPtrFunc) then
+ begin
+ LLeakedCppObjectTypeId := GetCppVirtObjTypeIdPtrFunc(LDataPtr, LSmallBlockSize);
+ end;
+ end;
+ if Assigned(LLeakedCppObjectTypeId) then
+ begin
+ LClassIndex := 3;
+ while LClassIndex <= High(TLeakedClasses) do
+ begin
+ if (Pointer(LPLeakedClasses[LClassIndex].CppTypeIdPtr) = LLeakedCppObjectTypeId)
+ or ((LPLeakedClasses[LClassIndex].CppTypeIdPtr = nil)
+ and (LPLeakedClasses[LClassIndex].ClassPointer = nil)) then
+ begin
+ Break;
+ end;
+ Inc(LClassIndex);
+ end;
+ if LClassIndex <= High(TLeakedClasses) then
+ Pointer(LPLeakedClasses[LClassIndex].CppTypeIdPtr) := LLeakedCppObjectTypeId
+ else
+ LClassIndex := 0;
+ end
+ else
+ begin
+ {$endif}
+ {Not a known class: Is it perhaps string data?}
+ case DetectStringData(LDataPtr, APSmallBlockPool.BlockType.BlockSize - (BlockHeaderSize {$ifdef FullDebugMode} + FullDebugBlockOverhead{$endif})) of
+ stAnsiString: LClassIndex := 1;
+ stUnicodeString: LClassIndex := 2;
+ end;
+ {$ifdef CheckCppObjectTypeEnabled}
+ end;
+ {$endif}
+ end
+ else
+ begin
+ LClassIndex := 3;
+ while LClassIndex <= High(TLeakedClasses) do
+ begin
+ if (LPLeakedClasses[LClassIndex].ClassPointer = LLeakedClass)
+ or ((LPLeakedClasses[LClassIndex].ClassPointer = nil)
+ {$ifdef CheckCppObjectTypeEnabled}
+ and (LPLeakedClasses[LClassIndex].CppTypeIdPtr = nil)
+ {$endif}
+ ) then
+ begin
+ Break;
+ end;
+ Inc(LClassIndex);
+ end;
+ if LClassIndex <= High(TLeakedClasses) then
+ LPLeakedClasses[LClassIndex].ClassPointer := LLeakedClass
+ else
+ LClassIndex := 0;
+ end;
+ {Add to the number of leaks for the class}
+ Inc(LPLeakedClasses[LClassIndex].NumLeaks);
+ end;
+ end;
+ end
+ else
+ begin
+ {$ifdef CheckUseOfFreedBlocksOnShutdown}
+ {Check that the block has not been modified since being freed}
+ CheckFreeBlockUnmodified(LCurPtr, APSmallBlockPool.BlockType.BlockSize, boBlockCheck);
+ {$endif}
+ end;
+ {Next block}
+ Inc(PByte(LCurPtr), APSmallBlockPool.BlockType.BlockSize);
+ end;
+ end;
+{$endif}
+
+begin
+{$ifdef EnableMemoryLeakReporting}
+ {Clear the leak arrays}
+ FillChar(LSmallBlockLeaks, SizeOf(LSmallBlockLeaks), 0);
+ FillChar(LMediumAndLargeBlockLeaks, SizeOf(LMediumAndLargeBlockLeaks), 0);
+ {Step through all the medium block pools}
+ LNumMediumAndLargeLeaks := 0;
+ {No unexpected leaks so far}
+ LExpectedLeaksOnly := True;
+{$endif}
+ {Step through all the medium block pools}
+ LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
+ while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
+ begin
+ LPMediumBlock := GetFirstMediumBlockInPool(LPMediumBlockPoolHeader);
+ while LPMediumBlock <> nil do
+ begin
+ LMediumBlockHeader := PNativeUInt(PByte(LPMediumBlock) - BlockHeaderSize)^;
+ {Is the block in use?}
+ if LMediumBlockHeader and IsFreeBlockFlag = 0 then
+ begin
+{$ifdef EnableMemoryLeakReporting}
+ if ACheckForLeakedBlocks then
+ begin
+ if (LMediumBlockHeader and IsSmallBlockPoolInUseFlag) <> 0 then
+ begin
+ {Get all the leaks for the small block pool}
+ CheckSmallBlockPoolForLeaks(LPMediumBlock);
+ end
+ else
+ begin
+ if (LNumMediumAndLargeLeaks < Length(LMediumAndLargeBlockLeaks))
+ {$ifdef FullDebugMode}
+ and CheckBlockBeforeFreeOrRealloc(LPMediumBlock, boBlockCheck)
+ {$endif}
+ then
+ begin
+ LMediumBlockSize := (LMediumBlockHeader and DropMediumAndLargeFlagsMask) - BlockHeaderSize;
+ {$ifdef FullDebugMode}
+ Dec(LMediumBlockSize, FullDebugBlockOverhead);
+ {$endif}
+ {Get the leak type}
+ LLeakType := GetMemoryLeakType(LPMediumBlock, LMediumBlockSize);
+ {Is it an expected leak?}
+ LExpectedLeaksOnly := LExpectedLeaksOnly and (LLeakType <> mltUnexpectedLeak);
+ {$ifdef LogMemoryLeakDetailToFile}
+ {$ifdef HideExpectedLeaksRegisteredByPointer}
+ if LLeakType <> mltExpectedLeakRegisteredByPointer then
+ {$endif}
+ LogMemoryLeakOrAllocatedBlock(LPMediumBlock, True);
+ {$endif}
+ {$ifdef HideExpectedLeaksRegisteredByPointer}
+ if LLeakType <> mltExpectedLeakRegisteredByPointer then
+ {$endif}
+ begin
+ {Add the leak to the list}
+ LMediumAndLargeBlockLeaks[LNumMediumAndLargeLeaks] := LMediumBlockSize;
+ Inc(LNumMediumAndLargeLeaks);
+ end;
+ end;
+ end;
+ end;
+{$endif}
+ end
+ else
+ begin
+{$ifdef CheckUseOfFreedBlocksOnShutdown}
+ {Check that the block has not been modified since being freed}
+ CheckFreeBlockUnmodified(LPMediumBlock, LMediumBlockHeader and DropMediumAndLargeFlagsMask, boBlockCheck);
+{$endif}
+ end;
+ {Next medium block}
+ LPMediumBlock := NextMediumBlock(LPMediumBlock);
+ end;
+ {Get the next medium block pool}
+ LPMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
+ end;
+{$ifdef EnableMemoryLeakReporting}
+ if ACheckForLeakedBlocks then
+ begin
+ {Get all leaked large blocks}
+ LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
+ while LPLargeBlock <> @LargeBlocksCircularList do
+ begin
+ if (LNumMediumAndLargeLeaks < length(LMediumAndLargeBlockLeaks))
+ {$ifdef FullDebugMode}
+ and CheckBlockBeforeFreeOrRealloc(Pointer(PByte(LPLargeBlock) + LargeBlockHeaderSize), boBlockCheck)
+ {$endif}
+ then
+ begin
+ LLargeBlockSize := (LPLargeBlock.BlockSizeAndFlags and DropMediumAndLargeFlagsMask) - BlockHeaderSize - LargeBlockHeaderSize;
+ {$ifdef FullDebugMode}
+ Dec(LLargeBlockSize, FullDebugBlockOverhead);
+ {$endif}
+ {Get the leak type}
+ LLeakType := GetMemoryLeakType(Pointer(PByte(LPLargeBlock) + LargeBlockHeaderSize), LLargeBlockSize);
+ {Is it an expected leak?}
+ LExpectedLeaksOnly := LExpectedLeaksOnly and (LLeakType <> mltUnexpectedLeak);
+ {$ifdef LogMemoryLeakDetailToFile}
+ {$ifdef HideExpectedLeaksRegisteredByPointer}
+ if LLeakType <> mltExpectedLeakRegisteredByPointer then
+ {$endif}
+ LogMemoryLeakOrAllocatedBlock(Pointer(PByte(LPLargeBlock) + LargeBlockHeaderSize), True);
+ {$endif}
+ {$ifdef HideExpectedLeaksRegisteredByPointer}
+ if LLeakType <> mltExpectedLeakRegisteredByPointer then
+ {$endif}
+ begin
+ {Add the leak}
+ LMediumAndLargeBlockLeaks[LNumMediumAndLargeLeaks] := LLargeBlockSize;
+ Inc(LNumMediumAndLargeLeaks);
+ end;
+ end;
+ {Get the next large block}
+ LPLargeBlock := LPLargeBlock.NextLargeBlockHeader;
+ end;
+ {Display the leak message if required}
+ if not LExpectedLeaksOnly then
+ begin
+ {Small leak header has not been added}
+ LSmallLeakHeaderAdded := False;
+ LPreviousBlockSize := 0;
+ {Set up the leak message header so long}
+ LMsgPtr := AppendStringToBuffer(LeakMessageHeader, @LLeakMessage[0], length(LeakMessageHeader));
+ {Step through all the small block types}
+ for LBlockTypeInd := 0 to NumSmallBlockTypes - 1 do
+ begin
+ LThisBlockSize := SmallBlockTypes[LBlockTypeInd].BlockSize - BlockHeaderSize;
+ {$ifdef FullDebugMode}
+ Dec(LThisBlockSize, FullDebugBlockOverhead);
+ if NativeInt(LThisBlockSize) < 0 then
+ LThisBlockSize := 0;
+ {$endif}
+ LBlockSizeHeaderAdded := False;
+ {Any leaks?}
+ for LClassInd := High(LSmallBlockLeaks[LBlockTypeInd]) downto 0 do
+ begin
+ {Is there still space in the message buffer? Reserve space for the message
+ footer.}
+ if LMsgPtr > @LLeakMessage[High(LLeakMessage) - 2048] then
+ Break;
+ {Check the count}
+ if LSmallBlockLeaks[LBlockTypeInd][LClassInd].NumLeaks > 0 then
+ begin
+ {Need to add the header?}
+ if not LSmallLeakHeaderAdded then
+ begin
+ LMsgPtr := AppendStringToBuffer(SmallLeakDetail, LMsgPtr, Length(SmallLeakDetail));
+ LSmallLeakHeaderAdded := True;
+ end;
+ {Need to add the size header?}
+ if not LBlockSizeHeaderAdded then
+ begin
+ LMsgPtr^ := #13;
+ Inc(LMsgPtr);
+ LMsgPtr^ := #10;
+ Inc(LMsgPtr);
+ LMsgPtr := NativeUIntToStrBuf(LPreviousBlockSize + 1, LMsgPtr);
+ LMsgPtr^ := ' ';
+ Inc(LMsgPtr);
+ LMsgPtr^ := '-';
+ Inc(LMsgPtr);
+ LMsgPtr^ := ' ';
+ Inc(LMsgPtr);
+ LMsgPtr := NativeUIntToStrBuf(LThisBlockSize, LMsgPtr);
+ LMsgPtr := AppendStringToBuffer(BytesMessage, LMsgPtr, Length(BytesMessage));
+ LBlockSizeHeaderAdded := True;
+ end
+ else
+ begin
+ LMsgPtr^ := ',';
+ Inc(LMsgPtr);
+ LMsgPtr^ := ' ';
+ Inc(LMsgPtr);
+ end;
+ {Show the count}
+ case LClassInd of
+ {Unknown}
+ 0:
+ begin
+ LMsgPtr := AppendStringToBuffer(UnknownClassNameMsg, LMsgPtr, Length(UnknownClassNameMsg));
+ end;
+ {AnsiString}
+ 1:
+ begin
+ LMsgPtr := AppendStringToBuffer(AnsiStringBlockMessage, LMsgPtr, Length(AnsiStringBlockMessage));
+ end;
+ {UnicodeString}
+ 2:
+ begin
+ LMsgPtr := AppendStringToBuffer(UnicodeStringBlockMessage, LMsgPtr, Length(UnicodeStringBlockMessage));
+ end;
+ {Classes}
+ else
+ begin
+ {$ifdef CheckCppObjectTypeEnabled}
+ if LSmallBlockLeaks[LBlockTypeInd][LClassInd].CppTypeIdPtr <> nil then
+ begin
+ if Assigned(GetCppVirtObjTypeNameByTypeIdPtrFunc) then
+ begin
+ LCppTypeName := GetCppVirtObjTypeNameByTypeIdPtrFunc(LSmallBlockLeaks[LBlockTypeInd][LClassInd].CppTypeIdPtr);
+ LMsgPtr := AppendStringToBuffer(LCppTypeName, LMsgPtr, StrLen(LCppTypeName));
+ end
+ else
+ LMsgPtr := AppendClassNameToBuffer(nil, LMsgPtr);
+ end
+ else
+ begin
+ {$endif}
+ LMsgPtr := AppendClassNameToBuffer(LSmallBlockLeaks[LBlockTypeInd][LClassInd].ClassPointer, LMsgPtr);
+ {$ifdef CheckCppObjectTypeEnabled}
+ end;
+ {$endif}
+ end;
+ end;
+ {Add the count}
+ LMsgPtr^ := ' ';
+ Inc(LMsgPtr);
+ LMsgPtr^ := 'x';
+ Inc(LMsgPtr);
+ LMsgPtr^ := ' ';
+ Inc(LMsgPtr);
+ LMsgPtr := NativeUIntToStrBuf(LSmallBlockLeaks[LBlockTypeInd][LClassInd].NumLeaks, LMsgPtr);
+ end;
+ end;
+ LPreviousBlockSize := LThisBlockSize;
+ end;
+ {Add the medium/large block leak message}
+ if LNumMediumAndLargeLeaks > 0 then
+ begin
+ {Any non-small leaks?}
+ if LSmallLeakHeaderAdded then
+ begin
+ LMsgPtr^ := #13;
+ Inc(LMsgPtr);
+ LMsgPtr^ := #10;
+ Inc(LMsgPtr);
+ LMsgPtr^ := #13;
+ Inc(LMsgPtr);
+ LMsgPtr^ := #10;
+ Inc(LMsgPtr);
+ end;
+ {Add the medium/large block leak message}
+ LMsgPtr := AppendStringToBuffer(LargeLeakDetail, LMsgPtr, Length(LargeLeakDetail));
+ {List all the blocks}
+ for LBlockInd := 0 to LNumMediumAndLargeLeaks - 1 do
+ begin
+ if LBlockInd <> 0 then
+ begin
+ LMsgPtr^ := ',';
+ Inc(LMsgPtr);
+ LMsgPtr^ := ' ';
+ Inc(LMsgPtr);
+ end;
+ LMsgPtr := NativeUIntToStrBuf(LMediumAndLargeBlockLeaks[LBlockInd], LMsgPtr);
+ {Is there still space in the message buffer? Reserve space for the
+ message footer.}
+ if LMsgPtr > @LLeakMessage[High(LLeakMessage) - 2048] then
+ Break;
+ end;
+ end;
+ {$ifdef LogErrorsToFile}
+ {Set the message footer}
+ LMsgPtr := AppendStringToBuffer(LeakMessageFooter, LMsgPtr, Length(LeakMessageFooter));
+ {Append the message to the memory errors file}
+ AppendEventLog(@LLeakMessage[0], UIntPtr(LMsgPtr) - UIntPtr(@LLeakMessage[1]));
+ {$else}
+ {Set the message footer}
+ AppendStringToBuffer(LeakMessageFooter, LMsgPtr, Length(LeakMessageFooter));
+ {$endif}
+ {$ifdef UseOutputDebugString}
+ OutputDebugStringA(LLeakMessage);
+ {$endif}
+ {$ifndef NoMessageBoxes}
+ {Show the message}
+ AppendStringToModuleName(LeakMessageTitle, LMessageTitleBuffer);
+ ShowMessageBox(LLeakMessage, LMessageTitleBuffer);
+ {$endif}
+ end;
+ end;
+{$endif}
+end;
+
+{Returns statistics about the current state of the memory manager}
+procedure GetMemoryManagerState(var AMemoryManagerState: TMemoryManagerState);
+var
+ LPMediumBlockPoolHeader: PMediumBlockPoolHeader;
+ LPMediumBlock: Pointer;
+ LInd: Integer;
+ LBlockTypeIndex, LMediumBlockSize: Cardinal;
+ LMediumBlockHeader, LLargeBlockSize: NativeUInt;
+ LPLargeBlock: PLargeBlockHeader;
+begin
+ {Clear the structure}
+ FillChar(AMemoryManagerState, SizeOf(AMemoryManagerState), 0);
+ {Set the small block size stats}
+ for LInd := 0 to NumSmallBlockTypes - 1 do
+ begin
+ AMemoryManagerState.SmallBlockTypeStates[LInd].InternalBlockSize :=
+ SmallBlockTypes[LInd].BlockSize;
+ AMemoryManagerState.SmallBlockTypeStates[LInd].UseableBlockSize :=
+ SmallBlockTypes[LInd].BlockSize - BlockHeaderSize{$ifdef FullDebugMode} - FullDebugBlockOverhead{$endif};
+ if NativeInt(AMemoryManagerState.SmallBlockTypeStates[LInd].UseableBlockSize) < 0 then
+ AMemoryManagerState.SmallBlockTypeStates[LInd].UseableBlockSize := 0;
+ end;
+ {Lock all small block types}
+ LockAllSmallBlockTypes;
+ {Lock the medium blocks}
+ LockMediumBlocks;
+ {Step through all the medium block pools}
+ LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
+ while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
+ begin
+ {Add to the medium block used space}
+ Inc(AMemoryManagerState.ReservedMediumBlockAddressSpace, MediumBlockPoolSize);
+ LPMediumBlock := GetFirstMediumBlockInPool(LPMediumBlockPoolHeader);
+ while LPMediumBlock <> nil do
+ begin
+ LMediumBlockHeader := PNativeUInt(PByte(LPMediumBlock) - BlockHeaderSize)^;
+ {Is the block in use?}
+ if LMediumBlockHeader and IsFreeBlockFlag = 0 then
+ begin
+ {Get the block size}
+ LMediumBlockSize := LMediumBlockHeader and DropMediumAndLargeFlagsMask;
+ if (LMediumBlockHeader and IsSmallBlockPoolInUseFlag) <> 0 then
+ begin
+ {Get the block type index}
+ LBlockTypeIndex := (UIntPtr(PSmallBlockPoolHeader(LPMediumBlock).BlockType) - UIntPtr(@SmallBlockTypes[0])) div SizeOf(TSmallBlockType);
+ {Subtract from medium block usage}
+ Dec(AMemoryManagerState.ReservedMediumBlockAddressSpace, LMediumBlockSize);
+ {Add it to the reserved space for the block size}
+ Inc(AMemoryManagerState.SmallBlockTypeStates[LBlockTypeIndex].ReservedAddressSpace, LMediumBlockSize);
+ {Add the usage for the pool}
+ Inc(AMemoryManagerState.SmallBlockTypeStates[LBlockTypeIndex].AllocatedBlockCount,
+ PSmallBlockPoolHeader(LPMediumBlock).BlocksInUse);
+ end
+ else
+ begin
+{$ifdef FullDebugMode}
+ Dec(LMediumBlockSize, FullDebugBlockOverhead);
+{$endif}
+ Inc(AMemoryManagerState.AllocatedMediumBlockCount);
+ Inc(AMemoryManagerState.TotalAllocatedMediumBlockSize, LMediumBlockSize - BlockHeaderSize);
+ end;
+ end;
+ {Next medium block}
+ LPMediumBlock := NextMediumBlock(LPMediumBlock);
+ end;
+ {Get the next medium block pool}
+ LPMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
+ end;
+ {Unlock medium blocks}
+ MediumBlocksLocked := False;
+ {Unlock all the small block types}
+ for LInd := 0 to NumSmallBlockTypes - 1 do
+ SmallBlockTypes[LInd].BlockTypeLocked := False;
+ {Step through all the large blocks}
+ LockLargeBlocks;
+ LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
+ while LPLargeBlock <> @LargeBlocksCircularList do
+ begin
+ LLargeBlockSize := LPLargeBlock.BlockSizeAndFlags and DropMediumAndLargeFlagsMask;
+ Inc(AMemoryManagerState.AllocatedLargeBlockCount);
+ Inc(AMemoryManagerState.ReservedLargeBlockAddressSpace, LLargeBlockSize);
+ Inc(AMemoryManagerState.TotalAllocatedLargeBlockSize, LPLargeBlock.UserAllocatedSize);
+ {Get the next large block}
+ LPLargeBlock := LPLargeBlock.NextLargeBlockHeader;
+ end;
+ LargeBlocksLocked := False;
+end;
+
+{Returns a summary of the information returned by GetMemoryManagerState}
+procedure GetMemoryManagerUsageSummary(
+ var AMemoryManagerUsageSummary: TMemoryManagerUsageSummary);
+var
+ LMMS: TMemoryManagerState;
+ LAllocatedBytes, LReservedBytes: NativeUInt;
+ LSBTIndex: Integer;
+begin
+ {Get the memory manager state}
+ GetMemoryManagerState(LMMS);
+ {Add up the totals}
+ LAllocatedBytes := LMMS.TotalAllocatedMediumBlockSize
+ + LMMS.TotalAllocatedLargeBlockSize;
+ LReservedBytes := LMMS.ReservedMediumBlockAddressSpace
+ + LMMS.ReservedLargeBlockAddressSpace;
+ for LSBTIndex := 0 to NumSmallBlockTypes - 1 do
+ begin
+ Inc(LAllocatedBytes, LMMS.SmallBlockTypeStates[LSBTIndex].UseableBlockSize
+ * LMMS.SmallBlockTypeStates[LSBTIndex].AllocatedBlockCount);
+ Inc(LReservedBytes, LMMS.SmallBlockTypeStates[LSBTIndex].ReservedAddressSpace);
+ end;
+ {Set the structure values}
+ AMemoryManagerUsageSummary.AllocatedBytes := LAllocatedBytes;
+ AMemoryManagerUsageSummary.OverheadBytes := LReservedBytes - LAllocatedBytes;
+ if LReservedBytes > 0 then
+ begin
+ AMemoryManagerUsageSummary.EfficiencyPercentage :=
+ LAllocatedBytes / LReservedBytes * 100;
+ end
+ else
+ AMemoryManagerUsageSummary.EfficiencyPercentage := 100;
+end;
+
+{$ifndef POSIX}
+{Gets the state of every 64K block in the 4GB address space. Under 64-bit this
+ returns only the state for the low 4GB.}
+procedure GetMemoryMap(var AMemoryMap: TMemoryMap);
+var
+ LPMediumBlockPoolHeader: PMediumBlockPoolHeader;
+ LPLargeBlock: PLargeBlockHeader;
+ LInd, LChunkIndex, LNextChunk, LLargeBlockSize: NativeUInt;
+ LMBI: TMemoryBasicInformation;
+begin
+ {Clear the map}
+ FillChar(AMemoryMap, SizeOf(AMemoryMap), Ord(csUnallocated));
+ {Step through all the medium block pools}
+ LockMediumBlocks;
+ LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
+ while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
+ begin
+ {Add to the medium block used space}
+ LChunkIndex := NativeUInt(LPMediumBlockPoolHeader) shr 16;
+ for LInd := 0 to (MediumBlockPoolSize - 1) shr 16 do
+ begin
+ if (LChunkIndex + LInd) > High(AMemoryMap) then
+ Break;
+ AMemoryMap[LChunkIndex + LInd] := csAllocated;
+ end;
+ {Get the next medium block pool}
+ LPMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
+ end;
+ MediumBlocksLocked := False;
+ {Step through all the large blocks}
+ LockLargeBlocks;
+ LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
+ while LPLargeBlock <> @LargeBlocksCircularList do
+ begin
+ LChunkIndex := UIntPtr(LPLargeBlock) shr 16;
+ LLargeBlockSize := LPLargeBlock.BlockSizeAndFlags and DropMediumAndLargeFlagsMask;
+ for LInd := 0 to (LLargeBlockSize - 1) shr 16 do
+ begin
+ if (LChunkIndex + LInd) > High(AMemoryMap) then
+ Break;
+ AMemoryMap[LChunkIndex + LInd] := csAllocated;
+ end;
+ {Get the next large block}
+ LPLargeBlock := LPLargeBlock.NextLargeBlockHeader;
+ end;
+ LargeBlocksLocked := False;
+ {Fill in the rest of the map}
+ LInd := 0;
+ while LInd <= 65535 do
+ begin
+ {If the chunk is not allocated by this MM, what is its status?}
+ if AMemoryMap[LInd] = csUnallocated then
+ begin
+ {Query the address space starting at the chunk boundary}
+ if VirtualQuery(Pointer(LInd * 65536), LMBI, SizeOf(LMBI)) = 0 then
+ begin
+ {VirtualQuery may fail for addresses >2GB if a large address space is
+ not enabled.}
+ FillChar(AMemoryMap[LInd], 65536 - LInd, csSysReserved);
+ Break;
+ end;
+ {Get the chunk number after the region}
+ LNextChunk := (LMBI.RegionSize - 1) shr 16 + LInd + 1;
+ {Validate}
+ if LNextChunk > 65536 then
+ LNextChunk := 65536;
+ {Set the status of all the chunks in the region}
+ if LMBI.State = MEM_COMMIT then
+ begin
+ FillChar(AMemoryMap[LInd], LNextChunk - LInd, csSysAllocated);
+ end
+ else
+ begin
+ if LMBI.State = MEM_RESERVE then
+ FillChar(AMemoryMap[LInd], LNextChunk - LInd, csSysReserved);
+ end;
+ {Point to the start of the next chunk}
+ LInd := LNextChunk;
+ end
+ else
+ begin
+ {Next chunk}
+ Inc(LInd);
+ end;
+ end;
+end;
+{$endif}
+
+{Returns summarised information about the state of the memory manager. (For
+ backward compatibility.)}
+function FastGetHeapStatus: THeapStatus;
+var
+ LPMediumBlockPoolHeader: PMediumBlockPoolHeader;
+ LPMediumBlock: Pointer;
+ LBlockTypeIndex, LMediumBlockSize: Cardinal;
+ LSmallBlockUsage, LSmallBlockOverhead, LMediumBlockHeader, LLargeBlockSize: NativeUInt;
+ LInd: Integer;
+ LPLargeBlock: PLargeBlockHeader;
+begin
+ {Clear the structure}
+ FillChar(Result, SizeOf(Result), 0);
+ {Lock all small block types}
+ LockAllSmallBlockTypes;
+ {Lock the medium blocks}
+ LockMediumBlocks;
+ {Step through all the medium block pools}
+ LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
+ while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
+ begin
+ {Add to the total and committed address space}
+ Inc(Result.TotalAddrSpace, ((MediumBlockPoolSize + $ffff) and $ffff0000));
+ Inc(Result.TotalCommitted, ((MediumBlockPoolSize + $ffff) and $ffff0000));
+ {Add the medium block pool overhead}
+ Inc(Result.Overhead, (((MediumBlockPoolSize + $ffff) and $ffff0000)
+ - MediumBlockPoolSize + MediumBlockPoolHeaderSize));
+ {Get the first medium block in the pool}
+ LPMediumBlock := GetFirstMediumBlockInPool(LPMediumBlockPoolHeader);
+ while LPMediumBlock <> nil do
+ begin
+ {Get the block header}
+ LMediumBlockHeader := PNativeUInt(PByte(LPMediumBlock) - BlockHeaderSize)^;
+ {Get the block size}
+ LMediumBlockSize := LMediumBlockHeader and DropMediumAndLargeFlagsMask;
+ {Is the block in use?}
+ if LMediumBlockHeader and IsFreeBlockFlag = 0 then
+ begin
+ if (LMediumBlockHeader and IsSmallBlockPoolInUseFlag) <> 0 then
+ begin
+ {Get the block type index}
+ LBlockTypeIndex := (UIntPtr(PSmallBlockPoolHeader(LPMediumBlock).BlockType) - UIntPtr(@SmallBlockTypes[0])) div SizeOf(TSmallBlockType);
+ {Get the usage in the block}
+ LSmallBlockUsage := PSmallBlockPoolHeader(LPMediumBlock).BlocksInUse
+ * SmallBlockTypes[LBlockTypeIndex].BlockSize;
+ {Get the total overhead for all the small blocks}
+ LSmallBlockOverhead := PSmallBlockPoolHeader(LPMediumBlock).BlocksInUse
+ * (BlockHeaderSize{$ifdef FullDebugMode} + FullDebugBlockOverhead{$endif});
+ {Add to the totals}
+ Inc(Result.FreeSmall, LMediumBlockSize - LSmallBlockUsage - BlockHeaderSize);
+ Inc(Result.Overhead, LSmallBlockOverhead + BlockHeaderSize);
+ Inc(Result.TotalAllocated, LSmallBlockUsage - LSmallBlockOverhead);
+ end
+ else
+ begin
+{$ifdef FullDebugMode}
+ Dec(LMediumBlockSize, FullDebugBlockOverhead);
+ Inc(Result.Overhead, FullDebugBlockOverhead);
+{$endif}
+ {Add to the result}
+ Inc(Result.TotalAllocated, LMediumBlockSize - BlockHeaderSize);
+ Inc(Result.Overhead, BlockHeaderSize);
+ end;
+ end
+ else
+ begin
+ {The medium block is free}
+ Inc(Result.FreeBig, LMediumBlockSize);
+ end;
+ {Next medium block}
+ LPMediumBlock := NextMediumBlock(LPMediumBlock);
+ end;
+ {Get the next medium block pool}
+ LPMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
+ end;
+ {Add the sequential feed unused space}
+ Inc(Result.Unused, MediumSequentialFeedBytesLeft);
+ {Unlock the medium blocks}
+ MediumBlocksLocked := False;
+ {Unlock all the small block types}
+ for LInd := 0 to NumSmallBlockTypes - 1 do
+ SmallBlockTypes[LInd].BlockTypeLocked := False;
+ {Step through all the large blocks}
+ LockLargeBlocks;
+ LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
+ while LPLargeBlock <> @LargeBlocksCircularList do
+ begin
+ LLargeBlockSize := LPLargeBlock.BlockSizeAndFlags and DropMediumAndLargeFlagsMask;
+ Inc(Result.TotalAddrSpace, LLargeBlockSize);
+ Inc(Result.TotalCommitted, LLargeBlockSize);
+ Inc(Result.TotalAllocated, LPLargeBlock.UserAllocatedSize
+ {$ifdef FullDebugMode} - FullDebugBlockOverhead{$endif});
+ Inc(Result.Overhead, LLargeBlockSize - LPLargeBlock.UserAllocatedSize
+ {$ifdef FullDebugMode} + FullDebugBlockOverhead{$endif});
+ {Get the next large block}
+ LPLargeBlock := LPLargeBlock.NextLargeBlockHeader;
+ end;
+ LargeBlocksLocked := False;
+ {Set the total number of free bytes}
+ Result.TotalFree := Result.FreeSmall + Result.FreeBig + Result.Unused;
+end;
+
+{Frees all allocated memory. Does not support segmented large blocks (yet).}
+procedure FreeAllMemory;
+var
+ LPMediumBlockPoolHeader, LPNextMediumBlockPoolHeader: PMediumBlockPoolHeader;
+ LPMediumFreeBlock: PMediumFreeBlock;
+ LPLargeBlock, LPNextLargeBlock: PLargeBlockHeader;
+ LInd: Integer;
+begin
+ {Free all block pools}
+ LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
+ while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
+ begin
+ {Get the next medium block pool so long}
+ LPNextMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
+{$ifdef ClearMediumBlockPoolsBeforeReturningToOS}
+ FillChar(LPMediumBlockPoolHeader^, MediumBlockPoolSize, 0);
+{$else}
+ {$ifdef ClearSmallAndMediumBlocksInFreeMem}
+ FillChar(LPMediumBlockPoolHeader^, MediumBlockPoolSize, 0);
+ {$endif}
+{$endif}
+ {Free this pool}
+ VirtualFree(LPMediumBlockPoolHeader, 0, MEM_RELEASE);
+ {Next pool}
+ LPMediumBlockPoolHeader := LPNextMediumBlockPoolHeader;
+ end;
+ {Clear all small block types}
+ for LInd := 0 to High(SmallBlockTypes) do
+ begin
+ SmallBlockTypes[Lind].PreviousPartiallyFreePool := @SmallBlockTypes[Lind];
+ SmallBlockTypes[Lind].NextPartiallyFreePool := @SmallBlockTypes[Lind];
+ SmallBlockTypes[Lind].NextSequentialFeedBlockAddress := Pointer(1);
+ SmallBlockTypes[Lind].MaxSequentialFeedBlockAddress := nil;
+ end;
+ {Clear all medium block pools}
+ MediumBlockPoolsCircularList.PreviousMediumBlockPoolHeader := @MediumBlockPoolsCircularList;
+ MediumBlockPoolsCircularList.NextMediumBlockPoolHeader := @MediumBlockPoolsCircularList;
+ {All medium bins are empty}
+ for LInd := 0 to High(MediumBlockBins) do
+ begin
+ LPMediumFreeBlock := @MediumBlockBins[LInd];
+ LPMediumFreeBlock.PreviousFreeBlock := LPMediumFreeBlock;
+ LPMediumFreeBlock.NextFreeBlock := LPMediumFreeBlock;
+ end;
+ MediumBlockBinGroupBitmap := 0;
+ FillChar(MediumBlockBinBitmaps, SizeOf(MediumBlockBinBitmaps), 0);
+ MediumSequentialFeedBytesLeft := 0;
+ {Free all large blocks}
+ LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
+ while LPLargeBlock <> @LargeBlocksCircularList do
+ begin
+ {Get the next large block}
+ LPNextLargeBlock := LPLargeBlock.NextLargeBlockHeader;
+{$ifdef ClearLargeBlocksBeforeReturningToOS}
+ FillChar(LPLargeBlock^,
+ LPLargeBlock.BlockSizeAndFlags and DropMediumAndLargeFlagsMask, 0);
+{$endif}
+ {Free this large block}
+ VirtualFree(LPLargeBlock, 0, MEM_RELEASE);
+ {Next large block}
+ LPLargeBlock := LPNextLargeBlock;
+ end;
+ {There are no large blocks allocated}
+ LargeBlocksCircularList.PreviousLargeBlockHeader := @LargeBlocksCircularList;
+ LargeBlocksCircularList.NextLargeBlockHeader := @LargeBlocksCircularList;
+end;
+
+{----------------------------Memory Manager Setup-----------------------------}
+
+{Checks that no other memory manager has been installed after the RTL MM and
+ that there are currently no live pointers allocated through the RTL MM.}
+function CheckCanInstallMemoryManager: Boolean;
+{$ifndef NoMessageBoxes}
+var
+ LErrorMessageTitle: array[0..1023] of AnsiChar;
+{$endif}
+begin
+ {Default to error}
+ Result := False;
+{$ifdef FullDebugMode}
+ {$ifdef LoadDebugDLLDynamically}
+ {$ifdef DoNotInstallIfDLLMissing}
+ {Should FastMM be installed only if the FastMM_FullDebugMode.dll file is
+ available?}
+ if FullDebugModeDLL = 0 then
+ Exit;
+ {$endif}
+ {$endif}
+{$endif}
+ {Is FastMM already installed?}
+ if FastMMIsInstalled then
+ begin
+{$ifdef UseOutputDebugString}
+ OutputDebugStringA(AlreadyInstalledMsg);
+{$endif}
+{$ifndef NoMessageBoxes}
+ AppendStringToModuleName(AlreadyInstalledTitle, LErrorMessageTitle);
+ ShowMessageBox(AlreadyInstalledMsg, LErrorMessageTitle);
+{$endif}
+ Exit;
+ end;
+ {Has another MM been set, or has the Embarcadero MM been used? If so, this
+ file is not the first unit in the uses clause of the project's .dpr file.}
+ if IsMemoryManagerSet then
+ begin
+ {When using runtime packages, another library may already have installed
+ FastMM: Silently ignore the installation request.}
+{$ifndef UseRuntimePackages}
+ {Another memory manager has been set.}
+ {$ifdef UseOutputDebugString}
+ OutputDebugStringA(OtherMMInstalledMsg);
+ {$endif}
+ {$ifndef NoMessageBoxes}
+ AppendStringToModuleName(OtherMMInstalledTitle, LErrorMessageTitle);
+ ShowMessageBox(OtherMMInstalledMsg, LErrorMessageTitle);
+ {$endif}
+{$endif}
+ Exit;
+ end;
+{$ifndef POSIX}
+ if GetHeapStatus.TotalAllocated <> 0 then
+ begin
+ {Memory has been already been allocated with the RTL MM}
+{$ifdef UseOutputDebugString}
+ OutputDebugStringA(MemoryAllocatedMsg);
+{$endif}
+ {$ifndef NoMessageBoxes}
+ AppendStringToModuleName(MemoryAllocatedTitle, LErrorMessageTitle);
+ ShowMessageBox(MemoryAllocatedMsg, LErrorMessageTitle);
+ {$endif}
+ Exit;
+ end;
+{$endif}
+ {All OK}
+ Result := True;
+end;
+
+{Initializes the lookup tables for the memory manager}
+procedure InitializeMemoryManager;
+const
+ {The size of the Inc(VMTIndex) code in TFreedObject.GetVirtualMethodIndex}
+ VMTIndexIncCodeSize = 6;
+var
+ LInd, LSizeInd, LMinimumPoolSize, LOptimalPoolSize, LGroupNumber,
+ LBlocksPerPool, LPreviousBlockSize: Cardinal;
+ LPMediumFreeBlock: PMediumFreeBlock;
+begin
+{$ifdef FullDebugMode}
+ {$ifdef LoadDebugDLLDynamically}
+ {Attempt to load the FullDebugMode DLL dynamically.}
+ FullDebugModeDLL := LoadLibrary(FullDebugModeLibraryName);
+ if FullDebugModeDLL <> 0 then
+ begin
+ GetStackTrace := GetProcAddress(FullDebugModeDLL,
+ {$ifdef RawStackTraces}'GetRawStackTrace'{$else}'GetFrameBasedStackTrace'{$endif});
+ LogStackTrace := GetProcAddress(FullDebugModeDLL, 'LogStackTrace');
+ end;
+ {$endif}
+{$endif}
+{$ifdef EnableMMX}
+ {$ifndef ForceMMX}
+ UseMMX := MMX_Supported;
+ {$endif}
+{$endif}
+ {Initialize the memory manager}
+ {-------------Set up the small block types-------------}
+ LPreviousBlockSize := 0;
+ for LInd := 0 to High(SmallBlockTypes) do
+ begin
+ {Set the move procedure}
+{$ifdef UseCustomFixedSizeMoveRoutines}
+ {The upsize move procedure may move chunks in 16 bytes even with 8-byte
+ alignment, since the new size will always be at least 8 bytes bigger than
+ the old size.}
+ if not Assigned(SmallBlockTypes[LInd].UpsizeMoveProcedure) then
+ {$ifdef UseCustomVariableSizeMoveRoutines}
+ SmallBlockTypes[LInd].UpsizeMoveProcedure := MoveX16LP;
+ {$else}
+ SmallBlockTypes[LInd].UpsizeMoveProcedure := @System.Move;
+ {$endif}
+{$endif}
+ {Set the first "available pool" to the block type itself, so that the
+ allocation routines know that there are currently no pools with free
+ blocks of this size.}
+ SmallBlockTypes[LInd].PreviousPartiallyFreePool := @SmallBlockTypes[LInd];
+ SmallBlockTypes[LInd].NextPartiallyFreePool := @SmallBlockTypes[LInd];
+ {Set the block size to block type index translation table}
+ for LSizeInd := (LPreviousBlockSize div SmallBlockGranularity) to ((SmallBlockTypes[LInd].BlockSize - 1) div SmallBlockGranularity) do
+ AllocSize2SmallBlockTypeIndX4[LSizeInd] := LInd * 4;
+ {Cannot sequential feed yet: Ensure that the next address is greater than
+ the maximum address}
+ SmallBlockTypes[LInd].MaxSequentialFeedBlockAddress := Pointer(0);
+ SmallBlockTypes[LInd].NextSequentialFeedBlockAddress := Pointer(1);
+ {Get the mask to use for finding a medium block suitable for a block pool}
+ LMinimumPoolSize :=
+ ((SmallBlockTypes[LInd].BlockSize * MinimumSmallBlocksPerPool
+ + SmallBlockPoolHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset)
+ and -MediumBlockGranularity) + MediumBlockSizeOffset;
+ if LMinimumPoolSize < MinimumMediumBlockSize then
+ LMinimumPoolSize := MinimumMediumBlockSize;
+ {Get the closest group number for the minimum pool size}
+ LGroupNumber := (LMinimumPoolSize - MinimumMediumBlockSize + MediumBlockBinsPerGroup * MediumBlockGranularity div 2)
+ div (MediumBlockBinsPerGroup * MediumBlockGranularity);
+ {Too large?}
+ if LGroupNumber > 7 then
+ LGroupNumber := 7;
+ {Set the bitmap}
+ SmallBlockTypes[LInd].AllowedGroupsForBlockPoolBitmap := Byte(-(1 shl LGroupNumber));
+ {Set the minimum pool size}
+ SmallBlockTypes[LInd].MinimumBlockPoolSize := MinimumMediumBlockSize + LGroupNumber * (MediumBlockBinsPerGroup * MediumBlockGranularity);
+ {Get the optimal block pool size}
+ LOptimalPoolSize := ((SmallBlockTypes[LInd].BlockSize * TargetSmallBlocksPerPool
+ + SmallBlockPoolHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset)
+ and -MediumBlockGranularity) + MediumBlockSizeOffset;
+ {Limit the optimal pool size to within range}
+ if LOptimalPoolSize < OptimalSmallBlockPoolSizeLowerLimit then
+ LOptimalPoolSize := OptimalSmallBlockPoolSizeLowerLimit;
+ if LOptimalPoolSize > OptimalSmallBlockPoolSizeUpperLimit then
+ LOptimalPoolSize := OptimalSmallBlockPoolSizeUpperLimit;
+ {How many blocks will fit in the adjusted optimal size?}
+ LBlocksPerPool := (LOptimalPoolSize - SmallBlockPoolHeaderSize) div SmallBlockTypes[LInd].BlockSize;
+ {Recalculate the optimal pool size to minimize wastage due to a partial
+ last block.}
+ SmallBlockTypes[LInd].OptimalBlockPoolSize :=
+ ((LBlocksPerPool * SmallBlockTypes[LInd].BlockSize + SmallBlockPoolHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset) and -MediumBlockGranularity) + MediumBlockSizeOffset;
+{$ifdef CheckHeapForCorruption}
+ {Debug checks}
+ if (SmallBlockTypes[LInd].OptimalBlockPoolSize < MinimumMediumBlockSize)
+ or (SmallBlockTypes[LInd].BlockSize div SmallBlockGranularity * SmallBlockGranularity <> SmallBlockTypes[LInd].BlockSize) then
+ begin
+ {$ifdef BCB6OrDelphi7AndUp}
+ System.Error(reInvalidPtr);
+ {$else}
+ System.RunError(reInvalidPtr);
+ {$endif}
+ end;
+{$endif}
+ {Set the previous small block size}
+ LPreviousBlockSize := SmallBlockTypes[LInd].BlockSize;
+ end;
+ {-------------------Set up the medium blocks-------------------}
+{$ifdef CheckHeapForCorruption}
+ {Check that there are no gaps between where the small blocks end and the
+ medium blocks start}
+ if (((MaximumSmallBlockSize - 3) + (MediumBlockGranularity - 1 + BlockHeaderSize - MediumBlockSizeOffset))
+ and -MediumBlockGranularity) + MediumBlockSizeOffset < MinimumMediumBlockSize then
+ begin
+ {$ifdef BCB6OrDelphi7AndUp}
+ System.Error(reInvalidPtr);
+ {$else}
+ System.RunError(reInvalidPtr);
+ {$endif}
+ end;
+{$endif}
+ {There are currently no medium block pools}
+ MediumBlockPoolsCircularList.PreviousMediumBlockPoolHeader := @MediumBlockPoolsCircularList;
+ MediumBlockPoolsCircularList.NextMediumBlockPoolHeader := @MediumBlockPoolsCircularList;
+ {All medium bins are empty}
+ for LInd := 0 to High(MediumBlockBins) do
+ begin
+ LPMediumFreeBlock := @MediumBlockBins[LInd];
+ LPMediumFreeBlock.PreviousFreeBlock := LPMediumFreeBlock;
+ LPMediumFreeBlock.NextFreeBlock := LPMediumFreeBlock;
+ end;
+ {------------------Set up the large blocks---------------------}
+ LargeBlocksCircularList.PreviousLargeBlockHeader := @LargeBlocksCircularList;
+ LargeBlocksCircularList.NextLargeBlockHeader := @LargeBlocksCircularList;
+ {------------------Set up the debugging structures---------------------}
+{$ifdef FullDebugMode}
+ {Set up the fake VMT}
+ {Copy the basic info from the TFreedObject class}
+ System.Move(Pointer(PByte(TFreedObject) + vmtSelfPtr + SizeOf(Pointer))^,
+ FreedObjectVMT.VMTData[vmtSelfPtr + SizeOf(Pointer)], vmtParent - vmtSelfPtr);
+ PNativeUInt(@FreedObjectVMT.VMTData[vmtSelfPtr])^ := NativeUInt(@FreedObjectVMT.VMTMethods[0]);
+ {Set up the virtual method table}
+ for LInd := 0 to MaxFakeVMTEntries - 1 do
+ begin
+ PNativeUInt(@FreedObjectVMT.VMTMethods[Low(FreedObjectVMT.VMTMethods) + Integer(LInd * SizeOf(Pointer))])^ :=
+ NativeUInt(@TFreedObject.GetVirtualMethodIndex) + LInd * VMTIndexIncCodeSize;
+ {$ifdef CatchUseOfFreedInterfaces}
+ VMTBadInterface[LInd] := @TFreedObject.InterfaceError;
+ {$endif}
+ end;
+ {Set up the default log file name}
+ SetDefaultMMLogFileName;
+{$endif}
+end;
+
+{Installs the memory manager (InitializeMemoryManager should be called first)}
+procedure InstallMemoryManager;
+{$ifdef MMSharingEnabled}
+var
+ i, LCurrentProcessID: Cardinal;
+ LPMapAddress: PPointer;
+ LChar: AnsiChar;
+{$endif}
+begin
+ if not FastMMIsInstalled then
+ begin
+{$ifdef FullDebugMode}
+ {$ifdef 32Bit}
+ {Try to reserve the 64K block covering address $80808080}
+ ReservedBlock := VirtualAlloc(Pointer(DebugReservedAddress), 65536, MEM_RESERVE, PAGE_NOACCESS);
+ {$endif}
+{$endif}
+{$ifdef MMSharingEnabled}
+ {Build a string identifying the current process}
+ LCurrentProcessID := GetCurrentProcessId;
+ for i := 0 to 7 do
+ begin
+ LChar := HexTable[((LCurrentProcessID shr (i * 4)) and $F)];
+ MappingObjectName[(High(MappingObjectName) - 1) - i] := LChar;
+ {$ifdef EnableBackwardCompatibleMMSharing}
+ UniqueProcessIDString[8 - i] := LChar;
+ UniqueProcessIDStringBE[8 - i] := LChar;
+ {$endif}
+ end;
+{$endif}
+{$ifdef AttemptToUseSharedMM}
+ {Is the replacement memory manager already installed for this process?}
+{$ifdef EnableBackwardCompatibleMMSharing}
+ MMWindow := FindWindowA('STATIC', PAnsiChar(@UniqueProcessIDString[1]));
+ MMWindowBE := FindWindowA('STATIC', PAnsiChar(@UniqueProcessIDStringBE[1]));
+{$endif}
+ MappingObjectHandle := OpenFileMappingA(FILE_MAP_READ, False, MappingObjectName);
+ {Is no MM being shared?}
+{$ifdef EnableBackwardCompatibleMMSharing}
+ if (MMWindow or MMWindowBE or MappingObjectHandle) = 0 then
+{$else}
+ if MappingObjectHandle = 0 then
+{$endif}
+ begin
+{$endif}
+{$ifdef ShareMM}
+ {Share the MM with other DLLs? - if this DLL is unloaded, then
+ dependent DLLs will cause a crash.}
+ {$ifndef ShareMMIfLibrary}
+ if not IsLibrary then
+ {$endif}
+ begin
+ {$ifdef EnableBackwardCompatibleMMSharing}
+ {No memory manager installed yet - create the invisible window}
+ MMWindow := CreateWindowA('STATIC', PAnsiChar(@UniqueProcessIDString[1]),
+ WS_POPUP, 0, 0, 0, 0, 0, 0, hInstance, nil);
+ MMWindowBE := CreateWindowA('STATIC', PAnsiChar(@UniqueProcessIDStringBE[1]),
+ WS_POPUP, 0, 0, 0, 0, 0, 0, hInstance, nil);
+ {The window data is a pointer to this memory manager}
+ if MMWindow <> 0 then
+ SetWindowLongA(MMWindow, GWL_USERDATA, NativeInt(@NewMemoryManager));
+ if MMWindowBE <> 0 then
+ SetWindowLongA(MMWindowBE, GWL_USERDATA, NativeInt(@NewMemoryManager));
+ {$endif}
+ {Create the memory mapped file}
+ MappingObjectHandle := CreateFileMappingA(INVALID_HANDLE_VALUE, nil,
+ PAGE_READWRITE, 0, SizeOf(Pointer), MappingObjectName);
+ {Map a view of the memory}
+ LPMapAddress := MapViewOfFile(MappingObjectHandle, FILE_MAP_WRITE, 0, 0, 0);
+ {Set a pointer to the new memory manager}
+ LPMapAddress^ := @NewMemoryManager;
+ {Unmap the file}
+ UnmapViewOfFile(LPMapAddress);
+ end;
+{$endif}
+ {We will be using this memory manager}
+{$ifndef FullDebugMode}
+ NewMemoryManager.GetMem := FastGetMem;
+ NewMemoryManager.FreeMem := FastFreeMem;
+ NewMemoryManager.ReallocMem := FastReallocMem;
+{$else}
+ NewMemoryManager.GetMem := DebugGetMem;
+ NewMemoryManager.FreeMem := DebugFreeMem;
+ NewMemoryManager.ReallocMem := DebugReallocMem;
+{$endif}
+{$ifdef BDS2006AndUp}
+ {$ifndef FullDebugMode}
+ NewMemoryManager.AllocMem := FastAllocMem;
+ {$else}
+ NewMemoryManager.AllocMem := DebugAllocMem;
+ {$endif}
+ {$ifdef EnableMemoryLeakReporting}
+ NewMemoryManager.RegisterExpectedMemoryLeak := RegisterExpectedMemoryLeak;
+ NewMemoryManager.UnRegisterExpectedMemoryLeak := UnRegisterExpectedMemoryLeak;
+ {$else}
+ NewMemoryManager.RegisterExpectedMemoryLeak := NoOpRegisterExpectedMemoryLeak;
+ NewMemoryManager.UnRegisterExpectedMemoryLeak := NoOpUnRegisterExpectedMemoryLeak;
+ {$endif}
+{$endif}
+ {Owns the memory manager}
+ IsMemoryManagerOwner := True;
+{$ifdef AttemptToUseSharedMM}
+ end
+ else
+ begin
+ {Get the address of the shared memory manager}
+ {$ifndef BDS2006AndUp}
+ {$ifdef EnableBackwardCompatibleMMSharing}
+ if MappingObjectHandle <> 0 then
+ begin
+ {$endif}
+ {Map a view of the memory}
+ LPMapAddress := MapViewOfFile(MappingObjectHandle, FILE_MAP_READ, 0, 0, 0);
+ {Set the new memory manager}
+ NewMemoryManager := PMemoryManager(LPMapAddress^)^;
+ {Unmap the file}
+ UnmapViewOfFile(LPMapAddress);
+ {$ifdef EnableBackwardCompatibleMMSharing}
+ end
+ else
+ begin
+ if MMWindow <> 0 then
+ begin
+ NewMemoryManager := PMemoryManager(GetWindowLong(MMWindow, GWL_USERDATA))^;
+ end
+ else
+ begin
+ NewMemoryManager := PMemoryManager(GetWindowLong(MMWindowBE, GWL_USERDATA))^;
+ end;
+ end;
+ {$endif}
+ {$else}
+ {$ifdef EnableBackwardCompatibleMMSharing}
+ if MappingObjectHandle <> 0 then
+ begin
+ {$endif}
+ {Map a view of the memory}
+ LPMapAddress := MapViewOfFile(MappingObjectHandle, FILE_MAP_READ, 0, 0, 0);
+ {Set the new memory manager}
+ NewMemoryManager := PMemoryManagerEx(LPMapAddress^)^;
+ {Unmap the file}
+ UnmapViewOfFile(LPMapAddress);
+ {$ifdef EnableBackwardCompatibleMMSharing}
+ end
+ else
+ begin
+ if MMWindow <> 0 then
+ begin
+ NewMemoryManager := PMemoryManagerEx(GetWindowLong(MMWindow, GWL_USERDATA))^;
+ end
+ else
+ begin
+ NewMemoryManager := PMemoryManagerEx(GetWindowLong(MMWindowBE, GWL_USERDATA))^;
+ end;
+ end;
+ {$endif}
+ {$endif}
+ {Close the file mapping handle}
+ CloseHandle(MappingObjectHandle);
+ MappingObjectHandle := 0;
+ {The memory manager is not owned by this module}
+ IsMemoryManagerOwner := False;
+ end;
+{$endif}
+ {Save the old memory manager}
+ GetMemoryManager(OldMemoryManager);
+ {Replace the memory manager with either this one or the shared one.}
+ SetMemoryManager(NewMemoryManager);
+ {FastMM is now installed}
+ FastMMIsInstalled := True;
+{$ifdef UseOutputDebugString}
+ if IsMemoryManagerOwner then
+ OutputDebugStringA(FastMMInstallMsg)
+ else
+ OutputDebugStringA(FastMMInstallSharedMsg);
+{$endif}
+ end;
+end;
+
+procedure UninstallMemoryManager;
+begin
+ {Is this the owner of the shared MM window?}
+ if IsMemoryManagerOwner then
+ begin
+{$ifdef ShareMM}
+ {$ifdef EnableBackwardCompatibleMMSharing}
+ {Destroy the window}
+ if MMWindow <> 0 then
+ begin
+ DestroyWindow(MMWindow);
+ MMWindow := 0;
+ end;
+ if MMWindowBE <> 0 then
+ begin
+ DestroyWindow(MMWindowBE);
+ MMWindowBE := 0;
+ end;
+ {$endif}
+ {Destroy the memory mapped file handle}
+ if MappingObjectHandle <> 0 then
+ begin
+ CloseHandle(MappingObjectHandle);
+ MappingObjectHandle := 0;
+ end;
+{$endif}
+{$ifdef FullDebugMode}
+ {Release the reserved block}
+ if ReservedBlock <> nil then
+ begin
+ VirtualFree(ReservedBlock, 0, MEM_RELEASE);
+ ReservedBlock := nil;
+ end;
+{$endif}
+ end;
+{$ifndef DetectMMOperationsAfterUninstall}
+ {Restore the old memory manager}
+ SetMemoryManager(OldMemoryManager);
+{$else}
+ {Set the invalid memory manager: no more MM operations allowed}
+ SetMemoryManager(InvalidMemoryManager);
+{$endif}
+ {Memory manager has been uninstalled}
+ FastMMIsInstalled := False;
+{$ifdef UseOutputDebugString}
+ if IsMemoryManagerOwner then
+ OutputDebugStringA(FastMMUninstallMsg)
+ else
+ OutputDebugStringA(FastMMUninstallSharedMsg);
+{$endif}
+end;
+
+procedure FinalizeMemoryManager;
+begin
+ {Restore the old memory manager if FastMM has been installed}
+ if FastMMIsInstalled then
+ begin
+{$ifndef NeverUninstall}
+ {Uninstall FastMM}
+ UninstallMemoryManager;
+{$endif}
+ {Do we own the memory manager, or are we just sharing it?}
+ if IsMemoryManagerOwner then
+ begin
+{$ifdef CheckUseOfFreedBlocksOnShutdown}
+ CheckBlocksOnShutdown(
+ {$ifdef EnableMemoryLeakReporting}
+ True
+ {$ifdef RequireIDEPresenceForLeakReporting}
+ and DelphiIsRunning
+ {$endif}
+ {$ifdef RequireDebuggerPresenceForLeakReporting}
+ and ((DebugHook <> 0)
+ {$ifdef PatchBCBTerminate}
+ or (Assigned(pCppDebugHook) and (pCppDebugHook^ <> 0))
+ {$endif PatchBCBTerminate}
+ )
+ {$endif}
+ {$ifdef ManualLeakReportingControl}
+ and ReportMemoryLeaksOnShutdown
+ {$endif}
+ {$else}
+ False
+ {$endif}
+ );
+{$else}
+ {$ifdef EnableMemoryLeakReporting}
+ if True
+ {$ifdef RequireIDEPresenceForLeakReporting}
+ and DelphiIsRunning
+ {$endif}
+ {$ifdef RequireDebuggerPresenceForLeakReporting}
+ and ((DebugHook <> 0)
+ {$ifdef PatchBCBTerminate}
+ or (Assigned(pCppDebugHook) and (pCppDebugHook^ <> 0))
+ {$endif PatchBCBTerminate}
+ )
+ {$endif}
+ {$ifdef ManualLeakReportingControl}
+ and ReportMemoryLeaksOnShutdown
+ {$endif}
+ then
+ CheckBlocksOnShutdown(True);
+ {$endif}
+{$endif}
+{$ifdef EnableMemoryLeakReporting}
+ {Free the expected memory leaks list}
+ if ExpectedMemoryLeaks <> nil then
+ begin
+ VirtualFree(ExpectedMemoryLeaks, 0, MEM_RELEASE);
+ ExpectedMemoryLeaks := nil;
+ end;
+{$endif}
+{$ifndef NeverUninstall}
+ {Clean up: Free all memory. If this is a .DLL that owns its own MM, then
+ it is necessary to prevent the main application from running out of
+ address space.}
+ FreeAllMemory;
+{$endif}
+ end;
+ end;
+end;
+
+procedure RunInitializationCode;
+begin
+ {Only run this code once during startup.}
+ if InitializationCodeHasRun then
+ Exit;
+ InitializationCodeHasRun := True;
+{$ifndef BCB}
+ {$ifdef InstallOnlyIfRunningInIDE}
+ if (DebugHook <> 0) and DelphiIsRunning then
+ {$endif}
+ begin
+ {Initialize all the lookup tables, etc. for the memory manager}
+ InitializeMemoryManager;
+ {Has another MM been set, or has the Embarcadero MM been used? If so, this
+ file is not the first unit in the uses clause of the project's .dpr
+ file.}
+ if CheckCanInstallMemoryManager then
+ begin
+ {$ifdef ClearLogFileOnStartup}
+ DeleteEventLog;
+ {$endif}
+ InstallMemoryManager;
+ end;
+ end;
+{$endif}
+end;
+
+initialization
+ RunInitializationCode;
+
+finalization
+{$ifndef PatchBCBTerminate}
+ FinalizeMemoryManager;
+{$endif}
+
+end. \ No newline at end of file
diff --git a/plugins/Libs/FastMM4Messages.pas b/plugins/Libs/FastMM4Messages.pas
new file mode 100644
index 0000000000..2dd0bee1a2
--- /dev/null
+++ b/plugins/Libs/FastMM4Messages.pas
@@ -0,0 +1,135 @@
+{
+
+Fast Memory Manager: Messages
+
+English translation by Pierre le Riche.
+
+}
+
+unit FastMM4Messages;
+
+interface
+
+{$Include FastMM4Options.inc}
+
+const
+ {The name of the debug info support DLL}
+ FullDebugModeLibraryName32Bit = 'FastMM_FullDebugMode.dll';
+ FullDebugModeLibraryName64Bit = 'FastMM_FullDebugMode64.dll';
+ {Event log strings}
+ LogFileExtension = '_MemoryManager_EventLog.txt'#0;
+ CRLF = #13#10;
+ EventSeparator = '--------------------------------';
+ {Class name messages}
+ UnknownClassNameMsg = 'Unknown';
+ {Memory dump message}
+ MemoryDumpMsg = #13#10#13#10'Current memory dump of 256 bytes starting at pointer address ';
+ {Block Error Messages}
+ BlockScanLogHeader = 'Allocated block logged by LogAllocatedBlocksToFile. The size is: ';
+ ErrorMsgHeader = 'FastMM has detected an error during a ';
+ GetMemMsg = 'GetMem';
+ FreeMemMsg = 'FreeMem';
+ ReallocMemMsg = 'ReallocMem';
+ BlockCheckMsg = 'free block scan';
+ OperationMsg = ' operation. ';
+ BlockHeaderCorruptedMsg = 'The block header has been corrupted. ';
+ BlockFooterCorruptedMsg = 'The block footer has been corrupted. ';
+ FreeModifiedErrorMsg = 'FastMM detected that a block has been modified after being freed. ';
+ FreeModifiedDetailMsg = #13#10#13#10'Modified byte offsets (and lengths): ';
+ DoubleFreeErrorMsg = 'An attempt has been made to free/reallocate an unallocated block.';
+ WrongMMFreeErrorMsg = 'An attempt has been made to free/reallocate a block that was allocated through a different FastMM instance. Check your memory manager sharing settings.';
+ PreviousBlockSizeMsg = #13#10#13#10'The previous block size was: ';
+ CurrentBlockSizeMsg = #13#10#13#10'The block size is: ';
+ PreviousObjectClassMsg = #13#10#13#10'The block was previously used for an object of class: ';
+ CurrentObjectClassMsg = #13#10#13#10'The block is currently used for an object of class: ';
+ PreviousAllocationGroupMsg = #13#10#13#10'The allocation group was: ';
+ PreviousAllocationNumberMsg = #13#10#13#10'The allocation number was: ';
+ CurrentAllocationGroupMsg = #13#10#13#10'The allocation group is: ';
+ CurrentAllocationNumberMsg = #13#10#13#10'The allocation number is: ';
+ BlockErrorMsgTitle = 'Memory Error Detected';
+ VirtualMethodErrorHeader = 'FastMM has detected an attempt to call a virtual method on a freed object. An access violation will now be raised in order to abort the current operation.';
+ InterfaceErrorHeader = 'FastMM has detected an attempt to use an interface of a freed object. An access violation will now be raised in order to abort the current operation.';
+ BlockHeaderCorruptedNoHistoryMsg = ' Unfortunately the block header has been corrupted so no history is available.';
+ FreedObjectClassMsg = #13#10#13#10'Freed object class: ';
+ VirtualMethodName = #13#10#13#10'Virtual method: ';
+ VirtualMethodOffset = 'Offset +';
+ VirtualMethodAddress = #13#10#13#10'Virtual method address: ';
+ {Stack trace messages}
+ CurrentThreadIDMsg = #13#10#13#10'The current thread ID is 0x';
+ CurrentStackTraceMsg = ', and the stack trace (return addresses) leading to this error is:';
+ ThreadIDPrevAllocMsg = #13#10#13#10'This block was previously allocated by thread 0x';
+ ThreadIDAtAllocMsg = #13#10#13#10'This block was allocated by thread 0x';
+ ThreadIDAtFreeMsg = #13#10#13#10'The block was previously freed by thread 0x';
+ ThreadIDAtObjectAllocMsg = #13#10#13#10'The object was allocated by thread 0x';
+ ThreadIDAtObjectFreeMsg = #13#10#13#10'The object was subsequently freed by thread 0x';
+ StackTraceMsg = ', and the stack trace (return addresses) at the time was:';
+ {Installation Messages}
+ AlreadyInstalledMsg = 'FastMM4 is already installed.';
+ AlreadyInstalledTitle = 'Already installed.';
+ OtherMMInstalledMsg = 'FastMM4 cannot be installed since another third party memory '
+ + 'manager has already installed itself.'#13#10'If you want to use FastMM4, '
+ + 'please make sure that FastMM4.pas is the very first unit in the "uses"'
+ + #13#10'section of your project''s .dpr file.';
+ OtherMMInstalledTitle = 'Cannot install FastMM4 - Another memory manager is already installed';
+ MemoryAllocatedMsg = 'FastMM4 cannot install since memory has already been '
+ + 'allocated through the default memory manager.'#13#10'FastMM4.pas MUST '
+ + 'be the first unit in your project''s .dpr file, otherwise memory may '
+ + 'be allocated'#13#10'through the default memory manager before FastMM4 '
+ + 'gains control. '#13#10#13#10'If you are using an exception trapper '
+ + 'like MadExcept (or any tool that modifies the unit initialization '
+ + 'order),'#13#10'go into its configuration page and ensure that the '
+ + 'FastMM4.pas unit is initialized before any other unit.';
+ MemoryAllocatedTitle = 'Cannot install FastMM4 - Memory has already been allocated';
+ {Leak checking messages}
+ LeakLogHeader = 'A memory block has been leaked. The size is: ';
+ LeakMessageHeader = 'This application has leaked memory. ';
+ SmallLeakDetail = 'The small block leaks are'
+{$ifdef HideExpectedLeaksRegisteredByPointer}
+ + ' (excluding expected leaks registered by pointer)'
+{$endif}
+ + ':'#13#10;
+ LargeLeakDetail = 'The sizes of leaked medium and large blocks are'
+{$ifdef HideExpectedLeaksRegisteredByPointer}
+ + ' (excluding expected leaks registered by pointer)'
+{$endif}
+ + ': ';
+ BytesMessage = ' bytes: ';
+ AnsiStringBlockMessage = 'AnsiString';
+ UnicodeStringBlockMessage = 'UnicodeString';
+ LeakMessageFooter = #13#10
+{$ifndef HideMemoryLeakHintMessage}
+ + #13#10'Note: '
+ {$ifdef RequireIDEPresenceForLeakReporting}
+ + 'This memory leak check is only performed if Delphi is currently running on the same computer. '
+ {$endif}
+ {$ifdef FullDebugMode}
+ {$ifdef LogMemoryLeakDetailToFile}
+ + 'Memory leak detail is logged to a text file in the same folder as this application. '
+ {$else}
+ + 'Enable the "LogMemoryLeakDetailToFile" to obtain a log file containing detail on memory leaks. '
+ {$endif}
+ {$else}
+ + 'To obtain a log file containing detail on memory leaks, enable the "FullDebugMode" and "LogMemoryLeakDetailToFile" conditional defines. '
+ {$endif}
+ + 'To disable this memory leak check, undefine "EnableMemoryLeakReporting".'#13#10
+{$endif}
+ + #0;
+ LeakMessageTitle = 'Memory Leak Detected';
+{$ifdef UseOutputDebugString}
+ FastMMInstallMsg = 'FastMM has been installed.';
+ FastMMInstallSharedMsg = 'Sharing an existing instance of FastMM.';
+ FastMMUninstallMsg = 'FastMM has been uninstalled.';
+ FastMMUninstallSharedMsg = 'Stopped sharing an existing instance of FastMM.';
+{$endif}
+{$ifdef DetectMMOperationsAfterUninstall}
+ InvalidOperationTitle = 'MM Operation after uninstall.';
+ InvalidGetMemMsg = 'FastMM has detected a GetMem call after FastMM was uninstalled.';
+ InvalidFreeMemMsg = 'FastMM has detected a FreeMem call after FastMM was uninstalled.';
+ InvalidReallocMemMsg = 'FastMM has detected a ReallocMem call after FastMM was uninstalled.';
+ InvalidAllocMemMsg = 'FastMM has detected an AllocMem call after FastMM was uninstalled.';
+{$endif}
+
+implementation
+
+end.
+
diff --git a/plugins/Libs/FastMM4Options.inc b/plugins/Libs/FastMM4Options.inc
new file mode 100644
index 0000000000..c6cc098e54
--- /dev/null
+++ b/plugins/Libs/FastMM4Options.inc
@@ -0,0 +1,426 @@
+{
+
+Fast Memory Manager: Options Include File
+
+Set the default options for FastMM here.
+
+}
+
+{---------------------------Miscellaneous Options-----------------------------}
+
+{Enable this define to align all blocks on 16 byte boundaries so aligned SSE
+ instructions can be used safely. If this option is disabled then some of the
+ smallest block sizes will be 8-byte aligned instead which may result in a
+ reduction in memory usage. Medium and large blocks are always 16-byte aligned
+ irrespective of this setting.}
+{.$define Align16Bytes}
+
+{Enable to use faster fixed-size move routines when upsizing small blocks.
+ These routines are much faster than the Borland RTL move procedure since they
+ are optimized to move a fixed number of bytes. This option may be used
+ together with the FastMove library for even better performance.}
+{$define UseCustomFixedSizeMoveRoutines}
+
+{Enable this option to use an optimized procedure for moving a memory block of
+ an arbitrary size. Disable this option when using the Fastcode move
+ ("FastMove") library. Using the Fastcode move library allows your whole
+ application to gain from faster move routines, not just the memory manager. It
+ is thus recommended that you use the Fastcode move library in conjunction with
+ this memory manager and disable this option.}
+{$define UseCustomVariableSizeMoveRoutines}
+
+{Enable this option to only install FastMM as the memory manager when the
+ application is running inside the Delphi IDE. This is useful when you want
+ to deploy the same EXE that you use for testing, but only want the debugging
+ features active on development machines. When this option is enabled and
+ the application is not being run inside the IDE debugger, then the default
+ Delphi memory manager will be used (which, since Delphi 2006, is FastMM
+ without FullDebugMode.}
+{.$define InstallOnlyIfRunningInIDE}
+
+{Due to QC#14070 ("Delphi IDE attempts to free memory after the shutdown code
+ of borlndmm.dll has been called"), FastMM cannot be uninstalled safely when
+ used inside a replacement borlndmm.dll for the IDE. Setting this option will
+ circumvent this problem by never uninstalling the memory manager.}
+{.$define NeverUninstall}
+
+{Set this option when you use runtime packages in this application or library.
+ This will automatically set the "AssumeMultiThreaded" option. Note that you
+ have to ensure that FastMM is finalized after all live pointers have been
+ freed - failure to do so will result in a large leak report followed by a lot
+ of A/Vs. (See the FAQ for more detail.) You may have to combine this option
+ with the NeverUninstall option.}
+{.$define UseRuntimePackages}
+
+{-----------------------Concurrency Management Options------------------------}
+
+{Enable to always assume that the application is multithreaded. Enabling this
+ option will cause a significant performance hit with single threaded
+ applications. Enable if you are using multi-threaded third party tools that do
+ not properly set the IsMultiThread variable. Also set this option if you are
+ going to share this memory manager between a single threaded application and a
+ multi-threaded DLL.}
+{.$define AssumeMultiThreaded}
+
+{Enable this option to not call Sleep when a thread contention occurs. This
+ option will improve performance if the ratio of the number of active threads
+ to the number of CPU cores is low (typically < 2). With this option set a
+ thread will usually enter a "busy waiting" loop instead of relinquishing its
+ timeslice when a thread contention occurs, unless UseSwitchToThread is
+ also defined (see below) in which case it will call SwitchToThread instead of
+ Sleep.}
+{.$define NeverSleepOnThreadContention}
+
+ {Set this option to call SwitchToThread instead of sitting in a "busy waiting"
+ loop when a thread contention occurs. This is used in conjunction with the
+ NeverSleepOnThreadContention option, and has no effect unless
+ NeverSleepOnThreadContention is also defined. This option may improve
+ performance with many CPU cores and/or threads of different priorities. Note
+ that the SwitchToThread API call is only available on Windows 2000 and later.}
+ {.$define UseSwitchToThread}
+
+{-----------------------------Debugging Options-------------------------------}
+
+{Enable this option to suppress the generation of debug info for the
+ FastMM4.pas unit. This will prevent the integrated debugger from stepping into
+ the memory manager code.}
+{$define NoDebugInfo}
+
+{Enable this option to suppress the display of all message dialogs. This is
+ useful in service applications that should not be interrupted.}
+{$define NoMessageBoxes}
+
+{Set this option to use the Windows API OutputDebugString procedure to output
+ debug strings on startup/shutdown and when errors occur.}
+{.$define UseOutputDebugString}
+
+{Set this option to use the assembly language version which is faster than the
+ pascal version. Disable only for debugging purposes. Setting the
+ CheckHeapForCorruption option automatically disables this option.}
+{$define ASMVersion}
+
+{FastMM always catches attempts to free the same memory block twice, however it
+ can also check for corruption of the memory heap (typically due to the user
+ program overwriting the bounds of allocated memory). These checks are
+ expensive, and this option should thus only be used for debugging purposes.
+ If this option is set then the ASMVersion option is automatically disabled.}
+{.$define CheckHeapForCorruption}
+
+{Enable this option to catch attempts to perform MM operations after FastMM has
+ been uninstalled. With this option set when FastMM is uninstalled it will not
+ install the previous MM, but instead a dummy MM handler that throws an error
+ if any MM operation is attempted. This will catch attempts to use the MM
+ after FastMM has been uninstalled.}
+{$define DetectMMOperationsAfterUninstall}
+
+{Set the following option to do extensive checking of all memory blocks. All
+ blocks are padded with both a header and trailer that are used to verify the
+ integrity of the heap. Freed blocks are also cleared to to ensure that they
+ cannot be reused after being freed. This option slows down memory operations
+ dramatically and should only be used to debug an application that is
+ overwriting memory or reusing freed pointers. Setting this option
+ automatically enables CheckHeapForCorruption and disables ASMVersion.
+ Very important: If you enable this option your application will require the
+ FastMM_FullDebugMode.dll library. If this library is not available you will
+ get an error on startup.}
+{.$define FullDebugMode}
+
+ {Set this option to perform "raw" stack traces, i.e. check all entries on the
+ stack for valid return addresses. Note that this is significantly slower
+ than using the stack frame tracing method, but is usually more complete. Has
+ no effect unless FullDebugMode is enabled}
+ {$define RawStackTraces}
+
+ {Set this option to check for user code that uses an interface of a freed
+ object. Note that this will disable the checking of blocks modified after
+ being freed (the two are not compatible). This option has no effect if
+ FullDebugMode is not also enabled.}
+ {.$define CatchUseOfFreedInterfaces}
+
+ {Set this option to log all errors to a text file in the same folder as the
+ application. Memory errors (with the FullDebugMode option set) will be
+ appended to the log file. Has no effect if "FullDebugMode" is not set.}
+ {$define LogErrorsToFile}
+
+ {Set this option to log all memory leaks to a text file in the same folder as
+ the application. Memory leak reports (with the FullDebugMode option set)
+ will be appended to the log file. Has no effect if "LogErrorsToFile" and
+ "FullDebugMode" are not also set. Note that usually all leaks are always
+ logged, even if they are "expected" leaks registered through
+ AddExpectedMemoryLeaks. Expected leaks registered by pointer may be excluded
+ through the HideExpectedLeaksRegisteredByPointer option.}
+ {$define LogMemoryLeakDetailToFile}
+
+ {Deletes the error log file on startup. No effect if LogErrorsToFile is not
+ also set.}
+ {$define ClearLogFileOnStartup}
+
+ {Loads the FASTMM_FullDebugMode.dll dynamically. If the DLL cannot be found
+ then stack traces will not be available. Note that this may cause problems
+ due to a changed DLL unload order when sharing the memory manager. Use with
+ care.}
+ {.$define LoadDebugDLLDynamically}
+
+ {.$define DoNotInstallIfDLLMissing}
+ {If the FastMM_FullDebugMode.dll file is not available then FastMM will not
+ install itself. No effect unless FullDebugMode and LoadDebugDLLDynamically
+ are also defined.}
+
+ {FastMM usually allocates large blocks from the topmost available address and
+ medium and small blocks from the lowest available address (This reduces
+ fragmentation somewhat). With this option set all blocks are always
+ allocated from the highest available address. If the process has a >2GB
+ address space and contains bad pointer arithmetic code, this option should
+ help to catch those errors sooner.}
+ {$define AlwaysAllocateTopDown}
+
+ {Disables the logging of memory dumps together with the other detail for
+ memory errors.}
+ {$define DisableLoggingOfMemoryDumps}
+
+ {If FastMM encounters a problem with a memory block inside the FullDebugMode
+ FreeMem handler then an "invalid pointer operation" exception will usually
+ be raised. If the FreeMem occurs while another exception is being handled
+ (perhaps in the try.. finally code) then the original exception will be
+ lost. With this option set FastMM will ignore errors inside FreeMem when an
+ exception is being handled, thus allowing the original exception to
+ propagate.}
+ {$define SuppressFreeMemErrorsInsideException}
+
+ {Adds support for notification of memory manager events in FullDebugMode.
+ With this define set, the application may assign the OnDebugGetMemFinish,
+ OnDebugFreeMemStart, etc. callbacks in order to be notified when the
+ particular memory manager event occurs.}
+ {.$define FullDebugModeCallBacks}
+
+{---------------------------Memory Leak Reporting-----------------------------}
+
+{Set this option to enable reporting of memory leaks. Combine it with the two
+ options below for further fine-tuning.}
+{.$define EnableMemoryLeakReporting}
+
+ {Set this option to suppress the display and logging of expected memory leaks
+ that were registered by pointer. Leaks registered by size or class are often
+ ambiguous, so these expected leaks are always logged to file (in
+ FullDebugMode with the LogMemoryLeakDetailToFile option set) and are never
+ hidden from the leak display if there are more leaks than are expected.}
+ {$define HideExpectedLeaksRegisteredByPointer}
+
+ {Set this option to require the presence of the Delphi IDE to report memory
+ leaks. This option has no effect if the option "EnableMemoryLeakReporting"
+ is not also set.}
+ {.$define RequireIDEPresenceForLeakReporting}
+
+ {Set this option to require the program to be run inside the IDE debugger to
+ report memory leaks. This option has no effect if the option
+ "EnableMemoryLeakReporting" is not also set. Note that this option does not
+ work with libraries, only EXE projects.}
+ {$define RequireDebuggerPresenceForLeakReporting}
+
+ {Set this option to require the presence of debug info ($D+ option) in the
+ compiled unit to perform memory leak checking. This option has no effect if
+ the option "EnableMemoryLeakReporting" is not also set.}
+ {.$define RequireDebugInfoForLeakReporting}
+
+ {Set this option to enable manual control of the memory leak report. When
+ this option is set the ReportMemoryLeaksOnShutdown variable (default = false)
+ may be changed to select whether leak reporting should be done or not. When
+ this option is selected then both the variable must be set to true and the
+ other leak checking options must be applicable for the leak checking to be
+ done.}
+ {.$define ManualLeakReportingControl}
+
+ {Set this option to disable the display of the hint below the memory leak
+ message.}
+ {$define HideMemoryLeakHintMessage}
+
+{--------------------------Instruction Set Options----------------------------}
+
+{Set this option to enable the use of MMX instructions. Disabling this option
+ will result in a slight performance hit, but will enable compatibility with
+ AMD K5, Pentium I and earlier CPUs. MMX is currently only used in the variable
+ size move routines, so if UseCustomVariableSizeMoveRoutines is not set then
+ this option has no effect.}
+{.$define EnableMMX}
+
+ {Set this option to force the use of MMX instructions without checking
+ whether the CPU supports it. If this option is disabled then the CPU will be
+ checked for compatibility first, and if MMX is not supported it will fall
+ back to the FPU move code. Has no effect unless EnableMMX is also set.}
+ {$define ForceMMX}
+
+{-----------------------Memory Manager Sharing Options------------------------}
+
+{Allow sharing of the memory manager between a main application and DLLs that
+ were also compiled with FastMM. This allows you to pass dynamic arrays and
+ long strings to DLL functions provided both are compiled to use FastMM.
+ Sharing will only work if the library that is supposed to share the memory
+ manager was compiled with the "AttemptToUseSharedMM" option set. Note that if
+ the main application is single threaded and the DLL is multi-threaded that you
+ have to set the IsMultiThread variable in the main application to true or it
+ will crash when a thread contention occurs. Note that statically linked DLL
+ files are initialized before the main application, so the main application may
+ well end up sharing a statically loaded DLL's memory manager and not the other
+ way around. }
+{.$define ShareMM}
+
+ {Allow sharing of the memory manager by a DLL with other DLLs (or the main
+ application if this is a statically loaded DLL) that were also compiled with
+ FastMM. Set this option with care in dynamically loaded DLLs, because if the
+ DLL that is sharing its MM is unloaded and any other DLL is still sharing
+ the MM then the application will crash. This setting is only relevant for
+ DLL libraries and requires ShareMM to also be set to have any effect.
+ Sharing will only work if the library that is supposed to share the memory
+ manager was compiled with the "AttemptToUseSharedMM" option set. Note that
+ if DLLs are statically linked then they will be initialized before the main
+ application and then the DLL will in fact share its MM with the main
+ application. This option has no effect unless ShareMM is also set.}
+ {.$define ShareMMIfLibrary}
+
+{Define this to attempt to share the MM of the main application or other loaded
+ DLLs in the same process that were compiled with ShareMM set. When sharing a
+ memory manager, memory leaks caused by the sharer will not be freed
+ automatically. Take into account that statically linked DLLs are initialized
+ before the main application, so set the sharing options accordingly.}
+{.$define AttemptToUseSharedMM}
+
+{Define this to enable backward compatibility for the memory manager sharing
+ mechanism used by Delphi 2006 and 2007, as well as older FastMM versions.}
+{$define EnableBackwardCompatibleMMSharing}
+
+{-----------------------Security Options------------------------}
+
+{Windows clears physical memory before reusing it in another process. However,
+ it is not known how quickly this clearing is performed, so it is conceivable
+ that confidential data may linger in physical memory longer than absolutely
+ necessary. If you're paranoid about this kind of thing, enable this option to
+ clear all freed memory before returning it to the operating system. Note that
+ this incurs a noticeable performance hit.}
+{.$define ClearMemoryBeforeReturningToOS}
+
+{With this option enabled freed memory will immediately be cleared inside the
+ FreeMem routine. This incurs a big performance hit, but may be worthwhile for
+ additional peace of mind when working with highly sensitive data. This option
+ supersedes the ClearMemoryBeforeReturningToOS option.}
+{.$define AlwaysClearFreedMemory}
+
+{--------------------------------Option Grouping------------------------------}
+
+{Enabling this option enables FullDebugMode, InstallOnlyIfRunningInIDE and
+ LoadDebugDLLDynamically. Consequently, FastMM will install itself in
+ FullDebugMode if the application is being debugged inside the Delphi IDE.
+ Otherwise the default Delphi memory manager will be used (which is equivalent
+ to the non-FullDebugMode FastMM since Delphi 2006.)}
+{.$define FullDebugModeInIDE}
+
+{Combines the FullDebugMode, LoadDebugDLLDynamically and
+ DoNotInstallIfDLLMissing options. Consequently FastMM will only be installed
+ (In FullDebugMode) when the FastMM_FullDebugMode.dll file is available. This
+ is useful when the same executable will be distributed for both debugging as
+ well as deployment.}
+{.$define FullDebugModeWhenDLLAvailable}
+
+{Group the options you use for release and debug versions below}
+{$ifdef Release}
+ {Specify the options you use for release versions below}
+ {.$undef FullDebugMode}
+ {.$undef CheckHeapForCorruption}
+ {.$define ASMVersion}
+ {.$undef EnableMemoryLeakReporting}
+ {.$undef UseOutputDebugString}
+{$else}
+ {Specify the options you use for debugging below}
+ {.$define FullDebugMode}
+ {.$define EnableMemoryLeakReporting}
+ {.$define UseOutputDebugString}
+{$endif}
+
+{--------------------Compilation Options For borlndmm.dll---------------------}
+{If you're compiling the replacement borlndmm.dll, set the defines below
+ for the kind of dll you require.}
+
+{Set this option when compiling the borlndmm.dll}
+{.$define borlndmmdll}
+
+{Set this option if the dll will be used by the Delphi IDE}
+{.$define dllforide}
+
+{Set this option if you're compiling a debug dll}
+{.$define debugdll}
+
+{Do not change anything below this line}
+{$ifdef borlndmmdll}
+ {$define AssumeMultiThreaded}
+ {$undef HideExpectedLeaksRegisteredByPointer}
+ {$undef RequireDebuggerPresenceForLeakReporting}
+ {$undef RequireDebugInfoForLeakReporting}
+ {$define DetectMMOperationsAfterUninstall}
+ {$undef ManualLeakReportingControl}
+ {$undef ShareMM}
+ {$undef AttemptToUseSharedMM}
+ {$ifdef dllforide}
+ {$define NeverUninstall}
+ {$define HideMemoryLeakHintMessage}
+ {$undef RequireIDEPresenceForLeakReporting}
+ {$ifndef debugdll}
+ {$undef EnableMemoryLeakReporting}
+ {$endif}
+ {$else}
+ {$define EnableMemoryLeakReporting}
+ {$undef NeverUninstall}
+ {$undef HideMemoryLeakHintMessage}
+ {$define RequireIDEPresenceForLeakReporting}
+ {$endif}
+ {$ifdef debugdll}
+ {$define FullDebugMode}
+ {$define RawStackTraces}
+ {$undef CatchUseOfFreedInterfaces}
+ {$define LogErrorsToFile}
+ {$define LogMemoryLeakDetailToFile}
+ {$undef ClearLogFileOnStartup}
+ {$else}
+ {$undef FullDebugMode}
+ {$endif}
+{$endif}
+
+{Move BCB related definitions here, because CB2006/CB2007 can build borlndmm.dll
+ for tracing memory leaks in BCB applications with "Build with Dynamic RTL"
+ switched on}
+{------------------------------Patch BCB Terminate----------------------------}
+{To enable the patching for BCB to make uninstallation and leak reporting
+ possible, you may need to add "BCB" definition
+ in "Project Options->Pascal/Delphi Compiler->Defines".
+ (Thanks to JiYuan Xie for implementing this.)}
+
+{$ifdef BCB}
+ {$ifdef CheckHeapForCorruption}
+ {$define PatchBCBTerminate}
+ {$else}
+ {$ifdef DetectMMOperationsAfterUninstall}
+ {$define PatchBCBTerminate}
+ {$else}
+ {$ifdef EnableMemoryLeakReporting}
+ {$define PatchBCBTerminate}
+ {$endif}
+ {$endif}
+ {$endif}
+
+ {$ifdef PatchBCBTerminate}
+ {$define CheckCppObjectType}
+ {$undef CheckCppObjectTypeEnabled}
+
+ {$ifdef CheckCppObjectType}
+ {$define CheckCppObjectTypeEnabled}
+ {$endif}
+
+ {Turn off "CheckCppObjectTypeEnabled" option if neither "CheckHeapForCorruption"
+ option or "EnableMemoryLeakReporting" option were defined.}
+ {$ifdef CheckHeapForCorruption}
+ {$else}
+ {$ifdef EnableMemoryLeakReporting}
+ {$else}
+ {$undef CheckCppObjectTypeEnabled}
+ {$endif}
+ {$endif}
+ {$endif}
+{$endif}
diff --git a/plugins/Libs/KOLCCtrls.pas b/plugins/Libs/KOLCCtrls.pas
new file mode 100644
index 0000000000..f90e8f0e90
--- /dev/null
+++ b/plugins/Libs/KOLCCtrls.pas
@@ -0,0 +1,1780 @@
+unit KOLCCtrls;
+{$UNDEF UNICODE}
+
+interface
+
+uses
+ Windows, Messages, ShellAPI, KOL;
+
+{ ====== TRACKBAR CONTROL CONSTANTS =================== }
+
+const
+ TRACKBAR_CLASS = 'msctls_trackbar32';
+
+ TBS_AUTOTICKS = $0001;
+ TBS_VERT = $0002;
+ TBS_HORZ = $0000;
+ TBS_TOP = $0004;
+ TBS_BOTTOM = $0000;
+ TBS_LEFT = $0004;
+ TBS_RIGHT = $0000;
+ TBS_BOTH = $0008;
+ TBS_NOTICKS = $0010;
+ TBS_ENABLESELRANGE = $0020;
+ TBS_FIXEDLENGTH = $0040;
+ TBS_NOTHUMB = $0080;
+ TBS_TOOLTIPS = $0100;
+
+ TBM_GETPOS = WM_USER;
+ TBM_GETRANGEMIN = WM_USER + 1;
+ TBM_GETRANGEMAX = WM_USER + 2;
+ TBM_GETTIC = WM_USER + 3;
+ TBM_SETTIC = WM_USER + 4;
+ TBM_SETPOS = WM_USER + 5;
+ TBM_SETRANGE = WM_USER + 6;
+ TBM_SETRANGEMIN = WM_USER + 7;
+ TBM_SETRANGEMAX = WM_USER + 8;
+ TBM_CLEARTICS = WM_USER + 9;
+ TBM_SETSEL = WM_USER + 10;
+ TBM_SETSELSTART = WM_USER + 11;
+ TBM_SETSELEND = WM_USER + 12;
+ TBM_GETPTICS = WM_USER + 14;
+ TBM_GETTICPOS = WM_USER + 15;
+ TBM_GETNUMTICS = WM_USER + 16;
+ TBM_GETSELSTART = WM_USER + 17;
+ TBM_GETSELEND = WM_USER + 18;
+ TBM_CLEARSEL = WM_USER + 19;
+ TBM_SETTICFREQ = WM_USER + 20;
+ TBM_SETPAGESIZE = WM_USER + 21;
+ TBM_GETPAGESIZE = WM_USER + 22;
+ TBM_SETLINESIZE = WM_USER + 23;
+ TBM_GETLINESIZE = WM_USER + 24;
+ TBM_GETTHUMBRECT = WM_USER + 25;
+ TBM_GETCHANNELRECT = WM_USER + 26;
+ TBM_SETTHUMBLENGTH = WM_USER + 27;
+ TBM_GETTHUMBLENGTH = WM_USER + 28;
+ TBM_SETTOOLTIPS = WM_USER + 29;
+ TBM_GETTOOLTIPS = WM_USER + 30;
+ TBM_SETTIPSIDE = WM_USER + 31;
+
+ // TrackBar Tip Side flags
+ TBTS_TOP = 0;
+ TBTS_LEFT = 1;
+ TBTS_BOTTOM = 2;
+ TBTS_RIGHT = 3;
+
+ TBM_SETBUDDY = WM_USER + 32; // wparam = BOOL fLeft; (or right)
+ TBM_GETBUDDY = WM_USER + 33; // wparam = BOOL fLeft; (or right)
+ TBM_SETUNICODEFORMAT = CCM_SETUNICODEFORMAT;
+ TBM_GETUNICODEFORMAT = CCM_GETUNICODEFORMAT;
+
+ TB_LINEUP = 0;
+ TB_LINEDOWN = 1;
+ TB_PAGEUP = 2;
+ TB_PAGEDOWN = 3;
+ TB_THUMBPOSITION = 4;
+ TB_THUMBTRACK = 5;
+ TB_TOP = 6;
+ TB_BOTTOM = 7;
+ TB_ENDTRACK = 8;
+
+ // custom draw item specs
+ TBCD_TICS = $0001;
+ TBCD_THUMB = $0002;
+ TBCD_CHANNEL = $0003;
+
+ { ^^^^^^^^ TRACKBAR CONTROL ^^^^^^^^ }
+
+type
+ PTrackbar = ^TTrackbar;
+ TTrackbarOption = (trbAutoTicks, trbEnableSelRange, trbFixedLength,
+ trbNoThumb, trbNoTicks, trbTooltips, trbTopLeftMarks,
+ trbVertical, trbNoBorder, trbBoth);
+ TTrackbarOptions = set of TTrackbarOption;
+
+ TOnScroll = procedure(Sender: PTrackbar; Code: Integer) of object;
+ {* Code:
+ |<pre>
+ TB_THUMBTRACK Slider movement (the user dragged the slider)
+ TB_THUMBPOSITION WM_LBUTTONUP following a TB_THUMBTRACK notification message
+ TB_BOTTOM VK_END
+ TB_ENDTRACK WM_KEYUP (the user released a key that sent a relevant virtual key code)
+ TB_LINEDOWN VK_RIGHT or VK_DOWN
+ TB_LINEUP VK_LEFT or VK_UP
+ TB_PAGEDOWN VK_NEXT (the user clicked the channel below or to the right of the slider)
+ TB_PAGEUP VK_PRIOR (the user clicked the channel above or to the left of the slider)
+ TB_TOP VK_HOME
+ |</pre>
+ }
+
+ TTrackbar = object(TControl)
+ private
+ function GetOnScroll: TOnScroll;
+ procedure SetOnScroll(const Value: TOnScroll);
+ function GetVal(const Index: Integer): Integer;
+ procedure SetVal(const Index, Value: Integer);
+ procedure SetThumbLen(const Index, Value: Integer);
+ protected
+ public
+ property OnScroll: TOnScroll read GetOnScroll write SetOnScroll;
+ property RangeMin: Integer index $80010007 read GetVal write SetVal;
+ property RangeMax: Integer index $80020008 read GetVal write SetVal;
+ property PageSize: Integer index $00160015 read GetVal write SetVal;
+ property LineSize: Integer index $00180017 read GetVal write SetVal;
+ property Position: Integer index $80000005 read GetVal write SetVal;
+ property NumTicks: Integer index $00100000 read GetVal;
+ property SelStart: Integer index $0011000B read GetVal write SetVal;
+ property SelEnd: Integer index $0012000C read GetVal write SetVal;
+ property ThumbLen: Integer index $001B0000 read GetVal write SetThumbLen;
+
+ function ChannelRect: TRect;
+ end;
+
+ PTrackbarData = ^TTrackbarData;
+ TTrackbarData = packed record
+ FOnScroll: TOnScroll;
+ end;
+
+ TKOLTrackbar = PTrackbar;
+
+ { SPC CONTROLS }
+
+ TSortBy = (sbName, sbExtention);
+
+ PSPCDirectoryEdit = ^TSPCDirectoryEdit;
+ TSPCDirectoryEditBox = PSPCDirectoryEdit;
+ TSPCDirectoryEdit = object(TObj)
+ private
+ { Private declarations }
+ fCreated: Boolean;
+ fBorder: Integer;
+ fControl: PControl;
+ fEdit: PControl;
+ fButton: PControl;
+ fDirList: POpenDirDialog;
+ fFont: PGraphicTool;
+ fPath: string;
+ fTitle: string;
+ fCaptionEmpty: string;
+ fOnChange: TOnEvent;
+ fColor: TColor;
+ function GetTop: Integer;
+ procedure SetTop(Value: Integer);
+ function GetLeft: Integer;
+ procedure SetLeft(Value: Integer);
+ function GetHeight: Integer;
+ procedure SetHeight(Value: Integer);
+ function GetWidth: Integer;
+ procedure SetWidth(Value: Integer);
+ procedure DoClick(Sender: PObj);
+ procedure SetPath(Value: string);
+ protected
+ { Protected declarations }
+ public
+ destructor Destroy; virtual;
+ procedure Initialize;
+ function SetAlign(Value: TControlAlign): PSPCDirectoryEdit; overload;
+ function SetPosition(X, Y: integer): PSPCDirectoryEdit; overload;
+ function SetSize(X, Y: integer): PSPCDirectoryEdit; overload;
+ function GetFont: PGraphicTool;
+ property Border: Integer read fBorder write fBorder;
+ { Public declarations }
+ property Font: PGraphicTool read GetFont;
+ property Color: TColor read fColor write fColor;
+ property Title: string read fTitle write fTitle;
+ property Path: string read fPath write SetPath;
+ property OnChange: TOnEvent read fOnChange write fOnChange;
+ property CaptionEmpty: string read fCaptionEmpty write fCaptionEmpty;
+ property Height: Integer read GetHeight write SetHeight;
+ property Width: Integer read GetWidth write SetWidth;
+ property Top: Integer read GetTop write SetTop;
+ property Left: Integer read GetLeft write SetLeft;
+ end;
+
+ TCase = (ctDefault, ctLower, ctUpper);
+
+ PSPCFileList = ^TSPCFileList;
+ TSPCFileListBox = PSPCFileList;
+ TSPCFileList = object(TObj)
+ private
+ { Private declarations }
+ fColor: TColor;
+ fIcons: PImageList;
+ fFilters: string;
+ fIntegralHeight: Boolean;
+ fFileList: PDirList;
+ fControl: PControl;
+ fPath: KOLString;
+ fFont: PGraphicTool;
+ FOnSelChange: TOnEvent;
+ fDoCase: TCase;
+ fHasBorder: Boolean;
+ fOnPaint: TOnPaint;
+ fExecuteOnDblClk: Boolean;
+ fSortBy: TSortBy;
+ FOnMouseDblClick: TOnMouse;
+ function GetVisible: Boolean; // Edited
+ procedure SetVisible(Value: Boolean); // Edited
+ function GetFocused: Boolean;
+ procedure SetFocused(Value: Boolean);
+ function GetTop: Integer;
+ procedure SetTop(Value: Integer);
+ function GetLeft: Integer;
+ procedure SetLeft(Value: Integer);
+ function GetHeight: Integer;
+ procedure SetHeight(Value: Integer);
+ function GetWidth: Integer;
+ procedure SetWidth(Value: Integer);
+ procedure DoSelChange(Sender: PObj);
+ procedure SetPath(Value: KOLString);
+ procedure SetFilters(Value: string);
+ procedure SetIntegralHeight(Value: Boolean);
+ function GetCurIndex: Integer;
+ procedure SetCurIndex(Value: Integer);
+ procedure SetHasBorder(Value: Boolean);
+ function GetSelected(Index: Integer): Boolean;
+ procedure SetSelected(Index: Integer; Value: Boolean);
+ function GetItem(Index: Integer): string;
+ function DrawOneItem(Sender: PObj; DC: HDC; const Rect: TRect; ItemIdx: Integer; DrawAction: TDrawAction; ItemState: TDrawState): Boolean;
+ procedure DoMouseDblClk(Sender: PControl; var Mouse: TMouseEventData);
+ procedure SetFont(Value: PGraphicTool);
+ procedure SetSortBy(Value: TSortBy);
+ protected
+ { Protected declarations }
+ public
+ property _SortBy: TSortBy read fSortBy write SetSortBy;
+ property OnMouseDblClk: TOnMouse read FOnMouseDblClick write FOnMouseDblClick;
+ destructor Destroy; virtual;
+ function GetFileName: string;
+ function GetFullFileName: string;
+ property Selected[Index: Integer]: Boolean read GetSelected write SetSelected;
+ property Items[Index: Integer]: string read GetItem;
+ function TotalSelected: Integer;
+ function SetPosition(X, Y: integer): PSPCFileList; overload;
+ function SetSize(X, Y: integer): PSPCFileList; overload;
+ function SetAlign(Value: TControlAlign): PSPCFileList; overload;
+ function GetFont: PGraphicTool;
+ { Public declarations }
+ property Color: TColor read fColor write fColor;
+ property Font: PGraphicTool read GetFont write SetFont;
+ property IntegralHeight: Boolean read fIntegralHeight write SetIntegralHeight;
+ property Path: KOLstring read fPath write SetPath;
+ property Filters: string read fFilters write SetFilters;
+ property OnSelChange: TOnEvent read FOnSelChange write FOnSelChange;
+ property OnPaint: TOnPaint read FOnPaint write FOnPaint;
+ property CurIndex: Integer read GetCurIndex write SetCurIndex;
+ function Count: LongInt;
+ property DoCase: TCase read fDoCase write fDoCase;
+ property HasBorder: Boolean read fHasBorder write SetHasBorder;
+ property Height: Integer read GetHeight write SetHeight;
+ property Width: Integer read GetWidth write SetWidth;
+ property Top: Integer read GetTop write SetTop;
+ property Left: Integer read GetLeft write SetLeft;
+ property Visible: Boolean read GetVisible write SetVisible; // Edited
+ property Focused: Boolean read GetFocused write SetFocused;
+ property ExecuteOnDblClk: Boolean read fExecuteOnDblClk write fExecuteOnDblClk;
+ procedure SortByName;
+ procedure SortByExtention;
+ end;
+
+ PSPCDirectoryList = ^TSPCDirectoryList;
+ TSPCDirectoryListBox = PSPCDirectoryList;
+ TSPCDirectoryList = object(TObj)
+ private
+ { Private declarations }
+ fColor: TColor;
+ fDoIndent: Boolean;
+ fTotalTree: Integer;
+ fDIcons: PImageList;
+ fFOLDER: PIcon;
+ fInitialized: Integer;
+ fCreated: Boolean;
+ fIntegralHeight: Boolean;
+ fDirList: PDirList;
+ fCurIndex: Integer;
+ fControl: PControl;
+ fPath: string;
+ fFont: PGraphicTool;
+ FOnMouseDblClick: TOnMouse;
+ fLVBkColor: Integer;
+ fOnChange: TOnEvent;
+ fFileListBox: PSPCFileList;
+ function GetTop: Integer;
+ procedure SetTop(Value: Integer);
+ function GetLeft: Integer;
+ procedure SetLeft(Value: Integer);
+ function GetHeight: Integer;
+ procedure SetHeight(Value: Integer);
+ function GetWidth: Integer;
+ procedure SetWidth(Value: Integer);
+ procedure DoMouseDblClick(Sender: PControl; var Mouse: TMouseEventData);
+ procedure SetPath(Value: string);
+ procedure SetFileListBox(Value: PSPCFileList);
+ protected
+ { Protected declarations }
+ public
+ destructor Destroy; virtual;
+ property FileListBox: PSPCFileList read fFileListBox write SetFileListBox;
+ function SetAlign(Value: TControlAlign): PSPCDirectoryList; overload;
+ function SetPosition(X, Y: integer): PSPCDirectoryList; overload;
+ function SetSize(X, Y: integer): PSPCDirectoryList; overload;
+ function GetFont: PGraphicTool;
+ property Color: TColor read fColor write fColor;
+ { Public declarations }
+ property Font: PGraphicTool read GetFont;
+ property IntegralHeight: Boolean read fIntegralHeight write fIntegralHeight;
+ property Path: string read fPath write SetPath;
+ property DoIndent: Boolean read fDoIndent write fDoIndent;
+ property OnMouseDblClk: TOnMouse read FOnMouseDblClick write FOnMouseDblClick;
+ property CurIndex: Integer read fCurIndex write fCurIndex;
+ property LVBkColor: Integer read fLVBkColor write fLVBkColor;
+ property OnChange: TOnEvent read fOnChange write fOnChange;
+ property Height: Integer read GetHeight write SetHeight;
+ property Width: Integer read GetWidth write SetWidth;
+ property Top: Integer read GetTop write SetTop;
+ property Left: Integer read GetLeft write SetLeft;
+ end;
+
+ PSPCDriveCombo = ^TSPCDriveCombo;
+ TSPCDriveComboBox = PSPCDriveCombo;
+ TSPCDriveCombo = object(TObj)
+ private
+ { Private declarations }
+ fIcons: PImageList;
+ fColor: TColor;
+ fInitialized: Integer;
+ fCurIndex: Integer;
+ fControl: PControl;
+ fDrive: KOLChar;
+ fFont: PGraphicTool;
+ fLVBkColor: Integer;
+ fOnChange: TOnEvent;
+ // fOnChangeInternal: TOnEvent;
+ fAOwner: PControl;
+ fDirectoryListBox: PSPCDirectoryList;
+ function GetTop: Integer;
+ procedure SetTop(Value: Integer);
+ function GetLeft: Integer;
+ procedure SetLeft(Value: Integer);
+ function GetHeight: Integer;
+ procedure SetHeight(Value: Integer);
+ function GetWidth: Integer;
+ procedure SetWidth(Value: Integer);
+ procedure SetDrive(Value: KOLChar);
+ procedure BuildList;
+ procedure DoChange(Obj: PObj);
+ // procedure DoChangeInternal(Obj: PObj);
+ function DoMeasureItem(Sender: PObj; Idx: Integer): Integer;
+ function DrawOneItem(Sender: PObj; DC: HDC; const Rect: TRect; ItemIdx: Integer; DrawAction: TDrawAction; ItemState: TDrawState): Boolean;
+ protected
+ { Protected declarations }
+ public
+ destructor Destroy; virtual;
+ function SetAlign(Value: TControlAlign): PSPCDriveCombo; overload;
+ function SetPosition(X, Y: integer): PSPCDriveCombo; overload;
+ function SetSize(X, Y: integer): PSPCDriveCombo; overload;
+ function GetFont: PGraphicTool;
+ procedure SetFont(Value: PGraphicTool);
+ property Color: TColor read fColor write fColor;
+ { Public declarations }
+ property DirectoryListBox: PSPCDirectoryList read fDirectoryListBox write fDirectoryListBox;
+ property Font: PGraphicTool read GetFont write SetFont;
+ property Drive: KOLChar read fDrive write SetDrive;
+ property CurIndex: Integer read fCurIndex write fCurIndex;
+ property LVBkColor: Integer read fLVBkColor write fLVBkColor;
+ property OnChange: TOnEvent read fOnChange write fOnChange;
+ property Height: Integer read GetHeight write SetHeight;
+ property Width: Integer read GetWidth write SetWidth;
+ property Top: Integer read GetTop write SetTop;
+ property Left: Integer read GetLeft write SetLeft;
+ end;
+
+ TFilterItem = class
+ private
+ fFull: string;
+ fDescription: string;
+ fFilter: string;
+ public
+ published
+ property Full: string read fFull write fFull;
+ property Description: string read fDescription write fDescription;
+ property Filter: string read fFilter write fFilter;
+ end;
+
+ PSPCFilterCombo = ^TSPCFilterCombo;
+ TSPCFilterComboBox = PSPCFilterCombo;
+ TSPCFilterCombo = object(TObj)
+ private
+ { Private declarations }
+ fColor: TColor;
+ fCurIndex: Integer;
+ fControl: PControl;
+ fFont: PGraphicTool;
+ fLVBkColor: Integer;
+ fOnChange: TOnEvent;
+ fFilterItems: PList;
+ fFilter: string;
+ fCreated: Boolean;
+ fInitialized: Integer;
+ fFileListBox: PSPCFileList;
+ ftext: string;
+ function GetTop: Integer;
+ procedure SetTop(Value: Integer);
+ function GetLeft: Integer;
+ procedure SetLeft(Value: Integer);
+ function GetHeight: Integer;
+ procedure SetHeight(Value: Integer);
+ function GetWidth: Integer;
+ procedure SetWidth(Value: Integer);
+ function GetFilterItem(Index: Integer): TFilterItem;
+ procedure SetFilter(Value: string);
+ procedure SetCurIndex(Value: Integer);
+ function GetCurIndex: Integer;
+ procedure DoChange(Obj: PObj);
+ function DoMeasureItem(Sender: PObj; Idx: Integer): Integer;
+ function GetItem(Index: Integer): string;
+ procedure SetItem(Index: Integer; Value: string);
+ function GetFilter: string;
+ protected
+ { Protected declarations }
+ public
+ destructor Destroy; virtual;
+ procedure Update;
+ procedure Add(fNewFilter: string);
+ procedure DeleteItem(Index: Integer);
+ function Count: Integer;
+ procedure BuildList;
+ property FileListBox: PSPCFileList read fFileListBox write fFileListBox;
+ function SetAlign(Value: TControlAlign): PSPCFilterCombo; overload;
+ function SetPosition(X, Y: integer): PSPCFilterCombo; overload;
+ function SetSize(X, Y: integer): PSPCFilterCombo; overload;
+ function GetFont: PGraphicTool;
+ procedure SetFont(Value: PGraphicTool);
+ property Filter: string read GetFilter write SetFilter;
+ property Color: TColor read fColor write fColor;
+ { Public declarations }
+ property Text: string read fText write fText;
+ property Font: PGraphicTool read GetFont write SetFont;
+ property CurIndex: Integer read GetCurIndex write SetCurIndex;
+ property LVBkColor: Integer read fLVBkColor write fLVBkColor;
+ property OnChange: TOnEvent read fOnChange write fOnChange;
+ property Items[Index: Integer]: string read GetItem write SetItem;
+ property Filters[Index: Integer]: TFilterItem read GetFilterItem;
+ property Height: Integer read GetHeight write SetHeight;
+ property Width: Integer read GetWidth write SetWidth;
+ property Top: Integer read GetTop write SetTop;
+ property Left: Integer read GetLeft write SetLeft;
+ end;
+
+ PSPCStatus = ^TSPCStatus;
+ TSPCStatusBar = PSPCStatus;
+ TSPCStatus = object(TControl)
+ private
+ { Private declarations }
+ fControl: PControl;
+ function GetTop: Integer;
+ procedure SetTop(Value: Integer);
+ function GetLeft: Integer;
+ procedure SetLeft(Value: Integer);
+ function GetHeight: Integer;
+ procedure SetHeight(Value: Integer);
+ function GetWidth: Integer;
+ procedure SetWidth(Value: Integer);
+ procedure SetSimpleStatusText(Value: string);
+ function GetSimpleStatusText: string;
+ protected
+ { Protected declarations }
+ public
+ destructor Destroy; virtual;
+ function SetAlign(Value: TControlAlign): PSPCStatus; overload;
+ function SetPosition(X, Y: integer): PSPCStatus; overload;
+ function SetSize(X, Y: integer): PSPCStatus; overload;
+ function GetFont: PGraphicTool;
+ procedure SetFont(Value: PGraphicTool);
+ { Public declarations }
+ property Font: PGraphicTool read GetFont write SetFont;
+ property SimpleStatusText: string read GetSimpleStatusText write SetSimpleStatusText;
+ property Height: Integer read GetHeight write SetHeight;
+ property Width: Integer read GetWidth write SetWidth;
+ property Top: Integer read GetTop write SetTop;
+ property Left: Integer read GetLeft write SetLeft;
+ // property SizeGrip;
+ end;
+
+function NewTrackbar(AParent: PControl; Options: TTrackbarOptions; OnScroll: TOnScroll): PTrackbar;
+
+function CheckBit(Value, Index: LongInt): Boolean;
+function GetLastPos(c: char; s: string): Integer;
+function NewTSPCDirectoryEditBox(AOwner: PControl): PSPCDirectoryEdit;
+function NewTSPCDirectoryListBox(AOwner: PControl; Style: TListViewStyle; Options: TListViewOptions; ImageListSmall, ImageListNormal, ImageListState: PImageList): PSPCDirectoryList;
+function NewTSPCDriveComboBox(AOwner: PControl; Options: TComboOptions): PSPCDriveCombo;
+function NewTSPCFileListBox(AOwner: PControl; Options: TListOptions): PSPCFileList;
+function NewTSPCFilterComboBox(AOwner: PControl; Options: TComboOptions): PSPCFilterCombo;
+function NewTSPCStatusBar(AOwner: PControl): PSPCStatus;
+
+implementation
+
+function WndProcTrackbarParent(Sender: PControl; var Msg: TMsg; var Rslt: Integer): Boolean;
+var
+ D : PTrackbarData;
+ Trackbar : PTrackbar;
+begin
+ Result := False;
+ if (Msg.message = WM_HSCROLL) or (Msg.message = WM_VSCROLL) then
+ if (Msg.lParam <> 0) then begin
+ Trackbar := Pointer({$IFDEF USE_PROP}
+ GetProp(Msg.lParam, ID_SELF)
+{$ELSE}
+ GetWindowLong(Msg.lParam, GWL_USERDATA)
+{$ENDIF});
+ if Assigned(Trackbar) then begin
+ D := Trackbar.CustomData;
+ if Assigned(D.FOnScroll) then
+ D.FOnScroll(Trackbar, Msg.wParam);
+ end;
+ end;
+end;
+
+function TTrackbar.ChannelRect: TRect;
+begin
+ Perform( TBM_GETCHANNELRECT, 0, Integer( @ Result ) );
+end;
+
+function NewTrackbar(AParent: PControl; Options: TTrackbarOptions; OnScroll: TOnScroll): PTrackbar;
+const
+ TrackbarOptions : array[TTrackbarOption] of Integer = (TBS_AUTOTICKS,
+ TBS_ENABLESELRANGE, TBS_FIXEDLENGTH, TBS_NOTHUMB, TBS_NOTICKS, TBS_TOOLTIPS,
+ TBS_TOP, TBS_VERT, 0, TBS_BOTH);
+var
+ aStyle : DWORD;
+ D : PTrackbarData;
+ W, H : Integer;
+begin
+ DoInitCommonControls(ICC_BAR_CLASSES);
+ aStyle := MakeFlags(@Options, TrackbarOptions) or WS_CHILD or WS_VISIBLE;
+ Result := PTrackbar(_NewCommonControl(AParent, TRACKBAR_CLASS, aStyle,
+ not (trbNoBorder in Options), nil));
+ W := 200;
+ H := 40;
+ if (trbVertical in Options) then begin
+ H := W;
+ W := 40;
+ end;
+ Result.Width := W;
+ Result.Height := H;
+ GetMem(D, Sizeof(D^));
+ Result.CustomData := D;
+ D.FOnScroll := OnScroll;
+ AParent.AttachProc(WndProcTrackbarParent);
+end;
+
+{ TTrackbar }
+
+function TTrackbar.GetOnScroll: TOnScroll;
+var
+ D : PTrackbarData;
+begin
+ D := CustomData;
+ Result := D.FOnScroll;
+end;
+
+function TTrackbar.GetVal(const Index: Integer): Integer;
+begin
+ Result := Perform(WM_USER + (HiWord(Index) and $7FFF), 0, 0);
+end;
+
+procedure TTrackbar.SetOnScroll(const Value: TOnScroll);
+var
+ D : PTrackbarData;
+begin
+ D := CustomData;
+ D.FOnScroll := Value;
+end;
+
+procedure TTrackbar.SetThumbLen(const Index, Value: Integer);
+begin
+ Perform(TBM_SETTHUMBLENGTH, Value, 0);
+end;
+
+procedure TTrackbar.SetVal(const Index, Value: Integer);
+begin
+ Perform(WM_USER + LoWord(Index), Index shr 31, Value);
+end;
+
+{ TSPCDirectoryEdit }
+
+function NewTSPCDirectoryEditBox;
+var
+ p : PSPCDirectoryEdit;
+ c : PControl;
+begin
+ c := NewPanel(AOwner, esNone);
+ c.ExStyle := c.ExStyle or WS_EX_CLIENTEDGE;
+ New(p, create);
+ AOwner.Add2AutoFree(p);
+ p.fControl := c;
+ p.fFont := NewFont;
+ p.fCreated := False;
+ Result := p;
+end;
+
+function TSPCDirectoryEdit.SetAlign(Value: TControlAlign): PSPCDirectoryEdit;
+begin
+ fControl.Align := Value;
+ Result := @Self;
+end;
+
+destructor TSPCDirectoryEdit.Destroy;
+begin
+ fFont.Free;
+ inherited;
+end;
+
+function TSPCDirectoryEdit.SetPosition(X, Y: integer): PSPCDirectoryEdit;
+begin
+ fControl.Left := X;
+ fControl.Top := Y;
+ Result := @self;
+end;
+
+function TSPCDirectoryEdit.SetSize(X, Y: integer): PSPCDirectoryEdit;
+begin
+ fControl.Width := X;
+ fControl.Height := Y;
+ Result := @self;
+end;
+
+function TSPCDirectoryEdit.GetFont;
+begin
+ Result := fFont;
+end;
+
+procedure TSPCDirectoryEdit.Initialize;
+begin
+ fEdit := NewEditBox(fControl, [eoReadOnly]);
+ fEdit.Font.FontHeight := -11;
+ fControl.Height := fEdit.Height - 1;
+ fEdit.Left := 0;
+ fEdit.Top := 1;
+ fEdit.Height := 17;
+ fEdit.Width := fControl.Width - 21;
+ fEdit.HasBorder := False;
+ fEdit.Color := fColor;
+ fEdit.Font.Assign(Font);
+ fButton := NewBitBtn(fControl, '...', [], glyphLeft, 0, 1);
+ fButton.Font.FontHeight := -11;
+ fButton.VerticalAlign := vaCenter;
+ fButton.LikeSpeedButton;
+ fButton.Width := 17;
+ fButton.Height := 17;
+ fButton.Top := 0;
+ fButton.Left := fEdit.Width;
+ fButton.OnClick := DoClick;
+ fDirList := NewOpenDirDialog(Title, []);
+ fDirList.CenterOnScreen := True;
+end;
+
+procedure TSPCDirectoryEdit.SetPath(Value: string);
+begin
+ if DirectoryExists(Value) then fPath := Value else fPath := '';
+ if Length(fPath) = 0 then fEdit.Text := CaptionEmpty else fEdit.Text := fPath;
+ if Assigned(fOnChange) then if fCreated then fOnChange(@Self) else fCreated := True;
+end;
+
+procedure TSPCDirectoryEdit.DoClick;
+begin
+ fDirList.InitialPath := Path;
+ if fDirList.Execute then begin
+ Path := fDirList.Path;
+ fEdit.Text := fDirList.Path;
+ end;
+end;
+
+function TSPCDirectoryEdit.GetHeight: Integer;
+begin
+ Result := fControl.Height;
+end;
+
+procedure TSPCDirectoryEdit.SetHeight(Value: Integer);
+begin
+ fControl.Height := Value;
+end;
+
+function TSPCDirectoryEdit.GetWidth: Integer;
+begin
+ Result := fControl.Width;
+end;
+
+procedure TSPCDirectoryEdit.SetWidth(Value: Integer);
+begin
+ fControl.Width := Value;
+end;
+
+function TSPCDirectoryEdit.GetTop: Integer;
+begin
+ Result := fControl.Top;
+end;
+
+procedure TSPCDirectoryEdit.SetTop(Value: Integer);
+begin
+ fControl.Top := Value;
+end;
+
+function TSPCDirectoryEdit.GetLeft: Integer;
+begin
+ Result := fControl.Left;
+end;
+
+procedure TSPCDirectoryEdit.SetLeft(Value: Integer);
+begin
+ fControl.Left := Value;
+end;
+
+{ TSPCDirectoryList }
+
+function NewTSPCDirectoryListBox;
+var
+ p : PSPCDirectoryList;
+ c : PControl;
+ Shell32 : LongInt;
+begin
+ c := NewListView(AOwner, lvsDetailNoHeader, [], ImageListSmall, ImageListNormal, ImageListState);
+ New(p, create);
+ AOwner.Add2AutoFree(p);
+ p.fControl := c;
+ p.fControl.OnMouseDblClk := p.DoMouseDblClick;
+ p.fControl.lvOptions := [lvoRowSelect, lvoInfoTip, lvoAutoArrange];
+ p.fCreated := False;
+ p.fDirList := NewDirList('', '', 0);
+ p.fFont := NewFont;
+ p.fDIcons := NewImageList(AOwner);
+ p.fDIcons.LoadSystemIcons(True);
+ Shell32 := LoadLibrary('shell32.dll');
+ p.fFOLDER := NewIcon;
+ p.fFOLDER.LoadFromResourceID(Shell32, 4, 16);
+ p.fDIcons.ReplaceIcon(0, p.fFOLDER.Handle);
+ p.fFOLDER.LoadFromResourceID(Shell32, 5, 16);
+ p.fDIcons.ReplaceIcon(1, p.fFOLDER.Handle);
+ FreeLibrary(Shell32);
+ p.fFOLDER.Free;
+ p.fControl.ImageListSmall := p.fDIcons;
+ p.fInitialized := 0;
+ Result := p;
+end;
+
+function TSPCDirectoryList.SetAlign(Value: TControlAlign): PSPCDirectoryList;
+begin
+ fControl.Align := Value;
+ Result := @Self;
+end;
+
+procedure TSPCDirectoryList.DoMouseDblClick;
+var
+ s : string;
+ i : Integer;
+begin
+ if fControl.lvCurItem > -1 then begin
+ s := '';
+ if fControl.LVCurItem <= fTotalTree - 1 then begin
+ for i := 0 to fControl.LVCurItem do s := s + fControl.lvItems[i, 0] + '\';
+ end else begin
+ for i := 0 to fTotalTree - 1 do s := s + fControl.lvItems[i, 0] + '\';
+ s := s + fControl.lvItems[fControl.lvCurItem, 0];
+ end;
+ Path := s;
+ if Assigned(fOnMouseDblClick) then fOnMouseDblClick(@Self, Mouse);
+ end;
+end;
+
+destructor TSPCDirectoryList.Destroy;
+begin
+ fFont.Free;
+ inherited;
+end;
+
+function TSPCDirectoryList.SetPosition(X, Y: integer): PSPCDirectoryList;
+begin
+ fControl.Left := X;
+ fControl.Top := Y;
+ Result := @self;
+end;
+
+function TSPCDirectoryList.SetSize(X, Y: integer): PSPCDirectoryList;
+begin
+ fControl.Width := X;
+ fControl.Height := Y;
+ Result := @self;
+end;
+
+function TSPCDirectoryList.GetFont;
+begin
+ Result := fFont;
+end;
+
+procedure TSPCDirectoryList.SetPath(Value: string);
+var
+ TPath, fValue : string;
+ i, z : Integer;
+ LastDir : Cardinal;
+ fImgIndex : Integer;
+ Code : Cardinal;
+ fDriveShown : Boolean;
+begin
+ fValue := Value;
+ fControl.lvBkColor := fColor;
+ fControl.lvTextBkColor := fColor;
+ if Length(fValue) = 1 then fValue := fValue + ':\';
+ if not fCreated then begin
+ fCreated := True;
+ fControl.LVColAdd('', taRight, fControl.Width);
+ // if fIntegralHeight then
+ // begin
+ // fControl.Height:=(fControl.Height div 16)*16+1;
+ // end;
+ end;
+ fControl.Clear;
+ if DirectoryExists(fValue) then begin
+ LastDir := 0;
+ fTotalTree := 0;
+ if fValue[Length(fValue)] = '\' then TPath := fValue else TPath := fValue + '\';
+ fPath := TPath;
+ fDriveShown := False;
+ fImgIndex := -1;
+ repeat
+ if fTotalTree > 0 then fImgIndex := 1;
+ if not fDriveShown then begin
+ fDriveShown := True;
+ fImgIndex := FileIconSystemIdx(Copy(TPath, 1, 3));
+ end;
+ fControl.LVAdd(Copy(TPath, 1, Pos('\', TPath) - 1), fImgIndex, [], 0, 0, 0);
+ fControl.LVItemIndent[LastDir] := LastDir;
+ Delete(TPath, 1, Pos('\', TPath));
+ if DoIndent then Inc(LastDir);
+ Inc(fTotalTree);
+ until Length(TPath) = 0;
+ fDirList.ScanDirectory(fValue, '*.*', FILE_ATTRIBUTE_NORMAL);
+ fDirList.Sort([sdrByName]);
+ z := -1;
+ for i := 0 to fDirList.Count - 1 do begin
+ Code := fDirList.Items[i].dwFileAttributes;
+ if Code = (Code or $10) then
+ if not (fDirList.Names[i] = '.') then
+ if not (fDirList.Names[i] = '..') then begin
+ Inc(z);
+ fControl.LVAdd(fDirList.Names[i], 0, [], 0, 0, 0);
+ if DoIndent then fControl.LVItemIndent[z + fTotalTree] := LastDir else fControl.LVItemIndent[z + fTotalTree] := 1;
+ end;
+ end;
+ end else begin
+ fPath := '';
+ end;
+ Inc(fInitialized);
+ if fInitialized > 2 then fInitialized := 2;
+ if Assigned(OnChange) then if fInitialized = 2 then OnChange(@Self);
+ if Assigned(fFileListBox) then fFileListBox.Path := Path;
+ fControl.LVColWidth[0] := -2;
+end;
+
+function TSPCDirectoryList.GetHeight: Integer;
+begin
+ Result := fControl.Height;
+end;
+
+procedure TSPCDirectoryList.SetHeight(Value: Integer);
+begin
+ fControl.Height := Value;
+end;
+
+function TSPCDirectoryList.GetWidth: Integer;
+begin
+ Result := fControl.Width;
+end;
+
+procedure TSPCDirectoryList.SetWidth(Value: Integer);
+begin
+ fControl.Width := Value;
+end;
+
+function TSPCDirectoryList.GetTop: Integer;
+begin
+ Result := fControl.Top;
+end;
+
+procedure TSPCDirectoryList.SetTop(Value: Integer);
+begin
+ fControl.Top := Value;
+end;
+
+function TSPCDirectoryList.GetLeft: Integer;
+begin
+ Result := fControl.Left;
+end;
+
+procedure TSPCDirectoryList.SetLeft(Value: Integer);
+begin
+ fControl.Left := Value;
+end;
+
+procedure TSPCDirectoryList.SetFileListBox(Value: PSPCFileList);
+begin
+ fFileListBox := Value;
+ fFileListBox.Path := Path;
+end;
+
+{ TSPCDriveCombo }
+
+function CheckBit;
+var
+ fL : LongInt;
+begin
+ fL := Value;
+ fL := fL shr Index;
+ fL := fL and $01;
+ Result := (fL = 1);
+end;
+
+function NewTSPCDriveComboBox;
+var
+ p : PSPCDriveCombo;
+ c : PControl;
+begin
+ c := NewComboBox(AOwner, [coReadOnly, coOwnerDrawVariable]);
+ New(p, create);
+ AOwner.Add2AutoFree(p);
+ p.fControl := c;
+ p.fFont := NewFont;
+ p.fFont.FontHeight := -8;
+ p.fControl.Font.Assign(p.fFont);
+ p.fIcons := NewImageList(AOwner);
+ p.fIcons.LoadSystemIcons(True);
+ p.fAOwner := AOwner;
+ p.fControl.OnDrawItem := p.DrawOneItem;
+ p.fControl.OnChange := p.DoChange;
+ p.fControl.OnMeasureItem := p.DoMeasureItem;
+ p.BuildList;
+ p.fInitialized := 0;
+ p.fControl.Color := $FF0000;
+ Result := p;
+end;
+
+procedure TSPCDriveCombo.DoChange(Obj: PObj);
+begin
+ Drive := fControl.Items[fControl.CurIndex][1];
+ SetCurrentDirectory(PKOLChar(Drive + ':\'));
+ if Assigned(fOnChange) then fOnChange(@Self);
+ if Assigned(fDirectoryListBox) then fDirectoryListBox.Path := Drive;
+end;
+
+destructor TSPCDriveCombo.Destroy;
+begin
+ fFont.Free;
+ inherited;
+end;
+
+function TSPCDriveCombo.SetAlign(Value: TControlAlign): PSPCDriveCombo;
+begin
+ fControl.Align := Value;
+ Result := @Self;
+end;
+
+function TSPCDriveCombo.SetPosition(X, Y: integer): PSPCDriveCombo;
+begin
+ fControl.Left := X;
+ fControl.Top := Y;
+ Result := @self;
+end;
+
+function TSPCDriveCombo.SetSize(X, Y: integer): PSPCDriveCombo;
+begin
+ fControl.Width := X;
+ fControl.Height := Y;
+ Result := @self;
+end;
+
+function TSPCDriveCombo.GetFont;
+begin
+ Result := fFont;
+end;
+
+procedure TSPCDriveCombo.SetFont(Value: PGraphicTool);
+begin
+ fFont := Value;
+ fControl.Font.Assign(Value);
+end;
+
+procedure TSPCDriveCombo.SetDrive;
+var
+ fC : KOLChar;
+begin
+ fControl.Font.Assign(fFont);
+ fControl.Color := fColor;
+ fC := Value;
+ if fControl.SearchFor(fc, 0, True) > -1 then begin
+ fDrive := fC;
+ fControl.CurIndex := fControl.SearchFor(fc, 0, True);
+ end;
+ Inc(fInitialized);
+ if fInitialized > 2 then fInitialized := 2;
+ if Assigned(fOnChange) then if fInitialized = 2 then fOnChange(@Self);
+end;
+
+function VolumeID(DriveChar: KOLChar): string;
+var
+ NotUsed, VolFlags : DWORD;
+ Buf : array[0..MAX_PATH] of KOLChar;
+begin
+ if GetVolumeInformation(PKOLChar(DriveChar + ':\'), Buf, DWORD(sizeof(Buf)), nil, NotUsed, VolFlags, nil, 0) then
+ Result := buf//Copy(Buf, 1, StrLen(Buf))
+ else
+ Result := '';
+end;
+
+function dr_property(path: KOLString): KOLString;
+var
+ Cpath : PKOLChar;
+ Spath : KOLChar;
+begin
+ Result := '';
+ Cpath := PKOLChar(Copy(path, 1, 2));
+ Spath := Cpath[0];
+ case GetDriveType(Cpath) of
+ 0: Result := '<unknown>'; //Íå èçâåñòåí
+ 1: Result := '<disabled>'; //Íå ñóùåñòâóåò :)
+ DRIVE_REMOVABLE: Result := 'Removable'; //Ôëîïèê
+ DRIVE_FIXED: if Length(VolumeID(Spath)) > 0 then Result := VolumeID(Spath) else Result := 'Local Disk'; //HDD
+ DRIVE_REMOTE: if Length(VolumeID(Spath)) > 0 then Result := VolumeID(Spath) else Result := 'Net Disk'; //Âíåøíèé íîñèòåëü
+ // DRIVE_REMOTE: if Length(VolumeID(Spath))>0 then Result:=NetworkVolume(Spath) else Result:='Net Disk';//Âíåøíèé íîñèòåëü
+ DRIVE_CDROM: if Length(VolumeID(Spath)) > 0 then Result := VolumeID(Spath) else Result := 'Compact Disc'; //CD
+ DRIVE_RAMDISK: if Length(VolumeID(Spath)) > 0 then Result := VolumeID(Spath) else Result := 'Removable Disk'; //Âíåøíèé íîñèòåëü
+ end;
+end;
+
+procedure TSPCDriveCombo.BuildList;
+var
+ b : Byte;
+ fFlags : LongInt;
+ fDir : string;
+ // a : integer;
+ fFullPath : string;
+ fdr_property : string;
+begin
+ GetDir(0, fDir);
+ fControl.Clear;
+ fFlags := GetLogicalDrives;
+ for b := 0 to 25 do if Boolean(fFlags and (1 shl b)) then begin
+ fFullPath := Chr(b + $41) + ':';
+ fdr_property := dr_property(fFullPath);
+ {a :=}fControl.Add(Chr(b + $41) + ' ' + fdr_property);
+ end;
+ fControl.CurIndex := fControl.SearchFor(fDir[1], 0, True);
+ fControl.Update;
+end;
+
+function TSPCDriveCombo.DrawOneItem(Sender: PObj; DC: HDC; //aded by tamerlan311
+ const Rect: TRect; ItemIdx: Integer; DrawAction: TDrawAction;
+ ItemState: TDrawState): Boolean;
+var
+ T_Rect : TRect;
+ B_Rect : TRect;
+ Ico : Integer;
+begin
+ SetBkMode(DC, opaque);
+ if ItemIdx > -1 then begin
+ //PControl(Sender).CanResize := True;
+ T_Rect := Rect;
+ B_Rect := Rect;
+ T_Rect.Left := Rect.Left + 19;
+ B_Rect.Left := Rect.Left + 18;
+ PControl(Sender).Canvas.Pen.PenMode := pmCopy;
+ PControl(Sender).Canvas.Pen.Color := $0000FF;
+ PControl(Sender).Brush.Color := clWindow;
+ if (odsFocused in ItemState) or (odsSelected in ItemState) then begin
+ SetBkMode(DC, TRANSPARENT);
+ PControl(Sender).Canvas.Brush.color := clWindow;
+ FillRect(DC, T_Rect, PControl(Sender).Canvas.Brush.Handle);
+ if (not (odsFocused in ItemState)) and ((odsSelected in ItemState)) then begin
+ PControl(Sender).Canvas.Brush.color := clInactiveBorder;
+ SetTextColor(DC, Font.Color);
+ fIcons.DrawingStyle := [];
+ end else begin
+ PControl(Sender).Canvas.Brush.color := clHighLight;
+ SetTextColor(DC, $FFFFFF);
+ fIcons.DrawingStyle := [dsBlend50];
+ end;
+ FillRect(DC, T_Rect, PControl(Sender).Canvas.Brush.Handle);
+ end else begin
+ SetTextColor(DC, Font.Color);
+ PControl(Sender).Canvas.Brush.color := clWindow;
+ SelectObject(DC, PControl(Sender).Canvas.Brush.Handle);
+ FillRect(DC, B_Rect, PControl(Sender).Canvas.Brush.Handle);
+ fIcons.DrawingStyle := [];
+ end;
+ Ico := FileIconSystemIdx(PControl(Sender).Items[ItemIdx][1] + ':\');
+ fIcons.Draw(Ico, DC, Rect.Left + 1, Rect.Top);
+ DrawText(DC, PKOLChar(PControl(Sender).Items[ItemIdx]), Length(PControl(Sender).Items[ItemIdx]), T_Rect, DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX);
+ end;
+ // PControl(Sender).Update;
+ Result := True; ///
+end;
+
+function TSPCDriveCombo.GetHeight: Integer;
+begin
+ Result := fControl.Height;
+end;
+
+procedure TSPCDriveCombo.SetHeight(Value: Integer);
+begin
+ fControl.Height := Value;
+end;
+
+function TSPCDriveCombo.GetWidth: Integer;
+begin
+ Result := fControl.Width;
+end;
+
+procedure TSPCDriveCombo.SetWidth(Value: Integer);
+begin
+ fControl.Width := Value;
+end;
+
+function TSPCDriveCombo.GetTop: Integer;
+begin
+ Result := fControl.Top;
+end;
+
+procedure TSPCDriveCombo.SetTop(Value: Integer);
+begin
+ fControl.Top := Value;
+end;
+
+function TSPCDriveCombo.GetLeft: Integer;
+begin
+ Result := fControl.Left;
+end;
+
+procedure TSPCDriveCombo.SetLeft(Value: Integer);
+begin
+ fControl.Left := Value;
+end;
+
+function TSPCDriveCombo.DoMeasureItem(Sender: PObj; Idx: Integer): Integer;
+begin
+ Result := 16;
+end;
+
+{ TSPCFileList }
+
+function NewTSPCFileListBox;
+var
+ p : PSPCFileList;
+begin
+ Options := Options + [loOwnerDrawFixed];
+ New(p, Create);
+ AOwner.Add2AutoFree(p);
+ p.fControl := NewListBox(AOwner, Options);
+ // p.fControl.OnMouseDblClk:=p.DoMouseDblClick;
+ p.fControl.OnChange := p.DoSelChange;
+ p.fControl.Font.FontHeight := -8;
+ p.fFileList := NewDirList('', '', 0);
+ p.fControl.OnDrawItem := p.DrawOneItem;
+ p.fFont := NewFont;
+ p.fIcons := NewImageList(nil);
+ p.fIcons.LoadSystemIcons(true);
+ p.fControl.OnMouseDblClk := p.DoMouseDblClk;
+ p.fControl.Font.FontHeight := -11;
+ Result := p;
+end;
+
+function TSPCFileList.SetAlign(Value: TControlAlign): PSPCFileList;
+begin
+ fControl.Align := Value;
+ Result := @Self;
+end;
+
+procedure TSPCFileList.SetFilters(Value: string);
+begin
+ fFilters := Value;
+ Path := Path;
+end;
+
+procedure TSPCFileList.DoSelChange;
+begin
+ if Assigned(fOnSelChange) then fOnSelChange(@Self);
+end;
+
+destructor TSPCFileList.Destroy;
+begin
+ fFont.Free;
+ inherited;
+end;
+
+function TSPCFileList.SetPosition(X, Y: integer): PSPCFileList;
+begin
+ fControl.Left := X;
+ fControl.Top := Y;
+ Result := @self;
+end;
+
+function TSPCFileList.SetSize(X, Y: integer): PSPCFileList;
+begin
+ fControl.Width := X;
+ fControl.Height := Y;
+ Result := @self;
+end;
+
+function TSPCFileList.GetFont;
+begin
+ Result := fControl.Font;
+end;
+
+procedure TSPCFileList.SetFont(Value: PGraphicTool);
+begin
+ fControl.Font.Assign(Value);
+end;
+
+procedure TSPCFileList.SetPath(Value: KOLstring);
+var
+ i : Integer;
+ fValue : string;
+begin
+ fValue := Value;
+ if Length(fValue) > 0 then begin
+ if not (fValue[Length(fValue)] = '\') then fValue := fValue + '\';
+ end;
+ if DirectoryExists(fValue) then begin
+ fFileList.Clear;
+ fFileList.ScanDirectoryEx(FileShortPath(fValue), Filters, FILE_ATTRIBUTE_NORMAL and not FILE_ATTRIBUTE_DIRECTORY);
+ fControl.Clear;
+ fControl.Color := fColor;
+ case _SortBy of
+ sbName: fFileList.Sort([sdrByName]);
+ sbExtention: fFileList.Sort([sdrByExt]);
+ end;
+ for i := 1 to fFileList.Count do if not fFileList.IsDirectory[i - 1] then fControl.Add(fFileList.Names[i - 1]);
+ fPath := fValue;
+ if fDoCase = ctLower then for i := 0 to fControl.Count - 1 do fControl.Items[i] := LowerCase(fControl.Items[i]);
+ if fDoCase = ctUpper then for i := 0 to fControl.Count - 1 do fControl.Items[i] := UpperCase(fControl.Items[i]);
+ end else begin
+ fControl.Clear;
+ fPath := '';
+ end;
+ if fIntegralHeight then begin
+ fControl.Height := Round(fControl.Height / 16) * 16 + 4;
+ end;
+end;
+
+procedure TSPCFileList.SetIntegralHeight;
+begin
+ fIntegralHeight := Value;
+ if fIntegralHeight then begin
+ fControl.Height := (fControl.Height div 14) * 14 + 6;
+ end;
+end;
+
+function TSPCFileList.GetFileName: string;
+begin
+ Result := fControl.Items[fControl.CurIndex];
+end;
+
+function TSPCFileList.GetFullFileName: string;
+begin
+ Result := Path + fControl.Items[fControl.CurIndex]
+end;
+
+function TSPCFileList.Count: LongInt;
+begin
+ Result := fControl.Count;
+end;
+
+function TSPCFileList.GetCurIndex: Integer;
+begin
+ Result := fControl.CurIndex;
+end;
+
+procedure TSPCFileList.SetCurIndex(Value: Integer);
+begin
+ fControl.CurIndex := Value;
+end;
+
+procedure TSPCFileList.SetHasBorder(Value: Boolean);
+var
+ NewStyle : DWORD;
+begin
+ if Value then
+ fControl.Style := fControl.Style or WS_THICKFRAME
+ else begin
+ NewStyle := fControl.Style and not (WS_BORDER or WS_THICKFRAME or WS_DLGFRAME or WS_CAPTION
+ or WS_MINIMIZEBOX or WS_MAXIMIZEBOX or WS_SYSMENU or WS_HSCROLL);
+ if not fControl.IsControl then NewStyle := NewStyle or WS_POPUP;
+ fControl.Style := NewStyle;
+ fControl.ExStyle := fControl.ExStyle and not (WS_EX_CONTROLPARENT or WS_EX_DLGMODALFRAME
+ or WS_EX_WINDOWEDGE or WS_EX_CLIENTEDGE);
+ end;
+end;
+
+function TSPCFileList.GetSelected(Index: Integer): Boolean;
+begin
+ if Index > Count - 1 then Result := False else Result := fControl.ItemSelected[Index];
+end;
+
+procedure TSPCFileList.SetSelected(Index: Integer; Value: Boolean);
+begin
+ if Index <= Count - 1 then fControl.ItemSelected[Index] := Value;
+end;
+
+function TSPCFileList.TotalSelected: Integer;
+var
+ i : Integer;
+begin
+ Result := 0;
+ if fControl.Count = 0 then Result := -1 else begin
+ for i := 0 to fControl.Count - 1 do if fControl.ItemSelected[i] then Result := Result + 1;
+ end;
+end;
+
+function TSPCFileList.GetItem(Index: Integer): string;
+begin
+ Result := fControl.Items[Index];
+end;
+
+function TSPCFileList.GetHeight: Integer;
+begin
+ Result := fControl.Height;
+end;
+
+procedure TSPCFileList.SetHeight(Value: Integer);
+begin
+ fControl.Height := Value;
+end;
+
+function TSPCFileList.GetWidth: Integer;
+begin
+ Result := fControl.Width;
+end;
+
+procedure TSPCFileList.SetWidth(Value: Integer);
+begin
+ fControl.Width := Value;
+end;
+
+function TSPCFileList.GetTop: Integer;
+begin
+ Result := fControl.Top;
+end;
+
+procedure TSPCFileList.SetTop(Value: Integer);
+begin
+ fControl.Top := Value;
+end;
+
+function TSPCFileList.GetVisible: Boolean; // Edited
+begin
+ Result := FControl.Visible;
+end;
+
+procedure TSPCFileList.SetVisible(Value: Boolean); // Edited
+begin
+ FControl.Visible := Value;
+end;
+
+function TSPCFileList.GetLeft: Integer;
+begin
+ Result := fControl.Left;
+end;
+
+procedure TSPCFileList.SetLeft(Value: Integer);
+begin
+ fControl.Left := Value;
+end;
+
+function TSPCFileList.GetFocused: Boolean;
+begin
+ Result := fControl.Focused;
+end;
+
+procedure TSPCFileList.SetFocused(Value: Boolean);
+begin
+ fControl.Focused := Value;
+end;
+
+function TSPCFileList.DrawOneItem(Sender: PObj; DC: HDC;
+ const Rect: TRect; ItemIdx: Integer; DrawAction: TDrawAction;
+ ItemState: TDrawState): Boolean;
+var
+ T_Rect, B_Rect : TRect;
+ Ico : Integer;
+begin
+ SetBkMode(DC, opaque);
+ if ItemIdx > -1 then begin
+ PControl(Sender).CanResize := True;
+ T_Rect := Rect;
+ B_Rect := Rect;
+ T_Rect.Left := Rect.Left + 19;
+ B_Rect.Left := Rect.Left + 18;
+ PControl(Sender).Canvas.Pen.PenMode := pmCopy;
+ PControl(Sender).Canvas.Pen.Color := $0000FF;
+ PControl(Sender).Brush.Color := clWindow;
+ if (odsFocused in ItemState) or (odsSelected in ItemState) then begin
+ SetBkMode(DC, transparent);
+ PControl(Sender).Canvas.Brush.color := clWindow;
+ FillRect(DC, T_Rect, PControl(Sender).Canvas.Brush.Handle);
+ if (not (odsFocused in ItemState)) and ((odsSelected in ItemState)) then begin
+ PControl(Sender).Canvas.Brush.color := clInactiveBorder;
+ SetTextColor(DC, Font.Color);
+ fIcons.DrawingStyle := [];
+ end
+ else begin
+ PControl(Sender).Canvas.Brush.color := clHighLight;
+ SetTextColor(DC, $FFFFFF);
+ fIcons.DrawingStyle := [dsBlend50];
+ end;
+ FillRect(DC, T_Rect, PControl(Sender).Canvas.Brush.Handle);
+ end else begin
+ SetTextColor(DC, Font.Color);
+ PControl(Sender).Canvas.Brush.color := clWindow;
+ SelectObject(DC, PControl(Sender).Canvas.Brush.Handle);
+ FillRect(DC, B_Rect, PControl(Sender).Canvas.Brush.Handle);
+ fIcons.DrawingStyle := [];
+ end;
+ Ico := FileIconSystemIdx(Path + PControl(Sender).Items[ItemIdx]);
+ fIcons.Draw(Ico, DC, Rect.Left + 1, Rect.Top);
+ DrawText(DC, PKOLChar(PControl(Sender).Items[ItemIdx]), Length(PControl(Sender).Items[ItemIdx]), T_Rect, DT_SINGLELINE or DT_VCENTER or DT_NOPREFIX);
+ end;
+ PControl(Sender).Update;
+ Result := True; ///
+end;
+
+procedure TSPCFileList.DoMouseDblClk(Sender: PControl; var Mouse: TMouseEventData);
+begin
+ if ExecuteOnDblClk then
+ {$IFDEF UNICODE_CTRLS}
+ ShellExecuteW
+ {$ELSE}
+ ShellExecuteA
+ {$ENDIF}
+ (fControl.Handle, nil, PKOLChar(Path + Sender.Items[CurIndex]), '', '', SW_SHOW)
+ else
+ if Assigned(fOnMouseDblClick) then fOnMouseDblClick(@Self, Mouse);
+end;
+
+procedure TSPCFileList.SetSortBy(Value: TSortBy);
+begin
+ fSortBy := Value;
+ Path := Path;
+end;
+
+procedure TSPCFileList.SortByName;
+begin
+ _SortBy := sbName;
+end;
+
+procedure TSPCFileList.SortByExtention;
+begin
+ _SortBy := sbExtention;
+end;
+
+{ TSPCFilterCombo }
+
+function GetLastPos(c: char; s: string): Integer;
+var
+ i : Integer;
+begin
+ Result := 0;
+ for i := 1 to Length(s) do if s[i] = c then Result := i;
+end;
+
+function NewTSPCFilterComboBox;
+var
+ p : PSPCFilterCombo;
+ c : PControl;
+begin
+ c := NewComboBox(AOwner, [coReadOnly]);
+ New(p, create);
+ AOwner.Add2AutoFree(p);
+ p.fControl := c;
+ p.fFont := NewFont;
+ p.fControl.Font.Assign(p.fFont);
+ p.Font.FontHeight := -8;
+ p.fControl.Font.FontHeight := -8;
+ p.fControl.OnChange := p.DoChange;
+ p.fControl.OnMeasureItem := p.DoMeasureItem;
+ p.fFilterItems := NewList;
+ p.fCreated := False;
+ p.fInitialized := 0;
+ Result := p;
+end;
+
+function TSPCFilterCombo.SetAlign(Value: TControlAlign): PSPCFilterCombo;
+begin
+ fControl.Align := Value;
+ Result := @Self;
+end;
+
+procedure TSPCFilterCombo.Add;
+begin
+ fFilterItems.Add(TFilterItem.Create);
+ TFilterItem(fFilterItems.Items[fFilterItems.Count - 1]).Description := Copy(fNewFilter, 1, Pos('|', fNewFilter) - 1);
+ TFilterItem(fFilterItems.Items[fFilterItems.Count - 1]).Filter := Copy(fNewFilter, Pos('|', fNewFilter) + 1, Length(fNewFilter) - Pos('|', fNewFilter));
+ BuildList;
+end;
+
+procedure TSPCFilterCombo.DeleteItem;
+begin
+ fFilterItems.Delete(Index);
+end;
+
+function TSPCFilterCombo.Count: Integer;
+begin
+ Result := fFilterItems.Count;
+end;
+
+function TSPCFilterCombo.GetFilterItem;
+begin
+ Result := fFilterItems.Items[Index];
+end;
+
+procedure TSPCFilterCombo.Update;
+begin
+ DoChange(@Self);
+end;
+
+procedure TSPCFilterCombo.DoChange(Obj: PObj);
+begin
+ Filter := TFilterItem(fFilterItems.Items[fControl.CurIndex]).Filter;
+ if Assigned(fOnChange) then fOnChange(@Self);
+ if Assigned(fFileListBox) then fFileListBox.Filters := Filter;
+end;
+
+destructor TSPCFilterCombo.Destroy;
+begin
+ fFont.Free;
+ inherited;
+end;
+
+function TSPCFilterCombo.SetPosition(X, Y: integer): PSPCFilterCombo;
+begin
+ fControl.Left := X;
+ fControl.Top := Y;
+ Result := @self;
+end;
+
+function TSPCFilterCombo.SetSize(X, Y: integer): PSPCFilterCombo;
+begin
+ fControl.Width := X;
+ fControl.Height := Y;
+ Result := @self;
+end;
+
+function TSPCFilterCombo.GetFont;
+begin
+ Result := fFont;
+ fControl.Color := $FFFFFF;
+end;
+
+procedure TSPCFilterCombo.SetFont(Value: PGraphicTool);
+begin
+ fFont := Value;
+end;
+
+procedure TSPCFilterCombo.BuildList;
+var
+ i : Integer;
+begin
+ fControl.Color := Color;
+ fControl.Font.Assign(Font);
+ fControl.Clear;
+ if fFilterItems.Count > 0 then
+ for i := 1 to fFilterItems.Count do fControl.Add(TFilterItem(fFilterItems.Items[i - 1]).Description);
+end;
+
+procedure TSPCFilterCombo.SetFilter(Value: string);
+begin
+ fFilter := Value;
+ if Assigned(fOnChange) then fOnChange(@Self);
+end;
+
+procedure TSPCFilterCombo.SetCurIndex(Value: Integer);
+begin
+ fCurIndex := Value;
+ fControl.CurIndex := Value;
+ Inc(fInitialized);
+ if fInitialized > 2 then fInitialized := 2;
+ if Assigned(fOnChange) then if fInitialized = 2 then fOnChange(@Self);
+end;
+
+function TSPCFilterCombo.GetHeight: Integer;
+begin
+ Result := fControl.Height;
+end;
+
+procedure TSPCFilterCombo.SetHeight(Value: Integer);
+begin
+ fControl.Height := Value;
+end;
+
+function TSPCFilterCombo.GetWidth: Integer;
+begin
+ Result := fControl.Width;
+end;
+
+procedure TSPCFilterCombo.SetWidth(Value: Integer);
+begin
+ fControl.Width := Value;
+end;
+
+function TSPCFilterCombo.GetTop: Integer;
+begin
+ Result := fControl.Top;
+end;
+
+procedure TSPCFilterCombo.SetTop(Value: Integer);
+begin
+ fControl.Top := Value;
+end;
+
+function TSPCFilterCombo.GetLeft: Integer;
+begin
+ Result := fControl.Left;
+end;
+
+procedure TSPCFilterCombo.SetLeft(Value: Integer);
+begin
+ fControl.Left := Value;
+end;
+
+function TSPCFilterCombo.DoMeasureItem(Sender: PObj; Idx: Integer): Integer;
+begin
+ Result := 16;
+end;
+
+function TSPCFilterCombo.GetItem(Index: Integer): string;
+begin
+ Result := fControl.Items[Index];
+end;
+
+procedure TSPCFilterCombo.SetItem(Index: Integer; Value: string);
+begin
+ if Index + 1 > fFilterItems.Count then fFilterItems.Add(TFilterItem.Create);
+ TFilterItem(fFilterItems.Items[Index]).Description := Copy(Value, 1, Pos('|', Value) - 1);
+ TFilterItem(fFilterItems.Items[Index]).Filter := Copy(Value, Pos('|', Value) + 1, Length(Value) - Pos('|', Value));
+ BuildList;
+end;
+
+function TSPCFilterCombo.GetFilter: string;
+begin
+ Result := TFilterItem(fFilterItems.Items[fControl.CurIndex]).Filter;
+end;
+
+function TSPCFilterCombo.GetCurIndex: Integer;
+begin
+ Result := fControl.CurIndex;
+end;
+
+{ TSPCStatus }
+
+function NewTSPCStatusBar;
+var
+ p : PSPCStatus;
+ c : PControl;
+ Style : DWord;
+begin
+ Style := $00000000;
+ Style := Style or WS_VISIBLE or WS_CHILD or WS_CLIPSIBLINGS or WS_CLIPCHILDREN; //msctls_statusbar32
+ c := _NewControl(AOwner, 'msctls_statusbar32', Style, True, nil);
+ // c:=_NewStatusBar(AOwner);
+ c.Style := Style;
+ c.ExStyle := c.ExStyle xor WS_EX_CLIENTEDGE;
+ c.BringToFront;
+ New(p, create);
+ p.fControl := c;
+ Result := p;
+end;
+
+destructor TSPCStatus.Destroy;
+begin
+ fFont.Free;
+ inherited;
+end;
+
+function TSPCStatus.SetAlign(Value: TControlAlign): PSPCStatus;
+begin
+ fControl.Align := Value;
+ Result := @Self;
+end;
+
+function TSPCStatus.SetPosition(X, Y: integer): PSPCStatus;
+begin
+ fControl.Left := X;
+ fControl.Top := Y;
+ Result := @self;
+end;
+
+function TSPCStatus.SetSize(X, Y: integer): PSPCStatus;
+begin
+ fControl.Width := X;
+ fControl.Height := Y;
+ Result := @self;
+end;
+
+function TSPCStatus.GetFont;
+begin
+ Result := fControl.Font;
+end;
+
+procedure TSPCStatus.SetFont(Value: PGraphicTool);
+begin
+ fControl.Font.Assign(Value);
+end;
+
+function TSPCStatus.GetHeight: Integer;
+begin
+ Result := fControl.Height;
+end;
+
+procedure TSPCStatus.SetHeight(Value: Integer);
+begin
+ fControl.Height := Value;
+end;
+
+function TSPCStatus.GetWidth: Integer;
+begin
+ Result := fControl.Width;
+end;
+
+procedure TSPCStatus.SetWidth(Value: Integer);
+begin
+ fControl.Width := Value;
+end;
+
+function TSPCStatus.GetTop: Integer;
+begin
+ Result := fControl.Top;
+end;
+
+procedure TSPCStatus.SetTop(Value: Integer);
+begin
+ fControl.Top := Value;
+end;
+
+function TSPCStatus.GetLeft: Integer;
+begin
+ Result := fControl.Left;
+end;
+
+procedure TSPCStatus.SetLeft(Value: Integer);
+begin
+ fControl.Left := Value;
+end;
+
+procedure TSPCStatus.SetSimpleStatusText(Value: string);
+begin
+ fControl.Caption := Value;
+end;
+
+function TSPCStatus.GetSimpleStatusText: string;
+begin
+ Result := fControl.Caption;
+end;
+
+end.
+
diff --git a/plugins/Libs/KOLDEF.inc b/plugins/Libs/KOLDEF.inc
new file mode 100644
index 0000000000..cc7a004604
--- /dev/null
+++ b/plugins/Libs/KOLDEF.inc
@@ -0,0 +1,308 @@
+{$IFDEF VER90}
+ {$DEFINE _D2}
+ {$DEFINE _D2orD3}
+ {$DEFINE _D2orD3orD4}
+{$ENDIF}
+
+{$IFDEF VER100}
+ {$DEFINE _D3}
+ {$DEFINE _D3orHigher}
+ {$DEFINE _D2orD3}
+ {$DEFINE _D2orD3orD4}
+ {$DEFINE _D3orD4}
+{$ENDIF}
+
+{$IFDEF VER120}
+ {$DEFINE _D3orHigher}
+ {$DEFINE _D3orD4}
+ {$DEFINE _D4}
+ {$DEFINE _D4orHigher}
+ {$DEFINE _D2orD3orD4}
+ {$IFnDEF KOL_MCK}
+ {$DEFINE INPACKAGE}
+ {$ENDIF}
+{$ENDIF}
+
+{$IFDEF VER130}
+ {$DEFINE _D3orHigher}
+ {$DEFINE _D4orHigher}
+ {$DEFINE _D5}
+ {$DEFINE _D5orHigher}
+{$ENDIF}
+
+{$IFDEF VER140}
+ {$DEFINE _D3orHigher}
+ {$DEFINE _D4orHigher}
+ {$DEFINE _D5orHigher}
+ {$DEFINE _D6}
+ {$DEFINE _D6orHigher}
+{$ENDIF}
+
+{$IFDEF VER150}
+ {$DEFINE _D3orHigher}
+ {$DEFINE _D4orHigher}
+ {$DEFINE _D5orHigher}
+ {$DEFINE _D6orHigher}
+ {$DEFINE _D7}
+ {$DEFINE _D7orHigher}
+{$WARN UNIT_DEPRECATED OFF}
+{$WARN SYMBOL_PLATFORM OFF}
+{$WARN UNSAFE_TYPE OFF}
+{$WARN UNSAFE_CAST OFF}
+{$WARN UNSAFE_CODE OFF}
+{$ENDIF}
+
+{$IFDEF VER160} // Delphi 8
+Delphi version 8 not supported! (delphi 8 is .net only)
+{$ENDIF}
+
+{$IFDEF VER170} // Delphi 2005
+ {$DEFINE _D3orHigher}
+ {$DEFINE _D4orHigher}
+ {$DEFINE _D5orHigher}
+ {$DEFINE _D6orHigher}
+ {$DEFINE _D7orHigher}
+ {$DEFINE _D8}
+ {$DEFINE _D8orHigher}
+ {$DEFINE _D2005}
+ {$DEFINE _D2005orHigher}
+{$WARN UNIT_DEPRECATED OFF}
+{$WARN SYMBOL_PLATFORM OFF}
+{$WARN UNSAFE_TYPE OFF}
+{$WARN UNSAFE_CAST OFF}
+{$WARN UNSAFE_CODE OFF}
+{$ENDIF}
+
+{$IFDEF VER180} // Delphi 2006
+ {$DEFINE _D3orHigher}
+ {$DEFINE _D4orHigher}
+ {$DEFINE _D5orHigher}
+ {$DEFINE _D6orHigher}
+ {$DEFINE _D7orHigher}
+ {$DEFINE _D8orHigher}
+ {$DEFINE _D2005}
+ {$DEFINE _D2005orHigher}
+ {$DEFINE _D2006orHigher}
+{$WARN UNIT_DEPRECATED OFF}
+{$WARN SYMBOL_PLATFORM OFF}
+{$WARN UNSAFE_TYPE OFF}
+{$WARN UNSAFE_CAST OFF}
+{$WARN UNSAFE_CODE OFF}
+{$ENDIF}
+
+{$IFDEF _D2005orHigher}
+
+ // by Thaddy de Koning:
+ {$IFDEF VER185} // Delphi 2007 ( and Highlander )
+ {$DEFINE _D3orHigher}
+ {$DEFINE _D4orHigher}
+ {$DEFINE _D5orHigher}
+ {$DEFINE _D6orHigher}
+ {$DEFINE _D7orHigher}
+ {$DEFINE _D2005orHigher}
+ {$DEFINE _D2006orHigher}
+ {$DEFINE _D2007}
+ {$DEFINE _D2007orHigher}
+ {$WARN UNIT_DEPRECATED OFF}
+ {$WARN SYMBOL_PLATFORM OFF}
+ {$WARN UNSAFE_TYPE OFF}
+ {$WARN UNSAFE_CAST OFF}
+ {$WARN UNSAFE_CODE OFF}
+ {$ENDIF}
+
+{$INLINE OFF}
+{$ENDIF}
+
+{$IFDEF VER200} // Delphi 2009
+ {$DEFINE _D3orHigher}
+ {$DEFINE _D4orHigher}
+ {$DEFINE _D5orHigher}
+ {$DEFINE _D6orHigher}
+ {$DEFINE _D7orHigher}
+ {$DEFINE _D2005orHigher}
+ {$DEFINE _D2006orHigher}
+ {$DEFINE _D2007orHigher}
+ {$DEFINE _D2009}
+ {$DEFINE _D2009orHigher}
+{$WARN UNIT_DEPRECATED OFF}
+{$WARN SYMBOL_PLATFORM OFF}
+{$WARN UNSAFE_TYPE OFF}
+{$WARN UNSAFE_CAST OFF}
+{$WARN UNSAFE_CODE OFF}
+{$ENDIF}
+
+{$IFDEF VER210} // Delphi 2010
+ {$DEFINE _D3orHigher}
+ {$DEFINE _D4orHigher}
+ {$DEFINE _D5orHigher}
+ {$DEFINE _D6orHigher}
+ {$DEFINE _D7orHigher}
+ {$DEFINE _D2005orHigher}
+ {$DEFINE _D2006orHigher}
+ {$DEFINE _D2007orHigher}
+ {$DEFINE _D2009orHigher}
+ {$DEFINE _D2010}
+ {$DEFINE _D2010orHigher}
+ {$WARN UNIT_DEPRECATED OFF}
+ {$WARN SYMBOL_PLATFORM OFF}
+ {$WARN UNSAFE_TYPE OFF}
+ {$WARN UNSAFE_CAST OFF}
+ {$WARN UNSAFE_CODE OFF}
+{$ENDIF}
+
+{$IFDEF VER220} // Delphi XE
+ {$DEFINE _D3orHigher}
+ {$DEFINE _D4orHigher}
+ {$DEFINE _D5orHigher}
+ {$DEFINE _D6orHigher}
+ {$DEFINE _D7orHigher}
+ {$DEFINE _D2005orHigher}
+ {$DEFINE _D2006orHigher}
+ {$DEFINE _D2007orHigher}
+ {$DEFINE _D2009orHigher}
+ {$DEFINE _D2010orHigher}
+ {$DEFINE _DXE}
+ {$DEFINE _DXEorHigher}
+ {$WARN UNIT_DEPRECATED OFF}
+ {$WARN SYMBOL_PLATFORM OFF}
+ {$WARN UNSAFE_TYPE OFF}
+ {$WARN UNSAFE_CAST OFF}
+ {$WARN UNSAFE_CODE OFF}
+{$ENDIF}
+
+{$IFDEF VER230} // Delphi XE2
+ {$DEFINE _D3orHigher}
+ {$DEFINE _D4orHigher}
+ {$DEFINE _D5orHigher}
+ {$DEFINE _D6orHigher}
+ {$DEFINE _D7orHigher}
+ {$DEFINE _D2005orHigher}
+ {$DEFINE _D2006orHigher}
+ {$DEFINE _D2007orHigher}
+ {$DEFINE _D2009orHigher}
+ {$DEFINE _D2010orHigher}
+ {$DEFINE _DXE}
+ {$DEFINE _DXEorHigher}
+ {$DEFINE _DXE2}
+ {$DEFINE _DXE2orHigher}
+ {$DEFINE PAS_VERSION}
+ {$WARN UNIT_DEPRECATED OFF}
+ {$WARN SYMBOL_PLATFORM OFF}
+ {$WARN UNSAFE_TYPE OFF}
+ {$WARN UNSAFE_CAST OFF}
+ {$WARN UNSAFE_CODE OFF}
+{$ENDIF}
+
+(*
+// TODO: check DLL project
+{$IFNDEF NO_STRIP_RELOC}
+ // by Thaddy de Koning:
+ {$IFDEF _D2006orHigher}
+ // strips relocs, like stripreloc.exe does
+ {$SetPEFlags 1}
+// {$SETPEFlAGS IMAGE_FILE_RELOCS_STRIPPED or IMAGE_FILE_DEBUG_STRIPPED or IMAGE_FILE_LINE_NUMS_STRIPPED or IMAGE_FILE_LOCAL_SYMS_STRIPPED or IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP or IMAGE_FILE_NET_RUN_FROM_SWAP}
+ {$ENDIF}
+{$ENDIF}
+*)
+{$IFDEF FPC}
+{------------------------------------
+by Thaddy de Koning:
+
+FPC version 2.1.1 is very compatible with Delphi and kol now.
+You can simply use the $(DELPHI)\source\rtl\win\*.pas files from Delphi 4/5 instead of the prepared files that were needed for
+FPC1.X
+
+That is all to have full compatibility.
+------------------------------------}
+ {$DEFINE PAS_VERSION}
+ {$IFDEF VER2}
+ {$DEFINE _D3orHigher}
+ {$DEFINE _D4orHigher}
+ {$DEFINE _D5orHigher}
+ {$DEFINE _D6orHigher}
+ {$DEFINE _D7}
+ {$DEFINE _D7orHigher}
+ {$ENDIF}
+{$ENDIF FPC}
+
+{$IFNDEF _NOT_KOLCtrlWrapper_}
+ {$DEFINE _KOLCtrlWrapper_}
+{$ENDIF}
+
+{$IFNDEF _NOT_KOLCtrlWrapper_}
+ {$DEFINE _KOLCtrlWrapper_}
+{$ENDIF}
+
+//// from delphidef.inc ////
+
+{$IFDEF WIN64}
+ {$DEFINE x64}
+ {$DEFINE PAS_VERSION}
+{$ENDIF}
+
+//{$DEFINE _FPC}
+{$DEFINE ASM_VERSION} // Comment this line to produce Pascal code.
+ // Or, just add PAS_VERSION to conditionals
+ // of your project (must be rebuilt).
+
+{$IFDEF ASM_VERSION}
+ {$IFDEF PAS_VERSION}
+ {$UNDEF ASM_VERSION}
+ // To compile a project with ASM_VERSION option turned off,
+ // define a symbol PAS_VERSION in project options.
+ {$ENDIF}
+{$ENDIF}
+
+//{$DEFINE USE_CONSTRUCTORS}
+// Comment this line to produce smaller code if constructors are not used.
+// When uncommented, this definition allows to create descendant controls
+// and objects overriding constructors, which are actually members of objects.
+// Otherwise, global functions (usually named New<ObjectName>) are used to
+// create and initialize object instances. This gives smaller code, but
+// prevents from using OOP inheritance.
+// Note: creating descendant objects derived from TObj does not require using
+// of this option. It is actually needed only for deriving new controls on
+// base of TControl. See also option USE_CUSTOMEXTENSIONS below.
+
+//{$DEFINE USE_CUSTOMEXTENSIONS}
+// Uncomment this option or add it to your project conditional defines,
+// if You wish to extend existing TControl object from
+// the inner of those. When this option is turned on, include directive at the
+// tail of TControl declaration is enabled, causing a compiler to include your
+// portion of source directly into the TControl body. See comments near this
+// directive there. (Search the word: USE_CUSTOMEXTENSIONS).
+// Please note, that this option is not fully supported now.
+
+//{$IFNDEF NOT_UNLOAD_RICHEDITLIB}
+// {$DEFINE UNLOAD_RICHEDITLIB}
+//{$ENDIF}
+// You can freely comment this directive. 1st, if the application does not
+// use richedit control. 2nd, even if it does, freeing the library handle
+// actually is not needed.
+// Another way to turn this option off is to define symbol NOT_UNLOAD_RICHEDITLIB
+// in your project options.
+
+//{$DEFINE TEST_VERSION}
+{$IFNDEF _D6orHigher}
+ {$DEFINE PARANOIA} //seems not needed from D6 !!! Inprise fixed this, finally...
+{$ENDIF}
+
+
+{$IFNDEF USE_OLD_FLAGS}
+ {$DEFINE USE_FLAGS}
+{$ELSE} {$UNDEF USE_FLAGS}
+{$ENDIF}
+
+{$IFnDEF EVENTS_STATIC}
+ {$DEFINE EVENTS_DYNAMIC}
+{$ENDIF}
+{$IFnDEF CMDACTIONS_RECORD}
+ {$DEFINE COMMANDACTIONS_OBJ}
+ {$DEFINE PACK_COMMANDACTIONS}
+ {$IFDEF NOT_PACK_COMMANDACTIONS}
+ {$UNDEF PACK_COMMANDACTIONS}
+ {$ENDIF}
+{$ENDIF}
+
+{$DEFINE KOL3XX}
+{$DEFINE DIBPixels32bitWithAlpha} \ No newline at end of file
diff --git a/plugins/Libs/KOL_ASM.inc b/plugins/Libs/KOL_ASM.inc
new file mode 100644
index 0000000000..f83b0b7851
--- /dev/null
+++ b/plugins/Libs/KOL_ASM.inc
@@ -0,0 +1,15855 @@
+//------------------------------------------------------------------------------
+// KOL_ASM.inc (to inlude in KOL.pas)
+// v 3.17
+
+function MsgBox( const S: KOLString; Flags: DWORD ): DWORD;
+asm
+ PUSH EDX
+ PUSH EAX
+
+ MOV ECX, [Applet]
+ XOR EAX, EAX
+ {$IFDEF SAFE_CODE}
+ JECXZ @@1
+ {$ENDIF}
+ {$IFDEF SNAPMOUSE2DFLTBTN}
+ PUSHAD
+ XCHG EAX, ECX
+ XOR EDX, EDX
+ PUSH EDX
+ PUSH EDX
+ PUSH EDX
+ PUSH EAX
+ MOV EDX, offset[WndProcSnapMouse2DfltBtn]
+ CALL TControl.AttachProc
+ CALL TControl.Postmsg
+ POPAD
+ {$ENDIF}
+
+ MOV EAX, [ECX].TControl.fCaption
+ {$IFDEF SNAPMOUSE2DFLTBTN}
+ MOV ECX, [ECX].TControl.fHandle
+ {$ENDIF}
+@@1:
+ XCHG EAX, [ESP]
+ PUSH EAX
+ PUSH 0
+ {$IFDEF UNICODE_CTRLS}
+ CALL MessageBoxW
+ {$ELSE}
+ CALL MessageBox
+ {$ENDIF}
+ {$IFDEF SNAPMOUSE2DFLTBTN}
+ MOV ECX, [Applet]
+ {$IFDEF SAFE_CODE}
+ JECXZ @@2
+ {$ENDIF}
+ PUSH EAX
+ XCHG EAX, ECX
+ MOV EDX, offset[WndProcSnapMouse2DfltBtn]
+ CALL TControl.DetachProc
+ POP EAX
+@@2:
+ {$ENDIF}
+end;
+
+function MakeRect( Left, Top, Right, Bottom: Integer ): TRect; stdcall;
+asm
+ PUSH ESI
+ PUSH EDI
+
+ MOV EDI, @Result
+ LEA ESI, [Left]
+
+ MOVSD
+ MOVSD
+ MOVSD
+ MOVSD
+
+ POP EDI
+ POP ESI
+end;
+
+function RectsEqual( const R1, R2: TRect ): Boolean;
+asm
+ //LEA EAX, [R1]
+ //LEA EDX, [R2]
+ MOV ECX, size_TRect
+ CALL CompareMem
+end;
+
+function PointInRect( const P: TPoint; const R: TRect ): Boolean;
+asm
+ PUSH ESI
+ MOV ECX, EAX
+ MOV ESI, EDX
+ LODSD
+ CMP EAX, [ECX]
+ JG @@fail
+ LODSD
+ CMP EAX, [ECX+4]
+ JG @@fail
+ LODSD
+ CMP [ECX], EAX
+ JG @@fail
+ LODSD
+ CMP [ECX+4], EAX
+@@fail: SETLE AL
+ POP ESI
+end;
+
+function OffsetPoint( const T: TPoint; dX, dY: Integer ): TPoint;
+asm
+ ADD EDX, [EAX].TPoint.X
+ ADD ECX, [EAX].TPoint.Y
+ MOV EAX, [Result]
+ MOV [EAX].TPoint.X, EDX
+ MOV [EAX].TPoint.Y, ECX
+end;
+
+function OffsetSmallPoint( const T: TSmallPoint; dX, dY: SmallInt ): TSmallPoint;
+asm
+ SHL EDX, 16
+ SHLD ECX, EDX, 16
+ CALL @@1
+@@1:
+ ROL EAX, 16
+ ROL ECX, 16
+ ADD AX, CX
+end;
+
+function Point2SmallPoint( const T: TPoint ): TSmallPoint;
+asm
+ XCHG EDX, EAX
+ MOV EAX, [EDX].TPoint.Y-2
+ MOV AX, word ptr [EDX].TPoint.X
+end;
+
+function SmallPoint2Point( const T: TSmallPoint ): TPoint;
+asm
+ MOVSX ECX, AX
+ MOV [EDX].TPoint.X, ECX
+ SAR EAX, 16
+ MOV [EDX].TPoint.Y, EAX
+end;
+
+function MakePoint( X, Y: Integer ): TPoint;
+asm
+ MOV ECX, @Result
+ MOV [ECX].TPoint.x, EAX
+ MOV [ECX].TPoint.y, EDX
+end;
+
+function MakeSmallPoint( X, Y: Integer ): TSmallPoint;
+asm
+ SHL EAX, 16
+ SHRD EAX, EDX, 16
+end;
+
+function MakeFlags( FlgSet: PDWORD; FlgArray: array of Integer): Integer;
+asm
+ PUSH EBX
+ PUSH ESI
+ MOV EBX, [EAX]
+ MOV ESI, EDX
+ XOR EDX, EDX
+ INC ECX
+ JZ @@exit
+@@loo:
+ LODSD
+ TEST EAX, EAX
+ JGE @@ge
+ NOT EAX
+ TEST BL, 1
+ JZ @@or
+ DEC EBX
+@@ge:
+ TEST BL, 1
+ JZ @@nx
+@@or:
+ OR EDX, EAX
+@@nx:
+ SHR EBX, 1
+ LOOP @@loo
+
+@@exit:
+ XCHG EAX, EDX
+ POP ESI
+ POP EBX
+end;
+
+constructor TObj.Create;
+asm
+ //CALL System.@ObjSetup - Generated always by compiler
+ //JZ @@exit
+
+ PUSH EAX
+ MOV EDX, [EAX]
+ CALL dword ptr [EDX]
+ POP EAX
+
+@@exit:
+end;
+
+{$IFDEF OLD_REFCOUNT}
+procedure TObj.DoDestroy;
+asm
+ MOV EDX, [EAX].fRefCount
+ SAR EDX, 1
+ JZ @@1
+ JC @@exit
+ DEC [EAX].fRefCount
+ STC
+
+@@1: JC @@exit
+ MOV EDX, [EAX]
+ CALL dword ptr [EDX + 4]
+@@exit:
+end;
+{$ENDIF OLD_REFCOUNT}
+
+function TObj.RefDec: Integer;
+asm
+ TEST EAX, EAX
+ JZ @@exit
+
+ SUB [EAX].fRefCount, 2
+ JGE @@exit
+ {$IFDEF OLD_REFCOUNT}
+ TEST [EAX].fRefCount, 1
+ JZ @@exit
+ MOV EDX, [EAX]
+ {$ENDIF}
+ MOV EDX, [EAX]
+ PUSH dword ptr [EDX+4]
+@@exit:
+end;
+
+{$IFDEF OLD_FREE}
+procedure TObj.Free;
+asm
+ //TEST EAX,EAX
+ JMP RefDec
+end;
+{$ENDIF OLD_FREE}
+
+{$IFNDEF CRASH_DEBUG}
+destructor TObj.Destroy;
+asm
+ PUSH EAX
+ CALL Final
+ POP EAX
+ {$IFDEF USE_NAMES}
+ PUSH EAX
+ XOR EDX, EDX
+ XOR ECX, ECX
+ CALL SetName
+ POP EAX
+ PUSH EAX
+ XOR ECX, ECX
+ XCHG ECX, [EAX].fNamedObjList
+ XCHG EAX, ECX
+ CALL TObj.RefDec
+ POP EAX
+ {$ENDIF}
+ XOR EDX, EDX
+ CALL System.@FreeMem
+ //CALL System.@Dispose
+end;
+{$ENDIF}
+
+procedure TObj.Add2AutoFree(Obj: PObj);
+asm //cmd //opd
+ PUSH EBX
+ PUSH EDX
+ XCHG EBX, EAX
+ MOV EAX, [EBX].fAutoFree
+ TEST EAX, EAX
+ JNZ @@1
+ CALL NewList
+ MOV [EBX].fAutoFree, EAX
+@@1: MOV EBX, EAX
+ XOR EDX, EDX
+ POP ECX
+ CALL TList.Insert
+ XCHG EAX, EBX
+ XOR EDX, EDX
+ MOV ECX, offset TObj.RefDec
+ //XOR ECX, ECX
+ CALL TList.Insert
+ POP EBX
+end;
+
+procedure TObj.Add2AutoFreeEx( Proc: TObjectMethod );
+asm //cmd //opd
+ PUSH EBX
+ XCHG EAX, EBX
+ MOV EAX, [EBX].fAutoFree
+ TEST EAX, EAX
+ JNZ @@1
+ CALL NewList
+ MOV [EBX].fAutoFree, EAX
+@@1: XOR EDX, EDX
+ MOV ECX, [EBP+12] // Data
+ MOV EBX, EAX
+ CALL TList.Insert
+ XCHG EAX, EBX
+ XOR EDX, EDX
+ MOV ECX, [EBP+8] // Code
+ CALL TList.Insert
+ POP EBX
+end;
+
+procedure TObj.RemoveFromAutoFree(Obj: PObj);
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ MOV ECX, [EBX].fAutoFree
+ JECXZ @@exit
+ XCHG EAX, ECX
+ PUSH EAX
+ CALL TList.IndexOf
+ TEST EAX, EAX
+ POP EDX
+ XCHG EDX, EAX
+ JL @@exit
+ PUSH EAX
+ AND EDX, not 1
+ XOR ECX, ECX
+ MOV CL, 2
+ CALL TList.DeleteRange
+ POP EAX
+ MOV ECX, [EAX].TList.fCount
+ INC ECX
+ LOOP @@exit
+ LEA EAX, [EBX].fAutoFree
+ CALL Free_And_Nil
+@@exit:
+ POP EBX
+end;
+
+destructor TList.Destroy;
+asm
+ PUSH EAX
+ CALL TList.Clear
+ POP EAX
+ CALL TObj.Destroy
+end;
+
+procedure TList.SetCapacity( Value: Integer );
+asm
+ {$IFDEF TLIST_FAST}
+ CMP [EAX].fUseBlocks, 0
+ JZ @@old
+ CMP [EAX].fBlockList, 0
+ JZ @@old
+
+ XOR ECX, ECX
+ MOV CH, 1
+ CMP EDX, ECX
+ JLE @@256
+ MOV EDX, ECX
+@@256:
+
+@@just_set:
+ MOV [EAX].fCapacity, EDX
+ RET
+@@old:
+ {$ENDIF}
+ CMP EDX, [EAX].fCount
+ {$IFDEF USE_CMOV}
+ CMOVL EDX, [EAX].fCount
+ {$ELSE}
+ JGE @@1
+ MOV EDX, [EAX].fCount
+@@1: {$ENDIF}
+ CMP EDX, [EAX].fCapacity
+ JE @@exit
+
+ MOV [EAX].fCapacity, EDX
+ SAL EDX, 2
+ LEA EAX, [EAX].fItems
+ CALL System.@ReallocMem
+@@exit:
+end;
+
+procedure TList.Clear;
+asm
+ {$IFDEF TLIST_FAST}
+ PUSH EAX
+ MOV ECX, [EAX].fBlockList
+ JECXZ @@1
+ MOV EDX, [ECX].fItems
+ MOV ECX, [ECX].fCount
+ SHR ECX, 1
+ JZ @@1
+@@0:
+ MOV EAX, [EDX]
+ ADD EDX, 8
+ PUSH EDX
+ PUSH ECX
+ CALL System.@FreeMem
+ POP ECX
+ POP EDX
+ LOOP @@0
+@@1:
+ POP EAX
+ PUSH EAX
+ XOR EDX, EDX
+ MOV [EAX].fLastKnownBlockIdx, EDX
+ LEA EAX, [EAX].fBlockList
+ CALL Free_And_Nil
+ POP EAX
+ {$ENDIF}
+ PUSH [EAX].fItems
+ XOR EDX, EDX
+ MOV [EAX].fItems, EDX
+ MOV [EAX].fCount, EDX
+ MOV [EAX].fCapacity, EDX
+ POP EAX
+ CALL System.@FreeMem
+end;
+
+{$IFDEF ASM_NO_VERSION}
+procedure TList.Add( Value: Pointer );
+asm
+ PUSH EDX
+ {$IFDEF TLIST_FAST}
+ //if fUseBlocks and ((fCount >= 256) or Assigned( fBlockList )) then
+ CMP [EAX].fUseBlocks, 0
+ JZ @@old
+ MOV ECX, [EAX].fBlockList
+ CMP [EAX].fCount, 256
+ JGE @@1
+ JECXZ @@old
+@@1:
+ PUSH EBX
+ PUSH ESI
+ XCHG EBX, EAX // EBX == @Self
+ MOV ESI, ECX
+ //if fBlockList = nil then
+ INC ECX
+ LOOP @@2
+ CALL NewList
+ XCHG ESI, EAX // ESI == fBlockList
+ MOV [EBX].fBlockList, ESI //fBlockList := NewList;
+ MOV [ESI].fUseBlocks, 0 //fBlockList.fUseBlocks := FALSE;
+ XOR EDX, EDX
+ XCHG EDX, [EBX].fItems //fItems := nil;
+ MOV EAX, ESI
+ CALL TList.Add //fBlockList.Add( fItems );
+ MOV EDX, [EBX].fCount
+ MOV EAX, ESI
+ CALL TList.Add //fBlockList.Add( Pointer( fCount ) );
+@@2:
+ //if fBlockList.fCount = 0 then
+ MOV ECX, [ESI].fCount
+ JECXZ @@2A
+ //LastBlockCount := Integer( fBlockList.fItems[ fBlockList.fCount-1 ] );
+ MOV EDX, [ESI].fItems
+ MOV EAX, [EDX+ECX*4-4]
+ //if LastBlockCount >= 256 then
+ CMP EAX, 256
+ JL @@3
+@@2A:
+ MOV EAX, ESI
+ XOR EDX, EDX
+ CALL TList.Add //fBlockList.Add( nil );
+ MOV EAX, ESI
+ XOR EDX, EDX
+ CALL TList.Add //fBlockList.Add( nil );
+ XOR EAX, EAX //LastBlockCount := 0;
+@@3:
+ PUSH EAX
+ //LastBlockStart := fBlockList.Items[ fBlockList.fCount-2 ];
+ MOV ECX, [ESI].fCount
+ MOV EDX, [ESI].fItems
+ LEA EDX, [EDX+ECX*4-8]
+ MOV EAX, [EDX]
+ //if LastBlockStart = nil then
+ TEST EAX, EAX
+ JNZ @@4
+ //GetMem( LastBlockStart, 256 * Sizeof( Pointer ) );
+ PUSH EDX
+ //MOV EAX, 1024
+ XOR EAX, EAX
+ MOV AH, 4
+ CALL System.@GetMem
+ POP EDX
+ //fBlockList.Items[ fBlockList.fCount-2 ] := LastBlockStart;
+ MOV [EDX], EAX
+@@4:
+ //fBlockList.Items[ fBlockList.fCount-1 ] := Pointer( LastBlockCount+1 );
+ INC dword ptr[EDX+4]
+ POP ECX // ECX == LastBlockCount
+
+ //inc( fCount );
+ INC [EBX].fCount
+ //PDWORD( Integer(LastBlockStart) + Sizeof(Pointer)*LastBlockCount )^ :=
+ // DWORD( Value );
+
+ POP ESI
+ POP EBX
+ POP EDX // EDX == Value
+ MOV [EAX+ECX*4], EDX
+ RET
+@@old:
+ {$ENDIF TLIST_FAST}
+ LEA ECX, [EAX].fCount
+ MOV EDX, [ECX]
+ INC dword ptr [ECX]
+ PUSH EDX
+ CMP EDX, [EAX].fCapacity
+ PUSH EAX
+ JL @@ok
+
+ MOV ECX, [EAX].fAddBy
+ TEST ECX, ECX
+ JNZ @@add
+ MOV ECX, EDX
+ SHR ECX, 2
+ INC ECX
+ @@add:
+ ADD EDX, ECX
+ CALL TList.SetCapacity
+@@ok:
+ POP ECX // ECX = Self
+ POP EAX // EAX = fCount -> Result (for TList.Insert)
+ POP EDX // EDX = Value
+
+ MOV ECX, [ECX].fItems
+ MOV [ECX + EAX*4], EDX
+end;
+{$ENDIF}
+
+{$IFDEF MoveItem_ASM}
+procedure TList.MoveItem(OldIdx, NewIdx: Integer);
+asm
+ CMP EDX, ECX
+ JE @@exit
+
+ CMP ECX, [EAX].fCount
+ JGE @@exit
+
+ PUSH EDI
+
+ MOV EDI, [EAX].fItems
+ PUSH dword ptr [EDI + EDX*4]
+ PUSH ECX
+ PUSH EAX
+ CALL TList.Delete
+ POP EAX
+ POP EDX
+ POP ECX
+
+ POP EDI
+ CALL TList.Insert
+@@exit:
+end;
+{$ENDIF}
+
+procedure TList.Put( Idx: Integer; Value: Pointer );
+asm
+ TEST EDX, EDX
+ JL @@exit
+ CMP EDX, [EAX].fCount
+ JGE @@exit
+ PUSH ESI
+ MOV ESI, ECX
+ {$IFDEF TLIST_FAST}
+ CMP [EAX].fUseBlocks, 0
+ JZ @@old
+ MOV ECX, [EAX].fBlockList
+ JECXZ @@old
+ PUSH EBX
+ PUSH ESI
+ PUSH EDI
+ PUSH EBP
+ XCHG EBX, EAX // EBX == @Self
+ XOR ECX, ECX // CountBefore := 0;
+ XOR EAX, EAX // i := 0;
+ CMP [EBX].fLastKnownBlockIdx, 0
+ JLE @@1
+ CMP EDX, [EBX].fLastKnownCountBefore
+ JL @@1
+ MOV ECX, [EBX].fLastKnownCountBefore
+ MOV EAX, [EBX].fLastKnownBlockIdx
+@@1:
+ MOV ESI, [EBX].fBlockList
+ MOV ESI, [ESI].fItems
+ MOV EDI, [ESI+EAX*8] // EDI = BlockStart
+ MOV ESI, [ESI+EAX*8+4] // ESI = CountCurrent
+ CMP ECX, EDX
+ JG @@next
+ LEA EBP, [ECX+ESI]
+ CMP EDX, EBP
+ JGE @@next
+ MOV [EBX].fLastKnownBlockIdx, EAX
+ MOV [EBX].fLastKnownCountBefore, ECX
+ SUB EDX, ECX
+ LEA EAX, [EDI+EDX*4]
+ POP EBP
+ POP EDI
+ POP ESI
+ POP EBX
+ MOV [EAX], ESI
+ POP ESI
+ RET
+@@next:
+ ADD ECX, ESI
+ INC EAX
+ JMP @@1
+@@old:
+ {$ENDIF}
+ MOV EAX, [EAX].fItems
+ MOV [EAX+EDX*4], ESI
+ POP ESI
+@@exit:
+end;
+
+function TList.Get( Idx: Integer ): Pointer;
+asm
+ TEST EDX, EDX
+ JL @@ret_nil
+ CMP EDX, [EAX].fCount
+ JGE @@ret_nil
+ {$IFDEF TLIST_FAST}
+ CMP [EAX].fUseBlocks, 0
+ JZ @@old
+ CMP [EAX].fNotOptimized, 0
+ JNZ @@slow
+
+ MOV ECX, [EAX].fBlockList
+ JECXZ @@old
+ MOV ECX, [ECX].fItems
+ MOV EAX, EDX
+ SHR EAX, 8
+ MOV ECX, dword ptr [ECX+EAX*8]
+ MOVZX EAX, DL
+ MOV EAX, dword ptr [ECX+EAX*4]
+ RET
+
+@@slow:
+ MOV ECX, [EAX].fBlockList
+ JECXZ @@old
+ PUSH EBX
+ PUSH ESI
+ PUSH EDI
+ PUSH EBP
+ XCHG EBX, EAX // EBX == @Self
+ XOR ECX, ECX // CountBefore := 0;
+ XOR EAX, EAX // i := 0;
+ CMP [EBX].fLastKnownBlockIdx, 0
+ JLE @@1
+ CMP EDX, [EBX].fLastKnownCountBefore
+ JL @@1
+ MOV ECX, [EBX].fLastKnownCountBefore
+ MOV EAX, [EBX].fLastKnownBlockIdx
+@@1:
+ MOV ESI, [EBX].fBlockList
+ MOV ESI, [ESI].fItems
+ MOV EDI, [ESI+EAX*8] // EDI = BlockStart
+ MOV ESI, [ESI+EAX*8+4] // ESI = CountCurrent
+ CMP ECX, EDX
+ JG @@next
+ LEA EBP, [ECX+ESI]
+ CMP EDX, EBP
+ JGE @@next
+ MOV [EBX].fLastKnownBlockIdx, EAX
+ MOV [EBX].fLastKnownCountBefore, ECX
+ SUB EDX, ECX
+ MOV EAX, [EDI+EDX*4]
+ POP EBP
+ POP EDI
+ POP ESI
+ POP EBX
+ RET
+@@next:
+ ADD ECX, ESI
+ INC EAX
+ JMP @@1
+@@old:
+ {$ENDIF}
+ MOV EAX, [EAX].fItems
+ MOV EAX, [EAX+EDX*4]
+ RET
+@@ret_nil:
+ XOR EAX, EAX
+end;
+
+procedure TerminateExecution( var AppletCtl: PControl );
+asm
+ PUSH EBX
+ PUSH ESI
+ MOV BX, $0100
+ XCHG BX, word ptr [AppletRunning]
+ XOR ECX, ECX
+ XCHG ECX, [Applet]
+ JECXZ @@exit
+
+ PUSH EAX
+
+ XCHG EAX, ECX
+ MOV ESI, EAX
+ CALL TObj.RefInc
+
+ TEST BH, BH
+ JNZ @@closed
+
+ MOV EAX, ESI
+ CALL TControl.ProcessMessages
+ PUSH 0
+ PUSH 0
+ PUSH WM_CLOSE
+ PUSH ESI
+ CALL TControl.Perform
+@@closed:
+ POP EAX
+ XOR ECX, ECX
+ MOV dword ptr [EAX], ECX
+ MOV EAX, ESI
+ CALL TObj.RefDec
+ XCHG EAX, ESI
+ CALL TObj.RefDec
+@@exit:
+ POP ESI
+ POP EBX
+end;
+
+procedure Run( var AppletCtl: PControl );
+asm
+ CMP EAX, 0
+ JZ @@exit
+ PUSH EBX
+ XCHG EBX, EAX
+ INC [AppletRunning]
+ MOV EAX, [EBX]
+ MOV [Applet], EAX
+ CALL CallTControlCreateWindow
+ JMP @@2
+@@1:
+ CALL WaitMessage
+ MOV EAX, [EBX]
+ CALL TControl.ProcessMessages
+ {$IFDEF USE_OnIdle}
+ MOV EAX, [EBX]
+ CALL [ProcessIdle]
+ {$ENDIF}
+@@2:
+ MOVZX ECX, [AppletTerminated]
+ JECXZ @@1
+
+ MOV ECX, [EBX]
+ XCHG EAX, EBX
+ POP EBX
+ JECXZ @@exit
+ CALL TerminateExecution
+@@exit:
+end;
+
+function SimpleGetCtlBrushHandle( Sender: PControl ): HBrush;
+asm // //
+ {$IFDEF SMALLEST_CODE}
+ PUSH COLOR_BTNFACE
+ CALL GetSysColorBrush
+ {$ELSE}
+@@1: MOV ECX, [EAX].TControl.fParent
+ JECXZ @@2
+ MOV EDX, [EAX].TControl.fColor
+ CMP EDX, [ECX].TControl.fColor
+ XCHG EAX, ECX
+ JE @@1
+ XCHG EAX, ECX
+@@2: {$IFDEF STORE_fTmpBrushColorRGB}
+ PUSH EBX
+ XCHG EBX, EAX
+ MOV ECX, [EBX].TControl.fTmpBrush
+ JECXZ @@3
+ MOV EAX, [EBX].TControl.fColor
+ CALL Color2RGB
+ CMP EAX, [EBX].TControl.fTmpBrushColorRGB
+ JE @@3
+ XOR EAX, EAX
+ XCHG [EBX].TControl.fTmpBrush, EAX
+ PUSH EAX
+ CALL DeleteObject
+@@3: MOV EAX, [EBX].TControl.fTmpBrush
+ TEST EAX, EAX
+ JNE @@4
+ MOV EAX, [EBX].TControl.fColor
+ CALL Color2RGB
+ MOV [EBX].TControl.fTmpBrushColorRGB, EAX
+ PUSH EAX
+ CALL CreateSolidBrush
+ MOV [EBX].TControl.fTmpBrush, EAX
+@@4: POP EBX
+ {$ELSE}
+ XCHG ECX, EAX
+ MOV EAX, [ECX].TControl.fTmpBrush
+ TEST EAX, EAX
+ JNZ @@ret_EAX
+ PUSH ECX
+ MOV EAX, [ECX].TControl.fColor
+ CALL Color2RGB
+ PUSH EAX
+ CALL CreateSolidBrush
+ POP ECX
+ MOV [ECX].TControl.fTmpBrush, EAX
+@@ret_EAX:
+ {$ENDIF not STORE_fTmpBrushColorRGB}
+ {$ENDIF not SMALLEST_CODE}
+end;
+
+function NormalGetCtlBrushHandle( Sender: PControl ): HBrush;
+asm
+ PUSH ESI
+ PUSH [EAX].TControl.fParent
+ CALL TControl.GetBrush
+ XCHG ESI, EAX // ESI = Sender.Brush
+ POP ECX
+ JECXZ @@retHandle
+ XCHG EAX, ECX
+ CALL TControl.GetBrush
+ MOV [ESI].TGraphicTool.fParentGDITool, EAX
+@@retHandle:
+ XCHG EAX, ESI
+ CALL TGraphicTool.GetHandle
+ POP ESI
+end;
+
+function NewBrush: PGraphicTool;
+asm
+ MOV [Global_GetCtlBrushHandle], offset NormalGetCtlBrushHandle
+ CALL _NewGraphicTool
+ MOV [EAX].TGraphicTool.fNewProc, offset[NewBrush]
+ MOV [EAX].TGraphicTool.fType, gttBrush
+ MOV [EAX].TGraphicTool.fMakeHandleProc, offset[MakeBrushHandle]
+ MOV [EAX].TGraphicTool.fData.Color, clBtnFace
+end;
+
+function NewFont: PGraphicTool;
+const FontDtSz = sizeof( TGDIFont );
+asm
+ MOV EAX, offset[DoApplyFont2Wnd]
+ MOV [ApplyFont2Wnd_Proc], EAX
+ CALL _NewGraphicTool
+ MOV [EAX].TGraphicTool.fNewProc, offset[NewFont]
+ MOV [EAX].TGraphicTool.fType, gttFont
+ MOV [EAX].TGraphicTool.fMakeHandleProc, offset[MakeFontHandle]
+ MOV EDX, [DefFontColor]
+ MOV [EAX].TGraphicTool.fData.Color, EDX
+
+ PUSH EAX
+ LEA EDX, [EAX].TGraphicTool.fData.Font
+ MOV EAX, offset[ DefFont ]
+ XOR ECX, ECX
+ MOV CL, FontDtSz
+ CALL System.Move
+ POP EAX
+end;
+
+function NewPen: PGraphicTool;
+asm
+ CALL _NewGraphicTool
+ MOV [EAX].TGraphicTool.fNewProc, offset[NewPen]
+ MOV [EAX].TGraphicTool.fType, gttPen
+ MOV [EAX].TGraphicTool.fMakeHandleProc, offset[MakePenHandle]
+ MOV [EAX].TGraphicTool.fData.Pen.Mode, pmCopy
+end;
+
+function Color2RGB( Color: TColor ): TColor;
+asm
+ BTR EAX, 31
+ JNC @@exit
+ AND EAX , $7F // <- a Fix Hallif
+ PUSH EAX
+ CALL GetSysColor
+@@exit:
+end;
+
+function Color2RGBQuad( Color: TColor ): TRGBQuad;
+asm
+ CALL Color2RGB
+ // code by bart:
+ xchg ah,al // xxRRGGBB
+ ror eax,16 // BBGGxxRR
+ xchg ah,al // BBGGRRxx
+ shr eax,8 // 00BBGGRR
+end;
+
+function Color2Color16( Color: TColor ): WORD;
+asm
+ MOV EDX, EAX
+ SHR EDX, 19
+ AND EDX, $1F
+ MOV ECX, EAX
+ SHR ECX, 5
+ AND ECX, $7E0;
+ MOV AH, AL
+ AND EAX, $F800
+ OR EAX, EDX
+ OR EAX, ECX
+end;
+
+function TGraphicTool.Assign(Value: PGraphicTool): PGraphicTool;
+const SzfData = sizeof( fData );
+asm // //
+ TEST EDX, EDX
+ JNZ @@1
+ {$IFDEF OLD_REFCOUNT}
+ TEST EAX, EAX
+ JZ @@0
+ CALL TObj.DoDestroy
+ {$ELSE}
+ CALL TObj.RefDec
+ {$ENDIF}
+ XOR EAX, EAX
+@@0: RET
+@@1: PUSH EDI
+ MOV EDI, EDX
+ TEST EAX, EAX
+ JNZ @@2
+ XCHG EAX, EDX
+ CALL dword ptr[EAX].TGraphicTool.fNewProc
+@@2: CMP EAX, EDI
+ JE @@exit
+ PUSH EBX
+ XCHG EBX, EAX
+
+ MOV ECX, [EBX].TGraphicTool.fHandle
+ JECXZ @@3
+ CMP ECX, [EDI].TGraphicTool.fHandle
+ JE @@exit1
+@@3:
+ MOV EAX, EBX
+ CALL TGraphicTool.Changed
+ LEA EDX, [EBX].TGraphicTool.fData
+ LEA EAX, [EDI].TGraphicTool.fData
+ MOV ECX, SzfData
+ CALL System.Move
+ MOV EAX, EBX
+ CALL TGraphicTool.Changed
+
+@@exit1:
+ XCHG EAX, EBX
+ POP EBX
+@@exit: POP EDI
+end;
+
+procedure TGraphicTool.Changed;
+asm
+ XOR ECX, ECX
+ XCHG ECX, [EAX].fHandle
+ JECXZ @@exit
+ PUSH EAX
+ PUSH ECX
+
+ CALL @@CallOnChange
+
+ CALL DeleteObject
+ POP EAX
+@@exit:
+
+@@CallOnChange:
+ MOV ECX, [EAX].fOnGTChange.TMethod.Code
+ JECXZ @@no_onChange
+ PUSH EAX
+ XCHG EDX, EAX
+ MOV EAX, [EDX].fOnGTChange.TMethod.Data
+ CALL ECX
+ POP EAX
+@@no_onChange:
+end;
+
+destructor TGraphicTool.Destroy;
+asm
+ PUSH EAX
+ CMP [EAX].fType, gttFont
+ JE @@0
+ MOV ECX, [EAX].fData.Brush.Bitmap
+ JECXZ @@0
+ PUSH ECX
+ CALL DeleteObject
+ POP EAX
+ PUSH EAX
+@@0:
+ MOV ECX, [EAX].fHandle
+ JECXZ @@1
+ PUSH ECX
+ CALL DeleteObject
+@@1:
+ POP EAX
+ CALL TObj.Destroy
+end;
+
+function TGraphicTool.ReleaseHandle: THANDLE;
+asm // //
+ PUSH EAX
+ CALL Changed
+ POP EDX
+ XOR EAX, EAX
+ XCHG [EDX].fHandle, EAX
+end;
+
+procedure TGraphicTool.SetInt( const Index: Integer; Value: Integer );
+asm
+ LEA EDX, [EDX+EAX].fData
+ CMP [EDX], ECX
+ JE @@exit
+ MOV [EDX], ECX
+ CALL Changed
+@@exit:
+end;
+
+function TGraphicTool.IsFontTrueType: Boolean;
+asm
+ CALL GetHandle
+ TEST EAX, EAX
+ JZ @@exit
+
+ PUSH EBX
+
+ PUSH EAX // fHandle
+
+ PUSH 0
+ CALL GetDC
+
+ PUSH EAX // DC
+ MOV EBX, EAX
+ CALL SelectObject
+ PUSH EAX
+
+ XOR ECX, ECX
+ PUSH ECX
+ PUSH ECX
+ PUSH ECX
+ PUSH ECX
+ PUSH EBX
+ CALL GetFontData
+
+ XCHG EAX, [ESP]
+
+ PUSH EAX
+ PUSH EBX
+ CALL SelectObject
+
+ PUSH EBX
+ PUSH 0
+ CALL ReleaseDC
+
+ POP EAX
+ INC EAX
+ SETNZ AL
+
+ POP EBX
+@@exit:
+end;
+
+procedure TextAreaEx( Sender: PCanvas; var Sz : TSize; var Pt : TPoint );
+asm
+ PUSH EBX
+ PUSH ESI
+ PUSH EDI
+ PUSH EBP
+ MOV EBP, ESP
+ PUSH EDX // [EBP-4] = @Sz
+ PUSH ECX // [EBP-8] = @Pt
+ MOV EBX, EAX
+ CALL TCanvas.GetFont
+ MOV ESI, [EAX].TGraphicTool.fData.Font.Orientation
+ CALL TGraphicTool.IsFontTrueType
+ TEST AL, AL
+ JZ @@exit
+
+ MOV EDI, [EBP-8]
+ XOR EAX, EAX
+ STOSD
+ STOSD
+ TEST ESI, ESI
+ JZ @@exit
+
+ PUSH EAX // Pts[1].x
+ PUSH EAX // Pts[1].y
+
+ PUSH ESI
+ FILD dword ptr [ESP]
+ POP EDX
+
+ FILD word ptr [@@1800]
+ FDIV
+ //FWAIT
+ FLDPI
+ FMUL
+ //FWAIT
+
+ FLD ST(0)
+ FSINCOS
+ FWAIT
+
+ MOV ESI, [EBP-4]
+ LODSD // Sz.cx
+ PUSH EAX
+ FILD dword ptr [ESP]
+ FMUL
+ FISTP dword ptr [ESP] // Pts[2].x
+ FWAIT
+ NEG EAX
+ PUSH EAX
+ FILD dword ptr [ESP]
+ FMUL
+ FISTP dword ptr [ESP] // Pts[2].y
+ FWAIT
+
+ FLDPI
+ FLD1
+ FLD1
+ FADD
+ FDIV
+ FADD
+ FSINCOS
+ FWAIT
+
+ LODSD
+ NEG EAX
+ PUSH EAX
+ FILD dword ptr [ESP]
+ FMUL
+ FISTP dword ptr [ESP] // Pts[4].x
+ FWAIT
+ NEG EAX
+ PUSH EAX
+ FILD dword ptr [ESP]
+ FMUL
+ FISTP dword ptr [ESP] // Pts[4].y
+ FWAIT
+
+ POP ECX
+ POP EDX
+ PUSH EDX
+ PUSH ECX
+ ADD EDX, [ESP+12]
+ ADD ECX, [ESP+8]
+ PUSH EDX
+ PUSH ECX
+
+ MOV ESI, ESP
+ XOR EDX, EDX // MinX
+ XOR EDI, EDI // MinY
+ XOR ECX, ECX
+ MOV CL, 3
+
+@@loo1: LODSD
+ CMP EAX, EDI
+ JGE @@1
+ XCHG EDI, EAX
+@@1: LODSD
+ CMP EAX, EDX
+ JGE @@2
+ XCHG EDX, EAX
+@@2: LOOP @@loo1
+
+ MOV ESI, [EBP-4]
+ MOV [ESI], ECX
+ MOV [ESI+4], ECX
+ MOV CL, 4
+@@loo2:
+ POP EBX
+ SUB EBX, EDI
+ CMP EBX, [ESI+4]
+ JLE @@3
+ MOV [ESI+4], EBX
+@@3:
+ POP EAX
+ SUB EAX, EDX
+ CMP EAX, [ESI]
+ JLE @@4
+ MOV [ESI], EAX
+@@4:
+ LOOP @@loo2
+
+ MOV EDI, [EBP-8]
+ STOSD
+ XCHG EAX, EBX
+ STOSD
+ JMP @@exit
+
+@@1800: DW 1800
+
+@@exit:
+ MOV ESP, EBP
+ POP EBP
+ POP EDI
+ POP ESI
+ POP EBX
+end;
+
+procedure TGraphicTool.SetFontOrientation(Value: Integer);
+asm
+ MOV byte ptr [GlobalGraphics_UseFontOrient], 1
+ MOV [GlobalCanvas_OnTextArea], offset[TextAreaEx]
+
+ PUSH EAX
+ XCHG EAX, EDX
+ MOV ECX, 3600
+ CDQ
+ IDIV ECX // EDX = Value mod 3600
+ POP EAX
+
+ MOV [EAX].fData.Font.Escapement, EDX
+ MOV ECX, EDX
+ XOR EDX, EDX
+ MOV DL, go_FontOrientation
+ CALL SetInt
+end;
+
+function TGraphicTool.GetFontStyle: TFontStyle;
+asm
+ MOV EDX, dword ptr [EAX].fData.Font.Italic
+ AND EDX, $010101
+ MOV EAX, [EAX].fData.Font.Weight
+ CMP EAX, 700
+ SETGE AL //AL:1 = fsBold
+ ADD EDX, EDX
+ OR EAX, EDX //AL:2 = fsItalic
+ SHR EDX, 7
+ OR EAX, EDX //AL:3 = fsUnderline
+ SHR EDX, 7
+ OR EAX, EDX //AL:4 = fsStrikeOut
+end;
+
+procedure TGraphicTool.SetFontStyle(const Value: TFontStyle);
+asm
+ PUSH EDI
+ MOV EDI, EAX
+ PUSH EDX
+ CALL GetFontStyle
+ POP EDX
+ CMP AL, DL
+ JE @@exit
+ PUSH EDI
+
+ LEA EDI, [EDI].fData.Font.Weight
+ MOV ECX, [EDI]
+ SHR EDX, 1
+ JNC @@1
+ CMP ECX, 700
+ JGE @@2
+ MOV ECX, 700
+ JMP @@2
+@@1: CMP ECX, 700
+ JL @@2
+ XOR ECX, ECX
+@@2: XCHG EAX, ECX
+ STOSD // change Weight
+ SHR EDX, 1
+ SETC AL
+ STOSB // change Italic
+ SHR EDX, 1
+ SETC AL
+ STOSB // change Underline
+ SHR EDX, 1
+ SETC AL
+ STOSB // change StrikeOut
+ POP EAX
+ CALL Changed
+@@exit: POP EDI
+end;
+
+function TGraphicTool.GetHandle: THandle;
+const DataSz = sizeof( TGDIToolData );
+asm
+ PUSH EBX
+@@start:
+ XCHG EBX, EAX
+ MOV ECX, [EBX].fHandle
+ JECXZ @@1
+
+ MOV EAX, [EBX].fData.Color
+ CALL Color2RGB
+ CMP EAX, [EBX].fColorRGB
+ JE @@1
+
+ MOV EAX, EBX
+ CALL ReleaseHandle
+ PUSH EAX
+ CALL DeleteObject
+
+@@1: MOV ECX, [EBX].fHandle
+ INC ECX
+ LOOP @@exit
+
+ MOV ECX, [EBX].fParentGDITool
+ JECXZ @@2
+ LEA EDX, [ECX].fData
+ LEA EAX, [EBX].fData
+ MOV ECX, DataSz
+ CALL CompareMem
+ TEST AL, AL
+ MOV EAX, [EBX].fParentGDITool
+ JNZ @@start
+
+@@2: MOV EAX, [EBX].fData.Color
+ CALL Color2RGB
+ MOV [EBX].fColorRGB, EAX
+ XCHG EAX, EBX
+ CALL dword ptr [EAX].fMakeHandleProc
+ XCHG ECX, EAX
+
+@@exit: XCHG EAX, ECX
+ POP EBX
+end;
+
+function MakeBrushHandle( Self_: PGraphicTool ): THandle;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ MOV EAX, [EBX].TGraphicTool.fHandle
+ TEST EAX, EAX
+ JNZ @@exit
+
+ MOV EAX, [EBX].TGraphicTool.fData.Color
+ CALL Color2RGB // EAX = ColorRef
+
+ XOR EDX, EDX
+
+ MOV ECX, [EBX].TGraphicTool.fData.Brush.Bitmap
+ PUSH ECX
+ JECXZ @@1
+
+ MOV DL, BS_PATTERN
+ JMP @@2
+
+@@1:
+ MOV CL, [EBX].TGraphicTool.fData.Brush.Style
+ MOV DL, CL
+ SUB CL, 2
+ JL @@2
+
+ XCHG ECX, [ESP]
+ MOV EAX, [EBX].TGraphicTool.fData.Brush.LineColor
+ CALL Color2RGB
+ XOR EDX, EDX
+ MOV DL, BS_HATCHED
+
+@@2: PUSH EAX
+ PUSH EDX
+
+ PUSH ESP
+ CALL CreateBrushIndirect
+ MOV [EBX].TGraphicTool.fHandle, EAX
+
+ ADD ESP, 12
+
+@@exit:
+ POP EBX
+end;
+
+function MakePenHandle( Self_: PGraphicTool ): THandle;
+asm
+ PUSH EBX
+ MOV EBX, EAX
+
+ MOV EAX, [EBX].TGraphicTool.fHandle
+ TEST EAX, EAX
+ JNZ @@exit
+
+ MOV EAX, [EBX].TGraphicTool.fData.Color
+ CALL Color2RGB
+ PUSH EAX
+ PUSH EAX
+ PUSH [EBX].TGraphicTool.fData.Pen.Width
+ MOVZX EAX, [EBX].TGraphicTool.fData.Pen.Style
+ PUSH EAX
+ PUSH ESP
+ CALL CreatePenIndirect
+ MOV [EBX].TGraphicTool.fHandle, EAX
+ ADD ESP, 16
+@@exit:
+ POP EBX
+end;
+
+function MakeGeometricPenHandle( Self_: PGraphicTool ): THandle;
+asm
+ MOV ECX, [EAX].TGraphicTool.fHandle
+ INC ECX
+ LOOP @@exit
+
+ PUSH EBX
+ XCHG EBX, EAX
+ MOV EAX, [EBX].TGraphicTool.fData.Color
+ CALL Color2RGB // EAX = Color2RGB( fColor )
+ CDQ // EDX = lbHatch (0)
+ MOV ECX, [EBX].TGraphicTool.fData.Pen.BrushBitmap
+ JECXZ @@no_brush_bitmap
+
+ XCHG EDX, ECX // lbHatch = fPenBrushBitmap
+ MOV CL, BS_PATTERN // = 3
+ JMP @@create_pen
+
+@@no_brush_bitmap:
+ MOVZX ECX, [EBX].TGraphicTool.fData.Pen.BrushStyle
+ CMP CL, 1
+ JLE @@create_pen
+ MOV EDX, ECX
+ MOV CL, 2
+ SUB EDX, ECX
+
+@@create_pen:
+ PUSH EDX
+ PUSH EAX
+ PUSH ECX
+ MOV ECX, ESP
+
+ CDQ
+ PUSH EDX
+ PUSH EDX
+ PUSH ECX
+ PUSH [EBX].TGraphicTool.fData.Pen.Width
+ MOVZX ECX, [EBX].TGraphicTool.fData.Pen.Join
+ SHL ECX, 12
+ MOVZX EDX, [EBX].TGraphicTool.fData.Pen.EndCap
+ SHL EDX, 8
+ OR EDX, ECX
+ OR DL, byte ptr [EBX].TGraphicTool.fData.Pen.Style
+ OR EDX, PS_GEOMETRIC
+ PUSH EDX
+ CALL ExtCreatePen
+
+ POP ECX
+ POP ECX
+ POP ECX
+
+ MOV [EBX].TGraphicTool.fHandle, EAX
+ POP EBX
+ RET
+@@exit:
+ XCHG EAX, ECX
+end;
+
+function TCanvas.Assign(SrcCanvas: PCanvas): Boolean;
+asm
+ PUSH EBX
+ PUSH ESI
+ XCHG EBX, EAX
+ MOV ESI, EDX
+
+ MOV EAX, [EBX].fFont
+ MOV EDX, [ESI].fFont
+ CALL TGraphicTool.Assign
+ MOV [EBX].fFont, EAX
+
+ MOV EAX, [EBX].fBrush
+ MOV EDX, [ESI].fBrush
+ CALL TGraphicTool.Assign
+ MOV [EBX].fBrush, EAX
+
+ MOV EAX, [EBX].fPen
+ MOV EDX, [ESI].fPen
+ CALL TGraphicTool.Assign
+ MOV [EBX].fPen, EAX
+
+ CALL AssignChangeEvents
+
+ MOV ECX, [EBX].fFont
+ OR ECX, [EBX].fBrush
+ OR ECX, [EBX].fPen
+ SETNZ AL
+
+ MOV EDX, [ESI].fPenPos.x
+ MOV ECX, [ESI].fPenPos.y
+ CMP EDX, [EBX].fPenPos.x
+ JNE @@chg_penpos
+ CMP ECX, [EBX].fPenPos.y
+ JE @@1
+@@chg_penpos:
+ MOV AL, 1
+ MOV [EBX].fPenPos.x, EDX
+ MOV [EBX].fPenPos.y, ECX
+@@1:
+ MOV EDX, [ESI].fCopyMode
+ CMP EDX, [EBX].fCopyMode
+ JE @@2
+ MOV [EBX].fCopyMode, EDX
+ MOV AL, 1
+@@2:
+ POP ESI
+ POP EBX
+end;
+
+procedure TCanvas.CreateBrush;
+asm
+ PUSH EBX
+ MOV EBX, EAX
+
+ MOV ECX, [EAX].fBrush
+ JECXZ @@chk_owner
+
+ MOV EAX, ECX
+ CALL TGraphicTool.GetHandle
+ PUSH EAX
+
+ MOV EAX, EBX
+ CALL AssignChangeEvents
+
+ MOV EAX, EBX
+ CALL TCanvas.GetHandle
+ PUSH EAX
+
+ CALL SelectObject
+
+ MOV EDX, [EBX].TCanvas.fBrush
+ CMP [EDX].TGraphicTool.fData.Brush.Style, bsSolid
+
+ MOV EAX, [EDX].TGraphicTool.fData.Color
+@@0:
+ MOV EBX, [EBX].TCanvas.fHandle
+ MOV ECX, offset[Color2RGB]
+ JNZ @@1
+
+ PUSH OPAQUE
+ PUSH EBX
+
+ CALL ECX //Color2RGB
+ PUSH EAX
+ PUSH EBX
+ JMP @@2
+@@1:
+ PUSH TRANSPARENT
+ PUSH EBX
+
+ CALL ECX //Color2RGB
+ NOT EAX
+ PUSH EAX
+ PUSH EBX
+@@2:
+ CALL SetBkColor
+ CALL SetBkMode
+@@exit:
+ POP EBX
+ RET
+
+@@chk_owner:
+ MOV ECX, [EBX].fOwnerControl
+ JECXZ @@exit
+
+ MOV EAX, [ECX].TControl.fColor
+ XOR ECX, ECX
+ JMP @@0
+end;
+
+procedure TCanvas.CreateFont;
+asm
+ PUSH EBX
+ MOV EBX, EAX
+
+ MOV ECX, [EAX].TCanvas.fFont
+ JECXZ @@chk_owner
+
+ MOV EAX, [ECX].TGraphicTool.fData.Color
+ PUSH ECX
+ CALL Color2RGB
+ XCHG EAX, [ESP]
+
+ CALL TGraphicTool.GetHandle
+ PUSH EAX
+
+ MOV EAX, EBX
+ CALL AssignChangeEvents;
+
+ MOV EAX, EBX
+ CALL TCanvas.GetHandle
+ PUSH EAX
+ MOV EBX, EAX
+
+ CALL SelectObject
+
+@@set_txcolor:
+ PUSH EBX
+ CALL SetTextColor
+
+@@exit:
+ POP EBX
+ RET
+
+@@chk_owner:
+ MOV ECX, [EBX].fOwnerControl
+ JECXZ @@exit
+
+ MOV EBX, [EBX].fHandle
+ MOV EAX, [ECX].TControl.fTextColor
+ CALL Color2RGB
+ PUSH EAX
+ JMP @@set_txcolor
+end;
+
+procedure TCanvas.CreatePen;
+asm
+ MOV ECX, [EAX].TCanvas.fPen
+ JECXZ @@exit
+
+ PUSH EBX
+ MOV EBX, EAX
+
+ MOV DL, [ECX].TGraphicTool.fData.Pen.Mode
+ MOVZX EDX, DL
+ INC EDX
+ PUSH EDX
+
+ MOV EAX, ECX
+ CALL TGraphicTool.GetHandle
+ PUSH EAX
+
+ MOV EAX, EBX
+ CALL AssignChangeEvents
+
+ MOV EAX, EBX
+ CALL TCanvas.GetHandle
+ PUSH EAX
+ MOV EBX, EAX
+
+ CALL SelectObject
+ PUSH EBX
+ CALL SetROP2
+
+ POP EBX
+@@exit:
+end;
+
+procedure TCanvas.DeselectHandles;
+asm
+ PUSH EBX
+ PUSH ESI
+ PUSH EDI
+ LEA EBX, [EAX].TCanvas.fState
+ //CALL TCanvas.GetHandle
+ MOV EAX, [EAX].TCanvas.fHandle
+ TEST EAX, EAX
+ JZ @@exit
+
+ MOVZX EDX, byte ptr[EBX]
+ AND DL, PenValid or BrushValid or FontValid
+ JZ @@exit
+
+ PUSH EAX
+ LEA EDI, [Stock]
+
+ MOV ECX, [EDI]
+ INC ECX
+ LOOP @@1
+
+ MOV ESI, offset[ GetStockObject ]
+
+ PUSH BLACK_PEN
+ CALL ESI
+ STOSD
+
+ PUSH HOLLOW_BRUSH
+ CALL ESI
+ STOSD
+
+ PUSH SYSTEM_FONT
+ CALL ESI
+ STOSD
+
+@@1:
+ LEA ESI, [Stock]
+ POP EDX
+
+ LODSD
+ PUSH EAX
+ PUSH EDX
+
+ LODSD
+ PUSH EAX
+ PUSH EDX
+
+ LODSD
+ PUSH EAX
+ PUSH EDX
+
+ MOV ESI, offset[ SelectObject ]
+ CALL ESI
+ CALL ESI
+ CALL ESI
+
+ AND byte ptr [EBX], not( PenValid or BrushValid or FontValid )
+@@exit:
+ POP EDI
+ POP ESI
+ POP EBX
+end;
+
+function TCanvas.RequiredState(ReqState: DWORD): HDC; stdcall;
+asm
+ PUSH EBX
+ PUSH ESI
+ MOV EBX, ReqState
+ MOV ESI, [EBP+8] //Self
+ MOV EAX, ESI
+ TEST BL, ChangingCanvas
+ JZ @@1
+ CALL Changing
+@@1: AND BL, 0Fh
+
+ TEST BL, HandleValid
+ JZ @@2
+ CALL TCanvas.GetHandle
+ TEST EAX, EAX
+ JZ @@ret_0
+@@2:
+ MOV AL, [ESI].TCanvas.fState
+ NOT EAX
+ AND BL, AL
+ JZ @@ret_handle
+
+ TEST BL, FontValid
+ JZ @@3
+ MOV EAX, ESI
+ CALL CreateFont
+@@3: TEST BL, PenValid
+ JZ @@5
+ MOV EAX, ESI
+ CALL CreatePen
+ MOV ECX, [ESI].TCanvas.fPen
+ JCXZ @@5
+ MOV AL, [ECX].TGraphicTool.fData.Pen.Style
+ DEC AL
+ {$IFDEF PARANOIA} DB $2C, 3 {$ELSE} SUB AL, 3 {$ENDIF}
+ JB @@6
+@@5: TEST BL, BrushValid
+ JZ @@7
+@@6: MOV EAX, ESI
+ CALL CreateBrush
+@@7: OR [ESI].TCanvas.fState, BL
+@@ret_handle:
+ MOV EAX, [ESI].TCanvas.fHandle
+@@ret_0:
+ POP ESI
+ POP EBX
+end;
+
+procedure TCanvas.SetHandle(Value: HDC);
+asm
+ PUSH EBX
+ PUSH ESI
+ MOV ESI, EDX // ESI = Value
+ MOV EBX, EAX // EAX = @ Self
+ MOV ECX, [EBX].fHandle // ECX = fHandle (before)
+ CMP ECX, ESI // compare with new Value in EDX
+ JZ @@exit // equal? -> nothing to do
+ JECXZ @@chk_val // fHandle = 0? -> check new value in EDX
+
+ PUSH ECX // fHandle
+ CALL DeselectHandles
+ POP EDX // fHandle
+
+ MOV ECX, [EBX].fOwnerControl
+ JECXZ @@chk_Release
+ CMP [ECX].TControl.fPaintDC, EDX
+ JE @@clr_Handle
+
+@@chk_Release:
+ CMP [EBX].fOnGetHandle.TMethod.Code, offset[TControl.DC2Canvas]
+ JNE @@deldc
+ PUSH EDX // fHandle
+ PUSH [ECX].TControl.fHandle
+ CALL ReleaseDC
+ JMP @@clr_Handle
+@@deldc:
+ CMP WORD PTR [EBX].fIsPaintDC, 0
+ JNZ @@clr_Handle
+ PUSH EDX // fHandle
+ CALL DeleteDC
+
+@@clr_Handle:
+ XOR ECX, ECX
+ MOV [EBX].TCanvas.fHandle, ECX
+ MOV [EBX].TCanvas.fIsPaintDC, CL
+ AND [EBX].TCanvas.fState, not HandleValid
+
+@@chk_val:
+ TEST ESI, ESI
+ JZ @@exit
+
+ OR [EBX].TCanvas.fState, HandleValid
+ MOV [EBX].TCanvas.fHandle, ESI
+ LEA EDX, [EBX].TCanvas.fPenPos
+ MOV EAX, EBX
+ CALL SetPenPos
+
+@@exit: POP ESI
+ POP EBX
+end;
+
+procedure TCanvas.SetPenPos(const Value: TPoint);
+asm
+ MOV ECX, [EDX].TPoint.y
+ MOV EDX, [EDX].TPoint.x
+ MOV [EAX].fPenPos.x, EDX
+ MOV [EAX].fPenPos.y, ECX
+ CALL MoveTo
+end;
+
+procedure TCanvas.Changing;
+asm
+ PUSHAD
+ MOV ECX, [EAX].fOnChangeCanvas.TMethod.Code
+ JECXZ @@exit
+ XCHG EDX, EAX
+ MOV EAX, [EDX].fOnChangeCanvas.TMethod.Data
+ CALL ECX
+@@exit:
+ POPAD
+end;
+
+procedure TCanvas.Arc(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer); stdcall;
+asm
+ PUSH ESI
+
+ PUSH HandleValid or PenValid or ChangingCanvas
+ PUSH dword ptr [EBP+8]
+ CALL RequiredState
+
+ MOV EDX, EAX
+
+ LEA ESI, [Y4]
+ STD
+
+ XOR ECX, ECX
+ MOV CL, 8
+@@1:
+ LODSD
+ PUSH EAX
+
+ LOOP @@1
+
+ CLD
+ PUSH EDX //Canvas.fHandle
+ CALL Windows.Arc
+ POP ESI
+end;
+
+procedure TCanvas.Chord(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer); stdcall;
+asm
+ PUSH HandleValid or PenValid or BrushValid or ChangingCanvas
+ PUSH dword ptr [EBP + 8]
+ CALL RequiredState
+
+ MOV EDX, EAX
+
+ PUSH ESI
+ LEA ESI, [Y4]
+ STD
+
+ XOR ECX, ECX
+ MOV CL, 8
+@@1:
+ LODSD
+ PUSH EAX
+
+ LOOP @@1
+
+ CLD
+ PUSH EDX //Canvas.fHandle
+ CALL Chord
+ POP ESI
+end;
+
+procedure TCanvas.CopyRect(const DstRect: TRect; SrcCanvas: PCanvas;
+ const SrcRect: TRect);
+asm
+ PUSH ESI
+ PUSH EDI
+
+ PUSH [EAX].fCopyMode
+
+ PUSH EDX
+
+ PUSH HandleValid or BrushValid
+ PUSH ECX
+
+ PUSH HandleValid or FontValid or BrushValid or ChangingCanvas
+ PUSH EAX
+ MOV ESI, offset[ RequiredState ]
+ CALL ESI
+ MOV EDI, EAX // EDI = @Self.fHandle
+
+ CALL ESI
+ MOV EDX, EAX // EDX = SrcCanvas.fHandle
+
+ POP ECX // ECX = @DstRect
+
+ MOV ESI, [SrcRect]
+
+ MOV EAX, [ESI].TRect.Bottom
+ SUB EAX, [ESI].TRect.Top
+ PUSH EAX
+
+ MOV EAX, [ESI].TRect.Right
+ SUB EAX, [ESI].TRect.Left
+ PUSH EAX
+
+ PUSH [ESI].TRect.Top
+
+ LODSD
+ PUSH EAX
+
+ PUSH EDX
+
+ MOV EAX, [ECX].TRect.Bottom
+ MOV EDX, [ECX].TRect.Top
+ SUB EAX, EDX
+ PUSH EAX
+
+ MOV EAX, [ECX].TRect.Right
+ MOV ESI, [ECX].TRect.Left
+ SUB EAX, ESI
+ PUSH EAX
+
+ PUSH EDX
+
+ PUSH ESI
+
+ PUSH EDI
+
+ CALL StretchBlt
+
+ POP EDI
+ POP ESI
+end;
+
+procedure TCanvas.DrawFocusRect({$IFNDEF FPC}const{$ENDIF} Rect: TRect);
+asm
+ PUSH EDX
+
+ PUSH HandleValid or BrushValid or FontValid or ChangingCanvas
+ PUSH EAX
+ CALL RequiredState
+
+ PUSH EAX
+ CALL Windows.DrawFocusRect
+end;
+
+procedure TCanvas.Ellipse(X1, Y1, X2, Y2: Integer);
+asm
+ PUSH [Y2]
+ PUSH [X2]
+ PUSH ECX
+ PUSH EDX
+
+ PUSH HandleValid or PenValid or BrushValid or ChangingCanvas
+ PUSH EAX
+ CALL RequiredState
+
+ PUSH EAX
+ CALL Windows.Ellipse
+end;
+
+procedure TCanvas.FillRect({$IFNDEF FPC}const{$ENDIF} Rect: TRect);
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ PUSH EDX
+ PUSH HandleValid or BrushValid or ChangingCanvas
+ PUSH EBX
+ CALL RequiredState
+ MOV ECX, [EBX].fBrush
+ JECXZ @@chk_ctl
+
+@@fill_with_Brush:
+ XCHG EAX, ECX
+ CALL TGraphicTool.GetHandle
+ POP EDX
+ PUSH EAX
+ JMP @@fin
+@@chk_ctl:
+ MOV ECX, [EBX].fOwnerControl
+ JECXZ @@dflt_fill
+ XCHG EAX, ECX
+ MOV ECX, [EAX].TControl.fBrush
+ INC ECX
+ LOOP @@fill_with_Brush
+ MOV EAX, [EAX].TControl.fColor
+ CALL Color2RGB
+ PUSH EAX
+ CALL CreateSolidBrush
+ POP EDX
+ PUSH EAX
+ PUSH EAX
+ PUSH EDX
+ PUSH [EBX].fHandle
+ CALL Windows.FillRect
+ CALL DeleteObject
+ POP EBX
+ RET
+@@dflt_fill:
+ POP EDX
+ PUSH COLOR_WINDOW + 1
+@@fin:
+ PUSH EDX
+ PUSH [EBX].fHandle
+ CALL Windows.FillRect
+ POP EBX
+end;
+
+procedure TCanvas.FillRgn(const Rgn: HRgn);
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ PUSH EDX
+
+ PUSH HandleValid or BrushValid or ChangingCanvas
+ PUSH EBX
+ CALL RequiredState
+
+ MOV ECX, [EBX].TCanvas.fBrush
+ JECXZ @@1
+
+@@fill_rgn_using_Brush:
+ XCHG EAX, ECX
+ CALL TGraphicTool.GetHandle
+ POP EDX
+ PUSH EAX
+ PUSH EDX
+ PUSH [EBX].fHandle
+ CALL Windows.FillRgn
+ JMP @@fin
+
+@@1: MOV ECX, [EBX].TCanvas.fOwnerControl
+ MOV EAX, -1 // clWhite
+ JECXZ @@2
+
+ XCHG EAX, ECX
+ MOV ECX, [EAX].TControl.fBrush
+ INC ECX
+ LOOP @@fill_rgn_using_Brush
+
+ MOV EAX, [EAX].TControl.fColor
+@@2:
+ CALL Color2RGB
+ PUSH EAX
+ CALL CreateSolidBrush // EAX = Br
+
+ POP EDX // Rgn
+
+ PUSH EAX //-------------------//
+ PUSH EAX // Br
+ PUSH EDX // Rgn
+ PUSH [EBX].FHandle // fHandle
+ CALL Windows.FillRgn
+
+ CALL DeleteObject
+
+@@fin:
+ POP EBX
+end;
+
+procedure TCanvas.FloodFill(X, Y: Integer; Color: TColor;
+ FillStyle: TFillStyle);
+asm
+ PUSH EBX
+ MOV EBX, EAX
+
+ MOVZX EAX, [FillStyle]
+ TEST EAX, EAX
+ MOV EAX, FLOODFILLSURFACE // = 1
+ JZ @@1
+ //MOV EAX, FLOODFILLBORDER // = 0
+ DEC EAX
+@@1:
+ PUSH EAX
+ PUSH [Color]
+ PUSH ECX
+ PUSH EDX
+
+ PUSH HandleValid or BrushValid or ChangingCanvas
+ PUSH EBX
+ CALL RequiredState
+ PUSH EAX
+ CALL Windows.ExtFloodFill
+
+ POP EBX
+end;
+
+procedure TCanvas.FrameRect({$IFNDEF FPC}const{$ENDIF} Rect: TRect);
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ PUSH EDX
+
+ MOV ECX, [EBX].TCanvas.fBrush
+ JECXZ @@1
+
+ PUSH [ECX].TGraphicTool.fData.Color
+ JMP @@cr_br
+
+@@1: MOV ECX, [EBX].TCanvas.fOwnerControl
+ JECXZ @@2
+
+ PUSH [ECX].TControl.fColor
+ JMP @@cr_br
+
+@@2: PUSH clWhite
+@@cr_br:POP EAX // @Rect
+ CALL Color2RGB
+ PUSH EAX
+ CALL CreateSolidBrush
+ POP EDX
+ PUSH EAX
+ PUSH EAX
+ PUSH EDX
+
+ PUSH HandleValid or ChangingCanvas
+ PUSH EBX
+ CALL RequiredState
+
+ PUSH EAX
+ CALL Windows.FrameRect
+
+ CALL DeleteObject
+
+ POP EBX
+end;
+
+procedure TCanvas.LineTo(X, Y: Integer);
+asm
+ PUSH ECX
+ PUSH EDX
+ PUSH HandleValid or PenValid or BrushValid or ChangingCanvas
+ PUSH EAX
+ CALL RequiredState
+ PUSH EAX //Canvas.fHandle
+ CALL Windows.LineTo
+end;
+
+procedure TCanvas.MoveTo(X, Y: Integer);
+asm
+ PUSH 0
+ PUSH ECX
+ PUSH EDX
+ PUSH HandleValid
+ PUSH EAX
+ CALL RequiredState
+ PUSH EAX //Canvas.fHandle
+ CALL Windows.MoveToEx
+end;
+
+procedure TCanvas.Pie(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer); stdcall;
+asm
+ PUSH HandleValid or PenValid or BrushValid or ChangingCanvas
+ PUSH dword ptr [EBP + 8]
+ CALL RequiredState
+
+ MOV EDX, EAX
+
+ PUSH ESI
+ LEA ESI, [Y4]
+ STD
+
+ XOR ECX, ECX
+ MOV CL, 8
+@@1:
+ LODSD
+ PUSH EAX
+
+ LOOP @@1
+
+ CLD
+ PUSH EDX //Canvas.fHandle
+ CALL Windows.Pie
+ POP ESI
+end;
+
+procedure TCanvas.Polygon(const Points: array of TPoint);
+asm
+ INC ECX
+ PUSH ECX
+ PUSH EDX
+
+ PUSH HandleValid or PenValid or BrushValid or ChangingCanvas
+ PUSH EAX
+ CALL RequiredState
+
+ PUSH EAX
+ CALL Windows.Polygon
+end;
+
+procedure TCanvas.Polyline(const Points: array of TPoint);
+asm
+ INC ECX
+ PUSH ECX
+ PUSH EDX
+
+ PUSH HandleValid or PenValid or BrushValid or ChangingCanvas
+ PUSH EAX
+ CALL RequiredState
+
+ PUSH EAX
+ CALL Windows.Polyline
+end;
+
+procedure TCanvas.Rectangle(X1, Y1, X2, Y2: Integer);
+asm
+ PUSH [Y2]
+ PUSH [X2]
+ PUSH ECX
+ PUSH EDX
+
+ PUSH HandleValid or BrushValid or PenValid or ChangingCanvas
+ PUSH EAX
+ CALL RequiredState
+
+ PUSH EAX
+ CALL Windows.Rectangle
+end;
+
+procedure TCanvas.RoundRect(X1, Y1, X2, Y2, X3, Y3: Integer);
+asm
+ PUSH [Y3]
+ PUSH [X3]
+ PUSH [Y2]
+ PUSH [X2]
+ PUSH ECX
+ PUSH EDX
+
+ PUSH HandleValid or BrushValid or PenValid or ChangingCanvas
+ PUSH EAX
+ CALL RequiredState
+
+ PUSH EAX
+ CALL Windows.RoundRect
+end;
+
+procedure TCanvas.TextArea(const Text: KOLString; var Sz: TSize;
+ var P0: TPoint);
+asm
+ PUSH EBX
+ MOV EBX, EAX
+
+ PUSH ECX
+ CALL TextExtent
+ POP EDX
+
+ MOV ECX, [P0]
+ XOR EAX, EAX
+ MOV [ECX].TPoint.x, EAX
+ MOV [ECX].TPoint.y, EAX
+
+ CMP [GlobalCanvas_OnTextArea], EAX
+ JZ @@exit
+ MOV EAX, EBX
+ CALL [GlobalCanvas_OnTextArea]
+
+@@exit:
+ POP EBX
+end;
+
+procedure TCanvas.TextRect(const Rect: TRect; X, Y: Integer; const Text: Ansistring);
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+
+ PUSH 0 // prepare 0
+
+ PUSH EDX
+ PUSH ECX
+
+ MOV EAX, [Text]
+ PUSH EAX
+ CALL System.@LStrLen
+
+ POP ECX // ECX = @Text[1]
+ POP EDX // EDX = X
+ XCHG EAX, [ESP] // prepare Length(Text), EAX = @Rect
+ PUSH ECX // prepare PChar(Text)
+ PUSH EAX // prepare @Rect
+
+ XOR EAX, EAX
+ MOV AL, ETO_CLIPPED // = 4
+ MOV ECX, [EBX].fBrush
+ JECXZ @@opaque
+
+ CMP [ECX].TGraphicTool.fData.Brush.Style, bsClear
+ JZ @@txtout
+
+@@opaque:
+ DB $0C, ETO_OPAQUE //OR AL, ETO_OPAQUE
+@@txtout:
+ PUSH EAX // prepare Options
+ PUSH [Y] // prepare Y
+ PUSH EDX // prepare X
+
+ PUSH HandleValid or FontValid or BrushValid or ChangingCanvas
+ PUSH EBX
+ CALL RequiredState // EAX = fHandle
+ PUSH EAX // prepare fHandle
+
+ CALL Windows.ExtTextOutA // KOL_ANSI
+
+ POP EBX
+end;
+
+procedure TCanvas.DrawText(Text: AnsiString; var Rect:TRect; Flags:DWord);
+asm
+ PUSH [Flags]
+ PUSH ECX
+ PUSH -1
+ CALL EDX2PChar
+ PUSH EDX
+
+ PUSH HandleValid or FontValid or BrushValid or ChangingCanvas
+ PUSH EAX
+ CALL RequiredState
+ PUSH EAX
+ CALL Windows.DrawTextA
+end;
+
+function TCanvas.GetBrush: PGraphicTool;
+asm
+ MOV ECX, [EAX].fBrush
+ INC ECX
+ LOOP @@exit
+
+ PUSH EAX
+ CALL NewBrush
+ POP EDX
+ PUSH EAX
+
+ MOV [EDX].fBrush, EAX
+
+ MOV [EAX].TGraphicTool.fOnGTChange.TMethod.Code, Offset[TCanvas.ObjectChanged]
+ MOV [EAX].TGraphicTool.fOnGTChange.TMethod.Data, EDX
+ MOV ECX, [EDX].fOwnerControl
+ JECXZ @@1
+
+ PUSH [ECX].TControl.fBrush
+ MOV ECX, [ECX].TControl.fColor
+ MOV [EAX].TGraphicTool.fData.Color, ECX
+ POP EDX
+ TEST EDX, EDX
+ JZ @@1
+
+ CALL TGraphicTool.Assign
+
+@@1: POP ECX
+
+@@exit: XCHG EAX, ECX
+end;
+
+function TCanvas.GetFont: PGraphicTool;
+asm
+ MOV ECX, [EAX].TCanvas.fFont
+ INC ECX
+ LOOP @@exit
+
+ PUSH EAX
+ CALL NewFont
+ POP EDX
+ PUSH EAX
+
+ MOV [EDX].TCanvas.fFont, EAX
+ MOV [EAX].TGraphicTool.fOnGTChange.TMethod.Code, Offset[TCanvas.ObjectChanged]
+ MOV [EAX].TGraphicTool.fOnGTChange.TMethod.Data, EDX
+
+ MOV ECX, [EDX].fOwnerControl
+ JECXZ @@1
+
+ PUSH [ECX].TControl.fFont
+ MOV ECX, [ECX].TControl.fTextColor
+ MOV [EAX].TGraphicTool.fData.Color, ECX
+ POP EDX
+ TEST EDX, EDX
+ JZ @@1
+
+ CALL TGraphicTool.Assign
+
+@@1: POP ECX
+
+@@exit: MOV EAX, ECX
+end;
+
+function TCanvas.GetPen: PGraphicTool;
+asm
+ MOV ECX, [EAX].TCanvas.fPen
+ INC ECX
+ LOOP @@exit
+
+ PUSH EAX
+ CALL NewPen
+ POP EDX
+ MOV [EDX].fPen, EAX
+ PUSH EAX
+ MOV EAX, EDX
+ CALL AssignChangeEvents
+ POP ECX
+
+@@exit: MOV EAX, ECX
+end;
+
+function TCanvas.GetHandle: HDC;
+asm
+ CMP word ptr[EAX].fOnGetHandle.TMethod.Code+2, 0
+ MOV EDX, EAX
+ MOV EAX, [EDX].fHandle
+ JZ @@exit
+ MOV EAX, [EDX].fOnGetHandle.TMethod.Data
+ PUSH EDX
+ CALL [EDX].fOnGetHandle.TMethod.Code
+ XCHG EAX, [ESP]
+ POP EDX
+ PUSH EDX
+ CALL SetHandle
+ POP EAX
+@@exit:
+end;
+
+procedure TCanvas.AssignChangeEvents;
+asm
+ PUSH ESI
+ LEA ESI, [EAX].fBrush
+ MOV CL, 3
+ MOV EDX, EAX
+@@1: LODSD
+ TEST EAX, EAX
+ JZ @@nxt
+ MOV [EAX].TGraphicTool.fOnGTChange.TMethod.Data, EDX
+ MOV [EAX].TGraphicTool.fOnGTChange.TMethod.Code, offset[ ObjectChanged ]
+@@nxt: DEC CL
+ JNZ @@1
+ POP ESI
+end;
+
+function Mul64i( const X: I64; Mul: Integer ): I64;
+asm //cmd //opd
+ TEST EDX, EDX
+ PUSHFD
+ JGE @@1
+ NEG EDX
+@@1: PUSH ECX
+ CALL Mul64EDX
+ POP EAX
+ POPFD
+ JGE @@2
+ MOV EDX, EAX
+ CALL Neg64
+@@2:
+end;
+
+function Div64i( const X: I64; D: Integer ): I64;
+asm //cmd //opd
+ PUSH EBX
+ XOR EBX, EBX
+ PUSH ESI
+ XCHG ESI, EAX
+ LODSD
+ MOV [ECX], EAX
+ LODSD
+ MOV [ECX+4], EAX
+ MOV ESI, ECX
+ PUSH EDX
+ XCHG EAX, ECX
+ CALL Sgn64
+ TEST EAX, EAX
+ JGE @@1
+ INC EBX
+ MOV EAX, ESI
+ MOV EDX, ESI
+ CALL Neg64
+@@1: POP EDX
+ TEST EDX, EDX
+ JGE @@2
+ XOR EBX, 1
+ NEG EDX
+@@2: MOV EAX, ESI
+ MOV ECX, ESI
+ CALL Div64EDX
+ DEC EBX
+ JNZ @@3
+ MOV EDX, ESI
+ XCHG EAX, ESI
+ CALL Neg64
+@@3: POP ESI
+ POP EBX
+end;
+
+function cHex2Int( const Value : KOLString) : Integer;
+asm
+ TEST EAX, EAX
+ JZ @@exit
+ CMP word ptr [EAX], '0x'
+ JZ @@skip_2_chars
+ CMP word ptr [EAX], '0X'
+ JNZ @@2Hex2Int
+@@skip_2_chars:
+ INC EAX
+ INC EAX
+@@2Hex2Int:
+ JMP Hex2Int
+@@exit:
+end;
+
+function Trim( const S : KOLString): KOLString;
+asm
+ PUSH EDX
+ CALL TrimRight
+ POP EDX
+ MOV EAX, [EDX]
+ CALL TrimLeft
+end;
+
+function LowerCase(const S: Ansistring): Ansistring;
+asm
+ PUSH ESI
+ XCHG EAX, EDX
+ PUSH EAX
+ CALL System.@LStrAsg
+ POP EAX
+
+ CALL UniqueString
+
+ PUSH EAX
+ CALL System.@LStrLen
+ POP ESI
+
+ XCHG ECX, EAX
+
+ JECXZ @@exit
+
+@@go:
+ LODSB
+ {$IFDEF PARANOIA} DB $2C, 'A' {$ELSE} SUB AL, 'A' {$ENDIF}
+ {$IFDEF PARANOIA} DB $3C, 26 {$ELSE} CMP AL, 'Z'-'A'+1 {$ENDIF}
+ JNB @@1
+
+ ADD byte ptr [ESI - 1], 20h
+@@1:
+ LOOP @@go
+@@exit:
+ POP ESI
+end;
+
+function UpperCase(const S: Ansistring): Ansistring;
+asm
+ PUSH ESI
+ XCHG EAX, EDX
+ PUSH EAX
+ CALL System.@LStrAsg
+ POP EAX
+
+ CALL UniqueString
+
+ PUSH EAX
+ CALL System.@LStrLen
+ POP ESI
+
+ XCHG ECX, EAX
+
+ JECXZ @@exit
+
+@@go:
+ LODSB
+ {$IFDEF PARANOIA} DB $2C, 'a' {$ELSE} SUB AL, 'a' {$ENDIF}
+ {$IFDEF PARANOIA} DB $3C, $1A {$ELSE} CMP AL, 'z'-'a'+1 {$ENDIF}
+ JNB @@1
+
+ SUB byte ptr [ESI - 1], 20h
+@@1:
+ LOOP @@go
+@@exit:
+ POP ESI
+end;
+
+function AllocMem( Size : Integer ) : Pointer;
+asm //cmd //opd
+ TEST EAX, EAX
+ JZ @@exit
+ PUSH EAX
+ CALL System.@GetMem
+ POP EDX
+ PUSH EAX
+ //MOV CL, 0
+ CALL ZeroMemory
+ POP EAX
+@@exit:
+end;
+
+function _WStrComp(S1, S2: PWideChar): Integer;
+asm
+ PUSH ESI
+ XCHG ESI, EAX
+ XOR EAX, EAX
+@@1:
+ LODSW
+ MOV ECX, EAX
+ SUB AX, word ptr [EDX]
+ JNZ @@exit
+ JECXZ @@exit
+ INC EDX
+ INC EDX
+ JMP @@1
+@@exit:
+ MOVSX EAX, AX
+ POP ESI
+end;
+
+function _AnsiCompareStrA_Fast2(S1, S2: PAnsiChar): Integer;
+asm
+ CALL EAX2PChar
+ CALL EDX2PChar
+ PUSH ESI
+ XCHG ESI, EAX
+ XOR EAX, EAX
+@@1:
+ LODSB
+ MOV CX, word ptr [EAX*2 + SortAnsiOrder]
+ MOV AL, [EDX]
+ SUB CX, word ptr [EAX*2 + SortAnsiOrder]
+ JNZ @@retCL
+ INC EDX
+ TEST AL, AL
+ JNZ @@1
+@@retCL:
+ MOVSX EAX, CX
+ POP ESI
+end;
+
+function _AnsiCompareStrNoCaseA_Fast2(S1, S2: PAnsiChar): Integer;
+asm
+ CALL EAX2PChar
+ CALL EDX2PChar
+ PUSH ESI
+ XCHG ESI, EAX
+ XOR EAX, EAX
+@@1:
+ LODSB
+ MOV CX, word ptr [EAX*2 + SortAnsiOrderNoCase]
+ MOV AL, [EDX]
+ SUB CX, word ptr [EAX*2 + SortAnsiOrderNoCase]
+ JNZ @@retCL
+ INC EDX
+ TEST AL, AL
+ JNZ @@1
+@@retCL:
+ MOVSX EAX, CX
+ POP ESI
+end;
+
+function StrPCopy(Dest: PAnsiChar; const Source: Ansistring): PAnsiChar;
+asm
+ PUSH EAX
+ MOV EAX, EDX
+ CALL System.@LStrLen
+ MOV ECX, EAX
+ POP EAX
+ CALL EDX2PChar
+ CALL StrLCopy
+end;
+
+function StrEq( const S1, S2 : AnsiString ) : Boolean;
+asm
+ TEST EDX, EDX
+ JNZ @@1
+@@0: CMP EAX, EDX
+ JMP @@exit
+@@1: TEST EAX, EAX
+ JZ @@0
+ MOV ECX, [EAX-4]
+ CMP ECX, [EDX-4]
+ JNE @@exit
+ PUSH EAX
+ PUSH EDX
+ PUSH 0
+ MOV EDX, ESP
+ CALL LowerCase
+ PUSH 0
+ MOV EAX, [ESP + 8]
+ MOV EDX, ESP
+ CALL LowerCase
+ POP EAX
+ POP EDX
+ PUSH EDX
+ PUSH EAX
+ CALL System.@LStrCmp
+ MOV EAX, ESP
+ PUSHFD
+ XOR EDX, EDX
+ MOV DL, 2
+ CALL System.@LStrArrayClr
+ POPFD
+ POP EDX
+ POP EDX
+ POP EDX
+ POP EDX
+@@exit:
+ SETZ AL
+end;
+
+function AnsiEq( const S1, S2 : KOLString ) : Boolean;
+asm
+ CALL AnsiCompareStrNoCase
+ TEST EAX, EAX
+ SETZ AL
+end;
+
+function StrIn(const S: AnsiString; const A: array of AnsiString): Boolean;
+asm
+@@1:
+ TEST ECX, ECX
+ JL @@ret_0
+
+ PUSH EDX
+ MOV EDX, [EDX+ECX*4]
+ DEC ECX
+
+ PUSH ECX
+ PUSH EAX
+ CALL StrEq
+ DEC AL
+ POP EAX
+ POP ECX
+
+ POP EDX
+ JNZ @@1
+
+ MOV AL, 1
+ RET
+
+@@ret_0:XOR EAX, EAX
+end;
+
+{$IFDEF ASM_no}
+procedure NormalizeUnixText( var S: AnsiString );
+asm //cmd //opd
+ CMP dword ptr [EAX], 0
+ JZ @@exit
+ PUSH EBX
+ PUSH EDI
+ MOV EBX, EAX
+ CALL UniqueString
+ MOV EDI, [EBX]
+@@1: MOV EAX, EDI
+ CALL System.@LStrLen
+ XCHG ECX, EAX
+ MOV AX, $0D0A
+
+ CMP byte ptr [EDI], AL
+ JNE @@loo
+ MOV byte ptr [EDI], AH
+@@loo:
+ TEST ECX, ECX
+ JZ @@fin
+@@loo1:
+ REPNZ SCASB
+ JNZ @@fin
+ CMP byte ptr [EDI-2], AH
+ JE @@loo
+ MOV byte ptr [EDI-1], AH
+ JNE @@loo1
+@@fin: POP EDI
+ POP EBX
+@@exit:
+end;
+{$ENDIF}
+
+function FileCreate( const FileName: KOLString; OpenFlags: DWord): THandle;
+asm
+ XOR ECX, ECX
+ PUSH ECX
+ MOV ECX, EDX
+ SHR ECX, 16
+ AND CX, $1FFF
+ JNZ @@1
+ MOV CL, FILE_ATTRIBUTE_NORMAL
+@@1: PUSH ECX
+ MOV CL, DH
+ PUSH ECX // CreationMode
+ PUSH 0
+ MOV CL, DL
+ PUSH ECX // ShareMode
+ MOV DX, 0
+ PUSH EDX // AccessMode
+ //CALL System.@LStrToPChar // FileName must not be ''
+ PUSH EAX
+ CALL CreateFile
+end;
+
+function FileClose( Handle: THandle): Boolean;
+asm
+ PUSH EAX
+ CALL CloseHandle
+ TEST EAX, EAX
+ SETNZ AL
+end;
+
+function FileRead( Handle: THandle; var Buffer; Count: DWord): DWord;
+asm
+ PUSH EBP
+ PUSH 0
+ MOV EBP, ESP
+ PUSH 0
+ PUSH EBP
+ PUSH ECX
+ PUSH EDX
+ PUSH EAX
+ CALL ReadFile
+ TEST EAX, EAX
+ POP EAX
+ JNZ @@exit
+ XOR EAX, EAX
+@@exit:
+ POP EBP
+end;
+
+function File2Str( Handle: THandle): AnsiString;
+asm
+ PUSH EDX
+ TEST EAX, EAX
+ JZ @@exit // return ''
+
+ PUSH EBX
+ MOV EBX, EAX // EBX = Handle
+ XOR EDX, EDX
+ XOR ECX, ECX
+ INC ECX
+ CALL FileSeek
+ PUSH EAX // Pos
+ PUSH 0
+ PUSH EBX
+ CALL GetFileSize
+ POP EDX
+ SUB EAX, EDX // EAX = Size - Pos
+ JZ @@exitEBX
+
+ PUSH EAX
+ CALL System.@GetMem
+ XCHG EAX, EBX
+ MOV EDX, EBX
+ POP ECX
+ PUSH ECX
+ CALL FileRead
+ POP ECX
+ MOV EDX, EBX
+ POP EBX
+ POP EAX
+ PUSH EDX
+ {$IFDEF _D2}
+ CALL _LStrFromPCharLen
+ {$ELSE}
+ {$IFDEF _D2009orHigher}
+ PUSH ECX // TODO: check to remove
+ XOR ECX, ECX
+ {$ENDIF}
+ CALL System.@LStrFromPCharLen
+ {$IFDEF _D2009orHigher}
+ POP ECX
+ {$ENDIF}
+
+ {$ENDIF}
+ JMP @@freebuf
+
+@@exitEBX:
+ POP EBX
+@@exit:
+ XCHG EDX, EAX
+ POP EAX // @Result
+ PUSH EDX
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX // TODO: confirm not need push
+ {$ENDIF}
+ CALL System.@LStrFromPChar
+@@freebuf:
+ POP EAX
+ TEST EAX, EAX
+ JZ @@fin
+ CALL System.@FreeMem
+@@fin:
+end;
+
+function FileWrite( Handle: THandle; const Buffer; Count: DWord): DWord;
+asm
+ PUSH EBP
+ PUSH EBP
+ MOV EBP, ESP
+ PUSH 0
+ PUSH EBP
+ PUSH ECX
+ PUSH EDX
+ PUSH EAX
+ CALL WriteFile
+ TEST EAX, EAX
+ POP EAX
+ JNZ @@exit
+ XOR EAX, EAX
+@@exit:
+ POP EBP
+end;
+
+function FileEOF( Handle: THandle ) : Boolean;
+asm
+ PUSH EAX
+
+ PUSH 0
+ PUSH EAX
+ CALL GetFileSize
+
+ XCHG EAX, [ESP]
+
+ MOV CL, spCurrent
+ XOR EDX, EDX
+ CALL FileSeek
+
+ POP EDX
+ CMP EAX, EDX
+ SETGE AL
+end;
+
+procedure FileTime( const Path: KOLString;
+ CreateTime, LastAccessTime, LastModifyTime: PFileTime ); stdcall;
+const Size_TFindFileData = (sizeof(TFindFileData) + 3) and not 3;
+asm
+ PUSH ESI
+ PUSH EDI
+ SUB ESP, Size_TFindFileData
+ MOV EDX, ESP
+ MOV EAX, [Path]
+ CALL Find_First
+ TEST AL, AL
+ JZ @@exit
+ MOV EAX, ESP
+ CALL Find_Close
+ XOR ECX, ECX
+ MOV CL, 3
+@@loop: LEA ESI, [ESP+ECX*8-8].TFindFileData.ftCreationTime
+ MOV EDI, [ECX*4+EBP+8]
+ TEST EDI, EDI
+ JZ @@e_loop
+ MOVSD
+ MOVSD
+@@e_loop: LOOP @@loop
+@@exit: ADD ESP, Size_TFindFileData
+ POP EDI
+ POP ESI
+end;
+
+function CompareSystemTime( const D1, D2 : TSystemTime) : Integer; assembler;
+asm
+ PUSH ESI
+ PUSH EBX
+ MOV ESI, EAX
+ XOR EAX, EAX
+ XOR ECX, ECX
+ MOV CL, 8 // 8 words: wYear, wMonth,..., wMilliseconds
+@@loo:
+ LODSW
+ MOV BX, [EDX]
+ INC EDX
+ INC EDX
+
+ CMP CL, 6
+ JE @@cont // skip compare DayOfWeek
+
+ SUB AX, BX
+ JNE @@calc
+
+@@cont:
+ LOOP @@loo
+ JMP @@exit
+
+@@calc:
+ SBB EAX, EAX
+ {$IFDEF PARANOIA} DB $0C, 1 {$ELSE} OR AL, 1 {$ENDIF}
+
+@@exit:
+ POP EBX
+ POP ESI
+end;
+
+function DirectoryExists( const Name: KOLString): Boolean;
+asm
+ PUSH EBX
+ //CALL System.@LStrToPChar // Name must not be ''
+ PUSH EAX
+ PUSH SEM_NOOPENFILEERRORBOX or SEM_FAILCRITICALERRORS
+ CALL SetErrorMode
+ XCHG EBX, EAX
+ CALL GetFileAttributes
+ INC EAX
+ JZ @@exit
+ DEC EAX
+ {$IFDEF PARANOIA} DB $24, FILE_ATTRIBUTE_DIRECTORY {$ELSE} AND AL, FILE_ATTRIBUTE_DIRECTORY {$ENDIF}
+ SETNZ AL
+@@exit:
+ XCHG EAX, EBX
+ PUSH EAX
+ CALL SetErrorMode
+ XCHG EAX, EBX
+ POP EBX
+end;
+
+procedure TDirList.Clear;
+asm
+ LEA EDX, [EAX].FListPositions
+ CALL @@clear
+ ADD EDX, 4 // fStoreFiles -- order of fields is important!!!
+@@clear:
+ PUSHAD
+ XOR EAX, EAX
+ XCHG EAX, dword ptr [EDX]
+ CALL TObj.RefDec
+ POPAD
+@@exit:
+end;
+
+destructor TDirList.Destroy;
+asm
+ PUSH EBX
+ MOV EBX, EAX
+ CALL Clear
+ LEA EAX, [EBX].FPath
+ {$IFDEF UNICODE_CTRLS}
+ CALL System.@WStrClr
+ {$ELSE}
+ CALL System.@LStrClr
+ {$ENDIF}
+ XCHG EAX, EBX
+ CALL TObj.Destroy
+ POP EBX
+end;
+
+function TDirList.GetCount: Integer;
+asm
+ {CMP EAX, 0
+ JNZ @@1
+ NOP
+@@1: }
+ MOV ECX, [EAX].FListPositions
+ JECXZ @@retECX
+ MOV ECX, [ECX].TList.fCount
+@@retECX:
+ XCHG EAX, ECX
+end;
+
+procedure SwapDirItems( Data : PSortDirData; const e1, e2 : DWORD );
+asm
+ MOV EAX, [EAX].TSortDirData.Dir
+ MOV EAX, [EAX].TDirList.FListPositions
+ {$IFDEF xxSPEED_FASTER} //|||||||||||||||||||||||||||||||||||||||||||||
+ MOV EAX, [EAX].TList.fItems
+ LEA EDX, [EAX+EDX*4]
+ LEA ECX, [EAX+ECX*4]
+ MOV EAX, [EDX]
+ XCHG EAX, [ECX]
+ MOV [EDX], EAX
+ {$ELSE}
+ CALL TList.Swap
+ {$ENDIF}
+end;
+
+destructor TThread.Destroy;
+asm
+ PUSH EBX
+ MOV EBX, EAX
+ CALL RefInc
+ MOV EAX, EBX
+ CMP [EBX].FTerminated, 0
+ JNZ @@1
+ CALL Terminate
+ MOV EAX, EBX
+ CALL WaitFor
+@@1: MOV ECX, [EBX].FHandle
+ JECXZ @@2
+ PUSH ECX
+ CALL CloseHandle
+@@2: POP EAX
+ XCHG EBX, EAX
+ JMP TObj.Destroy
+end;
+
+destructor TStream.Destroy;
+asm
+ PUSH EAX
+ PUSH [EAX].fData.fThread
+ CALL [EAX].fMethods.fClose
+ POP EAX
+ CALL TObj.RefDec
+ POP EAX
+ CALL TObj.Destroy
+end;
+
+procedure CloseMemStream( Strm: PStream );
+asm
+ XOR ECX, ECX
+ XCHG ECX, [EAX].TStream.fMemory
+ JECXZ @@exit
+ XCHG EAX, ECX
+ CALL System.@FreeMem
+@@exit:
+end;
+
+function NewReadFileStream( const FileName: KOLString ): PStream;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ MOV EAX, offset[BaseFileMethods]
+ CALL _NewStream
+ MOV EDX, [ReadFileStreamProc]
+ MOV [EAX].TStream.fMethods.fRead, EDX
+ XCHG EBX, EAX
+ MOV EDX, ofOpenRead or ofOpenExisting or ofShareDenyWrite
+ CALL FileCreate
+ MOV [EBX].TStream.fData.fHandle, EAX
+ XCHG EAX, EBX
+ POP EBX
+end;
+
+function NewWriteFileStream( const FileName: KOLString ): PStream;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ MOV EAX, offset[BaseFileMethods]
+ CALL _NewStream
+ MOV [EAX].TStream.fMethods.fWrite, offset[WriteFileStreamEOF]
+ MOV [EAX].TStream.fMethods.fSetSiz, offset[SetSizeFileStream]
+ XCHG EBX, EAX
+ MOV EDX, ofOpenWrite or ofCreateAlways or ofShareDenyWrite
+ CALL FileCreate
+ MOV [EBX].TStream.fData.fHandle, EAX
+ XCHG EAX, EBX
+ POP EBX
+end;
+
+destructor TIniFile.Destroy;
+asm //cmd //opd
+ PUSH EAX
+ LEA EDX, [EAX].fFileName
+ PUSH EDX
+ LEA EAX, [EAX].fSection
+ {$IFDEF UNICODE_CTRLS}
+ CALL System.@WStrClr
+ {$ELSE}
+ CALL System.@LStrClr
+ {$ENDIF}
+ POP EAX
+ {$IFDEF UNICODE_CTRLS}
+ CALL System.@WStrClr
+ {$ELSE}
+ CALL System.@LStrClr
+ {$ENDIF}
+ POP EAX
+ CALL TObj.Destroy
+end;
+
+function MakeAccelerator( fVirt: Byte; Key: Word ): TMenuAccelerator;
+asm
+ MOVZX EAX, AL
+ PUSH EAX
+ MOV [ESP+1], DX
+ POP EAX
+end;
+
+function NewCommandActionsObj_Packed( fromPack: PAnsiChar ): PCommandActionsObj;
+asm
+ PUSH ESI
+ PUSH EDI
+ PUSH EAX
+ CALL NewCommandActionsObj
+ POP ESI
+ CMP ESI, 120
+ MOV [EAX].TCommandActionsObj.fIndexInActions, ESI
+ JB @@exit
+ PUSH EAX
+ LEA EDI, [EAX].TCommandActionsObj.aClick
+ XOR EAX, EAX
+ LODSB
+ MOV dword ptr [EDI + 76], EAX // Result.fIndexInActions := fromPack[0]
+ XOR ECX, ECX
+ MOV CL, 38
+@@loop:
+ CMP byte ptr[ESI], 200
+ JB @@copy_word
+ JA @@clear_words
+ INC ESI
+@@copy_word:
+ MOVSW
+ LOOP @@loop
+ JMP @@fin
+@@clear_words:
+ LODSB
+ SUB AL, 200
+ SUB CL, AL
+ PUSH ECX
+ MOVZX ECX, AL
+ XOR EAX, EAX
+ REP STOSW
+ POP ECX
+ INC ECX
+ LOOP @@loop
+@@fin:
+ POP EAX
+@@exit:
+ POP EDI
+ POP ESI
+end;
+
+function _NewTControl( AParent: PControl ): PControl;
+begin
+ New( Result, CreateParented( AParent ) );
+end;
+
+function _NewWindowed( AParent: PControl; ControlClassName: PKOLChar;
+ Ctl3D: Boolean; ACommandActions: TCommandActionsParam ): PControl;
+const Sz_TCommandActions = Sizeof(TCommandActions);
+asm
+ PUSH EBX
+ PUSH ESI
+ PUSH EDI
+ MOV EDI, ACommandActions
+ MOV [ACommandActions], ECX // Ctl3D -> ACommandActions
+
+ PUSH EDX // ControlClassName
+
+ MOV ESI, EAX // ESI = AParent
+ CALL _NewTControl
+ XCHG EBX, EAX // EBX = Result
+ POP [EBX].TControl.fControlClassName
+ //INC [EBX].TControl.fWindowed // set in TControl.Init
+
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV EAX, EDI
+ CMP EAX, 120
+ JB @@IdxActions_Loaded
+ MOVZX EAX, byte ptr[EDI]
+@@IdxActions_Loaded:
+ PUSH EAX
+ MOV ECX, dword ptr [AllActions_Objs + EAX*4]
+ JECXZ @@create_new_action
+ XCHG EAX, ECX
+ PUSH EAX
+ CALL TObj.RefInc
+ POP EAX
+ JMP @@action_assign
+
+@@create_new_action:
+ {$IFDEF PACK_COMMANDACTIONS}
+ MOV EAX, EDI
+ CALL NewCommandActionsObj_Packed
+ {$ELSE not PACK_COMMANDACTIONS}
+ CALL NewCommandActionsObj
+
+ TEST EDI, EDI
+ JZ @@no_actions
+
+ PUSH EAX
+ LEA EDX, [EAX].TCommandActionsObj.aClear
+ XCHG EAX, EDI
+ XOR ECX, ECX
+ MOV CL, Sz_TCommandActions
+ CALL Move
+ POP EAX
+ JMP @@action_assign
+ @@no_actions:
+ {$ENDIF not PACK_COMMANDACTIONS}
+ MOV [EAX].TCommandActionsObj.aClear, offset[ClearText]
+
+@@action_assign:
+ POP EDX
+ MOV dword ptr [AllActions_Objs + EDX*4], EAX
+
+ MOV [EBX].TControl.fCommandActions, EAX
+ XCHG EDX, EAX
+ MOV EAX, EBX
+ CALL TControl.Add2AutoFree
+
+ {$ELSE}
+ TEST EDI, EDI
+ JZ @@no_actions2
+ PUSH ESI
+ MOV ESI, EDI
+ LEA EDI, [EBX].TControl.fCommandActions
+ XOR ECX, ECX
+ MOV CL, Sz_TCommandActions
+ REP MOVSB
+ POP ESI
+ JMP @@actions_created
+@@no_actions2:
+ MOV [EBX].TControl.fCommandActions.TCommandActions.aClear, offset[ClearText]
+ {$ENDIF}
+@@actions_created:
+
+ TEST ESI, ESI
+ JZ @@no_parent
+
+ MOV EAX, [ESI].TControl.PP.fGotoControl
+ MOV [EBX].TControl.PP.fGotoControl, EAX
+
+ LEA ESI, [ESI].TControl.fTextColor
+ LEA EDI, [EBX].TControl.fTextColor
+ MOVSD // fTextColor
+ MOVSD // fColor
+
+ {$IFDEF SMALLEST_CODE}
+ {$IFDEF SMALLEST_CODE_PARENTFONT}
+ LODSD
+ XCHG EDX, EAX
+ XOR EAX, EAX
+ CALL TGraphicTool.Assign
+ STOSD // fFont
+ {$ELSE}
+ LODSD
+ XOR EAX, EAX
+ STOSD // fFont = nil
+ {$ENDIF}
+ {$ELSE}
+ LODSD
+ XCHG EDX, EAX
+ XOR EAX, EAX
+ PUSH EDX
+ CALL TGraphicTool.Assign
+ STOSD // fFont
+ POP EDX
+ XCHG ECX, EAX
+ JECXZ @@no_font
+ MOV [ECX].TGraphicTool.fParentGDITool, EDX
+ MOV [ECX].TGraphicTool.fOnGTChange.TMethod.Code, offset[TControl.FontChanged]
+ MOV [ECX].TGraphicTool.fOnGTChange.TMethod.Data, EBX
+ MOV EAX, EBX
+ MOV EDX, ECX
+ CALL TControl.FontChanged
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ MOV EAX, EBX
+ MOV EDX, [EBX].TControl.fFont
+ CALL TControl.Add2AutoFree
+ {$ENDIF}
+@@no_font:
+ {$ENDIF}
+
+ {$IFDEF SMALLEST_CODE}
+ LODSD
+ XOR EAX, EAX
+ STOSD
+ {$ELSE}
+ LODSD
+ XCHG EDX, EAX
+ XOR EAX, EAX
+ PUSH EDX
+ CALL TGraphicTool.Assign
+ STOSD // fBrush
+ POP EDX
+ XCHG ECX, EAX
+ JECXZ @@no_brush
+ MOV [ECX].TGraphicTool.fParentGDITool, EDX
+ MOV [ECX].TGraphicTool.fOnGTChange.TMethod.Code, offset[TControl.BrushChanged]
+ MOV [ECX].TGraphicTool.fOnGTChange.TMethod.Data, EBX
+ MOV EAX, EBX
+ MOV EDX, ECX
+ CALL TControl.BrushChanged
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ MOV EAX, EBX
+ MOV EDX, [EBX].TControl.fBrush
+ CALL TControl.Add2AutoFree
+ {$ENDIF}
+@@no_brush:
+ {$ENDIF}
+
+ MOVSB // fMargin
+ LODSD // skip fClientXXXXX
+ ADD EDI, 4
+
+ LODSB // fCtl3D_child
+ TEST AL, 2
+ JZ @@passed3D
+ MOV EDX, [ACommandActions] // DL <- Ctl3D !!!
+ AND AL, not 1
+ AND DL, 1
+ OR EAX, EDX
+@@passed3D:
+ STOSB // fCtl3D_child
+
+@@no_parent:
+ XCHG EAX, EBX
+ POP EDI
+ POP ESI
+ POP EBX
+ {$IFDEF DUMP_WINDOWED}
+ CALL DumpWindowed
+ {$ENDIF}
+end;
+
+function NewForm( AParent: PControl; const Caption: KOLString ): PControl;
+const FormClass: array[ 0..4 ] of KOLChar = ( 'F', 'o', 'r', 'm', #0 );
+asm
+ PUSH EBX
+ PUSH EDX
+ MOV EDX, offset[FormClass]
+ MOV CL, 1
+ {$IFDEF COMMANDACTIONS_OBJ}
+ PUSH OTHER_ACTIONS
+ {$ELSE}
+ PUSH 0
+ {$ENDIF}
+ CALL _NewWindowed
+ MOV EBX, EAX
+ OR byte ptr [EBX].TControl.fClsStyle, CS_DBLCLKS
+ MOV EDX, offset[WndProcForm]
+ CALL TControl.AttachProc
+ MOV EDX, offset[WndProcDoEraseBkgnd]
+ MOV EAX, EBX
+ CALL TControl.AttachProc
+ POP EDX
+ MOV EAX, EBX
+ CALL TControl.SetCaption
+ {$IFDEF USE_FLAGS}
+ OR [EBX].TControl.fFlagsG3, (1 shl G3_IsForm) or (1 shl G3_SizeGrip)
+ {$ELSE}
+ INC [EBX].TControl.fSizeGrip
+ DEC WORD PTR [EBX].TControl.fIsForm // why word?
+ {$ENDIF}
+ XCHG EAX, EBX
+ POP EBX
+end;
+
+function NewButton( AParent: PControl; const Caption: KOLString ): PControl;
+const szActions = sizeof(TCommandActions);
+asm
+ PUSH EBX
+ PUSH EDX
+
+ PUSH 0
+ {$IFDEF PACK_COMMANDACTIONS}
+ PUSH [ButtonActions_Packed]
+ {$ELSE}
+ PUSH offset[ButtonActions]
+ {$ENDIF}
+ MOV EDX, offset[ButtonClass]
+ MOV ECX, WS_VISIBLE or WS_CHILD or BS_PUSHLIKE or WS_TABSTOP or BS_NOTIFY
+ CALL _NewControl
+ XCHG EBX, EAX
+ //MOV Byte Ptr[EBX].TControl.aAutoSzX, 14
+ //MOV Byte Ptr[EBX].TControl.aAutoSzY, 6
+ MOV word ptr [EBX].TControl.aAutoSzX, 6 shl 8 + 14
+ MOV EDX, [EBX].TControl.fBoundsRect.Top
+ ADD EDX, 22
+ MOV [EBX].TControl.fBoundsRect.Bottom, EDX
+ MOV [EBX].TControl.fTextAlign, taCenter
+ {$IFDEF USE_FLAGS}
+ OR [EBX].TControl.fFlagsG5, (1 shl G5_IsButton) or (1 shl G5_IgnoreDefault)
+ {$ELSE}
+ INC [EBX].TControl.fIsButton
+ INC [EBX].TControl.fIgnoreDefault
+ {$ENDIF}
+ POP EDX
+ MOV EAX, EBX
+ CALL TControl.SetCaption
+ {$IFNDEF SMALLEST_CODE}
+ {$IFNDEF BUTTON_DBLCLICK}
+ MOV EAX, EBX
+ MOV EDX, offset[WndProcBtnDblClkAsClk]
+ CALL TControl.AttachProc
+ {$ENDIF}
+ {$ENDIF SMALLEST_CODE}
+ {$IFDEF ALL_BUTTONS_RESPOND_TO_ENTER}
+ MOV EAX, EBX
+ MOV EDX, offset[WndProcBtnReturnClick]
+ CALL TControl.AttachProc
+ {$ENDIF}
+ XCHG EAX, EBX
+ POP EBX
+
+{$IFDEF GRAPHCTL_XPSTYLES}
+ PUSH EAX
+ MOV EDX, offset[XP_Themes_For_BitBtn]
+ CALL Attach_WM_THEMECHANGED
+ POP EAX
+{$ENDIF}
+end;
+
+function WndProc_DrawItem( Sender: PControl; var Msg: TMsg; var Rslt: Integer )
+ : Boolean;
+asm //cmd //opd
+ CMP word ptr [EDX].TMsg.message, WM_DRAWITEM
+ JNZ @@ret_false
+ MOV EAX, [EDX].TMsg.lParam
+ MOV ECX, [EAX].TDrawItemStruct.hwndItem
+ JECXZ @@ret_false
+ PUSH EDX
+ {$IFDEF USE_PROP}
+ PUSH offset[ID_SELF]
+ PUSH ECX
+ CALL GetProp
+ {$ELSE}
+ PUSH GWL_USERDATA
+ PUSH ECX
+ CALL GetWindowLong
+ {$ENDIF}
+ POP EDX
+ TEST EAX, EAX
+ JZ @@ret_false
+ PUSH [EDX].TMsg.lParam
+ PUSH [EDX].TMsg.wParam
+ PUSH CN_DRAWITEM
+ PUSH EAX
+ CALL TControl.Perform
+ MOV AL, 1
+ RET
+@@ret_false:
+ XOR EAX, EAX
+end;
+
+{$IFDEF BITBTN_ASM}
+function NewBitBtn( AParent: PControl; const Caption: KOLString;
+ Options: TBitBtnOptions; Layout: TGlyphLayout; GlyphBitmap: HBitmap; GlyphCount: Integer ): PControl;
+const szBitmapInfo = sizeof(TBitmapInfo);
+asm
+ PUSH EBX
+ PUSH EDX
+ PUSH ECX
+
+ PUSH 0
+ {$IFDEF PACK_COMMANDACTIONS}
+ PUSH [ButtonActions_Packed]
+ {$ELSE}
+ PUSH offset[ButtonActions]
+ {$ENDIF}
+ MOV EDX, offset[ButtonClass]
+ MOV ECX, WS_VISIBLE or WS_CHILD or WS_TABSTOP or BS_OWNERDRAW or BS_NOTIFY
+ CALL _NewControl
+ XCHG EBX, EAX
+ {$IFDEF USE_FLAGS}
+ OR [EBX].TControl.fFlagsG5, (1 shl G5_IgnoreDefault)or(1 shl G5_IsButton)or(1 shl G5_IsBitBtn)
+ {$ELSE}
+ INC [EBX].TControl.fIgnoreDefault
+ INC [EBX].TControl.fIsButton
+ INC [EBX].TControl.fIsBitBtn
+ {$ENDIF}
+ //MOV byte ptr [EBX].TControl.fCommandActions.aAutoSzX, 8
+ //MOV byte ptr [EBX].TControl.fCommandActions.aAutoSzY, 8
+ MOV word ptr [EBX].TControl.fCommandActions.aAutoSzY, $808
+ POP EAX
+ MOV [EBX].TControl.fBitBtnOptions, AL
+ MOVZX EDX, Layout
+ MOV [EBX].TControl.fGlyphLayout, DL
+ MOV ECX, GlyphBitmap
+ MOV [EBX].TControl.fGlyphBitmap, ECX
+ MOV EDX, [EBX].TControl.fBoundsRect.Top
+ ADD EDX, 22
+ MOV [EBX].TControl.fBoundsRect.Bottom, EDX
+ TEST ECX, ECX
+ JZ @@noGlyphWH
+ {$IFDEF PARANOIA} DB $A8, 01 {$ELSE} TEST AL, bboImageList {$ENDIF}
+ JZ @@getBmpWH
+ PUSH EAX
+ MOV EAX, ESP
+ PUSH EAX
+ MOV EDX, ESP
+ PUSH EAX
+ PUSH EDX
+ PUSH ECX
+ CALL ImageList_GetIconSize
+ POP EAX
+ POP EDX
+ MOV ECX, GlyphCount
+ JMP @@WHready
+@@getBmpWH:
+ ADD ESP, -szBitmapInfo
+ PUSH ESP
+ PUSH szBitmapInfo
+ PUSH ECX
+ CALL GetObject
+ XCHG ECX, EAX
+ POP EAX
+ POP EAX
+ POP EDX
+ ADD ESP, szBitmapInfo-12
+ TEST ECX, ECX
+ JZ @@noGlyphWH
+ MOV ECX, GlyphCount
+ INC ECX
+ LOOP @@GlyphCountOK
+ PUSH EAX
+ PUSH EDX
+ XCHG EDX, ECX
+ DIV ECX
+ XCHG ECX, EAX
+ POP EDX
+ POP EAX
+@@GlyphCountOK:
+ CMP ECX, 1
+ JLE @@WHReady
+ PUSH EDX
+ CDQ
+ IDIV ECX
+ POP EDX
+@@WHReady:
+ MOV [EBX].TControl.fGlyphWidth, EAX
+ MOV [EBX].TControl.fGlyphHeight, EDX
+ MOV [EBX].TControl.fGlyphCount, ECX
+ POP ECX // ECX = @ Caption[ 1 ]
+ PUSH ECX
+ PUSH EDX
+ PUSH EAX
+ TEST EAX, EAX
+ JLE @@noWidthResize
+ JECXZ @@addWLeft
+ CMP [Layout], glyphOver
+ JE @@addWLeft
+ MOVZX ECX, byte ptr[ECX]
+ JECXZ @@addWLeft
+ // else
+ CMP [Layout], glyphLeft
+ JZ @@addWRight
+ CMP [Layout], glyphRight
+ JNZ @@noWidthResize
+@@addWRight:
+ ADD [EBX].TControl.fBoundsRect.Right, EAX
+ ADD byte ptr [EBX].TControl.aAutoSzX, AL
+ JMP @@noWidthResize
+@@addWLeft:
+ // then
+ ADD EAX, [EBX].TControl.fBoundsRect.Left
+ MOV [EBX].TControl.fBoundsRect.Right, EAX
+ MOV byte ptr [EBX].TControl.aAutoSzX, 0
+@@noWidthResize:
+ TEST EDX, EDX
+ JLE @@noHeightResize
+ CMP [Layout], glyphTop
+ JE @@addHBottom
+ CMP [Layout], glyphBottom
+ JNE @@addHTop
+@@addHBottom:
+ ADD [EBX].TControl.fBoundsRect.Bottom, EDX
+ ADD byte ptr [EBX].TControl.aAutoSzY, DL
+ JMP @@noHeightResize
+@@addHTop:
+ ADD EDX, [EBX].TControl.fBoundsRect.Top
+ MOV [EBX].TControl.fBoundsRect.Bottom, EDX
+ MOV byte ptr [EBX].TControl.aAutoSzY, 0
+@@noHeightResize:
+ POP ECX
+ POP EAX
+ CDQ
+ MOV DL, 4
+ TEST [EBX].TControl.fBitBtnOptions, 2 //1 shl bboNoBorder
+ JNZ @@noBorderResize
+ JECXZ @@noBorderWinc
+ ADD [EBX].TControl.fBoundsRect.Right, EDX
+ CMP [EBX].TControl.aAutoSzX, 0
+ JZ @@noBorderWinc
+ ADD [EBX].TControl.aAutoSzX, DL
+@@noBorderWinc:
+ TEST EAX, EAX
+ JLE @@noBorderResize
+ ADD [EBX].TControl.fBoundsRect.Bottom, EDX
+ CMP [EBX].TControl.aAutoSzY, 0
+ JZ @@noBorderResize
+ ADD [EBX].TControl.aAutoSzY, DL
+@@noBorderResize:
+@@noGlyphWH:
+ MOV ECX, [EBX].TControl.fParent
+ JECXZ @@notAttach2Parent
+ XCHG EAX, ECX
+ MOV EDX, offset[WndProc_DrawItem]
+ CALL TControl.AttachProc
+@@notAttach2Parent:
+ MOV EAX, EBX
+ MOV EDX, offset[WndProcBitBtn]
+ CALL TControl.AttachProc
+ MOV EAX, EBX
+ POP EDX
+ CALL TControl.SetCaption
+ MOV [EBX].TControl.fTextAlign, taCenter
+ {$IFDEF ALL_BUTTONS_RESPOND_TO_ENTER}
+ MOV EAX, EBX
+ MOV EDX, offset[WndProcBtnReturnClick]
+ CALL TControl.AttachProc
+ {$ENDIF}
+ XCHG EAX, EBX
+ POP EBX
+
+{$IFDEF GRAPHCTL_XPSTYLES}
+ PUSH EAX
+ MOV EDX, offset[XP_Themes_For_BitBtn]
+ CALL Attach_WM_THEMECHANGED
+ POP EAX
+ {$ENDIF}
+end;
+{$ENDIF BITBTN_ASM}
+
+function NewCheckbox( AParent: PControl; const Caption: KOLString ): PControl;
+asm
+ CALL NewButton
+ MOV EDX, [EAX].TControl.fBoundsRect.Left
+ ADD EDX, 72
+ MOV [EAX].TControl.fBoundsRect.Right, EDX
+ MOV [EAX].TControl.fStyle, WS_VISIBLE or WS_CHILD or BS_AUTOCHECKBOX or WS_TABSTOP or BS_NOTIFY
+ MOV [EAX].TControl.aAutoSzX, 24
+
+{$IFDEF GRAPHCTL_XPSTYLES}
+ PUSH EAX
+ MOV EDX, offset[XP_Themes_For_CheckBox]
+ CALL Attach_WM_THEMECHANGED
+ POP EAX
+{$ENDIF}
+end;
+
+procedure ClickRadio( Sender:PObj );
+asm
+ PUSH EBX
+ MOV EBX, [EAX].TControl.fParent
+ TEST EBX, EBX
+ JZ @@exit
+ {$IFDEF USE_FLAGS}
+ PUSH ESI
+ PUSH EDI
+ XCHG ESI, EAX
+ OR EDI, -1
+@@cont_loop:
+ INC EDI
+ MOV EAX, [EBX].TControl.fChildren
+ CMP EDI, [EAX].TList.fCount
+ JGE @@e_loop
+ MOV EDX, EDI
+ CALL TList.Get
+ TEST [EAX].TControl.fFlagsG5, 1 shl G5_IsButton
+ JZ @@cont_loop
+ TEST [EAX].TControl.fStyle.f0_Style, BS_RADIOBUTTON
+ JZ @@cont_loop
+ CMP EAX, ESI
+ PUSH EAX
+ SETZ DL
+ PUSH EDX
+ CALL TControl.GetChecked
+ POP EDX
+ CMP DL, AL
+ POP EAX
+ JZ @@cont_loop
+ CALL TControl.SetChecked
+ JMP @@cont_loop
+@@e_loop:
+ POP EDI
+ POP ESI
+ {$ELSE not USE_FLAGS}
+ PUSH [EAX].TControl.fMenu
+ MOV EAX, EBX
+ MOV EDX, offset[RADIO_LAST]
+ CALL TControl.Get_Prop_Int
+ PUSH EAX
+ MOV EAX, EBX
+ MOV EDX, offset[RADIO_1ST]
+ CALL TControl.Get_Prop_Int
+ PUSH EAX
+ PUSH [EBX].TControl.fHandle
+ CALL CheckRadioButton
+ {$ENDIF USE_FLAGS}
+@@exit:
+ POP EBX
+end;
+
+function NewRadiobox( AParent: PControl; const Caption: KOLString ): PControl;
+const
+ RadioboxStyles = WS_VISIBLE or WS_CHILD or BS_RADIOBUTTON or
+ WS_TABSTOP or WS_GROUP or BS_NOTIFY;
+asm
+ PUSH EBX
+ PUSH ESI
+ MOV ESI, EAX
+ CALL NewCheckbox
+ XCHG EBX, EAX
+ MOV [EBX].TControl.fStyle, RadioboxStyles
+ MOV [EBX].TControl.PP.fControlClick, offset[ClickRadio]
+ TEST ESI, ESI
+ JZ @@exit
+ {$IFDEF USE_FLAGS}
+ BTS DWORD PTR [ESI].TControl.fFlagsG1, 1 shl G1_HasRadio
+ JNZ @@exit
+ MOV EAX, EBX
+ CALL TControl.SetRadioChecked
+ {$ELSE}
+ MOV ECX, [EBX].TControl.fMenu
+ PUSH ECX
+ MOV EDX, offset[RADIO_LAST]
+ MOV EAX, ESI
+ CALL TControl.Set_Prop_Int
+ MOV EDX, offset[RADIO_1ST]
+ PUSH EDX
+ MOV EAX, ESI
+ CALL TControl.Get_Prop_Int
+ TEST EAX, EAX
+ POP EDX
+ POP ECX
+ JNZ @@exit
+ MOV EAX, ESI
+ CALL TControl.Set_Prop_Int
+ MOV EAX, EBX
+ CALL TControl.SetRadioChecked
+ {$ENDIF}
+@@exit: XCHG EAX, EBX
+ POP ESI
+ POP EBX
+
+{$IFDEF GRAPHCTL_XPSTYLES}
+ PUSH EAX
+ MOV EDX, offset[XP_Themes_For_RadioBox]
+ CALL Attach_WM_THEMECHANGED
+ POP EAX
+{$ENDIF}
+end;
+
+function NewWordWrapLabel( AParent: PControl; const Caption: KOLString ): PControl;
+asm
+ CALL NewLabel
+ MOV EDX, [EAX].TControl.fBoundsRect.Top
+ ADD EDX, 44
+ MOV [EAX].TControl.fBoundsRect.Bottom, EDX
+ {$IFDEF USE_FLAGS}
+ OR [EAX].TControl.fFlagsG1, (1 shl G1_WordWrap)
+ {$ELSE}
+ INC [EAX].TControl.fWordWrap
+ {$ENDIF}
+ AND byte ptr [EAX].TControl.fStyle, not SS_LEFTNOWORDWRAP
+end;
+
+function NewLabelEffect( AParent: PControl; const Caption: KOLString; ShadowDeep: Integer ): PControl;
+asm
+ PUSH EBX
+
+ PUSH ECX
+ PUSH EDX
+ XOR EDX, EDX
+ CALL NewLabel
+ MOV EBX, EAX
+ {$IFDEF USE_FLAGS}
+ AND [EBX].TControl.fFlagsG1, not(1 shl G1_IsStaticControl)
+ {$ELSE}
+ DEC [EBX].TControl.fIsStaticControl // ñíîâà 0 !
+ {$ENDIF USE_FLAGS}
+ MOV EDX, offset[WndProcLabelEffect]
+ CALL TControl.AttachProc
+
+ POP EDX
+ MOV EAX, EBX
+ CALL TControl.SetCaption
+
+ MOV EDX, offset[WndProcDoEraseBkgnd]
+ MOV EAX,EBX
+ CALL TControl.AttachProc
+ MOV [EBX].TControl.fTextAlign, taCenter
+ MOV [EBX].TControl.fTextColor, clWindowText
+ POP [EBX].TControl.DF.fShadowDeep
+ {$IFDEF USE_FLAGS}
+ OR [EBX].TControl.fFlagsG1, (1 shl G1_IgnoreWndCaption)
+ {$ELSE}
+ INC [EBX].TControl.fIgnoreWndCaption
+ {$ENDIF USE_FLAGS}
+ ADD [EBX].TControl.fBoundsRect.Bottom, 40 - 22
+ MOV [EBX].TControl.DF.fColor2, clNone
+
+ XCHG EAX, EBX
+ POP EBX
+end;
+
+function WndProcDoEraseBkgnd( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+asm // //
+ CMP word ptr [EDX].TMsg.message, WM_ERASEBKGND
+ JNE @@ret_false
+ MOV byte ptr [ECX], 1
+ PUSH EBX
+ PUSH EDI
+ MOV EBX, EAX
+ MOV EDI, [EDX].TMsg.wParam
+
+ {$IFDEF SMALLEST_CODE}
+ {$ELSE}
+ CALL TControl.CreateChildWindows
+ {$IFDEF USE_FLAGS}
+ TEST [EBX].TControl.fFlagsG2, (1 shl G2_Transparent)
+ {$ELSE}
+ CMP [EBX].TControl.fTransparent, 0
+ {$ENDIF USE_FLAGS}
+ JNE @@exit
+ {$ENDIF}
+
+ {$IFDEF SMALLEST_CODE}
+ {$ELSE}
+ PUSH OPAQUE
+ PUSH EDI
+ CALL SetBkMode
+ MOV EAX, [EBX].TControl.fColor
+ CALL Color2RGB
+ PUSH EAX
+ PUSH EDI
+ CALL SetBkColor
+ XOR EAX, EAX
+ PUSH EAX
+ PUSH EAX
+ PUSH EAX
+ PUSH EDI
+ CALL SetBrushOrgEx
+ {$ENDIF}
+ SUB ESP, 16
+ PUSH ESP
+ PUSH [EBX].TControl.fHandle
+ CALL GetClientRect
+ MOV EAX, EBX
+ CALL dword ptr[Global_GetCtlBrushHandle]
+ MOV EDX, ESP
+ PUSH EAX
+ PUSH EDX
+ PUSH EDI
+ CALL Windows.FillRect
+ ADD ESP, 16
+@@exit: POP EDI
+ POP EBX
+@@ret_false:
+ XOR EAX, EAX
+end;
+
+function WndProcSplitter( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+asm
+ CMP word ptr [EDX].TMsg.message, WM_NCHITTEST
+ JNE @@noWM_NCHITTEST
+ PUSH ECX
+ PUSH [EDX].TMsg.lParam
+ PUSH [EDX].TMsg.wParam
+ PUSH [EDX].TMsg.message
+ PUSH [EAX].TControl.fHandle
+ CALL DefWindowProc
+ TEST EAX, EAX
+ JLE @@htReady
+ XOR EAX, EAX
+ INC EAX
+@@htReady:
+ POP ECX
+ MOV [ECX], EAX
+ MOV AL, 1
+ RET
+
+@@noWM_NCHITTEST:
+ PUSH EBX
+ XCHG EBX, EAX
+ CMP word ptr [EDX].TMsg.message, WM_MOUSEMOVE
+ JNE @@noWM_MOUSEMOVE
+
+ PUSH [EBX].TControl.fCursor
+ CALL Windows.SetCursor
+
+ XOR EDX, EDX
+
+ {$IFDEF USE_ASM_DODRAG}
+ CALL @@DoDrag
+ {$ELSE}
+ MOV EAX, EBX
+ CALL DoDrag
+ {$ENDIF}
+
+ POP EBX
+ RET
+
+{$IFDEF USE_ASM_DODRAG}
+@@DoDrag:
+ PUSHAD
+ MOVZX EDI, DL // EDI = 1 if Cancel, 0 otherwise
+ CMP [EBX].TControl.fDragging, 0
+ JZ @@e_DoDrag
+ MOV EAX, [EBX].TControl.fParent
+ MOV EAX, [EAX].TControl.fChildren
+ PUSH EAX
+ MOV EDX, EBX
+ CALL TList.IndexOf
+ POP EDX // EDX = Self_.fParent.fChildren:PList
+ MOV EBP, EBX // Prev := Self_;
+ TEST EAX, EAX
+ JLE @@noPrev
+ MOV EDX, [EDX].TList.fItems
+ MOV EBP, [EDX+EAX*4-4] // Prev = Self_.fParent.fChildren.fItems[I-1]
+ PUSH EBP // push Prev
+@@noPrev:
+ PUSH EDX
+ PUSH EDX
+ PUSH ESP
+ CALL GetCursorPos
+ DEC EDI
+ JNZ @@noCancel
+ POP EDX
+ POP EDX
+ PUSH [EBX].TControl.fSplitStartPos.y
+ PUSH [EBX].TControl.fSplitStartPos.x
+@@noCancel:
+ OR EDI, -1
+ MOV CL, [EBX].TControl.fAlign
+ MOV AL, 1
+ SHL EAX, CL
+ {$IFDEF PARANOIA} DB $A8, chkRight or chkBott {$ELSE} TEST AL, chkRight or chkBott {$ENDIF} //fAlign in [ caRight, caBottom ] ?
+ JNZ @@mReady
+ INC EDI
+ INC EDI
+@@mReady:
+ MOV EDX, [EBX].TControl.fParent
+ MOVSX EBP, [EDX].TControl.fMargin
+ NEG EBP
+ {$IFDEF PARANOIA} DB $A8, chkTop or chkBott {$ELSE} TEST AL, chkTop or chkBott {$ENDIF} // fAlign in [ caTop, caBottom ] ?
+ XCHG EAX, EDX
+ JZ @@noTopBottom
+
+ CALL TControl.GetClientHeight
+ XCHG EDX, EAX
+
+ POP EAX
+ POP ESI // MousePos.y
+ MOV EAX, ESI
+ PUSH EDX // Self_.fParent.ClientHeight
+ SUB EAX, [EBX].TControl.fSplitStartPos.y
+ IMUL EAX, EDI
+ ADD EAX, [EBX].TControl.fSplitStartSize // EAX = NewSize1
+
+ POP EDX
+ SUB EDX, EAX
+ SUB EDX, [EBX].TControl.fBoundsRect.Bottom
+ ADD EDX, [EBX].TControl.fBoundsRect.Top
+ LEA EDX, [EDX+EBP*4]
+
+ MOV ECX, [EBX].TControl.fSecondControl
+ JECXZ @@noSecondControl
+ MOV EDX, [ECX].TControl.fBoundsRect.Bottom
+ SUB EDX, [ECX].TControl.fBoundsRect.Top
+ CMP [ECX].TControl.fAlign, caClient
+ JNZ @@noSecondControl
+
+ PUSH EAX
+ MOV EAX, [EBX].TControl.fSplitStartPos.y
+ SUB EAX, ESI
+ IMUL EAX, EDI
+ ADD EAX, [EBX].TControl.fSplitStartPos2.y
+ LEA EDX, [EAX+EBP*4]
+ POP EAX
+
+@@noSecondControl:
+ JMP @@newSizesReady
+
+@@noTopBottom:
+ CALL TControl.GetClientWidth
+ XCHG EDX, EAX
+
+ POP ESI // MousePos.x
+ POP ECX
+ MOV EAX, ESI
+ PUSH EDX // Self_.fParent.ClientWidth
+ SUB EAX, [EBX].TControl.fSplitStartPos.x
+ IMUL EAX, EDI
+ ADD EAX, [EBX].TControl.fSplitStartSize // EAX = NewSize1
+
+ POP EDX
+ SUB EDX, EAX
+ SUB EDX, [EBX].TControl.fBoundsRect.Right
+ ADD EDX, [EBX].TControl.fBoundsRect.Left
+ LEA EDX, [EDX+EBP*4]
+
+ MOV ECX, [EBX].TControl.fSecondControl
+ JECXZ @@newSizesReady
+ MOV EDX, [ECX].TControl.fBoundsRect.Right
+ SUB EDX, [ECX].TControl.fBoundsRect.Left
+ CMP [ECX].TControl.fAlign, caClient
+ JNZ @@noSecondControl
+
+ PUSH EAX
+ MOV EAX, [EBX].TControl.fSplitStartPos.x
+ SUB EAX, ESI
+ IMUL EAX, EDI
+ ADD EAX, [EBX].TControl.fSplitStartPos2.x
+ LEA EDX, [EAX+EBP*4]
+ POP EAX
+
+@@newSizesReady:
+ MOV ECX, [EBX].TControl.fSplitMinSize1
+ SUB ECX, EAX
+ JLE @@noCheckMinSize1
+ SUB EDX, ECX
+ ADD EAX, ECX
+
+@@noCheckMinSize1:
+ MOV ECX, [EBX].TControl.fSplitMinSize2
+ SUB ECX, EDX
+ JLE @@noCheckMinSize2
+ SUB EAX, ECX
+ ADD EDX, ECX
+
+@@noCheckMinSize2:
+ MOV ECX, [EBX].TControl.fOnSplit.TMethod.Code
+ JECXZ @@noOnSplit
+ PUSHAD
+ PUSH EDX
+ MOV ESI, ECX
+ XCHG ECX, EAX
+ MOV EDX, EBX
+ MOV EAX, [EBX].TControl.fOnSplit.TMethod.Data
+ CALL ESI
+ TEST AL, AL
+ POPAD
+ JZ @@e_DoDrag
+
+@@noOnSplit:
+ XCHG ESI, EAX // NewSize1 -> ESI
+ POP EBP
+ ADD ESP, -16
+ MOV EAX, EBP
+ MOV EDX, ESP
+ CALL TControl.GetBoundsRect
+ MOVZX ECX, [EBX].TControl.fAlign
+ LOOP @@noPrev_caLeft
+ ADD ESI, [ESP].TRect.Left
+ MOV [ESP].TRect.Right, ESI
+@@noPrev_caLeft:
+ LOOP @@noPrev_caTop
+ ADD ESI, [ESP].TRect.Top
+ MOV [ESP].TRect.Bottom, ESI
+@@noPrev_caTop:
+ LOOP @@noPrev_caRight
+ MOV EAX, [ESP].TRect.Right
+ SUB EAX, ESI
+ MOV [ESP].TRect.Left, EAX
+@@noPrev_caRight:
+ LOOP @@noPrev_caBottom
+ MOV EAX, [ESP].TRect.Bottom
+ SUB EAX, ESI
+ MOV [ESP].TRect.Top, EAX
+@@noPrev_caBottom:
+ MOV EAX, EBP
+ MOV EDX, ESP
+ CALL TControl.SetBoundsRect
+ ADD ESP, 16
+ {$IFDEF OLD_ALIGN}
+ MOV EAX, [EBX].TControl.fParent
+ {$ELSE NEW_ALIGN}
+ MOV EAX, EBX
+ {$ENDIF}
+ CALL dword ptr[Global_Align]
+
+@@e_DoDrag:
+ POPAD
+ RET
+{$ENDIF USE_ASM_DODRAG}
+
+@@noWM_MOUSEMOVE:
+ CMP word ptr [EDX].TMsg.message, WM_LBUTTONDOWN
+ JNE @@noWM_LBUTTONDOWN
+ MOV ECX, [EBX].TControl.fParent
+ TEST ECX, ECX
+ JZ @@noWM_LBUTTONDOWN
+
+ MOV EAX, [ECX].TControl.fChildren
+ PUSH EAX
+ MOV EDX, EBX
+ CALL TList.IndexOf
+ POP ECX
+ MOV EDX, EBX
+ TEST EAX, EAX
+ JLE @@noParent1
+ MOV ECX, [ECX].TList.fItems
+ MOV EDX, [ECX+EAX*4-4]
+@@noParent1:
+
+ MOV CL, [EBX].TControl.fAlign
+ MOV AL, 1
+ SHL EAX, CL
+ {$IFDEF PARANOIA} DB $A8, chkTop or chkBott {$ELSE} TEST AL, chkTop or chkBott {$ENDIF} // fAlign in [caTop,caBottom] ?
+ XCHG EAX, EDX
+ JZ @@no_caTop_caBottom
+ CALL TControl.GetHeight
+ JMP @@caTop_caBottom
+@@no_caTop_caBottom:
+ CALL TControl.GetWidth
+@@caTop_caBottom:
+ MOV [EBX].TControl.DF.fSplitStartSize, EAX
+ MOV ECX, [EBX].TControl.DF.fSecondControl
+ JECXZ @@noSecondControl1
+ XCHG EAX, ECX
+ PUSH EAX
+ CALL TControl.GetWidth
+ MOV [EBX].TControl.DF.fSplitStartPos2.x, EAX
+ POP EAX
+ CALL TControl.GetHeight
+ MOV [EBX].TControl.DF.fSplitStartPos2.y, EAX
+@@noSecondControl1:
+ PUSH [EBX].TControl.fHandle
+ CALL SetCapture
+ {$IFDEF USE_FLAGS}
+ OR [EBX].TControl.fFlagsG6, 1 shl G6_Dragging
+ {$ELSE}
+ OR [EBX].TControl.fDragging, 1
+ {$ENDIF}
+ PUSH 0
+ PUSH 100
+ PUSH $7B
+ PUSH [EBX].TControl.fHandle
+ CALL SetTimer
+ LEA EAX, [EBX].TControl.DF.fSplitStartPos
+ PUSH EAX
+ CALL GetCursorPos
+ JMP @@exit
+
+@@noWM_LBUTTONDOWN:
+ CMP word ptr [EDX].TMsg.message, WM_LBUTTONUP
+ JNE @@noWM_LBUTTONUP
+ XOR EDX, EDX
+
+ {$IFDEF USE_ASM_DODRAG}
+ CALL @@DoDrag
+ {$ELSE}
+ MOV EAX, EBX
+ CALL DoDrag
+ {$ENDIF}
+
+ JMP @@killtimer
+
+@@noWM_LBUTTONUP:
+ CMP word ptr[EDX].TMsg.message, WM_TIMER
+ JNE @@exit
+ {$IFDEF USE_FLAGS}
+ TEST [EBX].TControl.fFlagsG6, 1 shl G6_Dragging
+ {$ELSE}
+ CMP [EBX].TControl.fDragging, 0
+ {$ENDIF}
+ JE @@exit
+ PUSH VK_ESCAPE
+ CALL GetAsyncKeyState
+ TEST EAX, EAX
+ JGE @@exit
+
+ MOV DL, 1
+ {$IFDEF USE_ASM_DODRAG}
+ CALL @@DoDrag
+ {$ELSE}
+ MOV EAX, EBX
+ CALL DoDrag
+ {$ENDIF}
+
+@@killtimer:
+ {$IFDEF USE_FLAGS}
+ AND [EBX].TControl.fFlagsG6, $7F //not(1 shl G6_Dragging)
+ {$ELSE}
+ MOV [EBX].TControl.fDragging, 0
+ {$ENDIF}
+ PUSH $7B
+ PUSH [EBX].TControl.fHandle
+ CALL KillTimer
+ CALL ReleaseCapture
+
+@@exit:
+ POP EBX
+ XOR EAX, EAX
+end;
+
+function NewSplitterEx( AParent: PControl; MinSizePrev, MinSizeNext: Integer;
+ EdgeStyle: TEdgeStyle ): PControl;
+const int_IDC_SIZEWE = integer( IDC_SIZEWE );
+asm
+ PUSH EBX
+ PUSH EAX // AParent
+ PUSH ECX // MinSizePrev
+ PUSH EDX // MinSizeNext
+ MOV DL, EdgeStyle
+ CALL NewPanel
+ XCHG EBX, EAX
+ POP [EBX].TControl.DF.fSplitMinSize1
+ POP [EBX].TControl.DF.fSplitMinSize2
+ {$IFDEF USE_FLAGS}
+ MOV [EBX].TControl.fFlagsG5, 1 shl G5_IsSplitter
+ {$ELSE}
+ INC [EBX].TControl.fIsSplitter
+ {$ENDIF}
+ XOR EDX, EDX
+ MOV DL, 4
+ MOV EAX, [EBX].TControl.fBoundsRect.Left
+ ADD EAX, EDX
+ MOV [EBX].TControl.fBoundsRect.Right, EAX
+ ADD EDX, [EBX].TControl.fBoundsRect.Top
+ MOV [EBX].TControl.fBoundsRect.Bottom, EDX
+
+ POP ECX // ECX = AParent
+ JECXZ @@noParent2
+ MOV EAX, [ECX].TControl.fChildren
+ MOV ECX, [EAX].TList.fCount
+ CMP ECX, 1
+ JLE @@noParent2
+
+ MOV EAX, [EAX].TList.fItems
+ MOV EAX, [EAX+ECX*4-8]
+ MOV CL, [EAX].TControl.fAlign
+ PUSH ECX
+ MOV AL, 1
+ SHL EAX, CL
+ {$IFDEF PARANOIA} DB $A8, chkTop or chkBott {$ELSE} TEST AL, chkTop or chkBott {$ENDIF}
+ MOV EAX, int_IDC_SIZEWE
+ JZ @@TopBottom
+ INC EAX
+@@TopBottom:
+ PUSH EAX
+ PUSH 0
+ CALL LoadCursor
+ MOV [EBX].TControl.fCursor, EAX
+ POP EDX
+ MOV EAX, EBX
+ CALL TControl.SetAlign
+
+@@noParent2:
+ MOV EAX, EBX
+ MOV EDX, offset[WndProcSplitter]
+ CALL TControl.AttachProc
+ XCHG EAX, EBX
+ POP EBX
+
+{$IFDEF GRAPHCTL_XPSTYLES}
+ PUSH EAX
+ MOV EDX, offset[XP_Themes_For_Splitter]
+ CALL Attach_WM_THEMECHANGED
+ POP EAX
+{$ENDIF}
+end;
+
+function NewGradientPanel( AParent: PControl; Color1, Color2: TColor ): PControl;
+asm
+ PUSH ECX
+ PUSH EDX
+ XOR EDX, EDX
+ CALL NewLabel
+ PUSH EAX
+ MOV EDX, offset[WndProcGradient]
+ CALL TControl.AttachProc
+ POP EAX
+ POP [EAX].TControl.DF.fColor1
+ POP [EAX].TControl.DF.fColor2
+ ADD [EAX].TControl.fBoundsRect.Right, 40-64
+ ADD [EAX].TControl.fBoundsRect.Bottom, 40 - 22
+end;
+
+function NewGradientPanelEx( AParent: PControl; Color1, Color2: TColor;
+ Style: TGradientStyle; Layout: TGradientLayout ): PControl;
+asm
+ PUSH ECX
+ PUSH EDX
+ XOR EDX, EDX
+ CALL NewLabel
+ PUSH EAX
+ MOV EDX, offset[WndProcGradientEx]
+ CALL TControl.AttachProc
+ POP EAX
+ POP [EAX].TControl.DF.fColor1
+ POP [EAX].TControl.DF.fColor2
+ ADD [EAX].TControl.fBoundsRect.Right, 40-100
+ ADD [EAX].TControl.fBoundsRect.Bottom, 40 - 22
+ MOV DL, Style
+ MOV [EAX].TControl.DF.fGradientStyle, DL
+ MOV DL, Layout
+ MOV [EAX].TControl.DF.fGradientLayout, DL
+end;
+
+const EditClass: array[0..4] of KOLChar = ( 'E','D','I','T',#0 );
+function NewEditbox( AParent: PControl; Options: TEditOptions ) : PControl;
+const int_IDC_IBEAM = integer( IDC_IBEAM );
+const WS_flags = integer( WS_VISIBLE or WS_CHILD or WS_TABSTOP or WS_BORDER );
+const WS_clear = integer( not(WS_VSCROLL or WS_HSCROLL) );
+asm
+ PUSH EBX
+ XCHG EBX, EAX // EBX=AParent
+ PUSH EDX
+ MOV EAX, ESP
+ XOR ECX, ECX
+ MOV CL, 11
+ MOV EDX, offset [EditFlags]
+ CALL MakeFlags
+ XCHG ECX, EAX // ECX = Flags
+ POP EAX // Options
+ PUSH EAX
+ {$IFDEF PARANOIA} DB $A8, 8 {$ELSE} TEST AL, 8 {$ENDIF}
+ JNZ @@1
+ AND ECX, WS_clear
+@@1: OR ECX, WS_flags
+ PUSH 1
+ {$IFDEF PACK_COMMANDACTIONS}
+ PUSH [EditActions_Packed]
+ {$ELSE}
+ PUSH offset[EditActions]
+ {$ENDIF}
+ MOV EDX, offset[EditClass]
+ XCHG EAX, EBX
+ CALL _NewControl
+ XCHG EBX, EAX
+ MOV Byte Ptr [EBX].TControl.aAutoSzY, 6
+ LEA ECX, [EBX].TControl.fBoundsRect
+ MOV EDX, [ECX].TRect.Left
+ ADD EDX, 100
+ MOV [ECX].TRect.Right, EDX
+ MOV EDX, [ECX].TRect.Top
+ ADD EDX, 22
+ MOV [ECX].TRect.Bottom, EDX
+ POP EAX // Options
+ {$IFDEF PARANOIA} DB $A8, 8 {$ELSE} TEST AL, 8 {$ENDIF}
+ MOV DL, $0D
+ JZ @@2
+ ADD [ECX].TRect.Right, 100
+ ADD [ECX].TRect.Bottom, 200 - 22
+ MOV DL, 1
+ {$IFDEF USE_FLAGS}
+ OR [EBX].TControl.fFlagsG5, 1 shl G5_IgnoreDefault
+ {$ELSE}
+ INC [EBX].TControl.fIgnoreDefault
+ {$ENDIF}
+@@2:
+ TEST AH, 4
+ JZ @@3
+ AND DL, $FE
+@@3: MOV [EBX].TControl.fLookTabKeys, DL
+{$IFDEF UNICODE_CTRLS}
+ MOV EAX, EBX
+ MOV EDX, offset[WndProcUnicodeChars]
+ CALL TControl.AttachProc
+{$ENDIF}
+ XCHG EAX, EBX
+ POP EBX
+end;
+
+{$IFNDEF USE_DROPDOWNCOUNT}
+procedure ComboboxDropDown( Sender: PObj );
+asm
+ PUSH EBX
+ PUSH ESI
+ MOV EBX, EAX
+ CALL TControl.GetItemsCount
+ CMP EAX, 1
+ JGE @@1
+ MOV AL, 1
+@@1: CMP EAX, 8
+ JLE @@2
+ XOR EAX, EAX
+ MOV AL, 8
+@@2: XOR ESI, ESI
+ PUSH SWP_NOMOVE or SWP_NOSIZE or SWP_NOZORDER or SWP_NOACTIVATE or SWP_NOREDRAW or SWP_SHOWWINDOW
+ PUSH ESI
+ PUSH ESI
+ PUSH SWP_NOMOVE or SWP_NOZORDER or SWP_NOACTIVATE or SWP_NOREDRAW or SWP_HIDEWINDOW
+ PUSH EAX
+ MOV EAX, EBX
+ CALL TControl.GetHeight
+ POP ECX
+ INC ECX
+ IMUL ECX
+ INC EAX
+ INC EAX
+ PUSH EAX
+ MOV EAX, EBX
+ CALL TControl.GetWidth
+ PUSH EAX
+ INC ESI
+@@3: XOR EDX, EDX
+ PUSH EDX
+ PUSH EDX
+ PUSH EDX
+ PUSH [EBX].TControl.fHandle
+ CALL SetWindowPos
+ DEC ESI
+ JZ @@3
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EBX].TControl.EV
+ MOV ECX, [EAX].TEvents.fOnDropDown.TMethod.Code
+ {$ELSE}
+ MOV ECX, [EBX].TControl.EV.fOnDropDown.TMethod.Code
+ {$ENDIF}
+ {$IFDEF NIL_EVENTS}
+ JECXZ @@exit
+ {$ENDIF}
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EAX].TEvents.fOnDropDown.TMethod.Data
+ {$ELSE}
+ MOV EAX, [EBX].TControl.EV.fOnDropDown.TMethod.Data
+ {$ENDIF}
+ MOV EDX, EBX
+ CALL ECX
+@@exit: POP ESI
+ POP EBX
+end;
+{$ENDIF}
+
+const ComboboxClass: array[0..8] of KOLChar = ('C','O','M','B','O','B','O','X',#0 );
+function NewCombobox( AParent: PControl; Options: TComboOptions ): PControl;
+asm
+ {$IFDEF GRAPHCTL_XPSTYLES}
+ {$IFDEF UNICODE_CTRLS}
+ PUSHAD
+ CALL InitCommonControls;
+ POPAD
+ {$ENDIF}
+ {$ENDIF}
+ PUSH EDX
+ PUSH EAX
+ PUSH EDX
+ MOV EAX, ESP
+ MOV EDX, offset[ComboFlags]
+ XOR ECX, ECX
+ MOV CL, 10
+ CALL MakeFlags
+ POP EDX
+ XCHG ECX, EAX
+ POP EAX
+ PUSH 1
+ {$IFDEF PACK_COMMANDACTIONS}
+ PUSH [ComboActions_Packed]
+ {$ELSE}
+ PUSH offset[ComboActions]
+ {$ENDIF}
+ MOV EDX, offset[ComboboxClass]
+ OR ECX, WS_VISIBLE or WS_CHILD or WS_VSCROLL or CBS_HASSTRINGS or WS_TABSTOP
+ TEST ECX, CBS_SIMPLE
+ JNZ @@O
+ OR ECX, CBS_DROPDOWN
+@@O:
+ CALL _NewControl
+ {$IFDEF PACK_COMMANDACTIONS}
+ MOV EDX, [EAX].TControl.fCommandActions
+ MOV [EDX].TCommandActionsObj.aClear, offset[ClearCombobox]
+ {$ENDIF}
+ MOV Byte Ptr [EAX].TControl.aAutoSzY, 6
+ MOV [EAX].TControl.PP.fCreateWndExt, offset[CreateComboboxWnd]
+ OR byte ptr [EAX].TControl.fClsStyle, CS_DBLCLKS
+ ADD [EAX].TControl.fBoundsRect.Right, 100-64
+ ADD [EAX].TControl.fBoundsRect.Bottom, 22-64
+ MOV CL, 1
+ POP EDX
+ TEST DL, 1
+ JZ @@exit
+ MOV CL, 3
+@@exit:
+ MOV [EAX].TControl.fLookTabKeys, CL
+ PUSH EAX
+ MOV EDX, offset[ WndProcCombo ]
+ CALL TControl.AttachProc
+ POP EAX
+end;
+
+function WndProcParentResize( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+asm
+ CMP word ptr [EDX].TMsg.message, CM_SIZE
+ JNZ @@exit
+ PUSH EAX
+ PUSH 0
+ PUSH 0
+ PUSH WM_SIZE
+ PUSH EAX
+ CALL TControl.Perform
+ POP EAX
+ CALL TControl.Invalidate
+@@exit: XOR EAX, EAX
+end;
+
+procedure InitCommonControlCommonNotify( Ctrl: PControl );
+asm
+ {$IFDEF USE_FLAGS}
+ OR [EAX].TControl.fFlagsG5, 1 shl G5_IsCommonCtl
+ {$ELSE}
+ MOV [EAX].TControl.fIsCommonControl, 1
+ {$ENDIF}
+ MOV ECX, [EAX].TControl.fParent
+ JECXZ @@fin
+ PUSH ECX
+ MOV EDX, offset[WndProcCommonNotify]
+ CALL TControl.AttachProc
+ POP EAX
+ MOV EDX, offset[WndProcNotify]
+ CALL TControl.AttachProc
+@@fin:
+end;
+
+function NewProgressbar( AParent: PControl ): PControl;
+asm
+ PUSH 1
+ {$IFDEF COMMANDACTIONS_OBJ}
+ PUSH PROGRESS_ACTIONS
+ {$ELSE}
+ PUSH 0
+ {$ENDIF}
+ MOV EDX, offset[Progress_class]
+ MOV ECX, WS_CHILD or WS_VISIBLE
+ CALL _NewCommonControl
+ LEA EDX, [EAX].TControl.fBoundsRect
+ MOV ECX, [EDX].TRect.Left
+ ADD ECX, 300
+ MOV [EDX].TRect.Right, ECX
+ MOV ECX, [EDX].TRect.Top
+ ADD ECX, 20
+ MOV [EDX].TRect.Bottom, ECX
+ XOR EDX, EDX
+ MOV [EAX].TControl.fMenu, EDX
+ MOV [EAX].TControl.fTextColor, clHighlight
+ {$IFDEF COMMANDACTIONS_OBJ} //todo: should be used separate Actions record
+ MOV ECX, [EAX].TControl.fCommandActions
+ MOV [ECX].TCommandActionsObj.aSetBkColor, PBM_SETBKCOLOR
+ {$ELSE}
+ MOV [EAX].TControl.fCommandActions.aSetBkColor, PBM_SETBKCOLOR
+ {$ENDIF}
+end;
+
+function NewProgressbarEx( AParent: PControl; Options: TProgressbarOptions ): PControl;
+asm
+ PUSH EDX
+ CALL NewProgressbar
+ POP ECX
+ XOR EDX, EDX
+ SHR ECX, 1
+ JNC @@notVert
+ MOV DL, 4
+@@notVert:
+ SHR ECX, 1
+ JNC @@notSmooth
+ INC EDX
+@@notSmooth:
+ OR [EAX].TControl.fStyle, EDX
+end;
+
+// by Galkov, Jun-2009
+function WndProcNotify( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+asm
+ CMP word ptr [EDX].TMsg.message, WM_NOTIFY
+ JNE @@ret_false
+ PUSH ECX
+ PUSH EDX
+ push eax
+ MOV ECX, [EDX].TMsg.lParam
+ {$IFDEF USE_PROP}
+ PUSH offset[ID_SELF]
+ PUSH [ECX].TNMHdr.hwndFrom
+ CALL GetProp
+ {$ELSE}
+ PUSH GWL_USERDATA
+ PUSH [ECX].TNMHdr.hwndFrom
+ CALL GetWindowLong
+ {$ENDIF}
+ pop ecx
+ POP EDX
+ TEST EAX, EAX
+ JZ @@ret_false_ECX
+ cmp eax, ecx
+ jz @@ret_false_ECX
+ MOV ECX, [EAX].TControl.fHandle
+ MOV [EDX].TMsg.hwnd, ECX
+ POP ECX
+ JMP TControl.EnumDynHandlers
+@@ret_false_ECX:
+ POP ECX
+@@ret_false:
+ XOR EAX, EAX
+end;
+
+function WndProcCommonNotify( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+asm
+ CMP word ptr [EDX].TMsg.message, WM_NOTIFY
+ JNE @@ret_false
+ PUSH EBX
+ MOV EBX, [EDX].TMsg.lParam
+ MOV EDX, [EBX].TNMHdr.code
+
+@@chk_nm_click:
+ XOR ECX, ECX
+ CMP EDX, NM_CLICK
+ JZ @@click
+ CMP EDX, NM_RCLICK
+ JNE @@chk_killfocus
+ {$IFDEF USE_FLAGS}
+ MOV CL, 1 shl G6_RightClick
+ {$ELSE}
+ INC ECX
+ {$ENDIF}
+@@click:
+ {$IFDEF USE_FLAGS}
+ AND [EAX].TControl.fFlagsG6, not(1 shl G6_RightClick)
+ OR [EAX].TControl.fFlagsG6, CL
+ {$ELSE}
+ MOV [EAX].TControl.fRightClick, CL
+ {$ENDIF}
+
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV ECX, [EAX].TControl.EV
+ MOV ECX, [ECX].TEvents.fOnClick.TMethod.Code
+ {$ELSE}
+ MOV ECX, [EAX].TControl.EV.fOnClick.TMethod.Code
+ {$ENDIF}
+ {$IFDEF NIL_EVENTS}
+ JECXZ @@fin_false
+ {$ENDIF}
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EDX, [EAX].TControl.EV
+ MOV EDX, [EDX].TEvents.fOnClick.TMethod.Data
+ {$ELSE}
+ MOV EDX, [EAX].TControl.EV.fOnClick.TMethod.Data
+ {$ENDIF}
+ JMP @@fin_event
+
+{$IFDEF NIL_EVENTS}
+@@fin_false:
+ POP EBX
+@@ret_false:
+ XOR EAX, EAX
+ RET
+{$ENDIF}
+
+@@chk_killfocus:
+ CMP EDX, NM_KILLFOCUS
+ JNE @@chk_setfocus
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EAX].TControl.EV
+ MOV ECX, [EAX].TEvents.fOnLeave.TMethod.Code
+ {$ELSE}
+ MOV ECX, [EAX].TControl.EV.fOnLeave.TMethod.Code
+ {$ENDIF}
+ {$IFDEF NIL_EVENTS}
+ JECXZ @@fin_false
+ {$ENDIF}
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EDX, [EAX].TEvents.fOnLeave.TMethod.Data
+ {$ELSE}
+ MOV EDX, [EAX].TControl.EV.fOnLeave.TMethod.Data
+ {$ENDIF}
+ JMP @@fin_event
+@@chk_setfocus:
+ CMP EDX, NM_RETURN
+ JE @@set_focus
+ CMP EDX, NM_SETFOCUS
+ JNE @@fin_false
+
+@@set_focus:
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EAX].TControl.EV
+ MOV ECX, [EAX].TEvents.fOnEnter.TMethod.Code
+ {$ELSE}
+ MOV ECX, [EAX].TControl.EV.fOnEnter.TMethod.Code
+ {$ENDIF}
+ {$IFDEF NIL_EVENTS}
+ JECXZ @@fin_false
+ {$ENDIF}
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EDX, [EAX].TEvents.fOnEnter.TMethod.Data
+ {$ELSE}
+ MOV EDX, [EAX].TControl.EV.fOnEnter.TMethod.Data
+ {$ENDIF}
+
+@@fin_event:
+ XCHG EAX, EDX
+ CALL ECX
+{$IFnDEF NIL_EVENTS}
+@@fin_false:
+{$ENDIF}
+ POP EBX
+{$IFnDEF NIL_EVENTS}
+@@ret_false:
+{$ENDIF}
+ //MOV AL, 1
+ XOR EAX, EAX
+end;
+
+procedure ApplyImageLists2Control( Sender: PControl );
+asm
+ PUSHAD
+ XCHG ESI, EAX
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [ESI].TControl.fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.aSetImgList
+ {$ELSE}
+ MOVZX ECX, [ESI].TControl.fCommandActions.aSetImgList
+ {$ENDIF}
+ JECXZ @@fin
+ MOV EBP, ECX
+ XOR EBX, EBX
+ MOV BL, 32
+ XOR EDI, EDI
+@@loo:
+ MOV EAX, ESI
+ MOV EDX, EBX
+ CALL TControl.GetImgListIdx
+ TEST EAX, EAX
+ JZ @@nx
+ CALL TImageList.GetHandle
+ PUSH EAX
+ PUSH EDI
+ PUSH EBP
+ PUSH ESI
+ CALL TControl.Perform
+@@nx:
+ INC EDI
+ SHR EBX, 1
+ JZ @@fin
+ CMP BL, 16
+ JGE @@loo
+ XOR EBX, EBX
+ JMP @@loo
+@@fin:
+ POPAD
+end;
+
+procedure ApplyImageLists2ListView( Sender: PControl );
+asm
+ PUSHAD
+
+ XCHG ESI, EAX
+ PUSH dword ptr [ESI].TControl.DF.fLVOptions
+ MOV EAX, ESP
+ MOV EDX, offset[ListViewFlags]
+ XOR ECX, ECX
+ MOV CL, 25
+ CALL MakeFlags
+ POP ECX
+ PUSH ECX
+
+ MOV EDX, [ESI].TControl.fStyle
+ //AND DH, 3
+ AND DX, not $403F
+ OR EDX, EAX
+
+ MOVZX EAX, [ESI].TControl.DF.fLVStyle
+ OR EDX, [EAX*4 + offset ListViewStyles]
+
+ MOV EAX, ESI
+ CALL TControl.SetStyle
+
+ MOV EAX, ESP
+ MOV EDX, offset[ListViewExFlags]
+ XOR ECX, ECX
+ MOV CL, 23
+ CALL MakeFlags
+ POP EDX
+ PUSH EAX
+ PUSH $3FFF
+ PUSH LVM_SETEXTENDEDLISTVIEWSTYLE
+ PUSH ESI
+ CALL TControl.Perform
+
+ POPAD
+ CALL ApplyImageLists2Control
+end;
+
+function NewListView( AParent: PControl; Style: TListViewStyle; Options: TListViewOptions;
+ ImageListSmall, ImageListNormal, ImageListState: PImageList ): PControl;
+asm
+ PUSH EDX
+ PUSH ECX
+ MOVZX EDX, DL
+ MOV ECX, [EDX*4 + offset ListViewStyles]
+ OR ECX, LVS_SHAREIMAGELISTS or WS_CHILD or WS_VISIBLE or WS_TABSTOP
+ MOV EDX, offset[WC_LISTVIEW]
+ PUSH 1
+ {$IFDEF PACK_COMMANDACTIONS}
+ PUSH [ListViewActions_Packed]
+ {$ELSE}
+ PUSH offset[ListViewActions]
+ {$ENDIF}
+ CALL _NewCommonControl
+
+ {$IFDEF PACK_COMMANDACTIONS}
+ MOV EDX, [EAX].TControl.fCommandActions
+ MOV [EDX].TCommandActionsObj.aClear, offset[ClearListView]
+ {$ENDIF}
+
+ MOV EDX, ESP
+ PUSH EAX
+ XCHG EAX, EDX
+ MOV EDX, offset ListViewFlags
+ XOR ECX, ECX
+ MOV CL, 25
+ CALL MakeFlags
+ XCHG EDX, EAX
+ POP EAX
+ MOV ECX, [EAX].TControl.fStyle
+ AND ECX, not LVS_TYPESTYLEMASK
+ OR EDX, ECX
+ MOV [EAX].TControl.fStyle, EDX
+
+ POP [EAX].TControl.DF.fLVOptions
+ POP EDX
+ MOV [EAX].TControl.DF.fLVStyle, DL
+ MOV [EAX].TControl.PP.fCreateWndExt, offset[ApplyImageLists2ListView]
+ ADD [EAX].TControl.fBoundsRect.Right, 200-64
+ ADD [EAX].TControl.fBoundsRect.Bottom, 150-64
+ MOV ECX, [ImageListState]
+ XOR EDX, EDX
+ PUSHAD
+ CALL TControl.SetImgListIdx
+ POPAD
+ MOV ECX, [ImageListSmall]
+ MOV DL, 16
+ PUSHAD
+ CALL TControl.SetImgListIdx
+ POPAD
+ MOV ECX, [ImageListNormal]
+ ADD EDX, EDX
+ PUSH EAX
+ CALL TControl.SetImgListIdx
+ POP EAX
+ MOV [EAX].TControl.DF.fLVTextBkColor, clWindow
+ XOR EDX, EDX
+ INC EDX
+ MOV [EAX].TControl.fLookTabKeys, DL
+end;
+
+function NewTreeView( AParent: PControl; Options: TTreeViewOptions;
+ ImgListNormal, ImgListState: PImageList ): PControl;
+asm //cmd //opd
+ PUSH EBX
+ PUSH ECX
+ PUSH EAX
+ PUSH EDX
+ MOV EAX, ESP
+ MOV EDX, offset[TreeViewFlags]
+ XOR ECX, ECX
+ MOV CL, 13
+ CALL MakeFlags
+ POP EDX
+ OR EAX, WS_VISIBLE or WS_CHILD or WS_TABSTOP
+ XCHG ECX, EAX
+ POP EAX
+ MOV EDX, offset[WC_TREEVIEW]
+ PUSH 1
+ {$IFDEF PACK_COMMANDACTIONS}
+ PUSH [TreeViewActions_Packed]
+ {$ELSE}
+ PUSH offset[TreeViewActions]
+ {$ENDIF}
+ CALL _NewCommonControl
+ MOV EBX, EAX
+ {$IFDEF PACK_COMMANDACTIONS}
+ MOV EDX, [EBX].TControl.fCommandActions
+ MOV [EDX].TCommandActionsObj.aClear, offset[ClearTreeView]
+ {$ENDIF}
+ MOV [EBX].TControl.PP.fCreateWndExt, offset[ApplyImageLists2Control]
+ MOV [EBX].TControl.fColor, clWindow
+ MOV EDX, offset[WndProcTreeView]
+ CALL TControl.AttachProc
+ ADD [EBX].TControl.fBoundsRect.Right, 150-64
+ ADD [EBX].TControl.fBoundsRect.Bottom, 200-64
+ MOV EAX, EBX
+ XOR EDX, EDX
+ MOV DL, 32
+ POP ECX // ImageListNormal
+ CALL TControl.SetImgListIdx
+ MOV EAX, EBX
+ XOR EDX, EDX
+ MOV ECX, [ImgListState]
+ CALL TControl.SetImgListIdx
+ MOV byte ptr [EBX].TControl.fLookTabKeys, 1
+ XCHG EAX, EBX
+ POP EBX
+end;
+
+function WndProcTabControl( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+asm //cmd //opd
+{$IFDEF OLD_ALIGN}
+ PUSH EBP
+ PUSH EBX
+ PUSH ESI
+ PUSH EDI
+ MOV EBX, EAX
+ CMP word ptr [EDX].TMsg.message, WM_NOTIFY
+ JNZ @@chk_WM_SIZE
+ MOV EDX, [EDX].TMsg.lParam
+//!!!
+ CMP word ptr [EDX].TNMHdr.code, TCN_SELCHANGING
+ JNZ @@chk_TCN_SELCHANGE
+ CALL TControl.GetCurIndex
+ MOV [EBX].TControl.fCurIndex, EAX
+ JMP @@ret_false
+@@chk_TCN_SELCHANGE:
+ CMP word ptr [EDX].TNMHdr.code, TCN_SELCHANGE
+ JNZ @@ret_false
+
+ CALL TControl.GetCurIndex
+ XCHG EDI, EAX
+ CMP EDI, [EBX].TControl.fCurIndex
+ PUSHFD // WasActive = ZF
+
+ MOV [EBX].TControl.FCurIndex, EDI
+
+ MOV EAX, EBX
+ CALL TControl.GetItemsCount
+ XCHG ESI, EAX // ESI := Self_.Count
+
+@@loo: DEC ESI
+ JS @@e_loo
+ MOV EDX, ESI
+ MOV EAX, EBX
+ CALL TControl.GetPages
+
+ CMP ESI, EDI
+ PUSH EAX
+ SETZ DL
+ CALL TControl.SetVisible
+ POP EAX
+ CMP ESI, EDI
+ JNE @@nx_loo
+ CALL TControl.BringToFront
+@@nx_loo:
+ JMP @@loo
+@@e_loo:
+ POPFD
+ JZ @@ret_false
+
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EBX].TControl.EV
+ MOV ECX, [EAX].TEvents.fOnSelChange.TMethod.Code
+ {$ELSE}
+ MOV ECX, [EBX].TControl.EV.fOnSelChange.TMethod.Code
+ {$ENDIF}
+ JECXZ @@ret_false
+ MOV EDX, EBX
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EAX].TEvents.fOnSelChange.TMethod.Data
+ {$ELSE}
+ MOV EAX, [EBX].TControl.EV.fOnSelChange.TMethod.Data
+ {$ENDIF}
+ CALL ECX
+ JMP @@ret_false
+@@chk_WM_SIZE:
+ CMP word ptr [EDX].TMsg.message, WM_SIZE
+ JNE @@ret_false
+ ADD ESP, -16
+ PUSH ESP
+ PUSH [EBX].TControl.fHandle
+ CALL Windows.GetClientRect
+ PUSH ESP
+ PUSH 0
+ PUSH TCM_ADJUSTRECT
+ PUSH EBX
+ CALL TControl.Perform
+ MOV EAX, EBX
+ CALL TControl.GetItemsCount
+ XCHG ESI, EAX
+@@loo2:
+ DEC ESI
+ JS @@e_loo2
+ MOV EDX, ESI
+ MOV EAX, EBX
+ CALL TControl.GetPages
+ MOV EDX, ESP
+ CALL TControl.SetBoundsRect
+ JMP @@loo2
+@@e_loo2:
+ ADD ESP, 16
+@@ret_false:
+ XOR EAX, EAX
+ POP EDI
+ POP ESI
+ POP EBX
+ POP EBP
+{$ELSE NEW_ALIGN}
+ PUSH EBX
+ MOV EBX, EAX
+ CMP word ptr [EDX].TMsg.message, WM_NOTIFY
+ JNZ @@chk_WM_SIZE
+ MOV EDX, [EDX].TMsg.lParam
+
+ CMP word ptr [EDX].TNMHdr.code, TCN_SELCHANGING
+ JNZ @@chk_TCN_SELCHANGE
+ CALL TControl.GetCurIndex
+ MOV [EBX].TControl.fCurIndex, EAX
+ JMP @@ret_false
+@@chk_TCN_SELCHANGE:
+ CMP word ptr [EDX].TNMHdr.code, TCN_SELCHANGE
+ JNZ @@ret_false
+ CALL TControl.GetCurIndex
+ MOV EDX, [EBX].TControl.fCurIndex
+ MOV [EBX].TControl.fCurIndex, EAX
+ CMP EAX, EDX
+ PUSHFD // WasActive = ZF
+ BT EDX,31
+ JBE @@00
+ MOV EAX, EBX
+ CALL TControl.GetPages
+ XOR EDX,EDX
+ CALL TControl.SetVisible
+@@00:
+ MOV EDX, [EBX].TControl.fCurIndex
+ MOV EAX, EBX
+ CALL TControl.GetPages
+ MOV DL,1
+ PUSH EAX
+ CALL TControl.SetVisible
+ POP EAX
+ CALL TControl.BringToFront
+ POPFD
+ JZ @@ret_false
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EBX].TControl.EV
+ MOV ECX, [EAX].TEvents.fOnSelChange.TMethod.Code
+ {$ELSE}
+ MOV ECX, [EBX].TControl.EV.fOnSelChange.TMethod.Code
+ {$ENDIF}
+ {$IFDEF NIL_EVENTS}
+ JECXZ @@ret_false
+ {$ENDIF}
+ MOV EDX, EBX
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EAX].TEvents.fOnSelChange.TMethod.Data
+ {$ELSE}
+ MOV EAX, [EBX].TControl.EV.fOnSelChange.TMethod.Data
+ {$ENDIF}
+ CALL ECX
+ JMP @@ret_false
+@@chk_WM_SIZE:
+ CMP word ptr [EDX].TMsg.message, WM_SIZE
+ JNE @@ret_false
+ SUB ESP, 10h
+ PUSH ESP
+ PUSH [EBX].TControl.fHandle
+ CALL Windows.GetClientRect
+ MOV EAX,[ESP].TRect.Right
+ MOV [EBX].TControl.fClientRight, AL
+ MOV EAX,[ESP].TRect.Bottom
+ MOV [EBX].TControl.fClientBottom, AL
+ PUSH ESP
+ PUSH 0
+ PUSH TCM_ADJUSTRECT
+ PUSH EBX
+ CALL TControl.Perform
+ POP EAX
+ MOV [EBX].TControl.fClientLeft, AL
+ POP EAX
+ MOV [EBX].TControl.fClientTop, AL
+ POP EAX
+ SUB [EBX].TControl.fClientRight, AL
+ POP EAX
+ SUB [EBX].TControl.fClientBottom, AL
+@@ret_false:
+ XOR EAX, EAX
+ POP EBX
+{$ENDIF}
+end;
+
+{$IFNDEF OLD_ALIGN}
+function NewTabEmpty( AParent: PControl; Options: TTabControlOptions;
+ ImgList: PImageList ): PControl;
+const lenf=high(TabControlFlags); //+++
+asm //cmd //opd
+ PUSH EBX
+ MOV EBX, EAX
+ PUSH ECX
+ PUSH EDX
+ MOV EAX, ESP
+ MOV EDX, offset[TabControlFlags]
+ XOR ECX, ECX
+ MOV CL, lenf
+ CALL MakeFlags
+ TEST byte ptr [ESP], 4
+ JZ @@0
+ OR EAX, WS_TABSTOP or TCS_FOCUSONBUTTONDOWN
+@@0: OR EAX, WS_CHILD or WS_CLIPSIBLINGS or WS_CLIPCHILDREN or WS_VISIBLE
+ XCHG ECX, EAX
+ XCHG EAX, EBX
+ MOV EDX, offset[WC_TABCONTROL]
+ PUSH 1
+ {$IFDEF PACK_COMMANDACTIONS}
+ PUSH [TabControlActions_Packed]
+ {$ELSE}
+ PUSH offset[TabControlActions]
+ {$ENDIF}
+ CALL _NewCommonControl
+ MOV EBX, EAX
+ POP ECX //Options
+ TEST ECX, 2 shl (tcoBorder - 1)
+ JNZ @@borderfixed
+ AND [EBX].TControl.fExStyle, not WS_EX_CLIENTEDGE
+@@borderfixed:
+ MOV EDX, offset[WndProcTabControl]
+ CALL TControl.AttachProc
+ ADD [EBX].TControl.fBoundsRect.Right, 100-64
+ ADD [EBX].TControl.fBoundsRect.Bottom, 100-64
+ POP ECX //ImgList
+ JECXZ @@2
+ XCHG EAX, ECX
+ CALL TImageList.GetHandle
+ PUSH EAX
+ PUSH 0
+ PUSH TCM_SETIMAGELIST
+ PUSH EBX
+ CALL TControl.Perform
+@@2:
+ MOV byte ptr [EBX].TControl.fLookTabKeys, 1
+ XCHG EAX, EBX
+ POP EBX
+end;
+{$ENDIF}
+
+{$IFNDEF NOT_USE_RICHEDIT}
+
+const RichEdit50W: array[0..11] of AnsiChar = ('R','i','c','h','E','d','i','t','5','0','W',#0 );
+function NewRichEdit( AParent: PControl; Options: TEditOptions ): PControl;
+const deltaChr = 24; // sizeof( TCharFormat2 ) - sizeof( RichEdit.TCharFormat );
+ deltaPar = sizeof( TParaFormat2 ) - sizeof( RichEdit.TParaFormat );
+asm
+ PUSHAD
+ CALL OleInit
+ TEST EAX, EAX
+ POPAD
+ JZ @@new1
+ MOV [RichEditIdx], 0
+ CALL NewRichEdit1
+ MOV byte ptr [EAX].TControl.DF.fCharFmtDeltaSz, deltaChr
+ MOV byte ptr [EAX].TControl.DF.fParaFmtDeltaSz, deltaPar
+ RET
+@@new1: CALL NewRichEdit1
+end;
+
+(*
+function WndProc_RE_LinkNotify( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+asm
+ CMP word ptr [EDX].TMsg.message, WM_NOTIFY
+ JNE @@ret_false
+ MOV EDX, [EDX].TMsg.lParam
+ CMP [EDX].TNMHdr.code, EN_LINK
+ JNE @@ret_false
+ PUSH EBX
+ PUSH EDX
+ XCHG EBX, EAX
+ XOR EAX, EAX
+ MOV [ECX], EAX
+ {$IFDEF UNICODE_CTRLS}
+ ADD ESP, -2040
+ {$ELSE}
+ ADD ESP, -1020
+ {$ENDIF}
+ PUSH EAX
+ PUSH ESP
+ PUSH [EDX].TENLink.chrg.cpMax
+ PUSH [EDX].TENLink.chrg.cpMin
+ PUSH ESP
+ PUSH 0
+ PUSH EM_GETTEXTRANGE
+ PUSH EBX
+ CALL TControl.Perform
+ LEA EAX, [EBX].TControl.fREUrl
+
+ POP EDX
+ POP ECX
+ DEC EDX
+ CMP ECX, EDX
+ POP ECX
+ MOV EDX, ESP
+ JLE @@1
+ CMP byte ptr [EDX+1], 0
+ JNZ @@1
+ // ñèñòåìà âåðíóëà òåêñò êàê unicode
+ {$IFDEF UNICODE_CTRLS}
+ CALL System.@WStrFromPWChar // TODO: not need ecx
+ {$ELSE not UNICODE_CTRLS}
+ {$IFDEF _D2}
+ CALL LStrFromPWChar
+ {$ELSE}
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX // TODO: fixme
+ {$ENDIF}
+ CALL System.@LStrFromPWChar
+ {$ENDIF}
+ {$ENDIF UNICODE_CTRLS}
+ JMP @@2
+@@1:
+ // ñèñòåìà âåðíóëà òåêñò êàê îáû÷íóþ ñòðîêó
+ {$IFDEF UNICODE_CTRLS}
+ CALL System.@WStrFromPChar
+ {$ELSE not UNICODE_CTRLS}
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX // TODO: fixme
+ {$ENDIF}
+ CALL System.@LStrFromPChar
+ {$ENDIF UNICODE_CTRLS}
+@@2:
+ {$IFDEF UNICODE_CTRLS}
+ ADD ESP, 2044
+ {$ELSE not UNICODE_CTRLS}
+ ADD ESP, 1024
+ {$ENDIF UNICODE_CTRLS}
+ POP EDX
+ MOV ECX, [EDX].TENLink.msg
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EBX].TControl.EV
+ LEA EAX, [EAX].TEvents.fOnREOverURL
+ {$ELSE}
+ LEA EAX, [EBX].TControl.EV.fOnREOverURL
+ {$ENDIF}
+ CMP ECX, WM_MOUSEMOVE
+ JE @@Url_event
+ //LEA EAX, [EBX].TControl.EV.fOnREUrlClick
+ ADD EAX, 8
+ CMP ECX, WM_LBUTTONDOWN
+ JE @@Url_Event
+ CMP ECX, WM_RBUTTONDOWN
+ JNE @@after_Url_event
+@@Url_event:
+ MOV ECX, [EAX].TMethod.Code
+ {$IFDEF NIL_EVENTS}
+ JECXZ @@after_Url_event
+ {$ENDIF}
+ MOV EDX, EBX
+ MOV EAX, [EAX].TMethod.Data
+ CALL ECX
+@@after_Url_event:
+ POP EBX
+ MOV AL, 1
+ RET
+@@ret_false:
+ XOR EAX, EAX
+end;
+*)
+{$ENDIF NOT_USE_RICHEDIT}
+
+function OleInit: Boolean;
+asm
+ MOV ECX, [OleInitCount]
+ INC ECX
+ LOOP @@init1
+ PUSH ECX
+ CALL OleInitialize
+ TEST EAX, EAX
+ MOV AL, 0
+ JNZ @@exit
+@@init1:
+ INC [OleInitCount]
+ MOV AL, 1
+@@exit:
+end;
+
+procedure OleUnInit;
+asm
+ MOV ECX, [OleInitCount]
+ JECXZ @@exit
+ DEC [OleInitCount]
+ JNZ @@exit
+ CALL OleUninitialize
+@@exit:
+end;
+
+procedure TControl.Init;
+const
+ IniStyle = WS_VISIBLE or WS_CLIPCHILDREN or WS_CLIPSIBLINGS or
+ WS_CAPTION or WS_SYSMENU or WS_MINIMIZEBOX or WS_MAXIMIZEBOX or
+ WS_BORDER or WS_THICKFRAME;
+asm //cmd //opd
+ PUSH EBX
+ PUSH EDI
+ MOV EBX, EAX
+ {$IFDEF CALL_INHERITED}
+ CALL TObj.Init // for now, TObj.Init do nothing for Delphi 4 and higher
+ {$ENDIF}
+ {$IFDEF USE_GRAPHCTLS}
+ MOV [EBX].PP.fDoInvalidate, offset[InvalidateWindowed]
+ {$ENDIF}
+
+ {$IFDEF OLD_EVENTS_MODEL}
+ MOV EAX, offset WndProcDummy
+ LEA EDI, [EBX].PP.fPass2DefProc
+ STOSD // fPass2DefProc := WndProcDummy
+ STOSD // fOnDynHandlers := WndProcDummy
+ STOSD // fWndProcKeybd := WndProcDummy
+ STOSD // fControlClick := WndProcDummy - similar to DefWindowProc
+ STOSD // fAutoSize := WndProcDummy - similar to DefWindowProc
+ LEA EDI, [EBX].PP.fWndProcResizeFlicks
+ STOSD
+
+ MOV [EBX].PP.fWndFunc, offset WndFunc
+ {$ELSE NEW_EVENTS_MODEL}
+ {$IFDEF EVENTS_DYNAMIC}
+ XOR ECX, ECX
+ CMP DWORD PTR[EmptyEvents].TEvents.fOnMessage.TMethod.Code, ECX
+ JNZ @@a2
+ MOV CL, idx_LastEvent+1
+ @@a1: MOVZX EDX, byte ptr [ECX+InitEventsTable-1]
+ AND DL, $0F
+ MOV EDX, dword ptr [EDX*4 + DummyProcTable]
+ MOV dword ptr [EmptyEvents+ECX*8-8], EDX
+ LOOP @@a1
+ @@a2:
+ MOV EDX, offset[EmptyEvents]
+ MOV [EBX].EV, EDX
+ MOV CL, idx_LastProc - idx_LastEvent
+ @@a3:
+ MOVZX EDX, byte ptr [ECX+InitEventsTable-1]
+ SHR EDX, 4
+ MOV EDX, dword ptr [EDX*4 + DummyProcTable]
+ MOV dword ptr [EBX+ECX*4-4].PP, EDX
+ LOOP @@a3
+ {$ELSE}
+ XOR ECX, ECX
+ MOV CL, idx_LastEvent+1
+ @@1:
+ MOVZX EDX, byte ptr [ECX+InitEventsTable-1]
+ PUSH EDX
+ AND DL, $0F
+ MOV EDX, [EDX*4 + DummyProcTable]
+ MOV dword ptr [EBX+ECX*8-8].EV, EDX
+ POP EDX
+ SHR EDX, 4
+ CMP ECX, idx_LastProc - idx_LastEvent + 1
+ JGE @@2
+
+ MOV EDX, [EDX*4 + DummyProcTable]
+ MOV dword ptr [EBX+ECX*4-4].PP, EDX
+ @@2:
+ LOOP @@1
+ {$ENDIF}
+ {$ENDIF NEW_EVENTS_MODEL}
+
+ {$IFDEF COMMANDACTIONS_OBJ} //--- moved to _NewWindowed
+ //---- MOV EDX, [EBX].fCommandActions
+ //---- MOV [EDX].TCommandActionsObj.aClear, offset[ClearText]
+ {$ELSE}
+ //---- MOV [EBX].fCommandActions.aClear, offset[ClearText]
+ {$ENDIF}
+ {$IFDEF USE_FLAGS}
+ {$ELSE}
+ INC [EBX].fWindowed
+ {$ENDIF}
+ MOV [EBX].fColor, clBtnFace
+ {$IFDEF SYSTEMCOLORS_DELPHI}
+ MOV [EBX].fTextColor, clWindowText and $FF
+ {$ELSE}
+ MOV [EBX].fTextColor, clWindowText
+ {$ENDIF}
+
+ MOV byte ptr [EBX].fMargin, 2
+ OR dword ptr [EBX].fCtl3D_child, 3
+
+ {$IFDEF SMALLEST_CODE}
+ {$ELSE}
+ DEC byte ptr [EBX].fAlphaBlend // has no effect until AlphaBlend changed
+ {$ENDIF}
+ MOV byte ptr[EBX].fClsStyle, CS_OWNDC
+ MOV [EBX].fStyle, IniStyle
+ INC dword ptr[EBX].fExStyle+2
+ {$IFDEF USE_FLAGS}
+ //AND [EBX].fStyle.f3_Style, not(1 shl F3_Disabled)
+ OR [EBX].fStyle.f3_Style, (1 shl F3_Visible)
+ {$ELSE}
+ DEC WORD PTR [EBX].fEnabled
+ {$ENDIF}
+
+ LEA EDI, [EBX].fDynHandlers
+ MOV EBX, offset[NewList]
+ CALL EBX
+ STOSD
+ CALL EBX
+ STOSD
+
+ POP EDI
+ POP EBX
+end;
+
+procedure CallTControlInit( Ctl: PControl );
+begin
+ Ctl.Init;
+end;
+
+procedure TControl.InitParented( AParent: PControl );
+const IStyle = WS_VISIBLE or WS_CLIPCHILDREN or WS_CLIPSIBLINGS or
+ WS_CAPTION or WS_SYSMENU or WS_MINIMIZEBOX or WS_MAXIMIZEBOX or
+ WS_BORDER or WS_THICKFRAME;
+ IExStyle = WS_EX_CONTROLPARENT;
+ IClsStyle = CS_OWNDC;
+ int_IDC_ARROW = integer( IDC_ARROW );
+asm
+ PUSH EAX
+ PUSH EDX
+ //CALL CallTControlInit
+ mov EDX, [EAX]
+ call dword ptr [EDX]
+
+ POP EDX
+ POP EAX
+ TEST EDX, EDX
+ JZ @@0
+ MOV ECX, [EDX].fColor
+ MOV [EAX].fColor, ECX
+@@0:
+ CALL SetParent
+end;
+
+destructor TControl.Destroy;
+asm
+ PUSH EBX
+ PUSH ESI
+ MOV EBX, EAX
+ CALL TControl.ParentForm
+ XCHG ECX, EAX
+ JECXZ @@cur_ctl_removed
+ MOV EDX, EBX
+ XOR EDX, [ECX].TControl.DF.fCurrentControl
+ JNE @@cur_ctl_removed
+ MOV [ECX].TControl.DF.fCurrentControl, EDX
+@@cur_ctl_removed:
+
+ MOV ECX, [EBX].fHandle
+ JECXZ @@wndhidden
+ PUSH SW_HIDE
+ PUSH ECX
+ CALL ShowWindow
+@@wndhidden:
+
+ MOV EAX, EBX
+ CALL Final
+ {$IFDEF USE_AUTOFREE4CHILDREN}
+ {$ELSE}
+ MOV EAX, EBX
+ CALL DestroyChildren
+ {$ENDIF}
+
+ {$IFDEF USE_FLAGS}
+ BTS DWORD PTR [EBX].fFlagsG2, G2_Destroying
+ JC @@destroyed
+ {$ELSE}
+ XOR ECX, ECX
+ CMP [EBX].fDestroying, CL
+ JNZ @@destroyed
+ INC [EBX].fDestroying
+ {$ENDIF USE_FLAGS}
+
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ XOR EAX, EAX
+ XCHG EAX, [EBX].fCanvas
+ CALL TObj.RefDec
+ {$ELSE}
+ PUSH EBX
+ LEA ESI, [EBX].fFont
+ MOV BL, 3
+@@free_font_brush_canvas:
+ XOR ECX, ECX
+ XCHG ECX, [ESI]
+ LODSD
+ XCHG EAX, ECX
+ CALL TObj.RefDec
+ DEC BL
+ JNZ @@free_font_brush_canvas
+ POP EBX
+ {$ENDIF}
+
+ MOV EAX, [EBX].fCustomObj
+ CALL TObj.RefDec
+
+ MOV EAX, [EBX].fHandle
+ TEST EAX, EAX
+ JZ @@free_fields
+
+ {$IFNDEF USE_AUTOFREE4CONTROLS}
+ {$IFNDEF NEW_MENU_ACCELL}
+ XOR ECX, ECX
+ XCHG ECX, [EBX].fAccelTable
+ JECXZ @@accelTable_destroyed
+ PUSH ECX
+ CALL DestroyAcceleratorTable
+@@accelTable_destroyed:
+ {$ENDIF}
+ MOV EAX, [EBX].fMenuObj
+ CALL TObj.RefDec
+@@destroy_img_list:
+ XOR EAX, EAX
+ XCHG EAX, [EBX].fImageList
+ TEST EAX, EAX
+ JZ @@img_list_destroyed
+ CALL TObj.RefDec
+ JMP @@destroy_img_list
+@@img_list_destroyed:
+ {$ENDIF}
+
+ MOV ECX, [EBX].DF.fIcon
+ JECXZ @@icoremoved
+ INC ECX
+ JZ @@icoremoved
+ {$IFDEF USE_FLAGS}
+ TEST [EBX].fFlagsG1, (1 shl G1_IconShared)
+ JNZ @@icoremoved
+ {$ELSE}
+ CMP [EBX].fIconShared, 0
+ JNZ @@icoremoved
+ {$ENDIF USE_FLAGS}
+ DEC ECX
+ PUSH ECX
+ CALL DestroyIcon
+@@icoremoved:
+
+ PUSH [EBX].fHandle
+ CALL IsWindow
+ TEST EAX, EAX
+ JZ @@destroy2
+ (* -- moved to WM_NCDESTROY handler - VK + Alexey Kirov
+ {$IFDEF USE_PROP}
+ PUSH offset[ID_SELF] //* Remarked By M.Gerasimov
+ PUSH [EBX].fHandle //* unremarked to prevent problems with progress bar
+ CALL RemoveProp
+ {$ELSE}
+ PUSH 0
+ PUSH GWL_USERDATA
+ PUSH [EBX].fHandle
+ CALL SetWindowLong
+ {$ENDIF}
+ *)
+ {$IFDEF USE_fNCDestroyed}
+ CMP [EBX].fNCDestroyed, 0
+ JNZ @@destroy2
+ {$ENDIF USE_fNCDestroyed}
+ PUSH [EBX].fHandle
+ CALL DestroyWindow
+@@destroy2:
+ XOR EAX, EAX
+ MOV [EBX].fHandle, EAX
+
+@@free_fields:
+ PUSH 0
+ {$IFDEF USE_FLAGS}
+ TEST [EBX].fFlagsG6, 1 shl G6_CtlClassNameChg
+ JZ @@notFreeCtlClsName
+ {$ELSE}
+ MOVZX ECX, [EBX].fCtlClsNameChg
+ JECXZ @@notFreeCtlClsName
+ {$ENDIF}
+ PUSH [EBX].fControlClassName
+@@notFreeCtlClsName:
+ MOV ECX, [EBX].fCustomData
+ JECXZ @@notFreeCustomData
+ PUSH ECX
+@@notFreeCustomData:
+@@FreeFieldsLoop:
+ POP ECX
+ JECXZ @@endFreeFieldsLoop
+ XCHG EAX, ECX
+ CALL System.@FreeMem
+ JMP @@FreeFieldsLoop
+@@endFreeFieldsLoop:
+
+ XOR ECX, ECX
+ XCHG ECX, [EBX].fTmpBrush
+ JECXZ @@tmpBrush_deleted
+ PUSH ECX
+ CALL DeleteObject
+@@tmpBrush_deleted:
+
+ MOV ECX, [EBX].fParent
+ JECXZ @@removed_from_parent
+ CMP [ECX].DF.fCurrentControl, EBX
+ JNE @@removefromParent
+ XOR EAX, EAX
+ MOV [ECX].DF.fCurrentControl, EAX
+@@removefromParent:
+ {$IFDEF USE_AUTOFREE4CHILDREN}
+ PUSH ECX
+ {$ENDIF}
+ MOV EAX, [ECX].fChildren
+ MOV EDX, EBX
+ CALL TList.Remove
+ {$IFDEF USE_AUTOFREE4CHILDREN}
+ POP EAX
+ MOV EDX, EBX
+ CALL TControl.RemoveFromAutoFree
+ {$ENDIF}
+@@removed_from_parent:
+
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ LEA ESI, [EBX].fDynHandlers
+ LODSD
+ CALL TObj.RefDec
+ LODSD // fChildren
+ CALL TObj.RefDec
+ {$ELSE}
+ PUSH EBX
+ LEA ESI, [EBX].fDynHandlers
+ MOV BL, 5
+@@freeloo:
+ LODSD
+ CALL TObj.RefDec
+ DEC BL
+ JNZ @@freeloo
+ POP EBX
+ {$ENDIF}
+
+ LEA EAX, [EBX].fCaption
+ {$IFDEF UNICODE_CTRLS}
+ CALL System.@WStrClr
+ {$ELSE}
+ CALL System.@LStrClr
+ {$ENDIF}
+ XCHG EAX, EBX
+ CALL TObj.Destroy
+@@destroyed:
+ POP ESI
+ POP EBX
+end;
+
+procedure TControl.SetEnabled( Value: Boolean );
+asm
+ PUSH EBX
+ MOV EBX, EAX
+ MOVZX EDX, DL
+ PUSH EDX
+ CALL GetEnabled
+ POP EDX
+ CMP AL, DL
+ JZ @@exit
+ {$IFDEF USE_FLAGS}
+ {$ELSE}
+ MOV [EBX].fEnabled, DL
+ {$ENDIF USE_FLAGS}
+ TEST EDX, EDX
+ JNZ @@andnot
+ OR [EBX].fStyle.f3_Style, (1 shl F3_Disabled)
+ JMP @@1
+@@andnot:
+ AND [EBX].fStyle.f3_Style, not(1 shl F3_Disabled)
+@@1:
+ MOV ECX, [EBX].fHandle
+ JECXZ @@2
+
+ PUSH EDX
+ PUSH ECX
+ CALL EnableWindow
+
+@@2:
+ XCHG EAX, EBX
+ CALL Invalidate
+
+@@exit:
+ POP EBX
+end;
+
+{function TControl.GetParentWindow: HWnd;
+asm
+ MOV ECX, [EAX].fHandle
+ JECXZ @@1
+ PUSH EAX
+ PUSH GW_OWNER
+ PUSH EAX
+ CALL GetWindow
+ POP ECX
+ TEST EAX, EAX
+ JZ @@0
+ RET
+@@0: XCHG EAX, ECX
+@@1:
+ MOV EAX, [EAX].fParent
+ TEST EAX, EAX
+ JNZ TControl.GetWindowHandle
+end;}
+
+function WndProcMouse(Self_: PControl; var Msg: TMsg; var Rslt: Integer): Boolean;
+asm
+ PUSH EBX
+ PUSH ESI
+ XCHG EBX, EAX
+
+ XOR ECX, ECX // Rslt not used. ECX <= Result = 0
+ MOV EAX, [EDX].TMsg.message
+ SUB AH, WM_MOUSEFIRST shr 8
+ CMP EAX, $20A - WM_MOUSEFIRST //WM_MOUSELAST - WM_MOUSEFIRST
+ JA @@exit
+
+ PUSH dword ptr [EDX].TMsg.lParam // prepare X, Y
+
+ PUSHAD
+ PUSH VK_MENU
+ CALL GetKeyState
+ ADD EAX, EAX
+ POPAD
+
+ XCHG EAX, EDX
+ MOV EAX, [EAX].TMsg.wParam
+
+ JNC @@noset_MKALT
+ {$IFDEF PARANOIA} DB $0C, MK_ALT {$ELSE} OR AL, MK_ALT {$ENDIF}
+@@noset_MKALT:
+
+ PUSH EAX // prepare Shift
+
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EBX].TControl.EV
+ LEA ESI, [EAX].TEvents.fOnMouseDown
+ {$ELSE}
+ LEA ESI, [EBX].TControl.EV.fOnMouseDown
+ {$ENDIF}
+ CALL dword ptr [EDX*4 + @@jump_table]
+
+@@call_evnt:
+
+ PUSH ECX // prepare Button, StopHandling
+ MOV ECX, ESP // ECX = @MouseData
+
+ {$IFDEF NIL_EVENTS}
+ CMP word ptr [ESI].TMethod.Code+2, 0
+ JZ @@after_call
+ {$ENDIF}
+
+ MOV EDX, EBX // EDX = Self_
+ MOV EAX, [ESI].TMethod.Data // EAX = Target_
+ CALL dword ptr [ESI].TMethod.Code
+
+@@after_call:
+ POP ECX
+ POP EDX
+ POP EDX
+ MOV CL, CH // Result := StopHandling
+
+@@exit:
+ XCHG EAX, ECX
+ POP ESI
+ POP EBX
+ RET
+
+@@jump_table:
+ DD Offset[@@MMove],Offset[@@LDown],Offset[@@LUp],Offset[@@LDblClk]
+ DD Offset[@@RDown],Offset[@@RUp],Offset[@@RDblClk]
+ DD Offset[@@MDown],Offset[@@MUp],Offset[@@MDblClk],Offset[@@MWheel]
+
+@@MDown: INC ECX
+@@RDown: INC ECX
+@@LDown: INC ECX
+ RET
+
+@@MUp: INC ECX
+@@RUp: INC ECX
+@@LUp: INC ECX
+ LODSD
+ LODSD
+ RET
+
+@@MMove: ADD ESI, 16
+ RET
+
+@@MDblClk: INC ECX
+@@RDblClk: INC ECX
+@@LDblClk: INC ECX
+ ADD ESI, 24
+ RET
+
+@@MWheel:ADD ESI, 32
+end;
+
+{$IFnDEF USE_GRAPHCTLS}
+{$IFnDEF NEW_MODAL}
+{$IFnDEF USE_MDI}
+function TControl.WndProc( var Msg: TMsg ): Integer;
+asm //cmd //opd
+ PUSH EBX
+ PUSH ESI
+ PUSH EDI
+ PUSH EBP
+ //MOV ESI, EAX
+ XCHG ESI, EAX
+ MOV EDI, EDX
+ //CALL TControl.RefInc
+ MOV EBP, [ESI].TControl.PP.fPass2DefProc
+
+ XOR EAX, EAX
+ CMP EAX, [EDI].TMsg.hWnd
+ JE @@1
+ CMP EAX, [ESI].TControl.fHandle
+ JNE @@1
+ {$IFDEF USE_GRAPHCTLS}
+ {$IFDEF USE_FLAGS}
+ TEST [ESI].TControl.fFlagsG6, 1 shl G6_GraphicCtl
+ {$ELSE}
+ CMP [ESI].TControl.fWindowed, AL
+ {$ENDIF}
+ JNE @@1
+ {$ENDIF}
+ MOV EAX, [EDI].TMsg.hWnd
+ MOV [ESI].TControl.fHandle, EAX
+@@1:
+ XOR eax, eax
+
+ CMP [AppletRunning], AL
+ JZ @@dyn2
+ MOV ECX, [Applet]
+ JECXZ @@dyn2
+ CMP ECX, ESI
+ JE @@dyn2
+
+ CALL @@onmess
+
+@@dyn2: MOV ECX, ESI
+ CALL @@onmess
+
+ MOV EBX, [ESI].TControl.PP.fOnDynHandlers
+ MOV EAX, ESI
+ CALL @@callonmes
+
+//**********************************************************
+ MOVZX EAX, word ptr [EDI].TMsg.message
+ CMP AX, WM_CLOSE
+ JNZ @@chk_WM_DESTROY
+
+ CMP ESI, [Applet]
+ JZ @@postquit
+ MOV EAX, ESI
+ CALL IsMainWindow
+ TEST AL, AL
+ JZ @@calldef
+@@postquit:
+ PUSH 0
+ CALL PostQuitMessage
+ MOV byte ptr [AppletTerminated], 1
+ JMP @@calldef
+//********************************************************** Added By M.Gerasimov
+@@chk_WM_DESTROY:
+ {$IFnDEF SMALLER_CODE}
+ MOV EDX, [EDI].TMsg.hWnd
+ {$ENDIF SMALLER_CODE}
+ CMP AX, WM_DESTROY
+ JNE @@chk_WM_NCDESTROY
+
+ {$IFnDEF SMALLER_CODE}
+ CMP EDX, [ESI].TControl.fHandle
+ JNE @@chk_WM_NCDESTROY
+ {$ENDIF SMALLER_CODE}
+
+ {$IFDEF USE_FLAGS}
+ OR [ESI].TControl.fFlagsG2, (1 shl G2_BeginDestroying)
+ {$ELSE}
+ MOV [ESI].TControl.fBeginDestroying, AL
+ {$ENDIF}
+ JMP @@calldef
+//**********************************************************
+@@chk_WM_NCDESTROY:
+ CMP AX, WM_NCDESTROY
+ JNE @@chk_WM_SIZE // @@chk_CM_RELEASE
+//********************************************************** Added By M.Gerasimov
+ {$IFnDEF SMALLER_CODE}
+ CMP EDX, [ESI].TControl.fHandle
+ JNE @@chk_WM_SIZE
+ {$ENDIF SMALLER_CODE}
+
+ {$IFDEF USE_PROP}
+ PUSH offset[ID_SELF]
+ PUSH [ESI].fHandle
+ CALL RemoveProp
+ {$ELSE}
+ PUSH 0
+ PUSH GWL_USERDATA
+ PUSH [ESI].fHandle
+ CALL SetWindowLong
+ {$ENDIF}
+ JMP @@calldef
+//**********************************************************
+@@return0:
+ XOR EAX, EAX
+ JMP @@exit // WM_NCDESTROY and CM_RELEASE
+ // is not a subject to pass it
+ // to fPass2DefProc
+@@onmess:
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [ECX].TControl.EV
+ MOV EBX, [EAX].TEvents.fOnMessage.TMethod.Code
+ MOV EAX, [EAX].TEvents.fOnMessage.TMethod.Data
+ {$ELSE}
+ MOV EAX, [ECX].TControl.EV.fOnMessage.TMethod.Data
+ MOV EBX, [ECX].TControl.EV.fOnMessage.TMethod.Code
+ {$ENDIF}
+@@callonmes:
+ {$IFDEF NIL_EVENTS}
+ TEST EBX, EBX
+ JZ @@ret
+ {$ENDIF}
+@@onmess1:
+ PUSH 0
+
+ MOV EDX, EDI
+ MOV ECX, ESP
+ CALL EBX
+ TEST AL, AL
+
+ POP EAX
+ JZ @@ret
+ POP EDX // pop retaddr
+ JMP @@pass2defproc
+
+//**************************************************************
+@@chk_WM_SIZE:
+ CMP AX, WM_SIZE
+ JNE @@chk_WM_SYSCOMMAND //@@chk_WM_SHOWWINDOW
+
+ MOV EDX, EDI
+ MOV EAX, ESI
+ CALL TControl.CallDefWndProc
+ PUSH EAX
+
+ {$IFDEF OLD_ALIGN}
+ {$IFDEF USE_FLAGS}
+ TEST [ESI].TControl.fFlagsG3, (1 shl G3_IsForm)
+ {$ELSE}
+ CMP [ESI].TControl.fIsForm, 0
+ {$ENDIF}
+ JNZ @@doGlobalAlignSelf
+ MOV EAX, [ESI].TControl.fParent
+ CALL dword ptr [Global_Align]
+@@doGlobalAlignSelf:
+ {$ENDIF}
+ MOV EAX, ESI
+ CALL dword ptr [Global_Align]
+ JMP @@popeax_exit // fPass2DefProc not needed, CallDefWndProc already called
+
+//**************************************************************
+@@chk_WM_SYSCOMMAND:
+ CMP AX, WM_SYSCOMMAND
+ JNE @@chk_WM_SETFOCUS
+
+ MOV EAX, [EDI].TMsg.wParam
+ {$IFDEF PARANOIA} DB $24, $F0 {$ELSE} AND AL, $F0 {$ENDIF}
+ CMP AX, SC_MINIMIZE
+ JNE @@calldef
+
+ MOV EAX, ESI
+ CALL TControl.IsMainWindow
+ TEST AL, AL
+ JZ @@calldef
+
+ CMP ESI, [Applet]
+ JE @@calldef
+
+ PUSH 0
+ PUSH SC_MINIMIZE
+ PUSH WM_SYSCOMMAND
+ MOV EAX, [Applet]
+ PUSH [EAX].TControl.fHandle
+ CALL PostMessage
+@@ret_0:
+ JMP @@0pass2defproc
+
+//***************************************************************
+@@chk_WM_SETFOCUS:
+ CMP AX, WM_SETFOCUS
+ JNE @@chk_WM_CTLCOLOR //@@chk_WM_SETCURSOR
+
+ MOV EAX, ESI
+ CALL TControl.DoSetFocus
+ TEST AL, AL
+ JZ @@0pass2defproc
+
+ INC [ESI].TControl.fClickDisabled
+
+ MOV EAX, ESI
+ MOV EDX, EDI
+ CALL TControl.CallDefWndProc
+
+ DEC [ESI].TControl.fClickDisabled
+ JMP @@exit
+
+//**************************************************************
+@@chk_WM_CTLCOLOR:
+ MOV EDX, EAX
+ SUB DX, WM_CTLCOLORMSGBOX
+ CMP DX, WM_CTLCOLORSTATIC-WM_CTLCOLORMSGBOX
+ JA @@chk_WM_COMMAND
+
+ PUSH [EDI].TMsg.lParam
+ PUSH [EDI].TMsg.wParam
+ ADD AX, CN_BASE //+WM_CTLCOLORMSGBOX
+ PUSH EAX
+ PUSH [EDI].TMsg.lParam
+ CALL SendMessage
+ JMP @@pass2defproc
+
+//**************************************************************
+@@chk_WM_COMMAND:
+ CMP AX, WM_COMMAND
+ JNE @@chk_WM_KEY
+
+ {$IFDEF USE_PROP}
+ PUSH offset[ID_SELF]
+ PUSH [EDI].TMsg.lParam
+ CALL GetProp
+ {$ELSE}
+ PUSH GWL_USERDATA
+ PUSH [EDI].TMsg.lParam
+ CALL GetWindowLong
+ {$ENDIF}
+ TEST EAX, EAX
+ JZ @@calldef
+
+ PUSH [EDI].TMsg.lParam
+ PUSH [EDI].TMsg.wParam
+ PUSH CM_COMMAND
+ PUSH [EDI].TMsg.lParam
+ CALL SendMessage
+ JMP @@pass2defproc
+
+//**************************************************************
+@@chk_WM_KEY:
+ MOV EDX, EAX
+ SUB DX, WM_KEYFIRST
+ CMP DX, WM_KEYLAST-WM_KEYFIRST
+ JA @@calldef //@@chk_CM_EXECPROC
+ {$IFDEF KEY_PREVIEW}
+ {$IFDEF USE_FLAGS}
+ TEST [ESI].TControl.fFlagsG4, 1 shl G4_Pushed
+ {$ELSE}
+ CMP [ESI].TControl.fKeyPreviewing, 0
+ {$ENDIF}
+ JNE @@in_focus
+ {$ENDIF KEY_PREVIEW}
+
+ CALL GetFocus
+ //--- CMP EAX, [ESI].TControl.fFocusHandle
+ //--- JE @@in_focus
+ CMP EAX, [ESI].TControl.fHandle
+ {$IFDEF USE_GRAPHCTLS}
+ JE @@in_focus
+ CMP [ESI].fWindowed, 0
+ {$ENDIF}
+ JNE @@0pass2defproc
+
+@@in_focus:
+ {$IFDEF KEY_PREVIEW}
+ {$IFDEF USE_FLAGS}
+ AND [ESI].TControl.fFlagsG4, not(1 shl G4_Pushed)
+ {$ELSE}
+ MOV [ESI].TControl.fKeyPreviewing, 0
+ {$ENDIF}
+ {$ENDIF KEY_PREVIEW}
+ PUSH EAX
+
+ MOV ECX, ESP
+ MOV EDX, EDI
+ MOV EAX, ESI
+ CALL dword ptr [fGlobalProcKeybd]
+ TEST AL, AL
+ JNZ @@to_exit
+
+ MOV ECX, ESP
+ MOV EDX, EDI
+ MOV EAX, ESI
+ CALL [ESI].PP.fWndProcKeybd
+ TEST AL, AL
+@@to_exit:
+ POP EAX
+ JNZ @@pass2defproc
+
+ PUSH VK_CONTROL
+ CALL GetKeyState
+ XCHG EBX, EAX
+ PUSH VK_MENU
+ CALL GetKeyState
+ OR EAX, EBX
+ JS @@calldef
+
+ CMP word ptr [EDI].TMsg.message, WM_CHAR
+ JNE @@to_fGotoControl
+
+ CMP byte ptr [EDI].TMsg.wParam, 9
+ JE @@clear_wParam
+ JMP @@calldef
+
+@@to_fGotoControl:
+ MOV EAX, ESI
+ CALL TControl.ParentForm
+ TEST EAX, EAX
+ JZ @@calldef
+
+ MOV ECX, [EAX].PP.fGotoControl
+ {$IFDEF NIL_EVENTS}
+ JECXZ @@calldef
+ {$ENDIF}
+
+ MOV EBX, ECX
+ CMP [EDI].TMsg.message, WM_KEYDOWN
+ SETNE CL
+ CMP [EDI].TMsg.message, WM_SYSKEYDOWN
+ SETNE CH
+ AND CL, CH
+ MOV EDX, [EDI].TMsg.wParam
+ MOV EAX, ESI
+ CALL EBX
+ TEST AL, AL
+ JZ @@calldef
+
+@@clear_wParam:
+ XOR EAX, EAX
+ MOV [EDI].TMsg.wParam, EAX
+ JMP @@pass2defproc
+
+@@calldef:
+ MOV EAX, ESI
+ MOV EDX, EDI
+ CALL TControl.CallDefWndProc
+ JMP @@exit
+
+@@0pass2defproc:
+ XOR EAX, EAX
+@@pass2defproc:
+ PUSH EAX
+@@1pass2defproc:
+ CMP [AppletTerminated], 0 //
+ JNZ @@popeax_exit // uncommented 25-Oct-2003
+ {$IFDEF USE_fNCDestroyed}
+ CMP [ESI].fNCDestroyed, 0 //
+ JNZ @@popeax_exit //
+ {$ENDIF USE_fNCDestroyed}
+
+ MOV ECX, ESP
+ MOV EAX, ESI
+ MOV EDX, EDI
+ CALL EBP
+@@popeax_exit:
+ POP EAX
+
+@@exit:
+ {XCHG ESI, EAX
+ CALL TControl.RefDec
+ XCHG EAX, ESI}
+
+ POP EBP
+ POP EDI
+ POP ESI
+ POP EBX
+@@ret:
+end;
+{$ENDIF no_USE_MDI}
+{$ENDIF no NEW_MODAL}
+{$ENDIF no USE_GRAPHCTLS}
+
+procedure TControl.SetClsStyle( Value: DWord );
+asm //cmd //opd
+ CMP EDX, [EAX].TControl.fClsStyle
+ JE @@exit
+ MOV [EAX].TControl.fClsStyle, EDX
+ MOV ECX, [EAX].TControl.fHandle
+ JECXZ @@exit
+ PUSH EDX
+ PUSH GCL_STYLE
+ PUSH ECX
+ CALL SetClassLong
+@@exit:
+end;
+
+procedure TControl.SetStyle( Value: DWord );
+const SWP_FLAGS = SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE or
+ SWP_NOZORDER or SWP_FRAMECHANGED;
+asm
+ CMP EDX, [EAX].fStyle
+ JZ @@exit
+ MOV [EAX].fStyle, EDX
+ MOV ECX, [EAX].fHandle
+ JECXZ @@exit
+
+ PUSH EAX
+
+ PUSH SWP_FLAGS
+ XOR EAX, EAX
+ PUSH EAX
+ PUSH EAX
+ PUSH EAX
+ PUSH EAX
+ PUSH EAX
+ PUSH ECX
+
+ PUSH EDX
+ PUSH GWL_STYLE
+ PUSH ECX
+ CALL SetWindowLong
+
+ CALL SetWindowPos
+
+ POP EAX
+ CALL Invalidate
+@@exit:
+end;
+
+procedure TControl.SetExStyle( Value: DWord );
+const SWP_FLAGS = SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE or
+ SWP_NOZORDER or SWP_FRAMECHANGED;
+asm
+ CMP EDX, [EAX].fExStyle
+ JZ @@exit
+ MOV [EAX].fExStyle, EDX
+ MOV ECX, [EAX].fHandle
+ JECXZ @@exit
+
+ PUSH EAX
+
+ PUSH SWP_FLAGS
+ XOR EAX, EAX
+ PUSH EAX
+ PUSH EAX
+ PUSH EAX
+ PUSH EAX
+ PUSH EAX
+ PUSH ECX
+
+ PUSH EDX
+ PUSH GWL_EXSTYLE
+ PUSH ECX
+ CALL SetWindowLong
+
+ CALL SetWindowPos
+
+ POP EAX
+ CALL Invalidate
+@@exit:
+end;
+
+procedure TControl.SetCursor( Value: HCursor );
+asm //cmd //opd
+ PUSH EBX
+ MOV EBX, EAX
+ PUSH EDX
+ LEA EDX, WndProcSetCursor
+ CALL TControl.AttachProc
+ POP EDX
+
+ CMP EDX, [EBX].TControl.fCursor
+ JE @@exit
+ MOV [EBX].TControl.fCursor, EDX
+ MOV ECX, [EBX].TControl.fHandle
+ JECXZ @@exit
+ TEST EDX, EDX //YS
+ JE @@exit //YS
+ MOV ECX, [ScreenCursor]
+ INC ECX
+ LOOP @@exit
+
+ PUSH EDX
+ PUSH EAX
+ PUSH EAX
+ PUSH ESP
+ CALL GetCursorPos
+ MOV EDX, ESP
+ MOV ECX, EDX
+ MOV EAX, EBX
+ CALL Screen2Client
+ ADD ESP, -16
+ MOV EDX, ESP
+ MOV EAX, EBX
+ CALL TControl.ClientRect
+ MOV EDX, ESP
+ LEA EAX, [ESP+16]
+ CALL PointInRect
+ ADD ESP, 24
+ TEST AL, AL
+ JZ @@fin
+ CALL Windows.SetCursor
+ PUSH EAX
+@@fin: POP EAX
+@@exit:
+ POP EBX
+end;
+
+procedure TControl.SetIcon( Value: HIcon );
+asm //cmd //opd
+ CMP EDX, [EAX].TControl.DF.fIcon
+ JE @@exit
+ MOV [EAX].TControl.DF.fIcon, EDX
+ INC EDX
+ JZ @@1
+ DEC EDX
+@@1:
+ PUSH EDX
+ PUSH 1 //ICON_BIG
+ PUSH WM_SETICON
+ PUSH EAX
+ CALL Perform
+ TEST EAX, EAX
+ JZ @@exit
+ PUSH EAX
+ CALL DestroyIcon
+@@exit:
+end;
+
+procedure TControl.SetMenu( Value: HMenu );
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ CMP [EBX].fMenu, EDX
+ JZ @@exit
+ PUSH EDX
+ MOV ECX, [EBX].fMenuObj
+ JECXZ @@no_free_menuctl
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ PUSH EDX
+ MOV EAX, EBX
+ CALL TControl.RemoveFromAutoFree
+ POP EAX
+ {$ELSE}
+ XCHG EAX, EDX
+ {$ENDIF}
+ CALL TObj.RefDec
+@@no_free_menuctl:
+ MOV ECX, [EBX].fMenu
+ JECXZ @@no_destroy
+ PUSH ECX
+ CALL DestroyMenu
+@@no_destroy:
+ POP EDX
+ MOV [EBX].fMenu, EDX
+ MOV ECX, [EBX].fHandle
+ JECXZ @@exit
+ PUSH EDX
+ PUSH ECX
+ CALL Windows.SetMenu
+@@exit:
+ POP EBX
+end;
+
+procedure TControl.DoAutoSize;
+asm
+ {$IFDEF NIL_EVENTS}
+ MOV ECX, [EAX].PP.fAutoSize
+ JECXZ @@exit
+ PUSH ECX
+ {$ELSE not NIL_EVENTS}
+ PUSH [EAX].PP.fAutoSize
+ {$ENDIF}
+@@exit:
+end;
+
+procedure TControl.SetCaption( const Value: KOLString );
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ LEA EAX, [EBX].fCaption
+ {$IFDEF UNICODE_CTRLS}
+ CALL System.@WStrAsg
+ {$ELSE}
+ CALL System.@LStrAsg
+ {$ENDIF}
+
+ MOV ECX, [EBX].fHandle
+ JECXZ @@0
+ PUSH [EBX].TControl.fCaption
+ PUSH 0
+ PUSH WM_SETTEXT
+ PUSH ECX
+ {$IFDEF UNICODE_CTRLS}
+ CALL SendMessageW
+ {$ELSE}
+ CALL SendMessage
+ {$ENDIF}
+@@0:
+ {$IFDEF USE_FLAGS}
+ TEST [EBX].fFlagsG1, (1 shl G1_IsStaticControl)
+ JNZ @@1
+ {$ELSE}
+ MOVZX ECX, byte ptr [EBX].fIsStaticControl
+ INC ECX
+ LOOP @@1
+ {$ENDIF}
+ MOV EAX, EBX
+ CALL Invalidate
+@@1:
+ XCHG EAX, EBX
+@@exit: POP EBX
+ PUSH [EAX].PP.fAutoSize
+@@exit_2:
+end;
+
+function TControl.GetVisible: Boolean;
+asm
+ //CALL UpdateWndStyles
+ {MOV ECX, [EAX].fHandle
+ JECXZ @@check_fStyle
+ PUSH EAX
+ PUSH ECX
+ CALL IsWindowVisible
+ TEST EAX, EAX
+ POP EAX
+ JMP @@checked // Z if not visible
+ }
+@@check_fStyle:
+ TEST byte ptr [EAX].fStyle.f3_Style, 1 shl F3_Visible // WS_VISIBLE shr 3
+@@checked:
+ {$IFDEF USE_FLAGS}
+ SETNZ AL
+ {$ELSE}
+ SETNZ DL
+ MOV [EAX].fVisible, DL
+ XCHG EAX, EDX
+ {$ENDIF}
+end;
+
+function TControl.Get_Visible: Boolean;
+asm // //
+ {$IFDEF USE_FLAGS}
+ CALL GetVisible
+ {$ELSE}
+ MOV ECX, [EAX].fHandle
+ JECXZ @@ret_fVisible
+ {$IFDEF USE_FLAGS}
+ TEST [EAX].fFlagsG3, 1 shl G3_IsControl
+ {$ELSE}
+ CMP [EAX].fIsControl, 0
+ {$ENDIF}
+ JNZ @@ret_fVisible
+ PUSH EAX
+ PUSH ECX
+ CALL IsWindowVisible
+ XCHG EDX, EAX
+ POP EAX
+ {$IFDEF USE_FLAGS}
+ SHL DL, F3_Visible
+ AND [EAX].TControl.fStyle.f3_Style, not(1 shl F3_Visible)
+ OR [EAX].TControl.fStyle.f3_Style, DL
+ {$ELSE}
+ MOV [EAX].fVisible, DL
+ {$ENDIF}
+@@ret_fVisible:
+ {$IFDEF USE_FLAGS}
+ TEST [EAX].fStyle.f3_Style, (1 shl F3_Visible)
+ SETNZ AL
+ {$ELSE}
+ MOVZX EAX, [EAX].fVisible
+ {$ENDIF}
+ {$ENDIF USE_FLAGS}
+end;
+
+procedure TControl.Set_Visible( Value: Boolean );
+const wsVisible = $10;
+asm
+ {$IFDEF OLD_ALIGN}
+ PUSH EBX
+ PUSH ESI
+ //MOV ESI, EAX
+ XCHG ESI, EAX
+ MOVZX EBX, DL
+ {CALL Get_Visible
+ CMP AL, BL
+ JE @@reset_fCreateHidden}
+
+ MOV AL, byte ptr [ESI].fStyle + 3
+ TEST EBX, EBX
+ JZ @@reset_WS_VISIBLE
+ {$IFDEF USE_FLAGS}
+ OR AL, 1 shl F3_Visible
+ {$ELSE}
+ OR AL, wsVisible
+ {$ENDIF}
+ PUSH SW_SHOW
+ JMP @@store_Visible
+@@reset_WS_VISIBLE:
+ {$IFDEF USE_FLAGS}
+ AND AL, not(1 shl F3_Visible)
+ {$ELSE}
+ AND AL, not wsVisible
+ {$ENDIF}
+ PUSH SW_HIDE
+
+@@store_Visible:
+ MOV byte ptr [ESI].fStyle + 3, AL
+ {$IFDEF USE_FLAGS}
+ {$ELSE}
+ MOV [ESI].fVisible, BL
+ {$ENDIF}
+ MOV ECX, [ESI].fHandle
+ JECXZ @@after_showwindow
+
+ PUSH ECX
+ CALL ShowWindow
+ PUSH ECX
+@@after_showwindow:
+ POP ECX
+
+ MOV EAX, [ESI].fParent
+ CALL dword ptr [Global_Align]
+
+@@chk_align_Self:
+ TEST EBX, EBX
+ JZ @@reset_fCreateHidden
+ MOV EAX, ESI
+ CALL dword ptr [Global_Align]
+
+
+@@reset_fCreateHidden:
+ MOV ECX, [ESI].fHandle
+ JECXZ @@exit
+ TEST BL, BL
+ JNZ @@exit
+ {$IFDEF USE_FLAGS}
+ AND [ESI], not(1 shl G4_CreateHidden)
+ {$ELSE}
+ MOV [ESI].fCreateHidden, BL { +++ }
+ {$ENDIF}
+@@exit:
+ POP ESI
+ POP EBX
+ {$ELSE NEW_ALIGN}
+ AND byte ptr [EAX].fStyle.f3_Style, not(1 shl F3_Visible)
+ TEST DL,DL
+ JZ @@0
+ OR byte ptr [EAX].fStyle.f3_Style, (1 shl F3_Visible)
+@@0:
+ {$IFDEF USE_FLAGS}
+ {$ELSE}
+ MOV [EAX].fVisible, DL
+ {$ENDIF USE_FLAGS}
+ MOV ECX, [EAX].fHandle
+ JECXZ @@exit
+ PUSH EAX
+ JZ @@1
+ CALL dword ptr [Global_Align]
+ POP EAX
+ PUSH SW_SHOW
+ PUSH [EAX].fHandle
+ CALL ShowWindow
+@@exit:
+ RET
+@@1:
+ {$IFDEF USE_FLAGS}
+ AND [EAX].fFlagsG4, not(1 shl G4_CreateHidden)
+ {$ELSE}
+ MOV [EAX].fCreateHidden, DL // = 0
+ {$ENDIF}
+ PUSH SW_HIDE
+ PUSH ECX
+ CALL ShowWindow
+ POP EAX
+ CALL dword ptr [Global_Align]
+ {$ENDIF}
+end;
+
+procedure TControl.SetVisible( Value: Boolean );
+asm
+ {$IFDEF USE_FLAGS}
+ OR [EAX].TControl.fFlagsG4, 1 shl G4_CreateVisible
+ {$ELSE}
+ MOV [EAX].TControl.fCreateVisible, 1
+ {$ENDIF}
+ CALL TControl.Set_Visible
+end;
+
+function TControl.GetBoundsRect: TRect;
+asm
+ PUSH ESI
+ PUSH EDI
+ LEA ESI, [EAX].fBoundsRect
+ MOV EDI, EDX
+
+ PUSH EDX
+
+ MOVSD
+ MOVSD
+ MOVSD
+ MOVSD
+
+ POP EDI
+
+ XCHG ESI, EAX
+ MOV ECX, [ESI].fHandle
+ JECXZ @@exit
+
+ PUSH EDI
+ PUSH ECX
+ CALL GetWindowRect
+
+ {$IFDEF USE_FLAGS}
+ TEST [ESI].fFlagsG3, (1 shl G3_IsControl) or (1 shl G3_IsMDIChild)
+ {$ELSE}
+ MOV AL, [ESI].fIsControl
+ OR AL, [ESI].fIsMDIChild
+ {$ENDIF}
+ JZ @@storeBounds
+
+@@chk_Parent:
+ MOV EAX, ESI
+ CALL TControl.GetParentWindow
+
+ TEST EAX, EAX
+ JZ @@exit
+
+ XOR EDX, EDX
+ PUSH EDX
+ PUSH EDX
+ PUSH ESP
+ PUSH EAX
+ CALL Windows.ClientToScreen
+
+ POP EAX
+ NEG EAX
+ POP ECX
+ NEG ECX
+ PUSH ECX
+ PUSH EAX
+ PUSH EDI
+ CALL OffsetRect
+
+@@storeBounds:
+ XCHG ESI, EDI
+ LEA EDI, [EDI].fBoundsRect
+ MOVSD
+ MOVSD
+ MOVSD
+ MOVSD
+
+@@exit:
+ POP EDI
+ POP ESI
+end;
+
+procedure HelpGetBoundsRect;
+asm
+ POP ECX
+ ADD ESP, - size_TRect
+ MOV EDX, ESP
+ PUSH ECX
+ PUSH EAX
+ CALL TControl.GetBoundsRect
+ POP EAX
+end;
+
+procedure TControl.SetBoundsRect( const Value: TRect );
+const swp_flags = SWP_NOZORDER or SWP_NOACTIVATE;
+asm
+ PUSH EDI
+ MOV EDI, EAX
+
+ PUSH ESI
+ MOV ESI, EDX
+
+ CALL HelpGetBoundsRect
+
+ MOV EAX, ESI
+ MOV EDX, ESP
+ CALL RectsEqual
+ TEST AL, AL
+ JNZ @@exit
+
+ POP EDX // left
+ POP ECX // top
+ POP EAX // right
+ PUSH EAX
+ PUSH ECX
+ PUSH EDX
+
+ SUB EAX, EDX // EAX = width
+ CMP EDX, [ESI].TRect.Left
+ {$IFDEF USE_FLAGS}
+ {$ELSE}
+ MOV DL, 0
+ {$ENDIF}
+ JNE @@11
+@@1: CMP ECX, [ESI].TRect.Top
+ JE @@2
+@@11:
+ {$IFDEF USE_FLAGS}
+ OR [EDI].fFlagsG2, (1 shl G2_ChangedPos)
+ {$ELSE}
+ OR DL, 2
+ OR [EDI].fChangedPosSz, DL
+ {$ENDIF}
+@@2:
+ PUSH EAX // W saved
+
+ MOV EAX, [EDI].fBoundsRect.Bottom
+ SUB EAX, ECX
+ PUSH EAX // H saved
+
+ PUSH EDI // @Self saved
+ {$IFDEF USE_GRAPHCTLS}
+ {$IFDEF USE_FLAGS}
+ TEST [EDI].fFlagsG6, 1 shl G6_GraphicCtl
+ JZ @@invalid1
+ {$ELSE}
+ CMP [EDI].fWindowed, 0
+ JNZ @@invalid1
+ {$ENDIF}
+ MOV EAX, EDI
+ CALL TControl.InvalidateNonWindowed
+@@invalid1:
+ {$ENDIF}
+
+ LEA EDI, [EDI].fBoundsRect
+ MOVSD
+ MOVSD
+ MOVSD
+ MOVSD
+
+ MOV ESI, EDI
+ POP EDI // @ Self restored
+
+ MOV ECX, [EDI].fHandle
+ JECXZ @@fin
+
+ STD
+ PUSH swp_flags
+
+ LODSD
+ LODSD
+ XCHG EDX, EAX // EDX = bottom
+ LODSD
+ XCHG ECX, EAX // ECX = right
+ LODSD
+ SUB EDX, EAX // EAX = bottom - top
+ PUSH EDX // push HEIGHT
+ XCHG EDX, EAX // EDX = top
+ LODSD // EAX = left
+ CLD
+
+ SUB ECX, EAX
+ PUSH ECX // push WIDTH
+
+ PUSH EDX // push TOP
+ PUSH EAX // push LEFT
+ PUSH 0
+
+ PUSH [EDI].fHandle
+ CALL SetWindowPos
+
+@@fin:
+ POP EDX // H restored
+ POP EAX // W restored
+
+ {$IFDEF USE_FLAGS}
+ TEST [EDI].fFlagsG1, (1 shl G1_SizeRedraw)
+ {$ELSE}
+ CMP [EDI].fSizeRedraw, 0
+ {$ENDIF USE_FLAGS}
+ JE @@exit
+@@invalid2:
+ XCHG EAX, EDI
+ CALL Invalidate
+
+@@exit:
+ ADD ESP, size_TRect
+ POP ESI
+ POP EDI
+end;
+
+procedure TControl.SetWindowState( Value: TWindowState );
+asm //cmd //opd
+ PUSH EAX
+ PUSH EDX
+ CALL TControl.GetWindowState
+ POP EDX
+ CMP AL, DL
+ POP EAX
+ JE @@exit
+ MOV [EAX].TControl.DF.fWindowState, DL
+ MOV ECX, [EAX].TControl.fHandle
+ JECXZ @@exit
+ XCHG EAX, EDX
+ CBW
+ CWDE
+ MOV AL, byte ptr [WindowStateShowCommands+EAX]
+ PUSH EAX
+ PUSH ECX
+ CALL ShowWindow
+@@exit:
+end;
+
+procedure TControl.Show;
+asm
+ PUSH EBX
+ MOV EBX, EAX
+ CALL CreateWindow
+ MOV DL, 1
+ MOV EAX, EBX
+ CALL SetVisible
+ PUSH [EBX].fHandle
+ CALL SetForegroundWindow
+ XCHG EAX, EBX
+ CALL DoSetFocus
+ POP EBX
+end;
+
+function TControl.Client2Screen( const P: TPoint ): TPoint;
+asm
+ PUSH ESI
+ PUSH EDI
+
+ MOV ESI, EDX
+ MOV EDI, ECX
+
+ MOVSD
+ MOVSD
+
+ PUSH ECX
+ MOV ECX, [EAX].fHandle
+ JECXZ @@exit
+
+ PUSH ECX
+ CALL ClientToScreen
+ PUSH ECX
+
+@@exit: POP ECX
+ POP EDI
+ POP ESI
+end;
+
+function TControl.Screen2Client( const P: TPoint ): TPoint;
+asm
+ PUSH ESI
+ PUSH EDI
+
+ MOV ESI, EDX
+ MOV EDI, ECX
+
+ MOVSD
+ MOVSD
+
+ PUSH ECX
+ MOV ECX, [EAX].fHandle
+ JECXZ @@exit
+
+ PUSH ECX
+ CALL ScreenToClient
+ PUSH ECX
+
+@@exit: POP ECX
+ POP EDI
+ POP ESI
+end;
+
+function TControl.ClientRect: TRect;
+asm
+ PUSH ESI
+ XCHG ESI, EAX
+ PUSH EDX
+ PUSH EDX // prepare 'dest' for GetClientRect
+
+ LEA EAX, [ESI].fBoundsRect
+ XOR ECX, ECX
+ MOV CL, size_TRect
+
+ CALL System.Move
+
+ MOV EAX, ESI
+ CALL TControl.GetWindowHandle
+
+ XCHG ECX, EAX
+ JECXZ @@exit
+
+ PUSH ECX // prepare 'handle' for GetClientRect
+ CALL GetClientRect
+
+ PUSH EDX
+
+@@exit: POP EDX
+ POP EDX // EDX = @Result
+ LEA ESI, [ESI].fClientTop
+ LODSB
+ MOVSX EAX, AL
+ ADD [EDX].TRect.Top, EAX
+ LODSB
+ MOVSX EAX, AL
+ SUB [EDX].TRect.Bottom, EAX
+ LODSB
+ MOVSX EAX, AL
+ ADD [EDX].TRect.Left, EAX
+ LODSB
+ MOVSX EAX, AL
+ SUB [EDX].TRect.Right, EAX
+ POP ESI
+end;
+
+procedure TControl.Invalidate;
+asm
+ {$IFDEF USE_GRAPHCTLS}
+ PUSH dword ptr [EAX].TControl.PP.fDoInvalidate
+ {$ELSE}
+ MOV ECX, [EAX].fHandle
+ JECXZ @@exit
+ PUSH $FF
+ PUSH 0
+ PUSH ECX
+ CALL Windows.InvalidateRect
+@@exit:
+ {$ENDIF}
+end;
+
+{$IFDEF USE_GRAPHCTLS}
+procedure InvalidateWindowed( Sender: PObj );
+asm
+ MOV ECX, [EAX].TControl.fHandle
+ JECXZ @@exit
+ PUSH $FF
+ PUSH 0
+ PUSH ECX
+ CALL Windows.InvalidateRect
+@@exit:
+end;
+{$ENDIF USE_GRAPHCTLS}
+
+function TControl.GetIcon: HIcon;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ MOV EAX, [EBX].DF.fIcon
+ INC EAX
+ JZ @@exit
+ DEC EAX
+ JNZ @@exit
+
+ MOV ECX, [Applet]
+ JECXZ @@load
+ CMP ECX, EBX
+ JZ @@load
+
+ XCHG EAX, ECX
+ CALL TControl.GetIcon
+ TEST EAX, EAX
+ JZ @@exit
+
+ XOR EDX, EDX
+ PUSH EDX
+ PUSH EDX
+ PUSH EDX
+ INC EDX // IMAGE_ICON = 1
+ PUSH EDX
+ PUSH EAX
+ CALL CopyImage
+ JMP @@store_fIcon
+
+@@main_icon:
+ {$IFDEF NUMERIC_APPICON} {$DEFINE CUSTOM_APPICON} {$ENDIF}
+ {$IFDEF CUSTOM_APPICON}
+ {$I CustomAppIconRsrcName_ASM.inc} // create such file with DB 'your icon rsrc name' / DD youriconnumber
+ {$ELSE}
+ {$IFDEF UNICODE_CTRLS}
+ DB 'M',0,'A',0,'I',0,'N',0,'I',0,'C',0,'O',0,'N',0,0
+ {$ELSE}
+ DB 'MAINICON'
+ {$ENDIF}
+ {$ENDIF}
+ DB 0
+
+@@load:
+ {$IFDEF NUMERIC_APPICON}
+ PUSH DWORD [@@main_icon]
+ {$ELSE}
+ PUSH offset @@main_icon
+ {$ENDIF}
+ PUSH [hInstance]
+ CALL LoadIcon
+@@store_fIcon:
+ MOV [EBX].DF.fIcon, EAX
+@@exit:
+ POP EBX
+end;
+
+function TControl.CallDefWndProc(var Msg: TMsg): Integer;
+asm
+ PUSH [EDX].TMsg.lParam
+ PUSH [EDX].TMsg.wParam
+ PUSH [EDX].TMsg.message
+
+ MOV ECX, [EAX].fDefWndProc
+ JECXZ @@defwindowproc
+
+ PUSH [EAX].fHandle
+ PUSH ECX
+ CALL CallWindowProc
+ RET
+
+@@defwindowproc:
+ PUSH [EDX].TMsg.hwnd
+ {$IFDEF UNICODE_CTRLS}
+ CALL DefWindowProcW
+ {$ELSE}
+ CALL DefWindowProc
+ {$ENDIF}
+end;
+
+function TControl.GetWindowState: TWindowState;
+asm //cmd //opd
+ PUSH EBX
+ PUSH ESI
+ XCHG ESI, EAX
+ MOVZX EBX, [ESI].TControl.DF.fWindowState
+ MOV ECX, [ESI].TControl.fHandle
+ JECXZ @@ret_EBX
+ MOV BL, 2
+ MOV ESI, ECX
+ PUSH ESI
+ CALL IsZoomed
+ TEST EAX, EAX
+ JNZ @@ret_EBX
+ DEC EBX
+ PUSH ESI
+ CALL IsIconic
+ TEST EAX, EAX
+ JNZ @@ret_EBX
+ DEC EBX
+@@ret_EBX:
+ XCHG EAX, EBX
+ POP ESI
+ POP EBX
+end;
+
+function TControl.DoSetFocus: Boolean;
+asm
+ PUSH ESI
+ MOV ESI, EAX
+
+ CALL GetEnabled
+ (*
+ {$IFDEF USE_FLAGS}
+ MOV DL, byte ptr [ESI].TControl.fStyle.f2_Style
+ // F2_Tabstop = 0 !
+ {$ELSE}
+ MOV DL, byte ptr [ESI+2].TControl.fStyle
+ OR DL, [ESI].TControl.fTabstop
+ {$ENDIF USE_FLAGS}
+ AND AL, DL
+ *)
+ TEST AL, AL
+ JZ @@exit
+
+ INC [ESI].TControl.fClickDisabled
+ PUSH [ESI].TControl.fHandle
+ CALL SetFocus
+ DEC [ESI].TControl.fClickDisabled
+ MOV AL, 1
+@@exit:
+ POP ESI
+end;
+
+function TControl.GetEnabled: Boolean;
+asm
+ MOV ECX, [EAX].fHandle
+ JECXZ @@get_field
+
+ PUSH ECX
+ CALL IsWindowEnabled
+ RET
+
+@@get_field:
+ TEST byte ptr [EAX].fStyle + 3, 8 //WS_DISABLED shr 3
+ SETZ AL
+end;
+
+function TControl.IsMainWindow: Boolean;
+asm XCHG ECX, EAX
+ XOR EDX, EDX
+ MOV EAX, [Applet]
+ TEST EAX, EAX
+ JNZ @@0
+ {$IFDEF USE_FLAGS}
+ TEST [ECX].fFlagsG3, 1 shl G3_IsControl
+ {$ELSE}
+ CMP [ECX].fIsControl, AL
+ {$ENDIF}
+ JMP @@3
+@@0: CMP [appbuttonUsed], DL
+ JZ @@2
+@@1: PUSH ECX
+ CALL TControl.GetMembers
+ POP ECX
+@@2: CMP ECX, EAX
+@@3: SETZ AL
+end;
+
+procedure TControl.SetParent( Value: PControl );
+asm
+ PUSH EBX
+ PUSH EDI
+ XCHG EBX, EAX
+ MOV EDI, EDX
+ MOV ECX, [EBX].fParent
+ CMP EDI, ECX
+ JE @@exit
+
+ JECXZ @@1
+ {$IFDEF USE_GRAPHCTLS}
+ PUSH ECX
+ MOV EAX, EBX
+ CALL TControl.Invalidate
+ POP ECX
+ {$ENDIF}
+ PUSH ECX
+
+ MOV EAX, [ECX].fChildren
+ MOV EDX, EBX
+ CALL TList.Remove
+
+ POP EAX
+ {$IFNDEF USE_AUTOFREE4CONTROL}
+ PUSH EAX
+ MOV EDX, EBX
+ CALL TObj.RemoveFromAutoFree
+ POP EAX
+ {$ENDIF}
+
+ {$IFNDEF SMALLEST_CODE}
+ MOV ECX, [EAX].PP.fNotifyChild
+ {$IFDEF NIL_EVENTS}
+ JECXZ @@1
+ {$ENDIF}
+ XOR EDX, EDX
+ CALL ECX
+ {$ENDIF}
+@@1:
+ MOV [EBX].fParent, EDI
+ TEST EDI, EDI
+ JZ @@exit
+
+ MOV EAX, [EDI].fChildren
+ MOV EDX, EBX
+ CALL TList.Add
+
+ {$IFDEF USE_AUTOFREE4CHILDREN}
+ MOV EAX, EDI
+ MOV EDX, EBX
+ CALL TControl.Add2AutoFree
+ {$ENDIF}
+
+ {$IFNDEF INPACKAGE}
+ MOV ECX, [EBX].fHandle
+ JECXZ @@2
+ MOV EAX, EDI
+ CALL TControl.GetWindowHandle
+ PUSH EAX
+ PUSH [EBX].fHandle
+ CALL Windows.SetParent
+@@2:
+ {$ENDIF}
+
+ {$IFNDEF SMALLEST_CODE}
+ MOV ECX, [EDI].PP.fNotifyChild
+ {$IFDEF NIL_EVENTS}
+ JECXZ @@3
+ {$ENDIF}
+ MOV EAX, EDI
+ MOV EDX, EBX
+ CALL ECX
+@@3:
+ MOV ECX, [EBX].PP.fNotifyChild
+ {$IFDEF NIL_EVENTS}
+ JECXZ @@4
+ {$ENDIF}
+ MOV EAX, EDI
+ MOV EDX, EBX
+ CALL ECX
+@@4: {$ENDIF}
+
+ {$IFNDEF USE_GRAPHCTLS}
+ XCHG EAX, EBX
+ CALL TControl.Invalidate
+ {$ENDIF}
+@@exit:
+ POP EDI
+ POP EBX
+end;
+
+constructor TControl.CreateParented(AParent: PControl);
+asm //cmd //opd
+ PUSH EAX
+ MOV EDX, ECX
+ MOV ECX, [EAX]
+ CALL dword ptr [ECX+8]
+ POP EAX
+end;
+
+function TControl.GetLeft: Integer;
+asm
+ CALL HelpGetBoundsRect
+ POP EAX
+
+ POP ECX
+ POP ECX
+ POP ECX
+end;
+
+procedure TControl.SetLeft( Value: Integer );
+asm
+ PUSH EDI
+
+ PUSH EDX
+ CALL HelpGetBoundsRect
+ POP EDX // EDX = Left
+ POP ECX // ECX = Top
+ POP EDI // EDI = Right
+
+ SUB EDI, EDX // EDI = width
+ MOV EDX, [ESP+4] // EDX = Left'
+ ADD EDI, EDX // EDI = Right'
+
+ PUSH EDI
+ PUSH ECX
+ PUSH EDX
+ MOV EDX, ESP
+
+ CALL SetBoundsRect
+ ADD ESP, size_TRect + 4
+
+ POP EDI
+
+end;
+
+function TControl.GetTop: Integer;
+asm
+ CALL HelpGetBoundsRect
+ POP EDX
+ POP EAX
+ POP EDX
+ POP EDX
+end;
+
+procedure TControl.SetTop( Value: Integer );
+asm
+ PUSH ESI
+ PUSH EDI
+
+ PUSH EDX
+ CALL HelpGetBoundsRect
+ POP EDX // EDX = Left
+ POP ECX // ECX = Top
+ POP EDI // EDI = Right
+ POP ESI // ESI = Bottom
+
+ SUB ESI, ECX // ESI = Height'
+ POP ECX // ECX = Top'
+ ADD ESI, ECX // ESI = Bottom'
+
+ PUSH ESI
+ PUSH EDI
+ PUSH ECX
+ PUSH EDX
+ MOV EDX, ESP
+
+ CALL SetBoundsRect
+ ADD ESP, size_TRect
+
+ POP EDI
+ POP ESI
+end;
+
+function TControl.GetWidth: Integer;
+asm
+ CALL HelpGetBoundsRect
+ POP EDX
+ POP ECX
+ POP EAX
+ SUB EAX, EDX
+ POP ECX
+end;
+
+procedure TControl.SetWidth( Value: Integer );
+asm
+ PUSH EDX
+
+ CALL HelpGetBoundsRect
+ POP EDX
+ PUSH EDX
+ ADD EDX, [ESP].size_TRect
+ MOV [ESP].TRect.Right, EDX
+
+ MOV EDX, ESP
+ CALL SetBoundsRect
+
+ ADD ESP, size_TRect + 4
+end;
+
+function TControl.GetHeight: Integer;
+asm
+ CALL HelpGetBoundsRect
+ POP ECX
+ POP EDX // EDX = top
+ POP ECX
+ POP EAX // EAX = bottom
+ SUB EAX, EDX // result = height
+end;
+
+procedure TControl.SetHeight( Value: Integer );
+asm
+ PUSH EDX
+
+ CALL HelpGetBoundsRect
+ MOV EDX, [ESP].TRect.Top
+ ADD EDX, [ESP].size_TRect
+ MOV [ESP].TRect.Bottom, EDX
+
+ MOV EDX, ESP
+ CALL SetBoundsRect
+
+ ADD ESP, size_TRect + 4
+end;
+
+function TControl.GetPosition: TPoint;
+asm
+ PUSH EDX
+ CALL HelpGetBoundsRect
+ POP EAX // EAX = left
+ POP ECX // ECX = top
+ POP EDX
+ POP EDX
+ POP EDX // EDX = @Result
+ MOV [EDX], EAX
+ MOV [EDX+4], ECX
+end;
+
+procedure TControl.Set_Position( Value: TPoint );
+asm
+ PUSH ESI
+ PUSH EDI
+
+ PUSH EAX
+ PUSH EDX
+ CALL HelpGetBoundsRect
+ POP EDX // left
+ POP EAX // top
+ POP ECX // right
+ SUB ECX, EDX // ECX = width
+ POP EDX // bottom
+ SUB EDX, EAX // EDX = height
+ POP EAX // EAX = @Value
+ POP ESI // ESI = @Self
+
+ MOV EDI, [EAX+4] // top'
+ ADD EDX, EDI
+ PUSH EDX // bottom'
+
+ MOV EAX, [EAX] // left'
+ ADD ECX, EAX
+ PUSH ECX // right'
+
+ PUSH EDI // top'
+ PUSH EAX // left'
+
+ MOV EAX, ESI
+ MOV EDX, ESP
+ CALL SetBoundsRect
+
+ ADD ESP, size_TRect
+
+ POP EDI
+ POP ESI
+end;
+
+procedure DefaultPaintBackground( Sender: PControl; DC: HDC; Rect: PRect );
+asm
+ PUSH EDI
+
+ PUSH EDI
+ MOV EDI, ESP
+
+ PUSH ECX
+ PUSH EDX
+
+ MOV EAX, [EAX].TControl.fColor
+ CALL Color2RGB
+ PUSH EAX
+ CALL CreateSolidBrush
+ STOSD
+ MOV EDI, EAX
+ CALL windows.FillRect
+ PUSH EDI
+ CALL DeleteObject
+ POP EDI
+end;
+
+procedure TControl.SetCtlColor( Value: TColor );
+asm
+ PUSH EBX
+ MOV EBX, EAX
+
+ {$IFNDEF INPACKAGE}
+ PUSH EDX
+
+ CALL GetWindowHandle
+ XCHG ECX, EAX
+
+ POP EDX
+ {$ELSE}
+ MOV ECX, [EBX].fHandle
+ {$ENDIF}
+
+ JECXZ @@1
+
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EBX].fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.aSetBkColor
+ {$ELSE}
+ MOVZX ECX, [EBX].fCommandActions.aSetBkColor
+ {$ENDIF}
+ JECXZ @@1
+
+ PUSH EDX
+
+ XCHG EAX, EDX
+ PUSH ECX
+ CALL Color2RGB
+ POP ECX
+
+ PUSH EAX // Color2RGB( Value )
+ PUSH 0 // 0
+ PUSH ECX // fCommandActions.aSetBkColor
+ PUSH EBX // @ Self
+ CALL TControl.Perform
+
+ POP EDX
+
+@@1:
+ CMP EDX, [EBX].fColor
+ JZ @@exit
+
+ MOV [EBX].fColor, EDX
+
+ XOR ECX, ECX
+ XCHG ECX, [EBX].fTmpBrush
+ JECXZ @@setbrushcolor
+
+ PUSH EDX
+ PUSH ECX
+ CALL DeleteObject
+ POP EDX
+
+@@setbrushcolor:
+ MOV ECX, [EBX].fBrush
+ JECXZ @@invldte
+
+ XCHG EAX, ECX
+ MOV ECX, EDX
+ //MOV EDX, go_Color
+ XOR EDX, EDX
+ CALL TGraphicTool.SetInt
+
+@@invldte:
+ XCHG EAX, EBX
+ CALL TControl.Invalidate
+@@exit:
+ POP EBX
+end;
+
+function TControl.GetParentWnd( NeedHandle: Boolean ): HWnd;
+asm
+ XCHG EDX, EAX
+ TEST AL, AL
+ MOV EAX, [EDX].fParentWnd
+ MOV ECX, [EDX].fParent
+ JECXZ @@exit
+
+ PUSH ECX
+ JZ @@load_handle
+
+ XCHG EAX, ECX
+ CALL GetWindowHandle
+
+@@load_handle:
+ POP EAX
+ MOV EAX, [EAX].fHandle
+@@exit:
+end;
+
+function TControl.ProcessMessage: Boolean;
+const size_TMsg = sizeof( TMsg );
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+
+ ADD ESP, -size_TMsg-4
+
+ MOV EDX, ESP
+ PUSH 1
+ XOR ECX, ECX
+ PUSH ECX
+ PUSH ECX
+ PUSH ECX
+ PUSH EDX
+ CALL PeekMessage
+
+ TEST EAX, EAX
+ JZ @@exit
+
+ CMP WORD PTR [ESP].TMsg.message, WM_QUIT
+ JNE @@tran_disp
+ OR [AppletTerminated], DL
+ {$IFDEF PROVIDE_EXITCODE}
+ MOV EDX, [ESP].TMsg.wParam
+ MOV [ExitCode], EDX
+ {$ENDIF PROVIDE_EXITCODE}
+ JMP @@fin
+
+@@tran_disp:
+ MOV ECX, [EBX].PP.fExMsgProc
+ {$IFDEF NIL_EVENTS}
+ JECXZ @@do_tran_disp
+ {$ENDIF}
+ XCHG EAX, EBX
+ MOV EDX, ESP
+ CALL ECX
+ TEST AL, AL
+ JNZ @@fin
+
+@@do_tran_disp:
+ MOV EAX, ESP
+ PUSH EAX
+ PUSH EAX
+ CALL TranslateMessage
+ CALL DispatchMessage
+
+@@fin:
+ CMP word ptr [ESP].TMsg.message, 0
+ SETNZ AL
+
+@@exit: ADD ESP, size_TMsg+4
+ POP EBX
+end;
+
+procedure TControl.ProcessMessages;
+asm
+@@loo: PUSH EAX
+ CALL ProcessMessage
+ DEC AL
+ POP EAX
+ JZ @@loo
+end;
+
+function WndProcForm(Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+const szPaintStruct = sizeof(TPaintStruct);
+asm //cmd //opd
+ {$IFDEF ENDSESSION_HALT}
+ CMP word ptr [EDX].TMsg.message, WM_ENDSESSION
+ JNE @@chk_WM_SETFOCUS
+
+ CMP [EDX].TMsg.wParam, 0
+ JZ @@ret_false
+
+ CALL TObj.RefDec
+ XOR EAX, EAX
+ MOV [AppletRunning], AL
+ XCHG EAX, [Applet]
+ INC [AppletTerminated]
+
+ CALL TObj.RefDec
+ CALL System.@Halt0
+ {$ENDIF ENDSESSION_HALT}
+
+@@chk_WM_SETFOCUS:
+ CMP word ptr [EDX].TMsg.message, WM_SETFOCUS
+ JNE @@ret_false
+
+ PUSH EBX
+ PUSH ESI
+ XOR EBX, EBX
+ INC EBX
+ XCHG ESI, EAX
+ {$IFDEF NEW_MODAL}
+ MOV ECX, [ESI].TControl.DF.fModalForm
+ JECXZ @@no_fix_modal_setfocus
+ PUSH [ECX].TControl.fHandle
+ CALL SetFocus
+@@no_fix_modal_setfocus:
+ MOV ECX, [ESI].TControl.DF.FCurrentControl
+ JECXZ @@setFocuswhenCreateWindow
+ {$IFDEF USE_FLAGS}
+ TEST [ECX].TControl.fFlagsG3, (1 shl G3_IsForm)
+ SETNZ DL
+ TEST [ESI].TControl.fFlagsG3, (1 shl G3_IsApplet)
+ SETNZ DH
+ XOR DL, DH
+ JNZ @@1
+ {$ELSE}
+ MOV DL, [ECX].TControl.fIsForm
+ XOR DL, [ESI].TControl.FIsApplet
+ JNZ @@1
+ {$ENDIF}
+ {$ELSE not NEW_MODAL}
+ MOV ECX, [ESI].TControl.DF.fCurrentControl
+ JECXZ @@0
+ {$ENDIF}
+@@setFocuswhenCreateWindow:
+ JECXZ @@1 //+++++++++++++++
+ //INC EBX
+ XCHG EAX, ECX
+
+ // or CreateForm?
+ PUSH EAX
+ CALL CallTControlCreateWindow
+ TEST AL, AL
+ POP EAX
+ JZ @@1
+
+ PUSH [EAX].TControl.fHandle
+ CALL SetFocus
+ INC EBX
+@@0: DEC EBX
+@@1: MOV ECX, [Applet]
+ JECXZ @@ret_EBX
+ CMP ECX, ESI
+ JE @@ret_EBX
+ MOV [ECX].TControl.DF.FCurrentControl, ESI
+@@ret_EBX:
+ XCHG EAX, EBX
+ POP ESI
+ POP EBX
+ RET
+
+@@ret_false:
+ XOR EAX, EAX
+end;
+
+function GetPrevCtrlBoundsRect( P: PControl; var R: TRect ): Boolean;
+asm
+ MOV EDX, EBX
+ MOV EAX, [EBX].TControl.fParent
+ TEST EAX, EAX
+ JZ @@exit
+ PUSH EAX
+ CALL TControl.ChildIndex
+ TEST EAX, EAX
+ XCHG EDX, EAX
+ POP EAX
+ JZ @@exit
+ DEC EDX
+ CALL TControl.GetMembers
+
+ POP ECX // retaddr
+ ADD ESP, -size_TRect
+ MOV EDX, ESP
+ PUSH ECX
+ CALL TControl.GetBoundsRect
+ STC // return CARRY
+@@exit:
+end;
+
+function TControl.PlaceUnder: PControl;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ CALL GetPrevCtrlBoundsRect
+ JNC @@exit
+ POP EDX // EDX = Left
+ MOV EAX, EBX
+ CALL TControl.SetLeft
+
+ POP EDX
+ POP EDX
+ POP EDX // EDX = Bottom
+
+ MOV EAX, [EBX].fParent
+ MOVSX ECX, [EAX].fMargin
+ ADD EDX, ECX
+
+ MOV EAX, EBX
+ CALL TControl.SetTop
+@@exit:
+ XCHG EAX, EBX
+ POP EBX
+end;
+
+function TControl.PlaceDown: PControl;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ CALL GetPrevCtrlBoundsRect
+ JNC @@exit
+ POP EDX
+ POP EDX
+ POP EDX
+ POP EDX // EDX = Bottom
+
+ MOV EAX, [EBX].fParent
+ MOVSX ECX, [EAX].fMargin
+ ADD EDX, ECX
+
+ MOV EAX, EBX
+ CALL TControl.SetTop
+@@exit:
+ XCHG EAX, EBX
+ POP EBX
+end;
+
+function TControl.PlaceRight: PControl;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ CALL GetPrevCtrlBoundsRect
+ JNC @@exit
+ POP EDX
+ POP EDX // EDX = Top
+ MOV EAX, EBX
+ CALL TControl.SetTop
+ POP EDX // EDX = Right
+
+ MOV EAX, [EBX].fParent
+ MOVSX ECX, [EAX].fMargin
+ ADD EDX, ECX
+
+ POP ECX
+ MOV EAX, EBX
+ CALL TControl.SetLeft
+@@exit:
+ XCHG EAX, EBX
+ POP EBX
+end;
+
+function TControl.SetSize(W, H: Integer): PControl;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ SUB ESP, 16
+ XCHG EAX, EDX
+ MOV EDX, ESP
+ PUSH ECX // save H
+ PUSH EAX // save W
+ MOV EAX, EBX
+ CALL GetBoundsRect
+ POP ECX // pop W
+ JECXZ @@nochg_W
+ ADD ECX, [ESP+4].TRect.Left
+ MOV [ESP+4].TRect.Right, ECX
+@@nochg_W:
+ POP ECX // pop H
+ JECXZ @@nochg_H
+ ADD ECX, [ESP].TRect.Top
+ MOV [ESP].TRect.Bottom, ECX
+@@nochg_H:
+ MOV EAX, EBX
+ MOV EDX, ESP
+ CALL TControl.SetBoundsRect
+ ADD ESP, 16
+ XCHG EAX, EBX
+ POP EBX
+end;
+
+function TControl.AlignLeft(P: PControl): PControl;
+asm
+ PUSH EAX
+ MOV EAX, EDX
+ CALL TControl.GetLeft
+ MOV EDX, EAX
+ POP EAX
+ PUSH EAX
+ CALL TControl.SetLeft
+ POP EAX
+end;
+
+function TControl.AlignTop(P: PControl): PControl;
+asm
+ PUSH EAX
+ MOV EAX, EDX
+ CALL TControl.GetTop
+ MOV EDX, EAX
+ POP EAX
+ PUSH EAX
+ CALL TControl.SetTop
+ POP EAX
+end;
+
+function WndProcCtrl( Self_: PControl; var Msg: TMsg; var Rslt: Integer): Boolean;
+asm //cmd //opd
+ PUSH EBX
+ XCHG EBX, EAX
+ PUSH ESI
+ PUSH EDI
+ MOV EDI, EDX
+ MOV EDX, [EDI].TMsg.message
+
+ SUB DX, CN_CTLCOLORMSGBOX
+ CMP DX, CN_CTLCOLORSTATIC-CN_CTLCOLORMSGBOX
+ JA @@chk_CM_COMMAND
+@@2:
+ PUSH ECX
+ MOV EAX, [EBX].TControl.fTextColor
+ CALL Color2RGB
+ XCHG ESI, EAX
+ PUSH ESI
+ PUSH [EDI].TMsg.wParam
+ CALL SetTextColor
+ {$IFDEF USE_FLAGS}
+ TEST [EBX].TControl.fFlagsG2, (1 shl G2_Transparent)
+ {$ELSE}
+ CMP [EBX].TControl.fTransparent, 0
+ {$ENDIF}
+ JZ @@opaque
+
+ PUSH Windows.TRANSPARENT
+ PUSH [EDI].TMsg.wParam
+ CALL SetBkMode
+ PUSH NULL_BRUSH
+ CALL GetStockObject
+ JMP @@ret_rslt
+
+@@opaque:
+ MOV EAX, [EBX].TControl.fColor
+ CALL Color2RGB
+ XCHG ESI, EAX
+ PUSH OPAQUE
+ PUSH [EDI].TMsg.wParam
+ CALL SetBkMode
+ PUSH ESI
+ PUSH [EDI].TMsg.wParam
+ CALL SetBkColor
+
+ MOV EAX, EBX
+ CALL Global_GetCtlBrushHandle
+@@ret_rslt:
+ XCHG ECX, EAX
+@@tmpbrushready:
+ POP EAX
+ MOV [EAX], ECX
+@@ret_true:
+ MOV AL, 1
+
+ JMP @@ret_EAX
+
+@@chk_CM_COMMAND:
+ CMP word ptr [EDI].TMsg.message, CM_COMMAND
+ JNE @@chk_WM_SETFOCUS
+
+ PUSH ECX
+
+ MOVZX ECX, word ptr [EDI].TMsg.wParam+2
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ESI, [EBX].TControl.fCommandActions
+ CMP CX, [ESI].TCommandActionsObj.aClick
+ {$ELSE}
+ CMP CX, [EBX].TControl.fCommandActions.aClick
+ {$ENDIF}
+ JNE @@chk_aEnter
+
+ CMP [EBX].TControl.fClickDisabled, 0
+ JG @@calldef
+ MOV EAX, EBX
+ MOV DL, 1
+ CALL TControl.SetFocused
+ MOV EAX, EBX
+ CALL TControl.DoClick
+ JMP @@calldef
+
+@@chk_aEnter:
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV EAX, [EBX].TControl.fCommandActions
+ CMP CX, [EAX].TCommandActionsObj.aEnter
+ {$ELSE}
+ CMP CX, [EBX].TControl.fCommandActions.aEnter
+ {$ENDIF}
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EBX].TControl.EV
+ LEA EAX, [EAX].TEvents.fOnEnter
+ {$ELSE}
+ LEA EAX, [EBX].TControl.EV.fOnEnter
+ {$ENDIF}
+ JE @@goEvent
+ //LEA EAX, [EBX].TControl.EV.fOnLeave
+ ADD EAX, 8
+ {$IFDEF COMMANDACTIONS_OBJ}
+ CMP CX, [ESI].TCommandActionsObj.aLeave
+ {$ELSE}
+ CMP CX, [EBX].TControl.fCommandActions.aLeave
+ {$ENDIF}
+ JE @@goEvent
+ //LEA EAX, [EBX].TControl.EV.fOnChangeCtl
+ SUB EAX, 16
+ {$IFDEF COMMANDACTIONS_OBJ}
+ CMP CX, [ESI].TCommandActionsObj.aChange
+ {$ELSE}
+ CMP CX, [EBX].TControl.fCommandActions.aChange
+ {$ENDIF}
+ JNE @@chk_aSelChange
+@@goEvent:
+ MOV ECX, [EAX].TMethod.Code
+ {$IFDEF NIL_EVENTS}
+ JECXZ @@2calldef
+ {$ENDIF}
+ MOV EAX, [EAX].TMethod.Data
+ MOV EDX, EBX
+ CALL ECX
+@@2calldef:
+ JMP @@calldef
+
+@@chk_aSelChange:
+ {$IFDEF COMMANDACTIONS_OBJ}
+ CMP CX, [ESI].TCommandActionsObj.aSelChange
+ {$ELSE}
+ CMP CX, [EBX].TControl.fCommandActions.aSelChange
+ {$ENDIF}
+ JNE @@chk_WM_SETFOCUS_1
+ MOV EAX, EBX
+ CALL TControl.DoSelChange
+
+@@calldef:
+ XCHG EAX, EBX
+ MOV EDX, EDI
+ CALL TControl.CallDefWndProc
+ JMP @@ret_rslt
+
+@@chk_WM_SETFOCUS_1:
+ POP ECX
+@@chk_WM_SETFOCUS:
+ XOR EAX, EAX
+ CMP word ptr [EDI].TMsg.message, WM_SETFOCUS
+ JNE @@chk_WM_KEYDOWN
+
+ MOV [ECX], EAX
+ MOV EAX, EBX
+ CALL TControl.ParentForm
+ TEST EAX, EAX
+ JZ @@ret_true
+
+ PUSH EAX
+ MOV ECX, [EAX].TControl.DF.FCurrentControl
+ JECXZ @@a1
+ CMP ECX, EBX
+ JZ @@a1
+ XCHG EAX, ECX
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EAX].TControl.EV
+ MOV ECX, [EAX].TEvents.fLeave.TMethod.Code
+ {$ELSE}
+ MOV ECX, [EAX].TControl.EV.fLeave.TMethod.Code
+ {$ENDIF}
+ {$IFDEF NIL_EVENTS}
+ JECXZ @@a1
+ {$ENDIF}
+ XCHG EDX, EAX
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EDX].TEvents.fLeave.TMethod.Data
+ {$ELSE}
+ MOV EAX, [EDX].TControl.EV.fLeave.TMethod.Data
+ {$ENDIF}
+ CALL ECX
+@@a1: POP EAX
+
+ MOV [EAX].TControl.DF.FCurrentControl, EBX
+ XOR EAX, EAX
+
+ PUSH EDX
+@@2ret_EAX:
+ POP EDX
+
+@@chk_WM_KEYDOWN:
+ {$IFDEF KEY_PREVIEW_OR_ESC_CLOSE_DIALOGS}
+ CMP word ptr [EDI].TMsg.message, WM_KEYDOWN
+ {$IFDEF KEY_PREVIEW}
+ JNE @@chk_other_KEYMSGS
+ {$ELSE}
+ JNE @@ret0
+ {$ENDIF}
+
+ {$IFDEF KEY_PREVIEW}
+ MOV EAX, EBX
+ CALL TControl.ParentForm
+ CMP EAX, EBX
+ JE @@kp_end
+
+ {$IFDEF USE_FLAGS}
+ TEST [EAX].TControl.fFlagsG6, 1 shl G6_KeyPreview
+ {$ELSE}
+ CMP [EAX].TControl.fKeyPreview, 0
+ {$ENDIF}
+ JZ @@kp_end
+
+ {$IFDEF USE_FLAGS}
+ OR [EAX].TControl.fFlagsG4, 1 shl G4_Pushed
+ {$ELSE}
+ MOV [EAX].TControl.fKeyPreviewing, 1
+ {$ENDIF}
+ INC [EAX].TControl.DF.fKeyPreviewCount
+ PUSH EAX
+
+ PUSH [EDI].TMsg.lParam
+ PUSH [EDI].TMsg.wParam
+ PUSH WM_KEYDOWN
+ PUSH EAX
+ CALL TControl.Perform
+ POP EAX
+ DEC [EAX].TControl.DF.fKeyPreviewCount
+@@kp_end:
+ {$ENDIF}
+
+ {$IFDEF ESC_CLOSE_DIALOGS}
+ MOV EAX, EBX
+ CALL TControl.ParentForm
+ TEST [EAX].TControl.fExStyle, WS_EX_DLGMODALFRAME
+ JZ @@ecd_end
+ CMP [EDI].TMsg.wParam, 27
+ JNE @@ecd_end
+ PUSH 0
+ PUSH 0
+ PUSH WM_CLOSE
+ PUSH EAX
+ CALL TControl.Perform
+@@ecd_end:
+ {$ENDIF}
+
+@@ret0:
+ XOR EAX, EAX
+ {$IFDEF KEY_PREVIEW}
+ JMP @@ret_EAX
+@@chk_other_KEYMSGS:
+ MOVZX EAX, word ptr [EDI].TMsg.message
+ SUB AX, WM_KEYDOWN
+ JB @@ret0
+ CMP AX, 6
+ JA @@ret0
+ // all WM_KEYUP=$101, WM_CHAR=$102, WM_DEADCHAR=$103, WM_SYSKEYDOWN=$104,
+ // WM_SYSKEYUP=$105, WM_SYSCHAR=$106, WM_SYSDEADCHAR=$107
+ MOV EAX, EBX
+ CALL TControl.ParentForm
+ CMP EAX, EBX
+ JE @@ret0
+
+ {$IFDEF USE_FLAGS}
+ TEST [EAX].TControl.fFlagsG6, 1 shl G6_KeyPreview
+ {$ELSE}
+ CMP [EAX].fKeyPreview, 0
+ {$ENDIF}
+ JZ @@ret0
+
+ {$IFDEF USE_FLAGS}
+ OR [EAX].TControl.fFlagsG4, 1 shl G4_Pushed
+ {$ELSE}
+ MOV [EAX].TControl.fKeyPreviewing, 1
+ {$ENDIF}
+ INC [EAX].TControl.DF.fKeyPreviewCount
+ PUSH EAX
+ PUSH [EDI].TMsg.lParam
+ PUSH [EDI].TMsg.wParam
+ PUSH [EDI].TMsg.message
+ PUSH EAX
+ CALL TControl.Perform
+ POP EAX
+ DEC [EAX].TControl.DF.fKeyPreviewCount
+ XOR EAX, EAX
+ {$ENDIF KEY_PREVIEW}
+ {$ENDIF KEY_PREVIEW_OR_ESC_CLOSE_DIALOGS}
+
+@@ret_EAX:
+ POP EDI
+ POP ESI
+ POP EBX
+end;
+
+procedure TControl.DoClick;
+asm
+ PUSH EAX
+ CALL [EAX].PP.fControlClick
+ POP EDX
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EDX].TControl.EV
+ MOV ECX, [EAX].TEvents.fOnClick.TMethod.Code
+ {$ELSE}
+ MOV ECX, [EDX].EV.fOnClick.TMethod.Code
+ {$ENDIF}
+ {$IFDEF NIL_EVENTS}
+ JECXZ @@exit
+ {$ENDIF}
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EAX].TEvents.fOnClick.TMethod.Data
+ {$ELSE}
+ MOV EAX, [EDX].EV.fOnClick.TMethod.Data
+ {$ENDIF}
+ CALL ECX
+@@exit:
+end;
+
+function TControl.ParentForm: PControl;
+asm
+@@1: {$IFDEF USE_FLAGS}
+ TEST [EAX].fFlagsG3, 1 shl G3_IsControl
+ {$ELSE}
+ CMP [EAX].fIsControl, 0
+ {$ENDIF}
+ JZ @@exit
+ MOV EAX, [EAX].fParent
+ TEST EAX, EAX
+ JNZ @@1
+@@exit:
+end;
+
+procedure TControl.SetProgressColor(const Value: TColor);
+asm
+ PUSH EDX
+ PUSH EAX
+ MOV EAX, EDX
+ CALL Color2RGB
+ POP EDX
+ PUSH EDX
+ PUSH EAX
+ PUSH 0
+ PUSH PBM_SETBARCOLOR
+ PUSH EDX
+ CALL Perform
+ TEST EAX, EAX
+ POP EAX
+ POP EDX
+ JZ @@exit
+ MOV [EAX].fTextColor, EDX
+@@exit:
+end;
+
+function TControl.GetFont: PGraphicTool;
+asm
+ MOV ECX, [EAX].FFont
+ INC ECX
+ LOOP @@exit
+ PUSH EAX
+ CALL NewFont
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ POP EDX
+ PUSH EDX
+ PUSH EAX
+ XCHG eax, edx
+ CALL TObj.Add2AutoFree
+ POP EAX
+ {$ENDIF}
+ POP EDX
+ MOV [EDX].FFont, EAX
+ MOV ECX, [EDX].fTextColor
+ MOV [EAX].TGraphicTool.fData.Color, ECX
+ MOV [EAX].TGraphicTool.fOnGTChange.TMethod.Code, offset[FontChanged]
+ MOV [EAX].TGraphicTool.fOnGTChange.TMethod.Data, EDX
+ RET
+@@exit: XCHG EAX, ECX
+end;
+
+function TControl.GetBrush: PGraphicTool;
+asm
+ MOV ECX, [EAX].FBrush
+ INC ECX
+ LOOP @@exit
+ PUSH EAX
+ CALL NewBrush
+ POP EDX // @ Self
+ MOV [EDX].FBrush, EAX
+ MOV ECX, [EDX].fColor
+ MOV [EAX].TGraphicTool.fData.Color, ECX
+ MOV [EAX].TGraphicTool.fOnGTChange.TMethod.Code, offset[BrushChanged]
+ MOV [EAX].TGraphicTool.fOnGTChange.TMethod.Data, EDX
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ PUSH EAX
+ XCHG EAX, EDX
+ CALL TControl.Add2AutoFree
+ POP ECX
+ {$ENDIF}
+@@exit: XCHG EAX, ECX
+end;
+
+procedure TControl.FontChanged(Sender: PGraphicTool);
+asm
+ MOV ECX, [EDX].TGraphicTool.fData.Color
+ MOV [EAX].fTextColor, ECX
+ PUSH EAX
+ CALL [ApplyFont2Wnd_Proc]
+ POP EAX
+ CALL Invalidate
+end;
+
+procedure TControl.BrushChanged(Sender: PGraphicTool);
+asm
+ MOV ECX, [EDX].TGraphicTool.fData.Color
+ MOV [EAX].fColor, ECX
+ XOR ECX, ECX
+ XCHG ECX, [EAX].fTmpBrush
+ JECXZ @@inv
+ PUSH EAX
+ PUSH ECX
+ CALL DeleteObject
+ POP EAX
+@@inv: CALL Invalidate
+end;
+
+procedure DoApplyFont2Wnd( _Self: PControl );
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+
+ MOV ECX, [EBX].TControl.fFont
+ JECXZ @@exit
+ XCHG EAX, ECX
+
+ MOV ECX, [EBX].TControl.fHandle
+ JECXZ @@0
+
+ MOV EDX, [EAX].TGraphicTool.fData.Color
+ MOV [EBX].TControl.fTextColor, EDX
+
+ PUSH $FFFF
+ CALL TGraphicTool.GetHandle
+ PUSH EAX
+ PUSH WM_SETFONT
+ PUSH EBX
+ CALL TControl.Perform
+
+@@0:
+ XOR ECX, ECX
+ XCHG ECX, [EBX].TControl.fCanvas
+ JECXZ @@1
+
+ XCHG EAX, ECX
+ CALL TObj.RefDec
+@@1:
+ XCHG EAX, EBX
+ CALL TControl.DoAutoSize
+@@exit:
+ POP EBX
+end;
+
+function TControl.ResizeParent: PControl;
+asm
+ LEA EDX, [TControl.ResizeParentRight]
+ PUSH EDX
+ CALL EDX
+ CALL TControl.ResizeParentBottom
+end;
+
+function TControl.ResizeParentBottom: PControl;
+asm
+ PUSH EAX
+ PUSH EBX
+ MOV EBX, [EAX].fParent
+ TEST EBX, EBX
+ JZ @@exit
+
+ MOV EDX, [EAX].fBoundsRect.Bottom
+ MOVSX ECX, [EBX].fMargin
+ ADD EDX, ECX
+
+ {$IFDEF USE_FLAGS}
+ TEST [EBX].fFlagsG2, (1 shl G2_ChangedH)
+ JZ @@1
+ {$ELSE}
+ TEST [EBX].fChangedPosSz, 20h
+ JZ @@1
+ {$ENDIF}
+
+ PUSH EDX
+ MOV EAX, EBX
+ CALL GetClientHeight
+ POP EDX
+
+ CMP EDX, EAX
+ JE @@exit
+@@1:
+ MOV EAX, EBX
+ CALL TControl.SetClientHeight
+ {$IFDEF USE_FLAGS}
+ OR [EBX].fFlagsG2, (1 shl G2_ChangedH)
+ {$ELSE}
+ OR [EBX].fChangedPosSz, 20h
+ {$ENDIF}
+@@exit:
+ POP EBX
+ POP EAX
+end;
+
+function TControl.ResizeParentRight: PControl;
+asm
+ PUSH EAX
+ PUSH EBX
+ MOV EBX, [EAX].fParent
+ TEST EBX, EBX
+ JZ @@exit
+
+ MOV EDX, [EAX].fBoundsRect.Right
+ MOVSX ECX, [EBX].fMargin
+ ADD EDX, ECX
+
+ {$IFDEF USE_FLAGS}
+ TEST [EBX].fFlagsG2, (1 shl G2_ChangedW)
+ {$ELSE}
+ TEST [EBX].fChangedPosSz, 10h
+ {$ENDIF}
+ JZ @@1
+
+ PUSH EDX
+ MOV EAX, EBX
+ CALL GetClientWidth
+ POP EDX
+
+ CMP EDX, EAX
+ JLE @@exit
+@@1:
+ MOV EAX, EBX
+ CALL TControl.SetClientWidth
+ {$IFDEF USE_FLAGS}
+ OR [EBX].fFlagsG2, (1 shl G2_ChangedW)
+ {$ELSE}
+ OR [EBX].fChangedPosSz, 10h
+ {$ENDIF}
+@@exit:
+ POP EBX
+ POP EAX
+end;
+
+function TControl.GetClientHeight: Integer;
+asm
+ ADD ESP, -size_TRect
+ MOV EDX, ESP
+ CALL TControl.ClientRect
+ POP EDX
+ POP ECX // Top
+ POP EDX
+ POP EAX // Bottom
+ SUB EAX, ECX // Result = Bottom - Top
+end;
+
+function TControl.GetClientWidth: Integer;
+asm
+ ADD ESP, -size_TRect
+ MOV EDX, ESP
+ CALL TControl.ClientRect
+ POP ECX // Left
+ POP EDX
+ POP EAX // Right
+ SUB EAX, ECX // Result = Right - Left
+ POP EDX
+end;
+
+procedure TControl.SetClientHeight(const Value: Integer);
+asm
+ PUSH EBX
+ PUSH EDX
+
+ MOV EBX, EAX
+ CALL TControl.GetClientHeight
+ PUSH EAX
+ MOV EAX, EBX
+ CALL TControl.GetHeight // EAX = Height
+
+ POP EDX // EDX = ClientHeight
+ SUB EAX, EDX // EAX = Delta
+ POP EDX // EDX = Value
+ ADD EDX, EAX // EDX = Value + Delta
+ XCHG EAX, EBX // EAX = @Self
+ CALL TControl.SetHeight
+ POP EBX
+end;
+
+procedure TControl.SetClientWidth(const Value: Integer);
+asm
+ PUSH EBX
+ PUSH EDX
+
+ MOV EBX, EAX
+ CALL TControl.GetClientWidth
+ PUSH EAX
+ MOV EAX, EBX
+ CALL TControl.GetWidth // EAX = Width
+
+ POP EDX // EDX = ClientWidth
+ SUB EAX, EDX // EAX = Width - ClientWidth
+ POP EDX // EDX = Value
+ ADD EDX, EAX // EDX = Value + Delta
+ XCHG EAX, EBX // EAX = @Self
+ CALL TControl.SetWidth
+ POP EBX
+end;
+
+function TControl.CenterOnParent: PControl;
+asm
+ PUSHAD
+
+ XCHG ESI, EAX
+ MOV ECX, [ESI].fParent
+ JECXZ @@1
+ {$IFDEF USE_FLAGS}
+ TEST [ESI].fFlagsG3, 1 shl G3_IsControl
+ {$ELSE}
+ CMP [ESI].fIsControl, 0
+ {$ENDIF}
+ JNZ @@2
+
+@@1:
+ PUSH SM_CYSCREEN
+ CALL GetSystemMetrics
+ PUSH EAX
+
+ PUSH SM_CXSCREEN
+ CALL GetSystemMetrics
+ PUSH EAX
+
+ PUSH 0
+ PUSH 0 // ESP -> Rect( 0, 0, CX, CY )
+
+ JMP @@3
+
+@@2: ADD ESP, -size_TRect
+ MOV EDX, ESP
+ XCHG EAX, ECX
+ CALL TControl.ClientRect
+ // ESP -> ClientRect
+@@3: MOV EAX, ESI
+ CALL GetWindowHandle
+
+ MOV EAX, ESI
+ CALL GetWidth
+
+ POP EDX // left
+ ADD EAX, EDX // + width
+
+ POP EDI // top
+ POP EDX // right
+
+ SUB EDX, EAX
+ SAR EDX, 1
+
+ MOV EAX, ESI
+ CALL SetLeft
+
+ MOV EAX, ESI
+ CALL GetHeight
+
+ ADD EAX, EDI // height + top
+
+ POP EDX // bottom
+ SUB EDX, EAX
+ SAR EDX, 1
+
+ XCHG EAX, ESI
+ CALL SetTop
+
+ POPAD
+end;
+
+function TControl.GetHasBorder: Boolean;
+const style_mask = WS_BORDER or WS_THICKFRAME or WS_DLGFRAME;
+asm
+ CALL UpdateWndStyles
+ MOV EDX, [EAX].fStyle
+ AND EDX, style_mask
+ SETNZ DL
+ MOV EAX, [EAX].fExStyle
+ AND EAX, WS_EX_CLIENTEDGE
+ SETNZ AL
+ OR AL, DL
+end;
+
+function TControl.GetHasCaption: Boolean;
+const style_mask1 = (WS_POPUP or WS_DLGFRAME) shr 16;
+ style_mask2 = WS_CAPTION shr 16;
+asm
+ CALL UpdateWndStyles
+ MOV ECX, [EAX].fStyle + 2
+ MOV EDX, ECX
+ MOV AL, 1
+ AND DX, style_mask1
+ JZ @@1
+ AND CX, style_mask2
+ JNZ @@1
+ XOR EAX, EAX
+@@1:
+end;
+
+procedure TControl.SetHasCaption(const Value: Boolean);
+const style_mask = not (WS_BORDER or WS_THICKFRAME or WS_DLGFRAME or WS_CAPTION
+ or WS_MINIMIZEBOX or WS_MAXIMIZEBOX or WS_SYSMENU);
+ exstyle_mask = not (WS_EX_CONTROLPARENT or WS_EX_DLGMODALFRAME
+ or WS_EX_WINDOWEDGE or WS_EX_CLIENTEDGE);
+asm
+ PUSH EAX
+ PUSH EDX
+
+ CALL GetHasCaption
+ POP ECX
+ CMP AL, CL
+
+ POP EAX
+ JZ @@exit // Value = HasCaption
+
+ MOV EDX, [EAX].fStyle
+ DEC CL
+ JNZ @@1 // if not Value -> @@1
+
+ AND EDX, not WS_POPUP
+ OR EDX, WS_CAPTION
+ JMP @@set_style
+
+@@1:
+ {$IFDEF USE_FLAGS}
+ TEST [EAX].fFlagsG3, 1 shl G3_IsControl
+ {$ELSE}
+ CMP [EAX].fIsControl, 0
+ {$ENDIF}
+ JNZ @@2 // if fIsControl -> @@2
+
+ AND EDX, not (WS_CAPTION or WS_SYSMENU)
+ OR EDX, WS_POPUP
+ JMP @@3
+
+@@2:
+ AND EDX, not WS_CAPTION
+ OR EDX, WS_DLGFRAME
+
+@@3:
+ PUSH EDX
+
+ MOV EDX, [EAX].fExStyle
+ OR EDX, WS_EX_DLGMODALFRAME
+
+ PUSH EAX
+ CALL SetExStyle
+ POP EAX
+
+ POP EDX
+@@set_style:
+ CALL SetStyle
+@@exit:
+end;
+
+function TControl.GetCanResize: Boolean;
+asm
+ {$IFDEF USE_FLAGS}
+ TEST [EAX].fFlagsG1, (1 shl G1_PreventResize)
+ SETZ AL
+ {$ELSE}
+ MOV AL, [EAX].fPreventResize
+ {$IFDEF PARANOIA} DB $34,$01 {$ELSE} XOR AL, 1 {$ENDIF}
+ {$ENDIF USE_FLAGS}
+end;
+
+procedure TControl.SetCanResize( const Value: Boolean );
+asm
+ PUSH EBX
+ MOV EBX, EAX
+
+ CALL GetCanResize
+ CMP AL, DL
+
+ JZ @@exit // Value = CanResize
+ {$IFDEF USE_FLAGS}
+ // AL:bit0 = can resize
+ SHL AL, G1_PreventResize
+ AND [EBX].fFlagsG1, not (1 shl G1_PreventResize)
+ OR [EBX].fFlagsG1, AL
+ {$ELSE}
+ MOV [EBX].fPreventResize, AL
+ {$ENDIF USE_FLAGS}
+ {$IFDEF CANRESIZE_THICKFRAME}
+ TEST DL, DL
+
+ MOV EDX, [EBX].fStyle
+ JZ @@set_thick
+
+ OR EDX, WS_THICKFRAME
+ JMP @@set_style
+
+@@set_thick:
+ AND EDX, not WS_THICKFRAME
+
+@@set_style:
+ MOV EAX, EBX
+ CALL SetStyle
+ {$ENDIF CANRESIZE_THICKFRAME}
+
+ {$IFDEF FIX_WIDTH_HEIGHT}
+ MOV EAX, EBX
+ CALL GetWindowHandle
+
+ MOV EAX, EBX
+ CALL GetWidth
+ MOV [EBX].FFixWidth, EAX
+
+ MOV EAX, EBX
+ CALL GetHeight
+ MOV [EBX].FFixHeight, EAX
+ {$ENDIF FIX_WIDTH_HEIGHT}
+
+ XCHG EAX, EBX
+ MOV EDX, offset[WndProcCanResize]
+ CALL TControl.AttachProc
+@@exit:
+ POP EBX
+end;
+
+function TControl.GetStayOnTop: Boolean;
+asm
+ CALL UpdateWndStyles
+ TEST byte ptr [EAX].fExStyle, WS_EX_TOPMOST
+ SETNZ AL
+end;
+
+procedure TControl.SetStayOnTop(const Value: Boolean);
+asm
+ PUSH EAX
+ PUSH EDX
+
+ CALL GetStayOnTop
+ POP ECX
+ MOVZX ECX, CL
+ CMP AL, CL
+
+ POP EAX
+ JZ @@exit // Value = StayOnTop
+
+ MOV EDX, [EAX].fHandle
+ TEST EDX, EDX
+ JZ @@1
+
+ PUSH SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE
+ XOR EAX, EAX
+ PUSH EAX
+ PUSH EAX
+ PUSH EAX
+ PUSH EAX
+ DEC ECX
+ DEC ECX
+ PUSH ECX
+
+ PUSH EDX
+ CALL SetWindowPos
+ RET
+
+@@1:
+ JECXZ @@1and
+
+ OR byte ptr [EAX].fExStyle, WS_EX_TOPMOST
+ RET
+
+@@1and: AND byte ptr [EAX].fExStyle, not WS_EX_TOPMOST
+
+@@exit:
+end;
+
+function TControl.UpdateWndStyles: PControl;
+asm
+ MOV ECX, [EAX].fHandle
+ JECXZ @@exit
+
+ PUSH EBX
+
+ XCHG EBX, EAX
+ PUSH GCL_STYLE
+ PUSH ECX
+
+ PUSH GWL_EXSTYLE
+ PUSH ECX
+
+ PUSH GWL_STYLE
+ PUSH ECX
+
+ CALL GetWindowLong
+ MOV [EBX].fStyle, EAX
+
+ CALL GetWindowLong
+ MOV [EBX].fExStyle, EAX
+
+ CALL GetClassLong
+ MOV [EBX].fClsStyle, EAX
+ XCHG EAX, EBX
+ POP EBX
+@@exit:
+end;
+
+function TControl.GetChecked: Boolean;
+asm
+ TEST [EAX].DF.fBitBtnOptions, 8 //1 shl Ord(bboFixed)
+ JZ @@1
+ {$IFDEF USE_FLAGS}
+ TEST [EAX].fFlagsG4, 1 shl G4_Checked
+ SETNZ AL
+ {$ELSE}
+ MOV AL, [EAX].fChecked
+ {$ENDIF}
+ RET
+@@1:
+ PUSH 0
+ PUSH 0
+ PUSH BM_GETCHECK
+ PUSH EAX
+ CALL Perform
+@@exit:
+end;
+
+procedure TControl.Set_Checked(const Value: Boolean);
+asm
+ TEST [EAX].DF.fBitBtnOptions, 8 //1 shl Ord(bboFixed)
+ JZ @@1
+ {$IFDEF USE_FLAGS}
+ SHL DL, G4_Checked
+ AND [EAX].fFlagsG4, not(1 shl G4_Checked)
+ OR [EAX].fFlagsG4, DL
+ {$ELSE}
+ MOV [EAX].fChecked, DL
+ {$ENDIF}
+ JMP Invalidate
+@@1:
+ PUSH 0
+ MOVZX EDX, DL
+ PUSH EDX
+ PUSH BM_SETCHECK
+ PUSH EAX
+ Call Perform
+end;
+
+function TControl.SetRadioChecked: PControl;
+asm
+ {$IFDEF USE_FLAGS}
+ PUSH DWORD PTR[EAX].fStyle
+ PUSH EAX
+ AND [EAX].fStyle.f2_Style, not(1 shl F2_Tabstop)
+ CALL DoClick
+ POP EAX
+ POP DWORD PTR[EAX].fStyle
+ {$ELSE}
+ PUSH EAX
+ PUSH DWORD PTR[EAX].fTabStop
+ MOV [EAX].fTabStop, 0
+@@1:
+ CALL DoClick
+ POP EDX
+ POP EAX
+ MOV [EAX].fTabStop, DL
+ {$ENDIF USE_FLAGS}
+end;
+
+function TControl.GetSelStart: Integer;
+asm
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EAX].fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.aGetSelRange
+ {$ELSE}
+ MOVZX ECX, [EAX].fCommandActions.aGetSelRange
+ {$ENDIF}
+ JECXZ @@exit
+ XOR EDX, EDX
+ PUSH EDX // space for Result
+ PUSH EDX // 0
+ LEA EDX, [ESP+4]
+ PUSH EDX // @ Result
+ PUSH ECX // EM_GETSEL
+ PUSH EAX
+ CALL Perform
+ POP ECX // Result
+@@exit:
+ XCHG EAX, ECX
+end;
+
+function TControl.GetSelLength: Integer;
+asm
+ XOR EDX, EDX
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EAX].fCommandActions
+ MOVZX ECX, word ptr[ECX].TCommandActionsObj.aGetSelCount
+ {$ELSE}
+ MOVZX ECX, word ptr[EAX].fCommandActions.aGetSelCount
+ {$ENDIF}
+ JECXZ @@ret_ecx
+
+ CMP CX, EM_GETSEL
+ JNZ @@1
+ PUSH EDX
+ PUSH EDX
+ MOV EDX, ESP
+ PUSH EDX
+ ADD EDX, 4
+ PUSH EDX
+ PUSH ECX
+ PUSH EAX
+ CALL Perform
+ POP ECX
+ POP EDX
+ SUB ECX, EDX
+@@ret_ecx:
+ XCHG EAX, ECX
+ RET
+
+@@1: // LB_GETSELCOUNT, LVM_GETSELECTEDCOUNT
+ PUSH EDX // 0
+ PUSH EDX // 0
+ PUSH ECX // aGetSelCount
+ PUSH EAX // Handle
+ CALL Perform
+@@fin_EAX:
+end;
+
+procedure TControl.SetSelLength(const Value: Integer);
+asm
+ PUSH EBP
+ MOV EBP, ESP
+ PUSH EAX
+ PUSH EDX
+ CALL GetSelStart
+ POP ECX
+ POP EDX
+ ADD ECX, EAX
+ PUSH ECX
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EDX].fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.aSetSelRange
+ {$ELSE}
+ MOVZX ECX, [EDX].fCommandActions.aSetSelRange
+ {$ENDIF}
+ JECXZ @@check_ex
+ PUSH EAX
+ JMP @@perform
+
+@@check_ex:
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EDX].fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.aExSetSelRange
+ {$ELSE}
+ MOVZX ECX, [EDX].fCommandActions.aExSetSelRange
+ {$ENDIF}
+ JECXZ @@exit
+ PUSH EAX
+ PUSH ESP
+ PUSH 0
+@@perform:
+ PUSH ECX
+ PUSH EDX
+ CALL Perform
+@@exit: MOV ESP, EBP
+ POP EBP
+end;
+
+function TControl.GetItemsCount: Integer;
+asm
+ PUSH 0
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EAX].fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.aGetCount
+ {$ELSE}
+ MOVZX ECX, [EAX].fCommandActions.aGetCount
+ {$ENDIF}
+ JECXZ @@ret_0
+ PUSH 0
+ PUSH ECX
+ PUSH EAX
+ CALL Perform
+ PUSH EAX
+
+@@ret_0:
+ POP EAX
+end;
+
+procedure HelpConvertItem2Pos;
+asm
+ JECXZ @@exit
+ PUSH 0
+ PUSH EDX
+ PUSH ECX
+ PUSH EAX
+ CALL TControl.Perform
+ {XOR EDX, EDX
+ TEST EAX, EAX
+ JL @@exit
+ RET}
+ XCHG EDX, EAX
+@@exit:
+ XCHG EAX, EDX
+end;
+
+function TControl.Item2Pos(ItemIdx: Integer): DWORD;
+asm
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EAX].fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.bItem2Pos
+ {$ELSE}
+ MOVZX ECX, BYTE PTR [EAX].fCommandActions.bItem2Pos
+ {$ENDIF}
+ JMP HelpConvertItem2Pos
+end;
+
+function TControl.Pos2Item(Pos: Integer): DWORD;
+asm
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EAX].fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.bPos2Item
+ {$ELSE}
+ MOVZX ECX, BYTE PTR [EAX].fCommandActions.bPos2Item
+ {$ENDIF}
+ JMP HelpConvertItem2Pos
+end;
+
+procedure TControl.Delete(Idx: Integer);
+asm
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EAX].fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.aDeleteItem
+ {$ELSE}
+ MOVZX ECX, [EAX].fCommandActions.aDeleteItem
+ {$ENDIF}
+ JECXZ @@exit
+
+ PUSH 0
+ PUSH EDX
+ PUSH ECX
+ PUSH EAX
+ CALL Perform
+@@exit:
+end;
+
+function TControl.GetItemSelected(ItemIdx: Integer): Boolean;
+asm
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EAX].fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.aGetSelected
+ {$ELSE}
+ MOVZX ECX, [EAX].fCommandActions.aGetSelected
+ {$ENDIF}
+ JECXZ @@check_range
+
+ PUSH 1
+ CMP CL, CB_GETCURSEL and $FF
+ JNZ @@1
+ MOV [ESP], EDX
+@@1:
+ PUSH LVIS_SELECTED // 2
+ PUSH EDX
+ PUSH ECX
+ PUSH EAX
+ CALL Perform
+ POP EDX
+ CMP EAX, EDX
+ SETZ AL
+ RET
+
+@@check_range:
+ PUSH EBX
+ PUSH ESI
+ XCHG ESI, EDX
+ MOV EBX, EAX
+
+ CALL GetSelStart
+ XCHG EBX, EAX
+ CALL GetSelLength
+
+ SUB ESI, EBX
+ JL @@ret_false
+
+ CMP EAX, ESI
+@@ret_false:
+ SETGE AL
+ POP ESI
+ POP EBX
+end;
+
+procedure TControl.SetItemSelected(ItemIdx: Integer; const Value: Boolean);
+asm
+ PUSH EDX
+ PUSH ECX
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EAX].fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.aSetSelected
+ {$ELSE}
+ MOVZX ECX, [EAX].fCommandActions.aSetSelected
+ {$ENDIF}
+ JECXZ @@chk_aSetCurrent
+
+@@0:
+ PUSH ECX
+ PUSH EAX
+ CALL Perform
+ RET
+
+@@chk_aSetCurrent:
+ POP ECX
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EAX].fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.aSetCurrent
+ {$ELSE}
+ MOVZX ECX, [EAX].fCommandActions.aSetCurrent
+ {$ENDIF}
+ JECXZ @@chk_aSetSelRange
+
+ POP EDX
+ PUSH 0
+ JMP @@3
+
+@@chk_aSetSelRange:
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EAX].fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.aSetSelRange
+ {$ELSE}
+ MOVZX ECX, [EAX].fCommandActions.aSetSelRange
+ {$ENDIF}
+ JECXZ @@chk_aExSetSelRange
+@@3:
+ PUSH EDX
+ JMP @@0
+
+@@else: MOV [EAX].FCurIndex, EDX
+ CALL TControl.Invalidate
+ JMP @@exit
+
+@@chk_aExSetSelRange:
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EAX].fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.aExSetSelRange
+ {$ELSE}
+ MOVZX ECX, [EAX].fCommandActions.aExSetSelRange
+ {$ENDIF}
+ JECXZ @@else
+
+ PUSH EDX
+ PUSH ESP
+ PUSH 0
+ PUSH ECX
+ PUSH EAX
+ CALL Perform
+ POP ECX
+
+@@exit:
+ POP ECX
+end;
+
+procedure TControl.SetCtl3D(const Value: Boolean);
+asm
+ AND [EAX].fCtl3D_child, not 1
+ OR [EAX].fCtl3D_child, DL
+
+ PUSHAD
+ CALL UpdateWndStyles
+ POPAD
+
+ MOV ECX, [EAX].fExStyle
+ DEC DL
+ MOV EDX, [EAX].fStyle
+ JNZ @@1
+ AND EDX, not WS_BORDER
+ OR CH, WS_EX_CLIENTEDGE shr 8
+ JMP @@2
+@@1:
+ OR EDX, WS_BORDER
+ AND CH, not(WS_EX_CLIENTEDGE shr 8)
+@@2:
+ PUSH ECX
+ PUSH EAX
+ CALL SetStyle
+ POP EAX
+ POP EDX
+ JMP SetExStyle
+@@exit:
+end;
+
+function TControl.Shift(dX, dY: Integer): PControl;
+asm
+ PUSHAD
+ ADD EDX, [EAX].fBoundsRect.TRect.Left
+ CALL SetLeft
+ POPAD
+ PUSH EAX
+ MOV EDX, [EAX].fBoundsRect.TRect.Top
+ ADD EDX, ECX
+ CALL SetTop
+ POP EAX
+end;
+
+function Tabulate2Control( Self_: PControl; Key: DWORD; checkOnly: Boolean ): Boolean;
+const tk_Tab = 1;
+ tk_LR = 2;
+ tk_UD = 4;
+ tk_PuPd= 8;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ PUSH ESI
+ MOV ESI, offset[@@data]
+ XOR EAX, EAX
+@@loop:
+ LODSW
+ TEST EAX, EAX
+ JZ @@exit_false
+
+ CMP AL, DL
+ JNZ @@loop
+
+ TEST [EBX].TControl.fLookTabKeys, AH
+ JZ @@exit_false
+
+ TEST CL, CL
+ JNZ @@exit_true
+
+ MOV DH, AH
+ PUSH EDX
+ XCHG EAX, EBX
+ CALL TControl.ParentForm
+ XCHG ESI, EAX
+ POP EAX
+
+ CMP AL, 9
+ JNZ @@test_flag
+
+ PUSH EAX
+ PUSH VK_SHIFT
+ CALL GetKeyState
+ POP EDX
+
+ AND AH, $80
+ OR AH, DH
+@@test_flag:
+ {XOR EDX, EDX
+ INC EDX
+ ADD AH, AH
+ JNC @@tabul_1
+ NEG EDX
+@@tabul_1:} //AH<80 //AH>=80
+ ADD AH, AH // //
+ SBB EDX, EDX //EDX=0 //EDX=-1
+ ADD EDX, EDX // 0 // -2
+ INC EDX // 1 // -1
+
+ XCHG EAX, ESI
+ CALL Tabulate2Next
+@@exit_true:
+ MOV AL, 1
+ POP ESI
+ POP EBX
+ RET
+
+@@data:
+ DB VK_TAB, tk_Tab, VK_LEFT, tk_LR or $80, VK_RIGHT, tk_LR
+ DB VK_UP, tk_UD or $80, VK_DOWN, tk_UD
+ DB VK_PRIOR, tk_PuPd or $80, VK_NEXT, tk_PuPd, 0, 0
+
+@@exit_false:
+ XOR EAX, EAX
+ POP ESI
+ POP EBX
+ RET
+end;
+
+function TControl.Tabulate: PControl;
+asm
+ PUSH EAX
+ CALL ParentForm
+ TEST EAX, EAX
+ JZ @@exit
+ MOV [EAX].PP.fGotoControl, offset[Tabulate2Control]
+@@exit: POP EAX
+end;
+
+function TControl.TabulateEx: PControl;
+asm
+ PUSH EAX
+ CALL ParentForm
+ TEST EAX, EAX
+ JZ @@exit
+ MOV [EAX].PP.fGotoControl, offset[Tabulate2ControlEx]
+@@exit: POP EAX
+end;
+
+function TControl.GetCurIndex: Integer;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ MOV EAX, [EBX].fCurIndex
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EBX].fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.aGetCurrent
+ {$ELSE}
+ MOVZX ECX, [EBX].fCommandActions.aGetCurrent
+ {$ENDIF}
+ JECXZ @@exit
+ XOR EAX, EAX
+ CDQ
+ CMP CX, LVM_GETNEXTITEM
+ JNE @@0
+ INC EAX
+ INC EAX
+ JMP @@1
+@@0:
+ CMP CL, EM_LINEINDEX and $FF
+ JNZ @@2
+@@1:
+ DEC EDX
+@@2:
+ PUSH EAX
+ PUSH EDX
+ PUSH ECX
+ PUSH EBX
+ CALL Perform
+
+@@exit: POP EBX
+end;
+
+{procedure TControl.SetCurIndex(const Value: Integer);
+asm
+ MOVZX ECX, [EAX].fCommandActions.aSetCurrent
+ JECXZ @@set_item_sel
+ PUSHAD
+ PUSH 0
+ PUSH EDX
+ PUSH ECX
+ PUSH EAX
+ CALL Perform
+ POPAD
+ CMP CX, TCM_SETCURSEL
+ JNE @@exit
+ PUSH TCN_SELCHANGE
+ PUSH EAX // idfrom doesn't matter
+ PUSH [EAX].fHandle
+ PUSH ESP
+ PUSH 0
+ PUSH WM_NOTIFY
+ PUSH EAX
+ CALL Perform
+ POP ECX
+ POP ECX
+ POP ECX
+@@exit:
+ RET
+@@set_item_sel:
+ INC ECX
+ CALL SetItemSelected
+end;}
+
+procedure TControl.SetCurIndex(const Value: Integer); // fix av
+asm
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EAX].fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.aSetCurrent
+ {$ELSE}
+ MOVZX ECX, [EAX].fCommandActions.aSetCurrent
+ {$ENDIF}
+ JECXZ @@set_item_sel
+ PUSH ECX //+aSetCurrent
+ PUSH EAX //+self
+ PUSH 0
+ PUSH EDX
+ PUSH ECX
+ PUSH EAX
+ CALL Perform
+ POP EDX //+self
+ POP ECX //+aSetCurrent
+ CMP CX, TCM_SETCURSEL
+ JNE @@exit
+ MOV [EDX].fCurIndex,EAX
+ PUSH TCN_SELCHANGE // NMHdr.code
+ PUSH EDX // NMHdr.idfrom - doesn't matter
+ PUSH [EDX].fHandle // NMHdr.hwndFrom
+ PUSH ESP
+ PUSH 0
+ PUSH WM_NOTIFY
+ PUSH EDX
+ CALL Perform
+ ADD ESP,12 //NMHdr destroy
+@@exit:
+ RET
+@@set_item_sel:
+ INC ECX
+ CALL SetItemSelected
+end;
+
+function TControl.GetTextAlign: TTextAlign;
+asm
+ PUSH EAX
+ CALL UpdateWndStyles
+ MOV ECX, [EAX].fStyle
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV EDX, [EAX].fCommandActions
+ MOV EDX, dword ptr [EDX].TCommandActionsObj.aTextAlignRight
+ {$ELSE}
+ MOV EDX, dword ptr [EAX].fCommandActions.aTextAlignRight
+ {$ENDIF}
+ XOR EAX, EAX
+ AND DX, CX
+ JNZ @@ret_1
+ SHR EDX, 16
+ AND ECX, EDX
+ JNZ @@ret_2
+ POP EAX
+ MOVZX EAX, [EAX].fTextAlign
+ RET
+
+@@ret_2:INC EAX
+@@ret_1:INC EAX
+@@ret_0:POP ECX
+end;
+
+procedure TControl.SetTextAlign(const Value: TTextAlign);
+asm
+ {$IFDEF COMMANDACTIONS_OBJ}
+ PUSH EBX
+ {$ENDIF}
+ MOV [EAX].fTextAlign, DL
+ XOR ECX, ECX
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV EBX, [EAX].fCommandActions
+ MOV CX, [EBX].TCommandActionsObj.aTextAlignLeft
+ OR CX, [EBX].TCommandActionsObj.aTextAlignCenter
+ OR CX, [EBX].TCommandActionsObj.aTextAlignRight
+ {$ELSE}
+ MOV CX, [EAX].fCommandActions.aTextAlignLeft
+ OR CX, [EAX].fCommandActions.aTextAlignCenter
+ OR CX, [EAX].fCommandActions.aTextAlignRight
+ {$ENDIF}
+ NOT ECX
+ AND ECX, [EAX].fStyle
+
+ AND EDX, 3
+ {$IFDEF COMMANDACTIONS_OBJ}
+ OR CX, [EBX + EDX * 2].TCommandActionsObj.aTextAlignLeft
+ MOV DL, BYTE PTR [EBX].TCommandActionsObj.bTextAlignMask
+ {$ELSE}
+ OR CX, [EAX + EDX * 2].fCommandActions.aTextAlignLeft
+ MOV DL, BYTE PTR [EAX].fCommandActions.bTextAlignMask
+ {$ENDIF}
+
+ NOT EDX
+ AND EDX, ECX
+ CALL SetStyle
+ {$IFDEF COMMANDACTIONS_OBJ}
+ POP EBX
+ {$ENDIF}
+end;
+
+function TControl.GetVerticalAlign: TVerticalAlign;
+asm
+ PUSH EAX
+ CALL UpdateWndStyles
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV EDX, [EAX].fCommandActions
+ MOV EDX, dword ptr [EDX].TCommandActionsObj.bVertAlignCenter
+ {$ELSE}
+ MOV EDX, dword ptr [EAX].fCommandActions.bVertAlignCenter
+ {$ENDIF}
+ MOV ECX, [EAX].fStyle
+ XOR EAX, EAX
+ MOV DH, DL
+ AND DL, CH
+ JZ @@1
+ CMP DL, DH
+ JE @@ret_0
+@@1: SHR EDX, 16
+ MOV DH, DL
+ AND DL, CH
+ JZ @@2
+ CMP DL, DH
+ JE @@ret_2
+@@2: POP EAX
+ MOVZX EAX, [EAX].fVerticalAlign
+ RET
+@@ret_2:INC EAX
+@@ret_1:INC EAX
+@@ret_0:POP ECX
+end;
+
+procedure TControl.SetVerticalAlign(const Value: TVerticalAlign);
+asm
+ MOVZX EDX, DL
+ MOV [EAX].fVerticalAlign, DL
+
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EAX].fCommandActions
+ MOVZX ECX, byte ptr [ECX+EDX].TCommandActionsObj.bVertAlignTop
+ {$ELSE}
+ MOVZX ECX, byte ptr [EAX+EDX].fCommandActions.bVertAlignTop
+ {$ENDIF}
+ SHL ECX, 8
+
+ MOV EDX, [EAX].fStyle
+ AND DH, $F3
+ OR EDX, ECX
+
+ CALL SetStyle
+end;
+
+function TControl.Dc2Canvas( Sender: PCanvas ): HDC;
+asm
+ MOV ECX, [EAX].fPaintDC
+ JECXZ @@chk_fHandle
+
+ PUSH ECX
+ XCHG EAX, EDX // EAX <= Sender
+ MOV EDX, ECX // EDX <= fPaintDC
+ PUSH EAX
+ CALL TCanvas.SetHandle
+ POP EAX
+ MOV [EAX].TCanvas.fIsPaintDC, 1
+ POP ECX
+@@ret_ECX:
+ XCHG EAX, ECX
+ RET
+
+@@chk_fHandle:
+ MOV ECX, [EDX].TCanvas.fHandle
+ INC ECX
+ LOOP @@ret_ECX
+
+ CALL GetWindowHandle
+ PUSH EAX
+ CALL GetDC
+end;
+
+function TControl.GetCanvas: PCanvas;
+asm
+ PUSH EBX
+ PUSH ESI
+ {$IFDEF SAFE_CODE}
+ MOV EBX, EAX
+ CALL CreateWindow
+ {$ELSE}
+ XCHG EBX, EAX
+ {$ENDIF}
+
+ MOV ESI, [EBX].fCanvas
+ TEST ESI, ESI
+ JNZ @@exit
+
+ XOR EAX, EAX
+ CALL NewCanvas
+ MOV [EBX].fCanvas, EAX
+ MOV [EAX].TCanvas.fOwnerControl, EBX
+ MOV [EAX].TCanvas.fOnGetHandle.TMethod.Code, offset[ DC2Canvas ]
+ MOV [EAX].TCanvas.fOnGetHandle.TMethod.Data, EBX
+ XCHG ESI, EAX
+
+ MOV ECX, [EBX].fFont
+ JECXZ @@exit
+
+ MOV EAX, [ESI].TCanvas.fFont
+ MOV EDX, ECX
+ CALL TGraphicTool.Assign
+ MOV [ESI].TCanvas.fFont, EAX
+
+ MOV ECX, [EBX].fBrush
+ JECXZ @@exit
+
+ MOV EAX, [ESI].TCanvas.fBrush
+ MOV EDX, ECX
+ CALL TGraphicTool.Assign
+ MOV [ESI].TCanvas.fBrush, EAX
+
+@@exit: XCHG EAX, ESI
+ POP ESI
+ POP EBX
+end;
+
+procedure TControl.SetDoubleBuffered(const Value: Boolean);
+asm
+ {$IFDEF USE_FLAGS}
+ TEST [EAX].fFlagsG1, 1 shl G1_CanNotDoubleBuf
+ JNZ @@exit
+ {$ELSE}
+ CMP [EAX].fCannotDoubleBuf, 0
+ JNZ @@exit
+ {$ENDIF}
+ {$IFDEF USE_FLAGS}
+ SHL DL, G2_DoubleBuffered
+ AND [EAX].fFlagsG2, not(1 shl G2_DoubleBuffered)
+ OR [EAX].fFlagsG2, DL
+ {$ELSE}
+ MOV [EAX].fDoubleBuffered, DL
+ {$ENDIF}
+ MOV EDX, offset[WndProcTransparent]
+ CALL TControl.AttachProc
+ {$IFnDEF SMALLEST_CODE}
+ LEA EAX, [TransparentAttachProcExtension]
+ MOV [Global_AttachProcExtension], EAX
+ {$ENDIF}
+@@exit:
+end;
+
+procedure TControl.SetTransparent(const Value: Boolean);
+asm
+ MOV ECX, [EAX].fParent
+ JECXZ @@exit
+ {$IFDEF USE_FLAGS}
+ AND [EAX].fFlagsG2, not(1 shl G2_Transparent)
+ TEST DL, DL
+ JZ @@exit
+ OR [EAX].fFlagsG2, 1 shl G2_Transparent
+ {$ELSE}
+ MOV [EAX].fTransparent, DL
+ TEST DL, DL
+ JZ @@exit
+ {$ENDIF}
+
+{$IFDEF GRAPHCTL_XPSTYLES}
+ CMP AppTheming, FALSE
+ JNE @@not_th
+ {$IFDEF USE_FLAGS}
+ OR [EAX].fFlagsG3, G3_ClassicTransparent
+ {$ELSE}
+ MOV [EAX].fClassicTransparent, DL
+ {$ENDIF USE_FLAGS}
+@@not_th:
+{$ENDIF}
+
+ PUSH EAX
+ XCHG EAX, ECX
+ CALL SetDoubleBuffered
+ POP EAX
+ MOV EDX, offset[WndProcTransparent]
+ CALL AttachProc
+@@exit:
+end;
+
+function _NewTrayIcon: PTrayIcon;
+begin
+ New(Result,Create);
+end;
+function NewTrayIcon( Wnd: PControl; Icon: HIcon ): PTrayIcon;
+asm
+ PUSH EBX
+ PUSH EDX // push Icon
+ PUSH EAX // push Wnd
+ CALL _NewTrayIcon
+ XCHG EBX, EAX
+
+ MOV EAX, [FTrayItems]
+ TEST EAX, EAX
+ JNZ @@1
+ CALL NewList
+ MOV [FTrayItems], EAX
+@@1:
+ MOV EDX, EBX
+ CALL TList.Add
+
+ POP EAX //Wnd
+ MOV [EBX].TTrayIcon.fControl, EAX
+ POP [EBX].TTrayIcon.fIcon //Icon
+
+ MOV EDX, offset[WndProcTray]
+ TEST EAX, EAX
+ JZ @@2
+ CALL TControl.AttachProc
+@@2:
+ MOV DL, 1
+ MOV EAX, EBX
+ CALL TTrayIcon.SetActive
+ XCHG EAX, EBX
+ POP EBX
+end;
+
+function WndProcRecreateTrayIcons( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+asm //cmd //opd
+ MOV ECX, [fRecreateMsg]
+ CMP word ptr [EDX].TMsg.message, CX
+ JNE @@ret_false
+ PUSH ESI
+ MOV ESI, [FTrayItems]
+ MOV ECX, [ESI].TList.fCount
+ MOV ESI, [ESI].TList.fItems
+@@loo: PUSH ECX
+ LODSD
+ MOV DL, [EAX].TTrayIcon.fAutoRecreate
+ AND DL, [EAX].TTrayIcon.fActive
+ JZ @@nx
+ DEC [EAX].TTrayIcon.fActive
+ CALL TTrayIcon.SetActive
+@@nx: POP ECX
+ LOOP @@loo
+@@e_loo:POP ESI
+@@ret_false:
+ XOR EAX, EAX
+end;
+
+procedure TTrayIcon.SetAutoRecreate(const Value: Boolean);
+asm //cmd //opd
+ MOV [EAX].fAutoRecreate, DL
+ MOV EAX, [EAX].FControl
+ CALL TControl.ParentForm
+ MOV EDX, offset[WndProcRecreateTrayIcons]
+ CALL TControl.AttachProc
+ PUSH offset[TaskbarCreatedMsg]
+ CALL RegisterWindowMessage
+ MOV [fRecreateMsg], EAX
+end;
+
+destructor TTrayIcon.Destroy;
+asm
+ PUSH EBX
+ PUSH ESI
+ MOV EBX, EAX
+ XOR EDX, EDX
+ CALL SetActive
+
+ MOV ECX, [EBX].fIcon
+ JECXZ @@icon_destroyed
+ PUSH ECX
+ CALL DestroyIcon
+@@icon_destroyed:
+
+ MOV EDX, EBX
+ MOV ESI, [FTrayItems]
+ MOV EAX, ESI
+ CALL TList.IndexOf
+ TEST EAX, EAX
+ JL @@fin
+ XCHG EDX, EAX
+ MOV EAX, ESI
+ CALL TList.Delete
+ MOV EAX, [ESI].TList.fCount
+ TEST EAX, EAX
+ JNZ @@fin
+ XCHG EAX, [FTrayItems]
+ CALL TObj.RefDec
+@@fin: LEA EAX, [EBX].FTooltip
+ {$IFDEF UNICODE_CTRLS}
+ CALL System.@WStrClr
+ {$ELSE}
+ CALL System.@LStrClr
+ {$ENDIF}
+ XCHG EAX, EBX
+ CALL TObj.Destroy
+ POP ESI
+ POP EBX
+end;
+
+procedure TTrayIcon.SetActive(const Value: Boolean);
+asm
+ CMP [EAX].fActive, DL
+ JE @@exit
+ MOV ECX, [EAX].fIcon
+ JECXZ @@exit
+ PUSH EDX
+ PUSH EAX
+ MOV ECX, [EAX].FWnd
+ INC ECX
+ LOOP @@1
+ MOV ECX, [EAX].fControl
+ XOR EAX, EAX
+ JECXZ @@1
+ XCHG EAX, ECX
+ CALL TControl.GetWindowHandle
+@@1:
+ POP ECX
+ POP EDX
+ XCHG EAX, ECX
+ JECXZ @@exit
+ MOV [EAX].fActive, DL
+ MOVZX EDX, DL
+ XOR DL, 1
+ ADD EDX, EDX
+ CALL SetTrayIcon
+@@exit:
+end;
+
+procedure TTrayIcon.SetIcon(const Value: HIcon);
+asm
+ MOV ECX, [EAX].fIcon
+ CMP ECX, EDX
+ JE @@exit
+ MOV [EAX].fIcon, EDX
+ XOR EDX, EDX
+ JECXZ @@nim_add
+ INC EDX // NIM_MODIFY = 1
+@@nim_add:
+ MOVZX ECX, [EAX].fActive
+ JECXZ @@exit
+ CALL SetTrayIcon
+@@exit:
+end;
+
+function WndProcJustOne( Control: PControl; var Msg: TMsg; var Rslt: Integer ) : Boolean;
+asm
+ MOV ECX, [EDX].TMsg.message
+ SUB ECX, WM_CLOSE
+ JE @@1
+ SUB ECX, WM_NCDESTROY - WM_CLOSE
+ JNE @@exit
+@@1:
+ MOV ECX, [EDX].TMsg.hwnd
+ SUB ECX, [EAX].TControl.fHandle
+ JNE @@exit
+
+ XCHG ECX, [JustOneMutex]
+ JECXZ @@exit
+
+ PUSH ECX
+ CALL CloseHandle
+
+@@exit:
+ XOR EAX, EAX
+end;
+
+destructor TStrList.Destroy;
+asm
+ PUSH EAX
+ CALL Clear
+ POP EAX
+ CALL TObj.Destroy
+end;
+
+function TStrList.Add(const S: Ansistring): integer;
+asm
+ MOV ECX, EDX
+ MOV EDX, [EAX].fCount
+ PUSH EDX
+ CALL Insert
+ POP EAX
+end;
+
+procedure TStrList.AddStrings(Strings: PStrList);
+asm
+ PUSH EAX
+ XCHG EAX, EDX
+ PUSH 0
+ MOV EDX, ESP
+ CALL GetTextStr
+ POP EDX
+ POP EAX
+ MOV CL, 1
+ PUSH EDX
+ CALL SetText
+ CALL RemoveStr
+end;
+
+procedure TStrList.Assign(Strings: PStrList);
+asm
+ PUSHAD
+ CALL Clear
+ POPAD
+ JMP AddStrings
+end;
+
+procedure TStrList.Clear;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ MOV EDX, [EBX].fCount
+@@loo: DEC EDX
+ JL @@eloo
+ PUSH EDX
+ MOV EAX, EBX
+ CALL Delete
+ POP EDX
+ JMP @@loo
+@@eloo:
+ XOR EAX, EAX
+ MOV [EBX].fTextSiz, EAX
+ XCHG EAX, [EBX].fTextBuf
+ TEST EAX, EAX
+ JZ @@1
+ CALL System.@FreeMem
+ {$IFNDEF _D2orD3} //???//
+ XOR EAX, EAX // not needed for Delphi4 and Higher: if OK, EAX = 0
+ {$ENDIF}
+@@1: XCHG EAX, [EBX].fList
+ CALL TObj.RefDec
+ POP EBX
+end;
+
+{$IFDEF TStrList_Delete_ASM}
+procedure TStrList.Delete(Idx: integer);
+asm
+ DEC [EAX].fCount
+ PUSH EAX
+ MOV EAX, [EAX].fList
+ MOV ECX, [EAX].TList.fItems
+ PUSH dword ptr [ECX+EDX*4]
+ CALL TList.Delete
+ POP EAX
+ POP EDX
+ MOV ECX, [EDX].fTextSiz
+ JECXZ @@fremem
+ CMP EAX, [EDX].fTextBuf
+ JB @@fremem
+ ADD ECX, [EDX].fTextBuf
+ CMP EAX, ECX
+ JB @@exit
+@@fremem:
+ CALL System.@FreeMem
+@@exit:
+end;
+{$ENDIF}
+
+function TStrList.Get(Idx: integer): Ansistring;
+asm
+ PUSH ECX
+ MOV EAX, [EAX].fList
+ TEST EAX, EAX
+ JZ @@1
+ CALL TList.Get
+@@1: XCHG EDX, EAX
+ POP EAX
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX // TODO: safe?
+ {$ENDIF}
+ JMP System.@LStrFromPChar
+end;
+
+procedure TStrList.Insert(Idx: integer; const S: Ansistring);
+asm
+ PUSH EBX
+ PUSH EDX
+ PUSH ECX
+ XCHG EBX, EAX
+ MOV EAX, [EBX].fList
+ TEST EAX, EAX
+ JNZ @@1
+ CALL NewList
+ MOV [EBX].fList, EAX
+@@1:
+ POP EAX
+ PUSH EAX // push S
+ CALL System.@LStrLen
+ INC EAX
+ PUSH EAX // push L
+ CALL System.@GetMem
+ MOV byte ptr[EAX], 0
+ XCHG EDX, EAX
+ POP ECX
+ POP EAX
+ PUSH EDX // push Mem
+ TEST EAX, EAX
+ JE @@2
+ CALL System.Move
+@@2: POP ECX
+ POP EDX
+ MOV EAX, [EBX].fList
+ CALL TList.Insert
+ INC [EBX].fCount
+ POP EBX
+end;
+
+procedure TStrList.Put(Idx: integer; const Value: Ansistring);
+asm
+ PUSH EAX
+ PUSH EDX
+ CALL Insert
+ POP EDX
+ POP EAX
+ INC EDX
+ JMP Delete
+end;
+
+procedure LowerCaseStrFromPCharEDX;
+asm
+ { <- EDX = PChar string
+ -> [ESP] = LowerCase( PChar( EDX ) ),
+ EAX, EDX, ECX - ?
+ }
+ POP EAX
+ PUSH 0
+ PUSH EAX
+ LEA EAX, [ESP+4]
+ PUSH EAX
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX // TODO: fixme
+ {$ENDIF}
+ CALL System.@LStrFromPChar
+ POP EDX
+ MOV EAX, [EDX]
+ JMP LowerCase
+end;
+
+procedure TStrList.Sort(CaseSensitive: Boolean);
+asm
+ MOV [EAX].fCaseSensitiveSort, DL
+ MOV [EAX].fAnsiSort, 0
+ {$IFDEF SPEED_FASTER}
+ {$DEFINE SORT_STRLIST_ARRAY}
+ {$ENDIF}
+ {$IFDEF TLIST_FAST}
+ {$UNDEF SORT_STRLIST_ARRAY}
+ {$ENDIF}
+ {$IFDEF SORT_STRLIST_ARRAY}
+ MOV ECX, offset[StrComp]
+ CMP DL, 0
+ JNZ @@01
+ {$IFDEF SMALLER_CODE}
+ MOV ECX, offset[StrComp_NoCase]
+ {$ELSE}
+ MOV ECX, [StrComp_NoCase]
+ {$ENDIF}
+@@01:
+ MOV EAX, [EAX].fList
+ TEST EAX, EAX
+ JZ @@exit
+ MOV EDX, [EAX].TList.fCount
+ CMP EDX, 1
+ JLE @@02
+ MOV EAX, [EAX].TList.fItems
+ CALL SortArray
+@@02:
+ {$ELSE}
+ PUSH Offset[TStrList.Swap]
+ MOV ECX, Offset[CompareStrListItems_Case]
+ CMP DL, 0
+ JNZ @1
+ MOV ECX, Offset[CompareStrListItems_NoCase]
+@1: MOV EDX, [EAX].fCount
+ CALL SortData
+ {$ENDIF}
+@@exit:
+end;
+
+procedure TStrList.MergeFromFile(const FileName: KOLString);
+asm
+ PUSH EAX
+ XCHG EAX, EDX
+ CALL NewReadFileStream
+ XCHG EDX, EAX
+ POP EAX
+ MOV CL, 1
+ PUSH EDX
+ CALL LoadFromStream
+ POP EAX
+ JMP TObj.RefDec
+end;
+
+procedure TStrList.SaveToStream(Stream: PStream);
+asm
+ PUSH EDX
+ PUSH 0
+ MOV EDX, ESP
+ CALL GetTextStr
+ POP EAX
+ PUSH EAX
+ CALL System.@LStrLen
+ XCHG ECX, EAX
+ POP EDX
+ POP EAX
+ PUSH EDX
+ JECXZ @@1
+ CALL TStream.Write
+@@1:
+ CALL RemoveStr
+end;
+
+procedure SortData( const Data: Pointer; const uNElem: Dword;
+ const CompareFun: TCompareEvent;
+ const SwapProc: TSwapEvent );
+asm
+ CMP EDX, 2
+ JL @@exit
+
+ PUSH EAX // [EBP-4] = Data
+ PUSH ECX // [EBP-8] = CompareFun
+ PUSH EBX // EBX = pivotP
+ XOR EBX, EBX
+ INC EBX // EBX = 1 to pass to qSortHelp as PivotP
+ MOV EAX, EDX // EAX = nElem
+ CALL @@qSortHelp
+ POP EBX
+ POP ECX
+ POP ECX
+@@exit:
+ POP EBP
+ RET 4
+
+@@qSortHelp:
+ PUSH EBX // EBX (in) = PivotP
+ PUSH ESI // ESI = leftP
+ PUSH EDI // EDI = rightP
+
+@@TailRecursion:
+ CMP EAX, 2
+ JG @@2
+ JNE @@exit_qSortHelp
+ LEA ECX, [EBX+1]
+ MOV EDX, EBX
+ CALL @@Compare
+ JLE @@exit_qSortHelp
+@@swp_exit:
+ CALL @@Swap
+@@exit_qSortHelp:
+ POP EDI
+ POP ESI
+ POP EBX
+ RET
+
+ // ESI = leftP
+ // EDI = rightP
+@@2: LEA EDI, [EAX+EBX-1]
+ MOV ESI, EAX
+ SHR ESI, 1
+ ADD ESI, EBX
+ MOV ECX, ESI
+ MOV EDX, EDI
+ CALL @@CompareLeSwap
+ MOV EDX, EBX
+ CALL @@Compare
+
+ JG @@4
+ CALL @@Swap
+ JMP @@5
+@@4: MOV ECX, EBX
+ MOV EDX, EDI
+ CALL @@CompareLeSwap
+@@5:
+ CMP EAX, 3
+ JNE @@6
+ MOV EDX, EBX
+ MOV ECX, ESI
+ JMP @@swp_exit
+@@6: // classic Horae algorithm
+
+ PUSH EAX // EAX = pivotEnd
+ LEA EAX, [EBX+1]
+ MOV ESI, EAX
+@@repeat:
+ MOV EDX, ESI
+ MOV ECX, EBX
+ CALL @@Compare
+ JG @@while2
+@@while1:
+ JNE @@7
+ MOV EDX, ESI
+ MOV ECX, EAX
+ CALL @@Swap
+ INC EAX
+@@7:
+ CMP ESI, EDI
+ JGE @@qBreak
+ INC ESI
+ JMP @@repeat
+@@while2:
+ CMP ESI, EDI
+ JGE @@until
+ MOV EDX, EBX
+ MOV ECX, EDI
+ CALL @@Compare
+ JGE @@8
+ DEC EDI
+ JMP @@while2
+@@8:
+ MOV EDX, ESI
+ MOV ECX, EDI
+ PUSHFD
+ CALL @@Swap
+ POPFD
+ JE @@until
+ INC ESI
+ DEC EDI
+@@until:
+ CMP ESI, EDI
+ JL @@repeat
+@@qBreak:
+ MOV EDX, ESI
+ MOV ECX, EBX
+ CALL @@Compare
+ JG @@9
+ INC ESI
+@@9:
+ PUSH EBX // EBX = PivotTemp
+ PUSH ESI // ESI = leftTemp
+ DEC ESI
+@@while3:
+ CMP EBX, EAX
+ JGE @@while3_break
+ CMP ESI, EAX
+ JL @@while3_break
+ MOV EDX, EBX
+ MOV ECX, ESI
+ CALL @@Swap
+ INC EBX
+ DEC ESI
+ JMP @@while3
+@@while3_break:
+ POP ESI
+ POP EBX
+
+ MOV EDX, EAX
+ POP EAX // EAX = nElem
+ PUSH EDI // EDI = lNum
+ MOV EDI, ESI
+ SUB EDI, EDX
+ ADD EAX, EBX
+ SUB EAX, ESI
+
+ PUSH EBX
+ PUSH EAX
+ CMP EAX, EDI
+ JGE @@10
+
+ MOV EBX, ESI
+ CALL @@qSortHelp
+ POP EAX
+ MOV EAX, EDI
+ POP EBX
+ JMP @@11
+
+@@10: MOV EAX, EDI
+ CALL @@qSortHelp
+ POP EAX
+ POP EBX
+ MOV EBX, ESI
+@@11:
+ POP EDI
+ JMP @@TailRecursion
+
+@@Compare:
+ PUSH EAX
+ PUSH EDX
+ PUSH ECX
+ MOV EAX, [EBP-4]
+ DEC EDX
+ DEC ECX
+ CALL dword ptr [EBP-8]
+ POP ECX
+ POP EDX
+ TEST EAX, EAX
+ POP EAX
+ RET
+
+@@CompareLeSwap:
+ CALL @@Compare
+ JG @@ret
+
+@@Swap: PUSH EAX
+ PUSH EDX
+ PUSH ECX
+ MOV EAX, [EBP-4]
+ DEC EDX
+ DEC ECX
+ CALL dword ptr [SwapProc]
+ POP ECX
+ POP EDX
+ TEST EAX, EAX
+ POP EAX
+@@ret:
+ RET
+
+end;
+
+procedure SortArray( const Data: Pointer; const uNElem: Dword;
+ const CompareFun: TCompareArrayEvent );
+asm
+ PUSH EBP
+ MOV EBP, ESP
+ CMP EDX, 2
+ JL @@exit
+
+ SUB EAX, 4
+ PUSH EAX // [EBP-4] = Data
+ PUSH ECX // [EBP-8] = CompareFun
+ PUSH EBX // EBX = pivotP
+ XOR EBX, EBX
+ INC EBX // EBX = 1 to pass to qSortHelp as PivotP
+ MOV EAX, EDX // EAX = nElem
+ CALL @@qSortHelp
+ POP EBX
+ POP ECX
+ POP ECX
+@@exit:
+ POP EBP
+ RET
+
+@@qSortHelp:
+ PUSH EBX // EBX (in) = PivotP
+ PUSH ESI // ESI = leftP
+ PUSH EDI // EDI = rightP
+
+@@TailRecursion:
+ CMP EAX, 2
+ JG @@2
+ JNE @@exit_qSortHelp
+ LEA ECX, [EBX+1]
+ MOV EDX, EBX
+ //CALL @@Compare
+ PUSH EAX
+ PUSH EDX
+ PUSH ECX
+ MOV EAX, [EBP-4]
+ MOV EAX, [EAX + EDX*4]
+ MOV EDX, [EBP-4]
+ MOV EDX, [EDX + ECX*4]
+ CALL dword ptr [EBP-8]
+ POP ECX
+ POP EDX
+ TEST EAX, EAX
+ POP EAX
+
+ JLE @@exit_qSortHelp
+@@swp_exit:
+ //CALL @@Swap
+ PUSH EAX
+ PUSH ESI
+ MOV ESI, [EBP-4]
+ MOV EAX, [ESI+EDX*4]
+ XCHG EAX, [ESI+ECX*4]
+ MOV [ESI+EDX*4], EAX
+ POP ESI
+ POP EAX
+
+@@exit_qSortHelp:
+ POP EDI
+ POP ESI
+ POP EBX
+ RET
+
+ // ESI = leftP
+ // EDI = rightP
+@@2: LEA EDI, [EAX+EBX-1]
+ MOV ESI, EAX
+ SHR ESI, 1
+ ADD ESI, EBX
+ MOV ECX, ESI
+ MOV EDX, EDI
+ CALL @@CompareLeSwap
+ MOV EDX, EBX
+ //CALL @@Compare
+ PUSH EAX
+ PUSH EDX
+ PUSH ECX
+ MOV EAX, [EBP-4]
+ MOV EAX, [EAX + EDX*4]
+ MOV EDX, [EBP-4]
+ MOV EDX, [EDX + ECX*4]
+ CALL dword ptr [EBP-8]
+ POP ECX
+ POP EDX
+ TEST EAX, EAX
+ POP EAX
+
+ JG @@4
+ //CALL @@Swap
+ PUSH EAX
+ PUSH ESI
+ MOV ESI, [EBP-4]
+ MOV EAX, [ESI+EDX*4]
+ XCHG EAX, [ESI+ECX*4]
+ MOV [ESI+EDX*4], EAX
+ POP ESI
+ POP EAX
+
+ JMP @@5
+@@4: MOV ECX, EBX
+ MOV EDX, EDI
+ CALL @@CompareLeSwap
+@@5:
+ CMP EAX, 3
+ JNE @@6
+ MOV EDX, EBX
+ MOV ECX, ESI
+ JMP @@swp_exit
+@@6: // classic Horae algorithm
+
+ PUSH EAX // EAX = pivotEnd
+ LEA EAX, [EBX+1]
+ MOV ESI, EAX
+@@repeat:
+ MOV EDX, ESI
+ MOV ECX, EBX
+ //CALL @@Compare
+ PUSH EAX
+ PUSH EDX
+ PUSH ECX
+ MOV EAX, [EBP-4]
+ MOV EAX, [EAX + EDX*4]
+ MOV EDX, [EBP-4]
+ MOV EDX, [EDX + ECX*4]
+ CALL dword ptr [EBP-8]
+ POP ECX
+ POP EDX
+ TEST EAX, EAX
+ POP EAX
+
+ JG @@while2
+@@while1:
+ JNE @@7
+ MOV EDX, ESI
+ MOV ECX, EAX
+ //CALL @@Swap
+ PUSH EAX
+ PUSH ESI
+ MOV ESI, [EBP-4]
+ MOV EAX, [ESI+EDX*4]
+ XCHG EAX, [ESI+ECX*4]
+ MOV [ESI+EDX*4], EAX
+ POP ESI
+ POP EAX
+
+ INC EAX
+@@7:
+ CMP ESI, EDI
+ JGE @@qBreak
+ INC ESI
+ JMP @@repeat
+@@while2:
+ CMP ESI, EDI
+ JGE @@until
+ MOV EDX, EBX
+ MOV ECX, EDI
+ //CALL @@Compare
+ PUSH EAX
+ PUSH EDX
+ PUSH ECX
+ MOV EAX, [EBP-4]
+ MOV EAX, [EAX + EDX*4]
+ MOV EDX, [EBP-4]
+ MOV EDX, [EDX + ECX*4]
+ CALL dword ptr [EBP-8]
+ POP ECX
+ POP EDX
+ TEST EAX, EAX
+ POP EAX
+
+ JGE @@8
+ DEC EDI
+ JMP @@while2
+@@8:
+ MOV EDX, ESI
+ MOV ECX, EDI
+ //PUSHFD
+ //CALL @@Swap
+ PUSH EAX
+ PUSH ESI
+ MOV ESI, [EBP-4]
+ MOV EAX, [ESI+EDX*4]
+ XCHG EAX, [ESI+ECX*4]
+ MOV [ESI+EDX*4], EAX
+ POP ESI
+ POP EAX
+
+ //POPFD
+ JE @@until
+ INC ESI
+ DEC EDI
+@@until:
+ CMP ESI, EDI
+ JL @@repeat
+@@qBreak:
+ MOV EDX, ESI
+ MOV ECX, EBX
+ //CALL @@Compare
+ PUSH EAX
+ PUSH EDX
+ PUSH ECX
+ MOV EAX, [EBP-4]
+ MOV EAX, [EAX + EDX*4]
+ MOV EDX, [EBP-4]
+ MOV EDX, [EDX + ECX*4]
+ CALL dword ptr [EBP-8]
+ POP ECX
+ POP EDX
+ TEST EAX, EAX
+ POP EAX
+
+ JG @@9
+ INC ESI
+@@9:
+ PUSH EBX // EBX = PivotTemp
+ PUSH ESI // ESI = leftTemp
+ DEC ESI
+@@while3:
+ CMP EBX, EAX
+ JGE @@while3_break
+ CMP ESI, EAX
+ JL @@while3_break
+ MOV EDX, EBX
+ MOV ECX, ESI
+ //CALL @@Swap
+ PUSH EAX
+ PUSH ESI
+ MOV ESI, [EBP-4]
+ MOV EAX, [ESI+EDX*4]
+ XCHG EAX, [ESI+ECX*4]
+ MOV [ESI+EDX*4], EAX
+ POP ESI
+ POP EAX
+
+ INC EBX
+ DEC ESI
+ JMP @@while3
+@@while3_break:
+ POP ESI
+ POP EBX
+
+ MOV EDX, EAX
+ POP EAX // EAX = nElem
+ PUSH EDI // EDI = lNum
+ MOV EDI, ESI
+ SUB EDI, EDX
+ ADD EAX, EBX
+ SUB EAX, ESI
+
+ PUSH EBX
+ PUSH EAX
+ CMP EAX, EDI
+ JGE @@10
+
+ MOV EBX, ESI
+ CALL @@qSortHelp
+ POP EAX
+ MOV EAX, EDI
+ POP EBX
+ JMP @@11
+
+@@10: MOV EAX, EDI
+ CALL @@qSortHelp
+ POP EAX
+ POP EBX
+ MOV EBX, ESI
+@@11:
+ POP EDI
+ JMP @@TailRecursion
+
+{@@Compare:
+ PUSH EAX
+ PUSH EDX
+ PUSH ECX
+ MOV EAX, [EBP-4]
+ MOV EAX, [EAX + EDX*4]
+ MOV EDX, [EBP-4]
+ MOV EDX, [EDX + ECX*4]
+ CALL dword ptr [EBP-8]
+ POP ECX
+ POP EDX
+ TEST EAX, EAX
+ POP EAX
+ RET}
+
+@@CompareLeSwap:
+ //CALL @@Compare
+ PUSH EAX
+ PUSH EDX
+ PUSH ECX
+ MOV EAX, [EBP-4]
+ MOV EAX, [EAX + EDX*4]
+ MOV EDX, [EBP-4]
+ MOV EDX, [EDX + ECX*4]
+ CALL dword ptr [EBP-8]
+ POP ECX
+ POP EDX
+ TEST EAX, EAX
+ POP EAX
+
+ JG @@ret
+
+@@Swap: PUSH EAX
+ PUSH ESI
+ MOV ESI, [EBP-4]
+ MOV EAX, [ESI+EDX*4]
+ XCHG EAX, [ESI+ECX*4]
+ MOV [ESI+EDX*4], EAX
+ POP ESI
+ //TEST EAX, EAX
+ POP EAX
+@@ret:
+ RET
+
+end;
+
+
+function CompareIntegers( const Sender : Pointer; const e1, e2 : DWORD ) : Integer;
+asm
+ MOV EDX, [EAX+EDX*4]
+ SUB EDX, [EAX+ECX*4]
+ XCHG EAX, EDX
+end;
+
+function CompareDwords( const Sender : Pointer; const e1, e2 : DWORD ) : Integer;
+asm
+ MOV EDX, [EAX+EDX*4]
+ SUB EDX, [EAX+ECX*4]
+ XCHG EAX, EDX
+ JNB @@1
+ SBB EAX, EAX
+@@1:
+end;
+
+function Compare2Dwords( e1, e2 : DWORD ) : Integer;
+asm
+ SUB EAX, EDX
+ JZ @@exit
+ MOV EAX, 0
+ JB @@neg
+ INC EAX
+ INC EAX
+@@neg:
+ DEC EAX
+@@exit:
+end;
+
+procedure SwapIntegers( const Sender : Pointer; const e1, e2 : DWORD );
+asm
+ LEA EDX, [EAX+EDX*4]
+ LEA ECX, [EAX+ECX*4]
+ MOV EAX, [EDX]
+ XCHG EAX, [ECX]
+ MOV [EDX], EAX
+end;
+
+function _NewStatusbar( AParent: PControl ): PControl;
+const STAT_CLS_NAM: PKOLChar = STATUSCLASSNAME;
+asm
+ PUSH 0
+ {$IFDEF COMMANDACTIONS_OBJ}
+ PUSH OTHER_ACTIONS
+ {$ELSE}
+ PUSH 0
+ {$ENDIF}
+ {$IFDEF USE_FLAGS}
+ TEST [EAX].TControl.fFlagsG3, (1 shl G3_SizeGrip)
+ {$ELSE}
+ CMP [EAX].TControl.fSizeGrip, 0
+ {$ENDIF}
+ MOV ECX, WS_CHILD or WS_CLIPSIBLINGS or WS_CLIPCHILDREN or 3 or WS_VISIBLE
+ JZ @@1
+ INC CH
+ AND CL, not 3
+@@1:
+ MOV EDX, [STAT_CLS_NAM]
+ CALL _NewCommonControl
+ PUSH EBX
+ XCHG EBX, EAX
+ PUSH EDI
+ LEA EDI, [EBX].TControl.fBoundsRect
+ XOR EAX, EAX
+ STOSD
+ STOSD
+ STOSD
+ STOSD
+ MOV [EBX].TControl.fAlign, caBottom
+ {$IFDEF USE_FLAGS}
+ OR [EBX].TControl.fFlagsG4, 1 shl G4_NotUseAlign
+ {$ELSE}
+ INC [EBX].TControl.fNotUseAlign
+ {$ENDIF}
+ POP EDI
+ MOV EAX, EBX
+ CALL InitCommonControlSizeNotify
+ XCHG EAX, EBX
+ POP EBX
+end;
+
+procedure TControl.RemoveStatus;
+asm
+ MOV ECX, [EAX].fStatusCtl
+ JECXZ @@exit
+ PUSH EBX
+ MOV EBX, EAX
+ CALL GetClientHeight
+ PUSH EAX
+ XOR EAX, EAX
+ XCHG [EBX].fStatusCtl, EAX
+ CALL TObj.RefDec
+ POP EAX
+ CDQ
+ MOV [EBX].fClientBottom, DL
+ XCHG EDX, EAX
+ XCHG EAX, EBX
+ POP EBX
+ CALL SetClientHeight
+@@exit:
+end;
+
+function TControl.StatusPanelCount: Integer;
+asm
+ MOV ECX, [EAX].fStatusCtl
+ JECXZ @@exit
+ PUSH 0
+ PUSH 0
+ PUSH SB_GETPARTS
+ PUSH ECX
+ CALL Perform
+@@exit:
+end;
+
+function TControl.GetStatusPanelX(Idx: Integer): Integer;
+asm
+ MOV ECX, [EAX].fStatusCtl
+ JECXZ @@exit
+ PUSH EBX
+ MOV EBX, EDX
+ ADD ESP, -1024
+ PUSH ESP
+ XOR EDX, EDX
+ DEC DL
+ PUSH EDX
+ MOV DX, SB_GETPARTS
+ PUSH EDX
+ PUSH ECX
+ CALL Perform
+ CMP EAX, EBX
+ MOV ECX, [ESP+EBX*4]
+ JG @@1
+ XOR ECX, ECX
+@@1: ADD ESP, 1024
+ POP EBX
+@@exit:
+ XCHG EAX, ECX
+end;
+
+procedure TControl.SetStatusPanelX(Idx: Integer; const Value: Integer);
+asm
+ ADD ESP, -1024
+ MOV EAX, [EAX].fStatusCtl
+ TEST EAX, EAX
+ JZ @@exit
+
+ PUSH ESP
+ PUSH EDX
+ PUSH SB_SETPARTS
+ PUSH EAX
+
+ PUSH EDX
+ PUSH ECX
+
+ LEA EDX, [ESP+24]
+ PUSH EDX
+ PUSH 255
+ PUSH SB_GETPARTS
+ PUSH EAX
+ CALL Perform
+
+ POP ECX
+ POP EDX
+ CMP EAX, EDX
+ JG @@1
+ ADD ESP, 16
+ JMP @@exit
+
+@@1: MOV [ESP+8], EAX
+ MOV [ESP+16+EDX*4], ECX
+ CALL Perform
+
+@@exit: ADD ESP, 1024
+end;
+
+destructor TImageList.Destroy;
+asm
+ PUSH EAX
+ XOR EDX, EDX
+ CALL SetHandle
+ POP EAX
+ MOV EDX, [EAX].fNext
+ MOV ECX, [EAX].fPrev
+ TEST EDX, EDX
+ JZ @@nonext
+ MOV [EDX].fPrev, ECX
+@@nonext:
+ JECXZ @@noprev
+ MOV [ECX].fNext, EDX
+@@noprev:
+ MOV ECX, [EAX].fControl
+ JECXZ @@fin
+ CMP [ECX].TControl.fImageList, EAX
+ JNZ @@fin
+ MOV [ECX].TControl.fImageList, EDX
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ PUSH EAX
+ XCHG EAX, ECX
+ MOV EDX, ECX
+ CALL TControl.RemoveFromAutoFree
+ POP EAX
+ {$ENDIF}
+@@fin: CALL TObj.Destroy
+end;
+
+function TImageList.GetHandle: THandle;
+asm
+ PUSH EAX
+ CALL HandleNeeded
+ POP EAX
+ MOV EAX, [EAX].FHandle
+end;
+
+procedure TImageList.SetHandle(const Value: THandle);
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ MOV ECX, [EBX].FHandle
+ CMP ECX, EDX
+ JZ @@exit
+ JECXZ @@set_handle
+ CMP [EBX].fShareImages, 0
+ JNZ @@set_handle
+ PUSH EDX
+ PUSH ECX
+ CALL ImageList_Destroy
+ POP EDX
+
+@@set_handle:
+ MOV [EBX].FHandle, EDX
+ TEST EDX, EDX
+ JZ @@set_sz0
+ LEA EAX, [EBX].FImgHeight
+ PUSH EAX
+ LEA EAX, [EBX].FImgWidth
+ PUSH EAX
+ PUSH EDX
+ CALL ImageList_GetIconSize
+ JMP @@exit
+
+@@set_sz0:
+ MOV [EBX].fImgWidth, EDX
+ MOV [EBX].fImgHeight, EDX
+
+@@exit:
+ POP EBX
+end;
+
+function TControl.Perform(msgcode: DWORD; wParam, lParam: Integer): Integer; stdcall;
+asm
+ PUSH [lParam]
+ PUSH [wParam]
+ PUSH [msgcode]
+ MOV EAX, [EBP+8]
+ CALL TControl.GetWindowHandle
+ PUSH EAX
+ {$IFDEF UNICODE_CTRLS}
+ CALL Windows.SendMessageW
+ {$ELSE}
+ CALL Windows.SendMessageA
+ {$ENDIF}
+end;
+
+function TControl.Postmsg(msgcode: DWORD; wParam, lParam: Integer): Boolean; stdcall;
+asm
+ PUSH [lParam]
+ PUSH [wParam]
+ PUSH [msgcode]
+ MOV EAX, [EBP+8]
+ CALL TControl.GetWindowHandle
+ PUSH EAX
+ CALL Windows.PostMessageA
+end;
+
+function TControl.GetChildCount: Integer;
+asm
+ MOV EAX, [EAX].fChildren
+ MOV EAX, [EAX].TList.fCount
+end;
+
+procedure TControl.SetItemVal(Item: Integer; const Index: Integer; const Value: Integer);
+asm
+ PUSH EAX
+ PUSH [Value]
+ PUSH EDX
+ MOV EDX, ECX
+ SHR EDX, 16
+ JNZ @@1
+ MOV EDX, ECX
+ INC EDX
+@@1:
+ MOV EBP, EDX
+ AND EDX, 7FFFh
+ PUSH EDX
+ PUSH EAX
+ CALL Perform
+ MOV EAX, EBP
+ ADD AX, AX
+ POP EAX
+ JNB @@2
+ CALL Invalidate
+@@2:
+end;
+
+destructor TOpenSaveDialog.Destroy;
+asm //cmd //opd
+ PUSH EAX
+ PUSH 0
+ LEA EDX, [EAX].FFilter
+ PUSH EDX
+ LEA EDX, [EAX].FInitialDir
+ PUSH EDX
+ LEA EDX, [EAX].FDefExtension
+ PUSH EDX
+ LEA EDX, [EAX].FFileName
+ PUSH EDX
+ LEA EAX, [EAX].FTitle
+@@loo:
+ {$IFDEF UNICODE_CTRLS}
+ CALL System.@WStrClr
+ {$ELSE}
+ CALL System.@LStrClr
+ {$ENDIF}
+ POP EAX
+ TEST EAX, EAX
+ JNZ @@loo
+ POP EAX
+ CALL TObj.Destroy
+end;
+
+destructor TOpenDirDialog.Destroy;
+asm //cmd //opd
+ PUSH EAX
+ PUSH 0
+ LEA EDX, [EAX].FTitle
+ PUSH EDX
+ LEA EDX, [EAX].FInitialPath
+ PUSH EDX
+ LEA EAX, [EAX].FStatusText
+@@loo:
+ {$IFDEF UNICODE_CTRLS}
+ CALL System.@WStrClr
+ {$ELSE}
+ CALL System.@LStrClr
+ {$ENDIF}
+ POP EAX
+ TEST EAX, EAX
+ JNZ @@loo
+ POP EAX
+ CALL TObj.Destroy
+end;
+
+{$IFNDEF NEW_OPEN_DIR_STYLE_EX}
+function OpenDirCallBack( Wnd: HWnd; Msg: DWORD; lParam, lpData: LParam ): Integer;
+ stdcall;
+asm
+ MOV EAX, [Wnd]
+ MOV EDX, [lpData]
+
+ MOV [EDX].TOpenDirDialog.FDialogWnd, EAX
+
+ MOV ECX, [Msg]
+ LOOP @@chk_sel_chg
+ // Msg = 1 -> BFFM_Initialized
+
+ MOV ECX, [EDX].TOpenDirDialog.FCenterProc
+ JECXZ @@1
+ PUSH EDX
+ CALL ECX
+ POP EDX
+@@1: MOV ECX, [EDX].TOpenDirDialog.FInitialPath
+ JECXZ @@exit
+ PUSH ECX
+ PUSH 1
+ {$IFDEF UNICODE_CTRLS}
+ PUSH BFFM_SETSELECTIONW
+ {$ELSE}
+ PUSH BFFM_SETSELECTION
+ {$ENDIF}
+ PUSH [Wnd]
+ CALL SendMessage
+ JMP @@exit
+
+@@chk_sel_chg:
+ LOOP @@exit
+ // Msg = 2 -> BFFM_SelChanged
+
+ MOV ECX, [EDX].TOpenDirDialog.FDoSelChanged
+ JECXZ @@exit
+ POP EBP
+ JMP ECX
+
+@@exit: XOR EAX, EAX
+end;
+{$ENDIF}
+
+procedure OpenDirDlgCenter( Wnd: HWnd );
+asm
+ PUSH EBX
+ MOV EBX, EAX
+
+ ADD ESP, -16
+ PUSH ESP
+ PUSH EAX
+ CALL GetWindowRect
+ POP EDX // EDX = Left
+ POP ECX // ECX = Top
+ POP EAX // EAX = Right
+ SUB EAX, EDX // EAX = W
+ POP EDX // EDX = Bottom
+ SUB EDX, ECX // EDX = H
+ XOR ECX, ECX
+ INC ECX
+ PUSH ECX // prepare True
+ PUSH EDX // prepare H
+ PUSH EAX // prepare W
+
+ INC ECX
+@@1:
+ PUSH ECX
+
+ DEC ECX
+ PUSH ECX
+ CALL GetSystemMetrics
+
+ POP ECX
+ SUB EAX, [ESP+4]
+ SAR EAX, 1
+ PUSH EAX
+
+ LOOP @@1
+
+ PUSH EBX
+ CALL MoveWindow
+ POP EBX
+end;
+
+procedure TOpenDirDialog.SetCenterOnScreen(const Value: Boolean);
+asm
+ MOV [EAX].FCenterOnScreen, DL
+ MOVZX ECX, DL
+ JECXZ @@1
+ MOV ECX, Offset[OpenDirDlgCenter]
+@@1: MOV [EAX].FCenterProc, ECX
+end;
+
+function TControl.TBAddButtons(const Buttons: array of PKOLChar;
+ const BtnImgIdxArray: array of Integer): Integer;
+asm
+ PUSH dword ptr [EBP+8]
+ PUSH dword ptr [EBP+12]
+ PUSH ECX
+ PUSH EDX
+ PUSH -1
+ PUSH EAX
+ CALL TBAddInsButtons
+end;
+
+function TControl.TBGetBtnStt(BtnID: Integer; const Index: Integer): Boolean;
+asm
+ PUSH 0
+ PUSH ECX
+ PUSH EAX
+ CALL GetTBBtnGoodID
+ POP EDX
+ POP ECX
+ PUSH EAX
+ ADD ECX, 8
+ PUSH ECX
+ PUSH EDX
+ CALL Perform
+ TEST EAX, EAX
+ SETNZ AL
+end;
+
+function TControl.TBIndex2Item(Idx: Integer): Integer;
+const //
+ _sizeof_TTBButton = sizeof( TTBButton ); //
+asm
+ ADD ESP, -_sizeof_TTBButton //
+ PUSH ESP
+ PUSH EDX
+ PUSH TB_GETBUTTON
+ PUSH EAX
+ CALL Perform
+ TEST EAX, EAX
+ MOV EAX, [ESP].TTBButton.idCommand
+ JNZ @@1
+ OR EAX, -1
+@@1: ADD ESP, _sizeof_TTBButton //
+end;
+
+// TODO: testcase
+//{$IFDEF ASM_UNICODE}
+procedure TControl.TBSetTooltips(BtnID1st: Integer;
+ const Tooltips: array of PKOLChar);
+asm
+ PUSH EBX
+ PUSH ESI
+ MOV ESI, ECX
+ MOV EBX, EAX
+ PUSHAD
+ MOV ECX, [EBX].DF.fTBttCmd
+ INC ECX
+ LOOP @@1
+ CALL NewList
+ MOV [EBX].DF.fTBttCmd, EAX
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ XCHG EDX, EAX
+ MOV EAX, EBX
+ CALL TControl.Add2AutoFree
+ {$ENDIF}
+ {$IFDEF UNICODE_CTRLS}
+ CALL NewWStrList
+ {$ELSE}
+ CALL NewStrList
+ {$ENDIF}
+ MOV [EBX].DF.fTBttTxt, EAX
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ XCHG EDX, EAX
+ MOV EAX, EBX
+ CALL TControl.Add2AutoFree
+ {$ENDIF}
+@@1: POPAD
+ MOV ECX, [EBP+8]
+ INC ECX
+ JZ @@exit
+@@loop:
+ PUSH ECX
+ PUSH EDX
+ PUSH 0
+ LODSD
+ MOV EDX, EAX
+ MOV EAX, ESP
+ {$IFDEF UNICODE_CTRLS}
+ CALL System.@WStrFromPWChar
+ {$ELSE}
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX // TODO: safe?
+ {$ENDIF}
+ CALL System.@LStrFromPChar
+ {$ENDIF}
+
+ MOV EDX, [ESP+4]
+ MOV EAX, [EBX].DF.fTBttCmd
+ CALL TList.IndexOf
+ TEST EAX, EAX
+ JGE @@2
+
+ MOV EDX, [ESP+4]
+ MOV EAX, [EBX].DF.fTBttCmd
+ CALL TList.Add
+ POP EDX
+ PUSH EDX
+ MOV EAX, [EBX].DF.fTBttTxt
+ {$IFDEF UNICODE_CTRLS}
+ CALL TWStrList.Add
+ {$ELSE}
+ CALL TStrList.Add
+ {$ENDIF}
+ JMP @@3
+
+@@2:
+ MOV EDX, EAX
+ POP ECX
+ PUSH ECX
+ MOV EAX, [EBX].DF.fTBttTxt
+ {$IFDEF UNICODE_CTRLS}
+ CALL TWStrList.Put
+ {$ELSE}
+ CALL TStrList.Put
+ {$ENDIF}
+@@3:
+ {$IFDEF UNICODE_CTRLS}
+ CALL RemoveWStr
+ {$ELSE}
+ CALL RemoveStr
+ {$ENDIF}
+
+ POP EDX
+ POP ECX
+ INC EDX
+ LOOP @@loop
+@@exit:
+ POP ESI
+ POP EBX
+end;
+//{$ENDIF}
+
+function TControl.TBButtonAtPos(X, Y: Integer): Integer;
+asm
+ PUSH EAX
+ CALL TBBtnIdxAtPos
+ TEST EAX, EAX
+ MOV EDX, EAX
+ POP EAX
+ JGE TBIndex2Item
+ MOV EAX, EDX
+end;
+
+function TControl.TBBtnIdxAtPos(X, Y: Integer): Integer;
+asm
+ PUSH EBX
+ PUSH ECX
+ PUSH EDX
+ MOV EBX, EAX
+ CALL GetItemsCount
+ MOV ECX, EAX
+ JECXZ @@fin
+@@1: PUSH ECX
+ ADD ESP, -16
+ PUSH ESP
+ DEC ECX
+ PUSH ECX
+ PUSH TB_GETITEMRECT
+ PUSH EBX
+ CALL Perform
+ MOV EDX, ESP
+ LEA EAX, [ESP+20]
+ CALL PointInRect
+ ADD ESP, 16
+ POP ECX
+ TEST AL, AL
+ {$IFDEF USE_CMOV}
+ CMOVNZ EAX, ECX
+ {$ELSE}
+ JZ @@2
+ MOV EAX, ECX
+ JMP @@fin
+@@2: {$ENDIF}
+ JNZ @@fin
+
+ LOOP @@1
+@@fin: DEC EAX
+ POP EDX
+ POP EDX
+ POP EBX
+end;
+
+procedure TControl.TBSetButtonText(BtnID: Integer; const Value: KOLString);
+asm
+ PUSH 0
+ PUSH ECX
+ PUSH EAX
+ CALL GetTBBtnGoodID
+ POP EDX
+
+ ADD ESP, -16
+ PUSH TBIF_TEXT
+ PUSH 32 //Sizeof( TTBButtonInfo )
+ PUSH ESP
+ PUSH EAX
+ PUSH TB_SETBUTTONINFO
+ PUSH EDX
+ CALL Perform
+ ADD ESP, 32 //sizeof( TTBButtonInfo )
+end;
+
+function TControl.TBGetBtnWidth(BtnID: Integer): Integer;
+asm
+ ADD ESP, -16
+ MOV ECX, ESP
+ CALL TBGetButtonRect
+ POP EDX
+ POP ECX
+ POP EAX
+ SUB EAX, EDX
+ POP EDX
+end;
+
+procedure TControl.TBSetBtnWidth(BtnID: Integer; const Value: Integer);
+asm
+ PUSH EBX
+ MOV EBX, ECX
+
+ PUSH EAX
+ CALL GetTBBtnGoodID
+ POP EDX
+
+ ADD ESP, -24
+ PUSH TBIF_SIZE or TBIF_STYLE
+ PUSH 32
+ MOV ECX, ESP
+
+ PUSH ECX
+ PUSH EAX
+ PUSH TB_SETBUTTONINFO
+ PUSH EDX
+
+ PUSH ECX
+ PUSH EAX
+ PUSH TB_GETBUTTONINFO
+ PUSH EDX
+ CALL Perform
+
+ MOV [ESP+16+18], BX
+ AND byte ptr [ESP+16].TTBButtonInfo.fsStyle, not TBSTYLE_AUTOSIZE
+ CALL Perform
+ ADD ESP, 32
+ POP EBX
+end;
+
+procedure TControl.AddDirList(const Filemask: KOLString; Attrs: DWORD);
+asm
+ CALL EDX2PChar
+ PUSH EDX
+ PUSH ECX
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EAX].fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.aDir
+ {$ELSE}
+ MOVZX ECX, [EAX].fCommandActions.aDir
+ {$ENDIF}
+ JECXZ @@exit
+ PUSH ECX
+ PUSH EAX
+ CALL Perform
+ RET
+@@exit:
+ POP ECX
+ POP ECX
+end;
+
+{$IFDEF noASM_VERSION}
+function WndProcShowModal( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+asm
+ CMP word ptr [EDX].TMsg.message, WM_CLOSE
+ JNZ @@ret_false
+
+ XCHG EDX, EAX
+ XOR EAX, EAX
+ CMP [EDX].TControl.fModalResult, EAX
+ JNZ @@1
+ OR [EDX].TControl.fModalResult, -1
+@@1:
+ MOV [ECX], EAX
+ INC EAX
+ RET
+@@ret_false:
+ XOR EAX, EAX
+
+end;
+{$ENDIF}
+
+function TimerProc( Wnd : HWnd; Msg : Integer; T : PTimer; CurrentTime : DWord ): Integer;
+ stdcall;
+asm //cmd //opd
+ {$IFDEF STOPTIMER_AFTER_APPLETTERMINATED}
+ CMP [AppletTerminated], 0
+ JNZ @@exit
+ {$ENDIF}
+ MOV EDX, T
+ MOV ECX, [EDX].TTimer.fOnTimer.TMethod.Code
+ JECXZ @@exit
+ MOV EAX, [EDX].TTimer.fOnTimer.TMethod.Data
+ CALL ECX
+@@exit: XOR EAX, EAX
+end;
+
+destructor TTimer.Destroy;
+asm
+ PUSH EAX
+ XOR EDX, EDX
+ CALL TTimer.SetEnabled
+ POP EAX
+ CALL TObj.Destroy
+ DEC [TimerCount]
+ JNZ @@exit
+ XOR EAX, EAX
+ XCHG EAX, [TimerOwnerWnd]
+ CALL TObj.RefDec
+@@exit:
+end;
+
+procedure TTimer.SetEnabled(const Value: Boolean);
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+
+ CMP [EBX].fEnabled, DL
+ JZ @@exit
+
+ {$IFDEF TIMER_APPLETWND}
+
+ MOV ECX, [Applet]
+ JECXZ @@exit
+
+ MOV [EBX].fEnabled, DL
+ TEST DL, DL
+ JZ @@disable
+
+ {$ELSE}
+
+ MOV [EBX].fEnabled, DL
+ TEST DL, DL
+ JZ @@disable
+
+ MOV ECX, [TimerOwnerWnd]
+ INC ECX
+ LOOP @@owner_ready
+
+ INC ECX
+ MOV EDX, offset[EmptyString]
+ XOR EAX, EAX
+ PUSH EAX
+ CALL _NewWindowed
+ MOV [TimerOwnerWnd], EAX
+ MOV [EAX].TControl.fStyle, 0
+ {$IFDEF USE_FLAGS}
+ OR [EAX].TControl.fFlagsG3, 1 shl G3_IsControl
+ {$ELSE}
+ INC [EAX].TControl.fIsControl
+ {$ENDIF}
+ XCHG ECX, EAX
+
+ {$ENDIF}
+
+@@owner_ready:
+
+ PUSH offset[TimerProc]
+ PUSH [EBX].fInterval
+ PUSH EBX
+ XCHG EAX, ECX
+ CALL TControl.GetWindowHandle
+ PUSH EAX
+ CALL SetTimer
+ MOV [EBX].fHandle, EAX
+
+ JMP @@exit
+
+@@disable:
+ XOR ECX, ECX
+ XCHG ECX, [EBX].TTimer.fHandle
+ JECXZ @@exit
+
+ PUSH ECX
+ {$IFDEF TIMER_APPLETWND}
+ MOV EAX, [Applet]
+ {$ELSE}
+ MOV EAX, [TimerOwnerWnd]
+ {$ENDIF}
+ PUSH [EAX].TControl.fHandle
+ CALL KillTimer
+
+@@exit:
+ POP EBX
+end;
+
+function PrepareBitmapHeader( W, H, BitsPerPixel: Integer ): PBitmapInfo;
+const szIH = sizeof(TBitmapInfoHeader);
+ szHd = szIH + 256 * Sizeof(TRGBQuad);
+asm
+ PUSH EDI
+
+ PUSH ECX // BitsPerPixel
+ PUSH EDX // H
+ PUSH EAX // W
+
+ MOV EAX, szHd
+ CALL AllocMem
+
+ MOV EDI, EAX
+ XCHG ECX, EAX
+
+ XOR EAX, EAX
+ MOV AL, szIH
+ STOSD // biSize = Sizeof( TBitmapInfoHeader )
+ POP EAX // ^ W
+ STOSD // -> biWidth
+ POP EAX // ^ H
+ STOSD // -> biHeight
+ XOR EAX, EAX
+ INC EAX
+ STOSW // 1 -> biPlanes
+ POP EAX // ^ BitsPerPixel
+ STOSW // -> biBitCount
+
+ XCHG EAX, ECX // EAX = Result
+ POP EDI
+end;
+
+function Bits2PixelFormat( BitsPerPixel: Integer ): TPixelFormat;
+asm
+ PUSH ESI
+ MOV ESI, offset[ BitsPerPixel_By_PixelFormat + 1 ]
+ XOR ECX, ECX
+ XCHG EDX, EAX
+@@loo: INC ECX
+ LODSB
+ CMP AL, DL
+ JZ @@exit
+ TEST AL, AL
+ JNZ @@loo
+@@exit: XCHG EAX, ECX
+ POP ESI
+end;
+
+function _NewBitmap( W, H: Integer ): PBitmap;
+begin
+ New( Result, Create );
+ Result.fDetachCanvas := DummyDetachCanvas;
+ Result.fWidth := W;
+ Result.fHeight := H;
+end;
+function NewBitmap( W, H: Integer ): PBitmap;
+asm
+ PUSH EAX
+ PUSH EDX
+ CALL _NewBitmap
+ POP EDX
+ POP ECX
+ PUSH EAX
+ INC [EAX].TBitmap.fHandleType
+ JECXZ @@exit
+ TEST EDX, EDX
+ JZ @@exit
+ PUSH EBX
+ PUSH EAX
+ PUSH EDX
+ PUSH ECX
+ PUSH 0
+ CALL GetDC
+ PUSH EAX
+ XCHG EBX, EAX
+ CALL CreateCompatibleBitmap
+ POP EDX
+ MOV [EDX].TBitmap.fHandle, EAX
+ PUSH EBX
+ PUSH 0
+ CALL ReleaseDC
+ POP EBX
+@@exit: POP EAX
+end;
+
+procedure PreparePF16bit( DIBHeader: PBitmapInfo );
+const szBIH = sizeof(TBitmapInfoHeader);
+asm
+ MOV byte ptr [EAX].TBitmapInfoHeader.biCompression, BI_BITFIELDS
+ ADD EAX, szBIH
+ XCHG EDX, EAX
+ MOV EAX, offset[InitColors]
+ XOR ECX, ECX
+ MOV CL, 19*4
+ CALL System.Move
+end;
+
+function NewDIBBitmap( W, H: Integer; PixelFormat: TPixelFormat ): PBitmap;
+asm
+ PUSH EBX
+
+ PUSH ECX
+ PUSH EDX
+ PUSH EAX
+ CALL _NewBitmap
+ XCHG EBX, EAX
+ POP EAX //W
+ POP EDX //H
+ POP ECX //PixelFormat
+
+ TEST EAX, EAX
+ JZ @@exit
+ TEST EDX, EDX
+ JZ @@exit
+
+ PUSH EAX
+ MOVZX EAX, CL
+ JMP @@loadBitsPixel
+@@loadDefault:
+ MOVZX EAX, [DefaultPixelFormat]
+@@loadBitsPixel:
+ MOVZX ECX, byte ptr [ BitsPerPixel_By_PixelFormat + EAX ]
+ JECXZ @@loadDefault
+ MOV [EBX].TBitmap.fNewPixelFormat, AL
+ {$IFDEF PARANOIA} DB $3C, pf16bit {$ELSE} CMP AL, pf16bit {$ENDIF}
+ POP EAX
+
+ PUSHFD
+ CALL PrepareBitmapHeader
+ MOV [EBX].TBitmap.fDIBHeader, EAX
+ POPFD
+ JNZ @@2
+
+ CALL PreparePF16bit
+
+@@2:
+ MOV EAX, EBX
+ CALL TBitmap.GetScanLineSize
+ MOV EDX, [EBX].TBitmap.fHeight
+ MUL EDX
+ MOV [EBX].TBitmap.fDIBSize, EAX
+ ADD EAX, 16
+ PUSH EAX
+ PUSH GMEM_FIXED or GMEM_ZEROINIT
+ CALL GlobalAlloc
+ MOV [EBX].TBitmap.fDIBBits, EAX
+@@exit:
+ XCHG EAX, EBX
+ POP EBX
+end;
+
+procedure TBitmap.ClearData;
+asm
+ PUSH EBX
+ MOV EBX, EAX
+ CALL [EBX].fDetachCanvas
+ XOR ECX, ECX
+ XCHG ECX, [EBX].fHandle
+ JECXZ @@1
+ PUSH ECX
+ CALL DeleteObject
+ XOR ECX, ECX
+ MOV [EBX].fDIBBits, ECX
+@@1: XCHG ECX, [EBX].fDIBBits
+ JECXZ @@2
+ CMP [EBX].fDIBAutoFree, 0
+ JNZ @@2
+ PUSH ECX
+ CALL GlobalFree
+@@2: XOR ECX, ECX
+ XCHG ECX, [EBX].fDIBHeader
+ JECXZ @@3
+ XCHG EAX, ECX
+ CALL System.@FreeMem
+@@3: XOR EAX, EAX
+ MOV [EBX].fScanLineSize, EAX
+ MOV [EBX].fGetDIBPixels, EAX
+ MOV [EBX].fSetDIBPixels, EAX
+ XCHG EAX, EBX
+ POP EBX
+ CALL ClearTransImage
+end;
+
+procedure TBitmap.Clear;
+asm
+ PUSH EAX
+ CALL RemoveCanvas
+ POP EAX
+ PUSH EAX
+ CALL ClearData
+ POP EAX
+ XOR EDX, EDX
+ MOV [EAX].fWidth, EDX
+ MOV [EAX].fHeight, EDX
+ MOV [EAX].fDIBAutoFree, DL
+end;
+
+destructor TBitmap.Destroy;
+asm
+ PUSH EAX
+ CALL Clear
+ POP EAX
+ CALL TObj.Destroy
+end;
+
+procedure TBitmap.Draw(DC: HDC; X, Y: Integer);
+const szBitmap = sizeof( tagBitmap );
+asm // [EBP+8] = Y
+ PUSH EDX // [EBP-4] = DC
+ PUSH ECX // [EBP-8] = X
+ PUSH EBX
+ PUSH ESI
+@@try_again:
+ MOV EBX, EAX
+ CALL GetEmpty // GetEmpty must be assembler version !
+ JZ @@exit
+
+ MOV ECX, [EBX].fHandle
+ JECXZ @@2
+ //MOV EAX, EBX
+ //CALL [EBX].fDetachCanvas // detached in StartDC
+ ADD ESP, -szBitmap
+ PUSH ESP
+ PUSH szBitmap
+ PUSH [EBX].fHandle
+ CALL GetObject
+ TEST EAX, EAX
+ MOV ESI, [ESP].tagBitmap.bmHeight
+ {$IFDEF USE_CMOV}
+ CMOVZ ESI, [EBX].fHeight
+ {$ELSE}
+ JNZ @@1
+ MOV ESI, [EBX].fHeight
+@@1: {$ENDIF}
+
+ ADD ESP, szBitmap
+ CALL StartDC
+
+ PUSH SRCCOPY
+ PUSH 0
+ PUSH 0
+ PUSH EAX
+ CALL @@prepare
+ CALL BitBlt
+ CALL FinishDC
+ JMP @@exit
+
+@@prepare:
+ XCHG ESI, [ESP]
+ PUSH [EBX].fWidth
+ PUSH Y
+ PUSH dword ptr [EBP-8]
+ PUSH dword ptr [EBP-4]
+ JMP ESI
+
+@@2:
+ MOV ECX, [EBX].fDIBHeader
+ JECXZ @@exit
+
+ MOV ESI, [ECX].TBitmapInfoHeader.biHeight
+ TEST ESI, ESI
+ JGE @@20
+ NEG ESI
+@@20:
+ PUSH SRCCOPY
+ PUSH DIB_RGB_COLORS
+ PUSH ECX
+ PUSH [EBX].fDIBBits
+ PUSH ESI
+ PUSH [EBX].fWidth
+ PUSH 0
+ PUSH 0
+ CALL @@prepare
+ CALL StretchDIBits
+ TEST EAX, EAX
+ JNZ @@exit
+ MOV EAX, EBX
+ CALL GetHandle
+ TEST EAX, EAX
+ XCHG EAX, EBX
+ JNZ @@try_again
+@@exit:
+ POP ESI
+ POP EBX
+ MOV ESP, EBP
+end;
+
+procedure TBitmap.StretchDraw(DC: HDC; const Rect: TRect);
+asm
+ PUSH EBX
+ PUSH EDI
+ PUSH EBP
+ MOV EBP, ESP
+ PUSH EDX
+ PUSH ECX
+ MOV EBX, EAX
+ CALL GetEmpty
+ JZ @@exit
+
+ MOV ECX, [EBX].fHandle
+ JECXZ @@2
+
+@@0:
+ CALL StartDC
+ PUSH SRCCOPY
+ PUSH [EBX].fHeight
+ PUSH [EBX].fWidth
+ PUSH 0
+ PUSH 0
+ PUSH EAX
+
+ CALL @@prepare
+ CALL StretchBlt
+ CALL FinishDC
+ JMP @@exit
+
+@@prepare:
+ POP EDI
+ MOV EAX, [EBP-8]
+ MOV EDX, [EAX].TRect.Bottom
+ MOV ECX, [EAX].TRect.Top
+ SUB EDX, ECX
+ PUSH EDX
+ MOV EDX, [EAX].TRect.Right
+ MOV EAX, [EAX].TRect.Left
+ SUB EDX, EAX
+ PUSH EDX
+ PUSH ECX
+ PUSH EAX
+ PUSH dword ptr [EBP-4]
+ JMP EDI
+
+
+@@2: MOV ECX, [EBX].fDIBHeader
+ JECXZ @@exit
+
+ PUSH SRCCOPY
+ PUSH DIB_RGB_COLORS
+ PUSH ECX
+ PUSH [EBX].fDIBBits
+ PUSH [EBX].fHeight
+ PUSH [EBX].fWidth
+ PUSH 0
+ PUSH 0
+ CALL @@prepare
+ CALL StretchDIBits
+ TEST EAX, EAX
+ JG @@exit
+
+ MOV EAX, EBX
+ CALL GetHandle
+ MOV ECX, [EBX].fHandle
+ JECXZ @@exit
+ JMP @@0
+
+@@exit: MOV ESP, EBP
+ POP EBP
+ POP EDI
+ POP EBX
+end;
+
+procedure TBitmap.DrawTransparent(DC: HDC; X, Y: Integer; TranspColor: TColor);
+asm
+ PUSH ECX
+ MOV ECX, TranspColor
+ INC ECX
+ MOV ECX, [Y]
+ JNZ @@2
+ XCHG ECX, [ESP]
+ CALL Draw
+ JMP @@exit
+@@2:
+ ADD ECX, [EAX].fHeight
+ PUSH ECX
+ MOV ECX, [EBP-4]
+ ADD ECX, [EAX].fWidth
+ PUSH ECX
+ PUSH [Y]
+ PUSH dword ptr [EBP-4]
+ MOV ECX, ESP
+ PUSH [TranspColor]
+ CALL StretchDrawTransparent
+@@exit:
+ MOV ESP, EBP
+end;
+
+procedure TBitmap.StretchDrawTransparent(DC: HDC; const Rect: TRect; TranspColor: TColor);
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ MOV EAX, [TranspColor]
+ INC EAX
+ MOV EAX, EBX
+ JNZ @@2
+ CALL StretchDraw
+ JMP @@exit
+@@2:
+ PUSH EDX
+ PUSH ECX
+ CALL GetHandle
+ TEST EAX, EAX
+ JZ @@exit2
+
+ MOV EAX, [TranspColor]
+ CALL Color2RGB
+ MOV ECX, [EBX].fTransMaskBmp
+ JECXZ @@makemask0
+ CMP EAX, [EBX].fTransColor
+ JE @@3
+@@makemask0:
+ MOV [EBX].fTransColor, EAX
+ INC ECX
+ LOOP @@20
+ XOR EAX, EAX // pass height = 0
+ // absolutely no matter what to pass as width
+ CALL NewBitmap
+ MOV [EBX].fTransMaskBmp, EAX
+@@20:
+ MOV EAX, [EBX].fTransMaskBmp
+ PUSH EAX
+ MOV EDX, EBX
+ CALL Assign
+ POP EAX
+ MOV EDX, [EBX].fTransColor
+ CALL Convert2Mask
+@@3:
+ MOV EAX, [EBX].fTransMaskBmp
+ CALL GetHandle
+ POP ECX
+ POP EDX
+ PUSH EAX
+ XCHG EAX, EBX
+ CALL StretchDrawMasked
+ JMP @@exit
+@@exit2:
+ POP ECX
+ POP EDX
+@@exit:
+ POP EBX
+end;
+
+procedure TBitmap.StretchDrawMasked(DC: HDC; const Rect: TRect; Mask: HBitmap);
+asm
+ PUSH EDX // [EBP-4] = DC
+ PUSH ECX // [EBP-8] = Rect
+ PUSH EBX // save EBX
+ MOV EBX, EAX // EBX = @ Self
+ PUSH ESI // save ESI
+ {$IFDEF FIX_TRANSPBMPPALETTE}
+ CALL GetPixelFormat
+ CMP AL, pf4bit
+ JZ @@draw_fixed
+ CMP AL, pf8bit
+ JNZ @@draw_normal
+ @@draw_fixed:
+ XOR EAX, EAX
+ XOR EDX, EDX
+ CALL NewBitmap
+ MOV ESI, EAX
+ MOV EDX, EBX
+ CALL Assign
+ MOV EAX, ESI
+ XOR EDX, EDX
+ MOV DL, pf32bit
+ CALL SetPixelFormat
+ MOV EAX, ESI
+ MOV EDX, [EBP-4]
+ MOV ECX, [EBP-8]
+ PUSH [Mask]
+ CALL StretchDrawMasked
+ XCHG EAX, ESI
+ CALL TObj.RefDec
+ JMP @@exit
+ @@draw_normal:
+ MOV EAX, EBX
+ {$ENDIF FIX_TRANSPBMPPALETTE}
+ CALL GetHandle
+ TEST EAX, EAX
+ JZ @@to_exit
+
+ PUSH 0
+ CALL CreateCompatibleDC
+ PUSH EAX // [EBP-20] = MaskDC
+
+ PUSH [Mask]
+ PUSH EAX
+ CALL SelectObject
+ PUSH EAX // [EBP-24] = Save4Mask
+
+ CALL StartDC // [EBP-28] = DCfrom; [EBP-32] = Save4From
+
+ PUSH [EBX].fHeight
+ PUSH [EBX].fWidth
+ PUSH EAX
+ CALL CreateCompatibleBitmap
+ PUSH EAX // [EBP-36] = MemBmp
+
+ PUSH 0
+ CALL CreateCompatibleDC
+ PUSH EAX // [EBP-40] = MemDC
+
+ PUSH dword ptr [EBP-36] //MemBmp
+ PUSH EAX
+ CALL SelectObject
+ PUSH EAX // [EBP-44] = Save4Mem
+
+ PUSH SRCCOPY
+ MOV EAX, [EBP-20] //MaskDC
+ CALL @@stretch1
+
+ PUSH SRCERASE
+ MOV EAX, [EBP-28] //DCfrom
+ CALL @@stretch1
+
+ PUSH 0
+ PUSH dword ptr [EBP-4] //DC
+ CALL SetTextColor
+ PUSH EAX // [EBP-48] = crText
+
+ PUSH $FFFFFF
+ PUSH dword ptr [EBP-4] //DC
+ CALL Windows.SetBkColor
+ PUSH EAX // [EBP-52] = crBack
+
+ PUSH SRCAND
+ MOV EAX, [EBP-20] //MaskDC
+ CALL @@stretch2
+
+ PUSH SRCINVERT
+ MOV EAX, [EBP-40] //MemDC
+ CALL @@stretch2
+
+ PUSH dword ptr [EBP-4] //DC
+ CALL Windows.SetBkColor
+
+ PUSH dword ptr [EBP-4] //DC
+ CALL SetTextColor
+
+ MOV ESI, offset[FinishDC]
+ CALL ESI
+ CALL DeleteObject // DeleteObject( MemBmp )
+ CALL ESI
+ CALL ESI
+@@to_exit:
+ STC
+ JC @@exit
+
+@@stretch1:
+ POP ESI
+ PUSH [EBX].fHeight
+ PUSH [EBX].fWidth
+ XOR EDX, EDX
+ PUSH EDX
+ PUSH EDX
+ PUSH EAX
+ PUSH [EBX].fHeight
+ PUSH [EBX].fWidth
+ PUSH EDX
+ PUSH EDX
+ PUSH dword ptr [EBP-40] //MemDC
+ JMP @@stretch3
+
+@@stretch2:
+ POP ESI
+ PUSH [EBX].fHeight
+ PUSH [EBX].fWidth
+ PUSH 0
+ PUSH 0
+ PUSH EAX
+ MOV EAX, [EBP-8] //Rect
+ MOV EDX, [EAX].TRect.Bottom
+ MOV ECX, [EAX].TRect.Top
+ SUB EDX, ECX
+ PUSH EDX
+ MOV EDX, [EAX].TRect.Right
+ MOV EAX, [EAX].TRect.Left
+ SUB EDX, EAX
+ PUSH EDX
+ PUSH ECX
+ PUSH EAX
+ PUSH dword ptr [EBP-4] //DC
+@@stretch3:
+ CALL StretchBlt
+ JMP ESI
+
+@@exit:
+ POP ESI
+ POP EBX
+ MOV ESP, EBP
+end;
+
+procedure DetachBitmapFromCanvas( Sender: PBitmap );
+asm
+ XOR ECX, ECX
+ XCHG ECX, [EAX].TBitmap.fCanvasAttached
+ JECXZ @@exit
+ PUSH ECX
+ MOV EAX, [EAX].TBitmap.fCanvas
+ PUSH [EAX].TCanvas.fHandle
+ CALL SelectObject
+@@exit:
+end;
+
+function TBitmap.GetCanvas: PCanvas;
+asm
+ PUSH EBX
+ MOV EBX, EAX
+ CALL GetEmpty
+ JZ @@exit
+ MOV EAX, EBX
+ CALL GetHandle
+ TEST EAX, EAX
+ JZ @@exit
+ MOV ECX, [EBX].fCanvas
+ INC ECX
+ LOOP @@ret_Canvas
+
+ MOV [EBX].fApplyBkColor2Canvas, offset[ApplyBitmapBkColor2Canvas]
+ //CALL CreateCompatibleDC
+ XOR EAX, EAX
+ //PUSH EAX
+ CALL NewCanvas
+ MOV [EBX].fCanvas, EAX
+ //MOV [EAX].TCanvas.fIsAlienDC, 0
+ MOV [EAX].TCanvas.fOnChangeCanvas.TMethod.Code, offset[CanvasChanged]
+ MOV [EAX].TCanvas.fOnChangeCanvas.TMethod.Data, EBX
+ CALL TCanvas.GetBrush
+ XOR EDX, EDX
+ MOV ECX, [EBX].fBkColor
+ JECXZ @@ret_Canvas
+ CALL TGraphicTool.SetInt
+
+@@ret_Canvas:
+ MOV EAX, [EBX].fCanvas
+ MOV ECX, [EAX].TCanvas.fHandle
+ INC ECX
+ LOOP @@attach_Canvas
+ PUSH EAX
+ MOV [EBX].fCanvasAttached, ECX
+ PUSH ECX
+ CALL CreateCompatibleDC
+ XCHG EDX, EAX
+ POP EAX
+ CALL TCanvas.SetHandle
+
+@@attach_Canvas:
+ MOV ECX, [EBX].fCanvasAttached
+ INC ECX
+ LOOP @@2
+ PUSH [EBX].fHandle
+ MOV EAX, [EBX].fCanvas
+ CALL TCanvas.GetHandle
+ PUSH EAX
+ CALL SelectObject
+ MOV [EBX].fCanvasAttached, EAX
+
+@@2: MOV [EBX].fDetachCanvas, offset[DetachBitmapFromCanvas]
+ MOV EAX, [EBX].fCanvas
+@@exit: POP EBX
+end;
+
+function TBitmap.GetEmpty: Boolean;
+asm
+ PUSH ECX
+ MOV ECX, [EAX].fWidth
+ JECXZ @@1
+ MOV ECX, [EAX].fHeight
+@@1: TEST ECX, ECX
+ POP ECX
+ SETZ AL
+end;
+
+procedure TBitmap.LoadFromFile(const Filename: KOLString);
+asm
+ PUSH EAX
+ XCHG EAX, EDX
+ CALL NewReadFileStream
+ XCHG EDX, EAX
+ POP EAX
+ PUSH EDX
+ CALL LoadFromStream
+ POP EAX
+ CALL TObj.RefDec
+end;
+
+function TBitmap.ReleaseHandle: HBitmap;
+asm
+ PUSH EBX
+ MOV EBX, EAX
+ XOR EDX, EDX
+ CALL SetHandleType
+ MOV EAX, EBX
+ CALL GetHandle
+ TEST EAX, EAX
+ JZ @@exit
+
+ CMP [EBX].fDIBAutoFree, 0
+ JZ @@1
+ MOV EAX, [EBX].fDIBSize
+ PUSH EAX
+ PUSH EAX
+ PUSH GMEM_FIXED {or GMEM_ZEROINIT}
+ CALL GlobalAlloc
+ MOV EDX, EAX
+ XCHG EAX, [EBX].fDIBBits
+ POP ECX
+ CALL System.Move
+@@1:
+ XOR EAX, EAX
+ MOV [EBX].fDIBAutoFree, AL
+ XCHG EAX, [EBX].fHandle
+
+@@exit: POP EBX
+end;
+
+procedure TBitmap.SaveToFile(const Filename: KOLString);
+asm
+ PUSH EAX
+ PUSH EDX
+ CALL GetEmpty
+ POP EAX
+ JZ @@exit
+ CALL NewWriteFileStream
+ XCHG EDX, EAX
+ POP EAX
+ PUSH EDX
+ CALL SaveToStream
+ POP EAX
+ CALL TObj.RefDec
+ PUSH EAX
+@@exit: POP EAX
+end;
+
+procedure TBitmap.SetHandle(const Value: HBitmap);
+const szB = sizeof( tagBitmap );
+ szDIB = sizeof( TDIBSection );
+ szBIH = sizeof( TBitmapInfoHeader ); // = 40
+asm
+ PUSH EBX
+ MOV EBX, EAX
+ PUSH EDX
+ CALL Clear
+ POP ECX
+ TEST ECX, ECX
+ JZ @@exit
+ PUSH ECX
+ ADD ESP, -szDIB
+
+ CALL WinVer
+ CMP AL, wvNT
+ JB @@ddb
+
+ PUSH ESP
+ PUSH szDIB
+ PUSH ECX
+ CALL GetObject
+ CMP EAX, szDIB
+ JNZ @@ddb
+
+ MOV [EBX].fHandleType, 0
+ MOV EAX, [ESP].TDIBSection.dsBm.bmWidth
+ MOV [EBX].fWidth, EAX
+ MOV EDX, [ESP].TDIBSection.dsBm.bmHeight
+ MOV [EBX].fHeight, EDX
+ MOVZX ECX, [ESP].TDIBSection.dsBm.bmBitsPixel
+ CALL PrepareBitmapHeader
+ MOV [EBX].fDIBHeader, EAX
+ LEA EDX, [EAX].TBitmapInfo.bmiColors
+ LEA EAX, [ESP].TDIBSection.dsBitfields
+ XOR ECX, ECX
+ MOV CL, 12
+ CALL System.Move
+
+ MOV EDX, [ESP].TDIBSection.dsBm.bmBits
+ MOV [EBX].fDIBBits, EDX
+ MOV EDX, [ESP].TDIBSection.dsBmih.biSizeImage
+ MOV [EBX].fDIBSize, EDX
+ MOV [EBX].fDIBAutoFree, 1
+ ADD ESP, szDIB
+ POP [EBX].fHandle
+ JMP @@exit
+
+@@ddb:
+ MOV ECX, [ESP+szDIB]
+ PUSH ESP
+ PUSH szB
+ PUSH ECX
+ CALL GetObject
+ POP EDX
+ POP EDX // bmWidth
+ POP ECX // bmHeight
+ ADD ESP, szDIB-12
+ TEST EAX, EAX
+ JZ @@exit
+ MOV [EBX].fWidth, EDX
+ MOV [EBX].fHeight, ECX
+ POP dword ptr [EBX].fHandle
+ MOV [EBX].fHandleType, 1
+@@exit: POP EBX
+end;
+
+procedure TBitmap.SetHeight(const Value: Integer);
+var
+ pf : TPixelFormat;
+asm
+ CMP EDX, [EAX].fHeight
+ JE @@exit
+
+ PUSHAD
+ CALL GetPixelFormat
+ MOV pf, AL
+ POPAD
+
+ PUSHAD
+ XOR EDX, EDX
+ INC EDX
+ CALL SetHandleType
+ POPAD
+ MOV [EAX].fHeight, EDX
+ CALL FormatChanged
+
+ PUSHAD
+ MOV DL, pf
+ CALL SetPixelFormat
+ POPAD
+@@exit:
+end;
+
+procedure TBitmap.SetPixelFormat(Value: TPixelFormat);
+asm
+ PUSH EBX
+ MOV EBX, EAX
+ CALL GetEmpty // if Empty then Exit;
+ JZ @@exit //
+ MOV EAX, EBX //
+ PUSH EDX
+ CALL GetPixelFormat
+ POP EDX
+ CMP EAX, EDX
+ JE @@exit
+ TEST EDX, EDX
+ MOV EAX, EBX
+ JNE @@2
+ POP EBX
+ INC EDX // EDX = bmDDB
+ JMP SetHandleType
+@@2:
+ MOV [EBX].fNewPixelFormat, DL
+@@3:
+ XOR EDX, EDX
+ CALL SetHandleType
+ XCHG EAX, EBX
+ CMP EAX, 0
+@@exit:
+ POP EBX
+ JNE FormatChanged
+end;
+
+function CalcScanLineSize( Header: PBitmapInfoHeader ): Integer;
+asm
+ MOVZX EDX, [EAX].TBitmapInfoHeader.biBitCount
+ MOV EAX, [EAX].TBitmapInfoHeader.biWidth
+ MUL EDX
+ ADD EAX, 31
+ SHR EAX, 3
+ AND EAX, -4
+end;
+
+procedure FillBmpWithBkColor( Bmp: PBitmap; DC2: HDC; oldWidth, oldHeight: Integer );
+asm
+ PUSH EBX
+ PUSH ESI
+ XCHG EAX, EBX
+ PUSH EDX // [EBP-12] = DC2
+ PUSH ECX // [EBP-16] = oldWidth
+ MOV EAX, [EBX].TBitmap.fBkColor
+ CALL Color2RGB
+ TEST EAX, EAX
+ JZ @@exit
+ XCHG ESI, EAX // ESI = Color2RGB( Bmp.fBkColor )
+ MOV EAX, EBX
+ CALL TBitmap.GetHandle
+ TEST EAX, EAX
+ JZ @@exit
+ PUSH EAX //fHandle
+ PUSH dword ptr [EBP-12] //DC2
+ CALL SelectObject
+ PUSH EAX // [EBP-20] = oldBmp
+ PUSH ESI
+ CALL CreateSolidBrush
+ XCHG ESI, EAX // ESI = Br
+ PUSH [EBX].TBitmap.fHeight
+ PUSH [EBX].TBitmap.fWidth
+ MOV EAX, [oldHeight]
+ MOV EDX, [EBP-16] //oldWidth
+ CMP EAX, [EBX].TBitmap.fHeight
+ JL @@fill
+ CMP EDX, [EBX].TBitmap.fWidth
+ JGE @@nofill
+@@fill: CMP EAX, [EBX].TBitmap.fHeight
+ JNE @@1
+ XOR EAX, EAX
+@@1:
+ CMP EDX, [EBX].TBitmap.fWidth
+ JNZ @@2
+ CDQ
+@@2: PUSH EAX
+ PUSH EDX
+
+ MOV EDX, ESP
+ PUSH ESI
+ PUSH EDX
+ PUSH dword ptr [EBP-12] //DC2
+ CALL Windows.FillRect
+ POP ECX
+ POP ECX
+@@nofill:
+ POP ECX
+ POP ECX
+ PUSH ESI //Br
+ CALL DeleteObject
+ PUSH dword ptr [EBP-12] //DC2
+ CALL SelectObject
+@@exit:
+ POP ECX
+ POP EDX
+ POP ESI
+ POP EBX
+end;
+
+procedure TBitmap.FormatChanged;
+type tBIH = TBitmapInfoHeader;
+ tBmp = tagBitmap;
+const szBIH = Sizeof( tBIH );
+ szBmp = Sizeof( tBmp );
+asm
+ PUSH EAX
+ CALL GetEmpty
+ POP EAX
+ JZ @@exit
+ PUSHAD
+ MOV EBX, EAX
+ CALL [EBX].fDetachCanvas
+ XOR EAX, EAX
+ MOV [EBX].fScanLineSize, EAX
+ MOV [EBX].fGetDIBPixels, EAX
+ MOV [EBX].fSetDIBPixels, EAX
+ MOV ESI, [EBX].fWidth // ESI := oldWidth
+ MOV EDI, [EBX].fHeight // EDI := oldHeight
+ MOV ECX, [EBX].fDIBBits
+ JECXZ @@noDIBBits
+ MOV EAX, [EBX].fDIBHeader
+ MOV ESI, [EAX].TBitmapInfo.bmiHeader.biWidth
+ MOV EDI, [EAX].TBitmapInfo.bmiHeader.biHeight
+ TEST EDI, EDI
+ JGE @@1
+ NEG EDI
+@@1: JMP @@createDC2
+@@noDIBBits:
+ MOV ECX, [EBX].fHandle
+ JECXZ @@createDC2
+ ADD ESP, -24 // -szBmp
+ PUSH ESP
+ PUSH 24 //szBmp
+ PUSH ECX
+ CALL GetObject
+ XCHG ECX, EAX
+ JECXZ @@2
+ MOV ESI, [ESP].tBmp.bmWidth
+ MOV EDI, [ESP].tBmp.bmHeight
+@@2: ADD ESP, 24 //szBmp
+@@createDC2:
+ PUSH 0
+ CALL CreateCompatibleDC
+ PUSH EAX // > DC2
+ CMP [EBX].fHandleType, bmDDB
+ JNE @@DIB_handle_type
+ PUSH 0
+ CALL GetDC
+ PUSH EAX // > DC0
+ PUSH [EBX].fHeight
+ PUSH [EBX].fWidth
+ PUSH EAX
+ CALL CreateCompatibleBitmap
+ XCHG EBP, EAX // EBP := NewHandle
+ PUSH 0
+ CALL ReleaseDC // <
+ POP EDX
+ PUSH EDX // EDX := DC2
+ PUSH EBP
+ PUSH EDX
+ CALL SelectObject
+ PUSH EAX // > OldBmp
+ PUSH [EBX].fHeight // prepare Rect(0,0,fWidth,fHeight)
+ PUSH [EBX].fWidth
+ PUSH 0
+ PUSH 0
+ MOV EAX, [EBX].fBkColor
+ CALL Color2RGB
+ PUSH EAX
+ CALL CreateSolidBrush
+ MOV EDX, ESP
+ PUSH EAX // > Br
+ PUSH EAX
+ PUSH EDX
+ PUSH dword ptr [ESP+32] // (DC2)
+ CALL Windows.FillRect
+ CALL DeleteObject // <
+ ADD ESP, 16 // remove Rect
+ MOV ECX, [EBX].fDIBBits
+ JECXZ @@draw
+ PUSH dword ptr [ESP+4] // (DC2)
+ CALL SelectObject // < (OldBmp)
+ PUSH DIB_RGB_COLORS // : DIB_RGB_COLORS
+ PUSH [EBX].fDIBHeader // : fDIBHeader
+ PUSH [EBX].fDIBBits // : fDIBBits
+ PUSH [EBX].fHeight // : fHeight
+ PUSH 0 // : 0
+ PUSH EBP // : NewHandle
+ PUSH dword ptr [ESP+24] // (DC2)
+ CALL SetDIBits
+ JMP @@clearData
+@@draw:
+ MOV EDX, [ESP+4]
+ PUSH EDX // prepare DC2 for SelectObject
+ MOV EAX, EBX
+ XOR ECX, ECX
+ PUSH ECX
+ CALL Draw
+ CALL SelectObject
+@@clearData:
+ MOV EAX, EBX
+ CALL ClearData
+ MOV [EBX].fHandle, EBP
+
+ JMP @@fillBkColor
+
+@@DIB_handle_type: // [ESP] = DC2
+ MOVZX EAX, [EBX].fNewPixelFormat
+@@getBitsPixel:
+ XCHG ECX, EAX
+ MOV CL, [ECX] + offset BitCounts
+ MOVZX EAX, [DefaultPixelFormat]
+ JECXZ @@getBitsPixel
+ XOR EBP, EBP // NewHandle := 0
+ MOV EAX, [EBX].fWidth // EAX := fWidth
+ MOV EDX, [EBX].fHeight // EDX := fHeight
+ CALL PrepareBitmapHeader
+ PUSH EAX // > NewHeader
+ CMP [EBX].fNewPixelFormat, pf16bit
+ JNE @@newHeaderReady
+ CALL PreparePF16bit
+@@newHeaderReady:
+ POP EAX
+ PUSH EAX
+ CALL CalcScanLineSize
+ MOV EDX, [EBX].fHeight
+ MUL EDX
+ PUSH EAX // > sizeBits
+
+ PUSH EAX
+ PUSH GMEM_FIXED
+ CALL GlobalAlloc
+
+ PUSH EAX // > NewBits
+ PUSH DIB_RGB_COLORS
+ PUSH dword ptr [ESP+12] // (NewHeader)
+ PUSH EAX
+ MOV EAX, [EBX].fHeight
+ CMP EAX, EDI
+ {$IFDEF USE_CMOV}
+ CMOVG EAX, EDI
+ {$ELSE}
+ JLE @@3
+ MOV EAX, EDI
+@@3: {$ENDIF}
+
+ PUSH EAX
+ PUSH 0
+ MOV EAX, EBX
+ CALL GetHandle
+ PUSH EAX
+ PUSH dword ptr [ESP+36] // (DC2)
+ CALL GetDIBits
+
+ MOV EDX, [EBX].fHeight
+ CMP EDX, EDI
+ {$IFDEF USE_CMOV}
+ CMOVG EDX, EDI
+ {$ELSE}
+ JLE @@30
+ MOV EDX, EDI
+@@30: {$ENDIF}
+
+ CMP EAX, EDX
+ JE @@2clearData
+
+ CALL GlobalFree
+
+ XOR EAX, EAX
+ PUSH EAX
+
+ MOV EDX, ESP // EDX = @NewBits
+ MOV ECX, [ESP+8] // ECX = @NewHeader
+ PUSH EAX // -> 0
+ PUSH EAX // -> 0
+ PUSH EDX // -> @NewBits
+ PUSH DIB_RGB_COLORS // -> DIB_RGB_COLORS
+ PUSH ECX // -> @NewHeader
+ PUSH dword ptr [ESP+32] // -> DC2
+ CALL CreateDIBSection
+
+ XOR ESI, -1 // use OldWidth to store NewDIBAutoFree flag
+
+ XCHG EBP, EAX // EBP := NewHandle
+ PUSH EBP
+ PUSH dword ptr [ESP+16] // -> DC2
+ CALL SelectObject
+ PUSH EAX // save oldBmp
+ MOV EDX, [ESP+16] // DC2 -> EDX (DC)
+ XOR ECX, ECX // 0 -> ECX (X)
+ PUSH ECX // 0 -> stack (Y)
+ MOV EAX, EBX
+ CALL TBitmap.Draw
+ PUSH dword ptr [ESP+16] // -> DC2
+ CALL SelectObject
+
+@@2clearData:
+ MOV EAX, EBX
+ CALL ClearData
+
+ POP [EBX].fDIBBits
+ POP [EBX].fDIBSize
+ POP [EBX].fDIBHeader
+ MOV [EBX].fHandle, EBP
+
+ TEST ESI, ESI
+ MOV [EBX].fDIBAutoFree, 0
+ JGE @@noDIBautoFree
+ INC [EBX].fDIBAutoFree
+@@noDIBautoFree:
+
+@@fillBkColor:
+ MOV ECX, [EBX].fFillWithBkColor
+ JECXZ @@deleteDC2
+ POP EDX // (DC2)
+ PUSH EDX
+ PUSH EDI
+ XCHG ECX, ESI
+ XCHG EAX, EBX
+ CALL ESI
+@@deleteDC2:
+ CALL DeleteDC
+ POPAD
+@@exit:
+end;
+
+function TBitmap.GetScanLine(Y: Integer): Pointer;
+asm
+ MOV ECX, [EAX].fDIBHeader
+ JECXZ @@exit
+ MOV ECX, [ECX].TBitmapInfoHeader.biHeight
+ TEST ECX, ECX
+ JL @@1
+
+ SUB ECX, EDX
+ DEC ECX
+ MOV EDX, ECX
+
+@@1: MOV ECX, [EAX].fScanLineSize
+ INC ECX
+ PUSH [EAX].fDIBBits
+ LOOP @@2
+
+ PUSH EDX
+ CALL GetScanLineSize
+ POP EDX
+ XCHG ECX, EAX
+
+@@2: XCHG EAX, ECX
+ MUL EDX
+ POP ECX
+ ADD ECX, EAX
+
+@@exit: XCHG EAX, ECX
+end;
+
+function TBitmap.GetScanLineSize: Integer;
+asm
+ MOV ECX, [EAX].fDIBHeader
+ JECXZ @@exit
+
+ PUSH EAX
+ XCHG EAX, ECX
+ CALL CalcScanLineSize
+ XCHG ECX, EAX
+ POP EAX
+ MOV [EAX].fScanLineSize, ECX
+
+@@exit: XCHG EAX, ECX
+end;
+
+procedure TBitmap.CanvasChanged( Sender : PObj );
+asm
+ PUSH EAX
+
+ XCHG EAX, EDX
+ CALL TCanvas.GetBrush
+ MOV EDX, [EAX].TGraphicTool.fData.Color
+
+ POP EAX
+ MOV [EAX].fBkColor, EAX
+ CALL ClearTransImage
+end;
+
+procedure TBitmap.Dormant;
+asm
+ PUSH EAX
+ CALL RemoveCanvas
+ POP EAX
+ MOV ECX, [EAX].fHandle
+ JECXZ @@exit
+ CALL ReleaseHandle
+ PUSH EAX
+ CALL DeleteObject
+@@exit:
+end;
+
+procedure TBitmap.SetBkColor(const Value: TColor);
+asm
+ CMP [EAX].fBkColor, EDX
+ JE @@exit
+ MOV [EAX].fBkColor, EDX
+ MOV [EAX].fFillWithBkColor, offset[FillBmpWithBkColor]
+ MOV ECX, [EAX].fApplyBkColor2Canvas
+ JECXZ @@exit
+ CALL ECX
+@@exit:
+end;
+
+function TBitmap.Assign(SrcBmp: PBitmap): Boolean;
+const szBIH = sizeof(TBitmapInfoHeader);
+asm
+ PUSHAD
+ XCHG EBX, EAX
+@@clear:
+ MOV ESI, EDX
+ MOV EAX, EBX
+ CALL Clear
+ MOV EAX, ESI
+ OR EAX, EAX
+ JZ @@exit
+ CALL GetEmpty
+ JZ @@exit
+ MOV EAX, [ESI].fWidth
+ MOV [EBX].fWidth, EAX
+ MOV EAX, [ESI].fHeight
+ MOV [EBX].fHeight, EAX
+ MOVZX ECX, [ESI].fHandleType
+ MOV [EBX].fHandleType, CL
+ JECXZ @@fmtDIB
+
+ DEC ECX // ECX = 0
+ PUSH ECX
+ PUSH ECX
+ PUSH ECX
+ PUSH ECX //IMAGE_BITMAP=0
+ PUSH [ESI].fHandle
+ CALL CopyImage
+ MOV [EBX].fHandle, EAX
+ TEST EAX, EAX
+ XCHG EDX, EAX
+ JZ @@clear
+ JMP @@exit
+
+@@fmtDIB:
+ XCHG EAX, ECX
+ MOV AX, szBIH+1024
+ PUSH EAX
+ CALL System.@GetMem
+ MOV [EBX].fDIBHeader, EAX
+ XCHG EDX, EAX
+ POP ECX
+ MOV EAX, [ESI].fDIBHeader
+ CALL System.Move
+ MOV EAX, [ESI].fDIBSize
+ MOV [EBX].fDIBSize, EAX
+ PUSH EAX
+ PUSH EAX
+ PUSH GMEM_FIXED
+ CALL GlobalAlloc
+ MOV [EBX].fDIBBits, EAX
+ XCHG EDX, EAX
+ POP ECX
+ MOV EAX, [ESI].fDIBBits
+ CALL System.Move
+
+ INC EBX // reset "ZF"
+
+@@exit:
+ POPAD
+ SETNZ AL
+end;
+
+procedure TBitmap.RemoveCanvas;
+asm
+ PUSH EAX
+ CALL [EAX].fDetachCanvas
+ POP EDX
+ XOR EAX, EAX
+ XCHG EAX, [EDX].fCanvas
+ CALL TObj.RefDec
+end;
+
+function TBitmap.DIBPalNearestEntry(Color: TColor): Integer;
+const szBIH = sizeof(TBitmapInfoHeader);
+asm
+ PUSH EBX
+ PUSH ESI
+ PUSH EDI
+ XCHG ESI, EAX
+ XCHG EAX, EDX
+ CALL Color2RGBQuad
+ XCHG EDI, EAX
+ MOV EAX, ESI
+ CALL GetDIBPalEntryCount
+ XCHG ECX, EAX
+ XOR EAX, EAX
+ JECXZ @@exit
+
+ MOV ESI, [ESI].fDIBHeader
+ ADD ESI, szBIH
+ XOR EDX, EDX
+ PUSH EDX
+ DEC DX
+
+@@loo: LODSD
+ XOR EAX, EDI
+ MOV EBX, EAX
+ SHR EBX, 16
+ MOV BH, 0
+ ADD AL, AH
+ MOV AH, 0
+ ADC AX, BX
+ CMP AX, DX
+ JAE @@1
+ MOV DX, AX
+ POP EBX
+ PUSH EDX // save better index (in high order word)
+@@1: ADD EDX, $10000 // increment index
+ LOOP @@loo
+
+ XCHG EAX, ECX
+ POP AX
+ POP AX
+@@exit:
+ POP EDI
+ POP ESI
+ POP EBX
+end;
+
+function TBitmap.GetDIBPalEntries(Idx: Integer): TColor;
+const szBIH = sizeof(TBitmapInfoHeader);
+asm
+ MOV ECX, [EAX].fDIBHeader
+ JECXZ @@exit
+
+ MOV ECX, [ECX+szBIH+EDX*4]
+ INC ECX
+
+@@exit: DEC ECX
+ XCHG EAX, ECX
+end;
+
+function TBitmap.GetDIBPalEntryCount: Integer;
+asm
+ PUSH EAX
+ CALL GetEmpty
+ POP EAX
+ JZ @@ret0
+ CALL GetPixelFormat
+ MOVZX ECX, AL
+ MOV EAX, ECX
+ LOOP @@1
+ // pf1bit:
+ INC EAX
+ RET
+@@1:
+ LOOP @@2
+ // pf4bit:
+ MOV AL, 16
+ RET
+@@2:
+ LOOP @@ret0
+ // pf8bit:
+ XOR EAX, EAX
+ INC AH
+ RET
+@@ret0:
+ XOR EAX, EAX
+end;
+
+procedure TBitmap.ClearTransImage;
+asm
+ OR [EAX].fTransColor, -1
+ XOR EDX, EDX
+ XCHG [EAX].fTransMaskBmp, EDX
+ XCHG EAX, EDX
+ CALL TObj.RefDec
+end;
+
+{$IFDEF USE_OLDCONVERT2MASK}
+procedure TBitmap.Convert2Mask(TranspColor: TColor);
+asm
+ PUSH EBX
+ PUSH ESI
+ MOV EBX, EAX
+ MOV ESI, EDX
+ CALL GetHandle
+ TEST EAX, EAX
+ JZ @@exit
+
+ PUSH 0
+ PUSH 1
+ PUSH 1
+ PUSH [EBX].fHeight
+ PUSH [EBX].fWidth
+ CALL CreateBitmap
+ PUSH EAX // MonoHandle
+ PUSH 0
+ CALL CreateCompatibleDC
+ POP EDX
+ PUSH EDX
+ PUSH EAX // MonoDC
+
+ PUSH EDX
+ PUSH EAX
+ CALL SelectObject
+ PUSH EAX // SaveMono
+
+ CALL StartDC // DCfrom, SaveFrom
+ XCHG EAX, ESI
+ CALL Color2RGB
+ PUSH EAX // Color2RGB(TranspColor)
+ PUSH dword ptr [ESP+8] //DCfrom
+ CALL Windows.SetBkColor
+ PUSH EAX // SaveBkColor
+
+ PUSH SRCCOPY
+ PUSH 0
+ PUSH 0
+ PUSH dword ptr [ESP+12+4+4] //DCfrom
+ PUSH [EBX].fHeight
+ PUSH [EBX].fWidth
+ PUSH 0
+ PUSH 0
+ PUSH dword ptr [ESP+32+16] //MonoDC
+ CALL BitBlt
+
+ PUSH dword ptr [ESP+8] //DCfrom
+ CALL Windows.SetBkColor // ESP-> SaveFrom
+ CALL FinishDC // ESP-> SaveMono
+ CALL FinishDC // ESP-> MonoHandle
+
+ MOV EAX, EBX
+ CALL ClearData
+ POP [EBX].fHandle
+ MOV [EBX].fHandleType, bmDDB
+@@exit:
+ POP ESI
+ POP EBX
+end;
+{$ELSE USE_OLDCONVERT2MASK} //Pascal
+procedure TBitmap.Convert2Mask(TranspColor: TColor);
+asm
+ PUSH EBX
+ PUSH ESI
+ PUSH EBP
+ PUSH EDI
+ XCHG EBP, EAX // EBP = @ Self
+ XCHG EAX, EDX // EAX = TranspColor
+ CALL Color2RGB
+ XCHG EBX, EAX // EBX := Color2RGB( TranspColor );
+ MOV EAX, EBP // EAX := @ Self;
+ CALL GetPixelFormat
+ CMP AL, pf15bit
+ JB @@SwapRB
+ CMP AL, pf24bit
+ JB @@noSwapRB
+@@SwapRB:
+ BSWAP EBX
+ SHR EBX, 8
+@@noSwapRB:
+ MOV DL, pf4bit
+ CMP AL, DL
+ JB @@setpixelformat
+@@1: MOV DL, pf32bit
+ CMP AL, DL
+ JBE @@translate
+@@setpixelformat:
+ MOV EAX, EBP
+ CALL SetPixelFormat
+@@translate:
+ MOV EAX, [EBP].fWidth
+ MOV EDX, [EBP].fHeight
+ MOV CL, pf1bit
+ CALL NewDibBitmap
+ PUSH EAX
+ XOR EDX, EDX
+ INC EDX
+ MOV ECX, $FFFFFF
+ CALL SetDIBPalEntries
+ XOR EDX, EDX
+@@Yloop:CMP EDX, [EBP].fHeight
+ JGE @@exit
+ PUSH EDX
+ MOV EAX, EBP
+ CALL GetScanLine
+ XCHG ESI, EAX
+ MOV EAX, [ESP+4]
+ POP EDX
+ PUSH EDX
+ CALL GetScanLine
+ XCHG EDI, EAX
+ MOV EAX, EBP
+ CALL GetPixelFormat
+ MOVZX ECX, AL
+ SUB ECX, pf4bit
+ MOV DL, 8
+ JNE @@chk_pf8bit
+ //-------- pf4bit:
+ CMP dword ptr [ESP], 0
+ JNZ @@4_0
+ XOR EDX, EDX
+@@4_searchentry:
+ PUSH EDX
+ MOV EAX, EBP //[ESP+8]
+ CALL GetDIBPalEntries
+ CMP EAX, EBX
+ POP EDX
+ JZ @@4_foundentry
+ INC EDX
+ CMP EDX, 16
+ JB @@4_searchentry
+@@4_foundentry:
+ XCHG EBX, EDX
+ MOV DL, 8
+@@4_0: MOV ECX, [EBP].fWidth
+ INC ECX
+ SHR ECX, 1
+@@Xloop_pf4bit:
+ MOV AH, [ESI]
+ SHR AH, 4
+ CMP AH, BL
+ SETZ AH
+ SHL AL, 1
+ OR AL, AH
+ MOV AH, [ESI]
+ AND AH, $0F
+ CMP AH, BL
+ SETZ AH
+ SHL AL, 1
+ OR AL, AH
+ DEC DL
+ DEC DL
+ JNZ @@4_1
+ STOSB
+ MOV DL, 8
+@@4_1: INC ESI
+ LOOP @@Xloop_pf4bit
+ JMP @@nextline
+@@chk_pf8bit:
+ LOOP @@chk_pf15bit
+ //-------- pf4bit:
+ CMP dword ptr [ESP], 0
+ JNZ @@8_0
+ XOR EDX, EDX
+@@8_searchentry:
+ PUSH EDX
+ MOV EAX, EBP //[ESP+8]
+ CALL GetDIBPalEntries
+ CMP EAX, EBX
+ POP EDX
+ JZ @@8_foundentry
+ INC DL
+ JNZ @@8_searchentry
+@@8_foundentry:
+ XCHG EBX, EDX
+ MOV DL, 8
+@@8_0: MOV ECX, [EBP].fWidth
+ INC ECX
+@@Xloop_pf8bit:
+ CMP BL, [ESI]
+ SETZ AH
+ SHL AL, 1
+ OR AL, AH
+ DEC DL
+ JNZ @@8_1
+ STOSB
+ MOV DL, 8
+@@8_1: INC ESI
+ LOOP @@Xloop_pf8bit
+ JMP @@nextline
+@@chk_pf15bit:
+ LOOP @@chk_pf16bit
+ //-------- pf15bit:
+ CMP dword ptr [ESP], 0
+ JNZ @@15_0
+ XCHG EAX, EBX
+ PUSH EDX
+ CALL Color2Color15
+ POP EDX
+ XCHG EBX, EAX
+@@15_0: MOV ECX, [EBP].fWidth
+@@Xloop_pf15bit:
+ CMP word ptr [ESI], BX
+ SETZ AH
+ SHL AL, 1
+ OR AL, AH
+ DEC DL
+ JNZ @@15_1
+ STOSB
+ MOV DL, 8
+@@15_1: ADD ESI, 2
+ LOOP @@Xloop_pf15bit
+ JMP @@nextline
+@@chk_pf16bit:
+ LOOP @@chk_pf24bit
+ //-------- pf16bit:
+ CMP dword ptr [ESP], 0
+ JNZ @@16_0
+ XCHG EAX, EBX
+ PUSH EDX
+ CALL Color2Color16
+ POP EDX
+ XCHG EBX, EAX
+@@16_0: MOV ECX, [EBP].fWidth
+@@Xloop_pf16bit:
+ CMP word ptr [ESI], BX
+ SETZ AH
+ SHL AL, 1
+ OR AL, AH
+ DEC DL
+ JNZ @@16_1
+ STOSB
+ MOV DL, 8
+@@16_1: ADD ESI, 2
+ LOOP @@Xloop_pf16bit
+ JMP @@nextline
+@@chk_pf24bit:
+ LOOP @@chk_pf32bit
+ //-------- pf24bit:
+ MOV ECX, [EBP].fWidth
+ PUSH EBP
+ //AND EBX, $FFFFFF
+@@Xloop_pf24bit:
+ MOV EBP, dword ptr [ESI]
+ AND EBP, $FFFFFF
+ CMP EBP, EBX
+ SETZ AH
+ SHL AL, 1
+ OR AL, AH
+ DEC DL
+ JNZ @@24_1
+ STOSB
+ MOV DL, 8
+@@24_1: ADD ESI, 3
+ LOOP @@Xloop_pf24bit
+ POP EBP
+ JMP @@nextline
+@@chk_pf32bit:
+ //-------- pf32bit:
+ MOV ECX, [EBP].fWidth
+@@Xloop_pf32bit:
+ and dword ptr [ESI], $FFFFFF
+ CMP EBX, dword ptr [ESI]
+ SETZ AH
+ SHL AL, 1
+ OR AL, AH
+ DEC DL
+ JNZ @@32_1
+ STOSB
+ MOV DL, 8
+@@32_1: ADD ESI, 4
+ LOOP @@Xloop_pf32bit
+@@nextline:
+ TEST DL, DL
+ JZ @@nx1
+ CMP DL, 8
+ JE @@nx1
+@@finloop1:
+ SHL AL, 1
+ DEC DL
+ JNZ @@finloop1
+ STOSB
+@@nx1:
+ POP EDX
+ INC EDX
+ JMP @@Yloop
+@@exit:
+ POP EDX
+ PUSH EDX
+ XCHG EAX, EBP
+ CALL Assign
+ POP EAX
+ CALL TObj.RefDec
+ POP EDI
+ POP EBP
+ POP ESI
+ POP EBX
+end;
+{$ENDIF USE_OLDCONVERT2MASK} //Pascal
+
+procedure _PrepareBmp2Rotate;
+const szBIH = sizeof(TBitmapInfoHeader);
+asm
+ { <- BL = increment to height }
+ XCHG EDI, EAX
+ MOV ESI, EDX // ESI = SrcBmp
+
+ XCHG EAX, EDX
+ CALL TBitmap.GetPixelFormat
+ MOVZX ECX, AL
+ PUSH ECX
+
+ MOV EDX, [ESI].TBitmap.fWidth
+ MOVZX EBX, BL
+ ADD EDX, EBX
+
+ MOV EAX, [ESI].TBitmap.fHeight
+ CALL NewDIBBitmap
+ STOSD
+ XCHG EDI, EAX
+
+ MOV EAX, [ESI].TBitmap.fDIBHeader
+ ADD EAX, szBIH
+ MOV EDX, [EDI].TBitmap.fDIBHeader
+ ADD EDX, szBIH
+ XOR ECX, ECX
+ MOV CH, 4
+ CALL System.Move
+
+ MOV EAX, EDI
+ XOR EDX, EDX
+ CALL TBitmap.GetScanLine
+ MOV EBX, [EDI].TBitmap.fWidth
+ DEC EBX // EBX = DstBmp.fWidth - 1
+ XCHG EDI, EAX // EDI = DstBmp.ScanLine[ 0 ]
+
+ XOR EDX, EDX
+ INC EDX
+ CALL TBitmap.GetScanLine
+ XCHG EDX, EAX
+ SUB EDX, EDI // EDX = BytesPerDstLine
+
+ MOV EBP, [ESI].TBitmap.fWidth
+ DEC EBP // EBP = SrcBmp.fWidth - 1
+
+ POP ECX // ECX = PixelFormat
+end;
+procedure _RotateBitmapMono( var DstBmp: PBitmap; SrcBmp: PBitmap );
+const szBIH = sizeof(TBitmapInfoHeader);
+asm
+ PUSHAD
+ MOV BL, 7
+ CALL _PrepareBmp2Rotate
+
+ SHR EBP, 3
+ SHL EBP, 8 // EBP = (WBytes-1) * 256
+
+ MOV ECX, EBX // ECX and 7 = Shf
+ SHR EBX, 3
+ ADD EDI, EBX // EDI = Dst
+
+ XOR EBX, EBX // EBX = temp mask
+ XOR EAX, EAX // Y = 0
+@@looY:
+ PUSH EAX
+ PUSH EDI // Dst1 = Dst (Dst1 in EDI, Dst saved)
+ PUSH ESI // SrcBmp
+
+ PUSH EDX //BytesPerDstLine
+ PUSH ECX //Shf
+
+ XCHG EDX, EAX
+ XCHG EAX, ESI
+ CALL TBitmap.GetScanLine
+ XCHG ESI, EAX // ESI = Src
+
+ POP ECX // CL = Shf
+ AND ECX, 7 // ECX = Shf
+ OR ECX, EBP // ECX = (Wbytes-1)*8 + Shf
+ POP EDX // EDX = BytesPerDstLine
+
+ MOV BH, $80
+ SHR EBX, CL // BH = mask, BL = mask & Tmp
+@@looX:
+ XOR EAX, EAX
+
+ LODSB
+
+ MOV AH, AL
+ SHR EAX, CL
+ OR EAX,$01000000
+
+@@looBits:
+ MOV BL, AH
+ AND BL, BH
+ OR [EDI], BL
+ ADD EDI, EDX
+ ADD EAX, EAX
+ JNC @@looBits
+
+ SUB ECX, 256
+ JGE @@looX
+
+ POP ESI // ESI = SrcBmp
+ POP EDI // EDI = Dst
+ POP EAX // EAX = Y
+
+ ADD ECX, 256-1
+ JGE @@1
+ DEC EDI
+@@1:
+ INC EAX
+ CMP EAX, [ESI].TBitmap.fHeight
+ JL @@looY
+
+ POPAD
+end;
+
+procedure _RotateBitmap4bit( var DstBmp: PBitmap; SrcBmp: PBitmap );
+const szBIH = sizeof(TBitmapInfoHeader);
+asm
+ PUSHAD
+ MOV BL, 1
+ CALL _PrepareBmp2Rotate
+
+ SHR EBP, 1 // EBP = WBytes - 1
+ SHL EBP, 8 // EBP = (WBytes - 1) * 256
+
+ // EBX = DstBmp.fWidth - 1
+ MOV ECX, EBX
+ SHL ECX, 2 // ECX and 7 = Shf (0 or 4)
+ SHR EBX, 1
+ ADD EDI, EBX // EDI = Dst
+
+ XOR EAX, EAX // Y = 0
+ XOR EBX, EBX
+
+@@looY:
+ PUSH EAX // save Y
+ PUSH EDI // Dst1 = Dst (Dst1 in EDI, Dst saved)
+ PUSH ESI // SrcBmp
+
+ PUSH EDX // BytesPerDstLine
+ PUSH ECX // Shf
+
+ XCHG EDX, EAX
+ XCHG EAX, ESI
+ CALL TBitmap.GetScanLine
+ XCHG ESI, EAX // ESI = Src
+
+ POP ECX
+ AND ECX, 7 // CL = Shf
+ OR ECX, EBP // ECX = (WBytes-1)*256 + Shf
+ POP EDX // EDX = BytesPerDstLine
+
+ MOV BH, $F0
+ SHR EBX, CL // shift mask right 4 or 0
+
+@@looX:
+ XOR EAX, EAX
+ LODSB
+ MOV AH, AL
+ SHR EAX, CL
+
+ MOV BL, AH
+ AND BL, BH
+ OR [EDI], BL
+ ADD EDI, EDX
+
+ SHL EAX, 4
+ AND AH, BH
+ OR [EDI], AH
+ ADD EDI, EDX
+
+ SUB ECX, 256
+ JGE @@looX
+
+ POP ESI // ESI = SrcBmp
+ POP EDI // EDI = Dst
+ POP EAX // EAX = Y
+
+ ADD ECX, 256 - 4
+ JGE @@1
+
+ DEC EDI
+@@1:
+ INC EAX
+ CMP EAX, [ESI].TBitmap.fHeight
+ JL @@looY
+
+ POPAD
+end;
+
+procedure _RotateBitmap8bit( var DstBmp: PBitmap; SrcBmp: PBitmap );
+const szBIH = sizeof(TBitmapInfoHeader);
+asm
+ PUSHAD
+ XOR EBX, EBX
+ CALL _PrepareBmp2Rotate
+
+ ADD EDI, EBX // EDI = Dst
+
+ MOV EBX, EDX // EBX = BytesPerDstLine
+ DEC EBX
+ MOV EBP, ESI // EBP = SrcBmp
+
+ XOR EDX, EDX // Y = 0
+
+@@looY:
+ PUSH EDX
+ PUSH EDI
+
+ MOV EAX, EBP
+ CALL TBitmap.GetScanLine
+ XCHG ESI, EAX
+ MOV ECX, [EBP].TBitmap.fWidth
+
+@@looX:
+ MOVSB
+ ADD EDI, EBX
+ LOOP @@looX
+
+ POP EDI
+ POP EDX
+
+ DEC EDI
+ INC EDX
+ CMP EDX, [EBP].TBitmap.fHeight
+ JL @@looY
+
+ POPAD
+end;
+
+procedure _RotateBitmap16bit( var DstBmp: PBitmap; SrcBmp: PBitmap );
+asm
+ PUSHAD
+ XOR EBX, EBX
+ CALL _PrepareBmp2Rotate
+
+ ADD EBX, EBX
+ ADD EDI, EBX // EDI = Dst
+ MOV EBX, EDX // EBX = BytesPerDstLine
+ DEC EBX
+ DEC EBX
+ MOV EBP, ESI // EBP = SrcBmp
+
+ XOR EDX, EDX // Y = 0
+
+@@looY:
+ PUSH EDX
+ PUSH EDI
+
+ MOV EAX, EBP
+ CALL TBitmap.GetScanLine
+ XCHG ESI, EAX
+ MOV ECX, [EBP].TBitmap.fWidth
+
+@@looX:
+ MOVSW
+ ADD EDI, EBX
+ LOOP @@looX
+
+ POP EDI
+ POP EDX
+
+ DEC EDI
+ DEC EDI
+ INC EDX
+ CMP EDX, [EBP].TBitmap.fHeight
+ JL @@looY
+
+ POPAD
+end;
+
+procedure _RotateBitmap2432bit( var DstBmp: PBitmap; SrcBmp: PBitmap );
+asm
+ PUSHAD
+ XOR EBX, EBX
+ CALL _PrepareBmp2Rotate
+
+ SUB ECX, pf24bit
+ JNZ @@10
+ LEA EBX, [EBX+EBX*2]
+ JMP @@11
+@@10:
+ LEA EBX, [EBX*4]
+@@11: ADD EDI, EBX // EDI = Dst
+
+ MOV EBX, EDX // EBX = BytesPerDstLine
+ DEC EBX
+ DEC EBX
+ DEC EBX
+
+ MOV EBP, ESI // EBP = SrcBmp
+
+ XOR EDX, EDX // Y = 0
+
+@@looY:
+ PUSH EDX
+ PUSH EDI
+ PUSH ECX // ECX = 0 if pf24bit (1 if pf32bit)
+
+ MOV EAX, EBP
+ CALL TBitmap.GetScanLine
+ XCHG ESI, EAX
+ MOV ECX, [EBP].TBitmap.fWidth
+ POP EAX
+ PUSH EAX
+
+@@looX:
+ MOVSW
+ MOVSB
+ ADD ESI, EAX
+ ADD EDI, EBX
+ LOOP @@looX
+
+ POP ECX
+ POP EDI
+ POP EDX
+
+ DEC EDI
+ DEC EDI
+ DEC EDI
+ SUB EDI, ECX
+ INC EDX
+ CMP EDX, [EBP].TBitmap.fHeight
+ JL @@looY
+
+ POPAD
+end;
+
+procedure _RotateBitmapRight( SrcBmp: PBitmap );
+asm
+ PUSH EBX
+ PUSH EDI
+ MOV EBX, EAX
+ CMP [EBX].TBitmap.fHandleType, bmDIB
+ JNZ @@exit
+
+ CALL TBitmap.GetPixelFormat
+ MOVZX ECX, AL
+ LOOP @@not1bit
+ MOV EAX, [RotateProcs.proc_RotateBitmapMono]
+@@not1bit:
+ LOOP @@not4bit
+ MOV EAX, [RotateProcs.proc_RotateBitmap4bit]
+@@not4bit:
+ LOOP @@not8bit
+ MOV EAX, [RotateProcs.proc_RotateBitmap8bit]
+@@not8bit:
+ LOOP @@not15bit
+ INC ECX
+@@not15bit:
+ LOOP @@not16bit
+ MOV EAX, [RotateProcs.proc_RotateBitmap16bit]
+@@not16bit:
+ LOOP @@not24bit
+ INC ECX
+@@not24bit:
+ LOOP @@not32bit
+ MOV EAX, [RotateProcs.proc_RotateBitmap2432bit]
+@@not32bit:
+ TEST EAX, EAX
+ JZ @@exit
+
+ PUSH ECX
+ XCHG ECX, EAX
+ MOV EAX, ESP
+ MOV EDX, EBX
+ CALL ECX
+
+ POP EDI
+ MOV EAX, [EBX].TBitmap.fWidth
+ CMP EAX, [EDI].TBitmap.fHeight
+ JGE @@noCutHeight
+
+ MOV EDX, [EDI].TBitmap.fScanLineSize
+ MUL EDX
+ MOV [EDI].TBitmap.fDIBSize, EAX
+
+ MOV EDX, [EDI].TBitmap.fDIBHeader
+ MOV EDX, [EDX].TBitmapInfoHeader.biHeight
+ TEST EDX, EDX
+ JL @@noCorrectImg
+
+ PUSH EAX
+
+ MOV EDX, [EDI].TBitmap.fHeight
+ DEC EDX
+ MOV EAX, EDI
+ CALL TBitmap.GetScanLine
+ PUSH EAX
+
+ MOV EDX, [EBX].TBitmap.fWidth
+ DEC EDX
+ MOV EAX, EDI
+ CALL TBitmap.GetScanLine
+ POP EDX
+
+ POP ECX
+ CALL System.Move
+
+@@noCorrectImg:
+ MOV EAX, [EBX].TBitmap.fWidth
+ MOV [EDI].TBitmap.fHeight, EAX
+ MOV EDX, [EDI].TBitmap.fDIBHeader
+ MOV [EDX].TBitmapInfoHeader.biHeight, EAX
+
+@@noCutHeight:
+ MOV EAX, EBX
+ CALL TBitmap.ClearData
+
+ XOR EAX, EAX
+ XCHG EAX, [EDI].TBitmap.fDIBHeader
+ XCHG [EBX].TBitmap.fDIBHeader, EAX
+
+ XCHG EAX, [EDI].TBitmap.fDIBBits
+ XCHG [EBX].TBitmap.fDIBBits, EAX
+
+ MOV AL, [EDI].TBitmap.fDIBAutoFree
+ MOV [EBX].TBitmap.fDIBAutoFree, AL
+
+ MOV EAX, [EDI].TBitmap.fDIBSize
+ MOV [EBX].TBitmap.fDIBSize, EAX
+
+ MOV EAX, [EDI].TBitmap.fWidth
+ MOV [EBX].TBitmap.fWidth, EAX
+
+ MOV EAX, [EDI].TBitmap.fHeight
+ MOV [EBX].TBitmap.fHeight, EAX
+
+ XCHG EAX, EDI
+ CALL TObj.RefDec
+@@exit:
+ POP EDI
+ POP EBX
+end;
+
+function TBitmap.GetPixels(X, Y: Integer): TColor;
+asm
+ PUSH EBX
+ MOV EBX, EAX
+ PUSH ECX
+ PUSH EDX
+ CALL GetEmpty
+ PUSHFD
+ OR EAX, -1
+ POPFD
+ JZ @@exit
+
+ CALL StartDC
+ PUSH dword ptr [ESP+12]
+ PUSH dword ptr [ESP+12]
+ PUSH EAX
+ CALL Windows.GetPixel
+ XCHG EBX, EAX
+ CALL FinishDC
+ XCHG EAX, EBX
+@@exit:
+ POP EDX
+ POP EDX
+ POP EBX
+end;
+
+procedure TBitmap.SetPixels(X, Y: Integer; const Value: TColor);
+asm
+ PUSH EBX
+ MOV EBX, EAX
+ PUSH ECX
+ PUSH EDX
+ CALL GetEmpty
+ JZ @@exit
+
+ CALL StartDC
+ MOV EAX, Value
+ CALL Color2RGB
+ PUSH EAX
+ PUSH dword ptr [ESP+16]
+ PUSH dword ptr [ESP+16]
+ PUSH dword ptr [ESP+16]
+ CALL Windows.SetPixel
+ CALL FinishDC
+@@exit:
+ POP EDX
+ POP ECX
+ POP EBX
+end;
+
+function _GetDIBPixelsPalIdx( Bmp: PBitmap; X, Y: Integer ): TColor;
+const szBIH = Sizeof(TBitmapInfoHeader);
+asm
+ PUSH EBX
+ PUSH EDI
+ PUSH EDX
+ XCHG EBX, EAX
+
+ XCHG EAX, EDX
+ MOV EDI, [EBX].TBitmap.fPixelsPerByteMask
+ INC EDI
+ CDQ
+ DIV EDI
+ DEC EDI
+ XCHG ECX, EAX // EAX = Y, ECX = X div (Bmp.fPixeldPerByteMask+1)
+
+ MOV EDX, [EBX].TBitmap.fScanLineDelta
+ IMUL EDX
+
+ ADD EAX, [EBX].TBitmap.fScanLine0
+ MOVZX EAX, byte ptr[EAX+ECX]
+
+ POP EDX
+ MOV ECX, [EBX].TBitmap.fPixelsPerByteMask
+ AND EDX, ECX
+ SUB ECX, EDX
+
+ PUSH EAX
+ MOV EDI, [EBX].TBitmap.fDIBHeader
+ MOVZX EAX, [EDI].TBitmapInfoHeader.biBitCount
+ MUL ECX
+ XCHG ECX, EAX
+ POP EAX
+ SHR EAX, CL
+ AND EAX, [EBX].TBitmap.fPixelMask
+
+ MOV EAX, [EDI+szBIH+EAX*4]
+ CALL Color2RGBQuad
+
+ POP EDI
+ POP EBX
+end;
+
+function _GetDIBPixels16bit( Bmp: PBitmap; X, Y: Integer ): TColor;
+asm
+ PUSH [EAX].TBitmap.fPixelMask
+ PUSH EDX // X
+ PUSH EAX
+ MOV EAX, [EAX].TBitmap.fScanLineDelta
+ IMUL ECX
+ POP EDX
+ ADD EAX, [EDX].TBitmap.fScanLine0
+ POP ECX
+ MOVZX EAX, word ptr [EAX+ECX*2]
+ POP EDX
+ CMP DL, 15
+ JNE @@16bit
+
+ MOV EDX, EAX
+ SHR EDX, 7
+ SHL EAX, 6
+ MOV DH, AH
+ AND DH, $F8
+ SHL EAX, 13
+ JMP @@1516bit
+
+@@16bit:
+ MOV DL, AH
+ SHL EAX, 5
+ MOV DH, AH
+ SHL EAX, 14
+@@1516bit:
+ AND EAX, $F80000
+ OR EAX, EDX
+ AND AX, $FCF8
+end;
+
+function _GetDIBPixelsTrueColor( Bmp: PBitmap; X, Y: Integer ): TColor;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ PUSH EDX
+ MOV EAX, [EBX].TBitmap.fScanLineDelta
+ IMUL ECX
+ XCHG ECX, EAX
+ POP EDX
+ MOV EAX, [EBX].TBitmap.fBytesPerPixel
+ MUL EDX
+ ADD EAX, [EBX].TBitmap.fScanLine0
+ MOV EAX, [EAX+ECX]
+ AND EAX, $FFFFFF
+ CALL Color2RGBQuad
+ POP EBX
+end;
+
+function _GetDIBPixelsTrueColorAlpha( Bmp: PBitmap; X, Y: Integer ): TColor;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ PUSH EDX
+ MOV EAX, [EBX].TBitmap.fScanLineDelta
+ IMUL ECX
+ XCHG ECX, EAX
+ POP EDX
+ MOV EAX, [EBX].TBitmap.fBytesPerPixel
+ MUL EDX
+ ADD EAX, [EBX].TBitmap.fScanLine0
+ MOV EAX, [EAX+ECX]
+ MOV EDX, EAX
+ AND EDX, $FF00FF
+ AND EAX, $FF00FF00
+ ROL EDX, 16
+ OR EAX, EDX
+ POP EBX
+end;
+
+function TBitmap.GetDIBPixels(X, Y: Integer): TColor;
+asm
+ CMP word ptr [EAX].fGetDIBPixels+2, 0
+ JNZ @@assigned
+
+ // if not assigned, this preparing will be performed for first call:
+ CMP [EAX].fHandleType, bmDDB
+ JZ @@GetPixels
+
+ PUSHAD
+ MOV EBX, EAX
+ XOR EDX, EDX
+ CALL GetScanLine
+ MOV [EBX].fScanLine0, EAX
+ XOR EDX, EDX
+ INC EDX
+ MOV EAX, EBX
+ CALL GetScanLine
+ SUB EAX, [EBX].fScanLine0
+ MOV [EBX].fScanLineDelta, EAX
+ MOV EAX, EBX
+ CALL GetPixelFormat
+ MOVZX ECX, AL
+ MOV DX, $0F00
+ MOV byte ptr [EBX].fBytesPerPixel, 4
+ XOR EAX, EAX
+ LOOP @@if4bit
+ MOV DX, $0107
+ JMP @@1bit4bit8bit
+@@if4bit:
+ LOOP @@if8bit
+ INC EDX // MOV DX, $0F01
+ JMP @@1bit4bit8bit
+@@if8bit:
+ LOOP @@if15bit
+ MOV DH, $FF //MOV DX, $FF00
+@@1bit4bit8bit:
+ MOV EAX, offset[_GetDIBPixelsPalIdx]
+@@if15bit:
+ LOOP @@if16bit
+ //MOV DH, $0F
+ DEC DH
+ INC ECX
+@@if16bit:
+ LOOP @@if24bit
+ INC DH
+ MOV EAX, offset[_GetDIBPixels16bit]
+@@if24bit:
+ LOOP @@if32bit
+ DEC [EBX].fBytesPerPixel
+ INC ECX
+ DEC EDX
+@@if32bit:
+ LOOP @@iffin
+ INC EDX
+ {$IFDEF DIBPixels32bitWithAlpha}
+ MOV EAX, offset[_GetDIBPixelsTrueColorAlpha]
+ {$ELSE}
+ MOV EAX, offset[_GetDIBPixelsTrueColor]
+ {$ENDIF}
+@@iffin:
+ MOV byte ptr [EBX].fPixelMask, DH
+ MOV byte ptr [EBX].fPixelsPerByteMask, DL
+ MOV [EBX].fGetDIBPixels, EAX
+ TEST EAX, EAX
+ POPAD
+@@GetPixels:
+ JZ GetPixels
+
+@@assigned:
+ JMP [EAX].fGetDIBPixels
+end;
+
+procedure _SetDIBPixels1bit( Bmp: PBitmap; X, Y: Integer; Value: TColor );
+asm
+ PUSH EDX
+ PUSH [EAX].TBitmap.fScanLine0
+ PUSH ECX
+ PUSH [EAX].TBitmap.fScanLineDelta
+ MOV EAX, Value
+ CALL Color2RGB
+ MOV EDX, EAX
+ SHR EAX, 16
+ ADD AL, DL
+ ADC AL, DH
+ CMP EAX, 170
+ SETGE CL
+ AND ECX, 1
+ SHL ECX, 7
+ POP EAX
+ POP EDX
+ IMUL EDX
+ POP EDX
+ ADD EAX, EDX
+ POP EDX
+ PUSH ECX
+ MOV ECX, EDX
+ SHR EDX, 3
+ ADD EAX, EDX
+ AND ECX, 7
+ MOV DX, $FF7F
+ SHR EDX, CL
+ AND byte ptr [EAX], DL
+ POP EDX
+ SHR EDX, CL
+ OR byte ptr [EAX], DL
+end;
+
+procedure _SetDIBPixelsPalIdx( Bmp: PBitmap; X, Y: Integer; Value: TColor );
+asm
+ XCHG EAX, EBP
+ PUSH EDX // -> X
+ PUSH ECX // -> Y
+ MOV ECX, [EBP].TBitmap.fPixelsPerByteMask
+ INC ECX
+ XCHG EAX, EDX
+ CDQ
+ DIV ECX
+ XCHG ECX, EAX // ECX = X div (fPixelsPerByteMask+1)
+ POP EAX // <- Y
+ MOV EDX, [EBP].TBitmap.fScanLineDelta
+ IMUL EDX
+ ADD ECX, EAX
+ ADD ECX, [EBP].TBitmap.fScanLine0 // ECX = Pos
+ PUSH ECX // -> Pos
+
+ MOV EDX, [ESP+16] // Value
+ MOV EAX, EBP
+ CALL TBitmap.DIBPalNearestEntry // EAX = Pixel
+
+ POP ECX // <- Pos
+ POP EDX // <- X
+
+ PUSH EAX // -> Pixel
+
+ MOV EAX, [EBP].TBitmap.fPixelsPerByteMask
+ AND EDX, EAX
+ SUB EAX, EDX
+ MOV EDX, [EBP].TBitmap.fDIBHeader
+ MOVZX EDX, [EDX].TBitmapInfoHeader.biBitCount
+ MUL EDX // EAX = Shf
+
+ XCHG ECX, EAX // ECX = Shf, EAX = Pos
+ MOV EDX, [EBP].TBitmap.fPixelMask
+ SHL EDX, CL
+ NOT EDX
+ AND byte ptr [EAX], DL
+
+ POP EDX // <- Pixel
+ SHL EDX, CL
+ OR byte ptr [EAX], DL
+end;
+
+procedure _SetDIBPixels16bit( Bmp: PBitmap; X, Y: Integer; Value: TColor );
+asm
+ ADD EDX, EDX
+ ADD EDX, [EAX].TBitmap.fScanLine0
+ PUSH EDX // -> X*2 + Bmp.fScanLine0
+ PUSH [EAX].TBitmap.fPixelMask
+ MOV EAX, [EAX].TBitmap.fScanLineDelta
+ IMUL ECX
+ PUSH EAX // -> Y* Bmp.fScanLineDelta
+ MOV EAX, Value
+ CALL Color2RGB
+ POP EBP // <- Y* Bmp.fScanLineDelta
+ POP EDX
+ XOR ECX, ECX
+ SUB DL, 16
+ JZ @@16bit
+
+ MOV CH, AL
+ SHR CH, 1
+ SHR EAX, 6
+ MOV EDX, EAX
+ AND DX, $3E0
+ SHR EAX, 13
+ JMP @@1516
+
+@@16bit:
+ {$IFDEF PARANOIA} DB $24, $F8 {$ELSE} AND AL, $F8 {$ENDIF}
+ MOV CH, AL
+ SHR EAX, 5
+ MOV EDX, EAX
+ AND DX, $7E0
+ SHR EAX, 14
+
+@@1516:
+ MOV AH, CH
+ AND AX, $FC1F
+ OR AX, DX
+
+ POP EDX
+ MOV [EBP+EDX], AX
+end;
+
+procedure _SetDIBPixelsTrueColor( Bmp: PBitmap; X, Y: Integer; Value: TColor );
+asm
+ PUSH [EAX].TBitmap.fScanLineDelta
+ PUSH [EAX].TBitmap.fScanLine0
+ MOV EAX, [EAX].TBitmap.fBytesPerPixel
+ MUL EDX
+ POP EDX
+ ADD EDX, EAX
+ POP EAX
+ PUSH EDX
+ IMUL ECX
+ POP EDX
+ ADD EDX, EAX
+ PUSH EDX
+ MOV EAX, Value
+ CALL Color2RGBQuad
+ POP EDX
+ AND dword ptr [EDX], $FF000000
+ OR [EDX], EAX
+end;
+
+procedure _SetDIBPixelsTrueColorAlpha( Bmp: PBitmap; X, Y: Integer; Value: TColor );
+asm
+ PUSH [EAX].TBitmap.fScanLineDelta
+ PUSH [EAX].TBitmap.fScanLine0
+ MOV EAX, [EAX].TBitmap.fBytesPerPixel
+ MUL EDX
+ POP EDX
+ ADD EDX, EAX
+ POP EAX
+ PUSH EDX
+ IMUL ECX
+ POP EDX
+ ADD EDX, EAX
+ MOV EAX, Value
+ MOV ECX, EAX
+ AND ECX, $FF00FF
+ AND EAX, $FF00FF00
+ ROL ECX, 16
+ OR EAX, ECX
+ MOV [EDX], EAX
+end;
+
+procedure TBitmap.SetDIBPixels(X, Y: Integer; const Value: TColor);
+asm
+ CMP word ptr [EAX].fSetDIBPixels+2, 0
+ JNZ @@assigned
+ PUSHAD
+ MOV EBX, EAX
+ XOR EDX, EDX
+ CMP [EBX].fHandleType, DL // bmDIB = 0
+ JNE @@ddb
+ CALL GetScanLine
+ MOV [EBX].fScanLine0, EAX
+ XOR EDX, EDX
+ INC EDX
+ MOV EAX, EBX
+ CALL GetScanLine
+ SUB EAX, [EBX].fScanLine0
+ MOV [EBX].fScanLineDelta, EAX
+ MOV EAX, EBX
+ CALL GetPixelFormat
+ MOVZX ECX, AL
+ MOV DX, $0F01
+ MOV EAX, offset[_SetDIBPixelsPalIdx]
+ MOV byte ptr [EBX].fBytesPerPixel, 4
+ LOOP @@if4bit
+ MOV EAX, offset[_SetDIBPixels1bit]
+@@if4bit:
+ LOOP @@if8bit
+@@if8bit:
+ LOOP @@if15bit
+ DEC DL
+ MOV DH, $FF
+@@if15bit:
+ LOOP @@if16bit
+ DEC DH
+ INC ECX
+@@if16bit:
+ LOOP @@if24bit
+ INC DH
+ MOV EAX, offset[_SetDIBPixels16bit]
+@@if24bit:
+ LOOP @@if32bit
+ DEC EDX
+ DEC [EBX].fBytesPerPixel
+ INC ECX
+@@if32bit:
+ LOOP @@ifend
+ INC EDX
+ {$IFDEF DIBPixels32bitWithAlpha}
+ MOV EAX, offset[_SetDIBPixelsTrueColor]
+ {$ELSE}
+ MOV EAX, offset[_SetDIBPixelsTrueColor]
+ {$ENDIF}
+@@ifend:
+ MOV byte ptr [EBX].fPixelMask, DH
+ MOV byte ptr [EBX].fPixelsPerByteMask, DL
+ MOV [EBX].fSetDIBPixels, EAX
+ TEST EAX, EAX
+@@ddb:
+ POPAD
+ JNZ @@assigned
+ PUSH Value
+ CALL SetPixels
+ JMP @@exit
+@@assigned:
+ PUSH Value
+ CALL [EAX].fSetDIBPixels
+@@exit:
+end;
+
+procedure TBitmap.FlipVertical;
+asm
+ PUSH EBX
+ MOV EBX, EAX
+ MOV ECX, [EBX].fHandle
+ JECXZ @@noHandle
+
+ CALL StartDC
+ PUSH SrcCopy
+ MOV EDX, [EBX].fHeight
+ PUSH EDX
+ MOV ECX, [EBX].fWidth
+ PUSH ECX
+ PUSH 0
+ PUSH 0
+ PUSH EAX
+ NEG EDX
+ PUSH EDX
+ PUSH ECX
+ NEG EDX
+ DEC EDX
+ PUSH EDX
+ PUSH 0
+ PUSH EAX
+ CALL StretchBlt
+ CALL FinishDC
+ POP EBX
+ RET
+
+@@noHandle:
+ MOV ECX, [EBX].fDIBBits
+ JECXZ @@exit
+
+ PUSHAD //----------------------------------------\
+ XOR EBP, EBP // Y = 0
+ //+++++++++++++++++++++++++++ provide fScanLineSize
+ MOV EAX, EBX
+ MOV EDX, EBP
+ CALL GetScanLine //
+ SUB ESP, [EBX].fScanLineSize
+
+@@loo: LEA EAX, [EBP*2]
+ CMP EAX, [EBX].fHeight
+ JGE @@finloo
+
+ MOV EAX, EBX
+ MOV EDX, EBP
+ CALL GetScanLine
+ MOV ESI, EAX // ESI = ScanLine[ Y ]
+ MOV EDX, ESP
+ MOV ECX, [EBX].fScanLineSize
+ PUSH ECX
+ CALL System.Move
+
+ MOV EAX, EBX
+ MOV EDX, [EBX].fHeight
+ SUB EDX, EBP
+ DEC EDX
+ CALL GetScanLine
+ MOV EDI, EAX
+ MOV EDX, ESI
+ POP ECX
+ PUSH ECX
+ CALL System.Move
+
+ POP ECX
+ MOV EAX, ESP
+ MOV EDX, EDI
+ CALL System.Move
+
+ INC EBP
+ JMP @@loo
+
+@@finloo:
+ ADD ESP, [EBX].fScanLineSize
+ POPAD
+@@exit:
+ POP EBX
+end;
+
+procedure TBitmap.FlipHorizontal;
+asm
+ PUSH EBX
+ MOV EBX, EAX
+ CALL GetHandle
+ TEST EAX, EAX
+ JZ @@exit
+
+ CALL StartDC
+ PUSH SrcCopy
+ MOV EDX, [EBX].fHeight
+ PUSH EDX
+ MOV ECX, [EBX].fWidth
+ PUSH ECX
+ PUSH 0
+ PUSH 0
+ PUSH EAX
+ PUSH EDX
+ NEG ECX
+ PUSH ECX
+ PUSH 0
+ NEG ECX
+ DEC ECX
+ PUSH ECX
+ PUSH EAX
+ CALL StretchBlt
+ CALL FinishDC
+@@exit:
+ POP EBX
+end;
+
+procedure TBitmap.CopyRect(const DstRect: TRect; SrcBmp: PBitmap;
+ const SrcRect: TRect);
+asm
+ PUSHAD
+ MOV EBX, EAX
+ MOV ESI, ECX
+ MOV EDI, EDX
+ CALL GetHandle
+ TEST EAX, EAX
+ JZ @@exit
+ MOV EAX, ESI
+ CALL GetHandle
+ TEST EAX, EAX
+ JZ @@exit
+ CALL StartDC
+ XCHG EBX, ESI
+ CMP EBX, ESI
+ JNZ @@diff1
+ PUSH EAX
+ PUSH 0
+ JMP @@nodiff1
+@@diff1:
+ CALL StartDC
+@@nodiff1:
+ PUSH SrcCopy // ->
+ MOV EBP, [SrcRect]
+ MOV EAX, [EBP].TRect.Bottom
+ MOV EDX, [EBP].TRect.Top
+ SUB EAX, EDX
+ PUSH EAX // ->
+ MOV EAX, [EBP].TRect.Right
+ MOV ECX, [EBP].TRect.Left
+ SUB EAX, ECX
+ PUSH EAX // ->
+ PUSH EDX // ->
+ PUSH ECX // ->
+ PUSH dword ptr [ESP+24] // -> DCsrc
+ MOV EAX, [EDI].TRect.Bottom
+ MOV EDX, [EDI].TRect.Top
+ SUB EAX, EDX
+ PUSH EAX // ->
+ MOV EAX, [EDI].TRect.Right
+ MOV ECX, [EDI].TRect.Left
+ SUB EAX, ECX
+ PUSH EAX // ->
+ PUSH EDX // ->
+ PUSH ECX // ->
+ PUSH dword ptr [ESP+13*4] // -> DCdst
+ CALL StretchBlt
+ CMP EBX, ESI
+ JNE @@diff2
+ POP ECX
+ POP ECX
+ JMP @@nodiff2
+@@diff2:
+ CALL FinishDC
+@@nodiff2:
+ CALL FinishDC
+@@exit:
+ POPAD
+end;
+
+procedure asmIconEmpty( Icon: PIcon );
+asm
+ CMP [EAX].TIcon.fHandle, 0
+end;
+
+procedure TIcon.Clear;
+asm //cmd //opd
+ XOR ECX, ECX
+ XCHG ECX, [EAX].fHandle
+ JECXZ @@1
+ CMP [EAX].fShareIcon, 0
+ JNZ @@1
+ PUSH EAX
+ PUSH ECX
+ CALL DestroyIcon
+ POP EAX
+@@1: MOV [EAX].fShareIcon, 0
+end;
+
+{$IFNDEF ICON_DIFF_WH}
+function TIcon.Convert2Bitmap(TranColor: TColor): HBitmap;
+asm //cmd //opd
+ PUSH EBX
+ PUSH ESI
+ PUSH EDI
+ PUSH EBP
+ MOV EBX, EAX
+ MOV EBP, EDX
+ XOR EDX, EDX
+ CALL asmIconEmpty
+ JZ @@ret_0
+ PUSH 0
+ CALL GetDC
+ PUSH EAX //> DC0
+ PUSH EAX
+ CALL CreateCompatibleDC
+ XCHG EDI, EAX
+ MOV EDX, [EBX].fSize
+
+ POP EAX
+ PUSH EAX
+ PUSH EDX //>Bottom
+ PUSH EDX //>Right
+ PUSH 0 //>Top
+ PUSH 0 //>Left
+
+ PUSH EDX
+ PUSH EDX
+ PUSH EAX
+ CALL CreateCompatibleBitmap
+ XCHG EBP, EAX
+
+ CALL Color2RGB
+ PUSH EAX
+
+ PUSH EBP
+ PUSH EDI
+ CALL SelectObject
+ XCHG ESI, EAX
+
+ CALL CreateSolidBrush
+
+ MOV EDX, ESP
+ PUSH EAX
+ PUSH EAX
+ PUSH EDX
+ PUSH EDI
+ CALL Windows.FillRect
+ CALL DeleteObject
+
+ XCHG EAX, EBX
+ MOV EDX, EDI
+ XOR ECX, ECX
+ PUSH ECX
+ CALL Draw
+
+ PUSH EDI
+ PUSH ESI
+ CALL FinishDC
+
+ ADD ESP, 16
+ PUSH 0
+ CALL ReleaseDC
+ MOV EDX, EBP
+
+@@ret_0:
+ XCHG EAX, EDX
+ POP EBP
+ POP EDI
+ POP ESI
+ POP EBX
+end;
+{$ENDIF}
+
+destructor TIcon.Destroy;
+asm //cmd //opd
+ PUSH EAX
+ CALL Clear
+ POP EAX
+ CALL TObj.Destroy
+end;
+
+procedure TIcon.Draw(DC: HDC; X, Y: Integer);
+asm //cmd //opd
+ CALL asmIconEmpty
+ JZ @@exit
+ PUSH DI_NORMAL
+ PUSH 0
+ PUSH 0
+ {$IFDEF ICON_DIFF_WH}
+ PUSH [EAX].fHeight
+ PUSH [EAX].fWidth
+ {$ELSE}
+ PUSH [EAX].fSize
+ PUSH [EAX].fSize
+ {$ENDIF}
+ PUSH [EAX].fHandle
+ PUSH Y
+ PUSH ECX
+ PUSH EDX
+ CALL DrawIconEx
+@@exit:
+end;
+
+procedure TIcon.StretchDraw(DC: HDC; Dest: TRect);
+asm //cmd //opd
+ CALL asmIconEmpty
+ JZ @@exit
+ PUSH DI_NORMAL
+ PUSH 0
+ PUSH 0
+ PUSH ECX
+ PUSH ECX
+ PUSH [EAX].fHandle
+ PUSH [ECX].TRect.Top
+ PUSH [ECX].TRect.Left
+ PUSH EDX
+ MOV EAX, [ECX].TRect.Bottom
+ SUB EAX, [ECX].TRect.Top
+ MOV [ESP+20], EAX
+ MOV EAX, [ECX].TRect.Right
+ SUB EAX, [ECX].TRect.Left
+ MOV [ESP+16], EAX
+ CALL DrawIconEx
+@@exit:
+end;
+
+procedure TIcon.SaveToFile(const FileName: KOLString);
+asm //cmd //opd
+ PUSH EAX
+ MOV EAX, ESP
+ MOV ECX, EDX
+ XOR EDX, EDX
+ CALL SaveIcons2File
+ POP EAX
+end;
+
+procedure TIcon.SaveToStream(Strm: PStream);
+asm //cmd //opd
+ PUSH EAX
+ MOV EAX, ESP
+ MOV ECX, EDX
+ XOR EDX, EDX
+ CALL SaveIcons2Stream
+ POP EAX
+end;
+
+function ColorBits( ColorsCount : Integer ) : Integer;
+asm //cmd //opd
+ PUSH EBX
+ MOV EDX, offset[PossibleColorBits]
+@@loop: MOVZX ECX, byte ptr [EDX]
+ JECXZ @@e_loop
+ INC EDX
+ XOR EBX, EBX
+ INC EBX
+ SHL EBX, CL
+ CMP EBX, EAX
+ JL @@loop
+@@e_loop:
+ XCHG EAX, ECX
+ POP EBX
+end;
+
+function WndProcUpdate( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+asm //cmd //opd
+ PUSH EBX
+ XCHG EBX, EAX
+ MOVZX EAX, [EBX].TControl.fUpdateCount
+ TEST EAX, EAX
+ JZ @@exit
+
+ XOR EAX, EAX
+ MOV EDX, [EDX].TMsg.message
+ CMP DX, WM_PAINT
+ JNE @@chk_erasebkgnd
+
+ MOV [ECX], EAX
+ PUSH EAX
+ PUSH [EBX].TControl.fHandle
+ CALL ValidateRect
+ JMP @@rslt_1
+@@chk_erasebkgnd:
+ CMP DX, WM_ERASEBKGND
+ JNE @@exit
+ INC EAX
+ MOV [ECX], EAX
+@@rslt_1:
+ MOV AL, 1
+@@exit:
+ POP EBX
+end;
+
+procedure TControl.SetFocused(const Value: Boolean);
+asm
+ PUSH ESI
+ MOV ESI, EAX
+ TEST DL, DL
+ JZ @@1
+ {$IFDEF USE_FLAGS}
+ TEST [ESI].fStyle.f2_Style, 1 shl F2_Tabstop
+ {$ELSE}
+ CMP [ESI].fTabstop, 0
+ {$ENDIF}
+ JZ @@exit
+@@1: {$IFDEF USE_FLAGS}
+ TEST [ESI].fFlagsG3, 1 shl G3_IsControl
+ {$ELSE}
+ CMP [ESI].fIsControl, 0
+ {$ENDIF}
+ JZ @@SetForegroundWindow
+ CALL TControl.ParentForm
+ PUSH EAX
+ MOV ECX, [EAX].DF.fCurrentControl
+ JECXZ @@PF_setCurCtl
+ CMP ECX, ESI
+ JZ @@PF_setCurCtl
+ MOV EAX, [EAX].DF.fCurrentControl
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV ECX, [EAX].EV
+ MOV EDX, [ECX].TEvents.fLeave.TMethod.Data
+ MOV ECX, [ECX].TEvents.fLeave.TMethod.Code
+ {$ELSE}
+ MOV ECX, [EAX].EV.fLeave.TMethod.Code
+ MOV EDX, [EAX].EV.fLeave.TMethod.Data
+ {$ENDIF}
+ JECXZ @@SetFocus0
+ XCHG EAX, EDX
+ CALL ECX
+ JMP @@PF_setCurCtl
+@@setFocus0:
+ PUSH 0
+ CALL Windows.SetFocus
+@@PF_setCurCtl:
+ POP EAX
+ MOV [EAX].DF.fCurrentControl, ESI
+ {$IFDEF USE_GRAPHCTLS}
+ MOV ECX, [ESI].fSetFocus.TMethod.Code
+ MOV EAX, [ESI].fSetFocus.TMethod.Data
+ JECXZ @@SetFocus_GetwindowHandle
+ MOV EDX, ESI
+ CALL ECX
+ {$ENDIF}
+@@SetFocus_GetwindowHandle:
+ XCHG EAX, ESI
+ CALL TControl.GetWindowHandle
+ PUSH EAX
+ CALL Windows.SetFocus
+ JMP @@exit
+@@SetForegroundWindow:
+ XCHG EAX, ESI
+ CALL TControl.GetWindowHandle
+ PUSH EAX
+ CALL SetForegroundWindow
+@@exit: POP ESI
+end;
+
+procedure TControl.AttachProcEx( Proc: TWindowFunc; ExecuteAfterAppletTerminated: Boolean );
+asm PUSH EBX
+ PUSH EDI
+ PUSH ECX
+ XCHG EBX, EAX
+ MOV EDI, EDX
+ MOV [EBX].PP.fOnDynHandlers, offset[EnumDynHandlers]
+ MOV EAX, [EBX].fDynHandlers
+ MOV EDX, EDI
+ CALL TList.IndexOf
+ TEST EAX, EAX
+ JGE @@exit
+
+ MOV EAX, [EBX].fDynHandlers
+ PUSH EAX
+ MOV EDX, EDI
+ CALL TList.Add
+ POP EAX
+ POP EDX
+ PUSH EDX
+ CALL TList.Add
+@@exit: {$IFNDEF SMALLEST_CODE}
+ MOV EAX, [EBX].fDynHandlers
+ CALL [Global_AttachProcExtension]
+ {$ENDIF}
+ POP ECX
+ POP EDI
+ POP EBX
+end;
+
+function TControl.IsProcAttached(Proc: TWindowFunc): Boolean;
+asm //cmd //opd
+ MOV EAX, [EAX].TControl.fDynHandlers
+ CALL TList.IndexOf
+ TEST EAX, EAX
+ SETGE AL
+end;
+
+{$IFDEF nASM_VERSION}
+function WndProcAutoPopupMenu( Control: PControl; var Msg: TMsg; var MsgRslt: Integer ): Boolean;
+asm
+ CMP WORD PTR[EDX].TMsg.message, WM_CONTEXTMENU
+ JNZ @@ret_0
+ CMP DWORD PTR[EAX].TControl.fAutoPopupMenu, 0
+ JZ @@ret_0
+ PUSH ESI
+ PUSH EDI
+ PUSH EBX
+ XCHG ESI, EAX // ESI = Control
+ MOV EDI, EDX
+
+ MOVSX EAX, WORD PTR[EDX].TMsg.lParam+2
+ PUSH EAX // P.Y
+ MOVSX EAX, WORD PTR[EDX].TMsg.lParam
+ PUSH EAX // P.X
+
+ CMP DWORD PTR[EDX].TMsg.lParam, -1
+ JNZ @@auto_popup
+
+ MOV EAX, ESI
+ CALL TControl.GetCurIndex
+ CMP EAX, 0
+ JL @@coords_2screen
+ // EAX = I
+
+ MOVZX EBX, WORD PTR[ESI].TControl.fCommandActions.aItem2XY
+ CMP EBX, 0
+ JZ @@coords_2screen
+
+ CMP BX, EM_POSFROMCHAR
+ JNZ @@chk_LB_LV_TC
+
+ PUSH 1
+ MOV EAX, ESI
+ CALL TControl.GetSelStart
+ PUSH EAX
+ MOV EAX, ESI
+ CALL TControl.GetSelLength
+ ADD DWORD PTR[ESP], EAX
+ PUSH EBX
+ PUSH ESI
+ CALL TControl.Perform
+ MOVSX EBX, AX
+ SHR EAX, 16
+ MOVSX EAX, AX
+ POP ECX
+ POP ECX
+ PUSH EAX
+ PUSH EBX
+ JMP @@check_bounds
+
+@@chk_LB_LV_TC:
+ CMP BX, LB_GETITEMRECT
+ JZ @@LB_LV_TC
+ CMP BX, LVM_GETITEMRECT
+ JZ @@LB_LV_TC
+ CMP BX, TCM_GETITEMRECT
+ JNZ @@chk_TVM
+@@LB_LV_TC: // EAX = I
+ PUSH ECX
+ PUSH LVIR_BOUNDS
+ PUSH ESP // @R
+ PUSH EAX // I
+ JMP @@get_2
+
+@@chk_TVM:
+ CMP BX, TVM_GETITEMRECT
+ JNZ @@check_bounds
+
+ MOV EDX, TVGN_CARET
+ MOV EAX, ESI
+ CALL TControl.TVGetItemIdx
+ PUSH ECX
+ PUSH EAX
+ PUSH ESP // @R
+ PUSH 1 // 1
+@@get_2:
+ PUSH EBX // M
+ PUSH ESI // Control
+ CALL TControl.Perform
+ POP EAX
+ POP ECX
+ POP ECX
+ PUSH EAX
+
+@@check_bounds:
+ POP EBX // P.X
+ POP EDI // P.Y
+ SUB ESP, 16
+ MOV EDX, ESP
+ MOV EAX, ESI
+ CALL TControl.ClientRect
+
+ POP EAX // R.Left == 0
+ POP EAX // R.Top == 0
+ POP EAX // R.Right
+ CMP EBX, EAX
+ JLE @@1
+ XCHG EBX, EAX
+@@1:POP EAX // R.Bottom
+ CMP EDI, EAX
+ JLE @@2
+ XCHG EDI, EAX
+@@2:PUSH EDI // P.Y
+ PUSH EBX // P.X
+
+@@coords_2screen:
+ MOV EDX, ESP
+ MOV EAX, ESI
+ MOV ECX, EDX
+ CALL TControl.Client2Screen
+
+@@auto_popup:
+ POP EDX // P.X
+ POP ECX // P.Y
+ MOV EAX, [ESI].TControl.fAutoPopupMenu
+ CALL TMenu.Popup
+
+ POP EBX
+ POP EDI
+ POP ESI
+ OR EAX, -1
+ RET
+@@ret_0:
+ XOR EAX, EAX
+end;
+{$ENDIF nASM_VERSION}
+
+function WndProcMouseEnterLeave( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+asm
+ PUSH ESI
+ XCHG ESI, EAX
+
+ MOV AX, word ptr [EDX].TMsg.message
+ CMP AX, WM_MOUSELEAVE
+ JE @@MOUSELEAVE
+ SUB AX, WM_MOUSEFIRST
+ CMP AX, WM_MOUSELEAVE-WM_MOUSEFIRST
+ JA @@retFalse
+
+ {$IFDEF USE_FLAGS}
+ TEST [ESI].TControl.fFlagsG3, 1 shl G3_MouseInCtl
+ SETNZ AL
+ {$ELSE}
+ MOV AL, [ESI].TControl.fMouseInControl
+ {$ENDIF}
+ PUSH EAX
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [ESI].TControl.EV
+ MOV ECX, [EAX].TEvents.fOnTestMouseOver.TMethod.Code
+ {$ELSE}
+ MOV ECX, [ESI].TControl.EV.fOnTestMouseOver.TMethod.Code
+ {$ENDIF}
+ JECXZ @@1
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EAX].TEvents.fOnTestMouseOver.TMethod.Data
+ {$ELSE}
+ MOV EAX, [ESI].TControl.EV.fOnTestMouseOver.TMethod.Data
+ {$ENDIF}
+ MOV EDX, ESI
+ CALL ECX
+ JMP @@2
+@@1:
+ PUSH ECX
+ PUSH ECX
+ PUSH ESP
+ CALL GetCursorPos
+ MOV EAX, ESI
+ MOV EDX, ESP
+ MOV ECX, EDX
+ CALL TControl.Screen2Client
+ MOV ECX, ESP // @P
+ SUB ESP, 16
+ MOV EDX, ESP // @ClientRect
+ MOV EAX, ESI
+
+ PUSH EDX
+ PUSH ECX
+ CALL TControl.ClientRect
+ POP EAX
+ POP EDX
+ CALL PointInRect
+ ADD ESP, 16+8
+
+@@2:
+ POP EDX
+ CMP AL, DL
+ JE @@retFalse
+
+ //MouseWasInControl <> Yes
+ PUSH EAX
+ MOV EAX, ESI
+ CALL TControl.Invalidate
+ POP EAX
+
+ TEST AL, AL
+ JZ @@3
+
+ {$IFDEF USE_FLAGS}
+ OR [ESI].TControl.fFlagsG3, 1 shl G3_MouseInCtl
+ {$ELSE}
+ MOV [ESI].TControl.fMouseInControl, 1
+ {$ENDIF}
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [ESI].TControl.EV
+ MOV ECX, [EAX].TEvents.fOnMouseEnter.TMethod.Code
+ {$ELSE}
+ MOV ECX, [ESI].TControl.EV.fOnMouseEnter.TMethod.Code
+ {$ENDIF}
+ JECXZ @@2_1
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EAX].TEvents.fOnMouseEnter.TMethod.Data
+ {$ELSE}
+ MOV EAX, [ESI].TControl.EV.fOnMouseEnter.TMethod.Data
+ {$ENDIF}
+ MOV EDX, ESI
+ CALL ECX
+@@2_1:
+ PUSH ECX
+ PUSH [ESI].TControl.fHandle
+ PUSH TME_LEAVE
+ PUSH 16
+ MOV EAX, ESP
+ CALL DoTrackMouseEvent
+ JMP @@4
+
+@@3:
+ {$IFDEF USE_FLAGS}
+ AND byte ptr [ESI].TControl.fFlagsG3, $7F // not(1 shl G3_MouseInCtl)
+ {$ELSE}
+ MOV [ESI].TControl.fMouseInControl, 0
+ {$ENDIF}
+ PUSH ECX
+ PUSH [ESI].TControl.fHandle
+ PUSH TME_LEAVE or TME_CANCEL
+ PUSH 16
+ MOV EAX, ESP
+ CALL DoTrackMouseEvent
+
+@@3_X:
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [ESI].TControl.EV
+ MOV ECX, [EAX].TEvents.fOnMouseLeave.TMethod.Code
+ {$ELSE}
+ MOV ECX, [ESI].TControl.EV.fOnMouseLeave.TMethod.Code
+ {$ENDIF}
+ JECXZ @@3_1
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EAX].TEvents.fOnMouseLeave.TMethod.Data
+ {$ELSE}
+ MOV EAX, [ESI].TControl.EV.fOnMouseLeave.TMethod.Data
+ {$ENDIF}
+ MOV EDX, ESI
+ CALL ECX
+@@3_1:
+
+@@4:
+ ADD ESP, 16
+@@4_1:
+ MOV EAX, ESI
+ CALL TControl.Invalidate
+ JMP @@retFalse
+
+@@MOUSELEAVE:
+ {$IFDEF USE_FLAGS}
+ BTR dword ptr [ESI].TControl.fFlagsG3, G3_MouseInCtl
+ JNC @@retFalse
+ {$ELSE}
+ BTR DWORD PTR [ESI].TControl.fMouseInControl, 0
+ JNC @@retFalse
+ {$ENDIF}
+
+ {$IFDEF GRAPHCTL_HOTTRACK}
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [ESI].TControl.EV
+ MOV ECX, [EAX].TEvents.fMouseLeaveProc.TMethod.Code
+ {$ELSE}
+ MOV ECX, [ESI].TControl.EV.fMouseLeaveProc.TMethod.Code
+ {$ENDIF}
+ {$IFDEF NIL_EVENTS}
+ JECXZ @@4_1
+ {$ENDIF}
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EAX].TEvents.fMouseLeaveProc.TMethod.Data
+ {$ELSE}
+ MOV EAX, [ESI].TControl.EV.fMouseLeaveProc.TMethod.Data
+ {$ENDIF}
+ CALL ECX
+ {$ENDIF}
+
+ SUB ESP, 16
+ JMP @@3_X
+
+@@retFalse:
+ XOR EAX, EAX
+ POP ESI
+end;
+
+function TControl.GetToBeVisible: Boolean;
+asm
+ {$IFDEF USE_FLAGS}
+ TEST [EAX].TControl.fStyle.f3_Style, 1 shl F3_Visible
+ SETNZ DH
+ TEST [EAX].TControl.fFlagsG4, (1 shl G4_CreateHidden) or (1 shl G4_VisibleWOParent)
+ SETNZ DL
+ OR DL, DH
+ TEST [EAX].TControl.fFlagsG3, 1 shl G3_IsControl
+ JZ @@retDL
+ MOV ECX, [EAX].TControl.fParent
+ JECXZ @@retDL
+
+ {$IFDEF OLD_ALIGN}
+ TEST [EAX].TControl.fFlagsG4, 1 shl G4_VisibleWOParent
+ JZ @@1
+ MOV DL, DH
+ JMP @@retDL
+ {$ENDIF}
+
+ {$ELSE not USE_FLAGS}
+ MOV DH, [EAX].TControl.fVisible
+ MOV DL, [EAX].TControl.fCreateHidden
+ OR DL, DH
+ OR DL, [EAX].TControl.fVisibleWoParent
+ CMP [EAX].TControl.fIsControl, 0
+ JZ @@retDL
+ MOV ECX, [EAX].TControl.fParent
+ JECXZ @@retDL
+
+ {$IFDEF OLD_ALIGN}
+ CMP [EAX].TControl.fVisibleWoParent, 0
+ JZ @@1
+ MOV DL, DH
+ JMP @@retDL
+ {$ENDIF}
+
+ {$ENDIF}
+
+@@1:
+ TEST DL, DL
+ JZ @@retDL
+ XCHG EAX, ECX
+ PUSH EAX
+ CALL TControl.Get_Visible
+ POP EAX
+ CALL TControl.GetToBeVisible
+ XCHG EDX, EAX
+@@retDL:
+ XCHG EAX, EDX
+end;
+
+// by MTsv DN - v2.90 -- chg by VK
+function WinVer : TWindowsVersion;
+asm
+ MOVSX EAX, byte ptr [SaveWinVer]
+ INC AH // åñëè <> 0 ïîñëå èíêðåìåíòà, òî AL ñîäåðæèò âû÷èñëåííóþ âåðñèþ
+ JNZ @@exit
+ CALL GetVersion // EAX < 0 äëÿ ïëàòôîðìû 9õ, èíà÷å NT; AL=MajorVersion; AH=MinorVersion
+ XCHG EDX, EAX
+ XOR EAX, EAX
+ TEST EDX, EDX
+ XCHG DL, DH // DH=MajorVersion; DL=MinorVersion
+
+ JL @@platform_9x
+ MOV AL, wvNT
+ CMP DX, $0400
+ JZ @@save_exit
+
+ INC AL // wvY2K
+ SUB DX, $0500
+ JZ @@save_exit
+
+ INC AL // wvXP
+ //CMP DX, $0501
+ DEC DX
+ JZ @@save_exit
+
+ INC AL // wvWin2003Server
+ //CMP DX, $0502
+ DEC DX
+ JZ @@save_exit
+
+ INC AL // wvVista
+ CMP DX, $0600 - $0502
+ JZ @@save_exit
+
+ INC AL // wvSeven
+ //CMP DX, $0601
+ //DEC DX
+ JMP @@save_exit
+@@platform_9x:
+ CMP DH, 4
+ JB @@save_exit // wv31
+ INC AL // wv95
+ CMP DX, $040A
+ JB @@save_exit
+ INC AL // wv98
+ CMP DX, $045A
+ JB @@save_exit
+ INC AL // wvME
+@@save_exit:
+ MOV byte ptr [SaveWinVer], AL
+@@exit:
+end;
+
+{$IFDEF USE_CONSTRUCTORS}
+constructor TControl.CreateGradientPanel(AParent: PControl; AColor1, //
+ AColor2: TColor); //
+asm //cmd //opd //
+ XOR EDX, EDX //
+ PUSH EDX //
+ CALL CreateLabel //
+ MOV ECX, AColor1 //
+ MOV [EAX].fColor1, ECX //
+ MOV ECX, AColor2 //
+ MOV [EAX].fColor2, ECX //
+ MOV EDX, [EAX].fBoundsRect.Left //
+ ADD EDX, 40 //
+ MOV [EAX].fBoundsRect.Right, EDX //
+ MOV EDX, [EAX].fBoundsRect.Top //
+ ADD EDX, 40 //
+ MOV [EAX].fBoundsRect.Bottom, EDX //
+ PUSH EAX //
+ MOV EDX, offset[ WndProcGradient ] //
+ CALL AttachProc //
+ POP EAX //
+end; //
+{$ENDIF USE_CONSTRUCTORS}
+
+function TControl.MakeWordWrap: PControl;
+asm
+ {$IFDEF USE_FLAGS}
+ OR [EAX].TControl.fFlagsG1, (1 shl G1_WordWrap)
+ {$ELSE}
+ MOV [EAX].TControl.fWordWrap, 1
+ {$ENDIF}
+
+ MOV EDX, [EAX].TControl.fStyle
+ {$IFDEF USE_FLAGS}
+ TEST [EAX].TControl.fFlagsG5, 1 shl G5_IsButton
+ {$ELSE}
+ CMP [EAX].TControl.fIsButton, 0
+ {$ENDIF}
+ JNZ @@1
+ AND DL, not SS_LEFTNOWORDWRAP
+@@1:
+ OR DH, $20 or SS_LEFTNOWORDWRAP // BS_MULTILINE >> 8
+@@2:
+ PUSH EAX
+ CALL TControl.SetStyle
+ POP EAX
+end;
+
+function TControl.FormGetIntParam: Integer;
+asm
+ PUSH ESI
+ PUSH EDI
+ MOV EDI, EAX // EDX = @ Self
+
+ XOR EDX, EDX
+@@loop:
+
+ LEA ECX, [EDI].DF.FormParams
+ MOV ESI, DWORD PTR[ECX]
+ LODSB
+ MOV DWORD PTR[ECX], ESI
+
+ SHR AL, 1
+ JNC @@nocont
+
+ SHL EDX, 7
+ OR DL, AL
+ JMP @@loop
+
+@@nocont:
+
+ SHR AL, 1
+ PUSHF
+ XCHG EDX, EAX
+ SHL EAX, 6
+ OR AL, DL
+ POPF
+ JNC @@noneg
+
+ NEG EAX
+@@noneg:
+ POP EDI
+ POP ESI
+end;
+
+function TControl.FormGetColorParam: Integer;
+asm
+ CALL FormGetIntParam
+ ROR EAX, 1
+end;
+
+procedure TControl.FormGetStrParam;
+asm
+ PUSH EDI
+ MOV EDI, EAX
+ CALL FormGetIntParam
+ XCHG ECX, EAX
+ LEA EAX, [EDI].FormString
+ PUSH ECX
+ MOV EDX, DWORD PTR[EDI].DF.FormParams
+ {$IFDEF _D2}
+ CALL System.@LStrFromLenStr
+ {$ELSE}
+ CALL System.@LStrFromPCharLen
+ {$ENDIF}
+ POP ECX
+ ADD DWORD PTR[EDI].DF.FormParams, ECX
+ POP EDI
+end;
+
+procedure TControl.FormExecuteCommands(AForm: PControl; ControlPtrOffsets: PSmallIntArray);
+asm
+ PUSH EBX
+ PUSH ESI
+ PUSH EDI
+ XCHG EDI, EAX // EDI = @ Self
+ MOV EBX, EDX // EBX = AForm
+ MOV ESI, ECX // ECX = @ ControlPtrOffsets[0]
+@@while_do:
+ MOV EAX, EDI
+ CALL FormGetIntParam
+ TEST EAX, EAX
+ JZ @@ewhile
+ JG @@not_create_ctrl
+
+ NEG EAX
+ MOV ECX, [EDI].DF.FormAlphabet
+ MOV ECX, [ECX+EAX*4-4]
+
+ MOV EAX, EDI
+
+ CALL ECX
+ XCHG ECX, EAX
+
+ XOR EAX, EAX
+ LODSW
+ MOV DWORD PTR[EBX+EAX*4], ECX
+ MOV [EDI].DF.FormLastCreatedChild, ECX
+ JMP @@while_do
+
+@@not_create_ctrl:
+ MOV ECX, [EDI].DF.FormAlphabet
+ MOV ECX, [ECX+EAX*4-4]
+ MOV EAX, [EDI].DF.FormLastCreatedChild
+
+ XOR EDX, EDX
+ INC EDX
+
+ CALL ECX
+ JMP @@while_do
+
+@@ewhile:
+ LEA EAX, [EDI].FormString
+ CALL System.@LStrClr
+
+ POP EDI
+ POP ESI
+ POP EBX
+end;
+
+function FormNewLabel( Form: PControl ): PControl;
+asm
+ CALL FormPrepareStrParamCreateCtrl
+ CALL NewLabel
+end;
+
+function FormNewWordWrapLabel( Form: PControl ): PControl;
+asm
+ CALL FormPrepareStrParamCreateCtrl
+ CALL NewWordWrapLabel
+end;
+
+function FormNewLabelEffect( Form: PControl ): PControl;
+asm
+ PUSH EAX
+ CALL TControl.FormGetStrParam
+ POP EAX
+ PUSH EAX
+ CALL TControl.FormGetIntParam
+ POP ECX
+ PUSH EAX
+ MOV EAX, [ECX].TControl.DF.FormCurrentParent
+ MOV EDX, [ECX].TControl.FormString
+ POP ECX
+ CALL NewLabelEffect
+end;
+
+function FormNewButton( Form: PControl ): PControl;
+asm
+ CALL FormPrepareStrParamCreateCtrl
+ CALL NewButton
+end;
+
+function FormNewPanel( Form: PControl ): PControl;
+asm
+ CALL FormPrepareIntParamCreateCtrl
+ CALL NewPanel
+end;
+
+function FormNewGroupbox( Form: PControl ): PControl;
+asm
+ CALL FormPrepareStrParamCreateCtrl
+ CALL NewGroupbox
+end;
+
+function FormNewEditBox( Form: PControl ): PControl;
+asm
+ CALL FormPrepareIntParamCreateCtrl
+ CALL NewEditBox
+end;
+
+{$IFDEF USE_RICHEDIT}
+function FormNewRichEdit( Form: PControl ): PControl;
+asm CALL FormPrepareIntParamCreateCtrl
+ CALL NewRichEdit
+end;
+{$ENDIF USE_RICHEDIT}
+
+function FormNewComboBox( Form: PControl ): PControl;
+asm
+ CALL FormPrepareIntParamCreateCtrl
+ CALL NewCombobox
+end;
+
+function FormNewCheckbox( Form: PControl ): PControl;
+asm
+ CALL FormPrepareStrParamCreateCtrl
+ CALL NewCheckbox
+end;
+
+function FormNewRadiobox( Form: PControl ): PControl;
+asm
+ CALL FormPrepareStrParamCreateCtrl
+ CALL NewRadiobox
+end;
+
+function FormNewListbox( Form: PControl ): PControl;
+asm
+ CALL FormPrepareIntParamCreateCtrl
+ CALL NewListbox
+end;
+
+//!!! asm version returns in EAX Control,
+// and integer parameter in EDX and ECX (EDX=ECX) !!!
+//--- this is enough to call method of Control with a single int param ---
+function ParentForm_IntParamAsm(Control: PControl): Integer;
+asm PUSH EAX
+ CALL TControl.FormParentForm
+ CALL TControl.FormGetIntParam
+ XCHG EDX, EAX
+ MOV ECX, EDX
+ POP EAX
+end;
+function ParentForm_ColorParamAsm(Control: PControl): Integer;
+asm CALL ParentForm_IntParamAsm
+ ROR EDX, 1
+end;
+
+procedure FormSetSize( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ PUSH EDX
+ CALL ParentForm_IntParamAsm
+ //XCHG ECX, EDX
+ POP EDX
+ CALL TControl.SetSize
+end;
+
+function ParentForm_PCharParamAsm(Control: PControl): PChar;
+asm PUSH EAX
+ CALL ParentForm_PCharParam
+ XCHG EDX, EAX
+ MOV ECX, EDX
+ POP EAX
+end;
+
+procedure FormSetPosition( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ PUSH EDX
+ CALL ParentForm_IntParamAsm
+ POP EDX
+ CALL TControl.SetPosition
+end;
+
+procedure FormSetClientSize( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ PUSH EDX
+ CALL ParentForm_IntParamAsm
+ //XCHG ECX, EDX
+ POP EDX
+ CALL TControl.SetClientSize
+end;
+
+procedure FormSetAlign( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ CALL TControl.SetAlign
+end;
+
+procedure FormSetCanResizeFalse( Form: PControl );
+asm
+ XOR EDX, EDX
+ CALL TControl.SetCanResize
+end;
+
+procedure FormInitMenu( Form: PControl );
+asm
+ PUSH 0
+ PUSH 0
+ PUSH WM_INITMENU
+ PUSH EAX
+ CALL TControl.Perform
+end;
+
+procedure FormSetExStyle( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ OR EDX, [EAX].TControl.fExStyle
+ CALL TControl.SetExStyle
+end;
+
+procedure FormSetVisibleFalse( Form: PControl );
+asm
+ XOR EDX, EDX
+ CALL TControl.SetVisible
+end;
+
+procedure FormSetEnabledFalse( Form: PControl );
+asm
+ XOR EDX, EDX
+ CALL TControl.SetEnabled
+end;
+
+procedure FormResetStyles( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ NOT EDX
+ AND EDX, [EAX].TControl.fStyle
+ CALL TControl.SetStyle
+end;
+
+procedure FormSetStyle( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ OR EDX, [EAX].TControl.fStyle
+ CALL TControl.SetStyle
+end;
+
+procedure FormSetAlphaBlend( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ CALL TControl.SetAlphaBlend
+end;
+
+procedure FormSetHasBorderFalse( Form: PControl );
+asm
+ XOR EDX, EDX
+ CALL TControl.SetHasBorder
+end;
+
+procedure FormSetHasCaptionFalse( Form: PControl );
+asm
+ XOR EDX, EDX
+ CALL TControl.SetHasCaption
+end;
+
+procedure FormResetCtl3D( Form: PControl );
+asm
+ XOR EDX, EDX
+ CALL TControl.SetCtl3D
+end;
+
+procedure FormIconLoad_hInstance( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ MOV EDX, [hInstance]
+ CALL TControl.IconLoad
+end;
+
+procedure FormIconLoadCursor_0( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ XOR EDX, EDX
+ CALL TControl.IconLoadCursor
+end;
+
+procedure FormSetIconNeg1( Form: PControl );
+asm
+ OR EDX, -1
+ CALL TControl.SetIcon
+end;
+
+procedure FormSetWindowState( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ CALL TControl.SetWindowState
+end;
+
+procedure FormCursorLoad_0( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ XOR EDX, EDX
+ CALL TControl.CursorLoad
+end;
+
+procedure FormSetColor( Form: PControl );
+asm
+ CALL ParentForm_ColorParamAsm
+ CALL TControl.SetCtlColor
+end;
+
+procedure FormSetBrushStyle( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ PUSH EDX
+ CALL TControl.GetBrush
+ POP EDX
+ CALL TGraphicTool.SetBrushStyle
+end;
+
+procedure FormSetBrushBitmap( Form: PControl );
+asm
+ PUSH EDI
+ MOV EDI, EAX
+ CALL TControl.FormParentForm
+
+ PUSH EAX
+ CALL ParentForm_PCharParam
+ XCHG EDX, EAX
+ MOV EAX, [hInstance]
+ POP ECX
+
+ CALL LoadBmp
+
+ PUSH EAX
+ MOV EAX, EDI
+ CALL TControl.GetBrush
+ POP EDX
+
+ CALL TGraphicTool.SetBrushBitmap
+ POP EDI
+end;
+
+procedure FormSetFontColor( Form: PControl );
+asm
+ CALL ParentForm_ColorParamAsm
+ PUSH EDX
+ CALL TControl.GetFont
+ POP EDX
+ CALL TGraphicTool.SetColor
+end;
+
+procedure FormSetFontStyles( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ PUSH EDX
+ CALL TControl.GetFont
+ POP EDX
+ CALL TGraphicTool.SetFontStyle
+end;
+
+procedure FormSetFontHeight( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ PUSH EDX
+ CALL TControl.GetFont
+ XOR EDX, EDX
+ MOV DL, 4
+ POP ECX
+ CALL TGraphicTool.SetInt
+end;
+
+procedure FormSetFontWidth( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ PUSH EDX
+ CALL TControl.GetFont
+ XOR EDX, EDX
+ MOV DL, 8
+ POP ECX
+ CALL TGraphicTool.SetInt
+end;
+
+procedure FormSetFontOrientation( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ PUSH EDX
+ CALL TControl.GetFont
+ POP EDX
+ CALL TGraphicTool.SetFontOrientation
+end;
+
+procedure FormSetFontCharset( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ PUSH EDX
+ CALL TControl.GetFont
+ POP EDX
+ CALL TGraphicTool.SetFontCharset
+end;
+
+procedure FormSetFontPitch( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ PUSH EDX
+ CALL TControl.GetFont
+ POP EDX
+ CALL TGraphicTool.SetFontPitch
+end;
+
+procedure FormSetBorder( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ MOV [EAX].TControl.fMargin, DL
+end;
+
+procedure FormSetMarginTop( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ XOR EDX, EDX
+ INC EDX
+ CALL TControl.SetClientMargin
+end;
+
+procedure FormSetMarginBottom( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ XOR EDX, EDX
+ MOV DL, 2
+ CALL TControl.SetClientMargin
+end;
+
+procedure FormSetMarginLeft( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ XOR EDX, EDX
+ MOV DL, 3
+ CALL TControl.SetClientMargin
+end;
+
+procedure FormSetMarginRight( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ XOR EDX, EDX
+ MOV DL, 4
+ CALL TControl.SetClientMargin
+end;
+
+procedure FormSetSimpleStatusText( Form: PControl );
+asm
+ CALL ParentForm_PCharParamAsm
+ XOR EDX, EDX
+ MOV DL, 255
+ CALL TControl.SetStatusText
+end;
+
+procedure FormSetStatusText( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ PUSH EDX
+ CALL ParentForm_PCharParamAsm
+ POP EDX
+ CALL TControl.SetStatusText
+end;
+
+procedure FormRemoveCloseIcon( Form: PControl );
+asm
+ PUSH MF_BYCOMMAND
+ PUSH SC_CLOSE
+ CALL TControl.GetWindowHandle
+ PUSH 0
+ PUSH EAX
+ CALL GetSystemMenu
+ PUSH EAX
+ CALL DeleteMenu
+end;
+
+procedure FormSetConstraint;
+asm
+ MOVZX EDX, DL
+ PUSH EDX
+ CALL ParentForm_IntParamAsm
+ POP EDX
+ CALL TControl.SetConstraint
+end;
+
+procedure FormSetMinWidth( Form: PControl );
+asm
+ XOR EDX, EDX
+ CALL FormSetConstraint
+end;
+
+procedure FormSetMaxWidth( Form: PControl );
+asm
+ MOV DL, 2
+ CALL FormSetConstraint
+end;
+
+procedure FormSetMinHeight( Form: PControl );
+asm
+ MOV DL, 1
+ CALL FormSetConstraint
+end;
+
+procedure FormSetMaxHeight( Form: PControl );
+asm
+ MOV DL, 3
+ CALL FormSetConstraint
+end;
+
+procedure FormSetTextShiftX( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ MOV [EAX].TControl.DF.fTextShiftX, EDX
+end;
+
+procedure FormSetTextShiftY( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ MOV [EAX].TControl.DF.fTextShiftY, EDX
+end;
+
+procedure FormSetColor2( Form: PControl );
+asm
+ CALL ParentForm_ColorParamAsm
+ CALL TControl.SetColor2
+end;
+
+procedure FormSetTextAlign( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ CALL TControl.SetTextAlign
+end;
+
+procedure FormSetTextVAlign( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ CALL TControl.SetVerticalAlign
+end;
+
+procedure FormSetIgnoreDefault( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ {$IFDEF USE_FLAGS}
+ SHL EDX, G5_IgnoreDefault
+ AND [EAX].TControl.fFlagsG5, $7F //not(1 shl G5_IgnoreDefault)
+ OR [EAX].TControl.fFlagsG5, DL
+ {$ELSE}
+ MOV [EAX].TControl.FIgnoreDefault, DL
+ {$ENDIF}
+end;
+
+procedure FormSetCaption( Form: PControl );
+asm
+ PUSH EAX
+ CALL TControl.FormParentForm
+ PUSH EAX
+ CALL TControl.FormGetStrParam
+ POP EAX
+ MOV EDX, [EAX].TControl.FormString
+ POP EAX
+ CALL TControl.SetCaption
+end;
+
+procedure FormSetGradienStyle( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ CALL TControl.SetGradientStyle
+end;
+
+{$IFDEF USE_RICHEDIT}
+procedure FormSetRE_AutoFontFalse( Form: PControl );
+asm
+ XOR EDX, EDX
+ MOV DL, 4
+ XOR ECX, ECX
+ CALL TControl.RESetLangOptions
+end;
+
+procedure FormSetRE_AutoFontSizeAdjustFalse( Form: PControl );
+asm
+ XOR EDX, EDX
+ MOV DL, 16
+ XOR ECX, ECX
+ CALL TControl.RESetLangOptions
+end;
+
+procedure FormSetRE_DualFontTrue( Form: PControl );
+asm
+ XOR EDX, EDX
+ MOV DL, 128
+ MOV CL, 1
+ CALL TControl.RESetLangOptions
+end;
+
+procedure FormSetRE_UIFontsTrue( Form: PControl );
+asm
+ XOR EDX, EDX
+ MOV DL, 32
+ MOV CL, 1
+ CALL TControl.RESetLangOptions
+end;
+
+procedure FormSetRE_IMECancelCompleteTrue( Form: PControl );
+asm
+ XOR EDX, EDX
+ MOV DL, 4
+ MOV CL, 1
+ CALL TControl.RESetLangOptions
+end;
+
+procedure FormSetRE_IMEAlwaysSendNotifyTrue( Form: PControl );
+asm
+ XOR EDX, EDX
+ MOV DL, 8
+ MOV CL, 1
+ CALL TControl.RESetLangOptions
+end;
+
+procedure FormSetMaxTextSize( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ CALL TControl.SetMaxTextSize
+end;
+
+procedure FormSetRE_AutoKeyboardTrue( Form: PControl );
+asm
+ XOR EDX, EDX
+ MOV DL, 1
+ MOV CL, 1
+ CALL TControl.RESetLangOptions
+end;
+
+procedure FormSetRE_Zoom( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ PUSH EDX
+ CALL ParentForm_IntParamAsm
+ POP EDX
+ SHL ECX, 16
+ OR EDX, ECX
+ CALL TControl.ReSetZoom
+end;
+{$ENDIF USE_RICHEDIT}
+
+procedure FormSetCount( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ CALL TControl.SetItemsCount
+end;
+
+procedure FormSetDroppedWidth( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ CALL TControl.SetDroppedWidth
+end;
+
+procedure FormSetButtonImage( Form: PControl );
+asm
+ PUSH EDI
+ MOV EDI, EAX
+ CALL ParentForm_IntParamAsm
+ PUSH ECX
+ CALL ParentForm_IntParamAsm
+ POP ECX
+ PUSH $8000 // LR_SHARED
+ PUSH ECX
+ PUSH EDX
+ PUSH IMAGE_ICON
+ CALL ParentForm_PCharParam
+ PUSH EAX
+ PUSH [hInstance]
+ CALL LoadImage
+ XCHG EDX, EAX
+ XCHG EAX, EDI
+ CALL TControl.SetButtonIcon
+ POP EDI
+end;
+
+procedure FormSetButtonBitmap( Form: PControl );
+asm
+ PUSH EAX
+ CALL ParentForm_PCharParam
+ PUSH EAX
+ PUSH [hInstance]
+ CALL LoadBitmap
+ XCHG EDX, EAX
+ POP EAX
+ CALL TControl.SetButtonBitmap
+end;
+
+procedure FormSetMaxProgress( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ MOV EDX, (PBM_SETRANGE32 or $8000) shl 16
+ CALL TControl.SetMaxProgress
+end;
+
+procedure FormSetProgress( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ MOV EDX, (PBM_SETPOS or $8000) shl 16
+ CALL TControl.SetIntVal
+end;
+
+procedure FormLVColumsAdd( Form: PControl );
+asm
+ PUSH EDI
+ MOV EDI, EAX
+ CALL ParentForm_IntParamAsm
+ JECXZ @@fin
+@@1:
+ PUSH ECX
+ MOV EAX, EDI
+ CALL ParentForm_IntParamAsm
+ PUSH ECX
+ CALL ParentForm_StrParam
+ MOV EAX, EDI
+ CALL TControl.FormParentForm
+ MOV EDX, [EAX].TControl.FormString
+ XOR ECX, ECX
+ MOV CL, taLeft
+ MOV EAX, EDI
+ CALL TControl.LVColAdd
+ POP ECX
+ LOOP @@1
+@@fin:
+ POP EDI
+end;
+
+procedure FormSetLVColOrder( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ PUSH EDX
+ CALL ParentForm_IntParamAsm
+ POP EDX
+ PUSH ECX
+ MOV ECX, LVCF_ORDER or (28 shl 16)
+ CALL TControl.SetLVColEx
+end;
+
+procedure FormSetLVColImage( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ PUSH EDX
+ CALL ParentForm_IntParamAsm
+ POP EDX
+ PUSH ECX
+ MOV ECX, LVCF_IMAGE or (24 shl 16)
+ CALL TControl.SetLVColEx
+end;
+
+procedure FormSetTVIndent( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ MOV EDX, TVM_GETINDENT
+ CALL TControl.SetIntVal
+end;
+
+procedure FormSetDateTimeFormat( Form: PControl );
+asm
+ PUSH EAX
+ CALL TControl.FormParentForm
+ PUSH EAX
+ CALL TControl.FormGetStrParam
+ POP EAX
+ MOV EDX, [EAX].TControl.FormString
+ POP EAX
+ CALL TControl.SetDateTimeFormat
+end;
+
+procedure FormSetCurrentTab( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ PUSHAD
+ CALL TControl.SetCurIndex
+ POPAD
+ CALL TControl.GetPages
+ CALL TControl.BringToFront
+end;
+
+procedure FormSetCurIdx( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ CALL TControl.SetCurIndex
+end;
+
+procedure FormSetSBMin( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ CALL TControl.SetSBMin
+end;
+
+procedure FormSetSBMax( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ CALL TControl.SetSBMax
+end;
+
+procedure FormSetSBPosition( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ CALL TControl.SetSBPosition
+end;
+
+procedure FormSetSBPageSize( Form: PControl );
+asm
+ CALL ParentForm_IntParamAsm
+ CALL TControl.SetSBPageSize
+end;
+
+procedure FormLastCreatedChildAsNewCurrentParent( Form: PControl );
+asm
+ PUSH EAX
+ CALL TControl.FormParentForm
+ POP [EAX].TControl.DF.FormCurrentParent
+end;
+
+procedure FormSetTabpageAsParent( Form: PControl );
+asm
+ PUSH EAX
+ CALL TControl.FormParentForm
+ CALL ParentForm_IntParamAsm
+ POP ECX
+ PUSH EAX
+ XCHG EAX, ECX
+ CALL TControl.GetPages
+ POP EDX
+ MOV [EDX].TControl.DF.FormCurrentParent, EAX
+ MOV [EDX].TControl.DF.FormLastCreatedChild, EAX
+end;
+
+procedure FormSetCurCtl( Form: PControl );
+asm
+ CALL TControl.FormParentForm
+ CALL ParentForm_IntParamAsm
+ MOV ECX, [EAX].TControl.DF.FormAddress
+ MOV ECX, [ECX + EDX*4]
+
+ TEST ECX, ECX
+ JNZ @@1
+ MOV ECX, EAX
+
+@@1:
+ MOV [EAX].TControl.DF.FormLastCreatedChild, ECX
+end;
+
+procedure FormSetEvent( Form: PControl );
+asm
+ PUSH EDI
+ MOV EDI, EAX
+ PUSH ESI
+ CALL TControl.FormParentForm
+ MOV ESI, EAX
+ PUSH [ESI].TControl.DF.FormObj
+ CALL ParentForm_IntParamAsm
+ MOV ESI, [EAX].TControl.DF.FormAlphabet
+ PUSH dword ptr [ESI+EDX*4]
+ CALL ParentForm_IntParamAsm
+ XCHG EAX, EDI
+ CALL dword ptr [ESI+EDX*4]
+ POP ESI
+ POP EDI
+end;
+
+procedure FormSetIndexedEvent( Form: PControl );
+asm
+ PUSH EDI
+ MOV EDI, EAX
+ PUSH ESI
+ CALL TControl.FormParentForm
+ MOV ESI, EAX
+ PUSH [ESI].TControl.DF.FormObj
+ CALL ParentForm_IntParamAsm
+ MOV ESI, [EAX].TControl.DF.FormAlphabet
+ PUSH dword ptr [ESI+EDX*4]
+
+ CALL ParentForm_IntParamAsm // idx
+ PUSH EDX
+
+ CALL ParentForm_IntParamAsm
+ XCHG EAX, EDI
+ MOV ECX, dword ptr [ESI+EDX*4]
+
+ POP EDX
+ CALL ECX
+ POP ESI
+ POP EDI
+end;
+
+{$ENDIF}
+
+//======================================== THE END OF FILE KOL_ASM.inc
diff --git a/plugins/Libs/KOL_ASM_NOUNICODE.inc b/plugins/Libs/KOL_ASM_NOUNICODE.inc
new file mode 100644
index 0000000000..29c9c49f15
--- /dev/null
+++ b/plugins/Libs/KOL_ASM_NOUNICODE.inc
@@ -0,0 +1,4351 @@
+//------------------------------------------------------------------------------
+// KOL_ASM_NOUNICODE.inc (to inlude in KOL.pas)
+// v 3.141592
+
+// this part of code is for case when ASM_VERSION is enabled and the symbol
+// UNICODE_CTRLS is NOT defined (functions, procedures and methods which work
+// with AnsiStrings only)
+
+const comctl32_const: PKOLChar = 'comctl32';
+ InitCommonControlsEx_const: PKOLChar = 'InitCommonControlsEx';
+procedure DoInitCommonControls( dwICC: DWORD );
+asm
+ PUSH EAX // dwICC
+ CALL InitCommonControls
+ MOV EAX, [ComCtl32_Module]
+ TEST EAX, EAX
+ JNZ @@1
+ PUSH [comctl32_const]
+ CALL LoadLibrary
+ MOV [ComCtl32_Module], EAX
+@@1:PUSH [InitCommonControlsEx_const]
+ PUSH EAX
+ CALL GetProcAddress
+ XCHG ECX, EAX
+ {$IFDEF SAFE_CODE}
+ POP EDX
+ JECXZ @@fin
+ PUSH EDX
+ {$ENDIF}
+ PUSH 8 // dwSize
+ PUSH ESP // @ ICC
+ CALL ECX // Proc( @ ICC )
+ POP ECX
+ POP ECX
+@@fin:
+end;
+
+function ShowMsg( const S: KOLString; Flags: DWORD ): DWORD;
+asm
+ push edx // Flags
+ mov ecx, [Applet]
+ {$IFDEF SNAPMOUSE2DFLTBTN}
+ {$IFDEF SAFE_CODE}
+ jecxz @@0
+ {$ENDIF}
+ pushad
+ xchg eax, ecx
+ mov edx, offset[WndProcSnapMouse2DfltBtn]
+ call TControl.AttachProc
+ popad
+@@0:
+ {$ENDIF}
+ mov edx, 0
+ {$IFDEF SAFE_CODE}
+ jecxz @@1
+ {$ENDIF}
+ mov edx, [ecx].TControl.fHandle
+ mov ecx, [ecx].TControl.fCaption
+@@1: push ecx // Title
+ push eax // S
+ push edx // Wnd
+ call MessageBox
+ {$IFDEF SNAPMOUSE2DFLTBTN}
+ mov ecx, [Applet]
+ {$IFDEF SAFE_CODE}
+ jecxz @@2
+ {$ENDIF}
+ pushad
+ xchg eax, ecx
+ mov edx, offset[WndProcSnapMouse2DfltBtn]
+ call TControl.DetachProc
+ popad
+@@2:
+ {$ENDIF}
+end;
+
+procedure TGraphicTool.SetFontName(const Value: KOLString);
+asm
+ PUSH EAX
+ LEA EAX, [EAX].fData.Font.Name
+ XOR ECX, ECX
+ MOV CL, 32
+ PUSH EAX
+ PUSH ECX
+ PUSH EDX
+ CALL StrLComp
+ //TEST EAX, EAX
+ POP EDX
+ POP ECX
+ POP EAX
+ JZ @@exit
+ CALL StrLCopy
+ POP EAX
+ PUSH EAX
+ CALL Changed
+@@exit: POP EAX
+end;
+
+{$IFDEF TEXT_EXTENT_OLD}
+function TCanvas.TextExtent(const Text: KOLString): TSize;
+asm
+ PUSH EBX
+ PUSH ESI
+ MOV EBX, EAX
+ PUSH ECX
+ PUSH ECX // prepare @Result
+ MOV EAX, EDX
+ CALL System.@LStrLen
+ PUSH EAX // prepare Length(Text)
+ CALL EDX2PChar
+ PUSH EDX // prepare PChar(Text)
+ {$IFDEF SAFE_CODE}
+ MOV EAX, EBX
+ CALL RefInc
+ {$ENDIF}
+ PUSH HandleValid or FontValid
+ PUSH EBX
+ CALL RequiredState
+ XCHG ESI, EAX
+ TEST ESI, ESI // ESI = fHandle before
+ JNZ @@1
+ PUSH ESI
+ CALL CreateCompatibleDC
+ MOV EDX, EBX
+ XCHG EAX, EDX // EAX := @Self; EDX := DC
+ CALL SetHandle
+//****************************************************** // Added By M.Gerasimov
+ CMP WORD PTR [EBX].TCanvas.fIsPaintDC, 0
+ JNZ @@2
+ XOR ESI,ESI
+@@2:
+//******************************************************
+@@1:
+ PUSH HandleValid or FontValid
+ PUSH EBX
+ CALL RequiredState
+ PUSH EAX // prepare DC
+ CALL Windows.GetTextExtentPoint32A // KOL_ANSI
+ POP EDX // @ Result
+ {$IFDEF FIX_ITALIC_TEXT_WIDTH}
+ MOV ECX, [EBX].fFont
+ //JECXZ @@0
+ CMP [ECX].TGraphicTool.fData.Font.Italic, 0
+ JZ @@0
+ MOV EAX, [EDX].TSize.cy
+ SHR EAX, 2
+ ADD DWORD PTR [EDX], EAX
+@@0: {$ENDIF}
+ TEST ESI, ESI
+ JNZ @@exit
+ XOR EDX, EDX
+ XCHG EAX, EBX
+ CALL SetHandle
+@@exit:
+ {$IFDEF SAFE_CODE}
+ PUSH EAX
+ XCHG EAX, EBX
+ CALL RefDec
+ POP EAX
+ {$ENDIF}
+ POP ESI
+ POP EBX
+end;
+{$ELSE TEXT_EXTENT_NEW}
+function TCanvas.TextExtent(const Text: KOLString): TSize;
+asm
+ PUSH ESI
+ {$IFDEF FIX_ITALIC_TEXT_WIDTH}
+ PUSH EBX
+ MOV EBX, ECX
+ {$ENDIF}
+ XCHG ESI, EAX // ESI = @Self: PCanvas
+ CALL EDX2PChar
+ PUSH ECX
+ PUSH EDX
+
+ XCHG EAX, EDX
+ CALL StrLen
+ XCHG [ESP], EAX
+ PUSH EAX
+
+ PUSH HandleValid or FontValid
+ PUSH ESI
+ CALL TCanvas.RequiredState
+ PUSH [ESI].TCanvas.fHandle
+ CALL GetTextExtentPoint32
+ {$IFDEF FIX_ITALIC_TEXT_WIDTH}
+ CMP [ESI].TGraphicTool.fData.Font.Italic, 0
+ JZ @@1
+ MOV EAX, [EBX].TSize.cy
+ SHR EAX, 2
+ ADD DWORD PTR [EBX].TSize, EAX
+@@1: POP EBX
+ {$ENDIF}
+ POP ESI
+end;
+{$ENDIF TEXT_EXTENT_NEW}
+
+procedure TCanvas.TextOut(X, Y: Integer; const Text: KOLString); stdcall;
+asm
+ PUSH EBX
+ MOV EBX, [EBP+8]
+
+ MOV EAX, [Text]
+ PUSH EAX
+ CALL System.@LStrLen
+ XCHG EAX, [ESP] // prepare Length(Text)
+
+ //CALL System.@LStrToPChar // string does not need to be null-terminated !
+ PUSH EAX // prepare PChar(Text)
+ PUSH [Y] // prepare Y
+ PUSH [X] // prepare X
+
+ PUSH HandleValid or FontValid or BrushValid or ChangingCanvas
+ PUSH EBX
+ CALL RequiredState
+ PUSH EAX // prepare fHandle
+ CALL Windows.TextOutA // KOL_ANSI
+
+ POP EBX
+end;
+
+function Int2Hex( Value : DWord; Digits : Integer ) : KOLString;
+asm // EAX = Value
+ // EDX = Digits
+ // ECX = @Result
+ PUSH 0
+ ADD ESP, -0Ch
+ PUSH EDI
+ PUSH ECX
+ LEA EDI, [ESP+8+0Fh] // EBX := @Buf[ 15 ]
+ {$IFDEF SMALLEST_CODE}
+ {$ELSE}
+ AND EDX, $F
+ {$ENDIF}
+@@loop: DEC EDI
+ DEC EDX
+ PUSH EAX
+ {$IFDEF PARANOIA} DB $24, $0F {$ELSE} AND AL, 0Fh {$ENDIF}
+ AAM
+ DB $D5, $11 //AAD
+ ADD AL, $30
+ STOSB
+ DEC EDI
+ POP EAX
+ SHR EAX, 4
+ JNZ @@loop
+ TEST EDX, EDX
+ JG @@loop
+ POP EAX // EAX = @Result
+ MOV EDX, EDI // EDX = @resulting string
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX
+ {$ENDIF}
+ CALL System.@LStrFromPChar
+ POP EDI
+ ADD ESP, 10h
+end;
+
+function Hex2Int( const Value : AnsiString) : Integer;
+asm
+ CALL EAX2PChar
+ PUSH ESI
+ XCHG ESI, EAX
+ XOR EDX, EDX
+ TEST ESI, ESI
+ JE @@exit
+ LODSB
+ {$IFDEF PARANOIA} DB $3C, '$' {$ELSE} CMP AL, '$' {$ENDIF}
+ JNE @@1
+@@0: LODSB
+@@1: TEST AL, AL
+ JE @@exit
+ {$IFDEF PARANOIA} DB $2C, '0' {$ELSE} SUB AL, '0' {$ENDIF}
+ {$IFDEF PARANOIA} DB $3C, 9 {$ELSE} CMP AL, '9' - '0' {$ENDIF}
+ JBE @@3
+
+ {$IFDEF PARANOIA} DB $2C, $11 {$ELSE} SUB AL, 'A' - '0' {$ENDIF}
+ {$IFDEF PARANOIA} DB $3C, 5 {$ELSE} CMP AL, 'F' - 'A' {$ENDIF}
+ JBE @@2
+
+ {$IFDEF PARANOIA} DB $2C, 32 {$ELSE} SUB AL, 32 {$ENDIF}
+ {$IFDEF PARANOIA} DB $3C, 5 {$ELSE} CMP AL, 'F' - 'A' {$ENDIF}
+ JA @@exit
+@@2:
+ {$IFDEF PARANOIA} DB $04, 0Ah {$ELSE} ADD AL, 0Ah {$ENDIF}
+@@3:
+ SHL EDX, 4
+ ADD DL, AL
+ JMP @@0
+
+@@exit: XCHG EAX, EDX
+ POP ESI
+end;
+
+function Int2Str( Value : Integer ) : KOLString;
+asm
+ XOR ECX, ECX
+ PUSH ECX
+ ADD ESP, -0Ch
+
+ PUSH EBX
+ LEA EBX, [ESP + 15 + 4]
+ PUSH EDX
+ CMP EAX, ECX
+ PUSHFD
+ JGE @@1
+ NEG EAX
+@@1:
+ MOV CL, 10
+
+@@2:
+ DEC EBX
+ XOR EDX, EDX
+ DIV ECX
+ ADD DL, 30h
+ MOV [EBX], DL
+ TEST EAX, EAX
+ JNZ @@2
+
+ POPFD
+ JGE @@3
+
+ DEC EBX
+ MOV byte ptr [EBX], '-'
+@@3:
+ POP EAX
+ MOV EDX, EBX
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX // TODO: safe to destory twice?
+ {$ENDIF}
+ CALL System.@LStrFromPChar
+
+ POP EBX
+ ADD ESP, 10h
+end;
+
+function Int2Ths( I : Integer ) : AnsiString;
+asm
+ PUSH EBP
+ MOV EBP, ESP
+ PUSH EAX
+ PUSH EDX
+ CALL Int2Str
+ POP EDX
+ POP EAX
+ TEST EAX, EAX
+ JGE @@0
+ NEG EAX
+@@0:
+ CMP EAX, 1000
+ JL @@Exit
+ PUSH EDX
+ MOV EAX, [EDX]
+ PUSH EAX
+ CALL System.@LStrLen // EAX = Length(Result)
+ POP EDX
+ PUSH EDX // EDX = @Result[ 1 ]
+ XOR ECX, ECX
+
+@@1:
+ ROL ECX, 8
+ DEC EAX
+ MOV CL, [EDX+EAX]
+ JZ @@fin
+ CMP ECX, 300000h
+ JL @@1
+
+ PUSH ECX
+ XOR ECX, ECX
+ MOV CL, [ThsSeparator]
+ JMP @@1
+
+@@fin: CMP CL, '-'
+ JNE @@fin1
+ CMP CH, [ThsSeparator]
+ JNE @@fin1
+ MOV CH, 0 // this corrects -,ddd,...
+@@fin1: CMP ECX, 01000000h
+ JGE @@fin2
+ INC EAX
+ ROL ECX, 8
+ JMP @@fin1
+@@fin2: PUSH ECX
+
+ LEA EDX, [ESP+EAX]
+ MOV EAX, [EBP-4]
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX // TODO: safe to change ecx?
+ {$ENDIF}
+ CALL System.@LStrFromPChar
+@@Exit:
+ MOV ESP, EBP
+ POP EBP
+end;
+
+function Int2Digs( Value, Digits : Integer ) : KOLString;
+asm
+ PUSH EBP
+ MOV EBP, ESP
+ PUSH EDX // [EBP-4] = Digits
+ PUSH ECX
+ MOV EDX, ECX
+ CALL Int2Str
+ POP ECX
+ PUSH ECX // [EBP-8] = @Result
+ MOV EAX, [ECX]
+ PUSH EAX
+ CALL System.@LStrLen
+ POP EDX // EDX = @Result[1]
+ MOV ECX, EAX // ECX = Length( Result )
+ ADD EAX, EAX
+ SUB ESP, EAX
+ MOV EAX, ESP
+ PUSHAD
+ CALL StrCopy
+ POPAD
+ MOV EDX, EAX
+ ADD ESP, -100
+ CMP byte ptr [EDX], '-'
+ PUSHFD
+ JNE @@1
+ INC EDX
+@@1:
+ MOV EAX, [EBP-4] // EAX = Digits
+ CMP ECX, EAX
+ JGE @@2
+ DEC EDX
+ MOV byte ptr [EDX], '0'
+ INC ECX
+ JMP @@1
+@@2:
+ POPFD
+ JNE @@3
+ DEC EDX
+ MOV byte ptr [EDX], '-'
+@@3:
+ MOV EAX, [EBP-8]
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX // TODO: eax or ecx affect result?
+ {$ENDIF}
+ CALL System.@LStrFromPChar
+ MOV ESP, EBP
+ POP EBP
+end;
+
+function Num2Bytes( Value : Double ) : KOLString;
+asm PUSH EBX
+ PUSH ESI
+ PUSH EDI
+ MOV EBX, ESP
+ MOV ESI, EAX
+ MOV ECX, 4
+ MOV EDX, 'TGMk'
+@@1: FLD [Value]
+@@10: FICOM dword ptr [@@1024]
+ FSTSW AX
+ SAHF
+ JB @@2
+ FIDIV dword ptr [@@1024]
+ FST [Value]
+ WAIT
+ TEST DL, 20h
+ JE @@ror
+ AND DL, not 20h
+ JMP @@nxt
+@@1024: DD 1024
+@@100: DD 100
+@@ror: ROR EDX, 8
+@@nxt: LOOP @@10
+@@2: TEST DL, 20h
+ JZ @@3
+ MOV DL, 0
+@@3: MOV DH, 0
+ PUSH DX
+ MOV EDI, ESP
+ FLD ST(0)
+ CALL System.@TRUNC
+ {$IFDEF _D2orD3}
+ PUSH 0
+ {$ELSE}
+ PUSH EDX
+ {$ENDIF}
+ PUSH EAX
+ FILD qword ptr [ESP]
+ POP EDX
+ POP EDX
+ MOV EDX, ESI
+ CALL Int2Str
+ FSUBP ST(1), ST
+ FIMUL dword ptr [@@100]
+ CALL System.@TRUNC
+ TEST EAX, EAX
+ JZ @@4
+ XOR ECX, ECX
+ MOV CL, 0Ah
+ CDQ
+ IDIV ECX
+ TEST EDX, EDX
+ JZ @@5
+ MOV AH, DL
+ SHL EAX, 16
+ ADD EAX, '00. '
+ PUSH EAX
+ MOV EDI, ESP
+ INC EDI
+ JMP @@4
+@@5: SHL EAX, 8
+ ADD AX, '0.'
+ PUSH AX
+ MOV EDI, ESP
+@@4: MOV EAX, [ESI]
+ CALL System.@LStrLen
+ ADD ESP, -100
+ SUB EDI, EAX
+ PUSH ESI
+ PUSH EDI
+ MOV ESI, [ESI]
+ MOV ECX, EAX
+ REP MOVSB
+ POP EDX
+ POP EAX
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX // TODO: IDIV
+ {$ENDIF}
+ CALL System.@LStrFromPChar
+ MOV ESP, EBX
+ POP EDI
+ POP ESI
+ POP EBX
+end;
+
+function S2Int( S: PKOLChar ): Integer;
+asm
+ XCHG EDX, EAX
+ XOR EAX, EAX
+ TEST EDX, EDX
+ JZ @@exit
+
+ XOR ECX, ECX
+ MOV CL, [EDX]
+ INC EDX
+ CMP CL, '-'
+ PUSHFD
+ JE @@0
+@@1: CMP CL, '+'
+ JNE @@2
+@@0: MOV CL, [EDX]
+ INC EDX
+@@2: SUB CL, '0'
+ CMP CL, '9'-'0'
+ JA @@fin
+ LEA EAX, [EAX+EAX*4] //
+ LEA EAX, [ECX+EAX*2] //
+ JMP @@0
+@@fin: POPFD
+ JNE @@exit
+ NEG EAX
+@@exit:
+end;
+
+function Str2Int(const Value : KOLString) : Integer;
+asm
+ CALL EAX2PChar
+ CALL S2Int
+end;
+
+function TrimLeft(const S: Ansistring): Ansistring;
+asm
+ XCHG EAX, EDX
+ CALL EDX2PChar
+ DEC EDX
+@@1: INC EDX
+ MOVZX ECX, byte ptr [EDX]
+ JECXZ @@fin
+ CMP CL, ' '
+ JBE @@1
+@@fin:
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX
+ {$ENDIF}
+ CALL System.@LStrFromPChar
+end;
+
+function TrimRight(const S: Ansistring): Ansistring;
+asm
+ PUSH EDX
+ PUSH EAX
+
+ PUSH EAX
+ CALL System.@LStrLen
+ XCHG EAX, [ESP]
+ CALL EAX2PChar
+ POP ECX
+ INC ECX
+@@1: DEC ECX
+ MOV DL, [EAX+ECX]
+ JL @@fin
+ CMP DL, ' '
+ JBE @@1
+@@fin:
+ INC ECX
+ POP EAX
+ XOR EDX, EDX
+ INC EDX
+ CALL System.@LStrCopy
+end;
+
+function CopyEnd( const S : AnsiString; Idx : Integer ) : AnsiString;
+asm
+ PUSH ECX
+ PUSH EAX
+ PUSH EDX
+
+ CALL System.@LStrLen
+
+ POP EDX
+ TEST EDX, EDX
+ JG @@1
+ XOR EDX, EDX
+ INC EDX
+@@1:
+ SUB EAX, EDX
+ MOV ECX, EAX
+
+ POP EAX
+ JGE @@ret_end
+
+ POP EAX
+ JL System.@LStrClr
+
+@@ret_end:
+ INC ECX
+ CALL System.@LStrCopy
+end;
+
+function CopyTail( const S : AnsiString; Len : Integer ) : AnsiString;
+asm
+ PUSH ECX
+ PUSH EAX
+ PUSH EDX
+ CALL System.@LStrLen
+ POP ECX
+ CMP ECX, EAX
+ {$IFDEF USE_CMOV}
+ CMOVG ECX, EAX
+ {$ELSE}
+ JLE @@1
+ MOV ECX, EAX
+@@1: {$ENDIF}
+
+ MOV EDX, EAX
+ SUB EDX, ECX
+ INC EDX
+ POP EAX
+ CALL System.@LStrCopy
+end;
+
+procedure DeleteTail( var S : AnsiString; Len : Integer );
+asm
+ PUSH EAX
+ PUSH EDX
+ MOV EAX, [EAX]
+ CALL System.@LStrLen
+ POP ECX
+ CMP ECX, EAX
+ {$IFDEF USE_CMOV}
+ CMOVG ECX, EAX
+ {$ELSE}
+ JLE @@1
+ MOV ECX, EAX
+@@1: {$ENDIF}
+
+ MOV EDX, EAX
+ SUB EDX, ECX
+ INC EDX
+ POP EAX
+ CALL System.@LStrDelete
+end;
+
+{$IFnDEF TEST_INDEXOFCHARS_COMPAT}
+function IndexOfChar( const S : KOLString; Chr : KOLChar ) : Integer;
+asm
+ CALL EAX2PChar
+ PUSH EAX
+ MOV ECX, [EAX-4]
+ CALL StrScanLen
+ POP EDX
+ JZ @@1
+ LEA EDX, [EAX+1]
+@@1: SUB EAX, EDX
+end;
+{$ENDIF}
+
+function IndexOfCharsMin( const S, Chars : AnsiString ) : Integer;
+asm PUSH ESI
+ PUSH EBX
+ PUSH EAX
+ CALL EDX2PChar
+ MOV ESI, EDX
+
+ OR EBX, -1
+ MOV ECX, [EDX-4]
+ JECXZ @@EXIT
+
+@@1: LODSB
+
+ XCHG EDX, EAX
+ POP EAX
+ PUSH EAX
+
+ PUSH ECX
+ CALL IndexOfChar
+ POP ECX
+ TEST EAX, EAX
+ JLE @@NEXT
+
+ TEST EBX, EBX
+ JLE @@ASGN
+ CMP EAX, EBX
+ JGE @@NEXT
+@@ASGN:
+ XCHG EAX, EBX
+@@NEXT: LOOP @@1
+
+@@EXIT: XCHG EAX, EBX
+ POP ECX
+ POP EBX
+ POP ESI
+end;
+
+function Parse( var S : AnsiString; const Separators : AnsiString ) : AnsiString;
+asm
+ PUSH EBX
+ PUSH ESI
+ PUSH EDI
+ MOV EDI, ECX
+ XCHG ESI, EAX
+ MOV EAX, [ESI]
+ CALL IndexOfCharsMin
+ XCHG EBX, EAX
+ TEST EBX, EBX
+ JG @@1
+ MOV EAX, [ESI]
+ CALL System.@LStrLen
+ XCHG EBX, EAX
+ INC EBX
+@@1:
+ XOR EDX, EDX
+ INC EDX
+ PUSH EDX
+
+ PUSH EDI
+ MOV ECX, EBX
+ DEC ECX
+ MOV EAX, [ESI]
+ CALL System.@LStrCopy
+ XCHG EAX, ESI
+ MOV ECX, EBX
+ POP EDX
+ CALL System.@LStrDelete
+ POP EDI
+ POP ESI
+ POP EBX
+end;
+
+function _StrSatisfy( S, Mask : PKOLChar ) : Boolean;
+asm
+ TEST EAX, EAX
+ JZ @@exit
+ XCHG ECX, EAX
+ // EDX <- Mask
+ // ECX <- S
+ XOR EAX, EAX
+ MOV AL, '*'
+@@rest_satisfy:
+ PUSH ECX
+ PUSH EDX
+
+@@nx_char:
+ MOV AH, [EDX]
+ OR AH, [ECX]
+ JZ @@fin //@@ret_true
+
+ MOV AH, 0
+
+ CMP word ptr [EDX], AX //'*'
+ JE @@fin //@@ret_true
+
+ CMP byte ptr [ECX], AH
+ JNE @@10
+
+ DEC EDX
+@@1:
+ INC EDX
+ CMP byte ptr [EDX], AL //'*'
+ JE @@1
+
+ CMP byte ptr [EDX], AH
+ SETZ AL
+ JMP @@fin
+
+@@10: CMP byte ptr [EDX], AH
+ JE @@ret_false
+
+ CMP byte ptr [EDX], '?'
+ JNE @@11
+
+@@go_nx_char:
+ INC ECX
+ INC EDX
+ JMP @@nx_char
+
+@@11:
+ CMP byte ptr [EDX], AL //'*'
+ JNE @@20
+
+ INC EDX
+@@12: CMP byte ptr [ECX], AH
+ JE @@ret_false
+
+ CALL @@rest_satisfy
+ TEST AL, AL
+ JNE @@fin
+ MOV AL, '*'
+
+ INC ECX
+ JMP @@12
+
+@@20: MOV AH, [EDX]
+ XOR AH, [ECX]
+
+ JE @@go_nx_char
+@@ret_false:
+ XOR EAX, EAX
+
+@@fin:
+ POP EDX
+ POP ECX
+@@exit:
+end;
+
+function StrSatisfy( const S, Mask: AnsiString ): Boolean;
+asm
+ PUSH ESI
+ TEST EAX, EAX
+ JZ @@exit
+
+ XCHG ESI, EAX
+
+ XCHG EAX, EDX
+ TEST EAX, EAX
+ JZ @@exit
+
+ CALL EAX2PChar
+
+ PUSH 0
+ MOV EDX, ESP
+ CALL AnsiLowerCase
+
+ XCHG EAX, ESI
+ CALL EAX2PChar
+
+ PUSH 0
+ MOV EDX, ESP
+ CALL AnsiLowerCase
+
+ POP EAX
+ POP EDX
+ PUSH EDX
+ PUSH EAX
+ CALL _StrSatisfy
+
+ XCHG ESI, EAX
+
+ CALL RemoveStr
+ CALL RemoveStr
+ XCHG EAX, ESI
+
+@@exit:
+ POP ESI
+end;
+
+function _2StrSatisfy( S, Mask: PAnsiChar ): Boolean;
+asm // //
+ PUSH EBX
+ PUSH ECX
+ XCHG EBX, EAX
+ PUSH 0
+ MOV EAX, ESP
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX
+ {$ENDIF}
+ CALL System.@LStrFromPChar
+ PUSH 0
+ MOV EAX, ESP
+ MOV EDX, EBX
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX
+ {$ENDIF}
+ CALL System.@LStrFromPChar
+ POP EAX
+ POP EDX
+ PUSH EDX
+ PUSH EAX
+ CALL StrSatisfy
+ XCHG EBX, EAX
+ CALL RemoveStr
+ CALL RemoveStr
+ XCHG EAX, EBX
+ POP ECX
+ POP EBX
+end;
+
+function SkipSpaces( P: PKOLChar ): PKOLChar;
+asm
+ DEC EAX
+@@loop: INC EAX
+ CMP byte ptr [EAX], 0
+ JE @@exit
+ CMP byte ptr [EAX], ' '
+ JBE @@loop
+@@exit:
+end;
+
+function SkipParam(P: PKOLChar): PKOLChar;
+asm
+ CALL SkipSpaces
+@@while: CMP byte ptr [EAX], ' '
+ JBE @@exit
+ CMP byte ptr [EAX], '"'
+ JNE @@incP_goLoop
+@@untilQuot:
+ INC EAX
+ CMP byte ptr [EAX], 0
+ JE @@exit
+ CMP byte ptr [EAX], '"'
+ JNE @@untilQuot
+@@incP_goLoop:
+ INC EAX
+ JMP @@while
+@@exit:
+end;
+
+function ParamCount: Integer;
+asm
+ CALL GetCommandLine
+ OR EDX, -1
+@@while: INC EDX
+ CALL SkipParam
+ CALL SkipSpaces
+ CMP byte ptr [EAX], 0
+ JNE @@while
+ XCHG EAX, EDX
+end;
+
+function __DelimiterLast( Str: PAnsiChar; Delimiters: PAnsiChar ): PAnsiChar;
+asm
+ PUSH ESI
+
+ CALL EAX2PChar
+
+ MOV ESI, EDX
+ MOV EDX, EAX
+
+@@tolast:
+ CMP byte ptr [EAX], 0
+ JZ @@next1
+ INC EAX
+ JMP @@tolast
+
+@@next1:
+ PUSH EAX
+
+@@next:
+ LODSB
+ TEST AL, AL
+ JZ @@exit
+
+ PUSH EDX
+ XCHG EDX, EAX
+ CALL StrRScan
+ POP EDX
+
+ TEST EAX, EAX
+ JZ @@next
+
+ POP ECX
+ CMP byte ptr [ECX], 0
+ JZ @@next1
+
+ CMP EAX, ECX
+ JG @@next1
+
+ PUSH ECX
+ JLE @@next
+
+@@exit: POP EAX
+ POP ESI
+end;
+
+function DelimiterLast( const Str, Delimiters: KOLString ): Integer;
+asm
+ CALL EAX2PChar
+ CALL EDX2PChar
+ PUSH EAX
+ CALL __DelimiterLast
+ POP EDX
+ SUB EAX, EDX
+ INC EAX
+end;
+
+function StrIsStartingFrom( Str, Pattern: PKOLChar ): Boolean;
+asm
+ {$IFDEF F_P}
+ MOV EAX, [Str]
+ MOV EDX, [Pattern]
+ {$ENDIF F_P}
+ XOR ECX, ECX
+ @@1:
+ MOV CL, [EDX] // pattern[ i ]
+ INC EDX
+ MOV CH, [EAX] // str[ i ]
+ INC EAX
+ JECXZ @@2 // str = pattern; CL = #0, CH = #0
+ CMP CL, CH
+ JE @@1
+ @@2:
+ TEST CL, CL
+ SETZ AL
+end {$IFDEF F_P} [ 'EAX', 'EDX', 'ECX' ] {$ENDIF};
+
+function Format( const fmt: KOLString; params: array of const ): AnsiString;
+asm
+ PUSH ESI
+ PUSH EDI
+ PUSH EBX
+ MOV EBX, ESP
+ {$IFDEF UNICODE_CTRLS}
+ ADD ESP, -2048
+ {$ELSE}
+ ADD ESP, -1024
+ {$ENDIF}
+ MOV ESI, ESP
+
+ INC ECX
+ JZ @@2
+@@1:
+ MOV EDI, [EDX + ECX*8 - 8]
+ PUSH EDI
+ LOOP @@1
+@@2:
+ PUSH ESP
+ PUSH EAX
+ PUSH ESI
+
+ CALL wvsprintf
+
+ MOV EDX, ESI
+ MOV EAX, @Result
+ {$IFDEF _D2009orHigher}
+ PUSH ECX
+ XOR ECX, ECX
+ {$ENDIF}
+ CALL System.@LStrFromPChar
+ {$IFDEF _D2009orHigher}
+ POP ECX
+ {$ENDIF}
+
+ MOV ESP, EBX
+ POP EBX
+ POP EDI
+ POP ESI
+end;
+
+function FileExists( const FileName : KOLString ) : Boolean;
+const size_TWin32FindData = sizeof( {$IFDEF UNICODE_CTRLS} TWin32FindDataW {$ELSE} TWin32FindDataA {$ENDIF} );
+ Size_TFindFileData = (sizeof(TFindFileData) + 3) and not 3;
+asm
+{$IFDEF FILE_EXISTS_EX}
+ PUSH EBX
+ MOV BL, 0
+ PUSH EAX
+ PUSH SEM_NOOPENFILEERRORBOX or SEM_FAILCRITICALERRORS
+ CALL SetErrorMode
+ XCHG EAX, [ESP]
+ SUB ESP, Size_TFindFileData
+ MOV EDX, ESP
+ CALL Find_First
+ TEST AL, AL
+ JZ @@fin
+ MOV EAX, ESP
+ CALL Find_Close
+ TEST byte ptr [ESP].TFindFileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY
+ JNZ @@fin
+ PUSH ESP
+ LEA EAX, [ESP+4].TFindFileData.ftLastWriteTime
+ PUSH EAX
+ CALL FileTimeToLocalFileTime
+ LEA EAX, [ESP+8]
+ PUSH EAX
+ INC EAX
+ INC EAX
+ PUSH EAX
+ SUB EAX, 10
+ PUSH EAX
+ CALL FileTimeToDOSDateTime
+ TEST EAX, EAX
+ SETNZ BL
+@@fin: ADD ESP, Size_TFindFileData
+ CALL SetErrorMode
+ XCHG EAX, EBX
+ POP EBX
+{$ELSE}
+ CALL EAX2PChar
+ PUSH EAX
+ CALL GetFileAttributes
+ INC EAX
+ JZ @@exit
+ DEC EAX
+ {$IFDEF PARANOIA} DB $24, FILE_ATTRIBUTE_DIRECTORY {$ELSE} AND AL, FILE_ATTRIBUTE_DIRECTORY {$ENDIF}
+ SETZ AL
+@@exit:
+{$ENDIF}
+end;
+
+function DiskPresent( const DrivePath: KOLString ): Boolean;
+asm
+ PUSH EBX
+ MOV BH, 0
+ TEST EAX, EAX
+ JZ @@dirExists
+ CMP byte ptr [EAX], '\'
+ JZ @@dirExists
+ PUSH EAX
+ PUSH EAX
+ CALL GetDriveType
+ CMP AL, DRIVE_REMOVABLE
+ JE @@setErrMode
+ CMP AL, DRIVE_CDROM
+ JE @@setErrMode
+ CMP AL, DRIVE_RAMDISK
+ JNE @@popPath_dirExists
+@@setErrMode:
+ INC BH
+ PUSH SEM_NOOPENFILEERRORBOX or SEM_FAILCRITICALERRORS
+ CALL SetErrorMode
+ XCHG [ESP], EAX
+ PUSH EAX
+@@popPath_dirExists:
+ POP EAX
+@@dirExists:
+ CALL DirectoryExists
+ MOV BL, AL
+ TEST BH, BH
+ JZ @@exit
+ CALL SetErrorMode
+@@exit: XCHG EAX, EBX
+ POP EBX
+end;
+
+function GetStartDir : AnsiString;
+asm
+ PUSH EBX
+ MOV EBX, EAX
+
+ XOR EAX, EAX
+ MOV AH, 2
+ SUB ESP, EAX
+ MOV EDX, ESP
+ PUSH EAX
+ PUSH EDX
+ PUSH 0
+ CALL GetModuleFileName // in KOL_ANSI
+
+ LEA EDX, [ESP + EAX]
+@@1: DEC EDX
+ CMP byte ptr [EDX], '\'
+ JNZ @@1
+
+ INC EDX
+ MOV byte ptr [EDX], 0
+
+ MOV EAX, EBX
+ MOV EDX, ESP
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX
+ {$ENDIF}
+ CALL System.@LStrFromPChar // AnsiSafe!
+
+ ADD ESP, 200h
+ POP EBX
+end;
+
+function IncludeTrailingChar( const S: KOLString; C: KOLChar ): KOLString;
+asm
+ push edx
+ push ecx
+ xchg ecx, eax
+ xchg edx, ecx
+ call System.@LStrAsg
+ pop eax
+ pop edx
+ mov ecx, [eax]
+ jecxz @@1
+ add ecx, [ecx-4]
+ dec ecx
+ cmp byte ptr [ecx], dl
+ jz @@exit
+@@1:
+ push eax
+ push 0
+ mov eax, esp
+ {$IFDEF _D2009orHigher}
+ //push ecx
+ xor ecx, ecx
+ {$ENDIF}
+ call System.@LStrFromChar
+ {$IFDEF _D2009orHigher}
+ //pop ecx
+ {$ENDIF}
+ mov edx, [esp]
+ mov eax, [esp+4]
+ call System.@LStrCat
+ call RemoveStr
+ pop eax
+@@exit:
+end;
+
+const
+ DirDelimiters: PAnsiChar = ':\/';
+function ExtractFileName( const Path : AnsiString ) : AnsiString;
+asm
+ PUSH EDX
+ PUSH EAX
+ MOV EDX, [DirDelimiters]
+ CALL __DelimiterLast
+ POP EDX
+ CMP byte ptr [EAX], 0
+ JZ @@1
+ XCHG EDX, EAX
+ INC EDX
+@@1: POP EAX
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX
+ {$ENDIF}
+ CALL System.@LStrFromPChar // Safe!
+end;
+
+function ExtractFileNameWOext( const Path : KOLString ) : KOLString;
+asm
+ push ebx
+
+ push edx
+ push eax
+ call ExtractFileName
+ pop edx // Path - íå íóæåí áîëüøå
+ mov eax, [esp] // eax = Result = ExtractFileName(Path)
+ mov eax, [eax]
+ push 0
+ mov edx, esp
+ call ExtractFileExt
+ mov eax, [esp]
+ call System.@LStrLen
+ xchg ebx, eax // ebx = Length(ExtractFileExt(Result))
+ call RemoveStr // ExtractFileExt - áîëüøå íå íóæåí
+ mov eax, [esp]
+ mov eax, [eax]
+ call System.@LStrLen // eax = Length(Result)
+ sub eax, ebx
+ xchg ecx, eax
+ xor edx, edx
+ inc edx
+ mov eax, [esp]
+ mov eax, [eax]
+ call System.@LStrCopy
+
+ pop ebx
+end;
+
+const
+ ExtDelimeters: PAnsiChar = '.';
+
+function ExtractFileExt( const Path : KOLString ) : KOLString;
+asm
+ PUSH EDX
+ MOV EDX, [ExtDelimeters]
+ CALL EAX2PChar
+ CALL __DelimiterLast
+@@1: XCHG EDX, EAX
+ POP EAX
+ {$IFDEF _D2009orHigher}
+ PUSH ECX
+ XOR ECX, ECX
+ {$ENDIF}
+ CALL System.@LStrFromPChar
+ {$IFDEF _D2009orHigher}
+ POP ECX // this routine hasn't touch ECX
+ {$ENDIF}
+end;
+
+function ReplaceExt( const Path, NewExt: KOLString ): KOLString;
+asm
+ push ecx // result
+ push edx // NewExt
+ push eax // Path
+
+ push 0
+ mov edx, esp
+ call ExtractFilePath
+ pop eax
+ xchg [esp], eax // eax=Path, Path in stack replaced with ExtractFilePath(Path)
+
+ push 0
+ mov edx, esp
+ call ExtractFileNameWOext
+ // now stack conatins: result,NewExt,ExtractFilePath(Path),ExtractFileNameWOext(Path)<-ESP
+
+ mov eax, [esp+12]
+ mov edx, esp
+ push dword ptr [edx+4] // ExtractFilePath(Path)
+ push dword ptr [edx] // ExtractFileNameWOext(Path)
+ push dword ptr [edx+8] // NewExt
+ mov edx, 3
+ call System.@LStrCatN
+ call RemoveStr
+ call RemoveStr
+ pop ecx
+ pop ecx
+end;
+
+function GetSystemDir: KOLString;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ SUB ESP, MAX_PATH
+ MOV EAX, ESP
+ PUSH MAX_PATH
+ PUSH EAX
+ CALL GetSystemDirectory
+ MOV EAX, EBX
+ MOV EDX, ESP
+ CALL System.@LStrFromPChar
+ MOV EDX, EBX
+ MOV EAX, [EDX]
+ CALL IncludeTrailingPathDelimiter
+ ADD ESP, MAX_PATH
+ POP EBX
+end;
+
+function GetWindowsDir : KOLString;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ SUB ESP, MAX_PATH
+ MOV EAX, ESP
+ PUSH MAX_PATH
+ PUSH EAX
+ CALL GetWindowsDirectory
+ MOV EAX, EBX
+ MOV EDX, ESP
+ CALL System.@LStrFromPChar
+ MOV EDX, EBX
+ MOV EAX, [EDX]
+ CALL IncludeTrailingPathDelimiter
+ ADD ESP, MAX_PATH
+ POP EBX
+end;
+
+function GetWorkDir : KOLString;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ SUB ESP, MAX_PATH
+ PUSH ESP
+ PUSH MAX_PATH
+ CALL GetCurrentDirectory
+ MOV EAX, EBX
+ MOV EDX, ESP
+ CALL System.@LStrFromPChar
+ MOV EDX, EBX
+ MOV EAX, [EDX]
+ CALL IncludeTrailingPathDelimiter
+ ADD ESP, MAX_PATH
+ POP EBX
+end;
+
+function GetTempDir : KOLString;
+asm
+ push eax
+ sub esp, 264
+ push esp
+ push 261
+ call GetTempPath
+ mov edx, esp
+ mov eax, [esp+264]
+ {$IFDEF _D2009orHigher}
+ xor ecx, ecx
+ {$ENDIF}
+ call System.@LStrFromPChar
+ add esp, 264
+ pop edx
+ mov eax, [edx]
+ call IncludeTrailingPathDelimiter
+end;
+
+function CreateTempFile( const DirPath, Prefix: KOLString ): KOLString;
+asm
+ push ecx
+ call EAX2PCHAR
+ call EDX2PCHAR
+ sub esp, 264
+ push esp
+ push 0
+ push edx
+ push eax
+ call GetTempFileName
+ mov eax, [esp+264]
+ mov edx, esp
+ {$IFDEF _D2009orHigher}
+ xor ecx, ecx // ecx is argument
+ {$ENDIF}
+ call System.@LStrFromPChar
+ add esp, 268
+end;
+
+function FindFilter( const Filter: AnsiString): AnsiString;
+asm
+ XCHG EAX, EDX
+ PUSH EAX
+ CALL System.@LStrAsg
+ POP EAX
+ CMP dword ptr [EAX], 0
+ JNE @@exit
+ LEA EDX, @@mask_all
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX
+ {$ENDIF}
+ JE System.@LStrFromPChar
+@@mask_all: DB '*.*',0
+@@exit:
+end;
+
+procedure TDirList.ScanDirectoryEx(const DirPath, Filters: AnsiString;
+ Attr: DWord);
+asm
+ PUSH EBX
+ MOV EBX, EAX
+
+ PUSHAD
+ LEA EAX, [EBX].fFilters
+ CALL Free_And_Nil
+
+ CALL NewStrList
+ MOV [EBX].fFilters, EAX
+ POPAD
+
+ PUSHAD
+ PUSH 0
+ MOV EAX, ESP
+ MOV EDX, ECX
+ CALL System.@LStrLAsg
+@@1: MOV ECX, [ESP]
+ JECXZ @@2
+ MOV EAX, ESP
+ MOV EDX, offset[@@semicolon]
+ PUSH 0
+ MOV ECX, ESP
+ CALL Parse
+ MOV EAX, [ESP]
+ MOV EDX, ESP
+ CALL Trim
+ POP EDX
+ PUSH EDX
+ TEST EDX, EDX
+ JZ @@filt_added
+ MOV EAX, [EBX].fFilters
+ CALL TStrList.Add
+@@filt_added:
+ CALL RemoveStr
+ JMP @@1
+
+ // ';' string literal
+ {$IFDEF _D2009orHigher}
+ DW 0, 1
+ {$ENDIF}
+ DD -1, 1
+@@semicolon:
+ DB ';',0
+
+@@2: POP ECX
+ POPAD
+ XOR ECX, ECX
+ PUSH [Attr]
+ CALL ScanDirectory
+ POP EBX
+@@exit:
+end;
+
+procedure _FillStrList; // Ýòà ÷àñòü êîäà îáùàÿ äëÿ äâóõ ñëåäóþùèõ ïðîöåäóð
+asm
+///////////////////////////////
+ OR EAX,0
+ JE @@EXIT //ERROR
+// LEA EAX,[EAX-IniBufferSize]
+// JE @@EXIT
+// âîçìîæíà íåõâàòêà Áóôåðà... â ïðèíöèïå íå îøèáêà :)
+// âîçâðàùàåì ÷òî âëåçëî...
+@@LOOP:
+ LEA EAX,[ESI+4]
+ CALL StrLen
+ MOV [ESI],EAX
+ LEA EDX,[ESI+4]
+ INC EAX
+ ADD ESI,EAX
+
+ MOV EAX,EDI
+
+ CALL TStrList.ADD
+
+ CMP byte ptr [ESI+4],0
+ JNE @@LOOP
+
+@@EXIT:
+ POP EAX
+ CALL System.@FreeMem
+
+
+ POP ECX
+ POP EBX
+ POP EDI
+ POP ESI
+end;
+
+procedure TIniFile.GetSectionNames(Names: PStrList);
+asm
+ PUSH ESI
+ PUSH EDI
+ PUSH EBX
+ PUSH ECX
+
+ MOV EBX,EAX
+ MOV EAX, IniBufferStrSize
+ MOV EDI,EDX
+
+ CALL System.@GetMem
+ MOV ESI,EAX
+ PUSH EAX
+
+ PUSH [EBX].fFileName
+ MOV EAX,IniBufferSize
+ PUSH EAX
+
+ LEA EAX,[ESI+4]
+ PUSH EAX
+
+ CALL GetPrivateProfileSectionNames
+ JMP _FillStrList
+end;
+
+procedure TIniFile.SectionData(Names: PStrList);
+asm
+ PUSH ESI
+ PUSH EDI
+ PUSH EBX
+ PUSH ECX
+
+ MOV EBX,EAX
+ MOV EAX, IniBufferStrSize
+ MOV EDI,EDX
+
+ CALL System.@GetMem
+ MOV ESI,EAX
+ PUSH EAX
+
+ OR [EBX].fMode,0
+ JNE @@DOWrite
+
+ PUSH [EBX].fFileName
+ MOV EAX,IniBufferSize
+ PUSH EAX
+
+ LEA EAX,[ESI+4]
+ PUSH EAX
+ PUSH [EBX].fSection
+
+ CALL GetPrivateProfileSection
+ JMP _FillStrList
+
+@@DOWrite:
+
+ PUSH EBX
+ PUSH ESI
+ PUSH EDX
+ PUSH EBP
+
+ MOV EDX,0
+ MOV EBP,[EDI].TStrList.fCount
+ MOV EBX,IniBufferSize-2 // îñòàâèì ìåñòî äëÿ #0#0
+
+{ECM+++>} OR EBP,EBP // otherwise GetPChars when StrList.Count = 0 crashed
+
+@@LOOP:
+ JE @@ENDLOOP
+
+ OR EBX,EBX
+ JE @@ENDLOOP
+
+ PUSH EDX
+ MOV EAX,EDI
+ CALL TStrList.GetPChars
+
+ PUSH EAX
+ CALL StrLen
+ POP EAX
+
+ XOR ECX,-1
+ MOV EDX,ESI
+
+ SUB EBX,ECX
+ JA @@L1
+ ADD ECX,EBX
+ XOR EBX,EBX
+@@L1:
+
+ ADD ESI,ECX
+
+ CALL MOVE
+@@L2:
+ POP EDX
+ INC EDX
+ DEC EBP
+ JMP @@LOOP
+@@ENDLOOP:
+ MOV WORD PTR [ESI],0
+
+ POP EBP
+ POP EDX
+ POP ESI
+ POP EBX
+ MOV EAX,EBX
+ CALL ClearSection
+
+ PUSH [EBX].fFileName
+ PUSH ESI
+ PUSH [EBX].fSection
+
+ CALL WritePrivateProfileSection
+
+ POP EAX
+ CALL System.@FreeMem
+
+ POP ECX
+ POP EBX
+ POP EDI
+ POP ESI
+
+end;
+
+function _NewControl( AParent: PControl; ControlClassName: PKOLChar;
+ Style: DWORD; Ctl3D: Boolean;
+ Actions: TCommandActionsParam ): PControl;
+const szActions = sizeof(TCommandActions);
+asm
+ PUSH EBX
+ PUSH EAX // push AParent
+ PUSH ECX // push Style
+ MOVZX ECX, [Ctl3D]
+ PUSH [Actions]
+ CALL _NewWindowed
+ XCHG EBX, EAX
+ {$IFDEF USE_FLAGS}
+ OR [EBX].TControl.fFlagsG3, (1 shl G3_IsControl)
+ {$ELSE}
+ INC [EBX].TControl.fIsControl
+ {$ENDIF}
+ POP EDX // pop Style
+ OR EDX, WS_CLIPSIBLINGS or WS_CLIPCHILDREN
+ //INC [EBX].TControl.fVerticalAlign
+ MOV byte ptr [EBX].TControl.fLookTabKeys, $0F
+ TEST [EBX].TControl.fCtl3D_child, 1
+ JZ @@noCtl3D
+ AND EDX, not WS_BORDER
+ OR byte ptr [EBX].TControl.fExStyle + 1, WS_EX_CLIENTEDGE shr 8
+@@noCtl3D:
+ MOV [EBX].TControl.fStyle, EDX
+ {$IFDEF USE_FLAGS}
+ {$ELSE}
+ TEST EDX, WS_VISIBLE
+ SETNZ AL
+ MOV [EBX].TControl.fVisible, AL
+ TEST EDX, WS_TABSTOP
+ SETNZ AL
+ MOV [EBX].TControl.fTabstop, AL
+ {$ENDIF USE_FLAGS}
+ POP ECX // pop AParent
+ JECXZ @@noParent
+
+ PUSH ESI
+ PUSH EDI
+ PUSH ECX
+ LEA ESI, [ECX].TControl.fMargin
+ LEA EDI, [EBX].TControl.fBoundsRect
+ LODSB
+ MOVSX EAX, AL
+ {$IFNDEF SMALLEST_CODE}
+ PUSH EAX
+ MOVSX ECX, byte ptr [ESI+2]
+ ADD EAX, ECX // AParent.fClientLeft
+ {$ENDIF}
+ STOSD // fBoundsRect.Left
+ {$IFNDEF SMALLEST_CODE}
+ POP EAX
+ PUSH EAX
+ MOVSX ECX, byte ptr [ESI+0]
+ ADD EAX, ECX // AParent.fClientTop
+ {$ENDIF}
+ STOSD // fBoundsRect.Top
+ {$IFNDEF SMALLEST_CODE}
+ XCHG EDX, EAX
+ POP EAX
+ {$ENDIF}
+ ADD EAX, 64
+ STOSD // fBoundsRect.Right
+ {$IFNDEF SMALLEST_CODE}
+ XCHG EAX, EDX
+ ADD EAX, 64
+ {$ENDIF}
+ STOSD // fBoundsRect.Bottom}
+ POP ECX
+ MOV EAX, [ECX].TControl.fCursor
+ STOSD
+ POP EDI
+ POP ESI
+
+ XCHG EAX, ECX
+ CALL TControl.ParentForm
+ XCHG ECX, EAX
+ JECXZ @@noParentForm
+ INC [ECX].TControl.fTabOrder
+ MOV DX, WORD PTR [ECX].TControl.fTabOrder
+ MOV WORD PTR [EBX].TControl.fTabOrder, DX
+ TEST [EBX].TControl.fStyle, WS_TABSTOP
+ JZ @@CurrentControl_set
+ CMP [ECX].TControl.DF.fCurrentControl, 0
+ JNZ @@CurrentControl_set
+ MOV [ECX].TControl.DF.fCurrentControl, EBX
+@@CurrentControl_set:
+@@noParentForm:
+@@noParent:
+ MOVZX EDX, [CtlIdCount]
+ INC [CtlIdCount]
+ MOV [EBX].TControl.fMenu, EDX
+ MOV EDX, offset[WndProcCtrl]
+ MOV EAX, EBX
+ CALL TControl.AttachProc
+ XCHG EAX, EBX
+ POP EBX
+ {$IFDEF DEBUG_ALTSPC}
+ PUSH EAX
+ CALL DumpWindowed
+ POP EAX
+ {$ENDIF}
+end;
+
+const StaticClass: Array[0..6] of AnsiChar=('S','T','A','T','I','C',#0);
+function NewLabel( AParent: PControl; const Caption: KOLString ): PControl;
+asm
+ PUSH EDX
+
+ PUSH 0
+ {$IFDEF PACK_COMMANDACTIONS}
+ PUSH [LabelActions_Packed]
+ {$ELSE}
+ PUSH offset[LabelActions]
+ {$ENDIF}
+ MOV ECX, WS_VISIBLE or WS_CHILD or SS_LEFTNOWORDWRAP or SS_NOPREFIX or SS_NOTIFY
+ MOV EDX, offset[StaticClass]
+ CALL _NewControl
+ MOV word ptr [EAX].TControl.aAutoSzX, $101
+ {$IFDEF USE_FLAGS}
+ OR [EAX].TControl.fFlagsG1, (1 shl G1_SizeRedraw) or (1 shl G1_IsStaticControl)
+ {$ELSE}
+ INC [EAX].TControl.fIsStaticControl
+ INC [EAX].TControl.fSizeRedraw
+ {$ENDIF}
+ MOV EDX, [EAX].TControl.fBoundsRect.Top
+ ADD EDX, 22
+ MOV [EAX].TControl.fBoundsRect.Bottom, EDX
+ POP EDX
+ PUSH EAX
+ CALL TControl.SetCaption
+ POP EAX
+
+{$IFDEF GRAPHCTL_XPSTYLES}
+ PUSH EAX
+ MOV EDX, offset[XP_Themes_For_Label]
+ CALL Attach_WM_THEMECHANGED
+ POP EAX
+{$ENDIF}
+end;
+
+function NewGroupbox( AParent: PControl; const Caption: KOLString ): PControl;
+asm
+ PUSH EDX
+ PUSH 0
+ {$IFDEF PACK_COMMANDACTIONS}
+ PUSH [ButtonActions_Packed]
+ {$ELSE}
+ PUSH offset[ButtonActions]
+ {$ENDIF}
+ MOV EDX, offset[ButtonClass]
+ MOV ECX, WS_VISIBLE or WS_CHILD or BS_GROUPBOX or WS_CLIPCHILDREN or WS_CLIPSIBLINGS
+ CALL _NewControl
+ OR [EAX].TControl.fExStyle, WS_EX_CONTROLPARENT
+ MOV EDX, [EAX].TControl.fBoundsRect.Left
+ ADD EDX, 100
+ MOV [EAX].TControl.fBoundsRect.Right, EDX
+ MOV EDX, [EAX].TControl.fBoundsRect.Top
+ ADD EDX, 100
+ MOV [EAX].TControl.fBoundsRect.Bottom, EDX
+ MOV byte ptr [EAX].TControl.fClientTop, 22
+ XOR EDX, EDX
+ {$IFDEF USE_FLAGS}
+ AND [EAX].TControl.fStyle.f2_Style, not(1 shl F2_Tabstop)
+ {$ELSE}
+ MOV [EAX].TControl.fTabstop, DL
+ {$ENDIF USE_FLAGS}
+ MOV DL, 2
+ ADD [EAX].TControl.fClientBottom, DL
+ ADD [EAX].TControl.fClientLeft, DL
+ ADD [EAX].TControl.fClientRight, DL
+ POP EDX
+ PUSH EAX
+ CALL TControl.SetCaption
+ POP EAX
+ PUSH EAX
+ {$IFDEF USE_FLAGS}
+ OR [EAX].TControl.fFlagsG5, 1 shl G5_IsGroupbox
+ {$ELSE}
+ INC [EAX].TControl.fIsGroupBox
+ {$ENDIF}
+ MOV EDX, offset[WndProcDoEraseBkgnd]
+ CALL TControl.AttachProc
+ POP EAX
+
+{$IFDEF GRAPHCTL_XPSTYLES}
+ PUSH EAX
+ MOV EDX, offset[XP_Themes_For_GroupBox]
+ CALL Attach_WM_THEMECHANGED
+ POP EAX
+{$ENDIF}
+end;
+
+function NewPanel( AParent: PControl; EdgeStyle: TEdgeStyle ): PControl;
+const CreateStyle = WS_VISIBLE or WS_CHILD or SS_LEFTNOWORDWRAP or
+ SS_NOPREFIX or SS_NOTIFY;
+asm
+{$IFDEF GRAPHCTL_XPSTYLES}
+ MOVZX EDX, EdgeStyle
+ PUSH EDX
+{$ENDIF}
+
+ PUSH EDX
+ MOV EDX, offset[StaticClass]
+ MOV ECX, CreateStyle
+ PUSH 0
+ {$IFDEF PACK_COMMANDACTIONS}
+ PUSH [LabelActions_Packed]
+ {$ELSE}
+ PUSH offset[LabelActions]
+ {$ENDIF}
+ CALL _NewControl
+ //INC byte ptr [EAX].TControl.aAutoSzX
+ //INC byte ptr [EAX].TControl.aAutoSzY
+ MOV word ptr [EAX].TControl.aAutoSzX, $101
+ ADD [EAX].TControl.fBoundsRect.Right, 100-64
+ ADD [EAX].TControl.fBoundsRect.Bottom, 100-64
+ OR byte ptr [EAX].TControl.fExStyle+2, 1
+ POP ECX
+ CMP CL, 1
+ JG @@exit
+ JE @@sunken
+ OR byte ptr [EAX].TControl.fStyle+2, $40
+{$IFDEF GRAPHCTL_XPSTYLES}
+ JMP @@visual
+{$ELSE}
+ RET
+{$ENDIF}
+@@sunken:
+ OR byte ptr [EAX].TControl.fStyle+1, $10
+@@exit:
+
+{$IFDEF GRAPHCTL_XPSTYLES}
+@@visual:
+ CMP AppTheming, TRUE
+ JNE @@es_none_
+ CMP CL, 1
+ JG @@es_none_
+ JE @@not_sunken
+ AND byte ptr [EAX].TControl.fStyle+2, $00
+ JNE @@es_none_
+@@not_sunken:
+ AND byte ptr [EAX].TControl.fStyle+1, $00
+@@es_none_:
+ POP EDX
+ PUSH EAX
+ CALL TControl.SetEdgeStyle
+ POP EAX
+ PUSH EAX
+ MOV EDX, offset[XP_Themes_For_Panel]
+ CALL Attach_WM_THEMECHANGED
+ POP EAX
+{$ENDIF}
+end;
+
+const ListBoxClass : Array[ 0..7 ] of AnsiChar = ( 'L','I','S','T','B','O','X',#0 );
+function NewListbox( AParent: PControl; Options: TListOptions ): PControl;
+asm
+ PUSH EAX
+ PUSH EDX
+ MOV EAX, ESP
+ MOV EDX, offset[ListFlags]
+ XOR ECX, ECX
+ MOV CL, 11
+ CALL MakeFlags
+ POP EDX
+ OR EAX, WS_VISIBLE or WS_CHILD or WS_TABSTOP or WS_BORDER or WS_VSCROLL or LBS_NOTIFY
+ XCHG ECX, EAX
+ POP EAX
+ PUSH 1
+ {$IFDEF PACK_COMMANDACTIONS}
+ PUSH [ListActions_Packed]
+ {$ELSE}
+ PUSH offset[ListActions]
+ {$ENDIF}
+ MOV EDX, offset[ListBoxClass]
+ CALL _NewControl
+ {$IFDEF PACK_COMMANDACTIONS}
+ MOV EDX, [EAX].TControl.fCommandActions
+ MOV [EDX].TCommandActionsObj.aClear, offset[ClearListbox]
+ {$ENDIF}
+ ADD [EAX].TControl.fBoundsRect.Right, 100
+ ADD [EAX].TControl.fBoundsRect.Bottom, 200-64
+ MOV [EAX].TControl.fColor, clWindow
+ MOV [EAX].TControl.fLookTabKeys, 3
+end;
+
+procedure CreateComboboxWnd( Combo: PControl );
+//const PrevProcStr: PAnsiChar = 'PREV_PROC'; //************ Remarked By M.Gerasimov
+asm
+ PUSH EDI
+ PUSH EBX
+ XCHG EBX, EAX
+ PUSH GW_CHILD
+ PUSH [EBX].TControl.fHandle
+@@getwindow:
+ CALL GetWindow
+ TEST EAX, EAX
+ JZ @@fin
+ PUSH offset[WndFuncCombo]
+ PUSH GWL_WNDPROC
+ PUSH EAX
+ XCHG EDI, EAX
+ CALL SetWindowLong
+ PUSH EAX
+ PUSH offset [ID_PREVPROC] //
+ PUSH EDI
+ CALL SetProp
+@@2getnext:
+ PUSH GW_HWNDNEXT
+ PUSH EDI
+ JMP @@getwindow
+@@fin: POP EBX
+ POP EDI
+end;
+
+{$IFDEF WNDPROCTREEVIEW_OLDASMVERSION}
+function WndProcTreeView( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+asm //cmd //opd
+ CMP word ptr [EDX].TMsg.message, WM_NOTIFY
+ JNZ @@ret_false
+ PUSH EBX
+ XCHG EBX, EAX
+ MOV EDX, [EDX].TMsg.lParam
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EBX].TControl.EV
+ LEA EAX, [EAX].TEvents.fOnTVBeginDrag
+ {$ELSE}
+ LEA EAX, [EBX].TControl.EV.fOnTVBeginDrag
+ {$ENDIF}
+ CMP word ptr [EDX].TNMTreeView.hdr.code, NM_RCLICK
+ JNE @@chk_TVN_BEGINDRAG
+ PUSH ECX
+ PUSH ECX
+ PUSH ESP
+ CALL GetCursorPos
+ MOV EAX, EBX
+ MOV EDX, ESP
+ MOV ECX, EDX
+ CALL TControl.Screen2Client
+ POP EAX
+ AND EAX, $FFFF
+ POP EDX
+ SHL EDX, 16
+ OR EAX, EDX
+ PUSH EAX
+ CALL GetShiftState
+ PUSH EAX
+ PUSH WM_RBUTTONUP
+ PUSH [EBX].TControl.fHandle
+ CALL PostMessage
+ JMP @@2fin_false1
+
+@@chk_TVN_BEGINDRAG:
+ {$IFDEF UNICODE_CTRLS}
+ CMP word ptr [EDX].TNMTreeView.hdr.code, TVN_BEGINDRAGW
+ JZ @@event_drag
+ CMP word ptr [EDX].TNMTreeView.hdr.code, TVN_BEGINRDRAGW
+ JZ @@event_drag
+ {$ENDIF UNICODE_CTRLS}
+ CMP word ptr [EDX].TNMTreeView.hdr.code, TVN_BEGINDRAG
+ JZ @@event_drag
+ CMP word ptr [EDX].TNMTreeView.hdr.code, TVN_BEGINRDRAG
+ JNZ @@chk_BEGINLABELEDIT
+@@event_drag:
+ MOV EDX, [EDX].TNMTreeView.itemNew.hItem
+@@event_call:
+ MOV ECX, [EAX].TMethod.Code
+ JECXZ @@2fin_false1
+ MOV EAX, [EAX].TMethod.Data
+ XCHG EBX, ECX
+ XCHG EDX, ECX
+ CALL EBX
+@@2fin_false1: JMP @@fin_false
+@@chk_BEGINLABELEDIT:
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EBX].TControl.EV
+ LEA EAX, [EAX].TEvents.FOnTVBeginEdit
+ {$ELSE}
+ LEA EAX, [EBX].TControl.EV.fOnTVBeginEdit
+ {$ENDIF}
+ {$IFDEF UNICODE_CTRLS}
+ CMP word ptr [EDX].TNMTreeView.hdr.code, TVN_BEGINLABELEDITW
+ JZ @@beginlabeledit
+ {$ENDIF UNICODE_CTRLS}
+ CMP word ptr [EDX].TNMTreeView.hdr.code, TVN_BEGINLABELEDIT
+ JNZ @@chk_ITEMEXPANDED //@@chk_DELETEITEM
+@@beginlabeledit:
+ {$IFDEF USE_FLAGS}
+ TEST [EBX].TControl.fFlagsG6, 1 shl G6_Dragging
+ {$ELSE}
+ CMP [EBX].TControl.fDragging, 0
+ {$ENDIF}
+ JZ @@allow_LABELEDIT
+ XOR EAX, EAX
+ INC EAX
+ MOV [ECX], EAX
+ JMP @@ret_true
+
+@@allow_LABELEDIT:
+ PUSH ECX // @Rslt
+
+ MOV ECX, [EAX].TMethod.Code
+ JECXZ @@2fin_false1
+ PUSH EBX
+ XCHG EBX, ECX
+ MOV EDX, [EDX].TTVDispInfo.item.hItem
+ XCHG EDX, ECX
+ MOV EAX, [EAX].TMethod.Data
+ CALL EBX
+ TEST AL, AL
+ SETZ AL // Rslt := not event result;
+ POP EBX
+ JMP @@ret_EAX
+
+@@call_EBX:
+ CALL EBX
+@@2fin_false:
+ JMP @@fin_false
+@@chk_ITEMEXPANDED:
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EBX].TControl.EV
+ LEA EAX, [EAX].TEvents.fOnTVExpanded
+ {$ELSE}
+ LEA EAX, [EBX].TControl.EV.fOnTVExpanded
+ {$ENDIF}
+ {$IFDEF UNICODE_CTRLS}
+ CMP word ptr [EDX].TNMTreeView.hdr.code, TVN_ITEMEXPANDEDW
+ JZ @@itemexpanded
+ {$ENDIF UNICODE_CTRLS}
+ CMP word ptr [EDX].TNMTreeView.hdr.code, TVN_ITEMEXPANDED
+ JNZ @@chk_SELCHANGING
+@@itemexpanded:
+ MOV ECX, [EAX].TMethod.Code
+ JECXZ @@2fin_false
+ CMP [EDX].TNMTreeView.action, TVE_EXPAND
+ PUSH ECX
+ SETZ CL
+ XCHG ECX, [ESP]
+ JMP @@event_drag
+@@chk_SELCHANGING:
+ CMP word ptr [EDX].TNMTreeView.hdr.code, TVN_SELCHANGING
+ JNE @@chk_ITEMEXPANDING
+ XCHG EAX, ECX
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV ECX, [EBX].TControl.EV
+ MOV ECX, [ECX].TEvents.fOnTVSelChanging.TMethod.Code
+ {$ELSE}
+ MOV ECX, [EBX].TControl.EV.fOnTVSelChanging.TMethod.Code
+ {$ENDIF}
+@@2fin_false2:
+ JECXZ @@2fin_false
+ PUSH EAX //@Rslt
+ PUSH [EDX].TNMTreeView.itemNew.hItem
+ XCHG ECX, EBX //EBX=OnTVSelChanging.Code ECX=Sender
+ XCHG ECX, EDX //EDX=Sender ECX=Msg
+ MOV ECX, [ECX].TNMTreeView.itemOld.hItem
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EDX].TControl.EV
+ MOV EAX, [EAX].TEvents.fOnTVSelChanging.TMethod.Data
+ {$ELSE}
+ MOV EAX, [EDX].TControl.EV.fOnTVSelChanging.TMethod.Data
+ {$ENDIF}
+ CALL EBX
+ XOR AL, 1
+ MOVZX EAX, AL
+ JMP @@ret_EAX
+
+@@chk_ITEMEXPANDING:
+ {$IFDEF UNICODE_CTRLS}
+ CMP word ptr [EDX].TNMTreeView.hdr.code, TVN_ITEMEXPANDINGW
+ JZ @@itemexpanding
+ {$ENDIF UNICODE_CTRLS}
+ CMP word ptr [EDX].TNMTreeView.hdr.code, TVN_ITEMEXPANDING
+ JNE @@chk_ENDLABELEDIT
+@@itemexpanding:
+ XCHG EAX, ECX
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV ECX, [EBX].TControl.EV
+ MOV ECX, [ECX].TEvents.fOnTVExpanding.TMethod.Code
+ {$ELSE}
+ MOV ECX, [EBX].TControl.EV.fOnTVExpanding.TMethod.Code
+ {$ENDIF}
+ JECXZ @@2fin_false2
+ PUSH EAX // @Rslt
+ CMP [EDX].TNMTreeView.action, TVE_EXPAND
+ PUSH ECX
+ SETZ CL
+ XCHG ECX, [ESP]
+ XCHG ECX, EBX //EBX=OnTVExpanding.Code ECX=Seneder
+ XCHG EDX, ECX //ECX=Msg EDX=Sender
+ MOV ECX, [ECX].TNMTreeView.itemNew.hItem //ECX=Item
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EDX].TControl.EV
+ MOV EAX, [EAX].TEvents.fOnTVExpanding.TMethod.Data
+ {$ELSE}
+ MOV EAX, [EDX].TControl.EV.fOnTVExpanding.TMethod.Data //EAX=object
+ {$ENDIF}
+@@111:
+ CALL EBX
+@@ret_EAX:
+ POP EDX //EDX=@Rslt
+ MOVZX EAX, AL
+ NEG EAX
+ MOV [EDX], EAX
+@@ret_true:
+ MOV AL, 1
+ POP EBX
+ RET
+@@chk_ENDLABELEDIT:
+ {$IFDEF UNICODE_CTRLS}
+ CMP word ptr [EDX].TNMTreeView.hdr.code, TVN_ENDLABELEDITW
+ JZ @@endlabeledit
+ {$ENDIF UNICODE_CTRLS}
+ CMP word ptr [EDX].TNMTreeView.hdr.code, TVN_ENDLABELEDIT
+ JNZ @@chk_SELCHANGED
+@@endlabeledit:
+ XCHG EAX, ECX
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV ECX, [EBX].TControl.EV
+ MOV ECX, [ECX].TEvents.fOnTVEndEdit.TMethod.Code
+ {$ELSE}
+ MOV ECX, [EBX].TControl.EV.fOnTVEndEdit.TMethod.Code
+ {$ENDIF}
+ JECXZ @@ret_1
+ PUSH EAX
+ PUSH EBX
+ PUSH 0
+
+ XCHG EDX, EBX
+ MOV EAX, [EBX].TTVDispInfo.item.pszText
+ PUSH EDX
+ PUSH ECX
+ XCHG EAX, EDX
+ {$IFDEF UNICODE_CTRLS}
+ CMP [EBX].TNMTreeView.hdr.code, TVN_ENDLABELEDITW
+ JNZ @@endlabeleditA
+ CALL TControl.TVGetItemTextW
+ JMP @@NewTxt_ready
+@@endlabeleditA:
+ {$ENDIF UNICODE_CTRLS}
+ TEST EDX, EDX
+ JNZ @@prepare_NewTxt
+ // NewTxt := [EDX].TControl.TVItemText[ hItem ]
+ LEA ECX, [ESP + 8]
+ MOV EDX, [EBX].TTVDispInfo.item.hItem
+ CALL TControl.TVGetItemText
+ JMP @@NewTxt_ready
+@@prepare_NewTxt:
+ LEA EAX, [ESP+8]
+ {$IFDEF _D2009orHigher}
+ PUSH ECX
+ XOR ECX, ECX
+ {$ENDIF}
+ CALL System.@LStrFromPChar
+ {$IFDEF _D2009orHigher}
+ POP ECX
+ {$ENDIF}
+@@NewTxt_ready:
+ POP ECX
+ POP EDX
+ POP EAX
+ PUSH EAX
+ PUSH EAX
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EDX].TControl.EV
+ MOV EAX, [EAX].TEvents.fOnTVEndEdit.TMethod.Data
+ {$ELSE}
+ MOV EAX, [EDX].TControl.EV.fOnTVEndEdit.TMethod.Data
+ {$ENDIF}
+ MOV EBX, [EBX].TTVDispInfo.item.hItem
+ XCHG ECX, EBX
+ CALL EBX
+ XCHG EBX, EAX
+ CALL RemoveStr
+ XCHG EAX, EBX
+ POP EBX
+ JMP @@ret_EAX
+@@ret_1:
+ INC ECX
+ MOV [EAX], ECX
+ JMP @@ret_true
+
+@@chk_SELCHANGED:
+ {$IFDEF UNICODE_CTRLS}
+ CMP word ptr [EDX].TNMTreeView.hdr.code, TVN_SELCHANGEDW
+ JZ @@selchanged
+ {$ENDIF UNICODE_CTRLS}
+ CMP word ptr [EDX].TNMTreeView.hdr.code, TVN_SELCHANGED
+ JNZ @@fin_false
+@@selchanged:
+ XCHG EAX, EBX
+ CALL TControl.DoSelChange
+
+@@fin_false:
+ POP EBX
+@@ret_false:
+ XOR EAX, EAX
+end;
+{$ELSE NEW VERSION OF WndProcTreeView}
+function WndProcTreeView( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+asm
+ PUSH ESI
+ PUSH EDI
+ MOV EDI, ECX // EDI -> Rslt
+ XOR ECX, ECX
+ CMP WORD PTR [EDX].TMsg.message, WM_NOTIFY
+ JNZ @@ret_false1
+ XCHG ESI, EAX
+ MOV EDX, [EDX].TMsg.lParam
+ CMP WORD PTR [EDX].TNMTreeView.hdr.code, NM_RCLICK
+ JNE @@chk_TVN_BEGINDRAG
+ PUSH ECX
+ PUSH ECX
+ PUSH ESP
+ CALL GetCursorPos
+ MOV EAX, ESI
+ MOV EDX, ESP
+ MOV ECX, EDX
+ CALL TControl.Screen2Client
+ POP EDX
+ POP EAX
+ SHLD EAX, EDX, 16
+ PUSH EAX
+ CALL GetShiftState
+ PUSH EAX
+ PUSH WM_RBUTTONUP
+ PUSH ESI
+ CALL TControl.PostMsg
+ JMP @@ret_false1
+@@prepareCallEvent:
+ STC
+ MOV EDX, ESI
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV ESI, [ESI].TControl.EV
+ LEA ECX, [ESI+ECX*8].TEvents.fOnTVBeginDrag
+ {$ELSE}
+ LEA ECX, [ESI+ECX*8].TControl.EV.fOnTVBeginDrag
+ {$ENDIF}
+ MOV EAX, [ECX].TMethod.Data
+ MOV ECX, [ECX].TMethod.Code
+ JECXZ @@noEvent
+ MOV ESI, ECX
+ AND EAX, EAX
+@@noEvent:
+ RET
+@@chk_TVN_BEGINDRAG: ///////////////////////////////////////////////////////////
+ CMP WORD PTR [EDX].TNMTreeView.hdr.code, TVN_BEGINDRAG
+ JE @@beginDrag
+ CMP WORD PTR [EDX].TNMTreeView.hdr.code, TVN_BEGINRDRAG
+ JNE @@chk_TVNBEGINLABELEDIT
+@@beginDrag:
+ PUSH [EDX].TNMTreeView.itemNew.hItem
+ CALL @@prepareCallEvent
+ POP ECX
+ JC @@ret_false1
+@@justEventCall:
+ CALL ESI
+@@RsltEAX_ResultFalse:
+ MOV [EDI], EAX
+ XOR EAX, EAX
+ POP EDI
+ POP ESI
+ RET
+@@chk_TVNBEGINLABELEDIT: ///////////////////////////////////////////////////////
+ INC ECX // -> FOnTVBeginEdit
+ CMP WORD PTR [EDX].TNMTreeView.hdr.code, TVN_BEGINLABELEDIT
+ JNE @@chk_ENDLABELEDIT
+ ///////////////////////////////////////////////////////////////////////
+ XOR EAX, EAX
+ INC EAX
+ {$IFDEF USE_FLAGS}
+ TEST [ESI].TControl.fFlagsG6, 1 shl G6_Dragging
+ {$ELSE}
+ CMP [ESI].TControl.fDragging, 0
+ {$ENDIF}
+ JNZ @@rsltEAX_ResultTrue
+ PUSH [EDX].TTVDispInfo.item.hItem
+ CALL @@prepareCallEvent
+ POP ECX
+ JC @@ret_false1
+ CALL ESI
+ XOR AL, 1 //+Dufa
+@@rsltEAX_ResultTrue:
+ MOV [EDI], AL //+VK
+@@ResultTrue:
+ MOV AL, 1
+ POP EDI
+ POP ESI
+ RET
+@@chk_ENDLABELEDIT:
+ INC ECX // -> fOnTVEndEdit
+ CMP WORD PTR [EDX].TNMTreeView.hdr.code, TVN_ENDLABELEDIT
+ JNE @@chk_ITEMEXPANDING
+ MOV EAX, [EDX].TTVDispInfo.item.pszText
+ TEST EAX, EAX
+ JZ @@ResultTrue
+ PUSH EAX
+ PUSH [EDX].TTVDispInfo.item.hItem
+ CALL @@prepareCallEvent
+ POP ECX
+ //JNC @@justEventCall ---//dufa
+ JC @@ret_false1 //dufa
+ CALL ESI //dufa
+ JMP @@rsltEAX_ResultTrue //dufa
+@@Rslt1_ResultTrue:
+ XOR EAX, EAX
+ INC EAX
+ JMP @@RsltEAX_ResultFalse
+@@chk_ITEMEXPANDING: ///////////////////////////////////////////////////////////
+ INC ECX // -> FOnTVExpanding
+ CMP WORD PTR [EDX].TNMTreeView.hdr.code, TVN_ITEMEXPANDING
+ JNE @@chk_ITEMEXPANDED
+@@expanding_expanded:
+ CMP [EDX].TNMTreeView.action, TVE_EXPAND
+ SETZ AL
+ PUSH EAX
+ PUSH [EDX].TNMTreeView.itemNew.hItem
+@@event3:
+ CALL @@prepareCallEvent
+ POP ECX
+ JNC @@justEventCall
+ POP EAX
+ JMP @@ret_false1
+@@chk_ITEMEXPANDED: ////////////////////////////////////////////////////////////
+ INC ECX // -> FOnTVExpanded
+ CMP [EDX].TNMTreeView.hdr.code, TVN_ITEMEXPANDED
+ JE @@expanding_expanded
+ ///////////////////////////////////////////////////////////////////////
+ INC ECX // -> FOnTVSelChanging
+ CMP [EDX].TNMTreeView.hdr.code, TVN_SELCHANGING
+ JNE @@chk_TVN_SELCHANGED
+ PUSH [EDX].TNMTreeView.itemNew.hItem
+ PUSH [EDX].TNMTreeView.itemOld.hItem
+ JMP @@event3
+@@chk_TVN_SELCHANGED:
+ CMP [EDX].TNMTreeView.hdr.code, TVN_SELCHANGED
+ JNE @@ret_false1
+ XCHG EAX, ESI
+ CALL TControl.DoSelChange
+@@ret_false1:
+ XOR EAX, EAX
+ POP EDI
+ POP ESI
+end;
+{$ENDIF}
+
+function NewTabControl( AParent: PControl; const Tabs: array of PKOLChar; Options: TTabControlOptions;
+ ImgList: PImageList; ImgList1stIdx: Integer ): PControl;
+const lenf=high(TabControlFlags); //+++
+asm //cmd //opd
+ PUSH EBX
+ PUSH ESI
+ PUSH EDI
+ XCHG EBX, EAX
+ PUSH EDX
+ PUSH ECX
+ LEA EAX, [Options]
+ MOV EDX, offset[TabControlFlags]
+ XOR ECX, ECX
+ MOV CL, lenf
+ CALL MakeFlags
+ TEST byte ptr [Options], 4
+ JZ @@0
+ OR EAX, WS_TABSTOP or TCS_FOCUSONBUTTONDOWN
+@@0: OR EAX, WS_CHILD or WS_CLIPSIBLINGS or WS_CLIPCHILDREN or WS_VISIBLE
+ XCHG ECX, EAX
+ XCHG EAX, EBX
+ MOV EDX, offset[WC_TABCONTROL]
+ PUSH 1
+ {$IFDEF PACK_COMMANDACTIONS}
+ PUSH [TabControlActions_Packed]
+ {$ELSE}
+ PUSH offset[TabControlActions]
+ {$ENDIF}
+ CALL _NewCommonControl
+ MOV EBX, EAX
+ TEST [Options], 2 shl (tcoBorder - 1)
+ JNZ @@borderfixed
+ AND [EBX].TControl.fExStyle, not WS_EX_CLIENTEDGE
+@@borderfixed:
+ MOV EDX, offset[WndProcTabControl]
+ CALL TControl.AttachProc
+ ADD [EBX].TControl.fBoundsRect.Right, 100-64
+ ADD [EBX].TControl.fBoundsRect.Bottom, 100-64
+ MOV ECX, [ImgList]
+ JECXZ @@2
+ XCHG EAX, ECX
+ CALL TImageList.GetHandle
+ PUSH EAX
+ PUSH 0
+ PUSH TCM_SETIMAGELIST
+ PUSH EBX
+ CALL TControl.Perform
+@@2:
+ POP EDI // EDI = High(Tabs)
+ POP ESI // ESI = Tabs
+ XOR EDX, EDX // EDX := 0 (=I)
+ MOV EAX, [ImgList1stIdx] //(=II)
+@@loop:
+ CMP EDX, EDI
+ JG @@e_loop
+ PUSH EAX
+ PUSH EDX
+ PUSH EAX
+ LODSD
+ XCHG ECX, EAX
+ MOV EAX, EBX
+ CALL TControl.TC_Insert
+ POP EDX
+ POP EAX
+ INC EAX
+ INC EDX
+ JMP @@loop
+@@e_loop:
+ MOV byte ptr [EBX].TControl.fLookTabKeys, 1
+ XCHG EAX, EBX
+ POP EDI
+ POP ESI
+ POP EBX
+end;
+
+function NewToolbar( AParent: PControl; Align: TControlAlign; Options: TToolbarOptions;
+ Bitmap: HBitmap; const Buttons: array of PKOLChar;
+ const BtnImgIdxArray: array of Integer ) : PControl;
+const szTBButton = Sizeof( TTBButton );
+ Option3DBorder = 1 shl Ord( tbo3DBorder );
+asm //cmd //opd
+ PUSH EDI
+ MOVZX EDX, DL
+ PUSH EDX // Align
+ PUSH EAX // AParent
+
+ XOR EAX, EAX
+ TEST CL, Option3DBorder
+ SETNZ AL
+ PUSH EAX
+
+ PUSH ECX // Options
+
+ MOV AL, ICC_BAR_CLASSES
+ CALL DoInitCommonControls
+
+ MOV EAX, ESP
+ MOV EDX, offset[ToolbarOptions]
+ XOR ECX, ECX
+ MOV CL, 6
+ CALL MakeFlags
+ POP EDX
+
+ {$IFDEF COMMANDACTIONS_OBJ}
+ PUSH TOOLBAR_ACTIONS
+ {$ELSE}
+ PUSH 0 //: actions : = nil
+ {$ENDIF}
+ XCHG ECX, EAX // ECX = MakeFlags(...)
+ MOV EDI, ECX
+ MOV EAX, [ESP+8] // EAX = AParent
+ MOV EDX, [ESP+12] // EDX = Align
+ OR ECX, [EDX*4+offset ToolbarAligns]
+ OR ECX, WS_CHILD or WS_VISIBLE or TBSTYLE_TOOLTIPS
+ MOV EDX, offset[ TOOLBARCLASSNAME ]
+ CALL _NewCommonControl
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV EDX, [EAX].TControl.fCommandActions
+ MOV [EDX].TCommandActionsObj.aClear, offset[ClearToolbar]
+ MOV [EDX].TCommandActionsObj.aGetCount, TB_BUTTONCOUNT
+ {$ELSE}
+ MOV [EAX].TControl.fCommandActions.aClear, offset[ClearToolbar]
+ MOV [EAX].TControl.fCommandActions.aGetCount, TB_BUTTONCOUNT
+ {$ENDIF}
+ {$IFDEF USE_FLAGS}
+ OR [EAX].TControl.fFlagsG5, 1 shl G5_IsButton
+ {$ELSE}
+ INC [EAX].TControl.fIsButton
+ {$ENDIF}
+ POP EDX // pop AParent
+ POP EDX // EDX = Align
+ PUSH EDX
+ TEST EDX, EDX
+ JE @@zero_bounds
+ ADD [EAX].TControl.fBoundsRect.Bottom, 26-64
+ ADD [EAX].TControl.fBoundsRect.Right, 1000-64
+ JMP @@bounds_ready
+@@zero_bounds:
+ MOV [EAX].TControl.fBoundsRect.Left, EDX
+ MOV [EAX].TControl.fBoundsRect.Top, EDX
+ MOV [EAX].TControl.fBoundsRect.Right, EDX
+ MOV [EAX].TControl.fBoundsRect.Bottom, EDX
+@@bounds_ready:
+ PUSH EBX
+ PUSH ESI
+ XCHG EBX, EAX
+ MOV ESI, offset[TControl.Perform]
+ PUSH 0
+ PUSH 0
+ PUSH TB_GETEXTENDEDSTYLE
+ PUSH EBX
+ CALL ESI
+ OR EAX, TBSTYLE_EX_DRAWDDARROWS
+ PUSH EAX
+ PUSH 0
+ PUSH TB_SETEXTENDEDSTYLE
+ PUSH EBX
+ CALL ESI
+ MOV EDX, offset[WndProcToolbarCtrl]
+ MOV EAX, EBX
+ CALL TControl.AttachProc
+ MOV EDX, offset[WndProcDoEraseBkgnd]
+ MOV EAX, EBX
+ CALL TControl.AttachProc
+ PUSH 0
+ PUSH szTBButton
+ PUSH TB_BUTTONSTRUCTSIZE
+ PUSH EBX
+ CALL ESI
+ PUSH 0
+ MOVSX EAX, [EBX].TControl.fMargin
+ PUSH EAX
+ PUSH TB_SETINDENT
+ PUSH EBX
+ CALL ESI
+ MOV EAX, [ESP+8] // Align
+ {$IFDEF PARANOIA} DB $2C, 1 {$ELSE} SUB AL, 1 {$ENDIF}
+ JL @@bounds_correct
+ JE @@corr_right
+ {$IFDEF PARANOIA} DB $2C, 2 {$ELSE} SUB AL, 2 {$ENDIF}
+ JNE @@corr_bottom
+ @@corr_right:
+ MOV EDX, [EBX].TControl.fBoundsRect.Left
+ ADD EDX, 24
+ MOV [EBX].TControl.fBoundsRect.Right, EDX
+ JMP @@bounds_correct
+ @@corr_bottom:
+ MOV EDX, [EBX].TControl.fBoundsRect.Top
+ ADD EDX, 22
+ MOV [EBX].TControl.fBoundsrect.Bottom, EDX
+ @@bounds_correct:
+ {$IFnDEF TBBUTTONS_DFLT_NOAUTOSIZE}
+ MOV byte ptr [EBX].TControl.DF.fDefaultTBBtnStyle, TBSTYLE_AUTOSIZE
+ {$ENDIF}
+ MOV EDX, [Bitmap]
+ TEST EDX, EDX
+ JZ @@bitmap_added
+ MOV EAX, EBX
+ CALL TControl.TBAddBitmap
+ @@bitmap_added:
+
+ PUSH dword ptr [BtnImgIdxArray]
+ PUSH dword ptr [BtnImgIdxArray-4]
+ MOV ECX, [Buttons-4]
+ MOV EDX, [Buttons]
+ MOV EAX, EBX
+ CALL TControl.TBAddButtons
+
+ PUSH 0
+ PUSH 0
+ PUSH WM_SIZE
+ PUSH EBX
+ CALL ESI
+ // ---
+ {+|ecm|}
+ // ---
+ MOV EDX,EDI
+ OR EDX,[EBX].TControl.FStyle
+ MOV EAX,EBX
+ CALL TControl.SetStyle
+ // ---
+ {/+|ecm|}
+ // ---
+ XCHG EAX, EBX
+ POP ESI
+ POP EBX
+ POP EDX
+ POP EDI
+end;
+
+function WndProcDateTimePickerNotify( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+type
+ TStrStr = record
+ param_Date: TDateTime;
+ param_PtrToAccept: PInteger;
+ Accept: Integer;
+ UserString: String;
+ end;
+const Size_TStrStr = sizeof( TStrStr );
+asm
+ PUSH ESI
+ PUSH EDI
+ MOV EDI, EDX
+ CMP WORD PTR [EDI].TMsg.message, WM_NOTIFY
+ JNZ @@ret_false
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV ESI, [EAX].TControl.EV
+ {$ENDIF}
+ MOV ECX, [EDI].TMsg.lParam
+ MOV EDX, [ECX].TNMHdr.code
+ CMP EDX, DTN_DROPDOWN
+ JNZ @@chk_DTN_CLOSEUP
+ {$IFDEF EVENTS_DYNAMIC}
+ LEA ECX, [ESI].TEvents.fOnDropDown.TMethod.Code
+ {$ELSE}
+ LEA ECX, [EAX].TControl.EV.fOnDropDown.TMethod.Code
+ {$ENDIF}
+@@event1:
+ MOV EDX, [ECX].TMethod.Data
+ MOV ECX, [ECX].TMethod.Code
+ {$IFDEF NIL_EVENTS}
+ JECXZ @@ret_false
+ {$ENDIF}
+ XCHG EAX, EDX
+ CALL ECX
+ JMP @@ret_false
+@@chk_DTN_CLOSEUP: /////////////////////////////////////////////////////////////
+ {$IFDEF EVENTS_DYNAMIC}
+ LEA ECX, [ESI].TEvents.fOnCloseUp.TMethod.Code
+ {$ELSE}
+ LEA ECX, [EAX].TControl.EV.fOnCloseUp.TMethod.Code
+ {$ENDIF}
+ CMP EDX, DTN_CLOSEUP
+ JE @@event1
+////////////////////////////////////////////////////////////////////////////////
+ {$IFDEF EVENTS_DYNAMIC}
+ LEA ECX, [ESI].TEvents.fOnChangeCtl.TMethod.Code
+ {$ELSE}
+ LEA ECX, [EAX].TControl.EV.fOnChangeCtl.TMethod.Code
+ {$ENDIF}
+ CMP EDX, DTN_DATETIMECHANGE
+ JE @@event1
+ CMP EDX, DTN_USERSTRING
+ JNE @@ret_false
+////////////////////////////////////////////////////////////////////////////////
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV ECX, [ESI].TEvents.fOnDTPUserString.TMethod.Code
+ MOV EDX, [ESI].TEvents.fOnDTPUserString.TMethod.Data
+ {$ELSE}
+ MOV ECX, [EAX].TControl.EV.fOnDTPUserString.TMethod.Code
+ MOV EDX, [EAX].TControl.EV.fOnDTPUserString.TMethod.Data
+ {$ENDIF}
+ {$IFDEF NIL_EVENTS}
+ JECXZ @@ret_false
+ {$ENDIF}
+ SUB ESP, Size_TStrStr
+ MOV ESI, ESP
+ PUSHAD
+ CALL TControl.GetDateTime
+ FSTP QWORD PTR [ESI].TStrStr.param_Date
+ WAIT
+ //POPAD
+ //PUSHAD
+ LEA EAX, [ESI].TStrStr.UserString
+ AND dword ptr [EAX], 0
+ MOV EDI, [EDI].TMsg.lParam
+ MOV EDX, [EDI].TNMDateTimeString.pszUserString
+ CALL System.@LStrFromPChar
+ LEA EAX, [ESI].TStrStr.Accept
+ MOV byte ptr [EAX], 1
+ MOV [ESI].TStrStr.param_PtrToAccept, EAX
+ POPAD
+ MOV ESI, ECX
+ MOV ECX, [ESI].TStrStr.UserString
+ XCHG EAX, EDX
+ CALL ESI
+ MOV EAX, [ESP].TStrStr.Accept
+ AND EAX, 1
+ MOV [EDI].TNMDateTimeString.dwFlags, EAX
+ LEA EAX, [ESI].TStrStr.UserString
+ CALL System.@LStrClr
+ ADD ESP, Size_TStrStr
+@@ret_false:
+ XOR EAX, EAX
+ POP EDI
+ POP ESI
+end;
+
+function TControl.GetWindowHandle: HWnd;
+asm
+ MOV ECX, [EAX].fHandle
+ JECXZ @@1
+ XCHG EAX, ECX
+ RET
+@@1:
+ PUSH EBX
+ MOV EBX, EAX
+ {$IFDEF USE_FLAGS}
+ TEST [EBX].fFlagsG4, 1 shl G4_CreateVisible
+ {$ELSE}
+ CMP [EBX].fCreateVisible, 0
+ {$ENDIF}
+ JNZ @@2
+
+ XOR EDX, EDX
+ CALL TControl.Set_Visible
+
+ MOV EAX, EBX
+ CALL CallTControlCreateWindow
+ { This is a call to Pascal piece of code, which
+ calls virtual method TControl.CreateWindow }
+
+ {$IFDEF USE_FLAGS}
+ OR [EBX].fFlagsG4, 1 shl G4_CreateHidden
+ {$ELSE}
+ INC [EBX].fCreateHidden
+ {$ENDIF}
+ JMP @@0
+
+@@2: CALL CallTControlCreateWindow
+@@0: MOV EAX, [EBX].fHandle
+ POP EBX
+end;
+
+function TControl.CreateWindow: Boolean;
+type PCreateWndParams = ^TCreateWndParams;
+const
+ CS_OFF = CS_OWNDC or CS_CLASSDC or CS_PARENTDC or CS_GLOBALCLASS;
+ CS_ON = 0; //CS_VREDRAW or CS_HREDRAW;
+ szWndClass = sizeof( TWndClass );
+ int_IDC_ARROW = integer( IDC_ARROW );
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ {$IFDEF DEBUG_CREATEWINDOW}
+ MOV EAX, EBX
+ CALL Debug_CreateWindow1
+ {$ENDIF}
+ MOV ECX, [EBX].fParent
+ JECXZ @@chk_handle
+ XCHG EAX, ECX
+ CALL GetWindowHandle
+ TEST EAX, EAX
+ JZ @@ret_0
+@@chk_handle:
+ MOV ECX, [EBX].fHandle
+ JECXZ @@prepare_Params
+ MOV EAX, EBX
+ {$IFDEF USE_FLAGS}
+ TEST [EBX].fFlagsG4, 1 shl G4_CreateHidden
+ {$ELSE}
+ CMP [EBX].fCreateHidden, 0
+ {$ENDIF}
+ JZ @@create_children
+ CALL CreateChildWindows
+ MOV EAX, EBX
+ MOV DL, 1
+ CALL Set_Visible
+ {$IFDEF USE_FLAGS}
+ AND [EBX].fFlagsG4, not(1 shl G4_CreateHidden)
+ {$ELSE}
+ MOV [EBX].fCreateHidden, 0
+ {$ENDIF}
+ JMP @@ret_true
+@@create_children:
+ CALL CreateChildWindows
+@@ret_true:
+ MOV AL, 1
+@@ret_0:
+ POP EBX
+ RET
+@@prepare_params:
+ {$IFDEF USE_GRAPHCTLS}
+ {$IFDEF USE_FLAGS}
+ TEST [EBX].fFlagsG6, 1 shl G6_GraphicCtl
+ SETNZ AL
+ JNZ @@ret_0
+ {$ELSE}
+ MOV AL, [EBX].fWindowed
+ CMP AL, 0
+ JZ @@ret_0
+ {$ENDIF}
+ {$ENDIF}
+ PUSH EBP
+ MOV EBP, ESP
+
+ PUSH ECX // Params.WindowClass.lpszClassName := nil
+ PUSH ECX // Params.WindowClass.lpszMenuName := nil
+ PUSH ECX // Params.WindowClass.hbrBackground := 0
+ PUSH int_IDC_ARROW
+ PUSH ECX
+ CALL LoadCursor
+ PUSH EAX // Params.WindowClass.hCursor := LoadCursor( 0, IDC_ARROW )
+ XOR ECX, ECX
+ PUSH ECX // Params.WindowClass.hIcon := 0
+ PUSH [hInstance]// Params.WindowClass.hInstance := hInstance
+ PUSH ECX // Params.WindowClass.cbWndExtra := 0
+ PUSH ECX // Params.WindowClass.cbClsExtra := 0
+ {$IFDEF SAFE_CODE}
+ PUSH [EBX].fDefWndProc // Params.WindowClass.lpfnWndProc := fDefWndProc
+ {$ELSE}
+ PUSH 0
+ {$ENDIF}
+ PUSH [EBX].fClsStyle // Params.WindowClass.style := fStyle
+ ADD ESP, -64
+ PUSH ECX
+ MOV EAX, EBX
+ MOV EDX, ESP
+ CALL get_ClassName
+ POP EDX
+ MOV EAX, ESP
+ PUSH EDX
+ //CALL StrPCopy // StrPCopy( Params.WinClsNamBuf, ClassName )
+ CALL StrCopy
+ CALL RemoveStr
+ PUSH 0 // Params.Param := nil
+ PUSH [hInstance] // Params.Inst := hInstance
+ PUSH [EBX].fMenu // Params.Menu := fMenu
+ MOV DL, 1
+ MOV EAX, EBX
+ CALL GetParentWnd
+ PUSH EAX // Params.WndParent := GetParentWnd( True )
+
+ MOV ECX, CW_USEDEFAULT
+ MOV EAX, [EBX].fBoundsRect.Bottom
+ MOV EDX, [EBX].fBoundsRect.Top
+ SUB EAX, EDX
+ JNZ @@1
+ MOV EAX, ECX
+@@1: PUSH EAX // Params.Height := Height | CW_UseDefault
+ MOV EAX, [EBX].fBoundsRect.Right
+ SUB EAX, [EBX].fBoundsRect.Left
+ {$IFDEF USE_CMOV}
+ CMOVZ EAX, ECX
+ {$ELSE}
+ JNZ @@2
+ MOV EAX, ECX
+@@2: {$ENDIF}
+
+ PUSH EAX // Params.Width := Width | CW_UseDefault
+ MOV EAX, [EBX].fBoundsRect.Left
+ {$IFDEF USE_FLAGS}
+ TEST [EBX].fFlagsG3, 1 shl G3_IsControl
+ {$ELSE}
+ CMP [EBX].fIsControl, CL
+ {$ENDIF}
+ JNZ @@3
+ {$IFDEF USE_FLAGS}
+ TEST [EBX].fFlagsG2, (1 shl G2_ChangedPos)
+ {$ELSE}
+ TEST byte ptr [EBX].fChangedPosSz, 3
+ {$ENDIF USE_FLAGS}
+ JNZ @@3
+ MOV EDX, ECX
+ XCHG EAX, ECX
+@@3: PUSH EDX // Params.Y := Top | CW_UseDefault
+ PUSH EAX // Params.X := Left | CW_UseDefault
+ PUSH [EBX].fStyle // Params.Style := fStyle
+ PUSH [EBX].fCaption // Params.Caption := fCaption
+ LEA EAX, [ESP+40]
+ PUSH EAX // Params.WinClassName := @Params.WinClsNamBuf
+ PUSH [EBX].fExStyle // Params.ExStyle := fExStyle
+
+ MOV ECX, [EBX].fControlClassName
+ JECXZ @@registerClass
+ LEA EAX, [ESP].TCreateWndParams.WindowClass
+ PUSH EAX // @Params.WindowClass
+ PUSH ECX // fControlClassName
+ PUSH [hInstance] // hInstance
+ CALL GetClassInfo
+ MOV EAX, [ESP].TCreateWndParams.Inst
+ MOV [ESP].TCreateWndParams.WindowClass.hInstance, EAX
+ AND [ESP].TCreateWndParams.WindowClass.style, not CS_OFF
+@@registerClass:
+ CMP [EBX].fDefWndProc, 0
+ JNE @@fDefWndProc_ready
+ MOV EAX, [ESP].TCreateWndParams.WindowClass.lpfnWndProc
+ MOV [EBX].fDefWndProc, EAX
+@@fDefWndProc_ready:
+ MOV ECX, [ESP].TCreateWndParams.WndParent
+ TEST ECX, ECX
+ JNZ @@registerClass1
+ TEST byte ptr [ESP].TCreateWndParams.Style+3, $40
+ XCHG EAX, ECX
+ JNZ @@fin
+@@registerClass1:
+ MOV EAX, [ESP].TCreateWndParams.WinClassName
+ MOV EDX, [ESP].TCreateWndParams.WindowClass.hInstance
+ ADD ESP, -szWndClass
+ PUSH ESP
+ PUSH EAX
+ PUSH EDX
+ CALL GetClassInfo
+ ADD ESP, szWndClass
+ TEST EAX, EAX
+ JNZ @@registered
+ MOV EAX, [ESP].TCreateWndParams.WinClassName
+ MOV [ESP].TCreateWndParams.WindowClass.lpszClassName, EAX
+ MOV [ESP].TCreateWndParams.WindowClass.lpfnWndProc, offset WndFunc
+ LEA EAX, [ESP].TCreateWndParams.WindowClass
+ PUSH EAX
+ CALL RegisterClass
+ TEST EAX, EAX
+ JZ @@fin
+@@registered:
+ MOV [CreatingWindow], EBX
+ {$IFDEF DEBUG_CREATEWINDOW}
+ MOV EAX, EBX
+ MOV EDX, ESP
+ CALL Debug_CreateWindow2
+ {$ENDIF}
+ CALL CreateWindowEx
+ MOV [EBX].fHandle, EAX
+ TEST EAX, EAX
+ JZ @@fin
+ PUSH EAX
+ {$IFDEF USE_PROP}
+ PUSH offset ID_SELF
+ {$ELSE}
+ PUSH GWL_USERDATA
+ {$ENDIF}
+ PUSH EAX
+
+ PUSH 0
+ PUSH $10002 //UIS_CLEAR or (UISF_HIDEFOCUS shl 16)
+ PUSH $0128 //WM_UPDATEUISTATE
+ PUSH EAX
+ CALL SendMessage
+
+ {$IFDEF USE_PROP}
+ CALL GetProp
+ {$ELSE}
+ CALL GetWindowLong
+ {$ENDIF}
+ XCHG ECX, EAX
+ POP EAX
+ INC ECX
+ LOOP @@propSet
+ MOV [CreatingWindow], ECX
+ PUSH EBX
+ {$IFDEF USE_PROP}
+ PUSH offset ID_SELF
+ PUSH EAX
+ CALL SetProp
+ {$ELSE}
+ PUSH GWL_USERDATA
+ PUSH EAX
+ CALL SetWindowLong
+ {$ENDIF}
+@@propSet:
+ {$IFDEF SMALLEST_CODE}
+ {$ELSE}
+ {$IFDEF USE_FLAGS}
+ TEST [EBX].fFlagsG3, 1 shl G3_IsControl
+ {$ELSE}
+ CMP [EBX].fIsControl, 0
+ {$ENDIF}
+ JNZ @@iconSet
+ MOV EAX, EBX
+ CALL GetIcon
+ PUSH EAX
+ PUSH 1
+ PUSH WM_SETICON
+ PUSH EBX
+ CALL Perform
+@@iconSet:
+ {$ENDIF}
+ MOV ECX, [EBX].PP.fCreateWndExt
+ {$IFDEF NIL_EVENTS}
+ JECXZ @@dblbufcreate
+ {$ENDIF}
+ MOV EAX, EBX
+ CALL ECX
+@@dblbufcreate:
+@@applyfont:
+ MOV EAX, EBX
+ CALL [ApplyFont2Wnd_Proc]
+ MOV EAX, EBX
+ CALL [ApplyFont2Wnd_Proc]
+@@createchildren:
+ XCHG EAX, EBX
+ CALL CreateChildWindows
+ MOV AL, 1
+@@fin:
+ MOV ESP, EBP
+ POP EBP
+@@ret_false:
+ POP EBX
+end;
+
+
+function WndProcKeybd( Self_: PControl; var Msg: TMsg; var Rslt: Integer): Boolean;
+asm
+ PUSH EBX
+ MOV ECX, [EDX].TMsg.message
+ SUB CX, $100
+ CMP ECX, 5
+ JA @@fin_false
+ XCHG EBX, EAX // EBX = @Self
+ XCHG EAX, ECX // EAX = message - WM_KEYFIRST
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV ECX, [EBX].TControl.EV
+ LEA ECX, [ECX].TEvents.fOnKeyUp
+ {$ELSE}
+ LEA ECX, [EBX].TControl.EV.fOnKeyUp
+ {$ENDIF}
+ JZ @@event
+ {$IFDEF PARANOIA} DB $34, 1 {$ELSE} XOR AL, 1 {$ENDIF}
+ JZ @@event
+ //LEA ECX, [EBX].TControl.EV.fOnKeyDown
+ ADD ECX, 8
+ {$IFDEF PARANOIA} DB $34, 1 {$ELSE} XOR AL, 1 {$ENDIF}
+ JZ @@event
+ {$IFDEF PARANOIA} DB $34, 4 {$ELSE} XOR AL, 4 {$ENDIF}
+ JZ @@event
+ //LEA ECX, [EBX].TControl.EV.fOnChar
+ SUB ECX, 24
+ {$IFDEF PARANOIA} DB $34, 6 {$ELSE} XOR AL, 2 xor 4 {$ENDIF}
+ JZ @@event
+ {$IFDEF PARANOIA} DB $34, 4 {$ELSE} XOR AL, 6 xor 2 {$ENDIF}
+ JNZ @@fin_false
+@@event:
+ {$IFDEF NIL_EVENTS}
+ CMP word ptr [ECX].TMethod.Code+2, 0
+ JZ @@fin_false
+ {$ENDIF}
+ PUSH EDX
+ PUSH ECX
+ LEA ECX, [EDX].TMsg.wParam
+ PUSH ECX
+ CALL GetShiftState
+ POP ECX // @wParam
+ XCHG EAX, [ESP] // ShiftState; EAX=@event
+ MOV EDX, EBX // @Self
+ MOV EBX, [EAX].TMethod.Code
+ MOV EAX, [EAX].TMethod.Data
+ CALL EBX
+
+ POP EDX
+ MOV ECX, [EDX].TMsg.wParam
+ JECXZ @@fin_true
+
+@@fin_false:
+ XOR EAX, EAX
+ POP EBX
+ RET
+
+@@fin_true:
+ MOV AL, 1
+ POP EBX
+end;
+
+function TControl.GetCaption: KOLString;
+asm
+ PUSH EBX
+ PUSH EDI
+ XCHG EBX, EAX
+ MOV EDI, EDX
+ {$IFDEF USE_FLAGS}
+ TEST [EBX].fFlagsG1, (1 shl G1_IgnoreWndCaption)
+ {$ELSE}
+ CMP [EBX].fIgnoreWndCaption, 0
+ {$ENDIF USE_FLAGS}
+ JNZ @@getFCaption
+ MOV ECX, [EBX].fHandle
+ JECXZ @@getFCaption
+@@getWndCaption:
+ PUSH ECX
+ CALL GetWindowTextLength
+ PUSH EAX
+ XCHG EDX, EAX
+ LEA EAX, [EBX].fCaption
+ CALL System.@LStrSetLength
+ POP ECX
+ JECXZ @@getFCaption
+ INC ECX
+ PUSH ECX
+ PUSH [EBX].fCaption
+ PUSH [EBX].fHandle
+ CALL GetWindowText
+@@getFCaption:
+ MOV EDX, [EBX].fCaption
+ XCHG EAX, EDI
+ {$IFNDEF UNICODE_CTRLS}
+ CALL System.@LStrAsg
+ {$ELSE}
+ CALL System.@WStrFromPChar
+ {$ENDIF}
+@@exit:
+ POP EDI
+ POP EBX
+end;
+
+function TControl.get_ClassName: AnsiString;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ XCHG EAX, EDX
+ MOV EDX, [EBX].fControlClassName
+ PUSH EAX
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX
+ {$ENDIF}
+ CALL System.@LStrFromPChar // EAX^ := String(EDX)
+ POP EAX
+ {$IFDEF USE_FLAGS}
+ TEST [EBX].fFlagsG6, 1 shl G6_CtlClassNameChg
+ {$ELSE}
+ CMP [EBX].fCtlClsNameChg, 0
+ {$ENDIF}
+ JNZ @@exit
+ MOV ECX, [EAX]
+ MOV EDX, offset[ @@obj ]
+ CALL System.@LStrCat3 // EAX^ := EDX + ECX
+ JMP @@exit
+
+ {$IFDEF _D2009orHigher}
+ DW 1252, 1 // CP_ANSI_LATIN1, Byte // TODO: CP_ACP
+ {$ENDIF}
+ DD -1, 4 // FFFFFFFF 04000000 obj_, 0
+@@obj: DB 'obj_', 0
+@@exit:
+ POP EBX
+end;
+
+function TControl.GetItems(Idx: Integer): AnsiString;
+asm
+ PUSH ESI
+ PUSH EDI
+ PUSH EBX
+ PUSH EBP
+ MOV EBP, ESP
+
+ MOV EBX, EAX // @Self
+ MOV ESI, EDX // Idx
+ MOV EDI, ECX // @Result
+
+ CALL Item2Pos
+ PUSH 0 // push 0
+ PUSH EAX // store Pos
+
+ XCHG EDX, EAX
+ MOV EAX, EBX
+ CALL Pos2Item // EAX = Idx'
+ XCHG ESI, EAX // ESI = Idx'
+
+ XOR EAX, EAX
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EBX].fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.aGetItemLength
+ {$ELSE}
+ MOVZX ECX, [EBX].fCommandActions.aGetItemLength
+ {$ENDIF}
+ JECXZ @@ret_empty
+
+ PUSH ECX // push aGetItemLength
+
+ PUSH EBX
+ CALL Perform
+
+ TEST EAX, EAX
+ JZ @@ret_empty
+
+ PUSH EAX // save L
+ ADD EAX, 4
+
+ CALL System.@GetMem // GetMem( L+4 )
+ POP EDX // restore L
+ LEA ECX, [EDX+1]
+ MOV dword ptr [EAX], ECX
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EBX].fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.aGetItemText
+ {$ELSE}
+ MOVZX ECX, [EBX].fCommandActions.aGetItemText
+ {$ENDIF}
+ JECXZ @@ret_buf
+
+ PUSH EDX // save L
+
+ PUSH EAX
+ PUSH EAX // push Buf
+ PUSH ESI // push Idx
+
+ PUSH ECX // push aGetItemText
+ PUSH EBX
+ CALL Perform
+ POP EAX
+
+ POP EDX
+@@ret_buf:
+ MOV byte ptr [EAX + EDX], 0 // Buf[ L ] := #0
+
+@@ret_empty: // EAX = 0
+ XCHG EDX, EAX
+ MOV EAX, EDI
+ PUSH EDX
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX
+ {$ENDIF}
+ CALL System.@LStrFromPChar
+ POP ECX
+ JECXZ @@exit
+ XCHG EAX, ECX
+ CALL System.@FreeMem
+@@exit:
+ MOV ESP, EBP
+ POP EBP
+ POP EBX
+ POP EDI
+ POP ESI
+end;
+
+procedure TControl.SetItems(Idx: Integer; const Value: AnsiString);
+asm
+ PUSH EDI
+ PUSH EBX
+ XCHG EBX, EAX
+ XCHG EDI, EDX // EDI = Idx
+ CALL ECX2PChar
+ PUSH ECX // @Value[1]
+
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EBX].fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.aSetItemText
+ {$ELSE}
+ MOVZX ECX, [EBX].fCommandActions.aSetItemText
+ {$ENDIF}
+ JECXZ @@1
+
+ PUSH 0
+ PUSH ECX
+
+ MOV EDX, EDI
+ MOV EAX, EBX
+ CALL Item2Pos
+ PUSH EAX // store Strt
+
+ MOV EDX, EDI
+ INC EDX
+ MOV EAX, EBX
+ CALL Item2Pos
+ POP EDX // EDX = Strt
+
+ SUB EAX, EDX
+ PUSH EAX // store L
+
+ MOV EAX, EBX
+ CALL SetSelStart
+
+ POP EDX // EDX = L
+ PUSH EBX // prepare @Self for Perform
+ XCHG EAX, EBX
+ CALL SetSelLength
+
+ // @Value[1] already in stack,
+ // 0 already in stack
+ // aSetItemText already in stack
+ // @Self already in stack
+
+ CALL Perform
+ JMP @@exit
+
+@@1: // @Value[1] in stack already
+ POP EDX
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EBX].fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.aDeleteItem
+ {$ELSE}
+ MOVZX ECX, [EBX].fCommandActions.aDeleteItem
+ {$ENDIF}
+ JECXZ @@exit
+
+ {$IFNDEF NOT_FIX_CURINDEX}
+ PUSH ESI
+ PUSH EBP
+
+ PUSH EDX
+
+ MOV EAX, EBX // +AK
+ CALL GetCurIndex // +AK
+ XCHG ESI, EAX // ESI = TmpCurIdx
+
+ MOV EAX, EBX
+ MOV EDX, EDI
+ CALL GetItemData
+ XCHG EBP, EAX // EBP = TmpData
+
+ MOV EDX, EDI
+ MOV EAX, EBX
+ CALL Delete
+
+ MOV EAX, EBX // *AK
+ MOV EDX, EDI
+ POP ECX
+ CALL Insert
+
+ MOV ECX, EBP // ECX = TmpData
+ MOV EDX, EDI
+ MOV EAX, EBX
+ CALL SetItemData
+
+ XCHG EAX, EBX // +AK
+ MOV EDX, ESI // +AK
+ CALL SetCurIndex // +AK
+
+ POP EBP
+ POP ESI
+ {$ELSE NOT_FIX_CURINDEX}
+ PUSH EDX
+
+ MOV EDX, EDI
+ MOV EAX, EBX
+ CALL Delete
+
+ XCHG EAX, EBX
+ XCHG EDX, EDI
+
+ POP ECX
+ CALL Insert
+ {$ENDIF NOT_FIX_CURINDEX}
+
+@@exit:
+ POP EBX
+ POP EDI
+end;
+
+function TControl.Add(const S: KOLString): Integer;
+asm
+ PUSH EBX
+ MOV EBX, EAX // EBX = @Self
+
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EBX].fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.aAddItem
+ {$ELSE}
+ MOVZX ECX, [EBX].fCommandActions.aAddItem // ECX = aAddItem
+ {$ENDIF}
+ JECXZ @@chk_addtext
+
+ CALL EDX2PChar
+ PUSH EDX
+ PUSH 0
+ PUSH ECX
+ PUSH EBX
+ CALL Perform
+ PUSH EAX
+
+ MOV EAX, EBX
+ CALL TControl.GetItemsCount
+ XCHG EAX, ECX
+ LOOP @@ret_EAX
+
+ XCHG EAX, EBX
+ INC ECX
+ XOR EDX, EDX
+ CALL TControl.SetItemSelected
+@@ret_EAX:
+ POP EAX
+ JMP @@exit
+
+@@chk_addtext:
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EBX].fCommandActions
+ MOV ECX, [ECX].TCommandActionsObj.aAddText
+ {$ELSE}
+ MOV ECX, [EBX].fCommandActions.aAddText
+ {$ENDIF}
+ JECXZ @@add_text_simple
+
+ CALL ECX
+ JMP @@exit_0
+
+@@add_text_simple:
+ LEA EAX, [EBX].fCaption
+ CALL System.@LStrCat
+ MOV EDX, [EBX].fCaption
+ MOV EAX, EBX
+ CALL SetCaption
+
+@@exit_0:
+ XOR EAX, EAX
+@@exit:
+ POP EBX
+end;
+
+function TControl.Insert(Idx: Integer; const S: AnsiString): Integer;
+asm
+ CALL ECX2PChar
+ PUSH ECX
+ {$IFDEF COMMANDACTIONS_OBJ}
+ MOV ECX, [EAX].fCommandActions
+ MOVZX ECX, [ECX].TCommandActionsObj.aInsertItem
+ {$ELSE}
+ MOVZX ECX, [EAX].fCommandActions.aInsertItem
+ {$ENDIF}
+ JECXZ @@exit_1
+
+ PUSH EDX
+ PUSH ECX
+ PUSH EAX
+ CALL Perform
+ RET
+
+@@exit_1:OR EAX, -1
+ POP ECX
+end;
+
+procedure TTrayIcon.SetTooltip(const Value: AnsiString);
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ MOV EAX, [EBX].fTooltip
+ PUSH EDX
+ CALL System.@LStrCmp
+ POP EDX
+ JE @@exit
+ LEA EAX, [EBX].fTooltip
+ CALL System.@LStrAsg
+ CMP [EBX].fActive, 0
+ JE @@exit
+ XOR EDX, EDX
+ INC EDX // EDX = NIM_MODIFY
+ XCHG EAX, EBX
+ CALL SetTrayIcon
+@@exit:
+ POP EBX
+end;
+
+procedure TTrayIcon.SetTrayIcon(const Value: DWORD);
+const sz_tid = sizeof( TNotifyIconData );
+asm
+ CMP [AppletTerminated], 0
+ JE @@1
+ MOV DL, NIM_DELETE
+@@1:
+ PUSH EBX
+ PUSH ESI
+ MOV ESI, EAX
+ MOV EBX, EDX
+
+ XOR ECX, ECX
+ PUSH ECX
+ ADD ESP, -60
+ MOV EDX, [ESI].fToolTip
+ CALL EDX2PChar
+ MOV EAX, ESP
+ MOV CL, 63
+ CALL StrLCopy
+
+ PUSH [ESI].fIcon
+ PUSH CM_TRAYICON
+ XOR EDX, EDX
+ CMP BL, NIM_DELETE
+ JE @@2
+ MOV DL, NIF_ICON or NIF_MESSAGE or NIF_TIP
+@@2: PUSH EDX
+ PUSH ESI
+ MOV EAX, [ESI].FWnd
+ TEST EAX, EAX
+ JNZ @@3
+ MOV EAX, [ESI].fControl
+ MOV EAX, [EAX].TControl.fHandle
+@@3:
+ PUSH EAX
+ PUSH sz_tid
+
+ PUSH ESP
+ PUSH EBX
+ CALL Shell_NotifyIcon
+
+ ADD ESP, sz_tid
+ POP ESI
+ POP EBX
+@@exit:
+end;
+
+function WndProcJustOneNotify( Control: PControl; var Msg: TMsg; var Rslt: Integer ) : Boolean;
+asm
+ PUSH EBP
+ MOV EBP, ESP
+ PUSHAD
+ CALL WndProcJustOne
+ POPAD
+ XOR EAX, EAX
+ PUSH ECX
+ MOV ECX, [EDX].TMsg.message
+ SUB ECX, [JustOneMsg]
+ POP ECX
+ JNE @@exit
+ MOV [ECX], EAX
+ CMP [OnAnotherInstance].TMethod.Code, EAX
+ JE @@exit_1
+
+ //MOV EAX, (MAX_PATH + 3) and 0FFFFCh
+ MOV AH, 2
+ SUB ESP, EAX
+
+ MOV ECX, ESP
+ PUSH EAX
+ PUSH ECX
+ PUSH [EDX].TMsg.lParam
+ CALL GetWindowText
+
+ MOV EDX, ESP
+ PUSH 0
+ MOV EAX, ESP
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX
+ {$ENDIF}
+ CALL System.@LStrFromPChar
+
+ MOV EDX, [ESP]
+ MOV EAX, [OnAnotherInstance].TMethod.Data
+ CALL [OnAnotherInstance].TMethod.Code
+
+ MOV EAX, ESP
+ CALL System.@LStrClr
+@@exit_1:
+ MOV AL, 1
+@@exit:
+ MOV ESP, EBP
+ POP EBP
+end;
+
+function JustOneNotify( Wnd: PControl; const Identifier : AnsiString;
+ const aOnAnotherInstance: TOnAnotherInstance ) : Boolean;
+asm
+ PUSHAD
+ MOV EBP, ESP
+
+ XCHG EAX, EDX
+ PUSH EAX
+ CALL System.@LStrLen
+ POP EDX
+ ADD EAX, EAX
+ SUB ESP, EAX
+ MOV EAX, ESP
+ CALL StrPCopy
+ PUSH '.ega'
+ PUSH 'sseM'
+ PUSH ESP
+ CALL RegisterWindowMessage
+ MOV [JustOneMsg], EAX
+ TEST EAX, EAX
+ MOV ESP, EBP
+ POPAD
+ JE @@exit_f
+ PUSHAD
+ CALL JustOne
+ DEC AL
+ POPAD
+ JZ @@exit_t
+ PUSH EBX
+ XCHG EBX, EAX
+ XOR EDX, EDX
+ XCHG [EBX].TControl.fCaption, EDX
+ PUSH EDX
+ CALL GetCommandLine
+ XCHG EDX, EAX
+ LEA EAX, [EBX].TControl.fCaption
+ {$IFDEF _D2009orHigher}
+ PUSH ECX
+ XOR ECX, ECX
+ {$ENDIF}
+ CALL System.@LStrFromPChar
+ {$IFDEF _D2009orHigher}
+ POP ECX
+ {$ENDIF}
+ MOV EAX, EBX
+ MOV EDX, [EBX].TControl.fCaption
+ CALL TControl.SetCaption
+ MOV EAX, EBX
+ CALL TControl.GetWindowHandle
+ TEST EAX, EAX
+ JZ @@rest_cap
+ PUSH BSM_APPLICATIONS
+ MOV EDX, ESP
+ PUSH EAX
+ PUSH 0
+ PUSH [JustOneMsg]
+ PUSH EDX
+ PUSH BSF_QUERY or BSF_IGNORECURRENTTASK
+ CALL BroadcastSystemMessage
+ POP EDX
+@@rest_cap:
+ LEA EAX, [EBX].TControl.fCaption
+ CALL System.@LStrClr
+ POP EDX
+ MOV [EBX].TControl.fCaption, EDX
+ MOV EAX, EBX
+ CALL TControl.SetCaption
+ POP EBX
+@@exit_f:
+ XOR EAX, EAX
+ JMP @@exit
+@@exit_t:
+ PUSHAD
+ LEA ESI, [aOnAnotherInstance]
+ LEA EDI, [OnAnotherInstance]
+ MOVSD
+ MOVSD
+ MOV EDX, offset[WndProcJustOneNotify]
+ CALL TControl.AttachProc
+ POPAD
+ MOV AL, 1
+@@exit:
+end;
+
+function TStrList.AppendToFile(const FileName: Ansistring): Boolean;
+asm
+ PUSH EBX
+ MOV EBX, EDX
+ PUSH 0
+ MOV EDX, ESP
+ CALL GetTextStr
+ XCHG EAX, EBX
+ MOV EDX, ofOpenWrite or ofOpenAlways
+ CALL FileCreate
+ MOV EBX, EAX
+ INC EAX
+ JZ @@exit
+ DEC EAX
+ XOR EDX, EDX
+ XOR ECX, ECX
+ MOV CL, spEnd
+ CALL FileSeek
+ POP EAX
+ PUSH EAX
+ CALL System.@LStrLen
+ XCHG ECX, EAX
+ MOV EAX, EBX
+ POP EDX
+ PUSH EDX
+ CALL FileWrite
+ XCHG EAX, EBX
+ CALL FileClose
+@@exit:
+ CALL RemoveStr
+ POP EBX
+end;
+
+function TStrList.LoadFromFile(const FileName: AnsiString): Boolean;
+asm
+ PUSH EAX
+ XCHG EAX, EDX
+ MOV EDX, ofOpenRead or ofShareDenyWrite or ofOpenExisting
+ CALL FileCreate
+ INC EAX
+ JZ @@exit
+ DEC EAX
+ PUSH EBX
+ XCHG EBX, EAX
+ PUSH 0
+ PUSH EBX
+ CALL GetFileSize
+ XOR EDX, EDX
+ PUSH EDX
+ XCHG ECX, EAX
+ MOV EAX, ESP
+ PUSH ECX
+ {$IFDEF _D2}
+ CALL _LStrFromPCharLen
+ {$ELSE}
+ {$IFDEF _D2009orHigher}
+ PUSH EDX // ushort 0, CodePage?
+ {$ENDIF}
+ CALL System.@LStrFromPCharLen
+ {$ENDIF}
+ POP ECX
+ MOV EAX, EBX
+ POP EDX
+ PUSH EDX
+ CALL FileRead
+ XCHG EAX, EBX
+ CALL FileClose
+ POP EDX
+ POP EBX
+ POP EAX
+ PUSH EDX
+ XOR ECX, ECX
+ CALL SetText
+ CALL RemoveStr
+ PUSH EDX
+ MOV AL, 1
+@@exit: POP EDX
+end;
+
+function TStrList.SaveToFile(const FileName: Ansistring): Boolean;
+asm
+ PUSH EBX
+ PUSH EAX
+ XCHG EAX, EDX
+ MOV EDX, ofOpenWrite or ofCreateAlways
+ CALL FileCreate
+ INC EAX
+ JZ @@exit
+ DEC EAX
+ XCHG EBX, EAX
+ POP EAX
+ PUSH 0
+ MOV EDX, ESP
+ CALL GetTextStr
+ POP EAX
+ PUSH EAX
+ CALL System.@LStrLen
+ XCHG ECX, EAX
+ POP EDX
+ PUSH EDX
+ MOV EAX, EBX
+ CALL FileWrite
+ PUSH EBX
+ CALL SetEndOfFile
+ XCHG EAX, EBX
+ CALL FileClose
+ CALL RemoveStr
+ PUSH EDX
+ INC EAX
+@@exit:
+ POP EDX
+ POP EBX
+end;
+
+procedure TControl.SetStatusText(Index: Integer; const Value: KOLString);
+asm
+ PUSHAD
+ MOV EBX, EDX // EBX = Index
+ MOV ESI, EAX // ESI = @Self
+ PUSH Value // prepare value for call at the end of procedure
+ PUSH EBX // prepare Index for call at the end of procedure
+ MOV ECX, [ESI].fStatusCtl
+ MOV EBP, ECX
+ INC ECX
+ LOOP @@status_created
+ CALL GetClientHeight
+ PUSH EAX // ch = old client height
+ MOV EAX, ESI
+ CALL _NewStatusBar
+ MOV [ESI].fStatusCtl, EAX
+ XCHG EBP, EAX
+ XOR EDX, EDX
+ PUSH EDX
+ INC DH
+ DEC EDX
+ CMP EBX, EDX
+ SETZ DL
+ NEG EDX
+ PUSH EDX
+ PUSH SB_SIMPLE
+ PUSH EBP
+ CALL TControl.Perform
+ ADD ESP, -16
+ PUSH ESP
+ PUSH [EBP].fHandle
+ CALL GetWindowRect
+ POP EAX
+ POP EDX
+ POP EAX
+ POP EAX
+ SUB EAX, EDX
+ MOV [ESI].fClientBottom, AL
+ POP EDX // ch
+ PUSH 0
+ PUSH 0
+ PUSH WM_SIZE
+ PUSH EBP
+ MOV EAX, ESI
+ CALL TControl.SetClientHeight
+ CALL TControl.Perform
+@@status_created:
+ CMP EBX, 255
+ JGE @@not_simple
+ PUSH 0
+ PUSH 0
+ PUSH SB_GETPARTS
+ PUSH EBP
+ CALL Perform
+ CMP EAX, EBX
+ JG @@reset_simple
+ MOV EAX, ESI
+ CALL GetWidth
+ CDQ
+ MOV ECX, EBX
+ INC ECX
+ IDIV ECX
+ MOV EDX, EAX
+ ADD ESP, -1024
+ ///////////////////
+ MOV ECX, EBX
+ MOV EDI, ESP
+ JECXZ @@2
+@@store_loo:
+ STOSD
+ ADD EAX, EDX
+ LOOP @@store_loo
+@@2:
+ OR dword ptr [ESP+EBX*4], -1
+ PUSH ESP
+ INC EBX
+ PUSH EBX
+ PUSH SB_SETPARTS
+ PUSH EBP
+ CALL Perform
+ ////////////////////
+ ADD ESP, 1024
+@@reset_simple:
+ PUSH 0
+ PUSH 0
+ PUSH SB_SIMPLE
+ PUSH EBP
+ CALL Perform
+@@not_simple:
+ PUSH SB_SETTEXT
+ PUSH EBP
+ CALL Perform
+ POPAD
+end;
+
+function TOpenSaveDialog.Execute: Boolean;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+
+ XOR ECX, ECX
+ {$IFDEF OpenSaveDialog_Extended}
+ MOVZX EAX, [EBX].NoPlaceBar
+ PUSH EAX
+ PUSH ECX
+ PUSH ECX
+ PUSH [EBX].TemplateName
+ PUSH [EBX].HookProc
+ {$ELSE}
+ PUSH ECX // prepare lpTemplateName = nil
+ PUSH ECX // prepare lpfnHook = nil
+ {$ENDIF}
+ PUSH EBX // prepare lCustData = @Self
+ MOV EDX, [EBX].FDefExtension
+ CALL EDX2PChar
+ PUSH EDX // prepare lpstrDefExt = FDefExtension
+ PUSH ECX // prepare nFileExtension, nFileOffset: Word = 0, 0
+ // prepare flags:
+ LEA EAX, [EBX].FOptions
+ MOV EDX, Offset[@@OpenSaveFlags]
+ {$IFDEF OpenSaveDialog_Extended}
+ MOV CL, 14
+ {$ELSE}
+ MOV CL, 12
+ {$ENDIF}
+ CALL MakeFlags
+ XOR ECX, ECX
+ OR EAX, OFN_EXPLORER or OFN_LONGNAMES or OFN_ENABLESIZING
+ PUSH EAX // push Flags
+ PUSH [EBX].FTitle // prepare lpstrTitle
+ PUSH [EBX].FInitialDir // prepare lpstrInitialDir
+ PUSH ECX // prepare nMaxFileTitle = 0
+ PUSH ECX // prepare lpstrFileTitle = nil
+ TEST AH, 2 // MultiSelect?
+ MOV EAX, 65520
+ JNZ @@1
+ MOV AX, MAX_PATH+2
+@@1: PUSH EAX // prepare nMaxFile
+ CALL System.@GetMem
+ POP ECX
+ PUSH ECX
+ PUSH EAX // prepare lpStrFile
+ XOR EDX, EDX
+
+@@2: MOV EDX, [EBX].fFileName // no, fill it initilly by FileName
+ CALL EDX2PChar
+ DEC ECX // added 5 october 2003 to prevent possible error if FileName too big
+ CALL StrLCopy
+ XOR EDX, EDX
+
+ PUSH [EBX].FFilterIndex // prepare nFilterIndex
+ PUSH EDX // prepare nMaxCustFilter
+ PUSH EDX // prepare lpstrCustomFilter
+ PUSH EDX // prepare lpstrFilter = nil
+ MOV EAX, ESP
+ OR EDX, [EBX].FFilter
+ JZ @@5
+
+ MOV ECX, offset[@@0]
+ CALL System.@LStrCat3 // prepare lpStrFilter = FFilter + #0
+ POP EAX
+ PUSH EAX
+ XOR EDX, EDX
+@@3: INC EAX // filter is not starting from ';' or '|'...
+ CMP [EAX], DL
+ JZ @@5
+ CMP byte ptr [EAX], '|'
+ JNZ @@3
+@@4: MOV [EAX], DL
+ JMP @@3
+@@OpenSaveFlags:
+ DD OFN_CREATEPROMPT, OFN_EXTENSIONDIFFERENT, OFN_FILEMUSTEXIST
+ DD OFN_HIDEREADONLY, OFN_NOCHANGEDIR, OFN_NODEREFERENCELINKS
+ DD OFN_ALLOWMULTISELECT, OFN_NONETWORKBUTTON, OFN_NOREADONLYRETURN
+ DD OFN_OVERWRITEPROMPT, OFN_PATHMUSTEXIST, OFN_READONLY, OFN_NOVALIDATE
+ {$IFDEF OpenSaveDialog_Extended}
+ DD OFN_ENABLETEMPLATE, OFN_ENABLEHOOK
+ {$ENDIF}
+
+ {$IFDEF _D2009orHigher}
+ DW 0, 1
+ {$ENDIF}
+ DD -1, 1
+@@0: DB 0
+
+
+@@5:
+ PUSH [hInstance] // prepare hInstance
+
+ MOV ECX, [EBX].TControl.fWnd
+ INC ECX
+ LOOP @@6
+ MOV ECX, [Applet]
+ JECXZ @@6
+ MOV ECX, [ECX].TControl.fHandle
+@@6: PUSH ECX // prepare hWndOwner
+ {$IFDEF OpenSaveDialog_Extended}
+ CALL WinVer
+ CMP AL, wvNT
+ MOV DL, 76+12
+ JA @@6a
+ CMP AL, wvME
+ JE @@6a
+ MOV DL, 76
+@@6a: MOVZX EAX, DL
+ PUSH EAX
+ {$ELSE}
+ PUSH 76 // prepare lStructSize
+ {$ENDIF}
+
+ PUSH ESP
+ CMP [EBX].TControl.FOpenDialog, 0
+ JZ @@7
+ CALL GetOpenFileName
+ JMP @@8
+@@7: CALL GetSaveFileName
+@@8:
+ PUSH EAX
+ XOR EDX, EDX
+ TEST EAX, EAX
+ JZ @@10
+
+ MOV EAX, [ESP+4].TOpenFileName.nFilterIndex
+ MOV [EBX].FFilterIndex, EAX
+
+ TEST BYTE PTR [ESP+4].TOpenFileName.Flags, OFN_READONLY
+ SETNZ AL
+ MOV [EBX].fOpenReadOnly, AL
+
+ MOV EAX, [ESP+4].TOpenFileName.lpstrFile
+ MOV EDX, EAX
+ XOR ECX, ECX
+
+ TEST [EBX].FOptions, 1 shl OSAllowMultiSelect
+ JZ @@10
+
+ DEC EAX
+@@9: INC EAX
+ CMP byte ptr [EAX], CL
+ JNZ @@9
+ CMP byte ptr [EAX+1], CL
+ JZ @@10
+ MOV byte ptr [EAX], 13
+ JMP @@9
+
+@@10:
+ LEA EAX, [EBX].FFileName
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX
+ {$ENDIF}
+ CALL System.@LStrFromPChar
+ MOV EAX, [ESP+4].TOpenFileName.lpstrFile
+ CALL System.@FreeMem // v1.86 +AK
+
+ LEA EAX, [ESP+4].TOpenFileName.lpstrFilter
+ CALL System.@LStrClr
+
+ POP EAX
+ {$IFDEF OpenSaveDialog_Extended}
+ ADD ESP, 76+12
+ {$ELSE}
+ ADD ESP, 76
+ {$ENDIF}
+ POP EBX
+end;
+
+function TOpenDirDialog.Execute: Boolean;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ XOR ECX, ECX
+ PUSH ECX // prepare iImage = 0
+ PUSH EBX // prepare lParam = @Self
+ PUSH [EBX].FCallBack // prepare lpfn = FCallBack
+ LEA EAX, [EBX].FOptions
+ MOV EDX, Offset[@@FlagsArray]
+ MOV CL, 8
+ CALL MakeFlags
+ PUSH EAX // prepare ulFlags = Options
+ PUSH [EBX].FTitle // prepare lpszTitle
+ LEA EAX, [EBX].FBuf
+ PUSH EAX // prepare pszDisplayName
+ PUSH 0 // prepare pidlRoot
+ MOV ECX, [EBX].fWnd
+ INC ECX
+ LOOP @@1
+ MOV ECX, Applet
+ JECXZ @@1
+ MOV ECX, [ECX].TControl.fHandle
+@@1: PUSH ECX // prepare hwndOwner
+ PUSH ESP
+ CALL SHBrowseForFolderA
+ ADD ESP, 32
+ TEST EAX, EAX
+ JZ @@exit
+ PUSH EAX
+ LEA EDX, [EBX].FBuf
+ PUSH EDX
+ PUSH EAX
+ CALL SHGetPathFromIDListA
+ CALL CoTaskMemFree
+ MOV AL, 1
+ JMP @@fin
+@@FlagsArray:
+ DD BIF_BROWSEFORCOMPUTER, BIF_BROWSEFORPRINTER, BIF_DONTGOBELOWDOMAIN
+ DD BIF_RETURNFSANCESTORS, BIF_RETURNONLYFSDIRS, BIF_STATUSTEXT
+ DD BIF_BROWSEINCLUDEFILES, BIF_EDITBOX, BIF_NEWDIALOGSTYLE
+@@exit: XOR EAX, EAX
+@@fin:
+ POP EBX
+end;
+
+function OpenDirSelChangeCallBack( Wnd: HWnd; Msg: DWORD; lParam, lpData: LParam ):
+ Integer; stdcall;
+asm
+ MOV EAX, [lpData]
+ MOV ECX, [EAX].TOpenDirDialog.FOnSelChanged.TMethod.Code
+ JECXZ @@exit
+ LEA EDX, [EAX].TOpenDirDialog.FBuf
+ PUSH EDX
+ PUSH [lParam]
+ CALL SHGetPathFromIDListA
+ MOV EDX, [lpData]
+ LEA ECX, [EDX].TOpenDirDialog.FBuf
+ PUSH 0
+ PUSH ESP
+ LEA EAX, [EDX].TOpenDirDialog.FStatusText
+ PUSH EAX
+ MOV EAX, [EDX].TOpenDirDialog.FOnSelChanged.TMethod.Data
+ CALL dword ptr [EDX].TOpenDirDialog.FOnSelChanged.TMethod.Code
+ PUSH 0
+ PUSH BFFM_ENABLEOK
+ PUSH [Wnd]
+ CALL SendMessage
+@@1: MOV EDX, [lpData]
+ MOV ECX, [EDX].TOpenDirDialog.FStatusText
+ JECXZ @@exit
+ PUSH ECX
+ PUSH 0
+ PUSH BFFM_SETSTATUSTEXT
+ PUSH [Wnd]
+ CALL SendMessage
+@@exit: XOR EAX, EAX
+end;
+
+function TControl.TBAddInsButtons(Idx: Integer; const Buttons: array of PKOLChar;
+ const BtnImgIdxArray: array of Integer): Integer; stdcall;
+asm { [EBP+$8] = @Self
+ [EBP+$C] = Idx
+ [EBP+$10] = Buttons
+ [EBP+$14] = High(Butons)
+ [EBP+$18] = BtnImgIdxArray
+ [EBP+$1C] = High(BtnImgIdxArray) }
+ PUSH EBX
+ PUSH ESI
+ PUSH EDI
+ OR EBX, -1
+ MOV EAX, 20
+ MOV ECX, [EBP+$14]
+ CMP ECX, EBX
+ JLE @@fin
+ INC ECX
+ MUL ECX
+ CALL System.@GetMem
+ PUSH EAX // save AB to FreeMem after
+ MOV EDX, EBX
+ DEC EDX // nBmp := -2
+ MOV ECX, [EBP+$14]
+ INC ECX
+ JZ @@exit
+ MOV ECX, [EBP+$1C]
+ INC ECX
+ JZ @@1
+ MOV ECX, [BtnImgIdxArray]
+ MOV EDX, [ECX]
+ DEC EDX // nBmp := BtnImgIdxArray[ 0 ] - 1
+@@1: MOV ECX, [EBP+$14]
+ INC ECX
+ MOV ESI, [Buttons]
+ MOV EDI, EAX // EDI = PAB
+ PUSH 0 // N:=0 in [EBP-$14]
+@@loop:
+ LODSD
+ TEST EAX, EAX
+ JZ @@break
+ PUSH ECX
+ CMP word ptr [EAX], '-'
+ JNE @@2
+ OR EAX, -1
+ STOSD
+ MOV EAX, [ToolbarsIDcmd]
+ TEST EBX, EBX
+ {$IFDEF USE_CMOV}
+ CMOVL EBX, EAX
+ {$ELSE}
+ JGE @@b0
+ MOV EBX, EAX
+@@b0: {$ENDIF}
+ STOSD
+ XOR EAX, EAX
+ INC AH // TBSTYLE_SEP = 1
+ STOSD
+ DEC AH
+ STOSD
+ DEC EAX
+ JMP @@3
+ {$IFDEF _D2009orHigher}
+ DW 0, 1
+ {$ENDIF}
+ DD -1, 1
+@@0: DB 0
+@@2:
+ INC EDX // Inc( nBmp )
+ PUSH EAX
+ MOV EAX, [EBP+$1C]
+ MOV ECX, [EBP-$14]
+ CMP EAX, ECX
+ MOV EAX, EDX
+ JL @@21
+ MOV EAX, [BtnImgIdxArray]
+ MOV EAX, [EAX+ECX*4]
+@@21: STOSD
+ TEST EDX, EDX
+ JGE @@2a
+ DEC EDX
+@@2a:
+ MOV EAX, [ToolbarsIDcmd]
+ STOSD
+ TEST EBX, EBX
+ {$IFDEF USE_CMOV}
+ CMOVL EBX, EAX
+ {$ELSE}
+ JGE @@210
+ MOV EBX, EAX
+@@210: {$ENDIF}
+ MOV ECX, [EBP+8]
+ MOV AH, BYTE PTR [ECX].TControl.DF.fDefaultTBBtnStyle
+ POP ECX
+ MOV AL, 4 // AL=fsState=_ENABLED, AH=fsStyle=_AUTOSIZE if fDefaultTBBtnStyle contains
+ CMP byte ptr [ECX], '^'
+ JNE @@22
+ OR AH, TBSTYLE_DROPDOWN
+ INC ECX
+@@22: CMP byte ptr [ECX], '-'
+ JZ @@23
+ CMP byte ptr [ECX], '+'
+ JNZ @@24
+ MOV AL, TBSTATE_ENABLED or TBSTATE_CHECKED
+@@23: INC ECX
+ OR AH, TBSTYLE_CHECK
+ CMP byte ptr [ECX], '!'
+ JNZ @@24
+ OR AH, TBSTYLE_GROUP
+ INC ECX
+@@24: {$IFDEF TOOLBAR_DOT_NOAUTOSIZE_BUTTON}
+ CMP byte ptr [ECX], '.'
+ JNZ @@25
+ AND AH, not TBSTYLE_AUTOSIZE
+ INC ECX
+@@25:
+ {$ENDIF TOOLBAR_DOT_NOAUTOSIZE_BUTTON}
+ STOSD
+ MOV EAX, [EBP+8]
+ STOSD
+ OR EAX, -1
+ CMP word ptr [ECX], ' '
+ JZ @@3
+ CMP byte ptr [ECX], 0
+ JZ @@3
+ PUSH EDX
+ PUSH 0
+ MOV EDX, ECX
+ MOV EAX, ESP
+ {$IFDEF _D2009orHigher}
+ PUSH ECX
+ XOR ECX, ECX
+ {$ENDIF}
+ CALL System.@LStrFromPChar
+ {$IFDEF _D2009orHigher}
+ POP ECX
+ {$ENDIF}
+ MOV EAX, ESP
+ MOV EDX, offset[@@0]
+ CALL System.@LStrCat
+ PUSH dword ptr [ESP]
+ PUSH 0
+ PUSH TB_ADDSTRING
+ PUSH dword ptr [EBP+8]
+ CALL Perform
+ STOSD
+ CALL RemoveStr
+ POP EDX
+ JMP @@30
+@@3: STOSD
+@@30: INC dword ptr [EBP-$14]
+ INC [ToolbarsIDcmd]
+ POP ECX
+ DEC ECX
+ JNZ @@loop
+@@break:
+ POP ECX
+ JECXZ @@exit
+ PUSH dword ptr [ESP]
+ MOV EAX, [Idx]
+ TEST EAX, EAX
+ JGE @@31
+ PUSH ECX
+ PUSH TB_ADDBUTTONS
+ JMP @@32
+@@31:
+ PUSH EAX
+ PUSH TB_INSERTBUTTON
+@@32:
+ PUSH dword ptr [EBP+8]
+ CALL Perform
+@@exit:
+ POP EAX
+ CALL System.@FreeMem
+@@fin:
+ POP EDI
+ POP ESI
+ XCHG EAX, EBX
+ POP EBX
+end;
+
+function TControl.TBGetButtonText( BtnID: Integer ): AnsiString;
+asm
+ PUSH ECX
+ ADD ESP, -1024
+ PUSH ESP
+ PUSH EAX
+ CALL GetTBBtnGoodID
+ POP EDX
+ PUSH EAX
+ PUSH TB_GETBUTTONTEXT
+ PUSH EDX
+ CALL Perform
+ TEST EAX, EAX
+ JLE @@2
+ MOV EDX, ESP
+ JMP @@1
+@@2: XOR EDX, EDX
+@@1: MOV EAX, [ESP+1024]
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX
+ {$ENDIF}
+ CALL System.@LStrFromPChar
+ ADD ESP, 1028
+end;
+
+procedure TBitmap.LoadFromResourceName(Inst: DWORD; ResName: PAnsiChar);
+asm
+ PUSH EBX
+ MOV EBX, EAX
+ PUSHAD
+ CALL Clear
+ POPAD
+ XOR EAX, EAX
+ PUSH ECX
+ MOVZX ECX, [EBX].fHandleType
+ INC ECX
+ LOOP @@1
+ MOV AH, LR_CREATEDIBSECTION shr 8 // = $2000
+@@1: MOV AL, LR_DEFAULTSIZE // = $40
+ POP ECX
+ PUSH EAX
+ PUSH 0
+ PUSH 0
+ PUSH IMAGE_BITMAP
+ PUSH ECX
+ PUSH EDX
+ CALL LoadImage
+ TEST EAX, EAX
+ JZ @@exit
+ XCHG EDX, EAX
+ XCHG EAX, EBX
+ CALL SetHandle
+@@exit: POP EBX
+end;
+{$ENDIF}
diff --git a/plugins/Libs/KOL_ansi.inc b/plugins/Libs/KOL_ansi.inc
new file mode 100644
index 0000000000..b40ef014c7
--- /dev/null
+++ b/plugins/Libs/KOL_ansi.inc
@@ -0,0 +1,2316 @@
+{*******************************************************************************
+ KOL_unicode.inc
+ Some redeclarations from Windows.pas for case, when UNICODE_CTRLS symbol is off.
+*******************************************************************************}
+{$IFDEF interface_part} ////////////////////////////////////////////////////////
+ //// for D3 gumno
+type
+ PRecoveryAgentInformationA = ^TRecoveryAgentInformationA;
+ PRecoveryAgentInformationW = ^TRecoveryAgentInformationW;
+ PRecoveryAgentInformation = PRecoveryAgentInformationA;
+ _RECOVERY_AGENT_INFORMATIONA = record
+ NextEntryOffset: DWORD;
+ AgentNameLength: DWORD;
+ AgentInformation: array[0..0] of AnsiChar;
+ end;
+ //// _RECOVERY_AGENT_INFORMATIONA}
+ _RECOVERY_AGENT_INFORMATIONW = record
+ NextEntryOffset: DWORD;
+ AgentNameLength: DWORD;
+ AgentInformation: array[0..0] of WideChar;
+ end;
+ //// _RECOVERY_AGENT_INFORMATIONW}
+ _RECOVERY_AGENT_INFORMATION = _RECOVERY_AGENT_INFORMATIONA;
+ TRecoveryAgentInformationA = _RECOVERY_AGENT_INFORMATIONA;
+ TRecoveryAgentInformationW = _RECOVERY_AGENT_INFORMATIONW;
+ TRecoveryAgentInformation = TRecoveryAgentInformationA;
+ RECOVERY_AGENT_INFORMATIONA = _RECOVERY_AGENT_INFORMATIONA;
+ //// RECOVERY_AGENT_INFORMATIONA}
+ RECOVERY_AGENT_INFORMATIONW = _RECOVERY_AGENT_INFORMATIONW;
+ //// RECOVERY_AGENT_INFORMATIONW}
+ RECOVERY_AGENT_INFORMATION = RECOVERY_AGENT_INFORMATIONA;
+ //
+ PTextMetricA = ^TTextMetricA;
+ PTextMetricW = ^TTextMetricW;
+ PTextMetric = PTextMetricA;
+ tagTEXTMETRICA = record
+ tmHeight: Longint;
+ tmAscent: Longint;
+ tmDescent: Longint;
+ tmInternalLeading: Longint;
+ tmExternalLeading: Longint;
+ tmAveCharWidth: Longint;
+ tmMaxCharWidth: Longint;
+ tmWeight: Longint;
+ tmOverhang: Longint;
+ tmDigitizedAspectX: Longint;
+ tmDigitizedAspectY: Longint;
+ tmFirstChar: AnsiChar;
+ tmLastChar: AnsiChar;
+ tmDefaultChar: AnsiChar;
+ tmBreakChar: AnsiChar;
+ tmItalic: Byte;
+ tmUnderlined: Byte;
+ tmStruckOut: Byte;
+ tmPitchAndFamily: Byte;
+ tmCharSet: Byte;
+ end;
+ tagTEXTMETRICW = record
+ tmHeight: Longint;
+ tmAscent: Longint;
+ tmDescent: Longint;
+ tmInternalLeading: Longint;
+ tmExternalLeading: Longint;
+ tmAveCharWidth: Longint;
+ tmMaxCharWidth: Longint;
+ tmWeight: Longint;
+ tmOverhang: Longint;
+ tmDigitizedAspectX: Longint;
+ tmDigitizedAspectY: Longint;
+ tmFirstChar: WideChar;
+ tmLastChar: WideChar;
+ tmDefaultChar: WideChar;
+ tmBreakChar: WideChar;
+ tmItalic: Byte;
+ tmUnderlined: Byte;
+ tmStruckOut: Byte;
+ tmPitchAndFamily: Byte;
+ tmCharSet: Byte;
+ end;
+ tagTEXTMETRIC = tagTEXTMETRICA;
+ TTextMetricA = tagTEXTMETRICA;
+ TTextMetricW = tagTEXTMETRICW;
+ TTextMetric = TTextMetricA;
+ TEXTMETRICA = tagTEXTMETRICA;
+ TEXTMETRICW = tagTEXTMETRICW;
+ TEXTMETRIC = TEXTMETRICA;
+ ///
+ PNewTextMetricA = ^TNewTextMetricA;
+ PNewTextMetricW = ^TNewTextMetricW;
+ PNewTextMetric = PNewTextMetricA;
+ //// tagNEWTEXTMETRICA}
+ tagNEWTEXTMETRICA = record
+ tmHeight: Longint;
+ tmAscent: Longint;
+ tmDescent: Longint;
+ tmInternalLeading: Longint;
+ tmExternalLeading: Longint;
+ tmAveCharWidth: Longint;
+ tmMaxCharWidth: Longint;
+ tmWeight: Longint;
+ tmOverhang: Longint;
+ tmDigitizedAspectX: Longint;
+ tmDigitizedAspectY: Longint;
+ tmFirstChar: AnsiChar;
+ tmLastChar: AnsiChar;
+ tmDefaultChar: AnsiChar;
+ tmBreakChar: AnsiChar;
+ tmItalic: Byte;
+ tmUnderlined: Byte;
+ tmStruckOut: Byte;
+ tmPitchAndFamily: Byte;
+ tmCharSet: Byte;
+ ntmFlags: DWORD;
+ ntmSizeEM: UINT;
+ ntmCellHeight: UINT;
+ ntmAvgWidth: UINT;
+ end;
+ //// tagNEWTEXTMETRICW}
+ tagNEWTEXTMETRICW = record
+ tmHeight: Longint;
+ tmAscent: Longint;
+ tmDescent: Longint;
+ tmInternalLeading: Longint;
+ tmExternalLeading: Longint;
+ tmAveCharWidth: Longint;
+ tmMaxCharWidth: Longint;
+ tmWeight: Longint;
+ tmOverhang: Longint;
+ tmDigitizedAspectX: Longint;
+ tmDigitizedAspectY: Longint;
+ tmFirstChar: WideChar;
+ tmLastChar: WideChar;
+ tmDefaultChar: WideChar;
+ tmBreakChar: WideChar;
+ tmItalic: Byte;
+ tmUnderlined: Byte;
+ tmStruckOut: Byte;
+ tmPitchAndFamily: Byte;
+ tmCharSet: Byte;
+ ntmFlags: DWORD;
+ ntmSizeEM: UINT;
+ ntmCellHeight: UINT;
+ ntmAvgWidth: UINT;
+ end;
+ // tagNEWTEXTMETRIC}
+ tagNEWTEXTMETRIC = tagNEWTEXTMETRICA;
+ TNewTextMetricA = tagNEWTEXTMETRICA;
+ TNewTextMetricW = tagNEWTEXTMETRICW;
+ TNewTextMetric = TNewTextMetricA;
+ // NEWTEXTMETRICA}
+ NEWTEXTMETRICA = tagNEWTEXTMETRICA;
+ // NEWTEXTMETRICW}
+ NEWTEXTMETRICW = tagNEWTEXTMETRICW;
+ // NEWTEXTMETRIC}
+ NEWTEXTMETRIC = NEWTEXTMETRICA;
+ PNewTextMetricExA = ^TNewTextMetricExA;
+ // tagNEWTEXTMETRICEXA}
+ tagNEWTEXTMETRICEXA = packed record
+ ntmTm: TNewTextMetricA;
+ ntmFontSig: TFontSignature;
+ end;
+ TNewTextMetricExA = tagNEWTEXTMETRICEXA;
+ // NEWTEXTMETRICEXA}
+ NEWTEXTMETRICEXA = tagNEWTEXTMETRICEXA;
+ PNewTextMetricExW = ^TNewTextMetricExW;
+ // tagNEWTEXTMETRICEXW}
+ tagNEWTEXTMETRICEXW = packed record
+ ntmTm: TNewTextMetricW;
+ ntmFontSig: TFontSignature;
+ end;
+ TNewTextMetricExW = tagNEWTEXTMETRICEXW;
+ // NEWTEXTMETRICEXW}
+ NEWTEXTMETRICEXW = tagNEWTEXTMETRICEXW;
+ PNewTextMetricEx = PNewTextMetricExA;
+ { Structure passed to FONTENUMPROC }
+ PEnumLogFontA = ^TEnumLogFontA;
+ PEnumLogFontW = ^TEnumLogFontW;
+ PEnumLogFont = PEnumLogFontA;
+ // tagENUMLOGFONTA}
+ tagENUMLOGFONTA = packed record
+ elfLogFont: TLogFontA;
+ elfFullName: array[0..LF_FULLFACESIZE - 1] of AnsiChar;
+ elfStyle: array[0..LF_FACESIZE - 1] of AnsiChar;
+ end;
+ // tagENUMLOGFONTW}
+ tagENUMLOGFONTW = packed record
+ elfLogFont: TLogFontW;
+ elfFullName: array[0..LF_FULLFACESIZE - 1] of WideChar;
+ elfStyle: array[0..LF_FACESIZE - 1] of WideChar;
+ end;
+ // tagENUMLOGFONT}
+ tagENUMLOGFONT = tagENUMLOGFONTA;
+ TEnumLogFontA = tagENUMLOGFONTA;
+ TEnumLogFontW = tagENUMLOGFONTW;
+ TEnumLogFont = TEnumLogFontA;
+ // ENUMLOGFONTA}
+ ENUMLOGFONTA = tagENUMLOGFONTA;
+ // ENUMLOGFONTW}
+ ENUMLOGFONTW = tagENUMLOGFONTW;
+ // ENUMLOGFONT}
+ ENUMLOGFONT = ENUMLOGFONTA;
+ PEnumLogFontExA = ^TEnumLogFontExA;
+ PEnumLogFontExW = ^TEnumLogFontExW;
+ PEnumLogFontEx = PEnumLogFontExA;
+ // tagENUMLOGFONTEXA}
+ tagENUMLOGFONTEXA = packed record
+ elfLogFont: TLogFontA;
+ elfFullName: array[0..LF_FULLFACESIZE - 1] of AnsiChar;
+ elfStyle: array[0..LF_FACESIZE - 1] of AnsiChar;
+ elfScript: array[0..LF_FACESIZE - 1] of AnsiChar;
+ end;
+ // tagENUMLOGFONTEXW}
+ tagENUMLOGFONTEXW = packed record
+ elfLogFont: TLogFontW;
+ elfFullName: array[0..LF_FULLFACESIZE - 1] of WideChar;
+ elfStyle: array[0..LF_FACESIZE - 1] of WideChar;
+ elfScript: array[0..LF_FACESIZE - 1] of WideChar;
+ end;
+ // tagENUMLOGFONTEX}
+ tagENUMLOGFONTEX = tagENUMLOGFONTEXA;
+ TEnumLogFontExA = tagENUMLOGFONTEXA;
+ TEnumLogFontExW = tagENUMLOGFONTEXW;
+ TEnumLogFontEx = TEnumLogFontExA;
+ // ENUMLOGFONTEXA}
+ ENUMLOGFONTEXA = tagENUMLOGFONTEXA;
+ // ENUMLOGFONTEXW}
+ ENUMLOGFONTEXW = tagENUMLOGFONTEXW;
+ // ENUMLOGFONTEX}
+ ENUMLOGFONTEX = ENUMLOGFONTEXA;
+ PExtLogFontA = ^TExtLogFontA;
+ PExtLogFontW = ^TExtLogFontW;
+ PExtLogFont = PExtLogFontA;
+ // tagEXTLOGFONTA}
+ tagEXTLOGFONTA = record
+ elfLogFont: TLogFontA;
+ elfFullName: array[0..LF_FULLFACESIZE - 1] of AnsiChar;
+ elfStyle: array[0..LF_FACESIZE - 1] of AnsiChar;
+ elfVersion: DWORD; { 0 for the first release of NT }
+ elfStyleSize: DWORD;
+ elfMatch: DWORD;
+ elfReserved: DWORD;
+ elfVendorId: array[0..ELF_VENDOR_SIZE - 1] of Byte;
+ elfCulture: DWORD; { 0 for Latin }
+ elfPanose: TPanose;
+ end;
+ // tagEXTLOGFONTW}
+ tagEXTLOGFONTW = record
+ elfLogFont: TLogFontW;
+ elfFullName: array[0..LF_FULLFACESIZE - 1] of WideChar;
+ elfStyle: array[0..LF_FACESIZE - 1] of WideChar;
+ elfVersion: DWORD; { 0 for the first release of NT }
+ elfStyleSize: DWORD;
+ elfMatch: DWORD;
+ elfReserved: DWORD;
+ elfVendorId: array[0..ELF_VENDOR_SIZE - 1] of Byte;
+ elfCulture: DWORD; { 0 for Latin }
+ elfPanose: TPanose;
+ end;
+ // tagEXTLOGFONT}
+ tagEXTLOGFONT = tagEXTLOGFONTA;
+ TExtLogFontA = tagEXTLOGFONTA;
+ TExtLogFontW = tagEXTLOGFONTW;
+ TExtLogFont = TExtLogFontA;
+ // EXTLOGFONTA}
+ EXTLOGFONTA = tagEXTLOGFONTA;
+ // EXTLOGFONTW}
+ EXTLOGFONTW = tagEXTLOGFONTW;
+ // EXTLOGFONT}
+ EXTLOGFONT = EXTLOGFONTA;
+ PDisplayDeviceA = ^TDisplayDeviceA;
+ PDisplayDeviceW = ^TDisplayDeviceW;
+ PDisplayDevice = PDisplayDeviceA;
+ // _DISPLAY_DEVICEA}
+ _DISPLAY_DEVICEA = packed record
+ cb: DWORD;
+ DeviceName: array[0..31] of AnsiChar;
+ DeviceString: array[0..127] of AnsiChar;
+ StateFlags: DWORD;
+ end;
+ // _DISPLAY_DEVICEW}
+ _DISPLAY_DEVICEW = packed record
+ cb: DWORD;
+ DeviceName: array[0..31] of WideChar;
+ DeviceString: array[0..127] of WideChar;
+ StateFlags: DWORD;
+ end;
+ // _DISPLAY_DEVICE}
+ _DISPLAY_DEVICE = _DISPLAY_DEVICEA;
+ TDisplayDeviceA = _DISPLAY_DEVICEA;
+ TDisplayDeviceW = _DISPLAY_DEVICEW;
+ TDisplayDevice = TDisplayDeviceA;
+ POutlineTextmetricA = ^TOutlineTextmetricA;
+ POutlineTextmetricW = ^TOutlineTextmetricW;
+ POutlineTextmetric = POutlineTextmetricA;
+ // _OUTLINETEXTMETRICA}
+ _OUTLINETEXTMETRICA = record
+ otmSize: UINT;
+ otmTextMetrics: TTextMetricA;
+ otmFiller: Byte;
+ otmPanoseNumber: TPanose;
+ otmfsSelection: UINT;
+ otmfsType: UINT;
+ otmsCharSlopeRise: Integer;
+ otmsCharSlopeRun: Integer;
+ otmItalicAngle: Integer;
+ otmEMSquare: UINT;
+ otmAscent: Integer;
+ otmDescent: Integer;
+ otmLineGap: UINT;
+ otmsCapEmHeight: UINT;
+ otmsXHeight: UINT;
+ otmrcFontBox: TRect;
+ otmMacAscent: Integer;
+ otmMacDescent: Integer;
+ otmMacLineGap: UINT;
+ otmusMinimumPPEM: UINT;
+ otmptSubscriptSize: TPoint;
+ otmptSubscriptOffset: TPoint;
+ otmptSuperscriptSize: TPoint;
+ otmptSuperscriptOffset: TPoint;
+ otmsStrikeoutSize: UINT;
+ otmsStrikeoutPosition: Integer;
+ otmsUnderscoreSize: Integer;
+ otmsUnderscorePosition: Integer;
+ otmpFamilyName: PAnsiChar;
+ otmpFaceName: PAnsiChar;
+ otmpStyleName: PAnsiChar;
+ otmpFullName: PAnsiChar;
+ end;
+ // _OUTLINETEXTMETRICW}
+ _OUTLINETEXTMETRICW = record
+ otmSize: UINT;
+ otmTextMetrics: TTextMetricW;
+ otmFiller: Byte;
+ otmPanoseNumber: TPanose;
+ otmfsSelection: UINT;
+ otmfsType: UINT;
+ otmsCharSlopeRise: Integer;
+ otmsCharSlopeRun: Integer;
+ otmItalicAngle: Integer;
+ otmEMSquare: UINT;
+ otmAscent: Integer;
+ otmDescent: Integer;
+ otmLineGap: UINT;
+ otmsCapEmHeight: UINT;
+ otmsXHeight: UINT;
+ otmrcFontBox: TRect;
+ otmMacAscent: Integer;
+ otmMacDescent: Integer;
+ otmMacLineGap: UINT;
+ otmusMinimumPPEM: UINT;
+ otmptSubscriptSize: TPoint;
+ otmptSubscriptOffset: TPoint;
+ otmptSuperscriptSize: TPoint;
+ otmptSuperscriptOffset: TPoint;
+ otmsStrikeoutSize: UINT;
+ otmsStrikeoutPosition: Integer;
+ otmsUnderscoreSize: Integer;
+ otmsUnderscorePosition: Integer;
+ otmpFamilyName: PWideChar;
+ otmpFaceName: PWideChar;
+ otmpStyleName: PWideChar;
+ otmpFullName: PWideChar;
+ end;
+ // _OUTLINETEXTMETRIC}
+ _OUTLINETEXTMETRIC = _OUTLINETEXTMETRICA;
+ TOutlineTextmetricA = _OUTLINETEXTMETRICA;
+ TOutlineTextmetricW = _OUTLINETEXTMETRICW;
+ TOutlineTextmetric = TOutlineTextmetricA;
+ // OUTLINETEXTMETRICA}
+ OUTLINETEXTMETRICA = _OUTLINETEXTMETRICA;
+ // OUTLINETEXTMETRICW}
+ OUTLINETEXTMETRICW = _OUTLINETEXTMETRICW;
+ // OUTLINETEXTMETRIC}
+ OUTLINETEXTMETRIC = OUTLINETEXTMETRICA;
+ PPolyTextA = ^TPolyTextA;
+ PPolyTextW = ^TPolyTextW;
+ PPolyText = PPolyTextA;
+ // tagPOLYTEXTA}
+ tagPOLYTEXTA = packed record
+ x: Integer;
+ y: Integer;
+ n: UINT;
+ PAnsiChar: PAnsiChar;
+ uiFlags: UINT;
+ rcl: TRect;
+ pdx: PINT;
+ end;
+ // tagPOLYTEXTW}
+ tagPOLYTEXTW = packed record
+ x: Integer;
+ y: Integer;
+ n: UINT;
+ PAnsiChar: PWideChar;
+ uiFlags: UINT;
+ rcl: TRect;
+ pdx: PINT;
+ end;
+ // tagPOLYTEXT}
+ tagPOLYTEXT = tagPOLYTEXTA;
+ TPolyTextA = tagPOLYTEXTA;
+ TPolyTextW = tagPOLYTEXTW;
+ TPolyText = TPolyTextA;
+ // POLYTEXTA}
+ POLYTEXTA = tagPOLYTEXTA;
+ // POLYTEXTW}
+ POLYTEXTW = tagPOLYTEXTW;
+ // POLYTEXT}
+ POLYTEXT = POLYTEXTA;
+ PGCPResultsA = ^TGCPResultsA;
+ PGCPResultsW = ^TGCPResultsW;
+ PGCPResults = PGCPResultsA;
+ // tagGCP_RESULTSA}
+ tagGCP_RESULTSA = packed record
+ lStructSize: DWORD;
+ lpOutString: PAnsiChar;
+ lpOrder: PUINT;
+ lpDx: PINT;
+ lpCaretPos: PINT;
+ lpClass: PAnsiChar;
+ lpGlyphs: PUINT;
+ nGlyphs: UINT;
+ nMaxFit: Integer;
+ end;
+ // tagGCP_RESULTSW}
+ tagGCP_RESULTSW = packed record
+ lStructSize: DWORD;
+ lpOutString: PWideChar;
+ lpOrder: PUINT;
+ lpDx: PINT;
+ lpCaretPos: PINT;
+ lpClass: PWideChar;
+ lpGlyphs: PUINT;
+ nGlyphs: UINT;
+ nMaxFit: Integer;
+ end;
+ // tagGCP_RESULTS}
+ tagGCP_RESULTS = tagGCP_RESULTSA;
+ TGCPResultsA = tagGCP_RESULTSA;
+ TGCPResultsW = tagGCP_RESULTSW;
+ TGCPResults = TGCPResultsA;
+ // GCP_RESULTSA}
+ GCP_RESULTSA = tagGCP_RESULTSA;
+ // GCP_RESULTSW}
+ GCP_RESULTSW = tagGCP_RESULTSW;
+ // GCP_RESULTS}
+ GCP_RESULTS = GCP_RESULTSA;
+const
+ MM_MAX_AXES_NAMELEN = 16;
+ MM_MAX_NUMAXES = 16;
+type
+ PAxisInfoA = ^TAxisInfoA;
+ tagAXISINFOA = packed record
+ axMinValue: Longint;
+ axMaxValue: Longint;
+ axAxisName: array[0..MM_MAX_AXES_NAMELEN-1] of AnsiChar;
+ end;
+ TAxisInfoA = tagAXISINFOA;
+ PAxisInfoW = ^TAxisInfoW;
+ // tagAXISINFOW}
+ tagAXISINFOW = packed record
+ axMinValue: Longint;
+ axMaxValue: Longint;
+ axAxisName: array[0..MM_MAX_AXES_NAMELEN-1] of WideChar;
+ end;
+ TAxisInfoW = tagAXISINFOW;
+ PAxisInfo = PAxisInfoA;
+ PAxesListA = ^TAxesListA;
+ // tagAXESLISTA}
+ tagAXESLISTA = packed record
+ axlReserved: DWORD;
+ axlNumAxes: DWORD;
+ axlAxisInfo: array[0..MM_MAX_NUMAXES-1] of TAxisInfoA;
+ end;
+ TAxesListA = tagAXESLISTA;
+ PAxesListW = ^TAxesListW;
+ // tagAXESLISTW}
+ tagAXESLISTW = packed record
+ axlReserved: DWORD;
+ axlNumAxes: DWORD;
+ axlAxisInfo: array[0..MM_MAX_NUMAXES-1] of TAxisInfoW;
+ end;
+ TAxesListW = tagAXESLISTW;
+ PAxesList = PAxesListA;
+ PEnumLogFontExDVA = ^TEnumLogFontExDVA;
+ PDesignVector = ^TDesignVector;
+ tagDESIGNVECTOR = packed record
+ dvReserved: DWORD;
+ dvNumAxes: DWORD;
+ dvValues: array[0..MM_MAX_NUMAXES-1] of Longint;
+ end;
+ TDesignVector = tagDESIGNVECTOR;
+ tagENUMLOGFONTEXDVA = packed record
+ elfEnumLogfontEx: TEnumLogFontExA;
+ elfDesignVector: TDesignVector;
+ end;
+ TEnumLogFontExDVA = tagENUMLOGFONTEXDVA;
+ PEnumLogFontExDVW = ^TEnumLogFontExDVW;
+ // tagENUMLOGFONTEXDVW}
+ tagENUMLOGFONTEXDVW = packed record
+ elfEnumLogfontEx: TEnumLogFontExW;
+ elfDesignVector: TDesignVector;
+ end;
+ TEnumLogFontExDVW = tagENUMLOGFONTEXDVW;
+ PEnumLogFontExDV = PEnumLogFontExDVA;
+ PEnumTextMetricA = ^TEnumTextMetricA;
+ // tagENUMTEXTMETRICA}
+ tagENUMTEXTMETRICA = packed record
+ etmNewTextMetricEx: TNewTextMetricExA;
+ etmAxesList: TAxesListA;
+ end;
+ TEnumTextMetricA = tagENUMTEXTMETRICA;
+ PEnumTextMetricW = ^TEnumTextMetricW;
+ // tagENUMTEXTMETRICW}
+ tagENUMTEXTMETRICW = packed record
+ etmNewTextMetricEx: TNewTextMetricExW;
+ etmAxesList: TAxesListW;
+ end;
+ TEnumTextMetricW = tagENUMTEXTMETRICW;
+ PEnumTextMetric = PEnumTextMetricA;
+ PDocInfoA = ^TDocInfoA;
+ PDocInfoW = ^TDocInfoW;
+ PDocInfo = PDocInfoA;
+ // _DOCINFOA}
+ _DOCINFOA = packed record
+ cbSize: Integer;
+ lpszDocName: PAnsiChar;
+ lpszOutput: PAnsiChar;
+ lpszDatatype: PAnsiChar;
+ fwType: DWORD;
+ end;
+ // _DOCINFOW}
+ _DOCINFOW = packed record
+ cbSize: Integer;
+ lpszDocName: PWideChar;
+ lpszOutput: PWideChar;
+ lpszDatatype: PWideChar;
+ fwType: DWORD;
+ end;
+ // _DOCINFO}
+ _DOCINFO = _DOCINFOA;
+ TDocInfoA = _DOCINFOA;
+ TDocInfoW = _DOCINFOW;
+ TDocInfo = TDocInfoA;
+ // DOCINFOA}
+ DOCINFOA = _DOCINFOA;
+ // DOCINFOW}
+ DOCINFOW = _DOCINFOW;
+ // DOCINFO}
+ DOCINFO = DOCINFOA;
+ PCreateStructA = ^TCreateStructA;
+ PCreateStructW = ^TCreateStructW;
+ PCreateStruct = PCreateStructA;
+ // tagCREATESTRUCTA}
+ tagCREATESTRUCTA = packed record
+ lpCreateParams: Pointer;
+ hInstance: HINST;
+ hMenu: HMENU;
+ hwndParent: HWND;
+ cy: Integer;
+ cx: Integer;
+ y: Integer;
+ x: Integer;
+ style: Longint;
+ lpszName: PAnsiChar;
+ lpszClass: PAnsiChar;
+ dwExStyle: DWORD;
+ end;
+ // tagCREATESTRUCTW}
+ tagCREATESTRUCTW = packed record
+ lpCreateParams: Pointer;
+ hInstance: HINST;
+ hMenu: HMENU;
+ hwndParent: HWND;
+ cy: Integer;
+ cx: Integer;
+ y: Integer;
+ x: Integer;
+ style: Longint;
+ lpszName: PWideChar;
+ lpszClass: PWideChar;
+ dwExStyle: DWORD;
+ end;
+ // tagCREATESTRUCT}
+ tagCREATESTRUCT = tagCREATESTRUCTA;
+ TCreateStructA = tagCREATESTRUCTA;
+ TCreateStructW = tagCREATESTRUCTW;
+ TCreateStruct = TCreateStructA;
+ // CREATESTRUCTA}
+ CREATESTRUCTA = tagCREATESTRUCTA;
+ // CREATESTRUCTW}
+ CREATESTRUCTW = tagCREATESTRUCTW;
+ // CREATESTRUCT}
+ CREATESTRUCT = CREATESTRUCTA;
+ PWndClassExA = ^TWndClassExA;
+ PWndClassExW = ^TWndClassExW;
+ PWndClassEx = PWndClassExA;
+ // tagWNDCLASSEXA}
+ tagWNDCLASSEXA = packed record
+ cbSize: UINT;
+ style: UINT;
+ lpfnWndProc: TFNWndProc;
+ cbClsExtra: Integer;
+ cbWndExtra: Integer;
+ hInstance: HINST;
+ hIcon: HICON;
+ hCursor: HCURSOR;
+ hbrBackground: HBRUSH;
+ lpszMenuName: PAnsiChar;
+ lpszClassName: PAnsiChar;
+ hIconSm: HICON;
+ end;
+ // tagWNDCLASSEXW}
+ tagWNDCLASSEXW = packed record
+ cbSize: UINT;
+ style: UINT;
+ lpfnWndProc: TFNWndProc;
+ cbClsExtra: Integer;
+ cbWndExtra: Integer;
+ hInstance: HINST;
+ hIcon: HICON;
+ hCursor: HCURSOR;
+ hbrBackground: HBRUSH;
+ lpszMenuName: PWideChar;
+ lpszClassName: PWideChar;
+ hIconSm: HICON;
+ end;
+ // tagWNDCLASSEX}
+ tagWNDCLASSEX = tagWNDCLASSEXA;
+ TWndClassExA = tagWNDCLASSEXA;
+ TWndClassExW = tagWNDCLASSEXW;
+ TWndClassEx = TWndClassExA;
+ // WNDCLASSEXA}
+ WNDCLASSEXA = tagWNDCLASSEXA;
+ // WNDCLASSEXW}
+ WNDCLASSEXW = tagWNDCLASSEXW;
+ // WNDCLASSEX}
+ WNDCLASSEX = WNDCLASSEXA;
+
+ PWndClassA = ^TWndClassA;
+ PWndClassW = ^TWndClassW;
+ PWndClass = PWndClassA;
+ // tagWNDCLASSA}
+ tagWNDCLASSA = packed record
+ style: UINT;
+ lpfnWndProc: TFNWndProc;
+ cbClsExtra: Integer;
+ cbWndExtra: Integer;
+ hInstance: HINST;
+ hIcon: HICON;
+ hCursor: HCURSOR;
+ hbrBackground: HBRUSH;
+ lpszMenuName: PAnsiChar;
+ lpszClassName: PAnsiChar;
+ end;
+ // tagWNDCLASSW}
+ tagWNDCLASSW = packed record
+ style: UINT;
+ lpfnWndProc: TFNWndProc;
+ cbClsExtra: Integer;
+ cbWndExtra: Integer;
+ hInstance: HINST;
+ hIcon: HICON;
+ hCursor: HCURSOR;
+ hbrBackground: HBRUSH;
+ lpszMenuName: PWideChar;
+ lpszClassName: PWideChar;
+ end;
+ // tagWNDCLASS}
+ tagWNDCLASS = tagWNDCLASSA;
+ TWndClassA = tagWNDCLASSA;
+ TWndClassW = tagWNDCLASSW;
+ TWndClass = TWndClassA;
+ // WNDCLASSA}
+ WNDCLASSA = tagWNDCLASSA;
+ // WNDCLASSW}
+ WNDCLASSW = tagWNDCLASSW;
+ // WNDCLASS}
+ WNDCLASS = WNDCLASSA;
+ HDEVNOTIFY = Pointer;
+ PHDEVNOTIFY = ^HDEVNOTIFY;
+ ////
+ MakeIntAtom = MakeIntAtomA;
+ PWin32FindData = PWin32FindDataA;
+ TWin32FindData = TWin32FindDataA;
+ {$IFDEF _D3orHigher}
+ PHWProfileInfo = PHWProfileInfoA;
+ THWProfileInfo = THWProfileInfoA;
+ {$ENDIF}
+ POSVersionInfo = POSVersionInfoA;
+ TOSVersionInfo = TOSVersionInfoA;
+ PLogColorSpace = PLogColorSpaceA;
+ TLogColorSpace = TLogColorSpaceA;
+ PLogFont = PLogFontA;
+ TLogFont = TLogFontA;
+ PDeviceMode = PDeviceModeA;
+ TDeviceMode = TDeviceModeA;
+ TFNOldFontEnumProc = TFNOldFontEnumProcA;
+ TFNFontEnumProc = TFNFontEnumProcA;
+ MakeIntResource = PAnsiChar; // MakeIntResourceA;
+ //PMenuItemInfo = PMenuItemInfoA;
+ //TMenuItemInfo = TMenuItemInfoA;
+ //MENUITEMINFO = MENUITEMINFOA;
+ PMsgBoxParams = PMsgBoxParamsA;
+ TMsgBoxParams = TMsgBoxParamsA;
+ PMsgBoxParamsA = ^TMsgBoxParamsA;
+ PMsgBoxParamsW = ^TMsgBoxParamsW;
+ // tagMSGBOXPARAMSA}
+ tagMSGBOXPARAMSA = packed record
+ cbSize: UINT;
+ hwndOwner: HWND;
+ hInstance: HINST;
+ lpszText: PAnsiChar;
+ lpszCaption: PAnsiChar;
+ dwStyle: DWORD;
+ lpszIcon: PAnsiChar;
+ dwContextHelpId: DWORD;
+ lpfnMsgBoxCallback: TPRMsgBoxCallback;
+ dwLanguageId: DWORD;
+ end;
+ // tagMSGBOXPARAMSW}
+ tagMSGBOXPARAMSW = packed record
+ cbSize: UINT;
+ hwndOwner: HWND;
+ hInstance: HINST;
+ lpszText: PWideChar;
+ lpszCaption: PWideChar;
+ dwStyle: DWORD;
+ lpszIcon: PWideChar;
+ dwContextHelpId: DWORD;
+ lpfnMsgBoxCallback: TPRMsgBoxCallback;
+ dwLanguageId: DWORD;
+ end;
+ // tagMSGBOXPARAMS}
+ tagMSGBOXPARAMS = tagMSGBOXPARAMSA;
+ TMsgBoxParamsA = tagMSGBOXPARAMSA;
+ TMsgBoxParamsW = tagMSGBOXPARAMSW;
+ // MSGBOXPARAMSA}
+ MSGBOXPARAMSA = tagMSGBOXPARAMSA;
+ // MSGBOXPARAMSW}
+ MSGBOXPARAMSW = tagMSGBOXPARAMSW;
+ // MSGBOXPARAMS}
+ MSGBOXPARAMS = MSGBOXPARAMSA;
+ PMDICreateStruct = PMDICreateStructA;
+ TMDICreateStruct = TMDICreateStructA;
+ PMultiKeyHelp = PMultiKeyHelpA;
+ TMultiKeyHelp = TMultiKeyHelpA;
+ // HELPPOLY}
+ HELPPOLY = DWORD;
+ PMultiKeyHelpA = ^TMultiKeyHelpA;
+ PMultiKeyHelpW = ^TMultiKeyHelpW;
+ // tagMULTIKEYHELPA}
+ tagMULTIKEYHELPA = record
+ mkSize: DWORD;
+ mkKeylist: AnsiChar;
+ szKeyphrase: array[0..0] of AnsiChar;
+ end;
+ // tagMULTIKEYHELPW}
+ tagMULTIKEYHELPW = record
+ mkSize: DWORD;
+ mkKeylist: WideChar;
+ szKeyphrase: array[0..0] of WideChar;
+ end;
+ // tagMULTIKEYHELP}
+ tagMULTIKEYHELP = tagMULTIKEYHELPA;
+ TMultiKeyHelpA = tagMULTIKEYHELPA;
+ TMultiKeyHelpW = tagMULTIKEYHELPW;
+ // MULTIKEYHELPA}
+ MULTIKEYHELPA = tagMULTIKEYHELPA;
+ // MULTIKEYHELPW}
+ MULTIKEYHELPW = tagMULTIKEYHELPW;
+ // MULTIKEYHELP}
+ MULTIKEYHELP = MULTIKEYHELPA;
+ PHelpWinInfoA = ^THelpWinInfoA;
+ PHelpWinInfoW = ^THelpWinInfoW;
+ PHelpWinInfo = PHelpWinInfoA;
+ // tagHELPWININFOA}
+ tagHELPWININFOA = record
+ wStructSize: Integer;
+ x: Integer;
+ y: Integer;
+ dx: Integer;
+ dy: Integer;
+ wMax: Integer;
+ rgchMember: array[0..1] of AnsiChar;
+ end;
+ // tagHELPWININFOW}
+ tagHELPWININFOW = record
+ wStructSize: Integer;
+ x: Integer;
+ y: Integer;
+ dx: Integer;
+ dy: Integer;
+ wMax: Integer;
+ rgchMember: array[0..1] of WideChar;
+ end;
+ // tagHELPWININFO}
+ tagHELPWININFO = tagHELPWININFOA;
+ THelpWinInfoA = tagHELPWININFOA;
+ THelpWinInfoW = tagHELPWININFOW;
+ THelpWinInfo = THelpWinInfoA;
+ // HELPWININFOA}
+ HELPWININFOA = tagHELPWININFOA;
+ // HELPWININFOW}
+ HELPWININFOW = tagHELPWININFOW;
+ // HELPWININFO}
+ HELPWININFO = HELPWININFOA;
+ // tagNONCLIENTMETRICSA}
+ tagNONCLIENTMETRICSA = packed record
+ cbSize: UINT;
+ iBorderWidth: Integer;
+ iScrollWidth: Integer;
+ iScrollHeight: Integer;
+ iCaptionWidth: Integer;
+ iCaptionHeight: Integer;
+ lfCaptionFont: TLogFontA;
+ iSmCaptionWidth: Integer;
+ iSmCaptionHeight: Integer;
+ lfSmCaptionFont: TLogFontA;
+ iMenuWidth: Integer;
+ iMenuHeight: Integer;
+ lfMenuFont: TLogFontA;
+ lfStatusFont: TLogFontA;
+ lfMessageFont: TLogFontA;
+ end;
+ // tagNONCLIENTMETRICSW}
+ tagNONCLIENTMETRICSW = packed record
+ cbSize: UINT;
+ iBorderWidth: Integer;
+ iScrollWidth: Integer;
+ iScrollHeight: Integer;
+ iCaptionWidth: Integer;
+ iCaptionHeight: Integer;
+ lfCaptionFont: TLogFontW;
+ iSmCaptionWidth: Integer;
+ iSmCaptionHeight: Integer;
+ lfSmCaptionFont: TLogFontW;
+ iMenuWidth: Integer;
+ iMenuHeight: Integer;
+ lfMenuFont: TLogFontW;
+ lfStatusFont: TLogFontW;
+ lfMessageFont: TLogFontW;
+ end;
+ // tagNONCLIENTMETRICS}
+ tagNONCLIENTMETRICS = tagNONCLIENTMETRICSA;
+ TNonClientMetricsA = tagNONCLIENTMETRICSA;
+ TNonClientMetricsW = tagNONCLIENTMETRICSW;
+ PNonClientMetricsA = ^TNonClientMetricsA;
+ PNonClientMetrics = PNonClientMetricsA;
+ TNonClientMetrics = TNonClientMetricsA;
+ PNonClientMetricsW = ^TNonClientMetricsW;
+ // NONCLIENTMETRICSA}
+ NONCLIENTMETRICSA = tagNONCLIENTMETRICSA;
+ // NONCLIENTMETRICSW}
+ NONCLIENTMETRICSW = tagNONCLIENTMETRICSW;
+ // NONCLIENTMETRICS}
+ NONCLIENTMETRICS = NONCLIENTMETRICSA;
+ // tagICONMETRICSA}
+ tagICONMETRICSA = packed record
+ cbSize: UINT;
+ iHorzSpacing: Integer;
+ iVertSpacing: Integer;
+ iTitleWrap: Integer;
+ lfFont: TLogFontA;
+ end;
+ // tagICONMETRICSW}
+ tagICONMETRICSW = packed record
+ cbSize: UINT;
+ iHorzSpacing: Integer;
+ iVertSpacing: Integer;
+ iTitleWrap: Integer;
+ lfFont: TLogFontW;
+ end;
+ // tagICONMETRICS}
+ tagICONMETRICS = tagICONMETRICSA;
+ TIconMetricsA = tagICONMETRICSA;
+ TIconMetricsW = tagICONMETRICSW;
+ PIconMetricsA = ^TIconMetricsA;
+ PIconMetricsW = ^TIconMetricsW;
+ PIconMetrics = PIconMetricsA;
+ TIconMetrics = TIconMetricsA;
+ // ICONMETRICSA}
+ ICONMETRICSA = tagICONMETRICSA;
+ // ICONMETRICSW}
+ ICONMETRICSW = tagICONMETRICSW;
+ // ICONMETRICS}
+ ICONMETRICS = ICONMETRICSA;
+ PSerialKeys = PSerialKeysA;
+ TSerialKeys = TSerialKeysA;
+ PSerialKeysA = ^TSerialKeysA;
+ PSerialKeysW = ^TSerialKeysW;
+ // tagSERIALKEYSA}
+ tagSERIALKEYSA = packed record
+ cbSize: UINT;
+ dwFlags: DWORD;
+ lpszActivePort: PAnsiChar;
+ lpszPort: PAnsiChar;
+ iBaudRate: UINT;
+ iPortState: UINT;
+ iActive: UINT;
+ end;
+ // tagSERIALKEYSW}
+ tagSERIALKEYSW = packed record
+ cbSize: UINT;
+ dwFlags: DWORD;
+ lpszActivePort: PWideChar;
+ lpszPort: PWideChar;
+ iBaudRate: UINT;
+ iPortState: UINT;
+ iActive: UINT;
+ end;
+ // tagSERIALKEYS}
+ tagSERIALKEYS = tagSERIALKEYSA;
+ TSerialKeysA = tagSERIALKEYSA;
+ TSerialKeysW = tagSERIALKEYSW;
+ // SERIALKEYSA}
+ SERIALKEYSA = tagSERIALKEYSA;
+ // SERIALKEYSW}
+ SERIALKEYSW = tagSERIALKEYSW;
+ // SERIALKEYS}
+ SERIALKEYS = SERIALKEYSA;
+ PHighContrast = PHighContrastA;
+ THighContrast = THighContrastA;
+ PHighContrastA = ^THighContrastA;
+ PHighContrastW = ^THighContrastW;
+ // tagHIGHCONTRASTA}
+ tagHIGHCONTRASTA = packed record
+ cbSize: UINT;
+ dwFlags: DWORD;
+ lpszDefaultScheme: PAnsiChar;
+ end;
+ // tagHIGHCONTRASTW}
+ tagHIGHCONTRASTW = packed record
+ cbSize: UINT;
+ dwFlags: DWORD;
+ lpszDefaultScheme: PWideChar;
+ end;
+ // tagHIGHCONTRAST}
+ tagHIGHCONTRAST = tagHIGHCONTRASTA;
+ THighContrastA = tagHIGHCONTRASTA;
+ THighContrastW = tagHIGHCONTRASTW;
+ // HIGHCONTRASTA}
+ HIGHCONTRASTA = tagHIGHCONTRASTA;
+ // HIGHCONTRASTW}
+ HIGHCONTRASTW = tagHIGHCONTRASTW;
+ // HIGHCONTRAST}
+ HIGHCONTRAST = HIGHCONTRASTA;
+ PSoundsEntry = PSoundsEntryA;
+ TSoundsEntry = TSoundsEntryA;
+ PSoundsEntryA = ^TSoundsEntryA;
+ PSoundsEntryW = ^TSoundsEntryW;
+ // tagSOUNDSENTRYA}
+ tagSOUNDSENTRYA = packed record
+ cbSize: UINT;
+ dwFlags: DWORD;
+ iFSTextEffect: DWORD;
+ iFSTextEffectMSec: DWORD;
+ iFSTextEffectColorBits: DWORD;
+ iFSGrafEffect: DWORD;
+ iFSGrafEffectMSec: DWORD;
+ iFSGrafEffectColor: DWORD;
+ iWindowsEffect: DWORD;
+ iWindowsEffectMSec: DWORD;
+ lpszWindowsEffectDLL: PAnsiChar;
+ iWindowsEffectOrdinal: DWORD;
+ end;
+ // tagSOUNDSENTRYW}
+ tagSOUNDSENTRYW = packed record
+ cbSize: UINT;
+ dwFlags: DWORD;
+ iFSTextEffect: DWORD;
+ iFSTextEffectMSec: DWORD;
+ iFSTextEffectColorBits: DWORD;
+ iFSGrafEffect: DWORD;
+ iFSGrafEffectMSec: DWORD;
+ iFSGrafEffectColor: DWORD;
+ iWindowsEffect: DWORD;
+ iWindowsEffectMSec: DWORD;
+ lpszWindowsEffectDLL: PWideChar;
+ iWindowsEffectOrdinal: DWORD;
+ end;
+ // tagSOUNDSENTRY}
+ tagSOUNDSENTRY = tagSOUNDSENTRYA;
+ TSoundsEntryA = tagSOUNDSENTRYA;
+ TSoundsEntryW = tagSOUNDSENTRYW;
+ // SOUNDSENTRYA}
+ SOUNDSENTRYA = tagSOUNDSENTRYA;
+ // SOUNDSENTRYW}
+ SOUNDSENTRYW = tagSOUNDSENTRYW;
+ // SOUNDSENTRY}
+ SOUNDSENTRY = SOUNDSENTRYA;
+ PNumberFmt = PNumberFmtA;
+ TNumberFmt = TNumberFmtA;
+ PNumberFmtA = ^TNumberFmtA;
+ PNumberFmtW = ^TNumberFmtW;
+ // _numberfmtA}
+ _numberfmtA = packed record
+ NumDigits: UINT; { number of decimal digits }
+ LeadingZero: UINT; { if leading zero in decimal fields }
+ Grouping: UINT; { group size left of decimal }
+ lpDecimalSep: PAnsiChar; { ptr to decimal separator AnsiString }
+ lpThousandSep: PAnsiChar; { ptr to thousand separator AnsiString }
+ NegativeOrder: UINT; { negative number ordering }
+ end;
+ // _numberfmtW}
+ _numberfmtW = packed record
+ NumDigits: UINT; { number of decimal digits }
+ LeadingZero: UINT; { if leading zero in decimal fields }
+ Grouping: UINT; { group size left of decimal }
+ lpDecimalSep: PWideChar; { ptr to decimal separator WideString }
+ lpThousandSep: PWideChar; { ptr to thousand separator WideString }
+ NegativeOrder: UINT; { negative number ordering }
+ end;
+ // _numberfmt}
+ _numberfmt = _numberfmtA;
+ TNumberFmtA = _numberfmtA;
+ TNumberFmtW = _numberfmtW;
+ // NUMBERFMTA}
+ NUMBERFMTA = _numberfmtA;
+ // NUMBERFMTW}
+ NUMBERFMTW = _numberfmtW;
+ // NUMBERFMT}
+ NUMBERFMT = NUMBERFMTA;
+ PCurrencyFmt = PCurrencyFmtA;
+ PCurrencyFmtA = ^TCurrencyFmtA;
+ PCurrencyFmtW = ^TCurrencyFmtW;
+ // _currencyfmtA}
+ _currencyfmtA = packed record
+ NumDigits: UINT; { number of decimal digits }
+ LeadingZero: UINT; { if leading zero in decimal fields }
+ Grouping: UINT; { group size left of decimal }
+ lpDecimalSep: PAnsiChar; { ptr to decimal separator AnsiString }
+ lpThousandSep: PAnsiChar; { ptr to thousand separator AnsiString }
+ NegativeOrder: UINT; { negative currency ordering }
+ PositiveOrder: UINT; { positive currency ordering }
+ lpCurrencySymbol: PAnsiChar; { ptr to currency symbol AnsiString }
+ end;
+ // _currencyfmtW}
+ _currencyfmtW = packed record
+ NumDigits: UINT; { number of decimal digits }
+ LeadingZero: UINT; { if leading zero in decimal fields }
+ Grouping: UINT; { group size left of decimal }
+ lpDecimalSep: PWideChar; { ptr to decimal separator WideString }
+ lpThousandSep: PWideChar; { ptr to thousand separator WideString }
+ NegativeOrder: UINT; { negative currency ordering }
+ PositiveOrder: UINT; { positive currency ordering }
+ lpCurrencySymbol: PWideChar; { ptr to currency symbol WideString }
+ end;
+ // _currencyfmt}
+ _currencyfmt = _currencyfmtA;
+ TCurrencyFmtA = _currencyfmtA;
+ TCurrencyFmtW = _currencyfmtW;
+ TCurrencyFmt = TCurrencyFmtA;
+ // CURRENCYFMTA}
+ CURRENCYFMTA = _currencyfmtA;
+ // CURRENCYFMTW}
+ CURRENCYFMTW = _currencyfmtW;
+ // CURRENCYFMT}
+ CURRENCYFMT = CURRENCYFMTA;
+ PPValue = PPValueA;
+{ Provider supplied value/context.}
+ PPValueA = ^TPValueA;
+ PPValueW = ^TPValueW;
+ // pvalueA}
+ pvalueA = packed record
+ pv_valuename: PAnsiChar; { The value name pointer }
+ pv_valuelen: BOOL;
+ pv_value_context: Pointer;
+ pv_type: DWORD;
+ end;
+ // pvalueW}
+ pvalueW = packed record
+ pv_valuename: PWideChar; { The value name pointer }
+ pv_valuelen: BOOL;
+ pv_value_context: Pointer;
+ pv_type: DWORD;
+ end;
+ // pvalue}
+ pvalue = pvalueA;
+ TPValueA = pvalueA;
+ TPValueW = pvalueW;
+ TPValue = TPValueA;
+ PValueEnt = PValueEntA;
+ TValueEnt = TValueEntA;
+ PValueEntA = ^TValueEntA;
+ PValueEntW = ^TValueEntW;
+ // value_entA}
+ value_entA = packed record
+ ve_valuename: PAnsiChar;
+ ve_valuelen: DWORD;
+ ve_valueptr: DWORD;
+ ve_type: DWORD;
+ end;
+ // value_entW}
+ value_entW = packed record
+ ve_valuename: PWideChar;
+ ve_valuelen: DWORD;
+ ve_valueptr: DWORD;
+ ve_type: DWORD;
+ end;
+ // value_ent}
+ value_ent = value_entA;
+ TValueEntA = value_entA;
+ TValueEntW = value_entW;
+ // VALENTA}
+ VALENTA = value_entA;
+ // VALENTW}
+ VALENTW = value_entW;
+ // VALENT}
+ VALENT = VALENTA;
+ TValEnt = TValueEnt;
+ PValEnt = PValueEnt;
+ PNetResource = PNetResourceA;
+ TNetResource = TNetResourceA;
+ PNetResourceA = ^TNetResourceA;
+ PNetResourceW = ^TNetResourceW;
+ // _NETRESOURCEA}
+ _NETRESOURCEA = packed record
+ dwScope: DWORD;
+ dwType: DWORD;
+ dwDisplayType: DWORD;
+ dwUsage: DWORD;
+ lpLocalName: PAnsiChar;
+ lpRemoteName: PAnsiChar;
+ lpComment: PAnsiChar;
+ lpProvider: PAnsiChar;
+ end;
+ // _NETRESOURCEW}
+ _NETRESOURCEW = packed record
+ dwScope: DWORD;
+ dwType: DWORD;
+ dwDisplayType: DWORD;
+ dwUsage: DWORD;
+ lpLocalName: PWideChar;
+ lpRemoteName: PWideChar;
+ lpComment: PWideChar;
+ lpProvider: PWideChar;
+ end;
+ // _NETRESOURCE}
+ _NETRESOURCE = _NETRESOURCEA;
+ TNetResourceA = _NETRESOURCEA;
+ TNetResourceW = _NETRESOURCEW;
+ // NETRESOURCEA}
+ NETRESOURCEA = _NETRESOURCEA;
+ // NETRESOURCEW}
+ NETRESOURCEW = _NETRESOURCEW;
+ // NETRESOURCE}
+ NETRESOURCE = NETRESOURCEA;
+ PDiscDlgStruct = PDiscDlgStructA;
+ PDiscDlgStructA = ^TDiscDlgStructA;
+ PDiscDlgStructW = ^TDiscDlgStructW;
+ // _DISCDLGSTRUCTA}
+ _DISCDLGSTRUCTA = packed record
+ cbStructure: DWORD; { size of this structure in bytes }
+ hwndOwner: HWND; { owner window for the dialog }
+ lpLocalName: PAnsiChar; { local device name }
+ lpRemoteName: PAnsiChar; { network resource name }
+ dwFlags: DWORD;
+ end;
+ // _DISCDLGSTRUCTW}
+ _DISCDLGSTRUCTW = packed record
+ cbStructure: DWORD; { size of this structure in bytes }
+ hwndOwner: HWND; { owner window for the dialog }
+ lpLocalName: PWideChar; { local device name }
+ lpRemoteName: PWideChar; { network resource name }
+ dwFlags: DWORD;
+ end;
+ // _DISCDLGSTRUCT}
+ _DISCDLGSTRUCT = _DISCDLGSTRUCTA;
+ TDiscDlgStructA = _DISCDLGSTRUCTA;
+ TDiscDlgStructW = _DISCDLGSTRUCTW;
+ TDiscDlgStruct = TDiscDlgStructA;
+ // DISCDLGSTRUCTA}
+ DISCDLGSTRUCTA = _DISCDLGSTRUCTA;
+ // DISCDLGSTRUCTW}
+ DISCDLGSTRUCTW = _DISCDLGSTRUCTW;
+ // DISCDLGSTRUCT}
+ DISCDLGSTRUCT = DISCDLGSTRUCTA;
+ PUniversalNameInfo = PUniversalNameInfoA;
+ TUniversalNameInfo = TUniversalNameInfoA;
+ PUniversalNameInfoA = ^TUniversalNameInfoA;
+ PUniversalNameInfoW = ^TUniversalNameInfoW;
+ // _UNIVERSAL_NAME_INFOA}
+ _UNIVERSAL_NAME_INFOA = packed record
+ lpUniversalName: PAnsiChar;
+ end;
+ // _UNIVERSAL_NAME_INFOW}
+ _UNIVERSAL_NAME_INFOW = packed record
+ lpUniversalName: PWideChar;
+ end;
+ // _UNIVERSAL_NAME_INFO}
+ _UNIVERSAL_NAME_INFO = _UNIVERSAL_NAME_INFOA;
+ TUniversalNameInfoA = _UNIVERSAL_NAME_INFOA;
+ TUniversalNameInfoW = _UNIVERSAL_NAME_INFOW;
+ // UNIVERSAL_NAME_INFOA}
+ UNIVERSAL_NAME_INFOA = _UNIVERSAL_NAME_INFOA;
+ // UNIVERSAL_NAME_INFOW}
+ UNIVERSAL_NAME_INFOW = _UNIVERSAL_NAME_INFOW;
+ // UNIVERSAL_NAME_INFO}
+ UNIVERSAL_NAME_INFO = UNIVERSAL_NAME_INFOA;
+ PRemoteNameInfo = PRemoteNameInfoA;
+ TRemoteNameInfo = TRemoteNameInfoA;
+ PRemoteNameInfoA = ^TRemoteNameInfoA;
+ PRemoteNameInfoW = ^TRemoteNameInfoW;
+ // _REMOTE_NAME_INFOA}
+ _REMOTE_NAME_INFOA = packed record
+ lpUniversalName: PAnsiChar;
+ lpConnectionName: PAnsiChar;
+ lpRemainingPath: PAnsiChar;
+ end;
+ // _REMOTE_NAME_INFOW}
+ _REMOTE_NAME_INFOW = packed record
+ lpUniversalName: PWideChar;
+ lpConnectionName: PWideChar;
+ lpRemainingPath: PWideChar;
+ end;
+ // _REMOTE_NAME_INFO}
+ _REMOTE_NAME_INFO = _REMOTE_NAME_INFOA;
+ TRemoteNameInfoA = _REMOTE_NAME_INFOA;
+ TRemoteNameInfoW = _REMOTE_NAME_INFOW;
+ // REMOTE_NAME_INFOA}
+ REMOTE_NAME_INFOA = _REMOTE_NAME_INFOA;
+ // REMOTE_NAME_INFOW}
+ REMOTE_NAME_INFOW = _REMOTE_NAME_INFOW;
+ // REMOTE_NAME_INFO}
+ REMOTE_NAME_INFO = REMOTE_NAME_INFOA;
+ AUDIT_EVENT_TYPE = DWORD;
+ {$IFDEF _D3orHigher}
+ PObjectTypeList = ^TObjectTypeList;
+ _OBJECT_TYPE_LIST = record
+ Level: WORD;
+ Sbz: WORD;
+ ObjectType: PGUID;
+ end;
+ TObjectTypeList = _OBJECT_TYPE_LIST;
+ OBJECT_TYPE_LIST = _OBJECT_TYPE_LIST;
+ {$ENDIF _D3orHigher}
+ { Alt-Tab Switch window information. }
+ PAltTabInfo = ^TAltTabInfo;
+ tagALTTABINFO = packed record
+ cbSize: DWORD;
+ cItems: Integer;
+ cColumns: Integer;
+ cRows: Integer;
+ iColFocus: Integer;
+ iRowFocus: Integer;
+ cxItem: Integer;
+ cyItem: Integer;
+ ptStart: TPoint;
+ end;
+ TAltTabInfo = tagALTTABINFO;
+
+function AbortSystemShutdown(lpMachineName: PKOLChar): BOOL; stdcall;
+function AccessCheckAndAuditAlarm(SubsystemName: PKOLChar;
+ HandleId: Pointer; ObjectTypeName, ObjectName: PKOLChar;
+ SecurityDescriptor: PSecurityDescriptor; DesiredAccess: DWORD;
+ const GenericMapping: TGenericMapping; ObjectCreation: BOOL;
+ var GrantedAccess: DWORD; var AccessStatus, pfGenerateOnClose: BOOL): BOOL; stdcall;
+{$IFDEF _D4orHigher}
+function AccessCheckByTypeAndAuditAlarm(SubsystemName: PKOLChar;
+ HandleId: Pointer; ObjectTypeName, ObjectName: PKOLChar;
+ SecurityDescriptor: PSecurityDescriptor; PrincipalSelfSid: PSID; DesiredAccess: DWORD;
+ AuditType: AUDIT_EVENT_TYPE; Flags: DWORD; ObjectTypeList: PObjectTypeList;
+ ObjectTypeListLength: DWORD; const GenericMapping: TGenericMapping; ObjectCreation: BOOL;
+ var GrantedAccess: DWORD; var AccessStatus, pfGenerateOnClose: BOOL): BOOL; stdcall;
+function AccessCheckByTypeResultListAndAuditAlarm(SubsystemName: PKOLChar;
+ HandleId: Pointer; ObjectTypeName, ObjectName: PKOLChar;
+ SecurityDescriptor: PSecurityDescriptor; PrincipalSelfSid: PSID; DesiredAccess: DWORD;
+ AuditType: AUDIT_EVENT_TYPE; Flags: DWORD; ObjectTypeList: PObjectTypeList;
+ ObjectTypeListLength: DWORD; const GenericMapping: TGenericMapping; ObjectCreation: BOOL;
+ var GrantedAccess: DWORD; var AccessStatusList: DWORD; var pfGenerateOnClose: BOOL): BOOL; stdcall;
+{$ENDIF _D4orHigher}
+function BackupEventLog(hEventLog: THandle; lpBackupFileName: PKOLChar): BOOL; stdcall;
+function ClearEventLog(hEventLog: THandle; lpBackupFileName: PKOLChar): BOOL; stdcall;
+function CreateProcessAsUser(hToken: THandle; lpApplicationName: PKOLChar;
+ lpCommandLine: PKOLChar; lpProcessAttributes: PSecurityAttributes;
+ lpThreadAttributes: PSecurityAttributes; bInheritHandles: BOOL;
+ dwCreationFlags: DWORD; lpEnvironment: Pointer; lpCurrentDirectory: PKOLChar;
+ const lpStartupInfo: TStartupInfo; var lpProcessInformation: TProcessInformation): BOOL; stdcall;
+{$IFDEF _D3orHigher}
+function GetCurrentHwProfile(var lpHwProfileInfo: THWProfileInfo): BOOL; stdcall;
+{$ENDIF _D3orHigher}
+function GetFileSecurity(lpFileName: PKOLChar; RequestedInformation: SECURITY_INFORMATION;
+ pSecurityDescriptor: PSecurityDescriptor; nLength: DWORD; var lpnLengthNeeded: DWORD): BOOL; stdcall;
+function GetUserName(lpBuffer: PKOLChar; var nSize: DWORD): BOOL; stdcall;
+function InitiateSystemShutdown(lpMachineName, lpMessage: PKOLChar;
+ dwTimeout: DWORD; bForceAppsClosed, bRebootAfterShutdown: BOOL): BOOL; stdcall;
+function LogonUser(lpszUsername, lpszDomain, lpszPassword: PKOLChar;
+ dwLogonType, dwLogonProvider: DWORD; var phToken: THandle): BOOL; stdcall;
+function LookupAccountName(lpSystemName, lpAccountName: PKOLChar;
+ Sid: PSID; var cbSid: DWORD; ReferencedDomainName: PKOLChar;
+ var cbReferencedDomainName: DWORD; var peUse: SID_NAME_USE): BOOL; stdcall;
+function LookupAccountSid(lpSystemName: PKOLChar; Sid: PSID;
+ Name: PKOLChar; var cbName: DWORD; ReferencedDomainName: PKOLChar;
+ var cbReferencedDomainName: DWORD; var peUse: SID_NAME_USE): BOOL; stdcall;
+function LookupPrivilegeDisplayName(lpSystemName, lpName: PKOLChar;
+ lpDisplayName: PKOLChar; var cbDisplayName, lpLanguageId: DWORD): BOOL; stdcall;
+function LookupPrivilegeName(lpSystemName: PKOLChar;
+ var lpLuid: TLargeInteger; lpName: PKOLChar; var cbName: DWORD): BOOL; stdcall;
+function LookupPrivilegeValue(lpSystemName, lpName: PKOLChar;
+ var lpLuid: TLargeInteger): BOOL; stdcall;
+function ObjectCloseAuditAlarm(SubsystemName: PKOLChar;
+ HandleId: Pointer; GenerateOnClose: BOOL): BOOL; stdcall;
+function ObjectDeleteAuditAlarm(SubsystemName: PKOLChar;
+ HandleId: Pointer; GenerateOnClose: BOOL): BOOL; stdcall;
+function ObjectOpenAuditAlarm(SubsystemName: PKOLChar; HandleId: Pointer;
+ ObjectTypeName: PKOLChar; ObjectName: PKOLChar; pSecurityDescriptor: PSecurityDescriptor;
+ ClientToken: THandle; DesiredAccess, GrantedAccess: DWORD;
+ var Privileges: TPrivilegeSet; ObjectCreation, AccessGranted: BOOL;
+ var GenerateOnClose: BOOL): BOOL; stdcall;
+function ObjectPrivilegeAuditAlarm(SubsystemName: PKOLChar;
+ HandleId: Pointer; ClientToken: THandle; DesiredAccess: DWORD;
+ var Privileges: TPrivilegeSet; AccessGranted: BOOL): BOOL; stdcall;
+function OpenBackupEventLog(lpUNCServerName, lpFileName: PKOLChar): THandle; stdcall;
+function OpenEventLog(lpUNCServerName, lpSourceName: PKOLChar): THandle; stdcall;
+function PrivilegedServiceAuditAlarm(SubsystemName, ServiceName: PKOLChar;
+ ClientToken: THandle; var Privileges: TPrivilegeSet; AccessGranted: BOOL): BOOL; stdcall;
+function ReadEventLog(hEventLog: THandle; dwReadFlags, dwRecordOffset: DWORD;
+ lpBuffer: Pointer; nNumberOfBytesToRead: DWORD;
+ var pnBytesRead, pnMinNumberOfBytesNeeded: DWORD): BOOL; stdcall;
+function RegConnectRegistry(lpMachineName: PKOLChar; hKey: HKEY;
+ var phkResult: HKEY): Longint; stdcall;
+function RegCreateKey(hKey: HKEY; lpSubKey: PKOLChar;
+ var phkResult: HKEY): Longint; stdcall;
+function RegCreateKeyEx(hKey: HKEY; lpSubKey: PKOLChar;
+ Reserved: DWORD; lpClass: PKOLChar; dwOptions: DWORD; samDesired: REGSAM;
+ lpSecurityAttributes: PSecurityAttributes; var phkResult: HKEY;
+ lpdwDisposition: PDWORD): Longint; stdcall;
+function RegDeleteKey(hKey: HKEY; lpSubKey: PKOLChar): Longint; stdcall;
+function RegDeleteValue(hKey: HKEY; lpValueName: PKOLChar): Longint; stdcall;
+function RegEnumKeyEx(hKey: HKEY; dwIndex: DWORD; lpName: PKOLChar;
+ var lpcbName: DWORD; lpReserved: Pointer; lpClass: PKOLChar;
+ lpcbClass: PDWORD; lpftLastWriteTime: PFileTime): Longint; stdcall;
+function RegEnumKey(hKey: HKEY; dwIndex: DWORD; lpName: PKOLChar; cbName: DWORD): Longint; stdcall;
+function RegEnumValue(hKey: HKEY; dwIndex: DWORD; lpValueName: PKOLChar;
+ var lpcbValueName: DWORD; lpReserved: Pointer; lpType: PDWORD;
+ lpData: PByte; lpcbData: PDWORD): Longint; stdcall;
+function RegLoadKey(hKey: HKEY; lpSubKey, lpFile: PKOLChar): Longint; stdcall;
+function RegOpenKey(hKey: HKEY; lpSubKey: PKOLChar; var phkResult: HKEY): Longint; stdcall;
+function RegOpenKeyEx(hKey: HKEY; lpSubKey: PKOLChar;
+ ulOptions: DWORD; samDesired: REGSAM; var phkResult: HKEY): Longint; stdcall;
+function RegQueryInfoKey(hKey: HKEY; lpClass: PKOLChar;
+ lpcbClass: PDWORD; lpReserved: Pointer;
+ lpcSubKeys, lpcbMaxSubKeyLen, lpcbMaxClassLen, lpcValues,
+ lpcbMaxValueNameLen, lpcbMaxValueLen, lpcbSecurityDescriptor: PDWORD;
+ lpftLastWriteTime: PFileTime): Longint; stdcall;
+function RegQueryMultipleValues(hKey: HKEY; var ValList;
+ NumVals: DWORD; lpValueBuf: PKOLChar; var ldwTotsize: DWORD): Longint; stdcall;
+function RegQueryValue(hKey: HKEY; lpSubKey: PKOLChar;
+ lpValue: PKOLChar; var lpcbValue: Longint): Longint; stdcall;
+function RegQueryValueEx(hKey: HKEY; lpValueName: PKOLChar;
+ lpReserved: Pointer; lpType: PDWORD; lpData: PByte; lpcbData: PDWORD): Longint; stdcall;
+function RegReplaceKey(hKey: HKEY; lpSubKey: PKOLChar;
+ lpNewFile: PKOLChar; lpOldFile: PKOLChar): Longint; stdcall;
+function RegRestoreKey(hKey: HKEY; lpFile: PKOLChar; dwFlags: DWORD): Longint; stdcall;
+function RegSaveKey(hKey: HKEY; lpFile: PKOLChar;
+ lpSecurityAttributes: PSecurityAttributes): Longint; stdcall;
+function RegSetValue(hKey: HKEY; lpSubKey: PKOLChar;
+ dwType: DWORD; lpData: PKOLChar; cbData: DWORD): Longint; stdcall;
+function RegSetValueEx(hKey: HKEY; lpValueName: PKOLChar;
+ Reserved: DWORD; dwType: DWORD; lpData: Pointer; cbData: DWORD): Longint; stdcall;
+function RegUnLoadKey(hKey: HKEY; lpSubKey: PKOLChar): Longint; stdcall;
+function RegisterEventSource(lpUNCServerName, lpSourceName: PKOLChar): THandle; stdcall;
+function ReportEvent(hEventLog: THandle; wType, wCategory: Word;
+ dwEventID: DWORD; lpUserSid: Pointer; wNumStrings: Word;
+ dwDataSize: DWORD; lpStrings, lpRawData: Pointer): BOOL; stdcall;
+function SetFileSecurity(lpFileName: PKOLChar; SecurityInformation: SECURITY_INFORMATION;
+ pSecurityDescriptor: PSecurityDescriptor): BOOL; stdcall;
+function AddAtom(lpString: PKOLChar): ATOM; stdcall;
+function BeginUpdateResource(pFileName: PKOLChar; bDeleteExistingResources: BOOL): THandle; stdcall;
+function BuildCommDCB(lpDef: PKOLChar; var lpDCB: TDCB): BOOL; stdcall;
+function BuildCommDCBAndTimeouts(lpDef: PKOLChar; var lpDCB: TDCB;
+ var lpCommTimeouts: TCommTimeouts): BOOL; stdcall;
+function CallNamedPipe(lpNamedPipeName: PKOLChar; lpInBuffer: Pointer;
+ nInBufferSize: DWORD; lpOutBuffer: Pointer; nOutBufferSize: DWORD;
+ var lpBytesRead: DWORD; nTimeOut: DWORD): BOOL; stdcall;
+function CommConfigDialog(lpszName: PKOLChar; hWnd: HWND; var lpCC: TCommConfig): BOOL; stdcall;
+function CompareString(Locale: LCID; dwCmpFlags: DWORD; lpString1: PKOLChar;
+ cchCount1: Integer; lpString2: PKOLChar; cchCount2: Integer): Integer; stdcall;
+function CopyFile(lpExistingFileName, lpNewFileName: PKOLChar; bFailIfExists: BOOL): BOOL; stdcall;
+{$IFDEF _D3orHigher}
+function CopyFileEx(lpExistingFileName, lpNewFileName: PKOLChar;
+ lpProgressRoutine: TFNProgressRoutine; lpData: Pointer; pbCancel: PBool;
+ dwCopyFlags: DWORD): BOOL; stdcall;
+{$ENDIF _D3orHigher}
+function CreateDirectory(lpPathName: PKOLChar;
+ lpSecurityAttributes: PSecurityAttributes): BOOL; stdcall;
+function CreateDirectoryEx(lpTemplateDirectory, lpNewDirectory: PKOLChar;
+ lpSecurityAttributes: PSecurityAttributes): BOOL; stdcall;
+function CreateEvent(lpEventAttributes: PSecurityAttributes;
+ bManualReset, bInitialState: BOOL; lpName: PKOLChar): THandle; stdcall;
+function CreateFile(lpFileName: PKOLChar; dwDesiredAccess, dwShareMode: DWORD;
+ lpSecurityAttributes: PSecurityAttributes; dwCreationDisposition, dwFlagsAndAttributes: DWORD;
+ hTemplateFile: THandle): THandle; stdcall;
+function CreateFileMapping(hFile: THandle; lpFileMappingAttributes: PSecurityAttributes;
+ flProtect, dwMaximumSizeHigh, dwMaximumSizeLow: DWORD; lpName: PKOLChar): THandle; stdcall;
+function CreateHardLink(lpFileName, lpExistingFileName: PKOLChar;
+ lpSecurityAttributes: PSecurityAttributes): BOOL; stdcall;
+function CreateMailslot(lpName: PKOLChar; nMaxMessageSize: DWORD;
+ lReadTimeout: DWORD; lpSecurityAttributes: PSecurityAttributes): THandle; stdcall;
+function CreateNamedPipe(lpName: PKOLChar;
+ dwOpenMode, dwPipeMode, nMaxInstances, nOutBufferSize, nInBufferSize, nDefaultTimeOut: DWORD;
+ lpSecurityAttributes: PSecurityAttributes): THandle; stdcall;
+function CreateProcess(lpApplicationName: PKOLChar; lpCommandLine: PKOLChar;
+ lpProcessAttributes, lpThreadAttributes: PSecurityAttributes;
+ bInheritHandles: BOOL; dwCreationFlags: DWORD; lpEnvironment: Pointer;
+ lpCurrentDirectory: PKOLChar; const lpStartupInfo: TStartupInfo;
+ var lpProcessInformation: TProcessInformation): BOOL; stdcall;
+function CreateSemaphore(lpSemaphoreAttributes: PSecurityAttributes;
+ lInitialCount, lMaximumCount: Longint; lpName: PKOLChar): THandle; stdcall;
+function CreateWaitableTimer(lpTimerAttributes: PSecurityAttributes; bManualReset: BOOL; lpTimerName: PKOLChar): THandle; stdcall;
+function DefineDosDevice(dwFlags: DWORD; lpDeviceName, lpTargetPath: PKOLChar): BOOL; stdcall;
+function DeleteFile(lpFileName: PKOLChar): BOOL; stdcall;
+function EndUpdateResource(hUpdate: THandle; fDiscard: BOOL): BOOL; stdcall;
+function EnumCalendarInfo(lpCalInfoEnumProc: TFNCalInfoEnumProc; Locale: LCID;
+ Calendar: CALID; CalType: CALTYPE): BOOL; stdcall;
+function EnumDateFormats(lpDateFmtEnumProc: TFNDateFmtEnumProc;
+ Locale: LCID; dwFlags: DWORD): BOOL; stdcall;
+function EnumResourceLanguages(hModule: HMODULE; lpType, lpName: PKOLChar;
+ lpEnumFunc: ENUMRESLANGPROC; lParam: Longint): BOOL; stdcall;
+function EnumResourceNames(hModule: HMODULE; lpType: PKOLChar;
+ lpEnumFunc: ENUMRESNAMEPROC; lParam: Longint): BOOL; stdcall;
+function EnumResourceTypes(hModule: HMODULE; lpEnumFunc: ENUMRESTYPEPROC;
+ lParam: Longint): BOOL; stdcall;
+function EnumSystemCodePages(lpCodePageEnumProc: TFNCodepageEnumProc; dwFlags: DWORD): BOOL; stdcall;
+function EnumSystemLocales(lpLocaleEnumProc: TFNLocaleEnumProc; dwFlags: DWORD): BOOL; stdcall;
+function EnumTimeFormats(lpTimeFmtEnumProc: TFNTimeFmtEnumProc;
+ Locale: LCID; dwFlags: DWORD): BOOL; stdcall;
+function ExpandEnvironmentStrings(lpSrc: PKOLChar; lpDst: PKOLChar; nSize: DWORD): DWORD; stdcall;
+procedure FatalAppExit(uAction: UINT; lpMessageText: PKOLChar); stdcall;
+function FillConsoleOutputCharacter(hConsoleOutput: THandle; cCharacter: KOLChar;
+ nLength: DWORD; dwWriteCoord: TCoord; var lpNumberOfCharsWritten: DWORD): BOOL; stdcall;
+function FindAtom(lpString: PKOLChar): ATOM; stdcall;
+function FindFirstChangeNotification(lpPathName: PKOLChar;
+ bWatchSubtree: BOOL; dwNotifyFilter: DWORD): THandle; stdcall;
+function FindFirstFile(lpFileName: PKOLChar; var lpFindFileData: TWIN32FindData): THandle; stdcall;
+{$IFDEF _D3orHigher}
+function FindFirstFileEx(lpFileName: PKOLChar; fInfoLevelId: TFindexInfoLevels;
+ lpFindFileData: Pointer; fSearchOp: TFindexSearchOps; lpSearchFilter: Pointer;
+ dwAdditionalFlags: DWORD): BOOL; stdcall;
+{$ENDIF _D3orHigher}
+function FindNextFile(hFindFile: THandle; var lpFindFileData: TWIN32FindData): BOOL; stdcall;
+function FindResource(hModule: HMODULE; lpName, lpType: PKOLChar): HRSRC; stdcall;
+function FindResourceEx(hModule: HMODULE; lpType, lpName: PKOLChar; wLanguage: Word): HRSRC; stdcall;
+function FoldString(dwMapFlags: DWORD; lpSrcStr: PKOLChar; cchSrc: Integer;
+ lpDestStr: PKOLChar; cchDest: Integer): Integer; stdcall;
+function FormatMessage(dwFlags: DWORD; lpSource: Pointer; dwMessageId: DWORD; dwLanguageId: DWORD;
+ lpBuffer: PKOLChar; nSize: DWORD; Arguments: Pointer): DWORD; stdcall;
+function FreeEnvironmentStrings(EnvBlock: PKOLChar): BOOL; stdcall;
+function GetAtomName(nAtom: ATOM; lpBuffer: PKOLChar; nSize: Integer): UINT; stdcall;
+function GetBinaryType(lpApplicationName: PKOLChar; var lpBinaryType: DWORD): BOOL; stdcall;
+function GetCommandLine: PKOLChar; stdcall;
+function GetCompressedFileSize(lpFileName: PKOLChar; lpFileSizeHigh: PDWORD): DWORD; stdcall;
+function GetComputerName(lpBuffer: PKOLChar; var nSize: DWORD): BOOL; stdcall;
+function GetConsoleTitle(lpConsoleTitle: PKOLChar; nSize: DWORD): DWORD; stdcall;
+function GetCurrencyFormat(Locale: LCID; dwFlags: DWORD; lpValue: PKOLChar;
+ lpFormat: PCurrencyFmt; lpCurrencyStr: PKOLChar; cchCurrency: Integer): Integer; stdcall;
+function GetCurrentDirectory(nBufferLength: DWORD; lpBuffer: PKOLChar): DWORD; stdcall;
+function GetDateFormat(Locale: LCID; dwFlags: DWORD; lpDate: PSystemTime;
+ lpFormat: PKOLChar; lpDateStr: PKOLChar; cchDate: Integer): Integer; stdcall;
+function GetDefaultCommConfig(lpszName: PKOLChar;
+ var lpCC: TCommConfig; var lpdwSize: DWORD): BOOL; stdcall;
+function GetDiskFreeSpace(lpRootPathName: PKOLChar;
+ var lpSectorsPerCluster, lpBytesPerSector, lpNumberOfFreeClusters, lpTotalNumberOfClusters: DWORD): BOOL; stdcall;
+function GetDiskFreeSpaceEx(lpDirectoryName: PKOLChar;
+ var lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes; lpTotalNumberOfFreeBytes: PLargeInteger): BOOL; stdcall;
+function GetDriveType(lpRootPathName: PKOLChar): UINT; stdcall;
+function GetEnvironmentStrings: PKOLChar; stdcall;
+function GetEnvironmentVariable(lpName: PKOLChar; lpBuffer: PKOLChar; nSize: DWORD): DWORD; stdcall; //overload;
+function GetFileAttributes(lpFileName: PKOLChar): DWORD; stdcall;
+{$IFDEF _D3orHigher}
+function GetFileAttributesEx(lpFileName: PKOLChar;
+ fInfoLevelId: TGetFileExInfoLevels; lpFileInformation: Pointer): BOOL; stdcall;
+{$ENDIF _D3orHigher}
+function GetFullPathName(lpFileName: PKOLChar; nBufferLength: DWORD;
+ lpBuffer: PKOLChar; var lpFilePart: PKOLChar): DWORD; stdcall;
+function GetLocaleInfo(Locale: LCID; LCType: LCTYPE; lpLCData: PKOLChar; cchData: Integer): Integer; stdcall;
+function GetLogicalDriveStrings(nBufferLength: DWORD; lpBuffer: PKOLChar): DWORD; stdcall;
+function GetModuleFileName(hModule: HINST; lpFilename: PKOLChar; nSize: DWORD): DWORD; stdcall;
+function GetModuleHandle(lpModuleName: PKOLChar): HMODULE; stdcall;
+function GetNamedPipeHandleState(hNamedPipe: THandle;
+ lpState, lpCurInstances, lpMaxCollectionCount, lpCollectDataTimeout: PDWORD;
+ lpUserName: PKOLChar; nMaxUserNameSize: DWORD): BOOL; stdcall;
+function GetNumberFormat(Locale: LCID; dwFlags: DWORD; lpValue: PKOLChar;
+ lpFormat: PNumberFmt; lpNumberStr: PKOLChar; cchNumber: Integer): Integer; stdcall;
+function GetPrivateProfileInt(lpAppName, lpKeyName: PKOLChar; nDefault: Integer; lpFileName: PKOLChar): UINT; stdcall;
+function GetPrivateProfileSection(lpAppName: PKOLChar; lpReturnedString: PKOLChar; nSize: DWORD; lpFileName: PKOLChar): DWORD; stdcall;
+function GetPrivateProfileSectionNames(lpszReturnBuffer: PKOLChar; nSize: DWORD; lpFileName: PKOLChar): DWORD; stdcall;
+function GetPrivateProfileString(lpAppName, lpKeyName, lpDefault: PKOLChar;
+ lpReturnedString: PKOLChar; nSize: DWORD; lpFileName: PKOLChar): DWORD; stdcall;
+function GetProfileInt(lpAppName, lpKeyName: PKOLChar; nDefault: Integer): UINT; stdcall;
+function GetProfileSection(lpAppName: PKOLChar; lpReturnedString: PKOLChar; nSize: DWORD): DWORD; stdcall;
+function GetProfileString(lpAppName, lpKeyName, lpDefault: PKOLChar;
+ lpReturnedString: PKOLChar; nSize: DWORD): DWORD; stdcall;
+function GetShortPathName(lpszLongPath: PKOLChar; lpszShortPath: PKOLChar;
+ cchBuffer: DWORD): DWORD; stdcall;
+procedure GetStartupInfo(var lpStartupInfo: TStartupInfo); stdcall;
+function GetStringTypeEx(Locale: LCID; dwInfoType: DWORD;
+ lpSrcStr: PKOLChar; cchSrc: Integer; var lpCharType): BOOL; stdcall;
+function GetSystemDirectory(lpBuffer: PKOLChar; uSize: UINT): UINT; stdcall;
+function GetTempFileName(lpPathName, lpPrefixString: PKOLChar;
+ uUnique: UINT; lpTempFileName: PKOLChar): UINT; stdcall;
+function GetTempPath(nBufferLength: DWORD; lpBuffer: PKOLChar): DWORD; stdcall;
+function GetTimeFormat(Locale: LCID; dwFlags: DWORD; lpTime: PSystemTime;
+ lpFormat: PKOLChar; lpTimeStr: PKOLChar; cchTime: Integer): Integer; stdcall;
+function GetVersionEx(var lpVersionInformation: TOSVersionInfo): BOOL; stdcall;
+function GetVolumeInformation(lpRootPathName: PKOLChar;
+ lpVolumeNameBuffer: PKOLChar; nVolumeNameSize: DWORD; lpVolumeSerialNumber: PDWORD;
+ var lpMaximumComponentLength, lpFileSystemFlags: DWORD;
+ lpFileSystemNameBuffer: PKOLChar; nFileSystemNameSize: DWORD): BOOL; stdcall;
+function GetWindowsDirectory(lpBuffer: PKOLChar; uSize: UINT): UINT; stdcall;
+function GlobalAddAtom(lpString: PKOLChar): ATOM; stdcall;
+function GlobalFindAtom(lpString: PKOLChar): ATOM; stdcall;
+function GlobalGetAtomName(nAtom: ATOM; lpBuffer: PKOLChar; nSize: Integer): UINT; stdcall;
+function IsBadStringPtr(lpsz: PKOLChar; ucchMax: UINT): BOOL; stdcall;
+function LCMapString(Locale: LCID; dwMapFlags: DWORD; lpSrcStr: PKOLChar;
+ cchSrc: Integer; lpDestStr: PKOLChar; cchDest: Integer): Integer; stdcall;
+function LoadLibrary(lpLibFileName: PKOLChar): HMODULE; stdcall;
+function LoadLibraryEx(lpLibFileName: PKOLChar; hFile: THandle; dwFlags: DWORD): HMODULE; stdcall;
+function MoveFile(lpExistingFileName, lpNewFileName: PKOLChar): BOOL; stdcall;
+function MoveFileEx(lpExistingFileName, lpNewFileName: PKOLChar; dwFlags: DWORD): BOOL; stdcall;
+{$IFDEF _D3orHigher}
+function MoveFileWithProgress(lpExistingFileName, lpNewFileName: PKOLChar; lpProgressRoutine: TFNProgressRoutine;
+ lpData: Pointer; dwFlags: DWORD): BOOL; stdcall;
+{$ENDIF _D3orHigher}
+function OpenEvent(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: PKOLChar): THandle; stdcall;
+function OpenFileMapping(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: PKOLChar): THandle; stdcall;
+function OpenMutex(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: PKOLChar): THandle; stdcall;
+function OpenSemaphore(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: PKOLChar): THandle; stdcall;
+function OpenWaitableTimer(dwDesiredAccess: DWORD; bInheritHandle: BOOL;
+ lpTimerName: PKOLChar): THandle; stdcall;
+procedure OutputDebugString(lpOutputString: PKOLChar); stdcall;
+function PeekConsoleInput(hConsoleInput: THandle; var lpBuffer: TInputRecord;
+ nLength: DWORD; var lpNumberOfEventsRead: DWORD): BOOL; stdcall;
+function QueryDosDevice(lpDeviceName: PKOLChar; lpTargetPath: PKOLChar; ucchMax: DWORD): DWORD; stdcall;
+function QueryRecoveryAgents(p1: PKOLChar; var p2: Pointer; var p3: TRecoveryAgentInformation): DWORD; stdcall;
+function ReadConsole(hConsoleInput: THandle; lpBuffer: Pointer;
+ nNumberOfCharsToRead: DWORD; var lpNumberOfCharsRead: DWORD; lpReserved: Pointer): BOOL; stdcall;
+function ReadConsoleInput(hConsoleInput: THandle; var lpBuffer: TInputRecord;
+ nLength: DWORD; var lpNumberOfEventsRead: DWORD): BOOL; stdcall;
+function ReadConsoleOutput(hConsoleOutput: THandle; lpBuffer: Pointer;
+ dwBufferSize, dwBufferCoord: TCoord; var lpReadRegion: TSmallRect): BOOL; stdcall;
+function ReadConsoleOutputCharacter(hConsoleOutput: THandle; lpCharacter: PKOLChar;
+ nLength: DWORD; dwReadCoord: TCoord; var lpNumberOfCharsRead: DWORD): BOOL; stdcall;
+function RemoveDirectory(lpPathName: PKOLChar): BOOL; stdcall;
+function ScrollConsoleScreenBuffer(hConsoleOutput: THandle;
+ const lpScrollRectangle: TSmallRect; lpClipRectangle: PSmallRect;
+ dwDestinationOrigin: TCoord; var lpFill: TCharInfo): BOOL; stdcall;
+function SearchPath(lpPath, lpFileName, lpExtension: PKOLChar;
+ nBufferLength: DWORD; lpBuffer: PKOLChar; var lpFilePart: PKOLChar): DWORD; stdcall;
+function SetComputerName(lpComputerName: PKOLChar): BOOL; stdcall;
+function SetConsoleTitle(lpConsoleTitle: PKOLChar): BOOL; stdcall;
+function SetCurrentDirectory(lpPathName: PKOLChar): BOOL; stdcall;
+function SetDefaultCommConfig(lpszName: PKOLChar; lpCC: PCommConfig; dwSize: DWORD): BOOL; stdcall;
+function SetEnvironmentVariable(lpName, lpValue: PKOLChar): BOOL; stdcall;
+function SetFileAttributes(lpFileName: PKOLChar; dwFileAttributes: DWORD): BOOL; stdcall;
+function SetLocaleInfo(Locale: LCID; LCType: LCTYPE; lpLCData: PKOLChar): BOOL; stdcall;
+function SetVolumeLabel(lpRootPathName: PKOLChar; lpVolumeName: PKOLChar): BOOL; stdcall;
+function UpdateResource(hUpdate: THandle; lpType, lpName: PKOLChar;
+ wLanguage: Word; lpData: Pointer; cbData: DWORD): BOOL; stdcall;
+function VerLanguageName(wLang: DWORD; szLang: PKOLChar; nSize: DWORD): DWORD; stdcall;
+function WaitNamedPipe(lpNamedPipeName: PKOLChar; nTimeOut: DWORD): BOOL; stdcall;
+function WriteConsole(hConsoleOutput: THandle; const lpBuffer: Pointer;
+ nNumberOfCharsToWrite: DWORD; var lpNumberOfCharsWritten: DWORD; lpReserved: Pointer): BOOL; stdcall;
+function WriteConsoleInput(hConsoleInput: THandle; const lpBuffer: TInputRecord;
+ nLength: DWORD; var lpNumberOfEventsWritten: DWORD): BOOL; stdcall;
+function WriteConsoleOutput(hConsoleOutput: THandle; lpBuffer: Pointer;
+ dwBufferSize, dwBufferCoord: TCoord; var lpWriteRegion: TSmallRect): BOOL; stdcall;
+function WriteConsoleOutputCharacter(hConsoleOutput: THandle;lpCharacter: PKOLChar;
+ nLength: DWORD; dwWriteCoord: TCoord; var lpNumberOfCharsWritten: DWORD): BOOL; stdcall;
+function WritePrivateProfileSection(lpAppName, lpString, lpFileName: PKOLChar): BOOL; stdcall;
+function WritePrivateProfileString(lpAppName, lpKeyName, lpString, lpFileName: PKOLChar): BOOL; stdcall;
+function WriteProfileSection(lpAppName, lpString: PKOLChar): BOOL; stdcall;
+function WriteProfileString(lpAppName, lpKeyName, lpString: PKOLChar): BOOL; stdcall;
+function lstrcat(lpString1, lpString2: PKOLChar): PKOLChar; stdcall;
+function lstrcmp(lpString1, lpString2: PKOLChar): Integer; stdcall;
+function lstrcmpi(lpString1, lpString2: PKOLChar): Integer; stdcall;
+function lstrcpy(lpString1, lpString2: PKOLChar): PKOLChar; stdcall;
+function lstrcpyn(lpString1, lpString2: PKOLChar; iMaxLength: Integer): PKOLChar; stdcall;
+function lstrlen(lpString: PKOLChar): Integer; stdcall;
+function MultinetGetConnectionPerformance(lpNetResource: PNetResource;
+ lpNetConnectInfoStruc: PNetConnectInfoStruct): DWORD; stdcall;
+function WNetAddConnection2(var lpNetResource: TNetResource;
+ lpPassword, lpUserName: PKOLChar; dwFlags: DWORD): DWORD; stdcall;
+function WNetAddConnection3(hwndOwner: HWND; var lpNetResource: TNetResource;
+ lpPassword, lpUserName: PKOLChar; dwFlags: DWORD): DWORD; stdcall;
+function WNetAddConnection(lpRemoteName, lpPassword, lpLocalName: PKOLChar): DWORD; stdcall;
+function WNetCancelConnection2(lpName: PKOLChar; dwFlags: DWORD; fForce: BOOL): DWORD; stdcall;
+function WNetCancelConnection(lpName: PKOLChar; fForce: BOOL): DWORD; stdcall;
+function WNetConnectionDialog1(var lpConnDlgStruct: TConnectDlgStruct): DWORD; stdcall;
+function WNetDisconnectDialog1(var lpConnDlgStruct: TDiscDlgStruct): DWORD; stdcall;
+function WNetEnumResource(hEnum: THandle; var lpcCount: DWORD;
+ lpBuffer: Pointer; var lpBufferSize: DWORD): DWORD; stdcall;
+function WNetGetConnection(lpLocalName: PKOLChar;
+ lpRemoteName: PKOLChar; var lpnLength: DWORD): DWORD; stdcall;
+function WNetGetLastError(var lpError: DWORD; lpErrorBuf: PKOLChar;
+ nErrorBufSize: DWORD; lpNameBuf: PKOLChar; nNameBufSize: DWORD): DWORD; stdcall;
+function WNetGetNetworkInformation(lpProvider: PKOLChar;
+ var lpNetInfoStruct: TNetInfoStruct): DWORD; stdcall;
+function WNetGetProviderName(dwNetType: DWORD; lpProviderName: PKOLChar;
+ var lpBufferSize: DWORD): DWORD; stdcall;
+function WNetGetResourceParent(lpNetResource: PNetResource;
+ lpBuffer: Pointer; var cbBuffer: DWORD): DWORD; stdcall;
+function WNetGetUniversalName(lpLocalPath: PKOLChar; dwInfoLevel: DWORD;
+ lpBuffer: Pointer; var lpBufferSize: DWORD): DWORD; stdcall;
+function WNetGetUser(lpName: PKOLChar; lpUserName: PKOLChar; var lpnLength: DWORD): DWORD; stdcall;
+function WNetOpenEnum(dwScope, dwType, dwUsage: DWORD;
+ lpNetResource: PNetResource; var lphEnum: THandle): DWORD; stdcall;
+function WNetSetConnection(lpName: PKOLChar; dwProperties: DWORD; pvValues: Pointer): DWORD; stdcall;
+function WNetUseConnection(hwndOwner: HWND;
+ var lpNetResource: TNetResource; lpUserID: PKOLChar;
+ lpPassword: PKOLChar; dwFlags: DWORD; lpAccessName: PKOLChar;
+ var lpBufferSize: DWORD; var lpResult: DWORD): DWORD; stdcall;
+function GetFileVersionInfo(lptstrFilename: PKOLChar; dwHandle, dwLen: DWORD;
+ lpData: Pointer): BOOL; stdcall;
+function GetFileVersionInfoSize(lptstrFilename: PKOLChar; var lpdwHandle: DWORD): DWORD; stdcall;
+function VerFindFile(uFlags: DWORD; szFileName, szWinDir, szAppDir, szCurDir: PKOLChar;
+ var lpuCurDirLen: UINT; szDestDir: PKOLChar; var lpuDestDirLen: UINT): DWORD; stdcall;
+function VerInstallFile(uFlags: DWORD;
+ szSrcFileName, szDestFileName, szSrcDir, szDestDir, szCurDir, szTmpFile: PKOLChar;
+ var lpuTmpFileLen: UINT): DWORD; stdcall;
+function VerQueryValue(pBlock: Pointer; lpSubBlock: PKOLChar;
+ var lplpBuffer: Pointer; var puLen: UINT): BOOL; stdcall;
+function GetPrivateProfileStruct(lpszSection, lpszKey: PKOLChar;
+ lpStruct: Pointer; uSizeStruct: UINT; szFile: PKOLChar): BOOL; stdcall;
+function WritePrivateProfileStruct(lpszSection, lpszKey: PKOLChar;
+ lpStruct: Pointer; uSizeStruct: UINT; szFile: PKOLChar): BOOL; stdcall;
+function AddFontResource(FileName: PKOLChar): Integer; stdcall;
+function AddFontResourceEx(p1: PKOLChar; p2: DWORD; p3: PDesignVector): Integer; stdcall;
+function CopyEnhMetaFile(p1: HENHMETAFILE; p2: PKOLChar): HENHMETAFILE; stdcall;
+function CopyMetaFile(p1: HMETAFILE; p2: PKOLChar): HMETAFILE; stdcall;
+function CreateColorSpace(var ColorSpace: TLogColorSpace): HCOLORSPACE; stdcall;
+function CreateDC(lpszDriver, lpszDevice, lpszOutput: PKOLChar;
+ lpdvmInit: PDeviceMode): HDC; stdcall;
+function CreateEnhMetaFile(DC: HDC; FileName: PKOLChar; Rect: PRect; Desc: PKOLChar): HDC; stdcall;
+function CreateFont(nHeight, nWidth, nEscapement, nOrientaion, fnWeight: Integer;
+ fdwItalic, fdwUnderline, fdwStrikeOut, fdwCharSet, fdwOutputPrecision,
+ fdwClipPrecision, fdwQuality, fdwPitchAndFamily: DWORD; lpszFace: PKOLChar): HFONT; stdcall;
+function CreateFontIndirect(const p1: TLogFont): HFONT; stdcall;
+function CreateFontIndirectEx(const p1: PEnumLogFontExDV): HFONT; stdcall;
+function CreateIC(lpszDriver, lpszDevice, lpszOutput: PKOLChar; lpdvmInit: PDeviceMode): HDC; stdcall;
+function CreateMetaFile(p1: PKOLChar): HDC; stdcall;
+function CreateScalableFontResource(p1: DWORD; p2, p3, p4: PKOLChar): BOOL; stdcall;
+function DeviceCapabilities(pDriverName, pDeviceName, pPort: PKOLChar;
+ iIndex: Integer; pOutput: PKOLChar; DevMode: PDeviceMode): Integer; stdcall;
+function EnumFontFamilies(DC: HDC; p2: PKOLChar; p3: TFNFontEnumProc; p4: LPARAM): BOOL; stdcall;
+function EnumFontFamiliesEx(DC: HDC; var p2: TLogFont;
+ p3: TFNFontEnumProc; p4: LPARAM; p5: DWORD): BOOL; stdcall;
+function EnumFonts(DC: HDC; lpszFace: PKOLChar; fntenmprc: TFNFontEnumProc;
+ lpszData: PKOLChar): Integer; stdcall;
+function EnumICMProfiles(DC: HDC; ICMProc: TFNICMEnumProc; p3: LPARAM): Integer; stdcall;
+function ExtTextOut(DC: HDC; X, Y: Integer; Options: Longint;
+ Rect: PRect; Str: PKOLChar; Count: Longint; Dx: PInteger): BOOL; stdcall;
+function GetCharABCWidths(DC: HDC; FirstChar, LastChar: UINT; const ABCStructs): BOOL; stdcall;
+function GetCharABCWidthsFloat(DC: HDC; FirstChar, LastChar: UINT; const ABCFloatSturcts): BOOL; stdcall;
+function GetCharWidth32(DC: HDC; FirstChar, LastChar: UINT; const Widths): BOOL; stdcall;
+function GetCharWidth(DC: HDC; FirstChar, LastChar: UINT; const Widths): BOOL; stdcall;
+function GetCharWidthFloat(DC: HDC; FirstChar, LastChar: UINT; const Widths): BOOL; stdcall;
+function GetCharacterPlacement(DC: HDC; p2: PKOLChar; p3, p4: BOOL;
+ var p5: TGCPResults; p6: DWORD): DWORD; stdcall;
+function GetEnhMetaFile(p1: PKOLChar): HENHMETAFILE; stdcall;
+function GetEnhMetaFileDescription(p1: HENHMETAFILE; p2: UINT; p3: PKOLChar): UINT; stdcall;
+function GetGlyphIndices(DC: HDC; p2: PKOLChar; p3: Integer; p4: PWORD; p5: DWORD): DWORD; stdcall;
+function GetGlyphOutline(DC: HDC; uChar, uFormat: UINT;
+ const lpgm: TGlyphMetrics; cbBuffer: DWORD; lpvBuffer: Pointer; const lpmat2: TMat2): DWORD; stdcall;
+function GetICMProfile(DC: HDC; var Size: DWORD; Name: PKOLChar): BOOL; stdcall;
+function GetLogColorSpace(p1: HCOLORSPACE; var ColorSpace: TLogColorSpace; Size: DWORD): BOOL; stdcall;
+function GetMetaFile(p1: PKOLChar): HMETAFILE; stdcall;
+function GetObject(p1: HGDIOBJ; p2: Integer; p3: Pointer): Integer; stdcall;
+function GetOutlineTextMetrics(DC: HDC; p2: UINT; OTMetricStructs: Pointer): UINT; stdcall;
+function GetTextExtentExPoint(DC: HDC; p2: PKOLChar;
+ p3, p4: Integer; p5, p6: PInteger; var p7: TSize): BOOL; stdcall;
+function GetTextExtentPoint32(DC: HDC; Str: PKOLChar; Count: Integer;
+ var Size: TSize): BOOL; stdcall;
+function GetTextExtentPoint(DC: HDC; Str: PKOLChar; Count: Integer;
+ var Size: TSize): BOOL; stdcall;
+function GetTextFace(DC: HDC; Count: Integer; Buffer: PKOLChar): Integer; stdcall;
+function GetTextMetrics(DC: HDC; var TM: TTextMetric): BOOL; stdcall;
+function PolyTextOut(DC: HDC; const PolyTextArray; Strings: Integer): BOOL; stdcall;
+function RemoveFontResource(FileName: PKOLChar): BOOL; stdcall;
+function RemoveFontResourceEx(p1: PKOLChar; p2: DWORD; p3: PDesignVector): BOOL; stdcall;
+function ResetDC(DC: HDC; const InitData: TDeviceMode): HDC; stdcall;
+function SetICMProfile(DC: HDC; Name: PKOLChar): BOOL; stdcall;
+function StartDoc(DC: HDC; const p2: TDocInfo): Integer; stdcall;
+function TextOut(DC: HDC; X, Y: Integer; Str: PKOLChar; Count: Integer): BOOL; stdcall;
+function UpdateICMRegKey(p1: DWORD; p2, p3: PKOLChar; p4: UINT): BOOL; stdcall;
+function wglUseFontBitmaps(DC: HDC; p2, p3, p4: DWORD): BOOL; stdcall;
+function wglUseFontOutlines(p1: HDC; p2, p3, p4: DWORD;
+ p5, p6: Single; p7: Integer; p8: PGlyphMetricsFloat): BOOL; stdcall;
+function AnsiToOem(const lpszSrc: LPCSTR; lpszDst: LPSTR): BOOL; stdcall;
+function AnsiToOemBuff(lpszSrc: LPCSTR; lpszDst: LPSTR; cchDstLength: DWORD): BOOL; stdcall;
+function AnsiUpper(lpsz: LPSTR): LPSTR; stdcall;
+function AnsiUpperBuff(lpsz: LPSTR; cchLength: DWORD): DWORD; stdcall;
+function AnsiLower(lpsz: LPSTR): LPSTR; stdcall;
+function AnsiLowerBuff(lpsz: LPSTR; cchLength: DWORD): DWORD; stdcall;
+function AnsiNext(const lpsz: LPCSTR): LPSTR; stdcall;
+function AnsiPrev(const lpszStart: LPCSTR; const lpszCurrent: LPCSTR): LPSTR; stdcall;
+function AppendMenu(hMenu: HMENU; uFlags, uIDNewItem: UINT;
+ lpNewItem: PKOLChar): BOOL; stdcall;
+//function BroadcastSystemMessage(Flags: DWORD; Recipients: PDWORD;
+// uiMessage: UINT; wParam: WPARAM; lParam: LPARAM): Longint; stdcall;
+//function BroadcastSystemMessageW(Flags: DWORD; Recipients: PDWORD;
+// uiMessage: UINT; wParam: WPARAM; lParam: LPARAM): Longint; stdcall;
+function CallMsgFilter(var lpMsg: TMsg; nCode: Integer): BOOL; stdcall;
+function CallWindowProc(lpPrevWndFunc: TFNWndProc; hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
+function ChangeDisplaySettings(var lpDevMode: TDeviceMode; dwFlags: DWORD): Longint; stdcall;
+function ChangeDisplaySettingsEx(lpszDeviceName: PKOLChar; var lpDevMode: TDeviceMode;
+ wnd: HWND; dwFlags: DWORD; lParam: Pointer): Longint; stdcall;
+function ChangeMenu(hMenu: HMENU; cmd: UINT; lpszNewItem: PKOLChar;
+ cmdInsert: UINT; flags: UINT): BOOL; stdcall;
+function CharLower(lpsz: PKOLChar): PKOLChar; stdcall;
+function CharLowerBuff(lpsz: PKOLChar; cchLength: DWORD): DWORD; stdcall;
+function CharNext(lpsz: PKOLChar): PKOLChar; stdcall;
+function CharNextEx(CodePage: Word; lpCurrentChar: LPCSTR; dwFlags: DWORD): LPSTR; stdcall;
+function CharPrev(lpszStart: PKOLChar; lpszCurrent: PKOLChar): PKOLChar; stdcall;
+function CharPrevEx(CodePage: Word; lpStart, lpCurrentChar: LPCSTR; dwFlags: DWORD): LPSTR; stdcall;
+function CharToOem(lpszSrc: PKOLChar; lpszDst: PKOLChar): BOOL; stdcall;
+function CharToOemBuff(lpszSrc: PKOLChar; lpszDst: PKOLChar; cchDstLength: DWORD): BOOL; stdcall;
+function CharUpper(lpsz: PKOLChar): PKOLChar; stdcall;
+function CharUpperBuff(lpsz: PKOLChar; cchLength: DWORD): DWORD; stdcall;
+function CopyAcceleratorTable(hAccelSrc: HACCEL; var lpAccelDst; cAccelEntries: Integer): Integer; stdcall;
+function CreateAcceleratorTable(var Accel; Count: Integer): HACCEL; stdcall;
+function CreateDesktop(lpszDesktop, lpszDevice: PKOLChar;
+ pDevmode: PDeviceMode; dwFlags: DWORD; dwDesiredAccess:
+ DWORD; lpsa: PSecurityAttributes): HDESK; stdcall;
+function CreateDialogIndirectParam(hInstance: HINST; const lpTemplate: TDlgTemplate;
+ hWndParent: HWND; lpDialogFunc: TFNDlgProc; dwInitParam: LPARAM): HWND; stdcall;
+function CreateDialogParam(hInstance: HINST; lpTemplateName: PKOLChar;
+ hWndParent: HWND; lpDialogFunc: TFNDlgProc; dwInitParam: LPARAM): HWND; stdcall;
+function CreateMDIWindow(lpClassName, lpWindowName: PKOLChar;
+ dwStyle: DWORD; X, Y, nWidth, nHeight: Integer;
+ hWndParent: HWND; hInstance: HINST; lParam: LPARAM): HWND; stdcall;
+function CreateWindowEx(dwExStyle: DWORD; lpClassName: PKOLChar;
+ lpWindowName: PKOLChar; dwStyle: DWORD; X, Y, nWidth, nHeight: Integer;
+ hWndParent: HWND; hMenu: HMENU; hInstance: HINST; lpParam: Pointer): HWND; stdcall;
+function CreateWindowStation(lpwinsta: PKOLChar; dwReserved, dwDesiredAccess: DWORD;
+ lpsa: PSecurityAttributes): HWINSTA; stdcall;
+function DefDlgProc(hDlg: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
+function DefFrameProc(hWnd, hWndMDIClient: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
+function DefMDIChildProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
+function DefWindowProc(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
+function DialogBoxIndirectParam(hInstance: HINST; const lpDialogTemplate: TDlgTemplate;
+ hWndParent: HWND; lpDialogFunc: TFNDlgProc; dwInitParam: LPARAM): Integer; stdcall;
+function DialogBoxParam(hInstance: HINST; lpTemplateName: PKOLChar;
+ hWndParent: HWND; lpDialogFunc: TFNDlgProc; dwInitParam: LPARAM): Integer; stdcall;
+function DispatchMessage(const lpMsg: TMsg): Longint; stdcall;
+function DlgDirList(hDlg: HWND; lpPathSpec: PKOLChar;
+ nIDListBox, nIDStaticPath: Integer; uFileType: UINT): Integer; stdcall;
+function DlgDirListComboBox(hDlg: HWND; lpPathSpec: PKOLChar;
+ nIDComboBox, nIDStaticPath: Integer; uFiletype: UINT): Integer; stdcall;
+function DlgDirSelectComboBoxEx(hDlg: HWND; lpString: PKOLChar;
+ nCount, nIDComboBox: Integer): BOOL; stdcall;
+function DlgDirSelectEx(hDlg: HWND; lpString: PKOLChar; nCount, nIDListBox: Integer): BOOL; stdcall;
+function DrawState(DC: HDC; Brush: HBRUSH; CBFunc: TFNDrawStateProc;
+ lData: LPARAM; wData: WPARAM; x, y, cx, cy: Integer; Flags: UINT): BOOL; stdcall;
+function DrawText(hDC: HDC; lpString: PKOLChar; nCount: Integer;
+ var lpRect: TRect; uFormat: UINT): Integer; stdcall;
+function DrawTextEx(DC: HDC; lpchText: PKOLChar; cchText: Integer; var p4: TRect;
+ dwDTFormat: UINT; DTParams: PDrawTextParams): Integer; stdcall;
+function EnumDesktops(hwinsta: HWINSTA; lpEnumFunc: TFNDeskTopEnumProc; lParam: LPARAM): BOOL; stdcall;
+function EnumDisplaySettings(lpszDeviceName: PKOLChar; iModeNum: DWORD;
+ var lpDevMode: TDeviceMode): BOOL; stdcall;
+function EnumDisplayDevices(Unused: Pointer; iDevNum: DWORD;
+ var lpDisplayDevice: TDisplayDevice; dwFlags: DWORD): BOOL; stdcall;
+function EnumProps(hWnd: HWND; lpEnumFunc: TFNPropEnumProc): Integer; stdcall;
+function EnumPropsEx(hWnd: HWND; lpEnumFunc: TFNPropEnumProcEx; lParam: LPARAM): Integer; stdcall;
+function EnumWindowStations(lpEnumFunc: TFNWinStaEnumProc; lParam: LPARAM): BOOL; stdcall;
+function FindWindow(lpClassName, lpWindowName: PKOLChar): HWND; stdcall;
+function FindWindowEx(Parent, Child: HWND; ClassName, WindowName: PKOLChar): HWND; stdcall;
+function GetAltTabInfo(hwnd: HWND; iItem: Integer; var pati: TAltTabInfo;
+ pszItemText: PKOLChar; cchItemText: UINT): BOOL; stdcall;
+function GetClassInfo(hInstance: HINST; lpClassName: PKOLChar;
+ var lpWndClass: TWndClass): BOOL; stdcall;
+function GetClassInfoEx(Instance: HINST; Classname: PKOLChar; var WndClass: TWndClassEx): BOOL; stdcall;
+function GetClassLong(hWnd: HWND; nIndex: Integer): DWORD; stdcall;
+function GetClassName(hWnd: HWND; lpClassName: PKOLChar; nMaxCount: Integer): Integer; stdcall;
+function GetClipboardFormatName(format: UINT; lpszFormatName: PKOLChar;
+ cchMaxCount: Integer): Integer; stdcall;
+function GetDlgItemText(hDlg: HWND; nIDDlgItem: Integer;
+ lpString: PKOLChar; nMaxCount: Integer): UINT; stdcall;
+function GetKeyNameText(lParam: Longint; lpString: PKOLChar; nSize: Integer): Integer; stdcall;
+function GetKeyboardLayoutName(pwszKLID: PKOLChar): BOOL; stdcall;
+function GetMenuItemInfo(p1: HMENU; p2: UINT; p3: BOOL; var p4: TMenuItemInfo): BOOL; stdcall;
+function GetMenuString(hMenu: HMENU; uIDItem: UINT; lpString: PKOLChar;
+ nMaxCount: Integer; uFlag: UINT): Integer; stdcall;
+function GetMessage(var lpMsg: TMsg; hWnd: HWND;
+ wMsgFilterMin, wMsgFilterMax: UINT): BOOL; stdcall;
+function GetProp(hWnd: HWND; lpString: PKOLChar): THandle; stdcall;
+function GetTabbedTextExtent(hDC: HDC; lpString: PKOLChar;
+ nCount, nTabPositions: Integer; var lpnTabStopPositions): DWORD; stdcall;
+function GetUserObjectInformation(hObj: THandle; nIndex: Integer; pvInfo: Pointer;
+ nLength: DWORD; var lpnLengthNeeded: DWORD): BOOL; stdcall;
+function GetWindowLong(hWnd: HWND; nIndex: Integer): Longint; stdcall;
+function GetWindowModuleFileName(hwnd: HWND; pszFileName: PKOLChar; cchFileNameMax: UINT): UINT; stdcall;
+function GetWindowText(hWnd: HWND; lpString: PKOLChar; nMaxCount: Integer): Integer; stdcall;
+function GetWindowTextLength(hWnd: HWND): Integer; stdcall;
+function GrayString(hDC: HDC; hBrush: HBRUSH; lpOutputFunc: TFNGrayStringProc;
+ lpData: LPARAM; nCount, X, Y, nWidth, nHeight: Integer): BOOL; stdcall;
+function InsertMenu(hMenu: HMENU; uPosition, uFlags, uIDNewItem: UINT;
+ lpNewItem: PKOLChar): BOOL; stdcall;
+function InsertMenuItem(p1: HMENU; p2: UINT; p3: BOOL; const p4: TMenuItemInfo): BOOL; stdcall;
+function IsCharAlpha(ch: KOLChar): BOOL; stdcall;
+function IsCharAlphaNumeric(ch: KOLChar): BOOL; stdcall;
+function IsCharLower(ch: KOLChar): BOOL; stdcall;
+function IsCharUpper(ch: KOLChar): BOOL; stdcall;
+function IsDialogMessage(hDlg: HWND; var lpMsg: TMsg): BOOL; stdcall;
+function LoadAccelerators(hInstance: HINST; lpTableName: PKOLChar): HACCEL; stdcall;
+function LoadBitmap(hInstance: HINST; lpBitmapName: PKOLChar): HBITMAP; stdcall;
+function LoadCursor(hInstance: HINST; lpCursorName: PKOLChar): HCURSOR; stdcall;
+function LoadCursorFromFile(lpFileName: PKOLChar): HCURSOR; stdcall;
+function LoadIcon(hInstance: HINST; lpIconName: PKOLChar): HICON; stdcall;
+function LoadImage(hInst: HINST; ImageName: PKOLChar; ImageType: UINT; X, Y: Integer; Flags: UINT): THandle; stdcall;
+function LoadKeyboardLayout(pwszKLID: PKOLChar; Flags: UINT): HKL; stdcall;
+function LoadMenu(hInstance: HINST; lpMenuName: PKOLChar): HMENU; stdcall;
+function LoadMenuIndirect(lpMenuTemplate: Pointer): HMENU; stdcall;
+function LoadString(hInstance: HINST; uID: UINT; lpBuffer: PKOLChar; nBufferMax: Integer): Integer; stdcall;
+function MapVirtualKey(uCode, uMapType: UINT): UINT; stdcall;
+function MapVirtualKeyEx(uCode, uMapType: UINT; dwhkl: HKL): UINT; stdcall;
+function MessageBox(hWnd: HWND; lpText, lpCaption: PKOLChar; uType: UINT): Integer; stdcall;
+function MessageBoxEx(hWnd: HWND; lpText, lpCaption: PKOLChar;
+ uType: UINT; wLanguageId: Word): Integer; stdcall;
+function MessageBoxIndirect(const MsgBoxParams: TMsgBoxParams): BOOL; stdcall;
+function ModifyMenu(hMnu: HMENU; uPosition, uFlags, uIDNewItem: UINT;
+ lpNewItem: PKOLChar): BOOL; stdcall;
+function OemToAnsi(const lpszSrc: LPCSTR; lpszDst: LPSTR): BOOL; stdcall;
+function OemToAnsiBuff(lpszSrc: LPCSTR; lpszDst: LPSTR; cchDstLength: DWORD): BOOL; stdcall;
+function OemToChar(lpszSrc: PKOLChar; lpszDst: PKOLChar): BOOL; stdcall;
+function OemToCharBuff(lpszSrc: PKOLChar; lpszDst: PKOLChar; cchDstLength: DWORD): BOOL; stdcall;
+function OpenDesktop(lpszDesktop: PKOLChar; dwFlags: DWORD; fInherit: BOOL;
+ dwDesiredAccess: DWORD): HDESK; stdcall;
+function OpenWindowStation(lpszWinSta: PKOLChar; fInherit: BOOL;
+ dwDesiredAccess: DWORD): HWINSTA; stdcall;
+function PeekMessage(var lpMsg: TMsg; hWnd: HWND;
+ wMsgFilterMin, wMsgFilterMax, wRemoveMsg: UINT): BOOL; stdcall;
+function PostMessage(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): BOOL; stdcall;
+function PostThreadMessage(idThread: DWORD; Msg: UINT; wParam: WPARAM; lParam: LPARAM): BOOL; stdcall;
+function RealGetWindowClass(hwnd: HWND; pszType: PKOLChar; cchType: UINT): UINT; stdcall;
+function RegisterClass(const lpWndClass: TWndClass): ATOM; stdcall;
+function RegisterClassEx(const WndClass: TWndClassEx): ATOM; stdcall;
+function RegisterClipboardFormat(lpszFormat: PKOLChar): UINT; stdcall;
+function RegisterDeviceNotification(hRecipient: THandle; NotificationFilter: Pointer; Flags: DWORD): HDEVNOTIFY; stdcall;
+function RegisterWindowMessage(lpString: PKOLChar): UINT; stdcall;
+function RemoveProp(hWnd: HWND; lpString: PKOLChar): THandle; stdcall;
+function SendDlgItemMessage(hDlg: HWND; nIDDlgItem: Integer;
+ Msg: UINT; wParam: WPARAM; lParam: LPARAM): Longint; stdcall;
+function SendMessage(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
+function SendMessageCallback(hWnd: HWND; Msg: UINT; wParam: WPARAM;
+ lParam: LPARAM; lpResultCallBack: TFNSendAsyncProc; dwData: DWORD): BOOL; stdcall;
+function SendMessageTimeout(hWnd: HWND; Msg: UINT; wParam: WPARAM;
+ lParam: LPARAM; fuFlags, uTimeout: UINT; var lpdwResult: DWORD): LRESULT; stdcall;
+function SendNotifyMessage(hWnd: HWND; Msg: UINT; wParam: WPARAM;
+ lParam: LPARAM): BOOL; stdcall;
+function SetClassLong(hWnd: HWND; nIndex: Integer; dwNewLong: Longint): DWORD; stdcall;
+function SetDlgItemText(hDlg: HWND; nIDDlgItem: Integer; lpString: PKOLChar): BOOL; stdcall;
+function SetMenuItemInfo(p1: HMENU; p2: UINT; p3: BOOL; const p4: TMenuItemInfo): BOOL; stdcall;
+function SetProp(hWnd: HWND; lpString: PKOLChar; hData: THandle): BOOL; stdcall;
+function SetUserObjectInformation(hObj: THandle; nIndex: Integer;
+ pvInfo: Pointer; nLength: DWORD): BOOL; stdcall;
+function SetWindowLong(hWnd: HWND; nIndex: Integer; dwNewLong: Longint): Longint; stdcall;
+function SetWindowText(hWnd: HWND; lpString: PKOLChar): BOOL; stdcall;
+function SetWindowsHook(nFilterType: Integer; pfnFilterProc: TFNHookProc): HHOOK; stdcall;
+function SetWindowsHookEx(idHook: Integer; lpfn: TFNHookProc; hmod: HINST; dwThreadId: DWORD): HHOOK; stdcall;
+function SystemParametersInfo(uiAction, uiParam: UINT;
+ pvParam: Pointer; fWinIni: UINT): BOOL; stdcall;
+function TabbedTextOut(hDC: HDC; X, Y: Integer; lpString: PKOLChar; nCount, nTabPositions: Integer;
+ var lpnTabStopPositions; nTabOrigin: Integer): Longint; stdcall;
+function TranslateAccelerator(hWnd: HWND; hAccTable: HACCEL; var lpMsg: TMsg): Integer; stdcall;
+function UnregisterClass(lpClassName: PKOLChar; hInstance: HINST): BOOL; stdcall;
+function VkKeyScan(ch: KOLChar): SHORT; stdcall;
+function VkKeyScanEx(ch: KOLChar; dwhkl: HKL): SHORT; stdcall;
+function WinHelp(hWndMain: HWND; lpszHelp: PKOLChar; uCommand: UINT; dwData: DWORD): BOOL; stdcall;
+function wsprintf(Output: PKOLChar; Format: PKOLChar): Integer; stdcall;
+function wvsprintf(Output: PKOLChar; Format: PKOLChar; arglist: {$IFDEF UNICODE} PAnsiChar {$ELSE} va_list {$ENDIF}): Integer; stdcall;
+function CreateMutex(lpMutexAttributes: PSecurityAttributes; bInitialOwner: BOOL; lpName: PAnsiChar): THandle;
+
+const
+ IDC_ARROW = MakeIntResource(32512);
+ IDC_IBEAM = MakeIntResource(32513);
+ IDC_WAIT = MakeIntResource(32514);
+ IDC_CROSS = MakeIntResource(32515);
+ IDC_UPARROW = MakeIntResource(32516);
+ IDC_SIZE = MakeIntResource(32640);
+ IDC_ICON = MakeIntResource(32641);
+ IDC_SIZENWSE = MakeIntResource(32642);
+ IDC_SIZENESW = MakeIntResource(32643);
+ IDC_SIZEWE = MakeIntResource(32644);
+ IDC_SIZENS = MakeIntResource(32645);
+ IDC_SIZEALL = MakeIntResource(32646);
+ IDC_NO = MakeIntResource(32648);
+ IDC_HANDs = MakeIntResource(32649);
+ IDC_APPSTARTING = MakeIntResource(32650);
+ IDC_HELP = MakeIntResource(32651);
+{$ENDIF interface_part} ////////////////////////////////////////////////////////
+
+{$IFDEF implementation_part} ///////////////////////////////////////////////////
+function AbortSystemShutdown; external advapi32 name 'AbortSystemShutdownA';
+function AccessCheckAndAuditAlarm; external advapi32 name 'AccessCheckAndAuditAlarmA';
+{$IFDEF _D4orHigher}
+function AccessCheckByTypeAndAuditAlarm; external advapi32 name 'AccessCheckByTypeAndAuditAlarmA';
+function AccessCheckByTypeResultListAndAuditAlarm; external advapi32 name 'AccessCheckByTypeResultListAndAuditAlarmA';
+{$ENDIF _D4orHigher}
+function BackupEventLog; external advapi32 name 'BackupEventLogA';
+function ClearEventLog; external advapi32 name 'ClearEventLogA';
+function CreateProcessAsUser; external advapi32 name 'CreateProcessAsUserA';
+{$IFDEF _D3orHigher}
+function GetCurrentHwProfile; external advapi32 name 'GetCurrentHwProfileA';
+{$ENDIF _D3orHigher}
+function GetFileSecurity; external advapi32 name 'GetFileSecurityA';
+function GetUserName; external advapi32 name 'GetUserNameA';
+function InitiateSystemShutdown; external advapi32 name 'InitiateSystemShutdownA';
+function LogonUser; external advapi32 name 'LogonUserA';
+function LookupAccountName; external advapi32 name 'LookupAccountNameA';
+function LookupAccountSid; external advapi32 name 'LookupAccountSidA';
+function LookupPrivilegeDisplayName; external advapi32 name 'LookupPrivilegeDisplayNameA';
+function LookupPrivilegeName; external advapi32 name 'LookupPrivilegeNameA';
+function LookupPrivilegeValue; external advapi32 name 'LookupPrivilegeValueA';
+function ObjectCloseAuditAlarm; external advapi32 name 'ObjectCloseAuditAlarmA';
+function ObjectDeleteAuditAlarm; external advapi32 name 'ObjectDeleteAuditAlarmA';
+function ObjectOpenAuditAlarm; external advapi32 name 'ObjectOpenAuditAlarmA';
+function ObjectPrivilegeAuditAlarm; external advapi32 name 'ObjectPrivilegeAuditAlarmA';
+function OpenBackupEventLog; external advapi32 name 'OpenBackupEventLogA';
+function OpenEventLog; external advapi32 name 'OpenEventLogA';
+function PrivilegedServiceAuditAlarm; external advapi32 name 'PrivilegedServiceAuditAlarmA';
+function ReadEventLog; external advapi32 name 'ReadEventLogA';
+function RegConnectRegistry; external advapi32 name 'RegConnectRegistryA';
+function RegCreateKey; external advapi32 name 'RegCreateKeyA';
+function RegCreateKeyEx; external advapi32 name 'RegCreateKeyExA';
+function RegDeleteKey; external advapi32 name 'RegDeleteKeyA';
+function RegDeleteValue; external advapi32 name 'RegDeleteValueA';
+function RegEnumKeyEx; external advapi32 name 'RegEnumKeyExA';
+function RegEnumKey; external advapi32 name 'RegEnumKeyA';
+function RegEnumValue; external advapi32 name 'RegEnumValueA';
+function RegLoadKey; external advapi32 name 'RegLoadKeyA';
+function RegOpenKey; external advapi32 name 'RegOpenKeyA';
+function RegOpenKeyEx; external advapi32 name 'RegOpenKeyExA';
+function RegQueryInfoKey; external advapi32 name 'RegQueryInfoKeyA';
+function RegQueryMultipleValues; external advapi32 name 'RegQueryMultipleValuesA';
+function RegQueryValue; external advapi32 name 'RegQueryValueA';
+function RegQueryValueEx; external advapi32 name 'RegQueryValueExA';
+function RegReplaceKey; external advapi32 name 'RegReplaceKeyA';
+function RegRestoreKey; external advapi32 name 'RegRestoreKeyA';
+function RegSaveKey; external advapi32 name 'RegSaveKeyA';
+function RegSetValue; external advapi32 name 'RegSetValueA';
+function RegSetValueEx; external advapi32 name 'RegSetValueExA';
+function RegUnLoadKey; external advapi32 name 'RegUnLoadKeyA';
+function RegisterEventSource; external advapi32 name 'RegisterEventSourceA';
+function ReportEvent; external advapi32 name 'ReportEventA';
+function SetFileSecurity; external advapi32 name 'SetFileSecurityA';
+function AddAtom; external kernel32 name 'AddAtomA';
+function BeginUpdateResource; external kernel32 name 'BeginUpdateResourceA';
+function BuildCommDCB; external kernel32 name 'BuildCommDCBA';
+function BuildCommDCBAndTimeouts; external kernel32 name 'BuildCommDCBAndTimeoutsA';
+function CallNamedPipe; external kernel32 name 'CallNamedPipeA';
+function CommConfigDialog; external kernel32 name 'CommConfigDialogA';
+function CompareString; external kernel32 name 'CompareStringA';
+function CopyFile; external kernel32 name 'CopyFileA';
+{$IFDEF _D3orHigher}
+function CopyFileEx; external kernel32 name 'CopyFileExA';
+{$ENDIF _D3orHigher}
+function CreateDirectory; external kernel32 name 'CreateDirectoryA';
+function CreateDirectoryEx; external kernel32 name 'CreateDirectoryExA';
+function CreateEvent; external kernel32 name 'CreateEventA';
+function CreateFile; external kernel32 name 'CreateFileA';
+function CreateFileMapping; external kernel32 name 'CreateFileMappingA';
+function CreateHardLink; external kernel32 name 'CreateHardLinkA';
+function CreateMailslot; external kernel32 name 'CreateMailslotA';
+function CreateNamedPipe; external kernel32 name 'CreateNamedPipeA';
+function CreateProcess; external kernel32 name 'CreateProcessA';
+function CreateSemaphore; external kernel32 name 'CreateSemaphoreA';
+function CreateWaitableTimer; external kernel32 name 'CreateWaitableTimerA';
+function DefineDosDevice; external kernel32 name 'DefineDosDeviceA';
+function DeleteFile; external kernel32 name 'DeleteFileA';
+function EndUpdateResource; external kernel32 name 'EndUpdateResourceA';
+function EnumCalendarInfo; external kernel32 name 'EnumCalendarInfoA';
+function EnumDateFormats; external kernel32 name 'EnumDateFormatsA';
+function EnumResourceLanguages; external kernel32 name 'EnumResourceLanguagesA';
+function EnumResourceNames; external kernel32 name 'EnumResourceNamesA';
+function EnumResourceTypes; external kernel32 name 'EnumResourceTypesA';
+function EnumSystemCodePages; external kernel32 name 'EnumSystemCodePagesA';
+function EnumSystemLocales; external kernel32 name 'EnumSystemLocalesA';
+function EnumTimeFormats; external kernel32 name 'EnumTimeFormatsA';
+function ExpandEnvironmentStrings; external kernel32 name 'ExpandEnvironmentStringsA';
+procedure FatalAppExit; external kernel32 name 'FatalAppExitA';
+function FillConsoleOutputCharacter; external kernel32 name 'FillConsoleOutputCharacterA';
+function FindAtom; external kernel32 name 'FindAtomA';
+function FindFirstChangeNotification; external kernel32 name 'FindFirstChangeNotificationA';
+function FindFirstFile; external kernel32 name 'FindFirstFileA';
+{$IFDEF _D3orHigher}
+function FindFirstFileEx; external kernel32 name 'FindFirstFileExA';
+{$ENDIF _D3orHigher}
+function FindNextFile; external kernel32 name 'FindNextFileA';
+function FindResource; external kernel32 name 'FindResourceA';
+function FindResourceEx; external kernel32 name 'FindResourceExA';
+function FoldString; external kernel32 name 'FoldStringA';
+function FormatMessage; external kernel32 name 'FormatMessageA';
+function FreeEnvironmentStrings; external kernel32 name 'FreeEnvironmentStringsA';
+function GetAtomName; external kernel32 name 'GetAtomNameA';
+function GetBinaryType; external kernel32 name 'GetBinaryTypeA';
+function GetCommandLine; external kernel32 name 'GetCommandLineA';
+function GetCompressedFileSize; external kernel32 name 'GetCompressedFileSizeA';
+function GetComputerName; external kernel32 name 'GetComputerNameA';
+function GetConsoleTitle; external kernel32 name 'GetConsoleTitleA';
+function GetCurrencyFormat; external kernel32 name 'GetCurrencyFormatA';
+function GetCurrentDirectory; external kernel32 name 'GetCurrentDirectoryA';
+function GetDateFormat; external kernel32 name 'GetDateFormatA';
+function GetDefaultCommConfig; external kernel32 name 'GetDefaultCommConfigA';
+function GetDiskFreeSpace; external kernel32 name 'GetDiskFreeSpaceA';
+function GetDiskFreeSpaceEx; external kernel32 name 'GetDiskFreeSpaceExA';
+function GetDriveType; external kernel32 name 'GetDriveTypeA';
+function GetEnvironmentStrings; external kernel32 name 'GetEnvironmentStringsA';
+function GetEnvironmentVariable(lpName: PKOLChar; lpBuffer: PKOLChar;
+ nSize: DWORD): DWORD; external kernel32 name 'GetEnvironmentVariableA';
+function GetFileAttributes; external kernel32 name 'GetFileAttributesA';
+{$IFDEF _D3orHigher}
+function GetFileAttributesEx; external kernel32 name 'GetFileAttributesExA';
+{$ENDIF _D3orHigher}
+function GetFullPathName; external kernel32 name 'GetFullPathNameA';
+function GetLocaleInfo; external kernel32 name 'GetLocaleInfoA';
+function GetLogicalDriveStrings; external kernel32 name 'GetLogicalDriveStringsA';
+function GetModuleFileName; external kernel32 name 'GetModuleFileNameA';
+function GetModuleHandle; external kernel32 name 'GetModuleHandleA';
+function GetNamedPipeHandleState; external kernel32 name 'GetNamedPipeHandleStateA';
+function GetNumberFormat; external kernel32 name 'GetNumberFormatA';
+function GetPrivateProfileInt; external kernel32 name 'GetPrivateProfileIntA';
+function GetPrivateProfileSection; external kernel32 name 'GetPrivateProfileSectionA';
+function GetPrivateProfileSectionNames; external kernel32 name 'GetPrivateProfileSectionNamesA';
+function GetPrivateProfileString; external kernel32 name 'GetPrivateProfileStringA';
+function GetProfileInt; external kernel32 name 'GetProfileIntA';
+function GetProfileSection; external kernel32 name 'GetProfileSectionA';
+function GetProfileString; external kernel32 name 'GetProfileStringA';
+function GetShortPathName; external kernel32 name 'GetShortPathNameA';
+procedure GetStartupInfo; external kernel32 name 'GetStartupInfoA';
+function GetStringTypeEx; external kernel32 name 'GetStringTypeExA';
+function GetSystemDirectory; external kernel32 name 'GetSystemDirectoryA';
+function GetTempFileName; external kernel32 name 'GetTempFileNameA';
+function GetTempPath; external kernel32 name 'GetTempPathA';
+function GetTimeFormat; external kernel32 name 'GetTimeFormatA';
+function GetVersionEx; external kernel32 name 'GetVersionExA';
+function GetVolumeInformation; external kernel32 name 'GetVolumeInformationA';
+function GetWindowsDirectory; external kernel32 name 'GetWindowsDirectoryA';
+function GlobalAddAtom; external kernel32 name 'GlobalAddAtomA';
+function GlobalFindAtom; external kernel32 name 'GlobalFindAtomA';
+function GlobalGetAtomName; external kernel32 name 'GlobalGetAtomNameA';
+function IsBadStringPtr; external kernel32 name 'IsBadStringPtrA';
+function LCMapString; external kernel32 name 'LCMapStringA';
+function LoadLibrary; external kernel32 name 'LoadLibraryA';
+function LoadLibraryEx; external kernel32 name 'LoadLibraryExA';
+function MoveFile; external kernel32 name 'MoveFileA';
+function MoveFileEx; external kernel32 name 'MoveFileExA';
+{$IFDEF _D3orHigher}
+function MoveFileWithProgress; external kernel32 name 'MoveFileWithProgressA';
+{$ENDIF _D3orHigher}
+function OpenEvent; external kernel32 name 'OpenEventA';
+function OpenFileMapping; external kernel32 name 'OpenFileMappingA';
+function OpenMutex; external kernel32 name 'OpenMutexA';
+function OpenSemaphore; external kernel32 name 'OpenSemaphoreA';
+function OpenWaitableTimer; external kernel32 name 'OpenWaitableTimerA';
+procedure OutputDebugString; external kernel32 name 'OutputDebugStringA';
+function PeekConsoleInput; external kernel32 name 'PeekConsoleInputA';
+function QueryDosDevice; external kernel32 name 'QueryDosDeviceA';
+function QueryRecoveryAgents; external kernel32 name 'QueryRecoveryAgentsA';
+function ReadConsole; external kernel32 name 'ReadConsoleA';
+function ReadConsoleInput; external kernel32 name 'ReadConsoleInputA';
+function ReadConsoleOutput; external kernel32 name 'ReadConsoleOutputA';
+function ReadConsoleOutputCharacter; external kernel32 name 'ReadConsoleOutputCharacterA';
+function RemoveDirectory; external kernel32 name 'RemoveDirectoryA';
+function ScrollConsoleScreenBuffer; external kernel32 name 'ScrollConsoleScreenBufferA';
+function SearchPath; external kernel32 name 'SearchPathA';
+function SetComputerName; external kernel32 name 'SetComputerNameA';
+function SetConsoleTitle; external kernel32 name 'SetConsoleTitleA';
+function SetCurrentDirectory; external kernel32 name 'SetCurrentDirectoryA';
+function SetDefaultCommConfig; external kernel32 name 'SetDefaultCommConfigA';
+function SetEnvironmentVariable; external kernel32 name 'SetEnvironmentVariableA';
+function SetFileAttributes; external kernel32 name 'SetFileAttributesA';
+function SetLocaleInfo; external kernel32 name 'SetLocaleInfoA';
+function SetVolumeLabel; external kernel32 name 'SetVolumeLabelA';
+function UpdateResource; external kernel32 name 'UpdateResourceA';
+function VerLanguageName; external kernel32 name 'VerLanguageNameA';
+function WaitNamedPipe; external kernel32 name 'WaitNamedPipeA';
+function WriteConsole; external kernel32 name 'WriteConsoleA';
+function WriteConsoleInput; external kernel32 name 'WriteConsoleInputA';
+function WriteConsoleOutput; external kernel32 name 'WriteConsoleOutputA';
+function WriteConsoleOutputCharacter; external kernel32 name 'WriteConsoleOutputCharacterA';
+function WritePrivateProfileSection; external kernel32 name 'WritePrivateProfileSectionA';
+function WritePrivateProfileString; external kernel32 name 'WritePrivateProfileStringA';
+function WriteProfileSection; external kernel32 name 'WriteProfileSectionA';
+function WriteProfileString; external kernel32 name 'WriteProfileStringA';
+function lstrcat; external kernel32 name 'lstrcatA';
+function lstrcmp; external kernel32 name 'lstrcmpA';
+function lstrcmpi; external kernel32 name 'lstrcmpiA';
+function lstrcpy; external kernel32 name 'lstrcpyA';
+function lstrcpyn; external kernel32 name 'lstrcpynA';
+function lstrlen; external kernel32 name 'lstrlenA';
+function MultinetGetConnectionPerformance; external mpr name 'MultinetGetConnectionPerformanceA';
+function WNetAddConnection2; external mpr name 'WNetAddConnection2A';
+function WNetAddConnection3; external mpr name 'WNetAddConnection3A';
+function WNetAddConnection; external mpr name 'WNetAddConnectionA';
+function WNetCancelConnection2; external mpr name 'WNetCancelConnection2A';
+function WNetCancelConnection; external mpr name 'WNetCancelConnectionA';
+function WNetConnectionDialog1; external mpr name 'WNetConnectionDialog1A';
+function WNetDisconnectDialog1; external mpr name 'WNetDisconnectDialog1A';
+function WNetEnumResource; external mpr name 'WNetEnumResourceA';
+function WNetGetConnection; external mpr name 'WNetGetConnectionA';
+function WNetGetLastError; external mpr name 'WNetGetLastErrorA';
+function WNetGetNetworkInformation; external mpr name 'WNetGetNetworkInformationA';
+function WNetGetProviderName; external mpr name 'WNetGetProviderNameA';
+function WNetGetResourceParent; external mpr name 'WNetGetResourceParentA';
+function WNetGetUniversalName; external mpr name 'WNetGetUniversalNameA';
+function WNetGetUser; external mpr name 'WNetGetUserA';
+function WNetOpenEnum; external mpr name 'WNetOpenEnumA';
+function WNetSetConnection; external mpr name 'WNetSetConnectionA';
+function WNetUseConnection; external mpr name 'WNetUseConnectionA';
+function GetFileVersionInfo; external version name 'GetFileVersionInfoA';
+function GetFileVersionInfoSize; external version name 'GetFileVersionInfoSizeA';
+function VerFindFile; external version name 'VerFindFileA';
+function VerInstallFile; external version name 'VerInstallFileA';
+function VerQueryValue; external version name 'VerQueryValueA';
+function GetPrivateProfileStruct; external kernel32 name 'GetPrivateProfileStructA';
+function WritePrivateProfileStruct; external kernel32 name 'WritePrivateProfileStructA';
+function AddFontResource; external gdi32 name 'AddFontResourceA';
+function AddFontResourceEx; external gdi32 name 'AddFontResourceExA';
+function CopyEnhMetaFile; external gdi32 name 'CopyEnhMetaFileA';
+function CopyMetaFile; external gdi32 name 'CopyMetaFileA';
+function CreateColorSpace; external gdi32 name 'CreateColorSpaceA';
+function CreateDC; external gdi32 name 'CreateDCA';
+function CreateEnhMetaFile; external gdi32 name 'CreateEnhMetaFileA';
+function CreateFont; external gdi32 name 'CreateFontA';
+function CreateFontIndirect; external gdi32 name 'CreateFontIndirectA';
+function CreateFontIndirectEx; external gdi32 name 'CreateFontIndirectExA';
+function CreateIC; external gdi32 name 'CreateICA';
+function CreateMetaFile; external gdi32 name 'CreateMetaFileA';
+function CreateScalableFontResource; external gdi32 name 'CreateScalableFontResourceA';
+function DeviceCapabilities; external gdi32 name 'DeviceCapabilitiesA';
+function EnumFontFamilies; external gdi32 name 'EnumFontFamiliesA';
+function EnumFontFamiliesEx; external gdi32 name 'EnumFontFamiliesExA';
+function EnumFonts; external gdi32 name 'EnumFontsA';
+function EnumICMProfiles; external gdi32 name 'EnumICMProfilesA';
+function ExtTextOut; external gdi32 name 'ExtTextOutA';
+function GetCharABCWidths; external gdi32 name 'GetCharABCWidthsA';
+function GetCharABCWidthsFloat; external gdi32 name 'GetCharABCWidthsFloatA';
+function GetCharWidth32; external gdi32 name 'GetCharWidth32A';
+function GetCharWidth; external gdi32 name 'GetCharWidthA';
+function GetCharWidthFloat; external gdi32 name 'GetCharWidthFloatA';
+function GetCharacterPlacement; external gdi32 name 'GetCharacterPlacementA';
+function GetEnhMetaFile; external gdi32 name 'GetEnhMetaFileA';
+function GetEnhMetaFileDescription; external gdi32 name 'GetEnhMetaFileDescriptionA';
+function GetGlyphIndices; external gdi32 name 'GetGlyphIndicesA';
+function GetGlyphOutline; external gdi32 name 'GetGlyphOutlineA';
+function GetICMProfile; external gdi32 name 'GetICMProfileA';
+function GetLogColorSpace; external gdi32 name 'GetLogColorSpaceA';
+function GetMetaFile; external gdi32 name 'GetMetaFileA';
+function GetObject; external gdi32 name 'GetObjectA';
+function GetOutlineTextMetrics; external gdi32 name 'GetOutlineTextMetricsA';
+function GetTextExtentExPoint; external gdi32 name 'GetTextExtentExPointA';
+function GetTextExtentPoint32; external gdi32 name 'GetTextExtentPoint32A';
+function GetTextExtentPoint; external gdi32 name 'GetTextExtentPointA';
+function GetTextFace; external gdi32 name 'GetTextFaceA';
+function GetTextMetrics; external gdi32 name 'GetTextMetricsA';
+function PolyTextOut; external gdi32 name 'PolyTextOutA';
+function RemoveFontResource; external gdi32 name 'RemoveFontResourceA';
+function RemoveFontResourceEx; external gdi32 name 'RemoveFontResourceExA';
+function ResetDC; external gdi32 name 'ResetDCA';
+function SetICMProfile; external gdi32 name 'SetICMProfileA';
+function StartDoc; external gdi32 name 'StartDocA';
+function TextOut; external gdi32 name 'TextOutA';
+function UpdateICMRegKey; external gdi32 name 'UpdateICMRegKeyA';
+function wglUseFontBitmaps; external opengl32 name 'wglUseFontBitmapsA';
+function wglUseFontOutlines; external opengl32 name 'wglUseFontOutlinesA';
+function AnsiToOem; external user32 name 'CharToOemA';
+function AnsiToOemBuff; external user32 name 'CharToOemBuffA';
+function AnsiUpper; external user32 name 'CharUpperA';
+function AnsiUpperBuff; external user32 name 'CharUpperBuffA';
+function AnsiLower; external user32 name 'CharLowerA';
+function AnsiLowerBuff; external user32 name 'CharLowerBuffA';
+function AnsiNext; external user32 name 'CharNextA';
+function AnsiPrev; external user32 name 'CharPrevA';
+function AppendMenu; external user32 name 'AppendMenuA';
+//function BroadcastSystemMessage; external user32 name 'BroadcastSystemMessageA';
+//function BroadcastSystemMessageA; external user32 name 'BroadcastSystemMessageA';
+function CallMsgFilter; external user32 name 'CallMsgFilterA';
+function CallWindowProc; external user32 name 'CallWindowProcA';
+function ChangeDisplaySettings; external user32 name 'ChangeDisplaySettingsA';
+function ChangeDisplaySettingsEx; external user32 name 'ChangeDisplaySettingsExA';
+function ChangeMenu; external user32 name 'ChangeMenuA';
+function CharLower; external user32 name 'CharLowerA';
+function CharLowerBuff; external user32 name 'CharLowerBuffA';
+function CharNext; external user32 name 'CharNextA';
+function CharNextEx; external user32 name 'CharNextExA';
+function CharPrev; external user32 name 'CharPrevA';
+function CharPrevEx; external user32 name 'CharPrevExA';
+function CharToOem; external user32 name 'CharToOemA';
+function CharToOemBuff; external user32 name 'CharToOemBuffA';
+function CharUpper; external user32 name 'CharUpperA';
+function CharUpperBuff; external user32 name 'CharUpperBuffA';
+function CopyAcceleratorTable; external user32 name 'CopyAcceleratorTableA';
+function CreateAcceleratorTable; external user32 name 'CreateAcceleratorTableA';
+function CreateDesktop; external user32 name 'CreateDesktopA';
+function CreateDialogIndirectParam; external user32 name 'CreateDialogIndirectParamA';
+function CreateDialogParam; external user32 name 'CreateDialogParamA';
+function CreateMDIWindow; external user32 name 'CreateMDIWindowA';
+function CreateWindowEx; external user32 name 'CreateWindowExA';
+function CreateWindowStation; external user32 name 'CreateWindowStationA';
+function DefDlgProc; external user32 name 'DefDlgProcA';
+function DefFrameProc; external user32 name 'DefFrameProcA';
+function DefMDIChildProc; external user32 name 'DefMDIChildProcA';
+function DefWindowProc; external user32 name 'DefWindowProcA';
+function DialogBoxIndirectParam; external user32 name 'DialogBoxIndirectParamA';
+function DialogBoxParam; external user32 name 'DialogBoxParamA';
+function DispatchMessage; external user32 name 'DispatchMessageA';
+function DlgDirList; external user32 name 'DlgDirListA';
+function DlgDirListComboBox; external user32 name 'DlgDirListComboBoxA';
+function DlgDirSelectComboBoxEx; external user32 name 'DlgDirSelectComboBoxExA';
+function DlgDirSelectEx; external user32 name 'DlgDirSelectExA';
+function DrawState; external user32 name 'DrawStateA';
+function DrawText; external user32 name 'DrawTextA';
+function DrawTextEx; external user32 name 'DrawTextExA';
+function EnumDesktops; external user32 name 'EnumDesktopsA';
+function EnumDisplaySettings; external user32 name 'EnumDisplaySettingsA';
+function EnumDisplayDevices; external user32 name 'EnumDisplayDevicesA';
+function EnumProps; external user32 name 'EnumPropsA';
+function EnumPropsEx; external user32 name 'EnumPropsExA';
+function EnumWindowStations; external user32 name 'EnumWindowStationsA';
+function FindWindow; external user32 name 'FindWindowA';
+function FindWindowEx; external user32 name 'FindWindowExA';
+function GetAltTabInfo; external user32 name 'GetAltTabInfoA';
+function GetClassInfo; external user32 name 'GetClassInfoA';
+function GetClassInfoEx; external user32 name 'GetClassInfoExA';
+function GetClassLong; external user32 name 'GetClassLongA';
+function GetClassName; external user32 name 'GetClassNameA';
+function GetClipboardFormatName; external user32 name 'GetClipboardFormatNameA';
+function GetDlgItemText; external user32 name 'GetDlgItemTextA';
+function GetKeyNameText; external user32 name 'GetKeyNameTextA';
+function GetKeyboardLayoutName; external user32 name 'GetKeyboardLayoutNameA';
+function GetMenuItemInfo; external user32 name 'GetMenuItemInfoA';
+function GetMenuString; external user32 name 'GetMenuStringA';
+function GetMessage; external user32 name 'GetMessageA';
+function GetProp; external user32 name 'GetPropA';
+function GetTabbedTextExtent; external user32 name 'GetTabbedTextExtentA';
+function GetUserObjectInformation; external user32 name 'GetUserObjectInformationA';
+function GetWindowLong; external user32 name 'GetWindowLongA';
+function GetWindowModuleFileName; external user32 name 'GetWindowModuleFileNameA';
+function GetWindowText; external user32 name 'GetWindowTextA';
+function GetWindowTextLength; external user32 name 'GetWindowTextLengthA';
+function GrayString; external user32 name 'GrayStringA';
+function InsertMenu; external user32 name 'InsertMenuA';
+function InsertMenuItem; external user32 name 'InsertMenuItemA';
+function IsCharAlpha; external user32 name 'IsCharAlphaA';
+function IsCharAlphaNumeric; external user32 name 'IsCharAlphaNumericA';
+function IsCharLower; external user32 name 'IsCharLowerA';
+function IsCharUpper; external user32 name 'IsCharUpperA';
+function IsDialogMessage; external user32 name 'IsDialogMessageA';
+function LoadAccelerators; external user32 name 'LoadAcceleratorsA';
+function LoadBitmap; external user32 name 'LoadBitmapA';
+function LoadCursor; external user32 name 'LoadCursorA';
+function LoadCursorFromFile; external user32 name 'LoadCursorFromFileA';
+function LoadIcon; external user32 name 'LoadIconA';
+function LoadImage; external user32 name 'LoadImageA';
+function LoadKeyboardLayout; external user32 name 'LoadKeyboardLayoutA';
+function LoadMenu; external user32 name 'LoadMenuA';
+function LoadMenuIndirect; external user32 name 'LoadMenuIndirectA';
+function LoadString; external user32 name 'LoadStringA';
+function MapVirtualKey; external user32 name 'MapVirtualKeyA';
+function MapVirtualKeyEx; external user32 name 'MapVirtualKeyExA';
+function MessageBox; external user32 name 'MessageBoxA';
+function MessageBoxEx; external user32 name 'MessageBoxExA';
+function MessageBoxIndirect; external user32 name 'MessageBoxIndirectA';
+function ModifyMenu; external user32 name 'ModifyMenuA';
+function OemToAnsi; external user32 name 'OemToCharA';
+function OemToAnsiBuff; external user32 name 'OemToCharBuffA';
+function OemToChar; external user32 name 'OemToCharA';
+function OemToCharBuff; external user32 name 'OemToCharBuffA';
+function OpenDesktop; external user32 name 'OpenDesktopA';
+function OpenWindowStation; external user32 name 'OpenWindowStationA';
+function PeekMessage; external user32 name 'PeekMessageA';
+function PostMessage; external user32 name 'PostMessageA';
+function PostThreadMessage; external user32 name 'PostThreadMessageA';
+function RealGetWindowClass; external user32 name 'RealGetWindowClassA';
+function RegisterClass; external user32 name 'RegisterClassA';
+function RegisterClassEx; external user32 name 'RegisterClassExA';
+function RegisterClipboardFormat; external user32 name 'RegisterClipboardFormatA';
+function RegisterDeviceNotification; external user32 name 'RegisterDeviceNotificationA';
+function RegisterWindowMessage; external user32 name 'RegisterWindowMessageA';
+function RemoveProp; external user32 name 'RemovePropA';
+function SendDlgItemMessage; external user32 name 'SendDlgItemMessageA';
+function SendMessage; external user32 name 'SendMessageA';
+function SendMessageCallback; external user32 name 'SendMessageCallbackA';
+function SendMessageTimeout; external user32 name 'SendMessageTimeoutA';
+function SendNotifyMessage; external user32 name 'SendNotifyMessageA';
+function SetClassLong; external user32 name 'SetClassLongA';
+function SetDlgItemText; external user32 name 'SetDlgItemTextA';
+function SetMenuItemInfo; external user32 name 'SetMenuItemInfoA';
+function SetProp; external user32 name 'SetPropA';
+function SetUserObjectInformation; external user32 name 'SetUserObjectInformationA';
+function SetWindowLong; external user32 name 'SetWindowLongA';
+function SetWindowText; external user32 name 'SetWindowTextA';
+function SetWindowsHook; external user32 name 'SetWindowsHookA';
+function SetWindowsHookEx; external user32 name 'SetWindowsHookExA';
+function SystemParametersInfo; external user32 name 'SystemParametersInfoA';
+function TabbedTextOut; external user32 name 'TabbedTextOutA';
+function TranslateAccelerator; external user32 name 'TranslateAcceleratorA';
+function UnregisterClass; external user32 name 'UnregisterClassA';
+function VkKeyScan; external user32 name 'VkKeyScanA';
+function VkKeyScanEx; external user32 name 'VkKeyScanExA';
+function WinHelp; external user32 name 'WinHelpA';
+function wsprintf; external user32 name 'wsprintfA';
+function wvsprintf; external user32 name 'wvsprintfA';
+// NT 4.0 bug workaround - NT 4.0 doesn't test bInitialOwner for zero/nonzero, it tests for 1
+function _CreateMutex(lpMutexAttributes: PSecurityAttributes;
+ bInitialOwner: Integer; lpName: PAnsiChar): THandle; stdcall;
+ external kernel32 name 'CreateMutexA';
+function CreateMutex(lpMutexAttributes: PSecurityAttributes; bInitialOwner: BOOL; lpName: PAnsiChar): THandle;
+begin
+ Result := _CreateMutex(lpMutexAttributes, Integer(Boolean(bInitialOwner)), lpName);
+end;
+
+{$ENDIF implementation_part} ///////////////////////////////////////////////////
diff --git a/plugins/Libs/KOL_unicode.inc b/plugins/Libs/KOL_unicode.inc
new file mode 100644
index 0000000000..30ab926812
--- /dev/null
+++ b/plugins/Libs/KOL_unicode.inc
@@ -0,0 +1,1277 @@
+{*******************************************************************************
+ KOL_unicode.inc
+ Some redeclarations from Windows.pas for case, when UNICODE_CTRLS symbol is on.
+*******************************************************************************}
+{$IFDEF interface_part} ////////////////////////////////////////////////////////
+ MakeIntAtom = MakeIntAtomW;
+ {$IFDEF _D4orHigher}
+ PRecoveryAgentInformation = PRecoveryAgentInformationW;
+ TRecoveryAgentInformation = TRecoveryAgentInformationW;
+ RECOVERY_AGENT_INFORMATION = RECOVERY_AGENT_INFORMATIONW;
+ {$ENDIF}
+ PWin32FindData = PWin32FindDataW;
+ TWin32FindData = TWin32FindDataW;
+ PHWProfileInfo = PHWProfileInfoW;
+ THWProfileInfo = THWProfileInfoW;
+ POSVersionInfo = POSVersionInfoW;
+ TOSVersionInfo = TOSVersionInfoW;
+ PLogColorSpace = PLogColorSpaceW;
+ TLogColorSpace = TLogColorSpaceW;
+ {$IFDEF _D4orHigher}
+ PTextMetric = PTextMetricW;
+ tagTEXTMETRIC = tagTEXTMETRICW;
+ TTextMetric = TTextMetricW;
+ TEXTMETRIC = TEXTMETRICW;
+ PNewTextMetric = PNewTextMetricW;
+ TNewTextMetric = TNewTextMetricW;
+ NEWTEXTMETRIC = NEWTEXTMETRICW;
+ PNewTextMetricEx = PNewTextMetricExW;
+ {$ENDIF}
+ PLogFont = PLogFontW;
+ TLogFont = TLogFontW;
+ {$IFDEF _D4orHigher}
+ PEnumLogFont = PEnumLogFontW;
+ TEnumLogFont = TEnumLogFontW;
+ ENUMLOGFONT = ENUMLOGFONTW;
+ PEnumLogFontEx = PEnumLogFontExW;
+ TEnumLogFontEx = TEnumLogFontExW;
+ ENUMLOGFONTEX = ENUMLOGFONTEXW;
+ PExtLogFont = PExtLogFontW;
+ tagEXTLOGFONT = tagEXTLOGFONTW;
+ TExtLogFont = TExtLogFontW;
+ EXTLOGFONT = EXTLOGFONTW;
+ {$ENDIF}
+ PDeviceMode = PDeviceModeW;
+ TDeviceMode = TDeviceModeW;
+ {$IFDEF _D4orHigher}
+ DEVMODE = DEVMODEW;
+ PDisplayDevice = PDisplayDeviceW;
+ TDisplayDevice = TDisplayDeviceW;
+ {$ENDIF}
+ POutlineTextmetric = POutlineTextmetricW;
+ TOutlineTextmetric = TOutlineTextmetricW;
+ {$IFDEF _D4orHigher}
+ OUTLINETEXTMETRIC = OUTLINETEXTMETRICW;
+ {$ENDIF}
+ PPolyText = PPolyTextW;
+ {$IFDEF _D4orHigher}
+ tagPOLYTEXT = tagPOLYTEXTW;
+ POLYTEXT = POLYTEXTW;
+ {$ENDIF}
+ TPolyText = TPolyTextW;
+ PGCPResults = PGCPResultsW;
+ TGCPResults = TGCPResultsW;
+ {$IFDEF _D4orHigher}
+ GCP_RESULTS = GCP_RESULTSW;
+ {$ENDIF}
+ TFNOldFontEnumProc = TFNOldFontEnumProcW;
+ TFNFontEnumProc = TFNFontEnumProcW;
+ {$IFDEF _D4orHigher}
+ PAxisInfo = PAxisInfoW;
+ PAxesList = PAxesListW;
+ PEnumLogFontExDV = PEnumLogFontExDVW;
+ PEnumTextMetric = PEnumTextMetricW;
+ {$ENDIF}
+ PDocInfo = PDocInfoW;
+ TDocInfo = TDocInfoW;
+ {$IFDEF _D4orHigher}
+ DOCINFO = DOCINFOW;
+ {$ENDIF}
+ MakeIntResource = MakeIntResourceW;
+ PCreateStruct = PCreateStructW;
+ TCreateStruct = TCreateStructW;
+ {$IFDEF _D4orHigher}
+ CREATESTRUCT = CREATESTRUCTW;
+ {$ENDIF}
+ PWndClassEx = PWndClassExW;
+ TWndClassEx = TWndClassExW;
+ {$IFDEF _D4orHigher}
+ WNDCLASSEX = WNDCLASSEXW;
+ {$ENDIF}
+ PWndClass = PWndClassW;
+ TWndClass = TWndClassW;
+ {$IFDEF _D4orHigher}
+ WNDCLASS = WNDCLASSW;
+ {$ENDIF}
+ //PMenuItemInfo = PMenuItemInfoW;
+ //TMenuItemInfo = TMenuItemInfoW;
+ //MENUITEMINFO = MENUITEMINFOW;
+ PMsgBoxParams = PMsgBoxParamsW;
+ TMsgBoxParams = TMsgBoxParamsW;
+ {$IFDEF _D4orHigher}
+ MSGBOXPARAMS = MSGBOXPARAMSW;
+ {$ENDIF}
+ PMDICreateStruct = PMDICreateStructW;
+ TMDICreateStruct = TMDICreateStructW;
+ PMultiKeyHelp = PMultiKeyHelpW;
+ TMultiKeyHelp = TMultiKeyHelpW;
+ {$IFDEF _D4orHigher}
+ MULTIKEYHELP = MULTIKEYHELPW;
+ {$ENDIF}
+ PHelpWinInfo = PHelpWinInfoW;
+ THelpWinInfo = THelpWinInfoW;
+ {$IFDEF _D4orHigher}
+ HELPWININFO = HELPWININFOW;
+ {$ENDIF}
+ PNonClientMetrics = PNonClientMetricsW;
+ TNonClientMetrics = TNonClientMetricsW;
+ {$IFDEF _D4orHigher}
+ NONCLIENTMETRICS = NONCLIENTMETRICSW;
+ {$ENDIF}
+ PIconMetrics = PIconMetricsW;
+ TIconMetrics = TIconMetricsW;
+ {$IFDEF _D4orHigher}
+ ICONMETRICS = ICONMETRICSW;
+ {$ENDIF}
+ PSerialKeys = PSerialKeysW;
+ TSerialKeys = TSerialKeysW;
+ {$IFDEF _D4orHigher}
+ SERIALKEYS = SERIALKEYSW;
+ {$ENDIF}
+ PHighContrast = PHighContrastW;
+ THighContrast = THighContrastW;
+ {$IFDEF _D4orHigher}
+ HIGHCONTRAST = HIGHCONTRASTW;
+ {$ENDIF}
+ PSoundsEntry = PSoundsEntryW;
+ TSoundsEntry = TSoundsEntryW;
+ {$IFDEF _D4orHigher}
+ SOUNDSENTRY = SOUNDSENTRYW;
+ {$ENDIF}
+ PNumberFmt = PNumberFmtW;
+ TNumberFmt = TNumberFmtW;
+ {$IFDEF _D4orHigher}
+ NUMBERFMT = NUMBERFMTW;
+ {$ENDIF}
+ PCurrencyFmt = PCurrencyFmtW;
+ {$IFDEF _D4orHigher}
+ _currencyfmt = _currencyfmtW;
+ {$ENDIF}
+ TCurrencyFmt = TCurrencyFmtW;
+ {$IFDEF _D4orHigher}
+ CURRENCYFMT = CURRENCYFMTW;
+ {$ENDIF}
+ PPValue = PPValueW;
+ {$IFDEF _D4orHigher}
+ pvalue = pvalueW;
+ {$ENDIF}
+ TPValue = TPValueW;
+ PValueEnt = PValueEntW;
+ TValueEnt = TValueEntW;
+ {$IFDEF _D4orHigher}
+ VALENT = VALENTW;
+ {$ENDIF}
+ PNetResource = PNetResourceW;
+ TNetResource = TNetResourceW;
+ {$IFDEF _D4orHigher}
+ NETRESOURCE = NETRESOURCEW;
+ {$ENDIF}
+ PDiscDlgStruct = PDiscDlgStructW;
+ {$IFDEF _D4orHigher}
+ _DISCDLGSTRUCT = _DISCDLGSTRUCTW;
+ {$ENDIF}
+ TDiscDlgStruct = TDiscDlgStructW;
+ {$IFDEF _D4orHigher}
+ DISCDLGSTRUCT = DISCDLGSTRUCTW;
+ {$ENDIF}
+ PUniversalNameInfo = PUniversalNameInfoW;
+ TUniversalNameInfo = TUniversalNameInfoW;
+ {$IFDEF _D4orHigher}
+ UNIVERSAL_NAME_INFO = UNIVERSAL_NAME_INFOW;
+ {$ENDIF}
+ PRemoteNameInfo = PRemoteNameInfoW;
+ TRemoteNameInfo = TRemoteNameInfoW;
+ {$IFDEF _D4orHigher}
+ REMOTE_NAME_INFO = REMOTE_NAME_INFOW;
+ {$ENDIF}
+
+function AbortSystemShutdown(lpMachineName: PKOLChar): BOOL; stdcall;
+function AccessCheckAndAuditAlarm(SubsystemName: PKOLChar;
+ HandleId: Pointer; ObjectTypeName, ObjectName: PKOLChar;
+ SecurityDescriptor: PSecurityDescriptor; DesiredAccess: DWORD;
+ const GenericMapping: TGenericMapping; ObjectCreation: BOOL;
+ var GrantedAccess: DWORD; var AccessStatus, pfGenerateOnClose: BOOL): BOOL; stdcall;
+{$IFDEF _D4orHigher}
+function AccessCheckByTypeAndAuditAlarm(SubsystemName: PKOLChar;
+ HandleId: Pointer; ObjectTypeName, ObjectName: PKOLChar;
+ SecurityDescriptor: PSecurityDescriptor; PrincipalSelfSid: PSID; DesiredAccess: DWORD;
+ AuditType: AUDIT_EVENT_TYPE; Flags: DWORD; ObjectTypeList: PObjectTypeList;
+ ObjectTypeListLength: DWORD; const GenericMapping: TGenericMapping; ObjectCreation: BOOL;
+ var GrantedAccess: DWORD; var AccessStatus, pfGenerateOnClose: BOOL): BOOL; stdcall;
+function AccessCheckByTypeResultListAndAuditAlarm(SubsystemName: PKOLChar;
+ HandleId: Pointer; ObjectTypeName, ObjectName: PKOLChar;
+ SecurityDescriptor: PSecurityDescriptor; PrincipalSelfSid: PSID; DesiredAccess: DWORD;
+ AuditType: AUDIT_EVENT_TYPE; Flags: DWORD; ObjectTypeList: PObjectTypeList;
+ ObjectTypeListLength: DWORD; const GenericMapping: TGenericMapping; ObjectCreation: BOOL;
+ var GrantedAccess: DWORD; var AccessStatusList: DWORD; var pfGenerateOnClose: BOOL): BOOL; stdcall;
+{$ENDIF}
+function BackupEventLog(hEventLog: THandle; lpBackupFileName: PKOLChar): BOOL; stdcall;
+function ClearEventLog(hEventLog: THandle; lpBackupFileName: PKOLChar): BOOL; stdcall;
+function CreateProcessAsUser(hToken: THandle; lpApplicationName: PKOLChar;
+ lpCommandLine: PKOLChar; lpProcessAttributes: PSecurityAttributes;
+ lpThreadAttributes: PSecurityAttributes; bInheritHandles: BOOL;
+ dwCreationFlags: DWORD; lpEnvironment: Pointer; lpCurrentDirectory: PKOLChar;
+ const lpStartupInfo: TStartupInfo; var lpProcessInformation: TProcessInformation): BOOL; stdcall;
+function GetCurrentHwProfile(var lpHwProfileInfo: THWProfileInfo): BOOL; stdcall;
+function GetFileSecurity(lpFileName: PKOLChar; RequestedInformation: SECURITY_INFORMATION;
+ pSecurityDescriptor: PSecurityDescriptor; nLength: DWORD; var lpnLengthNeeded: DWORD): BOOL; stdcall;
+function GetUserName(lpBuffer: PKOLChar; var nSize: DWORD): BOOL; stdcall;
+function InitiateSystemShutdown(lpMachineName, lpMessage: PKOLChar;
+ dwTimeout: DWORD; bForceAppsClosed, bRebootAfterShutdown: BOOL): BOOL; stdcall;
+function LogonUser(lpszUsername, lpszDomain, lpszPassword: PKOLChar;
+ dwLogonType, dwLogonProvider: DWORD; var phToken: THandle): BOOL; stdcall;
+function LookupAccountName(lpSystemName, lpAccountName: PKOLChar;
+ Sid: PSID; var cbSid: DWORD; ReferencedDomainName: PKOLChar;
+ var cbReferencedDomainName: DWORD; var peUse: SID_NAME_USE): BOOL; stdcall;
+function LookupAccountSid(lpSystemName: PKOLChar; Sid: PSID;
+ Name: PKOLChar; var cbName: DWORD; ReferencedDomainName: PKOLChar;
+ var cbReferencedDomainName: DWORD; var peUse: SID_NAME_USE): BOOL; stdcall;
+function LookupPrivilegeDisplayName(lpSystemName, lpName: PKOLChar;
+ lpDisplayName: PKOLChar; var cbDisplayName, lpLanguageId: DWORD): BOOL; stdcall;
+function LookupPrivilegeName(lpSystemName: PKOLChar;
+ var lpLuid: TLargeInteger; lpName: PKOLChar; var cbName: DWORD): BOOL; stdcall;
+function LookupPrivilegeValue(lpSystemName, lpName: PKOLChar;
+ var lpLuid: TLargeInteger): BOOL; stdcall;
+function ObjectCloseAuditAlarm(SubsystemName: PKOLChar;
+ HandleId: Pointer; GenerateOnClose: BOOL): BOOL; stdcall;
+function ObjectDeleteAuditAlarm(SubsystemName: PKOLChar;
+ HandleId: Pointer; GenerateOnClose: BOOL): BOOL; stdcall;
+function ObjectOpenAuditAlarm(SubsystemName: PKOLChar; HandleId: Pointer;
+ ObjectTypeName: PKOLChar; ObjectName: PKOLChar; pSecurityDescriptor: PSecurityDescriptor;
+ ClientToken: THandle; DesiredAccess, GrantedAccess: DWORD;
+ var Privileges: TPrivilegeSet; ObjectCreation, AccessGranted: BOOL;
+ var GenerateOnClose: BOOL): BOOL; stdcall;
+function ObjectPrivilegeAuditAlarm(SubsystemName: PKOLChar;
+ HandleId: Pointer; ClientToken: THandle; DesiredAccess: DWORD;
+ var Privileges: TPrivilegeSet; AccessGranted: BOOL): BOOL; stdcall;
+function OpenBackupEventLog(lpUNCServerName, lpFileName: PKOLChar): THandle; stdcall;
+function OpenEventLog(lpUNCServerName, lpSourceName: PKOLChar): THandle; stdcall;
+function PrivilegedServiceAuditAlarm(SubsystemName, ServiceName: PKOLChar;
+ ClientToken: THandle; var Privileges: TPrivilegeSet; AccessGranted: BOOL): BOOL; stdcall;
+function ReadEventLog(hEventLog: THandle; dwReadFlags, dwRecordOffset: DWORD;
+ lpBuffer: Pointer; nNumberOfBytesToRead: DWORD;
+ var pnBytesRead, pnMinNumberOfBytesNeeded: DWORD): BOOL; stdcall;
+function RegConnectRegistry(lpMachineName: PKOLChar; hKey: HKEY;
+ var phkResult: HKEY): Longint; stdcall;
+function RegCreateKey(hKey: HKEY; lpSubKey: PKOLChar;
+ var phkResult: HKEY): Longint; stdcall;
+function RegCreateKeyEx(hKey: HKEY; lpSubKey: PKOLChar;
+ Reserved: DWORD; lpClass: PKOLChar; dwOptions: DWORD; samDesired: REGSAM;
+ lpSecurityAttributes: PSecurityAttributes; var phkResult: HKEY;
+ lpdwDisposition: PDWORD): Longint; stdcall;
+function RegDeleteKey(hKey: HKEY; lpSubKey: PKOLChar): Longint; stdcall;
+function RegDeleteValue(hKey: HKEY; lpValueName: PKOLChar): Longint; stdcall;
+function RegEnumKeyEx(hKey: HKEY; dwIndex: DWORD; lpName: PKOLChar;
+ var lpcbName: DWORD; lpReserved: Pointer; lpClass: PKOLChar;
+ lpcbClass: PDWORD; lpftLastWriteTime: PFileTime): Longint; stdcall;
+function RegEnumKey(hKey: HKEY; dwIndex: DWORD; lpName: PKOLChar; cbName: DWORD): Longint; stdcall;
+function RegEnumValue(hKey: HKEY; dwIndex: DWORD; lpValueName: PKOLChar;
+ var lpcbValueName: DWORD; lpReserved: Pointer; lpType: PDWORD;
+ lpData: PByte; lpcbData: PDWORD): Longint; stdcall;
+function RegLoadKey(hKey: HKEY; lpSubKey, lpFile: PKOLChar): Longint; stdcall;
+function RegOpenKey(hKey: HKEY; lpSubKey: PKOLChar; var phkResult: HKEY): Longint; stdcall;
+function RegOpenKeyEx(hKey: HKEY; lpSubKey: PKOLChar;
+ ulOptions: DWORD; samDesired: REGSAM; var phkResult: HKEY): Longint; stdcall;
+function RegQueryInfoKey(hKey: HKEY; lpClass: PKOLChar;
+ lpcbClass: PDWORD; lpReserved: Pointer;
+ lpcSubKeys, lpcbMaxSubKeyLen, lpcbMaxClassLen, lpcValues,
+ lpcbMaxValueNameLen, lpcbMaxValueLen, lpcbSecurityDescriptor: PDWORD;
+ lpftLastWriteTime: PFileTime): Longint; stdcall;
+function RegQueryMultipleValues(hKey: HKEY; var ValList;
+ NumVals: DWORD; lpValueBuf: PKOLChar; var ldwTotsize: DWORD): Longint; stdcall;
+function RegQueryValue(hKey: HKEY; lpSubKey: PKOLChar;
+ lpValue: PKOLChar; var lpcbValue: Longint): Longint; stdcall;
+function RegQueryValueEx(hKey: HKEY; lpValueName: PKOLChar;
+ lpReserved: Pointer; lpType: PDWORD; lpData: PByte; lpcbData: PDWORD): Longint; stdcall;
+function RegReplaceKey(hKey: HKEY; lpSubKey: PKOLChar;
+ lpNewFile: PKOLChar; lpOldFile: PKOLChar): Longint; stdcall;
+function RegRestoreKey(hKey: HKEY; lpFile: PKOLChar; dwFlags: DWORD): Longint; stdcall;
+function RegSaveKey(hKey: HKEY; lpFile: PKOLChar;
+ lpSecurityAttributes: PSecurityAttributes): Longint; stdcall;
+function RegSetValue(hKey: HKEY; lpSubKey: PKOLChar;
+ dwType: DWORD; lpData: PKOLChar; cbData: DWORD): Longint; stdcall;
+function RegSetValueEx(hKey: HKEY; lpValueName: PKOLChar;
+ Reserved: DWORD; dwType: DWORD; lpData: Pointer; cbData: DWORD): Longint; stdcall;
+function RegUnLoadKey(hKey: HKEY; lpSubKey: PKOLChar): Longint; stdcall;
+function RegisterEventSource(lpUNCServerName, lpSourceName: PKOLChar): THandle; stdcall;
+function ReportEvent(hEventLog: THandle; wType, wCategory: Word;
+ dwEventID: DWORD; lpUserSid: Pointer; wNumStrings: Word;
+ dwDataSize: DWORD; lpStrings, lpRawData: Pointer): BOOL; stdcall;
+function SetFileSecurity(lpFileName: PKOLChar; SecurityInformation: SECURITY_INFORMATION;
+ pSecurityDescriptor: PSecurityDescriptor): BOOL; stdcall;
+function AddAtom(lpString: PKOLChar): ATOM; stdcall;
+function BeginUpdateResource(pFileName: PKOLChar; bDeleteExistingResources: BOOL): THandle; stdcall;
+function BuildCommDCB(lpDef: PKOLChar; var lpDCB: TDCB): BOOL; stdcall;
+function BuildCommDCBAndTimeouts(lpDef: PKOLChar; var lpDCB: TDCB;
+ var lpCommTimeouts: TCommTimeouts): BOOL; stdcall;
+function CallNamedPipe(lpNamedPipeName: PKOLChar; lpInBuffer: Pointer;
+ nInBufferSize: DWORD; lpOutBuffer: Pointer; nOutBufferSize: DWORD;
+ var lpBytesRead: DWORD; nTimeOut: DWORD): BOOL; stdcall;
+function CommConfigDialog(lpszName: PKOLChar; hWnd: HWND; var lpCC: TCommConfig): BOOL; stdcall;
+function CompareString(Locale: LCID; dwCmpFlags: DWORD; lpString1: PKOLChar;
+ cchCount1: Integer; lpString2: PKOLChar; cchCount2: Integer): Integer; stdcall;
+function CopyFile(lpExistingFileName, lpNewFileName: PKOLChar; bFailIfExists: BOOL): BOOL; stdcall;
+function CopyFileEx(lpExistingFileName, lpNewFileName: PKOLChar;
+ lpProgressRoutine: TFNProgressRoutine; lpData: Pointer; pbCancel: PBool;
+ dwCopyFlags: DWORD): BOOL; stdcall;
+function CreateDirectory(lpPathName: PKOLChar;
+ lpSecurityAttributes: PSecurityAttributes): BOOL; stdcall;
+function CreateDirectoryEx(lpTemplateDirectory, lpNewDirectory: PKOLChar;
+ lpSecurityAttributes: PSecurityAttributes): BOOL; stdcall;
+function CreateEvent(lpEventAttributes: PSecurityAttributes;
+ bManualReset, bInitialState: BOOL; lpName: PKOLChar): THandle; stdcall;
+function CreateFile(lpFileName: PKOLChar; dwDesiredAccess, dwShareMode: DWORD;
+ lpSecurityAttributes: PSecurityAttributes; dwCreationDisposition, dwFlagsAndAttributes: DWORD;
+ hTemplateFile: THandle): THandle; stdcall;
+function CreateFileMapping(hFile: THandle; lpFileMappingAttributes: PSecurityAttributes;
+ flProtect, dwMaximumSizeHigh, dwMaximumSizeLow: DWORD; lpName: PKOLChar): THandle; stdcall;
+function CreateHardLink(lpFileName, lpExistingFileName: PKOLChar;
+ lpSecurityAttributes: PSecurityAttributes): BOOL; stdcall;
+function CreateMailslot(lpName: PKOLChar; nMaxMessageSize: DWORD;
+ lReadTimeout: DWORD; lpSecurityAttributes: PSecurityAttributes): THandle; stdcall;
+function CreateNamedPipe(lpName: PKOLChar;
+ dwOpenMode, dwPipeMode, nMaxInstances, nOutBufferSize, nInBufferSize, nDefaultTimeOut: DWORD;
+ lpSecurityAttributes: PSecurityAttributes): THandle; stdcall;
+function CreateProcess(lpApplicationName: PKOLChar; lpCommandLine: PKOLChar;
+ lpProcessAttributes, lpThreadAttributes: PSecurityAttributes;
+ bInheritHandles: BOOL; dwCreationFlags: DWORD; lpEnvironment: Pointer;
+ lpCurrentDirectory: PKOLChar; const lpStartupInfo: TStartupInfo;
+ var lpProcessInformation: TProcessInformation): BOOL; stdcall;
+function CreateSemaphore(lpSemaphoreAttributes: PSecurityAttributes;
+ lInitialCount, lMaximumCount: Longint; lpName: PKOLChar): THandle; stdcall;
+function CreateWaitableTimer(lpTimerAttributes: PSecurityAttributes; bManualReset: BOOL; lpTimerName: PKOLChar): THandle; stdcall;
+function DefineDosDevice(dwFlags: DWORD; lpDeviceName, lpTargetPath: PKOLChar): BOOL; stdcall;
+function DeleteFile(lpFileName: PKOLChar): BOOL; stdcall;
+function EndUpdateResource(hUpdate: THandle; fDiscard: BOOL): BOOL; stdcall;
+function EnumCalendarInfo(lpCalInfoEnumProc: TFNCalInfoEnumProc; Locale: LCID;
+ Calendar: CALID; CalType: CALTYPE): BOOL; stdcall;
+function EnumDateFormats(lpDateFmtEnumProc: TFNDateFmtEnumProc;
+ Locale: LCID; dwFlags: DWORD): BOOL; stdcall;
+function EnumResourceLanguages(hModule: HMODULE; lpType, lpName: PKOLChar;
+ lpEnumFunc: ENUMRESLANGPROC; lParam: Longint): BOOL; stdcall;
+function EnumResourceNames(hModule: HMODULE; lpType: PKOLChar;
+ lpEnumFunc: ENUMRESNAMEPROC; lParam: Longint): BOOL; stdcall;
+function EnumResourceTypes(hModule: HMODULE; lpEnumFunc: ENUMRESTYPEPROC;
+ lParam: Longint): BOOL; stdcall;
+function EnumSystemCodePages(lpCodePageEnumProc: TFNCodepageEnumProc; dwFlags: DWORD): BOOL; stdcall;
+function EnumSystemLocales(lpLocaleEnumProc: TFNLocaleEnumProc; dwFlags: DWORD): BOOL; stdcall;
+function EnumTimeFormats(lpTimeFmtEnumProc: TFNTimeFmtEnumProc;
+ Locale: LCID; dwFlags: DWORD): BOOL; stdcall;
+function ExpandEnvironmentStrings(lpSrc: PKOLChar; lpDst: PKOLChar; nSize: DWORD): DWORD; stdcall;
+procedure FatalAppExit(uAction: UINT; lpMessageText: PKOLChar); stdcall;
+function FillConsoleOutputCharacter(hConsoleOutput: THandle; cCharacter: KOLChar;
+ nLength: DWORD; dwWriteCoord: TCoord; var lpNumberOfCharsWritten: DWORD): BOOL; stdcall;
+function FindAtom(lpString: PKOLChar): ATOM; stdcall;
+function FindFirstChangeNotification(lpPathName: PKOLChar;
+ bWatchSubtree: BOOL; dwNotifyFilter: DWORD): THandle; stdcall;
+function FindFirstFile(lpFileName: PKOLChar; var lpFindFileData: TWIN32FindData): THandle; stdcall;
+function FindFirstFileEx(lpFileName: PKOLChar; fInfoLevelId: TFindexInfoLevels;
+ lpFindFileData: Pointer; fSearchOp: TFindexSearchOps; lpSearchFilter: Pointer;
+ dwAdditionalFlags: DWORD): BOOL; stdcall;
+function FindNextFile(hFindFile: THandle; var lpFindFileData: TWIN32FindData): BOOL; stdcall;
+function FindResource(hModule: HMODULE; lpName, lpType: PKOLChar): HRSRC; stdcall;
+function FindResourceEx(hModule: HMODULE; lpType, lpName: PKOLChar; wLanguage: Word): HRSRC; stdcall;
+function FoldString(dwMapFlags: DWORD; lpSrcStr: PKOLChar; cchSrc: Integer;
+ lpDestStr: PKOLChar; cchDest: Integer): Integer; stdcall;
+function FormatMessage(dwFlags: DWORD; lpSource: Pointer; dwMessageId: DWORD; dwLanguageId: DWORD;
+ lpBuffer: PKOLChar; nSize: DWORD; Arguments: Pointer): DWORD; stdcall;
+function FreeEnvironmentStrings(EnvBlock: PKOLChar): BOOL; stdcall;
+function GetAtomName(nAtom: ATOM; lpBuffer: PKOLChar; nSize: Integer): UINT; stdcall;
+function GetBinaryType(lpApplicationName: PKOLChar; var lpBinaryType: DWORD): BOOL; stdcall;
+function GetCommandLine: PKOLChar; stdcall;
+function GetCompressedFileSize(lpFileName: PKOLChar; lpFileSizeHigh: PDWORD): DWORD; stdcall;
+function GetComputerName(lpBuffer: PKOLChar; var nSize: DWORD): BOOL; stdcall;
+function GetConsoleTitle(lpConsoleTitle: PKOLChar; nSize: DWORD): DWORD; stdcall;
+function GetCurrencyFormat(Locale: LCID; dwFlags: DWORD; lpValue: PKOLChar;
+ lpFormat: PCurrencyFmt; lpCurrencyStr: PKOLChar; cchCurrency: Integer): Integer; stdcall;
+function GetCurrentDirectory(nBufferLength: DWORD; lpBuffer: PKOLChar): DWORD; stdcall;
+function GetDateFormat(Locale: LCID; dwFlags: DWORD; lpDate: PSystemTime;
+ lpFormat: PKOLChar; lpDateStr: PKOLChar; cchDate: Integer): Integer; stdcall;
+function GetDefaultCommConfig(lpszName: PKOLChar;
+ var lpCC: TCommConfig; var lpdwSize: DWORD): BOOL; stdcall;
+function GetDiskFreeSpace(lpRootPathName: PKOLChar;
+ var lpSectorsPerCluster, lpBytesPerSector, lpNumberOfFreeClusters, lpTotalNumberOfClusters: DWORD): BOOL; stdcall;
+function GetDiskFreeSpaceEx(lpDirectoryName: PKOLChar;
+ var lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes; lpTotalNumberOfFreeBytes: PLargeInteger): BOOL; stdcall;
+function GetDriveType(lpRootPathName: PKOLChar): UINT; stdcall;
+function GetEnvironmentStrings: PKOLChar; stdcall;
+function GetEnvironmentVariable(lpName: PKOLChar; lpBuffer: PKOLChar; nSize: DWORD): DWORD; stdcall;
+ {$IFDEF _D4orHigher} overload; {$ENDIF}
+function GetFileAttributes(lpFileName: PKOLChar): DWORD; stdcall;
+function GetFileAttributesEx(lpFileName: PKOLChar;
+ fInfoLevelId: TGetFileExInfoLevels; lpFileInformation: Pointer): BOOL; stdcall;
+function GetFullPathName(lpFileName: PKOLChar; nBufferLength: DWORD;
+ lpBuffer: PKOLChar; var lpFilePart: PKOLChar): DWORD; stdcall;
+function GetLocaleInfo(Locale: LCID; LCType: LCTYPE; lpLCData: PKOLChar; cchData: Integer): Integer; stdcall;
+function GetLogicalDriveStrings(nBufferLength: DWORD; lpBuffer: PKOLChar): DWORD; stdcall;
+function GetModuleFileName(hModule: HINST; lpFilename: PKOLChar; nSize: DWORD): DWORD; stdcall;
+function GetModuleHandle(lpModuleName: PKOLChar): HMODULE; stdcall;
+function GetNamedPipeHandleState(hNamedPipe: THandle;
+ lpState, lpCurInstances, lpMaxCollectionCount, lpCollectDataTimeout: PDWORD;
+ lpUserName: PKOLChar; nMaxUserNameSize: DWORD): BOOL; stdcall;
+function GetNumberFormat(Locale: LCID; dwFlags: DWORD; lpValue: PKOLChar;
+ lpFormat: PNumberFmt; lpNumberStr: PKOLChar; cchNumber: Integer): Integer; stdcall;
+function GetPrivateProfileInt(lpAppName, lpKeyName: PKOLChar; nDefault: Integer; lpFileName: PKOLChar): UINT; stdcall;
+function GetPrivateProfileSection(lpAppName: PKOLChar; lpReturnedString: PKOLChar; nSize: DWORD; lpFileName: PKOLChar): DWORD; stdcall;
+function GetPrivateProfileSectionNames(lpszReturnBuffer: PKOLChar; nSize: DWORD; lpFileName: PKOLChar): DWORD; stdcall;
+function GetPrivateProfileString(lpAppName, lpKeyName, lpDefault: PKOLChar;
+ lpReturnedString: PKOLChar; nSize: DWORD; lpFileName: PKOLChar): DWORD; stdcall;
+function GetProfileInt(lpAppName, lpKeyName: PKOLChar; nDefault: Integer): UINT; stdcall;
+function GetProfileSection(lpAppName: PKOLChar; lpReturnedString: PKOLChar; nSize: DWORD): DWORD; stdcall;
+function GetProfileString(lpAppName, lpKeyName, lpDefault: PKOLChar;
+ lpReturnedString: PKOLChar; nSize: DWORD): DWORD; stdcall;
+function GetShortPathName(lpszLongPath: PKOLChar; lpszShortPath: PKOLChar;
+ cchBuffer: DWORD): DWORD; stdcall;
+procedure GetStartupInfo(var lpStartupInfo: TStartupInfo); stdcall;
+function GetStringTypeEx(Locale: LCID; dwInfoType: DWORD;
+ lpSrcStr: PKOLChar; cchSrc: Integer; var lpCharType): BOOL; stdcall;
+function GetSystemDirectory(lpBuffer: PKOLChar; uSize: UINT): UINT; stdcall;
+function GetTempFileName(lpPathName, lpPrefixString: PKOLChar;
+ uUnique: UINT; lpTempFileName: PKOLChar): UINT; stdcall;
+function GetTempPath(nBufferLength: DWORD; lpBuffer: PKOLChar): DWORD; stdcall;
+function GetTimeFormat(Locale: LCID; dwFlags: DWORD; lpTime: PSystemTime;
+ lpFormat: PKOLChar; lpTimeStr: PKOLChar; cchTime: Integer): Integer; stdcall;
+function GetVersionEx(var lpVersionInformation: TOSVersionInfo): BOOL; stdcall;
+function GetVolumeInformation(lpRootPathName: PKOLChar;
+ lpVolumeNameBuffer: PKOLChar; nVolumeNameSize: DWORD; lpVolumeSerialNumber: PDWORD;
+ var lpMaximumComponentLength, lpFileSystemFlags: DWORD;
+ lpFileSystemNameBuffer: PKOLChar; nFileSystemNameSize: DWORD): BOOL; stdcall;
+function GetWindowsDirectory(lpBuffer: PKOLChar; uSize: UINT): UINT; stdcall;
+function GlobalAddAtom(lpString: PKOLChar): ATOM; stdcall;
+function GlobalFindAtom(lpString: PKOLChar): ATOM; stdcall;
+function GlobalGetAtomName(nAtom: ATOM; lpBuffer: PKOLChar; nSize: Integer): UINT; stdcall;
+function IsBadStringPtr(lpsz: PKOLChar; ucchMax: UINT): BOOL; stdcall;
+function LCMapString(Locale: LCID; dwMapFlags: DWORD; lpSrcStr: PKOLChar;
+ cchSrc: Integer; lpDestStr: PKOLChar; cchDest: Integer): Integer; stdcall;
+function LoadLibrary(lpLibFileName: PKOLChar): HMODULE; stdcall;
+function LoadLibraryEx(lpLibFileName: PKOLChar; hFile: THandle; dwFlags: DWORD): HMODULE; stdcall;
+function MoveFile(lpExistingFileName, lpNewFileName: PKOLChar): BOOL; stdcall;
+function MoveFileEx(lpExistingFileName, lpNewFileName: PKOLChar; dwFlags: DWORD): BOOL; stdcall;
+function MoveFileWithProgress(lpExistingFileName, lpNewFileName: PKOLChar; lpProgressRoutine: TFNProgressRoutine;
+ lpData: Pointer; dwFlags: DWORD): BOOL; stdcall;
+function OpenEvent(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: PKOLChar): THandle; stdcall;
+function OpenFileMapping(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: PKOLChar): THandle; stdcall;
+function OpenMutex(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: PKOLChar): THandle; stdcall;
+function OpenSemaphore(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: PKOLChar): THandle; stdcall;
+function OpenWaitableTimer(dwDesiredAccess: DWORD; bInheritHandle: BOOL;
+ lpTimerName: PKOLChar): THandle; stdcall;
+procedure OutputDebugString(lpOutputString: PKOLChar); stdcall;
+function PeekConsoleInput(hConsoleInput: THandle; var lpBuffer: TInputRecord;
+ nLength: DWORD; var lpNumberOfEventsRead: DWORD): BOOL; stdcall;
+function QueryDosDevice(lpDeviceName: PKOLChar; lpTargetPath: PKOLChar; ucchMax: DWORD): DWORD; stdcall;
+{$IFDEF _D4orHigher}
+function QueryRecoveryAgents(p1: PKOLChar; var p2: Pointer; var p3: TRecoveryAgentInformation): DWORD; stdcall;
+{$ENDIF}
+function ReadConsole(hConsoleInput: THandle; lpBuffer: Pointer;
+ nNumberOfCharsToRead: DWORD; var lpNumberOfCharsRead: DWORD; lpReserved: Pointer): BOOL; stdcall;
+function ReadConsoleInput(hConsoleInput: THandle; var lpBuffer: TInputRecord;
+ nLength: DWORD; var lpNumberOfEventsRead: DWORD): BOOL; stdcall;
+function ReadConsoleOutput(hConsoleOutput: THandle; lpBuffer: Pointer;
+ dwBufferSize, dwBufferCoord: TCoord; var lpReadRegion: TSmallRect): BOOL; stdcall;
+function ReadConsoleOutputCharacter(hConsoleOutput: THandle; lpCharacter: PKOLChar;
+ nLength: DWORD; dwReadCoord: TCoord; var lpNumberOfCharsRead: DWORD): BOOL; stdcall;
+function RemoveDirectory(lpPathName: PKOLChar): BOOL; stdcall;
+function ScrollConsoleScreenBuffer(hConsoleOutput: THandle;
+ const lpScrollRectangle: TSmallRect; lpClipRectangle: PSmallRect;
+ dwDestinationOrigin: TCoord; var lpFill: TCharInfo): BOOL; stdcall;
+function SearchPath(lpPath, lpFileName, lpExtension: PKOLChar;
+ nBufferLength: DWORD; lpBuffer: PKOLChar; var lpFilePart: PKOLChar): DWORD; stdcall;
+function SetComputerName(lpComputerName: PKOLChar): BOOL; stdcall;
+function SetConsoleTitle(lpConsoleTitle: PKOLChar): BOOL; stdcall;
+function SetCurrentDirectory(lpPathName: PKOLChar): BOOL; stdcall;
+function SetDefaultCommConfig(lpszName: PKOLChar; lpCC: PCommConfig; dwSize: DWORD): BOOL; stdcall;
+function SetEnvironmentVariable(lpName, lpValue: PKOLChar): BOOL; stdcall;
+function SetFileAttributes(lpFileName: PKOLChar; dwFileAttributes: DWORD): BOOL; stdcall;
+function SetLocaleInfo(Locale: LCID; LCType: LCTYPE; lpLCData: PKOLChar): BOOL; stdcall;
+function SetVolumeLabel(lpRootPathName: PKOLChar; lpVolumeName: PKOLChar): BOOL; stdcall;
+function UpdateResource(hUpdate: THandle; lpType, lpName: PKOLChar;
+ wLanguage: Word; lpData: Pointer; cbData: DWORD): BOOL; stdcall;
+function VerLanguageName(wLang: DWORD; szLang: PKOLChar; nSize: DWORD): DWORD; stdcall;
+function WaitNamedPipe(lpNamedPipeName: PKOLChar; nTimeOut: DWORD): BOOL; stdcall;
+function WriteConsole(hConsoleOutput: THandle; const lpBuffer: Pointer;
+ nNumberOfCharsToWrite: DWORD; var lpNumberOfCharsWritten: DWORD; lpReserved: Pointer): BOOL; stdcall;
+function WriteConsoleInput(hConsoleInput: THandle; const lpBuffer: TInputRecord;
+ nLength: DWORD; var lpNumberOfEventsWritten: DWORD): BOOL; stdcall;
+function WriteConsoleOutput(hConsoleOutput: THandle; lpBuffer: Pointer;
+ dwBufferSize, dwBufferCoord: TCoord; var lpWriteRegion: TSmallRect): BOOL; stdcall;
+function WriteConsoleOutputCharacter(hConsoleOutput: THandle;lpCharacter: PKOLChar;
+ nLength: DWORD; dwWriteCoord: TCoord; var lpNumberOfCharsWritten: DWORD): BOOL; stdcall;
+function WritePrivateProfileSection(lpAppName, lpString, lpFileName: PKOLChar): BOOL; stdcall;
+function WritePrivateProfileString(lpAppName, lpKeyName, lpString, lpFileName: PKOLChar): BOOL; stdcall;
+function WriteProfileSection(lpAppName, lpString: PKOLChar): BOOL; stdcall;
+function WriteProfileString(lpAppName, lpKeyName, lpString: PKOLChar): BOOL; stdcall;
+function lstrcat(lpString1, lpString2: PKOLChar): PKOLChar; stdcall;
+function lstrcmp(lpString1, lpString2: PKOLChar): Integer; stdcall;
+function lstrcmpi(lpString1, lpString2: PKOLChar): Integer; stdcall;
+function lstrcpy(lpString1, lpString2: PKOLChar): PKOLChar; stdcall;
+function lstrcpyn(lpString1, lpString2: PKOLChar; iMaxLength: Integer): PKOLChar; stdcall;
+function lstrlen(lpString: PKOLChar): Integer; stdcall;
+function MultinetGetConnectionPerformance(lpNetResource: PNetResource;
+ lpNetConnectInfoStruc: PNetConnectInfoStruct): DWORD; stdcall;
+function WNetAddConnection2(var lpNetResource: TNetResource;
+ lpPassword, lpUserName: PKOLChar; dwFlags: DWORD): DWORD; stdcall;
+function WNetAddConnection3(hwndOwner: HWND; var lpNetResource: TNetResource;
+ lpPassword, lpUserName: PKOLChar; dwFlags: DWORD): DWORD; stdcall;
+function WNetAddConnection(lpRemoteName, lpPassword, lpLocalName: PKOLChar): DWORD; stdcall;
+function WNetCancelConnection2(lpName: PKOLChar; dwFlags: DWORD; fForce: BOOL): DWORD; stdcall;
+function WNetCancelConnection(lpName: PKOLChar; fForce: BOOL): DWORD; stdcall;
+function WNetConnectionDialog1(var lpConnDlgStruct: TConnectDlgStruct): DWORD; stdcall;
+function WNetDisconnectDialog1(var lpConnDlgStruct: TDiscDlgStruct): DWORD; stdcall;
+function WNetEnumResource(hEnum: THandle; var lpcCount: DWORD;
+ lpBuffer: Pointer; var lpBufferSize: DWORD): DWORD; stdcall;
+function WNetGetConnection(lpLocalName: PKOLChar;
+ lpRemoteName: PKOLChar; var lpnLength: DWORD): DWORD; stdcall;
+function WNetGetLastError(var lpError: DWORD; lpErrorBuf: PKOLChar;
+ nErrorBufSize: DWORD; lpNameBuf: PKOLChar; nNameBufSize: DWORD): DWORD; stdcall;
+function WNetGetNetworkInformation(lpProvider: PKOLChar;
+ var lpNetInfoStruct: TNetInfoStruct): DWORD; stdcall;
+function WNetGetProviderName(dwNetType: DWORD; lpProviderName: PKOLChar;
+ var lpBufferSize: DWORD): DWORD; stdcall;
+function WNetGetResourceParent(lpNetResource: PNetResource;
+ lpBuffer: Pointer; var cbBuffer: DWORD): DWORD; stdcall;
+function WNetGetUniversalName(lpLocalPath: PKOLChar; dwInfoLevel: DWORD;
+ lpBuffer: Pointer; var lpBufferSize: DWORD): DWORD; stdcall;
+function WNetGetUser(lpName: PKOLChar; lpUserName: PKOLChar; var lpnLength: DWORD): DWORD; stdcall;
+function WNetOpenEnum(dwScope, dwType, dwUsage: DWORD;
+ lpNetResource: PNetResource; var lphEnum: THandle): DWORD; stdcall;
+function WNetSetConnection(lpName: PKOLChar; dwProperties: DWORD; pvValues: Pointer): DWORD; stdcall;
+function WNetUseConnection(hwndOwner: HWND;
+ var lpNetResource: TNetResource; lpUserID: PKOLChar;
+ lpPassword: PKOLChar; dwFlags: DWORD; lpAccessName: PKOLChar;
+ var lpBufferSize: DWORD; var lpResult: DWORD): DWORD; stdcall;
+function GetFileVersionInfo(lptstrFilename: PKOLChar; dwHandle, dwLen: DWORD;
+ lpData: Pointer): BOOL; stdcall;
+function GetFileVersionInfoSize(lptstrFilename: PKOLChar; var lpdwHandle: DWORD): DWORD; stdcall;
+function VerFindFile(uFlags: DWORD; szFileName, szWinDir, szAppDir, szCurDir: PKOLChar;
+ var lpuCurDirLen: UINT; szDestDir: PKOLChar; var lpuDestDirLen: UINT): DWORD; stdcall;
+function VerInstallFile(uFlags: DWORD;
+ szSrcFileName, szDestFileName, szSrcDir, szDestDir, szCurDir, szTmpFile: PKOLChar;
+ var lpuTmpFileLen: UINT): DWORD; stdcall;
+function VerQueryValue(pBlock: Pointer; lpSubBlock: PKOLChar;
+ var lplpBuffer: Pointer; var puLen: UINT): BOOL; stdcall;
+function GetPrivateProfileStruct(lpszSection, lpszKey: PKOLChar;
+ lpStruct: Pointer; uSizeStruct: UINT; szFile: PKOLChar): BOOL; stdcall;
+function WritePrivateProfileStruct(lpszSection, lpszKey: PKOLChar;
+ lpStruct: Pointer; uSizeStruct: UINT; szFile: PKOLChar): BOOL; stdcall;
+function AddFontResource(FileName: PKOLChar): Integer; stdcall;
+{$IFDEF _D4orHigher}
+function AddFontResourceEx(p1: PKOLChar; p2: DWORD; p3: PDesignVector): Integer; stdcall;
+{$ENDIF}
+function CopyEnhMetaFile(p1: HENHMETAFILE; p2: PKOLChar): HENHMETAFILE; stdcall;
+function CopyMetaFile(p1: HMETAFILE; p2: PKOLChar): HMETAFILE; stdcall;
+function CreateColorSpace(var ColorSpace: TLogColorSpace): HCOLORSPACE; stdcall;
+function CreateDC(lpszDriver, lpszDevice, lpszOutput: PKOLChar;
+ lpdvmInit: PDeviceMode): HDC; stdcall;
+function CreateEnhMetaFile(DC: HDC; FileName: PKOLChar; Rect: PRect; Desc: PKOLChar): HDC; stdcall;
+function CreateFont(nHeight, nWidth, nEscapement, nOrientaion, fnWeight: Integer;
+ fdwItalic, fdwUnderline, fdwStrikeOut, fdwCharSet, fdwOutputPrecision,
+ fdwClipPrecision, fdwQuality, fdwPitchAndFamily: DWORD; lpszFace: PKOLChar): HFONT; stdcall;
+function CreateFontIndirect(const p1: TLogFont): HFONT; stdcall;
+{$IFDEF _D4orHigher}
+function CreateFontIndirectEx(const p1: PEnumLogFontExDV): HFONT; stdcall;
+{$ENDIF}
+function CreateIC(lpszDriver, lpszDevice, lpszOutput: PKOLChar; lpdvmInit: PDeviceMode): HDC; stdcall;
+function CreateMetaFile(p1: PKOLChar): HDC; stdcall;
+function CreateScalableFontResource(p1: DWORD; p2, p3, p4: PKOLChar): BOOL; stdcall;
+function DeviceCapabilities(pDriverName, pDeviceName, pPort: PKOLChar;
+ iIndex: Integer; pOutput: PKOLChar; DevMode: PDeviceMode): Integer; stdcall;
+function EnumFontFamilies(DC: HDC; p2: PKOLChar; p3: TFNFontEnumProc; p4: LPARAM): BOOL; stdcall;
+function EnumFontFamiliesEx(DC: HDC; var p2: TLogFont;
+ p3: TFNFontEnumProc; p4: LPARAM; p5: DWORD): BOOL; stdcall;
+function EnumFonts(DC: HDC; lpszFace: PKOLChar; fntenmprc: TFNFontEnumProc;
+ lpszData: PKOLChar): Integer; stdcall;
+function EnumICMProfiles(DC: HDC; ICMProc: TFNICMEnumProc; p3: LPARAM): Integer; stdcall;
+function ExtTextOut(DC: HDC; X, Y: Integer; Options: Longint;
+ Rect: PRect; Str: PKOLChar; Count: Longint; Dx: PInteger): BOOL; stdcall;
+function GetCharABCWidths(DC: HDC; FirstChar, LastChar: UINT; const ABCStructs): BOOL; stdcall;
+function GetCharABCWidthsFloat(DC: HDC; FirstChar, LastChar: UINT; const ABCFloatSturcts): BOOL; stdcall;
+function GetCharWidth32(DC: HDC; FirstChar, LastChar: UINT; const Widths): BOOL; stdcall;
+function GetCharWidth(DC: HDC; FirstChar, LastChar: UINT; const Widths): BOOL; stdcall;
+function GetCharWidthFloat(DC: HDC; FirstChar, LastChar: UINT; const Widths): BOOL; stdcall;
+function GetCharacterPlacement(DC: HDC; p2: PKOLChar; p3, p4: BOOL;
+ var p5: TGCPResults; p6: DWORD): DWORD; stdcall;
+function GetEnhMetaFile(p1: PKOLChar): HENHMETAFILE; stdcall;
+function GetEnhMetaFileDescription(p1: HENHMETAFILE; p2: UINT; p3: PKOLChar): UINT; stdcall;
+function GetGlyphIndices(DC: HDC; p2: PKOLChar; p3: Integer; p4: PWORD; p5: DWORD): DWORD; stdcall;
+function GetGlyphOutline(DC: HDC; uChar, uFormat: UINT;
+ const lpgm: TGlyphMetrics; cbBuffer: DWORD; lpvBuffer: Pointer; const lpmat2: TMat2): DWORD; stdcall;
+function GetICMProfile(DC: HDC; var Size: DWORD; Name: PKOLChar): BOOL; stdcall;
+function GetLogColorSpace(p1: HCOLORSPACE; var ColorSpace: TLogColorSpace; Size: DWORD): BOOL; stdcall;
+function GetMetaFile(p1: PKOLChar): HMETAFILE; stdcall;
+function GetObject(p1: HGDIOBJ; p2: Integer; p3: Pointer): Integer; stdcall;
+function GetOutlineTextMetrics(DC: HDC; p2: UINT; OTMetricStructs: Pointer): UINT; stdcall;
+function GetTextExtentExPoint(DC: HDC; p2: PKOLChar;
+ p3, p4: Integer; p5, p6: PInteger; var p7: TSize): BOOL; stdcall;
+function GetTextExtentPoint32(DC: HDC; Str: PKOLChar; Count: Integer;
+ var Size: TSize): BOOL; stdcall;
+function GetTextExtentPoint(DC: HDC; Str: PKOLChar; Count: Integer;
+ var Size: TSize): BOOL; stdcall;
+function GetTextFace(DC: HDC; Count: Integer; Buffer: PKOLChar): Integer; stdcall;
+function GetTextMetrics(DC: HDC; var TM: TTextMetric): BOOL; stdcall;
+function PolyTextOut(DC: HDC; const PolyTextArray; Strings: Integer): BOOL; stdcall;
+function RemoveFontResource(FileName: PKOLChar): BOOL; stdcall;
+{$IFDEF _D4orHigher}
+function RemoveFontResourceEx(p1: PKOLChar; p2: DWORD; p3: PDesignVector): BOOL; stdcall;
+{$ENDIF}
+function ResetDC(DC: HDC; const InitData: TDeviceMode): HDC; stdcall;
+function SetICMProfile(DC: HDC; Name: PKOLChar): BOOL; stdcall;
+function StartDoc(DC: HDC; const p2: TDocInfo): Integer; stdcall;
+function TextOut(DC: HDC; X, Y: Integer; Str: PKOLChar; Count: Integer): BOOL; stdcall;
+function UpdateICMRegKey(p1: DWORD; p2, p3: PKOLChar; p4: UINT): BOOL; stdcall;
+function wglUseFontBitmaps(DC: HDC; p2, p3, p4: DWORD): BOOL; stdcall;
+function wglUseFontOutlines(p1: HDC; p2, p3, p4: DWORD;
+ p5, p6: Single; p7: Integer; p8: PGlyphMetricsFloat): BOOL; stdcall;
+function AnsiToOem(const lpszSrc: LPCSTR; lpszDst: LPSTR): BOOL; stdcall;
+function AnsiToOemBuff(lpszSrc: LPCSTR; lpszDst: LPSTR; cchDstLength: DWORD): BOOL; stdcall;
+function AnsiUpper(lpsz: LPSTR): LPSTR; stdcall;
+function AnsiUpperBuff(lpsz: LPSTR; cchLength: DWORD): DWORD; stdcall;
+function AnsiLower(lpsz: LPSTR): LPSTR; stdcall;
+function AnsiLowerBuff(lpsz: LPSTR; cchLength: DWORD): DWORD; stdcall;
+function AnsiNext(const lpsz: LPCSTR): LPSTR; stdcall;
+function AnsiPrev(const lpszStart: LPCSTR; const lpszCurrent: LPCSTR): LPSTR; stdcall;
+function AppendMenu(hMenu: HMENU; uFlags, uIDNewItem: UINT;
+ lpNewItem: PKOLChar): BOOL; stdcall;
+//function BroadcastSystemMessage(Flags: DWORD; Recipients: PDWORD;
+// uiMessage: UINT; wParam: WPARAM; lParam: LPARAM): Longint; stdcall;
+//function BroadcastSystemMessageW(Flags: DWORD; Recipients: PDWORD;
+// uiMessage: UINT; wParam: WPARAM; lParam: LPARAM): Longint; stdcall;
+function CallMsgFilter(var lpMsg: TMsg; nCode: Integer): BOOL; stdcall;
+function CallWindowProc(lpPrevWndFunc: TFNWndProc; hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
+function ChangeDisplaySettings(var lpDevMode: TDeviceMode; dwFlags: DWORD): Longint; stdcall;
+function ChangeDisplaySettingsEx(lpszDeviceName: PKOLChar; var lpDevMode: TDeviceMode;
+ wnd: HWND; dwFlags: DWORD; lParam: Pointer): Longint; stdcall;
+function ChangeMenu(hMenu: HMENU; cmd: UINT; lpszNewItem: PKOLChar;
+ cmdInsert: UINT; flags: UINT): BOOL; stdcall;
+function CharLower(lpsz: PKOLChar): PKOLChar; stdcall;
+function CharLowerBuff(lpsz: PKOLChar; cchLength: DWORD): DWORD; stdcall;
+function CharNext(lpsz: PKOLChar): PKOLChar; stdcall;
+function CharNextEx(CodePage: Word; lpCurrentChar: LPCSTR; dwFlags: DWORD): LPSTR; stdcall;
+function CharPrev(lpszStart: PKOLChar; lpszCurrent: PKOLChar): PKOLChar; stdcall;
+function CharPrevEx(CodePage: Word; lpStart, lpCurrentChar: LPCSTR; dwFlags: DWORD): LPSTR; stdcall;
+function CharToOem(lpszSrc: PKOLChar; lpszDst: PKOLChar): BOOL; stdcall;
+function CharToOemBuff(lpszSrc: PKOLChar; lpszDst: PKOLChar; cchDstLength: DWORD): BOOL; stdcall;
+function CharUpper(lpsz: PKOLChar): PKOLChar; stdcall;
+function CharUpperBuff(lpsz: PKOLChar; cchLength: DWORD): DWORD; stdcall;
+function CopyAcceleratorTable(hAccelSrc: HACCEL; var lpAccelDst; cAccelEntries: Integer): Integer; stdcall;
+function CreateAcceleratorTable(var Accel; Count: Integer): HACCEL; stdcall;
+function CreateDesktop(lpszDesktop, lpszDevice: PKOLChar;
+ pDevmode: PDeviceMode; dwFlags: DWORD; dwDesiredAccess:
+ DWORD; lpsa: PSecurityAttributes): HDESK; stdcall;
+function CreateDialogIndirectParam(hInstance: HINST; const lpTemplate: TDlgTemplate;
+ hWndParent: HWND; lpDialogFunc: TFNDlgProc; dwInitParam: LPARAM): HWND; stdcall;
+function CreateDialogParam(hInstance: HINST; lpTemplateName: PKOLChar;
+ hWndParent: HWND; lpDialogFunc: TFNDlgProc; dwInitParam: LPARAM): HWND; stdcall;
+function CreateMDIWindow(lpClassName, lpWindowName: PKOLChar;
+ dwStyle: DWORD; X, Y, nWidth, nHeight: Integer;
+ hWndParent: HWND; hInstance: HINST; lParam: LPARAM): HWND; stdcall;
+function CreateWindowEx(dwExStyle: DWORD; lpClassName: PKOLChar;
+ lpWindowName: PKOLChar; dwStyle: DWORD; X, Y, nWidth, nHeight: Integer;
+ hWndParent: HWND; hMenu: HMENU; hInstance: HINST; lpParam: Pointer): HWND; stdcall;
+function CreateWindowStation(lpwinsta: PKOLChar; dwReserved, dwDesiredAccess: DWORD;
+ lpsa: PSecurityAttributes): HWINSTA; stdcall;
+function DefDlgProc(hDlg: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
+function DefFrameProc(hWnd, hWndMDIClient: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
+function DefMDIChildProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
+function DefWindowProc(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
+function DialogBoxIndirectParam(hInstance: HINST; const lpDialogTemplate: TDlgTemplate;
+ hWndParent: HWND; lpDialogFunc: TFNDlgProc; dwInitParam: LPARAM): Integer; stdcall;
+function DialogBoxParam(hInstance: HINST; lpTemplateName: PKOLChar;
+ hWndParent: HWND; lpDialogFunc: TFNDlgProc; dwInitParam: LPARAM): Integer; stdcall;
+function DispatchMessage(const lpMsg: TMsg): Longint; stdcall;
+function DlgDirList(hDlg: HWND; lpPathSpec: PKOLChar;
+ nIDListBox, nIDStaticPath: Integer; uFileType: UINT): Integer; stdcall;
+function DlgDirListComboBox(hDlg: HWND; lpPathSpec: PKOLChar;
+ nIDComboBox, nIDStaticPath: Integer; uFiletype: UINT): Integer; stdcall;
+function DlgDirSelectComboBoxEx(hDlg: HWND; lpString: PKOLChar;
+ nCount, nIDComboBox: Integer): BOOL; stdcall;
+function DlgDirSelectEx(hDlg: HWND; lpString: PKOLChar; nCount, nIDListBox: Integer): BOOL; stdcall;
+function DrawState(DC: HDC; Brush: HBRUSH; CBFunc: TFNDrawStateProc;
+ lData: LPARAM; wData: WPARAM; x, y, cx, cy: Integer; Flags: UINT): BOOL; stdcall;
+function DrawText(hDC: HDC; lpString: PKOLChar; nCount: Integer;
+ var lpRect: TRect; uFormat: UINT): Integer; stdcall;
+function DrawTextEx(DC: HDC; lpchText: PKOLChar; cchText: Integer; var p4: TRect;
+ dwDTFormat: UINT; DTParams: PDrawTextParams): Integer; stdcall;
+function EnumDesktops(hwinsta: HWINSTA; lpEnumFunc: TFNDeskTopEnumProc; lParam: LPARAM): BOOL; stdcall;
+function EnumDisplaySettings(lpszDeviceName: PKOLChar; iModeNum: DWORD;
+ var lpDevMode: TDeviceMode): BOOL; stdcall;
+{$IFDEF _D4orHigher}
+function EnumDisplayDevices(Unused: Pointer; iDevNum: DWORD;
+ var lpDisplayDevice: TDisplayDevice; dwFlags: DWORD): BOOL; stdcall;
+{$ENDIF}
+function EnumProps(hWnd: HWND; lpEnumFunc: TFNPropEnumProc): Integer; stdcall;
+function EnumPropsEx(hWnd: HWND; lpEnumFunc: TFNPropEnumProcEx; lParam: LPARAM): Integer; stdcall;
+function EnumWindowStations(lpEnumFunc: TFNWinStaEnumProc; lParam: LPARAM): BOOL; stdcall;
+function FindWindow(lpClassName, lpWindowName: PKOLChar): HWND; stdcall;
+function FindWindowEx(Parent, Child: HWND; ClassName, WindowName: PKOLChar): HWND; stdcall;
+{$IFDEF _D4orHigher}
+function GetAltTabInfo(hwnd: HWND; iItem: Integer; var pati: TAltTabInfo;
+ pszItemText: PKOLChar; cchItemText: UINT): BOOL; stdcall;
+{$ENDIF}
+function GetClassInfo(hInstance: HINST; lpClassName: PKOLChar;
+ var lpWndClass: TWndClass): BOOL; stdcall;
+function GetClassInfoEx(Instance: HINST; Classname: PKOLChar; var WndClass: TWndClassEx): BOOL; stdcall;
+function GetClassLong(hWnd: HWND; nIndex: Integer): DWORD; stdcall;
+function GetClassName(hWnd: HWND; lpClassName: PKOLChar; nMaxCount: Integer): Integer; stdcall;
+function GetClipboardFormatName(format: UINT; lpszFormatName: PKOLChar;
+ cchMaxCount: Integer): Integer; stdcall;
+function GetDlgItemText(hDlg: HWND; nIDDlgItem: Integer;
+ lpString: PKOLChar; nMaxCount: Integer): UINT; stdcall;
+function GetKeyNameText(lParam: Longint; lpString: PKOLChar; nSize: Integer): Integer; stdcall;
+function GetKeyboardLayoutName(pwszKLID: PKOLChar): BOOL; stdcall;
+function GetMenuItemInfo(p1: HMENU; p2: UINT; p3: BOOL; var p4: TMenuItemInfo): BOOL; stdcall;
+function GetMenuString(hMenu: HMENU; uIDItem: UINT; lpString: PKOLChar;
+ nMaxCount: Integer; uFlag: UINT): Integer; stdcall;
+function GetMessage(var lpMsg: TMsg; hWnd: HWND;
+ wMsgFilterMin, wMsgFilterMax: UINT): BOOL; stdcall;
+function GetProp(hWnd: HWND; lpString: PKOLChar): THandle; stdcall;
+function GetTabbedTextExtent(hDC: HDC; lpString: PKOLChar;
+ nCount, nTabPositions: Integer; var lpnTabStopPositions): DWORD; stdcall;
+function GetUserObjectInformation(hObj: THandle; nIndex: Integer; pvInfo: Pointer;
+ nLength: DWORD; var lpnLengthNeeded: DWORD): BOOL; stdcall;
+function GetWindowLong(hWnd: HWND; nIndex: Integer): Longint; stdcall;
+function GetWindowModuleFileName(hwnd: HWND; pszFileName: PKOLChar; cchFileNameMax: UINT): UINT; stdcall;
+function GetWindowText(hWnd: HWND; lpString: PKOLChar; nMaxCount: Integer): Integer; stdcall;
+function GetWindowTextLength(hWnd: HWND): Integer; stdcall;
+function GrayString(hDC: HDC; hBrush: HBRUSH; lpOutputFunc: TFNGrayStringProc;
+ lpData: LPARAM; nCount, X, Y, nWidth, nHeight: Integer): BOOL; stdcall;
+function InsertMenu(hMenu: HMENU; uPosition, uFlags, uIDNewItem: UINT;
+ lpNewItem: PKOLChar): BOOL; stdcall;
+function InsertMenuItem(p1: HMENU; p2: UINT; p3: BOOL; const p4: TMenuItemInfo): BOOL; stdcall;
+function IsCharAlpha(ch: KOLChar): BOOL; stdcall;
+function IsCharAlphaNumeric(ch: KOLChar): BOOL; stdcall;
+function IsCharLower(ch: KOLChar): BOOL; stdcall;
+function IsCharUpper(ch: KOLChar): BOOL; stdcall;
+function IsDialogMessage(hDlg: HWND; var lpMsg: TMsg): BOOL; stdcall;
+function LoadAccelerators(hInstance: HINST; lpTableName: PKOLChar): HACCEL; stdcall;
+function LoadBitmap(hInstance: HINST; lpBitmapName: PKOLChar): HBITMAP; stdcall;
+function LoadCursor(hInstance: HINST; lpCursorName: PKOLChar): HCURSOR; stdcall;
+function LoadCursorFromFile(lpFileName: PKOLChar): HCURSOR; stdcall;
+function LoadIcon(hInstance: HINST; lpIconName: PKOLChar): HICON; stdcall;
+function LoadImage(hInst: HINST; ImageName: PKOLChar; ImageType: UINT; X, Y: Integer; Flags: UINT): THandle; stdcall;
+function LoadKeyboardLayout(pwszKLID: PKOLChar; Flags: UINT): HKL; stdcall;
+function LoadMenu(hInstance: HINST; lpMenuName: PKOLChar): HMENU; stdcall;
+function LoadMenuIndirect(lpMenuTemplate: Pointer): HMENU; stdcall;
+function LoadString(hInstance: HINST; uID: UINT; lpBuffer: PKOLChar; nBufferMax: Integer): Integer; stdcall;
+function MapVirtualKey(uCode, uMapType: UINT): UINT; stdcall;
+function MapVirtualKeyEx(uCode, uMapType: UINT; dwhkl: HKL): UINT; stdcall;
+function MessageBox(hWnd: HWND; lpText, lpCaption: PKOLChar; uType: UINT): Integer; stdcall;
+function MessageBoxEx(hWnd: HWND; lpText, lpCaption: PKOLChar;
+ uType: UINT; wLanguageId: Word): Integer; stdcall;
+function MessageBoxIndirect(const MsgBoxParams: TMsgBoxParams): BOOL; stdcall;
+function ModifyMenu(hMnu: HMENU; uPosition, uFlags, uIDNewItem: UINT;
+ lpNewItem: PKOLChar): BOOL; stdcall;
+function OemToAnsi(const lpszSrc: LPCSTR; lpszDst: LPSTR): BOOL; stdcall;
+function OemToAnsiBuff(lpszSrc: LPCSTR; lpszDst: LPSTR; cchDstLength: DWORD): BOOL; stdcall;
+function OemToChar(lpszSrc: PKOLChar; lpszDst: PKOLChar): BOOL; stdcall;
+function OemToCharBuff(lpszSrc: PKOLChar; lpszDst: PKOLChar; cchDstLength: DWORD): BOOL; stdcall;
+function OpenDesktop(lpszDesktop: PKOLChar; dwFlags: DWORD; fInherit: BOOL;
+ dwDesiredAccess: DWORD): HDESK; stdcall;
+function OpenWindowStation(lpszWinSta: PKOLChar; fInherit: BOOL;
+ dwDesiredAccess: DWORD): HWINSTA; stdcall;
+function PeekMessage(var lpMsg: TMsg; hWnd: HWND;
+ wMsgFilterMin, wMsgFilterMax, wRemoveMsg: UINT): BOOL; stdcall;
+function PostMessage(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): BOOL; stdcall;
+function PostThreadMessage(idThread: DWORD; Msg: UINT; wParam: WPARAM; lParam: LPARAM): BOOL; stdcall;
+function RealGetWindowClass(hwnd: HWND; pszType: PKOLChar; cchType: UINT): UINT; stdcall;
+function RegisterClass(const lpWndClass: TWndClass): ATOM; stdcall;
+function RegisterClassEx(const WndClass: TWndClassEx): ATOM; stdcall;
+function RegisterClipboardFormat(lpszFormat: PKOLChar): UINT; stdcall;
+{$IFDEF _D4orHigher}
+function RegisterDeviceNotification(hRecipient: THandle; NotificationFilter: Pointer; Flags: DWORD): HDEVNOTIFY; stdcall;
+{$ENDIF}
+function RegisterWindowMessage(lpString: PKOLChar): UINT; stdcall;
+function RemoveProp(hWnd: HWND; lpString: PKOLChar): THandle; stdcall;
+function SendDlgItemMessage(hDlg: HWND; nIDDlgItem: Integer;
+ Msg: UINT; wParam: WPARAM; lParam: LPARAM): Longint; stdcall;
+function SendMessage(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
+function SendMessageCallback(hWnd: HWND; Msg: UINT; wParam: WPARAM;
+ lParam: LPARAM; lpResultCallBack: TFNSendAsyncProc; dwData: DWORD): BOOL; stdcall;
+function SendMessageTimeout(hWnd: HWND; Msg: UINT; wParam: WPARAM;
+ lParam: LPARAM; fuFlags, uTimeout: UINT; var lpdwResult: DWORD): LRESULT; stdcall;
+function SendNotifyMessage(hWnd: HWND; Msg: UINT; wParam: WPARAM;
+ lParam: LPARAM): BOOL; stdcall;
+function SetClassLong(hWnd: HWND; nIndex: Integer; dwNewLong: Longint): DWORD; stdcall;
+function SetDlgItemText(hDlg: HWND; nIDDlgItem: Integer; lpString: PKOLChar): BOOL; stdcall;
+function SetMenuItemInfo(p1: HMENU; p2: UINT; p3: BOOL; const p4: TMenuItemInfo): BOOL; stdcall;
+function SetProp(hWnd: HWND; lpString: PKOLChar; hData: THandle): BOOL; stdcall;
+function SetUserObjectInformation(hObj: THandle; nIndex: Integer;
+ pvInfo: Pointer; nLength: DWORD): BOOL; stdcall;
+function SetWindowLong(hWnd: HWND; nIndex: Integer; dwNewLong: Longint): Longint; stdcall;
+function SetWindowText(hWnd: HWND; lpString: PKOLChar): BOOL; stdcall;
+function SetWindowsHook(nFilterType: Integer; pfnFilterProc: TFNHookProc): HHOOK; stdcall;
+function SetWindowsHookEx(idHook: Integer; lpfn: TFNHookProc; hmod: HINST; dwThreadId: DWORD): HHOOK; stdcall;
+function SystemParametersInfo(uiAction, uiParam: UINT;
+ pvParam: Pointer; fWinIni: UINT): BOOL; stdcall;
+function TabbedTextOut(hDC: HDC; X, Y: Integer; lpString: PKOLChar; nCount, nTabPositions: Integer;
+ var lpnTabStopPositions; nTabOrigin: Integer): Longint; stdcall;
+function TranslateAccelerator(hWnd: HWND; hAccTable: HACCEL; var lpMsg: TMsg): Integer; stdcall;
+function UnregisterClass(lpClassName: PKOLChar; hInstance: HINST): BOOL; stdcall;
+function VkKeyScan(ch: KOLChar): SHORT; stdcall;
+function VkKeyScanEx(ch: KOLChar; dwhkl: HKL): SHORT; stdcall;
+function WinHelp(hWndMain: HWND; lpszHelp: PKOLChar; uCommand: UINT; dwData: DWORD): BOOL; stdcall;
+function wsprintf(Output: PKOLChar; Format: PKOLChar): Integer; stdcall;
+function wvsprintf(Output: PKOLChar; Format: PKOLChar; arglist: va_list): Integer; stdcall;
+function CreateMutex(lpMutexAttributes: PSecurityAttributes; bInitialOwner: BOOL; lpName: PWideChar): THandle;
+
+const
+ IDC_ARROW = MakeIntResource(32512);
+ IDC_IBEAM = MakeIntResource(32513);
+ IDC_WAIT = MakeIntResource(32514);
+ IDC_CROSS = MakeIntResource(32515);
+ IDC_UPARROW = MakeIntResource(32516);
+ IDC_SIZE = MakeIntResource(32640);
+ IDC_ICON = MakeIntResource(32641);
+ IDC_SIZENWSE = MakeIntResource(32642);
+ IDC_SIZENESW = MakeIntResource(32643);
+ IDC_SIZEWE = MakeIntResource(32644);
+ IDC_SIZENS = MakeIntResource(32645);
+ IDC_SIZEALL = MakeIntResource(32646);
+ IDC_NO = MakeIntResource(32648);
+ IDC_HAND = MakeIntResource(32649);
+ IDC_APPSTARTING = MakeIntResource(32650);
+ IDC_HELP = MakeIntResource(32651);
+ RT_CURSOR = PKOLChar(1);
+ RT_BITMAP = PKOLChar(2);
+ RT_ICON = PKOLChar(3);
+ RT_MENU = PKOLChar(4);
+ RT_DIALOG = PKOLChar(5);
+ RT_STRING = PKOLChar(6);
+ RT_FONTDIR = PKOLChar(7);
+ RT_FONT = PKOLChar(8);
+ RT_ACCELERATOR = PKOLChar(9);
+ RT_RCDATA = PKOLChar(10);
+ RT_MESSAGETABLE = PKOLChar(11);
+ RT_VERSION = PKOLChar(16);
+ RT_DLGINCLUDE = PKOLChar(17);
+ RT_PLUGPLAY = PKOLChar(19);
+ RT_VXD = PKOLChar(20);
+ RT_ANICURSOR = PKOLChar(21);
+ RT_ANIICON = PKOLChar(22);
+
+{$ENDIF interface_part} ////////////////////////////////////////////////////////
+
+{$IFDEF implementation_part} ///////////////////////////////////////////////////
+function AbortSystemShutdown; external advapi32 name 'AbortSystemShutdownW';
+function AccessCheckAndAuditAlarm; external advapi32 name 'AccessCheckAndAuditAlarmW';
+{$IFDEF _D4orHigher}
+function AccessCheckByTypeAndAuditAlarm; external advapi32 name 'AccessCheckByTypeAndAuditAlarmW';
+function AccessCheckByTypeResultListAndAuditAlarm; external advapi32 name 'AccessCheckByTypeResultListAndAuditAlarmW';
+{$ENDIF}
+function BackupEventLog; external advapi32 name 'BackupEventLogW';
+function ClearEventLog; external advapi32 name 'ClearEventLogW';
+function CreateProcessAsUser; external advapi32 name 'CreateProcessAsUserW';
+function GetCurrentHwProfile; external advapi32 name 'GetCurrentHwProfileW';
+function GetFileSecurity; external advapi32 name 'GetFileSecurityW';
+function GetUserName; external advapi32 name 'GetUserNameW';
+function InitiateSystemShutdown; external advapi32 name 'InitiateSystemShutdownW';
+function LogonUser; external advapi32 name 'LogonUserW';
+function LookupAccountName; external advapi32 name 'LookupAccountNameW';
+function LookupAccountSid; external advapi32 name 'LookupAccountSidW';
+function LookupPrivilegeDisplayName; external advapi32 name 'LookupPrivilegeDisplayNameW';
+function LookupPrivilegeName; external advapi32 name 'LookupPrivilegeNameW';
+function LookupPrivilegeValue; external advapi32 name 'LookupPrivilegeValueW';
+function ObjectCloseAuditAlarm; external advapi32 name 'ObjectCloseAuditAlarmW';
+function ObjectDeleteAuditAlarm; external advapi32 name 'ObjectDeleteAuditAlarmW';
+function ObjectOpenAuditAlarm; external advapi32 name 'ObjectOpenAuditAlarmW';
+function ObjectPrivilegeAuditAlarm; external advapi32 name 'ObjectPrivilegeAuditAlarmW';
+function OpenBackupEventLog; external advapi32 name 'OpenBackupEventLogW';
+function OpenEventLog; external advapi32 name 'OpenEventLogW';
+function PrivilegedServiceAuditAlarm; external advapi32 name 'PrivilegedServiceAuditAlarmW';
+function ReadEventLog; external advapi32 name 'ReadEventLogW';
+function RegConnectRegistry; external advapi32 name 'RegConnectRegistryW';
+function RegCreateKey; external advapi32 name 'RegCreateKeyW';
+function RegCreateKeyEx; external advapi32 name 'RegCreateKeyExW';
+function RegDeleteKey; external advapi32 name 'RegDeleteKeyW';
+function RegDeleteValue; external advapi32 name 'RegDeleteValueW';
+function RegEnumKeyEx; external advapi32 name 'RegEnumKeyExW';
+function RegEnumKey; external advapi32 name 'RegEnumKeyW';
+function RegEnumValue; external advapi32 name 'RegEnumValueW';
+function RegLoadKey; external advapi32 name 'RegLoadKeyW';
+function RegOpenKey; external advapi32 name 'RegOpenKeyW';
+function RegOpenKeyEx; external advapi32 name 'RegOpenKeyExW';
+function RegQueryInfoKey; external advapi32 name 'RegQueryInfoKeyW';
+function RegQueryMultipleValues; external advapi32 name 'RegQueryMultipleValuesW';
+function RegQueryValue; external advapi32 name 'RegQueryValueW';
+function RegQueryValueEx; external advapi32 name 'RegQueryValueExW';
+function RegReplaceKey; external advapi32 name 'RegReplaceKeyW';
+function RegRestoreKey; external advapi32 name 'RegRestoreKeyW';
+function RegSaveKey; external advapi32 name 'RegSaveKeyW';
+function RegSetValue; external advapi32 name 'RegSetValueW';
+function RegSetValueEx; external advapi32 name 'RegSetValueExW';
+function RegUnLoadKey; external advapi32 name 'RegUnLoadKeyW';
+function RegisterEventSource; external advapi32 name 'RegisterEventSourceW';
+function ReportEvent; external advapi32 name 'ReportEventW';
+function SetFileSecurity; external advapi32 name 'SetFileSecurityW';
+function AddAtom; external kernel32 name 'AddAtomW';
+function BeginUpdateResource; external kernel32 name 'BeginUpdateResourceW';
+function BuildCommDCB; external kernel32 name 'BuildCommDCBW';
+function BuildCommDCBAndTimeouts; external kernel32 name 'BuildCommDCBAndTimeoutsW';
+function CallNamedPipe; external kernel32 name 'CallNamedPipeW';
+function CommConfigDialog; external kernel32 name 'CommConfigDialogW';
+function CompareString; external kernel32 name 'CompareStringW';
+function CopyFile; external kernel32 name 'CopyFileW';
+function CopyFileEx; external kernel32 name 'CopyFileExW';
+function CreateDirectory; external kernel32 name 'CreateDirectoryW';
+function CreateDirectoryEx; external kernel32 name 'CreateDirectoryExW';
+function CreateEvent; external kernel32 name 'CreateEventW';
+function CreateFile; external kernel32 name 'CreateFileW';
+function CreateFileMapping; external kernel32 name 'CreateFileMappingW';
+function CreateHardLink; external kernel32 name 'CreateHardLinkW';
+function CreateMailslot; external kernel32 name 'CreateMailslotW';
+function CreateNamedPipe; external kernel32 name 'CreateNamedPipeW';
+function CreateProcess; external kernel32 name 'CreateProcessW';
+function CreateSemaphore; external kernel32 name 'CreateSemaphoreW';
+function CreateWaitableTimer; external kernel32 name 'CreateWaitableTimerW';
+function DefineDosDevice; external kernel32 name 'DefineDosDeviceW';
+function DeleteFile; external kernel32 name 'DeleteFileW';
+function EndUpdateResource; external kernel32 name 'EndUpdateResourceW';
+function EnumCalendarInfo; external kernel32 name 'EnumCalendarInfoW';
+function EnumDateFormats; external kernel32 name 'EnumDateFormatsW';
+function EnumResourceLanguages; external kernel32 name 'EnumResourceLanguagesW';
+function EnumResourceNames; external kernel32 name 'EnumResourceNamesW';
+function EnumResourceTypes; external kernel32 name 'EnumResourceTypesW';
+function EnumSystemCodePages; external kernel32 name 'EnumSystemCodePagesW';
+function EnumSystemLocales; external kernel32 name 'EnumSystemLocalesW';
+function EnumTimeFormats; external kernel32 name 'EnumTimeFormatsW';
+function ExpandEnvironmentStrings; external kernel32 name 'ExpandEnvironmentStringsW';
+procedure FatalAppExit; external kernel32 name 'FatalAppExitW';
+function FillConsoleOutputCharacter; external kernel32 name 'FillConsoleOutputCharacterW';
+function FindAtom; external kernel32 name 'FindAtomW';
+function FindFirstChangeNotification; external kernel32 name 'FindFirstChangeNotificationW';
+function FindFirstFile; external kernel32 name 'FindFirstFileW';
+function FindFirstFileEx; external kernel32 name 'FindFirstFileExW';
+function FindNextFile; external kernel32 name 'FindNextFileW';
+function FindResource; external kernel32 name 'FindResourceW';
+function FindResourceEx; external kernel32 name 'FindResourceExW';
+function FoldString; external kernel32 name 'FoldStringW';
+function FormatMessage; external kernel32 name 'FormatMessageW';
+function FreeEnvironmentStrings; external kernel32 name 'FreeEnvironmentStringsW';
+function GetAtomName; external kernel32 name 'GetAtomNameW';
+function GetBinaryType; external kernel32 name 'GetBinaryTypeW';
+function GetCommandLine; external kernel32 name 'GetCommandLineW';
+function GetCompressedFileSize; external kernel32 name 'GetCompressedFileSizeW';
+function GetComputerName; external kernel32 name 'GetComputerNameW';
+function GetConsoleTitle; external kernel32 name 'GetConsoleTitleW';
+function GetCurrencyFormat; external kernel32 name 'GetCurrencyFormatW';
+function GetCurrentDirectory; external kernel32 name 'GetCurrentDirectoryW';
+function GetDateFormat; external kernel32 name 'GetDateFormatW';
+function GetDefaultCommConfig; external kernel32 name 'GetDefaultCommConfigW';
+function GetDiskFreeSpace; external kernel32 name 'GetDiskFreeSpaceW';
+function GetDiskFreeSpaceEx; external kernel32 name 'GetDiskFreeSpaceExW';
+function GetDriveType; external kernel32 name 'GetDriveTypeW';
+function GetEnvironmentStrings; external kernel32 name 'GetEnvironmentStringsW';
+function GetEnvironmentVariable(lpName: PKOLChar; lpBuffer: PKOLChar;
+ nSize: DWORD): DWORD; external kernel32 name 'GetEnvironmentVariableW';
+function GetFileAttributes; external kernel32 name 'GetFileAttributesW';
+function GetFileAttributesEx; external kernel32 name 'GetFileAttributesExW';
+function GetFullPathName; external kernel32 name 'GetFullPathNameW';
+function GetLocaleInfo; external kernel32 name 'GetLocaleInfoW';
+function GetLogicalDriveStrings; external kernel32 name 'GetLogicalDriveStringsW';
+function GetModuleFileName; external kernel32 name 'GetModuleFileNameW';
+function GetModuleHandle; external kernel32 name 'GetModuleHandleW';
+function GetNamedPipeHandleState; external kernel32 name 'GetNamedPipeHandleStateW';
+function GetNumberFormat; external kernel32 name 'GetNumberFormatW';
+function GetPrivateProfileInt; external kernel32 name 'GetPrivateProfileIntW';
+function GetPrivateProfileSection; external kernel32 name 'GetPrivateProfileSectionW';
+function GetPrivateProfileSectionNames; external kernel32 name 'GetPrivateProfileSectionNamesW';
+function GetPrivateProfileString; external kernel32 name 'GetPrivateProfileStringW';
+function GetProfileInt; external kernel32 name 'GetProfileIntW';
+function GetProfileSection; external kernel32 name 'GetProfileSectionW';
+function GetProfileString; external kernel32 name 'GetProfileStringW';
+function GetShortPathName; external kernel32 name 'GetShortPathNameW';
+procedure GetStartupInfo; external kernel32 name 'GetStartupInfoW';
+function GetStringTypeEx; external kernel32 name 'GetStringTypeExW';
+function GetSystemDirectory; external kernel32 name 'GetSystemDirectoryW';
+function GetTempFileName; external kernel32 name 'GetTempFileNameW';
+function GetTempPath; external kernel32 name 'GetTempPathW';
+function GetTimeFormat; external kernel32 name 'GetTimeFormatW';
+function GetVersionEx; external kernel32 name 'GetVersionExW';
+function GetVolumeInformation; external kernel32 name 'GetVolumeInformationW';
+function GetWindowsDirectory; external kernel32 name 'GetWindowsDirectoryW';
+function GlobalAddAtom; external kernel32 name 'GlobalAddAtomW';
+function GlobalFindAtom; external kernel32 name 'GlobalFindAtomW';
+function GlobalGetAtomName; external kernel32 name 'GlobalGetAtomNameW';
+function IsBadStringPtr; external kernel32 name 'IsBadStringPtrW';
+function LCMapString; external kernel32 name 'LCMapStringW';
+function LoadLibrary; external kernel32 name 'LoadLibraryW';
+function LoadLibraryEx; external kernel32 name 'LoadLibraryExW';
+function MoveFile; external kernel32 name 'MoveFileW';
+function MoveFileEx; external kernel32 name 'MoveFileExW';
+function MoveFileWithProgress; external kernel32 name 'MoveFileWithProgressW';
+function OpenEvent; external kernel32 name 'OpenEventW';
+function OpenFileMapping; external kernel32 name 'OpenFileMappingW';
+function OpenMutex; external kernel32 name 'OpenMutexW';
+function OpenSemaphore; external kernel32 name 'OpenSemaphoreW';
+function OpenWaitableTimer; external kernel32 name 'OpenWaitableTimerW';
+procedure OutputDebugString; external kernel32 name 'OutputDebugStringW';
+function PeekConsoleInput; external kernel32 name 'PeekConsoleInputW';
+function QueryDosDevice; external kernel32 name 'QueryDosDeviceW';
+{$IFDEF _D4orHigher}
+function QueryRecoveryAgents; external kernel32 name 'QueryRecoveryAgentsW';
+{$ENDIF}
+function ReadConsole; external kernel32 name 'ReadConsoleW';
+function ReadConsoleInput; external kernel32 name 'ReadConsoleInputW';
+function ReadConsoleOutput; external kernel32 name 'ReadConsoleOutputW';
+function ReadConsoleOutputCharacter; external kernel32 name 'ReadConsoleOutputCharacterW';
+function RemoveDirectory; external kernel32 name 'RemoveDirectoryW';
+function ScrollConsoleScreenBuffer; external kernel32 name 'ScrollConsoleScreenBufferW';
+function SearchPath; external kernel32 name 'SearchPathW';
+function SetComputerName; external kernel32 name 'SetComputerNameW';
+function SetConsoleTitle; external kernel32 name 'SetConsoleTitleW';
+function SetCurrentDirectory; external kernel32 name 'SetCurrentDirectoryW';
+function SetDefaultCommConfig; external kernel32 name 'SetDefaultCommConfigW';
+function SetEnvironmentVariable; external kernel32 name 'SetEnvironmentVariableW';
+function SetFileAttributes; external kernel32 name 'SetFileAttributesW';
+function SetLocaleInfo; external kernel32 name 'SetLocaleInfoW';
+function SetVolumeLabel; external kernel32 name 'SetVolumeLabelW';
+function UpdateResource; external kernel32 name 'UpdateResourceW';
+function VerLanguageName; external kernel32 name 'VerLanguageNameW';
+function WaitNamedPipe; external kernel32 name 'WaitNamedPipeW';
+function WriteConsole; external kernel32 name 'WriteConsoleW';
+function WriteConsoleInput; external kernel32 name 'WriteConsoleInputW';
+function WriteConsoleOutput; external kernel32 name 'WriteConsoleOutputW';
+function WriteConsoleOutputCharacter; external kernel32 name 'WriteConsoleOutputCharacterW';
+function WritePrivateProfileSection; external kernel32 name 'WritePrivateProfileSectionW';
+function WritePrivateProfileString; external kernel32 name 'WritePrivateProfileStringW';
+function WriteProfileSection; external kernel32 name 'WriteProfileSectionW';
+function WriteProfileString; external kernel32 name 'WriteProfileStringW';
+function lstrcat; external kernel32 name 'lstrcatW';
+function lstrcmp; external kernel32 name 'lstrcmpW';
+function lstrcmpi; external kernel32 name 'lstrcmpiW';
+function lstrcpy; external kernel32 name 'lstrcpyW';
+function lstrcpyn; external kernel32 name 'lstrcpynW';
+function lstrlen; external kernel32 name 'lstrlenW';
+function MultinetGetConnectionPerformance; external mpr name 'MultinetGetConnectionPerformanceW';
+function WNetAddConnection2; external mpr name 'WNetAddConnection2W';
+function WNetAddConnection3; external mpr name 'WNetAddConnection3W';
+function WNetAddConnection; external mpr name 'WNetAddConnectionW';
+function WNetCancelConnection2; external mpr name 'WNetCancelConnection2W';
+function WNetCancelConnection; external mpr name 'WNetCancelConnectionW';
+function WNetConnectionDialog1; external mpr name 'WNetConnectionDialog1W';
+function WNetDisconnectDialog1; external mpr name 'WNetDisconnectDialog1W';
+function WNetEnumResource; external mpr name 'WNetEnumResourceW';
+function WNetGetConnection; external mpr name 'WNetGetConnectionW';
+function WNetGetLastError; external mpr name 'WNetGetLastErrorW';
+function WNetGetNetworkInformation; external mpr name 'WNetGetNetworkInformationW';
+function WNetGetProviderName; external mpr name 'WNetGetProviderNameW';
+function WNetGetResourceParent; external mpr name 'WNetGetResourceParentW';
+function WNetGetUniversalName; external mpr name 'WNetGetUniversalNameW';
+function WNetGetUser; external mpr name 'WNetGetUserW';
+function WNetOpenEnum; external mpr name 'WNetOpenEnumW';
+function WNetSetConnection; external mpr name 'WNetSetConnectionW';
+function WNetUseConnection; external mpr name 'WNetUseConnectionW';
+function GetFileVersionInfo; external version name 'GetFileVersionInfoW';
+function GetFileVersionInfoSize; external version name 'GetFileVersionInfoSizeW';
+function VerFindFile; external version name 'VerFindFileW';
+function VerInstallFile; external version name 'VerInstallFileW';
+function VerQueryValue; external version name 'VerQueryValueW';
+function GetPrivateProfileStruct; external kernel32 name 'GetPrivateProfileStructW';
+function WritePrivateProfileStruct; external kernel32 name 'WritePrivateProfileStructW';
+function AddFontResource; external gdi32 name 'AddFontResourceW';
+{$IFDEF _D4orHigher}
+function AddFontResourceEx; external gdi32 name 'AddFontResourceExW';
+{$ENDIF}
+function CopyEnhMetaFile; external gdi32 name 'CopyEnhMetaFileW';
+function CopyMetaFile; external gdi32 name 'CopyMetaFileW';
+function CreateColorSpace; external gdi32 name 'CreateColorSpaceW';
+function CreateDC; external gdi32 name 'CreateDCW';
+function CreateEnhMetaFile; external gdi32 name 'CreateEnhMetaFileW';
+function CreateFont; external gdi32 name 'CreateFontW';
+function CreateFontIndirect; external gdi32 name 'CreateFontIndirectW';
+{$IFDEF _D4orHigher}
+function CreateFontIndirectEx; external gdi32 name 'CreateFontIndirectExW';
+{$ENDIF}
+function CreateIC; external gdi32 name 'CreateICW';
+function CreateMetaFile; external gdi32 name 'CreateMetaFileW';
+function CreateScalableFontResource; external gdi32 name 'CreateScalableFontResourceW';
+function DeviceCapabilities; external gdi32 name 'DeviceCapabilitiesW';
+function EnumFontFamilies; external gdi32 name 'EnumFontFamiliesW';
+function EnumFontFamiliesEx; external gdi32 name 'EnumFontFamiliesExW';
+function EnumFonts; external gdi32 name 'EnumFontsW';
+function EnumICMProfiles; external gdi32 name 'EnumICMProfilesW';
+function ExtTextOut; external gdi32 name 'ExtTextOutW';
+function GetCharABCWidths; external gdi32 name 'GetCharABCWidthsW';
+function GetCharABCWidthsFloat; external gdi32 name 'GetCharABCWidthsFloatW';
+function GetCharWidth32; external gdi32 name 'GetCharWidth32W';
+function GetCharWidth; external gdi32 name 'GetCharWidthW';
+function GetCharWidthFloat; external gdi32 name 'GetCharWidthFloatW';
+function GetCharacterPlacement; external gdi32 name 'GetCharacterPlacementW';
+function GetEnhMetaFile; external gdi32 name 'GetEnhMetaFileW';
+function GetEnhMetaFileDescription; external gdi32 name 'GetEnhMetaFileDescriptionW';
+function GetGlyphIndices; external gdi32 name 'GetGlyphIndicesW';
+function GetGlyphOutline; external gdi32 name 'GetGlyphOutlineW';
+function GetICMProfile; external gdi32 name 'GetICMProfileW';
+function GetLogColorSpace; external gdi32 name 'GetLogColorSpaceW';
+function GetMetaFile; external gdi32 name 'GetMetaFileW';
+function GetObject; external gdi32 name 'GetObjectW';
+function GetOutlineTextMetrics; external gdi32 name 'GetOutlineTextMetricsW';
+function GetTextExtentExPoint; external gdi32 name 'GetTextExtentExPointW';
+function GetTextExtentPoint32; external gdi32 name 'GetTextExtentPoint32W';
+function GetTextExtentPoint; external gdi32 name 'GetTextExtentPointW';
+function GetTextFace; external gdi32 name 'GetTextFaceW';
+function GetTextMetrics; external gdi32 name 'GetTextMetricsW';
+function PolyTextOut; external gdi32 name 'PolyTextOutW';
+function RemoveFontResource; external gdi32 name 'RemoveFontResourceW';
+{$IFDEF _D4orHigher}
+function RemoveFontResourceEx; external gdi32 name 'RemoveFontResourceExW';
+{$ENDIF}
+function ResetDC; external gdi32 name 'ResetDCW';
+function SetICMProfile; external gdi32 name 'SetICMProfileW';
+function StartDoc; external gdi32 name 'StartDocW';
+function TextOut; external gdi32 name 'TextOutW';
+function UpdateICMRegKey; external gdi32 name 'UpdateICMRegKeyW';
+function wglUseFontBitmaps; external opengl32 name 'wglUseFontBitmapsW';
+function wglUseFontOutlines; external opengl32 name 'wglUseFontOutlinesW';
+function AnsiToOem; external user32 name 'CharToOemW';
+function AnsiToOemBuff; external user32 name 'CharToOemBuffW';
+function AnsiUpper; external user32 name 'CharUpperW';
+function AnsiUpperBuff; external user32 name 'CharUpperBuffW';
+function AnsiLower; external user32 name 'CharLowerW';
+function AnsiLowerBuff; external user32 name 'CharLowerBuffW';
+function AnsiNext; external user32 name 'CharNextW';
+function AnsiPrev; external user32 name 'CharPrevW';
+function AppendMenu; external user32 name 'AppendMenuW';
+//function BroadcastSystemMessage; external user32 name 'BroadcastSystemMessageW';
+//function BroadcastSystemMessageW; external user32 name 'BroadcastSystemMessageW';
+function CallMsgFilter; external user32 name 'CallMsgFilterW';
+function CallWindowProc; external user32 name 'CallWindowProcW';
+function ChangeDisplaySettings; external user32 name 'ChangeDisplaySettingsW';
+function ChangeDisplaySettingsEx; external user32 name 'ChangeDisplaySettingsExW';
+function ChangeMenu; external user32 name 'ChangeMenuW';
+function CharLower; external user32 name 'CharLowerW';
+function CharLowerBuff; external user32 name 'CharLowerBuffW';
+function CharNext; external user32 name 'CharNextW';
+function CharNextEx; external user32 name 'CharNextExW';
+function CharPrev; external user32 name 'CharPrevW';
+function CharPrevEx; external user32 name 'CharPrevExW';
+function CharToOem; external user32 name 'CharToOemW';
+function CharToOemBuff; external user32 name 'CharToOemBuffW';
+function CharUpper; external user32 name 'CharUpperW';
+function CharUpperBuff; external user32 name 'CharUpperBuffW';
+function CopyAcceleratorTable; external user32 name 'CopyAcceleratorTableW';
+function CreateAcceleratorTable; external user32 name 'CreateAcceleratorTableW';
+function CreateDesktop; external user32 name 'CreateDesktopW';
+function CreateDialogIndirectParam; external user32 name 'CreateDialogIndirectParamW';
+function CreateDialogParam; external user32 name 'CreateDialogParamW';
+function CreateMDIWindow; external user32 name 'CreateMDIWindowW';
+function CreateWindowEx; external user32 name 'CreateWindowExW';
+function CreateWindowStation; external user32 name 'CreateWindowStationW';
+function DefDlgProc; external user32 name 'DefDlgProcW';
+function DefFrameProc; external user32 name 'DefFrameProcW';
+function DefMDIChildProc; external user32 name 'DefMDIChildProcW';
+function DefWindowProc; external user32 name 'DefWindowProcW';
+function DialogBoxIndirectParam; external user32 name 'DialogBoxIndirectParamW';
+function DialogBoxParam; external user32 name 'DialogBoxParamW';
+function DispatchMessage; external user32 name 'DispatchMessageW';
+function DlgDirList; external user32 name 'DlgDirListW';
+function DlgDirListComboBox; external user32 name 'DlgDirListComboBoxW';
+function DlgDirSelectComboBoxEx; external user32 name 'DlgDirSelectComboBoxExW';
+function DlgDirSelectEx; external user32 name 'DlgDirSelectExW';
+function DrawState; external user32 name 'DrawStateW';
+function DrawText; external user32 name 'DrawTextW';
+function DrawTextEx; external user32 name 'DrawTextExW';
+function EnumDesktops; external user32 name 'EnumDesktopsW';
+function EnumDisplaySettings; external user32 name 'EnumDisplaySettingsW';
+{$IFDEF _D4orHigher}
+function EnumDisplayDevices; external user32 name 'EnumDisplayDevicesW';
+{$ENDIF}
+function EnumProps; external user32 name 'EnumPropsW';
+function EnumPropsEx; external user32 name 'EnumPropsExW';
+function EnumWindowStations; external user32 name 'EnumWindowStationsW';
+function FindWindow; external user32 name 'FindWindowW';
+function FindWindowEx; external user32 name 'FindWindowExW';
+{$IFDEF _D4orHigher}
+function GetAltTabInfo; external user32 name 'GetAltTabInfoW';
+{$ENDIF}
+function GetClassInfo; external user32 name 'GetClassInfoW';
+function GetClassInfoEx; external user32 name 'GetClassInfoExW';
+function GetClassLong; external user32 name 'GetClassLongW';
+function GetClassName; external user32 name 'GetClassNameW';
+function GetClipboardFormatName; external user32 name 'GetClipboardFormatNameW';
+function GetDlgItemText; external user32 name 'GetDlgItemTextW';
+function GetKeyNameText; external user32 name 'GetKeyNameTextW';
+function GetKeyboardLayoutName; external user32 name 'GetKeyboardLayoutNameW';
+function GetMenuItemInfo; external user32 name 'GetMenuItemInfoW';
+function GetMenuString; external user32 name 'GetMenuStringW';
+function GetMessage; external user32 name 'GetMessageW';
+function GetProp; external user32 name 'GetPropW';
+function GetTabbedTextExtent; external user32 name 'GetTabbedTextExtentW';
+function GetUserObjectInformation; external user32 name 'GetUserObjectInformationW';
+function GetWindowLong; external user32 name 'GetWindowLongW';
+function GetWindowModuleFileName; external user32 name 'GetWindowModuleFileNameW';
+function GetWindowText; external user32 name 'GetWindowTextW';
+function GetWindowTextLength; external user32 name 'GetWindowTextLengthW';
+function GrayString; external user32 name 'GrayStringW';
+function InsertMenu; external user32 name 'InsertMenuW';
+function InsertMenuItem; external user32 name 'InsertMenuItemW';
+function IsCharAlpha; external user32 name 'IsCharAlphaW';
+function IsCharAlphaNumeric; external user32 name 'IsCharAlphaNumericW';
+function IsCharLower; external user32 name 'IsCharLowerW';
+function IsCharUpper; external user32 name 'IsCharUpperW';
+function IsDialogMessage; external user32 name 'IsDialogMessageW';
+function LoadAccelerators; external user32 name 'LoadAcceleratorsW';
+function LoadBitmap; external user32 name 'LoadBitmapW';
+function LoadCursor; external user32 name 'LoadCursorW';
+function LoadCursorFromFile; external user32 name 'LoadCursorFromFileW';
+function LoadIcon; external user32 name 'LoadIconW';
+function LoadImage; external user32 name 'LoadImageW';
+function LoadKeyboardLayout; external user32 name 'LoadKeyboardLayoutW';
+function LoadMenu; external user32 name 'LoadMenuW';
+function LoadMenuIndirect; external user32 name 'LoadMenuIndirectW';
+function LoadString; external user32 name 'LoadStringW';
+function MapVirtualKey; external user32 name 'MapVirtualKeyW';
+function MapVirtualKeyEx; external user32 name 'MapVirtualKeyExW';
+function MessageBox; external user32 name 'MessageBoxW';
+function MessageBoxEx; external user32 name 'MessageBoxExW';
+function MessageBoxIndirect; external user32 name 'MessageBoxIndirectW';
+function ModifyMenu; external user32 name 'ModifyMenuW';
+function OemToAnsi; external user32 name 'OemToCharW';
+function OemToAnsiBuff; external user32 name 'OemToCharBuffW';
+function OemToChar; external user32 name 'OemToCharW';
+function OemToCharBuff; external user32 name 'OemToCharBuffW';
+function OpenDesktop; external user32 name 'OpenDesktopW';
+function OpenWindowStation; external user32 name 'OpenWindowStationW';
+function PeekMessage; external user32 name 'PeekMessageW';
+function PostMessage; external user32 name 'PostMessageW';
+function PostThreadMessage; external user32 name 'PostThreadMessageW';
+function RealGetWindowClass; external user32 name 'RealGetWindowClassW';
+function RegisterClass; external user32 name 'RegisterClassW';
+function RegisterClassEx; external user32 name 'RegisterClassExW';
+function RegisterClipboardFormat; external user32 name 'RegisterClipboardFormatW';
+{$IFDEF _D4orHigher}
+function RegisterDeviceNotification; external user32 name 'RegisterDeviceNotificationW';
+{$ENDIF}
+function RegisterWindowMessage; external user32 name 'RegisterWindowMessageW';
+function RemoveProp; external user32 name 'RemovePropW';
+function SendDlgItemMessage; external user32 name 'SendDlgItemMessageW';
+function SendMessage; external user32 name 'SendMessageW';
+function SendMessageCallback; external user32 name 'SendMessageCallbackW';
+function SendMessageTimeout; external user32 name 'SendMessageTimeoutW';
+function SendNotifyMessage; external user32 name 'SendNotifyMessageW';
+function SetClassLong; external user32 name 'SetClassLongW';
+function SetDlgItemText; external user32 name 'SetDlgItemTextW';
+function SetMenuItemInfo; external user32 name 'SetMenuItemInfoW';
+function SetProp; external user32 name 'SetPropW';
+function SetUserObjectInformation; external user32 name 'SetUserObjectInformationW';
+function SetWindowLong; external user32 name 'SetWindowLongW';
+function SetWindowText; external user32 name 'SetWindowTextW';
+function SetWindowsHook; external user32 name 'SetWindowsHookW';
+function SetWindowsHookEx; external user32 name 'SetWindowsHookExW';
+function SystemParametersInfo; external user32 name 'SystemParametersInfoW';
+function TabbedTextOut; external user32 name 'TabbedTextOutW';
+function TranslateAccelerator; external user32 name 'TranslateAcceleratorW';
+function UnregisterClass; external user32 name 'UnregisterClassW';
+function VkKeyScan; external user32 name 'VkKeyScanW';
+function VkKeyScanEx; external user32 name 'VkKeyScanExW';
+function WinHelp; external user32 name 'WinHelpW';
+function wsprintf; external user32 name 'wsprintfW';
+function wvsprintf; external user32 name 'wvsprintfW';
+// NT 4.0 bug workaround - NT 4.0 doesn't test bInitialOwner for zero/nonzero, it tests for 1
+function _CreateMutex(lpMutexAttributes: PSecurityAttributes;
+ bInitialOwner: Integer; lpName: PWideChar): THandle; stdcall;
+ external kernel32 name 'CreateMutexW';
+function CreateMutex(lpMutexAttributes: PSecurityAttributes; bInitialOwner: BOOL; lpName: PWideChar): THandle;
+begin
+ Result := _CreateMutex(lpMutexAttributes, Integer(Boolean(bInitialOwner)), lpName);
+end;
+{$ENDIF implementation_part} ///////////////////////////////////////////////////
diff --git a/plugins/Libs/KolZLibBzip.pas b/plugins/Libs/KolZLibBzip.pas
new file mode 100644
index 0000000000..f6b91a4d48
--- /dev/null
+++ b/plugins/Libs/KolZLibBzip.pas
@@ -0,0 +1,1940 @@
+//{$DEFINE USE_EXCEPTIONS}
+{*****************************************************************************
+* unit based on *
+* ZLibEx.pas (zlib 1.2.1) *
+* Edition 2003.12.18 *
+* *
+* copyright (c) 2002-2003 Roberto Della Pasqua (www.dellapasqua.com) *
+* copyright (c) 2000-2002 base2 technologies (www.base2ti.com) *
+* copyright (c) 1997 Borland International (www.borland.com) *
+* *
+* and *
+* *
+* BZip2 unit by Edison Mera (www.geocities.com/efmera/) *
+* Version 1.02 *
+* Edition 21-11-2002 *
+* *
+* Changes made by GMax: *
+* *
+* units joined. gzip support functions added. *
+* compression/decompression streams classes excluded, *
+* compression/decompression stream2stream functions added *
+* *
+* procedures converted to functions to add "no exceptions" functionality *
+* return values actual ONLY in this case (no exceptions) *
+* error occured while value<0 *
+* *
+* function names for Z(De)Compress changed to Z(De)CompressBuf *
+* *
+* (C) GMax 2004. email: gmax@loving.ru *
+*****************************************************************************}
+
+unit KolZLibBzip;
+
+interface
+
+uses
+ KOL{$IFDEF USE_EXCEPTIONS}, ERR{$ENDIF};
+
+const
+ ZLIB_VERSION = '1.2.1';
+ BZIP_VERSION = '1.0.2';
+
+type
+ TAlloc = function(opaque: Pointer; Items, Size: Integer): Pointer; cdecl;
+ TFree = procedure(opaque, Block: Pointer); cdecl;
+
+ TZCompressionLevel = (zcNone, zcFastest, zcDefault, zcMax);
+ TZCompressionStrategy = (zcsDefault, zcsFiltered, zcsHuffmanOnly);
+ {** TZStreamRec ***********************************************************}
+
+ TZStreamRec = packed record
+ next_in: PChar; // next input byte
+ avail_in: Longint; // number of bytes available at next_in
+ total_in: Longint; // total nb of input bytes read so far
+
+ next_out: PChar; // next output byte should be put here
+ avail_out: Longint; // remaining free space at next_out
+ total_out: Longint; // total nb of bytes output so far
+
+ msg: PChar; // last error message, NULL if no error
+ state: Pointer; // not visible by applications
+
+ zalloc: TAlloc; // used to allocate the internal state
+ zfree: TFree; // used to free the internal state
+ opaque: Pointer; // private data object passed to zalloc and zfree
+
+ data_type: Integer; // best guess about the data type: ascii or binary
+ adler: Longint; // adler32 value of the uncompressed data
+ reserved: Longint; // reserved for future use
+ end;
+
+ {** zlib public routines ****************************************************}
+
+ {*****************************************************************************
+ * ZCompressBuf *
+ * *
+ * pre-conditions *
+ * inBuffer = pointer to uncompressed data *
+ * inSize = size of inBuffer (bytes) *
+ * outBuffer = pointer (unallocated) *
+ * level = compression level *
+ * *
+ * post-conditions *
+ * outBuffer = pointer to compressed data (allocated) *
+ * outSize = size of outBuffer (bytes) *
+ *****************************************************************************}
+
+function ZCompressBuf(const inBuffer: Pointer; inSize: Integer;
+ out outBuffer: Pointer; out outSize: Integer;
+ level: TZCompressionLevel = zcDefault): Integer;
+
+{*****************************************************************************
+* ZDecompressBuf *
+* *
+* pre-conditions *
+* inBuffer = pointer to compressed data *
+* inSize = size of inBuffer (bytes) *
+* outBuffer = pointer (unallocated) *
+* outEstimate = estimated size of uncompressed data (bytes) *
+* *
+* post-conditions *
+* outBuffer = pointer to decompressed data (allocated) *
+* outSize = size of outBuffer (bytes) *
+*****************************************************************************}
+
+function ZDecompressBuf(const inBuffer: Pointer; inSize: Integer;
+ out outBuffer: Pointer; out outSize: Integer; outEstimate: Integer = 0): Integer;
+
+{** string routines *********************************************************}
+
+function ZCompressStr(const s: string; level: TZCompressionLevel = zcDefault): string;
+
+function ZDecompressStr(const s: string): string;
+
+{** stream routines *********************************************************}
+
+function ZCompressStream(inStream, outStream: PStream;
+ level: TZCompressionLevel = zcDefault): Integer;
+
+function ZDecompressStream(inStream, outStream: PStream): Integer;
+
+{** utility routines ********************************************************}
+
+function adler32(adler: LongInt; const buf: PChar; len: Integer): LongInt;
+function CRC32(CRC: Cardinal; const Data: PChar; cbData: Cardinal): Cardinal;
+function compressBound(sourceLen: LongInt): LongInt;
+
+{****************************************************************************}
+
+procedure MoveI32(const Source; var Dest; Count: Integer);
+procedure ZFastCompressString(var s: string; level: TZCompressionLevel);
+procedure ZFastDecompressString(var s: string);
+procedure ZSendToBrowser(var s: string);
+
+type
+ TgzipHeader = packed record
+ FileName: string;
+ Comment: string;
+ FileTime: TDateTime;
+ Extra: string;
+ end;
+
+function gZipCompressStream(inStream, outStream: PStream; var gzHdr: TgzipHeader; level: TZCompressionLevel = zcDefault; strategy: TZCompressionStrategy = zcsDefault): Integer; overload;
+function gZipCompressStream(inStream, outStream: PStream; level: TZCompressionLevel = zcDefault; strategy: TZCompressionStrategy = zcsDefault): Integer; overload;
+function gZipDecompressStreamHeader(inStream: PStream; var gzHdr: TgzipHeader): Integer;
+function gZipDecompressStreamBody(inStream, outStream: PStream): Integer;
+function gZipDecompressStream(inStream, outStream: PStream; var gzHdr: TgzipHeader): Integer;
+function gZipDecompressString(const S: String): String;
+
+{*******************************************************}
+{ }
+{ BZIP2 Data Compression Interface Unit }
+{ }
+{*******************************************************}
+type
+ // Internal structure.
+ TBZStreamRec = packed record
+ next_in: PChar; // next input byte
+ avail_in: Integer; // number of bytes available at next_in
+ total_in_lo32: Integer; // total nb of input bytes read so far
+ total_in_hi32: Integer;
+
+ next_out: PChar; // next output byte should be put here
+ avail_out: Integer; // remaining free space at next_out
+ total_out_lo32: Integer; // total nb of bytes output so far
+ total_out_hi32: Integer;
+
+ state: Pointer;
+
+ bzalloc: TAlloc; // used to allocate the internal state
+ bzfree: TFree; // used to free the internal state
+ opaque: Pointer;
+ end;
+ TBlockSize100k = 1..9;
+ { CompressBuf compresses data, buffer to buffer, in one call.
+ In: InBuf = ptr to compressed data
+ InBytes = number of bytes in InBuf
+ Out: OutBuf = ptr to newly allocated buffer containing decompressed data
+ OutBytes = number of bytes in OutBuf }
+function BZCompressBuf(const InBuf: Pointer; InBytes: Integer;
+ out OutBuf: Pointer; out OutBytes: Integer): Integer;
+
+{ DecompressBuf decompresses data, buffer to buffer, in one call.
+ In: InBuf = ptr to compressed data
+ InBytes = number of bytes in InBuf
+ OutEstimate = zero, or est. size of the decompressed data
+ Out: OutBuf = ptr to newly allocated buffer containing decompressed data
+ OutBytes = number of bytes in OutBuf }
+function BZDecompressBuf(const InBuf: Pointer; InBytes: Integer;
+ OutEstimate: Integer; out OutBuf: Pointer; out OutBytes: Integer): Integer;
+
+function BZCompressStream(inStream, outStream: PStream; BlockSize100k: TBlockSize100k = 5): Integer;
+function BZDecompressStream(inStream, outStream: PStream): Integer;
+
+
+{** deflate routines ********************************************************}
+
+function deflateInit_(var strm: TZStreamRec; level: Integer; version: PChar;
+ recsize: Integer): Integer; external;
+
+function DeflateInit2_(var strm: TZStreamRec; level: integer; method: integer; windowBits: integer;
+ memLevel: integer; strategy: integer; version: PChar; recsize: integer): integer; external;
+
+function deflate(var strm: TZStreamRec; flush: Integer): Integer;
+ external;
+
+function deflateEnd(var strm: TZStreamRec): Integer; external;
+
+{** inflate routines ********************************************************}
+
+function inflateInit_(var strm: TZStreamRec; version: PChar;
+ recsize: Integer): Integer; external;
+
+function inflateInit2_(var strm: TZStreamRec; windowBits: integer;
+ version: PChar; recsize: integer): integer; external;
+
+function inflate(var strm: TZStreamRec; flush: Integer): Integer;
+ external;
+
+function inflateEnd(var strm: TZStreamRec): Integer; external;
+
+function inflateReset(var strm: TZStreamRec): Integer; external;
+
+const
+ gzBufferSize = 16384;
+ gz_magic : array[0..1] of Byte = ($1F, $8B);
+ { gzip flag byte }
+
+ GZF_ASCII_FLAG = $01; { bit 0 set: file probably ascii text }
+ GZF_HEAD_CRC = $02; { bit 1 set: header CRC present }
+ GZF_EXTRA_FIELD = $04; { bit 2 set: extra field present }
+ GZF_ORIG_NAME = $08; { bit 3 set: original file name present }
+ GZF_COMMENT = $10; { bit 4 set: file comment present }
+ GZF_RESERVED = $E0; { bits 5..7: reserved }
+ Z_EOF = -1;
+
+const
+ { ** Maximum value for windowBits in deflateInit2 and inflateInit2 }
+ MAX_WBITS = 15;
+ { ** Maximum value for memLevel in deflateInit2 }
+const
+ MAX_MEM_LEVEL = 9;
+ DEF_MEM_LEVEL = 8;
+
+ {** link zlib 1.2.1 *********************************************************}
+ {** bcc32 flags: -c -6 -O2 -Ve -X- -pr -a8 -b -d -k- -vi -tWM -r -RT- }
+
+{$L zlib\adler32.obj}
+{$L zlib\compress.obj}
+ {.$L zlib\crc32.obj}
+{$L zlib\deflate.obj}
+{$L zlib\infback.obj}
+{$L zlib\inffast.obj}
+{$L zlib\inflate.obj}
+{$L zlib\inftrees.obj}
+{$L zlib\trees.obj}
+{$L zlib\uncompr.obj}
+
+ {*****************************************************************************
+ * note: do not reorder the above -- doing so will result in external *
+ * functions being undefined *
+ *****************************************************************************}
+
+const
+ {** flush constants *******************************************************}
+
+ Z_NO_FLUSH = 0;
+ Z_PARTIAL_FLUSH = 1;
+ Z_SYNC_FLUSH = 2;
+ Z_FULL_FLUSH = 3;
+ Z_FINISH = 4;
+
+ {** return codes **********************************************************}
+
+ Z_OK = 0;
+ Z_STREAM_END = 1;
+ Z_NEED_DICT = 2;
+ Z_ERRNO = (-1);
+ Z_STREAM_ERROR = (-2);
+ Z_DATA_ERROR = (-3);
+ Z_MEM_ERROR = (-4);
+ Z_BUF_ERROR = (-5);
+ Z_VERSION_ERROR = (-6);
+ Z_WRITE_ERROR = (-10);
+ Z_CRC_ERROR = (-11);
+ Z_SIZE_ERROR = (-12);
+
+ {** compression levels ****************************************************}
+
+ Z_NO_COMPRESSION = 0;
+ Z_BEST_SPEED = 1;
+ Z_BEST_COMPRESSION = 9;
+ Z_DEFAULT_COMPRESSION = (-1);
+
+ {** compression strategies ************************************************}
+
+ Z_FILTERED = 1;
+ Z_HUFFMAN_ONLY = 2;
+ Z_DEFAULT_STRATEGY = 0;
+
+ {** data types ************************************************************}
+
+ Z_BINARY = 0;
+ Z_ASCII = 1;
+ Z_UNKNOWN = 2;
+
+ {** compression methods ***************************************************}
+
+ Z_DEFLATED = 8;
+
+ Z_NULL = nil; { for initializing zalloc, zfree, opaque }
+
+ {** return code messages **************************************************}
+
+ _z_errmsg : array[0..14] of PChar = (
+ 'need dictionary', // Z_NEED_DICT (2)
+ 'stream end', // Z_STREAM_END (1)
+ '', // Z_OK (0)
+ 'file error', // Z_ERRNO (-1)
+ 'stream error', // Z_STREAM_ERROR (-2)
+ 'data error', // Z_DATA_ERROR (-3)
+ 'insufficient memory', // Z_MEM_ERROR (-4)
+ 'buffer error', // Z_BUF_ERROR (-5)
+ 'incompatible version', // Z_VERSION_ERROR (-6)
+ '', '', '',
+ 'stream write error', // Z_WRITE_ERROR = (-10);
+ 'crc error', // Z_CRC_ERROR = (-11);
+ 'size mismarch' // Z_SIZE_ERROR = (-12);
+ );
+
+ ZLevels : array[TZCompressionLevel] of Shortint = (
+ Z_NO_COMPRESSION,
+ Z_BEST_SPEED,
+ Z_DEFAULT_COMPRESSION,
+ Z_BEST_COMPRESSION
+ );
+ ZStrategy : array[TZCompressionStrategy] of Shortint = (
+ Z_DEFAULT_STRATEGY,
+ Z_FILTERED,
+ Z_HUFFMAN_ONLY
+ );
+
+ {************** BZip constants **********************************************}
+{$L bz2\blocks~1.obj} //blocksort
+{$L bz2\huffman.obj}
+{$L bz2\compress.obj}
+{$L bz2\decomp~1.obj} //decompress
+{$L bz2\bzlib.obj}
+ { $L bz2\crctable.obj}
+ { $L bz2\randtable.obj}
+
+procedure _BZ2_hbMakeCodeLengths; external;
+procedure _BZ2_blockSort; external;
+procedure _BZ2_hbCreateDecodeTables; external;
+procedure _BZ2_hbAssignCodes; external;
+procedure _BZ2_compressBlock; external;
+procedure _BZ2_decompress; external;
+
+const
+ bzBufferSize = 32768;
+
+ BZ_RUN = 0;
+ BZ_FLUSH = 1;
+ BZ_FINISH = 2;
+ BZ_OK = 0;
+ BZ_RUN_OK = 1;
+ BZ_FLUSH_OK = 2;
+ BZ_FINISH_OK = 3;
+ BZ_STREAM_END = 4;
+ BZ_SEQUENCE_ERROR = (-1);
+ BZ_PARAM_ERROR = (-2);
+ BZ_MEM_ERROR = (-3);
+ BZ_DATA_ERROR = (-4);
+ BZ_DATA_ERROR_MAGIC = (-5);
+ BZ_IO_ERROR = (-6);
+ BZ_UNEXPECTED_EOF = (-7);
+ BZ_OUTBUFF_FULL = (-8);
+
+ BZ_Error_Msg : array[1..8] of PChar = (
+ 'BZ_SEQUENCE_ERROR',
+ 'BZ_PARAM_ERROR',
+ 'BZ_MEM_ERROR',
+ 'BZ_DATA_ERROR',
+ 'BZ_DATA_ERROR_MAGIC',
+ 'BZ_IO_ERROR',
+ 'BZ_UNEXPECTED_EOF',
+ 'BZ_OUTBUFF_FULL'
+ );
+
+ BZ_BLOCK_SIZE_100K = 9;
+
+ _BZ2_rNums : array[0..511] of Longint = (
+ 619, 720, 127, 481, 931, 816, 813, 233, 566, 247,
+ 985, 724, 205, 454, 863, 491, 741, 242, 949, 214,
+ 733, 859, 335, 708, 621, 574, 73, 654, 730, 472,
+ 419, 436, 278, 496, 867, 210, 399, 680, 480, 51,
+ 878, 465, 811, 169, 869, 675, 611, 697, 867, 561,
+ 862, 687, 507, 283, 482, 129, 807, 591, 733, 623,
+ 150, 238, 59, 379, 684, 877, 625, 169, 643, 105,
+ 170, 607, 520, 932, 727, 476, 693, 425, 174, 647,
+ 73, 122, 335, 530, 442, 853, 695, 249, 445, 515,
+ 909, 545, 703, 919, 874, 474, 882, 500, 594, 612,
+ 641, 801, 220, 162, 819, 984, 589, 513, 495, 799,
+ 161, 604, 958, 533, 221, 400, 386, 867, 600, 782,
+ 382, 596, 414, 171, 516, 375, 682, 485, 911, 276,
+ 98, 553, 163, 354, 666, 933, 424, 341, 533, 870,
+ 227, 730, 475, 186, 263, 647, 537, 686, 600, 224,
+ 469, 68, 770, 919, 190, 373, 294, 822, 808, 206,
+ 184, 943, 795, 384, 383, 461, 404, 758, 839, 887,
+ 715, 67, 618, 276, 204, 918, 873, 777, 604, 560,
+ 951, 160, 578, 722, 79, 804, 96, 409, 713, 940,
+ 652, 934, 970, 447, 318, 353, 859, 672, 112, 785,
+ 645, 863, 803, 350, 139, 93, 354, 99, 820, 908,
+ 609, 772, 154, 274, 580, 184, 79, 626, 630, 742,
+ 653, 282, 762, 623, 680, 81, 927, 626, 789, 125,
+ 411, 521, 938, 300, 821, 78, 343, 175, 128, 250,
+ 170, 774, 972, 275, 999, 639, 495, 78, 352, 126,
+ 857, 956, 358, 619, 580, 124, 737, 594, 701, 612,
+ 669, 112, 134, 694, 363, 992, 809, 743, 168, 974,
+ 944, 375, 748, 52, 600, 747, 642, 182, 862, 81,
+ 344, 805, 988, 739, 511, 655, 814, 334, 249, 515,
+ 897, 955, 664, 981, 649, 113, 974, 459, 893, 228,
+ 433, 837, 553, 268, 926, 240, 102, 654, 459, 51,
+ 686, 754, 806, 760, 493, 403, 415, 394, 687, 700,
+ 946, 670, 656, 610, 738, 392, 760, 799, 887, 653,
+ 978, 321, 576, 617, 626, 502, 894, 679, 243, 440,
+ 680, 879, 194, 572, 640, 724, 926, 56, 204, 700,
+ 707, 151, 457, 449, 797, 195, 791, 558, 945, 679,
+ 297, 59, 87, 824, 713, 663, 412, 693, 342, 606,
+ 134, 108, 571, 364, 631, 212, 174, 643, 304, 329,
+ 343, 97, 430, 751, 497, 314, 983, 374, 822, 928,
+ 140, 206, 73, 263, 980, 736, 876, 478, 430, 305,
+ 170, 514, 364, 692, 829, 82, 855, 953, 676, 246,
+ 369, 970, 294, 750, 807, 827, 150, 790, 288, 923,
+ 804, 378, 215, 828, 592, 281, 565, 555, 710, 82,
+ 896, 831, 547, 261, 524, 462, 293, 465, 502, 56,
+ 661, 821, 976, 991, 658, 869, 905, 758, 745, 193,
+ 768, 550, 608, 933, 378, 286, 215, 979, 792, 961,
+ 61, 688, 793, 644, 986, 403, 106, 366, 905, 644,
+ 372, 567, 466, 434, 645, 210, 389, 550, 919, 135,
+ 780, 773, 635, 389, 707, 100, 626, 958, 165, 504,
+ 920, 176, 193, 713, 857, 265, 203, 50, 668, 108,
+ 645, 990, 626, 197, 510, 357, 358, 850, 858, 364,
+ 936, 638
+ );
+
+ _BZ2_crc32Table : array[0..255] of Longint = (
+ $00000000, $04C11DB7, $09823B6E, $0D4326D9,
+ $130476DC, $17C56B6B, $1A864DB2, $1E475005,
+ $2608EDB8, $22C9F00F, $2F8AD6D6, $2B4BCB61,
+ $350C9B64, $31CD86D3, $3C8EA00A, $384FBDBD,
+ $4C11DB70, $48D0C6C7, $4593E01E, $4152FDA9,
+ $5F15ADAC, $5BD4B01B, $569796C2, $52568B75,
+ $6A1936C8, $6ED82B7F, $639B0DA6, $675A1011,
+ $791D4014, $7DDC5DA3, $709F7B7A, $745E66CD,
+ -$67DC4920, -$631D54A9, -$6E5E7272, -$6A9F6FC7,
+ -$74D83FC4, -$70192275, -$7D5A04AE, -$799B191B,
+ -$41D4A4A8, -$4515B911, -$48569FCA, -$4C97827F,
+ -$52D0D27C, -$5611CFCD, -$5B52E916, -$5F93F4A3,
+ -$2BCD9270, -$2F0C8FD9, -$224FA902, -$268EB4B7,
+ -$38C9E4B4, -$3C08F905, -$314BDFDE, -$358AC26B,
+ -$0DC57FD8, -$09046261, -$044744BA, -$0086590F,
+ -$1EC1090C, -$1A0014BD, -$17433266, -$13822FD3,
+ $34867077, $30476DC0, $3D044B19, $39C556AE,
+ $278206AB, $23431B1C, $2E003DC5, $2AC12072,
+ $128E9DCF, $164F8078, $1B0CA6A1, $1FCDBB16,
+ $018AEB13, $054BF6A4, $0808D07D, $0CC9CDCA,
+ $7897AB07, $7C56B6B0, $71159069, $75D48DDE,
+ $6B93DDDB, $6F52C06C, $6211E6B5, $66D0FB02,
+ $5E9F46BF, $5A5E5B08, $571D7DD1, $53DC6066,
+ $4D9B3063, $495A2DD4, $44190B0D, $40D816BA,
+ -$535A3969, -$579B24E0, -$5AD80207, -$5E191FB2,
+ -$405E4FB5, -$449F5204, -$49DC74DB, -$4D1D696E,
+ -$7552D4D1, -$7193C968, -$7CD0EFBF, -$7811F20A,
+ -$6656A20D, -$6297BFBC, -$6FD49963, -$6B1584D6,
+ -$1F4BE219, -$1B8AFFB0, -$16C9D977, -$1208C4C2,
+ -$0C4F94C5, -$088E8974, -$05CDAFAB, -$010CB21E,
+ -$39430FA1, -$3D821218, -$30C134CF, -$3400297A,
+ -$2A47797D, -$2E8664CC, -$23C54213, -$27045FA6,
+ $690CE0EE, $6DCDFD59, $608EDB80, $644FC637,
+ $7A089632, $7EC98B85, $738AAD5C, $774BB0EB,
+ $4F040D56, $4BC510E1, $46863638, $42472B8F,
+ $5C007B8A, $58C1663D, $558240E4, $51435D53,
+ $251D3B9E, $21DC2629, $2C9F00F0, $285E1D47,
+ $36194D42, $32D850F5, $3F9B762C, $3B5A6B9B,
+ $0315D626, $07D4CB91, $0A97ED48, $0E56F0FF,
+ $1011A0FA, $14D0BD4D, $19939B94, $1D528623,
+ -$0ED0A9F2, -$0A11B447, -$075292A0, -$03938F29,
+ -$1DD4DF2E, -$1915C29B, -$1456E444, -$1097F9F5,
+ -$28D8444A, -$2C1959FF, -$215A7F28, -$259B6291,
+ -$3BDC3296, -$3F1D2F23, -$325E09FC, -$369F144D,
+ -$42C17282, -$46006F37, -$4B4349F0, -$4F825459,
+ -$51C5045E, -$550419EB, -$58473F34, -$5C862285,
+ -$64C99F3A, -$6008828F, -$6D4BA458, -$698AB9E1,
+ -$77CDE9E6, -$730CF453, -$7E4FD28C, -$7A8ECF3D,
+ $5D8A9099, $594B8D2E, $5408ABF7, $50C9B640,
+ $4E8EE645, $4A4FFBF2, $470CDD2B, $43CDC09C,
+ $7B827D21, $7F436096, $7200464F, $76C15BF8,
+ $68860BFD, $6C47164A, $61043093, $65C52D24,
+ $119B4BE9, $155A565E, $18197087, $1CD86D30,
+ $029F3D35, $065E2082, $0B1D065B, $0FDC1BEC,
+ $3793A651, $3352BBE6, $3E119D3F, $3AD08088,
+ $2497D08D, $2056CD3A, $2D15EBE3, $29D4F654,
+ -$3A56D987, -$3E97C432, -$33D4E2E9, -$3715FF60,
+ -$2952AF5B, -$2D93B2EE, -$20D09435, -$24118984,
+ -$1C5E343F, -$189F298A, -$15DC0F51, -$111D12E8,
+ -$0F5A42E3, -$0B9B5F56, -$06D8798D, -$0219643C,
+ -$764702F7, -$72861F42, -$7FC53999, -$7B042430,
+ -$6543742B, -$6182699E, -$6CC14F45, -$680052F4,
+ -$504FEF4F, -$548EF2FA, -$59CDD421, -$5D0CC998,
+ -$434B9993, -$478A8426, -$4AC9A2FD, -$4E08BF4C
+ );
+
+// deflate compresses data
+
+function BZ2_bzCompressInit(var strm: TBZStreamRec; blockSize100k: Integer;
+ verbosity: Integer; workFactor: Integer): Integer; stdcall; external;
+
+function BZ2_bzCompress(var strm: TBZStreamRec; action: Integer): Integer; stdcall; external;
+
+function BZ2_bzCompressEnd(var strm: TBZStreamRec): Integer; stdcall; external;
+
+function BZ2_bzBuffToBuffCompress(dest: Pointer; var destLen: Integer; source: Pointer;
+ sourceLen, blockSize100k, verbosity, workFactor: Integer): Integer; stdcall; external;
+
+// inflate decompresses data
+
+function BZ2_bzDecompressInit(var strm: TBZStreamRec; verbosity: Integer;
+ small: Integer): Integer; stdcall; external;
+
+function BZ2_bzDecompress(var strm: TBZStreamRec): Integer; stdcall; external;
+
+function BZ2_bzDecompressEnd(var strm: TBZStreamRec): Integer; stdcall; external;
+
+function BZ2_bzBuffToBuffDecompress(dest: Pointer; var destLen: Integer; source: Pointer;
+ sourceLen, small, verbosity: Integer): Integer; stdcall; external;
+
+{** utility routines *******************************************************}
+
+function adler32(adler: LongInt; const buf: PChar; len: Integer): LongInt; external;
+function compressBound(sourceLen: LongInt): LongInt; external;
+
+//
+function InflateInit(var stream: TZStreamRec): Integer;
+
+implementation
+
+procedure _bz_internal_error(errcode: Integer); cdecl;
+begin
+{$IFDEF USE_EXCEPTIONS}
+ //raise EBZip2Error.CreateFmt('Compression Error %d', [errcode]);
+ raise Exception.CreateFMT(e_Convert, 'Compression Error %d', [errcode]);
+ // I don't know, what make in {$ELSE} :(
+{$ENDIF}
+end;
+
+function _malloc(size: Integer): Pointer; cdecl;
+begin
+ GetMem(Result, Size);
+end;
+
+procedure _free(block: Pointer); cdecl;
+begin
+ FreeMem(block);
+end;
+
+function bzip2AllocMem(AppData: Pointer; Items, Size: Integer): Pointer; cdecl;
+begin
+ GetMem(Result, Items * Size);
+end;
+
+procedure bzip2FreeMem(AppData, Block: Pointer); cdecl;
+begin
+ FreeMem(Block);
+end;
+
+{*********************** Peter Morris not aligned move **********************}
+
+procedure MoveI32(const Source; var Dest; Count: Integer); register;
+asm
+ cmp ECX,0
+ Je @JustQuit
+ push ESI
+ push EDI
+ mov ESI, EAX
+ mov EDI, EDX
+ @Loop:
+ Mov AL, [ESI]
+ Inc ESI
+ mov [EDI], AL
+ Inc EDI
+ Dec ECX
+ Jnz @Loop
+ pop EDI
+ pop ESI
+ @JustQuit:
+end;
+{****************************************************************************}
+
+{** utility routines *******************************************************}
+
+//function crc32; external;
+function CRC32(CRC: Cardinal; const Data: PChar; cbData: Cardinal): Cardinal; assembler;
+asm
+ or edx, edx
+ je @@exi
+ jecxz @@exi
+ xor eax,0FFFFFFFFh
+ push ebx
+@@upd:
+ movzx ebx, al
+ xor bl, [ edx ]
+ shr eax, 8
+ and eax, 00FFFFFFh
+ xor eax, cs:[ebx*4 + offset @@c32tt ]//OFFSET @@C32TT ]
+ inc edx
+ loop @@upd
+ pop ebx
+ xor eax,0FFFFFFFFh
+@@exi:
+ ret
+
+@@C32TT:
+
+DD 000000000h, 077073096h, 0ee0e612ch, 0990951bah
+DD 0076dc419h, 0706af48fh, 0e963a535h, 09e6495a3h
+DD 00edb8832h, 079dcb8a4h, 0e0d5e91eh, 097d2d988h
+DD 009b64c2bh, 07eb17cbdh, 0e7b82d07h, 090bf1d91h
+DD 01db71064h, 06ab020f2h, 0f3b97148h, 084be41deh
+DD 01adad47dh, 06ddde4ebh, 0f4d4b551h, 083d385c7h
+DD 0136c9856h, 0646ba8c0h, 0fd62f97ah, 08a65c9ech
+DD 014015c4fh, 063066cd9h, 0fa0f3d63h, 08d080df5h
+DD 03b6e20c8h, 04c69105eh, 0d56041e4h, 0a2677172h
+DD 03c03e4d1h, 04b04d447h, 0d20d85fdh, 0a50ab56bh
+DD 035b5a8fah, 042b2986ch, 0dbbbc9d6h, 0acbcf940h
+DD 032d86ce3h, 045df5c75h, 0dcd60dcfh, 0abd13d59h
+DD 026d930ach, 051de003ah, 0c8d75180h, 0bfd06116h
+DD 021b4f4b5h, 056b3c423h, 0cfba9599h, 0b8bda50fh
+DD 02802b89eh, 05f058808h, 0c60cd9b2h, 0b10be924h
+DD 02f6f7c87h, 058684c11h, 0c1611dabh, 0b6662d3dh
+DD 076dc4190h, 001db7106h, 098d220bch, 0efd5102ah
+DD 071b18589h, 006b6b51fh, 09fbfe4a5h, 0e8b8d433h
+DD 07807c9a2h, 00f00f934h, 09609a88eh, 0e10e9818h
+DD 07f6a0dbbh, 0086d3d2dh, 091646c97h, 0e6635c01h
+DD 06b6b51f4h, 01c6c6162h, 0856530d8h, 0f262004eh
+DD 06c0695edh, 01b01a57bh, 08208f4c1h, 0f50fc457h
+DD 065b0d9c6h, 012b7e950h, 08bbeb8eah, 0fcb9887ch
+DD 062dd1ddfh, 015da2d49h, 08cd37cf3h, 0fbd44c65h
+DD 04db26158h, 03ab551ceh, 0a3bc0074h, 0d4bb30e2h
+DD 04adfa541h, 03dd895d7h, 0a4d1c46dh, 0d3d6f4fbh
+DD 04369e96ah, 0346ed9fch, 0ad678846h, 0da60b8d0h
+DD 044042d73h, 033031de5h, 0aa0a4c5fh, 0dd0d7cc9h
+DD 05005713ch, 0270241aah, 0be0b1010h, 0c90c2086h
+DD 05768b525h, 0206f85b3h, 0b966d409h, 0ce61e49fh
+DD 05edef90eh, 029d9c998h, 0b0d09822h, 0c7d7a8b4h
+DD 059b33d17h, 02eb40d81h, 0b7bd5c3bh, 0c0ba6cadh
+DD 0edb88320h, 09abfb3b6h, 003b6e20ch, 074b1d29ah
+DD 0ead54739h, 09dd277afh, 004db2615h, 073dc1683h
+DD 0e3630b12h, 094643b84h, 00d6d6a3eh, 07a6a5aa8h
+DD 0e40ecf0bh, 09309ff9dh, 00a00ae27h, 07d079eb1h
+DD 0f00f9344h, 08708a3d2h, 01e01f268h, 06906c2feh
+DD 0f762575dh, 0806567cbh, 0196c3671h, 06e6b06e7h
+DD 0fed41b76h, 089d32be0h, 010da7a5ah, 067dd4acch
+DD 0f9b9df6fh, 08ebeeff9h, 017b7be43h, 060b08ed5h
+DD 0d6d6a3e8h, 0a1d1937eh, 038d8c2c4h, 04fdff252h
+DD 0d1bb67f1h, 0a6bc5767h, 03fb506ddh, 048b2364bh
+DD 0d80d2bdah, 0af0a1b4ch, 036034af6h, 041047a60h
+DD 0df60efc3h, 0a867df55h, 0316e8eefh, 04669be79h
+DD 0cb61b38ch, 0bc66831ah, 0256fd2a0h, 05268e236h
+DD 0cc0c7795h, 0bb0b4703h, 0220216b9h, 05505262fh
+DD 0c5ba3bbeh, 0b2bd0b28h, 02bb45a92h, 05cb36a04h
+DD 0c2d7ffa7h, 0b5d0cf31h, 02cd99e8bh, 05bdeae1dh
+DD 09b64c2b0h, 0ec63f226h, 0756aa39ch, 0026d930ah
+DD 09c0906a9h, 0eb0e363fh, 072076785h, 005005713h
+DD 095bf4a82h, 0e2b87a14h, 07bb12baeh, 00cb61b38h
+DD 092d28e9bh, 0e5d5be0dh, 07cdcefb7h, 00bdbdf21h
+DD 086d3d2d4h, 0f1d4e242h, 068ddb3f8h, 01fda836eh
+DD 081be16cdh, 0f6b9265bh, 06fb077e1h, 018b74777h
+DD 088085ae6h, 0ff0f6a70h, 066063bcah, 011010b5ch
+DD 08f659effh, 0f862ae69h, 0616bffd3h, 0166ccf45h
+DD 0a00ae278h, 0d70dd2eeh, 04e048354h, 03903b3c2h
+DD 0a7672661h, 0d06016f7h, 04969474dh, 03e6e77dbh
+DD 0aed16a4ah, 0d9d65adch, 040df0b66h, 037d83bf0h
+DD 0a9bcae53h, 0debb9ec5h, 047b2cf7fh, 030b5ffe9h
+DD 0bdbdf21ch, 0cabac28ah, 053b39330h, 024b4a3a6h
+DD 0bad03605h, 0cdd70693h, 054de5729h, 023d967bfh
+DD 0b3667a2eh, 0c4614ab8h, 05d681b02h, 02a6f2b94h
+DD 0b40bbe37h, 0c30c8ea1h, 05a05df1bh, 02d02ef8dh
+
+end;
+
+{** zlib function implementations *******************************************}
+
+function zcalloc(opaque: Pointer; items, size: Integer): Pointer;
+begin
+ GetMem(result, items * size);
+end;
+
+procedure zcfree(opaque, block: Pointer);
+begin
+ FreeMem(block);
+end;
+
+{** c function implementations **********************************************}
+
+procedure _memset(p: Pointer; b: Byte; count: Integer); cdecl;
+begin
+ FillChar(p^, count, b);
+end;
+
+procedure _memcpy(dest, source: Pointer; count: Integer); cdecl;
+begin
+ Move(source^, dest^, count);
+end;
+
+{** custom zlib routines ****************************************************}
+
+function DeflateInit(var stream: TZStreamRec; level: Integer): Integer;
+begin
+ result := DeflateInit_(stream, level, ZLIB_VERSION, SizeOf(TZStreamRec));
+end;
+
+function DeflateInit2(var stream: TZStreamRec; level, method, windowBits, memLevel, strategy: Integer): Integer;
+begin
+ result := DeflateInit2_(stream, level, method, windowBits, memLevel,
+ strategy, ZLIB_VERSION, SizeOf(TZStreamRec));
+end;
+
+function InflateInit(var stream: TZStreamRec): Integer;
+begin
+ result := InflateInit_(stream, ZLIB_VERSION, SizeOf(TZStreamRec));
+end;
+
+function InflateInit2(var stream: TZStreamRec; windowBits: Integer): Integer;
+begin
+ result := InflateInit2_(stream, windowBits, ZLIB_VERSION, SizeOf(TZStreamRec));
+end;
+
+{****************************************************************************}
+{$IFDEF USE_EXCEPTIONS}
+
+function ZCompressCheck(code: Integer): Integer;
+begin
+ result := code;
+
+ if code < 0 then begin
+ raise Exception.CreateFMT(e_Convert, 'Compression Error %d - %s', [code, _z_errmsg[2 - code]]);
+ end;
+end;
+
+function ZDecompressCheck(code: Integer): Integer;
+begin
+ Result := code;
+
+ if code < 0 then begin
+ raise Exception.CreateFMT(e_Convert, 'Decompression Error %d - %s', [code, _z_errmsg[2 - code]]);
+ end;
+end;
+{$ENDIF}
+
+{****************************************************************************}
+{****************************************************************************}
+{****************************************************************************}
+{**** implementation itself *************************************************}
+{****************************************************************************}
+{****************************************************************************}
+{****************************************************************************}
+
+function ZCompressBuf(const inBuffer: Pointer; inSize: Integer; out outBuffer: Pointer; out outSize: Integer; level: TZCompressionLevel): Integer;
+const
+ delta = 256;
+var
+ zstream : TZStreamRec;
+begin
+ FillChar(zstream, SizeOf(TZStreamRec), 0);
+ Result := Z_OK;
+ outSize := ((inSize + (inSize div 10) + 12) + 255) and not 255;
+ outBuffer := nil;
+ GetMem(outBuffer, outSize);
+ try
+ zstream.next_in := inBuffer;
+ zstream.avail_in := inSize;
+ zstream.next_out := outBuffer;
+ zstream.avail_out := outSize;
+{$IFDEF USE_EXCEPTIONS}
+ ZCompressCheck(DeflateInit(zstream, ZLevels[level]));
+{$ELSE}
+ Result := DeflateInit(zstream, ZLevels[level]);
+ if Result < 0 then Exit;
+{$ENDIF}
+ try
+{$IFDEF USE_EXCEPTIONS}
+ Result := ZCompressCheck(deflate(zstream, Z_FINISH));
+{$ELSE}
+ Result := deflate(zstream, Z_FINISH);
+ if Result < 0 then Exit;
+{$ENDIF}
+ while Result <> Z_STREAM_END do begin
+ Inc(outSize, delta);
+ ReallocMem(outBuffer, outSize);
+
+ zstream.next_out := PChar(Integer(outBuffer) + zstream.total_out);
+ zstream.avail_out := delta;
+{$IFDEF USE_EXCEPTIONS}
+ Result := ZCompressCheck(deflate(zstream, Z_FINISH));
+{$ELSE}
+ Result := deflate(zstream, Z_FINISH);
+ if Result < 0 then Exit;
+{$ENDIF}
+ end; // while
+ finally
+{$IFDEF USE_EXCEPTIONS}
+ ZCompressCheck(deflateEnd(zstream));
+{$ELSE}
+ deflateEnd(zstream);
+{$ENDIF}
+ end;
+
+ ReallocMem(outBuffer, zstream.total_out);
+ outSize := zstream.total_out;
+{$IFDEF USE_EXCEPTIONS}
+ except
+ FreeMem(outBuffer);
+ raise;
+{$ELSE}
+ finally
+ if Result < 0 then FreeMem(outBuffer);
+{$ENDIF}
+ end;
+end;
+
+function ZCompressBuf2(const inBuffer: Pointer; inSize: Integer; out outBuffer: Pointer; out outSize: Integer): Integer;
+const
+ delta = 256;
+var
+ zstream : TZStreamRec;
+begin
+ FillChar(zstream, SizeOf(TZStreamRec), 0);
+
+ outSize := ((inSize + (inSize div 10) + 12) + 255) and not 255;
+ GetMem(outBuffer, outSize);
+ Result := Z_OK;
+ try
+ zstream.next_in := inBuffer;
+ zstream.avail_in := inSize;
+ zstream.next_out := outBuffer;
+ zstream.avail_out := outSize;
+{$IFDEF USE_EXCEPTIONS}
+ ZCompressCheck(DeflateInit2(zstream, 1, 8, -15, 9, 0));
+{$ELSE}
+ Result := DeflateInit2(zstream, 1, 8, -15, 9, 0);
+ if Result < 0 then Exit;
+{$ENDIF}
+
+ try
+{$IFDEF USE_EXCEPTIONS}
+ Result := ZCompressCheck(deflate(zstream, Z_FINISH));
+{$ELSE}
+ Result := deflate(zstream, Z_FINISH);
+ if Result < 0 then Exit;
+{$ENDIF}
+ while Result <> Z_STREAM_END do begin
+ Inc(outSize, delta);
+ ReallocMem(outBuffer, outSize);
+
+ zstream.next_out := PChar(Integer(outBuffer) + zstream.total_out);
+ zstream.avail_out := delta;
+{$IFDEF USE_EXCEPTIONS}
+ Result := ZCompressCheck(deflate(zstream, Z_FINISH));
+{$ELSE}
+ Result := deflate(zstream, Z_FINISH);
+ if Result < 0 then Exit;
+{$ENDIF}
+ end; // while
+ finally
+{$IFDEF USE_EXCEPTIONS}
+ ZCompressCheck(deflateEnd(zstream));
+{$ELSE}
+ deflateEnd(zstream);
+{$ENDIF}
+ end;
+
+ ReallocMem(outBuffer, zstream.total_out);
+ outSize := zstream.total_out;
+{$IFDEF USE_EXCEPTIONS}
+ except
+ FreeMem(outBuffer);
+ raise;
+{$ELSE}
+ finally
+ if Result < 0 then FreeMem(outBuffer);
+{$ENDIF}
+ end;
+end;
+
+function ZDecompressBuf(const inBuffer: Pointer; inSize: Integer; out outBuffer: Pointer; out outSize: Integer; outEstimate: Integer): Integer;
+var
+ zstream : TZStreamRec;
+ delta : Integer;
+begin
+ FillChar(zstream, SizeOf(TZStreamRec), 0);
+
+ delta := (inSize + 255) and not 255;
+
+ if outEstimate = 0 then outSize := delta
+ else outSize := outEstimate;
+ Result := Z_OK;
+ GetMem(outBuffer, outSize);
+ try
+ zstream.next_in := inBuffer;
+ zstream.avail_in := inSize;
+ zstream.next_out := outBuffer;
+ zstream.avail_out := outSize;
+
+{$IFDEF USE_EXCEPTIONS}
+ ZDecompressCheck(InflateInit(zstream));
+{$ELSE}
+ Result := InflateInit(zstream);
+ if Result < 0 then Exit;
+{$ENDIF}
+
+ try
+{$IFDEF USE_EXCEPTIONS}
+ Result := ZDecompressCheck(inflate(zstream, Z_NO_FLUSH));
+{$ELSE}
+ Result := inflate(zstream, Z_NO_FLUSH);
+ if Result < 0 then Exit;
+{$ENDIF}
+ while (Result <> Z_STREAM_END) do begin
+ Inc(outSize, delta);
+ ReallocMem(outBuffer, outSize);
+
+ zstream.next_out := PChar(Integer(outBuffer) + zstream.total_out);
+ zstream.avail_out := delta;
+{$IFDEF USE_EXCEPTIONS}
+ Result := ZDecompressCheck(inflate(zstream, Z_NO_FLUSH));
+{$ELSE}
+ Result := inflate(zstream, Z_NO_FLUSH);
+ if Result < 0 then Exit;
+{$ENDIF}
+ end;
+ finally
+{$IFDEF USE_EXCEPTIONS}
+ ZDecompressCheck(inflateEnd(zstream));
+{$ELSE}
+ inflateEnd(zstream);
+{$ENDIF}
+ end;
+
+ ReallocMem(outBuffer, zstream.total_out);
+ outSize := zstream.total_out;
+
+{$IFDEF USE_EXCEPTIONS}
+ except
+ FreeMem(outBuffer);
+ raise;
+{$ELSE}
+ finally
+ if Result < 0 then FreeMem(outBuffer);
+{$ENDIF}
+ end;
+end;
+
+{** string routines *********************************************************}
+
+function ZCompressStr(const s: string; level: TZCompressionLevel): string;
+var
+ buffer : Pointer;
+ size : Integer;
+begin
+ ZCompressBuf(PChar(s), Length(s), buffer, size, level);
+ SetLength(result, size);
+ Move(buffer^, pointer(result)^, size);
+ FreeMem(buffer);
+end;
+
+procedure ZFastCompressString(var s: string; level: TZCompressionLevel);
+var
+ outBuf : Pointer;
+ outBytes : Integer;
+begin
+ ZCompressBuf(pointer(s), length(s), outBuf, outBytes, level);
+ SetLength(s, outBytes);
+ MoveI32(pointer(outBuf)^, pointer(s)^, outBytes);
+ FreeMem(outBuf);
+end;
+
+procedure ZFastDecompressString(var s: string);
+var
+ outBuf : Pointer;
+ outBytes : Integer;
+begin
+ ZDecompressBuf(pointer(s), Length(s), outBuf, outBytes);
+ SetLength(s, outBytes);
+ MoveI32(pointer(outBuf)^, pointer(s)^, outBytes);
+ FreeMem(outBuf);
+end;
+
+procedure ZSendToBrowser(var s: string);
+var
+ outBuf : Pointer;
+ outBytes : Integer;
+begin
+ ZCompressBuf2(pointer(s), length(s), outBuf, outBytes);
+ SetLength(s, outBytes);
+ Move(pointer(outBuf)^, pointer(s)^, outBytes);
+ FreeMem(outBuf);
+end;
+
+function ZDecompressStr(const s: string): string;
+var
+ buffer : Pointer;
+ size : Integer;
+begin
+ ZDecompressBuf(PChar(s), Length(s), buffer, size);
+ SetLength(result, size);
+ Move(buffer^, pointer(result)^, size);
+ FreeMem(buffer);
+end;
+
+{** stream routines *********************************************************}
+
+function ZCompressStream(inStream, outStream: PStream; level: TZCompressionLevel): Integer;
+const
+ bufferSize = 32768;
+var
+ zstream : TZStreamRec;
+ inBuffer : array[0..bufferSize - 1] of Char;
+ outBuffer : array[0..bufferSize - 1] of Char;
+ inSize : Integer;
+ outSize : Integer;
+begin
+ FillChar(zstream, SizeOf(TZStreamRec), 0);
+{$IFDEF USE_EXCEPTIONS}
+ Result := Z_OK;
+ ZCompressCheck(DeflateInit(zstream, ZLevels[level]));
+{$ELSE}
+ Result := DeflateInit(zstream, ZLevels[level]);
+ if Result < 0 then Exit;
+{$ENDIF}
+ try
+ inSize := inStream.Read(inBuffer, bufferSize);
+
+ while inSize > 0 do begin
+ zstream.next_in := inBuffer;
+ zstream.avail_in := inSize;
+
+ repeat
+ zstream.next_out := outBuffer;
+ zstream.avail_out := bufferSize;
+
+{$IFDEF USE_EXCEPTIONS}
+ ZCompressCheck(deflate(zstream, Z_NO_FLUSH));
+{$ELSE}
+ Result := deflate(zstream, Z_NO_FLUSH);
+ if Result < 0 then Exit;
+{$ENDIF}
+
+ // outSize := zstream.next_out - outBuffer;
+ outSize := bufferSize - zstream.avail_out;
+
+ outStream.Write(outBuffer, outSize);
+ until (zstream.avail_in = 0) and (zstream.avail_out > 0);
+
+ inSize := inStream.Read(inBuffer, bufferSize);
+ end;
+
+ repeat
+ zstream.next_out := outBuffer;
+ zstream.avail_out := bufferSize;
+
+{$IFDEF USE_EXCEPTIONS}
+ Result := ZCompressCheck(deflate(zstream, Z_FINISH));
+{$ELSE}
+ Result := deflate(zstream, Z_FINISH);
+ if Result < 0 then Break;
+{$ENDIF}
+
+ // outSize := zstream.next_out - outBuffer;
+ outSize := bufferSize - zstream.avail_out;
+
+ outStream.Write(outBuffer, outSize);
+ until (Result = Z_STREAM_END) and (zstream.avail_out > 0);
+ finally
+{$IFDEF USE_EXCEPTIONS}
+ ZCompressCheck(deflateEnd(zstream));
+{$ELSE}
+ deflateEnd(zstream);
+{$ENDIF}
+ end;
+end;
+
+function ZDecompressStream(inStream, outStream: PStream): Integer;
+const
+ bufferSize = 32768;
+var
+ zstream : TZStreamRec;
+ inBuffer : array[0..bufferSize - 1] of Char;
+ outBuffer : array[0..bufferSize - 1] of Char;
+ inSize : Integer;
+ outSize : Integer;
+begin
+ FillChar(zstream, SizeOf(TZStreamRec), 0);
+
+{$IFDEF USE_EXCEPTIONS}
+ Result := ZCompressCheck(InflateInit(zstream));
+{$ELSE}
+ Result := InflateInit(zstream);
+ if Result < 0 then Exit;
+{$ENDIF}
+ try
+ inSize := inStream.Read(inBuffer, bufferSize);
+
+ while inSize > 0 do begin
+ zstream.next_in := inBuffer;
+ zstream.avail_in := inSize;
+
+ repeat
+ zstream.next_out := outBuffer;
+ zstream.avail_out := bufferSize;
+
+{$IFDEF USE_EXCEPTIONS}
+ ZCompressCheck(inflate(zstream, Z_NO_FLUSH));
+{$ELSE}
+ Result := inflate(zstream, Z_NO_FLUSH);
+ if Result < 0 then Exit;
+{$ENDIF}
+
+ // outSize := zstream.next_out - outBuffer;
+ outSize := bufferSize - zstream.avail_out;
+
+ outStream.Write(outBuffer, outSize);
+ until (zstream.avail_in = 0) and (zstream.avail_out > 0);
+
+ inSize := inStream.Read(inBuffer, bufferSize);
+ end;
+
+ repeat
+ zstream.next_out := outBuffer;
+ zstream.avail_out := bufferSize;
+
+{$IFDEF USE_EXCEPTIONS}
+ Result := ZCompressCheck(inflate(zstream, Z_FINISH));
+{$ELSE}
+ Result := inflate(zstream, Z_FINISH);
+ if Result < 0 then Break;
+{$ENDIF}
+
+ // outSize := zstream.next_out - outBuffer;
+ outSize := bufferSize - zstream.avail_out;
+
+ outStream.Write(outBuffer, outSize);
+ until (Result = Z_STREAM_END) and (zstream.avail_out > 0);
+ finally
+{$IFDEF USE_EXCEPTIONS}
+ ZCompressCheck(inflateEnd(zstream));
+{$ELSE}
+ inflateEnd(zstream);
+{$ENDIF}
+ end;
+end;
+
+{** gzip Stream routines ******************************************************}
+const
+ UnixDateDelta = 25569;
+
+function DateTimeToUnix(ConvDate: TDateTime): Longint;
+begin
+ //example: DateTimeToUnix(now);
+ Result := Round((ConvDate - UnixDateDelta) * 86400);
+end;
+
+function UnixToDateTime(USec: Longint): TDateTime;
+begin
+ //Example: UnixToDateTime(1003187418);
+ Result := (Usec / 86400) + UnixDateDelta;
+end;
+
+function gZipCompressStream(inStream, outStream: PStream; var gzHdr: TgzipHeader; level: TZCompressionLevel = zcDefault; strategy: TZCompressionStrategy = zcsDefault): Integer;
+var
+ rSize,
+ wSize,
+ zResult : LongInt;
+ done : Boolean;
+ iBuffer,
+ oBuffer : PChar; //Array [0..gzBufferSize-1] of Char;
+ fCrc : Cardinal;
+ zStream : TZStreamRec;
+ stamp : Integer;
+
+begin
+ iBuffer := nil;
+ oBuffer := nil;
+ Result := Z_MEM_ERROR;
+ try
+ GetMem(iBuffer, gzBufferSize);
+ GetMem(oBuffer, gzBufferSize);
+
+ fCrc := 0;
+ FillChar(zStream, SizeOf(zStream), 0);
+
+{$IFDEF USE_EXCEPTIONS}
+ ZCompressCheck(DeflateInit2(zStream, ZLevels[level], Z_DEFLATED, -MAX_WBITS,
+ DEF_MEM_LEVEL, ZStrategy[strategy]));
+{$ELSE}
+ Result := DeflateInit2(zStream, ZLevels[level], Z_DEFLATED, -MAX_WBITS,
+ DEF_MEM_LEVEL, ZStrategy[strategy]);
+ if Result < 0 then Exit;
+{$ENDIF}
+ { windowBits is passed < 0 to suppress zlib header }
+ oBuffer[0] := Char(gz_magic[0]);
+ oBuffer[1] := Char(gz_magic[1]); // gz Magic
+ oBuffer[2] := #08; // gz Compression method
+ oBuffer[3] := #0;
+ // set mtime
+ {
+ Inc(gzHdr.TimeStamp,gzTimeStampCorrection);
+ oBuffer[4]:=Lo(gzHdr.TimeStamp and $FFFF); oBuffer[5]:=Hi(gzHdr.TimeStamp and $FFFF);
+ oBuffer[6]:=Lo(gzHdr.TimeStamp shr 16); oBuffer[7]:=Hi(gzHdr.TimeStamp shr 16);
+ Dec(gzHdr.TimeStamp,gzTimeStampCorrection);
+ }
+ stamp := DateTimeToUnix(gzHdr.FileTime);
+ oBuffer[4] := Char(Lo(stamp and $FFFF));
+ oBuffer[5] := Char(Hi(stamp and $FFFF));
+ oBuffer[6] := Char(Lo(stamp shr 16));
+ oBuffer[7] := Char(Hi(stamp shr 16));
+
+ // xfl, os code sets to 0
+ oBuffer[8] := #0;
+ oBuffer[9] := #0;
+
+ if gzHdr.FileName <> '' then begin
+ oBuffer[3] := Char(Byte(oBuffer[3]) or GZF_ORIG_NAME);
+ end;
+ if gzHdr.Comment <> '' then begin
+ oBuffer[3] := Char(Byte(oBuffer[3]) or GZF_COMMENT);
+ end;
+ if gzHdr.Extra <> '' then begin
+ oBuffer[3] := Char(Byte(oBuffer[3]) or GZF_EXTRA_FIELD);
+ end;
+ wSize := outStream.Write(oBuffer^, 10);
+{$IFDEF USE_EXCEPTIONS}
+ if wSize <> 10 then ZCompressCheck(Z_WRITE_ERROR);
+{$ELSE}
+ if wSize <> 10 then begin
+ Result := Z_WRITE_ERROR;
+ Exit;
+ end;
+{$ENDIF}
+
+ // extra
+ if (byte(oBuffer[3]) and GZF_EXTRA_FIELD) <> 0 then begin
+ rSize := Length(gzHdr.Extra);
+ Move(gzHdr.Extra[1], iBuffer^, rSize);
+ iBuffer[rSize] := #0;
+ Inc(rSize);
+ wSize := outStream.Write(iBuffer^, rSize);
+{$IFDEF USE_EXCEPTIONS}
+ if wSize <> rSize then ZCompressCheck(Z_WRITE_ERROR);
+{$ELSE}
+ if wSize <> rSize then begin
+ Result := Z_WRITE_ERROR;
+ Exit;
+ end;
+{$ENDIF}
+ end;
+ // filename
+ if (byte(oBuffer[3]) and GZF_ORIG_NAME) <> 0 then begin
+ rSize := Length(gzHdr.FileName);
+ Move(gzHdr.FileName[1], iBuffer^, rSize);
+ iBuffer[rSize] := #0;
+ Inc(rSize);
+ wSize := outStream.Write(iBuffer^, rSize);
+{$IFDEF USE_EXCEPTIONS}
+ if wSize <> rSize then ZCompressCheck(Z_WRITE_ERROR);
+{$ELSE}
+ if wSize <> rSize then begin
+ Result := Z_WRITE_ERROR;
+ Exit;
+ end;
+{$ENDIF}
+ end;
+ // comment
+ if (byte(oBuffer[3]) and GZF_COMMENT) <> 0 then begin
+ rSize := Length(gzHdr.Comment);
+ Move(gzHdr.Comment[1], iBuffer^, rSize);
+ iBuffer[rSize] := #0;
+ Inc(rSize);
+ wSize := outStream.Write(iBuffer^, rSize);
+{$IFDEF USE_EXCEPTIONS}
+ if wSize <> rSize then ZCompressCheck(Z_WRITE_ERROR);
+{$ELSE}
+ if wSize <> rSize then begin
+ Result := Z_WRITE_ERROR;
+ Exit;
+ end;
+{$ENDIF}
+ end;
+ // hcrc
+
+ rSize := inStream.Read(iBuffer^, gzBufferSize);
+ zStream.next_out := PChar(oBuffer);
+ zStream.avail_out := gzBufferSize;
+ repeat
+ //DoProgressEvent;
+ zStream.next_in := PChar(iBuffer);
+ zStream.avail_in := rSize;
+ while (zStream.avail_in <> 0) do begin
+ if (zStream.avail_out = 0) then begin
+ zStream.next_out := PChar(oBuffer);
+ wSize := outStream.Write(oBuffer^, gzBufferSize);
+ if (wSize <> gzBufferSize) then begin
+{$IFDEF USE_EXCEPTIONS}
+ ZCompressCheck(Z_WRITE_ERROR);
+{$ELSE}
+ Result := Z_WRITE_ERROR;
+ Exit;
+{$ENDIF}
+ end;
+ zStream.avail_out := gzBufferSize;
+ end;
+{$IFDEF USE_EXCEPTIONS}
+ ZCompressCheck(deflate(zStream, Z_NO_FLUSH));
+{$ELSE}
+ Result := deflate(zStream, Z_NO_FLUSH);
+ if Result < 0 then Exit;
+{$ENDIF}
+ end; // while
+ fCrc := Crc32(fCrc, PChar(iBuffer), rSize);
+ rSize := inStream.Read(iBuffer^, gzBufferSize);
+ until rSize = 0;
+ { flush buffers }
+ zStream.avail_in := 0; { should be zero already anyway }
+ done := False;
+
+ repeat
+ rSize := gzBufferSize - zStream.avail_out;
+ if (rSize <> 0) then begin
+ wSize := outStream.Write(oBuffer^, rSize);
+{$IFDEF USE_EXCEPTIONS}
+ if (wSize <> rSize) then ZCompressCheck(Z_WRITE_ERROR);
+{$ELSE}
+ if (wSize <> rSize) then begin
+ Result := Z_WRITE_ERROR;
+ Exit;
+ end;
+{$ENDIF}
+ zStream.next_out := PChar(oBuffer);
+ zStream.avail_out := gzBufferSize;
+ end;
+ if done then Break;
+ zResult := deflate(zStream, Z_FINISH);
+ if (rSize = 0) and (zResult = Z_BUF_ERROR) then
+{$IFDEF USE_EXCEPTIONS}
+ else ZCompressCheck(zResult);
+{$ELSE}
+ else begin
+ Result := zResult;
+ if Result < 0 then Exit;
+ end;
+{$ENDIF}
+ { deflate has finished flushing only when it hasn't used up
+ all the available space in the output buffer: }
+ done := (zStream.avail_out <> 0) or (zResult = Z_STREAM_END);
+ until False;
+ wSize := outStream.Write(fCrc, 4);
+{$IFDEF USE_EXCEPTIONS}
+ if wSize <> 4 then ZCompressCheck(Z_WRITE_ERROR);
+{$ELSE}
+ if wSize <> 4 then begin
+ Result := Z_WRITE_ERROR;
+ Exit;
+ end;
+{$ENDIF}
+ rSize := inStream.Size;
+ wSize := outStream.Write(rSize, 4);
+{$IFDEF USE_EXCEPTIONS}
+ if wSize <> 4 then ZCompressCheck(Z_WRITE_ERROR);
+{$ELSE}
+ if wSize <> 4 then begin
+ Result := Z_WRITE_ERROR;
+ Exit;
+ end;
+{$ENDIF}
+ Result := Z_OK;
+ finally
+ deflateEnd(zStream);
+ if Assigned(iBuffer) then FreeMem(iBuffer);
+ if Assigned(oBuffer) then FreeMem(oBuffer);
+ end;
+end;
+
+function gZipCompressStream(inStream, outStream: PStream; level: TZCompressionLevel = zcDefault; strategy: TZCompressionStrategy = zcsDefault): Integer; overload;
+var
+ gzHdr : TgzipHeader;
+begin
+ FillChar(gzHdr, SizeOf(gzHdr), 0);
+ gzHdr.FileTime := Date;
+ Result := gZipCompressStream(inStream, outStream, gzHdr, level, strategy);
+end;
+
+function gZipDecompressStreamHeader(inStream: PStream; var gzHdr: TgzipHeader): Integer;
+var
+ i, c, flg : LongInt;
+ fEOF : Boolean;
+
+ function gz_getbyte: Integer;
+ var
+ b, c : Integer;
+ begin
+ b := 0;
+ c := inStream.Read(b, 1);
+ if c = 0 then begin
+ fEOF := True;
+ Result := Z_EOF;
+ end
+ else Result := b;
+ end;
+
+ function gz_getlong: Integer;
+ var
+ b, c : Integer;
+ begin
+ b := 0;
+ c := inStream.Read(b, 4);
+ if c < 4 then begin
+ fEOF := True;
+ Result := Z_EOF;
+ end
+ else Result := b;
+ end;
+begin
+ // fTransparent := False;
+ Result := Z_OK;
+ fEOF := False;
+ gzHdr.FileName := '';
+ gzHdr.Comment := '';
+ gzHdr.Extra := '';
+ try
+ for i := 0 to 1 do begin
+ flg := gz_getbyte;
+ if (flg <> gz_magic[i]) then begin
+ fEOF := True;
+ exit;
+ end;
+ end;
+ c := gz_getbyte; // method
+ flg := gz_getbyte; // flags
+ if (c <> Z_DEFLATED) or ((flg and GZF_RESERVED) <> 0) then begin
+ fEOF := True;
+ exit;
+ end;
+
+ gzHdr.FileTime := UnixToDateTime(gz_getLong);
+ gz_getbyte;
+ gz_getbyte; { skip xflags and OS code }
+
+ if (flg and GZF_EXTRA_FIELD) <> 0 then begin // skip extra fields
+ i := gz_getbyte + (gz_getbyte shl 8); // length of extra
+ SetLength(gzHdr.Extra, i);
+ c := inStream.Read(gzHdr.Extra, i);
+ if c <> i then begin
+ fEOF := True;
+ Exit;
+ end;
+ end;
+ if (flg and GZF_ORIG_NAME) <> 0 then begin // extract File Name
+ repeat
+ c := gz_getbyte;
+ if (c <> 0) and (c <> Z_EOF) then gzHdr.FileName := gzHdr.FileName + char(c);
+ until (c = 0) or (c = Z_EOF);
+ end;
+ if (flg and GZF_COMMENT) <> 0 then begin // extract Comment
+ repeat
+ c := gz_getbyte;
+ if (c <> 0) and (c <> Z_EOF) then gzHdr.Comment := gzHdr.Comment + char(c);
+ until (c = 0) or (c = Z_EOF);
+ end;
+ if (flg and GZF_HEAD_CRC) <> 0 then begin // skip head crc
+ gz_getbyte;
+ gz_getbyte;
+ end;
+ finally
+ if fEOF then Result := Z_DATA_ERROR
+ else Result := Z_OK;
+ end;
+end;
+
+function gZipDecompressStreamBody(inStream, outStream: PStream): Integer;
+var
+ iBuffer,
+ oBuffer : PChar; //Array [0..gzBufferSize-1] of Char;
+ fCrc : Cardinal;
+ zStream : TZStreamRec;
+ rSize,
+ wSize : LongInt;
+ startCRC : PChar;
+ fileCRC,
+ fileSize : Cardinal;
+ fEOF : Boolean;
+
+ function gz_getbyte: Integer;
+ begin
+ // if (eof) then result:=-1;
+ if (zStream.avail_in = 0) then begin
+ zStream.avail_in := inStream.Read(iBuffer^, gzBufferSize);
+ if (zStream.avail_in = 0) then begin
+ Result := Z_EOF;
+ fEOF := True;
+ exit;
+ end
+ else zStream.next_in := PChar(iBuffer);
+ end;
+ Dec(zStream.avail_in);
+ Result := Byte(zStream.next_in[0]);
+ Inc(zStream.next_in);
+ end;
+
+ function gz_getLong: Cardinal;
+ var
+ c : Integer;
+ begin
+ c := gz_getbyte;
+ c := c + gz_getbyte shl 8;
+ c := c + gz_getbyte shl 16;
+ c := c + gz_getbyte shl 24;
+ Result := Cardinal(c);
+ end;
+begin
+ iBuffer := nil;
+ oBuffer := nil;
+ Result := Z_MEM_ERROR;
+ try
+ GetMem(iBuffer, gzBufferSize);
+ GetMem(oBuffer, gzBufferSize);
+ fEOF := False;
+ {Check the gzip header of a gz_stream opened for reading.
+ Set the stream mode to transparent if the gzip magic header is not present.}
+
+ FillChar(zStream, SizeOf(zStream), 0);
+ zStream.next_in := pChar(iBuffer);
+ fCRC := 0;
+ { windowBits is passed < 0 to tell that there is no zlib header }
+{$IFDEF USE_EXCEPTIONS}
+ ZDecompressCheck(InflateInit2(zStream, -MAX_WBITS));
+{$ELSE}
+ Result := InflateInit2(zStream, -MAX_WBITS);
+ if Result < 0 then Exit;
+{$ENDIF}
+ while not fEOF do begin
+ // gzread()
+ // DoProgressEvent;
+ startCRC := PChar(oBuffer);
+ zStream.next_out := PChar(oBuffer);
+ zStream.avail_out := gzBufferSize;
+// rSize := 0;
+ Result := Z_OK;
+ while zStream.avail_out <> 0 do begin
+ // not transparent
+ if (zStream.avail_in = 0) and (not fEOF) then begin
+ zStream.avail_in := inStream.Read(iBuffer^, gzBufferSize);
+ if (zStream.avail_in = 0) then fEOF := True;
+ zStream.next_in := PChar(iBuffer);
+ end;
+ Result := inflate(zStream, Z_NO_FLUSH);
+ if (Result = Z_STREAM_END) then begin
+ { Check CRC and original size }
+ fCrc := crc32(fCrc, PChar(StartCRC), (zStream.next_out - startCRC));
+ startCRC := zStream.next_out;
+
+ fileCRC := gz_getLong;
+ fileSize := gz_getLong;
+ if (fCrc <> fileCRC) then
+{$IFDEF USE_EXCEPTIONS}
+ ZDecompressCheck(Z_CRC_ERROR)
+{$ELSE}
+ Result := Z_CRC_ERROR
+{$ENDIF}
+ else
+ if (Cardinal(zStream.total_out) <> fileSize) then
+{$IFDEF USE_EXCEPTIONS}
+ ZDecompressCheck(Z_SIZE_ERROR)
+{$ELSE}
+ Result := Z_SIZE_ERROR
+{$ENDIF}
+ else begin
+ if zStream.avail_in > 0 then inStream.Seek(-zStream.avail_in, spCurrent);
+ fEOF := True;
+ end;
+ end;
+ if (Result <> Z_OK) or (fEOF) then break;
+ end; // while zStream.avail_out<>0
+ // end of gzread()
+
+{$IFDEF USE_EXCEPTIONS}
+ ZDecompressCheck(Result);
+{$ELSE}
+ if Result < 0 then Exit;
+{$ENDIF}
+ fCrc := crc32(fCrc, PChar(oBuffer), (zStream.next_out - startCRC));
+ rSize := gzBufferSize - zStream.avail_out;
+
+{$IFDEF USE_EXCEPTIONS}
+ if rSize < 0 then ZDecompressCheck(rSize);
+{$ELSE}
+ if rSize <= 0 then break;
+{$ENDIF}
+ wSize := outStream.Write(oBuffer^, rSize);
+{$IFDEF USE_EXCEPTIONS}
+ if (rSize <> wSize) then ZDecompressCheck(Z_WRITE_ERROR);
+{$ELSE}
+ if (rSize <> wSize) then begin
+ Result := Z_WRITE_ERROR;
+ Exit;
+ end;
+{$ENDIF}
+ end;
+ if Result = Z_STREAM_END then Result := Z_OK;
+ finally
+ inflateEnd(zStream);
+ if Assigned(iBuffer) then FreeMem(iBuffer);
+ if Assigned(oBuffer) then FreeMem(oBuffer);
+ end;
+end;
+
+function gZipDecompressStream(inStream, outStream: PStream; var gzHdr: TgzipHeader): Integer;
+begin
+ Result := gZipDecompressStreamHeader(inStream, gzHdr);
+ if (Result >= 0) then
+ Result := gZipDecompressStreamBody(inStream, outStream);
+end;
+
+function gZipDecompressString(const S: String): String;
+var
+ Rslt: Integer;
+ gzHdr: TgzipHeader;
+ inStream: PStream;
+ outStream: PStream;
+begin
+ Result := '';
+ inStream := NewExMemoryStream(@S[1], Length(S));
+ Rslt := gZipDecompressStreamHeader(inStream, gzHdr);
+ if (Rslt >= 0) then begin
+ outStream := NewMemoryStream;
+ Rslt := gZipDecompressStreamBody(inStream, outStream);
+ if (Rslt >= 0) then begin
+ outStream.Position := 0;
+ Result := outStream.ReadStrLen(outStream.Size);
+ end;
+ outStream.Free;
+ end;
+ inStream.Free;
+end;
+
+{****************************************************************************}
+{** BZip implementation *****************************************************}
+{****************************************************************************}
+{$IFDEF USE_EXCEPTIONS}
+
+function CCheck(code: Integer): Integer;
+begin
+ Result := code;
+ if code < 0 then
+ raise Exception.CreateFMT(e_Convert, 'Compression Error %d - %s', [code, BZ_Error_Msg[-code]]);
+end;
+
+function DCheck(code: Integer): Integer;
+begin
+ Result := code;
+ if code < 0 then
+ raise Exception.CreateFMT(e_Convert, 'Decompression Error %d - %s', [code, BZ_Error_Msg[-code]]);
+end;
+{$ENDIF}
+
+function BZCompressBuf(const InBuf: Pointer; InBytes: Integer; out OutBuf: Pointer; out OutBytes: Integer): Integer;
+var
+ strm : TBZStreamRec;
+ P : Pointer;
+begin
+ FillChar(strm, sizeof(strm), 0);
+ strm.bzalloc := bzip2AllocMem;
+ strm.bzfree := bzip2FreeMem;
+ OutBytes := ((InBytes + (InBytes div 10) + 12) + 255) and not 255;
+ GetMem(OutBuf, OutBytes);
+ Result := BZ_OK;
+ try
+ strm.next_in := InBuf;
+ strm.avail_in := InBytes;
+ strm.next_out := OutBuf;
+ strm.avail_out := OutBytes;
+{$IFDEF USE_EXCEPTIONS}
+ CCheck(BZ2_bzCompressInit(strm, 9, 0, 0));
+{$ELSE}
+ Result := BZ2_bzCompressInit(strm, 9, 0, 0);
+ if Result < 0 then Exit;
+{$ENDIF}
+ try
+{$IFDEF USE_EXCEPTIONS}
+ Result := CCheck(BZ2_bzCompress(strm, BZ_FINISH));
+{$ELSE}
+ Result := BZ2_bzCompress(strm, BZ_FINISH);
+ if Result < 0 then Exit;
+{$ENDIF}
+ while Result <> BZ_STREAM_END do begin
+ P := OutBuf;
+ Inc(OutBytes, 256);
+ ReallocMem(OutBuf, OutBytes);
+ strm.next_out := PChar(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P)));
+ strm.avail_out := 256;
+{$IFDEF USE_EXCEPTIONS}
+ Result := CCheck(BZ2_bzCompress(strm, BZ_FINISH));
+{$ELSE}
+ Result := BZ2_bzCompress(strm, BZ_FINISH);
+ if Result < 0 then Exit;
+{$ENDIF}
+ end;
+ finally
+{$IFDEF USE_EXCEPTIONS}
+ CCheck(BZ2_bzCompressEnd(strm));
+{$ELSE}
+ BZ2_bzCompressEnd(strm);
+{$ENDIF}
+ end;
+ ReallocMem(OutBuf, strm.total_out_lo32);
+ OutBytes := strm.total_out_lo32;
+{$IFDEF USE_EXCEPTIONS}
+ except
+ FreeMem(outBuf);
+ raise;
+{$ELSE}
+ finally
+ if Result < 0 then FreeMem(outBuf);
+{$ENDIF}
+ end;
+end;
+
+function BZDecompressBuf(const InBuf: Pointer; InBytes: Integer; OutEstimate: Integer; out OutBuf: Pointer; out OutBytes: Integer): Integer;
+var
+ strm : TBZStreamRec;
+ P : Pointer;
+ BufInc : Integer;
+begin
+ FillChar(strm, sizeof(strm), 0);
+ strm.bzalloc := bzip2AllocMem;
+ strm.bzfree := bzip2FreeMem;
+ BufInc := (InBytes + 255) and not 255;
+ Result := BZ_OK;
+ if OutEstimate = 0 then
+ OutBytes := BufInc
+ else
+ OutBytes := OutEstimate;
+ GetMem(OutBuf, OutBytes);
+ try
+ strm.next_in := InBuf;
+ strm.avail_in := InBytes;
+ strm.next_out := OutBuf;
+ strm.avail_out := OutBytes;
+{$IFDEF USE_EXCEPTIONS}
+ DCheck(BZ2_bzDecompressInit(strm, 0, 0));
+{$ELSE}
+ Result := BZ2_bzDecompressInit(strm, 0, 0);
+ if Result < 0 then Exit;
+{$ENDIF}
+ try
+{$IFDEF USE_EXCEPTIONS}
+ Result := DCheck(BZ2_bzDecompress(strm));
+{$ELSE}
+ Result := BZ2_bzDecompress(strm);
+ if Result < 0 then Exit;
+{$ENDIF}
+ while Result <> BZ_STREAM_END do begin
+ P := OutBuf;
+ Inc(OutBytes, BufInc);
+ ReallocMem(OutBuf, OutBytes);
+ strm.next_out := PChar(Integer(OutBuf) + (Integer(strm.next_out) - Integer(P)));
+ strm.avail_out := BufInc;
+{$IFDEF USE_EXCEPTIONS}
+ Result := DCheck(BZ2_bzDecompress(strm));
+{$ELSE}
+ Result := BZ2_bzDecompress(strm);
+ if Result < 0 then Exit;
+{$ENDIF}
+ end;
+ finally
+{$IFDEF USE_EXCEPTIONS}
+ DCheck(BZ2_bzDecompressEnd(strm));
+{$ELSE}
+ BZ2_bzDecompressEnd(strm);
+{$ENDIF}
+ end;
+ ReallocMem(OutBuf, strm.total_out_lo32);
+ OutBytes := strm.total_out_lo32;
+ except
+ FreeMem(OutBuf);
+ raise
+ end;
+end;
+
+function BZCompressStream(inStream, outStream: PStream; BlockSize100k: TBlockSize100k = 5): Integer;
+var
+ FBZRec : TBZStreamRec;
+ iBuffer,
+ oBuffer : PChar;
+ wSize,
+ rSize : Integer;
+begin
+ Result := BZ_MEM_ERROR;
+ iBuffer := nil;
+ oBuffer := nil;
+ FillChar(FBZRec, SizeOf(FBZRec), 0);
+ // FBZRec.bzalloc := bzip2AllocMem;
+ // FBZRec.bzfree := bzip2FreeMem;
+ try
+ GetMem(iBuffer, bzBufferSize);
+ GetMem(oBuffer, bzBufferSize);
+{$IFDEF USE_EXCEPTIONS}
+ CCheck(BZ2_bzCompressInit(FBZRec, BlockSize100k, 0, 0));
+{$ELSE}
+ Result := BZ2_bzCompressInit(FBZRec, BlockSize100k, 0, 0);
+ if Result < 0 then Exit;
+{$ENDIF}
+ FBZRec.next_out := PChar(oBuffer);
+ FBZRec.avail_out := bzBufferSize;
+ rSize := inStream.Read(iBuffer^, bzBufferSize);
+ repeat
+ //DoProgressEvent;
+ FBZRec.next_in := PChar(iBuffer);
+ FBZRec.avail_in := rSize;
+ while (FBZRec.avail_in > 0) do begin
+ if (FBZRec.avail_out = 0) then begin
+ wSize := outStream.Write(oBuffer^, bzBufferSize);
+ if (wSize <> bzBufferSize) then begin
+{$IFDEF USE_EXCEPTIONS}
+ CCheck(BZ_IO_ERROR);
+{$ELSE}
+ Result := BZ_IO_ERROR;
+ Exit;
+{$ENDIF}
+ end;
+ FBZRec.next_out := PChar(oBuffer);
+ FBZRec.avail_out := bzBufferSize;
+ end;
+{$IFDEF USE_EXCEPTIONS}
+ CCheck(BZ2_bzCompress(FBZRec, BZ_RUN));
+{$ELSE}
+ Result := BZ2_bzCompress(FBZRec, BZ_RUN);
+ if Result < 0 then Exit;
+{$ENDIF}
+ end; // while
+ rSize := inStream.Read(iBuffer^, bzBufferSize);
+ until rSize = 0;
+ { flush buffers }
+ FBZRec.avail_in := 0; { should be zero already anyway }
+ repeat
+{$IFDEF USE_EXCEPTIONS}
+ Result := CCheck(BZ2_bzCompress(FBZRec, BZ_FINISH));
+{$ELSE}
+ Result := BZ2_bzCompress(FBZRec, BZ_FINISH);
+ if Result < 0 then Break;
+{$ENDIF}
+ rSize := bzBufferSize - FBZRec.avail_out;
+ wSize := outStream.Write(oBuffer^, rSize);
+{$IFDEF USE_EXCEPTIONS}
+ if (wSize <> rSize) then CCheck(BZ_IO_ERROR);
+{$ELSE}
+ if (wSize <> rSize) then begin
+ Result := BZ_IO_ERROR;
+ break;
+ end;
+{$ENDIF}
+ FBZRec.next_out := PChar(oBuffer);
+ FBZRec.avail_out := bzBufferSize;
+ until Result = BZ_STREAM_END;
+ finally
+ if Result = BZ_STREAM_END then Result := BZ_OK;
+ BZ2_bzCompressEnd(FBZRec);
+ if Assigned(iBuffer) then FreeMem(iBuffer);
+ if Assigned(oBuffer) then FreeMem(oBuffer);
+ end;
+
+end;
+
+function BZDecompressStream(inStream, outStream: PStream): Integer;
+var
+ FBZRec : TBZStreamRec;
+ iBuffer,
+ oBuffer : PChar;
+ wSize,
+ rSize : Integer;
+begin
+ Result := BZ_MEM_ERROR;
+ iBuffer := nil;
+ oBuffer := nil;
+ FillChar(FBZRec, SizeOf(FBZRec), 0);
+ try
+ GetMem(iBuffer, bzBufferSize);
+ GetMem(oBuffer, bzBufferSize);
+{$IFDEF USE_EXCEPTIONS}
+ DCheck(BZ2_bzDecompressInit(FBZRec, 0, 0));
+{$ELSE}
+ Result := BZ2_bzDecompressInit(FBZRec, 0, 0);
+ if Result < 0 then Exit;
+{$ENDIF}
+ rSize := inStream.Read(iBuffer^, bzBufferSize);
+ FBZRec.next_in := PChar(iBuffer);
+ FBZRec.avail_in := rSize;
+ repeat
+ FBZRec.next_out := PChar(oBuffer);
+ FBZRec.avail_out := bzBufferSize;
+ Result := 0;
+ while (FBZRec.avail_out > 0) and (Result <> BZ_STREAM_END) do begin
+{$IFDEF USE_EXCEPTIONS}
+ Result := CCheck(BZ2_bzDecompress(FBZRec));
+{$ELSE}
+ Result := BZ2_bzDecompress(FBZRec);
+ if Result < 0 then Break;
+{$ENDIF}
+ if FBZRec.avail_in = 0 then begin
+ rSize := inStream.Read(iBuffer^, bzBufferSize);
+ FBZRec.next_in := PChar(iBuffer);
+ FBZRec.avail_in := rSize;
+ end;
+ end;
+ FBZRec.avail_out := bzBufferSize - FBZRec.avail_out;
+ wSize := outStream.Write(oBuffer^, FBZRec.avail_out);
+{$IFDEF USE_EXCEPTIONS}
+ if FBZRec.avail_out <> wSize then CCheck(BZ_IO_ERROR);
+{$ELSE}
+ if FBZRec.avail_out <> wSize then Result := BZ_IO_ERROR;
+{$ENDIF}
+ until (rSize = 0) or (Result < 0);
+ finally
+ if Result = BZ_STREAM_END then Result := BZ_OK;
+ BZ2_bzDecompressEnd(FBZRec);
+ if Assigned(iBuffer) then FreeMem(iBuffer);
+ if Assigned(oBuffer) then FreeMem(oBuffer);
+ end;
+end;
+
+end.
diff --git a/plugins/Libs/MCKfakeClasses.inc b/plugins/Libs/MCKfakeClasses.inc
new file mode 100644
index 0000000000..5483d42442
--- /dev/null
+++ b/plugins/Libs/MCKfakeClasses.inc
@@ -0,0 +1,79 @@
+{
+ KOL MCK (C) 2000 by Vladimir Kladov
+
+ MCKfakeClasses.inc
+
+ This file redefines mirror class types to PControl / PObj
+ to use it by Delphi compiler - while compiling mirror KOL
+ project. At design time these definitions are not visible
+ for Delphi IDE because of conditional compiling directives.
+}
+
+{$I KOLDEF.INC}
+{$IFNDEF FPC}
+{$IFDEF _D7orHigher}
+ {$WARN UNSAFE_TYPE OFF} // Too many such warnings in Delphi7
+ {$WARN UNSAFE_CODE OFF}
+ {$WARN UNSAFE_CAST OFF}
+{$ENDIF}
+{$ENDIF}
+
+ TKOLProject = Pointer;
+ TKOLApplet = Pointer;
+ TKOLForm = Pointer;
+ TKOLDataModule = Pointer;
+ TKOLFrame = Pointer;
+ TKOLMDIClient = PControl;
+ TKOLMDIChild = Pointer;
+ TKOLService = Pointer;
+ TBringFront = Pointer;
+
+ TKOLButton = PControl;
+ TKOLLabel = PControl;
+ TKOLLabelEffect = PControl;
+ TKOLPanel = PControl;
+ TKOLSplitter = PControl;
+ TKOLGradientPanel = PControl;
+ TKOLBitBtn = PControl;
+ TKOLGroupBox = PControl;
+ TKOLCheckBox = PControl;
+ TKOLRadioBox = PControl;
+ TKOLEditBox = PControl;
+ TKOLMemo = PControl;
+ TKOLListBox = PControl;
+ TKOLComboBox = PControl;
+ TKOLPaintBox = PControl;
+ TKOLImageShow = PControl;
+
+ TKOLRichEdit = PControl;
+ TKOLProgressBar = PControl;
+ TKOLListView = PControl;
+ TKOLTreeView = PControl;
+ TKOLToolbar = PControl;
+ TKOLTabControl = PControl;
+ TKOLTabPage = PControl;
+ TTabPage = PControl;
+ TKOLScrollBox = PControl;
+ TKOLDateTimePicker = PControl;
+
+ TKOLTimer = PTimer;
+ TKOLThread = PThread;
+ TKOLImageList = PImageList;
+ TKOLMainMenu = PMenu;
+ TKOLPopupMenu = PMenu;
+ TKOLOpenSaveDialog = POpenSaveDialog;
+ //TKOLOpenDirDialog = POpenDirDialog;
+ TKOLTrayIcon = PTrayIcon;
+ TKOLColorDialog = PColorDialog;
+ //TKOLActionList = PActionList;
+ //TKOLAction = PAction;
+ TKOLScrollBar = PControl;
+
+{$IFNDEF FPC}
+{$IFDEF _D7orHigher}
+ //{$WARN UNSAFE_TYPE ON} // Too many such warnings in Delphi7
+ //{$WARN UNSAFE_CODE ON}
+ //{$WARN UNSAFE_CAST ON}
+{$ENDIF}
+{$ENDIF}
+
diff --git a/plugins/Libs/MCKfakeClasses200x.inc b/plugins/Libs/MCKfakeClasses200x.inc
new file mode 100644
index 0000000000..53aed52a68
--- /dev/null
+++ b/plugins/Libs/MCKfakeClasses200x.inc
@@ -0,0 +1,51 @@
+{$IFNDEF INPACKAGE}
+type
+ TKOLProject = Pointer;
+ TKOLApplet = Pointer;
+ TKOLForm = Pointer;
+ TKOLDataModule = Pointer;
+ TKOLFrame = Pointer;
+ TKOLMDIClient = PControl;
+ TKOLMDIChild = Pointer;
+ TKOLService = Pointer;
+
+ TKOLButton = PControl;
+ TKOLLabel = PControl;
+ TKOLLabelEffect = PControl;
+ TKOLPanel = PControl;
+ TKOLSplitter = PControl;
+ TKOLGradientPanel = PControl;
+ TKOLBitBtn = PControl;
+ TKOLGroupBox = PControl;
+ TKOLCheckBox = PControl;
+ TKOLRadioBox = PControl;
+ TKOLEditBox = PControl;
+ TKOLMemo = PControl;
+ TKOLListBox = PControl;
+ TKOLComboBox = PControl;
+ TKOLPaintBox = PControl;
+ TKOLImageShow = PControl;
+
+ TKOLRichEdit = PControl;
+ TKOLProgressBar = PControl;
+ TKOLListView = PControl;
+ TKOLTreeView = PControl;
+ TKOLToolbar = PControl;
+ TKOLTabControl = PControl;
+ TTabPage = PControl;
+ TKOLScrollBox = PControl;
+ TKOLDateTimePicker = PControl;
+
+ TKOLTimer = PTimer;
+ TKOLThread = PThread;
+ TKOLImageList = PImageList;
+ TKOLMainMenu = PMenu;
+ TKOLPopupMenu = PMenu;
+ TKOLOpenSaveDialog = POpenSaveDialog;
+ //TKOLOpenDirDialog = POpenDirDialog;
+ TKOLTrayIcon = PTrayIcon;
+ TKOLColorDialog = PColorDialog;
+ //TKOLActionList = PActionList;
+ //TKOLAction = PAction;
+ TKOLScrollBar = PControl;
+{$ENDIF} \ No newline at end of file
diff --git a/plugins/Libs/MsgDecode.pas b/plugins/Libs/MsgDecode.pas
new file mode 100644
index 0000000000..7f53615094
--- /dev/null
+++ b/plugins/Libs/MsgDecode.pas
@@ -0,0 +1,4957 @@
+type
+ TMessageDecoded = (
+ cWM_NULL, // = $0000;
+ cWM_CREATE, // = $0001;
+ cWM_DESTROY, // = $0002;
+ cWM_MOVE, // = $0003;
+ cWM_0004,
+ cWM_SIZE, // = $0005;
+ cWM_ACTIVATE, // = $0006;
+ cWM_SETFOCUS, // = $0007;
+ cWM_KILLFOCUS, // = $0008;
+ cWM_0009,
+ cWM_ENABLE, // = $000A;
+ cWM_SETREDRAW, // = $000B;
+ cWM_SETTEXT, // = $000C;
+ cWM_GETTEXT, // = $000D;
+ cWM_GETTEXTLENGTH,// = $000E;
+ cWM_PAINT, // = $000F;
+ cWM_CLOSE, // = $0010;
+ cWM_QUERYENDSESSION,// = $0011;
+ cWM_QUIT, // = $0012;
+ cWM_QUERYOPEN, // = $0013;
+ cWM_ERASEBKGND, // = $0014;
+ cWM_SYSCOLORCHANGE,// = $0015;
+ cWM_ENDSESSION, // = $0016;
+ cWM_SYSTEMERROR, // = $0017;
+ cWM_SHOWWINDOW, // = $0018;
+ cWM_CTLCOLOR, // = $0019;
+ cWM_WININICHANGE,// = $001A;
+ //WM_SETTINGCHANGE = WM_WININICHANGE;
+ cWM_DEVMODECHANGE,// = $001B;
+ cWM_ACTIVATEAPP, // = $001C;
+ cWM_FONTCHANGE, // = $001D;
+ cWM_TIMECHANGE, // = $001E;
+ cWM_CANCELMODE, // = $001F;
+ cWM_SETCURSOR, // = $0020;
+ cWM_MOUSEACTIVATE,// = $0021;
+ cWM_CHILDACTIVATE,// = $0022;
+ cWM_QUEUESYNC, // = $0023;
+ cWM_GETMINMAXINFO,// = $0024;
+ cWM_0025,
+ cWM_PAINTICON, // = $0026;
+ cWM_ICONERASEBKGND,// = $0027;
+ cWM_NEXTDLGCTL, // = $0028;
+ cWM_0029,
+ cWM_SPOOLERSTATUS,// = $002A;
+ cWM_DRAWITEM, // = $002B;
+ cWM_MEASUREITEM, // = $002C;
+ cWM_DELETEITEM, // = $002D;
+ cWM_VKEYTOITEM, // = $002E;
+ cWM_CHARTOITEM, // = $002F;
+ cWM_SETFONT, // = $0030;
+ cWM_GETFONT, // = $0031;
+ cWM_SETHOTKEY, // = $0032;
+ cWM_GETHOTKEY, // = $0033;
+ cWM_0034,
+ cWM_0035,
+ cWM_0036,
+ cWM_QUERYDRAGICON,// = $0037;
+ cWM_0038,
+ cWM_COMPAREITEM, // = $0039;
+ cWM_003A,
+ cWM_003B,
+ cWM_003C,
+ cWM_GETOBJECT, // = $003D;
+ cWM_003E,
+ cWM_003F,
+ cWM_0040,
+ cWM_COMPACTING, // = $0041;
+ cWM_0042,
+ cWM_0043,
+ cWM_COMMNOTIFY, // = $0044; { obsolete in Win32}
+ cWM_0045,
+ cWM_WINDOWPOSCHANGING,// = $0046;
+ cWM_WINDOWPOSCHANGED,// = $0047;
+ cWM_POWER, // = $0048;
+ cWM_0049,
+ cWM_COPYDATA, // = $004A;
+ cWM_CANCELJOURNAL,// = $004B;
+ cWM_004C,
+ cWM_004D,
+ cWM_NOTIFY, // = $004E;
+ cWM_004F,
+ cWM_INPUTLANGCHANGEREQUEST,// = $0050;
+ cWM_INPUTLANGCHANGE,// = $0051;
+ cWM_TCARD, // = $0052;
+ cWM_HELP, // = $0053;
+ cWM_USERCHANGED, // = $0054;
+ cWM_NOTIFYFORMAT, // = $0055;
+ cWM_0056,
+ cWM_0057,
+ cWM_0058,
+ cWM_0059,
+ cWM_005A,
+ cWM_005B,
+ cWM_005C,
+ cWM_005D,
+ cWM_005E,
+ cWM_005F,
+ cWM_0060,
+ cWM_0061,
+ cWM_0062,
+ cWM_0063,
+ cWM_0064,
+ cWM_0065,
+ cWM_0066,
+ cWM_0067,
+ cWM_0068,
+ cWM_0069,
+ cWM_006A,
+ cWM_006B,
+ cWM_006C,
+ cWM_006D,
+ cWM_006E,
+ cWM_006F,
+ cWM_0070,
+ cWM_0071,
+ cWM_0072,
+ cWM_0073,
+ cWM_0074,
+ cWM_0075,
+ cWM_0076,
+ cWM_0077,
+ cWM_0078,
+ cWM_0079,
+ cWM_007A,
+ cWM_CONTEXTMENU, // = $007B;
+ cWM_STYLECHANGING,// = $007C;
+ cWM_STYLECHANGED, // = $007D;
+ cWM_DISPLAYCHANGE,// = $007E;
+ cWM_GETICON, // = $007F;
+ cWM_SETICON, // = $0080;
+ cWM_NCCREATE, // = $0081;
+ cWM_NCDESTROY, // = $0082;
+ cWM_NCCALCSIZE, // = $0083;
+ cWM_NCHITTEST, // = $0084;
+ cWM_NCPAINT, // = $0085;
+ cWM_NCACTIVATE, // = $0086;
+ cWM_GETDLGCODE, // = $0087;
+ cWM_0088,
+ cWM_0089,
+ cWM_008A,
+ cWM_008B,
+ cWM_008C,
+ cWM_008D,
+ cWM_008E,
+ cWM_008F,
+ cWM_0090,
+ cWM_0091,
+ cWM_0092,
+ cWM_0093,
+ cWM_0094,
+ cWM_0095,
+ cWM_0096,
+ cWM_0097,
+ cWM_0098,
+ cWM_0099,
+ cWM_009A,
+ cWM_009B,
+ cWM_009C,
+ cWM_009D,
+ cWM_009E,
+ cWM_009F,
+ cWM_NCMOUSEMOVE, // = $00A0;
+ cWM_NCLBUTTONDOWN, // = $00A1;
+ cWM_NCLBUTTONUP, // = $00A2;
+ cWM_NCLBUTTONDBLCLK,// = $00A3;
+ cWM_NCRBUTTONDOWN, // = $00A4;
+ cWM_NCRBUTTONUP, // = $00A5;
+ cWM_NCRBUTTONDBLCLK,// = $00A6;
+ cWM_NCMBUTTONDOWN, // = $00A7;
+ cWM_NCMBUTTONUP, // = $00A8;
+ cWM_NCMBUTTONDBLCLK,// = $00A9;
+ cWM_00AA,
+ cWM_NCXBUTTONDOWN, // = $00AB;
+ cWM_NCXBUTTONUP, // = $00AC;
+ cWM_NCXBUTTONDBLCLK,// = $00AD;
+ cWM_00AE,
+ cWM_00AF,
+ cEM_GETSEL, // = $00B0;
+ cEM_SETSEL, // = $00B1;
+ cEM_GETRECT, // = $00B2;
+ cEM_SETRECT, // = $00B3;
+ cEM_SETRECTNP, // = $00B4;
+ cEM_SCROLL, // = $00B5;
+ cEM_LINESCROLL, // = $00B6;
+ cEM_SCROLLCARET, // = $00B7;
+ cEM_GETMODIFY, // = $00B8;
+ cEM_SETMODIFY, // = $00B9;
+ cEM_GETLINECOUNT, // = $00BA;
+ cEM_LINEINDEX, // = $00BB;
+ cEM_SETHANDLE, // = $00BC;
+ cEM_GETHANDLE, // = $00BD;
+ cEM_GETTHUMB, // = $00BE;
+ cWM_00BF,
+ cWM_00C0,
+ cEM_LINELENGTH, // = $00C1;
+ cEM_REPLACESEL, // = $00C2;
+ cWM_00C3,
+ cEM_GETLINE, // = $00C4;
+ cEM_LIMITTEXT, // = $00C5;
+ cEM_CANUNDO, // = $00C6;
+ cEM_UNDO, // = $00C7;
+ cEM_FMTLINES, // = $00C8;
+ cEM_LINEFROMCHAR, // = $00C9;
+ cWM_00CA,
+ cEM_SETTABSTOPS, // = $00CB;
+ cEM_SETPASSWORDCHAR,// = $00CC;
+ cEM_EMPTYUNDOBUFFER,// = $00CD;
+ cEM_GETFIRSTVISIBLELINE,// = $00CE;
+ cEM_SETREADONLY, // = $00CF;
+ cEM_SETWORDBREAKPROC,// = $00D0;
+ cEM_GETWORDBREAKPROC,// = $00D1;
+ cEM_GETPASSWORDCHAR,// = $00D2;
+ cEM_SETMARGINS, // = 211;
+ cEM_GETMARGINS, // = 212;
+ //EM_SETLIMITTEXT = EM_LIMITTEXT; //win40 Name change
+ cEM_GETLIMITTEXT, // = 213;
+ cEM_POSFROMCHAR, // = 214;
+ cEM_CHARFROMPOS, // = 215;
+ cEM_SETIMESTATUS, // = 216;
+ cEM_GETIMESTATUS, // = 217; = $D9;
+ cWM_00DA,
+ cWM_00DB,
+ cWM_00DC,
+ cWM_00DD,
+ cWM_00DE,
+ cWM_00DF,
+ cWM_00E0,
+ cWM_00E1,
+ cWM_00E2,
+ cWM_00E3,
+ cWM_00E4,
+ cWM_00E5,
+ cWM_00E6,
+ cWM_00E7,
+ cWM_00E8,
+ cWM_00E9,
+ cWM_00EA,
+ cWM_00EB,
+ cWM_00EC,
+ cWM_00ED,
+ cWM_00EE,
+ cWM_00EF,
+ cBM_GETCHECK, // = $00F0;
+ cBM_SETCHECK, // = $00F1;
+ cBM_GETSTATE, // = $00F2;
+ cBM_SETSTATE, // = $00F3;
+ cBM_SETSTYLE, // = $00F4;
+ cBM_CLICK, // = $00F5;
+ cBM_GETIMAGE, // = $00F6;
+ cBM_SETIMAGE, // = $00F7;
+ cWM_00F8,
+ cWM_00F9,
+ cWM_00FA,
+ cWM_00FB,
+ cWM_00FC,
+ cWM_00FD,
+ cWM_00FE,
+ cWM_INPUT, // = $00FF;
+ //WM_KEYFIRST = $0100;
+ cWM_KEYDOWN, // = $0100;
+ cWM_KEYUP, // = $0101;
+ cWM_CHAR, // = $0102;
+ cWM_DEADCHAR, // = $0103;
+ cWM_SYSKEYDOWN, // = $0104;
+ cWM_SYSKEYUP, // = $0105;
+ cWM_SYSCHAR, // = $0106;
+ cWM_SYSDEADCHAR, // = $0107;
+ cWM_KEYLAST, // = $0108;
+ cWM_0109,
+ cWM_010A,
+ cWM_010B,
+ cWM_010C,
+ cWM_IME_STARTCOMPOSITION, // = $010D;
+ cWM_IME_ENDCOMPOSITION, // = $010E;
+ cWM_IME_COMPOSITION, // = $010F;
+ cWM_INITDIALOG, // = $0110;
+ cWM_COMMAND, // = $0111;
+ cWM_SYSCOMMAND, // = $0112;
+ cWM_TIMER, // = $0113;
+ cWM_HSCROLL, // = $0114;
+ cWM_VSCROLL, // = $0115;
+ cWM_INITMENU, // = $0116;
+ cWM_INITMENUPOPUP, // = $0117;
+ cWM_0118,
+ cWM_0119,
+ cWM_011A,
+ cWM_011B,
+ cWM_011C,
+ cWM_011D,
+ cWM_011E,
+ cWM_MENUSELECT, // = $011F;
+ cWM_MENUCHAR, // = $0120;
+ cWM_ENTERIDLE, // = $0121;
+ cWM_MENURBUTTONUP, // = $0122;
+ cWM_MENUDRAG, // = $0123;
+ cWM_MENUGETOBJECT, // = $0124;
+ cWM_UNINITMENUPOPUP, // = $0125;
+ cWM_MENUCOMMAND, // = $0126;
+ cWM_CHANGEUISTATE, // = $0127;
+ cWM_UPDATEUISTATE, // = $0128;
+ cWM_QUERYUISTATE, // = $0129;
+ cWM_012A,
+ cWM_012B,
+ cWM_012C,
+ cWM_012D,
+ cWM_012E,
+ cWM_012F,
+ cWM_0130,
+ cWM_0131,
+ cWM_CTLCOLORMSGBOX, // = $0132;
+ cWM_CTLCOLOREDIT, // = $0133;
+ cWM_CTLCOLORLISTBOX, // = $0134;
+ cWM_CTLCOLORBTN, // = $0135;
+ cWM_CTLCOLORDLG, // = $0136;
+ cWM_CTLCOLORSCROLLBAR, // = $0137;
+ cWM_CTLCOLORSTATIC, // = $0138;
+ cWM_0139,
+ cWM_013A,
+ cWM_013B,
+ cWM_013C,
+ cWM_013D,
+ cWM_013E,
+ cWM_013F,
+ cCB_GETEDITSEL, // = $0140;
+ cCB_LIMITTEXT, // = $0141;
+ cCB_SETEDITSEL, // = $0142;
+ cCB_ADDSTRING, // = $0143;
+ cCB_DELETESTRING, // = $0144;
+ cCB_DIR, // = $0145;
+ cCB_GETCOUNT, // = $0146;
+ cCB_GETCURSEL, // = $0147;
+ cCB_GETLBTEXT, // = $0148;
+ cCB_GETLBTEXTLEN, // = $0149;
+ cCB_INSERTSTRING, // = $014A;
+ cCB_RESETCONTENT, // = $014B;
+ cCB_FINDSTRING, // = $014C;
+ cCB_SELECTSTRING, // = $014D;
+ cCB_SETCURSEL, // = $014E;
+ cCB_SHOWDROPDOWN, // = $014F;
+ cCB_GETITEMDATA, // = $0150;
+ cCB_SETITEMDATA, // = $0151;
+ cCB_GETDROPPEDCONTROLRECT,// = $0152;
+ cCB_SETITEMHEIGHT, // = $0153;
+ cCB_GETITEMHEIGHT, // = $0154;
+ cCB_SETEXTENDEDUI, // = $0155;
+ cCB_GETEXTENDEDUI, // = $0156;
+ cCB_GETDROPPEDSTATE, // = $0157;
+ cCB_FINDSTRINGEXACT, // = $0158;
+ cCB_SETLOCALE, // = 345;
+ cCB_GETLOCALE, // = 346;
+ cCB_GETTOPINDEX, // = 347;
+ cCB_SETTOPINDEX, // = 348;
+ cCB_GETHORIZONTALEXTENT, // = 349;
+ cCB_SETHORIZONTALEXTENT, // = 350;
+ cCB_GETDROPPEDWIDTH, // = 351;
+ cCB_SETDROPPEDWIDTH, // = 352;
+ cCB_INITSTORAGE, // = 353;
+ cCB_MSGMAX, // = 354; = $162
+ cWM_0163,
+ cWM_0164,
+ cWM_0165,
+ cWM_0166,
+ cWM_0167,
+ cWM_0168,
+ cWM_0169,
+ cWM_016A,
+ cWM_016B,
+ cWM_016C,
+ cWM_016D,
+ cWM_016E,
+ cWM_016F,
+ cWM_0170,
+ cWM_0171,
+ cWM_0172,
+ cWM_0173,
+ cWM_0174,
+ cWM_0175,
+ cWM_0176,
+ cWM_0177,
+ cWM_0178,
+ cWM_0179,
+ cWM_017A,
+ cWM_017B,
+ cWM_017C,
+ cWM_017D,
+ cWM_017E,
+ cWM_017F,
+ cLB_ADDSTRING, // = $0180;
+ cLB_INSERTSTRING, // = $0181;
+ cLB_DELETESTRING, // = $0182;
+ cLB_SELITEMRANGEEX, // = $0183;
+ cLB_RESETCONTENT, // = $0184;
+ cLB_SETSEL, // = $0185;
+ cLB_SETCURSEL, // = $0186;
+ cLB_GETSEL, // = $0187;
+ cLB_GETCURSEL, // = $0188;
+ cLB_GETTEXT, // = $0189;
+ cLB_GETTEXTLEN, // = $018A;
+ cLB_GETCOUNT, // = $018B;
+ cLB_SELECTSTRING, // = $018C;
+ cLB_DIR, // = $018D;
+ cLB_GETTOPINDEX, // = $018E;
+ cLB_FINDSTRING, // = $018F;
+ cLB_GETSELCOUNT, // = $0190;
+ cLB_GETSELITEMS, // = $0191;
+ cLB_SETTABSTOPS, // = $0192;
+ cLB_GETHORIZONTALEXTENT,// = $0193;
+ cLB_SETHORIZONTALEXTENT,// = $0194;
+ cLB_SETCOLUMNWIDTH, // = $0195;
+ cLB_ADDFILE, // = $0196;
+ cLB_SETTOPINDEX, // = $0197;
+ cLB_GETITEMRECT, // = $0198;
+ cLB_GETITEMDATA, // = $0199;
+ cLB_SETITEMDATA, // = $019A;
+ cLB_SELITEMRANGE, // = $019B;
+ cLB_SETANCHORINDEX, // = $019C;
+ cLB_GETANCHORINDEX, // = $019D;
+ cLB_SETCARETINDEX, // = $019E;
+ cLB_GETCARETINDEX, // = $019F;
+ cLB_SETITEMHEIGHT, // = $01A0;
+ cLB_GETITEMHEIGHT, // = $01A1;
+ cLB_FINDSTRINGEXACT,// = $01A2;
+ cWM_01A3,
+ cWM_01A4,
+ cLB_SETLOCALE, // = $01A5;
+ cLB_GETLOCALE, // = $01A6;
+ cLB_SETCOUNT, // = $01A7;
+ cLB_INITSTORAGE, // = $01A8;
+ cLB_ITEMFROMPOINT, // = $01A9;
+ cWM_01AA,
+ cWM_01AB,
+ cWM_01AC,
+ cWM_01AD,
+ cWM_01AE,
+ cWM_01AF,
+ cWM_01B0,
+ cWM_01B1,
+ cWM_01B2,
+ cWM_01B3,
+ cWM_01B4,
+ cWM_01B5,
+ cWM_01B6,
+ cWM_01B7,
+ cWM_01B8,
+ cWM_01B9,
+ cWM_01BA,
+ cWM_01BB,
+ cWM_01BC,
+ cWM_01BD,
+ cWM_01BE,
+ cWM_01BF,
+ cWM_01C0,
+ cWM_01C1,
+ cWM_01C2,
+ cWM_01C3,
+ cWM_01C4,
+ cWM_01C5,
+ cWM_01C6,
+ cWM_01C7,
+ cWM_01C8,
+ cWM_01C9,
+ cWM_01CA,
+ cWM_01CB,
+ cWM_01CC,
+ cWM_01CD,
+ cWM_01CE,
+ cWM_01CF,
+ cWM_01D0,
+ cWM_01D1,
+ cWM_01D2,
+ cWM_01D3,
+ cWM_01D4,
+ cWM_01D5,
+ cWM_01D6,
+ cWM_01D7,
+ cWM_01D8,
+ cWM_01D9,
+ cWM_01DA,
+ cWM_01DB,
+ cWM_01DC,
+ cWM_01DD,
+ cWM_01DE,
+ cWM_01DF,
+ cWM_01E0,
+ cWM_01E1,
+ cWM_01E2,
+ cWM_01E3,
+ cWM_01E4,
+ cWM_01E5,
+ cWM_01E6,
+ cWM_01E7,
+ cWM_01E8,
+ cWM_01E9,
+ cWM_01EA,
+ cWM_01EB,
+ cWM_01EC,
+ cWM_01ED,
+ cWM_01EE,
+ cWM_01EF,
+ cWM_01F0,
+ cWM_01F1,
+ cWM_01F2,
+ cWM_01F3,
+ cWM_01F4,
+ cWM_01F5,
+ cWM_01F6,
+ cWM_01F7,
+ cWM_01F8,
+ cWM_01F9,
+ cWM_01FA,
+ cWM_01FB,
+ cWM_01FC,
+ cWM_01FD,
+ cWM_01FE,
+ cWM_01FF,
+ cWM_MOUSEMOVE, // = $0200; WM_MOUSEFIRST
+ cWM_LBUTTONDOWN, // = $0201;
+ cWM_LBUTTONUP, // = $0202;
+ cWM_LBUTTONDBLCLK, // = $0203;
+ cWM_RBUTTONDOWN, // = $0204;
+ cWM_RBUTTONUP, // = $0205;
+ cWM_RBUTTONDBLCLK, // = $0206;
+ cWM_MBUTTONDOWN, // = $0207;
+ cWM_MBUTTONUP, // = $0208;
+ cWM_MBUTTONDBLCLK, // = $0209;
+ cWM_MOUSEWHEEL, // = $020A; WM_MOUSELAST
+ cWM_020B,
+ cWM_020C,
+ cWM_020D,
+ cWM_020E,
+ cWM_020F,
+ cWM_PARENTNOTIFY, // = $0210;
+ cWM_ENTERMENULOOP, // = $0211;
+ cWM_EXITMENULOOP, // = $0212;
+ cWM_NEXTMENU, // = $0213;
+ cWM_SIZING, // = 532; = $214
+ cWM_CAPTURECHANGED, // = 533;
+ cWM_MOVING, // = 534;
+ cWM_POWERBROADCAST, // = 536;
+ cWM_DEVICECHANGE, // = 537; = $218
+ cWM_0219,
+ cWM_021A,
+ cWM_021B,
+ cWM_021C,
+ cWM_021D,
+ cWM_021E,
+ cWM_021F,
+ cWM_MDICREATE, // = $0220;
+ cWM_MDIDESTROY, // = $0221;
+ cWM_MDIACTIVATE, // = $0222;
+ cWM_MDIRESTORE, // = $0223;
+ cWM_MDINEXT, // = $0224;
+ cWM_MDIMAXIMIZE, // = $0225;
+ cWM_MDITILE, // = $0226;
+ cWM_MDICASCADE, // = $0227;
+ cWM_MDIICONARRANGE, // = $0228;
+ cWM_MDIGETACTIVE, // = $0229;
+ cWM_022A,
+ cWM_022B,
+ cWM_022C,
+ cWM_022D,
+ cWM_022E,
+ cWM_022F,
+ cWM_MDISETMENU, // = $0230;
+ cWM_ENTERSIZEMOVE, // = $0231;
+ cWM_EXITSIZEMOVE, // = $0232;
+ cWM_DROPFILES, // = $0233;
+ cWM_MDIREFRESHMENU, // = $0234;
+ cWM_0235,
+ cWM_0236,
+ cWM_0237,
+ cWM_0238,
+ cWM_0239,
+ cWM_023A,
+ cWM_023B,
+ cWM_023C,
+ cWM_023D,
+ cWM_023E,
+ cWM_023F,
+ cWM_0240,
+ cWM_0241,
+ cWM_0242,
+ cWM_0243,
+ cWM_0244,
+ cWM_0245,
+ cWM_0246,
+ cWM_0247,
+ cWM_0248,
+ cWM_0249,
+ cWM_024A,
+ cWM_024B,
+ cWM_024C,
+ cWM_024D,
+ cWM_024E,
+ cWM_024F,
+ cWM_0250,
+ cWM_0251,
+ cWM_0252,
+ cWM_0253,
+ cWM_0254,
+ cWM_0255,
+ cWM_0256,
+ cWM_0257,
+ cWM_0258,
+ cWM_0259,
+ cWM_025A,
+ cWM_025B,
+ cWM_025C,
+ cWM_025D,
+ cWM_025E,
+ cWM_025F,
+ cWM_0260,
+ cWM_0261,
+ cWM_0262,
+ cWM_0263,
+ cWM_0264,
+ cWM_0265,
+ cWM_0266,
+ cWM_0267,
+ cWM_0268,
+ cWM_0269,
+ cWM_026A,
+ cWM_026B,
+ cWM_026C,
+ cWM_026D,
+ cWM_026E,
+ cWM_026F,
+ cWM_0270,
+ cWM_0271,
+ cWM_0272,
+ cWM_0273,
+ cWM_0274,
+ cWM_0275,
+ cWM_0276,
+ cWM_0277,
+ cWM_0278,
+ cWM_0279,
+ cWM_027A,
+ cWM_027B,
+ cWM_027C,
+ cWM_027D,
+ cWM_027E,
+ cWM_027F,
+ cWM_0280,
+ cWM_IME_SETCONTEXT, // = $0281;
+ cWM_IME_NOTIFY, // = $0282;
+ cWM_IME_CONTROL, // = $0283;
+ cWM_IME_COMPOSITIONFULL,// = $0284;
+ cWM_IME_SELECT, // = $0285;
+ cWM_IME_CHAR, // = $0286;
+ cWM_0287,
+ cWM_IME_REQUEST, // = $0288;
+ cWM_0289,
+ cWM_028A,
+ cWM_028B,
+ cWM_028C,
+ cWM_028D,
+ cWM_028E,
+ cWM_028F,
+ cWM_IME_KEYDOWN, // = $0290;
+ cWM_IME_KEYUP, // = $0291;
+ cWM_0292,
+ cWM_0293,
+ cWM_0294,
+ cWM_0295,
+ cWM_0296,
+ cWM_0297,
+ cWM_0298,
+ cWM_0299,
+ cWM_029A,
+ cWM_029B,
+ cWM_029C,
+ cWM_029D,
+ cWM_029E,
+ cWM_029F,
+ cWM_NCMOUSEHOVER, // = $02A0;
+ cWM_MOUSEHOVER, // = $02A1;
+ cWM_NCMOUSELEAVE, // = $02A2;
+ cWM_MOUSELEAVE, // = $02A3;
+ cWM_02A4,
+ cWM_02A5,
+ cWM_02A6,
+ cWM_02A7,
+ cWM_02A8,
+ cWM_02A9,
+ cWM_02AA,
+ cWM_02AB,
+ cWM_02AC,
+ cWM_02AD,
+ cWM_02AE,
+ cWM_02AF,
+ cWM_02B0,
+ cWM_WTSSESSION_CHANGE, // = $02B1;
+ cWM_02B2,
+ cWM_02B3,
+ cWM_02B4,
+ cWM_02B5,
+ cWM_02B6,
+ cWM_02B7,
+ cWM_02B8,
+ cWM_02B9,
+ cWM_02BA,
+ cWM_02BB,
+ cWM_02BC,
+ cWM_02BD,
+ cWM_02BE,
+ cWM_02BF,
+ cWM_TABLET_FIRST, // = $02C0;
+ cWM_02C1,
+ cWM_02C2,
+ cWM_02C3,
+ cWM_02C4,
+ cWM_02C5,
+ cWM_02C6,
+ cWM_02C7,
+ cWM_02C8,
+ cWM_02C9,
+ cWM_02CA,
+ cWM_02CB,
+ cWM_02CC,
+ cWM_02CD,
+ cWM_02CE,
+ cWM_02CF,
+ cWM_02D0,
+ cWM_02D1,
+ cWM_02D2,
+ cWM_02D3,
+ cWM_02D4,
+ cWM_02D5,
+ cWM_02D6,
+ cWM_02D7,
+ cWM_02D8,
+ cWM_02D9,
+ cWM_02DA,
+ cWM_02DB,
+ cWM_02DC,
+ cWM_02DD,
+ cWM_02DE,
+ cWM_TABLET_LAST, // = $02DF;
+ cWM_02E0,
+ cWM_02E1,
+ cWM_02E2,
+ cWM_02E3,
+ cWM_02E4,
+ cWM_02E5,
+ cWM_02E6,
+ cWM_02E7,
+ cWM_02E8,
+ cWM_02E9,
+ cWM_02EA,
+ cWM_02EB,
+ cWM_02EC,
+ cWM_02ED,
+ cWM_02EE,
+ cWM_02EF,
+ cWM_02F0,
+ cWM_02F1,
+ cWM_02F2,
+ cWM_02F3,
+ cWM_02F4,
+ cWM_02F5,
+ cWM_02F6,
+ cWM_02F7,
+ cWM_02F8,
+ cWM_02F9,
+ cWM_02FA,
+ cWM_02FB,
+ cWM_02FC,
+ cWM_02FD,
+ cWM_02FE,
+ cWM_02FF,
+ cWM_CUT, // = $0300;
+ cWM_COPY, // = $0301;
+ cWM_PASTE, // = $0302;
+ cWM_CLEAR, // = $0303;
+ cWM_UNDO, // = $0304;
+ cWM_RENDERFORMAT, // = $0305;
+ cWM_RENDERALLFORMATS, // = $0306;
+ cWM_DESTROYCLIPBOARD, // = $0307;
+ cWM_DRAWCLIPBOARD, // = $0308;
+ cWM_PAINTCLIPBOARD, // = $0309;
+ cWM_VSCROLLCLIPBOARD, // = $030A;
+ cWM_SIZECLIPBOARD, // = $030B;
+ cWM_ASKCBFORMATNAME, // = $030C;
+ cWM_CHANGECBCHAIN, // = $030D;
+ cWM_HSCROLLCLIPBOARD, // = $030E;
+ cWM_QUERYNEWPALETTE, // = $030F;
+ cWM_PALETTEISCHANGING, // = $0310;
+ cWM_PALETTECHANGED, // = $0311;
+ cWM_HOTKEY, // = $0312;
+ cWM_0313,
+ cWM_0314,
+ cWM_0315,
+ cWM_0316,
+ cWM_0317,
+ cWM_0318,
+ cWM_APPCOMMAND, // = $0319;
+ cWM_THEMECHANGED, // = $031A;
+ cWM_031B,
+ cWM_031C,
+ cWM_031D,
+ cWM_031E,
+ cWM_031F,
+ cWM_0320,
+ cWM_0321,
+ cWM_0322,
+ cWM_0323,
+ cWM_0324,
+ cWM_0325,
+ cWM_0326,
+ cWM_0327,
+ cWM_0328,
+ cWM_0329,
+ cWM_032A,
+ cWM_032B,
+ cWM_032C,
+ cWM_032D,
+ cWM_032E,
+ cWM_032F,
+ cWM_0330,
+ cWM_0331,
+ cWM_0332,
+ cWM_0333,
+ cWM_0334,
+ cWM_0335,
+ cWM_0336,
+ cWM_0337,
+ cWM_0338,
+ cWM_0339,
+ cWM_033A,
+ cWM_033B,
+ cWM_033C,
+ cWM_033D,
+ cWM_033E,
+ cWM_033F,
+ cWM_0340,
+ cWM_0341,
+ cWM_0342,
+ cWM_0343,
+ cWM_0344,
+ cWM_0345,
+ cWM_0346,
+ cWM_0347,
+ cWM_0348,
+ cWM_0349,
+ cWM_034A,
+ cWM_034B,
+ cWM_034C,
+ cWM_034D,
+ cWM_034E,
+ cWM_034F,
+ cWM_0350,
+ cWM_0351,
+ cWM_0352,
+ cWM_0353,
+ cWM_0354,
+ cWM_0355,
+ cWM_0356,
+ cWM_0357,
+ cWM_0358,
+ cWM_0359,
+ cWM_035A,
+ cWM_035B,
+ cWM_035C,
+ cWM_035D,
+ cWM_035E,
+ cWM_035F,
+ cWM_0360,
+ cWM_0361,
+ cWM_0362,
+ cWM_0363,
+ cWM_0364,
+ cWM_0365,
+ cWM_0366,
+ cWM_0367,
+ cWM_0368,
+ cWM_0369,
+ cWM_036A,
+ cWM_036B,
+ cWM_036C,
+ cWM_036D,
+ cWM_036E,
+ cWM_036F,
+ cWM_0370,
+ cWM_0371,
+ cWM_0372,
+ cWM_0373,
+ cWM_0374,
+ cWM_0375,
+ cWM_0376,
+ cWM_0377,
+ cWM_0378,
+ cWM_0379,
+ cWM_037A,
+ cWM_037B,
+ cWM_037C,
+ cWM_037D,
+ cWM_037E,
+ cWM_037F,
+ cWM_PENWINFIRST, // = $0380;
+ cWM_0381,
+ cWM_0382,
+ cWM_0383,
+ cWM_0384,
+ cWM_0385,
+ cWM_0386,
+ cWM_0387,
+ cWM_0388,
+ cWM_0389,
+ cWM_038A,
+ cWM_038B,
+ cWM_038C,
+ cWM_038D,
+ cWM_038E,
+ cWM_PENWINLAST, // = $038F;
+ cWM_COALESCE_FIRST, // = $0390;
+ cWM_0391,
+ cWM_0392,
+ cWM_0393,
+ cWM_0394,
+ cWM_0395,
+ cWM_0396,
+ cWM_0397,
+ cWM_0398,
+ cWM_0399,
+ cWM_039A,
+ cWM_039B,
+ cWM_039C,
+ cWM_039D,
+ cWM_039E,
+ cWM_COALESCE_LAST, // = $039F;
+ cWM_03A0,
+ cWM_03A1,
+ cWM_03A2,
+ cWM_03A3,
+ cWM_03A4,
+ cWM_03A5,
+ cWM_03A6,
+ cWM_03A7,
+ cWM_03A8,
+ cWM_03A9,
+ cWM_03AA,
+ cWM_03AB,
+ cWM_03AC,
+ cWM_03AD,
+ cWM_03AE,
+ cWM_03AF,
+ cWM_03B0,
+ cWM_03B1,
+ cWM_03B2,
+ cWM_03B3,
+ cWM_03B4,
+ cWM_03B5,
+ cWM_03B6,
+ cWM_03B7,
+ cWM_03B8,
+ cWM_03B9,
+ cWM_03BA,
+ cWM_03BB,
+ cWM_03BC,
+ cWM_03BD,
+ cWM_03BE,
+ cWM_03BF,
+ cWM_03C0,
+ cWM_03C1,
+ cWM_03C2,
+ cWM_03C3,
+ cWM_03C4,
+ cWM_03C5,
+ cWM_03C6,
+ cWM_03C7,
+ cWM_03C8,
+ cWM_03C9,
+ cWM_03CA,
+ cWM_03CB,
+ cWM_03CC,
+ cWM_03CD,
+ cWM_03CE,
+ cWM_03CF,
+ cWM_03D0,
+ cWM_03D1,
+ cWM_03D2,
+ cWM_03D3,
+ cWM_03D4,
+ cWM_03D5,
+ cWM_03D6,
+ cWM_03D7,
+ cWM_03D8,
+ cWM_03D9,
+ cWM_03DA,
+ cWM_03DB,
+ cWM_03DC,
+ cWM_03DD,
+ cWM_03DE,
+ cWM_03DF,
+ cWM_DDE_INITIATE, // = WM_DDE_FIRST + 0; WM_DDE_FIRST = $03E0;
+ cWM_DDE_TERMINATE, // = WM_DDE_FIRST + 1;
+ cWM_DDE_ADVISE, // = WM_DDE_FIRST + 2;
+ cWM_DDE_UNADVISE, // = WM_DDE_FIRST + 3;
+ cWM_DDE_ACK, // = WM_DDE_FIRST + 4;
+ cWM_DDE_DATA, // = WM_DDE_FIRST + 5;
+ cWM_DDE_REQUEST, // = WM_DDE_FIRST + 6;
+ cWM_DDE_POKE, // = WM_DDE_FIRST + 7;
+ cWM_DDE_EXECUTE, // = WM_DDE_FIRST + 8; WM_DDE_LAST
+ cWM_03E9,
+ cWM_03EA,
+ cWM_03EB,
+ cWM_03EC,
+ cWM_03ED,
+ cWM_03EE,
+ cWM_03EF,
+ cWM_03F0,
+ cWM_03F1,
+ cWM_03F2,
+ cWM_03F3,
+ cWM_03F4,
+ cWM_03F5,
+ cWM_03F6,
+ cWM_03F7,
+ cWM_03F8,
+ cWM_03F9,
+ cWM_03FA,
+ cWM_03FB,
+ cWM_03FC,
+ cWM_03FD,
+ cWM_03FE,
+ cWM_03FF,
+ cWM_USER, // = $0400;
+ cWM_0401,
+ cWM_0402,
+ cWM_0403,
+ cWM_0404,
+ cWM_0405,
+ cWM_0406,
+ cWM_0407,
+ cWM_0408,
+ cWM_0409,
+ cWM_040A,
+ cWM_040B,
+ cWM_040C,
+ cWM_040D,
+ cWM_040E,
+ cWM_040F,
+ cWM_0410,
+ cWM_0411,
+ cWM_0412,
+ cWM_0413,
+ cWM_0414,
+ cWM_0415,
+ cWM_0416,
+ cWM_0417,
+ cWM_0418,
+ cWM_0419,
+ cWM_041A,
+ cWM_041B,
+ cWM_041C,
+ cWM_041D,
+ cWM_041E,
+ cWM_041F,
+ cWM_0420,
+ cWM_0421,
+ cWM_0422,
+ cWM_0423,
+ cWM_0424,
+ cWM_0425,
+ cWM_0426,
+ cWM_0427,
+ cWM_0428,
+ cWM_0429,
+ cWM_042A,
+ cWM_042B,
+ cWM_042C,
+ cWM_042D,
+ cWM_042E,
+ cWM_042F,
+ cWM_0430,
+ cWM_0431,
+ cWM_0432,
+ cWM_0433,
+ cWM_0434,
+ cWM_0435,
+ cWM_0436,
+ cWM_0437,
+ cWM_0438,
+ cWM_0439,
+ cWM_043A,
+ cWM_043B,
+ cWM_043C,
+ cWM_043D,
+ cWM_043E,
+ cWM_043F,
+ cWM_0440,
+ cWM_0441,
+ cWM_0442,
+ cWM_0443,
+ cWM_0444,
+ cWM_0445,
+ cWM_0446,
+ cWM_0447,
+ cWM_0448,
+ cWM_0449,
+ cWM_044A,
+ cWM_044B,
+ cWM_044C,
+ cWM_044D,
+ cWM_044E,
+ cWM_044F,
+ cWM_0450,
+ cWM_0451,
+ cWM_0452,
+ cWM_0453,
+ cWM_0454,
+ cWM_0455,
+ cWM_0456,
+ cWM_0457,
+ cWM_0458,
+ cWM_0459,
+ cWM_045A,
+ cWM_045B,
+ cWM_045C,
+ cWM_045D,
+ cWM_045E,
+ cWM_045F,
+ cWM_0460,
+ cWM_0461,
+ cWM_0462,
+ cWM_0463,
+ cWM_0464,
+ cWM_0465,
+ cWM_0466,
+ cWM_0467,
+ cWM_0468,
+ cWM_0469,
+ cWM_046A,
+ cWM_046B,
+ cWM_046C,
+ cWM_046D,
+ cWM_046E,
+ cWM_046F,
+ cWM_0470,
+ cWM_0471,
+ cWM_0472,
+ cWM_0473,
+ cWM_0474,
+ cWM_0475,
+ cWM_0476,
+ cWM_0477,
+ cWM_0478,
+ cWM_0479,
+ cWM_047A,
+ cWM_047B,
+ cWM_047C,
+ cWM_047D,
+ cWM_047E,
+ cWM_047F,
+ cWM_0480,
+ cWM_0481,
+ cWM_0482,
+ cWM_0483,
+ cWM_0484,
+ cWM_0485,
+ cWM_0486,
+ cWM_0487,
+ cWM_0488,
+ cWM_0489,
+ cWM_048A,
+ cWM_048B,
+ cWM_048C,
+ cWM_048D,
+ cWM_048E,
+ cWM_048F,
+ cWM_0490,
+ cWM_0491,
+ cWM_0492,
+ cWM_0493,
+ cWM_0494,
+ cWM_0495,
+ cWM_0496,
+ cWM_0497,
+ cWM_0498,
+ cWM_0499,
+ cWM_049A,
+ cWM_049B,
+ cWM_049C,
+ cWM_049D,
+ cWM_049E,
+ cWM_049F,
+ cWM_04A0,
+ cWM_04A1,
+ cWM_04A2,
+ cWM_04A3,
+ cWM_04A4,
+ cWM_04A5,
+ cWM_04A6,
+ cWM_04A7,
+ cWM_04A8,
+ cWM_04A9,
+ cWM_04AA,
+ cWM_04AB,
+ cWM_04AC,
+ cWM_04AD,
+ cWM_04AE,
+ cWM_04AF,
+ cWM_04B0,
+ cWM_04B1,
+ cWM_04B2,
+ cWM_04B3,
+ cWM_04B4,
+ cWM_04B5,
+ cWM_04B6,
+ cWM_04B7,
+ cWM_04B8,
+ cWM_04B9,
+ cWM_04BA,
+ cWM_04BB,
+ cWM_04BC,
+ cWM_04BD,
+ cWM_04BE,
+ cWM_04BF,
+ cWM_04C0,
+ cWM_04C1,
+ cWM_04C2,
+ cWM_04C3,
+ cWM_04C4,
+ cWM_04C5,
+ cWM_04C6,
+ cWM_04C7,
+ cWM_04C8,
+ cWM_04C9,
+ cWM_04CA,
+ cWM_04CB,
+ cWM_04CC,
+ cWM_04CD,
+ cWM_04CE,
+ cWM_04CF,
+ cWM_04D0,
+ cWM_04D1,
+ cWM_04D2,
+ cWM_04D3,
+ cWM_04D4,
+ cWM_04D5,
+ cWM_04D6,
+ cWM_04D7,
+ cWM_04D8,
+ cWM_04D9,
+ cWM_04DA,
+ cWM_04DB,
+ cWM_04DC,
+ cWM_04DD,
+ cWM_04DE,
+ cWM_04DF,
+ cWM_04E0,
+ cWM_04E1,
+ cWM_04E2,
+ cWM_04E3,
+ cWM_04E4,
+ cWM_04E5,
+ cWM_04E6,
+ cWM_04E7,
+ cWM_04E8,
+ cWM_04E9,
+ cWM_04EA,
+ cWM_04EB,
+ cWM_04EC,
+ cWM_04ED,
+ cWM_04EE,
+ cWM_04EF,
+ cWM_04F0,
+ cWM_04F1,
+ cWM_04F2,
+ cWM_04F3,
+ cWM_04F4,
+ cWM_04F5,
+ cWM_04F6,
+ cWM_04F7,
+ cWM_04F8,
+ cWM_04F9,
+ cWM_04FA,
+ cWM_04FB,
+ cWM_04FC,
+ cWM_04FD,
+ cWM_04FE,
+ cWM_04FF,
+ cWM_0500,
+ cWM_0501,
+ cWM_0502,
+ cWM_0503,
+ cWM_0504,
+ cWM_0505,
+ cWM_0506,
+ cWM_0507,
+ cWM_0508,
+ cWM_0509,
+ cWM_050A,
+ cWM_050B,
+ cWM_050C,
+ cWM_050D,
+ cWM_050E,
+ cWM_050F,
+ cWM_0510,
+ cWM_0511,
+ cWM_0512,
+ cWM_0513,
+ cWM_0514,
+ cWM_0515,
+ cWM_0516,
+ cWM_0517,
+ cWM_0518,
+ cWM_0519,
+ cWM_051A,
+ cWM_051B,
+ cWM_051C,
+ cWM_051D,
+ cWM_051E,
+ cWM_051F,
+ cWM_0520,
+ cWM_0521,
+ cWM_0522,
+ cWM_0523,
+ cWM_0524,
+ cWM_0525,
+ cWM_0526,
+ cWM_0527,
+ cWM_0528,
+ cWM_0529,
+ cWM_052A,
+ cWM_052B,
+ cWM_052C,
+ cWM_052D,
+ cWM_052E,
+ cWM_052F,
+ cWM_0530,
+ cWM_0531,
+ cWM_0532,
+ cWM_0533,
+ cWM_0534,
+ cWM_0535,
+ cWM_0536,
+ cWM_0537,
+ cWM_0538,
+ cWM_0539,
+ cWM_053A,
+ cWM_053B,
+ cWM_053C,
+ cWM_053D,
+ cWM_053E,
+ cWM_053F,
+ cWM_0540,
+ cWM_0541,
+ cWM_0542,
+ cWM_0543,
+ cWM_0544,
+ cWM_0545,
+ cWM_0546,
+ cWM_0547,
+ cWM_0548,
+ cWM_0549,
+ cWM_054A,
+ cWM_054B,
+ cWM_054C,
+ cWM_054D,
+ cWM_054E,
+ cWM_054F,
+ cWM_0550,
+ cWM_0551,
+ cWM_0552,
+ cWM_0553,
+ cWM_0554,
+ cWM_0555,
+ cWM_0556,
+ cWM_0557,
+ cWM_0558,
+ cWM_0559,
+ cWM_055A,
+ cWM_055B,
+ cWM_055C,
+ cWM_055D,
+ cWM_055E,
+ cWM_055F,
+ cWM_0560,
+ cWM_0561,
+ cWM_0562,
+ cWM_0563,
+ cWM_0564,
+ cWM_0565,
+ cWM_0566,
+ cWM_0567,
+ cWM_0568,
+ cWM_0569,
+ cWM_056A,
+ cWM_056B,
+ cWM_056C,
+ cWM_056D,
+ cWM_056E,
+ cWM_056F,
+ cWM_0570,
+ cWM_0571,
+ cWM_0572,
+ cWM_0573,
+ cWM_0574,
+ cWM_0575,
+ cWM_0576,
+ cWM_0577,
+ cWM_0578,
+ cWM_0579,
+ cWM_057A,
+ cWM_057B,
+ cWM_057C,
+ cWM_057D,
+ cWM_057E,
+ cWM_057F,
+ cWM_0580,
+ cWM_0581,
+ cWM_0582,
+ cWM_0583,
+ cWM_0584,
+ cWM_0585,
+ cWM_0586,
+ cWM_0587,
+ cWM_0588,
+ cWM_0589,
+ cWM_058A,
+ cWM_058B,
+ cWM_058C,
+ cWM_058D,
+ cWM_058E,
+ cWM_058F,
+ cWM_0590,
+ cWM_0591,
+ cWM_0592,
+ cWM_0593,
+ cWM_0594,
+ cWM_0595,
+ cWM_0596,
+ cWM_0597,
+ cWM_0598,
+ cWM_0599,
+ cWM_059A,
+ cWM_059B,
+ cWM_059C,
+ cWM_059D,
+ cWM_059E,
+ cWM_059F,
+ cWM_05A0,
+ cWM_05A1,
+ cWM_05A2,
+ cWM_05A3,
+ cWM_05A4,
+ cWM_05A5,
+ cWM_05A6,
+ cWM_05A7,
+ cWM_05A8,
+ cWM_05A9,
+ cWM_05AA,
+ cWM_05AB,
+ cWM_05AC,
+ cWM_05AD,
+ cWM_05AE,
+ cWM_05AF,
+ cWM_05B0,
+ cWM_05B1,
+ cWM_05B2,
+ cWM_05B3,
+ cWM_05B4,
+ cWM_05B5,
+ cWM_05B6,
+ cWM_05B7,
+ cWM_05B8,
+ cWM_05B9,
+ cWM_05BA,
+ cWM_05BB,
+ cWM_05BC,
+ cWM_05BD,
+ cWM_05BE,
+ cWM_05BF,
+ cWM_05C0,
+ cWM_05C1,
+ cWM_05C2,
+ cWM_05C3,
+ cWM_05C4,
+ cWM_05C5,
+ cWM_05C6,
+ cWM_05C7,
+ cWM_05C8,
+ cWM_05C9,
+ cWM_05CA,
+ cWM_05CB,
+ cWM_05CC,
+ cWM_05CD,
+ cWM_05CE,
+ cWM_05CF,
+ cWM_05D0,
+ cWM_05D1,
+ cWM_05D2,
+ cWM_05D3,
+ cWM_05D4,
+ cWM_05D5,
+ cWM_05D6,
+ cWM_05D7,
+ cWM_05D8,
+ cWM_05D9,
+ cWM_05DA,
+ cWM_05DB,
+ cWM_05DC,
+ cWM_05DD,
+ cWM_05DE,
+ cWM_05DF,
+ cWM_05E0,
+ cWM_05E1,
+ cWM_05E2,
+ cWM_05E3,
+ cWM_05E4,
+ cWM_05E5,
+ cWM_05E6,
+ cWM_05E7,
+ cWM_05E8,
+ cWM_05E9,
+ cWM_05EA,
+ cWM_05EB,
+ cWM_05EC,
+ cWM_05ED,
+ cWM_05EE,
+ cWM_05EF,
+ cWM_05F0,
+ cWM_05F1,
+ cWM_05F2,
+ cWM_05F3,
+ cWM_05F4,
+ cWM_05F5,
+ cWM_05F6,
+ cWM_05F7,
+ cWM_05F8,
+ cWM_05F9,
+ cWM_05FA,
+ cWM_05FB,
+ cWM_05FC,
+ cWM_05FD,
+ cWM_05FE,
+ cWM_05FF,
+ cWM_0600,
+ cWM_0601,
+ cWM_0602,
+ cWM_0603,
+ cWM_0604,
+ cWM_0605,
+ cWM_0606,
+ cWM_0607,
+ cWM_0608,
+ cWM_0609,
+ cWM_060A,
+ cWM_060B,
+ cWM_060C,
+ cWM_060D,
+ cWM_060E,
+ cWM_060F,
+ cWM_0610,
+ cWM_0611,
+ cWM_0612,
+ cWM_0613,
+ cWM_0614,
+ cWM_0615,
+ cWM_0616,
+ cWM_0617,
+ cWM_0618,
+ cWM_0619,
+ cWM_061A,
+ cWM_061B,
+ cWM_061C,
+ cWM_061D,
+ cWM_061E,
+ cWM_061F,
+ cWM_0620,
+ cWM_0621,
+ cWM_0622,
+ cWM_0623,
+ cWM_0624,
+ cWM_0625,
+ cWM_0626,
+ cWM_0627,
+ cWM_0628,
+ cWM_0629,
+ cWM_062A,
+ cWM_062B,
+ cWM_062C,
+ cWM_062D,
+ cWM_062E,
+ cWM_062F,
+ cWM_0630,
+ cWM_0631,
+ cWM_0632,
+ cWM_0633,
+ cWM_0634,
+ cWM_0635,
+ cWM_0636,
+ cWM_0637,
+ cWM_0638,
+ cWM_0639,
+ cWM_063A,
+ cWM_063B,
+ cWM_063C,
+ cWM_063D,
+ cWM_063E,
+ cWM_063F,
+ cWM_0640,
+ cWM_0641,
+ cWM_0642,
+ cWM_0643,
+ cWM_0644,
+ cWM_0645,
+ cWM_0646,
+ cWM_0647,
+ cWM_0648,
+ cWM_0649,
+ cWM_064A,
+ cWM_064B,
+ cWM_064C,
+ cWM_064D,
+ cWM_064E,
+ cWM_064F,
+ cWM_0650,
+ cWM_0651,
+ cWM_0652,
+ cWM_0653,
+ cWM_0654,
+ cWM_0655,
+ cWM_0656,
+ cWM_0657,
+ cWM_0658,
+ cWM_0659,
+ cWM_065A,
+ cWM_065B,
+ cWM_065C,
+ cWM_065D,
+ cWM_065E,
+ cWM_065F,
+ cWM_0660,
+ cWM_0661,
+ cWM_0662,
+ cWM_0663,
+ cWM_0664,
+ cWM_0665,
+ cWM_0666,
+ cWM_0667,
+ cWM_0668,
+ cWM_0669,
+ cWM_066A,
+ cWM_066B,
+ cWM_066C,
+ cWM_066D,
+ cWM_066E,
+ cWM_066F,
+ cWM_0670,
+ cWM_0671,
+ cWM_0672,
+ cWM_0673,
+ cWM_0674,
+ cWM_0675,
+ cWM_0676,
+ cWM_0677,
+ cWM_0678,
+ cWM_0679,
+ cWM_067A,
+ cWM_067B,
+ cWM_067C,
+ cWM_067D,
+ cWM_067E,
+ cWM_067F,
+ cWM_0680,
+ cWM_0681,
+ cWM_0682,
+ cWM_0683,
+ cWM_0684,
+ cWM_0685,
+ cWM_0686,
+ cWM_0687,
+ cWM_0688,
+ cWM_0689,
+ cWM_068A,
+ cWM_068B,
+ cWM_068C,
+ cWM_068D,
+ cWM_068E,
+ cWM_068F,
+ cWM_0690,
+ cWM_0691,
+ cWM_0692,
+ cWM_0693,
+ cWM_0694,
+ cWM_0695,
+ cWM_0696,
+ cWM_0697,
+ cWM_0698,
+ cWM_0699,
+ cWM_069A,
+ cWM_069B,
+ cWM_069C,
+ cWM_069D,
+ cWM_069E,
+ cWM_069F,
+ cWM_06A0,
+ cWM_06A1,
+ cWM_06A2,
+ cWM_06A3,
+ cWM_06A4,
+ cWM_06A5,
+ cWM_06A6,
+ cWM_06A7,
+ cWM_06A8,
+ cWM_06A9,
+ cWM_06AA,
+ cWM_06AB,
+ cWM_06AC,
+ cWM_06AD,
+ cWM_06AE,
+ cWM_06AF,
+ cWM_06B0,
+ cWM_06B1,
+ cWM_06B2,
+ cWM_06B3,
+ cWM_06B4,
+ cWM_06B5,
+ cWM_06B6,
+ cWM_06B7,
+ cWM_06B8,
+ cWM_06B9,
+ cWM_06BA,
+ cWM_06BB,
+ cWM_06BC,
+ cWM_06BD,
+ cWM_06BE,
+ cWM_06BF,
+ cWM_06C0,
+ cWM_06C1,
+ cWM_06C2,
+ cWM_06C3,
+ cWM_06C4,
+ cWM_06C5,
+ cWM_06C6,
+ cWM_06C7,
+ cWM_06C8,
+ cWM_06C9,
+ cWM_06CA,
+ cWM_06CB,
+ cWM_06CC,
+ cWM_06CD,
+ cWM_06CE,
+ cWM_06CF,
+ cWM_06D0,
+ cWM_06D1,
+ cWM_06D2,
+ cWM_06D3,
+ cWM_06D4,
+ cWM_06D5,
+ cWM_06D6,
+ cWM_06D7,
+ cWM_06D8,
+ cWM_06D9,
+ cWM_06DA,
+ cWM_06DB,
+ cWM_06DC,
+ cWM_06DD,
+ cWM_06DE,
+ cWM_06DF,
+ cWM_06E0,
+ cWM_06E1,
+ cWM_06E2,
+ cWM_06E3,
+ cWM_06E4,
+ cWM_06E5,
+ cWM_06E6,
+ cWM_06E7,
+ cWM_06E8,
+ cWM_06E9,
+ cWM_06EA,
+ cWM_06EB,
+ cWM_06EC,
+ cWM_06ED,
+ cWM_06EE,
+ cWM_06EF,
+ cWM_06F0,
+ cWM_06F1,
+ cWM_06F2,
+ cWM_06F3,
+ cWM_06F4,
+ cWM_06F5,
+ cWM_06F6,
+ cWM_06F7,
+ cWM_06F8,
+ cWM_06F9,
+ cWM_06FA,
+ cWM_06FB,
+ cWM_06FC,
+ cWM_06FD,
+ cWM_06FE,
+ cWM_06FF,
+ cWM_0700,
+ cWM_0701,
+ cWM_0702,
+ cWM_0703,
+ cWM_0704,
+ cWM_0705,
+ cWM_0706,
+ cWM_0707,
+ cWM_0708,
+ cWM_0709,
+ cWM_070A,
+ cWM_070B,
+ cWM_070C,
+ cWM_070D,
+ cWM_070E,
+ cWM_070F,
+ cWM_0710,
+ cWM_0711,
+ cWM_0712,
+ cWM_0713,
+ cWM_0714,
+ cWM_0715,
+ cWM_0716,
+ cWM_0717,
+ cWM_0718,
+ cWM_0719,
+ cWM_071A,
+ cWM_071B,
+ cWM_071C,
+ cWM_071D,
+ cWM_071E,
+ cWM_071F,
+ cWM_0720,
+ cWM_0721,
+ cWM_0722,
+ cWM_0723,
+ cWM_0724,
+ cWM_0725,
+ cWM_0726,
+ cWM_0727,
+ cWM_0728,
+ cWM_0729,
+ cWM_072A,
+ cWM_072B,
+ cWM_072C,
+ cWM_072D,
+ cWM_072E,
+ cWM_072F,
+ cWM_0730,
+ cWM_0731,
+ cWM_0732,
+ cWM_0733,
+ cWM_0734,
+ cWM_0735,
+ cWM_0736,
+ cWM_0737,
+ cWM_0738,
+ cWM_0739,
+ cWM_073A,
+ cWM_073B,
+ cWM_073C,
+ cWM_073D,
+ cWM_073E,
+ cWM_073F,
+ cWM_0740,
+ cWM_0741,
+ cWM_0742,
+ cWM_0743,
+ cWM_0744,
+ cWM_0745,
+ cWM_0746,
+ cWM_0747,
+ cWM_0748,
+ cWM_0749,
+ cWM_074A,
+ cWM_074B,
+ cWM_074C,
+ cWM_074D,
+ cWM_074E,
+ cWM_074F,
+ cWM_0750,
+ cWM_0751,
+ cWM_0752,
+ cWM_0753,
+ cWM_0754,
+ cWM_0755,
+ cWM_0756,
+ cWM_0757,
+ cWM_0758,
+ cWM_0759,
+ cWM_075A,
+ cWM_075B,
+ cWM_075C,
+ cWM_075D,
+ cWM_075E,
+ cWM_075F,
+ cWM_0760,
+ cWM_0761,
+ cWM_0762,
+ cWM_0763,
+ cWM_0764,
+ cWM_0765,
+ cWM_0766,
+ cWM_0767,
+ cWM_0768,
+ cWM_0769,
+ cWM_076A,
+ cWM_076B,
+ cWM_076C,
+ cWM_076D,
+ cWM_076E,
+ cWM_076F,
+ cWM_0770,
+ cWM_0771,
+ cWM_0772,
+ cWM_0773,
+ cWM_0774,
+ cWM_0775,
+ cWM_0776,
+ cWM_0777,
+ cWM_0778,
+ cWM_0779,
+ cWM_077A,
+ cWM_077B,
+ cWM_077C,
+ cWM_077D,
+ cWM_077E,
+ cWM_077F,
+ cWM_0780,
+ cWM_0781,
+ cWM_0782,
+ cWM_0783,
+ cWM_0784,
+ cWM_0785,
+ cWM_0786,
+ cWM_0787,
+ cWM_0788,
+ cWM_0789,
+ cWM_078A,
+ cWM_078B,
+ cWM_078C,
+ cWM_078D,
+ cWM_078E,
+ cWM_078F,
+ cWM_0790,
+ cWM_PRINT, // = 791;
+ cWM_PRINTCLIENT, // = 792;
+ cWM_0793,
+ cWM_0794,
+ cWM_0795,
+ cWM_0796,
+ cWM_0797,
+ cWM_0798,
+ cWM_0799,
+ cWM_079A,
+ cWM_079B,
+ cWM_079C,
+ cWM_079D,
+ cWM_079E,
+ cWM_079F,
+ cWM_07A0,
+ cWM_07A1,
+ cWM_07A2,
+ cWM_07A3,
+ cWM_07A4,
+ cWM_07A5,
+ cWM_07A6,
+ cWM_07A7,
+ cWM_07A8,
+ cWM_07A9,
+ cWM_07AA,
+ cWM_07AB,
+ cWM_07AC,
+ cWM_07AD,
+ cWM_07AE,
+ cWM_07AF,
+ cWM_07B0,
+ cWM_07B1,
+ cWM_07B2,
+ cWM_07B3,
+ cWM_07B4,
+ cWM_07B5,
+ cWM_07B6,
+ cWM_07B7,
+ cWM_07B8,
+ cWM_07B9,
+ cWM_07BA,
+ cWM_07BB,
+ cWM_07BC,
+ cWM_07BD,
+ cWM_07BE,
+ cWM_07BF,
+ cWM_07C0,
+ cWM_07C1,
+ cWM_07C2,
+ cWM_07C3,
+ cWM_07C4,
+ cWM_07C5,
+ cWM_07C6,
+ cWM_07C7,
+ cWM_07C8,
+ cWM_07C9,
+ cWM_07CA,
+ cWM_07CB,
+ cWM_07CC,
+ cWM_07CD,
+ cWM_07CE,
+ cWM_07CF,
+ cWM_07D0,
+ cWM_07D1,
+ cWM_07D2,
+ cWM_07D3,
+ cWM_07D4,
+ cWM_07D5,
+ cWM_07D6,
+ cWM_07D7,
+ cWM_07D8,
+ cWM_07D9,
+ cWM_07DA,
+ cWM_07DB,
+ cWM_07DC,
+ cWM_07DD,
+ cWM_07DE,
+ cWM_07DF,
+ cWM_07E0,
+ cWM_07E1,
+ cWM_07E2,
+ cWM_07E3,
+ cWM_07E4,
+ cWM_07E5,
+ cWM_07E6,
+ cWM_07E7,
+ cWM_07E8,
+ cWM_07E9,
+ cWM_07EA,
+ cWM_07EB,
+ cWM_07EC,
+ cWM_07ED,
+ cWM_07EE,
+ cWM_07EF,
+ cWM_07F0,
+ cWM_07F1,
+ cWM_07F2,
+ cWM_07F3,
+ cWM_07F4,
+ cWM_07F5,
+ cWM_07F6,
+ cWM_07F7,
+ cWM_07F8,
+ cWM_07F9,
+ cWM_07FA,
+ cWM_07FB,
+ cWM_07FC,
+ cWM_07FD,
+ cWM_07FE,
+ cWM_07FF,
+ cWM_0800,
+ cWM_0801,
+ cWM_0802,
+ cWM_0803,
+ cWM_0804,
+ cWM_0805,
+ cWM_0806,
+ cWM_0807,
+ cWM_0808,
+ cWM_0809,
+ cWM_080A,
+ cWM_080B,
+ cWM_080C,
+ cWM_080D,
+ cWM_080E,
+ cWM_080F,
+ cWM_0810,
+ cWM_0811,
+ cWM_0812,
+ cWM_0813,
+ cWM_0814,
+ cWM_0815,
+ cWM_0816,
+ cWM_0817,
+ cWM_0818,
+ cWM_0819,
+ cWM_081A,
+ cWM_081B,
+ cWM_081C,
+ cWM_081D,
+ cWM_081E,
+ cWM_081F,
+ cWM_0820,
+ cWM_0821,
+ cWM_0822,
+ cWM_0823,
+ cWM_0824,
+ cWM_0825,
+ cWM_0826,
+ cWM_0827,
+ cWM_0828,
+ cWM_0829,
+ cWM_082A,
+ cWM_082B,
+ cWM_082C,
+ cWM_082D,
+ cWM_082E,
+ cWM_082F,
+ cWM_0830,
+ cWM_0831,
+ cWM_0832,
+ cWM_0833,
+ cWM_0834,
+ cWM_0835,
+ cWM_0836,
+ cWM_0837,
+ cWM_0838,
+ cWM_0839,
+ cWM_083A,
+ cWM_083B,
+ cWM_083C,
+ cWM_083D,
+ cWM_083E,
+ cWM_083F,
+ cWM_0840,
+ cWM_0841,
+ cWM_0842,
+ cWM_0843,
+ cWM_0844,
+ cWM_0845,
+ cWM_0846,
+ cWM_0847,
+ cWM_0848,
+ cWM_0849,
+ cWM_084A,
+ cWM_084B,
+ cWM_084C,
+ cWM_084D,
+ cWM_084E,
+ cWM_084F,
+ cWM_0850,
+ cWM_0851,
+ cWM_0852,
+ cWM_0853,
+ cWM_0854,
+ cWM_0855,
+ cWM_HANDHELDFIRST, // = 856;
+ cWM_0857,
+ cWM_0858,
+ cWM_0859,
+ cWM_085A,
+ cWM_085B,
+ cWM_085C,
+ cWM_085D,
+ cWM_085E,
+ cWM_085F,
+ cWM_0860,
+ cWM_0861,
+ cWM_0862,
+ cWM_HANDHELDLAST, // = 863;
+ cWM_0864,
+ cWM_0865,
+ cWM_0866,
+ cWM_0867,
+ cWM_0868,
+ cWM_0869,
+ cWM_086A,
+ cWM_086B,
+ cWM_086C,
+ cWM_086D,
+ cWM_086E,
+ cWM_086F,
+ cWM_0870,
+ cWM_0871,
+ cWM_0872,
+ cWM_0873,
+ cWM_0874,
+ cWM_0875,
+ cWM_0876,
+ cWM_0877,
+ cWM_0878,
+ cWM_0879,
+ cWM_087A,
+ cWM_087B,
+ cWM_087C,
+ cWM_087D,
+ cWM_087E,
+ cWM_087F,
+ cWM_0880,
+ cWM_0881,
+ cWM_0882,
+ cWM_0883,
+ cWM_0884,
+ cWM_0885,
+ cWM_0886,
+ cWM_0887,
+ cWM_0888,
+ cWM_0889,
+ cWM_088A,
+ cWM_088B,
+ cWM_088C,
+ cWM_088D,
+ cWM_088E,
+ cWM_088F,
+ cWM_0890,
+ cWM_0891,
+ cWM_0892,
+ cWM_0893,
+ cWM_0894,
+ cWM_0895,
+ cWM_0896,
+ cWM_0897,
+ cWM_0898,
+ cWM_0899,
+ cWM_089A,
+ cWM_089B,
+ cWM_089C,
+ cWM_089D,
+ cWM_089E,
+ cWM_089F,
+ cWM_08A0,
+ cWM_08A1,
+ cWM_08A2,
+ cWM_08A3,
+ cWM_08A4,
+ cWM_08A5,
+ cWM_08A6,
+ cWM_08A7,
+ cWM_08A8,
+ cWM_08A9,
+ cWM_08AA,
+ cWM_08AB,
+ cWM_08AC,
+ cWM_08AD,
+ cWM_08AE,
+ cWM_08AF,
+ cWM_08B0,
+ cWM_08B1,
+ cWM_08B2,
+ cWM_08B3,
+ cWM_08B4,
+ cWM_08B5,
+ cWM_08B6,
+ cWM_08B7,
+ cWM_08B8,
+ cWM_08B9,
+ cWM_08BA,
+ cWM_08BB,
+ cWM_08BC,
+ cWM_08BD,
+ cWM_08BE,
+ cWM_08BF,
+ cWM_08C0,
+ cWM_08C1,
+ cWM_08C2,
+ cWM_08C3,
+ cWM_08C4,
+ cWM_08C5,
+ cWM_08C6,
+ cWM_08C7,
+ cWM_08C8,
+ cWM_08C9,
+ cWM_08CA,
+ cWM_08CB,
+ cWM_08CC,
+ cWM_08CD,
+ cWM_08CE,
+ cWM_08CF,
+ cWM_08D0,
+ cWM_08D1,
+ cWM_08D2,
+ cWM_08D3,
+ cWM_08D4,
+ cWM_08D5,
+ cWM_08D6,
+ cWM_08D7,
+ cWM_08D8,
+ cWM_08D9,
+ cWM_08DA,
+ cWM_08DB,
+ cWM_08DC,
+ cWM_08DD,
+ cWM_08DE,
+ cWM_08DF,
+ cWM_08E0,
+ cWM_08E1,
+ cWM_08E2,
+ cWM_08E3,
+ cWM_08E4,
+ cWM_08E5,
+ cWM_08E6,
+ cWM_08E7,
+ cWM_08E8,
+ cWM_08E9,
+ cWM_08EA,
+ cWM_08EB,
+ cWM_08EC,
+ cWM_08ED,
+ cWM_08EE,
+ cWM_08EF,
+ cWM_08F0,
+ cWM_08F1,
+ cWM_08F2,
+ cWM_08F3,
+ cWM_08F4,
+ cWM_08F5,
+ cWM_08F6,
+ cWM_08F7,
+ cWM_08F8,
+ cWM_08F9,
+ cWM_08FA,
+ cWM_08FB,
+ cWM_08FC,
+ cWM_08FD,
+ cWM_08FE,
+ cWM_08FF,
+ cWM_0900,
+ cWM_0901,
+ cWM_0902,
+ cWM_0903,
+ cWM_0904,
+ cWM_0905,
+ cWM_0906,
+ cWM_0907,
+ cWM_0908,
+ cWM_0909,
+ cWM_090A,
+ cWM_090B,
+ cWM_090C,
+ cWM_090D,
+ cWM_090E,
+ cWM_090F,
+ cWM_0910,
+ cWM_0911,
+ cWM_0912,
+ cWM_0913,
+ cWM_0914,
+ cWM_0915,
+ cWM_0916,
+ cWM_0917,
+ cWM_0918,
+ cWM_0919,
+ cWM_091A,
+ cWM_091B,
+ cWM_091C,
+ cWM_091D,
+ cWM_091E,
+ cWM_091F,
+ cWM_0920,
+ cWM_0921,
+ cWM_0922,
+ cWM_0923,
+ cWM_0924,
+ cWM_0925,
+ cWM_0926,
+ cWM_0927,
+ cWM_0928,
+ cWM_0929,
+ cWM_092A,
+ cWM_092B,
+ cWM_092C,
+ cWM_092D,
+ cWM_092E,
+ cWM_092F,
+ cWM_0930,
+ cWM_0931,
+ cWM_0932,
+ cWM_0933,
+ cWM_0934,
+ cWM_0935,
+ cWM_0936,
+ cWM_0937,
+ cWM_0938,
+ cWM_0939,
+ cWM_093A,
+ cWM_093B,
+ cWM_093C,
+ cWM_093D,
+ cWM_093E,
+ cWM_093F,
+ cWM_0940,
+ cWM_0941,
+ cWM_0942,
+ cWM_0943,
+ cWM_0944,
+ cWM_0945,
+ cWM_0946,
+ cWM_0947,
+ cWM_0948,
+ cWM_0949,
+ cWM_094A,
+ cWM_094B,
+ cWM_094C,
+ cWM_094D,
+ cWM_094E,
+ cWM_094F,
+ cWM_0950,
+ cWM_0951,
+ cWM_0952,
+ cWM_0953,
+ cWM_0954,
+ cWM_0955,
+ cWM_0956,
+ cWM_0957,
+ cWM_0958,
+ cWM_0959,
+ cWM_095A,
+ cWM_095B,
+ cWM_095C,
+ cWM_095D,
+ cWM_095E,
+ cWM_095F,
+ cWM_0960,
+ cWM_0961,
+ cWM_0962,
+ cWM_0963,
+ cWM_0964,
+ cWM_0965,
+ cWM_0966,
+ cWM_0967,
+ cWM_0968,
+ cWM_0969,
+ cWM_096A,
+ cWM_096B,
+ cWM_096C,
+ cWM_096D,
+ cWM_096E,
+ cWM_096F,
+ cWM_0970,
+ cWM_0971,
+ cWM_0972,
+ cWM_0973,
+ cWM_0974,
+ cWM_0975,
+ cWM_0976,
+ cWM_0977,
+ cWM_0978,
+ cWM_0979,
+ cWM_097A,
+ cWM_097B,
+ cWM_097C,
+ cWM_097D,
+ cWM_097E,
+ cWM_097F,
+ cWM_0980,
+ cWM_0981,
+ cWM_0982,
+ cWM_0983,
+ cWM_0984,
+ cWM_0985,
+ cWM_0986,
+ cWM_0987,
+ cWM_0988,
+ cWM_0989,
+ cWM_098A,
+ cWM_098B,
+ cWM_098C,
+ cWM_098D,
+ cWM_098E,
+ cWM_098F,
+ cWM_0990,
+ cWM_0991,
+ cWM_0992,
+ cWM_0993,
+ cWM_0994,
+ cWM_0995,
+ cWM_0996,
+ cWM_0997,
+ cWM_0998,
+ cWM_0999,
+ cWM_099A,
+ cWM_099B,
+ cWM_099C,
+ cWM_099D,
+ cWM_099E,
+ cWM_099F,
+ cWM_09A0,
+ cWM_09A1,
+ cWM_09A2,
+ cWM_09A3,
+ cWM_09A4,
+ cWM_09A5,
+ cWM_09A6,
+ cWM_09A7,
+ cWM_09A8,
+ cWM_09A9,
+ cWM_09AA,
+ cWM_09AB,
+ cWM_09AC,
+ cWM_09AD,
+ cWM_09AE,
+ cWM_09AF,
+ cWM_09B0,
+ cWM_09B1,
+ cWM_09B2,
+ cWM_09B3,
+ cWM_09B4,
+ cWM_09B5,
+ cWM_09B6,
+ cWM_09B7,
+ cWM_09B8,
+ cWM_09B9,
+ cWM_09BA,
+ cWM_09BB,
+ cWM_09BC,
+ cWM_09BD,
+ cWM_09BE,
+ cWM_09BF,
+ cWM_09C0,
+ cWM_09C1,
+ cWM_09C2,
+ cWM_09C3,
+ cWM_09C4,
+ cWM_09C5,
+ cWM_09C6,
+ cWM_09C7,
+ cWM_09C8,
+ cWM_09C9,
+ cWM_09CA,
+ cWM_09CB,
+ cWM_09CC,
+ cWM_09CD,
+ cWM_09CE,
+ cWM_09CF,
+ cWM_09D0,
+ cWM_09D1,
+ cWM_09D2,
+ cWM_09D3,
+ cWM_09D4,
+ cWM_09D5,
+ cWM_09D6,
+ cWM_09D7,
+ cWM_09D8,
+ cWM_09D9,
+ cWM_09DA,
+ cWM_09DB,
+ cWM_09DC,
+ cWM_09DD,
+ cWM_09DE,
+ cWM_09DF,
+ cWM_09E0,
+ cWM_09E1,
+ cWM_09E2,
+ cWM_09E3,
+ cWM_09E4,
+ cWM_09E5,
+ cWM_09E6,
+ cWM_09E7,
+ cWM_09E8,
+ cWM_09E9,
+ cWM_09EA,
+ cWM_09EB,
+ cWM_09EC,
+ cWM_09ED,
+ cWM_09EE,
+ cWM_09EF,
+ cWM_09F0,
+ cWM_09F1,
+ cWM_09F2,
+ cWM_09F3,
+ cWM_09F4,
+ cWM_09F5,
+ cWM_09F6,
+ cWM_09F7,
+ cWM_09F8,
+ cWM_09F9,
+ cWM_09FA,
+ cWM_09FB,
+ cWM_09FC,
+ cWM_09FD,
+ cWM_09FE,
+ cWM_09FF,
+ cWM_0A00,
+ cWM_0A01,
+ cWM_0A02,
+ cWM_0A03,
+ cWM_0A04,
+ cWM_0A05,
+ cWM_0A06,
+ cWM_0A07,
+ cWM_0A08,
+ cWM_0A09,
+ cWM_0A0A,
+ cWM_0A0B,
+ cWM_0A0C,
+ cWM_0A0D,
+ cWM_0A0E,
+ cWM_0A0F,
+ cWM_0A10,
+ cWM_0A11,
+ cWM_0A12,
+ cWM_0A13,
+ cWM_0A14,
+ cWM_0A15,
+ cWM_0A16,
+ cWM_0A17,
+ cWM_0A18,
+ cWM_0A19,
+ cWM_0A1A,
+ cWM_0A1B,
+ cWM_0A1C,
+ cWM_0A1D,
+ cWM_0A1E,
+ cWM_0A1F,
+ cWM_0A20,
+ cWM_0A21,
+ cWM_0A22,
+ cWM_0A23,
+ cWM_0A24,
+ cWM_0A25,
+ cWM_0A26,
+ cWM_0A27,
+ cWM_0A28,
+ cWM_0A29,
+ cWM_0A2A,
+ cWM_0A2B,
+ cWM_0A2C,
+ cWM_0A2D,
+ cWM_0A2E,
+ cWM_0A2F,
+ cWM_0A30,
+ cWM_0A31,
+ cWM_0A32,
+ cWM_0A33,
+ cWM_0A34,
+ cWM_0A35,
+ cWM_0A36,
+ cWM_0A37,
+ cWM_0A38,
+ cWM_0A39,
+ cWM_0A3A,
+ cWM_0A3B,
+ cWM_0A3C,
+ cWM_0A3D,
+ cWM_0A3E,
+ cWM_0A3F,
+ cWM_0A40,
+ cWM_0A41,
+ cWM_0A42,
+ cWM_0A43,
+ cWM_0A44,
+ cWM_0A45,
+ cWM_0A46,
+ cWM_0A47,
+ cWM_0A48,
+ cWM_0A49,
+ cWM_0A4A,
+ cWM_0A4B,
+ cWM_0A4C,
+ cWM_0A4D,
+ cWM_0A4E,
+ cWM_0A4F,
+ cWM_0A50,
+ cWM_0A51,
+ cWM_0A52,
+ cWM_0A53,
+ cWM_0A54,
+ cWM_0A55,
+ cWM_0A56,
+ cWM_0A57,
+ cWM_0A58,
+ cWM_0A59,
+ cWM_0A5A,
+ cWM_0A5B,
+ cWM_0A5C,
+ cWM_0A5D,
+ cWM_0A5E,
+ cWM_0A5F,
+ cWM_0A60,
+ cWM_0A61,
+ cWM_0A62,
+ cWM_0A63,
+ cWM_0A64,
+ cWM_0A65,
+ cWM_0A66,
+ cWM_0A67,
+ cWM_0A68,
+ cWM_0A69,
+ cWM_0A6A,
+ cWM_0A6B,
+ cWM_0A6C,
+ cWM_0A6D,
+ cWM_0A6E,
+ cWM_0A6F,
+ cWM_0A70,
+ cWM_0A71,
+ cWM_0A72,
+ cWM_0A73,
+ cWM_0A74,
+ cWM_0A75,
+ cWM_0A76,
+ cWM_0A77,
+ cWM_0A78,
+ cWM_0A79,
+ cWM_0A7A,
+ cWM_0A7B,
+ cWM_0A7C,
+ cWM_0A7D,
+ cWM_0A7E,
+ cWM_0A7F,
+ cWM_0A80,
+ cWM_0A81,
+ cWM_0A82,
+ cWM_0A83,
+ cWM_0A84,
+ cWM_0A85,
+ cWM_0A86,
+ cWM_0A87,
+ cWM_0A88,
+ cWM_0A89,
+ cWM_0A8A,
+ cWM_0A8B,
+ cWM_0A8C,
+ cWM_0A8D,
+ cWM_0A8E,
+ cWM_0A8F,
+ cWM_0A90,
+ cWM_0A91,
+ cWM_0A92,
+ cWM_0A93,
+ cWM_0A94,
+ cWM_0A95,
+ cWM_0A96,
+ cWM_0A97,
+ cWM_0A98,
+ cWM_0A99,
+ cWM_0A9A,
+ cWM_0A9B,
+ cWM_0A9C,
+ cWM_0A9D,
+ cWM_0A9E,
+ cWM_0A9F,
+ cWM_0AA0,
+ cWM_0AA1,
+ cWM_0AA2,
+ cWM_0AA3,
+ cWM_0AA4,
+ cWM_0AA5,
+ cWM_0AA6,
+ cWM_0AA7,
+ cWM_0AA8,
+ cWM_0AA9,
+ cWM_0AAA,
+ cWM_0AAB,
+ cWM_0AAC,
+ cWM_0AAD,
+ cWM_0AAE,
+ cWM_0AAF,
+ cWM_0AB0,
+ cWM_0AB1,
+ cWM_0AB2,
+ cWM_0AB3,
+ cWM_0AB4,
+ cWM_0AB5,
+ cWM_0AB6,
+ cWM_0AB7,
+ cWM_0AB8,
+ cWM_0AB9,
+ cWM_0ABA,
+ cWM_0ABB,
+ cWM_0ABC,
+ cWM_0ABD,
+ cWM_0ABE,
+ cWM_0ABF,
+ cWM_0AC0,
+ cWM_0AC1,
+ cWM_0AC2,
+ cWM_0AC3,
+ cWM_0AC4,
+ cWM_0AC5,
+ cWM_0AC6,
+ cWM_0AC7,
+ cWM_0AC8,
+ cWM_0AC9,
+ cWM_0ACA,
+ cWM_0ACB,
+ cWM_0ACC,
+ cWM_0ACD,
+ cWM_0ACE,
+ cWM_0ACF,
+ cWM_0AD0,
+ cWM_0AD1,
+ cWM_0AD2,
+ cWM_0AD3,
+ cWM_0AD4,
+ cWM_0AD5,
+ cWM_0AD6,
+ cWM_0AD7,
+ cWM_0AD8,
+ cWM_0AD9,
+ cWM_0ADA,
+ cWM_0ADB,
+ cWM_0ADC,
+ cWM_0ADD,
+ cWM_0ADE,
+ cWM_0ADF,
+ cWM_0AE0,
+ cWM_0AE1,
+ cWM_0AE2,
+ cWM_0AE3,
+ cWM_0AE4,
+ cWM_0AE5,
+ cWM_0AE6,
+ cWM_0AE7,
+ cWM_0AE8,
+ cWM_0AE9,
+ cWM_0AEA,
+ cWM_0AEB,
+ cWM_0AEC,
+ cWM_0AED,
+ cWM_0AEE,
+ cWM_0AEF,
+ cWM_0AF0,
+ cWM_0AF1,
+ cWM_0AF2,
+ cWM_0AF3,
+ cWM_0AF4,
+ cWM_0AF5,
+ cWM_0AF6,
+ cWM_0AF7,
+ cWM_0AF8,
+ cWM_0AF9,
+ cWM_0AFA,
+ cWM_0AFB,
+ cWM_0AFC,
+ cWM_0AFD,
+ cWM_0AFE,
+ cWM_0AFF,
+ cWM_0B00,
+ cWM_0B01,
+ cWM_0B02,
+ cWM_0B03,
+ cWM_0B04,
+ cWM_0B05,
+ cWM_0B06,
+ cWM_0B07,
+ cWM_0B08,
+ cWM_0B09,
+ cWM_0B0A,
+ cWM_0B0B,
+ cWM_0B0C,
+ cWM_0B0D,
+ cWM_0B0E,
+ cWM_0B0F,
+ cWM_0B10,
+ cWM_0B11,
+ cWM_0B12,
+ cWM_0B13,
+ cWM_0B14,
+ cWM_0B15,
+ cWM_0B16,
+ cWM_0B17,
+ cWM_0B18,
+ cWM_0B19,
+ cWM_0B1A,
+ cWM_0B1B,
+ cWM_0B1C,
+ cWM_0B1D,
+ cWM_0B1E,
+ cWM_0B1F,
+ cWM_0B20,
+ cWM_0B21,
+ cWM_0B22,
+ cWM_0B23,
+ cWM_0B24,
+ cWM_0B25,
+ cWM_0B26,
+ cWM_0B27,
+ cWM_0B28,
+ cWM_0B29,
+ cWM_0B2A,
+ cWM_0B2B,
+ cWM_0B2C,
+ cWM_0B2D,
+ cWM_0B2E,
+ cWM_0B2F,
+ cWM_0B30,
+ cWM_0B31,
+ cWM_0B32,
+ cWM_0B33,
+ cWM_0B34,
+ cWM_0B35,
+ cWM_0B36,
+ cWM_0B37,
+ cWM_0B38,
+ cWM_0B39,
+ cWM_0B3A,
+ cWM_0B3B,
+ cWM_0B3C,
+ cWM_0B3D,
+ cWM_0B3E,
+ cWM_0B3F,
+ cWM_0B40,
+ cWM_0B41,
+ cWM_0B42,
+ cWM_0B43,
+ cWM_0B44,
+ cWM_0B45,
+ cWM_0B46,
+ cWM_0B47,
+ cWM_0B48,
+ cWM_0B49,
+ cWM_0B4A,
+ cWM_0B4B,
+ cWM_0B4C,
+ cWM_0B4D,
+ cWM_0B4E,
+ cWM_0B4F,
+ cWM_0B50,
+ cWM_0B51,
+ cWM_0B52,
+ cWM_0B53,
+ cWM_0B54,
+ cWM_0B55,
+ cWM_0B56,
+ cWM_0B57,
+ cWM_0B58,
+ cWM_0B59,
+ cWM_0B5A,
+ cWM_0B5B,
+ cWM_0B5C,
+ cWM_0B5D,
+ cWM_0B5E,
+ cWM_0B5F,
+ cWM_0B60,
+ cWM_0B61,
+ cWM_0B62,
+ cWM_0B63,
+ cWM_0B64,
+ cWM_0B65,
+ cWM_0B66,
+ cWM_0B67,
+ cWM_0B68,
+ cWM_0B69,
+ cWM_0B6A,
+ cWM_0B6B,
+ cWM_0B6C,
+ cWM_0B6D,
+ cWM_0B6E,
+ cWM_0B6F,
+ cWM_0B70,
+ cWM_0B71,
+ cWM_0B72,
+ cWM_0B73,
+ cWM_0B74,
+ cWM_0B75,
+ cWM_0B76,
+ cWM_0B77,
+ cWM_0B78,
+ cWM_0B79,
+ cWM_0B7A,
+ cWM_0B7B,
+ cWM_0B7C,
+ cWM_0B7D,
+ cWM_0B7E,
+ cWM_0B7F,
+ cWM_0B80,
+ cWM_0B81,
+ cWM_0B82,
+ cWM_0B83,
+ cWM_0B84,
+ cWM_0B85,
+ cWM_0B86,
+ cWM_0B87,
+ cWM_0B88,
+ cWM_0B89,
+ cWM_0B8A,
+ cWM_0B8B,
+ cWM_0B8C,
+ cWM_0B8D,
+ cWM_0B8E,
+ cWM_0B8F,
+ cWM_0B90,
+ cWM_0B91,
+ cWM_0B92,
+ cWM_0B93,
+ cWM_0B94,
+ cWM_0B95,
+ cWM_0B96,
+ cWM_0B97,
+ cWM_0B98,
+ cWM_0B99,
+ cWM_0B9A,
+ cWM_0B9B,
+ cWM_0B9C,
+ cWM_0B9D,
+ cWM_0B9E,
+ cWM_0B9F,
+ cWM_0BA0,
+ cWM_0BA1,
+ cWM_0BA2,
+ cWM_0BA3,
+ cWM_0BA4,
+ cWM_0BA5,
+ cWM_0BA6,
+ cWM_0BA7,
+ cWM_0BA8,
+ cWM_0BA9,
+ cWM_0BAA,
+ cWM_0BAB,
+ cWM_0BAC,
+ cWM_0BAD,
+ cWM_0BAE,
+ cWM_0BAF,
+ cWM_0BB0,
+ cWM_0BB1,
+ cWM_0BB2,
+ cWM_0BB3,
+ cWM_0BB4,
+ cWM_0BB5,
+ cWM_0BB6,
+ cWM_0BB7,
+ cWM_0BB8,
+ cWM_0BB9,
+ cWM_0BBA,
+ cWM_0BBB,
+ cWM_0BBC,
+ cWM_0BBD,
+ cWM_0BBE,
+ cWM_0BBF,
+ cWM_0BC0,
+ cWM_0BC1,
+ cWM_0BC2,
+ cWM_0BC3,
+ cWM_0BC4,
+ cWM_0BC5,
+ cWM_0BC6,
+ cWM_0BC7,
+ cWM_0BC8,
+ cWM_0BC9,
+ cWM_0BCA,
+ cWM_0BCB,
+ cWM_0BCC,
+ cWM_0BCD,
+ cWM_0BCE,
+ cWM_0BCF,
+ cWM_0BD0,
+ cWM_0BD1,
+ cWM_0BD2,
+ cWM_0BD3,
+ cWM_0BD4,
+ cWM_0BD5,
+ cWM_0BD6,
+ cWM_0BD7,
+ cWM_0BD8,
+ cWM_0BD9,
+ cWM_0BDA,
+ cWM_0BDB,
+ cWM_0BDC,
+ cWM_0BDD,
+ cWM_0BDE,
+ cWM_0BDF,
+ cWM_0BE0,
+ cWM_0BE1,
+ cWM_0BE2,
+ cWM_0BE3,
+ cWM_0BE4,
+ cWM_0BE5,
+ cWM_0BE6,
+ cWM_0BE7,
+ cWM_0BE8,
+ cWM_0BE9,
+ cWM_0BEA,
+ cWM_0BEB,
+ cWM_0BEC,
+ cWM_0BED,
+ cWM_0BEE,
+ cWM_0BEF,
+ cWM_0BF0,
+ cWM_0BF1,
+ cWM_0BF2,
+ cWM_0BF3,
+ cWM_0BF4,
+ cWM_0BF5,
+ cWM_0BF6,
+ cWM_0BF7,
+ cWM_0BF8,
+ cWM_0BF9,
+ cWM_0BFA,
+ cWM_0BFB,
+ cWM_0BFC,
+ cWM_0BFD,
+ cWM_0BFE,
+ cWM_0BFF,
+ cWM_0C00,
+ cWM_0C01,
+ cWM_0C02,
+ cWM_0C03,
+ cWM_0C04,
+ cWM_0C05,
+ cWM_0C06,
+ cWM_0C07,
+ cWM_0C08,
+ cWM_0C09,
+ cWM_0C0A,
+ cWM_0C0B,
+ cWM_0C0C,
+ cWM_0C0D,
+ cWM_0C0E,
+ cWM_0C0F,
+ cWM_0C10,
+ cWM_0C11,
+ cWM_0C12,
+ cWM_0C13,
+ cWM_0C14,
+ cWM_0C15,
+ cWM_0C16,
+ cWM_0C17,
+ cWM_0C18,
+ cWM_0C19,
+ cWM_0C1A,
+ cWM_0C1B,
+ cWM_0C1C,
+ cWM_0C1D,
+ cWM_0C1E,
+ cWM_0C1F,
+ cWM_0C20,
+ cWM_0C21,
+ cWM_0C22,
+ cWM_0C23,
+ cWM_0C24,
+ cWM_0C25,
+ cWM_0C26,
+ cWM_0C27,
+ cWM_0C28,
+ cWM_0C29,
+ cWM_0C2A,
+ cWM_0C2B,
+ cWM_0C2C,
+ cWM_0C2D,
+ cWM_0C2E,
+ cWM_0C2F,
+ cWM_0C30,
+ cWM_0C31,
+ cWM_0C32,
+ cWM_0C33,
+ cWM_0C34,
+ cWM_0C35,
+ cWM_0C36,
+ cWM_0C37,
+ cWM_0C38,
+ cWM_0C39,
+ cWM_0C3A,
+ cWM_0C3B,
+ cWM_0C3C,
+ cWM_0C3D,
+ cWM_0C3E,
+ cWM_0C3F,
+ cWM_0C40,
+ cWM_0C41,
+ cWM_0C42,
+ cWM_0C43,
+ cWM_0C44,
+ cWM_0C45,
+ cWM_0C46,
+ cWM_0C47,
+ cWM_0C48,
+ cWM_0C49,
+ cWM_0C4A,
+ cWM_0C4B,
+ cWM_0C4C,
+ cWM_0C4D,
+ cWM_0C4E,
+ cWM_0C4F,
+ cWM_0C50,
+ cWM_0C51,
+ cWM_0C52,
+ cWM_0C53,
+ cWM_0C54,
+ cWM_0C55,
+ cWM_0C56,
+ cWM_0C57,
+ cWM_0C58,
+ cWM_0C59,
+ cWM_0C5A,
+ cWM_0C5B,
+ cWM_0C5C,
+ cWM_0C5D,
+ cWM_0C5E,
+ cWM_0C5F,
+ cWM_0C60,
+ cWM_0C61,
+ cWM_0C62,
+ cWM_0C63,
+ cWM_0C64,
+ cWM_0C65,
+ cWM_0C66,
+ cWM_0C67,
+ cWM_0C68,
+ cWM_0C69,
+ cWM_0C6A,
+ cWM_0C6B,
+ cWM_0C6C,
+ cWM_0C6D,
+ cWM_0C6E,
+ cWM_0C6F,
+ cWM_0C70,
+ cWM_0C71,
+ cWM_0C72,
+ cWM_0C73,
+ cWM_0C74,
+ cWM_0C75,
+ cWM_0C76,
+ cWM_0C77,
+ cWM_0C78,
+ cWM_0C79,
+ cWM_0C7A,
+ cWM_0C7B,
+ cWM_0C7C,
+ cWM_0C7D,
+ cWM_0C7E,
+ cWM_0C7F,
+ cWM_0C80,
+ cWM_0C81,
+ cWM_0C82,
+ cWM_0C83,
+ cWM_0C84,
+ cWM_0C85,
+ cWM_0C86,
+ cWM_0C87,
+ cWM_0C88,
+ cWM_0C89,
+ cWM_0C8A,
+ cWM_0C8B,
+ cWM_0C8C,
+ cWM_0C8D,
+ cWM_0C8E,
+ cWM_0C8F,
+ cWM_0C90,
+ cWM_0C91,
+ cWM_0C92,
+ cWM_0C93,
+ cWM_0C94,
+ cWM_0C95,
+ cWM_0C96,
+ cWM_0C97,
+ cWM_0C98,
+ cWM_0C99,
+ cWM_0C9A,
+ cWM_0C9B,
+ cWM_0C9C,
+ cWM_0C9D,
+ cWM_0C9E,
+ cWM_0C9F,
+ cWM_0CA0,
+ cWM_0CA1,
+ cWM_0CA2,
+ cWM_0CA3,
+ cWM_0CA4,
+ cWM_0CA5,
+ cWM_0CA6,
+ cWM_0CA7,
+ cWM_0CA8,
+ cWM_0CA9,
+ cWM_0CAA,
+ cWM_0CAB,
+ cWM_0CAC,
+ cWM_0CAD,
+ cWM_0CAE,
+ cWM_0CAF,
+ cWM_0CB0,
+ cWM_0CB1,
+ cWM_0CB2,
+ cWM_0CB3,
+ cWM_0CB4,
+ cWM_0CB5,
+ cWM_0CB6,
+ cWM_0CB7,
+ cWM_0CB8,
+ cWM_0CB9,
+ cWM_0CBA,
+ cWM_0CBB,
+ cWM_0CBC,
+ cWM_0CBD,
+ cWM_0CBE,
+ cWM_0CBF,
+ cWM_0CC0,
+ cWM_0CC1,
+ cWM_0CC2,
+ cWM_0CC3,
+ cWM_0CC4,
+ cWM_0CC5,
+ cWM_0CC6,
+ cWM_0CC7,
+ cWM_0CC8,
+ cWM_0CC9,
+ cWM_0CCA,
+ cWM_0CCB,
+ cWM_0CCC,
+ cWM_0CCD,
+ cWM_0CCE,
+ cWM_0CCF,
+ cWM_0CD0,
+ cWM_0CD1,
+ cWM_0CD2,
+ cWM_0CD3,
+ cWM_0CD4,
+ cWM_0CD5,
+ cWM_0CD6,
+ cWM_0CD7,
+ cWM_0CD8,
+ cWM_0CD9,
+ cWM_0CDA,
+ cWM_0CDB,
+ cWM_0CDC,
+ cWM_0CDD,
+ cWM_0CDE,
+ cWM_0CDF,
+ cWM_0CE0,
+ cWM_0CE1,
+ cWM_0CE2,
+ cWM_0CE3,
+ cWM_0CE4,
+ cWM_0CE5,
+ cWM_0CE6,
+ cWM_0CE7,
+ cWM_0CE8,
+ cWM_0CE9,
+ cWM_0CEA,
+ cWM_0CEB,
+ cWM_0CEC,
+ cWM_0CED,
+ cWM_0CEE,
+ cWM_0CEF,
+ cWM_0CF0,
+ cWM_0CF1,
+ cWM_0CF2,
+ cWM_0CF3,
+ cWM_0CF4,
+ cWM_0CF5,
+ cWM_0CF6,
+ cWM_0CF7,
+ cWM_0CF8,
+ cWM_0CF9,
+ cWM_0CFA,
+ cWM_0CFB,
+ cWM_0CFC,
+ cWM_0CFD,
+ cWM_0CFE,
+ cWM_0CFF,
+ cWM_0D00,
+ cWM_0D01,
+ cWM_0D02,
+ cWM_0D03,
+ cWM_0D04,
+ cWM_0D05,
+ cWM_0D06,
+ cWM_0D07,
+ cWM_0D08,
+ cWM_0D09,
+ cWM_0D0A,
+ cWM_0D0B,
+ cWM_0D0C,
+ cWM_0D0D,
+ cWM_0D0E,
+ cWM_0D0F,
+ cWM_0D10,
+ cWM_0D11,
+ cWM_0D12,
+ cWM_0D13,
+ cWM_0D14,
+ cWM_0D15,
+ cWM_0D16,
+ cWM_0D17,
+ cWM_0D18,
+ cWM_0D19,
+ cWM_0D1A,
+ cWM_0D1B,
+ cWM_0D1C,
+ cWM_0D1D,
+ cWM_0D1E,
+ cWM_0D1F,
+ cWM_0D20,
+ cWM_0D21,
+ cWM_0D22,
+ cWM_0D23,
+ cWM_0D24,
+ cWM_0D25,
+ cWM_0D26,
+ cWM_0D27,
+ cWM_0D28,
+ cWM_0D29,
+ cWM_0D2A,
+ cWM_0D2B,
+ cWM_0D2C,
+ cWM_0D2D,
+ cWM_0D2E,
+ cWM_0D2F,
+ cWM_0D30,
+ cWM_0D31,
+ cWM_0D32,
+ cWM_0D33,
+ cWM_0D34,
+ cWM_0D35,
+ cWM_0D36,
+ cWM_0D37,
+ cWM_0D38,
+ cWM_0D39,
+ cWM_0D3A,
+ cWM_0D3B,
+ cWM_0D3C,
+ cWM_0D3D,
+ cWM_0D3E,
+ cWM_0D3F,
+ cWM_0D40,
+ cWM_0D41,
+ cWM_0D42,
+ cWM_0D43,
+ cWM_0D44,
+ cWM_0D45,
+ cWM_0D46,
+ cWM_0D47,
+ cWM_0D48,
+ cWM_0D49,
+ cWM_0D4A,
+ cWM_0D4B,
+ cWM_0D4C,
+ cWM_0D4D,
+ cWM_0D4E,
+ cWM_0D4F,
+ cWM_0D50,
+ cWM_0D51,
+ cWM_0D52,
+ cWM_0D53,
+ cWM_0D54,
+ cWM_0D55,
+ cWM_0D56,
+ cWM_0D57,
+ cWM_0D58,
+ cWM_0D59,
+ cWM_0D5A,
+ cWM_0D5B,
+ cWM_0D5C,
+ cWM_0D5D,
+ cWM_0D5E,
+ cWM_0D5F,
+ cWM_0D60,
+ cWM_0D61,
+ cWM_0D62,
+ cWM_0D63,
+ cWM_0D64,
+ cWM_0D65,
+ cWM_0D66,
+ cWM_0D67,
+ cWM_0D68,
+ cWM_0D69,
+ cWM_0D6A,
+ cWM_0D6B,
+ cWM_0D6C,
+ cWM_0D6D,
+ cWM_0D6E,
+ cWM_0D6F,
+ cWM_0D70,
+ cWM_0D71,
+ cWM_0D72,
+ cWM_0D73,
+ cWM_0D74,
+ cWM_0D75,
+ cWM_0D76,
+ cWM_0D77,
+ cWM_0D78,
+ cWM_0D79,
+ cWM_0D7A,
+ cWM_0D7B,
+ cWM_0D7C,
+ cWM_0D7D,
+ cWM_0D7E,
+ cWM_0D7F,
+ cWM_0D80,
+ cWM_0D81,
+ cWM_0D82,
+ cWM_0D83,
+ cWM_0D84,
+ cWM_0D85,
+ cWM_0D86,
+ cWM_0D87,
+ cWM_0D88,
+ cWM_0D89,
+ cWM_0D8A,
+ cWM_0D8B,
+ cWM_0D8C,
+ cWM_0D8D,
+ cWM_0D8E,
+ cWM_0D8F,
+ cWM_0D90,
+ cWM_0D91,
+ cWM_0D92,
+ cWM_0D93,
+ cWM_0D94,
+ cWM_0D95,
+ cWM_0D96,
+ cWM_0D97,
+ cWM_0D98,
+ cWM_0D99,
+ cWM_0D9A,
+ cWM_0D9B,
+ cWM_0D9C,
+ cWM_0D9D,
+ cWM_0D9E,
+ cWM_0D9F,
+ cWM_0DA0,
+ cWM_0DA1,
+ cWM_0DA2,
+ cWM_0DA3,
+ cWM_0DA4,
+ cWM_0DA5,
+ cWM_0DA6,
+ cWM_0DA7,
+ cWM_0DA8,
+ cWM_0DA9,
+ cWM_0DAA,
+ cWM_0DAB,
+ cWM_0DAC,
+ cWM_0DAD,
+ cWM_0DAE,
+ cWM_0DAF,
+ cWM_0DB0,
+ cWM_0DB1,
+ cWM_0DB2,
+ cWM_0DB3,
+ cWM_0DB4,
+ cWM_0DB5,
+ cWM_0DB6,
+ cWM_0DB7,
+ cWM_0DB8,
+ cWM_0DB9,
+ cWM_0DBA,
+ cWM_0DBB,
+ cWM_0DBC,
+ cWM_0DBD,
+ cWM_0DBE,
+ cWM_0DBF,
+ cWM_0DC0,
+ cWM_0DC1,
+ cWM_0DC2,
+ cWM_0DC3,
+ cWM_0DC4,
+ cWM_0DC5,
+ cWM_0DC6,
+ cWM_0DC7,
+ cWM_0DC8,
+ cWM_0DC9,
+ cWM_0DCA,
+ cWM_0DCB,
+ cWM_0DCC,
+ cWM_0DCD,
+ cWM_0DCE,
+ cWM_0DCF,
+ cWM_0DD0,
+ cWM_0DD1,
+ cWM_0DD2,
+ cWM_0DD3,
+ cWM_0DD4,
+ cWM_0DD5,
+ cWM_0DD6,
+ cWM_0DD7,
+ cWM_0DD8,
+ cWM_0DD9,
+ cWM_0DDA,
+ cWM_0DDB,
+ cWM_0DDC,
+ cWM_0DDD,
+ cWM_0DDE,
+ cWM_0DDF,
+ cWM_0DE0,
+ cWM_0DE1,
+ cWM_0DE2,
+ cWM_0DE3,
+ cWM_0DE4,
+ cWM_0DE5,
+ cWM_0DE6,
+ cWM_0DE7,
+ cWM_0DE8,
+ cWM_0DE9,
+ cWM_0DEA,
+ cWM_0DEB,
+ cWM_0DEC,
+ cWM_0DED,
+ cWM_0DEE,
+ cWM_0DEF,
+ cWM_0DF0,
+ cWM_0DF1,
+ cWM_0DF2,
+ cWM_0DF3,
+ cWM_0DF4,
+ cWM_0DF5,
+ cWM_0DF6,
+ cWM_0DF7,
+ cWM_0DF8,
+ cWM_0DF9,
+ cWM_0DFA,
+ cWM_0DFB,
+ cWM_0DFC,
+ cWM_0DFD,
+ cWM_0DFE,
+ cWM_0DFF,
+ cWM_0E00,
+ cWM_0E01,
+ cWM_0E02,
+ cWM_0E03,
+ cWM_0E04,
+ cWM_0E05,
+ cWM_0E06,
+ cWM_0E07,
+ cWM_0E08,
+ cWM_0E09,
+ cWM_0E0A,
+ cWM_0E0B,
+ cWM_0E0C,
+ cWM_0E0D,
+ cWM_0E0E,
+ cWM_0E0F,
+ cWM_0E10,
+ cWM_0E11,
+ cWM_0E12,
+ cWM_0E13,
+ cWM_0E14,
+ cWM_0E15,
+ cWM_0E16,
+ cWM_0E17,
+ cWM_0E18,
+ cWM_0E19,
+ cWM_0E1A,
+ cWM_0E1B,
+ cWM_0E1C,
+ cWM_0E1D,
+ cWM_0E1E,
+ cWM_0E1F,
+ cWM_0E20,
+ cWM_0E21,
+ cWM_0E22,
+ cWM_0E23,
+ cWM_0E24,
+ cWM_0E25,
+ cWM_0E26,
+ cWM_0E27,
+ cWM_0E28,
+ cWM_0E29,
+ cWM_0E2A,
+ cWM_0E2B,
+ cWM_0E2C,
+ cWM_0E2D,
+ cWM_0E2E,
+ cWM_0E2F,
+ cWM_0E30,
+ cWM_0E31,
+ cWM_0E32,
+ cWM_0E33,
+ cWM_0E34,
+ cWM_0E35,
+ cWM_0E36,
+ cWM_0E37,
+ cWM_0E38,
+ cWM_0E39,
+ cWM_0E3A,
+ cWM_0E3B,
+ cWM_0E3C,
+ cWM_0E3D,
+ cWM_0E3E,
+ cWM_0E3F,
+ cWM_0E40,
+ cWM_0E41,
+ cWM_0E42,
+ cWM_0E43,
+ cWM_0E44,
+ cWM_0E45,
+ cWM_0E46,
+ cWM_0E47,
+ cWM_0E48,
+ cWM_0E49,
+ cWM_0E4A,
+ cWM_0E4B,
+ cWM_0E4C,
+ cWM_0E4D,
+ cWM_0E4E,
+ cWM_0E4F,
+ cWM_0E50,
+ cWM_0E51,
+ cWM_0E52,
+ cWM_0E53,
+ cWM_0E54,
+ cWM_0E55,
+ cWM_0E56,
+ cWM_0E57,
+ cWM_0E58,
+ cWM_0E59,
+ cWM_0E5A,
+ cWM_0E5B,
+ cWM_0E5C,
+ cWM_0E5D,
+ cWM_0E5E,
+ cWM_0E5F,
+ cWM_0E60,
+ cWM_0E61,
+ cWM_0E62,
+ cWM_0E63,
+ cWM_0E64,
+ cWM_0E65,
+ cWM_0E66,
+ cWM_0E67,
+ cWM_0E68,
+ cWM_0E69,
+ cWM_0E6A,
+ cWM_0E6B,
+ cWM_0E6C,
+ cWM_0E6D,
+ cWM_0E6E,
+ cWM_0E6F,
+ cWM_0E70,
+ cWM_0E71,
+ cWM_0E72,
+ cWM_0E73,
+ cWM_0E74,
+ cWM_0E75,
+ cWM_0E76,
+ cWM_0E77,
+ cWM_0E78,
+ cWM_0E79,
+ cWM_0E7A,
+ cWM_0E7B,
+ cWM_0E7C,
+ cWM_0E7D,
+ cWM_0E7E,
+ cWM_0E7F,
+ cWM_0E80,
+ cWM_0E81,
+ cWM_0E82,
+ cWM_0E83,
+ cWM_0E84,
+ cWM_0E85,
+ cWM_0E86,
+ cWM_0E87,
+ cWM_0E88,
+ cWM_0E89,
+ cWM_0E8A,
+ cWM_0E8B,
+ cWM_0E8C,
+ cWM_0E8D,
+ cWM_0E8E,
+ cWM_0E8F,
+ cWM_0E90,
+ cWM_0E91,
+ cWM_0E92,
+ cWM_0E93,
+ cWM_0E94,
+ cWM_0E95,
+ cWM_0E96,
+ cWM_0E97,
+ cWM_0E98,
+ cWM_0E99,
+ cWM_0E9A,
+ cWM_0E9B,
+ cWM_0E9C,
+ cWM_0E9D,
+ cWM_0E9E,
+ cWM_0E9F,
+ cWM_0EA0,
+ cWM_0EA1,
+ cWM_0EA2,
+ cWM_0EA3,
+ cWM_0EA4,
+ cWM_0EA5,
+ cWM_0EA6,
+ cWM_0EA7,
+ cWM_0EA8,
+ cWM_0EA9,
+ cWM_0EAA,
+ cWM_0EAB,
+ cWM_0EAC,
+ cWM_0EAD,
+ cWM_0EAE,
+ cWM_0EAF,
+ cWM_0EB0,
+ cWM_0EB1,
+ cWM_0EB2,
+ cWM_0EB3,
+ cWM_0EB4,
+ cWM_0EB5,
+ cWM_0EB6,
+ cWM_0EB7,
+ cWM_0EB8,
+ cWM_0EB9,
+ cWM_0EBA,
+ cWM_0EBB,
+ cWM_0EBC,
+ cWM_0EBD,
+ cWM_0EBE,
+ cWM_0EBF,
+ cWM_0EC0,
+ cWM_0EC1,
+ cWM_0EC2,
+ cWM_0EC3,
+ cWM_0EC4,
+ cWM_0EC5,
+ cWM_0EC6,
+ cWM_0EC7,
+ cWM_0EC8,
+ cWM_0EC9,
+ cWM_0ECA,
+ cWM_0ECB,
+ cWM_0ECC,
+ cWM_0ECD,
+ cWM_0ECE,
+ cWM_0ECF,
+ cWM_0ED0,
+ cWM_0ED1,
+ cWM_0ED2,
+ cWM_0ED3,
+ cWM_0ED4,
+ cWM_0ED5,
+ cWM_0ED6,
+ cWM_0ED7,
+ cWM_0ED8,
+ cWM_0ED9,
+ cWM_0EDA,
+ cWM_0EDB,
+ cWM_0EDC,
+ cWM_0EDD,
+ cWM_0EDE,
+ cWM_0EDF,
+ cWM_0EE0,
+ cWM_0EE1,
+ cWM_0EE2,
+ cWM_0EE3,
+ cWM_0EE4,
+ cWM_0EE5,
+ cWM_0EE6,
+ cWM_0EE7,
+ cWM_0EE8,
+ cWM_0EE9,
+ cWM_0EEA,
+ cWM_0EEB,
+ cWM_0EEC,
+ cWM_0EED,
+ cWM_0EEE,
+ cWM_0EEF,
+ cWM_0EF0,
+ cWM_0EF1,
+ cWM_0EF2,
+ cWM_0EF3,
+ cWM_0EF4,
+ cWM_0EF5,
+ cWM_0EF6,
+ cWM_0EF7,
+ cWM_0EF8,
+ cWM_0EF9,
+ cWM_0EFA,
+ cWM_0EFB,
+ cWM_0EFC,
+ cWM_0EFD,
+ cWM_0EFE,
+ cWM_0EFF,
+ cWM_0F00,
+ cWM_0F01,
+ cWM_0F02,
+ cWM_0F03,
+ cWM_0F04,
+ cWM_0F05,
+ cWM_0F06,
+ cWM_0F07,
+ cWM_0F08,
+ cWM_0F09,
+ cWM_0F0A,
+ cWM_0F0B,
+ cWM_0F0C,
+ cWM_0F0D,
+ cWM_0F0E,
+ cWM_0F0F,
+ cWM_0F10,
+ cWM_0F11,
+ cWM_0F12,
+ cWM_0F13,
+ cWM_0F14,
+ cWM_0F15,
+ cWM_0F16,
+ cWM_0F17,
+ cWM_0F18,
+ cWM_0F19,
+ cWM_0F1A,
+ cWM_0F1B,
+ cWM_0F1C,
+ cWM_0F1D,
+ cWM_0F1E,
+ cWM_0F1F,
+ cWM_0F20,
+ cWM_0F21,
+ cWM_0F22,
+ cWM_0F23,
+ cWM_0F24,
+ cWM_0F25,
+ cWM_0F26,
+ cWM_0F27,
+ cWM_0F28,
+ cWM_0F29,
+ cWM_0F2A,
+ cWM_0F2B,
+ cWM_0F2C,
+ cWM_0F2D,
+ cWM_0F2E,
+ cWM_0F2F,
+ cWM_0F30,
+ cWM_0F31,
+ cWM_0F32,
+ cWM_0F33,
+ cWM_0F34,
+ cWM_0F35,
+ cWM_0F36,
+ cWM_0F37,
+ cWM_0F38,
+ cWM_0F39,
+ cWM_0F3A,
+ cWM_0F3B,
+ cWM_0F3C,
+ cWM_0F3D,
+ cWM_0F3E,
+ cWM_0F3F,
+ cWM_0F40,
+ cWM_0F41,
+ cWM_0F42,
+ cWM_0F43,
+ cWM_0F44,
+ cWM_0F45,
+ cWM_0F46,
+ cWM_0F47,
+ cWM_0F48,
+ cWM_0F49,
+ cWM_0F4A,
+ cWM_0F4B,
+ cWM_0F4C,
+ cWM_0F4D,
+ cWM_0F4E,
+ cWM_0F4F,
+ cWM_0F50,
+ cWM_0F51,
+ cWM_0F52,
+ cWM_0F53,
+ cWM_0F54,
+ cWM_0F55,
+ cWM_0F56,
+ cWM_0F57,
+ cWM_0F58,
+ cWM_0F59,
+ cWM_0F5A,
+ cWM_0F5B,
+ cWM_0F5C,
+ cWM_0F5D,
+ cWM_0F5E,
+ cWM_0F5F,
+ cWM_0F60,
+ cWM_0F61,
+ cWM_0F62,
+ cWM_0F63,
+ cWM_0F64,
+ cWM_0F65,
+ cWM_0F66,
+ cWM_0F67,
+ cWM_0F68,
+ cWM_0F69,
+ cWM_0F6A,
+ cWM_0F6B,
+ cWM_0F6C,
+ cWM_0F6D,
+ cWM_0F6E,
+ cWM_0F6F,
+ cWM_0F70,
+ cWM_0F71,
+ cWM_0F72,
+ cWM_0F73,
+ cWM_0F74,
+ cWM_0F75,
+ cWM_0F76,
+ cWM_0F77,
+ cWM_0F78,
+ cWM_0F79,
+ cWM_0F7A,
+ cWM_0F7B,
+ cWM_0F7C,
+ cWM_0F7D,
+ cWM_0F7E,
+ cWM_0F7F,
+ cWM_0F80,
+ cWM_0F81,
+ cWM_0F82,
+ cWM_0F83,
+ cWM_0F84,
+ cWM_0F85,
+ cWM_0F86,
+ cWM_0F87,
+ cWM_0F88,
+ cWM_0F89,
+ cWM_0F8A,
+ cWM_0F8B,
+ cWM_0F8C,
+ cWM_0F8D,
+ cWM_0F8E,
+ cWM_0F8F,
+ cWM_0F90,
+ cWM_0F91,
+ cWM_0F92,
+ cWM_0F93,
+ cWM_0F94,
+ cWM_0F95,
+ cWM_0F96,
+ cWM_0F97,
+ cWM_0F98,
+ cWM_0F99,
+ cWM_0F9A,
+ cWM_0F9B,
+ cWM_0F9C,
+ cWM_0F9D,
+ cWM_0F9E,
+ cWM_0F9F,
+ cWM_0FA0,
+ cWM_0FA1,
+ cWM_0FA2,
+ cWM_0FA3,
+ cWM_0FA4,
+ cWM_0FA5,
+ cWM_0FA6,
+ cWM_0FA7,
+ cWM_0FA8,
+ cWM_0FA9,
+ cWM_0FAA,
+ cWM_0FAB,
+ cWM_0FAC,
+ cWM_0FAD,
+ cWM_0FAE,
+ cWM_0FAF,
+ cWM_0FB0,
+ cWM_0FB1,
+ cWM_0FB2,
+ cWM_0FB3,
+ cWM_0FB4,
+ cWM_0FB5,
+ cWM_0FB6,
+ cWM_0FB7,
+ cWM_0FB8,
+ cWM_0FB9,
+ cWM_0FBA,
+ cWM_0FBB,
+ cWM_0FBC,
+ cWM_0FBD,
+ cWM_0FBE,
+ cWM_0FBF,
+ cWM_0FC0,
+ cWM_0FC1,
+ cWM_0FC2,
+ cWM_0FC3,
+ cWM_0FC4,
+ cWM_0FC5,
+ cWM_0FC6,
+ cWM_0FC7,
+ cWM_0FC8,
+ cWM_0FC9,
+ cWM_0FCA,
+ cWM_0FCB,
+ cWM_0FCC,
+ cWM_0FCD,
+ cWM_0FCE,
+ cWM_0FCF,
+ cWM_0FD0,
+ cWM_0FD1,
+ cWM_0FD2,
+ cWM_0FD3,
+ cWM_0FD4,
+ cWM_0FD5,
+ cWM_0FD6,
+ cWM_0FD7,
+ cWM_0FD8,
+ cWM_0FD9,
+ cWM_0FDA,
+ cWM_0FDB,
+ cWM_0FDC,
+ cWM_0FDD,
+ cWM_0FDE,
+ cWM_0FDF,
+ cWM_0FE0,
+ cWM_0FE1,
+ cWM_0FE2,
+ cWM_0FE3,
+ cWM_0FE4,
+ cWM_0FE5,
+ cWM_0FE6,
+ cWM_0FE7,
+ cWM_0FE8,
+ cWM_0FE9,
+ cWM_0FEA,
+ cWM_0FEB,
+ cWM_0FEC,
+ cWM_0FED,
+ cWM_0FEE,
+ cWM_0FEF,
+ cWM_0FF0,
+ cWM_0FF1,
+ cWM_0FF2,
+ cWM_0FF3,
+ cWM_0FF4,
+ cWM_0FF5,
+ cWM_0FF6,
+ cWM_0FF7,
+ cWM_0FF8,
+ cWM_0FF9,
+ cWM_0FFA,
+ cWM_0FFB,
+ cWM_0FFC,
+ cWM_0FFD,
+ cWM_0FFE,
+ cWM_0FFF,
+ //LVM_FIRST = $1000; { ListView messages }
+ cLVM_GETBKCOLOR, // = LVM_FIRST + 0;
+ cLVM_SETBKCOLOR, // = LVM_FIRST + 1;
+ cLVM_GETIMAGELIST, // = LVM_FIRST + 2;
+ cLVM_SETIMAGELIST, // = LVM_FIRST + 3;
+ cLVM_GETITEMCOUNT, // = LVM_FIRST + 4;
+ cLVM_GETITEMA, // = LVM_FIRST + 5;
+ cLVM_SETITEMA, // = LVM_FIRST + 6;
+ cLVM_INSERTITEMA, // = LVM_FIRST + 7;
+ cLVM_DELETEITEM, // = LVM_FIRST + 8;
+ cLVM_DELETEALLITEMS, // = LVM_FIRST + 9;
+ cLVM_GETCALLBACKMASK,// = LVM_FIRST + 10;
+ cLVM_SETCALLBACKMASK,// = LVM_FIRST + 11;
+ cLVM_GETNEXTITEM, // = LVM_FIRST + 12;
+ cLVM_FINDITEMA, // = LVM_FIRST + 13;
+ cLVM_GETITEMRECT, // = LVM_FIRST + 14;
+ cLVM_SETITEMPOSITION,// = LVM_FIRST + 15;
+ cLVM_GETITEMPOSITION,// = LVM_FIRST + 16;
+ cLVM_GETSTRINGWIDTHA,// = LVM_FIRST + 17;
+ cLVM_HITTEST, // = LVM_FIRST + 18;
+ cLVM_ENSUREVISIBLE, // = LVM_FIRST + 19;
+ cLVM_SCROLL, // = LVM_FIRST + 20;
+ cLVM_REDRAWITEMS, // = LVM_FIRST + 21;
+ cLVM_ARRANGE, // = LVM_FIRST + 22;
+ cLVM_EDITLABELA, // = LVM_FIRST + 23;
+ cWM_1018,
+ cLVM_GETCOLUMNA, // = LVM_FIRST + 25;
+ cLVM_SETCOLUMNA, // = LVM_FIRST + 26;
+ cLVM_INSERTCOLUMNA, // = LVM_FIRST + 27;
+ cLVM_DELETECOLUMN, // = LVM_FIRST + 28;
+ cLVM_GETCOLUMNWIDTH, // = LVM_FIRST + 29;
+ cLVM_SETCOLUMNWIDTH, // = LVM_FIRST + 30;
+ cLVM_GETHEADER, // = LVM_FIRST + 31;
+ cWM_1020,
+ cLVM_CREATEDRAGIMAGE,// = LVM_FIRST + 33;
+ cLVM_GETVIEWRECT, // = LVM_FIRST + 34;
+ cLVM_GETTEXTCOLOR, // = LVM_FIRST + 35;
+ cLVM_SETTEXTCOLOR, // = LVM_FIRST + 36;
+ cLVM_GETTEXTBKCOLOR, // = LVM_FIRST + 37;
+ cLVM_SETTEXTBKCOLOR, // = LVM_FIRST + 38;
+ cLVM_GETTOPINDEX, // = LVM_FIRST + 39;
+ cLVM_GETCOUNTPERPAGE,// = LVM_FIRST + 40;
+ cLVM_GETORIGIN, // = LVM_FIRST + 41;
+ cLVM_UPDATE, // = LVM_FIRST + 42;
+ cLVM_SETITEMSTATE, // = LVM_FIRST + 43;
+ cLVM_GETITEMSTATE, // = LVM_FIRST + 44;
+ cLVM_GETITEMTEXTA, // = LVM_FIRST + 45;
+ cLVM_SETITEMTEXTA, // = LVM_FIRST + 46;
+ cLVM_SETITEMCOUNT, // = LVM_FIRST + 47;
+ cLVM_SORTITEMS, // = LVM_FIRST + 48;
+ cLVM_SETITEMPOSITION32, // = LVM_FIRST + 49;
+ cLVM_GETSELECTEDCOUNT, // = LVM_FIRST + 50;
+ cLVM_GETITEMSPACING, // = LVM_FIRST + 51;
+ cLVM_GETISEARCHSTRINGA, // = LVM_FIRST + 52;
+ cLVM_SETICONSPACING, // = LVM_FIRST + 53;
+ cLVM_SETEXTENDEDLISTVIEWSTYLE, // = LVM_FIRST + 54;
+ cLVM_GETEXTENDEDLISTVIEWSTYLE, // = LVM_FIRST + 55;
+ cLVM_GETSUBITEMRECT, // = LVM_FIRST + 56;
+ cLVM_SUBITEMHITTEST, // = LVM_FIRST + 57;
+ cLVM_SETCOLUMNORDERARRAY, // = LVM_FIRST + 58;
+ cLVM_GETCOLUMNORDERARRAY, // = LVM_FIRST + 59;
+ cLVM_SETHOTITEM, // = LVM_FIRST + 60;
+ cLVM_GETHOTITEM, // = LVM_FIRST + 61;
+ cLVM_SETHOTCURSOR, // = LVM_FIRST + 62;
+ cLVM_GETHOTCURSOR, // = LVM_FIRST + 63;
+ cLVM_APPROXIMATEVIEWRECT, // = LVM_FIRST + 64;
+ cLVM_SETWORKAREA, // = LVM_FIRST + 65;
+ cLVM_GETSELECTIONMARK, // = LVM_FIRST + 66;
+ cLVM_SETSELECTIONMARK, // = LVM_FIRST + 67;
+ cLVM_SETBKIMAGEA, // = LVM_FIRST + 68;
+ cLVM_GETBKIMAGEA, // = LVM_FIRST + 69;
+ cLVM_GETWORKAREAS, // = LVM_FIRST + 70;
+ cLVM_SETHOVERTIME, // = LVM_FIRST + 71;
+ cLVM_GETHOVERTIME, // = LVM_FIRST + 72;
+ cLVM_GETNUMBEROFWORKAREAS, // = LVM_FIRST + 73;
+ cLVM_SETTOOLTIPS, // = LVM_FIRST + 74;
+ cLVM_GETITEMW, // = LVM_FIRST + 75;
+ cLVM_SETITEMW, // = LVM_FIRST + 76;
+ cLVM_INSERTITEMW, // = LVM_FIRST + 77;
+ cLVM_GETTOOLTIPS, // = LVM_FIRST + 78;
+ cWM_104F,
+ cWM_1050,
+ cLVM_SORTITEMSEX, // = LVM_FIRST + 81;
+ cWM_1052,
+ cLVM_FINDITEMW, // = LVM_FIRST + 83;
+ cWM_1054,
+ cWM_1055,
+ cWM_1056,
+ cLVM_GETSTRINGWIDTHW, // = LVM_FIRST + 87;
+ cWM_1058,
+ cWM_1059,
+ cWM_105A,
+ cWM_105B,
+ cWM_105C,
+ cWM_105D,
+ cWM_105E,
+ cLVM_GETCOLUMNW, // = LVM_FIRST + 95;
+ cLVM_SETCOLUMNW, // = LVM_FIRST + 96;
+ cLVM_INSERTCOLUMNW, // = LVM_FIRST + 97;
+ cWM_1062,
+ cWM_1063,
+ cWM_1064,
+ cWM_1065,
+ cWM_1066,
+ cWM_1067,
+ cWM_1068,
+ cWM_1069,
+ cWM_106A,
+ cWM_106B,
+ cWM_106C,
+ cWM_106D,
+ cWM_106E,
+ cWM_106F,
+ cWM_1070,
+ cWM_1071,
+ cWM_1072,
+ cLVM_GETITEMTEXTW, // = LVM_FIRST + 115;
+ cLVM_SETITEMTEXTW, // = LVM_FIRST + 116;
+ cLVM_GETISEARCHSTRINGW,// = LVM_FIRST + 117;
+ cLVM_EDITLABELW, // = LVM_FIRST + 118;
+ cWM_1077,
+ cWM_1078,
+ cWM_1079,
+ cWM_107A,
+ cWM_107B,
+ cWM_107C,
+ cWM_107D,
+ cWM_107E,
+ cWM_107F,
+ cWM_1080,
+ cWM_1081,
+ cWM_1082,
+ cWM_1083,
+ cWM_1084,
+ cWM_1085,
+ cWM_1086,
+ cWM_1087,
+ cWM_1088,
+ cWM_1089,
+ cLVM_SETBKIMAGEW, // = LVM_FIRST + 138;
+ cLVM_GETBKIMAGEW, // = LVM_FIRST + 139;
+ cWM_108C,
+ cWM_108D,
+ cWM_108E,
+ cWM_108F,
+ cWM_1090,
+ cWM_1091,
+ cWM_1092,
+ cWM_1093,
+ cWM_1094,
+ cWM_1095,
+ cWM_1096,
+ cWM_1097,
+ cWM_1098,
+ cWM_1099,
+ cWM_109A,
+ cWM_109B,
+ cWM_109C,
+ cWM_109D,
+ cWM_109E,
+ cWM_109F,
+ cWM_10A0,
+ cWM_10A1,
+ cWM_10A2,
+ cWM_10A3,
+ cWM_10A4,
+ cWM_10A5,
+ cWM_10A6,
+ cWM_10A7,
+ cWM_10A8,
+ cWM_10A9,
+ cWM_10AA,
+ cWM_10AB,
+ cWM_10AC,
+ cWM_10AD,
+ cWM_10AE,
+ cWM_10AF,
+ cWM_10B0,
+ cWM_10B1,
+ cWM_10B2,
+ cWM_10B3,
+ cWM_10B4,
+ cWM_10B5,
+ cWM_10B6,
+ cWM_10B7,
+ cWM_10B8,
+ cWM_10B9,
+ cWM_10BA,
+ cWM_10BB,
+ cWM_10BC,
+ cWM_10BD,
+ cWM_10BE,
+ cWM_10BF,
+ cWM_10C0,
+ cWM_10C1,
+ cWM_10C2,
+ cWM_10C3,
+ cWM_10C4,
+ cWM_10C5,
+ cWM_10C6,
+ cWM_10C7,
+ cWM_10C8,
+ cWM_10C9,
+ cWM_10CA,
+ cWM_10CB,
+ cWM_10CC,
+ cWM_10CD,
+ cWM_10CE,
+ cWM_10CF,
+ cWM_10D0,
+ cWM_10D1,
+ cWM_10D2,
+ cWM_10D3,
+ cWM_10D4,
+ cWM_10D5,
+ cWM_10D6,
+ cWM_10D7,
+ cWM_10D8,
+ cWM_10D9,
+ cWM_10DA,
+ cWM_10DB,
+ cWM_10DC,
+ cWM_10DD,
+ cWM_10DE,
+ cWM_10DF,
+ cWM_10E0,
+ cWM_10E1,
+ cWM_10E2,
+ cWM_10E3,
+ cWM_10E4,
+ cWM_10E5,
+ cWM_10E6,
+ cWM_10E7,
+ cWM_10E8,
+ cWM_10E9,
+ cWM_10EA,
+ cWM_10EB,
+ cWM_10EC,
+ cWM_10ED,
+ cWM_10EE,
+ cWM_10EF,
+ cWM_10F0,
+ cWM_10F1,
+ cWM_10F2,
+ cWM_10F3,
+ cWM_10F4,
+ cWM_10F5,
+ cWM_10F6,
+ cWM_10F7,
+ cWM_10F8,
+ cWM_10F9,
+ cWM_10FA,
+ cWM_10FB,
+ cWM_10FC,
+ cWM_10FD,
+ cWM_10FE,
+ cWM_10FF,
+ //TV_FIRST = $1100; { TreeView messages }
+ cTVM_INSERTITEMA, // = TV_FIRST + 0;
+ cTVM_DELETEITEM, // = TV_FIRST + 1;
+ cTVM_EXPAND, // = TV_FIRST + 2;
+ cWM_1103,
+ cTVM_GETITEMRECT, // = TV_FIRST + 4;
+ cTVM_GETCOUNT, // = TV_FIRST + 5;
+ cTVM_GETINDENT, // = TV_FIRST + 6;
+ cTVM_SETINDENT, // = TV_FIRST + 7;
+ cTVM_GETIMAGELIST, // = TV_FIRST + 8;
+ cTVM_SETIMAGELIST, // = TV_FIRST + 9;
+ cTVM_GETNEXTITEM, // = TV_FIRST + 10;
+ cTVM_SELECTITEM, // = TV_FIRST + 11;
+ cTVM_GETITEMA, // = TV_FIRST + 12;
+ cTVM_SETITEMA, // = TV_FIRST + 13;
+ cTVM_EDITLABELA, // = TV_FIRST + 14;
+ cTVM_GETEDITCONTROL, // = TV_FIRST + 15;
+ cTVM_GETVISIBLECOUNT,// = TV_FIRST + 16;
+ cTVM_HITTEST, // = TV_FIRST + 17;
+ cTVM_CREATEDRAGIMAGE,// = TV_FIRST + 18;
+ cTVM_SORTCHILDREN, // = TV_FIRST + 19;
+ cTVM_ENSUREVISIBLE, // = TV_FIRST + 20;
+ cTVM_SORTCHILDRENCB, // = TV_FIRST + 21;
+ cTVM_ENDEDITLABELNOW,// = TV_FIRST + 22;
+ cTVM_GETISEARCHSTRINGA, // = TV_FIRST + 23;
+ cTVM_SETTOOLTIPS, // = TV_FIRST + 24;
+ cTVM_GETTOOLTIPS, // = TV_FIRST + 25;
+ cTVM_SETINSERTMARK, // = TV_FIRST + 26;
+ cTVM_SETITEMHEIGHT, // = TV_FIRST + 27;
+ cTVM_GETITEMHEIGHT, // = TV_FIRST + 28;
+ cTVM_SETBKCOLOR, // = TV_FIRST + 29;
+ cTVM_SETTEXTCOLOR, // = TV_FIRST + 30;
+ cTVM_GETBKCOLOR, // = TV_FIRST + 31;
+ cTVM_GETTEXTCOLOR, // = TV_FIRST + 32;
+ cTVM_SETSCROLLTIME, // = TV_FIRST + 33;
+ cTVM_GETSCROLLTIME, // = TV_FIRST + 34;
+ cWM_1123,
+ cWM_1124,
+ cTVM_SETINSERTMARKCOLOR, // = TV_FIRST + 37;
+ cTVM_GETINSERTMARKCOLOR, // = TV_FIRST + 38;
+ cWM_1127,
+ cTVM_SETLINECOLOR, // = TV_FIRST + 40;
+ cWM_1129,
+ cWM_112A,
+ cWM_112B,
+ cWM_112C,
+ cWM_112D,
+ cWM_112E,
+ cWM_112F,
+ cWM_1130,
+ cWM_1131,
+ cTVM_INSERTITEMW, // = TV_FIRST + 50;
+ cWM_1133,
+ cWM_1134,
+ cWM_1135,
+ cWM_1136,
+ cWM_1137,
+ cWM_1138,
+ cWM_1139,
+ cWM_113A,
+ cWM_113B,
+ cWM_113C,
+ cWM_113D,
+ cTVM_GETITEMW, // = TV_FIRST + 62;
+ cTVM_SETITEMW, // = TV_FIRST + 63;
+ cTVM_GETISEARCHSTRINGW, // = TV_FIRST + 64;
+ cTVM_EDITLABELW, // = TV_FIRST + 65;
+ cWM_1142,
+ cWM_1143,
+ cWM_1144,
+ cWM_1145,
+ cWM_1146,
+ cWM_1147,
+ cWM_1148,
+ cWM_1149,
+ cWM_114A,
+ cWM_114B,
+ cWM_114C,
+ cWM_114D,
+ cWM_114E,
+ cWM_114F,
+ cWM_1150,
+ cWM_1151,
+ cWM_1152,
+ cWM_1153,
+ cWM_1154,
+ cWM_1155,
+ cWM_1156,
+ cWM_1157,
+ cWM_1158,
+ cWM_1159,
+ cWM_115A,
+ cWM_115B,
+ cWM_115C,
+ cWM_115D,
+ cWM_115E,
+ cWM_115F,
+ cWM_1160,
+ cWM_1161,
+ cWM_1162,
+ cWM_1163,
+ cWM_1164,
+ cWM_1165,
+ cWM_1166,
+ cWM_1167,
+ cWM_1168,
+ cWM_1169,
+ cWM_116A,
+ cWM_116B,
+ cWM_116C,
+ cWM_116D,
+ cWM_116E,
+ cWM_116F,
+ cWM_1170,
+ cWM_1171,
+ cWM_1172,
+ cWM_1173,
+ cWM_1174,
+ cWM_1175,
+ cWM_1176,
+ cWM_1177,
+ cWM_1178,
+ cWM_1179,
+ cWM_117A,
+ cWM_117B,
+ cWM_117C,
+ cWM_117D,
+ cWM_117E,
+ cWM_117F,
+ cWM_1180,
+ cWM_1181,
+ cWM_1182,
+ cWM_1183,
+ cWM_1184,
+ cWM_1185,
+ cWM_1186,
+ cWM_1187,
+ cWM_1188,
+ cWM_1189,
+ cWM_118A,
+ cWM_118B,
+ cWM_118C,
+ cWM_118D,
+ cWM_118E,
+ cWM_118F,
+ cWM_1190,
+ cWM_1191,
+ cWM_1192,
+ cWM_1193,
+ cWM_1194,
+ cWM_1195,
+ cWM_1196,
+ cWM_1197,
+ cWM_1198,
+ cWM_1199,
+ cWM_119A,
+ cWM_119B,
+ cWM_119C,
+ cWM_119D,
+ cWM_119E,
+ cWM_119F,
+ cWM_11A0,
+ cWM_11A1,
+ cWM_11A2,
+ cWM_11A3,
+ cWM_11A4,
+ cWM_11A5,
+ cWM_11A6,
+ cWM_11A7,
+ cWM_11A8,
+ cWM_11A9,
+ cWM_11AA,
+ cWM_11AB,
+ cWM_11AC,
+ cWM_11AD,
+ cWM_11AE,
+ cWM_11AF,
+ cWM_11B0,
+ cWM_11B1,
+ cWM_11B2,
+ cWM_11B3,
+ cWM_11B4,
+ cWM_11B5,
+ cWM_11B6,
+ cWM_11B7,
+ cWM_11B8,
+ cWM_11B9,
+ cWM_11BA,
+ cWM_11BB,
+ cWM_11BC,
+ cWM_11BD,
+ cWM_11BE,
+ cWM_11BF,
+ cWM_11C0,
+ cWM_11C1,
+ cWM_11C2,
+ cWM_11C3,
+ cWM_11C4,
+ cWM_11C5,
+ cWM_11C6,
+ cWM_11C7,
+ cWM_11C8,
+ cWM_11C9,
+ cWM_11CA,
+ cWM_11CB,
+ cWM_11CC,
+ cWM_11CD,
+ cWM_11CE,
+ cWM_11CF,
+ cWM_11D0,
+ cWM_11D1,
+ cWM_11D2,
+ cWM_11D3,
+ cWM_11D4,
+ cWM_11D5,
+ cWM_11D6,
+ cWM_11D7,
+ cWM_11D8,
+ cWM_11D9,
+ cWM_11DA,
+ cWM_11DB,
+ cWM_11DC,
+ cWM_11DD,
+ cWM_11DE,
+ cWM_11DF,
+ cWM_11E0,
+ cWM_11E1,
+ cWM_11E2,
+ cWM_11E3,
+ cWM_11E4,
+ cWM_11E5,
+ cWM_11E6,
+ cWM_11E7,
+ cWM_11E8,
+ cWM_11E9,
+ cWM_11EA,
+ cWM_11EB,
+ cWM_11EC,
+ cWM_11ED,
+ cWM_11EE,
+ cWM_11EF,
+ cWM_11F0,
+ cWM_11F1,
+ cWM_11F2,
+ cWM_11F3,
+ cWM_11F4,
+ cWM_11F5,
+ cWM_11F6,
+ cWM_11F7,
+ cWM_11F8,
+ cWM_11F9,
+ cWM_11FA,
+ cWM_11FB,
+ cWM_11FC,
+ cWM_11FD,
+ cWM_11FE,
+ cWM_11FF,
+ cHDM_FIRST, // = $1200; { Header messages }
+ cWM_1201,
+ cWM_1202,
+ cHDM_GETITEMA, // = HDM_FIRST + 3;
+ cWM_1204,
+ cWM_1205,
+ cHDM_HITTEST, // = HDM_FIRST + 6;
+ cHDM_GETITEMRECT, // = HDM_FIRST + 7;
+ cHDM_SETIMAGELIST, // = HDM_FIRST + 8;
+ cHDM_GETIMAGELIST, // = HDM_FIRST + 9;
+ cWM_120A,
+ cHDM_GETITEMW, // = HDM_FIRST + 11;
+ cWM_120C,
+ cWM_120D,
+ cWM_120E,
+ cHDM_ORDERTOINDEX, // = HDM_FIRST + 15;
+ cHDM_CREATEDRAGIMAGE, // = HDM_FIRST + 16; // wparam = which item = by index;
+ cHDM_GETORDERARRAY, // = HDM_FIRST + 17;
+ cHDM_SETORDERARRAY, // = HDM_FIRST + 18;
+ cHDM_SETHOTDIVIDER, // = HDM_FIRST + 19;
+ cWM_1214,
+ cWM_1215,
+ cWM_1216,
+ cWM_1217,
+ cWM_1218,
+ cWM_1219,
+ cWM_121A,
+ cWM_121B,
+ cWM_121C,
+ cWM_121D,
+ cWM_121E,
+ cWM_121F,
+ cWM_1220,
+ cWM_1221,
+ cWM_1222,
+ cWM_1223,
+ cWM_1224,
+ cWM_1225,
+ cWM_1226,
+ cWM_1227,
+ cWM_1228,
+ cWM_1229,
+ cWM_122A,
+ cWM_122B,
+ cWM_122C,
+ cWM_122D,
+ cWM_122E,
+ cWM_122F,
+ cWM_1230,
+ cWM_1231,
+ cWM_1232,
+ cWM_1233,
+ cWM_1234,
+ cWM_1235,
+ cWM_1236,
+ cWM_1237,
+ cWM_1238,
+ cWM_1239,
+ cWM_123A,
+ cWM_123B,
+ cWM_123C,
+ cWM_123D,
+ cWM_123E,
+ cWM_123F,
+ cWM_1240,
+ cWM_1241,
+ cWM_1242,
+ cWM_1243,
+ cWM_1244,
+ cWM_1245,
+ cWM_1246,
+ cWM_1247,
+ cWM_1248,
+ cWM_1249,
+ cWM_124A,
+ cWM_124B,
+ cWM_124C,
+ cWM_124D,
+ cWM_124E,
+ cWM_124F,
+ cWM_1250,
+ cWM_1251,
+ cWM_1252,
+ cWM_1253,
+ cWM_1254,
+ cWM_1255,
+ cWM_1256,
+ cWM_1257,
+ cWM_1258,
+ cWM_1259,
+ cWM_125A,
+ cWM_125B,
+ cWM_125C,
+ cWM_125D,
+ cWM_125E,
+ cWM_125F,
+ cWM_1260,
+ cWM_1261,
+ cWM_1262,
+ cWM_1263,
+ cWM_1264,
+ cWM_1265,
+ cWM_1266,
+ cWM_1267,
+ cWM_1268,
+ cWM_1269,
+ cWM_126A,
+ cWM_126B,
+ cWM_126C,
+ cWM_126D,
+ cWM_126E,
+ cWM_126F,
+ cWM_1270,
+ cWM_1271,
+ cWM_1272,
+ cWM_1273,
+ cWM_1274,
+ cWM_1275,
+ cWM_1276,
+ cWM_1277,
+ cWM_1278,
+ cWM_1279,
+ cWM_127A,
+ cWM_127B,
+ cWM_127C,
+ cWM_127D,
+ cWM_127E,
+ cWM_127F,
+ cWM_1280,
+ cWM_1281,
+ cWM_1282,
+ cWM_1283,
+ cWM_1284,
+ cWM_1285,
+ cWM_1286,
+ cWM_1287,
+ cWM_1288,
+ cWM_1289,
+ cWM_128A,
+ cWM_128B,
+ cWM_128C,
+ cWM_128D,
+ cWM_128E,
+ cWM_128F,
+ cWM_1290,
+ cWM_1291,
+ cWM_1292,
+ cWM_1293,
+ cWM_1294,
+ cWM_1295,
+ cWM_1296,
+ cWM_1297,
+ cWM_1298,
+ cWM_1299,
+ cWM_129A,
+ cWM_129B,
+ cWM_129C,
+ cWM_129D,
+ cWM_129E,
+ cWM_129F,
+ cWM_12A0,
+ cWM_12A1,
+ cWM_12A2,
+ cWM_12A3,
+ cWM_12A4,
+ cWM_12A5,
+ cWM_12A6,
+ cWM_12A7,
+ cWM_12A8,
+ cWM_12A9,
+ cWM_12AA,
+ cWM_12AB,
+ cWM_12AC,
+ cWM_12AD,
+ cWM_12AE,
+ cWM_12AF,
+ cWM_12B0,
+ cWM_12B1,
+ cWM_12B2,
+ cWM_12B3,
+ cWM_12B4,
+ cWM_12B5,
+ cWM_12B6,
+ cWM_12B7,
+ cWM_12B8,
+ cWM_12B9,
+ cWM_12BA,
+ cWM_12BB,
+ cWM_12BC,
+ cWM_12BD,
+ cWM_12BE,
+ cWM_12BF,
+ cWM_12C0,
+ cWM_12C1,
+ cWM_12C2,
+ cWM_12C3,
+ cWM_12C4,
+ cWM_12C5,
+ cWM_12C6,
+ cWM_12C7,
+ cWM_12C8,
+ cWM_12C9,
+ cWM_12CA,
+ cWM_12CB,
+ cWM_12CC,
+ cWM_12CD,
+ cWM_12CE,
+ cWM_12CF,
+ cWM_12D0,
+ cWM_12D1,
+ cWM_12D2,
+ cWM_12D3,
+ cWM_12D4,
+ cWM_12D5,
+ cWM_12D6,
+ cWM_12D7,
+ cWM_12D8,
+ cWM_12D9,
+ cWM_12DA,
+ cWM_12DB,
+ cWM_12DC,
+ cWM_12DD,
+ cWM_12DE,
+ cWM_12DF,
+ cWM_12E0,
+ cWM_12E1,
+ cWM_12E2,
+ cWM_12E3,
+ cWM_12E4,
+ cWM_12E5,
+ cWM_12E6,
+ cWM_12E7,
+ cWM_12E8,
+ cWM_12E9,
+ cWM_12EA,
+ cWM_12EB,
+ cWM_12EC,
+ cWM_12ED,
+ cWM_12EE,
+ cWM_12EF,
+ cWM_12F0,
+ cWM_12F1,
+ cWM_12F2,
+ cWM_12F3,
+ cWM_12F4,
+ cWM_12F5,
+ cWM_12F6,
+ cWM_12F7,
+ cWM_12F8,
+ cWM_12F9,
+ cWM_12FA,
+ cWM_12FB,
+ cWM_12FC,
+ cWM_12FD,
+ cWM_12FE,
+ cWM_12FF,
+ cTCM_FIRST, // = $1300; { Tab control messages }
+ cWM_1301,
+ cTCM_GETIMAGELIST, // = TCM_FIRST + 2;
+ cTCM_SETIMAGELIST, // = TCM_FIRST + 3;
+ cTCM_GETITEMCOUNT, // = TCM_FIRST + 4;
+ cTCM_GETITEMA, // = TCM_FIRST + 5;
+ cTCM_SETITEMA, // = TCM_FIRST + 6;
+ cTCM_INSERTITEMA, // = TCM_FIRST + 7;
+ cTCM_DELETEITEM, // = TCM_FIRST + 8;
+ cTCM_DELETEALLITEMS, // = TCM_FIRST + 9;
+ cTCM_GETITEMRECT, // = TCM_FIRST + 10;
+ cTCM_GETCURSEL, // = TCM_FIRST + 11;
+ cTCM_SETCURSEL, // = TCM_FIRST + 12;
+ cTCM_HITTEST, // = TCM_FIRST + 13;
+ cTCM_SETITEMEXTRA, // = TCM_FIRST + 14;
+ cWM_130F,
+ cWM_1310,
+ cWM_1311,
+ cWM_1312,
+ cWM_1313,
+ cWM_1314,
+ cWM_1315,
+ cWM_1316,
+ cWM_1317,
+ cWM_1318,
+ cWM_1319,
+ cWM_131A,
+ cWM_131B,
+ cWM_131C,
+ cWM_131D,
+ cWM_131E,
+ cWM_131F,
+ cWM_1320,
+ cWM_1321,
+ cWM_1322,
+ cWM_1323,
+ cWM_1324,
+ cWM_1325,
+ cWM_1326,
+ cWM_1327,
+ cTCM_ADJUSTRECT, // = TCM_FIRST + 40;
+ cTCM_SETITEMSIZE, // = TCM_FIRST + 41;
+ cTCM_REMOVEIMAGE, // = TCM_FIRST + 42;
+ cTCM_SETPADDING, // = TCM_FIRST + 43;
+ cTCM_GETROWCOUNT, // = TCM_FIRST + 44;
+ cTCM_GETTOOLTIPS, // = TCM_FIRST + 45;
+ cTCM_SETTOOLTIPS, // = TCM_FIRST + 46;
+ cTCM_GETCURFOCUS, // = TCM_FIRST + 47;
+ cTCM_SETCURFOCUS, // = TCM_FIRST + 48;
+ cTCM_SETMINTABWIDTH, // = TCM_FIRST + 49;
+ cTCM_DESELECTALL, // = TCM_FIRST + 50;
+ cTCM_HIGHLIGHTITEM, // = TCM_FIRST + 51;
+ cTCM_SETEXTENDEDSTYLE, // = TCM_FIRST + 52; // optional wParam == mask
+ cTCM_GETEXTENDEDSTYLE, // = TCM_FIRST + 53;
+ cWM_1336,
+ cWM_1337,
+ cWM_1338,
+ cWM_1339,
+ cWM_133A,
+ cWM_133B,
+ cTCM_GETITEMW, // = TCM_FIRST + 60;
+ cTCM_SETITEMW, // = TCM_FIRST + 61;
+ cTCM_INSERTITEMW // = TCM_FIRST + 62;
+ (*
+ cCCM_FIRST = $2000; { Common control shared messages }
+ cCCM_SETBKCOLOR = CCM_FIRST + 1; // lParam is bkColor
+ cCCM_SETCOLORSCHEME = CCM_FIRST + 2; // lParam is color scheme
+ cCCM_GETCOLORSCHEME = CCM_FIRST + 3; // fills in COLORSCHEME pointed to by lParam
+ cCCM_GETDROPTARGET = CCM_FIRST + 4;
+ cCCM_SETUNICODEFORMAT = CCM_FIRST + 5;
+ cCCM_GETUNICODEFORMAT = CCM_FIRST + 6;
+ //WM_APP = $8000;
+ *)
+ );
+
+ PMsgDecoded = ^TMsgDecoded;
+ TMsgDecoded = packed record
+ hwnd: HWND;
+ Cmessage: TMessageDecoded;
+ _filler: Word;
+ wParam: WPARAM;
+ lParam: LPARAM;
+ time: DWORD;
+ pt: TPoint;
+ end;
+
diff --git a/plugins/Libs/PsAPI.pas b/plugins/Libs/PsAPI.pas
new file mode 100644
index 0000000000..1f637cf3d9
--- /dev/null
+++ b/plugins/Libs/PsAPI.pas
@@ -0,0 +1,399 @@
+{*******************************************************}
+{ }
+{ CodeGear Delphi Runtime Library }
+{ }
+{ Copyright (c) 1985-1999, Microsoft Corporation }
+{ }
+{ Translator: Borland Software Corporation }
+{ }
+{*******************************************************}
+
+{*******************************************************}
+{ WinNT process API Interface Unit }
+{*******************************************************}
+
+unit PsAPI;
+
+interface
+
+uses Windows;
+
+type
+ PPointer = ^Pointer;
+ PHMODULE = ^HMODULE;
+{$IFNDEF FPC}
+ size_t = integer;
+{$ENDIF}
+
+ TEnumProcesses = function (lpidProcess: LPDWORD; cb: DWORD; var cbNeeded: DWORD): BOOL stdcall;
+ TEnumProcessModules = function (hProcess: THandle; lphModule: PHMODULE; cb: DWORD;
+ var lpcbNeeded: DWORD): BOOL stdcall;
+ TGetModuleBaseNameA = function (hProcess: THandle; hModule: HMODULE;
+ lpBaseName: PAnsiChar; nSize: DWORD): DWORD stdcall;
+ TGetModuleBaseNameW = function (hProcess: THandle; hModule: HMODULE;
+ lpBaseName: PWideChar; nSize: DWORD): DWORD stdcall;
+ TGetModuleBaseName = TGetModuleBaseNameW;
+ TGetModuleFileNameExA = function (hProcess: THandle; hModule: HMODULE;
+ lpFilename: PAnsiChar; nSize: DWORD): DWORD stdcall;
+ TGetModuleFileNameExW = function (hProcess: THandle; hModule: HMODULE;
+ lpFilename: PWideChar; nSize: DWORD): DWORD stdcall;
+ TGetModuleFileNameEx = TGetModuleFileNameExW;
+
+ _MODULEINFO = packed record
+ lpBaseOfDll: Pointer;
+ SizeOfImage: DWORD;
+ EntryPoint : Pointer;
+ end;
+ MODULEINFO = _MODULEINFO;
+ LPMODULEINFO = ^_MODULEINFO;
+ TModuleInfo = _MODULEINFO;
+ PModuleInfo = LPMODULEINFO;
+
+ TGetModuleInformation = function (hProcess: THandle; hModule: HMODULE;
+ lpmodinfo: LPMODULEINFO; cb: DWORD): BOOL stdcall;
+ TEmptyWorkingSet = function (hProcess: THandle): BOOL stdcall;
+ TQueryWorkingSet = function (hProcess: THandle; pv: Pointer; cb: DWORD): BOOL stdcall;
+ TInitializeProcessForWsWatch = function (hProcess: THandle): BOOL stdcall;
+
+ _PSAPI_WS_WATCH_INFORMATION = packed record
+ FaultingPc: Pointer;
+ FaultingVa: Pointer;
+ end;
+ PSAPI_WS_WATCH_INFORMATION = _PSAPI_WS_WATCH_INFORMATION;
+ PPSAPI_WS_WATCH_INFORMATION = ^_PSAPI_WS_WATCH_INFORMATION;
+ TPSAPIWsWatchInformation = _PSAPI_WS_WATCH_INFORMATION;
+ PPSAPIWsWatchInformation = PPSAPI_WS_WATCH_INFORMATION;
+
+ TGetWsChanges = function (hProcess: THandle; lpWatchInfo: PPSAPI_WS_WATCH_INFORMATION;
+ cb: DWORD): BOOL stdcall;
+
+ TGetMappedFileNameA = function (hProcess: THandle; lpv: Pointer;
+ lpFilename: PAnsiChar; nSize: DWORD): DWORD stdcall;
+ TGetMappedFileNameW = function (hProcess: THandle; lpv: Pointer;
+ lpFilename: PWideChar; nSize: DWORD): DWORD stdcall;
+ TGetMappedFileName = TGetMappedFileNameW;
+ TGetDeviceDriverBaseNameA = function (ImageBase: Pointer; lpBaseName: PAnsiChar;
+ nSize: DWORD): DWORD stdcall;
+ TGetDeviceDriverBaseNameW = function (ImageBase: Pointer; lpBaseName: PWideChar;
+ nSize: DWORD): DWORD stdcall;
+ TGetDeviceDriverBaseName = TGetDeviceDriverBaseNameW;
+ TGetDeviceDriverFileNameA = function (ImageBase: Pointer; lpFileName: PAnsiChar;
+ nSize: DWORD): DWORD stdcall;
+ TGetDeviceDriverFileNameW = function (ImageBase: Pointer; lpFileName: PWideChar;
+ nSize: DWORD): DWORD stdcall;
+ TGetDeviceDriverFileName = TGetDeviceDriverFileNameW;
+
+ TEnumDeviceDrivers = function (lpImageBase: PPointer; cb: DWORD;
+ var lpcbNeeded: DWORD): BOOL stdcall;
+
+ _PROCESS_MEMORY_COUNTERS = packed record
+ cb : DWORD;
+ PageFaultCount : DWORD;
+ PeakWorkingSetSize : size_t;
+ WorkingSetSize : size_t;
+ QuotaPeakPagedPoolUsage : size_t;
+ QuotaPagedPoolUsage : size_t;
+ QuotaPeakNonPagedPoolUsage: size_t;
+ QuotaNonPagedPoolUsage : size_t;
+ PagefileUsage : size_t;
+ PeakPagefileUsage : size_t;
+ end;
+ PROCESS_MEMORY_COUNTERS = _PROCESS_MEMORY_COUNTERS;
+ PPROCESS_MEMORY_COUNTERS = ^_PROCESS_MEMORY_COUNTERS;
+ TProcessMemoryCounters = _PROCESS_MEMORY_COUNTERS;
+ PProcessMemoryCounters = ^_PROCESS_MEMORY_COUNTERS;
+
+ TGetProcessMemoryInfo = function (Process: THandle;
+ ppsmemCounters: PPROCESS_MEMORY_COUNTERS; cb: DWORD): BOOL stdcall;
+
+function EnumProcesses(lpidProcess: LPDWORD; cb: DWORD; var cbNeeded: DWORD): BOOL;
+function EnumProcessModules(hProcess: THandle; lphModule:PHMODULE ; cb: DWORD;
+ var lpcbNeeded: DWORD): BOOL;
+function GetModuleBaseName(hProcess: THandle; hModule: HMODULE;
+ lpBaseName: PWideChar; nSize: DWORD): DWORD;
+function GetModuleBaseNameA(hProcess: THandle; hModule: HMODULE;
+ lpBaseName: PAnsiChar; nSize: DWORD): DWORD;
+function GetModuleBaseNameW(hProcess: THandle; hModule: HMODULE;
+ lpBaseName: PWideChar; nSize: DWORD): DWORD;
+function GetModuleFileNameEx(hProcess: THandle; hModule: HMODULE;
+ lpFilename: PWideChar; nSize: DWORD): DWORD;
+function GetModuleFileNameExA(hProcess: THandle; hModule: HMODULE;
+ lpFilename: PAnsiChar; nSize: DWORD): DWORD;
+function GetModuleFileNameExW(hProcess: THandle; hModule: HMODULE;
+ lpFilename: PWideChar; nSize: DWORD): DWORD;
+function GetModuleInformation(hProcess: THandle; hModule: HMODULE;
+ lpmodinfo: LPMODULEINFO; cb: DWORD): BOOL;
+function EmptyWorkingSet(hProcess: THandle): BOOL;
+function QueryWorkingSet(hProcess: THandle; pv: Pointer; cb: DWORD): BOOL;
+function InitializeProcessForWsWatch(hProcess: THandle): BOOL;
+function GetMappedFileName(hProcess: THandle; lpv: Pointer;
+ lpFilename: PWideChar; nSize: DWORD): DWORD;
+function GetMappedFileNameA(hProcess: THandle; lpv: Pointer;
+ lpFilename: PAnsiChar; nSize: DWORD): DWORD;
+function GetMappedFileNameW(hProcess: THandle; lpv: Pointer;
+ lpFilename: PWideChar; nSize: DWORD): DWORD;
+function GetDeviceDriverBaseName(ImageBase: Pointer; lpBaseName: PWideChar;
+ nSize: DWORD): DWORD;
+function GetDeviceDriverBaseNameA(ImageBase: Pointer; lpBaseName: PAnsiChar;
+ nSize: DWORD): DWORD;
+function GetDeviceDriverBaseNameW(ImageBase: Pointer; lpBaseName: PWideChar;
+ nSize: DWORD): DWORD;
+function GetDeviceDriverFileName(ImageBase: Pointer; lpFileName: PWideChar;
+ nSize: DWORD): DWORD;
+function GetDeviceDriverFileNameA(ImageBase: Pointer; lpFileName: PAnsiChar;
+ nSize: DWORD): DWORD;
+function GetDeviceDriverFileNameW(ImageBase: Pointer; lpFileName: PWideChar;
+ nSize: DWORD): DWORD;
+function EnumDeviceDrivers(lpImageBase: PPointer; cb: DWORD;
+ var lpcbNeeded: DWORD): BOOL;
+function GetProcessMemoryInfo(Process: THandle;
+ ppsmemCounters: PPROCESS_MEMORY_COUNTERS; cb: DWORD): BOOL;
+
+implementation
+
+var
+ hPSAPI: THandle;
+var
+ _EnumProcesses : TEnumProcesses;
+ _EnumProcessModules : TEnumProcessModules;
+ _GetModuleBaseName : TGetModuleBaseNameW;
+ _GetModuleFileNameEx : TGetModuleFileNameExW;
+ _GetModuleBaseNameA : TGetModuleBaseNameA;
+ _GetModuleFileNameExA : TGetModuleFileNameExA;
+ _GetModuleBaseNameW : TGetModuleBaseNameW;
+ _GetModuleFileNameExW : TGetModuleFileNameExW;
+ _GetModuleInformation : TGetModuleInformation;
+ _EmptyWorkingSet : TEmptyWorkingSet;
+ _QueryWorkingSet : TQueryWorkingSet;
+ _InitializeProcessForWsWatch: TInitializeProcessForWsWatch;
+ _GetMappedFileName : TGetMappedFileNameW;
+ _GetDeviceDriverBaseName : TGetDeviceDriverBaseNameW;
+ _GetDeviceDriverFileName : TGetDeviceDriverFileNameW;
+ _GetMappedFileNameA : TGetMappedFileNameA;
+ _GetDeviceDriverBaseNameA : TGetDeviceDriverBaseNameA;
+ _GetDeviceDriverFileNameA : TGetDeviceDriverFileNameA;
+ _GetMappedFileNameW : TGetMappedFileNameW;
+ _GetDeviceDriverBaseNameW : TGetDeviceDriverBaseNameW;
+ _GetDeviceDriverFileNameW : TGetDeviceDriverFileNameW;
+ _EnumDeviceDrivers : TEnumDeviceDrivers;
+ _GetProcessMemoryInfo : TGetProcessMemoryInfo;
+
+function CheckPSAPILoaded: Boolean;
+begin
+ if hPSAPI = 0 then
+ begin
+ hPSAPI := LoadLibrary('PSAPI.dll');
+ if hPSAPI < 32 then
+ begin
+ hPSAPI := 0;
+ Result := False;
+ Exit;
+ end;
+ @_EnumProcesses := GetProcAddress(hPSAPI, PAnsiChar('EnumProcesses'));
+ @_EnumProcessModules := GetProcAddress(hPSAPI, PAnsiChar('EnumProcessModules'));
+ @_GetModuleBaseName := GetProcAddress(hPSAPI, PAnsiChar('GetModuleBaseNameW'));
+ @_GetModuleFileNameEx := GetProcAddress(hPSAPI, PAnsiChar('GetModuleFileNameExW'));
+ @_GetModuleBaseNameA := GetProcAddress(hPSAPI, PAnsiChar('GetModuleBaseNameA'));
+ @_GetModuleFileNameExA := GetProcAddress(hPSAPI, PAnsiChar('GetModuleFileNameExA'));
+ @_GetModuleBaseNameW := GetProcAddress(hPSAPI, PAnsiChar('GetModuleBaseNameW'));
+ @_GetModuleFileNameExW := GetProcAddress(hPSAPI, PAnsiChar('GetModuleFileNameExW'));
+ @_GetModuleInformation := GetProcAddress(hPSAPI, PAnsiChar('GetModuleInformation'));
+ @_EmptyWorkingSet := GetProcAddress(hPSAPI, PAnsiChar('EmptyWorkingSet'));
+ @_QueryWorkingSet := GetProcAddress(hPSAPI, PAnsiChar('QueryWorkingSet'));
+ @_InitializeProcessForWsWatch := GetProcAddress(hPSAPI, PAnsiChar('InitializeProcessForWsWatch'));
+ @_GetMappedFileName := GetProcAddress(hPSAPI, PAnsiChar('GetMappedFileNameW'));
+ @_GetDeviceDriverBaseName := GetProcAddress(hPSAPI, PAnsiChar('GetDeviceDriverBaseNameW'));
+ @_GetDeviceDriverFileName := GetProcAddress(hPSAPI, PAnsiChar('GetDeviceDriverFileNameW'));
+ @_GetMappedFileNameA := GetProcAddress(hPSAPI, PAnsiChar('GetMappedFileNameA'));
+ @_GetDeviceDriverBaseNameA := GetProcAddress(hPSAPI, PAnsiChar('GetDeviceDriverBaseNameA'));
+ @_GetDeviceDriverFileNameA := GetProcAddress(hPSAPI, PAnsiChar('GetDeviceDriverFileNameA'));
+ @_GetMappedFileNameW := GetProcAddress(hPSAPI, PAnsiChar('GetMappedFileNameW'));
+ @_GetDeviceDriverBaseNameW := GetProcAddress(hPSAPI, PAnsiChar('GetDeviceDriverBaseNameW'));
+ @_GetDeviceDriverFileNameW := GetProcAddress(hPSAPI, PAnsiChar('GetDeviceDriverFileNameW'));
+ @_EnumDeviceDrivers := GetProcAddress(hPSAPI, PAnsiChar('EnumDeviceDrivers'));
+ @_GetProcessMemoryInfo := GetProcAddress(hPSAPI, PAnsiChar('GetProcessMemoryInfo'));
+ end;
+ Result := True;
+end;
+
+function EnumProcesses(lpidProcess: LPDWORD; cb: DWORD; var cbNeeded: DWORD): BOOL;
+begin
+ if CheckPSAPILoaded then
+ Result := _EnumProcesses(lpidProcess, cb, cbNeeded)
+ else Result := False;
+end;
+
+function EnumProcessModules(hProcess: THandle; lphModule: PHMODULE; cb: DWORD;
+ var lpcbNeeded: DWORD): BOOL;
+begin
+ if CheckPSAPILoaded then
+ Result := _EnumProcessModules(hProcess, lphModule, cb, lpcbNeeded)
+ else Result := False;
+end;
+
+function GetModuleBaseName(hProcess: THandle; hModule: HMODULE;
+ lpBaseName: PWideChar; nSize: DWORD): DWORD;
+begin
+ if CheckPSAPILoaded then
+ Result := _GetModuleBaseName(hProcess, hModule, lpBaseName, nSize)
+ else Result := 0;
+end;
+
+function GetModuleBaseNameA(hProcess: THandle; hModule: HMODULE;
+ lpBaseName: PAnsiChar; nSize: DWORD): DWORD;
+begin
+ if CheckPSAPILoaded then
+ Result := _GetModuleBaseNameA(hProcess, hModule, lpBaseName, nSize)
+ else Result := 0;
+end;
+
+function GetModuleBaseNameW(hProcess: THandle; hModule: HMODULE;
+ lpBaseName: PWideChar; nSize: DWORD): DWORD;
+begin
+ if CheckPSAPILoaded then
+ Result := _GetModuleBaseNameW(hProcess, hModule, lpBaseName, nSize)
+ else Result := 0;
+end;
+
+function GetModuleFileNameEx(hProcess: THandle; hModule: HMODULE;
+ lpFilename: PWideChar; nSize: DWORD): DWORD;
+begin
+ if CheckPSAPILoaded then
+ Result := _GetModuleFileNameEx(hProcess, hModule, lpFileName, nSize)
+ else Result := 0;
+end;
+
+function GetModuleFileNameExA(hProcess: THandle; hModule: HMODULE;
+ lpFilename: PAnsiChar; nSize: DWORD): DWORD;
+begin
+ if CheckPSAPILoaded then
+ Result := _GetModuleFileNameExA(hProcess, hModule, lpFileName, nSize)
+ else Result := 0;
+end;
+
+function GetModuleFileNameExW(hProcess: THandle; hModule: HMODULE;
+ lpFilename: PWideChar; nSize: DWORD): DWORD;
+begin
+ if CheckPSAPILoaded then
+ Result := _GetModuleFileNameExW(hProcess, hModule, lpFileName, nSize)
+ else Result := 0;
+end;
+
+function GetModuleInformation(hProcess: THandle; hModule: HMODULE;
+ lpmodinfo: LPMODULEINFO; cb: DWORD): BOOL;
+begin
+ if CheckPSAPILoaded then
+ Result := _GetModuleInformation(hProcess, hModule, lpmodinfo, cb)
+ else Result := False;
+end;
+
+function EmptyWorkingSet(hProcess: THandle): BOOL;
+begin
+ if CheckPSAPILoaded then
+ Result := _EmptyWorkingSet(hProcess)
+ else Result := False;
+end;
+
+function QueryWorkingSet(hProcess: THandle; pv: Pointer; cb: DWORD): BOOL;
+begin
+ if CheckPSAPILoaded then
+ Result := _QueryWorkingSet(hProcess, pv, cb)
+ else Result := False;
+end;
+
+function InitializeProcessForWsWatch(hProcess: THandle): BOOL;
+begin
+ if CheckPSAPILoaded then
+ Result := _InitializeProcessForWsWatch(hProcess)
+ else Result := False;
+end;
+
+function GetMappedFileName(hProcess: THandle; lpv: Pointer;
+ lpFilename: PWideChar; nSize: DWORD): DWORD;
+begin
+ if CheckPSAPILoaded then
+ Result := _GetMappedFileName(hProcess, lpv, lpFileName, nSize)
+ else Result := 0;
+end;
+
+function GetMappedFileNameA(hProcess: THandle; lpv: Pointer;
+ lpFilename: PAnsiChar; nSize: DWORD): DWORD;
+begin
+ if CheckPSAPILoaded then
+ Result := _GetMappedFileNameA(hProcess, lpv, lpFileName, nSize)
+ else Result := 0;
+end;
+
+function GetMappedFileNameW(hProcess: THandle; lpv: Pointer;
+ lpFilename: PWideChar; nSize: DWORD): DWORD;
+begin
+ if CheckPSAPILoaded then
+ Result := _GetMappedFileNameW(hProcess, lpv, lpFileName, nSize)
+ else Result := 0;
+end;
+
+function GetDeviceDriverBaseName(ImageBase: Pointer; lpBaseName: PWideChar;
+ nSize: DWORD): DWORD;
+begin
+ if CheckPSAPILoaded then
+ Result := _GetDeviceDriverBasename(ImageBase, lpBaseName, nSize)
+ else Result := 0;
+end;
+
+function GetDeviceDriverBaseNameA(ImageBase: Pointer; lpBaseName: PAnsiChar;
+ nSize: DWORD): DWORD;
+begin
+ if CheckPSAPILoaded then
+ Result := _GetDeviceDriverBasenameA(ImageBase, lpBaseName, nSize)
+ else Result := 0;
+end;
+
+function GetDeviceDriverBaseNameW(ImageBase: Pointer; lpBaseName: PWideChar;
+ nSize: DWORD): DWORD;
+begin
+ if CheckPSAPILoaded then
+ Result := _GetDeviceDriverBasenameW(ImageBase, lpBaseName, nSize)
+ else Result := 0;
+end;
+
+function GetDeviceDriverFileName(ImageBase: Pointer; lpFileName: PWideChar;
+ nSize: DWORD): DWORD;
+begin
+ if CheckPSAPILoaded then
+ Result := _GetDeviceDriverFileName(ImageBase, lpFileName, nSize)
+ else Result := 0;
+end;
+
+function GetDeviceDriverFileNameA(ImageBase: Pointer; lpFileName: PAnsiChar;
+ nSize: DWORD): DWORD;
+begin
+ if CheckPSAPILoaded then
+ Result := _GetDeviceDriverFileNameA(ImageBase, lpFileName, nSize)
+ else Result := 0;
+end;
+
+function GetDeviceDriverFileNameW(ImageBase: Pointer; lpFileName: PWideChar;
+ nSize: DWORD): DWORD;
+begin
+ if CheckPSAPILoaded then
+ Result := _GetDeviceDriverFileNameW(ImageBase, lpFileName, nSize)
+ else Result := 0;
+end;
+
+function EnumDeviceDrivers(lpImageBase: PPointer; cb: DWORD;
+ var lpcbNeeded: DWORD): BOOL;
+begin
+ if CheckPSAPILoaded then
+ Result := _EnumDeviceDrivers(lpImageBase, cb, lpcbNeeded)
+ else Result := False;
+end;
+
+function GetProcessMemoryInfo(Process: THandle;
+ ppsmemCounters: PPROCESS_MEMORY_COUNTERS; cb: DWORD): BOOL;
+begin
+ if CheckPSAPILoaded then
+ Result := _GetProcessMemoryInfo(Process, ppsmemCounters, cb)
+ else Result := False;
+end;
+
+end.
diff --git a/plugins/Libs/bz2/BLOCKS~1.OBJ b/plugins/Libs/bz2/BLOCKS~1.OBJ
new file mode 100644
index 0000000000..df6ef1bdb7
--- /dev/null
+++ b/plugins/Libs/bz2/BLOCKS~1.OBJ
Binary files differ
diff --git a/plugins/Libs/bz2/BZLIB.OBJ b/plugins/Libs/bz2/BZLIB.OBJ
new file mode 100644
index 0000000000..5579d79de0
--- /dev/null
+++ b/plugins/Libs/bz2/BZLIB.OBJ
Binary files differ
diff --git a/plugins/Libs/bz2/COMPRESS.OBJ b/plugins/Libs/bz2/COMPRESS.OBJ
new file mode 100644
index 0000000000..ea6fbed6ab
--- /dev/null
+++ b/plugins/Libs/bz2/COMPRESS.OBJ
Binary files differ
diff --git a/plugins/Libs/bz2/DECOMP~1.OBJ b/plugins/Libs/bz2/DECOMP~1.OBJ
new file mode 100644
index 0000000000..989a17da81
--- /dev/null
+++ b/plugins/Libs/bz2/DECOMP~1.OBJ
Binary files differ
diff --git a/plugins/Libs/bz2/HUFFMAN.OBJ b/plugins/Libs/bz2/HUFFMAN.OBJ
new file mode 100644
index 0000000000..994b1305af
--- /dev/null
+++ b/plugins/Libs/bz2/HUFFMAN.OBJ
Binary files differ
diff --git a/plugins/Libs/delphicommctrl.inc b/plugins/Libs/delphicommctrl.inc
new file mode 100644
index 0000000000..c7fa1bc628
--- /dev/null
+++ b/plugins/Libs/delphicommctrl.inc
@@ -0,0 +1,1594 @@
+{*******************************************************************************
+ delpicommctrl.inc
+ -- included in KOL.pas --
+*******************************************************************************}
+
+{$IFNDEF FPC}
+{$IFNDEF TMSG_WINDOWS}
+ {$DEFINE TMSG_DECODED}
+{$ENDIF}
+{$ENDIF}
+{$IFDEF TMSG_DECODED}
+{$I MsgDecode.pas}
+type
+ TMsg = packed record
+ CASE Integer OF
+ 0: (
+ hwnd: HWND;
+ message: UINT;
+ wParam: WPARAM;
+ lParam: LPARAM;
+ time: DWORD;
+ pt: TPoint;
+ );
+ //1: ( Bmsg: Windows.TMsg; );
+ 2: ( Cmsg: TMsgDecoded; );
+ end;
+
+ tagMSG = TMsg;
+{$ENDIF TMSG_DECODED}
+
+
+////////////////////////////////////////////////////////////////////////////
+// this part of unit contains definitions moved here from CommCtrl.pas
+// (using of CommCtrl.pas in Delphi3 leads to increase size of executable
+// onto 30K)
+
+type
+ PTCItemA = ^TTCItemA;
+ PTCItemW = ^TTCItemW;
+ PTCItem = {$IFDEF UNICODE_CTRLS} PTCItemW {$ELSE} PTCItemA {$ENDIF};
+ tagTCITEMA = packed record
+ mask: UINT;
+ dwState: UINT;
+ dwStateMask: UINT;
+ pszText: PAnsiChar;
+ cchTextMax: Integer;
+ iImage: Integer;
+ lParam: LPARAM;
+ end;
+ tagTCITEMW = packed record
+ mask: UINT;
+ dwState: UINT;
+ dwStateMask: UINT;
+ pszText: PWideChar;
+ cchTextMax: Integer;
+ iImage: Integer;
+ lParam: LPARAM;
+ end;
+
+ PTCKeyDown = ^TTCKeyDown;
+ TTCKEYDOWN = packed record
+ hdr: TNMHDR;
+ wVKey: Word;
+ flags: UINT;
+ end;
+
+ tagTCITEM = {$IFDEF UNICODE_CTRLS} tagTCITEMW {$ELSE} tagTCITEMA {$ENDIF};
+ _TC_ITEMA = tagTCITEMA;
+ _TC_ITEMW = tagTCITEMW;
+ _TC_ITEM = {$IFDEF UNICODE_CTRLS} _TC_ITEMW {$ELSE} _TC_ITEMA {$ENDIF};
+ TTCItemA = tagTCITEMA;
+ TTCItemW = tagTCITEMW;
+ TTCItem = {$IFDEF UNICODE_CTRLS} TTCItemW {$ELSE} TTCItemA {$ENDIF};
+ TC_ITEMA = tagTCITEMA;
+ TC_ITEMW = tagTCITEMW;
+ TC_ITEM = {$IFDEF UNICODE_CTRLS} TC_ITEMW {$ELSE} TC_ITEMA {$ENDIF};
+
+
+const
+ CCM_FIRST = $2000; { Common control shared messages }
+ CCM_SETBKCOLOR = CCM_FIRST + 1; // lParam is bkColor
+ CCM_SETCOLORSCHEME = CCM_FIRST + 2; // lParam is color scheme
+ CCM_GETCOLORSCHEME = CCM_FIRST + 3; // fills in COLORSCHEME pointed to by lParam
+ CCM_GETDROPTARGET = CCM_FIRST + 4;
+ CCM_SETUNICODEFORMAT = CCM_FIRST + 5;
+ CCM_GETUNICODEFORMAT = CCM_FIRST + 6;
+
+ TCS_SCROLLOPPOSITE = $0001; // assumes multiline tab
+ TCS_BOTTOM = $0002;
+ TCS_RIGHT = $0002;
+ TCS_MULTISELECT = $0004; // allow multi-select in button mode
+ TCS_FLATBUTTONS = $0008;
+ TCS_FORCEICONLEFT = $0010;
+ TCS_FORCELABELLEFT = $0020;
+ TCS_HOTTRACK = $0040;
+ TCS_VERTICAL = $0080;
+ TCS_TABS = $0000;
+ TCS_BUTTONS = $0100;
+ TCS_SINGLELINE = $0000;
+ TCS_MULTILINE = $0200;
+ TCS_RIGHTJUSTIFY = $0000;
+ TCS_FIXEDWIDTH = $0400;
+ TCS_RAGGEDRIGHT = $0800;
+ TCS_FOCUSONBUTTONDOWN = $1000;
+ TCS_OWNERDRAWFIXED = $2000;
+ TCS_TOOLTIPS = $4000;
+ TCS_FOCUSNEVER = $8000;
+
+ TCS_EX_FLATSEPARATORS = $00000001;
+ TCS_EX_REGISTERDROP = $00000002;
+
+ TCM_FIRST = $1300; { Tab control messages }
+ TCM_GETIMAGELIST = TCM_FIRST + 2;
+ TCM_SETIMAGELIST = TCM_FIRST + 3;
+ TCM_GETITEMCOUNT = TCM_FIRST + 4;
+ TCM_GETITEMA = TCM_FIRST + 5;
+ TCM_SETITEMA = TCM_FIRST + 6;
+ TCM_INSERTITEMA = TCM_FIRST + 7;
+ TCM_DELETEITEM = TCM_FIRST + 8;
+ TCM_DELETEALLITEMS = TCM_FIRST + 9;
+ TCM_GETITEMRECT = TCM_FIRST + 10;
+ TCM_GETCURSEL = TCM_FIRST + 11;
+ TCM_SETCURSEL = TCM_FIRST + 12;
+ TCM_HITTEST = TCM_FIRST + 13;
+ TCM_SETITEMEXTRA = TCM_FIRST + 14;
+ TCM_ADJUSTRECT = TCM_FIRST + 40;
+ TCM_SETITEMSIZE = TCM_FIRST + 41;
+ TCM_REMOVEIMAGE = TCM_FIRST + 42;
+ TCM_SETPADDING = TCM_FIRST + 43;
+ TCM_GETROWCOUNT = TCM_FIRST + 44;
+ TCM_GETTOOLTIPS = TCM_FIRST + 45;
+ TCM_SETTOOLTIPS = TCM_FIRST + 46;
+ TCM_GETCURFOCUS = TCM_FIRST + 47;
+ TCM_SETCURFOCUS = TCM_FIRST + 48;
+ TCM_SETMINTABWIDTH = TCM_FIRST + 49;
+ TCM_DESELECTALL = TCM_FIRST + 50;
+ TCM_HIGHLIGHTITEM = TCM_FIRST + 51;
+ TCM_SETEXTENDEDSTYLE = TCM_FIRST + 52; // optional wParam == mask
+ TCM_GETEXTENDEDSTYLE = TCM_FIRST + 53;
+ TCM_GETITEMW = TCM_FIRST + 60;
+ TCM_SETITEMW = TCM_FIRST + 61;
+ TCM_INSERTITEMW = TCM_FIRST + 62;
+ TCM_SETUNICODEFORMAT = CCM_SETUNICODEFORMAT;
+ TCM_GETUNICODEFORMAT = CCM_GETUNICODEFORMAT;
+ TCM_GETITEM = {$IFDEF UNICODE_CTRLS} TCM_GETITEMW
+ {$ELSE} TCM_GETITEMA {$ENDIF};
+ TCM_SETITEM = {$IFDEF UNICODE_CTRLS} TCM_SETITEMW
+ {$ELSE} TCM_SETITEMA {$ENDIF};
+ TCM_INSERTITEM = {$IFDEF UNICODE_CTRLS} TCM_INSERTITEMW
+ {$ELSE} TCM_INSERTITEMA {$ENDIF};
+
+ TCN_FIRST = 0-550; { tab control }
+ TCN_LAST = 0-580;
+ TCN_KEYDOWN = TCN_FIRST - 0;
+ TCN_SELCHANGE = TCN_FIRST - 1;
+ TCN_SELCHANGING = TCN_FIRST - 2;
+ TCN_GETOBJECT = TCN_FIRST - 3;
+
+ TCIF_TEXT = $0001;
+ TCIF_IMAGE = $0002;
+ TCIF_RTLREADING = $0004;
+ TCIF_PARAM = $0008;
+ TCIF_STATE = $0010;
+
+ PBS_SMOOTH = 01;
+ PBS_VERTICAL = 04;
+
+ PBM_SETRANGE = WM_USER+1;
+ PBM_SETPOS = WM_USER+2;
+ PBM_DELTAPOS = WM_USER+3;
+ PBM_SETSTEP = WM_USER+4;
+ PBM_STEPIT = WM_USER+5;
+ PBM_SETRANGE32 = WM_USER+6; // lParam = high, wParam = low
+ PBM_GETRANGE = WM_USER+7; // lParam = PPBRange or Nil
+ // wParam = False: Result = high
+ // wParam = True: Result = low
+ PBM_GETPOS = WM_USER+8;
+ PBM_SETBARCOLOR = WM_USER+9; // lParam = bar color
+ PBM_SETBKCOLOR = CCM_SETBKCOLOR; // lParam = bkColor
+
+ SB_SETTEXTA = WM_USER+1;
+ SB_GETTEXTA = WM_USER+2;
+ SB_GETTEXTLENGTHA = WM_USER+3;
+ SB_SETTIPTEXTA = WM_USER+16;
+ SB_GETTIPTEXTA = WM_USER+18;
+
+ SB_SETTEXTW = WM_USER+11;
+ SB_GETTEXTW = WM_USER+13;
+ SB_GETTEXTLENGTHW = WM_USER+12;
+ SB_SETTIPTEXTW = WM_USER+17;
+ SB_GETTIPTEXTW = WM_USER+19;
+
+ SB_SETTEXT = {$IFDEF UNICODE_CTRLS} SB_SETTEXTW {$ELSE} SB_SETTEXTA {$ENDIF};
+ SB_GETTEXT = {$IFDEF UNICODE_CTRLS} SB_GETTEXTW {$ELSE} SB_GETTEXTA {$ENDIF};
+ SB_GETTEXTLENGTH = {$IFDEF UNICODE_CTRLS} SB_GETTEXTLENGTHW {$ELSE} SB_GETTEXTLENGTHA {$ENDIF};
+ SB_SETTIPTEXT = {$IFDEF UNICODE_CTRLS} SB_SETTIPTEXTW {$ELSE} SB_SETTIPTEXTA {$ENDIF};
+ SB_GETTIPTEXT = {$IFDEF UNICODE_CTRLS} SB_GETTIPTEXTW {$ELSE} SB_GETTIPTEXTA {$ENDIF};
+
+ SB_SETPARTS = WM_USER+4;
+ SB_GETPARTS = WM_USER+6;
+ SB_GETBORDERS = WM_USER+7;
+ SB_SETMINHEIGHT = WM_USER+8;
+ SB_SIMPLE = WM_USER+9;
+ SB_GETRECT = WM_USER + 10;
+ SB_ISSIMPLE = WM_USER+14;
+ SB_SETICON = WM_USER+15;
+ SB_GETICON = WM_USER+20;
+ SB_SETUNICODEFORMAT = CCM_SETUNICODEFORMAT;
+ SB_GETUNICODEFORMAT = CCM_GETUNICODEFORMAT;
+
+ SBT_OWNERDRAW = $1000;
+ SBT_NOBORDERS = $0100;
+ SBT_POPOUT = $0200;
+ SBT_RTLREADING = $0400;
+ SBT_TOOLTIPS = $0800;
+
+ SB_SETBKCOLOR = CCM_SETBKCOLOR; // lParam = bkColor
+
+ SBARS_SIZEGRIP = $0100;
+
+ { List View Styles }
+ LVS_ICON = $0000;
+ LVS_REPORT = $0001;
+ LVS_SMALLICON = $0002;
+ LVS_LIST = $0003;
+
+ LVS_TYPEMASK = $0003;
+ LVS_SINGLESEL = $0004;
+ LVS_SHOWSELALWAYS = $0008;
+ LVS_SORTASCENDING = $0010;
+ LVS_SORTDESCENDING = $0020;
+ LVS_SHAREIMAGELISTS = $0040;
+ LVS_NOLABELWRAP = $0080;
+ LVS_AUTOARRANGE = $0100;
+ LVS_EDITLABELS = $0200;
+ LVS_OWNERDATA = $1000;
+ LVS_NOSCROLL = $2000;
+
+ LVS_TYPESTYLEMASK = $FC00;
+
+ LVS_ALIGNTOP = $0000;
+ LVS_ALIGNLEFT = $0800;
+ LVS_ALIGNMASK = $0c00;
+
+ LVS_OWNERDRAWFIXED = $0400;
+ LVS_NOCOLUMNHEADER = $4000;
+ LVS_NOSORTHEADER = $8000;
+
+ { List View Extended Styles }
+ LVS_EX_GRIDLINES = $00000001;
+ LVS_EX_SUBITEMIMAGES = $00000002;
+ LVS_EX_CHECKBOXES = $00000004;
+ LVS_EX_TRACKSELECT = $00000008;
+ LVS_EX_HEADERDRAGDROP = $00000010;
+ LVS_EX_FULLROWSELECT = $00000020; // applies to report mode only
+ LVS_EX_ONECLICKACTIVATE = $00000040;
+ LVS_EX_TWOCLICKACTIVATE = $00000080;
+ LVS_EX_FLATSB = $00000100;
+ LVS_EX_REGIONAL = $00000200;
+ LVS_EX_INFOTIP = $00000400; // listview does InfoTips for you
+ LVS_EX_LABELTIP = $00004000;
+ LVS_EX_UNDERLINEHOT = $00000800;
+ LVS_EX_UNDERLINECOLD = $00001000;
+ LVS_EX_MULTIWORKAREAS = $00002000;
+
+ I_IMAGECALLBACK = -1;
+ I_SKIP = -2;
+ LVSIL_NORMAL = 0;
+ LVSIL_SMALL = 1;
+ LVSIL_STATE = 2;
+
+ { List View column styles }
+ LVCF_FMT = $0001;
+ LVCF_WIDTH = $0002;
+ LVCF_TEXT = $0004;
+ LVCF_SUBITEM = $0008;
+ LVCF_IMAGE = $0010;
+ LVCF_ORDER = $0020;
+ LVSCW_AUTOSIZE = -1;
+ LVSCW_AUTOSIZE_USEHEADER = -2;
+
+ LVCFMT_LEFT = $0000;
+ LVCFMT_RIGHT = $0001;
+ LVCFMT_CENTER = $0002;
+ LVCFMT_JUSTIFYMASK = $0003;
+
+ LVCFMT_IMAGE = $0800;
+ LVCFMT_BITMAP_ON_RIGHT = $1000;
+ LVCFMT_COL_HAS_IMAGES = $8000;
+
+ LVIF_TEXT = $0001;
+ LVIF_IMAGE = $0002;
+ LVIF_PARAM = $0004;
+ LVIF_STATE = $0008;
+ LVIF_INDENT = $0010;
+ //LVIF_NORECOMPUTE = $0800;
+ LVIF_DI_SETITEM = $1000;
+
+ LVIS_FOCUSED = $0001;
+ LVIS_SELECTED = $0002;
+ LVIS_CUT = $0004;
+ LVIS_DROPHILITED = $0008;
+ //LVIS_ACTIVATING = $0020;
+
+ LVIS_OVERLAYMASK = $0F00;
+ LVIS_STATEIMAGEMASK = $F000;
+
+ { List View messages }
+ LVM_FIRST = $1000; { ListView messages }
+ LVM_SETUNICODEFORMAT = CCM_SETUNICODEFORMAT;
+ LVM_GETUNICODEFORMAT = CCM_GETUNICODEFORMAT;
+ LVM_GETBKCOLOR = LVM_FIRST + 0;
+ LVM_SETBKCOLOR = LVM_FIRST + 1;
+ LVM_GETIMAGELIST = LVM_FIRST + 2;
+ LVM_SETIMAGELIST = LVM_FIRST + 3;
+ LVM_GETITEMCOUNT = LVM_FIRST + 4;
+ LVM_GETITEMA = LVM_FIRST + 5;
+ LVM_SETITEMA = LVM_FIRST + 6;
+ LVM_INSERTITEMA = LVM_FIRST + 7;
+ LVM_DELETEITEM = LVM_FIRST + 8;
+ LVM_DELETEALLITEMS = LVM_FIRST + 9;
+ LVM_GETCALLBACKMASK = LVM_FIRST + 10;
+ LVM_SETCALLBACKMASK = LVM_FIRST + 11;
+ LVM_GETNEXTITEM = LVM_FIRST + 12;
+ LVM_FINDITEMA = LVM_FIRST + 13;
+ LVM_GETITEMRECT = LVM_FIRST + 14;
+ LVM_SETITEMPOSITION = LVM_FIRST + 15;
+ LVM_GETITEMPOSITION = LVM_FIRST + 16;
+ LVM_GETSTRINGWIDTHA = LVM_FIRST + 17;
+ LVM_HITTEST = LVM_FIRST + 18;
+ LVM_ENSUREVISIBLE = LVM_FIRST + 19;
+ LVM_SCROLL = LVM_FIRST + 20;
+ LVM_REDRAWITEMS = LVM_FIRST + 21;
+ LVM_ARRANGE = LVM_FIRST + 22;
+ LVM_EDITLABELA = LVM_FIRST + 23;
+ LVM_GETCOLUMNA = LVM_FIRST + 25;
+ LVM_SETCOLUMNA = LVM_FIRST + 26;
+ LVM_INSERTCOLUMNA = LVM_FIRST + 27;
+ LVM_DELETECOLUMN = LVM_FIRST + 28;
+ LVM_GETCOLUMNWIDTH = LVM_FIRST + 29;
+ LVM_SETCOLUMNWIDTH = LVM_FIRST + 30;
+ LVM_GETHEADER = LVM_FIRST + 31;
+
+ LVM_CREATEDRAGIMAGE = LVM_FIRST + 33;
+ LVM_GETVIEWRECT = LVM_FIRST + 34;
+ LVM_GETTEXTCOLOR = LVM_FIRST + 35;
+ LVM_SETTEXTCOLOR = LVM_FIRST + 36;
+ LVM_GETTEXTBKCOLOR = LVM_FIRST + 37;
+ LVM_SETTEXTBKCOLOR = LVM_FIRST + 38;
+ LVM_GETTOPINDEX = LVM_FIRST + 39;
+ LVM_GETCOUNTPERPAGE = LVM_FIRST + 40;
+ LVM_GETORIGIN = LVM_FIRST + 41;
+ LVM_UPDATE = LVM_FIRST + 42;
+ LVM_SETITEMSTATE = LVM_FIRST + 43;
+ LVM_GETITEMSTATE = LVM_FIRST + 44;
+ LVM_GETITEMTEXTA = LVM_FIRST + 45;
+ LVM_SETITEMTEXTA = LVM_FIRST + 46;
+ LVM_SETITEMCOUNT = LVM_FIRST + 47;
+ LVM_SORTITEMS = LVM_FIRST + 48;
+ LVM_SETITEMPOSITION32 = LVM_FIRST + 49;
+ LVM_GETSELECTEDCOUNT = LVM_FIRST + 50;
+ LVM_GETITEMSPACING = LVM_FIRST + 51;
+ LVM_GETISEARCHSTRINGA = LVM_FIRST + 52;
+ LVM_SETICONSPACING = LVM_FIRST + 53;
+ LVM_SETEXTENDEDLISTVIEWSTYLE = LVM_FIRST + 54;
+ LVM_GETEXTENDEDLISTVIEWSTYLE = LVM_FIRST + 55;
+ LVM_GETSUBITEMRECT = LVM_FIRST + 56;
+ LVM_SUBITEMHITTEST = LVM_FIRST + 57;
+ LVM_SETCOLUMNORDERARRAY = LVM_FIRST + 58;
+ LVM_GETCOLUMNORDERARRAY = LVM_FIRST + 59;
+ LVM_SETHOTITEM = LVM_FIRST + 60;
+ LVM_GETHOTITEM = LVM_FIRST + 61;
+ LVM_SETHOTCURSOR = LVM_FIRST + 62;
+ LVM_GETHOTCURSOR = LVM_FIRST + 63;
+ LVM_APPROXIMATEVIEWRECT = LVM_FIRST + 64;
+ LVM_SETWORKAREA = LVM_FIRST + 65;
+ LVM_GETSELECTIONMARK = LVM_FIRST + 66;
+ LVM_SETSELECTIONMARK = LVM_FIRST + 67;
+ LVM_SETBKIMAGEA = LVM_FIRST + 68;
+ LVM_GETBKIMAGEA = LVM_FIRST + 69;
+ LVM_GETWORKAREAS = LVM_FIRST + 70;
+ LVM_SETHOVERTIME = LVM_FIRST + 71;
+ LVM_GETHOVERTIME = LVM_FIRST + 72;
+ LVM_GETNUMBEROFWORKAREAS = LVM_FIRST + 73;
+ LVM_SETTOOLTIPS = LVM_FIRST + 74;
+ LVM_GETITEMW = LVM_FIRST + 75;
+ LVM_SETITEMW = LVM_FIRST + 76;
+ LVM_INSERTITEMW = LVM_FIRST + 77;
+ LVM_GETTOOLTIPS = LVM_FIRST + 78;
+
+ LVM_SORTITEMSEX = LVM_FIRST + 81;
+
+ LVM_FINDITEMW = LVM_FIRST + 83;
+ LVM_GETSTRINGWIDTHW = LVM_FIRST + 87;
+
+ LVM_GETCOLUMNW = LVM_FIRST + 95;
+ LVM_SETCOLUMNW = LVM_FIRST + 96;
+ LVM_INSERTCOLUMNW = LVM_FIRST + 97;
+
+ LVM_GETITEMTEXTW = LVM_FIRST + 115;
+ LVM_SETITEMTEXTW = LVM_FIRST + 116;
+ LVM_GETISEARCHSTRINGW = LVM_FIRST + 117;
+ LVM_EDITLABELW = LVM_FIRST + 118;
+
+ LVM_SETBKIMAGEW = LVM_FIRST + 138;
+ LVM_GETBKIMAGEW = LVM_FIRST + 139;
+
+ LVM_GETITEM = {$IFDEF UNICODE_CTRLS} LVM_GETITEMW {$ELSE} LVM_GETITEMA {$ENDIF};
+ LVM_SETITEM = {$IFDEF UNICODE_CTRLS} LVM_SETITEMW {$ELSE} LVM_SETITEMA {$ENDIF};
+ LVM_INSERTITEM = {$IFDEF UNICODE_CTRLS} LVM_INSERTITEMW {$ELSE} LVM_INSERTITEMA {$ENDIF};
+ LVM_GETCOLUMN = {$IFDEF UNICODE_CTRLS} LVM_GETCOLUMNW {$ELSE} LVM_GETCOLUMNA {$ENDIF};
+ LVM_SETCOLUMN = {$IFDEF UNICODE_CTRLS} LVM_SETCOLUMNW {$ELSE} LVM_SETCOLUMNA {$ENDIF};
+ LVM_INSERTCOLUMN = {$IFDEF UNICODE_CTRLS} LVM_INSERTCOLUMNW {$ELSE} LVM_INSERTCOLUMNA {$ENDIF};
+ LVM_GETITEMTEXT = {$IFDEF UNICODE_CTRLS} LVM_GETITEMTEXTW {$ELSE} LVM_GETITEMTEXTA {$ENDIF};
+ LVM_SETITEMTEXT = {$IFDEF UNICODE_CTRLS} LVM_SETITEMTEXTW {$ELSE} LVM_SETITEMTEXTA {$ENDIF};
+ LVM_EDITLABEL = {$IFDEF UNICODE_CTRLS} LVM_EDITLABELW {$ELSE} LVM_EDITLABELA {$ENDIF};
+ LVM_FINDITEM = {$IFDEF UNICODE_CTRLS} LVM_FINDITEMW {$ELSE} LVM_FINDITEMA {$ENDIF};
+ LVM_GETISEARCHSTRING = {$IFDEF UNICODE_CTRLS} LVM_GETISEARCHSTRINGW {$ELSE} LVM_GETISEARCHSTRINGA {$ENDIF};
+ LVM_GETSTRINGWIDTH = {$IFDEF UNICODE_CTRLS} LVM_GETSTRINGWIDTHW {$ELSE} LVM_GETSTRINGWIDTHA {$ENDIF};
+
+ LVM_SETBKIMAGE = {$IFDEF UNICODE_CTRLS} LVM_SETBKIMAGEW {$ELSE} LVM_SETBKIMAGEA {$ENDIF};
+ LVM_GETBKIMAGE = {$IFDEF UNICODE_CTRLS} LVM_GETBKIMAGEW {$ELSE} LVM_GETBKIMAGEA {$ENDIF};
+
+ LV_MAX_WORKAREAS = 16;
+
+ LVIR_BOUNDS = 0;
+ LVIR_ICON = 1;
+ LVIR_LABEL = 2;
+ LVIR_SELECTBOUNDS = 3;
+
+ LVHT_NOWHERE = $0001;
+ LVHT_ONITEMICON = $0002;
+ LVHT_ONITEMLABEL = $0004;
+ LVHT_ONITEMSTATEICON = $0008;
+ LVHT_ONITEM = LVHT_ONITEMICON or LVHT_ONITEMLABEL or
+ LVHT_ONITEMSTATEICON;
+ LVHT_ABOVE = $0008;
+ LVHT_BELOW = $0010;
+ LVHT_TORIGHT = $0020;
+ LVHT_TOLEFT = $0040;
+
+ LVNI_ALL = $0000;
+ LVNI_FOCUSED = $0001;
+ LVNI_SELECTED = $0002;
+ LVNI_CUT = $0004;
+ LVNI_DROPHILITED = $0008;
+
+ LVNI_ABOVE = $0100;
+ LVNI_BELOW = $0200;
+ LVNI_TOLEFT = $0400;
+ LVNI_TORIGHT = $0800;
+
+ { WM_NOTIFY codes }
+ NM_FIRST = 0- 0; { generic to all controls }
+ NM_LAST = 0- 99;
+ NM_OUTOFMEMORY = NM_FIRST-1;
+ NM_CLICK = NM_FIRST-2;
+ NM_DBLCLK = NM_FIRST-3;
+ NM_RETURN = NM_FIRST-4;
+ NM_RCLICK = NM_FIRST-5;
+ NM_RDBLCLK = NM_FIRST-6;
+ NM_SETFOCUS = NM_FIRST-7;
+ NM_KILLFOCUS = NM_FIRST-8;
+ NM_CUSTOMDRAW = NM_FIRST-12;
+ NM_HOVER = NM_FIRST-13;
+ NM_NCHITTEST = NM_FIRST-14; // uses NMMOUSE struct
+ NM_KEYDOWN = NM_FIRST-15; // uses NMKEY struct
+ NM_RELEASEDCAPTURE = NM_FIRST-16;
+ NM_SETCURSOR = NM_FIRST-17; // uses NMMOUSE struct
+ NM_CHAR = NM_FIRST-18; // uses NMCHAR struct
+
+ LVN_FIRST = 0-100; { listview }
+ LVN_LAST = 0-199;
+ LVN_ITEMCHANGING = LVN_FIRST-0;
+ LVN_ITEMCHANGED = LVN_FIRST-1;
+ LVN_INSERTITEM = LVN_FIRST-2;
+ LVN_DELETEITEM = LVN_FIRST-3;
+ LVN_DELETEALLITEMS = LVN_FIRST-4;
+ LVN_COLUMNCLICK = LVN_FIRST-8;
+ LVN_BEGINDRAG = LVN_FIRST-9;
+ LVN_BEGINRDRAG = LVN_FIRST-11;
+ LVN_ODCACHEHINT = LVN_FIRST-13;
+ LVN_ODFINDITEMA = LVN_FIRST-52;
+ LVN_ODFINDITEMW = LVN_FIRST-79;
+ LVN_ITEMACTIVATE = LVN_FIRST-14;
+ LVN_ODSTATECHANGED = LVN_FIRST-15;
+ LVN_ODFINDITEM = {$IFDEF UNICODE_CTRLS} LVN_ODFINDITEMW {$ELSE} LVN_ODFINDITEMA {$ENDIF};
+ LVN_BEGINLABELEDITA = LVN_FIRST-5;
+ LVN_ENDLABELEDITA = LVN_FIRST-6;
+ LVN_BEGINLABELEDITW = LVN_FIRST-75;
+ LVN_ENDLABELEDITW = LVN_FIRST-76;
+ LVN_BEGINLABELEDIT = {$IFDEF UNICODE_CTRLS} LVN_BEGINLABELEDITW {$ELSE} LVN_BEGINLABELEDITA {$ENDIF};
+ LVN_ENDLABELEDIT = {$IFDEF UNICODE_CTRLS} LVN_ENDLABELEDITW {$ELSE} LVN_ENDLABELEDITA {$ENDIF};
+ LVN_HOTTRACK = LVN_FIRST-21;
+ LVN_GETDISPINFOA = LVN_FIRST-50;
+ LVN_SETDISPINFOA = LVN_FIRST-51;
+ LVN_GETDISPINFOW = LVN_FIRST-77;
+ LVN_SETDISPINFOW = LVN_FIRST-78;
+ LVN_GETDISPINFO = {$IFDEF UNICODE_CTRLS} LVN_GETDISPINFOW {$ELSE} LVN_GETDISPINFOA {$ENDIF};
+ LVN_SETDISPINFO = {$IFDEF UNICODE_CTRLS} LVN_SETDISPINFOW {$ELSE} LVN_SETDISPINFOA {$ENDIF};
+
+type
+ tagNMLVODSTATECHANGE = packed record
+ hdr: TNMHdr;
+ iFrom: Integer;
+ iTo: Integer;
+ uNewState: UINT;
+ uOldState: UINT;
+ end;
+ PNMLVODStateChange = ^TNMLVODStateChange;
+ TNMLVODStateChange = tagNMLVODSTATECHANGE;
+
+type
+ PLVColumn = ^TLVColumn;
+ TLVColumn = packed record
+ mask: DWORD;
+ fmt: DWORD;
+ cx: Integer;
+ pszText: PKOL_Char;
+ cchTextMax: Integer;
+ iSubItem: Integer;
+ // only IE4+ :
+ iImage: Integer;
+ iOrder: Integer;
+ end;
+
+ PLVItem = ^TLVItem;
+ TLVItem = packed record
+ mask: DWORD;
+ iItem: Integer;
+ iSubItem: Integer;
+ state: Integer;
+ stateMask: DWORD;
+ pszText: PKOL_Char;
+ cchTextMax: Integer;
+ iImage: Integer;
+ lParam: LParam;
+ iIndent: Integer; // only for IE3.0 and higher
+ end;
+
+ PLVDispInfo = ^TLVDispInfo;
+ TLVDispInfo = packed record
+ hdr: TNMHDR;
+ item: TLVItem;
+ end;
+
+ PLVFindInfoA = ^TLVFindInfo;
+ TLVFindInfo = packed record
+ flags: UINT;
+ psz: PKOLChar;
+ lParam: LPARAM;
+ pt: TPoint;
+ vkDirection: UINT;
+ end;
+ PLVFindInfoW = ^TLVFindInfoW;
+ TLVFindInfoW = packed record
+ flags: UINT;
+ psz: PWideChar;
+ lParam: LPARAM;
+ pt: TPoint;
+ vkDirection: UINT;
+ end;
+
+ TLVHitTestInfo = packed record
+ pt: TPoint;
+ flags: DWORD;
+ iItem: Integer;
+ iSubItem: Integer;
+ end;
+
+const
+ LVFI_PARAM = $0001;
+ LVFI_STRING = $0002;
+ LVFI_PARTIAL = $0008;
+ LVFI_WRAP = $0020;
+ LVFI_NEARESTXY = $0040;
+
+const
+ HDM_FIRST = $1200; { Header messages }
+ HDM_GETITEMW = HDM_FIRST + 11;
+ HDM_GETITEMA = HDM_FIRST + 3;
+ HDM_GETITEM = {$IFDEF UNICODE_CTRLS} HDM_GETITEMW {$ELSE} HDM_GETITEMA {$ENDIF};
+
+ HDI_WIDTH = $0001;
+
+type
+ PHDItemA = ^THDItemA;
+ PHDItemW = ^THDItemW;
+ PHDItem = {$IFDEF UNICODE_CTRLS} PHDItemW {$ELSE} PHDItemA {$ENDIF};
+ _HD_ITEMA = packed record
+ Mask: Cardinal;
+ cxy: Integer;
+ pszText: PAnsiChar;
+ hbm: HBITMAP;
+ cchTextMax: Integer;
+ fmt: Integer;
+ lParam: LPARAM;
+ iImage: Integer; // index of bitmap in ImageList
+ iOrder: Integer; // where to draw this item
+ end;
+ _HD_ITEMW = packed record
+ Mask: Cardinal;
+ cxy: Integer;
+ pszText: PWideChar;
+ hbm: HBITMAP;
+ cchTextMax: Integer;
+ fmt: Integer;
+ lParam: LPARAM;
+ iImage: Integer; // index of bitmap in ImageList
+ iOrder: Integer; // where to draw this item
+ end;
+ THDItemA = _HD_ITEMA;
+ THDItemW = _HD_ITEMW;
+ THDItem = {$IFDEF UNICODE_CTRLS} _HD_ITEMW {$ELSE} _HD_ITEMA {$ENDIF};
+
+const
+ TVS_HASBUTTONS = $0001;
+ TVS_HASLINES = $0002;
+ TVS_LINESATROOT = $0004;
+ TVS_EDITLABELS = $0008;
+ TVS_DISABLEDRAGDROP = $0010;
+ TVS_SHOWSELALWAYS = $0020;
+ TVS_RTLREADING = $0040;
+ TVS_NOTOOLTIPS = $0080;
+ TVS_CHECKBOXES = $0100;
+ TVS_TRACKSELECT = $0200;
+ TVS_SINGLEEXPAND = $0400;
+ TVS_INFOTIP = $0800;
+ TVS_FULLROWSELECT = $1000;
+ TVS_NOSCROLL = $2000;
+ TVS_NONEVENHEIGHT = $4000;
+
+ TVIF_TEXT = $0001;
+ TVIF_IMAGE = $0002;
+ TVIF_PARAM = $0004;
+ TVIF_STATE = $0008;
+ TVIF_HANDLE = $0010;
+ TVIF_SELECTEDIMAGE = $0020;
+ TVIF_CHILDREN = $0040;
+ TVIF_INTEGRAL = $0080;
+ TVIF_DI_SETITEM = $1000;
+
+ TVIS_FOCUSED = $0001;
+ TVIS_SELECTED = $0002;
+ TVIS_CUT = $0004;
+ TVIS_DROPHILITED = $0008;
+ TVIS_BOLD = $0010;
+ TVIS_EXPANDED = $0020;
+ TVIS_EXPANDEDONCE = $0040;
+ TVIS_EXPANDPARTIAL = $0080;
+
+ TVIS_OVERLAYMASK = $0F00;
+ TVIS_STATEIMAGEMASK = $F000;
+ TVIS_USERMASK = $F000;
+
+ TV_FIRST = $1100; { TreeView messages }
+ TVM_INSERTITEMA = TV_FIRST + 0;
+ TVM_INSERTITEMW = TV_FIRST + 50;
+ TVM_INSERTITEM = {$IFDEF UNICODE_CTRLS} TVM_INSERTITEMW
+ {$ELSE} TVM_INSERTITEMA {$ENDIF};
+ TVM_DELETEITEM = TV_FIRST + 1;
+ TVM_EXPAND = TV_FIRST + 2;
+
+ TVE_COLLAPSE = $0001;
+ TVE_EXPAND = $0002;
+ TVE_TOGGLE = $0003;
+ TVE_EXPANDPARTIAL = $4000;
+ TVE_COLLAPSERESET = $8000;
+
+ TVM_GETITEMRECT = TV_FIRST + 4;
+ TVM_GETCOUNT = TV_FIRST + 5;
+ TVM_GETINDENT = TV_FIRST + 6;
+ TVM_SETINDENT = TV_FIRST + 7;
+ TVM_GETIMAGELIST = TV_FIRST + 8;
+
+ TVSIL_NORMAL = 0;
+ TVSIL_STATE = 2;
+
+ TVM_SETIMAGELIST = TV_FIRST + 9;
+ TVM_GETNEXTITEM = TV_FIRST + 10;
+
+ TVGN_ROOT = $0000;
+ TVGN_NEXT = $0001;
+ TVGN_PREVIOUS = $0002;
+ TVGN_PARENT = $0003;
+ TVGN_CHILD = $0004;
+ TVGN_FIRSTVISIBLE = $0005;
+ TVGN_NEXTVISIBLE = $0006;
+ TVGN_PREVIOUSVISIBLE = $0007;
+ TVGN_DROPHILITE = $0008;
+ TVGN_CARET = $0009;
+ TVGN_LASTVISIBLE = $000A;
+
+ TVM_SELECTITEM = TV_FIRST + 11;
+ TVM_GETITEMA = TV_FIRST + 12;
+ TVM_GETITEMW = TV_FIRST + 62;
+ TVM_GETITEM = {$IFDEF UNICODE_CTRLS} TVM_GETITEMW {$ELSE} TVM_GETITEMA {$ENDIF};
+ TVM_SETITEMA = TV_FIRST + 13;
+ TVM_SETITEMW = TV_FIRST + 63;
+ TVM_SETITEM = {$IFDEF UNICODE_CTRLS} TVM_SETITEMW {$ELSE} TVM_SETITEMA {$ENDIF};
+ TVM_EDITLABELA = TV_FIRST + 14;
+ TVM_EDITLABELW = TV_FIRST + 65;
+ TVM_EDITLABEL = {$IFDEF UNICODE_CTRLS} TVM_EDITLABELW {$ELSE} TVM_EDITLABELA {$ENDIF};
+ TVM_GETEDITCONTROL = TV_FIRST + 15;
+ TVM_GETVISIBLECOUNT = TV_FIRST + 16;
+ TVM_HITTEST = TV_FIRST + 17;
+
+ TVHT_NOWHERE = $0001;
+ TVHT_ONITEMICON = $0002;
+ TVHT_ONITEMLABEL = $0004;
+ TVHT_ONITEMINDENT = $0008;
+ TVHT_ONITEMBUTTON = $0010;
+ TVHT_ONITEMRIGHT = $0020;
+ TVHT_ONITEMSTATEICON = $0040;
+ TVHT_ONITEM = TVHT_ONITEMICON or TVHT_ONITEMLABEL or
+ TVHT_ONITEMSTATEICON;
+
+ TVHT_ABOVE = $0100;
+ TVHT_BELOW = $0200;
+ TVHT_TORIGHT = $0400;
+ TVHT_TOLEFT = $0800;
+
+ TVM_CREATEDRAGIMAGE = TV_FIRST + 18;
+ TVM_SORTCHILDREN = TV_FIRST + 19;
+ TVM_ENSUREVISIBLE = TV_FIRST + 20;
+ TVM_SORTCHILDRENCB = TV_FIRST + 21;
+ TVM_ENDEDITLABELNOW = TV_FIRST + 22;
+ TVM_GETISEARCHSTRINGA = TV_FIRST + 23;
+ TVM_GETISEARCHSTRINGW = TV_FIRST + 64;
+ TVM_GETISEARCHSTRING = {$IFDEF UNICODE_CTRLS} TVM_GETISEARCHSTRINGW {$ELSE} TVM_GETISEARCHSTRINGA {$ENDIF};
+ TVM_SETTOOLTIPS = TV_FIRST + 24;
+ TVM_GETTOOLTIPS = TV_FIRST + 25;
+ TVM_SETINSERTMARK = TV_FIRST + 26;
+ TVM_SETITEMHEIGHT = TV_FIRST + 27;
+ TVM_GETITEMHEIGHT = TV_FIRST + 28;
+ TVM_SETBKCOLOR = TV_FIRST + 29;
+ TVM_SETTEXTCOLOR = TV_FIRST + 30;
+ TVM_GETBKCOLOR = TV_FIRST + 31;
+ TVM_GETTEXTCOLOR = TV_FIRST + 32;
+ TVM_SETSCROLLTIME = TV_FIRST + 33;
+ TVM_GETSCROLLTIME = TV_FIRST + 34;
+ TVM_SETINSERTMARKCOLOR = TV_FIRST + 37;
+ TVM_GETINSERTMARKCOLOR = TV_FIRST + 38;
+ TVM_SETLINECOLOR = TV_FIRST + 40;
+
+ TVM_SETUNICODEFORMAT = CCM_SETUNICODEFORMAT;
+ TVM_GETUNICODEFORMAT = CCM_GETUNICODEFORMAT;
+
+ TVN_FIRST = 0-400; { treeview }
+ TVN_LAST = 0-499;
+ TVN_SELCHANGINGA = TVN_FIRST-1;
+ TVN_SELCHANGEDA = TVN_FIRST-2;
+ TVN_SELCHANGINGW = TVN_FIRST-50;
+ TVN_SELCHANGEDW = TVN_FIRST-51;
+ TVN_SELCHANGING = {$IFDEF UNICODE_CTRLS} TVN_SELCHANGINGW {$ELSE} TVN_SELCHANGINGA {$ENDIF};
+ TVN_SELCHANGED = {$IFDEF UNICODE_CTRLS} TVN_SELCHANGEDW {$ELSE} TVN_SELCHANGEDA {$ENDIF};
+
+ TVC_UNKNOWN = $0000;
+ TVC_BYMOUSE = $0001;
+ TVC_BYKEYBOARD = $0002;
+
+ TVN_GETDISPINFOA = TVN_FIRST-3;
+ TVN_SETDISPINFOA = TVN_FIRST-4;
+ TVN_GETDISPINFOW = TVN_FIRST-52;
+ TVN_SETDISPINFOW = TVN_FIRST-53;
+ TVN_GETDISPINFO = {$IFDEF UNICODE_CTRLS} TVN_GETDISPINFOW {$ELSE} TVN_GETDISPINFOA {$ENDIF};
+ TVN_SETDISPINFO = {$IFDEF UNICODE_CTRLS} TVN_SETDISPINFOW {$ELSE} TVN_SETDISPINFOA {$ENDIF};
+
+ TVN_ITEMEXPANDINGA = TVN_FIRST-5;
+ TVN_ITEMEXPANDEDA = TVN_FIRST-6;
+ TVN_BEGINDRAGA = TVN_FIRST-7;
+ TVN_BEGINRDRAGA = TVN_FIRST-8;
+ TVN_DELETEITEMA = TVN_FIRST-9;
+ TVN_BEGINLABELEDITA = TVN_FIRST-10;
+ TVN_ENDLABELEDITA = TVN_FIRST-11;
+ TVN_GETINFOTIPA = TVN_FIRST-13;
+ TVN_ITEMEXPANDINGW = TVN_FIRST-54;
+ TVN_ITEMEXPANDEDW = TVN_FIRST-55;
+ TVN_BEGINDRAGW = TVN_FIRST-56;
+ TVN_BEGINRDRAGW = TVN_FIRST-57;
+ TVN_DELETEITEMW = TVN_FIRST-58;
+ TVN_BEGINLABELEDITW = TVN_FIRST-59;
+ TVN_ENDLABELEDITW = TVN_FIRST-60;
+ TVN_GETINFOTIPW = TVN_FIRST-14;
+ TVN_ITEMEXPANDING = {$IFDEF UNICODE_CTRLS} TVN_ITEMEXPANDINGW {$ELSE} TVN_ITEMEXPANDINGA {$ENDIF};
+ TVN_ITEMEXPANDED = {$IFDEF UNICODE_CTRLS} TVN_ITEMEXPANDEDW {$ELSE} TVN_ITEMEXPANDEDA {$ENDIF};
+ TVN_BEGINDRAG = {$IFDEF UNICODE_CTRLS} TVN_BEGINDRAGW {$ELSE} TVN_BEGINDRAGA {$ENDIF};
+ TVN_BEGINRDRAG = {$IFDEF UNICODE_CTRLS} TVN_BEGINRDRAGW {$ELSE} TVN_BEGINRDRAGA {$ENDIF};
+ TVN_DELETEITEM = {$IFDEF UNICODE_CTRLS} TVN_DELETEITEMW {$ELSE} TVN_DELETEITEMA {$ENDIF};
+ TVN_BEGINLABELEDIT = {$IFDEF UNICODE_CTRLS} TVN_BEGINLABELEDITW {$ELSE} TVN_BEGINLABELEDITA {$ENDIF};
+ TVN_ENDLABELEDIT = {$IFDEF UNICODE_CTRLS} TVN_ENDLABELEDITW {$ELSE} TVN_ENDLABELEDITA {$ENDIF};
+ TVN_GETINFOTIP = {$IFDEF UNICODE_CTRLS} TVN_GETINFOTIPW {$ELSE} TVN_GETINFOTIPA {$ENDIF};
+ TVN_KEYDOWN = TVN_FIRST-12;
+ TVN_SINGLEEXPAND = TVN_FIRST-15;
+
+ TVI_ROOT = $FFFF0000;
+ TVI_FIRST = $FFFF0001;
+ TVI_LAST = $FFFF0002;
+ TVI_SORT = $FFFF0003;
+
+type
+ PTVItemA = ^TTVItemA;
+ PTVItemW = ^TTVItemW;
+ PTVItem = {$IFDEF UNICODE_CTRLS} PTVItemW {$ELSE} PTVItemA {$ENDIF};
+ tagTVITEMA = packed record
+ mask: UINT;
+ hItem: THandle;
+ state: UINT;
+ stateMask: UINT;
+ pszText: PAnsiChar;
+ cchTextMax: Integer;
+ iImage: Integer;
+ iSelectedImage: Integer;
+ cChildren: Integer;
+ lParam: LPARAM;
+ end;
+ tagTVITEMW = packed record
+ mask: UINT;
+ hItem: THandle;
+ state: UINT;
+ stateMask: UINT;
+ pszText: PWideChar;
+ cchTextMax: Integer;
+ iImage: Integer;
+ iSelectedImage: Integer;
+ cChildren: Integer;
+ lParam: LPARAM;
+ end;
+ tagTVITEM = {$IFDEF UNICODE_CTRLS} tagTVITEMW {$ELSE} tagTVITEMA {$ENDIF};
+ _TV_ITEMA = tagTVITEMA;
+ _TV_ITEMW = tagTVITEMW;
+ _TV_ITEM = {$IFDEF UNICODE_CTRLS} _TV_ITEMW {$ELSE} _TV_ITEMA {$ENDIF};
+ TTVItemA = tagTVITEMA;
+ TTVItemW = tagTVITEMW;
+ TTVItem = {$IFDEF UNICODE_CTRLS} TTVItemW {$ELSE} TTVItemA {$ENDIF};
+ TV_ITEMA = tagTVITEMA;
+ TV_ITEMW = tagTVITEMW;
+ TV_ITEM = {$IFDEF UNICODE_CTRLS} TV_ITEMW {$ELSE} TV_ITEMA {$ENDIF};
+
+ // only used for Get and Set messages. no notifies
+ tagTVITEMEXA = packed record
+ mask: UINT;
+ hItem: THandle;
+ state: UINT;
+ stateMask: UINT;
+ pszText: PAnsiChar;
+ cchTextMax: Integer;
+ iImage: Integer;
+ iSelectedImage: Integer;
+ cChildren: Integer;
+ lParam: LPARAM;
+ iIntegral: Integer;
+ end;
+ tagTVITEMEXW = packed record
+ mask: UINT;
+ hItem: THandle;
+ state: UINT;
+ stateMask: UINT;
+ pszText: PWideChar;
+ cchTextMax: Integer;
+ iImage: Integer;
+ iSelectedImage: Integer;
+ cChildren: Integer;
+ lParam: LPARAM;
+ iIntegral: Integer;
+ end;
+ tagTVITEMEX = {$IFDEF UNICODE_CTRLS} tagTVITEMEXW {$ELSE} tagTVITEMEXA {$ENDIF};
+ PTVItemExA = ^TTVItemExA;
+ PTVItemExW = ^TTVItemExW;
+ PTVItemEx = {$IFDEF UNICODE_CTRLS} PTVItemExW {$ELSE} PTVItemExA {$ENDIF};
+ TTVItemExA = tagTVITEMEXA;
+ TTVItemExW = tagTVITEMEXW;
+ TTVItemEx = {$IFDEF UNICODE_CTRLS} TTVItemExW {$ELSE} TTVItemExA {$ENDIF};
+
+ PNMTreeViewA = ^TNMTreeViewA;
+ PNMTreeViewW = ^TNMTreeViewW;
+ PNMTreeView = {$IFDEF UNICODE_CTRLS} PNMTreeViewW {$ELSE} PNMTreeViewA {$ENDIF};
+ tagNMTREEVIEWA = packed record
+ hdr: TNMHDR;
+ action: Integer;
+ itemOld: TTVItemA;
+ itemNew: TTVItemA;
+ ptDrag: TPoint;
+ end;
+ tagNMTREEVIEWW = packed record
+ hdr: TNMHDR;
+ action: Integer;
+ itemOld: TTVItemW;
+ itemNew: TTVItemW;
+ ptDrag: TPoint;
+ end;
+ tagNMTREEVIEW = {$IFDEF UNICODE_CTRLS} tagNMTREEVIEWW {$ELSE} tagNMTREEVIEWA {$ENDIF};
+ _NM_TREEVIEWA = tagNMTREEVIEWA;
+ _NM_TREEVIEWW = tagNMTREEVIEWW;
+ _NM_TREEVIEW = {$IFDEF UNICODE_CTRLS} _NM_TREEVIEWW {$ELSE} _NM_TREEVIEWA {$ENDIF};
+ TNMTreeViewA = tagNMTREEVIEWA;
+ TNMTreeViewW = tagNMTREEVIEWW;
+ TNMTreeView = {$IFDEF UNICODE_CTRLS} TNMTreeViewW {$ELSE} TNMTreeViewA {$ENDIF};
+ NM_TREEVIEWA = tagNMTREEVIEWA;
+ NM_TREEVIEWW = tagNMTREEVIEWW;
+ NM_TREEVIEW = {$IFDEF UNICODE_CTRLS} NM_TREEVIEWW {$ELSE} NM_TREEVIEWA {$ENDIF};
+
+ tagNMCUSTOMDRAWINFO = packed record
+ hdr: TNMHdr;
+ dwDrawStage: DWORD;
+ hdc: HDC;
+ rc: TRect;
+ dwItemSpec: DWORD; // this is control specific, but it's how to specify an item. valid only with CDDS_ITEM bit set
+ uItemState: UINT;
+ lItemlParam: LPARAM;
+ end;
+ PNMCustomDraw = ^TNMCustomDraw;
+ TNMCustomDraw = tagNMCUSTOMDRAWINFO;
+
+const
+ // custom draw return flags
+ // values under 0x00010000 are reserved for global custom draw values.
+ // above that are for specific controls
+ CDRF_DODEFAULT = $00000000;
+ CDRF_NEWFONT = $00000002;
+ CDRF_SKIPDEFAULT = $00000004;
+ CDRF_NOTIFYPOSTPAINT = $00000010;
+ CDRF_NOTIFYITEMDRAW = $00000020;
+ CDRF_NOTIFYSUBITEMDRAW = $00000020; // flags are the same, we can distinguish by context
+ CDRF_NOTIFYPOSTERASE = $00000040;
+
+ // drawstage flags
+ // values under = $00010000 are reserved for global custom draw values.
+ // above that are for specific controls
+ CDDS_PREPAINT = $00000001;
+ CDDS_POSTPAINT = $00000002;
+ CDDS_PREERASE = $00000003;
+ CDDS_POSTERASE = $00000004;
+ // the = $000010000 bit means it's individual item specific
+ CDDS_ITEM = $00010000;
+ CDDS_ITEMPREPAINT = CDDS_ITEM or CDDS_PREPAINT;
+ CDDS_ITEMPOSTPAINT = CDDS_ITEM or CDDS_POSTPAINT;
+ CDDS_ITEMPREERASE = CDDS_ITEM or CDDS_PREERASE;
+ CDDS_ITEMPOSTERASE = CDDS_ITEM or CDDS_POSTERASE;
+ CDDS_SUBITEM = $00020000;
+
+ // itemState flags
+ CDIS_SELECTED = $0001;
+ CDIS_GRAYED = $0002;
+ CDIS_DISABLED = $0004;
+ CDIS_CHECKED = $0008;
+ CDIS_FOCUS = $0010;
+ CDIS_DEFAULT = $0020;
+ CDIS_HOT = $0040;
+ CDIS_MARKED = $0080;
+ CDIS_INDETERMINATE = $0100;
+
+type
+ tagNMLVCUSTOMDRAW = packed record
+ nmcd: TNMCustomDraw;
+ clrText: COLORREF;
+ clrTextBk: COLORREF;
+ iSubItem: Integer; // IE 4.0 and higher
+ end;
+ PNMLVCustomDraw = ^TNMLVCustomDraw;
+ TNMLVCustomDraw = tagNMLVCUSTOMDRAW;
+
+
+
+
+ PTVDispInfoA = ^TTVDispInfoA;
+ PTVDispInfoW = ^TTVDispInfoW;
+ PTVDispInfo = {$IFDEF UNICODE_CTRLS} PTVDispInfoW {$ELSE} PTVDispInfoA {$ENDIF};
+ tagTVDISPINFOA = packed record
+ hdr: TNMHDR;
+ item: TTVItemA;
+ end;
+ tagTVDISPINFOW = packed record
+ hdr: TNMHDR;
+ item: TTVItemW;
+ end;
+ tagTVDISPINFO = {$IFDEF UNICODE_CTRLS} tagTVDISPINFOW {$ELSE} tagTVDISPINFOA {$ENDIF};
+ _TV_DISPINFOA = tagTVDISPINFOA;
+ _TV_DISPINFOW = tagTVDISPINFOW;
+ _TV_DISPINFO = {$IFDEF UNICODE_CTRLS} _TV_DISPINFOW {$ELSE} _TV_DISPINFOA {$ENDIF};
+ TTVDispInfoA = tagTVDISPINFOA;
+ TTVDispInfoW = tagTVDISPINFOW;
+ TTVDispInfo = {$IFDEF UNICODE_CTRLS} TTVDispInfoW {$ELSE} TTVDispInfoA {$ENDIF};
+ TV_DISPINFOA = tagTVDISPINFOA;
+ TV_DISPINFOW = tagTVDISPINFOW;
+ TV_DISPINFO = {$IFDEF UNICODE_CTRLS} TV_DISPINFOW {$ELSE} TV_DISPINFOA {$ENDIF};
+
+ tagNMMOUSE = packed record
+ hdr: TNMHdr;
+ dwItemSpec: DWORD;
+ dwItemData: DWORD;
+ pt: TPoint;
+ dwHitInfo: DWORD; // any specifics about where on the item or control the mouse is
+ end;
+ PNMMouse = ^TNMMouse;
+ TNMMouse = tagNMMOUSE;
+
+type
+ PTVHitTestInfo = ^TTVHitTestInfo;
+ TTVHitTestInfo = packed Record
+ pt: TPoint;
+ fl: DWORD;
+ hItem: THandle;
+ end;
+
+
+
+const
+
+ cctrl = 'comctl32.dll';
+
+ HINST_COMMCTRL = THandle(-1);
+
+ CCS_TOP = $00000001;
+ CCS_NOMOVEY = $00000002;
+ CCS_BOTTOM = $00000003;
+ CCS_NORESIZE = $00000004;
+ CCS_NOPARENTALIGN = $00000008;
+ CCS_ADJUSTABLE = $00000020;
+ CCS_NODIVIDER = $00000040;
+ CCS_VERT = $00000080;
+ CCS_LEFT = (CCS_VERT or CCS_TOP);
+ CCS_RIGHT = (CCS_VERT or CCS_BOTTOM);
+ CCS_NOMOVEX = (CCS_VERT or CCS_NOMOVEY);
+
+ PROGRESS_CLASS: array[ 0..17 ] of KOLChar = ('m','s','c','t','l','s','_',
+ 'p','r','o','g','r','e','s','s','3','2',#0);
+ STATUSCLASSNAME: array[ 0..18 ] of KOLChar = ('m','s','c','t','l','s','_',
+ 's','t','a','t','u','s','b','a','r','3','2',#0);
+ WC_LISTVIEW: array[0..13] of KOLChar = ('S','y','s','L','i','s','t',
+ 'V','i','e','w','3','2',#0);
+ TOOLBARCLASSNAME: array[0..15] of KOLChar = ('T','o','o','l','b','a','r',
+ 'W','i','n','d','o','w','3','2',#0 );
+ TOOLTIPS_CLASS: array[0..16] of KOLChar = ('t','o','o','l','t','i','p','s','_',
+ 'c','l','a','s','s','3','2',#0);
+ WC_TREEVIEW: array[0..13] of KOLChar = ('S','y','s','T','r','e','e',
+ 'V','i','e','w','3','2',#0);
+ WC_TABCONTROL: array[0..15] of KOLChar = ('S','y','s','T','a','b','C','o','n','t',
+ 'r','o','l','3','2',#0);
+ DATETIMEPICK_CLASS: array[ 0..17 ] of KOLChar = (
+ 'S','y','s','D','a','t','e','T','i','m','e','P','i','c','k','3','2',#0 );
+
+ TBN_FIRST = 0-700; { toolbar }
+ TBN_LAST = 0-720;
+
+ TBCDRF_NOEDGES = $00010000; // Don't draw button edges
+ TBCDRF_HILITEHOTTRACK = $00020000; // Use color of the button bk when hottracked
+ TBCDRF_NOOFFSET = $00040000; // Don't offset button if pressed
+ TBCDRF_NOMARK = $00080000; // Don't draw default highlight of image/text for TBSTATE_MARKED
+ TBCDRF_NOETCHEDEFFECT = $00100000; // Don't draw etched effect for disabled items
+
+ TB_ENABLEBUTTON = WM_USER + 1;
+ TB_CHECKBUTTON = WM_USER + 2;
+ TB_PRESSBUTTON = WM_USER + 3;
+ TB_HIDEBUTTON = WM_USER + 4;
+ TB_INDETERMINATE = WM_USER + 5;
+ TB_MARKBUTTON = WM_USER + 6;
+ TB_ISBUTTONENABLED = WM_USER + 9;
+ TB_ISBUTTONCHECKED = WM_USER + 10;
+ TB_ISBUTTONPRESSED = WM_USER + 11;
+ TB_ISBUTTONHIDDEN = WM_USER + 12;
+ TB_ISBUTTONINDETERMINATE = WM_USER + 13;
+ TB_ISBUTTONHIGHLIGHTED = WM_USER + 14;
+ TB_SETSTATE = WM_USER + 17;
+ TB_GETSTATE = WM_USER + 18;
+ TB_ADDBITMAP = WM_USER + 19;
+ TB_ADDBUTTONSA = WM_USER + 20;
+ TB_ADDBUTTONSW = WM_USER + 68;
+ TB_INSERTBUTTONA = WM_USER + 21;
+ TB_INSERTBUTTONW = WM_USER + 67;
+ TB_DELETEBUTTON = WM_USER + 22;
+ TB_GETBUTTON = WM_USER + 23;
+ TB_BUTTONCOUNT = WM_USER + 24;
+ TB_COMMANDTOINDEX = WM_USER + 25;
+
+ TB_SAVERESTOREA = WM_USER + 26;
+ TB_ADDSTRINGA = WM_USER + 28;
+ TB_GETBUTTONTEXTA = WM_USER + 45;
+ TBN_GETBUTTONINFOA = TBN_FIRST-0;
+
+ TB_GETBUTTONINFOW = WM_USER + 63;
+ TB_SETBUTTONINFOW = WM_USER + 64;
+ TB_GETBUTTONINFOA = WM_USER + 65;
+ TB_SETBUTTONINFOA = WM_USER + 66;
+ TB_GETBUTTONINFO = {$IFDEF UNICODE_CTRLS} TB_GETBUTTONINFOW {$ELSE} TB_GETBUTTONINFOA {$ENDIF};
+ TB_SETBUTTONINFO = {$IFDEF UNICODE_CTRLS} TB_SETBUTTONINFOW {$ELSE} TB_SETBUTTONINFOA {$ENDIF};
+
+ TB_SAVERESTOREW = WM_USER + 76;
+ TB_ADDSTRINGW = WM_USER + 77;
+ TB_GETBUTTONTEXTW = WM_USER + 75;
+ TBN_GETBUTTONINFOW = TBN_FIRST-20;
+ TB_SAVERESTORE = {$IFDEF UNICODE_CTRLS} TB_SAVERESTOREW {$ELSE} TB_SAVERESTOREA {$ENDIF};
+ TB_ADDSTRING = {$IFDEF UNICODE_CTRLS} TB_ADDSTRINGW {$ELSE} TB_ADDSTRINGA {$ENDIF};
+ TB_GETBUTTONTEXT = {$IFDEF UNICODE_CTRLS} TB_GETBUTTONTEXTW {$ELSE} TB_GETBUTTONTEXTA {$ENDIF};
+ TBN_GETBUTTONINFO = {$IFDEF UNICODE_CTRLS} TBN_GETBUTTONINFOW {$ELSE} TBN_GETBUTTONINFOA {$ENDIF};
+
+ TBN_DROPDOWN = TBN_FIRST-10;
+ TBN_CLOSEUP = TBN_FIRST-11;
+
+ TB_CUSTOMIZE = WM_USER + 27;
+ TB_GETITEMRECT = WM_USER + 29;
+ TB_BUTTONSTRUCTSIZE = WM_USER + 30;
+ TB_SETBUTTONSIZE = WM_USER + 31;
+ TB_SETBITMAPSIZE = WM_USER + 32;
+ TB_AUTOSIZE = WM_USER + 33;
+ TB_GETTOOLTIPS = WM_USER + 35;
+ TB_SETTOOLTIPS = WM_USER + 36;
+ TB_SETPARENT = WM_USER + 37;
+ TB_SETROWS = WM_USER + 39;
+ TB_GETROWS = WM_USER + 40;
+ TB_SETCMDID = WM_USER + 42;
+ TB_CHANGEBITMAP = WM_USER + 43;
+ TB_GETBITMAP = WM_USER + 44;
+ TB_REPLACEBITMAP = WM_USER + 46;
+ TB_SETINDENT = WM_USER + 47;
+ TB_SETIMAGELIST = WM_USER + 48;
+ TB_GETIMAGELIST = WM_USER + 49;
+ TB_LOADIMAGES = WM_USER + 50;
+ TB_GETRECT = WM_USER + 51; { wParam is the Cmd instead of index }
+ TB_SETHOTIMAGELIST = WM_USER + 52;
+ TB_GETHOTIMAGELIST = WM_USER + 53;
+ TB_SETDISABLEDIMAGELIST = WM_USER + 54;
+ TB_GETDISABLEDIMAGELIST = WM_USER + 55;
+ TB_SETSTYLE = WM_USER + 56;
+ TB_GETSTYLE = WM_USER + 57;
+ TB_GETBUTTONSIZE = WM_USER + 58;
+ TB_SETBUTTONWIDTH = WM_USER + 59;
+ TB_SETMAXTEXTROWS = WM_USER + 60;
+ TB_GETTEXTROWS = WM_USER + 61;
+
+ TB_GETOBJECT = WM_USER + 62; // wParam == IID, lParam void **ppv
+ TB_GETHOTITEM = WM_USER + 71;
+ TB_SETHOTITEM = WM_USER + 72; // wParam == iHotItem
+ TB_SETANCHORHIGHLIGHT = WM_USER + 73; // wParam == TRUE/FALSE
+ TB_GETANCHORHIGHLIGHT = WM_USER + 74;
+ TB_MAPACCELERATORA = WM_USER + 78; // wParam == ch, lParam int * pidBtn
+
+ TB_SETEXTENDEDSTYLE = WM_USER + 84; // For TBSTYLE_EX_*
+ TB_GETEXTENDEDSTYLE = WM_USER + 85; // For TBSTYLE_EX_*
+ TB_INSERTBUTTON = {$IFDEF UNICODE_CTRLS} TB_INSERTBUTTONW {$ELSE} TB_INSERTBUTTONA {$ENDIF};
+ TB_ADDBUTTONS = {$IFDEF UNICODE_CTRLS} TB_ADDBUTTONSW {$ELSE} TB_ADDBUTTONSA {$ENDIF};
+
+ IDB_STD_SMALL_COLOR = 0;
+ IDB_STD_LARGE_COLOR = 1;
+ IDB_VIEW_SMALL_COLOR = 4;
+ IDB_VIEW_LARGE_COLOR = 5;
+ IDB_HIST_SMALL_COLOR = 8;
+ IDB_HIST_LARGE_COLOR = 9;
+
+ STD_CUT = 0;
+ STD_COPY = 1;
+ STD_PASTE = 2;
+ STD_UNDO = 3;
+ STD_REDO = 4;
+ STD_DELETE = 5;
+ STD_FILENEW = 6;
+ STD_FILEOPEN = 7;
+ STD_FILESAVE = 8;
+ STD_PRINTPRE = 9;
+ STD_PROPERTIES = 10;
+ STD_HELP = 11;
+ STD_FIND = 12;
+ STD_REPLACE = 13;
+ STD_PRINT = 14;
+
+{ icon indexes for standard view bitmap }
+
+ VIEW_LARGEICONS = 0;
+ VIEW_SMALLICONS = 1;
+ VIEW_LIST = 2;
+ VIEW_DETAILS = 3;
+ VIEW_SORTNAME = 4;
+ VIEW_SORTSIZE = 5;
+ VIEW_SORTDATE = 6;
+ VIEW_SORTTYPE = 7;
+ VIEW_PARENTFOLDER = 8;
+ VIEW_NETCONNECT = 9;
+ VIEW_NETDISCONNECT = 10;
+ VIEW_NEWFOLDER = 11;
+ VIEW_VIEWMENU = 12;
+
+{ icon indexes for standard history bitmap }
+
+ HIST_BACK = 0;
+ HIST_FORWARD = 1;
+ HIST_FAVORITES = 2;
+ HIST_ADDTOFAVORITES = 3;
+ HIST_VIEWTREE = 4;
+
+ TBSTATE_CHECKED = $01;
+ TBSTATE_PRESSED = $02;
+ TBSTATE_ENABLED = $04;
+ TBSTATE_HIDDEN = $08;
+ TBSTATE_INDETERMINATE = $10;
+ TBSTATE_WRAP = $20;
+ TBSTATE_ELLIPSES = $40;
+ TBSTATE_MARKED = $80;
+
+ TBSTYLE_BUTTON = $00;
+ TBSTYLE_SEP = $01;
+ TBSTYLE_CHECK = $02;
+ TBSTYLE_GROUP = $04;
+ TBSTYLE_CHECKGROUP = TBSTYLE_GROUP or TBSTYLE_CHECK;
+ TBSTYLE_DROPDOWN = $08;
+ TBSTYLE_AUTOSIZE = $0010; // automatically calculate the cx of the button
+ TBSTYLE_NOPREFIX = $0020; // if this button should not have accel prefix
+
+ TBSTYLE_TOOLTIPS = $0100;
+ TBSTYLE_WRAPABLE = $0200;
+ TBSTYLE_ALTDRAG = $0400;
+ TBSTYLE_FLAT = $0800;
+ TBSTYLE_LIST = $1000;
+ TBSTYLE_CUSTOMERASE = $2000;
+ TBSTYLE_REGISTERDROP = $4000;
+ TBSTYLE_TRANSPARENT = $8000;
+ TBSTYLE_EX_DRAWDDARROWS = $00000001;
+
+ TBIF_IMAGE = $00000001;
+ TBIF_TEXT = $00000002;
+ TBIF_STATE = $00000004;
+ TBIF_STYLE = $00000008;
+ TBIF_LPARAM = $00000010;
+ TBIF_COMMAND = $00000020;
+ TBIF_SIZE = $00000040;
+ TBIF_BYINDEX = $80000000;
+
+ TTN_FIRST = 0-520; { tooltips }
+ TTN_LAST = 0-549;
+ TTN_NEEDTEXTA = TTN_FIRST - 0;
+ TTN_NEEDTEXT = TTN_FIRST - 0;
+ TTN_NEEDTEXTW = TTN_FIRST - 10;
+
+ TTS_ALWAYSTIP = $01;
+ TTS_NOPREFIX = $02;
+
+ TTM_ACTIVATE = WM_USER + 1;
+ TTM_SETDELAYTIME = WM_USER + 3;
+
+ TTM_ADDTOOLA = WM_USER + 4;
+ TTM_DELTOOLA = WM_USER + 5;
+ TTM_NEWTOOLRECTA = WM_USER + 6;
+ TTM_GETTOOLINFOA = WM_USER + 8;
+ TTM_SETTOOLINFOA = WM_USER + 9;
+ TTM_HITTESTA = WM_USER + 10;
+ TTM_GETTEXTA = WM_USER + 11;
+ TTM_UPDATETIPTEXTA = WM_USER + 12;
+ TTM_ENUMTOOLSA = WM_USER + 14;
+ TTM_GETCURRENTTOOLA = WM_USER + 15;
+
+ TTM_ADDTOOLW = WM_USER + 50;
+ TTM_DELTOOLW = WM_USER + 51;
+ TTM_NEWTOOLRECTW = WM_USER + 52;
+ TTM_GETTOOLINFOW = WM_USER + 53;
+ TTM_SETTOOLINFOW = WM_USER + 54;
+ TTM_HITTESTW = WM_USER + 55;
+ TTM_GETTEXTW = WM_USER + 56;
+ TTM_UPDATETIPTEXTW = WM_USER + 57;
+ TTM_ENUMTOOLSW = WM_USER + 58;
+ TTM_GETCURRENTTOOLW = WM_USER + 59;
+ TTM_WINDOWFROMPOINT = WM_USER + 16;
+ TTM_TRACKACTIVATE = WM_USER + 17; // wParam = TRUE/FALSE start end lparam = LPTOOLINFO
+ TTM_TRACKPOSITION = WM_USER + 18; // lParam = dwPos
+ TTM_SETTIPBKCOLOR = WM_USER + 19;
+ TTM_SETTIPTEXTCOLOR = WM_USER + 20;
+ TTM_GETDELAYTIME = WM_USER + 21;
+ TTM_GETTIPBKCOLOR = WM_USER + 22;
+ TTM_GETTIPTEXTCOLOR = WM_USER + 23;
+ TTM_SETMAXTIPWIDTH = WM_USER + 24;
+ TTM_GETMAXTIPWIDTH = WM_USER + 25;
+ TTM_SETMARGIN = WM_USER + 26; // lParam = lprc
+ TTM_GETMARGIN = WM_USER + 27; // lParam = lprc
+ TTM_POP = WM_USER + 28;
+ TTM_POPUP = WM_USER + 34;
+ TTM_UPDATE = WM_USER + 29;
+
+ TTM_ADDTOOL = {$IFDEF UNICODE_CTRLS} TTM_ADDTOOLW {$ELSE} TTM_ADDTOOLA {$ENDIF};
+ TTM_DELTOOL = {$IFDEF UNICODE_CTRLS} TTM_DELTOOLW {$ELSE} TTM_DELTOOLA {$ENDIF};
+ TTM_NEWTOOLRECT = {$IFDEF UNICODE_CTRLS} TTM_NEWTOOLRECTW {$ELSE} TTM_NEWTOOLRECTA {$ENDIF};
+ TTM_GETTOOLINFO = {$IFDEF UNICODE_CTRLS} TTM_GETTOOLINFOW {$ELSE} TTM_GETTOOLINFOA {$ENDIF};
+ TTM_SETTOOLINFO = {$IFDEF UNICODE_CTRLS} TTM_SETTOOLINFOW {$ELSE} TTM_SETTOOLINFOA {$ENDIF};
+ TTM_HITTEST = {$IFDEF UNICODE_CTRLS} TTM_HITTESTW {$ELSE} TTM_HITTESTA {$ENDIF};
+ TTM_GETTEXT = {$IFDEF UNICODE_CTRLS} TTM_GETTEXTW {$ELSE} TTM_GETTEXTA {$ENDIF};
+ TTM_UPDATETIPTEXT = {$IFDEF UNICODE_CTRLS} TTM_UPDATETIPTEXTW {$ELSE} TTM_UPDATETIPTEXTA {$ENDIF};
+ TTM_ENUMTOOLS = {$IFDEF UNICODE_CTRLS} TTM_ENUMTOOLSW {$ELSE} TTM_ENUMTOOLSA {$ENDIF};
+ TTM_GETCURRENTTOOL = {$IFDEF UNICODE_CTRLS} TTM_GETCURRENTTOOLW {$ELSE} TTM_GETCURRENTTOOLA {$ENDIF};
+
+ TTM_RELAYEVENT = WM_USER + 7;
+ TTM_GETTOOLCOUNT = WM_USER +13;
+
+ TTF_IDISHWND = $0001;
+ TTF_CENTERTIP = $0002;
+ TTF_RTLREADING = $0004;
+ TTF_SUBCLASS = $0010;
+ TTF_TRACK = $0020;
+ TTF_ABSOLUTE = $0080;
+ TTF_TRANSPARENT = $0100;
+ TTF_DI_SETITEM = $8000; // valid only on the TTN_NEEDTEXT callback
+
+ LPSTR_TEXTCALLBACKA = LPSTR(-1);
+ LPSTR_TEXTCALLBACKW = LPWSTR(-1);
+ LPSTR_TEXTCALLBACK = {$IFDEF UNICODE_CTRLS} LPSTR_TEXTCALLBACKW {$ELSE} LPSTR_TEXTCALLBACKA {$ENDIF};
+
+ CW_USEDEFAULT = Integer($80000000);
+
+type
+ PTBAddBitmap = ^TTBAddBitmap;
+ TTBAddBitmap = packed record
+ hInst: THandle;
+ nID: UINT;
+ end;
+
+ PTBButton = ^TTBButton;
+ TTBButton = packed record
+ iBitmap: Integer;
+ idCommand: Integer;
+ fsState: Byte;
+ fsStyle: Byte;
+ bReserved: array[1..2] of Byte;
+ dwData: Longint;
+ iString: Integer;
+ end;
+
+ PTBButtonInfo = ^TTBButtonInfo;
+ TTBButtonInfo = packed record
+ cbSize: UINT;
+ dwMask: DWORD;
+ idCommand: Integer;
+ iImage: Integer;
+ fsState: Byte;
+ fsStyle: Byte;
+ cx: Word;
+ lParam: DWORD;
+ pszText: PKOLChar;
+ cchText: Integer;
+ end;
+
+ PColorMap = ^TColorMap;
+ TColorMap = packed record
+ cFrom: TColorRef;
+ cTo: TColorRef;
+ end;
+
+ PTBNotify = ^TTBnotify;
+ TTBNotify = packed record
+ hdr: TNMHdr;
+ iItem: Integer;
+ tbButton: TTBButton;
+ cchText: Integer;
+ pszText: PChar;
+ end;
+
+ PNMTBCustomDraw = ^TNMTBCustomDraw;
+ TNMTBCustomDraw = packed record
+ nmcd: TNMCUSTOMDRAW;
+ hbrMonoDither: HBrush;
+ hbrLines : HBrush;
+ hpenLines : HPen;
+ clrText : COLORREF;
+ clrMark : COLORREF;
+ clrTextHighlight: COLORREF;
+ clrBtnFace : COLORREF;
+ clrBtnHighlight : COLORREF;
+ clrHighlightHotTrack: COLORREF;
+ rcText : TRect;
+ nStringBkMode : Integer;
+ nHLStringBkMode : Integer;
+ iListGap : Integer;
+ end;
+
+ PTooltipText = ^TTooltipText;
+ TTooltipText = packed record
+ hdr: TNMHdr;
+ lpszText: PKOLChar;
+ szText: array[0..79] of KOLChar;
+ hinst: HINST;
+ uFlags: UINT;
+ lParam: LPARAM;
+ end;
+
+ PToolInfo = ^TToolInfo;
+ TToolInfo = packed record
+ cbSize: UINT;
+ uFlags: UINT;
+ hwnd: HWND;
+ uId: UINT;
+ Rect: TRect;
+ hInst: THandle;
+ lpszText: PKOLChar;
+ lParam: LPARAM;
+ end;
+
+const
+ WM_MOUSEHOVER = $02A1;
+ WM_MOUSELEAVE = $02A3;
+
+ TME_HOVER = $00000001;
+ TME_LEAVE = $00000002;
+ TME_QUERY = $40000000;
+ TME_CANCEL = $80000000;
+
+ HOVER_DEFAULT = $FFFFFFFF;
+
+ ODT_HEADER = 100;
+ ODT_TAB = 101;
+ ODT_LISTVIEW = 102;
+
+type
+ tagTRACKMOUSEEVENT = packed record
+ cbSize: DWORD;
+ dwFlags: DWORD;
+ hwndTrack: HWND;
+ dwHoverTime: DWORD;
+ end;
+ PTrackMouseEvent = ^TTrackMouseEvent;
+ TTrackMouseEvent = tagTRACKMOUSEEVENT;
+
+//////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////
+// Some stuff from new Delphi versions (not available in old ones):
+ {$IFNDEF UNICODE_CTRLS}
+const
+ //IDC_HAND = MakeIntResource(32649);
+ IDC_HAND = PChar(32649);
+ {$ENDIF}
+
+/////////////////////////////////////////////////////////
+const
+ VK_PAGE_DOWN = VK_NEXT;
+ VK_PAGE_UP = VK_PRIOR;
+ VK_ALT = VK_MENU;
+
+PBT_APMQUERYSUSPEND = 00 ;
+PBT_APMQUERYSTANDBY = 01 ;
+PBT_APMQUERYSUSPENDFAILED = 02 ;
+PBT_APMQUERYSTANDBYFAILED = 03 ;
+PBT_APMSUSPEND = 04 ;
+PBT_APMSTANDBY = 05 ;
+PBT_APMRESUMECRITICAL = 06 ;
+PBT_APMRESUMESUSPEND = 07 ;
+PBT_APMRESUMESTANDBY = 08 ;
+PBTF_APMRESUMEFROMFAILURE = 000001 ;
+PBT_APMBATTERYLOW = 09 ;
+PBT_APMPOWERSTATUSCHANGE = 10 ;
+PBT_APMOEMEVENT = 11 ;
+PBT_APMRESUMEAUTOMATIC = $12 ; // hexadecimal $12 = 18 !
+
+{ DATETIMEPICKER}
+
+const
+ // messages
+ DTM_FIRST = $1000;
+ DTM_GETSYSTEMTIME = DTM_FIRST + 1;
+ DTM_SETSYSTEMTIME = DTM_FIRST + 2;
+ DTM_GETRANGE = DTM_FIRST + 3;
+ DTM_SETRANGE = DTM_FIRST + 4;
+ DTM_SETFORMATA = DTM_FIRST + 5;
+ DTM_SETMCCOLOR = DTM_FIRST + 6;
+ DTM_GETMCCOLOR = DTM_FIRST + 7;
+ DTM_GETMONTHCAL = DTM_FIRST + 8;
+ DTM_SETMCFONT = DTM_FIRST + 9;
+ DTM_GETMCFONT = DTM_FIRST + 10;
+ DTM_SETFORMATW = DTM_FIRST + 50;
+ DTM_SETFORMAT = {$IFDEF UNICODE_CTRLS} DTM_SETFORMATW {$ELSE} DTM_SETFORMATA {$ENDIF};
+
+ // Ranges
+ GDTR_MIN = $0001;
+ GDTR_MAX = $0002;
+
+ // Return Values
+ GDT_ERROR = -1;
+ GDT_VALID = 0;
+ GDT_NONE = 1;
+
+ // notifications
+ DTN_FIRST = 0-760; { datetimepick }
+ DTN_LAST = 0-799;
+
+ DTN_DATETIMECHANGE = DTN_FIRST + 1; // the systemtime has changed
+ DTN_USERSTRINGA = DTN_FIRST + 2; // the user has entered a string
+ DTN_USERSTRINGW = DTN_FIRST + 15;
+ DTN_WMKEYDOWNA = DTN_FIRST + 3; // modify keydown on app format field (X)
+ DTN_WMKEYDOWNW = DTN_FIRST + 16;
+ DTN_FORMATA = DTN_FIRST + 4; // query display for app format field (X)
+ DTN_FORMATW = DTN_FIRST + 17;
+ DTN_FORMATQUERYA = DTN_FIRST + 5; // query formatting info for app format field (X)
+ DTN_FORMATQUERYW = DTN_FIRST + 18;
+ DTN_DROPDOWN = DTN_FIRST + 6; // MonthCal has dropped down
+ DTN_CLOSEUP = DTN_FIRST + 7; // MonthCal is popping up
+ DTN_USERSTRING = {$IFDEF UNICODE_CTRLS} DTN_USERSTRINGW {$ELSE} DTN_USERSTRINGA {$ENDIF};
+ DTN_WMKEYDOWN = {$IFDEF UNICODE_CTRLS} DTN_WMKEYDOWNW {$ELSE} DTN_WMKEYDOWNA {$ENDIF};
+ DTN_FORMAT = {$IFDEF UNICODE_CTRLS} DTN_FORMATW {$ELSE} DTN_FORMATA {$ENDIF};
+ DTN_FORMATQUERY = {$IFDEF UNICODE_CTRLS} DTN_FORMATQUERYW {$ELSE} DTN_FORMATQUERYA {$ENDIF};
+
+ // styles
+ DTS_UPDOWN = $0001; // use UPDOWN instead of MONTHCAL
+ DTS_SHOWNONE = $0002; // allow a NONE selection
+ DTS_SHORTDATEFORMAT = $0000; // use the short date format
+ // (app must forward WM_WININICHANGE messages)
+ DTS_LONGDATEFORMAT = $0004; // use the long date format
+ // (app must forward WM_WININICHANGE messages)
+ DTS_TIMEFORMAT = $0008; // use the time format
+ // (app must forward WM_WININICHANGE messages)
+ DTS_APPCANPARSE = $0010; // allow user entered strings
+ // (app MUST respond to DTN_USERSTRING)
+ DTS_RIGHTALIGN = $0020; // right-align popup instead of left-align it
+
+ // color index constants
+ MCSC_BACKGROUND = 0; // the background color (between months)
+ MCSC_TEXT = 1; // the dates
+ MCSC_TITLEBK = 2; // background of the title
+ MCSC_TITLETEXT = 3;
+ MCSC_MONTHBK = 4; // background within the month cal
+ MCSC_TRAILINGTEXT = 5; // the text color of header & trailing days
+
+ // structures
+type
+ tagNMDATETIMESTRINGA = packed record
+ nmhdr: TNmHdr;
+ pszUserString: PAnsiChar; // string user entered
+ st: TSystemTime; // app fills this in
+ dwFlags: DWORD; // GDT_VALID or GDT_NONE
+ end;
+ tagNMDATETIMESTRINGW = packed record
+ nmhdr: TNmHdr;
+ pszUserString: PWideChar; // string user entered
+ st: TSystemTime; // app fills this in
+ dwFlags: DWORD; // GDT_VALID or GDT_NONE
+ end;
+ tagNMDATETIMESTRING = {$IFDEF UNICODE_CTRLS} tagNMDATETIMESTRINGW {$ELSE} tagNMDATETIMESTRINGA {$ENDIF};
+ PNMDateTimeStringA = ^TNMDateTimeStringA;
+ PNMDateTimeStringW = ^TNMDateTimeStringW;
+ PNMDateTimeString = {$IFDEF UNICODE_CTRLS} PNMDateTimeStringW {$ELSE} PNMDateTimeStringA {$ENDIF};
+ TNMDateTimeStringA = tagNMDATETIMESTRINGA;
+ TNMDateTimeStringW = tagNMDATETIMESTRINGW;
+ TNMDateTimeString = {$IFDEF UNICODE_CTRLS} TNMDateTimeStringW {$ELSE} TNMDateTimeStringA {$ENDIF};
+
+const
+ HDN_FIRST = 0-300; { header }
+ HDN_LAST = 0-399;
+ HDM_HITTEST = HDM_FIRST + 6;
+ HDM_GETITEMRECT = HDM_FIRST + 7;
+ HDM_SETIMAGELIST = HDM_FIRST + 8;
+ HDM_GETIMAGELIST = HDM_FIRST + 9;
+ HDM_ORDERTOINDEX = HDM_FIRST + 15;
+ HDM_CREATEDRAGIMAGE = HDM_FIRST + 16; // wparam = which item = by index;
+ HDM_GETORDERARRAY = HDM_FIRST + 17;
+ HDM_SETORDERARRAY = HDM_FIRST + 18;
+ HDM_SETHOTDIVIDER = HDM_FIRST + 19;
+ HDM_SETUNICODEFORMAT = CCM_SETUNICODEFORMAT;
+ HDM_GETUNICODEFORMAT = CCM_GETUNICODEFORMAT;
+ HDN_ITEMCHANGINGA = HDN_FIRST-0;
+ HDN_ITEMCHANGEDA = HDN_FIRST-1;
+ HDN_ITEMCLICKA = HDN_FIRST-2;
+ HDN_ITEMDBLCLICKA = HDN_FIRST-3;
+ HDN_DIVIDERDBLCLICKA = HDN_FIRST-5;
+ HDN_BEGINTRACKA = HDN_FIRST-6;
+ HDN_ENDTRACKA = HDN_FIRST-7;
+ HDN_TRACKA = HDN_FIRST-8;
+ HDN_GETDISPINFOA = HDN_FIRST-9;
+ HDN_BEGINDRAG = HDN_FIRST-10;
+ HDN_ENDDRAG = HDN_FIRST-11;
+
+ HDN_ITEMCHANGINGW = HDN_FIRST-20;
+ HDN_ITEMCHANGEDW = HDN_FIRST-21;
+ HDN_ITEMCLICKW = HDN_FIRST-22;
+ HDN_ITEMDBLCLICKW = HDN_FIRST-23;
+ HDN_DIVIDERDBLCLICKW = HDN_FIRST-25;
+ HDN_BEGINTRACKW = HDN_FIRST-26;
+ HDN_ENDTRACKW = HDN_FIRST-27;
+ HDN_TRACKW = HDN_FIRST-28;
+ HDN_GETDISPINFOW = HDN_FIRST-29;
+
+type
+ tagNMHEADERA = packed record
+ Hdr: TNMHdr;
+ Item: Integer;
+ Button: Integer;
+ PItem: PHDItemA;
+ end;
+ tagNMHEADERW = packed record
+ Hdr: TNMHdr;
+ Item: Integer;
+ Button: Integer;
+ PItem: PHDItemW;
+ end;
+ tagNMHEADER = tagNMHEADERA;
+ PHDNotifyA = ^THDNotifyA;
+ PHDNotifyW = ^THDNotifyW;
+ THDNotifyA = tagNMHEADERA;
+ THDNotifyW = tagNMHEADERW;
+
+{******************************************************************************}
diff --git a/plugins/Libs/dynbasswma.pas b/plugins/Libs/dynbasswma.pas
new file mode 100644
index 0000000000..60a85f588f
--- /dev/null
+++ b/plugins/Libs/dynbasswma.pas
@@ -0,0 +1,249 @@
+{
+ BASSWMA 2.4 Delphi unit
+ Copyright (c) 2002-2008 Un4seen Developments Ltd.
+
+ See the BASSWMA.CHM file for more detailed documentation
+}
+
+unit DynBassWMA;
+
+interface
+
+uses Windows, Dynamic_Bass;
+
+const
+ // Additional error codes returned by BASS_ErrorGetCode
+ BASS_ERROR_WMA_LICENSE = 1000; // the file is protected
+ BASS_ERROR_WMA = 1001; // Windows Media (9 or above) is not installed
+ BASS_ERROR_WMA_WM9 = BASS_ERROR_WMA;
+ BASS_ERROR_WMA_DENIED = 1002; // access denied (user/pass is invalid)
+ BASS_ERROR_WMA_INDIVIDUAL = 1004; // individualization is needed
+
+ // Additional BASS_SetConfig options
+ BASS_CONFIG_WMA_PRECHECK = $10100;
+ BASS_CONFIG_WMA_PREBUF = $10101;
+// BASS_CONFIG_WMA_ASX = $10102;
+ BASS_CONFIG_WMA_BASSFILE = $10103;
+ BASS_CONFIG_WMA_NETSEEK = $10104;
+ BASS_CONFIG_WMA_VIDEO = $10105;
+
+ // additional WMA sync types
+ BASS_SYNC_WMA_CHANGE = $10100;
+ BASS_SYNC_WMA_META = $10101;
+
+ // additional BASS_StreamGetFilePosition WMA mode
+ BASS_FILEPOS_WMA_BUFFER = 1000; // internet buffering progress (0-100%)
+
+ // Additional flags for use with BASS_WMA_EncodeOpen/File/Network/Publish
+ BASS_WMA_ENCODE_STANDARD = $2000; // standard WMA
+ BASS_WMA_ENCODE_PRO = $4000; // WMA Pro
+ BASS_WMA_ENCODE_24BIT = $8000; // 24-bit
+ BASS_WMA_ENCODE_PCM = $10000; // uncompressed PCM
+ BASS_WMA_ENCODE_SCRIPT = $20000; // set script (mid-stream tags) in the WMA encoding
+
+ // Additional flag for use with BASS_WMA_EncodeGetRates
+ BASS_WMA_ENCODE_RATES_VBR = $10000; // get available VBR quality settings
+
+ // WMENCODEPROC "type" values
+ BASS_WMA_ENCODE_HEAD = 0;
+ BASS_WMA_ENCODE_DATA = 1;
+ BASS_WMA_ENCODE_DONE = 2;
+
+ // BASS_WMA_EncodeSetTag "form" values
+ BASS_WMA_TAG_ANSI = 0;
+ BASS_WMA_TAG_UNICODE = 1;
+ BASS_WMA_TAG_UTF8 = 2;
+ BASS_WMA_TAG_BINARY = $100; // FLAG: binary tag (HIWORD=length)
+
+ // BASS_CHANNELINFO type
+ BASS_CTYPE_STREAM_WMA = $10300;
+ BASS_CTYPE_STREAM_WMA_MP3 = $10301;
+
+ // Additional BASS_ChannelGetTags type
+ BASS_TAG_WMA = 8; // WMA header tags : series of null-terminated UTF-8 strings
+ BASS_TAG_WMA_META = 11; // WMA mid-stream tag : UTF-8 string
+ BASS_TAG_WMA_CODEC = 12; // WMA codec
+
+
+type
+ HWMENCODE = DWORD; // WMA encoding handle
+
+ CLIENTCONNECTPROC = procedure(handle:HWMENCODE; connect:BOOL; ip:PChar; user:Pointer); stdcall;
+ {
+ Client connection notification callback function.
+ handle : The encoder
+ connect: TRUE=client is connecting, FALSE=disconnecting
+ ip : The client's IP (xxx.xxx.xxx.xxx:port)
+ user : The 'user' parameter value given when calling BASS_WMA_EncodeSetNotify
+ }
+
+ WMENCODEPROC = procedure(handle:HWMENCODE; dtype:DWORD; buffer:Pointer; length:DWORD; user:Pointer); stdcall;
+ {
+ Encoder callback function.
+ handle : The encoder handle
+ dtype : The type of data, one of BASS_WMA_ENCODE_xxx values
+ buffer : The encoded data
+ length : Length of the data
+ user : The 'user' parameter value given when calling BASS_WMA_EncodeOpen
+ }
+
+
+const
+ basswmadll = 'basswma.dll';
+
+var BASS_WMA_StreamCreateFile :function(mem:BOOL; fl:pointer; offset,length:QWORD; flags:DWORD): HSTREAM; stdcall;
+var BASS_WMA_StreamCreateFileAuth :function(mem:BOOL; fl:pointer; offset,length:QWORD; flags:DWORD; user,pass:PChar): HSTREAM; stdcall;
+var BASS_WMA_StreamCreateFileUser :function(system,flags:DWORD; var procs:BASS_FILEPROCS; user:Pointer): HSTREAM; stdcall;
+
+var BASS_WMA_GetTags :function(fname:PChar; flags:DWORD): PAnsiChar; stdcall;
+
+var BASS_WMA_EncodeGetRates :function(freq,chans,flags:DWORD): PDWORD; stdcall;
+var BASS_WMA_EncodeOpen :function(freq,chans,flags,bitrate:DWORD; proc:WMENCODEPROC; user:Pointer): HWMENCODE; stdcall;
+var BASS_WMA_EncodeOpenFile :function(freq,chans,flags,bitrate:DWORD; fname:PChar): HWMENCODE; stdcall;
+var BASS_WMA_EncodeOpenNetwork :function(freq,chans,flags,bitrate,port,clients:DWORD): HWMENCODE; stdcall;
+var BASS_WMA_EncodeOpenNetworkMulti:function(freq,chans,flags:DWORD; bitrates:PDWORD; port,clients:DWORD): HWMENCODE; stdcall;
+var BASS_WMA_EncodeOpenPublish :function(freq,chans,flags,bitrate:DWORD; url,user,pass:PChar): HWMENCODE; stdcall;
+var BASS_WMA_EncodeOpenPublishMulti:function(freq,chans,flags:DWORD; bitrates:PDWORD; url,user,pass:PChar): HWMENCODE; stdcall;
+var BASS_WMA_EncodeGetPort :function(handle:HWMENCODE): DWORD; stdcall;
+var BASS_WMA_EncodeSetNotify :function(handle:HWMENCODE; proc:CLIENTCONNECTPROC; user:Pointer): BOOL; stdcall;
+var BASS_WMA_EncodeGetClients :function(handle:HWMENCODE): DWORD; stdcall;
+var BASS_WMA_EncodeSetTag :function(handle:HWMENCODE; tag,text:PChar; ttype:DWORD): BOOL; stdcall;
+var BASS_WMA_EncodeWrite :function(handle:HWMENCODE; buffer:Pointer; length:DWORD): BOOL; stdcall;
+var BASS_WMA_EncodeClose :function(handle:HWMENCODE): BOOL; stdcall;
+
+var BASS_WMA_GetWMObject :function(handle:DWORD): Pointer; stdcall;
+
+function InitWMA:bool;
+Function Load_WMADLL(dllfilename:PAnsiChar):boolean; overload;
+Function Load_WMADLL(dllfilename:PWideChar):boolean; overload;
+
+implementation
+
+const
+ WMA_Handle:THANDLE = 0;
+ from:integer = 0;
+
+procedure SetProcs(handle:THANDLE);
+begin
+ @BASS_WMA_StreamCreateFile :=GetProcAddress(handle,'BASS_WMA_StreamCreateFile');
+ @BASS_WMA_StreamCreateFileAuth :=GetProcAddress(handle,'BASS_WMA_StreamCreateFileAuth');
+ @BASS_WMA_StreamCreateFileUser :=GetProcAddress(handle,'BASS_WMA_StreamCreateFileUser');
+
+ @BASS_WMA_GetTags :=GetProcAddress(handle,'BASS_WMA_GetTags');
+
+ @BASS_WMA_EncodeGetRates :=GetProcAddress(handle,'BASS_WMA_EncodeGetRates');
+ @BASS_WMA_EncodeOpen :=GetProcAddress(handle,'BASS_WMA_EncodeGetOpen');
+ @BASS_WMA_EncodeOpenFile :=GetProcAddress(handle,'BASS_WMA_EncodeOpenFile');
+ @BASS_WMA_EncodeOpenNetwork :=GetProcAddress(handle,'BASS_WMA_EncodeOpenNetwork');
+ @BASS_WMA_EncodeOpenNetworkMulti:=GetProcAddress(handle,'BASS_WMA_EncodeOpenNetworkMulti');
+ @BASS_WMA_EncodeOpenPublish :=GetProcAddress(handle,'BASS_WMA_EncodeOpenPublish');
+ @BASS_WMA_EncodeOpenPublishMulti:=GetProcAddress(handle,'BASS_WMA_EncodeOpenPublishMulti');
+ @BASS_WMA_EncodeGetPort :=GetProcAddress(handle,'BASS_WMA_EncodeGetPort');
+ @BASS_WMA_EncodeSetNotify :=GetProcAddress(handle,'BASS_WMA_EncodeSetNotify');
+ @BASS_WMA_EncodeGetClients :=GetProcAddress(handle,'BASS_WMA_EncodeGetClients');
+ @BASS_WMA_EncodeSetTag :=GetProcAddress(handle,'BASS_WMA_EncodeSetTag');
+ @BASS_WMA_EncodeWrite :=GetProcAddress(handle,'BASS_WMA_EncodeWrite');
+ @BASS_WMA_EncodeClose :=GetProcAddress(handle,'BASS_WMA_EncodeClose');
+
+ @BASS_WMA_GetWMObject:=GetProcAddress(handle,'BASS_WMA_GetWMObject');
+end;
+
+function InitWMA:bool;
+var
+ info:PBASS_PLUGININFO;
+ i:dword;
+ pHPlugin:^HPLUGIN;
+begin
+ if WMA_Handle<>0 then
+ begin
+ result:=true;
+ exit;
+ end;
+ result:=false;
+ pHPlugin:=pointer(BASS_PluginGetInfo(0));
+ if pHPlugin=nil then exit;
+ while pHPlugin^<>0 do
+ begin
+ info:=BASS_PluginGetInfo(pHPlugin^);
+ i:=0;
+ while i<info^.formatc do
+ begin
+ if info^.formats^[i].ctype=BASS_CTYPE_STREAM_WMA then
+ begin
+ WMA_Handle:=pHPlugin^;
+ SetProcs(pHPlugin^);
+ BASS_SetConfig(BASS_CONFIG_WMA_BASSFILE,1);
+ from:=2;
+ result:=true;
+ exit;
+ end;
+ inc(i);
+ end;
+ inc(pHPlugin);
+ end;
+end;
+
+Function Load_WMADLL(dllfilename:PAnsiChar):boolean;
+var
+ oldmode:integer;
+begin
+ if WMA_Handle<>0 then result:=true
+ else
+ begin
+ oldmode:=SetErrorMode($8001);
+ WMA_Handle:=LoadLibraryA(dllfilename);
+ SetErrorMode(oldmode);
+ result:=WMA_Handle<>0;
+ if result then
+ begin
+ from:=1;
+ SetProcs(WMA_Handle);
+ end;
+ end;
+end;
+
+Function Load_WMADLL(dllfilename:PWideChar):boolean;
+var
+ oldmode:integer;
+begin
+ if WMA_Handle<>0 then result:=true
+ else
+ begin
+ oldmode:=SetErrorMode($8001);
+ WMA_Handle:=LoadLibraryW(dllfilename);
+ SetErrorMode(oldmode);
+ result:=WMA_Handle<>0;
+ if result then
+ begin
+ from:=1;
+ SetProcs(WMA_Handle);
+ end;
+ end;
+end;
+
+Procedure Unload_WMADLL;
+begin
+ if WMA_Handle<>0 then
+ begin
+ if from=2 then
+ BASS_PluginFree(WMA_Handle)
+ else //if from=1 then
+ FreeLibrary(WMA_Handle);
+ WMA_Handle:=0;
+ end;
+ from:=0;
+end;
+
+var
+ mWMA:tBASSRegRec;
+
+procedure Init;
+begin
+ mWMA.Next:=BASSRegRec;
+ mWMA.Init:=@InitWMA;
+ BASSRegRec:=@mWMA;
+end;
+
+begin
+ Init;
+end.
diff --git a/plugins/Libs/err.pas b/plugins/Libs/err.pas
new file mode 100644
index 0000000000..ce21564e2d
--- /dev/null
+++ b/plugins/Libs/err.pas
@@ -0,0 +1,1199 @@
+//{$DEFINE ASM_VERSION}
+//{$DEFINE VARIANT_USED}
+
+{$IFDEF ASM_VERSION}
+ {$IFDEF PAS_VERSION}
+ {$UNDEF ASM_VERSION}
+ {$ENDIF}
+{$ENDIF}
+
+{=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+ KKKKK KKKKK OOOOOOOOO LLLLL
+ KKKKK KKKKK OOOOOOOOOOOOO LLLLL
+ KKKKK KKKKK OOOOO OOOOO LLLLL
+ KKKKK KKKKK OOOOO OOOOO LLLLL
+ KKKKKKKKKK OOOOO OOOOO LLLLL
+ KKKKK KKKKK OOOOO OOOOO LLLLL
+ KKKKK KKKKK OOOOO OOOOO LLLLL
+ KKKKK KKKKK OOOOOOOOOOOOO LLLLLLLLLLLLL
+ KKKKK KKKKK OOOOOOOOO LLLLLLLLLLLLL
+
+ Key Objects Library (C) 2000 by Kladov Vladimir.
+
+ mailto: bonanzas@xcl.cjb.net
+ Home: http://kol.nm.ru
+ http://xcl.cjb.net
+ http://xcl.nm.ru
+
+ =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-}
+{
+ This code is grabbed mainly from standard SysUtils.pas unit,
+ provided by Borland Delphi. This unit is for handling exceptions,
+ and to use it just place a reference to exceptions unit in
+ uses clause of any of your unit or dpr-file.
+}
+
+{ Copyright (C) 1995,99 Inprise Corporation }
+{ Copyright (C) 2001, Kladov Vladimir }
+
+unit err;
+{* Unit to provide error handling for KOL programs using efficient
+ exceptions mechanism. To use it, just place a reference to it into
+ uses clause of any unit of the project (or dpr-file).
+ |<br><br>
+ It is possible to use standard SysUtils instead, but it increases
+ size of executable at least by 10K. Using this unit to handle exceptions
+ increases executable only by 6,5K.
+}
+
+interface
+
+uses Windows, KOL;
+
+{$I KOLDEF.INC}
+{$IFDEF _D6orHigher}
+ {$WARN SYMBOL_DEPRECATED OFF}
+{$ENDIF}
+{$IFDEF _D7orHigher}
+ {$WARN UNSAFE_TYPE OFF}
+ {$WARN UNSAFE_CODE OFF}
+{$ENDIF}
+
+{+} // These resource strings are grabbed from SysConst and changed a bit to make it smaller.
+
+//{$DEFINE USE_RESOURCESTRING}
+{$IFDEF _D2orD3}
+ {$IFDEF USE_RESOURCESTRING}
+ {$UNDEF USE_RESOURCESTRING}
+ {$ENDIF}
+{$ENDIF}
+
+{$IFDEF _D2orD3}
+type
+ LongWord = DWORD;
+{$ENDIF}
+{$IFNDEF USE_RESOURCESTRING}
+const
+{$ELSE}
+resourcestring
+{$ENDIF}
+ SUnknown = '<unknown>';
+ //SInvalidInteger = '''%s'' is not a valid integer value';
+ //SInvalidFloat = '''%s'' is not a valid floating point value';
+ //SInvalidDate = '''%s'' is not a valid date';
+ //SInvalidTime = '''%s'' is not a valid time';
+ //SInvalidDateTime = '''%s'' is not a valid date and time';
+ //STimeEncodeError = 'Invalid argument to time encode';
+ //SDateEncodeError = 'Invalid argument to date encode';
+ SOutOfMemory = 'Out of memory';
+ SInOutError = 'I/O error %d';
+ SFileNotFound = 'File not found';
+ SInvalidFilename = 'Invalid filename';
+ STooManyOpenFiles = 'Too many open files';
+ SAccessDenied = 'File access denied';
+ SEndOfFile = //'Read beyond end of file';
+ 'End of file';
+ SDiskFull = 'Disk full';
+ //SInvalidInput = 'Invalid numeric input'; // {-} Seems for console input only
+ SDivByZero = 'Division by zero';
+ SRangeError = 'Range check error';
+ SIntOverflow = 'Integer overflow';
+ SInvalidOp = 'Invalid floating point operation';
+ SZeroDivide = 'Floating point division by zero';
+ SOverflow = 'Floating point overflow';
+ SUnderflow = 'Floating point underflow';
+ SInvalidPointer = 'Invalid pointer operation';
+ SInvalidCast = 'Invalid class typecast';
+ SAccessViolation = 'Access violation at address %p. %s of address %p';
+ SStackOverflow = 'Stack overflow';
+ SControlC = //'Control-C hit';
+ '^C'; // {-} for console applications only
+ SPrivilege = 'Privileged instruction';
+ SOperationAborted = 'Operation aborted';
+ SException = 'Exception %s in module %s at %p.'#10'%s%s';
+ //SExceptTitle = 'Application Error';
+ //SInvalidFormat = 'Format ''%s'' invalid or incompatible with argument';
+ //SArgumentMissing = 'No argument for format ''%s''';
+ SInvalidVarCast = 'Invalid variant type conversion';
+ SInvalidVarOp = 'Invalid variant operation';
+ SDispatchError = 'Variant method calls not supported';
+ SVarArrayCreate = 'Error creating variant array';
+ SVarNotArray = 'Variant is not an array';
+ SVarArrayBounds = 'Variant array index out of bounds';
+ SVar = 'EVariant';
+ SReadAccess = 'Read';
+ SWriteAccess = 'Write';
+ //SResultTooLong = 'Format result longer than 4096 characters';
+ //SFormatTooLong = 'Format string too long';
+ SExternalException = 'External exception %x';
+ SAssertionFailed = 'Assertion failed';
+ SIntfCastError = 'Interface not supported';
+ SSafecallException = 'Exception in safecall method';
+ SAssertError = '%s (%s, line %d)';
+ SAbstractError = 'Abstract Error';
+ SModuleAccessViolation = 'Access violation at address %p in module ''%s''. %s of address %p';
+ {SCannotReadPackageInfo = 'Cannot access package information for package ''%s''';
+ sErrorLoadingPackage = 'Can''t load package %s.'#13#10'%s';
+ SInvalidPackageFile = 'Invalid package file ''%s''';
+ SInvalidPackageHandle = 'Invalid package handle';
+ SDuplicatePackageUnit = 'Cannot load package ''%s.'' It contains unit ''%s,''' +
+ ';which is also contained in package ''%s''';}
+ SWin32Error = 'Win32 Error. Code: %d.'#10'%s';
+ SUnkWin32Error = 'A Win32 API function failed';
+ SNL = 'Application is not licensed to use this feature';
+{-}
+
+type
+
+{ Generic procedure pointer }
+
+ TProcedure = procedure;
+
+{ Generic filename type }
+
+ TFileName = type string;
+
+{ Exceptions }
+ Exception = class;
+ TDestroyException = procedure( Sender: Exception ) of object;
+
+ TError = ( e_Abort, e_Heap, e_OutOfMem, e_InOut, e_External, e_Int,
+ e_DivBy0, e_Range, e_IntOverflow, e_Math, e_Math_InvalidArgument,
+ e_InvalidOp, e_ZeroDivide, e_Overflow, e_Underflow, e_InvalidPointer,
+ e_InvalidCast, e_Convert, e_AccessViolation, e_Privilege,
+ e_StackOverflow, e_CtrlC, e_Variant, e_PropReadOnly,
+ e_PropWriteOnly, e_Assertion, e_Abstract, e_IntfCast,
+ e_InvalidContainer, e_InvalidInsert, e_Package, e_Win32,
+ e_SafeCall, e_License, e_Custom, e_Com, e_Ole, e_Registry );
+ {* Main error codes. These are to determine which exception occure. You
+ can use e_Custom code for your own exceptions. }
+
+ Exception = class(TObject)
+ {* Exception class. In KOL, there is a single exception class is used.
+ Instead of inheriting new exception classes from this ancestor, an
+ instance of the same Exception class should be used. The difference
+ is only in Code property, which contains a kind of exception. }
+ protected
+ FCode: TError;
+ FErrorCode: DWORD;
+ FMessage: KOLString;
+ FExceptionRecord: PExceptionRecord;
+ FData: Pointer;
+ FOnDestroy: TDestroyException;
+ procedure SetData(const Value: Pointer);
+ public
+ constructor Create(ACode: TError; const Msg: string);
+ {* Use this constructor to raise exception, which does not require of
+ argument formatting. }
+ constructor CreateFmt(ACode: TError; const Msg: string; const Args: array of const);
+ {* Use this constructor to raise an exception with formatted Message string.
+ Take into attention, that Format procedure defined in KOL, uses API wvsprintf
+ function, which can understand a restricted set of format specifications. }
+ constructor CreateCustom(AError: DWORD; const Msg: String);
+ {* Use this constructor to create e_Custom exception and to assign AError to
+ its ErrorCode property. }
+ constructor CreateCustomFmt(AError: DWORD; const Msg: String; const Args: array of const);
+ {* Use this constructor to create e_Custom exception with formatted message
+ string and to assign AError to its ErrorCode property. }
+ constructor CreateResFmt(ACode: TError; Ident: Integer; const Args: array of const);
+ {* }
+ destructor Destroy; override;
+ {* destructor }
+ property Message: KOLString read FMessage; // write FMessage;
+ {* Text string, containing descriptive message about the exception. }
+ property Code: TError read FCode;
+ {* Main exception code. This property can be used to determine, which exception
+ occure. }
+ property ErrorCode: DWORD read FErrorCode write FErrorCode;
+ {* This code is to detailize error. For Code = e_InOut, ErrorCode contains
+ more detail description of input/output error. For e_Custom, You can
+ assign it to any value You want. }
+ property ExceptionRecord: PExceptionRecord read FExceptionRecord;
+ {* This property is only for e_External exception. }
+ property Data: Pointer read FData write SetData;
+ {* Custom defined pointer. Use it in your custom exceptions. }
+ property OnDestroy: TDestroyException read FOnDestroy write FOnDestroy;
+ {* This event is to allow to do something when custom Exception is
+ released. }
+ end;
+ {*
+ With err unit, it is possible to use all capabilities of Delphi exception
+ handling almost in the same way as usual. The difference only in that the
+ single exception class should be used. To determine which exception occure,
+ use property Code. So, code to handle exception can be written like follow:
+ ! try
+ ! ...
+ ! except on E: Exception do
+ ! case E.Code of
+ ! e_DivBy0: HandleDivideByZero;
+ ! e_Overflow: HandleOverflow;
+ ! ...
+ ! end;
+ ! end;
+ To raise an error, create an instance of Exception class object, but
+ pass a Code to its constructor:
+ ! var E: Exception;
+ ! ...
+ ! E := Exception.Create( e_Custom, 'My custom exception' );
+ ! E.ErrorCode := MY_MAGIC_CODE_FOR_CUSTOM_EXCEPTION;
+ ! raise E;
+ }
+
+ ExceptClass = class of Exception;
+
+{ Exit procedure handling }
+
+{ AddExitProc adds the given procedure to the run-time library's exit
+ procedure list. When an application terminates, its exit procedures are
+ executed in reverse order of definition, i.e. the last procedure passed
+ to AddExitProc is the first one to get executed upon termination. }
+
+procedure AddExitProc(Proc: TProcedure);
+
+{ System error messages }
+
+function SysErrorMessage(ErrorCode: Integer): string;
+
+{ Exception handling routines }
+
+function ExceptObject: TObject;
+function ExceptAddr: Pointer;
+
+function ExceptionErrorMessage(ExceptObject: TObject; ExceptAddr: Pointer;
+ Buffer: PKOLChar; Size: Integer): Integer;
+
+procedure ShowException(ExceptObject: TObject; ExceptAddr: Pointer);
+
+procedure Abort;
+
+//procedure OutOfMemoryError;
+
+{ RaiseLastWin32Error calls the GetLastError API to retrieve the code for }
+{ the last occuring Win32 error. If GetLastError returns an error code, }
+{ RaiseLastWin32Error then raises an exception with the error code and }
+{ message associated with with error. }
+
+procedure RaiseLastWin32Error;
+
+{ Win32Check is used to check the return value of a Win32 API function }
+{ which returns a BOOL to indicate success. If the Win32 API function }
+{ returns False (indicating failure), Win32Check calls RaiseLastWin32Error }
+{ to raise an exception. If the Win32 API function returns True, }
+{ Win32Check returns True. }
+
+function Win32Check(RetVal: BOOL): BOOL;
+
+{ Termination procedure support }
+
+type
+ TTerminateProc = function: Boolean;
+
+{ Call AddTerminateProc to add a terminate procedure to the system list of }
+{ termination procedures. Delphi will call all of the function in the }
+{ termination procedure list before an application terminates. The user- }
+{ defined TermProc function should return True if the application can }
+{ safely terminate or False if the application cannot safely terminate. }
+{ If one of the functions in the termination procedure list returns False, }
+{ the application will not terminate. }
+
+procedure AddTerminateProc(TermProc: TTerminateProc);
+
+{ CallTerminateProcs is called by VCL when an application is about to }
+{ terminate. It returns True only if all of the functions in the }
+{ system's terminate procedure list return True. This function is }
+{ intended only to be called by Delphi, and it should not be called }
+{ directly. }
+
+function CallTerminateProcs: Boolean;
+
+{$IFNDEF _D2}
+function GDAL: LongWord;
+procedure RCS;
+procedure RPR;
+{$ENDIF}
+
+
+{ SafeLoadLibrary calls LoadLibrary, disabling normal Win32 error message
+ popup dialogs if the requested file can't be loaded. SafeLoadLibrary also
+ preserves the current FPU control word (precision, exception masks) across
+ the LoadLibrary call (in case the DLL you're loading hammers the FPU control
+ word in its initialization, as many MS DLLs do)}
+
+{$IFNDEF _D2orD3}
+function SafeLoadLibrary(const Filename: KOLString;
+ ErrorMode: UINT = SEM_NOOPENFILEERRORBOX): HMODULE;
+{$ENDIF}
+
+implementation
+
+{procedure ConvertError(const Ident: string);
+begin
+ raise Exception.Create(e_Convert, Ident);
+end;
+
+procedure ConvertErrorFmt(ResString: PResStringRec; const Args: array of const);
+begin
+ raise Exception.CreateFmt(e_Convert, LoadResString(ResString), Args);
+end;}
+
+{ Memory management routines }
+
+function AllocMem(Size: Cardinal): Pointer;
+begin
+ GetMem(Result, Size);
+ FillChar(Result^, Size, 0);
+end;
+
+{ Exit procedure handling }
+
+type
+ PExitProcInfo = ^TExitProcInfo;
+ TExitProcInfo = record
+ Next: PExitProcInfo;
+ SaveExit: Pointer;
+ Proc: TProcedure;
+ end;
+
+var
+ ExitProcList: PExitProcInfo = nil;
+
+procedure DoExitProc;
+var
+ P: PExitProcInfo;
+ Proc: TProcedure;
+begin
+ P := ExitProcList;
+ ExitProcList := P^.Next;
+ ExitProc := P^.SaveExit;
+ Proc := P^.Proc;
+ Dispose(P);
+ Proc;
+end;
+
+procedure AddExitProc(Proc: TProcedure);
+var
+ P: PExitProcInfo;
+begin
+ New(P);
+ P^.Next := ExitProcList;
+ P^.SaveExit := ExitProc;
+ P^.Proc := Proc;
+ ExitProcList := P;
+ ExitProc := @DoExitProc;
+end;
+
+{ System error messages }
+
+function SysErrorMessage(ErrorCode: Integer): string;
+var
+ Len: Integer;
+ Buffer: array[0..255] of KOLChar;
+begin
+ Len := FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM or
+ FORMAT_MESSAGE_ARGUMENT_ARRAY, nil, ErrorCode, 0, Buffer,
+ SizeOf(Buffer), nil);
+ while (Len > 0) and ((Buffer[Len - 1] <= ' ') or
+ (Buffer[Len - 1] = '.')) do Dec(Len);
+ SetString(Result, Buffer, Len);
+end;
+
+{ Exception handling routines }
+
+{var
+ OutOfMemory: EOutOfMemory;
+ InvalidPointer: EInvalidPointer;}
+
+type
+ PRaiseFrame = ^TRaiseFrame;
+ TRaiseFrame = record
+ NextRaise: PRaiseFrame;
+ ExceptAddr: Pointer;
+ ExceptObject: TObject;
+ ExceptionRecord: PExceptionRecord;
+ end;
+
+{ Return current exception object }
+
+function ExceptObject: TObject;
+begin
+ if RaiseList <> nil then
+ Result := PRaiseFrame(RaiseList)^.ExceptObject else
+ Result := nil;
+end;
+
+{ Return current exception address }
+
+function ExceptAddr: Pointer;
+begin
+ if RaiseList <> nil then
+ Result := PRaiseFrame(RaiseList)^.ExceptAddr else
+ Result := nil;
+end;
+
+{ Convert physical address to logical address }
+
+function ConvertAddr(Address: Pointer): Pointer; assembler;
+asm
+ TEST EAX,EAX { Always convert nil to nil }
+ JE @@1
+ SUB EAX, $1000 { offset from code start; code start set by linker to $1000 }
+@@1:
+end;
+
+{ Format and return an exception error message }
+
+{$IFDEF _D2} // this code is luck in D2 system.pas
+{type
+ PLibModule = ^TLibModule;
+ TLibModule = record
+ Next: PLibModule;
+ Instance: Longint;
+ ResInstance: Longint;
+ Reserved: Integer;
+ end;}
+
+function FindResourceHInstance(Instance: Longint): Longint;
+begin
+ Result := Instance;
+end;
+{$ENDIF}
+
+type
+ PStrData = ^TStrData;
+ TStrData = record
+ Ident: Integer;
+ Buffer: PKOLChar;
+ BufSize: Integer;
+ nChars: Integer;
+ end;
+
+function EnumStringModules(Instance: THANDLE; Data: Pointer): Boolean;
+begin
+ with PStrData(Data)^ do
+ begin
+ nChars := LoadString(Instance, Ident, Buffer, BufSize);
+ Result := nChars = 0;
+ end;
+end;
+
+{$IFNDEF _D2}
+function FindStringResource(Ident: Integer; Buffer: PKOLChar; BufSize: Integer): Integer;
+var
+ StrData: TStrData;
+begin
+ StrData.Ident := Ident;
+ StrData.Buffer := Buffer;
+ StrData.BufSize := BufSize;
+ StrData.nChars := 0;
+ EnumResourceModules(EnumStringModules, @StrData);
+ Result := StrData.nChars;
+end;
+{$ENDIF}
+
+{$IFDEF _D2}
+function LoadStr(Ident: Integer): string;
+var
+ Buffer: array[0..1023] of Char;
+begin
+ SetString(Result, Buffer, LoadString(HInstance, Ident, Buffer,
+ SizeOf(Buffer)));
+end;
+{$ELSE}
+function LoadStr(Ident: Integer): string;
+var
+ Buffer: array[0..1023] of KOLChar;
+begin
+ SetString(Result, Buffer, FindStringResource(Ident, Buffer, SizeOf(Buffer)));
+end;
+{$ENDIF}
+
+function FmtLoadStr(Ident: Integer; const Args: array of const): string;
+begin
+ //FmtStr(Result, LoadStr(Ident), Args);
+ Result := Format(LoadStr(Ident), Args);
+end;
+
+function ExceptionErrorMessage(ExceptObject: TObject; ExceptAddr: Pointer;
+ Buffer: PKOLChar; Size: Integer): Integer;
+var
+ ex: Exception;
+ MsgPtr: PKOLChar;
+ //MsgEnd: PChar;
+ //MsgLen: Integer;
+ ModuleName: array[0..MAX_PATH] of KOLChar;
+ //Temp: array[0..MAX_PATH] of Char;
+ Fmt: array[0..255] of KOLChar;
+ Info: TMemoryBasicInformation;
+ ConvertedAddress: Pointer;
+begin
+ VirtualQuery(ExceptAddr, Info, sizeof(Info));
+ if (Info.State <> MEM_COMMIT) or
+ (GetModuleFilename( THandle(Info.AllocationBase), {Temp} ModuleName,
+ SizeOf({Temp} ModuleName)) = 0) then
+ begin
+ GetModuleFileName(HInstance, {Temp} ModuleName, SizeOf({Temp} ModuleName));
+ ConvertedAddress := ConvertAddr(ExceptAddr);
+ end
+ else
+ Integer(ConvertedAddress) := Integer(ExceptAddr) - Integer(Info.AllocationBase);
+ //StrLCopy(ModuleName, AnsiStrRScan(Temp, '\') + 1, SizeOf(ModuleName) - 1);
+ {-} // Why to extract unit name from a path? Isn't it well to show complete path
+ // and to economy code for the extraction.
+ MsgPtr := '';
+ //MsgEnd := '';
+ if ExceptObject is Exception then
+ begin
+ ex := Exception(ExceptObject);
+ MsgPtr := PKOLChar(ex.Message);
+ //MsgLen := StrLen(MsgPtr);
+ //if (MsgLen <> 0) and (MsgPtr[MsgLen - 1] <> '.') then MsgEnd := '.';
+ {-} // Isn't it too beautiful - devote ~40 bytes of code just to decide,
+ // add or not a point at the end of the message.
+ end;
+ {$IFNDEF USE_RESOURCESTRING}
+ {$IFDEF UNICODE_CTRLS} WStrCopy {$ELSE} StrCopy {$ENDIF}( Fmt, SException );
+ {$ELSE}
+ LoadString(FindResourceHInstance(HInstance),
+ PResStringRec(@SException).Identifier, Fmt, SizeOf(Fmt));
+ {$ENDIF}
+ //MsgOK( ModuleName );
+ {$IFDEF UNICODE_CTRLS} WStrCopy {$ELSE} StrCopy {$ENDIF}
+ ( Buffer, PKOLChar( Format( Fmt, [ ExceptObject.ClassName,
+ ModuleName, ConvertedAddress, MsgPtr, '' {MsgEnd}]) ) );
+ Result := {$IFDEF UNICODE_CTRLS} WStrLen {$ELSE} StrLen {$ENDIF}(Buffer);
+end;
+
+{ Display exception message box }
+
+procedure ShowException(ExceptObject: TObject; ExceptAddr: Pointer);
+var
+ Buffer: array[0..1023] of KOLChar;
+begin
+ ExceptionErrorMessage(ExceptObject, ExceptAddr, Buffer, SizeOf(Buffer));
+ {if IsConsole then
+ WriteLn(Buffer)
+ else}
+ begin
+ {LoadString(FindResourceHInstance(HInstance), PResStringRec(@SExceptTitle).Identifier,
+ Title, SizeOf(Title));}
+ MessageBox(0, Buffer, {Title} nil, MB_OK {or MB_ICONSTOP} or MB_SYSTEMMODAL);
+ end;
+end;
+
+{ Raise abort exception }
+
+procedure Abort;
+
+ function ReturnAddr: Pointer;
+ asm
+// MOV EAX,[ESP + 4] !!! codegen dependant
+ MOV EAX,[EBP - 4]
+ end;
+
+begin
+ raise Exception.Create(e_Abort, SOperationAborted) at ReturnAddr;
+end;
+
+{ Raise out of memory exception }
+
+{procedure OutOfMemoryError;
+begin
+ raise OutOfMemory;
+end;}
+
+{ Exception class }
+
+constructor Exception.CreateResFmt(ACode: TError; Ident: Integer;
+ const Args: array of const);
+begin
+ FMessage := Format(LoadStr(Ident), Args);
+end;
+
+destructor Exception.Destroy;
+begin
+ if Assigned( FOnDestroy ) then
+ FOnDestroy( Self );
+ inherited;
+end;
+
+procedure Exception.SetData(const Value: Pointer);
+begin
+ FData := Value;
+end;
+
+constructor Exception.Create(ACode: TError; const Msg: string);
+begin
+ FCode := ACode;
+ FMessage := Msg;
+ //FAllowFree := TRUE;
+end;
+
+constructor Exception.CreateCustom(AError: DWORD; const Msg: String);
+begin
+ FCode := e_Custom;
+ FMessage := Msg;
+ FErrorCode := AError;
+end;
+
+constructor Exception.CreateCustomFmt(AError: DWORD; const Msg: String;
+ const Args: array of const);
+begin
+ FCode := e_Custom;
+ FErrorCode := AError;
+ FMessage := Format(Msg, Args);
+end;
+
+constructor Exception.CreateFmt(ACode: TError; const Msg: string;
+ const Args: array of const);
+begin
+ FCode := ACode;
+ FMessage := Format(Msg, Args);
+end;
+
+{ EHeapException class }
+
+{procedure EHeapException.FreeInstance;
+begin
+ if AllowFree then
+ inherited FreeInstance;
+end;}
+
+{ Create I/O exception }
+
+function CreateInOutError: Exception;
+type
+ TErrorRec = record
+ Code: Integer;
+ Ident: string;
+ end;
+const
+ ErrorMap: array[0..5] of TErrorRec = (
+ (Code: 2; Ident: SFileNotFound),
+ (Code: 3; Ident: SInvalidFilename),
+ (Code: 4; Ident: STooManyOpenFiles),
+ (Code: 5; Ident: SAccessDenied),
+ (Code: 100; Ident: SEndOfFile),
+ (Code: 101; Ident: SDiskFull){,
+ (Code: 106; Ident: SInvalidInput)} );
+var
+ I: Integer;
+ InOutRes: Integer;
+begin
+ I := Low(ErrorMap);
+ InOutRes := IOResult; // resets IOResult to zero
+ while (I <= High(ErrorMap)) and (ErrorMap[I].Code <> InOutRes) do Inc(I);
+ if I <= High(ErrorMap) then
+ Result := Exception.Create(e_InOut, ErrorMap[I].Ident)
+ else
+ Result := Exception.CreateFmt(e_InOut, SInOutError, [InOutRes]);
+ //Result := Exception.Create(e_InOut, SInOutError + Int2Str( InOutRes ) );
+ Result.ErrorCode := InOutRes;
+end;
+
+{ RTL error handler }
+
+type
+ TExceptMapRec = packed record
+ ECode: TError;
+ EIdent: String;
+ end;
+
+const
+ ExceptMap: array[1..24] of TExceptMapRec = (
+ (ECode: e_OutOfMem; EIdent: SOutOfMemory),
+ (ECode: e_InvalidPointer; EIdent: SInvalidPointer),
+ (ECode: e_DivBy0; EIdent: SDivByZero),
+ (ECode: e_Range; EIdent: SRangeError),
+ (ECode: e_IntOverflow; EIdent: SIntOverflow),
+ (ECode: e_InvalidOp; EIdent: SInvalidOp),
+ (ECode: e_ZeroDivide; EIdent: SDivByZero),
+ (ECode: e_Overflow; EIdent: SOverflow),
+ (ECode: e_Underflow; EIdent: SUnderflow),
+ (ECode: e_InvalidCast; EIdent: SInvalidCast),
+ (ECode: e_AccessViolation;EIdent: SAccessViolation),
+ (ECode: e_Privilege; EIdent: SPrivilege),
+ (ECode: e_CtrlC; EIdent: SControlC),
+ // {-} Only for console applications
+ (ECode: e_StackOverflow; EIdent: SStackOverflow),
+ {$IFDEF VARIANT_USED}
+ (ECode: e_Variant; EIdent: SInvalidVarCast),
+ (ECode: e_Variant; EIdent: SInvalidVarOp),
+ (ECode: e_Variant; EIdent: SDispatchError),
+ (ECode: e_Variant; EIdent: SVarArrayCreate),
+ (ECode: e_Variant; EIdent: SVarNotArray),
+ (ECode: e_Variant; EIdent: SVarArrayBounds),
+ {$ELSE}
+ (ECode: e_Variant; EIdent: SVar),
+ (ECode: e_Variant; EIdent: SVar),
+ (ECode: e_Variant; EIdent: SVar),
+ (ECode: e_Variant; EIdent: SVar),
+ (ECode: e_Variant; EIdent: SVar),
+ (ECode: e_Variant; EIdent: SVar),
+ {$ENDIF}
+ (ECode: e_Assertion; EIdent: SAssertionFailed),
+ (ECode: e_External; EIdent: SExternalException),
+ (ECode: e_IntfCast; EIdent: SIntfCastError),
+ (ECode: e_SafeCall; EIdent: SSafecallException));
+
+procedure ErrorHandler(ErrorCode: Integer; ErrorAddr: Pointer);
+var
+ E: Exception;
+begin
+ {case ErrorCode of
+ 1: E := OutOfMemory;
+ 2: E := InvalidPointer;
+ 3..24: with ExceptMap[ErrorCode] do E := EClass.Create(EIdent);
+ else
+ E := CreateInOutError;
+ end;}
+
+ { + }
+ if ErrorCode <= 24 then
+ with ExceptMap[ErrorCode] do E := Exception.Create(ECode, EIdent)
+ else E := CreateInOutError;
+ { - }
+
+ raise E at ErrorAddr;
+end;
+
+{ Assertion error handler }
+
+{ This is complicated by the desire to make it look like the exception }
+{ happened in the user routine, so the debugger can give a decent stack }
+{ trace. To make that feasible, AssertErrorHandler calls a helper function }
+{ to create the exception object, so that AssertErrorHandler itself does }
+{ not need any temps. After the exception object is created, the asm }
+{ routine RaiseAssertException sets up the registers just as if the user }
+{ code itself had raised the exception. }
+
+function CreateAssertException(const Message, Filename: string;
+ LineNumber: Integer): Exception;
+var
+ S: string;
+begin
+ if Message <> '' then S := Message else S := SAssertionFailed;
+ Result := Exception.CreateFmt(e_Assertion, SAssertError,
+ [S, Filename, LineNumber]);
+end;
+
+{ This code is based on the following assumptions: }
+{ - Our direct caller (AssertErrorHandler) has an EBP frame }
+{ - ErrorStack points to where the return address would be if the }
+{ user program had called System.@RaiseExcept directly }
+procedure RaiseAssertException(const E: Exception; const ErrorAddr, ErrorStack: Pointer);
+asm
+ MOV ESP,ECX
+ MOV [ESP],EDX
+ MOV EBP,[EBP]
+ JMP System.@RaiseExcept
+end;
+
+{ If you change this procedure, make sure it does not have any local variables }
+{ or temps that need cleanup - they won't get cleaned up due to the way }
+{ RaiseAssertException frame works. Also, it can not have an exception frame. }
+procedure AssertErrorHandler(const Message, Filename: string;
+ LineNumber: Integer; ErrorAddr: Pointer);
+var
+ E: Exception;
+begin
+ E := CreateAssertException(Message, Filename, LineNumber);
+ RaiseAssertException(E, ErrorAddr, PChar(@ErrorAddr)+4);
+end;
+
+{ Abstract method invoke error handler }
+
+procedure AbstractErrorHandler;
+begin
+ raise Exception.Create(e_Abstract, SAbstractError);
+end;
+
+{$IFDEF ASM_VERSION}
+function MapException(P: PExceptionRecord): Byte;
+asm //cmd //opd
+ MOV EAX, [EAX].TExceptionRecord.ExceptionCode
+ SUB EAX, $C0000000
+ CMP EAX, $FD
+ JA @@code22
+
+ XOR ECX, ECX
+ MOV EDX, offset @@cvTable - 1
+@@loo:
+ INC EDX
+ MOV CL, [EDX]
+ JECXZ @@code22
+ INC EDX
+ CMP AL, [EDX]
+ JNE @@loo
+
+ MOV AL, CL
+ RET
+
+@@cvTable:
+ DB 3, $94
+ DB 4, $8C
+ DB 5, $95
+ DB 6, $8F, 6, $90, 6, $92
+ DB 7, $8E
+ DB 8, $91
+ DB 9, $8D, 9, $93
+ DB 11, $05
+ DB 12, $96
+ DB 14, $FD
+ DB 0
+
+@@code22:
+ MOV AL, 22
+end;
+{$ELSE} //Pascal
+function MapException(P: PExceptionRecord): Byte;
+begin
+ case P.ExceptionCode of
+ STATUS_INTEGER_DIVIDE_BY_ZERO:
+ Result := 3;
+ STATUS_ARRAY_BOUNDS_EXCEEDED:
+ Result := 4;
+ STATUS_INTEGER_OVERFLOW:
+ Result := 5;
+ STATUS_FLOAT_INEXACT_RESULT,
+ STATUS_FLOAT_INVALID_OPERATION,
+ STATUS_FLOAT_STACK_CHECK:
+ Result := 6;
+ STATUS_FLOAT_DIVIDE_BY_ZERO:
+ Result := 7;
+ STATUS_FLOAT_OVERFLOW:
+ Result := 8;
+ STATUS_FLOAT_UNDERFLOW,
+ STATUS_FLOAT_DENORMAL_OPERAND:
+ Result := 9;
+ STATUS_ACCESS_VIOLATION:
+ Result := 11;
+ STATUS_PRIVILEGED_INSTRUCTION:
+ Result := 12;
+ STATUS_CONTROL_C_EXIT:
+ Result := 13;
+ STATUS_STACK_OVERFLOW:
+ Result := 14;
+ else
+ Result := 22; { must match System.reExternalException }
+ end;
+end;
+{$ENDIF}
+
+function GetExceptionClass(P: PExceptionRecord): ExceptClass;
+//var ErrorCode: Byte;
+begin
+ //ErrorCode := MapException(P);
+ Result := Exception; {ExceptMap[ErrorCode].EClass;}
+end;
+
+function GetExceptionObject(P: PExceptionRecord): Exception;
+var
+ ErrorCode: Integer;
+
+ function CreateAVObject: Exception;
+ var
+ AccessOp: string; // string ID indicating the access type READ or WRITE
+ AccessAddress: Pointer;
+ MemInfo: TMemoryBasicInformation;
+ ModName: array[0..MAX_PATH] of KOLChar;
+ begin
+ with P^ do
+ begin
+ if ExceptionInformation[0] = 0 then
+ AccessOp := SReadAccess else
+ AccessOp := SWriteAccess;
+ AccessAddress := Pointer(ExceptionInformation[1]);
+ VirtualQuery(ExceptionAddress, MemInfo, SizeOf(MemInfo));
+ if (MemInfo.State = MEM_COMMIT) and (GetModuleFileName(THandle(MemInfo.AllocationBase),
+ ModName, SizeOf(ModName)) <> 0) then
+ Result := Exception.CreateFmt(e_AccessViolation, sModuleAccessViolation,
+ [ExceptionAddress, ExtractFileName(ModName), AccessOp,
+ AccessAddress])
+ else Result := Exception.CreateFmt(e_AccessViolation, sAccessViolation,
+ [ExceptionAddress, AccessOp, AccessAddress]);
+ end;
+ end;
+
+begin
+ ErrorCode := MapException(P);
+ case ErrorCode of
+ 3..10, 12..21:
+ with ExceptMap[ErrorCode] do Result := Exception.Create(ECode, EIdent);
+ 11: Result := CreateAVObject;
+ else
+ begin
+ Result := Exception.CreateFmt(e_External, SExternalException, [P.ExceptionCode]);
+ //Result.FExceptionRecord := P;
+ end;
+ end;
+ Result.FExceptionRecord := P;
+end;
+
+{ RTL exception handler }
+
+procedure ExceptHandler(ExceptObject: TObject; ExceptAddr: Pointer); far;
+begin
+ ShowException(ExceptObject, ExceptAddr);
+ Halt(1);
+end;
+
+{+}
+function InitAssertErrorProc: Boolean;
+begin
+ AssertErrorProc := @AssertErrorHandler;
+ Result := TRUE;
+end;
+{-}
+
+procedure InitExceptions;
+begin
+ {OutOfMemory := EOutOfMemory.Create(SOutOfMemory);
+ InvalidPointer := EInvalidPointer.Create(SInvalidPointer);}
+ ErrorProc := @ErrorHandler;
+ ExceptProc := @ExceptHandler;
+ ExceptionClass := Exception;
+
+ ExceptClsProc := @GetExceptionClass;
+
+ ExceptObjProc := @GetExceptionObject;
+
+ {AssertErrorProc := @AssertErrorHandler;}
+ {+} // Initialize Assert only when "Assertions" option is turned on in Compiler:
+ Assert( InitAssertErrorProc, '' );
+ {-}
+
+ //AbstractErrorProc := @AbstractErrorHandler;
+ // {-} KOL does not use classes, so EAbstractError should never be raised.
+
+end;
+
+procedure DoneExceptions;
+begin
+ {OutOfMemory.AllowFree := True;
+ OutOfMemory.FreeInstance;
+ OutOfMemory := nil;
+ InvalidPointer.AllowFree := True;
+ InvalidPointer.Free;
+ InvalidPointer := nil;}
+ ErrorProc := nil;
+ ExceptProc := nil;
+ ExceptionClass := nil;
+ //ExceptClsProc := nil; --see InitExceptions
+ ExceptObjProc := nil;
+ AssertErrorProc := nil;
+end;
+
+{ RaiseLastWin32Error }
+
+procedure RaiseLastWin32Error;
+var
+ LastError: DWORD;
+ Error: Exception;
+begin
+ LastError := GetLastError;
+ if LastError <> ERROR_SUCCESS then
+ Error := Exception.CreateFmt(e_Win32, SWin32Error, [LastError,
+ SysErrorMessage(LastError)])
+ else
+ Error := Exception.Create(e_Win32, SUnkWin32Error );
+ Error.ErrorCode := LastError;
+ raise Error;
+end;
+
+{ Win32Check }
+
+function Win32Check(RetVal: BOOL): BOOL;
+begin
+ if not RetVal then RaiseLastWin32Error;
+ Result := RetVal;
+end;
+
+type
+ PTerminateProcInfo = ^TTerminateProcInfo;
+ TTerminateProcInfo = record
+ Next: PTerminateProcInfo;
+ Proc: TTerminateProc;
+ end;
+
+var
+ TerminateProcList: PTerminateProcInfo = nil;
+
+procedure AddTerminateProc(TermProc: TTerminateProc);
+var
+ P: PTerminateProcInfo;
+begin
+ New(P);
+ P^.Next := TerminateProcList;
+ P^.Proc := TermProc;
+ TerminateProcList := P;
+end;
+
+function CallTerminateProcs: Boolean;
+var
+ PI: PTerminateProcInfo;
+begin
+ Result := True;
+ PI := TerminateProcList;
+ while Result and (PI <> nil) do
+ begin
+ Result := PI^.Proc;
+ PI := PI^.Next;
+ end;
+end;
+
+procedure FreeTerminateProcs;
+var
+ PI: PTerminateProcInfo;
+begin
+ while TerminateProcList <> nil do
+ begin
+ PI := TerminateProcList;
+ TerminateProcList := PI^.Next;
+ Dispose(PI);
+ end;
+end;
+
+{ --- }
+
+function AL1(const P): LongWord;
+asm
+ MOV EDX,DWORD PTR [P]
+ XOR EDX,DWORD PTR [P+4]
+ XOR EDX,DWORD PTR [P+8]
+ XOR EDX,DWORD PTR [P+12]
+ MOV EAX,EDX
+end;
+
+function AL2(const P): LongWord;
+asm
+ MOV EDX,DWORD PTR [P]
+ ROR EDX,5
+ XOR EDX,DWORD PTR [P+4]
+ ROR EDX,5
+ XOR EDX,DWORD PTR [P+8]
+ ROR EDX,5
+ XOR EDX,DWORD PTR [P+12]
+ MOV EAX,EDX
+end;
+
+const
+ AL1s: array[0..2] of LongWord = ($FFFFFFF0, $FFFFEBF0, 0);
+ AL2s: array[0..2] of LongWord = ($42C3ECEF, $20F7AEB6, $D1C2F74E);
+
+procedure ALV;
+begin
+ raise Exception.Create(e_License, SNL);
+end;
+
+{$IFNDEF _D2}
+function ALR: Pointer;
+var
+ LibModule: PLibModule;
+begin
+ if MainInstance <> 0 then
+ Result := Pointer(LoadResource(MainInstance, FindResource(MainInstance, 'DVCLAL',
+ PKOLChar( RT_RCDATA ))))
+ else
+ begin
+ Result := nil;
+ LibModule := LibModuleList;
+ while LibModule <> nil do
+ begin
+ with LibModule^ do
+ begin
+ Result := Pointer(LoadResource(Instance, FindResource(Instance, 'DVCLAL',
+ PKOLChar( RT_RCDATA ))));
+ if Result <> nil then Break;
+ end;
+ LibModule := LibModule.Next;
+ end;
+ end;
+ if Result = nil then ALV;
+end;
+
+function GDAL: LongWord;
+type
+ TDVCLAL = array[0..3] of LongWord;
+ PDVCLAL = ^TDVCLAL;
+var
+ P: Pointer;
+ A1, A2: LongWord;
+ PAL1s, PAL2s: PDVCLAL;
+ ALOK: Boolean;
+begin
+ P := ALR;
+ A1 := AL1(P^);
+ A2 := AL2(P^);
+ Result := A1;
+ PAL1s := @AL1s;
+ PAL2s := @AL2s;
+ ALOK := ((A1 = PAL1s[0]) and (A2 = PAL2s[0])) or
+ ((A1 = PAL1s[1]) and (A2 = PAL2s[1])) or
+ ((A1 = PAL1s[2]) and (A2 = PAL2s[2]));
+ FreeResource(Integer(P));
+ if not ALOK then ALV;
+end;
+
+procedure RCS;
+var
+ P: Pointer;
+ ALOK: Boolean;
+begin
+ P := ALR;
+ ALOK := (AL1(P^) = AL1s[2]) and (AL2(P^) = AL2s[2]);
+ FreeResource(Integer(P));
+ if not ALOK then ALV;
+end;
+
+procedure RPR;
+var
+ AL: LongWord;
+begin
+ AL := GDAL;
+ if (AL <> AL1s[1]) and (AL <> AL1s[2]) then ALV;
+end;
+{$ENDIF}
+
+{$IFNDEF _D2orD3}
+function SafeLoadLibrary(const Filename: KOLString; ErrorMode: UINT): HMODULE;
+var
+ OldMode: UINT;
+ FPUControlWord: Word;
+begin
+ OldMode := SetErrorMode(ErrorMode);
+ try
+ asm
+ FNSTCW FPUControlWord
+ end;
+ try
+ Result := LoadLibrary(PKOLChar(Filename));
+ finally
+ asm
+ FNCLEX
+ FLDCW FPUControlWord
+ end;
+ end;
+ finally
+ SetErrorMode(OldMode);
+ end;
+end;
+{$ENDIF}
+
+{procedure Exception.FreeInstance;
+begin
+ if FAllowFree then
+ inherited;
+end;}
+
+
+
+initialization
+ InitExceptions;
+
+finalization
+ FreeTerminateProcs;
+ DoneExceptions;
+
+end.
+
diff --git a/plugins/Libs/kol.pas b/plugins/Libs/kol.pas
new file mode 100644
index 0000000000..74d97b7a85
--- /dev/null
+++ b/plugins/Libs/kol.pas
@@ -0,0 +1,61873 @@
+//[START OF KOL.pas]
+{****************************************************************
+
+ KKKKK KKKKK OOOOOOOOO LLLLL
+ KKKKK KKKKK OOOOOOOOOOOOO LLLLL
+ KKKKK KKKKK OOOOO OOOOO LLLLL
+ KKKKK KKKKK OOOOO OOOOO LLLLL
+ KKKKKKKKKK OOOOO OOOOO LLLLL
+ KKKKK KKKKK OOOOO OOOOO LLLLL
+ KKKKK KKKKK OOOOO OOOOO LLLLL
+ KKKKK KKKKK OOOOOOOOOOOOO LLLLLLLLLLLLL
+ KKKKK KKKKK OOOOOOOOO LLLLLLLLLLLLL
+
+ Key Objects Library (C) 2000 by Vladimir Kladov.
+
+****************************************************************
+* VERSION 3.18
+****************************************************************
+
+ K.O.L. - is a set of objects and functions to create small programs
+ with the Delphi, but without the VCL/CLX. KOL allows to create
+ executables of size about 10 times smaller. But this does not mean that
+ KOL is less power then the VCL - perhaps just the opposite...
+
+ KOL is provided free with the source code.
+ Copyright (C) Vladimir Kladov, 2000-2011.
+
+ For code provided by other developers (even if later
+ changed by me) authors are noted in the source.
+
+ mailto: vk@kolmck.net
+ Web-Page: http://kolmck.net
+
+ See also Mirror Classes Kit (M.C.K.) which allows
+ to create KOL programs visually.
+
+****************************************************************}
+
+{$I KOLDEF.inc}
+
+{$IFDEF x64}
+ {$DEFINE PAS_ONLY}
+{$ENDIF}
+{$IFDEF PAS_ONLY}
+ {$DEFINE PAS_VERSION}
+{$ENDIF}
+
+{$IFDEF EXTERNAL_KOLDEFS}
+ {$INCLUDE PROJECT_KOL_DEFS.INC}
+{$ENDIF}
+{$IFDEF EXTERNAL_DEFINES}
+ {$INCLUDE EXTERNAL_DEFINES.INC}
+{$ENDIF EXTERNAL_DEFINES}
+
+ {$DEFINE GDI}
+
+{$UNDEF LIN} {$UNDEF WIN} {$UNDEF GDI}
+{$IFDEF LINUX}
+ {$DEFINE UNIX}
+ {$DEFINE LIN}
+ {$DEFINE PAS_VERSION}
+ {$DEFINE NOT_USE_RICHEDIT}
+ {$IFNDEF GTK}
+ {$IFNDEF XQT}
+ {$DEFINE GTK} // it is also possible to define GTK as a project option
+ {$ENDIF XQT} // even for Windows system
+ {$ENDIF GTK}
+{$ELSE} // to exploit GTK under Win32 rather then native GDI
+ {$DEFINE WIN}
+ {$DEFINE GDI}
+{$ENDIF}
+
+ {$IFDEF GTK} {$UNDEF GDI} {$DEFINE _X_}
+ {$DEFINE NOT_USE_RICHEDIT}
+ {$ENDIF}
+//{$IFDEF Q_T} {$UNDEF GDI} {$DEFINE _X_} {$ENDIF}
+
+{$IFDEF WIN} {$IFDEF GDI}
+ {$DEFINE WIN_GDI}
+{$ENDIF GDI} {$ENDIF WIN}
+
+{.$INCLUDE delphidef.inc}
+
+{$IFDEF WIN_GDI}
+ //test
+{$ENDIF WIN_GDI}
+{$IFDEF LIN}
+ //test
+{$ENDIF LIN}
+
+unit KOL;
+{*
+ Please note, that KOL does not use keyword 'class'. Instead,
+ poor Pascal 'object' is the base of our objects. So, remember,
+ how we worked earlier with such Object Pascal's objects:
+|<br>
+ - to create objects dynamically, use P<objname> instead of
+ T<objname> to allocate a pointer for dynamically created
+ object instance;
+|<br>
+ - remember, that constructors of objects can not be virtual.
+ Override procedure Init instead in your own derived objects;
+|<br>
+ - rather then call constructors of objects, call global procedures
+ New<objname> (e.g. NewLabel). If not, first (for virtualally
+ created objects) call New( ); then call constructor Create
+ (which calls Init) - but this is possible only if the constructor
+ is overriden by a new one.
+|<br>
+ - the operator 'is' is not applicable to objects. And operator 'as'
+ is not necessary (and is not applicable too), use typecast to desired
+ object type, e.g.: "PSomeObjectType( C )" inplace of "C as TSomeClassType".
+|<br>
+|<hr>
+ Also remember, that IF [ MyObj: PMyObj ] THEN
+
+ NOT[ with MyObj do ] BUT[ with MyObj^ do ]
+
+ Though it is possible to skip '^' symbol when accessing member
+ fields, methods, properties, e.g. [ MyObj.Execute; ]
+|<hr>
+|&U=&nbsp;&nbsp;&nbsp;<a href="#%0">%0</a><br>
+|&B=<a href="%1.htm">%0</a><br>
+|&C=<a href="%1.htm">%0</a>
+| <table border=1 cellpadding=6 width=100%>
+| <colgroup valign=top span=2>
+| <tr>
+| <td> objects </td> <td> functions by category </td>
+| </tr>
+| <td>
+ <C _TObj> <B TObj>
+ <C TList> <C TListEx> <C TStrList> <B TStrListEx>
+ <C TTree> <C TDirList> <C TIniFile> <C TCabFile>
+ <B TStream>
+ <B TControl>
+ <C TGraphicTool> <C TCanvas> <C TImageList> <C TIcon> <C TBitmap>
+ <C TGif> <C TGifDecoder> <B TJpeg>
+ <C TTimer> <C TThread> <C TTrayIcon> <C TDirChange> <B TMediaPlayer>
+ <C TMenu> <C TOpenSaveDialog> <C TOpenDirDialog> <B TColorDialog>
+ <C TAction> <B TActionList>
+ <B Exception>
+| </td>
+| <td>
+|<a href="kol_pas.htm#visual_objects_constructors">
+ Visual objects constructing functions
+|</a><br><br>
+ <U Working with null-terminated and ansi strings>
+ <U Small bit arrays (max 32 bits in array)>
+ <U Arithmetics, geometry and other utility functions>
+ <U Data sorting (quicksort implementation)>
+ <U String to number and number to string conversions>
+ <U 64-bit integer numbers>
+ <U Floating point numbers>
+ <U Date and time handling>
+ <U File and directory routines>
+ <U System functions and working with windows>
+ <U Text in clipboard operations>
+ <U Wrappers to registry API functions>
+| </td>
+| </table>
+
+ Following conditional symbols can be used in a project
+ (Project | Options | Directories/Conditional Defines)
+ to change code generated a bit. There are following:
+|<pre>
+
+ EXTERNAL_KOLDEFS - since there are a lot of such symbols, it may be not
+ possible to include all the desired optional symbols
+ in the Project Options (Delphi has a restriction to 256
+ characters in a semicolon-separated list of included
+ options). This symbol allows to exceed this restriction:
+ you place your defines in an included file
+ EXTERNAL_DEFINES.INC, located in your project directory.
+ Since this is a normal pascal source, use usual Pascal
+ syntax: add a directive (*$DEFINE symbol*) for each
+ symbol you want, and you can decorate it with usual
+ comments if necessary.
+ ENABLE_DEPRECATED - some old declaration made "deprecated" and moved to
+ KOL_deprecated.inc. This symbol provides including
+ such declarations into KOL.pas and makes it available
+ again.
+ DISABLE_DEPRECATED - (default) - disables deprecated declaration.
+ WIN - (default) - version for Windows.
+ LINUX - version for Linux (only PAS_VERSION) -- not yet ready
+ When not defined, symbol WIN is defined automatically.
+ LINUX_USE_HOME_STARTFDIR - in Linux app, HOME directory of the user will be
+ returned by GetStartDir function.
+ GTK - version for GTK (Linux or Win32) -- not yet ready
+ XQT - version for QT (Linux or Win32) -- not yet ready
+ FPC - Free Pascal version. KOL can be used with such compiler
+ to create Win32 applications. To create Win-CE
+ applications (with FPC compiler)), use the separate
+ version of KOL specially designed for it.
+ INPACKAGE - version for Mirror Classes Library package (design-time
+ only). This option should be included only in MCK package
+ options and never in options of the KOL/MCK application.
+ PAS_VERSION - to use Pascal version of the code.
+ PARANOIA - to force short versions of asm instructions (for D5
+ and below, D6 and higher use those instructions always).
+ USE_CMOV - force using CMOV machine instruction in asm code (not
+ recommended, still on some machines your application
+ will not work).
+ SMALLEST_CODE - to create minimal code application (affected:
+ (o) SimpleGetCtlBrushHandle - returns solid silver brush
+ always;
+ (o) _NewWindowed
+ - only default system font used by default;
+ font of the parent control is not applied to its
+ children automatically (but see SMALLEST_CODE_PARENTFONT);
+ - fBrush always set to NIL by default (parent Brush
+ is not applied);
+ (o) WndProcDoEraseBkgnd
+ - child controls windows are not created in WM_ERASEBKGND
+ if were not created earlier (in most case, all OK
+ with this - controls are created BTW);
+ - SetBkColor, SetBkMode, SetBrushOrgEx are not
+ called (all OK therefore)
+ (o) by default, NOT_UNLOAD_RICHEDITLIB is defined if
+ UNLOAD_RICHEDITLIB is not defined in project options
+ (this minimizes finalization section).
+ (o) _NewControl
+ - BoundsRect initialized with a rectangle
+ (aParent.fMarginLeft, aParent.fMarginTop,
+ aParent.fMarginLeft+64, aParent.fMargin+64)
+ rather then with (aParent.fMargin+aParent.fMarginLeft,
+ aParent.fMargin+aParent.fMarginTop,
+ aParent.fMargin+aParent.fMarginLeft+64,
+ aParent.fMargin+aParent.fMarginTop+64).
+ In most cases this is enough.
+ (o) Int2Hex
+ there are no check for second perameter > 15
+ (o) .... other see in code
+ SMALLER_CODE - like smallest code, but fuctionality is the same.
+ The speed can be lower therefore.
+ SMALLEST_CODE_PARENTFONT - Parent font therefore is applied for child controls,
+ but initially only.
+ SPEED_FASTER - by default (but off when SMALLEST_CODE on) - sorting of
+ TStrList.AnsiSort and comparing using AnsiCompareStrA,
+ AnsiCompareStrNoCaseA is much faster (about 5-6 times).
+ Also, sorting of lists and strlists is redircted to
+ SortArray which is faster about 5-15% (vs SortData).
+ To turn off, add a symbol SPEED_NORMAL.
+ REGKEYGETSTREX_ALWAYS - If you use already RegKeyGetStrEx, add this option to
+ redirect RegKeyGetStr to it.
+ NOT_USE_KOLMATH - Only for _X_ (GTK + Linux): to prevent referencing
+ KOLmath in uses. This makes method TCanvas.Arc
+ unavailable, but the application become smaller.
+ NOT_USE_EXCEPTIONS - to prevent referencing unit ERR.PAS in uses even when
+ KOLmath is listed there.
+ REDEFINE_ABS - usual Abs works as a macro which is better in most
+ cases. But who knows...
+ CUSTOM_APPICON - when this option is defined, the resource name for the
+ application icon is extracted from a file
+ CusomAppIconRsrcName_PAS.inc (place it in your project
+ folder and type there name of the recource in qutations).
+ By default, string 'MAIN' is used like in usual Delphi
+ application.
+ USE_NAMES - to use property Name with any TObj. This makes also
+ available method TObj.FindObj( name ): PObj.
+ UNIQUE_NAMES - provide Name property to be unique among all siblings.
+ USE_MHTOOLTIP - to use KOLMHTOOLTIP.pas (actually it is not a separate
+ unit but a set of portions of code included into KOL.pas
+ in different places). This unit provides tooltips (hints)
+ for arbitrary controls which appear when mouse is over
+ such controls.
+ USE_GRUSH - to use ToGRush.pas unit, which provides automatic
+ redirection of the most cintrols creation functions
+ to the KOLGRushControls.pas.
+ (USE_CONSTRUCTORS - to use constructors like in VCL. Note: this option is
+ not carefully tested!)
+ TLIST_FAST - very fast implementation of TList (for coast of some
+ additional code).
+ DFLT_TLIST_NOUSE_BLOCKS - for TLIST_FAST: by default, do not make all TList
+ objects using new (fast) algoritms, but only those of
+ TList objects, which property UseBlocks was set to
+ TRUE after creating it.
+ STREAM_LARGE64 - turns on support of streams (and files) of size larger
+ then 4 Gbytes. Data type Int64 used for parameters of
+ the most of methods and functions in such case. (Note:
+ Int64 was introduced since Delphi5, so in earlier Delphi
+ versions using this symbol is not possible).
+ STREAM_COMPAT - still STREAM_LARGE64 appeared (in v2.84), most of
+ methods and functions declarations became incompatible
+ with earlier created extensions. This symbol provides
+ compatibility for such extensions, but it desables
+ using large streams.
+ OLD_STREAM_CAPACITY - to use elder TStream.SetCapacity algorithm (it did not
+ make Capacity smaller than already achieved, but in
+ newer version, Capacity can be set to a smaller value,
+ and for memory streams, rest of memory is freeing in
+ such case).
+ OLD_MEMSTREAMS_SETSIZE - to use elder TStream.SetSize for memory streams. In
+ a new version, setting new size also changes Capacity
+ to the same value (in earlier case, a value for
+ Capacity property was calculated to become a bit
+ greater then a value set for Size property).
+ OLD_COMPAT - to use symbol ';' as a file list separator (all operations
+ using DoFileOp function such as DeleteFile2Recycle and
+ CopyMoveFiles).
+ OLD_REGKEYGETSUBKEYS - to use elder version of RegKeyGetSubKeys functions
+ (new version is faster).
+ OLD_REGKEYGETVALUENAMES - to use elder version of RegKeyGetValueNames
+ (newer version is faster).
+ USE_CUSTOMEXTENSIONS - to extend TControl with custom additions.
+ DATE0_0001 - to correct correctly TDateTime to TSystemTime and vice
+ versa even for dates earlier then 1-Jan-1601.
+ UNICODE_CTRLS - to use Unicode versions of controls (WM_XXXXW messages,
+ etc.)
+ SAFE_CODE - use more safe code in some algorithms (but more slowly
+ and taking more code a bit).
+ USE_OnIdle - to use OnIdle event
+ SNAPMOUSE2DFLTBTN - for all MessageBox-based functions, snap mouse to
+ default button is provided if such option is on in
+ mouse driver settings.
+ BUTTON_DBLCLICK - to prevent clicking buttons with double click (separate
+ event OnMouseDblClk is fired in such case), this takes
+ smaller code but buttons can not be pressed with mouse
+ fast. When SMALLEST_CODE on, this option also is on.
+ ALL_BUTTONS_RESPOND_TO_ENTER - obvious (by default, buttons respond to key
+ SPACE, since those are working this way in Windows).
+ CLICK_DEFAULT_CANCEL_BTN_DIRECTLY - to prevent visual effect of default/cancel
+ button pressing with Enter/Escape keys. Also, button
+ don't become focused in such case.
+ DEFAULT_CANCEL_BTN_EXCLUSIVE - to disable assigning to a button properties
+ DefaultBtn and CancelBtn simultaneously.
+ NO_DEFAULT_BUTTON_BOLD - to prevent DefaultBtn to be visually with
+ a bold border.
+ BITBTN_DISABLEDGLYPH2 - to restore old behaviour of multi-glyph bitbtn, when
+ index 2 was used to represent the button in disabled
+ state, and glyph with index 1 was used forpressed dtate.
+ Now by default index 1 corresponds to the disabled state,
+ and index 2 to the pressed state, i.e. these are swapped.
+ ESC_CLOSE_DIALOGS - to allow closing all dialogs with ESCAPE.
+ KEY_PREVIEW - form also receive WM_KEYDOWN (OnKeyDown event fired)
+ SUPPORT_ONDEADCHAR - to support OnKeyDeadChar event in responce to
+ WM_DEADCHAR, WM_SYSDEADCHAR
+ OpenSaveDialog_Extended - to allow using custom extensions for OpenSaveDialog.
+ AUTO_CONTEXT_HELP - to use automatic respond to WM_CONTEXTMENU to call
+ context help.
+ NOT_FIX_CURINDEX - to use old version of TControl.SetItems, which could
+ lead to loose CurIndex value (e.g. for Combobox)
+ NOT_FIX_MODAL - not to fix modal (if fixed, click on any window
+ activates the application. If not fixed, code is
+ smaller very a little, but only click on modal form
+ activates the application). This does not fix calling
+ MsgBox though.
+ MODAL_ACTIVATE_FIX - if this option is set, all the windows of clicked app
+ with active modal form are brought to foreground, not
+ only modal form itself. This option is not necessary if
+ only two forms are visible at a time (the main form and
+ the active modal form).
+ NEW_MODAL - to use extended modalness.
+ USE_SETMODALRESULT - to guarantee ModalResult property assigning handling.
+ USE_SHOWMODALPARENTED_ALWAYS - to use TControl.ShowModalParented( Applet )
+ instead of TControl.ShowModal always.
+ USE_MENU_CURCTL - to use CurCtl property in popup menu to detect which
+ control initiated a pop-up.
+ NEW_MENU_ACCELL - to use new menu accelerators handling, without
+ AcceleratorTable (not tested for all cases)
+ USE_DROPDOWNCOUNT - to force setting combobox dropdown count.
+ NOT_UNLOAD_RICHEDITLIB - to stop unload Rich Edit library in finalization
+ section (to economy several byte of code).
+ NOT_USE_RICHEDIT - not use richedit (it will not be possible to create richedit)
+ TV_DRAG_RBUTTON - to allow dragging tree view items with right mouse
+ button too.
+ TOOLBAR_FORCE_CHILDALIGN - this option provides Align working for child
+ controls of the toolbar control, but when with this option
+ is turned on it is impossible to have neighbour controls
+ on a form correctly aligned. This last disadvantage is
+ not important if a toolbar is always placed on a separate
+ panel-like control as a child.
+ Note: this option has no effect for Win9x, still use of
+ it under Win9x can crash the application!!!
+ TOOLBAR_DOT_NOAUTOSIZE_BUTTON - this option forces prefix dot character in
+ button caption to be treated as an instruction to
+ remove TBSTYLE_AUTOSIZE from the button style. Actually,
+ this feature not necessary still custom button size can
+ be set even if such style is on for a button.
+ CANRESIZE_THICKFRAME - to use elder version of CanResize, changing border
+ style of the window (this cause incorrect form view in
+ Vista Aero theme (due a bug in Vista?)).
+ ANCHORS_WM_SIZE - to check WM_SIZE message in Anchor handling window
+ procedure. By default, now used WM_WINDOWPOSCHANGED.
+ USE_PROP - to use GetProp / SetProp (old style) in place of
+ Get / SetWindowLong( wnd, GWL_USERDATA... ) (slower?)
+
+ PROVIDE_EXITCODE - PostQuitMessage( value ) assigns value to ExitCode
+ INITIALFORMSIZE_FIXMENU - form size initially is really the same as defined at
+ design time even for forms having main menu bar
+ USE_GRAPHCTLS - to use graphic (non-windowed) controls
+ RICHEDIT_XPBORDER - provide correct drawing rich edit control border with
+ XP themes.
+ GRAPHCTL_XPSTYLES - to use XP themed Visual styles for drawing graphic
+ controls. This does not affect windowed controls
+ which visual style is controlled by the manifest.
+ This option also turns on RICHEDIT_XPBORDER option.
+ GRAPHCTL_HOTTRACK - to use hot-tracking also together with XP themed
+ graphic controls (otherwise only static XP themed
+ view is provided). Also, turn this option on if you
+ want to handle OnMouseEnter and OnMouseLeabe events
+ for graphic controls.
+ NEW_OPEN_DIR_STYLE_EX - to use new code for TOpenDirDialog, which provides
+ correct working of the dialog with an option
+ odNewDialogStyle set (even in Windows 9x system).
+ HTMLHELP_NOTOP - when Html help is called, its window become a child of
+ the desktop, not application (in such case it is not
+ closed together with the application, and it is apper
+ not on top of the application).
+ ICON_DIFF_WH - to support icons having Width <> Height
+ ICONLOAD_PRESERVEBMPS - when an icon is loaded, its bitmap and mask are
+ extracted and in case when such symbol is defined,
+ these one or two bitmaps are preserved until TIcon
+ object is destroyed.
+ LOADEX - to use TBitmap.LoadFromStreamEx while loading icon
+ from a stream or a file.
+ USE_OLDCONVERT2MASK - to use elder Convert2Mask method (newer is more correct).
+ FIX_TRANSPBMPPALETTE - for TBitmap.StretchDrawMasked, bitmaps with PixelFormat
+ = pf4bit or pf8bit are first converted (in a temporary
+ TBitmap object) to pf32bit, and then are drawn. This
+ fixes problems with palette usage for such DIB bitmaps.
+ FILL_BROKEN_BITMAP - TBitmap.LoadFromStreamEx: broken bitmaps rest of
+ scanlines are be filled with zeroes (usually black color)
+ rather then left containing trash memory bits.
+ AUTO_REPLACE_CLEARTYPE- to replace automatically CLEARTYPE_QUALITY fonts
+ with ANTIALIASED_QUALITY when running under elder
+ Windows version than XP.
+ FORCE_ALTERNATEFILENAME- TDirList.ScanDirectoryFORCE_ALTERNATEFILENAME - forced
+ using an alternate file path and filename for unicode
+ paths (ïðèíóäèòåëüíîå èñïîëüçîâàíèå àëüòåðíàòèâíîãî èìåíè
+ ïóòè è èìåíè ôàéëà äëÿ þíèêîä ïóòåé)
+
+ NEW_GRADIENT - to use new gradient painting by homm (fast).
+ OLD_ALIGN - to prevent using new Align by Galkov.
+ NEW_ALIGN - (default) - to use new Align implementation (faster).
+ OLD_TRANSPARENT - to prevent using NEW_TRANSPARENT
+ NEW_TRANSPARENT - created by Alexander Karpinsky a.k.a. homm (faster)
+ SBOX_OLDPOS - to use elder formulas to calculate scroll box positions
+ (just for compatibility with very old apps using it).
+ OLD_REFCOUNT - to prevent using new RefInc / RefDec behaviour
+ (new style of using RefCount works better).
+ OLD_FREE - to declare Free as a method as in earlier versions of KOL.
+ In new versions, Free is declared as a property, and
+ "calling" it just redirects call to RefDec. OLD_FREE
+ can be used for compatibility with compilers not
+ understanding "calling" a property without assigning
+ something to or from it (Turbo Delphi?).
+ SCROLL_OLD - for compatibility with the old applications using
+ TScrollBar: there was another method of adjusting
+ SBMax and SBPageSize: SBMax should be corrected to
+ (nMaxItems-1-SBPageSize).
+ FILE_EXISTS_EX - to use more correct (but a bit large code in FileExists functon)
+ USE_AUTOFREE4CONTROLS - (default) - from 2.40, most of control sub-objects are
+ destroying using Add2AutoFree (smaller code).
+ NOT_USE_AUTOFREE4CONTROLS - this option returns to previous behaviour (just to
+ compare code size). Will be deprecated in future.
+ Ignored when UNION_FIELDS is used (by default)
+ ENDSESSION_HALT - to halt the process when WM_ENDSESSION comes.
+ FILESTREAM_POSITION - in PAS_VERSION, Stream..fData.fPosition always show
+ current position (for debug purposes)
+ PSEUDO_THREADS - to use pseudo-threads instead of normal threads.
+ WAIT_SLEEP - for PSEUDO_THREADS: sleep 10 ms in a
+ WaitForMultipleObjects loop.
+ ENUM_DYN_HANDLERS_AFTER_RUN - to allow all the events handling even when
+ AppletTerminated become TRUE.
+ STOP_WNDPROCTRANSPARENT_AFTER_APPLETTERMINATED - use this long-named otpion to
+ prevent any functionality of WndProcTransparent after
+ AppletTerminated is set to true.
+ STOPTIMER_AFTER_APPLETTERMINATED - use this symbol to prevent timer event
+ firing after setting AppletTerminated to TRUE.
+ TIMER_APPLETWND - to use Applet window to handle WM_TIMER events
+ (otherwise special single invisible window is created
+ to handle such events).
+ SUPPORT_LONG_TIMER - LINUX only: set this option if TTimer.Interval can be
+ set to a value greater then 1,800,000 (30 minutes).
+ DEBUG_MENU - to debug menu.
+ DEBUG_GDIOBJECTS - to allow counting all the GDI objects used.
+ CHK_BITBLT - to check BitBlt operations.
+ DEBUG_ENDSESSION - to allow debugging WM_ENDSESSION handling.
+ DEBUG_CREATEWINDOW - to debug CreateWindow.
+ CRASH_DEBUG - to fill object memory with $DD before freeing it
+ (program really crashes when the object is
+ attempted to destroy more then once and in most
+ cases when a destroyed object is accessed after the
+ destruction).
+ DEBUG_MCK - specially designed to debug Mirror Classes Kit.
+ DEBUG_OBJKIND - for each TControl object kind a reference to PChar
+ with object kind name is stored in the structure of
+ the object (field fObjKind).
+ DEBUG - other debugging.
+ EXTERNAL_DEFINES - if count of options necessary to set is very large
+ Delphi ignores past of those. To avoid this problem,
+ set only this option in Project's options, and place
+ all other options to ExternalDefines.inc file as a
+ sequence of {$DEFINE ... directives.
+ But note, such file should be located in a
+ project directory, but not in the directory where KOL.pas
+ is located. This is enough to provide different sets
+ of defines for each project.
+ ---- from version 3.00, following symbols are added:
+ USE_FLAGS - to compress boolean flags used (about 6 bytes instead
+ more then 50 flags occupying earlies 1 byte for each
+ flag). This option is turned on by default. To turn off,
+ define a symbol USE_OLD_FLAGS !
+ EVENTS_DYNAMIC - to create events record (about 600 bytes) only for
+ controls having assigned events. To turn off, define a
+ symbol EVENTS_STATIC.
+ NIL_EVENTS - by default, is off. This option returns back again checking
+ TControl's events if it is assigned before calling. By
+ default, now this option is off, all events are assigned
+ to dummy event handlers at create, so checking if the handler
+ is assigned is not necessary. But it is not allowed to
+ assign NIL to the event, instead call ResetEvent method
+ with the correspondent index (e.g. idx_fOnMessage).
+ COMMANDACTIONS_OBJ - to store command actions certain for different control
+ kinds in shared objects, separately from TControl object
+ instances. To turn off, define a symbol COMMANDACTIONS_RECORD.
+ PACK_COMMANDACTIONS - this option must be defined together with COMMANDACTIONS_OBJ
+ and must not with COMMANDACTIONS_RECORD (just do nothing
+ and this is applied automatically).
+ |</pre>
+}
+{= K.O.L - êëþ÷åâàÿ áèáëèîòåêà îáúåêòîâ. (C) Êëàäîâ Âëàäèìèð, 2000-2007.
+}
+
+{$A-} // align off, otherwise code is not good
+
+{$Q-} // no overflow check: this option makes code wrong
+{$R-} // no range checking: this option makes code wrong
+{$T-} // not typed @-operator
+//{$D+}
+//______________________________________________________________________________
+//
+//{$DEFINE INPACKAGE} // Uncomment this line while rebuild MCK package
+// for Delphi3 only, then restore the comment mark!!!!!!!!!!!!!!!!!!!!
+//______________________________________________________________________________
+
+{$IFDEF INPACKAGE} // use this symbol in packages requiring kol.pas
+ {$WARNINGS OFF}
+ //{$DEFINE NOT_USE_AUTOFREE4CONTROLS}
+ {$DEFINE PAS_VERSION}
+ {$UNDEF ASM_VERSION}
+ {$UNDEF ASM_UNICODE}
+ {$IFDEF _D2009orHigher}
+ {$DEFINE UNICODE_CTRLS}
+ {$ENDIF}
+{$ENDIF}
+{$IFDEF _D7orHigher}
+ {$WARN UNSAFE_TYPE OFF} // Too many such warnings in Delphi7
+ {$WARN UNSAFE_CODE OFF}
+ {$WARN UNSAFE_CAST OFF}
+{$ENDIF}
+
+interface
+
+{$IFnDEF CREATE_VISIBLE}
+ {$DEFINE CREATE_HIDDEN}
+{$ENDIF}
+
+{$IFDEF NEW_ALIGN}
+ {$UNDEF OLD_ALIGN}
+{$ELSE}
+ {$IFNDEF OLD_ALIGN}
+ {$DEFINE NEW_ALIGN}
+ {$ENDIF}
+{$ENDIF}
+
+{$IFDEF OLD_ALIGN}
+ {$UNDEF NEW_ALIGN}
+{$ELSE}
+ {$IFNDEF NEW_ALIGN}
+ {$DEFINE NEW_ALIGN}
+ {$ENDIF}
+{$ENDIF}
+
+{$IFNDEF OLD_TRANSPARENT}
+ {$DEFINE NEW_TRANSPARENT}
+{$ENDIF}
+
+{$IFNDEF NOT_UNION_FIELDS}
+ {$DEFINE UNION_FIELDS}
+{$ENDIF}
+
+{$IFDEF UNION_FIELDS}
+ {$UNDEF NOT_USE_AUTOFREE4CONTROLS}
+{$ENDIF}
+
+{$IFNDEF NOT_USE_AUTOFREE4CONTROLS}
+ {$DEFINE USE_AUTOFREE4CONTROLS}
+ {$DEFINE USE_AUTOFREE4CHILDREN}
+{$ENDIF}
+
+{$IFDEF SMALLEST_CODE}
+ {$DEFINE NOT_UNLOAD_RICHEDITLIB}
+ {$DEFINE SMALLER_CODE}
+ {$DEFINE CREATE_VISIBLE}
+{$ELSE}
+ {$IFnDEF SPEED_NORMAL}
+ {$DEFINE SPEED_FASTER}
+ {$ENDIF}
+{$ENDIF}
+{$IFDEF _D2}
+ {$UNDEF SPEED_FASTER}
+{$ENDIF}
+
+{$IFDEF SAFE_CODE}
+ {$UNDEF NO_SAFE_CODE}
+{$ENDIF}
+{$IFDEF NO_SAFE_CODE}
+ {$UNDEF SAFE_CODE}
+{$ENDIF}
+{$IFnDEF NO_SAFE_CODE}
+{$IFnDEF SMALLER_CODE}
+ {$DEFINE SAFE_CODE}
+{$ENDIF}
+{$ENDIF}
+
+{$IFDEF NOT_USE_RICHEDIT}
+ {$DEFINE NOT_UNLOAD_RICHEDITLIB}
+{$ENDIF}
+
+//{$DEFINE DEBUG_GDIOBJECTS}
+//{$DEFINE CHK_GDI}
+
+uses {$IFDEF WIN}messages, windows {$IFNDEF NOT_USE_RICHEDIT}, RichEdit {$ENDIF}{$ENDIF WIN}
+ {$IFDEF LIN}, Libc, Xlib{$ENDIF}
+ {$IFDEF GTK}, Glib2 , Gdk2, Gtk2, pango {$ENDIF GTK}
+ {$IFDEF CHK_GDI}, ChkGdi {$ENDIF};
+
+{$IFDEF LIN}
+ {$DEFINE global_declare} {$I KOL_Linux.inc} {$UNDEF global_declare}
+////type HDC = TGC; // from Xlib (temporary definition?)
+{$ENDIF LIN}
+
+var
+ AppTheming: Boolean;
+{$IFDEF DEBUG_GDIOBJECTS}
+var
+ BrushCount: Integer;
+ FontCount: Integer;
+ PenCount: Integer;
+{$ENDIF}
+
+{$IFDEF _D2009orHigher}
+type KOLWideString = UnicodeString;
+{$ELSE}
+{$IFDEF _D3orHigher}
+type KOLWideString = WideString;
+{$ENDIF}
+{$ENDIF}
+
+{$IFDEF UNICODE_CTRLS}
+ {$IFDEF _D2}
+ {$ERROR 'Delphi 2 cannot compile with UNICODE_CTRLS defined!'}
+ {$ENDIF}
+const
+ SizeOfKOLChar = SizeOf(WideChar);
+
+ type
+ KOLString = KOLWideString;
+ KOL_String = type KOLWideString;
+ KOLChar = type WideChar;
+ PKOLChar = PWideChar;
+ PKOL_Char = type PWideChar;
+{$ELSE}
+const
+ SizeOfKOLChar = SizeOf(AnsiChar);
+
+ type
+ KOLString = AnsiString;
+ KOL_String = type AnsiString;
+ KOLChar = type AnsiChar;
+ PKOLChar = PAnsiChar;
+ PKOL_Char = type PAnsiChar;
+ {$IFDEF ASM_VERSION}
+ {$IFNDEF ASM_NOUNICODE}
+ {$DEFINE ASM_UNICODE}
+ {$ENDIF}
+ {$UNDEF PAS_VERSION}
+ {$ENDIF}
+{$ENDIF}
+
+{$IFNDEF ASM_VERSION}
+ {$DEFINE PAS_VERSION}
+{$ENDIF ASM_VERSION}
+
+{$IFDEF PAS_VERSION}
+ {$UNDEF ASM_VERSION}
+ {$UNDEF ASM_UNICODE}
+ {$UNDEF ASM_TLIST}
+{$ENDIF}
+
+{BCB++}(*type DWORD = Windows.DWORD;*){--BCB}
+
+{$IFDEF WIN}
+//{_#IF [DELPHI]}
+ {$INCLUDE delphicommctrl.inc}
+ {$IFNDEF FPC}
+ {$IFDEF UNICODE_CTRLS}
+ {$DEFINE interface_part} {$I KOL_unicode.inc} {$UNDEF interface_part}
+ {$ELSE} // ANSI_CTRLS
+ {$DEFINE interface_part} {$I KOL_ansi.inc} {$UNDEF interface_part}
+ {$ENDIF UNICODE_CTRLS}
+ {$ENDIF}
+//{_#ENDIF}
+{$ENDIF WIN}
+
+type
+ _TObj = object
+ {* auxiliary object type. See TObj. }
+ protected
+ procedure Init; virtual;
+ {* Is called from a constructor to initialize created object instance
+ filling its fields with 0. Can be overriden in descendant objects
+ to add another initialization code there. (Main reason of intending
+ is what constructors can not be virtual in poor objects). }
+ {= Âûçûâàåòñÿ äëÿ èíèöèàëèçàöèè îáúåêòà. }
+ public
+ function VmtAddr: Pointer;
+ {* Returns addres of virtual methods table of object. ? }
+ {= âîçâðàùàåò àäðåñ òàáëèöû âèðòóàëüíûõ ìåòîäîâ (VMT). ? }
+ end;
+
+ PObj = ^TObj;
+ {* }
+
+ PList = ^TList;
+ {* }
+
+ PPointerList = ^TPointerList;
+ TPointerList = array[0..{$IFDEF _DXE2orHigher} 65536
+ {$ELSE} MaxInt div 4 - 1 {$ENDIF}] of Pointer;
+
+ TObjectMethod = procedure of object;
+ {* }
+ TOnEvent = procedure( Sender: PObj ) of object;
+ {* This type of event is the most common - event handler when called can
+ know only what object was a sender of this call. Replaces good known
+ VCL TNotifyEvent event type. }
+
+ TOnEventMoving = procedure( Sender: PObj; P: PRect ) of object;
+
+{ ---------------------------------------------------------------------
+ TObj - base object to derive all others
+---------------------------------------------------------------------- }
+//[TObj DEFINITION]
+ TObj = object( _TObj )
+ {* Prototype for all objects of KOL. All its methods are important to
+ implement objects in a manner similar to Delphi TObject class. }
+ {= Áàçîâûé êëàññ äëÿ âñåõ ïðî÷èõ îáúåêòîâ KOL. }
+ protected
+ {$IFDEF DEBUG_OBJKIND}
+ fObjKind: PChar;
+ {$ENDIF}
+ fRefCount: Integer;
+ fOnDestroy: TOnEvent;
+ {$IFDEF OLD_REFCOUNT}
+ procedure DoDestroy;
+ {$ENDIF}
+ protected
+ fAutoFree: PList;
+ {* Is called from a constructor to initialize created object instance
+ filling its fields with 0. Can be overriden in descendant objects
+ to add another initialization code there. (Main reason of intending
+ is what constructors can not be virtual in poor objects). }
+ {= Âûçûâàåòñÿ äëÿ èíèöèàëèçàöèè îáúåêòà. }
+ fTag: DWORD;
+ {* Custom data. }
+ public
+ destructor Destroy; virtual;
+ {* Disposes memory, allocated to an object. Does not release huge strings,
+ dynamic arrays and so on. Such memory should be freeing in overriden
+ destructor. }
+ {= Îñâîáîæäàåò ïàìÿòü, âûäåëåííóþ äëÿ îáúåêòà. Íå îñâîáîæäàåò ïàìÿòü, âûäåëåííóþ
+ äëÿ ñòðîê, äèíàìè÷èñêèõ ìàññèâîâ è ò.ï. Òàêàÿ ïàìÿòü äîëæíà áûòü îñâîáîæäåíà
+ â ïåðåîïðåäåëåííîì äåñòðóêòîðå îáúåêòà. }
+ {$IFnDEF NIL_EVENTS}
+ //procedure Init; virtual;
+ {* Can be overriden in descendant objects
+ to add initialization code there. (Main reason of intending
+ is what constructors can not be virtual in poor objects). }
+ {$ENDIF NIL_EVENTS}
+ procedure Final;
+ {* It is called in destructor to perform OnDestroy event call and to
+ released objects, added to fAutoFree list. }
+ public
+ procedure RefInc;
+ {* See comments below. }
+ {= Ñì. RefDec íèæå. }
+ function RefDec: Integer;
+ {* Decrements reference count. If it is becoming <0, and Free
+ method was already called, object is (self-) destroyed. Otherwise,
+ Free method does not destroy object, but only sets flag
+ "Free was called".
+ |<br>
+ Use RefInc..RefDec to provide a block of code, where
+ object can not be destroyed by call of Free method.
+ This makes code more safe from intersecting flows of processing,
+ where some code want to destroy object, but others suppose that it
+ is yet existing.
+ |<br>
+ If You want to release object at the end of block RefInc..RefDec,
+ do it immediately BEFORE call of last RefDec (to avoid situation,
+ when object is released in result of RefDec, and attempt to
+ destroy it follow leads to AV exception).
+ |<br>
+ Actually, this "function" is a procedure and does not return
+ any sensible value. It is declared as a function for internal
+ needs (to avoid creating separate code for Free method)
+ }
+ {= Óìåíüøàåò ñ÷åò÷èê èñïîëüçîâàíèÿ. Åñëè â ðåçóëüòàòå ñ÷åò÷èê ñòàíîâèòñÿ
+ < 0, è ìåòîä Free óæå áûë âûçâàí, îáúåêò (ñàìî-) ðàçðóøàåòñÿ. Èíà÷å,
+ ìåòîä Free íå ðàçðóøàåò îáúåêò, à òîëüêî óñòàíàâëèâàåò ôëàã "Free áûë
+ âûçâàí".
+ |<br>
+ Èñïîëüçóéòå RefInc..RefDec äëÿ ïðåäîòâðàùåíèÿ ðàçðóøåíèÿ îáúåêòà íà
+ íåêîòîðîì ó÷àñòêå êîäà (åñëè åñòü òàêàÿ íåîáõîäèìîñòü).
+ |<br>
+ Åñëè íóæíî óáèòü (âðåìåííûé) îáúåêò âìåñòå ñ ïîñëåäíèì RefDec, ñäåëàéòå
+ âûçîâ Free íåìåäëåííî ÏÅÐÅÄ ïîñëåäíèì RefDec. }
+ property RefCount: Integer read fRefCount;
+ {* }
+ {$IFDEF OLD_FREE}
+ procedure Free;
+ {$ELSE NEW_FREE}
+ property Free: Integer read RefDec;
+ {* Before calling destructor of object, checks if passed pointer is not
+ nil - similar what is done in VCL for TObject. It is ALWAYS recommended
+ to use Free instead of Destroy - see also comments to RefInc, RefDec. }
+ {= Äî âûçîâà äåñòðóêòîðà, ïðîâåðÿåò, íå ïåðåäàí ëè nil â êà÷åñòâå ïàðàìåòðà.
+ ÂÑÅÃÄÀ ðåêîìåíäóåòñÿ èñïîëüçîâàòü Free âìåñòî Destroy - ñì. òàê æå RefInc,
+ RefDec. }
+ {$ENDIF NEW_FREE}
+
+ // By Vyacheslav Gavrik:
+ function InstanceSize: Integer;
+ {* Returns a size of object instance. }
+
+ constructor Create;
+ {* Constructor. Do not call it. Instead, use New<objectname> function
+ call for certain object, e.g., NewLabel( AParent, 'caption' ); }
+ {= Êîíñòðóêòîð. Íå ñëåäóåò âûçûâàòü åãî. Äëÿ êîíñòðóèðîâàíèÿ îáúåêòîâ,
+ âûçûâàéòå ñîîòâåòñòâóþùóþ ãëîáàëüíóþ ôóíêöèþ New<èìÿ-îáúåêòà>. Íàïðèìåð,
+ NewLabel( MyForm, 'Ìåòêà¹1' ); }
+ class function AncestorOfObject( Obj: Pointer ): Boolean;
+ {* Is intended to replace 'is' operator, which is not applicable to objects. }
+ function VmtAddr: Pointer;
+ {* Returns addres of virtual methods table of object. }
+ {= âîçâðàùàåò àëðåñ òàáëèöû âèðòóàëüíûõ ìåòîäîâ (VMT). }
+ property OnDestroy: TOnEvent read fOnDestroy write fOnDestroy;
+ {* This event is provided for any KOL object, so You can provide your own
+ OnDestroy event for it. }
+ {= Äàííîå ñîáûòèå îáåñïå÷èâàåòñÿ äëÿ âñåõ îáúåêòîâ KOL. Ïîçâîëÿåò ñäåëàòü
+ ÷òî-íèáóäü â ñâÿçè ñ ðàçðóøåíèåì îáúåêòà. }
+ procedure Add2AutoFree( Obj: PObj );
+ {* Adds an object to the list of objects, destroyed automatically
+ when the object is destroyed. Do not add here child controls of
+ the TControl (these are destroyed by another way). Only non-control
+ objects, which are not destroyed automatically, should be added here. }
+ procedure Add2AutoFreeEx( Proc: TObjectMethod );
+ {* Adds an event handler to the list of events, called in destructor.
+ This method is mainly for internal use, and allows to auto-destroy
+ VCL components, located on KOL form at design time (in MCK project). }
+ procedure RemoveFromAutoFree( Obj: PObj );
+ {* Removes an object from auto-free list }
+ procedure RemoveFromAutoFreeEx( Proc: TObjectMethod );
+ {* Removes a procedure from auto-free list }
+ property Tag: DWORD read fTag write fTag;
+ {* Custom data field. }
+ protected
+ {$IFDEF USE_NAMES}
+ fName: AnsiString;
+ fNamedObjList: Plist;
+ fOwnerObj: PObj;
+ {$ENDIF}
+ public
+ {$IFDEF USE_NAMES}
+ procedure SetName( NewOwnerObj: PObj; NewName: AnsiString);
+ property Name: Ansistring read FName;
+
+ property NamedObjList : PList read fNamedObjList;
+ property OwnerObj: PObj read FOwnerObj;
+ function FindObj(const ObjName: Ansistring): PObj;
+ {$ENDIF}
+ end;
+
+{ ---------------------------------------------------------------------
+ TList - object to implement list of pointers (or dwords)
+---------------------------------------------------------------------- }
+ TList = object( TObj )
+ {* Simple list of pointers. It is used in KOL instead of standard VCL
+ TList to store any kind data (or pointers to these ones). Can be created
+ calling function NewList. }
+ {= Ïðîñòîé ñïèñîê óêàçàòåëåé. }
+ protected
+ fItems: PPointerList;
+ fCount: Integer;
+ fCapacity: Integer;
+ fAddBy: Integer;
+ procedure SetCount(const Value: Integer);
+ procedure SetAddBy(Value: Integer);
+ destructor Destroy; virtual;
+ {* Destroys list, freeing memory, allocated for pointers. Programmer
+ is resposible for destroying of data, referenced by the pointers. }
+ procedure SetCapacity( Value: Integer );
+ function Get( Idx: Integer ): Pointer;
+ procedure Put( Idx: Integer; Value: Pointer );
+ {$IFDEF USE_CONSTRUCTORS}
+ procedure Init; virtual;
+ {$ENDIF}
+ protected
+ {$IFDEF TLIST_FAST}
+ fBlockList: PList;
+ fLastKnownBlockIdx: Integer;
+ fLastKnownCountBefore: Integer;
+ fUseBlocks: Boolean;
+ fNotOptimized: Boolean;
+ {$ENDIF}
+ public
+ procedure Clear;
+ {* Makes Count equal to 0. Not responsible for freeing (or destroying)
+ data, referenced by released pointers. }
+ procedure Add( Value: Pointer );
+ {* Adds pointer to the end of list, increasing Count by one. }
+ procedure Insert( Idx: Integer; Value: Pointer );
+ {* Inserts pointer before given item. Returns Idx, i.e. index of
+ inserted item in the list. Indeces of items, located after insertion
+ point, are increasing. To add item to the end of list, pass Count
+ as index parameter. To insert item before first item, pass 0 there. }
+ function IndexOf( Value: Pointer ): Integer;
+ {* Searches first (from start) item pointer with given value and returns
+ its index (zero-based) if found. If not found, returns -1. }
+ procedure Delete( Idx: Integer );
+ {* Deletes given (by index) pointer item from the list, shifting all
+ follow item indeces up by one. }
+ procedure DeleteRange( Idx, Len: Integer );
+ {* Deletes Len items starting from Idx. }
+ procedure Remove( Value: Pointer );
+ {* Removes first entry of a Value in the list. }
+ property Count: Integer read fCount write SetCount;
+ {* Returns count of items in the list. It is possible to delete a number
+ of items at the end of the list, keeping only first Count items alive,
+ assigning new value to Count property (less then Count it is). }
+ property Capacity: Integer read fCapacity write SetCapacity;
+ {* Returns number of pointers which could be stored in the list
+ without reallocating of memory. It is possible change this value
+ for optimize usage of the list (for minimize number of reallocating
+ memory operations). }
+ property Items[ Idx: Integer ]: Pointer read Get write Put; default;
+ {* Provides access (read and write) to items of the list. Please note,
+ that TList is not responsible for freeing memory, referenced by stored
+ pointers. }
+ function Last: Pointer;
+ {* Returns the last item (or nil, if the list is empty). }
+ procedure Swap( Idx1, Idx2: Integer );
+ {* Swaps two items in list directly (fast, but without testing of
+ index bounds). }
+ procedure MoveItem( OldIdx, NewIdx: Integer );
+ {* Moves item to new position. Pass NewIdx >= Count to move item
+ after the last one. }
+ procedure Release;
+ {* Especially for lists of pointers to dynamically allocated memory.
+ Releases all pointed memory blocks and destroys object itself. }
+ procedure ReleaseObjects;
+ {* Especially for a list of objects derived from TObj.
+ Calls Free for every of the object in the list, and then calls
+ Free for the object itself. }
+ property AddBy: Integer read fAddBy write SetAddBy;
+ {* Value to increment capacity when new items are added or inserted
+ and capacity need to be increased. }
+ property DataMemory: PPointerList read fItems;
+ {* Raw data memory. Can be used for direct access to items of a list.
+ Do not use it for TLIST_FAST ! }
+ procedure Assign( SrcList: PList );
+ {* Copies all source list items. }
+ {$IFDEF _D4orHigher}
+ procedure AddItems( const AItems: array of Pointer );
+ {* Adds a list of items given by a dynamic array. }
+ {$ENDIF}
+ function ItemAddress( Idx: Integer ): Pointer;
+ {* Returns an address of memory occupying by the item with index Idx.
+ (If the item is a pointer, returned value is a pointer to a pointer).
+ Item with index requested must exist. }
+ {$IFDEF TLIST_FAST}
+ property UseBlocks: Boolean read fUseBlocks write fUseBlocks;
+ {$ENDIF}
+ procedure OptimizeForRead;
+ end;
+
+function NewList: PList;
+{* Returns pointer to newly created TList object. Use it instead usual
+ TList.Create as it is done in VCL or XCL. }
+
+{$IFDEF _D4orHigher}
+function NewListInit( const AItems: array of Pointer ): PList;
+{* Creates a list filling it initially with certain Items. }
+{$ENDIF}
+
+{$IFNDEF TLIST_FAST}
+{$IFNDEF PAS_ONLY}
+procedure FastIncNum2Elements( List: TList; FromIdx, Count, Value: Integer );
+{* Very fast adds Value to List elements from List[FromIdx] to List[FromIdx+Count-1].
+ Given elements must exist. Count must be > 0. }
+{$ENDIF}
+{$ENDIF}
+
+procedure Free_And_Nil( var Obj );
+{* Obj.Free and Obj := nil, where Obj *MUST* be TObj or its descendant
+ (TControl, TMenu, etc.) This procedure is not compatible with VCL's
+ FreeAndNil, which works with TObject, since this it has another name. }
+
+{$IFDEF WIN_GDI}
+{ ------------------------------- threads ------------------------------------ }
+
+const
+ ABOVE_NORMAL_PRIORITY_CLASS = $8000; // only for Windows 2K
+ BELOW_NORMAL_PRIORITY_CLASS = $4000; // and higher !
+
+type
+ PThread = ^TThread;
+
+ TThreadMethod = procedure of object;
+ TThreadMethodEx = procedure( Sender: PThread; Param: Pointer ) of object;
+
+ TOnThreadExecute = function(Sender: PThread): Integer of object;
+ {* Event to be called when Execute method is called for TThread }
+
+{ ---------------------------------------------------------------------
+ TThread object
+---------------------------------------------------------------------- }
+ TThread = object(TObj)
+ private
+ function GetPriorityBoost: Boolean;
+ procedure SetPriorityBoost(const Value: Boolean);
+ {* Thread object. It is possible not to derive Your own thread-based
+ object, but instead create thread Suspended and assign event
+ OnExecute. To create, use one of NewThread of NewThreadEx functions,
+ or derive Your own descendant object and write creation function
+ (or constructor) for it.
+ |<br><br>
+ Aknowledgements. Originally class ZThread was developed for XCL:
+ |<br> * By: Tim Slusher : junior@nlcomm.com
+ |<br> * Home: http://www.nlcomm.com/~junior
+ }
+ protected
+ FSuspended,
+ FTerminated: Boolean;
+ FHandle: THandle;
+ FThreadId: DWORD;
+ FOnSuspend: TObjectMethod;
+ FOnResume: TOnEvent;
+ FData : Pointer;
+ FOnExecute : TOnThreadExecute;
+ FMethod: TThreadMethod;
+ FMethodEx: TThreadMethodEx;
+ F_AutoFree: Boolean;
+ FPriority: Integer;
+ function GetPriorityCls: Integer;
+ function GetThrdPriority: Integer;
+ procedure SetPriorityCls(Value: Integer);
+ procedure SetThrdPriority(Value: Integer);
+ procedure Init; virtual;
+ destructor Destroy; virtual;
+ {* }
+ public
+ {$IFDEF PSEUDO_THREADS}
+ FPrtyCls: Integer;
+ DoNotWakeUntil: DWORD;
+ AllThreads: PList; // only for MainThread
+ CurrentThread: PThread;
+ StackBottom: Pointer; // except for MainThread
+ CurStackPos: Pointer;
+ Stack_Empty: Boolean;
+ procedure SwitchToThread( T: PThread ); // methods of MainThread
+ procedure NextThread;
+ {$ENDIF}
+ public
+ FResult: Integer;
+ function Execute: integer; virtual;
+ {* Executes thread. Do not call this method from another thread! (Even do
+ not call this method at all!) Instead, use Resume.
+ |<br>
+ Note also that in contrast to VCL, it is not necessary to create your
+ own descendant object from TThread and override Execute method. In KOL,
+ it is sufficient to create an instance of TThread object (see NewThread,
+ NewThreadEx, NewThreadAutoFree functions) and assign OnExecute event
+ handler for it. }
+ procedure Resume;
+ {* Continues executing. It is necessary to make call for every
+ nested Suspend. }
+ procedure Suspend;
+ {* Suspends thread until it will be resumed. Can be called from another
+ thread or from the thread itself. }
+ procedure Terminate;
+ {* Terminates thread. }
+ function WaitFor: Integer;
+ {* Waits (infinitively) until thead will be finished. }
+ function WaitForTime( T: DWORD ): Integer;
+ {* Waits (T milliseconds) until thead will be finished. }
+
+ property Handle: THandle read FHandle;
+ {* Thread handle. It is created immediately when object is created
+ (using NewThread). }
+ property Suspended: Boolean read FSuspended;
+ {* True, if suspended. }
+ property Terminated: Boolean read FTerminated;
+ {* True, if terminated. }
+ property ThreadId: DWORD read FThreadId;
+ {* Thread id. }
+ property PriorityClass: Integer read GetPriorityCls write SetPriorityCls;
+ {* Thread priority class. One of following values: HIGH_PRIORITY_CLASS,
+ IDLE_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS, REALTIME_PRIORITY_CLASS. }
+ property ThreadPriority: Integer read GetThrdPriority write SetThrdPriority;
+ {* Thread priority value. One of following values: THREAD_PRIORITY_ABOVE_NORMAL,
+ THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_HIGHEST, THREAD_PRIORITY_IDLE,
+ THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_TIME_CRITICAL. }
+ property Data : Pointer read FData write FData;
+ {* Custom data pointer. Use it for Youe own purpose. }
+
+ property OnExecute: TOnThreadExecute read FOnExecute write FOnExecute;
+ {* Is called, when Execute is starting. }
+ property OnSuspend: TObjectMethod read FOnSuspend write FOnSuspend;
+ {* Is called, when Suspend is performed. }
+ property OnResume: TOnEvent read FOnResume write FOnResume;
+ {* Is called, when resumed. }
+ procedure Synchronize( Method: TThreadMethod );
+ {* Call it to execute given method in main thread context. Applet variable
+ must exist for that time. }
+ procedure SynchronizeEx( Method: TThreadMethodEx; Param: Pointer );
+ {* Call it to execute given method in main thread context, with a given
+ parameter. Applet variable must exist for that time. Param must not be nil. }
+ {$IFDEF USE_CONSTRUCTORS}
+ constructor ThreadCreate;
+ constructor ThreadCreateEx( const Proc: TOnThreadExecute );
+ {$ENDIF USE_CONSTRUCTORS}
+
+ property AutoFree: Boolean read F_AutoFree write F_AutoFree;
+ {* Set this property to true to provide automatic destroying of thread
+ object when its executing is finished. }
+ property PriorityBoost: Boolean read GetPriorityBoost write SetPriorityBoost;
+ {* By default, priority boost is enabled for all threads. }
+ end;
+
+function NewThread: PThread;
+{* Creates thread object (always suspended). After creating, set event
+ OnExecute and perform Resume operation. }
+
+function NewThreadEx( const Proc: TOnThreadExecute ): PThread; stdcall;
+{* Creates thread object, assigns Proc to its OnExecute event and runs
+ it. }
+
+function NewThreadAutoFree( const Proc: TOnThreadExecute ): PThread;
+{* Creates thread object similar to NewThreadEx, but freeing automatically
+ when executing of such thread finished. Be sure that a thread is resumed
+ at least to provide its object keeper freeing. }
+
+{$IFDEF PSEUDO_THREADS}
+var MainThread: PThread;
+ PseudoThreadStackSize: DWORD = 1024 * 1024;
+ CreatingMainThread: Boolean;
+
+function WaitForSingleObject( hHandle: THandle; dwMilliseconds: DWORD ): DWORD; stdcall;
+function WaitForMultipleObjects( nCount: DWORD;
+ lpHandles: PHandle; fWaitAll: BOOL; dwMilliseconds: DWORD ): DWORD; stdcall;
+procedure Sleep( n: DWORD );
+{$ENDIF}
+
+{ ----------------------------------- streams -------------------------------- }
+
+{$ENDIF WIN_GDI}
+type
+ TMoveMethod = ( spBegin, spCurrent, spEnd );
+{$IFDEF WIN_GDI}
+type
+ {$IFDEF STREAM_LARGE64}
+ TStrmSize = Int64;
+ TStrmMove = Int64;
+ {$UNDEF ASM_STREAM}
+ {$UNDEF STREAM_COMPAT}
+ {$ELSE}
+ TStrmSize = DWORD;
+ TStrmMove = Integer;
+ {$IFDEF ASM_VERSION}
+ {$IFNDEF ASM_NOSTREAM}
+ {$DEFINE ASM_STREAM}
+ {$ENDIF}
+ {$ENDIF}
+ {$ENDIF}
+
+ PStream = ^TStream;
+
+ PStreamMethods = ^TStreamMethods;
+ TStreamMethods = Packed Record
+ fSeek: function( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} MoveTo: TStrmMove; MoveMethod: TMoveMethod ): TStrmSize;
+ fGetSiz: function( Strm: PStream ): TStrmSize;
+ fSetSiz: procedure( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Value: TStrmSize );
+ fRead: function( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+ fWrite: function( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+ fClose: procedure( Strm: PStream );
+ fCustom: Pointer;
+ fWait: procedure( Strm: PStream );
+ end;
+
+ TStreamData = Packed Record
+ fHandle: THandle;
+ fCapacity, fSize, fPosition: TStrmSize;
+ fThread: PThread;
+ CASE Integer OF
+ 2: (
+ fStream1,
+ fStream2: PStream;
+ );
+ 3: (
+ fBaseStream: PStream;
+ fFromPos: TStrmSize;
+ );
+ 4: (
+ fBlkSize: Integer;
+ fBlocks: PList;
+ fJustWrittenBlkAddress: Pointer;
+ );
+ end;
+
+{ ---------------------------------------------------------------------
+ TStream - streaming objects incapsulation
+---------------------------------------------------------------------- }
+ TStream = object(TObj)
+ {* Simple stream object. Can be opened for file, or as memory stream (see
+ NewReadFileStream, NewWriteFileStream, NewMemoryStream, etc.). And, another
+ type of streaming object can be derived (without inheriting new object
+ type, just by writing another New...Stream method, which calls
+ _NewStream and pass methods record to it). }
+ protected
+ fPMethods: PStreamMethods;
+ fMethods: TStreamMethods;
+ fMemory: Pointer;
+ fData: TStreamData;
+ fParam1, fParam2: TStrmMove; // parameters to use in thread
+ fOnChangePos: TOnEvent;
+ function GetCapacity: TStrmSize;
+ procedure SetCapacity(const Value: TStrmSize);
+ function DoAsyncRead( Sender: PThread ): Integer;
+ function DoAsyncWrite( Sender: PThread ): Integer;
+ function DoAsyncSeek( Sender: PThread ): Integer;
+ protected
+ function GetFileStreamHandle: THandle;
+ procedure SetPosition(const Value: TStrmSize);
+ function GetPosition: TStrmSize;
+ function GetSize: TStrmSize;
+ procedure SetSize(const NewSize: TStrmSize);
+ destructor Destroy; virtual;
+ public
+ function Read(var Buffer; const Count: TStrmSize): TStrmSize;
+ {* Reads Count bytes from a stream. Returns number of bytes read. }
+ function Seek({$IFNDEF STREAM_COMPAT} const {$ENDIF} MoveTo: TStrmMove; MoveMethod: TMoveMethod): TStrmSize;
+ {* Allows to change current position or to obtain it. Property
+ Position uses this method both for get and set position. }
+ function Write(var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize): TStrmSize;
+ {* Writes Count bytes from Buffer, starting from current position
+ in a stream. Returns how much bytes are written. }
+ function WriteVal( Value: DWORD; Count: DWORD ): DWORD;
+ {* Writes maximum 4 bytes of Value to a stream. Allows writing constants
+ easier than via Write. }
+ function WriteStr( S: AnsiString ): DWORD;
+ {* Writes string to the stream, not including ending #0. Exactly
+ Length( S ) characters are written. }
+ function WriteStrZ( S: AnsiString ): DWORD;
+ {* Writes string, adding #0. Number of bytes written is returned. }
+ {$IFDEF _D3orHigher}
+ function WriteWStrZ( S: KOLWideString ): DWORD;
+ {* Writes string, adding #0. Number of bytes written is returned. }
+ {$ENDIF}
+ function ReadStrZ: AnsiString;
+ {* Reads string, finished by #0. After reading, current position in
+ the stream is set to the byte, follows #0. }
+ {$IFDEF _D3orHigher}
+ function ReadWStrZ: KOLWideString;
+ {* Reads string, finished by #0. After reading, current position in
+ the stream is set to the byte, follows #0. }
+ {$ENDIF}
+ function ReadStr: AnsiString;
+ {* Reads string, finished by #13, #10 or #13#10 symbols. Terminating symbols
+ #13 and/or #10 are not added to the end of returned string though
+ stream positioned follow it. }
+ function ReadStrLen( Len: Integer ): AnsiString;
+ {* Reads string of the given length Len. }
+ function WriteStrEx(S: AnsiString): DWord;
+ {* Writes string S to stream, also saving its size for future use by
+ ReadStrEx* functions. Returns number of actually written characters. }
+ function ReadStrExVar(var S: AnsiString): DWord;
+ {* Reads string from stream and assigns it to S.
+ Returns number of actually read characters.
+ Note:
+ String must be written by using WriteStrEx function.
+ Return value is count of characters READ, not the length of string. }
+ function ReadStrEx: AnsiString;
+ {* Reads string from stream and returns it. }
+ function WriteStrPas( S: AnsiString ): DWORD;
+ {* Writes a string in Pascal short string format - 1 byte length, then string
+ itself without trailing #0 char. S parameter length should not exceed 255
+ chars, rest chars are truncated while writing. Total amount of bytes
+ written is returned. }
+ function ReadStrPas: AnsiString;
+ {* Reads 1 byte from a stream, then treat it as a length of following string
+ which is read and returned. A purpose of this function is reading strings
+ written using WriteStrPas. }
+ property Size: TStrmSize read GetSize write SetSize;
+ {* Returns stream size. For some custom streams, can be slow
+ operation, or even always return undefined value (-1 recommended). }
+ property Position: TStrmSize read GetPosition write SetPosition;
+ {* Current position. }
+
+ property Memory: Pointer read fMemory;
+ {* Only for memory stream. }
+ property Handle: THandle read GetFileStreamHandle;
+ {* Only for file stream. It is possible to check that Handle <>
+ INVALID_HANDLE_VALUE to ensure that file stream is created OK. }
+
+ //---------- for asynchronous operations (using thread - not tested):
+ procedure SeekAsync(MoveTo: TStrmMove; MoveMethod: TMoveMethod);
+ {* Changes current position asynchronously. To wait for finishing the
+ operation, use method Wait. }
+ procedure ReadAsync(var Buffer; Count: DWord);
+ {* Reads Count bytes from a stream asynchronously. To wait finishing the
+ operation, use method Wait. }
+ procedure WriteAsync(var Buffer; Count: DWord);
+ {* Writes Count bytes from Buffer, starting from current position
+ in a stream - asynchronously. To wait finishing the operation,
+ use method Wait. }
+ function Busy: Boolean;
+ {* Returns TRUE until finishing the last asynchronous operation
+ started by calling SeekAsync, ReadAsync, WriteAsync methods. }
+ procedure Wait;
+ {* Waits for finishing the last asynchronous operation. }
+
+ property Methods: PStreamMethods read fPMethods;
+ {* Pointer to TStreamMethods record. Useful to implement custom-defined
+ streams, which can access its fCustom field, or even to change
+ methods when necessary. }
+ property Data: TStreamData read fData;
+ {* Pointer to TStreamData record. Useful to implement custom-defined
+ streams, which can access Data fields directly when implemented. }
+
+ property Capacity: TStrmSize read GetCapacity write SetCapacity;
+ {* Amound of memory allocated for data (MemoryStream). }
+
+ procedure SaveToFile( const Filename: KOLString; const Start, CountSave: TStrmSize );
+ {* }
+
+ property OnChangePos: TOnEvent read fOnChangePos write fOnChangePos;
+ {* To allow using this event, create stream with special constructing
+ function like NewMemoryStreamWithEvent or NewReadFileStreamWithEvent,
+ or replace reading / writing methods to certain supporting OnChangePos
+ event. }
+ end;
+
+function _NewStream( const StreamMethods: TStreamMethods ): PStream;
+{* Use this method only to define your own stream type. See also declared
+ below (in KOL.pas) methods used to implement standard KOL streams. You can use it in
+ your code to create streams, which are partially based on standard
+ methods. }
+
+// Methods below are declared here to simplify creating your
+// own streams with some methods standard and some non-standard
+// together:
+function SeekFileStream( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} MoveTo: TStrmMove; MoveFrom: TMoveMethod ): TStrmSize;
+function GetSizeFileStream( Strm: PStream ): TStrmSize;
+function ReadFileStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+function ReadFileStreamWithEvent( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+var ReadFileStreamProc: function( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize
+ = ReadFileStream;
+
+function WriteFileStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+function WriteFileStreamWithEvent( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+function WriteFileStreamEOF( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+function WriteFileStreamEOFWithEvent( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+procedure CloseFileStream( Strm: PStream );
+function SeekMemStream( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} MoveTo: TStrmMove; MoveFrom: TMoveMethod ): TStrmSize;
+function SeekMemStreamWithEvent( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} MoveTo: TStrmMove; MoveFrom: TMoveMethod ): TStrmSize;
+function GetSizeMemStream( Strm: PStream ): TStrmSize;
+
+var CapacityMask: DWORD = $4000 - 1; // must be 2**n-1
+procedure SetSizeMemStream( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} NewSize: TStrmSize );
+function ReadMemStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+function ReadMemStreamWithEvent( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+function WriteMemStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+function WriteMemStreamWithEvent( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+procedure CloseMemStream( Strm: PStream );
+procedure SetSizeFileStream( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} NewSize: TStrmSize );
+
+function ReadMemBlkStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+function SeekMemBlkStream( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} MoveTo: TStrmMove; MoveFrom: TMoveMethod ): TStrmSize;
+function WriteMemBlkStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+procedure ResizeMemBlkStream( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} NewSize: TStrmSize );
+procedure FreeMemBlkStream( Strm: PStream );
+
+function SeekConcatStream( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} MoveTo: TStrmMove; MoveFrom: TMoveMethod ): TStrmSize;
+function GetSizeConcatStream( Strm: PStream ): TStrmSize;
+procedure SetSizeConcatStream( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} NewSize: TStrmSize );
+function ReadConcatStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+function WriteConcatStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+procedure CloseConcatStream( Strm: PStream );
+
+function SeekSubStream( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} MoveTo: TStrmMove; MoveFrom: TMoveMethod ): TStrmSize;
+function GetSizeSubStream( Strm: PStream ): TStrmSize;
+procedure SetSizeSubStream( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} NewSize: TStrmSize );
+function ReadSubStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+function WriteSubStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+procedure CloseSubStream( Strm: PStream );
+
+procedure DummyCloseStream( Strm: PStream );
+
+function DummyReadWrite( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+procedure DummySetSize( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Value: TStrmSize );
+procedure DummyStreamProc(Strm: PStream);
+
+function NewFileStream( const FileName: KOLString; Options: DWORD ): PStream;
+{* Creates file stream for read and write. Exact set of open attributes
+ should be passed through Options parameter (see FileCreate where those
+ flags are listed). }
+
+function NewFileStreamWithEvent( const FileName: KOLString; Options: DWORD ): PStream;
+{* Creates file stream for read and write. Exact set of open attributes
+ should be passed through Options parameter (see FileCreate where those
+ flags are listed). Also, resulting stream is supporting OnChangePos event. }
+
+function NewReadFileStream( const FileName: KOLString ): PStream;
+{* Creates file stream for read only. }
+
+function NewReadFileStreamWithEvent( const FileName: KOLString ): PStream;
+{* Creates file stream for read only, supporting OnChangePos event. }
+
+function NewWriteFileStream( const FileName: KOLString ): PStream;
+{* Creates file stream for write only. Truncating of file (if needed)
+ is provided automatically. }
+
+function NewWriteFileStreamWithEvent( const FileName: KOLString ): PStream;
+{* Creates file stream for write only. Truncating of file (if needed)
+ is provided automatically. Created stream supports OnChangePos event. }
+
+function NewReadWriteFileStream( const FileName: KOLString ): PStream;
+{* Creates stream for read and write file. To truncate file, if it is
+ necessary, change Size property. }
+
+{$IFDEF _D3orHigher}
+function NewReadFileStreamW( const FileName: KOLWideString ): PStream;
+{* Creates file stream for read only. }
+
+function NewWriteFileStreamW( const FileName: KOLWideString ): PStream;
+{* Creates file stream for write only. Truncating of file (if needed)
+ is provided automatically. }
+
+function NewReadWriteFileStreamW( const FileName: KOLWideString ): PStream;
+{* Creates stream for read and write file. To truncate file, if it is
+ necessary, change Size property. }
+{$ENDIF}
+
+function NewExFileStream( F: HFile ): PStream;
+{* Creates read only stream to read from opened file or pipe from the current
+ position.
+ When stream is destroyed, file handle still not closed (your code should do
+ this) and file position is not changed (after the last read operation). }
+
+function NewMemoryStream: PStream;
+{* Creates memory stream (read and write). }
+
+function NewMemoryStreamWithEvent: PStream;
+{* Creates memory stream (read and write). Created stream support OnChangePos
+ event. }
+
+function NewExMemoryStream( ExistingMem: Pointer; Size: DWORD ): PStream;
+{* Creates memory stream on base of existing memory. It is not possible
+ to write out of top bound given by Size (i.e. memory can not be resized,
+ or reallocated. When stream object is destroyed this memory is not freed. }
+
+function NewMemBlkStream( BlkSize: Integer ): PStream;
+{* Creates memory stream which consists from blocks of given size. Contrary to
+ a memory stream, contents of the blocks stream should not be accessed
+ directly via fMemory but therefore it is possible to access its parts by
+ portions written to blocks still those were written contigously. To do so,
+ get an address of just written portion for further usage via field
+ fJustWrittenBlkAddress. It is guarantee that blocks of memory allocated
+ during write process never are relocated until destruction the stream. }
+
+function NewMemBlkStream_WriteOnly( BlkSize: Integer ): PStream;
+{* Same as NewMemoryStream}
+
+function NewConcatStream( Stream1, Stream2: PStream ): PStream;
+{* Creates a stream which is a concatenation of two source stream. After
+ the call, both source streams are belonging to the resulting stream and these
+ will be destroyed together with the resulting stream. (So forget about it).
+
+ After the call, first stream will not be changed in size via methods of
+ concatenated stream (and it is not recommended to use further Stream1 and
+ Stream2 methods too). But Stream2 can still be increased, if it allows doing
+ so when some data are appended or Size of resulting stream is changed (but
+ not less then Stream1.Size).
+
+ Nature and physical location of Stream1 and Stream2 are not important and
+ can be absolutely different. But it is supposed that both streams are not
+ compressed and its Size is known always and Seek operation is valid.
+
+ This function accepts recursive (multi-level) usage: resulting concatenation
+ stream can be used as a left or right parameter to create another concatenation
+ stream later, so it is possible to build a tree of streams concatenated,
+ concatenating this way several different streams and use it as a single
+ data streaming object.
+}
+
+function NewSubStream( BaseStream: PStream; const FromPos, Size: TStrmSize ): PStream;
+{* Creates a stream which is a subpart of BaseStream passes, starting from
+ FromPos and with given Size. Like in function NewConcatStream, passes
+ BaseStream become owned by newly created sub-stream object, and will be
+ destroyed automatically together with a sub-stream.
+
+ If you want to provide more long life time for a base stream (e.g. if you
+ plan to use it after a sub-stream based on it is destroyed), use method
+ RefInc for base stream once to prevent it from destroying when the sub-stream
+ is destroyed.
+
+ Note: be careful and avoid direct calling methods and properties of the base
+ stream, while you have a sub-stream created on base it, since the sub-stream
+ actually redirects all the requests to the parent base stream.
+
+ Sub-stream accepts setting Size to greater value later, and if some data
+ are written to it, it is written actually to the base stream, and when it
+ is written beyond the end position, this will increase size of the base
+ stream too (and if it is a file stream, this also will increase size of the
+ file on which the base stream was created).
+
+ This function accepts recursive (multi-level) usage: it is possible to create
+ later another sub-stream on base of existing sub-stream, still it is actully
+ can be treated as usual stream.
+}
+
+function Stream2Stream( Dst, Src: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+{* Copies Count (or less, if the rest of Src is not sufficiently long)
+ bytes from Src to Dst, but with optimizing in cases, when Src or/and
+ Dst are memory streams (intermediate buffer is not allocated). }
+function Stream2StreamEx( Dst, Src: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+{* Copies Count bytes from Src to Dst, but without any optimization.
+ Unlike Stream2Stream function, it can be applied to very large streams.
+ See also Stream2StreamExBufSz. }
+function Stream2StreamExBufSz( Dst, Src: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize; BufSz: DWORD ): TStrmSize;
+{* Copies Count bytes from Src to Dst using buffer of given size, but without
+ other optimizations.
+ Unlike Stream2Stream function, it can be applied to very large streams }
+function Resource2Stream( DestStrm : PStream; Inst : HInst;
+ ResName : PKOLChar; ResType : PKOLChar ): Integer;
+{* Loads given resource to DestStrm. Useful for non-standard
+ resources to load it into memory (use memory stream for such
+ purpose). Use one of following resource types to pass as ResType:
+ |<pre>
+RT_ACCELERATOR Accelerator table
+RT_ANICURSOR Animated cursor
+RT_ANIICON Animated icon
+RT_BITMAP Bitmap resource
+RT_CURSOR Hardware-dependent cursor resource
+RT_DIALOG Dialog box
+RT_FONT Font resource
+RT_FONTDIR Font directory resource
+RT_GROUP_CURSOR Hardware-independent cursor resource
+RT_GROUP_ICON Hardware-independent icon resource
+RT_ICON Hardware-dependent icon resource
+RT_MENU Menu resource
+RT_MESSAGETABLE Message-table entry
+RT_RCDATA Application-defined resource (raw data)
+RT_STRING String-table entry
+RT_VERSION Version resource
+ |</pre>
+ |<br>For example:
+ !var MemStrm: PStream;
+ ! JpgObj: PJpeg;
+ !......
+ ! MemStrm := NewMemoryStream;
+ ! JpgObj := NewJpeg;
+ !......
+ ! Resource2Stream( MemStrm, hInstance, 'MYJPEG', RT_RCDATA );
+ ! MemStrm.Position := 0;
+ ! JpgObj.LoadFromStream( MemStrm );
+ ! MemStrm.Free;
+ !......
+ }
+{$ENDIF WIN_GDI}
+
+{ ------------------------- string list objects ------------------------------ }
+
+type
+ TCompareStrListFun = function( const S1, S2: PAnsiChar ): Integer;
+ TCompareEvent = function (const Data: Pointer; const e1,e2 : Dword) : Integer;
+ {* Event type to define comparison function between two elements of an array.
+ This event handler must return negative or positive value (correspondently
+ for cases e1<e2 and e2>e2), or 0 if items are equal. Items are enumerated
+ from 0 to uNElem. }
+ TSwapEvent = procedure (const Data : Pointer; const e1,e2 : Dword);
+ {* Event type to define swap procedure which is swapping two elements of the
+ sorting data. }
+ TCompareArrayEvent = function(e1,e2 : DWord) : Integer;
+ {* Event type to define comparison function between two elements of an array.
+ Like in TCompareEvent, but e1 and e2 are not indexes in the array but items
+ itselves. }
+
+ PStrList = ^TStrList;
+{ ---------------------------------------------------------------------
+ TStrList - string list
+---------------------------------------------------------------------- }
+ TStrList = object(TObj)
+ {* Easy string list implementation (non-visual, just to store
+ string data). It is well improved and has very high performance
+ allowing to work fast with huge text files (more then megabyte
+ of text data).
+ |
+ Please note that #0 charaster if stored in string lines, will cut it
+ preventing reading the rest of a line. Be careful, if your data
+ contain such characters. }
+ protected
+ procedure Init; virtual;
+ protected
+ fList: PList;
+ fCount: Integer;
+ fCaseSensitiveSort: Boolean;
+ fAnsiSort: Boolean;
+ fTextBuf: PAnsiChar;
+ fTextSiz: DWORD;
+ fCompareStrListFun: TCompareStrListFun;
+ function GetPChars(Idx: Integer): PAnsiChar;
+ //procedure AddTextBuf( Src: PAnsiChar; Len: DWORD );
+ protected
+ function Get(Idx: integer): Ansistring;
+ function GetTextStr: Ansistring;
+ procedure Put(Idx: integer; const Value: Ansistring);
+ procedure SetTextStr(const Value: Ansistring);
+ destructor Destroy; virtual;
+ protected
+ // by Dod:
+ procedure SetValue(const AName, Value: Ansistring);
+ function GetValue(const AName: Ansistring): Ansistring;
+ public
+ // by Dod:
+ function IndexOfName(AName: Ansistring): Integer;
+ {* by Dod. Returns index of line starting like Name=... }
+ function IndexOfName_NoCase(AName: Ansistring): Integer;
+ property Values[const AName: Ansistring]: Ansistring read GetValue write SetValue;
+ {* by Dod. Returns right side of a line starting like Name=... }
+ public
+ function Add(const S: Ansistring): integer;
+ {* Adds a string to list. }
+ procedure AddStrings(Strings: PStrList);
+ {* Merges string list with given one. Very fast - more preferrable to
+ use than any loop with calling Add method. }
+ procedure Assign(Strings: PStrList);
+ {* Fills string list with strings from other one. The same as AddStrings,
+ but Clear is called first. }
+ procedure Clear;
+ {* Makes string list empty. }
+ procedure Delete(Idx: integer);
+ {* Deletes string with given index (it *must* exist). }
+ procedure DeleteLast;
+ {* Deletes the last string (it *must* exist). }
+ function IndexOf(const S: AnsiString): integer;
+ {* Returns index of first string, equal to given one. }
+ function IndexOf_NoCase(const S: Ansistring): integer;
+ {* Returns index of first string, equal to given one (while comparing it
+ without case sensitivity). }
+ function IndexOfStrL_NoCase( Str: PAnsiChar; L: Integer ): integer;
+ {* Returns index of first string, equal to given one (while comparing it
+ without case sensitivity). }
+ function Find(const S: AnsiString; var Index: Integer): Boolean;
+ {* Returns Index of the string, equal or greater to given pattern, but
+ works only for sorted TStrList object. Returns TRUE if exact string found,
+ otherwise nearest (greater then a pattern) string index is returned,
+ and the result is FALSE. And in such _case Index is returned negated
+ when the S string is less then the string found. }
+ function FindFirst(const S: AnsiString; var Index: Integer): Boolean;
+ {* Like above but always returns Index of the first string, equal or greater
+ to given pattern. Also works only for sorted TStrList object. Returns TRUE
+ if exact string found, otherwise nearest (greater then a pattern) string
+ index is returned, and the result is FALSE. }
+ procedure Insert(Idx: integer; const S: Ansistring);
+ {* Inserts string before one with given index. }
+ procedure Move(CurIndex, NewIndex: integer);
+ {* Moves string to another location. }
+ procedure SetText(const S: Ansistring; Append2List: Boolean);
+ {* Allows to set strings of string list from given string (in which
+ strings are separated by $0D,$0A or $0D characters). Text must not
+ contain #0 characters. Works very fast. This method is used in
+ all others, working with text arrays (LoadFromFile, MergeFromFile,
+ Assign, AddStrings). }
+ procedure SetUnixText( const S: AnsiString; Append2List: Boolean );
+ {* Allows to assign UNIX-style text (with #10 as string separator). }
+ property Count: integer read fCount;
+ {* Number of strings in a string list. }
+ property Items[Idx: integer]: Ansistring read Get write Put; default;
+ {* Strings array items. If item does not exist, empty string is returned.
+ But for assign to property, string with given index *must* exist. }
+ property ItemPtrs[ Idx: Integer ]: PAnsiChar read GetPChars;
+ {* Fast access to item strings as PChars. }
+ function Last: AnsiString;
+ {* Last item (or '', if string list is empty). }
+ property Text: Ansistring read GetTextStr write SetTextStr;
+ {* Content of string list as a single string (where strings are separated
+ by characters $0D,$0A). }
+ procedure Swap( Idx1, Idx2 : Integer );
+ {* Swaps to strings with given indeces. }
+ procedure Sort( CaseSensitive: Boolean );
+ {* Call it to sort string list. }
+ procedure AnsiSort( CaseSensitive: Boolean );
+ {* Call it to sort ANSI string list. }
+ procedure SortEx(const CompareFun: TCompareEvent); // by Dufa
+ {* Call it to sort via your own compare procedure }
+ protected // by Alexander Pravdin:
+ fNameDelim: AnsiChar;
+ function GetLineName( Idx: Integer ): AnsiString;
+ procedure SetLineName( Idx: Integer; const NV: AnsiString );
+ function GetLineValue(Idx: Integer): Ansistring;
+ procedure SetLineValue(Idx: Integer; const Value: Ansistring);
+ public
+ property LineName[ Idx: Integer ]: Ansistring read GetLineName write SetLineName;
+ property LineValue[ Idx: Integer ]: Ansistring read GetLineValue write SetLineValue;
+ property NameDelimiter: AnsiChar read fNameDelim write fNameDelim;
+ function Join( const sep: AnsiString ): AnsiString;
+ {* by Sergey Shishmintzev }
+ {$IFDEF WIN_GDI}
+ function LoadFromFile(const FileName: KOLString): Boolean;
+ {* Loads string list from a file. (If file does not exist, nothing
+ happens). Very fast even for huge text files. }
+ procedure LoadFromStream(Stream: PStream; Append2List: Boolean);
+ {* Loads string list from a stream (from current position to the end of
+ a stream). Very fast even for huge text. }
+ procedure MergeFromFile(const FileName: KOLString);
+ {* Merges string list with strings in a file. Fast. }
+ function SaveToFile(const FileName: KOLString): Boolean;
+ {* Stores string list to a file. }
+ procedure SaveToStream(Stream: PStream);
+ {* Saves string list to a stream (from current position). }
+ function AppendToFile(const FileName: KOLString): Boolean;
+ {* Appends strings of string list to the end of a file. }
+ {$ENDIF WIN_GDI}
+ procedure OptimizeForRead;
+ end;
+
+var DefaultNameDelimiter: AnsiChar = '=';
+ ThsSeparator: KOLChar = ',';
+
+function NewStrList: PStrList;
+{* Creates string list object. }
+
+{$IFNDEF _FPC}
+function WStrLen( W: PWideChar ): Integer;
+{* Returns Length of null-terminated Unicode string. }
+
+{$IFDEF _D3orHigher}
+function UTF8_2KOLWideString( const s: AnsiString ): KOLWideString;
+{$ENDIF}
+{$ENDIF _FPC}
+
+type
+ PStrListEx = ^TStrListEx;
+
+ TStrListEx = object( TStrList )
+ {* Extended string list object. Has additional capability to associate
+ numbers or objects with string list items. }
+ protected
+ FObjects: PList;
+ function GetObjects(Idx: Integer): DWORD;
+ function GetObjectCount: Integer;
+ procedure SetObjects(Idx: Integer; const Value: DWORD);
+ procedure Init; virtual;
+ procedure ProvideObjCapacity( NewCap: Integer );
+ public
+ destructor Destroy; virtual;
+ {* }
+ property Objects[ Idx: Integer ]: DWORD read GetObjects write SetObjects;
+ {* Objects are just 32-bit values. You can treat and use it as pointers to
+ any other data in the memory. But it is your task to free allocated
+ memory in such case therefore.
+ |<br>
+ If the last item of a string list is deleted vis DeleteLast method (but
+ not via Delete method), it's object still is preserved. As well, it is
+ possible to set Objects[idx] for idx >= Count.
+ To get know object's count, rather then strings count, use ObjectCount
+ property. }
+ property ObjectCount: Integer read GetObjectCount;
+ {* Returns number of objects available. This value can differ from Count
+ after some operations: objects are stored in the independant list and
+ only synchronization is provided while using methods Delete, Insert,
+ Add, AddObject, InsertObject while changing the list. }
+ procedure AddStrings(Strings: PStrListEx);
+ {* Merges string list with given one. Very fast - more preferrable to
+ use than any loop with calling Add method. }
+ procedure Assign(Strings: PStrListEx);
+ {* Fills string list with strings from other one. The same as AddStrings,
+ but Clear is called first. }
+ procedure Clear;
+ {* Makes string list empty. }
+ procedure Delete(Idx: integer);
+ {* Deletes string with given index (it *must* exist). }
+ procedure DeleteLast;
+ {* Deletes the last string and correspondent object in the list. }
+ procedure Move(CurIndex, NewIndex: integer);
+ {* Moves string to another location. }
+ procedure Swap( Idx1, Idx2 : Integer );
+ {* Swaps to strings with given indeces. }
+ procedure Sort( CaseSensitive: Boolean );
+ {* Call it to sort string list. }
+ procedure AnsiSort( CaseSensitive: Boolean );
+ {* Call it to sort ANSI string list. }
+ function LastObj: DWORD;
+ {* Object assotiated with the last string. }
+ function AddObject( const S: AnsiString; Obj: DWORD ): Integer;
+ {* Adds a string and associates given number with it. Index of the item added
+ is returned. }
+ procedure InsertObject( Before: Integer; const S: AnsiString; Obj: DWORD );
+ {* Inserts a string together with object associated. }
+ function IndexOfObj( Obj: Pointer ): Integer;
+ {* Returns an index of a string associated with the object passed as a
+ parameter. If there are no such strings, -1 is returned. }
+ procedure OptimizeForRead;
+ end;
+
+function NewStrListEx: PStrListEx;
+{* Creates extended string list object. }
+
+{$IFNDEF _FPC}
+procedure WStrCopy( Dest, Src: PWideChar );
+{* Copies null-terminated Unicode string (terminated null also copied). }
+procedure WStrLCopy( Dest, Src: PWideChar; MaxLen: Integer );
+{* Copies null-terminated Unicode string (terminated null also copied). }
+function WStrCmp( W1, W2: PWideChar ): Integer;
+{* Compares two null-terminated Unicode strings. }
+{$IFDEF _D3orHigher}
+function WStrCmp_NoCase( W1, W2: PWideChar ): Integer;
+{* Compares two null-terminated Unicode strings. }
+{$ENDIF}
+{$ENDIF _FPC}
+
+{$IFDEF WIN_GDI}
+{$IFNDEF _D2} //------------------ WideString is not supported in D2 -----------
+
+type
+ PWStrList = ^TWstrList;
+ {* }
+ TWStrList = object( TObj )
+ {* String list to store Unicode (null-terminated) strings. }
+ protected
+ function GetCount: Integer;
+ function GetItems(Idx: Integer): KOLWideString;
+ procedure SetItems(Idx: Integer; const Value: KOLWideString);
+ function GetPtrs(Idx: Integer): PWideChar;
+ function GetText: KOLWideString;
+ protected
+ fList: PList;
+ fText: PWideChar;
+ fTextBufSz: Integer;
+ fTmp1, fTmp2: KOLWideString;
+ procedure Init; virtual;
+ public
+ procedure SetText(const Value: KOLWideString);
+ {* See also TStrList.SetText }
+ destructor Destroy; virtual;
+ {* }
+ procedure Clear;
+ {* See also TStrList.Clear }
+ property Items[ Idx: Integer ]: KOLWideString read GetItems write SetItems;
+ {* See also TStrList.Items }
+ property ItemPtrs[ Idx: Integer ]: PWideChar read GetPtrs;
+ {* See also TStrList.ItemPtrs }
+ property Count: Integer read GetCount;
+ {* See also TStrList.Count }
+ function Add( const W: KOLWideString ): Integer;
+ {* See also TStrList.Add }
+ procedure Insert( Idx: Integer; const W: KOLWideString );
+ {* See also TStrList.Insert }
+ procedure Delete( Idx: Integer );
+ {* See also TStrList.Delete }
+ property Text: KOLWideString read GetText write SetText;
+ {* See also TStrList.Text }
+ procedure AddWStrings( WL: PWStrList );
+ {* See also TStrList.AddStrings }
+ procedure Assign( WL: PWStrList );
+ {* See also TStrList.Assign }
+ function LoadFromFile( const Filename: KOLString ): Boolean;
+ {* See also TStrList.LoadFromFile }
+ procedure LoadFromStream( Strm: PStream; AppendToList: Boolean );
+ {* See also TStrList.LoadFromStream }
+ function MergeFromFile( const Filename: KOLString ): Boolean;
+ {* See also TStrList.MergeFromFile }
+ procedure MergeFromStream( Strm: PStream );
+ {* See also TStrList.MergeFromStream }
+ function SaveToFile( const Filename: KOLString ): Boolean;
+ {* See also TStrList.SaveToFile }
+ procedure SaveToStream( Strm: PStream );
+ {* See also TStrList.SaveToStream }
+ function AppendToFile( const Filename: KOLString ): Boolean;
+ {* See also TStrList.AppendToFile }
+ procedure Swap( Idx1, Idx2: Integer );
+ {* See also TStrList.Swap }
+ procedure Sort( CaseSensitive: Boolean );
+ {* See also TStrList.Sort }
+ procedure Move( IdxOld, IdxNew: Integer );
+ {* See also TStrList.Move }
+ function IndexOf( const s: KOLWideString ): Integer;
+ {* }
+ function IndexOf_NoCase( const s: KOLWideString ): Integer;
+ {* }
+ function Last: KOLWideString;
+ {* }
+ procedure Put(Idx: integer; const Value: KOLWideString);
+ {* +azsd for TBButton }
+ protected // by Alexander Pravdin:
+ fNameDelim: WideChar;
+ function GetLineName( Idx: Integer ): KOLWideString;
+ procedure SetLineName( Idx: Integer; const NV: KOLWideString );
+ function GetLineValue(Idx: Integer): KOLWideString;
+ procedure SetLineValue(Idx: Integer; const Value: KOLWideString);
+ public
+ property LineName[ Idx: Integer ]: KOLWideString read GetLineName write SetLineName;
+ property LineValue[ Idx: Integer ]: KOLWideString read GetLineValue write SetLineValue;
+ property NameDelimiter: WideChar read fNameDelim write fNameDelim;
+ procedure OptimizeForRead;
+ protected // ++++++++++++++ by rdnks
+ procedure SetValue(const AName, Value: KOLWideString);
+ function GetValue(const AName: KOLWideString): KOLWideString;
+ public
+ function IndexOfName(AName: KOLWideString): Integer;
+ property Values[const AName: KOLWideString]: KOLWideString read GetValue write SetValue;
+ end;
+
+ PWStrListEx = ^TWStrListEx;
+
+ TWStrListEx = object( TWStrList )
+ {* Extended Unicode string list (with Objects). }
+ protected
+ function GetObjects(Idx: Integer): DWORD;
+ procedure SetObjects(Idx: Integer; const Value: DWORD);
+ procedure ProvideObjectsCapacity( NewCap: Integer );
+ protected
+ fObjects: PList;
+ procedure Init; virtual;
+ public
+ destructor Destroy; virtual;
+ {* }
+ property Objects[ Idx: Integer ]: DWORD read GetObjects write SetObjects;
+ {* }
+ procedure AddWStrings( WL: PWStrListEx );
+ {* }
+ procedure Assign( WL: PWStrListEx );
+ {* }
+ procedure Clear;
+ {* }
+ procedure Delete( Idx: Integer );
+ {* }
+ procedure Move( IdxOld, IdxNew: Integer );
+ {* }
+ procedure Swap( Idx1, Idx2: Integer );
+ {* See also TStrList.Swap }
+ procedure Sort( CaseSensitive: Boolean );
+ {* See also TStrList.Sort }
+ function AddObject( const S: KOLWideString; Obj: DWORD ): Integer;
+ {* Adds a string and associates given number with it. Index of the item added
+ is returned. }
+ procedure InsertObject( Before: Integer; const S: KOLWideString; Obj: DWORD );
+ {* Inserts a string together with object associated. }
+ function IndexOfObj( Obj: Pointer ): Integer;
+ {* Returns an index of a string associated with the object passed as a
+ parameter. If there are no such strings, -1 is returned. }
+ procedure OptimizeForRead;
+ end;
+
+function NewWStrList: PWStrList;
+{* Creates new TWStrList object and returns a pointer to it. }
+
+function NewWStrListEx: PWStrListEx;
+{* Creates new TWStrListEx objects and returns a pointer to it. }
+
+{$ENDIF not _D2}
+{$ENDIF WIN_GDI}
+
+{$IFDEF UNICODE_CTRLS}
+{$IFNDEF _D2}
+type TKOLStrList = TWStrList;
+ PKOLStrList = PWStrList;
+ TKOLStrListEx = TWStrListEx;
+ PKOLStrListEx = PWStrListEx;
+{$ELSE}
+type TKOLStrList = TStrList;
+ PKOLStrList = PStrList;
+ TKOLStrListEx = TStrListEx;
+ PKOLStrListEx = PStrListEx;
+{$ENDIF}
+{$ELSE}
+type TKOLStrList = TStrList;
+ PKOLStrList = PStrList;
+ TKOLStrListEx = TStrListEx;
+ PKOLStrListEx = PStrListEx;
+{$ENDIF}
+
+function NewKOLStrList: PKOLStrList;
+function NewKOLStrListEx: PKOLStrListEx;
+
+{$IFDEF WIN}
+function GetFileList(const dir: KOLString): PKOLStrList;
+{* By Alexander Shakhaylo. Returns list of file names of the given directory. }
+{$ENDIF WIN}
+
+////////////////////////////////////////////////////////////////////////////////
+// GRAPHIC OBJECTS //
+////////////////////////////////////////////////////////////////////////////////
+{
+ It is very important, that the most of code, implementing graphic objets
+ from this section, is included into executable ONLY if really accessed in your
+ project directly (e.g., if Font or Brush properies of a control are accessed
+ or changed).
+}
+type
+ TColor = Integer;
+const
+ clScrollBar = TColor(COLOR_SCROLLBAR or $80000000);
+ clBackground = TColor(COLOR_BACKGROUND or $80000000);
+ clActiveCaption = TColor(COLOR_ACTIVECAPTION or $80000000);
+ clInactiveCaption = TColor(COLOR_INACTIVECAPTION or $80000000);
+ clMenu = TColor(COLOR_MENU or $80000000);
+ clWindow = TColor(COLOR_WINDOW or $80000000);
+ clWindowFrame = TColor(COLOR_WINDOWFRAME or $80000000);
+ clMenuText = TColor(COLOR_MENUTEXT or $80000000);
+ clWindowText = TColor(COLOR_WINDOWTEXT or $80000000);
+ clCaptionText = TColor(COLOR_CAPTIONTEXT or $80000000);
+ clActiveBorder = TColor(COLOR_ACTIVEBORDER or $80000000);
+ clInactiveBorder = TColor(COLOR_INACTIVEBORDER or $80000000);
+ clAppWorkSpace = TColor(COLOR_APPWORKSPACE or $80000000);
+ clHighlight = TColor(COLOR_HIGHLIGHT or $80000000);
+ clHighlightText = TColor(COLOR_HIGHLIGHTTEXT or $80000000);
+ clBtnFace = TColor(COLOR_BTNFACE or $80000000);
+ clBtnShadow = TColor(COLOR_BTNSHADOW or $80000000);
+ clGrayText = TColor(COLOR_GRAYTEXT or $80000000);
+ clGreyText = TColor(COLOR_GRAYTEXT or $80000000);
+ clBtnText = TColor(COLOR_BTNTEXT or $80000000);
+ clInactiveCaptionText = TColor(COLOR_INACTIVECAPTIONTEXT or $80000000);
+ clBtnHighlight = TColor(COLOR_BTNHIGHLIGHT or $80000000);
+ cl3DDkShadow = TColor(COLOR_3DDKSHADOW or $80000000);
+ cl3DLight = TColor(COLOR_3DLIGHT or $80000000);
+ clInfoText = TColor(COLOR_INFOTEXT or $80000000);
+ clInfoBk = TColor(COLOR_INFOBK or $80000000);
+
+ clBlack = TColor($000000);
+ clMaroon = TColor($000080);
+ clGreen = TColor($008000);
+ clOlive = TColor($008080);
+ clNavy = TColor($800000);
+ clPurple = TColor($800080);
+ clTeal = TColor($808000);
+ clGray = TColor($808080);
+ clGrey = TColor($808080);
+ clSilver = TColor($C0C0C0);
+ clRed = TColor($0000FF);
+ clLime = TColor($00FF00);
+ clYellow = TColor($00FFFF);
+ clBlue = TColor($FF0000);
+ clFuchsia = TColor($FF00FF);
+ clAqua = TColor($FFFF00);
+ clLtGray = TColor($C0C0C0);
+ clLtGrey = TColor($C0C0C0);
+ clDkGray = TColor($808080);
+ clDkGrey = TColor($808080);
+ clWhite = TColor($FFFFFF);
+ clNone = TColor($1FFFFFFF);
+ clDefault = TColor($20000000);
+
+ clMoneyGreen = TColor($C0DCC0);
+ clSkyBlue = TColor($F0CAA6);
+ clCream = TColor($F0FBFF);
+ clMedGray = TColor($A4A0A0);
+ clMedGrey = TColor($A4A0A0);
+ clOrange = TColor( $3399FF );
+ clBrown = TColor( $505080 );
+ clDkBrown = TColor( $282840 );
+
+ clGRushHiLight = TColor( $F3706C );
+ clGRushLighten = TColor( $F1EEDF );
+ clGRushLight = TColor( $e1cebf );
+ clGRushNormal = TColor( $D1beaf );
+ clGRushMedium = TColor( $b6bFc6 );
+ clGRushDark = TColor( $9EACB4 );
+
+const
+ go_Color = 0;
+ go_FontHeight = 4;
+ go_FontWidth = 8;
+ go_FontEscapement = 12;
+ go_FontOrientation = 16;
+ go_FontWeight = 20;
+ go_FontItalic = 24;
+ go_FontUnderline = 25;
+ go_FontStrikeOut = 26;
+ go_FontCharSet = 27;
+ go_FontOutPrecision = 28;
+ go_FontClipPrecision = 29;
+ go_FontQuality = 30;
+ go_FontPitch = 31;
+ go_FontName = 32;
+ go_BrushBitmap = 4;
+ go_BrushStyle = 8;
+ go_BrushLineColor = 9;
+ go_PenBrushBitmap = 4;
+ go_PenBrushStyle = 8;
+ go_PenStyle = 9;
+ go_PenWidth = 10;
+ go_PenMode = 14;
+ go_PenGeometric = 15;
+ go_PenEndCap = 16;
+ go_PenJoin = 17;
+
+type
+ TGraphicToolType = ( gttBrush, gttFont, gttPen );
+ {* Graphic object types, mainly for internal use. }
+
+ PGraphicTool = ^TGraphicTool;
+ {* }
+ TOnGraphicChange = procedure ( Sender: PGraphicTool ) of object;
+ {* An event mainly for internal use. }
+
+ TBrushStyle = (bsSolid, bsClear, bsHorizontal, bsVertical,
+ bsFDiagonal, bsBDiagonal, bsCross, bsDiagCross);
+ {* Available brush styles. }
+
+ TFontStyles = (fsBold, fsItalic, fsUnderline, fsStrikeOut);
+ {* Available font styles. }
+ TFontStyle = set of TFontStyles;
+ {* Font style is representing as a set of XFontStyles. }
+ TFontPitch = (fpDefault, fpFixed, fpVariable);
+ {* Availabe font pitch values. }
+ TFontName = type string;
+ {* Font name is represented as a string. }
+ TFontCharset = 0..255;
+ {* Font charset is represented by number from 0 to 255. }
+ TFontQuality = (fqDefault, fqDraft, fqProof, fqNonAntialiased, fqAntialiased
+ , fqClearType);
+ {* Font quality. }
+
+ TPenStyle = (psSolid, psDash, psDot, psDashDot, psDashDotDot, psClear,
+ psInsideFrame);
+ {* Available pen styles. For more info see Delphi or Win32 help files. }
+ TPenMode = (pmBlack, pmNotMerge, pmMaskNotPen, pmNotCopy, pmMaskPenNot,
+ pmNot, pmXor, pmNotMask, pmMask, pmNotXor, pmNop, pmMergePenNot,
+ pmCopy, pmMergeNotPen, pmMerge, pmWhite);
+ {* Available pen modes. For more info see Delphi or Win32 help files. }
+ TPenEndCap = (pecRound, pecSquare, pecFlat);
+ {* Avalable (for geometric pen) end cap styles. }
+ TPenJoin = (pjRound, pjBevel, pjMiter);
+ {* Available (for geometric pen) join styles. }
+
+ TGDIFont = packed record
+ Height: Integer;
+ Width: Integer;
+ Escapement: Integer;
+ Orientation: Integer;
+ Weight: Integer;
+ Italic: Boolean;
+ Underline: Boolean;
+ StrikeOut: Boolean;
+ CharSet: TFontCharset;
+ OutPrecision: Byte;
+ ClipPrecision: Byte;
+ Quality: TFontQuality;
+ Pitch: TFontPitch;
+ Name: array[0..LF_FACESIZE - 1] of KOLChar;
+ end;
+
+ TGDIBrush = packed record
+ Bitmap: HBitmap;
+ Style: TBrushStyle;
+ LineColor: TColor;
+ end;
+
+ TGDIPen = packed record
+ BrushBitmap: HBitmap;
+ BrushStyle: TBrushStyle;
+ Style: TPenStyle;
+ Width: Integer;
+ Mode: TPenMode;
+ Geometric: Boolean;
+ EndCap: TPenEndCap;
+ Join: TPenJoin;
+ end;
+
+ TGDIToolData = packed record
+ Color: TColor;
+ case Integer of
+ 1: (Font: TGDIFont);
+ 2: (Pen: TGDIPen);
+ 3: (Brush: TGDIBrush);
+ end;
+
+ TNewGraphicTool = function: PGraphicTool;
+
+{ ---------------------------------------------------------------------
+ TGraphicTool - object to implement GDI-tools (brush, pen, font)
+---------------------------------------------------------------------- }
+ TGraphicTool = object( TObj )
+ {* Incapsulates all GDI objects: Pen, Brush and Font. }
+ protected
+ fType: TGraphicToolType;
+ {$IFDEF GDI}
+ fHandle: THandle;
+ fParentGDITool: PGraphicTool;
+ {$ENDIF GDI}
+ fColorRGB: TColor;
+ fOnGTChange: TOnGraphicChange;
+ fData: TGDIToolData;
+ fNewProc: TNewGraphicTool;
+ {$IFDEF GDI}
+ fMakeHandleProc: function( Self_: PGraphicTool ): THandle;
+ {$ENDIF GDI}
+ procedure SetInt( const Index: Integer; Value: Integer );
+ function GetInt( const Index: Integer ): Integer;
+ procedure SetColor( Value: TColor );
+ {$IFDEF GDI}
+ function GetBrushBitmap: HBitmap; // for BCB only
+ procedure SetBrushBitmap(const Value: HBitmap);
+ function GetBrushStyle: TBrushStyle; // for BCB only
+ {$ENDIF GDI}
+ procedure SetBrushStyle(const Value: TBrushStyle);
+ function GetFontName: KOLString;
+ procedure SetFontName(const Value: KOLString);
+ function GetFontStyle: TFontStyle;
+ procedure SetFontStyle(const Value: TFontStyle);
+ function GetFontWeight: Integer; // for BCB only
+ procedure SetFontWeight(const Value: Integer);
+ {$IFDEF GDI}
+ function GetFontCharset: TFontCharset; // for BCB only
+ procedure SetFontCharset(const Value: TFontCharset);
+ function GetFontQuality: TFontQuality; // for BCB only
+ procedure SetFontQuality(const Value: TFontQuality);
+ function GetFontOrientation: Integer; // for BCB only
+ procedure SetFontOrientation(Value: Integer);
+ function GetFontPitch: TFontPitch; // for BCB only
+ procedure SetFontPitch(const Value: TFontPitch);
+ function GetPenMode: TPenMode; // for BCB only
+ procedure SetPenMode(const Value: TPenMode);
+ function GetPenStyle: TPenStyle; // for BCB only
+ procedure SetPenStyle(const Value: TPenStyle);
+ function GetGeometricPen: Boolean; // for BCB only
+ procedure SetGeometricPen(const Value: Boolean);
+ function GetPenEndCap: TPenEndCap; // for BCB only
+ procedure SetPenEndCap(const Value: TPenEndCap);
+ function GetPenJoin: TPenJoin; // for BCB only
+ procedure SetPenJoin(const Value: TPenJoin);
+ procedure SetLogFontStruct(const Value: TLogFont);
+ function GetLogFontStruct: TLogFont;
+ {$ENDIF GDI}
+ protected
+ procedure Changed;
+ {* }
+ {$IFDEF GDI}
+ function GetHandle: THandle;
+ {* }
+ {$ENDIF GDI}
+ protected
+ {$IFDEF _X_}
+ {$IFDEF GTK}
+ fPangoFontDesc: PPangoFontDescription;
+ FUNCTION GetPangoFontDesc: PPangoFontDescription;
+ {$ENDIF GTK}
+ {$ENDIF _X_}
+ public
+ destructor Destroy; virtual;
+ {* }
+ {$IFDEF _X_}
+ {$IFDEF GTK}
+ PROPERTY FontHandle: PPangoFontDescription read GetPangoFontDesc;
+ {$ENDIF GTK}
+ {$ENDIF _X_}
+ {$IFDEF GDI}
+ property Handle: THandle read GetHandle;
+ {* Every time, when accessed, real GDI object is created (if it is
+ not yet created). So, to prevent creating of the handle, use
+ HandleAllocated instead of comparing Handle with value 0. }
+ function HandleAllocated: Boolean;
+ {* Returns True, if handle is allocated (i.e., if real GDI
+ objet is created. }
+ {$ENDIF GDI}
+ property OnChange: TOnGraphicChange read fOnGTChange write fOnGTChange;
+ {* Called, when object is changed. }
+ {$IFDEF GDI}
+ function ReleaseHandle: THANDLE;
+ {* Returns Handle value (if allocated), releasing it from the
+ object (so, it is no more knows about this handle and its
+ HandleAllocated function returns False. }
+ {$ENDIF GDI}
+ property Color: TColor {index go_Color} read fData.Color write SetColor;
+ {* Color is the most common property for all Pen, Brush and
+ Font objects, so it is placed in its common for all of them. }
+ function Assign( Value: PGraphicTool ): PGraphicTool;
+ {* Assigns properties of the same (only) type graphic object,
+ excluding Handle. If assigning is really leading to change
+ object, procedure Changed is called. }
+ {$IFDEF GDI}
+ procedure AssignHandle( NewHandle: THANDLE );
+ {* Assigns value to Handle property. }
+
+ property BrushBitmap: HBitmap read {-BCB-}fData.Brush.Bitmap{+BCB+}
+ {BCB++}(*GetBrushBitmap*){--BCB}
+ write SetBrushBitmap;
+ {* Brush bitmap. For more info about using brush bitmap,
+ see Delphi or Win32 help files. }
+ {$ENDIF GDI}
+ property BrushStyle: TBrushStyle read {-BCB-}fData.Brush.Style{+BCB+}
+ {BCB++}(*GetBrushStyle*){--BCB}
+ write SetBrushStyle;
+ {$IFDEF GDI}
+ {* Brush style. }
+ property BrushLineColor: TColor index go_BrushLineColor
+ {$IFDEF F_P}
+ read GetInt
+ {$ELSE DELPHI}
+ read {-BCB-}fData.Brush.LineColor{+BCB+}
+ {BCB++}(*GetInt*){--BCB}
+ {$ENDIF F_P/DELPHI}
+ write SetInt;
+ {* Brush line color, used to represent lines in hatched brush. Default value is clBlack. }
+
+ {$ENDIF GDI}
+ property FontHeight: Integer index go_FontHeight
+ {$IFDEF F_P}
+ read GetInt
+ {$ELSE DELPHI}
+ read {-BCB-}fData.Font.Height{+BCB+}
+ {BCB++}(*GetInt*){--BCB}
+ {$ENDIF F_P/DELPHI}
+ write SetInt;
+ {* Font height. Value 0 (default) says to use system default value,
+ negative values are to represent font height in "points", positive
+ - in pixels. In XCL usually positive values (if not 0) are used to
+ make appearance independent from different local settings. }
+ {$IFDEF GDI}
+ property FontWidth: Integer index go_FontWidth
+ {$IFDEF F_P}
+ read GetInt
+ {$ELSE DELPHI}
+ read {-BCB-}fData.Font.Width{+BCB+}
+ {BCB++}(*GetInt*){--BCB}
+ {$ENDIF F_P/DELPHI}
+ write SetInt;
+ {* Font width in logical units. If FontWidth = 0, then as it is said
+ in Win32.hlp, "the aspect ratio of the device is matched against the
+ digitization aspect ratio of the available fonts to find the closest match,
+ determined by the absolute value of the difference." }
+ property FontPitch: TFontPitch read {-BCB-}fData.Font.Pitch{+BCB+}
+ {BCB++}(*GetFontPitch*){--BCB}
+ write SetFontPitch;
+ {* Font pitch. Change it very rare. }
+ {$ENDIF GDI}
+ property FontStyle: TFontStyle read GetFontStyle write SetFontStyle;
+ {* Very useful property to control text appearance. }
+ {$IFDEF GDI}
+ property FontCharset: TFontCharset read {-BCB-}fData.Font.Charset{+BCB+}
+ {BCB++}(*GetFontCharset*){--BCB}
+ write SetFontCharset;
+ {* Do not change it if You do not know what You do. }
+ property FontQuality: TFontQuality read {-BCB-}fData.Font.Quality{+BCB+}
+ {BCB++}(*GetFontQuality*){--BCB}
+ write SetFontQuality;
+ {* Font quality. }
+ property FontOrientation: Integer read {-BCB-}fData.Font.Orientation{+BCB+}
+ {BCB++}(*GetFontOrientation*){--BCB}
+ write SetFontOrientation;
+ {* It is possible to rotate text in XCL just by changing this
+ property of a font (tenths of degree, i.e. value 900 represents
+ 90 degree - text written from bottom to top). }
+ {$ENDIF GDI}
+ property FontWeight: Integer read {-BCB-}fData.Font.Weight{+BCB+}
+ {BCB++}(*GetFontWeight*){--BCB}
+ write SetFontWeight;
+ {* Additional font weight for bold fonts (must be 0..1000). When set to
+ value <> 0, fsBold is added to FontStyle. And otherwise, when set to 0,
+ fsBold is removed from FontStyle. Value 700 corresponds to Bold,
+ 400 to Normal. }
+ property FontName: KOLString read GetFontName write SetFontName;
+ {* Font face name. }
+ {$IFDEF GDI}
+ function IsFontTrueType: Boolean;
+ {* Returns True, if font is True Type. Requires of creating of a Handle,
+ if it is not yet created. }
+
+ property PenWidth: Integer index go_PenWidth
+ {$IFDEF F_P}
+ read GetInt
+ {$ELSE DELPHI}
+ read {-BCB-}fData.Pen.Width{+BCB+}
+ {BCB++}(*GetInt*){--BCB}
+ {$ENDIF F_P/DELPHI}
+ write SetInt;
+ {* Value 0 means default pen width. }
+ property PenStyle: TPenStyle read {-BCB-}fData.Pen.Style{+BCB+}
+ {BCB++}(*GetPenStyle*){--BCB}
+ write SetPenStyle;
+ {* Pen style. }
+ property PenMode: TPenMode read {-BCB-}fData.Pen.Mode{+BCB+}
+ {BCB++}(*GetPenMode*){--BCB}
+ write SetPenMode;
+ {* Pen mode. }
+
+ property GeometricPen: Boolean read {-BCB-}fData.Pen.Geometric{+BCB+}
+ {BCB++}(*GetGeometricPen*){--BCB}
+ write SetGeometricPen;
+ {* True if Pen is geometric. Note, that under Win95/98 only pen styles
+ psSolid, psNull, psInsideFrame are supported by OS. }
+ property PenBrushStyle: TBrushStyle read {-BCB-}fData.Pen.BrushStyle{+BCB+}
+ {BCB++}(*GetBrushStyle*){--BCB}
+ write SetBrushStyle;
+ {* Brush style for hatched geometric pen. }
+ property PenBrushBitmap: HBitmap read {-BCB-}fData.Pen.BrushBitmap{+BCB+}
+ {BCB++}(*GetBrushBitmap*){--BCB}
+ write SetBrushBitmap;
+ {* Brush bitmap for geometric pen (if assigned Pen is functioning as
+ its style = BS_PATTERN, regadless of PenBrushStyle value). }
+ property PenEndCap: TPenEndCap read {-BCB-}fData.Pen.EndCap{+BCB+}
+ {BCB++}(*GetPenEndCap*){--BCB}
+ write SetPenEndCap;
+ {* Pen end cap mode - for GeometricPen only. }
+ property PenJoin: TPenJoin read {-BCB-}fData.Pen.Join{+BCB+}
+ {BCB++}(*GetPenJoin*){--BCB}
+ write SetPenJoin;
+ {* Pen join mode - for GeometricPen only. }
+ property LogFontStruct: TLogFont read GetLogFontStruct write SetLogFontStruct;
+ {* by Alex Pravdin: a property to change all font structure items at once. }
+ {$ENDIF GDI}
+ end;
+
+function Color2RGB( Color: TColor ): TColor;
+{* Function to get RGB color from system color. Parameter can be also RGB
+ color, in that case result is just equal to a parameter. }
+function RGB2BGR( Color: TColor ): TColor;
+{* Converts RGB color to BGR }
+{$IFDEF GTK}
+FUNCTION Color2GDKColor( Color: TColor ): TGdkColor;
+{$ENDIF GTK}
+function ColorsMix( Color1, Color2: TColor ): TColor;
+{* Returns color, which RGB components are build as an (approximate)
+ arithmetic mean of correspondent RGB components of both source
+ colors (these both are first converted from system to RGB, and
+ result is always RGB color). Please note: this function is fast,
+ but can be not too exact. }
+{$IFDEF WIN_GDI}
+function Color2RGBQuad( Color: TColor ): TRGBQuad;
+{* Converts color to RGB, used to represent RGB values in palette entries
+ (actually swaps R and B bytes). }
+function Color2Color16( Color: TColor ): WORD;
+{* Converts Color to RGB, packed to word (as it is used in format pf16bit). }
+function Color2Color15( Color: TColor ): WORD;
+{* Converts Color to RGB, packed to word (as it is used in format pf15bit). }
+
+var // New TFont instances are intialized with the values in this structure:
+ DefFont: TGDIFont = (
+ Height: 0;
+ Width: 0;
+ Escapement: 0;
+ Orientation: 0;
+ Weight: 0;
+ Italic: FALSE;
+ Underline: FALSE;
+ StrikeOut: FALSE;
+ CharSet: 1;
+ OutPrecision: 0;
+ ClipPrecision: 0;
+ Quality: fqDefault;
+ Pitch: fpDefault;
+ {$IFDEF UNICODE_CTRLS}
+ Name: ( 'T', 'a', 'h', 'o', 'm', 'a',
+ #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0,
+ #0, #0, #0, #0, #0, #0, #0, #0, #0 );
+ {$ELSE}
+ Name: 'System';
+ {$ENDIF}
+ );
+ DefFontColor: TColor = clWindowText;
+ {* Default font color. }
+
+ GlobalGraphics_UseFontOrient: Boolean;
+ {* Global flag. If stays False (default), Orientation property of Font
+ objects is ignored. This flag is set to True automatically in
+ RotateFonts add-on. }
+
+{$ENDIF WIN_GDI}
+
+function NewFont: PGraphicTool;
+{* Creates and returns font graphic tool object. }
+function NewBrush: PGraphicTool;
+{* Creates and returns new brush object. }
+function NewPen: PGraphicTool;
+{* Creates and returns new pen object. }
+
+{ ------------------------------ TCanvas object ------------------------------ }
+const
+ HandleValid = 1;
+ PenValid = 2;
+ BrushValid = 4;
+ FontValid = 8;
+ ChangingCanvas = 16;
+
+{$IFDEF WIN_GDI}
+type
+ TFillStyle = (fsSurface, fsBorder);
+ {* Available filling styles. For more info see Win32 or Delphi help files. }
+ TFillMode = (fmAlternate, fmWinding);
+ {* Available filling modes. For more info see Win32 or Delphi help files. }
+ TCopyMode = Integer;
+ {* Available copying modes are following:
+ | cmBlackness<br>
+ | cmDstInvert<br>
+ | cmMergeCopy<br>
+ | cmMergePaint<br>
+ | cmNotSrcCopy<br>
+ | cmNotSrcErase<br>
+ | cmPatCopy<br>
+ | cmPatInvert<br>
+ | cmPatPaint<br>
+ | cmSrcAnd<br>
+ | cmSrcCopy<br>
+ | cmSrcErase<br>
+ | cmSrcInvert<br>
+ | cmSrcPaint<br>
+ | cmWhiteness<br>&nbsp;&nbsp;&nbsp;
+ Also it is possible to use any other available ROP2 modes. For more info,
+ see Win32 help files. }
+
+const
+ cmBlackness = BLACKNESS;
+ cmDstInvert = DSTINVERT;
+ cmMergeCopy = MERGECOPY;
+ cmMergePaint = MERGEPAINT;
+ cmNotSrcCopy = NOTSRCCOPY;
+ cmNotSrcErase = NOTSRCERASE;
+ cmPatCopy = PATCOPY;
+ cmPatInvert = PATINVERT;
+ cmPatPaint = PATPAINT;
+ cmSrcAnd = SRCAND;
+ cmSrcCopy = SRCCOPY;
+ cmSrcErase = SRCERASE;
+ cmSrcInvert = SRCINVERT;
+ cmSrcPaint = SRCPAINT;
+ cmWhiteness = WHITENESS;
+
+{$ENDIF WIN_GDI}
+type
+ {$IFDEF _X_}
+ {$IFDEF GTK}
+ HDC = PGdkGC;
+ {$ENDIF GTK}
+ {$ENDIF _X_}
+ PCanvas = ^TCanvas;
+ {* }
+ TOnGetHandle = function( Canvas: PCanvas ): HDC of object;
+ {* For internal use mainly. }
+ TOnTextArea = procedure( Sender: PCanvas; var Size : TSize; var P0 : TPoint );
+ {* Event to calculate actual area, occupying by a text. It is used
+ to optionally extend calculating of TextArea taking into considaration
+ font Orientation property. }
+
+{ ---------------------------------------------------------------------
+ TCanvas - high-level drawing helper object
+----------------------------------------------------------------------- }
+ TCanvas = object( TObj )
+ {* Very similar to VCL's TCanvas object. But with some changes, specific
+ for KOL: there is no necessary to use canvases in all applications.
+ And graphic tools objects are not created with canvas, but only
+ if really accessed in program. (Actually, even if paint box used,
+ only programmer decides, if to implement painting using Canvas or
+ to call low level API drawing functions working directly with DC).
+ Therefore TCanvas has some powerful extensions: rotated text support,
+ geometric pen support - just by changing correspondent properties
+ of certain graphic tool objects (Font.FontOrientation, Pen.GeometricPen).
+ See also additional Font properties (Font.FontWeight, Font.FontQuality,
+ etc. }
+ protected
+ fOwnerControl: Pointer; //PControl;
+ {$IFDEF _X_}
+ {$IFDEF GTK}
+ fDrawable: PGdkDrawable;
+ fTmpColor: PGdkColor;
+ {$ENDIF GTK}
+ {$ENDIF _X_}
+ fHandle : HDC;
+ fPenPos : TPoint;
+ fState : Byte;
+ fBrush, fPen: PGraphicTool;
+ fFont : PGraphicTool; // order is important for ASM version
+ {$IFDEF GDI}
+ fCopyMode : TCopyMode;
+ fOnChangeCanvas: TOnEvent;
+ {$ENDIF GDI}
+ fOnGetHandle: TOnGetHandle;
+ {$IFDEF _X_}
+ {$IFDEF GTK}
+ fSavedState: TGdkGCValues;
+ PROCEDURE SaveState;
+ PROCEDURE RestoreState;
+ {$ENDIF GTK}
+ {$ENDIF _X_}
+ {$IFDEF GDI}
+ procedure SetHandle( Value : HDC );
+ {$ENDIF GDI}
+ procedure SetPenPos( const Value : TPoint );
+ {$IFDEF GDI}
+ procedure CreatePen;
+ procedure CreateBrush;
+ procedure CreateFont;
+ procedure Changing;
+ {$ENDIF GDI}
+ procedure ObjectChanged( Sender : PGraphicTool );
+ function GetBrush: PGraphicTool;
+ function GetFont: PGraphicTool;
+ function GetPen: PGraphicTool;
+ function GetHandle: HDC;
+ procedure AssignChangeEvents;
+ {$IFDEF GDI}
+ function GetPixels(X, Y: Integer): TColor;
+ procedure SetPixels(X, Y: Integer; const Value: TColor);
+ protected
+ fIsPaintDC : Boolean;
+ {* TRUE, if DC obtained during current WM_PAINT (or WM_ERASEBKGND?)
+ processing for a control. This affects a way how Handle is released. }
+ fIsAlienDC: Boolean;
+ {* TRUE if Canvas was created on base of existing DC, so DC is not
+ beloning to the Canvas and should not be deleted when the Canvas object
+ is destroyed. }
+ destructor Destroy; virtual;
+ {* }
+ {$ENDIF GDI}
+ property OnGetHandle: TOnGetHandle read fOnGetHandle write fOnGetHandle;
+ {* For internal use only. }
+ {$IFDEF GDI}
+ {$ENDIF GDI}
+ public
+ property Handle : HDC read GetHandle {$IFDEF GDI} write SetHandle {$ENDIF GDI};
+ {* GDI device context object handle. Never created by
+ Canvas itself (to use Canvas with memory bitmaps,
+ always create DC by yourself and assign it to the
+ Handle property of Canvas object, or use property
+ Canvas of a bitmap). }
+ property PenPos : TPoint read FPenPos write SetPenPos;
+ {* Position of a pen. }
+ property Pen : PGraphicTool read GetPen;
+ {* Pen of Canvas object. Do not change its Pen.OnChange event value. }
+ property Brush : PGraphicTool read GetBrush;
+ {* Brush of Canvas object. Do not change its Brush.OnChange event value. }
+ property Font : PGraphicTool read GetFont;
+ {* Font of Canvas object. Do not change its Font.OnChange event value. }
+ procedure OffsetAndRotate( Xoff, Yoff: Integer; Angle: Double );
+ {* Transforms world coordinates so that Xoff and Yoff become the
+ coordinates of the origin (0,0) and all further drawing is done
+ rotated around that point by the Angle (which is given in radians) }
+ {$IFNDEF NOT_USE_KOLMATH} // if using KOLmath disabled, Arc becomes unavailable
+ procedure Arc(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer); stdcall;
+ {* Draws arc. For more info, see Delphi TCanvas help. }
+ {$ENDIF NOT_USE_KOLMATH}
+ {$IFDEF GDI}
+ procedure Chord(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer); stdcall;
+ {* Draws chord. For more info, see Delphi TCanvas help. }
+ procedure DrawFocusRect({$IFNDEF FPC}const{$ENDIF} Rect: TRect);
+ {* Draws rectangle to represent focused visual object.
+ For more info, see Delphi TCanvas help. }
+ procedure Ellipse(X1, Y1, X2, Y2: Integer);
+ {* Draws an ellipse. For more info, see Delphi TCanvas help. }
+ {$ENDIF GDI}
+ procedure FillRect({$IFNDEF FPC}const{$ENDIF} Rect: TRect);
+ {* Fills rectangle. For more info, see Delphi TCanvas help. }
+ {$IFDEF GDI}
+ procedure FillRgn( const Rgn : HRgn );
+ {* Fills region. For more info, see Delphi TCanvas help. }
+ procedure FloodFill(X, Y: Integer; Color: TColor; FillStyle: TFillStyle);
+ {* Fills a figure with givien color, floodfilling its surface.
+ For more info, see Delphi TCanvas help. }
+ procedure FrameRect({$IFNDEF FPC}const{$ENDIF} Rect: TRect);
+ {* Draws a rectangle using Brush settings (color, etc.).
+ For more info, see Delphi TCanvas help. }
+ {$ENDIF GDI}
+ procedure MoveTo( X, Y : Integer );
+ {* Moves current PenPos to a new position.
+ For more info, see Delphi TCanvas help. }
+ procedure LineTo( X, Y : Integer );
+ {* Draws a line from current PenPos up to new position.
+ For more info, see Delphi TCanvas help. }
+ {$IFDEF GDI}
+ procedure Pie(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer); stdcall;
+ {* Draws a pie. For more info, see Delphi TCanvas help. }
+ procedure Polygon(const Points: array of TPoint);
+ {* Draws a polygon. For more info, see Delphi TCanvas help. }
+ procedure Polyline(const Points: array of TPoint);
+ {* Draws a bound for polygon. For more info, see Delphi TCanvas help. }
+ procedure Rectangle(X1, Y1, X2, Y2: Integer);
+ {* Draws a rectangle using current Pen and/or Brush.
+ For more info, see Delphi TCanvas help. }
+ procedure RoundRect(X1, Y1, X2, Y2, X3, Y3: Integer);
+ {* Draws a rounded rectangle. For more info, see Delphi TCanvas help. }
+ {$ENDIF GDI}
+ procedure TextOutA(X, Y: Integer; const Text: AnsiString); stdcall;
+ {* Draws an ANSI text. For more info, see Delphi TCanvas help. }
+ procedure TextOut(X, Y: Integer; const Text: KOLString); stdcall;
+ {* Draws a text. For more info, see Delphi TCanvas help. }
+ procedure ExtTextOut( X, Y: Integer; Options: DWORD; const Rect: TRect; const Text: AnsiString;
+ const Spacing: array of Integer );
+ {* }
+ procedure TextRect(const Rect: TRect; X, Y: Integer; const Text: Ansistring);
+ {* Draws a text, clipping output into given rectangle.
+ For more info, see Delphi TCanvas help. }
+ {$IFDEF GDI}
+ procedure DrawText(Text: AnsiString; var Rect:TRect; Flags:DWord);
+ {* }
+ {$ENDIF GDI}
+ function TextExtent(const Text: KOLString): TSize;
+ {* Calculates size of a Text, using current Font settings.
+ Does not need in Handle for Canvas object (if it is not
+ yet allocated, temporary device context is created and used. }
+ procedure TextArea( const Text : KOLString; var Sz : TSize; var P0 : TPoint );
+ {* Calculates size and starting point to output Text,
+ taking into considaration all Font attributes, including
+ Orientation (only if GlobalGraphics_UseFontOrient flag
+ is set to True, i.e. if rotated fonts are used).
+ Like for TextExtent, does not need in Handle (and if this
+ last is not yet allocated/assigned, temporary device context
+ is created and used). }
+ {$IFDEF _D3orHigher}
+ procedure WTextArea( const Text : KOLWideString; var Sz : TSize; var P0 : TPoint );
+ {* Calculates size and starting point to output Text,
+ taking into considaration all Font attributes, including
+ Orientation (only if GlobalGraphics_UseFontOrient flag
+ is set to True, i.e. if rotated fonts are used).
+ Like for TextExtent, does not need in Handle (and if this
+ last is not yet allocated/assigned, temporary device context
+ is created and used). }
+ {$ENDIF _D3orHigher}
+ function TextWidth(const Text: KOLString): Integer;
+ {* Calculates text width (using TextArea). }
+ function TextHeight(const Text: KOLString): Integer;
+ {* Calculates text height (using TextArea). }
+ {$IFDEF GDI}
+ function ClipRect: TRect;
+ {* returns ClipBox. by Dmitry Zharov. }
+
+ {$IFNDEF _FPC}
+ {$IFNDEF _D2} //------- KOLWideString not supported in D2
+ procedure WTextOut(X, Y: Integer; const WText: KOLWideString); stdcall;
+ {* Draws a Unicode text. }
+ procedure WExtTextOut( X, Y: Integer; Options: DWORD; const Rect: TRect;
+ const WText: KOLWideString; const Spacing: array of Integer );
+ {* }
+ procedure WDrawText(WText: KOLWideString; var Rect:TRect; Flags:DWord);
+ {* }
+ procedure WTextRect(const Rect: TRect; X, Y: Integer;
+ const WText: KOLWideString);
+ {* Draws a Unicode text, clipping output into given rectangle. }
+ function WTextExtent( const WText: KOLWideString ): TSize;
+ {* Calculates Unicode text width and height. }
+ function WTextWidth( const WText: KOLWideString ): Integer;
+ {* Calculates Unicode text width. }
+ function WTextHeight( const WText: KOLWideString ): Integer;
+ {* Calculates Unicode text height. }
+ {$ENDIF _D2}
+ {$ENDIF _FPC}
+
+ property ModeCopy : TCopyMode read fCopyMode write fCopyMode;
+ {* Current copy mode. Is used in CopyRect method. }
+ procedure CopyRect( const DstRect : TRect; SrcCanvas : PCanvas; const SrcRect : TRect );
+ {* Copyes a rectangle from source to destination, using StretchBlt. }
+ property OnChange: TOnEvent read fOnChangeCanvas write fOnChangeCanvas;
+ {* }
+ function Assign( SrcCanvas : PCanvas ) : Boolean;
+ {* }
+ {$ENDIF GDI}
+ {$IFDEF _X_}
+ protected // for _X_ case, RequiredState is protected yet (???)
+ procedure ForeBack(fg_color, bk_color: TColor); // install colors just before drawing
+ {$ENDIF _X_}
+ {$IFDEF GDI}
+ function RequiredState( ReqState : DWORD ): HDC; stdcall;// public now
+ {* It is possible to call this method before using Handle property
+ to pass it into API calls - to provide valid combinations of
+ pen, brush and font, selected into device context. This method
+ can not provide valid Handle - You always must create it by
+ yourself and assign to TCanvas.Handle property manually.
+ To optimize assembler version, returns Handle value. }
+ public
+ {$ENDIF GDI}
+ procedure DeselectHandles;
+ {* Call this method to deselect all graphic tool objects from the canvas. }
+ {$IFDEF GDI}
+ property Pixels[ X, Y: Integer ]: TColor read GetPixels write SetPixels;
+ {* Obvious. }
+ {$ENDIF GDI}
+ end;
+
+function NewCanvas( DC: HDC ): PCanvas;
+{* Use to construct Canvas on base of memory DC. }
+
+procedure DummyObjProc( Sender: PObj );
+
+var
+ GlobalCanvas_OnTextArea : Pointer = @DummyObjProc;
+ {* Global event to extend Canvas with possible add-ons, applied
+ when rotated fonts are used only (to take into consideration
+ Font.Orientation property in TextArea method). }
+
+{$IFDEF WIN_GDI}
+
+{ ------------------------------ Image list object --------------------------- }
+
+type
+ TImageListColors = (ilcColor,ilcColor4,ilcColor8,ilcColor16,
+ ilcColor24,ilcColor32,ilcColorDDB,ilcDefault);
+ {* ImageList color schemes available. }
+
+ TDrawingStyles = ( dsBlend25, dsBlend50, dsMask, dsTransparent );
+ {* ImageList drawing styles available. }
+ TDrawingStyle = Set of TDrawingStyles;
+ {* Style of drawing is a combination of all available drawing styles. }
+
+ TImageType = (itBitmap,itIcon,itCursor);
+ {* ImageList types available. }
+
+ PImageList = ^TImageList;
+ {* }
+
+ TImgLOVrlayIdx = 1..15;
+
+{ ---------------------------------------------------------------------
+ TImageList - images container
+----------------------------------------------------------------------- }
+ TImageList = object( TObj )
+ private
+ fOverlayIdx: Integer;
+ {* ImageList incapsulation. }
+ protected
+ FHandle: THandle;
+ FControl: Pointer; // PControl;
+ fPrev, fNext: PImageList;
+ FColors: TImageListColors;
+ FMasked: Boolean;
+ FImgWidth: Integer;
+ FImgHeight: Integer;
+ FDrawingStyle: TDrawingStyle;
+ FBlendColor: TColor;
+ fBkColor: TColor;
+ FAllocBy: Integer;
+ FShareImages: Boolean;
+ FOverlay: array[ TImgLOVrlayIdx ] of Integer;
+ function HandleNeeded : Boolean;
+ procedure SetColors(const Value: TImageListColors);
+ procedure SetMasked(const Value: Boolean);
+ procedure SetImgWidth(const Value: Integer);
+ procedure SetImgHeight(const Value: Integer);
+ function GetCount: Integer;
+ function GetBkColor: TColor;
+ procedure SetBkColor(const Value: TColor);
+ function GetBitmap: HBitmap;
+ function GetMask: HBitmap;
+ function GetDrawStyle : DWord;
+ procedure SetAllocBy(const Value: Integer);
+ function GetHandle: THandle;
+ function GetOverlay(Idx: TImgLOVrlayIdx): Integer;
+ procedure SetOverlay(Idx: TImgLOVrlayIdx; const Value: Integer);
+ protected
+ procedure SetHandle(const Value: THandle);
+ {*}
+ public
+ destructor Destroy; virtual;
+ {*}
+ property Handle : THandle read GetHandle write SetHandle;
+ {* Handle of ImageList object. }
+ property ShareImages : Boolean read FShareImages write FShareImages;
+ {* True if images are shared between processes (it is set to True,
+ if its Handle is assigned to given value, which is a handle of
+ already existing ImageList object). }
+ property Colors : TImageListColors read FColors write SetColors;
+ {* Colors used to represent images. }
+ property Masked : Boolean read FMasked write SetMasked;
+ {* True, if mask is used. It is set to True, if first added image
+ is icon, e.g. }
+ property ImgWidth : Integer read FImgWidth write SetImgWidth;
+ {* Width of every image in list. If change, ImageList is cleared. }
+ property ImgHeight : Integer read FImgHeight write SetImgHeight;
+ {* Height of every image in list. If change, ImageList is cleared. }
+ property Count : Integer read GetCount;
+ {* Number of images in list. }
+ property AllocBy : Integer read FAllocBy write SetAllocBy;
+ {* Allocation factor. Default is 1. Set it to size of ImageList if this
+ value is known - to optimize speed of allocation. }
+ property BkColor : TColor read GetBkColor write SetBkColor;
+ {* Background color. }
+ property BlendColor : TColor read FBlendColor write FBlendColor;
+ {* Blend color. }
+
+ property Bitmap : HBitmap read GetBitmap;
+ {* Bitmap, containing all ImageList images (tiled horizontally). }
+ property Mask : HBitmap read GetMask;
+ {* Monochrome bitmap, containing masks for all images in list (if not
+ Masked, always returns nil). }
+ function ImgRect( Idx : Integer ) : TRect;
+ {* Rectangle occupied of given image in ImageList. }
+
+ function Add( Bmp, Msk : HBitmap ) : Integer;
+ {* Adds bitmap and given mask to ImageList. }
+ function AddMasked( Bmp : HBitmap; Color : TColor ) : Integer;
+ {* Adds bitmap to ImageList, using given color to create mask. }
+ function AddIcon( Ico : HIcon ) : Integer;
+ {* Adds icon to ImageList (always masked). }
+ procedure Delete( Idx : Integer );
+ {* Deletes given image from ImageList. }
+ procedure Clear;
+ {* Makes ImageList empty. }
+ function Replace( Idx : Integer; Bmp, Msk : HBitmap ) : Boolean;
+ {* Replaces given (by index) image with bitmap and its mask with mask bitmap. }
+ function ReplaceIcon( Idx : Integer; Ico : HIcon ) : Boolean;
+ {* Replaces given (by index) image with an icon. }
+ function Merge( Idx : Integer; ImgList2 : PImageList; Idx2 : Integer; X, Y : Integer )
+ : PImageList;
+ {* Merges two ImageList objects, returns resulting ImageList. }
+ function ExtractIcon( Idx : Integer ) : HIcon;
+ {* Extracts icon by index. }
+ function ExtractIconEx( Idx : Integer ) : HIcon;
+ {* Extracts icon (is created using current drawing style). }
+
+ property DrawingStyle : TDrawingStyle read FDrawingStyle write FDrawingStyle;
+ {* Drawing style. }
+ procedure Draw( Idx : Integer; DC : HDC; X, Y : Integer );
+ {* Draws given (by index) image from ImageList onto passed Device Context. }
+ procedure StretchDraw( Idx : Integer; DC : HDC; const Rect : TRect );
+ {* Draws given image with stratching. }
+
+ function LoadBitmap( ResourceName : PKOLChar; TranspColor : TColor ) : Boolean;
+ {* Loads ImageList from resource. }
+ //function LoadIcon( ResourceName : PAnsiChar ) : Boolean;
+ //function LoadCursor( ResourceName : PAnsiChar ) : Boolean;
+ function LoadFromFile( FileName : PKOLChar; TranspColor : TColor; ImgType : TImageType ) : Boolean;
+ {* Loads ImageList from file. }
+ function LoadSystemIcons( SmallIcons : Boolean ) : Boolean;
+ {* Assigns ImageList to system icons list (big or small). }
+
+ property Overlay[ Idx: TImgLOVrlayIdx ]: Integer read GetOverlay write SetOverlay;
+ {* Overlay images for image list (images, used as overlay images to draw over
+ other images from the image list). These overalay images can be used in
+ listview and treeview as overlaying images (up to four masks at the same
+ time). }
+ property OverlayIdx: Integer read fOverlayIdx write fOverlayIdx;
+ {* Set this value to 1..15 to draw images overlayed (using Draw or DrawEx). }
+ {$IFDEF USE_CONSTRUCTORS}
+ constructor CreateImageList( POwner: Pointer );
+ {$ENDIF USE_CONSTRUCTORS}
+ end;
+
+const
+ CLR_NONE = $FFFFFFFF;
+ CLR_DEFAULT = $FF000000;
+
+type
+ HImageList = THandle;
+
+const
+ ILC_MASK = $0001;
+ ILC_COLOR = $00FE;
+ ILC_COLORDDB = $00FE;
+ ILC_COLOR4 = $0004;
+ ILC_COLOR8 = $0008;
+ ILC_COLOR16 = $0010;
+ ILC_COLOR24 = $0018;
+ ILC_COLOR32 = $0020;
+ ILC_PALETTE = $0800;
+
+const
+ ILD_NORMAL = $0000;
+ ILD_TRANSPARENT = $0001;
+ ILD_MASK = $0010;
+ ILD_IMAGE = $0020;
+ ILD_BLEND25 = $0002;
+ ILD_BLEND50 = $0004;
+ ILD_OVERLAYMASK = $0F00;
+
+const
+ ILD_SELECTED = ILD_BLEND50;
+ ILD_FOCUS = ILD_BLEND25;
+ ILD_BLEND = ILD_BLEND50;
+ CLR_HILIGHT = CLR_DEFAULT;
+
+function ImageList_Create(CX, CY: Integer; Flags: UINT;
+ Initial, Grow: Integer): HImageList; stdcall;
+function ImageList_Destroy(ImageList: HImageList): Bool; stdcall;
+function ImageList_GetImageCount(ImageList: HImageList): Integer; stdcall;
+function ImageList_SetImageCount(ImageList: HImageList; Count: Integer): Integer; stdcall;
+function ImageList_Add(ImageList: HImageList; Image, Mask: HBitmap): Integer; stdcall;
+function ImageList_ReplaceIcon(ImageList: HImageList; Index: Integer;
+ Icon: HIcon): Integer; stdcall;
+function ImageList_SetBkColor(ImageList: HImageList; ClrBk: TColorRef): TColorRef; stdcall;
+function ImageList_GetBkColor(ImageList: HImageList): TColorRef; stdcall;
+function ImageList_SetOverlayImage(ImageList: HImageList; Image: Integer;
+ Overlay: Integer): Bool; stdcall;
+
+function ImageList_AddIcon(ImageList: HImageList; Icon: HIcon): Integer;
+
+function Index2OverlayMask(Index: Integer): Integer;
+
+function ImageList_Draw(ImageList: HImageList; Index: Integer;
+ Dest: HDC; X, Y: Integer; Style: UINT): Bool; stdcall;
+
+function ImageList_Replace(ImageList: HImageList; Index: Integer;
+ Image, Mask: HBitmap): Bool; stdcall;
+function ImageList_AddMasked(ImageList: HImageList; Image: HBitmap;
+ Mask: TColorRef): Integer; stdcall;
+function ImageList_DrawEx(ImageList: HImageList; Index: Integer;
+ Dest: HDC; X, Y, DX, DY: Integer; Bk, Fg: TColorRef; Style: Cardinal): Bool; stdcall;
+function ImageList_Remove(ImageList: HImageList; Index: Integer): Bool; stdcall;
+function ImageList_GetIcon(ImageList: HImageList; Index: Integer;
+ Flags: Cardinal): HIcon; stdcall;
+{$IFDEF UNICODE_CTRLS}
+function ImageList_LoadImage(Instance: THandle; Bmp: PWideChar; CX, Grow: Integer;
+ Mask: TColorRef; pType, Flags: Cardinal): HImageList; stdcall;
+{$ELSE}
+function ImageList_LoadImage(Instance: THandle; Bmp: PAnsiChar; CX, Grow: Integer;
+ Mask: TColorRef; pType, Flags: Cardinal): HImageList; stdcall;
+{$ENDIF}
+function ImageList_BeginDrag(ImageList: HImageList; Track: Integer;
+ XHotSpot, YHotSpot: Integer): Bool; stdcall;
+function ImageList_EndDrag: Bool; stdcall;
+function ImageList_DragEnter(LockWnd: HWnd; X, Y: Integer): Bool; stdcall;
+function ImageList_DragLeave(LockWnd: HWnd): Bool; stdcall;
+function ImageList_DragMove(X, Y: Integer): Bool; stdcall;
+function ImageList_SetDragCursorImage(ImageList: HImageList; Drag: Integer;
+ XHotSpot, YHotSpot: Integer): Bool; stdcall;
+function ImageList_DragShowNolock(Show: Bool): Bool; stdcall;
+function ImageList_GetDragImage(Point, HotSpot: PPoint): HImageList; stdcall;
+
+{ macros }
+procedure ImageList_RemoveAll(ImageList: HImageList); stdcall;
+function ImageList_ExtractIcon(Instance: THandle; ImageList: HImageList;
+ Image: Integer): HIcon; stdcall;
+function ImageList_LoadBitmap(Instance: THandle; Bmp: PKOLChar;
+ CX, Grow: Integer; MasK: TColorRef): HImageList; stdcall;
+
+type
+ PImageInfo = ^TImageInfo;
+ TImageInfo = packed record
+ hbmImage: HBitmap;
+ hbmMask: HBitmap;
+ Unused1: Integer;
+ Unused2: Integer;
+ rcImage: TRect;
+ end;
+
+function ImageList_GetIconSize(ImageList: HImageList; var CX, CY: Integer): Bool; stdcall;
+function ImageList_SetIconSize(ImageList: HImageList; CX, CY: Integer): Bool; stdcall;
+function ImageList_GetImageInfo(ImageList: HImageList; Index: Integer;
+ var ImageInfo: TImageInfo): Bool; stdcall;
+function ImageList_Merge(ImageList1: HImageList; Index1: Integer;
+ ImageList2: HImageList; Index2: Integer; DX, DY: Integer)://Bool - ERROR IN VCL
+ HImageList; stdcall;
+
+function LoadBmp( Instance: Integer; Rsrc: PKOLChar; MasterObj: PObj ): HBitmap;
+function LoadBmp32( Instance: Integer; Rsrc: PKOLChar; MasterObj: PObj ): HBitmap;
+
+type
+ tagBitmap = Windows.TBitmap;
+
+ TPixelFormat = ( pfDevice, pf1bit, pf4bit, pf8bit, pf15bit, pf16bit, pf24bit,
+ pf32bit, pfCustom );
+ {* Available pixel formats. }
+ TBitmapHandleType = ( bmDIB, bmDDB );
+ {* Available bitmap handle types. }
+
+ PBitmap = ^TBitmap;
+{ ----------------------------------------------------------------------
+ TBitmap - bitmap image
+----------------------------------------------------------------------- }
+ TBitmap = object( TObj )
+ {* Bitmap incapsulation object. }
+ protected
+ fHeight: Integer;
+ fWidth: Integer;
+ fHandle: HBitmap;
+ fCanvas: PCanvas;
+ fScanLineSize: Integer;
+ fBkColor: TColor;
+ fApplyBkColor2Canvas: procedure( Sender: PBitmap );
+ fDetachCanvas: procedure( Sender: PBitmap );
+ fCanvasAttached : Integer;
+ fHandleType: TBitmapHandleType;
+ fDIBHeader: PBitmapInfo;
+ fDIBBits: Pointer;
+ fDIBSize: Integer;
+ fNewPixelFormat: TPixelFormat;
+ fFillWithBkColor: procedure( BmpObj: PBitmap; DC: HDC; oldW, oldH: Integer );
+ //stdcall;
+ fTransMaskBmp: PBitmap;
+ fTransColor: TColor;
+ fGetDIBPixels: function( Bmp: PBitmap; X, Y: Integer ): TColor;
+ fSetDIBPixels: procedure( Bmp: PBitmap; X, Y: Integer; Value: TColor );
+ fScanLine0: PByte;
+ fScanLineDelta: Integer;
+ fPixelMask: DWORD;
+ fPixelsPerByteMask: Integer;
+ fBytesPerPixel: Integer;
+ fDIBAutoFree: Boolean;
+ procedure SetHeight(const Value: Integer);
+ procedure SetWidth(const Value: Integer);
+ function GetEmpty: Boolean;
+ function GetHandle: HBitmap;
+ function GetHandleAllocated: Boolean;
+ procedure SetHandle(const Value: HBitmap);
+ procedure SetPixelFormat(Value: TPixelFormat);
+ procedure FormatChanged;
+ function GetCanvas: PCanvas;
+ procedure CanvasChanged( Sender: PObj );
+ function GetScanLine(Y: Integer): Pointer;
+ function GetScanLineSize: Integer;
+ procedure ClearData;
+ procedure ClearTransImage;
+ procedure SetBkColor(const Value: TColor);
+ function GetDIBPalEntries(Idx: Integer): TColor;
+ function GetDIBPalEntryCount: Integer;
+ procedure SetDIBPalEntries(Idx: Integer; const Value: TColor);
+ procedure SetHandleType(const Value: TBitmapHandleType);
+ function GetPixelFormat: TPixelFormat;
+ function GetPixels(X, Y: Integer): TColor;
+ procedure SetPixels(X, Y: Integer; const Value: TColor);
+ function GetDIBPixels(X, Y: Integer): TColor;
+ procedure SetDIBPixels(X, Y: Integer; const Value: TColor);
+ function GetBoundsRect: TRect;
+ protected
+ destructor Destroy; virtual;
+ public
+ property Width: Integer read fWidth write SetWidth;
+ {* Width of bitmap. To make code smaller, avoid changing Width or Height
+ after bitmap is created (using NewBitmap) or after it is loaded from
+ file, stream of resource. }
+ property Height: Integer read fHeight write SetHeight;
+ {* Height of bitmap. To make code smaller, avoid changing Width or Height
+ after bitmap is created (using NewBitmap) or after it is loaded from
+ file, stream of resource. }
+ property BoundsRect: TRect read GetBoundsRect;
+ {* Returns rectangle (0,0,Width,Height). }
+ property Empty: Boolean read GetEmpty;
+ {* Returns True if Width or Height is 0. }
+ procedure Clear;
+ {* Makes bitmap empty, setting its Width and Height to 0. }
+ procedure LoadFromFile( const Filename: KOLString );
+ {* Loads bitmap from file (LoadFromStream used). }
+ function LoadFromFileEx( const Filename: KOLString ): Boolean;
+ {* Loads bitmap from a file. If necessary, bitmap is RLE-decoded. Code given
+ by Vyacheslav A. Gavrik. }
+ procedure SaveToFile( const Filename: KOLString );
+ {* Stores bitmap to file (SaveToStream used). }
+ procedure CoreSaveToFile( const Filename: KOLString );
+ {* Stores bitmap to file (CoreSaveToStream used). }
+ procedure RLESaveToFile( const Filename: KOLString );
+ {* Stores bitmap to file (CoreSaveToStream used). }
+ procedure LoadFromStream( Strm: PStream );
+ {* Loads bitmap from stream. Follow loading, bitmap has DIB format (without
+ handle allocated). It is possible to draw DIB bitmap without creating
+ handle for it, which can economy GDI resources. }
+ function LoadFromStreamEx( Strm: PStream ): Boolean;
+ {* Loads bitmap from a stream. Difference is that RLE decoding supported.
+ Code given by Vyacheslav A. Gavrik. }
+ procedure SaveToStream( Strm: PStream );
+ {* Saves bitmap to stream. If bitmap is not DIB, it is converted to DIB
+ before saving. }
+ procedure CoreSaveToStream( Strm: PStream );
+ {* Saves bitmap to stream using CORE format with RGBTRIPLE palette and
+ with BITMAPCOREHEADER as a header.
+ If bitmap is not DIB, it is converted to DIB before saving. }
+ procedure RLESaveToStream( Strm: PStream );
+ {* Saves bitmap to stream using CORE format with RGBTRIPLE palette and
+ with BITMAPCOREHEADER as a header.
+ If bitmap is not DIB, it is converted to DIB before saving. }
+ procedure LoadFromResourceID( Inst: DWORD; ResID: Integer );
+ {* Loads bitmap from resource using integer ID of resource. To load by name,
+ use LoadFromResurceName. To load resource of application itself, pass
+ hInstance as first parameter. This method also can be used to load system
+ predefined bitmaps, if 0 is passed as Inst parameter:
+ |<pre>
+ OBM_BTNCORNERS OBM_REDUCE
+ OBM_BTSIZE OBM_REDUCED
+ OBM_CHECK OBM_RESTORE
+ OBM_CHECKBOXES OBM_RESTORED
+ OBM_CLOSE OBM_RGARROW
+ OBM_COMBO OBM_RGARROWD
+ OBM_DNARROW OBM_RGARROWI
+ OBM_DNARROWD OBM_SIZE
+ OBM_DNARROWI OBM_UPARROW
+ OBM_LFARROW OBM_UPARROWD
+ OBM_LFARROWD OBM_UPARROWI
+ OBM_LFARROWI OBM_ZOOM
+ OBM_MNARROW OBM_ZOOMD
+ |</pre> }
+ procedure LoadFromResourceName( Inst: DWORD; ResName: PKOLChar );
+ {* Loads bitmap from resurce (using passed name of bitmap resource. }
+ function Assign( SrcBmp: PBitmap ): Boolean;
+ {* Assigns bitmap from another. Returns False if not success.
+ Note: remember, that Canvas is not assigned - only bitmap image
+ is copied. And for DIB, handle is not allocating due this process. }
+ property Handle: HBitmap read GetHandle write SetHandle;
+ {* Handle of bitmap. Created whenever property accessed. To check if handle
+ is allocated (without allocating it), use HandleAllocated property. }
+ property HandleAllocated: Boolean read GetHandleAllocated;
+ {* Returns True, if Handle already allocated. }
+ function ReleaseHandle: HBitmap;
+ {* Returns Handle and releases it, so bitmap no more know about handle.
+ This method does not destroy bitmap image, but converts it into DIB.
+ Returned Handle actually is a handle of copy of original bitmap. If
+ You need not in keping it up, use Dormant method instead. }
+ procedure Dormant;
+ {* Releases handle from bitmap and destroys it. But image is not destroyed
+ and its data are preserved in DIB format. Please note, that in KOL, DIB
+ bitmaps can be drawn onto given device context without allocating of
+ handle. So, it is very useful to call Dormant preparing it using
+ Canvas drawing operations - to economy GDI resources. }
+ property HandleType: TBitmapHandleType read fHandleType write SetHandleType;
+ {* bmDIB, if DIB part of image data is filled and stored internally in
+ TBitmap object. DIB image therefore can have Handle allocated, which
+ require resources. Use HandleAllocated funtion to determine if handle
+ is allocated and Dormant method to remove it, if You want to economy
+ GDI resources. (Actually Handle needed for DIB bitmap only in case
+ when Canvas is used to draw on bitmap surface). Please note also, that
+ before saving bitmap to file or stream, it is converted to DIB. }
+ property PixelFormat: TPixelFormat read GetPixelFormat write SetPixelFormat;
+ {* Current pixel format. If format of bitmap is unknown, or bitmap is DDB,
+ value is pfDevice. Setting PixelFormat to any other format converts
+ bitmap to DIB, back to pfDevice converts bitmap to DDB again. Avoid
+ such conversations for large bitmaps or for numerous bitmaps in your
+ application to keep good performance. }
+ function BitsPerPixel: Integer;
+ {* Returns bits per pixel if possible. }
+ procedure Draw( DC: HDC; X, Y: Integer );
+ {* Draws bitmap to given device context. If bitmap is DIB, it is always
+ drawing using SetDIBitsToDevice API call, which does not require bitmap
+ handle (so, it is very sensible to call Dormant method to free correspondent
+ GDI resources). }
+ procedure StretchDraw( DC: HDC; const Rect: TRect );
+ {* Draws bitmap onto DC, stretching it to fit given rectangle Rect. }
+ procedure DrawTransparent( DC: HDC; X, Y: Integer; TranspColor: TColor );
+ {* Draws bitmap onto DC transparently, using TranspColor as transparent.
+ See function DesktopPixelFormat also. }
+ procedure StretchDrawTransparent( DC: HDC; const Rect: TRect; TranspColor: TColor );
+ {* Draws bitmap onto given rectangle of destination DC (with stretching it
+ to fit Rect) - transparently, using TranspColor as transparent.
+ See function DesktopPixelFormat also. }
+ procedure DrawMasked( DC: HDC; X, Y: Integer; Mask: HBitmap );
+ {* Draws bitmap to destination DC transparently by mask. It is possible
+ to pass as a mask handle of another TBitmap, previously converted to
+ monochrome mask using Convert2Mask method. }
+ procedure StretchDrawMasked( DC: HDC; const Rect: TRect; Mask: HBitmap );
+ {* Like DrawMasked, but with stretching image onto given rectangle. }
+ procedure Convert2Mask( TranspColor: TColor );
+ {* Converts bitmap to monochrome (mask) bitmap with TranspColor replaced
+ to clBlack and all other ones to clWhite. Such mask bitmap can be used
+ to draw original bitmap transparently, with given TranspColor as
+ transparent. (To preserve original bitmap, create new instance of
+ TBitmap and assign original bitmap to it). See also DrawTransparent and
+ StretchDrawTransparent methods. }
+ procedure Invert;
+ {* Obvious. }
+ property Canvas: PCanvas read GetCanvas;
+ {* Canvas can be used to draw onto bitmap. Whenever it is accessed, handle
+ is allocated for bitmap, if it is not yet (to make it possible
+ to select bitmap to display compatible device context). }
+ procedure RemoveCanvas;
+ {* Call this method to destroy Canvas and free GDI resources. }
+ property BkColor: TColor read fBkColor write SetBkColor;
+ {* Used to fill background for Bitmap, when its width or height is increased.
+ Although this value always synchronized with Canvas.Brush.Color, use it
+ instead if You do not use Canvas for drawing on bitmap surface. }
+ property Pixels[ X, Y: Integer ]: TColor read GetPixels write SetPixels;
+ {* Allows to obtain or change certain pixels of a bitmap. This method is
+ both for DIB and DDB bitmaps, and leads to allocate handle anyway. For
+ DIB bitmaps, it is possible to use property DIBPixels[ ] instead,
+ which is much faster and does not require in Handle. }
+ property ScanLineSize: Integer read GetScanLineSize;
+ {* Returns size of scan line in bytes. Use it to measure size of a single
+ ScanLine. To calculate increment value from first byte of ScanLine to
+ first byte of next ScanLine, use difference
+ ! Integer(ScanLine[1]-ScanLine[0])
+ (this is because bitmap can be oriented from bottom to top, so
+ step can be negative). }
+ property ScanLine[ Y: Integer ]: Pointer read GetScanLine;
+ {* Use ScanLine to access DIB bitmap pixels in memory to direct access it
+ fast. Take in attention, that for different pixel formats, different
+ bit counts are used to represent bitmap pixels. Also do not forget, that
+ for formats pf4bit and pf8bit, pixels actually are indices to palette
+ entries, and for formats pf16bit, pf24bit and pf32bit are actually
+ RGB values (for pf16bit B:5-G:6-R:5, for pf15bit B:5-G:5-R:5 (high order
+ bit not used), for pf24bit B:8-G:8-R:8, and for pf32bit high order byte
+ of TRGBQuad structure is not used). }
+ property DIBPixels[ X, Y: Integer ]: TColor read GetDIBPixels write SetDIBPixels;
+ {* Allows direct access to pixels of DIB bitmap, faster then Pixels[ ]
+ property. Access to read is slower for pf15bit, pf16bit formats (because
+ some conversation needed to translate packed RGB color to TColor). And
+ for write, operation performed most slower for pf4bit, pf8bit (searching
+ nearest color required) and fastest for pf24bit, pf32bit and pf1bit. }
+ property DIBPalEntryCount: Integer read GetDIBPalEntryCount;
+ {* Returns palette entries count for DIB image. Always returns 2 for pf1bit,
+ 16 for pf4bit, 256 for pf8bit and 0 for other pixel formats. }
+ property DIBPalEntries[ Idx: Integer ]: TColor read GetDIBPalEntries write
+ SetDIBPalEntries;
+ {* Provides direct access to DIB palette. }
+ function DIBPalNearestEntry( Color: TColor ): Integer;
+ {* Returns index of entry in DIB palette with color nearest (or matching)
+ to given one. }
+ property DIBBits: Pointer read fDIBBits;
+ {* This property is mainly for internal use. }
+ property DIBSize: Integer read fDIBSize;
+ {* Size of DIBBits array. }
+ property DIBHeader: PBitmapInfo read fDIBHeader;
+ {* This property is mainly for internal use. }
+ procedure DIBDrawRect( DC: HDC; X, Y: Integer; const R: TRect );
+ {* This procedure copies given rectangle to the target device context,
+ but only for DIB bitmap (using SetDIBBitsToDevice API call). }
+ procedure RotateRight;
+ {* Rotates bitmap right (90 degree). Bitmap must be DIB. If You definitevely
+ know format of a bitmap, use instead one of methods RotateRightMono,
+ RotateRight4bit, RotateRight8bit, RotateRight16bit or RotateRightTrueColor
+ - this will economy code. But if for most of formats such methods are
+ called, this can be more economy just to call always universal method
+ RotateRight. }
+ procedure RotateLeft;
+ {* Rotates bitmap left (90 degree). Bitmap must be DIB. If You definitevely
+ know format of a bitmap, use instead one of methods RotateLeftMono,
+ RotateLeft4bit, RotateLeft8bit, RotateLeft16bit or RotateLeftTrueColor
+ - this will economy code. But if for most of formats such methods are
+ called, this can be more economy just to call always universal method
+ RotateLeft. }
+ procedure RotateRightMono;
+ {* Rotates bitmat right, but only if bitmap is monochrome (pf1bit). }
+ procedure RotateLeftMono;
+ {* Rotates bitmap left, but only if bitmap is monochrome (pf1bit). }
+ procedure RotateRight4bit;
+ {* Rotates bitmap right, but only if PixelFormat is pf4bit. }
+ procedure RotateLeft4bit;
+ {* Rotates bitmap left, but only if PixelFormat is pf4bit. }
+ procedure RotateRight8bit;
+ {* Rotates bitmap right, but only if PixelFormat is pf8bit. }
+ procedure RotateLeft8bit;
+ {* Rotates bitmap left, but only if PixelFormat is pf8bit. }
+ procedure RotateRight16bit;
+ {* Rotates bitmap right, but only if PixelFormat is pf16bit. }
+ procedure RotateLeft16bit;
+ {* Rotates bitmap left, but only if PixelFormat is pf16bit. }
+ procedure RotateRightTrueColor;
+ {* Rotates bitmap right, but only if PixelFormat is pf24bit or pf32bit. }
+ procedure RotateLeftTrueColor;
+ {* Rotates bitmap left, but only if PixelFormat is pf24bit or pf32bit. }
+ procedure FlipVertical;
+ {* Flips bitmap vertically }
+ procedure FlipHorizontal;
+ {* Flips bitmap horizontally }
+ procedure CopyRect( const DstRect : TRect; SrcBmp : PBitmap; const SrcRect : TRect );
+ {* It is possible to use Canvas.CopyRect for such purpose, but if You
+ do not want use TCanvas, it is possible to copy rectangle from one
+ bitmap to another using this function. }
+ function CopyToClipboard: Boolean;
+ {* Copies bitmap to clipboard. }
+ function PasteFromClipboard: Boolean;
+ {* Takes CF_DIB format bitmap from clipboard and assigns it to the
+ TBitmap object. }
+ end;
+
+function Bits2PixelFormat( BitsPerPixel: Integer ): TPixelFormat;
+
+function NewBitmap( W, H: Integer ): PBitmap;
+{* Creates bitmap object of given size. If it is possible, do not change its
+ size (Width and Heigth) later - this can economy code a bit. See TBitmap. }
+function NewDIBBitmap( W, H: Integer; PixelFormat: TPixelFormat ): PBitmap;
+{* Creates DIB bitmap object of given size and pixel format. If it is possible,
+ do not change its size (Width and Heigth) later - this can economy code a bit.
+ See TBitmap. }
+function CalcScanLineSize( Header: PBitmapInfoHeader ): Integer;
+{* May be will be useful. }
+
+var
+ DefaultPixelFormat: TPixelFormat = pf32bit; //pf16bit;
+
+function LoadMappedBitmap( hInst: THandle; BmpResID: Integer; const Map: array of TColor )
+ : HBitmap;
+{* This function can be used to load bitmap and replace some it colors to
+ desired ones. This function especially useful when loaded by the such way
+ bitmap is used as toolbar bitmap - to replace some original colors to
+ system default colors. To use this function properly, the bitmap shoud
+ be prepared as 16-color bitmap, which uses only system colors. To do so,
+ create a new 16-color bitmap with needed dimensions in Borland Image Editor
+ and paste a bitmap image, copyed in another graphic tool, and then save it.
+ If this is not done, bitmap will not be loaded correctly! }
+function LoadMappedBitmapEx( MasterObj: PObj; hInst: THandle; BmpResName: PKOLChar;
+ const Map: array of TColor ): HBitmap;
+{* by Alex Pravdin: like LoadMappedBitmap, but much powerful. It uses
+ CreateMappedBitmapEx, so it understands any bitmap color format, including
+ pf24bit. Also, LoadMappedBitmapEx provides auto-destroying loaded resource
+ when MasterObj is destroyed. }
+function CreateMappedBitmap(Instance: THandle; Bitmap: Integer;
+ Flags: UINT; ColorMap: PColorMap; NumMaps: Integer): HBitmap; stdcall;
+{* Creates mapped bitmap replacing colors correspondently to the
+ ColorMap (each pare of colors defines color replaced and a color
+ used for replace it in the bitmap). See also CreateMappedBitmapEx. }
+function CreateMappedBitmapEx(Instance: THandle; BmpRsrcName: PKOLChar; Flags:
+ Cardinal; ColorMap: PColorMap; NumMaps: Integer): HBitmap;
+{* By Alex Pravdin.
+Creates mapped bitmap independently from bitmap color format (works
+correctly with bitmaps having format deeper than 8bit per pixel). }
+
+type
+ PIcon = ^TIcon;
+{ ----------------------------------------------------------------------
+ TIcon - icon image
+----------------------------------------------------------------------- }
+ TIcon = object( TObj )
+ {* Object type to incapsulate icon or cursor image. }
+ protected
+ {$IFDEF ICON_DIFF_WH}
+ FWidth: Integer;
+ FHeight: Integer;
+ {$ELSE}
+ FSize : Integer;
+ {$ENDIF}
+ FHandle: HIcon;
+ FShareIcon: Boolean;
+ procedure SetSize(const Value: Integer);
+ {$IFDEF ICON_DIFF_WH}
+ function GetIconSize: Integer;
+ {$ENDIF}
+ procedure SetHandle(const Value: HIcon);
+ function GetHotSpot: TPoint;
+ function GetEmpty: Boolean;
+ protected
+ destructor Destroy; virtual;
+ public
+ {$IFDEF ICONLOAD_PRESERVEBMPS}
+ ImgBmp, MskBmp : PBitmap;
+ Only_Bmp: Boolean;
+ {$ENDIF ICONLOAD_PRESERVEBMPS}
+ property Size : Integer read
+ {$IFDEF ICON_DIFF_WH}
+ GetIconSize
+ {$ELSE}
+ FSize
+ {$ENDIF}
+ write SetSize;
+ {* Icon dimension (width and/or height, which are equal to each other always). }
+ {$IFDEF ICON_DIFF_WH}
+ property Width: Integer read FWidth;
+ property Height: Integer read FHeight;
+ {$ENDIF}
+ property Handle : HIcon read FHandle write SetHandle;
+ {* Windows icon object handle. }
+ procedure SetHandleEx( NewHandle: HIcon );
+ {* Set Handle without changing Size (Width/Height). }
+ procedure Clear;
+ {* Clears icon, freeing image and allocated GDI resource (Handle). }
+ property Empty: Boolean read GetEmpty;
+ {* Returns True if icon is Empty. }
+ property ShareIcon : Boolean read FShareIcon write FShareIcon;
+ {* True, if icon object is shared and can not be deleted when TIcon object
+ is destroyed (set this flag is to True, if an icon is obtained from another
+ TIcon object, for example). }
+ property HotSpot : TPoint read GetHotSpot;
+ {* Hot spot point - for cursors. }
+ procedure Draw( DC : HDC; X, Y : Integer );
+ {* Draws icon onto given device context. Icon always is drawn transparently
+ using its transparency mask (stored internally in icon object). }
+ procedure StretchDraw( DC : HDC; Dest : TRect );
+ {* Draws icon onto given device context with stretching it to fit destination
+ rectangle. See also Draw. }
+ procedure LoadFromStream( Strm : PStream );
+ {* Loads icon from stream. If stream contains several icons (of
+ different dimentions), icon with the most appropriate size is loading. }
+ procedure LoadFromFile( const FileName : KOLString );
+ {* Load icon from file. If file contains several icons (of
+ different dimensions), icon with the most appropriate size is loading. }
+ procedure LoadFromResourceID( Inst: Integer; ResID: Integer; DesiredSize: Integer );
+ {* Loads icon from resource. To load system default icon, pass 0 as Inst and
+ one of followin values as ResID:
+ |<pre>
+ IDI_APPLICATION Default application icon.
+ IDI_ASTERISK Asterisk (used in informative messages).
+ IDI_EXCLAMATION Exclamation point (used in warning messages).
+ IDI_HAND Hand-shaped icon (used in serious warning messages).
+ IDI_QUESTION Question mark (used in prompting messages).
+ IDI_WINLOGO Windows logo.
+ |</pre> It is also possible to load icon from resources of another module,
+ if pass instance handle of loaded module as Inst parameter. }
+ procedure LoadFromResourceName( Inst: Integer; ResName: PKOLChar; DesiredSize: Integer );
+ {* Loads icon from resource. To load own application resource, pass
+ hInstance as Inst parameter. It is possible to load resource from
+ another module, if pass its instance handle as Inst. }
+ procedure LoadFromExecutable( const FileName: KOLString; IconIdx: Integer );
+ {* Loads icon from executable (exe or dll file). Always default sized icon
+ is loaded. It is possible also to get know how much icons are contained
+ in executable using gloabl function GetFileIconCount. To obtain icon of
+ another size, try to load given executable and use LoadFromResourceID
+ method. }
+ procedure SaveToStream( Strm : PStream );
+ {* Saves single icon to stream. To save icons with several different
+ dimensions, use global procedure SaveIcons2Stream. }
+ procedure SaveToFile( const FileName : KOLString );
+ {* Saves single icon to file. To save icons with several different
+ dimensions, use global procedure SaveIcons2File. }
+ function Convert2Bitmap( TranColor: TColor ): HBitmap;
+ {* Converts icon to bitmap, returning Windows GDI bitmap resource as
+ a result. It is possible later to assign returned bitmap handle to
+ Handle property of TBitmap object to use features of TBitmap.
+ Pass TranColor to replace transparent area of icon with given color. }
+ end;
+
+ procedure SaveIcons2Stream( const Icons : array of PIcon; Strm : PStream );
+ {* Saves several icons (of different dimentions) to stream. }
+ function SaveIcons2StreamEx( const BmpHandles: array of HBitmap; Strm: PStream ): Boolean;
+ {* Saves icons creating it from pairs of bitmaps and their masks.
+ BmpHandles array must contain pairs of bitmap handles, each pair
+ of color bitmap and mask bitmap of the same size. }
+ procedure SaveIcons2File( const Icons : array of PIcon; const FileName : KOLString );
+ {* Saves several icons (of different dimentions) to file. (Single file
+ with extension .ico can contain several different sized icon images
+ to use later one with the most appropriate size). }
+
+ function NewIcon: PIcon;
+ {* Creates new icon object, setting its Size to 32 by default. Created icon
+ is Empty. }
+ function GetFileIconCount( const FileName: KOLString ): Integer;
+ {* Returns number of icon resources stored in given (executable) file. }
+
+type
+ TIconHeader = packed record
+ idReserved: Word; (* Always set to 0 *)
+ idType: Word; (* Always set to 1 *)
+ idCount: Word; (* Number of icon images *)
+ (* immediately followed by idCount TIconDirEntries *)
+ end;
+
+ TIconDirEntry = packed record
+ bWidth: Byte; (* Width *)
+ bHeight: Byte; (* Height *)
+ bColorCount: Byte; (* Nr. of colors used *)
+ bReserved: Byte; (* not used, 0 *)
+ wPlanes: Word; (* not used, 0 *)
+ wBitCount: Word; (* not used, 0 *)
+ dwBytesInRes: Longint; (* total number of bytes in images *)
+ dwImageOffset: Longint;(* location of image from the beginning of file *)
+ end;
+
+function LoadImgIcon( RsrcName: PKOLChar; Size: Integer ): HIcon;
+{* Loads icon of specified size from the resource. }
+
+////////////////////////////////////////////////////////////////////////////////
+// UNIVERSAL CONTROL OBJECT //
+////////////////////////////////////////////////////////////////////////////////
+
+const
+ CM_EXECPROC = $8FFF;
+ CM_BASE = $B000;
+ CM_ACTIVATE = CM_BASE + 0;
+ CM_DEACTIVATE = CM_BASE + 1;
+ CM_ENTER = CM_BASE + 2;
+ CM_RELEASE = CM_BASE + 3;
+ CM_QUIT = CM_BASE + 4;
+ CM_COMMAND = CM_BASE + 5;
+ CM_MEASUREITEM = CM_BASE + 6;
+ CM_DRAWITEM = CM_BASE + 7;
+ CM_TRAYICON = CM_BASE + 8;
+ CM_INVALIDATE = CM_BASE + 9;
+ CM_UPDATE = CM_BASE + 10;
+ CM_NCUPDATE = CM_BASE + 11;
+ CM_SIZEPOS = CM_BASE + 12;
+ CM_SIZE = CM_BASE + 13;
+ CM_SETFOCUS = CM_BASE + 14;
+ CM_CBN_SELCHANGE = 15;
+
+ CM_UIACTIVATE = CM_BASE + 16;
+ CM_UIDEACTIVATE = CM_BASE + 17;
+ CM_PROCESS = CM_BASE + 18;
+ CM_SHOW = CM_BASE + 19;
+
+ CM_AUTOSIZE = CM_BASE + 20;
+ CM_MDIClientShowEdge = CM_BASE + 21;
+
+ CM_INVALIDATECHILD = CM_BASE + 22;
+ CM_FOCUSGRAPHCTL = CM_BASE + 23;
+
+ WM_SYNCPAINT = $88;
+
+ CN_BASE = $BC00;
+ CN_CHARTOITEM = CN_BASE + WM_CHARTOITEM;
+ CN_COMMAND = CN_BASE + WM_COMMAND;
+ CN_COMPAREITEM = CN_BASE + WM_COMPAREITEM;
+
+ CN_CTLCOLORMSGBOX = CN_BASE + WM_CTLCOLORMSGBOX;
+ CN_CTLCOLOREDIT = CN_BASE + WM_CTLCOLOREDIT;
+ CN_CTLCOLORLISTBOX = CN_BASE + WM_CTLCOLORLISTBOX;
+ CN_CTLCOLORBTN = CN_BASE + WM_CTLCOLORBTN;
+ CN_CTLCOLORDLG = CN_BASE + WM_CTLCOLORDLG;
+ CN_CTLCOLORSCROLLBAR = CN_BASE + WM_CTLCOLORSCROLLBAR;
+ CN_CTLCOLORSTATIC = CN_BASE + WM_CTLCOLORSTATIC;
+
+ CN_DELETEITEM = CN_BASE + WM_DELETEITEM;
+ CN_DRAWITEM = CN_BASE + WM_DRAWITEM;
+ CN_HSCROLL = CN_BASE + WM_HSCROLL;
+ CN_MEASUREITEM = CN_BASE + WM_MEASUREITEM;
+ CN_PARENTNOTIFY = CN_BASE + WM_PARENTNOTIFY;
+ CN_VKEYTOITEM = CN_BASE + WM_VKEYTOITEM;
+ CN_VSCROLL = CN_BASE + WM_VSCROLL;
+ CN_KEYDOWN = CN_BASE + WM_KEYDOWN;
+ CN_KEYUP = CN_BASE + WM_KEYUP;
+ CN_CHAR = CN_BASE + WM_CHAR;
+ CN_SYSKEYDOWN = CN_BASE + WM_SYSKEYDOWN;
+ CN_SYSCHAR = CN_BASE + WM_SYSCHAR;
+ CN_NOTIFY = CN_BASE + WM_NOTIFY;
+
+{$ENDIF WIN_GDI}
+const
+ ID_SELF: array[ 0..5 ] of KOLChar = ( 'S','E','L','F','_',#0 );
+ {* Identifier for window property "Self", stored directly in window, when
+ it is created. This property is used to [fast] find TControl object,
+ correspondent to given window handle (using API call GetProp). }
+
+{$IFDEF WIN_GDI}
+ ID_PREVPROC: array[ 0..9 ] of KOLChar = ( 'P','R','E','V','_','P','R','O','C',#0 );
+ {$IFNDEF USE_FLAGS}
+ RADIO_LAST: array[ 0..7 ] of KOLChar = ( 'R','A','D','_','L','S','T',#0 );
+ RADIO_1ST: array[ 0..7 ] of KOLChar = ( 'R','A','D','_','1','S','T',#0 );
+ {$ENDIF}
+ MIN_WND: array[ 0..7 ] of KOLChar = ( 'M','I','N','_','W','N','D',#0 );
+ DFLT_BTN: array[ 0..7 ] of KOLChar = ( 'D','F','L','T','_','B','T',#0 );
+ CNCL_BTN: array[ 0..7 ] of KOLChar = ( 'C','N','C','L','_','B','T',#0 );
+ DRAG_XY: array[ 0..7 ] of KOLChar = ( 'D','R','A','G','_','X','Y',#0 );
+ MDI_CHLDRN: array[ 0..10 ] of KOLChar = ( 'M','D','I','_','C','H','L','D','R','N',#0 );
+
+{$ENDIF WIN_GDI}
+const
+ MK_LBUTTON = 1;
+ MK_RBUTTON = 2;
+ MK_SHIFT = 4;
+ MK_CONTROL = 8;
+ MK_MBUTTON = $10;
+ MK_ALT = $20; // MK_ALT DEFINED
+ MK_LOCK = $40; // CAPS LOCK or SHIFT LOCK
+{$IFDEF WIN_GDI}
+
+{$IFNDEF NOT_USE_RICHEDIT}
+type
+ {$IFDEF UNICODE_CTRLS}
+ TCharFormat2 = TCharFormat2W;
+ {$ELSE}
+ {$IFDEF _D3orHigher}
+ TCharFormat2 = TCharFormat2A;
+ {$ELSE} // Delphi2
+ TCharFormat2 = packed record
+ cbSize: UINT;
+ dwMask: DWORD;
+ dwEffects: DWORD;
+ yHeight: Longint;
+ yOffset: Longint;
+ crTextColor: TColorRef;
+ bCharSet: Byte;
+ bPitchAndFamily: Byte;
+ szFaceName: array[0..LF_FACESIZE - 1] of KOLChar;
+ R2Bytes: Word;
+ wWeight: Word; { Font weight (LOGFONT value) }
+ sSpacing: Smallint; { Amount to space between letters }
+ crBackColor: TColorRef; { Background color }
+ lid: LCID; { Locale ID }
+ dwReserved: DWORD; { Reserved. Must be 0 }
+ sStyle: Smallint; { Style handle }
+ wKerning: Word; { Twip size above which to kern char pair }
+ bUnderlineType: Byte; { Underline type }
+ bAnimation: Byte; { Animated text like marching ants }
+ bRevAuthor: Byte; { Revision author index }
+ bReserved1: Byte;
+ end; {$ENDIF _D3orHigher}
+ {$ENDIF}
+
+ PParaFormat2 = ^TParaFormat2;
+ TParaFormat2 = packed record
+ cbSize: UINT;
+ dwMask: DWORD;
+ wNumbering: Word;
+ wReserved: Word;
+ dxStartIndent: Longint;
+ dxRightIndent: Longint;
+ dxOffset: Longint;
+ wAlignment: Word;
+ cTabCount: Smallint;
+ rgxTabs: array [0..MAX_TAB_STOPS - 1] of Longint;
+ dySpaceBefore: Longint; { Vertical spacing before para }
+ dySpaceAfter: Longint; { Vertical spacing after para }
+ dyLineSpacing: Longint; { Line spacing depending on Rule }
+ sStyle: Smallint; { Style handle }
+ bLineSpacingRule: Byte; { Rule for line spacing (see tom.doc) }
+ bCRC: Byte; { Reserved for CRC for rapid searching }
+ wShadingWeight: Word; { Shading in hundredths of a per cent }
+ wShadingStyle: Word; { Nibble 0: style, 1: cfpat, 2: cbpat }
+ wNumberingStart: Word; { Starting value for numbering }
+ wNumberingStyle: Word; { Alignment, roman/arabic, (), ), ., etc. }
+ wNumberingTab: Word; { Space bet 1st indent and 1st-line text }
+ wBorderSpace: Word; { Space between border and text (twips) }
+ wBorderWidth: Word; { Border pen width (twips) }
+ wBorders: Word; { Byte 0: bits specify which borders }
+ { Nibble 2: border style, 3: color index }
+ end;
+
+ TGetTextLengthEx = packed record
+ flags: DWORD; { flags (see GTL_XXX defines) }
+ codepage: UINT; { code page for translation (CP_ACP for default,
+ 1200 for Unicode }
+ end;
+
+const
+ PFM_SPACEBEFORE = $00000040;
+ PFM_SPACEAFTER = $00000080;
+ PFM_LINESPACING = $00000100;
+ PFM_STYLE = $00000400;
+ PFM_BORDER = $00000800; { (*) }
+ PFM_SHADING = $00001000; { (*) }
+ PFM_NUMBERINGSTYLE = $00002000; { (*) }
+ PFM_NUMBERINGTAB = $00004000; { (*) }
+ PFM_NUMBERINGSTART = $00008000; { (*) }
+
+ PFM_RTLPARA = $00010000;
+ PFM_KEEP = $00020000; { (*) }
+ PFM_KEEPNEXT = $00040000; { (*) }
+ PFM_PAGEBREAKBEFORE = $00080000; { (*) }
+ PFM_NOLINENUMBER = $00100000; { (*) }
+ PFM_NOWIDOWCONTROL = $00200000; { (*) }
+ PFM_DONOTHYPHEN = $00400000; { (*) }
+ PFM_SIDEBYSIDE = $00800000; { (*) }
+
+ PFM_TABLE = $c0000000; { (*) }
+ EM_REDO = WM_USER + 84;
+ EM_AUTOURLDETECT = WM_USER + 91;
+ EM_GETAUTOURLDETECT = WM_USER + 92;
+ CFM_UNDERLINETYPE = $00800000; { (*) }
+ CFM_HIDDEN = $0100; { (*) }
+ CFM_BACKCOLOR = $04000000;
+ CFE_AUTOBACKCOLOR = CFM_BACKCOLOR;
+ GTL_USECRLF = 1; { compute answer using CRLFs for paragraphs }
+ GTL_PRECISE = 2; { compute a precise answer }
+ GTL_CLOSE = 4; { fast computation of a "close" answer }
+ GTL_NUMCHARS = 8; { return the number of characters }
+ GTL_NUMBYTES = 16; { return the number of _bytes_ }
+ EM_GETTEXTLENGTHEX = WM_USER + 95;
+ EM_SETLANGOPTIONS = WM_USER + 120;
+ EM_GETLANGOPTIONS = WM_USER + 121;
+
+ EM_SETEDITSTYLE = $400 + 204;
+ EM_GETEDITSTYLE = $400 + 205;
+
+ SES_EMULATESYSEDIT = 1;
+ SES_BEEPONMAXTEXT = 2;
+ SES_EXTENDBACKCOLOR = 4;
+ SES_MAPCPS = 8;
+ SES_EMULATE10 = 16;
+ SES_USECRLF = 32;
+ SES_USEAIMM = 64;
+ SES_NOIME = 128;
+ SES_ALLOWBEEPS = 256;
+ SES_UPPERCASE = 512;
+ SES_LOWERCASE = 1024;
+ SES_NOINPUTSEQUENCECHK = 2048;
+ SES_BIDI = 4096;
+ SES_SCROLLONKILLFOCUS = 8192;
+ SES_XLTCRCRLFTOCR = 16384;
+
+ EM_GETSCROLLPOS = WM_USER + 221;
+ EM_SETSCROLLPOS = WM_USER + 222;
+ EM_GETZOOM = WM_USER + 224;
+ EM_SETZOOM = WM_USER + 225;
+{$ENDIF NOT_USE_RICHEDIT}
+{$ENDIF WIN_GDI}
+
+const
+ idx_fOnMessage = 0;
+ idx_fOldOnMessage = 1;
+ idx_fOnClick = 2;
+ idx_fOnMouseDown = 3;
+ idx_fOnMouseUp = 4;
+ idx_fOnMouseMove = 5;
+ idx_fOnMouseDblClk = 6;
+ idx_fOnMouseWheel = 7;
+ idx_fOnMouseEnter = 8;
+ idx_fOnMouseLeave = 9;
+ idx_fOnTestMouseOver = 10;
+ idx_fGraphCtlMouseEvent = 11;
+ idx_fMouseLeaveProc = 12;
+ idx_fOnScroll = 13;
+ idx_fOnChar = 14;
+ idx_fOnDeadChar = 15;
+ idx_fOnKeyUp = 16;
+ idx_fOnKeyDown = 17;
+ idx_fOnChangeCtl = 18;
+ idx_fOnEnter = 19;
+ idx_fOnLeave = 20;
+ idx_fLeave = 21;
+ idx_fOnPaint = 22;
+ idx_fOnPaint2 = 23;
+ idx_fOnPrepaint = 24;
+ idx_fOnPostPaint = 25;
+ idx_fPaintProc = 26;
+ idx_fOnEraseBkgnd = 27;
+ idx_fOnDrawItem = 28;
+ idx_fOnMeasureItem = 29;
+ idx_fDragCallback = 30;
+ idx_fOnSelChange = 31;
+ idx_fOnResize = 32;
+ idx_fOnHide = 33;
+ idx_fOnShow = 34;
+ idx_fOnClose = 35;
+ idx_fOnMove = 36;
+ idx_fOnMoving = 37;
+ idx_fOnHelp = 38;
+ idx_fOnQueryEndSession = 39;
+ idx_fOnMinimize = 40;
+ idx_fOnMaximize = 41;
+ idx_fOnRestore = 42;
+ idx_fOnLVCustomDraw = 43;
+ idx_fOnEndEditLVITem = 44;
+ idx_fOnLVData = 45;
+ idx_fOnCompareLVItems = 46;
+ idx_FOnLVStateChange = 47;
+ idx_fOnDeleteLVItem = 48;
+ idx_fOnColumnClick = 49;
+ idx_FOnSBBeforeScroll = 50;
+ idx_FOnSBScroll = 51;
+ idx_FOnDropDown = 52;
+ idx_FOnCloseUp = 53;
+ idx_FOnSplit = 54;
+ idx_FOnProgress = 55;
+ idx_FOnBitBtnDraw = 56;
+ idx_FOnTVBeginDrag = 57;
+ idx_FOnTVBeginEdit = 58;
+
+ idx_FOnTVEndEdit = 59;
+ idx_FOnTVExpanding = 60;
+ idx_FOnTVExpanded = 61;
+ idx_FOnTVSelChanging = 62;
+
+ idx_FOnTVDelete = 63;
+ idx_FOnDTPUserString = 64;
+ idx_FOnREInsModeChg = 65;
+ idx_FOnREOverURL = 66;
+ idx_FOnREURLClick = 67;
+ idx_fOnDropFiles = 68;
+
+ idx_LastEvent = 68;
+
+ idx_fWndFunc = 69;
+ idx_fDoInvalidate = 70;
+ idx_fOnDynHandlers = 71;
+ idx_fPass2DefProc = 72;
+ idx_fWndProcKeybd = 73;
+ idx_fControlClick = 74;
+ idx_fAutoSize = 75;
+ //{-2.95}//idx_fWndProcResizeFlicks
+ idx_fGotoControl = 76;
+ idx_fNotifyChild = 77;
+ idx_fScrollChildren = 78;
+ //idx_FBitBtnGetCaption
+ //idx_FBitBtnExtDraw
+ idx_fCreateWndExt = 79;
+ idx_fExMsgProc = 80;
+ idx_LastProc = 80;
+
+const
+ ANCHOR_LEFT = 1;
+ ANCHOR_RIGHT = 2;
+ ANCHOR_TOP = 4;
+ ANCHOR_BOTTOM = 8;
+ SELF_REQ_PAINT = 16;
+ PARENT_REQ_PAINT = 32;
+ MDI_NOT_AVAILABLE = 64;
+ MDI_DESTROYING = 128;
+
+type
+///////////////////////////////////////////
+{$ifndef _D6orHigher} //
+///////////////////////////////////////////
+ TMethod = packed record
+ {* Is defined here because using of VCL classes.pas unit is
+ not recommended in XCL. This record type is used often
+ to set/access event handlers, referring to a procedure
+ of object (usually to set such event to an ordinal
+ procedure setting Data field to nil. }
+ Code: Pointer; // Pointer to method code.
+ {* If used to fake assigning to event handler of type 'procedure
+ of object' with ordinal procedure pointer, use symbol '@'
+ before method:
+ |<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font face="Courier"><b>
+ | Method.Code := @MyProcedure;
+ |</b></font> }
+ Data: Pointer; // Pointer to object, owning the method.
+ {* To fake event of type 'procedure of object' with setting it to
+ ordinal procedure assign here NIL; }
+ end;
+ {* When assigning TMethod record to event handler, typecast it with
+ desired event type, e.g.:
+ |<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font face="Courier"><b>
+ | SomeObject.OnSomeEvent := TOnSomeEvent( Method );
+ |</b></font><br> }
+///////////////////////////////////////////
+{$endif} //
+///////////////////////////////////////////
+ PMethod = ^TMethod;
+ {* }
+
+ function MakeMethod( Data, Code: Pointer ): TMethod;
+ {* Help function to construct TMethod record. Can be useful to
+ assign regular type procedure/function as event handler for
+ event, defined as object method (do not forget, that in that
+ case it must have first dummy parameter to replace @Self,
+ passed in EAX to methods of object). }
+
+type
+ T3Style = ( F3_Maximize, F3_ClipChildren, F3_ClipSiblings,
+ F3_Disabled, F3_Visible, F3_Minimize,
+ F3_Child, F3_Popup );
+ T3Styles = Set of T3Style;
+
+ T2Style = ( F2_Tabstop, F2_Group, F2_Thickframe, F2_Sysmenu,
+ F2_HScroll, F2_VScroll, F2_Dlgframe, F2_Border );
+ T2Styles = Set of T2Style;
+
+ TStyle = packed record
+ CASE Integer OF
+ 1: (
+ f0_Style: Byte;
+ f1_Style: Byte;
+ f2_Style: T2Styles;
+ f3_Style: T3Styles;
+ );
+ 2: ( Value: DWORD; );
+ end;
+
+ T1Flag = ( G1_WordWrap, G1_PreventResize, G1_IconShared,
+ G1_IgnoreWndCaption, G1_SizeRedraw, G1_IsStaticControl,
+ G1_CanNotDoublebuf, G1_HasRadio ); //
+ T1Flags = Set of T1Flag;
+
+ T2Flag = ( G2_Transparent, G2_DoubleBuffered, G2_ClassicTransparent,
+ G2_Destroying, G2_BeginDestroying,
+ G2_ChangedPos, G2_ChangedW, G2_ChangedH ); //
+ T2Flags = Set of T2Flag;
+
+ T3Flag = ( G3_ClassicTransparent, G3_IsForm, G3_SizeGrip, G3_IsControl,
+ G3_IsApplet, G3_IsMDIChild, G3_Flat, G3_MouseInCtl ); //
+ T3Flags = Set of T3Flag;
+
+ T4Flag = ( G4_CreateHidden, G4_VisibleWOParent, G4_NotUseAlign,
+ G4_CreateVisible, G4_Pushed, G4_Checked, G4_Hot, G4_Pressed ); //
+ // use G4_Pushed also as KeyPreviewing for form
+ T4Flags = Set of T4Flag;
+
+ T5Flag = ( G5_IsButton, G5_IsBitBtn, G5_IsSplitter, G5_IsGroupbox,
+ G5_IsCommonCtl, G5_3ButtonPress, G5_EraseBkgnd, G5_IgnoreDefault );
+ T5Flags = Set of T5Flag;
+
+ T6Flag = ( G6_KeyPreview, G6_DefaultBtn, G6_CancelBtn, G6_Focused,
+ G6_GraphicCtl, G6_CtlClassNameChg, G6_RightClick, G6_Dragging );
+ T6Flags = Set of T6Flag;
+
+ PControl = ^TControl;
+ {* Type of pointer to TControl visual object. All
+ |<a href="kol_pas.htm#visual_objects_constructors">
+ constructing functions
+ |</a>
+ New[ControlName] are returning
+ pointer of this type. Do not forget about some difference
+ of using objects from using classes. Identifier Self for
+ methods of object is not of pointer type, and to pass
+ pointer to Self, it is necessary to pass @Self instead.
+ At the same time, to use pointer to object in 'WITH' operator,
+ it is necessary to apply suffix '^' to pointer to get know
+ to compiler, what do You want. }
+{$IFDEF WIN}
+ TWindowFunc = function( Sender: PControl; var Msg: TMsg; var Rslt: Integer )
+ : Boolean;
+ {* Event type to define custom extended message handlers (as pointers to
+ procedure entry points). Such handlers are usually defined like add-ons,
+ extending behaviour of certain controls and attached using AttachProc
+ method of TControl. If the handler detects, that it is necessary to stop
+ further message processing, it should return True. }
+{$ENDIF WIN}
+
+ TMouseButton = ( mbNone, mbLeft, mbRight, mbMiddle );
+ {* Available mouse buttons. mbNone is useful to get know, that
+ there were no mouse buttons pressed. }
+
+ TMouseEventData = packed Record
+ {* Record to pass it to mouse handling routines, assigned to OnMouseXXXX
+ events. }
+ Button: TMouseButton;
+ StopHandling: Boolean; // Set it to True in OnMouseXXXX event handler to
+ // stop further processing
+ R1, R2: Byte; // Not used
+ Shift : DWORD; // HiWord( Shift ) = zDelta in WM_MOUSEWHEEL
+ X, Y : SmallInt;
+ end;
+
+ TOnMouse = procedure( Sender: PControl; var Mouse: TMouseEventData ) of object;
+ {* Common mouse handling event type. }
+
+ TOnKey = procedure( Sender: PControl; var Key: Longint; Shift: DWORD ) of object;
+ {* Key events. Shift is a combination of flags MK_SHIFT, MK_CONTROL, MK_ALT.
+ (See GetShiftState funtion). }
+
+ TOnChar = procedure( Sender: PControl; var Key: KOLChar; Shift: DWORD ) of object;
+ {* Char event. Shift is a combination of flags MK_SHIFT, MK_CONTROL, MK_ALT. }
+
+ TTabKey = ( tkTab, tkLeftRight, tkUpDown, tkPageUpPageDn );
+ {* Available tabulating key groups. }
+ TTabKeys = Set of TTabKey;
+ {* Set of tabulating key groups, allowed to be used in with a control
+ (are installed by TControl.LookTabKey property). }
+
+{$IFDEF WIN}
+ TOnMessage = function( var Msg: TMsg; var Rslt: Integer ): Boolean of object;
+ {* Event type for events, which allows to extend behaviour of windowed controls
+ descendants using add-ons. }
+{$ENDIF WIN}
+
+ TOnEventAccept = procedure( Sender: PObj; var Accept: Boolean ) of object;
+ {* Event type for OnClose event. }
+ TCloseQueryReason = ( qClose, qShutdown, qLogoff );
+ {* Request reason type to call OnClose and OnQueryEndSession. }
+ TWindowState = ( wsNormal, wsMinimized, wsMaximized );
+ {* Avalable states of TControl's window object. }
+
+ TOnSplit = function( Sender: PControl; NewSize1, NewSize2: Integer ): Boolean of object;
+ {* Event type for OnSplit event handler, designed specially for splitter
+ control. Event handler must return True to accept new size of previous
+ (to splitter) control and new size of the rest of client area of parent. }
+
+ TOnTVBeginDrag = procedure( Sender: PControl; Item: THandle ) of object;
+ {* Event type for OnTVBeginDrag event (defined for tree view control). }
+ TOnTVBeginEdit = function( Sender: PControl; Item: THandle ): Boolean of object;
+ {* Event type for OnTVBeginEdit event (for tree view control). }
+ TOnTVEndEdit = function( Sender: PControl; Item: THandle; const NewTxt: KOL_String )
+ : Boolean of object;
+ {* Event type for TOnTVEndEdit event. }
+ TOnTVExpanding = function( Sender: PControl; Item: THandle; Expand: Boolean )
+ : Boolean of object;
+ {* Event type for TOnTVExpanding event. }
+ TOnTVExpanded = procedure( Sender: PControl; Item: THandle; Expand: Boolean )
+ of object;
+ {* Event type for OnTVExpanded event. }
+ TOnTVDelete = procedure( Sender: PControl; Item: THandle ) of object;
+ {* Event type for OnTVDelete event. }
+
+ //--------- by Sergey Shisminzev :
+ TOnTVSelChanging = function(Sender: PControl; oldItem, newItem: THandle): Boolean //~ss
+ of object;
+ {* When the handler returns False, selection is not changed. }
+ //-------------------------------
+ TOnDrag = function( Sender: PControl; ScrX, ScrY: Integer; var CursorShape: Integer;
+ var Stop: Boolean ): Boolean of object;
+ {* Event, called during dragging operation (it is initiated
+ with method Drag, where callback function of type TOnDrag is
+ passed as a parameter). Callback function receives Stop parameter True,
+ when operation is finishing. Otherwise, it can set it to True to force
+ finishing the operation (in such case, returning False means cancelling
+ drag operation, True - successful drag and in this last case callback is
+ no more called). During the operation, when input Stop value is False,
+ callback function can control Cursor shape, and return True, if the operation
+ can be finished successfully at the given ScrX, ScrY position.
+ ScrX, ScrY are screen coordinates of the mouse cursor. }
+
+{$IFDEF WIN}
+ TCreateParams = packed record
+ {* Record to pass it through CreateSubClass method. }
+ Caption: PKOLChar;
+ Style: cardinal;
+ ExStyle: cardinal;
+ X, Y: Integer;
+ Width, Height: Integer;
+ WndParent: HWnd;
+ Param: Pointer;
+ WindowClass: TWndClass;
+ WinClassName: array[0..63] of KOLChar;
+ end;
+
+ TCreateWndParams = packed Record
+ ExStyle: DWORD;
+ WinClassName: PKOLChar;
+ Caption: PKOLChar;
+ Style: DWORD;
+ X, Y, Width, Height: Integer;
+ WndParent: HWnd;
+ Menu: HMenu;
+ Inst: THandle;
+ Param: Pointer;
+ WinClsNamBuf: array[ 0..63 ] of KOLChar;
+ WindowClass: TWndClass;
+ end;
+
+ PCommandActions = ^TCommandActions;
+ TCommandActions = packed Record
+ aClear: procedure( Sender: PControl );
+ aAddText: procedure( Sender: PControl; const S: AnsiString );
+ aClick, aEnter, aLeave: WORD;
+ aChange: SmallInt; aSelChange: SmallInt;
+ aGetCount, aSetCount, aGetItemLength, aGetItemText, aSetItemText,
+ aGetItemData, aSetItemData: WORD;
+ aAddItem, aDeleteItem, aInsertItem: WORD;
+ aFindItem, aFindPartial: WORD;
+ bItem2Pos, bPos2Item: BYTE;
+ aGetSelCount, aGetSelected, aGetSelRange,
+ aGetCurrent,
+ aSetSelected, aSetCurrent, aSetSelRange, aExSetSelRange,
+ aGetSelection, aReplaceSel: WORD;
+ aTextAlignLeft, aTextAlignRight, aTextAlignCenter: WORD;
+ bTextAlignMask: Byte;
+ bVertAlignTop, bVertAlignCenter, bVertAlignBottom: Byte;
+ aDir, aSetLimit: Word; aSetImgList: Word;
+ aSetBkColor: Word;
+ aItem2XY: Word;
+ end;
+
+ {$IFDEF COMMANDACTIONS_OBJ}
+ PCommandActionsObj = ^TCommandActionsObj;
+ TCommandActionsObj = object(TObj)
+ aClear: procedure( Sender: PControl );
+ aAddText: procedure( Sender: PControl; const S: KOLString );
+ aClick, aEnter, aLeave: WORD;
+ aChange: SmallInt; aSelChange: SmallInt;
+ aGetCount, aSetCount, aGetItemLength, aGetItemText, aSetItemText,
+ aGetItemData, aSetItemData: WORD;
+ aAddItem, aDeleteItem, aInsertItem: WORD;
+ aFindItem, aFindPartial: WORD;
+ bItem2Pos, bPos2Item: BYTE;
+ aGetSelCount, aGetSelected, aGetSelRange,
+ aGetCurrent,
+ aSetSelected, aSetCurrent, aSetSelRange, aExSetSelRange,
+ aGetSelection, aReplaceSel: WORD;
+ aTextAlignLeft, aTextAlignRight, aTextAlignCenter: WORD;
+ bTextAlignMask: Byte;
+ bVertAlignTop, bVertAlignCenter, bVertAlignBottom: Byte;
+ aDir, aSetLimit: Word; aSetImgList: Word;
+ aSetBkColor: Word;
+ aItem2XY: Word;
+ fIndexInActions: Integer;
+ destructor Destroy; virtual;
+ end;
+ {$ENDIF}
+{$ENDIF WIN}
+
+ TTextAlign = ( taLeft, taRight, taCenter );
+ {* Text alignments available. }
+ TRichTextAlign = ( raLeft, raRight, raCenter,
+ // all other are only set but can not be displayed:
+ raJustify, // displayed like raLeft (though stored normally)
+ raInterLetter, raScaled, raGlyphs, raSnapGrid );
+ {* Text alignment styles, available for RichEdit control. }
+ TVerticalAlign = ( vaTop, vaCenter, vaBottom );
+ {* Vertical alignments available. }
+ TControlAlign = ( caNone, caLeft, caTop, caRight, caBottom, caClient );
+ {* Control alignments available. }
+ TAligning = (oaWaitAlign,oaFromSelf,oaAligning);
+ TAlignings = set of TAligning;
+
+ TBitBtnOption = ( bboImageList,
+ bboNoBorder,
+ bboNoCaption,
+ bboFixed,
+ bboFocusRect );
+ {* Options available for NewBitBtn. }
+ TBitBtnOptions = set of TBitBtnOption;
+ {* Set of options, available for NewBitBtn. }
+ TGlyphLayout = ( glyphLeft, glyphTop, glyphRight, glyphBottom, glyphOver );
+ {* Layout of glyph (for NewBitBtn). Layout glyphOver means that text is
+ drawn over glyph. }
+ TOnBitBtnDraw = function( Sender: PControl; BtnState: Integer ): Boolean of object;
+ {* Event type for TControl.OnBitBtnDraw event (which is called just before
+ drawing the BitBtn). If handler returns True, there are no drawing occure.
+ BtnState, passed to a handler, determines current button state and can
+ be following: 0 - not pressed, 1 - pressed, 2 - disabled, 3 - focused.
+ Value 4 is reserved for highlight state (then mouse is over it), but
+ highlighting is provided only if property Flat is set to True (or one
+ of events OnMouseEnter / OnMouseLeave is assigned to something). }
+
+ TListViewStyle = ( lvsIcon, lvsSmallIcon, lvsList, lvsDetail, lvsDetailNoHeader );
+ {* Styles of view for ListView control (see NewListVew). }
+
+ TListViewItemStates = ( lvisFocus, lvisSelect, lvisBlend, lvisHighlight );
+ TListViewItemState = Set of TListViewItemStates;
+ TListViewOption = (
+ lvoIconLeft, // in lvsIcon, lvsSmallIcon place icon left from text (rather then top)
+ lvoAutoArrange, // keep icons auto arranged in lvsIcon and lvsSmallIcon view
+ lvoButton, // icons look like buttons in lvsIcon view
+ lvoEditLabel, // allows edit labels inplace (first column #0 text)
+ lvoNoLabelWrap, // item text on a single line in lvsIcon view (by default, item text may wrap in lvsIcon view).
+ lvoNoScroll, // obvious
+ lvoNoSortHeader, // click on header button does not lead to sort items
+ lvoHideSel, // hide selection when not in focus
+ lvoMultiselect, // allow to select multiple items
+ lvoSortAscending,
+ lvoSortDescending,
+ // extended styles (not documented in my Win32.hlp :( , got from VCL source:
+ lvoGridLines,
+ lvoSubItemImages,
+ lvoCheckBoxes,
+ lvoTrackSelect,
+ lvoHeaderDragDrop,
+ lvoRowSelect,
+ lvoOneClickActivate,
+ lvoTwoClickActivate,
+ lvoFlatsb,
+ lvoRegional,
+ lvoInfoTip,
+ lvoUnderlineHot,
+ lvoMultiWorkares,
+ // virtual list view style:
+ lvoOwnerData,
+ // custom draw style:
+ lvoOwnerDrawFixed
+ );
+ TListViewOptions = Set of TListViewOption;
+
+ TOnEditLVItem = function( Sender: PControl; Idx, Col: Integer; NewText: PKOL_Char ): Boolean
+ of object;
+ {* Event type for OnEndEditLVItem. Return True in handler to accept new text value. }
+ TOnDeleteLVItem = procedure( Sender: PControl; Idx: Integer ) of object;
+ {* Event type for OnDeleteLVItem event. }
+ TOnLVData = procedure( Sender: PControl; Idx, SubItem: Integer;
+ var Txt: KOL_String; var ImgIdx: Integer; var State: DWORD;
+ var Store: Boolean ) of object;
+ {* Event type for OnLVData event. Used to provide virtual list view control
+ (i.e. having lvoOwnerData style) with actual data on request. Use parameter
+ Store as a flag if control should store obtained data by itself or not. }
+ {$IFDEF ENABLE_DEPRECATED}
+ {$DEFINE interface_1} {$I KOL_deprecated.inc} {$UNDEF interface_1}
+ {$ENDIF DISABLE_DEPRECATED}
+ TOnCompareLVItems = function( Sender: PControl; Idx1, Idx2: Integer ): Integer
+ of object;
+ {* Event type to compare two items of the list view (while sorting it). }
+ TOnLVColumnClick = procedure( Sender: PControl; Idx: Integer ) of object;
+ {* Event type for OnColumnClick event. }
+ TOnLVStateChange = procedure( Sender: PControl; IdxFrom, IdxTo: Integer; OldState, NewState: DWORD )
+ of object;
+ {* Event type for OnLVStateChange event, called in responce to select/unselect
+ a single item or items range in list view control). }
+
+ TDrawActions = ( odaEntire, odaFocus, odaSelect );
+ TDrawAction = Set of TDrawActions;
+ TDrawStates = ( odsSelected, odsGrayed, odsDisabled, odsChecked, odsFocused,
+ odsDefault, odsHotlist, odsInactive,
+ odsNoAccel, odsNoFocusRect,
+ ods400reserved, ods800reserved,
+ odsComboboxEdit,
+ // specific for common controls:
+ odsMarked, odsIndeterminate );
+ {* Possible draw states.
+ |<br>odsSelected - The menu item's status is selected.
+ |<br>odsGrayed - The item is to be grayed. This bit is used only in a menu.
+ |<br>odsDisabled - The item is to be drawn as disabled.
+ |<br>odsChecked - The menu item is to be checked. This bit is used only in
+ a menu.
+ |<br>odsFocused - The item has the keyboard focus.
+ |<br>odsDefault - The item is the default item.
+ |<br>odsHotList - <b>Windows 98, Windows 2000:</b> The item is being
+ hot-tracked, that is, the item will be highlighted when
+ the mouse is on the item.
+ |<br>odsInactive - <b>Windows 98, Windows 2000:</b> The item is inactive
+ and the window associated with the menu is inactive.
+ |<br>odsNoAccel - <b>Windows 2000:</b> The control is drawn without the
+ keyboard accelerator cues.
+ |<br>odsNoFocusRect - <b>Windows 2000:</b> The control is drawn without
+ focus indicator cues.
+ |<br>odsComboboxEdit - The drawing takes place in the selection field
+ (edit control) of an owner-drawn combo box.
+ |<br>odsMarked - for Common controls only. The item is marked. The meaning
+ of this is up to the implementation.
+ |<br>odsIndeterminate - for Common Controls only. The item is in an
+ indeterminate state. }
+ TDrawState = Set of TDrawStates;
+ {* Set of possible draw states. }
+ TOnDrawItem = function( Sender: PObj; DC: HDC; const Rect: TRect; ItemIdx: Integer;
+ DrawAction: TDrawAction; ItemState: TDrawState ): Boolean of object;
+ {* Event type for OnDrawItem event (applied to list box, combo box, list view). }
+ TOnMeasureItem = function( Sender: PObj; Idx: Integer ): Integer of object;
+ {* Event type for OnMeasureItem event. The event handler must return height of list box
+ item as a result. }
+ TGetLVItemPart = ( lvipBounds, lvipIcon, lvipLabel, lvupIconAndLabel );
+ {* }
+ TWherePosLVItem = ( lvwpOnIcon, lvwpOnLabel, lvwpOnStateIcon, lvwpOnColumn,
+ lvwpOnItem );
+ {* }
+
+ TOnLVCustomDraw = function( Sender: PControl; DC: HDC; Stage: DWORD;
+ ItemIdx, SubItemIdx: Integer; const Rect: TRect;
+ ItemState: TDrawState; var TextColor, BackColor: TColor )
+ : DWORD of object;
+ {* Event type for OnLVCustomDraw event. }
+
+ TOnPaint = procedure( Sender: PControl; DC: HDC ) of object;
+ TPaintProc = procedure( DC: HDC ) of object;
+
+ TGradientStyle = ( gsVertical, gsHorizontal, gsRectangle, gsElliptic, gsRombic,
+ gsTopToBottom, gsBottomToTop );
+ {* Gradient fill styles. See also TGradientLayout. }
+ TGradientLayout = ( glTopLeft, glTop, glTopRight,
+ glLeft, glCenter, glRight,
+ glBottomLeft, glBottom, glBottomRight );
+ {* Position of starting line / point for gradient filling. Depending on
+ TGradientStyle, means either position of first line of first rectangle
+ (ellipse) to be expanded in a loop to fit entire gradient panel area. }
+
+ TEditOption = ( eoNoHScroll, eoNoVScroll, eoLowercase, eoMultiline,
+ eoNoHideSel, eoOemConvert, eoPassword, eoReadonly,
+ eoUpperCase, eoWantReturn, eoWantTab, eoNumber );
+ {* Available edit options.
+ |<br> Please note, that eoWantTab option just removes TAB key from a list
+ of keys available to tabulate from the edit control. To provide insertion
+ of tabulating key, do so in TControl.OnChar event handler. Sorry for
+ inconvenience, but this is because such behaviour is not must in all cases.
+ See also TControl.EditTabChar property. }
+ TEditOptions = Set of TEditOption;
+ {* Set of available edit options. }
+
+ TEditPositions = packed record
+ SelStart: Integer;
+ SelLength: Integer;
+ TopLine: Integer;
+ TopColumn: Integer;
+ ScrollPos: TPoint;
+ RestoreScroll: Boolean;
+ end;
+
+ TRichFmtArea = ( raSelection, raWord, raAll );
+ {* Characters formatting area for RichEdit. }
+ TRETextFormat = ( reRTF, reText, rePlainRTF, reRTFNoObjs, rePlainRTFNoObjs,
+ reTextized, reUnicode, reTextUnicode );
+ {* Available formats for transfer RichEdit text using property
+ TControl.RE_Text.
+ |<pre>
+ reRTF - normal rich text (no transformations)
+ reText - plain text only (without OLE objects)
+ reTextized - plain text with text representation of COM objects
+ rePlainRTF - reRTF without language-specific keywords
+ reRTFNoObjs - reRTF without OLE objects
+ rePlainRTFNoObjs - rePlainRTF without OLE objects
+ reUnicode - stream is 2-byte Unicode characters rather then 1-byte Ansi
+ |</pre> }
+ TRichUnderline = ( ruSingle, ruWord, ruDouble, ruDotted,
+ //all other - only for RichEditv3.0:
+ ruDash, ruDashDot, ruDashDotDot, ruWave, ruThick, ruHairLine );
+ {* Rich text exteded underline styles (available only for RichEdit v2.0,
+ and even for RichEdit v2.0 additional styles can not displayed - but
+ ruDotted under Windows2000 is working). }
+ TRichTextSizes = ( rtsNoUseCRLF, rtsNoPrecise, rtsClose, rtsBytes );
+ {* Options to calculate size of rich text. Available only for RichEdit2.0
+ or higher. }
+ TRichTextSize = set of TRichTextSizes;
+ {* Set of all available optioins to calculate rich text size using
+ property TControl.RE_TextSize[ options ]. }
+ TRichNumbering = ( rnNone, rnBullets, rnArabic, rnLLetter, rnULetter,
+ rnLRoman, rnURoman );
+ {* Advanced numbering styles for paragraph (RichEdit).
+ |<pre>
+ rnNone - no numbering
+ rnBullets - bullets only
+ rnArabic - 1, 2, 3, 4, ...
+ rnLLetter - a, b, c, d, ...
+ rnULetter - A, B, C, D, ...
+ rnLRoman - i, ii, iii, iv, ...
+ rnURoman - I, II, III, IV, ...
+ rnNoNumber - do not show any numbers (but numbering is taking place).
+ |</pre> }
+ TRichNumBrackets = ( rnbRight, rnbBoth, rnbPeriod, rnbPlain, rnbNoNumber );
+ {* Brackets around number:
+ |<pre>
+ rnbRight - 1) 2) 3) - this is default !
+ rnbBoth - (1) (2) (3)
+ rnbPeriod - 1. 2. 3.
+ rnbPlain - 1 2 3
+ |</pre> }
+ TBorderEdge = (beLeft, beTop, beRight, beBottom);
+ {* Borders of rectangle. }
+
+ {$IFNDEF NOT_USE_RICHEDIT}
+ {$IFDEF _D3orHigher}
+ TCharFormat = TCharFormat2;
+ {$ENDIF _D3orHigher}
+ PCharFormat = ^TCharFormat;
+ TParaFormat = TParaFormat2;
+ {$ENDIF NOT_USE_RICHEDIT}
+
+ TOnTestMouseOver = function( Sender: PControl ): Boolean of object;
+ {* Event type for TControl.OnTestMouseOver event. The handler should
+ return True, if it detects if the mouse is over control. }
+
+ TEdgeStyle = ( esRaised, esLowered, esNone, esTransparent, esSolid );
+ {* Edge styles (for panel - see NewPanel).
+ esTransparent and esSolid - special styles equivalent to esNone
+ except GRushControls are used via USE_GRUSH symbol (ToGRush.pas) }
+
+ TListOption = ( loNoHideScroll, loNoExtendSel, loMultiColumn, loMultiSelect,
+ loNoIntegralHeight, loNoSel, loSort, loTabstops,
+ loNoStrings, loNoData, loOwnerDrawFixed, loOwnerDrawVariable,
+ loHScroll );
+ {* Options for ListBox (see NewListbox).
+ To use loHScroll, you also have to send LB_SETHORIZONTALEXTENT with a
+ maximum width of a line in pixels (wParam)! }
+ TListOptions = Set of TListOption;
+ {* Set of available options for Listbox. }
+
+ TComboOption = ( coReadOnly, coNoHScroll, coAlwaysVScroll, coLowerCase,
+ coNoIntegralHeight, coOemConvert, coSort, coUpperCase,
+ coOwnerDrawFixed, coOwnerDrawVariable, coSimple );
+ {* Options for combobox. }
+ TComboOptions = Set of TComboOption;
+ {* Set of options available for combobox. }
+
+ TProgressbarOption = ( pboVertical, pboSmooth );
+ {* Options for progress bar. }
+ TProgressbarOptions = set of TProgressbarOption;
+ {* Set of options available for progress bar. }
+
+ TTreeViewOption = ( tvoNoLines, tvoLinesRoot, tvoNoButtons, tvoEditLabels, tvoHideSel,
+ tvoDragDrop, tvoNoTooltips, tvoCheckBoxes, tvoTrackSelect,
+ tvoSingleExpand, tvoInfoTip, tvoFullRowSelect, tvoNoScroll,
+ tvoNonEvenHeight );
+ {* Tree view options. }
+ TTreeViewOptions = set of TTreeViewOption;
+ {* Set of tree view options. }
+
+ TTabControlOption = ( tcoButtons, tcoFixedWidth, tcoFocusTabs,
+ tcoIconLeft, tcoLabelLeft,
+ tcoMultiline, tcoMultiselect, tcoFitRows, tcoScrollOpposite,
+ tcoBottom, tcoVertical, tcoFlat, tcoHotTrack, tcoBorder,
+ tcoOwnerDrawFixed );
+ {* Options, available for TabControl. }
+ TTabControlOptions = set of TTabControlOption;
+ {* Set of options, available for TAbControl during its creation (by
+ NewTabControl function). }
+
+ TToolbarOption = ( tboTextRight, tboTextBottom, tboFlat, tboTransparent,
+ tboWrapable, tboNoDivider, tbo3DBorder, tboCustomErase );
+ {* Toolbar options. When tboFlat is set and toolbar is placed onto panel,
+ set its property Transparent to TRUE to provide its correct view. }
+ TToolbarOptions = Set of TToolbarOption;
+ {* Set of toolbar options. }
+ TOnToolbarButtonClick = procedure( Sender: PControl; BtnID: Integer ) of object;
+ {* Special event type to handle separate toolbar buttons click events. }
+ TOnTBCustomDraw = function( Sender: PControl; var NMCD: TNMTBCustomDraw ): Integer of object;
+ {* Event type for OnTBCustomDraw event. }
+
+ TDateTimePickerOption = ( dtpoTime, dtpoDateLong, dtpoUpDown, dtpoRightAlign,
+ dtpoShowNone, dtpoParseInput );
+ {* }
+ TDateTimePickerOptions = set of TDateTimePickerOption;
+ {* }
+ TDTParseInputEvent = procedure(Sender: PControl; const UserString: KOLString;
+ var DateAndTime: TDateTime; var AllowChange: Boolean) of object;
+ {* }
+ TDateTimeRange = packed record
+ FromDate, ToDate: TDateTime;
+ end;
+ {* }
+ TDateTimePickerColor = ( dtpcBackground, dtpcText, dtpcTitleBk,
+ dtpcTitleText, dtpcMonthBk, dtpcTrailingText );
+ {MCSC_BACKGROUND = 0; // the background color (between months)
+ MCSC_TEXT = 1; // the dates
+ MCSC_TITLEBK = 2; // background of the title
+ MCSC_TITLETEXT = 3;
+ MCSC_MONTHBK = 4; // background within the month cal
+ MCSC_TRAILINGTEXT = 5; // the text color of header & trailing days}
+
+ TOnDropFiles = procedure( Sender: PControl; const FileList: KOL_String; const Pt: TPoint ) of object;
+ {* An event type for OnDropFiles event. When the event is occur, FileList
+ parameter contains a list of files dropped. File names in a list are
+ separated with #13 character. This allows You to assign it to TStrList
+ object using its property Text (for example):
+ ! procedure TSomeObject.DropFiles( Sender: PControl; const FileList: AnsiString;
+ ! const Pt: TPoint ); )
+ ! var FList: PStrList;
+ ! I: Integer;
+ ! begin
+ ! FList := NewStrList;
+ ! FList.Text := FileList;
+ ! for I := 0 to FList.Count-1 do
+ ! begin
+ ! // do something with FList.Items[ I ]
+ ! end;
+ ! FList.Free;
+ ! end; }
+
+ TScrollerBar = ( sbHorizontal, sbVertical );
+ TScrollerBars = set of TScrollerBar;
+
+ TOnScroll = procedure( Sender: PControl; Bar: TScrollerBar; ScrollCmd: DWORD;
+ ThumbPos: DWORD ) of object;
+
+ TOnHelp = procedure( var Sender: PControl; var Context: Integer; var Popup: Boolean )
+ of object;
+
+ TOnSBBeforeScroll =
+ procedure(
+ Sender: PControl; OldPos, NewPos: Integer; Cmd: Word;
+ var AllowChange: Boolean) of object;
+ TOnSBScroll = procedure(Sender: PControl; Cmd: Word) of object;
+
+{$IFDEF WIN_GDI}
+ TOnGraphCtlMouse = procedure( var Msg: TMsg ) of object;
+{$ENDIF WIN_GDI}
+ TTriStateCheck = (tsUnchecked{=0}, tsChecked{=1}, tsIndeterminate{=2});
+
+{$IFDEF _X_}
+ //---- in GTK+, each type of widget requieres its own getcaption/setcaption call
+ TGetCaption = FUNCTION( Ctl: PControl ): KOLString;
+ TSetCaption = PROCEDURE( Ctl: PControl; CONST Value: KOLString );
+
+ {$IFDEF GTK}
+ //---- in GTK+, to allow setting absolute position for children,
+ // we should use one of special clients like gtk_fixed, gtk_layout
+ TGetClientArea = FUNCTION( Ctl: PControl ): PGtkWidget;
+ TChildSetPos = PROCEDURE( Ctl, Chld: PControl; x, y: Integer );
+ {$ENDIF GTK}
+{$ENDIF _X_}
+
+ TFormInitFunc = function(Form: PControl): PControl;
+ TFormInitFunc1 = function(Form: PControl; intParam: Integer): PControl;
+ TFormInitFuncArray = array[0..65535] of TFormInitFunc;
+ TFormInitFuncArray1 = array[0..65535] of TFormInitFunc1;
+ PFormInitFuncArray = ^TFormInitFuncArray;
+ PFormInitFuncArray1 = ^TFormInitFuncArray1;
+
+ TSmallIntArray = array[0..65535] of SmallInt;
+ PSmallIntArray = ^TSmallIntArray;
+
+ PPControl = ^PControl;
+
+ {$IFDEF USE_MHTOOLTIP}
+ {$DEFINE pre_interface}
+ PMHHint = ^TMHHint;
+ TKOLMHHint = PMHHint;
+ {$UNDEF pre_interface}
+ {$ENDIF}
+
+ TOnWndFunc = function( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+ TProcSender = procedure( Sender: PObj );
+ TOnGotoControl = function( Self_: PControl; Key: DWORD; CheckOnly: Boolean ): Boolean;
+
+ PEvents = ^TEvents;
+ TEvents = record
+ CASE Integer OF
+ 1:(
+ //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ //................... most common events ...................................
+ fOnMessage: TOnMessage;
+ fOldOnMessage: TOnMessage; // for applet only but...
+ fOnClick: TOnEvent;
+ fOnMouseDown: TOnMouse; // CAUTION!!! Order of mouse event handlers is important. ____
+ fOnMouseUp: TOnMouse; //
+ fOnMouseMove: TOnMouse; //
+ fOnMouseDblClk: TOnMouse; //
+ fOnMouseWheel: TOnMouse; //_____________________________________________________//
+
+ fOnMouseEnter: TOnEvent;
+ fOnMouseLeave: TOnEvent;
+ fOnTestMouseOver: TOnTestMouseOver; // mainly for bitbtn but...
+ fGraphCtlMouseEvent: TOnGraphCtlMouse;
+ fMouseLeaveProc: TOnEvent;
+ fOnScroll: TOnScroll;
+
+ fOnChar: TOnChar;
+ fOnDeadChar: TOnChar;
+ fOnKeyUp: TOnKey;
+ fOnKeyDown: TOnKey;
+
+ fOnChangeCtl: TOnEvent;
+ fOnEnter: TOnEvent;
+ fOnLeave: TOnEvent;
+ fLeave: TOnEvent;
+
+ fOnPaint: TOnPaint;
+ fOnPaint2: TOnPaint;
+ fOnPrepaint: TOnPaint;
+ fOnPostPaint: TOnPaint;
+ fPaintProc: TPaintProc;
+ fOnEraseBkgnd: TOnPaint;
+ fOnDrawItem: TOnDrawItem;
+ fOnMeasureItem: TOnMeasureItem;
+
+ fDragCallback: TOnDrag;
+
+ fOnSelChange: TOnEvent;
+ fOnResize: TOnEvent;
+
+ fOnHide: TOnEvent;
+ fOnShow: TOnEvent;
+
+ fOnClose: TOnEventAccept; // mainly for form but...
+
+ fOnMove: TOnEvent;
+ fOnMoving: TOnEventMoving;
+ fOnHelp: TOnHelp;
+ //................... other events .........................................
+ fOnQueryEndSession: TOnEventAccept;
+
+ //----- order of following 3 events important: // for form only ?
+ fOnMinimize: TOnEvent; //
+ fOnMaximize: TOnEvent; //
+ fOnRestore: TOnEvent; //
+ //---------------------------------------------//
+
+ fOnLVCustomDraw: TOnLVCustomDraw;
+ fOnEndEditLVItem: TOnEditLVItem;
+ fOnLVData: TOnLVData;
+ fOnCompareLVItems: TOnCompareLVItems;
+ FOnLVStateChange: TOnLVStateChange;
+ fOnDeleteLVItem: TOnDeleteLVItem;
+ fOnColumnClick: TOnLVColumnClick;
+
+ FOnSBBeforeScroll: TOnSBBeforeScroll;
+ FOnSBScroll: TOnSBScroll;
+
+ FOnDropDown: TOnEvent;
+ FOnCloseUp: TOnEvent;
+
+ FOnSplit: TOnSplit;
+
+ FOnProgress: TOnEvent;
+
+ FOnBitBtnDraw: TOnBitBtnDraw;
+
+ FOnTVBeginDrag: TOnTVBeginDrag;
+ FOnTVBeginEdit: TOnTVBeginEdit;
+ FOnTVEndEdit: TOnTVEndEdit;
+ FOnTVExpanding: TOnTVExpanding;
+ FOnTVExpanded: TOnTVExpanded;
+ FOnTVSelChanging: TOnTVSelChanging;
+
+ FOnTVDelete: TOnTVDelete;
+
+ FOnDTPUserString: TDTParseInputEvent;
+
+ FOnREInsModeChg: TOnEvent;
+ FOnREOverURL: TOnEvent;
+ FOnREURLClick: TOnEvent;
+ fOnDropFiles: TOnDropFiles;
+ );
+ 2: ( MethodEvents: array[ 0..idx_LastEvent ] of TMethod;
+ );
+ end;
+
+ TProcedures = record
+ CASE Integer OF
+ 1: (
+ //..........................................................................
+ fWndFunc: Pointer;
+ fDoInvalidate: TProcSender;
+ fOnDynHandlers: TWindowFunc;
+ fPass2DefProc: TOnWndFunc;
+ fWndProcKeybd: TOnWndFunc;
+ fControlClick: TProcSender; //
+ fAutoSize: TProcSender;
+ //{-2.95}//fWndProcResizeFlicks: TOnWndFunc;
+ fGotoControl: TOnGotoControl;
+ {* Initially pointer to WndFunc. For MDI child window, points to DefMDIChildProc. }
+ fNotifyChild: procedure( Self_, Child: PControl );
+ fScrollChildren: procedure( Self_: PControl );
+ fCreateWndExt: procedure( Sender: PControl );
+ fExMsgProc: function( Applet: PControl; var Msg: TMsg ): Boolean;
+ {* Additional message handler called directly from Applet.ProcessMessage.
+ Used to call TranslateMDISysAccel API function for MDI application. }
+ );
+ 2: ( Procedures: array[ 0..idx_LastProc-idx_LastEvent-1 ] of Pointer;
+ );
+ end;
+
+ // data fields of TControl which are certain for different kinds of control
+ // -- so these can be alternated using variant record type to economy run time
+ // size of TControl object instance
+ TDataFields = packed record
+ fCurrentControl: PControl; //---- sometimes it is used for a parent control,
+ // not only for parent form, so should be common.
+ {$IFDEF UNION_FIELDS}
+ CASE Integer OF
+ 1:( // Toolbar control fields
+ {$ENDIF}
+ fOnTBCustomDraw: TOnTBCustomDraw;
+ fTBevents: PList; // events for TBAssignEvents
+ fTBBtnImgWidth: Integer; // custom toolbar bitmap width
+ fTBBtMinWidth: Integer;
+ fTBBtMaxWidth: Integer;
+ fTBttCmd: PList;
+ fTBttTxt: {$IFDEF UNICODE_CTRLS} PWStrList {$ELSE} PStrList {$ENDIF};
+ fTBCurItem: Integer;
+ fDefaultTBBtnStyle: Byte; // for Toolbars
+ fTBDropped: Boolean;
+ {$IFDEF UNION_FIELDS}
+ );
+ 2:( // Combobox + Group box
+ {$ENDIF}
+ fDroppedWidth: Integer; // SmallInt;
+ fDropDownCount: Cardinal;
+ fCurIdxAtDrop: Integer;
+ fErasingBkgnd: Boolean; // for Group box
+ {$IFDEF UNION_FIELDS}
+ );
+ 3:( // Form + Applet
+ {$ENDIF}
+ fModalResult: Integer;
+ fModalForm: PControl;
+ //fCurrentControl: PControl;
+ //FMinimizeWnd: PControl;
+ fIcon: HIcon;
+
+ {$IFDEF USE_GRAPHCTLS}
+ {$IFDEF GRAPHCTL_HOTTRACK}
+ fHotCtl: PControl;
+ {$ENDIF}
+ {$ENDIF}
+ //fDefaultBtnCtl: PControl;
+ //fCancelBtnCtl: PControl;
+ fWindowState: TWindowState;
+ fActivating: Boolean;
+ fCloseQueryReason: TCloseQueryReason;
+ fFixingModal: ShortInt;
+ fShowAction: Byte;
+ fKeyPreviewCount: Byte;
+ fModal: Byte;
+ fAllBtnReturnClick: Boolean;
+ //-- âíèìàíèå! ïîðÿäîê ñëåäóþùèõ 3õ ïîëåé íå äîëæåí ìåíÿòüñÿ!!!
+ FormCurrentParent: PControl;
+ {* êîíòðîë, èñïîëüçóþùèéñÿ â êà÷åñòâå ðîäèòåëüñêîãî, â ôóíêöèÿõ ñîçäàíèÿ }
+ FormParams: PAnsiChar;
+ {* ñòðîêà êîìàíä è ïàðàìåòðîâ }
+ FormAddress: PPControl;
+ {* àäðåñ ïîëÿ Form â îáúåêòå ôîðìû MCK - íóæåí äëÿ âû÷èñëåíèÿ àäðåñîâ
+ êîíòðîëîâ ïî ñìåùåíèþ, äëÿ ôóíêöèè FormSetCurCtl }
+ FormObj: PObj;
+ FormAlphabet: PFormInitFuncArray;
+ {* àëôàâèò ïðîöåäóð }
+ FormLastCreatedChild: PControl;
+ {* êîíòðîë, ñîçäàííûé ïîñëåäíèì }
+ {$IFDEF UNION_FIELDS}
+ );
+ 4:( // ListView
+ {$ENDIF}
+ fColumn: Integer; // for listview only (column to sort)
+ fOnDeleteAllLVItems: TOnEvent;
+ fCtlImageListSml: PImageList;
+ {* ImageList object (with small icons 16x16) to use with a control (e.g.,
+ with ListView control).
+ If not set, but control has a list of image list objects, last added
+ image list with small icons is used automatically. }
+ fCtlImageListNormal: PImageList;
+ {* ImageList object (with big icons 32x32) to use with a control.
+ If not set, last added image list with big icons is used. }
+ fCtlImgListState: PImageList;
+ {* ImageList object to use as a state image list (for ListView control). }
+ fLVColCount: Integer;
+ fLVTextBkColor: TColor;
+ fLVItemHeight: Integer;
+ fLVOptions: TListViewOptions;
+ fLVStyle: TListViewStyle;
+ {$IFDEF UNION_FIELDS}
+ );
+ 5:( // Rich Edit -- 11 dwords
+ {$ENDIF}
+ {$IFNDEF NOT_USE_RICHEDIT}
+ {$IFDEF STATIC_RICHEDIT_DATA}
+ fRECharFormatRec: TCharFormat;
+ fREParaFmtRec: TParaFormat2;
+ {$ELSE}
+ fRECharFormatRec: PCharFormat;
+ fREParaFmtRec: PParaFormat2;
+ {$ENDIF}
+ fCharFmtDeltaSz: Integer;
+ fParaFmtDeltaSz: Integer;
+ fREError: Integer;
+ fREStream: PStream;
+ fREStrLoadLen: DWORD;
+ fREUrl: PKOLChar;
+ fTmpFont: PGraphicTool; // for RichEdit
+ fREUpdCount: SmallInt;
+ fReOvrDisable: Boolean;
+ fREOvr: Boolean;
+ fREScrolling: Boolean;
+ fRECharArea: TRichFmtArea;
+ FSupressTab: Boolean;
+ fRETransparent: Boolean;
+ {$ENDIF NOT_USE_RICHEDIT}
+ {$IFDEF UNION_FIELDS}
+ );
+ 6:( // Label Effect + Graphic edit control
+ {$ENDIF}
+ fShadowDeep: Integer;
+ fEditCtl: PControl;
+ fEditOptions: TEditOptions;
+ {$IFDEF UNION_FIELDS}
+ );
+ 7:( // BitBtn
+ {$ENDIF}
+ fGlyphBitmap : HBitmap;
+ fGlyphCount : Integer;
+ fGlyphWidth, fGlyphHeight: Integer;
+ fRepeatInterval: Integer;
+ fTextShiftX, fTextShiftY: Integer;
+ fBitBtnDrawMnemonic: Boolean;
+ fBitBtnOptions : TBitBtnOptions;
+ fGlyphLayout : TGlyphLayout;
+ fButtonIcon: HIcon; // for Graphic button control though...
+ FBitBtnGetCaption: function( Self_: PControl; const S: KOLString ): KOLString;
+ FBitBtnExtDraw: procedure( Self_: PControl; DC: HDC; X, Y: Integer; const R: TRect;
+ const CapText, CapTxtOrig: KOLString; Color: TColor );
+ {$IFDEF UNION_FIELDS}
+ );
+ 8:( // Splitter
+ {$ENDIF}
+ fSplitStartPos: TPoint;
+ fSplitStartPos2: TPoint;
+ fSplitStartSize: Integer;
+ fSplitMinSize1, fSplitMinSize2: Integer;
+ fSecondControl: PControl;
+ fSplitLastPos: TPoint;
+ {$IFDEF UNION_FIELDS}
+ );
+ 9:( // Gradient panel
+ {$ENDIF}
+ fColor1: TColor;
+ fColor2: TColor;
+ fGradientStyle: TGradientStyle;
+ fGradientLayout: TGradientLayout;
+ {$IFDEF UNION_FIELDS}
+ );
+ 10:( // Tree view only
+ {$ENDIF}
+ fTVRightClickSelect: Boolean;
+ {$IFDEF UNION_FIELDS}
+ );
+ 11:( // Scroll Bar
+ {$ENDIF}
+ FScrollLineDist: array[ 0..1 ] of Integer;
+ fSBMinMax: TPoint;
+ fSBPageSize: Integer;
+ fSBPosition: Integer;
+ {$IFDEF UNION_FIELDS}
+ );
+ 100:( // for custom controls
+ {$ENDIF}
+ //fCustom6: Integer;
+ //fCustEvent2: TOnEvent;
+ fCustom5: Integer;
+ fCustom4: Integer;
+ fCustEvent1: TOnEvent;
+ fCustom3: Integer;
+ fCustom2: Integer;
+ fCustEvent0: TOnEvent;
+ fCustom1: Integer;
+ fCustom0: Integer;
+ fCustFlag7: Boolean;
+ fCustFlag6: Boolean;
+ fCustFlag5: Boolean;
+ fCustFlag4: Boolean;
+ fCustFlag3: Byte;
+ fCustFlag2: Byte;
+ fCustFlag1: Byte;
+ fCustFlag0: Byte;
+ {$IFDEF UNION_FIELDS}
+ );
+ {$ENDIF}
+ end;
+
+{ ----------------------------------------------------------------------
+ TControl - object to implement any visual control
+----------------------------------------------------------------------- }
+//[TControl DEFINITION]
+ TControl = object( TObj )
+ {*! TControl is the basic visual object of KOL. And now, all visual
+ objects have the same type PControl, differing only in "constructor",
+ which during creating of object adjusts it so it can play role of
+ desired control. Idea of incapsulating of all visual objects having
+ the most common set of properties, is belonging to Vladimir Kladov,
+ (C) 2000.
+ |<br>&nbsp;&nbsp;&nbsp;<b> Since all visual objects are represented
+ in KOL by this single object type, not all methods, properties and
+ events defined in TControl, are applicable to different visual objects.
+ See also notes about certain control kinds, located together with its
+ |<a href="kol_pas.htm#visual_objects_constructors">
+ |constructing functions definitions</a></b>. }
+ public
+ procedure SetAnchor(const Index: Integer; const Value: Boolean);
+ protected
+ function GetAnchor(const Index: Integer): Boolean;
+ function Get_StatusWnd: HWND;
+ function Get_Prop_Int(PropName: PKOLChar): Integer;
+ procedure Set_Prop_Int(PropName: PKOLChar; const Value: Integer);
+ function GetHelpContext: Integer;
+ function Get_Ctl3D: Boolean;
+ function Get_OnMouseEvent(const Index: Integer): TOnMouse;
+ public
+ procedure SetOnMouseEvent(const Index: Integer; const Value: TOnMouse);
+ protected
+ {$IFDEF EVENTS_DYNAMIC}
+ function Get_TOnEvent(const Index: Integer): TOnEvent;
+ function Get_OnMessage: TOnMessage;
+ function Get_OnHelp: TOnHelp;
+ function Get_OnBitBtnDraw: TOnBitBtnDraw;
+ function Get_OnMeasureItem: TOnMeasureItem;
+ function Get_OnShow: TOnEvent;
+ function Get_OnHide: TOnEvent;
+ function Get_OnClose: TOnEventAccept;
+ function Get_OnQueryEndSession: TOnEventAccept;
+ function Get_OnPaint: TOnPaint;
+ function Get_OnPrePaint: TOnPaint;
+ function Get_OnPostPaint: TOnPaint;
+ function Get_OnEraseBkgnd: TOnPaint;
+ function Get_OnClick: TOnEvent;
+ function Get_OnResize: TOnEvent;
+ function Get_OnMove: TOnEvent;
+ function Get_OnMoving: TOnEventMoving;
+ function Get_OnSplit: TOnSplit;
+ function Get_OnKeyDown: TOnKey;
+ function Get_OnKeyUp: TOnKey;
+ function Get_OnChar: TOnChar;
+ function Get_OnDeadChar: TOnChar;
+ function Get_OnMouseUp: TOnMouse;
+ function Get_OnMouseDown: TOnMouse;
+ function Get_OnMouseMove: TOnMouse;
+ function Get_OnMouseDblClk: TOnMouse;
+ function Get_OnMouseWheel: TOnMouse;
+ function Get_OnMouseEnter: TOnEvent;
+ function Get_OnMouseLeave: TOnEvent;
+ function Get_OnTestMouseOver: TOnTestMouseOver;
+ function Get_OnEndEditLVItem: TOnEditLVItem;
+ function Get_OnDeleteLVItem: TOnDeleteLVItem;
+ function Get_OnLVData: TOnLVData;
+ function Get_OnCompareLVItems: TOnCompareLVItems;
+ function Get_OnColumnClick: TOnLVColumnClick;
+ function Get_OnLVStateChange: TOnLVStateChange;
+ function Get_OnDrawItem: TOnDrawItem;
+ function Get_OnLVCustomDraw: TOnLVCustomDraw;
+ function Get_OnTVBeginDrag: TOnTVBeginDrag;
+ function Get_OnTVBeginEdit: TOnTVBeginEdit;
+ function Get_OnTVEndEdit: TOnTVEndEdit;
+ function Get_OnTVExpanding: TOnTVExpanding;
+ function Get_OnTVExpanded: TOnTVExpanded;
+ function Get_OnTVDelete: TOnTVDelete;
+ function Get_OnTVSelChanging: TOnTVSelChanging;
+ function Get_OnDTPUserString: TDTParseInputEvent;
+ function Get_OnSBBeforeScroll: TOnSBBeforeScroll;
+ function Get_OnSBScroll: TOnSBScroll;
+ function Get_OnScroll: TOnScroll;
+ function Get_OnDropFiles: TOnDropFiles;
+ public
+ procedure Set_TOnEvent(const Index: Integer; const Value: TOnEvent);
+ procedure Set_OnMessage(const Value: TOnMessage);
+ procedure Set_OnHelp(const Value: TOnHelp);
+ procedure Set_OnBitBtnDraw(const Value: TOnBitBtnDraw);
+ procedure Set_OnPrePaint(const Value: TOnPaint);
+ procedure Set_OnPostPaint(const Value: TOnPaint);
+ procedure Set_OnEraseBkgnd(const Value: TOnPaint);
+ procedure Set_OnSplit(const Value: TOnSplit);
+ procedure Set_OnCompareLVItems(const Value: TOnCompareLVItems);
+ procedure Set_OnTVBeginDrag(const Value: TOnTVBeginDrag);
+ procedure Set_OnTVBeginEdit(const Value: TOnTVBeginEdit);
+ procedure Set_OnTVEndEdit(const Value: TOnTVEndEdit);
+ procedure Set_OnTVExpanding(const Value: TOnTVExpanding);
+ procedure Set_OnTVExpanded(const Value: TOnTVExpanded);
+ procedure Set_OnTVSelChanging(const Value: TOnTVSelChanging);
+ procedure Set_OnDTPUserString(const Value: TDTParseInputEvent);
+ procedure Set_OnSBBeforeScroll(const Value: TOnSBBeforeScroll);
+ procedure Set_OnSBScroll(const Value: TOnSBScroll);
+ {$ENDIF EVENTS_DYNAMIC}
+ protected
+ procedure SetTBAutoSizeButtons(const Value: Boolean);
+ function GetTBAutoSizeButtons: Boolean;
+ function GetTVEditing: Boolean;
+ function GetDroppedDown: Boolean;
+ {$IFDEF USE_FLAGS}
+ function Get_Dragging: Boolean;
+ function GetTabStop: Boolean;
+ procedure SetTabStop(const Value: Boolean);
+ function GetWordWrap: Boolean;
+ procedure SetWordWrap(const Value: Boolean);
+ function GetCannotDoubleBuf: Boolean;
+ procedure SetCannotDoubleBuf(const Value: Boolean);
+ function GetDoubleBuffered: Boolean;
+ function GetTransparent: Boolean;
+ function GetIsForm: Boolean;
+ function GetSizeGrip: Boolean;
+ procedure SetSizeGrip(const Value: Boolean);
+ function GetIsApplet: Boolean;
+ function GetIsControl: Boolean;
+ function GetIsMDIChild: Boolean;
+ function GetCreateVisible: Boolean;
+ procedure SetCreateVisible(const Value: Boolean);
+ function GetIsButton: Boolean;
+ function GetFlat: Boolean;
+ function GetMouseInCtl: Boolean;
+ function GetEraseBackground: Boolean;
+ procedure SetEraseBackground(const Value: Boolean);
+ function Get3ButtonPress: Boolean;
+ function GetKeyPreview: Boolean;
+ procedure SetKeyPreview(const Value: Boolean);
+ function GetIgnoreDefault: Boolean;
+ procedure SetIgnoreDefault(const Value: Boolean);
+ function GetWindowed: Boolean;
+ procedure SetWindowed(const Value: Boolean);
+ function Get_RightClick: Boolean;
+ function Get_SizeRedraw: Boolean;
+ procedure Set_SizeRedraw(const Value: Boolean);
+ {$ENDIF USE_FLAGS}
+ public //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ FormString: KOLString;
+ {* ñòðîêà òåêóùåãî ïàðàìåòðà. Î÷èùàåòñÿ ïîñëå êàæäîãî âûçîâà
+ FormExecuteCommands, òàê ÷òî ñïåöèàëüíàÿ î÷èñòêà íå òðåáóåòñÿ. }
+ function FormGetIntParam: Integer;
+ {* èçâëåêàåò î÷åðåäíîé öåëî÷èñëåííûé ïàðàìåòð äî ',' èëè äî ';' }
+ function FormGetColorParam: Integer;
+ {* èçâëåêàåò î÷åðåäíîé öåëî÷èñëåííûé ïàðàìåòð äî ',' èëè äî ';' }
+ procedure FormGetStrParam;
+ {* èçâëåêàåò î÷åðåäíîé ñòðîêîâûé ïàðàìåòð äî ',' èëè äî ';' -> FormString }
+ procedure FormCreateParameters( alphabet: PFormInitFuncArray; params: PAnsiChar );
+ {* çàäàåò ïåðâîíà÷àëüíûé àëôàâèò è ïàðàìåòðû ñ êîìàíäàìè }
+ procedure FormExecuteCommands(AForm: PControl; ControlPtrOffsets: PSmallIntArray);
+ {* âûïîëíÿåò êîìàíäû (ñ ïàðàìåòðàìè) äî êîíöà èëè äî ';' }
+ {$IFDEF GDI}
+ protected
+ function GetDate: TDateTime;
+ function GetTime: TDateTime;
+ procedure SetDate(const Value: TDateTime);
+ procedure SetTime(const Value: TDateTime);
+ {$ENDIF GDI}
+ protected
+ {$IFDEF GDI}
+ function GetHelpPath: KOLString;
+ procedure SetHelpPath(const Value: KOLString);
+ public
+ procedure SetOnQueryEndSession(const Value: TOnEventAccept);
+ procedure SetOnMinMaxRestore(const Index: Integer; const Value: TOnEvent);
+ procedure SetOnMinimize( const Value: TOnEvent );
+ procedure SetOnMaximize( const Value: TOnEvent );
+ procedure SetOnRestore( const Value: TOnEvent );
+ procedure SetOnScroll(const Value: TOnScroll);
+ protected
+ procedure SetConstraint(const Index: Integer; Value: SmallInt);
+ function GetOnMinMaxRestore(const Index: Integer): TOnEvent;
+ function GetConstraint(const Index: Integer): Integer;
+ function GetLVColalign(Idx: Integer): TTextAlign;
+ procedure SetLVColalign(Idx: Integer; const Value: TTextAlign);
+
+ {$ENDIF GDI}
+ procedure SetParent( Value: PControl );
+ function GetLeft: Integer;
+ procedure SetLeft( Value: Integer );
+ function GetTop: Integer;
+ procedure SetTop( Value: Integer );
+ function GetWidth: Integer;
+ procedure SetWidth( Value: Integer );
+ function GetHeight: Integer;
+ procedure SetHeight( Value: Integer );
+ function GetPosition: TPoint;
+ procedure Set_Position( Value: TPoint );
+ function GetMembers(Idx: Integer): PControl;
+ function GetFont: PGraphicTool;
+ procedure FontChanged( Sender: PGraphicTool );
+ {$IFDEF GDI}
+ function GetBrush: PGraphicTool;
+ procedure BrushChanged( Sender: PGraphicTool );
+ function GetClientHeight: Integer;
+ function GetClientWidth: Integer;
+ procedure SetClientHeight(const Value: Integer);
+ procedure SetClientWidth(const Value: Integer);
+ function GetHasBorder: Boolean;
+ public procedure SetHasBorder(const Value: Boolean);
+ protected
+
+ function GetHasCaption: Boolean;
+ procedure SetHasCaption(const Value: Boolean);
+
+ function GetCanResize: Boolean;
+ procedure SetCanResize( const Value: Boolean );
+
+ function GetStayOnTop: Boolean;
+ public procedure SetStayOnTop(const Value: Boolean);
+ protected
+ function GetChecked: Boolean;
+ procedure Set_Checked(const Value: Boolean);
+
+ function GetCheck3: TTriStateCheck;
+ procedure SetCheck3(value: TTriStateCheck);
+
+ function GetSelStart: Integer;
+ procedure SetSelStart(const Value: Integer);
+ function GetSelLength: Integer;
+ procedure SetSelLength(const Value: Integer);
+
+ function GetItems(Idx: Integer): KOLString;
+ procedure SetItems(Idx: Integer; const Value: KOLString);
+
+ function GetItemsCount: Integer;
+ function GetItemSelected(ItemIdx: Integer): Boolean;
+ procedure SetItemSelected(ItemIdx: Integer; const Value: Boolean);
+
+ public procedure SetCtl3D(const Value: Boolean);
+ protected
+ function GetCurIndex: Integer;
+ procedure SetCurIndex(const Value: Integer);
+
+ {$ENDIF GDI}
+ function GetTextAlign: TTextAlign;
+ public procedure SetTextAlign(const Value: TTextAlign);
+ protected
+ function GetVerticalAlign: TVerticalAlign;
+ public procedure SetVerticalAlign(const Value: TVerticalAlign);
+ protected
+ function GetCanvas: PCanvas;
+ {$IFDEF _X_}
+ {$IFDEF GTK}
+ protected
+ {} fInBkPaint: Boolean;
+ {} fSetTextAlign: PROCEDURE( Self_: PControl );
+ FUNCTION ProvideCanvasHandle( Sender: PCanvas ): HDC;
+ {$ENDIF GTK}
+ {$ENDIF _X_}
+ {$IFDEF GDI}
+ function Dc2Canvas( Sender: PCanvas ): HDC;
+ procedure SetShadowDeep(const Value: Integer);
+ public procedure SetDoubleBuffered(const Value: Boolean);
+ protected
+
+ procedure SetStatusText(Index: Integer; const Value: KOLString);
+ function GetStatusText( Index: Integer ): KOLString;
+ function GetStatusPanelX(Idx: Integer): Integer;
+ procedure SetStatusPanelX(Idx: Integer; const Value: Integer);
+
+ public procedure SetTransparent(const Value: Boolean);
+ protected
+ function GetImgListIdx(const Index: Integer): PImageList;
+
+ procedure SetImgListIdx(const Index: Integer; const Value: PImageList);
+ function GetLVColText(Idx: Integer): KOLString;
+ procedure SetLVColText(Idx: Integer; const Value: KOLString);
+ {$IFDEF ENABLE_DEPRECATED}
+ {$DEFINE interface_2}
+ {$I KOL_deprecated.inc}
+ {$UNDEF interface_2}
+ {$ENDIF DISABLE_DEPRECATED}
+ protected
+ function LVGetItemText(Idx, Col: Integer): KOLString;
+ procedure LVSetItemText(Idx, Col: Integer; const Value: KOLString);
+ procedure SetLVOptions(const Value: TListViewOptions);
+ procedure SetLVStyle(const Value: TListViewStyle);
+ function GetLVColEx(Idx: Integer; const Index: Integer): Integer;
+ procedure SetLVColEx(Idx: Integer; const Index: Integer;
+ const Value: Integer);
+ {$ENDIF GDI}
+ function GetChildCount: Integer;
+ {$IFDEF GDI}
+ function LVGetItemPos(Idx: Integer): TPoint;
+ procedure LVSetItemPos(Idx: Integer; const Value: TPoint);
+ procedure LVSetColorByIdx(const Index: Integer; const Value: TColor);
+ {$IFDEF F_P}
+ function LVGetColorByIdx(const Index: Integer): TColor;
+ {$ENDIF F_P}
+ function GetIntVal(const Index: Integer): Integer;
+ procedure SetIntVal(const Index, Value: Integer);
+ function GetItemVal(Item: Integer; const Index: Integer): Integer;
+ procedure SetItemVal(Item: Integer; const Index, Value: Integer);
+ function TBGetButtonVisible(BtnID: Integer): Boolean;
+ procedure TBSetButtonVisible(BtnID: Integer; const Value: Boolean);
+
+ function TBGetBtnStt(BtnID: Integer; const Index: Integer): Boolean;
+ procedure TBSetBtnStt(BtnID: Integer; const Index: Integer; const Value: Boolean);
+ function TBGetButtonText(BtnID: Integer): KOLString;
+ function TBGetButtonRect(BtnID: Integer): TRect;
+
+ function TBGetRows: Integer;
+ procedure TBSetRows(const Value: Integer);
+ procedure SetProgressColor(const Value: TColor);
+ function TBGetBtnImgIdx(BtnID: Integer): Integer;
+ procedure TBSetBtnImgIdx(BtnID: Integer; const Value: Integer);
+
+ procedure TBSetButtonText(BtnID: Integer; const Value: KOLString);
+
+ function TBGetBtnWidth(BtnID: Integer): Integer;
+ procedure TBSetBtnWidth(BtnID: Integer; const Value: Integer);
+ procedure TBSetBtMinMaxWidth(const Idx: Integer; const Value: Integer);
+ {$IFDEF F_P}
+ function TBGetBtMinMaxWidth(const Idx: Integer): Integer;
+ {$ENDIF F_P}
+ procedure TBFreeTBevents;
+ function TBGetButtonLParam(const Idx: Integer): DWORD;
+ procedure TBSetButtonLParam(const Idx: Integer; const Value: DWORD);
+ public
+ procedure Set_Align(const Value: TControlAlign);
+ protected
+ function GetSelection: KOLString;
+ procedure SetSelection(const Value: KOLString);
+ procedure SetTabOrder(const Value: SmallInt);
+ function GetFocused: Boolean;
+ procedure SetFocused(const Value: Boolean);
+ {$IFNDEF NOT_USE_RICHEDIT}
+ function REGetFont: PGraphicTool;
+ procedure RESetFont(Value: PGraphicTool);
+ procedure RESetFontEx(const Index: Integer);
+ function REGetFontEffects(const Index: Integer): Boolean;
+ function REGetFontMask(const Index: Integer): Boolean;
+ procedure RESetFontEffect(const Index: Integer; const Value: Boolean);
+ function REGetFontAttr(const Index: Integer): Integer;
+ procedure RESetFontAttr(const Index, Value: Integer);
+ procedure RESetFontAttr1(const Index, Value: Integer);
+ function REGetFontSizeValid: Boolean;
+ function REGetCharformat: TCharFormat;
+ procedure RESetCharFormat(const Value: TCharFormat);
+ function REReadText(Format: TRETextFormat; SelectionOnly: Boolean): KOLString;
+ procedure REWriteText(Format: TRETextFormat; SelectionOnly: Boolean;
+ const Value: KOLString);
+ function REGetFontName: KOLString;
+ procedure RESetFontName(const Value: KOLString);
+ function REGetParaFmt: TParaFormat;
+ procedure RESetParaFmt(const Value: TParaFormat);
+ function REGetNumbering: Boolean;
+ function REGetParaAttr( const Index: Integer ): Integer;
+ function REGetParaAttrValid( const Index: Integer ): Boolean;
+ function REGetTabCount: Integer;
+ function REGetTabs(Idx: Integer): Integer;
+ function REGetTextAlign: TRichTextAlign;
+ procedure RESetNumbering(const Value: Boolean);
+ procedure RESetParaAttr(const Index, Value: Integer);
+ procedure RESetTabCount(const Value: Integer);
+ procedure RESetTabs(Idx: Integer; const Value: Integer);
+ procedure RESetTextAlign(const Value: TRichTextAlign);
+ function REGetStartIndentValid: Boolean;
+ function REGetAutoURLDetect: Boolean;
+ public procedure RESetAutoURLDetect(const Value: Boolean);
+ protected
+ procedure RESetZoom( const Value: TSmallPoint );
+ function REGetZoom: TSmallPoint;
+
+ function GetMaxTextSize: DWORD;
+ procedure SetMaxTextSize(const Value: DWORD);
+ function REGetUnderlineEx: TRichUnderline;
+ procedure RESetUnderlineEx(const Value: TRichUnderline);
+
+ function GetTextSize: Integer;
+ function REGetTextSize(Units: TRichTextSize): Integer;
+
+ function REGetNumStyle: TRichNumbering;
+ procedure RESetNumStyle(const Value: TRichNumbering);
+ function REGetNumBrackets: TRichNumBrackets;
+ procedure RESetNumBrackets(const Value: TRichNumBrackets);
+ function REGetNumTab: Integer;
+ procedure RESetNumTab(const Value: Integer);
+ function REGetNumStart: Integer;
+ procedure RESetNumStart(const Value: Integer);
+ function REGetSpacing(const Index: Integer): Integer;
+ procedure RESetSpacing(const Index, Value: Integer);
+ function REGetSpacingRule: Integer;
+ procedure RESetSpacingRule(const Value: Integer);
+ function REGetLevel: Integer;
+ function REGetBorder(Side: TBorderEdge; const Index: Integer): Integer;
+ procedure RESetBorder(Side: TBorderEdge; const Index: Integer;
+ const Value: Integer);
+ function REGetParaEffect(const Index: Integer): Boolean;
+ procedure RESetParaEffect(const Index: Integer; const Value: Boolean);
+ function REGetOverwite: Boolean;
+ procedure RESetOverwrite(const Value: Boolean);
+ procedure RESetOvrDisable(const Value: Boolean);
+ function REGetTransparent: Boolean;
+ public procedure RESetTransparent(const Value: Boolean);
+ protected
+ procedure RESetOnURL(const Index: Integer; const Value: TOnEvent);
+ procedure SetOnRE_URLClick( const Value: TOnEvent );
+ procedure SetOnRE_OverURL( const Value: TOnEvent );
+ function REGetOnURL(const Index: Integer): TOnEvent;
+ function REGetLangOptions(const Index: Integer): Boolean;
+ procedure RESetLangOptions(const Index: Integer; const Value: Boolean);
+ {$ENDIF NOT_USE_RICHEDIT}
+ public
+ procedure SetOnResize(const Value: TOnEvent);
+ protected
+ procedure DoSelChange;
+ function LVGetItemImgIdx(Idx: Integer): Integer;
+ procedure LVSetItemImgIdx(Idx: Integer; const Value: Integer);
+ public procedure SetFlat(const Value: Boolean);
+ procedure SetOnMouseEnter(const Value: TOnEvent);
+ procedure SetOnMouseLeave(const Value: TOnEvent);
+ protected
+ procedure EdSetTransparent(const Value: Boolean);
+ procedure SetOnTestMouseOver(const Value: TOnTestMouseOver);
+ function GetPages(Idx: Integer): PControl;
+ function TCGetItemText(Idx: Integer): KOLString;
+ procedure TCSetItemText(Idx: Integer; const Value: KOLString);
+ function TCGetItemImgIDx(Idx: Integer): Integer;
+ procedure TCSetItemImgIdx(Idx: Integer; const Value: Integer);
+ function TCGetItemRect(Idx: Integer): TRect;
+ function TVGetItemIdx(const Index: Integer): THandle;
+ procedure TVSetItemIdx(const Index: Integer; const Value: THandle);
+ function TVGetItemNext(Item: THandle; const Index: Integer): THandle;
+ function TVGetItemRect(Item: THandle; TextOnly: Boolean): TRect;
+ function TVGetItemVisible(Item: THandle): Boolean;
+ procedure TVSetITemVisible(Item: THandle; const Value: Boolean);
+ function TVGetItemStateFlg(Item: THandle; const Index: Integer): Boolean;
+ procedure TVSetItemStateFlg(Item: THandle; const Index: Integer;
+ const Value: Boolean);
+ function TVGetItemImage(Item: THandle; const Index: Integer): Integer;
+ procedure TVSetItemImage(Item: THandle; const Index: Integer;
+ const Value: Integer);
+ function TVGetItemText(Item: THandle): KOLString;
+ procedure TVSetItemText(Item: THandle; const Value: KOLString);
+ function TV_GetItemHasChildren(Item: THandle): Boolean;
+ procedure TV_SetItemHasChildren(Item: THandle; const Value: Boolean);
+ function TV_GetItemChildCount(Item: THandle): Integer;
+ function TVGetItemData(Item: THandle): Pointer;
+ procedure TVSetItemData(Item: THandle; const Value: Pointer);
+ function GetToBeVisible: Boolean;
+ procedure SetAlphaBlend(const Value: Byte);
+ procedure SetMaxProgress(const Index, Value: Integer);
+ procedure SetDroppedWidth(const Value: Integer);
+ function LVGetItemState(Idx: Integer): TListViewItemState;
+ procedure LVSetItemState(Idx: Integer; const Value: TListViewItemState);
+ function LVGetSttImgIdx(Idx: Integer): Integer;
+ procedure LVSetSttImgIdx(Idx: Integer; const Value: Integer);
+ function LVGetOvlImgIdx(Idx: Integer): Integer;
+ procedure LVSetOvlImgIdx(Idx: Integer; const Value: Integer);
+ function LVGetItemData(Idx: Integer): DWORD;
+ procedure LVSetItemData(Idx: Integer; const Value: DWORD);
+ function LVGetItemIndent(Idx: Integer): Integer;
+ procedure LVSetItemIndent(Idx: Integer; const Value: Integer);
+ public
+ procedure SetOnDeleteAllLVItems(const Value: TOnEvent);
+ procedure SetOnDeleteLVItem(const Value: TOnDeleteLVItem);
+ procedure SetOnEndEditLVItem(const Value: TOnEditLVItem);
+ procedure SetOnLVData(const Value: TOnLVData);
+ procedure SetOnColumnClick(const Value: TOnLVColumnClick);
+ procedure SetOnDrawItem(const Value: TOnDrawItem);
+ procedure SetOnMeasureItem(const Value: TOnMeasureItem);
+
+ procedure SetItemsCount(const Value: Integer);
+ protected
+ function GetItemData(Idx: Integer): DWORD;
+ procedure SetItemData(Idx: Integer; const Value: DWORD);
+ function GetLVCurItem: Integer;
+ procedure SetLVCurItem(const Value: Integer);
+ function GetLVFocusItem: Integer;
+ public
+ procedure SetOnDropFiles(const Value: TOnDropFiles);
+ procedure SetOnHide(const Value: TOnEvent);
+ procedure SetOnShow(const Value: TOnEvent);
+ procedure SetClientMargin(const Index: Integer; Value: ShortInt);
+ protected
+ {$IFDEF F_P}
+ function GetClientMargin(const Index: Integer): Integer;
+ {$ENDIF F_P}
+ {$ENDIF GDI}
+ protected
+ {$IFDEF _X_}
+ {$IFDEF GTK}
+ {} fExposeEvent: Integer;
+ {$ENDIF GTK}
+ {$ENDIF _X_}
+ public
+ procedure SetOnPaint(const Value: TOnPaint);
+ {$IFDEF GDI}
+ procedure SetOnEraseBkgnd(const Value: TOnPaint);
+ procedure SetTVRightClickSelect(const Value: Boolean);
+ procedure SetOnLVStateChange(const Value: TOnLVStateChange);
+ procedure SetOnMove(const Value: TOnEvent);
+ procedure SetOnMoving(const Value: TOnEventMoving);
+ procedure SetColor1(const Value: TColor);
+ procedure SetColor2(const Value: TColor);
+ procedure SetGradientLayout(const Value: TGradientLayout);
+ procedure SetGradientStyle(const Value: TGradientStyle);
+ protected
+ procedure SetDroppedDown(const Value: Boolean);
+ function get_ClassName: KOLString;
+ procedure set_ClassName(const Value: KOLString);
+ procedure SetClsStyle( Value: DWord );
+
+{$IFDEF GRAPHCTL_XPSTYLES}
+ function GetEdgeStyle: TEdgeStyle;
+ procedure SetEdgeStyle( Value: TEdgeStyle );
+{$ENDIF}
+
+ procedure SetStyle( Value: DWord );
+ procedure SetExStyle( Value: DWord );
+
+ procedure SetCursor( Value: HCursor );
+
+ procedure SetIcon( Value: HIcon );
+ procedure SetMenu( Value: HMenu );
+ {$ENDIF GDI}
+ protected
+ {$IFDEF _X_}
+ {} fGetCaption: TGetCaption;
+ {} fSetCaption: TSetCaption;
+ {$ENDIF _X_}
+ function GetCaption: KOLString;
+ procedure SetCaption( const Value: KOLString );
+ {$IFDEF GDI}
+
+ public procedure SetWindowState( Value: TWindowState );
+ protected
+ function GetWindowState: TWindowState;
+ procedure DoClick;
+ function TBAddInsButtons( Idx: Integer; const Buttons: array of PKOLChar;
+ const BtnImgIdxArray: array of Integer ): Integer; stdcall;
+ public procedure SetBitBtnDrawMnemonic(const Value: Boolean);
+ protected
+ function GetBitBtnImgIdx: Integer;
+ procedure SetBitBtnImgIdx(const Value: Integer);
+ function GetBitBtnImageList: THandle;
+ procedure SetBitBtnImageList(const Value: THandle);
+
+ function GetModal: Boolean;
+ {$IFDEF USE_SETMODALRESULT}
+ procedure SetModalResult( const Value: Integer );
+ {$ENDIF}
+
+ {$ENDIF GDI}
+ protected
+ {$IFDEF GDI}
+ fHandle: HWnd;
+ {$ELSE}
+ {$IFDEF GTK} {} fHandle: PGtkWidget;
+ {} fCaptionHandle: PGtkWidget;
+ {} fEventboxHandle: PGtkWidget;
+ {} fGetClientArea: TGetClientArea;
+ {} fClient: PGtkWidget;
+ {} fChildPut: TChildSetPos;
+ {} fChildSetPos: TChildSetPos;
+ {$ENDIF}
+ {$IFDEF Q_T} fHandle: sometypehere ; {$ENDIF}
+ {$ENDIF}
+ {$IFDEF GDI}
+ fClsStyle: DWord;
+ fStyle: TStyle;
+ fExStyle: DWord;
+ {$ENDIF GDI}
+ {$IFDEF GDI}
+ fDefWndProc: Pointer;
+ {$ENDIF GDI}
+ FParent: PControl;
+
+ {$IFDEF USE_FLAGS} //................... less memory usage with USE_FLAGS ..
+ fFlagsG1: T1Flags;
+ fFlagsG2: T2Flags;
+ fFlagsG3: T3Flags;
+ fFlagsG4: T4Flags;
+ fFlagsG5: T5Flags;
+ fFlagsG6: T6Flags;
+ {$ELSE} //..................................................................
+ {} fEnabled: Boolean; // Caution!!! fVisible must follow fEnabled! ___
+ {} fVisible: Boolean; //____________________________________________//
+ {} fTabstop: Boolean;
+ {} fWordWrap: Boolean;
+ {} fPreventResize: Boolean;
+ // fCursorShared: Boolean;
+ {} fIconShared: Boolean;
+ {} fIgnoreWndCaption: Boolean;
+ {} fSizeRedraw: Boolean; {YS}
+ {} fIsStaticControl: Byte;
+ {} fCannotDoubleBuf: Boolean;
+ {} fDoubleBuffered: Boolean;
+ {* True, if cannot set DoubleBuffered to True (RichEdit). }
+ {* True, if it is static control with a caption. (Mainly, to prevent
+ flicks in DoubleBuffered mode. }
+ {} fTransparent: Boolean;
+ {} fClassicTransparent : Boolean;
+ // FCreating: Boolean;
+ {* True, when creating of object is in progress. }
+ {} fDestroying: Boolean;
+ {* True, when destroying of the window is started. }
+ {} fBeginDestroying: Boolean;
+ {* true, when destroying of the window is initiated by the system, i.e.
+ message WM_DESTROY fired }
+ {} fChangedPosSz: Byte;
+ {* Flags of changing left (1), top (2), width (4) or height (8) }
+ {} fIsForm: Boolean;
+ {* True, if the object is form. }
+ {} fSizeGrip: Boolean;
+ {} fIsApplet: Boolean;
+ {* True, if the object represent application taskbar button. }
+ {} fIsControl: Boolean;
+ {* True, if it is a control on form. }
+ {} fIsMDIChild: Boolean;
+ {* TRUE, if the object is MDI child form. }
+ {} fCreateHidden: Boolean;
+ {} fVisibleWoParent: Boolean;
+ {} fNotUseAlign: Boolean;
+ {} fNotUpdate: Boolean; // was used for PreventResizeFlicks -- now abandoned
+ {} fCreateVisible: Boolean;
+ {} fIsButton: Boolean;
+ {} fIsBitBtn: Boolean;
+ {} fIsGroupBox: Boolean;
+ {} fIsSplitter: Boolean;
+ {} fIsCommonControl: Boolean;
+ {* True, if it is common control. }
+ {} fFlat: Boolean;
+ {} fMouseInControl: Boolean;
+ {} fChecked: Boolean;
+ {} fPushed: Boolean;
+ {} fHot: Boolean;
+ {} fFocused: Boolean;
+ {} fPressed : Boolean;
+ // fDropped: Boolean;
+ {} f3ButtonPress: Boolean;
+ // fEditing: Boolean;
+ {} fEraseUpdRgn: Boolean;
+ {} fKeyPreview: Boolean;
+ {} fKeyPreviewing: Boolean;
+ {} fIgnoreDefault: Boolean;
+ {} fDefaultBtn: Boolean;
+ {} fCancelBtn: Boolean;
+ {} fWindowed: Boolean; //
+ {* True, if control is windowed (or is a form). It is set to FALSE only for
+ graphic controls. }
+ {} fCtlClsNameChg: Boolean; //
+ {* True, if control class name changed and memory is allocated to store it. } //
+ {} fRightClick: Boolean;
+ {} fDragging: Boolean;
+ {$ENDIF not USE_FLAGS} //.................................................................
+ fTextAlign: TTextAlign;
+ fVerticalAlign: TVerticalAlign;
+ {$IFDEF STORE_EDGESTYLE}
+ {} fEdgeStyle : TEdgeStyle;
+ {$ENDIF}
+ fLookTabKeys: TTabKeys;
+ fTabOrder: SmallInt;
+ fAlphaBlend: Byte;
+
+ // Caution!!! order of following 5 fields is important!!!
+ fDynHandlers: PList;
+ fChildren: PList;
+ {* List of children. }
+ //________________________________________________________//
+ {$IFDEF GDI}
+
+ fTmpBrush: HBrush;
+ {* Brush handle to return in response to some color set messages.
+ Intended for internal use instead of Brush.Color if possible
+ to avoid using it. }
+ {$IFDEF STORE_fTmpBrushColorRGB}
+ {} fTmpBrushColorRGB: TColor;
+ {$ENDIF}
+ { }
+ public
+ {$IFDEF COMMANDACTIONS_OBJ}
+ fCommandActions: PCommandActionsObj;
+ {$ELSE}
+ fCommandActions: TCommandActions;
+ {$ENDIF}
+ {$IFDEF EVENTS_DYNAMIC}
+ EV: PEvents;
+ protected
+ function ProvideUniqueEvents: PEvents;
+ procedure FreeEV;
+ {$ELSE}
+ protected
+ EV: TEvents;
+ {$ENDIF}
+ protected
+ PP: TProcedures;
+ fMenu: HMenu;
+ {* Usually used to store handle of attached main menu, but sometimes
+ is used to store control ID (for standard GUI controls only). }
+ {$ENDIF GDI}
+ fMenuObj: PObj;
+ {* PMenu pointer to TMenu object. Freed automatically with entire
+ chain of menu objects attached to a control (or form). }
+ {$IFDEF _X_}
+ {$IFDEF GTK}
+ //fMenuBar: PGtkWidget;
+ {$ENDIF GTK}
+ {$ENDIF _X_}
+ {$IFDEF GDI}
+{$IFNDEF NEW_MENU_ACCELL}
+ fAccelTable: HAccel;
+ procedure DoDestroyAccelTable;
+{$ENDIF}
+ {$ENDIF GDI}
+ protected
+ {$IFDEF GDI}
+ {* Handle of accelerator table created by menu(s). }
+ fImageList: PImageList;
+ {* Pointer to first private image list. Control can own several image,
+ lists, linked to a chain of image list objects. All these image lists
+ are released automatically, when control is destroyed. }
+ {$ENDIF GDI}
+ {$IFDEF GDI}
+ fUpdRgn: HRgn;
+ //fCollectUpdRgn: HRGN;
+ fPaintDC: HDC;
+ {$ENDIF GDI}
+ protected
+ fAutoPopupMenu: PObj;
+ //fHelpContext: Integer;
+
+ {$IFDEF GTK}
+ fDeltaX, fDeltaY: Integer;
+ {$ENDIF GTK}
+ // Order of following fields is important:
+ //_______________________________________________________________________________________________
+ //{$ENDIF GDI}
+ {$IFDEF GDI}
+ // //
+ {$ENDIF GDI}
+ fTextColor: TColor; //
+ {* Color of text. Used instead of fFont.Color internally to //
+ avoid usage of Font object if user is not accessing and changing it. } //
+ fColor: TColor; //
+ {* Color of control background. } //
+ fFont: PGraphicTool; //
+ fBrush: PGraphicTool; //
+ fMargin: ShortInt; //
+ fClientTop: ShortInt;
+ fClientBottom: ShortInt;
+ fClientLeft: ShortInt;
+ fClientRight: ShortInt; //
+ {* Store adjustment factor of ClientRect for some 'idiosincrasies' windows, //
+ such as Groupbox or Tabcontrol. } //
+ fCtl3D_child: Byte; //
+ fBoundsRect: TRect; //
+ fCursor: HCursor;
+ //_____________________________________________________________________________________________//
+ // this is the end of fiels set, which order is important
+ fCanvas: PCanvas;
+ {$IFDEF GDI}
+ fDblExcludeRgn: HDC;
+
+ {$ENDIF GDI}
+ {$IFDEF GTK}
+ {} fClickedEvent: Integer;
+ {$ENDIF}
+ public
+ procedure SetOnClick( const Value: TOnEvent );
+ protected
+ {$IFDEF GDI}
+ //fRadio1st: THandle;
+ //fRadioLast : THandle;
+ //fDropDownProc: procedure( Sender : PObj );
+ //fPrevWndProc: Pointer;
+
+ fCurIndex: Integer;
+
+ //fOldDefWndProc: Pointer;
+ procedure SetSBMax(Value: Longint);
+ procedure SetSBMin(Value: Longint);
+ procedure SetSBPageSize(Value: Integer);
+ procedure SetSBPosition(Value: Integer);
+ procedure SetSBMinMax(const Value: TPoint);
+ protected
+ procedure SetOnLVCustomDraw(const Value: TOnLVCustomDraw);
+ {$ENDIF GDI}
+ protected
+ {$IFDEF GDI}
+ //fPaintMsg: TMsg;
+
+ {$ENDIF GDI}
+ FMaxWidth: SmallInt;
+ FMinWidth: SmallInt;
+ FMaxHeight: SmallInt;
+ FMinHeight: SmallInt;
+ {$IFDEF GDI}
+ fStatusCtl: PControl;
+ //fStatusTxt: PKOLChar;
+ {$ENDIF GDI}
+ {$IFDEF GDI}
+ //fDragStartPos: TSmallPoint;
+ //fMouseStartPos: TSmallPoint;
+ {$IFDEF FIX_WIDTH_HEIGHT}
+ {} FFixWidth: Integer;
+ {} FFixHeight: Integer;
+ {$ENDIF}
+ {$ENDIF GDI}
+ //----- order of following 3 fields important: //
+ fCaption: KOLString;
+ fCustomData: Pointer;
+ fControlClassName: PKOLChar; //
+ {$IFDEF GDI}
+ //---------------------------------------------//
+ fCustomObj: PObj;
+ public
+ DF: TDataFields;
+ {* Data fields for certain controls. These are overlapped to
+ economy size of TControl object. }
+ //fCreateParamsExt: procedure( Self_: PControl; var Params: TCreateParams );
+ protected
+
+ {$IFDEF USE_MHTOOLTIP}
+ {$DEFINE var}
+ fHint: PMHHint;
+ {$UNDEF var}
+
+ {$DEFINE function}
+ function GetHint: PMHHint;
+ {$UNDEF function}
+ {$ENDIF}
+
+ {$ENDIF GDI}
+
+ procedure Init; virtual;
+ {$IFDEF GDI}
+ procedure InitParented( AParent: PControl ); virtual;
+ {* Initialization of visual object. }
+ procedure InitOrthaned( AParentWnd: HWnd ); virtual;
+ {* Initialization of visual object. }
+ {$ENDIF GDI}
+ {$IFDEF _X_}
+ {$IFDEF GTK}
+ PROCEDURE InitParented( AParent: PControl; widget: PGtkWidget;
+ {}need_eventbox: Boolean ); VIRTUAL;
+ {* Initialization of visual object. }
+ {$ENDIF GTK}
+ {$ENDIF _X_}
+ {$IFDEF GDI}
+ procedure DestroyChildren;
+ {* Destroys children. Is called in destructor, and can be
+ called in descending classes as earlier as needed to
+ prevent problems of too late destroying of visuals.
+ |<br>
+ Note: since v 2.40, used only for case when a symbol NOT_USE_AUTOFREE4CONTROLS
+ is defined, otherwise all children are destroyed using common mechanism of
+ Add2AutoFree. }
+
+ function GetParentWnd( NeedHandle: Boolean ): HWnd;
+ {* Returns handle of parent window. }
+ function GetParentWindow: HWnd;
+ {* }
+ procedure SetEnabled( Value: Boolean );
+ {* Changes Enabled property value. Overriden here to change enabling
+ status of a window. }
+ function GetEnabled: Boolean;
+ {* Returns True, if Enabled. Overriden here to obtain real window
+ state. }
+ procedure SetVisible( Value: Boolean );
+ {* Sets Visible property value. Overriden here to change visibility
+ of correspondent window. }
+ procedure Set_Visible( Value: Boolean );
+ {* }
+ function GetVisible: Boolean;
+ {* Returns True, if correspondent window is Visible. Overriden
+ to get visibility of real window, not just value stored in object. }
+ function Get_Visible: Boolean;
+ {* Returns True, if correspondent window is Visible, for forms and applet,
+ or if fVisible flag is set, for controls. }
+ protected
+ {$ENDIF GDI}
+ procedure SetCtlColor( Value: TColor );
+ {* Sets TControl's Color property value. }
+ procedure SetBoundsRect( const Value: TRect );
+ {* Sets BoudsRect property value. }
+ function GetBoundsRect: TRect;
+ {* Returns bounding rectangle. }
+ {$IFDEF GDI}
+ function GetIcon: HIcon;
+ {* Returns Icon property. By default, if it is not set,
+ returns Icon property of an Applet. }
+
+ procedure CreateSubclass( var Params: TCreateParams; ControlClassName: PKOLChar );
+ {* Can be used in descending classes to subclass window with given
+ standard Windows ControlClassName - must be called after
+ creating Params but before CreateWindow. Usually it is called
+ in overriden method CreateParams after calling of the inherited one. }
+
+ function UpdateWndStyles: PControl;
+ public
+ {* Updates fStyle, fExStyle, fClsStyle from window handle }
+ procedure SetOnChar(const Value: TOnChar);
+ {* }
+ {$IFDEF SUPPORT_ONDEADCHAR}
+ procedure SetOnDeadChar(const Value: TOnChar);
+ {* }
+ {$ENDIF SUPPORT_ONDEADCHAR}
+ procedure SetOnKeyDown(const Value: TOnKey);
+ {* }
+ procedure SetOnKeyUp(const Value: TOnKey);
+ {* }
+ {$ENDIF GDI}
+ {$IFDEF GDI}
+ procedure SetHelpContext( Value: Integer );
+ {* }
+ procedure SetOnTVDelete( const Value: TOnTVDelete );
+ {* }
+ public procedure SetDefaultBtn(const Index: Integer; const Value: Boolean);
+ protected
+ function GetDefaultBtn(const Index: Integer): Boolean;
+ function DefaultBtnProc( var Msg: TMsg; var Rslt: Integer ): Boolean;
+ {* }
+
+ procedure SetDateTime( Value: TDateTime );
+ function GetDateTime: TDateTime;
+ procedure SetDateTimeRange( Value: TDateTimeRange );
+ function GetDateTimeRange: TDateTimeRange;
+ procedure SetDateTimePickerColor( Index: TDateTimePickerColor; Value: TColor );
+ function GetDateTimePickerColor( Index: TDateTimePickerColor ): TColor;
+ procedure SetDateTimeFormat( const Value: KOLString );
+ function Get_SystemTime: TSystemTime;
+ procedure Set_SystemTime(const Value: TSystemTime);
+
+ procedure SetOnTBCustomDraw( const Value: TOnTBCustomDraw );
+
+ {$ENDIF GDI}
+ procedure DoAutoSize;
+
+ public
+ {$IFDEF GDI}
+ constructor CreateParented( AParent: PControl );
+ {* Creates new instance of TControl object, calling InitParented }
+ constructor CreateOrthaned( AParentWnd: HWnd );
+ {* Creates new instance of TControl object, calling InitOrthaned }
+ {$ENDIF GDI}
+ {$IFDEF _X_}
+ {$IFDEF GTK}
+ CONSTRUCTOR CreateParented( AParent: PControl; widget: PGtkWidget;
+ {}need_eventbox: Boolean );
+ {* Creates new instance of TControl object, calling InitParented }
+ {$ENDIF GTK}
+ {$ENDIF _X_}
+ {$IFDEF GDI}
+ destructor Destroy; virtual;
+ {* Destroyes object. First of all, destructors for all children
+ are called. }
+
+ function GetWindowHandle: HWnd;
+ {* Returns window handle. If window is not yet created,
+ method CreateWindow is called. }
+ procedure CreateChildWindows;
+ {* Enumerates all children recursively and calls CreateWindow for all
+ of these. }
+ {$ENDIF GDI}
+ property Parent: PControl read fParent write SetParent;
+ {* Parent of TParent object. Also must be of TParent type or derived from TParent. }
+ //property Tag: Integer read FTag write FTag; //--------- moved to TObj --------
+ {* User-defined pointer, which can contain any data or reference to
+ anywhere in memory (when used as a pointer).
+ }
+ function ChildIndex( Child: PControl ): Integer;
+ {* Returns index of given child. }
+ procedure MoveChild( Child: PControl; NewIdx: Integer );
+ {* Moves given Child into new position. }
+
+ {$IFDEF GDI}
+ property Enabled: Boolean read GetEnabled write SetEnabled;
+ {* Enabled usually used to decide if control can get keyboard focus
+ or been clicked by mouse. }
+ procedure EnableChildren( Enable, Recursive: Boolean );
+ {* Enables (Enable = TRUE) or disables (Enable = FALSE) all the children
+ of the control. If Recursive = TRUE then all the children of all the
+ children are enabled or disabled recursively. }
+ property Visible: Boolean read Get_Visible write SetVisible;
+ {* Obvious. }
+ property ToBeVisible: Boolean read GetToBeVisible;
+ {* Returns True, if a control is supposed to be visible when its
+ form is showing. }
+ property CreateVisible: Boolean
+ read {$IFDEF USE_FLAGS} GetCreateVisible {$ELSE} fCreateVisible {$ENDIF}
+ write {$IFDEF USE_FLAGS} SetCreateVisible {$ELSE} fCreateVisible {$ENDIF};
+ {* False by default. If You want your form to be created visible and
+ flick due creation, set it to True. This does not affect size of
+ executable anyway. }
+ {$ENDIF GDI}
+ property BoundsRect: TRect read GetBoundsRect write SetBoundsRect;
+ {* Bounding rectangle of the visual. Coordinates are relative
+ to top left corner of parent's ClientRect, or to top left corner
+ of screen (for TForm). }
+ property Left: Integer read GetLeft write SetLeft;
+ {* Left horizontal position. }
+ property Top: Integer read GetTop write SetTop;
+ {* Top vertical position. }
+ property Width: Integer read GetWidth write SetWidth;
+ {* Width of TVisual object. }
+ property Height: Integer read GetHeight write SetHeight;
+ {* Height of TVisual object. }
+ property Position: TPoint read GetPosition write Set_Position;
+ {* Represents top left position of the object. See also BoundsRect. }
+ {$IFDEF GDI}
+ property MinWidth: SmallInt index 0
+ {$IFDEF F_P} read GetConstraint
+ {$ELSE DELPHI} read FMinWidth
+ {$ENDIF F_P/DELPHI} write SetConstraint;
+ {* Minimal width constraint. }
+ property MinHeight: SmallInt index 1
+ {$IFDEF F_P} read GetConstraint
+ {$ELSE DELPHI} read FMinHeight
+ {$ENDIF F_P/DELPHI} write SetConstraint;
+ {* Minimal height constraint. }
+ property MaxWidth: SmallInt index 2
+ {$IFDEF F_P} read GetConstraint
+ {$ELSE DELPHI} read FMaxWidth
+ {$ENDIF F_P/DELPHI} write SetConstraint;
+ {* Maximal width constraint. }
+ property MaxHeight: SmallInt index 3
+ {$IFDEF F_P} read GetConstraint
+ {$ELSE DELPHI} read FMaxHeight
+ {$ENDIF F_P/DELPHI} write SetConstraint;
+ {* Maximal height constraint. }
+
+ {$ENDIF GDI}
+ function ClientRect: TRect;
+ {* Client rectangle of TControl. Contrary to VCL, for some
+ classes (e.g. for graphic controls) can be relative
+ not to itself, but to top left corner of the parent's ClientRect
+ rectangle. }
+ {$IFDEF GDI}
+ property ClientWidth: Integer read GetClientWidth write SetClientWidth;
+ {* Obvious. Accessing this property, program forces window latent creation. }
+ property ClientHeight: Integer read GetClientHeight write SetClientHeight;
+ {* Obvious. Accessing this property, program forces window latent creation. }
+
+ function ControlRect: TRect;
+ {* Absolute bounding rectangle relatively to nearest
+ Windowed parent client rectangle (at least to a form, but usually to
+ a Parent).
+ Useful while drawing on device context, provided by such
+ Windowed parent. For form itself is the same as BoundsRect. }
+
+ function ControlAtPos( X, Y: Integer; IgnoreDisabled: Boolean ): PControl;
+ {* Searches control at the given position (relatively to top left
+ corner of the ClientRect). }
+ {$ENDIF GDI}
+ procedure Invalidate;
+ {* Invalidates rectangle, occupied by the visual (but only if Showing =
+ True). }
+ {$IFDEF GDI}
+ public
+ procedure InvalidateEx;
+ {* Invalidates the window and all its children. }
+ procedure InvalidateNC( Recursive: Boolean );
+ {* Invalidates the window and all its children including non-client area. }
+ procedure Update;
+ {* Updates control's window and calls Update for all child controls. }
+ procedure BeginUpdate;
+ {* |<#treeview>
+ |<#listview>
+ |<#richedit>
+ |<#memo>
+ |<#listbox>
+ Call this method to stop visual updates of the control until correspondent
+ EndUpdate called (pairs BeginUpdate - EndUpdate can be nested). }
+ procedure EndUpdate;
+ {* See BeginUpdate. }
+
+ property Windowed: Boolean
+ read {$IFDEF USE_FLAGS} GetWindowed {$ELSE} fWindowed {$ENDIF}
+ write {$IFDEF USE_FLAGS} SetWindowed {$ELSE} fWindowed {$ENDIF};
+ {* Constantly returns True, if object is windowed (i.e. owns
+ correspondent window handle). Otherwise, returns False.
+ |<br>
+ By now, all the controls are windowed (there are no controls in KOL, which are
+ emulating window, acually belonging to Parent - like TGraphicControl
+ in VCL).
+ |<br>
+ Writing of this property provided only for internal purposes,
+ do not change it directly unless you understand well what you do. }
+
+ function HandleAllocated: Boolean;
+ {* Returns True, if window handle is allocated. Has no sense for
+ non-Windowed objects (but now, the KOL has no non-Windowed controls). }
+ {$ENDIF GDI}
+
+ property ChildCount: Integer read GetChildCount;
+ {* Returns number of commonly accessed child objects. }
+ property Children[ Idx: Integer ]: PControl read GetMembers;
+ {* Child items of TVisual object. Property is reintroduced here
+ to separate access to always visible Children[] from restricted
+ a bit Members[]. }
+ {$IFDEF GDI}
+ procedure PaintBackground( DC: HDC; Rect: PRect );
+ {* Is called to paint background in given rectangle. This
+ method is filling clipped area of the Rect rectangle with
+ Color, but only if global event Global_OnPaintBkgnd is
+ not assigned. If assigned, this one is called instead here.
+ |<br>&nbsp;&nbsp;&nbsp;
+ This method made public, so it can be called directly to
+ fill some device context's rectangle. But remember, that
+ independantly of Rect, top left corner of background piece
+ will be located so, if drawing is occure into ControlRect
+ rectangle. }
+ property WindowedParent: PControl read fParent;
+ {* Returns nearest windowed parent, the same as Parent. }
+ {$ENDIF GDI}
+ function ParentForm: PControl;
+ {* |<#form>
+ Returns parent form for a control (of @Self for form itself. }
+ function FormParentForm: PControl;
+ {* |<#form>
+ Returns parent form for a control (of @Self for form itself. For a frame,
+ returns frame panel instead. }
+ function MarkPanelAsForm: PControl;
+ {* Special function for MCK to mark panel as frame parent control. }
+ {$IFDEF GDI}
+ property ActiveControl: PControl read DF.fCurrentControl write DF.fCurrentControl;
+ {* }
+ function Client2Screen( const P: TPoint ): TPoint;
+ {* Converts the client coordinates of a specified point to screen coordinates. }
+ function Screen2Client( const P: TPoint ): TPoint;
+ {* Converts screen coordinates of a specified point to client coordinates. }
+ function CreateWindow: Boolean; virtual;
+ {* |<#form>
+ Creates correspondent window object. Returns True if success (if
+ window is already created, False is returned). If applied to a form,
+ all child controls also allocates handles that time.
+ |<br>&nbsp;&nbsp;&nbsp;
+ Call this method to ensure, that a hanle is allocated for a form,
+ an application button or a control. (It is not necessary to do so in
+ the most cases, even if You plan to work with control's handle directly.
+ But immediately after creating the object, if You want to pass its
+ handle to API function, this can be helpful). }
+ {$ENDIF GDI}
+ {$IFDEF _X_}
+ procedure VisualizyWindow; // for _X_, makes actually visible a window and
+ // all its subwindows recursively, if they are having Visible = TRUE
+ {$ENDIF _X_}
+ {$IFDEF GDI}
+ procedure Close;
+ {* |<#appbutton>
+ |<#form>
+ Closes window. If a window is the main form, this closes application,
+ terminating it. Also it is possible to call Close method for Applet
+ window to stop application. }
+
+ {$IFDEF USE_MHTOOLTIP}
+ {$DEFINE public}
+ property Hint: PMHHint read GetHint;
+ {$UNDEF public}
+ {$ENDIF}
+
+ property Handle: HWnd read fHandle; //GetHandle;
+ {* Returns descriptor of system window object. If window is not yet
+ created, 0 is returned. To allocate handle, call CreateWindow method. }
+
+ property ParentWindow: HWnd read GetParentWindow;
+ {* Returns handle of parent window (not TControl object, but system
+ window object handle). }
+ property ClsStyle: DWord read fClsStyle write SetClsStyle;
+ {* Window class style. Available styles are:
+ |<table border=0>
+ |&L=<tr><td valign=top><font face=Fixedsys>%1</font></td><td>
+ |&E=</td></tr>
+ |&N=<br>&nbsp;&nbsp;&nbsp;
+ <L CS_BYTEALIGNCLIENT> - Aligns the window's client area on the byte boundary
+ (in the x direction) to enhance performance during
+ drawing operations. <E>
+ <L CS_BYTEALIGNWINDOW> - Aligns a window on a byte boundary (in the x
+ direction). <E>
+ <L CS_CLASSDC> - Allocates one device context to be shared by all
+ windows in the class. <E>
+ <L CS_DBLCLKS> - Sends double-click messages to the window
+ procedure when the user double-clicks the mouse while the
+ cursor is within a window belonging to the class. <E>
+ <L CS_GLOBALCLASS> - Allows an application to create a window of
+ the class regardless of the value of the hInstance parameter.
+ <N> You can create a global class by creating
+ the window class in a dynamic-link library (DLL) and listing the
+ name of the DLL in the registry under specific keys. <E>
+ <L CS_HREDRAW> - Redraws the entire window if a movement or
+ size adjustment changes the width of the client area. <E>
+ <L CS_NOCLOSE> - Disables the Close command on the System menu. <E>
+ <L CS_OWNDC> - Allocates a unique device context for each window
+ in the class. <E>
+ <L CS_PARENTDC> - Sets the clipping region of the child window to
+ that of the parent window so that the child can draw on the parent. <E>
+ <L CS_SAVEBITS> - Saves, as a bitmap, the portion of the screen
+ image obscured by a window. Windows uses the saved bitmap to re-create
+ the screen image when the window is removed. <E>
+ <L CS_VREDRAW> - Redraws the entire window if a movement or size
+ adjustment changes the height of the client area. <E>
+ |</table> For more info, see Win32.hlp (keyword 'WndClass');
+ }
+
+
+{$IFDEF GRAPHCTL_XPSTYLES}
+ property edgeStyle : TEdgeStyle
+ read {$IFnDEF STORE_EDGESTYLE} GetEdgeStyle {$ELSE} fEdgeStyle {$ENDIF}
+ write SetEdgeStyle;
+{$ENDIF}
+
+ property Style: DWord read fStyle.Value write SetStyle;
+ {* Window styles. Available styles are:
+ |<table border=0>
+ <L WS_BORDER> Creates a window that has a thin-line border. <E>
+ <L WS_CAPTION> Creates a window that has a title bar (includes the
+ WS_BORDER style). <E>
+ <L WS_CHILD> Creates a child window. This style cannot be used with
+ the WS_POPUP style. <E>
+ <L WS_CHILDWINDOW> Same as the WS_CHILD style. <E>
+ <L WS_CLIPCHILDREN> Excludes the area occupied by child windows
+ when drawing occurs within the parent window. This style is used
+ when creating the parent window. <E>
+ <L WS_CLIPSIBLINGS> Clips child windows relative to each other;
+ that is, when a particular child window receives a WM_PAINT message,
+ the WS_CLIPSIBLINGS style clips all other overlapping child windows
+ out of the region of the child window to be updated. If
+ WS_CLIPSIBLINGS is not specified and child windows overlap, it is
+ possible, when drawing within the client area of a child window,
+ to draw within the client area of a neighboring child window. <E>
+ <L WS_DISABLED> Creates a window that is initially disabled. A
+ disabled window cannot receive input from the user. <E>
+ <L WS_DLGFRAME> Creates a window that has a border of a style
+ typically used with dialog boxes. A window with this style cannot
+ have a title bar. <E>
+ <L WS_GROUP> Specifies the first control of a group of controls.
+ The group consists of this first control and all controls defined
+ after it, up to the next control with the WS_GROUP style.
+ The first control in each group usually has the WS_TABSTOP
+ style so that the user can move from group to group. The user
+ can subsequently change the keyboard focus from one control in
+ the group to the next control in the group by using the direction
+ keys. <E>
+ <L WS_HSCROLL> Creates a window that has a horizontal scroll bar. <E>
+ <L WS_ICONIC> Creates a window that is initially minimized. Same as
+ the WS_MINIMIZE style. <E>
+ <L WS_MAXIMIZE> Creates a window that is initially maximized. <E>
+ <L WS_MAXIMIZEBOX> Creates a window that has a Maximize button.
+ Cannot be combined with the WS_EX_CONTEXTHELP style. The WS_SYSMENU
+ style must also be specified. <E>
+ <L WS_MINIMIZE> Creates a window that is initially minimized.
+ Same as the WS_ICONIC style. <E>
+ <L WS_MINIMIZEBOX> Creates a window that has a Minimize button.
+ Cannot be combined with the WS_EX_CONTEXTHELP style. The WS_SYSMENU
+ style must also be specified. <E>
+ <L WS_OVERLAPPED> Creates an overlapped window. An overlapped
+ window has a title bar and a border. Same as the WS_TILED style. <E>
+ <L WS_OVERLAPPEDWINDOW> Creates an overlapped window with the
+ WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX,
+ and WS_MAXIMIZEBOX styles. Same as the WS_TILEDWINDOW style. <E>
+ <L WS_POPUP> Creates a pop-up window. This style cannot be used with
+ the WS_CHILD style. <E>
+ <L WS_POPUPWINDOW> Creates a pop-up window with WS_BORDER,
+ WS_POPUP, and WS_SYSMENU styles. The WS_CAPTION and WS_POPUPWINDOW
+ styles must be combined to make the window menu visible. <E>
+ <L WS_SIZEBOX> Creates a window that has a sizing border. Same as the
+ WS_THICKFRAME style. <E>
+ <L WS_SYSMENU> Creates a window that has a window-menu on its title
+ bar. The WS_CAPTION style must also be specified. <E>
+ <L WS_TABSTOP> Specifies a control that can receive the keyboard focus
+ when the user presses the TAB key. Pressing the TAB key changes
+ the keyboard focus to the next control with the WS_TABSTOP style. <E>
+ <L WS_THICKFRAME> Creates a window that has a sizing border.
+ Same as the WS_SIZEBOX style. <E>
+ <L WS_TILED> Creates an overlapped window. An overlapped window has
+ a title bar and a border. Same as the WS_OVERLAPPED style. <E>
+ <L WS_TILEDWINDOW> Creates an overlapped window with the
+ WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME,
+ WS_MINIMIZEBOX, and WS_MAXIMIZEBOX styles. Same as the
+ WS_OVERLAPPEDWINDOW style. <E>
+ <L WS_VISIBLE> Creates a window that is initially visible. <E>
+ <L WS_VSCROLL> Creates a window that has a vertical scroll bar. <E>
+ |</table>
+ See also Win32.hlp (topic CreateWindow).
+ }
+ property ExStyle: DWord read fExStyle write SetExStyle;
+ {* Extra window styles. Available flags are following:
+ |<table border=0>
+ <L WS_EX_ACCEPTFILES> Specifies that a window created with this style
+ accepts drag-drop files. <E>
+ <L WS_EX_APPWINDOW> Forces a top-level window onto the taskbar
+ when the window is minimized. <E>
+ <L WS_EX_CLIENTEDGE> Specifies that a window has a border with a
+ sunken edge. <E>
+ <L WS_EX_CONTEXTHELP> Includes a question mark in the title bar of
+ the window. When the user clicks the question mark, the cursor
+ changes to a question mark with a pointer. If the user then clicks
+ a child window, the child receives a WM_HELP message. The child
+ window should pass the message to the parent window procedure,
+ which should call the WinHelp function using the HELP_WM_HELP
+ command. The Help application displays a pop-up window that
+ typically contains help for the child window.WS_EX_CONTEXTHELP
+ cannot be used with the WS_MAXIMIZEBOX or WS_MINIMIZEBOX styles. <E>
+ <L WS_EX_CONTROLPARENT> Allows the user to navigate among the child
+ windows of the window by using the TAB key. <E>
+ <L WS_EX_DLGMODALFRAME> Creates a window that has a double border;
+ the window can, optionally, be created with a title bar by
+ specifying the WS_CAPTION style in the dwStyle parameter. <E>
+ <L WS_EX_LEFT> Window has generic "left-aligned" properties. This
+ is the default. <E>
+ <L WS_EX_LEFTSCROLLBAR> If the shell language is Hebrew, Arabic, or
+ another language that supports reading order alignment, the
+ vertical scroll bar (if present) is to the left of the client
+ area. For other languages, the style is ignored and not treated
+ as an error. <E>
+ <L WS_EX_LTRREADING> The window text is displayed using Left to
+ Right reading-order properties. This is the default. <E>
+ <L WS_EX_MDICHILD> Creates an MDI child window. <E>
+ <L WS_EX_NOPARENTNOTIFY> Specifies that a child window created
+ with this style does not send the WM_PARENTNOTIFY message to its
+ parent window when it is created or destroyed. <E>
+ <L WS_EX_OVERLAPPEDWINDOW> Combines the WS_EX_CLIENTEDGE and
+ WS_EX_WINDOWEDGE styles. <E>
+ <L WS_EX_PALETTEWINDOW> Combines the WS_EX_WINDOWEDGE,
+ WS_EX_TOOLWINDOW, and WS_EX_TOPMOST styles. <E>
+ <L WS_EX_RIGHT> Window has generic "right-aligned" properties.
+ This depends on the window class. This style has an effect only
+ if the shell language is Hebrew, Arabic, or another language that
+ supports reading order alignment; otherwise, the style is
+ ignored and not treated as an error. <E>
+ <L WS_EX_RIGHTSCROLLBAR> Vertical scroll bar (if present) is to the
+ right of the client area. This is the default. <E>
+ <L WS_EX_RTLREADING> If the shell language is Hebrew, Arabic, or
+ another language that supports reading order alignment, the
+ window text is displayed using Right to Left reading-order
+ properties. For other languages, the style is ignored and not
+ treated as an error. <E>
+ <L WS_EX_STATICEDGE> Creates a window with a three-dimensional
+ border style intended to be used for items that do not accept
+ user input. <E>
+ <L WS_EX_TOOLWINDOW> Creates a tool window; that is, a window
+ intended to be used as a floating toolbar. A tool window has
+ a title bar that is shorter than a normal title bar, and the
+ window title is drawn using a smaller font. A tool window does
+ not appear in the taskbar or in the dialog that appears when
+ the user presses ALT+TAB. <E>
+ <L WS_EX_TOPMOST> Specifies that a window created with this style
+ should be placed above all non-topmost windows and should stay
+ above them, even when the window is deactivated. To add or remove
+ this style, use the SetWindowPos function. <E>
+ <L WS_EX_TRANSPARENT> Specifies that a window created with this
+ style is to be transparent. That is, any windows that are
+ beneath the window are not obscured by the window. A window
+ created with this style receives WM_PAINT messages only after
+ all sibling windows beneath it have been updated. <E>
+ <L WS_EX_WINDOWEDGE> Specifies that a window has a border with
+ a raised edge. <E>
+ |</table>
+ See also Win32.hlp (topic CreateWindowEx).
+ }
+
+ property Cursor: HCursor read fCursor write SetCursor;
+ {* Current cursor. For most of controls, sets initially to IDC_ARROW. See
+ also ScreenCursor. }
+ procedure CursorLoad( Inst: Integer; ResName: PKOLChar );
+ {* Loads Cursor from the resource. See also comments for Icon property. }
+
+ property Icon: HIcon read {$IFDEF SMALLEST_CODE} DF.fIcon {$ELSE} GetIcon {$ENDIF}
+ write SetIcon;
+ {* |<#appbutton>
+ |<#form>
+ Icon. By default, icon of the Applet is used. To load icon from the
+ resource, use IconLoad or IconLoadCursor method - this is more correct, because
+ in such case a special flag is set to prevent attempts to destroy
+ shared icon object in the destructor of the control. }
+
+ procedure IconLoad( Inst: Integer; ResName: PKOLChar );
+ {* |<#appbutton>
+ |<#form>
+ See Icon property. }
+ procedure IconLoadCursor( Inst: Integer; ResName: PKOLChar );
+ {* |<#appbutton>
+ |<#form>
+ Loads Icon from the cursor resource. See also Icon property. }
+
+ property Menu: HMenu read fMenu write SetMenu;
+
+ {* Menu (or ID of control - for standard GUI controls). }
+ property HelpContext: Integer read GetHelpContext write SetHelpContext;
+ {* Help context. }
+ function AssignHelpContext( Context: Integer ): PControl;
+ {* Assigns HelpContext and returns @ Self (can be used in initialization
+ of a control in a chain of "transparent" calls). }
+
+ procedure CallHelp( Context: Integer; CtxCtl: PControl {; CtlID: Integer} );
+ {* Method of a form or Applet. Call it to show help with the given context
+ ID. If the Context = 0, help contents is displayed. By default,
+ WinHelp is used. To allow using HtmlHelp, call AssignHtmlHelp global
+ function. When WinHelp used, HelpPath variable can be assigned directly.
+ If HelpPath variable is not assigned, application name
+ (and path) is used, with extension replaced to '.hlp'. }
+
+ property HelpPath: KOLString read GetHelpPath write SetHelpPath;
+ {* Property of a form or an Applet. Change it to provide custom path to
+ WinHelp format help file. If HtmlHelp used, call global procedure
+ AssignHtmlHelp instead. }
+
+ property OnHelp: TOnHelp
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnHelp {$ELSE} EV.fOnHelp {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_OnHelp {$ELSE} EV.fOnHelp {$ENDIF};
+ {* An event of a form, it is called when F1 pressed or help topic requested
+ by any other way. To prevent showing help, nullify Sender. Set Popup to
+ TRUE to provide showing help in a pop-up window. It is also possible to
+ change Context dynamically. }
+
+ {$ENDIF GDI}
+ property Caption: KOLString read GetCaption write SetCaption;
+ {* |<#appbutton>
+ |<#form>
+ |<#button>
+ |<#bitbtn>
+ |<#label>
+ |<#wwlabel>
+ |<#3dlabel>
+ Caption of a window. For standard Windows buttons, labels and so on
+ not a caption of a window, but text of the window. }
+ property Text: KOLString read GetCaption write SetCaption;
+ {* |<#edit>
+ |<#memo>
+ The same as Caption. To make more convenient with Edit controls. For
+ Rich Edit control, use property RE_Text. }
+
+ {$IFDEF GDI}
+ property SelStart: Integer read GetSelStart write SetSelStart;
+ {* |<#edit>
+ |<#memo>
+ |<#richedit>
+ Start of selection (editbox - character position). }
+ property SelLength: Integer read GetSelLength write SetSelLength;
+ {* |<#edit>
+ |<#memo>
+ |<#richedit>
+ |<#listbox>
+ |<#listview>
+ Length of selection (editbox - number of characters selected, multiselect
+ listbox or listview - number of items selected).
+ |<br>
+ Note, that for combobox and single-select listbox it always returns 0
+ (though for single-select listview, returns 1, if there is an item
+ selected).
+ |<br>
+ It is possible to set SelLength only for memo and richedit controls. }
+
+ property Selection: KOLString read GetSelection write SetSelection;
+ {* |<#edit>
+ |<#memo>
+ |<#richedit>
+ Selected text (editbox, richedit) as string. Can be useful to replace
+ selection. For rich edit, use RE_Text[ reText, TRUE ], if you want to
+ read correctly characters from another locale then ANSI only. }
+ procedure SelectAll;
+ {* |<#edit>
+ |<#memo>
+ |<#richedit>
+ Makes all the text in editbox or RichEdit, or all items in listbox
+ selected. }
+
+ procedure ReplaceSelection( const Value: KOLString; aCanUndo: Boolean );
+ {* |<#edit>
+ |<#memo>
+ |<#richedit>
+ Replaces selection (in edit, RichEdit). Unlike assigning new value
+ to Selection property, it is possible to specify, if operation can
+ be undone.
+ |<br>
+ Use this method or assigning value to a Selection property to format
+ text initially in the rich edit. E.g.:
+ ! RichEdit1.RE_FmtBold := TRUE;
+ ! RichEdit1.Selection := 'bolded text'#13#10;
+ ! RichEdit1.RE_FmtBold := FALSE;
+ ! RichEdit1.RE_FmtItalic := TRUE;
+ ! RichEdit1.Selection := 'italized text';
+ !... }
+
+ procedure DeleteLines( FromLine, ToLine: Integer );
+ {* |<#edit>
+ |<#memo>
+ |<#richedit>
+ Deletes lines from FromLine to ToLine (inclusively, i.e. 0 to 0 deletes
+ one line with index 0). Current selection is restored as possible. }
+ property CurIndex: Integer read GetCurIndex write SetCurIndex;
+ {* |<#listbox>
+ |<#combo>
+ |<#toolbar>
+ Index of current item (for listbox, combobox) or button index pressed
+ or dropped down (for toolbar button, and only in appropriate event
+ handler call).
+ |<br>
+ You cannot use it to set or remove a selection in a multiple-selection
+ list box, so you should set option loNoExtendSel to true.
+ |<br>
+ In OnClick event handler, CurIndex has not yet changed for listbox or combobox.
+ Use OnSelChange to respond to selection changes. }
+
+ property Count: Integer read GetItemsCount write SetItemsCount;
+ {* |<#listbox>
+ |<#combo>
+ |<#listview>
+ |<#treeview>
+ |<#edit>
+ |<#memo>
+ |<#richedit>
+ |<#toolbar>
+ Number of items (listbox, combobox, listview) or lines (multiline
+ editbox, richedit control) or buttons (toolbar). It is possible to
+ assign a value to this property only for listbox control with loNoData
+ style and for list view control with lvoOwnerData style (virtual list
+ box and list view). }
+
+ property Items[ Idx: Integer ]: KOLString read GetItems write SetItems;
+ {* |<#edit>
+ |<#listbox>
+ |<#combo>
+ |<#memo>
+ |<#richedit>
+ Obvious. Used with editboxes, listbox, combobox. With list view, use
+ property LVItems instead. }
+
+ function Item2Pos( ItemIdx: Integer ): DWORD;
+ {* |<#edit>
+ |<#memo>
+ Only for edit controls: converts line index to character position. }
+ function Pos2Item( Pos: Integer ): DWORD;
+ {* |<#edit>
+ |<#memo>
+ Only for edit controls: converts character position to line index. }
+
+ function SavePosition: TEditPositions;
+ {* |<#edit>
+ |<#memo>
+ Only for edit controls: saves current editor selection and scroll
+ positions. To restore position, use RestorePosition with a structure,
+ containing saved position as a parameter. }
+ procedure RestorePosition( const p: TEditPositions );
+ {* |<#edit>
+ |<#memo>
+ Call RestorePosition with a structure, containing saved position
+ as a parameter (this structure filled in in SavePosition method).
+ If you set RestoreScroll to FALSE, only selection is restored,
+ without scroll position. }
+ procedure UpdatePosition( var p: TEditPositions; FromPos,
+ CountInsertDelChars, CountInsertDelLines: Integer );
+ {* |<#edit>
+ |<#memo>
+ If you called SavePosition and then make some changes in the edit control,
+ calling RestorePosition will fail if chages are affecting selection size.
+ The problem can be solved updating saved position info using this method.
+ Pass a count of inserted characters and lines as a positive number and a
+ count of deleted characters as a negative number here. CountInsertDelLines
+ is optional paramters: if you do not specify it, only selection is fixed.
+ }
+
+ function EditTabChar: PControl;
+ {* |<#edit>
+ |<#memo>
+ Call this method (once) to provide insertion of tab character (code #9)
+ when tab key is pressed on keyboard. }
+
+ function IndexOf( const S: KOLString ): Integer;
+ {* |<#listbox>
+ |<#combobox>
+ |<#tabcontrol>
+ Works for the most of control types, though some of those
+ have its own methods to search given item. If a control is not
+ list box or combobox, item is finding by enumerating all
+ the Items one by one. See also SearchFor method. }
+ function SearchFor( const S: KOLString; StartAfter: Integer; Partial: Boolean ): Integer;
+ {* |<#listbox>
+ |<#combobox>
+ |<#tabcontrol>
+ Works for the most of control types, though some of those
+ have its own methods to search given item. If a control is not
+ list box or combobox, item is finding by enumerating all
+ the Items one by one. See also IndexOf method. }
+
+ property ItemSelected[ ItemIdx: Integer ]: Boolean read GetItemSelected write SetItemSelected;
+ {* |<#edit>
+ |<#memo>
+ |<#listbox>
+ |<#combo>
+ |<#listview>
+ Returns True, if a line (in editbox) or an item (in listbox, combobox,
+ listview) is selected.
+ Can be set only for listboxes. For listboxes, which are not multiselect, and
+ for combo lists, it is possible only to set to True, to change selection. }
+
+ property ItemData[ Idx: Integer ]: DWORD read GetItemData write SetItemData;
+ {* |<#listbox>
+ |<#combo>
+ Access to user-defined data, associated with the item of a list box and
+ combo box. }
+ property OnDropDown: TOnEvent index idx_FOnDropDown
+ read {$IFDEF EVENTS_DYNAMIC} Get_TOnEvent {$ELSE} EV.fOnDropDown {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_TOnEvent {$ELSE} EV.fOnDropDown {$ENDIF};
+ {* |<#combo>
+ |<#toolbar>
+ Is called when combobox is dropped down (or drop-down button of
+ toolbar is pressed - see also OnTBDropDown). }
+ property OnCloseUp: TOnEvent index idx_FOnCloseUp
+ read {$IFDEF EVENTS_DYNAMIC} Get_TOnEvent {$ELSE} EV.fOnCloseUp {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_TOnEvent {$ELSE} EV.fOnCloseUp {$ENDIF};
+ {* |<#combo>
+ Is called when combobox is closed up. When drop down list is closed
+ because user pressed "Escape" key, previous selection is restored.
+ To test if it is so, call GetKeyState( VK_ESCAPE ) and check, if
+ negative value is returned (i.e. Escape key is pressed when event
+ handler is calling). }
+ property DroppedWidth: Integer read DF.FDroppedWidth write SetDroppedWidth;
+ {* |<#combo>
+ Allows to change width of dropped down items list for combobox (only!)
+ control. }
+ property DroppedDown: Boolean read GetDroppedDown write SetDroppedDown;
+ {* |<#combo>
+ Dropped down state for combo box. Set it to TRUE or FALSE to change
+ dropped down state. }
+ procedure AddDirList( const Filemask: KOLString; Attrs: DWORD );
+ {* |<#listbox>
+ |<#combo>
+ Can be used only with listbox and combobox - to add directory list items,
+ filtered by given Filemask (can contain wildcards) and Attrs. Following
+ flags can be combined in Attrs:
+ |<table border=0>
+ |&L=<tr><td>%1</td><td>
+ <L DDL_ARCHIVE> Include archived files. <E>
+ <L DDL_DIRECTORY> Includes subdirectories. Subdirectory names are
+ enclosed in square brackets ([ ]). <E>
+ <L DDL_DRIVES> Includes drives. Drives are listed in the form [-x-],
+ where x is the drive letter. <E>
+ <L DDL_EXCLUSIVE> Includes only files with the specified attributes.
+ By default, read-write files are listed even if DDL_READWRITE is
+ not specified. Also, this flag needed to list directories only,
+ etc. <E>
+ <L DDL_HIDDEN> Includes hidden files. <E>
+ <L DDL_READONLY> Includes read-only files. <E>
+ <L DDL_READWRITE> Includes read-write files with no additional
+ attributes. <E>
+ <L DDL_SYSTEM> Includes system files. <E>
+ </table>
+ If the listbox is sorted, directory items will be sorted (alpabetically). }
+ property OnBitBtnDraw: TOnBitBtnDraw
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnBitBtnDraw {$ELSE} EV.fOnBitBtnDraw {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_OnBitBtnDraw {$ELSE} EV.fOnBitBtnDraw {$ENDIF};
+ {* |<#bitbtn>
+ Special event for BitBtn. Using it, it is possible to provide
+ additional effects, such as highlighting button text (by changing
+ its Font and other properties). If the handler returns True, it is
+ supposed that it made all drawing and there are no further drawing
+ occure. }
+ property BitBtnDrawMnemonic: Boolean read DF.fBitBtnDrawMnemonic write SetBitBtnDrawMnemonic;
+ {* |<#bitbtn>
+ Set this property to TRUE to provide correct drawing of bit btn control
+ caption with '&' characters (to remove such characters, and underline
+ follow ones). }
+ property TextShiftX: Integer read DF.fTextShiftX write DF.fTextShiftX;
+ {* |<#bitbtn>
+ Horizontal shift for bitbtn text when the bitbtn is pressed. }
+ property TextShiftY: Integer read DF.fTextShiftY write DF.fTextShiftY;
+ {* |<#bitbtn>
+ Vertical shift for bitbtn text when the bitbtn is pressed. }
+ property BitBtnImgIdx: Integer read GetBitBtnImgIdx write SetBitBtnImgIdx;
+ {* |<#bitbtn>
+ BitBtn image index for the first image in list view, used as bitbtn
+ image. It is used only in case when BitBtn is created with bboImageList
+ option. }
+ property BitBtnImgList: THandle read GetBitBtnImageList write SetBitBtnImageList;
+ {* |<#bitbtn>
+ BitBtn Image list. Assign image list handle to change it. }
+
+ function SetButtonIcon( aIcon: HIcon ): PControl;
+ {* |<#button>
+ Sets up button icon image and changes its styles. Returns button itself. }
+ function SetButtonBitmap( aBmp: HBitmap ): PControl;
+ {* |<#button>
+ Sets up button icon image and changes its styles. Returns button itself. }
+
+ property OnMeasureItem: TOnMeasureItem
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnMeasureItem {$ELSE} EV.fOnMeasureItem {$ENDIF}
+ write SetOnMeasureItem;
+ {* |<#combo>
+ |<#listbox>
+ |<#listview>
+ This event is called for owner-drawn controls, such as list box, combo box,
+ list view with appropriate owner-drawn style. For fixed item height controls
+ (list box with loOwnerDrawFixed style, combobox with coOwnerDrawFixed and
+ list view with lvoOwnerDrawFixed option) this event is called once. For
+ list box with loOwnerDrawVariable style and for combobox with coOwnerDrawVariable
+ style this event is called for every item. }
+
+ property DefaultBtn: Boolean index 13
+ {$IFDEF F_P} read GetDefaultBtn
+ {$ELSE DELPHI} read {$IFDEF USE_FLAGS} GetDefaultBtn {$ELSE} fDefaultBtn {$ENDIF}
+ {$ENDIF F_P/DELPHI} write SetDefaultBtn;
+ {* |<#button>
+ |<#bitbtn>
+ Set this property to true to make control clicked when ENTER key is pressed.
+ This property uses OnMessage event of the parent form, storing it into
+ fOldOnMessage field and calling in chain. So, assign default button
+ after setting OnMessage event for the form. }
+ property CancelBtn: Boolean index 27
+ {$IFDEF F_P} read GetDefaultBtn
+ {$ELSE DELPHI} read {$IFDEF USE_FLAGS} GetDefaultBtn {$ELSE} fCancelBtn {$ENDIF}
+ {$ENDIF F_P/DELPHI} write SetDefaultBtn;
+ {* |<#button>
+ |<#bitbtn>
+ Set this property to true to make control clicked when escape key is pressed.
+ This property uses OnMessage event of the parent form, storing it into
+ fOldOnMessage field and calling in chain. So, assign cancel button
+ after setting OnMessage event for the form. }
+ function AllBtnReturnClick: PControl;
+ {* Call this method for a form or control to provide clicking
+ a focused button when ENTER pressed. By default, a button can be clicked
+ only by SPACE key from the keyboard, or by mouse. }
+ property IgnoreDefault: Boolean
+ read {$IFDEF USE_FLAGS} GetIgnoreDefault {$ELSE} fIgnoreDefault {$ENDIF}
+ write {$IFDEF USE_FLAGS} SetIgnoreDefault {$ELSE} fIgnoreDefault {$ENDIF};
+ {* Change this property to TRUE to ignore default button reaction on
+ press ENTER key when a focus is grabbed of the control. Default
+ value is different for different controls. By default, DefaultBtn
+ ignored in memo, richedit (even if read-only). }
+
+ {$ENDIF GDI}
+ property Color: TColor read fColor write SetCtlColor;
+ {* Property Color is one of the most common for all visual
+ elements (like form, control etc.) Please note, that standard GUI button
+ can not change its color and the most characteristics of the Font. Also,
+ standard button can not become Transparent. Use bitbtn for such purposes.
+ Also, changing Color property for some kinds of control has no effect (rich edit,
+ list view, tree view, etc.). To solve this, use native (for such controls)
+ color property, or call Perform method with appropriate message to set the
+ background color. }
+ property Font: PGraphicTool read GetFont;
+ {* If the Font property is not accessed, correspondent TGraphicTool object
+ is not created and its methods are not included into executable. Leaving
+ properties Font and Brush untouched can economy executable size a lot. }
+ {$IFDEF GDI}
+ property Brush: PGraphicTool read GetBrush;
+ {* If not accessed, correspondent TGraphicTool object is not created
+ and its methods are not referenced. See also note on Font property. }
+
+ property Ctl3D: Boolean read Get_Ctl3D write SetCtl3D;
+ {* Inheritable from parent controls to child ones. }
+
+ procedure Show;
+ {* |<#appbutton>
+ |<#form>
+ Makes control visible and activates it. }
+ function ShowModal: Integer;
+ {* |<#form>
+ Can be used only with a forms to show it modal. See also global function
+ ShowMsgModal.
+ |<br>
+ To use a form as a modal, it is possible to make it either auto-created
+ or dynamically created. For a first case, You (may be prefer to hide a
+ form after showing it as a modal:
+ !
+ ! procedure TForm1.Button1Click( Sender: PObj );
+ ! begin
+ ! Form2.Form.ShowModal;
+ ! Form2.Form.Hide;
+ ! end;
+ !
+ Another way is to create modal form just before showing it (this economies
+ system resources):
+ !
+ ! procedure TForm1.Button1Click( Sender: PObj );
+ ! begin
+ ! NewForm2( Form2, Applet );
+ ! Form2.Form.ShowModal;
+ ! Form2.Form.Free; // Never call Form2.Free or Form2.Form.Close
+ ! end; // but always Form2.Form.Free; (!)
+ !
+ In samples above, You certainly can place any wished code before and after
+ calling ShowModal method.
+ |<br>
+ Do not forget that if You have more than a single form in your project,
+ separate Applet object should be used.
+ |<br>
+ See also ShowModalEx.
+ }
+ function ShowModalParented( const AParent: PControl ): Integer;
+ {* by Alexander Pravdin. The same as ShowModal, but with a certain
+ form as a parent. }
+ function ShowModalEx: Integer;
+ {* The same as ShowModal, but all the windows of current thread are
+ disabled while showing form modal. This is useful if KOL form from
+ a DLL is used modally in non-KOL application. }
+ property ModalResult: Integer read DF.fModalResult
+ write {$IFDEF USE_SETMODALRESULT} SetModalResult {$ELSE} DF.fModalResult {$ENDIF};
+ {* |<#form>
+ Modal result. Set it to value<>0 to stop modal dialog. By agreement,
+ value 1 corresponds 'OK', 2 - 'Cancel'. But it is totally by decision
+ of yours how to interpret this value. }
+ property Modal: Boolean read GetModal;
+ {* |<#form>
+ TRUE, if the form is shown modal. }
+ property ModalForm: PControl read DF.fModalForm write DF.fModalForm;
+ {* |<#form>
+ |<#appbutton>
+ Form currently shown modal from this form or from Applet. }
+
+ procedure Hide;
+ {* |<#appbutton>
+ |<#form>
+ Makes control hidden. }
+ property OnShow: TOnEvent
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnShow {$ELSE} EV.FOnShow {$ENDIF}
+ write SetOnShow;
+ {* Is called when a control or form is to be shown. This event is not fired
+ for a form, if its WindowState initially is set to wsMaximized or
+ wsMinimized. This behaviour is by design (the window does not receive
+ WM_SHOW message in such case). }
+ property OnHide: TOnEvent
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnHide {$ELSE} EV.FOnHide {$ENDIF}
+ write SetOnHide;
+ {* Is called when a control or form becomes hidden. }
+ property WindowState: TWindowState read GetWindowState write SetWindowState;
+ {* |<#form>
+ Window state. }
+
+ {$ENDIF GDI}
+ property Canvas: PCanvas read GetCanvas;
+ {* |<#paintbox>
+ Placeholder for Canvas: PCanvas. But in KOL, it is possible to
+ create applets without canvases at all. To do so, avoid using
+ Canvas and use DC directly (which is passed in OnPaint event). }
+ {$IFDEF GDI}
+ function CallDefWndProc( var Msg: TMsg ): Integer;
+ {* Function to be called in WndProc method to redirect message handling
+ to default window procedure. }
+ function DoSetFocus: Boolean;
+ {* Sets focus for Enabled window. Returns True, if success. }
+
+ procedure MinimizeNormalAnimated;
+ {* |<#form>
+ Apply this method to a main form (not to another form or Applet,
+ even when separate Applet control is not used and main form matches it!).
+ This provides normal animated visual minimization for the application.
+ It therefore has no effect, if animation during minimize/resore is
+ turned off by user.
+ |<br>
+ Applying this method also provides for the main form (only for it)
+ correct restoring the form maximized if it was maximized while
+ minimizing the application. See also RestoreNormalMaximized method. }
+ procedure RestoreNormalMaximized;
+ {* |<#form>
+ Apply to any form for which it is important to restore it maximized
+ when the application was minimizing while such form was maximized.
+ If the method MinimizeNormalAnimated was called for the main form,
+ then the correct behaviour is already provided for the main form, so
+ in such case it is no more necessary to call also this method, but
+ calling it therefore is not an error. }
+
+ property OnMessage: TOnMessage
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnMessage {$ELSE} EV.fOnMessage {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_OnMessage {$ELSE} EV.fOnMessage {$ENDIF};
+ {* |<#appbutton>
+ |<#form>
+ Is called for every message processed by TControl object. And for
+ Applet window, this event is called also for all messages, handled by
+ all its child windows (forms). }
+
+ {$ENDIF GDI}
+ function IsMainWindow: Boolean;
+ {* |<#appbutton>
+ |<#form>
+ Returns True, if a window is the main in application (created first
+ after the Applet, or matches the Applet). }
+ property IsApplet: Boolean read {$IFDEF USE_FLAGS} GetIsApplet {$ELSE} FIsApplet {$ENDIF};
+ {* Returns true, if the control is created using NewApplet (or CreateApplet).
+ }
+ property IsForm: Boolean read {$IFDEF USE_FLAGS} GetIsForm {$ELSE} fIsForm {$ENDIF};
+ {* Returns True, if the object is form window. }
+ property IsMDIChild: Boolean read {$IFDEF USE_FLAGS} GetIsMDIChild {$ELSE} fIsMDIChild {$ENDIF};
+ {* Returns TRUE, if the object is MDI child form. In such case, IsForm also
+ returns TRUE. }
+ property IsControl: Boolean read {$IFDEF USE_FLAGS} GetIsControl {$ELSE} fIsControl {$ENDIF};
+ {* Returns True, is the control is control (not form or applet). }
+ property IsButton: Boolean read {$IFDEF USE_FLAGS} GetIsButton {$ELSE} fIsButton {$ENDIF};
+ {* Returns True, if the control is button-like or containing buttons (button,
+ bitbtn, checkbox, radiobox, toolbar). }
+
+ {$IFDEF GDI}
+ function ProcessMessage: Boolean;
+ {* |<#appbutton>
+ Processes one message. See also ProcessMessages. }
+
+ procedure ProcessMessages;
+ {* |<#appbutton>
+ Processes pending messages during long cycle of calculation,
+ allowing to window to be repainted if needed and to respond to other
+ messages. But if there are no such messages, your application can be
+ stopped until such one appear in messages queue. To prevent such
+ situation, use method ProcessPendingMessages instead. }
+
+ procedure ProcessMessagesEx;
+ {* Version of ProcessMessages, which works always correctly, even if
+ the application is minimized or background. }
+
+ procedure ProcessPendingMessages;
+ {* |<#appbutton>
+ Similar to ProcessMessages, but without waiting of
+ message in messages queue. I.e., if there are no pending
+ messages, this method immediately returns control to your
+ code. This method is better to call during long cycle of
+ calculation (then ProcessMessages). }
+ procedure ProcessPaintMessages;
+ {* }
+ function WndProc( var Msg: TMsg ): Integer; virtual; //{$IFNDEF DEBUG_MCK} virtual; {$ENDIF}
+ {* Responds to all Windows messages, posted (sended) to the
+ window, before all other proceeding. You can override it in
+ derived controls, but in KOL there are several other ways
+ to control message flow of existing controls without deriving
+ another costom controls for only such purposes. See OnMessage,
+ AttachProc. }
+ property HasBorder: Boolean read GetHasBorder write SetHasBorder;
+ {* |<#form>
+ Obvious. Form-aware. }
+
+ property HasCaption: Boolean read GetHasCaption write SetHasCaption;
+ {* |<#form>
+ Obvious. Form-aware. }
+ property CanResize: Boolean read GetCanResize write SetCanResize;
+ {* |<#form>
+ Obvious. Form-aware. }
+ property StayOnTop: Boolean read GetStayOnTop write SetStayOnTop;
+ {* |<#form>
+ Obvious. Form-aware, but can be applied to controls. }
+ property Border: ShortInt read fMargin write fMargin;
+ {* |<#form>
+ Distance between edges and child controls and between child
+ controls by default (if methods PlaceRight, PlaceDown, PlaceUnder,
+ ResizeParent, ResizeParentRight, ResizeParentBottom are called).
+ |<br>
+ Originally was named Margin, now I recommend to use the name 'Border' to
+ avoid confusion with MarginTop, MarginBottom, MarginLeft and
+ MarginRight properties.
+ |<br>
+ Initial value is always 2. Border property is used in realigning
+ child controls (when its Align property is not caNone), and value
+ of this property determines size of borders between edges of children
+ and its parent and between aligned controls too.
+ |<br>
+ See also properties MarginLeft, MarginRight, MarginTop, MarginBottom. }
+ function SetBorder( Value: Integer ): PControl;
+ {* Assigns new Border value, and returns @ Self. }
+
+ property Margin: ShortInt read fMargin write fMargin;
+ {* |<#form>
+ Old name for property Border. }
+
+ property MarginTop: ShortInt index 1
+ {$IFDEF F_P} read GetClientMargin
+ {$ELSE DELPHI} read fClientTop
+ {$ENDIF F_P/DELPHI} write SetClientMargin;
+ {* Additional distance between true window client top and logical top of
+ client rectangle. This value is added to Top of rectangle, returning
+ by property ClientRect. Together with other margins and property Border,
+ this property allows to change view of form for case, that Align property
+ is used to align controls on parent (it is possible to provide some
+ distance from child controls to its parent, and between child controls.
+ |<br>
+ Originally this property was introduced to compensate incorrect
+ ClientRect property, calculated for some types of controls.
+ |<br>
+ See also properties Border, MarginBottom, MarginLeft, MarginRight. }
+ property MarginBottom: ShortInt index 2
+ {$IFDEF F_P} read GetClientMargin
+ {$ELSE DELPHI} read fClientBottom
+ {$ENDIF F_P/DELPHI} write SetClientMargin;
+ {* The same as MarginTop, but a distance between true window Bottom of
+ client rectangle and logical bottom one. Take in attention, that this value
+ should be POSITIVE to make logical bottom edge located above true edge.
+ |<br>
+ See also properties Border, MarginTop, MarginLeft, MarginRight. }
+ property MarginLeft: ShortInt index 3
+ {$IFDEF F_P} read GetClientMargin
+ {$ELSE DELPHI} read fClientLeft
+ {$ENDIF F_P/DELPHI} write SetClientMargin;
+ {* The same as MarginTop, but a distance between true window Left of
+ client rectangle and logical left edge.
+ |<br>
+ See also properties Border, MarginTop, MarginRight, MarginBottom. }
+ property MarginRight: ShortInt index 4
+ {$IFDEF F_P} read GetClientMargin
+ {$ELSE DELPHI} read fClientRight
+ {$ENDIF F_P/DELPHI} write SetClientMargin;
+ {* The same as MarginLeft, but a distance between true window Right of
+ client rectangle and logical bottom one. Take in attention, that this value
+ should be POSITIVE to make logical right edge located left of true edge.
+ |<br>
+ See also properties Border, MarginTop, MarginLeft, MarginBottom. }
+
+ property Tabstop: Boolean
+ {$IFDEF USE_FLAGS}
+ read GetTabStop write SetTabStop
+ {$ELSE}
+ read fTabstop write fTabstop
+ {$ENDIF}
+ ;
+ {* True, if control can be focused using tabulating between controls.
+ Set it to False to make control unavailable for keyboard, but only
+ for mouse. }
+
+ property TabOrder: SmallInt read fTabOrder write SetTabOrder;
+ {* Order of tabulating of controls. Initially, TabOrder is equal to
+ creation order of controls. If TabOrder changed, TabOrder of
+ all controls with not less value of one is shifted up. To place
+ control before another, assign TabOrder of one to another.
+ For example:
+ ! Button1.TabOrder := EditBox1.TabOrder;
+ In code above, Button1 is placed just before EditBox1 in tabulating
+ order (value of TabOrder of EditBox1 is incremented, as well as
+ for all follow controls). }
+
+ property Focused: Boolean read GetFocused write SetFocused;
+ {* True, if the control is current on form (but check also, what form
+ itself is focused). For form it is True, if the form is active (i.e.
+ it is foreground and capture keyboard). Set this value to True to make
+ control current and focused (if applicable). }
+
+ function BringToFront: PControl;
+ {* Changes z-order of the control, bringing it to the topmost level. }
+ function SendToBack: PControl;
+ {* Changes z-order of the control, sending it to the back of siblings. }
+ {$ENDIF GDI}
+ property TextAlign: TTextAlign read GetTextAlign write SetTextAlign;
+ {* |<#label>
+ |<#panel>
+ |<#button>
+ |<#bitbtn>
+ |<#edit>
+ |<#memo>
+ Text horizontal alignment. Applicable to labels, buttons,
+ multi-line edit boxes, panels. }
+ property VerticalAlign: TVerticalAlign read GetVerticalAlign write SetVerticalAlign;
+ {* |<#button>
+ |<#label>
+ |<#panel>
+ Text vertical alignment. Applicable to buttons, labels and panels. }
+ {$IFDEF GDI}
+ property WordWrap: Boolean
+ {$IFDEF USE_FLAGS}
+ read GetWordWrap write SetWordWrap
+ {$ELSE}
+ read fWordWrap write fWordWrap
+ {$ENDIF USE_FLAGS};
+ {* TRUE, if this is a label, created using NewWordWrapLabel. }
+ property ShadowDeep: Integer read DF.FShadowDeep write SetShadowDeep;
+ {* |<#3dlabel>
+ Deep of a shadow (for label effect only, created calling NewLabelEffect). }
+
+ property CannotDoubleBuf: Boolean
+ {$IFDEF USE_FLAGS}
+ read GetCannotDoubleBuf write SetCannotDoubleBuf
+ {$ELSE}
+ read fCannotDoubleBuf write fCannotDoubleBuf
+ {$ENDIF};
+ {* }
+ property DoubleBuffered: Boolean
+ read {$IFDEF USE_FLAGS} GetDoubleBuffered
+ {$ELSE} fDoubleBuffered {$ENDIF}
+ write SetDoubleBuffered;
+ {* Set it to true for some controls, which are flickering in repainting
+ (like label effect). Slow, and requires additional code. This property
+ is inherited by all child controls.
+ |<br>&nbsp;&nbsp;&nbsp;
+ Note: RichEdit control can not become DoubleBuffered. }
+ function DblBufTopParent: PControl;
+ {* Returns the topmost DoubleBuffered Parent control. }
+ property Transparent: Boolean
+ read {$IFDEF USE_FLAGS} GetTransparent {$ELSE} fTransparent {$ENDIF}
+ write SetTransparent;
+ {* Set it to true to get special effects. Transparency also uses
+ DoubleBuffered and inherited by child controls.
+ |<br>&nbsp;&nbsp;&nbsp;
+ Please note, that some controls can not be shown properly, when
+ Transparent is set to True for it. If You want to make edit control
+ transparent (e.g., over gradient filled panel), handle its OnChanged
+ property and call there Invalidate to provide repainting of edit
+ control content. Note also, that for RichEdit control property
+ Transparent has no effect (as well as DoubleBuffered). But special
+ property RE_Transparent is designed especially for RichEdit control
+ (it works fine, but with great number of flicks while resizing
+ of a control). Another note is about Edit control. To allow editing
+ of transparent edit box, it is necessary to invalidate it for
+ every pressed character. Or, use Ed_Transparent property instead. }
+ property Ed_Transparent: Boolean
+ read {$IFDEF USE_FLAGS} GetTransparent {$ELSE} fTransparent {$ENDIF}
+ write EdSetTransparent;
+ {* |<#edit>
+ |<#memo>
+ Use this property for editbox to make it really Transparent. Remember,
+ that though Transparent property is inherited by child controls from
+ its parent, this is not so for Ed_Transparent. So, it is necessary to
+ set Ed_Transparent to True for every edit control explicitly. }
+ property AlphaBlend: Byte read fAlphaBlend write SetAlphaBlend;
+ {* |<#form>
+ If assigned to 0..254, makes window (form or control) semi-transparent
+ (Win2K only).
+ |<br>
+ Depending on value assigned, it is possible to adjust transparency
+ level ( 0 - totally transparent, 255 - totally opaque).
+ |<br>Note: from XP, any control can be alpha blended! }
+ function MouseTransparent: PControl;
+ {* Call this method to set up mouse transparent control (which always
+ returns HTTRANSPARENT in responce to WM_NCHITTEST). This function
+ returns a pointer to a control itself. }
+
+ property LookTabKeys: TTabKeys read fLookTabKeys write fLookTabKeys;
+ {* Set of keys which can be used as tabulation keys in a control. }
+ procedure GotoControl( Key: DWORD );
+ {* |<#form>
+ Emulates tabulation key press w/o sending message to current control.
+ Can be applied to a form or to any its control. If VK_TAB is used,
+ state of shift kay is checked in: if it is pressed, tabulate is in
+ backward direction. }
+ property SubClassName: KOLString read get_ClassName write set_ClassName;
+ {* Name of window class - unique for every window class
+ in every run session of a program. }
+
+ public
+ procedure SetOnClose( const AOnClose: TOnEventAccept );
+ procedure SetFormOnClick( const AOnClick: TOnEvent );
+ public
+ property OnClose: TOnEventAccept
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnClose {$ELSE} EV.fOnClose {$ENDIF}
+ write SetOnClose;
+ {* |<#form>
+ |<#applet>
+ Called before closing the window. It is possible to set Accept
+ parameter to False to prevent closing the window. This event events
+ is not called when windows session is finishing (to handle this
+ event, handle WM_QUERYENDSESSION message, or assign OnQueryEndSession
+ event to another or the same event handler). }
+
+ property OnQueryEndSession: TOnEventAccept
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnQueryEndSession {$ELSE} EV.fOnQueryEndSession {$ENDIF}
+ write SetOnQueryEndSession;
+ {* |<#form>
+ |<#applet>
+ Called when WM_QUERYENDSESSION message come in. It is possible to set Accept
+ parameter to False to prevent closing the window (in such case session ending
+ is halted). It is possible to check CloseQueryReason property to find out,
+ why event occur.
+ |<br>
+ To provide normal application close while handling OnQueryEndSession,
+ call in your code PostQuitMessage( 0 ) or call method Close for the main form,
+ this is enough to provide all OnClose and OnDestroy handlers to be called. }
+ property CloseQueryReason: TCloseQueryReason read DF.fCloseQueryReason;
+ {* Reason why OnClose or OnQueryEndSession called. }
+ property OnMinimize: TOnEvent index 0 read
+ {$IFDEF F_P} GetOnMinMaxRestore {$ELSE DELPHI}
+ {$IFDEF EVENTS_DYNAMIC} GetOnMinMaxRestore
+ {$ELSE} EV.fOnMinimize {$ENDIF}
+ {$ENDIF F_P/DELPHI} write SetOnMinMaxRestore;
+ {* |<#form>
+ Called when window is minimized. }
+ property OnMaximize: TOnEvent index 8 read
+ {$IFDEF F_P} GetOnMinMaxRestore {$ELSE DELPHI}
+ {$IFDEF EVENTS_DYNAMIC} GetOnMinMaxRestore
+ {$ELSE} EV.fOnMaximize {$ENDIF}
+ {$ENDIF F_P/DELPHI} write SetOnMinMaxRestore;
+ {* |<#form>
+ Called when window is maximized. }
+ property OnRestore: TOnEvent index 16 read
+ {$IFDEF F_P} GetOnMinMaxRestore {$ELSE DELPHI}
+ {$IFDEF EVENTS_DYNAMIC} GetOnMinMaxRestore
+ {$ELSE} EV.fOnMaximize {$ENDIF}
+ {$ENDIF F_P/DELPHI} write SetOnMinMaxRestore;
+ {* |<#form>
+ Called when window is restored from minimized or maximized state. }
+
+ property UpdateRgn: HRgn read fUpdRgn;
+ {* A handle of update region. Valid only in OnPaint method. You
+ can use it to improve painting (for speed), if necessary. When
+ UpdateRgn is obtained in response to WM_PAINT message, value
+ of the property EraseBackground is used to pass it to the API
+ function GetUpdateRgn. If UpdateRgn = 0, this means that entire
+ window should be repainted. Otherwise, You (e.g.) can check
+ if the rectangle is in clipping region using API function
+ RectInRegion. }
+
+ property EraseBackground: Boolean
+ read {$IFDEF USE_FLAGS} GetEraseBackground {$ELSE} fEraseUpdRgn {$ENDIF}
+ write {$IFDEF USE_FLAGS} SetEraseBackground {$ELSE} fEraseUpdRgn {$ENDIF};
+ {* This value is used to pass it to the API function GetUpdateRgn,
+ when UpadateRgn property is obtained first in responce to WM_PAINT
+ message. If EraseBackground is set to True, system is responsible
+ for erasing background of update region before painting. If not
+ (default), the entire region invalidated should be painted by your
+ event handler. }
+ {$ENDIF GDI}
+ property OnPaint: TOnPaint
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnPaint {$ELSE} EV.fOnPaint {$ENDIF}
+ write SetOnPaint;
+ {* Event to set to override standard control painting. Can be applied
+ to any control (though originally was designed only for paintbox
+ control). When an event handler is called, it is possible to use
+ UpdateRgn to examine what parts of window require painting to
+ improve performance of the painting operation. }
+ {$IFDEF GDI}
+ property OnPrePaint: TOnPaint
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnPrePaint {$ELSE} EV.fOnPrePaint {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_OnPrePaint {$ELSE} EV.fOnPrePaint {$ENDIF};
+ {* Only for graphic controls. If you assign it, call Invalidate also. }
+ property OnPostPaint: TOnPaint
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnPostPaint {$ELSE} EV.fOnPostPaint {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_OnPostPaint {$ELSE} EV.fOnPostPaint {$ENDIF};
+ {* Only for graphic controls. If you assign it, call Invalidate also. }
+
+ property OnEraseBkgnd: TOnPaint
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnEraseBkgnd {$ELSE} EV.fOnEraseBkgnd {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_OnEraseBkgnd {$ELSE} SetOnEraseBkgnd {$ENDIF};
+ {* This event allows to override erasing window background in response
+ to WM_ERASEBKGND message. This allows to add some decorations to
+ standard controls without overriding its painting in total.
+ Note: When erase background, remember, that property ClientRect can
+ return not true client rectangle of the window - use GetClientRect
+ API function instead. For example:
+ !
+ !var BkBmp: HBitmap;
+ !
+ !procedure TForm1.KOLForm1FormCreate(Sender: PObj);
+ !begin
+ ! Toolbar1.OnEraseBkgnd := DecorateToolbar;
+ ! BkBmp := LoadBitmap( hInstance, 'BK1' );
+ !end;
+ !
+ !procedure TForm1.DecorateToolbar(Sender: PControl; DC: HDC);
+ !var CR: TRect;
+ !begin
+ ! GetClientRect( Sender.Handle, CR );
+ ! Sender.Canvas.Brush.BrushBitmap := BkBmp;
+ ! Sender.Canvas.FillRect( CR );
+ !end;
+ !
+ }
+
+ {$ENDIF GDI}
+ property OnClick: TOnEvent
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnClick {$ELSE} EV.fOnClick {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} SetOnClick {$ELSE}
+ {$IFDEF GDI} EV.fOnClick
+ {$ELSE _X_} SetOnClick {$ENDIF _X_}{$ENDIF};
+ {* |<#button>
+ |<#checkbox>
+ |<#radiobox>
+ |<#toolbar>
+ Called on click at control. For buttons, checkboxes and radioboxes
+ is called regadless if control clicked by mouse or keyboard. For toolbar,
+ the same event is used for all toolbar buttons and toolbar itself.
+ To determine which toolbar button is clicked, check CurIndex property.
+ And note, that all the buttons including separator buttons are enumerated
+ starting from 0. Though images are stored (and prepared) only for
+ non-separator buttons. And to determine, if toolbar button was clicked
+ with right mouse button, check RightClick property.
+ |<br>
+ This event does not work on a Form, still it is fired in responce to
+ WM_COMMAND window message mainly rather direct to mouse down. But, if
+ you want to have OnClick event to be fired on a Form, use (following)
+ property OnFormClick to assign it. }
+ {$IFDEF GDI}
+ property OnFormClick: TOnEvent
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnClick {$ELSE} EV.fOnClick {$ENDIF}
+ write SetFormOnClick;
+ {* |<#form>
+ Assign you OnClick event handler using this property, if you want it to
+ be fired in result of mouse click on a form surface. Use to assign the
+ event only for forms (to avoid doublicated firing the handler).
+ |<br>
+ Note: for a form, in case of WM_xDOUBLECLK event, this event is fired
+ for both clicks. So if you install both OnFormClick and OnMouseDblClk,
+ handlers will be called in the following sequence for each double click:
+ OnFormClick; OnMouseDblClk; OnFormClick. }
+ property RightClick: Boolean read {$IFDEF USE_FLAGS} Get_RightClick {$ELSE} fRightClick {$ENDIF};
+ {* |<#toolbar>
+ |<#listview>
+ Use this property to determine which mouse button was clicked
+ (applicable to toolbar in the OnClick event handler). }
+ property OnEnter: TOnEvent index idx_fOnEnter
+ read {$IFDEF EVENTS_DYNAMIC} Get_TOnEvent {$ELSE} EV.fOnEnter {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_TOnEvent {$ELSE} EV.fOnEnter {$ENDIF};
+ {* Called when control receives focus. }
+ property OnLeave: TOnEvent index idx_fOnLeave
+ read {$IFDEF EVENTS_DYNAMIC} Get_TOnEvent {$ELSE} EV.fOnLeave {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_TOnEvent {$ELSE} EV.fOnLeave{$ENDIF};
+ {* Called when control looses focus. }
+ property OnChange: TOnEvent index idx_fOnChangeCtl
+ read {$IFDEF EVENTS_DYNAMIC} Get_TOnEvent {$ELSE} EV.fOnChangeCtl {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_TOnEvent {$ELSE} EV.fOnChangeCtl {$ENDIF};
+ {* |<#edit>
+ |<#memo>
+ |<#listbox>
+ |<#combo>
+ |<#tabcontrol>
+ Called when edit control is changed, or selection in listbox or
+ current index in combobox is changed (but if OnSelChanged assigned,
+ the last is called for change selection). To respond to check/uncheck
+ checkbox or radiobox events, use OnClick instead. }
+ property OnSelChange: TOnEvent index idx_fOnSelChange
+ read {$IFDEF EVENTS_DYNAMIC} Get_TOnEvent {$ELSE} EV.fOnSelChange {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_TOnEvent {$ELSE} EV.fOnSelChange{$ENDIF};
+ {* |<#richedit>
+ |<#listbox>
+ |<#combo>
+ |<#treeview>
+ Called for rich edit control, listbox, combobox or treeview when current selection
+ (range, or current item) is changed. If not assigned, but OnChange is
+ assigned, OnChange is called instead. }
+ property OnResize: TOnEvent
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnResize {$ELSE} EV.FOnResize {$ENDIF}
+ write SetOnResize;
+ {* Called whenever control receives message WM_SIZE (thus is, if
+ control is resized. }
+ property OnMove: TOnEvent
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnMove {$ELSE} EV.FOnMove {$ENDIF}
+ write SetOnMove;
+ {* Called whenever control receives message WM_MOVE (i.e. when control is
+ moved over its parent). }
+ property OnMoving: TOnEventMoving
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnMoving {$ELSE} EV.FOnMoving {$ENDIF}
+ write SetOnMoving;
+ {* Called whenever control receives message WM_MOVE (i.e. when control is
+ moved over its parent). }
+
+ property MinSizePrev: Integer read DF.fSplitMinSize1 write DF.fSplitMinSize1;
+ {* |<#splitter>
+ Minimal allowed (while dragging splitter) size of previous control
+ for splitter (see NewSplitter). }
+ property SplitMinSize1: Integer read DF.fSplitMinSize1 write DF.fSplitMinSize1;
+ {* The same as MinSizePrev }
+ property MinSizeNext: Integer read DF.fSplitMinSize2 write DF.fSplitMinSize2;
+ {* |<#splitter>
+ Minimal allowed (while dragging splitter) size of the rest of parent
+ of splitter or of SecondControl (see NewSplitter). }
+ property SplitMinSize2: Integer read DF.fSplitMinSize2 write DF.fSplitMinSize2;
+ {* The same as MinSizeNext. }
+ property SecondControl: PControl read DF.fSecondControl write DF.fSecondControl;
+ {* |<#splitter>
+ Second control to check (while dragging splitter) if its size not less
+ than SplitMinSize2 (see NewSplitter). By default, second control is
+ not necessary, and needed only in rare case when SecondControl can not
+ be determined automatically to restrict splitter right (bottom) position. }
+ property OnSplit: TOnSplit
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnSplit {$ELSE} EV.fOnSplit {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_OnSplit {$ELSE} EV.fOnSplit{$ENDIF};
+ {* |<#splitter>
+ Called when splitter control is dragging - to allow for
+ your event handler to decide if to accept new size of
+ left (top) control, and new size of the rest area of parent. }
+ property Dragging: Boolean read {$IFDEF USE_FLAGS} Get_Dragging {$ELSE} FDragging{$ENDIF};
+ {* |<#splitter>
+ True, if splitter control is dragging now by user with left
+ mouse button. Also, this property can be used to detect if the control
+ is dragging with mouse (after calling DragStartEx method). }
+ procedure DragStart;
+ {* Call this method for a form or control to drag it with left mouse button,
+ when mouse left button is already down. Dragging is stopped when left mouse
+ button is released. See also DragStartEx, DragStopEx. }
+ procedure DragStartEx;
+ {* Call this method to start dragging the form by mouse. To stop
+ dragging, call DragStopEx method. (Tip: to detect mouse up event,
+ use OnMouseUp event of the dragging control). This method can be used
+ to move any control with the mouse, not only entire form. State of
+ mouse button is not significant. Determine dragging state of the control
+ checking its Dragging property. }
+ procedure DragStopEx;
+ {* Call this method to stop dragging the form (started by DragStopEx). }
+ procedure DragItem( OnDrag: TOnDrag );
+ {* Starts dragging something with mouse. During the process,
+ callback function OnDrag is called, which allows to control
+ drop target, change cursor shape, etc. }
+
+ property OnKeyDown: TOnKey
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnKeyDown {$ELSE} EV.fOnKeyDown {$ENDIF}
+ write SetOnKeyDown;
+ {* Obvious. }
+ property OnKeyUp: TOnKey
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnKeyUp {$ELSE} EV.fOnKeyUp {$ENDIF}
+ write SetOnKeyUp;
+ {* Obvious. }
+ property OnChar: TOnChar
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnChar {$ELSE} EV.fOnChar {$ENDIF}
+ write SetOnChar;
+ {* Deprecated event, use OnKeyChar. }
+ property OnKeyChar: TOnChar
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnChar {$ELSE} EV.fOnChar {$ENDIF}
+ write SetOnChar;
+ {* Obviuos. }
+ {$IFDEF SUPPORT_ONDEADCHAR}
+ property OnKeyDeadChar: TOnChar
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnDeadChar {$ELSE} EV.fOnDeadChar {$ENDIF}
+ write SetOnDeadChar;
+ {* Obviuos. }
+ {$ENDIF SUPPORT_ONDEADCHAR}
+
+ {$ENDIF GDI}
+ property OnMouseUp: TOnMouse index idx_fOnMouseUp
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnMouseEvent {$ELSE} EV.fOnMouseUp {$ENDIF}
+ write SetOnMouseEvent;
+ {* Obvious. }
+ property OnMouseDown: TOnMouse index idx_fOnMouseDown
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnMouseEvent {$ELSE} EV.fOnMouseDown {$ENDIF}
+ write SetOnMouseEvent;
+ {* Obvious. }
+ property OnMouseMove: TOnMouse index idx_fOnMouseMove
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnMouseEvent {$ELSE} EV.fOnMouseMove {$ENDIF}
+ write SetOnMouseEvent;
+ {* Obvious. }
+ property OnMouseDblClk: TOnMouse index idx_fOnMouseDblClk
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnMouseEvent {$ELSE} EV.fOnMouseDblClk {$ENDIF}
+ write SetOnMouseEvent;
+ {* Obvious. }
+ property ThreeButtonPress: Boolean
+ read {$IFDEF USE_FLAGS} Get3ButtonPress {$ELSE} f3ButtonPress {$ENDIF};
+ {* GDK (*nix) only. TRUE, if 3 button press detected. Check this flag in
+ OnMouseDblClk event handler. If 3rd button click is done for a short
+ period of time after the double click, the control receives OnMouseDblClk
+ the second time and this flag is set. (Applicable to the GDK and other
+ Linux systems). }
+ property OnMouseWheel: TOnMouse index idx_fOnMouseWheel
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnMouseEvent {$ELSE} EV.fOnMouseWheel {$ENDIF}
+ write SetOnMouseEvent;
+ {* Mouse wheel (up or down) event. In Windows, only focused controls and
+ controls having scrollbars (or a scrollbar iteself) receive such
+ message. To get direction and amount of wheel, use typecast:
+ SmallInt( HiWord( Mouse.Shift ) ). Value 120 corresponds to one wheel
+ step (-120 - for step back). }
+ {$IFDEF GDI}
+
+ property OnMouseEnter: TOnEvent
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnMouseEnter {$ELSE} EV.fOnMouseEnter {$ENDIF}
+ write SetOnMouseEnter;
+ {* Is called when mouse is entered into control. See also OnMouseLeave. }
+ property OnMouseLeave: TOnEvent
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnMouseLeave {$ELSE} EV.fOnMouseLeave {$ENDIF}
+ write SetOnMouseLeave;
+ {* Is called when mouse is leaved control. If this event is assigned,
+ then mouse is captured on mouse enter event to handle all other
+ mouse events until mouse cursor leaves the control. }
+ property OnTestMouseOver: TOnTestMouseOver
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnTestMouseOver {$ELSE} EV.fOnTestMouseOver {$ENDIF}
+ write SetOnTestMouseOver;
+ {* |<#bitbtn>
+ Special event, which allows to extend OnMouseEnter / OnMouseLeave
+ (and also Flat property for BitBtn control). If a handler is assigned
+ to this event, actual testing whether mouse is in control or not,
+ is occuring in the handler. So, it is possible to simulate more
+ careful hot tracking for controls with non-rectangular shape (such
+ as glyphed BitBtn control). }
+
+ property MouseInControl: Boolean
+ read {$IFDEF USE_FLAGS} GetMouseInCtl {$ELSE} fMouseInControl {$ENDIF};
+ {* |<#bitbtn>
+ This property can return True only if OnMouseEnter / OnMouseLeave
+ event handlers are set for a control (or, for BitBtn, property Flat
+ is set to True. Otherwise, False is returned always. }
+
+ property Flat: Boolean read {$IFDEF USE_FLAGS} GetFlat {$ELSE} fFlat {$ENDIF}
+ write SetFlat;
+ {* |<#bitbtn>
+ Set it to True for BitBtn, to provide either flat border for a button
+ or availability of "highlighting" (correspondent to glyph index 4).
+ |<br>
+ Note: this can work incorrectly a bit under win95 without comctl32.dll
+ updated. Therefore, application will launch. To enforce correct working
+ even under Win95, use your own timer, which event handler checks for
+ mouse over bitbtn control, e.g.:
+ ! procedure TForm1.Timer1Timer(Sender: PObj);
+ ! var P: TPoint;
+ ! begin
+ ! if not BitBtn1.MouseInControl then Exit;
+ ! GetCursorPos( P );
+ ! P := BitBtn1.Screen2Client( P );
+ ! if not PtInRect( BitBtn1.ClientRect, P ) then
+ ! begin
+ ! BitBtn1.Flat := FALSE;
+ ! BitBtn1.Flat := TRUE;
+ ! end;
+ ! end;
+ }
+ property RepeatInterval: Integer read DF.fRepeatInterval write DF.fRepeatInterval;
+ {* |<#bitbtn>
+ If this property is set to non-zero, it is interpreted (for BitBtn
+ only) as an interval in milliseconds between repeat button down events,
+ which are generated after first mouse or button click and until
+ button is released. Though, if the button is pressed with keyboard (with
+ space key), RepeatInterval value is ignored and frequency of repeatitive
+ clicking is determined by user keyboard settings only. }
+ function LikeSpeedButton: PControl;
+ {* |<#button>
+ |<#bitbtn>
+ Transparent method (returns control itself). Makes button not focusable. }
+
+ function Add( const S: KOLString ): Integer;
+ {* |<#listbox>
+ |<#combo>
+ Only for listbox and combobox. }
+
+ function Insert( Idx: Integer; const S: KOLString ): Integer;
+ {* |<#listbox>
+ |<#combo>
+ Only for listbox and combobox. }
+ procedure Delete( Idx: Integer );
+ {* |<#listbox>
+ |<#combo>
+ |<#listview>
+ |<#treeview>
+ Only listed controls. }
+ procedure Clear;
+ {* Clears object content. Has different sense for different controls.
+ E.g., for label, editbox, button and other simple controls it
+ assigns empty string to Caption property. For listbox, combobox,
+ listview it deletes all items. For toolbar, it deletes all buttons.
+ Et so on. }
+
+ property Progress: Integer index ((PBM_SETPOS or $8000) shl 16) or PBM_GETPOS
+ read GetIntVal write SetIntVal;
+ {* |<#progressbar>
+ Only for ProgressBar. }
+ property MaxProgress: Integer index ((PBM_SETRANGE32 or $8000) shl 16) or PBM_GETRANGE
+ read GetIntVal write SetMaxProgress;
+ {* |<#progressbar>
+ Only for ProgressBar. 100 is the default value. }
+ property ProgressColor: TColor read fTextColor write SetProgressColor;
+ {* |<#progressbar>
+ Only for ProgressBar. }
+ property ProgressBkColor: TColor read fColor write SetCtlColor; //SetProgressBkColor;
+ {* |<#progressbar>
+ Obsolete. Now the same as Color. }
+
+ property StatusText[ Idx: Integer ]: KOLString read GetStatusText write SetStatusText;
+ {* |<#form>
+ Only for forms to set/retrieve status text to/from given status panel.
+ Panels are enumerated from 0 to 254, 255 is to indicate simple
+ status bar. Size grip in right bottom corner of status window is
+ displayed only if form still CanResize.
+ |<br>
+ When a status text is set first time, status bar window is created
+ (always aligned to bottom), and form is resizing to preset client height.
+ While status bar is showing, client height value is returned without
+ height of status bar. To remove status bar, call RemoveStatus method for
+ a form.
+ |<br>
+ By default, text is left-aligned within the specified part of a status
+ window. You can embed tab characters (#9) in the text to center or
+ right-align it. Text to the right of a single tab character is centered,
+ and text to the right of a second tab character is right-aligned.
+ |<br>
+ If You use separate status bar onto several panels, these automatically
+ align its widths to the same value (width divided to number of panels).
+ To adjust status panel widths for every panel, use property StatusPanelRightX.
+ }
+ property SimpleStatusText: KOLString index 255 read GetStatusText write SetStatusText;
+ {* |<#form>
+ Only for forms to set/retrive status text to/from simple status bar.
+ Size grip in right bottom corner of status window is displayed only
+ if form CanResize.
+ |<br>
+ When status text set first time, (simple) status bar window is created
+ (always aligned to bottom), and form is resizing to preset client height.
+ While status bar is showing, client height value is returned without
+ height of status bar. To remove status bar, call RemoveStatus method for
+ a form.
+ |<br>
+ By default, text is left-aligned within the specified part of a status
+ window. You can embed tab characters (#9) in the text to center or
+ right-align it. Text to the right of a single tab character is centered,
+ and text to the right of a second tab character is right-aligned.
+ }
+ property StatusCtl: PControl read fStatusCtl;
+ {* Pointer to Status bar control. To "create" child controls on
+ the status bar, first create it as a child of form, for instance, and
+ then change its property Parent, e.g.:
+ ! var Progress1: PControl;
+ ! ...
+ ! Progress1 := NewProgressBar( Form1 );
+ ! Progress1.Parent := Form1.StatusCtl;
+ (If you use MCK, code should be another a bit, and in this case it is
+ possible to create and adjust the control at design-time, and at run-time
+ change its parent control. E.g. (Progress1 is created at run-time here too):
+ ! Progress1 := NewProgressBar( Form );
+ ! Progress1.Parent := Form.StatusCtl;
+ ).
+ Do not forget to provide StatusCtl to be existing first (e.g. assign
+ one-space string to SimpleStatusText property of the form, for MCK do
+ so using Object Inspector).
+
+ Please note that not only a form can have status bar
+ but any other control too!
+ }
+ property SizeGrip: Boolean
+ read {$IFDEF USE_FLAGS} GetSizeGrip {$ELSE} fSizeGrip {$ENDIF}
+ write {$IFDEF USE_FLAGS} SetSizeGrip {$ELSE} fSizeGrip {$ENDIF};
+ {* Size grip for status bar. Has effect only before creating window. }
+
+ procedure RemoveStatus;
+ {* |<#form>
+ Call it to remove status bar from a form (created in result of assigning
+ value(s) to StatusText[], SimpleStatusText properties). When status bar is
+ removed, form is resized to preset client height. }
+ function StatusPanelCount: Integer;
+ {* |<#form>
+ Returns number of status panels defined in status bar. }
+ property StatusPanelRightX[ Idx: Integer ]: Integer read GetStatusPanelX write SetStatusPanelX;
+ {* |<#form>
+ Use this property to adjust status panel right edges (if the status bar is
+ divided onto several subpanels). If the right edge for the last panel is
+ set to -1 (by default) it is expanded to the right edge of a form window.
+ Otherwise, status bar can be shorter then form width. }
+ property StatusWindow: HWND read Get_StatusWnd;
+ {* |<#form>
+ Provided for case if You want to use API direct message sending to
+ status bar. }
+
+ property Color1: TColor read DF.fColor1 write SetColor1;
+ {* |<#gradient>
+ Top line color for GradientPanel. }
+ property Color2: TColor read DF.fColor2 write SetColor2;
+ {* |<#gradient>
+ |<#3Dlabel>
+ Bottom line color for GradientPanel, or shadow color for LabelEffect.
+ (If clNone, shadow color for LabelEffect is calculated as a mix bitween
+ TextColor and clBlack). }
+ property GradientStyle: TGradientStyle read DF.fGradientStyle write SetGradientStyle;
+ {* |<#gradient>
+ Styles other then gsVertical and gsHorizontal has effect only for
+ gradient panel, created by NewGradientPanelEx. }
+ property GradientLayout: TGradientLayout read DF.fGradientLayout write SetGradientLayout;
+ {* |<#gradient>
+ Has only effect for gradient panel, created by NewGradientPanelEx.
+ Ignored for styles gsVertical and gsHorizontal. }
+
+ //======== Image lists (for ListView, TreeView, ToolBar and TabControl):
+ property ImageListSmall: PImageList index 16 read GetImgListIdx write SetImgListIdx;
+ {* |<#listview>
+ Image list with small icons used with List View control. If not set,
+ last added (i.e. created with a control as an owner) image list with
+ small icons is used. }
+ property ImageListNormal: PImageList index 32 read GetImgListIdx write SetImgListIdx;
+ {* |<#listview>
+ |<#treeview>
+ |<#tabcontrol>
+ |<#bitbtn>
+ Image list with normal size icons used with List View control (or with
+ icons for BitBtn, TreeView or TabControl). If not set,
+ last added (i.e. created with a control as an owner) image list is used.
+ }
+ property ImageListState: PImageList index 0 read GetImgListIdx write SetImgListIdx;
+ {* |<#listview>
+ |<#treeview>
+ Image list used as a state images list for ListView or TreeView control. }
+
+ //========
+ function SetUnicode( Unicode: Boolean ): PControl;
+ {* |<#listview>
+ |<#treeview>
+ |<#tabcontrol>
+ Sets control as Unicode or not. The control itself is returned as for
+ other "transparent" functions. A conditional define UNICODE_CTRLS must
+ be added to a project to provide handling unicode messages. }
+
+ //======== TabControl-specific properties and methods:
+ property Pages[ Idx: Integer ]: PControl read GetPages;
+ {* |<#tabcontrol>
+ Returns controls, which can be used as parent for controls, placed on
+ different pages of a tab control. Use it like in follows example:
+ | Label1 := NewLabel( TabControl1.Pages[ 0 ], 'Label1' );
+ To find number of pages available, check out Count property of the tab
+ control. Pages are enumerated from 0 to Count - 1, as usual. }
+ property TC_Pages[ Idx: Integer ]: PControl read GetPages;
+ {* |<#tabcontrol>
+ The same as above. }
+ function TC_Insert( Idx: Integer; const TabText: KOLString; TabImgIdx: Integer ): PControl;
+ {* |<#tabcontrol>
+ Inserts new tab before given, returns correspondent page control
+ (which can be used as a parent for controls to place on the page). }
+ procedure TC_Delete( Idx: Integer );
+ {* |<#tabcontrol>
+ Removes tab from tab control, destroying all its child controls. }
+{$IFNDEF OLD_ALIGN}
+ procedure TC_InsertControl( Idx: Integer; const TabText: KOLString; TabImgIdx: Integer; Page: PControl);
+ {* |<#tabcontrol>
+ Inserts new tab before given, but not construt this Page
+ (this control must be created before inserting, and may be not a Panel). }
+ function TC_Remove( Idx: Integer ):PControl;
+ {* |<#tabcontrol>
+ Only removes tab from tab control, and return this Page as Result. }
+{$ENDIF}
+ property TC_Items[ Idx: Integer ]: KOLString read TCGetItemText write TCSetItemText;
+ {* |<#tabcontrol>
+ Text, displayed on tab control tabs. }
+ property TC_Images[ Idx: Integer ]: Integer read TCGetItemImgIDx write TCSetItemImgIdx;
+ {* |<#tabcontrol>
+ Image index for a tab in tab control. }
+ property TC_ItemRect[ Idx: Integer ]: TRect read TCGetItemRect;
+ {* |<#tabcontrol>
+ Item rectangle for a tab in tab control. }
+ procedure TC_SetPadding( cx, cy: Integer );
+ {* |<#tabcontrol>
+ Sets space padding around tab text in a tab of tab control. }
+ function TC_TabAtPos( x, y: Integer ): Integer;
+ {* |<#tabcontrol>
+ Returns index of tab, found at the given position (relative to
+ a client rectangle of tab control). If no tabs found at the
+ position, -1 is returned. }
+ function TC_DisplayRect: TRect;
+ {* |<#tabcontrol>
+ Returns rectangle, occupied by a page rather then tab. }
+ function TC_IndexOf(const S: KOLString): Integer;
+ {* |<#tabcontrol>
+ By Mr Brdo. Index of page by its Caption. }
+ function TC_SearchFor(const S: KOLString; StartAfter: Integer; Partial: Boolean): Integer;
+ {* |<#tabcontrol>
+ By Mr Brdo. Index of page by its Caption. }
+
+ //======== ListView style and options:
+ property LVStyle: TListViewStyle read DF.fLVStyle write SetLVStyle;
+ {* |<#listview>
+ ListView style of view. Can be changed at run time. }
+
+ property LVOptions: TListViewOptions read DF.fLVOptions write SetLVOptions;
+ {* |<#listview>
+ ListView options. Can be changed at run time. }
+
+ property LVTextColor: TColor index LVM_GETTEXTCOLOR
+ {$IFDEF F_P} read LVGetColorByIdx
+ {$ELSE DELPHI} read fTextColor
+ {$ENDIF F_P/DELPHI} write LVSetColorByIdx;
+ {* |<#listview>
+ ListView text color. Use it instead of Font.Color. }
+ property LVTextBkColor: TColor index LVM_GETTEXTBKCOLOR
+ {$IFDEF F_P} read LVGetColorByIdx
+ {$ELSE DELPHI} read DF.fLVTextBkColor
+ {$ENDIF F_P/DELPHI} write LVSetColorByIdx;
+ {* |<#listview>
+ ListView background color for text. }
+ property LVBkColor: TColor read fColor write SetCtlColor; //LVSetBkColor;
+ {* |<#listview>
+ ListView background color. Use it instead of Color. }
+
+ //======== List View columns handling:
+ property LVColCount: Integer read DF.fLVColCount;
+ {* |<#listview>
+ ListView (additional) column count. Value 0 means that there are
+ no columns (single item text / icon is used). If You want
+ to provide several columns, first call LVColAdd to "insert" column 0,
+ i.e. to provide header text for first column (with index 0).
+ If there are no column, nothing will be shown in lvsDetail /
+ lvsDetailNoHeader view style. }
+ procedure LVColAdd( const aText: KOLString; aalign: TTextAlign; aWidth: Integer );
+ {* |<#listview>
+ Adds new column. Pass 'width' <= 0 to provide default column width.
+ 'text' is a column header text. }
+ procedure LVColInsert( ColIdx: Integer; const aText: KOLString; aAlign: TTextAlign; aWidth: Integer );
+ {* |<#listview>
+ Inserts new column at the Idx position (1-based column index). }
+ procedure LVColDelete( ColIdx: Integer );
+ {* |<#listview>
+ Deletes column from List View }
+ property LVColWidth[ Item: Integer ]: Integer index LVM_GETCOLUMNWIDTH
+ read GetItemVal write SetItemVal;
+ {* |<#listview>
+ Retrieves or changes column width. For lvsList view style, the same width
+ is returned for all columns (ColIdx is ignored). It is possible to use
+ special values to assign to a property:
+ |<br> LVSCW_AUTOSIZE - Automatically sizes the column
+ |<br> LVSCW_AUTOSIZE_USEHEADER - Automatically sizes the column to fit
+ the header text
+ |<br>
+ To set coumn width in lvsList view mode, column index must be -1
+ (and Width to set must be in range 0..32767 always). }
+ property LVColText[ Idx: Integer ]: KOLString read GetLVColText write SetLVColText;
+ {* |<#listview>
+ Allows to get/change column header text at run time. }
+ property LVColAlign[ Idx: Integer ]: TTextAlign read GetLVColalign write SetLVColalign;
+ {* |<#listview>
+ Column text aligning. }
+ property LVColImage[ Idx: Integer ]: Integer index LVCF_IMAGE or (24 shl 16) read GetLVColEx write SetLVColEx;
+ {* |<#listview>
+ Only starting from comctrl32.dll of version 4.70 (IE4+). Allows to
+ set an image for list view column itself from the ImageListSmall.
+ }
+ property LVColOrder[ Idx: Integer ]: Integer index LVCF_ORDER or (28 shl 16) read GetLVColEx write SetLVColEx;
+ {* |<#listview>
+ Only starting from comctrl32.dll of version 4.70 (IE4+). Allows to
+ set visual order of the list view column from the ImageListSmall.
+ This value does not affect the index, by which the column is still
+ accessible in the column array.
+ }
+
+ //======== List View items handling:
+ property LVCount: Integer read GetItemsCount write SetItemsCount;
+ {* |<#listview>
+ Returns item count for ListView control. It is possible to use Count
+ property instead when obtaining of item count is needed only. But this this
+ property allows also to set actual count of list view items when a list
+ view is virtual. }
+
+ property LVCurItem: Integer read GetLVCurItem write SetLVCurItem;
+ {* |<#listview>
+ Returns first selected item index in a list view. See also LVNextSelected,
+ LVNextItem and LVFocusItem functions. }
+
+ property LVFocusItem: Integer read GetLVFocusItem;
+ {* |<#listview>
+ Returns focused item index in a list view. See also LVCurItem. }
+
+ function LVNextItem( IdxPrev: Integer; Attrs: DWORD ): Integer;
+ {* |<#listview>
+ Returns an index of the next after IdxPrev item with given attributes in
+ the list view. Attributes can be:
+ LVNI_ALL - Searches for a subsequent item by index, the default value.
+ |<br><br>
+ Searchs by physical relationship to the index of the item where the
+ search is to begin.
+ LVNI_ABOVE - Searches for an item that is above the specified item.
+ LVNI_BELOW - Searches for an item that is below the specified item.
+ LVNI_TOLEFT - Searches for an item to the left of the specified item.
+ LVNI_TORIGHT - Searches for an item to the right of the specified item.
+ |<br><br>
+ The state of the item to find can be specified with one or a combination
+ of the following values:
+ LVNI_CUT - The item has the LVIS_CUT state flag set.
+ LVNI_DROPHILITED - The item has the LVIS_DROPHILITED state flag set
+ LVNI_FOCUSED - The item has the LVIS_FOCUSED state flag set.
+ LVNI_SELECTED - The item has the LVIS_SELECTED state flag set.}
+ function LVNextSelected( IdxPrev: Integer ): Integer;
+ {* |<#listview>
+ Returns an index of next (after IdxPrev) selected item in a list view. }
+
+ function LVAdd( const aText: KOLString; ImgIdx: Integer; State: TListViewItemState;
+ StateImgIdx, OverlayImgIdx: Integer; Data: DWORD ): Integer;
+ {* |<#listview>
+ Adds new line to the end of ListView control. Only content of item itself
+ is set (aText, ImgIdx). To change other column text and attributes of
+ item added, use appropriate properties / methods ().
+ |<br>
+ Returns an index of added item.
+ |<br>
+ There is no Unicode version defined, use LVItemAddW instead. }
+ function LVItemAdd( const aText: KOLString ): Integer;
+ {* |<#listview>
+ Adds an item to the end of list view. Returns an index of the item added. }
+ function LVInsert( Idx: Integer; const aText: KOLString; ImgIdx: Integer;
+ State: TListViewItemState; StateImgIdx, OverlayImgIdx: Integer; Data: DWORD ): Integer;
+ {* |<#listview>
+ Inserts new line before line with index Idx in ListView control. Only
+ content of item itself is set (aText, ImgIdx). To change other column
+ text and attributes of item added, use appropriate properties / methods ().
+ if ImgIdx = I_IMAGECALLBACK, event handler OnGetLVItemImgIdx is responsible
+ for returning image index for an item ( /// not implemented yet /// )
+ Pass StateImgIdx and OverlayImgIdx = 0 (ignored in that case) or 1..15 to
+ use correspondent icon from ImageListState image list.
+ |<br> Returns an index of item inserted.
+ |<br> There is no unicode version of this method, use LVItemInsertW. }
+ function LVItemInsert( Idx: Integer; const aText: KOLString ): Integer;
+ {* |<#listview>
+ Inserts an item to Idx position. }
+
+ procedure LVDelete( Idx: Integer );
+ {* |<#listview>
+ Deletes item of ListView with subitems (full row - in lvsDetail view style. }
+ procedure LVSetItem( Idx, Col: Integer; const aText: KOLString; ImgIdx: Integer;
+ State: TListViewItemState; StateImgIdx, OverlayImgIdx: Integer; Data: DWORD );
+ {* |<#listview>
+ Use this method to set item data and item columns data for ListView control.
+ It is possible to pass I_SKIP as ImgIdx, StateImgIdx, OverlayImgIdx values to
+ skip setting this fields. But all other are set always. Like in LVInsert /
+ LVAdd, ImgIdx can be I_IMAGECALLBACK to determine that image will be
+ retrieved in OnGetItemImgIdx event handler when needed.
+ |<br>
+ If this method is called to set data for column > 0, parameters ImgIdx and
+ Data are ignored anyway.
+ |<br> There is no unicode version of this method, use other methods
+ to set up listed properties separately using correspondent W-functions. }
+
+ property LVItemState[ Idx: Integer ]: TListViewItemState read LVGetItemState write LVSetItemState;
+ {* |<#listview>
+ Access to list view item states set [lvisBlend, lvisHighlight, lvisFocus,
+ lvisSelect]. When assign new value to the property, it is possible to use
+ special index value -1 to change state for all items for a list view
+ (but only when lvoMultiselect style is applied to the list view, otherwise
+ index -1 is referring to the last item of the list view). }
+
+ property LVItemIndent[ Idx: Integer ]: Integer read LVGetItemIndent write LVSetItemIndent;
+ {* Item indentation. Indentation is calculated as this value multiplied to
+ image list ImgWidth value (Image list must be applied to list view).
+ Note: indentation supported only if IE3.0 or higher installed. }
+ property LVItemStateImgIdx[ Idx: Integer ]: Integer read LVGetSttImgIdx write LVSetSttImgIdx;
+ {* |<#listview>
+ Access to state image of the item. Use index -1 to assign the same state
+ image index to all items of the list view at once (fast).
+ Option lvoCheckBoxes just means, that control itself creates special inner
+ image list for two state images. Later it is possible to examine checked
+ state for items or set checked state programmatically by changing
+ LVItemStateImgIdx[ ] property. Value 1 corresponds to unchecked state,
+ 2 to checked. Value 0 allows to remove checkbox at all. So, to check all
+ added items by default (e.g.), do following:
+ ! ListView1.LVItemStateImgIdx[ -1 ] := 2;
+ |<br>Use 1-based index of the image
+ in image list ImageListState. Value 0 reserved to use as "no state image".
+ Values 1..15 can be used only - this is the Windows restriction on
+ state images. }
+ property LVItemOverlayImgIdx[ Idx: Integer ]: Integer read LVGetOvlImgIdx write LVSetOvlImgIdx;
+ {* |<#listview>
+ Access to overlay image of the item. Use index -1 to assign the same
+ overlay image to all items of the list view at once (fast). }
+ property LVItemData[ Idx: Integer ]: DWORD read LVGetItemData write LVSetItemData;
+ {* |<#listview>
+ Access to user defined data, assiciated with the item of the list view. }
+ procedure LVSelectAll;
+ {* |<#listview>
+ Call this method to select all the items of the list view control. }
+ property LVSelCount: Integer read GetSelLength; // write SetSelLength;
+ {* |<#listview>
+ Returns number of items selected in listview. }
+ property LVItemImageIndex[ Idx: Integer ]: Integer read LVGetItemImgIdx write LVSetItemImgIdx;
+ {* |<#listview>
+ Image index of items in listview. When an item is created (using LVItemAdd
+ or LVItemInsert), image index 0 is set by default (not -1 like in VCL!). }
+ property LVItems[ Idx, Col: Integer ]: KOLString read LVGetItemText write LVSetItemText;
+ {* |<#listview>
+ Access to List View item text. }
+ function LVItemRect( Idx: Integer; Part: TGetLVItemPart ): TRect;
+ {* |<#listview>
+ Returns rectangle occupied by given item part(s) in ListView window.
+ Empty rectangle is returned, if the item is not viewing currently. }
+ function LVSubItemRect( Idx, ColIdx: Integer ): TRect;
+ {* |<#listview>
+ Returns rectangle occupied by given item's subitem in ListView window,
+ in lvsDetail or lvsDetailNoHeader style. Empty rectangle (0,0,0,0) is
+ returned if the item is not viewing currently. Left or/and right bounds
+ of the rectangle returned can be outbound item rectangle if only a part
+ of the subitem is visible or the subitem is not visible in the item,
+ which is visible itself. }
+ property LVItemPos[ Idx: Integer ]: TPoint read LVGetItemPos write LVSetItemPos;
+ {* |<#listview>
+ Position of List View item (can be changed in icon or small icon view). }
+ function LVItemAtPos( X, Y: Integer ): Integer;
+ {* |<#listview>
+ Return index of item at the given position. }
+ function LVItemAtPosEx( X, Y: Integer; var Where: TWherePosLVItem ): Integer;
+ {* |<#listview>
+ Retrieves index of item and sets in Where, what part of item is under
+ given coordinates. If there are no items at the specified position,
+ -1 is returned. }
+ procedure LVMakeVisible( Item: Integer; PartiallyOK: Boolean );
+ {* |<#listview>
+ Makes listview item visible. Ignred when Item passed < 0. }
+ procedure LVEditItemLabel( Idx: Integer );
+ {* |<#listview>
+ Begins in-place editing of item label (first column text). }
+ procedure LVSort;
+ {* |<#listview>
+ Initiates sorting of list view items. This sorting procedure is available only
+ for Win2K, WinNT4 with IE5, Win98 or Win95 with IE5. See also LVSortData. }
+ procedure LVSortData;
+ {* |<#listview>
+ Initiates sorting of list view items. This sorting procedure is always available
+ in Windows95/98, NT/2000. But OnCompareLVItems procedure receives not indexes of
+ items compared but its Data field associated instead. }
+ procedure LVSortColumn( Idx: Integer );
+ {* |<#listview>
+ This is a method to simplify sort by column. Just call it in your OnColumnClick
+ event passing column index and enjoy with your list view sorted automatically
+ when column header is clicked. Requieres Windows2000 or Winows98, not supported
+ under WinNT 4.0 and below and under Windows95.
+ |<br>
+ Either lvoSortAscending or lvoSortDescending option must be set in
+ LVOptions, otherwise no sorting is performed. }
+ function LVIndexOf( const S: KOLString ): Integer;
+ {* Returns first list view item index with caption matching S.
+ The same as LVSearchFor( S, -1, FALSE ). }
+ function LVSearchFor( const S: KOLString; StartAfter: Integer; Partial: Boolean ): Integer;
+ {* Searches an item with Caption equal to S (or starting from S, if Partial = TRUE).
+ Searching is started after an item specified by StartAfter parameter. }
+
+ //======== List view page:
+ property LVTopItem: Integer index LVM_GETTOPINDEX read GetIntVal; //LVGetTopItem;
+ {* |<#listview>
+ Returns index of topmost visible item of ListView in lvsList view style. }
+ property LVPerPage: Integer index LVM_GETCOUNTPERPAGE read GetIntVal; //LVGetPerPage;
+ {* |<#listview>
+ Returns the number of fully-visible items if successful. If the current
+ view is icon or small icon view, the return value is the total number
+ of items in the list view control. }
+
+ //======== List View specific events:
+ property OnEndEditLVItem: TOnEditLVItem
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnEndEditLVItem {$ELSE} EV.fOnEndEditLVItem {$ENDIF}
+ write SetOnEndEditLVItem;
+ {* |<#listview>
+ Called when edit of an item label in ListView control finished. Return
+ True to accept new label text, or false - to not accept it (item label
+ will not be changed). If handler not set to an event, all changes are
+ accepted. }
+
+ property OnLVDelete: TOnDeleteLVItem
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnDeleteLVItem {$ELSE} EV.fOnDeleteLVItem {$ENDIF}
+ write SetOnDeleteLVItem;
+ {* |<#listview>
+ This event is called when an item is deleted in the listview.
+ Do not add, delete, or rearrange items in the list view while processing
+ this notification. }
+ property OnDeleteLVItem: TOnDeleteLVItem
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnDeleteLVItem {$ELSE} EV.fOnDeleteLVItem {$ENDIF}
+ write SetOnDeleteLVItem;
+ {* |<#listview>
+ Called for every deleted list view item. }
+ property OnDeleteAllLVItems: TOnEvent read DF.fOnDeleteAllLVItems write SetOnDeleteAllLVItems;
+ {* |<#listview>
+ Called when all the items of the list view control are to be deleted. If after
+ returning from this event handler event OnDeleteLVItem is yet assigned,
+ an event OnDeleteLVItem will be called for every deleted item. }
+ property OnLVData: TOnLVData
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnLVData {$ELSE} EV.fOnLVData {$ENDIF}
+ write SetOnLVData;
+ {* |<#listview>
+ Called to provide virtual list view with actual data. To use list view as
+ virtaul list view, define also lvsOwnerData style and set Count property
+ to actual row count of the list view. This manner of working with list view
+ control can greatly improve performance of an application when working with
+ huge data sets represented in listview control. }
+
+ property OnCompareLVItems: TOnCompareLVItems
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnCompareLVItems {$ELSE} EV.fOnCompareLVItems {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_OnCompareLVItems {$ELSE} EV.fOnCompareLVItems {$ENDIF};
+ {* |<#listview>
+ Event to compare two list view items during sort operation (initiated by
+ LVSort method call). Do not send any messages to the list view control
+ while it is sorting - results can be unpredictable! }
+ property OnColumnClick: TOnLVColumnClick
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnColumnClick {$ELSE} EV.fOnColumnClick {$ENDIF}
+ write SetOnColumnClick;
+ {* |<#listview>
+ This event handler is called when column of the list view control is clicked.
+ You can use this event to initiate sorting of list view items by this column. }
+ property OnLVStateChange: TOnLVStateChange
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnLVStateChange {$ELSE} EV.FOnLVStateChange {$ENDIF}
+ write SetOnLVStateChange;
+ {* |<#listview>
+ This event occure when an item or items range in list view control are
+ changing its state (e.g. selected or unselected). }
+ property OnDrawItem: TOnDrawItem
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnDrawItem {$ELSE} EV.fOnDrawItem {$ENDIF}
+ write SetOnDrawItem;
+ {* |<#listview>
+ |<#listbox>
+ |<#combo>
+ This event can be used to implement custom drawing for list view, list box, dropped
+ list of a combobox. For a list view, custom drawing using this event is possible
+ only in lvsDetail and lvsDetailNoHeader styles, and OnDrawItem is called to draw
+ entire row at once only. See also OnLVCustomDraw event. }
+
+ property OnLVCustomDraw: TOnLVCustomDraw
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnLVCustomDraw {$ELSE} EV.FOnLVCustomDraw {$ENDIF}
+ write SetOnLVCustomDraw;
+ {* |<#listview>
+ Custom draw event for listview. For every item to be drawn, this event
+ can be called several times during a single drawing cycle - depending on
+ a result, returned by an event handler. Stage can have one of following
+ values:
+ |<pre>
+ CDDS_PREERASE
+ CDDS_POSTERASE
+ CDDS_ITEMPREERASE
+ CDDS_PREPAINT
+ CDDS_ITEMPREPAINT
+ CDDS_ITEM
+ CDDS_SUBITEM + CDDS_ITEMPREPAINT
+ CDDS_SUBITEM + CDDS_ITEMPOSTPAINT
+ CDDS_ITEMPOSTPAINT
+ CDDS_POSTPAINT
+ </pre>
+ When called, see on Stage to get know, on what stage the event is
+ activated. And depend on the stage and on what you want to paint,
+ return a value as a result, which instructs the system, if to use
+ default drawing on this (and follows) stage(s) for the item, and if
+ to notify further about different stages of drawing the item during
+ this drawing cycle. Possible values to return are:
+ |<pre>
+ CDRF_DODEFAULT - perform default drawing. Do not notify further for this
+ item (subitem) (or for entire listview, if called with
+ flag CDDS_ITEM reset - ?);
+ CDRF_NOTIFYITEMDRAW - return this value, when the event is called the
+ first time in a cycle of drawing, with ItemIdx = -1 and
+ flag CDDS_ITEM reset in Stage parameter;
+ CDRF_NOTIFYPOSTERASE - usually can be used to provide default erasing,
+ if you want to perform drawing immediately after that;
+ CDRF_NOTIFYPOSTPAINT - return this value to provide calling the event
+ after performing default drawing. Useful when you wish
+ redraw only a part of the (sub)item;
+ CDRF_SKIPDEFAULT - return this value to inform the system that all
+ drawing is done and system should not peform any more
+ drawing for the (sub)item during this drawing cycle.
+ CDRF_NEWFONT - informs the system, that font is changed and default
+ drawing should be performed with changed font;
+ |</pre>
+ If you want to get notifications for each subitem, do not use option
+ lvoOwnerDrawFixed, because such style prevents system from notifying
+ the application for each subitem to be drawn in the listview and only
+ notifications will be sent about entire items.
+ |<br>
+ See also NM_CUSTOMDRAW in API Help.
+ }
+
+ procedure Set_LVItemHeight(Value: Integer);
+ function SetLVItemHeight(Value: Integer): PControl;
+ property LVItemHeight: Integer read DF.fLVItemHeight write Set_LVItemHeight;
+ {* |<#listview>
+ |<#listbox>
+ |#combo>
+ It is possible to assign a value to LVItemHeight property only to
+ control with "owner-draw" style (lvoOwnerDrawFixed for listview,
+ loOwnerDrawFixed or loOwnerDrawVariable for listbox and
+ coOwnerDrawFixed or coOwnerDrawVariable for combobox. At least, the
+ control should have such option while creating it (after showing it
+ the first time it is possible to change its options to avoid owner
+ drawing later). }
+
+ //======== TreeView specific properties and methods:
+ function TVInsert( nParent, nAfter: THandle; const Txt: KOLString ): THandle;
+ {* |<#treeview>
+ Inserts item to a tree view. If nParent is 0 or TVI_ROOT, the item is
+ inserted at the root of tree view. It is possible to pass following special
+ values as nAfter parameter:
+ |<pre>
+ TVI_FIRST Inserts the item at the beginning of the list.
+ TVI_LAST Inserts the item at the end of the list.
+ TVI_SORT Inserts the item into the list in alphabetical order.
+ |</pre> }
+ procedure TVDelete( Item: THandle );
+ {* |<#treeview>
+ Removes an item from the tree view. If value TVI_ROOT is passed, all items
+ are removed. }
+
+ property TVSelected: THandle index TVGN_CARET read TVGetItemIdx write TVSetItemIdx;
+ {* |<#treeview>
+ Returns or sets currently selected item handle in tree view. }
+
+ property TVDropHilighted: THandle index TVGN_DROPHILITE read TVGetItemIdx write TVSetItemIdx;
+ {* |<#treeview>
+ Returns or sets item, which is currently highlighted as a drop target. }
+ property TVDropHilited: THandle index TVGN_DROPHILITE read TVGetItemIdx write TVSetItemIdx;
+ {* The same as TVDropHilighted. }
+ property TVFirstVisible: THandle index TVGN_FIRSTVISIBLE read TVGetItemIdx write TVSetItemIdx;
+ {* |<#treeview>
+ Returns or sets given item to top of tree view. }
+
+ property TVIndent: Integer index TVM_GETINDENT read GetIntVal write SetIntVal;
+ {* |<#treeview>
+ The amount, in pixels, that child items are indented relative to their
+ parent items. }
+ property TVVisibleCount: Integer index TVM_GETVISIBLECOUNT read GetIntVal;
+ {* |<#treeview>
+ Returns number of fully (not partially) visible items in tree view. }
+
+ property TVRoot: THandle index TVGN_ROOT read TVGetItemIdx;
+ {* |<#treeview>
+ Returns handle of root item in tree view (or 0, if tree is empty). }
+ property TVItemChild[ Item: THandle ]: THandle index TVGN_CHILD read TVGetItemNext;
+ {* |<#treeview>
+ Returns first child item for given one. }
+ property TVItemHasChildren[ Item: THandle ]: Boolean read TV_GetItemHasChildren write TV_SetItemHasChildren;
+ {* |<#treeview>
+ TRUE, if an Item has children. Set this value to true if you want to
+ force [+] sign appearing left from the node, even if there are no
+ subnodes added to the node yet. }
+ property TVItemChildCount[ Item: THandle ]: Integer read TV_GetItemChildCount;
+ {* |<#treeview>
+ Returns number of node child items in tree view.
+ }
+ property TVItemNext[ Item: THandle ]: THandle index TVGN_NEXT read TVGetItemNext;
+ {* |<#treeview>
+ Returns next sibling item handle for given one (or 0, if passed item is
+ the last child for its parent node). }
+ property TVItemPrevious[ Item: THandle ]: THandle index TVGN_PREVIOUS read TVGetItemNext;
+ {* |<#treeview>
+ Returns previous sibling item (or 0, if the is no such item). }
+ property TVItemNextVisible[ Item: THandle ]: THandle index TVGN_NEXTVISIBLE read TVGetItemNext;
+ {* |<#treeview>
+ Returns next visible item (passed item must be visible too, to determine,
+ if it is really visible, use property TVItemRect or TVItemVisible. }
+ property TVItemPreviousVisible[ Item: THandle ]: THandle index TVGN_PREVIOUSVISIBLE read TVGetItemNext;
+ {* |<#treeview>
+ Returns previous visible item. }
+ property TVItemParent[ Item: THandle ]: THandle index TVGN_PARENT read TVGetItemNext;
+ {* |<#treeview>
+ Returns parent item for given one (or 0 for root item). }
+
+ property TVItemText[ Item: THandle ]: KOLString read TVGetItemText write TVSetItemText;
+ {* |<#treeview>
+ Text of tree view item. }
+ function TVItemPath( Item: THandle; Delimiter: KOLChar ): KOLString;
+ {* |<#treeview>
+ Returns full path from the root item to given item. Path is calculated
+ as a concatenation of all parent nodes text strings, separated by
+ given delimiter character.
+ |<br>Please note, that returned path has no trailing delimiter, this
+ character is only separating different parts of the path.
+ |<br>If Item is not specified ( =0 ), path is returned
+ for Selected item. }
+
+ property TVItemRect[ Item: THandle; TextOnly: Boolean ]: TRect read TVGetItemRect;
+ {* |<#treeview>
+ Returns rectangle, occupied by an item in tree view. }
+
+ property TVItemVisible[ Item: THandle ]: Boolean read TVGetItemVisible write TVSetITemVisible;
+ {* |<#treeview>
+ Returs True, if item is visible in tree view. It is also possible to
+ assign True to this property to ensure that a tree view item is visible
+ (if False is assigned, this does nothing). }
+ function TVItemAtPos( x, y: Integer; var Where: DWORD ): THandle;
+ {* |<#treeview>
+ Returns handle of item found at specified position (relative to upper left
+ corener of client area of the tree view). If no item found, 0 is returned.
+ Variable Where receives additional flags combination, describing more
+ detailed, on which part of item or tree view given point is located,
+ such as:
+ |<pre>
+ TVHT_ABOVE Above the client area
+ TVHT_BELOW Below the client area
+ TVHT_NOWHERE In the client area, but below the last item
+ TVHT_ONITEM On the bitmap or label associated with an item
+ TVHT_ONITEMBUTTON On the button associated with an item
+ TVHT_ONITEMICON On the bitmap associated with an item
+ TVHT_ONITEMINDENT In the indentation associated with an item
+ TVHT_ONITEMLABEL On the label (string) associated with an item
+ TVHT_ONITEMRIGHT In the area to the right of an item
+ TVHT_ONITEMSTATEICON On the state icon for a tree-view item that is in a user-defined state
+ TVHT_TOLEFT To the right of the client area
+ TVHT_TORIGHT To the left of the client area
+ |</pre> }
+
+ property TVRightClickSelect: Boolean read DF.fTVRightClickSelect write SetTVRightClickSelect;
+ {* |<#treeview>
+ Set this property to True to allow change selection to an item, clicked with right mouse button. }
+ property TVEditing: Boolean read GetTVEditing;
+ {* |<#treeview>
+ Returns True, if tree view control is editing its item label. }
+ property TVItemBold[ Item: THandle ]: Boolean index TVIS_BOLD read TVGetItemStateFlg write TVSetItemStateFlg;
+ {* |<#treeview>
+ True, if item is bold. }
+ property TVItemCut[ Item: THandle ]: Boolean index TVIS_CUT read TVGetITemStateFlg write TVSetItemStateFlg;
+ {* |<#treeview>
+ True, if item is selected as part of "cut and paste" operation. }
+ property TVItemDropHighlighted[ Item: THandle ]: Boolean index TVIS_DROPHILITED read TVGetITemStateFlg write TVSetItemStateFlg;
+ {* |<#treeview>
+ True, if item is selected as drop target. }
+ property TVItemDropHilited[ Item: THandle ]: Boolean index TVIS_DROPHILITED read TVGetITemStateFlg write TVSetItemStateFlg;
+ {* The same as TVItemDropHighlighted. }
+ property TVItemExpanded[ Item: THandle ]: Boolean index TVIS_EXPANDED read TVGetITemStateFlg; // write TVSetItemStateFlg;
+ {* |<#treeview>
+ True, if item's list of child items is currently expanded. To change
+ expanded state, use method TVExpand. }
+ property TVItemExpandedOnce[ Item: THandle ]: Boolean index TVIS_EXPANDEDONCE read TVGetITemStateFlg; // write TVSetItemStateFlg;
+ {* |<#treeview>
+ True, if item's list of child items has been expanded at least once. }
+ property TVItemSelected[ Item: THandle ]: Boolean index TVIS_SELECTED read TVGetITemStateFlg write TVSetItemStateFlg;
+ {* |<#treeview>
+ True, if item is selected. }
+
+ procedure TVExpand( Item: THandle; Flags: DWORD );
+ {* |<#treeview>
+ Call it to expand/collapse item's child nodes. Possible values for Flags
+ parameter are:
+ <pre>
+ TVE_COLLAPSE Collapses the list.
+ TVE_COLLAPSERESET Collapses the list and removes the child items. Note
+ that TVE_COLLAPSE must also be specified.
+ TVE_EXPAND Expands the list.
+ TVE_TOGGLE Collapses the list if it is currently expanded or
+ expands it if it is currently collapsed.
+ </pre>
+ }
+ procedure TVSort( N: THandle );
+ {* |<#treeview>
+ By Alex Mokrov. Sorts treeview. If N = 0, entire treeview is sorted.
+ Otherwise, children of the given node only.
+ }
+
+ property TVItemImage[ Item: THandle ]: Integer index TVIF_IMAGE read TVGetItemImage write TVSetItemImage;
+ {* |<#treeview>
+ Image index for an item of tree view. To tell that there are no image
+ set, use index -2 (value -1 is reserved for callback image). }
+ property TVItemSelImg[ Item: THandle ]: Integer index TVIF_SELECTEDIMAGE read TVGetItemImage write TVSetItemImage;
+ {* |<#treeview>
+ Image index for an item of tree view in selected state. Use value -2 to
+ provide no image, -1 used for callback image. }
+ property TVItemOverlay[ Item: THandle ]: Integer index TVIS_OVERLAYMASK or $80000
+ read TVGetItemImage write TVSetItemImage;
+ {* |<#treeview>
+ Overlay image index for an item in tree view.
+ Values 1..15 can be used only - this is the Windows restriction on
+ overlay images. }
+ property TVItemStateImg[ Item: THandle ]: Integer index TVIS_STATEIMAGEMASK or $C0000
+ read TVGetItemImage write TVSetItemImage;
+ {* |<#treeview>
+ State image index for an item in tree view. Use 1-based index of the image
+ in image list ImageListState. Value 0 reserved to use as "no state image".
+ }
+
+ property TVItemData[ Item: THandle ]: Pointer read TVGetItemData write TVSetItemData;
+ {* |<#treeview>
+ Stores any program-defined pointer with the item. }
+ procedure TVEditItem( Item: THandle );
+ {* |<#treeview>
+ Begins editing given item label in tree view. }
+ procedure TVStopEdit( Cancel: Boolean );
+ {* |<#treeview>
+ Ends editing item label, started by user or explicitly by TVEditItem method. }
+
+ property OnTVBeginDrag: TOnTVBeginDrag
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnTVBeginDrag {$ELSE} EV.fOnTVBeginDrag {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_OnTVBeginDrag {$ELSE} EV.fOnTVBeginDrag {$ENDIF};
+ {* |<#treeview>
+ Is called for tree view, when its item is to be dragging. }
+ property OnTVBeginEdit: TOnTVBeginEdit
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnTVBeginEdit {$ELSE} EV.fOnTVBeginEdit {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_OnTVBeginEdit {$ELSE} EV.fOnTVBeginEdit {$ENDIF};
+ {* |<#treeview>
+ Is called for tree view, when its item label is to be editing.
+ Return TRUE to allow editing of the item. }
+ property OnTVEndEdit: TOnTVEndEdit
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnTVEndEdit {$ELSE} EV.fOnTVEndEdit {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_OnTVEndEdit {$ELSE} EV.fOnTVEndEdit {$ENDIF};
+ {* |<#treeview>
+ Is called when item label is edited. It is possible to cancel
+ edit, returning False as a result. }
+ property OnTVExpanding: TOnTVExpanding
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnTVExpanding {$ELSE} EV.fOnTVExpanding {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_OnTVExpanding {$ELSE} EV.fOnTVExpanding {$ENDIF};
+ {* |<#treeview>
+ Is called just before expanding/collapsing item. It is possible to
+ return TRUE to prevent expanding item, otherwise FALSE should be returned. }
+ property OnTVExpanded: TOnTVExpanded
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnTVExpanded {$ELSE} EV.fOnTVExpanded {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_OnTVExpanded {$ELSE} EV.fOnTVExpanded {$ENDIF};
+ {* |<#treeview>
+ Is called after expanding/collapsing item children. }
+ property OnTVDelete: TOnTVDelete
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnTVDelete {$ELSE} EV.fOnTVDelete {$ENDIF}
+ write SetOnTVDelete;
+ {* |<#treeview>
+ Is called just before deleting item. You may use this event to free
+ resources, associated with an item (see TVItemData property). }
+ //----------------- by Sergey Shisminzev:
+ property OnTVSelChanging: TOnTVSelChanging
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnTVSelChanging {$ELSE} EV.fOnTVSelChanging {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_OnTVSelChanging {$ELSE} EV.fOnTVSelChanging {$ENDIF};
+ {* |<#treeview>
+ Is called before changing the selection. The handler can return FALSE
+ to prevent changing the selection. }
+ //--------------------------------------
+
+ //======== Toolbar specific methods:
+ procedure TBAddBitmap( Bitmap: HBitmap );
+ {* |<#toolbar>
+ Adds bitmaps to a toolbar. You can pass special values as Bitmap to
+ add one of predefined system button images bitmaps:
+ |<br> THandle(-1) to add standard small icons,
+ |<br> THandle(-2) to add standard large icons,
+ |<br> THandle(-5) to add standard small view icons,
+ |<br> THandle(-6) to add standard large view icons,
+ |<br> THandle(-9) to add standard small history icons,
+ |<br> THandle(-10) to add standard large history icons,
+ (in that case use following values as indexes to the standard and view
+ bitmaps:
+ |<br>
+ STD_COPY, STD_CUT, STD_DELETE, STD_FILENEW, STD_FILEOPEN, STD_FILESAVE,
+ STD_FIND, STD_HELP, STD_PASTE, STD_PRINT, STD_PRINTPRE, STD_PROPERTIES,
+ STD_REDO, STD_REPLACE, STD_UNDO,
+ |<br>
+ VIEW_LARGEICONS, VIEW_SMALLICONS,
+ VIEW_LIST, VIEW_DETAILS, VIEW_SORTNAME, VIEW_SORTSIZE, VIEW_SORTDATE,
+ VIEW_SORTTYPE (use it as parameters BtnImgIdxArray in TBAddButtons or
+ TBInsertButtons methods, and in assigning value to TBButtonImage[ ]
+ property).
+ Added bitmaps have indeces starting from previous count of images
+ (as these are appended to existing - if any).
+ |<br>
+ Note, that if You add your own (custom) bitmap, it is not transparent.
+ Do not assume that clSilver is always equal to clBtnFace. Use API
+ function CreateMappedBitmap to load bitmap from resource and map
+ desired colors as you wish (e.g., convert clTeal to clBtnFace). Or,
+ call defined in KOL function LoadMappedBitmap to do the same more easy.
+ Unfortunately, resource identifier for bitmap to pass it to LoadMappedBitmap
+ or to CreateMappedBitmap seems must be integer, so it is necessary to
+ create rc-file manually and compile using Borland Resource Compiler to
+ figure it out. }
+
+ function TBAddButtons( const Buttons: array of PKOLChar;
+ const BtnImgIdxArray: array of Integer ): Integer;
+ {* |<#toolbar>
+ Adds buttons to toolbar. Last string in Buttons array *must* be empty
+ ('' or nil), so to add buttons without text, pass ' ' string (one space
+ char). It is not necessary to provide image indexes for all
+ buttons (it is sufficient to assign index for first button only).
+ But in place, correspondent to separator button (defined by string '-'),
+ any integer must be passed to assign follow image indexes correctly.
+ See example.
+ |*Toolbar adding buttons sample.
+ Code below shows how to call TBAddButtons method to add two buttons with
+ a separator between these buttons. idxNew and idxOld are integer
+ expressions assigning image indexes to buttons 'New' and 'Old'. This
+ indexes are zero-based and refer to bitmap images, added earlier (either
+ in creating toolbar by call of NewToolbar or later in call of TBAddBitmap).
+ !
+ ! TBAddButtons( [ '&New', '-', '&Old', '' ], [ idxNew, 0, idxOld ] );
+ !
+ |*
+ To add check buttons, use prefix '+' or '-' in button definition
+ string. If next character is '!', such buttons are grouped to a
+ radio-group. Also, it is possible to use '^' prefix (must be first) to
+ define button with small drop-down section (use also OnTBDropDown event
+ to respond to clicking drop down section of such buttons).
+ |<br>
+ This function returns command id for first added button (other
+ id's can be calculated incrementing the result by one for each
+ button, except separators, which have no command id).
+ |<br>
+ Note: for static toolbar (single in application and created
+ once) ids are started from value 100. }
+
+ function TBInsertButtons( BeforeIdx: Integer; Buttons: array of PKOLChar;
+ const BtnImgIdxArray: array of Integer ): Integer;
+ {* |<#toolbar>
+ Inserts buttons before button with given index on toolbar. Returns
+ command identifier for first button inserted (other can be calculated
+ incrementing returned value needed times. See also TBAddButtons. }
+
+ procedure TBDeleteButton( BtnID: Integer );
+ {* |<#toolbar>
+ Deletes single button given by its command id. To delete separator,
+ use TBDeleteBtnByIdx instead. }
+
+ procedure TBDeleteBtnByIdx( Idx: Integer );
+ {* |<#toolbar>
+ Deletes single button given by its index in toolbar (not by command ID). }
+
+ procedure TBClear;
+ {* |<#toolbar>
+ Deletes all buttons. Dufa }
+
+ procedure TBAssignEvents( BtnID: Integer; Events: array of TOnToolbarButtonClick );
+ {* |<#toolbar>
+ Allows to assign separate OnClick events for every toolbar button.
+ BtnID should be toolbar button ID or index of the first button to
+ assign event. If it is an ID, events are assigned to buttons in
+ creation order. Otherwise, events are assigned in placement order.
+ Anyway, separator buttons are not skipped, so pass at least nil for such
+ button as an event.
+ |<br>
+ Please note, that though not all buttons should exist before
+ assigning events to it, therefore at least the first button
+ (specified by BtnID) must be already added before calling TBAssignEvents. }
+
+ procedure TBResetImgIdx( BtnID, BtnCount: Integer );
+ {* |<#toolbar>
+ Resets image index for BtnCount buttons starting from BtnID. }
+
+ //property CurItem: Integer read DF.fTBCurItem;
+ {* |<#toolbar>
+ For toolbar, in OnClick event this property can be used to determine
+ which button was clicked (100-based button id in toolbar). It is also
+ possible to use CurIndex property (zero-based) for this purpose as
+ well, but do not assume, that CurItem always equal to CurIndex+100.
+ At least, it is possible to call TBItem2Index function to convert
+ button ID to its index in toolbar.
+ }
+ property TBCurItem: Integer read DF.fTBCurItem;
+ {* |<#toolbar>
+ Same as CurItem. }
+
+ property TBButtonCount: Integer read GetItemsCount; //TBGetButtonCount;
+ {* |<#toolbar>
+ Returns count of buttons on toolbar. The same as Count. }
+
+ property TBBtnImgWidth: Integer read DF.fTBBtnImgWidth write DF.fTBBtnImgWidth;
+ {* |<#toolbar>
+ Custom toolbar buttons width. Set it before assigning buttons bitmap.
+ Changing this property after assigning the bitmap has no effect. }
+
+ function TBItem2Index( BtnID: Integer ): Integer;
+ {* |<#toolbar>
+ Converts button command id to button index for tool bar. }
+
+ function TBIndex2Item( Idx: Integer ): Integer;
+ {* |<#toolbar>
+ Converts toolbar button index to its command ID. }
+
+ procedure TBConvertIdxArray2ID( const IdxVars: array of PDWORD );
+ {* |<#toolbar>
+ Converts toolbar button indexes to its command IDs for an array
+ of indexes (each item in the array passed is a pointer to
+ Integer, containing button index when the procedure is callled,
+ then all these indexes are relaced with a correspondent button ID).}
+
+ property TBButtonEnabled[ BtnID: Integer ]: Boolean index TB_ENABLEBUTTON
+ read TBGetBtnStt write TBSetBtnStt;
+ {* |<#toolbar>
+ Obvious. }
+
+ property TBButtonVisible[ BtnID: Integer ]: Boolean read TBGetButtonVisible
+ write TBSetButtonVisible;
+ {* |<#toolbar>
+ Allows to hide/show some of toolbar buttons. }
+
+ property TBButtonChecked[ BtnID: Integer ]: Boolean index TB_CHECKBUTTON
+ read TBGetBtnStt write TBSetBtnStt;
+ {* |<#toolbar>
+ Allows to determine 'checked' state of a button (e.g., radio-button),
+ and to check it programmatically. }
+
+ property TBButtonMarked[ BtnID: Integer ]: Boolean index TB_MARKBUTTON
+ read TBGetBtnStt write TBSetBtnStt;
+ {* |<#toolbar>
+ Returns True if toolbar button is marked (highlighted). Allows to
+ highlight buttons assigning True to this value. }
+
+ property TBButtonPressed[ BtnID: Integer ]: Boolean index TB_PRESSBUTTON
+ read TBGetBtnStt write TBSetBtnStt;
+ {* |<#toolbar>
+ Allows to detrmine if toolbar button (given by its command ID) pressed,
+ and press/unpress it programmatically. }
+
+ property TBButtonText[ BtnID: Integer ]: KOLString read TBGetButtonText write TBSetButtonText;
+ {* |<#toolbar>
+ Obtains toolbar button text and allows to change it. Be sure that text
+ is not empty for all buttons, if You want for it to be shown (if at least
+ one button has empty text, no text labels will be shown at all). At
+ least set it to ' ' for buttons, which You do not want to show labels,
+ if You want from other ones to have it. }
+
+ property TBButtonImage[ BtnID: Integer ]: Integer read TBGetBtnImgIdx write TBSetBtnImgIdx;
+ {* |<#toolbar>
+ Allows to access/change button image. Do not read this property for
+ separator buttons, returning value is not proper. If you do not know,
+ is the button a separator, using function below. }
+
+ function TBButtonSeparator( BtnID: Integer ): Boolean;
+ {* |<#toolbar>
+ Returns TRUE, if a toolbar button is separator. }
+
+ property TBButtonRect[ BtnID: Integer ]: TRect read TBGetButtonRect;
+ {* |<#toolbar>
+ Obtains rectangle occupied by toolbar button in toolbar window.
+ (It is not possible to obtain rectangle for buttons, currently
+ not visible). See also function ToolbarButtonRect. }
+
+ property TBButtonWidth[ BtnID: Integer ]: Integer read TBGetBtnWidth write TBSetBtnWidth;
+ {* |<#toolbar>
+ Allows to obtain / change toolbar button width. }
+
+ property TBButtonLParam[const Idx: Integer]: DWORD read TBGetButtonLParam write TBSetButtonLParam;
+ {* |<#toolbar>
+ Allows to access/change LParam. Dufa }
+
+ property TBButtonsMinWidth: Integer index 0
+ {$IFDEF F_P} read TBGetBtMinMaxWidth
+ {$ELSE DELPHI} read DF.fTBBtMinWidth
+ {$ENDIF F_P/DELPHI} write TBSetBtMinMaxWidth;
+ {* |<#toolbar>
+ Allows to set minimal width for all toolbar buttons. }
+ property TBButtonsMaxWidth: Integer index 1
+ {$IFDEF F_P} read TBGetBtMinMaxWidth
+ {$ELSE DELPHI} read DF.fTBBtMaxWidth
+ {$ENDIF F_P/DELPHI} write TBSetBtMinMaxWidth;
+ {* |<#toolbar>
+ Allows to set maximal width for all toolbar buttons. }
+
+ function TBButtonAtPos( X, Y: Integer ): Integer;
+ {* |<#toolbar>
+ Returns command ID of button at the given position on toolbar,
+ or -1, if there are no button at the position. Value 0 is returned
+ for separators. }
+
+ function TBBtnIdxAtPos( X, Y: Integer ): Integer;
+ {* |<#toolbar>
+ Returns index of button at the given position on toolbar.
+ This also can be index of separator button. -1 is returned if
+ there are no buttons found at the position. }
+
+ function TBBtnEvent( Idx: Integer ): TOnToolbarButtonClick;
+ {* Returns toolbar event handler assigned to a toolbar button
+ (by its index). }
+
+ function TBMoveBtn( FromIdx, ToIdx: Integer ): Boolean;
+ {* |<#toolbar>
+ By TR"]F. Moves button from one position to another. }
+
+ property TBRows: Integer read TBGetRows write TBSetRows;
+ {* |<#toolbar>
+ Returns number of rows for toolbar and allows to try to set
+ desired number of rows (but system can set another number of
+ rows in some cases). This property has no effect if tboWrapable
+ style not present in Options when toolbar is created. }
+
+ procedure TBSetTooltips( BtnID1st: Integer; const Tooltips: array of PKOLChar );
+ {* |<#toolbar>
+ Allows to assign tooltips to several buttons. Until this procedure
+ is not called, tooltips list is not created and no code is added
+ to executable. This method of tooltips maintainance for toolbar buttons
+ is useful both for static and dynamic toolbars (meaning "dynamic" -
+ toolbars with buttons, deleted and inserted at run-time). }
+
+ function TBBtnTooltip( BtnID: Integer ): KOLString;
+ {* |<#toolbar> Returns tooltip assigned to a toolbar button. }
+
+ property TBAutoSizeButtons: Boolean read GetTBAutoSizeButtons write SetTBAutoSizeButtons;
+
+ property OnTBDropDown: TOnEvent index idx_FOnDropDown
+ read {$IFDEF EVENTS_DYNAMIC} Get_TOnEvent {$ELSE} EV.fOnDropDown {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_TOnEvent {$ELSE} EV.fOnDropDown {$ENDIF};
+ {* |<#toolbar>
+ This event is called for drop down buttons, when user click drop part
+ of drop down button. To determine for which button event is called,
+ look at CurItem or CurIndex property. It is also possible to use
+ common (with combobox) property OnDropDown. }
+
+ property OnTBClick: TOnEvent
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnClick {$ELSE} EV.fOnClick {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} SetOnClick {$ELSE} EV.fOnClick{$ENDIF};
+ {* |<#toolbar>
+ The same as OnClick. }
+
+ property OnTBCustomDraw: TOnTBCustomDraw read DF.fOnTBCustomDraw write SetOnTBCustomDraw;
+ {* |<#toolbar>
+ An event (mainly) to customize toolbar background. }
+ //----------------------------------------------------------------------
+ // DateTimePicker
+ property OnDTPUserString: TDTParseInputEvent
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnDTPUserString {$ELSE} EV.FOnDTPUserString {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_OnDTPUserString {$ELSE} EV.FOnDTPUserString{$ENDIF};
+ {* Special event to parse input from the application. Option dtpoParseInput
+ must be set when control is created. }
+ property DateTime: TDateTime read GetDateTime write SetDateTime;
+ {* DateTime for DateTimePicker control only. }
+ property Date: TDateTime read GetDate write SetDate;
+ {* Date only for DateTimePicker control only. }
+ property Time: TDateTime read GetTime write SetTime;
+ {* Time only for DateTimePicker control only. }
+ property SystemTime: TSystemTime read Get_SystemTime write Set_SystemTime;
+ {* Date and Time as TSystemTime. When assing, use year 0 to set "no value". }
+ property DateTimeRange: TDateTimeRange read GetDateTimeRange
+ write SetDateTimeRange;
+ {* DateTimePicker range. If first date in the agrument assigned is NAN,
+ minimum system allowed value is used as the left bound, and if the second is
+ NAN, maximum system allowed is used as the right one. }
+ property DateTimePickerColors[ Index: TDateTimePickerColor ]: TColor
+ read GetDateTimePickerColor write SetDateTimePickerColor;
+ property DateTimeFormat: KOLString write SetDateTimeFormat;
+
+ //----------------------------------------------------------------------
+
+ //----------------------------------------------------------------------
+ // ScrollBar
+ property SBMin: Longint read DF.fSBMinMax.X write SetSBMin;
+ {* Minimum scrolling area position. }
+ property SBMax: Longint read DF.fSBMinMax.Y write SetSBMax;
+ {* Maximum scrolling area position (size of the text or image to be scrolling).
+ For case when SCROLL_OLD defined, this value should be set as scrolling
+ object size without SBPageSize. }
+ property SBMinMax: TPoint read DF.fSBMinMax write SetSBMinMax;
+ {* The property to adjust SBMin and SBMax for a single call (set X to a minimum
+ and Y to a maximum value). }
+ property SBPosition: Integer read DF.fSBPosition write SetSBPosition;
+ {* Current scroll position. When set, should be between SBMin and
+ SBMax - max(0, SBPageSize-1) }
+ property SBPageSize: Integer read DF.fSBPageSize write SetSBPageSize;
+ {* }
+
+ property OnSBBeforeScroll: TOnSBBeforeScroll
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnSBBeforeScroll {$ELSE} EV.FOnSBBeforeScroll {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_OnSBBeforeScroll {$ELSE} EV.FOnSBBeforeScroll {$ENDIF};
+ {* }
+ property OnSBScroll: TOnSBScroll
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnSBScroll {$ELSE} EV.FOnSBScroll {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_OnSBScroll {$ELSE} EV.FOnSBScroll {$ENDIF};
+ {* }
+
+ function SBSetScrollInfo(const SI: TScrollInfo): Integer;
+ function SBGetScrollInfo(var SI: TScrollInfo): Boolean;
+ function GetSBMinMax: TPoint;
+ function GetSBPageSize: Integer;
+ function GetSBPosition: Integer;
+ //----------------------------------------------------------------------
+
+ // "Through", or "transparent" methods to simplify initial
+ // adjustment of controls and make non-visual designing of
+ // forms more easy. All these functions return @Self as a
+ // result, so, it is possible to use such methods immediately
+ // in constructing statement, concatenating it with dots, e.g.:
+ //
+ // NewButton( MyForm, 'Click here' ).PlaceUnder.ResizeParentBottom;
+ //
+ {$ENDIF GDI}
+ function PlaceRight: PControl;
+ {* Places control right (to previously created on the same parent). }
+ function PlaceDown: PControl;
+ {* Places control below (to previously created on the same parent).
+ Left position is not changed (thus is, kept equal to Parent.Margin). }
+ function PlaceUnder: PControl;
+ {* Places control below (to previously created one, aligning its
+ Left position to Left position of previous control). }
+ function SetSize( W, H: Integer ): PControl;
+ {* Changes size of a control. If W or H less or equal to 0,
+ correspondent size is not changed. }
+ {$IFDEF GDI}
+ function Size( W, H: Integer ): PControl;
+ {* Like SetSize, but provides automatic resizing of parent control
+ (recursively). Especially useful for aligned controls. }
+ function SetClientSize( W, H: Integer ): PControl;
+ {* Like SetSize, but works setting W = ClientWidth, H = ClientHeight.
+ Use this method for forms, which can not be resized (dialogs). }
+
+ {$ENDIF GDI}
+ function AutoSize( AutoSzOn: Boolean ): PControl;
+ {$IFDEF GDI}
+ function MakeWordWrap: PControl;
+
+ {* Determines if to autosize control (like label, button, etc.) }
+ function IsAutoSize: Boolean;
+ {* TRUE, if a control is autosizing. }
+ function AlignLeft( P: PControl ): PControl;
+ {* assigns Left := P.Left }
+ function AlignTop( P: PControl ): PControl;
+ {* assigns Top := P.Top }
+ function ResizeParent: PControl;
+ {* Resizes parent, calling ResizeParentRight and ResizeParentBottom. }
+ function ResizeParentRight: PControl;
+ {* Resizes parent right edge (Margin of parent is added to right
+ coordinate of a control). If called second time (for the same
+ parent), resizes only for increasing of right edge of parent. }
+
+ function ResizeParentBottom: PControl;
+ {* Resizes parent bottom edge (Margin of parent is added to
+ bottom coordinate of a control). }
+ function CenterOnParent: PControl;
+ {* Centers control on parent, or if applied to a form, centers
+ form on screen. }
+ function CenterOnForm( Form1: PControl ): PControl;
+ {* Centers form on another form. If Form1 not present, centers on screen. }
+
+ function Shift( dX, dY : Integer ): PControl;
+ {* Moves control respectively to current position (Left := Left + dX,
+ Top := Top + dY). }
+ {$ENDIF GDI}
+ function SetPosition( X, Y: Integer ): PControl;
+ {* Moves control directly to the specified position. }
+ {$IFDEF GDI}
+
+ function Tabulate: PControl;
+ {* Call it once for form/applet to provide tabulation between controls on
+ form/on all forms using TAB / SHIFT+TAB and arrow keys. }
+ function TabulateEx: PControl;
+ {* Call it once for form/applet to provide tabulation between controls on
+ form/on all forms using TAB / SHIFT+TAB and arrow keys. Arrow keys are
+ used more smart, allowing go to nearest control in certain direction. }
+
+ function SetAlign( AAlign: TControlAlign ): PControl;
+ {* Assigns passed value to property Align, aligning control on parent,
+ and returns @Self (so it is "transparent" function, which can be
+ used to adjust control at the creation, e.g.:
+ ! MyLabel := NewLabel( MyForm, 'Label1' ).SetAlign( caBottom );
+ See also property Align. }
+ //{-2.95}//function PreventResizeFlicks: PControl;
+ { *
+ If called, prevents resizing flicks for child controls, aligned to
+ right and bottom (but with a lot of code added to executable - about 3,5K).
+ There is sensible to set DoubleBuffered to True also to eliminate the
+ most of flicks.
+ |<br>&nbsp;&nbsp;&nbsp;
+ This method been applied to a form, prevents, resizing flicks for
+ form and all controls on the form. If it is called for applet window,
+ all forms are affected. And if You want, You can apply it for certain
+ control only - in such case only given control and its children will
+ be resizing without flicks (e.g., using splitter control). } //{-2.95}
+
+ property Checked: Boolean read GetChecked write Set_Checked;
+ {* |<#checkbox>
+ |<#radiobox>
+ |<#bitbtn>
+ For checkbox and radiobox - if it is checked. Do not assign
+ value for radiobox - use SetRadioChecked instead. }
+ function SetChecked(const Value: Boolean): PControl;
+ {* |<#checkbox>
+ Use it to check/uncheck check box control or push button.
+ Do not apply it to check radio buttons - use SetRadioChecked
+ method below. }
+ function SetRadioChecked : PControl;
+ {* |<#radiobox>
+ Use it to check radio button item correctly (unchecking all
+ alternative ones). Actually, method Click is called, and control
+ itself is returned. }
+ property Check3: TTriStateCheck read GetCheck3 write SetCheck3;
+ {* |<#checkbox>
+ State of checkbox with BS_AUTO3STATE style. }
+ procedure Click;
+ {* |<#button>
+ |<#checkbox>
+ |<#radiobox>
+ Emulates click on control programmatically, sending WM_COMMAND
+ message with BN_CLICKED code. This method is sensible only for
+ buttons, checkboxes and radioboxes. }
+
+ function Perform( msgcode: DWORD; wParam, lParam: Integer): Integer; stdcall;
+ {* Sends message to control's window (created if needed). }
+ function Postmsg( msgcode: DWORD; wParam, lParam: Integer): Boolean; stdcall;
+ {* Sends message to control's window (created if needed). }
+ procedure AttachProc( Proc: TWindowFunc );
+ {* It is possible to attach dynamically any message handler to window
+ procedure using this method. Last attached procedure is called first.
+ If procedure returns True, further processing of a message is stopped.
+ Attached procedure can be detached using DetachProc (but do not
+ attach/detach procedures during handling of attached procedure -
+ this can hang application). }
+ procedure AttachProcEx( Proc: TWindowFunc; ExecuteAfterAppletTerminated: Boolean );
+ {* The same as AttachProc, but a handler is executed even after terminating
+ the main message loop processing (i.e. after assigning true to
+ AppletTerminated global variable. }
+ function IsProcAttached( Proc: TWindowFunc ): Boolean;
+ {* Returns True, if given procedure is already in chain of attached
+ ones for given control window proc. }
+ procedure DetachProc( Proc: TWindowFunc );
+ {* Detaches procedure attached earlier using AttachProc. }
+
+ property OnDropFiles: TOnDropFiles
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnDropFiles {$ELSE} EV.FOnDropFiles {$ENDIF}
+ write SetOnDropFiles;
+ {* Assign this event to your handler, if You want to accept drag and drop
+ files from other applications such as explorer onto your control. When
+ this event is assigned to a control or form, this has effect also for
+ all its child controls too. }
+
+ property CustomData: Pointer read fCustomData write fCustomData;
+ {* Can be used to exend the object when new type of control added. Memory,
+ pointed by this pointer, released automatically in the destructor. }
+ property CustomObj: PObj read fCustomObj write fCustomObj;
+ {* Can be used to exend the object when new type of control added. Object,
+ pointed by this pointer, released automatically in the destructor. }
+ procedure SetAutoPopupMenu( PopupMenu: PObj );
+ {* To assign a popup menu to the control, call SetAutoPopupMenu method of
+ the control with popup menu object as a parameter. }
+
+ function SupportMnemonics: PControl;
+ {* This method provides supporting mnemonic keys in menus, buttons, checkboxes,
+ toolbar buttons. }
+ property OnScroll: TOnScroll
+ read {$IFDEF EVENTS_DYNAMIC} Get_OnScroll {$ELSE} EV.FOnScroll {$ENDIF}
+ write SetOnScroll;
+ {* }
+ public
+ {$IFDEF USE_DROPDOWNCOUNT}
+ property DropDownCount: Cardinal read DF.fDropDownCount write DF.fDropDownCount;
+ {$ENDIF}
+ protected
+ {$IFDEF USE_GRAPHCTLS}
+ {} fKeyboardProcess: TOnMessage; // for graphic controls ???
+ {} fSetFocus: procedure(Ctl: PControl);
+ {} fPushedBtn: PControl;
+ {} fSaveCursor: HCursor;
+ function DoGraphCtlPrepaint: TRect;
+ procedure GraphicLabelPaint( DC: HDC );
+ procedure GraphicCheckBoxPaint( DC: HDC );
+ procedure GraphicCheckBoxMouse( var Msg: TMsg );
+ procedure GraphicRadioBoxPaint( DC: HDC );
+ procedure GraphicButtonPaint( DC: HDC );
+ procedure GraphicButtonMouse( var Msg: TMsg );
+ function GraphButtonKeyboardProcess( var Msg: TMsg; var Rslt: Integer ): Boolean;
+ procedure LeaveGraphButton( Sender: PObj );
+ procedure GraphicEditPaint( DC: HDC );
+ procedure GraphicEditMouse( var Msg: TMsg );
+ procedure DestroyGraphEdit( Sender: PObj );
+ procedure LeaveGraphEdit( Sender: PObj );
+ procedure ChangeGraphEdit( Sender: PObj );
+ procedure GraphCtlDrawFocusRect( DC: HDC; const R: TRect );
+ {$IFDEF GRAPHCTL_HOTTRACK}
+ procedure MouseLeaveFromParentOfGraphCtl( Sender: PObj );
+ {$ENDIF GRAPHCTL_HOTTRACK}
+ procedure GroupBoxPaint( DC: HDC );
+ {$ENDIF USE_GRAPHCTLS}
+ {$IFDEF KEY_PREVIEW}
+ public
+ property KeyPreview: Boolean
+ read {$IFDEF USE_FLAGS} GetKeyPreview {$ELSE} fKeyPreview {$ENDIF}
+ write {$IFDEF USE_FLAGS} SetKeyPreview {$ELSE} fKeyPreview {$ENDIF};
+ //property KeyPreviewing: Boolean read fKeyPreviewing write fKeyPreviewing;
+ {$ENDIF KEY_PREVIEW}
+ protected
+ fOldWidth: Word;
+ fOldHeight: Word;
+ fClickDisabled: Byte;
+ fAnchors: Byte;
+ fNestedMsgHandling: SmallInt;
+ {* level of nested message handling for a control. Only when it is 0 at
+ the end of message handling and fBeginDestroying set, the control is
+ destroyed. }
+ fUpdateCount: SmallInt;
+ public
+ property AnchorLeft: Boolean index ANCHOR_LEFT read GetAnchor write SetAnchor; //+Sormart
+ property AnchorTop: Boolean index ANCHOR_TOP read GetAnchor write SetAnchor; //+Sormart
+ property AnchorRight: Boolean index ANCHOR_RIGHT read GetAnchor write SetAnchor;
+ property AnchorBottom: Boolean index ANCHOR_BOTTOM read GetAnchor write SetAnchor;
+ function Anchor( aLeft, aTop, aRight, aBottom: Boolean ): PControl;
+ public
+ {$IFDEF USE_CONSTRUCTORS}
+ //------------------------------------------------------------
+ // constructors here:
+ constructor CreateWindowed( AParent: PControl; AClassName: PKOLChar; ACtl3D: Boolean );
+ constructor CreateApplet( const ACaption: AnsiString );
+ constructor CreateForm( AParent: PControl; const ACaption: AnsiString );
+ constructor CreateControl( AParent: PControl; AClassName: PAnsiChar; AStyle: DWORD;
+ {} ACtl3D: Boolean; Actions: PCommandActions );
+ constructor CreateButton( AParent: PControl; const ACaption: AnsiString );
+ constructor CreateBitBtn( AParent: PControl; const ACaption: AnsiString;
+ {} AOptions: TBitBtnOptions; ALayout: TGlyphLayout; AGlyphBitmap: HBitmap;
+ {} AGlyphCount: Integer);
+ constructor CreateLabel( AParent: PControl; const ACaption: AnsiString );
+ constructor CreateWordWrapLabel( AParent: PControl; const ACaption: AnsiString );
+ constructor CreateLabelEffect( AParent: PControl; ACaption: AnsiString; AShadowDeep: Integer );
+ constructor CreatePaintBox( AParent: PControl );
+ constructor CreateGradientPanel( AParent: PControl; AColor1, AColor2: TColor );
+ constructor CreateGradientPanelEx( AParent: PControl; AColor1, AColor2: TColor;
+ {} AStyle: TGradientStyle; ALayout: TGradientLayout );
+ constructor CreateGroupbox( AParent: PControl; const ACaption: AnsiString );
+ constructor CreateCheckbox( AParent: PControl; const ACaption: AnsiString );
+ constructor CreateRadiobox( AParent: PControl; const ACaption: AnsiString );
+ constructor CreateEditbox( AParent: PControl; AOptions: TEditOptions );
+ constructor CreatePanel( AParent: PControl; AStyle: TEdgeStyle );
+ constructor CreateSplitter( AParent: PControl; AMinSizePrev, AMinSizeNext: Integer;
+ {} EdgeStyle: TEdgeStyle );
+ constructor CreateListbox( AParent: PControl; AOptions: TListOptions );
+ constructor CreateCombobox( AParent: PControl; AOptions: TComboOptions );
+ constructor CreateCommonControl( AParent: PControl; AClassName: PAnsiChar; AStyle: DWORD;
+ {} ACtl3D: Boolean; Actions: PCommandActions );
+ constructor CreateRichEdit( AParent: PControl; AOptions: TEditOptions );
+ constructor CreateRichEdit1( AParent: PControl; AOptions: TEditOptions );
+ constructor CreateProgressbar( AParent: PControl );
+ constructor CreateProgressbarEx( AParent: PControl; AOptions: TProgressbarOptions );
+ constructor CreateListView( AParent: PControl; AStyle: TListViewStyle; AOptions: TListViewOptions;
+ {} AImageListSmall, AImageListNormal, AImageListState: PImageList );
+ constructor CreateTreeView( AParent: PControl; AOptions: TTreeViewOptions;
+ {} AImgListNormal, AImgListState: PImageList );
+ constructor CreateTabControl( AParent: PControl; ATabs: array of String;
+ {}AOptions: TTabControlOptions; AImgList: PImageList; AImgList1stIdx: Integer );
+ constructor CreateToolbar( AParent: PControl; AAlign: TControlAlign; AOptions: TToolbarOptions;
+ {} ABitmap: HBitmap; AButtons: array of PChar;
+ {} ABtnImgIdxArray: array of Integer );
+ {$ENDIF USE_CONSTRUCTORS}
+
+ {$IFDEF USE_CUSTOMEXTENSIONS}
+ {$I CUSTOM_TCONTROL_EXTENSION.inc}
+ {$ENDIF}
+ // If an option USE_CUSTOMEXTENSIONS is enabled (at the beginning of this
+ // unit), You can freely extend TControl definition by your own fields,
+ // methods and properties. This provides You with capability to extend
+ // TControl implementing another kinds of visual controls without deriving
+ // new descendant objects from TControl. This way is provided to avoid too
+ // large grow of executable size. You also can derive your own controls
+ // from TControl using standard OOP capabilities. In such case an option
+ // USE_CONSTRUCTORS should be turned on (see it at the start of this unit).
+ // If You choose this "flat" model of extending the TControl with your
+ // own properties, fieds, methods, events, etc. You should provide three
+ // inc-files: CUSTOM_TCONTROL_EXTENSION.inc, containing such definitions
+ // for TControl, CUSTOM_KOL_EXTENSION.inc, containing needed global
+ // declarations, and CUSTOM_CODE_EXTENSION.inc, the implementation of those
+ // two.
+ // Because KOL is always grow and constantly is extending by me, I also can
+ // add my own complements for TControl. To avoid naming conflicts, I suggest
+ // to use the same naming rule for all of You. Name your fields, properies, etc.
+ // using a form idx_SomeName, where idx is a prefix, containing several
+ // (at least one) letters and digits. E.g. ZK65_OnSomething.
+
+ protected // rare used fields are moved here from top to make code smaller a bit
+ //fFocusHandle: HWnd; // to store handle of focused control of form ?
+ FParentWnd: HWnd; // <<-- ++ for InitOrthaned !!
+ fParentCoordX: SmallInt;
+ fParentCoordY: SmallInt;
+ {$IFDEF USE_MDI}
+ fMDIClient: PControl;
+ fCreateWindowProc: function(
+ lpClassName, lpWindowName: PKOLChar;
+ dwStyle: DWORD; X, Y, nWidth, nHeight: Integer;
+ hwndParent: HWnd; hInstance: HInst; lParam: Integer ): HWnd;
+ stdcall;
+ {* MDI client window control }
+ {$ENDIF}
+ //fMDIChildren: PList;
+ //{* List of MDI children. It is filled for MDI client window. }
+
+ {$IFDEF USE_fNCDestroyed}
+ {} fNCDestroyed: Boolean;
+ {$ENDIF USE_fNCDestroyed}
+ public
+ {$IFDEF USE_MDI}
+ property MDIClient: PControl read fMDIClient; //Get_MDIClient;
+ {* For MDI forms only: returns MDI client window control, containng all MDI
+ children. Use this window to send specific messages to rule MDI children. }
+ {$ENDIF}
+ {$IFDEF OBSOLETE_FIELDS}
+ {} fPaintLater: Boolean;
+ {$ENDIF OBSOLETE_FIELDS}
+ // last changes (1-Jul-06) from ECM [Michalichenko Eugeny, rest in peace, friend]:
+ //======== ListBox
+ private
+ function GetLBTopIndex: Integer;
+ procedure SetLBTopIndex(const Value: Integer);
+ public
+ function LBItemAtPos(X,Y: Integer): Integer;
+ {* |<#listbox>
+ Return index of item at the given position. }
+ property LBTopIndex: Integer read GetLBTopIndex write SetLBTopIndex;
+ {* |<#listbox>
+ Index of the first visible item in a list box}
+ public
+ //================== RichEdit specific: ==================
+ {$IFNDEF NOT_USE_RICHEDIT}
+ property MaxTextSize: DWORD read GetMaxTextSize write SetMaxTextSize;
+ {* |<#richedit>
+ This property valid also for simple edit control, not only for RichEdit.
+ But for usual edit control, maximum text size available is 32K. For
+ RichEdit, limit is 4Gb. By default, RichEdit is limited to
+ 32767 bytes (to set maximum size available to 2Gb, assign MaxInt value
+ to a property). Also, to get current text size of RichEdit, use property
+ TextSize or RE_TextSize[ ]. }
+ property TextSize: Integer read GetTextSize;
+ {* |<#richedit>
+ Common for edit and rich edit controls property, which returns size of
+ text in edit control. Also, for any other control (or form, or applet
+ window) returns size (in characters) of Caption or Text (what is, the
+ same property actually). }
+ property RE_TextSize[ Units: TRichTextSize ]: Integer read REGetTextSize;
+ {* |<#richedit>
+ For RichEdit control, it returns text size, measured in desired units
+ (rtsChars - characters, including OLE objects, counted as a single
+ character; rtsBytes - presize length of text image (if it would be stored
+ in file or stream). Please note, that for RichEdit1.0, only size in
+ characters can be obtained. }
+ function RE_TextSizePrecise: Integer;
+ {* |<#richedit>
+ By Savva. Returns length of rich edit text. }
+
+ property RE_CharFmtArea: TRichFmtArea read DF.fRECharArea write DF.fRECharArea;
+ {* |<#richedit>
+ By default, this property is raSelection. Changing it, You determine in
+ for which area characters format is applyed, when changing
+ character formatting properties below (not paragraph formatting).
+ |&A=<a href=#RE_CharFmtArea target=main>%0</a>
+ }
+ property RE_CharFormat: TCharFormat read REGetCharformat write RESetCharFormat;
+ {* |<#richedit>
+ In differ to follow properties, which allow to control certain formatting
+ attributes, this property provides low level access for formatting current
+ character area (see RE_CharFmtArea). It returns TCharFormat structure,
+ filled in with formatting attributes, and by assigning another value to
+ this property You can change desired attributes as You wish. Even if
+ RichEdit1.0 is used, TCharFormat2 is returned (but extended fields are
+ ignored for RichEdit1.0). }
+ property RE_Font: PGraphicTool read REGetFont write RESetFont;
+ {* |<#richedit>
+ Font of the first character in current selection (when retrieve).
+ When set (or subproperties of RE_Font are set), all font attributes are
+ applied to entire <A area>. To apply only needed attributes, use another
+ properties: RE_FmtBold, RE_FmtItalic, RE_FmtStrikeout, RE_FmtUnderline,
+ RE_FmtName, etc.
+ |<br>
+ Note, that font size is measured in twips, which is about 1/10 of pixel. }
+ property RE_FmtBold: Boolean index CFM_BOLD read REGetFontEffects write RESetFontEffect;
+ {* |<#richedit>
+ Formatting flag. When retrieve, returns True, if fsBold style RE_Font.FontStyle
+ is valid for a first character in the selection. When set, changes fsBold
+ style (True - set, False - reset) for all characters in <A area>. }
+ property RE_FmtBoldValid: Boolean index CFM_BOLD read REGetFontMask;
+ {* }
+ property RE_FmtItalic: Boolean index CFM_ITALIC read REGetFontEffects write RESetFontEffect;
+ {* |<#richedit>
+ Formatting flag. Like RE_FmtBold, when retrieving, shows, is fsItalic
+ style valid for the first character of the selection, and when set, changes
+ only fsItalic style for an <A area>. }
+ property RE_FmtItalicValid: Boolean index CFM_ITALIC read REGetFontMask;
+ {* }
+ property RE_FmtStrikeout: Boolean index CFM_STRIKEOUT read REGetFontEffects write RESetFontEffect;
+ {* |<#richedit>
+ Formatting flag. Like RE_FmtBold, when retrieving, shows, is fsStrikeout
+ style valid for the first selected character, and when set, changes only
+ fsStrikeout style for an <A area>. }
+ property RE_FmtStrikeoutValid: Boolean index CFM_STRIKEOUT read REGetFontMask;
+ {* }
+ property RE_FmtUnderline: Boolean index CFM_UNDERLINE read REGetFontEffects write RESetFontEffect;
+ {* |<#richedit>
+ Formatting flag. Like RE_FmtBold, when retrieving, shows, is fsUnderline
+ style valid for the first selected character, and when set, changes
+ fsUnderline style for an <A area>. }
+ property RE_FmtUnderlineValid: Boolean index CFM_UNDERLINE read REGetFontMask;
+ {* }
+ property RE_FmtUnderlineStyle: TRichUnderline
+ read REGetUnderlineEx write RESetUnderlineEx;
+ {* |<#richedit>
+ Extended underline style. To check, if this property is valid for
+ entire selection, examine RE_FmtUnderlineValid value. }
+ property RE_FmtProtected: Boolean index CFM_PROTECTED read REGetFontEffects write RESetFontEffect;
+ {* |<#richedit>
+ Formatting flag. When retrieving, shows, is the first character of the selection
+ is protected from changing it by user (True) or not (False). To get know,
+ if retrived value is valid for entire selection, check the property
+ RE_FmtProtectedValid. When set, makes all characters in <A area> protected (
+ True) or not (False). }
+ property RE_FmtProtectedValid: Boolean index CFM_PROTECTED read REGetFontMask;
+ {* |<#richedit>
+ True, if property RE_FmtProtected is valid for entire selection, when
+ retrieving it. }
+ property RE_FmtHidden: Boolean index CFM_HIDDEN read REGetFontEffects write RESetFontEffect;
+ {* |<#richedit>
+ For RichEdit3.0, makes text hidden (not displayed). }
+ property RE_FmtHiddenValid: Boolean index CFM_HIDDEN read REGetFontMask;
+ {* |<#richedit>
+ Returns True, if RE_FmtHidden style is valid for entire selection. }
+
+ property RE_FmtLink: Boolean index $20 {CFM_LINK} read REGetFontEffects write RESetFontEffect;
+ {* |<#richedit>
+ Returns True, if the first selected character is a part of link (URL). }
+ // by Sergey Shisminzev
+
+ property RE_FmtLinkValid: Boolean index $20 {CFM_LINK} read REGetFontMask;
+ {* }
+ property RE_FmtFontSize: Integer index (12 shl 16) or CFM_SIZE read REGetFontAttr write RESetFontAttr;
+ {* |<#richedit>
+ Formatting value: font size, in twips (1/1440 of an inch, or 1/20 of a
+ printer's point, or about 1/10 of pixel). When retrieving, returns
+ RE_Font.FontHeight.
+ When set, changes font size for entire <A area> (but does not change
+ other font attributes). }
+ property RE_FmtFontSizeValid: Boolean read REGetFontSizeValid;
+ {* |<#richedit>
+ Returns True, if property RE_FmtFontSize is valid for entire selection,
+ when retrieving it. }
+ property RE_FmtAutoBackColor: Boolean index CFM_BACKCOLOR read REGetFontEffects write RESetFontEffect;
+ {* |<#richedit>
+ True, when automatic back color is used. }
+ property RE_FmtAutoBackColorValid: Boolean index CFM_BACKCOLOR read REGetFontMask;
+ {* }
+ property RE_FmtFontColor: Integer index (20 shl 16) or CFM_COLOR read REGetFontAttr write RESetFontAttr1;
+ {* |<#richedit>
+ Formatting value (font color). When retrieving, returns RE_Font.Color.
+ When set, changes font color for entire <A area> (but does not change
+ other font attributes). }
+ property RE_FmtFontColorValid: Boolean index CFM_COLOR read REGetFontMask;
+ {* |<#richedit>
+ Returns True, if property RE_FmtFontColor valid for entire selection,
+ when retrieving it. }
+ property RE_FmtAutoColor: Boolean index CFM_COLOR read REGetFontEffects write RESetFontEffect;
+ {* |<#richedit>
+ True, when automatic text color is used (in such case, RE_FmtFontColor
+ assignment is ignored for current area). }
+ property RE_FmtAutoColorValid: Boolean index CFM_COLOR read REGetFontMask;
+ {* }
+ property RE_FmtBackColor: Integer index ((64
+ {$IFDEF UNICODE_CTRLS} + 32 {$ENDIF}
+ ) shl 16) or CFM_BACKCOLOR read REGetFontAttr write RESetFontAttr1;
+ {* |<#richedit>
+ Formatting value (back color). Only available for Rich Edit 2.0 and higher.
+ When set, changes background color for entire <A area> (but does not change
+ other font attributes). }
+ property RE_FmtBackColorValid: Boolean index CFM_BACKCOLOR read REGetFontMask;
+ {* }
+ property RE_FmtFontOffset: Integer index (16 shl 16) or CFM_OFFSET read REGetFontAttr write RESetFontAttr;
+ {* |<#richedit>
+ Formatting value (font vertical offset from baseline, positive values
+ correspond to subscript). When retrieving, returns offset for first
+ character in the selection. When set, changes font offset for entire
+ <A area>. To get know, is retrieved value valid for entire selction,
+ check RE_FmtFontOffsetValid property. }
+ property RE_FmtFontOffsetValid: Boolean index CFM_OFFSET read REGetFontMask;
+ {* |<#richedit>
+ Returns True, if property RE_FmtFontOffset is valid for entire selection,
+ when retrieving it. }
+ property RE_FmtFontCharset: Integer index (25 shl 16) or CFM_CHARSET read REGetFontAttr write RESetFontAttr;
+ {* |<#richedit>
+ Returns charset for first character in current selection, when retrieved
+ (and to get know, if this value is valid for entire selection, check
+ property RE_FmtFontCharsetValid). When set, changes charset for all
+ characters in <A area>, but does not alter other formatting attributes. }
+ property RE_FmtFontCharsetValid: Boolean index CFM_CHARSET read REGetFontMask;
+ {* |<#richedit>
+ Returns True, only if rerieved property RE_FmtFontCharset is valid for
+ entire selection. }
+ property RE_FmtFontName: KOLString read REGetFontName write RESetFontName;
+ {* |<#richedit>
+ Returns font face name for first character in the selection, when retrieved,
+ and sets font name for entire <A area>, wnen assigned to (without
+ changing of other formatting attributes). To get know, if retrived
+ font name valid for entire selection, examine property RE_FmtFontNameValid. }
+ property RE_FmtFontNameValid: Boolean index CFM_FACE read REGetFontMask;
+ {* |<#richedit>
+ Returns True, only if the font name is the same for entire selection,
+ thus is, if rerieved property value RE_FmtFontName is valid for entire
+ selection. }
+
+ property RE_ParaFmt: TParaFormat read REGetParaFmt write RESetParaFmt;
+ {* |<#richedit>
+ Allows to retrieve or set paragraph formatting attributes for currently
+ selected paragraph(s) in RichEdit control. See also following properties,
+ which allow to do the same for certain paragraph format attributes
+ separately. }
+ property RE_TextAlign: TRichTextAlign read REGetTextAlign write RESetTextAlign;
+ {* |<#richedit>
+ Returns text alignment for current selection and allows to change it
+ (without changing other formatting attributes). }
+ property RE_TextAlignValid: Boolean index PFM_ALIGNMENT read REGetParaAttrValid;
+ {* |<#richedit>
+ Returns True, if property RE_TextAlign is valid for entire selection. If
+ False, it is concerning only start of selection. }
+ property RE_Numbering: Boolean read REGetNumbering write RESetNumbering;
+ {* |<#richedit>
+ Returns True, if selected text is numbered (or has style of list with
+ bullets). To get / change numbering style, see properties
+ RE_NumStyle and RE_NumBrackets. }
+ property RE_NumStyle: TRichNumbering read REGetNumStyle write RESetNumStyle;
+ {* |<#richedit>
+ Advanced numbering style, such as rnArabic etc. If You use it, do not
+ change RE_Numbering property simultaneously - this can cause changing
+ style to rnBullets only. }
+ property RE_NumStart: Integer read REGetNumStart write RESetNumStart;
+ {* |<#richedit>
+ Starting number for advanced numbering style. If this property is not
+ set, numbering is starting by default from 0. For rnLRoman and rnURoman
+ this cause, that first item has no number to be shown (ancient Roman
+ people did not invent '0'). }
+ property RE_NumBrackets: TRichNumBrackets read REGetNumBrackets write RESetNumBrackets;
+ {* |<#richedit>
+ Brackets style for advanced numbering. rnbPlain is default
+ brackets style, and every time, when RE_NumStyle is changed,
+ RE_NumBrackets is reset to rnbPlain. }
+ property RE_NumTab: Integer read REGetNumTab write RESetNumTab;
+ {* |<#richedit>
+ Tab between start of number and start of paragraph text. If too small too
+ view number, number is not displayed. (Default value seems to be sufficient
+ though). }
+ property RE_NumberingValid: Boolean index PFM_NUMBERING read REGetParaAttrValid;
+ {* |<#richedit>
+ Returns True, if RE_Numbering, RE_NumStyle, RE_NumBrackets, RE_NumTab,
+ RE_NumStart properties are valid for entire selection. }
+ property RE_Level: Integer read REGetLevel;
+ {* |<#richedit>
+ Outline level (for numbering paragraphs?). Read only. }
+ property RE_SpaceBefore: Integer index 0 or PFM_SPACEBEFORE read REGetSpacing write RESetSpacing;
+ {* |<#richedit>
+ Spacing before paragraph. }
+ property RE_SpaceBeforeValid: Boolean index PFM_SPACEBEFORE read REGetParaAttrValid;
+ {* |<#richedit>
+ True, if RE_SpaceBefore value is valid for all selected paragraph (if
+ False, this value is valid only for first paragraph. }
+ property RE_SpaceAfter: Integer index 4 or PFM_SPACEAFTER read REGetSpacing write RESetSpacing;
+ {* |<#richedit>
+ Spacing after paragraph. }
+ property RE_SpaceAfterValid: Boolean index PFM_SPACEAFTER read REGetParaAttrValid;
+ {* |<#richedit>
+ True, only if RE_SpaceAfter value is valid for all selected paragraphs. }
+ property RE_LineSpacing: Integer index 8 or PFM_LINESPACING read REGetSpacing write RESetSpacing;
+ {* |<#richedit>
+ Linespacing in paragraph (this value is based on RE_SpacingRule property). }
+ property RE_SpacingRule: Integer read REGetSpacingRule write RESetSpacingRule;
+ {* |<#richedit>
+ Linespacing rule. Do not know what is it. }
+ property RE_LineSpacingValid: Boolean index PFM_LINESPACING read REGetParaAttrValid;
+ {* |<#richedit>
+ True, only if RE_LineSpacing and RE_SpacingRule values are valid for
+ entire selection. }
+ property RE_Indent: Integer index (20 shl 16) or PFM_OFFSET read REGetParaAttr write RESetParaAttr;
+ {* |<#richedit>
+ Returns left indentation for paragraph in current selection and allows
+ to change it (without changing other formatting attributes). }
+ property RE_IndentValid: Boolean index PFM_OFFSET read REGetParaAttrValid;
+ {* |<#richedit>
+ Returns True, if RE_Indent property is valid for entire selection. }
+ property RE_StartIndent: Integer index (12 shl 16) or PFM_STARTINDENT read REGetParaAttr write RESetParaAttr;
+ {* |<#richedit>
+ Returns left indentation for first line in paragraph for current
+ selection, and allows to change it (without changing other formatting
+ attributes). }
+ property RE_StartIndentValid: Boolean read REGetStartIndentValid;
+ {* |<#richedit>
+ Returns True, if property RE_StartIndent is valid for entire selection. }
+ property RE_RightIndent: Integer index (16 shl 16) or PFM_RIGHTINDENT read REGetParaAttr write RESetParaAttr;
+ {* |<#richedit>
+ Returns right indent for paragraph in current selection, and allow to
+ change it (without changing other formatting attributes). }
+ property RE_RightIndentValid: Boolean index PFM_RIGHTINDENT read REGetParaAttrValid;
+ {* |<#richedit>
+ Returns True, if property RE_RightIndent is valid for entire selection only. }
+ property RE_TabCount: Integer read REGetTabCount write RESetTabCount;
+ {* |<#richedit>
+ Number of tab stops in current selection. This value can not be set greater
+ then MAX_TAB_COUNT (32). }
+ property RE_Tabs[ Idx: Integer ]: Integer read REGetTabs write RESetTabs;
+ {* |<#richedit>
+ Tab stops for RichEdit control. }
+ property RE_TabsValid: Boolean index PFM_TABSTOPS read REGetParaAttrValid;
+ {* |<#richedit>
+ Returns True, if properties RE_Tabs[ ] and RE_TabCount are valid for
+ entire selection. }
+
+ // following does not work now :
+ property RE_BorderWidth[ Side: TBorderEdge ]: Integer index 2 read REGetBorder write RESetBorder;
+ { * |<#richedit>
+ Border width. }
+ property RE_BorderSpace[ Side: TBorderEdge ]: Integer index 0 read REGetBorder write RESetBorder;
+ { * |<#richedit>
+ Border space. }
+ property RE_BorderStyle[ Side: TBorderEdge ]: Integer index 4 read REGetBorder write RESetBorder;
+ { * |<#richedit>
+ Border style. }
+ property RE_BorderValid: Boolean index PFM_BORDER read REGetParaAttrValid;
+ { * |<#richedit>
+ Returns True, if border style, space and width are the same for all
+ paragraphs in selection. }
+ property RE_Table: Boolean index $C000 read REGetParaEffect write RESetParaEffect;
+ { * |<#richedit>
+ True, if current paragraph is a part of table (row, cell or cell end).
+ seems working as read only property. }
+ // end of experiment section
+
+ function RE_FmtStandard: PControl;
+ {* |<#richedit>
+ "Transparent" method (returns @Self as a result), which (when called)
+ provides "standard" keyboard interface for formatting Rich text (just
+ call this method, for example:
+ ! RichEd1 := NewRichEdit( Panel1, [ ] ).SetAlign( caClient ).RE_FmtStandard;
+ Following keys will be maintained additionally:
+ |<pre>
+ CTRL+I - switch "Italic",
+ CTRL+B - switch "Bold",
+ CTRL+U - switch "Underline",
+ CTRL+SHIFT+U - swith underline type
+ and turn underline on (note, that some of underline styles
+ can not be shown properly in RichEdit v2.0 and lower,
+ though RichEdit2.0 stores data successfully).
+ CTRL+O - switch "StrikeOut",
+ CTRL+'gray+' - increase font size,
+ CTRL+'gray-' - decrease font size,
+ CTRL+SHIFT+'gray+' - superscript,
+ CTRL+SHIFT+'gray-' - subscript.
+ CTRL+SHIFT+Z - ReDo
+ |</pre>
+ And, though following standard formatting keys are provided by RichEdit
+ control itself in Windows2000, some of these are not functioning
+ automatically in earlier Windows versions, even for RichEdit2.0. So,
+ functionality of some of these (marked with (*) ) are added here too:
+ |<pre>
+ CTRL+L - align paragraph left, (*)
+ CTRL+R - align paragraph right, (*)
+ CTRL+E - align paragraph center, (*)
+ CTRL+A - select all, (*)
+ double-click on word - select word,
+ CTRL+Right - to next word,
+ CTRL+Left - to previous word,
+ CTRL+Home - to the beginning of text,
+ CTRL+End - to the end of text.
+ CTRL+Z - UnDo
+ |</pre>
+ If You originally assign some (plain) text to Text property, switching "underline"
+ can also change other font attributes, e.g., "bold" - if fsBold style is
+ in default Font. To prevent such behavior, select entire text first (see
+ SelectAll) and make assignment to RE_Font property, e.g.:
+ ! RichEd1.SelectAll;
+ ! RichEd1.RE_Font := RichEd1.RE_Font;
+ ! RichEd1.SelLength := 0;
+ |<br>
+ And, some other notices about formatting. Please remember, that only True
+ Type fonts can be succefully scaled and transformed to get desired effects
+ (e.g., bold). By default, RichEdit uses System font face name, which can
+ even have problems with fsBold style. Please remember also, that assigning
+ RE_Font to RE_Font just initializying formatting attributes, making all
+ those valid in entire text, but does not change font attributes. To use
+ True Type font, directly assign face name You wish, e.g.:
+ ! RichEd1.SelectAll;
+ ! RichEd1.RE_Font := RichEd1.RE_Font;
+ ! RichEd1.RE_Font.FontName := 'Arial';
+ ! RichEd1.SelLength := 0;
+ }
+ procedure RE_CancelFmtStandard;
+ {* Cancels RE_FmtStandard (detaching window procedure handler). }
+ property RE_AutoKeyboard: Boolean index 1 read REGetLangOptions write RESetLangOptions;
+ {* |<#richedit>
+ True if autokeyboard on (lovely "feature" of automatic switching keyboard
+ language when caret is over another language text). For older RichEdit,
+ is 'on' always, for newest - 'off' by default. }
+ property RE_AutoFont: Boolean index 2 read REGetLangOptions write RESetLangOptions;
+ {* |<#richedit>
+ True if autofont on (automatic switching font when keyboard layout is
+ changes). By default, is 'on' always. It is suggested to turn this option
+ off for Unicode control. }
+ property RE_AutoFontSizeAdjust: Boolean index 16 read REGetLangOptions write RESetLangOptions;
+ {* |<#richedit>
+ See IMF_AUTOFONTSIZEADJUST option in SDK:
+ Font-bound font sizes are scaled from insertion point size according to
+ script. For example, Asian fonts are slightly larger than Western ones.
+ This option is turned on by default. }
+ property RE_DualFont: Boolean index 128 read REGetLangOptions write RESetLangOptions;
+ {* |<#richedit>
+ See IMF_DUALFONT option in SDK:
+ Sets the control to dual-font mode. Used for Asian language support.
+ The control uses an English font for ASCII text and a Asian font for
+ Asian text. }
+ property RE_UIFonts: Boolean index 32 read REGetLangOptions write RESetLangOptions;
+ {* |<#richedit>
+ See IMF_UIFONTS option in SDK:
+ Use user-interface default fonts. This option is turned off by default. }
+ property RE_IMECancelComplete: Boolean index 4 read REGetLangOptions write RESetLangOptions;
+ {* |<#richedit>
+ See IMF_IMECANCELCOMPLETE option in SDK:
+ This flag determines how the control uses the composition string of an
+ IME if the user cancels it. If this flag is set, the control discards
+ the composition string. If this flag is not set, the control uses the
+ composition string as the result string. }
+ property RE_IMEAlwaysSendNotify: Boolean index 8 read REGetLangOptions write RESetLangOptions;
+ {* |<#richedit>
+ See IMF_IMEALWAYSSENDNOTIFY option in SDK:
+ Controls how Rich Edit notifies the client during IME composition:
+ |<br>
+ 0: No EN_CHANGED or EN_SELCHANGE notifications during undetermined state.
+ Send notification when final string comes in. (default)
+ |<br>
+ 1: Send EN_CHANGED and EN_SELCHANGE events during undetermined state. }
+
+ property RE_OverwriteMode: Boolean read REGetOverwite write RESetOverwrite;
+ {* |<#richedit>
+ This property allows to control insert/overwrite mode. First, to examine, if
+ insert or overwrite mode is current (but it is necessary either to
+ access this property, at least once, immediately after creating RichEdit
+ control, or to assign event OnRE_InsOvrMode_Change to your handler).
+ Second, to set desired mode programmatically - by assigning value to
+ this property (You also have to initialize monitoring procedure by either
+ reading RE_OverwriteMode property or assigning handler to event
+ OnRE_InsOvrMode_Change immediately following RichEdit control creation). }
+ property OnRE_InsOvrMode_Change: TOnEvent index idx_FOnREInsModeChg
+ read {$IFDEF EVENTS_DYNAMIC} Get_TOnEvent {$ELSE} EV.fOnREInsModeChg {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_TOnEvent {$ELSE} EV.fOnREInsModeChg {$ENDIF};
+ {* |<#richedit>
+ This event is called, whenever key INSERT is pressed in control (and for
+ RichEdit, this means, that insert mode is changed). }
+ property RE_DisableOverwriteChange: Boolean read DF.fReOvrDisable write RESetOvrDisable;
+ {* |<#richedit>
+ It is possible to disable switching between "insert" and "overwrite" mode
+ by user (therefore, event OnRE_InsOvrMode_Change continue works, but it
+ just called when key INSERT is pressed, though RE_OverwriteMode property
+ is not actually changed if switching is disabled). }
+
+ function RE_LoadFromStream( Stream: PStream; Length: Integer;
+ {} Format: TRETextFormat; SelectionOnly: Boolean ): Boolean;
+ {* |<#richedit>
+ Use this method rather then assignment to RE_Text property, if
+ source is stored in file or stream (to minimize resources during
+ loading of RichEdit content). Data is loading starting from current
+ position in stream and no more then Length bytes are loaded (use -1
+ value to load to the end of stream). Loaded data replaces entire
+ content of RichEdit control, or selection only, depending on SelectionOnly
+ flag.
+ |<br>&nbsp;&nbsp;&nbsp;
+ If You want to provide progress (e.g. in form of progress bar), assign
+ OnProgress event to your handler - and to examine current position of
+ loading, read TSream.Position property of soiurce stream). }
+ function RE_SaveToStream( Stream: PStream; Format: TRETextFormat; SelectionOnly: Boolean ): Boolean;
+ {* |<#richedit>
+ Use this method rather then RE_TextProperty to store data to file
+ or stream (to minimize resources during saving of RichEdit content).
+ Data is saving starting from current position in a stream (until
+ end of RichEdit data). If SelectionOnly flag is True, only selected
+ part of RichEdit text is saved.
+ |<br>&nbsp;&nbsp;&nbsp;
+ Like for RE_LoadFromStream, it is possible to assign your method to
+ OnProgress event (but to calculate progress of save-to-stream operation,
+ compare current stream position with RE_Size[ rsBytes ] property
+ value). }
+
+ property OnProgress: TOnEvent index idx_FOnProgress
+ read {$IFDEF EVENTS_DYNAMIC} Get_TOnEvent {$ELSE} EV.fOnProgress {$ENDIF}
+ write {$IFDEF EVENTS_DYNAMIC} Set_TOnEvent {$ELSE} EV.fOnProgress {$ENDIF};
+ {* |<#richedit>
+ This event is called during RE_SaveToStream, RE_LoadFromStream (and also
+ during RE_SaveToFile, RE_LoadFromFile and while accessing or changing
+ RE_Text property). To calculate relative progress, it is possible to
+ examine current position in stream/file with its total size while reading,
+ or with rich edit text size, while writing (property RE_TextSize[ rsBytes ]).
+ }
+ function RE_LoadFromFile( const Filename: KOLString; Format: TRETextFormat;
+ {} SelectionOnly: Boolean ): Boolean;
+ {* |<#richedit>
+ Use this method rather then other assignments to RE_Text property,
+ if a source for RichEdit is the file. See also RE_LoadFromStream. }
+ function RE_SaveToFile( const Filename: KOLString; Format: TRETextFormat;
+ {} SelectionOnly: Boolean ): Boolean;
+ {* |<#richedit>
+ Use this method rather then other similar, if You want to store
+ entire content of RichEdit or selection only of RichEdit to a file. }
+
+ property RE_Text[ Format: TRETextFormat; SelectionOnly: Boolean ]: KOLString read REReadText write REWriteText;
+ {* |<#richedit>
+ This property allows to get / replace content of RichEdit control
+ (entire text or selection only). Using different formats, it is
+ possible to exclude or replace undesired formatting information
+ (see TRETextFormat specification). To get or replace entire text
+ in reText mode (plain text only), it is possible to use habitual
+ for edit controls Text property.
+ |<br>&nbsp;&nbsp;&nbsp;
+ Note: it is possible to append text to the end of RichEdit control
+ using method Add, but only if property RE_Text is accessed at least
+ once:
+ ! RichEdit1.RE_Text[ reText, True ];
+ (This line can be written immediatelly after creating RichEdit control). }
+
+ procedure RE_Append( const S: KOLString; ACanUndo: Boolean );
+ {* }
+ procedure RE_InsertRTF( const S: KOLString );
+ {* }
+ property RE_Error: Integer read DF.fREError;
+ {* |<#richedit>
+ Contains error code, if access to RE_Text failed. }
+
+ procedure RE_HideSelection( aHide: Boolean );
+ {* |<#richedit>
+ Allows to hide / show selection in RichEdit. }
+
+ function RE_SearchText( const Value: KOLString; MatchCase, WholeWord, ScanForward: Boolean;
+ SearchFrom, SearchTo: Integer ): Integer;
+ {* |<#richedit>
+ Searches given string starting from SearchFrom position up to SearchTo
+ position (to the end of text, if SearchTo is -1). Returns zero-based
+ character position of the next match, or -1 if there are no more matches.
+ To search in bacward direction, set ScanForward to False, and pass
+ SearchFrom > SearchTo (or even SearchFrom = -1 and SearchTo = 0). }
+ {$IFNDEF DISABLE_DEPRECATED}
+ {$IFNDEF _FPC}
+ {$IFNDEF _D2} //------- KOLWideString not supported in D2
+ function RE_WSearchText( const Value: KOLWideString; MatchCase, WholeWord, ScanForward: Boolean;
+ SearchFrom, SearchTo: Integer ): Integer;
+ {* |<#richedit>
+ Searches given string starting from SearchFrom position up to SearchTo
+ position (to the end of text, if SearchTo is -1). Returns zero-based
+ character position of the next match, or -1 if there are no more matches.
+ To search in bacward direction, set ScanForward to False, and pass
+ SearchFrom > SearchTo (or even SearchFrom = -1 and SearchTo = 0). }
+ {$ENDIF}
+ {$ENDIF}
+ {$ENDIF DISABLE_DEPRECATED}
+
+ property RE_AutoURLDetect: Boolean read REGetAutoURLDetect write RESetAutoURLDetect;
+ {* |<#richedit>
+ If set to True, automatically detects URLs (and highlights it with
+ blue color, applying fsItalic and fsUnderline font styles (while
+ typing and loading). Default value is False. Note: if event OnRE_URLClick
+ or event OnRE_OverURL are set, property RE_AutoURLDetect is set to True
+ automatically. }
+
+ property RE_URL: PKOLChar read DF.fREUrl;
+ {* |<#richedit>
+ Detected URL (valid in OnRE_OverURL and OnRE_URLClick event handlers). }
+ property OnRE_OverURL: TOnEvent index 0 read {$IFDEF EVENTS_DYNAMIC} REGetOnURL {$ELSE}
+ {$IFDEF F_P} REGetOnURL
+ {$ELSE DELPHI} EV.fOnREOverURL
+ {$ENDIF F_P/DELPHI} {$ENDIF} write RESetOnURL;
+ {* |<#richedit>
+ Is called when mouse is moving over URL. This can be used to set
+ cursor, for example, depending on type of URL (to determine URL type
+ read property RE_URL). }
+ property OnRE_URLClick: TOnEvent index 8 read {$IFDEF EVENTS_DYNAMIC} REGetOnURL {$ELSE}
+ {$IFDEF F_P} REGetOnURL
+ {$ELSE DELPHI} EV.fOnREURLClick
+ {$ENDIF F_P/DELPHI} {$ENDIF} write RESetOnURL;
+ {* |<#richedit>
+ Is called when click on URL detected. }
+
+ //property RE_SelectionBar: Boolean read REGetSelectionBar write RESetSelectionBar;
+ //{* ??? - don't know that is this... }
+ function RE_NoOLEDragDrop: PControl;
+ {* |<#richedit>
+ Just prevents drop OLE objects to the rich edit control. Seems not
+ working for some cases. }
+
+ //function RE_Wyswig: PControl;
+
+ function RE_Bottomless: PControl;
+ // finished ?
+
+ property RE_Transparent: Boolean read REGetTransparent write RESetTransparent;
+ {* |<#richedit>
+ Use this property to make richedit control transparent, instead of
+ Ed_Transparent or Transparent. But do not place such transparent
+ richedit control directly on form - it can be draw incorrectly when
+ form is activated and rich editr control is not current active control.
+ Use at least panel as a parent instead.
+ }
+ property RE_Zoom: TSmallPoint read REGetZoom write RESetZoom;
+ {* |<#richedit>
+ To set zooming for rich edit control (3.0 and above), pass X as numerator
+ and Y as denominator. Resulting X/Y must be between 1/64 and 64. }
+ {$ENDIF NOT_USE_RICHEDIT}
+
+ //========== both for Edit and RichEdit: =====================
+ function CanUndo: Boolean;
+ {* |<#richedit>
+ |<#edit>
+ |<#memo>
+ Returns True, if the edit (or RichEdit) control can correctly process
+ the EM_UNDO message. }
+ procedure EmptyUndoBuffer;
+ {* |<#richedit>
+ |<#edit>
+ |<#memo>
+ Reset the undo flag of an edit control, preventing undoing all previous
+ changes. }
+ function Undo: Boolean;
+ {* |<#richedit>
+ |<#edit>
+ |<#memo>
+ For a single-line edit control, the return value is always TRUE. For a
+ multiline edit control and RichEdit control, the return value is TRUE if
+ the undo operation is successful, or FALSE if the undo operation fails. }
+
+ public
+ property PropInt[ PropName: PKOLChar ]: Integer read Get_Prop_Int write Set_Prop_Int;
+ {* For any windowed control: use it to store desired property in window
+ properties. }
+ {$IFNDEF NOT_USE_RICHEDIT}
+ function RE_Redo: Boolean;
+ procedure FreeCharFormatRec;
+ {* |<#richedit>
+ Only for RichEdit control: Returns True if successful. }
+ {$ENDIF NOT_USE_RICHEDIT}
+ public
+ aAutoSzX: Byte;
+ aAutoSzY: Byte;
+ protected
+ fAlign: TControlAlign;
+ fAligning:TAlignings;
+{$ENDIF GDI}
+ public
+ property Align: TControlAlign read FAlign write Set_Align;
+ {* Align style of a control. If this property is not used in your
+ application, there are no additional code added. Aligning of
+ controls is made in KOL like in VCL. To align controls when
+ initially create ones, use "transparent" function SetAlign
+ ("transparent" means that it returns @Self as a result).
+ |<br>
+ Note, that it is better not to align combobox caClient, caLeft or
+ caRight (better way is to place a panel with Border = 0 and
+ EdgeStyle = esNone, align it as desired and to place a combobox on it
+ aligning caTop or caBottom). Otherwise, big problems could be under
+ Win9x/Me, and some delay could occur under any other systems.
+ |<br> Do not attempt to align some kinds of controls (like combobox)
+ caLeft or caRight, this can cause infinite recursion. }
+ property SizeRedraw: Boolean
+ read {$IFDEF USE_FLAGS} Get_SizeRedraw {$ELSE} fSizeRedraw {$ENDIF}
+ write {$IFDEF USE_FLAGS} Set_SizeRedraw {$ELSE} fSizeRedraw {$ENDIF};
+ procedure ResetEvent( idx: Integer );
+ {$IFDEF FINAL_MARKER}
+ protected
+ ffinal_offset: Boolean;
+ {$ENDIF}
+ end;
+
+ {$IFDEF USE_MHTOOLTIP}
+ {$DEFINE interface_part}
+ {$I KOLMHToolTip_interface.inc}
+ {$UNDEF interface_part}
+ {$ENDIF}
+
+ {$IFDEF USE_MHTOOLTIP}
+ {$DEFINE interface_2}
+ {$I KOLMHToolTip_intf2.inc}
+ {$UNDEF interface_2}
+ {$ENDIF}
+
+{$IFDEF EVENTS_DYNAMIC}
+var EmptyEvents: TEvents;
+{$ENDIF}
+
+function DummyProc123_TRUE( Dummy: Pointer; Sender: PControl; param3: Integer ): Boolean;
+function DummyProc123_0( Dummy: Pointer; Sender: PObj; param3: Integer ): Integer;
+function DummyProc4_TRUE( Dummy: Pointer; Sender: PControl; p3, p4: Integer ): Boolean;
+function DummyProc5_TRUE( Dummy: Pointer; Sender: PControl; p3, p4, p5: Integer ): Boolean;
+procedure DummyOnLVDataProc( Dummy: Pointer; Sender: PControl; Idx, SubItem: Integer;
+ var Txt: KOL_String; var ImgIdx: Integer; var State: DWORD;
+ var Store: Boolean );
+function DummyProc4_0( Dummy: Pointer; Sender: PControl; p3, p4: Integer ): Integer;
+function DummyOnDrawItemProc( Dummy:Pointer; Sender: PObj; DC: HDC; const Rect: TRect; ItemIdx: Integer;
+ DrawAction: TDrawAction; ItemState: TDrawState ): Boolean;
+function DummyOnLVCustomDrawProc( Dummy: Pointer; Sender: PControl; DC: HDC; Stage: DWORD;
+ ItemIdx, SubItemIdx: Integer; const Rect: TRect;
+ ItemState: TDrawState; var TextColor, BackColor: TColor ): DWORD;
+function DummyOnSBBeforeScrollProc(Dummy: Pointer; Sender: PControl;
+ OldPos, NewPos: Integer; Cmd: Word; var AllowChange: Boolean): Boolean;
+
+{$IFDEF USE_GRAPHCTLS}
+procedure InvalidateWindowed( Sender: PObj );
+procedure InvalidateNonWindowed( Sender: PObj );
+{$ENDIF}
+
+function FormNewLabel( Form: PControl ): PControl;
+function FormNewWordWrapLabel( Form: PControl ): PControl;
+function FormNewLabelEffect( Form: PControl ): PControl;
+function FormNewButton( Form: PControl ): PControl;
+function FormNewBitBtn( Form: PControl ): PControl;
+function FormNewPanel( Form: PControl ): PControl;
+function FormNewGradientPanel( Form: PControl ): PControl;
+function FormNewGradientPanelEx( Form: PControl ): PControl;
+function FormNewGroupbox( Form: PControl ): PControl;
+function FormNewPaintbox( Form: PControl ): PControl;
+function FormNewImageShow( Form: PControl ): PControl;
+function FormNewEditBox( Form: PControl ): PControl;
+{$IFDEF USE_RICHEDIT}
+function FormNewRichEdit( Form: PControl ): PControl;
+{$ENDIF}
+function FormNewCombobox( Form: PControl ): PControl;
+function FormNewCheckbox( Form: PControl ): PControl;
+function FormNewRadiobox( Form: PControl ): PControl;
+function FormNewSplitter( Form: PControl ): PControl;
+function FormNewListbox( Form: PControl ): PControl;
+function FormNewListView( Form: PControl ): PControl;
+function FormNewTreeView( Form: PControl ): PControl;
+function FormNewScrollbox( Form: PControl ): PControl;
+function FormNewScrollboxEx( Form: PControl ): PControl;
+function FormNewScrollBar( Form: PControl ): PControl;
+function FormNewProgressBar( Form: PControl ): PControl;
+function FormNewProgressBarEx( Form: PControl ): PControl;
+//function FormNewToolbar( Form: PControl ): PControl;
+function FormNewDateTimePicker( Form: PControl ): PControl;
+{$IFDEF _D4orHigher}
+function FormNewTabControl( Form: PControl ): PControl;
+{$ENDIF}
+
+procedure FormSetSize( Form: PControl );
+procedure FormSetHeight( Form: PControl );
+procedure FormSetWidth( Form: PControl );
+procedure FormSetPosition( Form: PControl );
+procedure FormSetClientSize( Form: PControl );
+procedure FormSetAlign( Form: PControl );
+procedure FormSetTag( Form: PControl );
+{$IFDEF USE_NAMES}
+procedure FormSetName( Form: PControl );
+{$ENDIF USE_NAMES}
+{$IFDEF UNICODE_CTRLS}
+procedure FormSetUnicode( Form: PControl );
+{$ENDIF UNICODE_CTRLS}
+procedure FormAssignHelpContext( Form: PControl );
+procedure FormSetCanResizeFalse( Form: PControl );
+procedure FormInitMenu( Form: PControl );
+
+procedure FormSizeGripFalse( Form: PControl );
+procedure FormSetExStyle( Form: PControl );
+procedure FormSetVisibleFalse( Form: PControl );
+procedure FormSetEnabledFalse( Form: PControl );
+procedure FormResetStyles( Form: PControl );
+procedure FormSetStyle( Form: PControl );
+procedure FormSetAlphaBlend( Form: PControl );
+procedure FormSetHasBorderFalse( Form: PControl );
+procedure FormSetHasCaptionFalse( Form: PControl );
+procedure FormResetCtl3D( Form: PControl );
+procedure FormIconLoad_hInstance( Form: PControl );
+procedure FormIconLoadCursor_0( Form: PControl );
+procedure FormSetIconNeg1( Form: PControl );
+procedure FormIconLoad_hInstance_str( Form: PControl );
+procedure FormSetWindowState( Form: PControl );
+procedure FormCursorLoad_0( Form: PControl );
+procedure FormCursorLoad_hInstance( Form: PControl );
+procedure FormSetColor( Form: PControl );
+procedure FormSetBrushStyle( Form: PControl );
+procedure FormSetBrushBitmap( Form: PControl );
+procedure FormSetFontColor( Form: PControl );
+procedure FormSetFontStyles( Form: PControl );
+procedure FormSetFontHeight( Form: PControl );
+procedure FormSetFontWidth( Form: PControl );
+procedure FormSetFontName( Form: PControl );
+procedure FormSetFontOrientation( Form: PControl );
+procedure FormSetFontCharset( Form: PControl );
+procedure FormSetFontPitch( Form: PControl );
+procedure FormSetBorder( Form: PControl );
+procedure FormSetMarginTop( Form: PControl );
+procedure FormSetMarginBottom( Form: PControl );
+procedure FormSetMarginLeft( Form: PControl );
+procedure FormSetMarginRight( Form: PControl );
+procedure FormSetSimpleStatusText( Form: PControl );
+procedure FormSetStatusText( Form: PControl );
+procedure FormRemoveCloseIcon( Form: PControl );
+procedure FormSetEraseBkgndTrue( Form: PControl );
+procedure FormSetMinWidth( Form: PControl );
+procedure FormSetMaxWidth( Form: PControl );
+procedure FormSetMinHeight( Form: PControl );
+procedure FormSetMaxHeight( Form: PControl );
+procedure FormSetKeyPreviewTrue( Form: PControl );
+// BitBtn only:
+procedure FormSetRepeatInterval( Form: PControl );
+procedure FormSetTextShiftX( Form: PControl );
+procedure FormSetTextShiftY( Form: PControl );
+// LabelEffect only:
+procedure FormSetColor2( Form: PControl );
+
+procedure FormSetTextAlign( Form: PControl );
+procedure FormSetTextVAlign( Form: PControl );
+procedure FormSetTabStopFalse( Form: PControl );
+procedure FormSetIgnoreDefault( Form: PControl );
+procedure FormSetHintText( Form: PControl );
+procedure FormSetAnchor( Form: PControl );
+procedure FormSetCaption( Form: PControl );
+procedure FormSetGradienStyle( Form: PControl );
+procedure FormOverrideScrollbars( Form: PControl );
+// RichEdit only:
+{$IFDEF USE_RICHEDIT}
+procedure FormSetRE_AutoFontFalse( Form: PControl );
+procedure FormSetRE_AutoFontSizeAdjustFalse( Form: PControl );
+procedure FormSetRE_DualFontTrue( Form: PControl );
+procedure FormSetRE_UIFontsTrue( Form: PControl );
+procedure FormSetRE_IMECancelCompleteTrue( Form: PControl );
+procedure FormSetRE_IMEAlwaysSendNotifyTrue( Form: PControl );
+procedure FormSetMaxTextSize( Form: PControl );
+procedure FormSetRE_AutoKeyboardTrue( Form: PControl );
+procedure FormSetRE_DisableOverwriteChangeTrue( Form: PControl );
+procedure FormSetRE_Zoom( Form: PControl );
+{$ENDIF USE_RICHEDIT}
+procedure FormSetListItems( Form: PControl );
+procedure FormSetCount( Form: PControl );
+procedure FormSetDroppedWidth( Form: PControl );
+procedure FormSetButtonIcon( Form: PControl );
+procedure FormSetButtonImage( Form: PControl );
+procedure FormSetButtonBitmap( Form: PControl );
+procedure FormSetDefaultBtn( Form: PControl );
+// progress
+procedure FormSetMaxProgress( Form: PControl );
+procedure FormSetProgress( Form: PControl );
+// list view
+procedure FormLVColumsAdd( Form: PControl );
+procedure FormSetLVColOrder( Form: PControl );
+procedure FormSetLVColImage( Form: PControl );
+// tree view
+procedure FormSetTVIndent( Form: PControl );
+// toolbar
+procedure FormSetTBBtnImgWidth( Form: PControl );
+procedure FormTBAddBitmap( Form: PControl );
+procedure FormSetTBButtonSize( Form: PControl );
+{$IFDEF _D4orHigher}
+procedure FormTBSetTooltips( Form: PControl );
+{$ENDIF}
+procedure FormSetTBButtonsMinWidth( Form: PControl );
+procedure FormSetTBButtonsMaxWidth( Form: PControl );
+procedure FormHideToolbarButton( Form: PControl );
+procedure FormDisableToolbarButton( Form: PControl );
+procedure FormFixFlatXPToolbar( Form: PControl );
+// datetimepicker
+procedure FormSetDateTimeFormat( Form: PControl );
+procedure FormSetDateTimeColor( Form: PControl );
+// tabcontrol
+procedure FormSetCurrentTab( Form: PControl );
+procedure FormSetCurIdx( Form: PControl );
+// scrolbar
+procedure FormSetSBMin( Form: PControl );
+procedure FormSetSBMax( Form: PControl );
+procedure FormSetSBPosition( Form: PControl );
+procedure FormSetSBPageSize( Form: PControl );
+
+
+procedure FormLastCreatedChildAsNewCurrentParent( Form: PControl );
+procedure FormSetUpperParent( Form: PControl );
+procedure FormSetTabpageAsParent( Form: PControl );
+
+procedure FormSetCurCtl( Form: PControl );
+procedure FormSetParent( Form: PControl );
+procedure FormSetEvent( Form: PControl );
+procedure FormSetIndexedEvent( Form: PControl );
+
+
+{$IFDEF WIN_GDI}
+function ToolbarButtonRect( Toolbar: PControl; BtnID: Integer ): TRect;
+{* Use this function instead of reading TControl.TBButtonRect, if you want
+ to have it working the same way when standard toolbar is used or GRushControl
+ toolbar provided in ToGRush.pas unit.
+}
+procedure ToolbarSetTooltips( Toolbar: PControl; BtnID1st: Integer; const Tooltips: array of PKOLChar );
+{* Use this function instead of TContol.TBSetTooltips in your project, when
+ you use ToGRush unit.
+}
+function ToolbarButtonEnabled( Toolbar: PControl; BtnID: Integer ): Boolean;
+{* Use this function instead of reading the property TControl.TBButtonEnabled
+ when tou use ToGRush unit. }
+procedure EnableToolbarButton( Toolbar: PControl; BtnID: Integer; Enable: Boolean );
+{* Use this procedure instead of writing the property TControl.TBButtonEnabled
+ when you use ToGRush unit. }
+function ToolbarButtonVisible( Toolbar: PControl; BtnID: Integer ): Boolean;
+{* Use this function instead of reading the property TControl.TBButtonVisible
+ when tou use ToGRush unit. }
+procedure ShowHideToolbarButton( Toolbar: PControl; BtnID: Integer; Show: Boolean );
+{* Use this procedure instead of writing the property TControl.TBButtonVisible
+ when you use ToGRush unit. }
+function ToolbarButtonChecked( Toolbar: PControl; BtnID: Integer): Boolean;
+{* }
+procedure ToolbarButtonSetChecked( Toolbar: PControl; BtnID: Integer; Checked: Boolean );
+{* }
+procedure ToolbarAddButtons( Toolbar: PControl; const Buttons: array of PKOLChar;
+ const BtnImgIdxArray: array of Integer; Bitmap: HBitmap );
+{* }
+
+function Scrollbar_GetMinPos( sb: PControl ): Integer;
+procedure Scrollbar_SetMinPos( sb: PControl; m: Integer );
+procedure Scrollbar_SetAll( sb: PControl; min, max, pg, cur: Integer );
+function Scrollbar_GetMaxPos( sb: PControl ): Integer;
+procedure Scrollbar_SetMaxPos( sb: PControl; m: Integer );
+function Scrollbar_GetCurPos( sb: PControl ): Integer;
+procedure Scrollbar_SetCurPos( sb: PControl; newp: Integer );
+procedure Scrollbar_SetPageSz( sb: PControl; psz: Integer );
+function Scrollbar_GetPageSz( sb: PControl ): Integer;
+procedure Scrollbar_SetLineSz( sb: PControl; lnz: Integer );
+function Scrollbar_GetLineSz( sb: PControl ): Integer;
+{$ENDIF WIN_GDI}
+
+var ToolbarsIDcmd: Integer = 100;
+
+type
+ TOnPaintBkgnd = procedure( Sender: PControl; DC: HDC; Rect: PRect );
+ {* Global event definition. Used to define Global_OnPaintBackground
+ event placeholder. }
+
+procedure DefaultPaintBackground( Sender: PControl; DC: HDC; Rect: PRect );
+
+var
+ Global_OnPaintBkgnd: TOnPaintBkgnd = DefaultPaintBackground;
+ {* Global event. It is assigned in XBackgounds.pas add-on to replace
+ PaintBackground method for all TVisual objects, allowing great
+ visualization effect: transparent controls over [animated] bitmap
+ background. Idea:
+ | <a href=mailto:"bw@sunv.com">Wei&nbsp;Bao</a>. Implementation:
+ | <a href=mailto:"bonanzas@xcl.cjb.net">Kladov&nbsp;Vladimir</a>. }
+
+function GetShiftState: DWORD;
+{* Returns shift state. }
+
+{$IFDEF WIN_GDI}
+function WndProcMouse( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+function WndProcKeybd( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+function WndProcDummy( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+function WndProcMenu( Sender: PControl; var Msg: TMsg; var Rslt: Integer): Boolean;
+{$IFDEF ALL_BUTTONS_RESPOND_TO_ENTER}
+function WndProcBtnReturnClick( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+{$ENDIF}
+function AutoMinimizeApplet(Self_: PControl; var Msg: TMsg; var Rslt: Integer): Boolean;
+{* By Sergey Shishmintzev
+ Attach this handler to your modal dialog form handle to provide automatic
+ minimization of all other forms in the application together with the dialog. }
+
+procedure InitCommonControlSizeNotify( Ctrl: PControl );
+procedure InitCommonControlCommonNotify( Ctrl: PControl );
+
+procedure DummyAttachProcExtension ( DynHandlers: PList );
+procedure TransparentAttachProcExtension ( DynHandlers: PList );
+
+{$IFNDEF SMALLEST_CODE}
+var Global_AttachProcExtension: procedure( DynHandlers: PList ) = DummyAttachProcExtension;
+{$ENDIF}
+{$ENDIF WIN_GDI}
+var HelpFilePath: PKOLChar;
+ {* Path to application help file. If not assigned, application path with
+ extension replaced to '.hlp' used. To use '.chm' file (HtmlHelp),
+ call AssignHtmlHelp with a path to a html help file (or a name). }
+
+{$IFDEF WIN_GDI}
+procedure AssignHtmlHelp( const HtmlHelpPath: KOLString );
+procedure HtmlHelpCommand( Wnd: HWnd; const HelpFilePath: KOLString; Cmd, Data: Integer );
+{* Use this wrapper procedure to call HtmlHelp API function. }
+//+++++++++++ HTML HELP DEFINITIONS SECTION:
+// this section is from
+// HTML Help API Interface Unit
+// Copyright (c) 1999 The Helpware Group
+// provided for KOL by Alexey Babenko
+const
+ HH_DISPLAY_TOPIC = $0000; {**}
+ HH_HELP_FINDER = $0000; // WinHelp equivalent
+ HH_DISPLAY_TOC = $0001; // not currently implemented
+ HH_DISPLAY_INDEX = $0002; // not currently implemented
+ HH_DISPLAY_SEARCH = $0003; // not currently implemented
+ HH_SET_WIN_TYPE = $0004;
+ HH_GET_WIN_TYPE = $0005;
+ HH_GET_WIN_HANDLE = $0006;
+ HH_ENUM_INFO_TYPE = $0007; // Get Info type name, call repeatedly to enumerate, -1 at end
+ HH_SET_INFO_TYPE = $0008; // Add Info type to filter.
+ HH_SYNC = $0009;
+ HH_RESERVED1 = $000A;
+ HH_RESERVED2 = $000B;
+ HH_RESERVED3 = $000C;
+ HH_KEYWORD_LOOKUP = $000D;
+ HH_DISPLAY_TEXT_POPUP = $000E; // display string resource id or text in a popup window
+ HH_HELP_CONTEXT = $000F; {**}// display mapped numeric value in dwData
+ HH_TP_HELP_CONTEXTMENU = $0010; // text popup help, same as WinHelp HELP_CONTEXTMENU
+ HH_TP_HELP_WM_HELP = $0011; // text popup help, same as WinHelp HELP_WM_HELP
+ HH_CLOSE_ALL = $0012; // close all windows opened directly or indirectly by the caller
+ HH_ALINK_LOOKUP = $0013; // ALink version of HH_KEYWORD_LOOKUP
+ HH_GET_LAST_ERROR = $0014; // not currently implemented // See HHERROR.h
+ HH_ENUM_CATEGORY = $0015; // Get category name, call repeatedly to enumerate, -1 at end
+ HH_ENUM_CATEGORY_IT = $0016; // Get category info type members, call repeatedly to enumerate, -1 at end
+ HH_RESET_IT_FILTER = $0017; // Clear the info type filter of all info types.
+ HH_SET_INCLUSIVE_FILTER = $0018; // set inclusive filtering method for untyped topics to be included in display
+ HH_SET_EXCLUSIVE_FILTER = $0019; // set exclusive filtering method for untyped topics to be excluded from display
+ HH_INITIALIZE = $001C; // Initializes the help system.
+ HH_UNINITIALIZE = $001D; // Uninitializes the help system.
+ HH_PRETRANSLATEMESSAGE = $00fd; // Pumps messages. (NULL, NULL, MSG*).
+ HH_SET_GLOBAL_PROPERTY = $00fc; // Set a global property. (NULL, NULL, HH_GPROP)
+
+ { window properties }
+
+const
+ HHWIN_PROP_TAB_AUTOHIDESHOW = $00000001; // (1 << 0) Automatically hide/show tri-pane window
+ HHWIN_PROP_ONTOP = $00000002; // (1 << 1) Top-most window
+ HHWIN_PROP_NOTITLEBAR = $00000004; // (1 << 2) no title bar
+ HHWIN_PROP_NODEF_STYLES = $00000008; // (1 << 3) no default window styles (only HH_WINTYPE.dwStyles)
+ HHWIN_PROP_NODEF_EXSTYLES = $00000010; // (1 << 4) no default extended window styles (only HH_WINTYPE.dwExStyles)
+ HHWIN_PROP_TRI_PANE = $00000020; // (1 << 5) use a tri-pane window
+ HHWIN_PROP_NOTB_TEXT = $00000040; // (1 << 6) no text on toolbar buttons
+ HHWIN_PROP_POST_QUIT = $00000080; // (1 << 7) post WM_QUIT message when window closes
+ HHWIN_PROP_AUTO_SYNC = $00000100; // (1 << 8) automatically ssync contents and index
+ HHWIN_PROP_TRACKING = $00000200; // (1 << 9) send tracking notification messages
+ HHWIN_PROP_TAB_SEARCH = $00000400; // (1 << 10) include search tab in navigation pane
+ HHWIN_PROP_TAB_HISTORY = $00000800; // (1 << 11) include history tab in navigation pane
+ HHWIN_PROP_TAB_FAVORITES = $00001000; // (1 << 12) include favorites tab in navigation pane
+ HHWIN_PROP_CHANGE_TITLE = $00002000; // (1 << 13) Put current HTML title in title bar
+ HHWIN_PROP_NAV_ONLY_WIN = $00004000; // (1 << 14) Only display the navigation window
+ HHWIN_PROP_NO_TOOLBAR = $00008000; // (1 << 15) Don't display a toolbar
+ HHWIN_PROP_MENU = $00010000; // (1 << 16) Menu
+ HHWIN_PROP_TAB_ADVSEARCH = $00020000; // (1 << 17) Advanced FTS UI.
+ HHWIN_PROP_USER_POS = $00040000; // (1 << 18) After initial creation, user controls window size/position
+ HHWIN_PROP_TAB_CUSTOM1 = $00080000; // (1 << 19) Use custom tab #1
+ HHWIN_PROP_TAB_CUSTOM2 = $00100000; // (1 << 20) Use custom tab #2
+ HHWIN_PROP_TAB_CUSTOM3 = $00200000; // (1 << 21) Use custom tab #3
+ HHWIN_PROP_TAB_CUSTOM4 = $00400000; // (1 << 22) Use custom tab #4
+ HHWIN_PROP_TAB_CUSTOM5 = $00800000; // (1 << 23) Use custom tab #5
+ HHWIN_PROP_TAB_CUSTOM6 = $01000000; // (1 << 24) Use custom tab #6
+ HHWIN_PROP_TAB_CUSTOM7 = $02000000; // (1 << 25) Use custom tab #7
+ HHWIN_PROP_TAB_CUSTOM8 = $04000000; // (1 << 26) Use custom tab #8
+ HHWIN_PROP_TAB_CUSTOM9 = $08000000; // (1 << 27) Use custom tab #9
+ HHWIN_TB_MARGIN = $10000000; // (1 << 28) the window type has a margin
+
+ { window parameters }
+
+const
+ HHWIN_PARAM_PROPERTIES = $00000002; // (1 << 1) valid fsWinProperties
+ HHWIN_PARAM_STYLES = $00000004; // (1 << 2) valid dwStyles
+ HHWIN_PARAM_EXSTYLES = $00000008; // (1 << 3) valid dwExStyles
+ HHWIN_PARAM_RECT = $00000010; // (1 << 4) valid rcWindowPos
+ HHWIN_PARAM_NAV_WIDTH = $00000020; // (1 << 5) valid iNavWidth
+ HHWIN_PARAM_SHOWSTATE = $00000040; // (1 << 6) valid nShowState
+ HHWIN_PARAM_INFOTYPES = $00000080; // (1 << 7) valid apInfoTypes
+ HHWIN_PARAM_TB_FLAGS = $00000100; // (1 << 8) valid fsToolBarFlags
+ HHWIN_PARAM_EXPANSION = $00000200; // (1 << 9) valid fNotExpanded
+ HHWIN_PARAM_TABPOS = $00000400; // (1 << 10) valid tabpos
+ HHWIN_PARAM_TABORDER = $00000800; // (1 << 11) valid taborder
+ HHWIN_PARAM_HISTORY_COUNT = $00001000; // (1 << 12) valid cHistory
+ HHWIN_PARAM_CUR_TAB = $00002000; // (1 << 13) valid curNavType
+
+ { button constants }
+
+const
+ HHWIN_BUTTON_EXPAND = $00000002; // (1 << 1) Expand/contract button
+ HHWIN_BUTTON_BACK = $00000004; // (1 << 2) Back button
+ HHWIN_BUTTON_FORWARD = $00000008; // (1 << 3) Forward button
+ HHWIN_BUTTON_STOP = $00000010; // (1 << 4) Stop button
+ HHWIN_BUTTON_REFRESH = $00000020; // (1 << 5) Refresh button
+ HHWIN_BUTTON_HOME = $00000040; // (1 << 6) Home button
+ HHWIN_BUTTON_BROWSE_FWD = $00000080; // (1 << 7) not implemented
+ HHWIN_BUTTON_BROWSE_BCK = $00000100; // (1 << 8) not implemented
+ HHWIN_BUTTON_NOTES = $00000200; // (1 << 9) not implemented
+ HHWIN_BUTTON_CONTENTS = $00000400; // (1 << 10) not implemented
+ HHWIN_BUTTON_SYNC = $00000800; // (1 << 11) Sync button
+ HHWIN_BUTTON_OPTIONS = $00001000; // (1 << 12) Options button
+ HHWIN_BUTTON_PRINT = $00002000; // (1 << 13) Print button
+ HHWIN_BUTTON_INDEX = $00004000; // (1 << 14) not implemented
+ HHWIN_BUTTON_SEARCH = $00008000; // (1 << 15) not implemented
+ HHWIN_BUTTON_HISTORY = $00010000; // (1 << 16) not implemented
+ HHWIN_BUTTON_FAVORITES = $00020000; // (1 << 17) not implemented
+ HHWIN_BUTTON_JUMP1 = $00040000; // (1 << 18)
+ HHWIN_BUTTON_JUMP2 = $00080000; // (1 << 19)
+ HHWIN_BUTTON_ZOOM = $00100000; // (1 << 20)
+ HHWIN_BUTTON_TOC_NEXT = $00200000; // (1 << 21)
+ HHWIN_BUTTON_TOC_PREV = $00400000; // (1 << 22)
+
+ HHWIN_DEF_BUTTONS = (HHWIN_BUTTON_EXPAND
+ OR HHWIN_BUTTON_BACK
+ OR HHWIN_BUTTON_OPTIONS
+ OR HHWIN_BUTTON_PRINT);
+
+ { Button IDs }
+
+const
+ IDTB_EXPAND = 200;
+ IDTB_CONTRACT = 201;
+ IDTB_STOP = 202;
+ IDTB_REFRESH = 203;
+ IDTB_BACK = 204;
+ IDTB_HOME = 205;
+ IDTB_SYNC = 206;
+ IDTB_PRINT = 207;
+ IDTB_OPTIONS = 208;
+ IDTB_FORWARD = 209;
+ IDTB_NOTES = 210; // not implemented
+ IDTB_BROWSE_FWD = 211;
+ IDTB_BROWSE_BACK = 212;
+ IDTB_CONTENTS = 213; // not implemented
+ IDTB_INDEX = 214; // not implemented
+ IDTB_SEARCH = 215; // not implemented
+ IDTB_HISTORY = 216; // not implemented
+ IDTB_FAVORITES = 217; // not implemented
+ IDTB_JUMP1 = 218;
+ IDTB_JUMP2 = 219;
+ IDTB_CUSTOMIZE = 221;
+ IDTB_ZOOM = 222;
+ IDTB_TOC_NEXT = 223;
+ IDTB_TOC_PREV = 224;
+
+ { Notification codes }
+
+const
+ HHN_FIRST = (0-860);
+ HHN_LAST = (0-879);
+
+ HHN_NAVCOMPLETE = (HHN_FIRST-0);
+ HHN_TRACK = (HHN_FIRST-1);
+ HHN_WINDOW_CREATE = (HHN_FIRST-2);
+
+type
+ {*** Used by command HH_GET_LAST_ERROR
+ NOTE: Not part of the htmlhelp.h but documented in HH Workshop help
+ You must call SysFreeString(xx.description) to free BSTR
+ }
+ tagHH_LAST_ERROR = packed record
+ cbStruct: Integer; // sizeof this structure
+ hr: Integer; // Specifies the last error code.
+ description: PWideChar; // (BSTR) Specifies a Unicode string containing a description of the error.
+ end;
+ HH_LAST_ERROR = tagHH_LAST_ERROR;
+ THHLastError = tagHH_LAST_ERROR;
+
+type
+ {*** Notify event info for HHN_NAVCOMPLETE, HHN_WINDOW_CREATE }
+ PHHNNotify = ^THHNNotify;
+ tagHHN_NOTIFY = packed record
+ hdr: TNMHdr;
+ pszUrl: PAnsiChar; //PCSTR: Multi-byte, null-terminated string
+ end;
+ HHN_NOTIFY = tagHHN_NOTIFY;
+ THHNNotify = tagHHN_NOTIFY;
+
+ {** Use by command HH_DISPLAY_TEXT_POPUP}
+ PHHPopup = ^THHPopup;
+ tagHH_POPUP = packed record
+ cbStruct: Integer; // sizeof this structure
+ hinst: HINST; // instance handle for string resource
+ idString: cardinal; // string resource id, or text id if pszFile is specified in HtmlHelp call
+ pszText: PAnsiChar; // used if idString is zero
+ pt: TPOINT; // top center of popup window
+ clrForeground: COLORREF; // use -1 for default
+ clrBackground: COLORREF; // use -1 for default
+ rcMargins: TRect; // amount of space between edges of window and text, -1 for each member to ignore
+ pszFont: PAnsiChar; // facename, point size, char set, BOLD ITALIC UNDERLINE
+ end;
+ HH_POPUP = tagHH_POPUP;
+ THHPopup = tagHH_POPUP;
+
+ {** Use by commands - HH_ALINK_LOOKUP, HH_KEYWORD_LOOKUP}
+ PHHAKLink = ^THHAKLink;
+ tagHH_AKLINK = packed record
+ cbStruct: integer; // sizeof this structure
+ fReserved: BOOL; // must be FALSE (really!)
+ pszKeywords: PAnsiChar; // semi-colon separated keywords
+ pszUrl: PAnsiChar; // URL to jump to if no keywords found (may be NULL)
+ pszMsgText: PAnsiChar; // Message text to display in MessageBox if pszUrl is NULL and no keyword match
+ pszMsgTitle: PAnsiChar; // Message text to display in MessageBox if pszUrl is NULL and no keyword match
+ pszWindow: PAnsiChar; // Window to display URL in
+ fIndexOnFail: BOOL; // Displays index if keyword lookup fails.
+ end;
+ HH_AKLINK = tagHH_AKLINK;
+ THHAKLink = tagHH_AKLINK;
+
+const
+ HHWIN_NAVTYPE_TOC = 0;
+ HHWIN_NAVTYPE_INDEX = 1;
+ HHWIN_NAVTYPE_SEARCH = 2;
+ HHWIN_NAVTYPE_FAVORITES = 3;
+ HHWIN_NAVTYPE_HISTORY = 4; // not implemented
+ HHWIN_NAVTYPE_AUTHOR = 5;
+ HHWIN_NAVTYPE_CUSTOM_FIRST = 11;
+
+const
+ IT_INCLUSIVE = 0;
+ IT_EXCLUSIVE = 1;
+ IT_HIDDEN = 2;
+
+type
+ PHHEnumIT = ^THHEnumIT;
+ tagHH_ENUM_IT = packed record //tagHH_ENUM_IT, HH_ENUM_IT, *PHH_ENUM_IT
+ cbStruct: Integer; // size of this structure
+ iType: Integer; // the type of the information type ie. Inclusive, Exclusive, or Hidden
+ pszCatName: PAnsiChar; // Set to the name of the Category to enumerate the info types in a category; else NULL
+ pszITName: PAnsiChar; // volitile pointer to the name of the infotype. Allocated by call. Caller responsible for freeing
+ pszITDescription: PAnsiChar; // volitile pointer to the description of the infotype.
+ end;
+ THHEnumIT = tagHH_ENUM_IT;
+
+type
+ PHHEnumCat = ^THHEnumCat;
+ tagHH_ENUM_CAT = packed record //tagHH_ENUM_CAT, HH_ENUM_CAT, *PHH_ENUM_CAT
+ cbStruct: Integer; // size of this structure
+ pszCatName: PAnsiChar; // volitile pointer to the category name
+ pszCatDescription: PAnsiChar; // volitile pointer to the category description
+ end;
+ THHEnumCat = tagHH_ENUM_CAT;
+
+type
+ PHHSetInfoType = ^THHSetInfoType;
+ tagHH_SET_INFOTYPE = packed record //tagHH_SET_INFOTYPE, HH_SET_INFOTYPE, *PHH_SET_INFOTYPE
+ cbStruct: Integer; // the size of this structure
+ pszCatName: PAnsiChar; // the name of the category, if any, the InfoType is a member of.
+ pszInfoTypeName: PAnsiChar; // the name of the info type to add to the filter
+ end;
+ THHSetInfoType = tagHH_SET_INFOTYPE;
+
+type
+ HH_INFOTYPE = DWORD;
+ THHInfoType = HH_INFOTYPE;
+ PHHInfoType = ^THHInfoType; //PHH_INFOTYPE
+
+const
+ HHWIN_NAVTAB_TOP = 0;
+ HHWIN_NAVTAB_LEFT = 1;
+ HHWIN_NAVTAB_BOTTOM = 2;
+
+const
+ HH_MAX_TABS = 19; // maximum number of tabs
+const
+ HH_TAB_CONTENTS = 0;
+ HH_TAB_INDEX = 1;
+ HH_TAB_SEARCH = 2;
+ HH_TAB_FAVORITES = 3;
+ HH_TAB_HISTORY = 4;
+ HH_TAB_AUTHOR = 5;
+ HH_TAB_CUSTOM_FIRST = 11;
+ HH_TAB_CUSTOM_LAST = HH_MAX_TABS;
+
+ HH_MAX_TABS_CUSTOM = (HH_TAB_CUSTOM_LAST - HH_TAB_CUSTOM_FIRST + 1);
+
+ { HH_DISPLAY_SEARCH Command Related Structures and Constants }
+
+const
+ HH_FTS_DEFAULT_PROXIMITY = (-1);
+
+type
+ {** Used by command HH_DISPLAY_SEARCH}
+ PHHFtsQuery = ^THHFtsQuery;
+ tagHH_FTS_QUERY = packed record //tagHH_FTS_QUERY, HH_FTS_QUERY
+ cbStruct: integer; // Sizeof structure in bytes.
+ fUniCodeStrings: BOOL; // TRUE if all strings are unicode.
+ pszSearchQuery: PAnsiChar; // String containing the search query.
+ iProximity: LongInt; // Word proximity.
+ fStemmedSearch: Bool; // TRUE for StemmedSearch only.
+ fTitleOnly: Bool; // TRUE for Title search only.
+ fExecute: Bool; // TRUE to initiate the search.
+ pszWindow: PAnsiChar; // Window to display in
+ end;
+ THHFtsQuery = tagHH_FTS_QUERY;
+
+ { HH_WINTYPE Structure }
+
+type
+ {** Used by commands HH_GET_WIN_TYPE, HH_SET_WIN_TYPE}
+ PHHWinType = ^THHWinType;
+ tagHH_WINTYPE = packed record //tagHH_WINTYPE, HH_WINTYPE, *PHH_WINTYPE;
+ cbStruct: Integer; // IN: size of this structure including all Information Types
+ fUniCodeStrings: BOOL; // IN/OUT: TRUE if all strings are in UNICODE
+ pszType: PAnsiChar; // IN/OUT: Name of a type of window
+ fsValidMembers: DWORD; // IN: Bit flag of valid members (HHWIN_PARAM_)
+ fsWinProperties: DWORD; // IN/OUT: Properties/attributes of the window (HHWIN_)
+
+ pszCaption: PAnsiChar; // IN/OUT: Window title
+ dwStyles: DWORD; // IN/OUT: Window styles
+ dwExStyles: DWORD; // IN/OUT: Extended Window styles
+ rcWindowPos: TRect; // IN: Starting position, OUT: current position
+ nShowState: Integer; // IN: show state (e.g., SW_SHOW)
+
+ hwndHelp: HWND; // OUT: window handle
+ hwndCaller: HWND; // OUT: who called this window
+
+ paInfoTypes: PHHInfoType; // IN: Pointer to an array of Information Types
+
+ { The following members are only valid if HHWIN_PROP_TRI_PANE is set }
+
+ hwndToolBar: HWND; // OUT: toolbar window in tri-pane window
+ hwndNavigation: HWND; // OUT: navigation window in tri-pane window
+ hwndHTML: HWND; // OUT: window displaying HTML in tri-pane window
+ iNavWidth: Integer; // IN/OUT: width of navigation window
+ rcHTML: TRect; // OUT: HTML window coordinates
+
+ pszToc: PAnsiChar; // IN: Location of the table of contents file
+ pszIndex: PAnsiChar; // IN: Location of the index file
+ pszFile: PAnsiChar; // IN: Default location of the html file
+ pszHome: PAnsiChar; // IN/OUT: html file to display when Home button is clicked
+ fsToolBarFlags: DWORD; // IN: flags controling the appearance of the toolbar (HHWIN_BUTTON_)
+ fNotExpanded: BOOL; // IN: TRUE/FALSE to contract or expand, OUT: current state
+ curNavType: Integer; // IN/OUT: UI to display in the navigational pane
+ tabpos: Integer; // IN/OUT: HHWIN_NAVTAB_TOP, HHWIN_NAVTAB_LEFT, or HHWIN_NAVTAB_BOTTOM
+ idNotify: Integer; // IN: ID to use for WM_NOTIFY messages
+ tabOrder: packed array[0..HH_MAX_TABS] of Byte; // IN/OUT: tab order: Contents, Index, Search, History, Favorites, Reserved 1-5, Custom tabs
+ cHistory: Integer; // IN/OUT: number of history items to keep (default is 30)
+ pszJump1: PAnsiChar; // Text for HHWIN_BUTTON_JUMP1
+ pszJump2: PAnsiChar; // Text for HHWIN_BUTTON_JUMP2
+ pszUrlJump1: PAnsiChar; // URL for HHWIN_BUTTON_JUMP1
+ pszUrlJump2: PAnsiChar; // URL for HHWIN_BUTTON_JUMP2
+ rcMinSize: TRect; // Minimum size for window (ignored in version 1)
+
+ cbInfoTypes: Integer; // size of paInfoTypes;
+ pszCustomTabs: PAnsiChar; // multiple zero-terminated strings
+ end;
+ HH_WINTYPE = tagHH_WINTYPE;
+ THHWinType = tagHH_WINTYPE;
+
+const
+ HHACT_TAB_CONTENTS = 0;
+ HHACT_TAB_INDEX = 1;
+ HHACT_TAB_SEARCH = 2;
+ HHACT_TAB_HISTORY = 3;
+ HHACT_TAB_FAVORITES = 4;
+
+ HHACT_EXPAND = 5;
+ HHACT_CONTRACT = 6;
+ HHACT_BACK = 7;
+ HHACT_FORWARD = 8;
+ HHACT_STOP = 9;
+ HHACT_REFRESH = 10;
+ HHACT_HOME = 11;
+ HHACT_SYNC = 12;
+ HHACT_OPTIONS = 13;
+ HHACT_PRINT = 14;
+ HHACT_HIGHLIGHT = 15;
+ HHACT_CUSTOMIZE = 16;
+ HHACT_JUMP1 = 17;
+ HHACT_JUMP2 = 18;
+ HHACT_ZOOM = 19;
+ HHACT_TOC_NEXT = 20;
+ HHACT_TOC_PREV = 21;
+ HHACT_NOTES = 22;
+
+ HHACT_LAST_ENUM = 23;
+
+type
+ {*** Notify event info for HHN_TRACK }
+ PHHNTrack = ^THHNTrack;
+ tagHHNTRACK = packed record //tagHHNTRACK, HHNTRACK;
+ hdr: TNMHdr;
+ pszCurUrl: PAnsiChar; // Multi-byte, null-terminated string
+ idAction: Integer; // HHACT_ value
+ phhWinType: PHHWinType; // Current window type structure
+ end;
+ HHNTRACK = tagHHNTRACK;
+ THHNTrack = tagHHNTRACK;
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Global Control Properties.
+//
+const
+ HH_GPROPID_SINGLETHREAD = 1; // VARIANT_BOOL: True for single thread
+ HH_GPROPID_TOOLBAR_MARGIN = 2; // long: Provides a left/right margin around the toolbar.
+ HH_GPROPID_UI_LANGUAGE = 3; // long: LangId of the UI.
+ HH_GPROPID_CURRENT_SUBSET = 4; // BSTR: Current subset.
+ HH_GPROPID_CONTENT_LANGUAGE = 5; // long: LandId for desired content.
+
+type
+ tagHH_GPROPID = HH_GPROPID_SINGLETHREAD..HH_GPROPID_CONTENT_LANGUAGE;
+ HH_GPROPID = tagHH_GPROPID;
+ THHGPropID = HH_GPROPID;
+
+{$ENDIF WIN_GDI}
+
+{$IFDEF WIN_GDI}
+function SimpleGetCtlBrushHandle( Sender: PControl ): HBrush;
+
+var
+ Global_GetCtlBrushHandle: function( Sender: PControl ): HBrush = SimpleGetCtlBrushHandle;
+ {* Is called to obtain brush handle. }
+{$ENDIF WIN_GDI}
+
+ Global_Align: procedure( Sender: PObj ) = DummyObjProc;
+ {* Is set to perform aligning of control, and only if property Align
+ is changed for TControl, or SetAlign method is called for it. }
+
+{$IFDEF WIN_GDI}
+function WndFunc( W: HWnd; Msg: Cardinal; wParam, lParam: Integer )
+ : Integer; stdcall;
+{* Global message handler for window. Redirects all messages to
+ destination windows, obtaining target TControl object address from
+ window itself, using GetProp API call. }
+{$ENDIF WIN_GDI} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+var AppletRunning: Boolean;
+ {* Is set to True while message loop is processing (in Run procedure). }
+ AppletTerminated: Boolean;
+ {* Is set to True when message loop is terminated. }
+ Applet: PControl;
+ {* Applet window object. Actually, can be set to main form if program
+ not needed in special applet button window (useful to make applet
+ button invisible on taskbar, or to have several forms with single
+ applet button - crete it in that case using NewApplet). }
+ AppButtonUsed: Boolean;
+ {* True if special window to represent applet button (may be invisible)
+ is used. If no, every form is represented with its own taskbar button
+ (always visible). }
+
+{$IFDEF WIN_GDI} //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+ ScreenCursor: HCursor;
+ {* Set this global variable to override any cursor settings of current
+ form or control. }
+
+function ScreenWidth: Integer;
+{* Returns screen width in pixels. }
+function ScreenHeight: Integer;
+{* Returns screen height in pixels. }
+
+type
+ TStatusOption = ( soNoSizeGrip, soTop );
+ {* Options available for status bars. }
+ TStatusOptions = Set of TStatusOption;
+ {* Status bar options. }
+
+procedure DrawFormattedText( Ctl: PControl; DC: HDC; var R: TRect; Flags: DWORD {EditCtl: Boolean} );
+{* This procedure can be useful to draw control's text in custom-defined controls. }
+
+type TCommandActionsParam = {$IFDEF PACK_COMMANDACTIONS} PAnsiChar
+ {$ELSE} PCommandActions {$ENDIF};
+
+{$IFDEF USE_GRAPHCTLS}
+
+{$IFDEF GRAPHCTL_XPSTYLES}
+var DoNotDrawGraphCtlsUsingXPStyles: Boolean;
+procedure DrawFormattedTextXP( Theme: THandle; Ctl: PControl; DC: HDC;
+ var R: TRect; CtlType, CtlStates, Flags1, Flags2: Integer );
+{* This procedure can be useful to draw control's text in custom-defined controls. }
+{$ENDIF}
+
+function _NewGraphCtl( AParent: PControl; ATabStop: Boolean;
+ ACommandActions: TCommandActionsParam ): PControl;
+{* Creates graphic control basics. }
+
+function NewGraphLabel( AParent: PControl; const ACaption: AnsiString ): PControl;
+{* Creates graphic label, which does not require a window handle. }
+
+function NewWordWrapGraphLabel( AParent: PControl; const ACaption: KOLString ): PControl;
+{* Creates graphic label, which does not require a window handle. }
+
+function NewGraphPaintBox( AParent: PControl ): PControl;
+{* Creates graphic paint box (just the same as graphic label, but with empty Caption). }
+
+function NewGraphCheckBox( AParent: PControl; const ACaption: KOLString ): PControl;
+{* Creates graphic checkbox. }
+
+function NewGraphRadioBox( AParent: PControl; const ACaption: KOLString ): PControl;
+{* Creates graphic radiobox. }
+
+function NewGraphButton( AParent: PControl; const ACaption: KOLString ): PControl;
+{* Creates graphic button. }
+
+function NewGraphEditbox( AParent: PControl; Options: TEditOptions ): PControl;
+{* Creates graphic edit box. To do editing, this box should be replaced with
+ real edit box with a handle (actually, it is enough to place an edit box
+ on the same Parent having the same BoundsRect). }
+{$ENDIF USE_GRAPHCTLS}
+{$ENDIF WIN_GDI}
+
+procedure Run( var AppletCtl: PControl );
+{* |<#appbutton>
+ Call this procedure to process messages loop of your program.
+ Pass here pointer to applet button object (if You have created it
+ - see NewApplet) or your main form object of type PControl (created
+ using NewForm).
+ |<br><br>
+ |<h1 align=center><font color=#FF8040><a name="visual_objects_constructors"></a>
+ Visual objects constructing functions
+ |</font></h1>
+ Following constructing functions for visual controls are available:
+ |#control
+}
+
+{$IFDEF WIN_GDI}
+
+procedure TerminateExecution( var AppletCtl: PControl );
+
+procedure AppletMinimize;
+{* Minimizes the application (Applet should be assigned to have effect). }
+procedure AppletHide;
+{* Minimizes and hides application. }
+procedure AppletRestore;
+{* Restores Applet when minimized. }
+
+{YS+}
+procedure RegisterIdleHandler( const OnIdle: TOnEvent );
+{* Registers new Idle handler. Idle handler is called each time when
+ message queue becomes empty. }
+procedure UnRegisterIdleHandler( const OnIdle: TOnEvent );
+{* Unregisters Idle handler. }
+{YS-}
+
+{* ComCtrl32 controls initialization. }
+procedure InitCommonControls; stdcall;
+procedure DoInitCommonControls( dwICC: DWORD );
+{* Calls extended initialization for Common Controls (from ComCtrl32).
+ Pass one of following constants:
+ |<pre>
+ ICC_LISTVIEW_CLASSES = $00000001; // listview, header
+ ICC_TREEVIEW_CLASSES = $00000002; // treeview, tooltips
+ ICC_BAR_CLASSES = $00000004; // toolbar, statusbar, trackbar, tooltips
+ ICC_TAB_CLASSES = $00000008; // tab, tooltips
+ ICC_UPDOWN_CLASS = $00000010; // updown
+ ICC_PROGRESS_CLASS = $00000020; // progress
+ ICC_HOTKEY_CLASS = $00000040; // hotkey
+ ICC_ANIMATE_CLASS = $00000080; // animate
+ ICC_WIN95_CLASSES = $000000FF;
+ ICC_DATE_CLASSES = $00000100; // month picker, date picker, time picker, updown
+ ICC_USEREX_CLASSES = $00000200; // comboex
+ ICC_COOL_CLASSES = $00000400; // rebar (coolbar) control
+ ICC_INTERNET_CLASSES = $00000800;
+ ICC_PAGESCROLLER_CLASS = $00001000; // page scroller
+ ICC_NATIVEFNTCTL_CLASS = $00002000; // native font control
+ |</pre>
+ }
+
+const
+ ICC_LISTVIEW_CLASSES = $00000001; // listview, header
+ ICC_TREEVIEW_CLASSES = $00000002; // treeview, tooltips
+ ICC_BAR_CLASSES = $00000004; // toolbar, statusbar, trackbar, tooltips
+ ICC_TAB_CLASSES = $00000008; // tab, tooltips
+ ICC_UPDOWN_CLASS = $00000010; // updown
+ ICC_PROGRESS_CLASS = $00000020; // progress
+ ICC_HOTKEY_CLASS = $00000040; // hotkey
+ ICC_ANIMATE_CLASS = $00000080; // animate
+ ICC_WIN95_CLASSES = $000000FF;
+ ICC_DATE_CLASSES = $00000100; // month picker, date picker, time picker, updown
+ ICC_USEREX_CLASSES = $00000200; // comboex
+ ICC_COOL_CLASSES = $00000400; // rebar (coolbar) control
+ ICC_INTERNET_CLASSES = $00000800;
+ ICC_PAGESCROLLER_CLASS = $00001000; // page scroller
+ ICC_NATIVEFNTCTL_CLASS = $00002000; // native font control
+
+function OleInit: Boolean;
+{* Calls OleInitialize (once - all other calls are simulated by incrementing
+ call counter. Every OleInit shoud be complemented with correspondent OleUninit.
+ (Though, it is possible to call API function OleUnInitialize once to
+ cancel all OleInit calls). }
+procedure OleUnInit;
+{* Decrements counter and calls OleUnInitialize when it is zeroed. }
+var OleInitCount: Integer;
+
+function StringToOleStr(const Source: Ansistring): PWideChar;
+{* }
+
+function SysAllocStringLen(psz: PWideChar; len: Integer): PWideChar; stdcall;
+procedure SysFreeString( psz: PWideChar ); stdcall;
+
+{$ENDIF WIN_GDI}
+{ -- Contructors for visual controls -- }
+
+{$IFDEF GDI}
+{$IFDEF COMMANDACTIONS_OBJ}
+function NewCommandActionsObj: PCommandActionsObj;
+function NewCommandActionsObj_Packed( fromPack: PAnsiChar ): PCommandActionsObj;
+{$ENDIF}
+
+function _NewWindowed( AParent: PControl; ControlClassName: PKOLChar;
+ Ctl3D: Boolean; ACommandActions: TCommandActionsParam): PControl;
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+FUNCTION _NewWindowed( AParent: PControl; ControlClassName: PAnsiChar;
+ widget: PGtkWidget; need_eventbox: Boolean ): PControl;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF WIN_GDI} //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+function NewApplet( const Caption: KOLString ): PControl;
+{* |<#control>
+ Creates applet button window, which has to be parent of all other forms
+ in your project (but this is *not must*). See also comments about NewForm.
+ |<br>
+ Following methods, properties and events are useful to work with applet
+ control:
+ |#appbutton }
+{$ENDIF WIN_GDI}
+
+function NewForm( AParent: PControl; const Caption: KOLString ): PControl;
+{* |<#control>
+ Creates form window object and returns pointer to it. If You use only one form,
+ and You are not going to do applet button on task bar invisible, it is not
+ necessary to create also special applet button window - just pass
+ your (main) form object to Run procedure. In that case, it is a good
+ idea to assign pointer to your main form object to Applet variable
+ immediately following creating it - because some objects (e.g. TTimer)
+ want to have Applet assigned to something.
+ |<br>
+ |&D=<a href="tcontrol.htm#%1" target=_top> %0 </a>
+ Following methods, properties and events are useful to work with forms
+ (ones common for all visual objects, such as <D Left>, <D Top>, <D Width>,
+ <D Height>, etc. are not listed here - look TControl for it):
+ |#form }
+
+function NewAlienPanel( AParentWnd: HWnd; EdgeStyle: TEdgeStyle ): PControl;
+
+{$IFDEF GDI}
+function _NewControl( AParent: PControl; ControlClassName: PKOLChar;
+ Style: DWORD; Ctl3D: Boolean; Actions: TCommandActionsParam ): PControl;
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+FUNCTION _NewControl( AParent: PControl; ControlClassName: PAnsiChar;
+ Style: DWORD; Ctl3D: Boolean; widget: PGtkWidget; need_eventbox: Boolean ): PControl;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+function NewButton( AParent: PControl; const Caption: KOLString ): PControl;
+{* |<#control>
+ Creates button on given parent control or form.
+ Please note, that in Windows, buttons can not change its <D Font> color
+ and to be <D Transparent>.
+ |<br> Following methods, properies and events are (especially) useful with
+ a button:
+ |#button }
+
+{$IFDEF WIN_GDI} //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+function NewBitBtn( AParent: PControl; const Caption: KOLString;
+ Options: TBitBtnOptions; Layout: TGlyphLayout; GlyphBitmap: HBitmap; GlyphCount: Integer ): PControl;
+{* |<#control>
+ Creates image button (actually implemented as owner-drawn). In Options,
+ it is possible to determine, whether bitmap or image list used to contain
+ one or more (up to 5) images, correspondent to certain BitBtn state.
+ |<br>&nbsp;&nbsp;&nbsp;
+ For case of imagelist (option bboImageList), it is possible to use a
+ number of glyphs from the image list, starting from image index given
+ by GlyphCount parameter. Number of used glyphs is passed in that case
+ in high word of GlyphCount parameter (if 0, one image is used therefore).
+ For bboImageList, BitBtn can be Transparent (and in that case bboNoBorder
+ style can be useful to draw custom buttons of non-rectangular shape).
+ |<br>&nbsp;&nbsp;&nbsp;
+ For case of bitmap BitBtn, image is stretched down (if too big), but can
+ not be transparent. It is not necessary for bitmap BitBtn to pass correct
+ GlyphCount - it is calculated on base of bitmap size, if 0 is passed.
+ |<br>&nbsp;&nbsp;&nbsp;
+ And, certainly, BitBtn can be without glyph image (text only). For that
+ case, it is therefore is more flexible and power than usual Button (but
+ requires more code). E.g., BitBtn can change its <D Font>, <D Color>,
+ and to be totally <D Transparent>.
+ Moreover, BitBtn can be <D Flat>, bboFixed, <D SpeedButton> and
+ have property <D RepeatInterval>.
+ |<br>&nbsp;&nbsp;&nbsp;
+ Note: if You use bboFixed Style, use OnChange event instead of OnClick,
+ because <D Checked> state is changed immediately however OnClick occure
+ only when mouse or space key released (and can be not called at all if
+ mouse button is released out of BitBtn bounds). Also, bboFixed defines
+ only which glyph to show (the border if it is not turned off behaves as
+ usual for a button, i.e. it becomes lowered and then raised again at any click).
+ Here You can find references to other properties, events and methods
+ applicable to BitBtn:
+ |#bitbtn }
+
+{$ENDIF GDI}
+function NewLabel( AParent: PControl; const Caption: KOLString ): PControl;
+{* |<#control>
+ Creates static text control (native Windows STATIC control).
+ Use property <D Caption> at run time to change label text. Also
+ it is possible to adjust label <D Font>, <D Brush> or <D Color>.
+ Label can be <D Transparent>. If You want to have rotated text
+ label, call NewLabelEffect instead and change its <D Font>.FontOrientation.
+ Other references certain for a label:
+ |#label }
+{$IFDEF GDI}
+
+function NewWordWrapLabel( AParent: PControl; const Caption: KOLString ): PControl;
+{* |<#control>
+ Creates multiline static text control (native Windows STATIC control),
+ which can wrap long text onto several lines. See also NewLabel.
+ See also:
+ |#wwlabel
+ |#label }
+
+function NewLabelEffect( AParent: PControl; const Caption: KOLString; ShadowDeep: Integer ): PControl;
+{* |<#control>
+ Creates 3D-label with capability to rotate its text <D Caption>, which
+ is controlled by changing <D Font>.FontOrientation property. If You want
+ to get flat effect label (e.g. to rotate it only), pass <D ShadowDeep> = 0.
+ Please note, that drawing procedure uses <D Canvas> property, so using of
+ LabelEffect leads to increase size of executable.
+ See also:
+ |#3dlabel
+ |#label }
+
+{$ENDIF GDI}
+function NewPaintbox( AParent: PControl ): PControl;
+{* |<#control>
+ Creates owner-drawn STATIC control. Set its <D OnPaint> event to
+ perform custom painting.
+ |#paintbox }
+{$IFDEF GDI}
+
+function NewImageShow( AParent: PControl; AImgList: PImageList; ImgIdx: Integer ): PControl;
+{* |<#control>
+ Creates an image show control, implemented as a paintbox which is used to
+ draw an image from the imagelist. At run-time, use property CurIndex to
+ select another image from the imagelist, and a property ImageListNormal to
+ use another image list. When the control is created, its size becomes
+ equal to dimensions of imagelist (if any). }
+
+function NewScrollBar( AParent: PControl; BarSide: TScrollerBar ): PControl;
+{* |<#control>
+ Creates simple scroll bar. }
+
+function NewScrollBox( AParent: PControl; EdgeStyle: TEdgeStyle;
+ Bars: TScrollerBars ): PControl;
+{* |<#control>
+ Creates simple scrolling box, which can be used any way you wish, e.g. to scroll
+ certain large image. To provide automatic scrolling of a set of child controls,
+ use advanced scroll box, created with NewScrollBoxEx. }
+
+procedure NotifyScrollBox( Self_, Child: PControl );
+
+function NewScrollBoxEx( AParent: PControl; EdgeStyle: TEdgeStyle ): PControl;
+{* |<#control>
+ Creates extended scrolling box control, which automatically scrolls child
+ controls (if any). }
+
+function NewGradientPanel( AParent: PControl; Color1, Color2: TColor ): PControl;
+{* |<#control>
+ Creates gradient-filled STATIC control. To adjust colors at the
+ run time, change <D Color1> and <D Color2> properties (which initially are
+ assigned from Color1, Color2 parameters), and call <D Invalidate> method
+ to repaint control. }
+
+function NewGradientPanelEx( AParent: PControl; Color1, Color2: TColor;
+ Style: TGradientStyle; Layout: TGradientLayout ): PControl;
+{* |<#control>
+ Creates gradient-filled STATIC control. To adjust colors at the
+ run time, change <D Color1> and <D Color2> properties (which initially are
+ assigned from Color1, Color2 parameters), and call <D Invalidate> method
+ to repaint control. Depending on style and first line/point layout, can
+ looking different. Idea: Vladimir Stojiljkovic. }
+
+function NewPanel( AParent: PControl; EdgeStyle: TEdgeStyle ): PControl;
+{* |<#control>
+ Creates panel, which can be parent for other controls (though, any
+ control can be used as a parent for other ones, but panel is specially
+ designed for such purpose). }
+
+{$IFDEF USE_MDI}
+function NewMDIClient( AParent: PControl; WindowMenu: THandle ): PControl;
+{* |<#control>
+ Creates MDI client window, which is a special type of child window,
+ containing all MDI child windows, created calling NewMDIChild function.
+ On a form, MDI client behaves like a panel, so it can be placed and sized
+ (or aligned) like any other controls. To minimize flick during resizing
+ main form having another aligned controls, place MDI client window on
+ a panel and align it caClient in the panel.
+ |<br>Note:
+ MDI client must be a single on the form. }
+
+function NewMDIChild( AParent: PControl; const ACaption: KOLString ): PControl;
+{* |<#control>
+ Creates MDI client window. AParent should be a MDI client window,
+ created with NewMDIClient function. }
+{$ENDIF USE_MDI}
+
+function NewSplitter( AParent: PControl; MinSizePrev, MinSizeNext: Integer ): PControl;
+{* |<#control>
+ Creates splitter control, which will separate previous one (i.e. last
+ created one before splitter on the same parent) from created
+ next, allowing to user to adjust size of separated controls by dragging
+ the splitter in desired direction. Created splitter becomes vertical
+ or horizontal depending on Align style of previous control on the same
+ parent (if caLeft/caRight then vertical, if caTop/caBottom then horizontal).
+ |<br>&nbsp;&nbsp;&nbsp;
+ Please note, what if previous control has no Align equal to caLeft/caRight
+ or caTop/caBottom, splitter will not be able to function normally. If
+ previous control does not exist, it is yet possible to use splitter as
+ a resizeable panel (but set its initial Align value first - otherwise it
+ is not set by default. Also, change Cursor property as You wish in that
+ case, since it is not set too in case, when previous control does not
+ exist).
+ |<br>&nbsp;&nbsp;&nbsp;
+ Additional parameters determine, which minimal size (width or height -
+ correspondently to split direction) is allowed for left (top) control
+ and to rest of client area of parent, correspondently. (It is possible
+ later to set second control for checking its size with MinSizeNext
+ value - using TControl.SecondControl property). If -1 passed,
+ correspondent control size is not checked during dragging of splitter.
+ Usually 0 is more suitable value (with this value, it is garantee, that
+ splitter will be always available even if mouse was released far from the
+ edge of form).
+ |<br>&nbsp;&nbsp;&nbsp;
+ It is possible for user to press Escape any time while dragging splitter
+ to abort all adjustments made starting from left mouse button push and
+ begin of drag the splitter. But remember please, that such event is
+ controlled using timer, and therefore correspondent keyboard events
+ are received by currently focused control. Be sure, that pressing Escape
+ will not affect to any control on form, which could be focused, otherwise
+ filter keyboard messages (by yourself) to prevent undesired handling of
+ Escape key by certain controls while splitting. (Use Dragging property
+ to check if splitter is dragging by user with mouse).
+ |<br>&nbsp;&nbsp;&nbsp;
+ See also:
+ NewSplitterEx
+ |#splitter }
+
+function NewSplitterEx( AParent: PControl; MinSizePrev, MinSizeNext: Integer;
+ EdgeStyle: TEdgeStyle ): PControl;
+{* |<#control>
+ Creates splitter control. Difference from NewSplitter is what it is possible
+ to determine if a splitter will be beveled or not. See also NewSplitter. }
+
+function NewGroupbox( AParent: PControl; const Caption: KOLString ): PControl;
+{* |<#control>
+ Creates group box control. Note, that to group radio items, group
+ box is not necessary - any parent can play role of group for radio items.
+ See also NewPanel. }
+
+function NewCheckbox( AParent: PControl; const Caption: KOLString ): PControl;
+{* |<#control>
+ Creates check box control. Special properties, methods, events:
+ |#checkbox }
+
+function NewCheckBox3State( AParent: PControl; const Caption: KOLString ): PControl;
+{* |<#control>
+ Creates check box control with 3 states. Special properties, methods,
+ events:
+ |#checkbox }
+
+function NewRadiobox( AParent: PControl; const Caption: KOLString ): PControl;
+{* |<#control>
+ Creates radio box control. Alternative radio items must have the
+ same parent window (regardless of its kind, either groupbox (NewGroupbox),
+ panel (NewPanel) or form itself). Following properties, methods and events
+ are specially for radiobox controls:
+ |#radiobox }
+
+function NewEditbox( AParent: PControl; Options: TEditOptions ): PControl;
+{* |<#control>
+ Creates edit box control. To create multiline edit box, similar to
+ TMemo in VCL, apply eoMultiline in Options. Following properties, methods,
+ events are special for edit controls:
+ |#edit }
+
+{$IFNDEF NOT_USE_RICHEDIT}
+var FRichEditModule: Integer;
+ RichEditClass: PKOLChar;
+
+const RichEditLibnames: array[ 0..3 ] of PKOLChar =
+ ( 'msftedit', 'riched20',
+ 'riched32', 'riched' );
+ RichEditClasses: array[ 0..3 ] of PKOLChar =
+ ( 'RichEdit50W', 'RichEdit20A',
+ 'RichEdit', 'RichEdit' );
+var RichEditIdx: Byte = High( RichEditLibnames );
+
+function NewRichEdit( AParent: PControl; Options: TEditOptions ): PControl;
+{* |<#control>
+ Creates rich text edit control. A rich edit control is a window in which
+ the user can enter and edit text. The text can be assigned character and
+ paragraph formatting, and can include embedded OLE objects. Rich edit
+ controls provide a programming interface for formatting text. However, an
+ application must implement any user interface components necessary to make
+ formatting operations available to the user.
+ |<br>&nbsp;&nbsp;&nbsp;
+ Note: eoPassword, eoMultiline options have no effect for RichEdit control.
+ Some operations are supersided with special versions of those, created
+ especially for RichEdit, but in some cases it is necessary to use
+ another properties and methods, specially designed for RichEdit (see
+ methods and properties, which names are starting from RE_...).
+ |<br>&nbsp;&nbsp;&nbsp;
+ Following properties, methods, events are special for edit controls:
+ |#richedit
+ }
+
+function NewRichEdit1( AParent: PControl; Options: TEditOptions ): PControl;
+{* |<#control>
+ Like NewRichEdit, but to work with older RichEdit control version 1.0
+ (window class 'RichEdit' forced to use instead of 'RichEdit20A', even
+ if library RICHED20.DLL found and loaded successfully). One more
+ difference - OleInit is not called, so the most of OLE capabilities
+ of RichEdit could not working. }
+{$ENDIF NOT_USE_RICHEDIT}
+
+function NewListbox( AParent: PControl; Options: TListOptions ): PControl;
+{* |<#control>
+ Creates list box control. Following properties, methods and events are
+ special for Listbox:
+ |#listbox }
+
+function NewCombobox( AParent: PControl; Options: TComboOptions ): PControl;
+{* |<#control>
+ Creates new combo box control. Note, that it is not possible to align
+ combobox caLeft or caRight: this can cause infinite recursion in the
+ application.
+ |<br>Following properties, methods and events are
+ special for Combobox:
+ |#combo }
+
+function _NewCommonControl( AParent: PControl; ClassName: PKOLChar; Style: DWORD;
+ Ctl3D: Boolean; Actions: TCommandActionsParam
+ ): PControl;
+
+function NewProgressbar( AParent: PControl ): PControl;
+{* |<#control>
+ Creates progress bar control. Following properties are special for
+ progress bar:
+ |#progressbar
+ See also NewProgressEx. }
+
+function NewProgressbarEx( AParent: PControl; Options: TProgressbarOptions ): PControl;
+{* |<#control>
+ Can create progress bar with smooth style (progress is not segmented
+ onto bricks) or/and vertical progress bar - using additional parameter.
+ For list of properties, suitable for progress bars, see NewProgressbar. }
+
+function NewListView( AParent: PControl; Style: TListViewStyle; Options: TListViewOptions;
+ ImageListSmall, ImageListNormal, ImageListState: PImageList ): PControl;
+{* |<#control>
+ Creates list view control. It is very powerful control, which can partially
+ compensate absence of grid controls (in lvsDetail view mode). Properties,
+ methods and events, special for list view control are:
+ |#listview }
+
+function NewTreeView( AParent: PControl; Options: TTreeViewOptions;
+ ImgListNormal, ImgListState: PImageList ): PControl;
+{* |<#control>
+ Creates tree view control. See tree view methods and properties:
+ |#treeview }
+
+function NewTabControl( AParent: PControl; const Tabs: array of PKOLChar; Options: TTabControlOptions;
+ ImgList: PImageList; ImgList1stIdx: Integer ): PControl;
+{* |<#control>
+ Creates new tab control (like notebook). To place child control on a certain
+ page of TabControl, use property Pages[ Idx ], for example:
+ ! Label1 := NewLabel( TabControl1.Pages[ 0 ], 'Label1' );
+ | &nbsp;&nbsp;&nbsp;
+ To determine number of pages at run time, use property <D Count>;
+ |<br> to determine which page is currently selected (or to change
+ selection), use property <D CurIndex>;
+ |<br> to feedback to switch between tabs assign your handler to OnSelChange
+ event;
+ |<br>Note, that by default, tab control is created with a border lowered to
+ tab control's parent. To remove it, you can apply WS_EX_TRANSPARENT extended
+ style (see TControl.ExStyle property), but painting of some child controls
+ can be strange a bit in this case (no border drawing for edit controls was
+ found, but not always...). You can also apply style WS_THICKFRAME (TControl.Style
+ property) to make the border raised.
+ |<br> Other methods and properties, suitable for tab control, are:
+ |#tabcontrol }
+{$IFNDEF OLD_ALIGN}
+function NewTabEmpty( AParent: PControl; Options: TTabControlOptions;
+ ImgList: PImageList ): PControl;
+{* |<#control>
+ Creates new empty tab control for using metods TC_Insert (to create Pages as Panel),
+ or TC_InsertControl (if you want using your custom Pages).}
+{$ENDIF}
+
+var ToolbarDfltWidth: WORD = 1000;
+ ToolbarDfltHeight: WORD = 26;
+
+function NewToolbar( AParent: PControl; Align: TControlAlign; Options: TToolbarOptions;
+ Bitmap: HBitmap; const Buttons: array of PKOLChar;
+ const BtnImgIdxArray: array of Integer ) : PControl;
+{* |<#control>
+ Creates toolbar control. Bitmap (if present) must contain images for all buttons
+ excluding separators (defined by string '-' in Buttons array) and system images,
+ otherwise last buttons will no have images at all. Image width for every button
+ is assumed to be equal to Bitmap height (if last of "squares" has
+ insufficient width, it will not be used). To define fixed buttons, use
+ characters '+' or '-' as a prefix for button string (even empty). To
+ create groups of (radio-)buttons, use also '!' follow '+' or '-'. (These rules
+ are similar used in menu creation). To define drop down button, use (as
+ first) prefix '^'. (Do not forget to set <D OnTBDropDown> event for this
+ case). If You want to assign images to buttons not in the same order
+ how these are placed in Bitmap (or You use system bitmap), define for every
+ button (in BtnImgIdxArray array) indexes for every button (excluding
+ separator buttons). Otherwise, it is possible to define index only for first
+ button (e.g., [0]). It is also possible to change TBImages[ ] property
+ for such purpose, or do the same in method TBSetBtnImgIdx).
+ |<br>
+ Following properties, methods and event are specially designed to work with
+ toolbar control:
+ |#toolbar
+ |<br>&nbsp;&nbsp;&nbsp;
+ If your project uses Align property to align controls, this can conflict with
+ toolbar native aligning. To solve such problem, place toolbar to parent panel,
+ which has its own Align property assigned to desired value.
+ |<br>
+ To create toolbar with buttons, drawn from top to bottom, instead from left
+ to right, combine caLeft / caRight in Align parameter and style tboWrapable
+ when create toolbar. To adjust width of vertically aligned toolbar, it is
+ possible to call ResizeParentLeft for it. E.g.:
+
+ ! P0 := NewPanel( W, esRaised ) .SetSize( 30, 0 ) .SetAlign( caLeft );
+ ! // ^^^^^^^^^^^^^^^^^ //////
+ !TB := NewToolbar( P0, caLeft, [ tboNoDivider, tboWrapable ], DWORD(-1),
+ ! // ////// ///////////
+ ! [ ' ', ' ', ' ', '-', ' ', ' ' ],
+ ! [ STD_FILEOPEN ] ).ResizeParentRight;
+ !//Note, that caLeft is *must*, and tboWrapable style too. SetSize for
+ !//parent panel is not necessary, but only if ResizeParentRight is called
+ !//than for Toolbar.
+ |<br><br>
+ One more note: if You create toolbar without text labels (passing ' ' for
+ each button You add), include also option tboTextRight to fix incorrect
+ sizing of buttons under Windows9x.
+ |<br>
+ And, certainly, if you use image lists rather then bitmap, all written
+ above about Bitmap become absolutely incorrect.
+ }
+
+function NewDateTimePicker( AParent: PControl; Options: TDateTimePickerOptions )
+ : PControl;
+{* |<#control>
+ Creates date and time picker common control.
+}
+
+{ -- Constructor for Image List objet -- }
+
+function NewImageList( AOwner: PControl ): PImageList;
+{* Constructor of TImageList object. Unlike other non-visual objects, image list
+ can be parented by TControl object (but this does not *must*), and in that
+ case it is destroyed automatically when its parent control is destroyed.
+ Every control can have several TImageList objects, linked to a simple list.
+ But if any TImageList object is destroyed, all following ones are destroyed
+ too (at least, now I implemented it so). }
+
+{$ENDIF WIN_GDI}
+
+type
+ TTimerKind = ( tkReal, tkProcess, tkProfiler ); // only for UNIX!
+ PTimer = ^TTimer;
+{ ----------------------------------------------------------------------
+ TTimer object
+----------------------------------------------------------------------- }
+ TTimer = object( TObj )
+ {* Easy timer incapsulation object. It uses separate topmost window,
+ common for all timers in the application, to handle WM_TIMER message.
+ This allows using timers in non-windowed application (but anyway it
+ should contain message handling loop for a thread).
+ |<br>
+ Note: in UNIX, there are no special windows created, certainly. }
+ protected
+ fHandle : Integer;
+ fEnabled: Boolean;
+ fInterval: Integer;
+ fOnTimer: TOnEvent;
+ {$IFDEF LIN}
+ {$IFNDEF GTK}
+ {$IFNDEF QT}
+ fPrev, fNext: PTimer; // äâóñâÿçíûé ñïèñîê âñåõ _àêòèâíûõ_ òàéìåðîâ
+ fTimeStart: clock_t;
+ fExpireNext: clock_t;
+ fExpireTotal: Int64;
+ fTimerHandled: Boolean;
+ fResolution: Integer;
+ fPeriodic: Boolean;
+ fMultimedia: Boolean;
+ {$ENDIF QT}
+ {$ENDIF GTK}
+ {$ENDIF}
+ procedure SetEnabled(const Value: Boolean); {$IFDEF WIN} virtual; {$ENDIF}
+ procedure SetInterval(const Value: Integer);
+ protected
+ destructor Destroy; virtual;
+ {* Destructor. }
+ public
+ property Handle : Integer read fHandle;
+ {* Windows timer object handle. }
+ property Enabled : Boolean read fEnabled write SetEnabled;
+ {* True, is timer is on. Initially, always False. }
+ property Interval : Integer read fInterval write SetInterval;
+ {* Interval in milliseconds (1000 is default and means 1 second).
+ Note: in UNIX, if an Interval can be set to a value large then 30 minutes,
+ add a conditional definition SUPPORT_LONG_TIMER to the project options. }
+ property OnTimer : TOnEvent read fOnTimer write fOnTimer;
+ {* Event, which is called when time interval is over. }
+ {$IFDEF LIN}
+ {$IFNDEF GTK}
+ {$IFNDEF QT}
+ property Resolution: Integer read fResolution write fResolution; // dummy property, just for compatibility
+ property Periodic: Boolean read fPeriodic write fPeriodic;
+ {$ENDIF QT}
+ {$ENDIF GTK}
+ {$ENDIF LIN}
+ end;
+
+function NewTimer( Interval: Integer ): PTimer;
+{* Constructs initially disabled timer with interval 1000 (1 second). }
+
+{$IFDEF WIN}
+type
+ PMMTimer = ^TMMTimer;
+
+ TMMTimer = object( TTimer )
+ {* Multimedia timer incapsulation object. Does not require Applet or special
+ window to handle it. System creates a thread for each high resolution
+ timer, so using many such objects can degrade total PC performance. }
+ protected
+ FResolution: Integer;
+ FPeriodic: Boolean;
+ procedure SetEnabled(const Value: Boolean); virtual;
+ public
+ destructor Destroy; virtual;
+ {* }
+ property Resolution: Integer read FResolution write FResolution;
+ {* Minimum timer resolution. The less the more accuracy (0 is exactly
+ Interval milliseconds between timer shots). It is recommended to set
+ this property greater to prevent entire system from reducing overhead.
+ If you change this value, reset and then set Enabled again to apply
+ changes. }
+ property Periodic: Boolean read FPeriodic write FPeriodic;
+ {* TRUE, if timer is periodic (default). Otherwise, timer is one-shot
+ (set it Enabled every time in such case for each shot). If you change
+ this property, reset and set Enabled property again to get effect. }
+ end;
+
+function NewMMTimer( Interval: Integer ): PMMTimer;
+{* Creates multimedia timer object. Initially, it has Resolution = 0,
+ Periodic = TRUE and Enabled = FALSE. Do not forget also to assign your
+ event handler to OnTimer to do something on timer shot. }
+{$ENDIF WIN}
+
+{$IFDEF LIN}
+function NewMMTimer( Interval: Integer ): PTimer;
+{$ENDIF LIN}
+
+{$IFDEF WIN_GDI} //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+{ -- TTrayIcon object -- }
+
+type
+ TOnTrayIconMouse = procedure( Sender: PObj; Message : Word ) of object;
+ {* Event type to be called when Applet receives a message from an icon,
+ added to the taskbar tray. }
+
+ PTrayIcon = ^TTrayIcon;
+{ ----------------------------------------------------------------------
+ TTrayIcon - icon in tray area of taskbar
+----------------------------------------------------------------------- }
+ TTrayIcon = object(TObj)
+ {* Object to place (and change) a single icon onto taskbar tray. }
+ protected
+ FIcon: HIcon;
+ FActive: Boolean;
+ FTooltip: KOLString;
+ FOnMouse: TOnTrayIconMouse;
+ FControl: PControl;
+ fAutoRecreate: Boolean;
+ FNoAutoDeactivate: Boolean;
+ FWnd: HWnd;
+ procedure SetIcon(const Value: HIcon);
+ procedure SetActive(const Value: Boolean);
+ procedure SetTrayIcon( const Value : DWORD );
+ procedure SetTooltip(const Value: KOLString);
+ procedure SetAutoRecreate(const Value: Boolean);
+ protected
+ destructor Destroy; virtual;
+ {* Destructor. Use Free method instead (as usual). }
+ public
+ property Icon : HIcon read FIcon write SetIcon;
+ {* Icon to be shown on taskbar tray. If not set, value of Active
+ property has no effect. It is also possible to assign a value
+ to Icon property after assigning True to Active to install
+ icon first time or to replace icon with another one (e.g. to
+ get animation effect).
+ |<br>&nbsp;&nbsp;&nbsp;
+ Previously allocated icon (if any) is not deleted using
+ DeleteObject. This is normal for icons, loaded from resource
+ (e.g., by LoadIcon API call). But if icon was created (e.g.) by
+ CreateIconIndirect, your code is responsible for destroying
+ of it). }
+ property Active : Boolean read FActive write SetActive;
+ {* Set it to True to show assigned Icon on taskbar tray. Default
+ is False. Has no effect if Icon property is not assigned.
+ TrayIcon is deactivated automatically when Applet is finishing
+ (but only if Applet window is used as a "parent" for tray
+ icon object). }
+ property Tooltip : KOLString read FTooltip write SetTooltip;
+ {* Tooltip string, showing automatically when mouse is moving
+ over installed icon. Though "huge string" type is used, only
+ first 63 characters are considered. Also note, that only in
+ most recent versions of Windows multiline tooltips are supported. }
+ property OnMouse : TOnTrayIconMouse read FOnMouse write FOnMouse;
+ {* Is called then mouse message is taking place concerning installed
+ icon. Only type of message can be obtained (e.g. WM_MOUSEMOVE,
+ WM_LBUTTONDOWN etc.) }
+ property AutoRecreate: Boolean read fAutoRecreate write SetAutoRecreate;
+ {* If set to TRUE, auto-recreating of tray icon is proveded in case,
+ when Explorer is restarted for some (unpredictable) reasons. Otherwise,
+ your tray icon is disappeared forever, and if this is the single way
+ to communicate with your application, the user nomore can achieve it. }
+ property NoAutoDeactivate: Boolean read FNoAutoDeactivate write FNoAutoDeactivate;
+ {* If set to true, tray icon is not removed from tray automatically on
+ WM_CLOSE message receive by owner control. Set Active := FALSE in
+ your code for such case before accepting closing the form. }
+ property Wnd: HWnd read FWnd write FWnd;
+ {* A window to use as a base window for tray icon messages. Overrides
+ parent Control handle is assigned. Note, that if Wnd property used,
+ message handling is not done automatically, and you should do this in
+ your code, or at least for one tray icon object, call AttachProc2Wnd. }
+ procedure AttachProc2Wnd;
+ {* Call this method for a tray icon object in case if Wnd used rather then
+ control. It is enough to call this method once for each Wnd used, even
+ if several other tray icons are also based on the same Wnd. See also
+ DetachProc2Wnd method. }
+ procedure DetachProc2Wnd;
+ {* Call this method to detach window procedure attached via AttachProc2Wnd.
+ Do it once for a Wnd, used as a base to handle tray icon messages.
+ Caution! If you do not call this method before destroying Wnd, the
+ application will not functioning normally. }
+ end;
+ {* When You create invisible application, which should be represented by
+ only the tray icon, prepare a handle for the window, resposible for
+ messages handling. Remember, that window handle is created automatically
+ only when a window is showing first time. If window's property Visible is
+ set to False, You should to call CreateWindow manually.
+ <br>
+ There is a known bug exist with similar invisible tray-iconized applications.
+ When a menu is activated in response to tray mouse event, if there was
+ not active window, belonging to the application, the menu is not disappeared
+ when mouse is clicked anywhere else. This bug is occure in Windows9x/ME.
+ To avoid it, activate first your form window. This last window shoud have
+ status visible (but, certainly, there are no needs to place it on visible
+ part of screen - change its position, so it will not be visible for user,
+ if You wish).
+ <br>
+ Also, to make your application "invisible" but until special event is occure,
+ use Applet separate from the main form, and make for both Visible := False.
+ This allows for You to make your form visible any time You wish, and without
+ making application button visible if You do not wish.
+ }
+ {= Êîãäà Âû äåëàåòå íåâèäèìîå ïðèëîæåíèå, êîòîðîå äîëæíî áûòü ïðåäñòàâëåíî
+ òîëüêî èêîíêîé â òðåå, îáåñïå÷üòå íåíóëåâîé Handle äëÿ îêíà, îòâå÷àþùåãî
+ çà îáðàáîòêó ñîîáùåíèé. Ïîìíèòå, ÷òî Handle îêíà ñîçäàåòñÿ àâòîìàòè÷åñêè
+ òîëüêî â òîò ìîìåíò, êîãäà îíî äîëæíî ïîÿâèòüñÿ â ïåðâûé ðàç. Åñëè ñâîéñòâî
+ îêíà Visible óñòàíîâëåíî â FALSE, íåîáõîäèìî âûçâàòü CreateWindow ñàìîñòîÿòåëüíî.
+ <br>
+ Ñóùåñòâóåò èçâåñòíûé BUG ñ ïîäîáíûìè íåâèäèìûìè ìèíèìèçèðîâàííûìè â òðåé
+ ïðèëîæåíèÿìè. Êîãäà â îòâåò íà ñîáûòèå ìûøè àêòèâèçèðâàíî âûïàäàþùåå ìåíþ,
+ îíî íå èñ÷åçàåò ïî ùåë÷êó ìûøè âíå ýòîãî ìåíþ. Ïðîèñõîäèò ýòî â Windows9x/ME.
+ ÷òîáû ðåøèòü ýòó ïðîáëåìó, ñíà÷àëà àêòèâèçèðóéòå ñâîå îêíî (ôîðìó). Ýòî îêíî
+ äîëæíî áûòü âèäèìûì (íî, êîíå÷íî, åãî ìîæíî ðàçìåñòèòü âíå ïðåäåëîâ âèäèìîé
+ ÷àñòè ýêðàíà, òàê ÷òî ïîëüçîâàòåëþ åãî âèäíî íå áóäåò).
+ <br>
+ Òàê æå, ÷òîáû ñäåëàòü ïðèëîæåíèå íåâèäèìûì, ïî êðàéíåé ìåðå, ïîêà ýòî íå
+ ïîòðåáóåòñÿ, èñïîëüçóéòå îòäåëüíûé ïðåäñòàâèòåëü êëàññà TControl - ãëîáàëüíóþ
+ ïåðåìåííóþ Applet, è ïðèñâîéòå FALSE åå ñâîéñòâó Visible.
+ }
+
+function NewTrayIcon( Wnd: PControl; Icon: HIcon ): PTrayIcon;
+{* Constructor of TTrayIcon object. Pass main form or applet as Wnd
+ parameter. }
+
+{ -- JustOne -- }
+
+type
+ TOnAnotherInstance = procedure( const CmdLine: KOLString ) of object;
+ {* Event type to use in JustOneNotify function. }
+
+function JustOne( Wnd: PControl; const Identifier : KOLString ) : Boolean;
+{* Returns True, if this is a first instance. For all other instances
+ (application is already running), False is returned. }
+
+function JustOneNotify( Wnd: PControl; const Identifier : KOLString;
+ const aOnAnotherInstance: TOnAnotherInstance ) : Boolean;
+{* Returns True, if this is a first instance. For all other instances
+ (application is already running), False is returned. If handler
+ aOnAnotherInstance passed, it is called (in first instance) every time
+ when another instance of an application is started, receiving command
+ line used to run it. }
+
+{ -- string (mainly) utility procedures and functions. -- }
+
+{$IFDEF GDI}
+function MsgBox( const S: KOLString; Flags: DWORD ): DWORD;
+{* Displays message box with the same title as Applet.Caption. If applet
+ is not running, and Applet global variable is not assigned, caption
+ 'Error' is displayed (but actually this is not an error - the system
+ does so, if nil is passed as a title).
+ |<br>&nbsp;&nbsp;&nbsp;
+ Returns ID_... result (correspondently to flags passed (MB_OK, MBYESNO,
+ etc. -> ID_OK, ID_YES, ID_NO, etc.) }
+procedure MsgOK( const S: KOLString );
+{* Displays message box with the same title as Applet.Caption (or 'Error',
+ if Applet is not running). }
+function ShowMsg( const S: KOLString; Flags: DWORD ): DWORD;
+{* Displays message box like MsgBox, but uses Applet.Handle as a parent
+ (so the message has no button on a task bar). }
+procedure ShowMessage( const S: KOLString );
+{* Like ShowMsg, but has only styles MB_OK and MB_SETFOREGROUND. }
+{$ENDIF GDI}
+{$IFDEF WIN}
+{$IFNDEF PAS_ONLY}
+procedure SpeakerBeep( Freq: Word; Duration: DWORD );
+{* On Windows NT, calls Windows.Beep. On Windows 9x, produces beep on speaker
+ of desired frequency during given duration time (in milliseconds). }
+{$ENDIF PAS_ONLY}
+{$ENDIF WIN}
+
+function SysErrorMessage(ErrorCode: Integer): KOLString;
+{* Creates and returns a string containing formatted system error message.
+ It is possible then to display this message or write it to a log
+ file, e.g.:
+ ! ShowMsg( SysErrorMessage( GetLastError ) );
+
+ |&R=<a name="%0"></a><font color=#FF8040><h1>%0</h1></font>
+ <R 64-bit integer numbers>
+}
+{$ENDIF WIN_GDI}
+type
+ I64 = record
+ {* 64 bit integer record. Use it and correspondent functions below in KOL
+ projects to avoid dependancy from Delphi version (earlier versions of
+ Delphi had no Int64 type). }
+ Lo, Hi: DWORD;
+ end;
+ PI64 = ^I64;
+ {* }
+
+{$IFNDEF _D4orHigher}
+ Int64 = I64;
+ PInt64 = PI64;
+{$ENDIF}
+
+function MakeInt64( Lo, Hi: DWORD ): I64;
+{* }
+{$IFNDEF PAS_ONLY}
+function Int2Int64( X: Integer ): I64;
+{* }
+procedure IncInt64( var I64: I64; Delta: Integer );
+{* I64 := I64 + Delta; }
+procedure DecInt64( var I64: I64; Delta: Integer );
+{* I64 := I64 - Delta; }
+function Add64( const X, Y: I64 ): I64;
+{* Result := X + Y; }
+function Sub64( const X, Y: I64 ): I64;
+{* Result := X - Y; }
+function Neg64( const X: I64 ): I64;
+{* Result := -X; }
+function Mul64i( const X: I64; Mul: Integer ): I64;
+{* Result := X * Mul; }
+function Div64i( const X: I64; D: Integer ): I64;
+{* Result := X div D; }
+function Mod64i( const X: I64; D: Integer ): Integer;
+{* Result := X mod D; }
+function Sgn64( const X: I64 ): Integer;
+{* Result := sign( X ); i.e.:
+ |<br>
+ if X < 0 then -1
+ |<br>
+ if X = 0 then 0
+ |<br>
+ if X > 0 then 1 }
+function Cmp64( const X, Y: I64 ): Integer;
+{* Result := sign( X - Y ); i.e.
+ |<br>
+ if X < Y then -1
+ |<br>
+ if X = Y then 0
+ |<br>
+ if X > Y then 1 }
+function Int64_2Str( X: I64 ): AnsiString;
+{* }
+function Int64_2Hex( X: I64; MinDigits: Integer ): KOLString;
+{* }
+function Str2Int64( const S: AnsiString ): I64;
+{* }
+function Int64_2Double( const X: I64 ): Double;
+{* }
+function Double2Int64( D: Double ): I64;
+{$ENDIF PAS_ONLY}
+{*
+
+ <R Floating point numbers>
+}
+
+const
+ NAN = 0.0 / 0.0;
+ Infinity = 1.0 / 0.0;
+
+function IsNan(const AValue: Double): Boolean;
+{* Checks if an argument passed is NAN. }
+function IsInfinity(const AValue: Double): Boolean;
+{* Checks if an argument passed is Infinite. }
+function IntPower(Base: Extended; Exponent: Integer): Extended;
+{* Result := Base ^ Exponent; }
+function NextPowerOf2( n: DWORD ): DWORD;
+{* 0->1, 1->1, 2->2, 3->4, 4->4, 5->8, ... }
+function Str2Double( const S: KOLString ): Double;
+{* }
+function Str2Extended( const S: KOLString ): Extended;
+{* }
+function Double2Str( D: Double ): KOLString;
+{* }
+function Extended2Str( E: Extended ): KOLString;
+{* }
+function Extended2StrDigits( D: Double; n: Integer ): KOLString;
+{* Converts floating point number to string, leaving exactly n digits
+ following floating point. }
+function Double2StrEx( D: Double ): KOLString;
+{* experimental, do not use }
+{$IFNDEF PAS_ONLY}
+function TruncD( D: Double ): Double;
+{$ENDIF}
+{* Result := trunc( D ) as Double;
+|<hr>
+
+ <R Small bit arrays (max 32 bits in array)>
+ See also TBits object.
+}
+
+function IfThenElseBool( t, e, Cond: Boolean ): Boolean;
+function IfThenElseInt( t, e: Integer; Cond: Boolean ): Integer;
+function IfThenElseStr( const t, e: AnsiString; Cond: Boolean ): AnsiString;
+{$IFDEF _D5orHigher}
+function IfThenElse( t, e: Boolean; Cond: Boolean ): Boolean; overload;
+function IfThenElse( t, e: Integer; Cond: Boolean ): Integer; overload;
+function IfThenElse( t, e: AnsiString; Cond: Boolean ): AnsiString; overload;
+function IfThenElse( t, e: Double; Cond: Boolean ): Double; overload;
+{$ENDIF}
+
+function GetBits( N: DWORD; first, last: Byte ): DWord;
+{* Retuns bits straing from <first> and to <last> inclusively. }
+function GetBitsL( N: DWORD; from, len: Byte ): DWord;
+{* Retuns len bits starting from index <from>.
+|<hr>
+
+ <R Arithmetics, geometry and other utility functions>
+
+ See also units KolMath.pas, CplxMath.pas and Err.pas.
+}
+//[MulDiv DECLARATION]
+{$IFNDEF FPC}
+function MulDiv( A, B, C: Integer ): Integer;
+{* Returns A * B div C. Small and fast. }
+{$ENDIF}
+
+ function MakeRect( Left, Top, Right, Bottom: Integer ): TRect; stdcall;
+ {* Use it instead of VCL Rect function }
+ function RectsEqual( const R1, R2: TRect ): Boolean;
+ {* Returns True if rectangles R1 and R2 have the same bounds }
+ function RectsIntersected( const R1, R2: TRect ): Boolean;
+ {* Returns TRUE if rectangles R1 and R2 have at least one common point.
+ Note, that right and bottom bounds of rectangles are not their part,
+ so, if such points are lying on that bounds, FALSE is returned. }
+ function PointInRect( const P: TPoint; const R: TRect ): Boolean;
+ {* Returns True if point P is located in rectangle R (including
+ left and top bounds but without right and bottom bounds of the
+ rectangle). }
+ function OffsetPoint( const T: TPoint; dX, dY: Integer ): TPoint;
+ {* }
+ function OffsetSmallPoint( const T: TSmallPoint; dX, dY: SmallInt ): TSmallPoint;
+ {* }
+ function Point2SmallPoint( const T: TPoint ): TSmallPoint;
+ {* }
+ function SmallPoint2Point( const T: TSmallPoint ): TPoint;
+ {* }
+ function MakePoint( X, Y: Integer ): TPoint;
+ {* Use instead of VCL function Point }
+ function MakeSmallPoint( X, Y: Integer ): TSmallPoint;
+ {* Use to construct TSmallPoint }
+ function MakeFlags( FlgSet: PDWORD; FlgArray: array of Integer): Integer;
+ {* }
+ function MakeDateTimeRange( D1, D2: TDateTime ): TDateTimeRange;
+ {* Returns TDateTimeRange from two TDateTime bounds. }
+ procedure Swap( var X, Y: Integer );
+ {* exchanging values }
+ function Min( X, Y: Integer ): Integer;
+ {* minimum of two integers }
+ function Max( X, Y: Integer ): Integer;
+ {* maximum of two integers }
+{$IFDEF REDEFINE_ABS}
+ function Abs( X: Integer ): Integer;
+ {* absolute value }
+{$ENDIF}
+ function Sgn( X: Integer ): Integer;
+ {* sign of X: if X < 0, -1 is returned, if > 0, then +1, otherwise 0. }
+ function iSqrt( X: Integer ): Integer;
+ {* square root }
+ function iCbrt( X: DWORD ): Integer;
+ {* cubic root
+ |<hr>
+ <R String to number and number to string conversions>
+}
+function Int2Hex( Value : DWord; Digits : Integer ) : KOLString;
+{* Converts integer Value into string with hex number. Digits parameter
+ determines minimal number of digits (will be completed by adding
+ necessary number of leading zeroes). }
+function Int2Str( Value : Integer ) : KOLString;
+{* Obvious. }
+procedure Int2PChar( s: PAnsiChar; Value: Integer );
+{* Converts Value to string and puts it into buffer s. Buffer must have
+ enough size to store the number converted: buffer overflow does
+ not checked anyway! }
+function UInt2Str( Value: DWORD ): AnsiString;
+{* The same as Int2Str, but for unsigned integer value. }
+function Int2StrEx( Value, MinWidth: Integer ): KOLString;
+{* Like Int2Str, but resulting string filled with leading spaces to provide
+ at least MinWidth characters. }
+function Int2Rome( Value: Integer ): KOLString;
+{* Represents number 1..8999 to Rome numer. }
+function Int2Ths( I: Integer ): KOLString;
+{* Converts integer into string, separating every three digits from each
+ other by character ThsSeparator. (Convert to thousands). You }
+function Int2Digs( Value, Digits: Integer ): KOLString;
+{* Converts integer to string, inserting necessary number of leading zeroes
+ to provide desired length of string, given by Digits parameter. If
+ resulting string is greater then Digits, string is not truncated anyway. }
+function Num2Bytes( Value : Double ) : KOLString;
+{* Converts double float to string, considering it as a bytes count.
+ If Value is sufficiently large, number is represented in kilobytes (with
+ following letter K), or in megabytes (M), gigabytes (G) or terabytes (T).
+ Resulting string number is truncated to two decimals (.XX) or to one (.X),
+ if the second is 0. }
+function S2Int( S: PKOLChar ): Integer;
+{* Converts null-terminated string to Integer. Scanning stopped when any
+ non-digit character found. Even empty string or string not containing
+ valid integer number silently converted to 0. }
+function Str2Int(const Value : KOLString) : Integer;
+{* Converts string to integer. First character, which can not be
+ recognized as a part of number, regards as a separator. Even
+ empty string or string without number silently converted to 0. }
+function Hex2Int( const Value : KOLString) : Integer;
+{* Converts hexadecimal number to integer. Scanning is stopped
+ when first non-hexadicimal character is found. Leading dollar ('$')
+ character is skept (if present). Minus ('-') is not concerning as
+ a sign of number and also stops scanning.}
+function cHex2Int( const Value : KOLString) : Integer;
+{* As Hex2Int, but also checks for leading '0x' and skips it. }
+function Octal2Int( const Value: AnsiString ) : Integer;
+{* Converts octal number to integer. Scanning is stopped on first
+ non-octal digit (any char except 0..7). There are no checking if
+ there octal numer in the parameter. If the first char is not octal
+ digit, 0 is returned. }
+function Binary2Int( const Value: AnsiString ) : Integer;
+{* Converts binary number to integer. Like Octal2Int, but only digits
+ 0 and 1 are allowed. }
+type Radix_int = {$IFDEF _D5orHigher} Int64 {$ELSE} Integer {$ENDIF};
+function ToRadix( number: Radix_int; radix, min_digits: Integer ): KOLString;
+{* Converts unsigned number to string representing it literally in a numeric
+ base given by radix parameter. }
+function FromRadixStr( var Rslt: Radix_int; s: PKOLChar; radix: Integer ): PKOLChar;
+{* Converts unsigned number from string representation in a numeric base given by
+ a radix parameter. Returns a pointer to a character next to the last digit of
+ the number. }
+function FromRadix( const s: AnsiString; radix: Integer ): Radix_int;
+{* Converts unsigned number from string representation in a numeric base given by
+ a radix parameter. See also: FromRadixStr function. }
+function InsertSeparators( const s: KOLString; chars_between: Integer;
+ Separator: KOLChar ): KOLString;
+{* Inserts given Separator between symbols in s, separating each portion of
+ chars_between characters with a Separator starting from right side. See also:
+ Int2Ths function. }
+{$IFDEF WIN}
+{$IFNDEF _FPC}
+//{$IFNDEF PAS_ONLY}
+function Format( const fmt: KOLString; params: array of const ): KOLString;
+//{$ENDIF}
+{* Uses API call to wvsprintf, so does not understand extra formats,
+ such as floating point, date/time, currency conversions. See list of
+ available formats in win32.hlp (topic wsprintf).
+|<hr>
+
+ <R Working with null-terminated and ansi strings>
+}
+{$ENDIF _FPC}
+{$ENDIF WIN}
+function StrComp(const Str1, Str2: PAnsiChar): Integer;
+{* Compares two strings fast. -1: Str1<Str2; 0: Str1=Str2; +1: Str1>Str2 }
+
+{$IFDEF PAS_ONLY}
+function StrComp_NoCase(const Str1, Str2: PAnsiChar): Integer;
+function StrLComp_NoCase(const Str1, Str2: PAnsiChar; MaxLen: Cardinal): Integer;
+{$ELSE}
+{$IFDEF SMALLER_CODE}
+function StrComp_NoCase(const Str1, Str2: PAnsiChar): Integer;
+{* Compares two strings fast without case sensitivity.
+ Returns: -1 when Str1<Str2; 0 when Str1=Str2; +1 when Str1>Str2 }
+function StrLComp_NoCase(const Str1, Str2: PAnsiChar; MaxLen: Cardinal): Integer;
+{* Compare two strings fast without case sensitivity.
+ Terminating 0 is not considered, so if strings are equal,
+ comparing is continued up to MaxLen bytes.
+ Since this, pass minimum of lengths as MaxLen. }
+{$ELSE}
+function StrComp_NoCase1(const Str1, Str2: PAnsiChar): Integer;
+var StrComp_NoCase: function(const Str1, Str2: PAnsiChar): Integer = StrComp_NoCase1;
+{* Compares two strings fast without case sensitivity.
+ Returns: -1 when Str1<Str2; 0 when Str1=Str2; +1 when Str1>Str2 }
+function StrLComp_NoCase1(const Str1, Str2: PAnsiChar; MaxLen: Cardinal): Integer;
+var StrLComp_NoCase: function(const Str1, Str2: PAnsiChar; MaxLen: Cardinal): Integer = StrLComp_NoCase1;
+{$ENDIF}
+{$ENDIF}
+
+function StrLComp(const Str1, Str2: PAnsiChar; MaxLen: Cardinal): Integer;
+{* Compare two strings (fast). Terminating 0 is not considered, so if
+ strings are equal, comparing is continued up to MaxLen bytes.
+ Since this, pass minimum of lengths as MaxLen. }
+
+function StrCopy( Dest, Source: PAnsiChar ): PAnsiChar;
+{* Copy source string to destination (fast). Pointer to Dest is returned. }
+function StrCat( Dest, Source: PAnsiChar ): PAnsiChar;
+{* Append source string to destination (fast). Pointer to Dest is returned. }
+function StrLen(const Str: PAnsiChar): Cardinal;
+{* StrLen returns the number of characters in Str, not counting the null
+ terminator. }
+function StrScanLen(Str: PAnsiChar; Chr: AnsiChar; Len: Integer): PAnsiChar;
+{* Fast scans string Str of length Len searching character Chr.
+ Pointer to a character next to found or to Str[Len] (if no one found)
+ is returned. }
+function StrScan(Str: PAnsiChar; Chr: AnsiChar): PAnsiChar;
+{* Fast search of given character in a string. Pointer to found character
+ (or nil) is returned. }
+function StrRScan(Str: PAnsiChar; Chr: AnsiChar): PAnsiChar;
+{* StrRScan returns a pointer to the last occurrence of Chr in Str. If Chr
+ does not occur in Str, StrRScan returns NIL. The null terminator is
+ considered to be part of the string. }
+function StrIsStartingFrom( Str, Pattern: PKOLChar ): Boolean;
+{* Returns True, if string Str is starting from Pattern, i.e. if
+ Copy( Str, 1, StrLen( Pattern ) ) = Pattern. Str must not be nil! }
+function StrIsStartingFromNoCase( Str, Pattern: PAnsiChar ): Boolean;
+{* Like StrIsStartingFrom above, but without case sensitivity. }
+function TrimLeft(const S: KOLString): KOLString;
+{* Removes spaces, tabulations and control characters from the starting
+ of string S. }
+function TrimRight(const S: KOLString): KOLString;
+{* Removes spaces, tabulates and other control characters from the
+ end of string S. }
+function Trim( const S : KOLString): KOLString;
+{* Makes TrimLeft and TrimRight for given string. }
+function RemoveSpaces( const S: KOLString ): KOLString;
+{* Removes all characters less or equal to ' ' in S and returns it. }
+procedure Str2LowerCase( S: PAnsiChar );
+{* Converts null-terminated string to lowercase (inplace). }
+function LowerCase(const S: Ansistring): Ansistring;
+{* Obvious. }
+function UpperCase(const S: Ansistring): Ansistring;
+{* Obvious. }
+function AnsiUpperCase(const S: Ansistring): Ansistring;
+{* Obvious. }
+function AnsiLowerCase(const S: Ansistring): Ansistring;
+{* Obvious. }
+function KOLUpperCase(const S: KOLString): KOLString;
+{* Obvious. }
+function KOLLowerCase(const S: KOLString): KOLString;
+{* Obvious. }
+{$IFDEF _D3orHigher}
+function WUpperCase(const S: KOLWideString): KOLWideString;
+{* Obvious. }
+function WLowerCase(const S: KOLWideString): KOLWideString;
+{* Obvious. }
+{$ENDIF}
+{$IFNDEF _D2}
+{$IFNDEF _FPC}
+function WAnsiUpperCase(const S: KOLWideString): KOLWideString;
+{* Obvious. }
+function WAnsiLowerCase(const S: KOLWideString): KOLWideString;
+{* Obvious. }
+function WStrComp(const S1, S2: KOLWideString): Integer;
+{* }
+function _WStrComp(S1, S2: PWideChar): Integer;
+{* }
+function _WStrLComp(S1, S2: PWideChar; Len: Integer): Integer;
+{* }
+function WStrScan(Str: PWideChar; Chr: WideChar): PWideChar;
+{* Fast search of given character in a string. Pointer to found character
+ (or nil) is returned. }
+function WStrRScan(const Str: PWideChar; Chr: WideChar): PWideChar;
+{* StrRScan returns a pointer to the last occurrence of Chr in Str. If Chr
+ does not occur in Str, StrRScan returns NIL. The null terminator is
+ considered to be part of the string. }
+{$ENDIF _FPC}
+{$ENDIF _D2}
+//--- set of functions to work either with AnsiString or with KOLWideString
+// depending on UNICODE_CTRLS symbol ----------------------------------------
+function AnsiCompareStr(const S1, S2: KOLString): Integer;
+{* AnsiCompareStr compares S1 to S2, with case-sensitivity. The compare
+ operation is controlled by the current Windows locale. The return value
+ is the same as for CompareStr. }
+function _AnsiCompareStr(S1, S2: PKOLChar): Integer;
+{* The same, but for PChar ANSI strings }
+function AnsiCompareStrNoCase(const S1, S2: KOLString): Integer;
+{* AnsiCompareStrNoCase compares S1 to S2, without case-sensitivity. The compare
+ operation is controlled by the current Windows locale. The return value
+ is the same as for CompareStr. }
+function _AnsiCompareStrNoCase(S1, S2: PKOLChar): Integer;
+{* The same, but for PChar ANSI strings }
+function AnsiCompareText( const S1, S2: KOLString ): Integer;
+{* }
+function AnsiEq( const S1, S2 : KOLString ) : Boolean;
+{* Returns True, if AnsiLowerCase(S1) = AnsiLowerCase(S2). I.e., if ANSI
+ stringsare equal to each other without caring of characters case
+ sensitivity. }
+
+//--- set of functions to work always with AnsiString
+// even if UNICODE_CTRLS symbol is defined ----------------------------------
+function AnsiCompareStrA(const S1, S2: AnsiString): Integer;
+{* AnsiCompareStr compares S1 to S2, with case-sensitivity. The compare
+ operation is controlled by the current Windows locale. The return value
+ is the same as for CompareStr. }
+function _AnsiCompareStrA_Slow(S1, S2: PAnsiChar): Integer;
+function _AnsiCompareStrA_Fast(S1, S2: PAnsiChar): Integer;
+var _AnsiCompareStrA: function(S1, S2: PAnsiChar): Integer =
+ {$IFDEF SPEED_FASTER} _AnsiCompareStrA_Fast
+ {$ELSE} _AnsiCompareStrA_Slow {$ENDIF};
+{* The same, but for PChar ANSI strings }
+function _AnsiCompareStrA_Fast2(S1, S2: PAnsiChar): Integer;
+function _AnsiCompareStrNoCaseA_Fast2(S1, S2: PAnsiChar): Integer;
+function AnsiCompareStrNoCaseA(const S1, S2: AnsiString): Integer;
+{* AnsiCompareStr compares S1 to S2, with case-sensitivity. The compare
+ operation is controlled by the current Windows locale. The return value
+ is the same as for CompareStr. }
+function _AnsiCompareStrNoCaseA_Slow(S1, S2: PAnsiChar): Integer;
+function _AnsiCompareStrNoCaseA_Fast(S1, S2: PAnsiChar): Integer;
+var _AnsiCompareStrNoCaseA: function(S1, S2: PAnsiChar): Integer =
+ {$IFDEF SPEED_FASTER} _AnsiCompareStrNoCaseA_Fast
+ {$ELSE} _AnsiCompareStrNoCaseA_Slow {$ENDIF};
+{* The same, but for PChar ANSI strings }
+function AnsiCompareTextA( const S1, S2: AnsiString ): Integer;
+{* }
+
+{$IFDEF WIN}
+{$IFNDEF _FPC}
+function LStrFromPWCharLen(Source: PWideChar; Length: Integer): AnsiString;
+{* from Delphi5 - because D2 does not contain it. }
+function LStrFromPWChar(Source: PWideChar): AnsiString;
+{* from Delphi5 - because D2 does not contain it. }
+{$ENDIF _FPC}
+function WCharIn( C: KOLChar; const Chars: array of KOLChar ): Boolean;
+{$ENDIF WIN}
+
+function CopyEnd( const S : KOLString; Idx : Integer ) : KOLString;
+{* Returns copy of source string S starting from Idx up to the end of
+ string S. Works correctly for case, when Idx > Length( S ) (returns
+ empty string for such case). }
+function CopyTail( const S : KOLString; Len : Integer ) : KOLString;
+{* Returns last Len characters of the source string. If Len > Length( S ),
+ entire string S is returned. }
+procedure DeleteTail( var S : KOLString; Len : Integer );
+{* Deletes last Len characters from string. }
+function IndexOfChar( const S : KOLString; Chr : KOLChar ) : Integer;
+{* Returns index of given character (1..Length(S)), or
+ -1 if a character not found. }
+function IndexOfCharsMin( const S, Chars : KOLString ) : Integer;
+{* Returns index (in string S) of those character, what is taking place
+ in Chars string and located nearest to start of S. If no such
+ characters in string S found, -1 is returned. }
+{$IFDEF _D3orHigher}
+function WIndexOfChar( const S : KOLWideString; Chr : WideChar ) : Integer;
+function WIndexOfCharsMin( const S, Chars : KOLWideString ) : Integer;
+{$ENDIF}
+{$IFNDEF _D2}
+{$IFNDEF _FPC}
+function IndexOfWideCharsMin( const S, Chars : KOLWideString ) : Integer;
+{* Returns index (in wide string S) of those wide character, what
+ is taking place in Chars wide string and located nearest to start of S.
+ If no such characters in string S found, -1 is returned. }
+{$ENDIF _FPC}
+{$ENDIF _D2}
+
+function IndexOfStr( const S, Sub : KOLString ) : Integer;
+{* Returns index of given substring in source string S. If found,
+ 1..Length(S)-Length(Sub), if not found, -1. }
+function Parse( var S : KOLString; const Separators : KOLString ) : KOLString;
+{* Returns first characters of string S, separated from others by
+ one of characters, taking place in Separators string, assigning
+ a tail of string (after found separator) to source string. If
+ no separator characters found, source string S is returned, and
+ source string itself becomes empty. }
+{$IFDEF _D3orHigher}
+function ParseW( var S : KOLWideString; const Separators : KOLWideString ) : KOLWideString;
+{$ENDIF}
+
+{$IFNDEF _FPC}
+{$IFNDEF _D2}
+function WParse( var S : KOLWideString; const Separators : KOLWideString ) : KOLWideString;
+{* Returns first wide characters of wide string S, separated from others
+ by one of wide characters, taking place in Separators wide string,
+ assigning a tail of wide string (following found separator) to the
+ source one. If there are no separator characters found, source wide
+ string S is returned, and source wide string itself becomes empty. }
+{$ENDIF _D2}
+{$ENDIF _FPC}
+function ParsePascalString( var S : KOLString; const Separators : KOLString ) : KOLString;
+{* Returns first characters of string S, separated from others by
+ one of characters, taking place in Separators string, assigning
+ a tail of string (after the found separator) to source string. If
+ there are no separator characters found, the source string S is returned,
+ and the source string itself becomes empty. Additionally: if the first (after
+ a blank space) is the quote "'" or '#', pascal string is assumung first
+ and is converted to usual string (without quotas) before analizing
+ of other separators. }
+function String2PascalStrExpr( const S : KOLString ) : KOLString;
+{* Converts string to Pascal-like string expression (concatenation of
+ strings with quotas and characters with leading '#'). }
+function StrEq( const S1, S2 : AnsiString ) : Boolean;
+{* Returns True, if LowerCase(S1) = LowerCase(S2). I.e., if strings
+ are equal to each other without caring of characters case sensitivity
+ (ASCII only). }
+{$IFNDEF _D2}
+{$IFNDEF _FPC}
+function WAnsiEq( const S1, S2 : KOLWideString ) : Boolean;
+{* Returns True, if AnsiLowerCase(S1) = AnsiLowerCase(S2). I.e., if ANSI
+ stringsare equal to each other without caring of characters case
+ sensitivity. }
+{$ENDIF _FPC}
+{$ENDIF _D2}
+
+function StrIn( const S : AnsiString; const A : array of AnsiString ) : Boolean;
+{* Returns True, if S is "equal" to one of strings, taking place
+ in A array. To check equality, StrEq function is used, i.e.
+ comaprison is taking place without case sensitivity. }
+{$IFNDEF _FPC}
+type TSetOfChar = Set of AnsiChar;
+{$IFNDEF _D2}
+function WStrIn( const S : KOLWideString; const A : array of KOLWideString ) : Boolean;
+{* Returns True, if S is "equal" to one of strings, taking place
+ in A array. To check equality, WAnsiEq function is used, i.e.
+ comaprison is taking place without case sensitivity. }
+function CharIn( C: KOLChar; const A: TSetOfChar ): Boolean;
+{* To replace expressions like S[1] in [ '0'..'z' ] to CharIn( S[ 1 ], [ '0'..'z' ] )
+ (and to avoid problems with Unicode version of code). }
+{$ENDIF _D2}
+{$ENDIF _FPC}
+function StrIs( const S : AnsiString; const A : Array of AnsiString; var Idx: Integer ) : Boolean;
+{* Returns True, if S is "equal" to one of strings, taking place
+ in A array, and in such Case Idx also is assigned to an index of A element
+ equal to S. To check equality, StrEq function is used, i.e.
+ comaprison is taking place without case sensitivity. }
+function IntIn( Value: Integer; const List: array of Integer ): Boolean;
+{* Returns TRUE, if Value is found in a List. }
+function _StrSatisfy( S, Mask : PKOLChar ) : Boolean;
+{* }
+function _2StrSatisfy( S, Mask: PKOLChar ): Boolean;
+{* }
+function StrSatisfy( const S, Mask : KOLString ) : Boolean;
+{* Returns True, if S is satisfying to a given Mask (which can contain
+ wildcard symbols '*' and '?' interpeted correspondently as 'any
+ set of characters' and 'single any character'. If there are no
+ such wildcard symbols in a Mask, result is True only if S is maching
+ to Mask string.) }
+function StrReplace( var S: KOLString; const From, ReplTo: KOLString ): Boolean;
+{* Replaces first occurance of From to ReplTo in S, returns True,
+ if pattern From was found and replaced. }
+function KOLStrReplace( var S: KOLString; const From, ReplTo: KOLString ): Boolean;
+{* Replaces first occurance of From to ReplTo in S, returns True,
+ if pattern From was found and replaced. }
+{$IFNDEF _FPC}
+{$IFNDEF _D2}
+function WStrReplace( var S: KOLWideString; const From, ReplTo: KOLWideString ): Boolean;
+{* Replaces first occurance of From to ReplTo in S, returns True,
+ if pattern From was found and replaced. See also function StrReplace.
+ This function is not available in Delphi2 (this version of Delphi
+ does not support KOLWideString type). }
+{$ENDIF _D2}
+{$ENDIF _FPC}
+
+function StrRepeat( const S: KOLString; Count: Integer ): KOLString;
+{* Repeats given string Count times. E.g., StrRepeat( 'A', 5 ) gives 'AAAAA'. }
+{$IFNDEF _FPC}
+{$IFNDEF _D2}
+function WStrRepeat( const S: KOLWideString; Count: Integer ): KOLWideString;
+{* Repeats given wide string Count times. E.g., StrRepeat( 'A', 5 ) gives 'AAAAA'. }
+{$ENDIF _D2}
+{$ENDIF _FPC}
+
+procedure NormalizeUnixText( var S: AnsiString );
+{* In the string S, replaces all occurances of character #10 (without leading #13)
+ to the character #13. }
+procedure Koi8ToAnsi( s: PAnsiChar );
+{* Converts Koi8 text to Ansi (in place) }
+const KOI8_Rus: array[ #$C0..#$FF ] of AnsiChar = (
+ { 'þ',
+ 'à', 'á', 'ö', 'ä', 'å', 'ô', 'ã', 'õ', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï',
+ 'ÿ', 'ð', 'ñ', 'ò', 'ó', 'æ', 'â', 'ü', 'û', 'ç', 'ø', 'ý', 'ù', '÷', 'ú',
+ 'Þ',
+ 'À', 'Á', 'Ö', 'Ä', 'Å', 'Ô', 'Ã', 'Õ', 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï',
+ 'ß', 'Ð', 'Ñ', 'Ò', 'Ó', 'Æ', 'Â', 'Ü', 'Û', 'Ç', 'Ø', 'Ý', 'Ù', '×', 'Ú'}
+ #$FE,
+ #$E0, #$E1, #$F6, #$E4, #$E5, #$F4, #$E3, #$F5, #$E8, #$E9, #$EA, #$EB, #$EC, #$ED, #$EE, #$EF,
+ #$FF, #$F0, #$F1, #$F2, #$F3, #$E6, #$E2, #$FC, #$FB, #$E7, #$F8, #$FD, #$F9, #$F7, #$FA,
+ #$DE,
+ #$C0, #$C1, #$D6, #$C4, #$C5, #$D4, #$C3, #$D5, #$C8, #$C9, #$CA, #$CB, #$CC, #$CD, #$CE, #$CF,
+ #$DF, #$D0, #$D1, #$D2, #$D3, #$C6, #$C2, #$DC, #$DB, #$C7, #$D8, #$DD, #$D9, #$D7, #$DA
+ );
+
+function StrPCopy(Dest: PAnsiChar; const Source: Ansistring): PAnsiChar;
+{* Copyes string into null-terminated. }
+function StrLCopy(Dest: PAnsiChar; const Source: PAnsiChar; MaxLen: Cardinal): PAnsiChar;
+{* Copyes first MaxLen characters of the Source string into null-terminated Dest. }
+
+function DelimiterLast( const Str, Delimiters: KOLString ): Integer;
+{* Returns index of the last of delimiters given by same named parameter
+ among characters of Str. If there are no delimiters found, length of
+ Str is returned. This function is intended mainly to use in filename
+ parsing functions. }
+function __DelimiterLast( Str, Delimiters: PKOLChar ): PKOLChar;
+{* Returns address of the last of delimiters given by Delimiters parameter
+ among characters of Str. If there are no delimeters found, position of
+ the null terminator in Str is returned. This function is intended
+ mainly to use in filename parsing functions. }
+{$IFDEF _D3orHigher}
+function W__DelimiterLast( Str, Delimiters: PWideChar ): PWideChar;
+{* }
+{$ENDIF _D3orHigher}
+function SkipSpaces( P: PKOLChar ): PKOLChar;
+{* Skips all characters #1..' ' in a string.
+}
+{$IFDEF F_P}
+function DummyStrFun( const S: AnsiString ): AnsiString;
+{$ENDIF}
+
+function CompareMem(P1, P2: Pointer; Length: Integer): Boolean;
+{* Fast compare of two memory blocks. }
+function AllocMem( Size : Integer ) : Pointer;
+{* Allocates global memory and unlocks it. }
+procedure DisposeMem( var Addr : Pointer );
+{* Locks global memory block given by pointer, and frees it.
+ Does nothing, if the pointer is nil.
+ |<hr>
+
+ <R Text in clipboard operations>
+}
+{$IFDEF WIN_GDI}
+
+function ClipboardHasText: Boolean;
+{* Returns true, if the clipboard contain text to paste from. }
+function Clipboard2Text: AnsiString;
+{* If clipboard contains text, this function returns it for You. }
+{$IFNDEF _FPC}
+{$IFNDEF _D2}
+function Clipboard2WText: KOLWideString;
+{* If clipboard contains text, this function returns it for You (as Unicode string). }
+{$ENDIF _D2}
+{$ENDIF _FPC}
+function Text2Clipboard( const S: AnsiString ): Boolean;
+{* Puts given string to a clipboard. }
+{$IFNDEF _FPC}
+{$IFNDEF _D2}
+function WText2Clipboard( const WS: KOLWideString ): Boolean;
+{* Puts given Unicode string to a clipboard.
+|<hr>
+}
+{$ENDIF _D2}
+{$ENDIF _FPC}
+
+var SearchMnemonics: function ( const S: KOLString ): KOLString
+ = {$IFDEF F_P} DummyStrFun {$ELSE}
+ {$IFDEF UNICODE_CTRLS} WAnsiUpperCase {$ELSE} AnsiUpperCase {$ENDIF} {$ENDIF};
+ MnemonicsLocale: Integer;
+
+procedure SupportAnsiMnemonics( LocaleID: Integer );
+{* Provides encoding to work with given locale. Call this global function to
+ extend TControl.SupportMnemonics capability (also should be called for a form
+ or for Applet variable).
+
+ <R Date and time handling>
+}
+{$ENDIF WIN_GDI}
+{$IFDEF WIN_GDI}
+
+{$IFnDEF _D5orHigher}
+ {$DEFINE DATE0_0001}
+{$ENDIF _D5orHigher}
+{$IFnDEF DATE0_0001}
+ {$DEFINE DATE0_1601}
+{$ENDIF} //Starting from the version 3.1415926, (so called PI-version), datetime
+ //can be correctly handled (by default) from 1-Jan-1601 to 1-Jan-38827.
+ //This made it possible to use short calls to API functions to convert date and time.
+ //If you still want to count time correctly from 1-Jan-1 B.C., or a compatibility
+ //is required for old applications, define symbol DATE0_0001 in your
+ //project options. Actually this does not mean that TDateTime forma changed,
+ //but only restrictions are in converting date to TSystemTime from TDateTime
+ //and vice versa.
+type
+ //TDateTime = Double; // well, it is already defined so in System.pas
+ {* Basic date and time type. Integer part represents year and days (as is,
+ i.e. 1-Jan-2000 is representing by value 730141, which is a number of
+ days from 1-Jan-0001 to 1-Jan-2000 inclusively). Fractional part is
+ representing hours, minutes, seconds and milliseconds of a day
+ proportionally (like in VCL TDateTime type, e.g. 0.5 = 12:00, 0.25 = 6:00,
+ etc.). }
+
+ PDayTable = ^TDayTable;
+ TDayTable = array[1..12] of Byte;
+
+ TDateFormat = ( dfShortDate, dfLongDate );
+ {* Date formats available to use in formatting date/time to string. }
+ TTimeFormatFlag = ( tffNoMinutes, tffNoSeconds, tffNoMarker, tffForce24 );
+ {* Additional flags, used for formatting time. }
+ TTimeFormatFlags = Set of TTimeFormatFlag;
+ {* Set of flags, used for formatting time. }
+
+const
+ MonthDays: array [Boolean] of TDayTable =
+ ((31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31),
+ (31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31));
+ {* The MonthDays array can be used to quickly find the number of
+ days in a month: MonthDays[IsLeapYear(Y), M]. }
+
+ SecsPerDay = 24 * 60 * 60;
+ {* Seconds per day. }
+ MSecsPerDay = SecsPerDay * 1000;
+ {* Milliseconds per day. }
+
+ Date1601 = 584389;
+ VCLDate0 = 693594;
+ {* Value to convert VCL "date 0" to KOL "date 0" and back.
+ This value corresponds to 30-Dec-1899, 0:00:00. So,
+ to convert VCL date to KOL date, just subtract this
+ value from VCL date. And to convert back from KOL date
+ to VCL date, add this value to KOL date.}
+
+function Now : TDateTime;
+{* Returns local date and time on running PC. }
+function Date: TDateTime;
+{* Returns todaylocal date. }
+procedure DecodeDateFully( DateTime: TDateTime; var Year, Month, Day, DayOfWeek: WORD );
+{* Decodes date. }
+procedure DecodeDate( DateTime: TDateTime; var Year, Month, Day: WORD );
+{* Decodes date. }
+function EncodeDate( Year, Month, Day: WORD; var DateTime: TDateTime ): Boolean;
+{* Encodes date. }
+function CompareSystemTime(const D1, D2 : TSystemTime) : Integer;
+{* Compares to TSystemTime records. Returns -1, 0, or 1 if, correspondantly,
+ D1 < D2, D1 = D2 and D1 > D2. }
+procedure IncDays( var SystemTime : TSystemTime; DaysNum : Integer );
+{* Increases/decreases day in TSystemTime record onto given days count
+ (can be negative). }
+procedure IncMonths( var SystemTime : TSystemTime; MonthsNum : Integer );
+{* Increases/decreases month number in TSystemTime record onto given
+ months count (can be negative). Correct result is not garantee if
+ day number is incorrect for newly obtained month. }
+function IsLeapYear(Year: Integer): Boolean;
+{* Returns True, if given year is "leap" (i.e. has 29 days in the February). }
+function DayOfWeek(Date: TDateTime): Integer;
+{* Returns day of week (0..6) for given date. }
+function SystemTime2DateTime(const SystemTime : TSystemTime; var DateTime : TDateTime ) : Boolean;
+{* Converts TSystemTime record to XDateTime variable. }
+function DateTime2SystemTime(const DateTime : TDateTime; var SystemTime : TSystemTime ) : Boolean;
+{* Converts TDateTime variable to TSystemTime record. }
+function DateTime_System2Local( DTSys: TDateTime ): TDateTime;
+{* Converts DTSys representing system time (+0 Grinvich) to local time. }
+function DateTime_Local2System( DTLoc: TDateTime ): TDateTime;
+{* Converts DTLoc representing local time to system time (+0 Grinvich) }
+function FileTime2DateTime( const ft: TFileTime; var DT: TDateTime ): Boolean;
+{* }
+function DateTime2FileTime( DT: TDateTime; var ft: TFileTime ): Boolean;
+{* }
+
+procedure DivMod(Dividend: Integer; Divisor: Word; var Result, Remainder: Word);
+{* Dividing of integer onto divisor with obtaining both result of division
+ and remainder. }
+
+function SystemDate2Str( const SystemTime : TSystemTime; const LocaleID : DWORD;
+ const DfltDateFormat : TDateFormat;
+ const DateFormat : PKOLChar ) : KOLString;
+{* Formats date, stored in TSystemTime record into string, using given locale
+ and date/time formatting flags. (E.g.: GetUserDefaultLangID). }
+function SystemTime2Str( const SystemTime : TSystemTime; const LocaleID : DWORD;
+ const Flags : TTimeFormatFlags;
+ const TimeFormat : PKOLChar ) : KOLString;
+{* Formats time, stored in TSystemTime record into string, using given locale
+ and date/time formatting flags. }
+
+function Date2StrFmt( const Fmt: KOLString; D: TDateTime ): KOLString;
+{* Represents date as a string correspondently to Fmt formatting string.
+ See possible pictures in definition of the function Str2DateTimeFmt
+ (the first part). If Fmt string is empty, default system date format
+ for short date string used. }
+function Time2StrFmt( const Fmt: KOLString; D: TDateTime ): KOLString;
+{* Represents time as a string correspondently to Fmt formatting string.
+ See possible pictures in definition of the function Str2DateTimeFmt
+ (the second part). If Fmt string is empty, default system time format
+ for short date string used. }
+function DateTime2StrShort( D: TDateTime ): KOLString;
+{* Formats date and time to string in short date format using current user
+ locale. }
+function Str2DateTimeFmt( const sFmtStr, sS: KOLString ): TDateTime;
+{* Restores date or/and time from string correspondently to a format string.
+ Date and time formatting string can contain following pictures (case
+ sensitive):
+ |<pre>
+ DATE PICTURES
+ d Day of the month as digits without leading zeros for single digit days.
+ dd Day of the month as digits with leading zeros for single digit days
+ ddd Day of the week as a 3-letter abbreviation as specified by a
+ LOCALE_SABBREVDAYNAME value.
+ dddd Day of the week as specified by a LOCALE_SDAYNAME value.
+ M Month as digits without leading zeros for single digit months.
+ MM Month as digits with leading zeros for single digit months
+ MMM Month as a three letter abbreviation as specified by a LOCALE_SABBREVMONTHNAME value.
+ MMMM Month as specified by a LOCALE_SMONTHNAME value.
+ y Year represented only be the last digit.
+ yy Year represented only be the last two digits.
+ yyyy Year represented by the full 4 digits.
+ gg Period/era string as specified by the CAL_SERASTRING value. The gg
+ format picture in a date string is ignored if there is no associated era
+ string. In Enlish locales, usual values are BC or AD.
+
+ TIME PICTURES
+ h Hours without leading zeros for single-digit hours (12-hour clock).
+ hh Hours with leading zeros for single-digit hours (12-hour clock).
+ H Hours without leading zeros for single-digit hours (24-hour clock).
+ HH Hours with leading zeros for single-digit hours (24-hour clock).
+ m Minutes without leading zeros for single-digit minutes.
+ mm Minutes with leading zeros for single-digit minutes.
+ s Seconds without leading zeros for single-digit seconds.
+ ss Seconds with leading zeros for single-digit seconds.
+ t One character–time marker string (usually P or A, in English locales).
+ tt Multicharacter–time marker string (usually PM or AM, in English locales).
+ |</pre>
+ E.g., 'D, yyyy/MM/dd h:mm:ss'.
+ See also Str2DateTimeShort function.
+ }
+function Str2TimeFmt(const sFmtStr, sS: KOLString): TDateTime;
+{* Same as above but for time only }
+function Str2DateTimeShort( const S: KOLString ): TDateTime;
+{* Restores date and time from string correspondently to current user locale. }
+function Str2DateTimeShortEx( const S: KOLString ): TDateTime;
+{* Like Str2DateTimeShort above, but uses locale defined date and time
+ separators to avoid recognizing time as a date in some cases.}
+function Str2TimeShort(const S: KOLString): TDateTime;
+{* Like Str2DateTimeShort but for time only.
+|<hr>
+
+ <R File and directory routines>
+}
+{$ENDIF WIN_GDI}
+
+const
+ ofOpenRead = {$IFDEF LIN} O_RDONLY {$ELSE} $80000000 {$ENDIF};
+ {* Use this flag (in combination with others) to open file for "read" only. }
+ ofOpenWrite = {$IFDEF LIN} O_WRONLY {$ELSE} $40000000 {$ENDIF};
+ {* Use this flag (in combination with others) to open file for "write" only. }
+ ofOpenReadWrite = {$IFDEF LIN} O_RDWR {$ELSE} $C0000000 {$ENDIF};
+ {* Use this flag (in combination with others) to open file for "read" and "write". }
+
+ ofShareExclusive = {$IFDEF LIN} $10 {$ELSE} $00 {$ENDIF};
+ {* Use this flag (in combination with others) to open file for exclusive use. }
+ ofShareDenyWrite = {$IFDEF LIN} $20 {$ELSE} $01 {$ENDIF};
+ {* Use this flag (in combination with others) to open file in share mode, when
+ only attempts to open it in other process for "write" will be impossible.
+ I.e., other processes could open this file simultaneously for read only
+ access. }
+ ofShareDenyRead = {$IFDEF LIN} 0 {not supported} {$ELSE} $02 {$ENDIF};
+ {* Use this flag (in combination with others) to open file in share mode, when
+ only attempts to open it for "read" in other processes will be disabled.
+ I.e., other processes could open it for "write" only access. }
+ ofShareDenyNone = {$IFDEF LIN} $30 {$ELSE} $03 {$ENDIF};
+ {* Use this flag (in combination with others) to open file in full sharing mode.
+ I.e. any process will be able open this file using the same share flag. }
+ ofCreateNew = {$IFDEF LIN} O_CREAT or O_TRUNC {$ELSE} $100 {$ENDIF};
+ {* Default creation disposition. Use this flag for creating new file (usually
+ for write access. }
+ ofCreateAlways = {$IFDEF LIN} O_CREAT {$ELSE} $200 {$ENDIF};
+ {* Use this flag (in combination with others) to open existing or creating new
+ file. If existing file is opened, it is truncated to size 0. }
+ ofOpenExisting = {$IFDEF LIN} 0 {$ELSE} $300 {$ENDIF};
+ {* Use this flag (in combination with others) to open existing file only. }
+ ofOpenAlways = {$IFDEF LIN} O_CREAT {$ELSE} $400 {$ENDIF};
+ {* Use this flag (in combination with others) to open existing or create new
+ (if such file is not yet exists). }
+ ofTruncateExisting = {$IFDEF LIN} O_TRUNC {$ELSE} $500 {$ENDIF};
+ {* Use this flag (in combination with others) to open existing file and truncate
+ it to size 0. }
+
+ ofAttrReadOnly = {$IFDEF LIN} 0 {$ELSE} $10000 {$ENDIF};
+ {* Use this flag to create Read-Only file (?). }
+ ofAttrHidden = {$IFDEF LIN} 0 {$ELSE} $20000 {$ENDIF};
+ {* Use this flag to create hidden file. }
+ ofAttrSystem = {$IFDEF LIN} 0 {$ELSE} $40000 {$ENDIF};
+ {* Use this flag to create system file. }
+ ofAttrTemp = {$IFDEF LIN} 0 {$ELSE} $1000000 {$ENDIF};
+ {* Use this flag to create temp file. }
+ ofAttrArchive = {$IFDEF LIN} 0 {$ELSE} $200000 {$ENDIF};
+ {* Use this flag to create archive file. }
+ ofAttrCompressed = {$IFDEF LIN} 0 {$ELSE} $8000000 {$ENDIF};
+ {* Use this flag to create compressed file. Has effect only on NTFS, and
+ only if ofAttrCompressed is not specified also. }
+ ofAttrOffline = {$IFDEF LIN} 0 {$ELSE} $10000000 {$ENDIF};
+ {* Use this flag to create offline file. }
+
+{$IFDEF _D3orHigher}
+function WFileCreate(const FileName: KOLWideString; OpenFlags: DWord): THandle;
+{* }
+{$ENDIF}
+function FileCreate(const FileName: KOLString; OpenFlags: DWord): THandle;
+{* Call this function to open existing or create new file. OpenFlags
+ parameter can be a combination of up to three flags (by one from
+ each group:
+ |<table border=0>
+ |&L=<tr><td valign=top>%0</td><td valign=top>
+ |&E=</td></tr>
+ <L ofOpenRead, ofOpenWrite, ofOpenReadWrite> - 1st group. Here You decide
+ wish You open file for read, write or read-and-write operations; <E>
+ <L ofShareExclusive, ofShareDenyWrite, ofShareDenyRead, ofShareDenyNone> -2nd
+ group - sharing. Here You can mark out sharing mode, which is used to
+ open file. <E>
+ <L ofCreateNew, ofCreateAlways, ofOpenExisting, ofOpenAlways, ofTruncateExisting>
+ - 3rd group - creation disposition. Here You determine, either to create new
+ or open existing file and if to truncate existing or not.
+ |</table> }
+function FileClose(Handle: THandle): Boolean;
+{* Call it to close opened earlier file. }
+function FileExists( const FileName: KOLString ) : Boolean;
+{* Returns True, if given file exists.
+ |<br>Note (by Dod):
+ It is not documented in a help for GetFileAttributes, but it seems that
+ under NT-based Windows systems, FALSE is always returned for files
+ opened for excluseve use like pagefile.sys. }
+{$IFDEF _D3orHigher}
+function WFileExists( const FileName: KOLWideString ) : Boolean;
+{* Returns True, if given file exists.
+ |<br>Note (by Dod):
+ It is not documented in a help for GetFileAttributes, but it seems that
+ under NT-based Windows systems, FALSE is always returned for files
+ opened for excluseve use like pagefile.sys. }
+{$ENDIF}
+function FileSeek(Handle: THandle; {$IFNDEF STREAM_COMPAT} const {$ENDIF} MoveTo: TStrmMove; MoveMethod: TMoveMethod): TStrmSize;
+{* Changes current position in file. }
+function FileRead(Handle: THandle; var Buffer; Count: DWord): DWord;
+{* Reads bytes from current position in file to buffer. Returns number of
+ read bytes. }
+{$IFDEF LIN}
+function GetFileSize( Handle: THandle; HiSize: PDWORD ): DWORD;
+{$ENDIF LIN}
+function File2Str(Handle: THandle): AnsiString;
+{* Reads file from current position to the end and returns result as ansi string. }
+{$IFNDEF _D2}
+function File2WStr(Handle: THandle): KOLWideString;
+{* Reads UNICODE file from current position to the end and returns result as
+ unicode string. }
+{$ENDIF}
+function FileWrite(Handle: THandle; const Buffer; Count: DWord): DWord;
+{* Writes bytes from buffer to file from current position, extending its
+ size if needed. }
+function FileEOF( Handle: THandle ) : Boolean;
+{* Returns True, if EOF is achieved during read operations or last byte is
+ overwritten or append made to extend file during last write operation. }
+function FileFullPath( const FileName : KOLString ) : KOLString;
+{* Returns full path name for given file. Validness of source FileName path
+ is not checked at all. }
+{$IFDEF WIN} //--------------- these functions have not sense in Linux: --------
+function FileShortPath( const FileName: KOLString ): KOLString;
+{* Returns short path to the file or directory. }
+function FileIconSystemIdx( const Path: KOLString ): Integer;
+{* Returns index of the index of the system icon correspondent to the file or
+ directory in system icon image list. }
+function FileIconSysIdxOffline( const Path: KOLString ): Integer;
+{* The same as FileIconSystemIdx, but an icon is calculated for the file
+ as it were offline (it is possible to get an icon for file even if
+ it is not existing, on base of its extension only). }
+function DirIconSysIdxOffline( const Path: KOLString ): Integer;
+{* The same as FileIconSysIdxOffline, but for a folder rather then for a file. }
+{$ENDIF WIN} //-----------------------------------------------------------------
+procedure LogFileOutput( const filepath, str: KOLString );
+{* Debug function. Use it to append given string to the end of the given file. }
+
+function Str2File( Filename: PKOLChar; Str: PAnsiChar ): Boolean;
+{* Save null-terminated string to file directly. If file does not exists, it is
+ created. If it exists, it is overriden. If operation failed, FALSE is returned. }
+function WStr2File( Filename: PKOLChar; Str: PWideChar ): Boolean;
+{* Save null-terminated wide string to file directly. If file does not exists, it is
+ created. If it exists, it is overriden. If operation failed, FALSE is returned. }
+function StrSaveToFile( const Filename: KOLString; const Str: AnsiString ): Boolean;
+{* Saves a string to a file without any changes. If file does not exists, it is
+ created. If it exists, it is overriden. If operation failed, FALSE is returned. }
+function StrLoadFromFile( const Filename: KOLString ): AnsiString;
+{* Reads entire file and returns its content as a string. If operation failed,
+ an empty strinng is returned.
+ |<br>by Sergey Shishmintzev: it is possible to pass Filename = 'CON' to
+ read input from redirected console output. }
+{$IFNDEF _D2}
+function WStrSaveToFile( const Filename: KOLString; const Str: KOLWideString ): Boolean;
+{* Saves a string to a file without any changes. If file does not exists, it is
+ created. If it exists, it is overriden. If operation failed, FALSE is returned. }
+function WStrLoadFromFile( const Filename: KOLString ): KOLWideString;
+{* Reads entire file and returns its content as a string. If operation failed,
+ an empty strinng is returned.
+ |<br>by Sergey Shishmintzev: it is possible to pass Filename = 'CON' to
+ read input from redirected console output. }
+{$ENDIF}
+
+function Mem2File( Filename: PKOLChar; Mem: Pointer; Len: Integer ): Integer;
+{* Saves memory block to a file (if file exists it is overriden, created new if
+ not exists). }
+function File2Mem( Filename: PKOLChar; Mem: Pointer; MaxLen: Integer ): Integer;
+{* Loads file content to memory. }
+
+{$IFDEF WIN}
+type
+ PFindFileData = ^TFindFileData;
+ TFindFileData = packed record
+ // from TWin32FindData: -------------
+ dwFileAttributes: DWORD;
+ ftCreationTime: TFileTime;
+ ftLastAccessTime: TFileTime;
+ ftLastWriteTime: TFileTime;
+ nFileSizeHigh: DWORD;
+ nFileSizeLow: DWORD;
+ dwReserved0: DWORD;
+ dwReserved1: DWORD;
+ cFileName: Array[0..MAX_PATH - 1] of KOLChar;
+ cAlternateFileName: Array[0..13] of KOLChar;
+ //-------- + handle:
+ FindHandle: THandle;
+ end;
+{$ENDIF WIN}
+function Find_First( const FilePathName: KOLString; var F: TFindFileData ): Boolean;
+function Find_Next( var F: TFindFileData ): Boolean;
+procedure Find_Close( var F: TFindFileData );
+{$IFDEF _D2orD3}
+function FileSize( const Path: KOLString ) : Integer;
+{$ELSE}
+function FileSize( const Path: KOLString ) : Int64;
+{$ENDIF}
+{* Returns file size in bytes without opening it. If file too large
+ to represent its size as Integer, -1 is returned. }
+procedure FileTime( const Path: KOLString;
+ CreateTime, LastAccessTime, LastModifyTime: PFileTime ); stdcall;
+{* Returns file times without opening it. }
+function GetUniqueFilename( PathName: KOLString ) : KOLString;
+{* If file given by PathName exists, modifies it to create unique
+ filename in target folder and returns it. Modification is performed
+ by incrementing last number in name (if name part of file does not
+ represent a number, such number is generated and concatenated to
+ it). E.g., if file aaa.aaa is already exist, the function checks
+ names aaa1.aaa, aaa2.aaa, ..., aaa10.aaa, etc. For name abc123.ext,
+ names abc124.ext, abc125.ext, etc. will be checked. }
+function FileTimeCompare( const FT1, FT2 : TFileTime ) : Integer;
+{* Compares time of file (createing, writing, accessing. Returns
+ -1, 0, 1 if correspondantly FT1<FT2, FT1=FT2, FT1>FT2. }
+function DirectoryExists(const Name: KOLString): Boolean;
+{* Returns True if given directory (folder) exists. }
+function DiskPresent( const DrivePath: KOLString ): Boolean;
+{* Returns TRUE if the disk is present }
+{$IFDEF _D3orHigher}
+function WDirectoryExists(const Name: KOLWideString): Boolean;
+{* }
+{$ENDIF}
+function CheckDirectoryContent( const Name: KOLString; SubDirsOnly: Boolean;
+ const Mask: KOLString ): Boolean;
+{* Returns TRUE if directory does not contain files (or directories only)
+ satisfying given mask. }
+function DirectoryEmpty(const Name: KOLString): Boolean;
+{* Returns True if given directory is not exists or empty. }
+function DirectoryHasSubdirs( const Path: KOLString ): Boolean;
+{* Returns TRUE if given directory exists and has subdirectories. }
+function GetStartDir: KOLString;
+{* Returns path to directory where executable is located (regardless
+ of current directory). }
+function ExePath: KOLString;
+{* Returns the path to the exe-file (in case of dll hook, this is exe-file
+ of the process in which context dll hook function is called). }
+function ModulePath: KOLString;
+{* Returns the path to the module (exe, dll) itself. }
+
+
+
+//---------------------------------------------------------
+// Following functions/procedures are created by Edward Aretino:
+// IncludeTrailingPathDelimiter, ExcludeTrailingPathDelimiter,
+// ForceDirectories, CreateDir, ChangeFileExt
+//---------------------------------------------------------
+function ExcludeTrailingChar( const S: KOLString; C: KOLChar ): KOLString;
+{* If S is finished with character C, it is excluded. }
+function IncludeTrailingChar( const S: KOLString; C: KOLChar ): KOLString;
+{* If S is not finished with character C, it is added. }
+function IncludeTrailingPathDelimiter(const S: KOLString): KOLString;
+{* by Edward Aretino. Adds '\' to the end if it is not present. }
+function ExcludeTrailingPathDelimiter(const S: KOLString): KOLString;
+{* by Edward Aretino. Removes '\' at the end if it is present. }
+
+function ExtractFileDrive( const Path: KOLString ) : KOLString;
+{* Returns only drive part from exact path to a file or a directory.
+ For network paths, returns a computer name together with a following
+ name of shared directory (like '\\compname\shared\' ). }
+function ExtractFilePath( const Path: KOLString ) : KOLString;
+{* Returns only path part from exact path to file. }
+{$IFDEF _D3orHigher}
+function WExtractFilePath( const Path: KOLWideString ) : KOLWideString;
+{* Returns only path part from exact path to file. }
+{$ENDIF}
+function IsNetworkPath( const Path: KOLString ): Boolean;
+{* Returns TRUE, if Path is starting from '\\'. }
+function ExtractFileName( const Path: KOLString ) : KOLString;
+{* Extracts file name from exact path to file. }
+function ExtractFileNameWOext( const Path: KOLString ) : KOLString;
+{* Extracts file name from path to file or from filename. }
+function ExtractFileExt( const Path: KOLString ) : KOLString;
+{* Extracts extention from file name (returns it with dot '.' first) }
+function ReplaceExt( const Path, NewExt: KOLString ): KOLString;
+{* Returns Path to a file with extension replaced to a new extension.
+ Pass a new extension started with '.', e.g. '.txt'. }
+
+function ForceDirectories(Dir: KOLString): Boolean;
+{* by Edward Aretino. Creates given directory if not present. All needed
+ subdirectories are created if necessary. }
+function CreateDir(const Dir: KOLString): Boolean;
+{* by Edward Aretino. Creates given directory. }
+function ChangeFileExt(FileName: KOLString; const Extension: KOLString): KOLString;
+{* by Edward Aretino. Changes file extention. }
+function ReplaceFileExt( const Path, NewExt: KOLString ): KOLString;
+{* Returns a path with extension replaced to a given one. }
+{$IFDEF WIN} //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+function ExtractShortPathName( const Path: KOLString ): KOLString;
+{* }
+{$IFDEF GDI}
+function FilePathShortened( const Path: KOLString; MaxLen: Integer ): KOLString;
+{* Returns shortened file path to fit MaxLen characters. }
+function FilePathShortenPixels( const Path: KOLString; DC: HDC; MaxPixels: Integer ): KOLString;
+{* Returns shortened file path to fit MaxPixels for a given DC. If you pass
+ Canvas.Handle of any control or bitmap object, ensure that font is valid
+ for it (or call TCanvas.RequiredState( FontValid ) method before. If DC passed
+ = 0, call is equivalent to call FilePathShortened, and MaxPixels means in such
+ case maximum number of characters. }
+function MinimizeName( const Path: KOLString; DC: HDC; MaxPixels: Integer ): KOLString;
+{* Exactly the same as MinimizeName in FileCtrl.pas (VCL). }
+{$ENDIF GDI}
+
+function GetSystemDir: KOLString;
+{* Returns path to windows system directory. }
+function GetWindowsDir : KOLString;
+{* Returns path to Windows directory. }
+{$ENDIF WIN} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+function GetWorkDir : KOLString;
+{* Returns path to application's working directory. }
+function GetTempDir : KOLString;
+{* Returns path to default temp folder (directory to place temporary files). }
+function CreateTempFile( const DirPath, Prefix: KOLString ): KOLString;
+{* Returns path to just created temporary file. }
+function GetFileListStr(FPath{e.g.'c:\tmp\'}, FMask{e.g.'*.*'}: KOLString): KOLString;
+{* List of files in string, separating each path from others with a character stored
+ in FileOpSeparator variables (#13 by default).
+ E.g.: 'c:\tmp\unit1.dcu'#13'c:\tmp\unit1.~pa' (for use with DeleteFile2Recycle())}
+function DeleteFiles( const DirPath: KOLString ): Boolean;
+{* Deletes files by file mask (given with wildcards '*' and '?'). }
+{$IFDEF WIN_GDI} //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+var FileOpSeparator: KOLChar = {$IFDEF OLD_COMPAT}';'{$ELSE}#13{$ENDIF};
+function DoFileOp( const FromList, ToList: KOLString; FileOp: UINT; Flags: Word;
+ Title: PKOLChar): Boolean;
+{* By Unknown Mystic. FileOp can be: FO_MOVE, FO_COPY, FO_DELETE, FO_RENAME.
+ Flags can be a combination of values: FOF_MULTIDESTFILES, FOF_CONFIRMMOUSE,
+ FOF_SILENT, FOF_RENAMEONCOLLISION, FOF_NOCONFIRMATION, FOF_WANTMAPPINGHANDLE,
+ FOF_ALLOWUNDO, FOF_FILESONLY, FOF_SIMPLEPROGRESS, FOF_NOCONFIRMMKDIR,
+ FOF_NOERRORUI. Title used only with FOF_SIMPLEPROGRESS. }
+function DeleteFile2Recycle( const Filename : KOLString ) : Boolean;
+{* Deletes file to recycle bin. This operation can be very slow, when
+ called for a single file. To delete group of files at once (fast),
+ pass a list of paths to files to be deleted, separating each path
+ from others with a character stored in FileOpSeparator variable (by default #13,
+ but in case when OLD_COMPAT symbol added - ';'). E.g.: 'unit1.dcu'#13'unit1.~pa'
+ |<br>
+ FALSE is returned only in case when at least one file was not deleted
+ successfully.
+ |<br>
+ Note, that files are deleted not to recycle bin, if wildcards are
+ used or not fully qualified paths to files. }
+function CopyMoveFiles( const FromList, ToList: KOLString; Move: Boolean ): Boolean;
+{* }
+{$IFNDEF PAS_ONLY}
+function DiskFreeSpace( const Path: KOLString ): I64;
+{$ENDIF}
+{* Returns disk free space in bytes. Pass a path to root directory,
+ e.g. 'C:\'.
+ |<hr>
+
+ <R Wrappers to registry API functions>
+
+ These functions can be used independently to simplify access to Windows
+ registry. }
+{$ENDIF WIN_GDI} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+{$IFDEF WIN_GDI} //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+function RegKeyOpenRead( Key: HKey; const SubKey: KOLString ): HKey;
+{* Opens registry key for read operations (including enumerating of subkeys).
+ Pass either handle of opened earlier key or one of constans
+ HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS
+ as a first parameter. If not successful, 0 is returned. }
+function RegKeyOpenWrite( Key: HKey; const SubKey: KOLString ): HKey;
+{* Opens registry key for write operations (including adding new values or
+ subkeys), as well as for read operations too. See also RegKeyOpenRead. }
+function RegKeyOpenCreate( Key: HKey; const SubKey: KOLString ): HKey;
+{* Creates and opens key. }
+function RegKeyGetStr( Key: HKey; const ValueName: KOLString ): KOLString;
+{* Reads key, which must have type REG_SZ (null-terminated string). If
+ not successful, empty string is returned. This function as well as all
+ other registry manipulation functions, does nothing, if Key passed is 0
+ (without producing any error). }
+function RegKeyGetStrEx( Key: HKey; const ValueName: KOLString
+ {$IFDEF OPTIONAL_REG_EXPAND_SZ} ; ExpandEnvVars: Boolean {$ENDIF} ): KOLString;
+{* Like RegKeyGetStr, but accepts REG_EXPAND_SZ type, expanding all
+ environment variables in resulting string.
+ |<br>
+ Code provided by neuron, e-mailto:neuron@hollowtube.mine.nu }
+function RegKeyGetDw( Key: HKey; const ValueName: KOLString ): DWORD;
+{* Reads key value, which must have type REG_DWORD. If ValueName passed
+ is '' (empty string), unnamed (default) value is reading. If not
+ successful, 0 is returned. }
+function RegKeySetStr(Key: HKey; const ValueName: KOLString; const Value: KOLString ): Boolean;
+{* Writes new key value as null-terminated string (type REG_SZ). If not
+ successful, returns False. }
+function RegKeySetStrEx( Key: HKey; const ValueName: KOLString; const Value: KOLString;
+ expand: Boolean): Boolean;
+{* Writes new key value as REG_SZ or REG_EXPAND_SZ. - by neuron, e-mailto:neuron@hollowtube.mine.nu }
+function RegKeySetDw( Key: HKey; const ValueName: KOLString; Value: DWORD ): Boolean;
+{* Writes new key value as dword (with type REG_DWORD). Returns False,
+ if not successful. }
+procedure RegKeyClose( Key: HKey );
+{* Closes key, opened using RegKeyOpenRead or RegKeyOpenWrite. (But does
+ nothing, if Key passed is 0). }
+function RegKeyDelete( Key: HKey; const SubKey: KOLString ): Boolean;
+{* Deletes key. Does nothing if key passed is 0 (returns FALSE). }
+function RegKeyDeleteValue( Key: HKey; const SubKey: KOLString ): Boolean;
+{* Deletes value. - by neuron, e-mailto:neuron@hollowtube.mine.nu }
+function RegKeyExists( Key: HKey; const SubKey: KOLString ): Boolean;
+{* Returns TRUE, if given subkey exists under given Key. }
+function RegKeyValExists( Key: HKey; const ValueName: KOLString ): Boolean;
+{* Returns TRUE, if given value exists under the Key.
+}
+function RegKeyValueSize( Key: HKey; const ValueName: KOLString ): Integer;
+{* Returns a size of value. This is a size of buffer needed to store
+ registry key value. For string value, size returned is equal to a
+ length of string plus 1 for terminated null character. }
+function RegKeyGetBinary( Key: HKey; const ValueName: KOLString; var Buffer; Count: Integer ): Integer;
+{* Reads binary data from a registry, writing it to the Buffer.
+ It is supposed that size of Buffer provided is at least Count bytes.
+ Returned value is actul count of bytes read from the registry and written
+ to the Buffer.
+ |<br>
+ This function can be used to get data of any type from the registry, not
+ only REG_BINARY. }
+function RegKeySetBinary( Key: HKey; const ValueName: KOLString; const Buffer; Count: Integer ): Boolean;
+{* Stores binary data in the registry. }
+function RegKeyGetDateTime(Key: HKey; const ValueName: KOLString): TDateTime;
+{* Returns datetime variable stored in registry in binary format. }
+function RegKeySetDateTime(Key: HKey; const ValueName: KOLString; DateTime: TDateTime): Boolean;
+{* Stores DateTime variable in the registry. }
+
+//-------------------------------------------------------
+// registry functions by Valerian Luft <luft@valerian.de>
+//-------------------------------------------------------
+function RegKeyGetSubKeys( const Key: HKEY; List: PKOLStrList): Boolean;
+{* The function enumerates subkeys of the specified open registry key.
+ True is returned, if successful.
+}
+function RegKeyGetValueNames(const Key: HKEY; List: PKOLStrList): Boolean;
+{* The function enumerates value names of the specified open registry key.
+ True is returned, if successful.
+}
+function RegKeyGetValueTyp (const Key:HKEY; const ValueName: KOLString) : DWORD;
+{* The function receives the type of data stored in the specified value.
+ |<br>
+ If the function fails, the return value is the Key value.
+ |<br>
+ If the function succeeds, the return value return will be one of the following:
+ |<br>
+ REG_BINARY , REG_DWORD, REG_DWORD_LITTLE_ENDIAN,
+ REG_DWORD_BIG_ENDIAN, REG_EXPAND_SZ, REG_LINK , REG_MULTI_SZ,
+ REG_NONE, REG_RESOURCE_LIST, REG_SZ
+
+|<hr>
+
+ <R Data sorting (quicksort implementation)>
+ This part contains implementation of 'quick sort' algorithm,
+ based on following code:
+
+|<pre>
+| TQSort by Mike Junkin 10/19/95.
+| DoQSort routine adapted from Peter Szymiczek's QSort procedure which
+| was presented in issue#8 of The Unofficial Delphi Newsletter.
+
+| TQSort changed by Vladimir Kladov (Mr.Bonanzas) to allow 32-bit
+| sorting (of big arrays with more than 64K elements).
+|</pre>
+
+ Finally, this sort procedure is adapted to XCL (and then to KOL)
+ requirements (no references to SysUtils, Classes etc. TQSort object
+ is transferred to a single procedure call and DoQSort method is
+ renamed to SortData - which is a regular procedure now). }
+
+{$ENDIF WIN_GDI} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+procedure SortData( const Data: Pointer; const uNElem: Dword;
+ const CompareFun: TCompareEvent;
+ const SwapProc: TSwapEvent );
+{* Call it to sort any array of data of any kind, passing total
+ number of items in an array and two defined (regular) function
+ and procedure to perform custom compare and swap operations.
+ First procedure parameter is to pass it to callback function
+ CompareFun and procedure SwapProc. Items are enumerated from
+ 0 to uNElem-1. }
+
+{$IFDEF _D3orHigher}
+procedure SortArray( const Data: Pointer; const uNElem: Dword;
+ const CompareFun: TCompareArrayEvent );
+{* Like SortData, but faster and allows to sort only contigous arrays of
+ dwords (or integers or pointers occupying for 4 bytes for each item. }
+{$ENDIF}
+
+procedure SwapListItems( const L: Pointer; const e1, e2: DWORD );
+{* Use this function as the last parameter for SortData call when a PList
+ object is sorting. SwapListItems just exchanges two items of the list. }
+
+procedure SortIntegerArray( var A : array of Integer );
+{* procedure to sort array of integers. }
+
+procedure SortDwordArray( var A : array of DWORD );
+{* Procedure to sort array of unsigned 32-bit integers.
+|<hr>
+}
+{ ------------------- directory list object ---------------------------------- }
+
+type
+ TDirItemAction = ( diSkip, diAccept, diCancel );
+ TOnDirItem = procedure( Sender: PObj; var DirItem: TFindFileData; var Accept: TDirItemAction )
+ of object;
+ TSortDirRules = ( sdrNone, sdrFoldersFirst, sdrCaseSensitive, sdrByName, sdrByExt,
+ sdrBySize, sdrBySizeDescending, sdrByDateCreate, sdrByDateChanged,
+ sdrByDateAccessed, sdrInvertOrder );
+ {* List of rules (options) to sort directories. Rules are passed to Sort
+ method in an array, and first placed rules are applied first. }
+
+ PDirList = ^TDirList;
+{ ----------------------------------------------------------------------
+ TDirList - Directory scanning
+----------------------------------------------------------------------- }
+ TDirList = object( TObj )
+ {* Allows easy directory scanning. This is not visual object, but
+ storage to simplify working with directory content. }
+ protected
+ FListPositions : PList; //^^^^^^^^^^ Attention: order of FListPositions &
+ fStoreFiles: PStream; //__________ fStoreFiles is IMPORTANT!
+ FPath: KOLString;
+ fFilters: {$IFDEF UNICODE_CTRLS} PWStrList {$ELSE} PStrList {$ENDIF};
+ fOnItem: TOnDirItem;
+ function Get(Idx: Integer): PFindFileData;
+ function GetCount: Integer;
+ function GetNames(Idx: Integer): KOLString;
+ function GetIsDirectory(Idx: Integer): Boolean;
+ protected
+ function SatisfyFilter( FileName : PKOLChar; FileAttr, FindAttr : DWord ) : Boolean;
+ destructor Destroy; virtual;
+ {* Destructor. As usual, call Free method to destroy an object. }
+ public
+ property Items[ Idx : Integer ] : PFindfileData read Get; default;
+ {* Full access to scanned items (files and subdirectories). }
+ property IsDirectory[ Idx: Integer ]: Boolean read GetIsDirectory;
+ {* Returns TRUE, if specified item represents a directory, not a file. }
+ property Count : Integer read GetCount;
+ {* Number of items. }
+ property Names[ Idx : Integer ] : KOLString read GetNames;
+ {* Full long names of directory items. }
+ property Path : KOLString read FPath;
+ {* Path of scanned directory. }
+ procedure Clear;
+ {* Call it to clear list of files. }
+ procedure ScanDirectory( const DirPath, Filter : KOLString; Attr : DWord );
+ {* Call it to rescan directory or to scan another directory content
+ (method Clear is called first). Pass path to directory, file filter
+ and attributes to scan directory immediately.
+ |<br>&nbsp;&nbsp;&nbsp;
+ Note: Pass FILE_ATTRIBUTE_... constants or-combination as Attr
+ parameter. If 0 passed, both files and directories are listed. }
+ procedure ScanDirectoryEx( const DirPath, Filters : KOLString; Attr : DWord );
+ {* Call it to rescan directory or to scan another directory content
+ (method Clear is called first). Pass path to directory, file filter
+ and attributes to scan directory immediately.
+ |<br>&nbsp;&nbsp;&nbsp;
+ Note: Pass FILE_ATTRIBUTE_... constants or-combination as Attr
+ parameter. }
+ procedure Sort( Rules : array of TSortDirRules );
+ {* Sorts directory entries. If empty rules array passed, default rules
+ array DefSortDirRules is used. }
+ function FileList( const Separator {e.g.: ';', or #13}: KOLString;
+ Dirs, FullPaths: Boolean ): KOLString;
+ {* Returns a string containing all names separated with Separator.
+ If Dirs=FALSE, only files are returned. }
+ property OnItem: TOnDirItem read fOnItem write fOnItem;
+ {* This event is called on reading each item while scanning directory.
+ To use it, first create PDirList object with empty path to scan, then
+ assign OnItem event and call ScanDirectory with correct path. }
+ procedure DeleteItem( Idx: Integer );
+ {* Allows to delete an item from the directory list (not from the disk!) }
+ procedure AddItem( FindData: PFindFileData );
+ {* Allows to add arbitrary item to the list. }
+ procedure InsertItem( idx: Integer; FindData: PFindFileData );
+ {* Allows to add arbitrary item to the list. }
+ end;
+
+function NewDirList( const DirPath, Filter: KOLString; Attr: DWORD ): PDirList;
+{* Creates directory list object using easy one-string filter. If Attr = FILE_ATTRIBUTE_NORMAL,
+ only files are scanned without directories. If Attr = 0, both files and
+ directories are listed. }
+function NewDirListEx( const DirPath, Filters: KOLString; Attr: DWORD ): PDirList;
+{* Creates directory list object using several filters, separated by ';'.
+ Filters starting from '^' consider to be anti-filters, i.e. files,
+ satisfying to those masks, are skept during scanning. }
+const DefSortDirRules : array[ 0..3 ] of TSortDirRules = ( sdrFoldersFirst,
+ sdrByName, sdrBySize, sdrByDateCreate );
+{* Default rules to sort directory entries. }
+{$IFNDEF PAS_ONLY}
+function DirectorySize( const Path: KOLString ): I64;
+{* Returns directory size in bytes as large 64 bit integer. }
+{$ENDIF}
+
+{$IFDEF WIN_GDI} //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+type
+ TOpenSaveOption = ( OSCreatePrompt,
+ OSExtensionDiffent,
+ OSFileMustExist,
+ OSHideReadonly,
+ OSNoChangedir,
+ OSNoReferenceLinks,
+ OSAllowMultiSelect,
+ OSNoNetworkButton,
+ OSNoReadonlyReturn,
+ OSOverwritePrompt,
+ OSPathMustExist,
+ OSReadonly,
+ OSNoValidate
+ //{$IFDEF OpenSaveDialog_Extended}
+ ,
+ OSTemplate,
+ OSHook
+ //{$ENDIF}
+ );
+ TOpenSaveOptions = set of TOpenSaveOption;
+ {* Options available for TOpenSaveDialog. }
+
+ POpenSaveDialog = ^TOpenSaveDialog;
+{ ----------------------------------------------------------------------
+ TOpenSaveDialog
+----------------------------------------------------------------------- }
+ TOpenSaveDialog = object( TObj )
+ {* Object to show standard Open/Save dialog. Initially provided
+ for XCL by Carlo Kok. }
+ protected
+ FFilter : KOLString;
+ fFilterIndex : Integer;
+ fOpenDialog : Boolean;
+ FInitialDir : KOLString;
+ FDefExtension : KOLString;
+ FFilename : KOLString;
+ FTitle : KOLString;
+ FOptions : TOpenSaveOptions;
+ fWnd: THandle;
+ fOpenReadOnly: Boolean;
+ public
+ TemplateName: KOLString; // do not forget to add OpenSaveDialog_Extended
+ HookProc: Pointer; // to project options conditionals!
+ NoPlaceBar: Boolean; // TRUE, if place bar is disabled in the new style
+ // dialogs (if the symbol OpenSaveDialog_Extended is
+ // not added in project options, place bar is always
+ // enabled in Windows 2000 and higher).
+ destructor Destroy; virtual;
+ {* destructor }
+ Function Execute : Boolean;
+ {* Call it after creating to perform selecting of file by user. }
+ property Filename : KOLString read FFilename write FFileName;
+ {*
+ Filename is separated by #13 when multiselect is true and the first
+ file, is the path of the files selected.
+ |<pre>
+ | C:\Projects
+ | Test1.Dpr
+ | Test2.Dpr
+ |</pre>
+ If only one file is selected, it is provided as (e.g.)
+ C:\Projects\Test1.dpr
+ |<br> For case when OSAllowMultiselect option used, after each
+ call initial value for a Filename containing several files prevents
+ system from opening the dialog. To fix this, assign another initial
+ value to Filename property in your code, when you use multiselect.
+ }
+ property InitialDir : KOLString read FInitialDir write FInitialDir;
+ {* Initial directory path. If not set, current directory (usually
+ directory when program is started) is used. }
+ property Filter : KOLString read FFilter write FFilter;
+ {* A list of pairs of filter names and filter masks, separated with '|'.
+ If a mask contains more than one mask, it should be separated with ';'.
+ E.g.:
+ ! 'All files|*.*|Text files|*.txt;*.1st;*.diz' }
+ property FilterIndex : Integer read FFilterIndex write FFilterIndex;
+ {* Index of default filter mask (0 by default, which means "first"). }
+ property OpenDialog : Boolean read FOpenDialog write FOpenDialog;
+ {* True, if "Open" dialog. False, if "Save" dialog. True is default. }
+ property Title : KOLString read Ftitle write Ftitle;
+ {* Title for dialog. }
+ property Options : TOpenSaveOptions read FOptions write FOptions;
+ {* Options. }
+ property DefExtension : KOLString read FDefExtension write FDefExtension;
+ {* Default extention. Set it to desired extension without leading period,
+ e.g. 'txt', but not '.txt'. }
+ property WndOwner: THandle read fWnd write fWnd;
+ {* Owner window handle. If not assigned, Applet.Handle is used (whenever
+ possible). Assign it, if your application has stay-on-top forms, and
+ a separate Applet object is used. }
+ property OpenReadOnly: Boolean read fOpenReadOnly;
+ {* TRUE after Execute, if Read Only check box was checked by the user.
+ Options are not affected anyway. }
+ end;
+
+const DefOpenSaveDlgOptions: TOpenSaveOptions = [ OSHideReadonly,
+ OSOverwritePrompt, OSFileMustExist, OSPathMustExist ];
+
+function NewOpenSaveDialog( const Title, StrtDir: KOLString;
+ Options: TOpenSaveOptions ): POpenSaveDialog;
+{* Creates object, which can be used (several times) to open file(s)
+ selecting dialog. }
+
+type
+ POpenDirDialog = ^TOpenDirDialog;
+
+ TOpenDirOption = ( odBrowseForComputer, odBrowseForPrinter, odDontGoBelowDomain,
+ odOnlyFileSystemAncestors, odOnlySystemDirs, odStatusText,
+ odBrowseIncludeFiles, odEditBox, odNewDialogStyle );
+ {* Flags available for TOpenDirDialog object. }
+ // odfStatusText - do not support status callback
+ TOpenDirOptions = set of TOpenDirOption;
+ {* Set of all flags used to control ZOpenDirDialog class. }
+
+ TOnODSelChange = procedure( Sender: POpenDirDialog; NewSelDir: PKOL_Char;
+ var EnableOK: Integer; var StatusText: KOL_String )
+ of object;
+ {* Event type to be called when user select another directory in OpenDirDialog.
+ Set EnableOK to -1 to disable OK button, or to +1 to enable it.
+ It is also possible to set new StatusText string. }
+
+{ ----------------------------------------------------------------------
+ TOpenDirDialog
+----------------------------------------------------------------------- }
+ TOpenDirDialog = object( TObj )
+ {* Dialog for open directories, uses SHBrowseForFolder. }
+ protected
+ FTitle: KOLString;
+ FOptions: TOpenDirOptions;
+ FCallBack: Pointer;
+ FCenterProc: procedure( Wnd: HWnd );
+ FBuf : array[ 0..MAX_PATH ] of KOLChar;
+ FInitialPath: KOLString;
+ FCenterOnScreen: Boolean;
+ FDoSelChanged: procedure( Wnd: HWnd; Msg: DWORD; lParam, lpData: LParam ); stdcall;
+ FOnSelChanged: TOnODSelChange;
+ FStatusText: KOLString;
+ FWnd, FDialogWnd: HWnd;
+ function GetPath: KOLString;
+ procedure SetInitialPath(const Value: KOLString);
+ procedure SetCenterOnScreen(const Value: Boolean);
+ procedure SetOnSelChanged(const Value: TOnODSelChange);
+ function GetInitialPath: KOLString;
+ public
+ destructor Destroy; virtual;
+ {* destructor }
+ function Execute : Boolean;
+ {* Call it to select directory by user. Returns True, if operation was
+ not cancelled by user. }
+ property Title : KOLString read FTitle write FTitle;
+ {* Title for a dialog. }
+ property Options : TOpenDirOptions read FOptions write FOptions;
+ {* Option flags. }
+ property Path : KOLString read GetPath;
+ {* Resulting (selected by user) path. }
+ property InitialPath: KOLString read GetInitialPath write SetInitialPath;
+ {* Set this property to a path of directory to be selected initially
+ in a dialog. }
+ property CenterOnScreen: Boolean read FCenterOnScreen write SetCenterOnScreen;
+ {* Set it to True to center dialog on screen. }
+ property OnSelChanged: TOnODSelChange read FOnSelChanged write SetOnSelChanged;
+ {* This event is called every time, when user selects another directory.
+ It is possible to enable/disable OK button in dialog and/or change
+ dialog status text in responce to event. }
+ property WndOwner: HWnd read FWnd write FWnd;
+ {* Owner window. If you want to provide your dialog visible over stay-on-top
+ form, fire it as a child of the form, assigning the handle of form window
+ to this property first. }
+ property DialogWnd: HWnd read FDialogWnd;
+ {* Handle to the open directory dialog itself, become available on the
+ first call of callback procedure (i.e. on the first call to OnSelChanged).
+ }
+ end;
+
+function NewOpenDirDialog( const Title: KOLString; Options: TOpenDirOptions ):
+ POpenDirDialog;
+{* Creates object, which can be used (several times) to open directory
+ selecting dialog (using SHBrowseForFolder API call). }
+
+type
+ TColorCustomOption = ( ccoFullOpen, ccoShortOpen, ccoPreventFullOpen );
+
+{$IFDEF KOL_MCK}
+type TKOLOpenDirDialog = POpenDirDialog;
+{$ENDIF}
+
+ PColorDialog = ^TColorDialog;
+{ ----------------------------------------------------------------------
+ TColorDialog
+----------------------------------------------------------------------- }
+ TColorDialog = object( TObj )
+ {* Color choosing dialog. }
+ protected
+ public
+ OwnerWindow: HWnd;
+ {* Owner window (can be 0). }
+ CustomColors: array[ 1..16 ] of TColor;
+ {* Array of stored custom colors. }
+ ColorCustomOption: TColorCustomOption;
+ {* Options (how to open a dialog). }
+ Color: TColor;
+ {* Returned color (if the result of Execute is True). }
+ function Execute: Boolean;
+ {* Call this method to open a dialog and wait its result. }
+ end;
+
+function NewColorDialog( FullOpen: TColorCustomOption ): PColorDialog;
+{* Creates color choosing dialog object. }
+{$ENDIF WIN_GDI}
+{$IFDEF WIN_GDI}
+type
+ TIniFileMode = ( ifmRead, ifmWrite );
+ {* ifmRead is default mode (means "read" data from ini-file.
+ Set mode to ifmWrite to write data to ini-file, correspondent to
+ TIniFile. }
+
+ PIniFile = ^TIniFile;
+
+{ ----------------------------------------------------------------------
+ TIniFile - store/load data to ini-files
+----------------------------------------------------------------------- }
+ TIniFile = object( TObj )
+ {* Ini file incapsulation. The main feature is what the same block of
+ read-write operations could be defined (difference must be only in
+ Mode value).
+ |*Ini file sample.
+ This sample shows how the same Pascal operators can be used both
+ for read and write for the same variables, when working with TIniFile:
+ ! procedure ReadWriteIni( Write: Boolean );
+ ! var Ini: PIniFile;
+ ! begin
+ ! Ini := OpenIniFile( 'MyIniFile.ini' );
+ ! Ini.Section := 'Main';
+ ! if Write then // if Write, the same operators will save
+ ! Ini.Mode := ifmWrite; // data rather then load.
+ ! MyForm.Left := Ini.ValueInteger( 'Left', MyForm.Left );
+ ! MyForm.Top := Ini.ValueInteger( 'Top', MyForm.Top );
+ ! Ini.Free;
+ ! end;
+ !
+ |* }
+ protected
+ fMode: TIniFileMode;
+ fFileName: KOLString;
+ fSection: KOLString;
+ protected
+ public
+ destructor Destroy; virtual;
+ {* destructor }
+ property Mode: TIniFileMode read fMode write fMode;
+ {* ifmWrite, if write data to ini-file rather than read it. }
+ property FileName: KOLString read fFileName;
+ {* Ini file name. }
+ property Section: KOLString read fSection write fSection;
+ {* Current ini section. }
+ function ValueInteger( const Key: KOLString; Value: Integer ): Integer;
+ {* Reads or writes integer data value. }
+ function ValueString( const Key: KOLString; const Value: KOLString ): KOLString;
+ {* Reads or writes string data value. }
+ function ValueDouble( const Key: KOLString; const Value: Double ): Double;
+ {* Reads or writes Double data value. }
+ function ValueBoolean( const Key: KOLString; Value: Boolean ): Boolean;
+ {* Reads or writes Boolean data value. }
+ function ValueData( const Key: KOLString; Value: Pointer; Count: Integer ): Boolean;
+ {* Reads or writes data from/to buffer. Returns True, if success. }
+ procedure ClearAll;
+ {* Clears all sections of ini-file. }
+ procedure ClearSection;
+ {* Clears current Section of ini-file. }
+ procedure ClearKey( const Key: KOLString );
+ {* Clears given key in current section. }
+
+ /////////////// + by Vyacheslav A. Gavrik:
+ procedure GetSectionNames(Names:PKOLStrList);
+ {* Retrieves section names, storing it in string list passed as a parameter.
+ String list does not cleared before processing. Section names are added
+ to the end of the string list. }
+ procedure SectionData(Names:PKOLStrList);
+ {* Read/write current section content to/from string list. (Depending on
+ current Mode value). }
+ ///////////////
+
+ end;
+
+function OpenIniFile( const FileName: KOLString ): PIniFile;
+{* Opens ini file, creating TIniFile object instance to work with it. }
+{$ENDIF WIN_GDI}
+
+type
+ TMenuitemInfo = packed record
+ cbSize: UINT;
+ fMask: UINT;
+ fType: UINT; { used if MIIM_TYPE}
+ fState: UINT; { used if MIIM_STATE}
+ wID: UINT; { used if MIIM_ID}
+ hSubMenu: HMENU; { used if MIIM_SUBMENU}
+ hbmpChecked: HBITMAP; { used if MIIM_CHECKMARKS}
+ hbmpUnchecked: HBITMAP; { used if MIIM_CHECKMARKS}
+ dwItemData: DWORD; { used if MIIM_DATA}
+ dwTypeData: PKOLChar; { used if MIIM_TYPE}
+ cch: UINT; { used if MIIM_TYPE}
+ hbmpItem: HBITMAP; { used if MIIM_BITMAP - not exists under Windows95 }
+ end;
+
+const
+ TPM_HORPOSANIMATION = $0400;
+ TPM_HORNEGANIMATION = $0800;
+ TPM_VERPOSANIMATION = $1000;
+ TPM_VERNEGANIMATION = $2000;
+ TPM_NOANIMATION = $4000;
+
+type
+ PMenu = ^TMenu;
+
+ TOnMenuItem = procedure( Sender : PMenu; Item : Integer ) of object;
+ {* Event type to define OnMenuItem event. }
+
+ TMenuAccelerator = packed Record
+ {* Menu accelerator record. Use MakeAccelerator function to combine desired
+ attributes into a record, describing the accelerator. }
+ fVirt: Byte; // or-combination of FSHIFT, FCONTROL, FALT, FVIRTKEY, FNOINVERT
+ Key: Word; // character or virtual key code (FVIRTKEY flag is present above)
+ NotUsed: Byte; // not used
+ end;
+
+ // by Sergey Shisminzev:
+ TMenuOption = (moDefault, moDisabled, moChecked,
+ moCheckMark, moRadioMark, moSeparator, moBitmap, moSubMenu,
+ moBreak, moBarBreak);
+ {* Options to add menu items dynamically. }
+ TMenuOptions = set of TMenuOption;
+ {* Set of options for menu item to use it in TMenu.AddItem method. }
+
+ TMenuBreak = ( mbrNone, mbrBreak, mbrBarBreak );
+ {* Possible menu item break types. }
+
+{ ----------------------------------------------------------------------
+ TMenu - main, popup menu and menu item
+----------------------------------------------------------------------- }
+ TMenu = object( TObj )
+ protected
+ {$IFDEF GDI}
+ function GetItemHelpContext(Idx: Integer): Integer;
+ procedure SetItemHelpContext(Idx: Integer; const Value: Integer);
+ {* Dynamic menu incapsulation object. Can play role of form main menu or popup
+ menu, depending on kind of parent window (form or control) and order of
+ creation (created first (for a form) become main menu). Does not allow
+ merging menus, but items can be hidden. Additionally checkmark bitmaps,
+ shortcut key accelerators and other features are available. }
+ protected
+ FHandle: HMenu;
+ FId: Integer;
+ FControl: PControl;
+ {$ENDIF GDI}
+ fNextMenu : PMenu;
+ {$IFDEF GDI}
+ FMenuBreak: TMenuBreak;
+ FOnMenuItem : TOnMenuItem;
+ FOnRadioOff : TOnMenuItem;
+ fOnPopup: TOnEvent;
+ fByAccel: Boolean;
+ FIsCheckItem: Boolean;
+ FIsSeparator: Boolean;
+ FVisible: Boolean;
+ FOwnerDraw: Boolean;
+ FClearBitmaps: Boolean;
+ FNotPopup: Boolean;
+ f_DummyFiller: Byte;
+ FPopupFlags: DWORD;
+ FSavedState: DWORD;
+ FData: Pointer;
+ {$ENDIF GDI}
+ FParentMenu: PMenu;
+ FMenuItems: PList;
+ FRadioGroup: Integer;
+ FCaption: KOLString;
+ {$IFDEF _X_}
+ {$IFDEF GTK}
+ fChecked: Boolean;
+ fMnemonics: AnsiString;
+ fGtkMenuItem: PGtkWidget;
+ fGtkMenuShell: PGtkWidget;
+ fGtkMenuBar: PGtkWidget;
+ {$ENDIF GTK}
+ {$ENDIF _X_}
+ {$IFDEF GDI}
+ FBitmap: HBitmap;
+ FBmpChecked: HBitmap;
+ FBmpItem: HBitmap;
+ ClearBitmapsProc: procedure( Sender: PMenu );
+ FAccelerator: TMenuAccelerator;
+ FHelpContext: Integer;
+ FOnMeasureItem: TOnMeasureItem;
+ FOnDrawItem: TOnDrawItem;
+ {$IFDEF USE_MENU_CURCTL}
+ fCurCtl: PControl;
+ {$ENDIF USE_MENU_CURCTL}
+ function GetItems( Id: HMenu ): PMenu;
+ function GetCount: Integer;
+ function GetTopParent: PMenu;
+ function GetState( const Index: Integer ): Boolean;
+ procedure SetState( const Index: Integer; Value: Boolean );
+ procedure SetMenuVisible( Value: Boolean );
+ procedure SetData( Value: Pointer );
+ procedure SetMenuItemCaption( const Value: KOLString );
+ function FillMenuItems(AHandle: HMenu; StartIdx: Integer;
+ const Template: array of PKOLChar): Integer;
+ procedure SetMenuBreak( Value: TMenuBreak );
+ function GetControl: PControl;
+ function GetInfo( var MII: TMenuItemInfo ): Boolean;
+ function SetInfo( var MII: TMenuItemInfo ): Boolean;
+ function SetTypeInfo( var MII: TMenuItemInfo ): Boolean;
+ procedure SetBitmap( Value: HBitmap );
+ procedure SetBmpChecked( Value: HBitmap );
+ procedure SetBmpItem( Value: HBitmap );
+ procedure ClearBitmaps;
+ procedure SetAccelerator( const Value: TMenuAccelerator );
+ {$IFDEF GDI}
+ procedure SetHelpContext( Value: Integer );
+ {$ENDIF GDI}
+ procedure SetSubmenu( Value: HMenu );
+ procedure SetOnMeasureItem( const Value: TOnMeasureItem );
+ procedure SetOnDrawItem( const Value: TOnDrawItem );
+ procedure SetOwnerDraw( Value: Boolean );
+ protected
+ function GetItemChecked( Item : Integer ) : Boolean;
+ procedure SetItemChecked( Item : Integer; Value : Boolean );
+ function GetItemBitmap(Idx: Integer): HBitmap;
+ procedure SetItemBitmap(Idx: Integer; const Value: HBitmap);
+ function GetItemText(Idx: Integer): KOLString;
+ procedure SetItemText(Idx: Integer; const Value: KOLString);
+ function GetItemEnabled(Idx: Integer): Boolean;
+ procedure SetItemEnabled(Idx: Integer; const Value: Boolean);
+ function GetItemVisible(Idx: Integer): Boolean;
+ procedure SetItemVisible(Idx: Integer; const Value: Boolean);
+ function GetItemAccelerator(Idx: Integer): TMenuAccelerator;
+ procedure SetItemAccelerator(Idx: Integer; const Value: TMenuAccelerator);
+ function GetItemSubMenu( Idx: Integer ): HMenu;
+ {$ENDIF GDI}
+ public
+ destructor Destroy; virtual;
+ {* To release menu dynamically, call Free method instead. All (popup)
+ menus created after this (for the same control) are destroyed in
+ that case too.
+ |<br>
+ It is not necessary to release menu object manually: all menus,
+ created with given form (or control), are automatically released,
+ when owner form (or control) is destroyed.
+ }
+ {$IFDEF GDI}
+ property Handle : HMenu read FHandle;
+ {* Handle of Windows menu object. }
+ property MenuId: Integer read FId;
+ {* Id of the menu item object. If menu item has subitems, it has
+ also submenu Handle. Top parent menu object itself has no Id.
+ Id-s areassigned automatically starting from 4096. Do not
+ (re)create menu items instantly, because such values are not
+ reused, and maximum possible Id value must not exceed 65535. }
+ property Parent: PMenu read FParentMenu;
+ {* Parent menu item (or parent menu). }
+ property TopParent: PMenu read GetTopParent;
+ {* Top parent menu, owning all nested subitems. }
+ property Owner: PControl read GetControl;
+ {* Parent control or form. }
+ property Caption: KOLString read FCaption write SetMenuItemCaption;
+ {* Menu item caption text (including '&' indicating mnemonic characters,
+ and keyboard accelerator representation string, usually following
+ tabulation character). }
+ property Items[ Id: HMenu ]: PMenu read GetItems;
+ {* Returns menu item object by its index or by menu id. Since menu id
+ values are starting from 4096, values from 0 to 4095 are interpreted
+ as absolute index of menu item. Be careful accessing menu items or
+ submenus by index, if you dynamically insert or delete items or
+ submenus. In this version, separators are enumerating too, like
+ all other items. Use index -1 to access object itself. The first
+ item of a menu (or the first subitem of submenu item) has index 0.
+ Children are enumerating before all siblings. The maximum available
+ index is (Count - 1), when accessing menu items by index. }
+ property Count: Integer read GetCount;
+ {* Count of items together with all its nested subitems. }
+ function IndexOf( Item: PMenu ): Integer;
+ {* Returns index of an item. This index can be used to access
+ menu item. Value -2 is returned, if the Item is not a child for menu
+ or menu item, and has no parents, which are children for it, etc.
+ Menu object itself always has index -1. }
+ property OnMenuItem : TOnMenuItem read FOnMenuItem write FOnMenuItem;
+ {* Is called when menu item is clicked. Absolute index of menu item
+ clicked is passed as the second parameter. TopParent always is
+ passed as a Sender parameter. }
+ property ByAccel: Boolean read fByAccel;
+ {* True, when OnMenuItem is called not by mouse, but by accelerator key.
+ Check this flag for entire menu (TopParent), not for item itself.
+ (Note, that Sender in OnMenuItem always is TopParent menu object). )
+ }
+ property IsSeparator: Boolean read FIsSeparator;
+ {* TRUE, if a separator menu item. }
+ property MenuBreak: TMenuBreak read FMenuBreak write SetMenuBreak;
+ {* Menu item break type. }
+ property OnUncheckRadioItem : TOnMenuItem read FOnRadioOff write FOnRadioOff;
+ {* Is called when radio item becomes unchecked in menu in result of
+ checking another radio item of the same radio group. }
+ property RadioGroup: Integer read FRadioGroup write FRadioGroup;
+ {* Radio group index. Several neighbour items with the same radio group
+ index form radio group. Only single item from the same group can be
+ checked at a time. }
+ property IsCheckItem: Boolean read FIsCheckItem;
+ {* If menu item is defined as check item, it is checked automatically
+ when clicked. }
+ procedure RadioCheckItem;
+ {* Call this method to check radio item. (Calling this method for
+ an item, which is not belonging to a radio group, just sets its
+ Checked state to TRUE). }
+ property Checked: Boolean index MFS_CHECKED read GetState write SetState;
+ {* Checked state of the item. }
+ property Enabled: Boolean
+ {$IFDEF F_P}
+ index $80000000 or MFS_DISABLED
+ {$ELSE DELPHI}
+ index Integer( $80000000 or MFS_DISABLED )
+ {$ENDIF F_P/DELPHI}
+ read GetState write SetState;
+ {* Enabled state of the item. Whaen assigned, Grayed state also is
+ set to arbitrary value (i.e., when Enabled is set to true, Grayed
+ is set to FALSE. }
+ property DefaultItem: Boolean index MFS_DEFAULT read GetState write SetState;
+ {* Set this property to TRUE to make menu item default. Default item
+ is drawn with bold.
+ |<br>If you change DefaultItem at run-time and whant
+ to provide changing its visual state, recreate the item first resetting
+ Visible property, then setting it again. }
+ property Highlight: Boolean index MFS_HILITE read GetState write SetState;
+ {* Highlight state of the item. }
+ property Visible: Boolean read FVisible write SetMenuVisible;
+ {* Visibility of menu item. }
+ property Data: Pointer read FData write SetData;
+ {* Data pointer, associated with the menu item. }
+ property Bitmap: HBitmap read FBitmap write SetBitmap;
+ {* Bitmap used for unchecked state of the menu item. }
+ property BitmapChecked: HBitmap read FBmpChecked write SetBmpChecked;
+ {* Bitmap used for checked state of the menu item. }
+ property BitmapItem: HBitmap read FBmpItem write SetBmpItem;
+ {* Bitmap used for item itself. In addition, following special values
+ are possible:
+ HBMMENU_CALLBACK, HBMMENU_MBAR_CLOSE, HBMMENU_MBAR_CLOSE_D,
+ HBMMENU_MBAR_MINIMIZE, HBMMENU_MBAR_MINIMIZE_D, HBMMENU_MBAR_RESTORE,
+ HBMMENU_POPUP_CLOSE, HBMMENU_POPUP_MAXIMIZE, HBMMENU_POPUP_MINIMIZE,
+ HBMMENU_POPUP_RESTORE, HBMMENU_SYSTEM. }
+ property Accelerator: TMenuAccelerator read FAccelerator write SetAccelerator;
+ {* Accelerator for menu item. }
+ {$IFDEF GDI}
+ property HelpContext: Integer read FHelpContext write SetHelpContext;
+ {* Help context for entire menu (help context can not be assigned to
+ individual menu items). }
+ {$ENDIF GDI}
+
+ procedure AssignEvents( StartIdx: Integer; const Events: array of TOnMenuItem );
+ {* It is possible to assign its own event handler to every menu item
+ using this call. This procedure also is called automatically in
+ a constructor NewMenuEx. }
+
+ function Popup( X, Y : Integer ): Integer; {!ecm}
+ {* Only for popup menu - to popup it at the given position on screen.
+ Return: If you specify TPM_RETURNCMD in the uFlags parameter, the return
+ value is the menu-item identifier of the item that the user selected.
+ If the user cancels the menu without making a selection, or if an error
+ occurs, then the return value is zero.
+ If you do not specify TPM_RETURNCMD in the uFlags parameter, the return
+ value is nonzero if the function succeeds and zero if it fails. }
+ function PopupEx( X, Y: Integer ): Integer; {!ecm}
+ {* This version of popup command is very useful, when popup menu is activated
+ when its parent window is not visible (e.g., for a kind of applications,
+ which always are invisible, and can be activated only using tray icon).
+ PopupEx method provides correct tracking of menu disappearing when mouse
+ is clicked anywhere else on screen, fixing strange menu behavior in some
+ Windows versions (NT).
+ |<br>
+ Actually, when PopupEx used, parent form is shown but below of visible
+ screen, and when menu is disappearing, previous state of the form (visibility
+ and position) are restored. If such solvation is not satisfying You,
+ You can do something else (e.g., use region clipping, etc.) }
+ property OnPopup: TOnEvent read fOnPopup write fOnPopup;
+ {* This event occurs before the popup menu is shown. }
+ property NotPopup: Boolean read FNotPopup write FNotPopup;
+ {* Set this property to true to prevent popup of popup menu, e.g. in
+ OnPopup event handler. }
+ property Flags: DWORD read FPopupFlags write FPopupFlags;
+ {* Pop-up flags, which are used to call TrackPopupMenuEx, when Popup or
+ PopupEx method is called. Can be a combination of following values:
+ |<br>
+ TPM_CENTERALIGN or TPM_LEFTALIGN or TPM_RIGHTALIGN
+ |<br>
+ TPM_BOTTOMALIGN or TPM_TOPALIGN or TPM_VCENTERALIGN
+ |<br>
+ TPM_NONOTIFY or TPM_RETURNCMD
+ |<br>
+ TPM_LEFTBUTTON or TPM_RIGHTBUTTON
+ |<br>
+ TPM_HORNEGANIMATION or TPM_HORPOSANIMATION or TPM_NOANIMATION or
+ TPM_VERNEGANIMATION or TPM_VERPOSANIMATION
+ |<br>
+ TPM_HORIZONTAL or TPM_VERTICAL.
+ |<br>
+ By default, a combination TPM_LEFTALIGN or TPM_LEFTBUTTON is used. }
+ function Insert(InsertBefore: Integer; ACaption: PKOLChar; Event: TOnMenuItem;
+ Options: TMenuOptions): PMenu;
+ {* Inserts new menu item before item, given by Id (>=4096) or index
+ value InsertBefore. Pointer to an object created is returned. }
+ property SubMenu: HMenu read FHandle; // write SetSubMenu;
+ {* Submenu associated with the menu item. The same as Handle. It was possible
+ in ealier versions to change this value, replacing (removing, assigning)
+ entire popup menu as a submenu for menu item.
+ But in modern version of TMenu, this is not possible.
+ Instead, entire menu object should be added or removed using
+ InsertSubmenu or RemoveSubmenu methods. }
+ procedure InsertSubMenu( SubMenuToInsert: PMenu; InsertBefore: Integer );
+ {* Inserts existing menu item (together with its subitems if any present)
+ into given position. See also RemoveSubMenu. }
+ function RemoveSubMenu( ItemToRemove: Integer ): PMenu;
+ {* Removes menu item from the menu, returning TMenu object, representing it,
+ if submenu item, having its own children, detached. If an individual menu
+ item is removed, nil is returned.
+ This function can be useful to add or remove dynamically entire submenus
+ (created together with its subitems). }
+ property OnMeasureItem: TOnMeasureItem read FOnMeasureItem write SetOnMeasureItem;
+ {* This event is called for owner-drawn menu items. Event handler should return
+ menu item height in lower word of a result and item width (for menu) in
+ high word of result. If either for height or for width returned value is 0,
+ a default one is used. }
+ property OnDrawItem: TOnDrawItem read FOnDrawItem write SetOnDrawItem;
+ {* This event is called for owner-drawn menu items. }
+ property OwnerDraw: Boolean read FOwnerDraw write SetOwnerDraw;
+ {* Set this property to true for some items to make it owner-draw. }
+
+ // For compatibility with old code (be sure that item with given index
+ // actually exists):
+ function GetMenuItemHandle( Idx : Integer ): DWORD;
+ {* Returns Id of menu item with given index. }
+ property ItemHandle[ Idx: Integer ]: DWORD read GetMenuItemHandle;
+ {* Returns handle for item given by index. }
+ property ItemChecked[ Idx : Integer ] : Boolean read GetItemChecked write SetItemChecked;
+ {* True, if correspondent menu item is checked. }
+ procedure RadioCheck( Idx : Integer );
+ {* Call this method to check radio item. For radio items, do not
+ use assignment to ItemChecked or Checked properties. }
+ property ItemBitmap[ Idx: Integer ]: HBitmap read GetItemBitmap write SetItemBitmap;
+ {* This property allows to assign bitmap to menu item (for unchecked state
+ only - for checked menu items default checkmark bitmap is used). }
+ procedure AssignBitmaps( StartIdx: Integer; Bitmaps: array of HBitmap );
+ {* Can be used to assign bitmaps to several menu items during one call. }
+ property ItemText[ Idx: Integer ]: KOLString read GetItemText write SetItemText;
+ {* This property allows to get / modify menu item text at run time. }
+ property ItemEnabled[ Idx: Integer ]: Boolean read GetItemEnabled write SetItemEnabled;
+ {* Controls enabling / disabling menu items. Disabled menu items are
+ displayed (grayed) but inaccessible to click. }
+ property ItemVisible[ Idx: Integer ]: Boolean read GetItemVisible write SetItemVisible;
+ {* This property allows to simulate visibility of menu items (implementing
+ it by removing or inserting again if needed. For items of submenu, which
+ is made invisible, True is returned. If such item made Visible, entire
+ submenu with all its parent menu items becomes visible. To release menu
+ properly it is necessary to make before all its items visible again.
+ This does not matter, if menu is released at the end of execution, but
+ can be sensible if owner form is destroyed and re-created at run time
+ dynamically. }
+ property ItemHelpContext[ Idx: Integer ]: Integer read GetItemHelpContext
+ write SetItemHelpContext;
+ function ParentItem( Idx: Integer ): Integer;
+ {* Returns index of parent menu item (for submenu item). If there are no
+ such item (Idx corresponds to root level menu item), -1 is returned. }
+ property ItemAccelerator[ Idx: Integer ]: TMenuAccelerator read GetItemAccelerator write SetItemAccelerator;
+ {* Allows to get / change accelerator key kodes assigned to menu items.
+ Has no effect unless SupportMnemonics called for a form. }
+ property ItemSubmenu[ Idx: Integer ]: HMenu read GetItemSubmenu; // write SetItemSubmenu;
+ {* Retrieves submenu item dynamically. See also SubMenu property. }
+
+ // by Sergey Shisminzev:
+ function AddItem(ACaption: PKOLChar; Event: TOnMenuItem; Options: TMenuOptions): Integer;
+ {* Adds menu item dynamically. Returns ID of the added item. }
+ function InsertItem(InsertBefore: Integer; ACaption: PKOLChar; Event: TOnMenuItem; Options: TMenuOptions): Integer;
+ {* Inserts menu item before an item with ID, given by InsertBefore parameter. }
+ function InsertItemEx(InsertBefore: Integer; ACaption: PKOLChar; Event: TOnMenuItem; Options: TMenuOptions;
+ ByPosition: Boolean): Integer;
+ {* Inserts menu item by command or by position, dependant on ByPosition parameter }
+ procedure RedrawFormMenuBar;
+ {* }
+
+ {$IFDEF USE_MENU_CURCTL}
+ property CurCtl: PControl read fCurCtl write fCurCtl;
+ {* By Alexander Pravdin. This property is assigned to a control which were
+ initiated a pop-up, for popup menu. }
+ {$ENDIF USE_MENU_CURCTL}
+ {$ENDIF GDI}
+ end;
+
+{$IFDEF WIN_GDI}
+function MenuStructSize: Integer;
+{* Returns 44 under Windows95, and 48 (=sizeof(TMenuItemInfo) under all other
+ Windows versions. }
+
+var FDynamicMenuID: DWORD = $1000;
+{$ENDIF WIN_GDI}
+function NewMenu( AParent : PControl; MaxCmdReserve: DWORD;
+ const Template : array of PKOLChar; aOnMenuItem: TOnMenuItem ): PMenu;
+{* Menu constructor. First created menu becomes main menu of form (if AParent
+ is a form). All other menus becomes popup (can be activated using Popup
+ method). To provide dynamic replacing of main menu, create all popup
+ menus as children of any other control, not form itself.
+ When Menu is created, pass FirstCmd integer value to set it
+ as ID of first menu item (all other ID's obtained by incrementing this value),
+ and Template, which is an array of PChar (usually array of string constants),
+ containing list of menu item identifiers and/or formatting characters.
+|<br>&nbsp;&nbsp;&nbsp;
+ FirstCmd value is assigned to first menu item created as its ID,
+ all follow menu items are assigned to ID's obtained from FirstCmd incrementing
+ it by 1. It is desirable to provide not intersected ranges of ID's for
+ defferent menus in the applet.
+|<br>&nbsp;&nbsp;&nbsp;
+ Following formatting characters can be used in menu template strings:
+|&L=<br><b>%1</b>
+ <L &amp; (in identifier)> - to underline next character and use it as a shortcut character
+ when possible;
+ <L + (in front of identifier)> - to make item checked. If also
+|<b>!</b> is used before <b>
+ &
+|</b> than radioitem is defined;
+ <L - (in front of identifier)> - item not checked;
+ <L - (separate)> - separator (between two items);
+ <L ( (separate)> - start of submenu;
+ <L ) (separate)> - end of submenu;
+|<br>&nbsp;&nbsp;&nbsp;
+ To get access to menu items, use constants 0, 1, etc. It is a good idea
+ to create special enumerated type to index correspondent menu items
+ using Ord( ) operator. Note in that case, that it is necessary only to
+ define constants correspondent to identifiers (positions, correspondent
+ to separators or submenu brackets are not identified by numbers).
+|<br>&nbsp;&nbsp;&nbsp;
+}
+
+function NewMenuEx( AParent : PControl; FirstCmd : Integer;
+ const Template : array of PKOLChar; aOnMenuItems: array of TOnMenuItem ): PMenu;
+{* Creates menu, assigning its own event handler for every (enough) menu item. }
+{$IFDEF WIN_GDI}
+
+function MakeAccelerator( fVirt: Byte; Key: Word ): TMenuAccelerator;
+{* Creates accelerator item to assign it to TMenu.ItemAccelerator[ ] property
+ easy.}
+
+// {YS} added 7 Aug 2004
+function GetAcceleratorText( const Accelerator: TMenuAccelerator ): KOLString;
+{* Returns text representation of accelerator.
+ |<hr>
+
+ <R System functions and working with windows>
+}
+type
+ TWindowChildKind = ( wcActive, wcFocus, wcCapture, wcMenuOwner,
+ wcMoveSize, wcCaret );
+ {* Type of window child kind. Used in function GetWindowChild. }
+
+function GetWindowChild( Wnd: HWnd; Kind: TWindowChildKind ): HWnd;
+{* Returns child of given top-level window, having given characteristics.
+ For example, it is possible to get know for foreground window,
+ which of its child window has focus. This function does not work in old
+ Windows 95 (returns Wnd in that case). But for Windows 98, Windows NT/2000
+ this function works fine. To obtain focused child of the window,
+ use GetFocusedWindow, which is independant from Windows version. }
+
+function GetFocusedChild( Wnd: HWnd ): HWnd;
+{* Returns focused child of given window (which should be foreground
+ and active, certainly). 0 is returned either if Wnd is not active
+ or Wnd has no focused child window. }
+
+function Stroke2Window( Wnd: HWnd; const S: AnsiString ): Boolean;
+{* Posts characters from string S to those child window of Wnd, which
+ has focus now (top-level window Wnd must be foreground, and have
+ focused edit-aware control to receive the stroke).
+ |<br>
+ This function allows only to post typeable characters (including
+ such special symbols as #13 (Enter), #9 (Tab), #8 (BackSpace), etc.
+ |<br>
+ See also function Stroke2WindowEx, which allows to post any key down
+ and up events, simulating keyboard for given (automated) application. }
+
+function Stroke2WindowEx( Wnd: HWnd; const S: AnsiString; Wait: Boolean ): Boolean;
+{* In addition to function Stroke2Window, this one can send special keys
+ to given window, including functional keys and navigation keys. To
+ post special key to target window, place a combination of names of
+ such key together with keys, which should be passed simultaneously,
+ between square or figure brackets. For example, [Ctrl F1], [Alt Shift Home],
+ [Ctrl E]. For letters and usual characters, it is not necessary to
+ simulate pressing it with determining all Shift combinations and it is
+ sufficient to pass characters as is. (E.g., not '[Shift 1]', but '!'). }
+
+function FindWindowByThreadID( ThreadID : DWORD ) : HWnd;
+{* Searches for window, belonging to a given thread. }
+
+function DesktopPixelFormat: TPixelFormat;
+{* Returns the pixel format correspondent to current desktop color resolution.
+ Use this function to decide which format to use for converting bitmap,
+ planned to draw transparently using TBitmap.DrawTransparent or
+ TBitmap.StretchDrawTransparent methods. }
+
+function GetDesktopRect : TRect;
+{* Returns rectangle of screen, free of taskbar and other
+ similar app-bars, which reduces size of available desktop
+ when created. }
+function GetWorkArea: TRect;
+{* The same as GetDesktopRect, but obtained calling SystemParametersInfo. }
+
+function ExecuteWait( const AppPath, CmdLine, DfltDirectory: KOLString;
+ Show: DWORD; TimeOut: DWORD; ProcID: PDWORD ): Boolean;
+{* Allows to execute an application and wait when it is finished. Pass
+ INFINITE constant as TimeOut, if You sure that application is finished
+ anyway. If another value passed as a TimeOut (in milliseconds), and
+ application was not finished for that time, ExecuteWait is returning
+ FALSE, and if ProcID is not nil, than ProcID^ contains started process
+ handle (it can be used to wait it more, or to terminate it using
+ TerminateProcess API function).
+ |<br>
+ Launching application can be console or GUI - it does not matter.
+ Pass SW_SHOW, SW_HIDE or other SW_XXX constant as Show parameter
+ as appropriate.
+ |<br>
+ True is returned only in case when application specified was launched
+ successfully and finished for TimeOut specified. Otherwise, check
+ ProcID^ variable: if it is 0, process could not be launched (and it
+ is possible to get information about error using GetLastError API
+ function in a such case). You can freely pass nil in place of ProcID
+ parameter, but this is acually correct only when TimeOut is INFINITE. }
+function ExecuteIORedirect( const AppPath, CmdLine, DfltDirectory: KOLString;
+ Show: DWORD; ProcID: PDWORD; InPipe, OutPipeWr, OutPipeRd: PHandle ): Boolean;
+{* Executes an application with its console input and output redirection.
+ Terminating of the application is not waiting, but if ProcID pointer
+ is defined, it receives process Id launched, so it is possible to
+ call WaitForSingleObject for it. InPipe is a pointer to THandle variable
+ which receives a handle to input pipe of the console redirected. The same
+ is for OutPipeWr and OutPipeRd, but for output of the console redirected.
+ Before reading from OutPipeRd^, first close OutPipeWr^. If you run
+ simple console application, for which you want to read results after its
+ termination, you can use ExecuteConsoleAppIORedirect instead.
+ |<br>&nbsp;&nbsp;&nbsp;
+ Notes: if your application is not console and it does not create console
+ using AllocConsole, this function will fail to redirect input-output. }
+function ExecuteConsoleAppIORedirect( const AppPath, CmdLine, DfltDirectory: KOLString;
+ Show: DWORD; const InStr: KOLString; var OutStr: KOLString; WaitTimeout: DWORD )
+ : Boolean;
+{* Executes an application, redirecting its console input and output.
+ After redirecting input and output and launching the application,
+ content of InStr is written to input stream of the application, then
+ the application is waiting for its termination (WaitTimeout milliseconds
+ or INFINITE, as passed) and console output of the application is read to
+ OutStr. TRUE is returned only in case, when all these tasks are
+ completed successfully.
+ |<br>&nbsp;&nbsp;&nbsp;
+ Notes: if your application is not console and it does not create console
+ using AllocConsole, this function will fail to redirect input-output. }
+
+function WindowsShutdown( const Machine : KOLString; Force, Reboot : Boolean ) : Boolean;
+{* Shut down of Windows NT. Pass Machine = '' to shutdown this PC.
+ Pass Reboot = True to reboot immediatelly after shut down. }
+function WindowsLogoff( Force : Boolean ) : Boolean;
+{* Logoff of Windows. }
+
+
+type
+ TWindowsVersion = ( wv31, wv95, wv98, wvME, wvNT, wvY2K, wvXP, wvServer2003,
+ wvVista, wvSeven );
+ {* Windows versions constants. }
+ TWindowsVersions = Set of TWindowsVersion;
+ {* Set of Windows version (e.g. to define a range of versions supported by the
+ application). }
+
+function WinVer : TWindowsVersion;
+{* Returns Windows version. }
+function IsWinVer( Ver : TWindowsVersions ) : Boolean;
+{* Returns True if Windows version is in given range of values. }
+{$IFNDEF PARAMS_DEFAULT}
+function SkipParam(P: PKOLChar): PKOLChar; //forward;
+function ParamStr( Idx: Integer ): KOLString;
+{* Returns command-line parameter by index. This function supersides
+ standard ParamStr function. }
+function ParamCount: Integer;
+{* Returns number of parameters in command line.
+|<hr>
+}
+{$ENDIF}
+{$ENDIF WIN_GDI}
+
+{$IFDEF INPACKAGE}
+ {$IFDEF ASM_VERSION}
+ {$UNDEF ASM_VERSION}
+ {$ENDIF}
+{$ENDIF}
+
+{$IFDEF WIN_GDI}
+//{$DEFINE CHK_BITBLT}
+{$IFDEF CHK_BITBLT}
+procedure Chk_BitBlt;
+{$ENDIF}
+{$IFDEF ASM_VERSION}
+ {$DEFINE ASM_DC}
+{$ENDIF}
+{$IFDEF ASM_DC}
+procedure StartDC;
+procedure FinishDC;
+{$ENDIF ASM_VERSION}
+
+function WndProcCtrl( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+function WndProcDoEraseBkgnd( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+
+var CreatingWindow: PControl;
+ //ActiveWindow: HWnd;
+{$ENDIF WIN_GDI}
+{$IFDEF _D2}
+// Assert operator was not available in Delphi2. Provide here easy Assert
+// procedure for Delphi2.
+procedure Assert( Cond: Boolean; const Msg: AnsiString );
+
+var AssertErrorProc: procedure( const Message, Filename: AnsiString; LineNumber: Integer );
+{$ENDIF}
+
+{$IFDEF USE_CUSTOMEXTENSIONS}
+ {$I CUSTOM_KOL_EXTENSION.inc} // See comments in TControl
+{$ENDIF}
+
+{$IFDEF DEBUG_ENDSESSION}
+var EndSession_Initiated: Boolean;
+{$ENDIF}
+
+{$IFDEF WIN_GDI}
+var
+ FMMNotify: procedure( var Msg: TMsg );
+
+procedure ClearText( Sender: PControl );
+procedure ClearListbox( Sender: PControl );
+procedure ClearCombobox( Sender: PControl );
+procedure ClearListView( Sender: PControl );
+procedure ClearTreeView( TV: PControl );
+
+{$IFDEF COMMANDACTIONS_OBJ}
+const OTHER_ACTIONS = 0;
+ LABEL_ACTIONS = 1;
+ BUTTON_ACTIONS = 2;
+ EDIT_ACTIONS = 3;
+ LIST_ACTIONS = 4;
+ COMBO_ACTIONS = 5;
+ LISTVIEW_ACTIONS = 6;
+ TREEVIEW_ACTIONS = 7;
+ TABCONTROL_ACTIONS = 8;
+ RICHEDIT_ACTIONS = 9;
+ PROGRESS_ACTIONS = 10;
+ TOOLBAR_ACTIONS = 11;
+ LAST_ACTIONS = 11;
+var AllActions_Objs: array[ 0..LAST_ACTIONS ] of PCommandActionsObj;
+{$ENDIF}
+
+const
+ {$IFDEF PACK_COMMANDACTIONS}
+ ButtonActions_Packed: PAnsiChar = Char(BUTTON_ACTIONS) +
+ #0#0 + //BN_CLICKED
+ #6#0 + //BN_SETFOCUS
+ #7#0 + //BN_KILLFOCUS
+ #225 + //25 íóëåé
+ #0#1 + //BS_LEFT
+ #0#2 + //BS_RIGHT
+ #0#3 + //BS_CENTER
+ #0#4 + //0, BS_TOP>>8
+ #12#8+ // BS_VCENTER>>8, BS_BOTTOM>>8
+ #204 //4 íóëÿ
+ ;
+ {$ELSE}
+ ButtonActions: TCommandActions = (
+ aClear: ClearText;
+ aAddText: nil;
+ aClick: BN_CLICKED;
+ aEnter: BN_SETFOCUS;
+ aLeave: BN_KILLFOCUS;
+ aChange: 0;
+ aSelChange: 0;
+ aGetCount: 0;
+ aSetCount: 0;
+ aGetItemLength: 0;
+ aGetItemText: 0;
+ aSetItemText: 0;
+ aGetItemData: 0;
+ aSetItemData: 0;
+ aAddItem: 0;
+ aDeleteItem: 0;
+ aInsertItem: 0;
+ aFindItem: 0;
+ aFindPartial: 0;
+ bItem2Pos: 0;
+ bPos2Item: 0;
+ aGetSelCount: 0;
+ aGetSelected: 0;
+ aGetSelRange: 0;
+ aGetCurrent: 0;
+ aSetSelected: 0;
+ aSetCurrent: 0;
+ aSetSelRange: 0;
+ aExSetSelRange: 0;
+ aGetSelection: 0;
+ aReplaceSel: 0;
+ aTextAlignLeft: BS_LEFT;
+ aTextAlignRight: BS_RIGHT;
+ aTextAlignCenter: BS_CENTER;
+ bTextAlignMask: 0;
+ bVertAlignTop: BS_TOP shr 8; //=4
+ bVertAlignCenter: BS_VCENTER shr 8; //=12
+ bVertAlignBottom: BS_BOTTOM shr 8; //=8
+ aDir: 0;
+ aSetLimit: 0;
+ aSetImgList: 0;
+ //-----aAutoSzX: 14;
+ //-----aAutoSzY: 6;
+ aSetBkColor: 0;
+ );
+ {$ENDIF}
+
+const
+ {$IFDEF PACK_COMMANDACTIONS}
+ LabelActions_Packed: PAnsiChar = Char( LABEL_ACTIONS ) +
+ #229 + //29 íóëåé
+ #2#0 + // SS_RIGHT
+ #1#0 + // SS_CENTER
+ #12#0 + // SS_LEFTNOWORDWRAP, 0
+ #2#0 + // SS_CENTERIMAGE>>8, 0
+ #205;
+ {$ELSE}
+ LabelActions: TCommandActions = (
+ aClear: ClearText;
+ aAddText: nil;
+ aClick: 0;
+ aEnter: 0;
+ aLeave: 0;
+ aChange: 0;
+ aSelChange: 0;
+ aGetCount: 0;
+ aSetCount: 0;
+ aGetItemLength: 0;
+ aGetItemText: 0;
+ aSetItemText: 0;
+ aGetItemData: 0;
+ aSetItemData: 0;
+ aAddItem: 0;
+ aDeleteItem: 0;
+ aInsertItem: 0;
+ aFindItem: 0;
+ aFindPartial: 0;
+ bItem2Pos: 0;
+ bPos2Item: 0;
+ aGetSelCount: 0;
+ aGetSelected: 0;
+ aGetSelRange: 0;
+ aGetCurrent: 0;
+ aSetSelected: 0;
+ aSetCurrent: 0;
+ aSetSelRange: 0;
+ aExSetSelRange: 0;
+ aGetSelection: 0;
+ aReplaceSel: 0;
+ aTextAlignLeft: SS_LEFT;
+ aTextAlignRight: SS_RIGHT;
+ aTextAlignCenter: SS_CENTER;
+ bTextAlignMask: SS_LEFTNOWORDWRAP;
+ bVertAlignTop: 0;
+ bVertAlignCenter: SS_CENTERIMAGE shr 8;
+ bVertAlignBottom: 0;
+ aDir: 0;
+ aSetLimit: 0;
+ aSetImgList: 0;
+ //---- aAutoSzX: 1;
+ //---- aAutoSzY: 1;
+ aSetBkColor: 0;
+ );
+ {$ENDIF}
+
+const
+ EN_LINK = $070b;
+ {$IFDEF PACK_COMMANDACTIONS}
+ EditActions_Packed: PAnsiChar = Char( EDIT_ACTIONS ) +
+ #201 +
+ #0#1 + // EN_SETFOCUS
+ #0#2 + // EN_KILLFOCUS
+ #0#3 + // EN_CHANGE
+ #201 +
+ #$BA#0 + // EM_GETLINECOUNT
+ #201 +
+ #$C1#0 + // EM_LINELENGTH
+ #$C4#0 + // EM_GETLINE
+ #$C2#0 + // EM_REPLACESEL
+ #207 +
+ #$BB#$C9 + // EM_LINEINDEX, EM_LINEFROMCHAR
+ #$B0#0 + // EM_GETSEL
+ #201 +
+ #$B0#0 + // EM_GETSEL
+ #$BB#0 + // EM_LINEINDEX
+ #202 +
+ #$B1#0 + // EM_SETSEL
+ #202 +
+ #$C2#0 + // EM_REPLACESEL
+ #201 + // ES_LEFT
+ #2#0 + // ES_RIGHT
+ #1#0 + // ES_CENTER
+ #203 +
+ #$C5#0 + // EM_SETLIMITTEXT
+ #202 +
+ #200#214#0; // EM_POSFROMCHAR
+ {$ELSE}
+ EditActions: TCommandActions = (
+ aClear: ClearText;
+ aAddText: nil;
+ aClick: 0;
+ aEnter: EN_SETFOCUS;
+ aLeave: EN_KILLFOCUS;
+ aChange: EN_CHANGE;
+ aSelChange: 0;
+ aGetCount: EM_GETLINECOUNT;
+ aSetCount: 0;
+ aGetItemLength: EM_LINELENGTH;
+ aGetItemText: EM_GETLINE;
+ aSetItemText: EM_REPLACESEL;
+ aGetItemData: 0;
+ aSetItemData: 0;
+ aAddItem: 0;
+ aDeleteItem: 0;
+ aInsertItem: 0;
+ aFindItem: 0;
+ aFindPartial: 0;
+ bItem2Pos: EM_LINEINDEX;
+ bPos2Item: EM_LINEFROMCHAR;
+ aGetSelCount: EM_GETSEL;
+ aGetSelected: 0;
+ aGetSelRange: EM_GETSEL;
+ aGetCurrent: EM_LINEINDEX;
+ aSetSelected: 0;
+ aSetCurrent: 0;
+ aSetSelRange: EM_SETSEL;
+ aExSetSelRange: 0;
+ aGetSelection: 0;
+ aReplaceSel: EM_REPLACESEL;
+ aTextAlignLeft: ES_LEFT;
+ aTextAlignRight: ES_RIGHT;
+ aTextAlignCenter: ES_CENTER;
+ bTextAlignMask: 0;
+ bVertAlignTop: 0;
+ bVertAlignCenter: 0;
+ bVertAlignBottom: 0;
+ aDir: 0;
+ aSetLimit: EM_SETLIMITTEXT;
+ aSetImgList: 0;
+ //---- aAutoSzX: 0;
+ //---- aAutoSzY: 6;
+ aSetBkColor: 0;
+ aItem2XY: EM_POSFROMCHAR;
+ );
+ {$ENDIF}
+
+const
+ {$IFDEF PACK_COMMANDACTIONS}
+ ListActions_Packed: PAnsiChar = Char(LIST_ACTIONS) +
+ #2#0 + // LBN_DBLCLK
+ #4#0 + // LBN_SETFOCUS
+ #5#0 + // LBN_KILLFOCUS
+ #201 +
+ #1#0 + // LBN_SELCHANGE
+ #$8B#1 + // LB_GETCOUNT
+ #$A7#1 + // LB_SETCOUNT
+ #$8A#1 + // LB_GETTEXTLEN
+ #$89#1 + // LB_GETTEXT
+ #201 +
+ #$99#1 + // LB_GETITEMDATA
+ #$9A#1 + // LB_SETITEMDATA
+ #$80#1 + // LB_ADDSTRING
+ #$82#1 + // LB_DELETESTRING
+ #$81#1 + // LB_INSERTSTRING
+ #$A2#1 + // LB_FINDSTRINGEXACT
+ #$8F#1 + // LB_FINDSTRING
+ #201 +
+ #$90#1 + // LB_GETSELCOUNT
+ #$87#1 + // LB_GETSEL
+ #201 +
+ #$88#1 + // LB_GETCURSEL
+ #$85#1 + // LB_SETSEL
+ #$86#1 + // LB_SETCURSEL
+ #209 +
+ #$8D#1 + // LB_DIR
+ #203 +
+ #$98#1; // LB_GETITEMRECT
+ {$ELSE}
+ ListActions: TCommandActions = (
+ aClear: ClearListbox;
+ aAddText: nil;
+ aClick: LBN_DBLCLK;
+ aEnter: LBN_SETFOCUS;
+ aLeave: LBN_KILLFOCUS;
+ aChange: 0;
+ aSelChange: LBN_SELCHANGE;
+ aGetCount: LB_GETCOUNT;
+ aSetCount: LB_SETCOUNT;
+ aGetItemLength: LB_GETTEXTLEN;
+ aGetItemText: LB_GETTEXT;
+ aSetItemText: 0;
+ aGetItemData: LB_GETITEMDATA;
+ aSetItemData: LB_SETITEMDATA;
+ aAddItem: LB_ADDSTRING;
+ aDeleteItem: LB_DELETESTRING;
+ aInsertItem: LB_INSERTSTRING;
+ aFindItem: LB_FINDSTRINGEXACT;
+ aFindPartial: LB_FINDSTRING;
+ bItem2Pos: 0;
+ bPos2Item: 0;
+ aGetSelCount: LB_GETSELCOUNT;
+ aGetSelected: LB_GETSEL;
+ aGetSelRange: 0;
+ aGetCurrent: LB_GETCURSEL;
+ aSetSelected: LB_SETSEL;
+ aSetCurrent: LB_SETCURSEL;
+ aSetSelRange: 0;
+ aExSetSelRange: 0;
+ aGetSelection: 0;
+ aReplaceSel: 0;
+ aTextAlignLeft: 0;
+ aTextAlignRight: 0;
+ aTextAlignCenter: 0;
+ bTextAlignMask: 0;
+ bVertAlignTop: 0;
+ bVertAlignCenter: 0;
+ bVertAlignBottom: 0;
+ aDir: LB_DIR;
+ aSetLimit: 0;
+ aSetImgList: 0;
+ //---- aAutoSzX: 0;
+ //---- aAutoSzY: 0;
+ aSetBkColor: 0;
+ aItem2XY: LB_GETITEMRECT;
+ );
+ {$ENDIF}
+
+const
+ {$IFDEF PACK_COMMANDACTIONS}
+ ComboActions_Packed: PAnsiChar = Char(COMBO_ACTIONS) +
+ #2#0 + // CBN_DBLCLK
+ #3#0 + // CBN_SETFOCUS
+ #4#0 + // CBN_KILLFOCUS
+ #5#0 + // CBN_EDITCHANGE
+ #15#0 + // CM_CBN_SELCHANGE
+ #$46#1 + // CB_GETCOUNT
+ #201 +
+ #$49#1 + // CB_GETLBTEXTLEN
+ #$48#1 + // CB_GETLBTEXT
+ #201 +
+ #$50#1 + // CB_GETITEMDATA
+ #$51#1 + // CB_SETITEMDATA
+ #$43#1 + // CB_ADDSTRING
+ #$44#1 + // CB_DELETESTRING
+ #$4A#1 + // CB_INSERTSTRING
+ #$58#1 + // CB_FINDSTRINGEXACT
+ #$4C#1 + // CB_FINDSTRING
+ #202 +
+ #$47#1 + // CB_GETCURSEL
+ #201 +
+ #$47#1 + // CB_GETCURSEL
+ #201 +
+ #$4E#1 + // CB_SETCURSEL
+ #209 +
+ #$45#1 + // CB_DIR
+ #203;
+ {$ELSE}
+ ComboActions: TCommandActions = (
+ aClear: ClearCombobox;
+ aAddText: nil;
+ aClick: CBN_DBLCLK;
+ aEnter: CBN_SETFOCUS;
+ aLeave: CBN_KILLFOCUS;
+ aChange: CBN_EDITCHANGE;
+ aSelChange: CM_CBN_SELCHANGE;
+ aGetCount: CB_GETCOUNT;
+ aSetCount: 0;
+ aGetItemLength: CB_GETLBTEXTLEN;
+ aGetItemText: CB_GETLBTEXT;
+ aSetItemText: 0;
+ aGetItemData: CB_GETITEMDATA;
+ aSetItemData: CB_SETITEMDATA;
+ aAddItem: CB_ADDSTRING;
+ aDeleteItem: CB_DELETESTRING;
+ aInsertItem: CB_INSERTSTRING;
+ aFindItem: CB_FINDSTRINGEXACT;
+ aFindPartial: CB_FINDSTRING;
+ bItem2Pos: 0;
+ bPos2Item: 0;
+ aGetSelCount: 0;
+ aGetSelected: CB_GETCURSEL;
+ aGetSelRange: 0;
+ aGetCurrent: CB_GETCURSEL;
+ aSetSelected: 0;
+ aSetCurrent: CB_SETCURSEL;
+ aSetSelRange: 0;
+ aExSetSelRange: 0;
+ aGetSelection: 0;
+ aReplaceSel: 0;
+ aTextAlignLeft: 0; //ES_LEFT;
+ aTextAlignRight: 0; //ES_RIGHT;
+ aTextAlignCenter: 0; //ES_CENTER;
+ bTextAlignMask: 0;
+ bVertAlignTop: 0;
+ bVertAlignCenter: 0;
+ bVertAlignBottom: 0;
+ aDir: CB_DIR;
+ aSetLimit: 0;
+ aSetImgList: 0;
+ //---- aAutoSzX: 0;
+ //---- aAutoSzY: 6;
+ aSetBkColor: 0;
+ );
+ {$ENDIF}
+
+const
+ {$IFDEF PACK_COMMANDACTIONS}
+ ListViewActions_Packed: PAnsiChar = Char( LISTVIEW_ACTIONS ) +
+ #203 +
+ #$9B#$FF + // LVN_ITEMCHANGED
+ #201 +
+ #4#$10 + // LVM_GETITEMCOUNT
+ #47#$10 + // LVM_SETITEMCOUNT
+ //#211 +
+ #206 + #8#$10 // LVM_DELETEITEM
+ + #204 +
+ #50#$10 + // LVM_GETSELECTEDCOUNT
+ #44#$10 + // LVM_GETITEMSTATE
+ #201 +
+ #12#$10 + // LVM_GENEXTITEM
+ #213 +
+ #3#$10 + // LVM_SETIMAGELIST
+ #1#$10 + // LVM_SETBKCOLOR
+ #14#$10; // LVM_GETITEMRECT
+ {$ELSE}
+ ListViewActions: TCommandActions = (
+ aClear: ClearListView;
+ aAddText: nil;
+ aClick: 0;
+ aEnter: 0;
+ aLeave: 0;
+ aChange: LVN_ITEMCHANGED;
+ aSelChange: 0;
+ aGetCount: LVM_GETITEMCOUNT;
+ aSetCount: LVM_SETITEMCOUNT;
+ aGetItemLength: 0;
+ aGetItemText: 0;
+ aSetItemText: 0;
+ aGetItemData: 0;
+ aSetItemData: 0;
+ aAddItem: 0;
+ aDeleteItem: LVM_DELETEITEM;
+ aInsertItem: 0;
+ aFindItem: 0;
+ aFindPartial: 0;
+ bItem2Pos: 0;
+ bPos2Item: 0;
+ aGetSelCount: { $8000 or} LVM_GETSELECTEDCOUNT;
+ aGetSelected: LVM_GETITEMSTATE;
+ aGetSelRange: 0;
+ aGetCurrent: LVM_GETNEXTITEM;
+ aSetSelected: 0;
+ aSetCurrent: 0;
+ aSetSelRange: 0;
+ aExSetSelRange: 0;
+ aGetSelection: 0;
+ aReplaceSel: 0;
+ aTextAlignLeft: 0;
+ aTextAlignRight: 0;
+ aTextAlignCenter: 0;
+ bTextAlignMask: 0;
+ bVertAlignTop: 0;
+ bVertAlignCenter: 0;
+ bVertAlignBottom: 0;
+ aDir: 0;
+ aSetLimit: 0;
+ aSetImgList: LVM_SETIMAGELIST;
+ //---- aAutoSzX: 0;
+ //---- aAutoSzY: 0;
+ aSetBkColor: LVM_SETBKCOLOR;
+ aItem2XY: LVM_GETITEMRECT;
+ );
+ {$ENDIF}
+
+const
+ {$IFDEF PACK_COMMANDACTIONS}
+ TreeViewActions_Packed: PAnsiChar = Char( TREEVIEW_ACTIONS ) +
+ #203 +
+ {$IFDEF UNICODE_CTRLS} #$34#$FE {$ELSE} #$65#$FE {$ENDIF} + // TVN_ENDLABELEDIT(W)
+ {$IFDEF UNICODE_CTRLS} #$3E#$FE {$ELSE} #$6E#$FE {$ENDIF} + // TVN_SELCHANGED(W)
+ #5#$11 + // TVM_GETCOUNT
+ #207 +
+ #1#$11 + // TVM_DELETEITEM
+ #221 +
+ #9#$11 + // TVM_SETIMAGELIST
+ #29#$11 + // TVM_SETBKCOLOR
+ #4#$11; // TVM_GETITEMRECT
+ {$ELSE}
+ TreeViewActions: TCommandActions = (
+ aClear: ClearTreeView;
+ aAddText: nil;
+ aClick: 0;
+ aEnter: 0;
+ aLeave: 0;
+ aChange: TVN_ENDLABELEDIT;
+ aSelChange: TVN_SELCHANGED;
+ aGetCount: TVM_GETCOUNT;
+ aSetCount: 0;
+ aGetItemLength: 0;
+ aGetItemText: 0;
+ aSetItemText: 0;
+ aGetItemData: 0;
+ aSetItemData: 0;
+ aAddItem: 0;
+ aDeleteItem: TVM_DELETEITEM;
+ aInsertItem: 0;
+ aFindItem: 0;
+ aFindPartial: 0;
+ bItem2Pos: 0;
+ bPos2Item: 0;
+ aGetSelCount: 0;
+ aGetSelected: 0;
+ aGetSelRange: 0;
+ aGetCurrent: 0;
+ aSetSelected: 0;
+ aSetCurrent: 0;
+ aSetSelRange: 0;
+ aExSetSelRange: 0;
+ aGetSelection: 0;
+ aReplaceSel: 0;
+ aTextAlignLeft: 0;
+ aTextAlignRight: 0;
+ aTextAlignCenter: 0;
+ bTextAlignMask: 0;
+ bVertAlignTop: 0;
+ bVertAlignCenter: 0;
+ bVertAlignBottom: 0;
+ aDir: 0; //CB_DIR;
+ aSetLimit: 0;
+ aSetImgList: TVM_SETIMAGELIST;
+ //---- aAutoSzX: 0;
+ //---- aAutoSzY: 0;
+ aSetBkColor: TVM_SETBKCOLOR;
+ aItem2XY: TVM_GETITEMRECT;
+ );
+ {$ENDIF}
+
+const
+ {$IFDEF PACK_COMMANDACTIONS}
+ TabControlActions_Packed: PAnsiChar = Char( TABCONTROL_ACTIONS ) +
+ #203 +
+ #200#$D9#$FD + // TCN_SELCHANGE
+ #200#$D9#$FD + // TCN_SELCHANGE
+ #4#$13 + // TCM_GETITEMCOUNT
+ #215 +
+ #11#$13 + // TCM_GETCURSEL
+ #201 +
+ #12#$13 + // TCM_SETCURSEL
+ #211 +
+ #3#$13 + // TCM_SETIMAGELIST
+ #201 +
+ #10#$13; // TCM_GETITEMRECT
+ {$ELSE}
+ TabControlActions: TCommandActions = (
+ aClear: ClearText;
+ aAddText: nil;
+ aClick: 0;
+ aEnter: 0;
+ aLeave: 0;
+ aChange: TCN_SELCHANGE;
+ aSelChange: TCN_SELCHANGE;
+ aGetCount: TCM_GETITEMCOUNT;
+ aSetCount: 0;
+ aGetItemLength: 0;
+ aGetItemText: 0;
+ aSetItemText: 0;
+ aGetItemData: 0;
+ aSetItemData: 0;
+ aAddItem: 0;
+ aDeleteItem: 0;
+ aInsertItem: 0;
+ aFindItem: 0;
+ aFindPartial: 0;
+ bItem2Pos: 0;
+ bPos2Item: 0;
+ aGetSelCount: 0;
+ aGetSelected: 0;
+ aGetSelRange: 0;
+ aGetCurrent: TCM_GETCURSEL;
+ aSetSelected: 0;
+ aSetCurrent: TCM_SETCURSEL; //TCM_SETCURFOCUS;
+ aSetSelRange: 0;
+ aExSetSelRange: 0;
+ aGetSelection: 0;
+ aReplaceSel: 0;
+ aTextAlignLeft: 0;
+ aTextAlignRight: 0;
+ aTextAlignCenter: 0;
+ bTextAlignMask: 0;
+ bVertAlignTop: 0;
+ bVertAlignCenter: 0;
+ bVertAlignBottom: 0;
+ aDir: 0; // CB_DIR;
+ aSetLimit: 0;
+ aSetImgList: TCM_SETIMAGELIST;
+ //---- aAutoSzX: 0;
+ //---- aAutoSzY: 0;
+ aSetBkColor: 0;
+ aItem2XY: TCM_GETITEMRECT;
+ );
+ {$ENDIF}
+
+{$IFNDEF NOT_USE_RICHEDIT}
+const
+ {$IFDEF PACK_COMMANDACTIONS}
+ RichEditActions_Packed: PAnsiChar = Char( RICHEDIT_ACTIONS ) +
+ #201 +
+ #0#1 + // EN_SETFOCUS
+ #0#2 + // EN_KILLFOCUS
+ #0#3 + // EN_CHANGE
+ #2#7 + // EN_SELCHANGE
+ #$BA#0 + // EM_GETLINECOUNT
+ #201 +
+ #$C1#0 + // EM_LINELENGTH
+ #$C4#0 + // EM_GETLINE
+ #$C2#0 + // EM_REPLACESEL
+ #207 +
+ #$BB#$C9 + // EM_LINEINDEX, EM_LINEFROMCHAR
+ #$B0#0 + // EM_GETSEL
+ #201 +
+ #$B0#0 + // EM_GETSEL
+ #$BB#0 + // EM_LINEINDEX
+ #203 +
+ #55#4 + // EM_EXSETSEL
+ #62#4 + // EM_GETSELTEXT
+ #$C2#0 + // EM_REPLACESEL
+ #201 + // ES_LEFT
+ #2#0 + // ES_RIGHT
+ #1#0 + // ES_CENTER
+ #203 +
+ #53#4 + // EM_EXLIMITTEXT
+ #201 +
+ #67#4 + // EM_SETBKGNDCOLOR
+ #200#214#0; // EM_POSFROMCHAR
+ {$ELSE}
+ RichEditActions: TCommandActions = (
+ aClear: ClearText;
+ aAddText: nil;
+ aClick: 0;
+ aEnter: EN_SETFOCUS;
+ aLeave: EN_KILLFOCUS;
+ aChange: EN_CHANGE;
+ aSelChange: EN_SELCHANGE;
+ aGetCount: EM_GETLINECOUNT;
+ aSetCount: 0;
+ aGetItemLength: EM_LINELENGTH;
+ aGetItemText: EM_GETLINE;
+ aSetItemText: EM_REPLACESEL;
+ aGetItemData: 0;
+ aSetItemData: 0;
+ aAddItem: 0;
+ aDeleteItem: 0;
+ aInsertItem: 0;
+ aFindItem: 0;
+ aFindPartial: 0;
+ bItem2Pos: EM_LINEINDEX;
+ bPos2Item: EM_LINEFROMCHAR;
+ aGetSelCount: EM_GETSEL;
+ aGetSelected: 0;
+ aGetSelRange: EM_GETSEL;
+ aGetCurrent: EM_LINEINDEX;
+ aSetSelected: 0;
+ aSetCurrent: 0;
+ aSetSelRange: 0;
+ aExSetSelRange: EM_EXSETSEL;
+ aGetSelection: EM_GETSELTEXT;
+ aReplaceSel: EM_REPLACESEL;
+ aTextAlignLeft: ES_LEFT;
+ aTextAlignRight: ES_RIGHT;
+ aTextAlignCenter: ES_CENTER;
+ bTextAlignMask: 0;
+ bVertAlignTop: 0;
+ bVertAlignCenter: 0;
+ bVertAlignBottom: 0;
+ aDir: 0;
+ aSetLimit: EM_EXLIMITTEXT;
+ aSetImgList: 0;
+ //---- aAutoSzX: 0;
+ //---- aAutoSzY: 0;
+ aSetBkColor: EM_SETBKGNDCOLOR;
+ aItem2XY: EM_POSFROMCHAR;
+ );
+ {$ENDIF}
+
+{$ENDIF NOT_USE_RICHEDIT}
+
+const
+ BaseFileMethods: TStreamMethods = (
+ fSeek: SeekFileStream;
+ fGetSiz: GetSizeFileStream;
+ fSetSiz: DummySetSize;
+ fRead: DummyReadWrite;
+ fWrite: DummyReadWrite;
+ fClose: CloseFileStream;
+ fCustom: nil;
+ );
+
+ MemoryMethods: TStreamMethods = (
+ fSeek: SeekMemStream;
+ fGetSiz: GetSizeMemStream;
+ fSetSiz: SetSizeMemStream;
+ fRead: ReadMemStream;
+ fWrite: WriteMemStream;
+ fClose: CloseMemStream;
+ fCustom: nil;
+ );
+
+ ConcatStreamMethods: TStreamMethods = (
+ fSeek: SeekConcatStream;
+ fGetSiz: GetSizeConcatStream;
+ fSetSiz: SetSizeConcatStream;
+ fRead: ReadConcatStream;
+ fWrite: WriteConcatStream;
+ fClose: CloseConcatStream;
+ fCustom: nil;
+ );
+
+ SubStreamMethods: TStreamMethods = (
+ fSeek: SeekSubStream;
+ fGetSiz: GetSizeSubStream;
+ fSetSiz: SetSizeSubStream;
+ fRead: ReadSubStream;
+ fWrite: WriteSubStream;
+ fClose: CloseSubStream;
+ fCustom: nil;
+ );
+{$ENDIF WIN_GDI}
+
+{$IFDEF DEBUG_MCK}
+procedure dummy_Log( const s: AnsiString );
+var mck_Log: procedure( const s: AnsiString ) = dummy_Log;
+{$ENDIF}
+
+type
+ TThemedElement = (
+ teButton,
+ teClock,
+ teComboBox,
+ teEdit,
+ teExplorerBar,
+ teHeader,
+ teListView,
+ teMenu,
+ tePage,
+ teProgress,
+ teRebar,
+ teScrollBar,
+ teSpin,
+ teStartPanel,
+ teStatus,
+ teTab,
+ teTaskBand,
+ teTaskBar,
+ teToolBar,
+ teToolTip,
+ teTrackBar,
+ teTrayNotify,
+ teTreeview,
+ teWindow
+ );
+
+var DrawThemeBackground: function(hTheme: DWORD; hdc: HDC; iPartId, iStateId: Integer;
+ const pRect: TRect; pClipRect: PRECT): HRESULT; stdcall;
+ OpenThemeData: function(hwnd: HWND; pszClassList: LPCWSTR): DWORD; stdcall;
+ ThemeLibrary: THandle;
+ IsThemeBackgroundPartiallyTransparent: function(hTheme: DWORD;
+ iPartId, iStateId: Integer): BOOL; stdcall;
+ DrawThemeParentBackground: function(hwnd: HWND; hdc: HDC; prc: PRECT): HRESULT; stdcall;
+ CloseThemeData: function(hTheme: DWORD): HRESULT; stdcall;
+ DrawThemeText: function(hTheme: DWORD; hdc: HDC; iPartId, iStateId: Integer;
+ pszText: LPCWSTR; iCharCount: Integer; dwTextFlags, dwTextFlags2: DWORD;
+ var pRect: TRect): HRESULT; stdcall;
+ IsThemeActive: function: BOOL; stdcall;
+ IsAppThemed: function: BOOL; stdcall;
+ GetThemeColor: function(hTheme: DWORD; iPartId, iStateId, iPropId: Integer;
+ var pColor: COLORREF): HRESULT; stdcall;
+
+const
+ themelib = 'uxtheme.dll';
+
+type
+ PThemedElementDetails = ^TThemedElementDetails;
+ TThemedElementDetails = record
+ Element: TThemedElement;
+ Part,
+ State: Integer;
+ end;
+ TThemedEdit = (
+ teEditDontCare,
+ teEditRoot,
+ teEditTextNormal, teEditTextHot, teEditTextSelected, teEditTextDisabled, teEditTextFocused, teEditTextReadOnly, teEditTextAssist,
+ teEditCaret
+ );
+
+type TOverrideScrollbarsProc = procedure(Sender: PControl);
+procedure DummyOverrideScrollbars(Sender: PControl);
+var OverrideScrollbars: TOverrideScrollbarsProc = DummyOverrideScrollbars;
+
+{$IFNDEF PAS_ONLY}
+function CrackStack_MapInResource( const MapName: KOLString; Max_length: Integer;
+ HandleSuspiciousAddresses: Boolean ): KOLString;
+{* Allows to list all procedures and functions called before current cracking
+ stack frames. This version loads map-file from the resource.
+ Important note: you must provide latest map file created at the last
+ application build in the resource! See also CrackStack_MapInFile below. }
+function CrackStack_MapInFile( const MapFileName: KOLString; Max_length: Integer;
+ HandleSuspiciousAddresses: Boolean ): KOLString;
+{* Allows to list all procedures and functions called before current cracking
+ stack frames. This version loads map-file from the file.
+ Important note: you must have the latest map file created at the last
+ application build on a path specified! For example, use path GetStartDir +
+ appname_wo_extention + '.map' and do not forget to set flag Map file -
+ Detailed in Project Options | Linker. Use flag HandleSuspiciousAddresses
+ to show all suspicious addresses found in stack (this may help to find
+ errors not shown even by Delphi debugger since stack frames in some cases give
+ no enough data). }
+{$ENDIF}
+//......... these declarations are here to stop hints from Delphi5 while compiling MCK:
+function CallTControlCreateWindow( Ctl: PControl ): Boolean;
+function DumpWindowed( c: PControl ): PControl;
+{$IFNDEF PAS_ONLY}
+function WndProcAppAsm( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+{$ENDIF}
+//22{$IFDEF ASM_VERSION}
+const ButtonClass: array[ 0..6 ] of KOLChar = ( 'B','U','T','T','O','N',#0 );
+//22{$ENDIF ASM_VERSION}
+{$IFDEF _D3orHigher}
+function WndProcUnicodeChars( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+{$ENDIF}
+procedure SetMouseEvent( Self_: PControl );
+function CompareIntegers( const Sender : Pointer; const e1, e2 : DWORD ) : Integer;
+function CompareDwords( const Sender : Pointer; const e1, e2 : DWORD ) : Integer;
+procedure SwapIntegers( const Sender : Pointer; const e1, e2 : DWORD );
+function _GetDIBPixelsTrueColorAlpha( Bmp: PBitmap; X, Y: Integer ): TColor;
+procedure _SetDIBPixelsTrueColorAlpha( Bmp: PBitmap; X, Y: Integer; Value: TColor );
+//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+{$IFDEF DEBUG_MONITOR_MESSAGES}
+var OnMonitorMessage: procedure( var M: TMsg; Enter_WndFunc: Boolean ) of object = nil;
+{$ENDIF}
+
+
+{$IFDEF _D2006orHigher}
+ {$I MCKfakeClasses200x.inc} // Dufa
+{$ENDIF}
+implementation
+
+ {$UNDEF CALL_INHERITED}
+{$IFDEF _D2orD3}
+ {$DEFINE CALL_INHERITED}
+{$ENDIF}
+{$IFnDEF NIL_EVENTS}
+ {$DEFINE CALL_INHERITED}
+{$ENDIF}
+
+{ -- don't remove this comment!!!
+ uses
+ //ShellAPI,
+ //commdlg // removing reference to commdlg decreases executable about 0.5 K
+ ; //, commctrl;
+ // in Delphi3, including of commctrl.pas increases executable
+ // onto about 30K. So, all needed definitions are copied here
+ // (see commctrl.inc).}
+
+{$IFDEF _X_}
+ {$undef uses_2}
+ {$IFNDEF NOT_USE_KOLMATH}
+ {$define uses_2}
+ {$ENDIF NOT_USE_KOLMATH}
+ {$IFDEF uses_2}
+ uses {$IFNDEF NOT_USE_KOLMATH} KOLmath
+ {$IFNDEF NOT_USE_EXCEPTION} , err
+ {$IFDEF REDECLARATION_INSERTED_AUTOMATICALLY}
+ , gdk2, pango, gtk2
+ {$ENDIF REDECLARATION_INSERTED_AUTOMATICALLY}
+ {$ENDIF NOT_USE_EXCEPTION}
+ {$ENDIF NOT_USE_KOLMATH};
+ {$ENDIF uses_2}
+{$ELSE}
+ {$IFDEF USE_GRUSH}
+ uses ToGRush;
+ {$ELSE}
+ {$IFDEF INPACKAGE}
+ uses mirror, SysUtils;
+ {$ENDIF INPACKAGE}
+ {$ENDIF USE_GRUSH}
+{$ENDIF _X_}
+
+{$IFDEF WIN}
+ {$IFNDEF FPC}
+ {$IFDEF UNICODE_CTRLS}
+ {$DEFINE implementation_part} {$I KOL_unicode.inc} {$UNDEF implementation_part}
+ {$ELSE} // ANSI_CTRLS
+ {$DEFINE implementation_part} {$I KOL_ansi.inc} {$UNDEF implementation_part}
+ {$ENDIF UNICODE_CTRLS}
+ {$ENDIF}
+{$ENDIF WIN}
+
+{$IFDEF DEBUG_MCK}
+procedure dummy_Log( const s: AnsiString );
+begin
+ //
+end;
+{$ENDIF}
+{$IFDEF WIN}
+type
+ PSHFileInfoA = ^TSHFileInfoA;
+ PSHFileInfoW = ^TSHFileInfoW;
+ PSHFileInfo = PSHFileInfoA;
+ _SHFILEINFOA = record
+ hIcon: HICON; { out: icon }
+ iIcon: Integer; { out: icon index }
+ dwAttributes: DWORD; { out: SFGAO_ flags }
+ szDisplayName: array [0..MAX_PATH-1] of AnsiChar; { out: display name (or path) }
+ szTypeName: array [0..79] of AnsiChar; { out: type name }
+ end;
+ _SHFILEINFOW = record
+ hIcon: HICON; { out: icon }
+ iIcon: Integer; { out: icon index }
+ dwAttributes: DWORD; { out: SFGAO_ flags }
+ szDisplayName: array [0..MAX_PATH-1] of WideChar; { out: display name (or path) }
+ szTypeName: array [0..79] of WideChar; { out: type name }
+ end;
+ _SHFILEINFO = {$IFDEF UNICODE_CTRLS} _SHFILEINFOW {$ELSE} _SHFILEINFOA {$ENDIF};
+ TSHFileInfoA = _SHFILEINFOA;
+ TSHFileInfoW = _SHFILEINFOW;
+ TSHFileInfo = {$IFDEF UNICODE_CTRLS} TSHFileInfoW {$ELSE} TSHFileInfoA {$ENDIF};
+ SHFILEINFOA = _SHFILEINFOA;
+ SHFILEINFOW = _SHFILEINFOW;
+ SHFILEINFO = {$IFDEF UNICODE_CTRLS} SHFILEINFOW {$ELSE} SHFILEINFOA {$ENDIF};
+
+const
+ SHGFI_ICON = $000000100; { get icon }
+ SHGFI_DISPLAYNAME = $000000200; { get display name }
+ SHGFI_TYPENAME = $000000400; { get type name }
+ SHGFI_ATTRIBUTES = $000000800; { get attributes }
+ SHGFI_ICONLOCATION = $000001000; { get icon location }
+ SHGFI_EXETYPE = $000002000; { return exe type }
+ SHGFI_SYSICONINDEX = $000004000; { get system icon index }
+ SHGFI_LINKOVERLAY = $000008000; { put a link overlay on icon }
+ SHGFI_SELECTED = $000010000; { show icon in selected state }
+ SHGFI_LARGEICON = $000000000; { get large icon }
+ SHGFI_SMALLICON = $000000001; { get small icon }
+ SHGFI_OPENICON = $000000002; { get open icon }
+ SHGFI_SHELLICONSIZE = $000000004; { get shell size icon }
+ SHGFI_PIDL = $000000008; { pszPath is a pidl }
+ SHGFI_USEFILEATTRIBUTES = $000000010; { use passed dwFileAttribute }
+
+function SHGetFileInfoA(pszPath: PAnsiChar; dwFileAttributes: DWORD;
+ var psfi: TSHFileInfo; cbFileInfo, uFlags: UINT): DWORD; stdcall;
+ external 'shell32.dll' name 'SHGetFileInfoA';
+{$IFDEF UNICODE_CTRLS}
+function SHGetFileInfoW(pszPath: PWideChar; dwFileAttributes: DWORD;
+ var psfi: TSHFileInfo; cbFileInfo, uFlags: UINT): DWORD; stdcall;
+ external 'shell32.dll' name 'SHGetFileInfoW';
+{$ENDIF UNICODE_CTRLS}
+
+type
+ FILEOP_FLAGS = Word;
+ PRINTEROP_FLAGS = Word;
+
+ PSHFileOpStructA = ^TSHFileOpStructA;
+ PSHFileOpStructW = ^TSHFileOpStructW;
+ PSHFileOpStruct = PSHFileOpStructA;
+ _SHFILEOPSTRUCTA = packed record
+ Wnd: HWND;
+ wFunc: UINT;
+ pFrom: PAnsiChar;
+ pTo: PAnsiChar;
+ fFlags: FILEOP_FLAGS;
+ fAnyOperationsAborted: BOOL;
+ hNameMappings: Pointer;
+ lpszProgressTitle: PAnsiChar; { only used if FOF_SIMPLEPROGRESS }
+ end;
+ _SHFILEOPSTRUCTW = packed record
+ Wnd: HWND;
+ wFunc: UINT;
+ pFrom: PWideChar;
+ pTo: PWideChar;
+ fFlags: FILEOP_FLAGS;
+ fAnyOperationsAborted: BOOL;
+ hNameMappings: Pointer;
+ lpszProgressTitle: PWideChar; { only used if FOF_SIMPLEPROGRESS }
+ end;
+ _SHFILEOPSTRUCT = _SHFILEOPSTRUCTA;
+ TSHFileOpStructA = _SHFILEOPSTRUCTA;
+ TSHFileOpStructW = _SHFILEOPSTRUCTW;
+ TSHFileOpStruct = TSHFileOpStructA;
+ SHFILEOPSTRUCTA = _SHFILEOPSTRUCTA;
+ SHFILEOPSTRUCTW = _SHFILEOPSTRUCTW;
+ SHFILEOPSTRUCT = SHFILEOPSTRUCTA;
+
+const
+ FO_MOVE = $0001;
+ FO_COPY = $0002;
+ FO_DELETE = $0003;
+ FO_RENAME = $0004;
+
+ FOF_MULTIDESTFILES = $0001;
+ FOF_CONFIRMMOUSE = $0002;
+ FOF_SILENT = $0004; { don't create progress/report }
+ FOF_RENAMEONCOLLISION = $0008;
+ FOF_NOCONFIRMATION = $0010; { Don't prompt the user. }
+ FOF_WANTMAPPINGHANDLE = $0020; { Fill in SHFILEOPSTRUCT.hNameMappings
+ Must be freed using SHFreeNameMappings }
+ FOF_ALLOWUNDO = $0040;
+ FOF_FILESONLY = $0080; { on *.*, do only files }
+ FOF_SIMPLEPROGRESS = $0100; { means don't show names of files }
+ FOF_NOCONFIRMMKDIR = $0200; { don't confirm making any needed dirs }
+ FOF_NOERRORUI = $0400; { don't put up error UI }
+
+
+{$IFDEF UNICODE_CTRLS}
+function SHFileOperationW(const lpFileOp: TSHFileOpStructW): Integer; stdcall;
+ external 'shell32.dll' name 'SHFileOperationW';
+{$ENDIF}
+
+function SHFileOperationA(const lpFileOp: TSHFileOpStructA): Integer; stdcall;
+ external 'shell32.dll' name 'SHFileOperationA';
+
+type
+ PNotifyIconDataA = ^TNotifyIconDataA;
+ PNotifyIconDataW = ^TNotifyIconDataW;
+ PNotifyIconData = PNotifyIconDataA;
+ _NOTIFYICONDATAA = record
+ cbSize: DWORD;
+ Wnd: HWND;
+ uID: UINT;
+ uFlags: UINT;
+ uCallbackMessage: UINT;
+ hIcon: HICON;
+ szTip: array [0..63] of AnsiChar;
+ end;
+ _NOTIFYICONDATAW = record
+ cbSize: DWORD;
+ Wnd: HWND;
+ uID: UINT;
+ uFlags: UINT;
+ uCallbackMessage: UINT;
+ hIcon: HICON;
+ szTip: array [0..63] of WideChar;
+ end;
+ _NOTIFYICONDATA = _NOTIFYICONDATAA;
+ TNotifyIconDataA = _NOTIFYICONDATAA;
+ TNotifyIconDataW = _NOTIFYICONDATAW;
+ TNotifyIconData = TNotifyIconDataA;
+ NOTIFYICONDATAA = _NOTIFYICONDATAA;
+ NOTIFYICONDATAW = _NOTIFYICONDATAW;
+ NOTIFYICONDATA = NOTIFYICONDATAA;
+
+const
+ NIM_ADD = $00000000;
+ NIM_MODIFY = $00000001;
+ NIM_DELETE = $00000002;
+
+ NIF_MESSAGE = $00000001;
+ NIF_ICON = $00000002;
+ NIF_TIP = $00000004;
+
+{$IFDEF UNICODE_CTRLS}
+function Shell_NotifyIcon(dwMessage: DWORD; lpData: PNotifyIconDataW): BOOL; stdcall;
+ external 'shell32.dll' name 'Shell_NotifyIconW';
+{$ELSE}
+function Shell_NotifyIcon(dwMessage: DWORD; lpData: PNotifyIconData): BOOL; stdcall;
+ external 'shell32.dll' name 'Shell_NotifyIconA';
+{$ENDIF UNICODE_CTRLS}
+
+{$IFDEF UNICODE_CTRLS}
+function ExtractIcon(hInst: HINST; lpszExeFileName: PKOLChar;
+ nIconIndex: UINT): HICON; stdcall;
+ external 'shell32.dll' name 'ExtractIconW';
+{$ELSE}
+function ExtractIcon(hInst: HINST; lpszExeFileName: PKOLChar;
+ nIconIndex: UINT): HICON; stdcall;
+ external 'shell32.dll' name 'ExtractIconA';
+{$ENDIF UNICODE_CTRLS}
+{$ENDIF WIN}
+{$IFDEF WIN_GDI}
+
+type
+ HDROP = Longint;
+
+function DragQueryPoint(Drop: HDROP; var Point: TPoint): BOOL; stdcall;
+ external 'shell32.dll' name 'DragQueryPoint';
+{$IFDEF UNICODE_CTRLS}
+function DragQueryFile(Drop: HDROP; FileIndex: UINT; FileName: PWideChar; cb: UINT): UINT; stdcall;
+ external 'shell32.dll' name 'DragQueryFileW';
+{$ELSE}
+function DragQueryFile(Drop: HDROP; FileIndex: UINT; FileName: PAnsiChar; cb: UINT): UINT; stdcall;
+ external 'shell32.dll' name 'DragQueryFileA';
+{$ENDIF UNICODE_CTRLS}
+procedure DragFinish(Drop: HDROP); stdcall;
+ external 'shell32.dll' name 'DragFinish';
+procedure DragAcceptFiles(Wnd: HWND; Accept: BOOL); stdcall;
+ external 'shell32.dll' name 'DragAcceptFiles';
+
+const
+ OFN_READONLY = $00000001;
+ OFN_OVERWRITEPROMPT = $00000002;
+ OFN_HIDEREADONLY = $00000004;
+ OFN_NOCHANGEDIR = $00000008;
+ OFN_SHOWHELP = $00000010;
+ OFN_ENABLEHOOK = $00000020;
+ OFN_ENABLETEMPLATE = $00000040;
+ OFN_ENABLETEMPLATEHANDLE = $00000080;
+ OFN_NOVALIDATE = $00000100;
+ OFN_ALLOWMULTISELECT = $00000200;
+ OFN_EXTENSIONDIFFERENT = $00000400;
+ OFN_PATHMUSTEXIST = $00000800;
+ OFN_FILEMUSTEXIST = $00001000;
+ OFN_CREATEPROMPT = $00002000;
+ OFN_SHAREAWARE = $00004000;
+ OFN_NOREADONLYRETURN = $00008000;
+ OFN_NOTESTFILECREATE = $00010000;
+ OFN_NONETWORKBUTTON = $00020000;
+ OFN_NOLONGNAMES = $00040000;
+ OFN_EXPLORER = $00080000;
+ OFN_NODEREFERENCELINKS = $00100000;
+ OFN_LONGNAMES = $00200000;
+ OFN_ENABLEINCLUDENOTIFY = $00400000;
+ OFN_ENABLESIZING = $00800000;
+ OFN_DONTADDTORECENT = $02000000;
+ OFN_FORCESHOWHIDDEN = $10000000; // Show All files including System and hidden files
+ OFN_EX_NOPLACESBAR = $00000001;
+ OFN_SHAREFALLTHROUGH = 2;
+ OFN_SHARENOWARN = 1;
+ OFN_SHAREWARN = 0;
+type
+ POpenFilename = ^TOpenFilename;
+ tagOFN = packed record
+ lStructSize: DWORD;
+ hWndOwner: HWND;
+ hInstance: HINST;
+ lpstrFilter: PKOLChar;
+ lpstrCustomFilter: PKOLChar;
+ nMaxCustFilter: DWORD;
+ nFilterIndex: DWORD;
+ lpstrFile: PKOLChar;
+ nMaxFile: DWORD;
+ lpstrFileTitle: PKOLChar;
+ nMaxFileTitle: DWORD;
+ lpstrInitialDir: PKOLChar;
+ lpstrTitle: PKOLChar;
+ Flags: DWORD;
+ nFileOffset: Word;
+ nFileExtension: Word;
+ lpstrDefExt: PKOLChar;
+ lCustData: LPARAM;
+ lpfnHook: function(Wnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): UINT stdcall;
+ lpTemplateName: PKOLChar;
+ {$IFDEF OpenSaveDialog_Extended}
+ //---------- added from Windows2000:
+ pvReserved: Pointer;
+ dwReserved: DWORD;
+ FlagsEx: DWORD;
+ {$ENDIF}
+ end;
+ TOpenFilename = tagOFN;
+ OPENFILENAME = tagOFN;
+{$IFDEF UNICODE_CTRLS}
+function GetOpenFileName(var OpenFile: TOpenFilename): Bool; stdcall;
+ external 'comdlg32.dll' name 'GetOpenFileNameW';
+function GetSaveFileName(var OpenFile: TOpenFilename): Bool; stdcall;
+ external 'comdlg32.dll' name 'GetSaveFileNameW';
+{$ELSE}
+function GetOpenFileName(var OpenFile: TOpenFilename): Bool; stdcall;
+ external 'comdlg32.dll' name 'GetOpenFileNameA';
+function GetSaveFileName(var OpenFile: TOpenFilename): Bool; stdcall;
+ external 'comdlg32.dll' name 'GetSaveFileNameA';
+{$ENDIF UNICODE_CTRLS}
+
+type
+ PChooseColorA = ^TChooseColorA;
+ PChooseColorW = ^TChooseColorW;
+ PChooseColor = PChooseColorA;
+ tagCHOOSECOLORA = packed record
+ lStructSize: DWORD;
+ hWndOwner: HWND;
+ hInstance: HWND;
+ rgbResult: COLORREF;
+ lpCustColors: ^COLORREF;
+ Flags: DWORD;
+ lCustData: LPARAM;
+ lpfnHook: function(Wnd: HWND; Message: UINT; wParam: WPARAM; lParam: LPARAM): UINT stdcall;
+ lpTemplateName: PAnsiChar;
+ end;
+ tagCHOOSECOLORW = packed record
+ lStructSize: DWORD;
+ hWndOwner: HWND;
+ hInstance: HWND;
+ rgbResult: COLORREF;
+ lpCustColors: ^COLORREF;
+ Flags: DWORD;
+ lCustData: LPARAM;
+ lpfnHook: function(Wnd: HWND; Message: UINT; wParam: WPARAM; lParam: LPARAM): UINT stdcall;
+ lpTemplateName: PWideChar;
+ end;
+ tagCHOOSECOLOR = tagCHOOSECOLORA;
+ TChooseColorA = tagCHOOSECOLORA;
+ TChooseColorW = tagCHOOSECOLORW;
+ TChooseColor = TChooseColorA;
+
+const
+ CC_RGBINIT = $00000001;
+ CC_FULLOPEN = $00000002;
+ CC_PREVENTFULLOPEN = $00000004;
+ CC_SHOWHELP = $00000008;
+ CC_ENABLEHOOK = $00000010;
+ CC_ENABLETEMPLATE = $00000020;
+ CC_ENABLETEMPLATEHANDLE = $00000040;
+ CC_SOLIDCOLOR = $00000080;
+ CC_ANYCOLOR = $00000100;
+
+function ChooseColor(var CC: TChooseColor): Bool; stdcall;
+ external 'comdlg32.dll' name 'ChooseColorA';
+
+{$IFDEF GDI}
+{$IFDEF CHK_BITBLT}
+procedure Chk_BitBlt_ShowError;
+var Rslt: Integer;
+begin
+ Rslt := GetLastError;
+ ShowMessage( 'BitBlt ERROR: ' + Int2Str( Rslt )
+ + ' ' + SysErrorMessage( Rslt ) );
+end;
+
+procedure Chk_BitBlt;
+var Rslt: Integer;
+begin
+ asm
+ MOV Rslt, EAX
+ end;
+ if Rslt = 0 then
+ begin
+ Chk_BitBlt_ShowError;
+ asm
+ int 3;
+ end;
+ end;
+end;
+{$ENDIF CHK_BITBLT}
+{$ENDIF GDI}
+
+{$ifdef _D2}
+procedure Assert( Cond: Boolean; const Msg: AnsiString );
+begin
+ if not Cond then
+ begin
+ AssertErrorProc( Msg, '', 0 );
+ asm
+ int 3;
+ end;
+ end;
+end;
+
+function CreateDIBSection(DC: HDC; const p2: TBitmapInfo; p3: UINT;
+ var p4: Pointer; p5: THandle; p6: DWORD): HBITMAP; stdcall;
+external gdi32 name 'CreateDIBSection';
+
+procedure _LStrFromPCharLen(var Dest: AnsiString; Source: PAnsiChar; Length: Integer);
+asm
+ { -> EAX pointer to dest }
+ { EDX source }
+ { ECX length }
+
+ PUSH EBX
+ PUSH ESI
+ PUSH EDI
+
+ MOV EBX,EAX
+ MOV ESI,EDX
+ MOV EDI,ECX
+
+ { allocate new string }
+
+ MOV EAX,EDI
+
+ CALL System.@NewAnsiString
+ MOV ECX,EDI
+ MOV EDI,EAX
+
+ TEST ESI,ESI
+ JE @@noMove
+
+ MOV EDX,EAX
+ MOV EAX,ESI
+ CALL Move
+
+ { assign the result to dest }
+
+@@noMove:
+ MOV EAX,EBX
+ CALL System.@LStrClr
+ MOV [EBX],EDI
+
+ POP EDI
+ POP ESI
+ POP EBX
+end;
+{$endif}
+
+{$IFDEF _D2009orHigher}
+{$IFNDEF PAS_ONLY}
+procedure _aLStrFromPCharLen(var Dest: AnsiString; Source: PAnsiChar; Length: Integer);
+asm
+ push 0
+ CALL System.@LStrFromPCharLen
+end;
+
+procedure _aLStrFromPChar(var Dest: AnsiString; Source: PAnsiChar);
+asm
+ push ecx
+ xor ecx, ecx
+ CALL System.@LStrFromPChar
+ pop ecx
+end;
+{$ENDIF}
+{$ENDIF}
+
+procedure InitCommonControls; external cctrl name 'InitCommonControls';
+
+type
+ TInitCommonControlsEx = packed record
+ dwSize: DWORD;
+ dwICC: DWORD;
+ end;
+ PInitCommonControlsEx = ^TInitCommonControlsEx;
+
+var ComCtl32_Module: HModule;
+{$IFDEF ASM_UNICODE}
+{$ELSE PASCAL}
+procedure DoInitCommonControls( dwICC: DWORD );
+var Proc: procedure( ICC: PInitCommonControlsEx ); stdcall;
+ ICC: TInitCommonControlsEx;
+begin
+ InitCommonControls;
+ if ComCtl32_Module = 0 then
+ ComCtl32_Module := LoadLibrary( 'comctl32' );
+ @ Proc := GetProcAddress( ComCtl32_Module, 'InitCommonControlsEx' );
+ {$IFDEF SAFE_CODE} // DoInitCommonControls should work usually. If not, the System is
+ if Assigned( Proc ) then // not in normal state, and should be repaired anyway.
+ {$ENDIF}
+ begin
+ ICC.dwSize := Sizeof( ICC );
+ ICC.dwICC := dwICC;
+ Proc( @ ICC );
+ end;
+end;
+{$ENDIF}
+
+const size_TRect = 16; // used often in assembler versions of code
+
+{$IFDEF ASM_VERSION}
+const
+ EmptyString: AnsiString = '';
+
+procedure EAX2PChar;
+asm
+ TEST EAX, EAX
+ JNZ @@exit
+ MOV EAX, offset[EmptyString]
+ //LEA EAX, [EmptyString]
+ //MOV EAX, [EmptyString]
+@@exit:
+end;
+
+procedure EDX2PChar;
+asm
+ TEST EDX, EDX
+ JNZ @@exit
+ MOV EDX, offset[EmptyString]
+@@exit:
+end;
+
+procedure ECX2PChar;
+asm
+ JECXZ @@convert
+ RET
+@@convert:
+ MOV ECX, offset[EmptyString]
+@@exit:
+end;
+
+procedure RemoveStr;
+asm { <- [ESP+4] = string to remove
+ -> ESP := ESP + 4
+ EAX = 0 }
+ POP EAX
+ XCHG EAX, [ESP]
+ PUSH EAX
+ MOV EAX, ESP
+ CALL System.@LStrClr
+ POP EAX
+end;
+
+{$IFDEF _D3orHigher}
+procedure RemoveWStr;
+asm { <- [ESP+4] = string to remove
+ -> ESP := ESP + 4
+ EAX = 0 }
+ POP EAX
+ XCHG EAX, [ESP]
+ PUSH EAX
+ MOV EAX, ESP
+ CALL System.@WStrClr
+ POP EAX
+end;
+{$ENDIF _D3orHigher}
+{$ENDIF ASM_VERSION}
+
+const PossibleColorBits : array[1..7] of Byte = ( 1, 4, 8, 16, 24, 32, 0 );
+
+function FindFilter( const Filter: KOLString): KOLString; forward;
+function WriteExMemoryStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize; forward;
+procedure CreateComboboxWnd( Combo: PControl ); forward;
+procedure ComboboxDropDown( Sender: PObj ); forward;
+function WndProcParentResize(Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean; forward;
+function WndProcResize(Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean; forward;
+function WndProcJustOneNotify( Control: PControl; var Msg: TMsg; var Rslt: Integer ) : Boolean; forward;
+function WndProcJustOne( Control: PControl; var Msg: TMsg; var Rslt: Integer ) : Boolean; forward;
+function WndProcTreeView( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean; forward;
+function CompareAnsiStrListItems( const Sender : Pointer; const e1, e2 : DWORD ) : Integer; forward;
+function CompareAnsiStrListItems_Case( const Sender : Pointer; const e1, e2 : DWORD ) : Integer; forward;
+function CompareStrListItems_NoCase( const Sender : Pointer; const e1, e2 : DWORD ) : Integer; forward;
+function CompareStrListItems_Case( const Sender : Pointer; const e1, e2 : DWORD ) : Integer; forward;
+procedure ApplyImageLists2Control( Sender: PControl ); forward;
+procedure ApplyImageLists2ListView( Sender: PControl ); forward;
+function OpenDirCallBack( Wnd: HWnd; Msg: DWORD; lParam, lpData: LParam ): Integer;
+ stdcall; forward;
+function OpenDirSelChangeCallBack( Wnd: HWnd; Msg: DWORD; lParam, lpData: LParam ):
+ Integer; stdcall; forward;
+function WndProcShowModal( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean; forward;
+function TimerProc( Wnd : HWnd; Msg : Integer; T : PTimer; CurrentTime : DWord ): Integer;
+ stdcall; forward;
+function PrepareBitmapHeader( W, H, BitsPerPixel: Integer ): PBitmapInfo; forward;
+procedure PreparePF16bit( DIBHeader: PBitmapInfo ); forward;
+procedure _RotateBitmapMono( var DstBmp: PBitmap; SrcBmp: PBitmap ); forward;
+procedure _RotateBitmap4bit( var DstBmp: PBitmap; SrcBmp: PBitmap ); forward;
+procedure _RotateBitmap8bit( var DstBmp: PBitmap; SrcBmp: PBitmap ); forward;
+procedure _RotateBitmap16bit( var DstBmp: PBitmap; SrcBmp: PBitmap ); forward;
+procedure _RotateBitmap2432bit( var DstBmp: PBitmap; SrcBmp: PBitmap ); forward;
+procedure _RotateBitmapRight( SrcBmp: PBitmap ); forward;
+procedure _SetDIBPixels1bit( Bmp: PBitmap; X, Y: Integer; Value: TColor ); forward;
+procedure _SetDIBPixelsPalIdx( Bmp: PBitmap; X, Y: Integer; Value: TColor ); forward;
+procedure _SetDIBPixels16bit( Bmp: PBitmap; X, Y: Integer; Value: TColor ); forward;
+procedure _SetDIBPixelsTrueColor( Bmp: PBitmap; X, Y: Integer; Value: TColor ); forward;
+procedure FillBmpWithBkColor( Bmp: PBitmap; DC2: HDC; oldWidth, oldHeight: Integer ); forward;
+procedure DetachBitmapFromCanvas( Sender: PBitmap ); forward;
+function ColorBits( ColorsCount : Integer ) : Integer; forward;
+procedure AlignChildrenProc(Sender: PObj); forward;
+function WndProcUpdate( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean; forward;
+function CollectTabControls( Form: PControl ): PList; forward;
+{$IFNDEF NOT_USE_RICHEDIT}
+function WndProc_RE_LinkNotify( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean; forward;
+{$ENDIF NOT_USE_RICHEDIT}
+function WndProc_DrawItem( Sender: PControl; var Msg: TMsg; var Rslt: Integer )
+ : Boolean; forward;
+function WndProcTabControl( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+ forward;
+function WndProcSplitter( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+ forward;
+function Tabulate2ControlEx( Self_: PControl; Key: DWORD; checkOnly: Boolean ): Boolean;
+ forward;
+procedure Tabulate2Next( Form: PControl; Dir: Integer ); forward;
+function Tabulate2Control( Self_: PControl; Key: DWORD; checkOnly: Boolean ): Boolean;
+ forward;
+function GetPrevCtrlBoundsRect( P: PControl; var R: TRect ): Boolean; forward;
+
+////////////---------------------------------------------------/////////////////
+function WndProcDateTimePickerNotify( Self_: PControl; var Msg: TMsg;
+ var Rslt: Integer ): Boolean; forward;
+
+////////////////////////////////////////////////////////////////////////////////
+{$IFNDEF PAS_ONLY}
+var MapFile: PKOLStrList;
+ LineNumbersFrom: Integer;
+ MaxCrackStackLen: Integer;
+ HandleSuspicious: Boolean;
+ BelowBasePtr: PDWORD;
+ CrackedStack: KOLString;
+
+function DoCrackSingleFrame( RetAddr: DWORD; BasePtr: DWORD ): Boolean;
+var i, j, R: Integer;
+ A, Prev_A, N, Prev_N: DWORD;
+ s, CurUnit: KOLString;
+ Add_string: KOLString;
+ Line_found: Boolean;
+begin
+ Result := FALSE;
+ if Length( CrackedStack ) > MaxCrackStackLen then Exit; {>>>>>>>>>>>>>>>>>>}
+ Result := TRUE;
+ if RetAddr >= $70000000 then
+ begin
+ CrackedStack := CrackedStack + #13#10'$' + Int2Hex( RetAddr, 8 );
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Result := FALSE;
+ if RetAddr < $400000 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+
+ if HandleSuspicious then
+ if (BelowBasePtr <> nil) and (BasePtr <> 0)
+ and (DWORD( BelowBasePtr ) < BasePtr) then
+ begin
+ BelowBasePtr := Pointer( DWORD( BelowBasePtr ) + 4 );
+ while DWORD( BelowBasePtr ) < BasePtr do
+ begin
+ A := BelowBasePtr^;
+ if (A > $400000) and (A < $700000) then
+ DoCrackSingleFrame( A, 0 );
+ BelowBasePtr := Pointer( DWORD( BelowBasePtr ) + 4 );
+ end;
+ end;
+ if BasePtr <> 0 then
+ BelowBasePtr := Pointer( BasePtr );
+
+ Add_string := '';
+
+ // 1st: find
+ Prev_A := 0;
+ for i := 0 to MapFile.Count-1 do
+ begin
+ s := MapFile.Items[ i ];
+ if s = '' then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ R := 0;
+ j := 1;
+ while (j <= Length( s )) and (s[j] <= ' ') do inc( j );
+ while (j <= Length( s )) and
+ ( (s[j] >= '0') and (s[j] <= '9') or
+ (s[j] >= 'A') and (s[j] <= 'F') ) do
+ begin
+ if s[j] <= '9' then R := R * 16 + Ord( s[j] ) - Ord( '0' )
+ else R := R * 16 + Ord( s[j] ) - Ord( 'A' ) + 10;
+ inc( j );
+ end;
+ if (j > Length( s )) or (s[ j ] <> ':') then Exit; {>>>>>>>>>>>>>>>>>>>}
+ inc( j );
+ A := 0;
+ while (j <= Length( s )) and
+ ( (s[j] >= '0') and (s[j] <= '9') or
+ (s[j] >= 'A') and (s[j] <= 'F') ) do
+ begin
+ if s[j] <= '9' then A := A * 16 + Ord( s[j] ) - Ord( '0' )
+ else A := A * 16 + Ord( s[j] ) - Ord( 'A' ) + 10;
+ inc( j );
+ end;
+ A := A + $401000;
+ if (Prev_A <= RetAddr) and (A > RetAddr) and (Prev_A > 0) and (R = 1) then
+ begin
+ s := MapFile.Items[ i-1 ];
+ j := pos( AnsiString(':'), s );
+ if j <= 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ s := Copy( s, j+1, MaxInt );
+ for j := 1 to Length( s ) do
+ if s[ j ] <= ' ' then
+ begin
+ s := Trim( Copy( s, j, MaxInt ) );
+ Add_string := #13#10;
+ if BasePtr = 0 then
+ Add_string := Add_string + '? ' + Int2Hex( RetAddr, 8 ) + ':';
+ Add_string := Add_string + s;
+ Result := TRUE;
+ break;
+ end;
+ end;
+ Prev_A := A;
+ if Result then break;
+ end;
+ if not Result then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+
+ // 2nd: find line no
+ Line_found := FALSE;
+ CurUnit := '';
+ Prev_N := 0;
+ Prev_A := 0;
+ for i := LineNumbersFrom to MapFile.Count-1 do
+ begin
+ s := MapFile.Items[ i ];
+ if Copy( s, 1, 4 ) = 'Line' then
+ begin
+ j := pos( AnsiString('('), s );
+ if j > 0 then
+ begin
+ s := Copy( s, j+1, MaxInt );
+ j := pos( AnsiString(')'), s );
+ if j > 0 then
+ s := Copy( s, 1, j-1 );
+ end;
+ CurUnit := s;
+ Prev_N := 0;
+ end else
+ if s <> '' then
+ begin
+ j := 1;
+ while j < Length( s ) do
+ begin
+ while (j <= Length( s )) and (s[j] <= ' ') do inc( j );
+ N := 0;
+ while (j <= Length( s )) and
+ (s[j] >= '0') and (s[j] <= '9') do
+ begin
+ N := N * 10 + Ord( s[j] ) - Ord( '0' );
+ inc( j );
+ end;
+ while (j <= Length( s )) and (s[j] <= ' ') do inc( j );
+ R := 0;
+ while (j < Length( s )) and
+ ( (s[j] >= '0') and (s[j] <= '9') or
+ (s[j] >= 'A') and (s[j] <= 'F') ) do
+ begin
+ if s[j] <= '9' then
+ R := R * 16 + Ord( s[j] ) - Ord( '0' )
+ else R := R * 16 + Ord( s[j] ) - Ord( 'A' ) + 10;
+ inc( j );
+ end;
+ while (j <= Length( s )) and (s[j] <= ' ') do inc( j );
+ if (j <= Length(s)) and (s[ j ] = ':') then inc( j );
+ while (j <= Length( s )) and (s[j] <= ' ') do inc( j );
+ A := 0;
+ while (j <= Length( s )) and
+ ( (s[j] >= '0') and (s[j] <= '9') or
+ (s[j] >= 'A') and (s[j] <= 'F') ) do
+ begin
+ if s[j] <= '9' then
+ A := A * 16 + Ord( s[j] ) - Ord( '0' )
+ else A := A * 16 + Ord( s[j] ) - Ord( 'A' ) + 10;
+ inc( j );
+ end;
+ A := A + $401000;
+ if (Prev_A <= RetAddr) and (A > RetAddr) then
+ begin
+ if (Prev_A > 0) and (Prev_N > 0) then
+ begin
+ Add_string := Add_string + ' in ' + CurUnit + ', line: ' +
+ Int2Str( Prev_N );
+ Line_found := TRUE;
+ end;
+ s := '';
+ break;
+ end;
+ Prev_N := N;
+ Prev_A := A;
+ if Line_found then break;
+ end;
+ end;
+ if Line_found then break;
+ end;
+ if not Line_found and (BasePtr = 0) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ CrackedStack := CrackedStack + Add_string;
+ if Length( CrackedStack ) > MaxCrackStackLen then
+ begin
+ CrackedStack := Copy( CrackedStack, 1, MaxCrackStackLen );
+ Result := FALSE; // stop cracking
+ end;
+end;
+
+procedure DoCrackStack;
+asm
+ mov edx, ebp
+@@loop:
+ mov ecx, [edx]
+ mov eax, [edx+4]
+ mov edx, ecx
+ push edx
+ call DoCrackSingleFrame
+ pop edx
+ test al, al
+ jnz @@loop
+end;
+
+function CrackStack( Max_length: Integer; HandleSuspiciousAddresses: Boolean ): KOLString;
+begin
+ TRY
+ MaxCrackStackLen := Max_length;
+ HandleSuspicious := HandleSuspiciousAddresses;
+ CrackedStack := '';
+ DoCrackStack;
+ EXCEPT
+ END;
+ Result := CrackedStack;
+end;
+
+procedure PrepareMapFile;
+var i, j: Integer;
+ s: KOLString;
+begin
+ for i := 0 to MapFile.Count-1 do
+ begin
+ s := MapFile.Items[ i ];
+ if pos( AnsiString('Publics by Value'), s ) > 0 then
+ begin
+ j := i;
+ if Trim( MapFile.Items[ j+1 ] ) = '' then
+ inc( j );
+ for j := j downto 0 do
+ MapFile.Delete( j );
+ for j := 0 to MapFile.Count-1 do
+ begin
+ s := Trim( MapFile.Items[ j ] );
+ if (s = '') and (LineNumbersFrom = 0) then
+ begin
+ LineNumbersFrom := j;
+ end;
+ if s = 'Bound resource files' then
+ begin
+ while MapFile.Count > j do
+ MapFile.Delete( j );
+ break;
+ end;
+ end;
+ break;
+ end;
+ end;
+end;
+
+function CrackStack_MapInResource( const MapName: KOLString; Max_length: Integer;
+ HandleSuspiciousAddresses: Boolean ): KOLString;
+var MapStrm: PStream;
+begin
+ Result := '';
+ if MapFile = nil then
+ begin
+ MapStrm := NewMemoryStream;
+ TRY
+ Resource2Stream( MapStrm, hInstance, PKOLChar( MapName ), PKOLChar(RT_RCDATA) );
+ if MapStrm.Size = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ MapFile := NewKOLStrList;
+ MapStrm.Position := 0;
+ MapFile.LoadFromStream( MapStrm, FALSE );
+ PrepareMapFile;
+ FINALLY
+ MapStrm.Free;
+ END;
+ end;
+ if MapFile = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := CrackStack( Max_length, HandleSuspiciousAddresses );
+end;
+
+function CrackStack_MapInFile( const MapFileName: KOLString; Max_length: Integer;
+ HandleSuspiciousAddresses: Boolean ): KOLString;
+begin
+ Result := '';
+ if MapFile = nil then
+ begin
+ MapFile := NewKOLStrList;
+ MapFile.LoadFromFile( MapFileName );
+ if MapFile.Count = 0 then
+ Free_And_Nil( MapFile )
+ else PrepareMapFile;
+ end;
+ if MapFile = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := CrackStack( Max_length, HandleSuspiciousAddresses );
+end;
+{$ENDIF _no_PAS_ONLY}
+
+{$IFDEF GRAPHCTL_XPSTYLES}
+ {$I visual_xp_styles.inc}
+{$ENDIF}
+
+{$IFDEF SNAPMOUSE2DFLTBTN}
+var FoundMsgBoxWnd: HWnd;
+
+function EnumProcSnapMouse2DfltBtn( W: HWnd; lParam: Integer ): BOOL; stdcall;
+var ClassBuf: array[ 0..31 ] of KOLChar;
+begin
+ GetClassName( W, ClassBuf, Sizeof( ClassBuf ) div Sizeof( KOLChar ) );
+ Result := TRUE;
+ if ClassBuf = '#32770' then
+ begin
+ FoundMsgBoxWnd := W;
+ Result := FALSE;
+ end;
+end;
+
+function WndProcSnapMouse2DfltBtn( Sender: PControl; var M: TMsg; var Rslt: Integer ): Boolean;
+var W: HWnd;
+ R: TRect;
+ P: TPoint;
+ SnapMouse: Integer;
+begin
+ SnapMouse := 0;
+ if SystemParametersInfo( {SPI_GETSNAPTODEFBUTTON}95, 0, @ SnapMouse, 0 ) then
+ if SnapMouse <> 0 then
+ begin
+ FoundMsgBoxWnd := 0;
+ EnumThreadWindows( GetCurrentThreadID, @ EnumProcSnapMouse2DfltBtn, 0 );
+ if FoundMsgBoxWnd <> 0 then
+ begin
+ W := GetWindow( FoundMsgBoxWnd, GW_CHILD );
+ while W <> 0 do
+ begin
+ if GetWindowLong( W, GWL_STYLE ) and BS_DEFPUSHBUTTON <> 0 then
+ begin
+ GetWindowRect( W, R );
+ P.X := (R.Left + R.Right) div 2;
+ P.Y := (R.Top + R.Bottom) div 2;
+ SetCursorPos( P.X, P.Y );
+ end;
+ W := GetWindow( W, GW_HWNDNEXT );
+ end;
+ Applet.DetachProc( @WndProcSnapMouse2DfltBtn );
+ end;
+ end;
+ Result := FALSE;
+end;
+{$ENDIF SNAPMOUSE2DFLTBTN}
+
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function MsgBox( const S: KOLString; Flags: DWORD ): DWORD;
+var Title: PKOLChar;
+begin
+ {$IFnDEF NO_SAFE_CODE} // MsgBox should be called when Applet already created
+ Title := nil; // (and yet not destroyed)
+ if assigned( Applet ) then
+ {$ENDIF}
+ begin
+ Title := PKOLChar( Applet.fCaption );
+ end;
+ {$IFDEF SNAPMOUSE2DFLTBTN}
+ {$IFDEF SAFE_CODE}
+ if Assigned( Applet ) then
+ {$ENDIF}
+ begin
+ Applet.AttachProc( WndProcSnapMouse2DfltBtn );
+ Applet.Postmsg( 0, 0, 0 );
+ end;
+ {$ENDIF}
+ Result := MessageBox( 0, PKOLChar( S ), Title, Flags );
+ {$IFDEF SNAPMOUSE2DFLTBTN}
+ {$IFnDEF NO_SAFE_CODE}
+ if Assigned( Applet ) then
+ {$ENDIF}
+ Applet.DetachProc( WndProcSnapMouse2DfltBtn );
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+procedure MsgOK( const S: KOLString );
+begin
+ MsgBox( S, MB_OK );
+end;
+
+{$IFDEF ASM_UNICODE}{$ELSE PASCAL}
+function ShowMsg( const S: KOLString; Flags: DWORD ): DWORD;
+var Title: PKOLChar;
+ Wnd: HWnd;
+begin
+ {$IFDEF SNAPMOUSE2DFLTBTN}
+ {$IFDEF SAFE_CODE}
+ if Assigned( Applet ) then
+ {$ENDIF}
+ Applet.AttachProc( WndProcSnapMouse2DfltBtn );
+ {$ENDIF}
+ {$IFDEF SAFE_CODE}
+ Title := nil;
+ Wnd := 0;
+ if assigned( Applet ) then
+ {$ENDIF}
+ begin
+ Title := PKOLChar( Applet.fCaption );
+ //{$IFNDEF SNAPMOUSE2DFLTBTN}
+ Wnd := Applet.Handle;
+ //{$ENDIF}
+ end;
+ Result := MessageBox( Wnd, PKOLChar( S ), Title, Flags );
+ {$IFDEF SNAPMOUSE2DFLTBTN}
+ {$IFDEF SAFE_CODE}
+ if Assigned( Applet ) then
+ {$ENDIF}
+ Applet.DetachProc( WndProcSnapMouse2DfltBtn );
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+procedure ShowMessage( const S: KOLString );
+begin
+ ShowMsg( S, MB_OK or MB_SETFOREGROUND or MB_DEFBUTTON1 );
+end;
+{$ENDIF GDI}
+
+{$IFDEF WIN_GDI}
+{$IFDEF PAS_ONLY}
+procedure SpeakerBeep( Freq: Word; Duration: DWORD );
+begin
+ Windows.Beep( Freq, Duration );
+end;
+{$ELSE}
+procedure SpeakerBeep( Freq: Word; Duration: DWORD );
+begin
+ if WinVer >= wvNT then
+ Windows.Beep( Freq, Duration )
+ else
+ begin
+ if Freq < 18 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Freq := 1193181 div Freq;
+ if Freq = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ asm
+ mov al,0b6H
+ out 43H,al
+ mov ax,Freq
+ //xchg al, ah
+ out 42h,al
+ xchg al, ah
+ out 42h,al
+ in al,61H
+ or al,03H
+ out 61H,al
+ end {$IFDEF F_P} [ 'EAX' ] {$ENDIF} ;
+ Sleep(Duration);
+ asm
+ in al,61H
+ and al,0fcH
+ out 61H,al
+ end {$IFDEF F_P} [ 'EAX' ] {$ENDIF} ;
+ end;
+end;
+{$ENDIF}
+{$ENDIF WIN_GDI}
+
+function SysErrorMessage(ErrorCode: Integer): KOLString;
+var
+ Len: Integer;
+ Buffer: array[0..255] of KOLChar;
+begin
+ Len := FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM or
+ FORMAT_MESSAGE_ARGUMENT_ARRAY, nil, ErrorCode, 0, Buffer,
+ SizeOf(Buffer), nil);
+ while (Len > 0) and ({(Buffer[Len - 1] >= #0) and} (Buffer[Len - 1] <= ' ')) do Dec(Len);
+ SetString(Result, Buffer, Len);
+ //Result := Trim( Result );
+end;
+{$ENDIF WIN_GDI}
+
+function GetShiftState: DWORD;
+{$IFDEF WIN}
+const Buttons: array[0..6] of Byte = ( VK_SHIFT, VK_CONTROL, VK_MENU, VK_LBUTTON,
+ VK_RBUTTON, VK_MBUTTON, VK_CAPITAL );
+ Flags: array[0..6] of Byte = ( MK_SHIFT, MK_CONTROL, MK_ALT, MK_LBUTTON,
+ MK_RBUTTON, MK_MBUTTON, MK_LOCK );
+var i, mask: Integer;
+{$ENDIF WIN} //todo: for Linux / GTK ?
+begin
+ Result := 0;
+ {$IFDEF WIN}
+ mask := 1;
+ for i := High( Buttons ) downto 0 do
+ begin
+ if GetKeyState( Buttons[ i ] ) and mask <> 0 then
+ Result := Result or Flags[ i ];
+ mask := $8000;
+ end;
+ {$ENDIF WIN}
+end;
+
+function MakeMethod( Data, Code: Pointer ): TMethod;
+begin
+ Result.Data := Data;
+ Result.Code := Code;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function MakeRect( Left, Top, Right, Bottom: Integer ): TRect; stdcall;
+begin
+ Result.Left := Left;
+ Result.Top := Top;
+ Result.Right:= Right;
+ Result.Bottom := Bottom;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function RectsEqual( const R1, R2: TRect ): Boolean;
+begin
+ Result := CompareMem( @R1, @R2, Sizeof( TRect ) );
+end;
+{$ENDIF PAS_VERSION}
+
+function RectsIntersected( const R1, R2: TRect ): Boolean;
+begin
+ Result := ((R1.Left <= R2.Left) and (R1.Right > R2.Left ) or
+ (R1.Left <= R2.Right) and (R1.Right >= R2.Right) or
+ (R1.Left >= R2.Left) and (R1.Right <= R2.Right))
+ and
+ ((R1.Top <= R2.Top) and (R1.Bottom > R2.Top) or
+ (R1.Top <= R2.Bottom) and (R1.Bottom >= R2.Bottom) or
+ (R1.Top >= R2.Top) and (R1.Bottom <= R2.Bottom)) ;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function PointInRect( const P: TPoint; const R: TRect ): Boolean;
+begin
+ Result := (P.x >= R.Left) and (P.x < R.Right)
+ and (P.y >= R.Top) and (P.y < R.Bottom);
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} // Pascal
+function OffsetPoint( const T: TPoint; dX, dY: Integer ): TPoint;
+begin
+ Result := MakePoint( T.X + dX, T.Y + dY );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} // Pascal
+function OffsetSmallPoint( const T: TSmallPoint; dX, dY: SmallInt ): TSmallPoint;
+begin
+ Result.x := T.x + dX;
+ Result.y := T.y + dY;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} // Pascal
+function Point2SmallPoint( const T: TPoint ): TSmallPoint;
+begin
+ Result.x := T.X;
+ Result.y := T.Y;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function SmallPoint2Point( const T: TSmallPoint ): TPoint;
+begin
+ Result := MakePoint( T.x, T.y );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function MakePoint( X, Y: Integer ): TPoint;
+begin
+ Result.x := X;
+ Result.y := Y;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} // Pascal
+function MakeSmallPoint( X, Y: Integer ): TSmallPoint;
+begin
+ Result.x := X;
+ Result.y := Y;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function MakeFlags( FlgSet: PDWORD; FlgArray: array of Integer): Integer;
+var I : Integer;
+ Mask : DWORD;
+begin
+ Result := 0;
+ Mask := FlgSet^;
+ for I := 0 to High( FlgArray ) do
+ begin
+ if (FlgArray[ I ] < 0) and not LongBool( Mask and 1 ) then
+ Result := Result or not FlgArray[ I ]
+ else
+ if (FlgArray[ I ] >= 0) and LongBool( Mask and 1 ) then
+ Result := Result or FlgArray[ I ];
+ Mask := Mask shr 1;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+function MakeDateTimeRange( D1, D2: TDateTime ): TDateTimeRange;
+begin
+ Result.FromDate := D1;
+ Result.ToDate := D2;
+end;
+
+procedure Swap( var X, Y: Integer );
+{$IFDEF F_P}
+var Tmp: Integer;
+begin
+ Tmp := X;
+ X := Y;
+ Y := Tmp;
+end;
+{$ELSE DELPHI}
+asm
+ MOV ECX, [EDX]
+ XCHG ECX, [EAX]
+ MOV [EDX], ECX
+end;
+{$ENDIF F_P/DELPHI}
+
+function Min( X, Y: Integer ): Integer;
+asm
+ {$IFDEF F_P}
+ MOV EAX, [X]
+ MOV EDX, [Y]
+ {$ENDIF F_P}
+ {$IFDEF USE_CMOV}
+ CMP EAX, EDX
+ CMOVG EAX, EDX
+ {$ELSE}
+ CMP EAX, EDX
+ JLE @@exit
+ MOV EAX, EDX
+@@exit:
+ {$ENDIF}
+end {$IFDEF F_P} [ 'EAX', 'EDX' ] {$ENDIF};
+
+function Max( X, Y: Integer ): Integer;
+asm
+ {$IFDEF F_P}
+ MOV EAX, [X]
+ MOV EDX, [Y]
+ {$ENDIF F_P}
+ {$IFDEF USE_CMOV}
+ CMP EAX, EDX
+ CMOVL EAX, EDX
+ {$ELSE}
+ CMP EAX, EDX
+ JGE @@exit
+ MOV EAX, EDX
+@@exit:
+ {$ENDIF}
+end {$IFDEF F_P} [ 'EAX', 'EDX' ] {$ENDIF};
+
+{$IFDEF REDEFINE_ABS}
+function Abs( X: Integer ): Integer;
+asm
+ {$IFDEF F_P}
+ MOV EAX, [X]
+ {$ENDIF F_P}
+ cdq
+ xor eax, edx
+ sub eax, edx
+end {$IFDEF F_P} [ 'EAX' ] {$ENDIF};
+{$ENDIF}
+
+function Sgn( X: Integer ): Integer;
+asm
+ CMP EAX, 0
+ {$IFDEF USE_CMOV}
+ MOV EDX, -1
+ CMOVL EAX, EDX
+ MOV EDX, 1
+ CMOVG EAX, EDX
+ {$ELSE}
+ JZ @@exit
+ MOV EAX, 1
+ JG @@exit
+ MOV EAX, -1
+@@exit:
+ {$ENDIF}
+end;
+
+function iSQRT( X: Integer ): Integer;
+{$IFDEF _D4orHigher}
+// new version is more efficient but code is not compatible with older compilers
+var I, N: Int64;
+begin
+ Result := 0;
+ while Result < X do
+ begin
+ I := 1;
+ while I > 0 do
+ begin
+ N := (Result + I) * (Result + I);
+ if N > X then
+ begin
+ I := I shr 1;
+ break;
+ end else
+ if N = X then
+ begin
+ Result := Result + I;
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ I := I * 2;
+ end;
+ if I <= 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := Result + I;
+ end;
+end;
+{$ELSE _D3 or below or FPC1}
+var m, y, b: DWORD;
+begin
+ m := $40000000;
+ y := 0;
+ while m <> 0 do // 16 times
+ begin
+ b := y or m;
+ y := y shr 1;
+ if x >= b then
+ begin
+ x := x - b;
+ y := y or m;
+ end;
+ m := m shr 2;
+ end;
+ Result := y;
+end;
+{$ENDIF}
+
+function iCbrt( X: DWORD ): Integer;
+var s: Integer;
+ y, b: DWORD;
+begin
+ s := 30;
+ y := 0;
+ while s >= 0 do // 11 times
+ begin
+ y := 2 * y;
+ b := (3 * y * (y+1) + 1) shl s;
+ s := s - 3;
+ if x >= b then
+ begin
+ x := x - b;
+ y := y + 1;
+ end;
+ end;
+ Result := y;
+end;
+{$IFDEF WIN_GDI}
+
+{$IFDEF ASM_DC}
+procedure StartDC;
+asm
+ { <- EBX : PBitmap
+ -> EAX = dc
+ [ESP+8] = var dc
+ [ESP+4] = var SaveBmp
+ }
+ PUSH 0
+ CALL CreateCompatibleDC
+ POP EDX
+ PUSH EAX
+ PUSH EDX
+ MOV EAX, EBX
+ CALL [EBX].TBitmap.fDetachCanvas
+ MOV EAX, EBX
+ CALL TBitmap.GetHandle
+ PUSH EAX
+ PUSH dword ptr [ESP+8]
+ CALL SelectObject
+ POP EDX
+ PUSH EAX
+ PUSH EDX
+ MOV EAX, [ESP+8]
+end;
+
+procedure FinishDC;
+asm
+ POP ECX
+ POP EAX
+ POP EDX
+ PUSH ECX
+ PUSH EDX
+ PUSH EAX
+ PUSH EDX
+ CALL SelectObject
+ CALL DeleteDC
+end;
+{$ENDIF ASM_DC}
+
+function EnumDynHandlers( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+ forward;
+
+{$ENDIF WIN_GDI}
+
+procedure DummyObjProc( Sender: PObj );
+begin // 1-2-3 parameters, no result
+end;
+
+function DummyProc123_TRUE( Dummy: Pointer; Sender: PControl; param3: Integer ): Boolean;
+begin Result := TRUE; // 1-2-3 params, Result = TRUE
+end;
+
+function DummyProc123_0( Dummy: Pointer; Sender: PObj; param3: Integer ): Integer;
+begin Result := 0; // 1-2-3 params, Result = 0
+end;
+
+function DummyProc4_TRUE( Dummy: Pointer; Sender: PControl; p3, p4: Integer ): Boolean;
+begin Result := TRUE; // 4 params, result = TRUE
+end;
+
+function DummyProc5_TRUE( Dummy: Pointer; Sender: PControl; p3, p4, p5: Integer): Boolean;
+begin Result := TRUE; // 5 params, result = TRUE
+end;
+
+procedure DummyOnLVDataProc( Dummy: Pointer; Sender: PControl; Idx, SubItem: Integer;
+ var Txt: KOL_String; var ImgIdx: Integer; var State: DWORD;
+ var Store: Boolean );
+begin // 8 params
+end;
+
+function DummyProc4_0( Dummy: Pointer; Sender: PControl; p3, p4: Integer ): Integer;
+begin Result := 0; // 4 params, Result = 0
+end;
+
+function DummyOnDrawItemProc( Dummy:Pointer; Sender: PObj; DC: HDC; const Rect: TRect; ItemIdx: Integer;
+ DrawAction: TDrawAction; ItemState: TDrawState ): Boolean;
+begin Result := FALSE; // 7 params, Result = FALSE
+end;
+
+function DummyOnLVCustomDrawProc( Dummy: Pointer; Sender: PControl; DC: HDC; Stage: DWORD;
+ ItemIdx, SubItemIdx: Integer; const Rect: TRect;
+ ItemState: TDrawState; var TextColor, BackColor: TColor ): DWORD;
+begin Result := 0; // 10 params, Result = 0
+end;
+
+function DummyOnSBBeforeScrollProc(Dummy: Pointer; Sender: PControl;
+ OldPos, NewPos: Integer; Cmd: Word; var AllowChange: Boolean): Boolean;
+begin Result := FALSE; // 6 params
+end;
+
+var DummyProcTable: array[ 0..11 ] of Pointer = ( @DummyObjProc, @DummyProc123_TRUE,
+ @DummyProc123_0, @DummyProc4_TRUE, @DummyProc5_TRUE, @DummyOnLVDataProc,
+ @DummyProc4_0, @DummyOnDrawItemProc, @DummyOnLVCustomDrawProc,
+ @DummyOnSBBeforeScrollProc, @WndFunc,
+ {$IFDEF USE_GRAPHCTLS} @InvalidateWindowed {$ELSE} @DummyObjProc {$ENDIF} );
+const idummy123 = 0; //+
+ idummy123_TRUE = 1; //+
+ idummy123_0 = 2; //+
+ idummy4_TRUE = 3; //+
+ idummy5_TRUE = 4; //+
+ idummy8 = 5; //+
+ idummy4_0 = 6; //+
+ idummy7 = 7; //+
+ idummy10 = 8; //+
+ idummy6 = 9; //+
+ iWndFunc = 10; //+
+ iInvalidateWindowed = 11; //+
+const InitEventsTable: array[ 0..idx_LastEvent ] of Byte = (
+ idummy123_0 + iWndFunc shl 4, //idx_fOnMessage + idx_fWndFunc = 0; idx_fWndFunc = 69;
+ idummy123_0 + iInvalidateWindowed shl 4, //idx_fOldOnMessage + idx_fDoInvalidate = 1; idx_fDoInvalidate = 70;
+ idummy123 + idummy123_0 shl 4, //idx_fOnClick = 2; idx_fOnDynHandlers = 71;
+ idummy123 + idummy123_0 shl 4, //idx_fOnMouseDown = 3; idx_fPass2DefProc = 72;
+ idummy123 + idummy123_0 shl 4, //idx_fOnMouseUp = 4; idx_fWndProcKeybd = 73;
+ idummy123 + idummy123_0 shl 4, //idx_fOnMouseMove = 5; idx_fControlClick = 74;
+ idummy123 + idummy123_0 shl 4, //idx_fOnMouseDblClk = 6; idx_fAutoSize = 75;
+ idummy123 + idummy123_0 shl 4, //idx_fOnMouseWheel = 7; idx_fGotoControl = 77;
+ idummy123 + idummy123_0 shl 4, //idx_fOnMouseEnter = 8; idx_fNotifyChild = 78;
+ idummy123 + idummy123_0 shl 4, //idx_fOnMouseLeave = 9; idx_fScrollChildren = 79;
+ idummy123_TRUE + idummy123_0 shl 4, //idx_fOnTestMouseOver = 10; idx_fCreateWndExt = 80;
+ idummy123 + idummy123_0 shl 4, //idx_fGraphCtlMouseEvent = 11; idx_fExMsgProc = 81;
+ idummy123, //idx_fMouseLeaveProc = 12;
+ idummy5_TRUE, //idx_fOnScroll = 13;
+ idummy4_TRUE, //idx_fOnChar = 14;
+ idummy4_TRUE, //idx_fOnDeadChar = 15;
+ idummy4_TRUE, //idx_fOnKeyUp = 16;
+ idummy4_TRUE, //idx_fOnKeyDown = 17;
+ idummy123, //idx_fOnChangeCtl = 18;
+ idummy123, //idx_fOnEnter = 19;
+ idummy123, //idx_fOnLeave = 20;
+ idummy123, //idx_fLeave = 21;
+ idummy123, //idx_fOnPaint = 22;
+ idummy123, //idx_fOnPaint2 = 23;
+ idummy123, //idx_fOnPrepaint = 24;
+ idummy123, //idx_fOnPostPaint = 25;
+ idummy123, //idx_fPaintProc = 26;
+ idummy123, //idx_fOnEraseBkgnd = 27;
+ idummy7, //idx_fOnDrawItem = 28;
+ idummy123_0, //idx_fOnMeasureItem = 29;
+ idummy6, //idx_fDragCallback = 30;
+ idummy123, //idx_fOnSelChange = 31;
+ idummy123, //idx_fOnResize = 32;
+ idummy123, //idx_fOnHide = 33;
+ idummy123, //idx_fOnShow = 34;
+ idummy123, //idx_fOnClose = 35;
+ idummy123, //idx_fOnMove = 36;
+ idummy123, //idx_fOnMoving = 37;
+ idummy4_0, //idx_fOnHelp = 38;
+ idummy123, //idx_fOnQueryEndSession = 39;
+ idummy123, //idx_fOnMinimize = 40;
+ idummy123, //idx_fOnMaximize = 41;
+ idummy123, //idx_fOnRestore = 42;
+ idummy10, //idx_fOnLVCustomDraw = 43;
+ idummy5_TRUE, //idx_fOnEndEditLVITem = 44;
+ idummy8, //idx_fOnLVData = 45;
+ idummy4_0, //idx_fOnCompareLVItems = 46;
+ idummy6, //idx_FOnLVStateChange = 47;
+ idummy123, //idx_fOnDeleteLVItem = 48;
+ idummy123, //idx_fOnColumnClick = 49;
+ idummy6, //idx_FOnSBBeforeScroll = 54;
+ idummy123, //idx_FOnSBScroll = 55;
+ idummy123, //idx_FOnDropDown = 56;
+ idummy123, //idx_FOnCloseUp = 57;
+ idummy4_TRUE, //idx_FOnSplit = 58;
+ idummy123, //idx_FOnProgress = 59;
+ idummy123_0, //idx_FOnBitBtnDraw = 60;
+ idummy123, //idx_FOnTVBeginDrag = 61;
+ idummy123_TRUE, //idx_FOnTVBeginEdit = 62;
+
+ idummy4_TRUE, //idx_FOnTVEndEdit = 50;
+ idummy4_0, //idx_FOnTVExpanding = 52;
+ idummy4_TRUE, //idx_FOnTVExpanded = 51;
+ idummy4_TRUE, //idx_FOnTVSelChanging = 53;
+
+ idummy123, //idx_FOnTVDelete = 63;
+ idummy5_TRUE, //idx_FOnDTPUserString = 64;
+ idummy123, //idx_FOnREInsModeChg = 65;
+ idummy123, //idx_FOnREOverURL = 66;
+ idummy123, //idx_FOnREURLClick = 67;
+ idummy4_0 //idx_fOnDropFiles = 68;
+ );
+
+{ _TObj }
+
+procedure Free_And_Nil( var Obj );
+var Obj1: PObj;
+begin
+ Obj1 := PObj( Obj );
+ Pointer( Obj ) := nil;
+ Obj1.Free;
+end;
+
+procedure _TObj.Init;
+begin
+{$IFDEF _D2orD3}
+ //FillChar( Pointer( Integer(@Self) + 4 )^, Sizeof( Self ) - 4, 0 );
+ ZeroMemory( Pointer( Integer(@Self) + 4 ), Sizeof( Self ) - 4 );
+{$ENDIF}
+end;
+
+function _TObj.VmtAddr: Pointer;
+asm
+ MOV EAX, [EAX]
+end;
+
+{ TObj }
+
+class function TObj.AncestorOfObject(Obj: Pointer): Boolean;
+asm
+ MOV ECX, [EAX]
+ MOV EAX, EDX
+ JMP @@loop1
+@@loop:
+ MOV EAX,[EAX]
+@@loop1:
+ TEST EAX,EAX
+ JE @@exit
+ CMP EAX,ECX
+ JNE @@loop
+@@success:
+ MOV AL,1
+@@exit:
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+constructor TObj.Create;
+begin
+ Init;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF OLD_REFCOUNT}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TObj.DoDestroy;
+begin
+ {$IFDEF OLD_REFCOUNT}
+ if fRefCount > 0 then
+ begin
+ if not LongBool( fRefCount and 1) then
+ Dec( fRefCount, 2 );
+ RefDec;
+ end else
+ Self.Destroy;
+ if fRefCount <> 0 then
+ begin
+ if not LongBool( fRefCount and 1) then
+ Dec( fRefCount );
+ end else
+ Self.Destroy;
+ {$ELSE}
+ if fRefCount > 0 then
+ RefDec
+ else Self.Destroy;
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF OLD_REFCOUNT}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TObj.RefDec: Integer;
+begin
+ Result := 0; // stop Delphi alerting the Warning
+ if @ Self = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Dec( fRefCount, 2 );
+ {$IFDEF OLD_REFCOUNT}
+ if (fRefCount < 0) and LongBool(fRefCount and 1) then
+ Destroy;
+ {$ELSE}
+ if fRefCount < 0 then
+ Destroy;
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TObj.RefInc;
+begin
+ Inc( fRefCount, 2 );
+end;
+
+function TObj.VmtAddr: Pointer;
+asm
+ //MOV EAX, [EAX - 4]
+ MOV EAX, [EAX]
+end;
+
+function TObj.InstanceSize: Integer;
+asm
+ //MOV EAX, [EAX]
+ MOV EAX, [EAX-4]
+end;
+
+{$IFDEF OLD_FREE}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+procedure TObj.Free;
+begin
+ RefDec;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF OLD_FREE}
+
+{$UNDEF ASM_LOCAL}
+{$IFDEF ASM_VERSION} {$DEFINE ASM_LOCAL} {$ENDIF}
+{$IFDEF CRASH_DEBUG} {$UNDEF ASM_LOCAL} {$ENDIF}
+{$IFDEF ASM_DEBUG} {$DEFINE ASM_LOCAL} {$ENDIF}
+
+{$IFDEF ASM_LOCAL}
+{$ELSE PAS_VERSION} //Pascal
+destructor TObj.Destroy;
+begin
+ Final;
+
+ {$IFDEF DEBUG_ENDSESSION}
+ if EndSession_Initiated then
+ LogFileOutput( GetStartDir + 'es_debug.txt',
+ 'FINALLED: ' + Int2Hex( DWORD( @ Self ), 8 )
+ {$IFDEF USE_NAMES}
+ + ' (name:' + FName + ')'
+ {$ENDIF}
+ );
+ {$ENDIF}
+ {$IFDEF USE_NAMES}
+ fName := '';
+ if fNamedObjList <> nil then
+ Free_And_Nil(fNamedObjList);
+ {$ENDIF}
+ {$IFDEF CRASH_DEBUG}
+ FillChar( Pointer( Integer(@Self) + 4 )^, Sizeof( Self ) - 4, #$DD );
+ {$ENDIF}
+ FreeMem( @ Self );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}
+ {$DEFINE ASM_TLIST}
+{$IFDEF TLIST_FAST}
+ {$UNDEF ASM_TLIST}
+{$ENDIF}
+{$ENDIF}
+
+{$IFDEF ASM_TLIST}
+procedure TObj.Final;
+asm //cmd //opd
+ PUSH EBX
+ XCHG EBX, EAX
+ XOR ECX, ECX
+ XCHG ECX, [EBX].fOnDestroy.TMethod.Code
+ JECXZ @@freeloop
+ MOV EDX, EBX
+ MOV EAX, [EDX].fOnDestroy.TMethod.Data
+ CALL ECX
+@@freeloop:
+ MOV ECX, [EBX].fAutoFree
+ JECXZ @@eloop
+ MOV EDX, [ECX].TList.fItems
+ MOV ECX, [ECX].TList.fCount
+ JECXZ @@eloop
+ MOV EAX, [EDX+ECX*4-4]
+ MOV EDX, [EDX+ECX*4-8]
+ PUSH EAX
+ PUSH EDX
+ MOV EAX, [EBX].fAutoFree
+ LEA EDX, [ECX-2]
+ XOR ECX, ECX
+ MOV CL, 2
+ CALL TList.DeleteRange
+ POP EDX
+ POP EAX
+ CALL EDX
+ JMP @@freeloop
+@@eloop:
+ XOR EAX, EAX
+ XCHG [EBX].fAutoFree, EAX
+ CALL TObj.RefDec
+@@exit:
+ POP EBX
+end;
+{$ELSE PAS_VERSION} //Pascal
+procedure TObj.Final;
+var N: Integer;
+ ProcMethod: TMethod;
+ {$IFDEF _D2orD3}
+ Proc: TObjectMethod;
+ {$ELSE}
+ Proc: TObjectMethod Absolute ProcMethod;
+ {$ENDIF}
+var Destroy_evnt: TOnEvent;
+begin
+ if Assigned( fOnDestroy ) then
+ begin
+ Destroy_evnt := fOnDestroy;
+ fOnDestroy := nil;
+ Destroy_evnt( @Self );
+ end;
+ while (fAutoFree <> nil) and (fAutoFree.fCount > 0) do
+ begin
+ N := fAutoFree.fCount - 2;
+ ProcMethod.Code := fAutoFree.Items[ N ];
+ ProcMethod.Data := fAutoFree.Items[ N + 1 ];
+ fAutoFree.DeleteRange( N, 2 );
+ {$IFDEF _D2orD3}
+ Proc := TObjectMethod( ProcMethod );
+ {$ENDIF}
+ Proc;
+ end;
+ fAutoFree.Free;
+ fAutoFree := nil;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TObj.Add2AutoFree(Obj: PObj);
+begin
+ if fAutoFree = nil then
+ fAutoFree := NewList;
+ fAutoFree.Insert( 0, Obj );
+ fAutoFree.Insert( 0, Pointer( @TObj.RefDec ) );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TObj.Add2AutoFreeEx( Proc: TObjectMethod );
+{$IFDEF F_P}
+var Ptr1, Ptr2: Pointer;
+{$ENDIF F_P}
+begin
+ if fAutoFree = nil then
+ fAutoFree := NewList;
+ {$IFDEF F_P}
+ asm
+ MOV EAX, [Proc]
+ MOV [Ptr1], EAX
+ MOV EAX, [Proc+4]
+ MOV [Ptr2], EAX
+ end [ 'EAX' ];
+ fAutoFree.Insert( 0, Ptr2 );
+ fAutoFree.Insert( 0, Ptr1 );
+ {$ELSE DELPHI}
+ fAutoFree.Insert( 0, Pointer( TMethod( Proc ).Data ) );
+ fAutoFree.Insert( 0, Pointer( TMethod( Proc ).Code ) );
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+procedure TObj.RemoveFromAutoFree(Obj: PObj);
+var i: Integer;
+begin
+ if fAutoFree <> nil then
+ begin
+ i := fAutoFree.IndexOf( Obj );
+ if i >= 0 then
+ begin
+ fAutoFree.DeleteRange( i and not 1, 2 );
+ if fAutoFree.Count = 0 then
+ Free_And_Nil( fAutoFree );
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TObj.RemoveFromAutoFreeEx(Proc: TObjectMethod);
+var i: Integer;
+begin
+ if fAutoFree <> nil then
+ begin
+ for i := 0 to fAutoFree.Count-2 do
+ if (fAutoFree.Items[ i ] = TMethod( Proc ).Data) and
+ (fAutoFree.Items[ i+1 ] = TMethod( Proc ).Code) then
+ begin
+ fAutoFree.Delete( i );
+ fAutoFree.Delete( i );
+ break;
+ end;
+ end;
+end;
+
+{$IFDEF USE_NAMES}
+procedure TObj.SetName( NewOwnerObj: PObj; NewName: AnsiString );
+{$IFDEF UNIQUE_NAMES}
+var i: Integer;
+{$ENDIF}
+begin
+ if (FOwnerObj <> nil) then
+ if FOwnerObj <> NewOwnerObj then
+ begin
+ FOwnerObj.fNamedObjList.Remove( @ Self );
+ end;
+ FOwnerObj := NewOwnerObj;
+ if NewOwnerObj = nil then
+ begin
+ if NewName = '' then
+ begin
+ fName := '';
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ // çäåñü òîò ñëó÷àé, êîãäà â ïðèëîæåíèè áåç Applet'à óñòàíàâëèâàåòñÿ
+ // èìÿ äëÿ ãëàâíîé ôîðìû (íàâåðíîå)
+ FOwnerObj := @ Self; // âëàäåëüöåì ñïèñêà èìåíîâàííûõ îáúåêòîâ ñòàíîâèòñÿ
+ // ñàì îáúåêò. Äëÿ âûøåîçíà÷åííîãî ñëó÷àÿ - ãëàâíàÿ ôîðìà äåðæèò ñåáÿ è
+ // äðóãèå ôîðìû.
+ end;
+ if FOwnerObj.fNamedObjList = nil then
+ FOwnerObj.fNamedObjList := NewList;
+ {$IFDEF UNIQUE_NAMES}
+ for i := 0 to FOwnerObj.fNamedObjList.Count-1 do
+ begin
+ if PObj( FOwnerObj.fNamedObjList.Items[ i ] ).FName = NewName then
+ begin
+ NewName := '';
+ break;
+ end;
+ end;
+ {$ENDIF}
+ FName := NewName;
+ if FName = '' then
+ FOwnerObj.fNamedObjList.Remove( @ Self )
+ else if FOwnerObj.fNamedObjList.IndexOf( @ Self ) < 0 then
+ FOwnerObj.fNamedObjList.Add( @ Self );
+end;
+
+function TObj.FindObj(const ObjName: Ansistring): PObj;
+var i: Integer;
+ Obj: PObj;
+begin
+ if fNamedObjList <> nil then
+ for i := 0 to fNamedObjList.Count-1 do
+ begin
+ Obj := fNamedObjList.Items[ i ];
+ if ObjName = Obj.FName then
+ begin
+ Result := Obj; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ Result := nil;
+end;
+{$ENDIF}
+
+{ TList }
+
+{$IFDEF USE_CONSTRUCTORS}
+procedure TList.Init;
+begin
+ {$IFDEF CALL_INHERITED}
+ inherited;
+ {$ENDIF}
+ fAddBy := 4;
+ {$IFDEF TLIST_FAST}
+ {$IFNDEF DFLT_TLIST_NOUSE_BLOCKS} // for debug only
+ fUseBlocks := TRUE;
+ {$ENDIF}
+ {$ENDIF}
+end;
+
+function NewList: PList;
+begin
+ New( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ fObjKind := 'TList';
+ {$ENDIF}
+end;
+
+{$ELSE not_USE_CONSTRUCTORS}
+function NewList: PList;
+begin
+ New( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TList';
+ {$ENDIF}
+ Result.fAddBy := 4;
+ {$IFDEF TLIST_FAST}
+ {$IFNDEF DFLT_TLIST_NOUSE_BLOCKS} // for debug only
+ Result.fUseBlocks := TRUE;
+ {$ENDIF}
+ {$ENDIF}
+end;
+{$ENDIF USE_CONSTRUCTORS}
+
+{$IFDEF _D4orHigher}
+function NewListInit( const AItems: array of Pointer ): PList;
+var i: Integer;
+begin
+ Result := NewList;
+ Result.Capacity := Length( AItems );
+ for i := 0 to High( AItems ) do
+ Result.Add( AItems[ i ] );
+end;
+{$ENDIF}
+
+{$IFNDEF PAS_ONLY}
+procedure HelpFastIncNum2Els( DataArray: Pointer; Value, Count: Integer );
+asm
+ PUSH ESI
+ PUSH EDI
+ {$IFDEF F_P}
+ MOV ESI, [DataArray]
+ MOV EDX, [Value]
+ MOV ECX, [Count]
+ {$ELSE DELPHI}
+ MOV ESI, EAX
+ {$ENDIF F_P/DELPHI}
+ MOV EDI, ESI
+ CLD
+
+@@1:
+ LODSD
+ ADD EAX, EDX
+ STOSD
+ LOOP @@1
+
+ POP EDI
+ POP ESI
+end {$IFDEF F_P} [ 'EAX', 'EDX', 'ECX' ] {$ENDIF};
+
+{$IFNDEF TLIST_FAST}
+procedure FastIncNum2Elements( List: TList; FromIdx, Count, Value: Integer );
+begin
+ HelpFastIncNum2Els( @List.fItems[ FromIdx ], Value, Count );
+end;
+{$ENDIF}
+{$ENDIF PAS_ONLY}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+destructor TList.Destroy;
+begin
+ Clear;
+ inherited;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_TLIST}
+procedure TList.Release;
+asm
+ TEST EAX, EAX
+ JZ @@e
+ MOV ECX, [EAX].fCount
+ JECXZ @@e
+ MOV EDX, [EAX].fItems
+ PUSH EAX
+@@1:
+ MOV EAX, [EDX+ECX*4-4]
+ TEST EAX, EAX
+ JZ @@2
+ PUSH EDX
+ PUSH ECX
+ CALL System.@FreeMem
+ POP ECX
+ POP EDX
+@@2: LOOP @@1
+ POP EAX
+@@e: CALL TObj.RefDec
+end;
+{$ELSE PAS_VERSION} //Pascal
+procedure TList.Release;
+var I: Integer;
+begin
+ if @ Self = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ for I := 0 to fCount - 1 do
+ if {$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}[ I ] <> nil then
+ FreeMem( {$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}[ I ] );
+ Free;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TList.ReleaseObjects;
+var I: Integer;
+begin
+ if @ Self = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ for I := fCount-1 downto 0 do
+ PObj( {$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF} [ I ] ).Free;
+ Free;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TList.SetCapacity( Value: Integer );
+begin
+ {$IFDEF TLIST_FAST}
+ if fUseBlocks and (fBlockList <> nil) then
+ begin
+ if Value > 256 then // Capacitity â îáû÷íîì ñìûñëå ðàáîòàåò òîëüêî äëÿ ïåðâîãî
+ Value := 256; // áëîêà - äî 256 ýëåìåíòîâ, äàëåå îíî ñìûñëà íå èìååò,
+ fCapacity := Value; // ò.ê. âñå ïðî÷èå áëîêè âñåãäà ñîäåðæàò ïî 256 ïîçèöèé
+ // äëÿ ýëåìåíòîâ, íåçàâèñèìî îò ïðîöåíòà èñïîëüçîâàíèÿ.
+ end else
+ {$ENDIF}
+ begin
+ if Value < Count then
+ Value := Count;
+ if Value = fCapacity then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ ReallocMem( fItems, Value * Sizeof( Pointer ) );
+ fCapacity := Value;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TList.Clear;
+{$IFDEF TLIST_FAST}
+var i: Integer;
+{$ENDIF}
+begin
+ if fItems <> nil then
+ FreeMem( fItems );
+ fItems := nil;
+ fCount := 0;
+ fCapacity := 0;
+ {$IFDEF TLIST_FAST}
+ if fBlockList <> nil then
+ begin
+ for i := 0 to fBlockList.Count div 2 - 1 do
+ FreeMem( fBlockList.Items[ i*2 ] );
+ Free_And_Nil( fBlockList );
+ end;
+ fLastKnownBlockIdx := 0;
+ fLastKnownCountBefore := 0;
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TList.SetAddBy(Value: Integer);
+begin
+ if Value < 1 then Value := 1;
+ fAddBy := Value;
+end;
+
+{$IFDEF ASM_NO_VERSION} /// ASM-version disabled due some problems - 20-May-2010
+{$ELSE PAS_VERSION} //Pascal
+procedure TList.Add( Value: Pointer );
+{$IFDEF TLIST_FAST}
+var LastBlockCount: Integer;
+ LastBlockStart: Pointer;
+{$ENDIF}
+begin
+ {$IFDEF TLIST_FAST}
+ if fUseBlocks and ((fCount >= 256) or ( fBlockList <> nil )) then
+ begin
+ if fBlockList = nil then
+ begin
+ fBlockList := NewList;
+ fBlockList.fUseBlocks := FALSE;
+ fBlockList.Add( fItems );
+ fBlockList.Add( Pointer( fCount ) );
+ fItems := nil;
+ end;
+ if fBlockList.fCount = 0 then
+ begin
+ fBlockList.Add( nil );
+ fBlockList.Add( nil );
+ LastBlockCount := 0;
+ end else
+ begin
+ LastBlockCount := Integer( fBlockList.Items[ fBlockList.fCount-1 ] );
+ if LastBlockCount >= 256 then
+ begin
+ fBlockList.Add( nil );
+ fBlockList.Add( nil );
+ LastBlockCount := 0;
+ end;
+ end;
+ LastBlockStart := fBlockList.Items[ fBlockList.fCount-2 ];
+ if LastBlockStart = nil then
+ begin
+ GetMem( LastBlockStart, 256 * Sizeof( Pointer ) );
+ fBlockList.Items[ fBlockList.fCount-2 ] := LastBlockStart;
+ end;
+ fBlockList.Items[ fBlockList.fCount-1 ] := Pointer( LastBlockCount+1 );
+ PDWORD( Integer(LastBlockStart) + Sizeof(Pointer)*LastBlockCount )^ :=
+ DWORD( Value );
+ end else
+ {$ENDIF}
+ begin
+ if fCapacity <= fCount then
+ begin
+ if fAddBy <= 0 then
+ Capacity := fCount + Min( 1000, fCount div 4 + 1 )
+ else Capacity := fCount + fAddBy;
+ end;
+ fItems[ fCount ] := Value;
+ end;
+ Inc( fCount );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF _D4orHigher}
+procedure TList.AddItems(const AItems: array of Pointer);
+var i: Integer;
+begin
+ Capacity := Count + Length( AItems );
+ for i := 0 to High( AItems ) do
+ Add( AItems[ i ] );
+end;
+{$ENDIF}
+
+procedure TList.Delete( Idx: Integer );
+begin
+ DeleteRange( Idx, 1 );
+end;
+
+{$IFDEF ASM_TLIST}
+procedure TList.DeleteRange(Idx, Len: Integer);
+asm //cmd //opd
+ TEST ECX, ECX
+ JLE @@exit
+ CMP EDX, [EAX].fCount
+ JGE @@exit
+ PUSH EBX
+ XCHG EBX, EAX
+ LEA EAX, [EDX+ECX]
+ CMP EAX, [EBX].fCount
+ JBE @@1
+ MOV ECX, [EBX].fCount
+ SUB ECX, EDX
+@@1:
+ MOV EAX, [EBX].fItems
+ PUSH [EBX].fCount
+ SUB [EBX].fCount, ECX
+ MOV EBX, EDX
+ LEA EDX, [EAX+EDX*4]
+ LEA EAX, [EDX+ECX*4]
+ ADD EBX, ECX
+ POP ECX
+ SUB ECX, EBX
+ SHL ECX, 2
+ CALL System.Move
+ POP EBX
+@@exit:
+end;
+{$ELSE PAS_VERSION} //Pascal
+procedure TList.DeleteRange(Idx, Len: Integer);
+{$IFDEF TLIST_FAST}
+var i, DelFromBlock: Integer;
+ CountBefore, CountCurrent: Integer;
+ BlockStart: Pointer;
+{$ENDIF}
+begin
+ if Len <= 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if Idx >= Count then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$IFDEF KOL_ASSERTIONS}
+ Assert( (Idx >= 0), 'TList.DeleteRange: index out of bounds' );
+ {$ENDIF KOL_ASSERTIONS}
+ if DWORD( Idx + Len ) > DWORD( Count ) then
+ Len := Count - Idx;
+ {$IFDEF TLIST_FAST}
+ if fUseBlocks and ( fBlockList <> nil ) then
+ begin
+ CountBefore := 0;
+ i := 0;
+ if (fLastKnownBlockIdx > 0) and
+ (Idx >= fLastKnownCountBefore) then
+ begin
+ i := fLastKnownBlockIdx;
+ CountBefore := fLastKnownCountBefore;
+ end;
+ while i < fBlockList.fCount div 2 do
+ begin
+ BlockStart := fBlockList.fItems[ i * 2 ];
+ CountCurrent := Integer( fBlockList.fItems[ i * 2 + 1 ] );
+ if (Idx >= CountBefore) and (Idx < CountBefore + CountCurrent) then
+ begin
+ DelFromBlock := CountBefore + CountCurrent - Idx;
+ if DelFromBlock > Len then
+ DelFromBlock := Len;
+ if DelFromBlock < CountCurrent then
+ begin
+ fNotOptimized := TRUE;
+ move( Pointer( Integer( BlockStart ) + (Idx - CountBefore + DelFromBlock) * Sizeof( Pointer ) )^,
+ Pointer( Integer( BlockStart ) + (Idx - CountBefore) * Sizeof( Pointer ) )^,
+ (CountCurrent - (Idx - CountBefore) - DelFromBlock) * Sizeof( Pointer ) );
+ dec( CountCurrent, DelFromBlock );
+ fBlockList.fItems[ i * 2 + 1 ] := Pointer( CountCurrent );
+ dec( fCount, DelFromBlock );
+ dec( Len, DelFromBlock );
+ if Len <= 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end else
+ begin // delete entire block
+ //++ fix added: 21.06.08 ++ VK
+ fLastKnownBlockIdx := 0;
+ fLastKnownCountBefore := 0;
+ //++++++++++++++++++++++++++++
+ FreeMem( BlockStart );
+ fBlockList.DeleteRange( i * 2, 2 );
+ dec( fCount, CountCurrent );
+ dec( Len, CountCurrent );
+ if Len <= 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ CountCurrent := 0;
+ dec( i );
+ end;
+ end;
+ inc( i );
+ inc( CountBefore, CountCurrent );
+ end;
+ end else
+ {$ENDIF}
+ begin
+ Move( fItems[ Idx + Len ], fItems[ Idx ], Sizeof( Pointer ) * (Count - Idx - Len) );
+ Dec( fCount, Len );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TList.Remove(Value: Pointer);
+var I: Integer;
+begin
+ I := IndexOf( Value );
+ if I >= 0 then
+ Delete( I );
+end;
+
+function TList.ItemAddress(Idx: Integer): Pointer;
+{$IFDEF TLIST_FAST}
+var i: Integer;
+ BlockStart: Pointer;
+ CountBefore, CountCurrent: Integer;
+{$ENDIF}
+begin
+ {$IFDEF TLIST_FAST}
+ if fUseBlocks and ( fBlockList <> nil ) then
+ begin
+ CountBefore := 0;
+ i := 0;
+ if (fLastKnownBlockIdx > 0) and
+ (Idx >= fLastKnownCountBefore) then
+ begin
+ CountBefore := fLastKnownCountBefore;
+ i := fLastKnownBlockIdx;
+ end;
+ CountCurrent := CountBefore + Integer( fBlockList.fItems[ i*2+1 ] );
+ if Idx - CountCurrent > fCount - CountCurrent then
+ begin // ïîèñê â îáðàòíîì íàïðàâëåíèè ìîæåò îêàçàòüñÿ áûñòðåå
+ CountBefore := fCount;
+ i := fBlockList.fCount div 2 - 1;
+ while TRUE do
+ begin
+ BlockStart := fBlockList.fItems[ i * 2 ];
+ CountCurrent := Integer( fBlockList.fItems[ i * 2 + 1 ] );
+ if (CountBefore - CountCurrent <= Idx) and (Idx < CountBefore) then
+ begin
+ Result := Pointer( Integer( BlockStart ) +
+ (Idx - (CountBefore - CountCurrent))*Sizeof( Pointer ) );
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ dec( CountBefore, CountCurrent );
+ dec( i );
+ end;
+ end;
+ while TRUE { i < fBlockList.Count div 2 } do
+ begin
+ BlockStart := fBlockList.fItems[ i * 2 ];
+ CountCurrent := Integer( fBlockList.fItems[ i * 2 + 1 ] );
+ if (CountBefore <= Idx) and (Idx < CountBefore + CountCurrent) then
+ begin
+ Result := Pointer( Integer( BlockStart ) + (Idx - CountBefore) * Sizeof( Pointer ) );
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ inc( CountBefore, CountCurrent );
+ inc( i );
+ end;
+ end else
+ {$ENDIF}
+ Result := Pointer( Integer( fItems ) + Idx * Sizeof( Pointer ) );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+procedure TList.Put( Idx: Integer; Value: Pointer );
+{$IFDEF TLIST_FAST}
+var i: Integer;
+ BlockStart: Pointer;
+ CountBefore, CountCurrent: Integer;
+{$ENDIF}
+begin
+ if Idx < 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if Idx >= Count then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$IFDEF TLIST_FAST}
+ if fUseBlocks and ( fBlockList <> nil ) then
+ begin
+ CountBefore := 0;
+ i := 0;
+ if (fLastKnownBlockIdx > 0) and
+ (Idx >= fLastKnownCountBefore) then
+ begin
+ i := fLastKnownBlockIdx;
+ CountBefore := fLastKnownCountBefore;
+ end;
+ while i < fBlockList.fCount div 2 do
+ begin
+ BlockStart := fBlockList.fItems[ i * 2 ];
+ CountCurrent := Integer( fBlockList.fItems[ i * 2 + 1 ] );
+ if (CountBefore <= Idx) and (Idx < CountBefore + CountCurrent) then
+ begin
+ fLastKnownBlockIdx := i;
+ fLastKnownCountBefore := CountBefore;
+ PDWORD( Integer( BlockStart ) + (Idx - CountBefore) * Sizeof( Pointer ) )^ :=
+ DWORD( Value );
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ inc( CountBefore, CountCurrent );
+ inc( i );
+ end;
+ end else
+ {$ENDIF}
+ fItems[ Idx ] := Value;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+function TList.Get( Idx: Integer ): Pointer;
+{$IFDEF TLIST_FAST}
+var i: Integer;
+ BlockStart: Pointer;
+ CountBefore, CountCurrent: Integer;
+{$ENDIF}
+begin
+ Result := nil;
+ if Idx < 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if Idx >= fCount then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$IFDEF TLIST_FAST}
+ if fUseBlocks and ( fBlockList <> nil ) then
+ begin
+ if fNotOptimized then
+ begin
+ CountBefore := 0;
+ i := 0;
+ if (fLastKnownBlockIdx > 0) and
+ (Idx >= fLastKnownCountBefore) then
+ begin
+ i := fLastKnownBlockIdx;
+ CountBefore := fLastKnownCountBefore;
+ end;
+ while {i < fBlockList.fCount div 2} TRUE do
+ begin
+ BlockStart := fBlockList.fItems[ i * 2 ];
+ CountCurrent := Integer( fBlockList.fItems[ i * 2 + 1 ] );
+ if (CountBefore <= Idx) and (Idx < CountBefore + CountCurrent) then
+ begin
+ fLastKnownBlockIdx := i;
+ fLastKnownCountBefore := CountBefore;
+ Result := Pointer( PDWORD( Integer( BlockStart ) + (Idx - CountBefore) * Sizeof( Pointer ) )^ );
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ inc( CountBefore, CountCurrent );
+ inc( i );
+ end;
+ end else
+ begin // optimized!
+ i := Idx shr 8;
+ BlockStart := fBlockList.fItems[ i * 2 ];
+ i := Idx and 255;
+ Result := Pointer( PDWORD( Integer( BlockStart ) + i * Sizeof( Pointer ) )^ );
+ end;
+ end else
+ {$ENDIF}
+ Result := fItems[ Idx ];
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_TLIST}
+function TList.IndexOf( Value: Pointer ): Integer;
+asm
+ PUSH EDI
+
+ MOV EDI, [EAX].fItems
+ MOV ECX, [EAX].fCount
+ PUSH EDI
+ DEC EAX // make "NZ" - EAX always <> 1
+ MOV EAX, EDX
+ REPNZ SCASD
+ POP EDX
+ {$IFDEF USE_CMOV}
+ CMOVNZ EDI, EDX
+ {$ELSE}
+ JZ @@succ
+ MOV EDI, EDX
+@@succ: {$ENDIF}
+
+ MOV EAX, EDI
+ STC
+ SBB EAX, EDX
+ SAR EAX, 2
+
+ POP EDI
+end;
+{$ELSE PAS_VERSION} //Pascal
+function TList.IndexOf( Value: Pointer ): Integer;
+var I: Integer;
+ {$IFDEF TLIST_FAST}
+ BlockStart: PDWORD;
+ j: Integer;
+ CountBefore, CountCurrent: Integer;
+ {$ENDIF}
+begin
+ Result := -1;
+ {$IFDEF DEBUG_ANY}
+ TRY
+ {$ENDIF}
+ {$IFDEF TLIST_FAST}
+ if fUseBlocks and ( fBlockList <> nil ) then
+ begin
+ CountBefore := 0;
+ for I := 0 to fBlockList.fCount div 2 - 1 do
+ begin
+ BlockStart := fBlockList.fItems[ I * 2 ];
+ CountCurrent := Integer( fBlockList.fItems[ I * 2 + 1 ] );
+ for j := 0 to CountCurrent-1 do
+ begin
+ if BlockStart^ = DWORD( Value ) then
+ begin
+ Result := CountBefore + j; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ inc( BlockStart );
+ end;
+ inc( CountBefore, CountCurrent );
+ end;
+ end else
+ {$ENDIF}
+ begin
+ for I := 0 to fCount - 1 do
+ begin
+ if fItems[ I ] = Value then
+ begin
+ Result := I;
+ break;
+ end;
+ end;
+ end;
+ {$IFDEF DEBUG_ANY}
+ EXCEPT
+ END;
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_TLIST}
+procedure TList.Insert(Idx: Integer; Value: Pointer);
+asm
+ PUSH ECX
+ PUSH EAX
+ PUSH [EAX].fCount
+ PUSH EDX
+ CALL TList.Add // don't matter what to add
+ POP EDX // EDX = Idx, Eax = Count-1
+ POP EAX
+ SUB EAX, EDX
+
+ SAL EAX, 2
+ MOV ECX, EAX // ECX = (Count - Idx - 1) * 4
+ POP EAX
+ MOV EAX, [EAX].fItems
+ LEA EAX, [EAX + EDX*4]
+ JL @@1
+ PUSH EAX
+ LEA EDX, [EAX + 4]
+ CALL System.Move
+
+ POP EAX // EAX = @fItems[ Idx ]
+@@1:
+ POP ECX // ECX = Value
+ MOV [EAX], ECX
+end;
+{$ELSE PAS_VERSION} //Pascal
+procedure TList.Insert(Idx: Integer; Value: Pointer);
+{$IFDEF TLIST_FAST}
+var i: Integer;
+ CountBefore, CountCurrent: Integer;
+ BlockStart, NewBlock: Pointer;
+{$ENDIF}
+begin
+ {$IFDEF KOL_ASSERTIONS}
+ Assert( (Idx >= 0) and (Idx <= FCount+1), 'List index out of bounds' );
+ {$ENDIF KOL_ASSERTIONS}
+ {$IFDEF TLIST_FAST}
+ if fUseBlocks and (( fBlockList <> nil ) or (fCount >= 256)) then
+ begin
+ if ( fBlockList = nil ) then
+ begin
+ fBlockList := NewList;
+ fBlockList.fUseBlocks := FALSE;
+ fBlockList.Add( fItems );
+ fBlockList.Add( Pointer( fCount ) );
+ fItems := nil;
+ end;
+ if fBlockList.fCount = 0 then
+ begin
+ fNotOptimized := FALSE;
+ GetMem( NewBlock, 256 * Sizeof( Pointer ) );
+ fBlockList.Add( NewBlock );
+ fBlockList.Add( nil );
+ end;
+ CountBefore := 0;
+ i := 0;
+ if (fLastKnownBlockIdx > 0) and
+ (Idx >= fLastKnownCountBefore) then
+ begin
+ i := fLastKnownBlockIdx;
+ CountBefore := fLastKnownCountBefore;
+ end;
+ while TRUE {i < fBlockList.fCount div 2} do
+ begin
+ CountCurrent := Integer( fBlockList.Items[ i * 2 + 1 ] );
+ if (Idx >= CountBefore) and
+ ((Idx < CountBefore + CountCurrent) or
+ (Idx = CountBefore + CountCurrent) and
+ (CountCurrent < 256)) then // insert in block i
+ begin
+ BlockStart := fBlockList.fItems[ i * 2 ];
+ if BlockStart = nil then
+ begin
+ GetMem( BlockStart, 256 * Sizeof( Pointer ) );
+ fBlockList.fItems[ i * 2 ] := BlockStart;
+ end;
+ Idx := Idx - CountBefore;
+ if CountCurrent < 256 then
+ begin
+ if Idx < CountCurrent then
+ Move( Pointer( Integer( BlockStart ) + Idx * Sizeof( Pointer ) )^,
+ Pointer( Integer( BlockStart ) + (Idx+1) * Sizeof( Pointer ) )^,
+ (CountCurrent - Idx) * Sizeof( Pointer ) );
+ PDWORD( Integer( BlockStart ) + Idx * Sizeof( Pointer ) )^ :=
+ DWORD( Value );
+ fBlockList.fItems[ i * 2 + 1 ] := Pointer( CountCurrent + 1 );
+ end else // new block is created since current block is full 256 items
+ begin
+ fNotOptimized := TRUE;
+ GetMem( NewBlock, 256 * Sizeof( Pointer ) );
+ fBlockList.Insert( (i+1)*2, Pointer( 256-Idx ) );
+ fBlockList.Insert( (i+1)*2, NewBlock );
+ move( Pointer( Integer( BlockStart ) + Idx * Sizeof( Pointer ) )^,
+ NewBlock^, (256 - Idx) * Sizeof( Pointer ) );
+ PDWORD( Integer( BlockStart ) + Idx * Sizeof( Pointer ) )^ :=
+ DWORD( Value );
+ fBlockList.fItems[ i * 2 + 1 ] := Pointer( Idx + 1 );
+ end;
+ fLastKnownBlockIdx := i;
+ fLastKnownCountBefore := CountBefore;
+ inc( fCount );
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ inc( CountBefore, CountCurrent );
+ inc( i );
+ if i >= fBlockList.fCount div 2 then
+ begin
+ fBlockList.Add( nil );
+ fBlockList.Add( nil );
+ end;
+ end;
+ end else
+ {$ENDIF}
+ begin
+ Add( nil );
+ if fCount > Idx then
+ Move( FItems[ Idx ], FItems[ Idx + 1 ], (fCount - Idx - 1) * Sizeof( Pointer ) );
+ FItems[ Idx ] := Value;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION} {$DEFINE MoveItem_ASM} {$ENDIF}
+{$IFDEF TLIST_FAST} {$UNDEF MoveItem_ASM} {$ENDIF}
+
+{$IFDEF MoveItem_ASM}
+{$ELSE PAS_VERSION} //Pascal
+procedure TList.MoveItem(OldIdx, NewIdx: Integer);
+var Item: Pointer;
+begin
+ if OldIdx = NewIdx then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if NewIdx >= Count then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Item := Items[ OldIdx ];
+ Delete( OldIdx );
+ Insert( NewIdx, Item );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_TLIST}
+function TList.Last: Pointer;
+asm //cmd //opd
+ MOV ECX, [EAX].fCount
+ JECXZ @@0
+ MOV EAX, [EAX].fItems
+ DEC ECX
+ MOV ECX, [EAX + ECX*4]
+@@0: XCHG EAX, ECX
+end;
+{$ELSE PAS_VERSION} //Pascal
+function TList.Last: Pointer;
+begin
+ if Count = 0 then
+ Result := nil
+ else Result := Items[ Count-1 ];
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_TLIST}
+procedure TList.Swap(Idx1, Idx2: Integer);
+asm
+ MOV EAX, [EAX].fItems
+ PUSH dword ptr [EAX + EDX*4]
+ PUSH ECX
+ MOV ECX, [EAX + ECX*4]
+ MOV [EAX + EDX*4], ECX
+ POP ECX
+ POP EDX
+ MOV [EAX + ECX*4], EDX
+end;
+{$ELSE PAS_VERSION} //Pascal
+procedure TList.Swap(Idx1, Idx2: Integer);
+var Tmp: DWORD;
+ AItem1, AItem2: PDWORD;
+begin
+ {$IFDEF TLIST_FAST}
+ AItem1 := ItemAddress( Idx1 );
+ AItem2 := ItemAddress( Idx2 );
+ {$ELSE}
+ AItem1 := Pointer( Integer( fItems ) + Idx1 * Sizeof( Pointer ) );
+ AItem2 := Pointer( Integer( fItems ) + Idx2 * Sizeof( Pointer ) );
+ {$ENDIF}
+ Tmp := AItem1^;
+ AItem1^ := AItem2^;
+ AItem2^ := Tmp;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TList.SetCount(const Value: Integer);
+begin
+ if Value >= Count then exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fCount := Value;
+end;
+
+procedure TList.Assign(SrcList: PList);
+{$IFDEF TLIST_FAST}
+var i, CountCurrent: Integer;
+ SrcBlock, DstBlock: Pointer;
+{$ENDIF}
+begin
+ Clear;
+ if SrcList.fCount > 0 then
+ begin
+ {$IFDEF TLIST_FAST}
+ if SrcList.fUseBlocks and ( SrcList.fBlockList <> nil ) then
+ begin
+ fBlockList := NewList;
+ fBlockList.Assign( SrcList.fBlockList );
+ for i := 0 to fBlockList.Count div 2 - 1 do
+ begin
+ SrcBlock := SrcList.fBlockList.fItems[ i*2 ];
+ CountCurrent := Integer( fBlockList.fItems[ i*2+1 ] );
+ GetMem( DstBlock, 256 * Sizeof( Pointer ) );
+ fBlockList.fItems[ i*2 ] := DstBlock;
+ move( SrcBlock^, DstBlock^, CountCurrent );
+ end;
+ end else
+ {$ENDIF}
+ begin
+ Capacity := SrcList.fCount;
+ Move( SrcList.FItems[ 0 ], FItems[ 0 ], Sizeof( Pointer ) * SrcList.fCount );
+ end;
+ end;
+ fCount := SrcList.fCount;
+end;
+
+{$IFDEF WIN_GDI}
+
+{$UNDEF ASM_LOCAL}
+{$IFDEF ASM_noVERSION}
+ {$IFNDEF _D2orD3}
+ {$DEFINE ASM_LOCAL}
+ {$ENDIF}
+{$ENDIF}
+
+{$IFDEF ASM_LOCAL} //!!//!!
+function CallCtlWndProc( Ctl: PControl; var Msg: TMsg ): Integer;
+begin
+ Result := Ctl.WndProc( Msg );
+end;
+
+{ -- Window procedure -- }
+function WndFunc( W: HWnd; Msg: Cardinal; wParam, lParam: Integer )
+ : Integer; stdcall;
+const size_TMsg = sizeof( TMsg );
+asm
+ ADD ESP, -size_TMsg
+ MOV EDX, ESP
+
+ PUSH ESI
+ PUSH EDI
+
+ MOV EDI, EDX
+ LEA ESI, [W]
+
+ MOVSD
+ MOVSD
+ MOVSD
+ MOVSD
+
+ MOV EDI, EDX
+ MOV EAX, [EDI]
+ TEST EAX, EAX
+ JZ @@self_is_nil
+
+ MOV ECX, [CreatingWindow]
+ JECXZ @@get_self_prop
+
+ MOV [ECX].TControl.fHandle, EAX
+
+ PUSH ECX
+ PUSH ECX
+ {$IFDEF USE_PROP}
+ PUSH Offset[ID_SELF]
+ PUSH EAX
+ CALL SetProp
+ {$ELSE}
+ PUSH GWL_USERDATA
+ PUSH EAX
+ CALL SetWindowLong
+ {$ENDIF}
+
+ XOR EAX, EAX
+ MOV [CreatingWindow], EAX
+ POP EAX // EAX = self_
+ JMP @@self_got
+
+@@get_self_prop:
+ {$IFDEF USE_PROP}
+ PUSH Offset[ID_SELF]
+ PUSH EAX
+ CALL GetProp
+ {$ELSE}
+ PUSH GWL_USERDATA
+ PUSH EAX
+ CALL GetWindowLong
+ {$ENDIF}
+ TEST EAX, EAX
+ JNZ @@self_got
+
+@@self_is_nil:
+ OR EAX, [ Applet ]
+ JNZ @@self_got
+
+ POP EDI
+ POP ESI
+ MOV ESP, EBP
+ POP EBP
+ JMP DefWindowProc
+
+@@self_got:
+ MOV ESI, EAX
+ INC WORD PTR [ESI].TControl.fNestedMsgHandling
+ MOV EDX, EDI
+ CALL CallCtlWndProc
+ DEC WORD PTR [ESI].TControl.fNestedMsgHandling
+ JA @@1
+ {$IFDEF USE_FLAGS}
+ TEST [ESI].TControl.fFlagsG2, (1 shl G2_BeginDestroying)
+ JZ @@1
+ {$ELSE}
+ CMP [ESI].TControl.fBeginDestroying, 0
+ JZ @@1
+ {$ENDIF}
+ CMP [ESI].TObj.fRefCount, 0
+ JNZ @@1
+ CMP ESI, [Applet]
+ JZ @@1
+ XCHG EAX, ESI
+ CALL TObj.Free
+ XCHG ESI, EAX
+@@1:
+
+ POP EDI
+ POP ESI
+
+ MOV ESP, EBP
+end;
+{$ELSE PAS_VERSION} //Pascal
+function WndFunc( W: HWnd; Msg: Cardinal; wParam, lParam: Integer )
+ : Integer; stdcall;
+var M: TMsg;
+ self_: PControl;
+begin
+ {$IFDEF INPACKAGE}
+ Log( '->WndFunc ' + Int2Hex( Msg, 4 ) + ' (' + Int2Str( Msg ) + ')' );
+ TRY
+ {$ENDIF INPACKAGE}
+
+ M.hwnd := W;
+ M.message := Msg;
+ M.wParam := wParam;
+ M.lParam := lParam;
+
+ {$IFDEF DEBUG_MONITOR_MESSAGES}
+ if Assigned( OnMonitorMessage ) then
+ OnMonitorMessage( M, TRUE );
+ {$ENDIF}
+
+ {$IFDEF DEBUG_ENDSESSION}
+ if EndSession_Initiated then
+ begin
+ LogFileOutput( GetStartDir + 'es_debug.txt',
+ 'HWND:' + Int2Str( W ) + ' MSG:$' + Int2Hex( Msg, 4 ) +
+ ' WParam: ' + Int2Str( wParam ) + '($' + Int2Hex( wParam, 8 ) + ')' +
+ ' LParam: ' + Int2Str( lParam ) + '($' + Int2Hex( lParam, 8 ) + ')' );
+ end;
+ {$ENDIF}
+
+ self_ := nil;
+ if W <> 0 then
+ begin
+ if CreatingWindow <> nil then
+ begin
+ {$IFDEF INPACKAGE}
+ Log( '//// CreatingWindow <> nil' );
+ {$ENDIF INPACKAGE}
+ {$IFDEF DEBUG_CREATEWINDOW}
+ LogFileOutput( GetStartDir + 'Session.log',
+ 'WndFunc: Creating window = ' + Int2Hex( Integer( CreatingWindow ), 4 ) +
+ ' hwnd=' + Int2Str( M.hwnd ) +
+ ' message=' + Int2Hex( M.message, 4 ) +
+ ' wParam=' + Int2Str( M.wParam ) + '=$' + Int2Hex( M.wParam, 4 ) +
+ ' lParam=' + Int2Str( M.lParam ) + '=$' + Int2Hex( M.lParam, 4 )
+ );
+ {$ENDIF DEBUG_CREATEWINDOW}
+ self_ := CreatingWindow;
+ CreatingWindow.fHandle := W;
+ {$IFDEF USE_PROP}
+ {$IFDEF INPACKAGE}
+ Log( '//// SetProp' );
+ {$ENDIF INPACKAGE}
+ SetProp( W, ID_SELF, THandle( CreatingWindow ) );
+ {$ELSE}
+ SetWindowLong( W, GWL_USERDATA, Integer( CreatingWindow ) );
+ {$ENDIF}
+ CreatingWindow := nil;
+ end else
+ {$IFDEF USE_PROP}
+ self_ := Pointer( GetProp( W, ID_SELF ) );
+ {$ELSE}
+ self_ := Pointer( GetWindowLong( W, GWL_USERDATA ) );
+ {$ENDIF}
+ end;
+
+ if self_ <> nil then
+ begin
+ {$IFDEF INPACKAGE}
+ Log( '//// self_ <> nil, calling self_.WndProc' );
+ {$ENDIF INPACKAGE}
+ //self_.RefInc;
+ //TRY
+ Result := self_.WndProc( M );
+ //FINALLY
+ // self_.RefDec;
+ //END;
+ end else
+ if Applet <> nil then
+ Result := Applet.WndProc( M )
+ else Result := DefWindowProc( W, Msg, wParam, lParam );
+ {$IFDEF DEBUG_ENDSESSION}
+ if EndSession_Initiated then
+ begin
+ LogFileOutput( GetStartDir + 'es_debug.txt',
+ 'HWND:' + Int2Str( W ) + ' MSG:$' + Int2Hex( Msg, 4 ) +
+ ' Result: ' + Int2Str( Result ) + '($' + Int2Hex( Result, 8 ) + ')' );
+ end;
+ {$ENDIF}
+ {$IFDEF INPACKAGE}
+ LogOK;
+ FINALLY
+ Log( '<-WndFunc' );
+ END;
+ {$ENDIF INPACKAGE}
+ {$IFDEF DEBUG_MONITOR_MESSAGES}
+ if Assigned( OnMonitorMessage ) then
+ OnMonitorMessage( M, FALSE );
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TList.OptimizeForRead;
+{$IFDEF TLIST_FAST}
+var i, j, N: Integer;
+ NewBlocksList: PList;
+ BlockStart: PPointer;
+{$ENDIF}
+begin
+ {$IFDEF TLIST_FAST}
+ if fNotOptimized and fUseBlocks then
+ begin
+ NewBlocksList := NewList;
+ NewBlocksList.UseBlocks := FALSE;
+ i := 0;
+ while i < Count do
+ begin
+ N := 256;
+ if N > Count-i then
+ N := Count-i;
+ GetMem( BlockStart, 256 * Sizeof(Pointer) );
+ NewBlocksList.Add( BlockStart );
+ NewBlocksList.Add( Pointer(N) );
+ for j := i to i+N-1 do
+ begin
+ BlockStart^ := Items[j];
+ inc( BlockStart );
+ end;
+ inc( i, 256 );
+ end;
+ N := Count;
+ Clear;
+ Free_And_Nil( fBlockList );
+ fBlockList := NewBlocksList;
+ fCount := N;
+ fNotOptimized := FALSE;
+ end;
+ {$ENDIF}
+end;
+
+var
+ IdleHandlers: PList;
+ ProcessIdle: procedure ( Sender: PObj ) = DummyObjProc;
+
+procedure ProcessIdleProc( Sender: PObj );
+var
+ i: integer;
+ m: TMethod;
+begin
+ if AppletTerminated then exit; // YS + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+ i := 0;
+ with IdleHandlers^ do
+ while i < Count do begin
+ m.Code:=Items[i];
+ Inc(i);
+ m.Data:=Items[i];
+ Inc(i);
+ TOnEvent(m)(Sender);
+ end;
+end;
+
+function FindIdleHandler( const OnIdle: TOnEvent ): integer;
+var
+ i: integer;
+begin
+ i := 0;
+ if not AppletTerminated then //+ {Maxim Pushkar}
+ with TMethod(OnIdle), IdleHandlers^ do
+ while i < Count do begin
+ if (Items[i] = Code) and (Items[i + 1] = Data) then
+ begin
+ Result := i;
+ exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Inc(i, 2);
+ end;
+ Result := -1;
+end;
+
+procedure RegisterIdleHandler( const OnIdle: TOnEvent );
+begin
+ if IdleHandlers = nil then begin
+ IdleHandlers := NewList;
+ if Applet <> nil then
+ Applet.Add2AutoFree(IdleHandlers);
+ end;
+ with TMethod(OnIdle) do
+ begin
+ IdleHandlers.Add(Code);
+ IdleHandlers.Add(Data);
+ end;
+ ProcessIdle := @ProcessIdleProc;
+end;
+
+procedure UnRegisterIdleHandler( const OnIdle: TOnEvent );
+var
+ i: integer;
+begin
+ i := FindIdleHandler(OnIdle);
+ if i <> -1 then
+ with IdleHandlers^ do
+ begin
+ Delete(i);
+ Delete(i);
+ end;
+end;
+
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+procedure TerminateExecution( var AppletCtl: PControl );
+var App: PControl;
+ Appalreadyterminated: Boolean;
+begin
+ Appalreadyterminated := AppletTerminated;
+ AppletTerminated := TRUE;
+ AppletRunning := FALSE;
+ App := Applet;
+ Applet := nil;
+ if (App <> nil) {and (App.RefCount >= 0)} then
+ begin
+ App.RefInc;
+ if not Appalreadyterminated then
+ begin
+ App.ProcessMessages;
+ App.Perform( WM_CLOSE, 0, 0 );
+ end;
+ AppletCtl := nil;
+ App.Free;
+ App.RefDec;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+//22{$IFDEF ASM_VERSION}
+function CallTControlCreateWindow( Ctl: PControl ): Boolean;
+begin
+ {$IFDEF SAFE_CODE}
+ Result := FALSE;
+ TRY
+ if Ctl = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := Ctl.CreateWindow;
+ EXCEPT
+ END;
+ {$ELSE}
+ Result := Ctl.CreateWindow;
+ {$ENDIF}
+end;
+//22{$ENDIF}
+{$ENDIF GDI}
+{$ENDIF WIN_GDI}
+
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure Run( var AppletCtl: PControl );
+ {$IFDEF PSEUDO_THREADS}
+var n: Integer;
+ i: Integer;
+ T: PThread;
+ u: DWORD;
+ M: TMsg;
+ {$ENDIF}
+begin
+ if AppletCtl = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ AppletRunning := True;
+ Applet := AppletCtl;
+ AppletCtl.CreateWindow; //virtual!!!
+ //Applet_Wnd := AppletCtl.Handle;
+ while not AppletTerminated do
+ begin
+ {$IFDEF PSEUDO_THREADS}
+ if MainThread <> nil then
+ begin
+ while not PeekMessage( M, 0, 0, 0, pm_noremove ) do
+ begin
+ u := GetTickCount;
+ n := 0;
+ for i := 1 to MainThread.AllThreads.Count-1 do
+ begin
+ T := MainThread.AllThreads.Items[ i ];
+ if not T.Suspended and not T.Terminated and (T.DoNotWakeUntil < u) then
+ begin
+ inc( n );
+ break;
+ end;
+ end;
+ if n = 0 then WaitMessage
+ else MainThread.NextThread;
+ end;
+ end else
+ WaitMessage;
+ {$ELSE}
+ WaitMessage;
+ {$ENDIF}
+ AppletCtl.ProcessMessages;
+ {$IFDEF USE_OnIdle}
+ ProcessIdle( AppletCtl );
+ {$ENDIF}
+ end;
+ if Assigned( AppletCtl ) then
+ TerminateExecution( AppletCtl );
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+ PROCEDURE Run( var AppletWnd: PControl );
+ BEGIN
+ AppletRunning := True;
+ Applet := AppletWnd;
+ AppletWnd.VisualizyWindow; // for GTK, show all windows having Visible = TRUE, recursively
+ gtk_main( );
+ IF AppletWnd <> nil THEN
+ //TerminateExecution( AppletWnd );
+ Free_And_Nil( AppletWnd );
+ END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF WIN_GDI}
+{$IFDEF GDI}
+procedure AppletMinimize;
+begin
+ if Applet = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Applet.Perform( WM_SYSCOMMAND, SC_MINIMIZE, 0 );
+end;
+
+procedure AppletHide;
+begin
+ if Applet = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ AppletMinimize;
+ Applet.Hide;
+end;
+
+procedure AppletRestore;
+begin
+ if Applet = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Applet.Show;
+ Applet.Perform( WM_SYSCOMMAND, SC_RESTORE, 0 );
+end;
+
+function ScreenWidth: Integer;
+begin
+ Result := GetSystemMetrics( SM_CXSCREEN );
+end;
+
+function ScreenHeight: Integer;
+begin
+ Result := GetSystemMetrics( SM_CYSCREEN );
+end;
+{$ENDIF GDI}
+
+//22{$IFDEF ASM_VERSION}
+//function WndProcAppAsm( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean; forward;
+//22{$ENDIF}
+function WndProcAppPas( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean; forward;
+function WndProcForm( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean; forward;
+function WndProcPaint( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean; forward;
+function WndProcGradient( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean; forward;
+function WndProcGradientEx( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean; forward;
+function WndProcLabelEffect( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean; forward;
+function WndProcNotify( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean; forward;
+function WndProcCommonNotify( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean; forward;
+var fGlobalProcKeybd: function( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean =
+ WndProcDummy;
+
+{ -- Graphics support -- }
+
+{$ENDIF WIN_GDI}
+function _NewGraphicTool: PGraphicTool;
+begin
+ New( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TGraphicTool';
+ {$ENDIF}
+end;
+{$IFDEF WIN_GDI}
+
+{$IFDEF ASM_VERSION}{$ELSE ASM_VERSION PAS_VERSION}
+function SimpleGetCtlBrushHandle( Sender: PControl ): HBrush;
+{$IFDEF STORE_fTmpBrushColorRGB}{$ELSE}
+var tmpRGBColor: TColor;
+{$ENDIF}
+begin
+ if (Sender.fParent <> nil) and (Sender.fColor = Sender.fParent.fColor) then
+ Result := SimpleGetCtlBrushHandle( Sender.fParent )
+ else
+ begin
+ {$IFDEF GDI}
+ {$IFDEF STORE_fTmpBrushColorRGB}
+ if (Sender.fTmpBrush <> 0) and
+ (Color2RGB( Sender.fColor ) <> Sender.fTmpBrushColorRGB) then
+ begin
+ DeleteObject( Sender.fTmpBrush );
+ Sender.fTmpBrush := 0;
+ end;
+ {$ENDIF}
+ if Sender.fTmpBrush = 0 then
+ begin
+ {$IFDEF STORE_fTmpBrushColorRGB}
+ Sender.fTmpBrushColorRGB := Color2RGB( Sender.fColor );
+ Sender.fTmpBrush := CreateSolidBrush( Sender.fTmpBrushColorRGB );
+ {$ELSE}
+ tmpRGBColor := Color2RGB( Sender.fColor );
+ Sender.fTmpBrush := CreateSolidBrush( tmpRGBColor );
+ {$ENDIF}
+ end;
+ Result := Sender.fTmpBrush;
+ {$ELSE} Result := 0;
+ {$ENDIF GDI}
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE notASM_VERSION}
+function NormalGetCtlBrushHandle( Sender: PControl ): HBrush;
+var B: PGraphicTool;
+ //P: PControl;
+begin
+ {$IFDEF GDI}
+ B := Sender.Brush;
+ //P := Sender.fParent;
+ //if P <> nil then
+ if Sender.fParent <> nil then
+ B.fParentGDITool := Sender.fParent.Brush; //P.Brush;
+ Result := B.Handle;
+ {$ELSE} Result := 0;
+ {$ENDIF GDI}
+end;
+{$ENDIF PAS_VERSION}
+
+function MakeFontHandle( Self_: PGraphicTool ): THandle; forward;
+function MakeBrushHandle( Self_: PGraphicTool ): THandle; forward;
+function MakePenHandle( Self_: PGraphicTool ): THandle; forward;
+function MakeGeometricPenHandle( Self_: PGraphicTool ): THandle; forward;
+
+{$ENDIF WIN_GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewBrush: PGraphicTool;
+begin
+ {$IFDEF GDI}
+ Global_GetCtlBrushHandle := NormalGetCtlBrushHandle;
+ {$ENDIF GDI}
+ Result := _NewGraphicTool;
+ with Result^ do
+ begin
+ fNewProc := @ NewBrush;
+ fType := gttBrush;
+ {$IFDEF GDI}
+ fMakeHandleProc := @ MakeBrushHandle;
+ {$ENDIF GDI}
+ Result.fData.Color := clBtnFace;
+ Result.fData.Brush.Style := bsSolid;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewPen: PGraphicTool;
+begin
+ Result := _NewGraphicTool;
+ with Result^ do
+ begin
+ fNewProc := @ NewPen;
+ fType := gttPen;
+ {$IFDEF GDI}
+ fMakeHandleProc := @ MakePenHandle;
+ {$ENDIF GDI}
+ fData.Pen.Mode := pmCopy;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+var ApplyFont2Wnd_Proc: procedure( _Self: PObj ) = DummyObjProc;
+procedure DoApplyFont2Wnd( _Self: PControl ); forward;
+
+const size_FontData = sizeof( Integer {fFontHeight} ) + sizeof( Integer {fFontWidth} ) +
+ sizeof( TFontPitch ) + sizeof( TFontStyle ) +
+ sizeof( Integer {fFontOrientation} ) +
+ sizeof( Integer {fFontWeight} ) + sizeof( TFontCharset ) +
+ sizeof( TFontQuality );
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewFont: PGraphicTool;
+begin
+ ApplyFont2Wnd_Proc := @ DoApplyFont2Wnd;
+ Result := _NewGraphicTool;
+ with Result^ do
+ begin
+ fNewProc := @ NewFont;
+ fType := gttFont;
+ {$IFDEF GDI}
+ fMakeHandleProc := @ MakeFontHandle;
+ fData.Color := DefFontColor;
+ Move( DefFont, fData.Font, Sizeof( TGDIFont ) );
+ {$ENDIF GDI}
+ {$IFDEF GTK}
+ fData.Font.Weight := 400;
+ {$ENDIF GTK}
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+function Color2RGB( Color: TColor ): TColor;
+begin
+ if Color < 0 then
+ Result := GetSysColor(Color and $7F)
+ else Result := Color;
+end;
+{$ENDIF PAS_VERSION}
+
+function RGB2BGR( Color: TColor ): TColor;
+begin
+ Result := ((Color shr 16) or (Color shl 16) or Color and $00FF00)
+ and $FFFFFF;
+end;
+
+function ColorsMix( Color1, Color2: TColor ): TColor;
+{$IFDEF F_P}
+begin
+ Result := ((Color2RGB( Color1 ) and $FEFEFE) shr 1) +
+ ((Color2RGB( Color2 ) and $FEFEFE) shr 1);
+end;
+{$ELSE DELPHI}
+asm
+ //PUSH EDX
+ CALL Color2Rgb
+ //POP EDX
+ XCHG EAX, EDX
+ //PUSH EDX
+ CALL Color2Rgb
+ //POP EDX
+ MOV ECX, $0FEFEFE
+ AND EAX, ECX
+ AND EDX, ECX
+ ADD EAX, EDX
+ ROR EAX, 1
+end;
+{$ENDIF F_P/DELPHI}
+
+{$IFDEF WIN_GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function Color2RGBQuad( Color: TColor ): TRGBQuad;
+var C: Integer;
+begin
+ C := Color2RGB( Color );
+ C := ((C shr 16) and $FF)
+ or ((C shl 16) and $FF0000)
+ or (C and $FF00);
+ Result := TRGBQuad( C );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+function Color2Color16( Color: TColor ): WORD;
+begin
+ Color := Color2RGB( Color );
+ Result := (Color shr 19) and $1F or
+ (Color shr 5) and $7E0 or
+ (Color shl 8) and $F800;
+end;
+{$ENDIF PAS_VERSION}
+
+function Color2Color15( Color: TColor ): WORD;
+begin
+ Color := Color2RGB( Color );
+ Result := (Color shr 19) and $1F or
+ (Color shr 6) and $3E0 or
+ (Color shl 7) and $7C00;
+end;
+
+{$ENDIF WIN_GDI}
+{ TGraphicTool }
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+function TGraphicTool.Assign(Value: PGraphicTool): PGraphicTool;
+var _Self: PGraphicTool;
+begin
+ Result := nil;
+ if Value = nil then
+ begin
+ {$IFDEF OLD_REFCOUNT}
+ if @Self <> nil then
+ DoDestroy;
+ {$ELSE}
+ Free;
+ {$ENDIF}
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ _Self := @Self;
+ if _Self = nil then
+ _Self := Value.fNewProc();
+ Result := _Self;
+ if _Self = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ // to avoid infinite loop when assigning to itself
+ {$IFDEF GDI}
+ if _Self.fHandle <> 0 then
+ if Value.fHandle = _Self.fHandle then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$ENDIF GDI}
+ _Self.Changed; // to destroy handle if allocated and release it from the canvas (if any uses it)
+ {$IFDEF KOL_ASSERTIONS}
+ Assert( Value.fType = _Self.fType, 'Attempt to assign to different GDI tool type' );
+ {$ENDIF KOL_ASSERTIONS}
+ Move( Value.fData, _Self.fData, Sizeof( fData ) );
+ _Self.Changed; // to inform owner control, that its tool (font, brush) changed
+end;
+{$ENDIF PAS_VERSION}
+{$IFDEF WIN_GDI}
+
+procedure TGraphicTool.AssignHandle(NewHandle: THANDLE);
+begin
+ if fHandle <> 0 then //
+ DeleteObject( fHandle ); //
+ fHandle := NewHandle;
+ GetObject( fHandle, Sizeof( TGDIFont ), @ fData.Font );
+ Changed;
+end;
+
+{$ENDIF WIN_GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TGraphicTool.Changed;
+{$IFDEF GDI} var H: THandle; {$ENDIF GDI}
+begin
+ {$IFDEF GDI}
+ H := 0;
+ if fHandle <> 0 then
+ begin
+ H := fHandle;
+ fHandle := 0;
+ end;
+ ////////////////////////////////
+ if Assigned( TMethod( fOnGTChange ).Data ) then
+ fOnGTChange( @Self );
+ ////////////////////////////////
+ if H <> 0 then
+ begin
+ DeleteObject( H );
+ {$IFDEF DEBUG_GDIOBJECTS}
+ case fType of
+ gttBrush: Dec( BrushCount );
+ gttFont: Dec( FontCount );
+ gttPen: Dec( PenCount );
+ end;
+ {$ENDIF}
+ end;
+ {$ENDIF GDI}
+ {$IFDEF GTK}
+ IF Assigned( fPangoFontDesc ) THEN
+ BEGIN
+ pango_font_description_free( fPangoFontDesc );
+ fPangoFontDesc := nil;
+ END;
+ /////////////////////////////////
+ IF Assigned( fOnGTChange ) THEN
+ /////////////////////////////////
+ fOnGTChange( @Self );
+ {$ENDIF GTK}
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+destructor TGraphicTool.Destroy;
+begin
+ {$IFDEF GDI}
+ case fType of
+ gttBrush: if fData.Brush.Bitmap <> 0 then
+ DeleteObject( fData.Brush.Bitmap );
+ gttPen: if fData.Pen.BrushBitmap <> 0 then
+ DeleteObject( fData.Pen.BrushBitmap )
+ end;
+ if fHandle <> 0 then
+ begin
+ DeleteObject( fHandle );
+ {$IFDEF DEBUG_GDIOBJECTS}
+ case fType of
+ gttPen: Dec( PenCount );
+ gttBrush: Dec( BrushCount );
+ gttFont: Dec( FontCount );
+ end;
+ {$ENDIF}
+ //fHandle := 0; Why to do this? It is now destroying!
+ end;
+ {$ENDIF GDI}
+ inherited;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF WIN_GDI}
+function TGraphicTool.HandleAllocated: Boolean;
+begin
+ Result := fHandle <> 0;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE ASM_VERSION PAS_VERSION}
+function TGraphicTool.ReleaseHandle: THANDLE;
+begin
+ Changed;
+ Result := fHandle;
+ fHandle := 0;
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF WIN_GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TGraphicTool.SetInt( const Index: Integer; Value: Integer );
+var Where: PInteger;
+begin
+ Where := Pointer( Integer( @ fData ) + Index );
+ if Where^ = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Where^ := Value;
+ Changed;
+end;
+{$ENDIF PAS_VERSION}
+
+function TGraphicTool.GetInt(const Index: Integer): Integer;
+var Where: PInteger;
+begin
+ Where := Pointer( Integer( @ fData ) + Index );
+ Result := Where^;
+end;
+{$IFDEF WIN_GDI}
+
+{$ENDIF WIN_GDI}
+procedure TGraphicTool.SetColor( Value: TColor );
+begin
+ SetInt( go_Color, Value );
+ fColorRGB := Color2RGB( Value );
+end;
+{$IFDEF WIN_GDI}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TGraphicTool.IsFontTrueType: Boolean;
+var OldFont: HFont;
+ DC: HDC;
+begin
+ Result := False;
+ if GetHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ DC := GetDC( 0 );
+ OldFont := SelectObject( DC, fHandle );
+ if GetFontData( DC, 0, 0, nil, 0 ) <> GDI_ERROR then
+ Result := True;
+ SelectObject( DC, OldFont );
+ ReleaseDC( 0, DC );
+end;
+{$ENDIF PAS_VERSION}
+
+function TGraphicTool.GetBrushBitmap: HBitmap;
+begin
+ Result := fData.Brush.Bitmap; // for BCB only
+end;
+
+procedure TGraphicTool.SetBrushBitmap(const Value: HBitmap);
+begin
+ if fData.Brush.Bitmap = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if fData.Brush.Bitmap <> 0 then
+ begin
+ Changed; // !!!
+ DeleteObject( fData.Brush.Bitmap );
+ end;
+ fData.Brush.Bitmap := Value;
+ Changed;
+end;
+
+function TGraphicTool.GetBrushStyle: TBrushStyle;
+begin
+ Result := fData.Brush.Style; // for BCB only
+end;
+
+{$ENDIF WIN_GDI}
+procedure TGraphicTool.SetBrushStyle(const Value: TBrushStyle);
+begin
+ if fData.Brush.Style = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fData.Brush.Style := Value;
+ Changed;
+end;
+{$IFDEF WIN_GDI}
+
+function TGraphicTool.GetFontCharset: TFontCharset;
+begin
+ Result := fData.Font.CharSet; // for BCB only
+end;
+
+procedure TGraphicTool.SetFontCharset(const Value: TFontCharset);
+begin
+ if fData.Font.Charset = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fData.Font.Charset := Value;
+ Changed;
+end;
+
+function TGraphicTool.GetFontQuality: TFontQuality;
+begin
+ Result := fData.Font.Quality; // for BCB only
+end;
+
+procedure TGraphicTool.SetFontQuality(const Value: TFontQuality);
+begin
+ if fData.Font.Quality = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fData.Font.Quality := Value;
+ Changed;
+end;
+{$ENDIF WIN_GDI}
+
+function TGraphicTool.GetFontName: KOLString;
+begin
+ Result := fData.Font.Name;
+ {$IFDEF GTK}
+ IF Result = '' THEN
+ Result := 'Sans Serif';
+ {$ENDIF GTK}
+end;
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION}
+procedure TGraphicTool.SetFontName(const Value: KOLString);
+begin
+ if KOLString(fData.Font.Name) = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ //FillChar( fData.Font.Name[ 0 ], LF_FACESIZE, #0 );
+ //ZeroMemory( @fData.Font.Name[ 0 ], LF_FACESIZE );
+ {$IFDEF UNICODE_CTRLS} WStrLCopy {$ELSE} StrLCopy {$ENDIF}
+ ( PKOLChar(@fData.Font.Name[0]), PKOLChar( Value ), Length(Value) * SizeOf(KOLChar) {LF_FACESIZE} );
+ Changed;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF WIN_GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TextAreaEx( Sender: PCanvas; var Sz : TSize; var Pt : TPoint );
+var Orient : Integer;
+ Pts : array[ 1..4 ] of TPoint;
+ MinX, MinY, I : Integer;
+ A : Double;
+begin
+ if not Sender.Font.IsFontTrueType then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Orient := Sender.Font.FontOrientation;
+ Pt.x := 0; Pt.y := 0;
+ if Orient = 0 then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ A := Orient / 1800.0 * PI;
+ Pts[ 1 ] := Pt;
+ Pts[ 2 ].x := Round( Sz.cx * cos( A ) );
+ Pts[ 2 ].y := - Round( Sz.cx * sin( A ) );
+ Pts[ 4 ].x := - Round( Sz.cy * cos( A + PI / 2 ) );
+ Pts[ 4 ].y := Round( Sz.cy * sin( A + PI / 2 ) );
+ Pts[ 3 ].x := Pts[ 2 ].x + Pts[ 4 ].x;
+ Pts[ 3 ].y := Pts[ 2 ].y + Pts[ 4 ].y;
+ MinX := 0; MinY := 0;
+ for I := 2 to 4 do
+ begin
+ if Pts[ I ].x < MinX then
+ MinX := Pts[ I ].x;
+ if Pts[ I ].y < MinY then
+ MinY := Pts[ I ].y;
+ end;
+ Sz.cx := 0;
+ Sz.cy := 0;
+ for I := 1 to 4 do
+ begin
+ Pts[ I ].x := Pts[ I ].x - MinX;
+ Pts[ I ].y := Pts[ I ].y - MinY;
+ if Pts[ I ].x > Sz.cx then
+ Sz.cx := Pts[ I ].x;
+ if Pts[ I ].y > Sz.cy then
+ Sz.cy := Pts[ I ].y;
+ end;
+ Pt := Pts[ 1 ];
+end;
+{$ENDIF PAS_VERSION}
+
+function TGraphicTool.GetFontOrientation: Integer;
+begin
+ Result := fData.Font.Orientation; // for BCB only
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TGraphicTool.SetFontOrientation(Value: Integer);
+begin
+ GlobalGraphics_UseFontOrient := True;
+ TOnTextArea( GlobalCanvas_OnTextArea ) := TextAreaEx;
+ Value := Value mod 3600; // -3599..+3599
+ SetInt( go_FontOrientation, Value );
+ SetInt( go_FontEscapement, Value );
+end;
+{$ENDIF PAS_VERSION}
+
+function TGraphicTool.GetFontPitch: TFontPitch;
+begin
+ Result := fData.Font.Pitch; // for BCB only
+end;
+
+procedure TGraphicTool.SetFontPitch(const Value: TFontPitch);
+begin
+ if fData.Font.Pitch = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fData.Font.Pitch := Value;
+ Changed;
+end;
+{$ENDIF WIN_GDI}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TGraphicTool.GetFontStyle: TFontStyle;
+type PFontStyle = ^TFontStyle;
+begin
+ Result := [ ];
+ if fData.Font.Weight >= 700 then Result := [ fsBold ];
+ if fData.Font.Italic then include( Result, fsItalic );
+ if fData.Font.Underline then include( Result, fsUnderline );
+ if fData.Font.StrikeOut then include( Result, fsStrikeOut );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TGraphicTool.SetFontStyle(const Value: TFontStyle);
+begin
+ if FontStyle = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if fsBold in Value then
+ begin
+ if fData.Font.Weight < 700 then
+ fData.Font.Weight := 700;
+ end else
+ begin
+ if fData.Font.Weight >= 700 then
+ fData.Font.Weight := 0;
+ end;
+ fData.Font.Italic := fsItalic in Value;
+ fData.Font.Underline := fsUnderline in Value;
+ fData.Font.StrikeOut := fsStrikeOut in Value;
+ Changed;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF WIN_GDI}
+function TGraphicTool.GetPenMode: TPenMode;
+begin
+ Result := fData.Pen.Mode; // for BCB only
+end;
+
+procedure TGraphicTool.SetPenMode(const Value: TPenMode);
+begin
+ if fData.Pen.Mode = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fData.Pen.Mode := Value;
+ Changed;
+end;
+
+function TGraphicTool.GetPenStyle: TPenStyle;
+begin
+ Result := fData.Pen.Style; // for BCB only
+end;
+
+procedure TGraphicTool.SetPenStyle(const Value: TPenStyle);
+begin
+ if fData.Pen.Style = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fData.Pen.Style := Value;
+ Changed;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TGraphicTool.GetHandle: THandle;
+begin
+ Result := fHandle;
+ if Result <> 0 then
+ begin
+ if Color2RGB( fData.Color ) <> fColorRGB then
+ begin
+ DeleteObject( ReleaseHandle );
+ Result := 0;
+ end;
+ end;
+ if Result = 0 then
+ begin
+ if ( fParentGDITool <> nil ) then
+ begin
+ if CompareMem( @ fData, @ fParentGDITool.fData, Sizeof( fData ) ) then
+ begin
+ Result := fParentGDITool.Handle;
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ fColorRGB := Color2RGB( fData.Color );
+ fMakeHandleProc( @Self );
+ Result := fHandle;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function MakeBrushHandle( Self_: PGraphicTool ): THandle;
+var
+ LogBrush: TLogBrush;
+begin
+ if Self_.fHandle = 0 then
+ begin
+ LogBrush.lbColor := Color2RGB( Self_.fData.Color );
+ if Self_.fData.Brush.Bitmap <> 0 then
+ begin
+ LogBrush.lbStyle := BS_PATTERN;
+ LogBrush.lbHatch := Self_.fData.Brush.Bitmap;
+ end else
+ begin
+ LogBrush.lbHatch := 0;
+ case Self_.fData.Brush.Style of
+ bsSolid: LogBrush.lbStyle := BS_SOLID;
+ bsClear: LogBrush.lbStyle := BS_NULL;
+ else LogBrush.lbStyle := BS_HATCHED;
+ LogBrush.lbHatch := Ord(Self_.fData.Brush.Style)-Ord(bsHorizontal);
+ LogBrush.lbColor := Color2RGB( Self_.fData.Brush.LineColor );
+ end;
+ end;
+ Self_.fHandle := CreateBrushIndirect(LogBrush);
+ {$IFDEF DEBUG_GDIOBJECTS}
+ if Self_.fHandle <> 0 then
+ Inc( BrushCount )
+ else ShowMessage( 'Could not create brush, error ' + Int2Str( GetLastError ) +
+ ': ' + SysErrorMessage( GetLastError ) );
+ {$ENDIF}
+ end;
+ Result := Self_.fHandle;
+end;
+{$ENDIF PAS_VERSION}
+
+{$UNDEF ASM_LOCAL}
+{$IFNDEF UNICODE_CTRLS}
+ {$IFDEF ASM_VERSION}
+ {$IFNDEF AUTO_REPLACE_CLEARTYPE}
+ {$DEFINE ASM_LOCAL}
+ {$ENDIF AUTO_REPLACE_CLEARTYPE}
+ {$ENDIF PAS_VERSION}
+{$ENDIF}
+
+{$IFDEF ASM_LOCAL}
+function MakeFontHandle( Self_: PGraphicTool ): THandle;
+asm
+ XCHG EDX, EAX
+ MOV EAX, [EDX].TGraphicTool.fHandle
+ TEST EAX, EAX
+ JNZ @@exit
+ PUSH EDX
+ LEA ECX, [EDX].TGraphicTool.fData.Font
+ PUSH ECX
+ CALL CreateFontIndirect
+ POP EDX
+ MOV [EDX].TGraphicTool.fHandle, EAX
+@@exit:
+end;
+{$ELSE PAS_VERSION} //Pascal
+function MakeFontHandle( Self_: PGraphicTool ): THandle;
+{$IFDEF AUTO_REPLACE_CLEARTYPE}
+var LF: TLogFont;
+{$ENDIF}
+begin
+ with Self_^ do
+ begin
+ if fHandle = 0 then
+ begin
+ {$IFDEF AUTO_REPLACE_CLEARTYPE}
+ Move( fData.Font, LF, Sizeof( LF ) );
+ if WinVer < wvXP then
+ begin
+ if LF.lfQuality > ANTIALIASED_QUALITY then
+ LF.lfQuality := ANTIALIASED_QUALITY;
+ end;
+ fHandle := CreateFontIndirect( LF );
+ {$ELSE}
+ fHandle := CreateFontIndirect( PLogFont( @ fData.Font )^ );
+ {$ENDIF}
+ {$IFDEF DEBUG_GDIOBJECTS}
+ Inc( FontCount );
+ {$ENDIF}
+ end;
+ Result := fHandle;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function MakePenHandle( Self_: PGraphicTool ): THandle;
+var
+ LogPen: TLogPen;
+begin
+ with Self_^ do
+ begin
+ //GlobalGraphics_OnObjectCreating( @Self );
+ if fHandle = 0 then
+ with LogPen do
+ begin
+ lopnStyle := Byte( fData.Pen.Style );
+ lopnWidth.X := fData.Pen.Width;
+ lopnColor := Color2RGB( fData.Color );
+ fHandle := CreatePenIndirect( LogPen );
+ {$IFDEF DEBUG_GDIOBJECTS}
+ Inc( PenCount );
+ {$ENDIF}
+ end;
+ //GlobalGraphics_OnObjectCreated( @Self );
+ Result := fHandle;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+function TGraphicTool.GetGeometricPen: Boolean;
+begin
+ Result := fData.Pen.Geometric; // for BCB only
+end;
+
+procedure TGraphicTool.SetGeometricPen(const Value: Boolean);
+begin
+ if fData.Pen.Geometric = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fData.Pen.Geometric := Value;
+ fMakeHandleProc := MakeGeometricPenHandle;
+ Changed;
+end;
+
+function TGraphicTool.GetPenEndCap: TPenEndCap;
+begin
+ Result := fData.Pen.EndCap; // for BCB only
+end;
+
+procedure TGraphicTool.SetPenEndCap(const Value: TPenEndCap);
+begin
+ if fData.Pen.EndCap = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fData.Pen.EndCap := Value;
+ Changed;
+end;
+
+function TGraphicTool.GetPenJoin: TPenJoin;
+begin
+ Result := fData.Pen.Join; // for BCB only
+end;
+
+procedure TGraphicTool.SetPenJoin(const Value: TPenJoin);
+begin
+ if fData.Pen.Join = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fData.Pen.Join := Value;
+ Changed;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function MakeGeometricPenHandle( Self_: PGraphicTool ): THandle;
+const
+ PenStyles: array[ TPenStyle ] of Word =
+ (PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOT, PS_DASHDOTDOT, PS_NULL,
+ PS_INSIDEFRAME);
+ PenEndCapStyles: array[ TPenEndCap ] of Word =
+ (PS_ENDCAP_ROUND, PS_ENDCAP_SQUARE, PS_ENDCAP_FLAT);
+ PenJoinStyles: array[ TPenJoin ] of Word =
+ (PS_JOIN_ROUND, PS_JOIN_BEVEL, PS_JOIN_MITER );
+var
+ LogBrush: TLogBrush;
+begin
+ if Self_.fHandle = 0 then
+ with Self_^, LogBrush do
+ begin
+ lbColor := Color2RGB( fData.Color );
+ lbHatch := 0;
+ if fData.Pen.BrushBitmap <> 0 then
+ begin
+ lbStyle := BS_PATTERN;
+ lbHatch := fData.Pen.BrushBitmap;
+ end else
+ case fData.Pen.BrushStyle of
+ bsSolid: lbStyle := BS_SOLID;
+ bsClear: lbStyle := BS_NULL;
+ else lbStyle := BS_HATCHED;
+ case fData.Pen.BrushStyle of
+ bsHorizontal: lbHatch := HS_HORIZONTAL;
+ bsVertical: lbHatch := HS_VERTICAL;
+ bsFDiagonal: lbHatch := HS_FDIAGONAL;
+ bsBDiagonal: lbHatch := HS_BDIAGONAL;
+ bsCross: lbHatch := HS_CROSS;
+ bsDiagCross: lbHatch := HS_DIAGCROSS;
+ end;
+ end;
+ end;
+ Self_.fHandle := ExtCreatePen( PS_GEOMETRIC or Byte( Self_.fData.Pen.Style ) or
+ PenEndCapStyles[ Self_.fData.Pen.EndCap ] or
+ PenJoinStyles[ Self_.fData.Pen.Join ],
+ Self_.fData.Pen.Width, LogBrush, 0, nil );
+ {Assert( Self_.fHandle <> 0, 'Error ' + Int2Str( GetLastError ) +
+ ': ' + SysErrorMessage( GetLastError ) );}
+ {$IFDEF DEBUG_GDIOBJECTS}
+ Inc( PenCount );
+ {$ENDIF}
+ Result := Self_.fHandle;
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF WIN_GDI}
+function TGraphicTool.GetFontWeight: Integer;
+begin
+ Result := fData.Font.Weight; // for BCB only
+end;
+
+procedure TGraphicTool.SetFontWeight(const Value: Integer);
+begin
+ if fData.Font.Weight = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fData.Font.Weight := Value;
+ Changed;
+end;
+{$IFDEF WIN_GDI}
+
+procedure TGraphicTool.SetLogFontStruct(const Value: TLogFont);
+begin
+ if CompareMem(@fData.Font, @Value, SizeOf(TLogFont)) then Exit; {>>>>>>>>>>>}
+ Move(Value, fData.Font, SizeOF(TLogFont));
+ Changed;
+end;
+
+function TGraphicTool.GetLogFontStruct: TLogFont;
+begin
+ Move(fData.Font, Result, SizeOf(TLogFont));
+end;
+{$ENDIF WIN_GDI}
+
+{$IFDEF _X_}
+{$IFDEF GTK}
+FUNCTION TGraphicTool.GetPangoFontDesc: PPangoFontDescription;
+VAR s: AnsiString;
+ i: Integer;
+ FUNCTION IfThen( cond: Boolean; CONST s: AnsiString ): AnsiString;
+ BEGIN
+ Result := '';
+ IF cond THEN Result := s;
+ END;
+{const Weights: array[0..9] of String = ( 'Ultralight',
+ 'Ultralight', 'Ultralight',
+ 'Light', 'Normal', 'Normal', 'Normal',
+ 'Bold', 'Ultrabold', 'Heavy' );}
+BEGIN
+ IF NOT Assigned( fPangoFontDesc ) THEN
+ BEGIN
+ s := FontName; { + ' ' +
+ IfThen( FontWeight <> 400, Weights[ FontWeight div 100 ] + ' ' ) +
+ IfThen( fsItalic in FontStyle, 'Italic ' ) {+
+ Int2Str( FontHeight )};
+ fPangoFontDesc := pango_font_description_from_string( PAnsiChar( s ) );
+ i := FontHeight;
+ IF i > 0 THEN
+ pango_font_description_set_absolute_size( fPangoFontDesc, i * PANGO_SCALE );
+ //i := pango_font_description_get_size( fPangoFontDesc );
+ i := PANGO_STYLE_NORMAL;
+ IF fsItalic IN FontStyle THEN i := PANGO_STYLE_ITALIC;
+ pango_font_description_set_style( fPangoFontDesc, i );
+ pango_font_description_set_weight( fPangoFontDesc, FontWeight );
+ END;
+ Result := fPangoFontDesc;
+END;
+
+FUNCTION Color2GDKColor( Color: TColor ): TGdkColor;
+BEGIN
+ Color := Color2RGB( Color );
+ Result.pixel := 0;
+ Result.red := (Color and $FF) shl 8;
+ Result.green := Color and $FF00;
+ Result.blue := (Color shr 8) and $FF00;
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+{$IFDEF WIN_GDI}
+
+{ TCanvas }
+
+type
+ TStock = Packed Record
+ StockPen: HPEN;
+ StockBrush: HBRUSH;
+ StockFont: HFONT;
+ end;
+
+var
+ Stock: TStock;
+
+destructor TCanvas.Destroy;
+begin
+ Handle := 0;
+ fPen.Free;
+ fBrush.Free;
+ fFont.Free;
+ inherited;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TCanvas.Assign(SrcCanvas: PCanvas): Boolean;
+begin
+ fFont := fFont.Assign( SrcCanvas.fFont );
+ fBrush := fBrush.Assign( SrcCanvas.fBrush );
+ fPen := fPen.Assign( SrcCanvas.fPen );
+ AssignChangeEvents;
+ Result := (fFont <> nil) or (fBrush <> nil) or (fPen <> nil);
+ if (SrcCanvas.PenPos.x <> PenPos.x) or (SrcCanvas.PenPos.y <> PenPos.y) then
+ begin
+ Result := True;
+ PenPos := SrcCanvas.PenPos;
+ end;
+ if SrcCanvas.ModeCopy <> ModeCopy then
+ begin
+ Result := True;
+ ModeCopy := SrcCanvas.ModeCopy;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.CreateBrush;
+begin
+ if assigned( fBrush ) then
+ begin
+ SelectObject( GetHandle, fBrush.Handle );
+ AssignChangeEvents;
+ if fBrush.fData.Brush.Style = bsSolid then
+ begin
+ SetBkColor( fHandle, Color2RGB( fBrush.fData.Color ) );
+ SetBkMode( fHandle, OPAQUE );
+ end else
+ begin
+ { Win95 doesn't draw brush hatches if bkcolor = brush color }
+ { Since bkmode is transparent, nothing should use bkcolor anyway }
+ SetBkColor( fHandle, not Color2RGB( fBrush.fData.Color ) );
+ SetBkMode( fHandle, TRANSPARENT );
+ end;
+ end else
+ if Assigned( fOwnerControl ) then
+ begin
+ SetBkColor( GetHandle, Color2RGB( PControl( fOwnerControl ).fColor ) );
+ SetBkMode( fHandle, OPAQUE );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.CreateFont;
+begin
+ if ( fFont <> nil ) then
+ begin
+ SelectObject( GetHandle, fFont.Handle );
+ SetTextColor( fHandle, Color2RGB( fFont.fData.Color ) );
+ AssignChangeEvents;
+ end else
+ if ( fOwnerControl <> nil ) then
+ begin
+ SetTextColor( fHandle,
+ Color2RGB( PControl( fOwnerControl ).fTextColor ) );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.CreatePen;
+begin
+ if ( fPen <> nil ) then
+ begin
+ SelectObject( GetHandle, fPen.Handle );
+ SetROP2( fHandle, Ord( fPen.fData.Pen.Mode ) + 1 );
+ AssignChangeEvents;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+function TCanvas.GetPixels(X, Y: Integer): TColor;
+begin
+ RequiredState( HandleValid );
+ Result := Windows.GetPixel(FHandle, X, Y);
+end;
+
+procedure TCanvas.SetPixels(X, Y: Integer; const Value: TColor);
+begin
+ Changing;
+ RequiredState( HandleValid );
+ Windows.SetPixel(FHandle, X, Y, Color2RGB( Value ));
+end;
+
+procedure TCanvas.OffsetAndRotate(Xoff, Yoff: Integer; Angle: Double);
+var F: TXForm;
+begin
+ SetGraphicsMode( fHandle, GM_ADVANCED );
+ F.eM11 := cos( Angle );
+ F.eM12 := sin( Angle );
+ F.eM21 := -F.eM12;
+ F.eM22 := F.eM11;
+ F.eDx := Xoff;
+ F.eDy := Yoff;
+ SetWorldTransform( fHandle, F );
+ if (Angle = 0) and (Xoff = 0) and (Yoff = 0) then
+ SetGraphicsMode( fHandle, GM_COMPATIBLE );
+end;
+
+{$ENDIF WIN_GDI}
+
+{$IFDEF _X_}
+{$IFDEF GTK}
+PROCEDURE TCanvas.SaveState;
+BEGIN
+ gdk_gc_get_values( fHandle, @ fSavedState );
+END;
+
+PROCEDURE TCanvas.RestoreState;
+VAR mask: DWORD;
+BEGIN
+ mask := $1FFFF;
+ if fSavedState.font = nil then mask := mask and not GDK_GC_FONT;
+ if fSavedState.stipple = nil then mask := mask and not GDK_GC_STIPPLE;
+ gdk_gc_set_values( fHandle, @ fSavedState, mask );
+ DeselectHandles;
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.DeselectHandles;
+begin
+ if (fHandle <> 0) and
+ LongBool(fState and (PenValid or BrushValid or FontValid)) then
+ with Stock do
+ begin
+ if StockPen = 0 then
+ begin
+ StockPen := GetStockObject(BLACK_PEN);
+ StockBrush := GetStockObject(HOLLOW_BRUSH);
+ StockFont := GetStockObject(SYSTEM_FONT);
+ end;
+ SelectObject( fHandle, StockPen );
+ SelectObject( fHandle, StockBrush );
+ SelectObject( fHandle, StockFont );
+ fState := fState and not( PenValid or BrushValid or FontValid );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+PROCEDURE TCanvas.DeselectHandles;
+BEGIN
+ IF ( fFont <> nil ) AND ( fFont.fPangoFontDesc <> nil ) THEN
+ BEGIN
+ pango_font_description_free( fFont.fPangoFontDesc );
+ fFont.fPangoFontDesc := nil;
+ END;
+ fState := fState and not( PenValid or BrushValid or FontValid );
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF WIN_GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TCanvas.RequiredState(ReqState: DWORD): HDC; stdcall;
+var
+ NeededState: Byte;
+begin
+ if Boolean(ReqState and ChangingCanvas) then
+ Changing;
+ ReqState := ReqState and 15;
+ NeededState := Byte( ReqState ) and not fState;
+ Result := 0;
+ if Boolean(ReqState and HandleValid) then
+ begin
+ if GetHandle = 0 then Exit; // Important! {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ if NeededState <> 0 then
+ begin
+ if Boolean( NeededState and FontValid ) then
+ CreateFont;
+ if Boolean( NeededState and PenValid ) then
+ begin
+ CreatePen;
+ if ( fPen <> nil ) then
+ if fPen.fData.Pen.Style in [psDash, psDot, psDashDot, psDashDotDot] then
+ NeededState := NeededState or BrushValid;
+ end;
+ if Boolean( NeededState and BrushValid ) then
+ CreateBrush;
+ fState := fState or NeededState;
+ end;
+ Result := fHandle;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF WIN_GDI}
+
+{$IFDEF _X_}
+{$IFDEF GTK}
+PROCEDURE TCanvas.ForeBack(fg_color, bk_color: TColor); // install colors just before drawing
+BEGIN
+ fg_color := RGB2BGR( Color2RGB( fg_color ) );
+ bk_color := RGB2BGR( Color2RGB( bk_color ) );
+ gdk_rgb_gc_set_foreground( fHandle, fg_color );
+ gdk_rgb_gc_set_background( fHandle, bk_color );
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF WIN_GDI}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.SetHandle(Value: HDC);
+{$IFDEF F_P}
+var Ptr1: Pointer;
+{$ENDIF F_P}
+begin
+ if fHandle = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if fHandle <> 0 then
+ begin
+ DeselectHandles;
+ {$IFDEF GDI}
+ if (fOwnerControl = nil) or
+ (PControl(fOwnerControl).fPaintDC <> fHandle) then
+ begin
+ {$IFDEF F_P}
+ Ptr1 := Self;
+ asm
+ MOV EAX, [Ptr1]
+ MOV EAX, [EAX].TCanvas.fOnGetHandle
+ MOV [Ptr1], EAX
+ end [ 'EAX' ];
+ if Ptr1 = @ TControl.DC2Canvas then
+ {$ELSE DELPHI}
+ //////////////////// SLAG
+ if TMethod(fOnGetHandle).Code =
+ @TControl.Dc2Canvas then
+ {$ENDIF F_P/DELPHI}
+ ReleaseDC( PControl(fOwnerControl).Handle, fHandle )
+ else
+ if not (fIsAlienDC or fIsPaintDC) then
+ DeleteDC( fHandle );
+ ////////////////////
+ end;
+ {$ENDIF GDI}
+ fHandle := 0;
+ fIsPaintDC := False;
+ fState := fState and not HandleValid;
+ end;
+ if Value <> 0 then
+ begin
+ fState := fState or HandleValid;
+ fHandle := Value;
+ SetPenPos( fPenPos );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF WIN_GDI}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.SetPenPos(const Value: TPoint);
+begin
+ fPenPos := Value;
+ {$IFDEF GDI}
+ MoveTo( Value.x, Value.y );
+ {$ENDIF GDI}
+end;
+{$ENDIF PAS_VERSION}
+{$IFDEF WIN_GDI}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.Changing;
+begin
+ if Assigned( fOnChangeCanvas ) then
+ fOnChangeCanvas( @Self );
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF WIN_GDI}
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.Arc(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer); stdcall;
+begin
+ RequiredState( HandleValid or PenValid or ChangingCanvas );
+ Windows.Arc(FHandle, X1, Y1, X2, Y2, X3, Y3, X4, Y4);
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+PROCEDURE TCanvas.Arc(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer); STDCALL;
+VAR C: TPoint;
+ angle1, angle2: Integer;
+ A1, A2: Double;
+BEGIN
+ ////RequiredState( {HandleValid or} PenValid or ChangingCanvas );
+ C := MakePoint( (X1 + X2) div 2, (Y1 + Y2) div 2 );
+ {$IFDEF NOT_USE_EXCEPTION}
+ A1 := ArcTan2( Y3-C.Y, X3-C.X );
+ A2 := ArcTan2( Y4-C.Y, X4-C.X );
+ {$ELSE USE_EXCEPTION}
+ TRY
+ A1 := ArcTan2( Y3-C.Y, X3-C.X );
+ EXCEPT
+ A1 := 0;
+ END;
+ TRY
+ A2 := ArcTan2( Y4-C.Y, X4-C.X );
+ EXCEPT
+ A2 := 0;
+ END;
+ {$ENDIF NOT_USE_EXCEPTION}
+ angle1 := -Round(A1 * 180 * 64 / PI);
+ angle2 := -Round(A2 * 180 * 64 / PI);
+ IF Brush.BrushStyle <> bsClear THEN
+ BEGIN
+ ForeBack( Brush.Color, Brush.Color );
+ gdk_draw_arc( fDrawable, fHandle, 1, X1, Y1, X2-X1, Y2-Y1, angle1, angle2 );
+ END;
+ ForeBack( Pen.Color, Brush.Color );
+ gdk_draw_arc( fDrawable, fHandle, 0, X1, Y1, X2-X1, Y2-Y1, angle1, angle2 );
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+{$IFDEF WIN_GDI}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.Chord(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer); stdcall;
+begin
+ RequiredState( HandleValid or PenValid or BrushValid or ChangingCanvas );
+ Windows.Chord(FHandle, X1, Y1, X2, Y2, X3, Y3, X4, Y4);
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.CopyRect(const DstRect: TRect; SrcCanvas: PCanvas;
+ const SrcRect: TRect);
+begin
+ RequiredState( HandleValid or FontValid or BrushValid or ChangingCanvas );
+ SrcCanvas.RequiredState( HandleValid or BrushValid );
+ StretchBlt( fHandle, DstRect.Left, DstRect.Top, DstRect.Right - DstRect.Left,
+ DstRect.Bottom - DstRect.Top, SrcCanvas.Handle, SrcRect.Left, SrcRect.Top,
+ SrcRect.Right - SrcRect.Left, SrcRect.Bottom - SrcRect.Top, ModeCopy);
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.DrawFocusRect({$IFNDEF FPC}const{$ENDIF} Rect: TRect);
+begin
+ RequiredState( HandleValid or BrushValid or FontValid or ChangingCanvas );
+ Windows.DrawFocusRect(FHandle, Rect);
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.Ellipse(X1, Y1, X2, Y2: Integer);
+begin
+ RequiredState( HandleValid or PenValid or BrushValid or ChangingCanvas );
+ Windows.Ellipse(FHandle, X1, Y1, X2, Y2);
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF WIN_GDI}
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.FillRect({$IFNDEF FPC}const{$ENDIF} Rect: TRect);
+var Br: HBrush;
+begin
+ RequiredState( HandleValid or BrushValid or ChangingCanvas );
+ if fBrush <> nil then
+ begin
+ Windows.FillRect(fHandle, Rect, fBrush.Handle);
+ end else
+ if ( fOwnerControl <> nil ) then
+ begin
+ {$IFDEF GDI}
+ if ( PControl( fOwnerControl ).fBrush <> nil ) then
+ Windows.FillRect( fHandle, Rect, PControl( fOwnerControl ).fBrush.Handle )
+ else
+ begin
+ Br := CreateSolidBrush( Color2RGB(PControl(fOwnerControl).fColor) );
+ Windows.FillRect(fHandle, Rect, Br );
+ DeleteObject( Br );
+ end;
+ {$ENDIF GDI}
+ end else
+ Windows.FillRect(fHandle, Rect, HBrush(COLOR_WINDOW + 1) );
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+PROCEDURE TCanvas.FillRect({$IFNDEF FPC}const{$ENDIF} Rect: TRect);
+BEGIN
+ if (fBrush <> nil) and (fBrush.BrushStyle = bsClear) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ ForeBack( Brush.Color, Brush.Color );
+ gdk_draw_rectangle( fDrawable, fHandle, 1, Rect.Left, Rect.Top,
+ Rect.Right-Rect.Left, Rect.Bottom-Rect.Top );
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+{$IFDEF WIN_GDI}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.FillRgn(const Rgn: HRgn);
+var Br : HBrush;
+begin
+ RequiredState( HandleValid or BrushValid or ChangingCanvas );
+ if ( fBrush <> nil ) then
+ Windows.FillRgn(FHandle, Rgn, fBrush.Handle )
+ else if ( fOwnerControl <> nil ) then
+ begin
+ {$IFDEF GDI}
+ if ( PControl( fOwnerControl ).fBrush <> nil ) then
+ Windows.FillRgn( FHandle, Rgn, PControl( fOwnerControl ).fBrush.Handle )
+ else
+ begin
+ Br := CreateSolidBrush( Color2RGB(PControl(fOwnerControl).fColor) );
+ Windows.FillRgn( fHandle, Rgn, Br );
+ DeleteObject( Br );
+ end;
+ {$ENDIF GDI}
+ end else
+ begin
+ Br := CreateSolidBrush( DWORD(clWindow) );
+ Windows.FillRgn( fHandle, Rgn, Br );
+ DeleteObject( Br );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.FloodFill(X, Y: Integer; Color: TColor;
+ FillStyle: TFillStyle);
+const
+ FillStyles: array[TFillStyle] of Word =
+ (FLOODFILLSURFACE, FLOODFILLBORDER);
+begin
+ RequiredState( HandleValid or BrushValid or ChangingCanvas );
+ Windows.ExtFloodFill(FHandle, X, Y, Color, FillStyles[FillStyle]);
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.FrameRect({$IFNDEF FPC}const{$ENDIF} Rect: TRect);
+var SolidBr : HBrush;
+begin
+ RequiredState( HandleValid or ChangingCanvas );
+ if fBrush <> nil then
+ SolidBr := CreateSolidBrush( Color2RGB( fBrush.fData.Color ) )
+ else
+ if fOwnerControl <> nil then
+ SolidBr := CreateSolidBrush( PControl(fOwnerControl).fColor )
+ else SolidBr := CreateSolidBrush( clWhite );
+ Windows.FrameRect(FHandle, Rect, SolidBr);
+ DeleteObject( SolidBr );
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF WIN_GDI}
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.LineTo(X, Y: Integer);
+begin
+ RequiredState( HandleValid or PenValid or BrushValid or ChangingCanvas );
+ Windows.LineTo( fHandle, X, Y );
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+PROCEDURE TCanvas.LineTo(X, Y: Integer);
+BEGIN
+ ForeBack( Pen.Color, Brush.Color );
+ gdk_draw_line( fDrawable, fHandle, fPenPos.X, fPenPos.Y, X, Y );
+ fPenPos := MakePoint( X, Y );
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.MoveTo(X, Y: Integer);
+begin
+ RequiredState( HandleValid );
+ Windows.MoveToEx( fHandle, X, Y, nil );
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+PROCEDURE TCanvas.MoveTo(X, Y: Integer);
+BEGIN
+ fPenPos := MakePoint( X, Y );
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+procedure TCanvas.ObjectChanged(Sender: PGraphicTool);
+begin
+ DeselectHandles;
+end;
+
+{$IFDEF WIN_GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.Pie(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer); stdcall;
+begin
+ RequiredState( HandleValid or PenValid or BrushValid or ChangingCanvas );
+ Windows.Pie( fHandle, X1, Y1, X2, Y2, X3, Y3, X4, Y4);
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.Polygon(const Points: array of TPoint);
+type
+ PPoints = ^TPoints;
+ TPoints = array[0..0] of TPoint;
+begin
+ RequiredState( HandleValid or PenValid or BrushValid or ChangingCanvas );
+ {$IFDEF F_P} Windows_Polygon
+ {$ELSE DELPHI} Windows.Polygon
+ {$ENDIF}( fHandle, PPoints(@Points)^, High(Points) + 1);
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.Polyline(const Points: array of TPoint);
+type
+ PPoints = ^TPoints;
+ TPoints = array[0..0] of TPoint;
+begin
+ RequiredState( HandleValid or PenValid or BrushValid or ChangingCanvas );
+ {$IFDEF F_P}Windows_Polyline
+ {$ELSE DELPHI}Windows.Polyline
+ {$ENDIF}( fHandle, PPoints(@Points)^, High(Points) + 1);
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.Rectangle(X1, Y1, X2, Y2: Integer);
+begin
+ RequiredState( HandleValid or BrushValid or PenValid or ChangingCanvas );
+ Windows.Rectangle( fHandle, X1, Y1, X2, Y2);
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.RoundRect(X1, Y1, X2, Y2, X3, Y3: Integer);
+begin
+ RequiredState( HandleValid or BrushValid or PenValid or ChangingCanvas );
+ Windows.RoundRect( fHandle, X1, Y1, X2, Y2, X3, Y3);
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF WIN_GDI}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.TextArea(const Text: KOLString; var Sz: TSize;
+ var P0: TPoint);
+begin
+ Sz := TextExtent( Text );
+ P0.x := 0; P0.y := 0;
+ TOnTextArea( GlobalCanvas_OnTextArea )( @Self, Sz, P0 );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF _D3orHigher}
+procedure TCanvas.WTextArea(const Text: KOLWideString; var Sz: TSize;
+ var P0: TPoint);
+begin
+ Sz := WTextExtent( Text );
+ P0.x := 0; P0.y := 0;
+ TOnTextArea( GlobalCanvas_OnTextArea )( @Self, Sz, P0 );
+end;
+{$ENDIF _D3orHigher}
+
+{$IFDEF GDI}
+{$IFDEF TEXT_EXTENT_OLD}
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function TCanvas.TextExtent(const Text: KOLString): TSize;
+var DC : HDC;
+ ClearHandle : Boolean;
+begin
+ ClearHandle := False;
+ RequiredState( HandleValid or FontValid );
+ DC := fHandle;
+ if DC = 0 then
+ begin
+ DC := CreateCompatibleDC( 0 );
+ ClearHandle := True;
+ SetHandle( DC );
+ If Not (fIsAlienDC or fIsPaintDC) then
+ ClearHandle := True; //************ // Added By Gerasimov
+ end;
+ RequiredState( HandleValid or FontValid );
+ GetTextExtentPoint32( fHandle, PKOLChar(Text), Length(Text), Result);
+ {$IFDEF FIX_ITALIC_TEXT_WIDTH}
+ if fsItalic in Font.FontStyle then
+ begin
+ inc( Result.cx, Result.cy div 4 );
+ end;
+ {$ENDIF}
+ if ClearHandle then
+ SetHandle( 0 );
+ { DC must be freed here automatically (never leaks):
+ if Canvas created on base of existing DC, no memDC created,
+ if Canvas has fHandle:HDC = 0, it is not fIsPaintDC always. }
+end;
+{$ENDIF PAS_VERSION}
+{$ELSE TEXT_EXTENT_NEW}
+{$IFDEF ASM_UNICODE}{$ELSE notASM_VERSION}
+function TCanvas.TextExtent(const Text: KOLString): TSize;
+begin
+ RequiredState( HandleValid or FontValid );
+ GetTextExtentPoint32( fHandle, PKOLChar(Text), Length(Text), Result);
+ {$IFDEF FIX_ITALIC_TEXT_WIDTH}
+ if Font.fData.Font.Italic then
+ inc( Result.cx, Result.cy div 4 );
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF TEXT_EXTENT_NEW}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+FUNCTION TCanvas.TextExtent(const Text: KOLString): TSize;
+VAR layout: PPangoLayout;
+ context: PPangoContext;
+BEGIN
+ //RequiredState( HandleValid or FontValid );
+ IF fOwnerControl <> nil THEN
+ BEGIN
+ context := nil;
+ layout := gtk_widget_create_pango_layout(
+ PControl( fOwnerControl ).fEventboxHandle, nil );
+ END ELSE
+ BEGIN //todo: seems not working in such way... What to do for memory bitmap?
+ context := pango_context_new;
+ //layout := gtk_widget_create_pango_layout( fHandle, PChar( Text ) );
+ layout := pango_layout_new( context );
+ END;
+ pango_layout_set_font_description( layout, Font.FontHandle );
+ pango_layout_set_text( layout, PAnsiChar( Text ), Length( Text ) );
+ pango_layout_get_size( layout, @ Result.cx, @ Result.cy );
+ g_object_unref( layout );
+ IF context <> nil THEN g_object_unref( context );
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+function TCanvas.TextHeight(const Text: KOLString): Integer;
+begin
+ Result := TextExtent(Text).cY;
+end;
+
+{$IFDEF GDI}
+procedure TCanvas.TextOutA(X, Y: Integer; const Text: AnsiString); stdcall;
+begin
+ RequiredState( HandleValid or FontValid or BrushValid or ChangingCanvas );
+ Windows.TextOutA(FHandle, X, Y, PAnsiChar(Text), Length(Text));
+end;
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.TextOut(X, Y: Integer; const Text: KOLString); stdcall;
+begin
+ RequiredState( HandleValid or FontValid or BrushValid or ChangingCanvas );
+ {$IFDEF UNICODE_CTRLS}Windows.TextOutW
+ {$ELSE} Windows.TextOutA
+ {$ENDIF}(FHandle, X, Y, PKOLChar(Text), Length(Text));
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+PROCEDURE TCanvas.TextOut(X, Y: Integer; CONST Text: AnsiString); STDCALL;
+VAR Options: Integer;
+BEGIN
+ Options := 0;
+ if Brush.BrushStyle <> bsClear then Options := ETO_OPAQUE;
+ ExtTextOut( X, Y, Options, MakeRect( 0,0,0,0 ), Text, [ ] );
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.TextRect(const Rect: TRect; X, Y: Integer; const Text: Ansistring);
+var
+ Options: Integer;
+begin
+ //Changing;
+ RequiredState( HandleValid or FontValid or BrushValid or ChangingCanvas );
+ Options := ETO_CLIPPED;
+ if ( fBrush <> nil ) and (fBrush.fData.Brush.Style <> bsClear)
+ or ( fBrush = nil ) then Inc(Options, ETO_OPAQUE);
+ Windows.ExtTextOutA( fHandle, X, Y, Options,
+ @Rect, PAnsiChar(Text),
+ Length(Text), nil); // KOL_ANSI
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+PROCEDURE TCanvas.TextRect(CONST Rect: TRect; X, Y: Integer; CONST Text: Ansistring);
+VAR Options: Integer;
+BEGIN
+ Options := ETO_CLIPPED;
+ IF Brush.BrushStyle <> bsClear THEN Options := Options or ETO_OPAQUE;
+ ExtTextOut( X, Y, Options, Rect, Text, [] ); // KOL_ANSI
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF GDI}
+procedure TCanvas.ExtTextOut( X, Y: Integer; Options: DWORD; const Rect: TRect; const Text: AnsiString;
+ const Spacing: array of Integer );
+begin
+ RequiredState( HandleValid or FontValid or BrushValid or ChangingCanvas );
+ windows.ExtTextOutA(FHandle, X, Y, Options, @Rect, PAnsiChar(Text), Length(Text), @Spacing[ 0 ]); // KOL_ANSI have not Ex
+end;
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+PROCEDURE TCanvas.ExtTextOut( X, Y: Integer; Options: DWORD; CONST Rect: TRect;
+ CONST Text: AnsiString; CONST Spacing: ARRAY of Integer );
+VAR context: PPangoContext;
+ layout: PPangoLayout;
+ w, h: Integer;
+ pixmap: PGdkPixmap;
+BEGIN
+ ////RequiredState( {HandleValid or} FontValid or BrushValid or ChangingCanvas );
+ w := Rect.Right - Rect.Left;
+ h := Rect.Bottom - Rect.Top;
+ IF fOwnerControl <> nil THEN
+ BEGIN
+ context := nil;
+ layout := gtk_widget_create_pango_layout(
+ PControl( fOwnerControl ).fEventboxHandle, nil );
+ END ELSE
+ BEGIN //todo: seems not working in such way... What to do for memory bitmap?
+ context := pango_context_new;
+ //layout := gtk_widget_create_pango_layout( fHandle, PChar( Text ) );
+ layout := pango_layout_new( context );
+ END;
+ pango_layout_set_font_description( layout, Font.FontHandle );
+ pango_layout_set_text( layout, PAnsiChar( Text ), Length( Text ) );
+ IF Options AND ETO_CLIPPED = 0 THEN
+ BEGIN
+ pango_layout_get_size( layout, @ w, @ h );
+ w := w div PANGO_SCALE;
+ h := h div PANGO_SCALE;
+ END;
+ pixmap := gdk_pixmap_new( PControl( fOwnerControl ).fEventboxHandle.window,
+ w, h, -1 ); //todo: use MainForm
+ IF Options AND ETO_OPAQUE <> 0 THEN
+ BEGIN
+ ForeBack( Brush.Color, Brush.Color );
+ gdk_draw_rectangle( GDK_DRAWABLE( pixmap ), fHandle, 1, 0, 0, w, h );
+ END ELSE
+ BEGIN
+ gdk_draw_drawable( GDK_DRAWABLE( pixmap ), fHandle, fDrawable,
+ Rect.Left, Rect.Top, 0, 0, w, h );
+ END;
+ ForeBack( Font.Color, Brush.Color );
+ gdk_draw_layout( GDK_DRAWABLE( pixmap ), fHandle, X, Y, layout );
+ g_object_unref( layout );
+ gdk_draw_drawable( fDrawable, fHandle, GDK_DRAWABLE( pixmap ),
+ 0, 0, Rect.Left, Rect.Top, w, h );
+ g_object_unref( pixmap );
+ IF context <> nil THEN
+ g_object_unref( context );
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF WIN_GDI}
+{$IFDEF ASM_VERSION}{$ELSE}
+procedure TCanvas.DrawText(Text: AnsiString; var Rect:TRect; Flags:DWord);
+begin
+ RequiredState( HandleValid or FontValid or BrushValid or ChangingCanvas );
+ Windows.DrawTextA(Handle, PAnsiChar(Text), -1, Rect, Flags); // KOL_ANSI
+end;
+{$ENDIF PAS_VERSION}
+
+function TCanvas.ClipRect: TRect;
+begin
+ RequiredState( HandleValid or FontValid or BrushValid or ChangingCanvas );
+ GetClipBox(Handle, Result);
+end;
+{$ENDIF WIN_GDI}
+
+function TCanvas.TextWidth(const Text: KOLString): Integer;
+begin
+ Result := TextExtent(Text).cX;
+end;
+
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TCanvas.GetBrush: PGraphicTool;
+begin
+ if ( fBrush = nil ) then
+ begin
+ fBrush := NewBrush;
+ if ( fOwnerControl <> nil ) then
+ begin
+ fBrush.fData.Color := PControl(fOwnerControl).fColor;
+ if ( PControl(fOwnerControl).fBrush <> nil ) then
+ fBrush.Assign( PControl(fOwnerControl).fBrush );
+ // both statements above needed
+ end;
+ AssignChangeEvents;
+ end;
+ Result := fBrush;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+FUNCTION TCanvas.GetBrush: PGraphicTool;
+BEGIN
+ IF ( fBrush = nil ) THEN
+ BEGIN
+ fBrush := NewBrush;
+ IF ( fOwnerControl <> nil ) THEN
+ BEGIN
+ fBrush.fData.Color := PControl(fOwnerControl).fColor;
+ IF ( PControl(fOwnerControl).fBrush <> nil ) THEN
+ fBrush.Assign( PControl(fOwnerControl).fBrush );
+ // both statements above needed
+ END;
+ AssignChangeEvents;
+ END;
+ Result := fBrush;
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TCanvas.GetFont: PGraphicTool;
+begin
+ if ( fFont = nil ) then
+ begin
+ fFont := NewFont;
+ if ( fOwnerControl <> nil ) then
+ begin
+ fFont.Color := PControl(fOwnerControl).fTextColor;
+ if ( PControl(fOwnerControl).fFont <> nil ) then
+ fFont.Assign( PControl(fOwnerControl).fFont );
+ end;
+ AssignChangeEvents;
+ end;
+ Result := fFont;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TCanvas.GetPen: PGraphicTool;
+begin
+ if ( fPen = nil ) then
+ begin
+ fPen := NewPen;
+ AssignChangeEvents;
+ end;
+ Result := fPen;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TCanvas.GetHandle: HDC;
+begin
+ /////////////////////////////////
+ if Assigned( fOnGetHandle ) then
+ /////////////////////////////////
+ begin
+ Result := fOnGetHandle( @Self );
+ SetHandle( Result );
+ end else
+ Result := fHandle;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+FUNCTION TCanvas.GetHandle: HDC;
+BEGIN
+ ////////////////////////////////
+ IF Assigned( fOnGetHandle ) THEN
+ ////////////////////////////////
+ fHandle := fOnGetHandle( @Self );
+ Result := fHandle;
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TCanvas.AssignChangeEvents;
+begin
+ if ( fBrush <> nil ) then
+ fBrush.fOnGTChange := ObjectChanged;
+ if ( fPen <> nil ) then
+ fPen.fOnGTChange := ObjectChanged;
+ if ( fFont <> nil ) then
+ fFont.fOnGTChange := ObjectChanged;
+end;
+{$ENDIF PAS_VERSION}
+{$IFDEF WIN_GDI}
+
+{$IFNDEF _FPC}
+{$IFNDEF _D2}
+procedure TCanvas.WDrawText(WText: KOLWideString; var Rect: TRect;
+ Flags: DWord);
+begin
+ RequiredState( HandleValid or FontValid or BrushValid or ChangingCanvas );
+ Windows.DrawTextW(Handle,PWideChar(WText),-1,Rect,Flags);
+end;
+
+procedure TCanvas.WExtTextOut(X, Y: Integer; Options: DWORD;
+ const Rect: TRect; const WText: KOLWideString;
+ const Spacing: array of Integer);
+begin
+ RequiredState( HandleValid or FontValid or BrushValid or ChangingCanvas );
+ Windows.ExtTextOutW(FHandle, X, Y, Options, @Rect, PWideChar(WText), Length(WText), @Spacing[ 0 ]);
+end;
+
+procedure TCanvas.WTextOut(X, Y: Integer; const WText: KOLWideString);
+begin
+ RequiredState( HandleValid or FontValid or BrushValid or ChangingCanvas );
+ Windows.TextOutW(FHandle, X, Y, PWideChar(WText), Length(WText));
+ MoveTo(X + WTextWidth(WText), Y);
+end;
+
+procedure TCanvas.WTextRect(const Rect: TRect; X, Y: Integer;
+ const WText: KOLWideString);
+var
+ Options: Integer;
+begin
+ RequiredState( HandleValid or FontValid or BrushValid or ChangingCanvas );
+ Options := ETO_CLIPPED;
+ if ( fBrush <> nil ) and (fBrush.fData.Brush.Style <> bsClear)
+ or ( fBrush = nil ) then Inc(Options, ETO_OPAQUE);
+ Windows.ExtTextOutW( fHandle, X, Y, Options,
+ @Rect, PWideChar(WText),
+ Length(WText), nil);
+end;
+
+function TCanvas.WTextExtent(const WText: KOLWideString): TSize;
+var DC : HDC;
+ ClearHandle : Boolean;
+begin
+ ClearHandle := False;
+ RequiredState( HandleValid or FontValid );
+ DC := fHandle;
+ if DC = 0 then
+ begin
+ DC := CreateCompatibleDC( 0 );
+ ClearHandle := True;
+ SetHandle( DC );
+ end;
+ RequiredState( HandleValid or FontValid );
+ Windows.GetTextExtentPoint32W( fHandle, PWideChar(WText), Length(WText), Result);
+ if ClearHandle then
+ SetHandle( 0 );
+end;
+
+function TCanvas.WTextHeight(const WText: KOLWideString): Integer;
+begin
+ Result := WTextExtent( WText ).cy;
+end;
+
+function TCanvas.WTextWidth(const WText: KOLWideString): Integer;
+begin
+ Result := WTextExtent( WText ).cx;
+end;
+{$ENDIF _D2}
+{$ENDIF _FPC}
+
+{$ENDIF WIN_GDI}
+function MakeInt64( Lo, Hi: DWORD ): I64;
+begin
+ Result.Lo := Lo;
+ Result.Hi := Hi;
+end;
+
+{$IFDEF PAS_ONLY}
+{$ELSE}
+function Int2Int64( X: Integer ): I64;
+asm
+ MOV [EDX], EAX
+ MOV ECX, EDX
+ CDQ
+ MOV [ECX+4], EDX
+end;
+
+procedure IncInt64( var I64: I64; Delta: Integer );
+asm
+ ADD [EAX], EDX
+ ADC dword ptr [EAX+4], 0
+end;
+
+procedure DecInt64( var I64: I64; Delta: Integer );
+asm
+ SUB [EAX], EDX
+ SBB dword ptr [EDX], 0
+end;
+
+function Add64( const X, Y: I64 ): I64;
+asm
+ PUSH ESI
+ XCHG ESI, EAX
+ LODSD
+ ADD EAX, [EDX]
+ MOV [ECX], EAX
+ LODSD
+ ADC EAX, [EDX+4]
+ MOV [ECX+4], EAX
+ POP ESI
+end;
+
+function Sub64( const X, Y: I64 ): I64;
+asm
+ PUSH ESI
+ XCHG ESI, EAX
+ LODSD
+ SUB EAX, [EDX]
+ MOV [ECX], EAX
+ LODSD
+ SBB EAX, [EDX+4]
+ MOV [ECX+4], EAX
+ POP ESI
+end;
+
+function Neg64( const X: I64 ): I64;
+asm
+ MOV ECX, [EAX]
+ NEG ECX
+ MOV [EDX], ECX
+ MOV ECX, 0
+ SBB ECX, [EAX+4]
+ MOV [EDX+4], ECX
+end;
+
+function Mul64EDX( const X: I64; M: Integer ): I64;
+asm
+ PUSH ESI
+ PUSH EDI
+ XCHG ESI, EAX
+ MOV EDI, ECX
+ MOV ECX, EDX
+ LODSD
+ MUL ECX
+ STOSD
+ XCHG EDX, ECX
+ LODSD
+ MUL EDX
+ ADD EAX, ECX
+ STOSD
+ POP EDI
+ POP ESI
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function Mul64i( const X: I64; Mul: Integer ): I64;
+var Minus: Boolean;
+begin
+ Minus := FALSE;
+ if Mul < 0 then
+ begin
+ Minus := TRUE;
+ Mul := -Mul;
+ end;
+ Result := Mul64EDX( X, Mul );
+ if Minus then
+ Result := Neg64( Result );
+end;
+{$ENDIF PAS_VERSION}
+
+function Div64EDX( const X: I64; D: Integer ): I64;
+asm
+ PUSH ESI
+ PUSH EDI
+ XCHG ESI, EAX
+ MOV EDI, ECX
+ MOV ECX, EDX
+ MOV EAX, [ESI+4]
+ CDQ
+ DIV ECX
+ MOV [EDI+4], EAX
+ LODSD
+ DIV ECX
+ STOSD
+ POP EDI
+ POP ESI
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function Div64i( const X: I64; D: Integer ): I64;
+var Minus: Boolean;
+begin
+ Minus := FALSE;
+ if D < 0 then
+ begin
+ D := -D;
+ Minus := TRUE;
+ end;
+ Result := X;
+ if Sgn64( Result ) < 0 then
+ begin
+ Result := Neg64( Result );
+ Minus := not Minus;
+ end;
+ Result := Div64EDX( Result, D );
+ if Minus then
+ Result := Neg64( Result );
+end;
+{$ENDIF PAS_VERSION}
+
+function Mod64i( const X: I64; D: Integer ): Integer;
+begin
+ Result := Sub64( X, Mul64i( Div64i( X, D ), D ) ).Lo;
+end;
+
+function Sgn64( const X: I64 ): Integer;
+asm
+ XOR EDX, EDX
+ CMP [EAX+4], EDX
+ XCHG EAX, EDX
+ JG @@ret_1
+ JL @@ret_neg
+ CMP [EDX], EAX
+ JZ @@exit
+@@ret_1:
+ INC EAX
+ RET
+@@ret_neg:
+ DEC EAX
+@@exit:
+end;
+
+function Cmp64( const X, Y: I64 ): Integer;
+begin
+ Result := Sgn64( Sub64( X, Y ) );
+end;
+
+function Int64_2Str( X: I64 ): AnsiString;
+var M: Boolean;
+ Y: Integer;
+ Buf: array[ 0..31 ] of AnsiChar;
+ I: Integer;
+begin
+ M := FALSE;
+ case Sgn64( X ) of
+ -1: begin M := TRUE; X := Neg64( X ); end;
+ 0: begin Result := '0'; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ I := 31;
+ Buf[ 31 ] := #0;
+ while Sgn64( X ) > 0 do
+ begin
+ Dec( I );
+ Y := Mod64i( X, 10 );
+ Buf[ I ] := AnsiChar( Y + Integer( '0' ) );
+ X := Div64i( X, 10 );
+ end;
+ if M then
+ begin
+ Dec( I );
+ Buf[ I ] := '-';
+ end;
+ Result := PAnsiChar( @Buf[ I ] );
+end;
+
+function Int64_2Hex( X: I64; MinDigits: Integer ): KOLString;
+begin
+ if (MinDigits <= 8) and (X.Hi <> 0) then
+ Result := Int2Hex( X.Hi, 1 ) + Int2Hex( X.Lo, 8 )
+ else if X.Hi <> 0 then
+ Result := Int2Hex( X.Hi, MinDigits - 8 ) + Int2Hex( X.Lo, 8 )
+ else Result := Int2Hex( X.Lo, MinDigits );
+end;
+
+function Str2Int64( const S: AnsiString ): I64;
+var I: Integer;
+ M: Boolean;
+begin
+ Result.Lo := 0;
+ Result.Hi := 0;
+ I := 1;
+ if S = '' then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ M := FALSE;
+ if S[ 1 ] = '-' then
+ begin
+ M := TRUE;
+ Inc( I );
+ end else if S[ 1 ] = '+' then
+ Inc( I );
+ while I <= Length( S ) do
+ begin
+ if (S[ I ] < '0') or (S[ I ] > '9') then
+ break;
+ Result := Mul64i( Result, 10 );
+ IncInt64( Result, Integer( S[ I ] ) - Integer( '0' ) );
+ Inc( I );
+ end;
+ if M then
+ Result := Neg64( Result );
+end;
+
+function Int64_2Double( const X: I64 ): Double;
+asm
+ FILD qword ptr [EAX]
+ FSTP @Result
+end;
+
+function Double2Int64( D: Double ): I64;
+asm
+ FLD D
+ FISTP qword ptr [EAX]
+end;
+{$ENDIF PAS_ONLY}
+
+function IsNan(const AValue: Double): Boolean;
+{$IFDEF _D2orD3}
+type PI64 = ^I64;
+{$ENDIF}
+begin
+ Result := (PI64(@AValue).Hi and $7FF00000 = $7FF00000) and
+ ((PI64(@AValue).Hi and $000FFFFF <> 0) or (PI64(@AValue).Lo <> 0));
+end;
+
+function IsInfinity(const AValue: Double): Boolean;
+{$IFDEF _D2orD3}
+type PI64 = ^I64;
+{$ENDIF}
+begin
+ Result := (PI64(@AValue).Hi and $7FF00000 = $7FF00000) and
+ (PI64(@AValue).Hi and $000FFFFF = $00000000);
+end;
+
+{$IFDEF PAS_ONLY} {$DEFINE PAS_INTPOW} {$ENDIF}
+{$IFDEF F_P} {$DEFINE PAS_INTPOW} {$ENDIF}
+
+function IntPower(Base: Extended; Exponent: Integer): Extended;
+{$IFDEF PAS_ONLY}
+begin
+ Result := 1.0;
+ if Exponent = 0 then exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if Exponent < 0 then begin
+ Exponent := -Exponent;
+ Base := 1.0 / Base;
+ end;
+ REPEAT
+ Result := Result * Base;
+ Dec( Exponent );
+ UNTIL Exponent=0;
+end;
+{$ELSE DELPHI}
+// This version of code by Galkov: Changes in comparison to Delphi standard:
+// no Overflow exception if Exponent is very big negative value
+// (just 0 in result in such case).
+asm
+ fld1 { Result := 1 }
+ test eax,eax // check Exponent for 0, return 0 ** 0 = 1
+ jz @@3 // (though Mathematics says that this is not so...)
+ fld Base
+ jg @@2
+ fdivr ST,ST(1) { Base := 1 / Base }
+ neg eax
+ jmp @@2
+@@1: fmul ST,ST { X := Base * Base }
+@@2: shr eax,1
+ jnc @@1
+ fmul ST(1),ST { Result := Result * X }
+ jnz @@1
+ fstp st { pop X from FPU stack }
+@@3: fwait
+end;
+{$ENDIF PAS_ONLY}
+
+function NextPowerOf2( n: DWORD ): DWORD;
+begin
+ Result := 1;
+ while (Result < n) and (Result <> 0) do
+ Result := Result shl 1;
+end;
+
+function Str2Double( const S: KOLString ): Double;
+var I: Integer;
+ M, Pt: Boolean;
+ D: Double;
+ Ex: Integer;
+begin
+ Result := 0.0;
+ if S = '' then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ M := FALSE;
+ I := 1;
+ if S[ 1 ] = '-' then
+ begin
+ M := TRUE;
+ Inc( I );
+ end;
+ Pt := FALSE;
+ D := 1.0;
+ while I <= Length( S ) do
+ begin
+ case S[ I ] of
+ '.' {$IFNDEF SMALLEST_CODE}, ','{$ENDIF}:
+ if not Pt then Pt := TRUE else break;
+ '0'..'9': if not Pt then
+ Result := Result * 10 + Integer( S[ I ] ) - Integer( '0' )
+ else
+ begin
+ D := D * 0.1;
+ Result := Result + (Integer( S[ I ] ) - Integer( '0' )) * D;
+ end;
+ 'e', 'E': begin
+ Ex := Str2Int( CopyEnd( S, I + 1 ) );
+ Result := Result * IntPower( 10.0, Ex );
+ break;
+ end;
+ end;
+ Inc( I );
+ end;
+ if M then
+ Result := -Result;
+end;
+
+function Str2Extended( const S: KOLString ): Extended;
+var I: Integer;
+ M, Pt: Boolean;
+ D: Extended;
+ Ex: Integer;
+begin
+ Result := 0.0;
+ if S = '' then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ M := FALSE;
+ I := 1;
+ if S[ 1 ] = '-' then
+ begin
+ M := TRUE;
+ Inc( I );
+ end;
+ Pt := FALSE;
+ D := 1.0;
+ while I <= Length( S ) do
+ begin
+ case S[ I ] of
+ '.' {$IFNDEF SMALLEST_CODE}, ','{$ENDIF}:
+ if not Pt then Pt := TRUE else break;
+ '0'..'9': if not Pt then
+ Result := Result * 10 + Integer( S[ I ] ) - Integer( '0' )
+ else
+ begin
+ D := D * 0.1;
+ Result := Result + (Integer( S[ I ] ) - Integer( '0' )) * D;
+ end;
+ 'e', 'E': begin
+ Ex := Str2Int( CopyEnd( S, I + 1 ) );
+ Result := Result * IntPower( 10.0, Ex );
+ break;
+ end;
+ end;
+ Inc( I );
+ end;
+ if M then
+ Result := -Result;
+end;
+
+{$IFNDEF PAS_ONLY}
+function TruncD( D: Double ): Double;
+asm
+ FLD D
+ PUSH ECX
+ FNSTCW [ESP]
+ POP ECX
+ PUSH ECX
+ OR byte ptr [ESP+1], $0C
+ FLDCW [ESP]
+ PUSH ECX
+ FRNDINT
+ FSTP @Result
+ FLDCW [ESP]
+ POP ECX
+ POP ECX
+end;
+{$ENDIF}
+
+function IfThenElseBool( t, e: Boolean; Cond: Boolean ): Boolean;
+begin
+ if cond then Result := t else Result := e;
+end;
+function IfThenElseInt( t, e: Integer; Cond: Boolean ): Integer;
+begin
+ if cond then Result := t else Result := e;
+end;
+function IfThenElseStr( const t, e: AnsiString; Cond: Boolean ): AnsiString;
+begin
+ if cond then Result := t else Result := e;
+end;
+{$IFDEF _D5orHigher}
+function IfThenElse( t, e: Boolean; Cond: Boolean ): Boolean; overload;
+begin
+ if cond then Result := t else Result := e;
+end;
+function IfThenElse( t, e: Integer; Cond: Boolean ): Integer; overload;
+begin
+ if cond then Result := t else Result := e;
+end;
+function IfThenElse( t, e: AnsiString; Cond: Boolean ): AnsiString; overload;
+begin
+ if cond then Result := t else Result := e;
+end;
+function IfThenElse( t, e: Double; Cond: Boolean ): Double; overload;
+begin
+ if cond then Result := t else Result := e;
+end;
+{$ENDIF}
+
+// Precision 15
+function Extended2Str( E: Extended ): KOLString;
+ function UnpackFromBuf( const Buf: array of Byte; N: Integer ): KOLString;
+ var I, J, K, L: Integer;
+ begin
+ SetLength( Result, 16 );
+ J := 1;
+ for I := 7 downto 0 do
+ begin
+ K := Buf[ I ] shr 4;
+ Result[ J ] := KOLChar( Ord('0') + K );
+ Inc( J );
+ K := Buf[ I ] and $F;
+ Result[ J ] := KOLChar( Ord('0') + K );
+ Inc( J );
+ end;
+ //Assert( Result[ 1 ] = '0', 'error!' );
+ Delete( Result, 1, 1 );
+ if N <= 0 then
+ begin
+ while N < 0 do
+ begin
+ Result := '0' + Result;
+ Inc( N );
+ end;
+ Result := '0.' + Result;
+ end else
+ if N < Length( Result ) then
+ begin
+ Result := Copy( Result, 1, N ) + '.' + CopyEnd( Result, N + 1 );
+ end else
+ begin
+ while N > Length( Result ) do
+ begin
+ Result := Result + '0';
+ end;
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ L := Length( Result );
+ while L > 1 do
+ begin
+ if (Result[ L ] <> '0')
+ and (Result[ L ] <> '.') then
+ break;
+ Dec( L );
+ if Result[ L + 1 ] = '.' then break;
+ end;
+ if L < Length( Result ) then Delete( Result, L + 1, MaxInt );
+ end;
+var
+ S: Boolean;
+var F: Extended;
+ N: Integer;
+ Buf1: array[ 0..9 ] of Byte;
+ I10: Integer;
+begin
+ Result := '0';
+ if E = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ S := E < 0;
+ if S then E := -E;
+
+ N := 15;
+ F := 5E12;
+ I10 := 10;
+ while E < F do
+ begin
+ Dec( N );
+ E := E * I10;
+ end;
+ if N = 15 then
+ while E >= 1E13 do
+ begin
+ Inc( N );
+ E := E / I10;
+ end;
+
+ while TRUE do
+ begin
+ {$IFDEF PAS_ONLY}
+ if TRUNC(Abs(E)) >= 10000000 then
+ break;
+ {$ELSE}
+ asm
+ FLD [E]
+ FBSTP [Buf1]
+ end;
+ if Buf1[ 7 ] <> 0 then break;
+ {$ENDIF}
+ E := E * I10;
+ Dec( N );
+ end;
+ Result := UnpackFromBuf( Buf1, N );
+ if S then Result := '-' + Result;
+end;
+
+function Extended2StrDigits( D: Double; n: Integer ): KOLString;
+var i, m: Integer;
+label start;
+begin
+start:
+ Result := Extended2Str( D );
+ i := IndexOfChar( Result, '.' ); //pos( '.', Result );
+ if n <= 0 then
+ begin
+ if i <= 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ delete( Result, i, MaxInt );
+ end else
+ begin
+ if i <= 0 then
+ begin
+ i := Length( Result ) + 1;
+ Result := Result + '.';
+ end;
+ if Length( Result ) - i < n then
+ Result := Result + StrRepeat( '0', n + i - Length( Result ) )
+ else
+ begin
+ m := i + n;
+ if Length( Result ) <= m then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if (Result[m+1] > '5')
+ or (Length( Result ) > m+1)
+ and (Result[m+2] > '0') then
+ begin
+ n := m;
+ inc( Result[n] );
+ while Result[n] > '9' do
+ begin
+ Result[n] := '0';
+ dec( n );
+ if n = 0 then
+ begin
+ Result := '1' + Result;
+ break;
+ end;
+ if Result[n] = '.' then dec(n);
+ inc( Result[n] );
+ end;
+ end;
+ delete( Result, m+1, MaxInt );
+ end;
+ end;
+end;
+
+function Double2Str( D: Double ): KOLString;
+begin
+ Result := Extended2Str( D );
+end;
+
+function Double2StrEx( D: Double ): KOLString;
+var E, E1, E2: Double;
+ S: KOLString;
+begin
+ Result := Double2Str( D );
+ E := Str2Double( Result );
+ E1 := E - D;
+ if E1 < 0.0 then E1 := -E1;
+ if E1 < 1e-307 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ while TRUE do
+ begin
+ E := D - (E - D) * 0.3;
+ S := Double2Str( E );
+ if S = Result then break;
+ E := Str2Double( S );
+ E2 := E - D;
+ if E2 < 0.0 then E2 := -E2;
+ if E2 > E1 * 0.75 then break;
+ Result := S;
+ if E2 < E1 * 0.1 then break;
+ end;
+end;
+
+function GetBits( N: DWORD; first, last: Byte ): DWord;
+{$IFDEF F_P}
+begin
+ Result := 0;
+ if last > 31 then last := 31;
+ if first > last then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := (N and not ($FFFFFFFF shl last)) shr first;
+end;
+{$ELSE DELPHI}
+asm
+ XCHG EAX, EDX // (1) EDX=N, AL=first
+ {$IFDEF PARANOIA} DB $3C, 31 {$ELSE} CMP AL, 31 {$ENDIF} // first(AL) > 31 ?
+ JBE @@1 // (2) åñëè äà, òî Result := 0;
+@@0:
+ XOR EAX, EAX // (2)
+ RET // (1)
+@@1:
+
+ XCHG EAX, ECX // (1) AL = last CL = first
+ SHR EDX, CL // (2) EDX = N shr first
+ SUB AL, CL // (2) AL = last - first
+ JL @@0 // (2) åñëè last < first òî Result := 0;
+
+ {$IFDEF PARANOIA} DB $3C, 32 {$ELSE} CMP AL, 32 {$ENDIF} // (2) last - first >= 32 ?
+ XCHG ECX, EAX // (1) CL = last - first
+ XCHG EAX, EDX // (1) EAX = N shr first
+ JAE @@exit // (2) åñëè last - first > 31, òî Result := EAX;
+ SBB EDX, EDX // (2) EDX = -1
+ DEC EDX // (1) EDX = 1111...10 = -2
+ SHL EDX, CL // (2) EDX = 111...100..0 (ãäå n(0)=last-first+1)
+ NOT EDX // (2) EDX = ìàñêà 000..0111...1 (ãäå n(1)=last-first+1)
+ AND EAX, EDX // (2)
+@@exit:
+ // EAX = ðåçóëüòàò, (1 áàéò íà êîìàíäó RET)
+end;
+{$ENDIF F_P/DELPHI}
+
+function GetBitsL( N: DWORD; from, len: Byte ): DWord;
+{$IFDEF F_P}
+begin
+ Result := GetBits( N, from, from + len - 1 );
+end;
+{$ELSE DELPHI}
+asm
+ ADD CL, DL
+ DEC CL
+ JMP GetBits
+end;
+{$ENDIF F_P/DELPHI}
+
+{$IFNDEF FPC}
+function MulDiv( A, B, C: Integer ): Integer;
+asm
+ IMUL EDX
+ IDIV ECX
+end;
+{$ENDIF}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION}
+function Int2Hex( Value : DWord; Digits : Integer ) : KOLString;
+const
+ HexDigitChr: array[ 0..15 ] of KOLChar = ( '0','1','2','3','4','5','6','7',
+ '8','9','A','B','C','D','E','F' );
+var Buf: array[ 0..8 ] of KOLChar;
+ Dest : PKOLChar;
+begin
+ Dest := @Buf[ 8 ];
+ Dest^ := #0;
+ repeat
+ Dec( Dest );
+ Dest^ := '0';
+ if Value <> 0 then
+ begin
+ Dest^ := HexDigitChr[ Value and $F ];
+ Value := Value shr 4;
+ end;
+ Dec( Digits );
+ until (Value = 0) and (Digits <= 0);
+ Result := Dest;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function Hex2Int( const Value : KOLString) : Integer;
+var I : Integer;
+begin
+ Result := 0;
+ I := 1;
+ if Value = '' then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if Value[ 1 ] = '$' then Inc( I );
+ while I <= Length( Value ) do
+ begin
+ if (Value[ I ] >= '0')
+ and (Value[ I ] <= '9') then
+ Result := (Result shl 4) or (Ord(Value[I]) - Ord('0'))
+ else if (Value[ I ] >= 'A')
+ and (Value[ I ] <= 'F') then
+ Result := (Result shl 4) or (Ord(Value[I]) - Ord('A') + 10)
+ else if (Value[ I ] >= 'a')
+ and (Value[ I ] <= 'f') then
+ Result := (Result shl 4) or (Ord(Value[I]) - Ord('a') + 10)
+ else break;
+ Inc( I );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+function Octal2Int( const Value: AnsiString ) : Integer;
+var I: Integer;
+begin
+ Result := 0;
+ for I := 1 to Length( Value ) do
+ begin
+ if (Value[ I ] >= '0') and (Value[ I ] <= '7') then
+ Result := Result * 8 + Ord( Value[ I ] ) - Ord( '0' )
+ else break;
+ end;
+end;
+
+function Binary2Int( const Value: AnsiString ) : Integer;
+var I: Integer;
+begin
+ Result := 0;
+ for I := 1 to Length( Value ) do
+ begin
+ if (Value[ I ] = '0') or (Value[ I ] = '1') then
+ Result := Result * 2 + Ord( Value[ I ] ) - Ord( '0' )
+ else break;
+ end;
+end;
+
+function ToRadix( number: Radix_Int; radix: Integer; min_digits: Integer ): KOLString;
+var Buf: array[ 0..64 ] of KOLChar;
+ p: PKOLChar;
+ n: Integer;
+ {$IFDEF _D5orHigher}
+ numd: Extended;
+ {$ENDIF}
+begin
+ {$IFDEF KOL_ASSERTIONS}
+ Assert( (radix >= 2) and (radix <= 36), 'Radix base must be between 2 and 36' );
+ Assert( min_digits <= 64, 'Maximum possible digits number is 64' );
+ {$ENDIF KOL_ASSERTIONS}
+ p := @ Buf[ 64 ];
+ p^ := #0;
+ while (number <> 0) do
+ begin
+ dec( p );
+ {$IFDEF _D5orHigher}
+ if number < 0 then
+ begin
+ numd := 1.0 * I64( number ).Hi * $10000 * $10000 + I64( number ).Lo;
+ number := Round( numd / radix );
+ n := Round( numd - 1.0 * number * radix );
+ if n < 0 then
+ begin
+ n := radix + n;
+ dec( number );
+ end;
+ end else
+ {$ENDIF}
+ begin
+ n := number mod radix;
+ number := number div radix;
+ end;
+ if n <= 9 then p^ := KOLChar( n + Ord( '0' ) )
+ else p^ := KOLChar( n - 10 + Ord( 'A' ) );
+ dec( min_digits );
+ end;
+ while (min_digits > 0) do
+ begin
+ dec( p );
+ p^ := '0';
+ dec( min_digits );
+ end;
+ Result := p;
+end;
+
+function FromRadixStr( var Rslt: Radix_int; s: PKOLChar; radix: Integer ): PKOLChar;
+var n: Integer;
+begin
+ {$IFDEF KOL_ASSERTIONS}
+ Assert( (radix >= 2) and (radix <= 36), 'Radix base must be between 2 and 36' );
+ {$ENDIF KOL_ASSERTIONS}
+ Rslt := 0;
+ while s^ <> #0 do
+ begin
+ CASE s^ OF
+ '0'..'9': n := Ord( s^ ) - Ord( '0' );
+ 'a'..'z': n := Ord( s^ ) - Ord( 'a' ) + 10;
+ 'A'..'Z': n := Ord( s^ ) - Ord( 'A' ) + 10;
+ else n := 100;
+ END;
+ if n >= radix then break;
+ Rslt := Rslt * radix + n;
+ inc( s );
+ end;
+ Result := s;
+end;
+
+function FromRadix( const s: AnsiString; radix: Integer ): Radix_int;
+begin
+ Result := 0;
+ if s = '' then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ FromRadixStr( Result, @ s[ 1 ], radix );
+end;
+
+function InsertSeparators( const s: KOLString; chars_between: Integer; Separator: KOLChar ): KOLString;
+var L, from_L, n: Integer;
+begin
+ if (s = '') or (chars_between <= 0) then
+ begin
+ Result := s;
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ From_L := Length( s );
+ L := From_L + From_L div chars_between;
+ SetLength( Result, L );
+ while L >= 1 do
+ begin
+ for n := 1 to chars_between do
+ begin
+ Result[ L ] := s[ from_L ];
+ dec( L );
+ dec( from_L );
+ if L < 1 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Result[ L ] := Separator;
+ dec( L );
+ end;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+function cHex2Int( const Value : KOLString) : Integer;
+begin
+ if (Length(Value)>2) and (Value[1]='0')
+ and ((Value[2]='x') or (Value[2]='X')) then
+ Result := Hex2Int( CopyEnd( Value, 3 ) )
+ else Result := Hex2Int( Value );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function Int2Str( Value : Integer ) : KOLString;
+var Buf : Array[ 0..15 ] of KOLChar;
+ Dst : PKOLChar;
+ Minus : Boolean;
+ D: DWORD;
+begin
+ Dst := @Buf[ 15 ];
+ Dst^ := #0;
+ Minus := False;
+ if Value < 0 then
+ begin
+ Value := -Value;
+ Minus := True;
+ end;
+ D := Value;
+ repeat
+ Dec( Dst );
+ Dst^ := KOLChar( (D mod 10) + Byte( '0' ) );
+ D := D div 10;
+ until D = 0;
+ if Minus then
+ begin
+ Dec( Dst );
+ Dst^ := '-';
+ end;
+ Result := Dst;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure Int2PChar( s: PAnsiChar; Value: Integer );
+var Buf : array[ 0..15 ] of AnsiChar;
+ Dst : PAnsiChar;
+ Minus : Boolean;
+ D: DWORD;
+begin
+ Dst := @Buf[ 15 ];
+ Dst^ := #0;
+ Minus := False;
+ if Value < 0 then
+ begin
+ Value := -Value;
+ Minus := True;
+ end;
+ D := Value;
+ repeat
+ Dec( Dst );
+ Dst^ := AnsiChar( (D mod 10) + Byte( '0' ) );
+ D := D div 10;
+ until D = 0;
+ if Minus then
+ begin
+ Dec( Dst );
+ Dst^ := '-';
+ end;
+ StrCopy( s, Dst );
+end;
+
+function UInt2Str( Value: DWORD ): AnsiString;
+var Buf : Array[ 0..15 ] of AnsiChar;
+ Dst : PAnsiChar;
+ D: DWORD;
+begin
+ Dst := @Buf[ 15 ];
+ Dst^ := #0;
+ D := Value;
+ repeat
+ Dec( Dst );
+ Dst^ := AnsiChar( (D mod 10) + Byte( '0' ) );
+ D := D div 10;
+ until D = 0;
+ Result := Dst;
+end;
+
+function Int2StrEx( Value, MinWidth: Integer ): KOLString;
+begin
+ Result := Int2Str( Value );
+ while Length( Result ) < MinWidth do
+ Result := ' ' + Result;
+end;
+
+function Int2Rome( Value: Integer ): KOLString;
+const RomeDigs = KOLString('IVXLCDMT');
+ function RomeNum( N, FromIdx: Integer ): KOLString;
+ begin
+ CASE N OF
+ 1, 2, 3: Result := StrRepeat( RomeDigs[ FromIdx ], N );
+ 4: Result := KOLString('') + KOLString(RomeDigs[ FromIdx ]) +
+ KOLString(RomeDigs[ FromIdx + 1 ]);
+ 5, 6, 7, 8: Result := KOLString(RomeDigs[ FromIdx + 1 ]) +
+ StrRepeat( RomeDigs[ FromIdx ], N - 5 );
+ 9: Result := KOLString('') + KOLString(RomeDigs[ FromIdx ]) +
+ KOLString(RomeDigs[ FromIdx + 2 ]);
+ else Result := '';
+ END;
+ end;
+var I, J: Integer;
+begin
+ Result := '';
+ if Value < 1 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if Value > 8999 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ // maximum possible is TMMMCMXCIX, i.e. 8999
+ J := 1;
+ for I := 1 to 3 do
+ begin
+ Result := RomeNum( Value mod 10, J ) + Result;
+ Value := Value div 10;
+ if Value = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Inc( J, 2 );
+ end;
+end;
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION}
+function Int2Ths( I : Integer ): KOLString;
+var S : KOLString;
+begin
+ S := Int2Str( I );
+ Result := '';
+ while S <> '' do
+ begin
+ if Result <> '' then
+ Result := KOLString(ThsSeparator) + Result;
+ Result := CopyTail( S, 3 ) + Result;
+ S := Copy( S, 1, Length( S ) - 3 );
+ end;
+ if Copy( Result, 1, 2 ) = KOLString('-') + KOLString(ThsSeparator) then
+ Result := '-' + CopyEnd( Result, 3 );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function Int2Digs( Value, Digits : Integer ) : KOLString;
+var M : KOLString;
+begin
+ Result := Int2Str( Value );
+ M := '';
+ if Value < 0 then
+ begin
+ M := '-';
+ Result := CopyEnd( Result, 2 );
+ end;
+ if Digits >= 0 then
+ while Length( M + Result ) < Digits do
+ Result := '0' + Result
+ else
+ while Length( Result ) < -Digits do
+ Result := '0' + Result;
+ Result := M + Result;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+{$IFDEF _D2009orHigher} const Suffix: AnsiString = 'KMGT';
+{$ELSE} const Suffix = 'KMGT'; {$ENDIF}
+function Num2Bytes( Value : Double ) : KOLString;
+var V, I : Integer;
+begin
+ Result := '';
+ I := 0;
+ while (Value >= 1024) and (I < 4) do
+ begin
+ Inc( I );
+ Value := Value / 1024.0;
+ end;
+ Result := Int2Str( Trunc( Value ) );
+ V := Trunc( (Value - Trunc( Value )) * 100 );
+ if V <> 0 then
+ begin
+ if (V mod 10) = 0 then
+ V := V div 10;
+ Result := Result + ',' + Int2Str( V );
+ end;
+ if I > 0 then
+ Result := Result + KOLString( Suffix[ I ] );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function S2Int( S: PKOLChar ): Integer;
+var M : Integer;
+begin
+ Result := 0;
+ if S = '' then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ M := 1;
+ if S^ = '-' then
+ begin
+ M := -1;
+ Inc( S );
+ end else
+ if S^ = '+' then
+ Inc( S );
+ while (S^>='0') and (S^<='9') do
+ begin
+ Result := Result * 10 + Integer( S^ ) - Integer( '0' );
+ Inc( S );
+ end;
+ if M < 0 then Result := -Result;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function Str2Int(const Value : KOLString) : Integer;
+begin
+ Result := S2Int( PKOLChar( Value ) );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF PAS_ONLY}
+function StrCopy( Dest, Source: PAnsiChar ): PAnsiChar;
+var L: Integer;
+begin
+ L := StrLen(Source);
+ Move(Source^, Dest^, L+1);
+ Result := Dest;
+end;
+{$ELSE}
+function StrCopy( Dest, Source: PAnsiChar ): PAnsiChar; assembler;
+asm
+ {$IFDEF F_P}
+ MOV EAX, [Dest]
+ MOV EDX, [Source]
+ {$ENDIF F_P}
+ PUSH EDI
+ PUSH ESI
+ MOV ESI,EAX
+ MOV EDI,EDX
+ OR ECX, -1
+ XOR AL,AL
+ REPNE SCASB
+ NOT ECX
+ MOV EDI,ESI
+ MOV ESI,EDX
+ MOV EDX,ECX
+ MOV EAX,EDI
+ SHR ECX,2
+ REP MOVSD
+ MOV ECX,EDX
+ AND ECX,3
+ REP MOVSB
+ POP ESI
+ POP EDI
+end {$IFDEF F_P} [ 'EAX', 'EDX', 'ECX' ] {$ENDIF};
+{$ENDIF PAS_ONLY}
+
+function StrCat( Dest, Source: PAnsiChar ): PAnsiChar;
+begin
+ StrCopy( StrScan( Dest, #0 ), Source );
+ Result := Dest;
+end;
+
+{$IFDEF PAS_ONLY}
+function StrScan(Str: PAnsiChar; Chr: AnsiChar): PAnsiChar;
+begin
+ while Str^ <> Chr do
+ begin
+ if Str^ = #0 then break;
+ inc(Str);
+ end;
+ Result := Str;
+end;
+{$ELSE}
+function StrScan(Str: PAnsiChar; Chr: AnsiChar): PAnsiChar; assembler;
+asm
+ {$IFDEF F_P}
+ MOV EAX, [Str]
+ MOVZX EDX, [Chr]
+ {$ENDIF}
+ PUSH EDI
+ PUSH EAX
+ MOV EDI,Str
+ OR ECX, -1
+ XOR AL,AL
+ REPNE SCASB
+ NOT ECX
+ POP EDI
+ XCHG EAX, EDX
+ REPNE SCASB
+
+ XCHG EAX, EDI
+ POP EDI
+
+ JE @@1
+ XOR EAX, EAX
+ RET
+
+@@1: DEC EAX
+end {$IFDEF F_P} [ 'EAX', 'EDX', 'ECX' ] {$ENDIF};
+{$ENDIF PAS_ONLY}
+
+{$IFDEF PAS_ONLY}
+function StrRScan(Str: PAnsiChar; Chr: AnsiChar): PAnsiChar;
+begin
+ Result := nil;
+ while Str^ <> #0 do
+ begin
+ if Str^ = Chr then Result := Str;
+ inc(Str);
+ end;
+ if Result = nil then
+ Result := Str;
+end;
+{$ELSE}
+function StrRScan(Str: PAnsiChar; Chr: AnsiChar): PAnsiChar; assembler;
+asm
+ {$IFDEF F_P}
+ MOV EAX, [Str]
+ MOVZX EDX, [Chr]
+ {$ENDIF F_P}
+ PUSH EDI
+ MOV EDI,Str
+ MOV ECX,0FFFFFFFFH
+ XOR AL,AL
+ REPNE SCASB
+ NOT ECX
+ STD
+ DEC EDI
+ MOV AL,Chr
+ REPNE SCASB
+ MOV EAX,0
+ JNE @@1
+ MOV EAX,EDI
+ INC EAX
+@@1: CLD
+ POP EDI
+end {$IFDEF F_P} [ 'EAX', 'EDX', 'ECX' ] {$ENDIF};
+{$ENDIF PAS_ONLY}
+
+{$IFDEF PAS_ONLY}
+function StrScanLen(Str: PAnsiChar; Chr: AnsiChar; Len: Integer): PAnsiChar;
+begin
+ while (Str^ <> #0) and (Len > 0) do
+ begin
+ if Str^ = Chr then break;
+ inc(Str);
+ dec(Len);
+ end;
+ Result := Str;
+end;
+{$ELSE}
+function StrScanLen(Str: PAnsiChar; Chr: AnsiChar; Len: Integer): PAnsiChar; assembler;
+asm
+ {$IFDEF F_P}
+ MOV EAX, [Str]
+ MOVZX EDX, [Chr]
+ MOV ECX, [Len]
+ {$ENDIF F_P}
+ PUSH EDI
+ XCHG EDI, EAX
+ XCHG EAX, EDX
+ REPNE SCASB
+ XCHG EAX, EDI
+ POP EDI
+ { -> EAX => to next character after found or to the end of Str,
+ ZF = 0 if character found. }
+end {$IFDEF F_P} [ 'EAX', 'EDX', 'ECX' ] {$ENDIF};
+{$ENDIF}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function TrimLeft(const S: KOLString): KOLString;
+var
+ I, L: Integer;
+begin
+ L := Length(S);
+ I := 1;
+ while (I <= L) and (S[I] <= ' ') do Inc(I);
+ Result := Copy(S, I, Maxint);
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function TrimRight(const S: KOLString): KOLString;
+var
+ I: Integer;
+begin
+ I := Length(S);
+ while (I > 0) and (S[I] <= ' ') do Dec(I);
+ Result := Copy(S, 1, I);
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function Trim( const S : KOLString): KOLString;
+begin
+ Result := TrimLeft( TrimRight( S ) );
+end;
+{$ENDIF PAS_VERSION}
+
+function RemoveSpaces( const S: KOLString ): KOLString;
+var I: Integer;
+begin
+ Result := S;
+ for I := Length( S ) downto 1 do
+ if S[ I ] <= ' ' then Delete( Result, I, 1 );
+end;
+
+{$IFDEF PAS_ONLY}
+procedure Str2LowerCase( S: PAnsiChar );
+begin
+ while S^ <> #0 do
+ begin
+ if (S^ >= 'A') and (S^ <= 'Z') then
+ S^ := AnsiChar(Ord(S^)+32);
+ inc(S);
+ end;
+end;
+{$ELSE}
+procedure Str2LowerCase( S: PAnsiChar );
+asm
+ {$IFDEF F_P}
+ MOV EAX, [S]
+ {$ENDIF}
+ XOR ECX, ECX
+@@1:
+ MOV CL, byte ptr [EAX]
+ JECXZ @@exit {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ SUB CL, 'A'
+ CMP CL, 'Z'-'A'
+ JA @@2
+ ADD byte ptr [EAX], 32
+@@2: INC EAX
+ JMP @@1
+@@exit:
+end {$IFDEF F_P} [ 'EAX', 'ECX' ] {$ENDIF};
+{$ENDIF PAS_ONLY}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function LowerCase(const S: Ansistring): Ansistring;
+var I : Integer;
+begin
+ Result := S;
+ for I := 1 to Length( S ) do
+ if (Result[ I ] >= 'A') and (Result[ I ] <= 'Z') then
+ Inc( Result[ I ], 32 );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function UpperCase(const S: Ansistring): Ansistring;
+var I : Integer;
+begin
+ Result := S;
+ for I := 1 to Length( S ) do
+ if (Result[ I ] >= 'a') and (Result[ I ] <= 'z') then
+ Dec( Result[ I ], 32 );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF F_P}
+function DummyStrFun( const S: AnsiString ): AnsiString;
+begin
+ Result := S;
+end;
+{$ENDIF F_P}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function CopyEnd( const S : KOLString; Idx : Integer ) : KOLString;
+begin
+ Result := Copy( S, Idx, MaxInt );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function CopyTail( const S : KOLString; Len : Integer ) : KOLString;
+var L : Integer;
+begin
+ L := Length( S );
+ if L < Len then
+ Len := L;
+ Result := '';
+ if Len = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := Copy( S, L - Len + 1, Len );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+procedure DeleteTail( var S : KOLString; Len : Integer );
+var L : Integer;
+begin
+ L := Length( S );
+ if Len > L then
+ Len := L;
+ Delete( S, L - Len + 1, Len );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFNDEF TEST_INDEXOFCHARS_COMPAT}
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function IndexOfChar( const S : KOLString; Chr : KOLChar ) : Integer;
+var //P, F : PChar;
+ i, l : integer;
+begin
+ Result := -1;
+ if S = '' then exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ l := Length(S);
+ for I := 1 to l do
+ begin
+ if S[I] = Chr then
+ begin
+ Result := I;
+ break;
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+{$ELSE TEST_INDEXOFCHARS_COMPAT}////////////////////////////////////////////////
+function IndexOfChar_Old( const S : AnsiString; Chr : AnsiChar ) : Integer;
+var P, F : PAnsiChar;
+begin
+ P := PAnsiChar( S );
+ F := StrScan( P, Chr );
+ Result := -1;
+ if F = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := Integer( F ) - Integer( P ) + 1;
+end; ///////////////////////////////////////////////////////////////////////////
+function IndexOfChar_New( const S : AnsiString; Chr : AnsiChar ) : Integer;
+var P, F : PAnsiChar;
+begin
+ P := PAnsiChar( S );
+ F := StrScanLen( P, Chr, Length( S ) );
+ Result := -1;
+ if F = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := Integer( F ) - Integer( P );
+ if {(Result > Length(S)) or} (S[ Result ] <> Chr) then
+ Result := -1;
+end; ///////////////////////////////////////////////////////////////////////////
+function Replace0with_( const s: AnsiString ): AnsiString;
+var i: Integer;
+begin
+ Result := s;
+ for i := 1 to Length( s ) do
+ if s[i] = #0 then Result[i] := '_';
+end;
+function IndexOfChar( const S : KOLString; Chr : KOLChar ) : Integer;
+begin
+ Result := IndexOfChar_Old( S, Chr );
+ if Result <> IndexOfChar_New( S, Chr ) then
+ begin
+ LogFileOutput( 'c:\kol\TEST_INDEXOFCHARS_COMPAT.txt',
+ 'S=' + Replace0with_( S ) + #13#10 +
+ 'C=' + Replace0with_( Chr ) + ' Old=' + Int2Str( Result ) +
+ ' New=' + Int2Str( IndexOfChar_New( S, Chr ) ) + #13#10 );
+ end;
+end;
+{$ENDIF}
+
+{$IFDEF _D3orHigher}
+function WIndexOfChar( const S : KOLWideString; Chr : WideChar ) : Integer;
+var i, l : integer;
+begin
+ Result := -1;
+ if S = '' then exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ l := Length(S);
+ for I := 1 to l do
+ begin
+ if S[I] = Chr then
+ begin
+ Result := I;
+ break;
+ end;
+ end;
+end;
+{$ENDIF}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function IndexOfCharsMin( const S, Chars : KOLString ) : Integer;
+var I, J : Integer;
+begin
+ Result := -1;
+ for I := 1 to Length( Chars ) do
+ begin
+ J := IndexOfChar( S, Chars[ I ] );
+ if J > 0 then
+ begin
+ if (Result <= 0) or (J < Result) then
+ Result := J;
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF _D3orHigher}
+function WIndexOfCharsMin( const S, Chars : KOLWideString ) : Integer;
+var I, J : Integer;
+begin
+ Result := -1;
+ for I := 1 to Length( Chars ) do
+ begin
+ J := WIndexOfChar( S, Chars[ I ] );
+ if J > 0 then
+ begin
+ if (Result <= 0) or (J < Result) then
+ Result := J;
+ end;
+ end;
+end;
+{$ENDIF}
+
+{$IFNDEF _FPC}
+{$IFNDEF _D2}
+function IndexOfWideCharsMin( const S, Chars : KOLWideString ) : Integer;
+var I, J : Integer;
+begin
+ Result := -1;
+ for I := 1 to Length( Chars ) do
+ begin
+ J := pos( Chars[ I ], S );
+ if J > 0 then
+ begin
+ if (Result < 0) or (J < Result) then
+ Result := J;
+ end;
+ end;
+end;
+{$ENDIF _D2}
+{$ENDIF _FPC}
+
+ {$DEFINE ASM_LOCAL}
+{$IFDEF PAS_VERSION} {$UNDEF ASM_LOCAL}{$ENDIF}
+{$IFDEF UNICODE_CTRLS}{$UNDEF ASM_LOCAL}{$ENDIF}
+{$IFDEF _D2} {$UNDEF ASM_LOCAL}{$ENDIF}
+{$IFDEF _D3} {$UNDEF ASM_LOCAL}{$ENDIF}
+
+{$IFDEF ASM_LOCAL}
+function IndexOfStr( const S, Sub : KOLString ) : Integer;
+asm
+ PUSH EBX
+ PUSH ESI
+ PUSH EDI
+
+ PUSH EAX
+ MOV EAX, EDX
+ PUSH EDX
+ CALL System.@LStrLen
+ MOV EDI, EAX
+ POP EAX
+ CALL EAX2PChar
+ MOV BL, [EAX]
+ XCHG EAX, [ESP]
+ CALL EAX2PChar
+
+ MOV ESI, EAX
+
+ DEC EAX
+@@1: INC EAX
+ MOV DL, BL
+ MOV ECX, [ESI-4]
+ SUB ECX, EAX
+ ADD ECX, ESI
+
+ CMP ECX, EDI
+ JL @@ret__1
+
+ CALL StrScanLen
+ TEST EAX, EAX
+ JE @@exit__1
+ DEC EAX
+
+ POP EDX
+ PUSH EDX
+
+ MOV ECX, EDI
+ PUSH EAX
+ //CALL StrLComp
+ CALL CompareMem
+ TEST AL, AL
+ POP EAX
+ JZ @@1
+
+ SUB EAX, ESI
+ INC EAX
+ JMP @@exit
+
+@@ret__1:
+ XOR EAX, EAX
+@@exit__1:
+ DEC EAX
+@@exit:
+ POP EDX
+ POP EDI
+ POP ESI
+ POP EBX
+end;
+{$ELSE PAS_VERSION} //Pascal
+function IndexOfStr( const S, Sub : KOLString ) : Integer;
+begin
+ Result := pos( Sub, S );
+ if Result = 0 then Result := -1;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function Parse( var S : KOLString; const Separators : KOLString ) : KOLString;
+var Pos : Integer;
+begin
+ Pos := IndexOfCharsMin( S, Separators );
+ if Pos <= 0 then
+ Pos := Length( S )+1;
+ Result := Copy( S, 1, Pos-1 );
+ Delete( S, 1, Pos );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF _D3orHigher}
+function ParseW( var S : KOLWideString; const Separators : KOLWideString ) : KOLWideString;
+var Pos : Integer;
+begin
+ Pos := WIndexOfCharsMin( S, Separators );
+ if Pos <= 0 then
+ Pos := Length( S )+1;
+ Result := Copy( S, 1, Pos-1 );
+ Delete( S, 1, Pos );
+end;
+{$ENDIF}
+
+{$IFNDEF _FPC}
+{$IFNDEF _D2}
+function WParse( var S : KOLWideString; const Separators : KOLWideString ) : KOLWideString;
+var Pos : Integer;
+begin
+ Pos := IndexOfWideCharsMin( S, Separators );
+ if Pos <= 0 then
+ Pos := Length( S ) + 1;
+ Result := S;
+ S := Copy( Result, Pos + 1, MaxInt );
+ Result := Copy( Result, 1, Pos - 1 );
+end;
+{$ENDIF _D2}
+{$ENDIF _FPC}
+
+function ParsePascalString( var S : KOLString; const Separators : KOLString ) : KOLString;
+var Pos, Idx : Integer;
+ Hex, Spc : Boolean;
+ procedure SkipSpaces;
+ begin
+ if not Spc then
+ while (Length( S ) >= Pos) and (S[ Pos ] = ' ') do
+ Inc( Pos );
+ end;
+var Buf : KOLString;
+ Ou, Val : Integer;
+begin
+ Pos := 1;
+ Spc := IndexOfChar( Separators, ' ' ) >= 0;
+ SkipSpaces;
+ if Length( S ) < Pos then
+ begin
+ Result := S;
+ S := '';
+ exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Buf := PKOLChar( S );
+ Ou := 1;
+ if (S[ Pos ] = '''') or (S[ Pos ] = '#') then
+ begin
+ // skip here string constant expression
+ while Pos <= Length( S ) do
+ begin
+ if S[ Pos ] = '''' then
+ begin
+ Inc( Pos );
+ while Pos <= Length( S ) do
+ begin
+ if S[ Pos ] = '''' then
+ if (Pos = Length( S )) or (S[ Pos+1 ] <> '''') then
+ begin
+ Inc( Pos );
+ break;
+ end else Inc( Pos );
+ Buf[ Ou ] := S[ Pos ];
+ Inc( Ou );
+ Inc( Pos );
+ end;
+ end else
+ if S[ Pos ] = '#' then
+ begin
+ Inc( Pos ); Hex := False; Val := 0;
+ if (Pos < Length( S )) and (S[ Pos ] = '$') then
+ begin
+ Inc( Pos ); Hex := True;
+ end;
+ Dec( Pos );
+ while Pos < Length( S ) do
+ begin
+ Inc( Pos );
+ if (S[ Pos ] >= '0') and (S[ Pos ] <= '9') or
+ Hex and ( (S[ Pos ] >= 'a') and (S[ Pos ] <= 'f') or
+ (S[ Pos ] >= 'A') and (S[ Pos ] <= 'F') ) then
+ begin
+ if Hex then
+ Val := Val * 16
+ else Val := Val * 10;
+ if S[ Pos ] <= '9' then
+ Val := Val + Integer( S[ Pos ] ) - Integer( '0' )
+ else if S[ Pos ] <= 'F' then
+ Val := Val + 10 + Integer( S[ Pos ] ) - Integer( 'A' )
+ else Val := Val + 10 + Integer( S[ Pos ] ) - Integer( 'a' );
+ continue;
+ end;
+ Inc( Pos ); break;
+ end;
+ Buf[ Ou ] := KOLChar( Val );
+ Inc( Ou );
+ end else break;
+ SkipSpaces;
+ if S[ Pos ] <> '+' then break;
+ SkipSpaces;
+ end;
+ end;
+ Idx := IndexOfCharsMin( CopyEnd( S, Pos ), Separators );
+ if Idx <= 0 then
+ begin
+ Result := Copy( Buf, 1, Ou - 1 ) + CopyEnd( S, Pos );
+ S := '';
+ end else
+ begin
+ Result := Copy( Buf, 1, Ou - 1 ) + Copy( S, Pos, Idx - 1 );
+ S := CopyEnd( S, Pos + Idx );
+ end;
+end;
+
+function String2PascalStrExpr( const S : KOLString ) : KOLString;
+var I, Strt : Integer;
+ function String2DoubleQuotas( const S : KOLString ) : KOLString;
+ var I, J : Integer;
+ begin
+ if IndexOfChar( S, '''' ) <= 0 then
+ Result := S
+ else
+ begin
+ J := 0;
+ for I := 1 to Length( S ) do
+ if S[ I ] = '''' then Inc( J );
+ SetLength( Result, Length( S ) + J );
+ J := 1;
+ for I := 1 to Length( S ) do
+ begin
+ Result[ J ] := S[ I ];
+ Inc( J );
+ if S[ I ] = '''' then
+ begin
+ Result[ J ] := '''';
+ Inc( J );
+ end;
+ end;
+ end;
+ end;
+begin
+ Result := '';
+ if S = '' then
+ begin
+ Result := '''''';
+ exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Strt := 1;
+ for I := 1 to Length( S ) + 1 do
+ begin
+ if (I > Length( S )) or (S[ I ] < ' ') or (S[ I ] >= #$7F) then
+ begin
+ if (I > Strt) and (I > 1) then
+ begin
+ if Result <> '' then
+ Result := Result + '+';
+ Result := Result + '''' + String2DoubleQuotas( Copy( S, Strt, I - Strt ) ) + '''';
+ end;
+ if I > Length( S ) then break;
+ if Result <> '' then
+ Result := Result + '+'
+ else Result := Result + '''''+';
+ Result := Result + '#' + Int2Str( Integer( S[ I ] ) );
+ Strt := I + 1;
+ end;
+ end;
+end;
+
+{$IFDEF PAS_ONLY}
+function CompareMem(P1, P2: Pointer; Length: Integer): Boolean;
+var PP1, PP2: PByte;
+begin
+ Result := FALSE;
+ PP1 := P1;
+ PP2 := P2;
+ while (Length > 0) do
+ begin
+ if (PP1^ <> PP2^) then
+ Exit; //>>>>>>>>>>>>>>>>>>>>>>>>
+ inc(PP1);
+ inc(PP2);
+ dec(Length);
+ end;
+ Result := TRUE;
+end;
+{$ELSE}
+function CompareMem(P1, P2: Pointer; Length: Integer): Boolean; assembler;
+asm
+ {$IFDEF F_P}
+ MOV EAX, [P1]
+ MOV EDX, [P2]
+ MOV ECX, [Length]
+ {$ENDIF}
+ PUSH ESI
+ PUSH EDI
+ MOV ESI,P1
+ MOV EDI,P2
+ MOV EDX,ECX
+ XOR EAX,EAX
+ AND EDX,3
+ SHR ECX,1
+ SHR ECX,1
+ REPE CMPSD
+ JNE @@2
+ MOV ECX,EDX
+ REPE CMPSB
+ JNE @@2
+@@1: INC EAX
+@@2: POP EDI
+ POP ESI
+end {$IFDEF F_P} [ 'EAX', 'EDX', 'ECX' ] {$ENDIF};
+{$ENDIF}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function AllocMem( Size : Integer ) : Pointer;
+begin
+ Result := nil;
+ if Size > 0 then
+ begin
+ GetMem( Result, Size );
+ //FillChar( Result^, Size, 0 );
+ ZeroMemory( Result, Size );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure DisposeMem( var Addr : Pointer );
+begin
+ if Addr <> nil then
+ FreeMem( Addr );
+ Addr := nil;
+end;
+
+{$IFDEF WIN}
+function AnsiUpperCase(const S: AnsiString): AnsiString;
+var Len: Integer;
+begin
+ Len := Length(S);
+ SetString(Result, PAnsiChar(S), Len);
+ if Len > 0 then CharUpperBuffA(Pointer(Result), Len);
+end;
+
+function AnsiLowerCase(const S: Ansistring): Ansistring;
+var
+ Len: Integer;
+begin
+ Len := Length(S);
+ SetString(Result, PAnsiChar(S), Len);
+ if Len > 0 then CharLowerBuffA(Pointer(Result), Len);
+end;
+
+function KOLUpperCase(const S: KOLString): KOLString;
+var Len: Integer;
+begin
+ Len := Length(S);
+ SetString(Result, PKOLChar( S ), Len);
+ if Len > 0 then CharUpperBuff(PKOLChar(Result), Len);
+end;
+
+function KOLLowerCase(const S: KOLString): KOLString;
+var
+ Len: Integer;
+begin
+ Len := Length(S);
+ SetString(Result, PKOLChar(S), Len);
+ if Len > 0 then CharLowerBuff(PKOLChar(Result), Len);
+end;
+
+{$IFDEF _D3orHigher}
+function WUpperCase(const S: KOLWideString): KOLWideString;
+var Len: Integer;
+begin
+ Len := Length(S);
+ SetString(Result, PWideChar( S ), Len);
+ if Len > 0 then CharUpperBuffW(PWideChar(Result), Len);
+end;
+
+function WLowerCase(const S: KOLWideString): KOLWideString;
+var
+ Len: Integer;
+begin
+ Len := Length(S);
+ SetString(Result, PWideChar(S), Len);
+ if Len > 0 then CharLowerBuffW(PWideChar(Result), Len);
+end;
+{$ENDIF}
+{$ENDIF WIN}
+
+{$IFNDEF _D2}
+{$IFNDEF _FPC}
+
+{$IFDEF WIN}
+function WAnsiUpperCase(const S: KOLWideString): KOLWideString;
+var Len: Integer;
+begin
+ Result := S;
+ Len := Length(S);
+ if Len > 0 then CharUpperBuffW(Pointer(Result), Len);
+end;
+{$ENDIF WIN}
+
+{$IFDEF WIN}
+function WAnsiLowerCase(const S: KOLWideString): KOLWideString;
+var Len: Integer;
+begin
+ Result := S;
+ Len := Length(S);
+ if Len > 0 then CharLowerBuffW(Pointer(Result), Len);
+end;
+{$ENDIF WIN}
+
+{$IFDEF WIN}
+function WStrComp(const S1, S2: KOLWideString): Integer;
+var i: Integer;
+begin
+ for i := 1 to min( Length( S1 ), Length( S2 ) ) do
+ begin
+ Result := Ord( S1[ i ] ) - Ord( S2[ i ] );
+ if Result <> 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Result := Length( S1 ) - Length( S2 );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE}
+function _WStrComp(S1, S2: PWideChar): Integer;
+var
+ L, R : PWideChar;
+begin
+ L := S1;
+ R := S2;
+ Result := 0;
+ repeat
+ if L^ = R^ then
+ begin
+ if L^ = #0 then exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Inc(L);
+ Inc(R);
+ end else
+ begin
+ Result := (Word(L^) - Word(R^));
+ exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ until (False);
+end;
+{$ENDIF}
+
+{$IFDEF PAS_ONLY}
+function _WStrLComp(S1, S2: PWideChar; Len: Integer): Integer;
+begin
+ while (Len > 0) and (S1^ <> #0) and (S2^ <> #0) do
+ begin
+ Result := Ord(S1^) - Ord(S2^);
+ if Result <> 0 then Exit; // >>>>>>>>>>>>>>>>>>>>
+ dec(Len);
+ end;
+ Result := 0;
+end;
+{$ELSE}
+function _WStrLComp(S1, S2: PWideChar; Len: Integer): Integer;
+asm
+ {$IFDEF F_P}
+ MOV EAX, [S1]
+ MOV EDX, [S2]
+ MOV ECX, [Len]
+ {$ENDIF F_P}
+ PUSH EDI
+ PUSH ESI
+ MOV EDI,EDX
+ XCHG ESI,EAX
+ CMP EAX, EAX
+ REPE CMPSW
+ MOVZX EAX, word ptr [ESI-2]
+ MOVZX EDX, word ptr [EDI-2]
+ SUB EAX,EDX
+ POP ESI
+ POP EDI
+end {$IFDEF F_P} [ 'EAX', 'EDX', 'ECX' ] {$ENDIF};
+{$ENDIF}
+
+function WStrScan(Str: PWideChar; Chr: WideChar): PWideChar;
+begin
+ while (Str^ <> Chr) and (Str^ <> #0) do inc( Str );
+ Result := Str;
+end;
+
+function WStrRScan(const Str: PWideChar; Chr: WideChar): PWideChar;
+begin
+ Result := Str;
+ while Result^ <> #0 do inc( Result );
+ while (DWORD( Result ) >= DWORD( Str )) and
+ (Result^ <> Chr) do dec( Result );
+ if (DWORD( Result ) < DWORD( Str )) then
+ Result := nil;
+end;
+{$ENDIF WIN}
+{$ENDIF _FPC}
+{$ENDIF _D2}
+
+{$IFDEF WIN}
+function AnsiCompareStr(const S1, S2: KOLString): Integer;
+begin
+ Result := CompareString(LOCALE_USER_DEFAULT, 0, PKOLChar(S1), -1, PKOLChar(S2), -1 ) - 2;
+end;
+{$ENDIF WIN}
+
+{$IFDEF WIN}
+function AnsiCompareStrA(const S1, S2: AnsiString): Integer;
+begin
+ Result := CompareStringA(LOCALE_USER_DEFAULT, 0, PAnsiChar(S1), -1, PAnsiChar(S2), -1 ) - 2;
+end;
+{$ENDIF WIN}
+
+{$IFDEF WIN}
+function _AnsiCompareStr(S1, S2: PKOLChar): Integer;
+begin
+ Result := CompareString( LOCALE_USER_DEFAULT, 0, S1, -1,
+ S2, -1) - 2;
+end;
+{$ENDIF WIN}
+
+type
+ TSortAnsiRec = record
+ A: array[ AnsiChar ] of PAnsiChar;
+ end;
+ PSortAnsiRec = ^TSortAnsiRec;
+var SortAnsiOrderNoCase: array[ AnsiChar ] of SmallInt;
+ SortAnsiOrder: array[ AnsiChar ] of SmallInt;
+
+{$IFDEF WIN}
+function _AnsiCompareStrA_Slow(S1, S2: PAnsiChar): Integer;
+begin
+ Result := CompareStringA( LOCALE_USER_DEFAULT, 0, S1, -1,
+ S2, -1) - 2;
+end;
+{$ENDIF WIN}
+
+function CompareAnsiRec( R: PSortAnsiRec; const e1, e2: Integer ): Integer;
+begin
+ Result := _AnsiCompareStrA_Slow(
+ R.A[AnsiChar(e1)],
+ R.A[AnsiChar(e2)]
+ );
+end;
+
+procedure SwapAnsiRec( R: PSortAnsiRec; const e1, e2: Integer );
+{$IFDEF PAS_ONLY}
+var a: PAnsiChar;
+{$ENDIF}
+begin
+ {$IFDEF PAS_ONLY}
+ a := R.A[AnsiChar(e1)];
+ R.A[AnsiChar(e1)] := R.A[AnsiChar(e2)];
+ R.A[AnsiChar(e2)] := a;
+ {$ELSE}
+ Swap( Integer( R.A[AnsiChar(e1)] ),
+ Integer( R.A[AnsiChar(e2)] ) );
+ {$ENDIF}
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+function _AnsiCompareStrA_Fast2(S1, S2: PAnsiChar): Integer;
+begin
+ if S1 = nil then
+ S1 := '';
+ if S2 = nil then
+ S2 := '';
+ Result := 0;
+ while TRUE do
+ begin
+ Result := SortAnsiOrder[ S1^ ] - SortAnsiOrder[ S2^ ];
+ if Result <> 0 then break;
+ if (S1^ = #0) or (S2^ = #0) then break;
+ inc( S1 );
+ inc( S2 );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+function _AnsiCompareStrA_Fast(S1, S2: PAnsiChar): Integer;
+var c: AnsiChar;
+ R: TSortAnsiRec;
+ Buf: array[ 0..511 ] of AnsiChar;
+ P: PAnsiChar;
+begin
+ P := @Buf[0];
+ for c := Low(c) to High(c) do
+ begin
+ P^ := c;
+ R.A[c] := P;
+ inc( P );
+ P^ := #0;
+ inc( P );
+ end;
+ SortData( @R, 256, @CompareAnsiRec, @SwapAnsiRec );
+ for c := Low(c) to High(c) do
+ SortAnsiOrder[AnsiChar(R.A[c][0])] := Ord(c);
+ _AnsiCompareStrA := _AnsiCompareStrA_Fast2;
+ Result := _AnsiCompareStrA_Fast2( S1, S2 );
+end;
+
+{$IFDEF WIN}
+function AnsiCompareStrNoCase(const S1, S2: KOLString): Integer;
+begin
+ Result := CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, PKOLChar(S1), -1,
+ PKOLChar(S2), -1 ) - 2;
+end;
+{$ENDIF WIN}
+
+{$IFDEF WIN}
+function AnsiCompareStrNoCaseA(const S1, S2: AnsiString): Integer;
+begin
+ Result := CompareStringA(LOCALE_USER_DEFAULT, NORM_IGNORECASE, PAnsiChar(S1), -1,
+ PAnsiChar(S2), -1 ) - 2;
+end;
+{$ENDIF WIN}
+
+{$IFDEF WIN}
+function _AnsiCompareStrNoCase(S1, S2: PKOLChar): Integer;
+begin
+ Result := CompareString( LOCALE_USER_DEFAULT, NORM_IGNORECASE, S1, -1,
+ S2, -1) - 2;
+end;
+{$ENDIF WIN}
+
+{$IFDEF WIN}
+function _AnsiCompareStrNoCaseA_Slow(S1, S2: PAnsiChar): Integer;
+begin
+ Result := CompareStringA( LOCALE_USER_DEFAULT, NORM_IGNORECASE, S1, -1,
+ S2, -1) - 2;
+end;
+{$ENDIF WIN}
+
+function CompareAnsiRecNoCase( R: PSortAnsiRec; const e1, e2: Integer ): Integer;
+begin
+ Result := _AnsiCompareStrNoCaseA_Slow(
+ R.A[AnsiChar(e1)] + 1,
+ R.A[AnsiChar(e2)] + 1
+ );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+//{$DEFINE DEBUG_SORTFAST}
+{$IFDEF DEBUG_SORTFAST}
+var DBSF: Integer;
+{$ENDIF}
+function _AnsiCompareStrNoCaseA_Fast2(S1, S2: PAnsiChar): Integer;
+{$IFDEF DEBUG_SORTFAST}
+var S01, S02: PChar;
+{$ENDIF}
+begin
+ if S1 = nil then
+ S1 := '';
+ if S2 = nil then
+ S2 := '';
+ {$IFDEF DEBUG_SORTFAST}
+ S01 := S1;
+ S02 := S2;
+ {$ENDIF}
+ Result := 0;
+ while TRUE do
+ begin
+ Result := SortAnsiOrderNoCase[ S1^ ] - SortAnsiOrderNoCase[ S2^ ];
+ if Result <> 0 then break;
+ if (S1^ = #0) or (S2^ = #0) then break;
+ inc( S1 );
+ inc( S2 );
+ end;
+ {$IFDEF DEBUG_SORTFAST}
+ inc( DBSF );
+ if Result < 0 then
+ LogFileOutput( GetStartDir + 'LT.txt', Int2Str( DBSF ) + ': ' +
+ '"' + S01 + '" < "' + S02 + '"' )
+ else if Result > 0 then
+ LogFileOutput( GetStartDir + 'GT.txt', Int2Str( DBSF ) + ': ' +
+ '"' + S01 + '" > "' + S02 + '"' )
+ else LogFileOutput( GetStartDir + 'EQ.txt', Int2Str( DBSF ) + ': ' +
+ '"' + S01 + '" = "' + S02 + '"' )
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+function _AnsiCompareStrNoCaseA_Fast(S1, S2: PAnsiChar): Integer;
+var c: AnsiChar;
+ R: TSortAnsiRec;
+ Buf: array[ 0..767 ] of AnsiChar;
+ P: PAnsiChar;
+ {$IFDEF PAS_ONLY}
+ a: PAnsiChar;
+ {$ENDIF}
+begin
+ P := @Buf[0];
+ for c := Low(c) to High(c) do
+ begin
+ R.A[c] := P;
+ P^ := c;
+ inc( P );
+ P^ := AnsiLowerCase( c )[1];
+ inc( P );
+ P^ := #0;
+ inc( P );
+ //R.X[c] := Ord(c);
+ end;
+ SortData( @R, 256, @CompareAnsiRecNoCase, @SwapAnsiRec );
+ for c := Succ(Low(c)) to High(c) do
+ begin
+ //R.X[c] := Byte(c);
+ if _AnsiCompareStrNoCaseA_Slow( R.A[Pred(c)] + 1, R.A[c] + 1 ) = 0 then
+ begin
+ if _AnsiCompareStrA( R.A[Pred(c)], R.A[c] ) < 0 then
+ begin
+ {$IFDEF PAS_ONLY}
+ a := R.A[Pred(c)];
+ R.A[Pred(c)] := R.A[c];
+ R.A[c] := a;
+ {$ELSE}
+ Swap( Integer( R.A[Pred(c)] ), Integer( R.A[c] ) );
+ {$ENDIF}
+ end;
+ end;
+ // R.X[c] := R.X[Pred(c)];
+ end;
+ for c := Low(c) to High(c) do
+ SortAnsiOrderNoCase[AnsiChar(R.A[c][0])] := Ord( R.A[c][1] ); // Ord(c); // R.X[c];
+ _AnsiCompareStrNoCaseA := _AnsiCompareStrNoCaseA_Fast2;
+ Result := _AnsiCompareStrNoCaseA_Fast2( S1, S2 );
+end;
+
+function AnsiCompareText( const S1, S2: KOLString ): Integer;
+begin
+ Result := AnsiCompareStrNoCase( S1, S2 );
+end;
+
+function AnsiCompareTextA( const S1, S2: AnsiString ): Integer;
+begin
+ Result := AnsiCompareStrNoCaseA( S1, S2 );
+end;
+
+{$IFDEF PAS_ONLY}
+function StrLCopy(Dest: PAnsiChar; const Source: PAnsiChar; MaxLen: Cardinal): PAnsiChar;
+var Src: PAnsiChar;
+begin
+ Src := Source;
+ while MaxLen > 0 do
+ begin
+ Dest^ := Src^;
+ if Src^ = #0 then break;
+ inc(Dest);
+ inc(Src);
+ dec(MaxLen);
+ end;
+ Result := Dest;
+end;
+{$ELSE}
+function StrLCopy(Dest: PAnsiChar; const Source: PAnsiChar; MaxLen: Cardinal): PAnsiChar; assembler;
+asm
+ {$IFDEF F_P}
+ MOV EAX, [Dest]
+ MOV EDX, [Source]
+ MOV ECX, [MaxLen]
+ {$ENDIF F_P}
+ PUSH EDI
+ PUSH ESI
+ PUSH EBX
+ MOV ESI,EAX
+ MOV EDI,EDX
+ MOV EBX,ECX
+ XOR AL,AL
+ TEST ECX,ECX
+ JZ @@1
+ REPNE SCASB
+ JNE @@1
+ INC ECX
+@@1: SUB EBX,ECX
+ MOV EDI,ESI
+ MOV ESI,EDX
+ MOV EDX,EDI
+ MOV ECX,EBX
+ SHR ECX,2
+ REP MOVSD
+ MOV ECX,EBX
+ AND ECX,3
+ REP MOVSB
+ STOSB
+ MOV EAX,EDX
+ POP EBX
+ POP ESI
+ POP EDI
+end {$IFDEF F_P} [ 'EAX', 'EDX', 'ECX' ] {$ENDIF};
+{$ENDIF}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function StrPCopy(Dest: PAnsiChar; const Source: Ansistring): PAnsiChar;
+begin
+ Result := StrLCopy(Dest, PAnsiChar(Source), Length(Source));
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function StrEq( const S1, S2 : AnsiString ) : Boolean;
+begin
+ Result := (Length( S1 ) = Length( S2 )) and
+ (LowerCase( S1 ) = LowerCase( S2 ));
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function AnsiEq( const S1, S2 : KOLString ) : Boolean;
+begin
+ Result := AnsiCompareStrNoCase( S1, S2 ) = 0;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFNDEF _D2}
+{$IFNDEF _FPC}
+function WAnsiEq( const S1, S2 : KOLWideString ) : Boolean;
+begin
+ Result := WAnsiLowerCase( S1 )=WAnsiLowerCase( S2 );
+end;
+{$ENDIF _FPC}
+{$ENDIF _D2}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function StrIn(const S: AnsiString; const A: array of AnsiString): Boolean;
+var I : Integer;
+begin
+ for I := Low( A ) to High( A ) do
+ if StrEq( S, A[ I ] ) then
+ begin
+ Result := True; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Result := False;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFNDEF _D2}
+{$IFNDEF _FPC}
+function WStrIn( const S : KOLWideString; const A : array of KOLWideString ) : Boolean;
+var I : Integer;
+begin
+ for I := Low( A ) to High( A ) do
+ if WAnsiEq( S, A[ I ] ) then
+ begin
+ Result := True; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Result := False;
+end;
+{$ENDIF _FPC}
+{$ENDIF _D2}
+
+function CharIn( C: KOLChar; const A: TSetofChar ): Boolean;
+begin
+ Result := (DWord( C ) <= 255) and (AnsiChar( C ) in A);
+end;
+
+function StrIs( const S : AnsiString; const A : Array of AnsiString; var Idx: Integer ) : Boolean;
+var I : Integer;
+begin
+ Idx := -1;
+ for I := Low( A ) to High( A ) do
+ if StrEq( S, A[ I ] ) then
+ begin
+ Idx := I;
+ Result := True; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Result := False;
+end;
+
+function IntIn( Value: Integer; const List: array of Integer ): Boolean;
+var I: Integer;
+begin
+ Result := FALSE;
+ for I := 0 to High( List ) do
+ begin
+ if Value = List[ I ] then
+ begin
+ Result := TRUE;
+ break;
+ end;
+ end;
+end;
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function _StrSatisfy( S, Mask : PKOLChar ) : Boolean;
+label next_char;
+begin
+next_char:
+ Result := True;
+ if (S^ = #0) and (Mask^ = #0) then exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if (Mask^ = '*') and (Mask[1] = #0) then exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if S^ = #0 then
+ begin
+ while Mask^ = '*' do
+ Inc( Mask );
+ Result := Mask^ = #0; exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Result := False;
+ if Mask^ = #0 then exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if Mask^ = '?' then
+ begin
+ Inc( S ); Inc( Mask ); goto next_char;
+ end;
+ if Mask^ = '*' then
+ begin
+ Inc( Mask );
+ while S^ <> #0 do
+ begin
+ Result := _StrSatisfy( S, Mask );
+ if Result then exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Inc( S );
+ end;
+ exit; // (Result = False) {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Result := S^ = Mask^;
+ Inc( S ); Inc( Mask );
+ if Result then goto next_char;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function StrSatisfy( const S, Mask: KOLString ): Boolean;
+begin
+ Result := FALSE;
+ if (S = '') or (Mask = '') then Exit;
+ Result := _StrSatisfy( PKOLChar( {$IFDEF UNICODE_CTRLS} WAnsiLowerCase
+ {$ELSE} AnsiLowerCase {$ENDIF} ( S ) ),
+ PKOLChar( {$IFDEF UNICODE_CTRLS} WAnsiLowerCase
+ {$ELSE} AnsiLowerCase {$ENDIF} ( Mask ) ) );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} // Pascal
+function _2StrSatisfy( S, Mask: PKOLChar ): Boolean;
+begin
+ Result := StrSatisfy( S, Mask );
+end;
+{$ENDIF PAS_VERSION}
+
+function StrReplace( var S: KOLString; const From, ReplTo: KOLString ): Boolean;
+var I: Integer;
+begin
+ I := pos( From, S );
+ if I > 0 then
+ begin
+ S := Copy( S, 1, I - 1 ) + ReplTo + CopyEnd( S, I + Length( From ) );
+ Result := TRUE;
+ end else Result := FALSE;
+end;
+
+function KOLStrReplace( var S: KOLString; const From, ReplTo: KOLString ): Boolean;
+var I: Integer;
+begin
+ I := pos( From, S );
+ if I > 0 then
+ begin
+ S := Copy( S, 1, I - 1 ) + ReplTo + CopyEnd( S, I + Length( From ) );
+ Result := TRUE;
+ end else Result := FALSE;
+end;
+
+{$IFDEF _FPC}
+procedure SetLengthW( var W: KOLWideString; NewLength: Integer );
+begin
+ while Length( W ) < NewLength do
+ W := W + ' ' + W;
+ if Length( W ) > NewLength then
+ Delete( W, NewLength + 1, Length( W ) - NewLength );
+end;
+
+function CopyW( const W: KOLWideString; From, Count: Integer ): KOLWideString;
+begin
+ Result := '';
+ if Count <= 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ SetLengthW( Result, Count );
+ Move( W[ From ], Result[ 1 ], Count * Sizeof( WideChar ) );
+end;
+
+function posW( const S1, S2: AnsiString ): Integer; // not used. When use, change AnsiString to WideString ?
+var I, L1: Integer;
+begin
+ L1 := Length( S1 );
+ for I := 1 to Length( S2 )-L1+1 do
+ begin
+ if Copy( S2, I, L1 ) = S1 then
+ begin
+ Result := I; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ Result := 0;
+end;
+{$ENDIF _FPC}
+
+{$IFDEF ASM_VERSION}
+ procedure DoMove(const from; var to_; count: Integer);
+ asm
+ PUSH ESI
+ PUSH EDI
+ XCHG ESI, EAX
+ MOV EDI, EDX
+ REP MOVSB
+ POP EDI
+ POP ESI
+ end;
+{$ENDIF}
+
+{$IFNDEF _FPC}
+{$IFNDEF _D2}
+function WStrReplace( var S: KOLWideString; const From, ReplTo: KOLWideString ): Boolean;
+var I: Integer;
+begin
+ I := pos( From, S );
+ if I > 0 then
+ begin
+ S := Copy( S, 1, I - 1 ) + ReplTo + Copy( S, I + Length( From ), MaxInt );
+ Result := TRUE;
+ end else Result := FALSE;
+end;
+
+function WStrRepeat( const S: KOLWideString; Count: Integer ): KOLWideString;
+var {$IFDEF ASM_VERSION} {$ELSE} I, {$ENDIF} L: Integer;
+begin
+ L := Length( S );
+ SetLength( Result, L * Count );
+ if L = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$IFDEF ASM_VERSION}
+ Move( S[1], Result[1], L * Sizeof(WideChar) );
+ if Count > 1 then
+ DoMove( Result[1], Result[1+L], (Count-1)*L*Sizeof(WideChar) );
+ {$ELSE}
+ for I := 0 to Count-1 do
+ Move( S[ 1 ], Result[ 1 + I * L ], L * Sizeof( WideChar ) );
+ {$ENDIF PAS_VERSION}
+end;
+{$ENDIF _D2}
+{$ENDIF _FPC}
+
+{$IFDEF ASM_VERSION}
+ {$IFDEF UNICODE_CTRLS}
+ function StrRepeat( const S: KOLString; Count: Integer ): KOLString;
+ var L: Integer;
+ begin
+ Result := '';
+ L := Length(S);
+ if L = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ SetLength( Result, Count * Length( S ) );
+ Move( S[1], Result[1], Length(S)*Sizeof(KOLChar) );
+ if Count > 1 then
+ DoMove( Result[1], Result[1+Length(S)],
+ (Length(Result)-Length(S))*Sizeof(KOLChar) );
+ end;
+ {$ELSE notUNICODE}
+function StrRepeat( const S: KOLString; Count: Integer ): KOLString;
+asm
+ PUSH EBX
+ PUSH ESI
+ PUSH EDI
+ MOV EBX, ECX
+ MOV EDI, EDX
+ XCHG ESI, EAX
+
+ MOV EAX, ECX
+ CALL System.@LStrClr
+ TEST ESI, ESI
+ JZ @@exit
+ MOV EDX, [ESI-4]
+ imul edx, EDI
+ PUSH EDX
+ MOV EAX, EBX
+ CALL System.@LStrSetLength
+ PUSH ESI
+ PUSH EDI
+ MOV ECX, [ESI-4]
+ MOV EDI, [EBX]
+ REP MOVSB
+ POP EAX
+ POP ESI
+ DEC EAX
+ POP ECX
+ JLE @@exit
+ SUB ECX, [ESI-4]
+ MOV ESI, [EBX]
+ REP MOVSB
+@@exit:
+ POP EDI
+ POP ESI
+ XCHG EAX, EBX
+ POP EBX
+end;
+{$ENDIF notUNICODE_CTRLS}
+{$ELSE ASM_VERSION}
+function StrRepeat( const S: KOLString; Count: Integer ): KOLString;
+var I, L: Integer;
+begin
+ L := Length( S );
+ SetLength( Result, L * Count );
+ for I := 0 to Count-1 do
+ Move( S[ 1 ], Result[ 1 + I * L * Sizeof(KOLChar) ], L );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_noVERSION}
+{$ELSE PAS_VERSION} //Pascal
+procedure NormalizeUnixText( var S: AnsiString );
+var I, J, N: Integer;
+begin
+ if S <> '' then
+ begin
+ N := 0;
+ if S[ 1 ] = #10 then
+ begin
+ S[ 1 ] := #0;
+ inc( N );
+ end;
+ for I := Length(S) downto 2 do
+ begin
+ if (S[I]=#10) and (S[I-1]<>#13) then
+ S[I] := #0;
+ if S[I] = #0 then inc( N );
+ end;
+ if N > 0 then
+ begin
+ SetLength( S, N+Length(S) );
+ J := Length(S);
+ for I := Length(S)-N downto 1 do
+ begin
+ if S[I] = #0 then
+ begin
+ S[J] := #10;
+ S[J-1] := #13;
+ dec( J );
+ end else S[J] := S[I];
+ dec(J);
+ end;
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+var Koi8_to_Ansi: array[ Char ] of AnsiChar;
+procedure Koi8ToAnsi( s: PAnsiChar );
+var c: AnsiChar;
+begin
+ if Koi8_to_Ansi[ #1 ] = #0 then
+ begin
+ for c := #1 to #255 do
+ begin
+ Koi8_to_Ansi[ c ] := c;
+ if (c >= #$C0) and (c <= #$FF) then
+ Koi8_to_Ansi[ c ] := KOI8_Rus[ c ];
+ end;
+ end;
+ while s^ <> #0 do
+ begin
+ s^ := Koi8_to_Ansi[ s^ ];
+ inc( s );
+ end;
+end;
+
+{$IFDEF PAS_ONLY}
+function StrComp(const Str1, Str2: PAnsiChar): Integer;
+var S1, S2: PAnsiChar;
+begin
+ S1 := Str1;
+ S2 := Str2;
+ while (S1^ <> #0) and (S2^ <> #0) do
+ begin
+ Result := Integer(Ord(S1^)) - Integer(Ord(S2^));
+ if Result <> 0 then Exit;
+ inc(S1);
+ inc(S2);
+ end;
+ Result := 0;
+end;
+{$ELSE}
+function StrComp(const Str1, Str2: PAnsiChar): Integer; assembler;
+asm
+ {$IFDEF F_P}
+ MOV EAX, [Str1]
+ MOV EDX, [Str2]
+ {$ENDIF F_P}
+ PUSH EDI
+ PUSH ESI
+ MOV EDI,EDX
+ XCHG ESI,EAX
+ OR ECX, -1
+ XOR EAX,EAX
+ REPNE SCASB
+ NOT ECX
+ MOV EDI,EDX
+ XOR EDX,EDX
+ REPE CMPSB
+ MOV AL,[ESI-1]
+ MOV DL,[EDI-1]
+ SUB EAX,EDX
+ POP ESI
+ POP EDI
+end {$IFDEF F_P} [ 'EAX', 'EDX', 'ECX' ] {$ENDIF};
+{$ENDIF PAS_ONLY}
+
+var Upper: array[ AnsiChar ] of AnsiChar;
+ Upper_initialized: Boolean;
+
+procedure Init_Upper;
+var c: AnsiChar;
+ s: AnsiString;
+begin
+ if not Upper_initialized then
+ begin
+ for c := Low(c) to High(c) do
+ begin
+ s := c + AnsiChar( ' ' );
+ Upper[c] := AnsiUpperCase( s )[1];
+ end;
+ Upper_initialized := TRUE;
+ end;
+end;
+
+{$IFDEF PAS_ONLY}
+function StrLComp(const Str1, Str2: PAnsiChar; MaxLen: Cardinal): Integer;
+var S1, S2: PAnsiChar;
+ c1, c2: AnsiChar;
+begin
+ S1 := Str1;
+ S2 := Str2;
+ while (S1^ <> #0) and (S2^ <> #0) and (MaxLen > 0) do
+ begin
+ c1 := S1^;
+ c2 := S2^;
+ Result := Integer(c1) - Integer(c2);
+ if Result <> 0 then Exit;
+ inc(S1);
+ inc(S2);
+ dec(MaxLen);
+ end;
+ Result := 0;
+end;
+
+function StrLComp_NoCase(const Str1, Str2: PAnsiChar; MaxLen: Cardinal): Integer;
+var S1, S2: PAnsiChar;
+ c1, c2: AnsiChar;
+begin
+ S1 := Str1;
+ S2 := Str2;
+ while (S1^ <> #0) and (S2^ <> #0) and (MaxLen > 0) do
+ begin
+ c1 := S1^;
+ if (c1 >= 'a') and (c1 <= 'z') then
+ c1 := AnsiChar(Ord(c1)-32);
+ c2 := S2^;
+ if (c2 >= 'a') and (c2 <= 'z') then
+ c2 := AnsiChar(Ord(c2)-32);
+ Result := Integer(c1) - Integer(c2);
+ if Result <> 0 then Exit;
+ inc(S1);
+ inc(S2);
+ dec(MaxLen);
+ end;
+ Result := 0;
+end;
+
+function StrComp_NoCase(const Str1, Str2: PAnsiChar): Integer;
+var S1, S2: PAnsiChar;
+ c1, c2: AnsiChar;
+begin
+ S1 := Str1;
+ S2 := Str2;
+ while (S1^ <> #0) and (S2^ <> #0) do
+ begin
+ c1 := S1^;
+ if (c1 >= 'a') and (c1 <= 'z') then
+ c1 := AnsiChar(Ord(c1)-32);
+ c2 := S2^;
+ if (c2 >= 'a') and (c2 <= 'z') then
+ c2 := AnsiChar(Ord(c2)-32);
+ Result := Integer(c1) - Integer(c2);
+ if Result <> 0 then Exit;
+ inc(S1);
+ inc(S2);
+ end;
+ Result := 0;
+end;
+{$ELSE}
+
+{$IFDEF SMALLER_CODE}
+function StrComp_NoCase(const Str1, Str2: PAnsiChar): Integer;
+asm
+ {$IFDEF F_P}
+ MOV EAX, [Str1]
+ MOV EDX, [Str2]
+ {$ENDIF F_P}
+ PUSH EDI
+ PUSH ESI
+ MOV EDI,EDX
+ XCHG ESI,EAX
+ OR ECX, -1
+ XOR EAX,EAX
+ REPNE SCASB
+
+ NOT ECX
+ MOV EDI,EDX
+ @@0:
+ XOR EDX,EDX
+ REPE CMPSB
+ MOV AL,[ESI-1]
+ MOV AH, AL
+ SUB AH, 'a'
+ CMP AH, 25
+ JA @@1
+ SUB AL, $20
+ @@1:
+ MOV DL,[EDI-1]
+ MOV AH, DL
+ SUB AH, 'a'
+ CMP AH, 25
+ JA @@2
+ SUB DL, $20
+ @@2:
+ MOV AH, 0
+ SUB EAX,EDX
+ JNZ @@exit
+ CMP DL, 0
+ JNZ @@0
+
+ @@exit:
+ POP ESI
+ POP EDI
+end {$IFDEF F_P} [ 'EAX', 'EDX', 'ECX' ] {$ENDIF};
+
+function StrLComp_NoCase(const Str1, Str2: PAnsiChar; MaxLen: Cardinal): Integer;
+asm
+ {$IFDEF F_P}
+ MOV EAX, [Str1]
+ MOV EDX, [Str2]
+ MOV ECX, [MaxLen]
+ {$ENDIF F_P}
+ PUSH EDI
+ PUSH ESI
+ PUSH EBX
+ MOV EDI,EDX
+ MOV ESI,EAX
+ MOV EBX,ECX
+ XOR EAX,EAX
+ OR ECX,ECX
+ JE @@exit
+ REPNE SCASB
+ SUB EBX,ECX
+ MOV ECX,EBX
+ MOV EDI,EDX
+ @@0:
+ XOR EDX,EDX
+ REPE CMPSB
+ MOV AL,[ESI-1]
+ MOV AH, AL
+ SUB AH, 'a'
+ CMP AH, 25
+ JA @@1
+ SUB AL, $20
+ @@1:
+ MOV DL,[EDI-1]
+ MOV AH, DL
+ SUB AH, 'a'
+ CMP AH, 25
+ JA @@2
+ SUB DL, $20
+ @@2:
+ MOV AH, 0
+ SUB EAX,EDX
+ JECXZ @@exit
+ JZ @@0
+
+ @@exit:
+ POP EBX
+ POP ESI
+ POP EDI
+end {$IFDEF F_P} [ 'EAX', 'EDX', 'ECX' ] {$ENDIF};
+{$ELSE not SMALLER_CODE}
+function StrComp_NoCase2(const Str1, Str2: PAnsiChar): Integer;
+asm
+ {$IFDEF F_P}
+ MOV EAX, [Str1]
+ MOV EDX, [Str2]
+ {$ENDIF F_P}
+ PUSH ESI
+ XCHG ESI, EAX
+ @@1:
+ MOVZX EAX, BYTE PTR [EDX]
+ INC EDX
+ MOV CL, BYTE PTR [EAX+Upper]
+ LODSB
+ SUB CL, BYTE PTR [EAX+Upper]
+ JNZ @@fin
+ CMP AL, CL
+ JNZ @@1
+ @@fin:MOVSX EAX, CL
+ NEG EAX
+ POP ESI
+end {$IFDEF F_P} [ 'EAX', 'EDX', 'ECX' ] {$ENDIF};
+
+function StrComp_NoCase1(const Str1, Str2: PAnsiChar): Integer;
+begin
+ Init_Upper;
+ StrComp_NoCase := @StrComp_NoCase2;
+ Result := StrComp_NoCase2( Str1, Str2 );
+end;
+
+function StrLComp_NoCase2(const Str1, Str2: PAnsiChar; MaxLen: Cardinal): Integer;
+asm
+ {$IFDEF F_P}
+ MOV EAX, [Str1]
+ MOV EDX, [Str2]
+ MOV ECX, [MaxLen]
+ {$ENDIF F_P}
+ PUSH EDI
+ PUSH ESI
+ PUSH EBX
+ MOV EDI,EDX
+ XCHG ESI,EAX
+ XOR EBX, EBX
+ JECXZ @@fin
+ @@1: MOVZX EAX, BYTE PTR [EDI]
+ INC EDI
+ MOV BL, BYTE PTR [EAX+Upper]
+ LODSB
+ SUB BL, BYTE PTR [EAX+Upper]
+ JNZ @@fin
+ TEST EAX, EAX
+ JZ @@fin
+ LOOP @@1
+ @@fin:MOVSX EAX, BL
+ POP EBX
+ POP ESI
+ POP EDI
+end {$IFDEF F_P} [ 'EAX', 'EDX', 'ECX' ] {$ENDIF};
+
+function StrLComp_NoCase1(const Str1, Str2: PAnsiChar; MaxLen: Cardinal): Integer;
+begin
+ Init_Upper;
+ StrComp_NoCase := @StrComp_NoCase2;
+ Result := StrLComp_NoCase2( Str1, Str2, MaxLen );
+end;
+{$ENDIF}
+
+function StrLComp(const Str1, Str2: PAnsiChar; MaxLen: Cardinal): Integer; assembler;
+asm
+ {$IFDEF F_P}
+ MOV EAX, [Str1]
+ MOV EDX, [Str2]
+ MOV ECX, [MaxLen]
+ {$ENDIF F_P}
+ PUSH EDI
+ PUSH ESI
+ PUSH EBX
+ MOV EDI,EDX
+ MOV ESI,EAX
+ MOV EBX,ECX
+ XOR EAX,EAX
+ OR ECX,ECX
+ JE @@1
+ REPNE SCASB
+ SUB EBX,ECX
+ MOV ECX,EBX
+ MOV EDI,EDX
+ XOR EDX,EDX
+ REPE CMPSB
+ MOV AL,[ESI-1]
+ MOV DL,[EDI-1]
+ SUB EAX,EDX
+@@1: POP EBX
+ POP ESI
+ POP EDI
+end {$IFDEF F_P} [ 'EAX', 'EDX', 'ECX' ] {$ENDIF};
+{$ENDIF PAS_ONLY}
+
+function StrLen(const Str: PAnsiChar): Cardinal; assembler;
+asm
+ {$IFDEF F_P}
+ MOV EAX, [Str]
+ {$ENDIF F_P}
+ XCHG EAX, EDI
+ XCHG EDX, EAX
+ OR ECX, -1
+ XOR EAX, EAX
+ CMP EAX, EDI
+ JE @@exit0
+ REPNE SCASB
+ DEC EAX
+ DEC EAX
+ SUB EAX,ECX
+@@exit0:
+ MOV EDI,EDX
+end {$IFDEF F_P} [ 'EAX', 'EDX', 'ECX' ] {$ENDIF};
+
+{$IFDEF ASM_UNICODE}
+{$ELSE PAS_VERSION} //Pascal
+function __DelimiterLast( Str, Delimiters: PKOLChar ): PKOLChar;
+var
+ P, F : PKOLChar;
+begin
+ P := Str;
+ Result := P + {$IFDEF UNICODE_CTRLS} WStrLen {$ELSE} StrLen {$ENDIF}( Str );
+ while Delimiters^ <> #0 do
+ begin
+ F := {$IFDEF UNICODE_CTRLS} WStrRScan {$ELSE} StrRScan {$ENDIF}
+ ( P, Delimiters^ );
+ if F <> nil then
+ if (Result^ = #0) or (Integer(F) > Integer(Result)) then
+ Result := F;
+ Inc( Delimiters );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF _D3orHigher}
+function W__DelimiterLast( Str, Delimiters: PWideChar ): PWideChar;
+var
+ P, F : PWideChar;
+begin
+ P := Str;
+ Result := P + WStrLen( Str );
+ while Delimiters^ <> #0 do
+ begin
+ F := WStrRScan( P, Delimiters^ );
+ if F <> nil then
+ if (Result^ = #0) or (Integer(F) > Integer(Result)) then
+ Result := F;
+ Inc( Delimiters );
+ end;
+end;
+{$ENDIF _D3orHigher}
+
+{$IFDEF WIN}
+{$IFNDEF PARAMS_DEFAULT}
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION}
+function SkipSpaces( P: PKOLChar ): PKOLChar;
+begin
+ while (P[0] <> #0) and (P[0] <= ' ') do Inc(P);
+ Result := P;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE}
+function SkipParam(P: PKOLChar): PKOLChar;
+begin
+ P := SkipSpaces( P );
+ while P[0] > ' ' do
+ if P[0] = '"' then
+ begin
+ Inc(P);
+ while (P[0] <> #0) and (P[0] <> '"') do
+ Inc(P);
+ if P[0] <> #0 then Inc(P);
+ end else Inc(P);
+ Result := P;
+end;
+{$ENDIF}
+
+{$UNDEF ASM_LOCAL}
+{$IFDEF _D3orHigher}
+{$IFDEF ASM_UNICODE}
+ {$DEFINE ASM_LOCAL}
+{$ENDIF ASM_UNICODE}
+{$ENDIF _D3orHigher}
+
+{$IFDEF ASM_LOCAL}
+function ParamStr( Idx: Integer ): KOLString;
+asm
+ PUSH EDI
+ MOV EDI, EDX
+ TEST EAX, EAX
+ JNE @@1
+ SUB ESP, 260
+ MOV ECX, ESP
+ PUSH 260
+ PUSH ECX
+ PUSH 0
+ CALL GetModuleFileName
+ XCHG ECX, EAX
+ MOV EDX, ESP
+ MOV EAX, EDI
+ CALL System.@LStrFromPCharLen
+ ADD ESP, 260
+ JMP @@exit
+@@1:
+ PUSH EAX
+ CALL GetCommandLine
+ POP ECX
+ INC ECX
+@@loop: CALL SkipSpaces
+ MOV EDX, EAX
+ CALL SkipParam
+ LOOP @@loop
+ MOV ECX, EAX
+ SUB ECX, EDX
+ CMP ECX, 2
+ JL @@ready
+ CMP byte ptr [EDX], '"'
+ JNE @@ready
+ CMP byte ptr [EAX-1], '"'
+ JNE @@ready
+ INC EDX
+ DEC EAX
+@@ready: SUB EAX, EDX
+ XCHG ECX, EAX
+ XCHG EAX, EDI
+ CALL System.@LStrFromPCharLen
+@@exit: POP EDI
+end;
+{$ELSE PAS_VERSION}
+function ParamStr( Idx: Integer ): KOLString;
+var P, P1: PKOLChar;
+ Buffer: array[ 0..260 ] of KOLChar;
+begin
+ if Idx = 0 then
+ SetString( Result, Buffer, GetModuleFileName( 0, Buffer, Sizeof( Buffer ) ) )
+ else
+ begin
+ P := GetCommandLine;
+ repeat
+ P1 := SkipSpaces( P );
+ P := SkipParam(P1);
+ Dec(Idx);
+ until (Idx < 0); // or (P = P1);
+ if Integer(P-P1) >= 2 then
+ if (P1^ = '"') and ( (P-1)^ = '"') then
+ begin
+ inc( P1 );
+ dec( P );
+ end;
+ SetString( Result, P1, P-P1 );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION}
+function ParamCount: Integer;
+var p: PKOLChar;
+begin
+ p := GetCommandLine;
+ Result := -1;
+ while p^ <> #0 do
+ begin
+ inc( Result );
+ p := SkipParam( p );
+ p := SkipSpaces( p );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF PARAMS_DEFAULT}
+{$ENDIF WIN}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function DelimiterLast( const Str, Delimiters: KOLString ): Integer;
+var PStr: PKOLChar;
+begin
+ PStr := PKOLChar( Str );
+ Result := Integer( __DelimiterLast( PStr, PKOLChar( Delimiters ) ) )
+ - Integer( PStr )
+ + {$IFDEF UNICODE_CTRLS} 2 {$ELSE} 1 {$ENDIF}; // {Viman}
+ {$IFDEF UNICODE_CTRLS} Result := Result div SizeOf( WideChar ) {$ENDIF};
+end;
+{$ENDIF PAS_VERSION}
+
+// Thanks to Marco Bobba - Marisa Bo for this code
+{$IFDEF ASM_UNICODE}{$ELSE}
+function StrIsStartingFrom( Str, Pattern: PKOLChar ): Boolean;
+begin
+ Result := FALSE;
+ if (Str = nil) or (Pattern = nil) then
+ begin
+ Result := (Integer(Str) = Integer(Pattern));
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+
+ while Pattern^ <> #0 do
+ begin
+ if Str^ <> Pattern^ then Exit;
+ inc( Str );
+ inc( Pattern );
+ end;
+ Result := TRUE;
+end;
+{$ENDIF ASM_UNICODE}
+
+{$IFDEF PAS_ONLY}
+function StrIsStartingFromNoCase( Str, Pattern: PAnsiChar ): Boolean;
+begin
+ Result := FALSE;
+ while (Str^ <> #0) and (Pattern^ <> #0) do
+ begin
+ if Str^ <> Pattern^ then Exit;
+ inc(Str^);
+ inc(Pattern^);
+ end;
+ Result := Pattern^ = #0;
+end;
+{$ELSE}
+function StrIsStartingFromNoCase( Str, Pattern: PAnsiChar ): Boolean;
+asm
+ {$IFDEF F_P}
+ MOV EAX, [Str]
+ MOV EDX, [Pattern]
+ {$ENDIF F_P}
+ XOR ECX, ECX
+ @@1:
+ MOV CL, [EDX] // pattern[ i ]
+ INC EDX
+ MOV CH, [EAX] // str[ i ]
+ INC EAX
+ JECXZ @@2 // str = pattern; CL = #0, CH = #0
+ CMP CL, 'a'
+ JB @@cl_ok
+ CMP CL, 'z'
+ JA @@cl_ok
+ SUB CL, 32
+ @@cl_ok:
+ CMP CH, 'a'
+ JB @@ch_ok
+ CMP CH, 'z'
+ JA @@ch_ok
+ SUB CH, 32
+ @@ch_ok:
+ CMP CL, CH
+ JE @@1
+ @@2:
+ TEST CL, CL
+ SETZ AL
+end {$IFDEF F_P} [ 'EAX', 'EDX', 'ECX' ] {$ENDIF};
+{$ENDIF PAS_ONLY}
+
+
+{$IFNDEF _FPC}
+
+{$IFDEF WIN}
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function Format( const fmt: KOLString; params: Array of const ): KOLString;
+var Buffer: array[ 0..1023 ] of KOLChar;
+ ElsArray, El: PDWORD;
+ I : Integer;
+ P : PDWORD;
+begin
+ ElsArray := nil;
+ if High( params ) >= 0 then
+ GetMem( ElsArray, (High( params ) + 1) * sizeof( Pointer ) );
+ El := ElsArray;
+ for I := 0 to High( params ) do
+ begin
+ P := @params[ I ];
+ P := Pointer( P^ );
+ El^ := DWORD( P );
+ Inc( El );
+ end;
+ wvsprintf( PKOLChar(@Buffer[0]), PKOLChar( fmt ), Pointer( ElsArray ) );
+ Result := Buffer;
+ if ElsArray <> nil then
+ FreeMem( ElsArray );
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF WIN}
+
+function LStrFromPWCharLen(Source: PWideChar; Length: Integer): AnsiString;
+var
+ DestLen: Integer;
+ Buffer: array[0..2047] of AnsiChar;
+begin
+ if Length <= 0 then
+ begin
+ Result := ''; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ if Length < SizeOf(Buffer) div 2 then
+ begin
+ DestLen := WideCharToMultiByte(0, 0, Source, Length,
+ Buffer, SizeOf(Buffer), nil, nil);
+ if DestLen > 0 then
+ begin
+ Result := Buffer; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ DestLen := WideCharToMultiByte(0, 0, Source, Length, nil, 0, nil, nil);
+ SetLength( Result, DestLen );
+ WideCharToMultiByte(0, 0, Source, Length, Pointer(Result), DestLen, nil, nil);
+end;
+
+{$IFDEF PAS_ONLY}
+function LStrFromPWChar(Source: PWideChar): AnsiString;
+begin
+ Result := AnsiString(WideString(Source));
+end;
+{$ELSE}
+function LStrFromPWChar(Source: PWideChar): AnsiString;
+{* from Delphi5 - because D2 does not contain it. }
+asm
+ PUSH EDX
+ XOR EDX,EDX
+ TEST EAX,EAX
+ JE @@5
+ PUSH EAX
+@@0: CMP DX,[EAX+0]
+ JE @@4
+ CMP DX,[EAX+2]
+ JE @@3
+ CMP DX,[EAX+4]
+ JE @@2
+ CMP DX,[EAX+6]
+ JE @@1
+ ADD EAX,8
+ JMP @@0
+@@1: ADD EAX,2
+@@2: ADD EAX,2
+@@3: ADD EAX,2
+@@4: XCHG EDX,EAX
+ POP EAX
+ SUB EDX,EAX
+ SHR EDX,1
+@@5: POP ECX
+ JMP LStrFromPWCharLen
+end {$IFDEF F_P} [ 'EAX', 'EDX', 'ECX' ] {$ENDIF};
+{$ENDIF PAS_ONLY}
+
+{$ENDIF not_FPC}
+
+function WCharIn( C: KOLChar; const Chars: array of KOLChar ): Boolean;
+var i: Integer;
+begin
+ Result := TRUE;
+ for i := 0 to High( Chars ) do
+ if Chars[i] = C then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := FALSE;
+end;
+
+/////////////////////////////////////////////////////////////////////////
+//
+//
+// F I L E S
+//
+//
+/////////////////////////////////////////////////////////////////////////
+{
+ This part of the unit modified by Tim Slusher and Vladimir Kladov.
+}
+{* Set of utility methods to work with files
+ and reqistry.
+ When programming KOL, which is Windows API-oriented, You should
+ avoid alien (for Windows) embedded Pascal files handling, and
+ use API-calls which implemented very well. This set of functions
+ is intended to make this easier.
+ Also TDirList object implementation present here and some registry
+ access functions, which allow to make code more elegant.
+}
+
+{$UNDEF ASM_LOCAL}
+{$IFDEF ASM_VERSION}
+ {$DEFINE ASM_LOCAL}
+{$ENDIF PAS_VERSION}
+
+{$IFDEF WIN}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function FileCreate(const FileName: KOLString; OpenFlags: DWord): THandle;
+var Attr: DWORD;
+begin
+ Attr := (OpenFlags shr 16) and $1FFF;
+ if Attr = 0 then Attr := FILE_ATTRIBUTE_NORMAL;
+ Result := CreateFile( PKOLChar(FileName), OpenFlags and $F0000000,
+ OpenFlags and $F, nil, (OpenFlags shr 8) and $F,
+ Attr, 0 );
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF WIN}
+
+{$IFDEF _D3orHigher}
+function WFileCreate(const FileName: KOLWideString; OpenFlags: DWord): THandle;
+var Attr: DWORD;
+begin
+ Attr := (OpenFlags shr 16) and $1FFF;
+ if Attr = 0 then Attr := FILE_ATTRIBUTE_NORMAL;
+ Result := CreateFileW( PWideChar(FileName), OpenFlags and $F0000000,
+ OpenFlags and $F, nil, (OpenFlags shr 8) and $F,
+ Attr, 0 );
+end;
+{$ENDIF _D3orHigher}
+
+{$IFDEF WIN}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function FileClose(Handle: THandle): Boolean;
+begin
+ Result := CloseHandle(Handle);
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF WIN}
+
+{$IFDEF WIN}
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function FileExists( const FileName : KOLString ) : Boolean;
+{$IFDEF FILE_EXISTS_EX}
+var FD: TFindFileData;
+ LFT: TFileTime;
+ Hi, Lo: Word;
+ e: DWORD;
+{$ELSE}
+var Code: Integer;
+{$ENDIF}
+begin
+ {$IFDEF FILE_EXISTS_EX}
+ Result := FALSE;
+ e := SetErrorMode( SEM_NOOPENFILEERRORBOX or SEM_FAILCRITICALERRORS );
+ if Find_First( Filename, FD ) then
+ begin
+ if FD.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY = 0 then
+ begin
+ FileTimeToLocalFileTime( FD.ftLastWriteTime, LFT );
+ if FileTimeToDosDateTime( LFT, Hi, Lo ) then
+ Result := TRUE;
+ end;
+ Find_Close( FD );
+ end;
+ SetErrorMode( e );
+ {$ELSE}
+ Code := GetFileAttributes(PKOLChar(FileName));
+ Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code = 0);
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF WIN}
+
+{$IFDEF _D3orHigher}
+function WFileExists( const FileName: KOLWideString ) : Boolean;
+{$IFDEF notimplemented_FILE_EXISTS_EX}
+var FD: TFindFileData;
+ //F: DWORD;
+ LFT: TFileTime;
+ Hi, Lo: Word;
+{$ELSE}
+var Code: Integer;
+{$ENDIF}
+begin
+ {$IFDEF notimplemented_FILE_EXISTS_EX}
+ Result := FALSE;
+ if not WFind_First( Filename, FD ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if FD.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY <> 0 then Exit; {>>>>>>>>}
+ FileTimeToLocalFileTime( FD.ftLastWriteTime, LFT );
+ if FileTimeToDosDateTime( LFT, Hi, Lo ) then Result := TRUE;
+ WFind_Close( FD );
+ {$ELSE}
+ Code := GetFileAttributesW(PWideChar(FileName));
+ Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code = 0);
+ {$ENDIF}
+end;
+{$ENDIF _D3orHigher}
+
+{$IFDEF WIN}
+{$IFDEF ASM_STREAM}
+function FileSeek(Handle: THandle; {$IFNDEF STREAM_COMPAT} const {$ENDIF} MoveTo: TStrmMove; MoveMethod: TMoveMethod): TStrmSize;
+asm
+ MOVZX ECX, CL
+ PUSH ECX
+ PUSH 0
+ PUSH EDX
+ PUSH EAX
+ CALL SetFilePointer
+end;
+{$ELSE PAS_VERSION} //Pascal
+function FileSeek(Handle: THandle; {$IFNDEF STREAM_COMPAT} const {$ENDIF} MoveTo: TStrmMove; MoveMethod: TMoveMethod): TStrmSize;
+{$IFDEF STREAM_LARGE64}
+var HiPtr: DWORD;
+{$ENDIF}
+begin
+ {$IFDEF STREAM_LARGE64}
+ HiPtr := MoveTo shr 32;
+ Result := SetFilePointer(Handle, DWORD( MoveTo ), @ HiPtr, Ord( MoveMethod ) );
+ if (DWORD( Result ) = $FFFFFFFF {INVALID_SET_FILE_POINTER}) and
+ (GetLastError <> NO_ERROR) then
+ Result := -1; // Int64(-1)
+ if Result >= 0 then
+ Result := Result or (HiPtr shl 32);
+ {$ELSE}
+ Result := SetFilePointer(Handle, MoveTo, nil, Ord( MoveMethod ) );
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF WIN}
+
+{$IFDEF WIN}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function FileRead(Handle: THandle; var Buffer; Count: DWord): DWord;
+begin
+ if not ReadFile(Handle, Buffer, Count, Result, nil) then
+ Result := 0;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF WIN}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function File2Str(Handle: THandle): AnsiString;
+var Pos, Size: DWORD;
+begin
+ Result := '';
+ if Handle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Pos := FileSeek( Handle, 0, spCurrent );
+ Size := GetFileSize( Handle, nil );
+ SetString( Result, nil, Size - Pos + 1 );
+ FileRead( Handle, Result[ 1 ], Size - Pos );
+ Result[ Size - Pos + 1 ] := #0;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFNDEF _D2}
+function File2WStr(Handle: THandle): KOLWideString;
+var Pos, Size: DWORD;
+begin
+ Result := '';
+ if Handle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Pos := FileSeek( Handle, 0, spCurrent );
+ Size := GetFileSize( Handle, nil );
+ SetString( Result, nil, (Size - Pos + 1) div Sizeof( WideChar ) + 1 ); // fixed by zhoudi
+ FileRead( Handle, Result[ 1 ], Size - Pos );
+ Result[ Length(Result) ] := #0; // fixed by zhoudi
+end;
+{$ENDIF _D2}
+
+{$IFDEF WIN}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function FileWrite(Handle: THandle; const Buffer; Count: DWord): DWord;
+begin
+ if not WriteFile(Handle, Buffer, Count, Result, nil) then
+ Result := 0;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF WIN}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function FileEOF( Handle: THandle ) : Boolean;
+var Siz, Pos : DWord;
+begin
+ Siz := GetFileSize( Handle, nil );
+ Pos := FileSeek( Handle, 0, spCurrent );
+ Result := Pos >= Siz;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF WIN}
+{$IFDEF ASM_noVERSION_UNICODE}
+function FileFullPath( const FileName: AnsiString ) : AnsiString;
+const
+ BkSlash: AnsiString = '\';
+ szTShFileInfo = sizeof( TShFileInfo );
+asm
+ PUSH EBX
+ PUSH ESI
+ MOV EBX, EDX
+ PUSH EAX
+
+ XCHG EAX, EDX
+ CALL System.@LStrClr
+
+ POP EDX
+ PUSH 0
+ MOV EAX, ESP
+ CALL System.@LStrAsg
+ MOV ESI, ESP
+
+@@loo: CMP dword ptr [ESI], 0
+ JZ @@fin
+
+ MOV EAX, ESI
+ MOV EDX, [BkSlash]
+ PUSH 0
+ MOV ECX, ESP
+ CALL Parse
+
+ CMP dword ptr [EBX], 0
+ JE @@1
+ MOV EAX, EBX
+ MOV EDX, [BkSlash]
+ CALL System.@LStrCat
+ JMP @@2
+@@1:
+ POP EAX
+ PUSH EAX
+ CALL System.@LStrLen
+ CMP EAX, 2
+ JNE @@2
+ POP EAX
+ PUSH EAX
+ CMP byte ptr [EAX+1], ':'
+ JNE @@2
+
+ MOV EAX, EBX
+ POP EDX
+ PUSH EDX
+ CALL System.@LStrAsg
+ JMP @@3
+@@2:
+ PUSH 0
+ MOV EAX, ESP
+ MOV EDX, [EBX]
+ CALL System.@LStrAsg
+ MOV EAX, ESP
+ MOV EDX, [ESP+4]
+ CALL System.@LStrCat
+ POP EAX
+ PUSH EAX
+ SUB ESP, szTShFileInfo
+ MOV EDX, ESP
+ PUSH SHGFI_DISPLAYNAME
+ PUSH szTShFileInfo
+ PUSH EDX
+ PUSH 0
+ PUSH EAX
+ CALL ShGetFileInfo
+ LEA EDX, [ESP].TShFileInfo.szDisplayName
+ CMP byte ptr [EDX], 0
+ JE @@clr_stk
+ LEA EAX, [ESP+szTShFileInfo+4]
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX
+ {$ENDIF}
+ CALL System.@LStrFromPChar
+@@clr_stk:
+ ADD ESP, szTShFileInfo
+ CALL RemoveStr
+ POP EDX
+ PUSH EDX
+ MOV EAX, EBX
+ CALL System.@LStrCat
+
+@@3: CALL RemoveStr
+ JMP @@loo
+
+@@fin: CALL RemoveStr
+ POP ESI
+ POP EBX
+end;
+{$ELSE PAS_VERSION} //Pascal
+function FileFullPath( const FileName: KOLString ) : KOLString;
+var SFI: TShFileInfo;
+ Src, S: KOLString;
+begin
+ Result := '';
+ Src := FileName;
+ while Src <> '' do
+ begin
+ S := Parse( Src, '\' );
+ if Result <> '' then
+ Result := Result + '\';
+ if (Result = '') and (Length( S ) = 2) and (S[ 2 ] = ':') then
+ Result := S
+ else
+ begin
+ {$IFDEF UNICODE_CTRLS} ShGetFileInfoW {$ELSE} ShGetFileInfoA {$ENDIF}
+ ( PKOLChar( Result + S ), 0, SFI, Sizeof( SFI ), SHGFI_DISPLAYNAME );
+ if SFI.szDisplayName[ 0 ] <> #0 then
+ S := SFI.szDisplayName;
+ Result := Result + S;
+ end;
+ end;
+ if ExtractFileExt( Result ) = '' then
+ // case when flag 'Hide extensions for registered file types' is set on
+ // in the Explorer:
+ Result := Result + ExtractFileExt( FileName );
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF WIN}
+
+{$IFDEF WIN}
+function FileShortPath( const FileName: KOLString ): KOLString;
+var Buf: array[ 0..MAX_PATH ] of KOLChar;
+begin
+ GetShortPathName( PKOLChar( FileName ), Buf, Sizeof( Buf ) );
+ Result := Buf;
+end;
+
+function FileIconSystemIdx( const Path: KOLString ): Integer;
+var SFI: TShFileInfo;
+begin
+ SFI.iIcon := 0; // Bartov
+ {$IFDEF UNICODE_CTRLS} ShGetFileInfoW {$ELSE} ShGetFileInfoA {$ENDIF}
+ ( PKOLChar( Path ), 0, SFI, sizeof( SFI ), SHGFI_SMALLICON or SHGFI_SYSICONINDEX );
+ Result := SFI.iIcon;
+end;
+
+function FileIconSysIdxOffline( const Path: KOLString ): Integer;
+var SFI: TShFileInfo;
+begin
+ SFI.iIcon := 0; // Bartov
+ {$IFDEF UNICODE_CTRLS} ShGetFileInfoW {$ELSE} ShGetFileInfoA {$ENDIF}
+ ( PKOLChar( Path ), FILE_ATTRIBUTE_NORMAL, SFI, sizeof( SFI ),
+ SHGFI_SMALLICON or SHGFI_SYSICONINDEX or SHGFI_USEFILEATTRIBUTES );
+ Result := SFI.iIcon;
+end;
+{$ENDIF WIN}
+
+procedure LogFileOutput( const filepath, str: KOLString );
+var F: THandle;
+ Tmp: KOLString;
+begin
+ F := FileCreate( filepath, ofOpenWrite or ofOpenAlways or ofShareDenyWrite );
+ if F = INVALID_HANDLE_VALUE then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ FileSeek( F, 0, spEnd );
+ Tmp := str + {$IFDEF LIN} #10 {$ELSE} #13#10 {$ENDIF};
+ FileWrite( F, PKOLChar( Tmp )^, Length( Tmp ) * Sizeof(KOLChar) );
+ FileClose( F );
+end;
+
+function StrLoadFromFile( const Filename: KOLString ): AnsiString;
+var F: THandle;
+begin
+ {$IFDEF WIN}
+ if KOLLowerCase(Filename) = 'con' then
+ Result := File2Str(GetStdHandle(STD_INPUT_HANDLE))
+ else
+ {$ENDIF WIN}
+ begin
+ Result := '';
+ F := FileCreate( Filename, ofOpenRead or ofOpenExisting or ofShareDenyWrite );
+ if F = INVALID_HANDLE_VALUE then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := File2Str( F );
+ FileClose( F ); {Dark Knight}
+ end;
+end;
+
+function Str2File( Filename: PKOLChar; Str: PAnsiChar ): Boolean;
+var L: Integer;
+begin
+ L := StrLen( Str );
+ Result := Mem2File( Filename, Str, L ) = L;
+end;
+
+function WStr2File( Filename: PKOLChar; Str: PWideChar ): Boolean;
+var L: Integer;
+begin
+ L := WStrLen( Str );
+ Result := Mem2File( Filename, Str, L * Sizeof(WideChar) ) = L;
+end;
+
+function StrSaveToFile( const Filename: KOLString; const Str: AnsiString ): Boolean;
+begin
+ Result := Mem2File( PKOLChar( Filename ), PAnsiChar( Str ), Length( Str ) )
+ = Length( Str );
+end;
+
+{$IFNDEF _D2}
+function WStrLoadFromFile( const Filename: KOLString ): KOLWideString;
+var F: THandle;
+begin
+ {$IFDEF WIN}
+ //if StrEq( Filename, 'CON' ) then
+ if KOLLowerCase(Filename) = 'con' then
+ Result := File2WStr(GetStdHandle(STD_INPUT_HANDLE))
+ else
+ {$ENDIF WIN}
+ begin
+ Result := '';
+ F := FileCreate( Filename, ofOpenRead or ofOpenExisting or ofShareDenyWrite );
+ if F = INVALID_HANDLE_VALUE then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := File2WStr( F );
+ FileClose( F ); {Dark Knight}
+ end;
+end;
+
+function WStrSaveToFile( const Filename: KOLString; const Str: KOLWideString ): Boolean;
+var BytesToSave: Integer;
+begin
+ BytesToSave := Length( Str ) * Sizeof(WideChar);
+ Result := Mem2File( PKOLChar( Filename ), PWideChar( Str ), BytesToSave )
+ = BytesToSave; // fixed by zhoudi
+end;
+{$ENDIF _D2}
+
+function Mem2File( Filename: PKOLChar; Mem: Pointer; Len: Integer ): Integer;
+var F: THandle;
+begin
+ Result := 0;
+ F := //FileCreate( Filename, ofOpenWrite or ofCreateAlways );
+ CreateFile( Filename, GENERIC_WRITE, 0, nil, CREATE_ALWAYS, 0, 0 );
+ if F = INVALID_HANDLE_VALUE then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := FileWrite( F, Mem^, Len );
+ CloseHandle( F );
+end;
+
+function File2Mem( Filename: PKOLChar; Mem: Pointer; MaxLen: Integer ): Integer;
+var F: THandle;
+begin
+ Result := 0;
+ F := //FileCreate( Filename, ofOpenRead or ofOpenExisting or ofShareDenyWrite );
+ CreateFile( Filename, GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0 );
+ if F = INVALID_HANDLE_VALUE then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := FileRead( F, Mem^, MaxLen );
+ CloseHandle( F );
+end;
+
+{$IFDEF WIN}
+function Find_First( const FilePathName: KOLString; var F: TFindFileData ): Boolean;
+begin
+ {$IFDEF UNICODE_CTRLS}
+ F.FindHandle := THandle( FindFirstFileExW( PKOLChar( FilePathName ),
+ FindExInfoStandard, PWin32FindDataW( @ F ),
+ FindExSearchNameMatch, nil, 0 ) );
+ {$ELSE}
+ F.FindHandle := FindFirstFile( PKOLChar( FilePathName ),
+ PWin32FindData( @ F )^ );
+ {$ENDIF}
+ Result := F.FindHandle <> INVALID_HANDLE_VALUE;
+end;
+function Find_Next( var F: TFindFileData ): Boolean;
+begin
+ Result := FindNextFile( F.FindHandle,
+ {$IFDEF UNICODE_CTRLS} PWin32FindDataW {$ELSE} PWin32FindData {$ENDIF}
+ ( @ F )^ );
+end;
+procedure Find_Close( var F: TFindFileData );
+begin
+ Windows.FindClose( F.FindHandle );
+end;
+{$ENDIF WIN}
+
+{$IFDEF WIN}
+function FileSize( const Path: KOLString ) : {$IFDEF _D2orD3} Integer {$ELSE} Int64 {$ENDIF};
+var FD : TFindFileData;
+begin
+ Result := 0;
+ if not Find_First( Path, FD ) then exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$IFDEF _D2orD3}
+ Result := FD.nFileSizeLow;
+ {$ELSE}
+ I64( Result ).Lo := FD.nFileSizeLow;
+ I64( Result ).Hi := FD.nFileSizeHigh;
+ {$ENDIF}
+ Find_Close( FD );
+end;
+{$ENDIF WIN}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+procedure FileTime( const Path: KOLString;
+ CreateTime, LastAccessTime, LastModifyTime: PFileTime ); stdcall;
+var FD : TFindFileData;
+ procedure CopyTime( Dest, Src: PFileTime );
+ begin
+ if Dest <> nil then
+ Dest^ := Src^;
+ end;
+begin
+ if not Find_First( Path, FD ) then exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Find_Close( FD );
+ CopyTime( CreateTime, @ FD.ftCreationTime );
+ CopyTime( LastModifyTime, @ FD.ftLastWriteTime );
+ CopyTime( LastAccessTime, @ FD.ftLastAccessTime );
+end;
+{$ENDIF PAS_VERSION}
+
+function GetUniqueFilename( PathName: KOLString ) : KOLString;
+var Path, Nam, Ext : KOLString;
+ I, J, K : Integer;
+begin
+ Result := PathName;
+ Path := ExtractFilePath( PathName );
+ if not DirectoryExists( Path ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Nam := ExtractFileNameWOext( PathName );
+ if Nam = '' then
+ begin
+ Path := ExcludeTrailingPathDelimiter( Path );
+ PathName := Path;
+ Result := Path;
+ end;
+ Nam := ExtractFileNameWOext( PathName );
+ Ext := ExtractFileExt( PathName );
+ I := Length( Nam );
+ for J := I downto 1 do
+ if not ((Nam[ J ] >= '0') and (Nam[ J ] <= '9')) then
+ begin
+ I := J;
+ break;
+ end;
+ K := Str2Int( CopyEnd( Nam, I + 1 ) );
+ while FileExists( Result ) do
+ begin
+ Inc( K );
+ Result := Path + Copy( Nam, 1, I ) + Int2Str( K ) + Ext;
+ end;
+end;
+
+{$IFDEF WIN}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function CompareSystemTime(const D1, D2 : TSystemTime) : Integer;
+{$IFDEF DATE0_1601}
+var ft1, ft2: TFileTime;
+{$ELSE}
+var R: Integer;
+ procedure CompareFields(const F1, F2 : Integer);
+ begin
+ if R <> 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if F1 = F2 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if F1 < F2 then
+ R := -1
+ else R := 1;
+ end;
+{$ENDIF DATE0_0001}
+begin
+ {$IFDEF DATE0_1601}
+ SystemTimeToFileTime( D1, ft1 );
+ SystemTimeToFileTime( D2, ft2 );
+{$IFDEF FPC}
+ Result := CompareFileTime( @ft1, @ft2 );
+{$ELSE}
+ Result := CompareFileTime( ft1, ft2 );
+{$ENDIF}
+ {$ELSE}
+ R := 0;
+ CompareFields( D1.wYear, D2.wYear );
+ CompareFields( D1.wMonth, D2.wMonth );
+ CompareFields( D1.wDay, D2.wDay );
+ CompareFields( D1.wHour, D2.wHour );
+ CompareFields( D1.wMinute, D2.wMinute );
+ CompareFields( D1.wSecond, D2.wSecond );
+ CompareFields( D1.wMilliseconds, D2.wMilliseconds );
+ Result := R;
+ {$ENDIF DATE0_0001}
+end;
+{$ENDIF PAS_VERSION}
+
+function FileTimeCompare( const FT1, FT2 : TFileTime ) : Integer;
+begin
+{$IFDEF FPC}
+ Result := CompareFileTime( @FT1, @FT2 );
+{$ELSE}
+ Result := CompareFileTime( FT1, FT2 );
+{$ENDIF}
+end;
+{$ENDIF WIN}
+
+{$IFDEF WIN}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function DirectoryExists(const Name: KOLString): Boolean;
+var
+ Code: Integer;
+ e: DWORD;
+begin
+ e := SetErrorMode( SEM_NOOPENFILEERRORBOX or SEM_FAILCRITICALERRORS );
+ Code := GetFileAttributes(PKOLChar(Name));
+ Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0);
+ SetErrorMode( e );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION}
+function DiskPresent( const DrivePath: KOLString ): Boolean;
+var e: DWORD;
+ restore: Boolean;
+begin
+ e := 0;
+ Restore := FALSE;
+ //if Copy( DrivePath, 1, 2 ) <> '\\' then
+ if (DrivePath <> '') and (DrivePath[1] <> '\') then
+ CASE GetDriveType( PKOLChar( DrivePath ) ) OF
+ DRIVE_REMOVABLE, DRIVE_CDROM, DRIVE_RAMDISK:
+ begin
+ e := SetErrorMode( SEM_NOOPENFILEERRORBOX or SEM_FAILCRITICALERRORS );
+ Restore := TRUE;
+ end;
+ END;
+ Result := DirectoryExists( DrivePath );
+ if Restore then SetErrorMode( e );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF _D3orHigher}
+function WDirectoryExists(const Name: KOLWideString): Boolean;
+var
+ Code: Integer;
+begin
+ Code := GetFileAttributesW(PWideChar(Name));
+ Result := (Code <> -1) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0);
+end;
+{$ENDIF _D3orHigher}
+
+{$ENDIF WIN}
+
+function CheckDirectoryContent( const Name: KOLString; SubDirsOnly: Boolean;
+ const Mask: KOLString ): Boolean;
+var FD: TFindFileData;
+begin
+ Result := TRUE;
+ if DirectoryExists( Name ) then
+ begin
+ if Find_First( IncludeTrailingPathDelimiter( Name ) + Mask, FD ) then
+ begin
+ repeat
+ if not {$IFDEF UNICODE_CTRLS}WStrIn{$ELSE}StrIn{$ENDIF}( FD.cFileName, ['.','..'] ) then
+ begin
+ if SubDirsOnly and LongBool(FD.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY)
+ or not SubDirsOnly then
+ begin
+ Result := FALSE;
+ break;
+ end;
+ end;
+ until not Find_Next( FD );
+ Find_Close( FD );
+ end;
+ end;
+end;
+
+function DirectoryEmpty(const Name: KOLString): Boolean;
+begin
+ Result := CheckDirectoryContent( Name, FALSE, '*.*' );
+end;
+
+function DirectoryHasSubdirs( const Path: KOLString ): Boolean;
+begin
+ Result := not CheckDirectoryContent( Path, TRUE, '*.*' );
+end;
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+ {$IFDEF WIN}
+ {$UNDEF LINUX_USE_HOME_STARTFDIR}
+ {$ENDIF}
+function GetStartDir : KOLString;
+{$IFNDEF LINUX_USE_HOME_STARTFDIR}
+var Buffer:array[0..MAX_PATH] of KOLChar;
+ I : Integer;
+{$ENDIF}
+begin
+ {$IFDEF LINUX_USE_HOME_STARTFDIR}
+ Result := getenv( 'HOME' );
+ {$ELSE}
+ I := GetModuleFileName( 0, Buffer, MAX_PATH );
+ for I := I downto 0 do
+ if Buffer[ I ] = {$IFDEF LIN} '/' {$ELSE} '\' {$ENDIF} then
+ begin
+ Buffer[ I + 1 ] := #0;
+ break;
+ end;
+ Result := Buffer;
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+function ExePath: KOLString;
+var Buffer: array[ 0..MAX_PATH+1 ] of KOLChar;
+begin
+ Buffer[ MAX_PATH+1 ] := #0;
+ GetModuleFileName( 0, Buffer, MAX_PATH+1 );
+ Result := Buffer;
+end;
+
+function ModulePath: KOLString;
+var Buffer: array[ 0..MAX_PATH+1 ] of KOLChar;
+begin
+ Buffer[ MAX_PATH+1 ] := #0;
+ GetModuleFileName( hInstance, Buffer, MAX_PATH+1 );
+ Result := Buffer;
+end;
+
+{$IFNDEF PAS_ONLY}
+function DirectorySize( const Path: KOLString ): I64;
+var DirList: PDirList;
+ I: Integer;
+begin
+ Result := MakeInt64( 0, 0 );
+ DirList := NewDirList( Path, {$IFDEF LIN} '*' {$ELSE} '*.*' {$ENDIF}, 0 );
+ for I := 0 to DirList.Count-1 do
+ begin
+ if LongBool( DirList.Items[ I ].dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY ) then
+ Result := Add64( Result, DirectorySize( DirList.Path + DirList.Names[ I ] ) )
+ else Result := Add64( Result, MakeInt64( DirList.Items[ I ].nFileSizeLow,
+ DirList.Items[ I ].nFileSizeHigh ) );
+ end;
+ DirList.Free;
+end;
+{$ENDIF}
+
+{$IFDEF WIN} //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+function GetFileList(const dir: KOLString): PKOLStrList;
+var
+ Srch: TFindFileData;
+ succ: Boolean;
+begin
+ result := nil;
+ succ := Find_First(dir, Srch);
+ while succ do begin
+ if (not (Srch.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY > 0))then begin
+ if Result = nil then begin
+ Result := NewKOLStrList;
+ end;
+ Result.Add(Srch.cFileName);
+ end;
+ succ := Find_Next(Srch);
+ end;
+ Find_Close(Srch);
+end;
+
+{$ENDIF WIN}
+function ExcludeTrailingChar( const S: KOLString; C: KOLChar ): KOLString;
+begin
+ Result := S;
+ if Result <> '' then
+ if Result[ Length( Result ) ] = C then
+ Delete( Result, Length( Result ), 1 );
+end;
+
+{$IFDEF ASM_UNICODE}{$ELSE PASCAL}
+function IncludeTrailingChar( const S: KOLString; C: KOLChar ): KOLString;
+begin
+ Result := S;
+ if (Result = '') or (Result[ Length( Result ) ] <> C) then
+ Result := Result + KOLString(C);
+end;
+{$ENDIF PAS_VERSION}
+
+
+//---------------------------------------------------------
+// Following functions/procedures are created by Edward Aretino:
+// IncludeTrailingPathDelimiter, ExcludeTrailingPathDelimiter,
+// ForceDirectories, CreateDir, ChangeFileExt
+//---------------------------------------------------------
+function IncludeTrailingPathDelimiter(const S: KOLString): KOLString;
+begin
+ Result := IncludeTrailingChar( S, {$IFDEF UNIX} '/' {$ELSE} '\' {$ENDIF} );
+end;
+
+function ExcludeTrailingPathDelimiter(const S: KOLString): KOLString;
+begin
+ Result := ExcludeTrailingChar( S, {$IFDEF UNIX} '/' {$ELSE} '\' {$ENDIF} );
+end;
+
+function ExtractFileDrive( const Path: KOLString ) : KOLString;
+var i, j: Integer;
+begin
+ Result := Path;
+ if Result = '' then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if pos( KOLString(':'), Result ) > 1 then
+ Result := Parse( Result, ':' ) + ':\'
+ else if Length( Result ) > 2 then
+ begin
+ j := 0;
+ for i := 3 to Length( Result ) do
+ if Result[ i ] = '\' then
+ begin
+ inc( j );
+ if j = 2 then
+ begin
+ Result := Copy( Result, 1, i );
+ break;
+ end;
+ end;
+ Result := IncludeTrailingPathDelimiter( Result );
+ end else if Length( Result ) = 1 then
+ Result := Result + ':\';
+end;
+
+{$IFDEF ASM_LStrFromPCharLen} // LStrFromPCharLen - there are no in D2
+function ExtractFilePath( const Path : AnsiString ) : AnsiString;
+asm
+ PUSH EDX
+ MOV EDX, [DirDelimiters]
+ CALL EAX2PChar
+ PUSH EAX
+ CALL __DelimiterLast
+ XCHG EDX, EAX
+ XOR ECX, ECX // ECX = 0
+ POP EAX
+ CMP byte ptr [EDX], CL
+ JZ @@ret_0
+ SUB EDX, EAX
+ INC EDX
+ XCHG EDX, EAX
+ XCHG ECX, EAX // EAX = 0
+@@ret_0:
+ POP EAX
+ {$IFDEF _D2009orHigher}
+ PUSH 0
+ {$ENDIF}
+ CALL System.@LStrFromPCharLen
+end;
+{$ELSE} //Pascal
+function ExtractFilePath( const Path : KOLString ) : KOLString;
+//var I : Integer;
+var P, P0: PKOLChar;
+begin
+ P0 := PKOLChar( Path );
+ P := __DelimiterLast( P0, ':\/' );
+ if P^ = #0 then
+ Result := ''
+ else Result := Copy( Path, 1, P - P0 + 1 );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF _D3orHigher}
+function WExtractFilePath( const Path: KOLWideString ) : KOLWideString;
+var P, P0: PWideChar;
+begin
+ P0 := PWideChar( Path );
+ P := W__DelimiterLast( P0, ':\/' );
+ if P^ = #0 then
+ Result := ''
+ else Result := Copy( Path, 1, P - P0 + 1 );
+end;
+{$ENDIF}
+
+{$IFDEF ASM_VERSION}{$IFNDEF _D2}
+{$DEFINE ASM_LStrFromPCharLen}
+{$ENDIF}
+{$ENDIF PAS_VERSION}
+
+function IsNetworkPath( const Path: KOLString ): Boolean;
+begin
+ Result := (Length( Path ) >= 2) and (Path[1] = '\') and (Path[2] = '\');
+end;
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function ExtractFileName( const Path : KOLString ) : KOLString;
+var P: PKOLChar;
+begin
+ P := __DelimiterLast( PKOLChar( Path ), ':\/' );
+ if P^ = #0 then
+ Result := Path
+ else Result := P + 1;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PASCAL}
+function ExtractFileNameWOext( const Path : KOLString ) : KOLString;
+begin
+ Result := ExtractFileName( Path );
+ Result := Copy( Result, 1, Length( Result ) - Length( ExtractFileExt( Result ) ) );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function ExtractFileExt( const Path : KOLString ) : KOLString;
+var P: PKOLChar;
+begin
+ P := __DelimiterLast( PKOLChar( Path ), '.' );
+ Result := P;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PASCAL}
+function ReplaceExt( const Path, NewExt: KOLString ): KOLString;
+begin
+ Result := ExtractFilePath( Path ) + ExtractFileNameWOext( Path ) +
+ NewExt;
+end;
+{$ENDIF}
+
+function ForceDirectories(Dir: KOLString): Boolean;
+begin
+ Result := Length(Dir) > 0; {Centronix}
+ If not Result then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Dir := ExcludeTrailingPathDelimiter(Dir);
+ If (Length(Dir) < 3) or DirectoryExists(Dir) or
+ (ExtractFilePath(Dir) = Dir) then Exit; // avoid 'xyz:\' problem. {>>>>>>>>>}
+ Result := ForceDirectories(ExtractFilePath(Dir)) and CreateDir(Dir);
+end;
+
+function CreateDir(const Dir: KOLString): Boolean;
+begin
+ Result := {$IFDEF WIN} {Windows.}CreateDirectory(PKOLChar(Dir), nil)
+ {$ELSE LIN} Libc.__mkdir(PAnsiChar(Dir), S_IRWXU or S_IRWXG or S_IRWXO) = 0
+ {$ENDIF};
+end;
+
+function ChangeFileExt(FileName: KOLString; const Extension: KOLString): KOLString;
+var
+ FileExt: KOLString;
+begin
+ FileExt := ExtractFileExt(FileName);
+ DeleteTail(FileName, Length(FileExt));
+ Result := FileName+ Extension;
+end;
+
+function ReplaceFileExt( const Path, NewExt: KOLString ): KOLString;
+begin
+ Result := ExtractFilePath( Path ) +
+ ExtractFileNameWOext( ExtractFileName( Path ) ) +
+ NewExt;
+end;
+
+{$IFDEF WIN} //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+function ExtractShortPathName( const Path: KOLString ): KOLString;
+var
+ Buffer: array[0..MAX_PATH - 1] of KOLChar;
+begin
+ SetString(Result, Buffer,
+ GetShortPathName(PKOLChar(Path), Buffer, SizeOf(Buffer) div Sizeof(KOLChar)));
+end;
+
+{$IFDEF GDI}
+function FilePathShortened( const Path: KOLString; MaxLen: Integer ): KOLString;
+begin
+ Result := FilePathShortenPixels( Path, 0, MaxLen );
+end;
+
+function PixelsLength( DC: HDC; const Text: KOLString ): Integer;
+var Sz: TSize;
+begin
+ if DC = 0 then
+ Result := Length( Text )
+ else
+ begin
+ {$IFDEF UNICODE_CTRLS}Windows.GetTextExtentPoint32W
+ {$ELSE} Windows.GetTextExtentPoint32A
+ {$ENDIF}( DC, PKOLChar( Text ), Length( Text ), Sz );
+ Result := Sz.cx;
+ end;
+end;
+
+function FilePathShortenPixels( const Path: KOLString; DC: HDC; MaxPixels: Integer ): KOLString;
+var L0, L1: Integer;
+ Prev: KOLString;
+begin
+ Result := Path;
+ L0 := PixelsLength( DC, Result );
+ while L0 > MaxPixels do
+ begin
+ Prev := Result;
+ L1 := pos( KOLString('\...\'), Result ); // ambiguous
+ if L1 <= 0 then
+ Result := ExcludeTrailingPathDelimiter( ExtractFilePath( Result ) )
+ else Result := Copy( Result, 1, L1 - 1 );
+ if Result <> '' then
+ Result := IncludeTrailingPathDelimiter( ExtractFilePath( Result ) ) +
+ '...\' + ExtractFileName( Path );
+ if (Result = '') or (Result = Prev) then
+ begin
+ L1 := Length( ExtractFilePath( Result ) );
+ while (PixelsLength( DC, Result ) > MaxPixels) and (L1 > 1) do
+ begin
+ Dec( L1 );
+ Result := Copy( Result, 1, L1 ) + '...\' + ExtractFileName( Result );
+ end;
+ if PixelsLength( DC, Result ) > MaxPixels then
+ begin
+ L1 := MaxPixels + 1;
+ while ((MaxPixels > 0) and (L1 > 1) or (MaxPixels = 0) and (L1 > 0)) and
+ (PixelsLength( DC, Result ) > MaxPixels) do
+ begin
+ Dec( L1 );
+ Result := Copy( ExtractFileName( Path ), 1, L1 ) + '...';
+ end;
+ end;
+ break;
+ end;
+ L0 := PixelsLength( DC, Result );
+ end;
+end;
+{$ENDIF GDI}
+
+procedure CutFirstDirectory(var S: KOLString);
+var
+ Root: Boolean;
+ P: Integer;
+begin
+ if S = '\' then
+ S := ''
+ else
+ begin
+ if S[1] = '\' then
+ begin
+ Root := True;
+ Delete(S, 1, 1);
+ end else
+ Root := False;
+ if S[1] = '.' then
+ Delete(S, 1, 4);
+ P := Pos( KOLString('\'), S );
+ if P <> 0 then
+ begin
+ Delete(S, 1, P);
+ S := '...\' + S;
+ end else
+ S := '';
+ if Root then
+ S := '\' + S;
+ end;
+end;
+
+{$IFDEF GDI}
+function MinimizeName( const Path: KOLString; DC: HDC; MaxPixels: Integer ): KOLString;
+var
+ Drive, Dir, Name: KOLString;
+begin
+ Result := Path;
+ Dir := ExtractFilePath(Result);
+ Name := ExtractFileName(Result);
+
+ if (Length(Dir) >= 2) and (Dir[2] = ':') then
+ begin
+ Drive := Copy(Dir, 1, 2);
+ Delete(Dir, 1, 2);
+ end else Drive := '';
+ while ((Dir <> '') or (Drive <> '')) and (PixelsLength(DC, Result) > MaxPixels) do
+ begin
+ if Dir = '\...\' then
+ begin
+ Drive := '';
+ Dir := '...\';
+ end else if Dir = '' then
+ Drive := ''
+ else CutFirstDirectory(Dir);
+ Result := Drive + Dir + Name;
+ end;
+end;
+{$ENDIF GDI}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION}
+function GetSystemDir: KOLString;
+var Buf: array[ 0..MAX_PATH-1 ] of KOLChar;
+begin
+ GetSystemDirectory( @ Buf[ 0 ], MAX_PATH );
+ Result := IncludeTrailingPathDelimiter( PKOLChar( @ Buf[ 0 ] ) );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION}
+function GetWindowsDir : KOLString;
+var Buf : array[ 0..MAX_PATH-1 ] of KOLChar;
+begin
+ GetWindowsDirectory( @Buf[ 0 ], MAX_PATH );
+ Result := IncludeTrailingPathDelimiter( PKOLChar( @ Buf[ 0 ] ) );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION}
+function GetWorkDir : KOLString;
+var Buf: array[ 0..MAX_PATH ] of KOLChar;
+begin
+ GetCurrentDirectory( MAX_PATH, @ Buf[ 0 ] );
+ Result := IncludeTrailingPathDelimiter( PKOLChar( @ Buf[ 0 ] ) );
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF WIN}
+
+{$IFDEF ASM_UNICODE}{$ELSE PASCAL}
+function GetTempDir : KOLString;
+{$IFDEF WIN} var Buf : Array[ 0..MAX_PATH ] of KOLChar; {$ENDIF WIN}
+begin
+ {$IFDEF LIN} Result := '/tmp/'; {$ELSE WIN}
+ GetTempPath( MAX_PATH + 1, @Buf[ 0 ] );
+ Result := IncludeTrailingPathDelimiter( PKOLChar( @ Buf[ 0 ] ) );
+ {$ENDIF WIN}
+end;
+{$ENDIF}
+
+{$IFDEF WIN}
+{$IFDEF ASM_UNICODE}{$ELSE PASCAL}
+function CreateTempFile( const DirPath, Prefix: KOLString ): KOLString;
+var Buf: array[ 0..MAX_PATH ] of KOLChar;
+begin
+ GetTempFileName( PKOLChar( DirPath ), PKOLChar( Prefix ), 0, Buf );
+ Result := Buf;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF WIN}
+
+function GetFileListStr(FPath{e.g.'c:\tmp\'}, FMask{e.g.'*.*'}: KOLString): KOLString;
+{* List of files in string, separating each path from others with FileOpSeparator.
+ E.g.: 'c:\tmp\unit1.dcu'#13'c:\tmp\unit1.~pa' (for use with DeleteFile2Recycle())}
+var
+ Srch: TFindFileData;
+ succ: Boolean;
+ dir:KOLString;
+begin
+ result := '';
+ if (FPath<>'') then FPath := IncludeTrailingPathDelimiter( FPath );
+ if (FMask<>'') and (FMask[1]={$IFDEF LIN} '/' {$ELSE} '\' {$ENDIF}) then
+ FMask := CopyEnd(FMask,2);
+ dir:=FPath+FMask;
+ succ := Find_First(dir, Srch);
+ while succ do begin
+ if (not (Srch.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY > 0))then begin
+ if Result<>'' then Result := Result + KOLString(FileOpSeparator);
+ Result := Result + FPath + KOLString(Srch.cFileName);
+ end;
+ succ := Find_Next(Srch);
+ end;
+ Find_Close(Srch);
+end;
+
+function DeleteFiles( const DirPath: KOLString ): Boolean;
+var Files, Name: KOLString;
+begin
+ Files := GetFileListStr( ExtractFilePath( DirPath ), ExtractFileName( DirPath ) );
+ Result := TRUE;
+ while Files <> '' do
+ begin
+ Name := Parse( Files, FileOpSeparator );
+ Result := Result and DeleteFile( PKOLChar( Name ) );
+ end;
+end;
+
+{$IFDEF WIN_GDI} //>>>>>>>>>>>>
+function DeleteFile2Recycle( const Filename : KOLString ) : Boolean;
+begin
+ Result := DoFileOp( Filename, '', FO_DELETE, FOF_ALLOWUNDO or
+ FOF_NOCONFIRMATION or FOF_SIMPLEPROGRESS, 'Deleting...' );
+end;
+
+function CopyMoveFiles( const FromList, ToList: KOLString; Move: Boolean ): Boolean;
+begin
+ Result := DoFileOp(FromList, ToList, FO_COPY - Integer( Move ),
+ FOF_ALLOWUNDO, nil); //|\\ FO_COPY = 2, FO_MOVE = 1
+
+end;
+
+{$IFNDEF PAS_ONLY}
+function DiskFreeSpace( const Path: KOLString ): I64;
+type TGetDFSEx = function( Path: PKOLChar; CallerFreeBytes, TotalBytes, FreeBytes: Pointer )
+ : Bool; stdcall;
+var GetDFSEx: TGetDFSEx;
+ Kern32: THandle;
+ V: TOSVersionInfo;
+ Ex: Boolean;
+ SpC, BpS, NFC, TNC: DWORD;
+ FBA, TNB: I64;
+begin
+ GetDFSEx := nil;
+ V.dwOSVersionInfoSize := Sizeof( V );
+ GetVersionEx( POSVersionInfo( @ V )^ ); // bug in Windows.pas !
+ Ex := FALSE;
+ if V.dwPlatformId = VER_PLATFORM_WIN32_NT then
+ Ex := V.dwMajorVersion >= 4
+ else if V.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS then
+ begin
+ Ex := V.dwMajorVersion > 4;
+ if not Ex then
+ if V.dwMajorVersion = 4 then
+ begin
+ Ex := V.dwMinorVersion > 0;
+ if not Ex then
+ Ex := LoWord( V.dwBuildNumber ) >= $1111;
+ end;
+ end;
+ if Ex then
+ begin
+ Kern32 := GetModuleHandle( 'kernel32' );
+ GetDFSEx := GetProcAddress( Kern32, 'GetDiskFreeSpaceExA' );
+ end;
+ if Assigned( GetDFSEx ) then
+ GetDFSEx( PKOLChar( Path ), @ FBA, @ TNB, @Result )
+ else
+ begin
+ GetDiskFreeSpace( PKOLChar( Path ), SpC, BpS, NFC, TNC );
+ Result := Mul64i( MakeInt64( SpC * BpS, 0 ), NFC );
+ end;
+end;
+{$ENDIF}
+
+function DoFileOp( const FromList, ToList: KOLString; FileOp: UINT; Flags: Word;
+ Title: PKOLChar): Boolean;
+var FOS : {$IFDEF UNICODE_CTRLS}TSHFileOpStructW{$ELSE}TSHFileOpStruct{$ENDIF};
+ Buf : PKOLChar;
+ L : Integer;
+ ToList0: KOLString;
+begin
+ L := Length( FromList );
+ Buf := AllocMem( (L+2) * Sizeof( KOLChar ) );
+ Move( FromList[ 1 ], Buf^, L * Sizeof( KOLChar ) );
+ for L := L downto 0 do
+ if Buf[ L ] = FileOpSeparator then Buf[ L ] := #0;
+ //FillChar( FOS, Sizeof( FOS ), #0 );
+ ZeroMemory( @FOS, Sizeof( FOS ) );
+ if Applet <> nil then
+ FOS.Wnd := Applet.Handle;
+ FOS.wFunc := FileOp;
+ FOS.lpszProgressTitle := Title;
+ FOS.pFrom := Buf;
+ ToList0 := ToList + #0;
+ FOS.pTo := PKOLChar( ToList0 );
+ FOS.fFlags := Flags;
+ FOS.fAnyOperationsAborted := True;
+ Result := {$IFDEF UNICODE_CTRLS}SHFileOperationW{$ELSE}SHFileOperationA{$ENDIF}( FOS ) = 0;
+ if Result then
+ Result := not FOS.fAnyOperationsAborted;
+ FreeMem( Buf );
+end;
+{$ENDIF WIN_GDI}
+
+{$IFDEF WIN}
+function DirIconSysIdxOffline( const Path: KOLString ): Integer;
+var SFI: TShFileInfo;
+begin
+ SFI.iIcon := 0; // Bartov
+ {$IFDEF UNICODE_CTRLS} ShGetFileInfoW {$ELSE} ShGetFileInfoA {$ENDIF}
+ ( PKOLChar( Path ), FILE_ATTRIBUTE_DIRECTORY, SFI, sizeof( SFI ),
+ SHGFI_SMALLICON or SHGFI_SYSICONINDEX or SHGFI_USEFILEATTRIBUTES );
+ Result := SFI.iIcon;
+end;
+{$ENDIF WIN}
+
+{ TDirList }
+
+{$IFDEF SPEED_FASTER}
+ {$DEFINE DIRLIST_FASTER}
+{$ENDIF}
+
+function NewDirList( const DirPath, Filter: KOLString; Attr: DWORD ): PDirList;
+begin
+ New( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TDirList';
+ {$ENDIF}
+ Result.ScanDirectory( DirPath, Filter, Attr );
+end;
+
+function NewDirListEx( const DirPath, Filters: KOLString; Attr: DWORD ): PDirList;
+begin
+ New( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TDirListEx';
+ {$ENDIF}
+ Result.ScanDirectoryEx( DirPath, Filters, Attr );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TDirList.Clear;
+begin
+ Free_And_Nil( FListPositions );
+ Free_And_Nil( fStoreFiles );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+destructor TDirList.Destroy;
+begin
+ Clear;
+ FPath := '';
+ inherited;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function FindFilter(const Filter: KOLString): KOLString;
+begin
+ Result := Filter;
+ if Result = '' then Result := '*.*';
+end;
+{$ENDIF PAS_VERSION}
+
+function TDirList.Get(Idx: Integer): PFindFileData;
+begin
+ {$IFDEF DIRLIST_FASTER}
+ Result := FListPositions.Items[ Idx ];
+ {$ELSE}
+ Result := Pointer( Integer( fStoreFiles.fMemory )
+ + Integer( FListPositions.Items[ Idx ] ) );
+ {$ENDIF}
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TDirList.GetCount: Integer;
+begin
+ Result := 0;
+ if FListPositions = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := FListPositions.Count;
+end;
+{$ENDIF PAS_VERSION}
+
+function TDirList.GetNames(Idx: Integer): KOLString;
+var FData: PFindFileData;
+begin
+ FData := Get( Idx );
+ Result := FData.cFileName;
+end;
+
+function TDirList.GetIsDirectory(Idx: Integer): Boolean;
+begin
+ Result := LongBool( Items[ Idx ].dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY );
+end;
+
+{$IFDEF ASM_noVERSION}
+function TDirList.SatisfyFilter(FileName: PAnsiChar; FileAttr,
+ FindAttr: DWord): Boolean;
+asm
+ PUSH EBX
+ PUSH ESI
+ PUSH EDI
+ XCHG EBX, EAX // EBX = @ Self
+ MOV EAX, [FindAttr]
+ MOV EDI, EDX // EDI = FileName
+ MOV EDX, EAX
+ AND EDX, ECX
+ CMP EDX, EAX
+ JE @@1
+
+ TEST AL, FILE_ATTRIBUTE_NORMAL
+ JZ @@ret_false
+@@1:
+ CMP word ptr [EDI], '.'
+ JE @@1_1
+ CMP word ptr [EDI], '..'
+ JNE @@1_1
+ CMP byte ptr [EDI+2], 0
+ JNE @@1_1
+@@1_0:
+ MOV ECX, [FindAttr]
+ TEST CL, FILE_ATTRIBUTE_NORMAL
+ JZ @@1_1
+ CMP ECX, FILE_ATTRIBUTE_NORMAL
+ JE @@1_1
+ TEST AL, FILE_ATTRIBUTE_DIRECTORY
+ JZ @@1_1
+ TEST CL, FILE_ATTRIBUTE_DIRECTORY
+ JNZ @@ret_true
+
+@@1_1:
+ MOV ECX, [EBX].fFilters
+ JECXZ @@ret_false //?
+
+ MOV ESI, [ECX].TStrList.fList
+ MOV ESI, [ESI].TList.fItems
+ MOV ECX, [ECX].TStrList.fCount
+ JECXZ @@ret_false
+
+@@2:
+ LODSD
+ TEST EAX, EAX
+ JZ @@nx_filter
+
+ PUSHAD
+
+ MOV EDX, [EAX]
+ CMP DX, $002E
+ JE @@F_d_dd
+ AND EDX, $FFFFFF
+ CMP EDX, $002E2E
+ JE @@F_d_dd
+
+ MOV EDX, [EDI]
+ CMP DX, $002E
+ JE @@4
+ AND EDX, $FFFFFF
+ CMP EDX, $002E2E
+ JE @@4
+ JMP @@chk_anti
+
+@@F_d_dd:
+ MOV EDX, EDI
+ PUSH EAX
+ CALL StrComp
+ TEST EAX, EAX
+ POP EAX
+ JZ @@popad_ret_true
+
+@@chk_anti:
+ XCHG EDX, EAX // EDX = filter[ i ]
+ MOV EAX, EDI // EAX = FileName
+ CMP byte ptr [EDX], '^'
+ JNE @@3
+
+ INC EDX
+ CALL _2StrSatisfy
+ TEST AL, AL
+ JZ @@4
+ POPAD
+ JMP @@ret_false
+
+@@3: CALL _2StrSatisfy
+ TEST AL, AL
+ JZ @@4
+@@popad_ret_true:
+ POPAD
+@@ret_true:
+ MOV AL, 1
+ JMP @@exit
+
+@@4: POPAD
+@@nx_filter:
+ LOOP @@2
+
+@@ret_false:
+ XOR EAX, EAX
+@@exit:
+ POP EDI
+ POP ESI
+ POP EBX
+end;
+{$ELSE PAS_VERSION} //Pascal
+function TDirList.SatisfyFilter(FileName: PKOLChar; FileAttr,
+ FindAttr: DWord): Boolean;
+{$IFDEF F_P}
+const Dot: AnsiString = '.';
+{$ENDIF F_P}
+var I: Integer;
+ F: PKOLChar;
+ HasOnlyNegFilters: Boolean;
+ dots: Boolean;
+begin
+ Result := (((FileAttr and FindAttr) = FindAttr) or
+ LongBool(FindAttr and FILE_ATTRIBUTE_NORMAL));
+ if not Result then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+
+ dots := (FileName^ = {$IFDEF F_P}Dot{$ELSE}'.'{$ENDIF})
+ and ( (FileName[1] = {$IFDEF F_P}Dot{$ELSE}'.'{$ENDIF})
+ and (FileName[2] = #0)
+ or (FileName[1] = #0) );
+
+ if not dots then
+ if LongBool( FindAttr and FILE_ATTRIBUTE_NORMAL ) and
+ (FindAttr <> FILE_ATTRIBUTE_NORMAL) then
+ if LongBool( FindAttr and FILE_ATTRIBUTE_DIRECTORY ) and
+ LongBool( FileAttr and FILE_ATTRIBUTE_DIRECTORY ) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+
+ HasOnlyNegFilters := TRUE;
+ for I := 0 to fFilters.Count - 1 do
+ begin
+ F := fFilters.ItemPtrs[ I ];
+ if F = '' then continue;
+ if FileName = F then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if dots then
+ continue;
+ if F[ 0 ] = '^' then
+ begin
+ if StrSatisfy( FileName, PKOLChar(@F[ 1 ]) ) then
+ begin
+ Result := False; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end else
+ begin
+ HasOnlyNegFilters := FALSE;
+ if StrSatisfy( FileName, F ) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+
+ Result := HasOnlyNegFilters and not dots;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_nononoVERSION}
+procedure TDirList.ScanDirectory(const DirPath, Filter: KOLString;
+ Attr: DWord);
+const sz_win32finddata = sizeof(TWin32FindData);
+asm
+ PUSH EBX
+ PUSH EDI
+ MOV EBX, EAX
+
+ PUSHAD
+ CALL Clear
+ CALL NewList
+ MOV [EBX].fList, EAX
+ POPAD
+
+ PUSHAD
+ LEA EAX, [EBX].fPath
+ CALL System.@LStrAsg
+ POPAD
+
+ MOV EAX, [EBX].fPath
+ TEST EAX, EAX
+ JE @@exit
+
+ PUSHAD
+ LEA EDX, [EBX].fPath
+ MOV EAX, [EDX]
+ CALL IncludeTrailingPathDelimiter
+
+ MOV EAX, [EBX].fFilters
+ TEST EAX, EAX
+ JNZ @@1
+ CALL NewStrList
+ MOV [EBX].fFilters, EAX
+ POPAD
+
+ PUSHAD
+ PUSH ECX
+ XCHG EAX, ECX
+ MOV EDX, offset[@@star_d_star]
+ CALL StrComp
+ TEST AL, AL
+ POP EDX
+ JNZ @@asg_Filter
+ MOV EDX, offset[@@star]
+@@asg_Filter:
+ MOV EAX, [EBX].fFilters
+ CALL TStrList.Add
+ JMP @@1
+
+@@star_d_star:
+ DB '*.*', 0 // PCHAR
+
+ {$IFDEF _D2009orHigher}
+ DW 0, 1
+ {$ENDIF}
+ DD -1, 1
+@@star: DB '*', 0
+
+@@1:
+ POPAD
+
+ ADD ESP, -sz_win32finddata
+ XOR EDX, EDX
+ PUSH EDX
+ PUSH EDX
+ XCHG EAX, ECX
+ MOV EDX, ESP
+ CALL FindFilter
+
+ LEA EAX, [ESP+4]
+ MOV EDX, [EBX].fPath
+ POP ECX
+ PUSH ECX
+ CALL System.@LStrCat3
+ CALL RemoveStr
+
+ POP EAX
+ MOV EDX, ESP
+ PUSH EAX
+ PUSH EDX
+ PUSH EAX
+ CALL FindFirstFile
+ MOV EDI, EAX
+ INC EAX
+ MOV EAX, ESP
+
+ PUSHFD
+ CALL System.@LStrClr
+ POPFD
+ POP ECX
+
+ JZ @@fin
+
+@@loop:
+ MOV ECX, [ESP].TWin32FindData.dwFileAttributes
+ PUSH [Attr]
+ LEA EDX, [ESP+4].TWin32FindData.cFileName
+ MOV EAX, EBX
+ CALL SatisfyFilter
+
+ TEST AL, AL
+ JZ @@next
+
+ MOV ECX, [EBX].fOnItem.TMethod.Code
+ JECXZ @@accept
+ MOV EAX, [EBX].fOnItem.TMethod.Data
+ MOV ECX, ESP
+ PUSH 1
+ MOV EDX, ESP
+ PUSH EDX
+ MOV EDX, EBX
+ CALL dword ptr [EBX].fOnItem.TMethod.Code
+ POP ECX
+ JECXZ @@next
+ LOOP @@fin
+
+@@accept:
+ MOV EAX, sz_win32finddata
+ PUSH EAX
+ CALL System.@GetMem
+ PUSH EAX
+ XCHG EDX, EAX
+ MOV EAX, [EBX].fList
+ CALL TList.Add
+ POP EDX
+ POP ECX
+ MOV EAX, ESP
+ CALL System.Move
+
+@@next:
+ PUSH ESP
+ PUSH EDI
+ CALL FindNextFile
+ TEST EAX, EAX
+ JNZ @@loop
+
+ PUSH EDI
+ CALL FindClose
+
+@@fin:
+ ADD ESP, sz_win32finddata
+@@exit:
+ XOR EAX, EAX
+ XCHG EAX, [EBX].fFilters
+ CALL TObj.Free
+ POP EDI
+ POP EBX
+end;
+{$ELSE PAS_VERSION} //Pascal
+procedure TDirList.ScanDirectory(const DirPath, Filter: KOLString;
+ Attr: DWord);
+var FindData : TFindFileData;
+ Action: TDirItemAction;
+ {$IFDEF FORCE_ALTERNATEFILENAME}
+ IsUnicode: KOLString;
+ {$ENDIF}
+ {$IFDEF UNICODE_CTRLS}
+ {$IFDEF SPEED_FASTER}
+ {$IFDEF DIRLIST_OPTIMIZE_ASCII}
+ P: PKOLChar;
+ {$ENDIF}
+ {$ENDIF}
+ {$ENDIF}
+begin
+ Clear;
+ FPath := DirPath;
+ if FPath = '' then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ FPath := IncludeTrailingPathDelimiter( FPath );
+ if (fFilters = nil) then
+ begin
+ fFilters := {$IFDEF UNICODE_CTRLS} NewWStrList {$ELSE} NewStrList {$ENDIF};
+ if Filter = '*.*' then
+ fFilters.Add( '*' )
+ else fFilters.Add( Filter );
+ end;
+ if Find_First( PKOLChar( FPath + FindFilter( Filter ) ), FindData ) then
+ begin // D[u]fa. fix mem leaks (FList, fFilters)
+ FListPositions := NewList;
+ while True do
+ begin
+ {$IFDEF FORCE_ALTERNATEFILENAME} //+MtsVN
+ IsUnicode := FindData.cFileName;
+ if (IsUnicode <> '.') and (IsUnicode <> '..') then
+ begin
+ if pos('?', IsUnicode) > 0 then
+ CopyMemory( @FindData.cFileName, @FindData.cAlternateFileName,
+ SizeOf(FindData.cAlternateFileName));
+ end;
+ {$ENDIF}
+ if SatisfyFilter( PKOLChar(@FindData.cFileName[0]),
+ FindData.dwFileAttributes, Attr ) then
+ begin
+ Action := diAccept;
+ if Assigned( OnItem ) then
+ OnItem( @Self, FindData, Action );
+ CASE Action OF
+ diSkip: ;
+ diAccept:
+ begin
+ if fStoreFiles = nil then
+ begin
+ {$IFDEF DIRLIST_FASTER}
+ fStoreFiles := NewMemBlkStream_WriteOnly( 32 * Sizeof( FindData ) );
+ {$ELSE}
+ fStoreFiles := NewMemoryStream( );
+ fStoreFiles.Capacity := 64 * Sizeof( FindData );
+ {$ENDIF}
+ end;
+ {$IFDEF DIRLIST_FASTER}{$ELSE}
+ FListPositions.Add( Pointer( fStoreFiles.Position ) );
+ {$ENDIF}
+ {$IFDEF UNICODE_CTRLS}
+ {$IFDEF SPEED_FASTER}
+ {$IFDEF DIRLIST_OPTIMIZE_ASCII}
+ FindData.dwReserved0 := 0;
+ P := @ FindData.cFileName[0];
+ while P^ <> #0 do
+ begin
+ if PWord( P )^ > 255 then
+ begin
+ inc( FindData.dwReserved0 );
+ break;
+ end;
+ inc( P );
+ end;
+ {$ENDIF}
+ {$ENDIF}
+ {$ENDIF}
+ fStoreFiles.Write( FindData, Sizeof( FindData ) );
+ {$IFDEF DIRLIST_FASTER}
+ FListPositions.Add( fStoreFiles.fData.fJustWrittenBlkAddress );
+ {$ENDIF}
+ end;
+ diCancel: break;
+ END;
+ end;
+ if not Find_Next( FindData ) then break;
+ end;
+ Find_Close( FindData );
+ end;
+ Free_And_Nil(fFilters); //D[u]fa
+ {$IFnDEF SPEED_FASTER}
+ if fStoreFiles <> nil then
+ begin
+ fStoreFiles.fData.fCapacity := 0;
+ fStoreFiles.Size := fStoreFiles.Position;
+ end;
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+procedure TDirList.ScanDirectoryEx(const DirPath, Filters: KOLString;
+ Attr: DWord);
+var F, FF: KOLString;
+begin
+ FF := Filters;
+ Free_And_Nil( fFilters );
+ fFilters := {$IFDEF UNICODE_CTRLS} NewWStrList {$ELSE} NewStrList {$ENDIF};
+ repeat
+ F := Trim( Parse( FF, ';' ) );
+ if F <> '' then
+ fFilters.Add( F );
+ until FF = '';
+ ScanDirectory( DirPath, '', Attr );
+end;
+{$ENDIF PAS_VERSION}
+
+type
+ PSortDirData = ^TSortDirData;
+ TSortDirData = packed Record
+ CountRules: Integer;
+ FoldersFirst, CaseSensitive, InvertSortOrder : Boolean;
+ Rules : array[ 0..10 ] of TSortDirRules;
+ Dir : PDirList;
+ end;
+
+function CompareDirItems( const Data : PSortDirData; const e1, e2 : DWORD ) : Integer;
+var I : Integer;
+ Item1, Item2 : PFindFileData;
+ S1, S2 : PKOLChar;
+ {$IFDEF UNICODE_CTRLS}
+ W1, W2: KOLWideString;
+ {$ENDIF}
+ IsDir1, IsDir2 : Boolean;
+ {$IFDEF _D4orHigher}
+ sz1, sz2: I64;
+ {$ENDIF}
+begin
+ Item1 := Data.Dir.Get( e1 ); // fList.Items[ e1 ];
+ Item2 := Data.Dir.Get( e2 ); // fList.Items[ e2 ];
+ Result := 0;
+ if Data.FoldersFirst then
+ begin
+ IsDir1 := (Item1.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) <> 0;
+ IsDir2 := (Item2.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) <> 0;
+ if IsDir1 <> IsDir2 then
+ begin
+ if IsDir1 then Result := -1 else Result := 1;
+ exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ for I := 0 to High(Data.Rules){Data.CountRules} do
+ begin
+ case Data.Rules[ I ] of
+ sdrByName:
+ begin
+ S1 := Item1.cFileName;
+ S2 := Item2.cFileName;
+ if not Data.CaseSensitive then
+ begin
+ {$IFDEF UNICODE_CTRLS}
+ {$IFDEF SPEED_FASTER}
+ {$IFDEF DIRLIST_OPTIMIZE_ASCII}
+ if Item1.dwReserved0 or Item2.dwReserved1 = 0 then
+ begin
+ //// ATTANTION: _AnsiCompareStrNoCaseA( '', '' ); must be called before sort!
+ while TRUE do
+ begin
+ Result := SortAnsiOrderNoCase[ Char( S1^ ) ]
+ - SortAnsiOrderNoCase[ Char( S2^ ) ];
+ if Result <> 0 then break;
+ if S1^ = #0 then break;
+ inc( S1 );
+ inc( S2 );
+ end;
+ if Result = 0 then
+ Result := _AnsiCompareStr( Item1.cFileName, Item2.cFileName );
+ end else
+ {$ENDIF}
+ {$ENDIF}
+ begin
+ W1 := S1;
+ W2 := S2;
+ CharUpperBuffW(Pointer(@W1[1]), Length(W1));
+ CharUpperBuffW(Pointer(@W2[1]), Length(W2));
+ Result := _WStrComp( @W1[1], @W2[1] );
+ end;
+ {$ELSE not UNICODE_CTRLS}
+ Result := _AnsiCompareStrNoCaseA( S1, S2 );
+ if Result = 0 then
+ Result := _AnsiCompareStr( S1, S2 );
+ {$ENDIF}
+ end else
+ Result := {$IFDEF UNICODE_CTRLS}
+ _WStrComp( S1, S2 )
+ {$ELSE}
+ _AnsiCompareStrA( S1, S2 )
+ {$ENDIF};
+ end;
+ sdrByExt:
+ begin
+ S1 := Item1.cFileName;
+ S2 := Item2.cFileName;
+ S1 := {$IFDEF UNICODE_CTRLS} @ S1[ DelimiterLast( KOLWideString( S1 ), '.' ) - 1 ]
+ {$ELSE} __DelimiterLast( S1, '.' ) {$ENDIF};
+ S2 := {$IFDEF UNICODE_CTRLS} @ S2[ DelimiterLast( KOLWideString( S2 ), '.' ) - 1 ]
+ {$ELSE} __DelimiterLast( S2, '.' ) {$ENDIF};
+ if not Data.CaseSensitive then
+ Result := {$IFDEF UNICODE_CTRLS}
+ WStrComp( WAnsiUpperCase( S1 ), WAnsiUpperCase( S2 ) )
+ {$ELSE} _AnsiCompareStrNoCase( S1, S2 ) {$ENDIF}
+ else Result := {$IFDEF UNICODE_CTRLS} WStrComp( S1, S2 )
+ {$ELSE} _AnsiCompareStr( S1, S2 ) {$ENDIF};
+ end;
+ sdrBySize, sdrBySizeDescending:
+ begin
+ {$IFDEF _D5orHigher}
+ sz1 := MakeInt64( Item1.nFileSizeLow, Item1.nFileSizeHigh );
+ sz2 := MakeInt64( Item2.nFileSizeLow, Item2.nFileSizeHigh );
+ if Int64(sz1) < Int64(sz2) then
+ Result := -1
+ else if Int64(sz1) > Int64(sz2) then
+ Result := 1
+ else
+ Result := 0;
+ {$ELSE}
+ {$IFDEF _D4orHigher}
+ sz1 := MakeInt64( Item1.nFileSizeLow, Item1.nFileSizeHigh );
+ sz2 := MakeInt64( Item2.nFileSizeLow, Item2.nFileSizeHigh );
+ Result := Cmp64(sz1, sz2);
+ {$ELSE}
+ if Item1.nFileSizeHigh < Item2.nFileSizeHigh then
+ Result := -1
+ else if Item1.nFileSizeHigh > Item2.nFileSizeHigh then
+ Result := 1
+ else if Item1.nFileSizeLow < Item2.nFileSizeLow then
+ Result := -1
+ else if Item1.nFileSizeLow > Item2.nFileSizeLow then
+ Result := 1;
+ {$ENDIF}
+ {$ENDIF}
+ if Data.Rules[ I ] = sdrBySizeDescending then
+ Result := -Result;
+ end;
+{$IFDEF FPC}
+ sdrByDateCreate:
+ Result := CompareFileTime( @Item1.ftCreationTime, @Item2.ftCreationTime );
+ sdrByDateChanged:
+ Result := CompareFileTime( @Item1.ftLastWriteTime, @Item2.ftLastWriteTime );
+ sdrByDateAccessed:
+ Result := CompareFileTime( @Item1.ftLastAccessTime, @Item2.ftLastAccessTime );
+{$ELSE}
+ sdrByDateCreate:
+ Result := CompareFileTime( Item1.ftCreationTime, Item2.ftCreationTime );
+ sdrByDateChanged:
+ Result := CompareFileTime( Item1.ftLastWriteTime, Item2.ftLastWriteTime );
+ sdrByDateAccessed:
+ Result := CompareFileTime( Item1.ftLastAccessTime, Item2.ftLastAccessTime );
+{$ENDIF}
+ sdrNone: break;
+ end; {case}
+ if Result <> 0 then break;
+ end;
+ if Data.InvertSortOrder then
+ Result := -Result;
+end;
+
+procedure SwapDirItems( Data : PSortDirData; const e1, e2 : DWORD ); forward;
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+procedure SwapDirItems( Data : PSortDirData; const e1, e2 : DWORD );
+begin
+ Data.Dir.FListPositions.Swap( e1, e2 );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF noASM_VERSION}
+procedure TDirList.Sort(Rules: array of TSortDirRules);
+const high_DefSortDirRules = High( DefSortDirRules );
+asm
+ PUSH EBX
+ PUSH ESI
+ XOR EBX,EBX
+ CMP [EAX].FListPositions, EBX
+ JE @@exit
+
+ PUSH EAX // prepare Dir = @Self
+ XOR EAX, EAX
+ PUSH EAX
+ PUSH EAX
+ PUSH EAX
+ PUSH EAX
+ MOV ESI, ESP
+ INC ECX // ECX = High(Rules)
+ JZ @@2
+@@1: MOV AH, [EDX] // AH = Rules[ I ]
+ INC EDX
+ CALL @@add_rule
+ LOOP @@1
+@@2: LEA EDX, [DefSortDirRules]
+ MOV CL, high_DefSortDirRules + 1
+@@21: MOV AH, [EDX]
+ INC EDX
+ CALL @@add_rule
+ LOOP @@21
+
+ {$IFDEF UNICODE_CTRLS}
+ {$IFDEF SPEED_FASTER}
+ MOV EAX, offset[@@emptyStr]
+ MOV EDX, EAX
+ CALL dword ptr [_AnsiCompareStrNoCaseA]
+ {$ENDIF}
+ {$ENDIF}
+
+ PUSH BX // prepare FoldersFirst(BL), CaseSensitive(BH)
+ MOV EBX, [ESP].TSortDirData.Dir
+ MOV EAX, ESP
+ PUSH BX
+ PUSH offset[SwapDirItems]
+ MOV ECX, offset[CompareDirItems]
+ MOV EDX, [EBX].FListPositions
+ MOV EDX, [EDX].TList.fCount
+ CALL SortData
+
+ ADD ESP, 20
+ JMP @@exit
+
+ {$IFDEF UNICODE_CTRLS}
+ {$IFDEF SPEED_FASTER}
+@@emptyStr:
+ DW 0
+ {$ENDIF}
+ {$ENDIF}
+
+@@add_rule:
+ PUSH ESI
+ PUSH ECX
+ MOV CL, 11
+@@a1: LODSB
+ TEST AL, AL
+ JZ @@a2
+ CMP AL, AH
+ JE @@a3
+ LOOP @@a1
+@@a2: DEC ESI
+ MOV [ESI], AH
+ CMP AH, sdrFoldersFirst
+ JNE @@a4
+ INC BL
+@@a4: CMP AH, sdrCaseSensitive
+ JNE @@a3
+ INC BH
+@@a3: POP ECX
+ POP ESI
+ RET
+
+@@exit:
+ POP ESI
+ POP EBX
+end;
+{$ELSE PAS_VERSION} //Pascal
+procedure TDirList.Sort(Rules: array of TSortDirRules);
+var SortDirData : TSortDirData;
+ I, J : Integer;
+
+ function RulePresent( Rule : TSortDirRules ) : Boolean;
+ var K : Integer;
+ begin
+ Result := True;
+ for K := J - 1 downto 0 do
+ if Rule = SortDirData.Rules[ K ] then exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := False;
+ end;
+
+ procedure AddRule( Rule : TSortDirRules );
+ begin
+ if Rule in [sdrFoldersFirst, sdrCaseSensitive, sdrInvertOrder] then
+ begin
+ if Rule = sdrFoldersFirst then
+ SortDirData.FoldersFirst := TRUE;
+ if Rule = sdrCaseSensitive then
+ SortDirData.CaseSensitive := TRUE;
+ if Rule = sdrInvertOrder then
+ SortDirData.InvertSortOrder := TRUE;
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ {$IFDEF SAFE_CODE}
+ if J > High( SortDirData.Rules ) then exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$ENDIF}
+ if RulePresent( Rule ) then exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ SortDirData.Rules[ J ] := Rule;
+ Inc( J );
+ end;
+begin
+ if FListPositions = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ ZeroMemory( @ SortDirData, Sizeof( SortDirData ) ); //.CaseSensitive := false; // MTsv DN
+ J := 0;
+ for I := 0 to High(Rules) do
+ AddRule( Rules[ I ] );
+ for I := 0 to High(DefSortDirRules) do
+ AddRule( DefSortDirRules[ I ] );
+ SortDirData.CountRules := J;
+ SortDirData.Dir := @Self;
+ {$IFDEF UNICODE_CTRLS}
+ {$IFDEF SPEED_FASTER}
+ _AnsiCompareStrNoCaseA( '', '' );
+ {$ENDIF}
+ {$ENDIF}
+ SortData( Pointer( @SortDirData ), FListPositions.fCount, @CompareDirItems, @SwapDirItems );
+end;
+{$ENDIF PAS_VERSION}
+
+function TDirList.FileList(const Separator: KOLString; Dirs,
+ FullPaths: Boolean): KOLString;
+var I: Integer;
+begin
+ Result := '';
+ for I := 0 to Count-1 do
+ begin
+ if not Dirs and IsDirectory[ I ] then Continue;
+ if FullPaths then
+ Result := Result + Path;
+ Result := Result + Names[ I ] + Separator;
+ end;
+end;
+
+procedure TDirList.DeleteItem(Idx: Integer);
+begin
+ FListPositions.Delete( Idx );
+end;
+
+procedure TDirList.AddItem(FindData: PFindFileData);
+begin
+ if fStoreFiles = nil then
+ begin
+ {$IFDEF DIRLIST_FASTER}
+ fStoreFiles := NewMemBlkStream_WriteOnly( 32 * Sizeof( FindData ) );
+ {$ELSE}
+ fStoreFiles := NewMemoryStream( );
+ fStoreFiles.Capacity := 64 * Sizeof( FindData );
+ {$ENDIF}
+ FListPositions := NewList;
+ end;
+ {$IFDEF DIRLIST_FASTER}{$ELSE}
+ FListPositions.Add( Pointer( fStoreFiles.Position ) );
+ {$ENDIF}
+ {$IFDEF UNICODE_CTRLS}
+ {$IFDEF SPEED_FASTER}
+ {$IFDEF DIRLIST_OPTIMIZE_ASCII}
+ FindData.dwReserved0 := 0;
+ P := @ FindData.cFileName[0];
+ while P^ <> #0 do
+ begin
+ if PWord( P )^ > 255 then
+ begin
+ inc( FindData.dwReserved0 );
+ break;
+ end;
+ inc( P );
+ end;
+ {$ENDIF}
+ {$ENDIF}
+ {$ENDIF}
+ fStoreFiles.Write( FindData^, Sizeof( FindData^ ) );
+ {$IFDEF DIRLIST_FASTER}
+ FListPositions.Add( fStoreFiles.fData.fJustWrittenBlkAddress );
+ {$ENDIF}
+end;
+
+procedure TDirList.InsertItem(idx: Integer; FindData: PFindFileData);
+begin
+ if fStoreFiles = nil then
+ begin
+ {$IFDEF DIRLIST_FASTER}
+ fStoreFiles := NewMemBlkStream_WriteOnly( 32 * Sizeof( FindData ) );
+ {$ELSE}
+ fStoreFiles := NewMemoryStream( );
+ fStoreFiles.Capacity := 64 * Sizeof( FindData );
+ {$ENDIF}
+ FListPositions := NewList;
+ end;
+ {$IFDEF DIRLIST_FASTER}{$ELSE}
+ FListPositions.Insert( idx, Pointer( fStoreFiles.Position ) );
+ {$ENDIF}
+ {$IFDEF UNICODE_CTRLS}
+ {$IFDEF SPEED_FASTER}
+ {$IFDEF DIRLIST_OPTIMIZE_ASCII}
+ FindData.dwReserved0 := 0;
+ P := @ FindData.cFileName[0];
+ while P^ <> #0 do
+ begin
+ if PWord( P )^ > 255 then
+ begin
+ inc( FindData.dwReserved0 );
+ break;
+ end;
+ inc( P );
+ end;
+ {$ENDIF}
+ {$ENDIF}
+ {$ENDIF}
+ fStoreFiles.Write( FindData^, Sizeof( FindData^ ) );
+ {$IFDEF DIRLIST_FASTER}
+ FListPositions.Insert( idx, fStoreFiles.fData.fJustWrittenBlkAddress );
+ {$ENDIF}
+end;
+
+{$IFDEF WIN_GDI} //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+////////////////////////////////////////////////////////////////////////
+// R E G I S T R Y //
+////////////////////////////////////////////////////////////////////////
+
+{ -- registry -- }
+
+function RegKeyOpenRead( Key: HKey; const SubKey: KOLString ): HKey;
+begin
+ if RegOpenKeyEx( Key, PKOLChar( SubKey ), 0, KEY_READ, Result ) <> ERROR_SUCCESS then
+ Result := 0;
+end;
+
+function RegKeyOpenWrite( Key: HKey; const SubKey: KOLString ): HKey;
+begin
+ if RegOpenKeyEx( Key, PKOLChar( SubKey ), 0, KEY_READ or KEY_WRITE, Result ) <> ERROR_SUCCESS then
+ Result := 0;
+end;
+
+function RegKeyOpenCreate( Key: HKey; const SubKey: KOLString ): HKey;
+var dwDisp: DWORD;
+begin
+ if RegCreateKeyEx( Key, PKOLChar( SubKey ), 0, nil, 0, KEY_ALL_ACCESS, nil, Result,
+ @dwDisp ) <> ERROR_SUCCESS then
+ Result := 0;
+end;
+
+function RegKeyGetDw( Key: HKey; const ValueName: KOLString ): DWORD;
+var dwType, dwSize: DWORD;
+begin
+ dwSize := sizeof( DWORD );
+ Result := 0;
+ if (Key = 0) or
+ (RegQueryValueEx( Key, PKOLChar( ValueName ), nil, @dwType, PByte( @Result ), @dwSize ) <> ERROR_SUCCESS)
+ or (dwType <> REG_DWORD) then Result := 0;
+end;
+
+{$IFDEF REGKEYGETSTREX_ALWAYS}
+function RegKeyGetStr( Key: HKey; const ValueName: KOLString ): KOLString;
+begin
+ Result := RegKeyGetStrEx( Key, ValueName
+ {$IFDEF OPTIONAL_REG_EXPAND_SZ}, FALSE {$ENDIF} );
+end;
+{$ELSE}
+function RegKeyGetStr( Key: HKey; const ValueName: KOLString ): KOLString;
+var dwType, dwSize: DWORD;
+ Buffer: PKOLChar;
+
+ function Query: Boolean;
+ begin
+ Result := RegQueryValueEx( Key, PKOLChar( ValueName ), nil, @dwType,
+ Pointer( Buffer ), @dwSize ) = ERROR_SUCCESS;
+ end;
+begin
+ Result := '';
+ if Key = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ dwSize := 0;
+ Buffer := nil;
+ if not Query or (dwType <> REG_SZ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ GetMem( Buffer, dwSize * Sizeof( KOLChar ) );
+ if Query then
+ Result := Buffer;
+ FreeMem( Buffer );
+end;
+{$ENDIF}
+
+function RegKeyGetStrEx( Key: HKey; const ValueName: KOLString
+ {$IFDEF OPTIONAL_REG_EXPAND_SZ} ; ExpandEnvVars: Boolean {$ENDIF} ):
+KOLString;
+var dwType, dwSize: DWORD;
+ Buffer, Buffer2: PKOLChar;
+ Sz: Integer;
+ function Query: Boolean;
+ begin
+ Result := RegQueryValueEx( Key, PKOLChar( ValueName ), nil, @dwType,
+ Pointer( Buffer ), @dwSize ) = ERROR_SUCCESS;
+ end;
+begin
+ Result := '';
+ if Key = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ dwSize := 0;
+ Buffer := nil;
+ if not Query or ((dwType <> REG_SZ) and (dwtype <> REG_EXPAND_SZ)) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ GetMem( Buffer, dwSize * Sizeof( KOLChar ) );
+ if Query then
+ begin
+ if (dwtype = REG_EXPAND_SZ) {$IFDEF OPTIONAL_REG_EXPAND_SZ} and (ExpandEnvVars) {$ENDIF} then
+ begin
+ Sz := ExpandEnvironmentStrings(Buffer,nil,0);
+ GetMem(Buffer2,Sz * Sizeof( KOLChar )); //
+ ExpandEnvironmentStrings(Buffer, Buffer2, Sz); //
+ Result:=Buffer2; //
+ FreeMem(Buffer2); //
+ end else Result := Buffer;
+ end;
+ FreeMem( Buffer );
+end;
+
+function RegKeySetDw( Key: HKey; const ValueName: KOLString; Value: DWORD ): Boolean;
+begin
+ Result := (Key <> 0) and (RegSetValueEx( Key, PKOLChar( ValueName ), 0,
+ REG_DWORD, @Value, sizeof( DWORD ) ) = ERROR_SUCCESS);
+end;
+
+function RegKeySetStr( Key: HKey; const ValueName: KOLString; const Value: KOLString ): Boolean;
+begin
+ Result := (Key <> 0) and (RegSetValueEx( Key, PKOLChar( ValueName ), 0,
+ REG_SZ, PKOLChar(Value),
+ (Length( Value ) + 1)*Sizeof(KOLChar) ) = ERROR_SUCCESS);
+end;
+
+function RegKeySetStrEx( Key: HKey; const ValueName: KOLString; const Value: KOLString;
+ expand: Boolean): Boolean;
+var dwType: DWORD;
+begin
+ dwType := REG_SZ;
+ if expand then
+ dwType := REG_EXPAND_SZ;
+ Result := (Key <> 0) and (RegSetValueEx(Key, PKOLChar(ValueName), 0, dwType,
+ PKOLChar(Value), (Length(Value) + 1)*Sizeof(KOLChar)) = ERROR_SUCCESS);
+end;
+
+procedure RegKeyClose( Key: HKey );
+begin
+ if Key <> 0 then
+ RegCloseKey( Key );
+end;
+
+function RegKeyDelete( Key: HKey; const SubKey: KOLString ): Boolean;
+begin
+ Result := FALSE;
+ if Key <> 0 then
+ Result := RegDeleteKey( Key, PKOLChar( SubKey ) ) = ERROR_SUCCESS;
+end;
+
+function RegKeyDeleteValue( Key: HKey; const SubKey: KOLString ): Boolean;
+begin
+ Result := FALSE;
+ if Key <> 0 then
+ Result := RegDeleteValue( Key, PKOLChar( SubKey ) ) = ERROR_SUCCESS;
+end;
+
+function RegKeyExists( Key: HKey; const SubKey: KOLString ): Boolean;
+var K: Integer;
+begin
+ if Key = 0 then
+ begin
+ Result := FALSE; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ K := RegKeyOpenRead( Key, SubKey );
+ Result := K <> 0;
+ if K <> 0 then
+ RegKeyClose( K );
+end;
+
+function RegKeyValExists( Key: HKey; const ValueName: KOLString ): Boolean;
+var dwType, dwSize: DWORD;
+begin
+ Result := (Key <> 0) and
+ (RegQueryValueEx( Key, PKOLChar( ValueName ), nil,
+ @dwType, nil, @dwSize ) = ERROR_SUCCESS);
+end;
+
+function RegKeyValueSize( Key: HKey; const ValueName: KOLString ): Integer;
+begin
+ Result := 0;
+ if Key = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ RegQueryValueEx( Key, PKOLChar( ValueName ), nil, nil, nil, @ DWORD( Result ) );
+end;
+
+function RegKeyGetBinary( Key: HKey; const ValueName: KOLString; var Buffer; Count: Integer ): Integer;
+begin
+ Result := 0;
+ if Key = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := Count;
+ RegQueryValueEx( Key, PKOLChar( ValueName ), nil, nil, @ Buffer, @ Result );
+end;
+
+function RegKeySetBinary( Key: HKey; const ValueName: KOLString; const Buffer; Count: Integer ): Boolean;
+begin
+ Result := (Key <> 0) and (RegSetValueEx( Key, PKOLChar( ValueName ), 0,
+ REG_BINARY, @ Buffer, Count ) = ERROR_SUCCESS);
+end;
+
+function RegKeyGetDateTime(Key: HKey; const ValueName: KOLString): TDateTime;
+begin
+ RegKeyGetBinary( Key, ValueName, Result, Sizeof( Result ) );
+end;
+
+function RegKeySetDateTime(Key: HKey; const ValueName: KOLString; DateTime: TDateTime): Boolean;
+begin
+ Result := RegKeySetBinary( Key, ValueName, DateTime, Sizeof( DateTime ) );
+end;
+
+{$IFDEF OLD_REGKEYGETSUBKEYS}
+//-----------------------------------------------
+// functions by Valerian Luft <luft@valerian.de>
+//-----------------------------------------------
+function RegKeyGetSubKeys( const Key: HKEY; List: PKOLStrList) : Boolean;
+var
+ I, Size, NumSubKeys, MaxSubKeyLen : DWORD;
+ KeyName: KOLString;
+begin
+ Result := False;
+ List.Clear ;
+ if RegQueryInfoKey(Key, nil, nil, nil, @NumSubKeys, @MaxSubKeyLen, nil, nil, nil, nil,
+nil, nil) = ERROR_SUCCESS then
+ begin
+ if NumSubKeys > 0 then begin
+ for I := 0 to NumSubKeys-1 do
+ begin
+ Size := MaxSubKeyLen+1;
+ SetLength(KeyName, Size);
+ RegEnumKeyEx(Key, I, @KeyName[1], Size, nil, nil, nil, nil);
+ KeyName := Trim(KeyName); // fixed by Jon
+ List.Add(KeyName);
+ end;
+ end;
+ Result:= True;
+ end;
+end;
+{$ELSE} // new (faster) version by Alex Shyshko (Psychedelic)
+function RegKeyGetSubKeys(const Key: HKEY; List: PKOLStrList) : Boolean;
+var
+ i, MaxSubKeyLen, Size: DWORD;
+ Buf: PKOLChar;
+begin
+ Result:=false;
+ List.Clear;
+
+ if RegQueryInfoKey(Key, nil, nil, nil, nil, @MaxSubKeyLen, nil, nil, nil, nil,
+ nil, nil) = ERROR_SUCCESS then
+ begin
+ if MaxSubKeyLen > 0 then
+ begin
+ Size:=MaxSubKeyLen + 1; //
+ GetMem(Buf,Size*Sizeof(KOLChar)); // fixed by Jon
+ i:=0;
+
+ while RegEnumKeyEx(Key,i,buf,Size,nil,nil,nil,nil) <> ERROR_NO_MORE_ITEMS do
+ begin
+ List.Add(KOLString(Buf));
+ Size:=MaxSubKeyLen + 1;
+ inc(i);
+ end;
+
+ FreeMem(Buf{,MaxSubKeyLen + 1});
+ end; // if MaxSubKeyLen
+ Result:=true;
+ end; // if RegQueryInfoKey
+
+end;
+{$ENDIF}
+
+{$IFDEF OLD_REGKEYGETVALUENAMES}
+function RegKeyGetValueNames(const Key: HKEY; List: PKOLStrList): Boolean;
+var
+ I, Size, NumSubKeys, NumValueNames, MaxValueNameLen: DWORD;
+ ValueName: KOLString;
+begin
+ List.Clear ;
+ Result:=False;
+ if RegQueryInfoKey(Key, nil, nil, nil, @NumSubKeys, nil, nil, @NumValueNames,
+@MaxValueNameLen, nil, nil, nil) = ERROR_SUCCESS then
+ begin
+ if NumValueNames > 0 then
+ for I := 0 to NumValueNames - 1 do begin
+ Size := MaxValueNameLen + 1;
+ SetLength(ValueName, Size);
+ //FillChar(ValueName[1],Size,#0);
+ RegEnumValue(Key, I, @ValueName[1], Size, nil, nil, nil, nil);
+ ValueName := Trim(ValueName);
+ List.Add(ValueName);
+ end;
+ Result := True;
+ end ;
+end;
+{$ELSE} // new (faster) version by Alex Shyshko (Psychedelic)
+function RegKeyGetValueNames(const Key: HKEY; List: PKOLStrList) : Boolean;
+var
+ i, MaxValueNameLen, Size: DWORD;
+ Buf: PKOLchar;
+begin
+ Result:=false;
+ List.Clear;
+
+ if RegQueryInfoKey(Key, nil, nil, nil, nil, nil, nil, nil, @MaxValueNameLen, nil,
+ nil, nil) = ERROR_SUCCESS then
+ begin
+ if MaxValueNameLen > 0 then
+ begin
+ Size:=MaxValueNameLen+1;
+ GetMem(Buf,Size * SizeOf(KOLChar) );
+ i:=0;
+ while RegEnumValue(Key,i,buf,Size,nil,nil,nil,nil) <> ERROR_NO_MORE_ITEMS do
+ begin
+ List.Add(KOLString(Buf));
+ Size:=MaxValueNameLen+1;
+ inc(i);
+ end;
+
+ FreeMem(Buf {,MaxValueNameLen + ... system always knows how long buffer is});
+ end; // if MaxValueNameLen
+ Result:=true;
+ end; // if RegQueryInfoKey
+
+end;
+{$ENDIF}
+
+function RegKeyGetValueTyp (const Key:HKEY; const ValueName: KOLString) : DWORD;
+begin
+Result:= Key ;
+if Key <> 0 then
+ RegQueryValueEx (Key,@ValueName[1],NIL,@Result,NIL,NIL)
+end;
+
+//////////////////////////////////////////////////////////////////////
+// D A T E A N D T I M E
+//////////////////////////////////////////////////////////////////////
+
+{ -- date and time utilities -- }
+
+{* This part of the unit contains date-time routines. It is not a simple compilation
+ of Delphi VCL date-time. E.g., TDateTime type is not based on 31-Dec-1899,
+ but it is based on 31-Dec-0000 instead, allowing easy manipulating of dates
+ at all Christian era, and all other historical era too. }
+
+{$UNDEF PAS_LOCAL}
+{$IFDEF F_P} {$DEFINE PAS_LOCAL} {$ENDIF}
+{$IFDEF PAS_ONLY} {$DEFINE PAS_LOCAL} {$ENDIF}
+
+procedure DivMod(Dividend: Integer; Divisor: Word; var Result, Remainder: Word);
+{$IFDEF PAS_ONLY}
+begin
+ Result := Dividend div Divisor;
+ Remainder := Dividend mod Divisor;
+end;
+{$ELSE DELPHI}
+asm
+ PUSH EBX
+ MOV EBX,EDX
+ MOV EDX,EAX
+ SHR EDX,16
+ DIV BX
+ MOV EBX,Remainder
+ MOV [ECX],AX
+ MOV [EBX],DX
+ POP EBX
+end;
+{$ENDIF}
+
+function Now : TDateTime;
+var SystemTime : TSystemTime;
+begin
+ GetLocalTime( SystemTime );
+ SystemTime2DateTime( SystemTime, Result );
+end;
+
+function Date: TDateTime;
+begin
+ Result := Trunc( Now );
+end;
+
+procedure DecodeDateFully( DateTime: TDateTime; var Year, Month, Day, DayOfWeek: WORD );
+var ST: TSystemTime;
+begin
+ DateTime2SystemTime( DateTime, ST );
+ Year := ST.wYear;
+ Month := ST.wMonth;
+ Day := ST.wDay;
+ DayOfWeek := ST.wDayOfWeek;
+end;
+
+procedure DecodeDate( DateTime: TDateTime; var Year, Month, Day: WORD );
+var Dummy: Word;
+begin
+ DecodeDateFully( DateTime, Year, Month, Day, Dummy );
+end;
+
+function EncodeDate( Year, Month, Day: WORD; var DateTime: TDateTime ): Boolean;
+var ST: TSystemTime;
+begin
+ //FillChar( ST, Sizeof( ST ), #0 );
+ ZeroMemory( @ST, Sizeof(ST) );
+ ST.wYear := Year;
+ ST.wMonth := Month;
+ ST.wDay := Day;
+ Result := SystemTime2DateTime( ST, DateTime );
+end;
+
+procedure IncDays( var SystemTime : TSystemTime; DaysNum : Integer );
+var DateTime : TDateTime;
+begin
+ SystemTime2DateTime( SystemTime, DateTime );
+ DateTime := DateTime + DaysNum;
+ DateTime2SystemTime( DateTime, SystemTime );
+end;
+
+procedure IncMonths( var SystemTime : TSystemTime; MonthsNum : Integer );
+var M : Integer;
+ DateTime : TDateTime;
+begin
+ M := SystemTime.wMonth + MonthsNum - 1;
+ Inc( SystemTime.wYear, M div 12 );
+ SystemTime.wMonth := M mod 12 + 1;
+
+ // Normalize wDayOfWeek field:
+ SystemTime2DateTime( SystemTime, DateTime );
+ DateTime2SystemTime( DateTime, SystemTime );
+end;
+
+function IsLeapYear(Year: Integer): Boolean;
+begin
+ Result := (Year mod 4 = 0) and ((Year mod 100 <> 0) or (Year mod 400 = 0));
+end;
+
+function SystemTime2DateTime(const SystemTime : TSystemTime; var DateTime : TDateTime ) : Boolean;
+{$IFDEF DATE0_1601}
+type
+ TTimeRec = record
+ CASE Integer OF
+ 0: ( ft: TFileTime );
+ 1: ( it: I64 );
+ END;
+var TR: TTimeRec;
+{$ELSE}
+var I : Integer;
+ _Day : Integer;
+ DayTable: PDayTable;
+{$ENDIF}
+begin
+ {$IFDEF DATE0_1601}
+//Result := FALSE;
+//if (SystemTime.wYear < 1601) or (SystemTime.wYear > 30827) then Exit; {>>>>>}
+ Result := SystemTimeToFileTime( SystemTime, TR.ft );
+ if Result then
+ DateTime := Int64( TR.it ) / (10000000.0 * 24 * 3600 ) + Date1601;
+ {$ELSE}
+ Result := False;
+ DateTime := 0.0;
+ DayTable := @MonthDays[IsLeapYear(SystemTime.wYear)];
+ with SystemTime do
+ if {(wYear >= 0) !always true! and} (wYear <= 9999) and
+ {(wMonth >= 1) and !otherwise can not convert time only!}
+ (wMonth <= 12) and
+ {(wDay >= 1) and !otherwise can not convert time only!}
+ (wDay <= DayTable^[wMonth])
+ {$IFDEF SAFEST_CODE}
+ and (wHour < 24) and (wMinute < 60)
+ and (wSecond < 60) and (wMilliSeconds < 1000)
+ {$ENDIF} then //
+ begin
+ _Day := wDay;
+ for I := 1 to wMonth - 1 do
+ Inc(_Day, DayTable^[I]);
+ I := wYear - 1;
+ //--------------- by Vadim Petrov ------++
+ if I<0 then i := 0; //
+ //--------------------------------------++
+ DateTime := (((wHour * 60 + wMinute) * 60 + wSecond) * 1000 + wMilliSeconds)
+ / MSecsPerDay;
+ DateTime := DateTime + I * 365 + I div 4 - I div 100 + I div 400 + _Day;
+ Result := True;
+ end;
+ {$ENDIF DATE0_0001}
+end;
+
+function DayOfWeek(Date: TDateTime): Integer;
+begin
+ Result := (Trunc( Date ) + 6) mod 7 + 1;
+end;
+
+{$IFDEF DATE0_1601}
+
+{$UNDEF ASM_LOCAL}
+{$IFDEF ASM_VERSION}
+{$IFDEF _D6orHigher} {$DEFINE ASM_LOCAL}
+{$ENDIF}
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_LOCAL}
+function DateTime2SystemTime(const DateTime : TDateTime; var SystemTime : TSystemTime ) : Boolean;
+asm
+ PUSH EDI
+ XCHG EDI, EAX
+ FLD qword ptr [DateTime]
+ FSUB dword ptr [@@date1601]
+ FLD tbyte ptr [@@nsecsperday]
+ DB $DE, $C9 //FMULP ST(1)
+ JMP @@truncD7
+@@date1601: DB $50, $AC, $0E, $49
+@@nsecsperday: DB 0,0,0,0,$C0,$69,$2A,$C9,$26,$40
+@@truncD7: CALL System.@TRUNC
+ PUSH EDX
+ PUSH EAX
+ MOV EAX, ESP
+ PUSH EDI
+ PUSH EAX
+ CALL Windows.FileTimeToSystemTime
+ POP ECX
+ POP ECX
+ CMP EAX, 1
+ SBB EAX, EAX
+ INC EAX
+ POP EDI
+end;
+{$ELSE}
+function DateTime2SystemTime(const DateTime : TDateTime; var SystemTime : TSystemTime ) : Boolean;
+type
+ TTimeRec = record
+ CASE Integer OF
+ 0: ( ft: TFileTime );
+ 1: ( it: I64 );
+ END;
+var TR: TTimeRec;
+ {$IFnDEF _D6orHigher}
+ DD, DH, DL: Double;
+ {$ENDIF}
+begin
+ {$IFDEF _D6orHigher}
+ TR.it := I64(
+ Trunc( (DateTime - Date1601) * (24.0 * 3600 * 10000000) ) );
+ {$ELSE}
+ DD := Trunc( (DateTime - Date1601) * (24.0 * 3600 * 10000000) );
+ DH := DD / (4.0 * 1024.0 * 1024.0 * 1024.0);
+ TR.it.Hi := Trunc( DH );
+ DL := DD - TR.it.Hi * (4.0 * 1024.0 * 1024.0);
+ TR.it.Lo := Trunc( DL );
+ {$ENDIF}
+ Result := FileTimeToSystemTime( TR.ft, SystemTime );
+end;
+{$ENDIF PAS_VERSION}
+{$ELSE DATE0_0001}
+
+{$UNDEF ASM_LOCAL}
+{$IFDEF ASM_VERSION}
+{$IFDEF DATE0_0001}
+ {$DEFINE ASM_LOCAL}
+{$ENDIF DATE0_0001}
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_LOCAL}
+var _MSecsPerDay: Double = MSecsPerDay;
+//function DateTime2SystemTime_Asm(const DateTime : TDateTime; var SystemTime : TSystemTime ) : Boolean;
+function DateTime2SystemTime(const DateTime : TDateTime; var SystemTime : TSystemTime ) : Boolean;
+const
+ D1 = 365;
+ D4 = D1 * 4 + 1;
+ D100 = D4 * 25 - 1;
+ D400 = D100 * 4 + 1;
+asm
+ PUSH EBX
+ PUSH ESI
+ PUSH EDI
+ MOV ESI, SystemTime
+ FLD QWORD PTR [DateTime]
+ CALL System.@TRUNC
+ XCHG EDI, EAX // EDI = Days
+ PUSH EDI
+ FILD DWORD PTR [ESP]
+ POP ECX
+ FSUBR QWORD PTR [DateTime]
+ FMUL QWORD PTR [_MSecsPerDay]
+ CALL System.@ROUND
+ XCHG EBX, EAX // EBX = MSecs
+ XOR EAX, EAX
+ CMP EDI, EAX
+ JLE @@retFalse
+
+ DEC EDI
+ INC EAX // EAX = Y = 1
+ MOV ECX, D400
+@@while1:CMP EDI, ECX
+ JL @@1end
+ SUB EDI, ECX
+ ADD EAX, 400
+ JMP @@while1
+@@1end: PUSH EAX
+
+ MOV EAX, EDI
+ XOR EDX, EDX
+ MOV ECX, D100
+ DIV ECX // EAX = division = I, EDX = reminder = D
+ CMP EAX, 4
+ JNZ @@4
+ DEC EAX
+ ADD EDX, D100
+@@4:
+ XCHG EDX, [ESP] // EDX = Y, [ESP] = D
+ MOV ECX, EDX
+ XOR EDX, EDX
+ OR DL, 100
+ MUL EDX // EAX = I * 100
+ ADD ECX, EAX // ECX = Y + I * 100
+ XCHG [ESP], ECX // ECX = D, [ESP] = Y
+
+ XCHG EAX, ECX
+ XOR EDX, EDX
+ MOV ECX, D4
+ DIV ECX // EAX = [D/D4] = I, EDX = D mod D4 = D
+ SHL EAX, 2
+ ADD [ESP], EAX // Y := Y + I * 4;
+
+ XCHG EAX, EDX
+ XOR EDX, EDX
+ XOR ECX, ECX
+ MOV CX, D1
+ DIV ECX
+
+ CMP EAX, 4
+ JNZ @@4x
+ DEC EAX
+ ADD EDX, D1
+@@4x:
+ POP ECX
+ ADD EAX, ECX // inc( Y, I )
+
+ PUSH EDX // save D
+ MOV [ESI].TSystemTime.wYear, AX
+ CALL IsLeapYear
+ SHR EAX, 1
+ SBB EAX, EAX
+ AND EAX, 12
+ LEA ECX, [EAX+MonthDays]// ECX = DayTable
+ POP EAX // restore D
+ PUSH ECX
+@@whTrue:
+ MOVZX EDX, byte ptr [ECX]
+ CMP EAX, EDX
+ JL @@brk
+ SUB EAX, EDX
+ INC ECX
+ JMP @@whTrue
+@@brk:
+ POP EDX
+ SUB ECX, EDX
+ INC ECX
+ MOV [ESI].TSystemTime.wMonth, CX
+ INC EAX
+ MOV [ESI].TSystemTime.wDay, AX
+
+ PUSH dword ptr [DateTime+4]
+ PUSH dword ptr [DateTime]
+ CALL KOL.DayOfWeek
+ MOV [ESI].TSystemTime.wDayOfWeek, AX
+
+ XCHG EAX, EBX
+ XOR EDX, EDX
+ MOV ECX, 60000
+ DIV ECX // EAX = MinCount, EDX = MSecCount
+ PUSH EDX
+ XOR EDX, EDX
+ XOR ECX, ECX
+ MOV CL, 60
+ DIV ECX // EAX = hours, EDX = minutes
+ MOV [ESI].TSystemTime.wHour, AX
+ MOV [ESI].TSystemTime.wMinute, DX
+ POP EAX
+ XOR EDX, EDX
+ MOV CX, 1000
+ DIV ECX // EAX = seconds, EDX = milliseconds
+ MOV [ESI].TSystemTime.wSecond, AX
+ MOV [ESI].TSystemTime.wMilliseconds, DX
+ MOV AL, 1
+@@retFalse:
+ POP EDI
+ POP ESI
+ POP EBX
+end;
+{$ELSE PAS_VERSION}
+//function DateTime2SystemTime_Pas(const DateTime : TDateTime; var SystemTime : TSystemTime ) : Boolean;
+function DateTime2SystemTime(const DateTime : TDateTime; var SystemTime : TSystemTime ) : Boolean;
+const
+ D1 = 365;
+ D4 = D1 * 4 + 1;
+ D100 = D4 * 25 - 1;
+ D400 = D100 * 4 + 1;
+var Days : Integer;
+ Y, M, D, I: Word;
+ MSec : Integer;
+ DayTable: PDayTable;
+ MinCount, MSecCount: Word;
+begin
+ Days := Trunc( DateTime );
+ MSec := Round((DateTime - Days) * MSecsPerDay);
+ Result := False;
+ if IsNAN( DateTime ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ with SystemTime do
+ if Days > 0 then
+ begin
+ Dec(Days);
+ Y := 1;
+ while Days >= D400 do
+ begin
+ Dec(Days, D400);
+ Inc(Y, 400);
+ end;
+ DivMod(Days, D100, I, D);
+ if I = 4 then
+ begin
+ Dec(I);
+ Inc(D, D100);
+ end;
+ Inc(Y, I * 100);
+ DivMod(D, D4, I, D);
+ Inc(Y, I * 4);
+ DivMod(D, D1, I, D);
+ if I = 4 then
+ begin
+ Dec(I);
+ Inc(D, D1);
+ end;
+ Inc(Y, I);
+ DayTable := @MonthDays[IsLeapYear(Y)];
+ M := 1;
+ while True do
+ begin
+ I := DayTable^[M];
+ if D < I then Break;
+ Dec(D, I);
+ Inc(M);
+ end;
+ wYear := Y;
+ wMonth := M;
+ wDay := D + 1;
+ wDayOfWeek := KOL.DayOfWeek( DateTime );
+ DivMod(MSec, 60000, MinCount, MSecCount);
+ DivMod(MinCount, 60, wHour, wMinute);
+ DivMod(MSecCount, 1000, wSecond, wMilliSeconds);
+ Result := True;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF DATE0_0001}
+
+{function DateTime2SystemTime(const DateTime : TDateTime; var SystemTime : TSystemTime ) : Boolean;
+var ST_Pas, ST_Asm: TSystemTime;
+begin
+ if IsNAN( DateTime ) then
+ asm
+ nop
+ end;
+ Result := DateTime2SystemTime_Pas( DateTime, ST_Pas );
+ DateTime2SystemTime_Asm( DateTime, ST_Asm );
+ if Result and not CompareMem( @ ST_Asm, @ST_Pas, Sizeof( TSystemTime ) ) then
+ while TRUE do
+ begin
+ DateTime2SystemTime_Pas( DateTime, ST_Pas );
+ DateTime2SystemTime_Asm( DateTime, ST_Asm );
+ end;
+ Result := DateTime2SystemTime_Pas( DateTime, SystemTime );
+end;}
+
+function DateTime_DiffSysLoc: TDateTime;
+var ST, LT: TSystemTime;
+ FT, FT1: TFileTime;
+ D1, D2: TDateTime;
+begin
+ GetSystemTime( ST );
+ SystemTimeToFileTime( ST, FT );
+ FileTimeToLocalFileTime( FT, FT1 );
+ FileTimeToSystemTime( FT1, LT );
+ SystemTime2DateTime( ST, D1 );
+ SystemTime2DateTime( LT, D2 );
+ Result := D2 - D1;
+end;
+
+function DateTime_System2Local( DTSys: TDateTime ): TDateTime;
+begin
+ Result := DTSys + DateTime_DiffSysLoc;
+end;
+
+function DateTime_Local2System( DTLoc: TDateTime ): TDateTime;
+begin
+ Result := DTLoc - DateTime_DiffSysLoc;
+end;
+
+function FileTime2DateTime( const ft: TFileTime; var DT: TDateTime ): Boolean;
+var ft1: TFileTime;
+ st: TSystemTime;
+begin
+ Result := FileTimeToLocalFileTime( ft, ft1 ) and
+ FileTimeToSystemTime( ft1, st ) and
+ SystemTime2DateTime( st, dt );
+end;
+
+function DateTime2FileTime( DT: TDateTime; var ft: TFileTime ): Boolean;
+var st: TSystemTime;
+begin
+ Result := DateTime2SystemTime( DT, ST ) and
+ SystemTimeToFileTime( st, ft ) and
+ LocalFileTimeToFileTime( ft, ft );
+end;
+
+function SystemDate2Str( const SystemTime : TSystemTime; const LocaleID : DWORD;
+ const DfltDateFormat : TDateFormat;
+ const DateFormat : PKOLChar ) : KOLString;
+var Buf : PKOLChar;
+ Sz : Integer;
+ Flags : DWORD;
+begin
+ Sz := 100;
+ Buf := nil;
+ Result := '';
+ Flags := 0;
+ if DateFormat = nil then
+ if DfltDateFormat = dfShortDate then
+ Flags := DATE_SHORTDATE
+ else Flags := DATE_LONGDATE;
+ while True do
+ begin
+ if Buf <> nil then
+ FreeMem( Buf );
+ GetMem( Buf, Sz * Sizeof( KOLChar ) );
+ if Buf = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if GetDateFormat( LocaleID, Flags, @SystemTime, DateFormat, Buf, Sz ) = 0 then
+ begin
+ if GetLastError = ERROR_INSUFFICIENT_BUFFER then
+ Sz := Sz * 2
+ else break;
+ end else
+ begin
+ Result := Buf;
+ break;
+ end;
+ end;
+ if Buf <> nil then
+ FreeMem( Buf );
+end;
+
+function SystemTime2Str( const SystemTime : TSystemTime; const LocaleID : DWORD;
+ const Flags : TTimeFormatFlags;
+ const TimeFormat : PKOLChar ) : KOLString;
+var Buf : PKOLChar;
+ Sz : Integer;
+ Flg : DWORD;
+begin
+ Sz := 100;
+ Buf := nil;
+ Result := '';
+ Flg := 0;
+ if tffNoMinutes in Flags then
+ Flg := TIME_NOMINUTESORSECONDS
+ else if tffNoSeconds in Flags then
+ Flg := TIME_NOSECONDS;
+ if tffNoMarker in Flags then
+ Flg := Flg or TIME_NOTIMEMARKER;
+ if tffForce24 in Flags then
+ Flg := Flg or TIME_FORCE24HOURFORMAT;
+ while True do
+ begin
+ if Buf <> nil then
+ FreeMem( Buf );
+ GetMem( Buf, Sz * Sizeof( KOLChar ) );
+ if Buf = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if GetTimeFormat( LocaleID, Flg, @SystemTime, TimeFormat, Buf, Sz )
+ = 0 then
+ begin
+ if GetLastError = ERROR_INSUFFICIENT_BUFFER then
+ Sz := Sz * 2
+ else break;
+ end else
+ begin
+ Result := Buf;
+ break;
+ end;
+ end;
+ if Buf <> nil then
+ FreeMem( Buf );
+end;
+
+function Date2StrFmt( const Fmt: KOLString; D: TDateTime ): KOLString;
+var ST: TSystemTime;
+ lpFmt: PKOLChar;
+begin
+ DateTime2SystemTime( D, ST );
+ lpFmt := nil;
+ if Fmt <> '' then lpFmt := PKOLChar( Fmt );
+ Result := SystemDate2Str( ST, LOCALE_USER_DEFAULT, dfShortDate, lpFmt );
+end;
+
+function Time2StrFmt( const Fmt: KOLString; D: TDateTime ): KOLString;
+var ST: TSystemTime;
+ lpFmt: PKOLChar;
+begin
+ if D < 1 then D := D + 700000;
+ DateTime2SystemTime( D, ST );
+ lpFmt := nil;
+ if Fmt <> '' then lpFmt := PKOLChar( Fmt );
+ Result := SystemTime2Str( ST, LOCALE_USER_DEFAULT, [], lpFmt );
+end;
+
+function DateTime2StrShort( D: TDateTime ): KOLString;
+var ST: TSystemTime;
+begin
+ //--------- by Vadim Petrov --------++
+ if D < 1 then D := D + 1; //
+ //----------------------------------++
+ DateTime2SystemTime( D, ST );
+ Result := SystemDate2Str( ST, LOCALE_USER_DEFAULT {GetUserDefaultLCID}, dfShortDate, nil ) + ' ' +
+ SystemTime2Str( ST, LOCALE_USER_DEFAULT {GetUserDefaultLCID}, [], nil );
+end;
+
+function Str2DateTimeFmt( const sFmtStr, sS: KOLString ): TDateTime;
+var h12, hAM: Boolean;
+ FmtStr, S: PKOLChar;
+
+ function GetNum( var S: PKOLChar; NChars: Integer ): Integer;
+ begin
+ Result := 0;
+ while (S^ <> #0) and (NChars <> 0) do
+ begin
+ Dec( NChars );
+ if (S^ >= '0') and (S^ <= '9') then
+ begin
+ Result := Result * 10 + Ord(S^) - Ord('0');
+ Inc( S );
+ end else break;
+ end;
+ end;
+
+ function GetYear( var S: PKOLChar; NChars: Integer ): Integer;
+ var STNow: TSystemTime;
+ OldDate: Boolean;
+ begin
+ Result := GetNum( S, NChars );
+ GetSystemTime( STNow );
+ OldDate := (Result >= 50) and (Result < 100);
+ Result := Result + STNow.wYear - STNow.wYear mod 100;
+ if OldDate then Dec( Result, 100 );
+ end;
+
+ function GetMonth( const fmt: KOLString; var S: PKOLChar ): Integer;
+ var SD: TSystemTime;
+ M: Integer;
+ MonthStr: KOLString;
+ begin
+ GetSystemTime( SD );
+ SD.wDay := 1;
+ for M := 1 to 12 do
+ begin
+ SD.wMonth := M;
+ MonthStr := SystemDate2Str( SD, LOCALE_USER_DEFAULT, dfLongDate, PKOLChar( fmt {+ '/dd/yyyy/'} ) );
+ //MonthStr := Parse( C, '/' ); //++ -- by GMax
+ if AnsiCompareStrNoCase( MonthStr, Copy( S, 1, Length( MonthStr ) ) ) = 0 then
+ begin
+ Inc( S, Length( MonthStr ) );
+ Result := M; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ Result := 1;
+ end;
+
+ procedure SkipDayOfWeek( const fmt: KOLString; var S: PKOLChar );
+ var SD: TSystemTime;
+ Dt: TDateTime;
+ D: Integer;
+ C, DayWeekStr: KOLString;
+ begin
+ GetSystemTime( SD );
+ SystemTime2DateTime( SD, Dt );
+ Dt := Dt - SD.wDayOfWeek;
+ for D := 0 to 6 do
+ begin
+ DateTime2SystemTime( Dt, SD );
+ C := SystemDate2Str( SD, LOCALE_USER_DEFAULT, dfLongDate, PKOLChar( fmt + '/MM/yyyy/' ) );
+ DayWeekStr := Parse( C, '/' );
+ if AnsiCompareStrNoCase( DayWeekStr, Copy( S, 1, Length( DayWeekStr ) ) ) = 0 then
+ begin
+ Inc( S, Length( DayWeekStr ) );
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Dt := Dt + 1.0;
+ end;
+ end;
+
+ procedure GetTimeMark( const fmt: KOLString; var S: PKOLChar );
+ var SD: TSystemTime;
+ AM: Boolean;
+ C, TimeMarkStr: KOLString;
+ begin
+ GetSystemTime( SD );
+ SD.wHour := 0;
+ for AM := FALSE to TRUE do
+ begin
+ C := SystemDate2Str( SD, LOCALE_USER_DEFAULT, dfLongDate, PKOLChar( fmt + '/HH/mm' ) );
+ TimeMarkStr := Parse( C, '/' );
+ if AnsiCompareStrNoCase( TimeMarkStr, Copy( S, 1, Length( TimeMarkStr ) ) ) = 0 then
+ begin
+ Inc( S, Length( TimeMarkStr ) );
+ hAM := AM;
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ SD.wHour := 13;
+ end;
+ Result := 1;
+ end;
+
+ function FmtIs1( S: PKOLChar ): Boolean;
+ begin
+ if StrIsStartingFrom( FmtStr, S ) then
+ begin
+ Inc( FmtStr, {$IFDEF UNICODE_CTRLS} WStrLen {$ELSE} StrLen {$ENDIF}( S ) );
+ Result := TRUE;
+ end else Result := FALSE;
+ end;
+
+ function FmtIs( S1, S2: PKOLChar ): Boolean;
+ begin
+ Result := FmtIs1( S1 ) or FmtIs1( S2 );
+ end;
+
+var ST: TSystemTime;
+begin
+ FmtStr := PKOLChar( sFmtStr);
+ S := PKOLChar( sS );
+ //FillChar( ST, Sizeof( ST ), #0 );
+ ZeroMemory( @ST, Sizeof( ST ) );
+ h12 := FALSE;
+ hAM := FALSE;
+ while (FmtStr^ <> #0) and (S^ <> #0) do
+ begin
+ if ((FmtStr^ >= 'a') and (FmtStr^ <= 'z') or
+ (FmtStr^ >= 'A') and (FmtStr^ <= 'Z')) and
+ (S^ >= '0') and (S^ <= '9') then
+ begin
+ if FmtIs1( 'yyyy' ) then ST.wYear := GetNum( S, 4 )
+ else if FmtIs1( 'yy' ) then ST.wYear := GetYear( S, 2 )
+ else if FmtIs1( 'y' ) then ST.wYear := GetYear( S, -1 )
+ else if FmtIs( 'dd', 'd' ) then ST.wDay := GetNum( S, 2 )
+ else if FmtIs( 'MM', 'M' ) then ST.wMonth := GetNum( S, 2 )
+ else if FmtIs( 'HH', 'H' ) then ST.wHour := GetNum( S, 2 )
+ else if FmtIs( 'hh', 'h' ) then begin ST.wHour := GetNum( S, 2 ); h12 := TRUE end
+ else if FmtIs( 'mm', 'm' ) then ST.wMinute := GetNum( S, 2 )
+ else if FmtIs( 'ss', 's' ) then ST.wSecond := GetNum( S, 2 )
+ else break; // + ECM
+ end
+ else
+ if (FmtStr^ = 'M') or (FmtStr^ = 'd') or (FmtStr^ = 'g') then
+ begin
+ if FmtIs1( 'MMMM' ) then ST.wMonth := GetMonth( 'MMMM', S )
+ else if FmtIs1( 'MMM' ) then ST.wMonth := GetMonth( 'MMM', S )
+ else if FmtIs1( 'dddd' ) then SkipDayOfWeek( 'dddd', S )
+ else if FmtIs1( 'ddd' ) then SkipDayOfWeek( 'ddd', S )
+ else if FmtIs1( 'tt' ) then GetTimeMark( 'tt', S )
+ else if FmtIs1( 't' ) then GetTimeMark( 't', S )
+ else break; // + ECM
+ end
+ else
+ begin
+ if FmtStr^ = S^ then
+ Inc( FmtStr );
+ Inc( S );
+ end;
+ end;
+
+ if h12 then
+ if hAM then
+ Inc( ST.wHour, 12 );
+
+ SystemTime2DateTime( ST, Result );
+end;
+
+function Str2TimeFmt(const sFmtStr, sS: KOLString): TDateTime;
+begin
+ Result := Frac(Str2DateTimeFmt( 'y/M/d ' + sFmtStr, '2000/1/1 ' + sS ));
+end;
+
+var FmtBuf: PKOLChar;
+ DateSeparator : KOLChar = #0; // + ECM
+
+function Str2DateTimeShort( const S: KOLString ): TDateTime;
+var FmtStr, FmtStr2: KOLString;
+
+ function EnumDateFmt( lpstrFmt: PKOLChar ): Boolean; stdcall;
+ begin
+ GetMem( FmtBuf, ({$IFDEF UNICODE_CTRLS} WStrLen {$ELSE} StrLen {$ENDIF}
+ ( lpstrFmt ) + 1) * Sizeof( KOLChar ) );
+ {$IFDEF UNICODE_CTRLS} WStrCopy {$ELSE} StrCopy {$ENDIF}
+ ( FmtBuf, lpstrFmt );
+ Result := FALSE;
+ end;
+
+begin
+ FmtStr := 'dd.MM.yyyy';
+ FmtBuf := nil;
+ EnumDateFormats( @ EnumDateFmt, LOCALE_USER_DEFAULT, DATE_SHORTDATE );
+ if FmtBuf <> nil then
+ begin
+ FmtStr := FmtBuf;
+ FreeMem( FmtBuf );
+ end;
+
+ FmtStr2 := 'H:mm:ss';
+ FmtBuf := nil;
+ EnumTimeFormats( @ EnumDateFmt, LOCALE_USER_DEFAULT, 0 );
+ if FmtBuf <> nil then
+ begin
+ FmtStr2 := FmtBuf;
+ FreeMem( FmtBuf );
+ end;
+
+ Result := Str2DateTimeFmt( FmtStr + ' ' + FmtStr2, S );
+end;
+
+function Str2TimeShort(const S: KOLString): TDateTime;
+begin
+ Result := Frac( Str2DateTimeShort( Date2StrFmt( '', Now ) + ' ' + S ) );
+end;
+
+// + ECM
+function Str2DateTimeShortEx( const S: KOLString ): TDateTime;
+var
+ Buff: Array[0..1] of KOLChar;
+begin
+ if DateSeparator = #0 then
+ begin
+ if GetLocaleInfo(GetThreadLocale,LOCALE_SDATE,Buff,2) > 0 then
+ DateSeparator := Buff[0];
+ end;
+ if Pos(DateSeparator,S) = 0 then
+ //St := '0.0.0 '+S;
+ Result := Str2TimeShort(S)
+ else
+ Result := Str2DateTimeShort(S);
+end;
+
+///////////////////////////////////////////////////////////////////////
+// T H R E A D S
+///////////////////////////////////////////////////////////////////////
+
+{ -- Thread -- }
+
+function ThreadFunc(Thread: PThread): integer; stdcall;
+begin
+ Result := Thread.Execute;
+end;
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewThread: PThread;
+begin
+ new( Result, ThreadCreate );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TThread';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+function NewThread: PThread;
+begin
+ {$IFNDEF FPC105ORBELOW}
+ IsMultiThread := True;
+ {$ENDIF}
+ New( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TThread';
+ {$ENDIF}
+ Result.FSuspended := True;
+ {$IFDEF PSEUDO_THREADS}
+ {$ELSE}
+ Result.FHandle := CreateThread( nil, // no security
+ 0, // the same stack size
+ @ThreadFunc, // thread entry point
+ Result, // parameter to pass to ThreadFunc
+ CREATE_SUSPENDED, // always SUSPENDED
+ Result.FThreadID ); // receive thread ID
+ {$ENDIF}
+end;
+{$ENDIF USE_CONSTRUCTORS}
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewThreadEx( const Proc: TOnThreadExecute ): PThread;
+begin
+ new( Result, ThreadCreateEx( Proc ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TThreadEx';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF ASM_!VERSION}
+function NewThreadEx( const Proc: TOnThreadExecute ): PThread;
+asm
+ CALL NewThread
+ POP EBP
+ POP ECX
+ POP EDX
+ MOV [EAX].TThread.fOnExecute.TMethod.Code, EDX
+ POP EDX
+ MOV [EAX].TThread.fOnExecute.TMethod.Data, EDX
+ PUSH ECX
+ PUSH EAX
+ CALL TThread.Resume
+ POP EAX
+ RET
+end;
+{$ELSE PAS_VERSION} //Pascal
+function NewThreadEx( const Proc: TOnThreadExecute ): PThread;
+begin
+ Result := NewThread;
+ Result.OnExecute := Proc;
+ Result.Resume;
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF USE_CONSTRUCTORS}
+
+function NewThreadAutoFree( const Proc: TOnThreadExecute ): PThread;
+begin
+ Result := NewThread;
+ Result.OnExecute := Proc;
+ Result.F_AutoFree := TRUE;
+ {$IFDEF SAFE_CODE}
+ if Assigned( Proc ) then
+ {$ENDIF}
+ Result.Resume;
+end;
+
+{ TThread }
+
+function WndProcCMExec( Sender: PControl; var Msg: TMsg; var Rslt: Integer )
+ : Boolean;
+var Thread: PThread;
+begin
+ Result := FALSE;
+ if Msg.message = CM_EXECPROC then
+ begin
+ Thread := PThread( Msg.lParam );
+ if Msg.wParam <> 0 then
+ Thread.FMethodEx( Thread, Pointer( Msg.wParam ) )
+ else Thread.FMethod( );
+ Rslt := 0;
+ end;
+end;
+
+{$IFDEF PSEUDO_THREADS}
+function timeBeginPeriod(uPeriod: UINT): UINT; stdcall;
+external 'winmm.dll' name 'timeBeginPeriod';
+function timeEndPeriod(uPeriod: UINT): UINT; stdcall;
+external 'winmm.dll' name 'timeEndPeriod';
+{$ENDIF}
+
+procedure TThread.Init;
+begin
+ {$IFDEF CALL_INHERITED}
+ inherited;
+ {$ENDIF}
+ if Applet <> nil then
+ Applet.AttachProc( WndProcCMExec );
+ {$IFDEF PSEUDO_THREADS}
+ if (MainThread = nil) and not CreatingMainThread then
+ begin // creating main thread
+ CreatingMainThread := TRUE;
+ new( MainThread, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ MainThread.fObjKind := 'MainThread';
+ {$ENDIF}
+ CreatingMainThread := FALSE;
+ end;
+ if CreatingMainThread then
+ begin
+ MainThread := @ Self;
+ {MainThread.}AllThreads := NewList;
+ {MainThread.}CurrentThread := MainThread;
+ TimeBeginPeriod( 10 );
+ end;
+ if not CreatingMainThread and (MainThread <> @ Self) then
+ begin // creating other threads
+ GetMem( StackBottom, PseudoThreadStackSize );
+ CurStackPos := Pointer( DWORD( StackBottom ) + PseudoThreadStackSize );
+ Stack_Empty := TRUE;
+ end;
+ MainThread.AllThreads.Add( @ Self );
+ {$ENDIF}
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+destructor TThread.Destroy;
+begin
+ RefInc;
+ if not FTerminated then
+ begin
+ Terminate;
+ WaitFor;
+ end;
+ if (FHandle <> 0) then
+ CloseHandle(FHandle);
+ {$IFDEF PSEUDO_THREADS}
+ if StackBottom <> nil then
+ FreeMem( StackBottom );
+ if MainThread = @ Self then
+ begin
+ TimeEndPeriod( 10 );
+ AllThreads.Free;
+ end else
+ if MainThread <> nil then
+ begin
+ MainThread.AllThreads.Remove( @ Self );
+ if MainThread.AllThreads.Count <= 1 then
+ Free_And_Nil( MainThread );
+ end;
+ {$ENDIF}
+ inherited;
+end;
+{$ENDIF PAS_VERSION}
+
+function TThread.Execute: integer;
+{$IFDEF TERMAUTOFREE_THREAD}
+var H: THandle;
+{$ENDIF}
+begin
+ {$IFDEF SAFE_CODE}
+ Result := 0;
+ if Assigned( FOnExecute ) then
+ {$ENDIF}
+ Result := FOnExecute( @Self );
+ FResult := Result;
+ FTerminated := TRUE; // fake thread object (to prevent terminating while freeing)
+ if F_AutoFree then
+ begin
+ {$IFDEF TERMAUTOFREE_THREAD}
+ H := FHandle;
+ {$ENDIF}
+ CloseHandle( FHandle );
+ FHandle := 0;
+ Free;
+ {$IFDEF TERMAUTOFREE_THREAD}
+ TerminateThread( H, 0 );
+ {$ENDIF}
+ end;
+end;
+
+function TThread.GetPriorityCls: Integer;
+begin
+ {$IFDEF PSEUDO_THREADS}
+ Result := FPrtyCls;
+ {$ELSE}
+ Result := GetPriorityClass(FHandle);
+ {$ENDIF}
+end;
+
+function TThread.GetThrdPriority: Integer;
+begin
+ {$IFDEF PSEUDO_THREADS}
+ Result := FPriority;
+ {$ELSE}
+ Result := GetThreadPriority(FHandle);
+ {$ENDIF}
+end;
+
+procedure TThread.Resume;
+begin
+ {$IFDEF PSEUDO_THREADS}
+ if MainThread.CurrentThread = @ Self then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>}
+ MainThread.SwitchToThread( @ Self );
+ {$ELSE}
+ FSuspended := False;
+ if (ResumeThread(FHandle) > 1) then
+ FSuspended := True
+ else if Assigned(FOnResume) then
+ FOnResume(@Self);
+ {$ENDIF}
+end;
+
+procedure TThread.SetPriorityCls(Value: Integer);
+begin
+ {$IFDEF DEBUG_ANY}
+ if not SetPriorityClass(GetCurrentProcess, Value) then
+ begin
+ ShowMessage( SysErrorMessage( GetLastError ) );
+ end;
+ {$ELSE}
+ {$IFDEF PSEUDO_THREADS}
+ FPrtyCls := Value;
+ {$ELSE}
+ SetPriorityClass(GetCurrentProcess, Value);
+ {$ENDIF}
+ {$ENDIF DEBUG_ANY}
+end;
+
+procedure TThread.SetThrdPriority(Value: Integer);
+begin
+ FPriority := Value;
+ {$IFDEF PSEUDO_THREADS}
+ {$ELSE}
+ SetThreadPriority(FHandle, Value);
+ {$ENDIF}
+end;
+
+procedure TThread.Suspend;
+begin
+ {$IFDEF PSEUDO_THREADS}
+ if MainThread <> @ Self then
+ FSuspended := TRUE;
+ if MainThread.CurrentThread = @ Self then
+ MainThread.NextThread;
+ {$ELSE}
+ FSuspended := TRUE;
+ if Assigned(FOnSuspend) then
+ Synchronize( FOnSuspend );
+ SuspendThread(FHandle);
+ {$ENDIF}
+end;
+
+{$IFDEF PSEUDO_THREADS}
+procedure FinishThread;
+begin
+ MainThread.CurrentThread.fTerminated := TRUE;
+ MainThread.CurrentThread.Stack_Empty := TRUE;
+ MainThread.NextThread;
+end;
+
+procedure TThread.SwitchToThread(T: PThread);
+begin
+ {$IFDEF SAFE_CODE}
+ if (T <> MainThread)
+ and not Assigned( T.OnExecute ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$ENDIF}
+ if Assigned( MainThread.CurrentThread.OnSuspend ) then
+ begin
+ MainThread.CurrentThread.OnExecute( MainThread.CurrentThread );
+ end;
+ asm
+ mov edx, [T]
+ // 1. Suspending current thread
+ mov ecx, [MainThread]
+ mov eax, [ecx].CurrentThread
+ push ebx
+ push ebp
+ push esi
+ push edi
+ mov [eax].CurStackPos, esp
+ mov [eax].Stack_Empty, 0
+ // 2. Switching to another thread
+
+ mov [ecx].CurrentThread, edx
+
+ cmp [edx].Stack_Empty, 0
+ jz @@1
+ // the first call
+ mov [edx].Stack_Empty, 0
+ cmp [edx].FSuspended, 0
+ jz @@0
+ mov [edx].FSuspended, 0
+
+ mov esp, [edx].CurStackPos
+ mov ecx, [edx].fOnResume.TMethod.Code
+ jecxz @@0
+ mov eax, [edx].fOnResume.TMethod.Data
+ call ecx // calling OnResume for resuming thread
+ @@0:
+ mov eax, [edx].fOnExecute.TMethod.Data
+ mov ecx, [edx].fOnExecute.TMethod.Code
+ push offset [FinishThread] // if thread will be finished it will jump there
+ jmp ecx
+ @@1:
+ // other calls - resuming
+ mov esp, [edx].CurStackPos
+ pop edi
+ pop esi
+ pop ebp
+ pop ebx
+ cmp [edx].FSuspended, 0
+ jz @@2
+ mov [edx].FSuspended, 0
+
+ mov ecx, [edx].fOnResume.TMethod.Code
+ jecxz @@2
+ mov eax, [edx].fOnResume.TMethod.Data
+ call ecx // calling OnResume for resuming thread
+ @@2:
+ end;
+ // At this point, thread is resumed
+end;
+
+procedure TThread.NextThread;
+var i: Integer;
+ T: PThread;
+ C: DWORD;
+begin
+ i := MainThread.AllThreads.IndexOf( MainThread.CurrentThread );
+ if i >= 0 then
+ begin
+ C := GetTickCount;
+ while TRUE do
+ begin
+ inc( i );
+ if i >= MainThread.AllThreads.Count then i := 0;
+ T := MainThread.AllThreads.Items[ i ];
+ if (T.DoNotWakeUntil > C) and (T <> MainThread) then continue;
+ if (T = MainThread) and (MainThread.CurrentThread = T) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if not T.Terminated and not ((T <> MainThread) and (T.Suspended)) then
+ begin
+ break;
+ end;
+ end;
+ MainThread.SwitchToThread( MainThread.AllThreads.Items[ i ] );
+ end;
+end;
+
+procedure Sleep( n: DWORD );
+begin
+ if Assigned( MainThread ) then
+ begin
+ MainThread.CurrentThread.DoNotWakeUntil := GetTickCount + n;
+ MainThread.NextThread;
+ end else
+ if n > 0 then Windows.Sleep( n );
+end;
+
+function WaitForMultipleObjects( nCount: DWORD;
+ lpHandles: PHandle; fWaitAll: BOOL; dwMilliseconds: DWORD ): DWORD; stdcall;
+var i: Integer;
+ w: DWORD;
+ Ph: PHandle;
+ Limit: DWORD;
+begin
+ if dwMilliseconds = INFINITE then
+ Limit := INFINITE
+ else Limit := GetTickCount + dwMilliseconds;
+ while TRUE do
+ begin
+ Ph := lpHandles;
+ w := 0;
+ for i := 0 to nCount-1 do
+ begin
+ if Windows.WaitForSingleObject( Ph^, 0 ) = WAIT_OBJECT_0 then
+ begin
+ inc( w );
+ if not fWaitAll then
+ begin
+ Result := WAIT_OBJECT_0 + i; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ inc( Ph );
+ end;
+ if w = nCount then
+ begin
+ Result := WAIT_OBJECT_0; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ if (Limit <> INFINITE) and (GetTickCount > Limit) then
+ begin
+ Result := WAIT_TIMEOUT; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ if Assigned( MainThread ) then
+ MainThread.NextThread;
+ {$IFDEF WAIT_SLEEP}
+ Sleep( 10 );
+ {$ENDIF}
+ end;
+end;
+
+function WaitForSingleObject( hHandle: THandle; dwMilliseconds: DWORD ): DWORD; stdcall;
+begin
+ Result := WaitForMultipleObjects( 1, @ hHandle, TRUE, dwMilliseconds );
+end;
+{$ENDIF PSEUDO_THREADS}
+
+procedure TThread.Synchronize(Method: TThreadMethod);
+begin
+ {$IFDEF PSEUDO_THREADS}
+ Method;
+ {$ELSE}
+ FMethod := Method;
+ if Applet <> nil then
+ SendMessage( Applet.fHandle, CM_EXECPROC, 0, Integer( @Self ) );
+ {$ENDIF}
+end;
+
+procedure TThread.SynchronizeEx( Method: TThreadMethodEx; Param: Pointer );
+begin
+ {$IFDEF KOL_ASSERTIONS}
+ Assert( Param <> nil, 'Parameter must not be NIL' );
+ {$ENDIF KOL_ASSERTIONS}
+ {$IFDEF PSEUDO_THREADS}
+ Method( TMethod( Method ).Data, Param );
+ {$ELSE}
+ FMethodEx := Method;
+ SendMessage( Applet.fHandle, CM_EXECPROC, Integer( Param ), Integer( @Self ) );
+ {$ENDIF}
+end;
+
+procedure TThread.Terminate;
+begin
+ {$IFDEF PSEUDO_THREADS}
+ FTerminated := TRUE;
+ if Assigned( MainThread ) then
+ if MainThread.CurrentThread = @ Self then
+ MainThread.NextThread;
+ {$ELSE}
+ TerminateThread(FHandle,0);
+ FTerminated := True;
+ {$ENDIF}
+end;
+
+function TThread.WaitFor: Integer;
+begin
+ RefInc;
+ Result := -1;
+ {$IFDEF PSEUDO_THREADS}
+ while not Terminated do
+ Resume;
+ if Terminated then
+ Result := FResult;
+ {$ELSE}
+ if FHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ WaitForSingleObject(FHandle, INFINITE);
+ GetExitCodeThread(FHandle, DWORD(Result));
+ {$ENDIF}
+ RefDec;
+end;
+
+function TThread.WaitForTime(T: DWORD): Integer;
+{$IFDEF PSEUDO_THREADS}
+ var LimitTime: DWORD;
+{$ENDIF}
+begin
+ {$IFDEF PSEUDO_THREADS}
+ LimitTime := GetTickCount + T;
+ RefInc;
+ while not Terminated and (GetTickCount < LimitTime) do
+ Resume;
+ Result := -1;
+ if Terminated then
+ Result := FResult;
+ RefDec;
+ {$ELSE}
+ Result := WAIT_OBJECT_0;
+ RefInc;
+ if FHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := WaitForSingleObject(FHandle, T);
+ if Result = WAIT_OBJECT_0 then
+ GetExitCodeThread(FHandle, T);
+ RefDec;
+ {$ENDIF}
+end;
+
+{$IFDEF _D2}
+ {$DEFINE _D2orFPC}
+{$ENDIF}
+{$IFDEF _FPC}
+ {$IFNDEF _D2orFPC}
+ {$DEFINE _D2orFPC}
+ {$ENDIF}
+{$ENDIF}
+
+function TThread.GetPriorityBoost: Boolean;
+type TGetPriorityBoost = function(hThread: THandle;
+ var DisablePriorityBoost: Bool): BOOL; stdcall;
+var B: Bool;
+ GPB: TGetPriorityBoost;
+ M: THandle;
+begin
+ Result := TRUE;
+ if fHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if (WinVer >= WvNT) then // by TK: only evaluate if this is true, regardless of evaluation settings
+ begin
+ M := GetModuleHandle( 'kernel32' );
+ GPB := GetProcAddress( M, 'GetThreadPriorityBoost' );
+ {$IFDEF SAFE_CODE}
+ if Assigned( GPB ) then
+ {$ENDIF}
+ if GPB( fHandle, B ) then
+ Result := B;
+ end;
+end;
+
+procedure TThread.SetPriorityBoost(const Value: Boolean);
+type TSetPriorityBoost = function(hThread: THandle;
+ DisablePriorityBoost: Bool): Bool; stdcall;
+var M: THandle;
+ SPB: TSetPriorityBoost;
+begin
+ if fHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if WinVer >= WvNT then
+ begin
+ M := GetModuleHandle( 'kernel32' );
+ SPB := GetProcAddress( M, 'SetThreadPriorityBoost' );
+ {$IFDEF SAFE_CODE}
+ if Assigned( SPB ) then
+ {$ENDIF}
+ SPB( fHandle, not Value );
+ end;
+end;
+
+{ TStream }
+
+{* This part of the unit contains implementation of streams for KOL. Please note,
+ that both stream types (file stream and memory stream) are incapsulated
+ by a single object type TStream. To avoid including unnedeed code,
+ use constructing functions NewReadFileStream and NewWriteFileStream
+ to work with file streams, which do not require both types of operation. }
+
+{* To create new type of stream, define your own methods, and in your
+ constructing function, pass it to _NewStream function (through
+ TStreamMethods record). In a field Custom, You can store a reference to
+ your own data of any type (but do not forget to define correct releasing
+ of such data in your fClose procedure). }
+
+function TStream.GetPosition: TStrmSize;
+begin
+ Result := Seek( 0, spCurrent );
+end;
+
+procedure TStream.SetPosition(const Value: TStrmSize);
+begin
+ Seek( Value, spBegin );
+end;
+
+{$IFDEF ASM_STREAM}
+function TStream.GetSize: TStrmSize;
+asm
+ CALL [EAX].fMethods.fGetSiz
+end;
+{$ELSE PAS_VERSION} //Pascal
+function TStream.GetSize: TStrmSize;
+begin
+ Result := fMethods.fGetSiz( @Self );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_STREAM}
+procedure TStream.SetSize(const NewSize: TStrmSize);
+asm
+ CALL [EAX].fMethods.fSetSiz
+end;
+{$ELSE PAS_VERSION} //Pascal
+procedure TStream.SetSize(const NewSize: TStrmSize);
+begin
+ fMethods.fSetSiz( @Self, NewSize );
+end;
+{$ENDIF PAS_VERSION}
+
+function TStream.GetFileStreamHandle: THandle;
+begin
+ Result := fData.fHandle;
+end;
+
+{$IFDEF ASM_STREAM}
+function TStream.Read(var Buffer; const Count: TStrmSize): TStrmSize;
+asm
+ CALL [EAX].fMethods.fRead
+end;
+{$ELSE PAS_VERSION} //Pascal
+function TStream.Read(var Buffer; const Count: TStrmSize): TStrmSize;
+begin
+ Result := fMethods.fRead( @Self, Buffer, Count );
+end;
+{$ENDIF PAS_VERSION}
+
+function TStream.GetCapacity: TStrmSize;
+begin
+ Result := fData.fCapacity;
+end;
+
+procedure TStream.SetCapacity(const Value: TStrmSize);
+var OldSize: DWORD;
+ V: TStrmSize;
+begin
+ V := Value;
+ {$IFDEF OLD_STREAM_CAPACITY}
+ if fData.fCapacity >= Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ OldSize := Size;
+ Size := V;
+ Size := OldSize;
+ {$ELSE}
+ if Value < fData.fSize then V := fData.fSize;
+ if Value > fData.fCapacity then
+ begin
+ OldSize := Size;
+ Size := V;
+ Size := OldSize;
+ end else
+ if fMemory <> nil then
+ begin
+ {$IFDEF _D4orHigher}
+ fMemory := ReallocMemory( fMemory, V );
+ {$ELSE}
+ ReallocMem( fMemory, V );
+ {$ENDIF}
+ fData.fCapacity := V;
+ end;
+ {$ENDIF}
+end;
+
+function TStream.Busy: Boolean;
+begin
+ Result := ( fData.fThread <> nil );
+end;
+
+function TStream.DoAsyncRead( Sender: PThread ): Integer;
+begin
+ Read( Pointer( fParam1 )^, fParam2 );
+ fData.fThread := nil;
+ Result := 0;
+end;
+
+procedure TStream.ReadAsync(var Buffer; Count: DWord);
+begin
+ if Busy then Wait;
+ fData.fThread := NewThreadAutoFree( nil );
+ fData.fThread.OnExecute := DoAsyncRead;
+ fParam1 := DWORD( @ Buffer );
+ fParam2 := Count;
+ fData.fThread.Resume;
+end;
+
+function TStream.DoAsyncSeek( Sender: PThread ): Integer;
+begin
+ Seek( fParam1, TMoveMethod( fParam2 ) );
+ fData.fThread := nil;
+ Result := 0;
+end;
+
+procedure TStream.SeekAsync(MoveTo: TStrmMove; MoveMethod: TMoveMethod);
+begin
+ if Busy then Wait;
+ fData.fThread := NewThreadAutoFree( nil );
+ fData.fThread.OnExecute := DoAsyncSeek;
+ fParam1 := MoveTo;
+ fParam2 := Ord( MoveMethod );
+ fData.fThread.Resume;
+end;
+
+function TStream.DoAsyncWrite( Sender: PThread ): Integer;
+begin
+ Write( Pointer( fParam1 )^, fParam2 );
+ fData.fThread := nil;
+ Result := 0;
+end;
+
+procedure TStream.WriteAsync(var Buffer; Count: DWord);
+begin
+ if Busy then Wait;
+ fData.fThread := NewThreadAutoFree( nil );
+ fData.fThread.OnExecute := DoAsyncWrite;
+ fParam1 := DWORD( @ Buffer );
+ fParam2 := Count;
+ fData.fThread.Resume;
+end;
+
+procedure TStream.Wait;
+begin
+ if ( fData.fThread = nil ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if Assigned( fMethods.fWait ) then
+ fMethods.fWait( @Self )
+ else fData.fThread.WaitFor;
+end;
+
+{$IFDEF ASM_STREAM}
+function TStream.Write(var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize): TStrmSize;
+asm
+ CALL [EAX].fMethods.fWrite
+end;
+{$ELSE PAS_VERSION} //Pascal
+function TStream.Write(var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize): TStrmSize;
+begin
+ Result := fMethods.fWrite( @Self, Buffer, Count );
+end;
+{$ENDIF PAS_VERSION}
+
+function TStream.WriteVal(Value, Count: DWORD): DWORD;
+begin
+ Result := Write( Value, Count );
+end;
+
+function TStream.WriteStr(S: AnsiString): DWORD;
+begin
+ if S <> '' then
+ Result := fMethods.fWrite( @Self, S[1], Length( S ) )
+ else Result := 0;
+end;
+
+function TStream.ReadStrZ: AnsiString;
+var C: AnsiChar;
+begin
+ Result := '';
+ REPEAT
+ C := #0;
+ Read( C, 1 );
+ if C <> #0 then Result := Result + C;
+ UNTIL C = #0;
+end;
+
+{$IFDEF _D3orHigher}
+function TStream.ReadWStrZ: KOLWideString;
+var C: WideChar;
+begin
+ Result := '';
+ REPEAT
+ C := #0;
+ Read( C, 2 );
+ if C <> #0 then
+ Result := Result +
+ {$IFDEF _D3}
+ KOLWideString( C )
+ {$ELSE}
+ C
+ {$ENDIF};
+ UNTIL C = #0;
+end;
+{$ENDIF _D3orHigher}
+
+function TStream.ReadStr: AnsiString;
+var C: AnsiChar;
+begin
+ Result := '';
+ REPEAT
+ C := #0;
+ Read( C, 1 );
+ if C <> #0 then
+ begin
+ if C = #13 then
+ begin
+ C := #0;
+ Read( C, 1 );
+ if C <> #10 then Position := Position - 1;
+ C := #13;
+ end else if C = #10 then
+ C := #13;
+ if C <> #13 then
+ Result := Result + C;
+ end;
+ UNTIL (C = #13) or (C = #0);
+end;
+
+function TStream.ReadStrLen(Len: Integer): AnsiString;
+var i: Integer;
+begin
+ SetLength( Result, Len );
+ i := Read( Result[1], Len );
+ SetLength( Result, i );
+end;
+
+function TStream.WriteStrZ(S: AnsiString): DWORD;
+var C: AnsiChar;
+begin
+ if S = '' then
+ begin
+ C := #0;
+ Result := Write( C, 1 );
+ end
+ else Result := Write( S[ 1 ], Length( S ) + 1 );
+end;
+
+{$IFDEF _D3orHigher}
+function TStream.WriteWStrZ(S: KOLWideString): DWORD;
+var C: WideChar;
+begin
+ if S = '' then
+ begin
+ C := #0;
+ Result := Write( C, 2 );
+ end
+ else Result := Write( S[ 1 ], (Length( S ) + 1) * 2 );
+end;
+{$ENDIF _D3orHigher}
+
+function TStream.WriteStrEx(S: AnsiString): DWord;
+var L: DWORD;
+begin
+ L := length(s);
+ result:=fmethods.fwrite(@self,L,Sizeof(DWORD));
+ if result = Sizeof(DWORD) then
+ Inc( result, fmethods.fwrite(@self,s[1],L) );
+end;
+
+function TStream.ReadStrExVar(var S: AnsiString): DWord;
+begin
+ fmethods.fread(@self,result,Sizeof(DWORD));
+ setlength(s,result);
+ if result<>0 then result:=fmethods.fread(@self,s[1],result);
+end;
+
+function TStream.ReadStrEx: AnsiString;
+begin
+ readstrexvar(result);
+end;
+
+function TStream.WriteStrPas( S: AnsiString ): DWORD;
+var L: Integer;
+begin
+ Result := 0;
+ L := Length( S );
+ if L > 255 then L := 255;
+ if Write( L, 1 ) < 1 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := 1;
+ if L > 0 then
+ Result := Write( S[ 1 ], L ) + 1;
+end;
+
+function TStream.ReadStrPas: AnsiString;
+var L: Byte;
+begin
+ Result := '';
+ if Read( L, 1 ) < 1 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ SetLength( Result, L );
+ L := Read( Result[ 1 ], L );
+ Result := Copy( Result, 1, L );
+end;
+
+{$IFDEF ASM_STREAM}
+function TStream.Seek({$IFNDEF STREAM_COMPAT} const {$ENDIF} MoveTo: TStrmMove; MoveMethod: TMoveMethod): TStrmSize;
+//function TStream.Seek(MoveTo: TStrmMove; MoveMethod: TMoveMethod): TStrmSize;
+asm
+ CALL [EAX].fMethods.fSeek
+end;
+{$ELSE PAS_VERSION} //Pascal
+function TStream.Seek({$IFNDEF STREAM_COMPAT} const {$ENDIF} MoveTo: TStrmMove; MoveMethod: TMoveMethod): TStrmSize;
+begin
+ Result := fMethods.fSeek( @Self, MoveTo, MoveMethod );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+destructor TStream.Destroy;
+begin
+ fMethods.fClose( @Self );
+ fData.fThread.Free;
+ inherited;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TStream.SaveToFile(const Filename: KOLString; const Start, CountSave: TStrmSize);
+var F: PStream;
+ SavePos: DWORD;
+begin
+ F := NewWriteFileStream( Filename );
+ SavePos := Position;
+ Position := Start;
+ Stream2Stream( F, @ Self, CountSave );
+ Position := SavePos;
+ F.Free;
+end;
+
+function _NewStream( const StreamMethods: TStreamMethods ): PStream;
+begin
+ New( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TStream';
+ {$ENDIF}
+ Move( StreamMethods, Result.fMethods, Sizeof( TStreamMethods ) );
+ Result.fPMethods := @Result.fMethods;
+ TMethod( Result.fOnChangePos ).Code := @DummyObjProc;
+end;
+
+function SeekFileStream( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} MoveTo: TStrmMove; MoveFrom: TMoveMethod ): TStrmSize;
+begin
+ Result := FileSeek( Strm.fData.fHandle, MoveTo, MoveFrom );
+ {$IFDEF FILESTREAM_POSITION}
+ Strm.fData.fPosition := Result;
+ {$ENDIF}
+end;
+
+function GetSizeFileStream( Strm: PStream ): TStrmSize;
+{$IFDEF STREAM_LARGE64}
+var SizeHigh: DWORD;
+{$ENDIF}
+begin
+ {$IFDEF STREAM_LARGE64}
+ Result := GetFileSize( Strm.fData.fHandle, @ SizeHigh );
+ Result := Result or SizeHigh shl 32;
+ {$ELSE}
+ Result := GetFileSize( Strm.fData.fHandle, nil );
+ if Result = DWORD( -1 ) then Result := 0;
+ {$ENDIF}
+end;
+
+procedure DummySetSize( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Value: TStrmSize );
+begin
+end;
+
+procedure DummyStreamProc(Strm: PStream);
+begin
+end;
+
+function DummyReadWrite( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+asm
+ XOR EAX, EAX
+ {$IFDEF STREAM_LARGE64}
+ XOR EDX, EDX
+ {$ENDIF}
+end;
+
+function DummySeek( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} MoveTo: TStrmMove; MoveMethod: TMoveMethod ): TStrmSize;
+asm
+ XOR EAX, EAX
+ {$IFDEF STREAM_LARGE64}
+ XOR EDX, EDX
+ {$ENDIF}
+end;
+
+function ReadFileStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+begin
+ Result := FileRead( Strm.fData.fHandle, Buffer, Count );
+ {$IFDEF FILESTREAM_POSITION}
+ inc( Strm.fData.fPosition, Result );
+ {$ENDIF}
+end;
+
+function ReadFileStreamWithEvent( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+begin
+ Result := FileRead( Strm.fData.fHandle, Buffer, Count );
+ inc( Strm.fData.fPosition, Result );
+ if (Result > 0)
+ {$IFDEF SAFE_CODE} and Assigned( Strm.OnChangePos ) {$ENDIF} then
+ Strm.OnChangePos( Strm );
+end;
+
+function WriteFileStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+begin
+ Result := FileWrite( Strm.fData.fHandle, Buffer, Count );
+ {$IFDEF FILESTREAM_POSITION}
+ inc( Strm.fData.fPosition, Result );
+ {$ENDIF}
+end;
+
+function WriteFileStreamWithEvent( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+begin
+ Result := FileWrite( Strm.fData.fHandle, Buffer, Count );
+ inc( Strm.fData.fPosition, Result );
+ if (Result > 0)
+ {$IFDEF SAFE_CODE} and Assigned( Strm.OnChangePos ) {$ENDIF} then
+ Strm.OnChangePos( Strm );
+end;
+
+{$IFDEF ASM_STREAM}
+function WriteFileStreamEOF( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+asm
+ PUSH EBX
+ PUSH [EAX].TStream.fData.fHandle
+ CALL WriteFileStream
+ XCHG EBX, EAX
+ CALL SetEndOfFile
+ XCHG EAX, EBX
+ POP EBX
+end;
+{$ELSE PAS_VERSION} //Pascal
+function WriteFileStreamEOF( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+begin
+ Result := WriteFileStream( Strm, Buffer, Count );
+ {$IFDEF FILESTREAM_POSITION}
+ inc( Strm.fData.fPosition, Result );
+ {$ENDIF}
+ SetEndOfFile( Strm.fData.fHandle );
+end;
+{$ENDIF PAS_VERSION}
+
+function WriteFileStreamEOFWithEvent( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+begin
+ Result := WriteFileStream( Strm, Buffer, Count );
+ inc( Strm.fData.fPosition, Result );
+ SetEndOfFile( Strm.fData.fHandle );
+ if (Result > 0)
+ {$IFDEF SAFE_CODE} and Assigned( Strm.OnChangePos ) {$ENDIF} then
+ Strm.OnChangePos( Strm );
+end;
+
+procedure CloseFileStream( Strm: PStream );
+begin
+ if Strm.fData.fHandle <> INVALID_HANDLE_VALUE then
+ FileClose( Strm.fData.fHandle );
+ Strm.fData.fHandle := INVALID_HANDLE_VALUE;
+end;
+
+{$IFDEF ASM_STREAM}
+function SeekMemStream( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF}
+ MoveTo: TStrmMove; MoveFrom: TMoveMethod ): TStrmSize;
+asm
+ PUSH EBX
+ MOV EBX, EDX
+ AND ECX, $FF
+ LOOP @@not_from_cur
+ ADD EBX, [EAX].TStream.fData.fPosition
+@@not_from_cur:
+ LOOP @@not_from_end
+ ADD EBX, [EAX].TStream.fData.fSize
+@@not_from_end:
+ CMP EBX, [EAX].TStream.fData.fSize
+ JLE @@space_ok
+ PUSH EAX
+ MOV EDX, EBX
+ CALL TStream.SetSize
+ POP EAX
+@@space_ok:
+ XCHG EAX, EBX
+ MOV [EBX].TStream.fData.fPosition, EAX
+ POP EBX
+end;
+{$ELSE PAS_VERSION} //Pascal
+function SeekMemStream( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF}
+ MoveTo: TStrmMove; MoveFrom: TMoveMethod ): TStrmSize;
+var NewPos: DWORD;
+begin
+ case MoveFrom of
+ spBegin: NewPos := MoveTo;
+ spCurrent: NewPos := Strm.fData.fPosition + DWORD( MoveTo );
+ else //spEnd:
+ NewPos := Strm.fData.fSize + DWORD( MoveTo );
+ end;
+ if NewPos > Strm.fData.fSize then
+ Strm.SetSize( NewPos );
+ Strm.fData.fPosition := NewPos;
+ Result := NewPos;
+end;
+{$ENDIF PAS_VERSION}
+
+function SeekMemStreamWithEvent( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} MoveTo: TStrmMove; MoveFrom: TMoveMethod ): TStrmSize;
+var OldPos: DWORD;
+begin
+ OldPos := Strm.Position;
+ Result := SeekMemStream( Strm, MoveTo, MoveFrom );
+ if (OldPos <> Strm.Position)
+ {$IFDEF SAFE_CODE} and Assigned( Strm.OnChangePos ) {$ENDIF} then
+ Strm.OnChangePos( Strm );
+end;
+
+function GetSizeMemStream( Strm: PStream ): TStrmSize;
+begin
+ Result := Strm.fData.fSize;
+end;
+
+{$IFDEF ASM_STREAM}
+procedure SetSizeMemStream( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} NewSize: TStrmSize );
+asm
+ push ebx
+ push edx
+ xchg ebx, eax
+ cmp [ebx].TStream.fData.fCapacity, edx
+ jae @@mem_ok
+ {$IFDEF OLD_MEMSTREAMS_SETSIZE}
+ or edx, [CapacityMask]
+ inc edx
+ {$ENDIF}
+ mov [ebx].TStream.fData.fCapacity, edx
+ mov ecx, [ebx].TStream.fMemory
+ jecxz @@getmem
+ lea eax, [ebx].TStream.fMemory
+ call System.@ReallocMem
+ jmp @@setmem
+
+@@getmem:
+ or ecx, edx
+ jz @@mem_ok
+ xchg eax, ecx
+ call System.@GetMem
+@@setmem:
+ mov [ebx].TStream.fMemory, eax
+
+@@mem_ok:
+ pop ecx // NewSize
+ inc ecx
+ loop @@set_new_sz
+ cmp [ebx].TStream.fData.fSize, ecx
+ jz @@set_new_sz
+
+ mov [ebx].TStream.fData.fCapacity, ecx
+ xchg ecx, [ebx].TStream.fMemory
+ jecxz @@mem_freed
+ xchg eax, ecx
+ call System.@FreeMem
+@@mem_freed:
+ xor ecx, ecx
+
+@@set_new_sz:
+ mov [ebx].TStream.fData.fSize, ecx
+ cmp [ebx].TStream.fData.fPosition, ecx
+ jb @@exit
+ mov [ebx].TStream.fData.fPosition, ecx
+
+@@exit:
+ pop ebx
+end;
+{$ELSE PAS_VERSION} //Pascal
+procedure SetSizeMemStream( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} NewSize: TStrmSize );
+var S: PStream;
+ NewCapacity: DWORD;
+begin
+ S := Strm;
+ if S.fData.fCapacity < NewSize then
+ begin
+ {$IFDEF OLD_MEMSTREAMS_SETSIZE}
+ NewCapacity := (NewSize or CapacityMask) + 1;
+ {$ELSE}
+ NewCapacity := NewSize;
+ {$ENDIF}
+ if S.fMemory = nil then
+ begin
+ if NewSize <> 0 then
+ GetMem( S.fMemory, NewCapacity );
+ end else ReallocMem( S.fMemory, NewCapacity );
+ S.fData.fCapacity := NewCapacity;
+ end else
+ if (NewSize = 0) and (S.Size > 0) then
+ begin
+ if S.fMemory <> nil then
+ begin
+ FreeMem( S.fMemory );
+ S.fMemory := nil;
+ S.fData.fCapacity := 0;
+ end;
+ end;
+ S.fData.fSize := NewSize;
+ if S.fData.fPosition > S.fData.fSize then
+ S.fData.fPosition := S.fData.fSize;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_STREAM}
+function ReadMemStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ MOV EAX, [EBX].TStream.fData.fPosition
+ ADD EAX, ECX
+ CMP EAX, [EBX].TStream.fData.fSize
+ JLE @@count_ok
+ MOV ECX, [EBX].TStream.fData.fSize
+ SUB ECX, [EBX].TStream.fData.fPosition
+@@count_ok:
+ PUSH ECX
+ MOV EAX, [EBX].TStream.fMemory
+ ADD EAX, [EBX].TStream.fData.fPosition
+ CALL System.Move
+ POP EAX
+ ADD [EBX].TStream.fData.fPosition, EAX
+ POP EBX
+end;
+{$ELSE PAS_VERSION} //Pascal
+function ReadMemStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+var S: PStream;
+ C: TStrmSize;
+begin
+ S := Strm;
+ C := Count;
+ if C + S.fData.fPosition > S.fData.fSize then
+ C := S.fData.fSize - S.fData.fPosition;
+ Result := C;
+ Move( Pointer( DWORD(S.fMemory) + S.fData.fPosition )^, Buffer, Result );
+ Inc( S.fData.fPosition, Result );
+end;
+{$ENDIF PAS_VERSION}
+
+function ReadMemStreamWithEvent( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+begin
+ Result := ReadMemStream( Strm, Buffer, Count );
+ if (Result > 0)
+ {$IFDEF SAFE_CODE} and Assigned( Strm.OnChangePos ) {$ENDIF} then
+ Strm.OnChangePos( Strm );
+end;
+
+{$IFDEF ASM_STREAM}
+function WriteMemStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ MOV EAX, [EBX].TStream.fData.fPosition
+ ADD EAX, ECX
+ CMP EAX, [EBX].TStream.fData.fSize
+ PUSH EDX
+ PUSH ECX
+ JLE @@count_ok
+ XCHG EDX, EAX
+ MOV EAX, EBX
+ CALL TStream.SetSize
+@@count_ok:
+ POP ECX
+ POP EAX
+ MOV EDX, [EBX].TStream.fMemory
+ ADD EDX, [EBX].TStream.fData.fPosition
+ PUSH ECX
+ CALL System.Move
+ POP EAX
+ ADD [EBX].TStream.fData.fPosition, EAX
+ POP EBX
+end;
+{$ELSE PAS_VERSION} //Pascal
+function WriteMemStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+var S: PStream;
+begin
+ S := Strm;
+ if Count + S.fData.fPosition > S.fData.fSize then
+ S.SetSize( S.fData.fPosition + Count );
+ Result := Count;
+ Move( Buffer, Pointer( DWORD(S.fMemory) + S.fData.fPosition )^, Result );
+ Inc( S.fData.fPosition, Result );
+end;
+{$ENDIF PAS_VERSION}
+
+function WriteMemStreamWithEvent( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+begin
+ Result := WriteMemStream( Strm, Buffer, Count );
+ if (Result > 0)
+ {$IFDEF SAFE_CODE} and Assigned( Strm.OnChangePos ) {$ENDIF} then
+ Strm.OnChangePos( Strm );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure CloseMemStream( Strm: PStream );
+var S: PStream;
+begin
+ S := Strm;
+ if S.fMemory <> nil then
+ begin
+ FreeMem( S.fMemory );
+ S.fMemory := nil;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure DummyCloseStream( Strm: PStream );
+begin
+ // nothing here
+end;
+
+// by Roman Vorobets:
+procedure SetSizeFileStream( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} NewSize: TStrmSize );
+var P: DWORD;
+begin
+ P:=Strm.Position;
+ Strm.Position:=NewSize;
+ SetEndOfFile(Strm.Handle);
+ if P < NewSize then
+ Strm.Position:=P;
+end;
+
+function ReadMemBlkStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+var P, bStart, bLen, C: DWORD;
+ bAddr: PByte;
+ i: Integer;
+begin
+ P := Strm.Position;
+ i := 0;
+ bStart := 0;
+ bLen := 0;
+ bAddr := nil;
+ while i < Strm.fData.fBlocks.Count do
+ begin
+ bAddr := Strm.fData.fBlocks.fItems[i];
+ bLen := Integer( Strm.fData.fBlocks.fItems[i+1] );
+ if bStart + bLen > P then
+ break;
+ inc( i, 2 );
+ inc( bStart, bLen );
+ end;
+ if bStart + bLen < P then
+ begin
+ Result := 0; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ inc( bAddr, P - bStart );
+ C := Count;
+ if C > bLen - (P - bStart) then
+ C := bLen - (P - bStart);
+ if C > 0 then
+ Move( bAddr^, Buffer, C );
+ Result := C;
+ inc( Strm.fData.fPosition, C );
+end;
+
+function SeekMemBlkStream( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} MoveTo: TStrmMove; MoveFrom: TMoveMethod ): TStrmSize;
+var P: Integer;
+begin
+ P := MoveTo;
+ CASE MoveFrom OF
+ spCurrent: P := P + Integer( Strm.fData.fPosition );
+ spEnd: P := P + Integer( Strm.fData.fSize );
+ END;
+ if P < 0 then P := 0;
+ if P > Integer( Strm.fData.fSize ) then
+ P := Strm.fData.fSize;
+ Strm.fData.fPosition := P;
+ Result := P;
+end;
+
+function WriteMemBlkStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+var LastBlkAddr: PByte;
+ LastBlkUsed, C: Integer;
+ NewBlkSz: Integer;
+begin
+ C := Strm.fData.fBlocks.Count;
+ LastBlkUsed := Strm.fData.fBlkSize;
+ LastBlkAddr := nil;
+ if C > 1 then
+ begin
+ LastBlkAddr := Strm.fData.fBlocks.Items[C-2];
+ LastBlkUsed := Integer( Strm.fData.fBlocks.Items[C-1] );
+ end;
+ if Strm.fData.fBlkSize - LastBlkUsed < Integer( Count ) then
+ begin
+ NewBlkSz := Strm.fData.fBlkSize;
+ if NewBlkSz < Integer( Count ) then
+ NewBlkSz := Count;
+ GetMem( LastBlkAddr, NewBlkSz );
+ LastBlkUsed := 0;
+ Strm.fData.fBlocks.Add( LastBlkAddr );
+ Strm.fData.fBlocks.Add( nil );
+ inc( C, 2 );
+ end;
+ inc( LastBlkAddr, LastBlkUsed );
+ Strm.fData.fJustWrittenBlkAddress := LastBlkAddr;
+ Move( Buffer, LastBlkAddr^, Count );
+ inc( LastBlkUsed, Count );
+ Strm.fData.fBlocks.fItems[ C-1 ] := Pointer( LastBlkUsed );
+ inc( Strm.fData.fSize, Count );
+ Strm.fData.fPosition := Strm.fData.fSize;
+ Result := Count;
+end;
+
+procedure ResizeMemBlkStream( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} NewSize: TStrmSize );
+var i, del: Integer;
+ LastBlkAddr: PByte;
+ LastBlkUsed: Integer;
+begin
+ while Strm.fData.fSize > NewSize do
+ begin
+ i := Strm.fData.fBlocks.Count-2;
+ LastBlkAddr := Strm.fData.fBlocks.fItems[i];
+ LastBlkUsed := Integer( Strm.fData.fBlocks.fItems[i+1] );
+ del := Strm.fData.fSize - NewSize;
+ if del >= LastBlkUsed then
+ begin
+ FreeMem( LastBlkAddr );
+ Strm.fData.fBlocks.DeleteRange( i, 2 );
+ dec( Strm.fData.fSize, LastBlkUsed );
+ end else
+ begin
+ Strm.fData.fBlocks.fItems[ i+1 ] := Pointer( LastBlkUsed - del );
+ dec( Strm.fData.fSize, del );
+ end;
+ end;
+ if Strm.fData.fSize > Strm.fData.fPosition then
+ Strm.fData.fPosition := Strm.fData.fSize;
+end;
+
+procedure FreeMemBlkStream( Strm: PStream );
+var i: Integer;
+begin
+ i := 0;
+ while i < Strm.fData.fBlocks.Count do
+ begin
+ FreeMem( Strm.fData.fBlocks.fItems[i] );
+ inc( i, 2 );
+ end;
+ {$IFDEF SAFE_CODE}
+ Free_And_Nil( Strm.fData.fBlocks );
+ Strm.fData.fPosition := 0;
+ Strm.fData.fSize := 0;
+ {$ELSE}
+ Strm.fData.fBlocks.Free;
+ {$ENDIF}
+end;
+
+function SeekConcatStream( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} MoveTo: TStrmMove; MoveFrom: TMoveMethod ): TStrmSize;
+var NewPos: TStrmSize;
+begin
+ NewPos := MoveTo;
+ CASE MoveFrom OF
+ spCurrent: NewPos := TStrmMove( Strm.fData.fPosition ) + MoveTo;
+ spEnd : NewPos := TStrmMove( Strm.Size ) + MoveTo;
+ END;
+ if Strm.fData.fStream1.Size > NewPos then
+ begin
+ Strm.fData.fStream1.Position := NewPos;
+ Strm.fData.fStream2.Position := 0;
+ end else
+ begin
+ Strm.fData.fStream1.Position := Strm.fData.fStream1.Size;
+ Strm.fData.fStream2.Position := NewPos - Strm.fData.fStream1.Size;
+ end;
+ Strm.fData.fPosition := Strm.fData.fStream1.Position + Strm.fData.fStream2.Position;
+ Result := Strm.fData.fPosition;
+end;
+
+function GetSizeConcatStream( Strm: PStream ): TStrmSize;
+begin
+ Result := Strm.fData.fStream1.Size + Strm.fData.fStream2.Size;
+end;
+
+procedure SetSizeConcatStream( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} NewSize: TStrmSize );
+var New_Sz, Sz1: TStrmSize;
+begin
+ New_Sz := NewSize;
+ Sz1 := Strm.fData.fStream1.Size;
+ if New_Sz < Sz1 then
+ New_Sz := Sz1;
+ Strm.fData.fStream2.Size := New_Sz - Sz1;
+end;
+
+function ReadConcatStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+var C, Sz1, ToRead: TStrmSize;
+ ToAddr: PByte;
+begin
+ C := Count;
+ Sz1 := Strm.fData.fStream1.Size;
+ ToAddr := @ Buffer;
+ Result := 0;
+ if Strm.Position < Sz1 then
+ begin
+ ToRead := C;
+ if Strm.Position + C > Sz1 then
+ ToRead := Sz1 - Strm.Position;
+ Result := Strm.fData.fStream1.Read( ToAddr^, ToRead );
+ Strm.fData.fPosition := Strm.fData.fStream1.Position;
+ dec( C, Result );
+ inc( ToAddr, Result );
+ if Result < ToRead then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Strm.fData.fStream2.Position := 0;
+ end;
+ if C <= 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := Result + Strm.fData.fStream2.Read( ToAddr^, C );
+ Strm.fData.fPosition := Strm.fData.fStream1.Size +
+ Strm.fData.fStream2.Position;
+end;
+
+function WriteConcatStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+var C, Sz1, ToWrite: TStrmSize;
+ FromAddr: PByte;
+begin
+ C := Count;
+ Sz1 := Strm.fData.fStream1.Size;
+ FromAddr := @ Buffer;
+ Result := 0;
+ if Strm.Position < Sz1 then
+ begin
+ ToWrite := C;
+ if Strm.Position + C > Sz1 then
+ ToWrite := Sz1 - Strm.Position;
+ Result := Strm.fData.fStream1.Write( FromAddr^, ToWrite );
+ Strm.fData.fPosition := Strm.fData.fStream1.Position;
+ dec( C, Result );
+ inc( FromAddr, Result );
+ if Result < ToWrite then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Strm.fData.fStream2.Position := 0;
+ end;
+ if C <= 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := Result + Strm.fData.fStream2.Write( FromAddr^, C );
+ Strm.fData.fPosition := Strm.fData.fStream1.Size +
+ Strm.fData.fStream2.Position;
+end;
+
+procedure CloseConcatStream( Strm: PStream );
+begin
+ Strm.fData.fStream1.fMethods.fClose( Strm.fData.fStream1 );
+ Strm.fData.fStream2.fMethods.fClose( Strm.fData.fStream2 );
+end;
+
+function SeekSubStream( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} MoveTo: TStrmMove; MoveFrom: TMoveMethod ): TStrmSize;
+var NewPos, OldPos: TStrmMove;
+begin
+ OldPos := Strm.fData.fBaseStream.Position - Strm.fData.fFromPos;
+ {$IFDEF STREAM_LARGE64}
+ if OldPos < 0 then OldPos := 0;
+ {$ENDIF}
+ CASE MoveFrom OF
+ spCurrent: NewPos := OldPos + MoveTo;
+ spEnd : NewPos := TStrmMove( Strm.Size ) + MoveTo;
+ else NewPos := MoveTo;
+ END;
+ {$IFDEF STREAM_LARGE64}
+ if NewPos < 0 then NewPos := 0;
+ {$ENDIF}
+ Strm.fData.fBaseStream.Position := Strm.fData.fFromPos + TStrmSize( NewPos );
+ Result := Strm.fData.fBaseStream.Position - Strm.fData.fFromPos;
+ if Result > Strm.fData.fSize then
+ Strm.fData.fSize := Result;
+end;
+
+function GetSizeSubStream( Strm: PStream ): TStrmSize;
+begin
+ Result := Strm.fData.fSize;
+end;
+
+procedure SetSizeSubStream( Strm: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} NewSize: TStrmSize );
+begin
+ {$IFDEF STREAM_LARGE64}
+ if NewSize >= 0 then
+ {$ENDIF}
+ Strm.fData.fSize := NewSize;
+end;
+
+function ReadSubStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+var C: TStrmSize;
+begin
+ C := Count;
+ if Strm.Position + C > Strm.Size then
+ C := Strm.Size - Strm.Position;
+ Result := Strm.fData.fBaseStream.Read( Buffer, C );
+end;
+
+function WriteSubStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+begin
+ Result := Strm.fData.fBaseStream.Write( Buffer, Count );
+end;
+
+procedure CloseSubStream( Strm: PStream );
+begin
+ Strm.fData.fBaseStream.fMethods.fClose( Strm.fData.fBaseStream );
+end;
+
+
+function NewFileStream( const FileName: KOLString; Options: DWORD ): PStream;
+begin
+ Result := _NewStream( BaseFileMethods );
+ Result.fMethods.fRead := ReadFileStreamProc;
+ Result.fMethods.fWrite := WriteFileStream; // not WriteStreamEOF, Àëåêñåé Øóâàëîâ
+ Result.fMethods.fSetSiz := SetSizeFileStream;
+ Result.fData.fHandle := FileCreate( FileName, Options );
+end;
+
+function NewFileStreamWithEvent( const FileName: KOLString; Options: DWORD ): PStream;
+begin
+ Result := _NewStream( BaseFileMethods );
+ Result.fMethods.fRead := ReadFileStreamWithEvent;
+ Result.fMethods.fWrite := WriteFileStreamWithEvent; // not WriteStreamEOF, Àëåêñåé Øóâàëîâ
+ Result.fMethods.fSetSiz := SetSizeFileStream;
+ Result.fData.fHandle := FileCreate( FileName, Options );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewReadFileStream( const FileName: KOLString ): PStream;
+begin
+ Result := _NewStream( BaseFileMethods );
+ Result.fMethods.fRead := ReadFileStreamProc;
+ Result.fData.fHandle := FileCreate( FileName,
+ ofOpenRead or ofShareDenyWrite or ofOpenExisting );
+end;
+{$ENDIF PAS_VERSION}
+
+function NewReadFileStreamWithEvent( const FileName: KOLString ): PStream;
+begin
+ Result := _NewStream( BaseFileMethods );
+ Result.fMethods.fRead := ReadFileStreamWithEvent;
+ Result.fData.fHandle := FileCreate( FileName,
+ ofOpenRead or ofShareDenyWrite or ofOpenExisting );
+end;
+
+function NewExFileStream( F: HFile ): PStream;
+begin
+ Result := _NewStream( BaseFileMethods );
+ Result.fMethods.fRead := ReadFileStreamProc;
+ Result.fMethods.fWrite := WriteFileStream;
+ Result.fData.fHandle := F;
+ Result.fMethods.fClose := DummyCloseStream;
+end;
+
+{$IFDEF _D3orHigher}
+function NewReadFileStreamW( const FileName: KOLWideString ): PStream;
+begin
+ Result := _NewStream( BaseFileMethods );
+ Result.fMethods.fRead := ReadFileStreamProc;
+ Result.fData.fHandle := WFileCreate( FileName,
+ ofOpenRead or ofShareDenyWrite or ofOpenExisting );
+end;
+{$ENDIF _D3orHigher}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewWriteFileStream( const FileName: KOLString ): PStream;
+begin
+ Result := _NewStream( BaseFileMethods );
+ Result.fMethods.fWrite := WriteFileStreamEOF;
+ Result.fMethods.fSetSiz := SetSizeFileStream;
+ Result.fData.fHandle := FileCreate( FileName,
+ ofOpenWrite or ofCreateAlways or ofShareDenyWrite );
+end;
+{$ENDIF PAS_VERSION}
+
+function NewWriteFileStreamWithEvent( const FileName: KOLString ): PStream;
+begin
+ Result := _NewStream( BaseFileMethods );
+ Result.fMethods.fWrite := WriteFileStreamEOFWithEvent;
+ Result.fMethods.fSetSiz := SetSizeFileStream;
+ Result.fData.fHandle := FileCreate( FileName,
+ ofOpenWrite or ofCreateAlways or ofShareDenyWrite );
+end;
+
+{$IFDEF _D3orHigher}
+function NewWriteFileStreamW( const FileName: KOLWideString ): PStream;
+begin
+ Result := _NewStream( BaseFileMethods );
+ Result.fMethods.fWrite := WriteFileStreamEOF;
+ Result.fMethods.fSetSiz := SetSizeFileStream;
+ Result.fData.fHandle := WFileCreate( FileName,
+ ofOpenWrite or ofCreateAlways or ofShareDenyWrite );
+end;
+{$ENDIF _D3orHigher}
+
+{$IFDEF ASM_noVERSION}
+function NewReadWriteFileStream( const FileName: AnsiString ): PStream;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ MOV EAX, offset[BaseFileMethods]
+ CALL _NewStream
+ MOV EDX, [ReadFileStreamProc]
+ MOV [EAX].TStream.fMethods.fRead, EDX
+ MOV [EAX].TStream.fMethods.fWrite, offset[WriteFileStream]
+ MOV [EAX].TStream.fMethods.fSetSiz, offset[SetSizeFileStream]
+ XCHG EBX, EAX
+
+ PUSH EAX
+ CALL FileExists
+ MOV EDX, ofOpenReadWrite or ofCreateAlways or ofShareDenyWrite
+ ADD DH, AL // $200 (ofCreateAlways) -> $300 (ofCreateExisting)
+ POP EAX
+
+ CALL FileCreate
+ MOV [EBX].TStream.fData.fHandle, EAX
+ XCHG EAX, EBX
+ POP EBX
+end;
+{$ELSE PAS_VERSION} //Pascal
+function NewReadWriteFileStream( const FileName: KOLString ): PStream;
+var Creation: DWORD;
+begin
+ Result := _NewStream( BaseFileMethods );
+ Result.fMethods.fRead := ReadFileStreamProc;
+ Result.fMethods.fWrite := WriteFileStream;
+ Result.fMethods.fSetSiz := SetSizeFileStream;
+ Creation := ofCreateAlways;
+ if FileExists( FileName ) then Creation := ofOpenExisting;
+ Result.fData.fHandle := FileCreate( FileName,
+ ofOpenReadWrite or Creation or ofShareDenyWrite );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF _D3orHigher}
+function NewReadWriteFileStreamW( const FileName: KOLWideString ): PStream;
+var Creation: DWORD;
+begin
+ Result := _NewStream( BaseFileMethods );
+ Result.fMethods.fRead := ReadFileStreamProc;
+ Result.fMethods.fWrite := WriteFileStream;
+ Result.fMethods.fSetSiz := SetSizeFileStream;
+ Creation := ofCreateAlways;
+ if WFileExists( FileName ) then Creation := ofOpenExisting;
+ Result.fData.fHandle := WFileCreate( FileName,
+ ofOpenReadWrite or Creation or ofShareDenyWrite );
+end;
+{$ENDIF _D3orHigher}
+
+function NewMemoryStream: PStream;
+begin
+ Result := _NewStream( MemoryMethods );
+end;
+
+function NewMemoryStreamWithEvent: PStream;
+begin
+ Result := _NewStream( MemoryMethods );
+ Result.fMethods.fRead := ReadMemStreamWithEvent;
+ Result.fMethods.fWrite := WriteMemStreamWithEvent;
+end;
+
+{$IFDEF ASM_STREAM}
+function WriteExMemoryStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ MOV EAX, [EBX].TStream.fData.fSize
+ SUB EAX, [EBX].TStream.fData.fPosition
+ CMP EAX, ECX
+ JGE @@1
+ XCHG ECX, EAX
+@@1:
+ PUSH EDX
+ PUSH ECX
+ JLE @@count_ok
+ XCHG EDX, EAX
+ MOV EAX, EBX
+ CALL TStream.SetSize
+@@count_ok:
+ POP ECX
+ POP EAX
+ MOV EDX, [EBX].TStream.fMemory
+ ADD EDX, [EBX].TStream.fData.fPosition
+ PUSH ECX
+ CALL System.Move
+ POP EAX
+ ADD [EBX].TStream.fData.fPosition, EAX
+ POP EBX
+end;
+{$ELSE PAS_VERSION}
+function WriteExMemoryStream( Strm: PStream; var Buffer; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+var S: PStream;
+ C: TStrmSize;
+begin
+ S := Strm;
+ C := Count;
+ if C + S.fData.fPosition > S.fData.fSize then
+ C := S.fData.fSize - S.fData.fPosition;
+ Result := C;
+ Move( Buffer, Pointer( DWORD(S.fMemory) + S.fData.fPosition )^, Result );
+ Inc( S.fData.fPosition, Result );
+end;
+{$ENDIF PAS_VERSION}
+
+procedure DummyClose_ExMemStream( Strm: PStream );
+begin
+ // nothing to do - ignore call (memory is not released by any way)
+end;
+
+function NewExMemoryStream( ExistingMem: Pointer; Size: DWORD ): PStream;
+begin
+ Result := NewMemoryStream;
+ Result.fMemory := ExistingMem;
+ Result.fData.fCapacity := Size;
+ Result.fData.fSize := Size;
+ Result.fMethods.fWrite := WriteExMemoryStream;
+ Result.fMethods.fSetSiz := DummySetSize;
+ Result.fMethods.fClose := DummyClose_ExMemStream;
+end;
+
+function NewMemBlkStream( BlkSize: Integer ): PStream;
+begin
+ Result := NewMemoryStream;
+ Result.fData.fBlkSize := BlkSize;
+ Result.fData.fBlocks := NewList;
+ Result.fMethods.fWrite := WriteMemBlkStream;
+ Result.fMethods.fSetSiz := DummySetSize;
+ Result.fMethods.fClose := DummyClose_ExMemStream;
+ Result.fMethods.fRead := ReadMemBlkStream;
+ Result.fMethods.fSeek := SeekMemBlkStream;
+ Result.fMethods.fSetSiz := ResizeMemBlkStream;
+ Result.Add2AutoFreeEx( TObjectMethod( MakeMethod( Result, @FreeMemBlkStream ) ) );
+end;
+
+function NewMemBlkStream_WriteOnly( BlkSize: Integer ): PStream;
+begin
+ Result := NewMemoryStream;
+ Result.fData.fBlkSize := BlkSize;
+ Result.fData.fBlocks := NewList;
+ Result.fMethods.fWrite := WriteMemBlkStream;
+ Result.fMethods.fSetSiz := DummySetSize;
+ Result.fMethods.fClose := DummyClose_ExMemStream;
+ Result.fMethods.fRead := DummyReadWrite;
+ Result.fMethods.fSeek := DummySeek;
+ Result.fMethods.fSetSiz := ResizeMemBlkStream;
+ Result.Add2AutoFreeEx( TObjectMethod( MakeMethod( Result, @FreeMemBlkStream ) ) );
+end;
+
+function NewConcatStream( Stream1, Stream2: PStream ): PStream;
+begin
+ Result := _NewStream( ConcatStreamMethods );
+ Result.fData.fStream1 := Stream1;
+ Result.fData.fStream2 := Stream2;
+ Result.Add2AutoFree( Stream1 );
+ Result.Add2AutoFree( Stream2 );
+end;
+
+function NewSubStream( BaseStream: PStream; const FromPos, Size: TStrmSize ): PStream;
+begin
+ Result := _NewStream( SubStreamMethods );
+ Result.fData.fBaseStream := BaseStream;
+ Result.fData.fFromPos := FromPos;
+ Result.fData.fSize := Size;
+ Result.Position := 0;
+ Result.Add2AutoFree( BaseStream );
+end;
+
+function Stream2Stream( Dst, Src: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+var Buf: Pointer;
+ C: TStrmSize;
+begin
+ C := Count;
+ if Src.fMemory <> nil then
+ begin
+ if Src.fData.fPosition + C > Src.fData.fSize then
+ C := Src.fData.fSize - Src.fData.fPosition;
+ Result := Dst.Write( Pointer(DWORD(Src.fMemory)+Src.fData.fPosition)^,
+ C );
+ Inc( Src.fData.fPosition, Result );
+ end else
+ if Dst.fMemory <> nil then
+ begin
+ if Dst.fData.fPosition + C > Dst.fData.fSize then
+ Dst.SetSize( Dst.fData.fPosition + C );
+ Result := Src.Read( Pointer( DWORD( Dst.fMemory ) + Dst.fData.fPosition )^,
+ C );
+ Inc( Dst.fData.fPosition, Result );
+ end else
+ begin
+ GetMem( Buf, C );
+ C := Src.Read( Buf^, C );
+ Result := Dst.Write( Buf^, C );
+ FreeMem( Buf );
+ end;
+end;
+
+function Stream2StreamEx( Dst, Src: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize ): TStrmSize;
+begin
+ Result := Stream2StreamExBufSz( Dst, Src, Count, 65536 );
+end;
+
+function Stream2StreamExBufSz( Dst, Src: PStream; {$IFNDEF STREAM_COMPAT} const {$ENDIF} Count: TStrmSize; BufSz: DWORD ): TStrmSize;
+var
+ buf:pointer;
+ rd, wr:dword;
+ C: TStrmSize;
+begin
+ C := Count;
+ if C=0 then result:=0
+ else begin
+ result:=0;
+ BufSz := Min( BufSz, C );
+ if BufSz = 0 then BufSz := C;
+ getmem(buf,BufSz);
+ repeat
+ if C<BufSz then rd:=c else rd:=BufSz;
+ rd:=src.read(buf^,rd);
+ wr := dst.write(buf^,rd);
+ inc(result,wr);
+ dec(C, rd);
+ until (rd<>BufSz) or (C=0);
+ freemem(buf);
+ end;
+end;
+
+{$IFDEF ASM_UNICODE}
+ {$IFNDEF STREAM_LARGE64}
+ {$DEFINE ASM_Resource2Stream}
+ {$ENDIF}
+{$ENDIF}
+
+{$IFDEF ASM_Resource2Stream}
+function Resource2Stream( DestStrm : PStream; Inst : HInst;
+ ResName : PAnsiChar; ResType : PAnsiChar ): Integer;
+asm
+ PUSH EBX
+ PUSH ESI
+ MOV EBX, EDX // EBX = Inst
+ PUSH EAX // DestStrm
+ PUSH ResType
+ PUSH ECX
+ PUSH EDX
+ CALL FindResource
+ TEST EAX, EAX
+ JZ @@exit0
+
+ PUSH EAX
+ PUSH EBX
+ PUSH EAX
+ PUSH EBX
+ CALL SizeofResource
+ XCHG EBX, EAX
+ CALL LoadResource
+ TEST EAX, EAX
+ JZ @@exit0
+ XCHG ESI, EAX
+
+ PUSH ESI
+ CALL GlobalLock
+ TEST EAX, EAX
+ JNZ @@P_ok
+
+ CALL GetLastError
+ CMP EAX, ERROR_INVALID_HANDLE
+ JNZ @@exit_00
+ MOV EAX, ESI
+
+@@P_ok:
+ XCHG EDX, EAX
+ POP EAX // DestStrm
+ PUSH EDX
+ MOV ECX, EBX
+ CALL TStream.Write
+
+ //EAX = Result (length of written data)
+ XCHG EBX, EAX
+ POP EAX
+ CMP ESI, EAX
+ JE @@not_unlock
+
+ PUSH ESI
+ CALL GlobalUnlock
+@@not_unlock:
+ XCHG EAX, EBX
+ JMP @@exit
+
+@@exit_00:
+ XOR EAX, EAX
+@@exit0:
+ POP ECX
+@@exit:
+ POP ESI
+ POP EBX
+end;
+{$ELSE PAS_VERSION} //Pascal
+function Resource2Stream( DestStrm : PStream; Inst : HInst;
+ ResName : PKOLChar; ResType : PKOLChar ): Integer;
+var R : HRSRC;
+ G : HGlobal;
+ P : PAnsiChar;
+ Sz : DWORD;
+ E : Integer;
+begin
+ Result := 0;
+ R := FindResource( Inst, ResName, ResType );
+ if R <> 0 then
+ begin
+ Sz := SizeofResource( Inst, R );
+ G := LoadResource( Inst, R );
+ if G <> 0 then
+ begin
+ P := GlobalLock( G );
+ if P = nil then
+ begin
+ E := GetLastError;
+ if E = ERROR_INVALID_HANDLE then
+ P := Pointer( G )
+ else Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Result := DestStrm.Write( P^, Sz );
+ if P <> Pointer( G ) then
+ GlobalUnlock( G );
+ //FreeResource( G ); -- not necessary for resource loaded by LoadResource
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+///////////////////////////////////////////////////////////////////////////
+// I N I - F I L E S
+///////////////////////////////////////////////////////////////////////////
+
+{ TIniFile }
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+destructor TIniFile.Destroy;
+begin
+ fFileName := '';
+ fSection := '';
+ inherited;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TIniFile.ClearAll;
+begin
+ WritePrivateProfileString( nil, nil, nil,
+ PKOLChar( fFileName ) );
+end;
+
+procedure TIniFile.ClearKey(const Key: KOLString);
+begin
+ WritePrivateProfileString( PKOLChar( fSection ), PKOLChar( Key ), nil,
+ PKOLChar( fFileName ) );
+end;
+
+procedure TIniFile.ClearSection;
+begin
+ WritePrivateProfileString( PKOLChar( fSection ), nil, nil,
+ PKOLChar( fFileName ) );
+end;
+
+function TIniFile.ValueBoolean(const Key: KOLString; Value: Boolean): Boolean;
+var sec: PKOLChar;
+begin
+ sec := PKOLChar( fSection );
+ if fSection = '' then
+ sec := nil;
+ if fMode = ifmRead then
+ Result := GetPrivateProfileInt( sec, PKOLChar( Key ),
+ Integer( Value ), PKOLChar( fFileName ) ) <> 0
+ else
+ begin
+ WritePrivateProfileString( sec, PKOLChar( Key ),
+ PKOLChar( KOLString( Int2Str( Integer( Value ) ) ) ),
+ PKOLChar( fFileName ) );
+ Result := Value;
+ end;
+end;
+
+function TIniFile.ValueData(const Key: KOLString; Value: Pointer;
+ Count: Integer): Boolean;
+begin
+ if fMode = ifmRead then
+ Result := GetPrivateProfileStruct( PKOLChar( fSection ), PKOLChar( Key ),
+ Value, Count, PKOLChar( fFileName ) )
+ else Result := WritePrivateProfileStruct( PKOLChar( fSection ), PKOLChar( Key ),
+ Value, Count, PKOLChar( fFileName ) );
+end;
+
+function TIniFile.ValueInteger(const Key: KOLString; Value: Integer): Integer;
+begin
+ if fMode = ifmRead then
+ Result := GetPrivateProfileInt( PKOLChar( fSection ), PKOLChar( Key ),
+ Integer( Value ), PKOLChar( fFileName ) )
+ else
+ begin
+ Result := Value;
+ WritePrivateProfileString( PKOLChar( fSection ), PKOLChar( Key ),
+ PKOLChar( KOLString( Int2Str( Value ) ) ), PKOLChar( fFileName ) );
+ end;
+end;
+
+function TIniFile.ValueString(const Key, Value: KOLString): KOLString;
+var
+ Buffer: array[0..4095] of KOLChar;
+begin
+ if fMode = ifmRead then
+ begin
+ Buffer[ 0 ] := #0;
+ if GetPrivateProfileString(PKOLChar(fSection),
+ PKOLChar(Key), PKOLChar(Value), Buffer, SizeOf(Buffer) div Sizeof(KOLChar),
+ PKOLChar(fFileName)) <> 0 then
+ Result := Buffer
+ else Result := ''; //: FPC âûäàåò îøèáêó ïðè îòñóòñòâèè Key â INI-ôàéëå // MTsv DN
+ end else
+ begin
+ Result := Value;
+ WritePrivateProfileString( PKOLChar( fSection ), PKOLChar( Key ),
+ PKOLChar( Value ), PKOLChar( fFileName ) );
+ end;
+end;
+
+function TIniFile.ValueDouble(const Key: KOLString; const Value: Double): Double;
+begin
+ Result := Str2Double( ValueString( Key, Double2Str( Value ) ) );
+end;
+
+function OpenIniFile( const FileName: KOLString ): PIniFile;
+begin
+ New( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TIniFile';
+ {$ENDIF}
+ Result.fFileName := FileName;
+end;
+
+/////////////////////////////////////////////////// GetSectionNames, SectionData
+// - by Vyacheslav A. Gavrik :
+
+const
+ IniBufferSize = 32767;
+ IniBufferStrSize = IniBufferSize+4; /// äëÿ ìàõèíàöèé :)
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+procedure TIniFile.GetSectionNames(Names:PKOLStrList);
+var
+ i:integer;
+ Pc:PKOLChar;
+ PcEnd:PKOLChar;
+ Buffer:Pointer;
+begin
+ GetMem(Buffer,IniBufferSize * Sizeof( KOLChar ));
+ Pc:=Buffer;
+ i := GetPrivateProfileSectionNames(Buffer, IniBufferSize, PKOLChar(fFileName));
+ PcEnd:=Pc+i;
+ repeat
+ Names.Add(Pc);
+ Pc:=PC+Length(PC)+1;
+ until PC>=PcEnd;
+ FreeMem(Buffer);
+end;
+
+procedure TIniFile.SectionData(Names: PKOLStrList);
+var
+ i:integer;
+ Pc:PKOLChar;
+ PcEnd:PKOLChar;
+ Buffer:Pointer;
+begin
+ GetMem(Buffer,IniBufferSize * Sizeof(KOLChar));
+ Pc:=Buffer;
+ if fMode = ifmRead then
+ begin
+ i:=GetPrivateProfileSection(PKOLChar(fSection), Buffer, IniBufferSize, PKOLChar(fFileName));
+ PcEnd:=Pc+i;
+ while PC < PcEnd do // Chg by ECM from REPEAT-UNTIL: i=0 (empty section) => Names.Count=1
+ begin
+ Names.Add(Pc);
+ Pc:=PC+Length(PC)+1;
+ end;
+ end else
+ begin
+ for i:= 0 to Names.Count-1 do
+ begin
+ {$IFDEF UNICODE_CTRLS} WStrCopy {$ELSE} StrCopy {$ENDIF}
+ (Pc,Names.ItemPtrs[i]);
+ Pc:=PC+Length(PC)+1;
+ end;
+ Pc[0]:=#0;
+ ClearSection;
+ WritePrivateProfileSection(PKOLChar(fSection), Buffer, PKOLChar(fFileName));
+
+ end;
+ FreeMem(Buffer);
+end;
+{$ENDIF PAS_VERSION}
+
+/////////////////////////////////////////////////////////////////////////
+// M E N U
+/////////////////////////////////////////////////////////////////////////
+
+{ -- Menu implementation -- }
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function MakeAccelerator( fVirt: Byte; Key: Word ): TMenuAccelerator;
+begin
+ Result.fVirt := fVirt;
+ Result.Key := Key;
+end;
+{$ENDIF PAS_VERSION}
+
+function GetAcceleratorText( const Accelerator: TMenuAccelerator ): KOLString;
+var
+ KeyName: array[0..255] of KOLChar;
+
+ procedure AddKeyName( Code: Integer );
+ begin
+ Code := MapVirtualKey(Code, 0);
+ if Code = 0 then exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if GetKeyNameText(Code shl 16, KeyName, 256) > 0 then begin
+ if Result <> '' then
+ Result := Result + '+';
+ Result := Result + KOLString(KeyName);
+ end;
+ end;
+
+begin
+ Result := '';
+ with Accelerator do begin
+ if fVirt and FCONTROL <> 0 then
+ AddKeyName(VK_CONTROL);
+ if fVirt and FSHIFT <> 0 then
+ AddKeyName(VK_SHIFT);
+ if fVirt and FALT <> 0 then
+ AddKeyName(VK_ALT);
+ if fVirt and $20 <> 0 then
+ AddKeyName(VK_LWIN);
+ if fVirt and $40 <> 0 then
+ AddKeyName(VK_RWIN);
+
+ AddKeyName(Key);
+ end;
+end;
+
+const
+ MIDATA_CHECKITEM = $40000000;
+ MIDATA_RADIOITEM = $80000000;
+
+
+{$IFNDEF NEW_MENU_ACCELL}
+function WndProcMenu( Sender: PControl; var Msg: TMsg; var Rslt: Integer): Boolean;
+var M, M1: PMenu;
+ Idx: Integer;
+ Id: Integer;
+begin
+ Result := False;
+ if Msg.message = WM_COMMAND then
+ begin
+ if (Msg.lParam = 0) and (HIWORD( Msg.wParam ) <= 1) then
+ begin
+ M := PMenu( Sender.fMenuObj );
+ while (M = nil) and (Sender.Parent <> nil) do
+ begin
+ Sender := Sender.Parent;
+ M := PMenu( Sender.fMenuObj );
+ end;
+ while M <> nil do
+ begin
+ Id := LoWord( Msg.wParam );
+ M1 := M.Items[ Id ];
+ if M1 <> nil then
+ begin
+ Result := True;
+ Rslt := 0;
+ Idx := M.IndexOf( M1 );
+ M.fByAccel := HiWord( Msg.wParam ) <> 0;
+ if M1.FRadioGroup <> 0 then
+ M1.RadioCheckItem
+ else if M1.FIsCheckItem then
+ M1.Checked := not M1.Checked;
+ if Assigned(M1.FOnMenuItem) then
+ M1.FOnMenuItem( M, Idx )
+ else if Assigned( M.FOnMenuItem ) then
+ M.FOnMenuItem( M, Idx );
+ break;
+ end;
+ M := M.fNextMenu;
+ end;
+ end;
+ end;
+end;
+
+{$ELSE}
+
+function WndProcMenu( Sender: PControl; var Msg: TMsg; var Rslt: Integer): Boolean;
+
+ function ProcessMenuItem(M: PMenu; Id: Integer): Boolean;
+ var
+ M1: PMenu;
+ Idx: Integer;
+ begin
+ M1 := M.Items[ Id ];
+ Result := (M1 <> nil);
+ if Result then
+ begin
+ Idx := M.IndexOf( M1 );
+ M.fByAccel := HiWord( Msg.wParam ) <> 0;
+ if M1.FRadioGroup <> 0 then
+ M1.RadioCheckItem
+ else if M1.FIsCheckItem then
+ M1.Checked := not M1.Checked;
+ if Assigned(M1.FOnMenuItem) then
+ begin
+ {$IFDEF USE_MENU_CURCTL}
+ M.fCurCtl := Sender; // fixed
+ {$ENDIF}
+ M1.FOnMenuItem( M, Idx )
+ end else if Assigned( M.FOnMenuItem ) then
+ M.FOnMenuItem( M, Idx );
+ end;
+ end;
+
+var
+ M: PMenu;
+ Id: Integer;
+begin
+ Result := False;
+ if Msg.message = WM_COMMAND then
+ if (Msg.lParam = 0) and (HIWORD( Msg.wParam ) <= 1) then begin
+ Id := LoWord(Msg.wParam);
+ M := PMenu(Sender.fAutoPopupMenu);
+ if (M <> nil) and ProcessMenuItem(M, Id) then begin
+ Result := True;
+ Rslt := 0;
+ end else
+ begin
+ M := PMenu(Sender.fMenuObj);
+ while M <> nil do begin
+ if ProcessMenuItem(M, Id) then begin
+ Result := True;
+ Rslt := 0;
+ Break;
+ end;
+ M := M.fNextMenu;
+ end;
+ end;
+ end;
+end;
+{$ENDIF}
+
+{$ENDIF WIN_GDI}
+
+{$IFDEF GDI}
+function NewMenu( AParent : PControl; MaxCmdReserve : DWORD;
+ const Template : array of PKOLChar; aOnMenuItem: TOnMenuItem ): PMenu;
+var M: PMenu;
+ {$IFDEF INITIALFORMSIZE_FIXMENU}
+ R: TRect;
+ {$ENDIF}
+begin
+ New( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TMenu';
+ {$ENDIF}
+ Result.FVisible := TRUE;
+ Result.FPopupFlags := TPM_LEFTALIGN or TPM_LEFTBUTTON;
+ Result.FMenuItems := NewList;
+ Result.FOnMenuItem := aOnMenuItem;
+ if (High(Template)>=0) and (Template[0] <> nil) then
+ begin
+ if (AParent <> nil) and (AParent.fMenuObj = nil) and
+ {$IFDEF USE_FLAGS} not (G3_IsControl in AParent.fFlagsG3)
+ {$ELSE} not AParent.fIsControl {$ENDIF} then
+ Result.FHandle := CreateMenu
+ else Result.FHandle := CreatePopupMenu;
+ Result.FillMenuItems( Result.FHandle, 0, Template );
+ end;
+ if ( AParent <> nil ) then
+ begin
+ Result.FControl := AParent;
+ if AParent.fMenuObj <> nil then
+ begin
+ // add popup menu to the end of menu chain
+ M := PMenu( AParent.fMenuObj );
+ while M.fNextMenu <> nil do
+ M := M.fNextMenu;
+ M.fNextMenu := Result;
+ end else
+ begin
+ if {$IFDEF USE_FLAGS} not(G3_IsControl in AParent.fFlagsG3)
+ {$ELSE} not AParent.fIsControl {$ENDIF} then
+ begin
+ {$IFDEF INITIALFORMSIZE_FIXMENU}
+ R := AParent.ClientRect;
+ {$ENDIF}
+ AParent.Menu := Result.FHandle;
+ {$IFDEF INITIALFORMSIZE_FIXMENU}
+ AParent.SetClientSize( R.Right, R.Bottom );
+ {$ENDIF}
+ end;
+ AParent.fMenuObj := Result;
+ AParent.AttachProc( WndProcMenu );
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ AParent.Add2AutoFree( Result );
+ {$ENDIF}
+ end;
+ end;
+end;
+{$ENDIF GDI}
+
+{$IFDEF _X_}
+{$IFDEF GTK}
+//--- some code from samples - may be useful to see "how to"
+FUNCTION AddSeparatorToMenu( Menu : PGtkMenu ) : PgtkMenuItem ;
+BEGIN
+ Result := PGtkMenuitem( gtk_menu_item_new ) ;
+ gtk_menu_append( GTK_WIDGET( Menu ), PGtkWidget( Result ) ) ;
+ gtk_widget_show( PGtkWidget ( Result ) ) ;
+END;
+
+FUNCTION AddItemToMenu( Menu : PGtkMenu;
+ ShortCuts : PGtkAccelGroup;
+ const Caption : AnsiString;
+ const ShortCut : AnsiString;
+ CallBack : TGtkSignalFunc;
+ CallBackdata : Pointer ) : PGtkMenuItem;
+VAR
+ Key, Modifiers : DWORD;
+ //LocalAccelGroup : PGtkAccelGroup; -- not used since gtk_menu_ensure_uline_accel_group not defined anywhere...
+ TheLabel : PGtkLabel;
+BEGIN
+ Result := PGtkMenuItem ( gtk_menu_item_new_with_label( '' ) ) ;
+ TheLabel := GTK_LABEL(GTK_BIN( Result )^.child ) ;
+ Key:= gtk_label_parse_uline( TheLabel , Pchar ( Caption ) ) ;
+ //----------------
+ {If Key<>0 then // gtk_menu_ensure_uline_accel_group -- not defined anywhere...
+ begin
+ LocalAccelGroup := gtk_menu_ensure_uline_accel_group( Menu );
+ gtk_widget_add_accelerator( PGtkWidget ( Result ), 'activateitem',
+ LocalAccelGroup , Key ,
+ 0 , TGtkAccelFlags ( 0 ) ) ;
+ end;}
+ //-----------------
+ gtk_menu_append( GTK_WIDGET( Menu ), PGtkWidget( Result ) ) ;
+ //-----------------
+ IF ( ShortCut<>'' ) AND ( ShortCuts<> Nil ) THEN
+ BEGIN
+ gtk_accelerator_parse ( pchar( ShortCut ) , @key , @modifiers ) ;
+ gtk_widget_add_accelerator ( PGtkWidget ( Result ) , ' activateitem' ,
+ ShortCuts, Key, modifiers, GTK_ACCEL_VISIBLE );
+ END;
+ //------------------
+ IF Assigned( CallBack ) THEN
+ BEGIN
+ gtk_signal_connect( PGtkObject ( Result ) , 'activate' ,
+ CallBack , CallBackdata ) ;
+ gtk_widget_show( PgtkWidget ( Result ) ) ;
+ END;
+END;
+
+FUNCTION AddMenuToMenuBar( MenuBar : PGtkMenuBar;
+ ShortCuts : PGtkAccelGroup;
+ Caption : AnsiString;
+ CallBack : TGtkSignalFunc;
+ CallBackdata : Pointer;
+ AlignRight : Boolean;
+ Var MenuItem : PgtkMenuItem ) : PGtkMenu;
+VAR Key : DWORD;
+ TheLabel : PGtkLabel;
+BEGIN
+ MenuItem := PGtkMenuItem( gtk_menu_item_new_with_label( '' ) ) ;
+ IF AlignRight THEN
+ gtk_menu_item_right_justify( MenuItem );
+ TheLabel := GTK_LABEL( GTK_BIN( MenuItem )^ .child ) ;
+ Key := gtk_label_parse_uline( TheLabel, Pchar ( Caption ) ) ;
+ IF Key<>0 THEN
+ gtk_widget_add_accelerator( PGtkWidget( MenuItem ), 'activateitem',
+ Shortcuts, Key, GDK_MOD1_MASK, GTK_ACCEL_LOCKED );
+ Result := PGtkMenu( gtk_menu_new );
+ If Assigned( CallBack ) then
+ gtk_signal_connect( PGtkObject ( Result ), 'activate',
+ CallBack, CallBackdata ) ;
+ gtk_widget_show( PgtkWidget ( MenuItem ) ) ;
+ gtk_menu_item_set_submenu( MenuItem, PGtkWidget( Result ) ) ;
+ gtk_menu_bar_append( GTK_WIDGET( MenuBar ), PgtkWidget( MenuItem ) ) ;
+END;
+
+FUNCTION NewMenu( AParent : PControl; MaxCmdReserve : DWORD;
+ CONST Template : ARRAY of PKOLChar; aOnMenuItem: TOnMenuItem ): PMenu;
+ PROCEDURE CreateMenuItems( ParentMenu: PMenu; var i: Integer );
+ VAR Item, PrevItem: PMenu;
+ s: AnsiString;
+ j: Integer;
+ BEGIN
+ PrevItem := nil;
+ WHILE i <= High( Template )-1 DO
+ BEGIN
+ inc( i );
+ s := Template[ i ];
+ IF s = '' THEN BREAK; // end of template
+
+ IF s = ')' THEN
+ inc( i ); break; // end of submenu
+
+ new( Item, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Item.fObjKind := 'MenuItem';
+ {$ENDIF}
+ Item.FCaption := s;
+ Item.FVisible := TRUE;
+ Item.FParentMenu := ParentMenu;
+ if ParentMenu.FItems = nil then
+ ParentMenu.FItems := NewList;
+ ParentMenu.FItems.Add( Item );
+
+ IF (s <> '') AND ((s[ 1 ] = '+') or (s[ 1 ] = '-')) THEN
+ BEGIN
+ Item.fIsCheckItem := TRUE;
+ Item.fChecked := S[ 1 ] = '+';
+ s := CopyEnd( s, 2 );
+ IF (s <> '') and (s[ 1 ] = '!') THEN
+ BEGIN
+ IF PrevItem <> nil THEN
+ BEGIN
+ if PrevItem.fRadioGroup <> 0 THEN
+ Item.fRadioGroup := PrevItem.fRadioGroup;
+ END
+ ELSE inc( Item.fRadioGroup );
+ s := CopyEnd( s, 2 );
+ END;
+ END;
+
+ IF s = '-' THEN
+ Item.fIsSeparator := TRUE
+ ELSE
+ BEGIN
+ FOR j := Length( s )-1 DOWNTO 1 DO // extract mnemonic
+ BEGIN
+ IF (s[ j ] = '&') and (s[ j+1 ] <> '&') then // mnemonic
+ BEGIN
+ Item.fMnemonics := Item.fMnemonics + s[ j+1 ];
+ Delete( s, j, 1 );//? <U>m</U> ?
+ END;
+ END;
+ END;
+
+ //---------------------------- now call gtk for create item's widget
+ IF Item.FIsSeparator THEN
+ Item.fGtkMenuItem := gtk_menu_item_new
+ ELSE Item.fGtkMenuItem := gtk_menu_item_new_with_label( PAnsiChar( s ) );
+ IF ParentMenu.fGtkMenuBar <> nil THEN
+ gtk_menu_bar_append( ParentMenu.fGtkMenuBar, Item.fGtkMenuItem )
+ ELSE gtk_menu_shell_append(
+ GTK_MENU_SHELL( ParentMenu.fGtkMenuShell ), Item.fGtkMenuItem );
+
+ IF s = '(' THEN
+ BEGIN
+ inc( i );
+ IF PrevItem <> nil THEN
+ BEGIN
+ PrevItem.fGtkMenuShell := gtk_menu_new;
+ gtk_menu_item_set_submenu(
+ GTK_MENU_ITEM( PrevItem.fGtkMenuItem ),
+ PrevItem.fGtkMenuShell );
+ CreateMenuItems( PrevItem, i );
+ END;
+ END;
+
+ PrevItem := Item;
+ END;
+ END;
+VAR i: Integer;
+BEGIN
+ new( Result, Create );
+ i := -1;
+ IF AParent.fMenuObj = nil THEN
+ BEGIN // ñîçäàåòñÿ ãëàâíîå ìåíþ ñ ëèíåéêîé ìåíþ (íàâåðõó ôîðìû? ëþáîãî êîíòðîëà?)
+ AParent.fMenuObj := Result;
+ Result.fGtkMenuBar := gtk_menu_bar_new;
+ //AParent.fMenuBar := Result.fGtkMenuBar;
+ gtk_container_add( GTK_CONTAINER( AParent.fClient ), Result.fGtkMenuBar );
+ gtk_widget_show( Result.fGtkMenuBar );
+ END else
+ BEGIN
+ PMenu( AParent.fMenuObj ).fNextMenu := Result;
+ Result.fGtkMenuShell := gtk_menu_new;
+ END;
+ CreateMenuItems( Result, i );
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+function NewMenuEx( AParent : PControl; FirstCmd : Integer;
+ const Template : array of PKOLChar; aOnMenuItems: array of TOnMenuItem ): PMenu;
+begin
+ Result := NewMenu( AParent, FirstCmd, Template, nil );
+ {$IFDEF GDI}
+ Result.AssignEvents( 0, aOnMenuItems );
+ {$ENDIF GDI}
+end;
+
+{$IFDEF WIN_GDI}
+{ TMenu }
+
+const
+ Breaks: array[ TMenuBreak ] of DWORD = ( 0, MFT_MENUBREAK, MFT_MENUBARBREAK );
+
+{ + by AK - Andrzej Kubaszek }
+function MenuStructSize: Integer;
+begin
+ Result := 44;
+ if not( WinVer in [wv31, wv95, wvNT] ) then
+ Result := {48=} Sizeof( TMenuItemInfo );
+end;
+{$ENDIF WIN_GDI}
+
+{$IFDEF GDI}
+destructor TMenu.Destroy;
+var Next, Prnt: PMenu;
+begin
+ {$IFDEF DEBUG_MENU_DESTROY}
+ LogFileOutput( GetStartDir + 'TMenu.Destroy.txt',
+ Int2Hex( DWORD( @ Self ), 6 ) + ' ' + Int2Str( RefCount ) );
+ {$ENDIF}
+ if Count > 0 then
+ begin
+ FMenuItems.ReleaseObjects;
+ FMenuItems := NewList;
+ end;
+ if FParentMenu <> nil then
+ begin
+ Prnt := FParentMenu;
+ Next := Prnt.RemoveSubMenu( FId );
+ FParentMenu := nil;
+ Prnt.FMenuItems.Remove( @ Self );
+ if Next = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ if (FControl <> nil) and (FControl.fMenu = FHandle) and (FHandle <> 0) then
+ begin
+ if {$IFDEF USE_FLAGS} not (G2_Destroying in FControl.fFlagsG2)
+ {$ELSE} not FControl.fDestroying {$ENDIF} then //!!!fix by Galkov
+ begin
+ Windows.SetMenu( FControl.fHandle, 0 );
+ // this removes main menu from window, but does not destroy it
+ end;
+ FControl.fMenu := 0;
+ Next := PMenu( FControl.fMenuObj );
+ while Next <> nil do
+ begin
+ if Next.fNextMenu = @Self then
+ begin
+ Next.fNextMenu := fNextMenu;
+ break;
+ end;
+ Next := Next.fNextMenu;
+ end;
+ end;
+ Next := fNextMenu;
+ if FBitmap <> 0 then
+ Bitmap := 0;
+ if FHandle <> 0 then
+ begin
+ //if not
+ DestroyMenu( FHandle )
+ // then LogFileOutput( GetStartDir + 'err.log.txt', SysErrorMessage( GetLastError ) )
+ ;
+ end;
+ FCaption := '';
+ FMenuItems.Free;
+ Next.Free;
+ inherited;
+ // all later created (popup) menus (of the same control)
+ // are destroyed too
+end;
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+DESTRUCTOR TMenu.Destroy;
+//var Next, Prnt: PMenu;
+BEGIN
+ {$IFDEF DEBUG_MENU_DESTROY}
+ LogFileOutput( GetStartDir + 'TMenu.Destroy.txt',
+ Int2Hex( DWORD( @ Self ), 6 ) + ' ' + Int2Str( RefCount ) );
+ {$ENDIF}
+ //if Count > 0 then
+ IF ( fMenuItems <> nil ) THEN
+ BEGIN
+ FMenuItems.ReleaseObjects;
+ FMenuItems := NewList;
+ END;
+ FCaption := '';
+ fMnemonics := '';
+ FMenuItems.Free;
+ INHERITED;
+ // all later created (popup) menus (of the same control)
+ // are destroyed too
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF WIN_GDI}
+function TMenu.GetInfo( var MII: TMenuItemInfo ): Boolean;
+begin
+ MII.cbSize := MenuStructSize;
+ Result := GetMenuItemInfo( Parent.FHandle, FId, FALSE,
+ Windows.PMenuitemInfo( @ MII )^ );
+end;
+
+procedure TMenu.RedrawFormMenuBar;
+var C: PControl;
+begin
+ C := TopParent.FControl;
+ if not AppletTerminated then
+ if (C <> nil) and (Pointer( C.fMenuObj ) = Pointer( TopParent )) then
+ DrawMenuBar( C.FHandle );
+end;
+
+function TMenu.SetInfo( var MII: TMenuItemInfo ): Boolean;
+var H: THandle;
+begin
+ MII.cbSize := MenuStructSize;
+ H := FHandle;
+ if FParentMenu <> nil then
+ H := FParentMenu.FHandle;
+ {$IFNDEF UNICODE_CTRLS}
+ Result := SetMenuItemInfo( H, FId, FALSE, Windows.PMenuitemInfo( @ MII )^ );
+ {$ELSE}
+ Result := SetMenuItemInfoW( H, FId, FALSE, Windows.PMenuitemInfoW( @ MII )^ );
+ {$ENDIF}
+ if Result and ((FParentMenu = nil) or (FParentMenu.FParentMenu = nil)) then {YS}
+ RedrawFormMenuBar;
+end;
+
+function TMenu.SetTypeInfo( var MII: TMenuItemInfo ): Boolean;
+begin
+ if not FIsSeparator then
+ begin
+ if FBmpItem = 0 then
+ MII.dwTypeData := PKOLChar( FCaption )
+ else MII.dwTypeData := Pointer( FBmpItem );
+ MII.cch := Length( FCaption )*SizeOfKOLChar;
+ end;
+ Result := SetInfo( MII );
+end;
+
+function TMenu.GetTopParent: PMenu;
+begin
+ Result := @ Self;
+ while Result.FParentMenu <> nil do
+ Result := Result.FParentMenu;
+end;
+
+function TMenu.GetControl: PControl;
+begin
+ Result := TopParent.FControl;
+end;
+
+function TMenu.GetItems( Id: HMenu ): PMenu;
+ function SearchItems( ParentMenu: PMenu; var FromIdx: Integer ): PMenu;
+ var I: Integer;
+ begin
+ Result := ParentMenu;
+ if Id = HMenu( FromIdx ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if (Id >= 4096) and (DWORD( ParentMenu.FId ) = Id) then Exit; {>>>>>>>>>>>>}
+ if ParentMenu.FMenuItems = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ for I := 0 to ParentMenu.FMenuItems.FCount-1 do
+ begin
+ Inc( FromIdx );
+ Result := SearchItems( ParentMenu.FMenuItems.Items[ I ], FromIdx );
+ if Result <> nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Result := nil;
+ end;
+var I: Integer;
+begin
+ I := -1;
+ Result := SearchItems( @ Self, I );
+end;
+
+function TMenu.GetCount: Integer;
+var I: Integer;
+ SubM: PMenu;
+begin
+ Result := FMenuItems.FCount;
+ for I := 0 to Result-1 do
+ begin
+ SubM := FMenuItems.Items[ I ];
+ Result := Result + SubM.Count;
+ end;
+end;
+
+function TMenu.IndexOf( Item: PMenu ): Integer;
+ function SearchMenu( ParentMenu: PMenu; var FromIdx: Integer ): PMenu;
+ var I: Integer;
+ begin
+ Result := ParentMenu;
+ if Result = Item then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ for I := 0 to ParentMenu.FMenuItems.FCount-1 do
+ begin
+ Inc( FromIdx );
+ Result := SearchMenu( ParentMenu.FMenuItems.Items[ I ], FromIdx );
+ if Result <> nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Result := nil;
+ end;
+begin
+ Result := -1;
+ if SearchMenu( @ Self, Result ) = nil then
+ Result := -2;
+end;
+
+function TMenu.GetState( const Index: Integer ): Boolean;
+var MII: TMenuItemInfo;
+begin
+ if FVisible then
+ begin
+ MII.fMask := MIIM_STATE;
+ if GetInfo( MII ) then
+ FSavedState := MII.fState;
+ end;
+ Result := LongBool( FSavedState and Index );
+ if Index < 0 then
+ Result := not Result;
+end;
+
+procedure TMenu.SetState( const Index: Integer; Value: Boolean );
+var MII: TMenuItemInfo;
+begin
+ GetState( 0 );
+ if Value xor (Index < 0) then
+ FSavedState := FSavedState or DWORD( Index and $7FFFFFFF )
+ else FSavedState := FSavedState and not DWORD( Index );
+ if FVisible then
+ begin
+ MII.fMask := MIIM_STATE;
+ if GetInfo( MII ) then
+ begin
+ MII.fState := FSavedState;
+ SetInfo( MII );
+ end;
+ end;
+end;
+
+procedure TMenu.SetData( Value: Pointer );
+var MII: TMenuItemInfo;
+begin
+ MII.fMask := MIIM_DATA;
+ MII.dwItemData := DWORD( Value );
+ SetInfo( MII );
+ FData := Value;
+end;
+
+procedure TMenu.ClearBitmaps;
+begin
+ if FBitmap <> 0 then
+ DeleteObject( FBitmap );
+ if FBmpChecked <> 0 then
+ DeleteObject( FBmpChecked );
+ if FBmpItem <> 0 then
+ DeleteObject( FBmpItem );
+end;
+
+procedure TMenu.SetBitmap( Value: HBitmap );
+var MII: TMenuItemInfo;
+begin
+ if not FClearBitmaps then
+ begin
+ FClearBitmaps := TRUE;
+ Add2AutoFreeEx( ClearBitmaps );
+ end;
+ if Value = FBitmap then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if FBitmap <> 0 then
+ DeleteObject( FBitmap ); // seems not necessary.
+ FBitmap := Value;
+ MII.fMask := MIIM_CHECKMARKS;
+ MII.hbmpChecked := FBmpChecked;
+ MII.hbmpUnchecked := FBitmap;
+ SetInfo( MII );
+end;
+
+procedure TMenu.SetBmpChecked( Value: HBitmap );
+var MII: TMenuItemInfo;
+begin
+ if not FClearBitmaps then
+ begin
+ FClearBitmaps := TRUE;
+ Add2AutoFreeEx( ClearBitmaps );
+ end;
+ if Value = FBmpChecked then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if FBmpChecked <> 0 then
+ DeleteObject( FBmpChecked );
+ FBmpChecked := Value;
+ MII.fMask := MIIM_CHECKMARKS;
+ MII.hbmpChecked := FBmpChecked;
+ MII.hbmpUnchecked := FBitmap;
+ SetInfo( MII );
+end;
+
+procedure TMenu.SetBmpItem( Value: HBitmap );
+var MII: TMenuItemInfo;
+begin
+ if not FClearBitmaps then
+ begin
+ FClearBitmaps := TRUE;
+ Add2AutoFreeEx( ClearBitmaps );
+ end;
+ if Value = FBmpItem then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if FBmpItem <> 0 then
+ DeleteObject( FBmpItem );
+ FBmpItem := Value;
+ if WinVer >= wv98 then {AK}
+ begin {AK}
+ MII.fMask := $80 {MIIM_BITMAP} ; {AK}
+ MII.hbmpItem:=Value; {AK}
+ end else {AK}
+ begin//I haven't possibility to test it in Win95 {AK}
+ MII.fType := MFT_BITMAP;
+ MII.dwItemData := Value;
+ end; {AK}
+ SetInfo( MII );
+end;
+
+{$IFNDEF NEW_MENU_ACCELL}
+procedure TMenu.SetAccelerator(const Value: TMenuAccelerator);
+const MaxAccel = 1000;
+type TAccTab = array[0..10000] of TAccel;
+ PAccTab = ^TAccTab;
+var AccTab: PAccTab;
+ I, N : Integer;
+ M, SubM: PMenu;
+ C: PControl;
+ Main: Boolean;
+begin
+ if (FAccelerator.fVirt = Value.fVirt) and (FAccelerator.Key = Value.Key) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ FAccelerator := Value;
+ C := TopParent.FControl;
+ if C = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if C.fAccelTable <> 0 then
+ DestroyAcceleratorTable( C.fAccelTable );
+ C.fAccelTable := 0;
+ GetMem( AccTab, sizeof( TAccel ) * MaxAccel );
+ N := 0;
+ M := PMenu( C.fMenuObj );
+ Main := TRUE;
+ while M <> nil do
+ begin
+ if Main or M.Visible then
+ begin
+ for I := 0 to MaxInt-1 do
+ begin
+ SubM := M.Items[ I ];
+ if SubM = nil then break;
+ if SubM.FVisible then
+ if (SubM.FAccelerator.Key <> 0) or (SubM.FAccelerator.fVirt <> 0) then
+ begin
+ AccTab[ N ].fVirt := SubM.FAccelerator.fVirt;
+ AccTab[ N ].key := SubM.FAccelerator.Key;
+ AccTab[ N ].cmd := WORD( SubM.FId );
+ Inc( N );
+ if N > MaxAccel then break;
+ end;
+ end;
+ end;
+ if N > MaxAccel then break;
+ M := M.fNextMenu;
+ end;
+ if N > 0 then
+ begin
+ C.fAccelTable := CreateAcceleratorTable( AccTab[ 0 ], N );
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ C.Add2AutoFreeEx( C.DoDestroyAccelTable );
+ {$ENDIF}
+ C := C.ParentForm;
+ if C <> nil then
+ C.SupportMnemonics;
+ end;
+ FreeMem( AccTab );
+end;
+
+{$ELSE NEW_MENU_ACCELL}
+
+procedure TMenu.SetAccelerator(const Value: TMenuAccelerator);
+var C: PControl;
+ M: PMenu;
+begin
+ if (FAccelerator.fVirt = Value.fVirt) and (FAccelerator.Key = Value.Key) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ FAccelerator := Value;
+ C := FControl;
+ M := @Self;
+ while (C = nil) and (M <> nil) do begin
+ M := M.Parent;
+ if (M <> nil) then C := M.FControl;
+ end;
+ if C <> nil then C.SupportMnemonics;
+end;
+
+{$ENDIF NEW_MENU_ACCELL}
+
+procedure TMenu.SetMenuItemCaption( const Value: KOLString );
+var MII: TMenuItemInfo;
+begin
+ FCaption := Value;
+ if FParentMenu = nil then Exit; {+ecm} {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+{AK}if not (WinVer in [wv95,wvNT]) then
+{AK} MII.fMask := $40 {MIIM_STRING}
+{AK}else begin
+ MII.fMask := MIIM_TYPE;
+ MII.fType := MFT_STRING;
+{AK}end;
+ MII.cch := 0; // to fix turning radio mark to check mark in NT4
+ GetInfo( MII ); //-----------------------------------------------
+ MII.dwTypeData := PKOLChar( Value );
+ MII.cch := Length( Value )*SizeOfKOLChar;
+ SetInfo( MII );
+end;
+
+procedure TMenu.SetMenuBreak( Value: TMenuBreak );
+var MII: TMenuItemInfo;
+begin
+ if FId = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if FMenuBreak = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ FMenuBreak := Value;
+ //FillChar( MII, Sizeof( MII ), #0 );
+ ZeroMemory( @MII, Sizeof( MII ) );
+ MII.fMask := MIIM_TYPE;
+ MII.dwTypeData := nil;
+ if GetInfo( MII ) then
+ begin
+ MII.fType := MII.fType and not( MFT_MENUBREAK or MFT_MENUBARBREAK ) or
+ Breaks[ Value ];
+ SetTypeInfo( MII );
+ end;
+end;
+
+procedure TMenu.SetMenuVisible( Value: Boolean );
+var I, J: Integer;
+ M: PMenu;
+ Before: Integer;
+ ByPosition: Boolean;
+ MII: TMenuItemInfo;
+begin
+ if Value then
+ if FParentMenu <> nil then
+ FParentMenu.Visible := TRUE;
+ if Value = FVisible then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ FVisible := Value;
+ if (FControl <> nil) and (FControl.fMenuObj = @ Self) then
+ begin
+ FControl.GetWindowHandle;
+ if Value then
+ SetMenu( FControl.fHandle, FHandle )
+ else
+ SetMenu( FControl.fHandle, 0 );
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ if FId = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if FParentMenu = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if Value then
+ begin // show menu item inserting it again into appropriate position
+ Before := -1;
+ ByPosition := TRUE;
+ I := FParentMenu.FMenuItems.IndexOf( @ Self );
+ for J := I + 1 to FParentMenu.FMenuItems.FCount-1 do
+ begin
+ M := FParentMenu.FMenuItems.Items[ J ];
+ if M.FVisible then
+ begin
+ Before := M.FId;
+ ByPosition := FALSE;
+ break;
+ end;
+ end;
+ ZeroMemory( @MII, Sizeof( MII ) );
+ MII.cbSize := MenuStructSize;
+ MII.fMask := MII.fMask or
+ (MIIM_CHECKMARKS or MIIM_ID or MIIM_STATE or MIIM_TYPE);
+ MII.fType := Breaks[ FMenuBreak ];
+ MII.fState := FSavedState;
+ MII.wID := FId;
+ MII.dwItemData := DWORD( FData );
+ if not FIsSeparator then
+ begin
+ //MII.fType := MII.fType or MFT_STRING { = 0 };
+ MII.dwTypeData := PKOLChar( FCaption );
+ MII.cch := Length( FCaption )*SizeOfKOLChar;
+ end else
+ MII.fType := MII.fType or MFT_SEPARATOR;
+ if FRadioGroup <> 0 then
+ MII.fType := MII.fType or MFT_RADIOCHECK;
+ if FOwnerDraw then
+ MII.fType := MII.fType or MFT_OWNERDRAW;
+ if FBitmap <> 0 then
+ begin
+ MII.fMask := MII.fMask or MIIM_CHECKMARKS;
+ MII.hbmpUnchecked := FBitmap;
+ end;
+ if FHandle <> 0 then
+ begin
+ MII.fMask := MII.fMask or MIIM_SUBMENU;
+ MII.hSubMenu := FHandle;
+ end;
+ {$IFNDEF UNICODE_CTRLS}
+ InsertMenuItem( FParentMenu.FHandle, Before, ByPosition,
+ Windows.PMenuitemInfo( @ MII )^ );
+ {$ELSE}
+ InsertMenuItemW( FParentMenu.FHandle, Before, ByPosition,
+ Windows.PMenuitemInfoW( @ MII )^ );
+ {$ENDIF}
+ end else
+ begin // hide menu item removing it
+ GetState( 0 ); // store menu item state in FSavedState to allow
+ // changing its state while it is not attached to
+ // a menu
+ RemoveMenu( TopParent.FHandle, FId, MF_BYCOMMAND );
+ end;
+ if (FControl <> nil) or (FParentMenu <> nil) and (FParentMenu.FControl <> nil) then
+ RedrawFormMenuBar;
+end;
+
+procedure TMenu.RadioCheckItem;
+var I, J: Integer;
+ M, First, Last: PMenu;
+begin
+ if (FParentMenu <> nil) and (FRadioGroup <> 0) then
+ begin
+ I := FParentMenu.FMenuItems.IndexOf( @ Self );
+ if I >= 0 then
+ begin
+ First := @ Self;
+ Last := @ Self;
+ for J := I-1 downto 0 do
+ begin
+ M := FParentMenu.FMenuItems.Items[ J ];
+ if M.FRadioGroup <> FRadioGroup then break;
+ if M.FVisible then
+ First := M;
+ end;
+ for J := I+1 to FParentMenu.FMenuItems.FCount-1 do
+ begin
+ M := FParentMenu.FMenuItems.Items[ J ];
+ if M.FRadioGroup <> FRadioGroup then break;
+ if M.FVisible then
+ Last := M;
+ end;
+ if First <> Last then
+ begin
+ CheckMenuRadioItem( FParentMenu.FHandle, First.FId, Last.FId,
+ FId, MF_BYCOMMAND );
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ end;
+ Checked := TRUE;
+end;
+
+function TMenu.FillMenuItems(AHandle: HMenu; StartIdx: Integer;
+ const Template: array of PKOLChar): Integer;
+var S, S1: PKOLChar;
+ I: Integer;
+ MII: TMenuItemInfo;
+ Item, PrevItem: PMenu;
+begin
+ PrevItem := nil;
+ I := StartIdx;
+ while I <= High( Template ) do
+ begin
+ S := Template[ I ];
+ if (S = nil) or (S^ = #0) then break;
+ {$IFDEF UNICODE_CTRLS}
+ if KOLString( S ) = {$IFDEF F_P}'' +{$ENDIF} ')' then
+ {$ELSE}
+ if PWORD(S)^ = WORD(')') then
+ {$ENDIF}
+ begin
+ Result := I + 1; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+
+ new( Item, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Item.fObjKind := 'MenuItem';
+ {$ENDIF}
+ Item.FVisible := TRUE;
+ Item.FParentMenu := @ Self;
+ Item.FMenuItems := NewList;
+ FMenuItems.Add( Item );
+
+ ZeroMemory( @MII, Sizeof( MII ) );
+ MII.cbSize := MenuStructSize;
+ MII.fMask := MIIM_DATA or MIIM_ID or MIIM_STATE or MIIM_SUBMENU or MIIM_TYPE;
+ {$IFDEF UNICODE_CTRLS}
+ if KOLString( S ) <> {$IFDEF F_P}'' +{$ENDIF} '-' then
+ {$ELSE}
+ if PWORD(S)^ <> WORD('-') then
+ {$ENDIF}
+ begin
+ if (S^ = {$IFDEF F_P}'' +{$ENDIF} '-') or
+ (S^ = {$IFDEF F_P}'' +{$ENDIF} '+') then
+ begin
+ Item.FIsCheckItem := TRUE;
+ MII.dwItemData := MIDATA_CHECKITEM;
+ if S^ <> {$IFDEF F_P}'' +{$ENDIF} '-' then
+ MII.fState := MII.fState or MFS_CHECKED;
+ Inc( S );
+ if S^ = {$IFDEF F_P}'' +{$ENDIF} '!' then
+ begin
+ MII.fType := MII.fType or MFT_RADIOCHECK;
+ MII.dwItemData := MII.dwItemData or MIDATA_RADIOITEM;
+ Inc( S );
+ if PrevItem <> nil then
+ begin
+ if PrevItem.FRadioGroup <> 0 then
+ Item.FRadioGroup := PrevItem.FRadioGroup;
+ end;
+ if Item.FRadioGroup = 0 then
+ Inc( Item.FRadioGroup );
+ if S^ = {$IFDEF F_P}'' +{$ENDIF} '!' then
+ begin
+ Inc( S );
+ Inc( Item.FRadioGroup );
+ end;
+ end;
+ end;
+ Item.FCaption := S;
+ end
+ else
+ begin
+ Item.FIsSeparator := TRUE;
+ MII.fType := MFT_SEPARATOR;
+ MII.fState := MFS_GRAYED;
+ //MII.wID := 0;
+ end;
+ Item.FId := FDynamicMenuID;
+ Inc( FDynamicMenuID );
+ MII.wID := Item.FId;
+ if I <> High( Template ) then //YS
+ begin //YS
+ S1 := Template[ I + 1 ];
+ {$IFDEF UNICODE_CTRLS}
+ if KOLString( S1 ) = {$IFDEF F_P}'' +{$ENDIF} '(' then
+ {$ELSE}
+ if (S1 <> nil) and (PWORD(S1)^ = WORD('(')) then
+ {$ENDIF}
+ Item.FHandle := CreatePopupMenu;
+ end; //YS
+ MII.hSubMenu := Item.FHandle;
+ MII.dwTypeData := PKOLChar( S );
+ MII.cch := {$IFDEF UNICODE_CTRLS} WStrLen( S ) {$ELSE} StrLen( S ) {$ENDIF};
+ InsertMenuItem( AHandle, DWORD(-1), True, PMenuitemInfo( @ MII )^ );
+ if Item.FHandle <> 0 then
+ I := Item.FillMenuItems( Item.FHandle, I + 2, Template )
+ else
+ Inc( I );
+ PrevItem := Item;
+ end;
+ Result := I;
+end;
+
+procedure TMenu.AssignEvents(StartIdx: Integer;
+ const Events: array of TOnMenuItem);
+var I: Integer;
+ M: PMenu;
+begin
+ for I := 0 to High(Events) do
+ begin
+ M := Items[ StartIdx ];
+ if M = nil then break;
+ M.FOnMenuItem := Events[ I ];
+ Inc( StartIdx );
+ end;
+end;
+
+function TMenu.Popup(X, Y: Integer): Integer;
+begin
+ {$IFDEF GDI}
+ if Assigned( fOnPopup ) then fOnPopup( @Self );
+ if not FNotPopup then
+ Result := Integer( TrackPopupMenu( FHandle, FPopupFlags, {*ecm}
+ X, Y, 0, FControl.Handle, nil ) ) {*ecm}
+ else Result := 0; {*ecm}
+ {$ENDIF GDI}
+end;
+
+function TMenu.PopupEx( X, Y: Integer ): Integer;
+{$IFDEF GDI}
+var OldBounds: TRect;
+ WasVisible: Boolean;
+{$ENDIF GDI}
+begin
+ {$IFDEF GDI}
+ WasVisible := TRUE;
+ if FControl <> nil then
+ begin
+ OldBounds := FControl.BoundsRect;
+ if {$IFDEF USE_FLAGS} not(G3_IsControl in FControl.fFlagsG3)
+ {$ELSE} not FControl.fIsControl {$ENDIF} then
+ begin
+ WasVisible := FControl.Visible;
+ if not WasVisible then
+ FControl.Top := ScreenHeight + 50;
+ FControl.Show;
+ end;
+ end;
+ // -- by Martin Larsen: -----------------------
+ FControl.ProcessMessage; // specific for Win9x!
+ Result := Popup( X, Y ); {*ecm}
+ if FControl <> nil then
+ begin
+ if FControl.Top = ScreenHeight + 50 then
+ begin
+ if not WasVisible then
+ FControl.Visible := FALSE;
+ FControl.BoundsRect := OldBounds;
+ end;
+ end;
+ {$ENDIF GDI}
+end;
+
+function TMenu.GetItemChecked( Item : Integer ) : Boolean;
+begin
+ Result := Items[ Item ].Checked;
+end;
+
+procedure TMenu.SetItemChecked( Item : Integer; Value : Boolean );
+begin
+ Items[ Item ].Checked := Value;
+end;
+
+function TMenu.GetMenuItemHandle( Idx : Integer ): DWORD;
+begin
+ Result := Items[ Idx ].FId;
+end;
+
+procedure TMenu.RadioCheck( Idx : Integer );
+begin
+ Items[ Idx ].RadioCheckItem;
+end;
+
+function TMenu.GetItemBitmap(Idx: Integer): HBitmap;
+begin
+ Result := Items[ Idx ].Bitmap;
+end;
+
+procedure TMenu.SetItemBitmap(Idx: Integer; const Value: HBitmap);
+begin
+ Items[ Idx ].Bitmap := Value;
+end;
+
+procedure TMenu.AssignBitmaps(StartIdx: Integer; Bitmaps: array of HBitmap);
+var I: Integer;
+begin
+ for I := 0 to High(Bitmaps) do
+ ItemBitmap[ I + StartIdx ] := Bitmaps[ I ];
+end;
+
+function TMenu.GetItemText(Idx: Integer): KOLString;
+begin
+ Result := Items[ Idx ].FCaption;
+end;
+
+procedure TMenu.SetItemText(Idx: Integer; const Value: KOLString);
+begin
+ Items[ Idx ].Caption := Value;
+end;
+
+function TMenu.GetItemEnabled(Idx: Integer): Boolean;
+begin
+ Result := Items[ Idx ].Enabled;
+end;
+
+procedure TMenu.SetItemEnabled(Idx: Integer; const Value: Boolean);
+begin
+ Items[ Idx ].Enabled := Value;
+end;
+
+function TMenu.GetItemVisible(Idx: Integer): Boolean;
+begin
+ Result := Items[ Idx ].Visible;
+end;
+
+procedure TMenu.SetItemVisible(Idx: Integer; const Value: Boolean);
+begin
+ Items[ Idx ].Visible := Value;
+end;
+
+function TMenu.ParentItem( Idx: Integer ): Integer;
+begin
+ Result := TopParent.IndexOf( Items[ Idx ].FParentMenu );
+end;
+
+function TMenu.GetItemAccelerator(Idx: Integer): TMenuAccelerator;
+begin
+ Result := Items[ Idx ].Accelerator;
+end;
+
+procedure TMenu.SetItemAccelerator(Idx: Integer; const Value: TMenuAccelerator);
+begin
+ Items[ Idx ].Accelerator := Value;
+end;
+
+function TMenu.GetItemSubMenu( Idx: Integer ): HMenu;
+begin
+ Result := Items[ Idx ].SubMenu;
+end;
+
+function WndProcHelp( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+forward;
+
+{$IFDEF GDI}
+procedure TMenu.SetHelpContext( Value: Integer );
+var Form, C: PControl;
+begin
+ if TopParent <> @ Self then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ // Help context can not be associated with individual menu items
+ FHelpContext := Value;
+ C := FControl;
+ if C = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Form := C.ParentForm;
+ Form.AttachProc( WndProcHelp );
+ SetMenuContextHelpID( FHandle, Value );
+end;
+{$ENDIF GDI}
+
+procedure TMenu.SetSubmenu( Value: HMenu );
+var MII: TMenuItemInfo;
+begin
+ MII.fMask := MIIM_SUBMENU;
+ MII.hSubMenu := Value;
+ SetInfo( MII );
+ FHandle := Value;
+end;
+
+function WndProcMeasureItem( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var MIS: PMeasureItemStruct;
+ M, SM: PMenu;
+ H, I: Integer;
+begin
+ Result := FALSE;
+ if (Msg.message = WM_MEASUREITEM) and (Msg.wParam = 0) then
+ begin
+ MIS := Pointer( Msg.lParam );
+ if MIS.CtlType = ODT_MENU then
+ begin
+ M := Pointer( Sender.fMenuObj );
+ while M <> nil do
+ begin
+ SM := M.Items[ MIS.itemID ];
+ if SM <> nil then
+ begin
+ Sender.CallDefWndProc( Msg );
+ I := M.IndexOf( SM );
+ if Assigned( SM.OnMeasureItem ) then
+ M := SM;
+ if not Assigned( M.OnMeasureItem ) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ H := M.OnMeasureItem( M, I );
+ if HiWord( H ) <> 0 then
+ MIS.itemWidth := HiWord( H );
+ if LoWord( H ) <> 0 then
+ MIS.itemHeight := LoWord( H );
+ Rslt := 1;
+ Result := TRUE;
+ break;
+ end;
+ M := M.fNextMenu;
+ end;
+ end;
+ end;
+end;
+
+procedure TMenu.SetOnMeasureItem( const Value: TOnMeasureItem );
+var C: PControl;
+begin
+ FOnMeasureItem := Value;
+ C := TopParent.FControl;
+ if C <> nil then
+ C.AttachProc( WndProcMeasureItem );
+end;
+
+function WndProcDrawItem( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+type PDrawAction = ^TDrawAction;
+ PDrawState = ^TDrawState;
+var DIS: PDrawItemStruct;
+ M, SM: PMenu;
+ I: Integer;
+begin
+ Result := FALSE;
+ if (Msg.message = WM_DRAWITEM) and (Msg.wParam = 0) then
+ begin
+ DIS := Pointer( Msg.lParam );
+ if DIS.CtlType = ODT_MENU then
+ begin
+ M := Pointer( Sender.fMenuObj );
+ while M <> nil do
+ begin
+ SM := M.Items[ DIS.itemID ];
+ if SM <> nil then
+ begin
+ I := M.IndexOf( SM );
+ if Assigned( SM.OnDrawItem ) then
+ M := SM;
+ if Assigned( M.OnDrawItem ) then
+ begin
+ if not M.OnDrawItem( M, DIS.hDC, DIS.rcItem, I,
+ PDrawAction( @ DIS.itemAction )^,
+ PDrawState( @ DIS.itemState )^ ) then Exit; {>>>>>>>>}
+ end else Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Rslt := 1;
+ Result := TRUE;
+ break;
+ end;
+ M := M.fNextMenu;
+ end;
+ end;
+ end;
+end;
+
+procedure TMenu.SetOnDrawItem( const Value: TOnDrawItem );
+var C: PControl;
+begin
+ FOnDrawItem := Value;
+ C := TopParent.FControl;
+ if C <> nil then
+ C.AttachProc( WndProcDrawItem );
+end;
+
+procedure TMenu.SetOwnerDraw( Value: Boolean );
+const Masks: array[ Boolean ] of DWORD = ( 0, $FFFFFFFF );
+var MII: TMenuItemInfo;
+begin
+ FOwnerDraw := Value;
+ //FillChar( MII, Sizeof( MII ), #0 );
+ ZeroMemory( @MII, Sizeof( MII ) );
+ MII.fMask := MIIM_TYPE;
+ MII.dwTypeData := nil;
+ if GetInfo( MII ) then
+ begin
+ MII.fType := MII.fType and not MFT_OWNERDRAW or
+ (MFT_OWNERDRAW and Masks[ Value ]);
+ SetTypeInfo( MII );
+ end;
+end;
+
+function TMenu.Insert(InsertBefore: Integer; ACaption: PKOLChar; Event: TOnMenuItem;
+ Options: TMenuOptions): PMenu;
+const
+ MenuStateFlags: array[TMenuOption] of Integer = (MFS_DEFAULT, MFS_DISABLED, MFS_CHECKED, 0, 0,
+ MFS_DISABLED, 0, 0, 0, 0);
+ MenuTypeFlags: array[TMenuOption] of Integer = (0, 0, 0, 0, MFT_RADIOCHECK, MFT_SEPARATOR, MFT_BITMAP, 0,
+ MFT_MENUBREAK, MFT_MENUBARBREAK);
+var M: PMenu;
+ MII: TMenuItemInfo;
+begin
+ new( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TMenuItem';
+ {$ENDIF}
+ Result.FVisible := TRUE;
+ Result.FParentMenu := @ Self;
+ Result.FMenuItems := NewList;
+ Result.FIsSeparator := moSeparator in Options;
+ Result.FIsCheckItem := moCheckMark in Options; //+ by shilou, 12/2009
+ if FHandle = 0 then
+ SetSubMenu( CreatePopupMenu );
+ M := nil;
+ if (InsertBefore >= 0) and (InsertBefore < 4096) then
+ begin
+ M := Items[ InsertBefore ];
+ if M <> nil then
+ begin
+ InsertBefore := M.FId;
+ M.Parent.FMenuItems.Insert( M.Parent.FMenuItems.IndexOf( M ), Result );
+ end;
+ end;
+ if M = nil then
+ begin
+ InsertBefore := -1;
+ FMenuItems.Add( Result );
+ end;
+ Result.FOnMenuItem := Event;
+
+ //FillChar( MII, Sizeof( MII ), #0 );
+ ZeroMemory( @MII, Sizeof( MII ) );
+ MII.cbSize := MenuStructSize;
+ MII.fMask := MIIM_DATA or MIIM_ID or MIIM_STATE or MIIM_SUBMENU or MIIM_TYPE;
+
+ MII.fState := MakeFlags( Pointer( @Options ), MenuStateFlags);
+ MII.fType := MakeFlags( Pointer( @Options ), MenuTypeFlags);
+ Result.FId := FDynamicMenuID;
+ Inc( FDynamicMenuID );
+ MII.wID := Result.FId;
+ if moSubMenu in Options
+ then begin
+ Result.FHandle := CreatePopupMenu;
+ MII.hSubMenu := Result.FHandle;
+ end;
+ MII.dwTypeData := PKOLChar(ACaption);
+ {$IFNDEF UNICODE_CTRLS}
+ if not (moBitmap in Options) then MII.cch := StrLen( ACaption );
+ {$ELSE}
+ if not (moBitmap in Options) then MII.cch := WStrLen( ACaption );
+ {$ENDIF}
+ InsertMenuItem( FHandle, InsertBefore, InsertBefore = -1,
+ PMenuItemInfo( @ MII )^ );
+ if moBitmap in Options then
+ begin
+ Result.BitmapItem := DWORD( ACaption );
+ end
+ else
+ Result.FCaption := ACaption;
+ RedrawFormMenuBar;
+end;
+
+function TMenu.AddItem(ACaption: PKOLChar; Event: TOnMenuItem; Options: TMenuOptions): Integer;
+begin
+ Result := InsertItem( -1, ACaption, Event, Options );
+end;
+
+function TMenu.InsertItem( InsertBefore: Integer; ACaption: PKOLChar; Event: TOnMenuItem;
+ Options: TMenuOptions): Integer;
+begin
+ Result := InsertItemEx( InsertBefore, ACaption, Event, Options, FALSE );
+end;
+
+function TMenu.InsertItemEx(InsertBefore: Integer; ACaption: PKOLChar;
+ Event: TOnMenuItem; Options: TMenuOptions; ByPosition: Boolean): Integer;
+var M: PMenu;
+begin
+ M := Insert( InsertBefore, ACaption, Event, Options );
+ Result := M.FId;
+end;
+
+procedure TMenu.InsertSubMenu( SubMenuToInsert: PMenu; InsertBefore: Integer );
+var AFlags: DWORD;
+ M: PMenu;
+ MII: TMenuItemInfo;
+begin
+ if SubMenuToInsert.FParentMenu <> nil then
+ SubMenuToInsert := SubMenuToInsert.FParentMenu.RemoveSubMenu( SubMenuToInsert.FId );
+ if SubMenuToInsert = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+
+ AFlags := MF_BYPOSITION;
+ M := nil;
+ if (InsertBefore >= 0) and (InsertBefore < 4096) then
+ begin
+ M := Items[ InsertBefore ];
+ if M = nil then
+ InsertBefore := -1
+ else
+ InsertBefore := M.FId;
+ end;
+ if M = nil then
+ begin
+ FMenuItems.Add( SubMenuToInsert );
+ SubMenuToInsert.FParentMenu := @ Self;
+ end
+ else
+ begin
+ M.FParentMenu.FMenuItems.Insert( M.FParentMenu.FMenuItems.IndexOf( M ), SubMenuToInsert );
+ SubMenuToInsert.FParentMenu := M.FParentMenu;
+ end;
+
+ if InsertBefore > 0 then
+ AFlags := MF_BYCOMMAND;
+ if SubMenuToInsert.FBmpItem <> 0 then
+ InsertMenu( FHandle, InsertBefore, AFlags or MF_BITMAP or MF_POPUP,
+ SubMenuToInsert.FHandle, PKOLChar( SubMenuToInsert.FBmpItem ) )
+ else
+ InsertMenu( FHandle, InsertBefore, AFlags or MF_STRING or MF_POPUP,
+ SubMenuToInsert.FHandle, PKOLChar( SubMenuToInsert.Caption ) );
+ if SubMenuToInsert.FId = 0 then
+ begin
+ SubMenuToInsert.FId := FDynamicMenuID;
+ Inc( FDynamicMenuID );
+ MII.cbSize := MenuStructSize;
+ MII.fMask := MIIM_ID;
+ MII.wID := SubMenuToInsert.FId;
+ {$IFNDEF UNICODE_CTRLS}
+ SetMenuItemInfo( SubMenuToInsert.FParentMenu.FHandle,
+ SubMenuToInsert.FParentMenu.FMenuItems.IndexOf( SubMenuToInsert ),
+ TRUE, Windows.PMenuItemInfo( @ MII )^ );
+ {$ELSE}
+ SetMenuItemInfoW( SubMenuToInsert.FParentMenu.FHandle,
+ SubMenuToInsert.FParentMenu.FMenuItems.IndexOf( SubMenuToInsert ),
+ TRUE, Windows.PMenuItemInfoW( @ MII )^ );
+ {$ENDIF}
+ end;
+ RedrawFormMenuBar;
+end;
+
+function TMenu.RemoveSubMenu( ItemToRemove: Integer ): PMenu;
+{$IFDEF DEBUG_MENU}var OK: Boolean; {$ENDIF}
+var M: PMenu;
+begin
+ Result := Items[ ItemToRemove ];
+ if Result = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ M := Result.FParentMenu;
+ if M = nil then M := @Self;
+ {$IFDEF DEBUG_MENU} OK := {$ENDIF}
+ RemoveMenu( M.FHandle, Result.FId, MF_BYCOMMAND );
+ M.FMenuItems.Remove( Result );
+ {$IFDEF DEBUG_MENU}
+ if not OK then
+ ShowMessage( 'Error removing menu: ' + Int2Str( GetLastError ) + ' - ' +
+ SysErrorMessage( GetLastError ) );
+ {$ENDIF}
+ if Count = 0 then
+ begin
+ Result.Free;
+ Result := nil;
+ end;
+ RedrawFormMenuBar;
+end;
+
+function TMenu.GetItemHelpContext(Idx: Integer): Integer;
+begin
+ Result := Items[ Idx ].HelpContext;
+end;
+
+procedure TMenu.SetItemHelpContext(Idx: Integer; const Value: Integer);
+begin
+ Items[ Idx ].HelpContext := Value;
+end;
+
+procedure ClearText( Sender: PControl );
+begin
+ Sender.Caption := '';
+end;
+
+procedure ClearListbox( Sender: PControl );
+begin
+ Sender.Perform( LB_RESETCONTENT, 0, 0 );
+end;
+
+procedure ClearCombobox( Sender: PControl );
+begin
+ Sender.Perform( CB_RESETCONTENT, 0, 0 );
+end;
+
+procedure ClearListView( Sender: PControl );
+begin
+ Sender.Perform( LVM_DELETEALLITEMS, 0, 0 );
+end;
+
+procedure ClearToolbar( Sender: PControl );
+begin
+ while Sender.TBButtonCount > 0 do
+ Sender.TBDeleteButton( Sender.TBIndex2Item( 0 ) );
+ Sender.Perform( TB_SETBITMAPSIZE, 0, 0 );
+end;
+
+{$ENDIF WIN_GDI}
+{ -- Constructor of canvas -- }
+function NewCanvas( DC: HDC ): PCanvas;
+begin
+ New( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TCanvas';
+ {$ENDIF}
+ {$IFDEF GDI}
+ Result.ModeCopy := cmSrcCopy;
+ if DC <> 0 then
+ begin
+ Result.SetHandle( DC );
+ {//} Result.fIsAlienDC := True;
+ // When the Canvas will be destroyed, the DC will not be deleted
+ end;
+ {$ENDIF GDI}
+end;
+
+{ -- Contructors of controls -- }
+
+{$IFDEF GDI}
+{$IFDEF COMMANDACTIONS_OBJ}
+function NewCommandActionsObj: PCommandActionsObj;
+begin
+ new( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TCommandActionsObj';
+ {$ENDIF}
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+function NewCommandActionsObj_Packed( fromPack: PAnsiChar ): PCommandActionsObj;
+var Dest: PWord;
+ N, i: Integer;
+begin
+ new( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TCommandActionsObj';
+ {$ENDIF}
+ if Integer( fromPack ) < 120 then
+ begin
+ Result.fIndexInActions := Integer( fromPack ); Exit; {>>>>>>>>>>>>>>>>>}
+ end;
+ Result.fIndexInActions := Byte( fromPack^ );
+ inc( fromPack );
+ Dest := Pointer( @Result.aClick );
+ N := 38;
+ while N > 0 do
+ begin
+ if Byte( fromPack^ ) < 200 then
+ begin
+ Dest^ := PWord( fromPack )^;
+ inc( Dest );
+ inc( fromPack, 2 );
+ dec( N );
+ end
+ else
+ if Byte( fromPack^ ) = 200 then
+ begin
+ inc( fromPack );
+ Dest^ := PWord( fromPack )^;
+ inc( Dest );
+ inc( fromPack, 2 );
+ dec( N );
+ end
+ else
+ begin
+ i := Byte( fromPack^ ) - 200;
+ while i > 0 do
+ begin
+ Dest^ := 0;
+ inc( Dest );
+ dec( i );
+ dec( N );
+ end;
+ inc( fromPack );
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF COMMANDACTIONS_OBJ}
+
+function DumpWindowed( c: PControl ): PControl;
+var P: PByte;
+ i, j: Integer;
+ s, ss: KOLString;
+begin
+ P := Pointer( c );
+ ss := '';
+ i := 0;
+ while i < Sizeof( TControl ) do
+ begin
+ s := Int2Hex( i, 3 ) + ':';
+ for j := 0 to 15 do
+ begin
+ s := s + ' ' + Int2Hex( P^, 2 );
+ inc( P );
+ inc( i );
+ if i >= Sizeof( TControl ) then break;
+ end;
+ ss := ss + s + #13#10;
+ end;
+ LogFileOutput( GetStartDir + 'DumpWindowed.txt', Int2Hex( Integer( c ), 8 ) +
+ #13#10 + ss );
+ Result := c;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+function _NewWindowed( AParent: PControl; ControlClassName: PKOLChar;
+ Ctl3D: Boolean; ACommandActions: TCommandActionsParam ): PControl;
+{$IFDEF COMMANDACTIONS_OBJ}
+var IdxActions: Integer;
+{$ENDIF}
+begin
+ New( Result, CreateParented( AParent ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl';
+ {$ENDIF}
+ {$IFDEF COMMANDACTIONS_OBJ}
+ if Integer( ACommandActions ) < 120 then
+ IdxActions := Integer( ACommandActions )
+ else
+ IdxActions := PByte( ACommandActions )^;
+ if AllActions_Objs[IdxActions] <> nil then
+ begin
+ Result.fCommandActions := AllActions_Objs[IdxActions];
+ Result.fCommandActions.RefInc;
+ end
+ else
+ begin
+ {$IFDEF PACK_COMMANDACTIONS}
+ Result.fCommandActions := NewCommandActionsObj_Packed( ACommandActions );
+ AllActions_Objs[IdxActions] := Result.fCommandActions;
+ Result.fCommandActions.aClear := ClearText;
+ {$ELSE}
+ new( Result.fCommandActions, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fCommandActions.fObjKind := 'TCommandActionsObj';
+ {$ENDIF}
+ AllActions_Objs[IdxActions] := Result.fCommandActions;
+ if ACommandActions <> nil then
+ Move( ACommandActions^, Result.fCommandActions.aClear,
+ Sizeof( TCommandActions ) )
+ else
+ Result.fCommandActions.aClear := ClearText;
+ {$ENDIF}
+ end;
+ Result.Add2AutoFree( Result.fCommandActions );
+ {$ELSE}
+ if ACommandActions <> nil then
+ Result.fCommandActions := ACommandActions^
+ else
+ Result.fCommandActions.aClear := ClearText;
+ {$ENDIF}
+ //Result.fWindowed := TRUE; // is set in TControl.Init
+ Result.fControlClassName := ControlClassName;
+ if AParent <> nil then
+ begin
+ {$IFDEF WIN_GDI}
+ //{-2.95}Result.PP.fWndProcResizeFlicks := AParent.PP.fWndProcResizeFlicks;
+ {$ENDIF WIN_GDI}
+ Result.PP.fGotoControl := AParent.PP.fGotoControl;
+ Result.fCtl3D_child := AParent.fCtl3D_child and 2;
+ if AParent.fCtl3D_child and 2 <> 0 then
+ Result.fCtl3D_child := Result.fCtl3D_child or Integer( Ctl3D ) and 1
+ {else
+ Result.fCtl3D := False}; //
+ Result.fMargin := AParent.fMargin;
+ Result.fTextColor := AParent.fTextColor;
+ {$IFDEF SMALLEST_CODE}
+ {$ELSE}
+ {$IFDEF WIN_GDI} // for now Font is complicated a bit, implement it later
+ Result.fFont := Result.fFont.Assign( AParent.fFont );
+ if Result.fFont <> nil then
+ begin
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ Result.Add2AutoFree( Result.fFont );
+ {$ENDIF USE_AUTOFREE4CONTROLS}
+ Result.fFont.fParentGDITool := AParent.fFont;
+ Result.fFont.fOnGTChange := Result.FontChanged;
+ Result.FontChanged( Result.fFont );
+ end;
+ {$ENDIF WIN_GDI}
+ {$ENDIF SMALLEST_CODE}
+ Result.fColor := AParent.fColor;
+ {$IFDEF WIN_GDI}
+ Result.fBrush := Result.fBrush.Assign( AParent.fBrush );
+ if Result.fBrush <> nil then
+ begin
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ Result.Add2AutoFree( Result.fBrush );
+ {$ENDIF USE_AUTOFREE4CONTROLS}
+ Result.fBrush.fParentGDITool := AParent.fBrush;
+ Result.fBrush.fOnGTChange := Result.BrushChanged;
+ Result.BrushChanged( Result.fBrush );
+ end;
+ {$ENDIF WIN_GDI}
+ end;
+ {$IFDEF DUMP_WINDOWED}
+ DumpWindowed( Result );
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+
+{$IFDEF _X_}
+{$IFDEF GTK}
+VAR GTK_initialized: Boolean;
+ argc: Integer = 0;
+
+PROCEDURE FixedChildSetPos( Ctl, Chld: PControl; x, y: Integer );
+BEGIN
+ gtk_fixed_move( GTK_FIXED( Ctl.fClient ), Chld.fEventboxHandle, x, y );
+END;
+
+PROCEDURE LayoutChildSetPos( Ctl, Chld: PControl; x, y: Integer );
+BEGIN
+ gtk_layout_move( GTK_LAYOUT( Ctl.fClient ), Chld.fEventboxHandle, x, y );
+END;
+
+PROCEDURE FixedChildPut( Ctl, Chld: PControl; x, y: Integer );
+BEGIN
+ gtk_fixed_put( GTK_FIXED( Ctl.fClient ), Chld.fEventboxHandle, x, y );
+END;
+
+PROCEDURE LayoutChildPut( Ctl, Chld: PControl; x, y: Integer );
+BEGIN
+ gtk_layout_put( GTK_LAYOUT( Ctl.fClient ), Chld.fEventboxHandle, x, y );
+END;
+
+FUNCTION FixedClientArea( Ctl: PControl ): PGtkWidget;
+BEGIN
+ IF Ctl.fClient = nil THEN
+ BEGIN
+ Ctl.fClient := gtk_fixed_new;
+ gtk_container_set_border_width(GTK_CONTAINER(Ctl.fHandle), 0);
+ gtk_container_add( GTK_CONTAINER( Ctl.fHandle ), Ctl.fClient );
+ gtk_container_set_border_width(GTK_CONTAINER(Ctl.fClient), 0);
+ gtk_widget_show( Ctl.fClient );
+ Ctl.fChildPut := FixedChildPut;
+ Ctl.fChildSetPos := FixedChildSetPos;
+ END;
+ Result := Ctl.fClient;
+END;
+
+FUNCTION ClientAreaLayout( Ctl: PControl ): PGtkWidget;
+BEGIN
+ IF Ctl.fClient = nil THEN
+ BEGIN
+ Ctl.fClient := gtk_layout_new( {hadjustment} nil, {vadjustment} nil );
+ Ctl.fChildPut := LayoutChildPut;
+ Ctl.fChildSetPos := LayoutChildSetPos;
+ END;
+ Result := Ctl.fClient;
+END;
+
+FUNCTION _NewWindowed( AParent: PControl; ControlClassName: PAnsiChar;
+ widget: PGtkWidget; need_eventbox: Boolean ): PControl;
+//var GVal: TGValue;
+BEGIN
+ (*if not GTK_initialized then
+ begin
+ GTK_initialized := TRUE;
+ gtk_init( @ argc, {@ argv} nil );
+ end;*)
+ New( Result, CreateParented( AParent, widget, need_eventbox ) );
+ //Result.fWindowed := TRUE; // is set in TControl.Init
+ //???//Result.fControlClassName := ControlClassName;
+ IF AParent <> nil THEN
+ BEGIN
+ Result.fGotoControl := AParent.fGotoControl;
+ Result.fMargin := AParent.fMargin;
+ Result.fTextColor := AParent.fTextColor;
+ {$IFDEF SMALLEST_CODE}
+ {$ELSE}
+ {$IFDEF WIN_GDI} // for now Font is complicated a bit, implement it later
+ Result.fFont := Result.fFont.Assign( AParent.fFont );
+ IF Result.fFont <> nil THEN
+ begin
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ Result.Add2AutoFree( Result.fFont );
+ {$ENDIF USE_AUTOFREE4CONTROLS}
+ Result.fFont.fParentGDITool := AParent.fFont;
+ Result.fFont.fOnGTChange := Result.FontChanged;
+ Result.FontChanged( Result.fFont );
+ END;
+ {$ENDIF WIN_GDI}
+ {$ENDIF SMALLEST_CODE}
+ Result.fColor := AParent.fColor;
+ {$IFDEF WIN_GDI}
+ Result.fBrush := Result.fBrush.Assign( AParent.fBrush );
+ IF Result.fBrush <> nil THEN
+ BEGIN
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ Result.Add2AutoFree( Result.fBrush );
+ {$ENDIF USE_AUTOFREE4CONTROLS}
+ Result.fBrush.fParentGDITool := AParent.fBrush;
+ Result.fBrush.fOnGTChange := Result.BrushChanged;
+ Result.BrushChanged( Result.fBrush );
+ END;
+ {$ENDIF WIN_GDI}
+ END;
+ Result.fGetClientArea := FixedClientArea;
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+//===================== Form ========================//
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewForm( AParent: PControl; const Caption: AnsiString ): PControl;
+begin
+ new( Result, CreateForm( AParent, Caption ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Form';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewForm( AParent: PControl; const Caption: KOLString ): PControl;
+begin
+ Result := _NewWindowed( AParent, 'Form', True,
+ {$IFDEF PACK_COMMANDACTIONS} PAnsiChar( OTHER_ACTIONS )
+ {$ELSE} nil {$ENDIF} );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Form';
+ {$ENDIF}
+ Result.fClsStyle := Result.fClsStyle or CS_DBLCLKS;
+ Result.AttachProc( WndProcForm );
+ Result.AttachProc( WndProcDoEraseBkgnd );
+ Result.Caption := Caption;
+ {$IFDEF USE_FLAGS} Result.fFlagsG3 := Result.fFlagsG3 + [G3_SizeGrip, G3_IsForm];
+ {$ELSE}
+ {$IFNDEF SMALLEST_CODE}
+ Result.fSizeGrip := TRUE;
+ {$ENDIF}
+ Result.fIsForm := TRUE;
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+const Edgestyles: array[ TEdgeStyle ] of DWORD = ( WS_DLGFRAME, SS_SUNKEN, 0, 0, 0);
+
+function NewAlienPanel( AParentWnd: HWnd; EdgeStyle: TEdgeStyle ): PControl;
+begin
+ Result := _NewWindowed( nil, 'KOL', TRUE,
+ {$IFDEF PACK_COMMANDACTIONS} PAnsiChar( OTHER_ACTIONS )
+ {$ELSE} nil {$ENDIF} );
+ Result.fClsStyle := Result.fClsStyle or CS_DBLCLKS;
+ Result.FParentWnd := AParentWnd;
+ Result.AttachProc( WndProcForm );
+ Result.AttachProc( WndProcDoEraseBkgnd );
+ {$IFDEF USE_FLAGS} Result.fFlagsG3 := Result.fFlagsG3 + [G3_IsForm, G3_IsControl];
+ {$ELSE} Result.fIsForm := TRUE;
+ Result.fIsControl := TRUE;
+ {$ENDIF}
+ Result.fStyle.Value := WS_VISIBLE or WS_CHILD or WS_TABSTOP or
+ WS_CLIPSIBLINGS or WS_CLIPCHILDREN or Edgestyles[ EdgeStyle ];
+ Result.fExStyle := Result.fExStyle //or WS_EX_CLIENTEDGE
+ or WS_EX_CONTROLPARENT;
+ Result.SetSize( 100, 64 );
+end;
+
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+FUNCTION getFormCaption(F: PControl): KOLString;
+BEGIN
+ F.fCaption := gtk_window_get_title( GTK_WINDOW( F.fHandle ) );
+ Result := F.fCaption;
+END;
+
+PROCEDURE setFormCaption(F: PControl; const Value: KOLString);
+BEGIN
+ F.fCaption := Value;
+ gtk_window_set_title( GTK_WINDOW( F.fCaptionHandle ), PAnsiChar( String( Value ) ) );
+END;
+
+PROCEDURE DestroyForm( Widget: PGtkWidget; Sender: PControl ); CDECL;
+VAR Quit: Boolean;
+BEGIN
+ Quit := Sender.IsMainWindow;
+ Sender.Free;
+ IF Quit THEN
+ gtk_main_quit();
+END;
+
+FUNCTION NewForm( AParent: PControl; const Caption: KOLString ): PControl;
+VAR widget: PGtkWidget;
+BEGIN
+ IF not GTK_initialized THEN
+ BEGIN
+ GTK_initialized := TRUE;
+ gtk_init( @ argc, {@ argv} nil );
+ END;
+ widget := gtk_window_new( GTK_WINDOW_TOPLEVEL );
+ Result := _NewWindowed( AParent, 'Form', widget, FALSE );
+ Result.fGetCaption := getFormCaption;
+ Result.fSetCaption := setFormCaption;
+ Result.Caption := Caption;
+ {$IFDEF USE_FLAGS} include( Result.fFlagsG3, G3_IsForm );
+ {$ELSE} Result.fIsForm := TRUE; {$ENDIF}
+ gtk_signal_connect( Pointer( Result.fHandle ), 'destroy',
+ @ DestroyForm, Result );
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$ENDIF USE_CONSTRUCTORS}
+
+{$IFDEF WIN_GDI} //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+//===================== Applet button ========================//
+
+//22{$IFDEF ASM_VERSION}
+{$IFNDEF PAS_ONLY}
+ function WndProcAppAsm(Self_: PControl; var Msg: TMsg; var Rslt: Integer): Boolean;
+ asm
+ CMP word ptr [EDX].TMsg.message, WM_SETFOCUS
+ JNZ @@chk_CLOSE
+ MOV ECX, [EAX].TControl.DF.FCurrentControl
+ JECXZ @@ret_false
+ XCHG EAX, ECX
+ PUSH EAX
+ CALL CallTControlCreateWindow
+ TEST AL, AL
+ POP EAX
+ JZ @@1
+ PUSH [EAX].TControl.fHandle
+ CALL SetFocus
+ @@1: MOV AL, 1
+ RET
+ @@chk_CLOSE:
+ CMP word ptr [EDX].TMsg.message, WM_SYSCOMMAND
+ JNZ @@ret_false
+ MOV EDX, dword ptr [EDX].TMsg.wParam
+ AND DX, $FFF0
+ CMP DX, SC_CLOSE
+ JNZ @@ret_false
+ PUSH ECX
+ MOV ECX, [EAX].TControl.fChildren
+ JECXZ @@ret_false1
+ XCHG EAX, ECX
+ MOV ECX, [EAX].TList.fCount
+ JECXZ @@ret_false1
+ MOV EAX, [EAX].TList.fItems
+ MOV ECX, dword ptr [EAX]
+ JECXZ @@ret_false1
+ XCHG EAX, ECX
+ PUSH EAX
+ CALL TControl.IsMainWindow
+ TEST EAX, EAX
+ POP EAX
+ JZ @@ret_false1
+ CALL TControl.Close
+ POP ECX
+ XOR EAX, EAX
+ MOV dword ptr [ECX], EAX
+ INC EAX
+ JMP @@exit
+ @@ret_false1:
+ POP ECX
+ @@ret_false:
+ XOR EAX, EAX
+ @@exit:
+ end;
+{$ENDIF not PAS_ONLY}
+//22{$ENDIF}
+
+function WndProcAppPas(Self_: PControl; var Msg: TMsg; var Rslt: Integer): Boolean;
+begin
+ Result := False;
+ case Msg.message of
+ WM_SETFOCUS:
+ {$IFDEF NEW_MODAL}
+ if Self_.DF.fModalForm <> nil then
+ SetFocus( Self_.DF.fModalForm.fHandle )
+ else if ( Self_.DF.FCurrentControl <> nil ) and not
+ ( {$IFDEF USE_FLAGS} (G3_IsForm in Self_.DF.fCurrentControl.fFlagsG3)
+ {$ELSE} Self_.DF.fCurrentControl.fIsForm {$ENDIF}
+ xor
+ {$IFDEF USE_FLAGS} (G3_IsApplet in Self_.fFlagsG3)
+ {$ELSE} Self_.fIsApplet {$ENDIF} ) then
+ {$ELSE not_NEW_MODAL}
+ if Self_.DF.fCurrentControl <> nil then
+ {$ENDIF NEW_MODAL}
+ begin
+ if Self_.DF.FCurrentControl.CreateWindow then
+ SetFocus( Self_.DF.FCurrentControl.fHandle );
+ Result := True;
+ end;
+ WM_SYSCOMMAND:
+ CASE Msg.wParam and $FFF0 OF
+ SC_CLOSE:
+ if (Self_.fChildren <> nil) and (Self_.fChildren.fCount > 0) and
+ PControl( Self_.fChildren.fItems[ 0 ] ).IsMainWindow then
+ begin
+ PControl( Self_.fChildren.fItems[ 0 ] ).Close;
+ Rslt := 0;
+ Result := TRUE;
+ end;
+ END;
+ end;
+end;
+
+{$IFDEF USE_CONSTRUCTORS}
+{$DEFINE CREATEAPPBUTTON_USED}
+function NewApplet( const Caption: AnsiString ): PControl;
+begin
+ new( Result, CreateApplet( Caption ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Applet';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF ASM_TLIST}
+function NewApplet( const Caption: KOLString ): PControl;
+const AppClass: array[ 0..3 ] of KOLChar = ( 'A', 'p', 'p', #0 );
+asm
+ XOR ECX, ECX
+ INC ECX
+ MOV [AppButtonUsed], CL
+ PUSH EAX
+ MOV EDX, offset[AppClass]
+ XOR EAX, EAX
+ PUSH EAX
+ CALL _NewWindowed
+ {$IFDEF USE_FLAGS}
+ OR [EAX].TControl.fFlagsG3, (1 shl G3_IsApplet)
+ {$ELSE}
+ INC [EAX].TControl.FIsApplet
+ {$ENDIF}
+ MOV word ptr [EAX].TControl.fStyle + 2, $90CA //WS_VISIBLE or WS_SYSMENU or WS_POPUP or WS_MINIMIZEBOX or WS_CAPTION
+ MOV byte ptr [EAX].TControl.fExStyle + 2, WS_EX_APPWINDOW shr 16 // WS_EX_APPWINDOW = $40000
+ CALL @@newapp1
+
+ PUSH ESI // BODY of CreateAppButton here
+ PUSH 0
+ PUSH [EAX].TControl.fHandle
+ CALL GetSystemMenu
+ MOV ESI, offset[DeleteMenu]
+
+ XCHG ECX, EAX
+ MOV EAX, SC_MAXIMIZE
+ CDQ
+
+ PUSH EDX
+ PUSH EAX
+ PUSH ECX
+
+ PUSH EDX
+ {$IFDEF PARANOIA} DB $2C, $20 {$ELSE} SUB AL, $20 {$ENDIF} // SC_MOVE
+ PUSH EAX
+ PUSH ECX
+
+ PUSH EDX
+ {$IFDEF PARANOIA} DB $2C, $10 {$ELSE} SUB AL, $10 {$ENDIF} // SC_SIZE
+ PUSH EAX
+ PUSH ECX
+
+ PUSH 1 // MF_GRAYED or MF_BYCOMMAND
+ MOV AX, SC_RESTORE
+ PUSH EAX
+ PUSH ECX
+
+ CALL EnableMenuItem
+ CALL ESI
+ CALL ESI
+ CALL ESI
+ POP ESI
+@@ret_false:
+ XOR EAX, EAX
+ RET
+
+@@chk_CLOSE:
+ CMP word ptr [EDX].TMsg.message, WM_SYSCOMMAND
+ JNZ @@ret_false
+ MOV EDX, dword ptr [EDX].TMsg.wParam
+ AND DX, $FFF0
+ CMP DX, SC_CLOSE
+ JNZ @@ret_false
+ PUSH ECX
+ MOV ECX, [EAX].TControl.fChildren
+ JECXZ @@ret_false1
+ XCHG EAX, ECX
+ MOV ECX, [EAX].TList.fCount
+ JECXZ @@ret_false1
+ MOV EAX, [EAX].TList.fItems
+ MOV ECX, dword ptr [EAX]
+ JECXZ @@ret_false1
+ XCHG EAX, ECX
+ PUSH EAX
+ CALL TControl.IsMainWindow
+ TEST EAX, EAX
+ POP EAX
+ JZ @@ret_false1
+ CALL TControl.Close
+ POP ECX
+ XOR EAX, EAX
+ MOV dword ptr [ECX], EAX
+ INC EAX
+ RET
+ @@ret_false1:
+ POP ECX
+ JMP @@ret_false
+
+@@newapp1:
+ POP [EAX].TControl.PP.FCreateWndExt
+ PUSH EAX
+ CALL @@newapp2
+
+ // BODY of WndProcApp here:
+ CMP word ptr [EDX].TMsg.message, WM_SETFOCUS
+ JNZ @@chk_CLOSE
+ MOV ECX, [EAX].TControl.DF.fCurrentControl
+ JECXZ @@ret_false
+ XCHG EAX, ECX
+
+ PUSH EAX
+ CALL CallTControlCreateWindow
+ POP EAX
+ PUSH [EAX].TControl.fHandle
+
+ CALL SetFocus
+ MOV AL, 1
+ RET
+
+@@newapp2:
+ POP EDX
+ CALL TControl.AttachProc
+ POP EAX
+ POP EDX
+ PUSH EAX
+ CALL TControl.SetCaption
+ POP EAX
+end;
+{$ELSE PAS_VERSION} //Pascal
+
+procedure CreateAppButton( App: PControl );
+var M: HMenu;
+begin
+ M := GetSystemMenu( App.fHandle, False );
+ DeleteMenu( M, SC_MAXIMIZE, MF_BYCOMMAND );
+ DeleteMenu( M, SC_MOVE, MF_BYCOMMAND );
+ DeleteMenu( M, SC_SIZE, MF_BYCOMMAND );
+ EnableMenuItem( M, SC_RESTORE, MF_GRAYED or MF_BYCOMMAND );
+end;
+
+function NewApplet( const Caption: KOLString ): PControl;
+begin
+ AppButtonUsed := True;
+ Result := _NewWindowed( nil, 'App', True,
+ {$IFDEF PACK_COMMANDACTIONS} PAnsiChar( OTHER_ACTIONS )
+ {$ELSE} nil {$ENDIF} );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Applet';
+ {$ENDIF}
+ {$IFDEF USE_FLAGS} include( Result.fFlagsG3, G3_IsApplet );
+ {$ELSE} Result.FIsApplet := TRUE; {$ENDIF}
+ Result.fStyle.Value := WS_VISIBLE or WS_SYSMENU or WS_POPUP or WS_MINIMIZEBOX or WS_CAPTION;
+ Result.fExStyle := WS_EX_APPWINDOW;
+ Result.PP.FCreateWndExt := CreateAppButton;
+ {$IFDEF ASM_VERSION}
+ Result.AttachProc( WndProcAppAsm );
+ {$ELSE}
+ Result.AttachProc( WndProcAppPas );
+ {$ENDIF}
+ Result.Caption := Caption;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF USE_CONSTRUCTORS}
+
+{$IFDEF CREATEAPPBUTTON_USED}
+procedure CreateAppButton( App: PControl );
+asm
+ {$IFDEF F_P}
+ MOV EAX, [App]
+ {$ENDIF F_P}
+ PUSH ESI
+ PUSH 0
+ PUSH [EAX].TControl.fHandle
+ CALL GetSystemMenu
+ MOV ESI, offset[DeleteMenu]
+
+ XCHG ECX, EAX
+ MOV EAX, SC_MAXIMIZE
+ CDQ
+
+ PUSH EDX
+ PUSH EAX
+ PUSH ECX
+
+ PUSH EDX
+ {$IFDEF PARANOIA} DB $2C, $20 {$ELSE} SUB AL, $20 {$ENDIF} // SC_MOVE
+ PUSH EAX
+ PUSH ECX
+
+ PUSH EDX
+ {$IFDEF PARANOIA} DB $2C, $10 {$ELSE} SUB AL, $10 {$ENDIF} // SC_SIZE
+ PUSH EAX
+ PUSH ECX
+
+ PUSH 1 // MF_GRAYED or MF_BYCOMMAND
+ MOV AX, SC_RESTORE
+ PUSH EAX
+ PUSH ECX
+
+ CALL EnableMenuItem
+ CALL ESI
+ CALL ESI
+ CALL ESI
+ POP ESI
+end {$IFDEF F_P} [ 'EAX', 'EDX', 'ECX' ] {$ENDIF};
+{$ENDIF CREATEAPPBUTTON_USED}
+
+var CtlIdCount: WORD = $8000;
+
+{$ENDIF WIN_GDI}
+
+{$IFDEF GDI}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function _NewControl( AParent: PControl; ControlClassName: PKOLChar;
+ Style: DWORD; Ctl3D: Boolean; Actions: TCommandActionsParam ): PControl;
+var Form: PControl;
+begin
+ Result := _NewWindowed( AParent, ControlClassName, Ctl3D, Actions );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl';
+ {$ENDIF}
+ {$IFDEF USE_FLAGS} include( Result.fFlagsG3, G3_IsControl );
+ {$ELSE} Result.fIsControl := True; {$ENDIF}
+ Result.fStyle.Value := Style or WS_CLIPSIBLINGS or WS_CLIPCHILDREN;
+ Result.fVerticalAlign := vaTop;
+ Result.fLookTabKeys := [ tkTab, tkLeftRight, tkUpDown, tkPageUpPageDn ];
+ if Result.fCtl3D_child and 1 <> 0 then
+ begin
+ Result.fStyle.Value := Result.fStyle.Value and not WS_BORDER;
+ Result.fExStyle := Result.fExStyle or WS_EX_CLIENTEDGE;
+ end;
+ {$IFDEF USE_FLAGS}
+ {$ELSE}
+ Result.fVisible := (Style and WS_VISIBLE) <> 0;
+ Result.fTabstop := (Style and WS_TABSTOP) <> 0;
+ {$ENDIF}
+ if (AParent <> nil) then
+ begin
+ with Result.fBoundsRect do
+ begin
+ Left := AParent.fMargin + AParent.fClientLeft;
+ Top := AParent.fMargin + AParent.fClientTop;
+ Right := Left + 64;
+ Bottom := Top + 64;
+ end;
+ Form := AParent.ParentForm;
+ if Form <> nil then
+ begin
+ Inc( Form.fTabOrder );
+ Result.fTabOrder := Form.fTabOrder;
+ if F2_Tabstop in Result.fStyle.f2_Style then
+ begin
+ if Form.DF.FCurrentControl = nil then
+ Form.DF.FCurrentControl := Result;
+ end;
+ end;
+ Result.fCursor := AParent.fCursor;
+ end;
+ Result.fMenu := CtlIdCount;
+ Inc( CtlIdCount );
+ Result.AttachProc( WndProcCtrl );
+ {$IFDEF DEBUG_ALTSPC}
+ DumpWindowed(Result);
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+
+{$IFDEF _X_}
+{$IFDEF GTK}
+FUNCTION getLabelCaption( L: PControl ): KOLString;
+BEGIN
+ L.fCaption := gtk_label_get_text( Pointer( L.fCaptionHandle ) );
+ Result := L.fCaption;
+END;
+
+PROCEDURE setLabelCaption( L: PControl; const Value: KOLString );
+BEGIN
+ L.fCaption := Value;
+ gtk_label_set_text( Pointer( L.fCaptionHandle ), PAnsiChar( String( Value ) ) );
+END;
+
+FUNCTION _NewControl( AParent: PControl; ControlClassName: PAnsiChar;
+ Style: DWORD; Ctl3D: Boolean; widget: PGtkWidget; need_eventbox: Boolean ): PControl;
+VAR Rect: TRect;
+BEGIN
+ Result := _NewWindowed( AParent, ControlClassName, widget, need_eventbox );
+ Result.fIsControl := True;
+ Result.fVerticalAlign := vaTop;
+ Result.fVisible := (Style and WS_VISIBLE) <> 0;
+ Result.fTabstop := (Style and WS_TABSTOP) <> 0;
+ IF (AParent <> nil) THEN
+ BEGIN
+ WITH Rect DO
+ BEGIN
+ Left := AParent.fMargin + AParent.fClientLeft;
+ Top := AParent.fMargin + AParent.fClientTop;
+ END;
+ Inc( AParent.ParentForm.fTabOrder );
+ Result.fTabOrder := AParent.ParentForm.fTabOrder;
+ {$IFDEF GDI}
+ Result.fCursor := AParent.fCursor;
+ {$ENDIF GDI}
+ //gtk_container_add( GTK_CONTAINER( AParent.fHandle ), Result.fHandle );
+ END;
+ {with Rect do
+ begin
+ Right := Left + 64;
+ Bottom := Top + 64;
+ end;
+ Result.fBoundsRect := Result.BoundsRect;
+ Result.BoundsRect := Rect;}
+ Result.fLookTabKeys := [ tkTab, tkLeftRight, tkUpDown, tkPageUpPageDn ];
+ {$IFDEF GDI}
+ IF Result.fCtl3D THEN
+ BEGIN
+ Result.fStyle := Result.fStyle and not WS_BORDER;
+ Result.fExStyle := Result.fExStyle or WS_EX_CLIENTEDGE;
+ END;
+ IF (Style and WS_TABSTOP) <> 0 THEN
+ BEGIN
+ Form := Result.ParentForm;
+ IF Form <> nil THEN
+ IF Form.FCurrentControl = nil THEN
+ Form.FCurrentControl := Result;
+ END;
+ Result.fMenu := CtlIdCount;
+ Inc( CtlIdCount );
+ Result.AttachProc( WndProcCtrl );
+ {$ENDIF GDI}
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF WIN_GDI}
+
+//===================== Button ========================//
+
+function TControl.SetButtonIcon(aIcon: HIcon): PControl;
+var PrevImg: THandle;
+begin
+ Style := Style or BS_ICON;
+ DF.fButtonIcon := aIcon;
+ PrevImg := Perform( BM_SETIMAGE, IMAGE_ICON, aIcon );
+ if PrevImg <> 0 then
+ DeleteObject( PrevImg );
+ Result := @ Self;
+end;
+
+function TControl.SetButtonBitmap(aBmp: HBitmap): PControl;
+var PrevImg: THandle;
+begin
+ Style := Style or BS_BITMAP;
+ PrevImg := Perform( BM_SETIMAGE, IMAGE_BITMAP, aBmp );
+ if PrevImg <> 0 then
+ DeleteObject( PrevImg );
+ Result := @ Self;
+end;
+
+{$IFDEF ALL_BUTTONS_RESPOND_TO_ENTER}
+function WndProcBtnReturnClick( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+begin
+ Result := FALSE;
+ if ((Msg.message = WM_KEYDOWN) or (Msg.message = WM_KEYUP) or
+ (Msg.message = WM_CHAR)) and (Msg.wParam = 13) then
+ Msg.wParam := 32;
+end;
+{$ENDIF}
+
+{$IFNDEF BUTTON_DBLCLICK}
+function WndProcBtnDblClkAsClk( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+begin
+ Result := FALSE;
+ if Msg.message = WM_LBUTTONDBLCLK then
+ Msg.message := WM_LBUTTONDOWN;
+end;
+{$ENDIF}
+
+function AutoMinimizeApplet(Self_: PControl; var Msg: TMsg; var Rslt: Integer): Boolean;
+begin
+ if (msg.Message=WM_SYSCOMMAND) and ((msg.wParam and not 15)=SC_MINIMIZE) then begin
+ AppletMinimize;
+ Result := True;
+ end else
+ Result := False;
+end;
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewButton( AParent: PControl; const Caption: KOLString ): PControl;
+begin
+ new( Result, CreateButton( AParent, Caption ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Button';
+ {$ENDIF}
+end;
+{$ELSE USE_CONSTRUCTORS}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewButton( AParent: PControl; const Caption: KOLString ): PControl;
+begin
+ Result := _NewControl( AParent, 'BUTTON',
+ WS_VISIBLE or WS_CHILD or BS_NOTIFY or
+ BS_PUSHLIKE or WS_TABSTOP, False,
+ {$IFDEF PACK_COMMANDACTIONS} ButtonActions_Packed
+ {$ELSE} @ButtonActions {$ENDIF} );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Button';
+ {$ENDIF}
+ Result.aAutoSzX := 14;
+ Result.aAutoSzY := 6;
+ {$IFDEF BUTTON_DBLCLICK}
+ Result.ClsStyle := Result.ClsStyle - CS_DBLCLKS;
+ {$ENDIF}
+ //Result.fCtl3D := TRUE;
+ with Result.fBoundsRect do
+ Bottom := Top + 22;
+ Result.fTextAlign := taCenter;
+ Result.Caption := Caption;
+ {$IFDEF USE_FLAGS}
+ Result.fFlagsG5 := Result.fFlagsG5 + [G5_IsButton, G5_IgnoreDefault];
+ {$ELSE} Result.fIsButton := TRUE;
+ Result.fIgnoreDefault := TRUE;
+ {$ENDIF}
+ {$IFNDEF SMALLEST_CODE}
+ {$IFNDEF BUTTON_DBLCLICK}
+ Result.AttachProc( WndProcBtnDblClkAsClk );
+ {$ENDIF}
+ {$ENDIF}
+ {$IFDEF ALL_BUTTONS_RESPOND_TO_ENTER}
+ Result.AttachProc( WndProcBtnReturnClick );
+ {$ENDIF}
+{$IFDEF GRAPHCTL_XPSTYLES}
+ Attach_WM_THEMECHANGED( Result, XP_Themes_For_BitBtn );
+{$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF USE_CONSTRUCTORS}
+{$ENDIF WIN_GDI}
+
+{$IFDEF _X_}
+{$IFDEF GTK}
+CONST
+ HorAlignments: ARRAY[ TTextAlign ] of Single = ( {taLeft} 0, {taRight} 1, {taCenter} 0.5 );
+ VerAlignments: ARRAY[ TVerticalAlign ] of Single = ( {vaTop} 0, {vaCenter} 0.5, {vaBottom} 1 );
+
+PROCEDURE ButtonSetTextAlign( Self_: PControl );
+BEGIN
+ gtk_button_set_alignment( GTK_BUTTON( Self_.fHandle ), HorAlignments[ Self_.fTextAlign ],
+ VerAlignments[ Self_.fVerticalAlign ] );
+END;
+
+FUNCTION NewButton( AParent: PControl; const Caption: KOLString ): PControl;
+BEGIN
+ Result := _NewControl( AParent, 'BUTTON',
+ WS_VISIBLE or WS_CHILD or BS_NOTIFY or
+ BS_PUSHLIKE or WS_TABSTOP, False,
+ gtk_button_new{_with_label}( {PChar( String( Caption ) )} ), FALSE );
+ //Result.Height := 22;
+ gtk_container_set_border_width( GTK_CONTAINER( Result.fHandle ), 0 );
+ Result.fCaptionHandle := gtk_label_new( PAnsiChar( String( Caption ) ) );
+ gtk_container_add( GTK_CONTAINER( Result.fHandle ), Result.fCaptionHandle );
+ //gtk_container_set_border_width( GTK_CONTAINER( Result.fCaptionHandle ), 0 );
+ gtk_widget_show( Result.fCaptionHandle );
+ Result.fGetCaption := getLabelCaption;
+ Result.fSetCaption := setLabelCaption;
+ //Result.fIgnoreDefault := TRUE;
+ //Result.fCtl3D := TRUE;
+ //with Result.fBoundsRect do
+ // Bottom := Top + 22;
+ Result.fTextAlign := taCenter;
+ Result.fCaption := Caption;
+ Result.fIsButton := TRUE;
+ Result.fSetTextAlign := ButtonSetTextAlign;
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF WIN_GDI}
+//----------------- BitBtn -----------------------
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function WndProc_DrawItem( Sender: PControl; var Msg: TMsg; var Rslt: Integer )
+ : Boolean;
+var DI: PDrawItemStruct;
+ Control: PControl;
+begin
+ Result := FALSE;
+ if Msg.message = WM_DRAWITEM then
+ begin
+ DI := Pointer( Msg.lParam );
+ {$IFDEF USE_PROP}
+ Control := Pointer( GetProp( DI.hwndItem, ID_SELF ) );
+ {$ELSE}
+ Control := Pointer( GetWindowLong( DI.hwndItem, GWL_USERDATA ) );
+ {$ENDIF}
+ if Control <> nil then
+ begin
+ Rslt := Control.Perform( CN_DRAWITEM, Msg.wParam, Msg.lParam );
+ Result := TRUE;
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+function ExcludeAmpersands( Self_: PControl; const S: KOLString ): KOLString;
+var I: Integer;
+begin
+ Result := S;
+ if not Self_.DF.fBitBtnDrawMnemonic then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ for I := Length( Result ) downto 1 do
+ begin
+ if Result[ I ] = '&' then
+ Delete( Result, I, 1 );
+ end;
+end;
+
+procedure BitBtnExtDraw( Self_: PControl; DC: HDC; X, Y: Integer; const R: TRect;
+ const CapText, CapTxtOrig: KOLString; Color: TColor );
+var I, J, W, H: Integer;
+ Sz: TSize;
+ Pen, OldPen: HPen;
+begin
+ if not Self_.DF.fBitBtnDrawMnemonic then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ J := 0;
+ for I := 1 to Length( CapTxtOrig ) do
+ begin
+ if CapTxtOrig[ I ] <> '&' then
+ Inc( J )
+ else
+ begin
+ GetTextExtentPoint32( DC, PKOLChar( CapText ), J, Sz );
+ W := Sz.cx;
+ Windows.GetTextExtentPoint32( DC, '_', 1, Sz ); // A/W KOL_ANSI
+ H := Sz.cy - 1;
+ Windows.GetTextExtentPoint32( DC, @ CapTxtOrig[ I + 1 ], 1, Sz );
+ Windows.MoveToEx( DC, X + W, Y + H, nil );
+
+ Pen := CreatePen( PS_SOLID, 0, Color2RGB( Color ) );
+ OldPen := SelectObject( DC, Pen );
+
+ Windows.LineTo( DC, X + W + Sz.cx, Y + H );
+
+ SelectObject( DC, OldPen );
+ DeleteObject( Pen );
+ end;
+ end;
+end;
+
+procedure TControl.SetBitBtnDrawMnemonic(const Value: Boolean);
+begin
+ DF.fBitBtnDrawMnemonic := Value;
+ DF.FBitBtnGetCaption := ExcludeAmpersands;
+ DF.FBitBtnExtDraw := BitBtnExtDraw;
+ Invalidate;
+end;
+
+function TControl.GetBitBtnImgIdx: Integer;
+begin
+ Result := LoWord( DF.fGlyphCount );
+end;
+
+procedure TControl.SetBitBtnImgIdx(const Value: Integer);
+begin
+ if not( bboImageList in DF.fBitBtnOptions ) then Exit; {>>>>>>>>>>>>>>>>>>>>>}
+ DF.fGlyphCount := HiWord( DF.fGlyphCount ) or (Value and $FFFF);
+ Invalidate;
+end;
+
+function TControl.GetBitBtnImageList: THandle;
+begin
+ Result := 0;
+ if bboImageList in DF.fBitBtnOptions then
+ Result := DF.fGlyphBitmap;
+end;
+
+procedure TControl.SetBitBtnImageList(const Value: THandle);
+begin
+ DF.fGlyphBitmap := Value;
+ if Value <> 0 then
+ begin
+ include( DF.fBitBtnOptions, bboImageList );
+ ImageList_GetIconSize( Value, DF.fGlyphWidth, DF.fGlyphHeight );
+ end else
+ exclude( DF.fBitBtnOptions, bboImageList );
+ Invalidate;
+end;
+
+{$IFDEF ASM_noVERSION} // remove &-s from view //+ TextShift & if Y < 0 then Y := 0; // + glyph + TextShift if not glyphOver
+ // timer when RepeatInterval set
+function WndProcBitBtn( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+const szBitmapInfo = sizeof(TBitmapInfo);
+asm
+ CMP word ptr [EDX].TMsg.message, WM_LBUTTONDBLCLK
+ JNZ @@noWM_LBUTTONDBLCLK
+ PUSH ECX
+ PUSH [EDX].TMsg.wParam
+ PUSH [EDX].TMsg.lParam
+ PUSH WM_LBUTTONDOWN
+ PUSH EAX
+ CALL TControl.Perform
+ POP ECX
+ MOV [ECX], EAX
+ MOV AL, 1
+ RET
+@@noWM_LBUTTONDBLCLK:
+ PUSH EBX
+ CMP [EDX].TMsg.message, CN_DRAWITEM
+ JNZ @@noCN_DRAWITEM
+ PUSH EDI
+ PUSH ESI
+ XCHG EDI, EAX // EDI = @Self
+ MOV dword ptr [ECX], 1
+ MOV ESI, [EDX].TMsg.lParam // ESI = DIS
+ XOR EBX, EBX // G = 0
+ MOV EAX, [ESI].TDrawItemStruct.itemState
+ TEST byte ptr [EDI].TControl.fBitBtnOptions, 8 //1 shl Ord(bboFixed)
+ JNZ @@fixed_in_options
+ {$IFDEF PARANOIA} DB $A8, ODS_SELECTED {$ELSE} TEST AL, ODS_SELECTED {$ENDIF}
+ JZ @@not1
+ JMP @@1
+@@fixed_in_options:
+ {$IFDEF USE_FLAGS}
+ TEST [EDI].TControl.fFlagsG4, 1 shl G4_Checked
+ {$ELSE}
+ TEST byte ptr [EDI].TControl.fChecked, 1
+ {$ENDIF}
+ JZ @@not1
+@@1: INC EBX
+@@not1:
+ {$IFDEF PARANOIA} DB $A8, ODS_DISABLED {$ELSE} TEST AL, ODS_DISABLED {$ENDIF}
+ JZ @@not2
+ MOV BL, 2
+@@not2: TEST EBX, EBX
+ JNZ @@not3
+ {$IFDEF PARANOIA} DB $A8, ODS_FOCUS {$ELSE} TEST AL, ODS_FOCUS {$ENDIF}
+ JZ @@not3
+ MOV BL, 3
+@@not3: {$IFDEF USE_FLAGS}
+ TEST [EDI].TControl.fFlagsG3, 1 shl G3_MouseInCtl
+ {$ELSE}
+ CMP [EDI].TControl.fMouseInControl, BH
+ {$ENDIF}
+ JZ @@not4
+ TEST EBX, EBX
+ JZ @@4
+ CMP BL, 3
+ JNZ @@not4
+@@4: MOV BL, 4
+@@not4: MOV ECX, [EDI].TControl.fOnBitBtnDraw.TMethod.Code
+ {$IFDEF NIL_EVENTS}
+ TEST ECX, ECX
+ JZ @@noOnBitBtnDraw
+ {$ENDIF}
+ MOV EAX, [EDI].TControl.fCanvas
+ PUSH EAX
+ TEST EAX, EAX
+ JZ @@noCanvas
+ MOV EDX, [ESI].TDrawItemStruct.hDC
+ CALL TCanvas.SetHandle
+@@noCanvas:
+ MOV EAX, [EDI].TControl.fOnBitBtnDraw.TMethod.Data
+ MOV EDX, EDI
+ PUSH EBX
+ XCHG ECX, EBX
+ CALL EBX
+ POP EBX
+ POP ECX // Canvas
+ PUSH EAX
+ JECXZ @@noCanvas2
+ XCHG EAX, ECX
+ XOR EDX, EDX
+ CALL TCanvas.SetHandle
+@@noCanvas2:
+ POP EAX
+ TEST AL, AL
+ JNZ @@exit_draw
+@@noOnBitBtnDraw:
+ TEST byte ptr [EDI].TControl.fBitBtnOptions, 2 //1 shl Ord(bboNoBorder)
+ JNZ @@noborder
+ TEST byte ptr [ESI].TDrawItemStruct.itemState, ODS_FOCUS
+ JZ @@noDefaultBorder
+ PUSH {BLACK_BRUSH} DKGRAY_BRUSH
+ CALL GetStockObject
+ LEA EDX, [ESI].TDrawItemStruct.rcItem
+ OR ECX, -1
+ PUSH ECX
+ PUSH ECX
+ PUSH EDX
+ PUSH EAX
+ PUSH EDX
+ PUSH [ESI].TDrawItemStruct.hDC
+ CALL Windows.FrameRect
+ CALL InflateRect
+ XOR ECX, ECX
+ JMP @@noFlat
+@@noDefaultBorder:
+ {$IFDEF USE_FLAGS}
+ TEST [EDI].TControl.fFlagsG3, 1 shl G3_Flat
+ JZ @@noFlat
+ TEST [EDI].TControl.fFlagsG3, 1 shl G3_MouseInCtl
+ JZ @@noborder
+ {$ELSE}
+ MOVZX ECX, [EDI].TControl.fFlat
+ JECXZ @@noFlat
+ AND CL, [EDI].TControl.fMouseInControl
+ JZ @@noborder
+ {$ENDIF}
+@@noFlat:
+ TEST byte ptr [ESI].TDrawItemStruct.itemState, ODS_SELECTED
+ MOV CL, {BDR_SUNKENOUTER or} BDR_SUNKENINNER
+ JNZ @@border_sunken
+ MOV CL, {BDR_RAISEDOUTER or} BDR_RAISEDINNER
+@@border_sunken:
+ LEA EDX, [ESI].TDrawItemStruct.rcItem
+ OR EAX, -1
+ PUSH EAX
+ PUSH EAX
+ PUSH EDX
+ PUSH BF_ADJUST or BF_RECT
+ PUSH ECX
+ PUSH EDX
+ PUSH [ESI].TDrawItemStruct.hDC
+ CALL DrawEdge
+ CALL InflateRect
+@@noborder:
+ PUSH [ESI].TDrawItemStruct.rcItem.Bottom
+ PUSH [ESI].TDrawItemStruct.rcItem.Right
+ PUSH [ESI].TDrawItemStruct.rcItem.Top
+ PUSH [ESI].TDrawItemStruct.rcItem.Left
+ MOV EAX, [EDI].TControl.fGlyphWidth
+ MOV EDX, [EDI].TControl.fGlyphHeight
+ TEST EAX, EAX
+ JLE @@noglyph
+ TEST EDX, EDX
+ JLE @@noglyph
+ PUSH EBP
+ MOV EBP, ESP
+
+ PUSH EDX // ImgH -> [EBP-4]
+ PUSH EAX // ImgW -> [EBP-8]
+ PUSH EDX // OutH -> [EBP-12]
+ PUSH EAX // OutW -> [EBP-16]
+ MOV EAX, [ESI].TDrawItemStruct.rcItem.Left // X = DIS.rcItem.Left
+ MOV EDX, [ESI].TDrawItemStruct.rcItem.Top // Y = DIS.rcItem.Top
+ MOV ECX, [ESI].TDrawItemStruct.rcItem.Bottom
+ SUB ECX, EDX
+ PUSH ECX // H -> [EBP-20]
+ MOV ECX, [ESI].TDrawItemStruct.rcItem.Right
+ SUB ECX, EAX
+ PUSH ECX // W -> [EBP-24]
+ MOVZX ECX, [EDI].TControl.fGlyphLayout
+ PUSH EBX
+ INC ECX
+ LOOP @@noGlyphLeft
+ MOV EBX, EAX // X
+ ADD EBX, [EBP-16] // +OutW
+ MOV [EBP+4].TRect.Left, EBX // TxRect.Left = X+OutW
+ JMP @@centerY
+@@noGlyphLeft:
+ LOOP @@noGlyphTop
+ MOV EBX, EDX // Y
+ ADD EBX, [EBP-12] // +OutH
+ MOV [EBP+4].TRect.Top, EBX // TxRect.Top = Y+OutH
+ LOOP @@centerX // always JMP, ECX := -1
+@@noGlyphTop:
+ LOOP @@noGlyphRight
+ MOV EAX, [ESI].TDrawItemStruct.rcItem.Right
+ SUB EAX, [EBP-16] // -OutW -> X
+ MOV [EBP+4].TRect.Right, EAX
+@@centerY:
+ MOV EBX, [EBP-20] // H
+ SUB EBX, [EBP-12] // -OutH
+ JLE @@noGlyphRight
+ SAR EBX, 1
+ ADD EDX, EBX // Y = Y + (H-OutH)/2
+@@noGlyphRight:
+ LOOP @@noGlyphBottom
+ MOV EDX, [ESI].TDrawItemStruct.rcItem.Bottom
+ SUB EDX, [EBP-12] // -OutH -> Y
+ MOV [EBP+4].TRect.Bottom, EDX
+ LOOP @@centerX // always JMP, ECX := -1
+@@noGlyphBottom:
+ LOOP @@noGlyphOver
+@@centerX:
+ MOV EBX, [EBP-24] // W
+ SUB EBX, [EBP-16] // -OutW
+ SHR EBX, 1 // /2
+ ADD EAX, EBX // +EAX, X = X + (W-OutW)/2
+ JECXZ @@centerY
+@@noGlyphOver:
+ MOV ECX, [ESI].TDrawItemStruct.rcItem.Left
+ CMP EAX, ECX
+ JGE @@ok1
+ XCHG EAX, ECX
+@@ok1: CMP EDX, [ESI].TDrawItemStruct.rcItem.Top
+ {$IFDEF USE_CMOV}
+ CMOVL EDX, [ESI].TDrawItemStruct.rcItem.Top
+ {$ELSE}
+ JGE @@ok2
+ MOV EDX, [ESI].TDrawItemStruct.rcItem.Top
+@@ok2: {$ENDIF}
+
+ MOV ECX, [ESI].TDrawItemStruct.rcItem.Right
+ SUB ECX, EAX
+ CMP [EBP-16], ECX
+ JLE @@ok3
+ MOV [EBP-16], ECX // OutW := rcItem.Right - X;
+@@ok3: MOV ECX, [ESI].TDrawItemStruct.rcItem.Bottom
+ SUB ECX, EDX
+ CMP ECX, [EBP-12]
+ JGE @@ok4
+ MOV [EBP-12], ECX // OutH := rcItem.Bottom - Y;
+@@ok4:
+ POP EBX // EBX = G
+ TEST byte ptr [EDI].TControl.fBitBtnOptions, 1 //1 shl Ord(bboImageList)
+ JZ @@draw_bitmap
+ MOVZX ECX, word ptr [EDI].TControl.fGlyphCount
+ CMP word ptr [EDI].TControl.fGlyphCount + 2, BX
+ JLE @@no_add_glyphIdx
+ ADD ECX, EBX
+@@no_add_glyphIdx:
+ XOR EBX, EBX
+ PUSH ILD_TRANSPARENT // Flags = 1 (ILD_TRANSPARENT)
+ PUSH EBX // Blend = 0
+ PUSH -1 // Bk = CLR_NONE
+ PUSH EBX // 0
+ PUSH EBX // 0
+ PUSH EDX
+ PUSH EAX
+ PUSH [ESI].TDrawItemStruct.hDC
+ PUSH ECX
+ PUSH [EDI].TControl.fGlyphBitmap
+ CMP [EDI].TControl.fTransparent, BL
+ JNZ @@imgl_transp
+ MOV EAX, [EDI].TControl.fColor
+ CALL Color2RGB
+ MOV [ESP+32], EAX // Bk = Color2RGB(fColor)
+ MOV [ESP+40], EBX // Flags = 0
+@@imgl_transp:
+ INC EBX
+ CMP word ptr [EDI].TControl.fGlyphCount + 2, BX
+ JNZ @@draw_imagelist
+ DEC byte ptr [ESP+36+3] // $FF, CLR_DEFAULT = $FF000000
+ TEST byte ptr [ESI].TDrawItemStruct.itemState, ODS_FOCUS
+ JZ @@draw_imagelist
+ OR byte ptr [ESP+40], ILD_BLEND25 // Flags != 2
+@@draw_imagelist:
+ CALL ImageList_DrawEx
+ JMP @@glyph_drawn
+
+@@draw_bitmap:
+ PUSH EAX // PlaceHold for DC
+ PUSH EAX // PlaceHold for OldBmp
+ PUSH SRCCOPY
+ PUSH dword ptr [EBP-4] // ImgH
+ PUSH dword ptr [EBP-8] // ImgW
+ PUSH 0
+ PUSH EAX // PlaceHold for I
+ PUSH EAX // PlaceHold for DC
+ PUSH dword ptr [EBP-12] // OutH
+ PUSH dword ptr [EBP-16] // OutW
+ PUSH EDX // Y
+ PUSH EAX // X
+ PUSH [ESI].TDrawItemStruct.hDC
+
+ PUSH 0
+ CALL CreateCompatibleDC
+ MOV [ESP+48], EAX // save DC
+ MOV [ESP+20], EAX // place DC
+ PUSH [EDI].TControl.fGlyphBitmap
+ PUSH EAX
+ CALL SelectObject
+ MOV [ESP+44], EAX // save OldBitmap
+ XOR EAX, EAX
+ CMP [EDI].TControl.fGlyphCount, EBX
+ JLE @@no_incGlyIdx
+ MOV EAX, [EBP-8] // ImgW
+ IMUL EBX
+@@no_incGlyIdx:
+ MOV [ESP+24], EAX // place I
+ CALL StretchBlt
+ CALL FinishDC
+
+@@glyph_drawn:
+ MOV ESP, EBP
+ POP EBP
+
+@@noglyph:
+ TEST byte ptr[EDI].TControl.fBitBtnOptions, 4 //1 shl Ord(bboNoCaption)
+ JNZ @@noCaption
+
+ POP EAX
+ PUSH EAX
+ MOV EDX, [ESP].TRect.Right
+ CMP EDX, EAX
+ JLE @@noCaption
+ MOV EDX, [ESP].TRect.Bottom
+ CMP EDX, [ESP].TRect.Top
+ JLE @@noCaption
+
+ XOR EBX, EBX
+ PUSH EBX // > CapText
+ MOV EDX, ESP
+ MOV EAX, EDI
+ CALL TControl.GetCaption
+ PUSH EBX // > Bk
+ PUSH EBX // > Blend
+ CMP [EDI].TControl.fTransparent, BL
+ MOV BL, ETO_CLIPPED
+ JNZ @@drwTxTransparent
+ CMP [EDI].TControl.fGlyphLayout, glyphOver
+ JNZ @@drwTxOpaque
+@@drwTxTransparent:
+ PUSH TRANSPARENT
+ PUSH [ESI].TDrawItemStruct.hDC
+ CALL SetBkMode
+ MOV [ESP+4], EAX // Bk := SetBkMode( DIS.hDC, TRANSPARENT )
+ JMP @@drwTx1
+@@drwTxOpaque:
+ MOV BL, ETO_CLIPPED or ETO_OPAQUE
+ MOV EAX, [EDI].TControl.fColor
+ CALL Color2RGB
+ PUSH EAX
+ PUSH [ESI].TDrawItemStruct.hDC
+ CALL SetBkColor
+ POP ECX
+ PUSH EAX // Blend := SetBkColor(DIS.hDC,fColor)
+@@drwTx1:
+ PUSH 0 // > OldFont
+ PUSH 0 // > OldTextColor
+
+ PUSH 0 // push <nil>
+ MOV EDX, [ESP+20] // CapText
+ CALL EDX2PChar
+ PUSH dword ptr [EDX-4] // push Length(CapText)
+ PUSH EDX // push PChar(CapText)
+ LEA EAX, [ESP+32]
+ PUSH EAX // push @TxRect
+ PUSH EBX // push Flags
+
+ MOV EBX, [ESI].TDrawItemStruct.hDC
+
+ MOV ECX, [EDI].TControl.fFont
+ JECXZ @@drwTx_noFont
+ XCHG EAX, ECX
+ CALL TGraphicTool.GetHandle
+ PUSH EAX
+ PUSH EBX
+ CALL SelectObject
+ MOV [ESP+24], EAX // OldFont := SelectObject...
+@@drwTx_noFont:
+ MOV EAX, [EDI].TControl.fTextColor
+ CALL Color2RGB
+ PUSH EAX
+ PUSH EBX
+ CALL SetTextColor
+ MOV [ESP+20], EAX // OldTextColor := SetTextColor...
+
+ PUSH EAX
+ PUSH EAX
+ PUSH ESP
+ MOV ECX, [ESP+48] // ECX = CapText
+ XOR EAX, EAX
+ JECXZ @@drwTx0
+ MOV EAX, [ECX-4] // EAX = Length(CapText)
+@@drwTx0:
+ PUSH EAX
+ PUSH ECX
+ PUSH EBX
+ CALL GetTextExtentPoint32
+ POP ECX // ECX = TextSz.cx
+ POP EDX // EDX = TextSz.cy
+ MOV EAX, [ESP+40].TRect.Bottom
+ SUB EAX, [ESP+40].TRect.Top
+ SUB EAX, EDX
+ JGE @@yOk
+ XOR EAX, EAX
+@@yOk: SHR EAX, 1
+ ADD EAX, [ESP+40].TRect.Top
+ PUSH EAX // push Y
+ MOV EDX, [ESP+44].TRect.Right
+ MOV EAX, [ESP+44].TRect.Left // EAX = TxRect.Left
+ SUB EDX, EAX // EDX = W
+ PUSH EAX
+ CMP [EDI].TControl.fTextAlign, taRight
+ JL @@chk_X
+ JE @@alignR
+ SUB ECX, EDX
+ SAR ECX, 1
+ JMP @@alignC
+@@alignR:
+ ADD EAX, EDX
+@@alignC:
+ SUB EAX, ECX
+@@chk_X:POP EDX
+ CMP EAX, EDX
+ JGE @@xOk
+ XCHG EAX, EDX
+@@xOk: PUSH EAX // push X
+ PUSH EBX // push hDC
+ CALL ExtTextOut
+
+ PUSH EBX
+ CALL SetTextColor
+ POP ECX
+ JECXZ @@noRestoreFont
+ PUSH ECX
+ PUSH EBX
+ CALL SelectObject
+@@noRestoreFont:
+ POP ECX // Blend
+ JECXZ @@restoreBk
+ PUSH ECX
+ PUSH EBX
+ CALL SetBkColor
+ POP ECX
+ JMP @@delCaption
+@@restoreBk:
+ PUSH EBX
+ CALL SetBkMode
+@@delCaption:
+ CALL RemoveStr
+
+@@noCaption:
+ ADD ESP, 16
+
+@@exit_draw:
+ POP ESI
+ POP EDI
+ POP EBX
+ MOV AL, 1
+ RET
+
+@@noCN_DRAWITEM:
+ CMP word ptr [EDX].TMsg.message, WM_LBUTTONDOWN
+ JZ @@doDown
+ CMP word ptr [EDX].TMsg.message, WM_KEYDOWN
+ JNZ @@noWM_LBUTTONDOWN
+ CMP [EDX].TMsg.wParam, 32
+ JNZ @@noWM_LBUTTONDOWN
+@@doDown:
+ PUSH EDX
+ XCHG EBX, EAX
+
+ CALL @@fixed_proc
+ MOV ECX, [EBX].TControl.fRepeatInterval
+ JECXZ @@exit_LBUTTONDOWN
+ POP EDX
+ PUSH EDX
+ CMP word ptr [EDX].TMsg.message, WM_KEYDOWN
+ JZ @@not_SetTimer
+ PUSH 0
+ PUSH [EBX].TControl.fRepeatInterval
+ PUSH 1
+ PUSH [EBX].TControl.fHandle
+ CALL SetTimer
+@@exit_LBUTTONDOWN:
+@@not_SetTimer:
+ POP EDX
+ JMP @@invalidate
+
+@@noWM_LBUTTONDOWN:
+ CMP word ptr [EDX].TMsg.message, WM_LBUTTONUP
+ JE @@doKill1
+ CMP word ptr [EDX].TMsg.message, WM_KEYUP
+ JNE @@noWM_LBUTTONUP
+ PUSH 1
+ PUSH [EBX].TControl.fHandle
+ CALL KillTimer
+
+@@noWM_LBUTTONUP:
+ CMP word ptr [EDX].TMsg.message, WM_TIMER
+ JNZ @@noWM_TIMER
+
+ XCHG EBX, EAX
+ PUSH 0
+ PUSH 0
+ PUSH BM_GETSTATE
+ PUSH EBX
+ CALL TControl.Perform
+ {$IFDEF PARANOIA} DB $A8, 4 {$ELSE} TEST AL, BST_PUSHED {$ENDIF}
+ JNZ @@pushed
+ PUSH 1
+ PUSH [EBX].TControl.fHandle
+ CALL KillTimer
+ CALL ReleaseCapture
+ JMP @@noWM_TIMER
+@@fixed_proc:
+ TEST byte ptr [EBX].TControl.fBitBtnOptions, 8 // bboFixed
+ JZ @@not_fixed
+ {$IFDEF USE_FLAGS}
+ XOR [EBX].TControl.fFlagsG4, 1 shl G4_Checked
+ {$ELSE}
+ XOR [EBX].TControl.fChecked, 1
+ {$ENDIF}
+ MOV ECX, [EBX].TControl.fOnChangeCtl.TMethod.Code
+ {$IFDEF NIL_EVENTS}
+ JECXZ @@not_fixed
+ {$ENDIF}
+ MOV EAX, [EBX].TControl.fOnChangeCtl.TMethod.Data
+ MOV EDX, EBX
+ JMP ECX
+@@pushed:
+ CALL @@fixed_proc
+ MOV EAX, EBX
+ CALL TControl.DoClick
+@@invalidate:
+ XCHG EAX, EBX
+ CALL TControl.Invalidate
+@@noWM_TIMER:
+ XOR EAX, EAX
+ POP EBX
+@@not_fixed:
+end;
+{$ELSE PAS_VERSION} //Pascal
+function WndProcBitBtn( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var DIS: PDrawItemStruct;
+ IsDown, IsDefault, IsDisabled: Boolean;
+ Flags: Integer;
+ X, Y, W, H, ImgW, ImgH, OutW, OutH, I, G, Bk, Blend: Integer;
+ TxRect, FocusRect: TRect;
+ OldFont: HFont;
+ OldTextColor: TColor;
+ CapText, CapTxtOrig: KOLString;
+ TextSz: TSize;
+ DC: HDC;
+ OldBmp: HBitmap;
+ Handled: Boolean;
+begin
+ Result := False;
+ if (Msg.message = WM_LBUTTONDBLCLK) then
+ begin
+ Rslt := Self_.Perform( WM_LBUTTONDOWN, Msg.wParam, Msg.lParam );
+ Result := True; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ if (Msg.message = CN_DRAWITEM) then
+ begin
+ Result := True;
+ Rslt := 1;
+ DIS := Pointer( Msg.lParam );
+ IsDown := (DIS.itemState and ODS_SELECTED <> 0) or
+ {$IFDEF USE_FLAGS} (G4_Checked in Self_.fFlagsG4)
+ {$ELSE} Self_.fChecked {$ENDIF};
+ IsDefault := DIS.itemState and ODS_FOCUS <> 0;
+ IsDisabled := DIS.itemState and ODS_DISABLED <> 0;
+ G := 0;
+ if IsDown then G := {$IFDEF BITBTN_DISABLEDGLYPH2} 1 {$ELSE} 2 {$ENDIF};
+ if IsDisabled then G := {$IFDEF BITBTN_DISABLEDGLYPH2} 2 {$ELSE} 1 {$ENDIF};
+ if (G = 0) and IsDefault then G := 3;
+ if ((G = 0) or (G = 3)) and Self_.MouseInControl then G := 4;
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnBitBtnDraw ) then
+ {$ENDIF}
+ begin
+ if ( Self_.fCanvas <> nil ) then
+ Self_.fCanvas.SetHandle( DIS.hDC );
+ Handled := Self_.EV.fOnBitBtnDraw( Self_, G );
+ if ( Self_.fCanvas <> nil ) then
+ Self_.fCanvas.SetHandle( 0 );
+ if Handled then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ if not ( bboNoBorder in Self_.DF.fBitBtnOptions ) then
+ begin
+ if IsDefault and not( bboFocusRect in Self_.DF.fBitBtnOptions ) then
+ begin
+ Windows.FrameRect( DIS.hDC, DIS.rcItem, GetStockObject( {BLACK_BRUSH} DKGRAY_BRUSH ) );
+ InflateRect( DIS.rcItem, -1, -1 );
+ end;
+ if {$IFDEF USE_FLAGS} G3_Flat in Self_.fFlagsG3
+ {$ELSE} Self_.fFlat {$ENDIF} then
+ begin
+ if IsDown then
+ Flags := BDR_RAISEDINNER
+ else
+ Flags := 0; //EDGE_ETCHED;
+ DrawEdge( DIS.hDC, DIS.rcItem, Flags, BF_FLAT or BF_RECT );
+ //InflateRect( DIS.rcItem, -1, -1 );
+ end;
+ if {$IFDEF USE_FLAGS} not(G3_Flat in Self_.fFlagsG3)
+ {$ELSE} not Self_.fFlat {$ENDIF}
+ or {$IFDEF USE_FLAGS} (G3_MouseInCtl in Self_.fFlagsG3)
+ {$ELSE} Self_.fMouseInControl {$ENDIF} or IsDefault then
+ begin
+ if IsDown then
+ Flags := BDR_SUNKENOUTER or BDR_SUNKENINNER
+ else
+ Flags := BDR_RAISEDOUTER or BDR_RAISEDINNER;
+ DrawEdge( DIS.hDC, DIS.rcItem, Flags, BF_ADJUST or BF_RECT );
+ InflateRect( DIS.rcItem, -1, -1 );
+ end;
+ end;
+ TxRect := DIS.rcItem;
+ if Self_.DF.fGlyphBitmap <> 0 then
+ begin
+ ImgW := Self_.DF.fGlyphWidth;
+ ImgH := Self_.DF.fGlyphHeight;
+ if (ImgW > 0) and (ImgH > 0) then
+ begin
+ OutW := ImgW;
+ OutH := ImgH;
+ W := DIS.rcItem.Right - DIS.rcItem.Left;
+ H := DIS.rcItem.Bottom - DIS.rcItem.Top;
+ X := DIS.rcItem.Left;
+ Y := DIS.rcItem.Top;
+ if isDown and (Self_.DF.fGlyphLayout <> glyphOver) then
+ begin
+ Inc( X, Self_.TextShiftX );
+ Inc( Y, Self_.TextShiftY );
+ end;
+ case Self_.DF.fGlyphLayout of
+ glyphLeft:
+ begin
+ Y := Y + (H - OutH) div 2;
+ TxRect.Left := X + OutW;
+ end;
+ glyphTop:
+ begin
+ X := X + (W - OutW) div 2;
+ TxRect.Top := Y + OutH;
+ end;
+ glyphRight:
+ begin
+ X := DIS.rcItem.Right - OutW;
+ TxRect.Right := X;
+ Y := Y + (H - OutH) div 2;
+ end;
+ glyphBottom:
+ begin
+ Y := DIS.rcItem.Bottom - OutH;
+ TxRect.Bottom := Y;
+ X := X + (W - OutW) div 2;
+ end;
+ glyphOver:
+ begin
+ X := X + (W - OutW) div 2;
+ Y := Y + (H - OutH) div 2;
+ end;
+ end;
+ if X < DIS.rcItem.Left then
+ X := DIS.rcItem.Left;
+ if Y < DIS.rcItem.Top then
+ Y := DIS.rcItem.Top;
+ if X + OutW > DIS.rcItem.Right then
+ OutW := DIS.rcItem.Right - X;
+ if Y + OutH > DIS.rcItem.Bottom then
+ OutH := DIS.rcItem.Bottom - Y;
+
+ if bboImageList in Self_.DF.fBitBtnOptions then
+ begin
+ I := LoWord( Self_.DF.fGlyphCount );
+ if (HiWord( Self_.DF.fGlyphCount ) > G) then
+ I := I + G;
+ Flags := 0; // ILD_NORMAL
+ Blend := 0;
+ if {$IFDEF USE_FLAGS} not( G2_Transparent in Self_.fFlagsG2 )
+ {$ELSE} not Self_.fTransparent {$ENDIF} then
+ Bk := Color2RGB( Self_.fColor )
+ else
+ begin
+ Bk := Integer(CLR_NONE);
+ Flags := ILD_TRANSPARENT;
+ end;
+ if HiWord( Self_.DF.fGlyphCount ) = 1 then
+ begin
+ Blend := Integer(CLR_DEFAULT);
+ if IsDefault then
+ Flags := Flags or ILD_BLEND25;
+ end;
+ ImageList_DrawEx( Self_.DF.fGlyphBitmap, I, DIS.hDC, X, Y, 0, 0,
+ Bk, Blend, Flags );
+ end
+ else
+ begin
+ DC := CreateCompatibleDC( 0 );
+ OldBmp := SelectObject( DC, Self_.DF.fGlyphBitmap );
+
+ I := 0;
+ if Self_.DF.fGlyphCount > G then
+ I := I + G * ImgW;
+ StretchBlt( DIS.hDC, X, Y, OutW, OutH, DC, I, 0, ImgW, ImgH, SRCCOPY );
+
+ SelectObject( DC, OldBmp );
+ DeleteDC( DC );
+ end;
+ end;
+ end;
+ if not (bboNoCaption in Self_.DF.fBitBtnOptions) then
+ if (TxRect.Right > TxRect.Left) and (TxRect.Bottom > TxRect.Top) then
+ begin
+ CapText := Self_.Caption;
+ CapTxtOrig := CapText; /////////////////////////// added 19 Nov 2001
+ if Assigned( Self_.DF.FBitBtnGetCaption ) then
+ CapText := Self_.DF.FBitBtnGetCaption( Self_, CapText ); ////////////
+ Bk := 0;
+ Blend := 0;
+ Flags := ETO_CLIPPED;
+ if {$IFDEF USE_FLAGS} (G2_Transparent in Self_.fFlagsG2)
+ {$ELSE} Self_.fTransparent {$ENDIF}
+ or (Self_.DF.fGlyphLayout = glyphOver) then
+ Bk := SetBkMode( DIS.hDC, TRANSPARENT )
+ else
+ begin
+ Flags := Flags or ETO_OPAQUE;
+ Blend := SetBkColor( DIS.hDC, Color2RGB( Self_.fColor ) );
+ end; // Returned previous BkMode is either OPAQUE=1 or TRANSPARENT=2
+
+ OldFont := 0;
+ if ( Self_.fFont <> nil ) then
+ OldFont := SelectObject( DIS.hDC, Self_.fFont.Handle );
+ OldTextColor := SetTextColor( DIS.hDC, Color2RGB( Self_.fTextColor ) );
+
+ {$IFDEF UNICODE_CTRLS}Windows.GetTextExtentPoint32W
+ {$ELSE} Windows.GetTextExtentPoint32A
+ {$ENDIF}( DIS.hDC, PKOLChar( CapText ), Length( CapText ),
+ TextSz );
+ W := TxRect.Right - TxRect.Left;
+ H := TxRect.Bottom - TxRect.Top;
+ Y := TxRect.Top + (H - TextSz.cy) div 2;
+ case Self_.fTextAlign of
+ taLeft: X := TxRect.Left;
+ taCenter: X := TxRect.Left + (W - TextSz.cx) div 2;
+ else {taRight:} X := TxRect.Right - TextSz.cx;
+ end;
+ if isDown then
+ begin
+ Inc( X, Self_.TextShiftX );
+ Inc( Y, Self_.TextShiftY );
+ end;
+ if Y < 0 then
+ Y := 0;
+ if X < TxRect.Left then
+ X := TxRect.Left;
+
+ {$IFDEF UNICODE_CTRLS}
+ Windows.ExtTextOutW( DIS.hDC, X, Y, Flags, @TxRect,
+ PWideChar( CapText ), Length( CapText ), nil );
+ {$ELSE}
+ Windows.ExtTextOutA( DIS.hDC, X, Y, Flags, @TxRect,
+ PAnsiChar( CapText ), Length( CapText ), nil );
+ {$ENDIF}
+
+ if bboFocusRect in Self_.DF.fBitBtnOptions then
+ if IsDefault then
+ begin
+ FocusRect := TxRect;
+ //InflateRect( FocusRect, 1, 1 );
+ Windows.DrawFocusRect( DIS.hDC, FocusRect );
+ end;
+
+ //{$IFDEF NIL_EVENTS}
+ if Assigned( Self_.DF.FBitBtnExtDraw ) then // to provide underlying mnemonic characters
+ //{$ENDIF}
+ Self_.DF.FBitBtnExtDraw( Self_, DIS.hDC, X, Y, TxRect, CapText, CapTxtOrig,
+ OldTextColor ); /////////////////////////////////
+
+ SetTextColor( DIS.hDC, OldTextColor );
+ if OldFont <> 0 then
+ SelectObject( DIS.hDC, OldFont );
+
+ if Blend = 0 then
+ SetBkMode( DIS.hDC, Bk )
+ else
+ SetBkColor( DIS.hDC, Blend );
+ end;
+ end;
+ if (Msg.message = WM_LBUTTONDOWN) or (Msg.message = WM_KEYDOWN) and (Msg.wParam = 32) then
+ begin
+ if bboFixed in Self_.DF.fBitBtnOptions then
+ begin
+ {$IFDEF USE_FLAGS}
+ if G4_Checked in Self_.fFlagsG4 then
+ exclude( Self_.fFlagsG4, G4_Checked )
+ else include( Self_.fFlagsG4, G4_Checked );
+ {$ELSE} Self_.fChecked := not Self_.fChecked; {$ENDIF}
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnChangeCtl ) then
+ {$ENDIF}
+ Self_.EV.fOnChangeCtl( Self_ );
+ end;
+ if Self_.DF.fRepeatInterval > 0 then
+ begin
+ if Msg.message <> WM_KEYDOWN then
+ SetTimer( Self_.fHandle, 1, 400, nil );
+ Self_.Invalidate;
+ end;
+ end;
+
+ if Msg.message = WM_LBUTTONUP then
+ begin
+ if Self_.DF.fRepeatInterval > 0 then
+ KillTimer( Self_.fHandle, 1 );
+ end;
+
+ if Msg.message = WM_KILLFOCUS then // to repaint when focus lost
+ Self_.Invalidate;
+
+ if Msg.message = WM_TIMER then
+ begin
+ KillTimer( Self_.fHandle, 1 );
+ if bboFixed in Self_.DF.fBitBtnOptions then
+ begin
+ {$IFDEF USE_FLAGS}
+ if G4_Checked in Self_.fFlagsG4 then
+ exclude( Self_.fFlagsG4, G4_Checked )
+ else include( Self_.fFlagsG4, G4_Checked );
+ {$ELSE} Self_.fChecked := not Self_.fChecked; {$ENDIF}
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnChangeCtl ) then
+ {$ENDIF}
+ Self_.EV.fOnChangeCtl( Self_ );
+ end;
+ Self_.DoClick;
+ SetTimer( Self_.fHandle, 1, Self_.DF.fRepeatInterval, nil );
+ Self_.Invalidate;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewBitBtn( AParent: PControl; const Caption: AnsiString;
+ Options: TBitBtnOptions; Layout: TGlyphLayout; GlyphBitmap: HBitmap;
+ GlyphCount: Integer ): PControl;
+begin
+ new( Result, CreateBitBtn( AParent, Caption, Options, Layout, GlyphBitmap, GlyphCount ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:BitBtn';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF ASM_noVERSION} // todo: first correct asm version, then remove <no>
+{$ELSE PAS_VERSION} //Pascal
+function NewBitBtn( AParent: PControl; const Caption: KOLString;
+ Options: TBitBtnOptions; Layout: TGlyphLayout; GlyphBitmap: HBitmap;
+ GlyphCount: Integer ): PControl;
+var
+ B: TBitmapInfo;
+ W, H: Integer;
+ f: DWORD;
+begin
+ f := WS_VISIBLE or WS_CHILD or BS_OWNERDRAW or WS_TABSTOP or BS_NOTIFY;
+ Result := _NewControl( AParent, 'BUTTON', f, False,
+ {$IFDEF PACK_COMMANDACTIONS} ButtonActions_Packed
+ {$ELSE} @ButtonActions {$ENDIF} );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:BitBtn';
+ {$ENDIF}
+ {$IFDEF USE_FLAGS}
+ Result.fFlagsG5 := Result.fFlagsG5 +
+ [G5_IsButton, G5_IsBitBtn, G5_IgnoreDefault];
+ {$ELSE} Result.fIsButton := TRUE;
+ Result.fIsBitBtn := TRUE;
+ Result.fIgnoreDefault := TRUE;
+ {$ENDIF}
+ Result.aAutoSzX := 8;
+ Result.aAutoSzY := 8;
+ Result.DF.fBitBtnOptions := Options;
+ Result.DF.fGlyphLayout := Layout;
+ Result.DF.fGlyphBitmap := GlyphBitmap;
+ with Result.fBoundsRect do
+ begin
+ Bottom := Top + 22;
+ W := 0; H := 0;
+ if GlyphBitmap <> 0 then
+ begin
+ if bboImageList in Options then
+ ImageList_GetIconSize( GlyphBitmap, W, H )
+ else
+ begin
+ if GetObject( GlyphBitmap, Sizeof(B), @B ) > 0 then
+ begin
+ W := B.bmiHeader.biWidth;
+ H := B.bmiHeader.biHeight;
+ if GlyphCount = 0 then
+ GlyphCount := W div H;
+ if GlyphCount > 1 then
+ W := W div GlyphCount;
+ end;
+ end;
+ if W > 0 then
+ begin
+ if (Caption = '') or (Layout = glyphOver) then
+ begin
+ Right := Left + W;
+ Result.aAutoSzX := 0;
+ end
+ else
+ if Layout in [ glyphLeft, glyphRight ] then
+ begin
+ Right := Right + W;
+ Inc( Result.aAutoSzX, W );
+ end;
+ end;
+ if H > 0 then
+ begin
+ if Layout in [ glyphTop, glyphBottom ] then
+ begin
+ Bottom := Bottom + H;
+ Inc( Result.aAutoSzY, H );
+ end
+ else
+ begin
+ Bottom := Top + H;
+ Result.aAutoSzY := 0;
+ end;
+ end;
+ if not ( bboNoBorder in Options ) then
+ begin
+ if W > 0 then
+ begin
+ Inc( Right, 4 );
+ if Result.aAutoSzX > 0 then
+ Inc( Result.aAutoSzX, 4 );
+ end;
+ if H > 0 then
+ begin
+ Inc( Bottom, 4 );
+ if Result.aAutoSzY > 0 then
+ Inc( Result.aAutoSzY, 4 );
+ end;
+ end;
+ end;
+ Result.DF.fGlyphWidth := W;
+ Result.DF.fGlyphHeight := H;
+ end;
+ Result.DF.fGlyphCount := GlyphCount;
+ if AParent <> nil then
+ AParent.AttachProc( WndProc_DrawItem );
+ Result.AttachProc( WndProcBitBtn );
+ Result.fTextAlign := taCenter;
+ Result.Caption := Caption;
+ {$IFDEF ALL_BUTTONS_RESPOND_TO_ENTER}
+ Result.AttachProc( WndProcBtnReturnClick );
+ {$ENDIF}
+
+ {$IFDEF GRAPHCTL_XPSTYLES}
+ Attach_WM_THEMECHANGED(Result, XP_Themes_For_BitBtn);
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF USE_CONSTRUCTORS}
+
+//===================== Check box ========================//
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewCheckbox( AParent: PControl; const Caption: AnsiString ): PControl;
+begin
+ new( Result, CreateCheckbox( AParent, Caption ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:CheckBox';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewCheckbox( AParent: PControl; const Caption: KOLString ): PControl;
+begin
+ Result := NewButton( AParent, Caption );
+ with Result.fBoundsRect do
+ begin
+ Right := Left + 72;
+ end;
+ Result.fStyle.Value := WS_VISIBLE or WS_CHILD or
+ BS_AUTOCHECKBOX or WS_TABSTOP or BS_NOTIFY;
+ Result.aAutoSzX := 24;
+
+{$IFDEF GRAPHCTL_XPSTYLES}
+ Attach_WM_THEMECHANGED(Result, XP_Themes_For_CheckBox );
+{$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF USE_CONSTRUCTORS}
+
+function NewCheckBox3State( AParent: PControl; const Caption: KOLString ): PControl;
+begin
+ Result := NewCheckbox( AParent, Caption );
+ Result.fStyle.Value := Result.fStyle.Value and not BS_AUTOCHECKBOX or BS_AUTO3STATE;
+end;
+
+//===================== Radiobox ========================//
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure ClickRadio( Sender:PObj );
+var Self_:PControl;
+ {$IFDEF USE_FLAGS}
+ i: Integer;
+ C: PControl;
+ NewState: Boolean;
+ {$ENDIF}
+begin
+ Self_ := PControl( Sender );
+ if Self_.FParent <> nil then
+ {$IFDEF USE_FLAGS}
+ begin
+ for i := 0 to Self_.FParent.ChildCount-1 do
+ begin
+ C := Self_.FParent.Children[i];
+ if G5_IsButton in C.fFlagsG5 then
+ if C.fStyle.f0_Style and BS_RADIOBUTTON <> 0 then
+ begin
+ NewState := C = Self_;
+ if NewState <> C.Checked then
+ C.Checked := NewState;
+ end;
+ end;
+ end;
+ {$ELSE}
+ CheckRadioButton( Self_.fParent.fHandle,
+ Self_.fParent.PropInt[ @RADIO_1ST ],
+ Self_.fParent.PropInt[ @RADIO_LAST ],
+ Self_.fMenu );
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewRadiobox( AParent: PControl; const Caption: AnsiString ): PControl;
+begin
+ new( Result, CreateRadiobox( AParent, Caption ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Radiobox';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewRadiobox( AParent: PControl; const Caption: KOLString ): PControl;
+begin
+ Result := NewCheckbox( AParent, Caption );
+ Result.fStyle.Value := WS_VISIBLE or WS_CHILD or
+ BS_RADIOBUTTON or WS_TABSTOP or WS_GROUP or BS_NOTIFY;
+ Result.PP.fControlClick := ClickRadio;
+ if AParent <> nil then
+ begin
+ {$IFDEF USE_FLAGS}
+ if not(G1_HasRadio in AParent.fFlagsG1) then
+ begin
+ include( AParent.fFlagsG1, G1_HasRadio );
+ Result.SetRadioChecked;
+ end;
+ {$ELSE}
+ AParent.PropInt[ @RADIO_LAST ] := Result.fMenu;
+ if AParent.PropInt[ @RADIO_1ST ] = 0 then
+ begin
+ AParent.PropInt[ @RADIO_1ST ] := Result.fMenu;
+ Result.SetRadioChecked;
+ end;
+ {$ENDIF}
+ end;
+{$IFDEF GRAPHCTL_XPSTYLES}
+ Attach_WM_THEMECHANGED(Result, XP_Themes_For_RadioBox);
+{$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF USE_CONSTRUCTORS}
+
+//===================== Label ========================//
+
+{$ENDIF WIN_GDI}
+{$IFNDEF USE_CONSTRUCTORS}
+{$ENDIF not USE_CONSTRUCTORS}
+{$IFDEF USE_CONSTRUCTORS}
+function NewLabel( AParent: PControl; const Caption: AnsiString ): PControl;
+begin
+ new( Result, CreateLabel( AParent, Caption ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Label';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF GDI}
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function NewLabel( AParent: PControl; const Caption: KOLString ): PControl;
+begin
+ Result := _NewControl( AParent, 'STATIC', WS_VISIBLE or WS_CHILD or
+ SS_LEFTNOWORDWRAP or SS_NOPREFIX or SS_NOTIFY,
+ False, {$IFDEF PACK_COMMANDACTIONS} LabelActions_Packed
+ {$ELSE} @LabelActions {$ENDIF} );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Label';
+ {$ENDIF}
+ Result.aAutoSzX := 1;
+ Result.aAutoSzY := 1;
+ {$IFDEF USE_FLAGS}
+ Result.fFlagsG1 := Result.fFlagsG1 + [G1_SizeRedraw, G1_IsStaticControl];
+ {$ELSE} Result.fSizeRedraw := True;
+ Inc( Result.fIsStaticControl );
+ {$ENDIF}
+ with Result.fBoundsRect do
+ Bottom := Top + 22; //Right := Left + 64 {done in _NewControl};
+ Result.Caption := Caption;
+{$IFDEF GRAPHCTL_XPSTYLES}
+ Attach_WM_THEMECHANGED(Result, XP_Themes_For_Label);
+{$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+
+{$IFDEF _X_}
+{$IFDEF GTK}
+PROCEDURE LabelSetTextAlign( Self_: PControl );
+BEGIN
+ gtk_misc_set_alignment( GTK_MISC( Self_.fCaptionHandle ), HorAlignments[ Self_.fTextAlign ],
+ VerAlignments[ Self_.fVerticalAlign ] );
+END;
+
+FUNCTION NewLabel( AParent: PControl; const Caption: KOLString ): PControl;
+BEGIN
+ Result := _NewControl( AParent, 'STATIC', WS_VISIBLE or WS_CHILD or
+ SS_LEFTNOWORDWRAP or SS_NOPREFIX or SS_NOTIFY,
+ False, gtk_label_new( PAnsiChar( String( Caption ) ) ),
+ TRUE );
+ Result.fGetCaption := getLabelCaption;
+ Result.fSetCaption := setLabelCaption;
+ {$IFDEF USE_FLAGS} include( Result.fFlagsG1, G1_IsStaticControl );
+ {$ELSE} Inc( Result.fIsStaticControl ); {$ENDIF}
+ Result.fSetTextAlign := LabelSetTextAlign;
+ Result.fTextAlign := taCenter;
+ Result.TextAlign := taLeft;
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+{$ENDIF USE_CONSTRUCTORS}
+
+{$IFDEF WIN_GDI}
+//===================== word wrap Label ========================//
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewWordWrapLabel( AParent: PControl; const Caption: AnsiString ): PControl;
+begin
+ new( Result, CreateWordWrapLabel( AParent, Caption ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:WordWrapLabel';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewWordWrapLabel( AParent: PControl; const Caption: KOLString ): PControl;
+begin
+ Result := NewLabel( AParent, Caption );
+ {$IFDEF USE_FLAGS} include( Result.fFlagsG1, G1_WordWrap );
+ {$ELSE} Result.fWordWrap := TRUE; {$ENDIF}
+ with Result.fBoundsRect do
+ begin
+ Bottom := Top + 44;
+ end;
+ Result.fStyle.Value := Result.fStyle.Value and not SS_LEFTNOWORDWRAP;
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF USE_CONSTRUCTORS}
+
+//===================== Label Effect ========================//
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewLabelEffect( AParent: PControl; const Caption: AnsiString; ShadowDeep: Integer ): PControl;
+begin
+ new( Result, CreateLabelEffect( AParent, Caption, ShadowDeep ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:LabelEffect';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewLabelEffect( AParent: PControl; const Caption: KOLString; ShadowDeep: Integer ): PControl;
+begin
+ Result := NewLabel( AParent, '' );
+ {$IFDEF USE_FLAGS} exclude( Result.fFlagsG1, G1_IsStaticControl );
+ {$ELSE} Dec( Result.fIsStaticControl ); { ñíîâà 0 ! } {$ENDIF}
+ Result.AttachProc( WndProcLabelEffect );
+ Result.Caption := Caption;
+ Result.AttachProc( WndProcDoEraseBkgnd );
+ Result.fTextAlign := taCenter;
+ Result.fTextColor := clWindowText;
+ Result.DF.fShadowDeep := ShadowDeep;
+ {$IFDEF USE_FLAGS} include( Result.fFlagsG1, G1_IgnoreWndCaption );
+ {$ELSE} Result.fIgnoreWndCaption := True; {$ENDIF}
+ with Result.fBoundsRect do
+ begin
+ Bottom := Top + 40;
+ end;
+ Result.DF.fColor2 := clNone;
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF USE_CONSTRUCTORS}
+
+//===================== Paint box ========================//
+{$ENDIF WIN_GDI}
+{$IFDEF USE_CONSTRUCTORS}
+function NewPaintbox( AParent: PControl ): PControl;
+begin
+ new( Result, CreatePaintBox( AParent ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Paintbox';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF GDI}
+
+{$UNDEF ASM_LOCAL}
+{$IFNDEF GRAPHCTL_XPSTYLES}
+ {$IFDEF ASM_VERSION}
+ {$DEFINE ASM_LOCAL}
+ {$ENDIF PAS_VERSION}
+{$ENDIF GRAPHCTL_XPSTYLES}
+
+{$IFDEF ASM_LOCAL}
+function NewPaintbox( AParent: PControl ): PControl;
+asm
+ XOR EDX, EDX
+ CALL NewLabel
+ ADD [EAX].TControl.fBoundsRect.Bottom, 64-22
+end;
+{$ELSE ASM_LOCAL} //Pascal
+function NewPaintbox( AParent: PControl ): PControl;
+begin
+{$IFDEF GRAPHCTL_XPSTYLES}
+ Result := _NewControl( AParent, 'STATIC', WS_VISIBLE or WS_CHILD {or
+ SS_LEFTNOWORDWRAP or SS_NOPREFIX }or SS_NOTIFY,
+ False, {$IFDEF PACK_COMMANDACTIONS} LabelActions_Packed
+ {$ELSE} @LabelActions {$ENDIF} );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:PaintBox';
+ {$ENDIF}
+ {$IFDEF USE_FLAGS}
+ include( Result.fFlagsG1, G1_SizeRedraw );
+ if G2_Transparent in Result.fFlagsG2 then
+ include( Result.fFlagsG2, G2_ClassicTransparent )
+ else exclude( Result.fFlagsG2, G2_ClassicTransparent );
+ {$ELSE} Result.fSizeRedraw := True;
+ Result.fClassicTransparent := Result.fTransparent;
+ {$ENDIF}
+ Result.fControlClassName := 'obj_PAINT';
+{$ELSE}
+ Result := NewLabel( AParent, '' );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Paintbox';
+ {$ENDIF}
+ with Result.fBoundsRect do
+ begin
+ Bottom := Top + 64; //Right := Left + 64 {done in NewLabel};
+ end;
+{$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+
+{$IFDEF _X_}
+{$IFDEF GTK}
+FUNCTION NewPaintbox( AParent: PControl ): PControl;
+BEGIN
+ Result := NewLabel( AParent, '' );
+ Result.Height := 64;
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$ENDIF USE_CONSTRUCTORS}
+{$IFDEF WIN_GDI}
+
+{$IFDEF _D2}
+function SetBrushOrgEx(DC: HDC; X, Y: Integer; PrevPt: PPoint): BOOL; stdcall;
+external gdi32 name 'SetBrushOrgEx';
+{$ENDIF}
+
+{$IFDEF ASM_VERSION}{$ELSE ASM_VERSION PAS_VERSION}
+function WndProcDoEraseBkgnd( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var DC: HDC;
+ R: TRect;
+begin
+ Result := FALSE;
+ if Msg.message = WM_ERASEBKGND then
+ begin
+ Self_.CreateChildWindows;
+ if Self_.Transparent then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ DC := Msg.wParam;
+ SetBkMode( DC, OPAQUE );
+ SetBkColor( DC, Color2RGB( Self_.fColor ) );
+ SetBrushOrgEx( DC, 0, 0, nil );
+ GetClientRect( Self_.fHandle, R );
+ Windows.FillRect( DC, R, Global_GetCtlBrushHandle( Self_ ) );
+ Rslt := 1;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+function WndProcImageShow( Sender: PControl; var Msg: TMsg;
+ var Rslt: Integer ): Boolean;
+var PaintStruct: TPaintStruct;
+ IL: PImageList;
+ OldPaintDC: HDC;
+ {$IFDEF TEST_IL}
+ B: PBitmap;
+ {$ENDIF TEST_IL}
+begin
+ Result := FALSE;
+ if (Msg.message = WM_PAINT) or (Msg.message = WM_PRINT) then
+ begin
+ OldPaintDC := Sender.fPaintDC;
+ Sender.fPaintDC := Msg.wParam;
+ if Sender.fPaintDC = 0 then
+ Sender.fPaintDC := BeginPaint( Sender.fHandle, PaintStruct );
+ IL := Sender.ImageListNormal;
+ if IL <> nil then
+ begin
+ IL.DrawingStyle := [ dsTransparent ];
+ {$IFDEF TEST_IL}
+ B := NewBitmap( 0, 0 );
+ B.Handle := IL.GetBitmap;
+ B.SaveToFile( GetStartDir + 'test_IL_show.bmp' );
+ B.ReleaseHandle;
+ B.Free;
+ {$ENDIF TEST_IL}
+ IL.Draw( Sender.fCurIndex, Sender.fPaintDC, Sender.fClientLeft, Sender.fClientTop );
+ Result := TRUE;
+ end;
+ if Msg.wParam = 0 then
+ EndPaint( Sender.fHandle, PaintStruct );
+ Sender.fPaintDC := OldPaintDC;
+ Rslt := 0; {Result := True;} Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+end;
+
+function NewImageShow( AParent: PControl; AImgList: PImageList;
+ ImgIdx: Integer ): PControl;
+var W, H: Integer;
+begin
+ Result := NewLabel( AParent, '' );
+ Result.ImageListNormal := AImgList;
+ Result.AttachProc( WndProcImageShow );
+ Result.AttachProc( WndProcDoEraseBkgnd );
+ W := 32; H := 32;
+ if AImgList <> nil then
+ begin
+ W := AImgList.ImgWidth;
+ H := AImgList.ImgHeight;
+ end;
+ with Result.fBoundsRect do
+ begin
+ Right := Left + W;
+ Bottom := Top + H;
+ end;
+ Result.CurIndex := ImgIdx;
+end;
+
+//===================== Scrollbar ========================//
+const
+ KSB_INITIALIZE = WM_USER + 10000;
+ KSB_KEY = $3232;
+
+function WndProcScrollBarParent( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var
+ Bar: PControl;
+ SI: TScrollInfo;
+ NewPos: Integer;
+ AllowChange: Boolean;
+ Cmd: Word;
+
+begin
+ Result := False;
+ case Msg.message of
+ WM_HSCROLL, WM_VSCROLL:
+ if (Msg.lParam <> 0) then begin
+ {$IFDEF USE_PROP}
+ Bar := Pointer(GetProp(Msg.lParam, ID_SELF));
+ {$ELSE}
+ Bar := Pointer( GetWindowLong( Msg.lParam, GWL_USERDATA ) );
+ {$ENDIF}
+ if (Bar <> nil) then begin
+ ZeroMemory(@SI, SizeOf(SI));
+ SI.cbSize := SizeOf(SI);
+ SI.fMask := SIF_RANGE or SIF_POS or SIF_TRACKPOS or SIF_PAGE;
+ Bar.SBGetScrollInfo(SI);
+
+ Cmd := Msg.wParam and $0000FFFF;
+ case Cmd of
+ SB_BOTTOM: NewPos := SI.nMax;
+ SB_TOP: NewPos := SI.nMin;
+ SB_LINEDOWN: NewPos := SI.nPos + 1;
+ SB_LINEUP: NewPos := SI.nPos - 1;
+ SB_PAGEDOWN: NewPos := SI.nPos + Integer(SI.nPage);
+ SB_PAGEUP: NewPos := SI.nPos - Integer(SI.nPage);
+ {!ecm}
+ SB_THUMBPOSITION,SB_THUMBTRACK: NewPos := SI.nTrackPos;
+ SB_ENDSCROLL: NewPos := SI.nPos;
+ {/!ecm}
+ else Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+
+ if (NewPos > SI.nMax - Integer(SI.nPage) + 1) then
+ NewPos := SI.nMax - Integer(SI.nPage) + 1;
+ if (NewPos < SI.nMin) then
+ NewPos := SI.nMin;
+
+ AllowChange := True;
+ {$IFDEF NIL_EVENTS}
+ if Assigned(Bar.EV.fOnSBBeforeScroll) then
+ {$ENDIF}
+ Bar.EV.fOnSBBeforeScroll(Bar, SI.nPos, NewPos, Cmd, AllowChange);
+ if AllowChange then
+ SI.nPos := NewPos
+ else
+ SI.nTrackPos := SI.nPos;
+ Bar.DF.fSBPosition := SI.nPos;
+ Bar.DF.fSBPosition := Bar.SBSetScrollInfo(SI);
+ if AllowChange
+ {$IFDEF NIL_EVENTS} and Assigned(Bar.EV.fOnSBScroll) {$ENDIF} then
+ Bar.EV.fOnSBScroll(Bar, Cmd);
+ end;
+ end;
+ end;
+end;
+
+function NewScrollBar( AParent: PControl; BarSide: TScrollerBar ): PControl;
+const SBS_Directions: array[ TScrollerBar ] of DWORD = ( SBS_HORZ or SBS_BOTTOMALIGN,
+ SBS_VERT or SBS_RIGHTALIGN );
+begin
+ Result := _NewCommonControl( AParent, 'SCROLLBAR',
+ WS_VISIBLE or WS_CHILD or SBS_Directions[ BarSide ],
+ False, {$IFDEF PACK_COMMANDACTIONS} PAnsiChar( OTHER_ACTIONS )
+ {$ELSE} nil {$ENDIF} );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:ScrollBar';
+ {$ENDIF}
+ {!ecm} Result.GetWindowHandle; {/!ecm}
+ Result.DetachProc(WndProcCtrl);
+ Result.fLookTabKeys := [tkTab];
+
+ //#ecm Result.AttachProc(WndProcScrollBar);
+ AParent.AttachProc(WndProcScrollBarParent);
+end;
+
+//===================== Scrollbox ========================//
+function WndProcScrollBox( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var Bar: DWORD;
+ SI: TScrollInfo;
+ OldNotifyProc: pointer;
+begin
+
+ case Msg.message of
+ WM_HSCROLL: Bar := SB_HORZ;
+ WM_VSCROLL: Bar := SB_VERT;
+ WM_SIZE: begin
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Sender.PP.fNotifyChild ) then
+ {$ENDIF}
+ Sender.PP.fNotifyChild( Sender, nil );
+ Result := FALSE; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ else Result := FALSE; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+
+ SI.cbSize := Sizeof( SI );
+ SI.fMask := SIF_RANGE or SIF_POS or SIF_PAGE or
+ {$IFDEF F_P}$10{$ELSE}SIF_TRACKPOS{$ENDIF};
+ GetScrollInfo( Sender.fHandle, Bar, SI );
+ SI.fMask := SIF_POS;
+ case LoWord( Msg.wParam ) of
+ SB_BOTTOM: SI.nPos := SI.nMax;
+ SB_TOP: SI.nPos := SI.nMin;
+ SB_LINEDOWN: Inc( SI.nPos, Sender.DF.fScrollLineDist[ Bar ] );
+ SB_LINEUP: Dec( SI.nPos, Sender.DF.fScrollLineDist[ Bar ] );
+ SB_PAGEDOWN: Inc( SI.nPos, Max( SI.nPage, 1 ) );
+ SB_PAGEUP: Dec( SI.nPos, Max( SI.nPage, 1 ) );
+ SB_THUMBTRACK:SI.nPos := SI.nTrackPos;
+ end;
+ if SI.nPos > SI.nMax { - Integer( SI.nPage ) } then
+ SI.nPos := SI.nMax { - Integer( SI.nPage ) };
+ if SI.nPos < SI.nMin then
+ SI.nPos := SI.nMin;
+ SetScrollInfo( Sender.fHandle, Bar, SI, TRUE );
+
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Sender.PP.fScrollChildren ) then
+ {$ENDIF}
+ begin
+ OldNotifyProc := @ Sender.PP.fNotifyChild;
+ Sender.PP.fNotifyChild := @DummyObjProc;
+ Sender.PP.fScrollChildren( Sender );
+ Sender.PP.fNotifyChild := OldNotifyProc;
+ end;
+
+ SetScrollInfo( Sender.fHandle, Bar, SI, TRUE );
+ Result := FALSE;
+end;
+
+function NewScrollBox( AParent: PControl; EdgeStyle: TEdgeStyle;
+ Bars: TScrollerBars ): PControl;
+var SBFlag: Integer;
+begin
+ SBFlag := EdgeStyles[ EdgeStyle ];
+ if sbHorizontal in Bars then
+ SBFlag := SBFlag or WS_HSCROLL;
+ if sbVertical in Bars then
+ SBFlag := SBFlag or WS_VSCROLL;
+
+ Result := _NewControl( AParent, 'ScrollBox', WS_VISIBLE or WS_CHILD or
+ SBFlag, EdgeStyle = esLowered,
+ {$IFDEF PACK_COMMANDACTIONS} PAnsiChar( OTHER_ACTIONS )
+ {$ELSE} nil {$ENDIF} );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:ScrollBox';
+ {$ENDIF}
+ Result.AttachProc( WndProcForm ); //!!!
+ Result.AttachProc( WndProcScrollBox );
+ Result.AttachProc( WndProcDoEraseBkgnd );
+ {$IFDEF USE_FLAGS} include( Result.fFlagsG3, G3_IsControl );
+ {$ELSE} Result.fIsControl := TRUE; {$ENDIF}
+end;
+
+function Scrollbar_GetMinPos( sb: PControl ): Integer;
+begin
+ Result := sb.SBMax;
+end;
+procedure Scrollbar_SetMinPos( sb: PControl; m: Integer );
+begin
+ sb.SBMin := m;
+end;
+procedure Scrollbar_SetAll( sb: PControl; min, max, pg, cur: Integer );
+begin
+ sb.SBMin := min;
+ sb.SBMax := max;
+ sb.SBPageSize := pg;
+ sb.SBPosition := cur;
+end;
+function Scrollbar_GetMaxPos( sb: PControl ): Integer;
+begin
+ Result := sb.SBMax;
+end;
+procedure Scrollbar_SetMaxPos( sb: PControl; m: Integer );
+begin
+ sb.SBMax := m;
+end;
+function Scrollbar_GetCurPos( sb: PControl ): Integer;
+begin
+ Result := sb.SBPosition;
+end;
+procedure Scrollbar_SetCurPos( sb: PControl; newp: Integer );
+begin
+ sb.SBPosition := newp;
+end;
+procedure Scrollbar_SetPageSz( sb: PControl; psz: Integer );
+begin
+ sb.SBPageSize := psz;
+end;
+function Scrollbar_GetPageSz( sb: PControl ): Integer;
+begin
+ Result := sb.SBPageSize;
+end;
+procedure Scrollbar_SetLineSz( sb: PControl; lnz: Integer );
+begin
+ //
+end;
+function Scrollbar_GetLineSz( sb: PControl ): Integer;
+begin
+ Result := 1;
+end;
+
+function WndProcNotifyParentAboutResize( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var P: PControl;
+begin
+ if (Msg.message = WM_SIZE) or (Msg.message = WM_MOVE) or (Msg.message = CM_SHOW) then
+ begin
+ P := Sender.Parent;
+ if P <> nil then
+ {$IFDEF NIL_EVENTS}
+ if Assigned( P.PP.fNotifyChild ) then
+ {$ENDIF}
+ P.PP.fNotifyChild( P, nil );
+ end
+ else
+ if Msg.message = WM_SHOWWINDOW then
+ PostMessage( Sender.fHandle, CM_SHOW, 0, 0 );
+ Result := FALSE;
+end;
+
+procedure CalcMinMaxChildren( Self_: PControl; var SzR: TRect );
+var I: Integer;
+ C: PControl;
+ R: TRect;
+begin
+ Szr := MakeRect( 0, 0, 0, 0 );
+ for I := 0 to Self_.fChildren.fCount - 1 do
+ begin
+ C := Self_.fChildren.Items[ I ];
+ if C.ToBeVisible then
+ begin
+ R := C.BoundsRect;
+ if (SzR.Left = SzR.Right) or (R.Left < SzR.Left) or (R.Right > SzR.Right) then
+ begin
+ if SzR.Left = SzR.Right then
+ begin
+ SzR.Left := R.Left;
+ SzR.Right := R.Right;
+ end
+ else
+ begin
+ if R.Left < SzR.Left then SzR.Left := R.Left;
+ if R.Right > SzR.Right then SzR.Right := R.Right;
+ end;
+ end;
+ if (SzR.Top = SzR.Bottom) or (R.Top < SzR.Top) or (R.Bottom > SzR.Bottom) then
+ begin
+ if SzR.Top = SzR.Bottom then
+ begin
+ SzR.Top := R.Top;
+ SzR.Bottom := R.Bottom;
+ end
+ else
+ begin
+ if R.Top < SzR.Top then SzR.Top := R.Top;
+ if R.Bottom > SzR.Bottom then SzR.Bottom := R.Bottom;
+ end;
+ end;
+ end;
+ end;
+ Dec( SzR.Left, Self_.Border );
+ Inc( SzR.Right, Self_.Border - 1 );
+ Dec( SzR.Top, Self_.Border );
+ Inc( SzR.Bottom, Self_.Border - 1 );
+end;
+
+procedure NotifyScrollBox( Self_, Child: PControl );
+var SI: TScrollInfo;
+
+ procedure GetSetScrollInfo( SBar: DWORD; WH, R_RightBottom, SzR_LeftTop, SzR_RightBottom: Integer );
+ {$IFDEF SBOX_OLDPOS} var OldPos: Double; {$ENDIF}
+ begin
+ {$IFDEF SBOX_OLDPOS} OldPos := 0; {$ENDIF}
+ if not GetScrollInfo( Self_.fHandle, SBar, SI ) then
+ begin
+ SI.nMin := 0;
+ SI.nMax := Max( R_RightBottom - 1, SzR_RightBottom - 1 );
+ end
+ else
+ begin
+ {$IFDEF SBOX_OLDPOS}
+ if SI.nMax > SI.nMin then
+ begin
+ OldPos := (SI.nPos - SI.nMin) / (SI.nMax - SI.nMin);
+ SI.nMin := 0;
+ SI.nMax := Max( R_RightBottom - 1, SzR_RightBottom - 1 );
+ if SzR_LeftTop < 0 then
+ SI.nMax := Max( R_RightBottom - SzR_LeftTop - 1, WH - 1 );
+ end
+ else
+ begin
+ SI.nMin := 0;
+ SI.nMax := Max( R_RightBottom - 1, SzR_RightBottom - 1 );
+ end;
+ {$ENDIF}
+ SI.nMin := 0; {!ecm}
+ SI.nMax := SzR_RightBottom - SzR_LeftTop; {!ecm}
+ end;
+ {$IFDEF SBOX_OLDPOS}
+ SI.nPos := SI.nMin + Round( (SI.nMax - SI.nMin) * OldPos );
+ {$ELSE}
+ SI.nPos := - SzR_LeftTop;
+ {$ENDIF}
+ SI.nPage := R_RightBottom;
+ SetScrollInfo( Self_.fHandle, SBar, SI, TRUE );
+ end;
+
+var W, H: Integer;
+ SzR: TRect;
+ R: TRect;
+begin
+ if ( Child <> nil ) then
+ begin
+ Child.AttachProc( WndProcNotifyParentAboutResize ); Exit; {>>>>>>>>>>>>>>}
+ end;
+ CalcMinMaxChildren( Self_, SzR );
+ W := SzR.Right - SzR.Left;
+ H := SzR.Bottom - SzR.Top;
+
+ R := Self_.ClientRect;
+ if (R.Right = 0) or (R.Bottom = 0) then
+ Exit; // for case when form is minimized {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ SI.cbSize := sizeof( SI );
+ SI.fMask := SIF_RANGE or SIF_PAGE or SIF_POS;
+
+ SI.cbSize := sizeof( SI );
+ SI.fMask := SIF_RANGE or SIF_PAGE or SIF_POS;
+
+ GetSetScrollInfo( SB_HORZ, W, R.Right, SzR.Left, SzR.Right );
+{+ecm}R := Self_.ClientRect;{/+ecm}
+ GetSetScrollInfo( SB_VERT, H, R.Bottom, SzR.Top, SzR.Bottom );
+{+ecm} {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.PP.fScrollChildren ) then
+ {$ENDIF}
+ Self_.PP.fScrollChildren(Self_); {/+ecm}
+end;
+
+procedure ScrollChildren( _Self_: PControl );
+var SzR, R: TRect;
+ I, Xpos, Ypos: Integer;
+ OldNotifyProc: Pointer;
+ C: PControl;
+ DeltaX, DeltaY: Integer;
+
+begin
+
+ CalcMinMaxChildren( _Self_, SzR );
+ Xpos := GetScrollPos( _Self_.fHandle, SB_HORZ );
+ Ypos := GetScrollPos( _Self_.fHandle, SB_VERT );
+
+ DeltaX := -Xpos - SzR.Left;
+ DeltaY := -Ypos - SzR.Top;
+
+ if (DeltaX <> 0) or (DeltaY <> 0) then
+ begin
+
+ OldNotifyProc := @ _Self_.PP.fNotifyChild;
+ _Self_.PP.fNotifyChild := @DummyObjProc;
+
+ for I := 0 to _Self_.fChildren.fCount - 1 do
+ begin
+ C := _Self_.fChildren.Items[ I ];
+ R := C.BoundsRect;
+ OffsetRect( R, DeltaX, DeltaY );
+ C.BoundsRect := R;
+ end;
+
+ _Self_.PP.fNotifyChild := OldNotifyProc;
+ CalcMinMaxChildren( _Self_, R );
+ if //(SzR.Left <> R.Left) or (SzR.Top <> R.Top) or
+ //(Szr.Right <> R.Right) or (SzR.Bottom <> R.Bottom)
+ ((SzR.Right - SzR.Left) <> (R.Right - R.Left)) or
+ ((SzR.Bottom - SzR.Top) <> (R.Bottom - R.Top))
+ then
+ {$IFDEF NIL_EVENTS}
+ if Assigned( _Self_.PP.fNotifyChild ) then
+ {$ENDIF}
+ _Self_.PP.fNotifyChild( _Self_, nil );
+
+ end;
+
+end;
+
+function NewScrollBoxEx( AParent: PControl; EdgeStyle: TEdgeStyle ): PControl;
+begin
+ Result := NewScrollBox( AParent, EdgeStyle, [ ] );
+ Result.PP.fNotifyChild := NotifyScrollBox;
+ Result.PP.fScrollChildren := ScrollChildren;
+ Result.DF.fScrollLineDist[ 0 ] := 16;
+ Result.DF.fScrollLineDist[ 1 ] := 16;
+end;
+
+function WndProcOnScroll( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var Bar: TScrollerBar;
+begin
+ Bar := sbHorizontal; //0
+ if Msg.message = WM_VSCROLL then
+ Bar := sbVertical
+ else
+ if Msg.message <> WM_HSCROLL then
+ begin
+ Result := FALSE; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Sender.EV.fOnScroll ) then
+ {$ENDIF}
+ Sender.EV.fOnScroll( Sender, Bar, LoWord( Msg.wParam ),
+ HiWord( Msg.wParam ) );
+ Result := FALSE;
+end;
+
+procedure TControl.SetOnScroll(const Value: TOnScroll);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .FOnScroll := Value;
+ AttachProc( @ WndProcOnScroll );
+end;
+
+//===================== Groupbox ========================//
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewGroupbox( AParent: PControl; const Caption: AnsiString ): PControl;
+begin
+ new( Result, CreateGroupbox( AParent, Caption ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Groupbox';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function NewGroupbox( AParent: PControl; const Caption: KOLString ): PControl;
+begin
+ Result := _NewControl( AParent, 'BUTTON',
+ WS_CHILD
+ or WS_CLIPSIBLINGS
+ or WS_CLIPCHILDREN
+ or WS_VISIBLE
+ or BS_GROUPBOX,
+ FALSE, {$IFDEF PACK_COMMANDACTIONS} ButtonActions_Packed
+ {$ELSE} @ButtonActions {$ENDIF} );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Groupbox';
+ {$ENDIF}
+ Result.fExStyle := Result.fExStyle or WS_EX_CONTROLPARENT;
+ Result.Caption := Caption;
+ with Result.fBoundsRect do
+ begin
+ Right := Left + 100;
+ Bottom := Top + 100;
+ end;
+ Result.fClientTop := 22;
+ Result.fClientBottom := 2;
+ Result.fClientLeft := 2;
+ Result.fClientRight := 2;
+ {$IFDEF USE_FLAGS}
+ exclude( Result.fStyle.f2_Style, F2_Tabstop );
+ include( Result.fFlagsG5, G5_IsGroupbox );
+ {$ELSE} Result.fTabstop := False;
+ Result.fIsGroupBox := TRUE;
+ {$ENDIF}
+ Result.AttachProc( WndProcDoEraseBkgnd );
+{$IFDEF GRAPHCTL_XPSTYLES}
+ Attach_WM_THEMECHANGED(Result, XP_Themes_For_GroupBox);
+{$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF USE_CONSTRUCTORS}
+
+//===================== Panel ========================//
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewPanel( AParent: PControl; EdgeStyle: TEdgeStyle ): PControl;
+begin
+ new( Result, CreatePanel( AParent, EdgeStyle ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Panel';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function NewPanel( AParent: PControl; EdgeStyle: TEdgeStyle ): PControl;
+begin
+ Result := _NewControl( AParent, 'STATIC', WS_VISIBLE or WS_CHILD or SS_NOTIFY or
+ SS_LEFTNOWORDWRAP or SS_NOPREFIX, False,
+ {$IFDEF PACK_COMMANDACTIONS} LabelActions_Packed
+ {$ELSE} @LabelActions {$ENDIF} );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Panel';
+ {$ENDIF}
+ Result.aAutoSzX := 1;
+ Result.aAutoSzY := 1;
+ with Result.fBoundsRect do
+ begin
+ Right := Left + 100;
+ Bottom := Top + 100;
+ end;
+ Result.fStyle.Value := Result.fStyle.Value or Edgestyles[ EdgeStyle ];
+ Result.fExStyle := Result.fExStyle or WS_EX_CONTROLPARENT;
+ Result.fVerticalAlign := vaTop;
+{$IFDEF GRAPHCTL_XPSTYLES}
+ if AppTheming then
+ Result.fStyle.Value := Result.fStyle.Value and (not Edgestyles[ EdgeStyle ]);
+ Result.SetEdgeStyle(EdgeStyle);
+ Attach_WM_THEMECHANGED(Result, XP_Themes_For_Panel);
+{$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF USE_CONSTRUCTORS}
+
+//===================== Splitter ==============================//
+
+//{$DEFINE USE_ASM_DODRAG}
+
+ {$IFNDEF USE_ASM_DODRAG}
+ {$DEFINE USE_PAS_DODRAG}
+ {$ENDIF}
+ {$IFNDEF ASM_VERSION}
+ {$DEFINE USE_PAS_DODRAG}
+ {$ENDIF}
+{$IFDEF USE_PAS_DODRAG}
+procedure DoDrag( Self_: PControl; Cancel: Boolean );
+var NewSize1, NewSize2: Integer;
+ MousePos: TPoint;
+ R: TRect;
+ Prev: PControl;
+ I, M : Integer;
+begin
+ if {$IFDEF USE_FLAGS} G6_Dragging in Self_.fFlagsG6
+ {$ELSE} Self_.fDragging {$ENDIF} then
+ begin
+ I := Self_.fParent.fChildren.IndexOf( Self_ );
+ Prev := Self_;
+ if I > 0 then
+ Prev := Self_.FParent.fChildren.Items[ I - 1 ];
+ GetCursorPos( MousePos );
+ {$IFDEF SPEED_FASTER}
+ if (MousePos.X = Self_.DF.fSplitLastPos.X)
+ and (MousePos.Y = Self_.DF.fSplitLastPos.Y) then Exit; {>>>>>>>>>>>>>}
+ Self_.DF.fSplitLastPos := MousePos;
+ {$ENDIF SPEED_FASTER}
+ if Cancel then
+ MousePos := Self_.DF.fSplitStartPos;
+ M := 1;
+ if Self_.FAlign in [ caRight, caBottom ] then
+ M := -1;
+ if Self_.FAlign in [ caTop, caBottom ] then
+ begin
+ NewSize1 := (MousePos.y - Self_.DF.fSplitStartPos.y)* M
+ + Self_.DF.fSplitStartSize;
+ NewSize2 := Self_.fParent.ClientHeight - NewSize1
+ - Self_.fBoundsRect.Bottom + Self_.fBoundsRect.Top
+ - Self_.fParent.fMargin * 4;
+ if Self_.DF.fSecondControl <> nil then
+ begin
+ NewSize2 := Self_.DF.fSecondControl.fBoundsRect.Bottom
+ - Self_.DF.fSecondControl.fBoundsRect.Top;
+ if Self_.DF.fSecondControl.FAlign = caClient then
+ NewSize2 := Self_.DF.fSplitStartPos2.y
+ - (MousePos.y - Self_.DF.fSplitStartPos.y)* M
+ - Self_.fParent.fMargin * 4;
+ end;
+ end else
+ begin
+ NewSize1 := (MousePos.x - Self_.DF.fSplitStartPos.x)* M
+ + Self_.DF.fSplitStartSize;
+ NewSize2 := Self_.fParent.ClientWidth - NewSize1
+ - Self_.fBoundsRect.Right + Self_.fBoundsRect.Left
+ - Self_.fParent.fMargin * 4;
+ if Self_.DF.fSecondControl <> nil then
+ begin
+ NewSize2 := Self_.DF.fSecondControl.fBoundsRect.Right
+ - Self_.DF.fSecondControl.fBoundsRect.Left;
+ if Self_.DF.fSecondControl.FAlign = caClient then
+ NewSize2 := Self_.DF.fSplitStartPos2.x
+ - (MousePos.x - Self_.DF.fSplitStartPos.x)* M
+ - Self_.fParent.Margin * 4;
+ end;
+ end;
+ if (NewSize1 < Self_.DF.fSplitMinSize1) then
+ begin
+ Dec( NewSize2, Self_.DF.fSplitMinSize1 - NewSize1 );
+ NewSize1 := Self_.DF.fSplitMinSize1;
+ end;
+ if (NewSize2 < Self_.DF.fSplitMinSize2) then
+ begin
+ Dec( NewSize1, Self_.DF.fSplitMinSize2 - NewSize2 );
+ NewSize2 := Self_.DF.fSplitMinSize2;
+ end;
+ if NewSize1 < Self_.DF.fSplitMinSize1 then Exit; {>>>>>>>>>>>>>>>>>>>>>>}
+ if NewSize2 < Self_.DF.fSplitMinSize2 then Exit; {>>>>>>>>>>>>>>>>>>>>>>}
+ {$IFDEF NIL_EVENTS}
+ if assigned( Self_.EV.fOnSplit ) then
+ {$ENDIF}
+ if not Self_.EV.fOnSplit( Self_, NewSize1, NewSize2 ) then Exit; {>>}
+ R := Prev.BoundsRect;
+ case Self_.FAlign of
+ caTop: R.Bottom := R.Top + NewSize1;
+ caBottom: R.Top := R.Bottom - NewSize1;
+ caRight: R.Left := R.Right - NewSize1;
+ else R.Right := R.Left + NewSize1;
+ end;
+ Prev.BoundsRect := R;
+ {$IFDEF OLD_ALIGN}
+ Global_Align( Self_.fParent );
+ {$ELSE NEW_ALIGN}
+ Global_Align( Self_ );
+ {$ENDIF}
+ end;
+end;
+{$ENDIF}
+
+const
+ chkLeft=2;
+ chkTop=4;
+ chkRight=8;
+ chkBott=16;
+
+{$DEFINE USE!_ASM_DODRAG}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function WndProcSplitter( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var I: Integer;
+ Prev: PControl;
+
+ procedure FinDrag;
+ begin
+ KillTimer( Self_.fHandle, $7B );
+ {$IFDEF USE_FLAGS} exclude( Self_.fFlagsG6, G6_Dragging );
+ {$ELSE} Self_.fDragging := False; {$ENDIF}
+ ReleaseCapture;
+ end;
+begin
+ case Msg.message of
+ WM_NCHITTEST:
+ begin
+ Rslt := DefWindowProc( Self_.fHandle, Msg.message, Msg.wParam, Msg.lParam );
+ if Rslt > 0 then
+ Rslt := HTCLIENT;
+ Result := True; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ WM_MOUSEMOVE:
+ begin
+ Windows.SetCursor( Self_.fCursor );
+ DoDrag( Self_, False );
+ end;
+ WM_LBUTTONDOWN:
+ begin
+ if Self_.fParent <> nil then
+ begin
+ I := Self_.fParent.fChildren.IndexOf( Self_ );
+ Prev := Self_;
+ if I > 0 then
+ Prev := Self_.FParent.fChildren.Items[ I - 1 ];
+ if Self_.fAlign in [ caTop, caBottom ] then
+ Self_.DF.fSplitStartSize := Prev.Height
+ else
+ Self_.DF.fSplitStartSize := Prev.Width;
+ if Self_.DF.fSecondControl <> nil then
+ Self_.DF.fSplitStartPos2 :=
+ MakePoint( Self_.DF.fSecondControl.Width, Self_.DF.fSecondControl.Height );
+ SetCapture( Self_.fHandle );
+ {$IFDEF SPEED_FASTER}
+ Self_.DF.fSplitLastPos := MakePoint( -1, -1 );
+ {$ENDIF}
+ {$IFDEF USE_FLAGS} Include( Self_.fFlagsG6, G6_Dragging );
+ {$ELSE} Self_.fDragging := True; {$ENDIF}
+ SetTimer( Self_.fHandle, $7B, 100, nil );
+ GetCursorPos( Self_.DF.fSplitStartPos );
+ end;
+ end;
+ WM_LBUTTONUP:
+ begin
+ DoDrag( Self_, False );
+ FinDrag;
+ end;
+ WM_TIMER:
+ if {$IFDEF USE_FLAGS} (G6_Dragging in Self_.fFlagsG6)
+ {$ELSE} Self_.fDragging {$ENDIF}
+ and (GetAsyncKeyState( VK_ESCAPE ) < 0) then
+ begin
+ DoDrag( Self_, True );
+ FinDrag;
+ end;
+ end;
+ Result := False;
+end;
+{$ENDIF PAS_VERSION}
+
+function NewSplitter( AParent: PControl; MinSizePrev, MinSizeNext: Integer ): PControl;
+begin
+ Result := NewSplitterEx( AParent, MinSizePrev, MinSizeNext, esLowered );
+end;
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewSplitterEx( AParent: PControl; MinSizePrev, MinSizeNext: Integer;
+ EdgeStyle: TEdgeStyle ): PControl;
+begin
+ new( Result, CreateSplitter( AParent, MinSizePrev, MinSizeNext, EdgeStyle ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:SplitterEx';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewSplitterEx( AParent: PControl; MinSizePrev, MinSizeNext: Integer;
+ EdgeStyle: TEdgeStyle ): PControl;
+var PrevCtrl: PControl;
+ Sz0: Integer;
+begin
+ Result := NewPanel( AParent, EdgeStyle );
+ Result.DF.fSplitMinSize1 := MinSizePrev;
+ Result.DF.fSplitMinSize2 := MinSizeNext;
+ {$IFDEF USE_FLAGS} include( Result.fFlagsG5, G5_IsSplitter );
+ {$ELSE} Result.fIsSplitter := TRUE; {$ENDIF}
+ Sz0 := 4;
+ with Result.fBoundsRect do
+ begin
+ Right := Left + Sz0;
+ Bottom := Top + Sz0;
+ end;
+ if AParent <> nil then
+ begin
+ if AParent.fChildren.fCount > 1 then
+ begin
+ PrevCtrl := AParent.fChildren.Items[ AParent.fChildren.fCount - 2 ];
+ case PrevCtrl.FAlign of
+ caLeft, caRight:
+ begin
+ Result.fCursor := LoadCursor( 0, IDC_SIZEWE );
+ end;
+ caTop, caBottom:
+ begin
+ Result.fCursor := LoadCursor( 0, IDC_SIZENS );
+ end;
+ end;
+ Result.Align := PrevCtrl.FAlign;
+ end;
+ end;
+ Result.AttachProc( WndProcSplitter );
+{$IFDEF GRAPHCTL_XPSTYLES}
+ Attach_WM_THEMECHANGED(Result, XP_Themes_For_Splitter);
+{$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF USE_CONSTRUCTORS}
+
+{$IFDEF USE_MDI}
+//===================== MDI client window control =============//
+
+procedure DestroyMDIChildren( Form: PControl );
+var MDIClient: PControl;
+ I: Integer;
+ Ch: PControl;
+ MDIChildren: PList;
+begin
+ //MDIClient := Form.MDIClient;
+ MDIClient := nil;
+ for I := 0 to Form.ChildCount-1 do
+ begin
+ Ch := Form.Children[I];
+ if Ch.PropInt[ MDI_CHLDRN ] <> 0 then
+ begin
+ MDIClient := Ch;
+ break;
+ end;
+ end;
+ if MDIClient = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ MDIClient.fAnchors := MDIClient.fAnchors or MDI_DESTROYING;
+ MDIChildren := Pointer( MDIClient.PropInt[ MDI_CHLDRN ] );
+ if MDIChildren <> nil then
+ for I := MDIChildren.Count - 1 downto 0 do
+ begin
+ Ch := MDIChildren.Items[ I ];
+ if Ch.fHandle <> 0 then
+ MDIClient.Perform( WM_MDIDESTROY, Ch.fHandle, 0 );
+ end;
+ MDIChildren.Free;
+ MDIClient.PropInt[ MDI_CHLDRN ] := 0;
+ if Form.fMenu <> 0 then
+ begin
+ MDIClient.Perform( WM_MDISETMENU, 0, 0 );
+ MDIClient.Perform( WM_MDIREFRESHMENU, 0, 0 );
+ DrawMenuBar( Form.fHandle );
+ Form.fMenuObj.Free;
+ Form.fMenuObj := nil;
+ end;
+ MDIClient.Free;
+end;
+
+function ProcMDIAccel( Applet: PControl; var Msg: TMsg ): Boolean;
+var Form: PControl;
+begin
+ Result := FALSE;
+ if (Msg.message >= WM_KEYFIRST) and (Msg.message <= WM_KEYLAST) then
+ begin
+ Form := Applet.ActiveControl;
+ if Form <> nil then
+ begin
+ if Form.IsMDIChild then
+ Form := Form.Parent;
+ Form := Form.ParentForm;
+ if (Form <> nil) and (Form.MDIClient <> nil) then
+ Result := TranslateMDISysAccel( Form.MDIClient.fHandle,
+ Windows.TMsg(Msg) );
+ end;
+ end;
+end;
+
+function CallDefFrameProc( Wnd: HWnd; Msg: Integer; wParam, lParam: Integer ): Integer;
+stdcall;
+var Form, MDIClient: PControl;
+begin
+ {$IFDEF USE_PROP}
+ Form := Pointer( GetProp( Wnd, ID_SELF ) );
+ {$ELSE}
+ Form := Pointer( GetWindowLong( Wnd, GWL_USERDATA ) );
+ {$ENDIF}
+ if Form <> nil then
+ Form := Form.ParentForm;
+ MDIClient := Form.MDIClient;
+ if (Form <> nil) and (MDIClient <> nil) then
+ Result := DefFrameProc( Wnd, MDIClient.fHandle, Msg, wParam, lParam )
+ else
+ Result := DefWindowProc( Wnd, Msg, wParam, lParam );
+end;
+
+function WndFuncMDIClient( Wnd: HWnd; Msg, wParam, lParam: Integer ): Integer;
+stdcall;
+var C: PControl;
+ M: TMsg;
+begin
+ {$IFDEF USE_PROP}
+ C := Pointer( GetProp( Wnd, ID_SELF ) );
+ {$ELSE}
+ C := Pointer( GetWindowLong( Wnd, GWL_USERDATA ) );
+ {$ENDIF}
+ if C <> nil then
+ begin
+ M.hwnd := Wnd;
+ M.message := Msg;
+ M.wParam := wParam;
+ M.lParam := lParam;
+ Result := C.WndProc( M );
+ end
+ else
+ Result := DefWindowProc( Wnd, Msg, wParam, lParam );
+end;
+
+function ShowMDIClientEdge( MDIClient: PControl ): Boolean;
+var ShowEdge: Boolean;
+ I: Integer;
+ Ch: PControl;
+ ExStyle: Integer;
+ MDIChildren: PList;
+begin
+ Result := FALSE;
+ ShowEdge := TRUE;
+ MDIChildren := Pointer( MDIClient.PropInt[ MDI_CHLDRN ] );
+ if MDIChildren.Count > 0 then
+ for I := 0 to MDIChildren.Count-1 do
+ begin
+ Ch := MDIChildren.Items[ I ];
+ if IsZoomed( Ch.fHandle ) then
+ begin
+ ShowEdge := FALSE;
+ break;
+ end;
+ end;
+ ExStyle := MDIClient.ExStyle;
+ if ShowEdge then
+ if ExStyle and WS_EX_CLIENTEDGE = 0 then
+ ExStyle := ExStyle or WS_EX_CLIENTEDGE
+ else Exit {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ else if ExStyle and WS_EX_CLIENTEDGE <> 0 then
+ ExStyle := ExStyle and not WS_EX_CLIENTEDGE
+ else Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ MDIClient.ExStyle := ExStyle;
+ Result := TRUE;
+end;
+
+function WndProcMDIClient( MDIClient: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+begin
+ Result := FALSE;
+ if MDIClient.fAnchors and MDI_DESTROYING = 0 then
+ case Msg.message of
+ $3f:
+ begin
+ PostMessage( MDIClient.fHandle, CM_MDIClientShowEdge, 0, 0 );
+ end;
+ CM_MDIClientShowEdge:
+ begin
+ ShowMDIClientEdge( MDIClient );
+ end;
+ WM_NCHITTEST: // not necessary though
+ begin
+ Rslt := DefWindowProc( MDIClient.fHandle, WM_NCHITTEST, Msg.wParam, Msg.lParam );
+ if Rslt = HTCLIENT then Rslt := HTTRANSPARENT;
+ end;
+ WM_WINDOWPOSCHANGING:
+ begin
+ MDIClient.Perform( WM_SETREDRAW, 0, 0 );
+ end;
+ WM_WINDOWPOSCHANGED:
+ begin
+ Global_Align( {$IFDEF OLD_ALIGN}MDIClient.Parent{$ELSE}MDIClient{$ENDIF} );
+ MDIClient.Invalidate;
+ MDIClient.Parent.Invalidate;
+ MDIClient.Perform( WM_SETREDRAW, 1, 0 );
+ PostMessage( MDIClient.fHandle, CM_INVALIDATE, 0, 0 );
+ end;
+ CM_INVALIDATE:
+ begin
+ MDIClient.InvalidateNC( TRUE );
+ MDIClient.InvalidateEx;
+ end;
+ WM_DESTROY:
+ begin
+ MDIClient.FParent.fMDIClient := nil;
+ end;
+ end;
+end;
+
+// function added by Thaddy de Koning to fix MDI behaviour
+function WndProcParentNotifyMouseLDown( Sender: PControl; var Msg: TMsg;
+var Rslt: Integer ): Boolean;
+begin
+ Result := FALSE;
+ if (Sender.IsMDIChild) and (Msg.message = WM_PARENTNOTIFY) and
+ (LOWORD(msg.wparam)=WM_LBUTTONDOWN) then
+ BringWindowToTop( Sender.Handle );
+end;
+
+function NewMDIClient( AParent: PControl; WindowMenu: THandle ): PControl;
+var F: PControl;
+ CCS: TClientCreateStruct;
+ PrntWin: HWnd;
+begin
+ PrntWin := 0;
+ if AParent <> nil then
+ begin
+ F := AParent.ParentForm;
+ if F <> nil then
+ begin
+ F.Add2AutoFreeEx( TObjectMethod( MakeMethod( F, @ DestroyMDIChildren ) ) );
+ F.GetWindowHandle; // must be created before MDI client creation
+ F.fDefWndProc := @CallDefFrameProc;
+ end;
+ PrntWin := AParent.GetWindowHandle;
+ end;
+ Applet.PP.fExMsgProc := ProcMDIAccel;
+ Result := _NewControl( AParent, 'MDICLIENT',
+ WS_CHILD or WS_CLIPCHILDREN or WS_VSCROLL or WS_HSCROLL or
+ WS_VISIBLE or WS_TABSTOP or MDIS_ALLCHILDSTYLES, TRUE,
+ {$IFDEF PACK_COMMANDACTIONS} PAnsiChar(OTHER_ACTIONS)
+ {$ELSE} nil {$ENDIF} );
+ AParent.fMDIClient := Result;
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:MDIClient';
+ {$ENDIF}
+ Result.fExStyle := WS_EX_CLIENTEDGE;
+
+ CCS.hWindowMenu := WindowMenu;
+ CCS.idFirstChild := $FF00;
+ Result.fHandle := CreateWindowEx( WS_EX_CLIENTEDGE, 'MDICLIENT', nil,
+ WS_CHILD or WS_CLIPCHILDREN or WS_VSCROLL or WS_HSCROLL or
+ WS_VISIBLE or WS_TABSTOP,
+ 0, 0, 0, 0, PrntWin, 0, hInstance, @ CCS );
+ Result.fDefWndProc := Pointer( GetWindowLong( Result.fHandle, GWL_WNDPROC ) );
+ SetWindowLong( Result.fHandle, GWL_WNDPROC, Integer( @WndFuncMDIClient ) );
+ Result.PropInt[ MDI_CHLDRN ] := Integer( NewList );
+ {$IFDEF USE_PROP}
+ SetProp( Result.fHandle, ID_SELF, Integer( Result ) );
+ {$ELSE}
+ SetWindowLong( Result.fHandle, GWL_USERDATA, Integer( Result ) );
+ {$ENDIF}
+ Result.AttachProc( WndProcMDIClient );
+ Result.GetWindowHandle;
+
+ Applet.AttachProc( WndProcParentNotifyMouseLDown );
+end;
+
+//===================== MDI child window object ==============//
+function MDIChildFunc( Wnd: HWnd; Msg: DWord; wParam, lParam: Integer ): Integer;
+stdcall;
+var C: PControl;
+ M: TMsg;
+begin
+ {$IFDEF USE_PROP}
+ C := Pointer( GetProp( Wnd, ID_SELF ) );
+ {$ELSE}
+ C := Pointer( GetWindowLong( Wnd, GWL_USERDATA ) );
+ {$ENDIF}
+ if C <> nil then
+ begin
+ M.hwnd := Wnd;
+ M.message := Msg;
+ M.wParam := wParam;
+ M.lParam := lParam;
+ Result := C.WndProc( M );
+ end
+ else
+ Result := DefMDIChildProc( Wnd, Msg, wParam, lParam );
+end;
+
+function Pass2DefMDIChildProc( Sender_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+begin
+ Result := FALSE;
+ if Sender_ = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if Sender_.fParent = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if {$IFDEF USE_FLAGS} G2_Destroying in Sender_.fParent.fFlagsG2
+ {$ELSE} Sender_.fParent.fDestroying {$ENDIF} then Exit; {>>>>>>>>>>>>>>>>}
+ if (Msg.message = WM_SYSCOMMAND) or (Msg.message = WM_CHILDACTIVATE) or
+ (Msg.message = WM_SETFOCUS) or (Msg.message = WM_SIZE) or
+ (Msg.message = WM_MOVE) or (Msg.message = WM_MENUCHAR) or
+ (Msg.message = WM_GETMINMAXINFO) {and IsZoomed( Sender_.fHandle ) {and (Msg.hwnd = Sender_.fHandle) { -- doesn't work -- }
+ or (Msg.message = WM_PAINT)
+ then
+ begin
+ Rslt := DefMDIChildProc( Msg.hwnd, Msg.message, Msg.lParam, Msg.wParam );
+ Result := TRUE;
+ end;
+end;
+
+function WndProcMDIChild( MDIChild: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var ClientWnd: HWnd;
+ MDIClient: PControl;
+ MDIForm: PControl;
+ MDIChildren: PList;
+begin
+ Result := FALSE;
+ MDIClient := MDIChild.Parent;
+ if MDIClient = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ ClientWnd := MDIClient.fHandle;
+ if ClientWnd = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ case Msg.message of
+ WM_DESTROY:
+ begin
+ MDIChildren := Pointer( MDIClient.PropInt[ MDI_CHLDRN ] );
+ MDIChildren.Remove( MDIChild );
+ MDIForm := MDIClient.ParentForm;
+ if MDIForm <> nil then
+ if MDIForm.fHandle <> 0 then
+ DrawMenuBar( MDIForm.fHandle );
+ MDIChild.Free;
+ Result := TRUE; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ if MDIChild.fAnchors and MDI_NOT_AVAILABLE <> 0 then
+ begin
+ MDIChild.fAnchors := MDIChild.fAnchors and not MDI_NOT_AVAILABLE;
+ MDIChild.Invalidate;
+ end;
+end;
+
+procedure CreateMDIChildExt( Sender: PControl );
+var F: PControl;
+begin
+ F := Sender.Parent;
+ if F <> nil then
+ F := F.ParentForm;
+ if F <> nil then
+ DrawMenuBar( F.fHandle );
+end;
+
+var mdi_child_id: Integer = $FF00;
+
+function NewMDIChild( AParent: PControl; const ACaption: KOLString ): PControl;
+var MDIClient: PControl;
+ MDIChildren: PList;
+ i: Integer;
+begin
+ {$IFDEF KOL_ASSERTIONS}
+ Assert( (AParent <> nil) and (AParent.ParentForm <> nil) and
+ (AParent.ParentForm.MDIClient <> nil), 'Error creating MDI child' );
+ {$ENDIF KOL_ASSERTIONS}
+ MDIClient := AParent.ParentForm.MDIClient;
+ MDIChildren := Pointer( MDIClient.PropInt[ MDI_CHLDRN ] );
+
+ for i := 0 to MDIChildren.Count-1 do
+ begin
+ Result := MDIChildren.Items[i];
+ //if Result.DF.fWindowState = wsMaximized then
+ if IsZoomed( Result.fHandle ) then
+ begin
+ MDIClient.Perform( WM_MDIRESTORE, Result.fHandle, 0 );
+ end;
+ end;
+
+ Result := NewForm( MDIClient, ACaption );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:MDIChild';
+ {$ENDIF}
+ {$IFDEF USE_FLAGS} include( Result.fFlagsG3, G3_IsMDIChild );
+ {$ELSE} Result.fIsMDIChild := TRUE; {$ENDIF}
+ Result.fMenu := mdi_child_id; // CtlIdCount;
+ Inc( mdi_child_id );
+
+ MDIChildren.Add( Result );
+ Result.fExStyle := Result.fExStyle or WS_EX_MDICHILD;
+ Result.PP.fWndFunc := @ MDIChildFunc;
+ Result.fDefWndProc := @DefMDIChildProc;
+ Result.PP.fPass2DefProc := Pass2DefMDIChildProc;
+ Result.AttachProc( WndProcMDIChild );
+
+ Result.SubClassName := 'MDI_chld';
+ Result.fAnchors := Result.fAnchors or MDI_NOT_AVAILABLE;
+ Result.PP.fCreateWndExt := CreateMDIChildExt;
+ Result.fCreateWindowProc := CreateMDIWindow;
+end;
+{$ENDIF USE_MDI}
+
+//===================== Gradient panel ========================//
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewGradientPanel( AParent: PControl; Color1, Color2: TColor ): PControl;
+begin
+ new( Result, CreateGradientPanel( AParent, Color1, Color2 ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:GradientPanel';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewGradientPanel( AParent: PControl; Color1, Color2: TColor ): PControl;
+begin
+ Result := NewLabel( AParent, '' );
+ Result.AttachProc( WndProcGradient );
+ Result.DF.fColor2 := Color2;
+ Result.DF.fColor1 := Color1;
+ with Result.fBoundsRect do
+ begin
+ Right := Left + 40;
+ Bottom := Top + 40;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF USE_CONSTRUCTORS}
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewGradientPanelEx( AParent: PControl; Color1, Color2: TColor;
+ Style: TGradientStyle; Layout: TGradientLayout ): PControl;
+begin
+ new( Result, CreateGradientPanelEx( AParent, Color1, Color2,
+ Style, Layout ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:GradientPanelEx';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewGradientPanelEx( AParent: PControl; Color1, Color2: TColor;
+ Style: TGradientStyle; Layout: TGradientLayout ): PControl;
+begin
+ Result := NewLabel( AParent, '' );
+ Result.AttachProc( WndProcGradientEx );
+ Result.DF.fColor2 := Color2;
+ Result.DF.fColor1 := Color1;
+ Result.DF.fGradientStyle := Style;
+ Result.DF.fGradientLayout := Layout;
+ with Result.fBoundsRect do
+ begin
+ Right := Left + 40;
+ Bottom := Top + 40;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF USE_CONSTRUCTORS}
+
+//===================== Edit box ========================//
+
+const Editflags: array [ TEditOption ] of Integer = (
+ not (ES_AUTOHSCROLL or WS_HSCROLL),
+ not (es_AutoVScroll or WS_VSCROLL),
+ es_Lowercase, es_Multiline,
+ es_NoHideSel, es_OemConvert, es_Password, es_Readonly,
+ es_UpperCase, es_WantReturn, 0, es_Number );
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewEditbox( AParent: PControl; Options: TEditOptions ) : PControl;
+begin
+ new( Result, CreateEditbox( AParent, Options ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Editbox';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF _D3orHigher}
+function WndProcUnicodeChars( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var WStr, WW: KOLWideString;
+ RepeatCount: Integer;
+ C: KOLChar;
+begin
+ Result := FALSE;
+ if (Msg.message = WM_CHAR)
+ and (Msg.wParam >= 32)
+ {$IFDEF UNICODE_CHAR_EXTCTL}
+ and (GetKeyState(VK_CONTROL) >= 0)
+ and (GetKeyState(VK_ALT) >= 0)
+ and (GetKeyState(VK_LWIN) >= 0)
+ and (GetKeyState(VK_RWIN) >= 0)
+ {$ENDIF} then
+ begin
+ Result := TRUE;
+
+ {$IFDEF NIL_EVENTS}
+ if assigned( Sender.EV.fOnChar ) then
+ {$ENDIF}
+ begin
+ C := KOLChar( Msg.wParam );
+ Sender.EV.fOnChar( Sender, C, GetShiftState );
+ Msg.wParam := Integer( C );
+ end;
+
+ WStr := WideChar(Msg.wParam);
+ if WStr <> '' then
+ begin
+ RepeatCount := Msg.lParam and $FFFF;
+ if RepeatCount > 1 then
+ begin
+ WW := WStr[1];
+ for RepeatCount := 2 to RepeatCount do
+ WStr := WStr + WW;
+ end;
+ Sender.ReplaceSelection( KOLString( WStr ), TRUE );
+ end;
+ Rslt := 0;
+ end;
+end;
+{$ENDIF _D3orHigher}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewEditbox( AParent: PControl; Options: TEditOptions ) : PControl;
+var Flags: Integer;
+begin
+ Flags := MakeFlags( @Options, EditFlags );
+ if not(eoMultiline in Options) then
+ Flags := Flags and not(WS_HSCROLL or WS_VSCROLL);
+ Result := _NewControl( AParent, 'EDIT', WS_VISIBLE or WS_CHILD or WS_TABSTOP
+ or WS_BORDER or Flags, True,
+ {$IFDEF PACK_COMMANDACTIONS} EditActions_Packed
+ {$ELSE} @EditActions {$ENDIF} );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Editbox';
+ {$ENDIF}
+ Result.aAutoSzY := 6;
+ with Result.fBoundsRect do
+ begin
+ Right := Left + 100;
+ Bottom := Top + 22;
+ if eoMultiline in Options then
+ begin
+ Right := Right + 100;
+ Bottom := Top + 200;
+ {$IFDEF USE_FLAGS} include( Result.fFlagsG5, G5_IgnoreDefault );
+ {$ELSE} Result.fIgnoreDefault := TRUE; {$ENDIF}
+ end;
+ end;
+ Result.fLookTabKeys := [ tkTab, tkUpDown, tkPageUpPageDn ];
+ if eoMultiline in Options then
+ Result.fLookTabKeys := [ tkTab ];
+ if eoWantTab in Options then
+ exclude( Result.fLookTabKeys, tkTab );
+ {$IFDEF UNICODE_CTRLS}
+ {$IFDEF _D3orHigher}
+ Result.AttachProc( WndProcUnicodeChars );
+ {$ENDIF}
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF USE_CONSTRUCTORS}
+
+//===================== List box ========================//
+
+const ListFlags: array[TListOption] of Integer = (
+ LBS_DISABLENOScroll, not LBS_ExtendedSel,
+ LBS_MultiColumn or WS_HSCROLL,
+ LBS_MultiPLESel,
+ LBS_NoIntegralHeight, LBS_NoSel, LBS_Sort, LBS_USETabstops,
+ not LBS_HASSTRINGS, LBS_NODATA, LBS_OWNERDRAWFIXED,
+ LBS_OWNERDRAWVARIABLE, WS_HSCROLL );
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewListbox( AParent: PControl; Options: TListOptions ): PControl;
+begin
+ new( Result, CreateListbox( AParent, Options ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Listbox';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function NewListbox( AParent: PControl; Options: TListOptions ): PControl;
+var Flags: Integer;
+begin
+ Flags := MakeFlags( @Options, ListFlags );
+ Result := _NewControl( AParent, 'LISTBOX', WS_VISIBLE or WS_CHILD or WS_TABSTOP
+ or WS_BORDER or WS_VSCROLL
+ or LBS_NOTIFY or Flags, True,
+ {$IFDEF PACK_COMMANDACTIONS} ListActions_Packed
+ {$ELSE} @ListActions {$ENDIF} );
+ {$IFDEF PACK_COMMANDACTIONS}
+ Result.fCommandActions.aClear := ClearListbox;
+ {$ENDIF}
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Listbox';
+ {$ENDIF}
+ with Result.fBoundsRect do
+ begin
+ Right := Right + 100;
+ Bottom := Top + 200;
+ end;
+ Result.fColor := clWindow;
+ Result.fLookTabKeys := [ tkTab, tkLeftRight ];
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF USE_CONSTRUCTORS}
+
+//===================== Combo box ========================//
+
+{$IFNDEF USE_DROPDOWNCOUNT}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure ComboboxDropDown( Sender: PObj );
+var
+ CB: PControl;
+ IC: Integer;
+begin
+ CB := PControl( Sender );
+ IC := CB.Count;
+ if IC > 8 then IC := 8;
+ if IC < 1 then IC := 1;
+
+ SetWindowPos( CB.Handle, 0, 0, 0, CB.Width, CB.Height * (IC + 1) + 2,
+ SWP_NOMOVE + SWP_NOZORDER + SWP_NOACTIVATE + SWP_NOREDRAW +
+ SWP_HIDEWINDOW);
+
+ SetWindowPos( CB.Handle, 0, 0, 0, 0, 0, SWP_NOMOVE + SWP_NOSIZE
+ + SWP_NOZORDER + SWP_NOACTIVATE
+ + SWP_NOREDRAW + SWP_SHOWWINDOW);
+ {$IFDEF NIL_EVENTS}
+ if assigned( CB.EV.fOnDropDown ) then
+ {$ENDIF}
+ CB.EV.fOnDropDown( CB );
+end;
+{$ENDIF PAS_VERSION}
+{$ELSE newcode}
+procedure ComboboxDropDown( Sender: PObj );
+var
+ CB: PControl;
+ Count: Integer;
+ DropDownCount: Integer;
+ ItemHeight: Integer;
+begin
+ CB := PControl(Sender);
+ Count := CB.Count;
+ DropDownCount := CB.DropDownCount; // 8;
+ if (Count > DropDownCount) then
+ Count := DropDownCount;
+ if (Count < 1) then
+ Count := 1;
+ ItemHeight := CB.Perform(CB_GETITEMHEIGHT, 0, 0);
+ SetWindowPos(
+ CB.Handle, 0, 0, 0, CB.Width, ItemHeight * Count + CB.Height + 2,
+ SWP_NOMOVE or SWP_NOZORDER or SWP_NOACTIVATE or SWP_NOREDRAW or SWP_HIDEWINDOW);
+ SetWindowPos(
+ CB.Handle, 0, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE or SWP_NOZORDER or
+ SWP_NOACTIVATE or SWP_NOREDRAW or SWP_SHOWWINDOW);
+ {$IFDEF NIL_EVENTS}
+ if Assigned(CB.EV.fOnDropDown) then
+ {$ENDIF}
+ CB.EV.fOnDropDown(CB);
+end;
+{$ENDIF USE_DROPDOWNCOUNT}
+
+function WndFuncCombo( W: HWnd; Msg: Cardinal; wParam, lParam: Integer )
+ : Integer; stdcall;
+var Combo, Form: PControl;
+ ParentWnd : HWnd;
+ MsgStruct: TMsg;
+ PrevProc:Pointer; //********************************** Added By M.Gerasimov
+begin
+ Combo := nil;
+
+ ParentWnd := GetParent( W );
+ if ParentWnd <> 0 then
+ {$IFDEF USE_PROP}
+ Combo := Pointer( GetProp( ParentWnd, ID_SELF ) );
+ {$ELSE}
+ Combo := Pointer( GetWindowLong( ParentWnd, GWL_USERDATA ) );
+ {$ENDIF}
+
+ if (Combo <> nil) then
+ begin
+ MsgStruct.hwnd := Combo.fHandle;
+ MsgStruct.message := Msg;
+ MsgStruct.wParam := wParam;
+ MsgStruct.lParam := lParam;
+ Form := Combo.ParentForm;
+ if fGlobalProcKeybd( Combo, MsgStruct, Result ) then Exit; {>>>>>>>>>>>>>}
+ if W <> Combo.FHandle then
+ begin
+ if ( Applet <> nil )
+ {$IFDEF NIL_EVENTS} and Assigned( Applet.EV.fOnMessage ) {$ENDIF} then
+ if Applet.EV.fOnMessage( MsgStruct, Result ) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if (Applet <> Form) and (Form <> nil) then
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Form.EV.fOnMessage ) then
+ {$ENDIF}
+ if Form.EV.fOnMessage( MsgStruct, Result ) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ if (Combo.ToBeVisible) and
+ ((Msg = WM_KEYDOWN) or (Msg = WM_KEYUP) or (Msg = WM_CHAR)) then
+ begin
+ Result := 0;
+ if (wParam = VK_TAB) then
+ begin
+ case Msg of
+ WM_KEYDOWN:
+ if {$IFDEF NIL_EVENTS} Assigned( Combo.PP.fGotoControl ) and {$ENDIF}
+ Combo.PP.fGotoControl( Combo, wParam, FALSE ) then Exit; {>>>>>>}
+ else Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end
+ else
+ if (Msg = WM_CHAR) and ((wParam = VK_ESCAPE) or (wParam = VK_RETURN)) then
+ begin
+ if Combo.Perform( CB_GETDROPPEDSTATE, 0, 0 ) <> 0 then
+ begin
+ Combo.Perform( CB_SHOWDROPDOWN, 0, 0 );
+ if wParam = VK_ESCAPE then
+ Combo.Perform( CB_SETCURSEL, Combo.DF.fCurIdxAtDrop, 0 );
+ Combo.PP.fWndProcKeybd( Combo, MsgStruct, Result );
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end
+ {$IFDEF ESC_CLOSE_DIALOGS}
+ //---------------------------------Babenko Alexey--------------------------
+ else
+ if (wparam = VK_ESCAPE) then
+ if (combo.ParentForm.ExStyle and WS_EX_DLGMODALFRAME) <> 0 then begin
+ SendMessage(combo.ParentForm.Handle, WM_CLOSE, 0, 0);
+ exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ {$ENDIF}
+ end;
+ {$IFDEF KEY_PREVIEW}
+ if {$IFDEF USE_FLAGS} not(G4_Pushed in Form.fFlagsG4)
+ {$ELSE} not Form.fKeyPreviewing {$ENDIF} then
+ begin
+ if {$IFDEF USE_FLAGS} G6_KeyPreview in Form.fFlagsG6
+ {$ELSE} Form.fKeyPreview {$ENDIF} then
+ begin
+ {$IFDEF USE_FLAGS} include( Form.fFlagsG4, G4_Pushed );
+ {$ELSE} Form.fKeyPreviewing := TRUE; {$ENDIF}
+ inc( Form.DF.FKeyPreviewCount );
+ //Form.Perform(Msg, wParam, lParam);
+ Form.PP.fWndProcKeybd( Form, MsgStruct, Result );
+ dec( Form.DF.fKeyPreviewCount );
+ if MsgStruct.wParam = 0 then
+ begin
+ Result := 0;
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ end;
+ {$ENDIF}
+ Combo.PP.fWndProcKeybd( Combo, MsgStruct, Result );
+ end
+ else
+ if Msg = WM_SETFOCUS then
+ begin
+ if Form <> nil then Form.DF.fCurrentControl := Combo;
+ end;
+ MsgStruct.hwnd := W;
+//********************************************************* Added By M.Gerasimov
+ PrevProc:=Pointer(GetProp( W, ID_PREVPROC ));
+ if PrevProc <> Nil then
+ Result := CallWindowProc( PrevProc , W, MsgStruct.message,
+ MsgStruct.wParam, MsgStruct.lParam )
+ else
+ Result:=0;
+//*********************************************************
+ end
+ else
+ Result := DefWindowProc( W, Msg, wParam, lParam );
+end;
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+procedure CreateComboboxWnd( Combo: PControl );
+var W : HWND;
+ PrevProc: DWORD;
+begin
+ W := GetWindow( Combo.fHandle, GW_CHILD );
+ {if W <> 0 then
+ W := GetWindow( W, GW_HWNDNEXT );}
+ while W <> 0 do
+ begin
+ PrevProc :=
+ SetWindowLong( W, GWL_WNDPROC, Longint( @WndFuncCombo ) );
+ SetProp( W, ID_PREVPROC, PrevProc ); //
+ W := GetWindow( W, GW_HWNDNEXT );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure RemoveChldPrevProc( fHandle: HWnd );
+var Chld: HWnd;
+begin
+ Chld := GetWindow( fHandle, GW_CHILD );
+ while Chld <> 0 do
+ begin
+ if GetProp( Chld, ID_PREVPROC ) <> 0 then
+ RemoveProp(Chld, ID_PREVPROC);
+ Chld := GetWindow( Chld, GW_HWNDNEXT );
+ end;
+end;
+
+function WndProcCombo( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+{$IFDEF UNICODE_CTRLS}
+var s: KOLString;
+ w: PWideChar;
+ L: Integer;
+{$ENDIF}
+begin
+ Result := FALSE;
+ if (Msg.message >= WM_CTLCOLORMSGBOX) and (Msg.message <= WM_CTLCOLORSTATIC) then
+ begin
+ Rslt := Sender.Perform( Msg.message + CN_BASE, Msg.wParam, Msg.lParam );
+ Result := TRUE;
+ end
+ else
+ if (Msg.message >= CN_CTLCOLORMSGBOX) and (Msg.message <= CN_CTLCOLORSTATIC) then
+ begin
+ if {$IFDEF USE_FLAGS} G2_Transparent in Sender.fFlagsG2
+ {$ELSE} Sender.fTransparent {$ENDIF} then
+ case Msg.message of
+ CN_CTLCOLORLISTBOX:
+ begin
+ SetBkMode( Msg.wParam, Windows.OPAQUE );
+ SetBkColor(Msg.WParam, Color2RGB( Sender.fColor ) );
+ Rslt := Global_GetCtlBrushHandle( Sender );
+ Result := TRUE;
+ end;
+ end;
+ end
+ else
+ if (Msg.message = CM_COMMAND) and Sender.ToBeVisible then
+ begin
+ case HiWord( Msg.wParam ) of
+ CBN_DROPDOWN:
+ begin
+ Sender.DF.fCurIdxAtDrop := Sender.CurIndex;
+ //Sender.fDropDownProc( Sender );
+ ComboboxDropDown( Sender );
+ end;
+ CBN_CLOSEUP:
+ begin
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Sender.EV.fOnCloseUp ) then
+ {$ENDIF}
+ Sender.EV.fOnCloseUp( Sender );
+ end;
+ CBN_SELCHANGE:
+ begin
+ PostMessage( Sender.fHandle, CM_COMMAND, CM_CBN_SELCHANGE shl 16, 0 );
+ end;
+ end;
+ end
+ else
+ if Msg.message = WM_DESTROY then
+ RemoveChldPrevProc( Sender.Handle )
+ {$IFDEF UNICODE_CTRLS}
+ else
+ if (Msg.message = CB_INSERTSTRING)
+ or (Msg.message = CB_ADDSTRING) then
+ begin
+ if {$IFDEF USE_FLAGS} not(G5_IsButton in Sender.fFlagsG5)
+ {$ELSE} not Sender.fIsButton {$ENDIF} then
+ begin
+ {$IFDEF USE_FLAGS} Include( Sender.fFlagsG5, G5_IsButton );
+ {$ELSE} Sender.fIsButton := TRUE; {$ENDIF}
+ w := Pointer( Msg.lParam );
+ L := WStrLen( w );
+ SetLength( s, L );
+ move( w^, s[1], L * SizeOf(KOLChar) );
+ Rslt := SendMessageW( Msg.hwnd, Msg.message, Msg.wParam,
+ Integer( @s[1] ) );
+ Result := TRUE;
+ {$IFDEF USE_FLAGS} Exclude( Sender.fFlagsG5, G5_IsButton );
+ {$ELSE} Sender.fIsButton := FALSE; {$ENDIF}
+ end;
+ end;
+ {$ENDIF}
+end;
+
+const ComboFlags: array[ TComboOption ] of Integer = (
+ CBS_DROPDOWNLIST, not CBS_AUTOHScroll,
+ CBS_DISABLENOSCROLL, CBS_LowerCase, CBS_NoIntegralHeight,
+ CBS_OemConvert, CBS_Sort, CBS_UpperCase,
+ CBS_OWNERDRAWFIXED, CBS_OWNERDRAWVARIABLE, CBS_SIMPLE );
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewCombobox( AParent: PControl; Options: TComboOptions ): PControl;
+begin
+ new( Result, CreateCombobox( AParent, Options ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Combobox';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewCombobox( AParent: PControl; Options: TComboOptions ): PControl;
+var Flags: Integer;
+begin
+ {$IFDEF GRAPHCTL_XPSTYLES}
+ {$IFDEF UNICODE_CTRLS}
+ InitCommonControls;
+ {$ENDIF}
+ {$ENDIF}
+ Flags := MakeFlags( @Options, ComboFlags );
+ if not LongBool( Flags and CBS_SIMPLE ) then
+ Flags := Flags or CBS_DROPDOWN;
+ Result := _NewControl( AParent, 'COMBOBOX',
+ WS_VISIBLE
+ or WS_CHILD
+ or WS_VSCROLL
+ or CBS_HASSTRINGS or WS_TABSTOP
+ or Flags
+ ,True,
+ {$IFDEF PACK_COMMANDACTIONS} ComboActions_Packed
+ {$ELSE} @ComboActions {$ENDIF} );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Combobox';
+ {$ENDIF}
+ {$IFDEF PACK_COMMANDACTIONS}
+ Result.fCommandActions.aClear := @ClearCombobox;
+ {$ENDIF}
+ Result.aAutoSzY := 6;
+ Result.PP.fCreateWndExt := CreateComboboxWnd;
+ Result.fClsStyle := Result.fClsStyle or CS_DBLCLKS;
+ with Result.fBoundsRect do
+ begin
+ Right := Left + 100;
+ Bottom := Top + 22;
+ end;
+ Result.fLookTabKeys := [ tkTab ];
+ if coReadOnly in Options then
+ Result.fLookTabKeys := [ tkTab, tkLeftRight ];
+ Result.AttachProc( @ WndProcCombo );
+ {$IFDEF USE_DROPDOWNCOUNT}
+ Result.DropDownCount := 8;
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF USE_CONSTRUCTORS}
+
+{$IFDEF ASM_TLIST}
+function WndProcResize(Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+asm
+ PUSH ESI
+ CMP word ptr [EDX].TMsg.message, WM_SIZE
+ JNZ @@exit
+
+ MOV ESI, [EAX].TControl.fChildren
+ MOV ECX, [ESI].TList.fCount
+ JECXZ @@exit
+ MOV ESI, [ESI].TList.fItems
+@@loo: PUSH ECX
+ LODSD
+ PUSH EAX
+ PUSH EAX
+ PUSH CM_SIZE
+ PUSH EAX
+ CALL TControl.Perform
+ POP ECX
+ LOOP @@loo
+
+@@exit: XOR EAX, EAX
+ POP ESI
+end;
+{$ELSE PAS_VERSION} //Pascal
+function WndProcResize(Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var I: Integer;
+ C: PControl;
+begin
+ if Msg.message = WM_SIZE then
+ begin
+ for I:= 0 to Self_.fChildren.fCount - 1 do
+ begin
+ C := Self_.fChildren.Items[ I ];
+ C.Perform( CM_SIZE, 0, 0 );
+ end;
+ end;
+ Result := False; // don't stop further processing
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function WndProcParentResize(Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+begin
+ Result := False;
+ case Msg.message of
+ CM_SIZE:
+ begin
+ Self_.Perform( WM_SIZE, 0, 0 );
+ Self_.Invalidate;
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure InitCommonControlCommonNotify( Ctrl: PControl );
+var AParent: PControl;
+begin
+ {$IFDEF USE_FLAGS} include( Ctrl.fFlagsG5, G5_IsCommonCtl );
+ {$ELSE} Ctrl.fIsCommonControl := True; {$ENDIF}
+ AParent := Ctrl.Parent;
+ if AParent <> nil then
+ begin
+ Ctrl.AttachProc( WndProcCommonNotify );
+ AParent.AttachProc( WndProcNotify );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure InitCommonControlSizeNotify( Ctrl: PControl );
+var AParent: PControl;
+begin
+ AParent := Ctrl.Parent;
+ if AParent <> nil then
+ begin
+ Ctrl.AttachProc( WndProcParentResize );
+ AParent.AttachProc( WndProcResize );
+ end;
+end;
+
+function _NewCommonControl( AParent: PControl; ClassName: PKOLChar; Style: DWORD;
+ Ctl3D: Boolean; Actions: TCommandActionsParam ): PControl;
+begin
+ {*************} DoInitCommonControls( ICC_WIN95_CLASSES );
+ Result := _NewControl( AParent, ClassName, Style, Ctl3D, Actions );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:CommonControl';
+ {$ENDIF}
+ InitCommonControlCommonNotify( Result );
+end;
+
+//==================== Progress bar ======================//
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewProgressbar( AParent: PControl ): PControl;
+begin
+ new( Result, CreateProgressbar( AParent ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Progressbar';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewProgressbar( AParent: PControl ): PControl;
+begin
+ Result := _NewCommonControl( AParent, PROGRESS_CLASS,
+ WS_CHILD or WS_VISIBLE, True,
+ {$IFDEF PACK_COMMANDACTIONS} PAnsiChar( PROGRESS_ACTIONS )
+ {$ELSE} nil {$ENDIF} );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:ProgressBar';
+ {$ENDIF}
+ with Result.fBoundsRect do
+ begin
+ Right := Left + 300;
+ Bottom := Top + 20;
+ end;
+ Result.fMenu := 0;
+ Result.fTextColor := clHighlight;
+ Result.fCommandActions.aSetBkColor := PBM_SETBKCOLOR;
+ //Result.fNCDestroyed := TRUE; // do not call DestroyWindow!
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF USE_CONSTRUCTORS}
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewProgressbarEx( AParent: PControl; Options: TProgressbarOptions ): PControl;
+begin
+ new( Result, CreateProgressbarEx( AParent, Options ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:ProgressBarEx';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewProgressbarEx( AParent: PControl; Options: TProgressbarOptions ): PControl;
+const ProgressBarFlags: array[ TProgressbarOption ] of Integer =
+ (PBS_VERTICAL, PBS_SMOOTH );
+begin
+ Result := NewProgressbar( AParent );
+ Result.fStyle.Value := Result.fStyle.Value or
+ DWORD( MakeFlags( @Options, ProgressBarFlags ) );
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF USE_CONSTRUCTORS}
+
+//===================== List view ========================//
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function WndProcNotify( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var NMhdr: PNMHdr;
+ Child: PControl;
+begin
+ Result := False;
+ if Msg.message = WM_NOTIFY then
+ begin
+ NMhdr := Pointer( Msg.lParam );
+ {$IFDEF USE_PROP}
+ Child := Pointer( GetProp( NMhdr.hwndFrom, ID_SELF ) );
+ {$ELSE}
+ Child := Pointer( GetWindowLong( NMhdr.hwndFrom, GWL_USERDATA ) );
+ {$ENDIF}
+ if (Child <> nil)
+ and (Child <> Self_) //+ by Galkov, Jun-2009
+ then
+ begin
+ Msg.hwnd := Child.fHandle;
+ Result := EnumDynHandlers( Child, Msg, Rslt );
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function WndProcCommonNotify( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var NMhdr: PNMHdr;
+begin
+ Result := False;
+ if Msg.message = WM_NOTIFY then
+ begin
+ NMHdr := Pointer( Msg.lParam );
+ case NMHdr.code of
+ NM_RCLICK,
+ NM_CLICK: {$IFDEF NIL_EVENTS}
+ if assigned( Self_.EV.fOnClick ) then
+ {$ENDIF}
+ begin
+ {$IFDEF USE_FLAGS}
+ if NMHdr.code = NM_RCLICK then
+ include( Self_.fFlagsG6, G6_RightClick )
+ else exclude( Self_.fFlagsG6, G6_RightClick );
+ {$ELSE} Self_.fRightClick := NMHdr.code=NM_RCLICK; {$ENDIF}
+ Self_.EV.fOnClick( Self_ );
+ end;
+ NM_KILLFOCUS: {$IFDEF NIL_EVENTS}
+ if assigned( Self_.EV.fOnLeave ) then
+ {$ENDIF}
+ Self_.EV.fOnLeave( Self_ );
+ NM_RETURN,
+ NM_SETFOCUS: {$IFDEF NIL_EVENTS}
+ if assigned( Self_.EV.fOnEnter ) then
+ {$ENDIF}
+ Self_.EV.fOnEnter( Self_ );
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+const ListViewStyles: array[ TListViewStyle ] of DWORD = ( LVS_ICON, LVS_SMALLICON,
+ LVS_LIST, LVS_REPORT, LVS_REPORT or LVS_NOCOLUMNHEADER );
+ ListViewFlags: array[ TListViewOption ] of Integer = ( LVS_ALIGNLEFT, LVS_AUTOARRANGE,
+ $400 {LVS_BUTTON}, LVS_EDITLABELS, LVS_NOLABELWRAP,
+ LVS_NOSCROLL, LVS_NOSORTHEADER,
+ not LVS_SHOWSELALWAYS, not LVS_SINGLESEL, LVS_SORTASCENDING,
+ LVS_SORTDESCENDING, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ LVS_OWNERDATA, LVS_OWNERDRAWFIXED );
+
+ ListViewExFlags: array[ TListViewOption ] of Integer = ( 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, LVS_EX_GRIDLINES,
+ LVS_EX_SUBITEMIMAGES, LVS_EX_CHECKBOXES, LVS_EX_TRACKSELECT,
+ LVS_EX_HEADERDRAGDROP, LVS_EX_FULLROWSELECT, LVS_EX_ONECLICKACTIVATE,
+ LVS_EX_TWOCLICKACTIVATE, LVS_EX_FLATSB, LVS_EX_REGIONAL,
+ LVS_EX_INFOTIP, LVS_EX_UNDERLINEHOT, LVS_EX_MULTIWORKAREAS, 0, 0 );
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure ApplyImageLists2Control( Sender: PControl );
+var IL: PImageList;
+begin
+ if Sender.fCommandActions.aSetImgList = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>}
+ IL := Sender.ImageListNormal;
+ if IL <> nil then
+ Sender.Perform( Sender.fCommandActions.aSetImgList, LVSIL_NORMAL, IL.Handle );
+ IL := Sender.ImageListSmall;
+ if IL <> nil then
+ Sender.Perform( Sender.fCommandActions.aSetImgList, LVSIL_SMALL, IL.Handle );
+ IL := Sender.ImageListState;
+ if IL <> nil then
+ Sender.Perform( Sender.fCommandActions.aSetImgList, LVSIL_STATE, IL.Handle );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure ApplyImageLists2ListView( Sender: PControl );
+var Flags: DWORD;
+begin
+ Flags := MakeFlags( @Sender.DF.fLVOptions, ListViewFlags );
+ Sender.Style := Sender.Style and not $403F//$4FFC
+ or Flags or ListViewStyles[ Sender.DF.fLVStyle ];
+ Flags := MakeFlags( @Sender.DF.fLVOptions, ListViewExFlags );
+ Sender.Perform( LVM_SETEXTENDEDLISTVIEWSTYLE, $3FFF, Flags );
+ ApplyImageLists2Control( Sender );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewListView( AParent: PControl; Style: TListViewStyle; Options: TListViewOptions;
+ ImageListSmall, ImageListNormal, ImageListState: PImageList ): PControl;
+begin
+ new( Result, CreateListView( AParent, Style, Options, ImageListSmall,
+ ImageListNormal, ImageListState ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:ListView';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewListView( AParent: PControl; Style: TListViewStyle; Options: TListViewOptions;
+ ImageListSmall, ImageListNormal, ImageListState: PImageList ): PControl;
+begin
+ Result := _NewCommonControl( AParent, WC_LISTVIEW, ListViewStyles[ Style ] or
+ LVS_SHAREIMAGELISTS or WS_CHILD or WS_VISIBLE or WS_TABSTOP or WS_CLIPCHILDREN,
+ True, {$IFDEF PACK_COMMANDACTIONS} ListViewActions_Packed
+ {$ELSE} @ListViewActions {$ENDIF} );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:ListView';
+ {$ENDIF}
+ {$IFDEF PACK_COMMANDACTIONS}
+ Result.fCommandActions.aClear := @ClearListView;
+ {$ENDIF}
+ Result.DF.fLVOptions := Options;
+ Result.DF.fLVStyle := Style;
+ Result.fStyle.Value := Result.fStyle.Value and not LVS_TYPESTYLEMASK
+ or DWORD( MakeFlags( @Options, ListViewFlags ) );
+ Result.PP.fCreateWndExt := ApplyImageLists2ListView;
+ with Result.fBoundsRect do
+ begin
+ Right := Left + 200;
+ Bottom := Top + 150;
+ end;
+ Result.ImageListSmall := ImageListSmall;
+ Result.ImageListNormal := ImageListNormal;
+ Result.ImageListState := ImageListState;
+ Result.DF.fLVTextBkColor := clWindow;
+ Result.fLookTabKeys := [ tkTab ];
+ //Result.fMargin := 0;
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF USE_CONSTRUCTORS}
+
+//===================== Tree view ========================//
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function WndProcTreeView( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var NM: PNMTreeView;
+ DI: PTVDispInfo;
+ P: TPoint;
+ S: KOL_String;
+begin
+ if Msg.message = WM_NOTIFY then
+ begin
+ NM := Pointer( Msg.lParam );
+ case NM.hdr.code of
+ NM_RCLICK:
+ begin
+ GetCursorPos( P );
+ P := Self_.Screen2Client( P );
+ Self_.PostMsg( WM_RBUTTONUP, MK_RBUTTON or GetShiftState,
+ (P.x and $FFFF) or (P.y shl 16) );
+ end;
+ TVN_BEGINDRAG {$IFDEF TV_DRAG_RBUTTON}, TVN_BEGINRDRAG{$ENDIF}:
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnTVBeginDrag ) then
+ {$ENDIF}
+ Self_.EV.fOnTVBeginDrag( Self_, NM.itemNew.hItem );
+ TVN_BEGINLABELEDIT:
+ begin
+ if {$IFDEF USE_FLAGS} G6_Dragging in Self_.fFlagsG6
+ {$ELSE} Self_.fDragging {$ENDIF} then
+ begin
+ Rslt := 1; // do not allow edit while dragging
+ Result := TRUE; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ DI := Pointer( NM );
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnTVBeginEdit ) then
+ {$ENDIF}
+ begin
+ Rslt := Integer( not Self_.EV.fOnTVBeginEdit( Self_, DI.item.hItem ) );
+ Result := TRUE; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ TVN_ENDLABELEDIT:
+ begin
+ DI := Pointer( NM );
+ if Assigned( Self_.EV.fOnTVEndEdit ) then
+ begin
+ S := DI.item.pszText;
+ if (DI.item.pszText = nil) then
+ begin
+ Result := True; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Rslt := Integer(
+ Self_.EV.fOnTVEndEdit( Self_, DI.item.hItem, S ) );
+ end
+ else
+ Rslt := 1;
+ Result := True; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ TVN_ITEMEXPANDING:
+ begin
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnTVExpanding ) then
+ {$ENDIF}
+ begin
+ Rslt := Integer( Self_.EV.fOnTVExpanding( Self_, NM.itemNew.hItem,
+ NM.action = TVE_EXPAND ) );
+ //Result := TRUE; //Exit;
+ end;
+ end;
+ TVN_ITEMEXPANDED:
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnTVExpanded ) then
+ {$ENDIF}
+ Self_.EV.fOnTVExpanded( Self_, NM.itemNew.hItem, NM.action=TVE_EXPAND );
+ TVN_SELCHANGING:
+ begin //------------------ TVN_SELCHANGING by Sergey Shisminzev
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnTVSelChanging ) then
+ {$ENDIF}
+ begin
+ Rslt := Integer( not Self_.EV.fOnTVSelChanging( Self_, NM.itemOld.hItem, NM.itemNew.hItem ) );
+ //Result := TRUE; //Exit;
+ end;
+ end; //----------------------------------------
+ TVN_SELCHANGED:
+ Self_.DoSelChange;
+ end;
+ end;
+ Result := False;
+end;
+{$ENDIF PAS_VERSION}
+
+function ProcTVDeleteItem( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var NM: PNMTreeView;
+begin
+ if Msg.message = WM_NOTIFY then
+ begin
+ NM := Pointer( Msg.lParam );
+ case NM.hdr.code of
+ TVN_DELETEITEM:
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnTVDelete ) then
+ {$ENDIF}
+ Self_.EV.fOnTVDelete( Self_, NM.itemOld.hItem );
+ end;
+ end;
+ Result := FALSE;
+end;
+
+procedure ClearTreeView( TV: PControl );
+begin
+ TV.TVDelete( TVI_ROOT );
+end;
+
+const
+ TreeViewFlags: array[ TTreeViewOption ] of Integer = ( not TVS_HASLINES, TVS_LINESATROOT,
+ not TVS_HASBUTTONS, TVS_EDITLABELS, not TVS_SHOWSELALWAYS,
+ not TVS_DISABLEDRAGDROP, TVS_NOTOOLTIPS, TVS_CHECKBOXES,
+ TVS_TRACKSELECT, TVS_SINGLEEXPAND, TVS_INFOTIP,
+ TVS_FULLROWSELECT, TVS_NOSCROLL, TVS_NONEVENHEIGHT );
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewTreeView( AParent: PControl; Options: TTreeViewOptions;
+ ImgListNormal, ImgListState: PImageList ): PControl;
+begin
+ new( Result, CreateTreeView( AParent, Options, ImgListNormal, ImgListState ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:TreeView';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewTreeView( AParent: PControl; Options: TTreeViewOptions;
+ ImgListNormal, ImgListState: PImageList ): PControl;
+var Flags: Integer;
+begin
+ Flags := MakeFlags( @Options, TreeViewFlags );
+ Result := _NewCommonControl( AParent, WC_TREEVIEW, Flags or WS_VISIBLE or
+ WS_CHILD or WS_TABSTOP, True, {$IFDEF PACK_COMMANDACTIONS} TreeViewActions_Packed
+ {$ELSE} @TreeViewActions {$ENDIF} );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:TreeView';
+ {$ENDIF}
+ {$IFDEF PACK_COMMANDACTIONS}
+ Result.fCommandActions.aClear := @ClearTreeView;
+ {$ENDIF}
+ Result.PP.fCreateWndExt := ApplyImageLists2Control;
+ Result.fColor := clWindow;
+ Result.AttachProc( WndProcTreeView );
+ with Result.fBoundsRect do
+ begin
+ Right := Left + 150;
+ Bottom := Top + 200;
+ end;
+ Result.ImageListNormal := ImgListNormal;
+ Result.ImageListState := ImgListState;
+ Result.fLookTabKeys := [ tkTab ];
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF USE_CONSTRUCTORS}
+
+//===================== Tab Control ========================//
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function WndProcTabControl( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var Hdr: PNMHdr;
+ A: Integer;
+ R: TRect;
+ WasActive: Boolean;
+{$IFDEF OLD_ALIGN}
+ Page: PControl;
+ I: Integer;
+begin
+ case Msg.message of
+ WM_NOTIFY:
+ begin
+ Hdr := Pointer( Msg.lParam );
+ case Hdr.code of
+ TCN_SELCHANGING:
+ Self_.fCurIndex := Self_.GetCurIndex;
+ TCN_SELCHANGE:
+ begin
+ A := {Self_.????}Self_.GetCurIndex;
+ WasActive := Self_.fCurIndex = A;
+ Self_.fCurIndex := A;
+ for I := 0 to Self_.Count - 1 do
+ begin
+ Page := Self_.Pages[ I ];
+ Page.Visible := A = I;
+ if A = I then
+ Page.BringToFront;
+ end;
+ if not WasActive then
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnSelChange ) then
+ {$ENDIF}
+ Self_.EV.fOnSelChange( Self_ );
+ end;
+ end;
+ end;
+ WM_SIZE:
+ begin
+ GetClientRect( Self_.fHandle, R );
+ Self_.Perform( TCM_ADJUSTRECT, 0, Integer( @R ) );
+ for I := 0 to Self_.Count - 1 do
+ begin
+ Page := Self_.Pages[ I ];
+ Page.BoundsRect := R;
+ end;
+{$ELSE NEW_ALIGN}
+begin
+ case Msg.message of
+ WM_NOTIFY:
+ begin
+ Hdr := Pointer( Msg.lParam );
+ case Hdr.code of
+ TCN_SELCHANGING:
+ Self_.fCurIndex := Self_.GetCurIndex;
+ TCN_SELCHANGE:
+ begin
+ A := Self_.GetCurIndex;
+ WasActive := Self_.fCurIndex = A;
+ if (not WasActive)and(Self_.fCurIndex>=0) then
+ Self_.Pages[Self_.fCurIndex].Visible := false;
+ Self_.fCurIndex := A;
+ Self_.Pages[Self_.fCurIndex].Visible := true;
+ Self_.Pages[Self_.fCurIndex].BringToFront;
+ if not WasActive then
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnSelChange ) then
+ {$ENDIF}
+ Self_.EV.fOnSelChange( Self_ );
+ end;
+ end;
+ end;
+ WM_SIZE:
+ begin
+ GetClientRect( Self_.fHandle, R );
+ Self_.fClientRight := R.Right;
+ Self_.fClientBottom := R.Bottom;
+ Self_.Perform( TCM_ADJUSTRECT, 0, Integer( @R ) );
+ Self_.fClientLeft := R.Left;
+ Self_.fClientTop := R.Top;
+ Dec(Self_.fClientRight,R.Right);
+ Dec(Self_.fClientBottom,R.Bottom);
+{$ENDIF}
+ end;
+ end;
+ Result := False;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF GRAPHCTL_XPSTYLES}
+ {$DEFINE RICHEDIT_XPBORDER}
+{$ENDIF}
+
+{$IFDEF RICHEDIT_XPBORDER}
+function WndProc_RichEditXPBorder( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var ExStyle: DWORD;
+ DrawRect, EmptyRect: TRect;
+ DC: HDC;
+ Details: TThemedElementDetails;
+begin
+ Result := FALSE;
+ if Msg.message = WM_NCPAINT then
+ begin
+ ExStyle := GetWindowLong(Self_.Handle, GWL_EXSTYLE);
+ if (ExStyle and WS_EX_CLIENTEDGE) <> 0 then
+ begin
+ GetWindowRect(Self_.Handle, DrawRect);
+ OffsetRect(DrawRect, -DrawRect.Left, -DrawRect.Top);
+ DC := GetWindowDC(Self_.Handle);
+ //try
+ EmptyRect := DrawRect;
+ with DrawRect do
+ ExcludeClipRect(DC, Left + 2, Top + 2, Right - 2, Bottom - 2);
+ Details.Element := teEdit;
+ Details.Part := 1 {EP_EDITTEXT};
+ Details.State := Ord(teEditTextNormal) - Ord(teEditTextNormal) + 1;
+ if not Assigned( DrawThemeBackground ) then
+ begin
+ ThemeLibrary := LoadLibrary(themelib);
+ DrawThemeBackground := GetProcAddress(ThemeLibrary, 'DrawThemeBackground');
+ OpenThemeData := GetProcAddress(ThemeLibrary, 'OpenThemeData');
+ end;
+ if Assigned( DrawThemeBackground ) then
+ begin
+ Result := TRUE;
+ Rslt := Self_.CallDefWndProc( Msg );
+ with Details do
+ DrawThemeBackground(OpenThemeData(0, 'edit'),
+ DC, Part, State, DrawRect, nil);
+ end;
+ //finally
+ ReleaseDC(Self_.Handle, DC);
+ //end;
+ end;
+ end;
+end;
+{$ENDIF RICHEDIT_XPBORDER}
+
+const TabControlFlags: array[ TTabControlOption ] of Integer = ( TCS_BUTTONS,
+ TCS_FIXEDWIDTH, not TCS_FOCUSNEVER,
+ TCS_FIXEDWIDTH or TCS_FORCEICONLEFT, TCS_FIXEDWIDTH or TCS_FORCELABELLEFT,
+ TCS_MULTILINE, TCS_MULTISELECT, TCS_RIGHTJUSTIFY, TCS_SCROLLOPPOSITE,
+ TCS_BOTTOM, TCS_VERTICAL, TCS_FLATBUTTONS, TCS_HOTTRACK, 0, TCS_OWNERDRAWFIXED );
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewTabControl( AParent: PControl; Tabs: array of String; Options: TTabControlOptions;
+ ImgList: PImageList; ImgList1stIdx: Integer ): PControl;
+begin
+ new( Result, CreateTabControl( AParent, Tabs, Options, ImgList, ImgList1stIdx ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:TabControl';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function NewTabControl( AParent: PControl; const Tabs: array of PKOLChar; Options: TTabControlOptions;
+ ImgList: PImageList; ImgList1stIdx: Integer ): PControl;
+var I, II : Integer;
+ Flags: Integer;
+begin
+ Flags := MakeFlags( @Options, TabControlFlags );
+ if tcoFocusTabs in Options then
+ Flags := Flags or (WS_TABSTOP or TCS_FOCUSONBUTTONDOWN);
+ Result := _NewCommonControl( AParent, WC_TABCONTROL,
+ Flags or (WS_CHILD or WS_CLIPSIBLINGS or WS_CLIPCHILDREN or WS_VISIBLE),
+ True, {$IFDEF PACK_COMMANDACTIONS} TabControlActions_Packed
+ {$ELSE} @TabControlActions {$ENDIF} );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:TabControl';
+ {$ENDIF}
+ if not( tcoBorder in Options ) then
+ begin
+ Result.fExStyle := Result.fExStyle and not WS_EX_CLIENTEDGE;
+ end;
+ Result.AttachProc( WndProcTabControl );
+ with Result.fBoundsRect do
+ begin
+ Right := Left + 100;
+ Bottom := Top + 100;
+ end;
+ if ImgList <> nil then
+ Result.Perform( TCM_SETIMAGELIST, 0, ImgList.Handle );
+ II := ImgList1stIdx;
+ for I := 0 to High( Tabs ) do
+ begin
+ Result.TC_Insert( I, Tabs[ I ], II );
+ Inc( II );
+ end;
+ Result.fLookTabKeys := [ tkTab ];
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFNDEF OLD_ALIGN}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewTabEmpty( AParent: PControl; Options: TTabControlOptions;
+ ImgList: PImageList ): PControl;
+var Flags: Integer;
+begin
+ Flags := MakeFlags( @Options, TabControlFlags );
+ if tcoFocusTabs in Options then
+ Flags := Flags or (WS_TABSTOP or TCS_FOCUSONBUTTONDOWN);
+ Result := _NewCommonControl( AParent, WC_TABCONTROL,
+ Flags or (WS_CHILD or WS_CLIPSIBLINGS or WS_CLIPCHILDREN or WS_VISIBLE),
+ True, {$IFDEF PACK_COMMANDACTIONS} TabControlActions_Packed
+ {$ELSE} @TabControlActions {$ENDIF} );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:TabControl(TabEmpty)';
+ {$ENDIF}
+ if not( tcoBorder in Options ) then
+ Result.fExStyle := Result.fExStyle and not WS_EX_CLIENTEDGE;
+ Result.AttachProc( WndProcTabControl );
+ with Result.fBoundsRect do begin
+ Right := Left + 100;
+ Bottom := Top + 100;
+ end;
+ if ImgList <> nil then
+ Result.Perform( TCM_SETIMAGELIST, 0, ImgList.Handle );
+ Result.fLookTabKeys := [ tkTab ];
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF}
+
+{$ENDIF USE_CONSTRUCTORS}
+
+//===================== Tool bar ========================//
+
+{$IFDEF ASM_TLIST} //TTN_NEEDTEXTW ASM_TLIST!
+{$IFDEF _D3orHigher}
+{$IFDEF ASM_VERSION}
+procedure CopyPChar2WideChars( dest: PWideChar; src: PChar; Len: Integer );
+asm
+ PUSH ESI
+ PUSH EDI
+ XCHG EDI, EAX
+ MOV ESI, ECX
+ PUSH 0
+ MOV EAX, ESP
+ CALL System.@LStrFromPChar
+ MOV EAX, [ESP]
+ CALL System.@LStrLen
+ TEST EAX, EAX
+ JZ @@exit_copy
+ CMP ESI, EAX
+ JL @@1_len
+ XCHG EAX, ESI
+@@1_len:
+ POP EDX
+ PUSH EDX
+ PUSH 0
+ MOV EAX, ESP
+ CALL System.@WStrFromLStr
+
+ MOV ECX, ESI
+ INC ECX
+ POP ESI
+ PUSH ESI
+ REP MOVSW
+ MOV EAX, ESP
+ CALL System.@WStrClr
+ POP EAX
+@@exit_copy:
+ MOV EAX, ESP
+ CALL System.@LStrClr
+ POP EAX
+ POP EDI
+ POP ESI
+end;
+{$ELSE PAS_VERSION}
+procedure CopyPChar2WideChars( dest: PWideChar; src: PChar; Len: Integer );
+var W: WideString;
+ s: String;
+begin
+ s := src;
+ if Len > Length(s) then
+ Len := Length(s);
+ W := s;
+ Move( W[1], dest^, (Len+1) * Sizeof( WideChar ) );
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF _D3orHigher}
+
+function WndProcToolbarCtrl(Self_: PControl; var Msg: TMsg; var Rslt: Integer): Boolean;
+asm
+ PUSH EBX
+ XOR EBX, EBX
+ CMP word ptr [EDX].TMsg.message, WM_WINDOWPOSCHANGED
+ JNE @@chk_CM_COMMAND
+ MOV dword ptr [ECX], 0 // Rslt := 0
+ XCHG EDX, EAX
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EDX, [EDX].TControl.EV
+ MOV ECX, [EDX].TEvents.fOnResize.TMethod.Code
+ MOV EAX, [EDX].TEvents.fOnResize.TMethod.Data
+ {$ELSE}
+ MOV ECX, [EDX].TControl.EV.fOnResize.TMethod.Code
+ MOV EAX, [EDX].TControl.EV.fOnResize.TMethod.Data
+ {$ENDIF}
+ {$IFDEF NIL_EVENTS}
+ JECXZ @@ret_true1
+ {$ENDIF}
+ CALL ECX // Self_.fOnResize
+@@ret_true1:
+ MOV AL, 1 // Result := TRUE
+ POP EBX
+ RET
+@@chk_CM_COMMAND: //////////////////////////////////////////////////////////////
+ CMP word ptr [EDX].TMsg.message, CM_COMMAND
+ JNE @@chk_WM_NOTIFY
+ MOVZX ECX, word ptr [EDX].TMsg.wParam
+ MOV [EAX].TControl.DF.fTBCurItem, ECX
+ XCHG EBX, EAX
+ PUSH 0
+ PUSH ECX
+ PUSH TB_COMMANDTOINDEX
+ PUSH EBX
+ CALL TControl.Perform
+ PUSH EAX
+ PUSH VK_RETURN
+ CALL GetKeyState
+ TEST EAX, EAX
+ POP ECX
+ MOV [EBX].TControl.fCurIndex, ECX
+ {$IFDEF USE_FLAGS}
+ SETL DL
+ SHL DL, G6_RightClick
+ AND [EBX].TControl.fFlagsG6, not(1 shl G6_RightClick)
+ OR [EBX].TControl.fFlagsG6, DL
+ {$ELSE}
+ SETL DL
+ MOV [EBX].TControl.fRightClick, DL
+ {$ENDIF}
+@@ret_false1:
+ XOR EAX, EAX
+ POP EBX
+ RET
+@@chk_WM_NOTIFY: ///////////////////////////////////////////////////////////////
+ CMP word ptr [EDX].TMsg.message, WM_NOTIFY
+ JNE @@ret_false1
+ MOV EDX, [EDX].TMsg.lParam
+ MOV ECX, [EDX].TTooltipText.hdr.code
+ CMP ECX, TTN_NEEDTEXT
+ JE @@TTN_NEEDTEXT
+ CMP ECX, TTN_NEEDTEXTW
+ JNE @@chk_NM_RCLICK
+ MOV BL, 1
+@@TTN_NEEDTEXT:
+ PUSH EAX // ###>
+ PUSH EDX // ***>
+ MOV EDX, [EDX].TTooltipText.hdr.idFrom
+ MOV ECX, [EAX].TControl.DF.fTBttCmd
+ OR EAX, -1
+ JECXZ @@idxReady
+ XCHG EAX, ECX
+ CALL TList.IndexOf
+@@idxReady: // EAX = -1 or index of button tooltip
+ POP EDX //<***
+ LEA EDX, [EDX].TTooltipText.szText
+ AND word ptr [EDX], 0
+ POP ECX //<###
+ TEST EAX, EAX
+ JL @@ret_true1
+ MOV ECX, [ECX].TControl.DF.fTBttTxt
+ MOV ECX, [ECX].TStrList.fList
+ MOV ECX, [ECX].TList.fItems
+ MOV EAX, [ECX+EAX*4]
+ XCHG EAX, EDX
+ XOR ECX, ECX
+ MOV CL, 79
+ {$IFDEF _D3orHigher}
+ CMP BL, 0
+ JZ @@strlcopy
+ {$IFDEF UNICODE_CTRLS}
+ CALL WStrLCopy
+ {$ELSE}
+ //CALL CopyPChar2WideChars (inlined here)
+ PUSH ESI
+ PUSH EDI
+ XCHG EDI, EAX
+ MOV ESI, ECX
+ PUSH 0
+ MOV EAX, ESP
+ CALL System.@LStrFromPChar
+ MOV EAX, [ESP]
+ CALL System.@LStrLen
+ TEST EAX, EAX
+ JZ @@exit_copy
+ CMP ESI, EAX
+ JL @@1_len
+ XCHG EAX, ESI
+@@1_len:
+ POP EDX
+ PUSH EDX
+ PUSH 0
+ MOV EAX, ESP
+ CALL System.@WStrFromLStr
+
+ MOV ECX, ESI
+ INC ECX
+ POP ESI
+ PUSH ESI
+ REP MOVSW
+ MOV EAX, ESP
+ CALL System.@WStrClr
+ POP EAX
+@@exit_copy:
+ MOV EAX, ESP
+ CALL System.@LStrClr
+ POP EAX
+ POP EDI
+ POP ESI
+ {$ENDIF}
+ JMP @@ret_true1
+ {$ENDIF _D3orHigher}
+@@strlcopy:
+ CALL StrLCopy
+ JMP @@ret_true1
+@@chk_NM_RCLICK: ///////////////////////////////////////////////////////////////
+ CMP ECX, NM_RCLICK
+ JNE @@chk_NM_CLICK
+ {$IFDEF USE_FLAGS}
+ OR [EAX].TControl.fFlagsG6, 1 shl G6_RightClick
+ {$ELSE}
+ OR [EAX].TControl.fRightClick, 1
+ {$ENDIF}
+ MOV ECX, [EDX].TNMMouse.dwItemSpec
+ OR [EAX].TControl.fCurIndex, -1
+ XCHG EBX, EAX
+ PUSH 0
+ PUSH ECX
+ PUSH TB_COMMANDTOINDEX
+ PUSH EBX
+ CALL TControl.Perform
+ MOV [EBX].TControl.fCurIndex, EAX
+ JMP @@ret_false1
+@@chk_NM_CLICK: ///////////////////////////////////////////////////////////////
+ CMP ECX, NM_CLICK
+ JNE @@chk_TBN_DROPDOWN
+ {$IFDEF USE_FLAGS}
+ AND [EAX].TControl.fFlagsG6, not(1 shl G6_RightClick)
+ {$ELSE}
+ MOV [EAX].TControl.fRightClick, 0
+ {$ENDIF}
+ OR [EAX].TControl.DF.fTBCurItem, -1
+ OR [EAX].TControl.fCurIndex, -1
+ CMP [EDX].TTBNotify.iItem, -1
+ SETNZ AL
+ POP EBX
+ RET
+@@chk_TBN_DROPDOWN: ////////////////////////////////////////////////////////////
+ CMP ECX, TBN_DROPDOWN
+ JNE @@ret_false1
+ MOV EDX, [EDX].TTBNotify.iItem
+ MOV [EAX].TControl.DF.fTBCurItem, EDX
+ PUSH EAX
+ CALL TControl.TBItem2Index
+ POP EDX
+ MOV [EDX].TControl.fCurIndex, EAX
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EDX].TControl.EV
+ MOV ECX, [EAX].TEvents.fOnDropDown.TMethod.Code
+ {$ELSE}
+ MOV ECX, [EDX].TControl.EV.fOnDropDown.TMethod.Code
+ {$ENDIF}
+ {$IFDEF NIL_EVENTS}
+ JECXZ @@ret_z
+ {$ENDIF}
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EAX].TEvents.fOnDropDown.TMethod.Data
+ {$ELSE}
+ MOV EAX, [EDX].TControl.EV.fOnDropDown.TMethod.Data
+ {$ENDIF}
+ CALL ECX
+@@ret_z:
+ XOR EAX, EAX
+ POP EBX
+end;
+{$ELSE PAS_VERSION} //Pascal
+function WndProcToolbarCtrl(Self_: PControl; var Msg: TMsg; var Rslt: Integer): Boolean;
+var lpttt: PTooltipText;
+ idBtn, Idx: Integer;
+var Notify: PTBNotify;
+ Mouse: PNMMouse;
+{$IFNDEF _FPC}
+{$IFNDEF _D2}
+var WStr: KOLWideString;
+{$ENDIF _D2}
+{$ENDIF _FPC}
+begin
+ Result := False;
+ if Msg.message = WM_WINDOWPOSCHANGED then
+ begin
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnResize ) then
+ {$ENDIF}
+ Self_.EV.fOnResize( Self_ );
+ {$IFNDEF TOOLBAR_FORCE_CHILDALIGN}
+ //-- removed by MTsv DN (v.290), crash in Win 98:
+ //-- if WinVer >= wvNT then // todo: check it.
+ Result := TRUE; // this provides (prevents?) the Align working for child controls of Toolbar !
+ // but removing this line makes it impossible to correct the Align property for
+ // the neighbour controls on form!!!
+ {$ENDIF}
+ Rslt := 0;
+ end
+ else if Msg.message = CM_COMMAND then
+ begin
+ Self_.DF.fTBCurItem := Loword( Msg.wParam );
+ Self_.fCurIndex := Self_.Perform( TB_COMMANDTOINDEX, Loword( Msg.wParam ), 0 );
+ {$IFDEF USE_FLAGS}
+ if GetKeyState( VK_RBUTTON ) < 0 then
+ include( Self_.fFlagsG6, G6_RightClick )
+ else exclude( Self_.fFlagsG6, G6_RightClick );
+ {$ELSE} Self_.fRightClick := GetKeyState( VK_RBUTTON ) < 0; {$ENDIF}
+ end
+ else if Msg.message = WM_NOTIFY then
+ begin
+ lpttt := Pointer( Msg.lParam );
+ Notify := Pointer( Msg.lParam );
+ case lpttt.hdr.code of
+ TTN_NEEDTEXT:
+ begin
+ Result := True;
+ idBtn := lpttt.hdr.idFrom;
+ Idx := -1;
+ if Self_.DF.fTBttCmd <> nil then
+ Idx := Self_.DF.fTBttCmd.IndexOf( Pointer( idBtn ) );
+ lpttt.szText[ 0 ] := #0;
+ if Idx >= 0 then
+ {$IFDEF UNICODE_CTRLS} WStrLCopy {$ELSE} StrLCopy {$ENDIF}
+ ( lpttt.szText, Self_.DF.fTBttTxt.fList.Items[ Idx ], 79 );
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ {$IFNDEF _FPC}
+ {$IFNDEF _D2}
+ TTN_NEEDTEXTW: // for Windows XP
+ begin
+ Result := True;
+ idBtn := lpttt.hdr.idFrom;
+ Idx := -1;
+ if Self_.DF.fTBttCmd <> nil then
+ Idx := Self_.DF.fTBttCmd.IndexOf( Pointer( idBtn ) );
+ ZeroMemory( @lpttt.szText[ 0 ], 160 );
+ if Idx >= 0 then
+ begin
+ WStr := KOLWideString(Self_.DF.fTBttTxt.Items[ Idx ]);
+ if WStr <> '' then
+ Move( Wstr[ 1 ], lpttt.szText, Min( 158,
+ (Length( WStr ) + 1) * Sizeof(WideChar) ) );
+ end;
+ Exit;{>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ {$ENDIF _D2}
+ {$ENDIF _FPC}
+ NM_RCLICK:
+ begin
+ Mouse := Pointer( Msg.lParam );
+ Self_.DF.fTBCurItem := Mouse.dwItemSpec;
+ Self_.fCurIndex := Self_.Perform( TB_COMMANDTOINDEX, Mouse.dwItemSpec, 0 );
+ {$IFDEF USE_FLAGS} include( Self_.fFlagsG6, G6_RightClick );
+ {$ELSE} Self_.fRightClick := True; {$ENDIF}
+ end;
+ NM_CLICK:
+ begin
+ Self_.DF.fTBCurItem := -1; // return CurItem = -1
+ Self_.fCurIndex := -1;
+ {$IFDEF USE_FLAGS}
+ exclude( Self_.fFlagsG6, G6_RightClick );
+ {$ELSE}
+ Self_.fRightClick := False;
+ {$ENDIF}
+ Result := Notify.iItem <> -1; // do not handle - will be handled in WM_COMMAND
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ TBN_DROPDOWN:
+ begin
+ Self_.DF.fTBCurItem := Notify.iItem;
+ Self_.fCurIndex := Self_.TBItem2Index( Self_.DF.fTBCurItem );
+ {$IFDEF NIL_EVENTS}
+ if assigned( Self_.EV.fOnDropDown ) then
+ {$ENDIF}
+ Self_.EV.fOnDropDown( Self_ );
+ end;
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+const ToolbarAligns: array[ TControlAlign ] of DWORD =
+ ( CCS_NOPARENTALIGN {or CCS_NOMOVEY} {or CCS_NORESIZE} or CCS_NODIVIDER, CCS_TOP or CCS_VERT, CCS_TOP, CCS_BOTTOM or CCS_VERT, CCS_BOTTOM,
+ CCS_TOP );
+ ToolbarOptions: array[ TToolbarOption ] of Integer = ( TBSTYLE_LIST, not TBSTYLE_LIST,
+ TBSTYLE_FLAT, TBSTYLE_TRANSPARENT, TBSTYLE_WRAPABLE, CCS_NODIVIDER, 0,
+ TBSTYLE_CUSTOMERASE );
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewToolbar( AParent: PControl; Align: TControlAlign; Options: TToolbarOptions;
+ Bitmap: HBitmap; Buttons: array of PAnsiChar;
+ BtnImgIdxArray: array of Integer ) : PControl;
+begin
+ new( Result, CreateToolbar( AParent, Align, Options, Bitmap, Buttons, BtnImgIdxArray ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Toolbar';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function NewToolbar( AParent: PControl; Align: TControlAlign; Options: TToolbarOptions;
+ Bitmap: HBitmap; const Buttons: array of PKOLChar;
+ const BtnImgIdxArray: array of Integer ) : PControl;
+var Flags: DWORD;
+begin
+ if Options <> [] then
+ begin
+ if not( tboTextBottom in Options ) then
+ include( Options, tboTextRight );
+ if tboTextRight in Options then
+ exclude( Options, tboTextBottom );
+ end;
+ Flags := MakeFlags( @Options, ToolbarOptions )
+ //or TBSTYLE_AUTOSIZE
+ //or CCS_NOPARENTALIGN or CCS_NOMOVEY //or CCS_NORESIZE
+ or CCS_NODIVIDER or TBSTYLE_TRANSPARENT
+ ;
+ DoInitCommonControls( ICC_BAR_CLASSES );
+ Result := _NewCommonControl( AParent, TOOLBARCLASSNAME,
+ (ToolbarAligns[ Align ] or WS_CHILD or WS_VISIBLE or TBSTYLE_TOOLTIPS
+ or Flags and not (TBSTYLE_FLAT or TBSTYLE_TRANSPARENT)), {!ecm}
+ tbo3DBorder in Options,
+ {$IFDEF PACK_COMMANDACTIONS} PAnsiChar( TOOLBAR_ACTIONS )
+ {$ELSE} nil {$ENDIF} );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:Toolbar';
+ {$ENDIF}
+ Result.fCommandActions.aClear := ClearToolbar; ///+++ anyway +++///
+ Result.fCommandActions.aGetCount := TB_BUTTONCOUNT;
+ {$IFDEF USE_FLAGS} include( Result.fFlagsG5, G5_IsButton );
+ {$ELSE} Result.fIsButton := TRUE; {$ENDIF}
+ with Result.fBoundsRect do
+ begin
+ if Align in [ caNone ] then
+ begin
+ Bottom := Top + 26;
+ Right := Left + 1000;
+ end
+ else
+ begin
+ Left := 0; Right := 0;
+ Top := 0; Bottom := 0;
+ end;
+ end;
+ Result.AttachProc( WndProcToolbarCtrl );
+ Result.AttachProc( WndProcDoEraseBkgnd );
+ Result.Perform(TB_SETEXTENDEDSTYLE, 0, Result.Perform(TB_GETEXTENDEDSTYLE, 0, 0) or
+ TBSTYLE_EX_DRAWDDARROWS);
+
+ Result.Perform( TB_BUTTONSTRUCTSIZE, Sizeof( TTBButton ), 0 );
+ Result.Perform( TB_SETINDENT, Result.fMargin, 0 );
+ with Result.fBoundsRect do
+ begin
+ if Align in [ caLeft, caRight ] then
+ Right := Left + 24
+ else if not (Align in [caNone]) then
+ Bottom := Top + 22;
+ end;
+ {$IFnDEF TBBUTTONS_DFLT_NOAUTOSIZE}
+ Result.DF.fDefaultTBBtnStyle := TBSTYLE_AUTOSIZE;
+ {$ENDIF}
+ if Bitmap <> 0 then
+ Result.TBAddBitmap( Bitmap );
+ Result.TBAddButtons( Buttons, BtnImgIdxArray );
+ Result.Perform( WM_SIZE, 0, 0 );
+ Result.Style := Result.Style or Flags; {+ecm}
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF USE_CONSTRUCTORS}
+
+//================== DateTimePicker =====================//
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION}
+function WndProcDateTimePickerNotify( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var NMhdr: PNMHdr;
+ D: TDateTime;
+ AllowChg: Boolean;
+ NMDTString: PNMDateTimeString;
+begin
+ Result := False;
+ if Msg.message = WM_NOTIFY then
+ begin
+ NMHdr := Pointer( Msg.lParam );
+ CASE NMHdr.code OF
+ DTN_DROPDOWN:{$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnDropDown ) then
+ {$ENDIF}
+ Self_.EV.fOnDropDown( Self_ );
+ DTN_CLOSEUP: {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnCloseUp ) then
+ {$ENDIF}
+ Self_.EV.fOnCloseUp( Self_ );
+ DTN_DATETIMECHANGE:
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnChangeCtl ) then
+ {$ENDIF}
+ Self_.EV.fOnChangeCtl( Self_ );
+ DTN_USERSTRING:
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnDTPUserString ) then
+ {$ENDIF}
+ begin
+ NMDTString := Pointer( NMHdr );
+ D := Self_.DateTime;
+ AllowChg := TRUE;
+ Self_.EV.fOnDTPUserString( Self_, KOLString(NMDTString.pszUserString), D, AllowChg );
+ NMDTString.dwFlags := Integer( not AllowChg );
+ end;
+ END;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+const
+ DateTimePickerOptions: array[ TDateTimePickerOption ] of Integer = (
+ DTS_TIMEFORMAT, DTS_LONGDATEFORMAT, DTS_UPDOWN, DTS_RIGHTALIGN,
+ DTS_SHOWNONE, DTS_APPCANPARSE );
+
+function NewDateTimePicker( AParent: PControl; Options: TDateTimePickerOptions )
+ : PControl;
+var Flags: DWORD;
+const
+ CS_OFF = CS_OWNDC or CS_CLASSDC or CS_PARENTDC or CS_GLOBALCLASS or
+ CS_VREDRAW or CS_HREDRAW;
+begin
+ DoInitCommonControls( ICC_DATE_CLASSES );
+ Flags := MakeFlags( @Options, DateTimePickerOptions );
+ Result := _NewCommonControl( AParent, DATETIMEPICK_CLASS,
+ (WS_CHILD or WS_VISIBLE or WS_TABSTOP or Flags {or DTS_APPCANPARSE}),
+ TRUE, {$IFDEF PACK_COMMANDACTIONS} PAnsiChar( OTHER_ACTIONS )
+ {$ELSE} nil {$ENDIF} );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:DateTimePicker';
+ {$ENDIF}
+ Result.SetSize( 110, 24 );
+ Result.AttachProc( WndProcDateTimePickerNotify );
+end;
+
+procedure TControl.SetDateTime(Value: TDateTime);
+var ST: TSystemTime;
+ D0: TDateTime;
+begin
+ if not IsNAN( Value ) then
+ begin
+ EncodeDate( 1899, 12, 31, D0 );
+ if Trunc( Value ) < D0 then
+ Value := Frac( Value ) + D0;
+ DateTime2SystemTime( Value, ST );
+ end;
+ Perform( DTM_SETSYSTEMTIME, Integer( IsNAN( Value ) ) , Integer( @ ST ) );
+end;
+
+function TControl.GetDateTime: TDateTime;
+var ST: TSystemTime;
+begin
+ if Perform( DTM_GETSYSTEMTIME, 0, Integer( @ ST ) ) = GDT_VALID then
+ SystemTime2DateTime( ST, Result )
+ else
+ Result := NAN;
+end;
+
+function TControl.Get_SystemTime: TSystemTime;
+begin
+ //FillChar( Result, Sizeof( Result ), #0 );
+ ZeroMemory( @Result, Sizeof( Result ) );
+ Perform( DTM_GETSYSTEMTIME, 0, Integer( @ Result ) ); // <> GDT_VALID then
+end;
+
+procedure TControl.Set_SystemTime(const Value: TSystemTime);
+begin
+ Perform( DTM_SETSYSTEMTIME, Integer( Value.wYear = 0 ) , Integer( @ Value ) );
+end;
+
+function TControl.GetDate: TDateTime;
+begin
+ Result := DateTime;
+ if not IsNAN( Result ) then
+ Result := Trunc( DateTime );
+end;
+
+function TControl.GetTime: TDateTime;
+begin
+ Result := DateTime;
+ if not IsNAN( Result ) then
+ Result := Frac( Result );
+end;
+
+procedure TControl.SetDate(const Value: TDateTime);
+begin
+ if IsNAN( Value ) then
+ DateTime := Value
+ else
+ if not IsNAN( DateTime ) then
+ DateTime := Trunc( Value ) + Frac( DateTime )
+ else
+ DateTime := Trunc( Value );
+end;
+
+procedure TControl.SetTime(const Value: TDateTime);
+begin
+ if IsNAN( Value ) then
+ DateTime := Value
+ else
+ if not IsNAN( DateTime ) then
+ DateTime := Trunc( DateTime ) + Frac( Value )
+ else
+ DateTime := 1.0 + Frac( Value );
+end;
+
+function TControl.GetDateTimeRange: TDateTimeRange;
+var ST_R: array[ 0..1 ] of TSystemTime;
+begin
+ Perform( DTM_GETRANGE, 0, Integer( @ ST_R[ 0 ] ) );
+ SystemTime2DateTime( ST_R[ 0 ], Result.FromDate );
+ SystemTime2DateTime( ST_R[ 1 ], Result.ToDate );
+end;
+
+procedure TControl.SetDateTimeRange(Value: TDateTimeRange);
+var ST_R: array[ 0..1 ] of TSystemTime;
+begin
+ DateTime2SystemTime( Value.FromDate, ST_R[ 0 ] );
+ DateTime2SystemTime( Value.ToDate , ST_R[ 1 ] );
+ Perform( DTM_SETRANGE,
+ Integer( IsNAN( Value.FromDate ) ) or
+ (Integer( IsNAN( Value.ToDate ) ) shl 1),
+ Integer( @ ST_R[ 0 ] ) );
+end;
+
+function TControl.GetDateTimePickerColor( Index: TDateTimePickerColor): TColor;
+begin
+ Result := Perform( DTM_GETMCCOLOR, Integer( Index ), 0 );
+end;
+
+procedure TControl.SetDateTimePickerColor(
+ Index: TDateTimePickerColor; Value: TColor);
+begin
+ Perform( DTM_SETMCCOLOR, Integer( Index ), Color2RGB( Value ) );
+end;
+
+procedure TControl.SetDateTimeFormat(const Value: KOLString);
+begin
+ Perform( DTM_SETFORMAT, 0, Integer( PKOLChar( Value ) ) );
+end;
+
+function TControl.GetTBAutoSizeButtons: Boolean;
+begin
+ Result := DF.fDefaultTBBtnStyle and TBSTYLE_AUTOSIZE <> 0;
+end;
+
+function TControl.GetTVEditing: Boolean;
+begin
+ Result := Perform( TVM_GETEDITCONTROL, 0, 0 ) <> 0;
+end;
+
+procedure TControl.SetTBAutoSizeButtons(const Value: Boolean);
+begin
+ DF.fDefaultTBBtnStyle := Integer( Value ) shl 4;
+end;
+
+{$IFDEF USE_FLAGS}
+function TControl.GetTabStop: Boolean;
+begin
+ Result := F2_Tabstop in fStyle.f2_Style;
+end;
+
+procedure TControl.SetTabStop(const Value: Boolean);
+begin
+ if Value then include( fStyle.f2_Style, F2_Tabstop )
+ else exclude( fStyle.f2_Style, F2_Tabstop );
+end;
+
+function TControl.GetWordWrap: Boolean;
+begin
+ Result := G1_WordWrap in fFlagsG1;
+end;
+
+procedure TControl.SetWordWrap(const Value: Boolean);
+begin
+ if Value then include( fFlagsG1, G1_WordWrap )
+ else exclude( fFlagsG1, G1_WordWrap );
+end;
+
+function TControl.GetCannotDoubleBuf: Boolean;
+begin
+ Result := G1_CanNotDoublebuf in fFlagsG1;
+end;
+
+procedure TControl.SetCannotDoubleBuf(const Value: Boolean);
+begin
+ if Value then include( fFlagsG1, G1_CanNotDoublebuf )
+ else exclude( fFlagsG1, G1_CanNotDoublebuf );
+end;
+
+function TControl.GetDoubleBuffered: Boolean;
+begin
+ Result := G2_DoubleBuffered in fFlagsG2;
+end;
+
+function TControl.GetTransparent: Boolean;
+begin
+ Result := G2_Transparent in fFlagsG2;
+end;
+
+function TControl.GetIsForm: Boolean;
+begin
+ Result := G3_IsForm in fFlagsG3;
+end;
+
+function TControl.GetSizeGrip: Boolean;
+begin
+ Result := G3_SizeGrip in fFlagsG3;
+end;
+
+procedure TControl.SetSizeGrip(const Value: Boolean);
+begin
+ if Value then include( fFlagsG3, G3_SizeGrip )
+ else exclude( fFlagsG3, G3_SizeGrip );
+end;
+
+function TControl.GetIsApplet: Boolean;
+begin
+ Result := G3_IsApplet in fFlagsG3;
+end;
+
+function TControl.GetIsControl: Boolean;
+begin
+ Result := G3_IsControl in fFlagsG3;
+end;
+
+function TControl.GetIsMDIChild: Boolean;
+begin
+ Result := G3_IsMDIChild in fFlagsG3;
+end;
+
+function TControl.GetCreateVisible: Boolean;
+begin
+ Result := G4_CreateVisible in fFlagsG4;
+end;
+
+procedure TControl.SetCreateVisible(const Value: Boolean);
+begin
+ if Value then include( fFlagsG4, G4_CreateVisible )
+ else exclude( fFlagsG4, G4_CreateVisible );
+end;
+
+function TControl.GetIsButton: Boolean;
+begin
+ Result := G5_IsButton in fFlagsG5;
+end;
+
+function TControl.GetFlat: Boolean;
+begin
+ Result := G3_Flat in fFlagsG3;
+end;
+
+function TControl.GetMouseInCtl: Boolean;
+begin
+ Result := G3_MouseInCtl in fFlagsG3;
+end;
+
+function TControl.GetEraseBackground: Boolean;
+begin
+ Result := G5_EraseBkgnd in fFlagsG5;
+end;
+
+procedure TControl.SetEraseBackground(const Value: Boolean);
+begin
+ if Value then include( fFlagsG5, G5_EraseBkgnd )
+ else exclude( fFlagsG5, G5_EraseBkgnd );
+end;
+
+function TControl.Get3ButtonPress: Boolean;
+begin
+ Result := G5_3ButtonPress in fFlagsG5;
+end;
+
+function TControl.GetKeyPreview: Boolean;
+begin
+ Result := G6_KeyPreview in fFlagsG6;
+end;
+
+procedure TControl.SetKeyPreview(const Value: Boolean);
+begin
+ if Value then include( fFlagsG6, G6_KeyPreview )
+ else exclude( fFlagsG6, G6_KeyPreview );
+end;
+
+function TControl.GetIgnoreDefault: Boolean;
+begin
+ Result := G5_IgnoreDefault in fFlagsG5;
+end;
+
+procedure TControl.SetIgnoreDefault(const Value: Boolean);
+begin
+ if Value then include( fFlagsG5, G5_IgnoreDefault )
+ else exclude( fFlagsG5, G5_IgnoreDefault );
+end;
+
+function TControl.GetWindowed: Boolean;
+begin
+ Result := not(G6_GraphicCtl in fFlagsG6);
+end;
+
+procedure TControl.SetWindowed(const Value: Boolean);
+begin
+ if Value then exclude( fFlagsG6, G6_GraphicCtl )
+ else include( fFlagsG6, G6_GraphicCtl );
+end;
+
+function TControl.Get_RightClick: Boolean;
+begin
+ Result := G6_RightClick in fFlagsG6;
+end;
+
+function TControl.Get_Dragging: Boolean;
+begin
+ Result := G6_Dragging in fFlagsG6;
+end;
+
+function TControl.Get_SizeRedraw: Boolean;
+begin
+ Result := G1_SizeRedraw in fFlagsG1;
+end;
+
+procedure TControl.Set_SizeRedraw(const Value: Boolean);
+begin
+ if Value then include( fFlagsG1, G1_SizeRedraw )
+ else exclude( fFlagsG1, G1_SizeRedraw );
+end;
+
+{$ENDIF USE_FLAGS}
+
+function TControl.GetDroppedDown: Boolean;
+begin
+ Result := DF.fTBDropped
+ or (Perform( CB_GetDroppedState, 0, 0 ) <> 0);
+end;
+
+//===================== RichEdit ========================//
+{$IFNDEF NOT_USE_RICHEDIT}
+type PENLink = ^TENLink;
+ TENLink = packed record
+ hdr: TNMHDR;
+ msg: DWORD;
+ wParam: Integer;
+ lParam: Integer;
+ chrg: TCHARRANGE;
+ end;
+ TEXTRANGEA = packed record
+ chrg: TCharRange;
+ lpstrText: PAnsiChar;
+ end;
+
+{$IFDEF not_ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function WndProc_RE_LinkNotify( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var Link: PENLink;
+ Range: TextRangeA;
+ Buffer: Array[ 0..1023 ] of AnsiChar; // KOL_ANSI
+ Buf_W : array[ 0..511 ] of WideChar absolute Buffer;
+ s: KOLString;
+begin
+ Result := False;
+ if (Msg.message = WM_NOTIFY) and (PNMHdr( Msg.lParam ).code = EN_LINK) then
+ begin
+ Link := Pointer( Msg.lParam );
+ Range.chrg := Link.chrg;
+ Range.lpstrText := @Buffer[ 0 ];
+ Buffer[ 0 ] := #0;
+ Self_.Perform( EM_GETTEXTRANGE, 0, Integer( @Range ) );
+ {$IFDEF UNICODE_CTRLS}
+ s := Buf_W; //todo: check it!
+ {$ELSE}
+ {$IFDEF _D3orHigher}
+ if (Buffer[ 1 ] = #0) and (Range.chrg.cpMax - Range.chrg.cpMin > 1) then
+ begin
+ {$WARNINGS OFF}
+ s := Buf_W;
+ {$WARNINGS ON}
+ end
+ else
+ {$ENDIF}
+ s := Buffer;
+ {$ENDIF}
+ if Self_.DF.fREUrl <> nil then
+ FreeMem( Self_.DF.fREUrl );
+ if s <> '' then
+ begin
+ GetMem( Self_.DF.fREUrl, (Length(s)+1) * Sizeof(KOLChar) );
+ Move( s[1], Self_.DF.fREUrl^, (Length(s)+1)*Sizeof(KOLChar) );
+ end;
+ case Link.msg of
+ WM_MOUSEMOVE:
+ {$IFDEF NIL_EVENTS}
+ if assigned( Self_.EV.fOnREOverURL ) then
+ {$ENDIF}
+ Self_.EV.fOnREOverURL( Self_ );
+ WM_LBUTTONDOWN, WM_RBUTTONDOWN:
+ {$IFDEF NIL_EVENTS}
+ if assigned( Self_.EV.fOnREUrlClick ) then
+ {$ENDIF}
+ Self_.EV.fOnREUrlClick( Self_ );
+ end;
+ Rslt := 0;
+ Result := TRUE;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_noVERSION}
+function WndProcRichEditNotify( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+const int_IDC_ARROW = integer( IDC_ARROW );
+asm
+ CMP word ptr [EDX].TMsg.message, WM_NOTIFY
+ JNE @@chk_WM_DESTROY
+ MOV EDX, [EDX].TMsg.lParam
+ CMP [EDX].TNMHdr.code, EN_SELCHANGE
+ JNE @@ret_false
+ CALL TControl.DoSelChange
+ JMP @@ret_false
+@@chk_WM_DESTROY:
+ CMP word ptr [EDX].TMsg.message, WM_DESTROY
+ JNZ @@ret_false
+ LEA EAX, [EAX].TControl.fREUrl
+ CALL @LStrClr
+@@ret_false:
+ XOR EAX, EAX
+ RET
+end;
+{$ELSE PAS_VERSION} //Pascal
+function WndProcRichEditNotify( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var NMhdr: PNMHdr;
+begin
+ Result := False;
+ if Msg.message = WM_NOTIFY then
+ begin
+ NMHdr := Pointer( Msg.lParam );
+ case NMHdr.code of
+ EN_SELCHANGE:
+ begin
+ Self_.DoSelChange;
+ if {$IFDEF USE_FLAGS} G2_Transparent in Self_.fFlagsG2
+ {$ELSE} Self_.fTransparent {$ENDIF} then
+ Self_.Invalidate;
+ end;
+ end;
+ end
+ else
+ if Msg.message = WM_DESTROY then
+ begin
+ if Self_.DF.fREUrl <> nil then
+ FreeMem( Self_.DF.fREUrl );
+ Self_.DF.fREURL := nil;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+const RichEditflags: array [ TEditOption ] of Integer = (
+ not (es_AutoHScroll or WS_HSCROLL),
+ not (es_AutoVScroll or WS_VSCROLL),
+ 0 {es_Lowercase - not supported},
+ 0 {es_Multiline - RichEdit always multiline},
+ es_NoHideSel,
+ 0 {es_OemConvert - not suppoted},
+ 0 {es_Password - not supported},
+ es_Readonly,
+ 0 {es_UpperCase - not supported},
+ es_WantReturn, 0, es_Number );
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewRichEdit1( AParent: PControl; Options: TEditOptions ): PControl;
+begin
+ new( Result, CreateRichEdit1( AParent, Options ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:RichEdit';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF noASM_UNICODE}
+function NewRichEdit1( AParent: PControl; Options: TEditOptions ): PControl;
+const
+ RichNamesCount = High( RichEditLibnames ) + 1;
+asm
+ PUSH EDX
+
+ MOV ECX, [FRichEditModule]
+ INC ECX
+ LOOP @@loaded
+ PUSHAD
+ {$IFNDEF SMALLEST_CODE}
+ {$IFNDEF SMALLER_CODE}
+ PUSH SEM_NOOPENFILEERRORBOX or SEM_FAILCRITICALERRORS
+ CALL SetErrorMode
+ PUSH EAX
+ {$ENDIF}
+ {$ENDIF}
+@@search_richedit:
+ MOV BX, RichNamesCount + $400
+ LEA ESI, [RichEditLibNames]
+ LEA EDI, [RichEditClasses]
+ CMP [RichEditIdx], 0
+ JZ @@loo
+ LEA ESI, [ESI+(RichNamesCount-1)*4]
+ LEA EDI, [EDI+(RichNamesCount-1)*4]
+ NEG BH
+@@loo:
+ MOV ECX, [EDI]
+ MOV [RichEditClass], ECX
+ MOVSX ECX, BH
+ ADD EDI, ECX
+ MOV EAX, [ESI]
+ ADD ESI, ECX
+ PUSH EAX
+ CALL LoadLibrary
+ CMP EAX, HINSTANCE_ERROR
+ JG @@break
+ DEC BL
+ JNZ @@loo
+ JMP @@fault
+@@break:
+ MOV [FRichEditModule], EAX
+@@fault:
+ {$IFNDEF SMALLEST_CODE}
+ {$IFNDEF SMALLER_CODE}
+ CALL SetErrorMode
+ {$ENDIF}
+ {$ENDIF}
+ POPAD
+@@loaded:
+ PUSH EAX
+ PUSH EDX
+ MOV EAX, ESP
+ MOV EDX, offset[RichEditFlags]
+ XOR ECX, ECX
+ MOV CL, 10
+ CALL MakeFlags
+ XCHG ECX, EAX
+ POP EDX
+ POP EAX
+ PUSH 1
+ {$IFDEF PACK_COMMANDACTIONS}
+ PUSH [RichEditActions_Packed]
+ {$ELSE}
+ PUSH offset[RichEditActions]
+ {$ENDIF}
+ MOV EDX, [RichEditClass]
+ OR ECX, WS_VISIBLE or WS_CHILD or WS_TABSTOP or WS_BORDER or ES_MULTILINE
+ CALL _NewCommonControl
+ {$IFDEF USE_FLAGS}
+ OR [EAX].TControl.fFlagsG5, 1 shl G5_IgnoreDefault
+ {$ELSE}
+ INC [EAX].TControl.fIgnoreDefault
+ {$ENDIF}
+ POP EDX
+ TEST DH, 4 // is eoWantTab in Options ?
+ SETZ DL
+ MOV [EAX].TControl.fLookTabKeys, DL
+ PUSH EBX
+ MOV EBX, EAX
+ MOV EDX, offset[WndProcRichEditNotify]
+ CALL TControl.AttachProc
+ {$IFDEF USE_FLAGS}
+ OR [EBX].TControl.fFlagsG1, (1 shl G1_CanNotDoublebuf)
+ AND [EBX].TControl.fFlagsG2, not (1 shl G2_DoubleBuffered)
+ {$ELSE}
+ INC [EBX].TControl.fCannotDoubleBuf
+ MOV [EBX].TControl.fDoubleBuffered, 0
+ {$ENDIF USE_FLAGS}
+ ADD [EBX].TControl.fBoundsRect.Right, 100-64
+ ADD [EBX].TControl.fBoundsRect.Bottom, 200-64
+ PUSH ENM_CHANGE or ENM_SELCHANGE or ENM_REQUESTRESIZE or ENM_PROTECTED or $04000000
+ PUSH 0
+ PUSH EM_SETEVENTMASK
+ PUSH EBX
+ CALL TControl.Perform
+ MOV EAX, clWindow
+ MOV [EBX].TControl.fColor, EAX
+ CALL Color2RGB
+ PUSH EAX
+ PUSH 0
+ PUSH EM_SETBKGNDCOLOR
+ PUSH EBX
+ CALL TControl.Perform
+ {$IFDEF RICHEDIT_XPBORDER}
+ MOV EDX, offset[WndProc_RichEditXPBorder]
+ MOV EAX, EBX
+ CALL TControl.AttachProc
+ {$ENDIF RICHEDIT_XPBORDER}
+ XCHG EAX, EBX
+ POP EBX
+end;
+{$ELSE PAS_VERSION} //Pascal
+function NewRichEdit1( AParent: PControl; Options: TEditOptions ): PControl;
+var Flags, I, d, Last, SaveErrMode: Integer;
+label search_richedit;
+begin
+ {$IFDEF INPACKAGE}
+ Log( '->NewRichEdit1' );
+ TRY
+ {$ENDIF INPACKAGE}
+ if FRichEditModule = 0 then
+ begin
+ search_richedit:
+ I := RichEditIdx;
+ Last := High( RichEditLibnames );
+ d := 1;
+ if RichEditIdx > 1 then // 50W, 20A
+ begin
+ I := Last;
+ Last := 0;
+ d := -1;
+ end;
+ SaveErrMode := SetErrorMode( SEM_NOOPENFILEERRORBOX or SEM_FAILCRITICALERRORS );
+ while I <> Last + d do
+ begin
+ FRichEditModule := LoadLibrary( RichEditLibnames[ I ] );
+ RichEditClass := RichEditClasses[ I ];
+ if FRichEditModule > HINSTANCE_ERROR then break;
+ inc( I, d );
+ end;
+ if FRichEditModule <= HINSTANCE_ERROR then
+ FRichEditModule := 0;
+ SetErrorMode( SaveErrMode );
+ end;
+ Flags := MakeFlags( @Options, RichEditFlags );
+ {$IFDEF INPACKAGE}
+ Log( '//// calling _NewCommonControl' );
+ {$ENDIF INPACKAGE}
+ Result := _NewCommonControl( AParent, RichEditClass, WS_VISIBLE or WS_CHILD
+ or WS_TABSTOP or WS_BORDER or ES_MULTILINE or Flags,
+ True, {$IFDEF PACK_COMMANDACTIONS} RichEditActions_Packed
+ {$ELSE} @RichEditActions {$ENDIF} );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:RichEdit';
+ {$ENDIF}
+ {$IFDEF STATIC_RICHEDIT_DATA}{$ELSE}
+ Result.DF.fRECharFormatRec := AllocMem( Sizeof( TCharFormat ) + Sizeof( TParaFormat2 ) );
+ Result.DF.fREParaFmtRec := Pointer( Integer( @ Result.DF.fRECharFormatRec )
+ + Sizeof( TCharFormat ) );
+ Result.Add2AutoFreeEx( Result.FreeCharFormatRec );
+ {$ENDIF}
+ {$IFDEF INPACKAGE}
+ Log( '//// after _NewCommonControl called' );
+ {$ENDIF INPACKAGE}
+ Result.fLookTabKeys := [ tkTab ];
+ if eoWantTab in Options then
+ Result.fLookTabKeys := [ ];
+
+ Result.AttachProc( WndProcRichEditNotify );
+ {$IFDEF USE_FLAGS}
+ include( Result.fFlagsG1, G1_CanNotDoublebuf );
+ exclude( Result.fFlagsG2, G2_DoubleBuffered );
+ include( Result.fFlagsG5, G5_IgnoreDefault );
+ {$ELSE} Result.fCannotDoubleBuf := True;
+ Result.fDoubleBuffered := False;
+ Result.fIgnoreDefault := TRUE;
+ {$ENDIF}
+ with Result.fBoundsRect do
+ begin
+ Right := Right + 100;
+ Bottom := Top + 200;
+ end;
+ {$IFDEF INPACKAGE}
+ Log( '//// before Perform' );
+ {$ENDIF INPACKAGE}
+ Result.Perform( EM_SETEVENTMASK, 0,
+ ENM_CHANGE or ENM_SELCHANGE or ENM_REQUESTRESIZE or
+ ENM_PROTECTED or $04000000 {ENM_LINK} or ENM_KEYEVENTS );
+ {$IFDEF INPACKAGE}
+ Log( '//// after Perform' );
+ {$ENDIF INPACKAGE}
+ Result.fColor := clWindow;
+ Result.Perform( EM_SETBKGNDCOLOR, 0, Color2RGB(Result.fColor));
+ {$IFDEF RICHEDIT_XPBORDER}
+ Result.AttachProc( WndProc_RichEditXPBorder );
+ {$ENDIF}
+ {$IFDEF INPACKAGE}
+ LogOK;
+ FINALLY
+ Log( '<-NewRichEdit1' );
+ END;
+ {$ENDIF INPACKAGE}
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF NOT_USE_RICHEDIT}
+
+{$ENDIF USE_CONSTRUCTORS}
+
+function OleInitialize(pwReserved: Pointer): HResult; stdcall;
+ external 'ole32.dll' name 'OleInitialize';
+procedure OleUninitialize; stdcall;
+ external 'ole32.dll' name 'OleUninitialize';
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function OleInit: Boolean;
+begin
+ if OleInitCount = 0 then
+ begin
+ Result := False;
+ if OleInitialize( nil ) <> 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Inc( OleInitCount );
+ Result := True;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure OleUnInit;
+begin
+ if OleInitCount > 0 then
+ begin
+ Dec( OleInitCount );
+ if OleInitCount = 0 then
+ OleUninitialize;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+function SysAllocStringLen;
+ external 'oleaut32.dll' name 'SysAllocStringLen';
+procedure SysFreeString( psz: PWideChar ); stdcall;
+ external 'oleaut32.dll' name 'SysFreeString';
+
+function StringToOleStr(const Source: Ansistring): PWideChar;
+var
+ SourceLen, ResultLen: Integer;
+ Buffer: array[0..1023] of WideChar;
+begin
+ SourceLen := Length(Source);
+ if Length(Source) < SizeOf(Buffer) div 2 then
+ Result := SysAllocStringLen(Buffer, MultiByteToWideChar(0, 0,
+ PAnsiChar(Source), SourceLen, Buffer, SizeOf(Buffer) div 2))
+ else
+ begin
+ ResultLen := MultiByteToWideChar(0, 0,
+ Pointer(Source), SourceLen, nil, 0);
+ Result := SysAllocStringLen(nil, ResultLen);
+ MultiByteToWideChar(0, 0, Pointer(Source), SourceLen,
+ Result, ResultLen);
+ end;
+end;
+
+{$IFNDEF NOT_USE_RICHEDIT}
+{$IFDEF USE_CONSTRUCTORS}
+function NewRichEdit( AParent: PControl; Options: TEditOptions ): PControl;
+begin
+ new( Result, CreateRichEdit( AParent, Options ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:RichEdit';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewRichEdit( AParent: PControl; Options: TEditOptions ): PControl;
+begin
+ {$IFDEF INPACKAGE}
+ Log( '->NewRichEdit' );
+ TRY
+ {$ENDIF INPACKAGE}
+ if OleInit then
+ begin
+ {$IFDEF INPACKAGE}
+ Log( '//// OleInit OK: call NewRichEdit1' );
+ {$ENDIF INPACKAGE}
+ {$IFDEF UNICODE_CTRLS}
+ RichEditIdx := 0;
+ {$ELSE}
+ RichEditIdx := 0; // Richedit20A / RichEdit
+ {$ENDIF}
+ Result := NewRichEdit1( AParent, Options );
+ Result.DF.fCharFmtDeltaSz := 24; //sizeof( TCharFormat2 ) - sizeof( RichEdit.TCharFormat );
+ // sizeof( TCharFormat2 ) is calculated incorrectly
+ Result.DF.fParaFmtDeltaSz := sizeof( TParaFormat2 ) - sizeof( RichEdit.TParaFormat );
+ end
+ else
+ begin
+ {$IFDEF INPACKAGE}
+ Log( '//// OleInit failed: call NewRichEdit1' );
+ {$ENDIF INPACKAGE}
+ Result := NewRichEdit1( AParent, Options );
+ end;
+ {$IFDEF INPACKAGE}
+ LogOK;
+ FINALLY
+ Log( '<-NewRichEdit' );
+ END;
+ {$ENDIF INPACKAGE}
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF USE_CONSTRUCTORS}
+{$ENDIF NOT_USE_RICHEDIT}
+
+//=====================================================================//
+{$ENDIF WIN_GDI}
+
+{ TControl }
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.Init;
+{$IFNDEF OLD_EVENTS_MODEL}
+var i: Integer;
+{$ENDIF}
+begin
+ {$IFDEF CALL_INHERITED}
+ inherited; // nothing here for Delphi 4 and higher
+ {$ENDIF}
+ {$IFDEF GDI}
+ {$IFDEF OLD_EVENTS_MODEL}
+ {$IFDEF USE_GRAPHCTLS}
+ PP.fDoInvalidate := InvalidateWindowed;
+ {$ENDIF}
+ PP.fOnDynHandlers := WndProcDummy;
+ PP.fWndProcKeybd := WndProcDummy;
+ //{-2.95}PP.fWndProcResizeFlicks := WndProcDummy;
+ PP.fPass2DefProc := WndProcDummy;
+ PP.fControlClick := DummyObjProc;
+ PP.fAutoSize := DummyObjProc;
+ PP.fWndFunc := @ WndFunc;
+ {$ELSE}
+ {$IFDEF EVENTS_DYNAMIC}
+ if not Assigned( EmptyEvents.fOnMessage ) then
+ for i := 0 to idx_LastEvent do
+ EmptyEvents.MethodEvents[i].Code := DummyProcTable[InitEventsTable[i] and $F];
+ EV := @ EmptyEvents;
+ for i := 0 to High(PP.Procedures) do
+ PP.Procedures[i] := DummyProcTable[InitEventsTable[i] shr 4];
+ {$ELSE}
+ for i := 0 to idx_LastEvent do
+ begin
+ EV.MethodEvents[i].Code := DummyProcTable[InitEventsTable[i] and $F];
+ //EV.MethodEvents[i].Data := @Self;
+ if i < idx_LastProc - idx_LastEvent then
+ PP.Procedures[i] := DummyProcTable[InitEventsTable[i] shr 4];
+ end;
+ {$ENDIF}
+ {$ENDIF NEW_EVENTS_MODEL}
+ fAlphaBlend := 255;
+ //---- fCommandActions.aClear := ClearText; //--- moved to _NewWindowed
+ fColor := clBtnFace;
+ fTextColor := clWindowText;
+ {$ENDIF GDI}
+ fMargin := 2;
+ {$IFDEF GDI}
+ //fCtl3D := True; fCtl3Dchild := True;
+ fCtl3D_child := 3;
+ {$ENDIF GDI}
+ fChildren := NewList;
+ {$IFDEF GDI}
+ fClsStyle := CS_OWNDC;
+ fStyle.Value := WS_VISIBLE or WS_CLIPCHILDREN or WS_CLIPSIBLINGS or
+ WS_CAPTION or WS_SYSMENU or WS_MINIMIZEBOX or WS_MAXIMIZEBOX or
+ WS_BORDER or WS_THICKFRAME;
+ fExStyle := WS_EX_CONTROLPARENT;
+ {$ENDIF GDI}
+ {$IFDEF USE_FLAGS}
+ {$ELSE} fWindowed := True;
+ fVisible := True;
+ fEnabled := True;
+ {$ENDIF}
+ fDynHandlers := NewList;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.InitParented( AParent: PControl );
+begin
+ Init;
+ if AParent <> nil then
+ fColor := AParent.fColor;
+ Parent := AParent;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+PROCEDURE TControl.InitParented( AParent: PControl; widget: PGtkWidget;
+ need_eventbox: Boolean );
+BEGIN
+ Init;
+ fHandle := widget;
+ fCaptionHandle := fHandle;
+ fEventboxHandle := fHandle;
+ IF need_eventbox THEN
+ BEGIN
+ fEventboxHandle := gtk_event_box_new();
+ gtk_widget_set_events( fEventboxHandle, GDK_ALL_EVENTS_MASK );
+ //gtk_container_add( GTK_CONTAINER( AParent.fHandle ), fEventboxHandle );
+ gtk_widget_show( fEventboxHandle );
+ gtk_container_add( GTK_CONTAINER( fEventboxHandle ), fHandle );
+ END;
+ g_object_set_data( G_OBJECT( fEventboxHandle ), ID_SELF, @ Self );
+ if AParent <> nil then
+ fColor := AParent.fColor;
+ Parent := AParent;
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+{$IFDEF WIN_GDI}
+
+procedure TControl.InitOrthaned( AParentWnd: HWnd );
+begin
+ Init;
+ FParentWnd := AParentWnd;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+destructor TControl.Destroy;
+var I: Integer;
+ F: PControl;
+ Ico: HIcon;
+begin
+ {$IFDEF USE_CUSTOMEXTENSIONS}
+ {$I CUSTOM_TCONTROL_DESTROY.INC}
+ {$ENDIF}
+ {$IFDEF USE_MHTOOLTIP}
+ {$DEFINE destroy}
+ /////fHint.Free;
+ {$UNDEF destroy}
+ {$ENDIF USE_MHTOOLTIP}
+ {$IFDEF DEBUG_ANY}
+ F := nil;
+ TRY
+ F := ParentForm; // or Applet - for form ???
+ EXCEPT
+ asm
+ nop
+ end;
+ END;
+ {$ELSE}
+ F := ParentForm; // or Applet - for form ???
+ {$ENDIF DEBUG_ANY}
+ if F <> nil then
+ if F.DF.FCurrentControl = @Self then
+ F.DF.FCurrentControl := nil;
+
+ if fHandle <> 0 then
+ ShowWindow( fHandle, SW_HIDE );
+
+ Final;
+ {$IFDEF USE_AUTOFREE4CHILDREN}
+ {$ELSE}
+ DestroyChildren;
+ {$ENDIF}
+
+ if {$IFDEF USE_FLAGS} not(G2_Destroying in fFlagsG2)
+ {$ELSE} not fDestroying {$ENDIF} then
+ begin
+ {$IFDEF USE_FLAGS} include( fFlagsG2, G2_Destroying );
+ {$ELSE} fDestroying := True; {$ENDIF}
+
+ if {$IFDEF USE_FLAGS} G6_CtlClassNameChg in fFlagsG6
+ {$ELSE} fCtlClsNameChg {$ENDIF} then
+ begin
+ FreeMem( fControlClassName );
+ {$IFDEF USE_FLAGS} exclude( fFlagsG6, G6_CtlClassNameChg );
+ {$ELSE} fCtlClsNameChg := FALSE; {$ENDIF}
+ end;
+
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ {$ELSE}
+ fFont.Free;
+ fFont := nil;
+ fBrush.Free;
+ fBrush := nil;
+ {$ENDIF}
+ fCanvas.Free;
+ fCanvas := nil;
+
+ if fHandle <> 0 then
+ begin
+ {$IFNDEF NEW_MENU_ACCELL}
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ {$ELSE}
+ if fAccelTable <> 0 then
+ begin
+ DestroyAcceleratorTable( fAccelTable );
+ fAccelTable := 0;
+ end;
+ {$ENDIF}
+ {$ENDIF}
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ {$ELSE}
+ fMenuObj.Free;
+ while fImageList <> nil do
+ fImageList.Free;
+ {$ENDIF}
+ I := fHandle;
+ Ico := DF.fIcon;
+ if (Ico <> 0) and (Ico <> HIcon(-1)) then
+ if {$IFDEF USE_FLAGS} not(G1_IconShared in fFlagsG1)
+ {$ELSE} not fIconShared {$ENDIF} then
+ DestroyIcon( Ico );
+ if IsWindow( I ) then
+ begin
+ // RemoveProp( I, ID_SELF ); //************** Remarked By M.Gerasimov
+ {$IFDEF USE_fNCDestroyed}
+ if not fNCDestroyed then
+ {$ENDIF}
+ begin
+ {$IFDEF DEBUG_ENDSESSION}
+ if EndSession_Initiated then
+ LogFileOutput( GetStartDir + 'es_debug.txt',
+ 'DESTROYING HWND:' + Int2Str( I ) );
+ {$ENDIF}
+ (* -- moved to WM_NCDESTROY -- VK + Alexey Kirov, 23.02.2012
+ {$IFnDEF SMALLER_CODE}
+ {$IFDEF USE_PROP}
+ SetProp( I, ID_SELF, 0 );
+ {$ELSE}
+ SetWindowLong( I, GWL_USERDATA, 0 );
+ {$ENDIF}
+ {$ENDIF}
+ *)
+ DestroyWindow( I );
+ end;
+ end;
+ fHandle := 0;
+ end;
+
+ if fCustomData <> nil then
+ FreeMem( fCustomData );
+ fCustomData := nil;
+ fCustomObj.Free;
+ fCustomObj := nil;
+
+ if fTmpBrush <> 0 then
+ DeleteObject( fTmpBrush );
+ fTmpBrush := 0;
+
+ //if FCaption <> nil then FreeMem( FCaption );
+ fCaption := '';
+ //if fStatusTxt <> nil then
+ // FreeMem( fStatusTxt );
+
+ if fParent <> nil then
+ begin
+ fParent.fChildren.Remove( @Self );
+ {$IFDEF USE_AUTOFREE4CHILDREN}
+ fParent.RemoveFromAutoFree( @ Self );
+ {$ENDIF}
+ if fParent.DF.fCurrentControl = @Self then
+ fParent.DF.fCurrentControl := nil;
+ end;
+
+ fChildren.Free;
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ {$ELSE}
+ DF.fTBttCmd.Free;
+ DF.fTBttTxt.Free;
+ fTmpFont.Free;
+ {$ENDIF}
+ fDynHandlers.Free;
+ inherited;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+ {$IFDEF USE_MHTOOLTIP}
+ {$DEFINE code}
+ function TControl.GetHint: PMHHint;
+ begin
+ if fHint = nil then
+ fHint := NewHint(@Self);
+ Result := fHint;
+ end;
+ {$UNDEF code}
+ {$ENDIF}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetEnabled( Value: Boolean );
+begin
+ if GetEnabled = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$IFDEF USE_FLAGS}
+ {$ELSE}
+ fEnabled := Value;
+ {$ENDIF USE_FLAGS}
+ if Value then
+ exclude( fStyle.f3_Style, F3_Disabled )
+ else include( fStyle.f3_Style, F3_Disabled );
+ if fHandle <> 0 then
+ begin
+ {$IFDEF USE_FLAGS}
+ EnableWindow( fHandle, not(F3_Disabled in fStyle.f3_Style));
+ {$ELSE}
+ EnableWindow( fHandle, fEnabled );
+ {$ENDIF}
+ end;
+ Invalidate; // necessary for Graphic controls
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_noVERSION}
+{$ELSE PAS_VERSION} //Pascal
+function TControl.GetParentWindow: HWnd;
+begin
+ Result := GetParentWnd( TRUE );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetWindowHandle: HWnd;
+begin
+ {$IFDEF INPACKAGE}
+ Log( '->TControl.GetWindowHandle' );
+ TRY
+ {$ENDIF INPACKAGE}
+ if fHandle = 0 then
+ begin
+ {$IFDEF CREATE_HIDDEN}
+ if {$IFDEF USE_FLAGS} not(G4_CreateVisible in fFlagsG4)
+ {$ELSE} not fCreateVisible {$ENDIF} then
+ begin
+ Set_Visible( False );
+ CreateWindow; //virtual!!!
+ {$IFDEF USE_FLAGS} include( fFlagsG4, G4_CreateHidden );
+ {$ELSE} fCreateHidden := True; {$ENDIF}
+ end else
+ {$ENDIF CREATE_HIDDEN}
+ CreateWindow; //virtual!!!
+ end;
+ Result := fHandle;
+ {$IFDEF INPACKAGE}
+ LogOK;
+ FINALLY
+ Log( '<-TControl.GetWindowHandle' );
+ END;
+ {$ENDIF INPACKAGE}
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF DEBUG_CREATEWINDOW}
+procedure Debug_CreateWindow1( _Self: PControl );
+begin
+ {LogFileOutput( GetStartDir + 'Session.log', 'TControl.CreateWindow, ' +
+ ' Self = ' + Int2Str( Integer( _Self ) ) +
+ ' Caption = ' + _Self.fCaption +
+ ' fChildren = ' + Int2Hex( Integer( _Self.fChildren ), 4 ) +
+ ' ChildCount = ' + Int2Str( _Self.ChildCount ) );}
+end;
+
+procedure Debug_CreateWindow2( _Self: PControl; const Params: TCreateWndParams );
+begin
+ LogFileOutput( GetStartDir + 'Session.log',
+ ' ExStyle=' + Int2Hex( Params.ExStyle, 4 ) +
+ ' WinClassName=' + Params.WinClassName +
+ ' Caption=' + Params.Caption +
+ ' Style=' + Int2Hex( Params.Style, 4 ) +
+ ' X=' + Int2Str( Params.X ) +
+ ' Y=' + Int2Str( Params.Y ) +
+ ' Width=' + Int2Str( Params.Width ) +
+ ' Height=' + Int2Str( Params.Height ) +
+ //' WndParent=' + Int2Str( Params.WndParent ) +
+ ' Parent=' + Int2Hex( DWORD( _Self.Parent ), 6 ) +
+ ' Menu=' + Int2Str( Params.Menu ) +
+ ' hInstance=' + Int2Str( Params.WindowClass.hInstance ) +
+ ' Param=' + Int2Str( Integer( Params.Param ) ) +
+ ' WindowClass.style:' + Int2Str( Params.WindowClass.style ) +
+ ' WindowClass.lpfnWndProc:' + Int2Str( DWORD( Pointer( Params.WindowClass.lpfnWndProc ) ) ) +
+ ' WindowClass.cbClsExtra:' + Int2Str( DWORD( Params.WindowClass.cbClsExtra ) ) +
+ ' WindowClass.cbWndExtra:' + Int2Str( DWORD( Params.WindowClass.cbWndExtra ) ) +
+ ' WindowClass.hInstance:' + Int2Str( Params.WindowClass.hInstance ) +
+ ' WindowClass.hIcon:' + Int2Str( Params.WindowClass.hIcon ) +
+ ' WindowClass.hCursor:' + Int2Str( Params.WindowClass.hCursor ) +
+ ' WindowClass.hbrBackground:' + Int2Str( Params.WindowClass.hbrBackground ) +
+ ' WindowClass.lpszMenuName:' + Params.WindowClass.lpszMenuName +
+ ' WindowClass.lpszClassName:' + Params.WindowClass.lpszClassName
+ );
+end;
+{$ENDIF DEBUG_CREATEWINDOW}
+
+//var LockedWindow: HWnd;
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function TControl.CreateWindow: Boolean;
+const
+ CS_OFF = CS_OWNDC or CS_CLASSDC or CS_PARENTDC or CS_GLOBALCLASS;
+ CS_ON = 0; //CS_VREDRAW or CS_HREDRAW;
+var TempClass: TWndClass;
+ Params: TCreateWndParams;
+ ClassRegistered: Boolean;
+ {$IFDEF _FPC}
+ SClassName: AnsiString;
+ {$ENDIF PAS_VERSION}
+ {$IFDEF UNICODE_CTRLS}
+ TempOleStr : PWideChar;
+ {$ENDIF}
+ {$IFDEF CREATE_HIDDEN}
+ {$ELSE}
+ lock: Boolean;
+ {$ENDIF}
+begin
+ {$IFDEF INPACKAGE}
+ Log( '->TControl.CreateWindow' );
+ TRY
+ {$ENDIF INPACKAGE}
+ {$IFDEF DEBUG_CREATEWINDOW}
+ Debug_CreateWindow1( @ Self );
+ {$ENDIF DEBUG_CREATEWINDOW}
+ Result := False;
+ if fParent <> nil then
+ if fParent.GetWindowHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if fHandle <> 0 then
+ begin
+ {$IFDEF CREATE_HIDDEN}
+ if {$IFDEF USE_FLAGS} G4_CreateHidden in fFlagsG4
+ {$ELSE} fCreateHidden {$ENDIF} then
+ begin
+ CreateChildWindows;
+ Set_Visible( True );
+ {$IFDEF USE_FLAGS} exclude( fFlagsG4, G4_CreateHidden );
+ {$ELSE} fCreateHidden := False; {$ENDIF}
+ end else
+ begin
+ CreateChildWindows;
+ end;
+ {$ELSE}
+ begin
+ lock := LockedWindow <> 0;
+ if lock then
+ begin
+ LockWindowUpdate( fHandle );
+ LockedWindow := fHandle;
+ end;
+ CreateChildWindows;
+ if lock then
+ begin
+ LockWindowUpdate( 0 );
+ LockedWindow := 0;
+ end;
+ end;
+ {$ENDIF CREATE_HIDDEN}
+ Result := True;
+ {$IFDEF INPACKAGE}
+ LogOK;
+ {$ENDIF INPACKAGE}
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+
+ {$IFDEF USE_GRAPHCTLS}
+ if {$IFDEF USE_FLAGS} (G6_GraphicCtl in fFlagsG6)
+ {$ELSE} not fWindowed {$ENDIF} then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$ENDIF}
+
+ {$IFDEF INPACKAGE}
+ Log( '/// Filling Params' );
+ {$ENDIF INPACKAGE}
+
+ //FillChar( Params, Sizeof( Params ), 0 );
+ ZeroMemory( @Params, Sizeof( Params ) );
+ Params.WindowClass.hCursor := LoadCursor( 0, IDC_ARROW );
+ Params.WindowClass.hInstance := hInstance;
+ Params.WindowClass.lpfnWndProc := fDefWndProc;
+ Params.WindowClass.style := fClsStyle;
+ {$IFDEF _FPC}
+ SClassName := SubClassName;
+ StrCopy( Params.WinClsNamBuf, @ SClassName[ 1 ] );
+ {$ELSE}
+ {$IFNDEF UNICODE_CTRLS}
+ StrCopy( Params.WinClsNamBuf, @ SubClassName[ 1 ] );
+ {$ELSE}
+ TempOleStr := StringToOleStr(AnsiString(SubClassName));
+ lstrcpyW(Params.WinClsNamBuf, TempOleStr); // vampir_infernal 15.10.2008
+ SysFreeString( TempOleStr );
+ {$ENDIF}
+ {$ENDIF}
+ Params.Param := nil;
+ Params.Inst := hInstance;
+ Params.Menu := fMenu;
+ Params.WndParent := GetParentWnd( TRUE );
+ Params.Height := fBoundsRect.Bottom - fBoundsRect.Top;
+ if Params.Height = 0 then
+ Params.Height := CW_UseDefault;
+ Params.Width := fBoundsRect.Right - fBoundsRect.Left;
+ if Params.Width = 0 then
+ Params.Width := CW_UseDefault;
+ Params.Y := fBoundsRect.Top;
+ Params.X := fBoundsRect.Left;
+ if {$IFDEF USE_FLAGS} not(G3_IsControl in fFlagsG3)
+ {$ELSE} not fIsControl {$ENDIF}
+ and {$IFDEF USE_FLAGS} not(G2_ChangedPos in fFlagsG2)
+ {$ELSE} (fChangedPosSz and 3 = 0) {$ENDIF} then
+ begin
+ Params.Y := CW_UseDefault;
+ Params.X := CW_UseDefault;
+ end;
+ Params.Style := fStyle.Value;
+ Params.Caption := PKOLChar( fCaption );
+ Params.WinClassName := @ Params.WinClsNamBuf[ 0 ];
+ Params.ExStyle := fExStyle;
+
+ {$IFDEF INPACKAGE}
+ Log( '/// Getting class info' );
+ {$ENDIF INPACKAGE}
+ if fControlClassName <> nil then
+ begin
+ GetClassInfo( hInstance,fControlClassName,Params.WindowClass );
+ Params.WindowClass.hInstance := Params.Inst;
+ Params.WindowClass.style := Params.WindowClass.style and
+ not CS_OFF or CS_ON;
+ end;
+ if fDefWndProc = nil then
+ fDefWndProc := {$ifdef FPC}@{$endif}Params.WindowClass.lpfnWndProc;
+ if Params.WndParent = 0 then
+ if Params.Style and WS_CHILD <> 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>}
+
+ {$IFNDEF UNICODE_CTRLS}
+ ClassRegistered := GetClassInfo( Params.WindowClass.hInstance,Params.WinClassName, TempClass );
+ {$ELSE}
+ ClassRegistered := GetClassInfoW( Params.WindowClass.hInstance,Params.WinClassName, TempClass );
+ {$ENDIF}
+
+ {$IFDEF INPACKAGE}
+ Log( '/// Registering window class' );
+ {$ENDIF INPACKAGE}
+ if not ClassRegistered then
+ begin
+ Params.WindowClass.lpszClassName := Params.WinClassName;
+ Params.WindowClass.lpfnWndProc := @ WndFunc;
+ {$IFNDEF UNICODE_CTRLS}
+ if RegisterClass( Params.WindowClass ) = 0 then Exit; {>>>>>>>>>>>>>>>>>>>}
+ {$ELSE}
+ if RegisterClassW(Params.WindowClass ) = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>}
+ {$ENDIF}
+ end;
+
+ {$IFDEF DEBUG_CREATEWINDOW}
+ Debug_CreateWindow2( @ Self, Params );
+ {$ENDIF}
+ CreatingWindow := @Self;
+ {$IFDEF INPACKAGE}
+ Log( '/// Calling CreateWindowEx' );
+ {$ENDIF INPACKAGE}
+ {$IFDEF USE_MDI}
+ if Assigned( fCreateWindowProc ) then
+ fHandle := fCreateWindowProc(
+ Params.WinClassName, Params.Caption, Params.Style,
+ Params.X, Params.Y, Params.Width, Params.Height,
+ Params.WndParent, Params.WindowClass.hInstance,
+ Integer( Params.Param ) )
+ else
+ {$ENDIF}
+ begin
+ {$IFNDEF UNICODE_CTRLS}
+ fHandle := CreateWindowEx(
+ Params.ExStyle, Params.WinClassName,
+ Params.Caption, Params.Style, Params.X, Params.Y,
+ Params.Width, Params.Height, Params.WndParent,
+ Params.Menu, Params.WindowClass.hInstance,
+ Params.Param );
+ {$ELSE}
+ fHandle := CreateWindowExW(
+ Params.ExStyle{ or WS_EX_RTLREADING}, Params.WinClassName,
+ Params.Caption, Params.Style, Params.X, Params.Y,
+ Params.Width, Params.Height, Params.WndParent,
+ Params.Menu, Params.WindowClass.hInstance,
+ Params.Param );
+ {$ENDIF}
+ end;
+ {$IFDEF INPACKAGE}
+ Log( '/// CreateWindowEx called' );
+ {$ENDIF INPACKAGE}
+
+
+ {$IFDEF DEBUG_CREATEWINDOW}
+ if fHandle = 0 then
+ begin
+ MessageBox(0,
+ PKOLChar(SysErrorMessage(GetLastError)),
+ 'Error creating window',mb_iconhand);
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ {$ENDIF}
+ {$IFDEF INPACKAGE}
+ Log( '/// SendMessage WM_UPDATEUISTATE' );
+ {$ENDIF INPACKAGE}
+ SendMessage( fHandle, $0128 {WM_UPDATEUISTATE},
+ 2 {UIS_CLEAR} or (1 {UISF_HIDEFOCUS} shl 16),0);
+ {$IFDEF USE_PROP}
+ if GetProp(FHandle,ID_SELF) = 0 then
+ begin
+ CreatingWindow := nil;
+ SetProp(FHandle, ID_SELF, THandle(@Self));
+ end;
+ {$ELSE}
+ CreatingWindow := nil;
+ SetWindowLong( FHandle, GWL_USERDATA, Integer(@Self) );
+ {$ENDIF}
+ //***
+ {$IFDEF INPACKAGE}
+ Log( '/// Perform WM_SETICON' );
+ {$ENDIF INPACKAGE}
+ {$IFDEF SMALLEST_CODE}
+ {$ELSE}
+ if {$IFDEF USE_FLAGS} not(G3_IsControl in fFlagsG3)
+ {$ELSE} not fIsControl {$ENDIF} then
+ Perform( WM_SETICON, 1 {ICON_BIG}, GetIcon );
+ {$ENDIF}
+ {$IFDEF NIL_EVENTS}
+ if Assigned( PP.FCreateWndExt ) then
+ {$ENDIF}
+ PP.FCreateWndExt( @Self );
+ {$IFDEF INPACKAGE}
+ Log( '/// ApplyFont2Wnd' );
+ {$ENDIF INPACKAGE}
+
+ ApplyFont2Wnd_Proc( @Self );
+ ApplyFont2Wnd_Proc( @Self );
+
+ {$IFDEF INPACKAGE}
+ Log( '/// CreateChildWindows' );
+ {$ENDIF INPACKAGE}
+
+ CreateChildWindows;
+
+ {$IFDEF INPACKAGE}
+ Log( '/// CreateChildWindows called OK' );
+ {$ENDIF INPACKAGE}
+
+ Result := True;
+ {$IFDEF INPACKAGE}
+ LogOK;
+ FINALLY
+ Log( '<-TControl.CreateWindow' );
+ END;
+ {$ENDIF INPACKAGE}
+end;
+{$ENDIF}
+{$ENDIF WIN_GDI}
+
+{$IFDEF _X_}
+{$IFDEF GTK}
+PROCEDURE TControl.VisualizyWindow;
+VAR i: Integer;
+ C: PControl;
+BEGIN
+ IF fHandle = nil THEN Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ IF {$IFDEF USE_FLAGS} not(G3_IsApplet in fFlagsG3)
+ {$ELSE} not fIsApplet {$ENDIF}
+ AND {$IFDEF USE_FLAGS} (F3_Visible in fStyle.f3_Style)
+ {$ELSE} FVisible {$ENDIF} then
+ BEGIN
+ FOR i := 0 to ChildCount-1 do
+ BEGIN
+ C := Children[ i ];
+ if {$IFDEF USE_FLAGS} F3_Visible in fStyle.f3_Style
+ {$ELSE} C.fVisible {$ENDIF} then
+ C.VisualizyWindow;
+ END;
+ gtk_widget_show( fHandle );
+ END;
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF WIN_GDI}
+//-
+procedure TControl.CreateSubclass(var Params: TCreateParams;
+ ControlClassName: PKOLChar);
+const
+ CS_OFF = CS_OWNDC or CS_CLASSDC or CS_PARENTDC or CS_GLOBALCLASS;
+ CS_ON = 0; //CS_VREDRAW or CS_HREDRAW;
+var
+ SaveInstance: THandle;
+begin
+ if fControlClassName <> nil then
+ with Params do
+ begin
+ SaveInstance := WindowClass.hInstance;
+ {$IFNDEF UNICODE_CTRLS}
+ if not GetClassInfo(HInstance, fControlClassName, WindowClass) and
+ not GetClassInfo(0, fControlClassName, WindowClass)
+ then
+ GetClassInfo(WindowClass.hInstance, fControlClassName, WindowClass);
+ {$ELSE}
+ if not GetClassInfoW(HInstance, pWideChar(fControlClassName), WindowClass) and
+ not GetClassInfoW(0, pWidechar(fControlClassName), WindowClass)
+ then
+ GetClassInfoW(WindowClass.hInstance, pWideChar(fControlClassName), WindowClass);
+ {$ENDIF}
+ WindowClass.hInstance := SaveInstance;
+ WindowClass.style := WindowClass.style and not CS_OFF or CS_ON;
+ end;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function WndProcMouse(Self_: PControl; var Msg: TMsg; var Rslt: Integer): Boolean;
+var MouseData: TMouseEventData;
+begin
+ Result := False;
+ if (Msg.message >= WM_MOUSEFIRST) and (Msg.message <= $20A {WM_MOUSELAST}) then
+ with MouseData do
+ begin
+ Shift := Msg.wParam;
+ if GetKeyState( VK_MENU ) < 0 then
+ Shift := Shift or MK_ALT;
+ X := LoWord( Msg.lParam );
+ Y := HiWord( Msg.lParam );
+ //Button := TMouseButton(Msg.wParam);
+ // not possible: wParam can contain a combination of flags
+ // MK_CONTROL, MK_LBUTTON, MK_RBUTTON, MK_MBUTTON, MK_SHIFT, MK_XBUTTON1, MK_XBUTTON2
+ // So, Shift must be tested.
+ Button := mbNone;
+
+ StopHandling := FALSE;
+ Rslt := 0; // needed ?
+ case Msg.message of
+ WM_LBUTTONDOWN:
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnMouseDown ) then
+ {$ENDIF}
+ begin
+ Button := mbLeft;
+ Self_.EV.fOnMouseDown( Self_, MouseData );
+ end;
+ WM_RBUTTONDOWN:
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnMouseDown ) then
+ {$ENDIF}
+ begin
+ Button := mbRight;
+ Self_.EV.fOnMouseDown( Self_, MouseData );
+ end;
+ WM_MBUTTONDOWN:
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnMouseDown ) then
+ {$ENDIF}
+ begin
+ Button := mbMiddle;
+ Self_.EV.fOnMouseDown( Self_, MouseData );
+ end;
+ WM_LBUTTONUP:
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnMouseUp ) then
+ {$ENDIF}
+ begin
+ Button := mbLeft;
+ Self_.EV.fOnMouseUp( Self_, MouseData );
+ end;
+ WM_RBUTTONUP:
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnMouseUp ) then
+ {$ENDIF}
+ begin
+ Button := mbRight;
+ Self_.EV.fOnMouseUp( Self_, MouseData );
+ end;
+ WM_MBUTTONUP:
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnMouseUp ) then
+ {$ENDIF}
+ begin
+ Button := mbMiddle;
+ Self_.EV.fOnMouseUp( Self_, MouseData );
+ end;
+ WM_MOUSEMOVE:
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnMouseMove ) then
+ {$ENDIF}
+ Self_.EV.fOnMouseMove( Self_, MouseData );
+ WM_LBUTTONDBLCLK:
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnMouseDblClk ) then
+ {$ENDIF}
+ begin
+ Button := mbLeft;
+ Self_.EV.fOnMouseDblClk( Self_, MouseData );
+ end;
+ WM_RBUTTONDBLCLK:
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnMouseDblClk ) then
+ {$ENDIF}
+ begin
+ Button := mbRight;
+ Self_.EV.fOnMouseDblClk( Self_, MouseData );
+ end;
+ WM_MBUTTONDBLCLK:
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnMouseDblClk ) then
+ {$ENDIF}
+ begin
+ Button := mbMiddle;
+ Self_.EV.fOnMouseDblClk( Self_, MouseData );
+ end;
+ $020A {WM_MOUSEWHEEL}:
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnMouseWheel ) then
+ {$ENDIF}
+ Self_.EV.fOnMouseWheel( Self_, MouseData );
+ else
+ Exit; //Result := False; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Result := StopHandling;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function WndProcKeybd(Self_: PControl; var Msg: TMsg; var Rslt: Integer): Boolean;
+var C : KOLChar;
+ Key: Integer;
+begin
+ Result := True;
+ case Msg.message of
+ WM_KEYDOWN, WM_SYSKEYDOWN:
+ begin
+ {$IFDEF NIL_EVENTS}
+ if assigned( Self_.EV.fOnKeyDown ) then
+ {$ENDIF}
+ Key := Msg.wParam;
+ Self_.EV.fOnKeyDown( Self_, Key, GetShiftState );
+ Msg.wParam := Key;
+ end;
+ WM_KEYUP, WM_SYSKEYUP:
+ begin
+ {$IFDEF NIL_EVENTS}
+ if assigned( Self_.EV.fOnKeyUp ) then
+ {$ENDIF}
+ Key := Msg.wParam;
+ Self_.EV.fOnKeyUp( Self_, Key, GetShiftState );
+ Msg.wParam := Key;
+ end;
+ WM_CHAR, WM_SYSCHAR:
+ {$IFDEF NIL_EVENTS}
+ if assigned( Self_.EV.fOnChar ) then
+ {$ENDIF}
+ begin
+ C := KOLChar( Msg.wParam );
+ Self_.EV.fOnChar( Self_, C, GetShiftState );
+ Msg.wParam := Integer( C );
+ end;
+ {$IFDEF SUPPORT_ONDEADCHAR}
+ WM_DEADCHAR, WM_SYSDEADCHAR:
+ {$IFDEF NIL_EVENTS}
+ if assigned( Self_.EV.fOnDeadChar ) then
+ {$ENDIF}
+ begin
+ C := KOLChar( Msg.wParam );
+ Self_.EV.fOnDeadChar( Self_, C, GetShiftState );
+ Msg.wParam := Integer( C );
+ end;
+ {$ENDIF SUPPORT_ONDEADCHAR}
+ else begin
+ Result := False; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ if Msg.wParam <> 0 then
+ Result := False;
+end;
+{$ENDIF PAS_VERSION}
+
+function WndProcDummy(Self_: PControl; var Msg: TMsg; var Rslt: Integer): Boolean;
+begin
+ Result := False;
+end;
+
+const
+ MM_MCINOTIFY = $3B9;
+
+function WndProcOnClose( Sender: PControl; var Msg: TMsg; var Rslt: Integer )
+ : Boolean;
+var Accept: Boolean;
+begin
+ Result := FALSE;
+ if Msg.message = WM_CLOSE then
+ begin
+ {$IFDEF NEW_MODAL}
+ // version of code by Alexander Pravdin
+ begin
+ Accept := True;
+ if Assigned( Sender.EV.fOnClose ) then
+ begin
+ Sender.EV.fOnClose( Sender, Accept );
+ if AppletRunning then
+ if Accept then
+ if Sender.DF.fModal > 0 then
+ begin
+ if Sender.DF.fModalResult = 0 then
+ Sender.DF.fModalResult := Integer($80000000);
+ Msg.message := 0;
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end
+ else
+ TMethod( Sender.EV.fOnClose ).Code :=
+ {$IFDEF NIL_EVENTS} nil
+ {$ELSE} @DummyObjProc {$ENDIF}
+ else
+ begin
+ Rslt := 0;
+ Sender.DF.fModalResult := 0;
+ Result := TRUE;
+ end
+ else TMethod( Sender.EV.fOnClose ).Code :=
+ {$IFDEF NIL_EVENTS} nil
+ {$ELSE} @DummyObjProc {$ENDIF};
+ end else
+ begin
+ if Sender.DF.fModal > 0 then begin
+ if Sender.DF.fModalResult = 0 then
+ Sender.DF.fModalResult := Integer($80000000);
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+
+ if Accept then begin
+ if Sender.IsMainWindow or ( Applet = Sender ) then
+ begin
+ PostQuitMessage( 0 );
+ AppletTerminated := TRUE;
+ Rslt := 0;
+ end
+ else Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ {$ELSE}
+ begin
+ Accept := True;
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Sender.EV.fOnClose ) then
+ {$ENDIF}
+ begin
+ Sender.EV.fOnClose( Sender, Accept );
+ if (not Accept) and (AppletRunning) then
+ begin
+ Rslt := 0;
+ Result := TRUE;
+ end else
+ Sender.EV.fOnClose := nil;
+ end;
+ if Accept then
+ begin
+ if Sender.IsMainWindow or (Applet = Sender) then
+ begin
+ PostQuitMessage( 0 );
+ AppletTerminated := TRUE;
+ Rslt := 0;
+ end else
+ Exit; //Default; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ {$ENDIF}
+ end;
+end;
+
+procedure TControl.SetOnClose(const AOnClose: TOnEventAccept);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnClose := AOnClose;
+ AttachProc( WndProcOnClose );
+end;
+
+function WndProcFormOnClick( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+begin
+ Result := FALSE;
+ if (Msg.message = WM_LBUTTONDOWN) or (Msg.message = WM_LBUTTONDBLCLK) or
+ (Msg.message = WM_RBUTTONDOWN) or (Msg.message = WM_RBUTTONDBLCLK) or
+ (Msg.message = WM_MBUTTONDOWN) or (Msg.message = WM_MBUTTONDBLCLK)
+ then
+ begin
+ {$IFDEF USE_FLAGS}
+ if (Msg.message = WM_RBUTTONDOWN) or
+ (Msg.message = WM_RBUTTONDBLCLK) then
+ include( Sender.fFlagsG6, G6_RightClick )
+ else exclude( Sender.fFlagsG6, G6_RightClick );
+ {$ELSE}
+ Sender.fRightClick := (Msg.message = WM_RBUTTONDOWN) or
+ (Msg.message = WM_RBUTTONDBLCLK);
+ {$ENDIF}
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Sender.EV.fOnClick ) then
+ {$ENDIF}
+ Sender.EV.fOnClick( Sender );
+ end;
+end;
+
+procedure TControl.SetFormOnClick(const AOnClick: TOnEvent);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnClick := AOnClick;
+ AttachProc( WndProcFormOnClick );
+end;
+
+{$IFDEF ASM_VERSION}//------------------
+
+{$DEFINE ASM_LOCAL}
+{$IFDEF NEW_MODAL}
+ {$UNDEF ASM_LOCAL}
+{$ENDIF}
+{$IFDEF USE_MDI}
+ {$UNDEF ASM_LOCAL}
+{$ENDIF}
+
+{$ELSE}//-------------------------------
+
+{$IFDEF ASM_LOCAL}
+ {$UNDEF ASM_LOCAL}
+{$ENDIF}
+
+{$ENDIF}//------------------------------
+
+{$IFDEF USE_GRAPHCTLS}
+ {$UNDEF ASM_LOCAL}
+{$ENDIF}
+
+{$IFDEF ASM_LOCAL}
+{$ELSE ASM_LOCAL} //Pascal
+
+ {$IFDEF DEBUG_CREATEWINDOW}
+ var DbgCWCount: Integer = 0;
+ {$ENDIF DEBUG_CREATEWINDOW}
+function TControl.WndProc( var Msg: TMsg ): Integer;
+var C : PControl;
+ F: HWnd;
+ PassFun: function( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+
+ procedure Default;
+ begin
+ Result := CallDefWndProc( Msg );
+ end;
+
+begin
+ //RefInc;
+ {$IFDEF INPACKAGE}
+ Log( '->TControl.WndProc' );
+ TRY
+ {$ENDIF INPACKAGE}
+ {$IFDEF DEBUG_CREATEWINDOW}
+ Inc( DbgCWCount );
+ if DbgCWCount < 10 then
+ LogFileOutput( GetStartDir + 'Session.log', 'TControl.WndProc: ' +
+ ' Msg.hwnd=' + Int2Str( Msg.hwnd ) +
+ ' Msg.message=' + Int2Hex( Msg.message, 2 ) +
+ ' Msg.wParam=' + Int2Str( Msg.wParam ) + '=$' + Int2Hex( Msg.wParam, 4 ) +
+ ' Msg.lParam=' + Int2Str( Msg.lParam ) + '=$' + Int2Hex( Msg.lParam, 4 ) );
+ {$ENDIF DEBUG_CREATEWINDOW}
+ if (Msg.hwnd <> 0) and (fHandle = 0)
+ {$IFDEF USE_GRAPHCTLS} and
+ {$IFDEF USE_FLAGS} not(G6_GraphicCtl in fFlagsG6)
+ {$ELSE} fWindowed {$ENDIF} {$ENDIF} then
+ fHandle := Msg.hwnd;
+
+ {$IFDEF DEBUG_MCK} mck_Log( '01' ); {$ENDIF}
+ PassFun := PP.fPass2DefProc;
+ {$IFDEF DEBUG_MCK} mck_Log( '01' ); {$ENDIF}
+ if not (AppletRunning and (Applet <> @Self) and ( Applet <> nil ) and
+ {$IFDEF NIL_EVENTS} Assigned( Applet.EV.fOnMessage ) and {$ENDIF}
+ Applet.EV.fOnMessage( Msg, Result )) then
+ begin {$IFDEF DEBUG_MCK} mck_Log( '02' ); {$ENDIF}
+ if not ({$IFDEF NIL_EVENTS} Assigned( EV.fOnMessage ) and {$ENDIF}
+ EV.fOnMessage( Msg, Result )) then
+ begin {$IFDEF DEBUG_MCK} mck_Log( '03' ); {$ENDIF}
+ if not PP.fOnDynHandlers( @Self, Msg, Result ) then
+ begin {$IFDEF DEBUG_MCK} mck_Log( '04' ); {$ENDIF}
+ //{-2.95}//if not PP.fWndProcResizeFlicks( @Self, Msg, Result ) then
+ begin {$IFDEF DEBUG_MCK} mck_Log( '05' ); {$ENDIF}
+ case Msg.message of
+ WM_CLOSE:
+ begin // handler by default - simple:
+ if (Applet = @ Self) or IsMainWindow then
+ begin
+ PostQuitMessage( 0 );
+ AppletTerminated := TRUE;
+ end;
+ Default;
+ end;
+ (*
+ {$IFDEF USE_PROP}
+ WM_NCDESTROY:
+ begin
+ RemoveProp( fHandle, ID_SELF ); //********* Added By M.Gerasimov
+ //RefDec;
+ end;
+ {$ENDIF}
+ *)
+ WM_NCDESTROY:
+ {$IFnDEF SMALLER_CODE}
+ if fHandle = Msg.hwnd then
+ {$ENDIF}
+ begin
+ {$IFnDEF SMALLER_CODE}
+ {$IFDEF USE_PROP}
+ RemoveProp( fHandle, ID_SELF ); //********* Added By M.Gerasimov
+ {$ELSE}
+ SetWindowLong( fHandle, GWL_USERDATA, 0 ); // VK + Alexey Kirov, 23.02.2012
+ {$ENDIF}
+ {$ENDIF} //-------------------------------------------
+ Default;
+ Exit;
+ end;
+ WM_DESTROY:
+ {$IFnDEF SMALLER_CODE}
+ if fHandle = Msg.hwnd then
+ {$ENDIF}
+ begin
+ {$IFDEF USE_FLAGS} include( fFlagsG2, G2_BeginDestroying );
+ {$ELSE} fBeginDestroying := TRUE; {$ENDIF}
+ Default;
+ {$IFDEF INPACKAGE} LogOK; {$ENDIF INPACKAGE}
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ WM_SIZE: begin
+ {$IFDEF INPACKAGE}
+ Log( 'WM_SIZE >>> Default' );
+ {$ENDIF INPACKAGE}
+ Default;
+ {$IFDEF INPACKAGE}
+ Log( '//// Default called' );
+ {$ENDIF INPACKAGE}
+ {$IFDEF OLD_ALIGN}
+ if {$IFDEF USE_FLAGS} not(G3_IsForm in fFlagsG3)
+ {$ELSE} not fIsForm {$ENDIF} then
+ Global_Align( fParent );
+ {$ENDIF}
+ {$IFDEF INPACKAGE}
+ Log( '//// Before Global_Align' );
+ {$ENDIF INPACKAGE}
+ Global_Align( @Self );
+ {$IFDEF INPACKAGE}
+ LogOK;
+ {$ENDIF INPACKAGE}
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ WM_SysCommand:
+ begin
+ if ((Msg.wParam and $FFF0) = SC_MINIMIZE) and
+ IsMainWindow and (@Self <> Applet) then
+ begin
+ PostMessage( Applet.Handle, WM_SYSCOMMAND, SC_MINIMIZE, 0 );
+ Result := 0;
+ end
+ else Default;
+ end;
+ WM_SETFOCUS:
+ begin
+ if not DoSetFocus then
+ begin
+ Result := 0;
+ end
+ else
+ begin
+ Inc( fClickDisabled );
+ Default;
+ Dec( fClickDisabled );
+ {$IFDEF INPACKAGE}
+ LogOK;
+ {$ENDIF INPACKAGE}
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ WM_CTLCOLORMSGBOX..WM_CTLCOLORSTATIC:
+ begin
+ Result := SendMessage(Msg.LParam, CN_BASE + Msg.message, Msg.WParam, Msg.LParam);
+ end;
+ WM_COMMAND:
+ begin
+ {$IFDEF USE_PROP}
+ C := Pointer( GetProp( Msg.lParam, ID_SELF ) );
+ {$ELSE}
+ C := Pointer( GetWindowLong( Msg.lParam, GWL_USERDATA ) );
+ {$ENDIF}
+ if C <> nil then
+ begin
+ Result := SendMessage( Msg.lParam, CM_COMMAND, Msg.wParam, Msg.lParam );
+ end
+ else Default;
+ end;
+ WM_KEYFIRST..WM_KEYLAST:
+ begin
+ F := GetFocus;
+ if {(F <> fFocusHandle) and} (F <> fHandle)
+ {$IFDEF USE_GRAPHCTLS} and
+ {$IFDEF USE_FLAGS} not(G6_GraphicCtl in fFlagsG6)
+ {$ELSE} fWindowed {$ENDIF} {$ENDIF}
+ {$IFDEF KEY_PREVIEW}
+ and {$IFDEF USE_FLAGS} not(G4_Pushed in fFlagsG4)
+ {$ELSE} not fKeyPreviewing {$ENDIF}
+ {$ENDIF}
+ then
+ begin
+ Result := 0;
+ // Jump to PassFun here. Prevents beep in case when WM_KEYDOWN
+ // called another form and focus is changed, so WM_KEYUP failed
+ // to handle.
+ end
+ else
+ begin
+ {$IFDEF KEY_PREVIEW} //ADDITION JUST FOR CORRECT KEYPREVIEWING
+ {$IFDEF USE_FLAGS} exclude( fFlagsG4, G4_Pushed );
+ {$ELSE} fKeyPreviewing:=false; {$ENDIF}
+ {$ENDIF}
+ if fGlobalProcKeybd( @Self, Msg, Result ) then
+ begin
+ {$IFDEF INPACKAGE}
+ LogOK;
+ {$ENDIF INPACKAGE}
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ if PP.fWndProcKeybd( @Self, Msg, Result ) then
+ begin
+ {$IFDEF INPACKAGE}
+ LogOK;
+ {$ENDIF INPACKAGE}
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ if ((GetKeystate( VK_CONTROL ) or GetKeyState( VK_MENU )) >= 0) then
+ begin
+ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ if (Msg.message <> WM_CHAR) // v1.02 Tabulate AND " in EditBox fix
+ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ then
+ begin
+ C := ParentForm;
+ if (C <> nil)
+ {$IFDEF NIL_EVENTS}
+ and Assigned(C.PP.fGotoControl)
+ {$ENDIF}
+ and C.PP.fGotoControl( @Self, Msg.wParam,
+ (Msg.message <> WM_KEYDOWN) and
+ (Msg.message <> WM_SYSKEYDOWN) ) then
+ begin
+ Msg.wParam := 0;
+ Result := 0;
+ end
+ else Default;
+ end else
+ //+++++++++++++++++++++++++++++++++++++++++++++//
+ if Msg.wParam = 9 then // prevent system beep //
+ begin //
+ Msg.wParam := 0; //
+ Result := 0; //
+ end //
+ //+++++++++++++++++++++++++++++++++++++++++++++//
+ else Default;
+ end
+ else Default;
+ end;
+ end;
+ else begin
+ {$IFDEF DEBUG_MCK} mck_Log( 'else' ); {$ENDIF}
+ Default;
+ {$IFDEF INPACKAGE}
+ LogOK;
+ {$ENDIF INPACKAGE}
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ end;
+ end;
+ end;
+ end;
+ {$IFDEF DEBUG_MCK} mck_Log( '06' ); {$ENDIF}
+ if not AppletTerminated
+ {$IFDEF USE_fNCDestroyed} and not fNCDestroyed {$ENDIF} then
+ begin {$IFDEF DEBUG_MCK} mck_Log( '07' ); {$ENDIF}
+ PassFun( @Self, Msg, Result ); //+-+
+ {$IFDEF DEBUG_MCK} mck_Log( '08' ); {$ENDIF}
+ end;
+ {$IFDEF INPACKAGE}
+ LogOK;
+ FINALLY
+ Log( '<-TControl.WndProc' );
+ //RefDec;
+ END;
+ {$ELSE}
+ //RefDec;
+ {$ENDIF INPACKAGE}
+end;
+{$ENDIF ASM_LOCAL}
+
+{$UNDEF ASM_LOCAL}
+
+{$ENDIF WIN_GDI}
+{$IFDEF GDI}
+procedure SetMouseEvent( Self_: PControl );
+begin
+ Self_.AttachProc( WndProcMouse );
+end;
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+FUNCTION mouse_events_handler( Obj: PGtkWidget; VAR Event: TGdkEventAny ): Boolean;
+ CDECL;
+VAR Sender: PControl;
+ M: TMouseEventData;
+ PROCEDURE PrepareMouseEvent( const Evt: TGdkEventMotion );
+ BEGIN
+ M.Button := mbNone;
+ if Evt.state and GDK_BUTTON1_MASK <> 0 then M.Button := mbLeft
+ else
+ if Evt.state and GDK_BUTTON2_MASK <> 0 then M.Button := mbRight
+ else
+ if Evt.state and GDK_BUTTON3_MASK <> 0 then M.Button := mbMiddle;
+ M.Shift := 0;
+ if Evt.state and GDK_SHIFT_MASK <> 0 then M.Shift := MK_SHIFT;
+ if Evt.state and GDK_CONTROL_MASK <> 0 then M.Shift := M.Shift or MK_CONTROL;
+ if Evt.state and GDK_LOCK_MASK <> 0 then M.Shift := M.Shift or MK_LOCK;
+ if Evt.state and GDK_BUTTON1_MASK <> 0 then M.Shift := M.Shift or MK_LBUTTON;
+ if Evt.state and GDK_BUTTON2_MASK <> 0 then M.Shift := M.Shift or MK_RBUTTON;
+ if Evt.state and GDK_BUTTON3_MASK <> 0 then M.Shift := M.Shift or MK_MBUTTON;
+ if Evt.state and GDK_LOCK_MASK <> 0 then M.Shift := M.Shift or MK_LOCK;
+ M.X := Round( Evt.x );
+ M.Y := Round( Evt.y );
+ END;
+VAR scrl: PGdkEventScroll;
+ z: SmallInt;
+BEGIN
+ Result := FALSE;
+ //Sender := Pointer( Event.window );
+ Sender := g_object_get_data( G_OBJECT( Obj ), ID_SELF );
+ CASE Event._type OF
+ GDK_MOTION_NOTIFY,
+ GDK_BUTTON_PRESS,
+ GDK_2BUTTON_PRESS,
+ GDK_3BUTTON_PRESS, // òðîéíîé êëèê ìûøè - ñ÷èòàòü êàê äâîéíîé?
+ GDK_BUTTON_RELEASE,
+ GDK_SCROLL: ;
+ else Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ END;
+ PrepareMouseEvent( PGdkEventMotion( @ Event )^ );
+ CASE Event._type OF
+ GDK_MOTION_NOTIFY :
+ BEGIN
+ IF Assigned( Sender.fOnMouseMove ) THEN
+ BEGIN
+ Sender.fOnMouseMove( Sender, M );
+ Result := TRUE;
+ END;
+ END;
+ GDK_BUTTON_PRESS :
+ BEGIN
+ IF Assigned( Sender.fOnMouseDown ) THEN
+ BEGIN
+ Sender.fOnMouseDown( Sender, M );
+ Result := TRUE;
+ END;
+ END;
+ GDK_2BUTTON_PRESS,
+ GDK_3BUTTON_PRESS :
+ BEGIN
+ IF Assigned( Sender.fOnMouseDblClk ) THEN
+ BEGIN
+ {$IFDEF USE_FLAGS}
+ IF Event._type = GDK_3BUTTON_PRESS THEN
+ include( Sender.fFlagsG5, G5_3ButtonPress )
+ ELSE exclude( Sender.fFlagsG5, G5_3ButtonPress );
+ {$ELSE}
+ Sender.f3ButtonPress := Event._type = GDK_3BUTTON_PRESS;
+ {$ENDIF}
+ Sender.fOnMouseDblClk( Sender, M );
+ Result := TRUE;
+ END;
+ END;
+ GDK_BUTTON_RELEASE :
+ BEGIN
+ IF Assigned( Sender.fOnMouseUp ) THEN
+ BEGIN
+ Sender.fOnMouseUp( Sender, M );
+ Result := TRUE;
+ END;
+ if Assigned( Sender.fOnClick ) then
+ Sender.fOnClick( Sender );
+ END;
+ GDK_SCROLL :
+ BEGIN
+ IF Assigned( Sender.fOnMouseWheel ) THEN
+ BEGIN
+ scrl := @ Event;
+ IF scrl.direction = GDK_SCROLL_UP THEN
+ z := 120
+ ELSE IF scrl.direction = GDK_SCROLL_DOWN THEN
+ z := -120 //todo: direction and value?
+ ELSE
+ z := 0;
+ M.Shift := M.Shift or DWord(z shl 16);
+ Sender.fOnMouseWheel( Sender, M );
+ Result := TRUE;
+ END;
+ END;
+ END;
+END;
+
+PROCEDURE SetMouseEvent( Self_: PControl; event_name: PAnsiChar );
+BEGIN
+ gtk_signal_connect( GTK_OBJECT( Self_.fEventboxHandle ), event_name,
+ @mouse_events_handler, Self_ );
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+function TControl.Get_OnMouseEvent(const Index: Integer): TOnMouse;
+begin
+ Result := TOnMouse( EV.MethodEvents[Index] );
+end;
+
+procedure TControl.SetOnMouseEvent(const Index: Integer;
+ const Value: TOnMouse);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .MethodEvents[Index] := TMethod( Value );
+ AttachProc( WndProcMouse );
+end;
+
+{$IFDEF WIN_GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetClsStyle( Value: DWord );
+begin
+ if fClsStyle = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fClsStyle := Value;
+ if fHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ SetClassLong( fHandle, GCL_STYLE, Value );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetStyle( Value: DWord );
+begin
+ if fStyle.Value = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fStyle.Value := Value;
+ if fHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ SetWindowLong( fHandle, GWL_STYLE, Value );
+
+ SetWindowPos( fHandle, 0, 0, 0, 0, 0,
+ SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE or
+ SWP_NOZORDER or SWP_FRAMECHANGED );
+ Invalidate;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF GRAPHCTL_XPSTYLES}
+function TControl.GetEdgeStyle: TEdgeStyle;
+begin
+ Result := esRaised;
+ if Style and WS_DLGFRAME = 0 then
+ begin
+ if Style and SS_SUNKEN <> 0 then
+ Result := esLowered
+ else
+ Result := esNone;
+ end;
+end;
+
+procedure TControl.SetEdgeStyle( Value: TEdgeStyle );
+begin
+ {$IFDEF STORE_EDGESTYLE}
+ if fedgeStyle = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fedgeStyle := Value;
+ {$ENDIF}
+ if fHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ case Value of
+ esRaised:
+ begin
+ Style := Style and (not SS_SUNKEN);
+ ExStyle := ExStyle and (not WS_EX_STATICEDGE);
+ ExStyle := ExStyle or WS_EX_WINDOWEDGE;
+ Style := Style or WS_DLGFRAME;
+ end;
+ esLowered:
+ begin
+ Style := Style and (not WS_DLGFRAME);
+ ExStyle := ExStyle or WS_EX_WINDOWEDGE;
+ ExStyle := ExStyle or WS_EX_STATICEDGE;
+ Style := Style or SS_SUNKEN;
+ end;
+ else
+ Style := Style and (not SS_SUNKEN) and (not WS_DLGFRAME);
+ ExStyle := ExStyle and (not WS_EX_STATICEDGE) or WS_EX_WINDOWEDGE;
+ end;
+
+ Invalidate;
+end;
+{$ENDIF}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetExStyle( Value: DWord );
+begin
+ if fExStyle = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fExStyle := Value;
+ if fHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ SetWindowLong( fHandle, GWL_EXSTYLE, Value );
+
+ SetWindowPos( fHandle, 0, 0, 0, 0, 0,
+ SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE or
+ SWP_NOZORDER or SWP_FRAMECHANGED );
+ Invalidate;
+end;
+{$ENDIF PAS_VERSION}
+
+function WndProcSetCursor( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var Cur: HCursor;
+begin
+ Result := FALSE;
+ if Msg.message = WM_SETCURSOR then
+ begin
+ if (GetCapture = 0) and
+ (LOWORD( Msg.lParam ) = HTCLIENT) then
+ begin
+ if ScreenCursor <> 0 then //YS
+ Cur := ScreenCursor //YS
+ else Cur := Self_.fCursor; //YS
+ if Cur <> 0 then //YS
+ begin //YS
+ Windows.SetCursor( Cur ); //YS
+ Rslt := 1; //YS
+ Result := TRUE;
+ end;
+ end;
+ end;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetCursor( Value: HCursor );
+var P: TPoint;
+begin
+ AttachProc( WndProcSetCursor );
+ if fCursor = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fCursor := Value;
+ if (fHandle = 0) or (fCursor = 0) then Exit; //YS {>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if ScreenCursor <> 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ GetCursorPos( P );
+ P := Screen2Client( P );
+ if PointInRect( P, ClientRect ) then
+ Windows.SetCursor( Value );
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TControl.CursorLoad(Inst: Integer; ResName: PKOLChar);
+begin
+ Cursor := LoadCursor( Inst, ResName );
+ //{$IFDEF USE_FLAGS} include( fFlagsG1, G1_CursorShared );
+ //{$ELSE} fCursorShared := TRUE; {$ENDIF}
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetIcon( Value: HIcon );
+var OldIco: HIcon;
+begin
+ if DF.fIcon = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ DF.fIcon := Value;
+ if Value = THandle(-1) then
+ Value := 0;
+ OldIco := Perform( WM_SETICON, 1 {ICON_BIG}, Value );
+ if OldIco <> 0 then
+ DestroyIcon( OldIco );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetMenu( Value: HMenu );
+begin
+ if fMenu = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if fMenuObj <> nil then
+ begin
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ RemoveFromAutoFree( fMenuObj );
+ {$ENDIF}
+ fMenuObj.Free;
+ end;
+ if fMenu <> 0 then
+ DestroyMenu( fMenu );
+ fMenu := Value;
+ if fHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Windows.SetMenu( fHandle, Value );
+end;
+{$ENDIF PAS_VERSION}
+
+procedure CallWinHelp( Context: Integer; CtxCtl: PControl );
+var Cmd: Integer;
+ Form: PControl;
+ Popup: Boolean;
+begin
+ Cmd := HELP_CONTEXT;
+ if CtxCtl <> nil then
+ begin
+ Form := CtxCtl.ParentForm;
+ if Form <> nil then
+ if Assigned( Form.EV.fOnHelp ) then
+ begin
+ Popup := FALSE;
+ Form.EV.fOnHelp( CtxCtl, Context, Popup );
+ if Popup then
+ Cmd := HELP_CONTEXTPOPUP;
+ if CtxCtl = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end
+ else
+ if Context = 0 then
+ Cmd := HELP_CONTENTS;
+ WinHelp( Applet.Handle, PKOLChar( Applet.GetHelpPath ), Cmd, Context );
+end;
+
+var HHCtrl: THandle;
+ HtmlHelp: procedure( Wnd: HWnd; Path: PKOLChar; Cmd, Data: Integer ); stdcall;
+
+procedure HtmlHelpCommand( Wnd: HWnd; const HelpFilePath: KOLString; Cmd, Data: Integer );
+begin
+ if HHCtrl = 0 then
+ HHCtrl := LoadLibrary( 'HHCTRL.OCX' );
+ if HHCtrl = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if not Assigned( HtmlHelp ) then
+ HtmlHelp := GetProcAddress( HHCtrl,
+ {$IFDEF UNICODE_CTRLS} 'HtmlHelpW' {$ELSE} 'HtmlHelpA' {$ENDIF} );
+ if not Assigned( HtmlHelp ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ HtmlHelp( Wnd, PKOLChar( HelpFilePath ), Cmd, Data );
+end;
+
+procedure CallHtmlHelp( Context: Integer; CtxCtl: PControl );
+var Cmd: Integer;
+ Form: PControl;
+ Popup: Boolean;
+ Ids: array[ 0..2 ] of DWORD;
+begin
+
+ Cmd := $F; // HH_HELP_CONTEXT;
+ if CtxCtl <> nil then
+ begin
+ Form := CtxCtl.ParentForm;
+ if Form <> nil then
+ if Assigned( Form.EV.fOnHelp ) then
+ begin
+ Popup := FALSE;
+ Form.EV.fOnHelp( CtxCtl, Context, Popup );
+ if Popup then
+ begin
+ Cmd := $10; //HH_TP_HELPCONTEXTMENU;
+ Ids[ 0 ] := CtxCtl.fMenu;
+ Ids[ 1 ] := Context;
+ Ids[ 2 ] := 0;
+ Context := Integer( @ Ids );
+ end;
+ if CtxCtl = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end
+ else
+ if Context = 0 then
+ Cmd := 1; // HH_DISPLAY_TOC;
+ HtmlHelpCommand( {$IFDEF HTMLHELP_NOTOP} 0 {$ELSE} Applet.Handle {$ENDIF},
+ HelpFilePath, Cmd, Context );
+end;
+
+var
+ Global_HelpProc: procedure( Context: Integer; CtxCtl: PControl ) = CallWinHelp;
+
+function WndProcHelp( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var HI: PHelpInfo;
+ Ctx: Integer;
+ Ctl: PControl;
+begin
+ Result := FALSE;
+ if Msg.message = WM_HELP then
+ begin
+ Ctx := 0;
+ Ctl := nil;
+ HI := Pointer( Msg.lParam );
+ if HI.iContextType = HELPINFO_WINDOW then
+ begin
+ {$IFDEF USE_PROP}
+ Ctl := Pointer( GetProp( HI.hItemHandle, ID_SELF ) );
+ {$ELSE}
+ Ctl := Pointer( GetWindowLong( HI.hItemHandle, GWL_USERDATA ) );
+ {$ENDIF}
+ while Ctl <> nil do
+ begin
+ Ctx := Ctl.HelpContext;
+ if Ctx <> 0 then break;
+ Ctl := Ctl.Parent;
+ end;
+ end
+ else
+ Ctx := GetMenuContextHelpID( HI.hItemHandle );
+ Applet.CallHelp( Ctx, Ctl );
+ Rslt := 1;
+ Result := TRUE;
+ end
+ {$IFDEF AUTO_CONTEXT_HELP}
+ else
+ if (Msg.message = WM_CONTEXTMENU) then
+ begin
+ {$IFDEF USE_PROP}
+ Ctl := Pointer( GetProp( Msg.wParam, ID_SELF ) );
+ {$ELSE}
+ Ctl := Pointer( GetWindowLong( Msg.wParam, GWL_USERDATA ) );
+ {$ENDIF}
+ if (Ctl <> nil) and (Ctl.HelpContext <> 0) then
+ begin
+ Applet.CallHelp( Ctl.HelpContext, Ctl );
+ Rslt := 1;
+ Result := TRUE;
+ end;
+ end
+ {$ENDIF};
+end;
+
+procedure TControl.SetHelpContext(Value: Integer);
+var F: PControl;
+begin
+ F := ParentForm;
+ if F = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ F.AttachProc( WndProcHelp );
+ SetWindowContextHelpId( GetWindowHandle, Value );
+end;
+
+function TControl.AssignHelpContext(Context: Integer): PControl;
+begin
+ SetHelpContext( Context );
+ Result := @ Self;
+end;
+
+procedure AssignHtmlHelp( const HtmlHelpPath: KOLString );
+var Lbytes: Integer;
+begin
+ {$IFDEF KOL_ASSERTIONS}
+ Assert( (HtmlHelpPath <> '') and (Applet <> nil), 'Error parameters' );
+ {$ENDIF KOL_ASSERTIONS}
+ if HelpFilePath <> '' then
+ FreeMem( HelpFilePath );
+ Lbytes := (Length( HtmlHelpPath ) + 1) * Sizeof( KOLChar );
+ GetMem( HelpFilePath, Lbytes );
+ Move( HtmlHelpPath[ 1 ], HelpFilePath^, Lbytes );
+ Global_HelpProc := CallHtmlHelp;
+ Applet.AttachProc( WndProcHelp );
+end;
+
+procedure TControl.CallHelp(Context: Integer; CtxCtl: PControl {; CtlID: Integer} );
+begin
+ Global_HelpProc( Context, CtxCtl {, CtlID} );
+end;
+
+function TControl.GetHelpPath: KOLString;
+begin
+ Result := KOLString(HelpFilePath);
+ if Result = '' then
+ begin
+ Result := ParamStr( 0 );
+ Result := ReplaceFileExt( Result, '.hlp' );
+ end;
+end;
+
+procedure TControl.SetHelpPath(const Value: KOLString);
+var Lbytes: Integer;
+begin
+ {$IFDEF KOL_ASSERTIONS}
+ Assert( Value <> '', 'Error parameter' );
+ {$ENDIF KOL_ASSERTIONS}
+ if HelpFilePath <> '' then
+ FreeMem( HelpFilePath );
+ Lbytes := (Length( Value ) + 1)*Sizeof( KOLChar );
+ GetMem( HelpFilePath, Lbytes );
+ Move( Value[ 1 ], HelpFilePath^, Lbytes );
+end;
+{$ENDIF WIN_GDI}
+
+{$IFDEF ASM_VERSION}{$ELSE}
+procedure TControl.DoAutoSize;
+begin
+ {$IFDEF NIL_EVENTS}
+ if Assigned( PP.fAutoSize ) then
+ {$ENDIF}
+ PP.fAutoSize( @Self );
+end;
+{$ENDIF}
+
+{$IFDEF GDI}
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetCaption: KOLString;
+var Sz: Integer;
+begin
+ if {$IFDEF USE_FLAGS} not(G1_IgnoreWndCaption in fFlagsG1)
+ {$ELSE} not fIgnoreWndCaption {$ENDIF}
+ and (FHandle <> 0) then
+ begin
+ Sz := GetWindowTextLength( FHandle );
+ SetLength( fCaption, Sz );
+ if Sz > 0 then
+ begin
+ {$IFNDEF UNICODE_CTRLS}
+ GetWindowText( FHandle, @ fCaption[ 1 ], Sz + 1 );
+ {$ELSE}
+ GetWindowTextW( FHandle, @ fCaption[ 1 ], Sz + 1 );
+ {$ENDIF}
+ end;
+ end;
+ Result := FCaption;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+FUNCTION TControl.GetCaption: KOLString;
+BEGIN
+ if {$IFDEF USE_FLAGS} not (G1_IgnoreWndCaption in fFlagsG1)
+ {$ELSE} fIgnoreWndCaption {$ENDIF} then
+ FCaption := fGetCaption(@Self);
+ Result := FCaption;
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetCaption( const Value: KOLString );
+begin
+ fCaption := Value;
+ if fHandle <> 0 then
+ SendMessage( fHandle, WM_SETTEXT,
+ 0, Integer( PKOLChar( Value ) ) );
+ if {$IFDEF USE_FLAGS} (G1_IsStaticControl in fFlagsG1)
+ {$ELSE} fIsStaticControl <> 1 {$ENDIF} then
+ Invalidate;
+ DoAutoSize;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+PROCEDURE TControl.SetCaption( CONST Value: KOLString );
+BEGIN
+ fCaption := Value;
+ if Assigned( fSetCaption ) THEN
+ fSetCaption( @Self, Value );
+ DoAutoSize;
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+{$IFDEF WIN_GDI}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+function TControl.GetVisible: Boolean;
+begin
+ //UpdateWndStyles;
+ {$IFDEF USE_FLAGS}
+ {if (fHandle <> 0) then
+ Result := //IsWindowVisible( fHandle ) -- incorrectly is false in OnShow !
+ GetWindowLong( fHandle, GWL_STYLE ) and WS_VISIBLE <> 0
+ else}
+ Result := F3_Visible in fStyle.f3_Style;
+ {$ELSE}
+ {if (fHandle <> 0) then
+ fVisible := IsWindowVisible( fHandle )
+ else}
+ fVisible := (FStyle.Value and WS_VISIBLE) <> 0;
+ Result := fVisible;
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} // Pascal
+function TControl.Get_Visible: Boolean;
+begin
+ {$IFDEF USE_FLAGS}
+ Result := GetVisible;
+ {$ELSE}
+ if (fHandle <> 0) and not fIsControl then
+ fVisible := IsWindowVisible( fHandle );
+ Result := fVisible;
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} // Pascal
+procedure TControl.Set_Visible( Value: Boolean );
+{$IFDEF OLD_ALIGN}
+var CmdShow: DWORD;
+{$ENDIF}
+begin
+ {$IFDEF OLD_ALIGN}
+ //if Get_Visible <> Value then // commented to allow to set up controls visibility
+ begin // on invisible form (Vladimir Piven)
+ if Value then
+ begin
+ {$IFDEF USE_FLAGS} include( fStyle.f3_Style, F3_Visible );
+ {$ELSE} fStyle.Value := fStyle.Value or WS_VISIBLE; {$ENDIF}
+ CmdShow := SW_SHOW;
+ end else
+ begin
+ {$IFDEF USE_FLAGS} exclude( fStyle.f3_Style, F3_Visible );
+ {$ELSE} fStyle.Value := fStyle.Value and not WS_VISIBLE; {$ENDIF}
+ CmdShow := SW_HIDE;
+ end;
+ {$IFDEF USE_FLAGS}{$ELSE}
+ fVisible := Value;
+ {$ENDIF}
+ if fHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ ShowWindow( fHandle, CmdShow );
+ Global_Align( fParent );
+ if Value then
+ Global_Align( @Self );
+ end;
+ {$IFDEF CREATE_HIDDEN}
+ if not Value and (fHandle <> 0) then
+ {$IFDEF USE_FLAGS} exclude( fFlagsG4, G4_CreateHidden );
+ {$ELSE} fCreateHidden := FALSE; {$ENDIF} // { +++ }
+ {$ENDIF CREATE_HIDDEN}
+{$ELSE NEW_ALIGN}
+ fStyle.Value := fStyle.Value and not WS_VISIBLE;
+ if Value then
+ fStyle.Value := fStyle.Value or WS_VISIBLE;
+ {$IFDEF USE_FLAGS}
+ {$ELSE}
+ fVisible := Value;
+ {$ENDIF}
+ if fHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if Value then
+ begin
+ Global_Align( @Self );
+ ShowWindow( fHandle, SW_SHOW );
+ end else
+ begin
+ {$IFDEF CREATE_HIDDEN}
+ {$IFDEF USE_FLAGS} exclude( fFlagsG4, G4_CreateHidden );
+ {$ELSE} fCreateHidden := FALSE; {$ENDIF} // { +++ }
+ {$ENDIF CREATE_HIDDEN}
+ ShowWindow( fHandle, SW_HIDE );
+ Global_Align( @Self );
+ end;
+{$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure TControl.SetVisible( Value: Boolean );
+begin
+ {$IFDEF USE_FLAGS} include( fFlagsG4, G4_CreateVisible );
+ {$ELSE} fCreateVisible := TRUE; {$ENDIF}
+ Set_Visible( Value );
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF WIN_GDI}
+
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetBoundsRect: TRect;
+var W: HWnd;
+ P: TPoint;
+begin
+ Result := fBoundsRect;
+ if fHandle <> 0 then
+ begin
+ GetWindowRect( fHandle, Result );
+ if {$IFDEF USE_FLAGS} ([G3_IsControl, G3_IsMDIChild] * fFlagsG3 <> [])
+ {$ELSE} fIsControl or fIsMDIChild {$ENDIF}
+ then
+ begin
+ W := ParentWindow;
+ if W <> 0 then
+ begin
+ P.x := 0; P.y := 0;
+ Windows.ClientToScreen( W, P );
+ OffsetRect( Result, -P.x, -P.y );
+ end;
+ end;
+ {$IFDEF TEST_BOUNDSRECT}
+ if not CompareMem( @ fBoundsRect, @ Result, Sizeof( TRect ) ) then
+ {$ENDIF}
+ fBoundsRect := Result;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+
+{$IFDEF _X_}
+{$IFDEF GTK}
+FUNCTION TControl.GetBoundsRect: TRect;
+VAR R: TRect;
+ window: PGtkWindow;
+ requisition: TGtkRequisition;
+BEGIN
+ //if fHandle <> nil then
+ BEGIN
+ IF fIsControl THEN
+ BEGIN
+ R.Left := fBoundsRect.Left;
+ R.Top := fBoundsRect.Top;
+ gtk_widget_get_size_request( fEventboxHandle, @ R.Right, @ R.Bottom );
+ gtk_widget_size_request( fHandle, @ requisition );
+ IF R.Right < 0 THEN R.Right := requisition.width;
+ IF R.Bottom < 0 THEN R.Bottom := requisition.height;
+ END ELSE
+ BEGIN
+ window := GTK_WINDOW( fHandle );
+ gtk_window_get_position(window, @ R.Left, @ R.Top);
+ gtk_window_get_size(window, @ R.Right, @ R.Bottom);
+ END;
+ inc( R.Right, R.Left );
+ inc( R.Bottom, R.Top );
+ fBoundsRect := R;
+ END;
+ Result := fBoundsRect;
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetBoundsRect( const Value: TRect );
+var Rect: TRect;
+begin
+ Rect := GetBoundsRect;
+ if RectsEqual( Value, Rect ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$IFDEF USE_FLAGS}
+ if (Value.Left <> fBoundsRect.Left) or (Value.Top <> fBoundsRect.Top) then
+ include( fFlagsG2, G2_ChangedPos );
+ {$ELSE}
+ if Value.Left <> fBoundsRect.Left then fChangedPosSz := fChangedPosSz or 1;
+ if Value.Top <> fBoundsRect.Top then fChangedPosSz := fChangedPosSz or 2;
+ {$ENDIF}
+ {$IFDEF USE_GRAPHCTLS}
+ if {$IFDEF USE_FLAGS} (G6_GraphicCtl in fFlagsG6)
+ {$ELSE} not fWindowed {$ENDIF} then
+ Invalidate;
+ {$ENDIF}
+
+ fBoundsRect := Value;
+ Rect := Value;
+
+ if fHandle <> 0 then
+ begin
+ SetWindowPos( fHandle, 0, Rect.Left, Rect.Top, Rect.Right - Rect.Left,
+ Rect.Bottom - Rect.Top, SWP_NOZORDER or SWP_NOACTIVATE );
+ end;
+ if {$IFDEF USE_FLAGS} (G1_SizeRedraw in fFlagsG1)
+ {$ELSE} fSizeRedraw {$ENDIF} then
+ Invalidate;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+
+{$IFDEF _X_}
+{$IFDEF GTK}
+PROCEDURE TControl.SetBoundsRect( const Value: TRect );
+VAR Rect: TRect;
+ window: PGtkWindow;
+BEGIN
+ Rect := GetBoundsRect;
+ if RectsEqual( Value, Rect ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$IFDEF USE_FLAGS}
+ if (Value.Left <> fBoundsRect.Left) or (Value.Top <> fBoundsRect.Top) then
+ include( fFlagsG2, G2_ChangedPos );
+ {$ELSE}
+ if Value.Left <> fBoundsRect.Left then fChangedPosSz := fChangedPosSz or 1;
+ if Value.Top <> fBoundsRect.Top then fChangedPosSz := fChangedPosSz or 2;
+ {$ENDIF}
+ fBoundsRect := Value;
+ Rect := Value;
+
+ IF fIsControl then
+ BEGIN
+ //gtk_widget_set_uposition( fHandle, Rect.Left, Rect.Top );
+ IF fParent <> nil then
+ fParent.fChildSetPos( fParent, @ Self, Rect.Left, Rect.Top );
+ IF (Rect.Right > Rect.Left) and (Rect.Bottom > Rect.Top) then
+ gtk_widget_set_size_request( fEventboxHandle,
+ Rect.Right - Rect.Left, Rect.Bottom - Rect.Top );
+ END ELSE
+ BEGIN
+ window := GTK_WINDOW( fHandle );
+ gtk_window_move( window, Rect.Left, Rect.Top );
+ gtk_window_resize( window, Rect.Right - Rect.Left, Rect.Bottom - Rect.Top );
+ END;
+ //if fSizeRedraw then
+ // Invalidate;
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF WIN_GDI}
+const
+ WindowStateShowCommands: array[TWindowState] of Byte =
+ (SW_SHOWNOACTIVATE, SW_SHOWMINNOACTIVE, SW_SHOWMAXIMIZED);
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetWindowState( Value: TWindowState );
+begin
+ if WindowState <> Value then
+ begin
+ DF.fWindowState := Value;
+ if fHandle <> 0 then
+ ShowWindow(fHandle, WindowStateShowCommands[Value]);
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.Show;
+begin
+ CreateWindow;
+ SetVisible( True );
+ SetForegroundWindow( Handle );
+ DoSetFocus;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TControl.Hide;
+begin
+ SetVisible( False );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.Client2Screen( const P: TPoint ): TPoint;
+begin
+ Result := P;
+ if fHandle <> 0 then
+ Windows.ClientToScreen( fHandle, Result );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.Screen2Client( const P: TPoint ): TPoint;
+begin
+ Result := P;
+ if Handle <> 0 then
+ Windows.ScreenToClient( Handle, Result );
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF WIN_GDI}
+
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.ClientRect: TRect;
+const BorderParams: array[ 0..5 ] of DWORD =
+ ( SM_CXBORDER, SM_CXFRAME, SM_CXSIZEFRAME, SM_CYBORDER, SM_CYFRAME, SM_CYSIZEFRAME );
+begin
+ Result := fBoundsRect;
+ GetWindowHandle;
+ if (fHandle <> 0) then
+ GetClientRect( fHandle, Result );
+ Inc( Result.Top, fClientTop );
+ Dec( Result.Bottom, fClientBottom );
+ Inc( Result.Left, fClientLeft );
+ Dec( Result.Right, fClientRight );
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+FUNCTION TControl.ClientRect: TRect; //todo: implement exact, now for PaintBox only
+BEGIN
+ Result := fBoundsRect;
+ OffsetRect( Result, -Result.Left, -Result.Top );
+ Inc( Result.Top, fClientTop );
+ Dec( Result.Bottom, fClientBottom );
+ Inc( Result.Left, fClientLeft );
+ Dec( Result.Right, fClientRight );
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+procedure TControl.Invalidate;
+begin
+ {$IFDEF USE_GRAPHCTLS}
+ PP.fDoInvalidate( @Self );
+ {$ELSE}
+ if fHandle <> 0 then
+ InvalidateRect( fHandle, nil, TRUE );
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+PROCEDURE TControl.Invalidate;
+BEGIN
+ gtk_widget_queue_draw_area( fHandle, 0, 0, Width, Height );
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF WIN_GDI}
+{$IFDEF USE_GRAPHCTLS}
+procedure InvalidateNonWindowed( Sender: PObj );
+var R: TRect;
+begin
+ R := PControl( Sender ).BoundsRect;
+ if PControl( Sender ).fParent.fHandle <> 0 then
+ InvalidateRect( PControl( Sender ).fParent.fHandle, @ R, TRUE );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+procedure InvalidateWindowed( Sender: PObj );
+begin
+ if PControl( Sender ).fHandle <> 0 then
+ InvalidateRect( PControl( Sender ).fHandle, nil, TRUE );
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF USE_GRAPHCTLS}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetIcon: HIcon;
+begin
+ Result := DF.fIcon;
+ if Result = THandle( -1 ) then
+ begin
+ Result := 0; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ if Result = 0 then
+ if (Applet <> nil) and (@Self <> Applet) then
+ begin
+ Result := Applet.Icon;
+ if Result <> 0 then
+ Result := CopyImage( Result, IMAGE_ICON, 0, 0, 0 );
+ end
+ else
+ begin
+ {$IFDEF NUMERIC_APPICON} {$DEFINE CUSTOM_APPICON} {$ENDIF}
+ Result := LoadIcon( hInstance,
+ {$IFDEF CUSTOM_APPICON} {$IFDEF NUMERIC_APPICON} PKOLChar( {$ENDIF} // avoid A/W casting
+ {$I CustomAppIconRsrcName_PAS.inc} // create such file with 'your icon rsrc name' or yourIconID
+ {$IFDEF NUMERIC_APPICON} ) {$ENDIF}
+ {$ELSE} 'MAINICON' {$ENDIF} );
+ end;
+ DF.fIcon := Result;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TControl.IconLoad(Inst: Integer; ResName: PKOLChar);
+begin
+ Icon := LoadIcon( Inst, ResName );
+ {$IFDEF USE_FLAGS} include( fFlagsG1, G1_IconShared );
+ {$ELSE} fIconShared := TRUE; {$ENDIF}
+end;
+
+procedure TControl.IconLoadCursor(Inst: Integer; ResName: PKOLChar);
+begin
+ Icon := LoadCursor( Inst, ResName );
+ {$IFDEF USE_FLAGS} include( fFlagsG1, G1_IconShared );
+ {$ELSE} fIconShared := TRUE; {$ENDIF}
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.CallDefWndProc(var Msg: TMsg): Integer;
+begin
+ {$IFDEF INPACKAGE}
+ Result := 0;
+ Log( '->TControl.CallDefWndProc FHandle = ' + Int2Str( FHandle ) +
+ ', Msg.hwd = ' + Int2Str( Msg.hwnd ) );
+ TRY
+ {$ENDIF INPACKAGE}
+ if FDefWndProc <> nil then
+ begin
+ {$IFDEF INPACKAGE}
+ Log( '//// CallWindowProc, FDefWndProc = ' + Int2Hex( DWORD( FDefWndProc ), 6 ) );
+ TRY
+ TRY
+ {$ENDIF INPACKAGE}
+ Result := CallWindowProc(
+ FDefWndProc, FHandle, Msg.message, Msg.wParam, Msg.lParam );
+ {$IFDEF INPACKAGE}
+ EXCEPT on E: Exception do
+ Log( '*** Exception in CallWindowProc, msg = ' + E.Message );
+ END;
+ EXCEPT
+ Log( '*** Exception handled' );
+ END;
+ {$ENDIF INPACKAGE}
+ end
+ else
+ begin
+ {$IFDEF INPACKAGE}
+ Log( '//// DefWindowProc' );
+ {$ENDIF INPACKAGE}
+ Result := DefWindowProc( Msg.hwnd, Msg.message, Msg.wParam, Msg.lParam );
+ end;
+ {$IFDEF INPACKAGE}
+ LogOK;
+ FINALLY
+ Log( '<-TControl.CallDefWndProc' );
+ END;
+ {$ENDIF INPACKAGE}
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetWindowState: TWindowState;
+begin
+ Result := DF.fWindowState;
+ if Handle <> 0 then
+ begin
+ if IsIconic( Handle ) then
+ Result := wsMinimized
+ else
+ if IsZoomed( Handle ) then
+ Result := wsMaximized
+ else
+ Result := wsNormal;
+ //DF.fWindowState := Result;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.DoSetFocus: Boolean;
+begin
+ Result := False;
+ //if Enabled and (
+ // {$IFDEF USE_FLAGS}{$ELSE} fTabstop or {$ENDIF}
+ // (F2_Tabstop in fStyle.f2_Style)) then
+ if Enabled then
+ begin
+ Inc( fClickDisabled );
+ SetFocus( fHandle );
+ Dec( fClickDisabled );
+ Result := True;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+function TControl.HandleAllocated: Boolean;
+begin
+ Result := FHandle <> 0;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetEnabled: Boolean;
+begin
+ if FHandle = 0 then
+ Result := (Style and WS_DISABLED) = 0
+ else Result := IsWindowEnabled( FHandle );
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF WIN_GDI}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.IsMainWindow: Boolean;
+begin
+ if Applet = nil then
+ Result := not IsControl
+ else if not AppButtonUsed then
+ Result := @ Self = Applet
+ else
+ Result := Applet.Children[ 0 ] = @ Self;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF WIN_GDI}
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function TControl.get_ClassName: KOLString;
+begin
+ Result := fControlClassName;
+ if {$IFDEF USE_FLAGS} not(G6_CtlClassNameChg in fFlagsG6)
+ {$ELSE} not fCtlClsNameChg {$ENDIF} then
+ Result := KOLString('obj_') + Result;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TControl.set_ClassName(const Value: KOLString);
+begin
+ if {$IFDEF USE_FLAGS} G6_CtlClassNameChg in fFlagsG6
+ {$ELSE} fCtlClsNameChg {$ENDIF} then
+ FreeMem( fControlClassName );
+ GetMem( fControlClassName, (Length( Value ) + 1) * Sizeof( KOLChar ) );
+ {$IFDEF UNICODE_CTRLS} WStrCopy {$ELSE} StrCopy {$ENDIF}
+ ( fControlClassName, @ Value[ 1 ] );
+ {$IFDEF USE_FLAGS} include( fFlagsG6, G6_CtlClassNameChg );
+ {$ELSE} fCtlClsNameChg := TRUE; {$ENDIF}
+end;
+
+function WndProcQueryEndSession( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var Accept: Boolean;
+begin
+ Result := FALSE;
+ if Msg.message = WM_QUERYENDSESSION then
+ begin
+ {$IFDEF DEBUG_ENDSESSION}
+ LogFileOutput( GetStartDir + 'end_session.txt', '!' );
+ {$ENDIF}
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Sender.EV.fOnQueryEndSession ) then
+ {$ENDIF}
+ begin
+ Accept := TRUE;
+ Sender.DF.fCloseQueryReason := qShutdown;
+ if LongBool(Msg.lParam and {ENDSESSION_LOGOFF} DWORD($80000000)) then
+ Sender.DF.fCloseQueryReason := qLogoff;
+ Sender.EV.fOnQueryEndSession( Sender, Accept );
+ Sender.DF.fCloseQueryReason := qClose;
+ Rslt := Integer( Accept );
+ // Äîáàâèòü. Íóæíî äëÿ òîãî, ÷òîáû îòìåíèëîñü çàâåðøåíèå ñåàíñà,
+ // åñëè Accept óñòàíîâëåí â False è ñåàíñ çàâåðøèëñÿ ïðè Accept = True
+ // Add (YS). To cancel ending session if Accept=FALSE but allow ending
+ // session if Accept=TRUE.
+ Result := True; // {YS}: no further processing
+ end;
+ end;
+end;
+
+procedure TControl.SetOnQueryEndSession(const Value: TOnEventAccept);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnQueryEndSession := Value;
+ AttachProc( WndProcQueryEndSession );
+end;
+
+function WndProcMinMaxRestore( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+begin
+ Result := FALSE;
+ if Msg.message = WM_SYSCOMMAND then
+ begin
+ case Msg.wParam and not 15 of
+ SC_MINIMIZE: {$IFDEF NIL_EVENTS}
+ if Assigned( Sender.EV.fOnMinimize ) then
+ {$ENDIF}
+ Sender.EV.fOnMinimize( Sender );
+ SC_MAXIMIZE: {$IFDEF NIL_EVENTS}
+ if Assigned( Sender.EV.fOnMaximize ) then
+ {$ENDIF}
+ Sender.EV.fOnMaximize( Sender );
+ SC_RESTORE: {$IFDEF NIL_EVENTS}
+ if Assigned( Sender.EV.fOnRestore ) then
+ {$ENDIF}
+ Sender.EV.fOnRestore( Sender );
+ end;
+ end;
+end;
+
+procedure TControl.SetOnMinMaxRestore(const Index: Integer;
+ const Value: TOnEvent);
+type POnEvent = ^TOnEvent;
+{$IFDEF F_P}
+var Ptr1: Pointer;
+{$ELSE DELPHI}
+var Evt: POnEvent;
+{$ENDIF F_P/DELPHI}
+begin
+ {$IFDEF F_P}
+ Ptr1 := Self;
+ asm
+ MOV EAX, [Ptr1]
+ LEA EAX, [EAX].TControl.fOnMinimize
+ ADD EAX, [Index]
+ MOV EDX, [Value]
+ MOV [EAX], EDX
+ MOV EDX, [Value+4]
+ MOV [EAX+4], EDX
+ end [ 'EAX', 'EDX' ];
+ {$ELSE DELPHI}
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents; {$ENDIF}
+ Evt := Pointer( Integer( @ TMethod( EV.fOnMinimize ).Code ) + Index );
+ Evt^ := Value;
+ {$ENDIF}
+ AttachProc( WndProcMinMaxRestore );
+end;
+
+procedure TControl.SetOnMinimize(const Value: TOnEvent);
+begin
+ SetOnMinMaxRestore( 0, Value );
+end;
+
+procedure TControl.SetOnMaximize(const Value: TOnEvent);
+begin
+ SetOnMinMaxRestore( 8, Value );
+end;
+
+procedure TControl.SetOnRestore(const Value: TOnEvent);
+begin
+ SetOnMinMaxRestore( 16, Value );
+end;
+
+function TControl.GetOnMinMaxRestore(const Index: Integer): TOnEvent;
+begin
+ CASE Index OF
+ 0: Result := EV.fOnMinimize;
+ 8: Result := EV.fOnMaximize;
+ 16: Result := EV.fOnRestore;
+ END;
+end;
+
+{$IFDEF INPACKAGE}
+ {$IFDEF ASM_LOCAL}
+ {$UNDEF ASM_LOCAL}
+ {$ENDIF}
+{$ELSE}
+ {$IFDEF ASM_VERSION}
+ {$DEFINE ASM_LOCAL}
+ {$ENDIF}
+{$ENDIF}
+
+{$ENDIF WIN_GDI}
+
+{$IFDEF GDI}
+{$IFDEF ASM_LOCAL}
+{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetParent( Value: PControl );
+begin
+ if Value = fParent then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if fParent <> nil then
+ begin
+ {$IFDEF USE_GRAPHCTLS}
+ Invalidate; // necessary for graphic controls
+ {$ENDIF}
+ {$IFDEF DEBUG_MCK}
+ if ( fParent.fChildren <> nil ) then
+ begin
+ mck_Log( 'remove from old parent children 1st' );
+ fParent.fChildren.Remove( @Self );
+ mck_Log( 'removed ok' );
+ end;
+ {$ELSE not DEBUG_MCK}
+ fParent.fChildren.Remove( @Self );
+ {$IFDEF NOT_USE_AUTOFREE4CONTROLS}
+ {$ELSE}
+ fParent.RemoveFromAutoFree( @Self );
+ {$ENDIF}
+
+ {$IFDEF NIL_EVENTS}
+ if Assigned( fParent.PP.fNotifyChild ) then
+ {$ENDIF}
+ fParent.PP.fNotifyChild( fParent, nil );
+ {$ENDIF not DEBUG_MCK}
+ end;
+ fParent := Value;
+ if fParent <> nil then
+ begin
+ fParent.fChildren.Add( @Self );
+ {$IFDEF USE_AUTOFREE4CHILDREN}
+ fParent.Add2AutoFree( @ Self );
+ {$ENDIF}
+ {$IFNDEF INPACKAGE} //-----------------------------------------------------
+ if FHandle <> 0 then
+ Windows.SetParent( FHandle, Value.GetWindowHandle );
+ {$ENDIF not INPACKAGE} //--------------------------------------------------
+ {$IFDEF NIL_EVENTS}
+ if Assigned( fParent.PP.fNotifyChild ) then
+ {$ENDIF}
+ fParent.PP.fNotifyChild( fParent, @ Self );
+ {$IFDEF NIL_EVENTS}
+ if Assigned( PP.fNotifyChild ) then
+ {$ENDIF}
+ PP.fNotifyChild( fParent, @ Self );
+ {$IFDEF USE_GRAPHCTLS}
+ Invalidate; // necessary for graphic controls
+ {$ENDIF}
+ end;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+PROCEDURE TControl.SetParent( Value: PControl );
+BEGIN
+ IF Value = fParent THEN Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ IF fParent <> nil THEN
+ begin
+ fParent.fChildren.Remove( @Self );
+
+ {$IFDEF NOT_USE_AUTOFREE4CONTROLS}
+ {$ELSE}
+ fParent.RemoveFromAutoFree( @Self );
+ {$ENDIF}
+ END;
+ fParent := Value;
+ IF fParent <> nil THEN
+ BEGIN
+ fParent.fChildren.Add( @Self );
+ {$IFDEF USE_AUTOFREE4CHILDREN}
+ fParent.Add2AutoFree( @ Self );
+ {$ENDIF}
+ END;
+ fParent.fGetClientArea( fParent );
+ fParent.fChildPut( fParent, @ Self, fBoundsRect.Left, fBoundsRect.Top );
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+function TControl.ChildIndex(Child: PControl): Integer;
+begin
+ Result := fChildren.IndexOf( Child );
+end;
+
+procedure TControl.MoveChild(Child: PControl; NewIdx: Integer);
+var I: Integer;
+begin
+ I := ChildIndex( Child );
+ {$IFDEF KOL_ASSERTIONS}
+ Assert( I>=0, 'TControl.MoveChild: index out of bounds' );
+ {$ENDIF KOL_ASSERTIONS}
+ fChildren.MoveItem( I, NewIdx );
+end;
+
+{$IFDEF WIN_GDI}
+procedure TControl.EnableChildren(Enable, Recursive: Boolean);
+var I: Integer;
+ C: PControl;
+begin
+ for I := 0 to ChildCount-1 do
+ begin
+ C := Children[ I ];
+ C.Enabled := Enable;
+ if Recursive then
+ C.EnableChildren( Enable, TRUE );
+ end;
+end;
+
+{$ENDIF WIN_GDI}
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+constructor TControl.CreateParented(AParent: PControl);
+begin
+ InitParented( AParent ); // because InitParented is virtual, but CreateParented
+end; // can not be virtual (as an _object_ - not a class - constructor)
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+CONSTRUCTOR TControl.CreateParented(AParent: PControl; widget: PGtkWidget;
+ need_eventbox: Boolean);
+BEGIN
+ InitParented( AParent, widget, need_eventbox );
+ // because InitParented is virtual, but CreateParented
+END; // can not be virtual (as an _object_ - not a class - constructor)
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+constructor TControl.CreateOrthaned( AParentWnd: HWnd );
+begin
+ InitOrthaned( AParentWnd );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetLeft: Integer;
+begin
+ Result := BoundsRect.Left;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetLeft( Value: Integer );
+var R: TRect;
+begin
+ R := BoundsRect;
+ R.Left := Value;
+ R.Right := Value + Width;
+ SetBoundsRect( R );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetTop: Integer;
+begin
+ Result := BoundsRect.Top;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetTop( Value: Integer );
+var R: TRect;
+begin
+ R := BoundsRect;
+ R.Top := Value;
+ R.Bottom := Value + Height;
+ SetBoundsRect( R );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetWidth: Integer;
+begin
+ with BoundsRect do
+ Result := Right - Left;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetWidth( Value: Integer );
+var R: TRect;
+begin
+ R := BoundsRect;
+ with R do
+ Right := Left + Value;
+ SetBoundsRect( R );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetHeight: Integer;
+begin
+ with BoundsRect do
+ Result := Bottom - Top;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetHeight( Value: Integer );
+var R: TRect;
+begin
+ R := BoundsRect;
+ with R do
+ Bottom := Top + Value;
+ SetBoundsRect( R );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetPosition: TPoint;
+begin
+ Result.x := BoundsRect.Left;
+ Result.y := BoundsRect.Top;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.Set_Position( Value: TPoint );
+var R: TRect;
+begin
+ R.Top := Value.y;
+ R.Left := Value.x;
+ R.Right := R.Left + Width;
+ R.Bottom := R.Top + Height;
+ BoundsRect := R;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF WIN_GDI}
+function WndProcConstraints( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var MMI: PMinMaxInfo;
+begin
+ Result := FALSE;
+ if Msg.message = WM_GETMINMAXINFO then
+ begin
+ Rslt := Sender.CallDefWndProc( Msg );
+ MMI := Pointer( Msg.lParam );
+ if Sender.FMaxWidth > 0 then
+ begin
+ MMI.ptMaxSize.x := Sender.FMaxWidth;
+ MMI.ptMaxTrackSize.x := Sender.FMaxWidth;
+ end;
+ if Sender.FMaxHeight > 0 then
+ begin
+ MMI.ptMaxSize.y := Sender.FMaxHeight;
+ MMI.ptMaxTrackSize.y := Sender.FMaxHeight;
+ end;
+ MMI.ptMinTrackSize := MakePoint( Sender.FMinWidth, Sender.FMinHeight );
+ Rslt := 0;
+ Result := TRUE;
+ end;
+end;
+
+{$IFDEF USE_MHTOOLTIP}
+{$DEFINE implementation_part}
+{$I KOLMHToolTip_implem.inc}
+{$UNDEF implementation_part}
+{$ENDIF}
+
+procedure TControl.SetConstraint(const Index: Integer; Value: SmallInt);
+begin
+ AttachProc( WndProcConstraints );
+ case Index of
+ 0: FMinWidth := Value;
+ 1: FMinHeight := Value;
+ 2: FMaxWidth := Value;
+ 3: FMaxHeight := Value;
+ end;
+end;
+
+function TControl.GetConstraint(const Index: Integer): Integer;
+begin
+ CASE Index OF
+ 0: Result := FMinWidth;
+ 1: Result := FMinHeight;
+ 2: Result := FMaxWidth;
+ else Result := FMaxHeight;
+ END;
+end;
+
+function TControl.ControlRect: TRect;
+var C: PControl;
+ R: TRect;
+begin
+ Result := BoundsRect;
+ C := Parent;
+ if C <> nil then
+ begin
+ if {$IFDEF USE_FLAGS} not(G3_IsControl in C.fFlagsG3)
+ {$ELSE} not C.fIsControl {$ENDIF} then Exit; {>>>>>>>>>>>>>>>>>>>>>>>}
+
+ R := C.ControlRect;
+ OffsetRect( Result, R.Left, R.Top );
+
+ if C.fChildren <> nil then
+ if C.FChildren.IndexOf( @Self ) >= 0 then
+ begin
+ R := C.ClientRect;
+ Dec( R.Top, C.fClientTop );
+ Dec( R.Left, C.fClientLeft );
+ OffsetRect( Result, R.Left, R.Top );
+ end;
+ end;
+end;
+
+function TControl.ControlAtPos( X, Y: Integer; IgnoreDisabled: Boolean ): PControl;
+var I: Integer;
+ C: PControl;
+ CR, VR: TRect;
+begin
+ Result := nil;
+ CR := ControlRect; // îòíîñèòåëüíûå êîîðäèíàòû â ñèñòåìå ÐÎÄÈÒÅËÜÑÊÎÃÎ ÊÎÍÒÐÎËÀ
+ if {$IFDEF USE_FLAGS} not(G6_GraphicCtl in fFlagsG6)
+ {$ELSE} fWindowed {$ENDIF} then CR := MakeRect( 0, 0, 0, 0 );
+ X := X + CR.Left; // - R.Left;
+ Y := Y + CR.Top; // - R.Top;
+ for I := ChildCount - 1 downto 0 do
+ begin
+ C := Children[ I ]; //Members[ I ];
+ if C.Visible then
+ if (not IgnoreDisabled) or IgnoreDisabled and C.Enabled then
+ begin
+ VR := C.ControlRect;
+ if (X >= VR.Left) and (X < VR.Right) and
+ (Y >= VR.Top) and (Y < VR.Bottom) then
+ begin
+ Result := C; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ end;
+end;
+{$ENDIF WIN_GDI}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure DefaultPaintBackground( Sender: PControl; DC: HDC; Rect: PRect );
+{$IFDEF GDI} var B: HBrush; {$ENDIF GDI}
+begin
+ {$IFDEF GDI}
+ B := CreateSolidBrush( Color2Rgb( Sender.Color ) );
+ Windows.FillRect( DC, Rect^, B );
+ DeleteObject( B );
+ {$ENDIF GDI}
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF WIN_GDI}
+procedure TControl.PaintBackground( DC: HDC; Rect: PRect );
+begin
+ Global_OnPaintBkgnd( @Self, DC, Rect );
+end;
+{$ENDIF WIN_GDI}
+
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetCtlColor( Value: TColor );
+begin
+ {$IFNDEF INPACKAGE}
+ if GetWindowHandle <> 0 then
+ {$ELSE}
+ if fHandle <> 0 then
+ {$ENDIF}
+ if fCommandActions.aSetBkColor <> 0 then
+ Perform( fCommandActions.aSetBkColor, 0, Color2RGB( Value ) );
+ if fColor = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fColor := Value;
+ if fTmpBrush <> 0 then
+ begin
+ DeleteObject( fTmpBrush );
+ fTmpBrush := 0;
+ end;
+ if fBrush <> nil then
+ fBrush.Color := Value;
+ Invalidate;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+PROCEDURE TControl.SetCtlColor( Value: TColor );
+VAR gcolor: TGdkColor;
+ i: Integer;
+BEGIN
+ if fColor = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fColor := Value;
+ gcolor := Color2GdkColor( Value );
+ FOR i := 0 to 4 do
+ BEGIN
+ gtk_widget_modify_bg( fEventboxHandle, {GTK_STATE_NORMAL} i, @ gcolor );
+ gtk_widget_modify_base( fEventboxHandle, {GTK_STATE_NORMAL} i, @ gcolor );
+ END;
+ //if Assigned( _Self.fFont ) then
+ {begin
+ _Self.fHandle.style.font_desc :=
+ pango_font_description_copy( _Self.fFont.GetPangoFontDesc );
+ if oldfontdesc <> nil then
+ pango_font_description_free( oldfontdesc );
+ end;}
+
+ //Invalidate;
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF WIN_GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetParentWnd( NeedHandle: Boolean ): HWnd;
+var C: PControl;
+begin
+ Result := FParentWnd;
+ C := fParent; // WindowedParent;
+ if C <> nil then
+ begin
+ if NeedHandle then
+ C.GetWindowHandle;
+ Result := C.fHandle;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_TLIST}
+procedure TControl.CreateChildWindows;
+asm
+ PUSH ESI
+ MOV ESI, [EAX].TControl.fChildren
+ MOV ECX, [ESI].TList.fCount
+ MOV ESI, [ESI].TList.fItems
+ JECXZ @@exit
+
+@@loop: PUSH ECX
+ LODSD
+ CALL CallTControlCreateWindow
+ POP ECX
+ LOOP @@loop
+
+@@exit: POP ESI
+end;
+{$ELSE PAS_VERSION} //Pascal
+procedure TControl.CreateChildWindows;
+var I: Integer;
+ C: PControl;
+begin
+ {$IFDEF INPACKAGE}
+ Log( '->TControl.CreateChildWindows' );
+ TRY
+ {$ENDIF INPACKAGE}
+ for I := 0 to fChildren.Count - 1 do
+ begin
+ {$IFDEF INPACKAGE}
+ Log( Int2Str( I ) );
+ {$ENDIF INPACKAGE}
+ C := fChildren.Items[ I ];
+ C.CreateWindow; //virtual!!!
+ end;
+ {$IFDEF INPACKAGE}
+ LogOK;
+ FINALLY
+ Log( '<-TControl.CreateChildWindows' );
+ END;
+ {$ENDIF INPACKAGE}
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF WIN_GDI}
+function TControl.GetMembers(Idx: Integer): PControl;
+begin
+ Result := fChildren.Items[ Idx ];
+ // Important: .Items but not .fItems - when fChildren.Count=0, nil is returned
+end;
+{$IFDEF WIN_GDI}
+
+{$IFDEF ASM_TLIST}
+procedure TControl.DestroyChildren;
+asm
+ PUSH ESI
+
+ MOV EAX, [EAX].fChildren
+ PUSH EAX
+ MOV ECX, [EAX].TList.fCount
+ JECXZ @@clear
+ MOV ESI, [EAX].TList.fItems
+ LEA ESI, [ESI + ECX*4 - 4] // is order really important ?
+
+@@loop: STD //
+ LODSD
+ CLD //
+
+ PUSH ECX
+ CALL TObj.RefDec
+ POP ECX
+
+ LOOP @@loop
+
+@@clear:
+ POP EAX
+ CALL TList.Clear
+
+ POP ESI
+end;
+{$ELSE PAS_VERSION} //Pascal
+procedure TControl.DestroyChildren;
+var I: Integer;
+ W: PControl;
+begin
+ for I := fChildren.fCount - 1 downto 0 do
+ begin
+ W := fChildren.{$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}[ I ];
+ W.Free;
+ end;
+ fChildren.Clear;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.ProcessMessage: Boolean;
+var Msg: TMsg;
+ P: Windows.PMsg;
+begin
+ Result := False;
+ if PeekMessage( Msg, 0, 0, 0, PM_REMOVE ) then
+ begin
+ Result := Msg.message <> 0;
+ if (Msg.message = WM_QUIT) then
+ begin
+ AppletTerminated := True;
+ {$IFDEF PROVIDE_EXITCODE}
+ ExitCode := Msg.wParam;
+ {$ENDIF PROVIDE_EXITCODE}
+ end
+ else
+ begin
+ if not(
+ {$IFDEF NIL_EVENTS} Assigned( PP.fExMsgProc ) and {$ENDIF}
+ PP.fExMsgProc( @Self, Msg )) then
+ begin
+ P := Pointer( @Msg );
+ TranslateMessage( P^ );
+ DispatchMessage( Msg );
+ {$IFDEF PSEUDO_THREADS}
+ if Assigned( MainThread ) then
+ MainThread.NextThread;
+ {$ENDIF}
+ end;
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.ProcessMessages;
+begin
+ while ProcessMessage do ;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TControl.ProcessMessagesEx;
+begin
+ PostMessage( GetWindowHandle, CM_PROCESS, 0, 0 );
+ ProcessMessages;
+end;
+
+procedure TControl.ProcessPendingMessages;
+var Msg: TMsg;
+begin
+ if LOWORD( GetQueueStatus( QS_ALLINPUT ) ) <> 0 then
+ if PeekMessage( Msg, 0, 0, 0, PM_NOREMOVE {or PM_NOYIELD} )
+ or PeekMessage( Msg, HWnd(-1), 0, 0, PM_NOREMOVE {or PM_NOYIELD} )
+ then
+ Applet.ProcessMessages;
+end;
+
+procedure TControl.ProcessPaintMessages;
+var Msg: TMsg;
+begin
+ while PeekMessage( Msg, Handle, 15, 15, PM_NOREMOVE ) do
+ Applet.ProcessMessage;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function WndProcForm(Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+{$IFDEF ENDSESSION_HALT}
+var App: PControl;
+{$ENDIF}
+begin
+ Result := True;
+ case Msg.message of
+ {$IFDEF ENDSESSION_HALT}
+ WM_ENDSESSION:
+ begin
+ if Msg.wParam <> 0 then
+ begin
+ Self_.RefDec;
+ { Normally, WM_ENDSESSION is sent to a main form, not to Applet.
+ Since we do not plan further working after handling this message,
+ we decrease RefCount for the form (in was increased in EnumDynHandlers
+ to prevent object destroying while its message processing is not
+ finished). }
+ App := Applet;
+ //Rslt := 0; { We will not return any result at all. }
+ {$IFDEF DEBUG_ENDSESSION}
+ EndSession_Initiated := TRUE;
+ LogFileOutput( GetStartDir + 'es_debug.txt',
+ 'Self_=' + Int2Hex( DWORD( Self_ ), 8 ) +
+ ' Self_.Handle=' + Int2Str( Self_.FHandle ) );
+ {$ENDIF}
+ AppletTerminated := TRUE;
+ AppletRunning := FALSE;
+ Applet := nil;
+ App.Free; { We provide OnDestroy handlers to be called for any objects here }
+ Halt; { Stop further executing. }
+ end else Result := FALSE;
+ end;
+ {$ENDIF ENDSESSION_HALT}
+ WM_SETFOCUS:
+ begin
+ {$IFDEF NEW_MODAL}
+ if Self_.DF.fModalForm <> nil then
+ SetFocus( Self_.DF.fModalForm.fHandle )
+ else if ( Self_.DF.FCurrentControl <> nil ) and
+ {$IFDEF USE_FLAGS} not( (G3_IsForm in Self_.DF.fCurrentControl.fFlagsG3)
+ xor(G3_IsApplet in Self_.fFlagsG3) )
+ {$ELSE} not(Self_.DF.FCurrentControl.fIsForm xor Self_.fIsApplet)
+ {$ENDIF} then
+ {$ELSE not NEW_MODAL}
+ if Self_.DF.FCurrentControl <> nil then
+ {$ENDIF}
+ begin
+ if Self_.DF.FCurrentControl.CreateWindow then
+ SetFocus( Self_.DF.FCurrentControl.fHandle );
+ end
+ else
+ Result := False;
+ if assigned( Applet ) and (Applet <> Self_) then
+ Applet.DF.FCurrentControl := Self_;
+ end;
+ //WM_NCDESTROY:
+ // Self_.RefDec;
+ else Result := False;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF WIN_GDI}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function GetPrevCtrlBoundsRect( P: PControl; var R: TRect ): Boolean;
+var Idx: Integer;
+begin
+ Result := False;
+ if P.FParent = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Idx := P.FParent.ChildIndex( P ) - 1;
+ if Idx < 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := True;
+ R := P.FParent.Children[ Idx ].BoundsRect;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.PlaceUnder: PControl;
+var R: TRect;
+begin
+ Result := @Self;
+ if not GetPrevCtrlBoundsRect( @Self, R ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>}
+ Top := R.Bottom + fParent.fMargin;
+ Left := R.Left;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.PlaceDown: PControl;
+var R: TRect;
+begin
+ Result := @Self;
+ if not GetPrevCtrlBoundsRect( @Self, R ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>}
+ Top := R.Bottom + fParent.fMargin;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.PlaceRight: PControl;
+var R: TRect;
+begin
+ Result := @Self;
+ if not GetPrevCtrlBoundsRect( @Self, R ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>}
+ Top := R.Top;
+ Left := R.Right + fParent.fMargin;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.SetSize(W, H: Integer): PControl;
+var R: TRect;
+begin
+ R := BoundsRect;
+ if W > 0 then R.Right := R.Left + W;
+ if H > 0 then R.Bottom := R.Top + H;
+ SetBoundsRect( R );
+ Result := @Self;
+end;
+{$ENDIF PAS_VERSION}
+{$IFDEF WIN_GDI}
+
+function TControl.SetClientSize(W, H: Integer): PControl;
+begin
+ if W > 0 then ClientWidth := W;
+ if H > 0 then ClientHeight := H;
+ Result := @Self;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.AlignLeft(P: PControl): PControl;
+begin
+ Result := @Self;
+ Left := P.Left;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.AlignTop(P: PControl): PControl;
+begin
+ Result := @Self;
+ Top := P.Top;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF KEY_PREVIEW}
+ {$DEFINE KEY_PREVIEW_OR_ESC_CLOSE_DIALOGS}
+{$ENDIF}
+{$IFDEF ESC_CLOSE_DIALOGS}
+ {$IFNDEF KEY_PREVIEW_OR_ESC_CLOSE_DIALOGS}
+ {$DEFINE KEY_PREVIEW_OR_ESC_CLOSE_DIALOGS}
+ {$ENDIF}
+{$ENDIF}
+
+{$IFDEF ASM_VERSION} // see addition for combobox in pas version
+{$ELSE PAS_VERSION} //Pascal
+function WndProcCtrl(Self_: PControl; var Msg: TMsg; var Rslt: Integer): Boolean;
+var F: PControl;
+ Cmd : DWORD;
+begin
+ Result := FALSE;
+ with Self_^ do
+ case Msg.message of
+ CN_CTLCOLORMSGBOX..CN_CTLCOLORSTATIC:
+ begin
+ SetTextColor(Msg.WParam, Color2RGB(fTextColor));
+ if {$IFDEF USE_FLAGS} G2_Transparent in fFlagsG2
+ {$ELSE} fTransparent {$ENDIF} then
+ begin
+ SetBkMode( Msg.wParam, Windows.TRANSPARENT );
+ Rslt := GetStockObject( NULL_BRUSH );
+ end else
+ begin
+ SetBkMode( Msg.wParam, Windows.OPAQUE );
+ SetBkColor(Msg.WParam, Color2RGB( fColor ) );
+ Rslt := Global_GetCtlBrushHandle( Self_ );
+ end;
+ Result := TRUE;
+ end;
+ CM_COMMAND:
+ begin
+ Result := True;
+ Cmd := HiWord( Msg.wParam );
+ if Cmd = fCommandActions.aClick then
+ begin
+ if Integer( fClickDisabled ) <= 0 then
+ begin
+ Focused := TRUE;
+ DoClick;
+ end;
+ end else
+ if Cmd = fCommandActions.aEnter then
+ begin
+ if Assigned( EV.fOnEnter ) then EV.fOnEnter( Self_ );
+ end else
+ if Cmd = fCommandActions.aLeave then
+ begin
+ if Assigned( EV.fOnLeave ) then EV.fOnLeave( Self_ );
+ end else
+ if Integer(Cmd) = fCommandActions.aChange then
+ begin
+ if Assigned( EV.fOnChangeCtl ) then EV.fOnChangeCtl( Self_ );
+ end else
+ if Integer(Cmd) = fCommandActions.aSelChange then
+ begin
+ DoSelChange;
+ end
+ else Result := False;
+
+ if Result then
+ Rslt := CallDefWndProc( Msg );
+
+ end;
+
+ WM_SETFOCUS:
+ begin
+ Rslt := 0;
+ Result := TRUE;
+ F := ParentForm;
+ if F <> nil then
+ begin
+ if (F.DF.fCurrentControl <> nil) and (F.DF.fCurrentControl <> Self_)
+ {$IFDEF NIL_EVENTS}
+ and Assigned( F.DF.fCurrentControl.EV.fLeave )
+ {$ENDIF}
+ then
+ F.DF.fCurrentControl.EV.fLeave( F.DF.fCurrentControl );
+ F.DF.fCurrentControl := Self_;
+ Result := False; // go further handling
+ end;
+ end;
+ {$IFDEF KEY_PREVIEW_OR_ESC_CLOSE_DIALOGS}
+ WM_KEYDOWN:
+ begin
+ {$IFDEF KEY_PREVIEW}
+ //--------------------------------Truf-------------------------------------
+ F := ParentForm;
+ if F <> Self_ then
+ begin
+ if {$IFDEF USE_FLAGS} G6_KeyPreview in F.fFlagsG6
+ {$ELSE} F.fKeyPreview {$ENDIF} then
+ begin
+ {$IFDEF USE_FLAGS}
+ include( F.fFlagsG4, G4_Pushed );
+ {$ELSE} F.fKeyPreviewing := TRUE; {$ENDIF}
+ inc( F.DF.fKeyPreviewCount );
+ F.Perform(WM_KEYDOWN,msg.wParam,msg.lParam);
+ dec( F.DF.fKeyPreviewCount );
+ end;
+ end;
+ //--------------------------------Truf-------------------------------------
+ {$ENDIF KEY_PREVIEW}
+ {$IFDEF ESC_CLOSE_DIALOGS}
+ //---------------------------------Babenko Alexey--------------------------
+ begin
+ F := ParentForm;
+ if (F.fExStyle and WS_EX_DLGMODALFRAME) <> 0 then
+ if Msg.wParam = 27 then
+ F.Perform(WM_CLOSE, 0, 0);
+ end;
+ //---------------------------------Babenko Alexey--------------------------
+ {$ENDIF ESC_CLOSE_DIALOGS}
+ end;
+ {$IFDEF KEY_PREVIEW}
+ WM_KEYUP..WM_SYSDEADCHAR:
+ begin
+ F := ParentForm;
+ if F <> Self_ then
+ begin
+ if {$IFDEF USE_FLAGS} G6_KeyPreview in F.fFlagsG6
+ {$ELSE} F.fKeyPreview {$ENDIF} then
+ begin
+ {$IFDEF USE_FLAGS}
+ include( F.fFlagsG4, G4_Pushed );
+ {$ELSE} F.fKeyPreviewing := TRUE; {$ENDIF}
+ inc( F.DF.fKeyPreviewCount );
+ F.Perform(Msg.message,msg.wParam,msg.lParam);
+ dec( F.DF.fKeyPreviewCount );
+ end;
+ end;
+ end;
+ {$ENDIF KEY_PREVIEW}
+ {$ENDIF KEY_PREVIEW_OR_ESC_CLOSE_DIALOGS}
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF OLD_TRANSPARENT}
+function WndProcTransparent( Sender: PControl; var Msg: TMsg;
+ var Rslt: Integer ): Boolean;
+var DC, PDC, BLTDC: HDC;
+ Save: integer;
+ OLDp: THANDLE;
+ L, T: SmallInt;
+ TP, ParentClient: TPoint;
+ TR, Margins: TRect;
+ Wnd: HWND;
+ tRgn: HRgn;
+ C: PControl;
+begin
+ Result := FALSE;
+ {$IFDEF STOP_WNDPROCTRANSPARENT_AFTER_APPLETTERMINATED}
+ if AppletTerminated or not Sender.ToBeVisible then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$ENDIF}
+ case Msg.message of
+ WM_HSCROLL, WM_VSCROLL:
+ begin
+ Sender.Invalidate; exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ WM_SETTEXT:
+ begin
+ if {$IFDEF USE_FLAGS} not(G1_IsStaticControl in Sender.fFlagsG1)
+ {$ELSE} Sender.fIsStaticControl = 0 {$ENDIF} then
+ exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Sender.Invalidate;
+ Rslt := DefWindowProc
+ ( Sender.fHandle, WM_SETTEXT, Msg.wParam, Msg.lParam );
+ Result := TRUE; exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ WM_NCPAINT:
+ begin
+ if Sender.fTransparent then
+ Result := TRUE;
+ exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ if Sender.fTransparent and (
+ {$IFDEF USE_FLAGS} not(G2_DoubleBuffered in Sender.FParent.fFlagsG2)
+ {$ELSE} not Sender.fParent.fDoubleBuffered {$ENDIF} ) then
+ Sender.fTransparent := FALSE;
+ if {$IFDEF USE_FLAGS} [G2_DoubleBuffered, G2_Transparent] * Sender.fFlagsG2 = []
+ {$ELSE} not (Sender.fTransparent or Sender.fDoubleBuffered) {$ENDIF} then
+ exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if Sender.fAnchors and SELF_REQ_PAINT <> 0 then exit; {>>>>>>>>>>>>>>>>>>}
+
+ case Msg.message of
+ WM_ERASEBKGND:
+ begin
+ Result := TRUE;
+ end;
+ WM_PAINT:
+ begin
+ ValidateRect(Sender.fHandle, nil); //???--brandys???
+ if (Sender.fTransparent)
+ and (Sender.fAnchors and PARENT_REQ_PAINT = 0) then
+ begin
+ InvalidateRect(Sender.fParent.Handle, nil, FALSE);
+ Result := TRUE; exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ GetClientRect(Msg.hwnd, Margins);
+ OLDp := 0;
+ if Sender.fAnchors and PARENT_REQ_PAINT = 0 then
+ begin
+ Sender.fDblExcludeRgn := CreateRectRgn(0, 0, Margins.Right, Margins.Bottom);
+ DC := GetDC(0);
+ PDC := CreateCompatibleDC( DC );
+ OLDp := SelectObject(PDC,
+ CreateCompatibleBitmap(DC, Margins.Right, Margins.Bottom) );
+ ReleaseDC(0, DC);
+ Sender.fParentCoordX := 0;
+ Sender.fParentCoordy := 0;
+ end else begin
+ PDC := Msg.wParam;
+ Sender.fDblExcludeRgn := Sender.fParent.fDblExcludeRgn;
+ end;
+
+ Sender.fAnchors := Sender.fAnchors or SELF_REQ_PAINT;
+ Sender.fPaintDC := PDC;
+ if (Sender.fAnchors and PARENT_REQ_PAINT = 0) or
+ {$IFDEF USE_FLAGS} G2_DoubleBuffered in Sender.fFlagsG2
+ {$ELSE} Sender.fDoubleBuffered {$ENDIF} then
+ Sender.Perform(WM_ERASEBKGND, PDC, 0);
+ Sender.Perform(WM_PAINT, PDC, 0);
+
+ Wnd := GetWindow( Sender.fHandle, GW_CHILD );
+ Wnd := GetWindow( Wnd, GW_HWNDLAST);
+ while Wnd <> 0 do begin
+ if IsWindowVisible(Wnd) then begin
+ {$IFDEF USE_PROP}
+ C := Pointer( GetProp( Wnd, ID_SELF ) );
+ {$ELSE}
+ C := Pointer( GetWindowLong( Wnd, GWL_USERDATA ) );
+ {$ENDIF}
+ with C^ do begin
+ if (C <> nil) and
+ {$IFDEF USE_FLAGS} ( [G2_DoubleBuffered, G2_Transparent]
+ * fFlagsG2 <> [] )
+ {$ELSE} (fTransparent or fDoubleBuffered) {$ENDIF} then
+ begin
+ Save := SaveDC( PDC );
+ Include( fAnchors, PARENT_REQ_PAINT );
+ L := Sender.fParentCoordX + Left;
+ T := Sender.fParentCoordY + Top;
+ SetWindowOrgEx(PDC, -L, -T, nil);
+ SendMessage(Wnd, WM_PRINT, PDC, PRF_NONCLIENT);
+ TP.x := 0; TP.Y := 0;
+ ClientToScreen(fHandle, TP);
+ GetWindowRect(fHandle, TR);
+ fParentCoordX := L + TP.X - TR.Left;
+ fParentCoordY := T + TP.Y - TR.Top;
+ SetWindowOrgEx(PDC, -fParentCoordX, -fParentCoordY, nil);
+ GetClientRect(Wnd, TR);
+ IntersectClipRect(PDC, 0, 0, TR.Right, TR.Bottom);
+ SendMessage(Wnd, WM_PAINT, PDC, 0);
+ Exclude( fAnchors, PARENT_REQ_PAINT );
+ RestoreDC( PDC, Save );
+ end else
+ begin
+ GetWindowRect(Wnd, TR);
+ TP.X := 0; TP.Y := 0;
+ ClientToScreen(Sender.fHandle, TP);
+ TP.X := TR.Left - TP.X + Sender.fParentCoordX;
+ TP.Y := TR.Top - TP.Y + Sender.fParentCoordY;
+ TR.Left := TR.Right - TR.Left;
+ TR.Top := TR.Bottom - TR.Top;
+
+ tRgn := CreateRectRgn(TP.X, TP.Y, TP.X+TR.Left, TP.Y+TR.Top);
+ CombineRgn(Sender.fDblExcludeRgn, Sender.fDblExcludeRgn, tRgn, RGN_DIFF);
+ DeleteObject(tRgn);
+ end;
+ end;
+ end;
+ Wnd := GetWindow( Wnd, GW_HWNDPREV );
+ end;
+ Sender.fPaintDC := 0;
+ Sender.fAnchors := Sender.fAnchors and not SELF_REQ_PAINT;
+
+ if Sender.fAnchors and PARENT_REQ_PAINT = 0 then
+ begin
+ BLTDC := GetWindowDC(Sender.fHandle);
+ GetWindowRect( Sender.fHandle, TR );
+ ParentClient.x := 0; ParentClient.y := 0;
+ ClientToScreen( Sender.fHandle, ParentClient );
+ SetWindowOrgEx(BLTDC, TR.Left - ParentClient.x, TR.Top - ParentClient.y, nil);
+ OffsetRgn(Sender.fDblExcludeRgn, ParentClient.x - TR.Left, ParentClient.y - TR.Top);
+ ExtSelectClipRgn(BLTDC, Sender.fDblExcludeRgn, RGN_AND);
+
+ BitBlt( BLTDC, 0, 0, Margins.Right, Margins.Bottom, PDC, 0, 0, SRCCOPY );
+ ReleaseDC(Sender.fHandle, BLTDC);
+ DeleteObject(SelectObject( PDC, OLDp ));
+ DeleteObject(Sender.fDblExcludeRgn);
+ DeleteDC( PDC );
+ end;
+
+ //ValidateRect(Sender.fHandle, nil); //???++brandys???//
+ Result := TRUE;
+ end;
+ end;
+end;
+{$ELSE NEW_TRANSPARENT} // by Alexander Karpinsky a.k.a. homm
+function WndProcTransparent( Sender: PControl; var Msg: TMsg;
+ var Rslt: Integer ): Boolean;
+
+ function SetRectRgnInderect(tRgn: HRGN; const TR: TRect): BOOL;
+ begin
+ Result := SetRectRgn(tRgn, TR.Left, TR.Top, TR.Right, TR.Bottom);
+ end;
+
+var
+ DC, PDC, BLTDC: HDC;
+ Save: integer;
+ OLDp: THANDLE;
+ L, T: SmallInt;
+ TP: TPoint;
+ TR, Margins: TRect;
+ Wnd: HWND;
+ C: PControl;
+ ChildRgn: HRGN;
+ PS: TPaintStruct;
+
+begin
+ Result := FALSE;
+
+ {$IFDEF STOP_WNDPROCTRANSPARENT_AFTER_APPLETTERMINATED}
+ if AppletTerminated or not Sender.ToBeVisible then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$ENDIF}
+
+ if {$IFDEF USE_FLAGS} (G2_Transparent in Sender.fFlagsG2)
+ {$ELSE} Sender.fTransparent {$ENDIF} and (
+ {$IFDEF USE_FLAGS} not(G2_DoubleBuffered in Sender.FParent.fFlagsG2)
+ {$ELSE} not Sender.fParent.fDoubleBuffered {$ENDIF} ) then
+ {$IFDEF USE_FLAGS} exclude( Sender.fFlagsG2, G2_Transparent );
+ {$ELSE} Sender.fTransparent := FALSE; {$ENDIF}
+ if {$IFDEF USE_FLAGS} [G2_DoubleBuffered, G2_Transparent] * Sender.fFlagsG2 = []
+ {$ELSE} not (Sender.fTransparent or Sender.fDoubleBuffered) {$ENDIF} then
+ exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+
+ case Msg.message of
+ WM_HSCROLL, WM_VSCROLL:
+ begin
+ Sender.Invalidate; exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ WM_SETTEXT:
+ begin
+ if {$IFDEF USE_FLAGS} not(G1_IsStaticControl in Sender.fFlagsG1)
+ {$ELSE} Sender.fIsStaticControl = 0 {$ENDIF} then exit; {>>>>>>>>>>}
+ Sender.Invalidate;
+ Rslt := DefWindowProc ( Sender.fHandle, WM_SETTEXT, Msg.wParam, Msg.lParam );
+ Result := TRUE; exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ WM_PAINT,
+ WM_ERASEBKGND:;
+ WM_NCPAINT:
+ if {$IFDEF USE_FLAGS} not(G2_Transparent in Sender.fFlagsG2)
+ {$ELSE} not Sender.fTransparent {$ENDIF} then
+ exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ else exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ if Sender.fAnchors and SELF_REQ_PAINT <> 0 then exit; {>>>>>>>>>>>>>>>>>>>>>}
+ Result := TRUE;
+ if Assigned(Sender.fParent)
+ and {$IFDEF USE_FLAGS} not(G3_IsForm in Sender.fFlagsG3)
+ {$ELSE} (not Sender.fIsForm) {$ENDIF}
+ and {$IFDEF USE_FLAGS} (G2_DoubleBuffered in Sender.FParent.fFlagsG2)
+ {$ELSE} Sender.FParent.fDoubleBuffered {$ENDIF}
+ and (Sender.fAnchors and PARENT_REQ_PAINT = 0) then
+ begin
+ TR := Sender.BoundsRect;
+ InvalidateRect(Sender.fParent.fHandle, @TR, true);
+ ValidateRect(Sender.fHandle, nil); //???--brandys???+
+ exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+
+ if Msg.message = WM_PAINT then begin
+ OLDp := 0;
+ if Sender.fAnchors and PARENT_REQ_PAINT = 0 then
+ begin
+ Sender.fDblExcludeRgn := CreateRectRgn(0, 0, 0, 0);
+ if Integer( GetUpdateRgn(Sender.fHandle, Sender.fDblExcludeRgn, TRUE) ) <= NULLREGION then
+ begin
+ DeleteObject(Sender.fDblExcludeRgn);
+ exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+
+ DC := BeginPaint(Sender.fHandle, PS);
+ PDC := CreateCompatibleDC( DC );
+ GetClientRect(Msg.hwnd, Margins);
+ OLDp := SelectObject(PDC, CreateCompatibleBitmap(DC, Margins.Right, Margins.Bottom) );
+ Sender.fParentCoordX := 0;
+ Sender.fParentCoordy := 0;
+ end else
+ begin
+ PDC := Msg.wParam;
+ Sender.fDblExcludeRgn := Sender.fParent.fDblExcludeRgn;
+ end;
+
+ Sender.fAnchors := Sender.fAnchors or SELF_REQ_PAINT;
+ Sender.fPaintDC := PDC;
+ if (Sender.fAnchors and PARENT_REQ_PAINT = 0) or
+ {$IFDEF USE_FLAGS} (G2_DoubleBuffered in Sender.fFlagsG2)
+ {$ELSE} Sender.fDoubleBuffered {$ENDIF} then
+ Sender.Perform(WM_ERASEBKGND, PDC, 0);
+ Sender.Perform(WM_PAINT, PDC, 0);
+
+
+ Wnd := GetWindow( Sender.fHandle, GW_CHILD );
+ Wnd := GetWindow( Wnd, GW_HWNDLAST);
+ while Wnd <> 0 do begin
+ if IsWindowVisible(Wnd) then begin
+ ChildRgn := CreateRectRgn(0, 0, 0, 0);
+ if Integer( GetWindowRgn(WND, ChildRgn) ) <= NULLREGION then begin
+ GetWindowRect(WND, TR);
+ TP.X := 0; TP.Y := 0;
+ ClientToScreen(Sender.fHandle, TP);
+ OffsetRect(TR, -TP.X , -TP.Y);
+ SetRectRgnInderect(ChildRgn, TR);
+ end;
+ OffsetRgn(ChildRgn, Sender.fParentCoordX, Sender.fParentCoordY);
+
+ {$IFDEF USE_PROP}
+ C := Pointer( GetProp( Wnd, ID_SELF ) );
+ {$ELSE}
+ C := Pointer( GetWindowLong( Wnd, GWL_USERDATA ) );
+ {$ENDIF}
+ if CombineRgn(ChildRgn, ChildRgn, Sender.fDblExcludeRgn, RGN_AND) >= SIMPLEREGION then begin
+ with C^ do begin
+ if (C <> nil) and
+ {$IFDEF USE_FLAGS} ( [G2_DoubleBuffered, G2_Transparent] * fFlagsG2 <> [] )
+ {$ELSE} (fTransparent or fDoubleBuffered) {$ENDIF} then
+ begin
+ Save := SaveDC( PDC );
+ fAnchors := fAnchors or PARENT_REQ_PAINT;
+
+ L := Sender.fParentCoordX + Left;
+ T := Sender.fParentCoordY + Top;
+ SetWindowOrgEx(PDC, -L, -T, nil);
+ SendMessage(Wnd, WM_PRINT, PDC, PRF_NONCLIENT);
+ TP.x := 0; TP.Y := 0;
+ ClientToScreen(fHandle, TP);
+ GetWindowRect(fHandle, TR);
+ fParentCoordX := L + TP.X - TR.Left;
+ fParentCoordY := T + TP.Y - TR.Top;
+ SetWindowOrgEx(PDC, -fParentCoordX, -fParentCoordY, nil);
+ GetClientRect(Wnd, TR);
+ IntersectClipRect(PDC, 0, 0, TR.Right, TR.Bottom);
+ SendMessage(Wnd, WM_PAINT, PDC, 0);
+ fAnchors := fAnchors and not PARENT_REQ_PAINT;
+ RestoreDC( PDC, Save );
+ end else begin
+ CombineRgn(Sender.fDblExcludeRgn, Sender.fDblExcludeRgn, ChildRgn, RGN_DIFF);
+ end;
+ end;
+ end; // if Save >= SIMPLEREGION then begin
+ DeleteObject(ChildRgn);
+ end;
+ Wnd := GetWindow( Wnd, GW_HWNDPREV );
+ end;
+ Sender.fPaintDC := 0;
+ Sender.fAnchors := Sender.fAnchors and not SELF_REQ_PAINT;
+
+ if Sender.fAnchors and PARENT_REQ_PAINT = 0 then
+ begin
+ BLTDC := GetDCEx(Sender.fHandle, 0, DCX_CACHE or DCX_CLIPSIBLINGS);
+ ExtSelectClipRgn(BLTDC, Sender.fDblExcludeRgn, RGN_AND);
+
+ BitBlt(BLTDC, 0, 0, Margins.Right, Margins.Bottom, PDC, 0, 0, SRCCOPY );
+
+ ReleaseDC(Sender.fHandle, BLTDC);
+ DeleteObject(SelectObject( PDC, OLDp ));
+ DeleteObject(Sender.fDblExcludeRgn);
+ DeleteDC( PDC );
+ EndPaint(Sender.fHandle, PS);
+ end;
+ end;
+end;
+{$ENDIF}
+
+{$IFDEF ASM_noVERSION}
+function WndProcPaint( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+const szPaintStruct = sizeof(TPaintStruct);
+asm
+ CMP word ptr [EDX].TMsg.message, WM_PRINT
+ JE @@print
+ CMP word ptr [EDX].TMsg.message, WM_PAINT
+ JNE @@ret_false
+@@print:
+ CMP word ptr [EAX].TControl.fOnPaint.TMethod.Code+2, 0
+ JE @@ret_false
+ PUSH EBX
+ PUSH ESI
+
+ XCHG EBX, EAX
+ MOV ESI, EDX
+ XOR EAX, EAX
+ PUSH ECX
+ PUSH EAX
+ PUSH EAX
+ PUSH EAX
+ PUSH EAX
+ CALL CreateRectRgn
+ MOV [EBX].TControl.fUpdRgn, EAX
+
+ MOVSX EDX, [EBX].TControl.fEraseUpdRgn
+ PUSH EDX
+ PUSH EAX
+ PUSH [EBX].TControl.fHandle
+ CALL GetUpdateRgn
+
+ CMP EAX, 1
+ JA @@collectUpdRgn
+
+ XOR EAX, EAX
+ XCHG EAX, [EBX].TControl.fUpdRgn
+ PUSH EAX
+ CALL DeleteObject
+
+@@collectUpdRgn:
+ MOV ECX, [EBX].TControl.fCollectUpdRgn
+ JECXZ @@asg_fPaintDC
+ XCHG EAX, ECX
+ MOV ECX, [EBX].TControl.fUpdRgn
+ JECXZ @@asg_fPaintDC
+
+ PUSH RGN_OR
+ PUSH ECX
+ PUSH EAX
+ PUSH EAX
+ CALL CombineRgn
+
+ DEC EAX
+ JNZ @@invalidateRgn
+
+ ADD ESP, -16
+ PUSH ESP
+ PUSH [EBX].TControl.fHandle
+ CALL Windows.GetClientRect
+
+ PUSH [EBX].TControl.fCollectUpdRgn
+ CALL DeleteObject
+ CALL CreateRectRgn
+ MOV [EBX].TControl.fCollectUpdRgn, EAX
+
+@@invalidateRgn:
+ MOVSX EDX, [EBX].TControl.fEraseUpdRgn
+ PUSH EDX
+ PUSH [EBX].TControl.fCollectUpdRgn
+ PUSH [EBX].TControl.fHandle
+ CALL InvalidateRgn
+
+@@asg_fPaintDC:
+ MOV ECX, [ESI].TMsg.wParam
+ INC ECX
+ LOOP @@storePaintDC
+
+ ADD ESP, -szPaintStruct
+ PUSH ESP
+ PUSH [EBX].TControl.fHandle
+ CALL BeginPaint
+ XCHG ECX, EAX
+@@storePaintDC:
+ MOV [EBX].TControl.fPaintDC, ECX
+ XCHG EAX, ECX
+
+ MOV ECX, [EBX].TControl.fCollectUpdRgn
+ JECXZ @@doOnPaint
+
+ PUSH ECX
+ PUSH EAX
+ CALL SelectClipRgn
+
+@@doOnPaint:
+ MOV ECX, [EBX].TControl.fPaintDC
+ MOV EDX, EBX
+
+ MOV EAX, [EBX].TControl.fOnPaint.TMethod.Data
+ CALL dword ptr [EBX].TControl.fOnPaint.TMethod.Code
+
+ MOV ECX, [EBX].TControl.fCanvas
+ JECXZ @@e_paint
+
+ XCHG EAX, ECX
+ XOR EDX, EDX
+ CALL TCanvas.SetHandle
+
+@@e_paint:
+ MOV ECX, [ESI].TMsg.wParam
+ INC ECX
+ LOOP @@zero_fPaintDC
+
+ PUSH ESP
+ PUSH [EBX].TControl.fHandle
+ CALL EndPaint
+ ADD ESP, szPaintStruct
+
+@@zero_fPaintDC:
+ XOR ECX, ECX
+ MOV [EBX].TControl.fPaintDC, ECX
+
+ POP EAX
+ MOV [EAX], ECX
+
+ XCHG ECX, [EBX].TControl.fUpdRgn
+ JECXZ @@exit_True
+
+ PUSH ECX
+ CALL DeleteObject
+
+@@exit_True:
+ POP ESI
+ POP EBX
+ MOV AL, 1
+ RET
+
+@@ret_false:
+ XOR EAX, EAX
+end;
+{$ELSE PAS_VERSION} //Pascal
+function WndProcPaint( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var PaintStruct: TPaintStruct;
+ Cplxity: Integer;
+ OldPaintDC: HDC;
+begin
+ with Self_^ do
+ case Msg.message of
+ //WM_PRINT,
+ WM_PAINT: if assigned( EV.fOnPaint ) then
+ begin
+ fUpdRgn := CreateRectRgn( 0, 0, 0, 0 );
+ Cplxity := Integer( GetUpdateRgn( fHandle, fUpdRgn,
+ {$IFDEF USE_FLAGS} G5_EraseBkgnd in fFlagsG5
+ {$ELSE} fEraseUpdRgn {$ENDIF} ) );
+ if (Cplxity = NULLREGION) or (Cplxity = ERROR) then
+ begin
+ DeleteObject( fUpdRgn );
+ fUpdRgn := 0;
+ end;
+
+ OldPaintDC := fPaintDC;
+ fPaintDC := Msg.wParam;
+ if fPaintDC = 0 then
+ fPaintDC := BeginPaint( fHandle, PaintStruct );
+
+ EV.fOnPaint( Self_, fPaintDC );
+
+ if assigned( Self_.fCanvas ) then
+ Self_.fCanvas.SetHandle( 0 );
+
+ if Msg.wParam = 0 then
+ EndPaint( fHandle, PaintStruct );
+ fPaintDC := OldPaintDC;
+
+ Rslt := 0;
+
+ Result := True;
+ if fUpdRgn <> 0 then
+ DeleteObject( fUpdRgn );
+ fUpdRgn := 0;
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ Result := FALSE;
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF WIN_GDI}
+{$IFDEF GDI}
+procedure TControl.SetOnPaint( const Value: TOnPaint );
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnPaint := Value;
+ AttachProc( WndProcPaint );
+end;
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+FUNCTION expose_widget( Widget: PGtkWidget; Event: PGdkEventExpose;
+ Sender: PControl ): Boolean; cdecl;
+BEGIN
+ IF not Assigned( Sender.fOnPaint ) THEN Result := FALSE
+ ELSE
+ BEGIN
+ Sender.Canvas.SaveState;
+ Sender.fOnPaint( Sender, Sender.Canvas.Handle );
+ Sender.Canvas.RestoreState;
+ Result := TRUE;
+ END;
+END;
+
+PROCEDURE TControl.SetOnPaint( const Value: TOnPaint );
+BEGIN
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnPaint := Value;
+ {$IFNDEF SMALLER_CODE} // it is actually not necessary to disconnect, event
+ // still will be fired but fOnPaint is not assigned
+ // so FALSE will be returned to GTK.
+ IF NOT Assigned( Value ) THEN
+ gtk_signal_disconnect( fHandle, fExposeEvent )
+ ELSE
+ {$ENDIF}
+ fExposeEvent := gtk_signal_connect( GTK_OBJECT( fHandle ), 'expose_event',
+ @ expose_widget, @ Self );
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+{$IFDEF WIN_GDI}
+
+function WndProcEraseBkgnd( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var PaintStruct: TPaintStruct;
+ OldPaintDC: HDC;
+begin
+ Result := FALSE;
+ if Msg.message = WM_ERASEBKGND then
+ begin
+ if Assigned( Sender.OnEraseBkgnd ) then
+ begin
+ OldPaintDC := Sender.fPaintDC;
+ Sender.fPaintDC := Msg.wParam;
+ if Sender.fPaintDC = 0 then
+ Sender.fPaintDC := BeginPaint( Sender.fHandle, PaintStruct );
+ Sender.OnEraseBkgnd( Sender, Msg.wParam );
+ if Msg.wParam = 0 then
+ EndPaint( Sender.fHandle, PaintStruct );
+ if Assigned( Sender.fCanvas ) then
+ Sender.fCanvas.SetHandle( 0 );
+ Sender.fPaintDC := OldPaintDC;
+ Rslt := 0;
+ Result := TRUE;
+ end
+ else
+ Rslt := 0;
+ end;
+end;
+
+procedure TControl.SetOnEraseBkgnd(const Value: TOnPaint);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnEraseBkgnd := Value;
+ AttachProc( WndProcEraseBkgnd );
+end;
+
+procedure DummyPaintClear( Self_: PControl; Sender: PControl; DC: HDC );
+begin
+ Sender.Canvas.FillRect( Sender.ClientRect );
+end;
+
+{$IFDEF NEW_GRADIENT}
+function WndProcGradient( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var PaintStruct: TPaintStruct;
+ Bmp: PBitmap;
+ CR: TRect;
+ I: Integer;
+ R, G, B: Integer;
+ R1, G1, B1: Integer;
+ C: TColor;
+ W, H, WH: Integer;
+ OldPaintDC: HDC;
+ Pattern: PBitmap;
+ pdc: HDC;
+ pw: integer;
+
+begin
+ case Msg.message of
+ WM_PAINT, WM_PRINTCLIENT:
+ begin
+ result := false;
+ CR := Self_.ClientRect;
+ case Self_.DF.fGradientStyle of
+ gsHorizontal: begin
+ W := CR.Right;
+ H := 1;
+ WH := W;
+ pw := 32;
+ end;
+ gsVertical: begin
+ W := 1;
+ H := CR.Bottom;
+ WH := H;
+ pw := 32
+ end;
+ gsTopToBottom,
+ gsBottomToTop: begin
+ W := CR.Bottom + CR.Right;
+ H := 1;
+ WH := W;
+ pw := 1 + (CR.Bottom div 16);
+ if pw > 6 then
+ pw := 6;
+ end;
+ else exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ // <-- impartant if user change GradientStyle to not supported by this object
+ end;
+ OldPaintDC := Self_.fPaintDC;
+ Self_.fPaintDC := Msg.wParam;
+ if Self_.fPaintDC = 0 then
+ Self_.fPaintDC := BeginPaint( Self_.fHandle, PaintStruct );
+ Bmp := NewDIBBitmap( W, H, pf24bit );
+ C := Color2RGB( Self_.DF.fColor1 );
+ R := C shr 16;
+ G := (C shr 8) and $FF;
+ B := C and $FF;
+ C := Color2RGB( Self_.DF.fColor2 );
+ R1 := C shr 16;
+ G1 := (C shr 8) and $FF;
+ B1 := C and $FF;
+ for I := 0 to WH-1 do begin
+ C := (( R + (R1 - R) * I div WH ) shl 16) or
+ (( G + (G1 - G) * I div WH ) shl 8) or
+ ( B + (B1 - B) * I div WH );
+ if Self_.DF.fGradientStyle = gsVertical then
+ Bmp.DIBPixels[ 0, I ] := C
+ else
+ Bmp.DIBPixels[ I, 0 ] := C;
+ end;
+ if Self_.DF.fGradientStyle = gsVertical then
+ Pattern := NewBitMap(pw, H)
+ else
+ Pattern := NewBitMap(W, pw);
+ pdc := Pattern.Canvas.Handle;
+ SetStretchBltMode( pdc, HALFTONE);
+ SetBrushOrgEx( pdc, 0, 0, nil );
+ StretchBlt( pdc, 0, 0, Pattern.Width, Pattern.Height, Bmp.Canvas.Handle,
+ 0, 0, W, H, SRCCOPY );
+
+ case Self_.DF.fGradientStyle of
+ gsHorizontal: for i := 0 to (CR.Bottom div pw) do
+ Pattern.Draw(Self_.fPaintDC, 0, i*pw);
+ gsVertical: for i := 0 to (CR.Right div pw) do
+ Pattern.Draw(Self_.fPaintDC, i*pw, 0);
+ gsTopToBottom: for i := 0 to ((CR.Bottom + pw -1) div pw)-1 do
+ Pattern.Draw(Self_.fPaintDC, -i*pw, i*pw);
+ gsBottomToTop: for i := 0 to ((CR.Bottom + pw -1) div pw)-1 do
+ Pattern.Draw(Self_.fPaintDC, -CR.Bottom + i*pw, i*pw);
+ end;
+ Bmp.Free;
+ Pattern.Free;
+
+ if TMethod( Self_.EV.fOnPaint2 ).Code = @ DummyPaintClear then
+ {$IFDEF MAKE_METHOD}
+ Self_.EV.fOnPaint2 := TOnPaint( MakeMethod( nil, @ DummyObjProc ) );
+ {$ELSE}
+ TMethod( Self_.EV.fOnPaint2 ).Code := @DummyObjProc;
+ {$ENDIF}
+ if Assigned( Self_.EV.fOnPaint ) then
+ Self_.EV.fOnPaint( Self_, Self_.fPaintDC );
+
+ if Msg.wParam = 0 then
+ EndPaint( Self_.fHandle, PaintStruct );
+ Self_.fPaintDC := OldPaintDC;
+ Rslt := 0;
+ Result := True;
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ Result := False;
+end;
+{$ELSE OLD_GRADIENT}
+function WndProcGradient( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var PaintStruct: TPaintStruct;
+ Bmp: PBitmap;
+ CR: TRect;
+ I, R, G, B, R1, G1, B1, W, H, WH: Integer;
+ C: TColor;
+ W9x: Boolean;
+ Br: HBrush;
+ OldPaintDC: HDC;
+begin
+ case Msg.message of
+ WM_PAINT, WM_PRINTCLIENT:
+ begin
+ OldPaintDC := Self_.fPaintDC;
+ Self_.fPaintDC := Msg.wParam;
+ if Self_.fPaintDC = 0 then
+ Self_.fPaintDC := BeginPaint( Self_.fHandle, PaintStruct );
+ CR := Self_.ClientRect;
+ W9x := WinVer < wvNT;
+ W := 1;
+ H := CR.Bottom;
+ WH := H;
+ Bmp := nil;
+ if Self_.DF.fGradientStyle = gsHorizontal then
+ begin
+ W := CR.Right;
+ H := 1;
+ WH := W;
+ end;
+ if not W9x then
+ Bmp := NewDIBBitmap( W, H, pf32bit );
+ C := Color2RGB( Self_.DF.fColor1 );
+ R := C shr 16;
+ G := (C shr 8) and $FF;
+ B := C and $FF;
+ C := Color2RGB( Self_.DF.fColor2 );
+ R1 := C shr 16;
+ G1 := (C shr 8) and $FF;
+ B1 := C and $FF;
+ for I := 0 to WH-1 do
+ begin
+ C := ((( R + (R1 - R) * I div WH ) and $FF) shl 16) or
+ ((( G + (G1 - G) * I div WH ) and $FF) shl 8) or
+ ( B + (B1 - B) * I div WH ) and $FF;
+ if W9x then
+ begin
+ if Self_.DF.fGradientStyle <> gsHorizontal then
+ CR.Bottom := CR.Top + 1
+ else
+ CR.Right := CR.Left + 1;
+ Br := CreateSolidBrush( C );
+ Windows.FillRect( Self_.fPaintDC, CR, Br );
+ DeleteObject( Br );
+ if Self_.DF.fGradientStyle <> gsHorizontal then
+ Inc( CR.Top )
+ else
+ Inc( CR.Left );
+ end
+ else
+ begin
+ if Self_.DF.fGradientStyle <> gsHorizontal then
+ Bmp.DIBPixels[ 0, I ] := C
+ else
+ Bmp.DIBPixels[ I, 0 ] := C;
+ end;
+ end;
+ if not W9x then
+ begin
+ SetStretchBltMode( Self_.fPaintDC, HALFTONE );
+ SetBrushOrgEx( Self_.fPaintDC, 0, 0, nil );
+ StretchBlt( Self_.fPaintDC, 0, 0, CR.Right, CR.Bottom, Bmp.Canvas.Handle,
+ 0, 0, W, H, SRCCOPY );
+ Bmp.Free;
+ end;
+
+ if TMethod( Self_.EV.fOnPaint2 ).Code = @ DummyPaintClear then
+ {$IFDEF MAKE_METHOD}
+ Self_.EV.fOnPaint2 := TOnPaint( MakeMethod( nil, @ DummyObjProc ) );
+ {$ELSE}
+ TMethod( Self_.EV.fOnPaint2 ).Code := @DummyObjProc;
+ {$ENDIF}
+ if Assigned( Self_.EV.fOnPaint ) then
+ Self_.EV.fOnPaint( Self_, Self_.fPaintDC );
+
+ if Msg.wParam = 0 then
+ EndPaint( Self_.fHandle, PaintStruct );
+ Self_.fPaintDC := OldPaintDC;
+ Rslt := 0;
+ Result := True;
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ Result := False;
+end;
+{$ENDIF OLD_GRADIENT}
+
+function WndProcGradientEx( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+ function Ceil( X: Double ): Integer;
+ begin
+ Result := Round( X ) {+ 1};
+ //if X > 0 then dec( Result ) else inc( Result );
+ end;
+const
+ SQRT2 = 1.4142135623730950488016887242097;
+var
+ RC, R0: TRect;
+ C, C2: TColor;
+ R1, G1, B1: Integer;
+ R2, G2, B2: Integer;
+ DX1, DX2, DY1, DY2, DR, DG, DB, K: Double;
+ PaintStruct: TPaintStruct;
+ I: Integer;
+ Br: HBrush;
+ Rgn: HRgn;
+ Poly: array[ 0..3 ] of TPoint;
+ OldPaintDC: HDC;
+ fX1, fX2, fY1, fY2: Double;
+
+ procedure OffsetF( DX, DY: Double );
+ begin
+ fX1 := fX1 + DX;
+ fX2 := fX2 + DX;
+ fY1 := fY1 + DY;
+ fY2 := fY2 + DY;
+ end;
+begin
+ Result := FALSE;
+ if (Msg.message <> WM_PAINT) and (Msg.message <> WM_PRINTCLIENT) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if Self_.DF.fGradientStyle in [ gsHorizontal, gsVertical ] then
+ begin
+ Result := WndProcGradient( Self_, Msg, Rslt );
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ C := Color2RGB( Self_.DF.fColor2 );
+ R2 := C and $FF;
+ G2 := (C shr 8) and $FF;
+ B2 := (C shr 16) and $FF;
+ C := Color2RGB( Self_.DF.fColor1 );
+ R1 := C and $FF;
+ G1 := (C shr 8) and $FF;
+ B1 := (C shr 16) and $FF;
+ DR := (R2 - R1) / 256;
+ DG := (G2 - G1) / 256;
+ DB := (B2 - B1) / 256;
+ OldPaintDC := Self_.fPaintDC;
+ Self_.fPaintDC := Msg.wParam;
+ if Self_.fPaintDC = 0 then
+ Self_.fPaintDC := BeginPaint( Self_.fHandle, PaintStruct );
+ RC := Self_.ClientRect;
+ fX1 := 0;
+ fY1 := 0;
+ case Self_.DF.fGradientStyle of
+ gsRombic:
+ begin
+ fX2 := RC.Right / 128;
+ fY2 := RC.Bottom / 128;
+ end;
+ gsElliptic:
+ begin
+ fX2 := RC.Right / 256 * SQRT2;
+ fY2 := RC.Bottom / 256 * SQRT2;
+ end;
+ else
+ begin
+ fX2 := RC.Right / 256;
+ fY2 := RC.Bottom / 256;
+ end;
+ end;
+ case Self_.DF.fGradientStyle of
+ gsRectangle, gsRombic, gsElliptic:
+ begin
+ case Self_.DF.fGradientLayout of
+ glCenter, glTop, glBottom:
+ OffsetF( (RC.Right - fX2) / 2, 0 );
+ glTopRight, glBottomRight, glRight:
+ OffsetF( RC.Right - fX2 / 2, 0 );
+ glTopLeft, glBottomLeft, glLeft:
+ OffsetF( -fX2 / 2, 0 );
+ end;
+ case Self_.DF.fGradientLayout of
+ glCenter, glLeft, glRight:
+ OffsetF( 0, (RC.Bottom - fY2) / 2 );
+ glBottom, glBottomLeft, glBottomRight:
+ OffsetF( 0, RC.Bottom - fY2 / 2 );
+ glTop, glTopLeft, glTopRight:
+ OffsetF( 0, -fY2 / 2 )
+ end;
+ end;
+ end;
+ DX1 := -fX1 / 255; //(-RF.Left) / 255;
+ DY1 := -fY1 / 255; // (-RF.Top) / 255;
+ DX2 := (RC.Right - fX2) / 255; //(RC.Right - RF.Right) / 255;
+ DY2 := (RC.Bottom - fY2) / 255;
+ case Self_.DF.fGradientStyle of
+ gsRombic, gsElliptic:
+ begin
+ if DX2 < -DX1 then DX2 := -DX1;
+ if DY2 < -DY1 then DY2 := -DY1;
+ K := 2;
+ if Self_.DF.fGradientStyle = gsElliptic then K := SQRT2;
+ DX2 := DX2 * K;
+ DY2 := DY2 * K;
+ DX1 := -DX2;
+ DY1 := -DY2;
+ end;
+ end;
+ C2 := C;
+ for I := 0 to 255 do
+ begin
+ if (I < 255) then
+ begin
+ C2 := TColor( (( Ceil( B1 + DB * (I+1) ) and $FF) shl 16) or
+ (( Ceil( G1 + DG * (I+1) ) and $FF) shl 8) or
+ Ceil( R1 + DR * (I+1) ) and $FF );
+ if (Self_.DF.fGradientStyle in [gsRombic,gsElliptic,gsRectangle]) and
+ (C2 = C) then continue;
+ end;
+ Br := CreateSolidBrush( C );
+ R0 := MakeRect( Ceil( fX1 + DX1 * I ),
+ Ceil( fY1 + DY1 * I ),
+ Ceil( fX2 + DX2 * I ) + 1,
+ Ceil( fY2 + DY2 * I ) + 1 );
+ Rgn := 0;
+ case Self_.DF.fGradientStyle of
+ gsRectangle:
+ Rgn := CreateRectRgnIndirect( R0 );
+ gsRombic:
+ begin
+ Poly[ 0 ].x := R0.Left;
+ Poly[ 0 ].y := R0.Top + (R0.Bottom - R0.Top) div 2;
+ Poly[ 1 ].x := R0.Left + (R0.Right - R0.Left) div 2;
+ Poly[ 1 ].y := R0.Top;
+ Poly[ 2 ].x := R0.Right;
+ Poly[ 2 ].y := Poly[ 0 ].y;
+ Poly[ 3 ].x := Poly[ 1 ].x;
+ Poly[ 3 ].y := R0.Bottom;
+ Rgn := CreatePolygonRgn( Poly[ 0 ].x, 4, ALTERNATE );
+ end;
+ gsElliptic:
+ Rgn := CreateEllipticRgnIndirect( R0 );
+ end;
+ if Rgn <> 0 then
+ begin
+ if Rgn <> NULLREGION then
+ begin
+ Windows.FillRgn( Self_.fPaintDC, Rgn, Br );
+ ExtSelectClipRgn( Self_.fPaintDC, Rgn, RGN_DIFF );
+ end;
+ DeleteObject( Rgn );
+ end;
+ DeleteObject( Br );
+ C := C2;
+ end;
+ if TMethod( Self_.EV.fOnPaint2 ).Code = @ DummyPaintClear then
+ {$IFDEF MAKE_METHOD}
+ Self_.EV.fOnPaint2 := TOnPaint( MakeMethod( nil, @ DummyObjProc ) );
+ {$ELSE}
+ TMethod( Self_.EV.fOnPaint2 ).Code := @DummyObjProc;
+ {$ENDIF}
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnPaint ) then
+ {$ENDIF}
+ Self_.EV.fOnPaint( Self_, Self_.fPaintDC );
+ if Self_.fPaintDC <> HDC( Msg.wParam ) then
+ EndPaint( Self_.fHandle, PaintStruct );
+ Self_.fPaintDC := OldPaintDC;
+ Rslt := 0;
+ Result := True;
+end;
+
+function WndProcLabelEffect( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var
+ Sz: TSize;
+ P0: TPoint;
+ CR: TRect;
+ B : Boolean;
+ CShadow: TColor;
+ Target: PCanvas;
+ Txt: KOLString;
+ //LCaption: PKOLChar;
+ OldPaintDC: HDC;
+
+ procedure doTextOut( shfx, shfy: Integer; col: TColor );
+ begin
+ SetTextColor( Target.fHandle, col );
+ {$IFDEF UNICODE_CTRLS}
+ Windows.ExtTextOutW( Target.fHandle, P0.x + shfx, P0.y + shfy,
+ ETO_CLIPPED, @CR,
+ PWideChar(Txt), Length(Txt), nil ); // KOL_ANSI
+ {$ELSE}
+ Windows.ExtTextOutA( Target.fHandle, P0.x + shfx, P0.y + shfy,
+ ETO_CLIPPED, @CR,
+ PAnsiChar(Txt), Length(Txt), nil ); // KOL_ANSI
+ {$ENDIF}
+ //GDIFlush; // for test only
+ end;
+
+var I, J, Istp : Integer;
+ PS: TPaintStruct;
+ //DoEndPaint: Boolean;
+begin
+ Result := False;
+ case Msg.message of
+ WM_SETTEXT:
+ begin
+ Self_.fCaption := PKOLChar( Msg.lParam );
+ Result := True;
+ Rslt := 1;
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ WM_PRINTCLIENT, WM_PAINT:
+ begin
+ OldPaintDC := Self_.fPaintDC;
+ Self_.fPaintDC := Msg.wParam;
+ if Self_.fPaintDC = 0 then
+ Self_.fPaintDC := BeginPaint( Self_.fHandle, PS );
+ begin
+ Target := Self_.Canvas;
+ Txt := Self_.fCaption;
+ Target.{$IFDEF UNICODE_CTRLS}WTextArea{$ELSE}TextArea{$ENDIF}( Txt, Sz, P0 );
+ if Self_.DF.fShadowDeep <> 0 then
+ begin
+ for B := False to Self_.fCtl3D_child and 1 <> 0 do
+ begin
+ Inc( Sz.cx, Abs( Self_.DF.fShadowDeep ) );
+ Inc( Sz.cy, Abs( Self_.DF.fShadowDeep ) );
+ end;
+ end;
+ CR := Self_.ClientRect;
+ case Self_.fTextAlign of
+ taCenter: P0.x := P0.x + (CR.Right - Sz.cx) div 2;
+ taRight: P0.x := P0.x + (CR.Right - Sz.cx);
+ end;
+ case Self_.fVerticalAlign of
+ vaCenter: P0.y := P0.y + (CR.Bottom - Sz.cy) div 2;
+ vaBottom: P0.y := P0.y + (CR.Bottom - Sz.cy);
+ end;
+ if Self_.DF.fShadowDeep <> 0 then
+ begin
+ if Self_.DF.fColor2 = clNone then
+ CShadow := ColorsMix(Color2RGB(Self_.fTextColor),Color2RGB(Self_.DF.fColor2))
+ else
+ CShadow := Color2RGB( Self_.DF.fColor2 );
+ if {$IFDEF USE_FLAGS} not(G2_Transparent in Self_.fFlagsG2)
+ {$ELSE} not Self_.fTransparent {$ENDIF} then
+ Target.FillRect( CR ); // GDIFlush; for test only
+ Target.RequiredState( HandleValid or FontValid or BrushValid or ChangingCanvas );
+ SetBkMode( Target.fHandle, Windows.TRANSPARENT );
+ if Self_.fCtl3D_child and 1 <> 0 then
+ begin
+ I := - Self_.DF.fShadowDeep;
+ Istp := 1;
+ if Self_.DF.fShadowDeep > 0 then Istp := -1;
+ repeat
+ J := - Self_.DF.fShadowDeep;
+ repeat
+ if not ( (I=0) and (J=0) ) then
+ begin
+ if (I * Istp < 0) and (J * Istp < 0) then
+ doTextOut( I, J, CShadow );
+ end;
+ J := J - Istp;
+ until J = Self_.DF.fShadowDeep - IStp;
+ I := I - Istp;
+ until I = Self_.DF.fShadowDeep - IStp;
+ end
+ else
+ doTextout( Self_.DF.fShadowDeep, Self_.DF.fShadowdeep, CShadow );
+ doTextout( 0, 0, Color2RGB(Self_.fTextColor) );
+ end
+ else
+ begin
+ Target.RequiredState( HandleValid or FontValid or BrushValid or ChangingCanvas );
+ SetBkMode( Target.fHandle, Windows.TRANSPARENT );
+ doTextout( 0, 0, Color2RGB(Self_.fTextColor) );
+ end;
+ end;
+ if Self_.fCanvas <> nil then
+ Self_.fCanvas.SetHandle( 0 );
+ if Msg.wParam = 0 then
+ EndPaint( Self_.fHandle, PS );
+ Self_.fPaintDC := OldPaintDC;
+ Rslt := 0;
+ Result := True; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.DoClick;
+begin
+ PP.fControlClick( @Self );
+ {$IFDEF NIL_EVENTS}
+ if Assigned( EV.fOnClick ) then
+ {$ENDIF}
+ EV.fOnClick( @Self );
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF WIN_GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.ParentForm: PControl;
+begin
+ Result := @Self;
+ if {$IFDEF USE_FLAGS} G3_IsControl in Result.fFlagsG3
+ {$ELSE} Result.fIsControl {$ENDIF} then
+ repeat
+ Result := Result.fParent;
+ until (Result = nil) or
+ {$IFDEF USE_FLAGS} not(G3_IsControl in Result.fFlagsG3)
+ {$ELSE} not Result.fIsControl {$ENDIF};
+end;
+{$ENDIF PAS_VERSION}
+{$IFDEF WIN_GDI}
+
+function TControl.FormParentForm: PControl;
+begin
+ Result := @Self;
+ while ( {$IFDEF USE_FLAGS} G3_IsControl in Result.fFlagsG3
+ {$ELSE} Result.fIsControl {$ENDIF} )
+ and not( {$IFDEF USE_FLAGS}
+ [G5_IsButton, G5_IsBitBtn] * Result.fFlagsG5 = [G5_IsBitBtn]
+ {$ELSE}
+ Result.fIsBitBtn and not Result.fIsButton
+ {$ENDIF} ) do
+ Result := Result.fParent;
+end;
+
+function TControl.MarkPanelAsForm: PControl;
+begin
+ Result := @ Self;
+ {$IFDEF USE_FLAGS}
+ Include( fFlagsG5, G5_IsBitBtn );
+ {$ELSE}
+ fIsBitBtn := TRUE;
+ {$ENDIF}
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetProgressColor(const Value: TColor);
+begin
+ if Perform( PBM_SETBARCOLOR, 0, Color2RGB(Value) ) <> 0 then
+ fTextColor := Value;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TControl.SetShadowDeep(const Value: Integer);
+begin
+ DF.fShadowDeep := Value;
+ Invalidate;
+end;
+{$ENDIF WIN_GDI}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetFont: PGraphicTool;
+begin
+ if FFont = nil then
+ begin
+ FFont := NewFont;
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ Add2AutoFree( FFont );
+ {$ENDIF}
+ FFont.fData.Color := fTextColor;
+ FFont.OnChange := FontChanged;
+ end;
+ Result := FFont;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF WIN_GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetBrush: PGraphicTool;
+begin
+ if FBrush = nil then
+ begin
+ FBrush := NewBrush;
+ FBrush.fData.Color := fColor;
+ FBrush.OnChange := BrushChanged;
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ Add2AutoFree( FBrush );
+ {$ENDIF}
+ end;
+ Result := FBrush;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF WIN_GDI}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.FontChanged(Sender: PGraphicTool);
+begin
+ fTextColor := Sender.fData.Color;
+ ApplyFont2Wnd_Proc(@Self);
+ Invalidate;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF WIN_GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.BrushChanged(Sender: PGraphicTool);
+begin
+ fColor := Sender.fData.Color;
+ if fTmpBrush <> 0 then
+ begin
+ DeleteObject( fTmpBrush );
+ fTmpBrush := 0;
+ end;
+ if fPaintDC = 0 then
+ // only if not in painting already :
+ Invalidate;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF WIN_GDI}
+
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure DoApplyFont2Wnd( _Self: PControl );
+begin
+ if _Self.fFont <> nil then
+ begin
+ if _Self.fHandle <> 0 then
+ begin
+ _Self.fTextColor := _Self.fFont.fData.Color;
+ _Self.Perform( WM_SETFONT, _Self.FFont.Handle, 1 );
+ end;
+
+ if _Self.fCanvas <> nil then
+ begin
+ _Self.fCanvas.Free;
+ _Self.fCanvas := nil;
+ end;
+
+ _Self.DoAutoSize;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+PROCEDURE DoApplyFont2Wnd( _Self: PControl );
+VAR oldfontdesc: PPangoFontDescription;
+ rcstyle: PGtkRcStyle;
+ gcolor: TGdkColor;
+ i: Integer;
+BEGIN
+ IF ( _Self.fFont <> nil ) THEN
+ BEGIN
+ gcolor := Color2GdkColor( _Self.fFont.Color );
+
+ rcstyle := gtk_widget_get_modifier_style( _Self.fHandle );
+ oldfontdesc := rcstyle.font_desc;
+ rcstyle.font_desc :=
+ pango_font_description_copy( _Self.fFont.GetPangoFontDesc );
+ gtk_widget_modify_style( _Self.fHandle, rcstyle );
+
+ IF oldfontdesc <> nil THEN
+ pango_font_description_free( oldfontdesc );
+
+ FOR i := 0 TO 4 DO
+ gtk_widget_modify_fg( _Self.fCaptionHandle, {GTK_STATE_NORMAL} i, @ gcolor );
+ END;
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF WIN_GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.ResizeParent: PControl;
+begin
+ ResizeParentBottom;
+ ResizeParentRight;
+ // Once again, to fix Windows (or my???) bug with
+ // incorrect calculating of GetClientRect after
+ // SetWindowLong( GWL_[EX}STYLE,... )
+ Result := ResizeParentBottom;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.ResizeParentBottom: PControl;
+var NewCH: Integer;
+begin
+ Result := @Self;
+ if fParent <> nil then
+ begin
+ NewCH := BoundsRect.Bottom + fParent.fMargin;
+ if {$IFDEF USE_FLAGS} G2_ChangedH in fParent.fFlagsG2
+ {$ELSE} (fParent.fChangedPosSz and $20) <> 0 {$ENDIF} then
+ if NewCH <> fParent.ClientHeight then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fParent.ClientHeight := NewCH;
+ {$IFDEF USE_FLAGS} include( fParent.fFlagsG2, G2_ChangedH );
+ {$ELSE} fParent.fChangedPosSz := fParent.fChangedPosSz or $20; {$ENDIF}
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.ResizeParentRight: PControl;
+var NewCW: Integer;
+begin
+ Result := @Self;
+ if fParent <> nil then
+ begin
+ NewCW := fBoundsRect.Right + fParent.fMargin;
+ if {$IFDEF USE_FLAGS} G2_ChangedW in fParent.fFlagsG2
+ {$ELSE} (fParent.fChangedPosSz and $10) <> 0 {$ENDIF} then
+ if NewCW < fParent.ClientWidth then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fParent.ClientWidth := NewCW;
+ {$IFDEF USE_FLAGS} include( fParent.fFlagsG2, G2_ChangedW );
+ {$ELSE} fParent.fChangedPosSz := fParent.fChangedPosSz or $10; {$ENDIF}
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetClientHeight: Integer;
+begin
+ with ClientRect do
+ Result := Bottom - Top;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetClientWidth: Integer;
+begin
+ with ClientRect do
+ Result := Right - Left;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetClientHeight(const Value: Integer);
+var Delta: Integer;
+begin
+ Delta := ClientHeight;
+ Delta := Height - Delta;
+ Height := Value + Delta;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetClientWidth(const Value: Integer);
+var Delta: Integer;
+begin
+ Delta := ClientWidth;
+ Delta := Width - Delta;
+ Width := Value + Delta;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.CenterOnParent: PControl;
+var PCR: TRect;
+begin
+ Result := @Self;
+ if (fParent = nil) or
+ {$IFDEF USE_FLAGS} not(G3_IsControl in fFlagsG3)
+ {$ELSE} not fIsControl {$ENDIF} then
+ PCR := MakeRect( 0, 0, GetSystemMetrics( SM_CXSCREEN ), GetSystemMetrics( SM_CYSCREEN ) )
+ else
+ PCR := fParent.ClientRect;
+ GetWindowHandle;
+ Left := (PCR.Right - PCR.Left - Width) div 2;
+ Top := (PCR.Bottom - PCR.Top - Height) div 2;
+end;
+{$ENDIF PAS_VERSION}
+
+function TControl.CenterOnForm( Form1: PControl ): PControl;
+var PCR, DR: TRect;
+begin
+ Result := @Self;
+ if (Form1 = nil) then
+ PCR := MakeRect( 0, 0, GetSystemMetrics( SM_CXSCREEN ), GetSystemMetrics( SM_CYSCREEN ) )
+ else
+ PCR := Form1.BoundsRect;
+ GetWindowHandle;
+ Left := PCR.Left + (PCR.Right - PCR.Left - Width) div 2;
+ Top := PCR.Top + (PCR.Bottom - PCR.Top - Height) div 2;
+ PCR := BoundsRect;
+ DR := GetDesktopRect;
+ if PCR.Right > DR.Right then
+ OffsetRect( PCR, DR.Right - PCR.Right, 0 );
+ if PCR.Bottom > DR.Bottom then
+ OffsetRect( PCR, 0, DR.Bottom - PCR.Bottom );
+ if PCR.Left < DR.Left then
+ OffsetRect( PCR, DR.Left - PCR.Left, 0 );
+ if PCR.Top < DR.Top then
+ OffsetRect( PCR, 0, DR.Top - PCR.Top );
+ BoundsRect := PCR;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetHasBorder: Boolean;
+begin
+ UpdateWndStyles;
+ Result := LongBool( fStyle.Value and (WS_BORDER or WS_THICKFRAME or WS_DLGFRAME))
+ or LongBool( fExStyle and WS_EX_CLIENTEDGE );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_noVERSION} // YS
+procedure TControl.SetHasBorder(const Value: Boolean);
+const style_mask = WS_BORDER or WS_THICKFRAME or WS_DLGFRAME or WS_CAPTION
+ or WS_MINIMIZEBOX or WS_MAXIMIZEBOX or WS_SYSMENU;
+ exstyle_mask = not (WS_EX_CONTROLPARENT or WS_EX_DLGMODALFRAME
+ or WS_EX_WINDOWEDGE or WS_EX_CLIENTEDGE);
+asm
+
+ PUSH EAX
+ PUSH EDX
+
+ CALL GetHasBorder
+ POP ECX
+ CMP AL, CL
+
+ POP EAX
+ JZ @@exit
+
+ MOV EDX, [EAX].fStyle
+ DEC CL
+ MOVZX ECX, [EAX].fIsControl
+ JNZ @@1
+
+ OR EDX, WS_THICKFRAME
+ INC ECX
+ LOOP @@set_style
+ OR EDX, style_mask
+ JMP @@set_style
+
+@@1: AND EDX, not style_mask
+ INC ECX
+ LOOP @@2
+ OR EDX, WS_POPUP
+
+@@2: PUSH EDX
+
+ MOV EDX, [EAX].fExStyle
+ AND EDX, exstyle_mask
+
+ PUSH EAX
+ CALL SetExStyle
+ POP EAX
+
+ POP EDX
+@@set_style:
+ TEST [EAX].fTabStop, 1
+ JZ @@no_tabstop
+ OR DX, WS_TABSTOP
+ JMP @@set_style_1
+@@no_tabstop:
+ AND DX, not WS_TABSTOP
+@@set_style_1:
+ CALL SetStyle
+@@exit:
+end;
+{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetHasBorder(const Value: Boolean);
+var NewStyle: DWORD;
+begin
+ if Value = GetHasBorder then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if Value then
+ begin
+ if {$IFDEF USE_FLAGS} not(G3_IsControl in fFlagsG3)
+ {$ELSE} not fIsControl {$ENDIF} then
+ Style := fStyle.Value or WS_THICKFRAME or WS_BORDER or
+ WS_DLGFRAME or WS_CAPTION or WS_MINIMIZEBOX or WS_MAXIMIZEBOX or
+ WS_SYSMENU
+ else
+ if fCtl3D_child and 1 <> 0 then
+ ExStyle := fExStyle or WS_EX_CLIENTEDGE
+ else
+ Style := fStyle.Value or WS_BORDER;
+ end
+ else
+ begin
+ NewStyle := fStyle.Value and not (WS_BORDER or WS_THICKFRAME or WS_DLGFRAME or WS_CAPTION
+ or WS_MINIMIZEBOX or WS_MAXIMIZEBOX or WS_SYSMENU);
+ if {$IFDEF USE_FLAGS} not(G3_IsControl in fFlagsG3)
+ {$ELSE} not fIsControl {$ENDIF} then
+ NewStyle := NewStyle or WS_POPUP;
+ Style := NewStyle;
+ ExStyle := fExStyle and not (WS_EX_CONTROLPARENT or WS_EX_DLGMODALFRAME
+ or WS_EX_WINDOWEDGE or WS_EX_CLIENTEDGE);
+ end;
+ {$IFDEF USE_FLAGS}
+ {$ELSE} //+MTsv DN
+ if fIsControl then
+ if fTabStop then
+ Style := fStyle.Value or WS_TABSTOP
+ else Style := fStyle.Value {xor} and not WS_TABSTOP;
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetHasCaption: Boolean;
+begin
+ UpdateWndStyles;
+ Result := not LongBool( fStyle.Value and (WS_POPUP or WS_DLGFRAME))
+ or LongBool( fStyle.Value and WS_CAPTION);
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetHasCaption(const Value: Boolean);
+begin
+ if Value = GetHasCaption then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if Value then
+ begin
+ Style := fStyle.Value and not (WS_POPUP or WS_DLGFRAME) or WS_CAPTION;
+ end
+ else
+ begin
+ if {$IFDEF USE_FLAGS} G3_IsControl in fFlagsG3
+ {$ELSE} fIsControl {$ENDIF} then
+ Style := fStyle.Value and not WS_CAPTION or WS_DLGFRAME
+ else
+ Style := fStyle.Value and not (WS_CAPTION or WS_SYSMENU) or WS_POPUP;
+ ExStyle := fExStyle or WS_EX_DLGMODALFRAME;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetCanResize: Boolean;
+begin
+ {$IFDEF USE_FLAGS}
+ Result := not(G1_PreventResize in fFlagsG1);
+ {$ELSE}
+ Result := not fPreventResize;
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+function WndProcCanResize( Sender: PControl; var M: TMsg; var Rslt: Integer ): Boolean;
+var W, H: Integer;
+ P: PMinMaxInfo;
+begin
+ if not Sender.CanResize then
+ if M.message = WM_GETMINMAXINFO then
+ begin
+ Rslt := Sender.CallDefWndProc( M );
+ {$IFDEF FIX_WIDTH_HEIGHT}
+ W := Sender.FFixWidth;
+ H := Sender.FFixHeight;
+ {$ELSE}
+ W := Sender.fBoundsRect.Right - Sender.fBoundsRect.Left;
+ H := Sender.fBoundsRect.Bottom - Sender.fBoundsRect.Top;
+ {$ENDIF}
+ P := Pointer( M.lParam );
+ P.ptMinTrackSize.x := W;
+ P.ptMinTrackSize.y := H;
+ P.ptMaxTrackSize := P.ptMinTrackSize;
+ Result := True; // stop further processing (prevent resizing)
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end
+ else
+ if M.message = WM_NCHITTEST then
+ begin
+ Rslt := Sender.CallDefWndProc( M );
+ if (Rslt >= 10) and (Rslt <= 17) then
+ begin
+ {$IFDEF CANRESIZE_THICKFRAME}
+ Rslt := HTBORDER;
+ {$ELSE}
+ Rslt := HTNOWHERE;
+ {$ENDIF}
+ Result := True;
+ exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end else
+ if M.message = WM_INITMENU then
+ begin
+ if not Sender.CanResize then
+ EnableMenuItem( GetSystemMenu( Sender.fHandle, FALSE ),
+ SC_SIZE, MF_GRAYED );
+ end;
+ Result := False; // continue message processing
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetCanResize( const Value: Boolean );
+begin
+ if Value = CanResize then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$IFDEF USE_FLAGS}
+ if Value then exclude( fFlagsG1, G1_PreventResize )
+ else include( fFlagsG1, G1_PreventResize );
+ {$ELSE}
+ fPreventResize := not Value;
+ {$ENDIF}
+ {$IFDEF CANRESIZE_THICKFRAME}
+ if Value then
+ Style := Style or WS_THICKFRAME
+ else
+ Style := Style and not WS_THICKFRAME;
+ {$ENDIF}
+ {$IFDEF FIX_WIDTH_HEIGHT}
+ GetWindowHandle;
+ FFixWidth := Width;
+ FFixHeight := Height;
+ {$ENDIF FIX_WIDTH_HEIGHT}
+ AttachProc( WndProcCanResize );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetStayOnTop: Boolean;
+begin
+ UpdateWndStyles;
+ Result := LongBool( fExStyle and WS_EX_TOPMOST);
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetStayOnTop(const Value: Boolean);
+begin
+ if Value = GetStayOnTop then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if fHandle <> 0 then
+ if Value then
+ SetWindowPos( fHandle, HWND_TOPMOST, 0,0,0,0,
+ SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE )
+ else
+ SetWindowPos( fHandle, HWND_NOTOPMOST, 0,0,0,0,
+ SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE )
+ else
+ if Value then fExStyle := fExStyle or WS_EX_TOPMOST
+ else fExStyle := fExStyle and not WS_EX_TOPMOST;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.UpdateWndStyles: PControl;
+begin
+ Result := @Self;
+ if fHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fStyle.Value := GetWindowLong( fHandle, GWL_STYLE );
+ fExStyle := GetWindowLong( fHandle, GWL_EXSTYLE );
+ fClsStyle := GetClassLong( fHandle, GCL_STYLE );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetChecked: Boolean;
+begin
+ if bboFixed in DF.fBitBtnOptions then
+ Result := {$IFDEF USE_FLAGS} G4_Checked in fFlagsG4
+ {$ELSE} fChecked {$ENDIF}
+ else
+ Result := LongBool( Perform( BM_GETCHECK, 0, 0 ) ) ; //= BST_CHECKED;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.Set_Checked(const Value: Boolean);
+begin
+ if bboFixed in DF.fBitBtnOptions then
+ begin
+ {$IFDEF USE_FLAGS} include( fFlagsG4, G4_Checked );
+ {$ELSE} fChecked := Value; {$ENDIF}
+ Invalidate;
+ end
+ else
+ Perform( BM_SETCHECK, Integer( Value ), 0 );
+end;
+{$ENDIF PAS_VERSION}
+
+function TControl.SetChecked(const Value: Boolean): PControl;
+begin
+ Perform( BM_SETCHECK, Integer( Value ), 0 );
+ Result := @Self;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+function TControl.SetRadioChecked: PControl;
+{$IFDEF USE_FLAGS}
+var WasStyle: DWORD;
+{$ELSE}
+var WasTabStop: Boolean;
+{$ENDIF}
+begin
+ {$IFDEF USE_FLAGS}
+ WasStyle := fStyle.Value;
+ exclude( fStyle.f2_Style, F2_Tabstop );
+ DoClick;
+ fStyle.Value := WasStyle;
+ {$ELSE}
+ WasTabStop := fTabStop;
+ fTabStop := FALSE;
+ DoClick;
+ fTabStop := WasTabStop;
+ {$ENDIF}
+ Result := @Self;
+end;
+{$ENDIF PAS_VERSION}
+
+function TControl.GetCheck3: TTriStateCheck;
+begin
+ Result := TTriStateCheck(Perform(BM_GETCHECK, 0, 0) and 3);
+end;
+
+procedure TControl.SetCheck3(value: TTriStateCheck);
+var
+ wp: WPARAM;
+begin
+ wp := Perform(BM_GETCHECK, 0, 0) and not 3;
+ wp := wp or byte(value);
+ Perform(BM_SETCHECK, wp, 0);
+end;
+
+procedure TControl.Click;
+begin
+ if (fCommandActions.aClick <> 0) or
+ (fCommandActions.aEnter = BN_SETFOCUS) then
+ Perform( WM_COMMAND, (fCommandActions.aClick shl 16) or fMenu,
+ GetWindowHandle )
+ else
+ begin
+ Perform( WM_LBUTTONDOWN, MK_LBUTTON, 0 );
+ Perform( WM_LBUTTONUP, MK_LBUTTON, 0 );
+ end;
+end;
+
+type
+ TCharRange = record
+ cpMin: Longint;
+ cpMax: LongInt;
+ end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetSelStart: Integer;
+begin
+ Result := 0;
+ if fCommandActions.aGetSelRange <> 0 then
+ Perform( fCommandActions.aGetSelRange, Integer( @ Result ), 0 );
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TControl.SetSelStart(const Value: Integer);
+begin
+ ItemSelected[ Value ] := True;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetSelLength: Integer;
+var Start, Finish: Integer;
+begin
+ Result := 0;
+ if fCommandActions.aGetSelCount <> 0 then
+ begin
+ if fCommandActions.aGetSelCount = EM_GETSEL then
+ begin
+ Perform( fCommandActions.aGetSelCount, Integer( @ Start ), Integer( @ Finish ) );
+ Result := Finish - Start;
+ end
+ else
+ begin
+ Result := Perform( fCommandActions.aGetSelCount {and $7FFF}, 0, 0 );
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetSelLength(const Value: Integer);
+var SR: TCharRange;
+begin
+ SR.cpMin := GetSelStart;
+ SR.cpMax := SR.cpMin + Value;
+ if Value < 0 then
+ SR.cpMax := -1;
+ if fCommandActions.aSetSelRange <> 0 then
+ Perform( fCommandActions.aSetSelRange, SR.cpMin, SR.cpMax )
+ else
+ if fCommandActions.aExSetSelRange <> 0 then
+ Perform( fCommandActions.aExSetSelRange, 0, Integer( @SR ) );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetItems(Idx: Integer): KOLString;
+var L, Pos: Integer;
+ Buf: PKOLChar;
+begin
+ Result := '';
+ Pos := Item2Pos( Idx );
+ Idx := Pos2Item( Pos );
+ if fCommandActions.aGetItemLength <> 0 then
+ L := Perform( fCommandActions.aGetItemLength, Pos, 0 )
+ else Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if L = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ GetMem( Buf, (L + 4) * SizeOf( KOLChar ) );
+ PDWORD( Buf )^ := L + 1;
+ if fCommandActions.aGetItemText <> 0 then
+ Perform( fCommandActions.aGetItemText, Idx, Integer( Buf ) );
+ Buf[ L ] := #0;
+ Result := Buf;
+ FreeMem( Buf );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetItems(Idx: Integer; const Value: KOLString);
+var Strt, L : DWORD;
+ {$IFNDEF NOT_FIX_CURINDEX}
+ TmpCurIdx: Integer; // AK - Andrzey Kubasek
+ TmpData: DWORD;
+ {$ENDIF NOT_FIX_CURINDEX}
+begin
+ if fCommandActions.aSetItemText <> 0 then
+ begin
+ Strt := Item2Pos( Idx );
+ L := Item2Pos( Idx + 1 ) - Strt;
+ SelStart := Strt;
+ SelLength := L;
+ Perform( fCommandActions.aSetItemText, 0, Integer( PKOLChar( Value ) ) );
+ end
+ else
+ if fCommandActions.aDeleteItem <> 0 then
+ begin
+ {$IFNDEF NOT_FIX_CURINDEX}
+ TmpCurIdx := CurIndex; // +AK
+ TmpData := ItemData[ Idx ];
+ {$ENDIF}
+ Delete( Idx );
+ Insert( Idx, Value );
+ {$IFNDEF NOT_FIX_CURINDEX}
+ CurIndex := TmpCurIdx; //+AK
+ ItemData[ Idx ] := TmpData;
+ {$ENDIF}
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetItemsCount: Integer;
+begin
+ Result := 0;
+ {$IFDEF DEBUG_ANY}
+ try
+ if fCommandActions.aGetCount = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := Perform( fCommandActions.aGetCount, 0, 0 );
+ except
+ asm
+ int 3
+ end;
+ end;
+ {$ELSE}
+ if fCommandActions.aGetCount = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := Perform( fCommandActions.aGetCount, 0, 0 );
+ {$ENDIF DEBUG_ANY}
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TControl.SetItemsCount(const Value: Integer);
+begin
+ if fCommandActions.aSetCount = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Perform( fCommandActions.aSetCount, Value, 0 );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.Item2Pos(ItemIdx: Integer): DWORD;
+begin
+ Result := ItemIdx;
+ if Byte( fCommandActions.bItem2Pos ) <> 0 then
+ Result := Perform( fCommandActions.bItem2Pos, ItemIdx, 0 );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.Pos2Item(Pos: Integer): DWORD;
+begin
+ Result := Pos;
+ if Byte( fCommandActions.bPos2Item ) <> 0 then
+ Result := Perform( fCommandActions.bPos2Item, Pos, 0 );
+end;
+{$ENDIF PAS_VERSION}
+
+function TControl.SavePosition: TEditPositions;
+var {$IFNDEF NOT_USE_RICHEDIT}
+ p: TPoint;
+ {$ENDIF USE_RICHEDIT}
+ i: Integer;
+begin
+ Result.SelStart := SelStart;
+ Result.SelLength := SelLength;
+ {$IFNDEF NOT_USE_RICHEDIT}
+ if {$IFDEF USE_FLAGS} (G1_CanNotDoublebuf in fFlagsG1)
+ {$ELSE} fCannotDoubleBuf {$ENDIF}
+ { TRUE for rich edit, FALSE for edit } then
+ begin
+ P.X := 0;
+ P.Y := 0;
+ i := Perform( EM_CHARFROMPOS, 0, Integer( @ P ) );
+ Result.TopLine := Pos2Item( i );
+ Result.TopColumn := i - Integer( Item2Pos( Result.TopLine ) );
+ Perform( EM_GETSCROLLPOS, 0, Integer( @ Result.ScrollPos ) );
+ end
+ else
+ {$ENDIF USE_RICHEDIT}
+ begin
+ i := 0;
+ i := Perform( EM_CHARFROMPOS, 0, i );
+ Result.TopLine := HiWord( i );
+ Result.TopColumn := LoWord( i ) - Item2Pos( Result.TopLine );
+ Result.ScrollPos.Y := GetScrollPos( Handle, SB_VERT );
+ Result.ScrollPos.X := GetScrollPos( Handle, SB_HORZ );
+ end;
+ Result.RestoreScroll := TRUE;
+end;
+
+procedure TControl.RestorePosition( const P: TEditPositions );
+var Cur: TEditPositions;
+begin
+ SelStart := P.SelStart;
+ SelLength := P.SelLength;
+ if P.RestoreScroll then
+ begin
+ Perform( EM_SCROLLCARET, 0, 0 );
+ Cur := SavePosition;
+ {$IFNDEF NOT_USE_RICHEDIT}
+ if {$IFDEF USE_FLAGS} (G1_CanNotDoublebuf in fFlagsG1)
+ {$ELSE} fCannotDoubleBuf {$ENDIF} then
+ begin // RichEdit
+ if P.TopLine <> Cur.TopLine then
+ Perform( EM_LINESCROLL, 0, P.TopLine - Cur.TopLine );
+ Perform( EM_SETSCROLLPOS, 0, Integer( @ P.ScrollPos ) );
+ end else // Edit
+ {$ENDIF USE_RICHEDIT}
+ begin
+ if (P.TopLine <> Cur.TopLine) or
+ (P.TopColumn <> Cur.TopColumn) then
+ Perform( EM_LINESCROLL, P.TopColumn - Cur.TopColumn,
+ P.TopLine - Cur.TopLine );
+ SetScrollPos( Handle, SB_VERT, P.ScrollPos.Y, TRUE );
+ SetScrollPos( Handle, SB_HORZ, P.ScrollPos.X, TRUE );
+ end;
+ end;
+end;
+
+procedure TControl.UpdatePosition( var p: TEditPositions; FromPos,
+ CountInsertDelChars, CountInsertDelLines: Integer );
+var d: Integer;
+begin
+ if (FromPos <= p.SelStart) and (CountInsertDelChars >= 0) or
+ (CountInsertDelChars < 0) and
+ ((FromPos + Abs( CountInsertDelChars ) <= p.SelStart)
+ ) then
+ begin
+ p.SelStart := p.SelStart + CountInsertDelChars;
+ end else
+ if FromPos >= p.SelStart + p.SelLength then
+ begin
+ // nothing to do
+ end else
+ if CountInsertDelChars < 0 then // deleting
+ begin
+ if FromPos - CountInsertDelChars > p.SelStart + p.SelLength then
+ CountInsertDelChars := -( p.SelStart + p.SelLength - FromPos );
+ if FromPos - CountInsertDelChars >= p.SelStart then
+ begin
+ d := FromPos - CountInsertDelChars - p.SelStart;
+ p.SelLength := p.SelLength - d;
+ //inc( CountInsertDelChars, d );
+ end;
+ inc( p.SelStart, CountInsertDelChars );
+ end else // inserting
+ begin
+ if (FromPos > p.SelStart) and (FromPos < p.SelStart + p.SelLength) then
+ inc( p.SelLength, CountInsertDelChars )
+ else
+ if FromPos <= p.SelStart then
+ inc( p.SelStart, CountInsertDelChars );
+ end;
+ p.TopLine := p.TopLine + CountInsertDelLines;
+end;
+
+function WndProcTabChar( Sender: PControl; var M: TMsg; var Rslt: Integer ): Boolean;
+begin
+ if M.message = WM_CHAR then
+ begin
+ if M.wParam = 9 then
+ Sender.ReplaceSelection( #9, TRUE );
+ end;
+ Result := FALSE;
+end;
+
+function TControl.EditTabChar: PControl;
+begin
+ AttachProc( WndProcTabChar );
+ Result := @Self;
+end;
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function TControl.Add(const S: KOLString): Integer;
+begin
+ if fCommandActions.aAddItem <> 0 then
+ begin
+ Result := Perform( fCommandActions.aAddItem, 0, Integer( PKOLChar( S ) ) );
+ if Count = 1 then
+ ItemSelected[ 0 ] := True;
+ end
+ else
+ begin
+ if Assigned( fCommandActions.aAddText ) then
+ fCommandActions.aAddText( @Self, S )
+ else
+ Text := Text + S;
+ Result := 0;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.Delete(Idx: Integer);
+begin
+ if fCommandActions.aDeleteItem <> 0 then
+ Perform( fCommandActions.aDeleteItem, Idx, 0 );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function TControl.Insert(Idx: Integer; const S: KOLString): Integer;
+begin
+ if fCommandActions.aInsertItem <> 0 then
+ Result := Perform( fCommandActions.aInsertItem, Idx, Integer( PKOLChar( S ) ) )
+ else
+ Result := -1;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetItemSelected(ItemIdx: Integer): Boolean;
+var SS: Integer;
+begin
+ if fCommandActions.aGetSelected <> 0 then
+ begin
+ SS := Perform( fCommandActions.aGetSelected, ItemIdx, LVIS_SELECTED );
+ { Though it is written in docs that for combobox lParam for CB_GETCURSEL
+ is not used and _must_ be 0, therefore this code is working for
+ combobox too. }
+ if fCommandActions.aGetSelected <> CB_GETCURSEL then
+ ItemIdx := 1;
+ Result := SS = ItemIdx;
+ end
+ else
+ begin
+ SS := SelStart;
+ Result := (ItemIdx >= SS) and (ItemIdx < SS + SelLength);
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetItemSelected(ItemIdx: Integer; const Value: Boolean);
+var SR: TCharRange;
+begin
+ if fCommandActions.aSetSelected <> 0 then
+ Perform( fCommandActions.aSetSelected, Integer( Value ), ItemIdx )
+ else
+ if fCommandActions.aSetCurrent <> 0 then
+ Perform( fCommandActions.aSetCurrent, ItemIdx, 0 )
+ else
+ if fCommandActions.aSetSelRange <> 0 then
+ Perform( fCommandActions.aSetSelRange, ItemIdx, ItemIdx )
+ else
+ if fCommandActions.aExSetSelRange <> 0 then
+ begin
+ SR.cpMin := ItemIdx;
+ SR.cpMax := ItemIdx;
+ Perform( fCommandActions.aExSetSelRange, 0, Integer( @SR ) );
+ end
+ else
+ begin // for ImageShow: set the index and invalidate the control
+ FCurIndex := ItemIdx;
+ Invalidate;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetCtl3D(const Value: Boolean);
+begin
+ fCtl3D_child := fCtl3D_child and not 1 or Integer( Value ) and 1;
+ UpdateWndStyles;
+ if Value then
+ begin
+ Style := fStyle.Value and not WS_BORDER;
+ ExStyle := fExStyle or WS_EX_CLIENTEDGE;
+ end else
+ begin
+ Style := fStyle.Value or WS_BORDER;
+ ExStyle := fExStyle and not WS_EX_CLIENTEDGE;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.Shift(dX, dY: Integer): PControl;
+begin
+ Left := fBoundsRect.Left + dX;
+ Top := fBoundsRect.Top + dY;
+ Result := @Self;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure SetKeyEvent( Self_: PControl );
+begin
+ Self_.PP.fWndProcKeybd := WndProcKeybd;
+end;
+
+procedure TControl.SetOnChar(const Value: TOnChar);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnChar := Value;
+ SetKeyEvent( @Self );
+end;
+
+{$IFDEF SUPPORT_ONDEADCHAR}
+procedure TControl.SetOnDeadChar(const Value: TOnChar);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnDeadChar := Value;
+ SetKeyEvent( @Self );
+end;
+{$ENDIF SUPPORT_ONDEADCHAR}
+
+procedure TControl.SetOnKeyDown(const Value: TOnKey);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnKeyDown := Value;
+ SetKeyEvent( @Self );
+end;
+
+procedure TControl.SetOnKeyUp(const Value: TOnKey);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnKeyUp := Value;
+ SetKeyEvent( @Self );
+end;
+
+{$IFDEF ASM_TLIST}
+function CollectTabControls( Form: PControl ): PList;
+asm
+ PUSH EDI
+ PUSH EAX
+ CALL NewList
+ XCHG EDI, EAX
+ POP EAX
+ CALL @@collecttab
+ XCHG EAX, EDI
+ POP EDI
+ RET
+@@collecttab:
+ { <- EDI = Result:PList
+ EAX = Form (or Control)
+ }
+ PUSH EBP
+ XOR EBP, EBP // Result := FALSE;
+ PUSH ESI
+ PUSH EBX
+ MOV EDX, [EAX].TControl.fChildren
+ MOV ECX, [EDX].TList.fCount
+ MOV ESI, [EDX].TList.fItems
+ JECXZ @@e_loop
+@@loo: PUSH ECX
+ LODSD
+
+ PUSH EAX
+
+ TEST byte ptr [EAX].TControl.fStyle+2, WS_TABSTOP shr 16
+ JZ @@call_recur
+
+ {$IFDEF USE_FLAGS}
+ MOV EDX, dword ptr [EAX].TControl.fStyle.f2_Style
+ OR DL, DH
+ AND DL, (1 shl F3_Disabled) or (1 shl F2_Tabstop)
+ CMP DL, (1 shl F2_Tabstop)
+ JNZ @@call_recur
+ {$ELSE}
+ MOV DL, [EAX].TControl.fTabStop
+ AND DL, [EAX].TControl.fEnabled
+ JZ @@call_recur
+ {$ENDIF}
+
+ CALL TControl.GetToBeVisible
+ TEST AL, AL
+ POP EAX
+ JZ @@next
+ PUSH EAX
+
+ XCHG EDX, EAX
+ PUSH ESI
+ MOV ECX, [EDI].TList.fCount
+ MOV ESI, [EDI].TList.fItems
+ XOR EBX, EBX
+ JECXZ @@e_loo2
+@@loo2: LODSD
+ MOV AX, [EAX].TControl.fTabOrder
+ CMP AX, [EDX].TControl.fTabOrder
+ JLE @@next2
+ POP ESI
+ MOV ECX, EDX
+ MOV EDX, EBX
+ MOV EAX, EDI
+ CALL TList.Insert
+ JMP @@call_recur
+
+@@next2: INC EBX
+ LOOP @@loo2
+@@e_loo2:
+ POP ESI
+ MOV EAX, EDI
+ CALL TList.Add
+
+@@call_recur:
+ //OR EBP, 1 // Result := TRUE;
+ INC EBP
+ POP EAX
+ {$IFDEF USE_FLAGS}
+ TEST [EAX].TControl.fStyle.f3_Style, (1 shl F3_Disabled)
+ JNZ @@next
+ {$ELSE}
+ MOVZX ECX, [EAX].TControl.fEnabled
+ JECXZ @@next
+ {$ENDIF USE_FLAGS}
+ PUSH EAX
+ CALL @@collecttab
+ POP EDX
+ JZ @@next
+
+ MOV EAX, EDI
+ CALL TList.Remove
+
+@@next: POP ECX
+ LOOP @@loo
+
+@@e_loop:
+ POP EBX
+ POP ESI
+ TEST EBP, EBP
+ POP EBP
+end;
+{$ELSE PAS_VERSION} //Pascal
+function CollectTabControls( Form: PControl ): PList;
+var R: PList;
+ function CollectTab( P: PControl ): Boolean;
+ var I, J: Integer;
+ C, D: PControl;
+ begin
+ Result := FALSE;
+ for I := 0 to P.fChildren.fCount - 1 do
+ begin
+ C := P.fChildren.{$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}[ I ];
+ if {$IFDEF USE_FLAGS} (TRUE)
+ {$ELSE} C.fTabstop {$ENDIF}
+ and {$IFDEF USE_FLAGS} not(F3_Disabled in C.fStyle.f3_Style)
+ {$ELSE} C.fEnabled {$ENDIF}
+ and C.ToBeVisible and
+ (F2_Tabstop in C.fStyle.f2_Style) then
+ begin
+ D := nil;
+ for J := 0 to R.fCount - 1 do
+ begin
+ D := R.{$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}[ J ];
+ if D.fTabOrder > C.fTabOrder then
+ begin
+ Result := TRUE;
+ R.Insert( J, C );
+ break;
+ end
+ else
+ D := nil;
+ end;
+ if D = nil then
+ begin
+ R.Add( C );
+ Result := TRUE;
+ end;
+ end;
+ if {$IFDEF USE_FLAGS} not (F3_Disabled in C.fStyle.f3_Style)
+ {$ELSE} C.fEnabled {$ENDIF} then
+ begin
+ if CollectTab( C ) then
+ R.Remove( C );
+ end;
+ end;
+ end;
+ {$IFDEF DEBUG_COLLECTTABCONTROLS}
+ var SL: PStrList;
+ i: Integer;
+ C: PControl;
+ {$ENDIF}
+begin
+ R := NewList;
+ CollectTab( Form );
+ {$IFDEF DEBUG_COLLECTTABCONTROLS}
+ SL := NewStrList;
+ for i := 0 to R.Count-1 do
+ begin
+ C := R.Items[ i ];
+ SL.Add( Int2Str( C.fTabOrder ) + ' ' + Int2Str( C.fTag ) + ' ' + C.fCaption );
+ end;
+ SL.SaveToFile( GetStartDir + 'debug_collecttabcontrols.txt' );
+ SL.Free;
+ {$ENDIF}
+
+ Result := R;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_TLIST}
+procedure Tabulate2Next( Form: PControl; Dir: Integer );
+asm
+ PUSHAD
+ PUSH EAX // save Form
+ MOV EBX, EAX
+ MOV EBP, EDX // EBP = Dir (direction <0 or >0)
+ CALL CollectTabControls
+ XCHG EDI, EAX // EDI = CL (list of controls)
+
+ MOV ECX, [EBX].TControl.DF.fCurrentControl // C := Form.fCurrentControl
+ XOR EBX, EBX // I = 0
+ JECXZ @@1
+ MOV BX, [ECX].TControl.fTabOrder // I = C.fTabOrder
+@@1:
+ MOV ECX, [EDI].TList.fCount
+ MOV ESI, [EDI].TList.fItems
+ XOR EDX, EDX
+ PUSH EDX // Ctrl1 = nil
+ PUSH EDX // Ctrl2 = nil
+ TEST ECX, ECX
+ JZ @@e_loop
+
+@@loop: PUSH ECX
+ LODSD
+ CMP [EAX].TControl.fTabOrder, BX
+ JZ @@next
+
+ MOV ECX, [ESP+8] // ECX = Ctrl1
+ JECXZ @@c1nil
+ MOV CX, [ECX].TControl.fTabOrder // ECX = Ctrl1.fTabOrder
+ TEST EBP, EBP
+ JGE @@c1ge
+
+ CMP [EAX].TControl.fTabOrder, BX
+ JGE @@2
+ CMP [EAX].TControl.fTabOrder, CX
+ JLE @@2
+
+@@c1new:
+ MOV [ESP+8], EAX // Ctrl1 := C
+ JMP @@2
+
+@@c1ge: CMP [EAX].TControl.fTabOrder, BX
+ JLE @@2
+ CMP [EAX].TControl.fTabOrder, CX
+ JL @@c1new
+ JMP @@2
+
+@@c1nil:
+ TEST EBP, EBP
+ JL @@c1nil_dirL
+ CMP [EAX].TControl.fTabOrder, BX
+ JG @@c1new
+ JMP @@2
+
+@@c1nil_dirL:
+ CMP [EAX].TControl.fTabOrder, BX
+ JL @@c1new
+
+@@2:
+ MOV ECX, [ESP+4] // ECX = Ctrl2
+ JECXZ @@c2new
+ MOV CX, [ECX].TControl.fTabOrder
+
+ TEST EBP, EBP
+ JL @@c2dirL
+ CMP [EAX].TControl.fTabOrder, CX
+ JGE @@next
+ JMP @@c2new
+
+@@c2dirL:
+ CMP [EAX].TControl.fTabOrder, CX
+ JLE @@next
+@@c2new:
+ MOV [ESP+4], EAX
+
+@@next: POP ECX
+ DEC ECX
+ JNZ @@loop
+ //LOOP @@loop
+@@e_loop:
+
+ POP EDX // Ctrl2
+ POP ECX // Ctrl1
+ INC ECX
+ LOOP @@3
+ MOV ECX, EDX
+@@3:
+ POP EBX // EBX = Form
+ JECXZ @@exit
+
+ XCHG EAX, ECX
+ {$IFDEF USE_GRAPHCTLS}
+ {$IFDEF USE_FLAGS}
+ TEST [EAX].TControl.fFlagsG6, 1 shl G6_GraphicCtl
+ JNZ @@4
+ {$ELSE}
+ CMP [EAX].TControl.fWindowed, 0
+ JZ @@4
+ {$ENDIF}
+ {$ENDIF}
+ MOV ECX, [EAX].TControl.fHandle
+ JECXZ @@no_handle
+@@4:
+ INC [EAX].TControl.fClickDisabled
+
+ PUSH EAX
+ MOV DL, 1
+ CALL TControl.SetFocused
+ POP EAX
+
+ DEC [EAX].TControl.fClickDisabled
+
+@@no_handle:
+ MOV [EBX].TControl.DF.fCurrentControl, EAX
+
+@@exit:
+ XCHG EAX, EDI
+ CALL TObj.RefDec
+ POPAD
+end;
+{$ELSE PAS_VERSION} //Pascal
+procedure Tabulate2Next( Form: PControl; Dir: Integer );
+var CL : PList;
+ I, J : Integer;
+ Ctrl1, Ctrl2, C : PControl;
+begin
+ CL := CollectTabControls( Form );
+
+ I := 0;
+ C := Form.DF.fCurrentControl;
+ if C <> nil then
+ I := C.fTabOrder;
+ Ctrl2 := nil;
+ Ctrl1 := nil;
+ for J := 0 to CL.fCount - 1 do
+ begin
+ C := CL.{$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}[ J ];
+ if C.fTabOrder = I then continue;
+ if (Ctrl1 = nil)
+ and ( (Dir >= 0) and (C.fTabOrder > I)
+ or (Dir < 0) and (C.fTabOrder < I) )
+ or (Dir >= 0)
+ and (C.fTabOrder > I) and (C.fTabOrder < Ctrl1.fTabOrder)
+ or (Dir < 0)
+ and (C.fTabOrder < I) and (C.fTabOrder > Ctrl1.fTabOrder)
+ then Ctrl1 := C;
+ if (Ctrl2 = nil)
+ or (Dir >= 0) and (C.fTabOrder < Ctrl2.fTabOrder)
+ or (Dir < 0) and (C.fTabOrder > Ctrl2.fTabOrder)
+ then Ctrl2 := C;
+ end;
+ if Ctrl1 = nil then
+ Ctrl1 := Ctrl2;
+ if Ctrl1 <> nil then
+ begin
+ if (Ctrl1.fHandle <> 0) {$IFDEF USE_GRAPHCTLS} or
+ {$IFDEF USE_FLAGS} (G6_GraphicCtl in Ctrl1.fFlagsG6)
+ {$ELSE} not Ctrl1.fWindowed {$ENDIF} {$ENDIF} then
+ begin
+ Inc( Ctrl1.fClickDisabled );
+ Ctrl1.Focused := TRUE;
+ Dec( Ctrl1.fClickDisabled );
+ end;
+ Form.DF.fCurrentControl := Ctrl1;
+ end;
+ CL.Free;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function Tabulate2Control( Self_: PControl; Key: DWORD; checkOnly: Boolean ): Boolean;
+var Form: PControl;
+begin
+ Result := False;
+ case Key of
+ VK_TAB: if not (tkTab in Self_.fLookTabKeys) then exit; {>>>>>>>>>>>>>>>>>>>>}
+ VK_LEFT, VK_RIGHT: if not (tkLeftRight in Self_.fLookTabKeys) then exit; {>>>}
+ VK_UP, VK_DOWN: if not (tkUpDown in Self_.fLookTabKeys) then exit; {>>>>>>>>>}
+ VK_NEXT, VK_PRIOR: if not (tkPageUpPageDn in Self_.fLookTabKeys) then
+ exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ else Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Result := True;
+ if checkOnly then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Form := Self_.ParentForm;
+ case Key of
+ VK_TAB:
+ if GetKeyState( VK_SHIFT ) < 0 then
+ Tabulate2Next( Form, -1 )
+ else
+ Tabulate2Next( Form, 1 );
+ VK_RIGHT, VK_DOWN, VK_NEXT: Tabulate2Next( Form, 1 );
+ VK_LEFT, VK_UP, VK_PRIOR: Tabulate2Next( Form, -1 );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_TLIST}
+function Tabulate2ControlEx( Self_: PControl; Key: DWORD; checkOnly: Boolean ): Boolean;
+asm
+ PUSH EDI
+ MOVZX EDI, CL
+ TEST byte ptr [EAX].TControl.fLookTabKeys, 1
+ JZ @@1
+@@0:
+ MOV ECX, EDX
+ AND CL, 7Fh
+ CMP CL, VK_TAB
+ JNE @@1
+
+ PUSH EDX
+ CALL TControl.ParentForm
+ POP EDX
+ MOVSX EDX, DL
+ TEST EDX, EDX
+ JS @@tab
+
+ PUSH EAX
+
+ PUSH VK_SHIFT
+ CALL GetAsyncKeyState
+ SAR EAX, 31
+ {$IFDEF PARANOIA} DB $0C, $01 {$ELSE} OR AL, 1 {$ENDIF}
+ MOV EDX, EAX
+
+ POP EAX
+@@tab:
+ TEST EDI, EDI
+ POP EDI
+ JNZ @@no_tab
+ CALL Tabulate2Next
+@@no_tab:
+ MOV AL, 1
+ RET
+
+@@data: DB VK_LEFT, VK_LEFT
+ DD offset[@@left]
+ DB VK_UP, 2
+ DB VK_RIGHT, VK_RIGHT
+ DD offset[@@right]
+ DB VK_DOWN, 2
+ DB VK_UP, VK_PRIOR
+ DD offset[@@up]
+ DB VK_TAB or 80h, $C
+ DB VK_DOWN, VK_NEXT
+ DD offset[@@down]
+ DB VK_TAB, $C
+
+@@1:
+ // EAX <- Self_:PControl
+ // DL <- Key
+ PUSH ESI
+ MOV ESI, offset[@@data]-6
+ MOV DH, 9
+ PUSH EAX
+@@loop:
+ ADD DH, DH
+ JNB @@l1
+ JMP @@abort
+@@fault1:
+ POP EDI
+ POPAD
+ PUSH EAX
+@@abort:
+ POP EAX
+@@abort1:
+ POP ESI
+ POP EDI
+ XOR EAX, EAX
+ RET
+
+@@right:
+ MOV EAX, [ESP].TRect.Left
+ SUB EAX, [ESP+16].TRect.Left
+@@left_right:
+ JL @@next1
+ MOV EDX, [ESP].TRect.Bottom
+ SUB EDX, [ESP+16].TRect.Top
+ JL @@next1
+ MOV EDX, [ESP].TRect.Top
+ SUB EDX, [ESP+16].TRect.Bottom
+ JGE @@next1
+@@chk_dist:
+ CMP EAX, EDI
+ JA @@next1
+ MOV EDI, EAX
+ MOV EAX, [EBX+ECX*4-4]
+ MOV [ESP+36], EAX // Found = Ctrl
+ JMP @@next1
+
+@@l1:
+ LODSD
+ LODSW
+ LODSW
+ CMP AL, DL
+ JE @@2
+ CMP AH, DL
+ JNE @@loop
+
+@@2:
+ PUSH ESI
+ LODSD
+ LODSW
+ POP ESI
+ XCHG EDX, EAX
+ POP EAX
+ TEST [EAX].TControl.fLookTabKeys, DH
+ JZ @@abort1
+
+ PUSHAD
+ PUSH EDI
+ CALL TControl.ParentForm
+ MOV ECX, [EAX].TControl.DF.fCurrentControl
+ JECXZ @@fault1
+ MOV EBP, ECX // EBP = CurCtrl
+
+ PUSH EAX // save Form
+ MOV EBX, EAX
+ CALL CollectTabControls
+ PUSH 0 // save Found = nil
+ PUSH EAX // save CollectedList
+ MOV EDI, EAX
+
+ MOV EBX, [EDI].TList.fItems
+ ADD ESP, -16
+ PUSH ESP
+ PUSH [EBP].TControl.fHandle
+ CALL GetWindowRect
+
+ MOV ECX, [EDI].TList.fCount
+ OR EDI, -1 // EDI = minDist
+@@loop1:
+ MOV EAX, [EBX+ECX*4-4]
+ CMP EAX, EBP
+ JE @@next
+ {}
+ {$IFDEF USE_FLAGS}
+ MOV DX, word ptr [EAX].TControl.fStyle.f2_Style
+ AND DX, ($100 shl F3_Disabled) or (1 shl F2_Tabstop)
+ XOR DH, (1 shl F3_Disabled)
+ {$ELSE}
+ MOV DL, [EAX].TControl.fEnabled
+ AND DL, [EAX].TControl.fTabstop
+ {$ENDIF USE_FLAGS}
+ JZ @@next
+ {}
+ ADD ESP, -16
+ MOV EDX, ESP
+ PUSH ECX
+ PUSH EDX
+ PUSH [EAX].TControl.fHandle
+ CALL GetWindowRect
+ POP ECX
+ JMP dword ptr [ESI]
+
+@@left:
+ MOV EAX, [ESP+16].TRect.Left
+ SUB EAX, [ESP].TRect.Left
+ JMP @@left_right
+
+@@not_found:
+ POP EDI
+ POPAD
+ MOV DL, [ESI+4]
+ POP ESI
+ JMP @@0
+
+@@up:
+ MOV EAX, [ESP+16].TRect.Top
+ SUB EAX, [ESP].TRect.Top
+ JMP @@up_down
+@@down:
+ MOV EAX, [ESP].TRect.Top
+ SUB EAX, [ESP+16].TRect.Top
+@@up_down:
+ JL @@next1
+ MOV EDX, [ESP].TRect.Right
+ SUB EDX, [ESP+16].TRect.Left
+ JL @@next1
+ MOV EDX, [ESP].TRect.Left
+ SUB EDX, [ESP+16].TRect.Right
+ JL @@chk_dist
+
+@@next1:
+ ADD ESP, 16
+@@next:
+ LOOP @@loop1
+ ADD ESP, 16
+ POP EAX // pop CollectedList
+ CALL TObj.RefDec
+ POP ECX // pop Found
+ POP EAX // pop Form
+ JECXZ @@not_found
+
+ POP EDI
+ TEST EDI, EDI
+ JNZ @@no_go
+
+ MOV [EAX].TControl.DF.fCurrentControl, ECX
+ INC [ECX].TControl.fClickDisabled
+ PUSH ECX
+ MOV ECX, [ECX].TControl.fHandle
+ JECXZ @@4
+ PUSH ECX
+ CALL Windows.SetFocus
+@@4: POP ECX
+ DEC [ECX].TControl.fClickDisabled
+@@no_go:
+ POPAD
+ POP ESI
+ POP EDI
+ MOV AL, 1 // Result = True
+end;
+{$ELSE PAS_VERSION} //Pascal
+function Tabulate2ControlEx( Self_: PControl; Key: DWORD; checkOnly: Boolean ): Boolean;
+label search_tabcontrol;
+var Form: PControl;
+ CL : PList;
+ I : Integer;
+ CurCtrl, Ctrl, Found : PControl;
+ MinDist, Dist: Integer;
+ R, R1 : TRect;
+begin
+ Result := False;
+ case Key of
+ VK_TAB: if not (tkTab in Self_.fLookTabKeys) then exit; {>>>>>>>>>>>>>>>>>>>>}
+ VK_LEFT, VK_RIGHT: if not (tkLeftRight in Self_.fLookTabKeys) then exit; {>>>}
+ VK_UP, VK_DOWN: if not (tkUpDown in Self_.fLookTabKeys) then exit; {>>>>>>>>>}
+ VK_NEXT, VK_PRIOR: if not (tkPageUpPageDn in Self_.fLookTabKeys) then
+ exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ else exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Result := True;
+ if checkOnly then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Form := Self_.ParentForm;
+ if Key = VK_TAB then
+ if GetKeyState( VK_SHIFT ) < 0 then
+ Tabulate2Next( Form, -1 )
+ else
+ Tabulate2Next( Form, 1 )
+ else
+ begin
+ CL := CollectTabControls( Form );
+ I := CL.IndexOf( Form.DF.fCurrentControl );
+ Found := nil;
+ if I >= 0 then
+ begin
+ CurCtrl := CL.{$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}[ I ];
+ GetWindowRect( CurCtrl.Handle, R );
+ search_tabcontrol:
+ MinDist := MaxInt;
+ for I := CL.fCount - 1 downto 0 do
+ begin
+ Ctrl := CL.{$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}[ I ];
+ if Ctrl = CurCtrl then continue;
+ if not ({$IFDEF USE_FLAGS} not(F3_Disabled in Ctrl.fStyle.f3_Style)
+ {$ELSE} Ctrl.fEnabled {$ENDIF}
+ and
+ {$IFDEF USE_FLAGS} (F2_Tabstop in Ctrl.fStyle.f2_Style)
+ {$ELSE} Ctrl.fTabstop {$ENDIF}
+ ) then continue;
+ GetWindowRect( Ctrl.Handle, R1 );
+ Dist := MaxInt;
+ case Key of
+ VK_LEFT:
+ begin
+ if (R1.Bottom < R.Top)
+ or (R1.Top >= R.Bottom)
+ or (R1.Left > R.Left) then continue;
+ Dist := R.Left - R1.Left;
+ end;
+ VK_RIGHT:
+ begin
+ if (R1.Bottom < R.Top)
+ or (R1.Top >= R.Bottom)
+ or (R1.Left < R.Left) then continue;
+ Dist := R1.Left - R.Left;
+ end;
+ VK_UP, VK_PRIOR:
+ begin
+ if (R1.Right < R.Left)
+ or (R1.Left >= R.Right)
+ or (R1.Top > R.Top) then continue;
+ Dist := R.Top - R1.Top;
+ end;
+ VK_DOWN, VK_NEXT:
+ begin
+ if (R1.Right < R.Left)
+ or (R1.Left >= R.Right)
+ or (R1.Top < R.Bottom) then continue;
+ Dist := R1.Top - R.Top;
+ end;
+ end;
+ if Dist < MinDist then
+ begin
+ Found := Ctrl;
+ MinDist := Dist;
+ end;
+ end;
+ if Found = nil then
+ begin
+ case Key of
+ VK_LEFT:
+ begin
+ Key := VK_UP; goto search_tabcontrol;
+ end;
+ VK_RIGHT:
+ begin
+ Key := VK_DOWN; goto search_tabcontrol;
+ end;
+ VK_UP, VK_PRIOR:
+ Tabulate2Next( Form, -1 );
+ VK_DOWN, VK_NEXT:
+ Tabulate2Next( Form, 1 );
+ end;
+ end
+ else
+ begin
+ if Found.fHandle <> 0 then
+ begin
+ Inc( Found.fClickDisabled );
+ SetFocus( Found.fHandle );
+ Dec( Found.fClickDisabled );
+ end;
+ Form.DF.fCurrentControl := Found;
+ end;
+ end;
+ CL.Free;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.Tabulate: PControl;
+var F : PControl;
+begin
+ Result := @Self;
+ F := ParentForm;
+ if F = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ F.PP.fGotoControl := Tabulate2Control;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.TabulateEx: PControl;
+var F : PControl;
+begin
+ Result := @Self;
+ F := ParentForm;
+ if F = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ F.PP.fGotoControl := Tabulate2ControlEx;
+end;
+{$ENDIF PAS_VERSION}
+
+function WndProcMouseTransparent( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+begin
+ Result := FALSE;
+ if Msg.message = WM_NCHITTEST then
+ begin
+ Rslt := HTTRANSPARENT;
+ Result := TRUE;
+ end;
+end;
+
+function TControl.MouseTransparent: PControl;
+begin
+ AttachProc( WndProcMouseTransparent );
+ Result := @ Self;
+end;
+
+procedure TControl.GotoControl(Key: DWORD);
+var Form: PControl;
+begin
+ Form := ParentForm;
+ if Form <> nil then
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Form.PP.fGotoControl ) then
+ {$ENDIF}
+ Form.PP.fGotoControl( Form.DF.fCurrentControl, Key, false );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetCurIndex: Integer;
+var I, J: Integer;
+begin
+ Result := fCurIndex;
+ if fCommandActions.aGetCurrent = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ I := 0;
+ if fCommandActions.aGetCurrent = EM_LINEINDEX then
+ Dec( I );
+ J := 0;
+ if fCommandActions.aGetCurrent = LVM_GETNEXTITEM then
+ begin
+ J := 2 {LVNI_SELECTED};
+ Dec( I );
+ end;
+ Result := Perform( fCommandActions.aGetCurrent, I, J );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetCurIndex(const Value: Integer);
+var NMHdr: TNMHdr; idx: Integer;
+begin
+ if fCommandActions.aSetCurrent <> 0 then
+ begin
+ idx := Perform( fCommandActions.aSetCurrent, Value, 0 ); // fix AV
+ if fCommandActions.aSetCurrent = TCM_SETCURSEL then
+ begin
+ fCurIndex := idx; // fix AV
+ NMHdr.code := TCN_SELCHANGE;
+ NMHdr.hwndFrom := fHandle;
+ Perform( WM_NOTIFY, 0, Integer( @NMHdr ) );
+ end;
+ end
+ else
+ ItemSelected[ Value ] := True;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF WIN_GDI}
+
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetTextAlign: TTextAlign;
+begin
+ UpdateWndStyles;
+ if (fStyle.Value and fCommandActions.aTextAlignRight) = fCommandActions.aTextAlignRight then
+ Result := taRight
+ else
+ if (fStyle.Value and fCommandActions.aTextAlignCenter) = fCommandActions.aTextAlignCenter then
+ Result := taCenter
+ else
+ Result := fTextAlign;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+FUNCTION TControl.GetTextAlign: TTextAlign;
+BEGIN
+ Result := fTextAlign;
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetTextAlign(const Value: TTextAlign);
+var NewStyle: DWORD;
+begin
+ fTextAlign := Value;
+ NewStyle := 0;
+ with fCommandActions{$IFDEF COMMANDACTIONS_OBJ}^{$ENDIF} do
+ case Value of
+ taLeft: NewStyle := fStyle.Value and not DWORD(aTextAlignCenter or aTextAlignRight)
+ or aTextAlignLeft;
+ taRight: NewStyle := fStyle.Value and not DWORD(aTextAlignLeft or aTextAlignCenter)
+ or aTextAlignRight;
+ taCenter: NewStyle := fStyle.Value and not DWORD(aTextAlignLeft or aTextAlignRight)
+ or aTextAlignCenter;
+ end;
+ NewStyle := NewStyle and not DWORD(fCommandActions.bTextAlignMask);
+ Style := NewStyle;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+PROCEDURE TControl.SetTextAlign(const Value: TTextAlign);
+BEGIN
+ IF fTextAlign = Value THEN Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fTextAlign := Value;
+ IF Assigned( fSetTextAlign ) THEN
+ fSetTextAlign( @ Self );
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetVerticalAlign: TVerticalAlign;
+begin
+ UpdateWndStyles;
+ if (fStyle.Value and (Byte( fCommandActions.bVertAlignCenter ) shl 8))
+ = (Byte( fCommandActions.bVertAlignCenter ) shl 8) then
+ Result := vaCenter
+ else
+ if (fStyle.Value and (fCommandActions.bVertAlignBottom shl 8))
+ = (fCommandActions.bVertAlignBottom shl 8) then
+ Result := vaBottom
+ else
+ Result := fVerticalAlign;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+FUNCTION TControl.GetVerticalAlign: TVerticalAlign;
+BEGIN
+ Result := fVerticalAlign;
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetVerticalAlign(const Value: TVerticalAlign);
+var NewStyle: DWORD;
+begin
+ fVerticalAlign := Value;
+ with fCommandActions{$IFDEF COMMANDACTIONS_OBJ}^{$ENDIF} do
+ begin
+ NewStyle := fStyle.Value and
+ not DWORD((bVertAlignTop or bVertAlignCenter or bVertAlignBottom) shl 8);
+ case Value of
+ vaCenter: NewStyle := NewStyle or (bVertAlignCenter shl 8);
+ vaTop: NewStyle := NewStyle or (bVertAlignTop shl 8);
+ vaBottom: NewStyle := NewStyle or (bVertAlignBottom shl 8);
+ end;
+ end;
+ Style := NewStyle;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+PROCEDURE TControl.SetVerticalAlign(const Value: TVerticalAlign);
+BEGIN
+ if fVerticalAlign = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fVerticalAlign := Value;
+ if Assigned( fSetTextAlign ) then
+ fSetTextAlign( @ Self );
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+{$IFDEF WIN_GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.Dc2Canvas( Sender: PCanvas ): HDC;
+begin
+ if fPaintDC <> 0 then
+ begin
+ Result := fPaintDC;
+ Sender.SetHandle( Result );
+ Sender.fIsPaintDC := True;
+ end
+ else
+ begin
+ if Sender.fHandle <> 0 then
+ Result := Sender.fHandle
+ else
+ Result := GetDC( GetWindowHandle );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF WIN_GDI}
+
+{$IFDEF GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetCanvas: PCanvas;
+begin
+ {$IFDEF SAFE_CODE}
+ CreateWindow;
+ {$ENDIF}
+ if ( fCanvas = nil ) then
+ begin
+ fCanvas := NewCanvas( 0 );
+ fCanvas.fOnGetHandle := Dc2Canvas;
+ fCanvas.fOwnerControl := @Self;
+ if ( fFont <> nil ) then
+ fCanvas.fFont := fCanvas.fFont.Assign( fFont );
+ if ( fBrush <> nil ) then
+ fCanvas.fBrush := fCanvas.fBrush.Assign( fBrush );
+ end;
+ Result := fCanvas;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+FUNCTION TControl.ProvideCanvasHandle( Sender: PCanvas ): HDC;
+TYPE PPGdkGC = ^PGdkGC;
+VAR Array_gc: PPGdkGC;
+BEGIN
+ IF fInBkPaint THEN Array_gc := @ fEventboxHandle.style.bg_gc[ 0 ]
+ ELSE Array_gc := @ fEventboxHandle.style.fg_gc[ 0 ];
+ CASE fEventboxHandle.state OF
+ GTK_STATE_NORMAL,
+ GTK_STATE_ACTIVE,
+ GTK_STATE_PRELIGHT,
+ GTK_STATE_SELECTED,
+ GTK_STATE_INSENSITIVE: Result := PPGdkGC( Integer( Array_gc ) + fEventboxHandle.state * sizeof( Pointer ) )^;
+ ELSE Result := Array_gc^;
+ END;
+END;
+
+FUNCTION TControl.GetCanvas: PCanvas;
+BEGIN
+ {$IFDEF SAFE_CODE}
+ CreateWindow;
+ {$ENDIF}
+ IF ( fCanvas = nil ) then
+ BEGIN
+ fCanvas := NewCanvas( nil );
+ fCanvas.fOnGetHandle := ProvideCanvasHandle;
+ fCanvas.fOwnerControl := @Self;
+ fCanvas.fDrawable := Pointer( fEventboxHandle.window );
+ END;
+ fCanvas.GetHandle; // ïîëó÷èì çäåñü òîò êîíòåêñò, êîòîðûé ñîîòâåòñòâóåò
+ // òåêóùåìó ñîñòîÿíèþ êîíòðîëà (åñëè ýòî êîíòðîë) è òåêóùåé
+ // ñòàäèè ðèñîâàíèÿ
+ Result := fCanvas;
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+{$IFDEF WIN_GDI}
+
+function TControl.DblBufTopParent: PControl;
+var Ctl: PControl;
+begin
+ Result := nil;
+ Ctl := @ Self;
+ while Ctl <> nil do
+ begin
+ if {$IFDEF USE_FLAGS} ( [G2_DoubleBuffered, G2_Transparent] * Ctl.fFlagsG2 <> [] )
+ {$ELSE} (Ctl.fDoubleBuffered) or (Ctl.fTransparent) {$ENDIF} then
+ Result := Ctl;
+ Ctl := Ctl.fParent;
+ end;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure TControl.SetDoubleBuffered(const Value: Boolean);
+begin
+ if {$IFDEF USE_FLAGS} (G1_CanNotDoublebuf in fFlagsG1)
+ {$ELSE} CannotDoubleBuf {$ENDIF} then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$IFDEF USE_FLAGS}
+ if Value then
+ include( fFlagsG2, G2_DoubleBuffered )
+ else exclude( fFlagsG2, G2_DoubleBuffered );
+ {$ELSE} fDoubleBuffered := Value; {$ENDIF}
+ AttachProc(WndProcTransparent);
+ {$IFNDEF SMALLEST_CODE}
+ Global_AttachProcExtension := @TransparentAttachProcExtension;
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetTransparent(const Value: Boolean);
+begin
+ if fParent = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$IFDEF USE_FLAGS}
+ if Value then
+ include( fFlagsG2, G2_Transparent )
+ else exclude( fFlagsG2, G2_Transparent );
+ {$ELSE} fTransparent := Value; {$ENDIF}
+
+{$IFDEF GRAPHCTL_XPSTYLES}
+ if not AppTheming then
+ begin
+ {$IFDEF USE_FLAGS}
+ if Value then
+ include( fFlagsG3, G3_ClassicTransparent )
+ else exclude( fFlagsG3, G3_ClassicTransparent );
+ {$ELSE} fClassicTransparent := Value; {$ENDIF}
+ end;
+{$ENDIF}
+
+ if Value then begin
+ AttachProc(WndProcTransparent);
+ fParent.DoubleBuffered := TRUE;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+function TControl.SetBorder( Value: Integer ): PControl;
+begin
+ fMargin := Value;
+ Result := @ Self;
+end;
+
+{ TTrayIcon }
+
+var FTrayItems: PList;
+
+{$IFDEF ASM_noVERSION} // ASM_TLIST!
+function WndProcTray( Control: PControl; var Msg: TMsg; var Rslt: Integer ) : Boolean;
+asm
+ PUSH ECX
+ MOV ECX, [EDX].TMsg.message
+ CMP CX, CM_TRAYICON
+ JNE @@1
+
+ MOV ECX, [EDX].TMsg.lParam
+ MOV EDX, [EDX].TMsg.wParam
+ MOV EAX, [EDX].TTrayIcon.fOnMouse.TMethod.Data
+ CMP word ptr [EDX].TTrayIcon.fOnMouse.TMethod.Code+2, 0
+ JE @@no_on
+
+ CALL [EDX].TTrayIcon.fOnMouse.TMethod.Code
+@@no_on:
+ POP ECX
+ XOR EAX, EAX
+ MOV [ECX], EAX
+ INC EAX
+ RET
+
+@@1:
+ SUB ECX, WM_CLOSE
+ JNE @@exit_0
+@@2:
+
+ POP ECX
+ PUSH EBX
+ XCHG EBX, EAX
+
+ MOV EAX, [EBX].TControl.fHandle
+ CMP EAX, [EDX].TMsg.hwnd
+ JNE @@otherwin
+
+ MOV EDX, [FTrayItems]
+ MOV ECX, [EDX].TList.fCount
+ MOV EDX, [EDX].TList.fItems
+@@loop:
+ MOV EAX, [EDX + ECX*4 - 4]
+ CMP [EAX].TTray.FNoAutoDeactivate, 0
+ JNZ @@3
+ CMP [EAX].TTrayIcon.fControl, EBX
+ JNE @@3
+ PUSHAD
+ XOR EDX, EDX
+ CALL TTrayIcon.SetActive
+ POPAD
+@@3: LOOP @@loop
+
+@@otherwin:
+ POP EBX
+ PUSH ECX
+@@exit_0:
+ XOR EAX, EAX
+ POP ECX
+end;
+{$ELSE PAS_VERSION} //Pascal
+function WndProcTray( Control: PControl; var Msg: TMsg; var Rslt: Integer ) : Boolean;
+var Self_: PTrayIcon;
+ I : Integer;
+begin
+ Result := False;
+ case Msg.message of
+ CM_TRAYICON:
+ begin
+ Self_ := Pointer( Msg.wParam );
+ if Assigned( Self_.FOnMouse ) then
+ Self_.FOnMouse( @Self_, Msg.lParam );
+ Rslt := 0;
+ Result := True;
+ end;
+ WM_CLOSE:
+ if Msg.hwnd = Control.fHandle then
+ begin
+ if FTrayItems <> nil then // ?????????????????
+ for I := FTrayItems.Count - 1 downto 0 do
+ begin
+ Self_ := FTrayItems.Items[ I ];
+ if not Self_.FNoAutoDeactivate then
+ if Self_.FControl = Control then
+ Self_.Active := False;
+ end;
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+function WndProcTrayIconWnd( Wnd: HWnd; Msg: DWORD; wParam, lParam: Integer ): Integer;
+ stdcall;
+var PrevProc: function ( Wnd: HWnd; Msg: DWORD;
+ wParam, lParam: Integer ): Integer; stdcall;
+var Tr: PTrayIcon;
+begin
+ PrevProc := Pointer( GetProp( Wnd, 'TRAYSAVEPROC' ) );
+ if Msg = CM_TRAYICON then
+ begin
+ Tr := Pointer( wParam );
+ if Assigned( Tr.FOnMouse ) then
+ Tr.FOnMouse( Tr, lParam );
+ Result := 0; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end
+ else
+ if Msg = WM_CLOSE then
+ begin
+ if Assigned( PrevProc ) then
+ begin
+ SetWindowLong( Wnd, GWL_WNDPROC, Integer( @ PrevProc ) );
+ RemoveProp( Wnd, 'TRAYSAVEPROC' );
+ PostMessage( Wnd, WM_CLOSE, wParam, lParam );
+ Result := 0; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ if (Wnd <> 0) and IsWindow( Wnd ) and Assigned( PrevProc ) then
+ Result := PrevProc( Wnd, Msg, wParam, lParam )
+ else Result := DefWindowProc( Wnd, Msg, wParam, lParam );
+end;
+
+procedure TTrayIcon.AttachProc2Wnd;
+begin
+ if FWnd = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if GetProp( FWnd, 'TRAYSAVEPROC' ) <> 0 then Exit; // already attached {>>>>>}
+ SetProp( FWnd, 'TRAYSAVEPROC', GetWindowLong( FWnd, GWL_WNDPROC ) );
+ SetWindowLong( FWnd, GWL_WNDPROC, Integer( @ WndProcTrayIconWnd ) );
+end;
+// [END TTrayIcon.AttachProc2Wnd]
+
+// [PROCEDURE TTrayIcon.DetachProc2Wnd]
+procedure TTrayIcon.DetachProc2Wnd;
+var OldProc: function ( Wnd: HWnd; Msg: DWORD;
+ wParam, lParam: Integer ): Integer; stdcall;
+begin
+ if FWnd = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ OldProc := Pointer( GetProp( FWnd, 'TRAYSAVEPROC' ) );
+ if not Assigned( OldProc ) then Exit; // not attached {>>>>>>>>>>>>>>>>>>>>}
+ SetWindowLong( FWnd, GWL_WNDPROC, Integer( @ OldProc ) );
+ RemoveProp( FWnd, 'TRAYSAVEPROC' );
+end;
+// [END TTrayIcon.DetachProc2Wnd]
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewTrayIcon( Wnd: PControl; Icon: HIcon ): PTrayIcon;
+begin
+ if FTrayItems = nil then
+ FTrayItems := NewList;
+ New( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TTrayIcon';
+ {$ENDIF}
+ FTrayItems.Add( Result );
+ if Wnd <> nil then
+ Wnd.AttachProc( WndProcTray );
+ Result.FControl := Wnd;
+ Result.FIcon := Icon;
+ Result.Active := True;
+end;
+{$ENDIF PAS_VERSION}
+
+var fRecreateMsg: DWORD;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function WndProcRecreateTrayIcons( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var I: Integer;
+ TI: PTrayIcon;
+begin
+ if Msg.message = fRecreateMsg then
+ begin
+ for I := 0 to FTrayItems.fCount - 1 do
+ begin
+ TI := FTrayItems.Items[ I ];
+ if TI.fAutoRecreate then
+ if TI.fActive then
+ begin
+ TI.fActive := False;
+ TI.Active := True;
+ end;
+ end;
+ end;
+ Result := False;
+end;
+{$ENDIF PAS_VERSION}
+
+const
+ TaskbarCreatedMsg: array[ 0..14 ] of KOLChar = ('T','a','s','k','b','a','r',
+ 'C','r','e','a','t','e','d',#0);
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TTrayIcon.SetAutoRecreate(const Value: Boolean);
+begin
+ fAutoRecreate := Value;
+ FControl.ParentForm.AttachProc( WndProcRecreateTrayIcons );
+ fRecreateMsg := RegisterWindowMessage( TaskbarCreatedMsg );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+destructor TTrayIcon.Destroy;
+begin
+ Active := False;
+
+ if fIcon <> 0 then
+ DestroyIcon( fIcon );
+
+ FTrayItems.Remove( @ Self );
+ if FTrayItems.Count = 0 then
+ Free_And_Nil( FTrayItems );
+ FTooltip := '';
+ inherited;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TTrayIcon.SetActive(const Value: Boolean);
+begin
+ if FActive = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if FIcon = 0 then Exit;
+ if (Wnd = 0) and ((FControl = nil) or (FControl.GetWindowHandle = 0)) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ FActive := Value;
+ if Value then
+ SetTrayIcon( NIM_ADD )
+ else
+ SetTrayIcon( NIM_DELETE );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TTrayIcon.SetIcon(const Value: HIcon);
+var Cmd : DWORD;
+begin
+ if FIcon = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ // Previous icon is not destroying. This is normal for icons, loaded from
+ // resources using LoadIcon. For icons, created using CreateIconIndirect, You
+ // have to call DestroyIcon manually.
+ Cmd := NIM_MODIFY;
+ if FIcon = 0 then
+ Cmd := NIM_ADD;
+ FIcon := Value;
+ if FActive then
+ SetTrayIcon( Cmd );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+procedure TTrayIcon.SetTooltip(const Value: KOLString);
+begin
+ if FTooltip = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ FTooltip := Value;
+ if Active then
+ SetTrayIcon( NIM_MODIFY );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+procedure TTrayIcon.SetTrayIcon(const Value: DWORD);
+var NID : {$IFDEF UNICODE_CTRLS} TNotifyIconDataW {$ELSE} TNotifyIconData {$ENDIF};
+ L : Integer;
+ V : DWORD;
+begin
+ V := Value;
+ if AppletTerminated then
+ V := NIM_DELETE;
+ if Wnd <> 0 then
+ NID.Wnd := Wnd
+ else
+ NID.Wnd := FControl.fHandle;
+
+ NID.cbSize := Sizeof( NID );
+ NID.uID := DWORD( @Self );
+ NID.uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;
+ if V = NIM_DELETE then
+ NID.uFlags := 0;
+ NID.uCallbackMessage := CM_TRAYICON;
+ NID.hIcon := FIcon;
+ L := Length( FToolTip );
+ if L > 63 then L := 63;
+ Move( FTooltip[1], NID.szTip[0], Min( 63, L )*SizeOf(KOLChar) );
+ NID.szTip[ L ] := #0;
+
+ Shell_NotifyIcon( V, @NID );
+end;
+{$ENDIF PAS_VERSION}
+
+{ -- JustOne -- }
+
+var JustOneMutex: THandle;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function WndProcJustOne( Control: PControl; var Msg: TMsg; var Rslt: Integer ) : Boolean;
+begin
+ Result := False;
+ case Msg.message of
+ WM_CLOSE, WM_NCDESTROY:
+ if LongBool( JustOneMutex ) and (Control.Handle = Msg.hwnd) then
+ begin
+ CloseHandle( JustOneMutex );
+ JustOneMutex := 0;
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_noUNICODE}
+function JustOne( Wnd: PControl; const Identifier : KOLString ) : Boolean;
+asm
+ PUSH EBX
+ PUSH ESI
+ XOR ESI, ESI
+ PUSH EDI
+ XCHG EBX, EAX
+
+ CALL EDX2PChar
+ PUSH EDX
+
+ PUSH 0
+ PUSH 1
+ PUSH ESI
+ MOV EDI, offset[CreateMutex]
+ CALL EDI
+
+ POP EDX
+ TEST EAX, EAX
+ JZ @@exit //
+ PUSH EAX
+ PUSH EAX
+
+ PUSH EDX
+ PUSH ESI
+ PUSH ESI
+ CALL EDI
+ MOV [JustOneMutex], EAX
+ TEST EAX, EAX
+ JE @@1 //
+
+ PUSH ESI
+ PUSH EAX
+ CALL WaitForSingleObject
+ SUB EAX, WAIT_TIMEOUT
+ JE @@1
+
+ INC ESI
+@@1:
+ XCHG EAX, EBX
+ MOV EDX, offset[WndProcJustOne]
+ CALL TControl.AttachProc
+
+ CALL ReleaseMutex
+ CALL CloseHandle
+
+@@exit:
+ XCHG EAX, ESI
+ POP EDI
+ POP ESI
+ POP EBX
+end;
+{$ELSE PAS_VERSION} //Pascal
+function JustOne( Wnd: PControl; const Identifier : KOLString ) : Boolean;
+var CritSecMutex : THandle;
+ DW : Longint;
+begin
+ Result := False;
+ CritSecMutex := CreateMutex( nil, True, nil );
+ if CritSecMutex = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+
+ JustOneMutex := CreateMutex( nil, False, PKOLChar( Identifier ) );
+ if JustOneMutex <> 0 then
+ begin
+ DW := WaitForSingleObject( JustOneMutex, 0 );
+ Result := (DW <> WAIT_TIMEOUT);
+ end;
+ Wnd.AttachProc( WndProcJustOne );
+ CloseHandle( CritSecMutex );
+end;
+{$ENDIF PAS_VERSION}
+
+{ JustOneNotify }
+
+var
+ OnAnotherInstance: TOnAnotherInstance;
+ JustOneMsg: DWORD;
+
+{$IFDEF ASM_UNICODE}{$ELSE ASM_UNICODE} //Pascal
+function WndProcJustOneNotify( Control: PControl; var Msg: TMsg; var Rslt: Integer ) : Boolean;
+var Buf : array[0..MAX_PATH] of KOLChar;
+begin
+ WndProcJustOne( Control, Msg, Rslt );
+ Result := False;
+ if Msg.message = JustOneMsg then
+ begin
+ Result := True;
+ if assigned( OnAnotherInstance ) then
+ begin
+ GetWindowText( Msg.lParam, Buf, MAX_PATH );
+ OnAnotherInstance( Buf );
+ end;
+ Rslt := 0;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+// Redefine here incorrectly declared BroadcastSystemMessage API function.
+// It should not refer to BroadcastSystemMessageA, which is not present in
+// earlier versions of Windows95, but to BroadcastSystemMessage, which is
+// present in all Windows95/98/Me and NT/2K/XP.
+function BroadcastSystemMessage(Flags: DWORD; Recipients: PDWORD;
+ uiMessage: UINT; wParam: WPARAM; lParam: LPARAM): Longint; stdcall;
+external user32 name 'BroadcastSystemMessage';
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function JustOneNotify( Wnd: PControl; const Identifier : KOLString;
+ const aOnAnotherInstance: TOnAnotherInstance ) : Boolean;
+var Recipients : DWord;
+ OldCap: KOLString;
+begin
+ Result := False;
+ JustOneMsg := RegisterWindowMessage( PKOLChar( 'Message.' + Identifier ) );
+ if JustOneMsg = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+
+ Result := JustOne( Wnd, Identifier );
+ if not Result then
+ begin
+ // Send a message to the first instance of applet
+ OldCap := Wnd.Caption;
+ Wnd.Caption := GetCommandLine;
+ if Wnd.GetWindowHandle <> 0 then
+ begin
+ Recipients := BSM_APPLICATIONS;
+ BroadcastSystemMessage( BSF_QUERY or BSF_IGNORECURRENTTASK, @Recipients,
+ JustOneMsg, 0, Wnd.fHandle );
+ end;
+ Wnd.Caption := OldCap;
+ end
+ else
+ begin
+ // Store event handler to notify this instance about another
+ // instance staring:
+ OnAnotherInstance := aOnAnotherInstance;
+ Wnd.AttachProc( WndProcJustOneNotify );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+///////////////////////////////////////// STRING LIST OBJECT /////////////////
+
+{$ENDIF WIN}
+{ TStrList }
+
+function NewStrList: PStrList;
+begin
+ New( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TStrList';
+ {$ENDIF}
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+destructor TStrList.Destroy;
+begin
+ Clear;
+ inherited;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TStrList.Init;
+begin
+ {$IFDEF CALL_INHERITED}
+ inherited;
+ {$ENDIF}
+ fNameDelim := DefaultNameDelimiter;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TStrList.Add(const S: Ansistring): integer;
+begin
+ Result := fCount;
+ Insert( Result, S );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TStrList.AddStrings(Strings: PStrList);
+begin
+ SetText( Strings.Text, True );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TStrList.Assign(Strings: PStrList);
+begin
+ Clear;
+ AddStrings( Strings );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TStrList.Clear;
+var I: Integer;
+begin
+ if fCount > 0 then
+ for I := fList.Count - 1 downto 0 do
+ Delete( I );
+ fList.Free;
+ fList := nil;
+ fCount := 0;
+ if fTextBuf <> nil then
+ begin
+ FreeMem( fTextBuf );
+ fTextBuf := nil;
+ fTextSiz := 0;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION} {$DEFINE TStrList_Delete_ASM} {$ENDIF}
+{$IFDEF TLIST_FAST} {$UNDEF TStrList_Delete_ASM} {$ENDIF}
+
+{$IFDEF TStrList_Delete_ASM}
+{$ELSE PAS_VERSION} //Pascal
+procedure TStrList.Delete(Idx: integer);
+var P: DWORD;
+ El:Pointer;
+begin
+ P := DWORD( fList.Items[ Idx ] );
+ if (fTextBuf <> nil) and ( P >= DWORD( fTextBuf )) and
+ ( P < DWORD( fTextBuf ) + fTextSiz ) then
+ else
+ begin
+ El := FList.Items[ Idx ];
+ FreeMem( El );
+ end;
+ fList.Delete( Idx );
+ Dec( fCount );
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TStrList.DeleteLast;
+begin
+ Delete( Count-1 );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TStrList.Get(Idx: integer): Ansistring;
+begin
+ if fList <> nil then
+ Result := PAnsiChar( fList.Items[ Idx ] )
+ else Result := '';
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_TLIST}
+function TStrList.GetPChars(Idx: Integer): PAnsiChar;
+asm
+ MOV EAX, [EAX].fList
+ MOV EAX, [EAX].TList.fItems
+ MOV EAX, [EAX+EDX*4]
+end;
+{$ELSE PAS_VERSION} //Pascal
+function TStrList.GetPChars(Idx: Integer): PAnsiChar;
+begin
+ Result := PAnsiChar( fList.{$IFDEF TLIST_FAST}Items{$ELSE}fItems{$ENDIF}[ Idx ] )
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_TLIST}
+function TStrList.GetTextStr: Ansistring;
+asm
+ PUSH ESI
+ PUSH EDI
+ MOV ECX, [EAX].fCount
+ MOV EAX, [EAX].fList
+ PUSH ECX
+ JECXZ @@1
+ MOV ESI, [EAX].TList.fItems
+@@1: PUSH ESI
+ XCHG EAX, EDX
+ XOR EDX, EDX
+ JECXZ @@10
+ PUSH EAX
+@@loo1: PUSH ECX
+ PUSH EDX
+ LODSD
+ CALL StrLen
+ POP EDX
+ LEA EDX, [EDX+EAX+2]
+ POP ECX
+ LOOP @@loo1
+ POP EAX
+ POP ESI
+ XCHG ECX, EDX
+ PUSH EAX
+@@10: {$IFDEF _D2}
+ CALL _LStrFromPCharLen
+ {$ELSE}
+ {$IFDEF _D2009orHigher}
+ XOR ECX, ECX
+ {$ENDIF}
+ CALL System.@LStrFromPCharLen
+ {$ENDIF}
+ POP EDI
+ POP ECX
+ JECXZ @@exit
+ MOV EDI, [EDI]
+@@loo2: PUSH ECX
+ LODSD
+ PUSH EAX
+ CALL StrLen
+ XCHG ECX, EAX
+ POP EAX
+ XCHG EAX, ESI
+ REP MOVSB
+ XCHG ESI, EAX
+ MOV AX, $0A0D
+ STOSW
+ POP ECX
+ LOOP @@loo2
+ XCHG EAX, ECX
+ STOSB
+@@exit: POP EDI
+ POP ESI
+end;
+{$ELSE PAS_VERSION} //Pascal
+function TStrList.GetTextStr: Ansistring;
+var
+ I, Len, Size: integer;
+ P: PAnsiChar;
+begin
+ Size := 0;
+
+ for I := 0 to fCount - 1 do
+ Inc(Size, StrLen( PAnsiChar(fList.
+ {$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF} [I]) ) +
+ {$IFDEF LIN} 1 {$ELSE} 2 {$ENDIF});
+
+ SetString(Result, nil, Size);
+
+ P := Pointer(Result);
+ for I := 0 to Count - 1 do
+ begin
+ Len := StrLen(PAnsiChar(fList.
+ {$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF} [I]));
+ if (Len > 0) then
+ begin
+ System.Move(PAnsiChar(fList.
+ {$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}[I])^,
+ P^, Len);
+ Inc(P, Len);
+ end;
+ P^ := #13;
+ Inc(P);
+ {$IFDEF WIN}
+ P^ := #10;
+ Inc(P);
+ {$ENDIF WIN}
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_TLIST}
+function TStrList.IndexOf(const S: Ansistring): integer;
+asm
+ PUSH EDI
+ PUSH ESI
+ PUSH EBX
+ OR EDI, -1
+ MOV ECX, [EAX].fCount
+ JECXZ @@exit
+ MOV ESI, [EAX].fList
+ MOV ESI, [ESI].TList.fItems
+ CALL EDX2PChar
+ MOVZX EBX, BYTE[EDX]
+@@loo: LODSD
+ INC EDI
+ CMP BL, BYTE[EAX]
+ JNE @@1
+ PUSH EDX
+ PUSH ECX
+ CALL StrComp
+ POP ECX
+ POP EDX
+ JE @@exit
+@@1: LOOP @@loo
+ OR EDI, -1
+@@exit: XCHG EAX, EDI
+ POP EBX
+ POP ESI
+ POP EDI
+end;
+{$ELSE PAS_VERSION} //Pascal
+function TStrList.IndexOf(const S: AnsiString): integer;
+var Word1: Word;
+begin
+ if S = '' then
+ begin
+ for Result := 0 to fCount - 1 do
+ if PAnsiChar(fList.Items[Result])^ = #0 then Exit; {>>>>>>>>>>>>>>>>>>}
+ end else
+ begin
+ Word1 := PWord(PAnsiChar( S ))^;
+ for Result := 0 to fCount - 1 do
+ if (PWord(fList.Items[Result])^ = Word1)
+ and (StrComp( fList.Items[Result], PAnsiChar( S ) ) = 0) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Result := -1;
+end;
+{$ENDIF PAS_VERSION}
+
+function TStrList.IndexOf_NoCase(const S: AnsiString): integer;
+var tmp: PAnsiChar;
+ c: AnsiChar;
+begin
+ if S = '' then
+ begin
+ for Result := 0 to fCount - 1 do
+ if PAnsiChar( fList.Items[Result] )^ = #0 then Exit; {>>>>>>>>>>}
+ end else
+ begin
+ if not Upper_initialized then
+ Init_Upper;
+ for Result := 0 to fCount - 1 do
+ begin
+ tmp := fList.Items[Result];
+ c := Upper[S[1]];
+ if (c = Upper[tmp^]) and
+ (_AnsiCompareStrNoCaseA( PAnsiChar( S ), tmp ) = 0) then Exit; {>>>}
+ end;
+ end;
+ Result := -1;
+end;
+
+function TStrList.IndexOfStrL_NoCase( Str: PAnsiChar; L: Integer ): integer;
+begin
+ if L = 0 then
+ Result := 0
+ else
+ begin
+ for Result := 0 to fCount - 1 do
+ if (StrLen( PAnsiChar( fList.
+ {$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}[ Result ]
+ ) ) = DWORD( L )) and
+ (StrLComp_NoCase( Str, PAnsiChar(
+ fList.{$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}[ Result ]
+ ), L ) = 0) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := -1;
+ end;
+end;
+
+function CompareAnsiCase( const S1, S2: PAnsiChar ): Integer;
+begin
+ Result := _AnsiCompareStrA( S1, S2 );
+end;
+
+function CompareAnsiNoCase( const S1, S2: PAnsiChar ): Integer;
+begin
+ Result := _AnsiCompareStrNoCaseA( S1, S2 );
+end;
+
+function TStrList.Find(const S: AnsiString; var Index: Integer): Boolean;
+var
+ L, H, C: Integer;
+begin
+ Result := FALSE;
+ Index := 0;
+ L := 0;
+ H := FCount - 1;
+ if H < 0 then Exit; // === if FCount = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>}
+ if fAnsiSort then
+ begin
+ if fCaseSensitiveSort then
+ fCompareStrListFun := CompareAnsiCase
+ else
+ fCompareStrListFun := CompareAnsiNoCase;
+ end else
+ begin
+ if fCaseSensitiveSort then
+ fCompareStrListFun := StrComp
+ else
+ fCompareStrListFun := StrComp_NoCase;
+ end;
+ C := 0;
+ while L <= H do
+ begin
+ Index := (L + H) shr 1;
+ C := fCompareStrListFun( PAnsiChar( fList.Items[ Index ] ),
+ PAnsiChar( S ) );
+ if C < 0 then L := Index + 1 else
+ begin
+ H := Index - 1;
+ if C = 0 then
+ begin
+ Result := TRUE; {Index := I;} Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ end;
+ if C < 0 then Index := -L;
+end;
+
+function TStrList.FindFirst(const S: AnsiString; var Index: Integer): Boolean;
+begin
+ Result := Find( S, Index );
+ if Result then
+ begin
+ while (Index > 0)
+ and (fCompareStrListFun( PAnsiChar( fList.Items[ Index-1 ] ),
+ PAnsiChar( S )) = 0) do
+ dec( Index );
+ end;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TStrList.Insert(Idx: integer; const S: Ansistring);
+var Mem: PAnsiChar;
+ L: Integer;
+begin
+ if fList = nil then
+ fList := NewList;
+ L := Length( S ) + 1;
+ GetMem( Mem, L );
+ Mem[0] := #0;
+ if L > 1 then
+ System.Move( S[1], Mem[0], L );
+ fList.Insert( Idx, Mem );
+ Inc( fCount );
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TStrList.Move(CurIndex, NewIndex: integer);
+begin
+ fList.MoveItem( CurIndex, NewIndex );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TStrList.Put(Idx: integer; const Value: Ansistring);
+begin
+ Delete( Idx );
+ Insert( Idx, Value );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_TLIST}
+procedure TStrList.SetText(const S: Ansistring; Append2List: boolean);
+asm
+ DEC CL
+ JZ @@1
+ PUSHAD
+ CALL Clear
+ POPAD
+@@1: CALL EDX2PChar
+ JZ @@exit
+ PUSH EBX
+ PUSH EDI
+ MOV EBX, EAX
+ MOV EDI, [EBX].fTextSiz
+ MOV EAX, [EDX-4] // EAX = Length(S)
+ INC EAX
+ PUSH EAX
+ // add S to text buffer
+ PUSH EDX
+ PUSH [EBX].fTextBuf
+ ADD EAX, [EBX].fTextSiz
+ CALL System.@GetMem
+ MOV [EBX].fTextBuf, EAX
+ MOV ECX, EDI
+ XCHG EDX, EAX
+ POP EAX
+ JECXZ @@atb_fin
+ PUSH EAX
+ CALL System.Move
+ POP EDX
+ PUSH EDX
+ PUSH ESI
+ MOV ESI, [EBX].fList
+ MOV ESI, [ESI].TList.fItems
+ MOV ECX, [EBX].fCount
+@@atb_loo:
+ LODSD
+ SUB EAX, EDX
+ CMP EAX, [EBX].fTextSiz
+ JAE @@atb_nxt
+ ADD EAX, [EBX].fTextBuf
+ MOV [ESI-4], EAX
+@@atb_nxt: LOOP @@atb_loo
+ POP ESI
+ POP EAX
+ CALL System.@FreeMem
+@@atb_fin:
+ POP EAX
+ MOV EDX, EDI
+ ADD EDX, [EBX].fTextBuf
+ POP ECX
+ PUSH ECX
+ ADD [EBX].fTextSiz, ECX
+ CALL System.Move
+@@eatb:
+ ADD EDI, [EBX].fTextBuf // EDI ~ P
+ MOV ECX, [EBX].fList
+ INC ECX
+ LOOP @@2
+ CALL NewList
+ MOV [EBX].fList, EAX
+@@2:
+ POP ECX
+ MOV EDX, [EBX].fCount
+ PUSH EDI
+ PUSH ECX
+ MOV AL, $0D
+@@loo1: CMP byte ptr [EDI], 0
+ JZ @@eloo1
+ INC EDX
+ REPNZ SCASB
+ JNZ @@eloo1
+ CMP byte ptr [EDI], $0A
+ JNZ @@loo1
+ INC EDI
+ LOOP @@loo1
+@@eloo1:
+ MOV [EBX].fCount, EDX
+ MOV EAX, [EBX].fList
+ {$IFNDEF TLIST_FAST}
+ PUSH EDX
+ PUSH EAX
+ CMP EDX, [EAX].TList.fCapacity
+ JLE @@3
+ CALL TList.SetCapacity
+@@3: POP EAX
+ POP ECX
+ {$ENDIF TLIST_FAST}
+ XCHG ECX, [EAX].TList.fCount
+ MOV EDX, [EAX].TList.fItems
+ LEA EDX, [EDX+ECX*4]
+ POP ECX
+ POP EDI
+ MOV EAX, $0D
+@@loo2: CMP byte ptr [EDI], AH
+ JZ @@eloo2
+ MOV [EDX], EDI
+ ADD EDX, 4
+ REPNZ SCASB
+ JNZ @@eloo2
+ MOV [EDI-1], AH
+ CMP byte ptr [EDI], $0A
+ JNZ @@loo2
+ INC EDI
+ LOOP @@loo2
+@@eloo2:
+ POP EDI
+ POP EBX
+@@exit:
+end;
+{$ELSE PAS_VERSION} //Pascal
+procedure TStrList.SetText(const S: Ansistring; Append2List: Boolean);
+var
+ P, TheLast : PAnsiChar;
+ L, I : Integer;
+
+ procedure AddTextBuf(Src: PAnsiChar; Len: DWORD);
+ var OldTextBuf, P: PAnsiChar;
+ I : Integer;
+ begin
+ if Src <> nil then
+ begin
+ OldTextBuf := fTextBuf;
+ GetMem( fTextBuf, fTextSiz + Len );
+ if fTextSiz <> 0 then
+ begin
+ System.Move( OldTextBuf^, fTextBuf^, fTextSiz );
+ for I := 0 to fCount - 1 do
+ begin
+ P := fList.{$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}[ I ];
+ if (DWORD( P ) >= DWORD( OldTextBuf )) and
+ (DWORD( P ) < DWORD( OldTextBuf ) + fTextSiz) then
+ fList.{$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}[ I ] :=
+ Pointer( DWORD( P ) - DWORD( OldTextBuf ) + DWORD( fTextBuf ) );
+ end;
+ FreeMem( OldTextBuf );
+ end;
+ System.Move( Src^, fTextBuf[ fTextSiz ], Len );
+ Inc( fTextSiz, Len );
+ end;
+ end;
+begin
+ if not Append2List then Clear;
+ if S = '' then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ L := fTextSiz;
+ AddTextBuf( PAnsiChar( S ), Length( S ) + 1 );
+ P := PAnsiChar( DWORD( fTextBuf ) + DWORD( L ) );
+ if fList = nil then fList := NewList;
+ I := 0;
+ TheLast := P + Length( S );
+ while P^ <> #0 do
+ begin
+ Inc( I );
+ {$IFDEF WIN}
+ P := StrScanLen( P, #13, TheLast - P );
+ if P^ = #10 then
+ Inc( P );
+ {$ELSE LIN}
+ P := StrScanLen( P, #10, TheLast - P );
+ {$ENDIF}
+ end;
+ Inc( fCount, I );
+ {$IFNDEF TLIST_FAST}
+ if fList.fCapacity < fCount then
+ fList.Capacity := fCount;
+ {$ENDIF}
+ P := PAnsiChar( DWORD( fTextBuf ) + DWORD( L ) );
+ while P^ <> #0 do
+ begin
+ fList.Add( P );
+ {$IFDEF WIN}
+ P := StrScanLen( P, #13, TheLast - P );
+ if PAnsiChar( P - 1 )^ = #13 then
+ PAnsiChar( P - 1 )^ := #0;
+ if P^ = #10 then Inc(P);
+ {$ELSE LIN}
+ P := StrScanLen( P, #10, TheLast - P );
+ {$ENDIF}
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TStrList.SetUnixText(const S: AnsiString; Append2List: Boolean);
+var S1: AnsiString;
+begin
+ S1 := S;
+ NormalizeUnixText( S1 );
+ SetText( S1, Append2List );
+end;
+
+procedure TStrList.SetTextStr(const Value: Ansistring);
+begin
+ SetText( Value, False );
+end;
+
+{$IFDEF ASM_TLIST}
+function CompareStrListItems_NoCase( const Sender : Pointer; const e1, e2 : DWORD ) : Integer;
+asm
+ MOV EAX, [EAX].TStrList.fList
+ MOV EAX, [EAX].TList.fItems
+ MOV EDX, [EAX+EDX*4]
+ MOV EAX, [EAX+ECX*4]
+ XCHG EAX, EDX
+ JMP StrComp_NoCase
+end;
+{$ELSE PAS_VERSION} //Pascal
+function CompareStrListItems_NoCase( const Sender : Pointer; const e1, e2 : DWORD ) : Integer;
+var S1, S2 : PAnsiChar;
+begin
+ S1 := PStrList( Sender ).fList.Items[ e1 ];
+ S2 := PStrList( Sender ).fList.Items[ e2 ];
+ Result := StrComp_NoCase( S1, S2 );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_TLIST}
+function CompareStrListItems_Case( const Sender : Pointer; const e1, e2 : DWORD ) : Integer;
+asm
+ MOV EAX, [EAX].TStrList.fList
+ MOV EAX, [EAX].TList.fItems
+ MOV EDX, [EAX+EDX*4]
+ MOV EAX, [EAX+ECX*4]
+ XCHG EAX, EDX
+ JMP StrComp
+end;
+{$ELSE PAS_VERSION} //Pascal
+function CompareStrListItems_Case( const Sender : Pointer; const e1, e2 : DWORD ) : Integer;
+var S1, S2 : PAnsiChar;
+begin
+ S1 := PStrList( Sender ).fList.Items[ e1 ];
+ S2 := PStrList( Sender ).fList.Items[ e2 ];
+ Result := StrComp( S1, S2 );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_TLIST}
+function CompareAnsiStrListItems( const Sender : Pointer; const e1, e2 : DWORD ) : Integer;
+asm
+ MOV EAX, [EAX].TStrList.fList
+ MOV EAX, [EAX].TList.fItems
+ MOV EDX, [EAX+EDX*4]
+ MOV EAX, [EAX+ECX*4]
+ XCHG EAX, EDX
+ JMP _AnsiCompareStrNoCase
+end;
+{$ELSE PAS_VERSION} //Pascal
+function CompareAnsiStrListItems( const Sender : Pointer; const e1, e2 : DWORD ) : Integer;
+var S1, S2 : PAnsiChar;
+begin
+ S1 := PStrList( Sender ).fList.Items[ e1 ];
+ S2 := PStrList( Sender ).fList.Items[ e2 ];
+ Result := _AnsiCompareStrNoCaseA( S1, S2 );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_TLIST}
+function CompareAnsiStrListItems_Case( const Sender : Pointer; const e1, e2 : DWORD ) : Integer;
+asm
+ MOV EAX, [EAX].TStrList.fList
+ MOV EAX, [EAX].TList.fItems
+ MOV EDX, [EAX+EDX*4]
+ MOV EAX, [EAX+ECX*4]
+ XCHG EAX, EDX
+ JMP _AnsiCompareStr
+end;
+{$ELSE PAS_VERSION} //Pascal
+function CompareAnsiStrListItems_Case( const Sender : Pointer; const e1, e2 : DWORD ) : Integer;
+var S1, S2 : PAnsiChar;
+begin
+ S1 := PStrList( Sender ).fList.Items[ e1 ];
+ S2 := PStrList( Sender ).fList.Items[ e2 ];
+ Result := _AnsiCompareStrA( S1, S2 )
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TStrList.Sort(CaseSensitive: Boolean);
+begin
+ fCaseSensitiveSort := CaseSensitive;
+ fAnsiSort := FALSE;
+ {$IFDEF SPEED_FASTER}
+ {$DEFINE SORT_STRLIST_ARRAY}
+ {$ENDIF}
+ {$IFDEF TLIST_FAST}
+ {$UNDEF SORT_STRLIST_ARRAY}
+ {$ENDIF}
+ {$IFDEF _D2}
+ {$UNDEF SORT_STRLIST_ARRAY}
+ {$ENDIF}
+ {$IFDEF SORT_STRLIST_ARRAY}
+ if Count > 1 then
+ if CaseSensitive then
+ SortArray( fList.fItems, fCount, @StrComp )
+ else
+ SortArray( fList.fItems, fCount, @StrComp_NoCase );
+ {$ELSE}
+ if CaseSensitive then
+ SortData( @Self, fCount, @CompareStrListItems_Case, @TStrList.Swap )
+ else
+ SortData( @Self, fCount, @CompareStrListItems_NoCase, @TStrList.Swap )
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF noASM_VERSION}
+procedure TStrList.AnsiSort(CaseSensitive: Boolean);
+asm
+ MOV [EAX].fCaseSensitiveSort, DL
+ MOV [EAX].fAnsiSort, 1
+ {$IFDEF SORT_STRLIST_ARRAY}
+ MOV ECX, Offset[_AnsiCompareStrA]
+ CMP DL, 0
+ JNZ @@01
+ MOV ECX, [_AnsiCompareStrNoCaseA]
+@@01:
+ MOV EAX, [EAX].fList
+ MOV EDX, [EAX].TList.fCount
+ CMP EDX, 1
+ JLE @@02
+ MOV EAX, [EAX].TList.fItems
+ CALL SortArray
+@@02:
+ {$ELSE}
+ PUSH Offset[TStrList.Swap]
+ MOV ECX, Offset[CompareAnsiStrListItems]
+ CMP DL, 0
+ JNZ @1
+ MOV ECX, Offset[CompareAnsiStrListItems_Case]
+@1: MOV EDX, [EAX].fCount
+ CALL SortData
+ {$ENDIF}
+end;
+{$ELSE PAS_VERSION} //Pascal
+procedure TStrList.AnsiSort(CaseSensitive: Boolean);
+begin
+ fCaseSensitiveSort := CaseSensitive;
+ fAnsiSort := TRUE;
+ {$IFDEF SPEED_FASTER}
+ {$DEFINE SORT_STRLIST_ARRAY}
+ {$ENDIF}
+ {$IFDEF TLIST_FAST}
+ {$UNDEF SORT_STRLIST_ARRAY}
+ {$ENDIF}
+ {$IFDEF _D2}
+ {$UNDEF SORT_STRLIST_ARRAY}
+ {$ENDIF}
+ if Count > 1 then
+ begin
+ {$IFDEF SPEED_FASTER}
+ if CaseSensitive then // to prepare !!!
+ _AnsiCompareStrA( ItemPtrs[0], ItemPtrs[1] )
+ else _AnsiCompareStrNoCaseA( ItemPtrs[0], ItemPtrs[1] );
+ {$ENDIF}
+ {$IFDEF SORT_STRLIST_ARRAY}
+ if CaseSensitive then
+ SortArray( fList.fItems, fCount, @_AnsiCompareStrA )
+ else
+ SortArray( fList.fItems, fCount, @_AnsiCompareStrNoCaseA );
+ {$ELSE}
+ if CaseSensitive then
+ SortData( @Self, fCount, @CompareAnsiStrListItems_Case, @TStrList.Swap )
+ else
+ SortData( @Self, fCount, @CompareAnsiStrListItems, @TStrList.Swap );
+ {$ENDIF}
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TStrList.SortEx(const CompareFun: TCompareEvent);
+begin
+ SortData( @Self, Count, CompareFun, @TStrList.Swap );
+end;
+
+procedure TStrList.Swap(Idx1, Idx2: Integer);
+begin
+ fList.Swap( Idx1, Idx2 );
+end;
+
+function TStrList.Last: AnsiString;
+begin
+ if Count = 0 then
+ Result := ''
+ else
+ Result := Items[ Count - 1 ];
+end;
+
+//-- code by Dod:
+function TStrList.IndexOfName(AName: Ansistring): Integer;
+var i: Integer;
+ L: Integer;
+begin
+ Result:=-1;
+ // Do not start search if empty string
+ L := Length( AName );
+ if L > 0 then
+ begin
+ AName := LowerCase( AName ) + fNameDelim;
+ Inc( L );
+ for i := 0 to fCount - 1 do
+ begin
+ // For optimization, check only list entry that begin with same letter as searched name
+ if StrLComp( PAnsiChar( LowerCase( ItemPtrs[ i ] ) ), PAnsiChar( AName ), L ) = 0 then
+ begin
+ Result:=i; exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ end;
+end;
+
+function TStrList.IndexOfName_NoCase(AName: Ansistring): Integer;
+var i: Integer;
+ L: Integer;
+ s, p: PAnsiChar;
+begin
+ Result:=-1;
+ L := Length( AName );
+ if L > 0 then
+ begin
+ s := PAnsiChar( AName );
+ for i := 0 to fCount - 1 do
+ begin
+ if StrLComp_NoCase( ItemPtrs[ i ], s, L ) = 0 then
+ begin
+ p := ItemPtrs[ i ];
+ inc( p, L );
+ while (p^ <> #0) and (p^ <= ' ') do inc( p );
+ if p^ = fNameDelim then
+ begin
+ Result := i; exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ end;
+ end;
+end;
+
+//-- code by Dod:
+function TStrList.GetValue(const AName: Ansistring): Ansistring;
+var
+ i: Integer;
+begin
+ I := IndexOfName(AName);
+ if I >= 0
+ then Result := Copy(Items[i], Length(AName) + 2, Length(Items[i])-Length(AName)-1)
+ else Result := '';
+end;
+
+//-- code by Dod:
+procedure TStrList.SetValue(const AName, Value: Ansistring);
+var
+ I: Integer;
+begin
+ I := IndexOfName(AName);
+ if i=-1
+ then Add( AName + fNameDelim + Value )
+ else Items[i] := AName + fNameDelim + Value;
+end;
+
+function TStrList.GetLineName(Idx: Integer): AnsiString;
+var s: AnsiString;
+ Q: PAnsiChar;
+begin
+ s := ItemPtrs[ Idx ];
+ Q := StrScan( PAnsiChar(s), fNameDelim );
+ if Assigned(Q) {by Dufa} then Q^ := #0;
+ Result := PAnsiChar(s);
+end;
+
+procedure TStrList.SetLineName(Idx: Integer; const NV: AnsiString);
+begin
+ Items[ Idx ] := NV + fNameDelim + LineValue[ Idx ];
+end;
+
+function TStrList.GetLineValue(Idx: Integer): AnsiString;
+var Q: PAnsiChar;
+begin
+ Q := ItemPtrs[ Idx ];
+ Q := StrScan( Q, fNameDelim );
+ if Q <> nil then
+ inc( Q );
+ Result := Q;
+end;
+
+procedure TStrList.SetLineValue(Idx: Integer; const Value: Ansistring);
+begin
+ Items[ Idx ] := LineName[ Idx ] + fNameDelim + Value;
+end;
+
+function TStrList.Join( const sep: AnsiString ): AnsiString;
+var
+ I, Len, Size: integer;
+ P: PAnsiChar;
+begin
+ Size := 0;
+
+ for I := 0 to Count - 1 do
+ Inc(Size, Integer( StrLen( ItemPtrs[I] ) ) + Length(Sep));
+
+ SetString(Result, nil, Size);
+
+ P := @ Result[ 1 ];
+ for I := 0 to Count - 1 do
+ begin
+ Len := StrLen( ItemPtrs[I] );
+ if (Len > 0) then
+ begin
+ System.Move( ItemPtrs[I]^, P^, Len);
+ Inc(P, Len);
+ end;
+ P := StrPCopy(P, Sep);
+ inc( P, Length( Sep ) ); // + by Korneev Ivan
+ end;
+end;
+
+{$IFDEF WIN_GDI}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function TStrList.AppendToFile(const FileName: KOLString): Boolean;
+var F: HFile;
+ Buf: AnsiString;
+ L: Integer;
+begin
+ F := FileCreate( FileName, ofOpenWrite or ofOpenAlways );
+ Result := F <> INVALID_HANDLE_VALUE;
+ if Result then
+ begin
+ FileSeek( F, 0, spEnd );
+ Buf := Text;
+ L := Length( Buf );
+ FileWrite( F, Buf[ 1 ], L );
+ FileClose( F );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function TStrList.LoadFromFile(const FileName: KOLString): Boolean;
+var Buf: AnsiString;
+ F: HFile;
+ Sz: Integer;
+begin
+ F := FileCreate( FileName, ofOpenRead or ofShareDenyWrite or ofOpenExisting );
+ Result := F <> INVALID_HANDLE_VALUE;
+ if Result then
+ begin
+ Sz := GetFileSize( F, nil );
+ SetString( Buf, nil, Sz );
+ FileRead( F, Buf[1], Sz );
+ FileClose( F );
+
+ SetText( Buf, False );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_STREAM}
+procedure TStrList.LoadFromStream(Stream: PStream; Append2List: boolean);
+asm
+ PUSH EAX
+ PUSH ECX
+ PUSH EBX
+ XCHG EAX, EDX
+ MOV EBX, EAX
+ CALL TStream.GetSize
+ PUSH EAX
+ MOV EAX, EBX
+ CALL TStream.GetPosition
+ POP ECX
+ SUB ECX, EAX
+ XOR EDX, EDX
+ PUSH EDX
+ MOV EAX, ESP
+ PUSH ECX
+ {$IFDEF _D2}
+ CALL _LStrFromPCharLen
+ {$ELSE}
+ {$IFDEF _D2009orHigher}
+ push 0
+ {$ENDIF}
+ CALL System.@LStrFromPCharLen
+ {$ENDIF}
+ POP ECX
+ POP EDX
+ XCHG EAX, EBX
+ PUSH EDX
+ CALL TStream.Read
+ POP EDX
+ POP EBX
+ POP ECX
+ POP EAX
+ PUSH EDX
+ CALL SetText
+ CALL RemoveStr
+end;
+{$ELSE PAS_VERSION} //Pascal
+procedure TStrList.LoadFromStream(Stream: PStream; Append2List: Boolean);
+var Buf: AnsiString;
+ Sz: Integer;
+begin
+ Sz := Stream.Size - Stream.Position;
+ SetString( Buf, nil, Sz );
+ Stream.Read( Buf[1], Sz );
+ SetText( Buf, Append2List );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TStrList.MergeFromFile(const FileName: KOLString);
+var TmpStream: PStream;
+begin
+ TmpStream := NewReadFileStream( FileName );
+ LoadFromStream( TmpStream, True );
+ TmpStream.Free;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function TStrList.SaveToFile(const FileName: KOLString): Boolean;
+var F: HFile;
+ Buf: AnsiString;
+begin
+ F := FileCreate( FileName, ofOpenWrite or ofCreateAlways );
+ Result := F <> INVALID_HANDLE_VALUE;
+ if Result then
+ begin
+ Buf := Text;
+ FileWrite( F, Buf[ 1 ], Length( Buf ) );
+ SetEndOfFile( F ); // necessary! - V.K.
+ FileClose( F );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TStrList.SaveToStream(Stream: PStream);
+var S: Ansistring;
+ L: Integer;
+begin
+ S := GetTextStr;
+ L := Length( S );
+ if L <> 0 then
+ Stream.Write( S[1], L );
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TStrList.OptimizeForRead;
+begin
+ {$IFDEF TLIST_FAST}
+ if fList <> nil then
+ fList.OptimizeForRead;
+ {$ENDIF}
+end;
+
+{$ENDIF WIN_GDI}
+////////////////////////////////// EXTENDED STRING LIST OBJECT ////////////////
+
+{$IFDEF PAS_ONLY}
+procedure WStrCopy( Dest, Src: PWideChar );
+begin
+ while Src^ <> #0 do
+ begin
+ Dest^ := Src^;
+ inc(Src);
+ inc(Dest);
+ end;
+end;
+{$ELSE}
+procedure WStrCopy( Dest, Src: PWideChar );
+asm
+ PUSH EDI
+ PUSH ESI
+ MOV ESI,EAX
+ MOV EDI,EDX
+ OR ECX, -1
+ XOR EAX, EAX
+ REPNE SCASW
+ NOT ECX
+ MOV EDI,ESI
+ MOV ESI,EDX
+ REP MOVSW
+ POP ESI
+ POP EDI
+end;
+{$ENDIF}
+
+procedure WStrLCopy( Dest, Src: PWideChar; MaxLen: Integer );
+begin
+ while MaxLen > 0 do
+ begin
+ Dest^ := Src^;
+ if Src^ = #0 then break;
+ inc( Dest );
+ inc( Src );
+ dec( MaxLen );
+ if MaxLen = 0 then
+ Dest^ := Src^;
+ end;
+end;
+
+{$IFDEF PAS_ONLY}
+function WStrCmp( W1, W2: PWideChar ): Integer;
+begin
+ while (W1^ <> #0) and (w2^ <> #0) do
+ begin
+ Result := Integer(Ord(w1^)) - Integer(Ord(w2^));
+ if Result <> 0 then Exit;
+ inc(w1);
+ inc(w2);
+ end;
+ Result := 0;
+end;
+{$ELSE}
+function WStrCmp( W1, W2: PWideChar ): Integer;
+asm
+ PUSH ESI
+ PUSH EDI
+ XCHG ESI, EAX
+ MOV EDI, EDX
+ XOR EAX, EAX
+@@loop: LODSW
+ MOVZX EDX, word ptr [EDI]
+ INC EDI
+ INC EDI
+ CMP EAX, EDX
+ JNE @@exit
+ TEST EAX, EAX
+ JNZ @@loop
+@@exit: SUB EAX, EDX
+ POP EDI
+ POP ESI
+end;
+{$ENDIF}
+
+{$IFDEF _D3orHigher}
+function WStrCmp_NoCase( W1, W2: PWideChar ): Integer;
+begin
+ Result := 0;
+ while (WUpperCase( '' + W1^ ) = WUpperCase( '' + W2^ )) do
+ begin
+ if W1^ = #0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ inc( W1 );
+ inc( W2 );
+ end;
+ Result := Integer(W1^) - Integer(W2^);
+end;
+{$ENDIF}
+
+{ TStrListEx }
+
+function NewStrListEx: PStrListEx;
+begin
+ new( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TStrListEx';
+ {$ENDIF}
+end;
+
+destructor TStrListEx.Destroy;
+var Obj: PList;
+begin
+ Obj := FObjects;
+ inherited;
+ Obj.Free;
+end;
+
+function TStrListEx.GetObjects(Idx: Integer): DWORD;
+begin
+ Result := 0;
+ if FObjects.fCount > Idx then
+ Result := DWORD( FObjects.Items[ Idx ] );
+end;
+
+function TStrListEx.GetObjectCount: Integer;
+begin
+ Result := FObjects.Count;
+end;
+
+procedure TStrListEx.SetObjects(Idx: Integer; const Value: DWORD);
+begin
+ ProvideObjCapacity( Idx + 1 );
+ FObjects.Items[ Idx ] := Pointer( Value );
+end;
+
+procedure TStrListEx.Init;
+begin
+ inherited;
+ FObjects := NewList;
+end;
+
+procedure SwapStrListExItems( const Sender: Pointer; const e1, e2: DWORD );
+begin
+ PStrListEx( Sender ).Swap( e1, e2 );
+end;
+
+procedure TStrListEx.AnsiSort(CaseSensitive: Boolean);
+begin
+ fCaseSensitiveSort := CaseSensitive;
+ fAnsiSort := TRUE;
+ if CaseSensitive then
+ SortData( @Self, fCount, @CompareAnsiStrListItems_Case, @SwapStrListExItems )
+ else
+ SortData( @Self, fCount, @CompareAnsiStrListItems, @SwapStrListExItems )
+end;
+
+procedure TStrListEx.Sort(CaseSensitive: Boolean);
+begin
+ fCaseSensitiveSort := CaseSensitive;
+ fAnsiSort := FALSE;
+ if CaseSensitive then
+ SortData( @Self, fCount, @CompareStrListItems_Case, @SwapStrListExItems )
+ else SortData( @Self, fCount, @CompareStrListItems_NoCase, @SwapStrListExItems );
+end;
+
+procedure TStrListEx.Move(CurIndex, NewIndex: integer);
+begin
+ // move string
+ fList.MoveItem( CurIndex, NewIndex );
+ // move object
+ if FObjects.fCount >= Min( CurIndex, NewIndex ) then
+ begin
+ ProvideObjCapacity( max( CurIndex, NewIndex ) + 1 );
+ FObjects.MoveItem( CurIndex, NewIndex );
+ end;
+end;
+
+procedure TStrListEx.Swap(Idx1, Idx2: Integer);
+begin
+ // swap strings
+ fList.Swap( Idx1, Idx2 );
+ // swap objects
+ if FObjects.fCount >= Min( Idx1, Idx2 ) then
+ begin
+ ProvideObjCapacity( max( Idx1, Idx2 ) + 1 );
+ FObjects.Swap( Idx1, Idx2 );
+ end;
+end;
+
+procedure TStrListEx.ProvideObjCapacity(NewCap: Integer);
+begin
+ if FObjects.FCount < NewCap then
+ begin
+ {$IFDEF TLIST_FAST}
+ while FObjects.FCount < NewCap do
+ FObjects.Add( nil );
+ {$ELSE}
+ FObjects.Capacity := NewCap;
+ ZeroMemory( @FObjects.{$IFDEF TLIST_FAST} Items {$ELSE} FItems {$ENDIF}[ FObjects.FCount ],
+ (FObjects.Capacity - FObjects.Count) * sizeof( Pointer ) );
+ FObjects.FCount := NewCap;
+ {$ENDIF}
+ end;
+end;
+
+procedure TStrListEx.AddStrings(Strings: PStrListEx);
+var I: Integer;
+begin
+ I := Count;
+ if Strings.FObjects.fCount > 0 then
+ ProvideObjCapacity( I );
+ inherited AddStrings( Strings );
+ if Strings.FObjects.fCount > 0 then
+ begin
+ {$IFDEF TLIST_FAST}
+ for I := 0 to Strings.FObjects.fCount-1 do
+ FObjects.Add( Strings.FObjects.Items[ I ] );
+ {$ELSE}
+ ProvideObjCapacity( I + Strings.FObjects.fCount );
+ System.Move( Strings.FObjects.fItems[ 0 ],
+ FObjects.FItems[ I ],
+ Sizeof( Pointer ) * Strings.FObjects.fCount );
+ {$ENDIF}
+ end;
+end;
+
+procedure TStrListEx.Assign(Strings: PStrListEx);
+begin
+ inherited Assign( Strings );
+ FObjects.Assign( Strings.FObjects );
+end;
+
+procedure TStrListEx.Clear;
+begin
+ inherited;
+ FObjects.Clear;
+end;
+
+procedure TStrListEx.Delete(Idx: integer);
+begin
+ inherited;
+ if FObjects.fCount > Idx then // mdw: '>=' -> '>'
+ FObjects.Delete( Idx );
+end;
+
+procedure TStrListEx.DeleteLast;
+var C: Integer;
+begin
+ C := fCount;
+ if C <= 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ inherited;
+ if FObjects.fCount >= C then FObjects.Delete( C );
+end;
+
+
+function TStrListEx.LastObj: DWORD;
+begin
+ if Count = 0 then
+ Result := 0
+ else
+ Result := Objects[ Count - 1 ];
+end;
+
+function TStrListEx.AddObject(const S: AnsiString; Obj: DWORD): Integer;
+begin
+ Result := Count;
+ InsertObject( Count, S, Obj );
+end;
+
+procedure TStrListEx.InsertObject(Before: Integer; const S: AnsiString; Obj: DWORD);
+begin
+ Insert( Before, S );
+ ProvideObjCapacity( Before );
+ FObjects.Insert( Before, Pointer( Obj ) );
+end;
+
+function TStrListEx.IndexOfObj( Obj: Pointer ): Integer;
+begin
+ Result := FObjects.IndexOf( Obj );
+end;
+
+function WStrLen( W: PWideChar ): Integer;
+asm
+ XCHG EDI, EAX
+ XCHG EDX, EAX
+ OR ECX, -1
+ XOR EAX, EAX
+ CMP EAX, EDI
+ JE @@exit0
+ REPNE SCASW
+ DEC EAX
+ DEC EAX
+ SUB EAX, ECX
+@@exit0:
+ MOV EDI, EDX
+end;
+
+procedure TStrListEx.OptimizeForRead;
+begin
+ {$IFDEF TLIST_FAST}
+ if fList <> nil then
+ fList.OptimizeForRead;
+ if FObjects <> nil then
+ FObjects.OptimizeForRead;
+ {$ENDIF}
+end;
+
+{$IFDEF _D3orHigher}
+function UTF8_2KOLWideString( const s: AnsiString ): KOLWideString;
+var Buffer: PWideChar;
+ L: Integer;
+begin
+ L := Length( s ) + 1;
+ GetMem( Buffer, L * 2 );
+ MultiByteToWideChar( CP_UTF8, 0, PAnsiChar( s ), L-1,
+ Buffer, L );
+ Result := Buffer;
+ FreeMem( Buffer );
+end;
+{$ENDIF _D3orHigher}
+
+{------------------------------------------------------------------------------)
+| |
+| T W S t r L i s t |
+| |
+(------------------------------------------------------------------------------}
+
+{$IFDEF WIN_GDI}
+{$IFNDEF _D2}
+
+function NewWStrList: PWStrList;
+begin
+ new( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TWStrList';
+ {$ENDIF}
+end;
+
+{ TWStrList }
+
+function TWStrList.Add(const W: KOLWideString): Integer;
+begin
+ Result := Count;
+ Insert( Result, W );
+end;
+
+procedure TWStrList.AddWStrings(WL: PWStrList);
+begin
+ Text := Text + WL.Text;
+end;
+
+function TWStrList.AppendToFile(const Filename: KOLString): Boolean;
+var Strm: PStream;
+begin
+ Strm := NewReadWriteFileStream( Filename );
+ Result := Strm.Handle <> INVALID_HANDLE_VALUE;
+ if Result then
+ begin
+ Strm.Position := Strm.Size;
+ SaveToStream( Strm );
+ end;
+ Strm.Free;
+end;
+
+procedure TWStrList.Assign(WL: PWStrList);
+begin
+ Text := WL.Text;
+end;
+
+procedure TWStrList.Clear;
+var I: Integer;
+ P: Pointer;
+begin
+ for I := 0 to Count-1 do
+ begin
+ P := fList.Items[ I ];
+ if P <> nil then
+ if not( (P >= fText) and (P <= fText + fTextBufSz) ) then
+ FreeMem( P );
+ end;
+ if fText <> nil then
+ FreeMem( fText );
+ fText := nil;
+ fTextBufSz := 0;
+ fList.Clear;
+end;
+
+procedure TWStrList.Delete(Idx: Integer);
+var P: Pointer;
+begin
+ P := fList.Items[ Idx ];
+ if P <> nil then
+ if not( (P >= fText) and (P <= fText + fTextBufSz) ) then
+ FreeMem( P );
+ fList.Delete( Idx );
+end;
+
+destructor TWStrList.Destroy;
+begin
+ Clear;
+ fList.Free;
+ inherited;
+end;
+
+function TWStrList.GetCount: Integer;
+begin
+ Result := fList.Count;
+end;
+
+function TWStrList.GetItems(Idx: Integer): KOLWideString;
+begin
+ Result := PWideChar( fList.Items[ Idx ] );
+end;
+
+function TWStrList.GetPtrs(Idx: Integer): PWideChar;
+begin
+ Result := fList.Items[ Idx ];
+end;
+
+function TWStrList.GetText: KOLWideString;
+const
+ EoL: Array[ 0..5 ] of AnsiChar = ( #13, #0, #10, #0, #0, #0 ); // KOL_ANSI
+var
+ L, I: Integer;
+ P, Dest: Pointer;
+begin
+ L := 0;
+ for I := 0 to Count-1 do
+ begin
+ P := fList.Items[ I ];
+ if P <> nil then
+ L := L + WStrLen( P ) + 2
+ else
+ L := L + 2;
+ end;
+ SetLength( Result, L );
+ Dest := PWideChar( Result );
+ for I := 0 to Count-1 do
+ begin
+ P := fList.Items[ I ];
+ if P <> nil then
+ begin
+ WStrCopy( Dest, P );
+ Dest := Pointer( Integer( Dest ) + WStrLen( P ) * 2 );
+ end;
+ WStrCopy( Dest, Pointer( @ EoL[ 0 ] ) );
+ Dest := Pointer( Integer( Dest ) + 4 );
+ end;
+end;
+
+procedure TWStrList.Init;
+begin
+ fList := NewList;
+ fNameDelim := WideChar( DefaultNameDelimiter );
+end;
+
+procedure TWStrList.Insert(Idx: Integer; const W: KOLWideString);
+var P: Pointer;
+begin
+ while Idx > Count do // by Misha Shar. a.k.a. kreit
+ fList.Add( nil );
+ GetMem( P, (Length( W ) + 1) * Sizeof(WideChar) );
+ fList.Insert( Idx, P );
+ WStrCopy( P, PWideChar( W ) );
+end;
+
+function TWStrList.LoadFromFile(const Filename: KOLString): Boolean;
+begin
+ Clear;
+ Result := MergeFromFile( Filename );
+end;
+
+procedure TWStrList.LoadFromStream(Strm: PStream; AppendToList: Boolean);
+begin
+ if not AppendToList then Clear;
+ MergeFromStream( Strm );
+end;
+
+const
+ BOM : WideChar = #$FEFF;
+
+function TWStrList.MergeFromFile(const Filename: KOLString): Boolean;
+var Strm: PStream;
+ DBOM: WideChar;
+begin
+ Strm := NewReadFileStream( Filename );
+ Result := Strm.Handle <> INVALID_HANDLE_VALUE;
+ if Result then
+ begin
+ Strm.Read(DBOM, SizeOf(DBOM));
+ if DBOM<>BOM then Strm.Position := 0;
+ MergeFromStream( Strm );
+ end;
+ Strm.Free;
+end;
+
+procedure TWStrList.MergeFromStream(Strm: PStream);
+var Buf: KOLWideString;
+ L: Integer;
+begin
+ L := Strm.Size - Strm.Position;
+ {$IFDEF KOL_ASSERTIONS}
+ Assert( L mod 1 = 0, 'Wide strings streams must be of even length in bytes.' );
+ {$ENDIF KOL_ASSERTIONS}
+ if L = 0 then Exit;
+ SetLength( Buf, L div 2 );
+ Strm.Read( Buf[ 1 ], L );
+ Text := Text + Buf;
+end;
+
+procedure TWStrList.Move(IdxOld, IdxNew: Integer);
+begin
+ fList.MoveItem( IdxOld, IdxNew );
+end;
+
+procedure TWStrList.Put(Idx: integer; const Value: KOLWideString);
+begin
+ Delete( Idx );
+ Insert( Idx, Value );
+end;
+
+function TWStrList.SaveToFile(const Filename: KOLString): Boolean;
+var Strm: PStream;
+ DBOM: WideChar;
+begin
+ Strm := NewWriteFileStream( Filename );
+ Result := Strm.Handle <> INVALID_HANDLE_VALUE;
+ if Result then
+ begin
+ DBOM := BOM;
+ Strm.Write(DBOM, SizeOf(DBOM));
+ SaveToStream( Strm );
+ end;
+ Strm.Free;
+end;
+
+procedure TWStrList.SaveToStream(Strm: PStream);
+var Buf, Dest: PWideChar;
+ I, L, Sz: Integer;
+ P: Pointer;
+begin
+ Sz := 0;
+ for I := 0 to Count-1 do
+ begin
+ P := fList.Items[ I ];
+ if P <> nil then
+ Sz := Sz + WStrLen( P ) * 2 + 4
+ else
+ Sz := Sz + 4;
+ end;
+ GetMem( Buf, Sz );
+ Dest := Buf;
+ for I := 0 to Count-1 do
+ begin
+ P := fList.Items[ I ];
+ if P <> nil then
+ begin
+ L := WStrLen( P );
+ System.Move( P^, Dest^, L * 2 );
+ Inc( Dest, L );
+ end;
+ Dest^ := #13;
+ Inc( Dest );
+ Dest^ := #10;
+ Inc( Dest );
+ end;
+ Strm.Write( Buf^, Sz );
+ FreeMem( Buf );
+end;
+
+procedure TWStrList.SetItems(Idx: Integer; const Value: KOLWideString);
+var P: Pointer;
+begin
+ while Idx > Count-1 do
+ fList.Add( nil );
+ if WStrLen( ItemPtrs[ Idx ] ) > Length( Value ) then // fixed by kreit
+ WStrCopy( ItemPtrs[ Idx ], PWideChar( Value ) )
+ else
+ begin
+ P := fList.Items[ Idx ];
+ if P <> nil then
+ if not ((P >= fText) and (P <= fText + fTextBufSz)) then
+ FreeMem( P );
+ GetMem( P, (Length( Value ) + 1) * Sizeof(WideChar) );
+ fList.Items[ Idx ] := P;
+ WStrCopy( P, PWideChar( Value ) );
+ end;
+end;
+
+procedure TWStrList.SetText(const Value: KOLWideString);
+var L, N: Integer;
+ P: PWideChar;
+begin
+ Clear;
+ if Value = '' then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ L := (Length( Value ) + 1) * Sizeof( WideChar );
+ GetMem( fText, L );
+ System.Move( Value[ 1 ], fText^, L );
+ fTextBufSz := Length( Value );
+ fText[ fTextBufSz ] := #0;
+ N := 0;
+ P := fText;
+ while Word( P^ ) <> 0 do
+ begin
+ if (Word( P^ ) = 13) then
+ begin
+ Inc( N );
+ PWord( P )^ := 0;
+ if Word( P[ 1 ] ) = 10 then
+ begin
+ Inc( P );
+ //PWord( P )^ := 0;
+ end;
+ end
+ else
+ if (Word( P^ ) = 10) and ((P = fText) or (Word( P[ -1 ] ) <> 0)) then
+ begin
+ Inc( N );
+ PWord( P )^ := 0;
+ end;
+ Inc( P );
+ end;
+ fList.Capacity := N;
+ P := fText;
+ while P < fText + fTextBufSz do
+ begin
+ fList.Add( P );
+ while Word( P^ ) <> 0 do Inc( P );
+ Inc( P );
+ if Word( P^ ) = 10 then Inc( P );
+ end;
+end;
+
+function CompareWStrListItems( const Sender: Pointer; const Idx1, Idx2: DWORD ): Integer;
+var WL: PWStrList;
+begin
+ WL := Sender;
+ Result := WStrCmp( WL.fList.Items[ Idx1 ], WL.fList.Items[ Idx2 ] );
+end;
+
+function CompareWStrListItems_UpperCase( const Sender: Pointer; const Idx1, Idx2: DWORD ): Integer;
+var WL: PWStrList;
+ L1, L2, tL1, tL2: Integer;
+begin
+ WL := Sender;
+ L1 := WStrLen( WL.fList.Items[ Idx1 ] );
+ L2 := WStrLen( WL.fList.Items[ Idx2 ] );
+ tL1 := Length( WL.fTmp1 );
+ if tL1 <= L1 then
+ SetLength( WL.fTmp1, L1 + 1 );
+ tL2 := Length( WL.fTmp2 );
+ if tL2 <= L2 then
+ SetLength( WL.fTmp2, L2 + 1 );
+ if L1 > 0 then
+ Move( WL.fList.Items[ Idx1 ]^, WL.fTmp1[ 1 ], (L1 + 1) * 2 )
+ else
+ WL.fTmp1[ 1 ] := #0;
+ if L2 > 0 then
+ Move( WL.fList.Items[ Idx2 ]^, WL.fTmp2[ 1 ], (L2 + 1) * 2 )
+ else
+ WL.fTmp2[ 1 ] := #0;
+ CharUpperBuffW( PWideChar( WL.fTmp1 ), L1 );
+ CharUpperBuffW( PWideChar( WL.fTmp2 ), L2 );
+ Result := WStrCmp( PWideChar( WL.fTmp1 ), PWideChar( WL.fTmp2 ) );
+end;
+
+procedure SwapWStrListItems( const Sender: Pointer; const Idx1, Idx2: DWORD );
+var WL: PWStrList;
+begin
+ WL := Sender;
+ WL.Swap( Idx1, Idx2 );
+end;
+
+procedure TWStrList.Sort( CaseSensitive: Boolean );
+begin
+ if CaseSensitive then
+ SortData( @ Self, Count, @CompareWStrListItems, @SwapWStrListItems )
+ else
+ begin
+ SortData( @ Self, Count, @CompareWStrListItems_UpperCase, @SwapWStrListItems );
+ fTmp1 := '';
+ fTmp2 := '';
+ end;
+end;
+
+procedure TWStrList.Swap(Idx1, Idx2: Integer);
+begin
+ fList.Swap( Idx1, Idx2 );
+end;
+
+function TWStrList.IndexOf( const s: KOLWideString ): Integer;
+var i: Integer;
+ p: PWideChar;
+begin
+ if s = '' then
+ begin
+ for i := 0 to fList.fCount-1 do
+ begin
+ p := ItemPtrs[ i ];
+ if (p = nil) or
+ (p^ = #0) then
+ begin
+ Result := i; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ end
+ else
+ begin
+ for i := 0 to Count-1 do
+ begin
+ p := ItemPtrs[ i ];
+ if (p <> nil) and
+ (WStrCmp( PWideChar( s ), p ) = 0) then
+ begin
+ Result := i; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ end;
+ Result := -1;
+end;
+
+function TWStrList.IndexOf_NoCase( const s: KOLWideString ): Integer;
+var i: Integer;
+ p: PWideChar;
+begin
+ if s = '' then
+ begin
+ for i := 0 to fList.fCount-1 do
+ begin
+ p := ItemPtrs[ i ];
+ if (p = nil) or
+ (p^ = #0) then
+ begin
+ Result := i; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ end
+ else
+ begin
+ for i := 0 to Count-1 do
+ begin
+ p := ItemPtrs[ i ];
+ if (p <> nil) and
+ (WStrCmp_NoCase( PWideChar( s ), p ) = 0) then
+ begin
+ Result := i; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ end;
+ Result := -1;
+end;
+
+function TWStrList.Last: KOLWideString;
+begin
+ if Count <= 0 then Result := ''
+ else Result := Items[ Count-1 ];
+end;
+
+function NewWStrListEx: PWStrListEx;
+begin
+ new( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TWStrListEx';
+ {$ENDIF}
+end;
+
+function TWStrList.GetLineName(Idx: Integer): KOLWideString;
+var s: KOLWideString;
+ Q: PWideChar;
+begin
+ s := ItemPtrs[ Idx ];
+ Q := WStrScan( PWideChar(s), fNameDelim );
+ Q^ := #0;
+ Result := PWideChar(s);
+end;
+
+function TWStrList.GetLineValue(Idx: Integer): KOLWideString;
+var Q: PWideChar;
+begin
+ Q := ItemPtrs[ Idx ];
+ Q := WStrScan( Q, fNameDelim );
+ if Q <> nil then
+ inc( Q );
+ Result := Q;
+end;
+
+procedure TWStrList.SetLineName(Idx: Integer; const NV: KOLWideString);
+var del: KOLWideString;
+begin
+ del := fNameDelim;
+ Items[ Idx ] := NV + del + LineValue[ Idx ];
+end;
+
+procedure TWStrList.SetLineValue(Idx: Integer; const Value: KOLWideString);
+var del: KOLWideString;
+begin
+ del := fNameDelim;
+ Items[ Idx ] := LineName[ Idx ] + del + Value;
+end;
+
+procedure TWStrList.OptimizeForRead;
+begin
+ {$IFDEF TLIST_FAST}
+ if fList <> nil then
+ fList.OptimizeForRead;
+ {$ENDIF}
+end;
+
+function TWStrList.IndexOfName(AName: KOLWideString): Integer;
+var i: Integer;
+ L: Integer;
+ fCount: integer;
+begin
+ Result:=-1;
+ L := Length( AName );
+ if L > 0 then
+ begin
+ AName := WLowerCase( AName ) + fNameDelim;
+ Inc( L );
+ fCount := GetCount - 1;
+ for i := 0 to fCount do
+ begin
+ if _WStrLComp( PWideChar( WLowerCase( ItemPtrs[ i ] ) ), PWideChar( AName ), L ) = 0 then
+ begin
+ Result:=i; exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ end;
+end;
+
+procedure TWStrList.SetValue(const AName, Value: KOLWideString);
+var
+ I: Integer;
+begin
+ I := IndexOfName(AName);
+ if i=-1
+ then Add( AName + fNameDelim + Value )
+ else Items[i] := AName + fNameDelim + Value;
+end;
+
+function TWStrList.GetValue(const AName: KOLWideString): KOLWideString;
+var
+ i: Integer;
+begin
+ I := IndexOfName(AName);
+ if I >= 0
+ then Result := Copy(Items[i], Length(AName) + 2, Length(Items[i])-Length(AName)-1)
+ else Result := '';
+end;
+
+{ TWStrListEx }
+
+function TWStrListEx.AddObject(const S: KOLWideString; Obj: DWORD): Integer;
+begin
+ Result := Count;
+ InsertObject( Count, S, Obj );
+end;
+
+procedure TWStrListEx.AddWStrings(WL: PWStrListEx);
+var I: Integer;
+begin
+ {$IFDEF TLIST_FAST}
+ {$ELSE}
+ I := Count;
+ {$ENDIF}
+ if WL.FObjects.Count > 0 then
+ ProvideObjectsCapacity( Count );
+ inherited AddWStrings( WL );
+ if WL.FObjects.Count > 0 then
+ begin
+ {$IFDEF TLIST_FAST}
+ for I := 0 to WL.FObjects.Count-1 do
+ FObjects.Add( WL.fObjects.Items[ I ] );
+ {$ELSE}
+ ProvideObjectsCapacity( I + WL.FObjects.Count );
+ System.Move( WL.FObjects.FItems[ 0 ],
+ FObjects.FItems[ I ],
+ Sizeof( Pointer ) * WL.FObjects.Count );
+ {$ENDIF}
+ end;
+end;
+
+procedure TWStrListEx.Assign(WL: PWStrListEx);
+begin
+ inherited Assign( WL );
+ FObjects.Assign( WL.FObjects );
+end;
+
+procedure TWStrListEx.Clear;
+begin
+ inherited Clear;
+ FObjects.Clear;
+end;
+
+procedure TWStrListEx.Delete(Idx: Integer);
+begin
+ inherited Delete( Idx );
+ if FObjects.FCount >= Idx then
+ FObjects.Delete( Idx );
+end;
+
+destructor TWStrListEx.Destroy;
+begin
+ fObjects.Free;
+ inherited;
+end;
+
+function TWStrListEx.GetObjects(Idx: Integer): DWORD;
+begin
+ Result := DWORD( fObjects.Items[ Idx ] );
+end;
+
+function TWStrListEx.IndexOfObj(Obj: Pointer): Integer;
+begin
+ Result := FObjects.IndexOf( Obj );
+end;
+
+procedure TWStrListEx.Init;
+begin
+ inherited;
+ fObjects := NewList;
+end;
+
+procedure TWStrListEx.InsertObject(Before: Integer; const S: KOLWideString;
+ Obj: DWORD);
+begin
+ Insert( Before, S );
+ FObjects.Insert( Before, Pointer( Obj ) );
+end;
+
+procedure TWStrListEx.Move(IdxOld, IdxNew: Integer);
+begin
+ fList.MoveItem( IdxOld, IdxNew );
+ if FObjects.FCount >= Min( IdxOld, IdxNew ) then
+ begin
+ ProvideObjectsCapacity( Max( IdxOld, IdxNew ) + 1 );
+ FObjects.MoveItem( IdxOld, IdxNew );
+ end;
+end;
+
+procedure SwapWStrListExItems( const Sender: Pointer; const Idx1, Idx2: DWORD );
+var WL: PWStrListEx;
+begin
+ WL := Sender;
+ WL.Swap( Idx1, Idx2 );
+end;
+
+procedure TWStrListEx.Sort(CaseSensitive: Boolean);
+begin
+ if CaseSensitive then
+ SortData( @ Self, Count, @CompareWStrListItems, @SwapWStrListExItems )
+ else
+ begin
+ SortData( @ Self, Count, @CompareWStrListItems_UpperCase, @SwapWStrListExItems );
+ fTmp1 := '';
+ fTmp2 := '';
+ end;
+end;
+
+procedure TWStrListEx.Swap(Idx1, Idx2: Integer);
+begin
+ inherited Swap( Idx1, Idx2 );
+ if FObjects.fCount >= Min( Idx1, Idx2 ) then
+ begin
+ ProvideObjectsCapacity( max( Idx1, Idx2 ) + 1 );
+ FObjects.Swap( Idx1, Idx2 );
+ end;
+end;
+
+procedure TWStrListEx.OptimizeForRead;
+begin
+ {$IFDEF TLIST_FAST}
+ if fList <> nil then
+ fList.OptimizeForRead;
+ if FObjects <> nil then
+ FObjects.OptimizeForRead;
+ {$ENDIF}
+end;
+
+procedure TWStrListEx.ProvideObjectsCapacity(NewCap: Integer);
+begin
+ if fObjects.Capacity >= NewCap then Exit;
+ fObjects.Capacity := NewCap;
+ {$IFDEF TLIST_FAST}
+ {$ELSE}
+ ZeroMemory( @FObjects.FItems[ FObjects.Count ],
+ (FObjects.Capacity - FObjects.Count) * Sizeof( Pointer ) );
+ FObjects.FCount := NewCap;
+ {$ENDIF}
+end;
+
+procedure TWStrListEx.SetObjects(Idx: Integer; const Value: DWORD);
+begin
+ ProvideObjectsCapacity( Idx + 1 );
+ fObjects.Items[ Idx ] := Pointer( Value );
+end;
+
+{$ENDIF}
+{$ENDIF WIN_GDI}
+
+function NewKOLStrList: PKOLStrList;
+begin
+ new( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TKOLStrList';
+ {$ENDIF}
+end;
+
+function NewKOLStrListEx: PKOLStrListEx;
+begin
+ new( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TKOLStrListEx';
+ {$ENDIF}
+end;
+
+//////////////////////////////////////////////////////////////////////////
+// S O R T I N G
+//////////////////////////////////////////////////////////////////////////
+
+{ -- qsort -- }
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure SortData( const Data: Pointer; const uNElem: Dword;
+ const CompareFun: TCompareEvent;
+ const SwapProc: TSwapEvent );
+{ uNElem - number of elements to sort }
+
+ function Compare( const e1, e2 : DWord ) : Integer;
+ begin
+ Result := CompareFun( Data, e1 - 1, e2 - 1 );
+ end;
+
+ procedure Swap( const e1, e2 : DWord );
+ begin
+ SwapProc( Data, e1 - 1, e2 - 1 );
+ end;
+
+ procedure qSortHelp(pivotP: Dword; nElem: Dword);
+ label
+ TailRecursion,
+ qBreak;
+ var
+ leftP, rightP, pivotEnd, pivotTemp, leftTemp: Dword;
+ lNum: Dword;
+ retval: integer;
+ begin
+ TailRecursion:
+ if (nElem <= 2) then
+ begin
+ if (nElem = 2) then
+ begin
+ rightP := pivotP +1;
+ retval := Compare(pivotP,rightP);
+ if (retval > 0) then Swap(pivotP,rightP);
+ end;
+ exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ rightP := (nElem -1) + pivotP;
+ leftP := (nElem shr 1) + pivotP;
+ { sort pivot, left, and right elements for "median of 3" }
+ retval := Compare(leftP,rightP);
+ if (retval > 0) then Swap(leftP, rightP);
+ retval := Compare(leftP,pivotP);
+
+ if (retval > 0) then
+ Swap(leftP, pivotP)
+ else
+ begin
+ retval := Compare(pivotP,rightP);
+ if retval > 0 then Swap(pivotP, rightP);
+ end;
+ if (nElem = 3) then
+ begin
+ Swap(pivotP, leftP); exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ { now for the classic Horae algorithm }
+ pivotEnd := pivotP + 1;
+ leftP := pivotEnd;
+ repeat
+
+ retval := Compare(leftP, pivotP);
+ while (retval <= 0) do
+ begin
+
+ if (retval = 0) then
+ begin
+ Swap(leftP, pivotEnd);
+ Inc(pivotEnd);
+ end;
+ if (leftP < rightP) then
+ Inc(leftP)
+ else
+ goto qBreak;
+ retval := Compare(leftP, pivotP);
+ end; {while}
+ while (leftP < rightP) do
+ begin
+ retval := Compare(pivotP, rightP);
+ if (retval < 0) then
+ Dec(rightP)
+
+ else
+ begin
+ Swap(leftP, rightP);
+ if (retval <> 0) then
+ begin
+ Inc(leftP);
+ Dec(rightP);
+ end;
+ break;
+ end;
+ end; {while}
+
+ until (leftP >= rightP);
+ qBreak:
+ retval := Compare(leftP,pivotP);
+ if (retval <= 0) then Inc(leftP);
+
+ leftTemp := leftP -1;
+ pivotTemp := pivotP;
+ while ((pivotTemp < pivotEnd) and (leftTemp >= pivotEnd)) do
+ begin
+ Swap(pivotTemp, leftTemp);
+ Inc(pivotTemp);
+ Dec(leftTemp);
+ end; {while}
+ lNum := (leftP - pivotEnd);
+ nElem := ((nElem + pivotP) -leftP);
+
+ if (nElem < lNum) then
+ begin
+ qSortHelp(leftP, nElem);
+ nElem := lNum;
+ end
+ else
+ begin
+ qSortHelp(pivotP, lNum);
+ pivotP := leftP;
+ end;
+ goto TailRecursion;
+ end; {qSortHelp }
+
+begin
+ if (uNElem < 2) then exit; { nothing to sort } {>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ qSortHelp(1, uNElem);
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF _D3orHigher}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+procedure SortArray( const Data: Pointer; const uNElem: Dword;
+ const CompareFun: TCompareArrayEvent );
+{ uNElem - number of elements to sort }
+type TDWORDArray = array[0..0] of Integer;
+ PDWORDArray = ^TDWORDArray;
+var DataArray: PDWORDArray;
+
+ procedure SwapIdx( const e1, e2 : DWord );
+ begin
+ Swap( DataArray[e1], DataArray[e2] );
+ end;
+
+ procedure qSortArrayHelp(pivotP: Dword; nElem: Dword);
+ label
+ TailRecursion,
+ qBreak;
+ var
+ leftP, rightP, pivotEnd, pivotTemp, leftTemp: Dword;
+ lNum: Dword;
+ retval: integer;
+ begin
+ TailRecursion:
+ if (nElem <= 2) then
+ begin
+ if (nElem = 2) then
+ begin
+ rightP := pivotP +1;
+ retval := CompareFun(DataArray[pivotP],DataArray[rightP]);
+ if (retval > 0) then SwapIdx(pivotP,rightP);
+ end;
+ exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ rightP := (nElem -1) + pivotP;
+ leftP := (nElem shr 1) + pivotP;
+ { sort pivot, left, and right elements for "median of 3" }
+ retval := CompareFun(DataArray[leftP],DataArray[rightP]);
+ if (retval > 0) then SwapIdx(leftP, rightP);
+ retval := CompareFun(DataArray[leftP],DataArray[pivotP]);
+
+ if (retval > 0) then
+ SwapIdx(leftP, pivotP)
+ else
+ begin
+ retval := CompareFun(DataArray[pivotP],DataArray[rightP]);
+ if retval > 0 then SwapIdx(pivotP, rightP);
+ end;
+ if (nElem = 3) then
+ begin
+ SwapIdx(pivotP, leftP); exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ { now for the classic Horae algorithm }
+ pivotEnd := pivotP + 1;
+ leftP := pivotEnd;
+ repeat
+
+ retval := CompareFun(DataArray[leftP], DataArray[pivotP]);
+ while (retval <= 0) do
+ begin
+
+ if (retval = 0) then
+ begin
+ SwapIdx(leftP, pivotEnd);
+ Inc(pivotEnd);
+ end;
+ if (leftP < rightP) then
+ Inc(leftP)
+ else
+ goto qBreak;
+ retval := CompareFun(DataArray[leftP], DataArray[pivotP]);
+ end; {while}
+ while (leftP < rightP) do
+ begin
+ retval := CompareFun(DataArray[pivotP], DataArray[rightP]);
+ if (retval < 0) then
+ Dec(rightP)
+
+ else
+ begin
+ SwapIdx(leftP, rightP);
+ if (retval <> 0) then
+ begin
+ Inc(leftP);
+ Dec(rightP);
+ end;
+ break;
+ end;
+ end; {while}
+
+ until (leftP >= rightP);
+ qBreak:
+ retval := CompareFun( DataArray[leftP], DataArray[pivotP] );
+ if (retval <= 0) then Inc(leftP);
+
+ leftTemp := leftP -1;
+ pivotTemp := pivotP;
+ while ((pivotTemp < pivotEnd) and (leftTemp >= pivotEnd)) do
+ begin
+ SwapIdx(pivotTemp, leftTemp);
+ Inc(pivotTemp);
+ Dec(leftTemp);
+ end; {while}
+ lNum := (leftP - pivotEnd);
+ nElem := ((nElem + pivotP) -leftP);
+
+ if (nElem < lNum) then
+ begin
+ qSortArrayHelp(leftP, nElem);
+ nElem := lNum;
+ end
+ else
+ begin
+ qSortArrayHelp(pivotP, lNum);
+ pivotP := leftP;
+ end;
+ goto TailRecursion;
+ end; {qSortHelp }
+
+begin
+ DataArray := Pointer( Integer( Data ) - Sizeof( DWORD ) );
+ if (uNElem < 2) then exit; { nothing to sort } {>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ qSortArrayHelp(1, uNElem);
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF _D3orHigher}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function CompareIntegers( const Sender : Pointer; const e1, e2 : DWORD ) : Integer;
+var I1, I2 : Integer;
+begin
+ I1 := PInteger( DWORD( Sender ) + e1 * Sizeof( Integer ) )^;
+ I2 := PInteger( DWORD( Sender ) + e2 * Sizeof( Integer ) )^;
+ Result := 0;
+ if I1 < I2 then Result := -1
+ else
+ if I1 > I2 then Result := 1;
+end;
+{$ENDIF PAS_VERSION}
+
+function Compare2Integers( e1, e2: Integer ) : Integer;
+begin
+ Result := e1-e2;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function CompareDwords( const Sender : Pointer; const e1, e2 : DWORD ) : Integer;
+var I1, I2 : DWord;
+begin
+ I1 := PDWORD( DWORD( Sender ) + e1 * Sizeof( Integer ) )^;
+ I2 := PDWORD( DWORD( Sender ) + e2 * Sizeof( Integer ) )^;
+ Result := 0;
+ if I1 < I2 then Result := -1
+ else
+ if I1 > I2 then Result := 1;
+end;
+{$ENDIF PAS_VERSION}
+
+function Compare2Dwords( e1, e2 : DWORD ) : Integer; forward;
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+function Compare2Dwords( e1, e2 : DWORD ) : Integer;
+begin
+ if e1 < e2 then
+ Result := -1
+ else
+ if e1 > e2 then
+ Result := 1
+ else
+ Result := 0;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure SwapIntegers( const Sender : Pointer; const e1, e2 : DWORD );
+var Tmp : Integer;
+begin
+ Tmp := PInteger( DWORD( Sender ) + e1 * SizeOf( Integer ) )^;
+ PInteger( DWORD( Sender ) + e1 * Sizeof( Integer ) )^ :=
+ PInteger( DWORD( Sender ) + e2 * Sizeof( Integer ) )^;
+ PInteger( DWORD( Sender ) + e2 * Sizeof( Integer ) )^ := Tmp;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure SortIntegerArray( var A : array of Integer );
+begin
+ {$IFDEF SPEED_FASTER}
+ SortArray( @A[ 0 ], High(A)-Low(A)+1, @Compare2Integers );
+ {$ELSE}
+ SortData( @A[ 0 ], High(A)-Low(A)+1, @CompareIntegers, @SwapIntegers );
+ {$ENDIF}
+end;
+
+procedure SwapListItems( const L: Pointer; const e1, e2: DWORD );
+begin
+ PList( L ).Swap( e1, e2 );
+end;
+
+procedure SortDwordArray( var A : array of DWORD );
+begin
+ {$IFDEF SPEED_FASTER}
+ SortArray( @A[ 0 ], High(A)-Low(A)+1, @Compare2DWORDS );
+ {$ELSE}
+ SortData( @A[ 0 ], High(A)-Low(A)+1, @CompareDwords, @SwapIntegers );
+ {$ENDIF}
+end;
+{$IFDEF WIN_GDI}
+
+{ -- status bar implementation -- }
+
+function _NewStatusbar( AParent: PControl ): PControl; forward;
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function _NewStatusbar( AParent: PControl ): PControl;
+var Style: DWORD;
+begin
+ Style := WS_CHILD or WS_CLIPSIBLINGS or WS_CLIPCHILDREN or 3 or WS_VISIBLE;
+ if {$IFDEF USE_FLAGS} G3_SizeGrip in AParent.fFlagsG3
+ {$ELSE} AParent.fSizeGrip {$ENDIF} then
+ Style := (Style or SBARS_SIZEGRIP) and not 3;
+ Result := _NewCommonControl( AParent, STATUSCLASSNAME,
+ Style, FALSE,
+ {$IFDEF PACK_COMMANDACTIONS} PAnsiChar( OTHER_ACTIONS )
+ {$ELSE} nil {$ENDIF} );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:StatusBar';
+ {$ENDIF}
+ with Result.fBoundsRect do
+ begin
+ Left := 0;
+ Right := 0;
+ Top := 0;
+ Bottom := 0;
+ end;
+ Result.fAlign := caBottom;
+ {$IFDEF USE_FLAGS} include( Result.fFlagsG4, G4_NotUseAlign );
+ {$ELSE} Result.fNotUseAlign := True; {$ENDIF}
+ {$IFDEF TEST_VERSION}
+ Result.fTag := DWORD( PAnsiChar( 'Status bar' ) );
+ {$ENDIF}
+ InitCommonControlSizeNotify( Result );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetStatusText(Index: Integer; const Value: KOLString);
+var ch: Integer;
+ R : TRect;
+ N, I, L, W : Integer;
+ WidthsBuf: array[ 0..254 ] of Integer;
+ Val: Integer;
+begin
+ if fStatusCtl = nil then
+ begin
+ ch := GetClientHeight;
+ fStatusCtl := _NewStatusBar( @Self );
+ fStatusCtl.Perform( SB_SIMPLE, Integer( LongBool( Index = 255 ) ), 0 );
+ GetWindowRect( {fStatusWnd}fStatusCtl.fHandle, R );
+ fClientBottom := R.Bottom - R.Top;
+ SetClientHeight( ch );
+ fStatusCtl.Perform( WM_SIZE, 0, 0 );
+ end;
+ if Index < 255 then
+ begin
+ N := fStatusCtl.Perform( SB_GETPARTS, 0, 0 );
+ if N <= Index then
+ begin
+ W := Width;
+ L := W div (Index + 1);
+ W := L;
+ for I := 0 to Index - 1 do
+ begin
+ WidthsBuf[ I ] := W;
+ Inc( W, L );
+ end;
+ WidthsBuf[ Index ] := -1;
+ fStatusCtl.Perform( SB_SETPARTS, Index + 1, Integer( @WidthsBuf[ 0 ] ) );
+ end;
+ fStatusCtl.Perform( SB_SIMPLE, 0, 0 );
+ end;
+ Val := 0;
+ if Value <> '' then
+ Val := Integer( @ Value[1] );
+ fStatusCtl.Perform(
+ {$IFDEF UNICODE_CTRLS} SB_SETTEXTW {$ELSE} SB_SETTEXT {$ENDIF}, Index, Val );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF noASM_UNICODE}
+function TControl.GetStatusText( Index: Integer ): KOLString;
+asm
+ MOV ECX, [EAX].fStatusCtl
+ JECXZ @@exit
+ PUSH EBX
+ PUSH ESI
+ XCHG ESI, EAX // ESI = @Self
+ MOV EBX, EDX // EBX = Index
+ XOR EAX, EAX
+ XCHG EAX, [ESI].fStatusTxt
+ TEST EAX, EAX
+ JZ @@1
+ CALL System.@FreeMem
+@@1:
+ XOR EAX, EAX
+ CDQ
+ MOV DL, WM_GETTEXTLENGTH
+ PUSH WM_GETTEXT
+ CMP EBX, 255
+ JZ @@2
+ POP EAX
+ MOV EAX, EBX
+ MOV DX, SB_GETTEXTLENGTH
+ PUSH SB_GETTEXT
+@@2:
+ MOV EBX, EAX
+ PUSH 0
+ PUSH EAX
+ PUSH EDX
+ PUSH [ESI].fStatusCtl
+ CALL Perform
+ TEST AX, AX
+ JZ @@get_rslt
+ PUSH EAX
+ INC EAX
+ CALL System.@GetMem
+ POP EDX
+ MOV [ESI].fStatusTxt, EAX
+ MOV byte ptr [EAX+EDX], 0
+ POP EDX // Msg
+ PUSH EAX
+ PUSH EBX
+ PUSH EDX
+ PUSH [ESI].fStatusCtl
+ CALL Perform
+ PUSH EDX
+@@get_rslt:
+ POP EDX
+ MOV ECX, [ESI].fStatusTxt
+ POP ESI
+ POP EBX
+@@exit: XCHG EAX, ECX
+end;
+{$ELSE PAS_VERSION} //Pascal
+function TControl.GetStatusText( Index: Integer ): KOLString;
+var L, I: Integer;
+ Msg: DWORD;
+begin
+ Result := '';
+ if fStatusCtl = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Msg := SB_GETTEXTLENGTH;
+ I := Index;
+ if Index = 255 then
+ begin
+ Msg := WM_GETTEXTLENGTH;
+ I := 0;
+ end;
+ L := fStatusCtl.Perform( Msg, I, 0 ) and $FFFF;
+ if L > 0 then
+ begin
+ SetLength( Result, L );
+ Msg := {$IFDEF UNICODE_CTRLS} SB_GETTEXTW {$ELSE} SB_GETTEXT {$ENDIF};
+ if Index = 255 then
+ Msg := WM_GETTEXT;
+ fStatusCtl.Perform( Msg, I, Integer( @ Result[1] ) );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.RemoveStatus;
+var ch: Integer;
+begin
+ if fStatusCtl = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ ch := ClientHeight;
+ fStatusCtl.Free;
+ fStatusCtl := nil;
+ fClientBottom := 0;
+ ClientHeight := ch;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.StatusPanelCount: Integer;
+begin
+ Result := 0;
+ if fStatusCtl = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := fStatusCtl.Perform( SB_GETPARTS, 0, 0 );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetStatusPanelX(Idx: Integer): Integer;
+var Buf: array[0..254] of Integer;
+ N : Integer;
+begin
+ Result := 0;
+ if fStatusCtl = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ N := fStatusCtl.Perform( SB_GETPARTS, 255, Integer( @Buf[ 0 ] ) );
+ if N <= Idx then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := Buf[ Idx ];
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetStatusPanelX(Idx: Integer; const Value: Integer);
+var Buf: array[0..254] of Integer;
+ N : Integer;
+begin
+ if fStatusCtl = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ N := fStatusCtl.Perform( SB_GETPARTS, 255, Integer( @Buf[ 0 ] ) );
+ if N <= Idx then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Buf[ Idx ] := Value;
+ fStatusCtl.Perform( SB_SETPARTS, N, Integer( @Buf[ 0 ] ) );
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TControl.SetColor1(const Value: TColor);
+begin
+ DF.fColor1 := Value;
+ Invalidate;
+end;
+
+procedure TControl.SetColor2(const Value: TColor);
+begin
+ DF.fColor2 := Value;
+ Invalidate;
+end;
+
+procedure TControl.SetGradientLayout(const Value: TGradientLayout);
+begin
+ DF.fGradientLayout := Value;
+ Invalidate;
+end;
+
+procedure TControl.SetGradientStyle(const Value: TGradientStyle);
+begin
+ DF.fGradientStyle := Value;
+ Invalidate;
+end;
+
+{ -- Image List -- }
+
+{$IFDEF USE_CONSTRUCTORS}
+function NewImageList( AOwner: PControl ): PImageList;
+begin
+ new( Result, CreateImageList( AOwner ) );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TImageList';
+ {$ENDIF}
+end;
+{$ELSE not_USE_CONSTRUCTORS}
+function NewImageList( AOwner: PControl ): PImageList;
+begin
+ {*************} DoInitCommonControls( ICC_WIN95_CLASSES );
+ New( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TImageList';
+ {$ENDIF}
+ Result.FAllocBy := 1;
+ Result.FMasked := True;
+ Result.fBkColor := clNone;
+ //ImageList_SetBkColor( Result.FHandle, CLR_NONE );
+ Result.FImgWidth := 32;
+ Result.FImgHeight := 32;
+ Result.FColors := ilcDefault;
+
+ if AOwner = nil then exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result.fNext := PImageList( AOwner.fImageList );
+ if AOwner.fImageList <> nil then
+ PImageList( AOwner.fImageList ).fPrev := Result;
+ Result.FControl := AOwner;
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ AOwner.Add2AutoFree( Result );
+ {$ENDIF}
+ AOwner.fImageList := Result;
+end;
+{$ENDIF USE_CONSTRUCTORS}
+
+function ImageList_Create; stdcall; external cctrl name 'ImageList_Create';
+function ImageList_Destroy; external cctrl name 'ImageList_Destroy';
+function ImageList_GetImageCount; external cctrl name 'ImageList_GetImageCount';
+function ImageList_SetImageCount; external cctrl name 'ImageList_SetImageCount';
+function ImageList_Add; external cctrl name 'ImageList_Add';
+function ImageList_ReplaceIcon; external cctrl name 'ImageList_ReplaceIcon';
+function ImageList_SetBkColor; external cctrl name 'ImageList_SetBkColor';
+function ImageList_GetBkColor; external cctrl name 'ImageList_GetBkColor';
+function ImageList_SetOverlayImage; external cctrl name 'ImageList_SetOverlayImage';
+function ImageList_Draw; external cctrl name 'ImageList_Draw';
+function ImageList_Replace; external cctrl name 'ImageList_Replace';
+function ImageList_AddMasked; external cctrl name 'ImageList_AddMasked';
+function ImageList_DrawEx; external cctrl name 'ImageList_DrawEx';
+function ImageList_Remove; external cctrl name 'ImageList_Remove';
+function ImageList_GetIcon; external cctrl name 'ImageList_GetIcon';
+{$IFDEF UNICODE_CTRLS}
+function ImageList_LoadImage; external cctrl name 'ImageList_LoadImageW';
+{$ELSE}
+function ImageList_LoadImage; external cctrl name 'ImageList_LoadImageA';
+{$ENDIF}
+function ImageList_BeginDrag; external cctrl name 'ImageList_BeginDrag';
+function ImageList_EndDrag; external cctrl name 'ImageList_EndDrag';
+function ImageList_DragEnter; external cctrl name 'ImageList_DragEnter';
+function ImageList_DragLeave; external cctrl name 'ImageList_DragLeave';
+function ImageList_DragMove; external cctrl name 'ImageList_DragMove';
+function ImageList_SetDragCursorImage; external cctrl name 'ImageList_SetDragCursorImage';
+function ImageList_DragShowNolock; external cctrl name 'ImageList_DragShowNolock';
+function ImageList_GetDragImage; external cctrl name 'ImageList_GetDragImage';
+function ImageList_GetIconSize; external cctrl name 'ImageList_GetIconSize';
+function ImageList_SetIconSize; external cctrl name 'ImageList_SetIconSize';
+function ImageList_GetImageInfo; external cctrl name 'ImageList_GetImageInfo';
+function ImageList_Merge; external cctrl name 'ImageList_Merge';
+
+function ImageList_AddIcon(ImageList: HImageList; Icon: HIcon): Integer;
+begin
+ Result := ImageList_ReplaceIcon(ImageList, -1, Icon);
+end;
+
+function Index2OverlayMask(Index: Integer): Integer;
+begin
+ Result := Index shl 8;
+end;
+
+{ macros }
+procedure ImageList_RemoveAll(ImageList: HImageList); stdcall;
+begin
+ ImageList_Remove(ImageList, -1);
+end;
+
+function ImageList_ExtractIcon(Instance: THandle; ImageList: HImageList;
+ Image: Integer): HIcon; stdcall;
+begin
+ Result := ImageList_GetIcon(ImageList, Image, 0);
+end;
+
+function ImageList_LoadBitmap(Instance: THandle; Bmp: PKOLChar;
+ CX, Grow: Integer; Mask: TColorRef): HImageList; stdcall;
+begin
+ Result := ImageList_LoadImage(Instance, Bmp, CX, Grow, Mask, IMAGE_BITMAP, 0);
+end;
+
+procedure FreeBmp( Bmp: HBitmap );
+begin
+ DeleteObject( Bmp );
+end;
+
+function LoadBmp( Instance: Integer; Rsrc: PKOLChar; MasterObj: PObj ): HBitmap;
+{$IFDEF LOAD_RLE_BMP_RSRCES}
+var B: PBitmap;
+ R: PStream;
+{$ENDIF}
+begin
+ {$IFDEF LOAD_RLE_BMP_RSRCES}
+ R := NewMemoryStream;
+ Resource2Stream( R, hInstance, Rsrc, RT_BITMAP );
+ B := NewBitmap( 0, 0 );
+ R.Position := 0;
+ B.LoadFromStreamEx( R );
+ R.Free;
+ //B.SaveToFile( GetStartDir + 'test_loadbmp.bmp' );
+ Result := B.ReleaseHandle;
+ B.Free;
+ {$ELSE}
+ Result := LoadBitmap( Instance, Rsrc );
+ {$ENDIF}
+ MasterObj.Add2AutoFreeEx( TObjectMethod( MakeMethod( Pointer( Result ), @ FreeBmp ) ) );
+end;
+
+function LoadBmp32( Instance: Integer; Rsrc: PKOLChar; MasterObj: PObj ): HBitmap;
+var B: PBitmap;
+begin
+ B := NewBitmap( 0, 0 );
+ B.Handle := LoadBmp( Instance, Rsrc, MasterObj );
+ B.PixelFormat := pf32bit;
+ Result := B.ReleaseHandle;
+ B.Free;
+end;
+
+{ TImageList }
+
+function TImageList.Add(Bmp, Msk: HBitmap): Integer;
+begin
+ Result := -1;
+ if not HandleNeeded then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := ImageList_Add( FHandle, Bmp, Msk );
+end;
+
+function TImageList.AddIcon(Ico: HIcon): Integer;
+{var Bmp : HBitmap;
+ DC : HDC;}
+begin
+ Result := -1;
+ if ImgWidth = 0 then
+ ImgWidth := 32;
+ if ImgHeight = 0 then
+ ImgHeight := 32;
+ if not HandleNeeded then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := ImageList_AddIcon( fHandle, Ico );
+end;
+
+function TImageList.AddMasked(Bmp: HBitmap; Color: TColor): Integer;
+{$IFDEF TEST_IL}
+var B: PBitmap;
+{$ENDIF}
+begin
+ Result := -1;
+ if not HandleNeeded then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$IFDEF TEST_IL}
+ B := NewBitmap( 0, 0 );
+ B.Handle := Bmp;
+ B.PixelFormat := pf32bit;
+ B.SaveToFile( GetStartDir + 'test_Add_masked1.bmp' );
+ Bmp := B.ReleaseHandle;
+ B.Free;
+ {$ENDIF}
+ Result := ImageList_AddMasked( FHandle, Bmp, Color2RGB( Color ) );
+ {$IFDEF TEST_IL}
+ B := NewBitmap( 0, 0 );
+ B.Handle := GetBitmap;
+ B.SaveToFile( GetStartDir + 'test_Add_masked2.bmp' );
+ B.ReleaseHandle;
+ B.Free;
+ B := NewBitmap( 0, 0 );
+ B.Handle := GetMask;
+ B.SaveToFile( GetStartDir + 'test_Add_masked3.bmp' );
+ B.ReleaseHandle;
+ B.Free;
+ {$ENDIF}
+end;
+
+procedure TImageList.Clear;
+begin
+ Handle := 0;
+end;
+
+procedure TImageList.Delete(Idx: Integer);
+begin
+ if FHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ ImageList_Remove( FHandle, Idx );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+destructor TImageList.Destroy;
+begin
+ Clear;
+ if fNext <> nil then
+ fNext.fPrev := fPrev;
+ if fPrev <> nil then
+ fPrev.fNext := fNext;
+ if fControl <> nil then
+ begin
+ if PControl( fControl ).fImageList = @Self then
+ PControl( fControl ).fImageList := fNext;
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ PControl(fControl).RemoveFromAutoFree( @ Self );
+ {$ENDIF}
+ end;
+ inherited;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TImageList.Draw(Idx: Integer; DC: HDC; X, Y: Integer);
+begin
+ if FHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ ImageList_Draw( FHandle, Idx, DC, X, Y, GetDrawStyle );
+end;
+
+function TImageList.ExtractIcon(Idx: Integer): HIcon;
+begin
+ Result := ImageList_ExtractIcon( 0, FHandle, Idx );
+end;
+
+function TImageList.ExtractIconEx(Idx: Integer): HIcon;
+begin
+ Result := ImageList_GetIcon( FHandle, Idx, GetDrawStyle );
+end;
+
+function TImageList.GetBitmap: HBitmap;
+var II : TImageInfo;
+begin
+ Result := 0;
+ if FHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if ImageList_GetImageInfo( FHandle, 0, II ) then
+ Result := II.hbmImage;
+end;
+
+function TImageList.GetBkColor: TColor;
+begin
+ Result := fBkColor;
+ if FHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := ImageList_GetBkColor( FHandle );
+end;
+
+function TImageList.GetCount: Integer;
+begin
+ Result := 0;
+ if FHandle <> 0 then
+ Result := ImageList_GetImageCount( FHandle );
+end;
+
+function TImageList.GetDrawStyle: DWord;
+begin
+ Result := 0;
+ if dsBlend25 in DrawingStyle then
+ Result := Result or ILD_BLEND25;
+ if dsBlend50 in DrawingStyle then
+ Result := Result or ILD_BLEND50;
+ if dsTransparent in DrawingStyle then
+ Result := Result or ILD_TRANSPARENT
+ else
+ if dsMask in DrawingStyle then
+ Result := Result or ILD_MASK
+ {else Result := Result or ILD_NORMAL}; // ILD_NORMAL = 0
+ Result := Result or WORD(FOverlayIdx shl 8);
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TImageList.GetHandle: THandle;
+begin
+ HandleNeeded;
+ Result := FHandle;
+end;
+{$ENDIF PAS_VERSION}
+
+function TImageList.GetMask: HBitmap;
+var II : TImageInfo;
+begin
+ Result := 0;
+ if FHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if ImageList_GetImageInfo( FHandle, 0, II ) then
+ Result := II.hbmMask;
+end;
+
+{$IFDEF ASM_noVERSION}
+function TImageList.HandleNeeded: Boolean;
+const ColorFlags : array[ TImageListColors ] of Byte = ( ILC_COLOR,
+ ILC_COLOR4, ILC_COLOR8, ILC_COLOR16, ILC_COLOR24,
+ ILC_COLOR32, ILC_COLORDDB );
+asm
+ MOV ECX, [EAX].FHandle
+ JECXZ @@make_handle
+ MOV AL, 1
+ RET
+@@make_handle:
+ MOV ECX, [EAX].fImgWidth
+ JECXZ @@ret_ECX
+ MOV EDX, ECX
+ MOV ECX, [EAX].fImgHeight
+ JECXZ @@ret_ECX
+ PUSH EBX
+ XCHG EBX, EAX
+
+ PUSH [EBX].FAllocBy
+ PUSH 0
+ MOVZX EAX, [EBX].FColors
+ MOVZX EAX, byte ptr [ColorFlags+EAX]
+ CMP [EBX].FMasked, 0
+ JZ @@flags_ready
+ {$IFDEF PARANOIA} DB $0C, $01 {$ELSE} OR AL, 1 {$ENDIF}
+@@flags_ready:
+ PUSH EAX
+ PUSH ECX
+ PUSH EDX
+ CALL ImageList_Create
+ MOV [EBX].FHandle, EAX
+ XCHG ECX, EAX
+ POP EBX
+@@ret_ECX:
+ TEST ECX, ECX
+ SETNZ AL
+end;
+{$ELSE PAS_VERSION} //Pascal
+function TImageList.HandleNeeded: Boolean;
+const ColorFlags : array[ TImageListColors ] of Byte = ( ILC_COLOR,
+ ILC_COLOR4, ILC_COLOR8, ILC_COLOR16, ILC_COLOR24,
+ ILC_COLOR32, ILC_COLORDDB, 0 );
+var Flags : DWord;
+begin
+ Result := True;
+ if FHandle <> 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := False;
+ if ImgWidth = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if ImgHeight = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Flags := ColorFlags[ FColors ];
+ if Masked then
+ Flags := Flags or ILC_MASK;
+ FHandle := ImageList_Create( ImgWidth, ImgHeight, Flags, 0, FAllocBy );
+ if fBkColor <> clNone then
+ SetBkColor( fBkColor );
+ Result := FHandle <> 0;
+end;
+{$ENDIF PAS_VERSION}
+
+function TImageList.ImgRect(Idx: Integer): TRect;
+var II : TImageInfo;
+begin
+ Result := MakeRect( 0, 0, 0, 0 );
+ if FHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if ImageList_GetImageInfo( FHandle, Idx, II ) then
+ Result := II.rcImage;
+end;
+
+{$IFDEF ASM_noVERSION_UNICODE}
+function TImageList.LoadBitmap(ResourceName: PAnsiChar;
+ TranspColor: TColor): Boolean;
+asm
+ PUSH EBX
+ XCHG EBX, EAX
+ XCHG EAX, ECX //TranspColor
+ PUSH EDX
+ CMP EAX, clNone
+ JNE @@2rgb
+ OR EAX, -1
+ JMP @@tranColorReady
+@@2rgb:
+ CALL Color2RGB
+@@tranColorReady:
+ POP EDX
+ PUSH EAX
+ PUSH [EBX].fAllocBy
+ PUSH [EBX].fImgWidth
+ PUSH EDX
+ PUSH [hInstance]
+ CALL ImageList_LoadBitmap
+ TEST EAX, EAX
+ JZ @@exit
+ XCHG EDX, EAX
+ XCHG EAX, EBX
+ CALL SetHandle
+ MOV AL, 1
+@@exit: POP EBX
+end;
+{$ELSE PAS_VERSION} //Pascal
+function TImageList.LoadBitmap(ResourceName: PKOLChar;
+ TranspColor: TColor): Boolean;
+var NewHandle : THandle;
+ TranColr: TColor;
+begin
+ TranColr := TranspColor;
+ if TranColr = clNone then TranColr := TColor( CLR_NONE )
+ else TranColr := Color2RGB( TranColr );
+ NewHandle := ImageList_LoadBitmap( hInstance, ResourceName,
+ ImgWidth, AllocBy, TranColr );
+ //ImageList_GetIconSize( NewHandle, fImgWidth, fImgHeight );
+ Result := NewHandle <> 0;
+ if Result then
+ Handle := NewHandle;
+ ImageList_GetIconSize( fHandle, FImgWidth, FImgHeight );
+end;
+{$ENDIF PAS_VERSION}
+
+function TImageList.LoadFromFile(FileName: PKOLChar; TranspColor: TColor;
+ ImgType: TImageType): Boolean;
+const ImgTypes:array[ TImageType ] of DWord = ( IMAGE_BITMAP, IMAGE_ICON, IMAGE_CURSOR );
+var NewHandle : THandle;
+ TranspFlag : DWord;
+begin
+ TranspFlag := 0;
+ if TranspColor <> clNone then
+ TranspFlag := LR_LOADTRANSPARENT;
+ NewHandle := ImageList_LoadImage( hInstance, FileName, ImgWidth, AllocBy,
+ Color2RGB( TranspColor ), ImgTypes[ ImgType ],
+ LR_LOADFROMFILE or LR_CREATEDIBSECTION or TranspFlag );
+ Result := NewHandle <> 0;
+ if Result then
+ Handle := NewHandle;
+end;
+
+function TImageList.LoadSystemIcons(SmallIcons: Boolean): Boolean;
+var NewHandle : THandle;
+ FileInfo : TSHFileInfo;
+ Flags : DWord;
+begin
+ OleInit;
+ Flags := SHGFI_SYSICONINDEX;
+ if SmallIcons then
+ Flags := Flags or SHGFI_SMALLICON;
+ NewHandle := {$IFDEF UNICODE_CTRLS} SHGetFileInfoW {$ELSE} SHGetFileInfoA {$ENDIF}
+ ( '', 0, FileInfo, Sizeof( FileInfo ), Flags );
+ Result := NewHandle <> 0;
+ if Result then
+ begin
+ Handle := NewHandle;
+ FShareImages := True;
+ end;
+end;
+
+function TImageList.Merge(Idx: Integer; ImgList2: PImageList; Idx2, X,
+ Y: Integer): PImageList;
+var L : THandle;
+begin
+ Result := nil;
+ L := ImageList_Merge( FHandle, Idx, ImgList2.Handle, Idx2, X, Y );
+ if L <> 0 then
+ begin
+ Result := NewImageList( fControl );
+ Result.Handle := L;
+ end;
+end;
+
+function TImageList.Replace(Idx: Integer; Bmp, Msk: HBitmap): Boolean;
+begin
+ Result := False;
+ if FHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := ImageList_Replace( FHandle, Idx, Bmp, Msk );
+end;
+
+function TImageList.ReplaceIcon(Idx: Integer; Ico: HIcon): Boolean;
+begin
+ Result := False;
+ if FHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := ImageList_ReplaceIcon( FHandle, Idx, Ico ) >= 0;
+end;
+
+procedure TImageList.SetAllocBy(const Value: Integer);
+begin
+ if FHandle <> 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ // AllocBy can be changed only before adding images and creating handle
+ FAllocBy := Value;
+end;
+
+procedure TImageList.SetBkColor(const Value: TColor);
+begin
+ fBkColor := Value;
+ if fHandle <> 0 then
+ ImageList_SetBkColor( FHandle, Color2RGB( Value ) );
+end;
+
+procedure TImageList.SetColors(const Value: TImageListColors);
+begin
+ if FHandle <> 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ FColors := Value;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TImageList.SetHandle(const Value: THandle);
+begin
+ if FHandle = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if (FHandle <> 0) and not FShareImages then
+ ImageList_Destroy( FHandle );
+ FHandle := Value;
+ if FHandle <> 0 then
+ ImageList_GetIconSize( FHandle, FImgWidth, FImgHeight )
+ else
+ begin
+ FImgWidth := 0;
+ FImgHeight := 0;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TImageList.SetImgHeight(const Value: Integer);
+begin
+ if FHandle <> 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ FImgHeight := Value;
+end;
+
+procedure TImageList.SetImgWidth(const Value: Integer);
+begin
+ if FHandle <> 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ FImgWidth := Value;
+end;
+
+procedure TImageList.SetMasked(const Value: Boolean);
+begin
+ if FHandle <> 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ FMasked := Value;
+end;
+
+function TImageList.GetOverlay(Idx: TImgLOVrlayIdx): Integer;
+begin
+ Result := fOverlay[ Idx ];
+end;
+
+procedure TImageList.SetOverlay(Idx: TImgLOVrlayIdx; const Value: Integer);
+begin
+ if ImageList_SetOverlayImage( fHandle, Value, Idx shl 8 ) then
+ fOverlay[ Idx ] := Value;
+end;
+
+procedure TImageList.StretchDraw(Idx: Integer; DC: HDC; const Rect: TRect);
+begin
+ if FHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ ImageList_DrawEx( FHandle, Idx, DC, Rect.Left, Rect.Top,
+ Rect.Right- Rect.Left, Rect.Bottom-Rect.Top,
+ BkColor, BlendColor, GetDrawStyle );
+end;
+
+function GetImgListSize( Sender: PControl; Size: Integer ): PImageList;
+begin
+ if Size > 16 then
+ Result := Sender.DF.fCtlImageListNormal
+ else
+ Result := Sender.DF.fCtlImageListSml;
+ if Result <> nil then
+ begin
+ if Result.fImgWidth = 0 then
+ Result.ImgWidth := Size;
+ if Result.fImgHeight = 0 then
+ Result.ImgHeight := Size;
+ end;
+ if Result = nil then
+ begin
+ Result := Sender.fImageList;
+ while Result <> nil do
+ begin
+ if (Result.FImgWidth = Size) and (Result.FImgHeight = Size) then
+ break;
+ Result := Result.fNext;
+ end;
+ end;
+end;
+
+function TControl.GetImgListIdx(const Index: Integer): PImageList;
+begin
+ if Index <> 0 then
+ Result := GetImgListSize( @Self, Index )
+ else
+ begin
+ Result := DF.fCtlImgListState;
+ if Result = nil then
+ begin
+ Result := fImageList;
+ while Result <> nil do
+ begin
+ if (Result <> GetImgListIdx( 16 )) and (Result <> GetImgListIdx( 32 )) then
+ break;
+ Result := Result.fNext;
+ end;
+ end;
+ end;
+end;
+
+procedure TControl.SetImgListIdx(const Index: Integer;
+ const Value: PImageList);
+begin
+
+ if Value <> nil then
+ begin
+ if Index <> 0 then
+ if (Value.ImgWidth = 0) or (Value.ImgHeight = 0) then
+ begin
+ Value.ImgWidth := Index;
+ Value.ImgHeight := Index;
+ end;
+ end;
+
+ case Index of
+ 32: DF.fCtlImageListNormal := Value;
+ 16: DF.fCtlImageListSml := Value;
+ else DF.fCtlImgListState := Value;
+ end;
+ ApplyImageLists2Control( @Self );
+end;
+
+{ -- list view -- }
+
+function WndProcEndLabelEdit( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var NMhdr: PNMHdr;
+ LVDisp: PLVDispInfo;
+ Flag: Boolean;
+begin
+ Result := False;
+ if Msg.message = WM_NOTIFY then
+ begin
+ NMHdr := Pointer( Msg.lParam );
+ case NMHdr.code of
+ LVN_ENDLABELEDIT:
+ begin
+ LVDisp := Pointer( Msg.lParam );
+ Result := True;
+ if LVDisp.item.pszText = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Rslt := 1;
+ {$IFDEF NIL_EVENTS}
+ if assigned( Self_.EV.fOnEndEditLVItem ) then
+ {$ENDIF}
+ begin
+ Flag := Self_.EV.fOnEndEditLVItem( Self_, LVDisp.item.iItem,
+ LVDisp.item.iSubItem, LVDisp.item.pszText );
+ if Flag then Rslt := 1
+ else Rslt := 0;
+ end;
+ end;
+ end;
+ end;
+end;
+
+procedure TControl.SetOnEndEditLVItem(const Value: TOnEditLVItem);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnEndEditLVITem := Value;
+ AttachProc( WndProcEndLabelEdit );
+end;
+
+procedure TControl.LVColAdd(const aText: KOLString; aalign: TTextAlign;
+ aWidth: Integer);
+begin
+ LVColInsert( DF.fLVColCount, aText, aalign, aWidth );// 21.10.2001
+end;
+
+//****************** changed by Mike Gerasimov
+procedure TControl.LVColInsert(ColIdx: Integer; const aText: KOLString;
+ aAlign: TTextAlign; aWidth: Integer);
+var LVColData: TLVColumn;
+begin
+ LVColData.mask := LVCF_FMT or LVCF_TEXT;
+ if ImageListSmall <> nil then
+ LVColData.mask := LVColData.mask; // or LVCF_IMAGE ;
+ LVColData.iImage := -1;
+ LVColData.fmt := Ord( aAlign );
+ if aWidth < 0 then
+ begin
+ aWidth := -aWidth;
+ LVColData.fmt := LVColData.fmt or LVCFMT_BITMAP_ON_RIGHT;
+ end;
+ LVColData.cx := aWidth;
+ if aWidth > 0 then
+ LVColData.mask := LVColData.mask or LVCF_WIDTH;
+ LVColData.pszText := PKOL_Char( aText );
+ if Perform( LVM_INSERTCOLUMN, ColIdx, Integer( @LVColData ) ) >= 0 then
+ Inc( DF.fLVColCount );
+end;
+
+function TControl.GetLVColText(Idx: Integer): KOLString;
+var Buf: array[ 0..4095 ] of KOLChar;
+ LC: TLVColumn;
+begin
+ LC.mask := LVCF_TEXT;
+ LC.pszText := @ Buf[ 0 ];
+ LC.cchTextMax := 4096;
+ Buf[ 0 ] := #0;
+ Perform( LVM_GETCOLUMN, Idx, Integer( @ LC ) );
+ Result := Buf;
+end;
+
+procedure TControl.SetLVColText(Idx: Integer; const Value: KOLString);
+var LC: TLVColumn;
+begin
+ ZeroMemory( @LC, Sizeof( LC ) ); {Alexey (Lecha2002)}
+ LC.mask := LVCF_TEXT;
+ LC.pszText := '';
+ if Value <> '' then
+ LC.pszText := @ Value[ 1 ];
+ Perform( LVM_SETCOLUMN, Idx, Integer( @ LC ) );
+end;
+
+function TControl.GetLVColalign(Idx: Integer): TTextAlign;
+const Formats: array[ 0..2 ] of TTextAlign = ( taLeft, taRight, taCenter );
+var LC: TLVColumn;
+begin
+ ZeroMemory( @LC, Sizeof( LC ) ); {Alexey (Lecha2002)}
+ LC.mask := LVCF_FMT;
+ Perform( LVM_GETCOLUMN, Idx, Integer( @ LC ) );
+ Result := Formats[ LC.fmt and LVCFMT_JUSTIFYMASK ];
+end;
+
+procedure TControl.SetLVColalign(Idx: Integer; const Value: TTextAlign);
+const FormatFlags: array[ TTextAlign ] of BYTE = ( LVCFMT_LEFT, LVCFMT_RIGHT,
+ LVCFMT_CENTER );
+var LC: TLVColumn;
+begin
+ ZeroMemory( @LC, Sizeof( LC ) ); {Alexey (Lecha2002)}
+ LC.mask := LVCF_FMT;
+ Perform( LVM_GETCOLUMN, Idx, Integer( @ LC ) );
+ LC.fmt := LC.fmt and not LVCFMT_JUSTIFYMASK or FormatFlags[ Value ];
+ Perform( LVM_SETCOLUMN, Idx, Integer( @ LC ) );
+end;
+
+function TControl.GetLVColEx(Idx: Integer; const Index: Integer): Integer;
+var LC: TLVColumn;
+begin
+ ZeroMemory( @LC, Sizeof( LC ) ); {Alexey (Lecha2002)}
+ LC.mask := LoWord( Index );
+ Perform( LVM_GETCOLUMN, Idx, Integer( @ LC ) );
+ Result := PDWORD( Integer( @ LC ) + HiWord( Index ) )^;
+end;
+
+//********************** changed by Mike Gerasimov
+procedure TControl.SetLVColEx(Idx: Integer; const Index: Integer;
+ const Value: Integer);
+var LC: TLVColumn;
+begin
+ ZeroMemory(@LC,SizeOf(LC)); // Added Line
+ LC.mask := LoWord( Index );
+ if HiWord( Index ) = 24 then // Added Line
+ begin // Added Line
+ LC.mask := LC.mask or LVCF_FMT; // Added Line
+ if Value <>-1 then // Added Line
+ LC.fmt := LC.fmt or LVCFMT_IMAGE or LVCFMT_COL_HAS_IMAGES // Added Line
+ else LC.mask := LC.mask and not LVCF_IMAGE; // + by non
+ end;
+ if (value<>-1)or(HiWord( Index )<>24) then // + by non
+ PDWORD( Integer( @ LC ) + HiWord( Index ) )^ := Value;
+ Perform( LVM_SETCOLUMN, Idx, Integer( @ LC ) );
+end;
+
+function TControl.LVAdd(const aText: KOLString; ImgIdx: Integer;
+ State: TListViewItemState; StateImgIdx, OverlayImgIdx: Integer;
+ Data: DWORD): Integer;
+begin
+ Result := LVInsert( MaxInt {Count}, aText, ImgIdx, State, StateImgIdx, OverlayImgIdx, Data );
+end;
+
+function TControl.LVInsert(Idx: Integer; const aText: KOLString;
+ ImgIdx: Integer; State: TListViewItemState; StateImgIdx, OverlayImgIdx: Integer;
+ Data: DWORD): Integer;
+const
+ LVM_REDRAWITEMS = LVM_FIRST + 21;
+var LVI: TLVItem;
+begin
+ LVI.mask := LVIF_TEXT or LVIF_IMAGE or LVIF_PARAM or LVIF_STATE
+ or LVIF_DI_SETITEM;
+ LVI.iItem := Idx;
+ LVI.iSubItem := 0;
+ LVI.state := 0;
+ if lvisBlend in State then
+ LVI.state := LVIS_CUT;
+ if lvisHighlight in State then
+ LVI.state := LVI.state or LVIS_DROPHILITED;
+ if lvisFocus in State then
+ LVI.state := LVI.state or LVIS_FOCUSED;
+ if lvisSelect in State then
+ LVI.state := LVI.state or LVIS_SELECTED;
+ LVI.stateMask := $FFFF;
+ if StateImgIdx <> 0 then
+ LVI.state := LVI.state or ((StateImgIdx and $F) shl 12);
+ if OverlayImgIdx <> 0 then
+ LVI.state := LVI.state or ((OverlayImgIdx and $F) shl 8);
+ LVI.pszText := PKOL_Char( aText );
+ LVI.iImage := ImgIdx;
+ LVI.lParam := Data;
+ Result := Perform( LVM_INSERTITEM, 0, Integer( @LVI ) );
+end;
+
+procedure TControl.LVSetItem(Idx, Col: Integer; const aText: KOLString;
+ ImgIdx: Integer; State: TListViewItemState; StateImgIdx,
+ OverlayImgIdx: Integer; Data: DWORD);
+var LVI: TLVItem;
+ {$IFDEF KOL_ASSERTIONS} I: Integer; {$ENDIF}
+begin
+ LVI.mask := LVIF_TEXT or {LVIF_STATE or} LVIF_DI_SETITEM;
+ if Col = 0 then
+ begin
+ LVI.mask := LVIF_TEXT or LVIF_STATE or LVIF_PARAM
+ or LVIF_DI_SETITEM;
+ end;
+ if ImgIdx <> I_SKIP then
+ LVI.mask := LVI.mask or LVIF_IMAGE;
+ if ImgIdx < I_SKIP then
+ LVI.mask := LVIF_TEXT or LVIF_DI_SETITEM;
+ LVI.iItem := Idx;
+ LVI.iSubItem := Col;
+ LVI.state := 0;
+ if lvisBlend in State then
+ LVI.state := LVIS_CUT;
+ if lvisHighlight in State then
+ LVI.state := LVI.state or LVIS_DROPHILITED;
+ if lvisFocus in State then
+ LVI.state := LVI.state or LVIS_FOCUSED;
+ if lvisSelect in State then
+ LVI.state := LVI.state or LVIS_SELECTED;
+ LVI.stateMask := $FFFF;
+ if StateImgIdx <> 0 then
+ LVI.state := LVI.state or ((StateImgIdx and $F) shl 12);
+ if StateImgIdx < 0 {= I_SKIP} then
+ LVI.stateMask := $F0FF;
+ if OverlayImgIdx <> 0 then
+ LVI.state := LVI.state or ((OverlayImgIdx and $F) shl 8);
+ if OverlayImgIdx < 0 {=I_SKIP} then
+ LVI.stateMask := LVI.stateMask and $FFF;
+ LVI.pszText := PKOL_Char( aText );
+ LVI.iImage := ImgIdx;
+ LVI.lParam := Data;
+ {$IFDEF KOL_ASSERTIONS} I := {$ENDIF}
+ Perform( LVM_SETITEM, 0, Integer( @LVI ) );
+ {$IFDEF KOL_ASSERTIONS}
+ if (I = 0) and (Col = 0) then
+ Assert( False, 'Can not set item ' );
+ {$ENDIF KOL_ASSERTIONS}
+end;
+
+procedure LVGetItem( Sender: PControl; Idx, Col: Integer; var LVI: TLVItem;
+ TextBuf: PKOL_Char; TextBufSize: Integer );
+begin
+ LVI.mask := LVIF_STATE or LVIF_PARAM or LVIF_IMAGE;
+ if Col > 0 then
+ if not (lvoSubItemImages in Sender.DF.fLVOptions) then
+ LVI.mask := LVIF_STATE or LVIF_PARAM;
+ LVI.iItem := Idx;
+ LVI.iSubItem := Col;
+ LVI.pszText := TextBuf;
+ LVI.cchTextMax := TextBufSize;
+ if TextBufSize <> 0 then
+ LVI.mask := LVI.mask or LVIF_TEXT;
+ Sender.Perform( LVM_GETITEM, 0, Integer( @LVI ) );
+end;
+
+function TControl.LVGetItemImgIdx(Idx: Integer): Integer;
+var LVI: TLVItem;
+begin
+ LVI.iImage := -1;//= Result if image is not assigned {Andrzej Kubaszek}
+ LVGetItem( @Self, Idx, 0, LVI, nil, 0 );
+ Result := LVI.iImage;
+end;
+
+procedure TControl.LVSetItemImgIdx(Idx: Integer; const Value: Integer);
+var LVI: TLVItem;
+begin
+ LVGetItem( @Self, Idx, 0, LVI, nil, 0 );
+ LVI.iImage := Value;
+ Perform( LVM_SETITEM, 0, Integer( @LVI ) );
+end;
+
+function TControl.LVGetItemText(Idx, Col: Integer): KOLString;
+var LVI: TLVItem;
+ TextBuf: PKOL_Char;
+ BufSize: Integer;
+begin
+ BufSize := 0;
+ TextBuf := nil;
+ repeat
+ if TextBuf <> nil then
+ FreeMem( TextBuf );
+ BufSize := BufSize * 2 + 100; // to vary in asm version
+ GetMem( TextBuf, BufSize * Sizeof( KOLChar ) );
+ TextBuf[ 0 ] := #0;
+ LVGetItem( @Self, Idx, Col, LVI, TextBuf, BufSize );
+ until Integer({$IFDEF UNICODE_CTRLS} WStrLen {$ELSE} StrLen {$ENDIF}
+ ( PKOLChar( TextBuf ) )) < BufSize - 1;
+ Result := TextBuf;
+ FreeMem( TextBuf );
+end;
+
+procedure TControl.LVSetItemText(Idx, Col: Integer; const Value: KOLString);
+var LVI: TLVItem;
+begin
+ LVI.iSubItem := Col;
+ LVI.pszText := PKOL_Char( Value );
+ Perform( LVM_SETITEMTEXT, Idx, Integer( @LVI ) );
+end;
+
+procedure TControl.LVColDelete(ColIdx: Integer);
+begin
+ Perform( LVM_DELETECOLUMN, ColIdx, 0 );
+ if DF.fLVColCount > 0 then
+ Dec( DF.fLVColCount );
+end;
+
+procedure TControl.SetLVOptions(const Value: TListViewOptions);
+begin
+ if DF.fLVOptions = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ DF.fLVOptions := Value;
+ ApplyImageLists2ListView( @Self );
+ PostMessage( fHandle, WM_SIZE, 0, 0 ); // to restore scrollers (otherwise its are lost)
+end;
+
+procedure TControl.SetLVStyle(const Value: TListViewStyle);
+begin
+ if DF.fLVStyle = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ DF.fLVStyle := Value;
+ ApplyImageLists2ListView( @Self );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.Perform(msgcode: DWORD; wParam, lParam: Integer): Integer; stdcall;
+begin
+ {$IFDEF INPACKAGE}
+ Log( '->TControl.Perform' );
+ TRY
+ {$ENDIF INPACKAGE}
+ Result := SendMessage( GetWindowHandle, msgcode, wParam, lParam );
+ {$IFDEF INPACKAGE}
+ LogOK;
+ FINALLY
+ Log( '<-TControl.Perform' );
+ END;
+ {$ENDIF INPACKAGE}
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.Postmsg(msgcode: DWORD; wParam, lParam: Integer): Boolean; stdcall;
+begin
+ Result := PostMessage( GetWindowHandle, msgcode, wParam, lParam );
+end;
+{$ENDIF PAS_VERSION}
+
+{$ENDIF WIN_GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.GetChildCount: Integer;
+begin
+ Result := fChildren.Count;
+end;
+{$ENDIF PAS_VERSION}
+{$IFDEF WIN_GDI}
+
+procedure TControl.LVDelete(Idx: Integer);
+begin
+ Perform( LVM_DELETEITEM, Idx, 0 );
+end;
+
+procedure TControl.LVEditItemLabel(Idx: Integer);
+begin
+ Perform( LVM_EDITLABEL, Idx, 0 );
+end;
+
+function TControl.LVItemRect(Idx: Integer; Part: TGetLVItemPart): TRect;
+const Parts: array[ TGetLVItemPart ] of Byte = (
+ LVIR_BOUNDS, LVIR_ICON, LVIR_LABEL, LVIR_SELECTBOUNDS );
+begin
+ Result := MakeRect( Parts[ Part ], 0, 0, 0 );
+ if Perform( LVM_GETITEMRECT, Idx, Integer( @Result ) ) = 0 then
+ Result := MakeRect( 0, 0, 0, 0 );
+end;
+
+function TControl.LVSubItemRect(Idx, ColIdx: Integer): TRect;
+var Hdr: HWnd;
+ R, R1: TRect;
+ ClassNameBuf: array[ 0..31 ] of KOLChar;
+ HdItem: THDItem;
+begin
+ Result.Top := ColIdx; // + 1; error in MSDN ?
+ Result.Left := LVIR_BOUNDS;
+ if Perform( LVM_GETSUBITEMRECT, Idx, Integer( @Result ) ) <> 0 then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := MakeRect( 0, 0, 0, 0 );
+ if ColIdx > 0 then R := LVSubItemRect( Idx, ColIdx - 1 )
+ else R := LVItemRect( Idx, lvipBounds );
+ if (R.Left = 0) and (R.Right = 0) and
+ (R.Top = 0) and (R.Bottom = 0) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Hdr := GetWindow( GetWindowHandle, GW_CHILD );
+ if Hdr <> 0 then
+ begin
+ if GetClassName( Hdr, ClassNameBuf, 32 ) > 0 then
+ if ClassNameBuf = 'SysHeader32' then
+ begin
+ if ColIdx > 0 then R.Left := R.Right
+ else R.Left := 0;
+ R1.Top := 0; R1.Left := 0;
+ Windows.ClientToScreen( Hdr,{$IFDEF FPC} PPoint( @ R1.Left )^ {$ELSE} R1.TopLeft {$ENDIF} );
+ Windows.ScreenToClient( fHandle, {$IFDEF FPC} PPoint( @ R1.Left )^ {$ELSE} R1.TopLeft {$ENDIF} );
+ R1 := R;
+ HdItem.Mask := HDI_WIDTH;
+ if SendMessage( Hdr, HDM_GETITEM, ColIdx, Integer( @HdItem ) ) = 0 then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ R1.Right := R1.Left + HdItem.cxy;
+ Result := R1;
+ end;
+ end;
+end;
+
+function TControl.LVGetItemPos(Idx: Integer): TPoint;
+begin
+ Perform( LVM_GETITEMPOSITION, Idx, Integer( @Result ) );
+end;
+
+procedure TControl.LVSetItemPos(Idx: Integer; const Value: TPoint);
+begin
+ Perform( LVM_SETITEMPOSITION32, Idx, Integer( @Value ) );
+end;
+
+function TControl.LVItemAtPos(X, Y: Integer): Integer;
+var Dummy: TWherePosLVItem;
+begin
+ Result := LVItemAtPosEx( X, Y, Dummy );
+end;
+
+function TControl.LVItemAtPosEx(X, Y: Integer;
+ var Where: TWherePosLVItem): Integer;
+var HTI: TLVHitTestInfo;
+begin
+ HTI.pt.x := X;
+ HTI.pt.y := Y;
+ Perform( LVM_HITTEST, 0, Integer( @HTI ) );
+ Result := HTI.iItem;
+ Where := lvwpOnColumn;
+ if HTI.flags = LVHT_ONITEMICON then
+ Where := lvwpOnIcon
+ else
+ if HTI.flags = LVHT_ONITEMLABEL then
+ Where := lvwpOnLabel
+ else
+ if HTI.flags = LVHT_ONITEMSTATEICON then
+ Where := lvwpOnStateIcon
+ else
+ if HTI.flags = LVHT_ONITEM then
+ Where := lvwpOnItem;
+end;
+
+procedure TControl.LVMakeVisible(Item: Integer; PartiallyOK: Boolean);
+begin
+ if Item < 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Perform( LVM_ENSUREVISIBLE, Item, Integer( PartiallyOK ) );
+end;
+
+procedure TControl.LVSetColorByIdx(const Index: Integer;
+ const Value: TColor);
+var MsgCode: Integer;
+ ColorValue: TColor;
+begin
+ MsgCode := Index + 1;
+ case MsgCode of
+ LVM_SETTEXTCOLOR: fTextColor := Value;
+ LVM_SETTEXTBKCOLOR: DF.fLVTextBkColor := Value;
+ LVM_SETBKCOLOR: fColor := Value;
+ end;
+ ColorValue := Color2RGB( Value );
+ Perform( MsgCode, 0, ColorValue );
+end;
+
+{$IFDEF F_P}
+function TControl.LVGetColorByIdx(const Index: Integer): TColor;
+begin
+ CASE Index OF
+ LVM_SETTEXTCOLOR: Result := fTextColor;
+ LVM_SETTEXTBKCOLOR: Result := fLVTextBkColor;
+ LVM_SETBKCOLOR: Result := fColor;
+ END;
+end;
+{$ENDIF F_P}
+
+function TControl.GetIntVal(const Index: Integer): Integer;
+begin
+ Result := GetItemVal( 0, Index );
+end;
+
+procedure TControl.SetIntVal(const Index, Value: Integer);
+begin
+ SetItemVal( Value, Index, 0 );
+end;
+
+function TControl.GetItemVal(Item: Integer; const Index: Integer): Integer;
+begin
+ Result := Perform( LoWord(Index), Item, 0 );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.SetItemVal(Item: Integer; const Index: Integer; const Value: Integer);
+var MsgCode: Integer;
+begin
+ MsgCode := HiWord( Index );
+ if MsgCode = 0 then
+ MsgCode := Index + 1;
+ Perform( MsgCode and $7FFF, Item, Value );
+ if (MsgCode and $8000) <> 0 then
+ Invalidate;
+end;
+{$ENDIF PAS_VERSION}
+
+function TControl.GetSBMinMax: TPoint;
+{$IFDEF _D2}
+var X, Y: Integer;
+{$ENDIF}
+begin
+ if (Handle <> 0) then begin
+ {$IFDEF _D2}
+ GetScrollRange(Handle, SB_CTL, X, Y);
+ Result.X := X;
+ Result.Y := Y;
+ {$ELSE}
+ GetScrollRange(Handle, SB_CTL, Result.X, Result.Y);
+ {$ENDIF}
+ Dec(Result.Y, SBPageSize - 1);
+ end
+ else
+ Result := DF.fSBMinMax;
+end;
+
+function TControl.GetSBPageSize: Integer;
+var
+ SI: TScrollInfo;
+begin
+ ZeroMemory(@SI, SizeOf(SI));
+ SI.cbSize := SizeOf(SI);
+ SI.fMask := SIF_PAGE;
+ SBGetScrollInfo(SI);
+ Result := SI.nPage;
+end;
+
+function TControl.GetSBPosition: Integer;
+begin
+ Result := GetScrollPos(Handle, SB_CTL);
+end;
+
+procedure TControl.SetSBMax(Value: Longint);
+var
+ P: TPoint;
+begin
+ DF.fSBMinMax.Y := Value;
+ if (Handle <> 0) then
+ begin
+ P := SBMinMax;
+ P.Y := Value;
+ SBMinMax := P;
+ end;
+end;
+
+procedure TControl.SetSBMin(Value: Longint);
+var
+ P: TPoint;
+begin
+ DF.fSBMinMax.X := Value;
+ if (Handle <> 0) then
+ begin
+ P := SBMinMax;
+ P.X := Value;
+ SBMinMax := P;
+ end;
+end;
+
+procedure TControl.SetSBPageSize(Value: Integer);
+var
+ SI: TScrollInfo;
+begin
+ DF.fSBPageSize := Value;
+ if fHandle <> 0 then
+ begin
+ ZeroMemory(@SI, SizeOf(SI));
+ SI.cbSize := SizeOf(SI);
+ SI.fMask := SIF_PAGE or SIF_RANGE;
+ SBGetScrollInfo(SI);
+ {$IFDEF SCROLL_OLD} // by QAZ
+ {$IFDEF SCROLL_OLD_MAX1}
+ if (SI.nMax = 0) and (SI.nMin = 0) then
+ SI.nMax := 1;
+ {$ENDIF}
+ SI.nMax := SI.nMax - Integer(SI.nPage) + Value;
+ {$ENDIF}
+ SI.nPage := Value;
+ SBSetScrollInfo(SI);
+ end;
+end;
+
+procedure TControl.SetSBPosition(Value: Integer);
+begin
+ DF.fSBPosition := Value;
+ if (Handle <> 0) then
+ SetScrollPos(Handle, SB_CTL, Value, True);
+end;
+
+procedure TControl.SetSBMinMax(const Value: TPoint);
+begin
+ GetSBMinMax;
+ if (Handle <> 0) then
+ SetScrollRange(Handle, SB_CTL, Value.X,
+ Value.Y {$IFDEF SCROLL_OLD} + SBPageSize - 1{$ENDIF (by QAZ)} , True)
+ else
+ DF.fSBMinMax := Value;
+end;
+
+function TControl.SBSetScrollInfo(const SI: TScrollInfo): Integer;
+begin
+ Result := SetScrollInfo(Handle, SB_CTL, SI, True)
+end;
+
+function TControl.SBGetScrollInfo(var SI: TScrollInfo): Boolean;
+begin
+ Result := Cardinal(GetScrollInfo(Handle, SB_CTL, SI)) <> 0;
+end;
+
+{ -- OpenSaveDialog -- }
+
+function NewOpenSaveDialog( const Title, StrtDir: KOLString;
+ Options: TOpenSaveOptions ): POpenSaveDialog;
+begin
+ New( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TOpenSaveDialog';
+ {$ENDIF}
+ Result.FOptions := Options;
+ if Options = [] then
+ Result.FOptions := DefOpenSaveDlgOptions;
+ Result.fOpenDialog := True;
+ Result.FTitle := Title;
+ Result.FInitialDir := StrtDir;
+end;
+
+{ TOpenSaveDialog }
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+destructor TOpenSaveDialog.Destroy;
+begin
+ FFilter := '';
+ FInitialDir := '';
+ FDefExtension := '';
+ FFileName := '';
+ FTitle := '';
+ {$IFDEF OpenSaveDialog_Extended}
+ TemplateName := '';
+ {$ENDIF}
+ inherited;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function TOpenSaveDialog.Execute: Boolean;
+const OpenSaveFlags: array[ TOpenSaveOption ] of Integer = (
+ OFN_CREATEPROMPT,
+ OFN_EXTENSIONDIFFERENT,
+ OFN_FILEMUSTEXIST,
+ OFN_HIDEREADONLY,
+ OFN_NOCHANGEDIR,
+ OFN_NODEREFERENCELINKS,
+ OFN_ALLOWMULTISELECT,
+ OFN_NONETWORKBUTTON,
+ OFN_NOREADONLYRETURN,
+ OFN_OVERWRITEPROMPT,
+ OFN_PATHMUSTEXIST,
+ OFN_READONLY,
+ OFN_NOVALIDATE,
+ OFN_ENABLETEMPLATE,
+ OFN_ENABLEHOOK );
+var
+ Ofn : TOpenFilename;
+ Fltr : KOLString;
+ TempFilename : KOLString;
+ Function MakeFilter(s : KOLString) : KOLString;
+ { format of filter for API call is following:
+ 'text files'#0'*.txt'#0
+ 'bitmap files'#0'*.bmp'#0#0 }
+ var Str: PKOLChar;
+ begin
+ Result := s;
+ if Result='' then exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result:=Result+#0; {Delphi string always end on #0 is this is #0#0}
+ Str := PKOLChar( Result );
+ while Str^ <> #0 do
+ begin
+ if Str^ = '|' then
+ Str^ := #0;
+ Inc( Str );
+ end;
+ end;
+
+var m: Integer;
+begin
+ ZeroMemory( @ofn, sizeof( ofn ) );
+
+ {$IFDEF OpenSaveDialog_Extended}
+ if (WinVer <= wvNT) and (WinVer <> wvME) then
+ ofn.lStructSize := 76
+ else
+ begin
+ ofn.lStructSize := Sizeof( ofn );
+ ofn.FlagsEx := Integer( NoPlaceBar );
+ end;
+ {$ELSE}
+ ofn.lStructSize:= 76; //to provide correct work in Win9x
+ {$ENDIF}
+ if fWnd <> 0 then
+ ofn.hWndOwner := fWnd
+ else
+ if Applet <> nil then
+ ofn.hwndOwner := applet.Handle;
+
+ ofn.hInstance:=HInstance;
+
+ Fltr := MakeFilter(FFilter);
+ if Fltr <> '' then
+ ofn.lpstrFilter := PKOLchar(Fltr);
+ ofn.nFilterIndex := FFilterIndex;
+
+ if OSAllowMultiSelect in FOptions then
+ ofn.nMaxFile := High(word)-14 // by V.K. (exchanged condition)
+ else
+ ofn.nMaxFile := MAX_PATH+2;
+
+ SetLength( TempFileName, ofn.nMaxFile );
+ ZeroMemory( @TempFileName[ 1 ], ofn.nMaxFile * sizeof( KOLChar ) );
+ m := Min( ofn.nMaxFile, Length(fFileName) );
+ {$IFDEF UNICODE_CTRLS}
+ ofn.lpstrFile := PKOLchar( TempFileName );
+ WStrLCopy(PWideChar(TempFileName), PWideChar(fFileName), m );
+ {$ELSE}
+ ofn.lpstrFile := StrLCopy(PKOLChar(TempFileName), PKOLchar(fFileName), m );
+ {$ENDIF}
+
+ ofn.lpstrInitialDir:=Pointer(FInitialDir);
+ ofn.lpstrTitle := Pointer(FTitle);
+ ofn.Flags := MakeFlags( @FOptions, OpenSaveFlags )
+ or OFN_EXPLORER or OFN_LONGNAMES or OFN_ENABLESIZING;
+
+ ofn.lpstrDefExt := PKOLChar(FDefExtension);
+ ofn.lCustData := integer(@self);
+ {$IFDEF OpenSaveDialog_Extended}
+ ofn.lpTemplateName := PKOLChar( TemplateName );
+ ofn.lpfnHook := HookProc;
+ {$ELSE}
+ ofn.lpTemplateName := nil;
+ ofn.lpfnHook := nil;
+ {$ENDIF}
+ if fOpenDialog then
+ result := GetOpenFileName(POpenFileName( @ofn )^)
+ else
+ result := GetSaveFileName(POpenFileName( @ofn )^);
+ if result then begin
+ fFilterIndex := ofn.nFilterIndex; // by Vadim
+ fOpenReadOnly := OFN_READONLY and ofn.Flags <> 0; // by ECM (in my redaction)
+ if OSAllowMultiSelect in foptions then begin
+ FFileName := copy(TempFileName, 1, pos(KOLString(#0#0), tempfilename)-1);
+ while pos(KOLString(#0), ffilename) > 0 do begin
+ FFilename[pos(KOLString(#0), ffilename)]:=#13;
+ end;
+ end else
+ FFileName := copy(tempFileName, 1, pos(KOLString(#0), TempFilename)
+ -1 // by X.Y.B.
+ );
+ end else
+ FFilename:='';
+end;
+{$ENDIF PAS_VERSION}
+
+{ -- OpenDirDialog -- }
+
+function NewOpenDirDialog( const Title: KOLString; Options: TOpenDirOptions ):
+ POpenDirDialog;
+begin
+ New( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TOpenDirDialog';
+ {$ENDIF}
+ Result.FOptions := [ odOnlySystemDirs ];
+ if Options <> [] then
+ Result.FOptions := Options;
+ Result.FTitle := Title;
+end;
+
+{ TOpenDirDialog }
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+destructor TOpenDirDialog.Destroy;
+begin
+ FTitle := '';
+ FInitialPath := '';
+ FStatusText := '';
+ inherited;
+end;
+{$ENDIF PAS_VERSION}
+
+type
+ PSHItemID = ^TSHItemID;
+ TSHItemID = packed record
+ cb: Word; { Size of the ID (including cb itself) }
+ abID: array[0..0] of Byte; { The item ID (variable length) }
+ end;
+
+ PItemIDList = ^TItemIDList;
+ TItemIDList = record
+ mkid: TSHItemID;
+ end;
+
+ PBrowseInfo = ^TBrowseInfo;
+ TBrowseInfoA = record
+ hwndOwner: HWND;
+ pidlRoot: PItemIDList;
+ pszDisplayName: PAnsiChar; { Return display name of item selected. }
+ lpszTitle: PAnsiChar; { text to go in the banner over the tree. }
+ ulFlags: UINT; { Flags that control the return stuff }
+ lpfn: Pointer; //TFNBFFCallBack;
+ lParam: LPARAM; { extra info that's passed back in callbacks }
+ iImage: Integer; { output var: where to return the Image index. }
+ end;
+ TBrowseInfoW = record
+ hwndOwner: HWND;
+ pidlRoot: PItemIDList;
+ pszDisplayName: PWideChar; { Return display name of item selected. }
+ lpszTitle: PWideChar; { text to go in the banner over the tree. }
+ ulFlags: UINT; { Flags that control the return stuff }
+ lpfn: Pointer; //TFNBFFCallBack;
+ lParam: LPARAM; { extra info that's passed back in callbacks }
+ iImage: Integer; { output var: where to return the Image index. }
+ end;
+ TBrowseInfo = {$IFDEF UNICODE_CTRLS} TBrowseInfoW {$ELSE} TBrowseInfoA {$ENDIF};
+
+function SHBrowseForFolderA(var lpbi: TBrowseInfoA): PItemIDList; stdcall;
+ external 'shell32.dll' name 'SHBrowseForFolderA';
+{$IFDEF UNICODE_CTRLS}
+function SHBrowseForFolderW(var lpbi: TBrowseInfoW): PItemIDList; stdcall;
+ external 'shell32.dll' name 'SHBrowseForFolderW';
+{$ENDIF UNICODE_CTRLS}
+function SHGetPathFromIDListA(pidl: PItemIDList; pszPath: PAnsiChar): BOOL; stdcall;
+ external 'shell32.dll' name 'SHGetPathFromIDListA';
+{$IFDEF UNICODE_CTRLS}
+function SHGetPathFromIDListW(pidl: PItemIDList; pszPath: PKOLChar): BOOL; stdcall;
+ external 'shell32.dll' name 'SHGetPathFromIDListW';
+{$ENDIF UNICODE_CTRLS}
+procedure CoTaskMemFree(pv: Pointer); stdcall; external 'ole32.dll'
+ name 'CoTaskMemFree';
+
+const
+ BIF_RETURNONLYFSDIRS = $0001; { For finding a folder to start document searching }
+ BIF_DONTGOBELOWDOMAIN = $0002; { For starting the Find Computer }
+ BIF_STATUSTEXT = $0004;
+ BIF_RETURNFSANCESTORS = $0008;
+ BIF_EDITBOX = $0010;
+ BIF_VALIDATE = $0020; { insist on valid result (or CANCEL) }
+ BIF_NEWDIALOGSTYLE = $0040; { Use the new dialog layout with the ability to resize }
+ { Caller needs to call OleInitialize() before using this API (c) JVCL }
+ BIF_BROWSEFORCOMPUTER = $1000; { Browsing for Computers. }
+ BIF_BROWSEFORPRINTER = $2000; { Browsing for Printers }
+ BIF_BROWSEINCLUDEFILES = $4000; { Browsing for Everything }
+
+ BFFM_INITIALIZED = 1;
+ BFFM_SELCHANGED = 2;
+
+ BFFM_SETSTATUSTEXT = WM_USER + 100;
+ BFFM_ENABLEOK = WM_USER + 101;
+ BFFM_SETSELECTION = WM_USER + 102;
+ BFFM_SETSELECTIONW = WM_USER + 103;
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function TOpenDirDialog.Execute: Boolean;
+const FlagsArray: array[ TOpenDirOption ] of Integer =
+ ( BIF_BROWSEFORCOMPUTER, BIF_BROWSEFORPRINTER, BIF_DONTGOBELOWDOMAIN,
+ BIF_RETURNFSANCESTORS, BIF_RETURNONLYFSDIRS, BIF_STATUSTEXT,
+ BIF_BROWSEINCLUDEFILES, BIF_EDITBOX, BIF_NEWDIALOGSTYLE );
+var BI : TBrowseInfo;
+ Browse : PItemIdList;
+begin
+ Result := False;
+ if WndOwner <> 0 then
+ BI.hwndOwner := WndOwner
+ else
+ if Applet <> nil then
+ BI.hwndOwner := Applet.Handle
+ else
+ BI.hwndOwner := 0;
+ BI.pidlRoot := nil;
+ BI.pszDisplayName := @FBuf[ 0 ];
+ BI.lpszTitle := PKOLChar( Title );
+ BI.ulFlags := MakeFlags( @FOptions, FlagsArray );
+ BI.lpfn := FCallBack;
+ BI.lParam := Integer( @Self );
+ Browse := {$IFDEF UNICODE_CTRLS} SHBrowseForFolderW {$ELSE} SHBrowseForFolderA {$ENDIF}
+ ( BI );
+ if Browse <> nil then
+ begin
+ {$IFDEF UNICODE_CTRLS}SHGetPathFromIDListW{$ELSE} SHGetPathFromIDListA{$ENDIF}( Browse, @FBuf[ 0 ] );
+ CoTaskMemFree( Browse );
+ Result := True;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+function TOpenDirDialog.GetInitialPath: KOLString;
+begin
+ Result := IncludeTrailingPathDelimiter( fInitialPath );
+end;
+
+function TOpenDirDialog.GetPath: KOLString;
+begin
+ Result := FBuf;
+end;
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function OpenDirSelChangeCallBack( Wnd: HWnd; Msg: DWORD; lParam, lpData: LParam ):
+ Integer; stdcall;
+var _Self_: POpenDirDialog;
+ EnableOK: Integer;
+begin
+ _Self_ := Pointer( lpData );
+ if Assigned( _Self_.FOnSelChanged ) then
+ begin
+ {$IFDEF UNICODE_CTRLS} SHGetPathFromIDListW {$ELSE} SHGetPathFromIDListA {$ENDIF}( PItemIDList( lParam ), @ _Self_.FBuf[ 0 ] );
+ EnableOK := 0;
+ _Self_.FOnSelChanged( _Self_, _Self_.FBuf, EnableOK,
+ KOL_String( KOLString( _Self_.FStatusText ) ) );
+ SendMessage( Wnd, BFFM_ENABLEOK, 0, EnableOK );
+ if _Self_.FStatusText <> '' then
+ SendMessage( Wnd, BFFM_SETSTATUSTEXT, 0, Integer( PKOLChar( _Self_.FStatusText ) ) );
+ end;
+ Result := 0;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_LOCAL} {$UNDEF ASM_LOCAL} {$ENDIF}
+{$IFNDEF NEW_OPEN_DIR_STYLE_EX}
+ {$IFDEF ASM_VERSION} {$DEFINE ASM_LOCAL} {$ENDIF}
+{$ENDIF}
+
+{$IFDEF ASM_LOCAL}
+{$ELSE PAS_VERSION} //Pascal
+function OpenDirCallBack( Wnd: HWnd; Msg: DWORD; lParam, lpData: LParam ): Integer;
+ stdcall;
+const
+ Shel: array[ 0..3 ] of AnsiChar = 'SHBr'; // KOL_ANSI used as DWORD
+var Self_ : POpenDirDialog;
+ {$IFDEF NEW_OPEN_DIR_STYLE_EX}
+ WList: HWnd;
+ ClassBuf: array[ 0..127 ] of KOLChar;
+ {$ENDIF}
+begin
+ Self_ := Pointer( lpData );
+ Self_.FDialogWnd := Wnd;
+ if Msg = BFFM_INITIALIZED then
+ begin
+ if Assigned( Self_.FCenterProc ) then
+ Self_.FCenterProc( Wnd );
+ if Self_.FInitialPath <> '' then
+ begin
+ {$IFDEF NEW_OPEN_DIR_STYLE_EX}
+ WList := GetWindow( Wnd, GW_CHILD );
+ while WList <> 0 do
+ begin
+ WList := GetWindow( WList, GW_HWNDNEXT );
+ GetClassName( WList, @ ClassBuf[ 0 ], Sizeof( ClassBuf ) );
+ if PDWord( @ ClassBuf[ 0 ] )^ = DWORD( Shel ) then
+ begin
+ PostMessage( Wnd, WM_NEXTDLGCTL, WList, 1 );
+ break;
+ end;
+ end;
+ PostMessage( Wnd, {$IFDEF UNICODE_CTRLS} BFFM_SETSELECTIONW
+ {$ELSE} BFFM_SETSELECTION {$ENDIF}, 1, Integer( PKOLChar(
+ ExtractFilePath( Self_.FInitialPath ) ) ) );
+ PostMessage( WND, WM_KEYDOWN, VK_ADD, 0 );
+ PostMessage( WND, WM_KEYUP, VK_ADD, 0 );
+ PostMessage( Wnd, {$IFDEF UNICODE_CTRLS} BFFM_SETSELECTIONW
+ {$ELSE} BFFM_SETSELECTION {$ENDIF}, 1, Integer( PKOLChar( Self_.FInitialPath ) ) );
+ {$ELSE}
+ SendMessage( Wnd, {$IFDEF UNICODE_CTRLS} BFFM_SETSELECTIONW
+ {$ELSE} BFFM_SETSELECTION {$ENDIF}, 1, Integer( PKOLChar( Self_.FInitialPath ) ) );
+ {$ENDIF}
+ SendMessage( Wnd, BFFM_ENABLEOK, 0, 1 );
+ end;
+ end
+ else
+ if Msg = BFFM_SELCHANGED then
+ begin
+ if Assigned( Self_.FDoSelChanged ) then
+ Self_.FDoSelChanged( Wnd, Msg, lParam, lpData )
+ else
+ SendMessage( Wnd, BFFM_ENABLEOK, 0, 1 );
+ end;
+ Result := 0;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure OpenDirDlgCenter( Wnd: HWnd );
+var R: TRect;
+ W, H: Integer;
+begin
+ GetWindowRect( Wnd, R );
+ W := R.Right - R.Left;
+ H := R.Bottom - R.Top;
+ R.Left := (GetSystemMetrics( SM_CXSCREEN ) - W) div 2;
+ R.Top := (GetSystemMetrics( SM_CYSCREEN ) - H) div 2;
+ MoveWindow( Wnd, R.Left, R.Top, W, H, True );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TOpenDirDialog.SetCenterOnScreen(const Value: Boolean);
+var P: procedure( Wnd: HWnd );
+begin
+ FCenterOnScreen := Value;
+ P := nil;
+ if Value then
+ P := @OpenDirDlgCenter;
+ FCenterProc := P;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TOpenDirDialog.SetInitialPath(const Value: KOLString);
+begin
+ FCallBack := @OpenDirCallBack;
+ FInitialPath := ExcludeTrailingPathDelimiter( Value );
+ if (FInitialPath <> '') and
+ (FInitialPath[ Length( FInitialPath ) ] = ':') then
+ FInitialPath := IncludeTrailingPathDelimiter( Value );
+end;
+
+procedure TOpenDirDialog.SetOnSelChanged(const Value: TOnODSelChange);
+begin
+ FOnSelChanged := Value;
+ FCallBack := @OpenDirCallBack;
+ FDoSelChanged := @OpenDirSelChangeCallBack;
+end;
+
+type
+ PByteArray =^TByteArray;
+ TByteArray = array[Word]of Byte;
+
+function CreateMappedBitmap(Instance: THandle; Bitmap: Integer;
+ Flags: UINT; ColorMap: PColorMap; NumMaps: Integer): HBitmap; stdcall;
+ external cctrl name 'CreateMappedBitmap';
+
+function CreateMappedBitmapEx(Instance: THandle; BmpRsrcName: PKOLChar; Flags:
+Cardinal; ColorMap: PColorMap; NumMaps: Integer): HBitmap;
+var bi: TBITMAPINFO;
+ DC, tmcl: Cardinal;
+ Bits: PByteArray;
+ i, j, k, CO, bps: Integer;
+ tm: array [1..4] of byte absolute tmcl;
+ bm: Windows.TBITMAP;
+ CM: PColorMap;
+ DW: HWnd;
+begin
+ Result := LoadBitmap( Instance, BmpRsrcName );
+ if Result = 0 then
+ begin
+ {$IFDEF DEBUG_ANY}
+ ShowMessage( AnsiString('Can not load bitmap ') + BmpRsrcName + ', error ' +
+ Int2Str( GetLastError ) + ': ' + SysErrorMessage( GetLastError ) );
+ {$ENDIF DEBUG_ANY}
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ DW := GetDesktopWindow;
+ DC := GetDC(DW);
+ ZeroMemory( @bm, SizeOf(bm) );
+ GetObject( Result, SizeOf( bm ), @bm );
+
+ ZeroMemory( @bi, SizeOf( bi ) );
+ bi.bmiHeader.biSize := SizeOf( bi.bmiHeader );
+ bi.bmiHeader.biWidth := bm.bmWidth;
+ bi.bmiHeader.biHeight := -bm.bmHeight;
+ bi.bmiHeader.biPlanes := 1;
+ bi.bmiHeader.biBitCount := 24;
+// BitCout - always 24 for easy algorythm
+ bi.bmiHeader.biCompression:=BI_RGB;
+ bps := CalcScanLineSize( @bi.bmiHeader );
+
+ GetMem( Bits, bps * bm.bmHeight );
+ GetDIBits( DC, Result, 0, bm.bmHeight, @Bits[0], bi, DIB_RGB_COLORS );
+ DeleteObject( Result );
+
+ for i := 0 to bm.bmHeight - 1 do begin
+ for j := 0 to bm.bmWidth - 1 do begin
+ CO := bps * i + 3 * j;
+ for k := 0 to NumMaps - 1 do begin
+ CM := Pointer( Integer( ColorMap ) + SizeOf( TColorMap ) * k );
+ if RGB( Bits[CO+2], Bits[CO+1], Bits[CO] ) = CM.cFrom then
+ begin
+ tmcl := CM.cTo;
+ tm[4]:=tm[1];
+ tm[1]:=tm[3];
+ tm[3]:=tm[4];
+ Move( tmcl, Bits[CO], 3);
+ end;
+ end;
+ end;
+ end;
+ Result := CreateDIBitmap( DC, bi.bmiHeader, CBM_INIT, @Bits[0], bi,
+ DIB_RGB_COLORS );
+ ReleaseDC( DW, DC );
+ FreeMem( Bits );
+end;
+
+function LoadMappedBitmap( hInst: THandle; BmpResID: Integer; const Map: array of TColor )
+ : HBitmap;
+var Map2Pass: Pointer;
+begin
+ Map2Pass := nil;
+ if High( Map ) > 0 then
+ Map2Pass := PColorMap( @Map[ 0 ] );
+ Result := CreateMappedBitmap( hInst, BmpResID, 0, Map2Pass, (High( Map ) + 1) div 2 );
+end;
+
+function LoadMappedBitmapEx( MasterObj: PObj; hInst: THandle; BmpResName: PKOLChar; const Map: array of TColor )
+ : HBitmap;
+var Map2Pass: Pointer;
+begin
+ Map2Pass := nil;
+ if High( Map ) > 0 then
+ Map2Pass := PColorMap( @Map[ 0 ] );
+ Result := CreateMappedBitmapEx( hInst, BmpResName, 0, Map2Pass, (High( Map ) + 1) div 2 );
+ if MasterObj <> nil then
+ MasterObj.Add2AutoFreeEx( TObjectMethod( MakeMethod( Pointer( Result ), @ FreeBmp ) ) );
+end;
+
+{ -- Toolbar -- }
+
+{$IFDEF ASM_noVERSION} // width
+procedure TControl.TBAddBitmap(Bitmap: HBitmap);
+const szBI = sizeof(TBitmapInfo);
+asm
+ TEST EDX, EDX
+ JZ @@exit
+ JGE @@1
+ CMP EDX, -6
+ JL @@1
+ NEG EDX
+ DEC EDX
+ PUSH EDX
+ PUSH -1
+ XOR EDX, EDX
+ JMP @@2
+@@1: PUSH EDX // AB.hInst = Bitmap
+ PUSH 0 // AB.nID = 0
+ PUSH EAX // > @Self
+ ADD ESP, -szBI
+ PUSH ESP
+ PUSH szBI
+ PUSH EDX
+ CALL GetObject
+ TEST EAX, EAX
+ JG @@11
+ ADD ESP, szBI
+ JMP @@exit
+@@11: MOV EAX, [ESP].TBitmapInfo.bmiHeader.biWidth
+ MOV ECX, [ESP].TBitmapInfo.bmiHeader.biHeight
+ TEST ECX, ECX
+ JGE @@12
+ NEG ECX
+@@12: ADD ESP, szBI
+ CDQ // EDX = 0
+ DIV ECX // EAX = N
+ XCHG EAX, [ESP] // > N
+ PUSH EAX // > @Self
+ MOV EDX, ECX
+ SHL EDX, 16
+ OR ECX, EDX
+ CDQ
+ PUSH EDX
+ PUSH EDX
+ PUSH TB_AUTOSIZE
+ PUSH EAX
+ PUSH ECX
+ PUSH EDX
+ PUSH TB_SETBITMAPSIZE
+ PUSH EAX
+ CALL Perform
+ CALL Perform
+ POP EAX
+ POP EDX
+@@2: PUSH ESP
+ PUSH EDX
+ PUSH TB_ADDBITMAP
+ PUSH EAX
+ CALL Perform
+ POP ECX
+ POP ECX
+@@exit:
+end;
+{$ELSE PAS_VERSION} //Pascal
+procedure TControl.TBAddBitmap(Bitmap: HBitmap);
+const NstdBitmaps: array[ 0..5 ] of DWORD = ( 15, 15, 0, 0, 13, 13 );
+var BI: TBitmapInfo;
+ AB: TTBAddBitmap;
+ N, W: Integer;
+begin
+ if Bitmap = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if (Integer( Bitmap ) >= -10) and (Integer( Bitmap ) <= -1) then
+ begin
+ AB.hInst := THandle(-1);
+ AB.nID := -Integer(Bitmap) - 1;
+ N := 0; //NstdBitmaps[ AB.nID ]; // (this value is ignored)
+ end
+ else
+ if GetObject( Bitmap, sizeof( TBitmapInfo ), @BI ) > 0 then
+ begin
+ AB.hInst := 0;
+ AB.nID := Bitmap;
+ W := DF.fTBBtnImgWidth;
+ if W = 0 then
+ W := Abs( BI.bmiHeader.biHeight );
+ N := BI.bmiHeader.biWidth div W;
+ Perform( TB_SETBITMAPSIZE, 0, MAKELONG( W, Abs(BI.bmiHeader.biHeight )) );
+ Perform( TB_AUTOSIZE, 0, 0 );
+ end else Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Perform( TB_ADDBITMAP, N, Integer( @AB ) );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function TControl.TBAddInsButtons(Idx: Integer; const Buttons: array of PKOLChar;
+ const BtnImgIdxArray: array of Integer): Integer; stdcall;
+
+ function AddInsButtons: Integer;
+ type TTBBtnArray = array[ 0..100000 ] of TTBButton;
+ PTBBtnArray = ^TTBBtnArray;
+ var AB: PTBBtnArray;
+ I, N, nBmp: Integer;
+ PAB: PTBButton;
+ Str: PKOLChar;
+ Str0: KOLString;
+ begin
+ Result := -1;
+ AB := nil;
+ if High( Buttons ) >= 0 then
+ GetMem( AB, Sizeof( TTBButton ) * (High(Buttons) + 1) );
+ N := 0;
+ PAB := @AB[ 0 ];
+ nBmp := -2;
+ if High(BtnImgIdxArray) >= 0 then
+ nBmp := BtnImgIdxArray[ 0 ] - 1;
+ for I:= 0 to High( Buttons ) do
+ begin
+ if Buttons[ I ] = nil then break;
+ if {$IFDEF UNICODE_CTRLS} WStrComp {$ELSE} StrComp {$ENDIF}
+ ( Buttons[ I ], {$IFDEF F_P}''+{$ENDIF} '-' ) = 0 then
+ begin
+ PAB.iBitmap := -1;
+ //PAB.idCommand := 0;
+ PAB.fsState := 0;
+ PAB.fsStyle := TBSTYLE_SEP;
+ PAB.iString := -1;
+ end
+ else
+ begin
+ Str := Buttons[ I ];
+ Inc( nBmp );
+ PAB.iBitmap := nBmp;
+ if nBmp < 0 then
+ Dec( nBmp );
+ if High( BtnImgIdxArray ) >= N then
+ PAB.iBitmap := BtnImgIdxArray[ N ];
+ PAB.fsState := TBSTATE_ENABLED;
+ PAB.fsStyle := TBSTYLE_BUTTON or DF.fDefaultTBBtnStyle;
+ if Str^ = '^' then
+ begin
+ PAB.fsStyle := TBSTYLE_DROPDOWN or DF.fDefaultTBBtnStyle;
+ Inc( Str );
+ end;
+ if CharIn( Str^, [ '-', '+' ] ) then
+ begin
+ PAB.fsStyle := PAB.fsStyle or TBSTYLE_CHECK;
+ if Str^ = '+' then
+ PAB.fsState := PAB.fsState or TBSTATE_CHECKED;
+ Inc( Str );
+ if Str^ = '!' then
+ begin
+ PAB.fsStyle := PAB.fsStyle or TBSTYLE_GROUP;
+ Inc( Str );
+ end;
+ end;
+ {$IFDEF TOOLBAR_DOT_NOAUTOSIZE_BUTTON}
+ if Str^ = '.' then
+ begin
+ PAB.fsStyle := PAB.fsStyle and not TBSTYLE_AUTOSIZE;
+ inc( Str );
+ end;
+ {$ENDIF TOOLBAR_DOT_NOAUTOSIZE_BUTTON}
+ if (KOLString(Str) = KOLString( {$IFDEF F_P}''+{$ENDIF} KOLChar( ' ' ) )) or (Str^ = #0) then
+ PAB.iString := -1
+ else
+ begin
+ Str0 := KOLString('') + KOLString(Str) + #0;
+ PAB.iString := Perform( TB_ADDSTRING, 0, Integer(PKOLChar(Str0)) );
+ end;
+ end;
+
+ PAB.idCommand := ToolbarsIDcmd;
+ if Result < 0 then Result := PAB.idCommand;
+ Inc( ToolbarsIDcmd );
+
+ PAB.dwData := Integer( @Self );
+ Inc( N );
+ Inc( PAB );
+ end;
+ if N > 0 then
+ begin
+ if Idx < 0 then
+ Perform( TB_ADDBUTTONS, N, Integer( @AB[ 0 ] ) )
+ else
+ Perform( TB_INSERTBUTTON, Idx, Integer( @AB[ 0 ] ) );
+ end;
+ if AB <> nil then
+ FreeMem( AB );
+ end;
+begin
+ if High( Buttons ) < 0 then
+ Result := -1
+ else
+ Result := AddInsButtons;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.TBAddButtons(const Buttons: array of PKOLChar;
+ const BtnImgIdxArray: array of Integer): Integer;
+begin
+ Result := TBAddInsButtons( -1, Buttons, BtnImgIdxArray );
+end;
+{$ENDIF PAS_VERSION}
+
+function TControl.TBInsertButtons(BeforeIdx: Integer;
+ Buttons: array of PKOLChar; const BtnImgIdxArray: array of Integer): Integer;
+var I, J, K: Integer;
+begin
+ J := -1;
+ Result := -1;
+ for I := 0 to High( Buttons ) do
+ begin
+ if I <= High( BtnImgIdxArray ) then
+ J := BtnImgIdxArray[ I ]
+ else
+ if J >= 0 then Inc( J );
+ K := TBAddInsButtons( BeforeIdx, [ Buttons[ I ], '' ], [ J ] );
+ if Result < 0 then Result := K;
+ end;
+end;
+
+function GetTBBtnGoodID( Toolbar: PControl; BtnIDorIdx: Integer ): Integer;
+// change by Alexander Pravdin (to fix toolbar with separator first):
+var Btn1st, i: Integer; btn: TTBButton;
+begin
+ Result := BtnIDorIdx;
+ Btn1st := 0;
+ for i := 0 to Toolbar.TBButtonCount - 1 do begin
+ Toolbar.Perform( TB_GETBUTTON, i, Integer( @btn ) );
+ if btn.fsStyle <> TBSTYLE_SEP then begin
+ Btn1st := i;
+ Break;
+ end;
+ end;
+ if Result < Toolbar.TBIndex2Item( Btn1st ) then
+ Result := Toolbar.TBIndex2Item( Result );
+end;
+
+type
+ TTBButtonEvent = packed Record
+ BtnID: DWORD;
+ Event: TOnToolbarButtonClick;
+ end;
+ PTBButtonEvent = ^TTBButtonEvent;
+
+procedure TControl.TBFreeTBevents;
+begin
+ DF.fTBevents.Release;
+end;
+
+function WndProcToolbarButtonsClicks( TB: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var Notify: PTBNotify;
+ I: Integer;
+ Event: PTBButtonEvent;
+begin
+ Result := FALSE;
+ if Msg.message = WM_NOTIFY then
+ begin
+ Notify := Pointer( Msg.lParam );
+ if Notify.hdr.code = NM_CLICK then
+ begin
+ for I := TB.DF.fTBevents.fCount-1 downto 0 do
+ begin
+ Event := TB.DF.fTBevents.{$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}[ I ];
+ if Integer( Event.BtnID ) = Notify.iItem then
+ begin
+ if Assigned( Event.Event ) then
+ begin
+ TB.RefInc;
+ Rslt := DefWindowProc( Msg.hwnd, Msg.message, Msg.wParam, Msg.lParam );
+ Event.Event( TB, Event.BtnID );
+ TB.RefDec;
+ Result := TRUE; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ break;
+ end;
+ end;
+ end;
+ end;
+end;
+
+procedure TControl.TBAssignEvents(BtnID: Integer;
+ Events: array of TOnToolbarButtonClick);
+var I: Integer;
+ EventRec: PTBButtonEvent;
+begin
+ if DF.fTBevents = nil then
+ begin
+ DF.fTBevents := NewList;
+ Add2AutoFreeEx( TBFreeTBevents );
+ AttachProc( WndProcToolbarButtonsClicks );
+ end;
+ BtnID := GetTBBtnGoodID( @Self, BtnID );
+ for I := 0 to High( Events ) do
+ begin
+ GetMem( EventRec, Sizeof( TTBButtonEvent ) );
+ DF.fTBevents.Add( EventRec );
+ EventRec.Event := Events[ I ];
+ EventRec.BtnID := BtnID;
+ Inc( BtnID );
+ end;
+end;
+
+function TControl.TBBtnEvent( Idx: Integer ): TOnToolbarButtonClick;
+var EventRec: PTBButtonEvent;
+begin
+ Result := nil;
+ if DF.fTBevents = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if Idx < DF.fTBevents.Count then
+ begin
+ EventRec := DF.fTBevents.{$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}
+ [ Idx ];
+ Result := EventRec.Event;
+ end;
+end;
+
+procedure TControl.TBResetImgIdx( BtnID, BtnCount: Integer );
+begin
+ while BtnCount > 0 do
+ begin
+ TBButtonImage[ BtnID ] := -2;
+ Inc( BtnID );
+ Dec( BtnCount );
+ end;
+end;
+
+function TControl.TBGetButtonVisible(BtnID: Integer): Boolean;
+begin
+ Result := Perform( TB_ISBUTTONHIDDEN, GetTBBtnGoodID( @ Self, BtnID ), 0 ) = 0;
+end;
+
+function TControl.TBItem2Index(BtnID: Integer): Integer;
+begin
+ Result := Perform( TB_COMMANDTOINDEX, BtnID, 0 );
+end;
+
+procedure TControl.TBSetButtonVisible(BtnID: Integer;
+ const Value: Boolean);
+begin
+ BtnID := GetTBBtnGoodID( @Self, BtnID );
+ Perform( TB_HIDEBUTTON, BtnID, Integer( not Value ) );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.TBGetBtnStt(BtnID: Integer; const Index: Integer): Boolean;
+begin
+ BtnID := GetTBBtnGoodID( @Self, BtnID );
+ Result := Perform( Index + 8, BtnID, 0 ) <> 0;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TControl.TBSetBtnStt(BtnID: Integer; const Index: Integer; const Value: Boolean);
+begin
+ BtnID := GetTBBtnGoodID( @Self, BtnID );
+ Perform( Index, BtnID, Integer( Value ) );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.TBIndex2Item(Idx: Integer): Integer;
+var ButtonInfo: TTBButton;
+begin
+ Result := -1;
+ if Perform( TB_GETBUTTON, Idx, Integer( @ButtonInfo ) ) <> 0 then
+ Result := ButtonInfo.idCommand;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TControl.TBConvertIdxArray2ID(const IdxVars: array of PDWORD);
+var i: Integer;
+begin
+ for i := 0 to High( IdxVars ) do
+ IdxVars[ i ]^ := TBIndex2Item( IdxVars[ I ]^ );
+end;
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+function TControl.TBGetButtonText( BtnID: Integer ): KOLString;
+var Buffer: array[ 0..1023 ] of KOLChar;
+begin
+ BtnID := GetTBBtnGoodID( @Self, BtnID );
+ if Perform( TB_GETBUTTONTEXT, BtnID, Integer( @Buffer[ 0 ] ) ) > 0 then
+ Result := Buffer
+ else
+ Result := '';
+end;
+{$ENDIF PAS_VERSION}
+
+function TControl.TBGetButtonRect(BtnID: Integer): TRect;
+begin
+ BtnID := GetTBBtnGoodID( @Self, BtnID );
+ Perform( TB_GETITEMRECT, TBItem2Index( BtnID ), Integer( @Result ) );
+end;
+
+function ToolbarButtonRect( Toolbar: PControl; BtnID: Integer ): TRect;
+begin
+ Result := Toolbar.TBGetButtonRect(BtnID);
+end;
+
+function TControl.TBGetRows: Integer;
+begin
+ Result := 1;
+ UpdateWndStyles;
+ if (TBSTYLE_WRAPABLE and fStyle.Value) <> 0 then
+ Result := Perform( TB_GETROWS, 0, 0 );
+end;
+
+procedure TControl.TBSetRows(const Value: Integer);
+begin
+ Perform( TB_SETROWS, Value, 0 );
+end;
+
+function TControl.TBMoveBtn(FromIdx, ToIdx: Integer): Boolean;
+var btn: TTBButton;
+begin
+ Perform(TB_GETBUTTON,FromIdx,integer(@btn));
+ Result := Perform(TB_DELETEBUTTON,FromIdx,0) <> 0;
+ if Result then
+ Perform(TB_INSERTBUTTON,ToIdx,integer(@btn));
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.TBSetTooltips(BtnID1st: Integer;
+ const Tooltips: array of PKOLChar);
+var I, J: Integer;
+begin
+ if ( DF.fTBttCmd = nil ) then
+ begin
+ DF.fTBttCmd := NewList;
+ DF.fTBttTxt := {$IFDEF UNICODE_CTRLS} NewWStrList {$ELSE} NewStrList {$ENDIF};
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ Add2AutoFree( DF.fTBttCmd );
+ Add2AutoFree( DF.fTBttTxt );
+ {$ENDIF}
+ end;
+ for I:= 0 to High( Tooltips ) do
+ begin
+ J := DF.fTBttCmd.IndexOf( Pointer( BtnID1st ) );
+ if J < 0 then
+ begin
+ DF.fTBttCmd.Add( Pointer( BtnID1st ) );
+ DF.fTBttTxt.Add( Tooltips[ I ] );
+ end
+ else
+ DF.fTBttTxt.Items[ J ] := Tooltips[ I ];
+ Inc( BtnID1st );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+function TControl.TBBtnTooltip( BtnID: Integer ): KOLString;
+var J: Integer;
+begin
+ Result := '';
+ if DF.fTBttCmd = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ J := DF.fTBttCmd.IndexOf( Pointer( BtnID ) );
+ if J < 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := DF.fTBttTxt.Items[ J ];
+end;
+
+procedure ToolbarSetTooltips( Toolbar: PControl; BtnID1st: Integer;
+ const Tooltips: array of PKOLChar );
+begin
+ Toolbar.TBSetTooltips( BtnID1st, Tooltips );
+end;
+
+function ToolbarButtonEnabled( Toolbar: PControl; BtnID: Integer ): Boolean;
+begin
+ Result := Toolbar.TBButtonEnabled[ BtnID ];
+end;
+
+procedure EnableToolbarButton( Toolbar: PControl; BtnID: Integer; Enable: Boolean );
+begin
+ Toolbar.TBButtonEnabled[ BtnID ] := Enable;
+end;
+
+function ToolbarButtonVisible( Toolbar: PControl; BtnID: Integer ): Boolean;
+begin
+ Result := Toolbar.TBButtonVisible[ BtnID ];
+end;
+
+procedure ShowHideToolbarButton( Toolbar: PControl; BtnID: Integer; Show: Boolean );
+begin
+ Toolbar.TBButtonVisible[ BtnID ] := Show;
+end;
+
+function ToolbarButtonChecked( Toolbar: PControl; BtnID: Integer): Boolean;
+begin
+ Result := Toolbar.TBButtonChecked[ BtnID ];
+end;
+
+procedure ToolbarButtonSetChecked( Toolbar: PControl; BtnID: Integer; Checked: Boolean );
+begin
+ Toolbar.TBButtonChecked[ BtnID ] := Checked;
+end;
+
+procedure ToolbarAddButtons( Toolbar: PControl; const Buttons: array of PKOLChar;
+ const BtnImgIdxArray: array of Integer; Bitmap: HBitmap );
+begin
+ Toolbar.TBAddButtons( Buttons, BtnImgIdxArray );
+ if Bitmap <> 0 then
+ Toolbar.TBAddBitmap( Bitmap );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.TBButtonAtPos(X, Y: Integer): Integer;
+var I: Integer;
+begin
+ I := TBBtnIdxAtPos( X, Y );
+ if I >= 0 then
+ I := TBIndex2Item( I );
+ Result := I;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.TBBtnIdxAtPos(X, Y: Integer): Integer;
+var I: Integer;
+ R: TRect;
+ P: TPoint;
+begin
+ P := MakePoint( X, Y );
+ for I := TBButtonCount - 1 downto 0 do
+ begin
+ Perform( TB_GETITEMRECT, I, Integer( @R ) );
+ if PointInRect( P, R ) then
+ begin
+ Result := I; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ Result := -1;
+end;
+{$ENDIF PAS_VERSION}
+
+function TControl.TBButtonSeparator(BtnID: Integer): Boolean;
+var B: TTBButton;
+begin
+ Perform( TB_GETBUTTON, TBItem2Index( GetTBBtnGoodID( @Self, BtnID )
+), Integer( @B ) ) ;
+ Result := B.fsStyle = TBSTYLE_SEP;
+end;
+
+procedure TControl.TBDeleteButton(BtnID: Integer);
+begin
+ BtnID := GetTBBtnGoodID( @Self, BtnID );
+ Perform( TB_DELETEBUTTON, TBItem2Index( BtnID ), 0 );
+end;
+
+procedure TControl.TBDeleteBtnByIdx(Idx: Integer);
+begin
+ Perform( TB_DELETEBUTTON, Idx, 0 );
+end;
+
+procedure TControl.TBClear;
+var
+ i: Integer;
+begin
+ for i := 0 to Pred(TBButtonCount) do
+ TBDeleteBtnByIdx(0);
+end;
+
+procedure TControl.Clear;
+begin
+ fCommandActions.aClear( @Self );
+end;
+
+{$IFDEF ASM_noVERSION}
+function TControl.TBGetBtnImgIdx(BtnID: Integer): Integer;
+const szTBButton = sizeof( TTBButton );
+asm
+ ADD ESP, -szTBButton
+ PUSH ESP
+ PUSH EAX
+ CALL TBItem2Index
+ POP EDX
+ PUSH EAX
+ PUSH TB_GETBUTTON
+ PUSH EDX
+ CALL Perform
+ POP EAX
+ ADD ESP, szTBButton-4
+end;
+{$ELSE PAS_VERSION} //Pascal
+function TControl.TBGetBtnImgIdx(BtnID: Integer): Integer;
+var B: TTBButton;
+begin
+ Perform( TB_GETBUTTON, TBItem2Index( GetTBBtnGoodID( @Self, BtnID ) ), Integer( @B ) );
+ Result := B.iBitmap;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TControl.TBSetBtnImgIdx(BtnID: Integer; const Value: Integer);
+begin
+ Perform( TB_CHANGEBITMAP, GetTBBtnGoodID( @Self, BtnID ), Value );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.TBSetButtonText(BtnID: Integer; const Value: KOLString);
+var BI: TTBButtonInfo;
+begin
+ BtnID := GetTBBtnGoodID( @Self, BtnID );
+ BI.cbSize := Sizeof( BI );
+ BI.dwMask := TBIF_TEXT;
+ BI.pszText := PKOLChar( Value );
+ Perform( TB_SETBUTTONINFO, BtnID, Integer( @BI ) );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.TBGetBtnWidth(BtnID: Integer): Integer;
+var R: TRect;
+begin
+ R := TBButtonRect[ BtnID ];
+ Result := R.Right - R.Left;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.TBSetBtnWidth(BtnID: Integer; const Value: Integer);
+var BI: TTBButtonInfo;
+begin
+ BI.cbSize := Sizeof( BI );
+ BI.dwMask := TBIF_SIZE or TBIF_STYLE;
+ BtnID := GetTBBtnGoodID( @Self, BtnID );
+ Perform( TB_GETBUTTONINFO, BtnID, Integer( @BI ) );
+ BI.cx := Value;
+ BI.fsStyle := BI.fsStyle and not TBSTYLE_AUTOSIZE;
+ Perform( TB_SETBUTTONINFO, BtnID, Integer( @BI ) );
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TControl.TBSetBtMinMaxWidth(const Idx: Integer; const Value: Integer);
+begin
+ case Idx of
+ 0: DF.fTBBtMinWidth := Value;
+ 1: DF.fTBBtMaxWidth := Value;
+ end;
+ Perform( TB_SETBUTTONWIDTH, 0, DF.fTBBtMaxWidth or (DF.fTBBtMinWidth shl 16) );
+end;
+
+{$IFDEF F_P}
+function TControl.TBGetBtMinMaxWidth(const Idx: Integer): Integer;
+begin
+ CASE Idx OF
+ 0: Result := FTBBtMinWidth;
+ 1: Result := FTBBtMaxWidth;
+ END;
+end;
+{$ENDIF F_P}
+
+function TControl.TBGetButtonLParam(const Idx: Integer): DWORD;
+var
+ tb: TTBButtonInfo;
+begin
+ tb.cbSize := sizeof(tb);
+ tb.dwMask := TBIF_BYINDEX or TBIF_LPARAM;
+ Perform(TB_GETBUTTONINFO, Idx, Integer(@tb));
+ Result := tb.lParam;
+end;
+
+procedure TControl.TBSetButtonLParam(const Idx: Integer; const Value: DWORD);
+var
+ tb: TTBButtonInfo;
+begin
+ tb.cbSize := sizeof(tb);
+ tb.dwMask := TBIF_BYINDEX or TBIF_LPARAM;
+ tb.lParam := Value;
+ Perform(TB_SETBUTTONINFO, Idx, Integer(@tb));
+end;
+
+function WndProcTBCustomDraw( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var CD: PNMTBCustomDraw;
+ Br: HBrush;
+begin
+ Result := FALSE;
+ if Msg.message = WM_NOTIFY then
+ begin
+ CD := Pointer( Msg.lParam );
+ if CD.nmcd.hdr.code = NM_CUSTOMDRAW then
+ begin
+ if Assigned( Sender.DF.fOnTBCustomDraw ) then
+ Rslt := Sender.DF.fOnTBCustomDraw( Sender, CD^ )
+ else
+ begin
+ if Sender.fBrush <> nil then
+ Windows.FillRect( CD.nmcd.hdc, Sender.ClientRect, Sender.fBrush.Handle )
+ else
+ begin
+ Br := CreateSolidBrush( Color2RGB( Sender.Color ) );
+ Windows.FillRect( CD.nmcd.hdc, Sender.ClientRect, Br );
+ DeleteObject( Br );
+ end;
+ Rslt := CDRF_SKIPDEFAULT;
+ end;
+ end;
+ end;
+end;
+
+procedure TControl.SetOnTBCustomDraw( const Value: TOnTBCustomDraw );
+begin
+ DF.fOnTBCustomDraw := Value;
+ AttachProc( WndProcTBCustomDraw );
+end;
+
+
+procedure TControl.SetDroppedDown(const Value: Boolean);
+begin
+ Perform( CB_SHOWDROPDOWN, Integer( Value ), 0 );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.AddDirList(const Filemask: KOLString; Attrs: DWORD);
+begin
+ if fCommandActions.aDir <> 0 then
+ Perform( fCommandActions.aDir, Attrs, Integer( PKOLChar( Filemask ) ) );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_noVERSION}
+{$ELSE PAS_VERSION} //Pascal
+function WndProcShowModal( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+//var Accept: Boolean; // {Alexander Pravdin, AP}
+begin
+ Result := FALSE;
+ if Msg.message = WM_CLOSE then
+ begin
+ if Self_.DF.fModalResult = 0 then { (Sergey Shishmintzev) }
+ Self_.DF.fModalResult := -1;
+ Rslt := 0;
+ Result := True; // Do not process !
+ end
+ ;
+end;
+{$ENDIF PAS_VERSION}
+
+// by TR"]F
+function WndProcFixModal( Self_: PControl; var Msg: TMsg; var Rslt:
+Integer ): Boolean;
+const HTERROR = $FFFE;
+ LBtnDown = $201;
+ LBtnUp = $202;
+ RBtnDown = $204;
+ RBtnUp = $205;
+ WeelDown = $207;
+ WeelUp = $208;
+{$IFDEF MODAL_ACTIVATE_FIX}
+var i: Integer;
+ C: PControl;
+{$ENDIF MODAL_ACTIVATE_FIX}
+begin
+ Result := false;
+ if (Msg.message = WM_SETCURSOR) then
+ if (LoWord(Msg.lParam) = HTERROR) then
+ if (HiWord(Msg.lParam) >= LBtnDown) and
+ (HiWord(Msg.lParam) <= RBtnUp) then
+ begin
+ if Applet.DF.fModalForm <> nil then
+ SetForegroundWindow(Applet.DF.fModalForm.Handle);
+ Rslt := 1;
+ Result := TRUE;
+ end;
+
+ {$IFDEF MODAL_ACTIVATE_FIX}
+ if (Msg.message = WM_ACTIVATEAPP) then
+ begin
+ if not Applet.DF.fActivating then
+ begin
+ Applet.DF.fActivating := TRUE;
+ if Msg.wParam <> 0 then
+ begin
+ for i := Applet.ChildCount-1 downto 0 do
+ begin
+ C := Applet.Children[ i ];
+ if C.Visible and not C.Enabled then
+ SetForegroundWindow( C.Handle );
+ end;
+ if Applet.DF.fModalForm <> nil then
+ SetForegroundWindow( Applet.DF.fModalForm.Handle );
+ end;
+ Applet.DF.fActivating := FALSE;
+ end;
+ end;
+ {$ENDIF MODAL_ACTIVATE_FIX}
+end;
+
+{$IFDEF ASM_noVERSION} // ASM_TLIST!
+function TControl.ShowModal: Integer;
+asm
+ MOV ECX, [EAX].fParent
+ JECXZ @@show
+ MOVZX ECX, [EAX].fIsControl
+ JECXZ @@show_modal
+@@show:
+ CALL Show
+ XOR EAX, EAX
+ RET
+@@show_modal:
+ PUSHAD
+ MOV EBX, EAX
+ MOV EDI, [Applet]
+ XOR EBP, EBP // CurCtl = nil
+ MOV EAX, [EDI].fCurrentControl
+ {$IFDEF USE_FLAGS}
+ TEST [EDI].TControl.fFlagsG3, (1 shl G3_IsApplet)
+ {$ELSE}
+ CMP [EDI].TControl.FIsApplet, 0
+ {$ENDIF}
+ {$IFDEF USE_CMOV}
+ CMOVZ EAX, EDI
+ {$ELSE}
+ JNZ @@curctrl_save
+ MOV EAX, EDI
+@@curctrl_save:
+ {$ENDIF}
+ PUSH EAX
+ MOV EDX, offset[WndProcShowModal]
+ PUSH EDX
+ MOV EAX, EBX
+ CALL TControl.AttachProc
+ XOR EDX, EDX
+ MOV [EBX].fModalResult, EDX
+ CALL NewList
+ XCHG EAX, EBP
+ XOR ECX, ECX
+ INC ECX
+ MOV ESI, EDI
+ {$IFDEF USE_FLAGS}
+ TEST [EDI].TControl.fFlagsG3, (1 shl G3_IsApplet)
+ {$ELSE}
+ CMP [EDI].TControl.FIsApplet, 0
+ {$ENDIF}
+ JZ @@isapplet
+ MOV EBP, [EDI].fCurrentControl // CurCtl = Applet.fCurrentControl
+ MOV ESI, [EDI].fChildren
+ MOV ECX, [ESI].TList.fCount
+ MOV ESI, [ESI].TList.fItems
+@@1loo: LODSD
+@@isapplet:
+ PUSH ECX
+ CMP EAX, EBX
+ JE @@1nx
+ PUSH EAX
+ CALL GetEnabled
+ TEST AL, AL
+ POP EAX
+ JZ @@1nx
+ PUSH EAX
+ MOV DL, 0
+ CALL SetEnabled
+ POP EDX
+ MOV EAX, EBP
+ CALL TList.Add
+@@1nx: POP ECX
+ LOOP @@1loo
+ INC [EBX].fModal
+ MOV EAX, [Applet]
+ MOV [EAX].fModalForm, EBX
+ MOV EAX, EBX
+ CALL Show
+@@msgloo:
+ MOVZX ECX, [AppletTerminated]
+ OR ECX, [EBX].fModalResult
+ JNZ @@e_msgloo
+ CALL WaitMessage
+ MOV EAX, EDI
+ CALL ProcessMessages
+ {$IFDEF USE_OnIdle}
+ MOV EAX, EBX
+ CALL [ProcessIdle]
+ {$ENDIF}
+ JMP @@msgloo
+@@e_msgloo:
+ POP EDX
+ MOV EAX, EBX
+ CALL TControl.DetachProc
+ DEC [EBX].fModal
+ MOV EAX, [Applet]
+ XOR ECX, ECX
+ MOV [EAX].fModalForm, ECX
+ MOV ECX, [EBP].TList.fCount
+ JECXZ @@2end
+ MOV ESI, [EBP].TList.fItems
+@@2loo: LODSD
+ PUSH ECX
+ MOV DL, 1
+ CALL TControl.SetEnabled
+ POP ECX
+ LOOP @@2loo
+@@2end:
+ MOV EAX, EBP
+ CALL TObj.Free
+ POP ECX
+ JECXZ @@exit
+ PUSH 0
+ PUSH WA_ACTIVE
+ PUSH WM_ACTIVATE
+ PUSH [ECX].fHandle
+ CALL PostMessage
+ TEST EBP, EBP // CurCtl = nil ?
+ JZ @@exit
+ MOV EAX, EBP
+ MOV DL, 1
+ CALL TControl.SetFocused
+@@exit:
+ POPAD
+ MOV EAX, [EAX].fModalResult
+end;
+{$ELSE PAS_VERSION} //Pascal
+{$IFDEF USE_SHOWMODALPARENTED_ALWAYS}
+function TControl.ShowModal: Integer;
+begin
+ Result := ShowModalParented(Applet);
+end;
+{$ELSE not USE_SHOWMODALPARENTED_ALWAYS}
+function TControl.ShowModal: Integer;
+var FL: PList;
+var CurForm: PControl;
+ I: Integer;
+ F: PControl;
+ CurCtl: PControl; // { Alexander Pravdin }
+begin
+ Result := 0;
+ if {$IFDEF USE_FLAGS} (G3_IsControl in fFlagsG3)
+ {$ELSE} (fIsControl) {$ENDIF}
+ or (fParent = nil) then
+ begin
+ Show; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ AttachProc( WndProcShowModal );
+ CurForm := Applet.DF.fCurrentControl;
+ FL := NewList;
+ CurCtl := nil; // { Alexander Pravdin }
+
+ if Applet.IsApplet then
+ begin
+ for I := 0 to Applet.ChildCount - 1 do
+ begin
+ F := Applet.fChildren.Items[ I ];
+ if F <> @Self then
+ if F.Enabled then
+ begin
+ FL.Add( F );
+ F.Enabled := FALSE;
+ {$IFNDEF NOT_FIX_MODAL}
+ Inc( F.DF.fFixingModal );
+ F.AttachProc(WndProcFixModal); {**************}
+ {$ENDIF}
+ end;
+ end
+ end
+ else
+ begin
+ CurForm := Applet;
+ if Applet.Enabled then
+ begin
+ FL.Add( Applet );
+ CurCtl := Applet.DF.fCurrentControl; { Alexander Pravdin }
+ Applet.Enabled := FALSE;
+ {$IFNDEF NOT_FIX_MODAL}
+ Inc( Applet.DF.fFixingModal );
+ Applet.AttachProc(WndProcFixModal); {**************}
+ {$ENDIF}
+ end;
+ end;
+
+ Inc( DF.fModal );
+ Applet.DF.fModalForm := @ Self;
+ Enabled := TRUE;
+
+ ModalResult := 0;
+ Show;
+ while not AppletTerminated and (ModalResult = 0) do
+ begin
+ WaitMessage;
+ Applet.ProcessMessages;
+ {$IFDEF USE_OnIdle}
+ ProcessIdle( @Self );
+ {$ENDIF}
+ end;
+
+ Dec( DF.fModal );
+ Applet.DF.fModalForm := nil;
+
+ DetachProc( WndProcShowModal );
+ for I := 0 to FL.Count - 1 do
+ begin
+ F := FL.Items[ I ];
+ {$IFNDEF NOT_FIX_MODAL}
+ Dec( F.DF.fFixingModal );
+ if F.DF.fFixingModal <= 0 then
+ F.DetachProc(WndProcFixModal); {**************}
+ {$ENDIF}
+ F.Enabled := TRUE;
+ end;
+ FL.Free;
+
+ if CurForm <> nil then
+ PostMessage( CurForm.Handle, WM_ACTIVATE, WA_ACTIVE, 0 );
+ if CurCtl <> nil then CurCtl.SetFocused( TRUE ); { Alexander Pravdin }
+
+ Result := ModalResult;
+end;
+{$ENDIF USE_SHOWMODALPARENTED_ALWAYS}
+{$ENDIF PAS_VERSION}
+
+{$IFNDEF NEW_MODAL}
+function TControl.ShowModalParented( const AParent: PControl ): Integer;
+begin
+ Result := 0;
+end;
+{$ELSE NEW_MODAL defined}
+function TControl.ShowModalParented( const AParent: PControl ): Integer;
+var
+ FL: PList;
+ OldMF, F: PControl;
+ I: Integer;
+begin
+ Result := 0;
+ if ( AParent = nil ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Inc( DF.fModal );
+ FL := NewList;
+ OldMF := AParent.DF.fModalForm;
+ AParent.DF.fModalForm := @Self;
+ if {$IFDEF USE_FLAGS} (G3_IsApplet in AParent.fFlagsG3)
+ {$ELSE} AParent.fIsApplet {$ENDIF}
+ or ( AParent.IsMainWindow and
+ {$IFDEF USE_FLAGS} (G3_IsForm in AParent.fFlagsG3)
+ {$ELSE} AParent.fIsForm {$ENDIF} ) then
+ begin
+ for I := 0 to AParent.ChildCount - 1 do
+ begin
+ F := AParent.fChildren.Items[ I ];
+ if ( F <> @Self )
+ and {$IFDEF USE_FLAGS} (G3_IsForm in F.fFlagsG3)
+ {$ELSE} F.fIsForm {$ENDIF}
+ and {$IFDEF USE_FLAGS}
+ not(F3_Disabled in F.fStyle.f3_Style) and
+ (F3_Visible in F.fStyle.f3_Style)
+ {$ELSE} F.fEnabled and F.fVisible {$ENDIF} then
+ begin
+ FL.Add( F );
+ F.Enabled := FALSE;
+ {$IFNDEF NOT_FIX_MODAL}
+ F.AttachProc(WndProcFixModal); {**************}
+ {$ENDIF}
+ end;
+ end;
+ end;
+
+ if {$IFDEF USE_FLAGS} (G3_IsForm in AParent.fFlagsG3)
+ {$ELSE} AParent.fIsForm {$ENDIF}
+ and {$IFDEF USE_FLAGS} not(F3_Disabled in AParent.fStyle.f3_Style)
+ {$ELSE} AParent.Enabled {$ENDIF} then
+ begin
+ FL.Add( AParent );
+ AParent.Enabled := FALSE;
+ end;
+
+ ModalResult := 0;
+ Show;
+ while not AppletTerminated and ( ModalResult = 0 ) do
+ begin
+ WaitMessage;
+ AParent.ProcessMessages;
+{$IFDEF USE_OnIdle}
+ ProcessIdle( @Self );
+{$ENDIF}
+ end;
+
+ AParent.DF.fModalForm := OldMF;
+ Dec( DF.fModal );
+ for I := 0 to FL.Count - 1 do
+ begin
+ F := PControl( FL.Items[ I ] );
+ F.Enabled := True;
+ {$IFNDEF NOT_FIX_MODAL}
+ F.DetachProc(WndProcFixModal); {**************}
+ {$ENDIF}
+ end;
+ FL.Free;
+ Hide;
+ Result := ModalResult;
+end;
+{$ENDIF NEW_MODAL}
+
+function DisableWindows( W: hwnd; LPARAM: Integer ): Bool; stdcall;
+var FL: PList;
+ Buf: Array[ 0..127 ] of AnsiChar;
+begin
+ FL := Pointer( LPARAM );
+ if IsWindowEnabled( W ) and (W <> FL.Tag) then
+ begin
+ GetClassName( W, @ Buf[ 0 ], Sizeof( Buf ) );
+ if Buf <> 'ComboLBox' then
+ begin
+ FL.Add( Pointer( W ) );
+ EnableWindow( W, FALSE );
+ end;
+ end;
+ Result := TRUE;
+end;
+
+function TControl.ShowModalEx: Integer;
+var FL: PList;
+var CurForm: PControl;
+ I: Integer;
+ W: HWnd;
+ CurCtl: PControl; { Alexander Pravdin }
+begin
+ Result := 0;
+ if {$IFDEF USE_FLAGS} (G3_IsControl in fFlagsG3)
+ {$ELSE} (fIsControl) {$ENDIF}
+ or (fParent = nil) then
+ begin
+ Show; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ AttachProc( WndProcShowModal );
+ CurForm := Applet.DF.fCurrentControl;
+ FL := NewList;
+ FL.Tag := fHandle;
+ // ++++ { Alexander Pravdin }
+ if {$IFDEF USE_FLAGS} not(G3_IsApplet in Applet.fFlagsG3)
+ {$ELSE} not Applet.fIsApplet {$ENDIF} then
+ CurCtl := Applet.DF.fCurrentControl
+ else CurCtl := nil;
+ // ----
+ CreateWindow;
+
+ EnumThreadWindows( GetCurrentThreadID, @ DisableWindows, Integer( FL ) );
+ Enabled := TRUE;
+
+ Inc( DF.fModal );
+ Applet.DF.fModalForm := @ Self;
+ Show;
+ DF.fModalResult := 0;
+ while not AppletTerminated and (DF.fModalResult = 0) do
+ begin
+ WaitMessage;
+ Applet.ProcessMessages;
+ {$IFDEF USE_OnIdle}
+ ProcessIdle( @Self );
+ {$ENDIF}
+ end;
+
+ Dec( DF.fModal );
+ Applet.DF.fModalForm := @ Self;
+
+ DetachProc( WndProcShowModal );
+
+ for I := 0 to FL.Count - 1 do
+ begin
+ W := THandle( FL.Items[ I ] );
+ EnableWindow( W, TRUE );
+ end;
+ FL.Free;
+
+ if CurForm <> nil then
+ PostMessage( CurForm.Handle, WM_ACTIVATE, WA_ACTIVE, 0 );
+ if CurCtl <> nil then CurCtl.SetFocused( True ); { Alexander Pravdin }
+ Result := ModalResult;
+end;
+
+function TControl.GetModal: Boolean;
+begin
+ Result := DF.fModal > 0;
+end;
+
+{$IFDEF USE_SETMODALRESULT}
+procedure TControl.SetModalResult( const Value: Integer );
+begin
+ DF.fModalResult := Value;
+ if Value <> 0 then
+ PostMessage( GetWindowHandle, 0, 0, 0 );
+end;
+{$ENDIF}
+
+{$IFNDEF NEW_MENU_ACCELL}
+procedure TControl.DoDestroyAccelTable;
+begin
+ if fAccelTable <> 0 then
+ begin
+ DestroyAcceleratorTable( fAccelTable );
+ fAccelTable := 0;
+ end;
+end;
+{$ENDIF}
+
+{$ENDIF WIN_GDI} //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+{$IFDEF _X_}
+{$IFDEF GTK}
+FUNCTION control_clicked( Obj: PGtkWidget; Sender: PControl ): Boolean; cdecl;
+BEGIN
+ IF Assigned( Sender.fOnClick ) THEN
+ Sender.fOnClick( Sender );
+ Result := FALSE;
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+procedure TControl.SetOnClick( const Value: TOnEvent );
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnClick := Value;
+ {$IFDEF GTK}
+ IF fEventboxHandle = fHandle THEN
+ BEGIN
+ {$IFNDEF SMALLER_CODE}
+ IF NOT Assigned( Value ) THEN
+ gtk_signal_disconnect( GTK_OBJECT( fEventboxHandle ), fClickedEvent )
+ ELSE
+ {$ENDIF SMALLEST_CODE}
+ fClickedEvent := gtk_signal_connect( GTK_OBJECT( fEventboxHandle ), 'clicked',
+ @ control_clicked, @ Self )
+ END ELSE SetMouseEvent( @ Self, 'button_release_event' );
+ {$ENDIF GTK}
+end;
+//////////////////////////////////////////////////////////////////
+// T I M E R
+//////////////////////////////////////////////////////////////////
+
+var {$IFDEF WIN} TimerOwnerWnd: PControl; {$ENDIF} // in Linux, timer not need in a window
+ TimerCount: Integer = 0;
+
+{ -- Constructor of timer -- }
+
+function NewTimer( Interval: Integer ): PTimer;
+begin
+ New( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TTimer';
+ {$ENDIF}
+ if Interval <= 0 then Interval := 1000;
+ Result.fInterval := Interval;
+ Inc( TimerCount );
+end;
+
+{ -- Timer procedure -- }
+
+{$IFDEF WIN}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TimerProc( Wnd : HWnd; Msg : Integer; T : PTimer; CurrentTime : DWord ): Integer;
+ stdcall;
+begin
+ {$IFDEF STOPTIMER_AFTER_APPLETTERMINATED}
+ if not AppletTerminated then
+ {$ENDIF}
+ if Assigned( T.fOnTimer ) then
+ T.fOnTimer( T );
+ Result := 0;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF WIN}
+
+{ TTimer }
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+destructor TTimer.Destroy;
+begin
+ Enabled := False;
+ inherited;
+ Dec( TimerCount );
+ {$IFDEF WIN}
+ if TimerCount = 0 then
+ begin
+ TimerOwnerWnd.Free;
+ TimerOwnerWnd := nil;
+ end;
+ {$ENDIF WIN}
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF WIN_GDI}
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TTimer.SetEnabled(const Value: Boolean);
+var WasEnabled: Boolean;
+begin
+ WasEnabled := fEnabled;
+ fEnabled := Value;
+ if WasEnabled = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$IFDEF TIMER_APPLETWND}
+ if Applet = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$ENDIF}
+
+ if Value then
+ begin
+ {$IFDEF TIMER_APPLETWND}
+ fHandle := SetTimer( Applet.GetWindowHandle, Integer( @Self ),
+ fInterval, @TimerProc );
+ {$ELSE}
+ if TimerOwnerWnd = nil then
+ begin
+ TimerOwnerWnd := _NewWindowed( nil, '', TRUE,
+ {$IFDEF PACK_COMMANDACTIONS} PAnsiChar( OTHER_ACTIONS )
+ {$ELSE} nil {$ENDIF} );
+ TimerOwnerWnd.fStyle.Value := 0;
+ {$IFDEF USE_FLAGS} include( TimerOwnerWnd.fFlagsG3, G3_IsControl );
+ {$ELSE} TimerOwnerWnd.fIsControl := TRUE; {$ENDIF}
+ end;
+ fHandle := SetTimer( TimerOwnerWnd.GetWindowHandle, Integer( @Self ),
+ fInterval, @TimerProc );
+ {$ENDIF}
+ end
+ else
+ begin
+ if fHandle <> 0 then
+ begin
+ KillTimer( {$IFDEF TIMER_APPLETWND} Applet.fHandle
+ {$ELSE} TimerOwnerWnd.fHandle {$ENDIF}, fHandle );
+ fHandle := 0;
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF WIN_GDI}
+
+{$IFDEF _X_}
+{$IFDEF GTK}
+FUNCTION TimerGTKTick( Sender: Pointer ): LONGBOOL; cdecl;
+BEGIN
+ IF NOT PTimer( Sender ).fEnabled THEN Result := FALSE
+ ELSE
+ BEGIN
+ IF Assigned( PTimer( Sender ).fOnTimer ) THEN
+ Ptimer( Sender ).fOnTimer( Sender );
+ Result := PTimer( Sender ).fEnabled;
+ END;
+ IF Result THEN
+ PTimer( Sender ).RefDec;
+END;
+
+PROCEDURE TTimer.SetEnabled(const Value: Boolean);
+BEGIN
+ IF FEnabled = Value THEN Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fEnabled := Value;
+ IF Value THEN
+ BEGIN
+ RefInc;
+ fHandle := gtk_timeout_add( fInterval, TimerGTKTick, @ Self );
+ END ELSE
+ BEGIN
+ IF AppletTerminated THEN
+ BEGIN
+ gtk_timeout_remove( fHandle );
+ RefDec;
+ END;
+ END;
+END;
+{$ELSE not GTK}
+VAR fActiveTimerList: PTimer;
+ fClockPerSecond: Integer;
+ fAlarmHandling: Boolean;
+
+PROCEDURE SetAlarm; FORWARD;
+
+PROCEDURE AlarmHandler(SigNum: Integer); CDECL;
+VAR T, NT: PTimer;
+ c: Integer;
+ count_handled: Integer;
+BEGIN
+ c := clock;
+ fAlarmHandling := TRUE; // to prevent SetAlarm working while timers are handling
+ TRY
+ //--- 1. Clear fTimerHandled flag for all active timers
+ T := fActiveTimerList;
+ WHILE T <> nil DO
+ BEGIN
+ T.fTimerHandled := FALSE;
+ T := T.fNext;
+ END;
+ //--- 2. Handle all expired timers
+ count_handled := 0;
+ WHILE not AppletTerminated DO // until all timers expired are handled or
+ BEGIN // until the application is terminated
+ //--- 2.A. Search a timer which was expired before all others
+ T := fActiveTimerList;
+ NT := nil;
+ WHILE T <> nil do
+ BEGIN
+ IF not T.fTimerHandled and (
+ (NT = nil) or ((T.fExpireNext - c) < (NT.fExpireNext - c))
+ ) THEN
+ NT := T;
+ T := T.fNext;
+ END;
+ IF NT = nil then break; // there are no more timers expired
+ IF (count_handled > 0) and
+ ((NT.fExpireNext - c > 0) or (NT.fExpireNext < 0) and (c > 0)) then break;
+ //--- 2.B. Handle found timer (NT)
+ inc( count_handled ); // count handled timer to ensure that at least 1 timer
+ // was handled in result of alarm call
+ {$IFDEF SUPPORT_LONG_TIMER}
+ NT.fExpireTotal := NT.fExpireTotal - (c - NT.fTimeStart);
+ IF NT.fExpireTotal > 30 * 60 * fClockPerSecond then
+ NT.fExpireNext := c + 30 * 60 * fClockPerSecond
+ ELSE
+ NT.fExpireNext := c + NT.fExpireTotal;
+ {$ELSE not SUPPORT_LONG_TIMER}
+ NT.fExpireNext := // next time to expire this timer
+ NT.fExpireNext + fClockPerSecond * NT.fInterval;
+ {$ENDIF SUPPORT_LONG_TIMER}
+ NT.fTimerHandled := TRUE; // do not handle that timer again in that loop
+ {$IFDEF SUPPORT_LONG_TIMER}
+ IF NT.fExpireTotal <= 0 then
+ {$ENDIF SUPPORT_LONG_TIMER}
+ BEGIN IF NT.fMultimedia and not NT.fPeriodic then
+ NT.Enabled := FALSE; // one-shot timer, disable it now
+ IF Assigned( NT.fOnTimer ) then
+ NT.fOnTimer( NT ); // in result of this action, timer NT or any
+ // other active timer can be disabled and dropped from
+ // fActiveTimerList and any amount of previously disabled timers
+ // can be added
+ END;
+ END;
+ FINALLY
+ fAlarmHandling := FALSE;
+ END;
+ // 3. finally, install the next alarm to the nearest expirating timer if any
+ SetAlarm;
+END;
+
+PROCEDURE SetAlarm;
+VAR i: Integer;
+ T, NT: PTimer;
+ TV: itimerval;
+ c: clock_t;
+BEGIN
+ IF AppletTerminated then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ // if the application is terminated we do not install alarms
+ IF fAlarmHandling then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ // while alarm is handling do not reinstall alarms
+ c := clock;
+ T := fActiveTimerList;
+ NT := T;
+ WHILE T <> nil do
+ BEGIN
+ if (T.fExpireNext - c) < (NT.fExpireNext - c) then
+ NT := T;
+ T := T.fNext;
+ END;
+ IF NT = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ i := (NT.fExpireNext - c) * 1000 div fClockPerSecond;
+ IF i < 0 then i := 10; // 10 milliseconds as minimum time to alarm
+ TV.it_interval.tv_sec := 0; // set interval to alarm once
+ TV.it_interval.tv_usec := 0;
+ TV.it_value.tv_sec := i div 1000; // set time to alarm next time
+ TV.it_value.tv_usec := (i mod 1000) * 1000;
+ signal( SIGALRM, AlarmHandler );
+ setitimer( ITIMER_REAL, TV, nil );
+END;
+
+PROCEDURE TTimer.SetEnabled(const Value: Boolean);
+BEGIN
+ IF FEnabled = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fEnabled := Value;
+ IF Value then
+ BEGIN
+ IF fClockPerSecond = 0 then
+ fClockPerSecond := CLK_TCK;
+ fExpireTotal := Int64( fClockPerSecond ) * fInterval;
+ {$IFDEF SUPPORT_LONG_TIMER}
+ IF fExpireTotal > 30 * 60 * fClockPerSecond then
+ fExpireNext := clock + 30 * 60 * fClockPerSecond
+ ELSE
+ fExpireNext := clock + fExpireTotal;
+ {$ELSE}
+ fExpireNext := clock + fExpireTotal;
+ {$ENDIF SUPPORT_LONG_TIMER}
+ IF fActiveTimerList <> nil then
+ BEGIN
+ fNext := fActiveTimerList;
+ fActiveTimerList.fPrev := @ Self;
+ END;
+ fActiveTimerList := @ Self;
+ END ELSE
+ BEGIN
+ IF fPrev <> nil then fPrev.fNext := fNext;
+ IF fNext <> nil then fNext.fPrev := fPrev;
+ IF fActiveTimerList = @ Self then
+ fActiveTimerList := fNext;
+ fPrev := nil;
+ fNext := nil;
+ end;
+ if fActiveTimerList <> nil then
+ SetAlarm; // set alarm to the nearest expiring timer
+END;
+{$ENDIF not GTK}
+{$ENDIF _X_}
+
+procedure TTimer.SetInterval(const Value: Integer);
+var WasEnabled : Boolean;
+begin
+ if fInterval = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fInterval := Value;
+ WasEnabled := Enabled;
+ Enabled := False;
+ Enabled := WasEnabled {$IFDEF STOPTIMER_AFTER_APPLETTERMINATED}
+ and not AppletTerminated
+ {$ENDIF};
+end;
+
+{$IFDEF WIN}
+{ TMMTimer }
+
+{ ------------ declarations moved here from MMSystem -------------------- }
+const
+ TIME_ONESHOT = 0; { program timer for single event }
+ TIME_PERIODIC = 1; { program for continuous periodic event }
+ TIME_CALLBACK_FUNCTION = $0000; { callback is function }
+ TIME_CALLBACK_EVENT_SET = $0010; { callback is event - use SetEvent }
+ TIME_CALLBACK_EVENT_PULSE = $0020; { callback is event - use PulseEvent }
+
+type
+ TFNTimeCallBack = procedure(uTimerID, uMessage: UINT;
+ dwUser, dw1, dw2: DWORD) stdcall;
+function timeSetEvent(uDelay, uResolution: UINT;
+ lpFunction: TFNTimeCallBack; dwUser: DWORD; uFlags: UINT): THandle; stdcall;
+ external 'winmm.dll' name 'timeSetEvent';
+function timeKillEvent(uTimerID: UINT): Integer; stdcall;
+ external 'winmm.dll' name 'timeKillEvent';
+{ ----------------------------------------------------------------------- }
+
+procedure MMTimerCallback(uTimerID, uMessage: UINT; dwUser, dw1, dw2: DWORD);
+ stdcall;
+var MMTimer: PMMTimer;
+begin
+ MMTimer := Pointer( dwUser );
+ if Assigned( MMTimer.FOnTimer ) then
+ MMTimer.fOnTimer( MMTimer );
+end;
+
+function NewMMTimer( Interval: Integer ): PMMTimer;
+begin
+ New( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TMMTimer';
+ {$ENDIF}
+ Result.fInterval := Interval;
+ Result.FPeriodic := TRUE;
+end;
+
+destructor TMMTimer.Destroy;
+begin
+ Enabled := FALSE;
+ Inc( TimerCount );
+ inherited;
+end;
+
+procedure TMMTimer.SetEnabled(const Value: Boolean);
+begin
+ if Value xor (fHandle <> 0) then
+ begin
+ if fHandle = 0 then
+ fHandle := timeSetEvent( Interval, Resolution, MMTimerCallback, DWORD( @ Self ),
+ Integer( Periodic ) or TIME_CALLBACK_FUNCTION )
+ else
+ begin
+ timeKillEvent( fHandle );
+ fHandle := 0;
+ end;
+ end;
+ fEnabled := Value;
+end;
+{$ENDIF WIN}
+{$IFDEF LIN}
+function NewMMTimer( Interval: Integer ): PTimer;
+begin
+ Result := NewTimer( Interval );
+ {$IFNDEF GTK}
+ {$IFNDEF QT}
+ Result.fMultimedia := TRUE;
+ Result.fPeriodic := TRUE;
+ Result.fResolution := 1;
+ {$ENDIF QT}
+ {$ENDIF GTK}
+end;
+{$ENDIF LIN}
+
+{$IFDEF WIN_GDI} //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
+////////////////////////////////////////////////////////////////////////
+// t B I T M A P
+///////////////////////////////////////////////////////////////////////
+
+{ -- bitmap -- }
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function PrepareBitmapHeader( W, H, BitsPerPixel: Integer ): PBitmapInfo;
+begin
+ {$IFDEF KOL_ASSERTIONS}
+ Assert( W > 0, 'Width must be >0' );
+ Assert( H > 0, 'Height must be >0' );
+ {$ENDIF KOL_ASSERTIONS}
+ Result := AllocMem( 256*Sizeof(TRGBQuad)+Sizeof(TBitmapInfoHeader) );
+ {$IFDEF KOL_ASSERTIONS}
+ Assert( Result <> nil, 'No memory' );
+ {$ENDIF KOL_ASSERTIONS}
+ Result.bmiHeader.biSize := Sizeof( TBitmapInfoHeader );
+ Result.bmiHeader.biWidth := W;
+ Result.bmiHeader.biHeight := H; // may be, -H ?
+ Result.bmiHeader.biPlanes := 1;
+ Result.bmiHeader.biBitCount := BitsPerPixel;
+end;
+{$ENDIF PAS_VERSION}
+
+const
+ BitsPerPixel_By_PixelFormat: array[ TPixelFormat ] of Byte =
+ ( 0, 1, 4, 8, 16, 16, 24, 32, 0 );
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function Bits2PixelFormat( BitsPerPixel: Integer ): TPixelFormat;
+var I: TPixelFormat;
+begin
+ for I := High(I) downto Low(I) do
+ if BitsPerPixel = BitsPerPixel_By_PixelFormat[ I ] then
+ begin
+ Result := I; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Result := pfDevice;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure DummyDetachCanvas( Sender: PBitmap );
+begin
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewBitmap( W, H: Integer ): PBitmap;
+var DC: HDC;
+begin
+ New( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TBitmap';
+ {$ENDIF}
+ Result.fHandleType := bmDDB;
+ Result.fDetachCanvas := DummyDetachCanvas;
+ Result.fWidth := W;
+ Result.fHeight := H;
+ if (W <> 0) and (H <> 0) then
+ begin
+ DC := GetDC( 0 );
+ Result.fHandle := CreateCompatibleBitmap( DC, W, H );
+ {$IFDEF KOL_ASSERTIONS}
+ Assert( Result.fHandle <> 0, 'Can not create bitmap handle' );
+ {$ENDIF KOL_ASSERTIONS}
+ ReleaseDC( 0, DC );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+const InitColors: array[ 0..17 ] of DWORD = ( $F800, $7E0, $1F, 0, $800000, $8000,
+ $808000, $80, $800080, $8080, $808080, $C0C0C0, $FF0000, $FF00, $FFFF00, $FF,
+ $FF00FF, $FFFF );
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure PreparePF16bit( DIBHeader: PBitmapInfo );
+begin
+ DIBHeader.bmiHeader.biCompression := BI_BITFIELDS;
+ Move( InitColors[ 0 ], DIBHeader.bmiColors[ 0 ], 19*Sizeof(TRGBQUAD) );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function NewDIBBitmap( W, H: Integer; PixelFormat: TPixelFormat ): PBitmap;
+const BitsPerPixel: array[ TPixelFormat ] of Byte = ( 0, 1, 4, 8, 16, 16, 24, 32, 0 );
+var BitsPixel: Integer;
+begin
+ New( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TBitmap:DIBBitmap';
+ {$ENDIF}
+ Result.fDetachCanvas := DummyDetachCanvas;
+ Result.fWidth := W;
+ Result.fHeight := H;
+ if (W <> 0) and (H <> 0) then
+ begin
+ BitsPixel := BitsPerPixel[ PixelFormat ];
+ if BitsPixel = 0 then
+ begin
+ Result.fNewPixelFormat := DefaultPixelFormat;
+ BitsPixel := BitsPerPixel[DefaultPixelFormat];
+ end
+ else
+ Result.fNewPixelFormat := PixelFormat;
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( Result.fNewPixelFormat in [ pf1bit..pf32bit ], 'Strange pixel format' );
+ {$ENDIF KOL_ASSERTIONS}
+ Result.fDIBHeader := PrepareBitmapHeader( W, H, BitsPixel );
+ if PixelFormat = pf16bit then
+ begin
+ PreparePF16bit( Result.fDIBHeader );
+ end;
+
+ Result.fDIBSize := Result.ScanLineSize * H;
+ Result.fDIBBits :=
+ Pointer( GlobalAlloc( GMEM_FIXED or GMEM_ZEROINIT, Result.fDIBSize + 16 ) );
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( Result.fDIBBits <> nil, 'No memory' );
+ {$ENDIF KOL_ASSERTIONS}
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{ TBitmap }
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.ClearData;
+begin
+ fDetachCanvas( @Self );
+ if fHandle <> 0 then
+ begin
+ DeleteObject( fHandle );
+ fHandle := 0;
+ fDIBBits := nil;
+ end;
+ if fDIBBits <> nil then
+ begin
+ if not fDIBAutoFree then
+ GlobalFree( THandle( fDIBBits ) );
+ fDIBBits := nil;
+ end;
+ if fDIBHeader <> nil then
+ begin
+ FreeMem( fDIBHeader );
+ fDIBHeader := nil;
+ end;
+ fScanLineSize := 0;
+ fGetDIBPixels := nil;
+ fSetDIBPixels := nil;
+ ClearTransImage;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.Clear;
+begin
+ RemoveCanvas;
+ ClearData;
+ fWidth := 0;
+ fHeight := 0;
+ fDIBAutoFree := FALSE;
+end;
+{$ENDIF PAS_VERSION}
+
+function TBitmap.GetBoundsRect: TRect;
+begin
+ Result := MakeRect( 0, 0, Width, Height );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+destructor TBitmap.Destroy;
+begin
+ Clear;
+ inherited;
+end;
+{$ENDIF PAS_VERSION}
+
+function TBitmap.BitsPerPixel: Integer;
+var B: tagBitmap;
+begin
+ CASE PixelFormat OF
+ pf1bit: Result := 1;
+ pf4bit: Result := 4;
+ pf8bit: Result := 8;
+ pf15bit: Result := 15;
+ pf16bit: Result := 16;
+ pf24bit: Result := 24;
+ pf32bit: Result := 32;
+ else begin
+ Result := 0;
+ if fHandle <> 0 then
+ if GetObject( fHandle, Sizeof( B ), @B ) > 0 then
+ Result := B.bmBitsPixel * B.bmPlanes;
+ end;
+ END;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.Draw(DC: HDC; X, Y: Integer);
+var
+ DCfrom, DC0: HDC;
+ oldBmp: HBitmap;
+ oldHeight: Integer;
+ B: tagBitmap;
+label
+ TRYAgain;
+begin
+TRYAgain:
+ if Empty then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if fHandle <> 0 then
+ begin
+ fDetachCanvas( @Self );
+ oldHeight := fHeight;
+ if GetObject( fHandle, sizeof( B ), @B ) <> 0 then
+ oldHeight := B.bmHeight;
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( oldHeight > 0, 'oldHeight must be > 0' );
+ {$ENDIF KOL_ASSERTIONS}
+
+ DC0 := GetDC( 0 );
+ DCfrom := CreateCompatibleDC( DC0 );
+ ReleaseDC( 0, DC0 );
+
+ oldBmp := SelectObject( DCfrom, fHandle );
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( oldBmp <> 0, 'Can not select bitmap to DC' );
+ {$ENDIF KOL_ASSERTIONS}
+
+ BitBlt( DC, X, Y, fWidth, oldHeight, DCfrom, 0, 0, SRCCOPY );
+ {$IFDEF CHK_BITBLT} Chk_BitBlt; {$ENDIF}
+
+ SelectObject( DCfrom, oldBmp );
+ DeleteDC( DCfrom );
+ end
+ else
+ if fDIBBits <> nil then
+ begin
+ oldHeight := Abs(fDIBHeader.bmiHeader.biHeight);
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( oldHeight > 0, 'oldHeight must be > 0' );
+ ASSERT( fWidth > 0, 'Width must be > 0' );
+ {$ENDIF KOL_ASSERTIONS}
+ if StretchDIBits( DC, X, Y, fWidth, oldHeight, 0, 0, fWidth, oldHeight,
+ fDIBBits, fDIBHeader^, DIB_RGB_COLORS, SRCCOPY ) = 0 then
+ begin
+ if GetHandle <> 0 then
+ goto TRYAgain;
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.StretchDraw(DC: HDC; const Rect: TRect);
+var DCfrom: HDC;
+ oldBmp: HBitmap;
+label DrawHandle;
+begin
+ if Empty then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+DrawHandle:
+ if fHandle <> 0 then
+ begin
+ fDetachCanvas( @Self );
+ DCfrom := CreateCompatibleDC( 0 );
+ oldBmp := SelectObject( DCfrom, fHandle );
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( oldBmp <> 0, 'Can not select bitmap to DC' );
+ {$ENDIF KOL_ASSERTIONS}
+ StretchBlt( DC, Rect.Left, Rect.Top, Rect.Right - Rect.Left,
+ Rect.Bottom - Rect.Top, DCfrom, 0, 0, fWidth, fHeight,
+ SRCCOPY );
+ SelectObject( DCfrom, oldBmp );
+ DeleteDC( DCfrom );
+ end
+ else
+ if fDIBBits <> nil then
+ begin
+ if StretchDIBits( DC, Rect.Left, Rect.Top, Rect.Right - Rect.Left,
+ Rect.Bottom - Rect.Top, 0, 0, fWidth, fHeight,
+ fDIBBits, fDIBHeader^, DIB_RGB_COLORS, SRCCOPY )<=0 then
+ begin
+ if GetHandle <> 0 then
+ goto DrawHandle;
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TBitmap.DrawMasked(DC: HDC; X, Y: Integer; Mask: HBitmap);
+begin
+ StretchDrawMasked( DC, MakeRect( X, Y, X + fWidth, Y + fHeight ), Mask );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.DrawTransparent(DC: HDC; X, Y: Integer; TranspColor: TColor);
+begin
+ if TranspColor = clNone then
+ Draw( DC, X, Y )
+ else
+ StretchDrawTransparent( DC, MakeRect( X, Y, X + fWidth, Y + fHeight ),
+ TranspColor );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.StretchDrawTransparent(DC: HDC; const Rect: TRect; TranspColor: TColor);
+begin
+ if TranspColor = clNone then
+ StretchDraw( DC, Rect )
+ else
+ begin
+ if GetHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ TranspColor := Color2RGB( TranspColor );
+ if (fTransMaskBmp = nil) or (fTransColor <> TranspColor) then
+ begin
+ if fTransMaskBmp = nil then
+ fTransMaskBmp := NewBitmap( 0, 0 {fWidth, fHeight} );
+ fTransColor := TranspColor;
+ // Create here mask bitmap:
+ fTransMaskBmp.Assign( @Self );
+ fTransMaskBmp.Convert2Mask( TranspColor );
+ end;
+ StretchDrawMasked( DC, Rect, fTransMaskBmp.Handle );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF DEBUG_DRAWTRANSPARENT}
+procedure DebugDrawTransparent( DC: HDC; X, Y, W, H: Integer; PF: TPixelFormat;
+ const Note: AnsiString );
+const PixelFormatAsStr: array[ TPixelFormat ] of String = ( 'pfDevice', 'pf1bit',
+ 'pf4bit', 'pf8bit', 'pf15bit', 'pf16bit', 'pf24bit', 'pf32bit', 'pfCustom' );
+var Bmp: PBitmap;
+begin
+ Bmp := NewDibBitmap( W, H, pf32bit );
+ BitBlt( Bmp.Canvas.Handle, 0, 0, W, H, DC, X, Y, SrcCopy );
+ Bmp.SaveToFile( GetStartDir + PixelFormatAsStr[ PF ] + Note );
+ Bmp.Free;
+end;
+{$ENDIF DEBUG_DRAWTRANSPARENT}
+
+const
+ ROP_DstCopy = $00AA0029;
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.StretchDrawMasked(DC: HDC; const Rect: TRect; Mask: HBitmap);
+var
+ DCfrom, MemDC, MaskDC: HDC;
+ MemBmp: HBITMAP;
+ //Save4From,
+ Save4Mem, Save4Mask: THandle;
+ crText, crBack: TColorRef;
+ {$IFDEF FIX_TRANSPBMPPALETTE}
+ FixBmp: PBitmap;
+ {$ENDIF FIX_TRANSPBMPPALETTE}
+begin
+ {$IFDEF FIX_TRANSPBMPPALETTE}
+ if PixelFormat in [ pf4bit, pf8bit ] then
+ begin
+ FixBmp := NewBitmap( 0, 0 );
+ FixBmp.Assign( @ Self );
+ FixBmp.PixelFormat := pf32bit;
+ FixBmp.StretchDrawMasked( DC, Rect, Mask );
+ FixBmp.Free; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ {$ENDIF FIX_TRANSPBMPPALETTE}
+ if GetHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ DCFrom := Canvas.Handle;
+ MaskDC := CreateCompatibleDC( 0 );
+ Save4Mask := SelectObject( MaskDC, Mask );
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( Save4Mask <> 0, 'Can not select mask bitmap to DC' );
+ {$ENDIF KOL_ASSERTIONS}
+ MemDC := CreateCompatibleDC( 0 );
+ MemBmp := CreateCompatibleBitmap( DCfrom, fWidth, fHeight );
+ Save4Mem := SelectObject( MemDC, MemBmp ); if Save4Mem <> 0 then;
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( Save4Mem <> 0, 'Can not select memory bitmap to DC' );
+ {$ENDIF KOL_ASSERTIONS}
+ StretchBlt( MemDC, 0, 0, fWidth, fHeight, MaskDC, 0, 0, fWidth, fHeight, SrcCopy);
+ {$IFDEF DEBUG_DRAWTRANSPARENT}
+ DebugDrawTransparent( MemDC, 0, 0, fWidth, fWidth, PixelFormat, '1SrcCopy.bmp' );
+ {$ENDIF}
+ StretchBlt( MemDC, 0, 0, fWidth, fHeight, DCfrom, 0, 0, fWidth, fHeight, SrcErase);
+ {$IFDEF DEBUG_DRAWTRANSPARENT}
+ DebugDrawTransparent( MemDC, 0, 0, fWidth, fWidth, PixelFormat, '2SrcErase.bmp' );
+ {$ENDIF}
+ crText := SetTextColor(DC, $0);
+ crBack := Windows.SetBkColor(DC, $FFFFFF);
+ StretchBlt( DC, Rect.Left, Rect.Top, Rect.Right - Rect.Left, Rect.Bottom - Rect.Top,
+ MaskDC, 0, 0, fWidth, fHeight, SrcAnd);
+ {$IFDEF DEBUG_DRAWTRANSPARENT}
+ DebugDrawTransparent( DC, Rect.Left, Rect.Top, fWidth, fHeight, PixelFormat, '3SrcAnd.bmp' );
+ {$ENDIF}
+ StretchBlt( DC, Rect.Left, Rect.Top, Rect.Right - Rect.Left, Rect.Bottom - Rect.Top,
+ MemDC, 0, 0, fWidth, fHeight, SrcInvert);
+ {$IFDEF DEBUG_DRAWTRANSPARENT}
+ DebugDrawTransparent( DC, Rect.Left, Rect.Top, fWidth, fHeight, PixelFormat, '4SrcInvert.bmp' );
+ {$ENDIF}
+ Windows.SetBkColor( DC, crBack);
+ SetTextColor( DC, crText);
+ DeleteObject(MemBmp);
+ DeleteDC(MemDC);
+ SelectObject( MaskDC, Save4Mask );
+ DeleteDC( MaskDC );
+end;
+{$ENDIF PAS_VERSION}
+
+procedure ApplyBitmapBkColor2Canvas( Sender: PBitmap );
+begin
+ if Sender.fCanvas = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Sender.fCanvas.Brush.Color := Sender.BkColor;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure DetachBitmapFromCanvas( Sender: PBitmap );
+begin
+ if Sender.fCanvasAttached = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ SelectObject( Sender.fCanvas.fHandle, Sender.fCanvasAttached );
+ Sender.fCanvasAttached := 0;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TBitmap.GetCanvas: PCanvas;
+var DC: HDC;
+begin
+ Result := nil;
+ if Empty then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if GetHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if fCanvas = nil then
+ begin
+ fApplyBkColor2Canvas := ApplyBitmapBkColor2Canvas;
+ fCanvas := NewCanvas( 0 );
+ fCanvas.OnChange := CanvasChanged;
+ if fBkColor <> 0 then
+ fCanvas.Brush.Color := fBkColor;
+ end;
+ Result := fCanvas;
+
+ if fCanvas.fHandle = 0 then
+ begin
+ DC := CreateCompatibleDC( 0 );
+ fCanvas.Handle := DC;
+ fCanvasAttached := 0;
+ end;
+
+ if fCanvasAttached = 0 then
+ begin
+ fCanvasAttached := SelectObject( fCanvas.Handle, fHandle );
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( fCanvasAttached <> 0, 'Can not select bitmap to DC of Canvas' );
+ {$ENDIF KOL_ASSERTIONS}
+ end;
+ fDetachCanvas := DetachBitmapFromCanvas;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TBitmap.GetEmpty: Boolean;
+begin
+ Result := (fWidth = 0) or (fHeight = 0);
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( (fWidth >= 0) and (fHeight >= 0), 'Bitmap dimensions can be negative' );
+ {$ENDIF KOL_ASSERTIONS}
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_noVERSION}
+function TBitmap.GetHandle: HBitmap;
+asm
+ PUSH EBX
+ MOV EBX, EAX
+ CALL GetEmpty
+ JZ @@exit
+ MOV EAX, EBX
+ CALL [EAX].fDetachCanvas
+ MOV ECX, [EBX].fHandle
+ INC ECX
+ LOOP @@exit
+ MOV ECX, [EBX].fDIBBits
+ JECXZ @@exit
+ PUSH ECX
+ PUSH 0
+ CALL GetDC
+ PUSH EAX
+ PUSH 0
+ PUSH 0
+ LEA EDX, [EBX].fDIBBits
+ PUSH EDX
+ PUSH DIB_RGB_COLORS
+ PUSH [EBX].fDIBHeader
+ PUSH EAX
+ CALL CreateDIBSection
+ MOV [EBX].fHandle, EAX
+ PUSH 0
+ CALL ReleaseDC
+ POP EAX
+ PUSH EAX
+ MOV EDX, [EBX].fDIBBits
+ MOV ECX, [EBX].fDIBSize
+ CALL System.Move
+ POP EAX
+ CMP [EBX].fDIBAutoFree, 0
+ JNZ @@freed
+ PUSH EAX
+ CALL GlobalFree
+@@freed:MOV [EBX].fDIBAutoFree, 1
+ XOR EAX, EAX
+ MOV [EBX].fGetDIBPixels, EAX
+ MOV [EBX].fSetDIBPixels, EAX
+@@exit: MOV EAX, [EBX].fHandle
+ POP EBX
+end;
+{$ELSE PAS_VERSION} //Pascal
+function TBitmap.GetHandle: HBitmap;
+var OldBits: Pointer;
+ DC0: HDC;
+begin
+ Result := 0;
+ if Empty then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fDetachCanvas( @ Self );
+ if fHandle = 0 then
+ begin
+ if fDIBBits <> nil then
+ begin
+ OldBits := fDIBBits;
+ DC0 := GetDC( 0 );
+ fDIBBits := nil;
+ fHandle := CreateDIBSection( DC0, fDIBHeader^, DIB_RGB_COLORS,
+ fDIBBits, 0, 0 );
+ {$IFDEF DEBUG_ANY}
+ if fHandle = 0 then
+ ShowMessage( 'Can not create DIB section, error: ' + Int2Str( GetLastError ) +
+ ', ' + SysErrorMessage( GetLastError ) );
+ {$ELSE}
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( fHandle <> 0, 'Can not create DIB section, error: ' + Int2Str( GetLastError ) +
+ ', ' + SysErrorMessage( GetLastError ) );
+ {$ENDIF KOL_ASSERTIONS}
+ {$ENDIF DEBUG_ANY}
+ ReleaseDC( 0, DC0 );
+ if fHandle <> 0 then
+ begin
+ Move( OldBits^, fDIBBits^, fDIBSize );
+ if not fDIBAutoFree then
+ GlobalFree( THandle( OldBits ) );
+ fDIBAutoFree := TRUE;
+
+ fGetDIBPixels := nil;
+ fSetDIBPixels := nil;
+ end
+ else
+ fDIBBits := OldBits;
+ end;
+ end;
+ Result := fHandle;
+end;
+{$ENDIF PAS_VERSION}
+
+function TBitmap.GetHandleAllocated: Boolean;
+begin
+ Result := fHandle <> 0;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.LoadFromFile(const Filename: KOLString);
+var Strm: PStream;
+begin
+ Strm := NewReadFileStream( Filename );
+ LoadFromStream( Strm );
+ Strm.Free;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TBitmap.LoadFromResourceID(Inst: DWORD; ResID: Integer);
+begin
+ LoadFromResourceName( Inst, MAKEINTRESOURCE( ResID ) );
+end;
+
+{$IFDEF ASM_UNICODE}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.LoadFromResourceName(Inst: DWORD; ResName: PKOLChar);
+var ResHandle: HBitmap;
+ Flg: DWORD;
+begin
+ Clear;
+ Flg := 0;
+ if fHandleType = bmDIB then
+ Flg := LR_CREATEDIBSECTION;
+ ResHandle := LoadImage( Inst, ResName, IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE or Flg );
+ if ResHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Handle := ResHandle;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF F_P}
+type
+ TBITMAPFILEHEADER = packed record
+ bfType: Word;
+ bfSize: DWORD;
+ bfReserved1: Word;
+ bfReserved2: Word;
+ bfOffBits: DWORD;
+ end;
+{$ENDIF}
+
+{$IFDEF ASM_noVERSION} // error + 16Colors->swap(Gray,Silver) + Core
+procedure TBitmap.LoadFromStream(Strm: PStream);
+type tBFH = TBitmapFileHeader;
+ tBIH = TBitmapInfoHeader;
+const szBIH = Sizeof( tBIH );
+ szBFH = Sizeof( tBFH );
+asm
+ PUSH EBX
+ PUSH ESI
+ MOV EBX, EAX
+ PUSH EDX
+ CALL Clear
+ POP ESI
+ MOV EAX, ESI
+ CALL TStream.GetPosition
+ PUSH EAX // [EBP+4] = Strm.Pos (starting pos)
+ PUSH EBP
+ MOV EBP, ESP
+ ADD ESP, -(szBIH + szBFH)
+ // reading bitmap
+ XOR ECX, ECX
+ MOV [EBX].fHandleType, CL
+ MOV CL, szBFH
+ MOV EDX, ESP
+ PUSH ECX
+ MOV EAX, ESI
+ CALL TStream.Read
+ POP ECX
+ SUB ECX, EAX
+ JNZ @@eread1
+ CMP [ESP].tBFH.bfType, $4D42
+ JE @@1
+ MOV EDX, [EBP+4]
+ MOV EAX, ESI
+ CALL TStream.Seek
+ XOR EAX, EAX
+ XOR EDX, EDX
+ JMP @@2
+@@1:
+ MOV EDX, [ESP].tBFH.bfSize
+ MOV EAX, [ESP].tBFH.bfOffBits
+@@2:
+ PUSH EDX // Push Size
+ PUSH EAX // Push Off
+ XOR ECX, ECX
+ MOV CL, szBIH
+ LEA EDX, [EBP-szBIH]
+ MOV EAX, ESI
+ PUSH ECX
+ CALL TStream.Read // read BIH
+ POP ECX
+@@eread1:
+ XOR ECX, EAX
+ JNZ @@eread
+ MOVZX EAX, [EBP-szBIH].tBIH.biBitCount
+ MOVZX EDX, [EBP-szBIH].tBIH.biPlanes
+ MUL EDX
+ CALL Bits2PixelFormat
+ {$IFDEF PARANOIA} DB $3C, pf15bit {$ELSE} CMP AL, pf15bit {$ENDIF}
+ JNZ @@no15bit
+ CMP [EBP-szBIH].tBIH.biCompression, 0
+ JZ @@no15bit
+ INC AL // AL = pf16bit
+@@no15bit:
+ MOV [EBX].fNewPixelFormat, AL
+ MOV EAX, szBIH + 1024
+ CALL System.@GetMem
+ MOV [EBX].fDIBHeader, EAX
+ XCHG EDX, EAX
+ LEA EAX, [EBP-szBIH]
+ XOR ECX, ECX
+ MOV CL, szBIH
+ CALL System.Move
+ MOV EAX, [EBP-szBIH].tBIH.biWidth
+ MOV [EBX].fWidth, EAX
+ MOV EAX, [EBP-szBIH].tBIH.biHeight
+ TEST EAX, EAX
+ JGE @@20
+ NEG EAX
+@@20: MOV [EBX].fHeight, EAX
+ MOV EAX, EBX
+ CALL GetScanLineSize
+ MOV EDX, [EBX].fHeight
+ MUL EDX
+ MOV [EBX].fDIBSize, EAX
+ PUSH EAX
+ PUSH GMEM_FIXED or GMEM_ZEROINIT
+ CALL GlobalAlloc
+ MOV [EBX].fDIBBits, EAX
+ MOVZX EAX, [EBP-szBIH].tBIH.biBitCount
+ {$IFDEF PARANOIA} DB $3C, 8 {$ELSE} CMP AL, 8 {$ENDIF}
+ JA @@3
+ MOV AL, 4
+ MOVZX ECX, [EBP-szBIH].tBIH.biBitCount
+ SAL EAX, CL
+ XCHG ECX, EAX
+@@3:
+ CMP [EBX].TBitmap.fNewPixelFormat, pf16bit
+ JNE @@30
+ XOR ECX, ECX
+ MOV CL, 12 // ColorCount = 12
+@@30:
+ POP EAX // EAX = off
+ TEST EAX, EAX
+ JLE @@4
+ SUB EAX, szBFH + szBIH
+ CMP EAX, ECX
+ JZ @@4
+ XCHG ECX, EAX
+@@4:
+ JECXZ @@5
+ PUSH ECX
+ MOV EDX, [EBX].fDIBHeader
+ ADD EDX, szBIH
+ MOV EAX, ESI
+ CALL TStream.Read
+ POP ECX
+ XOR EAX, ECX
+ JNZ @@eread
+@@5:
+ MOV ECX, [EBX].fDIBSize
+@@7:
+ PUSH ECX
+ MOV EAX, ESI
+ CALL TStream.GetPosition
+ PUSH EAX
+ MOV EAX, ESI
+ CALL TStream.GetSize
+ POP EDX
+ SUB EAX, EDX
+ POP ECX // Size = fDIBSize
+ CMP EAX, ECX // Strm.Size - Strm.Position > Size ?
+ JL @@8
+ XCHG ECX, EAX
+@@8: // ++++++++++++++ 26-Oct-2003 VK see comment in Pascal
+ MOV EAX, [EBX].fDIBSize
+ CMP ECX, EAX
+ JGE @@9
+ SUB EAX, ECX
+ PUSH EAX
+ MOV EAX, ESI
+ PUSH ECX
+ CALL TStream.GetPosition
+ POP ECX
+ POP EDX
+ CMP EDX, EAX
+ JG @@9
+ MOV EAX, ESI
+ NEG EDX
+ XOR ECX, ECX
+ INC ECX
+ CALL TStream.Seek
+ MOV ECX, [EBX].fDIBSize
+@@9: // ++++++++++++++
+ PUSH ECX
+ MOV EDX, [EBX].fDIBBits
+ MOV EAX, ESI
+ CALL TStream.Read
+ POP ECX
+ XOR EAX, ECX
+ POP EAX // Strm.Size - Position
+ POP ECX // fDIBSize
+ // end of reading bitmap
+@@eread:
+ MOV ESP, EBP
+ POP EBP
+ POP EDX
+ JZ @@exit
+ // not success:
+ XCHG EAX, ESI
+ XOR ECX, ECX // ECX = spBegin
+ CALL TStream.Seek
+ XCHG EAX, EBX
+ CALL Clear
+@@exit: POP ESI
+ POP EBX
+end;
+{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.LoadFromStream(Strm: PStream);
+type
+ TColorsArray = array[ 0..15 ] of TColor;
+ PColorsArray = ^TColorsArray;
+ PColor = ^TColor;
+var Pos : DWORD;
+ BFH : TBitmapFileHeader;
+
+ function ReadBitmap : Boolean;
+ var Size, Size1: Integer;
+ BCH: TBitmapCoreHeader;
+ RGBSize: DWORD;
+ C: PColor;
+ Off, HdSz, ColorCount: DWORD;
+ begin
+ fHandleType := bmDIB;
+ Result := False;
+ if Strm.Read( BFH, Sizeof( BFH ) ) <> Sizeof( BFH ) then Exit; {>>>>>>>>>}
+ Off := 0; Size := 0;
+ if BFH.bfType <> $4D42 then
+ Strm.Seek( Pos, spBegin )
+ else
+ begin
+ Off := BFH.bfOffBits - Sizeof( BFH );
+ Size := BFH.bfSize; // don't matter, just <> 0 is good
+ end;
+ RGBSize := 4;
+ HdSz := Sizeof( TBitmapInfoHeader );
+ fDIBHeader := AllocMem( 256*sizeof(TRGBQuad) + HdSz );
+ if Strm.Read( fDIBHeader.bmiHeader.biSize, Sizeof( DWORD ) ) <> Sizeof( DWORD ) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if fDIBHeader.bmiHeader.biSize = HdSz then
+ begin
+ if Strm.Read( fDIBHeader.bmiHeader.biWidth, HdSz - Sizeof( DWORD ) ) <>
+ HdSz - Sizeof( DWORD ) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end
+ else
+ if fDIBHeader.bmiHeader.biSize = Sizeof( TBitmapCoreHeader ) then
+ begin
+ RGBSize := 3;
+ HdSz := Sizeof( TBitmapCoreHeader );
+ if Strm.Read( BCH.bcWidth, HdSz - Sizeof( DWORD ) ) <>
+ HdSz - Sizeof( DWORD ) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fDIBHeader.bmiHeader.biSize := Sizeof( TBitmapInfoHeader );
+ fDIBHeader.bmiHeader.biWidth := BCH.bcWidth;
+ fDIBHeader.bmiHeader.biHeight := BCH.bcHeight;
+ fDIBHeader.bmiHeader.biPlanes := BCH.bcPlanes;
+ fDIBHeader.bmiHeader.biBitCount := BCH.bcBitCount;
+ end else Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fNewPixelFormat := Bits2PixelFormat( fDIBHeader.bmiHeader.biBitCount
+ * fDIBHeader.bmiHeader.biPlanes );
+ {$IFDEF KOL_ASSERTIONS}
+ if (fNewPixelFormat = pf15bit) and (fDIBHeader.bmiHeader.biCompression <> BI_RGB) then
+ begin
+ ASSERT( fDIBHeader.bmiHeader.biCompression = BI_BITFIELDS, 'Unsupported bitmap format' );
+ end;
+ {$ENDIF KOL_ASSERTIONS}
+ fWidth := fDIBHeader.bmiHeader.biWidth;
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( fWidth > 0, 'Bitmap width must be > 0' );
+ {$ENDIF KOL_ASSERTIONS}
+ fHeight := Abs(fDIBHeader.bmiHeader.biHeight);
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( fHeight > 0, 'Bitmap height must be > 0' );
+ {$ENDIF KOL_ASSERTIONS}
+
+ fDIBSize := ScanLineSize * fHeight;
+ fDIBBits :=
+ Pointer( GlobalAlloc( GMEM_FIXED or GMEM_ZEROINIT, fDIBSize ) );
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( fDIBBits <> nil, 'No memory' );
+ {$ENDIF KOL_ASSERTIONS}
+
+ ColorCount := 0;
+ if fDIBHeader.bmiHeader.biBitCount <= 8 then
+ begin
+ if fDIBHeader.bmiHeader.biClrUsed > 0 then
+ ColorCount := fDIBHeader.bmiHeader.biClrUsed * Sizeof( TRGBQuad )
+ else
+ ColorCount := (1 shl fDIBHeader.bmiHeader.biBitCount) * Sizeof( TRGBQuad )
+ end
+ else if (fNewPixelFormat in [ pf16bit ]) or
+ (fDIBHeader.bmiHeader.biCompression = BI_BITFIELDS) then
+ ColorCount := 12;
+
+ if Off > 0 then
+ begin
+ Off := Off - HdSz;
+ if (Off <> ColorCount) then
+ if not(fNewPixelFormat in [pf15bit,pf16bit])
+ or (Off = 0) //+++ to fix loading 15- and 16-bit bmps with mask omitted
+ then
+ ColorCount := Min( 1024, Off );
+ end;
+ if ColorCount <> 0 then
+ begin
+ if Off >= ColorCount then
+ Off := Off - ColorCount;
+ if RGBSize = 4 then
+ begin
+ if Strm.Read( fDIBheader.bmiColors[ 0 ], ColorCount )
+ <> DWORD( ColorCount ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end
+ else
+ begin
+ C := @ fDIBHeader.bmiColors[ 0 ];
+ while ColorCount > 0 do
+ begin
+ if Strm.Read( C^, RGBSize ) <> RGBSize then Exit; {>>>>>>>>>>>>>>>}
+ Dec( ColorCount, RGBSize );
+ Inc( C );
+ end;
+ end;
+ end;
+ if Off > 0 then
+ Strm.Seek( Off, spCurrent );
+ if (Size = 0) or (Strm.Size <= 0) then
+ Size := fDIBSize
+ else
+ Size := Min( fDIBSize, Strm.Size - Strm.Position );
+ Size1 := Min( Size, fDIBSize );
+
+ if (Size1 < fDIBSize)
+ and (DWORD( fDIBSize - Size1 ) <= Strm.Position) then
+ begin
+ Strm.Seek( Size1 - fDIBSize, spCurrent );
+ Size1 := fDIBSize;
+ end;
+ if Size1 > fDIBSize then Size1 := fDIBSize;
+ // +++++++++++++++++++ to fix some "incorrect" bitmaps while loading
+ if Strm.Read( fDIBBits^, Size1 ) <> DWORD( Size1 ) then Exit; {>>>>>>>>>>}
+ if Size > Size1 then
+ Strm.Seek( Size - Size1, spCurrent );
+ Result := True;
+ end;
+begin
+ Clear;
+ Pos := Strm.Position;
+ if not ReadBitmap then
+ begin
+ Strm.Seek( Pos, spBegin );
+ Clear;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+////////////////// bitmap RLE-decoding and loading - by Vyacheslav A. Gavrik
+
+// by Vyacheslav A. Gavrik
+procedure DecodeRLE4(Bmp:PBitmap;Data:Pointer; MaxSize: DWORD);
+ procedure OddMove(Src,Dst:PByte;Size:Integer);
+ begin
+ if Size=0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ repeat
+ Dst^:=(Dst^ and $F0)or(Src^ shr 4);
+ Inc(Dst);
+ Dst^:=(Dst^ and $0F)or(Src^ shl 4);
+ Inc(Src);
+ Dec(Size);
+ until Size=0;
+ end;
+ procedure OddFill(Mem:PByte;Size,Value:Integer);
+ begin
+ Value:=(Value shr 4)or(Value shl 4);
+ Mem^:=(Mem^ and $F0)or(Value and $0F);
+ Inc(Mem);
+ if Size>1 then FillChar(Mem^,Size,Char( Value ))
+ else Mem^:=(Mem^ and $0F)or(Value and $F0);
+ end;
+var
+ pb: PByte;
+ x,y,z,i: Integer;
+begin
+ pb:=Data; x:=0; y:=0;
+ if Bmp.fScanLineSize = 0 then
+ Bmp.ScanLineSize;
+ while (y<Bmp.Height) and (DWORD(pb) - DWORD(Data) < MaxSize) do
+ begin
+ if pb^=0 then
+ begin
+ Inc(pb);
+ z:=pb^;
+ case pb^ of
+ 0: begin
+ Inc(y);
+ x:=0;
+ end;
+ 1: Break;
+ 2: begin
+ Inc(pb); Inc(x,pb^);
+ Inc(pb); Inc(y,pb^);
+ end;
+ else
+ begin
+ Inc(pb);
+ i:=(z+1)shr 1;
+ if i and 1 = 1 then Inc( i );
+ if x + z <= bmp.Width then
+ if x and 1 =1 then
+ OddMove(pb,@PByteArray(Integer( Bmp.fDIBBits ) + Bmp.fScanLineSize * y)[x shr 1],(z+1)shr 1)
+ else
+ Move(pb^,PByteArray(Integer( Bmp.fDIBBits ) + Bmp.fScanLineSize * y)[x shr 1],(z+1)shr 1);
+ Inc(pb,i-1);
+ Inc(x,z);
+ end;
+ end;
+ end else
+ begin
+ z:=pb^;
+ Inc(pb);
+ if x + z <= Bmp.Width then
+ if x and 1 = 1 then
+ OddFill(@PByteArray(Integer( Bmp.fDIBBits ) + Bmp.fScanLineSize * y)[x shr 1],(z+1) shr 1,pb^)
+ else
+ FillChar( PByteArray(Integer( Bmp.fDIBBits ) + Bmp.fScanLineSize * y)[x shr 1],
+ (z+1) shr 1, AnsiChar( pb^ ));
+ Inc(x,z);
+ end;
+ Inc(pb);
+ end;
+end;
+
+// by Vyacheslav A. Gavrik
+procedure DecodeRLE8(Bmp:PBitmap;Data:Pointer; MaxSize: DWORD);
+var
+ pb: PByte;
+ x,y,z,i: Integer;
+begin
+ pb:=Data; y:=0; x:=0;
+ if Bmp.fScanLineSize = 0 then
+ Bmp.ScanLineSize;
+
+ while (y<Bmp.Height) and (DWORD(pb) - DWORD(Data) < MaxSize) do
+ begin
+ if pb^=0 then
+ begin
+ Inc(pb);
+ case pb^ of
+ 0: begin
+ Inc(y);
+ x:=0;
+ end;
+ 1: Break;
+ 2: begin
+ Inc(pb); Inc(x,pb^);
+ Inc(pb); Inc(y,pb^);
+ end;
+ else
+ begin
+ i:=pb^;
+ z:=(i+1)and(not 1);
+ Inc(pb);
+ Move(pb^,PByteArray(Integer( Bmp.fDIBBits ) + Bmp.fScanLineSize * y)[x],i);
+ Inc(pb,z-1);
+ Inc(x,i);
+ end;
+ end;
+ end else
+ begin
+ i:=pb^; Inc(pb);
+ if x + i <= Bmp.Width then
+ FillChar( PByteArray(Integer( Bmp.fDIBBits ) + Bmp.fScanLineSize * y)[x],
+ i, AnsiChar( pb^ ));
+ Inc(x,i);
+ end;
+ Inc(pb);
+ end;
+end;
+
+function TBitmap.LoadFromFileEx(const Filename: KOLString): Boolean; // by Vyacheslav A. Gavrik
+var Strm: PStream;
+begin
+ Strm := NewReadFileStream( Filename );
+ Result := LoadFromStreamEx(Strm);
+ Strm.Free;
+end;
+
+function TBitmap.LoadFromStreamEx(Strm: PStream): Boolean; // by Vyacheslav A. Gavrik
+var Pos : DWORD;
+ i: Integer;
+
+ function ReadBitmap : Boolean;
+ var Off, Size, ColorCount: Integer;
+ BFH : TBitmapFileHeader;
+ BCH: TBITMAPCOREHEADER;
+ BFHValid: Boolean;
+ Buffer: Pointer;
+ L: DWORD;
+ ColorTriples: Boolean;
+ PColr: PDWORD;
+ FinalPos: DWORD;
+ ZI: DWORD;
+ begin
+ fHandleType := bmDIB;
+ Result := False;
+ BFHValid := FALSE;
+ if Strm.Read( BFH, Sizeof( BFH ) ) <> Sizeof( BFH ) then Exit; {>>>>>>>>>}
+ Off := 0; Size := 0;
+ ColorTriples := FALSE;
+ if BFH.bfType <> $4D42 then
+ begin
+ Strm.Seek( Pos, spBegin );
+ BFH.bfOffBits := 0;
+ BFH.bfSize := 0;
+ end
+ else
+ begin
+ BFHValid := TRUE;
+ Off := BFH.bfOffBits;
+ Size := BFH.bfSize;
+ end;
+ fDIBHeader := AllocMem( 256*sizeof(TRGBQuad) + sizeof(TBitmapInfoHeader) );
+ if Strm.Read( fDIBHeader.bmiHeader.biSize, Sizeof( fDIBHeader.bmiHeader.biSize ) ) <>
+ Sizeof( fDIBHeader.bmiHeader.biSize ) then Exit; {>>>>>>>>>>>>>>>>>>>>>}
+ if (fDIBHeader.bmiHeader.biSize <> Sizeof( TBITMAPCOREHEADER )) and
+ (fDIBHeader.bmiHeader.biSize <> Sizeof( TBitmapInfoHeader )) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ L := fDIBHeader.bmiHeader.biSize - Sizeof( fDIBHeader.bmiHeader.biSize );
+ if (fDIBHeader.bmiHeader.biSize = Sizeof( TBITMAPCOREHEADER )) then
+ begin
+ if Strm.Read( BCH.bcWidth, L ) <> L then Exit; {>>>>>>>>>>>>>>>>>>>>>>>}
+ fDIBHeader.bmiHeader.biSize := Sizeof( TBitmapInfoHeader );
+ fDIBHeader.bmiHeader.biWidth := BCH.bcWidth;
+ fDIBHeader.bmiHeader.biHeight := BCH.bcHeight;
+ fDIBHeader.bmiHeader.biPlanes := BCH.bcPlanes;
+ fDIBHeader.bmiHeader.biBitCount := BCH.bcBitCount;
+ ColorTriples := TRUE;
+ end
+ else
+ begin
+ if Strm.Read( fDIBHeader.bmiHeader.biWidth, L) <> L then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ fNewPixelFormat := Bits2PixelFormat( fDIBHeader.bmiHeader.biBitCount
+ * fDIBHeader.bmiHeader.biPlanes );
+ fWidth := fDIBHeader.bmiHeader.biWidth;
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( fWidth > 0, 'Bitmap width must be > 0' );
+ {$ENDIF KOL_ASSERTIONS}
+ fHeight := Abs(fDIBHeader.bmiHeader.biHeight);
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( fHeight > 0, 'Bitmap height must be > 0' );
+ {$ENDIF KOL_ASSERTIONS}
+
+ fDIBSize := ScanLineSize * fHeight;
+ ZI := 0;
+ if (fDIBHeader.bmiHeader.biCompression = BI_RLE8) or
+ (fDIBHeader.bmiHeader.biCompression = BI_RLE4) then
+ ZI := GMEM_ZEROINIT;
+ fDIBBits := Pointer( GlobalAlloc( GMEM_FIXED or ZI, fDIBSize + 4 ) );
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( fDIBBits <> nil, 'No memory' );
+ ASSERT( (fDIBHeader.bmiHeader.biCompression and
+ (BI_RLE8 or BI_RLE4 or BI_RLE8 or BI_BITFIELDS) <> 0) or
+ (fDIBHeader.bmiHeader.biCompression = BI_RGB),
+ 'Unknown compression algorithm');
+ {$ENDIF KOL_ASSERTIONS}
+
+ ColorCount := 0;
+ if fDIBHeader.bmiHeader.biBitCount <= 8 then
+ begin
+ if fDIBHeader.bmiHeader.biClrUsed > 0 then
+ ColorCount := fDIBHeader.bmiHeader.biClrUsed * Sizeof( TRGBQuad )
+ else
+ ColorCount := (1 shl fDIBHeader.bmiHeader.biBitCount) * Sizeof( TRGBQuad )
+ end
+ else if (fNewPixelFormat in [ pf15bit, pf16bit ]) or
+ (fDIBHeader.bmiHeader.biCompression = BI_BITFIELDS) then
+ begin
+ if (Strm.Size = 0) or (Strm.Size - Strm.Position - DWORD( Size ) >= 12) then
+ ColorCount := 12;
+ end;
+
+ if ColorTriples then
+ ColorCount := ColorCount div 4 * 3;
+
+ if Off > 0 then
+ begin
+ if ColorTriples then
+ Off := Off - SizeOf( TBitmapFileHeader ) - Sizeof( TBitmapCoreHeader )
+ else Off := Off - SizeOf( TBitmapFileHeader ) - Sizeof( TBitmapInfoHeader );
+ if (Off <> ColorCount) and (fNewPixelFormat <= pf8bit) then
+ if ColorTriples then
+ ColorCount := min( Off, 3 * 256 )
+ else
+ ColorCount := min( Off, 4 * 256 );
+ end;
+ if (fNewPixelFormat in [ pf15bit, pf16bit ]) then
+ if (fDIBHeader.bmiHeader.biCompression = BI_BITFIELDS) then
+ begin
+ PDWORD( DWORD( @ fDIBHeader.bmiColors[ 0 ] ) + 8 )^ := ( $00001F );
+ PDWORD( DWORD( @ fDIBHeader.bmiColors[ 0 ] ) + 4 )^ := ( $0007E0 );
+ TColor( fDIBHeader.bmiColors[ 0 ] ) := ( $00F800 );
+ end else
+ ColorCount := 0;
+
+ if ColorCount <> 0 then
+ if ColorTriples then
+ begin
+ PColr := @ fDIBheader.bmiColors[ 0 ];
+ while ColorCount >= 3 do
+ begin
+ if strm.Read( PColr^, 3 ) <> 3 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>}
+ Inc( PColr );
+ Dec( ColorCount, 3 );
+ end;
+ end else
+ begin
+ if (Integer( Strm.Size - Strm.Position ) > fDIBSize) or
+ (fDIBHeader.bmiHeader.biCompression = BI_RLE8) or
+ (fDIBHeader.bmiHeader.biCompression = BI_RLE4) then
+ begin
+ if Strm.Read( fDIBheader.bmiColors[ 0 ], ColorCount )
+ <> DWORD( ColorCount ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if Off - ColorCount > 0 then
+ Strm.Position := Integer( Strm.Position ) + Off - ColorCount;
+ end;
+ end;
+
+ if not BFHValid then
+ Size := fDIBSize
+ else
+ if (fDIBHeader.bmiHeader.biCompression = BI_RLE8) or
+ (fDIBHeader.bmiHeader.biCompression = BI_RLE4) then
+ begin
+ //if BFHValid then //-- already TRUE here
+ Size := BFH.bfSize - BFH.bfOffBits;
+ end
+ else
+ begin
+ if (Strm.Size = 0) or
+ (Integer( Strm.Size - BFH.bfOffBits - Pos ) > Integer(Size)) then
+ Size := fDIBSize
+ else
+ Size := Strm.Size - BFH.bfOffBits - DWORD( Pos );
+ if Size > fDIBSize then Size := fDIBSize
+ else if (Size < fDIBSize) and (fDIBheader.bmiHeader.biClrUsed <> 0) then
+ begin
+ BFHValid := FALSE;
+ Strm.Position := Strm.Position + fDIBheader.bmiHeader.biClrUsed * 4;
+ Size := Strm.Size - Strm.Position;
+ end;
+ end;
+
+ if (fDIBHeader.bmiHeader.biCompression = BI_RGB) or
+ (fDIBHeader.bmiHeader.biCompression = BI_BITFIELDS) then
+ begin
+ if BFHValid and
+ ( (Strm.Size > 0) and
+ (Integer( Strm.Size - BFH.bfOffBits - Pos) > Integer(Size))
+ or
+ (Strm.Size = 0) and
+ (Off > 0)
+ ) then
+ if Integer( Strm.Position - Pos ) <= Integer( BFH.bfOffbits ) then
+ Strm.Position := Pos + BFH.bfOffbits;
+ i := Strm.Read( fDIBBits^, Size );
+ if i <> Size then
+ begin
+ {$IFDEF FILL_BROKEN_BITMAP}
+ ZeroMemory( Pointer( Integer( fDIBBits ) + i ), Size - i );
+ {$ENDIF FILL_BROKEN_BITMAP}
+ end;
+ end
+ else
+ begin
+ if (Integer( fDIBHeader.bmiHeader.biSizeImage ) > 0) and
+ (Integer( fDIBHeader.bmiHeader.biSizeImage ) < Size) then
+ Size := Integer( fDIBHeader.bmiHeader.biSizeImage ); // - ColorCount;
+ // it is possible that bitmap "compressed" with RLE has size
+ // greater then non-compressed one:
+ FinalPos := Strm.Position + DWORD( Size );
+ L := Strm.Size - Strm.Position;
+ if L > DWORD( Size ) then
+ L := Size;
+ Buffer := AllocMem( Size * 3 );
+ if Strm.Read(Buffer^,L) <> DWORD( L ) then ;
+ if fDIBHeader.bmiHeader.biCompression=BI_RLE8 then
+ DecodeRLE8(@Self,Buffer,Size * 3)
+ else
+ DecodeRLE4(@Self,Buffer,Size * 3);
+ Strm.Position := FinalPos;
+ fDIBHeader.bmiHeader.biCompression := BI_RGB;
+ FreeMem(Buffer);
+ end;
+
+ Result := True;
+ end;
+begin
+ Clear;
+ Pos := Strm.Position;
+ result := ReadBitmap;
+ if not result then
+ begin
+ Strm.Seek( Pos, spBegin );
+ Clear;
+ end;
+end;
+
+///////////////////////////
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TBitmap.ReleaseHandle: HBitmap;
+var OldBits: Pointer;
+begin
+ HandleType := bmDIB;
+ Result := GetHandle;
+ if Result = 0 then Exit; // only when bitmap is empty {>>>>>>>>>>>>>>>>>>>>>>}
+ if fDIBAutoFree then
+ begin
+ OldBits := fDIBBits;
+ fDIBBits := Pointer( GlobalAlloc( GMEM_FIXED {or GMEM_ZEROINIT}, fDIBSize ) );
+ Move( OldBits^, fDIBBits^, fDIBSize );
+ fDIBAutoFree := FALSE;
+ end;
+ fHandle := 0;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.SaveToFile(const Filename: KOLString);
+var Strm: PStream;
+begin
+ if Empty then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Strm := NewWritefileStream( Filename );
+ SaveToStream( Strm );
+ Strm.Free;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TBitmap.CoreSaveToFile(const Filename: KOLString);
+var Strm: PStream;
+begin
+ if Empty then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Strm := NewWritefileStream( Filename );
+ CoreSaveToStream( Strm );
+ Strm.Free;
+end;
+
+procedure TBitmap.RLESaveToFile(const Filename: KOLString);
+var Strm: PStream;
+begin
+ if Empty then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Strm := NewWritefileStream( Filename );
+ RLESaveToStream( Strm );
+ Strm.Free;
+end;
+
+{$IFDEF ASM_STREAM}
+procedure TBitmap.SaveToStream(Strm: PStream);
+type tBFH = TBitmapFileHeader;
+ tBIH = TBitmapInfoHeader;
+const szBIH = Sizeof( tBIH );
+ szBFH = Sizeof( tBFH );
+asm
+ PUSH EBX
+ PUSH ESI
+ MOV EBX, EAX
+ MOV ESI, EDX
+ CALL GetEmpty
+ JZ @@exit
+ MOV EAX, ESI
+ CALL TStream.GetPosition
+ PUSH EAX
+
+ MOV EAX, EBX
+ XOR EDX, EDX // EDX = bmDIB
+ CALL SetHandleType
+ XOR EAX, EAX
+ MOV EDX, [EBX].fDIBHeader
+ MOVZX ECX, [EDX].TBitmapInfoHeader.biBitCount
+ CMP CL, 8
+ JG @@1
+ MOV AL, 4
+ SHL EAX, CL
+@@1:
+ PUSH EAX // ColorsSize
+ LEA ECX, [EAX + szBFH + szBIH]
+ CMP [EDX].TBitmapInfoHeader.biCompression, 0
+ JZ @@10
+ ADD ECX, 74
+@@10:
+ PUSH ECX // BFH.bfOffBits
+ PUSH 0
+ ADD ECX, [EBX].fDIBSize
+ PUSH ECX
+ MOV CX, $4D42
+ PUSH CX
+ XOR ECX, ECX
+ MOV EDX, ESP
+ MOV CL, szBFH
+ PUSH ECX
+ MOV EAX, ESI
+ CALL TStream.Write
+ POP ECX
+ ADD ESP, szBFH
+ XOR EAX, ECX
+ POP ECX // ColorsSize
+ JNZ @@ewrite
+ MOV EDX, [EBX].fDIBHeader
+ CMP [EDX].TBitmapInfoHeader.biCompression, 0
+ JZ @@11
+ ADD ECX, 74
+@@11:
+ ADD ECX, szBIH
+ PUSH ECX
+ MOV EAX, ESI
+ CALL TStream.Write
+ POP ECX
+ XOR EAX, ECX
+ JNZ @@ewrite
+
+ MOV ECX, [EBX].fDIBSize
+ MOV EDX, [EBX].fDIBBits
+ MOV EAX, ESI
+ PUSH ECX
+ CALL TStream.Write
+ POP ECX
+ XOR EAX, ECX
+@@ewrite:
+ POP EDX
+ JZ @@exit
+ XCHG EAX, ESI
+ XOR ECX, ECX
+ CALL TStream.Seek
+@@exit:
+ POP ESI
+ POP EBX
+end;
+{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.SaveToStream(Strm: PStream);
+var BFH : TBitmapFileHeader;
+ Pos : Integer;
+ function WriteBitmap : Boolean;
+ var ColorsSize, BitsSize, Size : Integer;
+ begin
+ Result := False;
+ if Empty then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ HandleType := bmDIB; // convert to DIB if DDB
+ ZeroMemory( @BFH, Sizeof( BFH ) );
+ ColorsSize := 0;
+ with fDIBHeader.bmiHeader do
+ if biBitCount <= 8 then
+ ColorsSize := (1 shl biBitCount) * Sizeof( TRGBQuad );
+ BFH.bfOffBits := Sizeof( BFH ) + Sizeof( TBitmapInfoHeader ) + ColorsSize;
+ BitsSize := fDIBSize; //ScanLineSize * fHeight;
+ BFH.bfSize := BFH.bfOffBits + DWord( BitsSize );
+ BFH.bfType := $4D42; // 'BM';
+ if fDIBHeader.bmiHeader.biCompression <> 0 then
+ begin
+ ColorsSize := 12 + 16*sizeof(TRGBQuad);
+ Inc( BFH.bfOffBits, ColorsSize );
+ end;
+ if Strm.Write( BFH, Sizeof( BFH ) ) <> Sizeof( BFH ) then Exit; {>>>>>>>>}
+ Size := Sizeof( TBitmapInfoHeader ) + ColorsSize;
+ if Strm.Write( fDIBHeader^, Size ) <> DWORD(Size) then Exit; {>>>>>>>>>>>}
+ if Strm.Write( fDIBBits^, BitsSize ) <> DWord( BitsSize ) then Exit; {>>>}
+ Result := True;
+ end;
+begin
+ Pos := Strm.Position;
+ if not WriteBitmap then
+ Strm.Seek( Pos, spBegin );
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TBitmap.CoreSaveToStream(Strm: PStream);
+type TRGBTriple = packed record
+ bRed, bGreen, bBlue: Byte;
+ end;
+var BFH : TBitmapFileHeader;
+ Pos : Integer;
+ function WriteCoreBitmap : Boolean;
+ var ColorsSize, ColorsCount, BitsSize, i: Integer;
+ CH: TBitmapCoreHeader;
+ begin
+ Result := False;
+ if Empty then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ HandleType := bmDIB; // convert to DIB if DDB
+ ZeroMemory( @BFH, Sizeof( BFH ) );
+ ColorsSize := 0;
+ ColorsCount := 1 shl fDIBHeader.bmiHeader.biBitCount;
+ with fDIBHeader.bmiHeader do
+ if biBitCount <= 8 then
+ ColorsSize := ColorsCount * Sizeof( TRGBTriple );
+ BFH.bfOffBits := Sizeof( BFH ) + Sizeof( CH ) + ColorsSize;
+ BitsSize := fDIBSize; //ScanLineSize * fHeight;
+ BFH.bfSize := BFH.bfOffBits + DWord( BitsSize );
+ BFH.bfType := $4D42; // 'BM';
+
+ if Strm.Write( BFH, Sizeof( BFH ) ) <> Sizeof( BFH ) then Exit; {>>>>>>>>}
+ CH.bcSize := Sizeof( CH );
+ CH.bcWidth := Width;
+ CH.bcHeight := Height;
+ CH.bcPlanes := 1;
+ CH.bcBitCount := fDIBHeader.bmiHeader.biBitCount;
+ if Strm.Write( CH, Sizeof( CH ) ) <> Sizeof(CH) then Exit; {>>>>>>>>>>>>>}
+ for i := 0 to ColorsCount-1 do
+ begin
+ if Strm.Write( fDIBHeader.bmiColors[i], 3 ) <> 3 then Exit; {>>>>>>>}
+ end;
+ if Strm.Write( fDIBBits^, BitsSize ) <> DWord( BitsSize ) then Exit; {>>>}
+ Result := True;
+ end;
+begin
+ if (fDIBHeader.bmiHeader.biBitCount > 8)
+ or (fDIBHeader.bmiHeader.biCompression <> 0) then
+ begin
+ SaveToStream( Strm ); Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Pos := Strm.Position;
+ if not WriteCoreBitmap then
+ Strm.Seek( Pos, spBegin );
+end;
+
+procedure TBitmap.RLESaveToStream(Strm: PStream);
+var BFH : TBitmapFileHeader;
+ Pos : Integer;
+ MS: PStream;
+ function CountZeroes( P: PByte; maxBytes: Integer ): Integer;
+ begin
+ Result := 0;
+ while (P^ = 0) and (Result < maxBytes) do
+ begin
+ inc( P );
+ inc( Result );
+ end;
+ end;
+ function CountSame( P: PByte; maxBytes: Integer ): Integer;
+ var B: Byte;
+ begin
+ Result := 1;
+ B := P^;
+ while maxBytes > 1 do
+ begin
+ inc(P);
+ if P^ <> B then break;
+ inc(Result);
+ dec(maxBytes);
+ end;
+ end;
+ function CountSame2( P: PByteArray; maxPixels: Integer ): Integer;
+ var B1, B2: Byte;
+ i: Integer;
+ begin
+ Result := 2;
+ B1 := P[0];
+ B2 := P[1];
+ i := 0;
+ dec( maxPixels, 2 );
+ while maxPixels > 0 do
+ begin
+ inc(i, 2);
+ if P[i] <> B1 then break;
+ inc(Result);
+ dec(maxPixels);
+ if maxPixels = 0 then break;
+ if P[i+1] <> B2 then break;
+ inc(Result);
+ dec(maxPixels);
+ end;
+ end;
+ function CountDiff( P: PByte; maxBytes: Integer; minSame: Integer ): Integer;
+ var Cnt: Integer;
+ begin
+ Result := 1;
+ while (maxBytes > 1) do
+ begin
+ inc(P);
+ dec(maxBytes);
+ Cnt := CountSame( P, maxBytes );
+ if Cnt >= minSame then
+ break;
+ inc( Result );
+ end;
+ end;
+ function CountDiff2( P: PByte; maxPixels: Integer; minSame: Integer ): Integer;
+ var Cnt: Integer;
+ begin
+ Result := 1;
+ while (maxPixels > 1) do
+ begin
+ inc(P);
+ dec(maxPixels);
+ Cnt := CountSame2( Pointer( P ), maxPixels );
+ if Cnt >= minSame then
+ break;
+ inc( Result );
+ end;
+ end;
+ procedure WriteOffset( dx, dy: Integer );
+ var b: Byte;
+ begin
+ while (dx > 0) or (dy > 0) do
+ begin
+ Strm.WriteVal( 0, 1 );
+ Strm.WriteVal( 2, 1 );
+ b := min( dx, 255 );
+ Strm.WriteVal( b, 1 );
+ dec( dx, b );
+ b := min( dy, 255 );
+ Strm.WriteVal( b, 1 );
+ dec( dy, b );
+ end;
+ end;
+ procedure WriteRep( cnt: Integer; Value: Byte );
+ var n: Integer;
+ begin
+ while cnt > 0 do
+ begin
+ n := min( cnt, 255 );
+ dec( cnt, n );
+ while (cnt > 0) and (cnt < 3) do
+ begin
+ inc( cnt );
+ dec( n );
+ end;
+ Strm.WriteVal( n, 1 );
+ Strm.WriteVal( Value, 1 );
+ end;
+ end;
+ procedure WriteRun( P: PByte; cnt: Integer );
+ var n: Integer;
+ begin
+ while cnt > 0 do
+ begin
+ n := min( cnt, 255 );
+ dec( cnt, n );
+ if (cnt < 3) and (n = 255) then
+ begin
+ inc( cnt, 2 );
+ dec( n, 2 );
+ end;
+ if n > 2 then
+ begin
+ Strm.WriteVal( 00, 1 );
+ Strm.WriteVal( n, 1 );
+ Strm.Write( P^, n );
+ inc( P, n );
+ if n and 1 <> 0 then
+ Strm.WriteVal( 00, 1 );
+ end else
+ while n > 0 do
+ begin
+ Strm.WriteVal( 01, 1 );
+ Strm.Write( P^, 1 );
+ inc( P );
+ dec( n );
+ end;
+ end;
+ end;
+ procedure WriteRun2( P: PByteArray; cnt: Integer );
+ var n, i, L: Integer;
+ begin
+ i := 0;
+ while cnt > 0 do
+ begin
+ n := min( cnt, 252 );
+ dec( cnt, n );
+ if (cnt < 3) and (n = 252) then
+ begin
+ inc( n, cnt );
+ cnt := 0;
+ end;
+ if n > 2 then
+ begin
+ Strm.WriteVal( 00, 1 );
+ Strm.WriteVal( n, 1 );
+ L := 0;
+ while n > 0 do
+ begin
+ Strm.WriteVal( P[i] shl 4 or P[i+1], 1 );
+ inc( i, 2 );
+ dec( n, 2 );
+ inc( L );
+ end;
+ if L and 1 <> 0 then
+ Strm.WriteVal( 0, 1 );
+ end else
+ while n > 0 do
+ begin
+ if n = 1 then
+ Strm.WriteVal( 01, 1 )
+ else
+ Strm.WriteVal( 02, 1 );
+ Strm.WriteVal( P[i] shl 4 or P[i+1], 1 );
+ inc( i, 2 );
+ dec( n, 2 );
+ end;
+ end;
+ end;
+ function WriteRLE4: Boolean;
+ var line_len_left, y, cnt: Integer;
+ P, Pnext: PByte;
+ PnextLine: PByte;
+ offX, offY: Integer;
+ begin
+ y := 0;
+ P := MS.Memory;
+ while y < Height do
+ begin
+ line_len_left := Width;
+ PnextLine := P; inc( PnextLine, line_len_left );
+ while line_len_left > 0 do
+ begin
+ if P^ = 0 then
+ begin
+ cnt := CountZeroes( P, line_len_left + (Height-y-1)*Width );
+ if cnt > 3 then
+ begin // generate offset
+ offY := cnt div Width;
+ offX := cnt - offY * Width;
+ if (offX < 0)
+ or (offY = 0) and (offX >= line_len_left)
+ or (line_len_left < offX) then
+ begin
+ inc( P, line_len_left );
+ break;
+ end;
+ if offY > 0 then
+ begin
+ WriteOffset( offX, offY );
+ inc( P, cnt );
+ dec( line_len_left, offX );
+ inc( Y, offY );
+ continue;
+ end;
+ end;
+ end;
+ cnt := CountSame2( Pointer( P ), line_len_left );
+ if cnt >= 3 then
+ begin
+ Pnext := P; inc( Pnext );
+ WriteRep( cnt, (P^ shl 4) or (Pnext^) );
+ inc( P, cnt );
+ dec( line_len_left, cnt );
+ end else
+ begin
+ cnt := CountDiff2( P, line_len_left, 3 );
+ WriteRun2( Pointer( P ), cnt );
+ inc( P, cnt );
+ dec( line_len_left, cnt );
+ end;
+ end;
+ Strm.WriteVal( 0, 1 );
+ if y < Height-1 then
+ Strm.WriteVal( 0, 1 ) // EOL
+ else Strm.WriteVal( 1, 1 ); // EOB
+ inc(y);
+ if ( Integer( P ) - Integer( PnextLine ) ) mod Width <> 0 then
+ begin {$IFNDEF PAS_ONLY}
+ asm
+ nop
+ end;{$ENDIF}
+ end;
+ end;
+ Result := TRUE;
+ end;
+ function WriteRLE8: Boolean;
+ var line_len_left, y, cnt: Integer;
+ P: PByte;
+ //Pnext: PByte;
+ offX, offY: Integer;
+ begin
+ y := 0;
+ P := MS.Memory;
+ while y < Height do
+ begin
+ line_len_left := Width;
+ //Pnext := P; inc( Pnext, line_len_left );
+ while line_len_left > 0 do
+ begin
+ if P^ = 0 then
+ begin
+ cnt := CountZeroes( P, line_len_left + (Height-y-1)*Width );
+ if cnt > 3 then
+ begin // generate offset
+ offY := cnt div Width;
+ offX := cnt - offY * Width;
+ if (offX < 0)
+ or (offY = 0) and (offX >= line_len_left)
+ or (line_len_left < offX) then
+ begin
+ inc( P, line_len_left );
+ break;
+ end;
+ if offY > 0 then
+ begin
+ WriteOffset( offX, offY );
+ inc( P, cnt );
+ dec( line_len_left, offX );
+ inc( Y, offY );
+ continue;
+ end;
+ end;
+ end;
+ cnt := CountSame( P, line_len_left );
+ if cnt >= 2 then
+ begin
+ WriteRep( cnt, P^ );
+ inc( P, cnt );
+ dec( line_len_left, cnt );
+ end else
+ begin
+ cnt := CountDiff( P, line_len_left, 2 );
+ WriteRun( P, cnt );
+ inc( P, cnt );
+ dec( line_len_left, cnt );
+ end;
+ end;
+ Strm.WriteVal( 00, 1 );
+ if y < Height-1 then
+ Strm.WriteVal( 00, 1 ) // EOL
+ else Strm.WriteVal( 01, 1 ); // EOB
+ inc(y);
+ {if P <> Pnext then
+ asm
+ nop
+ end;}
+ end;
+ Result := TRUE;
+ end;
+ function WriteBitmap : Boolean;
+ var ColorsSize, BitsSize : Integer;
+ BIH: TBitmapInfoHeader;
+ x, y: Integer;
+ Line: PByte;
+ Buffer: PByteArray;
+ begin
+ Result := False;
+ if Empty then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ HandleType := bmDIB; // convert to DIB if DDB
+ ZeroMemory( @BFH, Sizeof( BFH ) );
+ ColorsSize := 0;
+ with fDIBHeader.bmiHeader do
+ if biBitCount <= 8 then
+ ColorsSize := (1 shl biBitCount) * Sizeof( TRGBQuad );
+ BFH.bfOffBits := Sizeof( BFH ) + Sizeof( TBitmapInfoHeader ) + ColorsSize;
+ BitsSize := fDIBSize; //ScanLineSize * fHeight;
+ BFH.bfSize := BFH.bfOffBits + DWord( BitsSize );
+ BFH.bfType := $4D42; // 'BM';
+ if fDIBHeader.bmiHeader.biCompression <> 0 then
+ begin
+ ColorsSize := 12 + 16*sizeof(TRGBQuad);
+ Inc( BFH.bfOffBits, ColorsSize );
+ end;
+ if Strm.Write( BFH, Sizeof( BFH ) ) <> Sizeof( BFH ) then Exit; {>>>>>>>>}
+ BIH := fDIBHeader.bmiHeader;
+ MS := NewMemoryStream;
+ if fDIBHeader.bmiHeader.biBitCount = 8 then
+ begin
+ for y := Height-1 downto 0 do
+ begin
+ Line := ScanLine[y];
+ MS.Write( Line^, Width );
+ end;
+ end else
+ begin
+ Buffer := AllocMem( Width );
+ for y := Height-1 downto 0 do
+ begin
+ Line := ScanLine[y];
+ x := 0;
+ while x < Width do
+ begin
+ Buffer[x] := Line^ shr 4;
+ inc( x );
+ if x >= Width then break;
+ Buffer[x] := Line^ and 15;
+ inc( x );
+ inc( Line );
+ end;
+ MS.Write( Buffer^, Width );
+ end;
+ MS.WriteVal( 0, 2 );
+ end;
+ if fDIBHeader.bmiHeader.biBitCount = 8 then
+ BIH.biCompression := BI_RLE8
+ else BIH.biCompression := BI_RLE4;
+ if Strm.Write( BIH, Sizeof( BIH ) ) <> Sizeof( BIH ) then Exit; {>>>>>>>}
+ if Strm.Write( fDIBHeader.bmiColors, ColorsSize ) <> DWORD(ColorsSize) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if fDIBHeader.bmiHeader.biBitCount = 8 then
+ Result := WriteRLE8
+ else Result := WriteRLE4;
+ MS.Free;
+ end;
+begin
+ Pos := Strm.Position;
+ if (fDIBHeader.bmiHeader.biBitCount <> 4)
+ and (fDIBHeader.bmiHeader.biBitCount <> 8) then
+ begin
+ SaveToStream( Strm ); Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ if not WriteBitmap then
+ Strm.Seek( Pos, spBegin );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.SetHandle(const Value: HBitmap);
+var B: tagBitmap;
+ Dib: TDIBSection;
+begin
+ Clear;
+ if Value = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if (WinVer >= wvNT) and
+ (GetObject( Value, Sizeof( Dib ), @ Dib ) = Sizeof( Dib ))
+ and (Dib.dsBmih.biBitCount > 8) then
+ begin
+ fHandle := Value;
+ fHandleType := bmDIB;
+ fDIBHeader := PrepareBitmapHeader( Dib.dsBm.bmWidth, Dib.dsBm.bmHeight,
+ Dib.dsBm.bmBitsPixel );
+ Move( Dib.dsBitfields, fDIBHeader.bmiColors, 3 * 4 );
+ fWidth := Dib.dsBm.bmWidth;
+ fHeight := Dib.dsBm.bmHeight;
+ fDIBBits := Dib.dsBm.bmBits;
+ fDIBSize := Dib.dsBmih.biSizeImage;
+ fDIBAutoFree := true;
+ end
+ else
+ begin
+ if GetObject( Value, Sizeof( B ), @B ) = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>}
+ fHandle := Value;
+ fWidth := B.bmWidth;
+ fHeight := B.bmHeight;
+ fHandleType := bmDDB;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TBitmap.SetWidth(const Value: Integer);
+begin
+ if fWidth = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fWidth := Value;
+ FormatChanged;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.SetHeight(const Value: Integer);
+{$IFNDEF SMALLER_CODE}
+var
+ pf : TPixelFormat;
+{$ENDIF SMALLER_CODE}
+begin
+ if fHeight = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+{$IFNDEF SMALLER_CODE}
+ pf := PixelFormat;
+{$ENDIF SMALLER_CODE}
+ HandleType := bmDDB;
+ // Not too good, but provides correct changing of height
+ // preserving previous image
+ fHeight := Value;
+ FormatChanged;
+{$IFNDEF SMALLER_CODE}
+ PixelFormat := pf;
+{$ENDIF SMALLER_CODE}
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.SetPixelFormat(Value: TPixelFormat);
+begin
+ if PixelFormat = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if Empty then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if Value = pfDevice then
+ HandleType := bmDDB
+ else
+ begin
+ fNewPixelFormat := Value;
+ HandleType := bmDIB;
+ FormatChanged;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function CalcScanLineSize( Header: PBitmapInfoHeader ): Integer;
+begin
+ Result := ((Header.biBitCount * Header.biWidth + 31) shr 3) and $FFFFFFFC;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure FillBmpWithBkColor( Bmp: PBitmap; DC2: HDC; oldWidth, oldHeight: Integer );
+var oldBmp: HBitmap;
+ R: TRect;
+ Br: HBrush;
+begin
+ with Bmp^ do
+ if Color2RGB( fBkColor ) <> 0 then
+ if (oldWidth < fWidth) or (oldHeight < fHeight) then
+ if GetHandle <> 0 then
+ begin
+ oldBmp := SelectObject( DC2, fHandle );
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( oldBmp <> 0, 'Can not select bitmap to DC' );
+ {$ENDIF KOL_ASSERTIONS}
+ Br := CreateSolidBrush( Color2RGB( fBkColor ) );
+ R := MakeRect( oldWidth, oldHeight, fWidth, fHeight );
+ if oldWidth = fWidth then
+ R.Left := 0;
+ if oldHeight = fHeight then
+ R.Top := 0;
+ Windows.FillRect( DC2, R, Br );
+ DeleteObject( Br );
+ SelectObject( DC2, oldBmp );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+const BitCounts: array[ TPixelFormat ] of Byte = ( 0, 1, 4, 8, 16, 16, 24, 32, 0 );
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.FormatChanged;
+// This method is used whenever Width, Height, PixelFormat or HandleType
+// properties are changed.
+// Old image will be drawn here to a new one (excluding cases when
+// old width or height was 0, and / or new width or height is 0).
+// To avoid inserting this code into executable, try not to change
+// properties Width / Height of bitmat after it is created using
+// NewBitmap( W, H ) function or after it is loaded from file, stream or resource.
+var B: tagBitmap;
+ oldBmp, NewHandle: HBitmap;
+ DC0, DC2: HDC;
+ NewHeader: PBitmapInfo;
+ NewBits: Pointer;
+ oldHeight, oldWidth, sizeBits, bitsPixel: Integer;
+ Br: HBrush;
+ N: Integer;
+ NewDIBAutoFree: Boolean;
+ Hndl: THandle;
+begin
+ if Empty then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ NewDIBAutoFree := FALSE;
+ fDetachCanvas( @Self );
+ fScanLineSize := 0;
+ fGetDIBPixels := nil;
+ fSetDIBPixels := nil;
+
+ oldWidth := fWidth;
+ oldHeight := fHeight;
+ if fDIBBits <> nil then
+ begin
+ oldWidth := fDIBHeader.bmiHeader.biWidth;
+ oldHeight := Abs(fDIBHeader.bmiHeader.biHeight);
+ end
+ else
+ if fHandle <> 0 then
+ begin
+ if GetObject( fHandle, Sizeof( B ), @ B ) <> 0 then
+ begin
+ oldWidth := B.bmWidth;
+ oldHeight := B.bmHeight;
+ end;
+ end;
+
+ DC2 := CreateCompatibleDC( 0 );
+
+ if fHandleType = bmDDB then
+ begin
+ // New HandleType is bmDDB: old bitmap can be copied using Draw method
+ DC0 := GetDC( 0 );
+ NewHandle := CreateCompatibleBitmap( DC0, fWidth, fHeight );
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( NewHandle <> 0, 'Can not create DDB' );
+ {$ENDIF KOL_ASSERTIONS}
+ ReleaseDC( 0, DC0 );
+
+ oldBmp := SelectObject( DC2, NewHandle );
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( oldBmp <> 0, 'Can not select bitmap to DC' );
+ {$ENDIF KOL_ASSERTIONS}
+
+ Br := CreateSolidBrush( Color2RGB( fBkColor ) );
+ FillRect( DC2, MakeRect( 0, 0, fWidth, fHeight ), Br );
+ DeleteObject( Br );
+
+ if fDIBBits <> nil then
+ begin
+ SelectObject( DC2, oldBmp );
+ SetDIBits( DC2, NewHandle, 0, fHeight, fDIBBits, fDIBHeader^, DIB_RGB_COLORS );
+ end
+ else
+ begin
+ Draw( DC2, 0, 0 );
+ SelectObject( DC2, oldBmp );
+ end;
+
+ ClearData; // Image is cleared but fWidth and fHeight are preserved
+ fHandle := NewHandle;
+ end
+ else
+ begin
+ // New format is DIB. GetDIBits applied to transform old data to new one.
+ bitsPixel := BitCounts[ fNewPixelFormat ];
+ if bitsPixel = 0 then
+ begin
+ bitsPixel := BitCounts[DefaultPixelFormat];
+ end;
+
+ NewHandle := 0;
+ NewHeader := PrepareBitmapHeader( fWidth, fHeight, bitsPixel );
+ if fNewPixelFormat = pf16bit then
+ PreparePF16bit( NewHeader );
+
+ sizeBits := CalcScanLineSize( @NewHeader.bmiHeader ) * fHeight;
+
+ NewBits := Pointer( GlobalAlloc( GMEM_FIXED, sizeBits ) );
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( NewBits <> nil, 'No memory' );
+ {$ENDIF KOL_ASSERTIONS}
+
+ Hndl := GetHandle;
+ if Hndl = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ N :=
+ GetDIBits( DC2, Hndl, 0, Min( fHeight, oldHeight ),
+ NewBits, NewHeader^, DIB_RGB_COLORS );
+ if N <> Min( fHeight, oldHeight ) then
+ begin
+ GlobalFree( DWORD( NewBits ) );
+ NewBits := nil;
+ NewHandle := CreateDIBSection( DC2, NewHeader^, DIB_RGB_COLORS, NewBits, 0, 0 );
+ NewDIBAutoFree := TRUE;
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( NewHandle <> 0, 'Can not create DIB secion for pf16bit bitmap' );
+ {$ENDIF KOL_ASSERTIONS}
+ oldBmp := SelectObject( DC2, NewHandle );
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( oldBmp <> 0, 'Can not select pf16bit to DC' );
+ {$ENDIF KOL_ASSERTIONS}
+ Draw( DC2, 0, 0 );
+ SelectObject( DC2, oldBmp );
+ end;
+
+ ClearData;
+ fDIBSize := sizeBits;
+ fDIBBits := NewBits;
+ fDIBHeader := NewHeader;
+ fHandle := NewHandle;
+ fDIBAutoFree := NewDIBAutoFree;
+
+ end;
+
+ if Assigned( fFillWithBkColor ) then
+ fFillWithBkColor( @Self, DC2, oldWidth, oldHeight );
+
+ DeleteDC( DC2 );
+
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TBitmap.GetScanLine(Y: Integer): Pointer;
+begin
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( (Y >= 0) {and (Y < fHeight)}, 'ScanLine index out of bounds' );
+ ASSERT( fDIBBits <> nil, 'No bits available' );
+ {$ENDIF KOL_ASSERTIONS}
+ Result := nil;
+ if fDIBHeader = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if fDIBHeader.bmiHeader.biHeight > 0 then
+ Y := fHeight - 1 - Y;
+ if fScanLineSize = 0 then
+ ScanLineSize;
+
+ Result := Pointer( Integer( fDIBBits ) + fScanLineSize * Y );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TBitmap.GetScanLineSize: Integer;
+begin
+ Result := 0;
+ if fDIBHeader = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ FScanLineSize := CalcScanLineSize( @fDIBHeader.bmiHeader );
+ Result := FScanLineSize;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.CanvasChanged( Sender : PObj );
+begin
+ fBkColor := PCanvas( Sender ).Brush.Color;
+ ClearTransImage;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.Dormant;
+begin
+ RemoveCanvas;
+ if fHandle <> 0 then
+ DeleteObject( ReleaseHandle );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.SetBkColor(const Value: TColor);
+begin
+ if fBkColor = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fBkColor := Value;
+ fFillWithBkColor := FillBmpWithBkColor;
+ if Assigned( fApplyBkColor2Canvas ) then
+ fApplyBkColor2Canvas( @Self );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TBitmap.Assign(SrcBmp: PBitmap): Boolean;
+begin
+ Clear;
+ Result := False;
+ if SrcBmp = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if SrcBmp.Empty then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fWidth := SrcBmp.fWidth;
+ fHeight := SrcBmp.fHeight;
+ fHandleType := SrcBmp.fHandleType;
+ //fNewPixelFormat := SrcBmp.PixelFormat;
+ if SrcBmp.fHandleType = bmDDB then
+ begin
+ fHandle := CopyImage( SrcBmp.fHandle, IMAGE_BITMAP, 0, 0, 0 {LR_COPYRETURNORG} );
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( fHandle <> 0, 'Can not copy bitmap image' );
+ {$ENDIF KOL_ASSERTIONS}
+ Result := fHandle <> 0;
+ if not Result then Clear;
+ end
+ else
+ begin
+ GetMem( fDIBHeader, Sizeof(TBitmapInfoHeader) + 256*sizeof(TRGBQuad) );
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( fDIBHeader <> nil, 'No memory' );
+ {$ENDIF KOL_ASSERTIONS}
+ Move( SrcBmp.fDIBHeader^, fDIBHeader^, Sizeof(TBitmapInfoHeader) + 256*sizeof(TRGBQuad) );
+ fDIBSize := SrcBmp.fDIBSize;
+ fDIBBits := Pointer( GlobalAlloc( GMEM_FIXED {or GMEM_ZEROINIT}, fDIBSize ) );
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( fDIBBits <> nil, 'No memory' );
+ {$ENDIF KOL_ASSERTIONS}
+ Move( SrcBmp.fDIBBits^, fDIBBits^, fDIBSize );
+ Result := True;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.RemoveCanvas;
+begin
+ fDetachCanvas( @Self );
+ fCanvas.Free;
+ fCanvas := nil;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TBitmap.DIBPalNearestEntry(Color: TColor): Integer;
+var I, Diff, D: Integer;
+ C : Integer;
+begin
+ Color := TColor( Color2RGBQuad( Color ) );
+ Result := 0;
+ Diff := MaxInt;
+ for I := 0 to DIBPalEntryCount - 1 do
+ begin
+ C := Color xor PInteger( Integer( @fDIBHeader.bmiColors[ 0 ] )
+ + I * Sizeof( TRGBQuad ) )^;
+ D := TRGBQuad( C ).rgbBlue + TRGBQuad( C ).rgbGreen + TRGBQuad( C ).rgbRed;
+ if D < Diff then
+ begin
+ Diff := D;
+ Result := I;
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TBitmap.GetDIBPalEntries(Idx: Integer): TColor;
+begin
+ Result := TColor(-1);
+ if fDIBBits = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( PixelFormat in [pf1bit..pf8bit], 'Format has no DIB palette entries available' );
+ ASSERT( (Idx >= 0) and (Idx < (1 shl fDIBHeader.bmiHeader.biBitCount)),
+ 'DIB palette index out of bounds' );
+ {$ENDIF KOL_ASSERTIONS}
+ Result := PDWORD( Integer( @fDIBHeader.bmiColors[ 0 ] )
+ + Idx * Sizeof( TRGBQuad ) )^;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TBitmap.GetDIBPalEntryCount: Integer;
+begin
+ Result := 0;
+ if Empty then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ case PixelFormat of
+ pf1bit: Result := 2;
+ pf4bit: Result := 16;
+ pf8bit: Result := 256;
+ else;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TBitmap.SetDIBPalEntries(Idx: Integer; const Value: TColor);
+begin
+ if fDIBBits = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Dormant;
+ PDWORD( Integer( @fDIBHeader.bmiColors[ 0 ] )
+ + Idx * Sizeof( TRGBQuad ) )^ := Color2RGB( Value );
+end;
+
+procedure TBitmap.SetHandleType(const Value: TBitmapHandleType);
+begin
+ if fHandleType = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fHandleType := Value;
+ FormatChanged;
+end;
+
+function TBitmap.GetPixelFormat: TPixelFormat;
+begin
+ if (HandleType = bmDDB) or (fDIBBits = nil) then
+ Result := pfDevice
+ else
+ begin
+ Result := Bits2PixelFormat( fDIBHeader.bmiHeader.biBitCount );
+ if fDIBHeader.bmiHeader.biCompression <> 0 then
+ begin
+ {$IFDEF KOL_ASSERTIONS}
+ Assert( fDIBHeader.bmiHeader.biCompression = BI_BITFIELDS, 'Unsupported bitmap format' );
+ {$ENDIF KOL_ASSERTIONS}
+ if (TColor( fDIBHeader.bmiColors[ 0 ] ) = $F800) and
+ (PInteger( DWORD(@ fDIBHeader.bmiColors[ 0 ])+4 )^ = $7E0) and
+ (PInteger( DWORD(@ fDIBHeader.bmiColors[ 0 ])+8 )^ = $1F) then
+ Result := pf16bit
+ else
+ if (TColor( fDIBHeader.bmiColors[ 0 ] ) = $7C00) and
+ (PInteger( DWORD(@ fDIBHeader.bmiColors[ 0 ])+4 )^ = $3E0) and
+ (PInteger( DWORD(@ fDIBHeader.bmiColors[ 0 ])+8 )^ = $1F) then
+ Result := pf15bit
+ else
+ Result := pfCustom;
+ end;
+ end;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.ClearTransImage;
+begin
+ fTransColor := clNone;
+ fTransMaskBmp.Free;
+ fTransMaskBmp := nil;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+{$IFDEF USE_OLDCONVERT2MASK}
+procedure TBitmap.Convert2Mask(TranspColor: TColor);
+var MonoHandle: HBitmap;
+ SaveMono, SaveFrom: THandle;
+ MonoDC, DCfrom: HDC;
+ SaveBkColor: TColorRef;
+begin
+ if GetHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fDetachCanvas( @Self );
+ MonoHandle := CreateBitmap( fWidth, fHeight, 1, 1, nil );
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( MonoHandle <> 0, 'Can not create monochrome bitmap' );
+ {$ENDIF KOL_ASSERTIONS}
+ MonoDC := CreateCompatibleDC( 0 );
+ SaveMono := SelectObject( MonoDC, MonoHandle );
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( SaveMono <> 0, 'Can not select bitmap to DC' );
+ {$ENDIF KOL_ASSERTIONS}
+ DCfrom := CreateCompatibleDC( 0 );
+ SaveFrom := SelectObject( DCfrom, fHandle );
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( SaveFrom <> 0, 'Can not select source bitmap to DC' );
+ {$ENDIF KOL_ASSERTIONS}
+ TranspColor := Color2RGB( TranspColor );
+ SaveBkColor := Windows.SetBkColor( DCfrom, TranspColor );
+ BitBlt( MonoDC, 0, 0, fWidth, fHeight, DCfrom, 0, 0, SRCCOPY );
+ {$IFDEF CHK_BITBLT} Chk_BitBlt; {$ENDIF}
+ Windows.SetBkColor( DCfrom, SaveBkColor );
+ SelectObject( DCfrom, SaveFrom );
+ DeleteDC( DCfrom );
+ SelectObject( MonoDC, SaveMono );
+ DeleteDC( MonoDC );
+ ///ReleaseDC( 0, DC0 );
+ ClearData;
+ fHandle := MonoHandle;
+ fHandleType := bmDDB;
+end;
+{$ELSE NOT USE_OLDCONVERT2MASK} //Pascal
+procedure TBitmap.Convert2Mask(TranspColor: TColor);
+var Y, X, i: Integer;
+ Src, Dst: PByte;
+ W: Word;
+ TmpMsk: PBitmap;
+ B, C: Byte;
+ TranspColor32: TColor;
+begin
+ HandleType := bmDIB;
+ if PixelFormat < pf4bit then
+ PixelFormat := pf4bit;
+ if PixelFormat > pf32bit then
+ PixelFormat := pf32bit;
+ TranspColor := Color2RGB( TranspColor ) and $FFFFFF;
+ TranspColor32 := TColor( Color2RGBQuad( TranspColor ) );
+ TmpMsk := NewDIBBitmap( fWidth, fHeight, pf1bit );
+ TmpMsk.DIBPalEntries[ 1 ] := $FFFFFF;
+ for Y := 0 to fHeight-1 do
+ begin
+ Src := ScanLine[ Y ];
+ Dst := TmpMsk.ScanLine[ Y ];
+ B := 0; C := 8;
+ CASE PixelFormat OF
+ pf4bit:
+ begin
+ W := 16;
+ for i := 0 to 15 do
+ if DIBPalEntries[ i ] = TranspColor32 then
+ begin
+ W := i; break;
+ end;
+ for X := 0 to (fWidth div 2)-1 do
+ begin
+ B := B shl 1;
+ if Src^ shr 4 = W then inc( B );
+ B := B shl 1;
+ if Src^ and $0F = W then inc( B );
+ Inc( Src );
+ Dec( C, 2 );
+ if C = 0 then
+ begin
+ Dst^ := B;
+ Inc( Dst );
+ C := 8;
+ end;
+ end;
+ end;
+ pf8bit:
+ begin
+ W := 256;
+ for i := 0 to 255 do
+ if DIBPalEntries[ i ] = TranspColor32 then
+ begin
+ W := i; break;
+ end;
+ for X := 0 to fWidth-1 do
+ begin
+ B := B shl 1;
+ if Src^ = W then inc( B );
+ Inc( Src );
+ Dec( C );
+ if C = 0 then
+ begin
+ Dst^ := B;
+ Inc( Dst );
+ C := 8;
+ end;
+ end;
+ end;
+ pf15bit:
+ begin
+ W := Color2Color15( TranspColor );
+ for X := 0 to fWidth-1 do
+ begin
+ B := B shl 1;
+ if PWord( Src )^ = W then inc( B );
+ Inc( Src, 2 );
+ Dec( C );
+ if C = 0 then
+ begin
+ Dst^ := B;
+ Inc( Dst );
+ C := 8;
+ end;
+ end;
+ end;
+ pf16bit:
+ begin
+ W := Color2Color16( TranspColor );
+ for X := 0 to fWidth-1 do
+ begin
+ B := B shl 1;
+ if PWord( Src )^ = W then inc( B );
+ Inc( Src, 2 );
+ Dec( C );
+ if C = 0 then
+ begin
+ Dst^ := B;
+ Inc( Dst );
+ C := 8;
+ end;
+ end;
+ end;
+ pf24bit:
+ begin
+ for X := 0 to fWidth-1 do
+ begin
+ B := B shl 1;
+ if PInteger( Src )^ and $FFFFFF = TranspColor32 then inc( B );
+ Inc( Src, 3 );
+ Dec( C );
+ if C = 0 then
+ begin
+ Dst^ := B;
+ Inc( Dst );
+ C := 8;
+ end;
+ end;
+ end;
+ pf32bit:
+ begin
+ for X := 0 to fWidth-1 do
+ begin
+ B := B shl 1;
+ if PInteger( Src )^ and $FFFFFF = TranspColor32 then inc( B );
+ Inc( Src, 4 );
+ Dec( C );
+ if C = 0 then
+ begin
+ Dst^ := B;
+ Inc( Dst );
+ C := 8;
+ end;
+ end;
+ end;
+ END;
+ if (C > 0) and (C < 8) then
+ begin
+ while C > 0 do
+ begin
+ B := B shl 1;
+ dec( C );
+ end;
+ Dst^ := B;
+ end;
+ end;
+ Assign( TmpMsk );
+ TmpMsk.Free;
+end;
+{$ENDIF USE_OLDCONVERT2MASK} //Pascal
+{$ENDIF PAS_VERSION}
+
+procedure TBitmap.Invert;
+var R: TRect;
+begin
+ //BitBlt( Canvas.Handle, 0, 0, Width, Height, Canvas.Handle, 0, 0, DSTINVERT )
+ R := BoundsRect;
+ InvertRect(Canvas.Handle, R);
+end;
+
+procedure TBitmap.DIBDrawRect( DC: HDC; X, Y: Integer; const R: TRect );
+begin
+ if fDIBBits = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ StretchDIBits( DC, X, Y, R.Right - R.Left, R.Bottom - R.Top,
+ R.Left, fHeight - R.Bottom, R.Right - R.Left, R.Bottom - R.Top,
+ fDIBBits, fDIBHeader^, DIB_RGB_COLORS, SRCCOPY );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure _RotateBitmapMono( var DstBmp: PBitmap; SrcBmp: PBitmap );
+var X, Y, Z, Shf, Wbytes, BytesPerDstLine: Integer;
+ Src, Dst, Dst1: PByte;
+ Tmp: Byte;
+begin
+
+ DstBmp := NewDIBBitmap( SrcBmp.fHeight, (SrcBmp.fWidth + 7) and not 7, pf1bit );
+ Move( SrcBmp.fDIBHeader.bmiColors[ 0 ], DstBmp.fDIBHeader.bmiColors[ 0 ], 2 * Sizeof( TRGBQuad ) );
+
+ // Calculate ones:
+ Dst := DstBmp.ScanLine[ 0 ];
+ BytesPerDstLine := Integer( DstBmp.ScanLine[ 1 ]) - Integer( Dst );
+ Wbytes := (SrcBmp.fWidth + 7) shr 3;
+
+ Inc( Dst, (DstBmp.fWidth - 1) shr 3 );
+ Shf := (DstBmp.fWidth - 1) and 7;
+
+ // Rotating bits:
+ for Y := 0 to SrcBmp.fHeight - 1 do
+ begin
+ Src := SrcBmp.ScanLine[ Y ];
+ Dst1 := Dst;
+ for X := Wbytes downto 1 do
+ begin
+ Tmp := Src^;
+ Inc( Src );
+ for Z := 8 downto 1 do
+ begin
+ Dst1^ := Dst1^ or ( (Tmp and $80) shr Shf );
+ Tmp := Tmp shl 1;
+ Inc( Dst1, BytesPerDstLine );
+ end;
+ end;
+ Dec( Shf );
+ if Shf < 0 then
+ begin
+ Shf := 7;
+ Dec( Dst );
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure _RotateBitmap4bit( var DstBmp: PBitmap; SrcBmp: PBitmap );
+var X, Y, Shf, Wbytes, BytesPerDstLine: Integer;
+ Src, Dst, Dst1: PByte;
+ Tmp: Byte;
+begin
+ DstBmp := NewDIBBitmap( SrcBmp.fHeight, (SrcBmp.fWidth + 1) and not 1, pf4bit );
+ Move( SrcBmp.fDIBHeader.bmiColors[ 0 ], DstBmp.fDIBHeader.bmiColors[ 0 ], 16 * Sizeof( TRGBQuad ) );
+
+ // Calculate ones:
+ Dst := DstBmp.ScanLine[ 0 ];
+ BytesPerDstLine := Integer( DstBmp.ScanLine[ 1 ]) - Integer( Dst );
+ Wbytes := (SrcBmp.fWidth + 1) shr 1;
+ Inc( Dst, (DstBmp.fWidth - 1) shr 1 );
+ Shf := ((DstBmp.fWidth - 1) and 1) shl 2;
+
+ // Rotating bits:
+ for Y := 0 to SrcBmp.fHeight - 1 do
+ begin
+ Src := SrcBmp.ScanLine[ Y ];
+ Dst1 := Dst;
+ for X := Wbytes downto 1 do
+ begin
+ Tmp := Src^;
+ Inc( Src );
+ Dst1^ := Dst1^ or ( (Tmp and $F0) shr Shf );
+ Inc( Dst1, BytesPerDstLine );
+ Dst1^ := Dst1^ or ( ((Tmp shl 4) and $F0) shr Shf );
+ Inc( Dst1, BytesPerDstLine );
+ end;
+ Dec( Shf, 4 );
+ if Shf < 0 then
+ begin
+ Shf := 4;
+ Dec( Dst );
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure _RotateBitmap8bit( var DstBmp: PBitmap; SrcBmp: PBitmap );
+var X, Y, Wbytes, BytesPerDstLine: Integer;
+ Src, Dst, Dst1: PByte;
+ Tmp: Byte;
+begin
+
+ DstBmp := NewDIBBitmap( SrcBmp.fHeight, SrcBmp.fWidth, SrcBmp.PixelFormat );
+ Move( SrcBmp.fDIBHeader.bmiColors[ 0 ], DstBmp.fDIBHeader.bmiColors[ 0 ], 256 * Sizeof( TRGBQuad ) );
+
+ // Calculate ones:
+ Wbytes := SrcBmp.fWidth;
+ Dst := DstBmp.ScanLine[ 0 ];
+ BytesPerDstLine := Integer( DstBmp.ScanLine[ 1 ]) - Integer( Dst );
+
+ Inc( Dst, DstBmp.fWidth - 1 );
+
+ // Rotating bits:
+ for Y := 0 to SrcBmp.fHeight - 1 do
+ begin
+ Src := SrcBmp.ScanLine[ Y ];
+ Dst1 := Dst;
+ for X := Wbytes downto 1 do
+ begin
+ Tmp := Src^;
+ Inc( Src );
+ Dst1^ := Tmp;
+ Inc( Dst1, BytesPerDstLine );
+ end;
+ Dec( Dst );
+ end;
+
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure _RotateBitmap16bit( var DstBmp: PBitmap; SrcBmp: PBitmap );
+var X, Y, Wwords, BytesPerDstLine: Integer;
+ Src, Dst, Dst1: PWord;
+ Tmp: Word;
+begin
+ DstBmp := NewDIBBitmap( SrcBmp.fHeight, SrcBmp.fWidth, SrcBmp.PixelFormat );
+ Wwords := SrcBmp.fWidth;
+ Dst := DstBmp.ScanLine[ 0 ];
+ BytesPerDstLine := Integer( DstBmp.ScanLine[ 1 ]) - Integer( Dst );
+ Inc( Dst, DstBmp.fWidth - 1 );
+
+ // Rotating bits:
+ for Y := 0 to SrcBmp.fHeight - 1 do
+ begin
+ Src := SrcBmp.ScanLine[ Y ];
+ Dst1 := Dst;
+ for X := Wwords downto 1 do
+ begin
+ Tmp := Src^;
+ Inc( Src );
+ Dst1^ := Tmp;
+ Inc( PByte(Dst1), BytesPerDstLine );
+ end;
+ Dec( Dst );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure _RotateBitmap2432bit( var DstBmp: PBitmap; SrcBmp: PBitmap );
+var X, Y, Wwords, BytesPerDstLine, IncW: Integer;
+ Src, Dst, Dst1: PDWord;
+ Tmp: DWord;
+begin
+
+ DstBmp := NewDIBBitmap( SrcBmp.fHeight, SrcBmp.fWidth, SrcBmp.PixelFormat );
+
+ // Calculate ones:
+ IncW := 4;
+ if DstBmp.PixelFormat = pf24bit then
+ IncW := 3;
+ Wwords := SrcBmp.fWidth;
+ Dst := DstBmp.ScanLine[ 0 ];
+ BytesPerDstLine := Integer( DstBmp.ScanLine[ 1 ]) - Integer( Dst );
+
+ Inc( PByte(Dst), (DstBmp.fWidth - 1) * IncW );
+
+ // Rotating bits:
+ for Y := 0 to SrcBmp.fHeight - 1 do
+ begin
+ Src := SrcBmp.ScanLine[ Y ];
+ Dst1 := Dst;
+ for X := Wwords downto 1 do
+ begin
+ Tmp := Src^ and $FFFFFF;
+ Inc( PByte(Src), IncW );
+ Dst1^ := Dst1^ or Tmp;
+ Inc( PByte(Dst1), BytesPerDstLine );
+ end;
+ Dec( PByte(Dst), IncW );
+ end;
+
+end;
+{$ENDIF PAS_VERSION}
+
+type
+ TRotateBmpRefs = packed record
+ proc_RotateBitmapMono: procedure( var Dst: PBitmap; Src: PBitmap );
+ proc_RotateBitmap4bit: procedure( var Dst: PBitmap; Src: PBitmap );
+ proc_RotateBitmap8bit: procedure( var Dst: PBitmap; Src: PBitmap );
+ proc_RotateBitmap16bit: procedure( var Dst: PBitmap; Src: PBitmap );
+ proc_RotateBitmap2432bit: procedure( var Dst: PBitmap; Src: PBitmap );
+ end;
+
+var
+ RotateProcs: TRotateBmpRefs;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure _RotateBitmapRight( SrcBmp: PBitmap );
+var DstBmp: PBitmap;
+ RotateProc: procedure( var DstBmp: PBitmap; SrcBmp: PBitmap );
+begin
+ if SrcBmp.fHandleType <> bmDIB then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ case SrcBmp.PixelFormat of
+ pf1bit: RotateProc := RotateProcs.proc_RotateBitmapMono;
+ pf4bit: RotateProc := RotateProcs.proc_RotateBitmap4bit;
+ pf8bit: RotateProc := RotateProcs.proc_RotateBitmap8bit;
+ pf15bit, pf16bit: RotateProc := RotateProcs.proc_RotateBitmap16bit;
+ else RotateProc := RotateProcs.proc_RotateBitmap2432bit;
+ end;
+ if not Assigned( RotateProc ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ RotateProc( DstBmp, SrcBmp );
+ if DstBmp.fHeight > SrcBmp.fWidth then
+ begin
+ DstBmp.fDIBSize := DstBmp.fScanLineSize * SrcBmp.fWidth;
+ if DstBmp.fDIBHeader.bmiHeader.biHeight > 0 then
+ Move( DstBmp.ScanLine[ SrcBmp.fWidth - 1 ]^, DstBmp.ScanLine[ DstBmp.fHeight - 1 ]^,
+ DstBmp.fDIBSize );
+ DstBmp.fHeight := SrcBmp.fWidth;
+ DstBmp.fDIBHeader.bmiHeader.biHeight := DstBmp.fHeight;
+ end;
+
+ SrcBmp.ClearData;
+
+ SrcBmp.fDIBHeader := DstBmp.fDIBHeader;
+ DstBmp.fDIBHeader := nil;
+
+ SrcBmp.fDIBBits := DstBmp.fDIBBits;
+ DstBmp.fDIBBits := nil;
+ SrcBmp.fDIBAutoFree := DstBmp.fDIBAutoFree;
+
+ SrcBmp.fDIBSize := DstBmp.fDIBSize;
+
+ SrcBmp.fWidth := DstBmp.fWidth;
+ SrcBmp.fHeight := DstBmp.fHeight;
+ DstBmp.Free;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TBitmap.RotateRight;
+const AllRotators: TRotateBmpRefs = (
+ proc_RotateBitmapMono: _RotateBitmapMono;
+ proc_RotateBitmap4bit: _RotateBitmap4bit;
+ proc_RotateBitmap8bit: _RotateBitmap8bit;
+ proc_RotateBitmap16bit: _RotateBitmap16bit;
+ proc_RotateBitmap2432bit: _RotateBitmap2432bit );
+begin
+ RotateProcs := AllRotators;
+ _RotateBitmapRight( @Self );
+end;
+
+procedure _RotateBitmapLeft( Src: PBitmap );
+begin
+ _RotateBitmapRight( Src );
+ _RotateBitmapRight( Src );
+ _RotateBitmapRight( Src );
+end;
+
+procedure TBitmap.RotateLeft;
+begin
+ RotateRight;
+ _RotateBitmapRight( @Self );
+ _RotateBitmapRight( @Self );
+end;
+
+procedure TBitmap.RotateLeftMono;
+begin
+ if PixelFormat <> pf1bit then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ RotateProcs.proc_RotateBitmapMono := _RotateBitmapMono;
+ _RotateBitmapRight( @Self );
+end;
+
+procedure TBitmap.RotateRightMono;
+begin
+ if PixelFormat <> pf1bit then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ RotateProcs.proc_RotateBitmapMono := _RotateBitmapMono;
+ _RotateBitmapLeft( @Self );
+end;
+
+procedure TBitmap.RotateLeft16bit;
+begin
+ if PixelFormat <> pf16bit then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ RotateProcs.proc_RotateBitmap16bit := _RotateBitmap16bit;
+ _RotateBitmapLeft( @Self );
+end;
+
+procedure TBitmap.RotateLeft4bit;
+begin
+ if PixelFormat <> pf4bit then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ RotateProcs.proc_RotateBitmap4bit := _RotateBitmap4bit;
+ _RotateBitmapLeft( @Self );
+end;
+
+procedure TBitmap.RotateLeft8bit;
+begin
+ if PixelFormat <> pf8bit then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ RotateProcs.proc_RotateBitmap8bit := _RotateBitmap8bit;
+ _RotateBitmapLeft( @Self );
+end;
+
+procedure TBitmap.RotateLeftTrueColor;
+begin
+ if not (PixelFormat in [ pf24bit, pf32bit ]) then Exit; {>>>>>>>>>>>>>>>>>>>>}
+ RotateProcs.proc_RotateBitmap2432bit := _RotateBitmap2432bit;
+ _RotateBitmapLeft( @Self );
+end;
+
+procedure TBitmap.RotateRight16bit;
+begin
+ if PixelFormat <> pf16bit then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ RotateProcs.proc_RotateBitmap16bit := _RotateBitmap16bit;
+ _RotateBitmapRight( @Self );
+end;
+
+procedure TBitmap.RotateRight4bit;
+begin
+ if PixelFormat <> pf4bit then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ RotateProcs.proc_RotateBitmap4bit := _RotateBitmap4bit;
+ _RotateBitmapRight( @Self );
+end;
+
+procedure TBitmap.RotateRight8bit;
+begin
+ if PixelFormat <> pf8bit then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ RotateProcs.proc_RotateBitmap8bit := _RotateBitmap8bit;
+ _RotateBitmapRight( @Self );
+end;
+
+procedure TBitmap.RotateRightTrueColor;
+begin
+ if not (PixelFormat in [ pf24bit, pf32bit ]) then Exit; {>>>>>>>>>>>>>>>>>>>>}
+ RotateProcs.proc_RotateBitmap2432bit := _RotateBitmap2432bit;
+ _RotateBitmapRight( @Self );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TBitmap.GetPixels(X, Y: Integer): TColor;
+var DC: HDC;
+ Save: THandle;
+begin
+ Result := clNone;
+ if Empty then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fDetachCanvas( @Self );
+ DC := CreateCompatibleDC( 0 );
+ Save := SelectObject( DC, GetHandle );
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( Save <> 0, 'Can not select bitmap to DC' );
+ {$ENDIF KOL_ASSERTIONS}
+ Result := Windows.GetPixel( DC, X, Y );
+ SelectObject( DC, Save );
+ DeleteDC( DC );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.SetPixels(X, Y: Integer; const Value: TColor);
+var DC: HDC;
+ Save: THandle;
+begin
+ if Empty then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fDetachCanvas( @Self );
+ DC := CreateCompatibleDC( 0 );
+ Save := SelectObject( DC, GetHandle );
+ {$IFDEF KOL_ASSERTIONS}
+ ASSERT( Save <> 0, 'Can not select bitmap to DC' );
+ {$ENDIF KOL_ASSERTIONS}
+ Windows.SetPixel( DC, X, Y, Color2RGB( Value ) );
+ SelectObject( DC, Save );
+ DeleteDC( DC );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function _GetDIBPixelsPalIdx( Bmp: PBitmap; X, Y: Integer ): TColor;
+var Pixel: Byte;
+begin
+ Pixel := PByte( Integer(Bmp.fScanLine0) + Y * Bmp.fScanLineDelta
+ + (X div (Bmp.fPixelsPerByteMask + 1)) )^;
+ Pixel := ( Pixel shr ( (Bmp.fPixelsPerByteMask - (X and Bmp.fPixelsPerByteMask))
+ * Bmp.fDIBHeader.bmiHeader.biBitCount ) )
+ and Bmp.fPixelMask;
+ Result := TColor( Color2RGBQuad( TColor( PRGBQuad( DWORD(@Bmp.fDIBHeader.bmiColors[ 0 ])
+ + Pixel * Sizeof( TRGBQuad ) )^ ) ) );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function _GetDIBPixels16bit( Bmp: PBitmap; X, Y: Integer ): TColor;
+var Pixel: Word;
+begin
+ Pixel := PWord( Integer(Bmp.fScanLine0) + Y * Bmp.fScanLineDelta + X * 2 )^;
+ if Bmp.fPixelMask = 15 then
+ Result := (Pixel shr 7) and $F8 or (Pixel shl 6) and $F800
+ or (Pixel shl 19) and $F80000
+ else
+ Result := (Pixel shr 8) and $F8 or (Pixel shl 5) and $FC00
+ or (Pixel shl 19) and $F80000;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function _GetDIBPixelsTrueColor( Bmp: PBitmap; X, Y: Integer ): TColor;
+var Pixel: DWORD;
+begin
+ Pixel := PDWORD( Integer(Bmp.fScanLine0) + Y * Bmp.fScanLineDelta +
+ X * Bmp.fBytesPerPixel )^ and $FFFFFF;
+ Result := TColor( Color2RGBQuad( TColor( Pixel ) ) );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+function _GetDIBPixelsTrueColorAlpha( Bmp: PBitmap; X, Y: Integer ): TColor;
+var Pixel: DWORD;
+ RGB: TRGBQuad;
+ blue, red: Byte;
+begin
+ Pixel := PDWORD( Integer(Bmp.fScanLine0) + Y * Bmp.fScanLineDelta +
+ X * Bmp.fBytesPerPixel )^;
+ RGB := TRGBQuad(Pixel);
+ blue := RGB.rgbRed;
+ red := RGB.rgbBlue;
+ RGB.rgbBlue := blue;
+ RGB.rgbRed := red;
+ Result := TColor( RGB );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TBitmap.GetDIBPixels(X, Y: Integer): TColor;
+begin
+ if not Assigned( fGetDIBPixels ) then
+ begin
+ if fHandleType = bmDIB then
+ begin
+ fScanLine0 := ScanLine[ 0 ];
+ fScanLineDelta := Integer(ScanLine[ 1 ]) - Integer(fScanLine0);
+ case PixelFormat of
+ pf1bit:
+ begin
+ fPixelMask := $01;
+ fPixelsPerByteMask := 7;
+ fGetDIBPixels := _GetDIBPixelsPalIdx;
+ end;
+ pf4bit:
+ begin
+ fPixelMask := $0F;
+ fPixelsPerByteMask := 1;
+ fGetDIBPixels := _GetDIBPixelsPalIdx;
+ end;
+ pf8bit:
+ begin
+ fPixelMask := $FF;
+ fPixelsPerByteMask := 0;
+ fGetDIBPixels := _GetDIBPixelsPalIdx;
+ end;
+ pf15bit:
+ begin
+ fPixelMask := 15;
+ fGetDIBPixels := _GetDIBPixels16bit;
+ end;
+ pf16bit:
+ begin
+ fPixelMask := 16;
+ fGetDIBPixels := _GetDIBPixels16bit;
+ end;
+ pf24bit:
+ begin
+ fPixelsPerByteMask := 0;
+ fBytesPerPixel := 3;
+ fGetDIBPixels := _GetDIBPixelsTrueColor;
+ end;
+ pf32bit:
+ begin
+ fPixelsPerByteMask := 1;
+ fBytesPerPixel := 4;
+ fGetDIBPixels := {$IFDEF DIBPixels32bitWithAlpha} _GetDIBPixelsTrueColorAlpha
+ {$ELSE} _GetDIBPixelsTrueColor {$ENDIF};
+ end;
+ else;
+ end;
+ end;
+ if not Assigned( fGetDIBPixels ) then
+ begin
+ Result := Pixels[ X, Y ]; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ Result := fGetDIBPixels( @Self, X, Y );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure _SetDIBPixels1bit( Bmp: PBitmap; X, Y: Integer; Value: TColor );
+var Pixel: Byte;
+ Pos: PByte;
+ Shf: Integer;
+begin
+ Value := Color2RGB( Value );
+ if ((Value shr 16) and $FF) + ((Value shr 8) and $FF) + (Value and $FF)
+ < 255 * 3 div 2 then Pixel := 0 else Pixel := $80;
+ Pos := PByte( Integer(Bmp.fScanLine0) + Y * Bmp.fScanLineDelta + X div 8 );
+ Shf := X and 7;
+ Pos^ := Pos^ and ($FF7F shr Shf) or (Pixel shr Shf);
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure _SetDIBPixelsPalIdx( Bmp: PBitmap; X, Y: Integer; Value: TColor );
+var Pixel: Byte;
+ Pos: PByte;
+ Shf: Integer;
+begin
+ Pixel := Bmp.DIBPalNearestEntry( Value );
+ Pos := PByte( Integer(Bmp.fScanLine0) + Y * Bmp.fScanLineDelta
+ + X div (Bmp.fPixelsPerByteMask + 1) );
+ Shf := (Bmp.fPixelsPerByteMask - (X and Bmp.fPixelsPerByteMask))
+ * Bmp.fDIBHeader.bmiHeader.biBitCount;
+ Pos^ := Pos^ and not (Bmp.fPixelMask shl Shf) or (Pixel shl Shf);
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure _SetDIBPixels16bit( Bmp: PBitmap; X, Y: Integer; Value: TColor );
+var RGB16: Word;
+ Pos: PWord;
+begin
+ Value := Color2RGB( Value );
+ if Bmp.fPixelMask = 15 then
+ RGB16 := (Value shr 19) and $001F or (Value shr 6) and $03E0
+ or (Value shl 7) and $7C00
+ else
+ RGB16 := (Value shr 19) and $001F or (Value shr 5) and $07E0
+ or (Value shl 8) and $F800;
+ Pos := PWord( Integer(Bmp.fScanLine0) + Y * Bmp.fScanLineDelta + X * 2 );
+ Pos^ := RGB16;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure _SetDIBPixelsTrueColor( Bmp: PBitmap; X, Y: Integer; Value: TColor );
+var RGB: TRGBQuad;
+ Pos: PDWord;
+begin
+ RGB := Color2RGBQuad( Value );
+ Pos := PDWORD( Integer(Bmp.fScanLine0) + Y * Bmp.fScanLineDelta
+ + X * Bmp.fBytesPerPixel );
+ Pos^ := Pos^ and $FF000000 or DWORD(RGB);
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+procedure _SetDIBPixelsTrueColorAlpha( Bmp: PBitmap; X, Y: Integer; Value: TColor );
+var RGB: TRGBQuad;
+ Pos: PDWord;
+ blue, red: Byte;
+begin
+ RGB := TRGBQuad(Value);
+ blue := RGB.rgbRed;
+ red := RGB.rgbBlue;
+ RGB.rgbBlue := blue;
+ RGB.rgbRed := red;
+ Pos := PDWORD( Integer(Bmp.fScanLine0) + Y * Bmp.fScanLineDelta
+ + X * Bmp.fBytesPerPixel );
+ Pos^ := Pos^ or DWORD(RGB);
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.SetDIBPixels(X, Y: Integer; const Value: TColor);
+begin
+ if not Assigned( fSetDIBPixels ) then
+ begin
+ if fHandleType = bmDIB then
+ begin
+ fScanLine0 := ScanLine[ 0 ];
+ fScanLineDelta := Integer(ScanLine[ 1 ]) - Integer(fScanLine0);
+ case PixelFormat of
+ pf1bit:
+ begin
+ //fPixelMask := $01;
+ //fPixelsPerByteMask := 7;
+ fSetDIBPixels := _SetDIBPixels1bit;
+ end;
+ pf4bit:
+ begin
+ fPixelMask := $0F;
+ fPixelsPerByteMask := 1;
+ fSetDIBPixels := _SetDIBPixelsPalIdx;
+ end;
+ pf8bit:
+ begin
+ fPixelMask := $FF;
+ fPixelsPerByteMask := 0;
+ fSetDIBPixels := _SetDIBPixelsPalIdx;
+ end;
+ pf15bit:
+ begin
+ fPixelMask := 15;
+ fSetDIBPixels := _SetDIBPixels16bit;
+ end;
+ pf16bit:
+ begin
+ fPixelMask := 16;
+ fSetDIBPixels := _SetDIBPixels16bit;
+ end;
+ pf24bit:
+ begin
+ fPixelsPerByteMask := 0;
+ fBytesPerPixel := 3;
+ fSetDIBPixels := _SetDIBPixelsTrueColor;
+ end;
+ pf32bit:
+ begin
+ fPixelsPerByteMask := 1;
+ fBytesPerPixel := 4;
+ fSetDIBPixels := {$IFDEF DIBPixels32bitWithAlpha} _SetDIBPixelsTrueColorAlpha
+ {$ELSE} _SetDIBPixelsTrueColor {$ENDIF};
+ end;
+ else;
+ end;
+ end;
+ if not Assigned( fSetDIBPixels ) then
+ begin
+ Pixels[ X, Y ] := Value; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ fSetDIBPixels( @Self, X, Y, Value );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.FlipVertical;
+var DC: HDC;
+ Save: THandle;
+ TmpScan: PByte;
+ Y: Integer;
+begin
+ if fHandle <> 0 then
+ begin
+ fDetachCanvas( @Self );
+ DC := CreateCompatibleDC( 0 );
+ Save := SelectObject( DC, fHandle );
+ StretchBlt( DC, 0, fHeight - 1, fWidth, -fHeight, DC, 0, 0, fWidth, fHeight, SRCCOPY );
+ SelectObject( DC, Save );
+ DeleteDC( DC );
+ end
+ else
+ if fDIBBits <> nil then
+ begin
+ GetMem( TmpScan, ScanLineSize );
+ for Y := 0 to fHeight div 2-1 do
+ begin
+ Move( ScanLine[ Y ]^, TmpScan^, fScanLineSize );
+ Move( ScanLine[ fHeight - Y - 1 ]^, ScanLine[ Y ]^, fScanLineSize );
+ Move( TmpScan^, ScanLine[ fHeight - Y - 1 ]^, fScanLineSize );
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.FlipHorizontal;
+var DC: HDC;
+ Save: THandle;
+begin
+ if GetHandle <> 0 then
+ begin
+ fDetachCanvas( @Self );
+ DC := CreateCompatibleDC( 0 );
+ Save := SelectObject( DC, fHandle );
+ StretchBlt( DC, fWidth - 1, 0, -fWidth, fHeight, DC, 0, 0, fWidth, fHeight, SRCCOPY );
+ SelectObject( DC, Save );
+ DeleteDC( DC );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TBitmap.CopyRect(const DstRect: TRect; SrcBmp: PBitmap;
+ const SrcRect: TRect);
+var DCsrc, DCdst: HDC;
+ SaveSrc, SaveDst: THandle;
+begin
+ if (GetHandle = 0) or (SrcBmp.GetHandle = 0) then Exit; {>>>>>>>>>>>>>>>>>>>>}
+ fDetachCanvas( @Self );
+ SrcBmp.fDetachCanvas( SrcBmp );
+ DCsrc := CreateCompatibleDC( 0 );
+ SaveSrc := SelectObject( DCsrc, SrcBmp.fHandle );
+ DCdst := DCsrc;
+ SaveDst := 0;
+ if SrcBmp <> @Self then
+ begin
+ DCdst := CreateCompatibleDC( 0 );
+ SaveDst := SelectObject( DCdst, fHandle );
+ end;
+ StretchBlt( DCdst, DstRect.Left, DstRect.Top, DstRect.Right - DstRect.Left,
+ DstRect.Bottom - DstRect.Top, DCsrc, SrcRect.Left, SrcRect.Top,
+ SrcRect.Right - SrcRect.Left, SrcRect.Bottom - SrcRect.Top,
+ SRCCOPY );
+ if SrcBmp <> @Self then
+ begin
+ SelectObject( DCdst, SaveDst );
+ DeleteDC( DCdst );
+ end;
+ SelectObject( DCsrc, SaveSrc );
+ DeleteDC( DCsrc );
+end;
+{$ENDIF PAS_VERSION}
+
+function TBitmap.CopyToClipboard: Boolean;
+var DibMem: PAnsiChar;
+ HdrSize: Integer;
+ Gbl: HGlobal;
+ //Mem: PStream;
+ //Sz: Integer;
+ //Pt: Pointer;
+ Restore_Compression: Integer;
+begin
+ Result := FALSE;
+ if Applet = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if not OpenClipboard( Applet.GetWindowHandle ) then Exit; {>>>>>>>>>>>>>>>>>>}
+ if EmptyClipboard then
+ begin
+ HandleType := bmDIB;
+ HdrSize := sizeof( TBitmapInfoHeader );
+ Restore_Compression := -1;
+ TRY
+ if fDIBHeader.bmiHeader.biBitCount <= 8 then
+ Inc( HdrSize,
+ (1 shl fDIBHeader.bmiHeader.biBitCount) * Sizeof( TRGBQuad ) )
+ else
+ begin
+ if fDIBHeader.bmiHeader.biCompression = BI_RGB then
+ begin
+ CASE fDIBHeader.bmiHeader.biBitCount OF
+ {24,} 32:
+ begin
+ Restore_Compression := fDIBHeader.bmiHeader.biCompression;
+ fDIBHeader.bmiHeader.biCompression := BI_BITFIELDS;
+ PDWORD( @ fDIBHeader.bmiColors[ 0 ] )^ := $FF0000;
+ PDWORD( Integer( @ fDIBHeader.bmiColors[ 0 ] ) + 4 )^ := $FF00;
+ PDWORD( Integer( @ fDIBHeader.bmiColors[ 0 ] ) + 8 )^ := $FF;
+ Inc( HdrSize, 12 );
+ end;
+ END;
+ end;
+ end;
+ Gbl := GlobalAlloc( GMEM_MOVEABLE, HdrSize + fDIBSize );
+ DibMem := GlobalLock( Gbl );
+ if DibMem <> nil then
+ begin
+ Move( fDIBHeader^, DibMem^, HdrSize );
+ Move( fDIBBits^, Pointer( Integer( DibMem ) + HdrSize )^, fDIBSize );
+ if not GlobalUnlock( Gbl ) and (GetLastError = NO_ERROR) then
+ begin
+ Result := SetClipboardData( CF_DIB, Gbl ) <> 0;
+ end;
+ end;
+ FINALLY
+ if Restore_Compression >= 0 then
+ fDIBHeader.bmiHeader.biCompression := Restore_Compression;
+ END;
+
+ end;
+ CloseClipboard;
+end;
+
+function TBitmap.PasteFromClipboard: Boolean;
+var Gbl: HGlobal;
+ Size {, HdrSize}: Integer;
+ Mem: PAnsiChar;
+ Strm: PStream;
+begin
+ Result := FALSE;
+ if Applet = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if not OpenClipboard( Applet.GetWindowHandle ) then Exit; {>>>>>>>>>>>>>>>>>>}
+ TRY
+ if IsClipboardFormatAvailable( CF_DIB ) then
+ begin
+ Gbl := GetClipboardData( CF_DIB );
+ if Gbl <> 0 then
+ begin
+ Size := GlobalSize( Gbl );
+ Mem := GlobalLock( Gbl );
+ TRY
+ if (Size > 0) and (Mem <> nil) then
+ begin
+ Strm := NewMemoryStream;
+ Strm.Write( Mem^, Size );
+ Strm.Position := 0;
+ LoadFromStreamEx( Strm );
+ Strm.Free;
+ Result := not Empty;
+ end;
+ FINALLY
+ GlobalUnlock( Gbl );
+ END;
+ end;
+ end;
+ FINALLY
+ CloseClipboard;
+ END;
+end;
+
+///////////////////////////////////////////////////////////////////////
+// I C O N
+///////////////////////////////////////////////////////////////////////
+
+{ -- icon -- }
+
+function NewIcon: PIcon;
+begin
+ New( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TIcon';
+ {$ENDIF}
+ {$IFDEF ICON_DIFF_WH}
+ Result.FWidth := 32;
+ Result.FHeight := 32;
+ {$ELSE}
+ Result.FSize := 32;
+ {$ENDIF}
+end;
+
+{ TIcon }
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TIcon.Clear;
+begin
+ if fHandle <> 0 then
+ begin
+ if not FShareIcon then
+ DestroyIcon( fHandle );
+ fHandle := 0;
+ end;
+ fShareIcon := False;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_LOCAL}
+ {$UNDEF ASM_LOCAL}
+{$ENDIF}
+
+{$IFNDEF ICON_DIFF_WH}
+ {$IFDEF ASM_VERSION} {$DEFINE ASM_LOCAL} {$ENDIF}
+{$ENDIF}
+
+{$IFDEF ASM_LOCAL}
+{$ELSE PAS_VERSION} //Pascal
+function TIcon.Convert2Bitmap(TranColor: TColor): HBitmap;
+var DC0, DC2: HDC;
+ Save: THandle;
+ Br: HBrush;
+begin
+ Result := 0;
+ if Empty then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ DC0 := GetDC( 0 );
+ DC2 := CreateCompatibleDC( DC0 );
+ {$IFDEF ICON_DIFF_WH}
+ Result := CreateCompatibleBitmap( DC0, fWidth, fHeight );
+ {$ELSE}
+ Result := CreateCompatibleBitmap( DC0, fSize, fSize );
+ {$ENDIF}
+ Save := SelectObject( DC2, Result );
+ Br := CreateSolidBrush( Color2RGB( TranColor ) );
+ {$IFDEF ICON_DIFF_WH}
+ FillRect( DC2, MakeRect( 0, 0, fWidth, fHeight ), Br );
+ {$ELSE}
+ FillRect( DC2, MakeRect( 0, 0, fSize, fSize ), Br );
+ {$ENDIF}
+ DeleteObject( Br );
+ Draw( DC2, 0, 0 );
+ SelectObject( DC2, Save );
+ DeleteDC( DC2 );
+ ReleaseDC( 0, DC0 );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+destructor TIcon.Destroy;
+begin
+ Clear;
+ inherited;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TIcon.Draw(DC: HDC; X, Y: Integer);
+begin
+ if Empty then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$IFDEF ICON_DIFF_WH}
+ DrawIconEx( DC, X, Y, fHandle, fWidth, fHeight, 0, 0, DI_NORMAL );
+ {$ELSE}
+ DrawIconEx( DC, X, Y, fHandle, fSize, fSize, 0, 0, DI_NORMAL );
+ {$ENDIF}
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TIcon.StretchDraw(DC: HDC; Dest: TRect);
+begin
+ if Empty then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ DrawIconEx( DC, Dest.Left, Dest.Top, FHandle, Dest.Right - Dest.Left,
+ Dest.Bottom - Dest.Top, 0, 0, DI_NORMAL );
+end;
+{$ENDIF PAS_VERSION}
+
+function TIcon.GetEmpty: Boolean;
+begin
+ Result := (fHandle = 0)
+ {$IFDEF ICONLOAD_PRESERVEBMPS}
+ and ((ImgBmp = nil) or ImgBmp.Empty)
+ {$ENDIF ICONLOAD_PRESERVEBMPS}
+ ;
+end;
+
+function TIcon.GetHotSpot: TPoint;
+var II : TIconInfo;
+begin
+ Result := MakePoint( 0, 0 );
+ if FHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ GetIconInfo( FHandle, II );
+ Result.x := II.xHotspot;
+ Result.y := II.yHotspot;
+ if II.hbmMask <> 0 then
+ DeleteObject( II.hbmMask );
+ if II.hbmColor <> 0 then
+ DeleteObject( II.hbmColor );
+end;
+
+procedure TIcon.LoadFromFile(const FileName: KOLString);
+var Strm : PStream;
+begin
+ Strm := NewReadFileStream( Filename );
+ LoadFromStream( Strm );
+ Strm.Free;
+end;
+
+procedure TIcon.LoadFromStream(Strm: PStream);
+var DesiredSize : Integer;
+ Pos : DWord;
+ Mem : PStream;
+ {$IFNDEF ICONLOAD_PRESERVEBMPS}
+ ImgBmp, MskBmp : PBitmap;
+ {$ENDIF ICONLOAD_PRESERVEBMPS}
+ TmpBmp: PBitmap;
+ function ReadIcon : Boolean;
+ var IH : TIconHeader;
+ IDI, FoundIDI : TIconDirEntry;
+ I, J, SumSz, FoundSz, D : Integer;
+ II : TIconInfo;
+ BIH : TBitmapInfoheader;
+ SzImg: DWORD;
+ begin
+ Result := False;
+ if Strm.Read( IH, Sizeof( IH ) ) <> Sizeof( IH ) then Exit; {>>>>>>>>>>>>>}
+ if (IH.idReserved = Sizeof( TBitmapInfoHeader )) then
+ begin
+ Strm.Position := Strm.Position - Sizeof( IH );
+ {$IFDEF ICON_DIFF_WH} fWidth := 0;
+ fHeight := 0;
+ {$ELSE} fSize := 0;
+ {$ENDIF}
+ SumSz := 0;
+ end
+ else
+ if (IH.idReserved = 0) and ((IH.idType = 1) or (IH.idType = 2)) and
+ (IH.idCount >= 1) then
+ begin
+ if (IH.idReserved <> 0) or ((IH.idType <> 1) and (IH.idType <> 2)) or
+ (IH.idCount < 1) or (IH.idCount >= 1024) then Exit; {>>>>>>>>>>>>>>>>}
+ SumSz := Sizeof( IH );
+ FoundSz := 1000000;
+ for I := 1 to IH.idCount do
+ begin
+ if Strm.Read( IDI, Sizeof( IDI ) ) <> Sizeof( IDI ) then Exit; {>>>>>}
+ Inc( SumSz, IDI.dwBytesInRes + Sizeof( IDI ) );
+ D := IDI.bWidth - DesiredSize;
+ if D < 0 then D := -D;
+ if D < FoundSz then
+ begin
+ FoundSz := D;
+ FoundIDI := IDI;
+ end;
+ end;
+ if FoundSz = 1000000 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Strm.Position := Integer( Pos ) + FoundIDI.dwImageOffset;
+ {$IFDEF ICON_DIFF_WH} fWidth := FoundIDI.bWidth;
+ fHeight := FoundIDI.bHeight;
+ {$ELSE} fSize := FoundIDI.bWidth;
+ {$ENDIF}
+ end else Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if Strm.Read( BIH, Sizeof( BIH ) ) <> Sizeof( BIH ) then Exit; {>>>>>>>>>>}
+ {$IFDEF ICON_DIFF_WH}
+ fWidth := BIH.biWidth;
+ BIH.biHeight := BIH.biHeight div 2; // fSize;
+ fHeight := BIH.biHeight;
+ {$ELSE}
+ fSize := BIH.biWidth;
+ BIH.biHeight := BIH.biHeight div 2; // fSize;
+ {$ENDIF}
+ Mem := NewMemoryStream;
+ if (FoundIDI.bColorCount >= 2) or (FoundIDI.bReserved = 1) or
+ (FoundIDI.bColorCount = 0) then
+ begin
+ I := 0;
+ SzImg := ((BIH.biBitCount * BIH.biWidth + 31) div 32) * 4 * BIH.biHeight;
+ if (BIH.biSizeImage > 0) and (SzImg > BIH.biSizeImage) then
+ SzImg := BIH.biSizeImage;
+ if BIH.biBitCount <= 8 then
+ begin
+ I := (1 shl BIH.biBitCount) * Sizeof( TRGBQuad );
+ end;
+ Mem.Write( BIH, Sizeof( BIH ) );
+ if I > 0 then
+ begin
+ if Stream2Stream( Mem, Strm, I ) <> DWORD(I) then Exit; {>>>>>>>>>>>>}
+ end
+ else
+ if BIH.biBitCount = 16 then
+ begin
+ if BIH.biCompression = BI_BITFIELDS then // + by mdw - fix for
+ Stream2Stream(Mem, Strm, 12) // 16 bit per pixels
+ else
+ for I := 0 to 2 do
+ begin
+ J := InitColors[ I ];
+ Mem.Write( J, 4 );
+ end;
+ end;
+ I := Stream2Stream( Mem, Strm, SzImg );
+ if I <> Integer( SzImg ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$IFDEF ICON_DIFF_WH}
+ ImgBmp := NewBitmap( fWidth, fHeight );
+ {$ELSE}
+ ImgBmp := NewBitmap( fSize, fSize );
+ {$ENDIF}
+ {$IFDEF ICONLOAD_PRESERVEBMPS}
+ Add2AutoFree( ImgBmp );
+ {$ENDIF ICONLOAD_PRESERVEBMPS}
+ Mem.Seek( 0, spBegin );
+ {$IFDEF LOADEX}
+ ImgBmp.LoadFromStreamEx( Mem );
+ {$ELSE}
+ ImgBmp.LoadFromStream( Mem );
+ {$ENDIF}
+ if ImgBmp.Empty then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end
+ else
+ begin
+ Mem.Write( BIH, Sizeof( BIH ) );
+ end;
+
+ BIH.biBitCount := 1;
+ BIH.biPlanes := 1;
+ BIH.biClrUsed := 0;
+ BIH.biCompression := 0;
+ Mem.Seek( 0, spBegin );
+ BIH.biSizeImage := ((BIH.biWidth + 31) div 32) * 4 * BIH.biHeight;
+ Mem.Write( BIH, Sizeof( BIH ) );
+ I := 0;
+ Mem.Write( I, Sizeof( I ) );
+ I := $FFFFFF;
+ Mem.Write( I, Sizeof( I ) );
+ I := BIH.biSizeImage;
+ J := Stream2Stream( Mem, Strm, I );
+ while J < I do
+ begin
+ D := 0;
+ Mem.Write( D, 4 );
+ Inc( J, 4 );
+ end;
+
+ {$IFDEF ICON_DIFF_WH}
+ MskBmp := NewBitmap( fWidth, fHeight );
+ {$ELSE}
+ MskBmp := NewBitmap( fSize, fSize );
+ {$ENDIF}
+ {$IFDEF ICONLOAD_PRESERVEBMPS}
+ Add2AutoFree( MskBmp );
+ {$ENDIF ICONLOAD_PRESERVEBMPS}
+ Mem.Seek( 0, spBegin );
+ {$IFDEF LOADEX}
+ MskBmp.LoadFromStreamEx( Mem );
+ {$ELSE}
+ MskBmp.LoadFromStream( Mem );
+ {$ENDIF}
+
+ {$IFDEF ICONLOAD_PRESERVEBMPS}
+ Result := TRUE;
+ if not Only_Bmp then
+ {$ENDIF ICONLOAD_PRESERVEBMPS}
+ begin
+ II.fIcon := True;
+ II.xHotspot := 0;
+ II.yHotspot := 0;
+ II.hbmMask := 0;
+ if Assigned( MskBmp ) and not MskBmp.Empty then
+ II.hbmMask := MskBmp.Handle;
+ II.hbmColor := 0;
+ if ImgBmp <> nil then
+ II.hbmColor := ImgBmp.Handle;
+ fHandle := CreateIconIndirect( II );
+ if SumSz > 0 then
+ Strm.Seek( Integer( Pos ) + SumSz, spBegin );
+ Result := fHandle <> 0;
+ end;
+
+ end;
+begin
+ DesiredSize := Size;
+ if DesiredSize = 0 then
+ DesiredSize := GetSystemMetrics( SM_CXICON );
+ Clear;
+ Pos := Strm.Position;
+
+ Mem := nil;
+ {$IFDEF ICONLOAD_PRESERVEBMPS}
+ if ImgBmp <> nil then
+ begin
+ RemoveFromAutoFree( ImgBmp );
+ RemoveFromAutoFree( MskBmp );
+ Free_And_Nil( ImgBmp );
+ Free_And_Nil( MskBmp );
+ end;
+ {$ELSE}
+ ImgBmp := nil;
+ MskBmp := nil;
+ {$ENDIF ICONLOAD_PRESERVEBMPS}
+ TmpBmp := nil;
+
+ if not ReadIcon then
+ begin
+ Clear;
+ Strm.Seek( Pos, spBegin );
+ end;
+
+ Mem.Free;
+ {$IFNDEF ICONLOAD_PRESERVEBMPS}
+ ImgBmp.Free;
+ MskBmp.Free;
+ {$ENDIF ICONLOAD_PRESERVEBMPS}
+ TmpBmp.Free;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TIcon.SaveToFile(const FileName: KOLString);
+begin
+ SaveIcons2File( [ @Self ], FileName );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TIcon.SaveToStream(Strm: PStream);
+begin
+ SaveIcons2Stream( [ @Self ], Strm );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_noVERSION}
+procedure TIcon.SetHandle(const Value: HIcon);
+const szII = sizeof( TIconInfo );
+ szBIH = sizeof(TBitmapInfoHeader);
+asm //cmd //opd
+ CMP EDX, [EAX].fHandle
+ JE @@exit
+ PUSHAD
+ PUSH EDX
+ MOV EBX, EAX
+ CALL Clear
+ POP ECX
+ MOV [EBX].fHandle, ECX
+ JECXZ @@fin
+ ADD ESP, -szBIH
+ PUSH ESP
+ PUSH ECX
+ CALL GetIconInfo
+ MOV ESI, [ESP].TIconInfo.hbmMask
+ MOV EDI, [ESP].TIconInfo.hbmColor
+ PUSH ESP
+ PUSH szBIH
+ PUSH ESI
+ CALL GetObject
+ POP EAX
+ POP [EBX].fSize
+ ADD ESP, szBIH-8
+ TEST ESI, ESI
+ JZ @@1
+ PUSH ESI
+ CALL DeleteObject
+@@1: TEST EDI, EDI
+ JZ @@fin
+ PUSH EDI
+ CALL DeleteObject
+@@fin: POPAD
+@@exit:
+end;
+{$ELSE PAS_VERSION} //Pascal
+procedure TIcon.SetHandle(const Value: HIcon);
+var II : TIconInfo;
+ B: TagBitmap;
+begin
+ if FHandle = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Clear;
+ FHandle := Value;
+ if Value <> 0 then
+ begin
+ GetIconInfo( FHandle, II );
+ GetObject( II.hbmMask, Sizeof( B ), @B );
+ {$IFDEF ICON_DIFF_WH}
+ fWidth := B.bmWidth;
+ fHeight := B.bmHeight;
+ {$ELSE}
+ fSize := B.bmWidth;
+ {$ENDIF}
+ if II.hbmMask <> 0 then
+ DeleteObject( II.hbmMask );
+ if II.hbmColor <> 0 then
+ DeleteObject( II.hbmColor );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TIcon.SetHandleEx(NewHandle: HIcon);
+begin
+ if FHandle = NewHandle then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Clear;
+ FHandle := NewHandle;
+end;
+
+procedure TIcon.SetSize(const Value: Integer);
+begin
+ {$IFDEF ICON_DIFF_WH}
+ if (fWidth = Value) and (fHeight = Value) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>}
+ {$ELSE}
+ if FSize = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$ENDIF}
+ Clear;
+ {$IFDEF ICON_DIFF_WH}
+ fWidth := Value;
+ fHeight := Value;
+ {$ELSE}
+ FSize := Value;
+ {$ENDIF}
+end;
+
+{$IFDEF ICON_DIFF_WH}
+function TIcon.GetIconSize: Integer;
+begin
+ Result := Max( fWidth, fHeight );
+end;
+{$ENDIF}
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function ColorBits( ColorsCount : Integer ) : Integer;
+var I : Integer;
+begin
+ for I := 1 to 6 do
+ begin
+ Result := PossibleColorBits[ I ];
+ if (1 shl Result) >= ColorsCount then break;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+function SaveIcons2StreamEx( const BmpHandles: array of HBitmap; Strm: PStream ): Boolean;
+var I, Off : Integer;
+ IDI : TIconDirEntry;
+ BIH : TBitmapInfoHeader;
+ B: TagBitmap;
+ function RGBArraySize : Integer;
+ begin
+ Result := 0;
+ if (IDI.bColorCount >= 2) or (IDI.bReserved = 1) then
+ Result := (IDI.bColorCount + (IDI.bReserved shl 8)) * Sizeof( TRGBQuad );
+ end;
+ function ColorDataSize( W, H: Integer ) : Integer;
+ var N: Integer;
+ begin
+ if (IDI.bColorCount >= 2) or (IDI.bReserved = 1) then
+ N := (ColorBits( IDI.bColorCount + (IDI.bReserved shl 8) ) )
+ else
+ begin
+ N := IDI.wBitCount;
+ end;
+ Result := ((N * W + 31) div 32) * 4
+ * H;
+ end;
+ function MaskDataSize( W, H: Integer ) : Integer;
+ begin
+ Result := ((W + 31) div 32) * 4 * H;
+ end;
+var BColor, BMask: HBitmap;
+ W, H: Integer;
+ ImgBmp, MskBmp: PBitmap;
+ IH : TIconHeader;
+ Colors : PList;
+begin
+ {$IFDEF KOL_ASSERTIONS}
+ Assert( (High(BmpHandles) >= 0) and (High(BmpHandles) and 1 <> 0),
+ 'Incorrect parameters count in call to SaveIcons2StreamEx' );
+ {$ENDIF KOL_ASSERTIONS}
+ Result := False;
+ IH.idReserved := 0;
+ IH.idType := 1;
+ IH.idCount := (High( BmpHandles )+1) div 2;
+ if Strm.Write( IH, Sizeof( IH ) ) <> Sizeof( IH ) then Exit; {>>>>>>>>>>>>>>>}
+ Off := Sizeof( IH ) + IH.idCount * Sizeof( IDI );
+ Colors := NewList;
+ ImgBmp := NewBitmap( 0, 0 );
+ MskBmp := NewBitmap( 0, 0 );
+ TRY
+
+ for I := 0 to High( BmpHandles ) div 2 do
+ begin
+ BColor := BmpHandles[ I * 2 ];
+ BMask := BmpHandles[ I * 2 + 1 ];
+ if (BColor = 0) and (BMask = 0) then break;
+ {$IFDEF KOL_ASSERTIONS}
+ Assert( BMask <> 0, 'Mask bitmap not provided for saving icons in SaveIcons2StreamEx' );
+ {$ENDIF KOL_ASSERTIONS}
+ GetObject( BMask, Sizeof( B ), @ B );
+ W := B.bmWidth;
+ H := B.bmHeight;
+ if BColor <> 0 then
+ begin
+ GetObject( BColor, Sizeof( B ), @B );
+ {$IFDEF KOL_ASSERTIONS}
+ Assert( (B.bmWidth = W) and (B.bmHeight = H),
+ 'Mask bitmap size must much color bitmap size in SaveIcons2StreamEx' );
+ {$ENDIF KOL_ASSERTIONS}
+ end;
+ ZeroMemory( @IDI, Sizeof( IDI ) );
+
+ IDI.bWidth := W;
+ IDI.bHeight := H;
+ if BColor = 0 then
+ IDI.bColorCount := 2
+ else
+ begin
+ ImgBmp.Handle := CopyImage( BColor, IMAGE_BITMAP, W, H,
+ LR_CREATEDIBSECTION );
+ ZeroMemory( @BIH, Sizeof( BIH ) );
+ BIH.biSize := Sizeof( BIH );
+ GetObject( ImgBmp.Handle, Sizeof( B ), @B );
+ if (B.bmPlanes = 1) and (B.bmBitsPixel >= 15) then
+ begin
+ IDI.bColorCount := 0;
+ IDI.bReserved := 0;
+ IDI.wBitCount := B.bmBitsPixel;
+ end
+ else
+ if B.bmPlanes * (1 shl B.bmBitsPixel) < 16 then
+ begin
+ ImgBmp.PixelFormat := pf1bit;
+ IDI.bColorCount := 2;
+ end
+ else
+ if B.bmPlanes * (1 shl B.bmBitsPixel) < 256 then
+ begin
+ ImgBmp.PixelFormat := pf4bit;
+ IDI.bColorCount := 16;
+ end
+ else
+ begin
+ ImgBmp.PixelFormat := pf8bit;
+ IDI.bColorCount := 0;
+ IDI.bReserved := 1;
+ end;
+ end;
+ Colors.Add( Pointer(IDI.bColorCount + (IDI.bReserved shl 8)) );
+ IDI.dwBytesInRes := Sizeof( BIH ) + RGBArraySize +
+ ColorDataSize( W, H ) + MaskDataSize( W, H );
+ IDI.dwImageOffset := Off;
+ if Strm.Write( IDI, Sizeof( IDI ) ) <> Sizeof( IDI ) then Exit; {>>>>>>>>}
+ Inc( Off, IDI.dwBytesInRes );
+ end;
+ for I := 0 to High( BmpHandles ) div 2 do
+ begin
+ BColor := BmpHandles[ I * 2 ];
+ BMask := BmpHandles[ I * 2 + 1 ];
+ if (BColor = 0) and (BMask = 0) then break;
+ GetObject( BMask, Sizeof( B ), @ B );
+ W := B.bmWidth;
+ H := B.bmHeight;
+
+ ZeroMemory( @BIH, Sizeof( BIH ) );
+ BIH.biSize := Sizeof( BIH );
+ BIH.biWidth := W;
+ BIH.biHeight := H;
+ if BColor <> 0 then
+ BIH.biHeight := W * 2;
+ BIH.biPlanes := 1;
+ PWord( @ IDI.bColorCount )^ := DWord( Colors.Items[ I ] );
+ if IDI.wBitCount = 0 then
+ IDI.wBitCount := ColorBits( PWord( @ IDI.bColorCount )^ );
+ BIH.biBitCount := IDI.wBitCount;
+ BIH.biSizeImage := Sizeof( BIH ) + ColorDataSize( W, H ) + MaskDataSize( W, H );
+ if Strm.Write( BIH, Sizeof( BIH ) ) <> Sizeof( BIH ) then Exit; {>>>>>>>>}
+ if BColor <> 0 then
+ begin
+
+ ImgBmp.Handle := CopyImage( BColor, IMAGE_BITMAP, W, H, 0 );
+ case BIH.biBitCount of
+ 1 : ImgBmp.PixelFormat := pf1bit;
+ 4 : ImgBmp.PixelFormat := pf4bit;
+ 8 : ImgBmp.PixelFormat := pf8bit;
+ 16: ImgBmp.PixelFormat := pf16bit;
+ 24: ImgBmp.PixelFormat := pf24bit;
+ 32: ImgBmp.PixelFormat := pf32bit;
+ end;
+ end
+ else
+ begin
+ ImgBmp.Handle := CopyImage( BMask, IMAGE_BITMAP, W, H, 0 );
+ ImgBmp.PixelFormat := pf1bit;
+ end;
+ if ImgBmp.FDIBBits <> nil then
+ begin
+ if Strm.Write( Pointer(Integer(ImgBmp.FDIBHeader) + Sizeof(TBitmapInfoHeader))^,
+ PWord( @ IDI.bColorCount )^ * Sizeof( TRGBQuad ) ) <>
+ PWord( @ IDI.bColorCount )^ * Sizeof( TRGBQuad ) then Exit; {>>>>>>>}
+ if Strm.Write( ImgBmp.FDIBBits^, ColorDataSize( W, H ) ) <>
+ DWord( ColorDataSize( W, H ) ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ MskBmp.Handle := CopyImage( BMask, IMAGE_BITMAP, W, H, 0 );
+
+ MskBmp.PixelFormat := pf1bit;
+ if Strm.Write( MskBmp.FDIBBits^, MaskDataSize( W, H ) ) <>
+ DWord( MaskDataSize( W, H ) ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+
+ FINALLY
+ Colors.Free;
+ ImgBmp.Free;
+ MskBmp.Free;
+ END;
+ Result := True;
+end;
+
+{$IFDEF FPC}
+ {$DEFINE _D3orFPC}
+{$ENDIF}
+{$IFDEF _D2orD3}
+ {$DEFINE _D3orFPC}
+{$ENDIF}
+procedure SaveIcons2Stream( const Icons : array of PIcon; Strm : PStream );
+var I, J, Pos : Integer;
+ {$IFDEF _D3orFPC}
+ Bitmaps: array[ 0..63 ] of HBitmap;
+ {$ELSE DELPHI}
+ Bitmaps: array of HBitmap;
+ {$ENDIF FPC/DELPHI}
+ II: TIconInfo;
+ Bmp: HBitmap;
+begin
+ for I := 0 to High( Icons ) do
+ begin
+ if Icons[ I ].Handle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ for J := I + 1 to High( Icons ) do
+ if Icons[ I ].Size = Icons[ J ].Size then Exit; {>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Pos := Strm.Position;
+
+ {$IFDEF _D3orFPC}
+ for I := 0 to High( Bitmaps ) do
+ Bitmaps[ I ] := 0;
+ {$ELSE DELPHI}
+ SetLength( Bitmaps, Length( Icons ) * 2 );
+ {$ENDIF FPC/DELPHI}
+ for I := 0 to High( Icons ) do
+ begin
+ GetIconInfo( Icons[ I ].Handle, II );
+ Bitmaps[ I * 2 ] := II.hbmColor;
+ Bitmaps[ I * 2 + 1 ] := II.hbmMask;
+ end;
+
+ if not SaveIcons2StreamEx( Bitmaps, Strm ) then
+ Strm.Seek( Pos, spBegin );
+
+ for I := 0 to High( Bitmaps ) do
+ begin
+ Bmp := Bitmaps[ I ];
+ if Bmp <> 0 then
+ DeleteObject( Bmp );
+ end;
+end;
+
+procedure SaveIcons2File( const Icons : array of PIcon; const FileName : KOLString );
+var Strm: PStream;
+begin
+ Strm := NewWriteFileStream( FileName );
+ SaveIcons2Stream( Icons, Strm );
+ Strm.Free;
+end;
+
+procedure TIcon.LoadFromExecutable(const FileName: KOLString; IconIdx: Integer);
+var I: Integer;
+begin
+ Clear;
+ I := ExtractIcon( hInstance, PKOLChar( FileName ), IconIdx );
+ if I > 1 then
+ Handle := I;
+end;
+
+function GetFileIconCount( const FileName: KOLString ): Integer;
+begin
+ Result := ExtractIcon( hInstance, PKOLChar( FileName ), DWORD(-1) );
+end;
+
+procedure TIcon.LoadFromResourceID(Inst, ResID, DesiredSize: Integer);
+begin
+ LoadFromResourceName( Inst, MAKEINTRESOURCE( ResID ), DesiredSize );
+end;
+
+procedure TIcon.LoadFromResourceName(Inst: Integer; ResName: PKOLChar; DesiredSize: Integer);
+begin
+ Handle := LoadImage( Inst, ResName, IMAGE_ICON, DesiredSize, DesiredSize, $8000 {LR_SHARED} );
+ if fHandle <> 0 then FShareIcon := True;
+end;
+
+function LoadImgIcon( RsrcName: PKOLChar; Size: Integer ): HIcon;
+begin
+ Result := LoadImage( hInstance, RsrcName, IMAGE_ICON, Size, Size, $8000 {LR_SHARED} );
+end;
+
+{$IFDEF OLD_ALIGN}
+procedure AlignChildrenProc( Sender: PObj );
+type
+ TAligns = set of TControlAlign;
+var P: PControl;
+ CR: TRect;
+ procedure DoAlign( Allowed: TAligns );
+ var I: Integer;
+ C: PControl;
+ R, R1: TRect;
+ W, H: Integer;
+ ChgPos, ChgSiz: Boolean;
+ begin
+ for I := 0 to P.fChildren.fCount - 1 do
+ begin
+ C := P.fChildren.{$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}[ I ];
+ if not C.ToBeVisible then continue;
+ // important: not fVisible, and even not Visible, but ToBeVisible!
+ if {$IFDEF USE_FLAGS} G4_NotUseAlign in C.fFlagsG4
+ {$ELSE} C.fNotUseAlign {$ENDIF} then continue;
+ if C.FAlign in Allowed then
+ begin
+ R := C.BoundsRect;
+ R1 := R;
+ W := R.Right - R.Left;
+ H := R.Bottom - R.Top;
+ case C.FAlign of
+ caTop:
+ begin
+ OffsetRect( R, 0, -R.Top + CR.Top + P.Margin );
+ Inc( CR.Top, H + P.Margin );
+ R.Left := CR.Left + P.Margin;
+ R.Right := CR.Right - P.Margin;
+ end;
+ caBottom:
+ begin
+ OffsetRect( R, 0, -R.Bottom + CR.Bottom - P.Margin );
+ Dec( CR.Bottom, H + P.Margin );
+ R.Left := CR.Left + P.Margin;
+ R.Right := CR.Right - P.Margin;
+ end;
+ caLeft:
+ begin
+ OffsetRect( R, -R.Left + CR.Left + P.Margin, 0 );
+ Inc( CR.Left, W + P.Margin );
+ R.Top := CR.Top + P.Margin;
+ R.Bottom := CR.Bottom - P.Margin;
+ end;
+ caRight:
+ begin
+ OffsetRect( R, -R.Right + CR.Right - P.Margin, 0 );
+ Dec( CR.Right, W + P.Margin );
+ R.Top := CR.Top + P.Margin;
+ R.Bottom := CR.Bottom - P.Margin;
+ end;
+ caClient:
+ begin
+ R := CR;
+ InflateRect( R, -P.Margin, -P.Margin );
+ end;
+ end;
+ if R.Right < R.Left then R.Right := R.Left;
+ if R.Bottom < R.Top then R.Bottom := R.Top;
+ ChgPos := (R.Left <> R1.Left) or (R.Top <> R1.Top);
+ ChgSiz := (R.Right - R.Left <> W) or (R.Bottom - R.Top <> H);
+ if ChgPos or ChgSiz then
+ begin
+ C.BoundsRect := R;
+ if ChgSiz then
+ AlignChildrenProc( C );
+ end;
+ end;
+ end;
+ end;
+begin
+ P := Pointer( Sender );
+ if P = nil then Exit; // Called for form - ignore. {>>>>>>>>>>>>>>>>>>>>>>>>>}
+ CR := P.ClientRect;
+ if CR.Right <= CR.Left then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ DoAlign( [ caTop, caBottom ] );
+ DoAlign( [ caLeft, caRight ] );
+ DoAlign( [ caClient ] );
+end;
+{$ELSE NEW_ALIGN}
+
+procedure AlignChildrenProc_(P:PControl);
+type TAligns = set of TControlAlign;
+var CR: TRect;
+ procedure DoAlign( Allowed: TAligns );
+ var I, W, H: Integer;
+ C: PControl;
+ R, R1: TRect;
+ ChgPos, ChgSiz: Boolean;
+ begin
+ for I := 0 to P.fChildren.fCount - 1 do
+ begin
+ if not (oaAligning in P.fAligning) then exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ C := P.fChildren.{$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}[ I ];
+ with C^ do
+ begin
+ {$IFDEF SAFE_CODE}
+ C.RefInc;
+ TRY
+ {$ENDIF}
+ if (not(
+ {$IFDEF USE_FLAGS} (F3_Visible in fStyle.f3_Style)
+ {$ELSE} fVisible {$ENDIF}
+ {$IFDEF CREATE_HIDDEN}
+ or
+ {$IFDEF USE_FLAGS} (G4_CreateHidden in fFlagsG4)
+ {$ELSE} fCreateHidden {$ENDIF}
+ {$ENDIF CREATE_HIDDEN}
+ ))
+ or(not(fAlign in Allowed)) then continue;
+ if {$IFDEF USE_FLAGS} not(G4_NotUseAlign in fFlagsG4)
+ {$ELSE} not fNotUseAlign {$ENDIF} then
+ begin
+ R := BoundsRect;
+ R1 := R;
+ W := R.Right - R.Left;
+ H := R.Bottom - R.Top;
+ case FAlign of
+ caTop:
+ begin
+ OffsetRect( R, 0, -R.Top + CR.Top + P.Margin );
+ Inc( CR.Top, H + P.Margin );
+ R.Left := CR.Left + P.Margin;
+ R.Right := CR.Right - P.Margin;
+ end;
+ caBottom:
+ begin
+ OffsetRect( R, 0, -R.Bottom + CR.Bottom - P.Margin );
+ Dec( CR.Bottom, H + P.Margin );
+ R.Left := CR.Left + P.Margin;
+ R.Right := CR.Right - P.Margin;
+ end;
+ caLeft:
+ begin
+ OffsetRect( R, -R.Left + CR.Left + P.Margin, 0 );
+ Inc( CR.Left, W + P.Margin );
+ R.Top := CR.Top + P.Margin;
+ R.Bottom := CR.Bottom - P.Margin;
+ end;
+ caRight:
+ begin
+ OffsetRect( R, -R.Right + CR.Right - P.Margin, 0 );
+ Dec( CR.Right, W + P.Margin );
+ R.Top := CR.Top + P.Margin;
+ R.Bottom := CR.Bottom - P.Margin;
+ end;
+ caClient:
+ begin
+ R := CR;
+ InflateRect( R, -P.Margin, -P.Margin );
+ end;
+ end;
+ if R.Right < R.Left then R.Right := R.Left;
+ if R.Bottom < R.Top then R.Bottom := R.Top;
+ ChgPos := (R.Left <> R1.Left) or (R.Top <> R1.Top);
+ ChgSiz := (R.Right - R.Left <> W) or (R.Bottom - R.Top <> H);
+ if ChgSiz then
+ begin
+ exclude(fAligning,oaWaitAlign);
+ include(fAligning,oaFromSelf);
+ end;
+ if ChgPos or ChgSiz then BoundsRect := R;
+ end;
+ {$IFDEF SAFE_CODE}
+ FINALLY
+ C.RefDec;
+ END;
+ {$ENDIF SAFE_CODE}
+ if oaWaitAlign in fAligning then AlignChildrenProc_(C);
+ end;
+ end;
+ end;
+
+begin
+ exclude(P.fAligning,oaWaitAlign);
+ include(P.fAligning,oaAligning);
+ CR := P.ClientRect;
+ DoAlign( [ caTop, caBottom ] );
+ DoAlign( [ caLeft, caRight ] );
+ DoAlign( [ caClient,caNone ] );
+ exclude(P.fAligning,oaAligning);
+end;
+
+{$IFDEF ASM_TLIST}
+procedure AlignChildrenProc(Sender: PObj);
+const AlignModes = (1 shl byte(caBottom))+(1 shl byte(caTop))+
+ (((1 shl byte(caRight)) +(1 shl byte(caLeft)))shl 8)+
+ (((1 shl byte(caClient))+(1 shl byte(caNone)))shl 16);
+asm //cmd //opd
+ TEST EAX,EAX
+ JZ @@21
+ CMP [EAX].TControl.fParent,0
+ SETZ DL
+ {$IFDEF USE_FLAGS}
+ TEST [EAX].TControl.fFlagsG3, (1 shl G3_IsForm)
+ SETNZ DH
+ OR DL, DH
+ {$ELSE}
+ OR DL,[EAX].TControl.fIsForm
+ {$ENDIF}
+ BTR dword ptr[EAX].TControl.fAligning,oaFromSelf
+ JA @@20
+ OR byte ptr[EAX].TControl.fAligning,(1 shl oaWaitAlign)
+ MOV EAX,[EAX].TControl.fParent
+@@20: TEST EAX, EAX
+ JZ @@21
+ CALL @@ToBeAlign
+ JNZ @@DoAlign
+@@21: RETN
+
+@@ToBeAlign:
+ {$IFDEF USE_FLAGS}
+ TEST [EAX].TControl.fStyle.f3_Style, 1 shl F3_Visible
+ SETNZ DL
+ {$ELSE}
+ MOV DL,[EAX].TControl.fVisible
+ {$ENDIF}
+ {$IFDEF USE_FLAGS}
+ TEST [EAX].TControl.fFlagsG3, 1 shl G3_IsForm
+ SETNZ DH
+ OR DL, DH
+ TEST [EAX].TControl.fFlagsG4, (1 shl G4_CreateHidden)
+ SETNZ DH
+ OR DL, DH
+ {$ELSE}
+ OR DL,[EAX].TControl.fCreateHidden
+ {$ENDIF}
+ JE @@10
+ {$IFDEF USE_FLAGS}
+ TEST [EAX].TControl.fFlagsG3, 1 shl G3_IsForm
+ SETNZ DH
+ AND DL, DH
+ {$ELSE}
+ AND DL,[EAX].TControl.fIsForm
+ {$ENDIF}
+ JNE @@12
+ CMP dword ptr[EAX].TControl.fParent,0
+ JE @@11
+ PUSH EAX
+ MOV EAX,[EAX].TControl.fParent
+ CALL @@ToBeAlign
+ POP EAX
+@@10: XOR DL,1
+//!!! Important: oaWaitAlign=0
+ OR [EAX].TControl.fAligning,DL
+@@11: XOR DL,1
+@@12: RETN
+
+@@DoAlign:
+ //CALL AlignChildrenProc_
+ //RET
+ PUSH EBP
+ PUSH EBX
+ PUSH ESI
+ PUSH EDI
+ PUSH AlignModes //00210A14h
+ SUB ESP,030h
+ MOV EBX,EAX
+ AND byte ptr[EBX].TControl.fAligning,not(1 shl oaWaitAlign)
+ OR byte ptr[EBX].TControl.fAligning,(1 shl oaAligning)
+ LEA EDX,[ESP+20h] //@CR
+ CALL TControl.ClientRect
+@@Main:
+ MOV EAX,[EBX].TControl.fChildren
+ MOV EDI,[EAX].TList.fCount
+ MOV EBP,[EAX].TList.fItems
+ JMP @@entry
+@@loop:
+ MOV ESI,[EBP]
+ {$IFDEF USE_FLAGS}
+ MOV AL,[ESI].TControl.fStyle.f3_Style
+ SHR AL, F3_Visible
+ OR AL,[ESI].TControl.fFlagsG4
+ AND AL, 1 shl G4_CreateHidden // G4_CreateHidden = 0 !!!
+ {$ELSE}
+ MOV AL,[ESI].TControl.fVisible
+ OR AL,[ESI].TControl.fCreateHidden
+ {$ENDIF}
+ JZ @@continue
+ MOVZX EAX,[ESI].TControl.fAlign
+ BT [ESP+30h],EAX //Allowed
+ JNC @@continue
+ {$IFDEF USE_FLAGS}
+ TEST [ESI].TControl.fFlagsG4, 1 shl G4_NotUseAlign
+ {$ELSE}
+ CMP byte ptr[ESI].TControl.fNotUseAlign,0
+ {$ENDIF}
+ JNE @@align
+ MOV EDX,ESP //@R
+ MOV EAX,ESI //C
+ CALL TControl.GetBoundsRect
+ MOV EAX,[ESP+0Ch] //R.Bottom
+ MOV [ESP+1Ch],EAX //H
+ MOV EAX,[ESP+08h] //R.Right
+ MOV [ESP+18h],EAX //W
+ MOV EAX,[ESP+04h] //R.Top
+ MOV [ESP+14h],EAX //R1.Top
+ SUB [ESP+1Ch],EAX //H
+ MOV EAX,[ESP] //R.Left
+ MOV [ESP+10h],EAX //R1.Left
+ SUB [ESP+18h],EAX //W
+ MOVSX EDX,[EBX].TControl.fMargin
+ MOVZX ECX,byte ptr[ESI].TControl.fAlign
+//!!! Order of caXXX-constants is important
+ LOOP @@caTop
+ MOV EAX,[ESP+20h] //CR.Left
+ SUB EAX,[ESP] //R.Left
+ ADD EAX,EDX //+Margin
+ MOV ECX,[ESP+18h] //W
+ ADD ECX,EDX //+Margin
+ ADD [ESP+20h],ECX //CR.Left
+ JMP @@00
+@@caTop:
+ LOOP @@caRight
+ MOV EAX,[ESP+24h] //CR.Top
+ SUB EAX,[ESP+04h] //R.Top
+ ADD EAX,EDX //+Margin
+ MOV ECX,[ESP+1Ch] //H
+ ADD ECX,EDX //+Margin
+ ADD [ESP+24h],ECX //CR.Top
+ JMP @@01
+@@caRight:
+ LOOP @@caBottom
+ MOV EAX,[ESP+28h] //CR.Right
+ SUB EAX,[ESP+08h] //R.Right
+ SUB EAX,EDX //-Margin
+ MOV ECX,[ESP+18h] //W
+ ADD ECX,EDX //+Margin
+ SUB [ESP+28h],ECX //CR.Right
+@@00: ADD [ESP],EAX //R.Left
+ ADD [ESP+08h],EAX //R.Right
+ MOV EAX,[ESP+2Ch] //CR.Bottom
+ SUB EAX,EDX //+Margin
+ MOV [ESP+0Ch],EAX //R.Bottom
+ ADD EDX,[esp+24h] //Margin+CR.Top
+ MOV [ESP+04h],edx //R.Top
+ JMP @@caNone
+@@caBottom:
+ LOOP @@caClient
+ MOV EAX,[ESP+2Ch] //CR.Bottom
+ SUB EAX,[ESP+0Ch] //R.Bottom
+ SUB EAX,EDX //-Margin
+ MOV ECX,[ESP+1Ch] //H
+ ADD ECX,EDX //+Margin
+ SUB [ESP+2Ch],ECX //CR.Bottom
+@@01: ADD [ESP+04h],EAX //R.Top
+ ADD [ESP+0Ch],EAX //R.Bottom
+ MOV EAX,[ESP+28h] //CR.Right
+ SUB EAX,EDX //-Margin
+ MOV [esp+08h],EAX //R.Right
+ ADD EDX,[ESP+20h] //Margin+CR.Left
+ MOV [ESP],EDX //R.Left
+ JMP @@caNone
+@@caClient:
+ LOOP @@caNone
+ MOV EAX,[ESP+2Ch] //CR.Bottom
+ SUB EAX,EDX //-Margin
+ MOV [ESP+0Ch],EAX //R.Bottom
+ MOV EAX,[ESP+28h] //CR.Right
+ SUB EAX,EDX //-Margin
+ MOV [ESP+08h],EAX //R.Right
+ MOV EAX,[ESP+24h] //CR.Top
+ ADD EAX,EDX //+Margin
+ MOV [ESP+04h],EAX //R.Top
+ ADD EDX,[ESP+20h] //Margin+CR.Left
+ MOV [ESP],EDX //R.Left
+@@caNone:
+ MOV EAX,[ESP] //R.Left
+ CMP EAX,[ESP+08h] //R.Right
+ JLE @@02 //CMOVG ???
+ MOV [ESP+08h],EAX //R.Right
+@@02: MOV EAX,[ESP+04h] //R.Top
+ CMP EAX,[ESP+0Ch] //R.Bottom
+ JLE @@03 //CMOVG ???
+ MOV [ESP+0Ch],EAX //R.Bottom
+@@03: MOV EDX,[ESP] //R.Left
+ SUB EDX,[ESP+10h] //R1.Left
+ MOV EAX,[ESP+04h] //R.Top
+ SUB EAX,[ESP+14h] //R1.Top
+ OR EDX,EAX //ChgPos
+ MOV ECX,[ESP+08h] //R.Right
+ SUB ECX,[ESP] //R.Left
+ SUB ECX,[ESP+18h] //W
+ MOV EAX,[ESP+0Ch] //R.Bottom
+ SUB EAX,[ESP+04h] //R.Top
+ SUB EAX,[ESP+1Ch] //H
+ OR EAX,ECX
+ JZ @@04
+ AND byte ptr[ESI].TControl.fAligning,not(1 shl oaWaitAlign)
+ OR byte ptr[ESI].TControl.fAligning,(1 shl oaFromSelf)
+@@04: OR EAX,EDX
+ JZ @@align
+ MOV EDX,ESP //@R
+ MOV EAX,ESI //C
+ CALL TControl.SetBoundsRect
+@@align:
+ TEST byte ptr[ESI].TControl.fAligning,(1 shl oaWaitAlign)
+ JZ @@continue
+ MOV EAX,ESI //C
+ CALL @@DoAlign
+@@continue:
+ TEST byte ptr[EBX].TControl.fAligning,(1 shl oaAligning)
+ JZ @@exit
+ ADD EBP,4
+@@entry:
+ DEC EDI
+ JGE @@loop
+ SHR dword ptr[ESP+30h],8 //Allowed
+ JNZ @@Main
+ AND byte ptr[EBX].TControl.fAligning,not(1 shl oaAligning)
+@@exit:
+ ADD ESP,34h
+ POP EDI
+ POP ESI
+ POP EBX
+ POP EBP
+end;
+{$ELSE PAS_VERSION} // Pascal
+procedure AlignChildrenProc(Sender: PObj);
+ function ToBeAlign( S: PControl ):Boolean;
+ begin
+ {$IFDEF USE_FLAGS}
+ Result := (
+ (F3_Visible in S.fStyle.f3_Style)
+ or (
+ (G3_IsForm in S.fFlagsG3) // òàê íàäî!
+ {$IFDEF CREATE_HIDDEN}
+ or (G4_CreateHidden in S.fFlagsG4)
+ {$ENDIF CREATE_HIDDEN}
+ ) )
+ and ( (G3_IsForm in S.fFlagsG3)
+ or (S.fParent=nil) or ToBeAlign(S.fParent)
+ );
+ {$ELSE}
+ Result := (
+ S.fVisible
+ {$IFDEF CREATE_HIDDEN}
+ or (
+ S.fCreateHidden
+ )
+ {$ENDIF CREATE_HIDDEN}
+ )
+ and ( S.fIsForm
+ or (S.fParent=nil) or ToBeAlign(S.fParent)
+ );
+ {$ENDIF}
+ if not Result then include(S.fAligning,oaWaitAlign);
+ end;
+var fromSelf: Boolean;
+ S: PControl;
+begin
+ if Sender = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ S := Pointer( Sender );
+ fromSelf := oaFromSelf in S.fAligning;
+ Exclude( S.fAligning, oaFromSelf );
+ if ( (S.fParent = nil)
+ or {$IFDEF USE_FLAGS} (G3_IsForm in S.fFlagsG3)
+ {$ELSE} (S.fIsForm) {$ENDIF} ) and (not fromSelf) then
+ else
+ begin
+ include(S.fAligning, oaWaitAlign);
+ S := S.Parent;
+ end;
+ if (S <> nil) and ToBeAlign(S) then
+ AlignChildrenProc_(S);
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF OLD_ALIGN}
+
+procedure TControl.Set_Align(const Value: TControlAlign);
+begin
+ Global_Align := AlignChildrenProc;
+ if {$IFDEF USE_FLAGS} G4_NotUseAlign in fFlagsG4
+ {$ELSE} fNotUseAlign {$ENDIF} then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if FAlign = Value then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ FAlign := Value;
+ {$IFDEF OLD_ALIGN}
+ AlignChildrenProc( Parent );
+ {$ELSE NEW_ALIGN}
+ AlignChildrenProc(@Self);
+ {$ENDIF}
+end;
+
+function TControl.SetAlign(AAlign: TControlAlign): PControl;
+begin
+ Set_Align( AAlign );
+ Result := @Self;
+end;
+
+{$IFDEF LOG_ANTIFLICK}
+procedure LogFlick( const s: AnsiString; const rects: array of TRect );
+var s1: AnsiString;
+ i: Integer;
+begin
+ s1 := s + ' ';
+ for i := 0 to High( rects ) do
+ begin
+ s1 := s1 + '[' + Int2Str( rects[i].Left ) + ',' + Int2Str( rects[i].top ) +
+ ',' + Int2Str( rects[i].Right ) + ',' + Int2Str( rects[i].Bottom ) +
+ '=' + Int2Str( rects[i].Right - rects[i].Left ) + 'x' +
+ Int2Str( rects[i].Bottom - rects[i].Top ) + ']';
+ end;
+ LogFileOutput( GetStartDir + 'log_antiflick', s1 );
+end;
+{$ENDIF}
+
+procedure TControl.Update;
+var I: Integer;
+ C: PControl;
+begin
+ if fUpdateCount > 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if fHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ UpdateWindow( fHandle );
+ for I := 0 to fChildren.fCount - 1 do
+ begin
+ C := fChildren.{$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}[ I ];
+ C.Update;
+ end;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function WndProcUpdate( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+begin
+ if Sender.fUpdateCount <> 0 then
+ begin
+ case Msg.message of
+ WM_PAINT:
+ begin
+ ValidateRect( Sender.Handle, nil );
+ Rslt := 0;
+ end;
+ WM_ERASEBKGND: Rslt := 1;
+ else begin
+ Result := FALSE; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ Result := TRUE;
+ end
+ else Result := FALSE;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TControl.BeginUpdate;
+begin
+ Inc( fUpdateCount );
+ AttachProc( @WndProcUpdate );
+end;
+
+procedure TControl.EndUpdate;
+begin
+ Dec( fUpdateCount );
+ if fUpdateCount <= 0 then
+ begin
+ Invalidate;
+ //Update;
+ end;
+end;
+
+function TControl.GetSelection: KOLString;
+var L: Integer;
+begin
+ if fCommandActions.aGetSelection <> 0 then
+ begin
+ L := SelLength;
+ SetString( Result, nil, L + 1 );
+ Perform( fCommandActions.aGetSelection, 0, Integer( @Result[ 1 ] ) );
+ end
+ else
+ Result := Copy( Text, SelStart + 1, SelLength );
+end;
+
+procedure TControl.SetSelection(const Value: KOLString);
+begin
+ ReplaceSelection( Value, True );
+end;
+
+procedure TControl.ReplaceSelection(const Value: KOLString; aCanUndo: Boolean);
+begin
+ if fCommandActions.aReplaceSel <> 0 then
+ begin
+ Perform( fCommandActions.aReplaceSel, Integer( aCanUndo ), Integer( PKOLchar( Value ) ) );
+ end;
+end;
+
+procedure TControl.DeleteLines(FromLine, ToLine: Integer);
+var I1, I2: DWORD;
+ SStart, SLength: DWORD;
+begin
+ if FromLine > ToLine then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$IFDEF KOL_ASSERTIONS}
+ Assert( FromLine >= 0, 'Incorrect line index' );
+ {$ENDIF KOL_ASSERTIONS}
+ I1 := Item2Pos( FromLine );
+ I2 := Item2Pos( ToLine+1 ) - I1;
+ SStart := SelStart;
+ SLength := SelLength;
+ SelStart := I1;
+ {if ToLine >= Count-1 then
+ I2 := MaxInt;}
+ SelLength := I2;
+ ReplaceSelection( '', TRUE );
+ if SStart >= I2 then
+ begin
+ SStart := SStart - (I2 - I1);
+ end
+ else
+ if SStart >= I1 then
+ begin
+ SLength := SLength - (I2 - SStart);
+ SStart := I1;
+ end
+ else
+ if SStart + SLength >= I2 then
+ begin
+ SLength := SLength - (I2 - I1);
+ end
+ else
+ if SStart + SLength >= I1 then
+ begin
+ SLength := I1 - SLength;
+ end;
+ SelStart := SStart;
+ SelLength := Max( 0, SLength );
+end;
+
+procedure TControl.SetTabOrder(const Value: SmallInt);
+var CL: PList;
+ I : Integer;
+ C: PControl;
+begin
+ if Value = fTabOrder then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ CL := CollectTabControls( ParentForm );
+ for I := 0 to CL.fCount - 1 do
+ begin
+ C := CL.{$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}[ I ];
+ if C.fTabOrder >= Value then
+ Inc( C.fTabOrder );
+ end;
+ fTabOrder := Value;
+ CL.Free;
+end;
+
+function TControl.GetFocused: Boolean;
+begin
+ if {$IFDEF USE_FLAGS} G3_IsControl in fFlagsG3
+ {$ELSE} fIsControl {$ENDIF} then
+ Result := ParentForm.DF.fCurrentControl = @Self
+ else
+ Result := GetForegroundWindow = fHandle;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+procedure TControl.SetFocused(const Value: Boolean);
+var PF: PControl;
+begin
+ if not Value or
+ {$IFDEF USE_FLAGS} not( F2_Tabstop in fStyle.f2_Style )
+ {$ELSE} not fTabStop {$ENDIF} then Exit; {>>>>>>>>>>>>>>>>>>>>}
+ if {$IFDEF USE_FLAGS} G3_IsControl in fFlagsG3
+ {$ELSE} fIsControl {$ENDIF} then
+ begin
+ PF := ParentForm;
+ if ( PF.DF.fCurrentControl <> nil ) and (PF.DF.fCurrentControl <> @ Self) then
+ if Assigned( PF.DF.fCurrentControl.EV.fLeave ) then
+ PF.DF.fCurrentControl.EV.fLeave( PF.DF.fCurrentControl )
+ else
+ Windows.SetFocus( 0 );
+ PF.DF.fCurrentControl := @Self;
+ {$IFDEF USE_GRAPHCTLS}
+ if Assigned( fSetFocus ) then
+ fSetFocus(@Self)
+ else
+ {$ENDIF}
+ SetFocus( GetWindowHandle );
+ end
+ else
+ SetForegroundWindow( GetWindowHandle );
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFNDEF NOT_USE_RICHEDIT}
+
+//////////////////////////////////////////////////////////////////////
+// R I C H E D I T
+//////////////////////////////////////////////////////////////////////
+
+{ -- rich edit -- }
+
+function TControl.REGetFont: PGraphicTool;
+var
+ CF: PCharFormat;
+ //CFA: PCharFormat2A;
+ //CFW: PCharFormat2W;
+ FS: TFontStyle;
+begin
+ {$IFDEF STATIC_RICHEDIT_DATA}
+ CF := @ DF.fRECharFormatRec;
+ {$ELSE}
+ CF := DF.fRECharFormatRec;
+ {$ENDIF}
+ ZeroMemory( CF, Sizeof( CF^ ) );
+ {$IFDEF UNICODE_CTRLS}
+ CF.cbSize := Sizeof( CF^ );
+ {$ELSE}
+ CF.cbSize := sizeof( RichEdit.TCharFormat ) + DF.fCharFmtDeltaSz;
+ {$ENDIF}
+ if DF.fTmpFont = nil then
+ begin
+ DF.fTmpFont := NewFont;
+ {$IFDEF USE_AUTOFREE4CONTROLS}
+ Add2AutoFree( DF.fTmpFont );
+ {$ENDIF}
+ end;
+ Result := DF.fTmpFont;
+ Result.OnChange := nil;
+ Perform( EM_GETCHARFORMAT, 1, Integer( CF ) );
+ Result.FontHeight := CF.yHeight;
+ FS := [ ];
+ if LongBool(CF.dwEffects and CFE_BOLD) then
+ FS := [ fsBold ];
+ if LongBool(CF.dwEffects and CFE_ITALIC) then
+ include( FS, fsItalic );
+ if LongBool(CF.dwEffects and CFE_STRIKEOUT) then
+ include( FS, fsStrikeOut );
+ if LongBool(CF.dwEffects and CFE_UNDERLINE) then
+ include( FS, fsUnderline );
+ Result.FontStyle := FS;
+ if not LongBool(CF.dwEffects and CFE_AUTOCOLOR) then
+ Result.Color := CF.crTextColor;
+ Result.FontPitch := TFontPitch( CF.bPitchAndFamily and 3 );
+ Result.FontCharset := CF.bCharSet;
+ {$IFDEF UNICODE_CTRLS}
+ {$ELSE}
+ if (PWord( @CF.szFaceName[0] )^ shr 8) <> 0 then
+ Result.FontName := PAnsiChar(@CF.szFaceName[0]) // real T,0 works fine.
+ else
+ {$ENDIF}
+ Result.FontName := KOLString(PWideChar(@CF.szFaceName[0]));
+ Result.OnChange := RESetFont;
+end;
+
+const RichAreas: array[ TRichFmtArea ] of Integer = ( SCF_SELECTION,
+ 3 {SCF_WORD}, 4 {SCF_ALL} );
+
+procedure TControl.RESetFontEx(const Index: Integer);
+var CF: PCharFormat;
+ FS: TFontStyle;
+begin
+ {$IFDEF STATIC_RICHEDIT_DATA}
+ CF := @ DF.fRECharFormatRec;
+ {$ELSE}
+ CF := DF.fRECharFormatRec;
+ {$ENDIF}
+ ZeroMemory( CF, {82} sizeof( CF^ ) );
+ {$IFDEF UNICODE_CTRLS}
+ CF.cbSize := Sizeof( CF^ );
+ {$ELSE}
+ CF.cbSize := 60 { sizeof( TCharFormat ) } + DF.fCharFmtDeltaSz;
+ {$ENDIF}
+ CF.dwMask := CFM_BOLD or CFM_COLOR or CFM_FACE or CFM_ITALIC
+ or CFM_SIZE or CFM_STRIKEOUT or CFM_UNDERLINE;
+ CF.yHeight := DF.fTmpFont.FontHeight;
+ FS := DF.fTmpFont.FontStyle;
+ if fsBold in FS then CF.dwEffects := CFE_BOLD;
+ if fsItalic in FS then CF.dwEffects := CF.dwEffects or CFE_ITALIC;
+ if fsStrikeOut in FS then CF.dwEffects := CF.dwEffects or CFE_STRIKEOUT;
+ if fsUnderline in FS then CF.dwEffects := CF.dwEffects or CFE_UNDERLINE;
+ CF.crTextColor := Color2RGB(DF.fTmpFont.Color);
+ CF.bCharSet := DF.fTmpFont.FontCharset;
+ CF.bPitchAndFamily := Ord( DF.fTmpFont.FontPitch );
+ {$IFDEF UNICODE_CTRLS} WStrLCopy {$ELSE} StrLCopy {$ENDIF}
+ ( CF.szFaceName, PKOLChar( DF.fTmpFont.FontName ), 31 );
+ Perform( EM_SETCHARFORMAT, RichAreas[ DF.fRECharArea ], Integer( CF ) );
+end;
+
+procedure TControl.RESetFont(Value: PGraphicTool);
+var H: Integer;
+begin
+ if Value <> DF.fTmpFont then
+ REGetFont;
+ H := DF.fTmpFont.fData.Font.Height;
+ DF.fTmpFont := DF.fTmpFont.Assign( Value );
+ if DF.fTmpFont.fData.Font.Height = 0 then
+ DF.fTmpFont.fData.Font.Height := H;
+ RESetFontEx( Integer( CFM_BOLD or CFM_COLOR or CFM_FACE or CFM_ITALIC
+ or CFM_SIZE or CFM_STRIKEOUT or CFM_UNDERLINE ) );
+end;
+
+function TControl.REGetFontMask( const Index: Integer ): Boolean;
+begin
+ REGetFont;
+ Result := LongBool( DF.fRECharFormatRec.dwMask and Index );
+end;
+
+function TControl.REGetFontEffects(const Index: Integer): Boolean;
+begin
+ REGetFont;
+ Result := LongBool( DF.fRECharFormatRec.dwEffects and Index );
+end;
+
+procedure TControl.RESetFontEffect(const Index: Integer;
+ const Value: Boolean);
+var
+ CF: PCharFormat;
+begin
+ ReGetFont;
+ {$IFDEF STATIC_RICHEDIT_DATA}
+ CF := @ DF.fRECharFormatRec;
+ {$ELSE}
+ CF := DF.fRECharFormatRec;
+ {$ENDIF}
+ {
+ CF.dwEffects := $FFFFFFFF and Index;
+ if not Value then CF.dwEffects := 0;
+ }
+ CF.dwEffects := CF.dwEffects or DWORD( Index );
+ if not Value then CF.dwEffects := CF.dwEffects and not Index;
+ CF.dwMask := Index;
+ Perform( EM_SETCHARFORMAT, RichAreas[ DF.fRECharArea ], Integer( CF ) );
+end;
+
+function TControl.REGetFontAttr(const Index: Integer): Integer;
+var CF: PDWORD;
+ Mask: DWORD;
+begin
+ REGetFont;
+ CF := Pointer( Integer( @DF.fRECharFormatRec ) + (HiWord(Index) and $7E) );
+ Mask := $FFFFFFFF;
+ if LongBool( HiWord(Index) and $1 ) then
+ Mask := $FF;
+ Result := CF^ and Mask;
+end;
+
+procedure TControl.RESetFontAttr(const Index, Value: Integer);
+var CF: PDWORD;
+ Mask: DWORD;
+begin
+ REGetFont;
+ {$IFDEF STATIC_RICHEDIT_DATA}
+ CF := Pointer( Integer( @ DF.fRECharFormatRec ) + (HiWord(Index) and $7E) );
+ {$ELSE}
+ CF := Pointer( Integer( DF.fRECharFormatRec ) + (HiWord(Index) and $7E) );
+ {$ENDIF}
+ Mask := 0;
+ if LongBool( HiWord(Index) and $1 ) then
+ Mask := $FFFFFF00;
+ CF^ := CF^ and Mask or DWORD(Value);
+ DF.fRECharFormatRec.dwMask := Index and $FF81FFFF;
+ if LongBool( DF.fRECharFormatRec.dwMask and (CFM_COLOR or CFM_BACKCOLOR) ) then
+ DF.fRECharFormatRec.dwEffects := DF.fRECharFormatRec.dwEffects and
+ not (CFE_AUTOCOLOR or CFE_AUTOBACKCOLOR);
+ Perform( EM_SETCHARFORMAT, RichAreas[ DF.fRECharArea ],
+ Integer( {$IFDEF STATIC_RICHEDIT_DATA} @ {$ENDIF} DF.fRECharFormatRec ) );
+end;
+
+procedure TControl.RESetFontAttr1(const Index, Value: Integer);
+begin
+ RESetFontAttr( Index, Color2RGB( Value ) );
+end;
+
+function TControl.REGetFontSizeValid: Boolean;
+begin
+ Result := REGetFontMask( Integer( CFM_SIZE ) );
+end;
+
+function TControl.REGetFontName: KOLString;
+begin
+ ReGetFont;
+ Result := DF.fRECharFormatRec.szFaceName;
+end;
+
+procedure TControl.RESetFontName(const Value: KOLString);
+begin
+ ReGetFont;
+ {$IFDEF UNICODE_CTRLS} WStrLCopy {$ELSE} StrLCopy {$ENDIF}
+ ( DF.fRECharFormatRec.szFaceName, PKOLChar( Value ), Sizeof( DF.fRECharFormatRec.szFaceName ) - 1 );
+ DF.fRECharFormatRec.dwMask := CFM_FACE;
+ Perform( EM_SETCHARFORMAT, RichAreas[ DF.fRECharArea ], Integer( @DF.fRECharFormatRec ) );
+end;
+
+function TControl.REGetCharformat: TCharFormat;
+begin
+ REGetFont;
+ Result := {$IFDEF STATIC_RICHEDIT_DATA} DF.fRECharFormatRec
+ {$ELSE} DF.fRECharFormatRec^ {$ENDIF};
+end;
+
+procedure TControl.RESetCharFormat(const Value: TCharFormat);
+begin
+ Perform( EM_SETCHARFORMAT, RichAreas[ DF.fRECharArea ], Integer( @Value ) );
+end;
+
+function REOut2Stream( Sender: PControl; Buf: PByte; Sz: DWORD; pSz: PInteger )
+ :DWORD; stdcall;
+begin
+ if Sz + Sender.DF.fREStream.Position > Sender.DF.fREStream.Size then
+ Sender.DF.fREStream.Size := Sender.DF.fREStream.Size + DWORD( {Min(} Sz {, 8192 )} );
+ pSz^ := Sender.DF.fREStream.Write( Buf^, Sz );
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Sender.EV.fOnProgress ) then
+ {$ENDIF}
+ Sender.EV.fOnProgress( Sender );
+ Result := 0;
+end;
+
+const TextTypes: array[ TRETextFormat ] of WORD = ( SF_RTF, SF_TEXT,
+ SF_RTF or SFF_PLAINRTF, SF_RTFNOOBJS, SF_RTFNOOBJS or SFF_PLAINRTF,
+ SF_TEXTIZED, {SF_UNICODE} $0010, $0010 or SF_TEXT );
+
+function TControl.RE_SaveToStream(Stream: PStream; Format: TRETextFormat;
+ SelectionOnly: Boolean): Boolean;
+var ES: TEditStream;
+ SelFlag: Integer;
+begin
+ DF.fREStream := Stream;
+ ES.dwCookie := Integer( @Self );
+ ES.dwError := 0;
+ ES.pfnCallback := @REOut2Stream;
+ SelFlag := 0;
+ if SelectionOnly then
+ SelFlag := SFF_SELECTION;
+ Perform( EM_STREAMOUT, TextTypes[ Format ] or SelFlag, Integer( @ES ) );
+ DF.fREStream := nil;
+ DF.fREError := ES.dwError;
+ Result := DF.fREError = 0;
+end;
+
+procedure RE_AddText( Self_: PControl; const S: KOLString );
+begin
+ Self_.SelStart := Self_.TextSize;
+ Self_.RE_Text[ reText, True ] := S;
+end;
+
+function TControl.REReadText(Format: TRETextFormat;
+ SelectionOnly: Boolean): KOLString;
+var B0: Integer;
+ MS: PStream;
+begin
+ fCommandActions.aAddText := RE_AddText;
+ MS := NewMemoryStream;
+ RE_SaveToStream( MS, Format, SelectionOnly );
+ B0 := 0;
+ MS.Write( B0, Sizeof( KOLChar ) );
+ {$IFDEF UNICODE_CTRLS}
+ {$ELSE}
+ if not (Format in [reUnicode,reTextUnicode]) then
+ Result := AnsiString(PAnsiChar( MS.fMemory )) // must be PAnsiChar, not PKOLChar!
+ else
+ {$ENDIF}
+ Result := PKOLChar( MS.fMemory );
+ MS.Free;
+end;
+
+function REInFromStream( Sender: PControl; Buf: PByte; Sz: DWORD; pSz: PInteger )
+ :DWORD; stdcall;
+begin
+ {$IFDEF _D3} if Sender.DF.fREStrLoadLen >= 0 then {$ENDIF}
+ if Sz > Sender.DF.fREStrLoadLen then
+ Sz := Sender.DF.fREStrLoadLen;
+ pSz^ := Sender.DF.fREStream.Read( Buf^, Sz );
+ Dec( Sender.DF.fREStrLoadLen, pSz^ );
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Sender.EV.fOnProgress ) then
+ {$ENDIF}
+ Sender.EV.fOnProgress( Sender );
+ Result := 0;
+end;
+
+function TControl.RE_LoadFromStream(Stream: PStream; Length: Integer;
+ Format: TRETextFormat; SelectionOnly: Boolean): Boolean;
+var ES: TEditStream;
+ SelFlag: Integer;
+begin
+ DF.fREStream := Stream;
+ DF.fREStrLoadLen := DWORD( Length );
+ ES.dwCookie := Integer( @Self );
+ ES.dwError := 0;
+ ES.pfnCallback := @REInFromStream;
+ SelFlag := 0;
+ if SelectionOnly then
+ SelFlag := SFF_SELECTION;
+ Perform( EM_STREAMIN, TextTypes[ Format ] or SelFlag, Integer( @ES ) );
+ DF.fREStream := nil;
+ DF.fREError := ES.dwError;
+ Result := DF.fREError = 0;
+end;
+
+procedure TControl.REWriteText(Format: TRETextFormat;
+ SelectionOnly: Boolean; const Value: KOLString);
+var MS: PStream;
+ {$IFDEF UNICODE_CTRLS}
+ {$ELSE}
+ s: AnsiString; // not KOLString!
+ {$ENDIF}
+begin
+ fCommandActions.aAddText := RE_AddText;
+ {$IFDEF UNICODE_CTRLS}
+ {$ELSE}
+ if not (Format in [reUnicode,reTextUnicode]) then
+ begin
+ s := Value;
+ MS := NewExMemoryStream( @ s[ 1 ], Length( s ) );
+ end
+ else
+ {$ENDIF}
+ MS := NewExMemoryStream( @ Value[ 1 ], Length( Value ) * Sizeof( KOLChar ) );
+ RE_LoadFromStream( MS, MS.fData.fSize, Format, SelectionOnly );
+ MS.Free;
+end;
+
+function TControl.RE_LoadFromFile(const Filename: KOLString;
+ Format: TRETextFormat; SelectionOnly: Boolean): Boolean;
+var Strm: PStream;
+begin
+ Strm := NewReadFileStream( Filename );
+ Result := RE_LoadFromStream( Strm, -1, Format, SelectionOnly );
+ Strm.Free;
+end;
+
+function TControl.RE_SaveToFile(const Filename: KOLString;
+ Format: TRETextFormat; SelectionOnly: Boolean): Boolean;
+var Strm: PStream;
+begin
+ Strm := NewWriteFileStream( Filename );
+ Result := RE_SaveToStream( Strm, Format, SelectionOnly );
+ Strm.Free;
+end;
+
+function TControl.REGetParaFmt: TParaFormat;
+begin
+ ZeroMemory( @Result, sizeof( TParaFormat2 ) );
+ Result.cbSize := sizeof( RichEdit.TParaFormat ) + DF.fParaFmtDeltaSz;
+ Perform( EM_GETPARAFORMAT, 0, Integer( @Result ) );
+end;
+
+procedure TControl.RESetParaFmt(const Value: TParaFormat);
+begin
+ Perform( EM_SETPARAFORMAT, 0, Integer( @Value ) );
+end;
+
+function TControl.REGetNumbering: Boolean;
+begin
+ Result := LongBool( ReGetParaAttr( 9 shl 16 ) );
+end;
+
+function TControl.REGetParaAttr( const Index: Integer ): Integer;
+var pDw : PDWORD;
+begin
+ {$IFDEF STATIC_RICHEDIT_DATA} DF.fREParaFmtRec
+ {$ELSE} DF.fREParaFmtRec^ {$ENDIF}
+ := REGetParaFmt;
+ pDw := Pointer( Integer( @DF.fREParaFmtRec ) + ( HiWord( Index ) and $7E ) );
+ Result := pDw^;
+ if LongBool( HiWord( Index ) and 1 ) then
+ Result := Result and $FFFF;
+end;
+
+function TControl.REGetParaAttrValid( const Index: Integer ): Boolean;
+begin
+ Result := LongBool( ReGetParaAttr( 4 shl 16 ) and Index );
+end;
+
+function TControl.REGetTabCount: Integer;
+begin
+ Result := ReGetParaAttr( 27 shl 16 );
+end;
+
+function TControl.REGetTabs(Idx: Integer): Integer;
+begin
+ Result := ReGetParaAttr( (28 + 4 * Idx) shl 16 );
+end;
+
+function TControl.REGetTextAlign: TRichTextAlign;
+begin
+ Result := TRichTextAlign( ReGetParaAttr( 25 shl 16 ) - 1 );
+end;
+
+procedure TControl.RESetNumbering(const Value: Boolean);
+begin
+ RESetParaAttr( (9 shl 16) or PFM_NUMBERING, Integer( Value ) );
+end;
+
+procedure TControl.RESetParaAttr(const Index, Value: Integer);
+var pDw: PDWORD;
+ Mask: Integer;
+begin
+ REGetParaAttr( 0 );
+ pDw := Pointer( Integer( @DF.fREParaFmtRec ) + ( HiWord( Index ) and $7E ) );
+ Mask := 0;
+ if LongBool( HiWord( Index ) and 1 ) then
+ Mask := Integer( $FFFF0000 );
+ pDw^ := pDw^ and Mask or DWORD(Value);
+ DF.fREParaFmtRec.dwMask := Index and $8000FFFF;
+ RESetParaFmt( {$IFDEF STATIC_RICHEDIT_DATA} DF.fREParaFmtRec
+ {$ELSE} DF.fREParaFmtRec^ {$ENDIF} );
+end;
+
+procedure TControl.RESetTabCount(const Value: Integer);
+begin
+ REGetParaAttr( 0 );
+ RESetParaAttr( (27 shl 16) or PFM_TABSTOPS, Value );
+end;
+
+procedure TControl.RESetTabs(Idx: Integer; const Value: Integer);
+begin
+ REGetParaAttr( 0 );
+ RESetParaAttr( (28 + 4 * Idx) or PFM_TABSTOPS, Value );
+end;
+
+procedure TControl.RESetTextAlign(const Value: TRichTextAlign);
+begin
+ RESetParaAttr( (25 shl 16) or PFM_ALIGNMENT, Ord( Value ) + 1 );
+end;
+
+function TControl.REGetStartIndentValid: Boolean;
+begin
+ Result := REGetParaAttrValid( Integer( PFM_STARTINDENT ) );
+end;
+
+procedure TControl.RE_HideSelection(aHide: Boolean);
+begin
+ Perform( EM_HIDESELECTION, Integer( aHide ), 1 );
+end;
+
+function TControl.RE_SearchText(const Value: KOLString; MatchCase,
+ WholeWord, ScanForward: Boolean; SearchFrom, SearchTo: Integer): Integer;
+var Flags: Integer;
+ FT: {$IFDEF UNICODE_CTRLS} TFindTextW {$ELSE}
+ {$IFDEF _D2} TFindText {$ELSE} TFindTextA {$ENDIF} {$ENDIF};
+begin
+ Flags := Integer( ScanForward );
+{$IFDEF _D2009orHigher}
+{$WARN SYMBOL_DEPRECATED OFF} // check deprecate state
+{$ENDIF}
+ if WholeWord then Flags := Flags or FT_WHOLEWORD;
+ if MatchCase then Flags := Flags or FT_MATCHCASE;
+{$IFDEF _D2009orHigher}
+{$WARN SYMBOL_DEPRECATED ON}
+{$ENDIF}
+ FT.chrg.cpMin := SearchFrom;
+ FT.chrg.cpMax := SearchTo;
+ FT.lpstrText := PKOLChar( Value );
+ Result := Perform( EM_FINDTEXT, Flags, Integer( @FT ) );
+end;
+
+ {$IFNDEF _FPC}
+ {$IFNDEF _D2} //------- KOLWideString not supported in D2
+function TControl.RE_WSearchText(const Value: KOLWideString; MatchCase,
+ WholeWord, ScanForward: Boolean; SearchFrom, SearchTo: Integer): Integer;
+var Flags: Integer;
+ FT: TFindTextW;
+begin
+ Flags := Integer( ScanForward );
+{$IFDEF _D2009orHigher}
+{$WARN SYMBOL_DEPRECATED OFF} // check deprecate state
+{$ENDIF}
+ if WholeWord then Flags := Flags or FT_WHOLEWORD;
+ if MatchCase then Flags := Flags or FT_MATCHCASE;
+{$IFDEF _D2009orHigher}
+{$WARN SYMBOL_DEPRECATED ON} // switch on!
+{$ENDIF}
+ FT.chrg.cpMin := SearchFrom;
+ FT.chrg.cpMax := SearchTo;
+ FT.lpstrText := PWideChar( Value );
+ Result := Perform( WM_USER+123 {EM_FINDTEXTW}, Flags, Integer( @FT ) );
+end;
+ {$ENDIF}{$ENDIF}
+
+{$ENDIF NOT_USE_RICHEDIT}
+
+function TControl.CanUndo: Boolean;
+begin
+ Result := LongBool( Perform( EM_CANUNDO, 0, 0 ) );
+end;
+
+procedure TControl.EmptyUndoBuffer;
+begin
+ Perform( EM_EMPTYUNDOBUFFER, 0, 0 );
+end;
+
+function TControl.Undo: Boolean;
+begin
+ Result := LongBool( Perform( EM_UNDO, 0, 0 ) );
+end;
+
+{$IFNDEF NOT_USE_RICHEDIT}
+function TControl.RE_Redo: Boolean;
+begin
+ Result := LongBool( Perform( EM_REDO, 0, 0 ) );
+end;
+
+function TControl.REGetAutoURLDetect: Boolean;
+begin
+ Result := LongBool( Perform( EM_GETAUTOURLDETECT, 0, 0 ) );
+end;
+
+procedure TControl.RESetAutoURLDetect(const Value: Boolean);
+begin
+ AttachProc( WndProc_RE_LinkNotify );
+ Perform( EM_AUTOURLDETECT, Integer( Value ), 0 );
+end;
+
+procedure TControl.RESetZoom( const Value: TSmallPoint );
+begin
+ Perform( EM_SETZOOM, Value.x, Value.y );
+end;
+
+function TControl.REGetZoom: TSmallPoint;
+var P: TPoint;
+begin
+ Perform( EM_GETZOOM, Integer( @ P.X ), Integer( @ P.Y ) );
+ Result := Point2SmallPoint( P );
+end;
+
+function TControl.GetMaxTextSize: DWORD;
+begin
+ Result := Perform( EM_GETLIMITTEXT, 0, 0 );
+end;
+
+procedure TControl.SetMaxTextSize(const Value: DWORD);
+var V1, V2: Integer;
+begin
+ if fCommandActions.aSetLimit <> 0 then
+ begin
+ V1 := 0; V2 := Value;
+ if fCommandActions.aSetLimit = EM_SETLIMITTEXT then
+ begin
+ V1 := Value; V2 := 0;
+ end;
+ Perform( fCommandActions.aSetLimit, V1, V2 );
+ end;
+end;
+
+function WndProc_REFmt( _Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var Mask: Integer;
+ Shft, Alt, Ctrl, Flg: Boolean;
+ Delta: Integer;
+ TA: TRichTextAlign;
+ ChgTA: Boolean;
+ US: TRichUnderline;
+ NS: TRichNumbering;
+ NB: TRichNumBrackets;
+ Side: TBorderEdge;
+ Param: DWORD;
+begin
+ Result := False;
+ if Msg.message = WM_CHAR then
+ if _Self_.DF.FSupressTab then
+ begin
+ _Self_.DF.FSupressTab := FALSE;
+ if Msg.wParam = 9 then
+ begin
+ Result := TRUE; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ if (Msg.message = WM_KEYDOWN) or (Msg.message = WM_SYSKEYDOWN) then
+ begin
+ Ctrl := GetKeyState( VK_CONTROL ) < 0;
+ Alt := GetKeyState( VK_MENU ) < 0;
+ Param := Msg.wParam;
+ if Ctrl or
+ Alt and IntIn(Param, [ VK_ADD, VK_SUBTRACT, Integer( '-' ), Integer( '=' ),
+ Integer( '+' ), 189 , 187 ]) then
+ begin
+ Shft := GetKeyState( VK_SHIFT ) < 0;
+ Rslt := 0;
+ Result := True;
+ Mask := 0;
+ ChgTA := False; TA := raLeft;
+ case Param of
+ Integer('Z'):
+ begin
+ if Shft then
+ begin
+ _Self_.RE_Redo; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Result := False;
+ end;
+
+ Integer('L'): begin ChgTA := True; TA := raLeft; end;
+ Integer('R'): begin ChgTA := True; TA := raRight; end;
+ Integer('E'): begin ChgTA := True; TA := raCenter; end;
+ Integer('J'): begin ChgTA := True; TA := raJustify; end;
+ Integer('N'): begin
+ if Shft then
+ begin
+ NS := _Self_.RE_NumStyle;
+ NB := _Self_.RE_NumBrackets;
+ if NS = rnBullets then
+ begin
+ _Self_.RE_NumStyle := rnNone; Exit; {>>>>>>>>>>>>>>>>>>}
+ end;
+ if NS = rnNone then
+ begin
+ _Self_.RE_NumStyle := rnBullets; Exit; {>>>>>>>>>>>>>>>}
+ end
+ else
+ if Ord( NB ) = 0 then
+ NB := High(NB) else
+ NB := Pred(NB);
+ _Self_.RE_NumBrackets := NB;
+ end
+ else
+ begin
+ NS := _Self_.RE_NumStyle;
+ if Ord( NS ) = 0 then
+ begin
+ NS := rnURoman; //rnULetter; //High( NS );
+ { because rnLRoman, rnURoman, rnNoNumber are not shown
+ in RichEdit. }
+ _Self_.RE_NumBrackets := rnbPeriod;
+ end else
+ NS := Pred(NS);
+ _Self_.RE_NumStyle := NS;
+ if NS in [ rnLRoman, rnURoman, rnArabic ] then
+ _Self_.RE_NumStart := 1;
+ end;
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Integer('W'): begin
+ Delta := _Self_.RE_BorderWidth[ beLeft ] + 4;
+ if Shft then Delta := -1;
+ for Side := Low(Side) to High(Side) do
+ begin
+ if Delta < 0 then
+ _Self_.RE_BorderStyle[ Side ] := _Self_.RE_BorderStyle[ Side ] + 1
+ else
+ begin
+ _Self_.RE_BorderWidth[ Side ] := Delta;
+ _Self_.RE_BorderSpace[ Side ] := Delta;
+ end;
+ end;
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ (* TABLES STUFF -- to try, uncomment it and press CTRL+T in RichEdit.
+ (and uncomment declaration for Tmp above).
+
+ Not finished, and seems no way to figure it out - even RichEdit20.dll
+ (i.e. Rich Edit v3.0) can not display tables properly formatted. :(((
+
+ Integer('T'): begin
+ if _Self_.RE_Table then
+ begin
+ //MsgOK( 'table' );
+ end;
+ Tmp := _Self_.REReadText( reRTF, True );
+ if StrIsStartingFrom( PAnsiChar(Tmp), '{\rtf' )
+ and (CopyTail( Tmp, 3 ) = '}'#$D#$A) then
+ begin
+ //Tmp := Copy( Tmp, 1, Length(Tmp) - 3 );
+ _Self_.RE_Text[ reRTF, True ] := '{\rtf1' + //Copy( Tmp, 1, 6 ) +
+ '\trowd' +
+ //'\lytcalctblwd' +
+ //'\oldlinewrap' +
+ //'\alntblind' +
+ //'\trgaph108' +
+ '\trleft-108' +
+ {'\trbrdrt\brdrs\brdrw10' +
+ '\trbrdrl\brdrs\brdrw10' +
+ '\trbrdrb\brdrs\brdrw10' +
+ '\trbrdrr\brdrs\brdrw10' +
+ '\trbrdrh\brdrs\brdrw10' +
+ '\trbrdrv\brdrs\brdrw10' +}
+ //'\clvertalt' +
+ {'\clbrdrt\brdrs\brdrw10' +
+ '\clbrdrl\brdrs\brdrw10' +
+ '\clbrdrb\brdrs\brdrw10' +
+ '\clbrdrr\brdrs\brdrw10' +}
+ //'\cltxlrtb' +
+ '\cellx1414' +
+ //'\pard' +
+ //'\plain' +
+ //'\widctlpar' +
+ '\trautofit1' +
+ '\intbl' +
+ //'\adjustright' +
+ //'\fs20\lang1049' +
+ //'\cgrid' +
+ '\trrh0' +
+ '{\clFitText{{\box\brdrs\brdrw20\brsp20}'+
+ '\par}\cell\row}' +
+ //'\pard\widctlpar' +
+ //'\intbl'+
+ //'\adjustright'+
+ //'{\row}' +
+ '\pard\widctlpar' +
+ '}'#$D#$A;
+ _Self_.Perform( WM_KEYDOWN, VK_UP, 0 );
+ _Self_.Perform( WM_KEYUP, VK_UP, 0 );
+ end; Exit;
+ end;
+ *)
+ Integer('B'): Mask := CFM_BOLD;
+ Integer('I'):
+ begin
+ Mask := CFM_ITALIC;
+ _Self_.DF.FSupressTab := TRUE;
+ end;
+ Integer('U'):
+ begin
+ if Shft then
+ begin
+ US := _Self_.RE_FmtUnderlineStyle;
+ if Ord(US) = 0 then US := High(TRichUnderLine)
+ else US := Pred( US );
+ _Self_.RE_FmtUnderlineStyle := US;
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Mask := CFM_UNDERLINE;
+ end;
+ Integer('O'): Mask := CFM_STRIKEOUT;
+ VK_SUBTRACT, VK_ADD, Integer( '+' ), 187, Integer( '-' ), 189:
+ ;
+ else
+ begin
+ Result := False;
+ Msg.wParam := Param;
+ end;
+ end;
+ if not Result then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if ChgTA then
+ begin
+ if Shft then Result := False
+ else _Self_.RE_TextAlign := TA;
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ _Self_.REGetFont;
+ if Mask > 0 then
+ begin
+ if Shft then Result := False
+ else begin
+ Flg := _Self_.REGetFontEffects( Mask );
+ if not Flg then
+ _Self_.DF.fRECharFormatRec.dwEffects := _Self_.DF.fRECharFormatRec.dwEffects and not Mask;
+ _Self_.DF.fRECharFormatRec.dwEffects := _Self_.DF.fRECharFormatRec.dwEffects xor DWORD(Mask);
+ end;
+ end
+ else
+ if IntIn( Param, [ VK_ADD, VK_SUBTRACT, Integer( '+' ),
+ Integer( '-' ), 189, 187 ] ) then
+ begin
+ if (Param = VK_SUBTRACT) or (Param = DWORD( '-' )) or (Param = 189) then
+ Delta := -1
+ else
+ Delta := 1;
+ if Alt and Ctrl then
+ begin
+ Mask := Integer( CFM_SIZE ) or Integer( CFM_OFFSET );
+ Delta := 0;
+ _Self_.DF.fRECharFormatRec.yOffset := 0;
+ _Self_.DF.fRECharFormatRec.yHeight := 200;
+ end
+ else
+ if Alt then Mask := Integer( CFM_SIZE )
+ else Mask := Integer( CFM_OFFSET );
+ Inc( _Self_.DF.fRECharFormatRec.yOffset, Delta * _Self_.DF.fRECharFormatRec.yHeight div 3 );
+ Inc( _Self_.DF.fRECharFormatRec.yHeight, Delta * _Self_.DF.fRECharFormatRec.yHeight div 8 );
+ Flg := LongBool( _Self_.DF.fRECharFormatRec.dwMask and Mask );
+ if not Flg then
+ _Self_.DF.fRECharFormatRec.yOffset := 0;
+ end;
+ _Self_.DF.fRECharFormatRec.dwMask := Mask;
+ if _Self_.SelLength = 0 then
+ _Self_.SelLength := 1;
+ _Self_.Perform( EM_SETCHARFORMAT, SCF_SELECTION { RichAreas[ _Self_.fRECharArea ] },
+ Integer( {$IFDEF STATIC_RICHEDIT_DATA} @_Self_.DF.fRECharFormatRec
+ {$ELSE} _Self_.DF.fRECharFormatRec {$ENDIF} ) );
+ end;
+ end;
+end;
+
+function TControl.RE_FmtStandard: PControl;
+begin
+ AttachProc( WndProc_REFmt );
+ Result := @Self;
+end;
+
+procedure TControl.RE_CancelFmtStandard;
+begin
+ DetachProc( WndProc_REFmt );
+end;
+{$ENDIF NOT_USE_RICHEDIT}
+
+{$IFDEF ASM_TLIST}
+function EnumDynHandlers( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+asm //cmd //opd
+ CMP [EAX].TControl.fRefCount, 0
+ JL @@fin_false
+ PUSHAD
+ MOV EBX, EAX
+ MOV EBP, ECX
+ MOV ECX, [EBX].TControl.fDynHandlers
+ JECXZ @@ret_false
+ MOV ESI, ECX
+ MOV ECX, [ESI].TList.fCount
+ JECXZ @@ret_false
+ MOV EDI, ECX
+ SHR EDI, 1
+ CALL TControl.RefInc
+@@loo: DEC EDI
+ JS @@e_loo
+ PUSH EDX
+ PUSH EBX
+{$IFNDEF SMALLEST_CODE}
+{$IFNDEF ENUM_DYN_HANDLERS_AFTER_RUN}
+ XOR EAX, EAX
+ CMP [AppletTerminated], AL
+ JZ @@do_call
+ MOV ECX, [ESI].TList.fItems
+ MOV ECX, [ECX+EDI*8+4]
+ JECXZ @@skip_call
+{$ENDIF}
+{$ENDIF}
+@@do_call:
+ MOV EAX, [ESI].TList.fItems
+ MOV EAX, [EAX+EDI*8]
+ XCHG EAX, EBX
+ MOV ECX, EBP
+ CALL EBX
+@@skip_call:
+ POP EBX
+ POP EDX
+ TEST AL, AL
+ JZ @@loo
+@@ret_true:
+ MOV EAX, EBX
+ CALL TControl.RefDec
+ POPAD
+ MOV AL, 1
+ RET
+@@e_loo:
+ XOR EAX, EAX
+ INC EAX
+ CMP [EBX].TControl.fRefCount, EAX
+ JE @@ret_true
+ MOV EAX, EBX
+ CALL TControl.RefDec
+@@ret_false:
+ POPAD
+@@fin_false:
+ XOR EAX, EAX
+end;
+{$ELSE PAS_VERSION} //Pascal
+function EnumDynHandlers( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var I: Integer;
+ Proc: TWindowFunc;
+begin
+ Result := False;
+ if Self_.fRefCount < 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if (Self_.fDynHandlers = nil) or (Self_.fDynHandlers.fCount = 0) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Self_.RefInc; // Prevent destroying Self_
+ for I := Self_.fDynHandlers.fCount div 2 - 1 downto 0 do
+ begin
+ Proc := Self_.fDynHandlers.{$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}[ I * 2 ];
+{$IFNDEF SMALLEST_CODE}
+{$IFNDEF ENUM_DYN_HANDLERS_AFTER_RUN}
+ if not AppletTerminated or (
+ Self_.fDynHandlers.{$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}
+ [ I * 2 + 1 ] <> nil) then
+{$ENDIF}
+{$ENDIF}
+ if Proc( Self_, Msg, Rslt ) then
+ begin
+ Result := True;
+ break;
+ end;
+ end;
+ {$IFDEF DEBUG_ENDSESSION}
+ if EndSession_Initiated then
+ begin
+ LogFileOutput( GetStartDir + 'es_debug.txt',
+ 'ENUM_DYN_HANDLERS: Self_:' + Int2Hex( DWORD( Self_ ), 8 ) );
+ LogFileOutput( GetStartDir + 'es_debug.txt',
+ 'ENUM_DYN_HANDLERS: Self_.fRefCount:' + Int2Str( Self_.fRefCount ) );
+ end;
+ {$ENDIF}
+ if LongBool(Self_.fRefCount and 1) then
+ Result := True; // If Self_ will be destroyed now, stop further processing
+ Self_.RefDec; // Destroy Self_, if Free was called for it while processing attached procedures
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TransparentAttachProcExtension ( DynHandlers: PList );
+var i: integer;
+begin
+ I := DynHandlers.IndexOf( @WndProcTransparent );
+ if I >=0 then begin
+ DynHandlers.Delete( I );
+ DynHandlers.Delete( I );
+ DynHandlers.Add( @WndProcTransparent );
+ DynHandlers.Add( nil );
+ end;
+end;
+
+procedure DummyAttachProcExtension ( DynHandlers: PList );
+begin
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+procedure TControl.AttachProcEx( Proc: TWindowFunc; ExecuteAfterAppletTerminated: Boolean );
+begin
+ //if fDynHandlers = nil then
+ // fDynHandlers := NewList;
+ if not IsProcAttached( Proc ) then
+ begin
+ fDynHandlers.Add( @Proc );
+ fDynHandlers.Add( Pointer( Integer( ExecuteAfterAppletTerminated ) ) );
+ end;
+ {$IFNDEF SMALLEST_CODE}
+ Global_AttachProcExtension(fDynHandlers);
+ {$ENDIF}
+ PP.fOnDynHandlers := EnumDynHandlers;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TControl.AttachProc(Proc: TWindowFunc);
+begin
+ AttachProcEx( Proc, FALSE );
+end;
+
+procedure TControl.DetachProc(Proc: TWindowFunc);
+var I: Integer;
+begin
+ if fDynHandlers = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ I := fDynHandlers.IndexOf( @Proc );
+ if I >=0 then
+ begin
+ fDynHandlers.Delete( I );
+ fDynHandlers.Delete( I );
+ end;
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal
+function TControl.IsProcAttached(Proc: TWindowFunc): Boolean;
+var I: Integer;
+begin
+ I := fDynHandlers.IndexOf( @Proc );
+ Result := I >=0;
+end;
+{$ENDIF PAS_VERSION}
+
+{$IFDEF nASM_VERSION}{$ELSE PAS_VERSION}
+function WndProcAutoPopupMenu( Control: PControl; var Msg: TMsg; var MsgRslt: Integer ): Boolean;
+var {$IFNDEF SMALLEST_CODE}
+ R: TRect;
+ M: Word;
+ I: Integer;
+ {$ENDIF SMALLEST_CODE}
+ P: TPoint;
+begin
+ if (Msg.message = WM_CONTEXTMENU) and
+ (Control.fAutoPopupMenu <> nil) then
+ begin
+ {$IFDEF USE_MENU_CURCTL}
+ PMenu( Control.fAutoPopupMenu ).fCurCtl := Control;
+ {$ENDIF USE_MENU_CURCTL}
+ P.X := SmallInt( LoWord( Msg.lParam ) );
+ P.Y := SmallInt( HiWord( Msg.lParam ) );
+ {$IFNDEF SMALLEST_CODE}
+ if (Msg.lParam = -1) then
+ begin
+ I := Control.CurIndex;
+ M := Control.fCommandActions.aItem2XY;
+ if (I >= 0) and (M <> 0) then
+ begin
+ CASE M OF
+ EM_POSFROMCHAR:
+ begin
+ I := Control.SelStart + Control.SelLength;
+ // Edit or Rich Edit 2:
+ I := Control.Perform( M, I, 1 );
+ P.X := SmallInt( LoWord( I ) );
+ P.Y := SmallInt( HiWord( I ) );
+ end;
+ LB_GETITEMRECT, LVM_GETITEMRECT, TCM_GETITEMRECT:
+ begin
+ R.Left := LVIR_BOUNDS;
+ Control.Perform( M, I, Integer( @ R ) );
+ P.X := R.Left;
+ P.Y := R.Bottom;
+ end;
+ TVM_GETITEMRECT:
+ begin
+ I := Control.TVSelected;
+ R.Left := I;
+ Control.Perform( M, 1, Integer( @ R ) );
+ P.X := R.Left;
+ P.Y := R.Bottom;
+ end;
+ END;
+ R := Control.ClientRect;
+ if P.X < R.Left then P.X := R.Left;
+ if P.X > R.Right then P.X := R.Right;
+ if P.Y < R.Top then P.Y := R.Top;
+ if P.Y > R.Bottom then P.Y := R.Bottom;
+ end;
+ P := Control.Client2Screen( P );
+ end;
+ {$ENDIF SMALLEST_CODE}
+ PMenu( Control.fAutoPopupMenu ).Popup( P.X, P.Y );
+ Result := TRUE;
+ end
+ else
+ Result := FALSE;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure TControl.SetAutoPopupMenu(PopupMenu: PObj);
+{ new version - by Alexander Pravdin. Allows to attach a submenu (e.g. of the
+ main menu) as a popup menu to a control, to avoid duplicating menu object,
+ if it is the same already as desired. }
+var pm: PMenu;
+begin
+ if PopupMenu <> nil then
+ {$IFDEF USE_MENU_CURCTL}
+ begin
+ pm := PMenu( PopupMenu );
+ if ( pm.FParentMenu <> nil ) then
+ begin
+ while pm.FControl = nil do
+ pm := pm.FParentMenu;
+ PMenu( PopupMenu ).FControl := pm.FControl;
+ end
+ else
+ begin
+ PMenu( PopupMenu ).FControl := @Self;
+ end;
+ AttachProc(WndProcAutoPopupMenu);
+ AttachProc(WndProcMenu)
+ end
+ else begin
+ DetachProc(WndProcAutoPopupMenu);
+ DetachProc(WndProcMenu);
+ end;
+ {$ELSE}
+ begin
+ pm := PMenu( PopupMenu );
+ while pm.FControl = nil do pm := pm.Parent;
+ PMenu( PopupMenu ).FControl := pm.FControl;
+ end;
+ {$ENDIF}
+ fAutoPopupMenu := PopupMenu;
+ {$IFNDEF USE_MENU_CURCTL}
+ AttachProc( WndProcAutoPopupMenu );
+ {$ENDIF}
+end;
+
+function SearchAnsiMnemonics( const S: KOLString ): KOLString;
+var I: Integer;
+ Sh: ShortInt;
+begin
+ Result := S;
+ for I := 1 to Length( Result ) do
+ begin
+ Sh := VkKeyScanEx( Result[ I ], MnemonicsLocale );
+ if Sh <> -1 then
+ Result[ I ] := KOLChar( Sh );
+ end;
+end;
+
+procedure SupportAnsiMnemonics( LocaleID: Integer );
+begin
+ MnemonicsLocale := LocaleID;
+ SearchMnemonics := SearchAnsiMnemonics;
+end;
+
+function WndProcMnemonics( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var Form: PControl;
+
+ function HandleMnemonic( Prnt: PControl ): Boolean;
+ var C: PControl;
+ XY: Integer;
+ procedure DoPressMnemonic;
+ begin
+ if Msg.message = WM_SYSKEYDOWN then
+ begin
+ //Form.DF.fPressedMnemonic := Msg.wParam;
+ C.Perform( WM_LBUTTONDOWN, MK_LBUTTON, XY );
+ end
+ else
+ begin
+ //Form.DF.fPressedMnemonic := 0;
+ C.Perform( WM_LBUTTONUP, MK_LBUTTON, XY );
+ end;
+ end;
+ var I, J: Integer;
+ R: TRect;
+ begin
+ for I := 0 to Prnt.ChildCount-1 do
+ begin
+ C := Prnt.Children[ I ];
+ if {$IFDEF USE_FLAGS} G5_IsButton in C.fFlagsG5
+ {$ELSE} C.IsButton {$ENDIF} then
+ if C.Enabled then
+ begin
+ if C.fCommandActions.aGetCount = TB_BUTTONCOUNT then
+ for J := 0 to C.Count-1 do
+ begin
+ if C.TBButtonEnabled[ J ] then
+ if pos( KOLString('&') + AnsiChar( Msg.wParam ), SearchMnemonics( C.TBButtonText[ J ] ) ) > 0 then
+ begin
+ C.fCurIndex := J;
+ C.DF.fTBCurItem := C.TBIndex2Item( J );
+ R := C.TBButtonRect[ J ];
+ XY := R.Left or (R.Top shl 16);
+ DoPressMnemonic;
+ Result := TRUE; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ if pos( KOLString('&') + AnsiChar( Msg.wParam ), SearchMnemonics( C.Caption ) ) > 0 then
+ begin
+ XY := 0;
+ DoPressMnemonic;
+ Result := TRUE; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ if HandleMnemonic( C ) then
+ begin
+ Result := TRUE; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ Result := FALSE;
+ end;
+
+{$IFDEF NEW_MENU_ACCELL}
+ function FindByCtlRef(C: PControl; Accell: TMenuAccelerator): Boolean;
+
+ function FindInMenu(M: PMenu): PMenu;
+ var
+ I: Integer;
+ SM: PMenu;
+ begin
+ for I := 0 to M.FMenuItems.Count - 1 do begin
+ Result := M.FMenuItems.Items[I];
+ if (Cardinal(Result.Accelerator) = Cardinal(Accell)) and Result.Enabled then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Result := nil;
+ for I := 0 to M.FMenuItems.Count - 1 do begin
+ SM := PMenu(M.FMenuItems.Items[I]);
+ if (SM.FMenuItems.Count > 0) then
+ Result := FindInMenu(SM);
+ if (Result <> nil) then
+ Break;
+ end;
+ end;
+
+ function FindInMenu2(M: PMenu): Boolean;
+ var
+ MI: PMenu;
+ begin
+ if (M <> nil) then begin
+ MI := FindInMenu(M);
+ if (MI <> nil) then begin
+ //M.FControl.Perform(WM_COMMAND, MI.FId, 0);
+ C.Perform(WM_COMMAND, MI.FId, 0); // fixed
+ Result := True; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ Result := False;
+ end;
+
+ var
+ Parent: PControl;
+ begin
+ Result := False;
+ if not FindInMenu2(PMenu(C.fAutoPopupMenu)) then
+ if not FindInMenu2(PMenu(C.fMenuObj)) then begin
+ Parent := C.Parent;
+ if (Parent <> nil) then
+ Result := FindByCtlRef(Parent, Accell);
+ end;
+ end;
+
+var
+ Ac: TMenuAccelerator;
+{$ENDIF}
+begin
+ Result := FALSE;
+ if (Msg.message >= WM_KEYFIRST) and (Msg.message <= WM_KEYLAST) then
+ begin
+{$IFDEF NEW_MENU_ACCELL}
+ Ac := MakeAccelerator(FVIRTKEY or GetShiftState, Msg.wParam);
+ Result := FindByCtlRef(Sender, Ac);
+{$ELSE}
+ if (Sender.fAccelTable <> 0)
+ {$IFDEF KEY_PREVIEW}
+ and (Sender.DF.fKeyPreviewCount = 0)
+ {$ENDIF}
+ then
+ Result := LongBool( TranslateAccelerator( Sender.fHandle, Sender.fAccelTable, Msg ) );
+ if not Result then
+ begin
+ if Sender.DF.fCurrentControl <> nil then
+ if Sender.DF.fCurrentControl.fAccelTable <> 0 then
+ Result := LongBool( TranslateAccelerator( Sender.DF.fCurrentControl.fHandle,
+ Sender.DF.fCurrentControl.fAccelTable, Msg ) );
+ end;
+ if not Result then
+ begin
+ Form := Sender.ParentForm;
+ if (Form <> nil) and (Form <> Sender)
+ {$IFDEF KEY_PREVIEW}
+ and (Form.DF.fKeyPreviewCount = 0)
+ {$ENDIF KEY_PREVIEW}
+ then
+ if Form.fAccelTable <> 0 then
+ Result := LongBool( TranslateAccelerator( Form.fHandle,
+ Form.fAccelTable, Msg ) );
+ end;
+{$ENDIF}
+ end;
+ if Result then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if (Msg.message = WM_SYSKEYUP) or
+ (Msg.message = WM_SYSKEYDOWN) and (GetKeyState( VK_MENU ) < 0) then
+ begin
+ Rslt := 0;
+ Form := Sender.ParentForm;
+ if Form <> nil then
+ begin
+ if AnsiChar( Msg.wParam ) in [ 'A'..'Z', '0'..'9' ] then
+ begin
+ if HandleMnemonic( Form ) then
+ begin
+ Result := TRUE; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ end;
+ end else
+ if Msg.message = WM_KEYUP then
+ begin
+ Rslt := 0;
+ Form := Sender.ParentForm;
+ if Form <> nil then
+ begin
+ if Msg.wParam = VK_MENU then
+ // if Form.DF.fPressedMnemonic <> 0 then
+ // Form.DF.fPressedMnemonic := Form.DF.fPressedMnemonic or $80000000;
+ else if AnsiChar( Msg.wParam ) in [ 'A'..'Z', '0'..'9' ] then
+ begin
+ if HandleMnemonic( Form ) then
+ begin
+ Result := TRUE; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ end;
+ end;
+ Result := FALSE;
+end;
+
+function TControl.SupportMnemonics: PControl;
+begin
+ fGlobalProcKeybd := WndProcMnemonics;
+ Result := @Self;
+end;
+
+procedure TControl.SelectAll;
+begin
+ SelStart := 0;
+ SelLength := -1; // this can be not working for some controls... //*//*
+end;
+
+{$IFnDEF NOT_USE_RICHEDIT}
+function RevokeDragDrop(wnd: HWnd): HResult; stdcall;
+ external 'ole32.dll' name 'RevokeDragDrop';
+
+function TControl.RE_NoOLEDragDrop: PControl;
+begin
+ RevokeDragDrop( Handle );
+ Result := @Self;
+end;
+{$ENDIF NOT_USE_RICHEDIT}
+
+function WndProcOnResize( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+begin
+ if Msg.message = WM_SIZE then
+ begin
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnResize ) then
+ {$ENDIF}
+ Self_.EV.fOnResize( Self_ );
+ end;
+ Result := False;
+end;
+
+procedure TControl.SetOnResize(const Value: TOnEvent);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .FOnResize := Value;
+ AttachProc( WndProcOnResize );
+end;
+
+function WndProcMove( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+begin
+ if Msg.message = WM_MOVE then
+ begin
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.FOnMove ) then
+ {$ENDIF}
+ Self_.EV.FOnMove( Self_ );
+ end;
+ Result := False;
+end;
+
+procedure TControl.SetOnMove(const Value: TOnEvent);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .FOnMove := Value;
+ AttachProc( WndProcMove );
+end;
+
+function WndProcMoving( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+begin
+ Result := False;
+ if Msg.message = WM_MOVING then
+ begin
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.FOnMoving ) then
+ {$ENDIF}
+ Self_.EV.FOnMoving( Self_, Pointer( Msg.lParam ) );
+ Rslt := 1;
+ Result := TRUE;
+ end;
+end;
+
+procedure TControl.SetOnMoving(const Value: TOnEventMoving);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .FOnMoving := Value;
+ AttachProc( WndProcMoving );
+end;
+
+{$IFNDEF NOT_USE_RICHEDIT}
+function WndProc_REBottomless( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+begin
+ if Msg.message = WM_SIZE then
+ Self_.Perform( EM_REQUESTRESIZE, 0, 0 );
+ Result := False;
+end;
+
+function TControl.RE_Bottomless: PControl;
+begin
+ AttachProc( WndProc_REBottomless );
+ Result := @Self;
+end;
+
+procedure TControl.RE_Append(const S: KOLString; ACanUndo: Boolean);
+begin
+ SelStart := TextSize;
+ if S <> '' then
+ begin
+ ReplaceSelection( S, ACanUndo );
+ SelStart := TextSize;
+ end;
+end;
+
+procedure TControl.RE_InsertRTF(const S: KOLString);
+var MS: PStream;
+begin
+ MS := NewMemoryStream;
+ MS.Size := (Length( S ) + 1) * Sizeof(KOLChar);
+ Move( S[ 1 ], MS.Memory^, ( Length( S ) + 1 ) * Sizeof( KOLChar ) );
+ RE_LoadFromStream( MS, Length( S ), reRTF, TRUE );
+ MS.Free;
+end;
+{$ENDIF NOT_USE_RICHEDIT}
+
+procedure TControl.DoSelChange;
+begin
+ {$IFDEF NIL_EVENTS}
+ if Assigned( EV.fOnSelChange ) then
+ {$ELSE}
+ if TMethod( EV.fOnSelChange ).Code <> @DummyObjProc then
+ {$ENDIF}
+ EV.fOnSelChange( @Self )
+ else
+ {$IFDEF NIL_EVENTS}
+ if Assigned( EV.fOnChangeCtl ) then
+ {$ENDIF}
+ EV.fOnChangeCtl( @Self );
+end;
+
+{$IFNDEF NOT_USE_RICHEDIT}
+function TControl.REGetUnderlineEx: TRichUnderline;
+begin
+ Result := TRichUnderline( REGetFontAttr( ((81
+ {$IFDEF UNICODE_CTRLS} + 32 {$ENDIF})
+ shl 16) or CFM_UNDERLINETYPE ) - 1 );
+end;
+
+procedure TControl.RESetUnderlineEx(const Value: TRichUnderline);
+begin
+ RESetFontAttr( ((81
+ {$IFDEF UNICODE_CTRLS} + 32 {$ENDIF})
+ shl 16) or CFM_UNDERLINETYPE, Ord( Value ) + 1 );
+ RESetFontEffect( CFM_UNDERLINE, True );
+end;
+
+function TControl.GetTextSize: Integer;
+begin
+ Result := 0;
+ if fHandle <> 0 then
+ Result := GetWindowTextLength( fHandle );
+end;
+
+function TControl.REGetTextSize(Units: TRichTextSize): Integer;
+const TextLengthFlags: array[ TRichTextSizes ] of Integer =
+ ( not GTL_UseCRLF, not GTL_Precise, GTL_Close, GTL_NUMBytes );
+var GTL: TGetTextLengthEx;
+begin
+ GTL.flags := MakeFlags( @Units, TextLengthFlags );
+ if not(rtsBytes in Units) then
+ GTL.flags := GTL.flags or GTL_NUMCHARS;
+ GTL.codepage := CP_ACP;
+ Result := Perform( EM_GETTEXTLENGTHEX, Integer( @GTL ), 0 );
+end;
+
+function TControl.RE_TextSizePrecise: Integer;
+var gtlex : TGetTextLengthEx;
+begin
+ gtlex.flags := GTL_PRECISE;
+ gtlex.codepage := CP_ACP;
+ Result := Perform(EM_GETTEXTLENGTHEX,WPARAM(@gtlex), 0 );
+end;
+
+function TControl.REGetNumStyle: TRichNumbering;
+begin
+ Result := TRichNumbering( ReGetParaAttr( 9 shl 16 ) );
+end;
+
+procedure TControl.RESetNumStyle(const Value: TRichNumbering);
+begin
+ RESetParaAttr( (9 shl 16) or PFM_NUMBERING, Ord( Value ) );
+end;
+
+function TControl.REGetNumBrackets: TRichNumBrackets;
+begin
+ REGetParaAttr( 0 );
+ Result := TRichNumBrackets( (DF.fREParaFmtRec.wNumberingStyle shr 8) );
+end;
+
+procedure TControl.RESetNumBrackets(const Value: TRichNumBrackets);
+begin
+ REGetParaAttr( 0 );
+ DF.fREParaFmtRec.wNumberingStyle := DF.fREParaFmtRec.wNumberingStyle and $F8FF
+ or Word( Ord( Value ) shl 8 );
+ DF.fREParaFmtRec.dwMask := PFM_NUMBERINGSTYLE;
+ RE_ParaFmt := {$IFDEF STATIC_RICHEDIT_DATA} DF.fREParaFmtRec
+ {$ELSE} DF.fREParaFmtRec^ {$ENDIF};
+end;
+
+function TControl.REGetNumTab: Integer;
+begin
+ REGetParaAttr( 0 );
+ Result := DF.fREParaFmtRec.wNumberingTab;
+end;
+
+procedure TControl.RESetNumTab(const Value: Integer);
+begin
+ REGetParaAttr( 0 );
+ DF.fREParaFmtRec.wNumberingTab := Value;
+ DF.fREParaFmtRec.dwMask := PFM_NUMBERINGTAB;
+ RE_ParaFmt := {$IFDEF STATIC_RICHEDIT_DATA} DF.fREParaFmtRec
+ {$ELSE} DF.fREParaFmtRec^ {$ENDIF};
+end;
+
+function TControl.REGetNumStart: Integer;
+begin
+ REGetParaAttr( 0 );
+ Result := DF.fREParaFmtRec.wNumberingStart;
+end;
+
+procedure TControl.RESetNumStart(const Value: Integer);
+begin
+ REGetParaAttr( 0 );
+ DF.fREParaFmtRec.wNumberingStart := Value;
+ DF.fREParaFmtRec.dwMask := PFM_NUMBERINGSTART;
+ RE_ParaFmt := {$IFDEF STATIC_RICHEDIT_DATA} DF.fREParaFmtRec
+ {$ELSE} DF.fREParaFmtRec^ {$ENDIF};
+end;
+
+function TControl.REGetSpacing( const Index: Integer ): Integer;
+begin
+ REGetParaAttr( 0 );
+ Result := PInteger( Integer(@DF.fREParaFmtRec.dySpaceBefore) + (Index and $F) )^;
+end;
+
+procedure TControl.RESetSpacing(const Index, Value: Integer);
+begin
+ REGetParaAttr( 0 );
+ PInteger( Integer(@DF.fREParaFmtRec.dySpaceBefore) + (Index and $F) )^ := Value;
+ DF.fREParaFmtRec.dwMask := Index and not $F;
+ RE_ParaFmt := {$IFDEF STATIC_RICHEDIT_DATA} DF.fREParaFmtRec
+ {$ELSE} DF.fREParaFmtRec^ {$ENDIF};
+end;
+
+function TControl.REGetSpacingRule: Integer;
+begin
+ REGetParaAttr( 0 );
+ Result := DF.fREParaFmtRec.bLineSpacingRule;
+end;
+
+procedure TControl.RESetSpacingRule(const Value: Integer);
+begin
+ REGetParaAttr( 0 );
+ DF.fREParaFmtRec.bLineSpacingRule := Value;
+ DF.fREParaFmtRec.dwMask := PFM_LINESPACING;
+ RE_ParaFmt := {$IFDEF STATIC_RICHEDIT_DATA} DF.fREParaFmtRec
+ {$ELSE} DF.fREParaFmtRec^ {$ENDIF};
+end;
+
+function TControl.REGetLevel: Integer;
+begin
+ REGetParaAttr( 0 );
+ Result := DF.fREParaFmtRec.bCRC;
+end;
+
+function TControl.REGetBorder(Side: TBorderEdge; const Index: Integer): Integer;
+begin
+ REGetParaAttr( 0 );
+ Result := PWORD( Integer(@DF.fREParaFmtRec.wBorderSpace) + Index )^ shr (Ord(Side) * 4);
+end;
+
+procedure TControl.RESetBorder(Side: TBorderEdge; const Index: Integer;
+ const Value: Integer);
+var Mask: Word;
+ pW : PWord;
+begin
+ REGetParaAttr( 0 );
+ pw := PWORD( Integer(@DF.fREParaFmtRec.wBorderSpace) + Index );
+ Mask := $F shl (Ord(Side) * 4);
+ pw^ := pw^ and not Mask or (Value shl (4 * Ord(Side)) );
+ DF.fREParaFmtRec.dwMask := PFM_BORDER;
+ RE_ParaFmt := {$IFDEF STATIC_RICHEDIT_DATA} DF.fREParaFmtRec
+ {$ELSE} DF.fREParaFmtRec^ {$ENDIF};
+end;
+
+function TControl.REGetParaEffect(const Index: Integer): Boolean;
+begin
+ Result := LongBool( HiWord( REGetParaAttr( 8 shl 16 ) ) and Index );
+end;
+
+procedure TControl.RESetParaEffect(const Index: Integer;
+ const Value: Boolean);
+var Idx: Integer;
+begin
+ REGetParaAttr( 0 );
+ DF.fREParaFmtRec.wReserved := Index;
+ Idx := Index;
+ DF.fREParaFmtRec.dwMask := Idx shl 16;
+ RE_ParaFmt := {$IFDEF STATIC_RICHEDIT_DATA} DF.fREParaFmtRec
+ {$ELSE} DF.fREParaFmtRec^ {$ENDIF};
+end;
+
+function WndProc_REMonitorIns( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+begin
+ Result := False;
+ if (Msg.message = WM_KEYDOWN) and (Msg.wParam = VK_INSERT) and
+ ((GetKeyState(VK_CONTROL) or GetKeyState(VK_SHIFT) or GetKeyState(VK_MENU)) >= 0) then
+ begin
+ if not Self_.DF.fReOvrDisable then
+ Self_.DF.fREOvr := not Self_.DF.fREOvr
+ else
+ Result := True;
+ {$IFDEF NIL_EVENTS}
+ if assigned( Self_.EV.fOnREInsModeChg ) then
+ {$ENDIF}
+ Self_.EV.fOnREInsModeChg( Self_ );
+ end;
+end;
+
+function TControl.REGetOverwite: Boolean;
+begin
+ AttachProc( WndProc_REMonitorIns );
+ Result := DF.fREOvr;
+end;
+
+procedure TControl.RESetOverwrite(const Value: Boolean);
+begin
+ if REGetOverwite = Value then // do not replace with fREOvr here!
+ Exit; // this installs monitor WndProc_REMonitorIns. {>>>>>>>>>>>>>>>>>>>>>}
+ Perform( WM_KEYDOWN, VK_INSERT, 0 );
+ Perform( WM_KEYUP, VK_INSERT, 0 );
+end;
+
+procedure TControl.RESetOvrDisable(const Value: Boolean);
+begin
+ REGetOverwite;
+ DF.fReOvrDisable := Value;
+end;
+
+function WndProc_RichEdTransp_ParentPaint( Self_:PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var I: Integer;
+ C: PControl;
+begin
+ if (Msg.message = WM_PAINT) and (Msg.wParam = 0) then
+ begin
+ for I := 0 to Self_.fChildren.fCount - 1 do
+ begin
+ C := Self_.fChildren.{$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}[ I ];
+ if {$IFDEF USE_FLAGS} G5_IsCommonCtl in C.fFlagsG5
+ {$ELSE} C.fIsCommonControl {$ENDIF} then
+ begin
+ Inc( C.DF.fREUpdCount );
+ PostMessage( C.fHandle, CM_NCUPDATE, C.DF.fREUpdCount, WM_PAINT );
+ InvalidateRect( C.fHandle, nil, False );
+ end;
+ end;
+ end;
+ Result := False;
+end;
+
+function WndProc_RichEdTransp_Update( Self_:PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var Rgn, Rgn1: HRgn;
+ R, CR: TRect;
+ Pt: TPoint;
+ VW, HH, VH, HW: Integer;
+begin
+ if Self_.DF.fRETransparent then
+ case Msg.message of
+ WM_CHAR, WM_KILLFOCUS, WM_SETFOCUS, WM_KEYDOWN, WM_LBUTTONDOWN:
+ begin
+ PostMessage( Self_.fHandle, CM_INVALIDATE, 0, 0 );
+ end;
+ WM_PAINT:
+ if Msg.wParam = 0 then
+ begin
+ Inc( Self_.DF.fREUpdCount );
+ PostMessage( Self_.fHandle, CM_NCUPDATE, Self_.DF.fREUpdCount, Msg.message );
+ end;
+ WM_SIZE:
+ begin
+ Inc( Self_.DF.fREUpdCount );
+ PostMessage( Self_.fHandle, CM_NCUPDATE, Self_.DF.fREUpdCount, Msg.message );
+ PostMessage( Self_.fHandle, CM_INVALIDATE, 0, 0 );
+ end;
+ WM_ERASEBKGND:
+ if Msg.wParam = 0 then
+ begin
+ Inc( Self_.DF.fREUpdCount );
+ PostMessage( Self_.fHandle, CM_NCUPDATE, Self_.DF.fREUpdCount, Msg.message );
+ end;
+ WM_HSCROLL, WM_VSCROLL:
+ begin
+ Self_.DF.fREScrolling := LoWord( Msg.wParam ) <> SB_ENDSCROLL;
+ Inc( Self_.DF.fREUpdCount );
+ PostMessage( Self_.fHandle, CM_NCUPDATE, Self_.DF.fREUpdCount, Msg.message );
+ if Self_.DF.fREScrolling then
+ Self_.Invalidate;
+ end;
+ CM_INVALIDATE:
+ begin
+ Self_.Parent.Invalidate;
+ Self_.Invalidate;
+ end;
+ CM_NCUPDATE:
+ if DWORD(Msg.wParam) = DWORD(Self_.DF.fREUpdCount) then
+ begin
+ GetWindowRect( Self_.fHandle, R );
+ Windows.GetClientRect( Self_.fHandle, CR );
+ Pt.x := 0; Pt.y := 0;
+ Pt := Self_.Client2Screen( Pt );
+ OffsetRect( CR, Pt.x, Pt.y );
+ Rgn := CreateRectRgn( R.Left, R.Top, R.Right, R.Bottom );
+ if Self_.DF.fREScrolling then
+ begin
+ VW := GetSystemMetrics( SM_CXVSCROLL );
+ HH := GetSystemMetrics( SM_CYHSCROLL );
+ VH := GetSystemMetrics( SM_CYVSCROLL );
+ HW := GetSystemMetrics( SM_CXHSCROLL );
+ if CR.Right + VW <= R.Right then
+ begin
+ Rgn1 := CreateRectRgn( CR.Right, CR.Top + VH, CR.Right + VW, CR.Bottom - VH );
+ CombineRgn( Rgn, Rgn, Rgn1, RGN_DIFF );
+ DeleteObject( Rgn1 );
+ end;
+ if CR.Bottom + HH <= R.Bottom then
+ begin
+ Rgn1 := CreateRectRgn( CR.Left + HW, CR.Bottom, CR.Right - HW, CR.Bottom + HH );
+ CombineRgn( Rgn, Rgn, Rgn1, RGN_DIFF );
+ DeleteObject( Rgn1 );
+ end;
+ end;
+ Self_.Perform( WM_NCPAINT, Rgn, 0 );
+ DeleteObject( Rgn ); // Unremarked By M.Gerasimov
+ end;
+ end;
+ Result := False;
+end;
+
+function TControl.REGetTransparent: Boolean;
+begin
+ Result := Longbool(ExStyle and WS_EX_TRANSPARENT);
+end;
+
+procedure TControl.RESetTransparent(const Value: Boolean);
+begin
+ if Value then
+ ExStyle := ExStyle or WS_EX_TRANSPARENT
+ else
+ ExStyle := ExStyle and not WS_EX_TRANSPARENT;
+ DF.fRETransparent := Value;
+ fParent.AttachProc( WndProc_RichEdTransp_ParentPaint );
+ AttachProc( WndProc_RichEdTransp_Update );
+ {$IFDEF USE_FLAGS}
+ if Value then
+ include( fFlagsG2, G2_Transparent )
+ else exclude( fFlagsG2, G2_Transparent );
+ {$ELSE} fTransparent := Value; {$ENDIF}
+end;
+
+procedure TControl.RESetOnURL(const Index: Integer; const Value: TOnEvent);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents; {$ENDIF}
+ if Index = 0 then
+ EV.fOnREOverURL := Value
+ else
+ EV.fOnREURLClick := Value;
+ RE_AutoURLDetect :=
+ assigned(EV.fOnREOverURL) or assigned(EV.fOnREURLClick);
+end;
+
+procedure TControl.SetOnRE_URLClick(const Value: TOnEvent);
+begin
+ RESetOnURL( 1, Value );
+end;
+
+procedure TControl.SetOnRE_OverURL(const Value: TOnEvent);
+begin
+ RESetOnURL( 0, Value );
+end;
+
+function TControl.REGetOnURL(const Index: Integer): TOnEvent;
+begin
+ CASE Index OF
+ 0: Result := EV.fOnREOverURL;
+ else Result := EV.fOnREURLClick;
+ END;
+end;
+
+function TControl.REGetLangOptions(const Index: Integer): Boolean;
+begin
+ Result := LongBool( Perform( EM_GETLANGOPTIONS, 0, 0 ) and Index);
+end;
+
+procedure TControl.RESetLangOptions(const Index: Integer;
+ const Value: Boolean);
+var Mask: Integer;
+begin
+ Mask := -1;
+ if not Value then Inc( Mask );
+ Perform( EM_SETLANGOPTIONS, 0, Perform( EM_GETLANGOPTIONS, 0, 0 ) and
+ not Index or (Mask and Index) );
+end;
+{$ENDIF NOT_USE_RICHEDIT}
+
+function DoTrackMouseEvent(lpEventTrack: PTrackMouseEvent): BOOL;
+var FunTrack: function(lpEventTrack: PTrackMouseEvent): BOOL; stdcall;
+ ComCtlModule: THandle;
+begin
+ Result := FALSE;
+ ComCtlModule := GetModuleHandle( cctrl );
+ if ComCtlModule = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ FunTrack := GetProcAddress( ComCtlModule, '_TrackMouseEvent' );
+ if not Assigned( FunTrack ) then Exit; // is necessary for Win95! {>>>>>>>>>>}
+ Result := FunTrack( lpEventTrack );
+end;
+
+function WndProcMouseEnterLeave( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+ forward;
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+function WndProcMouseEnterLeave( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var P: TPoint;
+ MouseWasInControl: Boolean;
+ Yes: Boolean;
+ Track: TTrackMouseEvent;
+begin
+ case Msg.message of
+ WM_MOUSEFIRST..WM_MOUSELAST:
+ begin
+ MouseWasInControl := {$IFDEF USE_FLAGS} G3_MouseInCtl in Self_.fFlagsG3;
+ {$ELSE} Self_.fMouseInControl; {$ENDIF}
+ if Assigned( Self_.EV.fOnTestMouseOver ) then
+ Yes := Self_.EV.fOnTestMouseOver( Self_ )
+ else
+ begin
+ GetCursorPos( P );
+ P := Self_.Screen2Client( P );
+ Yes := PointInRect( P, Self_.ClientRect );
+ end;
+ if MouseWasInControl <> Yes then
+ begin
+ Self_.Invalidate;
+ if Yes then
+ begin
+ {$IFDEF USE_FLAGS} include( Self_.fFlagsG3, G3_MouseInCtl );
+ {$ELSE} Self_.fMouseInControl := TRUE; {$ENDIF}
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnMouseEnter ) then
+ {$ENDIF}
+ Self_.EV.fOnMouseEnter( Self_ );
+ Track.cbSize := Sizeof( Track );
+ Track.dwFlags := TME_LEAVE;
+ Track.hwndTrack := Self_.Handle;
+ DoTrackMouseEvent( @ Track );
+ Self_.Invalidate;
+ end
+ else
+ begin
+ {$IFDEF USE_FLAGS} exclude( Self_.fFlagsG3, G3_MouseInCtl );
+ {$ELSE} Self_.fMouseInControl := FALSE; {$ENDIF}
+ Track.cbSize := Sizeof( Track );
+ Track.dwFlags := TME_LEAVE or TME_CANCEL;
+ Track.hwndTrack := Self_.Handle;
+ DoTrackMouseEvent( @ Track );
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnMouseLeave ) then
+ {$ENDIF}
+ Self_.EV.fOnMouseLeave( Self_ );
+ Self_.Invalidate;
+ end;
+ end;
+ end;
+ WM_MOUSELEAVE:
+ begin
+ if {$IFDEF USE_FLAGS} G3_MouseInCtl in Self_.fFlagsG3
+ {$ELSE} Self_.fMouseInControl {$ENDIF} then
+ begin
+ {$IFDEF USE_FLAGS} exclude( Self_.fFlagsG3, G3_MouseInCtl);
+ {$ELSE} Self_.fMouseInControl := FALSE; {$ENDIF}
+ {$IFDEF GRAPHCTL_HOTTRACK}
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fMouseLeaveProc ) then
+ {$ENDIF}
+ Self_.EV.fMouseLeaveProc( Self_ );
+ {$ENDIF}
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Self_.EV.fOnMouseLeave ) then
+ {$ENDIF}
+ Self_.EV.fOnMouseLeave( Self_ );
+ Self_.Invalidate;
+ end;
+ end;
+ end;
+ Result := False;
+end;
+{$ENDIF PAS_VERSION}
+
+procedure ProvideMouseEnterLeave( Self_: PControl );
+begin
+ InitCommonControls;
+ Self_.AttachProc( WndProcMouseEnterLeave );
+end;
+
+procedure TControl.SetFlat(const Value: Boolean);
+begin
+ {$IFDEF USE_FLAGS}
+ if Value then
+ include( fFlagsG3, G3_Flat )
+ else exclude( fFlagsG3, G3_Flat );
+ exclude( fFlagsG3, G3_MouseInCtl );
+ {$ELSE}
+ fFlat := Value;
+ fMouseInControl := FALSE;
+ {$ENDIF}
+ ProvideMouseEnterLeave( @Self );
+ Invalidate;
+end;
+
+procedure TControl.SetOnMouseEnter(const Value: TOnEvent);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnMouseEnter := Value;
+ ProvideMouseEnterLeave( @Self );
+end;
+
+procedure TControl.SetOnMouseLeave(const Value: TOnEvent);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnMouseLeave := Value;
+ ProvideMouseEnterLeave( @Self );
+end;
+
+procedure TControl.SetOnTestMouseOver(const Value: TOnTestMouseOver);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnTestMouseOver := Value;
+ ProvideMouseEnterLeave( @Self );
+end;
+
+function WndProcEdTransparent( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+begin
+ if (Msg.message = WM_KEYDOWN) or
+ (Msg.message = WM_MOUSEMOVE) and (GetKeyState( VK_LBUTTON ) < 0) or
+ (Msg.message = WM_LBUTTONUP) or (Msg.message = WM_LBUTTONDOWN) then
+ Self_.Invalidate;
+ Result := False; // continue handling of a message anyway
+end;
+
+procedure TControl.EdSetTransparent(const Value: Boolean);
+begin
+ Transparent := Value;
+ AttachProc( WndProcEdTransparent );
+end;
+
+var LastHWnd: HWnd; // + Don
+function WndProcSpeedButton( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+begin
+ Result := False;
+ if Msg.message = WM_SETFOCUS then
+ begin
+ Result := TRUE;
+ Rslt := 0;
+ LastHWnd := Msg.wParam; // + don
+ end else // + Don
+ if (Msg.message = WM_CAPTURECHANGED) and
+ (Msg.lParam = 0) and
+ (LastHwnd <> 0) then
+ begin
+ SetFocus(LastHwnd);
+ LastHwnd := 0;
+ end;
+end;
+
+function TControl.LikeSpeedButton: PControl;
+var Form: PControl;
+begin
+ AttachProc( WndProcSpeedButton );
+ {$IFDEF USE_FLAGS}
+ {$ELSE} fTabstop := False; {$ENDIF}
+ Style := Style and not WS_TABSTOP;
+ Form := ParentForm;
+ if Form <> nil then
+ if Form.DF.fCurrentControl = @Self then
+ begin
+ Form.GotoControl( VK_TAB );
+ if Form.DF.fCurrentControl = @Self then
+ Form.DF.fCurrentControl := nil;
+ end;
+ Result := @Self;
+end;
+
+{ -- Unicode -- }
+function TControl.SetUnicode(Unicode: Boolean): PControl;
+begin
+ Perform( CCM_SETUNICODEFORMAT, Integer( Unicode ), 0 );
+ Result := @ Self;
+end;
+
+{ -- TabControl -- }
+
+function TControl.GetPages(Idx: Integer): PControl;
+var Item: TTCItem;
+begin
+ Item.mask := TCIF_PARAM;
+ if Perform( TCM_GETITEM, Idx, Integer( @Item ) ) = 0 then
+ Result := nil
+ else
+ Result := Pointer( Item.lParam );
+end;
+
+function TControl.TCGetItemText(Idx: Integer): KOLString;
+var TI: TTCItem;
+ Buffer: array[ 0..1023 ] of KOLChar;
+begin
+ TI.mask := TCIF_TEXT;
+ TI.pszText := @Buffer[ 0 ];
+ TI.cchTextMax := sizeof( Buffer );
+ Buffer[ 0 ] := #0;
+ Perform( TCM_GETITEM, Idx, Integer( @TI ) );
+ Result := PKOLChar( @ Buffer[ 0 ] );
+end;
+
+procedure TControl.TCSetItemText(Idx: Integer; const Value: KOLString);
+var TI: TTCItem;
+begin
+ TI.mask := TCIF_TEXT;
+ TI.pszText := PKOLChar( Value );
+ Perform( TCM_SETITEM, Idx, Integer( @TI ) );
+end;
+
+function TControl.TCGetItemImgIDx(Idx: Integer): Integer;
+var TI: TTCItem;
+begin
+ TI.mask := TCIF_IMAGE;
+ if Perform( TCM_GETITEM, Idx, Integer( @TI ) ) = 0 then
+ Result := -1
+ else Result := TI.iImage;
+end;
+
+procedure TControl.TCSetItemImgIdx(Idx: Integer; const Value: Integer);
+var TI: TTCItem;
+begin
+ TI.mask := TCIF_IMAGE;
+ TI.iImage := Value;
+ Perform( TCM_SETITEM, Idx, Integer( @TI ) );
+end;
+
+function TControl.TCGetItemRect(Idx: Integer): TRect;
+begin
+ if Perform( TCM_GETITEMRECT, Idx, Integer( @Result ) ) = 0 then
+ begin
+ Result.Left := 0;
+ Result.Right := 0;
+ Result.Top := 0;
+ Result.Bottom := 0;
+ end;
+end;
+
+procedure TControl.TC_SetPadding(cx, cy: Integer);
+begin
+ Perform( TCM_SETPADDING, 0, cx or (cy shl 16) );
+end;
+
+function TControl.TC_TabAtPos(x, y: Integer): Integer;
+type TTCHittestInfo = packed record
+ Pt: TPoint;
+ Fl: DWORD;
+ end;
+var HTI: TTCHitTestInfo;
+begin
+ HTI.Pt.x := x;
+ HTI.Pt.y := y;
+ Result := Perform( TCM_HITTEST, 0, Integer( @HTI ) );
+end;
+
+function TControl.TC_DisplayRect: TRect;
+begin
+ Windows.GetClientRect( fHandle, Result );
+ Perform( TCM_ADJUSTRECT, 0, Integer( @Result ) );
+end;
+
+function TControl.TC_IndexOf(const S: KOLString): Integer;
+begin
+ Result := TC_SearchFor( S, -1, FALSE );
+end;
+
+function TControl.TC_SearchFor(const S: KOLString; StartAfter: Integer;
+ Partial: Boolean): Integer;
+var I: Integer;
+begin
+ Result := -1;
+ for I := StartAfter+1 to Count-1 do
+ begin
+ if Partial and ( Copy( TC_Items[ I ], 1, Length( S ) ) = S ) or
+ ( TC_Items[ I ] = S ) then
+ begin
+ Result := I;
+ break;
+ end;
+ end;
+end;
+
+function TControl.TC_Insert(Idx: Integer; const TabText: KOLString;
+ TabImgIdx: Integer): PControl;
+var TI: TTCItem;
+begin
+ Result := NewPanel( @Self, esNone );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TControl:TabPage';
+ {$ENDIF}
+ {$IFDEF OLD_ALIGN}
+ Result.FAlign := caClient; //+ Galkov
+ {$IFDEF USE_FLAGS} Result.fFlagsG4 := Result.fFlagsG4 +
+ [G4_VisibleWOParent, G4_NotUseAlign];
+ {$ELSE} Result.fVisibleWoParent := TRUE;
+ Result.fNotUseAlign := True;
+ {$ENDIF}
+ {$ELSE NEW_ALIGN}
+ Result.Align := caClient; //+ Galkov
+ {$ENDIF}
+ Result.Visible := CurIndex<0;
+ TI.mask := TCIF_TEXT or TCIF_IMAGE or TCIF_PARAM;
+ TI.iImage := TabImgIdx;
+ TI.pszText := PKOLChar( TabText );
+ TI.lParam := Integer( Result );
+ Perform( TCM_INSERTITEM, Idx, Integer( @TI ) );
+ {$IFDEF OLD_ALIGN}
+ Result.BoundsRect := TC_DisplayRect;//+ Galkov
+ {$ENDIF}
+ Perform(WM_SIZE,0,0); //May be changes of margins for TabControl
+ {$IFDEF GRAPHCTL_XPSTYLES}
+ Attach_WM_THEMECHANGED(Result, XP_Themes_For_TabPanel);
+ {$ENDIF}
+end;
+
+procedure TControl.TC_Delete(Idx: Integer);
+var Page: PControl;
+begin
+ Page := TC_Pages[ Idx ];
+ if Page = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Perform( TCM_DELETEITEM, Idx, 0 );
+ Page.Free;
+ Perform(WM_SIZE,0,0); //May be changes of margins for TabControl
+end;
+
+{$IFNDEF OLD_ALIGN}
+procedure TControl.TC_InsertControl( Idx: Integer; const TabText: KOLString;
+ TabImgIdx: Integer; Page: PControl);
+var TI: TTCItem;
+begin
+ Page.Visible := CurIndex<0;
+ TI.mask := TCIF_TEXT or TCIF_IMAGE or TCIF_PARAM;
+ TI.iImage := TabImgIdx;
+ TI.pszText := PKOLChar( TabText );
+ TI.lParam := Integer( Page );
+ Perform( TCM_INSERTITEM, Idx, Integer( @TI ) );
+ Perform(WM_SIZE,0,0); //May be changes of margins for TabControl
+end;
+
+function TControl.TC_Remove( Idx: Integer ):PControl;
+begin
+ Result := TC_Pages[ Idx ];
+ if Result = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Perform( TCM_DELETEITEM, Idx, 0 );
+ Perform(WM_SIZE,0,0); //May be changes of margins for TabControl
+end;
+{$ENDIF}
+
+{ -- TreeView -- }
+
+function TControl.TVGetItemIdx(const Index: Integer): THandle;
+begin
+ Result := Perform( TVM_GETNEXTITEM, Index, 0 );
+end;
+
+procedure TControl.TVSetItemIdx(const Index: Integer;
+ const Value: THandle);
+begin
+ Perform( TVM_SELECTITEM, Index, Value );
+end;
+
+function TControl.TVGetItemNext(Item: THandle; const Index: Integer): THandle;
+begin
+ Result := Perform( TVM_GETNEXTITEM, Index, Item );
+end;
+
+function TControl.TVGetItemRect(Item: THandle; TextOnly: Boolean): TRect;
+begin
+ Result.Left := Item;
+ if Perform( TVM_GETITEMRECT, Integer( TextOnly ), Integer( @Result ) ) = 0 then
+ begin
+ Result.Left := 0;
+ Result.Right := 0;
+ Result.Top := 0;
+ Result.Bottom := 0;
+ end;
+end;
+
+function TControl.TVGetItemVisible(Item: THandle): Boolean;
+var R: TRect;
+begin
+ R := TVItemRect[ Item, False ];
+ Result := R.Bottom > R.Top;
+end;
+
+procedure TControl.TVSetItemVisible(Item: THandle; const Value: Boolean);
+begin
+ if Value then
+ Perform( TVM_ENSUREVISIBLE, 0, Item );
+end;
+
+function TControl.TVGetItemStateFlg(Item: THandle; const Index: Integer): Boolean;
+var TVI: TTVItem;
+begin
+ TVI.mask := TVIF_HANDLE or TVIF_STATE;
+ TVI.hItem := Item;
+ TVI.stateMask := Index;
+ Result := False;
+ if Perform( TVM_GETITEM, 0, Integer( @TVI ) ) <> 0 then
+ Result := (TVI.state and Index) <> 0;
+end;
+
+procedure TControl.TVSetItemStateFlg(Item: THandle; const Index: Integer;
+ const Value: Boolean);
+var TVI: TTVItem;
+begin
+ TVI.mask := TVIF_HANDLE or TVIF_STATE;
+ TVI.hItem := Item;
+ TVI.stateMask := Index;
+ TVI.state := $FFFFFFFF and Index;
+ if not Value then
+ TVI.state := 0;
+ Perform( TVM_SETITEM, 0, Integer( @TVI ) );
+end;
+
+function TControl.TVGetItemImage(Item: THandle; const Index: Integer): Integer;
+var TVI: TTVItem;
+begin
+ TVI.mask := TVIF_HANDLE or Loword( Index );
+ TVI.hItem := Item;
+ if Hiword( Index ) <> 0 then
+ begin
+ TVI.mask := TVIF_STATE or TVIF_HANDLE;
+ TVI.stateMask := Loword( Index );
+ end;
+ Result := -1;
+ if Perform( TVM_GETITEM, 0, Integer( @TVI ) ) <> 0 then
+ begin
+ if Hiword( Index ) <> 0 then
+ Result := (TVI.state shr Hiword( Index )) and $F
+ else if Loword( Index ) = TVIF_IMAGE then
+ Result := TVI.iImage
+ else Result := TVI.iSelectedImage;
+ end;
+end;
+
+procedure TControl.TVSetItemImage(Item: THandle; const Index: Integer;
+ const Value: Integer);
+var TVI: TTVItem;
+begin
+ TVI.mask := TVIF_HANDLE or Loword( Index );
+ TVI.hItem := Item;
+ TVI.iImage := Value;
+ TVI.iSelectedImage := Value;
+ if Hiword( Index ) <> 0 then
+ begin
+ TVI.mask := TVIF_STATE or TVIF_HANDLE;
+ TVI.stateMask := Loword( Index );
+ TVI.state := Value shl Hiword( Index );
+ end;
+ Perform( TVM_SETITEM, 0, Integer( @TVI ) );
+end;
+
+function TControl.TVGetItemText(Item: THandle): KOLString;
+var TVI: TTVItem;
+ Buffer: array[ 0..4095 ] of KOLChar;
+begin
+ TVI.mask := TVIF_HANDLE or TVIF_TEXT;
+ TVI.hItem := Item;
+ TVI.pszText := @Buffer[ 0 ];
+ Buffer[ 0 ] := #0;
+ TVI.cchTextMax := Sizeof( Buffer ) {$IFDEF UNICODE_CTRLS} div Sizeof( KOLChar ) {$ENDIF};
+ Perform( TVM_GETITEM, 0, Integer( @TVI ) );
+ Result := PKOLChar( @ Buffer[ 0 ] );
+end;
+
+procedure TControl.TVSetItemText(Item: THandle; const Value: KOLString);
+var TVI: TTVItem;
+begin
+ TVI.mask := TVIF_HANDLE or TVIF_TEXT;
+ TVI.hItem := Item;
+ TVI.pszText := PKOLChar( Value );
+ Perform( TVM_SETITEM, 0, Integer( @TVI ) );
+end;
+
+function TControl.TVItemPath(Item: THandle; Delimiter: KOLChar): KOLString;
+begin
+ if Item = 0 then
+ Item := TVSelected;
+ Result := '';
+ while Item <> 0 do
+ begin
+ if Result <> '' then
+ Result := KOLString(Delimiter) + Result;
+ Result := TVItemText[ Item ] + Result;
+ Item := TVItemParent[ Item ];
+ end;
+end;
+
+function TControl.TV_GetItemHasChildren(Item: THandle): Boolean;
+var TVI: TTVItem;
+begin
+ TVI.mask := TVIF_HANDLE or TVIF_CHILDREN;
+ TVI.hItem := Item;
+ Perform( TVM_GETITEM, 0, Integer( @TVI ) );
+ Result := TVI.cChildren = 1;
+end;
+
+function TControl.TV_GetItemChildCount(Item: THandle): Integer;
+var Node: THandle;
+begin
+ Result := 0;
+ Node := TVItemChild[ Item ];
+ while Node <> 0 do
+ begin
+ Inc( Result );
+ Node := TVItemNext[ Node ];
+ end;
+end;
+
+procedure TControl.TV_SetItemHasChildren(Item: THandle;
+ const Value: Boolean);
+var TVI: TTVItem;
+begin
+ TVI.mask := TVIF_HANDLE or TVIF_CHILDREN;
+ TVI.hItem := Item;
+ TVI.cChildren := 1 and Integer( Value );
+ Perform( TVM_SETITEM, 0, Integer( @TVI ) );
+end;
+
+function TControl.TVItemAtPos(x, y: Integer; var Where: DWORD): THandle;
+var HTI: TTVHitTestInfo;
+begin
+ HTI.pt.x := x;
+ HTI.pt.y := y;
+ Result := Perform( TVM_HITTEST, 0, Integer( @HTI ) );
+ Where := HTI.fl;
+end;
+
+type
+ TTVInsertStruct = packed Record
+ hParent: THandle;
+ hAfter : THandle;
+ item: TTVItem;
+ end;
+ TTVInsertStructEx = packed Record
+ hParent: THandle;
+ hAfter : THandle;
+ item: TTVItemEx;
+ end;
+
+function TControl.TVInsert(nParent, nAfter: THandle;
+ const Txt: KOLString): THandle;
+var TVIns: TTVInsertStruct;
+begin
+ TVIns.hParent := nParent;
+ TVIns.hAfter := nAfter;
+ TVIns.item.mask := TVIF_TEXT;
+ TVIns.item.pszText := PKOLChar( Txt );
+ Result := Perform( TVM_INSERTITEM, 0, Integer( @TVIns ) );
+ if fUpdateCount <= 0 then
+ Invalidate;
+end;
+
+procedure TControl.TVExpand(Item: THandle; Flags: DWORD);
+begin
+ Perform( TVM_EXPAND, Flags, Item );
+end;
+
+procedure TControl.TVSort( N: THandle );
+var a: Cardinal;
+ b: Boolean;
+begin
+ b := N = 0;
+ if b then
+ N := TVRoot;
+ while N <> 0 do
+ begin
+ a := TVItemChild[N];
+ if a > 0 then TVSort(a);
+ Perform(TVM_SORTCHILDREN, 0, N);
+ N := TVItemNext[N];
+ end;
+ if b then //moved by Tr"]f
+ Perform(TVM_SORTCHILDREN, 0, 0); //+ by YS
+end;
+
+procedure TControl.TVDelete(Item: THandle);
+begin
+ Perform( TVM_DELETEITEM, 0, Item );
+ Invalidate;
+end;
+
+function TControl.TVGetItemData(Item: THandle): Pointer;
+var TVI: TTVItem;
+begin
+ TVI.mask := TVIF_HANDLE or TVIF_PARAM;
+ TVI.hItem := Item;
+ Result := nil;
+ if Perform( TVM_GETITEM, 0, Integer( @TVI ) ) <> 0 then
+ Result := Pointer( TVI.lParam );
+end;
+
+procedure TControl.TVSetItemData(Item: THandle; const Value: Pointer);
+var TVI: TTVItem;
+begin
+ TVI.mask := TVIF_HANDLE or TVIF_PARAM;
+ TVI.hItem := Item;
+ TVI.lParam := Integer( Value );
+ Perform( TVM_SETITEM, 0, Integer( @TVI ) );
+end;
+
+procedure TControl.TVEditItem(Item: THandle);
+begin
+ Perform( TVM_EDITLABEL, 0, Item );
+end;
+
+procedure TControl.TVStopEdit(Cancel: Boolean);
+begin
+ Perform( TVM_ENDEDITLABELNOW, Integer( Cancel ), 0 );
+end;
+
+function WndProcTVRightClickSelect( Sender: PControl; var Msg: TMsg; var R: Integer ): Boolean;
+var I: Integer;
+ Where: DWORD;
+begin
+ if Msg.message = WM_RBUTTONDOWN then
+ begin
+ I := Sender.TVItemAtPos( SmallInt( Msg.lParam and $FFFF ),
+ SmallInt( Msg.lParam shr 16 ), Where );
+ if I <> 0 then
+ Sender.TVSelected := I;
+ end;
+ Result := FALSE;
+end;
+
+procedure TControl.SetTVRightClickSelect(const Value: Boolean);
+begin
+ DF.fTVRightClickSelect := Value;
+ if Value then
+ AttachProc( @WndProcTVRightClickSelect );
+end;
+
+procedure TControl.SetOnTVDelete( const Value: TOnTVDelete );
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnTVDelete := Value;
+ if fParent <> nil then
+ begin
+ fParent.Add2AutoFreeEx( Clear );
+ fParent.DetachProc( WndProcNotify );
+ fParent.AttachProcEx( WndProcNotify, TRUE );
+ end;
+ AttachProcEx( ProcTVDeleteItem, TRUE );
+end;
+
+function ClipboardHasText: Boolean;
+begin
+ Result := false;
+ if OpenClipboard( 0 ) then
+ begin
+ if IsClipboardFormatAvailable( CF_TEXT ) then
+ Result := TRUE;
+ CloseClipboard;
+ end;
+end;
+
+function Clipboard2Text: AnsiString;
+var gbl: THandle;
+ str: PAnsiChar;
+begin
+ Result := '';
+ if OpenClipboard( 0 ) then
+ begin
+ if IsClipboardFormatAvailable( CF_TEXT ) then
+ begin
+ gbl := GetClipboardData( CF_TEXT );
+ if gbl <> 0 then
+ begin
+ str := GlobalLock( gbl );
+ if str <> nil then
+ begin
+ Result := str;
+ GlobalUnlock( gbl );
+ end;
+ end;
+ end;
+ CloseClipboard;
+ end;
+end;
+
+{$IFNDEF _D2}
+function Clipboard2WText: KOLWideString;
+var gbl: THandle;
+ str: PWideChar;
+begin
+ Result := '';
+ if OpenClipboard( 0 ) then
+ begin
+ if IsClipboardFormatAvailable( CF_UNICODETEXT ) then
+ begin
+ gbl := GetClipboardData( CF_UNICODETEXT );
+ if gbl <> 0 then
+ begin
+ str := GlobalLock( gbl );
+ if str <> nil then
+ begin
+ Result := str;
+ GlobalUnlock( gbl );
+ end;
+ end;
+ end;
+ CloseClipboard;
+ end;
+end;
+{$ENDIF}
+
+function Text2Clipboard( const S: AnsiString ): Boolean;
+var gbl: THandle;
+ str: PAnsiChar;
+begin
+ Result := False;
+ if not OpenClipboard( 0 ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ EmptyClipboard;
+ if S <> '' then
+ begin
+ gbl := GlobalAlloc( GMEM_DDESHARE, Length( S ) + 1 );
+ if gbl <> 0 then
+ begin
+ str := GlobalLock( gbl );
+ Move( S[ 1 ], str^, Length( S ) + 1 );
+ GlobalUnlock( gbl );
+ Result := SetClipboardData( CF_TEXT, gbl ) <> 0;
+ end;
+ end
+ else
+ Result := True;
+ CloseClipboard;
+end;
+
+{$IFNDEF _D2}
+function WText2Clipboard( const WS: KOLWideString ): Boolean;
+var gbl: THandle;
+ str: PAnsiChar;
+begin
+ Result := False;
+ if not OpenClipboard( 0 ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ EmptyClipboard;
+ if WS <> '' then
+ begin
+ gbl := GlobalAlloc( GMEM_DDESHARE, (Length( WS ) + 1) * 2 );
+ if gbl <> 0 then
+ begin
+ str := GlobalLock( gbl );
+ Move( WS[ 1 ], str^, (Length( WS ) + 1) * 2 );
+ GlobalUnlock( gbl );
+ Result := SetClipboardData( CF_UNICODETEXT, gbl ) <> 0;
+ end;
+ end
+ else
+ Result := True;
+ CloseClipboard;
+end;
+{$ENDIF}
+
+function TControl.Size(W, H: Integer): PControl;
+var C, P: PControl;
+ dW, dH: Integer;
+begin
+ C := @Self;
+ while True do
+ begin
+ dW := 0; dH := 0;
+ P := C.FParent;
+ if C.ToBeVisible then
+ begin
+ if C.fAlign in [caLeft, caRight, caClient] then
+ begin
+ if H > 0 then
+ begin
+ dH := H - C.Height; H := 0;
+ end;
+ end;
+ if C.fAlign in [caTop, caBottom, caClient] then
+ begin
+ if W > 0 then
+ begin
+ dW := W - C.Width; W := 0;
+ end;
+ end;
+ end;
+ if (W > 0) or (H > 0) then
+ begin
+ C.SetSize( W, H );
+ if (P <> nil) // {Ralf Junker}
+ and not P.IsApplet then
+ C.ResizeParent;
+ end;
+ if (dW = 0) and (dH = 0) then break;
+ C := P; //C.FParent;
+ if C = nil then break;
+ //if not C.fIsControl then break;
+ if C.IsApplet then break;
+ W := C.Width + dW;
+ H := C.Height + dH;
+ end;
+ Result := @Self;
+end;
+{$ENDIF WIN_GDI}
+
+{$IFDEF GDI}
+procedure AutoSzProc( Self_: PObj );
+var DeltaX, DeltaY: Integer;
+ SZ: TSize; PT: TPoint;
+ Txt: KOLString;
+ Chg: Boolean;
+ R: TRect;
+ Flags: DWORD;
+{+ecm}
+ OldFont: HFONT;
+ CtlHavingFont: PControl;
+{/+ecm}
+begin
+ Txt := PControl( Self_ ).fCaption;
+ SZ.cx := 0;
+ SZ.cy := 0;
+ if Txt <> '' then
+ begin
+ if ( PControl( Self_ ).fFont <> nil ) then
+ if PControl( Self_ ).fFont.fData.Font.Italic then
+ Txt := Txt + ' ';
+ PControl( Self_ ).GetWindowHandle; // this line must be here.
+ //-- otherwise, when handle is not yet allocated,
+ // it is requested in TCanvas.GetHandle, and in result
+ // of unpredictable recursion some memory can be currupted.
+ PControl( Self_ ).Canvas.TextArea( Txt, SZ, PT );
+ if {$IFDEF USE_FLAGS} (G1_WordWrap in PControl(Self_).fFlagsG1)
+ {$ELSE} PControl( Self_ ).fWordWrap {$ENDIF}
+ and (PControl( Self_ ).fAlign <> caClient) then
+ begin
+ R := PControl( Self_ ).ClientRect;
+ Flags := DT_CALCRECT or DT_EXPANDTABS or DT_WORDBREAK;
+ CASE PControl( Self_ ).fTextAlign OF
+ taCenter: Flags := Flags or DT_CENTER;
+ taRight : Flags := Flags or DT_RIGHT;
+ END;
+ {-ecm}
+// CASE Self_.fVerticalAlign OF
+// vaCenter: Flags := Flags or DT_VCENTER;
+// vaBottom: Flags := Flags or DT_BOTTOM;
+// END;
+ {/-ecm}
+ {+ecm}
+ CtlHavingFont := PControl( Self_ );
+ while (CtlHavingFont <> nil)
+ and ( CtlHavingFont.FFont = nil ) do
+ CtlHavingFont := CtlHavingFont.Parent;
+ OldFont := 0;
+ if ( CtlHavingFont ) <> nil then
+ OldFont := SelectObject( PControl( Self_ ).Canvas.Handle, CtlHavingFont.Font.Handle );
+ {/+ecm}
+ // DrawText return the height of the text !
+ SZ.cy := DrawText( PControl( Self_ ).fCanvas.Handle, PKOLChar( Txt ), Length( Txt ), R, Flags );
+ {+ecm}
+ if ( CtlHavingFont <> nil ) then
+ SelectObject(PControl( Self_ ).Canvas.fHandle,OldFont);
+ {/+ecm}
+ SZ.cx := R.Right - R.Left;
+ //SZ.cy := R.Bottom - R.Top;
+ end;
+ end;
+ Chg := FALSE;
+ if PControl( Self_ ).FAlign in [ caNone, caLeft, caRight ] then
+ begin
+ DeltaX := PControl( Self_ ).aAutoSzX;
+ if PControl( Self_ ).Width <> SZ.cx + DeltaX then
+ begin
+ PControl( Self_ ).Width := SZ.cx + DeltaX;
+ Chg := TRUE;
+ end;
+ if PControl( Self_ ).fMinWidth > PControl( Self_ ).Width then
+ begin
+ PControl( Self_ ).Width := PControl( Self_ ).fMinWidth;
+ Chg := TRUE;
+ end;
+ end;
+ if PControl( Self_ ).FAlign in [ caNone, caTop, caBottom ] then
+ begin
+ DeltaY := PControl( Self_ ).aAutoSzY;
+ if PControl( Self_ ).Height <> SZ.cy + DeltaY then
+ begin
+ PControl( Self_ ).Height := SZ.cy + DeltaY;
+ Chg := TRUE;
+ end;
+ if PControl( Self_ ).FMinHeight > PControl( Self_ ).Height then
+ begin
+ PControl( Self_ ).Height := PControl( Self_ ).FMinHeight;
+ Chg := TRUE;
+ end;
+ end;
+ if Chg then
+ begin
+ {$IFDEF OLD_ALIGN}
+ if PControl( Self_ ).fParent <> nil then
+ Global_Align( PControl( Self_ ).fParent );
+ {$ENDIF}
+ Global_Align( Self_ );
+ end;
+end;
+{$ENDIF GDI}
+{$IFDEF _X_}
+{$IFDEF GTK}
+PROCEDURE AutoSzProc( Self_: PObj );
+VAR SZ: TSize;
+ //Txt: KOLString;
+ Chg: Boolean;
+ req_captn, req_evbox: TGtkRequisition;
+BEGIN
+ //Txt := PControl( Self_ ).fCaption;
+ SZ.cx := 0;
+ SZ.cy := 0;
+ //if Txt <> '' then
+ BEGIN
+ gtk_widget_size_request( PControl( Self_ ).fCaptionHandle, @ req_captn );
+ IF (PControl( Self_ ).fDeltaX = 0) AND
+ (PControl( Self_ ).fDeltaY = 0) THEN
+ BEGIN
+ gtk_widget_size_request( PControl( Self_ ).fEventboxHandle, @ req_evbox );
+ PControl( Self_ ).fDeltaX := Max( 0, req_evbox.width - req_captn.width );
+ PControl( Self_ ).fDeltaY := Max( 0, req_evbox.height - req_captn.height );
+ END;
+ Sz.cx := req_captn.width + PControl( Self_ ).fDeltaX;
+ Sz.cy := req_captn.height + PControl( Self_ ).fDeltaY;
+ //gtk_widget_get_size_request( PControl( Self_ ).fHandle, @ Sz.cx, @ Sz.cy );
+ END;
+ Chg := FALSE;
+ IF PControl( Self_ ).FAlign in [ caNone, caLeft, caRight ] THEN
+ BEGIN
+ //DeltaX := PControl( Self_ ).fCommandActions.aAutoSzX;
+ if PControl( Self_ ).Width <> SZ.cx {+ DeltaX} then
+ BEGIN
+ PControl( Self_ ).Width := SZ.cx {+ DeltaX};
+ Chg := TRUE;
+ END;
+ IF PControl( Self_ ).fMinWidth > PControl( Self_ ).Width THEN
+ BEGIN
+ PControl( Self_ ).Width := PControl( Self_ ).fMinWidth;
+ Chg := TRUE;
+ END;
+ END;
+ IF PControl( Self_ ).FAlign in [ caNone, caTop, caBottom ] THEN
+ begin
+ //DeltaY := PControl( Self_ ).fCommandActions.aAutoSzY;
+ IF PControl( Self_ ).Height <> SZ.cy {+ DeltaY} THEN
+ BEGIN
+ PControl( Self_ ).Height := SZ.cy {+ DeltaY};
+ Chg := TRUE;
+ END;
+ IF PControl( Self_ ).FMinHeight > PControl( Self_ ).Height THEN
+ BEGIN
+ PControl( Self_ ).Height := PControl( Self_ ).FMinHeight;
+ Chg := TRUE;
+ END;
+ END;
+ IF Chg THEN
+ BEGIN
+ {$IFDEF OLD_ALIGN}
+ if PControl( Self_ ).fParent <> nil then
+ Global_Align( PControl( Self_ ).fParent );
+ {$ENDIF}
+ Global_Align( Self_ );
+ END;
+END;
+{$ENDIF GTK}
+{$ENDIF _X_}
+
+function TControl.AutoSize(AutoSzOn: Boolean): PControl;
+begin
+ if AutoSzOn then
+ begin
+ PP.fAutoSize := AutoSzProc;
+ DoAutoSize;
+ end
+ else
+ PP.fAutoSize := DummyObjProc;
+ Result := @Self;
+end;
+
+{$IFDEF WIN_GDI}
+function TControl.IsAutoSize: Boolean;
+begin
+ Result := Assigned( PP.fAutoSize );
+end;
+
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+function TControl.GetToBeVisible: Boolean;
+begin
+ Result := {$IFDEF USE_FLAGS} (F3_Visible in fStyle.f3_Style)
+ {$ELSE} fVisible {$ENDIF}
+ or {$IFDEF USE_FLAGS} ([G4_CreateHidden, G4_VisibleWOParent]
+ * fFlagsG4 <> [])
+ or (G3_IsForm in fFlagsG3)
+ {$ELSE} fCreateHidden or fVisibleWoParent or IsForm {$ENDIF};
+ if {$IFDEF USE_FLAGS} G3_IsControl in fFlagsG3
+ {$ELSE} fIsControl {$ENDIF} then
+ if Parent <> nil then
+ begin
+ {$IFDEF OLD_ALIGN}
+ if {$IFDEF USE_FLAGS} G4_VisibleWOParent in fFlagsG4
+ {$ELSE} fVisibleWoParent {$ENDIF} then
+ Result := {$IFDEF USE_FLAGS} F3_Visible in fStyle.f3_Style
+ {$ELSE} fVisible {$ENDIF}
+ else
+ {$ENDIF}
+ begin
+ if Result then
+ begin
+ Parent.Visible; // needed to provide correct fVisible for a form!
+ //todo: check if necessary for USE_FLAGS ???
+ Result := Parent.ToBeVisible;
+ end;
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+///////////////////////////////////////////////////////////////////////
+// W I N D O W S
+///////////////////////////////////////////////////////////////////////
+
+{ -- Set of window-related utility functions. -- }
+type
+ PGUIThreadInfo = ^TGUIThreadInfo;
+ tagGUITHREADINFO = packed record
+ cbSize: DWORD;
+ flags: DWORD;
+ hwndActive: HWND;
+ hwndFocus: HWND;
+ hwndCapture: HWND;
+ hwndMenuOwner: HWND;
+ hwndMoveSize: HWND;
+ hwndCaret: HWND;
+ rcCaret: TRect;
+ end;
+ TGUIThreadInfo = tagGUITHREADINFO;
+
+const
+ GUI_CARETBLINKING = $00000001;
+ GUI_INMOVESIZE = $00000002;
+ GUI_INMENUMODE = $00000004;
+ GUI_SYSTEMMENUMODE = $00000008;
+ GUI_POPUPMENUMODE = $00000010;
+
+{function GetGUIThreadInfo (idThread: DWORD; var pgui: TGUIThreadinfo): BOOL; stdcall;
+ external user32 name 'GetGUIThreadInfo';}
+
+type TGUIThreadInfo_Proc = function( ThreadID: THandle; var GTI: TGUIThreadInfo )
+ : Boolean; stdcall;
+
+var Proc_GetGUIThreadInfo: TGuiThreadInfo_Proc;
+
+function GetWindowChild( Wnd: HWnd; Kind: TWindowChildKind ): HWnd;
+var GTI: TGuiThreadInfo;
+ ThreadID: THandle;
+ Module: THandle;
+begin
+ if not Assigned( Proc_GetGUIThreadInfo ) then
+ begin
+ Module := GetModuleHandle( 'User32' );
+ Proc_GetGUIThreadInfo := GetProcAddress( Module, 'GetGUIThreadInfoA' );
+ if not Assigned( Proc_GetGUIThreadInfo ) then
+ Proc_GetGUIThreadInfo := Pointer( -1 );
+ end;
+ Result := Wnd;
+ if Integer( @Proc_GetGUIThreadInfo ) = -1 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>}
+ Result := 0;
+ if Wnd = 0 then
+ ThreadID := GetCurrentThreadID
+ else
+ ThreadID := GetWindowThreadProcessID( Wnd, nil );
+ if ThreadID = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ GTI.cbSize := Sizeof( GTI );
+ if Proc_GetGUIThreadInfo( ThreadId, GTI ) then
+ begin
+ case Kind of
+ wcActive: Result := GTI.hwndActive;
+ wcFocus: Result := GTI.hwndFocus;
+ wcCapture: Result := GTI.hwndCapture;
+ wcMenuOwner: Result := GTI.hwndMenuOwner;
+ wcMoveSize: Result := GTI.hwndMoveSize;
+ wcCaret: Result := GTI.hwndCaret;
+ end;
+ end;
+end;
+
+function GetFocusedChild( Wnd: HWnd ): HWnd;
+var Tr1, Tr2: THandle;
+begin
+ Result := 0;
+ Tr1 := GetCurrentThreadId;
+ Tr2 := GetWindowThreadProcessId( Wnd, nil );
+ if Tr1 = Tr2 then
+ Result := GetFocus
+ else
+ if AttachThreadInput( Tr2, Tr1, True ) then
+ begin
+ Result := GetFocus;
+ AttachThreadInput( Tr2, Tr1, False );
+ end;
+end;
+
+function WaitFocusedWndChild( Wnd: HWnd ): HWnd;
+var T1, T2: Integer;
+ W: HWnd;
+begin
+ Sleep( 50 );
+ T1 := GetTickCount;
+ while True do
+ begin
+ W := GetTopWindow( Wnd );
+ if W = 0 then W := Wnd;
+ W := GetFocusedChild( W );
+ if W <> 0 then
+ begin
+ Wnd := W;
+ break;
+ end;
+ T2 := GetTickCount;
+ if Abs( T1 - T2 ) > 100 then break;
+ end;
+ Result := Wnd;
+end;
+
+function Stroke2Window( Wnd: HWnd; const S: AnsiString ): Boolean;
+var P: PAnsiChar;
+begin
+ Result := False;
+ Wnd := WaitFocusedWndChild( Wnd );
+ if Wnd = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ P := PAnsiChar( S );
+ while P^ <> #0 do
+ begin
+ PostMessage( Wnd, WM_CHAR, Integer( P^ ), 1 );
+ Inc( P );
+ end;
+ Result := True;
+end;
+
+function Stroke2WindowEx( Wnd: HWnd; const S: AnsiString; Wait: Boolean ): Boolean;
+var P: PAnsiChar;
+ EndChar: AnsiChar;
+ MsgDn, MsgUp, SCA: Integer;
+
+ function Compare( Pattern: PAnsiChar ): Boolean;
+ var Pos: PAnsiChar;
+ C1, C2: AnsiChar;
+ begin
+ Pos := P;
+ while Pattern^ <> #0 do
+ begin
+ C1 := Pattern^;
+ C2 := Pos^;
+ if (C1 >= 'a') and (C1 <= 'z') then
+ C1 := AnsiChar( Ord( C1 ) - $20 );
+ if (C2 >= 'a') and (C2 <= 'z') then
+ C2 := AnsiChar( Ord( C2 ) - $20 );
+ if C1 <> C2 then
+ begin
+ Result := False; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Inc( Pos );
+ Inc( Pattern );
+ end;
+ while Pos^ = ' ' do Inc( Pos );
+ P := Pos;
+ Result := True;
+ end;
+
+ procedure Send( Msg, KeyCode: Integer );
+ var lParam: Integer;
+ begin
+ Wnd := WaitFocusedWndChild( Wnd );
+ if Wnd = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ lParam := 1;
+ if longBool( SCA and 4 ) then
+ lParam := $20000001;
+ if Msg = MsgUp then
+ lParam := lParam or Integer($D0000000);
+ PostMessage( Wnd, Msg, KeyCode, lParam );
+ Applet.ProcessMessages;
+ if Wait then
+ Sleep( 50 );
+ end;
+
+ function CompareSend( Pattern: PAnsiChar; Value2Send: Integer ): Boolean;
+ begin
+ if Compare( Pattern ) then
+ begin
+ Send( MsgDn, Value2Send );
+ Send( MsgUp, Value2Send );
+ Result := True;
+ end
+ else
+ Result := False;
+ end;
+
+ function ParseKeys( EndChar: AnsiChar ): PAnsiChar;
+ var FN: Integer;
+ begin
+ SCA := 0;
+ while not (P^ in [ #0, EndChar ]) do
+ begin
+ if Compare( 'Shift' ) then SCA := SCA or 1
+ else
+ if Compare( 'Ctrl' ) then SCA := SCA or 2
+ else
+ if Compare( 'Alt' ) then SCA := SCA or 4
+ else
+ break;
+ end;
+ MsgDn := WM_KEYDOWN;
+ MsgUp := WM_KEYUP;
+ if LongBool( SCA and 4 ) then
+ begin
+ MsgDn := WM_SYSKEYDOWN;
+ MsgUp := WM_SYSKEYUP;
+ keybd_event( VK_MENU, 0, 0, 0 );
+ Send( WM_SYSKEYDOWN, VK_MENU );
+ end;
+ if LongBool( SCA and 2 ) then
+ begin
+ keybd_event( VK_CONTROL, 0, 0, 0 );
+ Send( WM_KEYDOWN, VK_CONTROL );
+ end;
+ if Longbool( SCA and 1 ) then
+ begin
+ keybd_event( VK_SHIFT, 0, 0, 0 );
+ Send( WM_KEYDOWN, VK_SHIFT );
+ end;
+ while not (P^ in [ #0, EndChar ]) do
+ begin
+ if (P^ = 'F') and (P[ 1 ] in [ '1'..'9' ]) then
+ begin
+ Inc( P );
+ FN := Ord( P^ ) - Ord( '0' );
+ if (FN = 1) and (P[ 1 ] in [ '0'..'2' ]) then
+ begin
+ Inc( P );
+ FN := 10 + Ord( P^ ) - Ord( '0' );
+ end;
+ repeat Inc( P ) until P^ <> ' ';
+ FN := FN + $6F;
+ Send( MsgDn, FN );
+ Send( MsgUp, FN );
+ end
+ else
+ if Compare( 'Numpad' ) then
+ begin
+ if P^ in [ '0'..'9' ] then
+ begin
+ FN := Ord( P^ ) - Ord( '0' ) + $60;
+ repeat Inc( P^ ) until P^ <> ' ';
+ Send( MsgDn, FN );
+ Send( MsgUp, FN );
+ end;
+ end
+ else
+ if not (CompareSend( 'Add', $6B ) or
+ CompareSend( 'Gray+', $6B ) or
+ CompareSend( 'Apps', $5D ) or
+ CompareSend( 'BackSpace', $08 ) or
+ CompareSend( 'BkSp', $08 ) or
+ CompareSend( 'BS', $08 ) or
+ CompareSend( 'Break', $13 ) or
+ CompareSend( 'CapsLock', $14 ) or
+ CompareSend( 'Clear', $0C ) or
+ CompareSend( 'Decimal', $6E ) or
+ CompareSend( 'Del', $2E ) or
+ CompareSend( 'Delete', $2E ) or
+ CompareSend( 'Divide', $6F ) or
+ CompareSend( 'Gray/', $6F ) or
+ CompareSend( 'Down', $28 ) or
+ CompareSend( 'End', $23 ) or
+ CompareSend( 'Enter', $0D ) or
+ CompareSend( 'Return', $0D ) or
+ CompareSend( 'CR', $0D ) or
+ CompareSend( 'Esc', $1B ) or
+ CompareSend( 'Escape', $1B ) or
+ CompareSend( 'Help', $2F ) or
+ CompareSend( 'Home', $24 ) or
+ CompareSend( 'Ins', $2D ) or
+ CompareSend( 'Insert', $2D ) or
+ CompareSend( 'Left', $25 ) or
+ CompareSend( 'LWin', $5B ) or
+ CompareSend( 'Multiply', $6A ) or
+ CompareSend( 'Gray*', $6A ) or
+ CompareSend( 'NumLock', $90 ) or
+ CompareSend( 'PgDn', $22 ) or
+ CompareSend( 'PgUp', $21 ) or
+ CompareSend( 'PrintScrn', $2C ) or
+ CompareSend( 'Right', $27 ) or
+ CompareSend( 'RWin', $5C ) or
+ CompareSend( 'Separator', $6C ) or
+ CompareSend( 'ScrollLock', $91 ) or
+ CompareSend( 'Subtract', $6D ) or
+ CompareSend( 'Tab', $09 ) or
+ CompareSend( 'Gray-', $6D ) or
+ CompareSend( 'Up', $26 )) then break;
+ end;
+ while not (P^ in [ #0, EndChar ]) do
+ begin
+ if P^ in [ 'A'..'Z', '0'..'9' ] then
+ begin
+ Send( MsgDn, Integer( P^ ) );
+ Send( MsgUp, Integer( P^ ) );
+ end
+ else
+ if P^ in [ #1..#255 ] then
+ Stroke2Window( Wnd, AnsiString('') + P^ );
+ repeat Inc( P ) until (P^ <> AnsiString(' '));
+ end;
+ if P^ = EndChar then
+ Inc( P );
+ if Longbool( SCA and 1 ) then
+ begin
+ Send( WM_KEYUP, VK_SHIFT );
+ keybd_event( VK_SHIFT, 0, KEYEVENTF_KEYUP, 0 );
+ end;
+ if LongBool( SCA and 2 ) then
+ begin
+ Send( WM_KEYUP, VK_CONTROL );
+ keybd_event( VK_CONTROL, 0, KEYEVENTF_KEYUP, 0 );
+ end;
+ if LongBool( SCA and 4 ) then
+ begin
+ Send( WM_SYSKEYUP, VK_MENU );
+ keybd_event( VK_MENU, 0, KEYEVENTF_KEYUP, 0 );
+ end;
+ Result := P;
+ end;
+
+begin
+ Result := False;
+ Wnd := GetTopWindow( Wnd );
+ Wnd := GetFocusedChild( Wnd );
+ if Wnd = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ P := PAnsiChar( S );
+ while P^ <> #0 do
+ begin
+ if not (P^ in [ '[', '{' ]) then
+ begin
+ Stroke2Window( Wnd, AnsiString('') + P^ ); // TODO: adjust compile options?
+ Inc( P );
+ end
+ else
+ begin
+ if P^ = '[' then
+ EndChar := ']'
+ else
+ EndChar := '}';
+ Inc( P );
+ P := ParseKeys( EndChar );
+ end;
+ end;
+ Result := True;
+end;
+
+type
+ PHWnd = ^HWnd;
+
+ TFindWndRec = packed Record
+ ThreadID : DWord;
+ WndFound : HWnd;
+ end;
+ PFindWndRec = ^TFindWndRec;
+
+function EnumWindowsProc( Wnd : HWnd; Find : PFindWndRec ) : Boolean;
+stdcall;
+var Id : DWord;
+begin
+ Result := True;
+ Id := GetWindowThreadProcessId( Wnd, @Id );
+ if Id = Find.ThreadID then
+ begin
+ Find.WndFound := Wnd;
+ Result := False;
+ end;
+end;
+
+function FindWindowByThreadID( ThreadID : DWORD ) : HWnd;
+var Find : TFindWndRec;
+begin
+ Find.ThreadID := ThreadID;
+ Find.WndFound := 0;
+ EnumWindows( @EnumWindowsProc, Integer( @Find ) );
+ Result := Find.WndFound;
+end;
+
+function DesktopPixelFormat: TPixelFormat;
+var DC: HDC;
+ Nbits_per_pixel, Nplanes: Integer;
+begin
+ DC := GetDC( 0 );
+ Nbits_per_pixel := GetDeviceCaps( DC, BITSPIXEL );
+ Nplanes := GetDeviceCaps( DC, PLANES );
+ ReleaseDC( 0, DC );
+ CASE Nplanes * Nbits_per_pixel OF
+ 1: Result := pf1bit;
+ 4: Result := pf4bit;
+ 8: Result := pf8bit;
+ 16: Result := pf16bit;
+ 24, 32: Result := pf32bit;
+ else Result := pfDevice;
+ END;
+end;
+
+function GetDesktopRect : TRect;
+var W1, W2 : HWnd;
+begin
+ if WinVer >= wvVista then
+ begin
+ Result := GetWorkArea; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Result := MakeRect( 0, 0, GetSystemMetrics( SM_CXSCREEN ), GetSystemMetrics( SM_CYSCREEN ) );
+ W2 := findwindow('Progman',nil);
+ W1 := findwindowex(W2,0,'SHELLDLL_DefView',nil);
+ if W1 = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ GetWindowRect( W1, Result );
+end;
+
+function GetWorkArea: TRect;
+begin
+ SystemParametersInfo( SPI_GETWORKAREA, 0, @ Result, 0 );
+end;
+
+function ExecuteWait( const AppPath, CmdLine, DfltDirectory: KOLString;
+ Show: DWORD; TimeOut: DWORD; ProcID: PDWORD ): Boolean;
+var Flags: DWORD;
+ Startup: TStartupInfo;
+ ProcInf: TProcessInformation;
+ DfltDir: PKOLChar;
+ App: KOLString;
+begin
+ Result := FALSE;
+ Flags := CREATE_NEW_CONSOLE;
+ if Show = SW_HIDE then
+ Flags := Flags or {$IFDEF F_P}$08000000{$ELSE}CREATE_NO_WINDOW{$ENDIF};
+ ZeroMemory( @Startup, SizeOf( Startup ) );
+ Startup.cb := Sizeof( Startup );
+ Startup.wShowWindow := Show;
+ Startup.dwFlags := STARTF_USESHOWWINDOW;
+ if ProcID <> nil then
+ ProcID^ := 0;
+ DfltDir := nil;
+ if DfltDirectory <> '' then
+ DfltDir := PKOLChar( DfltDirectory );
+ App := AppPath;
+ //if (pos( KOLString(' '), App ) > 0) and (pos( KOLString('"'), App ) <= 0) then
+ if (App <> '') and (App[1] <> '"') and (pos( KOLString(' '), App ) > 0) then
+ App := '"' + App + '"';
+ if (App <> '') and (CmdLine <> '') then
+ App := App + ' ';
+ if CreateProcess( nil, PKOLChar( App + CmdLine ), nil,
+ nil, FALSE, Flags, nil, DfltDir, Startup,
+ ProcInf ) then
+ begin
+ if WaitForSingleObject( ProcInf.hProcess, TimeOut ) = WAIT_OBJECT_0 then
+ begin
+ CloseHandle( ProcInf.hProcess );
+ Result := TRUE;
+ end
+ else
+ begin
+ if ProcID <> nil then
+ ProcID^ := ProcInf.hProcess;
+ end;
+ CloseHandle( ProcInf.hThread );
+ end;
+end;
+
+function ExecuteIORedirect( const AppPath, CmdLine, DfltDirectory: KOLString;
+ Show: DWORD; ProcID: PDWORD; InPipe, OutPipeWr, OutPipeRd: PHandle ): Boolean;
+var Flags: DWORD;
+ Startup: TStartupInfo;
+ ProcInf: TProcessInformation;
+ DfltDir: PKOLChar;
+ SecurityAttributes: TSecurityAttributes;
+ SaveStdOut, SaveStdIn: THandle;
+ ChildStdOutRd, ChildStdOutWr: THandle;
+ ChildStdInRd, ChildStdInWr: THandle;
+ ChildStdOutRdDup: THandle;
+ ChildStdInWrDup: THandle;
+
+ procedure Do_CloseHandle( var Handle: THandle );
+ begin
+ if Handle <> 0 then
+ begin
+ CloseHandle( Handle );
+ Handle := 0;
+ end;
+ end;
+
+ procedure Close_Handles;
+ begin
+ Do_CloseHandle( ChildStdOutRd );
+ Do_CloseHandle( ChildStdOutWr );
+ Do_CloseHandle( ChildStdInRd );
+ Do_CloseHandle( ChildStdInWr );
+ end;
+
+ function RedirectInputOutput: Boolean;
+ begin
+ Result := FALSE;
+ if (OutPipeRd <> nil) or (OutPipeWr <> nil) then
+ begin
+ // redirect output
+ SaveStdOut := GetStdHandle(STD_OUTPUT_HANDLE);
+ if not CreatePipe( ChildStdOutRd, ChildStdOutWr, @ SecurityAttributes, 0 ) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if not SetStdHandle( STD_OUTPUT_HANDLE, ChildStdOutWr ) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if not DuplicateHandle( GetCurrentProcess, ChildStdOutRd,
+ GetCurrentProcess, @ ChildStdOutRdDup, 0, FALSE,
+ 2 {DUPLICATE_SAME_ACCESS} ) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Do_CloseHandle( ChildStdOutRd );
+ if OutPipeRd <> nil then
+ OutPipeRd^ := ChildStdOutRdDup;
+ if OutPipeWr <> nil then
+ OutPipeWr^ := ChildStdOutWr;
+ end;
+ if InPipe <> nil then
+ begin
+ // redirect input
+ SaveStdIn := GetStdHandle(STD_INPUT_HANDLE);
+ if not CreatePipe( ChildStdInRd, ChildStdInWr, @ SecurityAttributes, 0 ) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if not SetStdHandle( STD_INPUT_HANDLE, ChildStdInRd ) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if not DuplicateHandle( GetCurrentProcess, ChildStdInWr,
+ GetCurrentProcess, @ ChildStdInWrDup, 0, FALSE,
+ 2 {DUPLICATE_SAME_ACCESS} ) then
+ Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Do_CloseHandle( ChildStdInWr );
+ if InPipe <> nil then
+ InPipe^ := ChildStdInWrDup;
+ Do_CloseHandle( ChildStdInRd );
+ end;
+ Result := TRUE;
+ end;
+
+ procedure Restore_Saved_StdInOut;
+ begin
+ SetStdHandle( STD_OUTPUT_HANDLE, SaveStdOut );
+ SetStdHandle( STD_INPUT_HANDLE, SaveStdIn );
+ end;
+
+var Cmd: KOLString;
+begin
+ Result := FALSE;
+ Flags := 0;
+ if Show = SW_HIDE then
+ Flags := Flags or {$IFDEF F_P}$08000000{$ELSE}CREATE_NO_WINDOW{$ENDIF};
+ ZeroMemory( @Startup, SizeOf( Startup ) );
+ Startup.cb := Sizeof( Startup );
+ if ProcID <> nil then
+ ProcID^ := 0;
+ DfltDir := nil;
+ SecurityAttributes.nLength := Sizeof( SecurityAttributes );
+ SecurityAttributes.lpSecurityDescriptor := nil;
+ SecurityAttributes.bInheritHandle := TRUE;
+ SaveStdOut := 0;
+ SaveStdIn := 0;
+ ChildStdOutRd := 0;
+ ChildStdOutWr := 0;
+ ChildStdInRd := 0;
+ ChildStdInWr := 0;
+ if not RedirectInputOutput then
+ begin
+ Close_Handles; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ if DfltDirectory <> '' then
+ DfltDir := PKOLChar( DfltDirectory );
+ Cmd := '"' + AppPath + '" ' + CmdLine;
+ if CreateProcess( nil, PKOLChar( Cmd ), nil, nil, TRUE, Flags, nil,
+ DfltDir, Startup, ProcInf ) then
+ begin
+ if ProcID <> nil then
+ ProcID^ := ProcInf.hProcess
+ else
+ CloseHandle( ProcInf.hProcess );
+ CloseHandle( ProcInf.hThread );
+ Restore_Saved_StdInOut;
+ Result := TRUE;
+ end else
+ begin
+ Restore_Saved_StdInOut;
+ Close_Handles;
+ end;
+end;
+
+function ExecuteConsoleAppIORedirect( const AppPath, CmdLine, DfltDirectory: KOLString;
+ Show: DWORD; const InStr: KOLString; var OutStr: KOLString; WaitTimeout: DWORD ): Boolean;
+var PipeIn, PipeOutRd, PipeOutWr: THandle;
+ ProcID: DWORD;
+ BytesCount: DWORD;
+ Buffer: Array[ 0..4096 ] of KOLChar; // KOL_ANSI
+ BufStr: KOLString;
+ PPipeIn: PHandle;
+begin
+ Result := FALSE;
+ PPipeIn := @ PipeIn;
+ if InStr = '' then
+ PPipeIn := nil;
+ PipeOutRd := 0;
+ PipeOutWr := 0;
+ if not ExecuteIORedirect( AppPath, CmdLine, DfltDirectory, Show, @ ProcID,
+ PPipeIn, @ PipeOutWr, @ PipeOutRd ) then Exit; {>>>>>>>>>>}
+ if PPipeIn <> nil then
+ begin
+ if InStr <> '' then
+ WriteFile( PipeIn, InStr[ 1 ], Length( InStr ), BytesCount, nil );
+ CloseHandle( PipeIn );
+ end;
+ OutStr := '';
+ if WaitForSingleObject( ProcID, WaitTimeOut ) = WAIT_OBJECT_0 then
+ begin
+ CloseHandle( ProcID );
+ CloseHandle( PipeOutWr );
+ while ReadFile( PipeOutRd, Buffer, Sizeof( Buffer ), BytesCount, nil ) do
+ begin
+ SetLength( BufStr, BytesCount );
+ Move( Buffer[ 0 ], BufStr[ 1 ], BytesCount );
+ OutStr := OutStr + BufStr;
+ end;
+ end
+ else
+ CloseHandle( PipeOutWr );
+ CloseHandle( PipeOutRd );
+ Result := TRUE;
+end;
+
+{$IFDEF _D2}
+function OpenProcessToken(ProcessHandle: THandle; DesiredAccess: DWORD;
+ var TokenHandle: THandle): BOOL; stdcall;
+ external advapi32 name 'OpenProcessToken';
+{$ENDIF}
+
+function WindowsShutdown( const Machine : KOLString; Force, Reboot : Boolean ) : Boolean;
+var
+ hToken: THandle;
+ tkp, tkp_prev: TTokenPrivileges;
+ dwRetLen :DWORD;
+ Flags: Integer;
+begin
+ Result := False;
+ if Integer( GetVersion ) < 0 then // Windows95/98/Me
+ begin
+ if Machine <> '' then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Flags := EWX_SHUTDOWN;
+ if Reboot then
+ Flags := Flags or EWX_REBOOT;
+ if Force then
+ Flags := Flags or EWX_FORCE;
+ Result := ExitWindowsEx( Flags, 0 ); Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ OpenProcessToken(GetCurrentProcess(),
+ TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY,
+ hToken);
+ if not LookupPrivilegeValue(PKOLChar(Machine), 'SeShutdownPrivilege',
+ tkp.Privileges[0].Luid) then Exit; {>>>>>>>>>>>>>}
+ tkp_prev:=tkp;
+ tkp.PrivilegeCount:=1;
+ tkp.Privileges[0].Attributes:=SE_PRIVILEGE_ENABLED;
+ AdjustTokenPrivileges(hToken, FALSE, tkp, sizeof(tkp), tkp_prev,
+dwRetLen);
+ if not LookupPrivilegeValue(PKOLChar(Machine),
+ 'SeRemoteShutdownPrivilege',
+ tkp.Privileges[0].Luid) then Exit; {>>>>>>>>>>>>}
+ tkp.PrivilegeCount:=1;
+ tkp.Privileges[0].Attributes:=SE_PRIVILEGE_ENABLED;
+ AdjustTokenPrivileges(hToken, FALSE, tkp, sizeof(tkp), tkp_prev, dwRetLen);
+ Result := InitiateSystemShutdown(PKOLChar(Machine),nil, 0, Force, Reboot);
+end;
+
+function WindowsLogoff( Force : Boolean ) : Boolean;
+var Flags: Integer;
+begin
+ Flags := 0;
+ if Force then
+ Flags := EWX_FORCE;
+ Result := ExitWindowsEx( Flags, 0 );
+end;
+
+var SaveWinVer: Byte = $FF;
+
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} // asm version by MTsv DN (v 2.90)
+function WinVer : TWindowsVersion;
+var MajorVersion, MinorVersion: Byte;
+ dwVersion: Integer;
+begin
+ if SaveWinVer <> $FF then Result := TWindowsVersion( SaveWinVer )
+ else
+ begin
+ dwVersion := GetVersion;
+ MajorVersion := LoByte( dwVersion );
+ MinorVersion := HiByte( LoWord( dwVersion ) );
+ if dwVersion >= 0 then
+ begin
+ Result := wvNT;
+ if (MajorVersion >= 6) then begin
+ if (MinorVersion >= 1) then
+ Result := wvSeven
+ else
+ Result := wvVista;
+ end else begin
+ if MajorVersion >= 5 then
+ if MinorVersion >= 1 then
+ begin
+ Result := wvXP;
+ if MinorVersion >= 2 then
+ Result := wvServer2003;
+ end
+ else Result := wvY2K;
+ end;
+ end
+ else
+ begin
+ Result := wv95;
+ if (MajorVersion > 4) or
+ (MajorVersion = 4) and (MinorVersion >= 10) then
+ begin
+ Result := wv98;
+ if (MajorVersion = 4) and (MinorVersion >= $5A) then
+ Result := wvME;
+ end
+ else
+ if MajorVersion <= 3 then
+ Result := wv31;
+ end;
+ SaveWinVer := Ord( Result );
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+function IsWinVer( Ver : TWindowsVersions ) : Boolean;
+{* Returns True if Windows version is in given range of values. }
+begin
+ Result := WinVer in Ver;
+end;
+
+procedure TControl.SetAlphaBlend(const Value: Byte);
+const
+ LWA_COLORKEY=$00000001;
+ LWA_ALPHA=$00000002;
+ ULW_COLORKEY=$00000001;
+ ULW_ALPHA=$00000002;
+ ULW_OPAQUE=$00000004;
+ WS_EX_LAYERED=$00080000;
+type
+ TSetLayeredWindowAttributes=
+ function( hwnd: Integer; crKey: TColor; bAlpha: Byte; dwFlags: DWORD )
+ : Boolean; stdcall;
+var
+ SetLayeredWindowAttributes: TSetLayeredWindowAttributes;
+ User32: THandle;
+ dw: DWORD;
+begin
+ if Value = fAlphaBlend then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ fAlphaBlend := Value;
+ User32 := GetModuleHandle( 'User32' );
+ SetLayeredWindowAttributes := GetProcAddress( User32,
+ 'SetLayeredWindowAttributes' );
+ if Assigned( SetLayeredWindowAttributes ) then
+ begin
+ dw := GetWindowLong( GetWindowHandle, GWL_EXSTYLE );
+ if Value < 255 then
+ begin
+ SetWindowLong( fHandle, GWL_EXSTYLE, dw or WS_EX_LAYERED );
+ SetLayeredWindowAttributes( fHandle, 0, Value {and $FF}, LWA_ALPHA);
+ end else
+ SetWindowLong( fHandle, GWL_EXSTYLE, dw and not WS_EX_LAYERED );
+ end;
+end;
+
+{$ENDIF WIN_GDI}
+function TControl.SetPosition( X, Y: Integer ): PControl;
+begin
+ Left := X;
+ Top := Y;
+ Result := @Self;
+end;
+{$IFDEF WIN_GDI}
+
+function NewColorDialog( FullOpen: TColorCustomOption ): PColorDialog;
+var I: Integer;
+begin
+ New( Result, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fObjKind := 'TColorDialog';
+ {$ENDIF}
+ Result.ColorCustomOption := FullOpen;
+ for I := 1 to 16 do
+ Result.CustomColors[ I ] := clWhite;
+end;
+
+{ TColorDialog }
+
+function TColorDialog.Execute: Boolean;
+var CD: TChooseColor;
+begin
+ CD.lStructSize := Sizeof( CD );
+ CD.hWndOwner := OwnerWindow;
+ //CD.hInstance := 0;
+ CD.rgbResult := Color2RGB( Color );
+ CD.lpCustColors := @CustomColors[ 1 ];
+ CD.Flags := CC_RGBINIT;
+ case ColorCustomOption of
+ ccoFullOpen: CD.Flags := CD.Flags or CC_FULLOPEN;
+ ccoPreventFullOpen: CD.Flags := CD.Flags or CC_PREVENTFULLOPEN;
+ end;
+ Result := ChooseColor( CD );
+ if Result then
+ Color := CD.rgbResult;
+end;
+
+procedure TControl.SetMaxProgress(const Index, Value: Integer);
+begin
+ // ignore index, and set Value via PBM_SETRANGE32: ()
+ Perform( PBM_SETRANGE32, 0, Value );
+end;
+
+procedure TControl.SetDroppedWidth(const Value: Integer);
+begin
+ DF.fDroppedWidth := Value;
+ Perform( CB_SETDROPPEDWIDTH, Value, 0 );
+end;
+
+function TControl.LVGetItemState(Idx: Integer): TListViewItemState;
+type
+ PListViewItemState = ^TListViewItemState;
+var I: Byte;
+begin
+ I := Perform( LVM_GETITEMSTATE, Idx,
+ LVIS_CUT or LVIS_DROPHILITED or LVIS_FOCUSED or LVIS_SELECTED );
+ Result := PListViewItemState( @ I )^;
+end;
+
+procedure TControl.LVSetItemState(Idx: Integer; const Value: TListViewItemState);
+var Data: TLVItem;
+begin
+ Data.stateMask := LVIS_FOCUSED or LVIS_SELECTED or LVIS_CUT or LVIS_DROPHILITED;
+ Data.state := PByte( @ Value )^;
+ Perform( LVM_SETITEMSTATE, Idx, Integer( @Data ) );
+end;
+
+procedure TControl.LVSelectAll;
+begin
+ LVSetItemState( -1, [ lvisSelect ] );
+end;
+
+function TControl.LVItemInsert(Idx: Integer; const aText: KOLString): Integer;
+var LVI: TLVItem;
+begin
+ LVI.mask := LVIF_TEXT or LVIF_DI_SETITEM;
+ LVI.iItem := Idx;
+ LVI.iSubItem := 0;
+ LVI.pszText := PKOL_Char( aText );
+ Result := Perform( LVM_INSERTITEM, 0, Integer( @LVI ) );
+end;
+
+function TControl.LVItemAdd(const aText: KOLString): Integer;
+begin
+ Result := LVItemInsert( Count, aText );
+end;
+
+function TControl.LVGetSttImgIdx(Idx: Integer): Integer;
+begin
+ Result := Perform( LVM_GETITEMSTATE, Idx, LVIS_STATEIMAGEMASK ) shr 12;
+end;
+
+procedure TControl.LVSetSttImgIdx(Idx: Integer; const Value: Integer);
+var LVI: TLVItem;
+begin
+ LVI.stateMask := LVIS_STATEIMAGEMASK;
+ LVI.state := Value shl 12;
+ Perform( LVM_SETITEMSTATE, Idx, Integer( @LVI ) );
+end;
+
+function TControl.LVGetOvlImgIdx(Idx: Integer): Integer;
+begin
+ Result := Perform( LVM_GETITEMSTATE, Idx, LVIS_OVERLAYMASK ) shr 8;
+end;
+
+procedure TControl.LVSetOvlImgIdx(Idx: Integer; const Value: Integer);
+var LVI: TLVItem;
+begin
+ LVI.stateMask := LVIS_OVERLAYMASK;
+ LVI.state := Value shl 8;
+ Perform( LVM_SETITEMSTATE, Idx, Integer( @LVI ) );
+end;
+
+function TControl.LVGetItemData(Idx: Integer): DWORD;
+var LVI: TLVItem;
+begin
+ LVI.mask := LVIF_PARAM;
+ LVI.iItem := Idx;
+ LVI.iSubItem := 0;
+ Perform( LVM_GETITEM, 0, Integer( @LVI ) );
+ Result := LVI.lParam;
+end;
+
+procedure TControl.LVSetItemData(Idx: Integer; const Value: DWORD);
+var LVI: TLVItem;
+begin
+ LVI.mask := LVIF_PARAM;
+ LVI.iItem := Idx;
+ LVI.iSubItem := 0;
+ LVI.lParam := Value;
+ Perform( LVM_SETITEM, 0, Integer( @LVI ) );
+end;
+
+function TControl.LVGetItemIndent(Idx: Integer): Integer;
+var LI: TLVItem;
+begin
+ LI.mask := LVIF_INDENT;
+ LI.iItem := Idx;
+ LI.iSubItem := 0;
+ Perform( LVM_GETITEM, 0, Integer( @LI ) );
+ Result := LI.iIndent;
+end;
+
+procedure TControl.LVSetItemIndent(Idx: Integer; const Value: Integer);
+var LI: TLVItem;
+begin
+ LI.mask := LVIF_INDENT or LVIF_DI_SETITEM;
+ LI.iItem := Idx;
+ LI.iSubItem := 0;
+ LI.iIndent := Value;
+ Perform( LVM_SETITEM, 0, Integer( @LI ) );
+end;
+
+type
+ TNMLISTVIEW = packed Record
+ hdr: TNMHDR;
+ iItem: Integer;
+ iSubItem: Integer;
+ uNewState: Integer;
+ uOldState: Integer;
+ uChanged: Integer;
+ ptAction: Integer;
+ lParam: DWORD;
+ end;
+ PNMLISTVIEW = ^TNMLISTVIEW;
+
+function WndProc_LVDeleteItem( Sender: PControl; var Msg: TMsg; var Rslt: Integer )
+ : Boolean;
+var Hdr: PNMHDR;
+ LV: PNMListView;
+begin
+ Result := FALSE;
+ if Msg.message = WM_NOTIFY then
+ begin
+ Hdr := Pointer(Msg.lParam);
+ if Hdr.hwndFrom = Sender.Handle then
+ begin
+ LV := Pointer( Hdr );
+ if Hdr.code = LVN_DELETEITEM then
+ begin
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Sender.EV.fOnDeleteLVItem ) then
+ {$ENDIF}
+ Sender.EV.fOnDeleteLVItem( Sender, LV.iItem );
+ Result := TRUE;
+ end
+ else
+ if Hdr.code = LVN_DELETEALLITEMS then
+ begin
+ if Assigned( Sender.DF.fOnDeleteAllLVItems ) then
+ begin
+ Sender.DF.fOnDeleteAllLVItems( Sender );
+ Rslt := 0;
+ if Assigned( Sender.EV.fOnDeleteLVItem ) then
+ Rslt := 1;
+ end;
+ Result := TRUE;
+ end;
+ end;
+ end;
+end;
+
+procedure TControl.SetOnDeleteAllLVItems(const Value: TOnEvent);
+begin
+ DF.fOnDeleteAllLVItems := Value;
+ AttachProc( @WndProc_LVDeleteItem );
+end;
+
+procedure TControl.SetOnDeleteLVItem(const Value: TOnDeleteLVItem);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnDeleteLVItem := Value;
+ AttachProc( @WndProc_LVDeleteItem );
+end;
+
+function WndProc_LVData( Sender: PControl; var Msg: TMsg; var Rslt: Integer )
+ : Boolean;
+var Hdr: PNMHDR;
+ DI: PLVDispInfo;
+ Store: Boolean;
+ Txt: KOL_String;
+ LV: PControl;
+begin
+ Result := FALSE;
+ if Msg.message = WM_NOTIFY then
+ begin
+ Hdr := Pointer(Msg.lParam);
+ if Hdr.hwndFrom = Sender.Handle then
+ begin
+ if (Hdr.code = LVN_GETDISPINFO)
+ {$IFDEF UNICODE_CTRLS}
+ or (Hdr.code = LVN_GETDISPINFOW)
+ {$ENDIF UNICODE_CTRLS}
+ then
+ begin
+ DI := Pointer( Hdr );
+ LV := Sender;
+ if LV <> nil then
+ begin
+ Txt := '';
+ DI.item.iImage := -1;
+ DI.item.state := 0;
+ if {$IFDEF NIL_EVENTS} Assigned( LV.EV.fOnLVData ) and {$ENDIF}
+ (DI.item.iItem >= 0) then
+ begin
+ Store := FALSE;
+ LV.EV.fOnLVData( LV, DI.item.iItem, DI.item.iSubItem, Txt,
+ DI.item.iImage, DWORD( DI.item.state ), Store );
+ LV.fCaption := Txt;
+ DI.item.pszText := PKOL_Char( PKOLChar( LV.fCaption ) );
+ if Store then
+ DI.item.mask := DI.item.mask or LVIF_DI_SETITEM;
+ end;
+ Result := TRUE;
+ end;
+ end;
+ end;
+ end;
+end;
+
+procedure TControl.SetOnLVData(const Value: TOnLVData);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnLVData := Value;
+ AttachProc( @WndProc_LVData );
+ Perform( LVM_SETCALLBACKMASK, LVIS_OVERLAYMASK or LVIS_STATEIMAGEMASK, 0 );
+end;
+
+{$IFDEF ENABLE_DEPRECATED}
+{$DEFINE implementation} {$I KOL_deprecated.inc} {$UNDEF implementation}
+{$ENDIF DISABLE_DEPRECATED}
+
+function WndProc_LVCustomDraw( Sender: PControl; var Msg: TMsg;
+ var Rslt: Integer ): Boolean;
+var NMCustDraw: PNMLVCustomDraw;
+ NMHdr: PNMHdr;
+ ItemIdx, SubItemIdx: Integer;
+ S: TListViewItemState;
+ ItemState: TDrawState;
+begin
+ Result := FALSE;
+ if Msg.message = WM_NOTIFY then
+ begin
+ NMHdr := Pointer( Msg.lParam );
+ if (NMHdr.code = NM_CUSTOMDRAW)
+ {$IFDEF NIL_EVENTS} and Assigned( Sender.EV.fOnLVCustomDraw ) {$ENDIF}
+ then
+ begin
+ NMCustDraw := Pointer( Msg.lParam );
+ ItemIdx := -1;
+ SubItemIdx := -1;
+ if LongBool( NMCustDraw.nmcd.dwDrawStage and CDDS_ITEM ) then
+ ItemIdx := NMCustDraw.nmcd.dwItemSpec;
+ if LongBool( NMCustDraw.nmcd.dwDrawStage and CDDS_SUBITEM ) then
+ SubItemIdx := NMCustDraw.iSubItem;
+ ItemState := [ ];
+ if ItemIdx >= 0 then
+ begin
+ S := Sender.LVItemState[ ItemIdx ];
+ if lvisFocus in S then
+ include( ItemState, odsFocused );
+ if lvisSelect in S then
+ include( ItemState, odsSelected );
+ if lvisBlend in S then
+ include( ItemState, odsGrayed );
+ if lvisHighlight in S then
+ include( ItemState, odsMarked );
+ end;
+ Rslt := Sender.EV.FOnLVCustomDraw( Sender, {Sender.fPaintDC} NMCustDraw.nmcd.hdc,
+ NMCustDraw.nmcd.dwDrawStage, ItemIdx, SubItemIdx, NMCustDraw.nmcd.rc,
+ ItemState, TColor( NMCustDraw.clrText ), TColor( NMCustDraw.clrTextBk ) );
+
+ Result := TRUE;
+ end;
+ end;
+end;
+
+procedure TControl.SetOnLVCustomDraw(const Value: TOnLVCustomDraw);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnLVCustomDraw := Value;
+ AttachProc( @WndProc_LVCustomDraw );
+end;
+
+function CompareLVItems( Idx1, Idx2: Integer; ListView: PControl ): Integer; stdcall;
+begin
+ {$IFDEF NIL_EVENTS}
+ if Assigned( ListView.EV.fOnCompareLVItems ) then
+ {$ENDIF}
+ Result := ListView.EV.fOnCompareLVItems( ListView, Idx1, Idx2 )
+ {$IFDEF NIL_EVENTS}
+ else
+ Result := 0
+ {$ENDIF} ;
+end;
+
+procedure TControl.LVSort;
+begin
+ Perform( LVM_SORTITEMSEX, Integer(@Self), Integer(@CompareLVItems) );
+end;
+
+function CompareLVItemsData( D1, D2: DWORD; ListView: PControl ): Integer; stdcall;
+begin
+ {$IFDEF NIL_EVENTS}
+ if Assigned( ListView.EV.fOnCompareLVItems ) then
+ {$ENDIF}
+ Result := ListView.EV.fOnCompareLVItems( ListView, D1, D2 )
+ {$IFDEF NIL_EVENTS}
+ else
+ Result := 0
+ {$ENDIF} ;
+end;
+
+procedure TControl.LVSortData;
+begin
+ Perform( LVM_SORTITEMS, Integer( @Self ), Integer( @CompareLVItemsData ) );
+end;
+
+function WndProc_LVColumnClick( Sender: PControl; var Msg: TMsg; var Rslt: Integer )
+ : Boolean;
+var Hdr: PNMHDR;
+ LV: PNMListView;
+begin
+ Result := FALSE;
+ if Msg.message = WM_NOTIFY then
+ begin
+ Hdr := Pointer(Msg.lParam);
+ if Hdr.hwndFrom = Sender.Handle then
+ begin
+ LV := Pointer( Hdr );
+ if Hdr.code = LVN_COLUMNCLICK then
+ begin
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Sender.EV.fOnColumnClick ) then
+ {$ENDIF}
+ Sender.EV.fOnColumnClick( Sender, LV.iSubItem );
+ Result := TRUE;
+ end;
+ end;
+ end;
+end;
+
+procedure TControl.SetOnColumnClick(const Value: TOnLVColumnClick);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnColumnClick := Value;
+ AttachProc( @WndProc_LVColumnClick );
+end;
+
+function WndProc_LVStateChange( Sender: PControl; var Msg: TMsg; var R: Integer ): Boolean;
+var NMOD: PNMLVODStateChange;
+ NMLV: PNMLISTVIEW;
+begin
+ if Msg.message = WM_NOTIFY then
+ begin
+ NMOD := Pointer( Msg.lParam );
+ NMLV := Pointer( Msg.lParam );
+ if NMOD.hdr.code = LVN_ODSTATECHANGED then
+ begin
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Sender.EV.fOnLVStateChange ) then
+ {$ENDIF}
+ Sender.EV.fOnLVStateChange( Sender, NMOD.iFrom, NMOD.iTo,
+ NMOD.uOldState, NMOD.uNewState );
+ end
+ else
+ if NMLV.hdr.code = LVN_ITEMCHANGED then
+ begin
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Sender.EV.fOnLVStateChange ) then
+ {$ENDIF}
+ Sender.EV.fOnLVStateChange( Sender, NMLV.iItem, NMLV.iItem,
+ NMLV.uOldState, NMLV.uNewState );
+ end;
+ end;
+ Result := FALSE;
+end;
+
+procedure TControl.SetOnLVStateChange(const Value: TOnLVStateChange);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .FOnLVStateChange := Value;
+ AttachProc( WndProc_LVStateChange );
+end;
+
+function CompareLVColumns( Idx1, Idx2: Integer; Sender: PControl ): Integer; stdcall;
+var S1, S2: KOLString;
+begin
+ //--- changed by Mike Gerasimov:
+ S1 := Sender.LVItems[ Idx1, Sender.DF.fColumn ];
+ S2 := Sender.LVItems[ Idx2, Sender.DF.fColumn ];
+ If lvoSortAscending in Sender.DF.fLVOptions Then
+ Result := AnsiCompareStrNoCase( S1, S2 )
+ Else
+ If lvoSortDescending in Sender.DF.fLVOptions Then
+ Result := AnsiCompareStrNoCase( S2, S1 )
+ Else
+ Result:=0;
+end;
+
+procedure TControl.LVSortColumn(Idx: Integer);
+begin
+ DF.fColumn := Idx;
+ Perform( LVM_SORTITEMSEX, Integer(@Self), Integer(@CompareLVColumns) );
+end;
+
+function TControl.LVIndexOf(const S: KOLString): Integer;
+begin
+ Result := LVSearchFor( S, -1, FALSE );
+end;
+
+function TControl.LVSearchFor(const S: KOLString; StartAfter: Integer;
+ Partial: Boolean): Integer;
+var f: TLVFindInfo;
+begin
+ f.lParam := 0;
+ f.flags := LVFI_STRING;
+ if Partial then
+ f.flags := LVFI_STRING or LVFI_PARTIAL;
+ f.psz := @s[1];
+ result := Perform(LVM_FINDITEM,StartAfter,integer(@f));
+end;
+
+function WndProcLVMeasureItem( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var
+ pMI: PMeasureItemStruct;
+ P: PControl;
+ H: Integer;
+ wId: DWORD;
+ i: Integer;
+begin
+ Result := FALSE;
+ if Msg.message = WM_MEASUREITEM then begin
+ pMI := Pointer(Msg.lParam);
+ with pMI^ do begin
+ for i:=0 to Sender.ChildCount-1 do begin
+ P := Sender.Children[i];
+ if P <> nil then begin
+ wId := GetWindowLong(P.Handle,GWL_ID);
+ if CtlID = wId then begin
+ H := P.Perform(WM_MEASUREITEM,0,0);
+ if H > 0 then begin
+ itemHeight := H;
+ Rslt:=1;
+ Result := TRUE;
+ end;
+ break;
+ end;
+ end;
+ end;
+ end;
+ end;
+end;
+
+function WndProcLVMeasureItem2( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+begin
+ Result := FALSE;
+ if (Msg.message = WM_MEASUREITEM) and (Msg.wParam = 0) then begin
+ Rslt := Sender.DF.fLVItemHeight;
+ Result := TRUE;
+ end;
+
+end;
+
+function TControl.SetLVItemHeight(Value: Integer): PControl;
+begin
+ Set_LVItemHeight( Value );
+ Result := @ Self;
+end;
+
+procedure TControl.Set_LVItemHeight(Value: Integer);
+begin
+ if DF.fLVItemHeight <> Value then
+ begin
+ if DF.fLVItemHeight = 0 then
+ begin
+ Parent.AttachProc(WndProcLVMeasureItem);
+ AttachProc(WndProcLVMeasureItem2);
+ end;
+ DF.fLVItemHeight := Value;
+ end;
+end;
+
+function TControl.IndexOf(const S: KOLString): Integer;
+begin
+ Result := SearchFor( S, -1, FALSE );
+end;
+
+function TControl.SearchFor(const S: KOLString; StartAfter: Integer;
+ Partial: Boolean): Integer;
+var Cmd: Integer;
+ I: Integer;
+begin
+ Cmd := fCommandActions.aFindItem;
+ if Partial then
+ Cmd := fCommandActions.aFindPartial;
+ if Cmd <> 0 then
+ Result := Perform( Cmd, StartAfter, Integer( PKOLChar( S ) ) )
+ else
+ begin
+ Result := -1;
+ for I := StartAfter+1 to Count-1 do
+ begin
+ if Partial and ( Copy( Items[ I ], 1, Length( S ) ) = S ) or
+ ( Items[ I ] = S ) then
+ begin
+ Result := I;
+ break;
+ end;
+ end;
+ end;
+end;
+
+{$UNDEF ASM_LOCAL}
+{$IFDEF ASM_VERSION}
+ {$IFDEF USE_FLAGS}
+ {$IFDEF EVENTS_DYNAMIC}
+ //{$IFNDEF NIL_EVENTS}
+ {$IFNDEF CLICK_DEFAULT_CANCEL_BTN_DIRECTLY}
+ {$DEFINE ASM_LOCAL}
+ {$ENDIF CLICK_DEFAULT_CANCEL_BTN_DIRECTLY}
+ //{$ENDIF NIL_EVENTS}
+ {$ENDIF EVENTS_DYNAMIC}
+ {$ENDIF USE_FLAGS}
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_LOCAL}
+function TControl.DefaultBtnProc(var Msg: TMsg; var Rslt: Integer): Boolean;
+asm
+ PUSH EBX
+ PUSH ESI
+ PUSH EDI
+ PUSH ECX // save @Rslt
+ PUSH EDX // save Msg
+ MOV EBX, EDX // EBX = @ Msg
+ XCHG ESI, EAX // ESI = @ Self
+ MOV EAX, [ESI].TControl.EV
+ MOV EDI, [EAX].TEvents.fOldOnMessage.TMethod.Code
+ MOV EAX, [EAX].TEvents.fOldOnMessage.TMethod.Data
+ {$IFDEF NIL_EVENTS}
+ TEST EDI, EDI
+ JZ @@cont
+ {$ELSE}
+ {$ENDIF}
+ CALL EDI
+ TEST AL, AL
+ JNZ @@exit1
+@@cont:
+ CMP [AppletTerminated], AL
+ JNZ @@exit
+ MOV AX, word ptr [EBX].TMsg.message
+ //SUB AX, WM_KEYDOWN
+ DEC AH
+ CMP AX, WM_CHAR - WM_KEYDOWN
+ JA @@exit
+ XCHG EAX, EBX
+ MOV EBX, [EAX].TMsg.message
+ SHL EBX, 16
+ MOV BL, byte ptr [EAX].TMsg.wParam
+ CMP BL, 13
+ JE @@ok1327
+ CMP BL, 27
+ JNE @@exit
+@@ok1327:
+ MOV EDI, [Applet]
+ TEST [EDI].TControl.fFlagsG3, 1 shl G3_IsForm
+ JNZ @@1
+ MOV EDI, [EDI].DF.fCurrentControl
+@@1:
+ TEST EDI, EDI
+ JZ @@exit
+
+ PUSH EBP
+ XOR EBP, EBP // Btn := nil;
+
+ MOV BH, 13
+ MOV EDX, offset[DFLT_BTN]
+@@findButton:
+ MOV EAX, EDI
+ CALL TControl.Get_Prop_Int
+ TEST EAX, EAX
+ JZ @@notFromProp
+ CMP BL, BH
+ JNZ @@notFromProp
+ MOV EBP, EAX
+ CALL TControl.GetToBeVisible
+ TEST AL, AL
+ JZ @@notFromProp
+ MOV EAX, EBP
+ CALL TControl.GetEnabled
+ TEST AL, AL
+ JZ @@notFromProp
+ CMP BL, 13
+ JNZ @@yesFound
+ MOV ECX, [EDI].TControl.DF.fCurrentControl
+ JECXZ @@yesFound
+ TEST word ptr [ECX].TControl.fFlagsG5, (1 shl G6_CancelBtn) shl 8 or(1 shl G5_IgnoreDefault)
+ JZ @@yesFound
+ CMP EBP, ECX
+ JZ @@yesFound
+@@notFromProp:
+ XOR EBP, EBP
+ CMP BL, 13
+ JNZ @@notFound
+ MOV AL, [EDI].TControl.DF.fAllBtnReturnClick
+ OR AL, [ESI].TControl.DF.fAllBtnReturnClick
+ JZ @@notFound
+ MOV ECX, [EDI].DF.fCurrentControl
+ JECXZ @@notFound
+ MOV AL, [ECX].TControl.fFlagsG5
+ AND AL, (1 shl G5_IsButton) or (1 shl G5_IsGroupbox)
+ CMP AL, (1 shl G5_IsButton)
+ JNZ @@notFound
+ MOV EBP, EAX
+ CALL TControl.GetToBeVisible
+ TEST AL, AL
+ JNZ @@yesFound
+@@notFound:
+ XOR EBP, EBP
+@@yesFound:
+ CMP BH, 13
+ MOV BH, 27
+ MOV EDX, offset[CNCL_BTN]
+ JNZ @@check_Found
+ TEST EBP, EBP
+ JZ @@findButton
+@@check_Found:
+ MOV ECX, EBP
+ POP EBP
+ JECXZ @@exit
+
+ MOV ESI, ECX
+ XCHG EAX, ECX
+
+ SHR EBX, 16
+ CMP BX, WM_KEYDOWN
+ JNZ @@doclick
+
+ MOV DL, 1
+ CALL TControl.SetFocused
+
+@@doclick:
+ POP EDI
+ POP EBX
+
+ PUSH [EDI].TMsg.lParam
+ PUSH 32
+ PUSH [EDI].TMsg.message
+ PUSH ESI
+ CALL TControl.Perform
+
+ XOR EAX, EAX
+ AND [EDI].TMsg.wParam, EAX
+ AND [EBX], EAX
+ INC EAX
+ PUSH EAX
+ PUSH EAX
+ JMP @@exit1
+
+@@exit: XOR EAX, EAX
+@@exit1:
+ POP EDX
+ POP ECX
+ POP EDI
+ POP ESI
+ POP EBX
+end;
+{$ELSE PAS_VERSION}
+function TControl.DefaultBtnProc(var Msg: TMsg;
+ var Rslt: Integer): Boolean;
+var Btn: PControl;
+ F: PControl;
+
+ procedure FindBtn( key: Word; s: PKOLChar; for_dflt: Boolean );
+ var Ctl: PControl;
+ begin
+ Ctl := Pointer( F.PropInt[ s ] );
+ if (Msg.wParam = key) and
+ (Ctl <> nil) and
+ Ctl.ToBeVisible and
+ Ctl.Enabled and
+ ( not for_dflt or
+ for_dflt and
+ ( (F.DF.fCurrentControl=nil) or
+ ({$IFDEF USE_FLAGS} not(G6_CancelBtn in F.DF.fCurrentControl.fFlagsG6)
+ {$ELSE} not F.DF.fCurrentControl.fCancelBtn {$ENDIF} and
+ {$IFDEF USE_FLAGS} not(G5_IgnoreDefault in F.DF.fCurrentControl.fFlagsG5)
+ {$ELSE} not F.DF.fCurrentControl.fIgnoreDefault {$ENDIF})
+ or (F.DF.fCurrentControl = Ctl)
+ ) ) then
+ Btn := Ctl
+ else
+ if for_dflt
+ AND (Msg.wParam = VK_RETURN) and
+ (F.DF.fAllBtnReturnClick or DF.fAllBtnReturnClick)
+ and (F.ActiveControl <> nil) and
+ (F.ActiveControl.ToBeVisible) and
+ {$IFDEF USE_FLAGS} (G5_IsButton in F.ActiveControl.fFlagsG5)
+ and not(G5_IsGroupbox in F.ActiveControl.fFlagsG5)
+ {$ELSE} (F.ActiveControl.IsButton and not F.ActiveControl.fIsGroupbox) {$ENDIF}
+ {and (F.ActiveControl.Count = 0)} then
+ Btn := F.ActiveControl;
+ end;
+begin
+ {$IFDEF NIL_EVENTS}
+ if Assigned( EV.fOldOnMessage ) then
+ {$ENDIF}
+ begin
+ Result := EV.fOldOnMessage( Msg, Rslt );
+ if Result then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Result := FALSE;
+ if AppletTerminated then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ F := Applet;
+ if {$IFDEF USE_FLAGS} not(G3_IsForm in F.fFlagsG3)
+ {$ELSE} not F.fIsForm {$ENDIF} then
+ F := F.DF.fCurrentControl;
+ if F = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Btn := nil;
+ if //((Msg.message = WM_KEYDOWN) or (Msg.message = WM_KEYUP) or (Msg.message = WM_CHAR)) and
+ ((Msg.message >= WM_KEYDOWN) and (Msg.message <= WM_CHAR)) and
+ ((Msg.wParam = VK_RETURN) or (Msg.wParam = VK_ESCAPE)) then
+ begin
+ FindBtn( VK_RETURN, @DFLT_BTN, TRUE );
+ FindBtn( VK_ESCAPE, @CNCL_BTN, FALSE );
+ if Btn <> nil then
+ begin
+ if Msg.message = WM_KEYDOWN then
+ begin
+ {$IFDEF CLICK_DEFAULT_CANCEL_BTN_DIRECTLY}
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Btn.EV.fOnClick ) then
+ {$ENDIF}
+ Btn.EV.fOnClick( Btn );
+ {$ELSE}
+ Btn.Focused := TRUE;
+ {$ENDIF}
+ end;
+ {$IFDEF CLICK_DEFAULT_CANCEL_BTN_DIRECTLY}
+ {$ELSE}
+ Btn.Perform( Msg.message, DWORD( ' ' ), Msg.lParam );
+ {$ENDIF}
+ Msg.wParam := 0;
+ Result := TRUE;
+ Rslt := 0; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end
+ end;
+ Result := FALSE;
+end;
+{$ENDIF PAS_VERSION}
+
+{$UNDEF ASM_LOCAL}
+{$IFDEF ASM_VERSION}
+ {$IFDEF USE_FLAGS}
+ {$IFNDEF DEFAULT_CANCEL_BTN_EXCLUSIVE}
+ {$IFDEF EVENTS_DYNAMIC}
+ {$DEFINE ASM_LOCAL}
+ {$ENDIF EVENTS_DYNAMIC}
+ {$ENDIF DEFAULT_CANCEL_BTN_EXCLUSIVE}
+ {$ENDIF USE_FLAGS}
+{$ENDIF PAS_VERSION}
+
+{$IFDEF ASM_LOCAL}
+procedure TControl.SetDefaultBtn(const Index: Integer;
+ const Value: Boolean);
+asm
+ PUSH EBX
+ PUSH EDI
+ PUSH ESI
+ MOV BL, DL // index
+ MOV BH, CL // value
+ MOV ESI, EAX // @Self
+ ADD ECX, ECX // DL = 2
+ CMP BL, 13
+ JZ @@1
+ CMP BL, 27
+ JNZ @@2
+ ADD ECX, ECX // DL := Index = 13 ? 2 : 4
+@@1: CMP BH, 0
+ JNZ @@set_flag
+ NOT CL
+ AND [ESI].fFlagsG6, CL
+ MOV CL, 0
+@@set_flag:
+ OR [ESI].fFlagsG6, CL
+@@2:
+ CMP [Applet], 0
+ JZ @@exit
+ CALL TControl.ParentForm
+ TEST EAX, EAX
+ JZ @@exit
+
+ XCHG EDI, EAX // EDI = ParentForm
+ MOV AL, BH
+ SHR EAX, 1
+ SBB ECX, ECX
+ AND ECX, ESI // ECX = Value ? @ Self : 0
+ MOV EDX, offset[DFLT_BTN]
+ CMP BL, 13
+ JZ @@3
+ MOV EDX, offset[CNCL_BTN]
+@@3:
+ XCHG EAX, EDI
+ CALL TControl.Set_Prop_Int
+
+ {$IFnDEF NO_DEFAULT_BUTTON_BOLD}
+ XCHG EAX, ESI //---- áîëüøå @Self íå íóæåí
+ MOV EDX, [EAX].TControl.fStyle
+ AND DL, not BS_DEFPUSHBUTTON //---- BS_DEFPUSHBUTTON = 1, BH = Value = 1 : 0
+ OR DL, BH
+ CALL TControl.SetStyle
+ {$ENDIF}
+
+ TEST BH, BH
+ MOV ESI, [Applet] // ESI = Applet
+ MOV EBX, [ESI].TControl.EV
+ JZ @@notValue
+
+ MOV EDX, [EBX].TEvents.fOnMessage.TMethod.Code
+ CMP EDX, offset[TControl.DefaultBtnProc]
+ JZ @@setDefaultBtnProc
+
+ MOV [EBX].TEvents.fOldOnMessage.TMethod.Code, EDX
+ MOV EDX, [EBX].TEvents.fOnMessage.TMethod.Data
+ MOV [EBX].TEvents.fOldOnMessage.TMethod.Data, EDX
+
+@@setDefaultBtnProc:
+ MOV [EBX].TEvents.fOnMessage.TMethod.Code, offset[TControl.DefaultBtnProc]
+ MOV [EBX].TEvents.fOnMessage.TMethod.Data, ESI
+ JMP @@exit
+
+@@notValue:
+ LEA ESI, [EBX].TEvents.fOldOnMessage
+ LEA EDI, [EBX].TEvents.fOnMessage
+ MOVSD
+ MOVSD
+ MOV [EBX].TEvents.fOldOnMessage.TMethod.Code, offset[DummyProc123_0]
+
+@@exit: POP ESI
+ POP EDI
+ POP EBX
+end;
+{$ELSE notASM_VERSION}
+procedure TControl.SetDefaultBtn(const Index: Integer;
+ const Value: Boolean);
+var F, C: PControl;
+begin
+ if Index = 13 then
+ begin
+ {$IFDEF USE_FLAGS} if Value
+ then include( fFlagsG6, G6_DefaultBtn )
+ else exclude( fFlagsG6, G6_DefaultBtn );
+ {$ELSE} fDefaultBtn := Value; {$ENDIF}
+ {$IFDEF DEFAULT_CANCEL_BTN_EXCLUSIVE}
+ {$IFDEF USE_FLAGS} Exclude( fFlagsG6, G6_CancelBtn );
+ {$ELSE} fCancelBtn := FALSE; {$ENDIF}
+ {$ENDIF}
+ end else
+ if Index = 27 then // this check is necessary still could be Index = 0 to reset both !
+ begin
+ {$IFDEF USE_FLAGS} if Value
+ then include( fFlagsG6, G6_CancelBtn )
+ else exclude( fFlagsG6, G6_CancelBtn );
+ {$ELSE} fCancelBtn := Value; {$ENDIF}
+
+ {$IFDEF DEFAULT_CANCEL_BTN_EXCLUSIVE}
+ {$IFDEF USE_FLAGS} Exclude( fFlagsG6, G6_DefaultBtn );
+ {$ELSE} fDefaultBtn := FALSE; {$ENDIF}
+ {$ENDIF}
+ end;
+ if Applet = nil then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ F := ParentForm;
+ if F <> nil then
+ begin
+ C := nil;
+ if Value then C := @ Self;
+ if Index = 13 then
+ begin
+ F.PropInt[ @DFLT_BTN ] := Integer( C );
+ {$IFDEF NO_DEFAULT_BUTTON_BOLD}
+ {$ELSE}
+ if Value then
+ Style := fStyle.Value or BS_DEFPUSHBUTTON
+ else
+ Style := fStyle.Value and not BS_DEFPUSHBUTTON;
+ {$ENDIF}
+ end
+ else if Index = 27 then
+ F.PropInt[ @CNCL_BTN ] := Integer( C );
+
+ if Value then
+ begin
+ if @ Applet.EV.fOnMessage <> @ TControl.DefaultBtnProc then
+ Applet.EV.fOldOnMessage := Applet.EV.fOnMessage; // fixed by YS
+ Applet.EV.fOnMessage := Applet.DefaultBtnProc;
+ end else
+ begin
+ Applet.EV.fOnMessage := Applet.EV.fOldOnMessage;
+ Applet.EV.fOldOnMessage :=
+ {$IFDEF SAFEST_CODE} TOnMessage( MakeMethod( nil, @ DummyProc123_0 ) )
+ {$ELSE} nil {$ENDIF};
+ end;
+ end;
+end;
+{$ENDIF PAS_VERSION}
+
+function TControl.GetDefaultBtn(const Index: Integer): Boolean;
+begin
+ CASE Index OF
+ 13 : Result := {$IFDEF USE_FLAGS} G6_DefaultBtn in fFlagsG6
+ {$ELSE} fDefaultBtn {$ENDIF};
+ else Result := {$IFDEF USE_FLAGS} G6_CancelBtn in fFlagsG6
+ {$ELSE} fCancelBtn {$ENDIF};
+ END;
+end;
+
+function TControl.AllBtnReturnClick: PControl;
+{$IFDEF ALL_BUTTONS_RESPOND_TO_ENTER}
+begin
+ // nothing: already implemented in WndProcBtnReturnClick
+ Result := @ Self;
+end;
+{$ELSE}
+var F: PControl;
+begin
+ {$IFDEF SAFE_CODE}
+ if {$IFDEF USE_FLAGS} [G3_IsForm, G3_IsApplet] * fFlagsG3 <> []
+ {$ELSE} fIsForm or fIsApplet {$ENDIF} then
+ {$ENDIF}
+ begin
+ SetDefaultBtn( 0, TRUE );
+ F := ParentForm;
+ if F <> nil then
+ F.DF.fAllBtnReturnClick := TRUE;
+ end;
+ Result := @ Self;
+end;
+{$ENDIF}
+
+function WndProc_CNDrawItem( Sender: PControl; var Msg: TMsg; var Rslt: Integer )
+ : Boolean;
+type PDrawAction = ^TDrawAction;
+ PDrawState = ^TDrawState;
+var DI: PDrawItemStruct;
+begin
+ Result := FALSE;
+ if Msg.message = CN_DRAWITEM then
+ begin
+ DI := Pointer( Msg.lParam );
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Sender.EV.fOnDrawItem ) then
+ {$ENDIF}
+ begin
+ if Sender.EV.fOnDrawItem( Sender, DI.hDC, DI.rcItem, DI.itemID,
+ PDrawAction( @ DI.itemAction )^,
+ PDrawState( @ DI.itemState )^ )
+ then Rslt := 1
+ else Rslt := 0;
+ Result := TRUE;
+ end
+ {$IFDEF NIL_EVENTS}
+ else Rslt := 0
+ {$ENDIF}
+ ;
+ end;
+end;
+
+procedure TControl.SetOnDrawItem(const Value: TOnDrawItem);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnDrawItem := Value;
+ if Parent <> nil then
+ Parent.AttachProc( @WndProc_DrawItem );
+ AttachProc( @WndProc_CNDrawItem );
+end;
+
+function WndProc_MeasureItem( Sender: PControl; var Msg: TMsg; var Rslt: Integer )
+ : Boolean;
+var MI: PMeasureItemStruct;
+ Control: PControl;
+ I: Integer;
+begin
+ Result := FALSE;
+ if Msg.message = WM_MEASUREITEM then
+ begin
+ MI := Pointer( Msg.lParam );
+ for I := 0 to Sender.ChildCount - 1 do
+ begin
+ Control := Sender.Children[ I ];
+ if Control.Menu = MI.CtlID then
+ begin
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Control.EV.fOnMeasureItem ) then
+ {$ENDIF}
+ begin
+ MI.itemHeight := Control.EV.fOnMeasureItem( Control, MI.itemID );
+ if MI.itemHeight > 0 then
+ begin
+ Rslt := 1;
+ Result := TRUE;
+ end;
+ end;
+ break;
+ end;
+ end;
+ end;
+end;
+
+procedure TControl.SetOnMeasureItem(const Value: TOnMeasureItem);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnMeasureItem := Value;
+ if Parent <> nil then
+ Parent.AttachProc( @WndProc_MeasureItem );
+end;
+
+function TControl.GetItemData(Idx: Integer): DWORD;
+begin
+ Result := 0;
+ if fCommandActions.aGetItemData <> 0 then
+ Result := Perform( fCommandActions.aGetItemData, Idx, 0 );
+end;
+
+procedure TControl.SetItemData(Idx: Integer; const Value: DWORD);
+begin
+ if fCommandActions.aSetItemData <> 0 then
+ Perform( fCommandActions.aSetItemData, Idx, Value );
+end;
+
+function TControl.GetLVCurItem: Integer;
+begin
+ Result := Perform( LVM_GETNEXTITEM, -1, LVNI_SELECTED );
+end;
+
+procedure TControl.SetLVCurItem(const Value: Integer);
+begin
+ if (lvoMultiselect in LVOptions) or (Value <> LVCurItem ) then
+ LVItemState[ -1 ] := [ ];
+ if Value >= 0 then
+ LVItemState[ Value ] := [ lvisSelect, lvisFocus ];
+end;
+
+function TControl.LVNextItem(IdxPrev: Integer; Attrs: DWORD): Integer;
+begin
+ Result := Perform( LVM_GETNEXTITEM, IdxPrev, Attrs );
+end;
+
+function TControl.LVNextSelected(IdxPrev: Integer): Integer;
+begin
+ Result := Perform( LVM_GETNEXTITEM, IdxPrev, LVNI_SELECTED );
+end;
+
+function TControl.GetLVFocusItem: Integer;
+begin
+ Result := Perform( LVM_GETNEXTITEM, -1, LVNI_FOCUSED );
+end;
+
+procedure TControl.Close;
+begin
+ PostMessage( Handle, WM_CLOSE, 0, 0 );
+end;
+
+function WndProcMinimize( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var Wnd: PControl;
+begin
+ Result := FALSE;
+ if (Msg.message = WM_SYSCOMMAND) and ((Msg.wParam and $FFF0) = SC_MINIMIZE)then
+ begin
+ if Applet <> nil then
+ begin
+ Wnd := Pointer( Applet.PropInt[ @MIN_WND ] ); // fMinimizeWnd;
+ if Wnd <> nil then
+ SetWindowPos( Applet.Handle, 0, Wnd.Left, Wnd.Top, Wnd.Width, 0,
+ SWP_NOZORDER or SWP_NOREDRAW);
+ end;
+ end;
+end;
+
+function WndProcRestore( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+begin
+ Result := FALSE;
+ CASE Msg.message OF
+ WM_SHOWWINDOW:
+ begin
+ case Msg.lParam of
+ SW_PARENTCLOSING:
+ begin
+ if IsIconic( Self_.fHandle ) then
+ Self_.DF.fShowAction := SW_SHOWMINNOACTIVE
+ else
+ if IsZoomed( Self_.fHandle ) then
+ Self_.DF.fShowAction := SW_SHOWMAXIMIZED
+ else
+ Self_.DF.fShowAction := SW_SHOWNOACTIVATE;
+ end;
+ SW_PARENTOPENING:
+ begin
+ if Self_.DF.fShowAction <> 0 then
+ begin
+ ShowWindow( Self_.fHandle, Self_.DF.fShowAction );
+ Self_.DF.fShowAction := 0;
+ end;
+ Rslt := 0;
+ end;
+ end;
+ end;
+ END;
+end;
+
+procedure TControl.MinimizeNormalAnimated;
+var App: PControl;
+begin
+ App := Applet;
+ if App = nil then
+ App := @Self;
+ App.PropInt[ @MIN_WND ] // fMinimizeWnd
+ := Integer( @Self );
+ App.AttachProc( @WndProcMinimize );
+ AttachProc( @WndProcRestore );
+end;
+
+procedure TControl.RestoreNormalMaximized;
+begin
+ AttachProc( @WndProcRestore );
+end;
+
+function WndProcDropFiles( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var hDrop: THandle;
+ Pt: TPoint;
+ FList: KOLString;
+ I, N: Integer;
+ Buf: array[ 0..MAX_PATH ] of KOLChar;
+begin
+ if Msg.message = WM_DROPFILES then
+ if TMethod(Sender.EV.fOnDropFiles).Code <> nil then
+ begin
+ hDrop := Msg.wParam;
+ DragQueryPoint( hDrop, Pt );
+ N := DragQueryFile( hDrop, $FFFFffff, nil, 0 );
+ FList := '';
+ for I := 0 to N-1 do
+ begin
+ if FList <> '' then
+ FList := FList + #13;
+ DragQueryFile( hDrop, I, Buf, Sizeof( Buf ) );
+ FList := FList + KOLString(Buf);
+ end;
+ DragFinish( hDrop );
+ Sender.EV.FOnDropFiles( Sender, FList, Pt );
+ Rslt := 0;
+ Result := TRUE; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ Result := FALSE;
+end;
+
+procedure TControl.SetOnDropFiles(const Value: TOnDropFiles);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .FOnDropFiles := Value;
+ AttachProc( @WndProcDropFiles );
+ DragAcceptFiles( GetWindowHandle, Assigned( Value ) );
+end;
+
+function WndProcShowHide( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var IsVisible: Boolean;
+begin
+ if Msg.message = WM_SHOWWINDOW then
+ if Msg.hwnd = Sender.Handle then
+ begin
+ IsVisible := IsWindowVisible( Sender.Handle );
+ if LongBool( Msg.wParam ) then
+ begin
+ {$IFDEF USE_FLAGS} include( Sender.fStyle.f3_Style, F3_Visible );
+ {$ELSE} Sender.fVisible := TRUE; {$ENDIF}
+ if not IsVisible then
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Sender.EV.FOnShow ) then
+ {$ENDIF}
+ Sender.EV.FOnShow( Sender );
+ end else
+ begin
+ {$IFDEF USE_FLAGS} exclude( Sender.fStyle.f3_Style, F3_Visible );
+ {$ELSE} Sender.fVisible := FALSE; {$ENDIF}
+ if IsVisible then
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Sender.EV.FOnHide ) then
+ {$ENDIF}
+ Sender.EV.FOnHide( Sender );
+ end;
+ end;
+ Sender.UpdateWndStyles;
+ Result := FALSE;
+end;
+
+procedure TControl.SetOnHide(const Value: TOnEvent);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .FOnHide := Value;
+ AttachProc( WndProcShowHide );
+end;
+
+procedure TControl.SetOnShow(const Value: TOnEvent);
+begin
+ {$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .FOnShow := Value;
+ AttachProc( WndProcShowHide );
+end;
+
+function TControl.BringToFront: PControl;
+begin
+ SetWindowPos( GetWindowHandle, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE or
+ SWP_NOACTIVATE or SWP_NOOWNERZORDER or SWP_SHOWWINDOW );
+ Result := @Self;
+end;
+
+function TControl.SendToBack: PControl;
+begin
+ SetWindowPos( GetWindowHandle, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE or
+ SWP_NOACTIVATE or SWP_NOOWNERZORDER );
+ Result := @Self;
+end;
+
+procedure TControl.DragStart;
+begin
+ PostMessage( GetWindowHandle, WM_SYSCOMMAND, $F012, 0 );
+end;
+
+function WndProcDragWindow( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var P: TPoint;
+ Delta: DWORD;
+ dX, dY: Integer;
+begin
+ if Msg.message = WM_MOUSEMOVE then
+ begin
+ if {$IFDEF USE_FLAGS} G6_Dragging in Sender.fFlagsG6
+ {$ELSE} Sender.FDragging {$ENDIF} then
+ begin
+ GetCursorPos( P );
+ Delta := Sender.PropInt[ @DRAG_XY ];
+ dX := SmallInt( LoWord( Delta ) );
+ dY := SmallInt( HiWord( Delta ) );
+ P.x := P.x + dX; // - Sender.fMouseStartPos.x + Sender.fDragStartPos.x;
+ P.y := P.y + dY; // - Sender.fMouseStartPos.y + Sender.fDragStartPos.y;
+ Sender.Position := P;
+ end;
+ end;
+ Result := FALSE;
+end;
+
+procedure TControl.DragStartEx;
+var StartBounds: TRect;
+ MSP: TPoint;
+ dX, dY: Integer;
+ Delta: Integer;
+begin
+ {$IFNDEF SMALLEST_CODE}
+ if {$IFDEF USE_FLAGS} G6_Dragging in fFlagsG6
+ {$ELSE} fDragging {$ENDIF} then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ {$ENDIF}
+ GetCursorPos( MSP );
+ StartBounds := BoundsRect;
+ dX := StartBounds.Left - MSP.X;
+ dY := StartBounds.Top - MSP.Y;
+ Delta := (dX and $FFFF) or (dY shl 16);
+ PropInt[ @DRAG_XY ] := Delta;
+ SetCapture( GetWindowHandle );
+ {$IFDEF USE_FLAGS} include( fFlagsG6, G6_Dragging );
+ {$ELSE} fDragging := TRUE; {$ENDIF}
+ AttachProc( WndProcDragWindow );
+end;
+
+procedure TControl.DragStopEx;
+begin
+ if {$IFDEF USE_FLAGS} G6_Dragging in fFlagsG6
+ {$ELSE} FDragging {$ENDIF} then
+ begin
+ ReleaseCapture;
+ {$IFDEF USE_FLAGS} exclude( fFlagsG6, G6_Dragging );
+ {$ELSE} FDragging := FALSE; {$ENDIF}
+ end;
+end;
+
+function CallDragCallBack( Sender: PControl; var Stop: Boolean ): Boolean;
+var P: TPoint;
+ Shape, ShapeWas: Integer;
+begin
+ Sender.AttachProc( WndProcSetCursor );
+ GetCursorPos( P );
+ Shape := LoadCursor( 0, PKOLChar(IDC_HAND) );
+ ShapeWas := Shape;
+ Result := Sender.EV.fDragCallback( Sender, P.x, P.y, Shape, Stop );
+ if not Stop then
+ begin
+ if not Result then
+ if Shape = ShapeWas then
+ Shape := LoadCursor( 0, IDC_NO );
+ ScreenCursor := Shape;
+ end
+ else
+ begin
+ ScreenCursor := 0;
+ Shape := Sender.fCursor;
+ end;
+ Windows.SetCursor( Shape );
+end;
+
+function WndProcDrag( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var Stop: Boolean;
+begin
+ if {$IFDEF USE_FLAGS} G6_Dragging in Sender.fFlagsG6
+ {$ELSE} Sender.fDragging {$ENDIF} then
+ begin
+ Stop := FALSE;
+ case Msg.message of
+ WM_MOUSEMOVE:
+ CallDragCallBack( Sender, Stop );
+ WM_LBUTTONUP, WM_RBUTTONUP:
+ begin
+ Stop := TRUE;
+ CallDragCallBack( Sender, Stop );
+ end;
+ else Result := FALSE; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ if Stop then
+ begin
+ ReleaseCapture;
+ {$IFDEF USE_FLAGS} exclude( Sender.fFlagsG6, G6_Dragging );
+ {$ELSE} Sender.fDragging := FALSE; {$ENDIF}
+ end else
+ begin
+ Result := TRUE; exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ Result := FALSE;
+end;
+
+procedure TControl.DragItem(OnDrag: TOnDrag);
+begin
+ EV.fDragCallback := OnDrag;
+ {$IFDEF USE_FLAGS} include( fFlagsG6, G6_Dragging );
+ {$ELSE} fDragging := TRUE; {$ENDIF}
+ SetCapture( GetWindowHandle );
+ AttachProc( WndProcDrag );
+end;
+
+{$IFDEF USE_CONSTRUCTORS} //****************************************************//
+ //
+constructor TControl.CreateWindowed(AParent: PControl; AClassName: PKOLChar; //
+ ACtl3D: Boolean); //
+begin //
+ CreateParented( AParent ); //
+ fOnDynHandlers := WndProcDummy; //
+ fWndProcKeybd := WndProcDummy; //
+ //{-2.95}//fWndProcResizeFlicks := WndProcDummy; //
+ fCommandActions.aClear := ClearText; //
+ //fWindowed := True; // is set in TControl.Init
+ fControlClassName := AClassName; //
+ //
+ fControlClick := DummyObjProc; //
+ //
+ fColor := clBtnFace; //
+ fTextColor := clWindowText; //
+ fMargin := 2; //
+ fCtl3D := True; //
+ fCtl3Dchild := True; //
+ if AParent <> nil then //
+ begin //
+ //{-2.95}//fWndProcResizeFlicks := AParent.fWndProcResizeFlicks; //
+ fGotoControl := AParent.fGotoControl; //
+ {$IFDEF USE_FLAGS}
+ exc fFlagsG2 := fFlagsG2 - [G2_DoubleBuffered, G2_Transparent] +
+ (AParent.fFlagsG2 * [G2_DoubleBuffered, G2_Transparent]);
+ {$ELSE} fDoubleBuffered := AParent.fDoubleBuffered;
+ fTransparent := AParent.fTransparent; //
+ {$ENDIF}
+ fCtl3Dchild := AParent.fCtl3Dchild; //
+ if AParent.fCtl3Dchild then //
+ fCtl3D := ACtl3D //
+ else fCtl3D := False; //
+ fMargin := AParent.fMargin; //
+ with fBoundsRect do //
+ begin //
+ Left := AParent.fMargin + AParent.fClientLeft; //
+ Top := AParent.fMargin + AParent.fClientTop; //
+ Right := Left + 64; //
+ Bottom := Top + 64; //
+ end; //
+ fTextColor := AParent.fTextColor; //
+ fFont := fFont.Assign( AParent.fFont ); //
+ if fFont <> nil then //
+ begin //
+ fFont.fOnGTChange := FontChanged; //
+ FontChanged( fFont ); //
+ end; //
+ fColor := AParent.fColor; //
+ fBrush := fBrush.Assign( AParent.fBrush ); //
+ if fBrush <> nil then //
+ begin //
+ fBrush.fOnGTChange := BrushChanged; //
+ BrushChanged( fBrush ); //
+ end; //
+ end; //
+end; //
+ //
+constructor TControl.CreateApplet(const ACaption: AnsiString); //
+begin //
+ AppButtonUsed := True; //
+ CreateWindowed( nil, 'App', TRUE ); //
+ {$IFDEF USE_FLAGS} include( fFlagsG3, G3_IsApplet );
+ {$ELSE} FIsApplet := TRUE; {$ENDIF}
+ fStyle := WS_VISIBLE or WS_SYSMENU or WS_POPUP or WS_MINIMIZEBOX //
+ or WS_CAPTION; //
+ fExStyle := WS_EX_APPWINDOW; //
+ FCreateWndExt := CreateAppButton; //
+ AttachProc( WndProcApp ); //
+ Caption := ACaption; //
+end; //
+ //
+constructor TControl.CreateForm(AParent: PControl; const ACaption: AnsiString); //
+begin //
+ CreateWindowed( AParent, 'Form', TRUE ); //
+ AttachProc( WndProcForm ); //
+ AttachProc( WndProcDoEraseBkgnd ); //
+ Caption := ACaption; //
+end; //
+ //
+constructor TControl.CreateControl(AParent: PControl; AClassName: PAnsiChar; //
+ AStyle: DWORD; ACtl3D: Boolean; Actions: PCommandActions); //
+var Form: PControl; //
+begin //
+ CreateWindowed( AParent, AClassName, ACtl3D ); //
+ if Actions <> nil then //
+ fCommandActions := Actions^; //
+ fIsControl := True; //
+ fStyle := AStyle or WS_CLIPSIBLINGS or WS_CLIPCHILDREN; //
+ fVisible := (Style and WS_VISIBLE) <> 0; //
+ fTabstop := (Style and WS_TABSTOP) <> 0; //
+ if (AParent <> nil) then //
+ begin //
+ Inc( AParent.ParentForm.fTabOrder ); //
+ fTabOrder := AParent.ParentForm.fTabOrder; //
+ end; //
+ fLookTabKeys := [ tkTab, tkLeftRight, tkUpDown, tkPageUpPageDn ]; //
+ if fCtl3D then //
+ begin //
+ fStyle := fStyle and not WS_BORDER; //
+ fExStyle := fExStyle or WS_EX_CLIENTEDGE; //
+ end; //
+ if (Style and WS_TABSTOP) <> 0 then //
+ begin //
+ Form := ParentForm; //
+ if Form <> nil then //
+ if Form.FCurrentControl = nil then //
+ Form.FCurrentControl := @Self; //
+ end; //
+ //fCreateParamsExt := CreateParams2; //
+ fMenu := CtlIdCount; //
+ Inc( CtlIdCount ); //
+ AttachProc( WndProcCtrl ); //
+end; //
+ //
+constructor TControl.CreateButton(AParent: PControl; //
+ const ACaption: AnsiString); //
+begin //
+ CreateControl( AParent, 'BUTTON', //
+ WS_VISIBLE or WS_CHILD or //
+ BS_PUSHLIKE or WS_TABSTOP, False, @ButtonActions ); //
+ with fBoundsRect do //
+ Bottom := Top + 22; //
+ fTextAlign := taCenter; //
+ Caption := ACaption; //
+end; //
+ //
+constructor TControl.CreateBitBtn(AParent: PControl; //
+ const ACaption: AnsiString; AOptions: TBitBtnOptions; ALayout: TGlyphLayout; //
+ AGlyphBitmap: HBitmap; AGlyphCount: Integer); //
+var //
+ B: TBitmapInfo; //
+ W, H: Integer; //
+begin //
+ CreateControl( AParent, 'BUTTON', WS_VISIBLE or WS_CHILD or //
+ WS_TABSTOP or BS_OWNERDRAW, False, @ButtonActions ); //
+ fBitBtnOptions := AOptions; //
+ fGlyphLayout := ALayout; //
+ fGlyphBitmap := AGlyphBitmap; //
+ with fBoundsRect do //
+ begin //
+ Bottom := Top + 22; //
+ W := 0; H := 0; //
+ if AGlyphBitmap <> 0 then //
+ begin //
+ if bboImageList in AOptions then //
+ ImageList_GetIconSize( AGlyphBitmap, W, H ) //
+ else //
+ begin //
+ if GetObject( AGlyphBitmap, Sizeof(B), @B ) > 0 then //
+ begin //
+ W := B.bmiHeader.biWidth; //
+ H := B.bmiHeader.biHeight; //
+ if AGlyphCount = 0 then //
+ AGlyphCount := W div H; //
+ if AGlyphCount > 1 then //
+ W := W div AGlyphCount; //
+ end; //
+ end; //
+ if W > 0 then //
+ if ACaption = '' then //
+ Right := Left + W //
+ else Right := Right + W; //
+ if H > 0 then //
+ Bottom := Top + H; //
+ if not ( bboNoBorder in AOptions ) then //
+ begin //
+ if W > 0 then Inc( Right, 2 ); //
+ if H > 0 then Inc( Bottom, 2 ); //
+ end; //
+ end; //
+ fGlyphWidth := W; //
+ fGlyphHeight := H; //
+ end; //
+ fGlyphCount := AGlyphCount; //
+ if AParent <> nil then //
+ AParent.AttachProc( WndProc_DrawItem ); //
+ AttachProc( WndProcBitBtn ); //
+ fTextAlign := taCenter; //
+ Caption := ACaption; //
+end; //
+ //
+constructor TControl.CreateLabel(AParent: PControl; //
+ const ACaption: AnsiString); //
+begin //
+ CreateControl( AParent, 'STATIC', WS_VISIBLE or WS_CHILD or //
+ SS_LEFTNOWORDWRAP or SS_NOPREFIX or SS_NOTIFY, //
+ False, @LabelActions );
+ aAutoSzX := 1;
+ aAutoSzY := 1;
+ {$IFDEF USE_FLAGS} fFlagsG1 := fFlagsG1 + [G1_SizeRedraw, G1_IsStaticControl];
+ {$ELSE} fSizeRedraw := True;
+ fIsStaticControl := 1; //
+ {$ENDIF} //
+ fBoundsRect.Bottom := fBoundsRect.Top + 22; //
+ Caption := ACaption; //
+end; //
+ //
+constructor TControl.CreateWordWrapLabel(AParent: PControl; //
+ const ACaption: AnsiString); //
+begin //
+ CreateLabel( AParent, ACaption ); //
+ fBoundsRect.Bottom := fBoundsRect.Top + 44; //
+ fStyle := fStyle and not SS_LEFTNOWORDWRAP; //
+end; //
+ //
+constructor TControl.CreateLabelEffect(AParent: PControl; ACaption: AnsiString; //
+ AShadowDeep: Integer); //
+begin //
+ CreateLabel( AParent, ACaption ); //
+ {$IFDEF USE_FLAGS} exclude( fFlagsG1, G1_IsStaticControl );
+ {$ELSE} fIsStaticControl := 0; {$ENDIF}
+ AttachProc( WndProcLabelEffect ); //
+ fTextAlign := taCenter; //
+ fTextColor := clBtnShadow; //
+ fShadowDeep := AShadowDeep; //
+ {$IFDEF USE_FLAGS} include( fFlagsG1, G1_IgnoreWndCaption );
+ {$ELSE} fIgnoreWndCaption := True; {$ENDIF} //
+ with fBoundsRect do //
+ begin //
+ Bottom := Top + 40; //
+ end; //
+end; //
+ //
+constructor TControl.CreatePaintBox(AParent: PControl); //
+begin //
+ CreateLabel( AParent, '' ); //
+ with fBoundsRect do //
+ begin //
+ Right := Left + 40; //
+ Bottom := Top + 40; //
+ end; //
+end; //
+ //
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION} //Pascal //
+constructor TControl.CreateGradientPanel(AParent: PControl; AColor1, //
+ AColor2: TColor); //
+begin //
+ CreateLabel( AParent, '' ); //
+ AttachProc( WndProcGradient ); //
+ fColor2 := AColor2; //
+ fColor1 := AColor1; //
+ with fBoundsRect do //
+ begin //
+ Right := Left + 40; //
+ Bottom := Top + 40; //
+ end; //
+end; //
+{$ENDIF PAS_VERSION} //
+ //
+constructor TControl.CreateGradientPanelEx(AParent: PControl; AColor1, //
+ AColor2: TColor; AStyle: TGradientStyle; ALayout: TGradientLayout); //
+begin //
+ CreateLabel( AParent, '' ); //
+ AttachProc( WndProcGradientEx ); //
+ fColor2 := AColor2; //
+ fColor1 := AColor1; //
+ fGradientStyle := AStyle; //
+ fGradientLayout := ALayout; //
+ with fBoundsRect do //
+ begin //
+ Right := Left + 40; //
+ Bottom := Top + 40; //
+ end; //
+end; //
+ //
+constructor TControl.CreateGroupbox(AParent: PControl; //
+ const ACaption: AnsiString); //
+begin //
+ CreateButton( AParent, ACaption ); //
+ with fBoundsRect do //
+ begin //
+ Right := Left + 100; //
+ Bottom := Top + 100; //
+ end; //
+ fStyle := WS_VISIBLE or WS_CHILD or BS_GROUPBOX or WS_TABSTOP; //
+ fClientTop := 22; //
+ fClientLeft := 2; //
+ fClientBottom := 2; //
+ fClientRight := 2; //
+ fTabstop := False; //
+end; //
+ //
+constructor TControl.CreateCheckbox(AParent: PControl; //
+ const ACaption: AnsiString); //
+begin //
+ CreateButton( AParent, ACaption ); //
+ with fBoundsRect do //
+ begin //
+ Right := Left + 72; //
+ end; //
+ fStyle := WS_VISIBLE or WS_CHILD or //
+ BS_AUTOCHECKBOX or WS_TABSTOP; //
+end; //
+ //
+constructor TControl.CreateRadiobox(AParent: PControl; //
+ const ACaption: AnsiString); //
+begin //
+ CreateCheckbox( AParent, ACaption ); //
+ fStyle := WS_VISIBLE or WS_CHILD or //
+ BS_RADIOBUTTON or WS_TABSTOP or WS_GROUP; //
+ fControlClick := ClickRadio; //
+ if AParent <> nil then //
+ begin //
+ AParent.fRadioLast := fMenu; //
+ if AParent.fRadio1st = 0 then //
+ begin //
+ AParent.fRadio1st := fMenu; //
+ SetRadioChecked; //
+ end; //
+ end; //
+end; //
+ //
+constructor TControl.CreateEditbox(AParent: PControl; //
+ AOptions: TEditOptions); //
+var Flags: Integer; //
+begin //
+ Flags := MakeFlags( @AOptions, EditFlags ); //
+ if not(eoMultiline in AOptions) then //
+ Flags := Flags and not(WS_HSCROLL or WS_VSCROLL); //
+ CreateControl( AParent, 'EDIT', WS_VISIBLE or WS_CHILD or WS_TABSTOP //
+ or WS_BORDER or Flags, True, @EditActions ); //
+ aAutoSzY := 6;
+//YS fCursor := LoadCursor( 0, IDC_IBEAM ); // //YS
+ with fBoundsRect do //
+ begin //
+ Right := Left + 100; //
+ Bottom := Top + 22; //
+ if eoMultiline in AOptions then //
+ begin //
+ Right := Right + 100; //
+ Bottom := Top + 200; //
+ end; //
+ end; //
+ fColor := clWindow; //
+ fLookTabKeys := [ tkTab, tkUpDown, tkPageUpPageDn ]; //
+ if eoMultiline in AOptions then //
+ fLookTabKeys := [ tkTab ]; //
+ if eoWantTab in AOptions then //
+ exclude( fLookTabKeys, tkTab );
+end; //
+ //
+constructor TControl.CreatePanel(AParent: PControl; AStyle: TEdgeStyle); //
+begin //
+ CreateControl( AParent, 'STATIC', WS_VISIBLE or WS_CHILD or //
+ SS_LEFTNOWORDWRAP or SS_NOPREFIX or SS_NOTIFY, False, //
+ @LabelActions );
+ aAutoSzX := 1;
+ aAutoSzY := 1;
+ with fBoundsRect do //
+ begin //
+ Right := Left + 100; //
+ Bottom := Top + 100; //
+ end; //
+ Style := Style or Edgestyles[ AStyle ]; //
+ ExStyle := ExStyle or WS_EX_CONTROLPARENT; //
+end; //
+ //
+constructor TControl.CreateSplitter(AParent: PControl; AMinSizePrev, //
+ AMinSizeNext: Integer; EdgeStyle: TEdgeStyle); //
+var PrevCtrl: PControl; //
+ Sz0: Integer; //
+begin //
+ CreatePanel( AParent, EdgeStyle ); //
+ fSplitMinSize1 := AMinSizePrev; //
+ fSplitMinSize2 := AMinSizeNext; //
+ Sz0 := 4; //
+ with fBoundsRect do //
+ begin //
+ Right := Left + Sz0; //
+ Bottom := Top + Sz0; //
+ end; //
+ if AParent <> nil then //
+ begin //
+ if AParent.fChildren.fCount > 1 then //
+ begin //
+ PrevCtrl := AParent.fChildren.{$IFDEF TLIST_FAST} Items {$ELSE} fItems {$ENDIF}[ AParent.fChildren.fCount - 2 ]; //
+ case PrevCtrl.FAlign of //
+ caLeft, caRight: //
+ begin //
+ fCursor := LoadCursor( 0, IDC_SIZEWE ); //
+ end; //
+ caTop, caBottom: //
+ begin //
+ fCursor := LoadCursor( 0, IDC_SIZENS ); //
+ end; //
+ end; //
+ Align := PrevCtrl.FAlign; //
+ end; //
+ end; //
+ AttachProc( WndProcSplitter ); //
+end; //
+ //
+constructor TControl.CreateListbox(AParent: PControl; //
+ AOptions: TListOptions); //
+var Flags: Integer; //
+begin //
+ Flags := MakeFlags( @AOptions, ListFlags ); //
+ CreateControl( AParent, 'LISTBOX', WS_VISIBLE or WS_CHILD or WS_TABSTOP //
+ or WS_BORDER or WS_VSCROLL //
+ or LBS_NOTIFY or Flags, True, @ListActions ); //
+ with fBoundsRect do //
+ begin //
+ Right := Right + 100; //
+ Bottom := Top + 200; //
+ end; //
+ fColor := clWindow; //
+ fLookTabKeys := [ tkTab, tkLeftRight ]; //
+end; //
+ //
+constructor TControl.CreateCombobox(AParent: PControl; //
+ AOptions: TComboOptions); //
+var Flags: Integer; //
+begin //
+ Flags := MakeFlags( @AOptions, ComboFlags ); //
+ CreateControl( AParent, 'COMBOBOX', //
+ WS_VISIBLE or WS_CHILD or WS_VSCROLL or //
+ CBS_DROPDOWN or CBS_HASSTRINGS or WS_TABSTOP or Flags, //
+ True, @ComboActions ); //
+ aAutoSzY := 6;
+ fCreateWndExt := CreateComboboxWnd; //
+ //fDropDownProc := ComboboxDropDown; //
+ fClsStyle := fClsStyle or CS_DBLCLKS; //
+ with fBoundsRect do //
+ begin //
+ Right := Left + 100; //
+ Bottom := Top + 22; //
+ end; //
+ fColor := clWindow; //
+ fLookTabKeys := [ tkTab ]; //
+ if coReadOnly in AOptions then //
+ fLookTabKeys := [ tkTab, tkLeftRight ]; //
+end; //
+ //
+constructor TControl.CreateCommonControl(AParent: PControl; //
+ AClassName: PAnsiChar; AStyle: DWORD; ACtl3D: Boolean; //
+ Actions: PCommandActions); //
+begin //
+ {*************} DoInitCommonControls( ICC_WIN95_CLASSES ); //
+ CreateControl( AParent, AClassName, AStyle, ACtl3D, Actions ); //
+ {$IFDEF USE_FLAGS} include( fFlagsG2, G2_IsCommonCtl );
+ {$ELSE} fIsCommonControl := True; {$ENDIF}
+ if AParent <> nil then //
+ begin //
+ AttachProc( WndProcParentResize ); //
+ AParent.AttachProc( WndProcResize ); //
+ AttachProc( WndProcCommonNotify ); //
+ AParent.AttachProc( WndProcNotify ); //
+ end; //
+end; //
+ //
+constructor TControl.CreateRichEdit1(AParent: PControl; //
+ AOptions: TEditOptions); //
+var Flags, I: Integer; //
+begin //
+ if FRichEditModule = 0 then //
+ begin //
+ for I := 0 to High( RichEditLibnames ) do //
+ begin //
+ FRichEditModule := LoadLibrary( RichEditLibnames[ I ] ); //
+ if FRichEditModule > HINSTANCE_ERROR then break; //
+ RichEditClass := RichEditClasses[ I ]; //
+ end; //
+ if FRichEditModule <= HINSTANCE_ERROR then //
+ FRichEditModule := 0; //
+ end; //
+ Flags := MakeFlags( @AOptions, RichEditFlags ); //
+ CreateCommonControl( AParent, RichEditClass, WS_VISIBLE or WS_CHILD //
+ or WS_TABSTOP or WS_BORDER or ES_MULTILINE or Flags, //
+ True, @RichEditActions ); //
+ //
+ AttachProc( WndProcRichEditNotify ); //
+ {$IFDEF USE_FLAGS} exclude( fFlagsG2, G2_DoubleBuffered );
+ {$ELSE} fDoubleBuffered := False; {$ENDIF}
+ {$IFDEF USE_FLAGS} include( fFlagsG1, G1_CanNotDoublebuf );
+ {$ELSE} fCannotDoubleBuf := True; {$ENDIF} //
+ with fBoundsRect do //
+ begin //
+ Right := Right + 100; //
+ Bottom := Top + 200; //
+ end; //
+ fColor := clWindow; //
+ fLookTabKeys := [ tkTab ]; //
+ if eoWantTab in AOptions then //
+ fLookTabKeys := [ ]; //
+ Perform( EM_SETEVENTMASK, 0, //
+ ENM_CHANGE or ENM_SELCHANGE or ENM_REQUESTRESIZE or //
+ ENM_PROTECTED or $04000000 {ENM_LINK} ); //
+ Perform( EM_SETBKGNDCOLOR, 0, Color2RGB(fColor)); //
+end; //
+ //
+constructor TControl.CreateRichEdit(AParent: PControl; //
+ AOptions: TEditOptions); //
+var OldRichEditClass, OldRichEditLib: PAnsiChar; //
+begin //
+ if OleInit then //
+ begin //
+ OldRichEditClass := RichEditClass; //
+ OldRichEditLib := RichEditLib; //
+ CreateRichEdit1( AParent, AOptions ); //
+ fCharFmtDeltaSz := 24; //
+ fParaFmtDeltaSz := sizeof( TParaFormat2 ) - sizeof( RichEdit.TParaFormat ); //
+ RichEditClass := OldRichEditClass; //
+ RichEditLib := OldRichEditLib; //
+ end else //
+ CreateRichEdit1( AParent, AOptions ); //
+end; //
+ //
+constructor TControl.CreateProgressbar(AParent: PControl); //
+const ProgressBarFlags: array[ TProgressbarOption ] of Integer = //
+ (PBS_VERTICAL, PBS_SMOOTH ); //
+begin //
+ CreateCommonControl( AParent, PROGRESS_CLASS, //
+ WS_CHILD or WS_VISIBLE, True, nil ); //
+ with fBoundsRect do //
+ begin //
+ Right := Left + 300; //
+ Bottom := Top + 20; //
+ end; //
+ fMenu := 0; //
+ fTextColor := clHighlight; //
+end; //
+ //
+constructor TControl.CreateProgressbarEx(AParent: PControl; //
+ AOptions: TProgressbarOptions); //
+const ProgressBarFlags: array[ TProgressbarOption ] of Integer = //
+ (PBS_VERTICAL, PBS_SMOOTH ); //
+begin //
+ CreateProgressbar( AParent ); //
+ fStyle := fStyle or DWORD( MakeFlags( @AOptions, ProgressBarFlags ) ); //
+end; //
+ //
+constructor TControl.CreateListView(AParent: PControl; //
+ AStyle: TListViewStyle; AOptions: TListViewOptions; AImageListSmall, //
+ AImageListNormal, AImageListState: PImageList); //
+begin //
+ CreateCommonControl( AParent, WC_LISTVIEW, ListViewStyles[ AStyle ] or //
+ LVS_SHAREIMAGELISTS or WS_CHILD or WS_VISIBLE or WS_TABSTOP, //
+ True, @ListViewActions ); //
+ fLVOptions := AOptions; //
+ fLVStyle := AStyle; //
+ fCreateWndExt := ApplyImageLists2ListView; //
+ with fBoundsRect do //
+ begin //
+ Right := Left + 200; //
+ Bottom := Top + 150; //
+ end; //
+ ImageListSmall := AImageListSmall; //
+ ImageListNormal := AImageListNormal; //
+ ImageListState := AImageListState; //
+ fLVTextBkColor := clWindow; //
+ fLookTabKeys := [ tkTab ]; //
+end; //
+ //
+constructor TControl.CreateTreeView(AParent: PControl; //
+ AOptions: TTreeViewOptions; AImgListNormal, AImgListState: PImageList); //
+var Flags: Integer; //
+begin //
+ Flags := MakeFlags( @AOptions, TreeViewFlags ); //
+ CreateCommonControl( AParent, WC_TREEVIEW, Flags or WS_VISIBLE or //
+ WS_CHILD or WS_TABSTOP, True, @TreeViewActions ); //
+ fCreateWndExt := ApplyImageLists2Control; //
+ fColor := clWindow; //
+ AttachProc( WndProcTreeView ); //
+ with fBoundsRect do //
+ begin //
+ Right := Left + 150; //
+ Bottom := Top + 200; //
+ end; //
+ ImageListNormal := AImgListNormal; //
+ ImageListState := AImgListState; //
+ fLookTabKeys := [ tkTab ]; //
+end; ///////////////////////////////////////////////////////////////////////////
+constructor TControl.CreateTabControl(AParent: PControl; ATabs: array of String;//
+ AOptions: TTabControlOptions; //
+ AImgList: PImageList; AImgList1stIdx: Integer); //
+var I, II : Integer; //
+ Flags: Integer; //
+begin Flags := MakeFlags( @AOptions, TabControlFlags ); //
+ if tcoFocusTabs in AOptions then //
+ Flags := Flags or (WS_TABSTOP or TCS_FOCUSONBUTTONDOWN); //
+ CreateCommonControl( AParent, WC_TABCONTROL, //
+ Flags or (WS_CHILD or WS_CLIPSIBLINGS or WS_CLIPCHILDREN or //
+ WS_VISIBLE), True, @TabControlActions ); //
+ if not( tcoBorder in AOptions ) then //
+ fExStyle := fExStyle and not WS_EX_CLIENTEDGE; //
+ AttachProc( WndProcTabControl ); //
+ with fBoundsRect do //
+ begin //
+ Right := Left + 100; //
+ Bottom := Top + 100; //
+ end; //
+ if AImgList <> nil then //
+ Perform( TCM_SETIMAGELIST, 0, AImgList.Handle ); //
+ II := AImgList1stIdx; //
+ for I := 0 to High( ATabs ) do //
+ begin //
+ TC_Insert( I, ATabs[ I ], II ); //
+ Inc( II ); //
+ end; //
+ fLookTabKeys := [ tkTab ]; //
+end; ///////////////////////////////////////////////////////////////////////////
+constructor TControl.CreateToolbar(AParent: PControl; //
+ AAlign: TControlAlign; AOptions: TToolbarOptions; ABitmap: HBitmap; //
+ AButtons: array of PAnsiChar; ABtnImgIdxArray: array of Integer); //
+var Flags: DWORD; //
+begin //
+ if not( tboTextBottom in AOptions ) then //
+ include( AOptions, tboTextRight );
+ if tboTextRight in AOptions then //
+ exclude( AOptions, tboTextBottom );
+ Flags := MakeFlags( @AOptions, ToolbarOptions ); //
+ CreateCommonControl( AParent, TOOLBARCLASSNAME, ToolbarAligns[ Align ] or //
+ WS_CHILD or WS_VISIBLE {or WS_TABSTOP} //
+ or TBSTYLE_TOOLTIPS or Flags, //
+ (not (Align in [caNone])) and //
+ not (tboNoDivider in AOptions), nil ); //
+ fCommandActions.aClear := ClearToolbar; //
+ fCommandActions.aGetCount := TB_BUTTONCOUNT; //
+ with fBoundsRect do //
+ begin if AAlign in [ caNone ] then //
+ begin Bottom := Top + 26; //
+ Right := Left + 1000; //
+ end else //
+ begin Left := 0; Right := 0; //
+ Top := 0; Bottom := 0; //
+ end; //
+ end; //
+ Perform(TB_SETEXTENDEDSTYLE, 0, Perform(TB_GETEXTENDEDSTYLE, 0, 0) or //
+ TBSTYLE_EX_DRAWDDARROWS); //
+ AttachProc( WndProcToolbarCtrl ); //
+ Perform( TB_BUTTONSTRUCTSIZE, Sizeof( TTBButton ), 0 ); //
+ Perform( TB_SETINDENT, fMargin, 0 ); //
+ with fBoundsRect do //
+ begin //
+ if AAlign in [ caLeft, caRight ] then //
+ Right := Left + 24 //
+ else if not (AAlign in [caNone]) then //
+ Bottom := Top + 22; //
+ end; //
+ if ABitmap <> 0 then //
+ TBAddBitmap( ABitmap ); //
+ TBAddButtons( AButtons, ABtnImgIdxArray ); //
+ Perform( WM_SIZE, 0, 0 ); //
+end; ///////////////////////////////////////////////////////////////////////////
+constructor TImageList.CreateImageList(POwner: Pointer); //
+var AOwner: PControl; //
+begin {*************} DoInitCommonControls( ICC_WIN95_CLASSES ); //
+ Create; //
+ FAllocBy := 1; //
+ FMasked := True; //
+ if POwner = nil then exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ FBkColor := TColor( CLR_NONE );
+ AOwner := POwner; //
+ FControl := AOwner; //
+ fNext := PImageList( AOwner.fImageList ); //
+ if AOwner.fImageList <> nil then //
+ PImageList( AOwner.fImageList ).fPrev := @Self; //
+ AOwner.fImageList := @Self; //
+end;////////////////////////////////////////////////////////////////////////////
+constructor TThread.ThreadCreate; //
+begin IsMultiThread := True; //
+ Create; //
+ FSuspended := True; //
+ FHandle := CreateThread( nil, // no security //
+ 0, // the same stack size //
+ @ThreadFunc, // thread entry point //
+ @Self, // parameter to pass to ThreadFunc //
+ CREATE_SUSPENDED, // always SUSPENDED //
+ FThreadID ); // receive thread ID //
+end;////////////////////////////////////////////////////////////////////////////
+constructor TThread.ThreadCreateEx( const Proc: TOnThreadExecute ); //
+begin //
+ ThreadCreate; //
+ OnExecute := Proc; //
+ Resume; //
+end; //
+{$ENDIF USE_CONSTRUCTORS} //****************************************************//
+procedure InvalidateExW( Wnd: HWnd );
+begin InvalidateRect( Wnd, nil, TRUE );
+ Wnd := GetWindow( Wnd, GW_CHILD );
+ while Wnd <> 0 do
+ begin
+ InvalidateExW( Wnd );
+ Wnd := GetWindow( Wnd, GW_HWNDNEXT );
+ end;
+end; ///////////////////////////////////////////////////////////////////////////
+procedure TControl.InvalidateEx;
+begin if fHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ InvalidateExW( fHandle );
+end; ///////////////////////////////////////////////////////////////////////////
+procedure InvalidateNCW( Wnd: HWnd; Recursive: Boolean );
+begin SendMessage( Wnd, WM_NCPAINT, 1, 0 );
+ if not Recursive then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Wnd := GetWindow( Wnd, GW_CHILD );
+ while Wnd <> 0 do
+ begin
+ InvalidateNCW( Wnd, Recursive );
+ Wnd := GetWindow( Wnd, GW_HWNDNEXT );
+ end;
+end; ///////////////////////////////////////////////////////////////////////////
+procedure TControl.InvalidateNC(Recursive: Boolean);
+begin
+ if fHandle = 0 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ InvalidateNCW( fHandle, Recursive );
+end; ///////////////////////////////////////////////////////////////////////////
+procedure TControl.SetClientMargin(const Index: Integer; Value: ShortInt);
+begin
+ case Index of
+ 1: fClientTop := Value;
+ 2: fClientBottom := Value;
+ 3: fClientLeft := Value;
+ 4: fClientRight := Value;
+ end;
+ {$IFNDEF OLD_ALIGN}include(fAligning,oaFromSelf);{$ENDIF}//???
+ Global_Align( @Self );
+end;
+{$IFDEF F_P}
+function TControl.GetClientMargin(const Index: Integer): Integer;
+begin
+ CASE Index OF
+ 1: Result := fClientTop;
+ 2: Result := fClientBottom;
+ 3: Result := fClientLeft;
+ 4: Result := fClientRight;
+ END;
+end;
+{$ENDIF F_P}
+{------------------------------------------------------------------------------}
+{ G R A P H C O N T R O L S }
+{------------------------------------------------------------------------------}
+type TGrayTextData = packed record
+ Ctl: PControl;
+ W, H: Integer;
+ Flags: DWORD;
+ end;
+ PGrayTextData = ^TGrayTextData; ///////////////////////////////////////////
+function DrawTextGrayed( DC: HDC; lData, wData, cX, cY: Integer ): BOOL; stdcall;
+var GDT: PGrayTextData;
+ R: TRect;
+begin
+ GDT := Pointer( lData );
+ R := MakeRect( 0, 0, cX, cY );
+ DrawFormattedText( GDT.Ctl, DC, R, GDT.Flags or $80000000 );
+ Result := TRUE;
+end; ///////////////////////////////////////////////////////////////////////////
+procedure DrawFormattedText( Ctl: PControl; DC: HDC; var R: TRect; Flags: DWORD {EditCtl: Boolean} );
+var Fmt: DWORD;
+ OldFont: Integer;
+ OldBrush: Integer;
+ OldBk: Integer;
+ ParentHavingFont: PControl;
+ GTD: TGrayTextData;
+ dX, dY: Integer;
+ R1: TRect;
+begin Fmt := DT_EXPANDTABS or Flags and $7FFFFFFF;
+ if Ctl.WordWrap then
+ Fmt := Fmt or DT_WORDBREAK;
+ if Flags and DT_EDITCONTROL <> 0 then
+ Inc( R.Left, 4 );
+ ParentHavingFont := Ctl;
+ while (ParentHavingFont <> nil) and ( ParentHavingFont.FFont = nil )
+ and {$IFDEF USE_FLAGS} not(G3_IsForm in ParentHavingFont.fFlagsG3)
+ {$ELSE} not ParentHavingFont.IsForm {$ENDIF} do
+ ParentHavingFont := ParentHavingFont.Parent;
+ OldFont := 0;
+ if ( ParentHavingFont <> nil ) then
+ begin OldFont := SelectObject( DC, ParentHavingFont.Font.Handle );
+ SetTextColor( DC, ParentHavingFont.Font.FColorRGB );
+ end;
+ R1 := R;
+ {$IFDEF UNICODE_CTRLS}Windows.DrawTextW
+ {$ELSE} Windows.DrawTextA
+ {$ENDIF}( DC, PKOLChar( Ctl.Caption ), Length( Ctl.Caption ), R,
+ Fmt or DT_CALCRECT ); // TODO: fixme (Length('kanji') != WStrLen('kanji'))
+ CASE Ctl.fTextAlign OF
+ taCenter: dX := (R1.Right - R1.Left - (R.Right - R.Left)) div 2;
+ taRight: dX := R1.Right - R.Right;
+ else dX := 0;
+ END;
+ CASE Ctl.fVerticalAlign OF
+ vaCenter: dY := (R1.Bottom - R1.Top - (R.Bottom - R.Top)) div 2;
+ vaBottom: dY := R1.Bottom - R.Bottom;
+ else dY := 0;
+ END;
+ OffsetRect( R, dX, dY );
+ if {$IFDEF USE_FLAGS} not(F3_Disabled in Ctl.fStyle.f3_Style)
+ {$ELSE} Ctl.fEnabled {$ENDIF}
+ or (Flags and $80000000 <> 0) then
+ begin OldBk := SetBkMode( DC, TRANSPARENT );
+ OldBrush := SelectObject( DC, GetStockObject( NULL_BRUSH ) );
+ {$IFDEF UNICODE_CTRLS}Windows.DrawTextW
+ {$ELSE} Windows.DrawTextA
+ {$ENDIF}( DC, PKOLChar( Ctl.Caption ), Length( Ctl.Caption ), R, Fmt );
+ SelectObject( DC, OldBrush );
+ SetBkMode( DC, OldBk );
+ end else
+ begin GTD.Ctl := Ctl;
+ GTD.W := R.Right - R.Left;
+ GTD.H := R.Bottom - R.Top;
+ GTD.Flags := Flags;
+ Windows.DrawState( DC, GetStockObject( NULL_BRUSH ), @ DrawTextGrayed,
+ Integer( @ GTD ), Length( Ctl.fCaption ), R.Left, R.Top, R.Right - R.Left, R.Bottom - R.Top,
+ DST_COMPLEX or DSS_DISABLED );
+ end;
+ if ( ParentHavingFont <> nil ) then
+ SelectObject( DC, OldFont );
+end;
+
+{$IFDEF USE_GRAPHCTLS}
+{$IFDEF GRAPHCTL_XPSTYLES}
+type TOpenThemeDataProc = function( Wnd: HWnd; pszClassList: PWideChar ): THandle;
+ stdcall;
+ TDrawThemeBackground = function( Theme: THandle; DC: HDC; iPartId: Integer;
+ iStateId: Integer; Rect, ClipRect: PRect ): Integer;
+ stdcall;
+ TGetThemeBackgroundContentRect = function( Theme: THandle; DC: HDC;
+ iPartId, iStateId: Integer; Rect, ContentRect: PRect ):
+ Integer; stdcall;
+ TDrawThemeText = function( Theme: THandle; DC: HDC; iPartId, iStateId: Integer;
+ pszText: PWideChar; iCharCount: Integer;
+ dwTextFlags, dwTextFlags2: DWORD; Rect: PRect ): Integer;
+ stdcall;
+ TCloseThemeData = function( Theme: THandle ): Integer; stdcall;
+var fOpenThemeDataProc: TOpenThemeDataProc;
+ fDrawthemeBackground: TDrawThemeBackground;
+ fGetThemeBackgroundcontentRect: TGetThemeBackgroundContentRect;
+ fDrawThemeText: TDrawThemeText;
+ fCloseThemeData: TCloseThemeData;
+ uxtheme_lib: THandle;
+function OpenThemeDataProc: TOpenThemeDataProc;
+begin Result := nil;
+ if Integer(uxtheme_lib) = -1 then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if uxtheme_lib = 0 then
+ uxtheme_lib := LoadLibrary( 'uxtheme' );
+ if uxtheme_lib = 0 then
+ begin uxtheme_lib := DWORD( -1 ); Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ fOpenThemeDataProc := GetProcAddress( uxtheme_lib, 'OpenThemeData' );
+ fDrawthemeBackground := GetProcAddress( uxtheme_lib, 'DrawThemeBackground' );
+ fGetThemeBackgroundcontentRect := GetProcAddress( uxtheme_lib, 'GetThemeBackgroundContentRect' );
+ fDrawThemeText := GetProcAddress( uxtheme_lib, 'DrawThemeText' );
+ fCloseThemeData := GetProcAddress( uxtheme_lib, 'CloseThemeData' );
+ if not Assigned( fOpenThemeDataProc ) or
+ not Assigned( fDrawThemeBackground ) or
+ not Assigned( fGetThemeBackgroundcontentRect ) or
+ not Assigned( fDrawThemeText ) or
+ not Assigned( fCloseThemeData ) then
+ begin
+ FreeLibrary( uxtheme_lib );
+ uxtheme_lib := DWORD( -1 );
+ fOpenThemeDataProc := nil;
+ fDrawThemeBackground := nil;
+ fGetThemeBackgroundcontentRect := nil;
+ fDrawThemeText := nil;
+ fCloseThemeData := nil;
+ end;
+ Result := fOpenThemeDataProc;
+end;
+
+procedure DrawFormattedTextXP( Theme: THandle; Ctl: PControl; DC: HDC;
+ var R: TRect; CtlType, CtlStates, Flags1, Flags2: Integer );
+var OldFont: Integer;
+ OldBrush: Integer;
+ ParentHavingFont: PControl;
+begin ParentHavingFont := Ctl;
+ while (ParentHavingFont <> nil) and ( ParentHavingFont.FFont = nil )
+ and {$IFDEF USE_FLAGS} not(G3_IsForm in ParentHavingFont.fFlagsG3)
+ {$ELSE} not ParentHavingFont.IsForm {$ENDIF} do
+ ParentHavingFont := ParentHavingFont.Parent;
+ OldFont := 0;
+ if ( ParentHavingFont <> nil ) then
+ OldFont := SelectObject( DC, ParentHavingFont.Font.Handle );
+ OldBrush := SelectObject( DC, GetStockObject( NULL_BRUSH ) );
+ fDrawThemeText( Theme, DC, CtlType, CtlStates, @ KOLWideString( Ctl.fCaption )[ 1 ],
+ Length( Ctl.fCaption ), Flags1, Flags2, @ R );
+ SelectObject( DC, OldBrush );
+ if ( ParentHavingFont <> nil ) then SelectObject( DC, OldFont );
+end;
+{$ENDIF}
+
+procedure PaintGraphicChildren( Self_, _Sender: PControl; DC: HDC );
+var i, sav: Integer;
+ C: PControl;
+ R: TRect;
+ rgn: HRgn;
+begin
+ for i := Self_.ChildCount-1 downto 0 do
+ begin
+ C := Self_.Children[ i ];
+ if not C.Visible then continue;
+ R := C.BoundsRect;
+ if (C.Handle = 0)
+ and {$IFDEF USE_FLAGS} (G6_GraphicCtl in C.fFlagsG6)
+ {$ELSE} not C.fWindowed {$ENDIF}
+ {$IFDEF SAFE_CODE} and Assigned( C.EV.fPaintProc ) {$ENDIF} then
+ begin
+ sav := SaveDC( DC );
+ rgn := CreateRectRgnIndirect( R );
+ ExtSelectClipRgn( DC, rgn, RGN_AND );
+ SelectClipRgn( DC, rgn );
+ DeleteObject( rgn );
+ Free_And_Nil( C.fCanvas );
+ C.fCanvas := Self_.Canvas;
+ Self_.Canvas.Brush.Assign( Self_.Brush );
+ Self_.Canvas.Font.Assign( Self_.Font ); // íå ïðèñâàèâàåòñÿ?
+ Self_.fCanvas.DeselectHandles; // íå ïîìîãàåò???
+ {$IFDEF NIL_EVENTS}
+ if Assigned( C.EV.fOnPrepaint ) then
+ {$ENDIF}
+ C.EV.fOnPrePaint( C, DC );
+ C.EV.fPaintProc( DC );
+ if Assigned( C.EV.fOnPaint ) then
+ C.EV.fOnPaint( C, DC );
+ {$IFDEF NIL_EVENTS}
+ if Assigned( C.EV.fOnPostPaint ) then
+ {$ENDIF}
+ C.EV.fOnPostPaint( C, DC );
+ C.fCanvas := nil;
+ Self_.Canvas.Brush.Assign( Self_.Brush );
+ Self_.Canvas.Font.Assign( Self_.Font );
+ RestoreDC( DC, sav );
+ ExcludeClipRect( DC, R.Left, R.Top, R.Right, R.Bottom );
+ end;
+ end;
+ if {$IFDEF USE_FLAGS} G5_IsGroupbox in Self_.fFlagsG5
+ {$ELSE} Self_.fIsGroupBox {$ENDIF} then
+ begin
+ Self_.DF.fErasingBkgnd := TRUE;
+ R := Self_.BoundsRect;
+ OffsetRect( R, -R.Left, -R.Top );
+ Self_.Canvas.FillRect( R );
+ Self_.GroupBoxPaint( DC );
+ Self_.DF.fErasingBkgnd := FALSE;
+ end else
+ if Assigned( Self_.EV.fOnPaint2 ) then
+ Self_.EV.fOnPaint2( Self_, DC )
+ else Self_.Canvas.FillRect( Self_.ClientRect );
+end;
+
+function WndProc_ParentOfGraphicCtl( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var WasOnPaint: TOnPaint;
+ i: Integer;
+ C: PControl;
+ Pt: TPoint;
+ PF: PControl;
+ save_Paint2: TOnPaint;
+begin
+ Result := FALSE;
+ if (Msg.message = WM_PAINT) {or (Msg.message = WM_PRINT)} then
+ begin WasOnPaint := Self_.EV.fOnPaint;
+ Self_.{$IFDEF EVENTS_DYNAMIC} ProvideUniqueEvents {$ELSE} EV {$ENDIF}
+ .fOnPaint2 := Self_.EV.fOnPaint;
+ //Self_.fPaintMsg := Msg;
+ {$IFDEF MAKE_METHOD}
+ TMethod( Self_.EV.fOnPaint ) := MakeMethod( Self_, @ PaintGraphicChildren );
+ {$ELSE}
+ TMethod( Self_.EV.fOnPaint ).Code := @ PaintGraphicChildren;
+ TMethod( Self_.EV.fOnPaint ).Data := Self_;
+ {$ENDIF}
+ save_Paint2 := Self_.EV.fOnPaint2;
+ if not Assigned( Self_.EV.fOnPaint2 ) then
+ begin
+ {$IFDEF MAKE_METHOD}
+ Self_.EV.fOnPaint2 := TOnPaint( MakeMethod( nil, @ DummyPaintClear ) );
+ {$ELSE}
+ TMethod( Self_.EV.fOnPaint2 ).Code := @ DummyPaintClear;
+ //TMethod( Self_.EV.fOnPaint2 ).Data := nil;
+ {$ENDIF}
+ end;
+ i := Self_.fDynHandlers.fCount;
+ Self_.fDynHandlers.fCount := Self_.fDynHandlers.IndexOf( @ WndProc_ParentOfGraphicCtl );
+ Result := EnumDynHandlers( Self_, Msg, Rslt );
+ Self_.fDynHandlers.fCount := i;
+ if not Result then
+ {Result :=} WndProcPaint( Self_, Msg, Rslt );
+ Self_.EV.fOnPaint := WasOnPaint;
+ Result := TRUE;
+ end else
+ if (Msg.message >= WM_MOUSEFIRST) and (Msg.message <= WM_MOUSELAST) then
+ begin
+ Pt.X := SmallInt( LoWord( Msg.lParam ) );
+ Pt.Y := SmallInt( HiWord( Msg.lParam ) );
+ for i := 0 to Self_.ChildCount-1 do
+ begin
+ if (i = 0) and (Self_.fPushedBtn <> nil) then
+ C := Self_.fPushedBtn
+ else C := Self_.Children[ i ];
+ if (C = Self_.fPushedBtn) OR
+ {$IFDEF USE_FLAGS}
+ (F3_Visible in C.fStyle.f3_Style)
+ and not (F3_Disabled in C.fStyle.f3_Style)
+ {$ELSE} C.fVisible and C.fEnabled {$ENDIF}
+ and PtInRect( C.BoundsRect, Pt ) then
+ begin
+ if {$IFDEF USE_FLAGS} (G6_GraphicCtl in C.fFlagsG6)
+ {$ELSE} not C.fWindowed {$ENDIF}
+ and (C.fCursor <> 0) and (C.fCursor <> Self_.fCursor) and
+ (ScreenCursor = 0) then
+ begin if Self_.fSaveCursor = 0 then
+ begin Self_.fSaveCursor := Self_.fCursor;
+ if Self_.fCursor = 0 then
+ Self_.fSaveCursor := LoadCursor( 0, IDC_ARROW );
+ end;
+ Self_.Cursor := C.fCursor;
+ Windows.SetCursor( C.fCursor );
+ end;
+ {$IFDEF GRAPHCTL_HOTTRACK}
+ if {$IFDEF USE_FLAGS} (G6_GraphicCtl in C.fFlagsG6)
+ {$ELSE} not C.fWindowed {$ENDIF}
+ and (Applet.DF.fHotCtl <> C) then
+ begin
+ if Applet.DF.fHotCtl <> nil then
+ begin
+ {$IFDEF USE_FLAGS}
+ exclude( Applet.DF.fHotCtl.fFlagsG4, G4_Hot );
+ {$ELSE} Applet.DF.fHotCtl.fHot := FALSE; {$ENDIF}
+ if {$IFDEF USE_FLAGS} (G6_GraphicCtl in Applet.DF.fHotCtl.fFlagsG6)
+ {$ELSE} not Applet.DF.fHotCtl.fWindowed {$ENDIF} then
+ begin
+ Applet.DF.fHotCtl.Invalidate;
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Applet.DF.fHotCtl.EV.fOnMouseLeave ) then
+ {$ENDIF}
+ Applet.DF.fHotCtl.EV.fOnMouseLeave( Applet.DF.fHotCtl );
+ end;
+ Applet.DF.fHotCtl.RefDec;
+ end;
+ C.RefInc;
+ Applet.DF.fHotCtl := C;
+ {$IFDEF USE_FLAGS} include( C.fFlagsG4, G4_Hot );
+ {$ELSE} C.fHot := TRUE; {$ENDIF}
+ C.Invalidate;
+ Self_.EV.fMouseLeaveProc := Self_.MouseLeaveFromParentOfGraphCtl;
+ ProvideMouseEnterLeave( Self_ );
+ {$IFDEF NIL_EVENTS}
+ if Assigned( C.EV.fOnMouseEnter ) then
+ {$ENDIF}
+ C.EV.fOnMouseEnter( C );
+ end;
+ {$ENDIF GRAPHCTL_HOTTRACK}
+ if {$IFDEF USE_FLAGS} not(G6_GraphicCtl in C.fFlagsG6)
+ {$ELSE} C.fWindowed {$ENDIF} then
+ begin
+ Msg.hwnd := C.fHandle;
+ Pt := Self_.Client2Screen( Pt );
+ Pt := C.Screen2Client( Pt );
+ Msg.lParam := Pt.Y shl 16 or (Pt.X and $FFFF);
+ end;
+ Rslt := C.WndProc( Msg );
+ if {$IFDEF USE_FLAGS} (G6_GraphicCtl in C.fFlagsG6)
+ {$ELSE} not C.fWindowed {$ENDIF} then
+ {$IFDEF NIL_EVENTS}
+ if Assigned( C.EV.fGraphCtlMouseEvent ) then
+ {$ENDIF}
+ C.EV.fGraphCtlMouseEvent( Msg )
+ else if (Msg.message = WM_LBUTTONDOWN) or
+ (Msg.message = WM_RBUTTONDOWN) or
+ (Msg.message = WM_MBUTTONDOWN) then C.DoClick;
+ Result := TRUE; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ end;
+ {$IFDEF GRAPHCTL_HOTTRACK}
+ Self_.MouseLeaveFromParentOfGraphCtl( Self_ );
+ {$ENDIF GRAPHCTL_HOTTRACK}
+ if {$IFDEF USE_FLAGS} (G5_IsGroupbox in Self_.fFlagsG5)
+ {$ELSE} Self_.fIsGroupBox {$ENDIF}
+ and (
+ (Msg.message = WM_LBUTTONDOWN) or
+ (Msg.message = WM_LBUTTONDBLCLK) or
+ (Msg.message = WM_LBUTTONUP)
+ ) then
+ begin
+ Self_.Invalidate;
+ end;
+ if Self_.fSaveCursor <> 0 then
+ begin Self_.Cursor := Self_.fSaveCursor;
+ Self_.fSaveCursor := 0;
+ if ScreenCursor = 0 then Windows.SetCursor( Self_.fCursor );
+ end;
+ end else
+ if (Msg.message >= WM_KEYFIRST) and (Msg.message <= WM_KEYLAST) then
+ begin
+ if Self_.IsControl then
+ PF := Self_.ParentForm
+ else PF := Self_;
+ if (PF.DF.fCurrentControl <> nil)
+ and {$IFDEF USE_FLAGS} (G6_GraphicCtl in PF.DF.fCurrentControl.fFlagsG6)
+ {$ELSE} not PF.DF.fCurrentControl.fWindowed {$ENDIF} then
+ begin if Assigned( PF.DF.fCurrentControl.fKeyboardProcess ) and
+ PF.DF.fCurrentControl.fKeyboardProcess( Msg, Rslt ) then
+ else Rslt := PF.DF.fCurrentControl.WndProc( Msg );
+ Result := TRUE;
+ end else
+ begin
+ if {$IFDEF USE_FLAGS} (G5_IsGroupbox in Self_.fFlagsG5)
+ {$ELSE} Self_.fIsGroupBox {$ENDIF}
+ and (Msg.wParam = WORD( ' ' )) and
+ (
+ (Msg.message = WM_KEYDOWN) or
+ (Msg.message = WM_SYSKEYDOWN) or
+ (Msg.message = WM_KEYUP) or
+ (Msg.message = WM_SYSKEYUP) or
+ (Msg.message = WM_CHAR) or
+ (Msg.message = WM_SYSCHAR)
+ ) then
+ begin
+ Self_.Invalidate;
+ end;
+ end;
+ end else
+ if Msg.message = CM_QUIT then
+ begin
+ C := Pointer( Msg.wParam );
+ C.Free;
+ end else
+ if Msg.message = CM_FOCUSGRAPHCTL then
+ begin
+ C := Pointer( Msg.wParam );
+ PF := C.ParentForm;
+ if (PF.DF.fCurrentControl <> nil) and (PF.DF.fCurrentControl <> C) then
+ begin
+ {$IFDEF USE_FLAGS}
+ exclude( PF.DF.fCurrentControl.fFlagsG6, G6_Focused );
+ {$ELSE} PF.DF.fCurrentControl.fFocused := FALSE; {$ENDIF}
+ PF.DF.fCurrentControl.Invalidate;
+ end;
+ PF.DF.fCurrentControl := C;
+ C.Parent.DF.fCurrentControl := C;
+ //C.Parent.fFocusHandle := C.Parent.fHandle;
+ {$IFDEF USE_FLAGS} include( C.fFlagsG6, G6_Focused );
+ {$ELSE} C.fFocused := TRUE; {$ENDIF}
+ if Assigned( C.EV.fOnEnter ) then
+ C.EV.fOnEnter( C );
+ C.Invalidate;
+ C.EV.fLeave := C.LeaveGraphButton;
+ C.RefDec;
+ end;
+end;////////////////////////////////////////////////////////////////////////////
+function WndProc_FormHavingGraphCtl( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var Msg2: TMsg;
+begin Result := FALSE;
+ if Msg.message = WM_ACTIVATE then
+ begin if Self_.DF.fCurrentControl <> nil then
+ Self_.DF.fCurrentControl.Invalidate;
+ end else
+ if (Msg.message >= WM_KEYFIRST) and (Msg.message <= WM_KEYLAST) then
+ begin if (Self_.DF.fCurrentControl <> nil)
+ and {$IFDEF USE_FLAGS} (G6_GraphicCtl in Self_.DF.fCurrentControl.fFlagsG6)
+ {$ELSE} not Self_.DF.fCurrentControl.fWindowed {$ENDIF} then
+ begin if (Msg.message = WM_KEYDOWN) and ((Msg.wParam = 32) or (Msg.wParam = 13)) then
+ begin if not PeekMessage( Msg2, Msg.hwnd, WM_CHAR, WM_CHAR, pm_noRemove )
+ or (Msg2.wParam <> Msg.wParam) then
+ Msg.message := WM_CHAR;
+ end else
+ if (Msg.message = WM_SYSKEYDOWN) and ((Msg.wParam = 32) or (Msg.wParam = 13)) then
+ begin if not PeekMessage( Msg2, Msg.hwnd, WM_SYSCHAR, WM_SYSCHAR, pm_noRemove ) or
+ (Msg2.wParam <> Msg.wParam) then
+ Msg.message := WM_SYSCHAR;
+ end;
+ if Assigned( Self_.DF.fCurrentControl.fKeyboardProcess ) and
+ Self_.DF.fCurrentControl.fKeyboardProcess( Msg, Rslt ) then
+ else Rslt := Self_.DF.fCurrentControl.WndProc( Msg );
+ Result := TRUE;
+ end;
+ end;
+end;////////////////////////////////////////////////////////////////////////////
+{$IFDEF GRAPHCTL_HOTTRACK}
+procedure TControl.MouseLeaveFromParentOfGraphCtl(Sender: PObj);
+var C: PControl;
+ Pt: TPoint;
+begin
+ if AppletTerminated then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ GetCursorPos( Pt );
+ Pt := Screen2Client( Pt );
+ if (Applet.DF.fHotCtl <> nil) and (fChildren.IndexOf( Applet.DF.fHotCtl ) >= 0) then
+ begin
+ C := Applet.DF.fHotCtl;
+ if PtInRect( C.BoundsRect, Pt ) then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Applet.DF.fHotCtl := nil;
+ {$IFDEF USE_FLAGS} exclude( C.fFlagsG4, G4_Hot );
+ {$ELSE} C.fHot := FALSE; {$ENDIF}
+ if {$IFDEF USE_FLAGS} (G6_GraphicCtl in C.fFlagsG6)
+ {$ELSE} not C.fWindowed {$ENDIF} then
+ C.Invalidate;
+ if Assigned( C.OnMouseLeave ) then
+ C.OnMouseLeave( C );
+ C.RefDec;
+ end;
+end;
+{$ENDIF GRAPHCTL_HOTTRACK}
+
+procedure NotifyGraphCtlAboutNewParent(Prnt, Chld: PControl);
+begin
+ if (Chld <> nil) and (Prnt <> nil) then
+ Prnt.AttachProc( WndProc_ParentOfGraphicCtl );
+end;
+
+function _NewGraphCtl( AParent: PControl; ATabStop: Boolean;
+ ACommandActions: TCommandActionsParam ): PControl;
+var IdxActions: Integer;
+begin new( Result, Create );
+ {$IFDEF DEBUG_OBJKIND} Result.fObjKind := 'TControl:GraphicControl';
+ {$ENDIF}
+ {$IFDEF COMMANDACTIONS_OBJ}
+ IdxActions := Integer( ACommandActions );
+ if IdxActions >= 120 then
+ IdxActions := PByte( ACommandActions )^;
+ if AllActions_Objs[IdxActions] <> nil then
+ begin Result.fCommandActions := AllActions_Objs[IdxActions];
+ Result.fCommandActions.RefInc;
+ end else
+ begin new( Result.fCommandActions, Create );
+ {$IFDEF DEBUG_OBJKIND}
+ Result.fCommandActions.fObjKind := 'TCommandActionsObj';
+ {$ENDIF}
+ AllActions_Objs[IdxActions] := Result.fCommandActions;
+ {$IFDEF SAFE_CODE}
+ if ACommandActions <> nil then
+ {$ENDIF}
+ Move( ACommandActions^, Result.fCommandActions.aClear, Sizeof( TCommandActions ) );
+ end;
+ Result.Add2AutoFree( Result.fCommandActions );
+ {$ELSE} {$IFDEF SAFE_CODE}
+ if ACommandActions <> nil then
+ {$ENDIF}
+ Result.fCommandActions := ACommandActions^;
+ {$ENDIF}
+ Result.PP.fDoInvalidate := InvalidateNonWindowed;
+ {$IFDEF USE_FLAGS} include( Result.fFlagsG6, G6_GraphicCtl );
+ {$ELSE} Result.fWindowed := FALSE; {$ENDIF}
+ {$IFDEF USE_FLAGS}
+ include( Result.fFlagsG3, G3_IsControl );
+ include( Result.fFlagsG4, G4_CreateVisible );
+ if ATabStop then
+ include( Result.fStyle.f2_Style, F2_TabStop );
+ {$ELSE} Result.fCreateVisible := TRUE;
+ Result.fVisible := TRUE;
+ Result.fIsControl := TRUE;
+ Result.fTabstop := ATabStop;
+ {$ENDIF}
+ Result.fMenu := CtlIdCount;
+ Inc( CtlIdCount );
+ Result.DF.fBitBtnOptions := [ bboFixed ]; // to return Checked = fChecked w/o window handle
+ {$IFDEF USE_FLAGS}
+ Result.fFlagsG1 := Result.fFlagsG1 + [ G1_IgnoreWndCaption, G1_SizeRedraw ];
+ {$ELSE} Result.fIgnoreWndCaption := TRUE;
+ Result.fSizeRedraw := TRUE;
+ {$ENDIF}
+ Result.PP.fNotifyChild := @ NotifyGraphCtlAboutNewParent;
+ if ATabStop then
+ Result.fLookTabKeys := [ tkTab, tkLeftRight, tkUpDown, tkPageUpPageDn ];
+ if AParent <> nil then
+ begin Result.Parent := AParent;
+ Result.Border := AParent.Border;
+ AParent.AttachProc( WndProc_ParentOfGraphicCtl );
+ if ATabStop then
+ begin Inc( AParent.ParentForm.fTabOrder );
+ Result.fTabOrder := AParent.ParentForm.fTabOrder;
+ end;
+ if {$IFDEF USE_FLAGS} G3_IsControl in AParent.fFlagsG3
+ {$ELSE} AParent.fIsControl {$ENDIF} then
+ AParent.ParentForm.AttachProc( WndProc_FormHavingGraphCtl );
+ if {$IFDEF USE_FLAGS} G5_IsGroupbox in APArent.fFlagsG5
+ {$ELSE} AParent.fIsGroupBox {$ENDIF} then
+ begin AParent.Style := AParent.Style and
+ not BS_GROUPBOX; // otherwise the groupbox is flickering A LOT!
+ AParent.Parent.AttachProc( WndProc_ParentOfGraphicCtl );
+ end;
+
+ Result.fFont := Result.fFont.Assign( AParent.fFont );
+ if Result.fFont <> nil then
+ begin Result.fFont.fParentGDITool := AParent.fFont;
+ Result.fFont.fOnGTChange := Result.FontChanged;
+ Result.FontChanged( Result.fFont );
+ end;
+ end;
+ Result.fBoundsRect.Right := Result.fBoundsRect.Left + 64;
+ Result.fBoundsRect.Bottom := Result.fBoundsRect.Top + 22;
+ Result.EV.fOnPaint := nil;
+
+ {$IFDEF GRAPHCTL_XPSTYLES}
+ if WinVer < wvXP then
+ DoNotDrawGraphCtlsUsingXPStyles := TRUE;
+ {$ENDIF}
+end;////////////////////////////////////////////////////////////////////////////
+function NewGraphLabel( AParent: PControl; const ACaption: AnsiString ): PControl;
+begin {$IFDEF INPACKAGE} Result := NewLabel( AParent, ACaption );
+ {$ELSE} Result := _NewGraphCtl( AParent, FALSE,
+ {$IFDEF PACK_COMMANDACTIONS} LabelActions_Packed
+ {$ELSE} @LabelActions {$ENDIF} );
+ Result.aAutoSzX := 1;
+ Result.aAutoSzY := 1;
+ Result.EV.fPaintProc := Result.GraphicLabelPaint;
+ Result.Caption := ACaption;
+ {$ENDIF}
+end;////////////////////////////////////////////////////////////////////////////
+function NewWordWrapGraphLabel( AParent: PControl; const ACaption: KOLString ): PControl;
+begin {$IFDEF INPACKAGE} Result := NewWordWrapLabel( AParent, ACaption );
+ {$ELSE} Result := NewGraphLabel( AParent, ACaption );
+ {$IFDEF USE_FLAGS} include( Result.fFlagsG1, G1_WordWrap );
+ {$ELSE} Result.fWordWrap := TRUE; {$ENDIF}
+ {$ENDIF}
+end;////////////////////////////////////////////////////////////////////////////
+function NewGraphPaintBox( AParent: PControl ): PControl;
+begin {$IFDEF INPACKAGE} Result := NewPaintbox( AParent );
+ {$ELSE} Result := NewGraphLabel( AParent, '' ); {$ENDIF}
+end;////////////////////////////////////////////////////////////////////////////
+procedure ClickGraphCheck(Sender: PObj);
+var Ctl: PControl;
+begin Ctl := Pointer( Sender );
+ if not Ctl.Enabled then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Ctl.Focused := TRUE;
+ if Assigned( Ctl.OnEnter ) then
+ Ctl.OnEnter( Ctl );
+ {$IFDEF USE_FLAGS}
+ if G4_Checked in Ctl.fFlagsG4 then
+ exclude( Ctl.fFlagsG4, G4_Checked )
+ else include( Ctl.fFlagsG4, G4_Checked );
+ {$ELSE} Ctl.fChecked := not Ctl.fChecked; {$ENDIF}
+ Ctl.Invalidate;
+ if Assigned( Ctl.OnClick ) then
+ Ctl.OnClick( Ctl );
+end;////////////////////////////////////////////////////////////////////////////
+function NewGraphCheckBox( AParent: PControl; const ACaption: KOLString ): PControl;
+begin {$IFDEF INPACKAGE} Result := NewCheckbox( AParent, ACaption );
+ {$ELSE} Result := NewGraphButton( AParent, ACaption );
+ Result.TextAlign := taLeft;
+ Result.aAutoSzX := GetSystemMetrics( SM_CXMENUCHECK ) + 4;
+ Result.EV.fPaintProc := Result.GraphicCheckBoxPaint;
+ Result.EV.fGraphCtlMouseEvent := Result.GraphicCheckBoxMouse;
+ Result.PP.fControlClick := @ ClickGraphCheck;
+ {$ENDIF}
+end;////////////////////////////////////////////////////////////////////////////
+procedure ClickGraphRadio(Sender: PObj);
+var Ctl, C: PControl;
+ i: Integer;
+begin Ctl := Pointer( Sender );
+ if not Ctl.Enabled then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ Ctl.Focused := TRUE;
+ Ctl.Checked := TRUE;
+ if Ctl.Parent <> nil then
+ for i := 0 to Ctl.Parent.ChildCount-1 do
+ begin C := Ctl.Parent.Children[ i ];
+ if (C <> Ctl) and (@ C.PP.fControlClick = @ ClickGraphRadio) then
+ C.Checked := FALSE;
+ end;
+end;////////////////////////////////////////////////////////////////////////////
+function NewGraphRadioBox( AParent: PControl; const ACaption: KOLString ): PControl;
+begin {$IFDEF INPACKAGE} Result := NewRadiobox( AParent, ACaption );
+ if (@ ClickGraphRadio) <> nil then;
+ {$ELSE} Result := NewGraphButton( AParent, ACaption );
+ Result.TextAlign := taLeft;
+ Result.aAutoSzX := GetSystemMetrics( SM_CXMENUCHECK ) + 4;
+ Result.EV.fPaintProc := Result.GraphicRadioBoxPaint;
+ Result.PP.fControlClick := @ ClickGraphRadio;
+ {$IFDEF USE_FLAGS}
+ if not(G1_HasRadio in AParent.fFlagsG1) then
+ begin
+ include( AParent.fFlagsG1, G1_HasRadio );
+ Result.SetRadioChecked;
+ end;
+ {$ELSE}
+ AParent.PropInt[ @RADIO_LAST ] := Result.fMenu;
+ if AParent.PropInt[ @RADIO_1ST ] = 0 then
+ begin
+ AParent.PropInt[ @RADIO_1ST ] := Result.fMenu;
+ Result.SetRadioChecked;
+ end;
+ {$ENDIF}
+ {$ENDIF}
+end;////////////////////////////////////////////////////////////////////////////
+procedure GraphButtonSetFocus(Ctl: PControl);
+var PF, CC: PControl;
+ W: HWnd;
+begin if {$IFDEF USE_FLAGS} not(F2_Tabstop in Ctl.fStyle.f2_Style)
+ {$ELSE} not Ctl.fTabStop {$ENDIF} then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ PF := Ctl.ParentForm;
+ if (PF.DF.fCurrentControl <> nil) and (PF.DF.fCurrentControl <> Ctl) and
+ (PF.DF.fCurrentControl <> Ctl.fParent) then
+ begin CC := PF.DF.fCurrentControl;
+ CC.RefInc;
+ Ctl.fParent.Focused := TRUE;
+ if Assigned( CC.EV.fLeave ) then
+ CC.EV.fLeave( PF.DF.fCurrentControl )
+ else
+ Windows.SetFocus( 0 );
+ CC.RefDec;
+ end else
+ begin W := GetFocus;
+ if (W <> Ctl.Parent.fHandle) and (W <> 0) then
+ begin Windows.SetFocus( 0 );
+ Ctl.fParent.Focused := TRUE;
+ end;
+ end;
+ if Ctl.fParent.fHandle <> 0 then
+ begin {$IFDEF USE_FLAGS} include( Ctl.fFlagsG6, G6_Focused );
+ {$ELSE} Ctl.fFocused := TRUE; {$ENDIF}
+ Ctl.fParent.Postmsg( CM_FOCUSGRAPHCTL, Integer( Ctl ), 0 );
+ Ctl.RefInc;
+ end;
+ if Assigned( Ctl.EV.fOnEnter ) then
+ Ctl.EV.fOnEnter( Ctl );
+end;////////////////////////////////////////////////////////////////////////////
+function NewGraphButton( AParent: PControl; const ACaption: KOLString ): PControl;
+begin {$IFDEF INPACKAGE}
+ Result := NewButton( AParent, ACaption );
+ {$ELSE}
+ Result := _NewGraphCtl( AParent, TRUE,
+ {$IFDEF PACK_COMMANDACTIONS} ButtonActions_Packed
+ {$ELSE} @ButtonActions {$ENDIF} );
+ Result.EV.fPaintProc := Result.GraphicButtonPaint;
+ Result.Caption := ACaption;
+ Result.TextAlign := taCenter;
+ Result.VerticalAlign := vaCenter;
+ Result.EV.fGraphCtlMouseEvent := Result.GraphicButtonMouse;
+ Result.fSetFocus := @GraphButtonSetFocus;
+ Result.fKeyboardProcess := Result.GraphButtonKeyboardProcess;
+ {$ENDIF}
+end;////////////////////////////////////////////////////////////////////////////
+function EditGraphEdit(Ctl: PControl): PControl;
+var E: PControl;
+begin E := NewEditBox( Ctl.fParent, Ctl.DF.fEditOptions );
+ E.SetBoundsRect( Ctl.BoundsRect );
+ E.SetAlign( Ctl.Align );
+ E.fTabOrder := Ctl.fTabOrder;
+ E.Text := Ctl.Text;
+ E.OnChange := Ctl.ChangeGraphEdit;
+ E.Color := Ctl.Color;
+ E.fCursor := Ctl.fCursor;
+ E.CreateWindow;
+ E.OnLeave := Ctl.LeaveGraphEdit;
+ E.EV.fLeave := Ctl.LeaveGraphEdit;
+ E.Focused := TRUE;
+ E.OnChar := Ctl.OnChar;
+ E.OnKeyDown := Ctl.OnKeyDown;
+ E.OnKeyUp := Ctl.OnKeyUp;
+ E.OnDestroy := Ctl.DestroyGraphEdit;
+ //E.Font.Assign( Font );
+ Result := E;
+ Ctl.Visible := FALSE;
+ Ctl.DF.fEditCtl := E;
+ {$IFDEF NIL_EVENTS}
+ if Assigned( Ctl.EV.fOnEnter ) then
+ {$ENDIF}
+ Ctl.EV.fOnEnter( Ctl );
+end;////////////////////////////////////////////////////////////////////////////
+function NewGraphEditbox( AParent: PControl; Options: TEditOptions ): PControl;
+begin {$IFDEF INPACKAGE}
+ Result := NewEditbox( AParent, Options );
+ {$ELSE}
+ Result := _NewGraphCtl( AParent, TRUE,
+ {$IFDEF PACK_COMMANDACTIONS} EditActions_Packed
+ {$ELSE} @EditActions {$ENDIF} );
+ Result.aAutoSzY := 1;
+ Result.EV.fPaintProc := Result.GraphicEditPaint;
+ Result.DF.fEditOptions := Options;
+ Result.VerticalAlign := vaCenter;
+ Result.fColor := clWindow;
+ Result.EV.fGraphCtlMouseEvent := Result.GraphicEditMouse;
+ Result.fSetFocus := @EditGraphEdit;
+ Result.fLookTabKeys := [ tkTab, tkUpDown, tkPageUpPageDn ];
+ Result.EV.fLeave := Result.LeaveGraphEdit;
+ {$ENDIF}
+end;////////////////////////////////////////////////////////////////////////////
+{ TGraphicControl }
+
+function TControl.DoGraphCtlPrepaint: TRect;
+begin
+ Result := ClientRect;
+ if not Assigned( TMethod( EV.fOnPrepaint ).Data ) and not Transparent then
+ begin if fBrush <> nil then
+ Canvas.Brush.Assign( fBrush )
+ else Canvas.Brush.Color := Color;
+ Canvas.FillRect( Result );
+ end;
+end;////////////////////////////////////////////////////////////////////////////
+procedure TControl.GraphicLabelPaint(DC: HDC);
+var R: TRect;
+begin R := DoGraphCtlPrepaint;
+ if Text <> '' then DrawFormattedText( @ Self, DC, R, 0 );
+end;////////////////////////////////////////////////////////////////////////////
+procedure TControl.GraphicCheckBoxPaint(DC: HDC);
+var R, R1: TRect;
+ Flag: DWORD;
+ W, H: Integer;
+ {$IFDEF GRAPHCTL_XPSTYLES}
+ Theme: THandle;
+ {$ENDIF}
+begin R := DoGraphCtlPrepaint;
+ {$IFDEF GRAPHCTL_XPSTYLES}
+ OpenThemeDataProc;
+ Theme := 0;
+ if Assigned( fOpenThemeDataProc ) and not DoNotDrawGraphCtlsUsingXPStyles then
+ Theme := fOpenThemeDataProc( 0, 'Button' );
+ if Theme <> 0 then begin
+ W := GetSystemMetrics( SM_CXMENUCHECK );
+ H := GetSystemMetrics( SM_CYMENUCHECK );
+ R1 := R;
+ R1.Right := R1.Left + W;
+ if {$IFDEF USE_FLAGS} G1_WordWrap in fFlagsG1
+ {$ELSE} fWordWrap {$ENDIF} then
+ R1.Top := R1.Top + Border
+ else R1.Top := R1.Top + (R1.Bottom - R1.Top - H) div 2;
+ R1.Bottom := R1.Top + H;
+ Flag := 1; {CBS_UNCHECKEDNORMAL}
+ if not Enabled then
+ Flag := 4 {CBS_UNCHECKEDDISABLED}
+ else if {$IFDEF USE_FLAGS} G4_Hot in fFlagsG4
+ {$ELSE} fHot {$ENDIF} then
+ Flag := 2; {CBS_UNCHECKEDHOT}
+ if {$IFDEF USE_FLAGS} G4_Checked in fFlagsG4
+ {$ELSE} fChecked {$ENDIF} then
+ Inc( Flag, 4 );
+ fDrawThemeBackground( Theme, DC, 3 {BP_CHECKBOX}, Flag, @R1, @R );
+ R.Left := R1.Left + W + Border;
+ if fCaption <> '' then begin
+ DrawFormattedText( @ Self, DC, R, DT_CALCRECT );
+ if {$IFDEF USE_FLAGS} G1_WordWrap in fFlagsG1
+ {$ELSE} fWordWrap {$ENDIF} then begin
+ DrawFormattedText( @ Self, DC, R, 0 );
+ GraphCtlDrawFocusRect( DC, R );
+ end else begin
+ GraphCtlDrawFocusRect( DC, R );
+ DrawFormattedTextXP( Theme, @ Self, DC, R, 3 {BP_CHECKBOX}, Flag, 0, 0 );
+ end;
+ end;
+ fCloseThemeData( Theme );
+ end else
+ {$ENDIF}
+ begin W := GetSystemMetrics( SM_CXMENUCHECK );
+ H := GetSystemMetrics( SM_CYMENUCHECK );
+ R1 := R;
+ R1.Right := R1.Left + W;
+ if {$IFDEF USE_FLAGS} G1_WordWrap in fFlagsG1
+ {$ELSE} fWordWrap {$ENDIF} then
+ R1.Top := R1.Top + Border
+ else R1.Top := R1.Top + (R1.Bottom - R1.Top - H) div 2;
+ R1.Bottom := R1.Top + H;
+ Flag := 0;
+ if {$IFDEF USE_FLAGS} G4_Checked in fFlagsG4
+ {$ELSE} fChecked {$ENDIF} then
+ Flag := DFCS_CHECKED;
+ DrawFrameControl( DC, R1, DFC_BUTTON, DFCS_BUTTONCHECK or
+ $800 {DFCS_TRANSPARENT} or Flag );
+ R.Left := R1.Left + W + Border;
+ DrawFormattedText( @ Self, DC, R, 0 );
+ GraphCtlDrawFocusRect( DC, R );
+ end;
+end;////////////////////////////////////////////////////////////////////////////
+procedure TControl.GraphicCheckBoxMouse(var Msg: TMsg);
+begin if (Msg.message = WM_LBUTTONDOWN) or (Msg.message = WM_LBUTTONDBLCLK) then
+ ClickGraphCheck( @ Self );
+end;////////////////////////////////////////////////////////////////////////////
+procedure TControl.GraphicRadioBoxPaint(DC: HDC);
+var R, R1: TRect;
+ Flag: DWORD;
+ W, H: Integer;
+ {$IFDEF GRAPHCTL_XPSTYLES}
+ Theme: THandle;
+ {$ENDIF}
+begin R := DoGraphCtlPrepaint;
+ {$IFDEF GRAPHCTL_XPSTYLES}
+ OpenThemeDataProc;
+ Theme := 0;
+ if Assigned( fOpenThemeDataProc ) and not DoNotDrawGraphCtlsUsingXPStyles then
+ Theme := fOpenThemeDataProc( 0, 'Button' );
+ if Theme <> 0 then begin
+ W := GetSystemMetrics( SM_CXMENUCHECK );
+ H := GetSystemMetrics( SM_CYMENUCHECK );
+ R1 := R;
+ R1.Right := R1.Left + W;
+ if {$IFDEF USE_FLAGS} G1_WordWrap in fFlagsG1
+ {$ELSE} fWordWrap {$ENDIF} then
+ R1.Top := R1.Top + Border
+ else R1.Top := R1.Top + (R1.Bottom - R1.Top - H) div 2;
+ R1.Bottom := R1.Top + H;
+ Flag := 1; {CBS_UNCHECKEDNORMAL}
+ if not Enabled then
+ Flag := 4 {CBS_UNCHECKEDDISABLED}
+ else if {$IFDEF USE_FLAGS} G4_Hot in fFlagsG4
+ {$ELSE} fHot {$ENDIF} then
+ Flag := 2; {CBS_UNCHECKEDHOT}
+ if {$IFDEF USE_FLAGS} G4_Checked in fFlagsG4
+ {$ELSE} fChecked {$ENDIF} then
+ Inc( Flag, 4 );
+ fDrawThemeBackground( Theme, DC, 2 {BP_RADIOBOX}, Flag, @R1, @R );
+ R.Left := R1.Left + W + Border;
+ if fCaption <> '' then begin
+ DrawFormattedText( @ Self, DC, R, DT_CALCRECT );
+ if {$IFDEF USE_FLAGS} G1_WordWrap in fFlagsG1
+ {$ELSE} fWordWrap {$ENDIF} then begin
+ DrawFormattedText( @ Self, DC, R, 0 );
+ GraphCtlDrawFocusRect( DC, R );
+ end else begin
+ GraphCtlDrawFocusRect( DC, R );
+ DrawFormattedTextXP( Theme, @ Self, DC, R, 2 {BP_RADIOBOX}, Flag, 0, 0 );
+ end;
+ end;
+ fCloseThemeData( Theme );
+ end else
+ {$ENDIF}
+ begin W := GetSystemMetrics( SM_CXMENUCHECK );
+ H := GetSystemMetrics( SM_CYMENUCHECK );
+ R1 := R;
+ R1.Right := R1.Left + W;
+ if {$IFDEF USE_FLAGS} G1_WordWrap in fFlagsG1
+ {$ELSE} fWordWrap {$ENDIF} then
+ R1.Top := R1.Top + Border
+ else R1.Top := R1.Top + (R1.Bottom - R1.Top - H) div 2;
+ R1.Bottom := R1.Top + H;
+ Flag := 0;
+ if {$IFDEF USE_FLAGS} G4_Checked in fFlagsG4
+ {$ELSE} fChecked {$ENDIF} then
+ Flag := DFCS_CHECKED;
+ DrawFrameControl( DC, R1, DFC_BUTTON, DFCS_BUTTONRADIO
+ or $800 {DFCS_TRANSPARENT} {or DFCS_ADJUSTRECT} or Flag );
+ R.Left := R1.Right + 2;
+ DrawFormattedText( @ Self, DC, R, 0 );
+ GraphCtlDrawFocusRect( DC, R );
+ end;
+end;////////////////////////////////////////////////////////////////////////////
+procedure TControl.GraphicButtonPaint(DC: HDC);
+var R: TRect;
+ Flag: DWORD;
+ {$IFDEF GRAPHCTL_XPSTYLES}
+ Flag1: DWORD;
+ Theme: THandle;
+ {$ENDIF}
+ II: TIconInfo;
+ BI: TagBitmap;
+ Y: Integer;
+ R1: TRect;
+begin R := DoGraphCtlPrepaint;
+ {$IFDEF GRAPHCTL_XPSTYLES}
+ OpenThemeDataProc;
+ Theme := 0;
+ if Assigned( fOpenThemeDataProc ) and not DoNotDrawGraphCtlsUsingXPStyles then
+ Theme := fOpenThemeDataProc( 0, 'Button' );
+ if Theme <> 0 then begin
+ Flag := 1; {PBS_UNCHECKEDNORMAL}
+ if not Enabled then
+ Flag := 4 {PBS_UNCHECKEDDISABLED}
+ else
+ if {$IFDEF USE_FLAGS} G4_Pushed in fFlagsG4
+ {$ELSE} fPushed {$ENDIF} then
+ Flag := 3 {PBS_UNCHECKEDPRESSED}
+ else
+ if {$IFDEF USE_FLAGS} G4_Hot in fFlagsG4
+ {$ELSE} fHot {$ENDIF} then
+ Flag := 2; {PBS_UNCHECKEDHOT}
+ if {$IFDEF USE_FLAGS} G4_Checked in fFlagsG4
+ {$ELSE} fChecked {$ENDIF} then
+ Inc( Flag, 4 );
+ fDrawThemeBackground( Theme, DC, 1 {BP_PUSHBUTTON}, Flag, @R, @R );
+ fGetThemeBackgroundContentRect( Theme, DC, 1 {BS_PUSHBUTTON}, Flag, @R, @R1 );
+ GraphCtlDrawFocusRect( DC, R1 );
+ if (DF.fButtonIcon <> 0) and GetIconInfo( DF.fButtonIcon, II ) then begin
+ if GetObject( II.hbmColor, Sizeof( BI ), @ BI ) <> 0 then begin
+ CASE fVerticalAlign OF
+ vaTop: Y := R.Top + Border;
+ vaBottom: Y := R.Bottom - Border - BI.bmHeight;
+ else {vaCenter:}Y := R.Top + (R.Bottom - R.Top - BI.bmHeight) div 2;
+ END;
+ DrawIcon( DC, R.Left + Border, Y, DF.fButtonIcon );
+ Inc( R1.Left, BI.bmWidth + Border * 2 );
+ end;
+ DeleteObject( II.hbmColor );
+ if II.hbmMask <> 0 then
+ DeleteObject( II.hbmMask );
+ end;
+ if fCaption <> '' then begin
+ Flag1 := DT_SINGLELINE;
+ if WordWrap then Flag1 := DT_WORDBREAK;
+ DrawFormattedText( @ Self, DC, R1, DT_CALCRECT );
+ DrawFormattedTextXP( Theme, @ Self, DC, R1, 1 {BP_PUSHBUTTON}, Flag,
+ Flag1, 0 );
+ end;
+ fCloseThemeData( Theme );
+ end else
+ {$ENDIF}
+ begin
+ Flag := 0;
+ if {$IFDEF USE_FLAGS} G4_Checked in fFlagsG4
+ {$ELSE} fChecked {$ENDIF} then Flag := DFCS_CHECKED
+ else if {$IFDEF USE_FLAGS} G4_Pushed in fFlagsG4
+ {$ELSE} fPushed {$ENDIF} then
+ Flag := DFCS_PUSHED;
+ if {$IFDEF USE_FLAGS} G3_Flat in fFlagsG3
+ {$ELSE} fFlat {$ENDIF} then
+ Flag := Flag or DFCS_FLAT;
+ DrawFrameControl( DC, R, DFC_BUTTON, DFCS_BUTTONPUSH or
+ $800 {DFCS_TRANSPARENT} or DFCS_ADJUSTRECT or Flag );
+ R1 := R;
+ if (DF.fButtonIcon <> 0) and GetIconInfo( DF.fButtonIcon, II ) then begin
+ if GetObject( II.hbmColor, Sizeof( BI ), @ BI ) <> 0 then begin
+ CASE fVerticalAlign OF
+ vaTop: Y := R.Top + Border;
+ vaBottom: Y := R.Bottom - Border - BI.bmHeight;
+ else {vaCenter:}Y := R.Top + (R.Bottom - R.Top - BI.bmHeight) div 2;
+ END;
+ DrawIcon( DC, R.Left + Border, Y, DF.fButtonIcon );
+ Inc( R1.Left, BI.bmWidth + Border * 2 );
+ end;
+ DeleteObject( II.hbmColor );
+ if II.hbmMask <> 0 then DeleteObject( II.hbmMask );
+ end;
+ DrawFormattedText( @ Self, DC, R1, 0 );
+ GraphCtlDrawFocusRect( DC, R );
+ end;
+end;////////////////////////////////////////////////////////////////////////////
+procedure TControl.GraphicButtonMouse(var Msg: TMsg);
+var Pt: TPoint;
+begin CASE Msg.message OF
+ WM_LBUTTONDOWN, WM_LBUTTONDBLCLK:
+ begin GraphButtonSetFocus(@Self);
+ RefInc;
+ SetCapture( Parent.Handle );
+ Parent.fPushedBtn := @ Self;
+ {$IFDEF USE_FLAGS} include( fFlagsG4, G4_Pushed );
+ {$ELSE} fPushed := TRUE; {$ENDIF}
+ Invalidate;
+ end;
+ WM_LBUTTONUP:
+ begin
+ ReleaseCapture;
+ Invalidate;
+ if {$IFDEF USE_FLAGS} G4_Pushed in fFlagsG4
+ {$ELSE} fPushed {$ENDIF} then begin
+ Pt.X := SmallInt( LoWord( Msg.lParam ) );
+ Pt.Y := SmallInt( HiWord( Msg.lParam ) );
+ if PtInRect( ClientRect, Pt ) then DoClick;
+ {$IFDEF USE_FLAGS} exclude( fFlagsG4, G4_Pushed );
+ {$ELSE} fPushed := FALSE; {$ENDIF}
+ Parent.fPushedBtn := nil;
+ RefDec;
+ end;
+ end;
+ END;
+end;////////////////////////////////////////////////////////////////////////////
+procedure TControl.LeaveGraphButton( Sender: PObj );
+begin {$IFDEF USE_FLAGS} exclude( fFlagsG6, G6_Focused );
+ {$ELSE} fFocused := FALSE; {$ENDIF}
+ if Parent.DF.fCurrentControl = @ Self then
+ Parent.DF.fCurrentControl := nil;
+ if ParentForm.DF.fCurrentControl = @ Self then
+ ParentForm.DF.fCurrentControl := nil;
+ Invalidate;
+ {$IFDEF NIL_EVENTS}
+ if Assigned( EV.fOnLeave ) then
+ {$ENDIF}
+ EV.fOnLeave( @ Self );
+end;////////////////////////////////////////////////////////////////////////////
+function TControl.GraphButtonKeyboardProcess(var Msg: TMsg;
+ var Rslt: Integer): Boolean;
+var SpacePressed: Boolean;
+begin Result := FALSE;
+ SpacePressed := Msg.wParam = Word( ' ' );
+ {$IFDEF ALL_BUTTONS_RESPOND_TO_ENTER}
+ SpacePressed := SpacePressed or (Msg.wParam = 13);
+ {$ENDIF}
+ if not SpacePressed then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ if (Msg.message = WM_KEYDOWN) or (Msg.message = WM_SYSKEYDOWN) then
+ begin
+ Parent.fPushedBtn := @ Self;
+ {$IFDEF USE_FLAGS} include( fFlagsG4, G4_Pushed );
+ {$ELSE} fPushed := TRUE; {$ENDIF}
+ Invalidate;
+ Result := TRUE; /////
+ end else
+ if (Msg.message = WM_KEYUP) or (Msg.message = WM_SYSKEYUP) then
+ begin
+ {$IFDEF USE_FLAGS} exclude( fFlagsG4, G4_Pushed );
+ {$ELSE} fPushed := FALSE; {$ENDIF}
+ Parent.fPushedBtn := nil;
+ Invalidate;
+ Result := TRUE; /////
+ end else
+ if (Msg.message = WM_CHAR) or (Msg.message = WM_SYSCHAR) then
+ begin
+ DoClick;
+ Result := TRUE;
+ end;
+end;////////////////////////////////////////////////////////////////////////////
+procedure TControl.GraphicEditPaint(DC: HDC);
+var R: TRect;
+ {$IFDEF GRAPHCTL_XPSTYLES}
+ R1: TRect;
+ Flag, Flag1: DWORD;
+ Theme: THandle;
+ {$ENDIF}
+begin R := ClientRect;
+ {$IFDEF GRAPHCTL_XPSTYLES}
+ OpenThemeDataProc;
+ Theme := 0;
+ if Assigned( fOpenThemeDataProc ) and not DoNotDrawGraphCtlsUsingXPStyles then
+ Theme := fOpenThemeDataProc( 0, 'Edit' );
+ if Theme <> 0 then
+ begin
+ Flag := 1; {ETS_NORMAL}
+ if not Enabled then
+ Flag := 4 {ETS_DISABLED}
+ else if eoReadonly in DF.fEditOptions then
+ Flag := 6 {ETS_READONLY}
+ else if {$IFDEF USE_FLAGS} G6_Focused in fFlagsG6
+ {$ELSE} fFocused {$ENDIF} then
+ Flag := 5 {ETS_FOCUSED}
+ else if {$IFDEF USE_FLAGS} G4_Hot in fFlagsG4
+ {$ELSE} fHot {$ENDIF} then
+ Flag := 2; {ETS_HOT}
+ fDrawThemeBackground( Theme, DC, 1 {EP_EDITTEXT}, Flag, @R, @R );
+ Inc( R.Left, 2 );
+ Dec( R.Right, 2 );
+ fGetThemeBackgroundContentRect( Theme, DC, 1 {EP_EDITTEXT}, Flag, @R, @R1 );
+ if fCaption <> '' then
+ begin
+ Flag1 := DT_SINGLELINE;
+ if eoMultiline in DF.fEditOptions then
+ Flag1 := DT_WORDBREAK;
+ CASE fTextAlign OF
+ taCenter: Flag1 := Flag1 or DT_CENTER;
+ taRight: Flag1 := Flag1 or DT_RIGHT;
+ END;
+ CASE fVerticalAlign OF
+ vaCenter: Flag1 := Flag1 or DT_VCENTER;
+ vaBottom: Flag1 := Flag1 or DT_BOTTOM;
+ END;
+ DrawFormattedTextXP( Theme, @ Self, DC, R1, 1 {EP_EDITTEXT}, Flag,
+ Flag1, 0 );
+ end;
+ fCloseThemeData( Theme );
+ end else
+ {$ENDIF}
+ begin
+ if not Assigned( EV.fOnPrepaint ) and not Transparent then begin
+ Canvas.Brush.Color := fColor;
+ Canvas.FillRect( R );
+ end;
+ DrawEdge( DC, R, BDR_SUNKENINNER or BDR_SUNKENOUTER, BF_ADJUST or BF_RECT );
+ DrawFormattedText( @ Self, DC, R, DT_EDITCONTROL );
+ end;
+end;////////////////////////////////////////////////////////////////////////////
+procedure TControl.GraphicEditMouse(var Msg: TMsg);
+var E: PControl;
+ Pt: TPoint;
+begin CASE Msg.message OF
+ WM_LBUTTONDOWN, WM_LBUTTONDBLCLK:
+ if not ( eoReadOnly in DF.fEditOptions ) then begin
+ E := EditGraphEdit(@Self);
+ Pt.X := Smallint( LoWord( Msg.lParam ) ) - Left;
+ Pt.Y := Smallint( HiWord( Msg.lParam ) ) - Top;
+ PostMessage( E.Handle, Msg.message, Msg.wParam,
+ Pt.Y shl 16 or Pt.X and $FFFF );
+ end;
+ END;
+end;////////////////////////////////////////////////////////////////////////////
+procedure TControl.LeaveGraphEdit(Sender: PObj);
+begin if {$IFDEF USE_FLAGS} not(G6_GraphicCtl in PControl(Sender).fFlagsG6)
+ {$ELSE} PControl( Sender ).fWindowed {$ENDIF}
+ and ( DF.fEditCtl <> nil ) then begin
+ Text := PControl( Sender ).Text;
+ DF.fEditCtl := nil;
+ Visible := TRUE;
+ ParentForm.DF.fCurrentControl := @ Self;
+ Parent.DF.fCurrentControl := @ Self;
+ Parent.Postmsg( CM_QUIT, DWORD( Sender ), 0 );
+ end else
+ if Assigned( DF.fEditCtl ) then
+ DF.fEditCtl.EV.fLeave( DF.fEditCtl );
+end;////////////////////////////////////////////////////////////////////////////
+procedure TControl.ChangeGraphEdit(Sender: PObj);
+begin Text := PControl( Sender ).Text;
+end;////////////////////////////////////////////////////////////////////////////
+procedure TControl.DestroyGraphEdit(Sender: PObj);
+begin
+ DF.fEditCtl := nil;
+end;////////////////////////////////////////////////////////////////////////////
+procedure TControl.GraphCtlDrawFocusRect(DC: HDC; const R: TRect);
+var rgn: HRgn;
+begin if {$IFDEF USE_FLAGS} (G6_Focused in fFlagsG6)
+ {$ELSE} fFocused {$ENDIF}
+ and (GetActiveWindow = ParentForm.Handle) then begin
+ BeginPath( DC );
+ Canvas.FrameRect( R );
+ EndPath( DC );
+ Canvas.FrameRect( R );
+ DrawFocusRect( DC, R );
+ rgn := PathToRegion( DC );
+ ExtSelectClipRgn( DC, rgn, RGN_DIFF );
+ DeleteObject( rgn );
+ end;
+end;////////////////////////////////////////////////////////////////////////////
+procedure TControl.GroupBoxPaint(DC: HDC);
+var bk_erased: Boolean;
+ procedure DoEraseBkgnd;
+ var R: TRect;
+ begin
+ bk_erased := TRUE;
+ If Assigned( EV.fOnEraseBkgnd ) then
+ EV.fOnEraseBkgnd( @ Self, DC )
+ else begin
+ R := BoundsRect;
+ OffsetRect( R, -R.Left, -R.Top );
+ SetBkMode( DC, OPAQUE );
+ SetBkColor( DC, Color2RGB( fColor ) );
+ SetBrushOrgEx( DC, 0, 0, nil );
+ Windows.FillRect( DC, R, Global_GetCtlBrushHandle( @ Self ) );
+ End;
+ end;
+var R, R1, R0: TRect;
+ rgn, rgn2, rgntxt, rgnsav, rgnsavall: HRgn;
+ i: Integer;
+ C: PControl;
+ {$IFDEF GRAPHCTL_XPSTYLES}
+ Theme: THandle;
+ Flag: DWORD;
+ {$ENDIF}
+begin if not DF.fErasingBkgnd then Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ R := ClientRect;
+ Dec( R.Top, 14 { Self_.fClientTop div 2 } );
+ Dec( R.Left, fClientLeft );
+ Inc( R.Right, fClientRight );
+ Inc( R.Bottom, fClientBottom );
+ rgnsavall := CreateRectRgn( 0, 0, 0, 0 );
+ GetClipRgn( DC, rgnsavall );
+ TRY
+ For i := 0 to ChildCount-1 do begin
+ C := Children[ i ];
+ If {$IFDEF USE_FLAGS} (G6_GraphicCtl in C.fFlagsG6)
+ {$ELSE} not C.fWindowed {$ENDIF}
+ and {$IFDEF USE_FLAGS} (F3_Visible in C.fStyle.f3_Style)
+ {$ELSE} C.fVisible {$ENDIF} then begin
+ rgn := CreateRectRgnIndirect( C.BoundsRect );
+ ExtSelectClipRgn( DC, rgn, RGN_DIFF );
+ DeleteObject( rgn );
+ End;
+ End;
+ {$IFDEF GRAPHCTL_XPSTYLES}
+ OpenThemeDataProc;
+ Theme := 0;
+ if Assigned( fOpenThemeDataProc ) and not DoNotDrawGraphCtlsUsingXPStyles then
+ Theme := fOpenThemeDataProc( 0, 'Button' );
+ If Theme <> 0 then begin
+ DoEraseBkgnd;
+ Flag := 1; {GBS_NORMAL}
+ if not Enabled then Flag := 2; {GBS_DISABLED}
+ R1 := R;
+ rgnsav := 0;
+ if fCaption <> '' then
+ begin
+ R1.Top := 0;
+ Inc( R1.Left, 8 );
+ Dec( R1.Right, 8 );
+ BeginPath( DC );
+ DrawFormattedTextXP( Theme, @ Self, DC, R1, 4 {BP_GROUPBOX}, Flag, 0, 0 );
+ EndPath( DC );
+ rgntxt := PathToRegion( DC );
+ if rgntxt = 0 then begin
+ R1.Right := R1.Left + Canvas.TextWidth( fCaption );
+ R1.Bottom := R1.Top + Canvas.TextHeight( fCaption );
+ rgntxt := CreateRectRgnIndirect( R1 );
+ end;
+ DrawFormattedTextXP( Theme, @ Self, DC, R1, 4 {BP_GROUPBOX}, Flag, 0, 0 );
+ GetRgnBox( rgntxt, R0 );
+ Dec( R0.Left, 3 );
+ Inc( R0.Right, 3 );
+ DeleteObject( rgntxt );
+ rgn := CreateRectRgnIndirect( R0 );
+ end else rgn := 0;
+ if rgn <> 0 then
+ begin
+ rgnsav := CreateRectRgn( 0, 0, 0, 0 );
+ GetClipRgn( DC, rgnsav );
+ ExtSelectClipRgn( DC, rgn, RGN_DIFF );
+ DeleteObject( rgn );
+ end;
+ fDrawThemeBackground( Theme, DC, 4 {BP_GROUPBOX}, Flag, @R, @R );
+ if rgnsav <> 0 then begin
+ SelectClipRgn( DC, rgnsav );
+ DeleteObject( rgnsav );
+ end;
+ fCloseThemeData( Theme );
+ end else
+ {$ENDIF}
+ begin
+ bk_erased := FALSE;
+ R1 := R;
+ R1.Top := 0;
+ R1.Bottom := ClientRect.Top;
+ Inc( R1.Left, 16 );
+ Dec( R1.Right, 16 );
+ fVerticalAlign := vaCenter;
+ BeginPath( DC );
+ Canvas.TextOut( R1.Left, R1.Top, fCaption );
+ EndPath( DC );
+ Canvas.TextOut( R1.Left, R1.Top, fCaption );
+ rgntxt := PathToRegion( DC );
+ if rgntxt = 0 then // òàêîå - â ñëó÷àå øðèôòà ïî óìîë÷àíèþ!
+ begin
+ R1.Right := R1.Left + Canvas.TextWidth( fCaption );
+ R1.Bottom := R1.Top + Canvas.TextHeight( fCaption );
+ rgntxt := CreateRectRgnIndirect( R1 );
+ end;
+
+ GetRgnBox( rgntxt, R0 );
+ rgn2 := CreateRectRgnIndirect( R0 );
+
+ rgnsav := CreateRectRgn( 0, 0, 0, 0 );
+ GetClipRgn( DC, rgnsav );
+ ExtSelectClipRgn( DC, rgn2, RGN_DIFF );
+ DeleteObject( rgn2 );
+
+ BeginPath( DC );
+ DrawEdge( DC, R, BDR_RAISEDINNER or BDR_SUNKENOUTER, BF_RECT );
+ EndPath( DC );
+ rgn := PathToRegion( DC );
+ if rgn = 0 then DoEraseBkgnd;
+ DrawEdge( DC, R, BDR_RAISEDINNER or BDR_SUNKENOUTER, BF_RECT );
+
+ SelectClipRgn( DC, rgnsav );
+ DeleteObject( rgnsav );
+
+ if rgn <> 0 then
+ begin
+ ExtSelectClipRgn( DC, rgn, RGN_DIFF );
+ DeleteObject( rgn );
+ end;
+ ExtSelectClipRgn( DC, rgntxt, RGN_DIFF );
+ DeleteObject( rgntxt );
+
+ if not bk_erased then DoEraseBkgnd;
+ End;
+ FINALLY
+ SelectClipRgn( DC, rgnsavall );
+ DeleteObject( rgnsavall );
+ END;
+end;
+{$ENDIF USE_GRAPHCTLS}//--------------------------------------------------------
+
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+function TControl.MakeWordWrap: PControl;
+begin {$IFDEF USE_FLAGS} include( fFlagsG1, G1_WordWrap );
+ {$ELSE} fWordWrap := TRUE; {$ENDIF}
+ if IsButton then
+ Style := fStyle.Value or BS_MULTILINE
+ else
+ Style := fStyle.Value and not SS_LEFTNOWORDWRAP;
+ Result := @ Self;
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+function ParentAnchorChildren( Sender: PControl; var Msg: TMsg;
+ var Rslt: Integer ): Boolean;
+var NewW, NewH: Integer;
+ dW, dH: Integer;
+ i: Integer;
+ C: PControl;
+ {$IFNDEF ANCHORS_WM_SIZE}
+ CR: TRect;
+ {$ENDIF}
+begin Result := FALSE;
+ If (Msg.message = {$IFDEF ANCHORS_WM_SIZE} WM_SIZE {$ELSE} WM_WINDOWPOSCHANGED {$ENDIF} )
+ and not IsIconic(Sender.Handle) then begin
+ {$IFDEF ANCHORS_WM_SIZE}
+ NewW := LoWord( Msg.lParam );
+ NewH := HiWord( Msg.lParam );
+ {$ELSE}
+ CR := Sender.ClientRect;
+ NewW := CR.Right;
+ NewH := CR.Bottom;
+ {$ENDIF}
+ dW := NewW - Sender.fOldWidth;
+ dH := NewH - Sender.fOldHeight;
+ For i := 0 to Sender.ChildCount - 1 do begin
+ C := Sender.Children[ i ];
+ If dW <> 0 then begin
+ if C.AnchorRight and C.AnchorLeft then
+ C.Width := C.Width + dW
+ else if C.AnchorRight then
+ C.Left := C.Left + dW;
+ End;
+ If dH <> 0 then begin
+ if C.AnchorBottom and C.AnchorTop then
+ C.Height := C.Height + dH
+ else if C.AnchorBottom then
+ C.Top := C.Top + dH;
+ End;
+ End;
+ Sender.fOldWidth := NewW;
+ Sender.fOldHeight := NewH;
+ End;
+end;////////////////////////////////////////////////////////////////////////////
+function TControl.Anchor(aLeft, aTop, aRight, aBottom: Boolean): PControl;
+begin if (not aLeft) and aRight then
+ AnchorLeft := FALSE
+ else AnchorLeft := aLeft;
+ if (not aTop) and aBottom then
+ AnchorTop := FALSE
+ else AnchorTop := aTop;
+ AnchorRight := aRight;
+ AnchorBottom := aBottom;
+ Result := @ Self;
+end;
+function TControl.GetLBTopIndex: Integer;
+begin Result := Perform(LB_GETTOPINDEX,0,0);
+end;////////////////////////////////////////////////////////////////////////////
+function TControl.LBItemAtPos(X, Y: Integer): Integer;
+var R: TRect;
+ P: TPoint;
+ i: Integer;
+begin P := MakePoint(X,Y);
+ For i := LBTopIndex to Count -1 do begin
+ Perform(LB_GETITEMRECT, i , Integer(@R));
+ if PointInRect(P,R) then begin
+ Result := i; Exit; {>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
+ end;
+ End;
+ Result := -1;
+end;////////////////////////////////////////////////////////////////////////////
+procedure TControl.SetLBTopIndex(const Value: Integer);
+begin Perform(LB_SETTOPINDEX,Value,0); end;/////////////////////////////////////
+{$ENDIF WIN_GDI}//--------------------------------------------------------------
+{$IFDEF ASM_VERSION}{$ELSE PAS_VERSION}
+function TControl.FormGetIntParam: Integer;
+var C: Byte;
+ Sign, Cont: Boolean;
+begin Result := 0;
+ While TRUE do begin
+ C := Byte( DF.FormParams^ );
+ inc( DF.FormParams );
+ Cont := C and 1 <> 0;
+ C := C shr 1;
+ If Cont then
+ Result := (Result shl 7) or C
+ else begin
+ Sign := C and 1 <> 0;
+ C := C shr 1;
+ Result := (Result shl 6) or C;
+ if Sign then
+ Result := -Result;
+ break;
+ End;
+ End;
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE}
+function TControl.FormGetColorParam: Integer;
+begin Result := FormGetIntParam;
+ Result := (Result shr 1) or (Result shl 31);
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure TControl.FormGetStrParam;
+var i: Integer;
+begin i := FormGetIntParam;
+ SetString( FormString, DF.FormParams, i );
+ inc( DF.FormParams, i );
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+procedure TControl.FormCreateParameters(
+ alphabet: PFormInitFuncArray;
+ params: PAnsiChar );
+begin DF.FormCurrentParent := @Self;
+ DF.FormLastCreatedChild := @Self;
+ DF.FormParams := params;
+ DF.FormAlphabet := alphabet;
+end;////////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure TControl.FormExecuteCommands(AForm: PControl; ControlPtrOffsets: PSmallIntArray);
+var N: Integer;
+ Ctrl: PPcontrol;
+begin while {FormParams <> ''} TRUE do begin
+ N := FormGetIntParam;
+ if N = 0 then
+ break;
+ if N < 0 then
+ begin
+ N := -N;
+ Ctrl := PPControl( Pointer( Integer(AForm)
+ + (ControlPtrOffsets[0] shl 2) ) );
+ ControlPtrOffsets := Pointer( Integer( ControlPtrOffsets ) + 2 );
+ Ctrl^ := DF.FormAlphabet[N-1]( @Self );
+ DF.FormLastCreatedChild := Ctrl^;
+ end else begin
+ Ctrl := @ DF.FormLastCreatedChild;
+ PFormInitFuncArray1( DF.FormAlphabet )[N-1]( Ctrl^, 1 );
+ end;
+ end;
+ FormString := '';
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}
+procedure FormPrepareStrParamCreateCtrl;
+asm PUSH EAX
+ CALL TControl.FormGetStrParam
+ POP ECX
+ MOV EAX, [ECX].TControl.DF.FormCurrentParent
+ MOV EDX, [ECX].TControl.FormString
+end;////////////////////////////////////////////////////////////////////////////
+procedure FormPrepareIntParamCreateCtrl;
+asm PUSH EAX
+ CALL TControl.FormGetIntParam
+ XCHG EDX, EAX
+ POP ECX
+ MOV EAX, [ECX].TControl.DF.FormCurrentParent
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE}
+function FormNewLabel( Form: PControl ): PControl;
+begin Form.FormGetStrParam;
+ Result := NewLabel( Form.DF.FormCurrentParent, Form.FormString );
+end;
+{$ENDIF}////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE}
+function FormNewWordWrapLabel( Form: PControl ): PControl;
+begin Form.FormGetStrParam;
+ Result := NewWordWrapLabel( Form.DF.FormCurrentParent, Form.FormString );
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE}
+function FormNewLabelEffect( Form: PControl ): PControl;
+var Shd: Integer;
+begin Form.FormGetStrParam;
+ Shd := Form.FormGetIntParam;
+ Result := NewLabelEffect( Form.DF.FormCurrentParent, Form.FormString, Shd );
+end;
+{$ENDIF}////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE}
+function FormNewButton( Form: PControl ): PControl;
+begin Form.FormGetStrParam;
+ Result := NewButton( Form.DF.FormCurrentParent, Form.FormString );
+end;
+{$ENDIF}////////////////////////////////////////////////////////////////////////
+function FormNewBitBtn( Form: PControl ): PControl;
+type PBitBtnOptions = ^TBitBtnOptions;
+var Cap: KOLString;
+ i, j, k, bmp: Integer;
+begin Form.FormGetStrParam;
+ Cap := Form.FormString;
+ i := Form.FormGetIntParam;
+ j := Form.FormGetIntParam;
+ Form.FormGetStrParam;
+ k := Form.FormGetIntParam;
+ bmp := 0;
+ if Form.FormString <> '' then
+ bmp := LoadBmp( hInstance, PKOLChar( KOLString( Form.FormString ) ), Form );
+ Result := NewBitBtn( Form.DF.FormCurrentParent, Cap,
+ PBitBtnOptions( @i )^,
+ TGlyphLayout( j ),
+ bmp, k );
+end;////////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE}
+function FormNewPanel( Form: PControl ): PControl;
+begin Result := NewPanel( Form.DF.FormCurrentParent,
+ TEdgeStyle( Form.FormGetIntParam ) );
+end;
+{$ENDIF}////////////////////////////////////////////////////////////////////////
+function FormNewGradientPanel( Form: PControl ): PControl;
+var C1, C2: TColor;
+begin C1 := Form.FormGetColorParam;
+ C2 := Form.FormGetColorParam;
+ Result := NewGradientPanel( Form.DF.FormCurrentParent, C1, C2 );
+end;////////////////////////////////////////////////////////////////////////////
+function FormNewGradientPanelEx( Form: PControl ): PControl;
+var C1, C2: TColor;
+ Style, Layout: Integer;
+begin C1 := Form.FormGetColorParam;
+ C2 := Form.FormGetColorParam;
+ Style := Form.FormGetIntParam;
+ Layout := Form.FormGetIntParam;
+ Result := NewGradientPanelEx( Form.DF.FormCurrentParent, C1, C2,
+ TGradientStyle( Style ), TGradientLayout( Layout ) );
+end;////////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+function FormNewGroupbox( Form: PControl ): PControl;
+begin Form.FormGetStrParam;
+ Result := NewGroupbox( Form.DF.FormCurrentParent,
+ Form.FormString );
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+function FormNewPaintbox( Form: PControl ): PControl;
+begin Result := NewPaintbox( Form.DF.FormCurrentParent ); end;//////////////////
+function FormNewImageShow( Form: PControl ): PControl;
+begin Result := NewImageShow( Form.DF.FormCurrentParent, nil, 0 ); end;/////////
+{$IFDEF ASM_VERSION}{$ELSE}
+function FormNewEditBox( Form: PControl ): PControl;
+type PEditOptions = ^TEditOptions;
+var i: Integer;
+begin i := Form.FormGetIntParam;
+ Result := NewEditbox( Form.DF.FormCurrentParent, PEditOptions( @ i )^ );
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF USE_RICHEDIT}///////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE}
+function FormNewRichEdit( Form: PControl ): PControl;
+type PEditOptions = ^TEditOptions;
+var i: Integer;
+begin i := Form.FormGetIntParam;
+ Result := NewRichEdit( Form.DF.FormCurrentParent,
+ PEditOptions( @ i )^ );
+end;
+{$ENDIF PAS_VERSION}
+{$ENDIF USE_RICHEDIT}///////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE}
+function FormNewComboBox( Form: PControl ): PControl;
+type PComboOptions = ^TComboOptions;
+var i: Integer;
+begin i := Form.FormGetIntParam;
+ Result := NewCombobox( Form.DF.FormCurrentParent, PComboOptions( @ i )^ );
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE}
+function FormNewCheckbox( Form: PControl ): PControl;
+begin Form.FormGetStrParam;
+ Result := NewCheckbox( Form.DF.FormCurrentParent, Form.FormString );
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE}
+function FormNewRadiobox( Form: PControl ): PControl;
+begin Form.FormGetStrParam;
+ Result := NewRadiobox( Form.DF.FormCurrentParent, Form.FormString );
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+function FormNewSplitter( Form: PControl ): PControl;
+var p, n: Integer;
+begin p := Form.FormGetIntParam;
+ n := Form.FormGetIntParam;
+ Result := NewSplitter( Form.DF.FormCurrentParent, p, n );
+end;////////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE}
+function FormNewListbox( Form: PControl ): PControl;
+type PListOptions = ^TListOptions;
+var i: Integer;
+begin i := Form.FormGetIntParam;
+ Result := NewListbox( Form.DF.FormCurrentParent, PListOptions( @ i )^ );
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+function FormNewListView( Form: PControl ): PControl;
+type PListViewOptions = ^TListViewOptions;
+var lvs: TListViewStyle;
+ i: Integer;
+begin lvs := TListViewStyle( Form.FormGetIntParam );
+ i := Form.FormGetIntParam;
+ Result := NewListView( Form.DF.FormCurrentParent,
+ lvs, PListViewOptions( @i )^,
+ nil, nil, nil );
+end;////////////////////////////////////////////////////////////////////////////
+function FormNewTreeView( Form: PControl ): PControl;
+type PTreeViewOptions = ^TTreeViewOptions;
+var i: Integer;
+begin i := Form.FormGetIntParam;
+ Result := NewTreeView( Form.DF.FormCurrentParent,
+ PTreeViewOptions( @i )^,
+ nil, nil );
+end;////////////////////////////////////////////////////////////////////////////
+function FormNewScrollbox( Form: PControl ): PControl;
+type PScrollerBars = ^TScrollerBars;
+var es: TEdgeStyle;
+ b: Integer;
+begin es := TEdgeStyle( Form.FormGetIntParam );
+ b := Form.FormGetIntParam;
+ Result := NewScrollbox( Form.DF.FormCurrentParent, es, PScrollerBars( @ b )^ );
+end;////////////////////////////////////////////////////////////////////////////
+function FormNewScrollboxEx( Form: PControl ): PControl;
+begin Result := NewScrollboxEx( Form.DF.FormCurrentParent,
+ TEdgeStyle( Form.FormGetIntParam ) );
+end;////////////////////////////////////////////////////////////////////////////
+function FormNewScrollBar( Form: PControl ): PControl;
+begin Result := NewScrollbar( Form.DF.FormCurrentParent,
+ TScrollerBar( Form.FormGetIntParam ) );
+end;////////////////////////////////////////////////////////////////////////////
+function FormNewProgressBar( Form: PControl ): PControl;
+begin Result := NewProgressBar( Form.DF.FormCurrentParent ); end;///////////////
+function FormNewProgressBarEx( Form: PControl ): PControl;
+type PProgressbarOptions = ^TProgressbarOptions;
+begin Result := NewProgressBarEx( Form.DF.FormCurrentParent,
+ PProgressbarOptions(Form.FormGetIntParam)^ );
+end;////////////////////////////////////////////////////////////////////////////
+function FormNewDateTimePicker( Form: PControl ): PControl;
+type PDateTimePickerOptions = ^TDateTimePickerOptions;
+var o: Integer;
+begin o := Form.FormGetIntParam;
+ Result := NewDateTimePicker( Form.DF.FormCurrentParent,
+ PDateTimePickerOptions( @ o )^ );
+end;////////////////////////////////////////////////////////////////////////////
+{$IFDEF _D4orHigher}
+function FormNewTabControl( Form: PControl ): PControl;
+type PTabControlOptions = ^TTabControlOptions;
+var N, i, o: Integer;
+ Tabs1: array of KOLString;
+ Tabs2: array of PKOLChar;
+begin N := Form.FormGetIntParam;
+ SetLength( Tabs1, N );
+ SetLength( Tabs2, N );
+ for i := 0 to N-1 do
+ begin
+ Form.FormGetStrParam;
+ Tabs1[i] := Form.FormString;
+ Tabs2[i] := PKOLChar( Tabs1[i] );
+ end;
+ o := Form.FormGetIntParam;
+ i := Form.FormGetIntParam;
+ Result := NewTabControl( Form.DF.FormCurrentParent,
+ Tabs2,
+ PTabControlOptions(@ o)^,
+ nil, i );
+ SetLength( Tabs1, 0 );
+ SetLength( Tabs2, 0 );
+end;
+{$ENDIF}////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ENDIF PAS_VERSION}////////////////////////////////////////
+function ParentForm_PCharParam(Control: PControl): PKOLChar;
+var Form: PControl;
+begin Form := Control.FormParentForm;
+ Form.FormGetStrParam;
+ Result := PKOLChar( KOLString( Form.FormString ) );
+end;////////////////////////////////////////////////////////////////////////////
+function ParentForm_IntParamPas(Form: PControl): Integer;
+begin Result := Form.FormParentForm.FormGetIntParam; end;///////////////////////////
+function ParentForm_ColorParamPas(Form: PControl): Integer;
+begin Result := Form.FormParentForm.FormGetColorParam; end;/////////////////////////
+{$IFDEF ASM_VERSION} // only to call from asm -- returns EAX=Parent Form, EDX=ECX=PChar param
+{$ENDIF ASM_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetSize( Form: PControl );
+var W, H: Integer;
+begin W := ParentForm_IntParamPas( Form );
+ H := ParentForm_IntParamPas( Form );
+ Form.SetSize( W, H );
+end;
+{$ENDIF}////////////////////////////////////////////////////////////////////////
+procedure FormSetHeight( Form: PControl );
+begin Form.Height := ParentForm_IntParamPas(Form); end;/////////////////////////
+procedure FormSetWidth( Form: PControl );
+begin Form.Width := ParentForm_IntParamPas(Form); end;//////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetPosition( Form: PControl );
+var X, Y: Integer;
+begin X := ParentForm_IntParamPas(Form);
+ Y := ParentForm_IntParamPas(Form);
+ Form.SetPosition( X, Y );
+end;
+{$ENDIF}////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetClientSize( Form: PControl );
+var W, H: Integer;
+begin W := ParentForm_IntParamPas(Form);
+ H := ParentForm_IntParamPas(Form);
+ Form.SetClientSize( W, H );
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetAlign( Form: PControl );
+begin Form.SetAlign( TControlAlign( ParentForm_IntParamPas(Form) ) ); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF USE_NAMES}
+procedure FormSetName( Form: PControl );
+var C: PControl;
+begin C := Form;
+ Form := Form.FormParentForm;
+ Form.FormGetStrParam;
+ C.SetName( Form, Form.FormString );
+end;
+{$ENDIF USE_NAMES}//////////////////////////////////////////////////////////////
+procedure FormSetTag( Form: PControl );
+var tag: DWORD;
+begin
+ tag := ParentForm_IntParamPas(Form);
+ Form.Tag := tag;
+end;
+{$IFDEF UNICODE_CTRLS}
+procedure FormSetUnicode( Form: PControl );
+begin Form.SetUnicode( TRUE ); end;
+{$ENDIF UNICODE_CTRLS}//////////////////////////////////////////////////////////
+procedure FormAssignHelpContext( Form: PControl );
+begin Form.AssignHelpContext( ParentForm_IntParamPas( Form ) ); end;////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetCanResizeFalse( Form: PControl );
+begin Form.CanResize := FALSE; end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormInitMenu( Form: PControl );
+begin Form.Perform( WM_INITMENU, 0, 0 ); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+procedure FormSizeGripFalse( Form: PControl );
+begin Form.SizeGrip := FALSE; end; /////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetExStyle( Form: PControl );
+begin Form.ExStyle := Form.ExStyle or DWORD( ParentForm_IntParamPas(Form) ); end;
+{$ENDIF}////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetVisibleFalse( Form: PControl );
+begin Form.Visible := FALSE; end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetEnabledFalse( Form: PControl );
+begin Form.Enabled := FALSE; end;
+{$ENDIF}////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormResetStyles( Form: PControl );
+begin Form.Style := Form.Style and not ParentForm_IntParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetStyle( Form: PControl );
+begin Form.Style := Form.Style or DWORD( ParentForm_IntParamPas(Form)); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetAlphaBlend( Form: PControl );
+begin Form.AlphaBlend := ParentForm_IntParamPas( Form ); end;
+{$ENDIF}////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetHasBorderFalse( Form: PControl );
+begin Form.HasBorder := FALSE; end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetHasCaptionFalse( Form: PControl );
+begin Form.HasCaption := FALSE; end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormResetCtl3D( Form: PControl );
+begin Form.Ctl3D := FALSE; end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormIconLoad_hInstance( Form: PControl );
+begin Form.IconLoad( hInstance,
+ MAKEINTRESOURCE( ParentForm_IntParamPas(Form) ) );
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormIconLoadCursor_0( Form: PControl );
+begin Form.IconLoadCursor( 0, MakeIntResource( ParentForm_IntParamPas(Form) ) );
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetIconNeg1( Form: PControl );
+begin Form.Icon := THandle( -1 ); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+procedure FormIconLoad_hInstance_str( Form: PControl );
+begin Form.FormGetStrParam;
+ Form.IconLoad( hInstance, PKOLChar( KOLString( Form.FormString ) ) );
+end;////////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetWindowState( Form: PControl );
+begin Form.WindowState := TWindowState( ParentForm_IntParamPas(Form) ); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormCursorLoad_0( Form: PControl );
+begin Form.CursorLoad( 0, MAKEINTRESOURCE( ParentForm_IntParamPas(Form) ) ); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+procedure FormCursorLoad_hInstance( Form: PControl );
+var C: PControl;
+begin C := Form;
+ Form := Form.FormParentForm;
+ Form.FormGetStrParam;
+ C.CursorLoad( 0, PKOLChar( KOLString( Form.FormString ) ) );
+end;////////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetColor( Form: PControl );
+begin Form.Color := ParentForm_ColorParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetBrushStyle( Form: PControl );
+begin Form.Brush.BrushStyle := TBrushStyle( ParentForm_IntParamPas(Form) ); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetBrushBitmap( Form: PControl );
+var C: PControl;
+begin C := Form;
+ Form := Form.FormParentForm;
+ {$IFDEF UNICODE_CTRLS}
+ Form.FormGetStrParam;
+ {$ENDIF}
+ C.Brush.BrushBitmap :=
+ LoadBmp( hInstance,
+ {$IFDEF UNICODE_CTRLS} PKOLChar( KOLString( Form.FormString ) )
+ {$ELSE} ParentForm_PCharParam(Form)
+ {$ENDIF} , Form );
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetFontColor( Form: PControl );
+begin Form.Font.Color := ParentForm_ColorParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetFontStyles( Form: PControl );
+type PFontStyle = ^TFontStyle;
+var fs: Byte;
+begin fs := ParentForm_IntParamPas(Form);
+ Form.Font.FontStyle := PFontStyle( @ fs )^;
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetFontHeight( Form: PControl );
+begin Form.Font.FontHeight := ParentForm_IntParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetFontWidth( Form: PControl );
+begin Form.Font.FontWidth := ParentForm_IntParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+procedure ParentForm_StrParam( Form: PControl );
+begin Form := Form.FormParentForm;
+ Form.FormGetStrParam;
+end;////////////////////////////////////////////////////////////////////////////
+procedure FormSetFontName( Form: PControl );
+begin ParentForm_StrParam(Form);
+ Form.Font.FontName := Form.FormParentForm.FormString;
+end;////////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetFontOrientation( Form: PControl );
+begin Form.Font.FontOrientation := ParentForm_IntParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetFontCharset( Form: PControl );
+begin Form.Font.FontCharset := ParentForm_IntParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetFontPitch( Form: PControl );
+begin Form.Font.FontPitch := TFontPitch( ParentForm_IntParamPas(Form) ); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetBorder( Form: PControl );
+begin Form.Border := ParentForm_IntParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetMarginTop( Form: PControl );
+begin Form.MarginTop := ParentForm_IntParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetMarginBottom( Form: PControl );
+begin Form.MarginBottom := ParentForm_IntParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetMarginLeft( Form: PControl );
+begin Form.MarginLeft := ParentForm_IntParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetMarginRight( Form: PControl );
+begin Form.MarginRight := ParentForm_IntParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetSimpleStatusText( Form: PControl );
+begin Form.SimpleStatusText := ParentForm_PCharParam(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetStatusText( Form: PControl );
+var I: Integer;
+begin I := ParentForm_IntParamPas(Form);
+ Form.StatusText[I] := ParentForm_PCharParam(Form);
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormRemoveCloseIcon( Form: PControl );
+begin DeleteMenu( GetSystemMenu( Form.GetWindowHandle, False ),
+ SC_CLOSE, MF_BYCOMMAND );
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+procedure FormSetEraseBkgndTrue( Form: PControl );
+begin Form.EraseBackground := TRUE; end;
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetMinWidth( Form: PControl );
+begin Form.MinWidth := ParentForm_IntParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetMaxWidth( Form: PControl );
+begin Form.MaxWidth := ParentForm_IntParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetMinHeight( Form: PControl );
+begin Form.MinHeight := ParentForm_IntParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetMaxHeight( Form: PControl );
+begin Form.MaxHeight := ParentForm_IntParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF noASM_VERSION}
+procedure FormSetRepeatInterval( Form: PControl );
+asm CALL ParentForm_IntParamAsm
+ MOV [EAX].TControl.fRepeatInterval, EDX
+end;
+{$ELSE PAS_VERSION}
+procedure FormSetRepeatInterval( Form: PControl );
+begin Form.RepeatInterval := ParentForm_IntParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+procedure FormSetKeyPreviewTrue( Form: PControl );
+begin {$IFDEF KEY_PREVIEW}
+ Form.KeyPreview := TRUE;
+ {$ENDIF}
+end;////////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetTextShiftX( Form: PControl );
+begin Form.TextShiftX := ParentForm_IntParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetTextShiftY( Form: PControl );
+begin Form.TextShiftY := ParentForm_IntParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetColor2( Form: PControl );
+begin Form.Color2 := ParentForm_ColorParamPas( Form ); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetTextAlign( Form: PControl );
+begin Form.TextAlign := TTextAlign( ParentForm_IntParamPas(Form) ); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetTextVAlign( Form: PControl );
+begin Form.VerticalAlign := TVerticalAlign( ParentForm_IntParamPas(Form) ); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+procedure FormSetTabStopFalse( Form: PControl );
+begin Form.TabStop := FALSE; end;
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetIgnoreDefault( Form: PControl );
+begin Form.IgnoreDefault := Boolean( ParentForm_IntParamPas(Form) ); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+procedure FormSetHintText( Form: PControl );
+begin {$IFDEF USE_MHTOOLTIP}
+ ParentForm_StrParam(Form);
+ Form.Hint.Text := Form.FormParentForm.FormString;
+ {$ENDIF USE_MHTOOLTIP}
+end;////////////////////////////////////////////////////////////////////////////
+procedure FormSetAnchor( Form: PControl );
+var i: Integer;
+begin i := ParentForm_IntParamPas(Form);
+ Form.AnchorLeft := I and 1 <> 0;
+ Form.AnchorTop := I and 2 <> 0;
+ Form.AnchorRight := I and 4 <> 0;
+ Form.AnchorBottom := I and 8 <> 0;
+end;////////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetCaption( Form: PControl );
+var Ctl: PControl;
+begin Ctl := Form;
+ Form := Form.FormParentForm;
+ Form.FormGetStrParam;
+ Ctl.Caption := Form.FormString;
+end;
+{$ENDIF PAS_VERSION}
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetGradienStyle( Form: PControl );
+begin Form.GradientStyle := TGradientStyle( ParentForm_IntParamPas(Form) ); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+procedure FormOverrideScrollbars( Form: PControl );
+begin OverrideScrollbars( Form ); end;
+{$IFDEF USE_RICHEDIT}
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetRE_AutoFontFalse( Form: PControl );
+begin Form.RE_AutoFont := FALSE; end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetRE_AutoFontSizeAdjustFalse( Form: PControl );
+begin Form.RE_AutoFontSizeAdjust := FALSE; end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetRE_DualFontTrue( Form: PControl );
+begin Form.RE_DualFont := TRUE; end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetRE_UIFontsTrue( Form: PControl );
+begin Form.RE_UIFonts := TRUE; end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetRE_IMECancelCompleteTrue( Form: PControl );
+begin Form.RE_IMECancelComplete := TRUE; end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetRE_IMEAlwaysSendNotifyTrue( Form: PControl );
+begin Form.RE_IMEAlwaysSendNotify := TRUE; end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetMaxTextSize( Form: PControl );
+begin Form.MaxTextSize := DWORD( ParentForm_IntParamPas(Form) ); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetRE_AutoKeyboardTrue( Form: PControl );
+begin Form.RE_AutoKeyboard := TRUE; end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+procedure FormSetRE_DisableOverwriteChangeTrue( Form: PControl );
+begin Form.RE_DisableOverwriteChange := TRUE;
+end;////////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetRE_Zoom( Form: PControl );
+var zoom: TSmallPoint;
+begin zoom.X := ParentForm_IntParamPas(Form);
+ zoom.Y := ParentForm_IntParamPas(Form);
+ Form.RE_Zoom := zoom;
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$ENDIF USE_RICHEDIT}
+procedure FormSetListItems( Form: PControl );
+var N, i: Integer;
+begin N := ParentForm_IntParamPas(Form);
+ for i := 0 to N-1 do BEGIN
+ ParentForm_StrParam(Form);
+ Form.Items[i] := Form.FormParentForm.FormString;
+ END;
+end;
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetCount( Form: PControl );
+begin Form.Count := ParentForm_IntParamPas(Form);
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetDroppedWidth( Form: PControl );
+begin Form.DroppedWidth := ParentForm_IntParamPas(Form);
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+procedure FormSetButtonIcon( Form: PControl );
+begin
+ Form.SetButtonIcon( LoadImage( hInstance,
+ ParentForm_PCharParam(Form),
+ IMAGE_ICON, 0, 0, $8000 {LR_SHARED} ) );
+end;////////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetButtonImage( Form: PControl );
+var w, h: Integer;
+begin w := ParentForm_IntParamPas(Form);
+ h := ParentForm_IntParamPas(Form);
+ Form.SetButtonIcon( LoadImage( hInstance,
+ ParentForm_PCharParam(Form),
+ IMAGE_ICON, w, h, $8000 {LR_SHARED} ) );
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetButtonBitmap( Form: PControl );
+begin Form.SetButtonBitmap( LoadBitmap( hInstance,
+ ParentForm_PCharParam(Form) ) );
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+procedure FormSetDefaultBtn( Form: PControl );
+var i: Integer;
+begin i := ParentForm_IntParamPas(Form);
+ Form.SetDefaultBtn( i, TRUE );
+end;
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetMaxProgress( Form: PControl );
+begin Form.MaxProgress := ParentForm_IntParamPas(Form);
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetProgress( Form: PControl );
+begin Form.Progress := ParentForm_IntParamPas(Form);
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormLVColumsAdd( Form: PControl );
+var N, i, w: Integer;
+begin N := ParentForm_IntParamPas(Form);
+ for i := 0 to N-1 do BEGIN
+ w := ParentForm_IntParamPas(Form);
+ ParentForm_StrParam(Form);
+ Form.LVColAdd( Form.FormParentForm.FormString, taLeft, w );
+ END;
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetLVColOrder( Form: PControl );
+var N, i: Integer;
+begin N := ParentForm_IntParamPas(Form);
+ i := ParentForm_IntParamPas(Form);
+ Form.LVColOrder[N] := i;
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetLVColImage( Form: PControl );
+var N, i: Integer;
+begin N := ParentForm_IntParamPas(Form);
+ i := ParentForm_IntParamPas(Form);
+ Form.LVColImage[N] := i;
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetTVIndent( Form: PControl );
+begin Form.TVIndent := ParentForm_IntParamPas(Form);
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+procedure FormSetTBBtnImgWidth( Form: PControl );
+begin Form.TBBtnImgWidth := ParentForm_IntParamPas( Form );
+end;////////////////////////////////////////////////////////////////////////////
+procedure FormTBAddBitmap( Form: PControl );
+var map: array[ 0..1 ] of TColor;
+ b: Integer;
+ C: PControl;
+begin C := Form;
+ Form := Form.FormParentForm;
+ b := Form.FormGetIntParam;
+ if b >= 0 then
+ begin
+ Form.FormGetStrParam;
+ if b <> 0 then
+ begin
+ map[0] := Form.FormGetColorParam;
+ map[1] := Color2RGB( clBtnFace );
+ b := LoadMappedBitmapEx( Form, hInstance, PKOLChar( KOLString( Form.FormString )), map );
+ end else
+ b := LoadBmp( hInstance, PKOLChar(KOLString(Form.FormString)), Form );
+ end;
+ C.TBAddBitmap( b );
+end;////////////////////////////////////////////////////////////////////////////
+procedure FormSetTBButtonSize( Form: PControl );
+begin Form.Perform( TB_SETBUTTONSIZE, 0,
+ ParentForm_IntParamPas(Form) or $10000 {or (HiWord(HW) shl 16)} );
+end;////////////////////////////////////////////////////////////////////////////
+{$IFDEF _D4orHigher}
+procedure FormTBSetTooltips( Form: PControl );
+var A1: array of KOLString;
+ A2: array of PKOLChar;
+ N, i: Integer;
+ C: PControl;
+begin C := Form;
+ Form := Form.FormParentForm;
+ N := Form.FormGetIntParam;
+ SetLength( A1, N );
+ SetLength( A2, N );
+ for i := 0 to N-1 do
+ begin
+ Form.FormGetStrParam;
+ A1[i] := Form.FormString;
+ A2[i] := PKOLChar( A1[i] );
+ end;
+ C.TBSetTooltips( 0, A2 );
+ SetLength( A1, 0 );
+ SetLength( A2, 0 );
+end;
+{$ENDIF _D4orHigher}////////////////////////////////////////////////////////////
+procedure FormSetTBButtonsMinWidth( Form: PControl );
+begin Form.TBButtonsMinWidth := ParentForm_IntParamPas(Form);
+end;////////////////////////////////////////////////////////////////////////////
+procedure FormSetTBButtonsMaxWidth( Form: PControl );
+begin Form.TBButtonsMaxWidth := ParentForm_IntParamPas(Form);
+end;////////////////////////////////////////////////////////////////////////////
+procedure FormHideToolbarButton( Form: PControl );
+var i: Integer;
+begin i := ParentForm_IntParamPas(Form);
+ {$IFDEF USE_GRUSH}
+ ShowHideToolbarButton( Form, i, FALSE );
+ {$ELSE} Form.TBButtonVisible[ i ] := FALSE; {$ENDIF}
+end;////////////////////////////////////////////////////////////////////////////
+procedure FormDisableToolbarButton( Form: PControl );
+var i: Integer;
+begin i := ParentForm_IntParamPas(Form);
+ {$IFDEF USE_GRUSH}
+ EnableToolbarButton( Form, i, FALSE );
+ {$ELSE} Form.TBButtonEnabled[ i ] := FALSE; {$ENDIF}
+end;////////////////////////////////////////////////////////////////////////////
+procedure FormFixFlatXPToolbar( Form: PControl );
+begin Form.OnTBCustomDraw := nil;
+end;////////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetDateTimeFormat( Form: PControl );
+begin ParentForm_StrParam(Form);
+ Form.DateTimeFormat := Form.FormParentForm.FormString;
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+procedure FormSetDateTimeColor( Form: PControl );
+var i: Integer;
+ C: TColor;
+begin C := ParentForm_ColorParamPas( Form );
+ i := ParentForm_IntParamPas( Form );
+ Form.DateTimePickerColors[TDateTimePickerColor(i)] := C;
+end;////////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetCurrentTab( Form: PControl );
+var i: Integer;
+begin i := ParentForm_IntParamPas(Form);
+ Form.CurIndex := i;
+ Form.Pages[i].BringToFront;
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetCurIdx( Form: PControl );
+begin Form.CurIndex := ParentForm_IntParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetSBMin( Form: PControl );
+begin Form.SBMin := ParentForm_IntParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetSBMax( Form: PControl );
+begin Form.SBMax := ParentForm_IntParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetSBPosition( Form: PControl );
+begin Form.SBPosition := ParentForm_IntParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetSBPageSize( Form: PControl );
+begin Form.SBPageSize := ParentForm_IntParamPas(Form); end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormLastCreatedChildAsNewCurrentParent( Form: PControl );
+var C: PControl;
+begin C := Form;
+ Form := Form.FormParentForm;
+ Form.DF.FormCurrentParent := C;
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+procedure FormSetUpperParent( Form: PControl );
+begin Form := Form.FormParentForm;
+ Form.DF.FormCurrentParent := Form.DF.FormCurrentParent.Parent;
+end;////////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}
+procedure FormSetTabpageAsParent( Form: PControl );
+var i: Integer;
+ C: PControl;
+begin C := Form;
+ Form := Form.FormParentForm;
+ i := Form.FormGetIntParam;
+ Form.DF.FormCurrentParent := C.Pages[i];
+ Form.DF.FormLastCreatedChild := Form.DF.FormCurrentParent;
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE PASCAL}//////////////////////////////////////////////
+procedure FormSetCurCtl( Form: PControl );
+var i: Integer;
+ C: PControl;
+begin Form := Form.FormParentForm;
+ i := Form.FormGetIntParam;
+ C := PPControl(Integer( Form.DF.FormAddress ) + i * 4)^;
+ if C = nil then C := Form;
+ Form.DF.FormLastCreatedChild := C;
+end;
+{$ENDIF PAS_VERSION}////////////////////////////////////////////////////////////
+procedure FormSetParent( Form: PControl );
+var C: PControl;
+begin C := Form;
+ Form := Form.FormParentForm;
+ Form.DF.FormCurrentParent := C;
+end;////////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE}/////////////////////////////////////////////////////
+procedure FormSetEvent( Form: PControl );
+type TSetEventProc = procedure( TargetCtl: PControl; const event: TOnEvent );
+var C: PControl;
+ idx_handler, idx_setter: Integer;
+ handler, setter: Pointer;
+ event: TOnEvent;
+ set_proc: TSetEventProc;
+begin C := Form;
+ Form := Form.FormParentForm;
+ idx_handler := Form.FormGetIntParam;
+ idx_setter := Form.FormGetIntParam;
+ handler := @Form.DF.FormAlphabet[idx_handler];
+ setter := @Form.DF.FormAlphabet[idx_setter];
+ set_proc := TSetEventProc( setter );
+ Pointer( TMethod( event ).Code ) := handler;
+ TMethod( event ).Data := Form.DF.FormObj;
+ set_proc( PControl( C ), event );
+end;
+{$ENDIF}////////////////////////////////////////////////////////////////////////
+{$IFDEF ASM_VERSION}{$ELSE}/////////////////////////////////////////////////////
+procedure FormSetIndexedEvent( Form: PControl );
+type TSetIndexedEventProc = procedure( TargetCtl: PControl; Index: Integer;
+ const event: TOnEvent );
+var C: PControl;
+ idx_handler, idx_setter, idx: Integer;
+ handler, setter: Pointer;
+ event: TOnEvent;
+ set_proc: TSetIndexedEventProc;
+begin C := Form;
+ Form := Form.FormParentForm;
+ idx_handler := Form.FormGetIntParam;
+ idx := Form.FormGetIntParam;
+ idx_setter := Form.FormGetIntParam;
+ handler := @Form.DF.FormAlphabet[idx_handler];
+ setter := @Form.DF.FormAlphabet[idx_setter];
+ set_proc := TSetIndexedEventProc( setter );
+ Pointer( TMethod( event ).Code ) := handler;
+ TMethod( event ).Data := Form.DF.FormObj;
+ set_proc( PControl( C ), idx, event );
+end;
+{$ENDIF}////////////////////////////////////////////////////////////////////////
+procedure DummyOverrideScrollbars(Sender: PControl);
+begin
+end;
+
+{$IFnDEF PAS_VERSION}
+ {$I KOL_ASM.inc} //<<<<<<<<<<<<<<<<<<<<<<< KOL_ASM.inc
+ {$IFnDEF UNICODE_CTRLS}
+ {$I KOL_ASM_NOUNICODE.inc} //<<<<<<<<< KOL_ASM_NOUNICODE.inc
+ {$ENDIF noUNICODE}
+{$ENDIF PAS_VERSION}
+{$IFDEF LIN}
+ {$DEFINE implementation} {$I KOL_Linux.inc} {$UNDEF implementation}
+{$ENDIF LIN}
+
+{$IFDEF USE_CUSTOMEXTENSIONS}
+ {$I CUSTOM_CODE_EXTENSION.inc} // See comments in TControl
+{$ENDIF USE_CUSTOMEXTENSIONS}
+
+{$IFDEF EVENTS_DYNAMIC}//-------------------------------------------------------
+{$IFDEF ASM_VERSION}
+function TControl.ProvideUniqueEvents: PEvents;
+const Size_TEvents = Sizeof(TEvents);
+asm PUSH ESI
+ XCHG ESI, EAX
+ MOV EAX, [ESI].TControl.EV
+ CMP EAX, offset[EmptyEvents]
+ JNZ @@ready
+
+ MOV EAX, Size_TEvents
+ CALL System.@GetMem
+ MOV [ESI].TControl.EV, EAX
+ PUSH EAX
+ XCHG EDX, EAX
+ MOV EAX, offset[EmptyEvents]
+ MOV ECX, Size_TEvents
+ CALL Move
+ PUSH ESI
+ PUSH offset[FreeEV]
+ XCHG EAX, ESI
+ CALL TControl.Add2AutoFreeEx
+ POP EAX
+@@ready:
+ POP ESI
+end;
+{$ELSE}
+function TControl.ProvideUniqueEvents: PEvents;
+begin if EV = @EmptyEvents then
+ begin
+ GetMem( EV, Sizeof(TEvents) );
+ Move( EmptyEvents, EV^, Sizeof(TEvents) );
+ Add2AutoFreeEx( FreeEV );
+ end;
+ Result := EV;
+end; {$ENDIF PAS_VERSION}///////////////////////////////////////////////////////
+
+procedure TControl.FreeEV;
+begin FreeMem( EV );
+ EV := @EmptyEvents;
+end;////////////////////////////////////////////////////////////////////////////
+function TControl.Get_OnHelp: TOnHelp;
+begin Result := EV.fOnHelp; end;
+procedure TControl.Set_OnHelp(const Value: TOnHelp);
+begin ProvideUniqueEvents.fOnHelp := Value; end;
+function TControl.Get_OnBitBtnDraw: TOnBitBtnDraw;
+begin Result := EV.FOnBitBtnDraw; end;
+procedure TControl.Set_OnBitBtnDraw(const Value: TOnBitBtnDraw);
+begin ProvideUniqueEvents.FOnBitBtnDraw := Value; end;
+function TControl.Get_OnMeasureItem: TOnMeasureItem;
+begin Result := EV.fOnMeasureItem; end;
+function TControl.Get_OnShow: TOnEvent;
+begin Result := EV.fOnShow; end;
+function TControl.Get_OnHide: TOnEvent;
+begin Result := EV.fOnHide; end;
+function TControl.Get_OnClose: TOnEventAccept;
+begin Result := EV.fOnClose; end;
+function TControl.Get_OnQueryEndSession: TOnEventAccept;
+begin Result := EV.fOnQueryEndSession; end;
+function TControl.Get_OnPaint: TOnPaint;
+begin Result := EV.fOnPaint; end;
+function TControl.Get_OnPrePaint: TOnPaint;
+begin Result := EV.fOnPrepaint; end;
+procedure TControl.Set_OnPrePaint(const Value: TOnPaint);
+begin ProvideUniqueEvents.fOnPrepaint := Value; end;
+function TControl.Get_OnPostPaint: TOnPaint;
+begin Result := EV.fOnPostPaint; end;
+procedure TControl.Set_OnPostPaint(const Value: TOnPaint);
+begin ProvideUniqueEvents.fOnPostPaint := Value; end;
+function TControl.Get_OnEraseBkgnd: TOnPaint;
+begin Result := EV.fOnEraseBkgnd; end;
+procedure TControl.Set_OnEraseBkgnd(const Value: TOnPaint);
+begin ProvideUniqueEvents.fOnEraseBkgnd := Value;
+ AttachProc( WndProcEraseBkgnd );
+end;////////////////////////////////////////////////////////////////////////////
+function TControl.Get_OnClick: TOnEvent;
+begin Result := EV.fOnClick; end;
+function TControl.Get_OnResize: TOnEvent;
+begin Result := EV.fOnResize; end;
+function TControl.Get_OnMove: TOnEvent;
+begin Result := EV.fOnMove; end;
+function TControl.Get_OnMoving: TOnEventMoving;
+begin Result := EV.fOnMoving; end;
+function TControl.Get_OnSplit: TOnSplit;
+begin Result := EV.FOnSplit; end;
+procedure TControl.Set_OnSplit(const Value: TOnSplit);
+begin ProvideUniqueEvents.FOnSplit := Value; end;
+function TControl.Get_OnKeyDown: TOnKey;
+begin Result := EV.fOnKeyDown; end;
+function TControl.Get_OnKeyUp: TOnKey;
+begin Result := EV.fOnKeyUp; end;
+function TControl.Get_OnChar: TOnChar;
+begin Result := EV.fOnChar; end;
+function TControl.Get_OnDeadChar: TOnChar;
+begin Result := EV.fOnDeadChar; end;
+function TControl.Get_OnMouseUp: TOnMouse;
+begin Result := EV.fOnMouseUp; end;
+function TControl.Get_OnMouseDown: TOnMouse;
+begin Result := EV.fOnMouseDown; end;
+function TControl.Get_OnMouseMove: TOnMouse;
+begin Result := EV.fOnMouseMove; end;
+function TControl.Get_OnMouseDblClk: TOnMouse;
+begin Result := EV.fOnMouseDblClk; end;
+function TControl.Get_OnMouseWheel: TOnMouse;
+begin Result := EV.fOnMouseWheel; end;
+function TControl.Get_OnMouseEnter: TOnEvent;
+begin Result := EV.fOnMouseEnter; end;
+function TControl.Get_OnMouseLeave: TOnEvent;
+begin Result := EV.fOnMouseLeave; end;
+function TControl.Get_OnTestMouseOver: TOnTestMouseOver;
+begin Result := EV.fOnTestMouseOver; end;
+function TControl.Get_OnEndEditLVItem: TOnEditLVItem;
+begin Result := EV.fOnEndEditLVItem; end;
+function TControl.Get_OnDeleteLVItem: TOnDeleteLVItem;
+begin Result := EV.fOnDeleteLVItem; end;
+function TControl.Get_OnLVData: TOnLVData;
+begin Result := EV.fOnLVData; end;
+function TControl.Get_OnCompareLVItems: TOnCompareLVItems;
+begin Result := EV.fOnCompareLVItems; end;
+procedure TControl.Set_OnCompareLVItems(const Value: TOnCompareLVItems);
+begin ProvideUniqueEvents.fOnCompareLVItems := Value; end;
+function TControl.Get_OnColumnClick: TOnLVColumnClick;
+begin Result := EV.fOnColumnClick; end;
+function TControl.Get_OnLVStateChange: TOnLVStateChange;
+begin Result := EV.FOnLVStateChange; end;
+function TControl.Get_OnDrawItem: TOnDrawItem;
+begin Result := EV.fOnDrawItem; end;
+function TControl.Get_OnLVCustomDraw: TOnLVCustomDraw;
+begin Result := EV.fOnLVCustomDraw; end;
+function TControl.Get_OnTVBeginDrag: TOnTVBeginDrag;
+begin Result := EV.FOnTVBeginDrag; end;
+procedure TControl.Set_OnTVBeginDrag(const Value: TOnTVBeginDrag);
+begin ProvideUniqueEvents.FOnTVBeginDrag := Value; end;
+function TControl.Get_OnTVBeginEdit: TOnTVBeginEdit;
+begin Result := EV.FOnTVBeginEdit; end;
+procedure TControl.Set_OnTVBeginEdit(const Value: TOnTVBeginEdit);
+begin ProvideUniqueEvents.FOnTVBeginEdit := Value; end;
+function TControl.Get_OnTVEndEdit: TOnTVEndEdit;
+begin Result := EV.FOnTVEndEdit; end;
+procedure TControl.Set_OnTVEndEdit(const Value: TOnTVEndEdit);
+begin ProvideUniqueEvents.fOnTVEndEdit := Value; end;
+function TControl.Get_OnTVExpanding: TOnTVExpanding;
+begin Result := EV.FOnTVExpanding; end;
+procedure TControl.Set_OnTVExpanding(const Value: TOnTVExpanding);
+begin ProvideUniqueEvents.FOnTVExpanding := Value; end;
+function TControl.Get_OnTVExpanded: TOnTVExpanded;
+begin Result := EV.FOnTVExpanded; end;
+procedure TControl.Set_OnTVExpanded(const Value: TOnTVExpanded);
+begin ProvideUniqueEvents.FOnTVExpanded := Value; end;
+function TControl.Get_OnTVDelete: TOnTVDelete;
+begin Result := EV.FOnTVDelete; end;
+function TControl.Get_OnTVSelChanging: TOnTVSelChanging;
+begin Result := EV.fOnTVSelChanging; end;
+procedure TControl.Set_OnTVSelChanging(const Value: TOnTVSelChanging);
+begin ProvideUniqueEvents.FOnTVSelChanging := Value; end;
+function TControl.Get_OnDTPUserString: TDTParseInputEvent;
+begin Result := EV.FOnDTPUserString; end;
+procedure TControl.Set_OnDTPUserString(const Value: TDTParseInputEvent);
+begin ProvideUniqueEvents.FOnDTPUserString := Value; end;
+function TControl.Get_OnSBBeforeScroll: TOnSBBeforeScroll;
+begin Result := EV.FOnSBBeforeScroll; end;
+procedure TControl.Set_OnSBBeforeScroll(const Value: TOnSBBeforeScroll);
+begin ProvideUniqueEvents.fOnSBBeforeScroll := Value; end;
+function TControl.Get_OnSBScroll: TOnSBScroll;
+begin Result := EV.FOnSBScroll; end;
+procedure TControl.Set_OnSBScroll(const Value: TOnSBScroll);
+begin ProvideUniqueEvents.FOnSBScroll := Value; end;
+function TControl.Get_OnScroll: TOnScroll;
+begin Result := EV.fOnScroll; end;
+function TControl.Get_OnMessage: TOnMessage;
+begin Result := EV.fOnMessage; end;
+procedure TControl.Set_OnMessage(const Value: TOnMessage);
+begin ProvideUniqueEvents.fOnMessage := Value; end;
+function TControl.Get_TOnEvent(const Index: Integer): TOnEvent;
+begin Result := TOnEvent( EV.MethodEvents[Index] ); end;
+procedure TControl.Set_TOnEvent(const Index: Integer; const Value: TOnEvent);
+begin ProvideUniqueEvents.MethodEvents[Index] := TMethod( Value ); end;
+function TControl.Get_OnDropFiles: TOnDropFiles;
+begin Result := EV.fOnDropFiles; end;
+{$ENDIF EVENTS_DYNAMIC}//-------------------------------------------------------
+{$IFnDEF NOT_USE_RICHEDIT}
+procedure TControl.FreeCharFormatRec;
+begin {$IFnDEF STATIC_RICHEDIT_DATA} FreeMem( DF.fRECharFormatRec ); {$ENDIF} end;
+{$ENDIF}
+function TControl.GetAnchor(const Index: Integer): Boolean;
+begin Result := fAnchors and Index <> 0; end;
+procedure TControl.SetAnchor(const Index: Integer; const Value: Boolean);
+begin if Value then
+ fAnchors := fAnchors or Index
+ else fAnchors := fAnchors and not Index;
+ if Parent <> nil then
+ begin
+ fParent.AttachProc( ParentAnchorChildren );
+ Parent.fOldWidth := Parent.ClientWidth;
+ Parent.fOldHeight := Parent.ClientHeight;
+ end;
+end;////////////////////////////////////////////////////////////////////////////
+function TControl.Get_StatusWnd: HWND;
+begin Result := 0;
+ if fStatusCtl <> nil then
+ Result := fStatusCtl.GetWindowHandle;
+end;////////////////////////////////////////////////////////////////////////////
+function TControl.Get_Prop_Int(PropName: PKOLChar): Integer;
+begin Result := GetProp( GetWindowHandle, PropName ); end;
+procedure TControl.Set_Prop_Int(PropName: PKOLChar; const Value: Integer);
+begin SetProp( GetWindowHandle, PropName, Value ); end;
+function TControl.GetHelpContext: Integer;
+begin Result := 0;
+ if fHandle <> 0 then
+ Result := GetWindowContextHelpId( fHandle );
+end;////////////////////////////////////////////////////////////////////////////
+function TControl.Get_Ctl3D: Boolean;
+begin Result := fCtl3D_child and 2 <> 0; end;
+procedure TControl.ResetEvent(idx: Integer);
+begin TMethod( EV.MethodEvents[idx] ).Code := DummyProcTable[ InitEventsTable[ idx ] and $F ];
+ TMethod( EV.MethodEvents[idx] ).Data := nil;
+end;////////////////////////////////////////////////////////////////////////////
+{$IFDEF COMMANDACTIONS_OBJ}
+{ TCommandActionsObj }
+{$IFDEF ASM_VERSION}////////////////////////////////////////////////////////////
+destructor TCommandActionsObj.Destroy;
+asm MOV EDX, [EAX].fIndexInActions
+ MOV dword ptr [EDX*4+AllActions_Objs], 0
+ CALL TObj.Destroy
+end; {$ELSE}////////////////////////////////////////////////////////////////////
+destructor TCommandActionsObj.Destroy;
+begin AllActions_Objs[fIndexInActions] := nil;
+ inherited;
+end; {$ENDIF}///////////////////////////////////////////////////////////////////
+{$ENDIF}
+{$IFDEF GRAPHCTL_XPSTYLES}{$DEFINE INIT_FINIT}{$ENDIF}
+{$IFDEF USE_NAMES}{$DEFINE INIT_FINIT}{$ENDIF}
+{$IFNDEF NOT_UNLOAD_RICHEDITLIB}{$IFDEF UNLOAD_RICHEDITLIB}
+ {$DEFINE INIT_FINIT}
+{$ENDIF}{$ENDIF}
+{$IFDEF INIT_FINIT}//-----------------------------------------------------------
+//******************************************************************************
+initialization //...............................................................
+{$IFDEF GRAPHCTL_XPSTYLES}
+ CheckThemes;
+ if AppTheming then
+ InitThemes;
+{$ENDIF}
+finalization //.................................................................
+{$IFDEF GRAPHCTL_XPSTYLES}
+ if AppTheming then
+ DeinitThemes;
+{$ENDIF}
+{$IFNDEF NOT_UNLOAD_RICHEDITLIB}
+{$IFDEF UNLOAD_RICHEDITLIB}
+ if FRichEditModule <> 0 then
+ FreeLibrary( FRichEditModule );
+{$ENDIF UNLOAD_RICHEDITLIB}
+{$ENDIF}
+{$ENDIF INIT_FINIT}//-----------------------------------------------------------
+
+end.
+
+
+
+
+
+
+
+
diff --git a/plugins/Libs/kolcomobj.pas b/plugins/Libs/kolcomobj.pas
new file mode 100644
index 0000000000..8d8010906a
--- /dev/null
+++ b/plugins/Libs/kolcomobj.pas
@@ -0,0 +1,2352 @@
+
+{*******************************************************}
+{ }
+{ Borland Delphi Runtime Library }
+{ COM object support }
+{ }
+{ Copyright (C) 1997,99 Inprise Corporation }
+{ }
+{*******************************************************}
+
+{$IMPORTEDDATA ON}
+unit KOLComObj;
+
+{$G+}
+{$DEFINE NOWARNINGS}
+{$I KOLDEF.inc}
+
+interface
+
+uses Windows, ActiveX, KOL, err {$IFDEF _D6orHigher}, Variants {$ENDIF};
+
+type
+{ Forward declarations }
+
+ TComObjectFactory = class;
+
+{ COM server abstract base class }
+
+ TComServerObject = class(TObject)
+ protected
+ function CountObject(Created: Boolean): Integer; virtual; abstract;
+ function CountFactory(Created: Boolean): Integer; virtual; abstract;
+ function GetHelpFileName: AnsiString; virtual; abstract;
+ function GetServerFileName: AnsiString; virtual; abstract;
+ function GetServerKey: AnsiString; virtual; abstract;
+ function GetServerName: AnsiString; virtual; abstract;
+ function GetStartSuspended: Boolean; virtual; abstract;
+ function GetTypeLib: ITypeLib; virtual; abstract;
+ procedure SetHelpFileName(const Value: AnsiString); virtual; abstract;
+ public
+ property HelpFileName: AnsiString read GetHelpFileName write SetHelpFileName;
+ property ServerFileName: AnsiString read GetServerFileName;
+ property ServerKey: AnsiString read GetServerKey;
+ property ServerName: AnsiString read GetServerName;
+ property TypeLib: ITypeLib read GetTypeLib;
+ property StartSuspended: Boolean read GetStartSuspended;
+ end;
+
+
+{ TMultiReadExclusiveWriteSynchronizer minimizes thread serialization to gain
+ read access to a resource shared among threads while still providing complete
+ exclusivity to callers needing write access to the shared resource.
+ (multithread shared reads, single thread exclusive write)
+ Reading is allowed while owning a write lock.
+ Read locks can be promoted to write locks.}
+
+ {$IFNDEF _D2orD3}
+ TActiveThreadRecord = record
+ ThreadID: Integer;
+ RecursionCount: Integer;
+ end;
+ TActiveThreadArray = array of TActiveThreadRecord;
+
+ TMultiReadExclusiveWriteSynchronizer = class
+ private
+ FLock: TRTLCriticalSection;
+ FReadExit: THandle;
+ FCount: Integer;
+ FSaveReadCount: Integer;
+ FActiveThreads: TActiveThreadArray;
+ FWriteRequestorID: Integer;
+ FReallocFlag: Integer;
+ FWriting: Boolean;
+ function WriterIsOnlyReader: Boolean;
+ public
+ constructor Create;
+ destructor Destroy; override;
+ procedure BeginRead;
+ procedure EndRead;
+ procedure BeginWrite;
+ procedure EndWrite;
+ end;
+ {$ENDIF}
+
+{ COM class manager }
+
+ TFactoryProc = procedure(Factory: TComObjectFactory) of object;
+
+ TComClassManager = class(TObject)
+ private
+ FFactoryList: TComObjectFactory;
+ {$IFNDEF _D2orD3}
+ FLock: TMultiReadExclusiveWriteSynchronizer;
+ {$ENDIF}
+ procedure AddObjectFactory(Factory: TComObjectFactory);
+ procedure RemoveObjectFactory(Factory: TComObjectFactory);
+ public
+ constructor Create;
+ destructor Destroy; override;
+ procedure ForEachFactory(ComServer: TComServerObject;
+ FactoryProc: TFactoryProc);
+ function GetFactoryFromClass(ComClass: TClass): TComObjectFactory;
+ function GetFactoryFromClassID(const ClassID: TGUID): TComObjectFactory;
+ end;
+
+{ IServerExceptionHandler }
+{ This interface allows you to report safecall exceptions that occur in a
+ TComObject server to a third party, such as an object that logs errors into
+ the system event log or a server monitor residing on another machine.
+ Obtain an interface from the error logger implementation and assign it
+ to your TComObject's ServerExceptionHandler property. Each TComObject
+ instance can have its own server exception handler, or all instances can
+ share the same handler. The server exception handler can override the
+ TComObject's default exception handling by setting Handled to True and
+ assigning an OLE HResult code to the HResult parameter.
+}
+
+ IServerExceptionHandler = interface
+ ['{6A8D432B-EB81-11D1-AAB1-00C04FB16FBC}']
+ procedure OnException(
+ const ServerClass, ExceptionClass, ErrorMessage: WideString;
+ ExceptAddr: Integer; const ErrorIID, ProgID: WideString;
+ var Handled: Integer; var Result: HResult); dispid 2;
+ end;
+
+{ COM object }
+
+ TComObject = class(TObject, IUnknown, ISupportErrorInfo)
+ private
+ FController: Pointer;
+ FFactory: TComObjectFactory;
+ FNonCountedObject: Boolean;
+ FRefCount: Integer;
+ FServerExceptionHandler: IServerExceptionHandler;
+ function GetController: IUnknown;
+ protected
+ { IUnknown }
+ function IUnknown.QueryInterface = ObjQueryInterface;
+ function IUnknown._AddRef = ObjAddRef;
+ function IUnknown._Release = ObjRelease;
+ { IUnknown methods for other interfaces }
+ function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
+ function _AddRef: Integer; stdcall;
+ function _Release: Integer; stdcall;
+ { ISupportErrorInfo }
+ function InterfaceSupportsErrorInfo(const iid: TIID): HResult; stdcall;
+ public
+ constructor Create;
+ constructor CreateAggregated(const Controller: IUnknown);
+ constructor CreateFromFactory(Factory: TComObjectFactory;
+ const Controller: IUnknown);
+ destructor Destroy; override;
+ procedure Initialize; virtual;
+ function ObjAddRef: Integer; virtual; stdcall;
+ function ObjQueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall;
+ function ObjRelease: Integer; virtual; stdcall;
+ function SafeCallException(ExceptObject: TObject;
+ ExceptAddr: Pointer): HResult; override;
+ property Controller: IUnknown read GetController;
+ property Factory: TComObjectFactory read FFactory;
+ property RefCount: Integer read FRefCount;
+ property ServerExceptionHandler: IServerExceptionHandler
+ read FServerExceptionHandler write FServerExceptionHandler;
+ end;
+
+{ COM class }
+
+ TComClass = class of TComObject;
+
+{ Instancing mode for COM classes }
+
+ TClassInstancing = (ciInternal, ciSingleInstance, ciMultiInstance);
+
+{ Threading model supported by COM classes }
+
+ TThreadingModel = (tmSingle, tmApartment, tmFree, tmBoth);
+
+{ COM object factory }
+
+ {$IFDEF NOWARNINGS}
+ {$WARNINGS OFF}
+ {$ENDIF}
+ TComObjectFactory = class(TObject, IUnknown, IClassFactory, IClassFactory2)
+ private
+ FNext: TComObjectFactory;
+ FComServer: TComServerObject;
+ FComClass: TClass;
+ FClassID: TGUID;
+ FClassName: AnsiString;
+ FDescription: AnsiString;
+ FErrorIID: TGUID;
+ FInstancing: TClassInstancing;
+ FLicString: WideString;
+ FRegister: Longint;
+ FShowErrors: Boolean;
+ FSupportsLicensing: Boolean;
+ FThreadingModel: TThreadingModel;
+ protected
+ function GetProgID: AnsiString; virtual;
+ function GetLicenseString: WideString; virtual;
+ function HasMachineLicense: Boolean; virtual;
+ function ValidateUserLicense(const LicStr: WideString): Boolean; virtual;
+ { IUnknown }
+ function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
+ function _AddRef: Integer; stdcall;
+ function _Release: Integer; stdcall;
+ { IClassFactory }
+ function CreateInstance(const UnkOuter: IUnknown; const IID: TGUID;
+ out Obj): HResult; stdcall;
+ function LockServer(fLock: BOOL): HResult; stdcall;
+ { IClassFactory2 }
+ function GetLicInfo(var licInfo: TLicInfo): HResult; stdcall;
+ function RequestLicKey(dwResrved: Longint; out bstrKey: WideString): HResult; stdcall;
+ function CreateInstanceLic(const unkOuter: IUnknown; const unkReserved: IUnknown;
+ const iid: TIID; const bstrKey: WideString; out vObject): HResult; stdcall;
+ public
+ constructor Create(ComServer: TComServerObject; ComClass: TComClass;
+ const ClassID: TGUID; const ClassName, Description: AnsiString;
+ Instancing: TClassInstancing; ThreadingModel: TThreadingModel {= tmSingle} );
+ destructor Destroy; override;
+ function CreateComObject(const Controller: IUnknown): TComObject; virtual;
+ procedure RegisterClassObject;
+ procedure UpdateRegistry(Register: Boolean); virtual;
+ property ClassID: TGUID read FClassID;
+ property ClassName: AnsiString read FClassName;
+ property ComClass: TClass read FComClass;
+ property ComServer: TComServerObject read FComServer;
+ property Description: AnsiString read FDescription;
+ property ErrorIID: TGUID read FErrorIID write FErrorIID;
+ property LicString: WideString read FLicString write FLicString;
+ property ProgID: AnsiString read GetProgID;
+ property Instancing: TClassInstancing read FInstancing;
+ property ShowErrors: Boolean read FShowErrors write FShowErrors;
+ property SupportsLicensing: Boolean read FSupportsLicensing write FSupportsLicensing;
+ property ThreadingModel: TThreadingModel read FThreadingModel;
+ end;
+ {$IFDEF NOWARNINGS}
+ {$WARNINGS ON}
+ {$ENDIF}
+
+{ COM objects intended to be aggregated / contained }
+
+ TAggregatedObject = class
+ private
+ FController: Pointer;
+ function GetController: IUnknown;
+ protected
+ { IUnknown }
+ function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
+ function _AddRef: Integer; stdcall;
+ function _Release: Integer; stdcall;
+ public
+ constructor Create(Controller: IUnknown);
+ property Controller: IUnknown read GetController;
+ end;
+
+ TContainedObject = class(TAggregatedObject, IUnknown)
+ protected
+ { IUnknown }
+ function QueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall;
+ end;
+
+{ COM object with type information }
+
+ TTypedComObject = class(TComObject, IProvideClassInfo)
+ protected
+ { IProvideClassInfo }
+ function GetClassInfo(out TypeInfo: ITypeInfo): HResult; stdcall;
+ end;
+
+ TTypedComClass = class of TTypedComObject;
+
+ {$IFDEF NOWARNINGS}
+ {$WARNINGS OFF}
+ {$ENDIF}
+ TTypedComObjectFactory = class(TComObjectFactory)
+ private
+ FClassInfo: ITypeInfo;
+ public
+ constructor Create(ComServer: TComServerObject;
+ TypedComClass: TTypedComClass; const ClassID: TGUID;
+ Instancing: TClassInstancing; ThreadingModel: TThreadingModel {= tmSingle} );
+ function GetInterfaceTypeInfo(TypeFlags: Integer): ITypeInfo;
+ procedure UpdateRegistry(Register: Boolean); override;
+ property ClassInfo: ITypeInfo read FClassInfo;
+ end;
+ {$IFDEF NOWARNINGS}
+ {$WARNINGS ON}
+ {$ENDIF}
+
+{ OLE Automation object }
+
+ TConnectEvent = procedure (const Sink: IUnknown; Connecting: Boolean) of object;
+
+ TAutoObjectFactory = class;
+
+ TAutoObject = class(TTypedComObject, IDispatch)
+ private
+ FEventSink: IUnknown;
+ FAutoFactory: TAutoObjectFactory;
+ protected
+ { IDispatch }
+ function GetIDsOfNames(const IID: TGUID; Names: Pointer;
+ NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; virtual; stdcall;
+ function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; virtual; stdcall;
+ function GetTypeInfoCount(out Count: Integer): HResult; virtual; stdcall;
+ function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
+ Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; virtual; stdcall;
+ { Other methods }
+ procedure EventConnect(const Sink: IUnknown; Connecting: Boolean);
+ procedure EventSinkChanged(const EventSink: IUnknown); virtual;
+ property AutoFactory: TAutoObjectFactory read FAutoFactory;
+ property EventSink: IUnknown read FEventSink write FEventSink;
+ public
+ procedure Initialize; override;
+ end;
+
+{ OLE Automation class }
+
+ TAutoClass = class of TAutoObject;
+
+{ OLE Automation object factory }
+
+ TAutoObjectFactory = class(TTypedComObjectFactory)
+ private
+ FDispTypeInfo: ITypeInfo;
+ FDispIntfEntry: PInterfaceEntry;
+ FEventIID: TGUID;
+ FEventTypeInfo: ITypeInfo;
+ public
+ constructor Create(ComServer: TComServerObject; AutoClass: TAutoClass;
+ const ClassID: TGUID; Instancing: TClassInstancing;
+ ThreadingModel: TThreadingModel {= tmSingle} );
+ function GetIntfEntry(Guid: TGUID): PInterfaceEntry; virtual;
+ property DispIntfEntry: PInterfaceEntry read FDispIntfEntry;
+ property DispTypeInfo: ITypeInfo read FDispTypeInfo;
+ property EventIID: TGUID read FEventIID;
+ property EventTypeInfo: ITypeInfo read FEventTypeInfo;
+ end;
+
+ TAutoIntfObject = class(TInterfacedObject, IDispatch, ISupportErrorInfo)
+ private
+ FDispTypeInfo: ITypeInfo;
+ FDispIntfEntry: PInterfaceEntry;
+ FDispIID: TGUID;
+ protected
+ { IDispatch }
+ function GetIDsOfNames(const IID: TGUID; Names: Pointer;
+ NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
+ function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
+ function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
+ function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
+ Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
+ { ISupportErrorInfo }
+ function InterfaceSupportsErrorInfo(const iid: TIID): HResult; stdcall;
+ public
+ constructor Create(const TypeLib: ITypeLib; const DispIntf: TGUID);
+ function SafeCallException(ExceptObject: TObject;
+ ExceptAddr: Pointer): HResult; override;
+ property DispIntfEntry: PInterfaceEntry read FDispIntfEntry;
+ property DispTypeInfo: ITypeInfo read FDispTypeInfo;
+ property DispIID: TGUID read FDispIID;
+ end;
+
+{ OLE exception classes }
+
+ EOleError = Exception; // class(Exception);
+
+ EOleSysError = EOleError; { class(EOleError)
+ private
+ FErrorCode: HRESULT;
+ public
+ constructor Create(const Message: AnsiString; ErrorCode: HRESULT;
+ HelpContext: Integer);
+ property ErrorCode: HRESULT read FErrorCode write FErrorCode;
+ end;}
+
+ EOleException = EOleSysError; { class(EOleSysError)
+ private
+ FSource: AnsiString;
+ FHelpFile: AnsiString;
+ public
+ constructor Create(const Message: AnsiString; ErrorCode: HRESULT;
+ const Source, HelpFile: AnsiString; HelpContext: Integer);
+ property HelpFile: AnsiString read FHelpFile write FHelpFile;
+ property Source: AnsiString read FSource write FSource;
+ end;}
+
+ EOleRegistrationError = EOleError; { class(EOleError);}
+
+ { Dispatch call descriptor }
+
+ PCallDesc = ^TCallDesc;
+ TCallDesc = packed record
+ CallType: Byte;
+ ArgCount: Byte;
+ NamedArgCount: Byte;
+ ArgTypes: array[0..255] of Byte;
+ end;
+
+ PDispDesc = ^TDispDesc;
+ TDispDesc = packed record
+ DispID: Integer;
+ ResType: Byte;
+ CallDesc: TCallDesc;
+ end;
+
+procedure DispatchInvoke(const Dispatch: IDispatch; CallDesc: PCallDesc;
+ DispIDs: PDispIDList; Params: Pointer; Result: PVariant);
+procedure DispatchInvokeError(Status: Integer; const ExcepInfo: TExcepInfo);
+
+{function HandleSafeCallException(ExceptObject: TObject;
+ ExceptAddr: Pointer; const ErrorIID: TGUID; const ProgID,
+ HelpFileName: WideString): HResult;}
+
+function CreateComObject(const ClassID: TGUID): IUnknown;
+function CreateRemoteComObject(const MachineName: WideString; const ClassID: TGUID): IUnknown;
+function CreateOleObject(const ClassName: AnsiString): IDispatch;
+function GetActiveOleObject(const ClassName: AnsiString): IDispatch;
+
+procedure OleError(ErrorCode: HResult);
+procedure OleCheck(Result: HResult);
+
+function StringToGUID(const S: AnsiString): TGUID;
+function GUIDToString(const ClassID: TGUID): AnsiString;
+
+function ProgIDToClassID(const ProgID: AnsiString): TGUID;
+function ClassIDToProgID(const ClassID: TGUID): AnsiString;
+
+procedure CreateRegKey(const Key, ValueName, Value: KOLstring);
+procedure DeleteRegKey(const Key: KOLstring);
+function GetRegStringValue(const Key, ValueName: KOLstring): KOLstring;
+
+function StringToLPOLESTR(const Source: KOLstring): POleStr;
+
+procedure RegisterComServer(const DLLName: KOLstring);
+procedure RegisterAsService(const ClassID, ServiceName: KOLstring);
+
+function CreateClassID: KOLstring;
+
+procedure InterfaceConnect(const Source: IUnknown; const IID: TIID;
+ const Sink: IUnknown; var Connection: Longint);
+procedure InterfaceDisconnect(const Source: IUnknown; const IID: TIID;
+ var Connection: Longint);
+
+type
+ TCoCreateInstanceExProc = function (const clsid: TCLSID;
+ unkOuter: IUnknown; dwClsCtx: Longint; ServerInfo: PCoServerInfo;
+ dwCount: Longint; rgmqResults: PMultiQIArray): HResult stdcall;
+ TCoInitializeExProc = function (pvReserved: Pointer;
+ coInit: Longint): HResult; stdcall;
+ TCoAddRefServerProcessProc = function :Longint; stdcall;
+ TCoReleaseServerProcessProc = function :Longint; stdcall;
+ TCoResumeClassObjectsProc = function :HResult; stdcall;
+ TCoSuspendClassObjectsProc = function :HResult; stdcall;
+
+// COM functions that are only available on DCOM updated OSs
+// These pointers may be nil on Win95 or Win NT 3.51 systems
+var
+ CoCreateInstanceEx: TCoCreateInstanceExProc = nil;
+ CoInitializeEx: TCoInitializeExProc = nil;
+ CoAddRefServerProcess: TCoAddRefServerProcessProc = nil;
+ CoReleaseServerProcess: TCoReleaseServerProcessProc = nil;
+ CoResumeClassObjects: TCoResumeClassObjectsProc = nil;
+ CoSuspendClassObjects: TCoSuspendClassObjectsProc = nil;
+
+
+{ CoInitFlags determines the COM threading model of the application or current
+ thread. This bitflag value is passed to CoInitializeEx in ComServ initialization.
+ Assign COINIT_APARTMENTTHREADED or COINIT_MULTITHREADED to this variable before
+ Application.Initialize is called by the project source file to select a
+ threading model. Other CoInitializeEx flags (such as COINIT_SPEED_OVER_MEMORY)
+ can be OR'd in also. }
+var
+ CoInitFlags: Integer = -1; // defaults to no threading model, call CoInitialize()
+
+function ComClassManager: TComClassManager;
+
+const
+ GUID_NULL: TGUID = '{00000000-0000-0000-0000-000000000000}';
+
+implementation
+
+resourcestring
+ SCreateRegKeyError = 'Error creating system registry entry';
+ SOleError = 'OLE error %.8x';
+ SObjectFactoryMissing = 'Object factory for class %s missing';
+ STypeInfoMissing = 'Type information missing for class %s';
+ SBadTypeInfo = 'Incorrect type information for class %s';
+ SDispIntfMissing = 'Dispatch interface missing from class %s';
+ SNoMethod = 'Method ''%s'' not supported by automation object';
+ SVarNotObject = 'Variant does not reference an automation object';
+ SDCOMNotInstalled = 'DCOM not installed';
+ SDAXError = 'DAX Error';
+
+ SAutomationWarning = 'COM Server Warning';
+ SNoCloseActiveServer1 = 'There are still active COM objects in this ' +
+ 'application. One or more clients may have references to these objects, ' +
+ 'so manually closing ';
+ SNoCloseActiveServer2 = 'this application may cause those client ' +
+ 'application(s) to fail.'#13#10#13#10'Are you sure you want to close this ' +
+ 'application?';
+
+var
+ OleUninitializing: Boolean;
+
+{ Handle a safe call exception }
+
+{function HandleSafeCallException(ExceptObject: TObject;
+ ExceptAddr: Pointer; const ErrorIID: TGUID; const ProgID,
+ HelpFileName: WideString): HResult;
+var
+ E: TObject;
+ CreateError: ICreateErrorInfo;
+ ErrorInfo: IErrorInfo;
+begin
+ Result := E_UNEXPECTED;
+ E := ExceptObject;
+ if Succeeded(CreateErrorInfo(CreateError)) then
+ begin
+ CreateError.SetGUID(ErrorIID);
+ if ProgID <> '' then CreateError.SetSource(PWideChar(ProgID));
+ if HelpFileName <> '' then CreateError.SetHelpFile(PWideChar(HelpFileName));
+ if E is Exception then
+ begin
+ CreateError.SetDescription(PWideChar(WideString(Exception(E).Message)));
+ CreateError.SetHelpContext(Exception(E).HelpContext);
+ if (E is EOleSysError) and (EOleSysError(E).ErrorCode < 0) then
+ Result := EOleSysError(E).ErrorCode;
+ end;
+ if CreateError.QueryInterface(IErrorInfo, ErrorInfo) = S_OK then
+ SetErrorInfo(0, ErrorInfo);
+ end;
+end;}
+
+{ TDispatchSilencer }
+
+type
+ TDispatchSilencer = class(TInterfacedObject, IUnknown, IDispatch)
+ private
+ Dispatch: IDispatch;
+ DispIntfIID: TGUID;
+ public
+ constructor Create(ADispatch: IUnknown; const ADispIntfIID: TGUID);
+ { IUnknown }
+ function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
+ { IDispatch }
+ function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
+ function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
+ function GetIDsOfNames(const IID: TGUID; Names: Pointer;
+ NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
+ function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
+ Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
+ end;
+
+constructor TDispatchSilencer.Create(ADispatch: IUnknown;
+ const ADispIntfIID: TGUID);
+begin
+ inherited Create;
+ DispIntfIID := ADispIntfIID;
+ OleCheck(ADispatch.QueryInterface(ADispIntfIID, Dispatch));
+end;
+
+function TDispatchSilencer.QueryInterface(const IID: TGUID; out Obj): HResult;
+begin
+ Result := inherited QueryInterface(IID, Obj);
+ if Result = E_NOINTERFACE then
+ if IsEqualGUID(IID, DispIntfIID) then
+ begin
+ IDispatch(Obj) := Self;
+ Result := S_OK;
+ end
+ else
+ Result := Dispatch.QueryInterface(IID, Obj);
+end;
+
+function TDispatchSilencer.GetTypeInfoCount(out Count: Integer): HResult;
+begin
+ Result := Dispatch.GetTypeInfoCount(Count);
+end;
+
+function TDispatchSilencer.GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult;
+begin
+ Result := Dispatch.GetTypeInfo(Index, LocaleID, TypeInfo);
+end;
+
+function TDispatchSilencer.GetIDsOfNames(const IID: TGUID; Names: Pointer;
+ NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
+begin
+ Result := Dispatch.GetIDsOfNames(IID, Names, NameCount, LocaleID, DispIDs);
+end;
+
+function TDispatchSilencer.Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
+ Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult;
+begin
+ { Ignore error since some containers, such as Internet Explorer 3.0x, will
+ return error when the method was not handled, or scripting errors occur }
+ Dispatch.Invoke(DispID, IID, LocaleID, Flags, Params, VarResult, ExcepInfo,
+ ArgErr);
+ Result := S_OK;
+end;
+
+{$IFNDEF _D2orD3}
+{ TMultiReadExclusiveWriteSynchronizer }
+
+constructor TMultiReadExclusiveWriteSynchronizer.Create;
+begin
+ inherited Create;
+ InitializeCriticalSection(FLock);
+ FReadExit := CreateEvent(nil, True, True, nil); // manual reset, start signaled
+ SetLength(FActiveThreads, 4);
+end;
+
+destructor TMultiReadExclusiveWriteSynchronizer.Destroy;
+begin
+ BeginWrite;
+ inherited Destroy;
+ CloseHandle(FReadExit);
+ DeleteCriticalSection(FLock);
+end;
+
+function TMultiReadExclusiveWriteSynchronizer.WriterIsOnlyReader: Boolean;
+var
+ I, Len: Integer;
+begin
+ Result := False;
+ if FWriteRequestorID = 0 then Exit;
+ // We know a writer is waiting for entry with the FLock locked,
+ // so FActiveThreads is stable - no BeginRead could be resizing it now
+ I := 0;
+ Len := High(FActiveThreads);
+ while (I < Len) and
+ ((FActiveThreads[I].ThreadID = 0) or (FActiveThreads[I].ThreadID = FWriteRequestorID)) do
+ Inc(I);
+ Result := I >= Len;
+end;
+
+procedure TMultiReadExclusiveWriteSynchronizer.BeginWrite;
+begin
+ EnterCriticalSection(FLock); // Block new read or write ops from starting
+ if not FWriting then
+ begin
+ FWriteRequestorID := GetCurrentThreadID; // Indicate that writer is waiting for entry
+ if not WriterIsOnlyReader then // See if any other thread is reading
+ WaitForSingleObject(FReadExit, INFINITE); // Wait for current readers to finish
+ FSaveReadCount := FCount; // record prior read recursions for this thread
+ FCount := 0;
+ FWriteRequestorID := 0;
+ FWriting := True;
+ end;
+ Inc(FCount); // allow read recursions during write without signalling FReadExit event
+end;
+
+procedure TMultiReadExclusiveWriteSynchronizer.EndWrite;
+begin
+ Dec(FCount);
+ if FCount = 0 then
+ begin
+ FCount := FSaveReadCount; // restore read recursion count
+ FSaveReadCount := 0;
+ FWriting := False;
+ end;
+ LeaveCriticalSection(FLock);
+end;
+
+procedure TMultiReadExclusiveWriteSynchronizer.BeginRead;
+var
+ I: Integer;
+ ThreadID: Integer;
+ ZeroSlot: Integer;
+ AlreadyInRead: Boolean;
+begin
+ ThreadID := GetCurrentThreadID;
+ // First, do a lightweight check to see if this thread already has a read lock
+ while InterlockedExchange(FReallocFlag, ThreadID) <> 0 do Sleep(0);
+ try // FActiveThreads array is now stable
+ I := 0;
+ while (I < High(FActiveThreads)) and (FActiveThreads[I].ThreadID <> ThreadID) do
+ Inc(I);
+ AlreadyInRead := I < High(FActiveThreads);
+ if AlreadyInRead then // This thread already has a read lock
+ begin // Don't grab FLock, since that could deadlock with
+ if not FWriting then // a waiting BeginWrite
+ begin // Bump up ref counts and exit
+ InterlockedIncrement(FCount);
+ Inc(FActiveThreads[I].RecursionCount); // thread safe = unique to threadid
+ end;
+ end
+ finally
+ FReallocFlag := 0;
+ end;
+ if not AlreadyInRead then
+ begin // Ok, we don't already have a lock, so do the hard work of making one
+ EnterCriticalSection(FLock);
+ try
+ if not FWriting then
+ begin
+ // This will call ResetEvent more than necessary on win95, but still work
+ if InterlockedIncrement(FCount) = 1 then
+ ResetEvent(FReadExit); // Make writer wait until all readers are finished.
+ I := 0; // scan for empty slot in activethreads list
+ ZeroSlot := -1;
+ while (I < High(FActiveThreads)) and (FActiveThreads[I].ThreadID <> ThreadID) do
+ begin
+ if (FActiveThreads[I].ThreadID = 0) and (ZeroSlot < 0) then ZeroSlot := I;
+ Inc(I);
+ end;
+ if I >= High(FActiveThreads) then // didn't find our threadid slot
+ begin
+ if ZeroSlot < 0 then // no slots available. Grow array to make room
+ begin // spin loop. wait for EndRead to put zero back into FReallocFlag
+ while InterlockedExchange(FReallocFlag, ThreadID) <> 0 do Sleep(0);
+ try
+ SetLength(FActiveThreads, High(FActiveThreads) + 3);
+ finally
+ FReallocFlag := 0;
+ end;
+ end
+ else // use an empty slot
+ I := ZeroSlot;
+ // no concurrency issue here. We're the only thread interested in this record.
+ FActiveThreads[I].ThreadID := ThreadID;
+ FActiveThreads[I].RecursionCount := 1;
+ end
+ else // found our threadid slot.
+ Inc(FActiveThreads[I].RecursionCount); // thread safe = unique to threadid
+ end;
+ finally
+ LeaveCriticalSection(FLock);
+ end;
+ end;
+end;
+
+procedure TMultiReadExclusiveWriteSynchronizer.EndRead;
+var
+ I, ThreadID, Len: Integer;
+begin
+ if not FWriting then
+ begin
+ // Remove our threadid from the list of active threads
+ I := 0;
+ ThreadID := GetCurrentThreadID;
+ // wait for BeginRead to finish any pending realloc of FActiveThreads
+ while InterlockedExchange(FReallocFlag, ThreadID) <> 0 do Sleep(0);
+ try
+ Len := High(FActiveThreads);
+ while (I < Len) and (FActiveThreads[I].ThreadID <> ThreadID) do Inc(I);
+ assert(I < Len);
+ // no concurrency issues here. We're the only thread interested in this record.
+ Dec(FActiveThreads[I].RecursionCount); // threadsafe = unique to threadid
+ if FActiveThreads[I].RecursionCount = 0 then
+ FActiveThreads[I].ThreadID := 0; // must do this last!
+ finally
+ FReallocFlag := 0;
+ end;
+ if (InterlockedDecrement(FCount) = 0) or WriterIsOnlyReader then
+ SetEvent(FReadExit); // release next writer
+ end;
+end;
+
+procedure FreeAndNil(var Obj);
+var
+ P: TObject;
+begin
+ P := TObject(Obj);
+ TObject(Obj) := nil; // clear the reference before destroying the object
+ P.Free;
+end;
+{$ENDIF}
+
+{ TComClassManager }
+constructor TComClassManager.Create;
+begin
+ inherited Create;
+ {$IFNDEF _D2orD3}
+ FLock := TMultiReadExclusiveWriteSynchronizer.Create;
+ {$ENDIF}
+end;
+
+destructor TComClassManager.Destroy;
+begin
+ {$IFNDEF _D2orD3}
+ FLock.Free;
+ {$ENDIF}
+ inherited Destroy;
+end;
+
+procedure TComClassManager.AddObjectFactory(Factory: TComObjectFactory);
+begin
+ {$IFNDEF _D2orD3}
+ FLock.BeginWrite;
+ try
+ {$ENDIF}
+ Factory.FNext := FFactoryList;
+ FFactoryList := Factory;
+ {$IFNDEF _D2orD3}
+ finally
+ FLock.EndWrite;
+ end;
+ {$ENDIF}
+end;
+
+procedure TComClassManager.ForEachFactory(ComServer: TComServerObject;
+ FactoryProc: TFactoryProc);
+var
+ Factory, Next: TComObjectFactory;
+begin
+ {$IFNDEF _D2orD3}
+ FLock.BeginWrite; // FactoryProc could add or delete factories from list
+ try
+ {$ENDIF}
+ Factory := FFactoryList;
+ while Factory <> nil do
+ begin
+ Next := Factory.FNext;
+ if Factory.ComServer = ComServer then FactoryProc(Factory);
+ Factory := Next;
+ end;
+ {$IFNDEF _D2orD3}
+ finally
+ FLock.EndWrite;
+ end;
+ {$ENDIF}
+end;
+
+function TComClassManager.GetFactoryFromClass(ComClass: TClass): TComObjectFactory;
+begin
+ {$IFNDEF _D2orD3}
+ FLock.BeginRead;
+ try
+ {$ENDIF}
+ Result := FFactoryList;
+ while Result <> nil do
+ begin
+ if Result.ComClass = ComClass then Exit;
+ Result := Result.FNext;
+ end;
+ raise EOleError.CreateResFmt(e_Ole, Integer( @SObjectFactoryMissing ), [ComClass.ClassName]);
+ {$IFNDEF _D2orD3}
+ finally
+ FLock.EndRead;
+ end;
+ {$ENDIF}
+end;
+
+function TComClassManager.GetFactoryFromClassID(const ClassID: TGUID): TComObjectFactory;
+begin
+ {$IFNDEF _D2orD3}
+ FLock.BeginRead;
+ try
+ {$ENDIF}
+ Result := FFactoryList;
+ while Result <> nil do
+ begin
+ if IsEqualGUID(Result.ClassID, ClassID) then Exit;
+ Result := Result.FNext;
+ end;
+ {$IFNDEF _D2orD3}
+ finally
+ FLock.EndRead;
+ end;
+ {$ENDIF}
+end;
+
+procedure TComClassManager.RemoveObjectFactory(Factory: TComObjectFactory);
+var
+ F, P: TComObjectFactory;
+begin
+ {$IFNDEF _D2orD3}
+ FLock.BeginWrite;
+ try
+ {$ENDIF}
+ P := nil;
+ F := FFactoryList;
+ while F <> nil do
+ begin
+ if F = Factory then
+ begin
+ if P <> nil then P.FNext := F.FNext else FFactoryList := F.FNext;
+ Exit;
+ end;
+ P := F;
+ F := F.FNext;
+ end;
+ {$IFNDEF _D2orD3}
+ finally
+ FLock.EndWrite;
+ end;
+ {$ENDIF}
+end;
+
+{ TComObject }
+
+constructor TComObject.Create;
+begin
+ FNonCountedObject := True;
+ CreateFromFactory(ComClassManager.GetFactoryFromClass(ClassType), nil);
+end;
+
+constructor TComObject.CreateAggregated(const Controller: IUnknown);
+begin
+ FNonCountedObject := True;
+ CreateFromFactory(ComClassManager.GetFactoryFromClass(ClassType), Controller);
+end;
+
+constructor TComObject.CreateFromFactory(Factory: TComObjectFactory;
+ const Controller: IUnknown);
+begin
+ FRefCount := 1;
+ FFactory := Factory;
+ FController := Pointer(Controller);
+ if not FNonCountedObject then FFactory.ComServer.CountObject(True);
+ Initialize;
+ Dec(FRefCount);
+end;
+
+destructor TComObject.Destroy;
+begin
+ if not OleUninitializing then
+ begin
+ if (FFactory <> nil) and not FNonCountedObject then
+ FFactory.ComServer.CountObject(False);
+ if FRefCount > 0 then CoDisconnectObject(Self, 0);
+ end;
+end;
+
+function TComObject.GetController: IUnknown;
+begin
+ Result := IUnknown(FController);
+end;
+
+procedure TComObject.Initialize;
+begin
+end;
+
+function TComObject.SafeCallException(ExceptObject: TObject;
+ ExceptAddr: Pointer): HResult;
+var
+ Msg: AnsiString;
+ Handled: Integer;
+begin
+ Handled := 0;
+ if ServerExceptionHandler <> nil then
+ begin
+ if ExceptObject is Exception then
+ Msg := Exception(ExceptObject).Message;
+ Result := 0;
+ ServerExceptionHandler.OnException(ClassName,
+ ExceptObject.ClassName, Msg, Integer(ExceptAddr),
+ WideString(GUIDToString(FFactory.ErrorIID)),
+ FFactory.ProgID, Handled, Result);
+ end;
+ if Handled = 0 then
+ {Result := HandleSafeCallException(ExceptObject, ExceptAddr,
+ FFactory.ErrorIID, FFactory.ProgID, FFactory.ComServer.HelpFileName);}
+end;
+
+{ TComObject.IUnknown }
+
+function TComObject.ObjQueryInterface(const IID: TGUID; out Obj): HResult;
+begin
+ if GetInterface(IID, Obj) then Result := S_OK else Result := E_NOINTERFACE;
+end;
+
+function TComObject.ObjAddRef: Integer;
+begin
+ Result := InterlockedIncrement(FRefCount);
+end;
+
+function TComObject.ObjRelease: Integer;
+begin
+ // InterlockedDecrement returns only 0 or 1 on Win95 and NT 3.51
+ // returns actual result on NT 4.0
+ Result := InterlockedDecrement(FRefCount);
+ if Result = 0 then Destroy;
+end;
+
+{ TComObject.IUnknown for other interfaces }
+
+function TComObject.QueryInterface(const IID: TGUID; out Obj): HResult;
+begin
+ if FController <> nil then
+ Result := IUnknown(FController).QueryInterface(IID, Obj) else
+ Result := ObjQueryInterface(IID, Obj);
+end;
+
+function TComObject._AddRef: Integer;
+begin
+ if FController <> nil then
+ Result := IUnknown(FController)._AddRef else
+ Result := ObjAddRef;
+end;
+
+function TComObject._Release: Integer;
+begin
+ if FController <> nil then
+ Result := IUnknown(FController)._Release else
+ Result := ObjRelease;
+end;
+
+{ TComObject.ISupportErrorInfo }
+
+function TComObject.InterfaceSupportsErrorInfo(const iid: TIID): HResult;
+begin
+ if GetInterfaceEntry(iid) <> nil then
+ Result := S_OK else
+ Result := S_FALSE;
+end;
+
+{ TComObjectFactory }
+
+constructor TComObjectFactory.Create(ComServer: TComServerObject;
+ ComClass: TComClass; const ClassID: TGUID; const ClassName,
+ Description: AnsiString; Instancing: TClassInstancing;
+ ThreadingModel: TThreadingModel);
+begin
+ IsMultiThread := IsMultiThread or (ThreadingModel <> tmSingle);
+ if ThreadingModel in [tmFree, tmBoth] then
+ CoInitFlags := COINIT_MULTITHREADED else
+ if (ThreadingModel = tmApartment) and (CoInitFlags <> COINIT_MULTITHREADED) then
+ CoInitFlags := COINIT_APARTMENTTHREADED;
+ ComClassManager.AddObjectFactory(Self);
+ FComServer := ComServer;
+ FComClass := ComClass;
+ FClassID := ClassID;
+ FClassName := ClassName;
+ FDescription := Description;
+ FInstancing := Instancing;
+ FErrorIID := IUnknown;
+ FShowErrors := True;
+ FThreadingModel := ThreadingModel;
+ FRegister := -1;
+end;
+
+destructor TComObjectFactory.Destroy;
+begin
+ if FRegister <> -1 then CoRevokeClassObject(FRegister);
+ ComClassManager.RemoveObjectFactory(Self);
+end;
+
+function TComObjectFactory.CreateComObject(const Controller: IUnknown): TComObject;
+begin
+ Result := TComClass(FComClass).CreateFromFactory(Self, Controller);
+end;
+
+function TComObjectFactory.GetProgID: AnsiString;
+begin
+ if FClassName <> '' then
+ Result := FComServer.ServerName + '.' + FClassName else
+ Result := '';
+end;
+
+procedure TComObjectFactory.RegisterClassObject;
+const
+ RegFlags: array[ciSingleInstance..ciMultiInstance] of Integer = (
+ REGCLS_SINGLEUSE, REGCLS_MULTIPLEUSE);
+ SuspendedFlag: array[Boolean] of Integer = (0, REGCLS_SUSPENDED);
+begin
+ if FInstancing <> ciInternal then
+ OleCheck(CoRegisterClassObject(FClassID, Self, CLSCTX_LOCAL_SERVER,
+ RegFlags[FInstancing] or SuspendedFlag[FComServer.StartSuspended], FRegister));
+end;
+
+procedure TComObjectFactory.UpdateRegistry(Register: Boolean);
+const
+ ThreadStrs: array[TThreadingModel] of AnsiString =
+ ('', 'Apartment', 'Free', 'Both');
+var
+ ClassID, ProgID, ServerKeyName, ShortFileName: AnsiString;
+begin
+ if FInstancing = ciInternal then Exit;
+ ClassID := GUIDToString(FClassID);
+ ProgID := GetProgID;
+ ServerKeyName := 'CLSID\' + ClassID + '\' + FComServer.ServerKey;
+ if Register then
+ begin
+ CreateRegKey('CLSID\' + ClassID, '', Description);
+ ShortFileName := FComServer.ServerFileName;
+ if {Ansi}Pos(' ', ShortFileName) <> 0 then
+ ShortFileName := ExtractShortPathName(ShortFileName);
+ CreateRegKey(ServerKeyName, '', ShortFileName);
+ if (FThreadingModel <> tmSingle) and IsLibrary then
+ CreateRegKey(ServerKeyName, 'ThreadingModel', ThreadStrs[FThreadingModel]);
+ if ProgID <> '' then
+ begin
+ CreateRegKey(ProgID, '', Description);
+ CreateRegKey(ProgID + '\Clsid', '', ClassID);
+ CreateRegKey('CLSID\' + ClassID + '\ProgID', '', ProgID);
+ end;
+ end else
+ begin
+ if ProgID <> '' then
+ begin
+ DeleteRegKey('CLSID\' + ClassID + '\ProgID');
+ DeleteRegKey(ProgID + '\Clsid');
+ DeleteRegKey(ProgID);
+ end;
+ DeleteRegKey(ServerKeyName);
+ DeleteRegKey('CLSID\' + ClassID);
+ end;
+end;
+
+function TComObjectFactory.GetLicenseString: WideString;
+begin
+ if FSupportsLicensing then Result := FLicString
+ else Result := '';
+end;
+
+function TComObjectFactory.HasMachineLicense: Boolean;
+begin
+ Result := True;
+end;
+
+function TComObjectFactory.ValidateUserLicense(const LicStr: WideString): Boolean;
+begin
+ Result := AnsiCompareText(LicStr, FLicString) = 0;
+end;
+
+{ TComObjectFactory.IUnknown }
+
+function TComObjectFactory.QueryInterface(const IID: TGUID; out Obj): HResult;
+begin
+ if GetInterface(IID, Obj) then Result := S_OK else Result := E_NOINTERFACE;
+end;
+
+function TComObjectFactory._AddRef: Integer;
+begin
+ Result := ComServer.CountFactory(True);
+end;
+
+function TComObjectFactory._Release: Integer;
+begin
+ Result := ComServer.CountFactory(False);
+end;
+
+{ TComObjectFactory.IClassFactory }
+
+function TComObjectFactory.CreateInstance(const UnkOuter: IUnknown;
+ const IID: TGUID; out Obj): HResult;
+begin
+ Result := CreateInstanceLic(UnkOuter, nil, IID, '', Obj);
+end;
+
+function TComObjectFactory.LockServer(fLock: BOOL): HResult;
+begin
+ Result := CoLockObjectExternal(Self, fLock, True);
+ // Keep com server alive until this class factory is unlocked
+ ComServer.CountObject(fLock);
+end;
+
+{ TComObjectFactory.IClassFactory2 }
+
+function TComObjectFactory.GetLicInfo(var licInfo: TLicInfo): HResult;
+begin
+ Result := S_OK;
+ try
+ with licInfo do
+ begin
+ cbLicInfo := SizeOf(licInfo);
+ fRuntimeKeyAvail := (not FSupportsLicensing) or (GetLicenseString <> '');
+ fLicVerified := (not FSupportsLicensing) or HasMachineLicense;
+ end;
+ except
+ Result := E_UNEXPECTED;
+ end;
+end;
+
+function TComObjectFactory.RequestLicKey(dwResrved: Longint; out bstrKey: WideString): HResult;
+begin
+ // Can't give away a license key on an unlicensed machine
+ if not HasMachineLicense then
+ begin
+ Result := CLASS_E_NOTLICENSED;
+ Exit;
+ end;
+ bstrKey := FLicString;
+ Result := NOERROR;
+end;
+
+function TComObjectFactory.CreateInstanceLic(const unkOuter: IUnknown;
+ const unkReserved: IUnknown; const iid: TIID; const bstrKey: WideString;
+ out vObject): HResult; stdcall;
+var
+ ComObject: TComObject;
+begin
+ // We can't write to a nil pointer. Duh.
+ if @vObject = nil then
+ begin
+ Result := E_POINTER;
+ Exit;
+ end;
+ // In case of failure, make sure we return at least a nil interface.
+ Pointer(vObject) := nil;
+ // Check for licensing.
+ if FSupportsLicensing and
+ ((bstrKey <> '') and (not ValidateUserLicense(bstrKey))) or
+ ((bstrKey = '') and (not HasMachineLicense)) then
+ begin
+ Result := CLASS_E_NOTLICENSED;
+ Exit;
+ end;
+ // We can only aggregate if they are requesting our IUnknown.
+ if (unkOuter <> nil) and not (IsEqualIID(iid, IUnknown)) then
+ begin
+ Result := CLASS_E_NOAGGREGATION;
+ Exit;
+ end;
+ try
+ ComObject := CreateComObject(UnkOuter);
+ except
+ if FShowErrors and (ExceptObject is Exception) then
+ with Exception(ExceptObject) do
+ begin
+ {if (Message <> '') and (AnsiLastChar(Message) > '.') then
+ Message := Message + '.';}
+ MessageBox(0, PKOLChar(Message), PKOLChar(KOLString( SDAXError )), MB_OK or MB_ICONSTOP or
+ MB_SETFOREGROUND);
+ end;
+ Result := E_UNEXPECTED;
+ Exit;
+ end;
+ Result := ComObject.ObjQueryInterface(IID, vObject);
+ if ComObject.RefCount = 0 then ComObject.Free;
+end;
+
+{ TAggregatedObject }
+
+constructor TAggregatedObject.Create(Controller: IUnknown);
+begin
+ FController := Pointer(Controller);
+end;
+
+function TAggregatedObject.GetController: IUnknown;
+begin
+ Result := IUnknown(FController);
+end;
+
+{ TAggregatedObject.IUnknown }
+
+function TAggregatedObject.QueryInterface(const IID: TGUID; out Obj): HResult;
+begin
+ Result := IUnknown(FController).QueryInterface(IID, Obj);
+end;
+
+function TAggregatedObject._AddRef: Integer;
+begin
+ Result := IUnknown(FController)._AddRef;
+end;
+
+function TAggregatedObject._Release: Integer; stdcall;
+begin
+ Result := IUnknown(FController)._Release;
+end;
+
+{ TContainedObject.IUnknown }
+
+function TContainedObject.QueryInterface(const IID: TGUID; out Obj): HResult;
+begin
+ if GetInterface(IID, Obj) then Result := S_OK else Result := E_NOINTERFACE;
+end;
+
+{ TTypedComObject.IProvideClassInfo }
+
+function TTypedComObject.GetClassInfo(out TypeInfo: ITypeInfo): HResult;
+begin
+ TypeInfo := TTypedComObjectFactory(FFactory).FClassInfo;
+ Result := S_OK;
+end;
+
+{ TTypedComObjectFactory }
+
+constructor TTypedComObjectFactory.Create(ComServer: TComServerObject;
+ TypedComClass: TTypedComClass; const ClassID: TGUID;
+ Instancing: TClassInstancing; ThreadingModel: TThreadingModel);
+var
+ ClassName, Description: WideString;
+begin
+ if ComServer.TypeLib.GetTypeInfoOfGUID(ClassID, FClassInfo) <> S_OK then
+ raise EOleError.CreateResFmt(e_Ole, Integer(@STypeInfoMissing), [TypedComClass.ClassName]);
+ OleCheck(FClassInfo.GetDocumentation(MEMBERID_NIL, @ClassName,
+ @Description, nil, nil));
+ inherited Create(ComServer, TypedComClass, ClassID,
+ ClassName, Description, Instancing, ThreadingModel);
+end;
+
+function TTypedComObjectFactory.GetInterfaceTypeInfo(
+ TypeFlags: Integer): ITypeInfo;
+const
+ FlagsMask = IMPLTYPEFLAG_FDEFAULT or IMPLTYPEFLAG_FSOURCE;
+var
+ ClassAttr: PTypeAttr;
+ I, TypeInfoCount, Flags: Integer;
+ RefType: HRefType;
+begin
+ OleCheck(FClassInfo.GetTypeAttr(ClassAttr));
+ TypeInfoCount := ClassAttr^.cImplTypes;
+ ClassInfo.ReleaseTypeAttr(ClassAttr);
+ for I := 0 to TypeInfoCount - 1 do
+ begin
+ OleCheck(ClassInfo.GetImplTypeFlags(I, Flags));
+ if Flags and FlagsMask = TypeFlags then
+ begin
+ OleCheck(ClassInfo.GetRefTypeOfImplType(I, RefType));
+ OleCheck(ClassInfo.GetRefTypeInfo(RefType, Result));
+ Exit;
+ end;
+ end;
+ Result := nil;
+end;
+
+procedure TTypedComObjectFactory.UpdateRegistry(Register: Boolean);
+var
+ ClassKey: AnsiString;
+ TypeLib: ITypeLib;
+ TLibAttr: PTLibAttr;
+begin
+ ClassKey := 'CLSID\' + GUIDToString(FClassID);
+ if Register then
+ begin
+ inherited UpdateRegistry(Register);
+ TypeLib := FComServer.TypeLib;
+ OleCheck(TypeLib.GetLibAttr(TLibAttr));
+ try
+ CreateRegKey(ClassKey + '\Version', '', Format('%d.%d',
+ [TLibAttr.wMajorVerNum, TLibAttr.wMinorVerNum]));
+ CreateRegKey(ClassKey + '\TypeLib', '', GUIDToString(TLibAttr.guid));
+ finally
+ TypeLib.ReleaseTLibAttr(TLibAttr);
+ end;
+ end else
+ begin
+ DeleteRegKey(ClassKey + '\TypeLib');
+ DeleteRegKey(ClassKey + '\Version');
+ inherited UpdateRegistry(Register);
+ end;
+end;
+
+{ TAutoObject }
+
+procedure TAutoObject.EventConnect(const Sink: IUnknown;
+ Connecting: Boolean);
+begin
+ if Connecting then
+ begin
+ OleCheck(Sink.QueryInterface(FAutoFactory.FEventIID, FEventSink));
+ EventSinkChanged(TDispatchSilencer.Create(Sink, FAutoFactory.FEventIID));
+ end
+ else
+ begin
+ FEventSink := nil;
+ EventSinkChanged(nil);
+ end;
+end;
+
+procedure TAutoObject.EventSinkChanged(const EventSink: IUnknown);
+begin
+end;
+
+procedure TAutoObject.Initialize;
+begin
+ FAutoFactory := Factory as TAutoObjectFactory;
+ inherited Initialize;
+end;
+
+{ TAutoObject.IDispatch }
+
+function TAutoObject.GetIDsOfNames(const IID: TGUID; Names: Pointer;
+ NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
+begin
+ Result := DispGetIDsOfNames(FAutoFactory.DispTypeInfo,
+ Names, NameCount, DispIDs);
+end;
+
+function TAutoObject.GetTypeInfo(Index, LocaleID: Integer;
+ out TypeInfo): HResult;
+begin
+ Pointer(TypeInfo) := nil;
+ if Index <> 0 then
+ begin
+ Result := DISP_E_BADINDEX;
+ Exit;
+ end;
+ ITypeInfo(TypeInfo) := TAutoObjectFactory(Factory).DispTypeInfo;
+ Result := S_OK;
+end;
+
+function TAutoObject.GetTypeInfoCount(out Count: Integer): HResult;
+begin
+ Count := 1;
+ Result := S_OK;
+end;
+
+function TAutoObject.Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
+ Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult;
+const
+ INVOKE_PROPERTYSET = INVOKE_PROPERTYPUT or INVOKE_PROPERTYPUTREF;
+begin
+ if Flags and INVOKE_PROPERTYSET <> 0 then Flags := INVOKE_PROPERTYSET;
+ Result := TAutoObjectFactory(Factory).DispTypeInfo.Invoke(Pointer(
+ Integer(Self) + TAutoObjectFactory(Factory).DispIntfEntry.IOffset),
+ DispID, Flags, TDispParams(Params), VarResult, ExcepInfo, ArgErr);
+end;
+
+{ TAutoObjectFactory }
+
+constructor TAutoObjectFactory.Create(ComServer: TComServerObject;
+ AutoClass: TAutoClass; const ClassID: TGUID;
+ Instancing: TClassInstancing; ThreadingModel: TThreadingModel);
+var
+ TypeAttr: PTypeAttr;
+begin
+ inherited Create(ComServer, AutoClass, ClassID, Instancing, ThreadingModel);
+ FDispTypeInfo := GetInterfaceTypeInfo(IMPLTYPEFLAG_FDEFAULT);
+ if FDispTypeInfo = nil then
+ raise EOleError.CreateResFmt(e_Ole, Integer(@SBadTypeInfo), [AutoClass.ClassName]);
+ OleCheck(FDispTypeInfo.GetTypeAttr(TypeAttr));
+ FDispIntfEntry := GetIntfEntry(TypeAttr^.guid);
+ FDispTypeInfo.ReleaseTypeAttr(TypeAttr);
+ if FDispIntfEntry = nil then
+ raise EOleError.CreateResFmt(e_Ole, Integer(@SDispIntfMissing),
+ [AutoClass.ClassName]);
+ FErrorIID := FDispIntfEntry^.IID;
+ FEventTypeInfo := GetInterfaceTypeInfo(IMPLTYPEFLAG_FDEFAULT or
+ IMPLTYPEFLAG_FSOURCE);
+ if FEventTypeInfo <> nil then
+ begin
+ OleCheck(FEventTypeInfo.GetTypeAttr(TypeAttr));
+ FEventIID := TypeAttr.guid;
+ FEventTypeInfo.ReleaseTypeAttr(TypeAttr);
+ end;
+end;
+
+function TAutoObjectFactory.GetIntfEntry(Guid: TGUID): PInterfaceEntry;
+begin
+ Result := FComClass.GetInterfaceEntry(Guid);
+end;
+
+{ TAutoIntfObject }
+
+constructor TAutoIntfObject.Create(const TypeLib: ITypeLib; const DispIntf: TGUID);
+begin
+ inherited Create;
+ OleCheck(TypeLib.GetTypeInfoOfGuid(DispIntf, FDispTypeInfo));
+ FDispIntfEntry := GetInterfaceEntry(DispIntf);
+end;
+
+{ TAutoIntfObject.IDispatch }
+
+function TAutoIntfObject.GetIDsOfNames(const IID: TGUID; Names: Pointer;
+ NameCount, LocaleID: Integer; DispIDs: Pointer): HResult;
+begin
+ Result := DispGetIDsOfNames(FDispTypeInfo, Names, NameCount, DispIDs);
+end;
+
+function TAutoIntfObject.GetTypeInfo(Index, LocaleID: Integer;
+ out TypeInfo): HResult;
+begin
+ Pointer(TypeInfo) := nil;
+ if Index <> 0 then
+ begin
+ Result := DISP_E_BADINDEX;
+ Exit;
+ end;
+ ITypeInfo(TypeInfo) := FDispTypeInfo;
+ Result := S_OK;
+end;
+
+function TAutoIntfObject.GetTypeInfoCount(out Count: Integer): HResult;
+begin
+ Count := 1;
+ Result := S_OK;
+end;
+
+function TAutoIntfObject.Invoke(DispID: Integer; const IID: TGUID;
+ LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,
+ ArgErr: Pointer): HResult;
+const
+ INVOKE_PROPERTYSET = INVOKE_PROPERTYPUT or INVOKE_PROPERTYPUTREF;
+begin
+ if Flags and INVOKE_PROPERTYSET <> 0 then Flags := INVOKE_PROPERTYSET;
+ Result := FDispTypeInfo.Invoke(Pointer(Integer(Self) +
+ FDispIntfEntry.IOffset), DispID, Flags, TDispParams(Params), VarResult,
+ ExcepInfo, ArgErr);
+end;
+
+function TAutoIntfObject.InterfaceSupportsErrorInfo(const iid: TIID): HResult;
+begin
+ if IsEqualGUID(DispIID, iid) then
+ Result := S_OK else
+ Result := S_FALSE;
+end;
+
+function TAutoIntfObject.SafeCallException(ExceptObject: TObject;
+ ExceptAddr: Pointer): HResult;
+begin
+ Result := 0; { HandleSafeCallException(ExceptObject, ExceptAddr, DispIID, '', ''); }
+end;
+
+const
+{ Maximum number of dispatch arguments }
+
+ MaxDispArgs = 64; {!!!}
+
+{ Special variant type codes }
+
+ varStrArg = $0048;
+
+{ Parameter type masks }
+
+ atVarMask = $3F;
+ atTypeMask = $7F;
+ atByRef = $80;
+
+{function TrimPunctuation(const S: AnsiString): AnsiString;
+var
+ P: PChar;
+begin
+ Result := S;
+ P := AnsiLastChar(Result);
+ while (Length(Result) > 0) and (P^ in [#0..#32, '.']) do
+ begin
+ SetLength(Result, P - PChar(Result));
+ P := AnsiLastChar(Result);
+ end;
+end;}
+
+{ EOleSysError }
+
+{constructor EOleSysError.Create(const Message: AnsiString;
+ ErrorCode: HRESULT; HelpContext: Integer);
+var
+ S: AnsiString;
+begin
+ S := Message;
+ if S = '' then
+ begin
+ S := SysErrorMessage(ErrorCode);
+ if S = '' then FmtStr(S, SOleError, [ErrorCode]);
+ end;
+ inherited CreateHelp(S, HelpContext);
+ FErrorCode := ErrorCode;
+end;}
+
+{ EOleException }
+
+{constructor EOleException.Create(const Message: AnsiString; ErrorCode: HRESULT;
+ const Source, HelpFile: AnsiString; HelpContext: Integer);
+begin
+ inherited Create(TrimPunctuation(Message), ErrorCode, HelpContext);
+ FSource := Source;
+ FHelpFile := HelpFile;
+end;}
+
+
+{ Raise EOleSysError exception from an error code }
+
+procedure OleError(ErrorCode: HResult);
+begin
+ raise EOleSysError.Create(e_Ole, 'OLE error: ' + Int2Str( ErrorCode ) );
+end;
+
+{ Raise EOleSysError exception if result code indicates an error }
+
+procedure OleCheck(Result: HResult);
+begin
+ if not Succeeded(Result) then OleError(Result);
+end;
+
+{ Convert a AnsiString to a GUID }
+
+function StringToGUID(const S: AnsiString): TGUID;
+begin
+ OleCheck(CLSIDFromString(PWideChar(WideString(S)), Result));
+end;
+
+{ Convert a GUID to a AnsiString }
+
+function GUIDToString(const ClassID: TGUID): AnsiString;
+var
+ P: PWideChar;
+begin
+ OleCheck(StringFromCLSID(ClassID, P));
+ Result := P;
+ CoTaskMemFree(P);
+end;
+
+{ Convert a programmatic ID to a class ID }
+
+function ProgIDToClassID(const ProgID: AnsiString): TGUID;
+begin
+ OleCheck(CLSIDFromProgID(PWideChar(WideString(ProgID)), Result));
+end;
+
+{ Convert a class ID to a programmatic ID }
+
+function ClassIDToProgID(const ClassID: TGUID): AnsiString;
+var
+ P: PWideChar;
+begin
+ OleCheck(ProgIDFromCLSID(ClassID, P));
+ Result := P;
+ CoTaskMemFree(P);
+end;
+
+{ Create registry key }
+
+procedure CreateRegKey(const Key, ValueName, Value: KOLstring);
+var
+ Handle: HKey;
+ Status, Disposition: Integer;
+begin
+ Status := RegCreateKeyEx(HKEY_CLASSES_ROOT, PKOLChar(Key), 0, '',
+ REG_OPTION_NON_VOLATILE, KEY_READ or KEY_WRITE, nil, Handle,
+ @Disposition);
+ if Status = 0 then
+ begin
+ Status := RegSetValueEx(Handle, PKOLChar(ValueName), 0, REG_SZ,
+ PKOLChar(Value), Length(Value) + 1);
+ RegCloseKey(Handle);
+ end;
+ if Status <> 0 then raise EOleRegistrationError.CreateResFmt(e_Registry,
+ Integer(@SCreateRegKeyError), [ nil ] );
+end;
+
+{ Delete registry key }
+
+procedure DeleteRegKey(const Key: KOLstring);
+begin
+ RegDeleteKey(HKEY_CLASSES_ROOT, PKOLChar(Key));
+end;
+
+{ Get registry value }
+
+function GetRegStringValue(const Key, ValueName: KOLstring): KOLstring;
+var
+ Size: DWord;
+ RegKey: HKEY;
+begin
+ Result := '';
+ if RegOpenKey(HKEY_CLASSES_ROOT, PKOLChar(Key), RegKey) = ERROR_SUCCESS then
+ try
+ Size := 256;
+ SetLength(Result, Size);
+ if RegQueryValueEx(RegKey, PKOLChar(ValueName), nil, nil, PByte(PKOLChar(Result)), @Size) = ERROR_SUCCESS then
+ SetLength(Result, Size - 1) else
+ Result := '';
+ finally
+ RegCloseKey(RegKey);
+ end;
+end;
+
+function CreateComObject(const ClassID: TGUID): IUnknown;
+begin
+ OleCheck(CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or
+ CLSCTX_LOCAL_SERVER, IUnknown, Result));
+end;
+
+function CreateRemoteComObject(const MachineName: WideString;
+ const ClassID: TGUID): IUnknown;
+const
+ LocalFlags = CLSCTX_LOCAL_SERVER or CLSCTX_REMOTE_SERVER or CLSCTX_INPROC_SERVER;
+ RemoteFlags = CLSCTX_REMOTE_SERVER;
+var
+ MQI: TMultiQI;
+ ServerInfo: TCoServerInfo;
+ IID_IUnknown: TGuid;
+ Flags, Size: DWORD;
+ LocalMachine: array [0..MAX_COMPUTERNAME_LENGTH] of KOLchar;
+begin
+ if @CoCreateInstanceEx = nil then
+ raise Exception.CreateResFmt(e_Com, Integer(@SDCOMNotInstalled), [nil]);
+ FillChar(ServerInfo, sizeof(ServerInfo), 0);
+ ServerInfo.pwszName := PWideChar(MachineName);
+ IID_IUnknown := IUnknown;
+ MQI.IID := @IID_IUnknown;
+ MQI.itf := nil;
+ MQI.hr := 0;
+ { If a MachineName is specified check to see if it the local machine.
+ If it isn't, do not allow LocalServers to be used. }
+ if Length(MachineName) > 0 then
+ begin
+ Size := Sizeof(LocalMachine); // Win95 is hypersensitive to size
+ if GetComputerName(LocalMachine, Size) and
+ (AnsiCompareText(LocalMachine, MachineName) = 0) then
+ Flags := LocalFlags else
+ Flags := RemoteFlags;
+ end else
+ Flags := LocalFlags;
+ OleCheck(CoCreateInstanceEx(ClassID, nil, Flags, @ServerInfo, 1, @MQI));
+ OleCheck(MQI.HR);
+ Result := MQI.itf;
+end;
+
+function CreateOleObject(const ClassName: AnsiString): IDispatch;
+var
+ ClassID: TCLSID;
+begin
+ ClassID := ProgIDToClassID(ClassName);
+ OleCheck(CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or
+ CLSCTX_LOCAL_SERVER, IDispatch, Result));
+end;
+
+function GetActiveOleObject(const ClassName: AnsiString): IDispatch;
+var
+ ClassID: TCLSID;
+ Unknown: IUnknown;
+begin
+ ClassID := ProgIDToClassID(ClassName);
+ OleCheck(GetActiveObject(ClassID, nil, Unknown));
+ OleCheck(Unknown.QueryInterface(IDispatch, Result));
+end;
+
+function StringToLPOLESTR(const Source: KOLstring): POleStr;
+var
+ SourceLen: Integer;
+ Buffer: PWideChar;
+begin
+ SourceLen := Length(Source);
+ Buffer := CoTaskMemAlloc((SourceLen+1) * sizeof(WideChar));
+ StringToWideChar( Source, Buffer, SourceLen+1 );
+ Result := POleStr( Buffer );
+end;
+
+function CreateClassID: KOLstring;
+var
+ ClassID: TCLSID;
+ P: PWideChar;
+begin
+ CoCreateGuid(ClassID);
+ StringFromCLSID(ClassID, P);
+ Result := P;
+ CoTaskMemFree(P);
+end;
+
+procedure RegisterComServer(const DLLName: KOLstring);
+type
+ TRegProc = function: HResult; stdcall;
+const
+ RegProcName = 'DllRegisterServer'; { Do not localize }
+var
+ Handle: THandle;
+ RegProc: TRegProc;
+begin
+ {$IFDEF _D2orD3}
+ Handle := LoadLibrary( PChar( DLLName ) );
+ {$ELSE}
+ Handle := SafeLoadLibrary(DLLName);
+ {$ENDIF}
+ if Handle <= HINSTANCE_ERROR then
+ raise Exception.CreateFmt( e_Com, '%s: %s', [SysErrorMessage(GetLastError), DLLName]);
+ try
+ RegProc := GetProcAddress(Handle, RegProcName);
+ if Assigned(RegProc) then OleCheck(RegProc) else RaiseLastWin32Error;
+ finally
+ FreeLibrary(Handle);
+ end;
+end;
+
+procedure RegisterAsService(const ClassID, ServiceName: KOLstring);
+begin
+ CreateRegKey('AppID\' + ClassID, 'LocalService', ServiceName);
+ CreateRegKey('CLSID\' + ClassID, 'AppID', ClassID);
+end;
+
+{ Connect an IConnectionPoint interface }
+
+procedure InterfaceConnect(const Source: IUnknown; const IID: TIID;
+ const Sink: IUnknown; var Connection: Longint);
+var
+ CPC: IConnectionPointContainer;
+ CP: IConnectionPoint;
+begin
+ Connection := 0;
+ if Succeeded(Source.QueryInterface(IConnectionPointContainer, CPC)) then
+ if Succeeded(CPC.FindConnectionPoint(IID, CP)) then
+ CP.Advise(Sink, Connection);
+end;
+
+{ Disconnect an IConnectionPoint interface }
+
+procedure InterfaceDisconnect(const Source: IUnknown; const IID: TIID;
+ var Connection: Longint);
+var
+ CPC: IConnectionPointContainer;
+ CP: IConnectionPoint;
+begin
+ if Connection <> 0 then
+ if Succeeded(Source.QueryInterface(IConnectionPointContainer, CPC)) then
+ if Succeeded(CPC.FindConnectionPoint(IID, CP)) then
+ if Succeeded(CP.Unadvise(Connection)) then Connection := 0;
+end;
+
+procedure LoadComExProcs;
+var
+ Ole32: HModule;
+begin
+ Ole32 := GetModuleHandle('ole32.dll');
+ if Ole32 <> 0 then
+ begin
+ @CoCreateInstanceEx := GetProcAddress(Ole32, 'CoCreateInstanceEx');
+ @CoInitializeEx := GetProcAddress(Ole32, 'CoInitializeEx');
+ @CoAddRefServerProcess := GetProcAddress(Ole32, 'CoAddRefServerProcess');
+ @CoReleaseServerProcess := GetProcAddress(Ole32, 'CoReleaseServerProcess');
+ @CoResumeClassObjects := GetProcAddress(Ole32, 'CoResumeClassObjects');
+ @CoSuspendClassObjects := GetProcAddress(Ole32, 'CoSuspendClassObjects');
+ end;
+end;
+
+procedure SafeCallError(ErrorCode: Integer; ErrorAddr: Pointer);
+var
+ ErrorInfo: IErrorInfo;
+ Source, Description, HelpFile: WideString;
+ HelpContext: Longint;
+begin
+ HelpContext := 0;
+ if GetErrorInfo(0, ErrorInfo) = S_OK then
+ begin
+ ErrorInfo.GetSource(Source);
+ ErrorInfo.GetDescription(Description);
+ ErrorInfo.GetHelpFile(HelpFile);
+ ErrorInfo.GetHelpContext(HelpContext);
+ end;
+ raise EOleException.Create(e_Ole, Description + Int2Str( ErrorCode ) {, Source,
+ HelpFile, HelpContext} ) at ErrorAddr;
+end;
+
+{ Call Invoke method on the given IDispatch interface using the given
+ call descriptor, dispatch IDs, parameters, and result }
+
+procedure DispatchInvoke(const Dispatch: IDispatch; CallDesc: PCallDesc;
+ DispIDs: PDispIDList; Params: Pointer; Result: PVariant);
+type
+ PVarArg = ^TVarArg;
+ TVarArg = array[0..3] of DWORD;
+ TStringDesc = record
+ BStr: PWideChar;
+ PStr: pAnsiString;
+ end;
+var
+ I, J, K, ArgType, ArgCount, StrCount, DispID, InvKind, Status: Integer;
+ VarFlag: Byte;
+ ParamPtr: ^Integer;
+ ArgPtr, VarPtr: PVarArg;
+ DispParams: TDispParams;
+ ExcepInfo: TExcepInfo;
+ Strings: array[0..MaxDispArgs - 1] of TStringDesc;
+ Args: array[0..MaxDispArgs - 1] of TVarArg;
+begin
+ StrCount := 0;
+ try
+ ArgCount := CallDesc^.ArgCount;
+ if ArgCount <> 0 then
+ begin
+ ParamPtr := Params;
+ ArgPtr := @Args[ArgCount];
+ I := 0;
+ repeat
+ Dec(Integer(ArgPtr), SizeOf(TVarData));
+ ArgType := CallDesc^.ArgTypes[I] and atTypeMask;
+ VarFlag := CallDesc^.ArgTypes[I] and atByRef;
+ if ArgType = varError then
+ begin
+ ArgPtr^[0] := varError;
+ ArgPtr^[2] := DWORD(DISP_E_PARAMNOTFOUND);
+ end else
+ begin
+ if ArgType = varStrArg then
+ begin
+ with Strings[StrCount] do
+ if VarFlag <> 0 then
+ begin
+ BStr := StringToOleStr(pAnsiString(ParamPtr^)^);
+ PStr := pAnsiString(ParamPtr^);
+ ArgPtr^[0] := varOleStr or varByRef;
+ ArgPtr^[2] := Integer(@BStr);
+ end else
+ begin
+ BStr := StringToOleStr(pAnsiString(ParamPtr)^);
+ PStr := nil;
+ ArgPtr^[0] := varOleStr;
+ ArgPtr^[2] := Integer(BStr);
+ end;
+ Inc(StrCount);
+ end else
+ if VarFlag <> 0 then
+ begin
+ if (ArgType = varVariant) and
+ (PVarData(ParamPtr^)^.VType = varString) then
+ VarCast(PVariant(ParamPtr^)^, PVariant(ParamPtr^)^, varOleStr);
+ ArgPtr^[0] := ArgType or varByRef;
+ ArgPtr^[2] := ParamPtr^;
+ end else
+ if ArgType = varVariant then
+ begin
+ if PVarData(ParamPtr)^.VType = varString then
+ begin
+ with Strings[StrCount] do
+ begin
+ BStr := StringToOleStr(AnsiString(PVarData(ParamPtr^)^.VString));
+ PStr := nil;
+ ArgPtr^[0] := varOleStr;
+ ArgPtr^[2] := Integer(BStr);
+ end;
+ Inc(StrCount);
+ end else
+ begin
+ VarPtr := PVarArg(ParamPtr);
+ ArgPtr^[0] := VarPtr^[0];
+ ArgPtr^[1] := VarPtr^[1];
+ ArgPtr^[2] := VarPtr^[2];
+ ArgPtr^[3] := VarPtr^[3];
+ Inc(Integer(ParamPtr), 12);
+ end;
+ end else
+ begin
+ ArgPtr^[0] := ArgType;
+ ArgPtr^[2] := ParamPtr^;
+ if (ArgType >= varDouble) and (ArgType <= varDate) then
+ begin
+ Inc(Integer(ParamPtr), 4);
+ ArgPtr^[3] := ParamPtr^;
+ end;
+ end;
+ Inc(Integer(ParamPtr), 4);
+ end;
+ Inc(I);
+ until I = ArgCount;
+ end;
+ DispParams.rgvarg := @Args;
+ DispParams.rgdispidNamedArgs := @DispIDs[1];
+ DispParams.cArgs := ArgCount;
+ DispParams.cNamedArgs := CallDesc^.NamedArgCount;
+ DispID := DispIDs[0];
+ InvKind := CallDesc^.CallType;
+ if InvKind = DISPATCH_PROPERTYPUT then
+ begin
+ if Args[0][0] and varTypeMask = varDispatch then
+ InvKind := DISPATCH_PROPERTYPUTREF;
+ DispIDs[0] := DISPID_PROPERTYPUT;
+ Dec(Integer(DispParams.rgdispidNamedArgs), SizeOf(Integer));
+ Inc(DispParams.cNamedArgs);
+ end else
+ if (InvKind = DISPATCH_METHOD) and (ArgCount = 0) and (Result <> nil) then
+ InvKind := DISPATCH_METHOD or DISPATCH_PROPERTYGET;
+ Status := Dispatch.Invoke(DispID, GUID_NULL, 0, InvKind, DispParams,
+ Result, @ExcepInfo, nil);
+ if Status <> 0 then DispatchInvokeError(Status, ExcepInfo);
+ J := StrCount;
+ while J <> 0 do
+ begin
+ Dec(J);
+ with Strings[J] do
+ if PStr <> nil then OleStrToStrVar(BStr, PStr^);
+ end;
+ finally
+ K := StrCount;
+ while K <> 0 do
+ begin
+ Dec(K);
+ SysFreeString(Strings[K].BStr);
+ end;
+ end;
+end;
+
+{ Call GetIDsOfNames method on the given IDispatch interface }
+
+procedure GetIDsOfNames(const Dispatch: IDispatch; Names: PAnsiChar;
+ NameCount: Integer; DispIDs: PDispIDList);
+
+ procedure RaiseNameException;
+ begin
+ raise EOleError.CreateResFmt(e_Com, Integer( @SNoMethod ), [Names]);
+ end;
+
+type
+ PNamesArray = ^TNamesArray;
+ TNamesArray = array[0..0] of PWideChar;
+var
+ N, SrcLen, DestLen: Integer;
+ Src: PAnsiChar;
+ Dest: PWideChar;
+ NameRefs: PNamesArray;
+ StackTop: Pointer;
+ Temp: Integer;
+begin
+ Src := Names;
+ N := 0;
+ asm
+ MOV StackTop, ESP
+ MOV EAX, NameCount
+ INC EAX
+ SHL EAX, 2 // sizeof pointer = 4
+ SUB ESP, EAX
+ LEA EAX, NameRefs
+ MOV [EAX], ESP
+ end;
+ repeat
+ SrcLen := StrLen(Src);
+ DestLen := MultiByteToWideChar(0, 0, Src, SrcLen, nil, 0) + 1;
+ asm
+ MOV EAX, DestLen
+ ADD EAX, EAX
+ ADD EAX, 3 // round up to 4 byte boundary
+ AND EAX, not 3
+ SUB ESP, EAX
+ LEA EAX, Dest
+ MOV [EAX], ESP
+ end;
+ if N = 0 then NameRefs[0] := Dest else NameRefs[NameCount - N] := Dest;
+ MultiByteToWideChar(0, 0, Src, SrcLen, Dest, DestLen);
+ Dest[DestLen-1] := #0;
+ Inc(Src, SrcLen+1);
+ Inc(N);
+ until N = NameCount;
+ Temp := Dispatch.GetIDsOfNames(GUID_NULL, NameRefs, NameCount,
+ GetThreadLocale, DispIDs);
+ if Temp = Integer(DISP_E_UNKNOWNNAME) then RaiseNameException else OleCheck(Temp);
+ asm
+ MOV ESP, StackTop
+ end;
+end;
+
+{ Central call dispatcher }
+
+procedure VarDispInvoke(Result: PVariant; const Instance: Variant;
+ CallDesc: PCallDesc; Params: Pointer); cdecl;
+
+ procedure RaiseException;
+ begin
+ raise EOleError.CreateResFmt(e_Com, Integer( @SVarNotObject ), [ nil ] );
+ end;
+
+var
+ Dispatch: Pointer;
+ DispIDs: array[0..MaxDispArgs - 1] of Integer;
+begin
+ if TVarData(Instance).VType = varDispatch then
+ Dispatch := TVarData(Instance).VDispatch
+ else if TVarData(Instance).VType = (varDispatch or varByRef) then
+ Dispatch := Pointer(TVarData(Instance).VPointer^)
+ else RaiseException;
+ GetIDsOfNames(IDispatch(Dispatch), @CallDesc^.ArgTypes[CallDesc^.ArgCount],
+ CallDesc^.NamedArgCount + 1, @DispIDs);
+ if Result <> nil then VarClear(Result^);
+ DispatchInvoke(IDispatch(Dispatch), CallDesc, @DispIDs, @Params, Result);
+end;
+
+{ Raise exception given an OLE return code and TExcepInfo structure }
+
+procedure DispCallError(Status: Integer; var ExcepInfo: TExcepInfo;
+ ErrorAddr: Pointer; FinalizeExcepInfo: Boolean);
+var
+ E: Exception;
+begin
+ if Status = Integer(DISP_E_EXCEPTION) then
+ begin
+ with ExcepInfo do
+ E := EOleException.Create(e_Com, bstrDescription {, scode, bstrSource,
+ bstrHelpFile, dwHelpContext } );
+ if FinalizeExcepInfo then
+ Finalize(ExcepInfo);
+ end else
+ E := EOleSysError.Create(e_com, '' {, Status, 0});
+ if ErrorAddr <> nil then
+ raise E at ErrorAddr
+ else
+ raise E;
+end;
+
+{ Raise exception given an OLE return code and TExcepInfo structure }
+
+procedure DispatchInvokeError(Status: Integer; const ExcepInfo: TExcepInfo);
+begin
+ DispCallError(Status, PExcepInfo(@ExcepInfo)^, nil, False);
+end;
+
+procedure ClearExcepInfo(var ExcepInfo: TExcepInfo);
+begin
+ FillChar(ExcepInfo, SizeOf(ExcepInfo), 0);
+end;
+
+procedure DispCall(const Dispatch: IDispatch; CallDesc: PCallDesc;
+ DispID: Integer; NamedArgDispIDs, Params, Result: Pointer); stdcall;
+type
+ TExcepInfoRec = record // mock type to avoid auto init and cleanup code
+ wCode: Word;
+ wReserved: Word;
+ bstrSource: PWideChar;
+ bstrDescription: PWideChar;
+ bstrHelpFile: PWideChar;
+ dwHelpContext: Longint;
+ pvReserved: Pointer;
+ pfnDeferredFillIn: Pointer;
+ scode: HResult;
+ end;
+var
+ DispParams: TDispParams;
+ ExcepInfo: TExcepInfoRec;
+asm
+ PUSH EBX
+ PUSH ESI
+ PUSH EDI
+ MOV EBX,CallDesc
+ XOR EDX,EDX
+ MOV EDI,ESP
+ MOVZX ECX,[EBX].TCallDesc.ArgCount
+ MOV DispParams.cArgs,ECX
+ TEST ECX,ECX
+ JE @@10
+ ADD EBX,OFFSET TCallDesc.ArgTypes
+ MOV ESI,Params
+@@1: MOVZX EAX,[EBX].Byte
+ TEST AL,atByRef
+ JNE @@3
+ CMP AL,varVariant
+ JE @@2
+ CMP AL,varDouble
+ JB @@4
+ CMP AL,varDate
+ JA @@4
+ PUSH [ESI].Integer[4]
+ PUSH [ESI].Integer[0]
+ PUSH EDX
+ PUSH EAX
+ ADD ESI,8
+ JMP @@5
+@@2: PUSH [ESI].Integer[12]
+ PUSH [ESI].Integer[8]
+ PUSH [ESI].Integer[4]
+ PUSH [ESI].Integer[0]
+ ADD ESI,16
+ JMP @@5
+@@3: AND AL,atTypeMask
+ OR EAX,varByRef
+@@4: PUSH EDX
+ PUSH [ESI].Integer[0]
+ PUSH EDX
+ PUSH EAX
+ ADD ESI,4
+@@5: INC EBX
+ DEC ECX
+ JNE @@1
+ MOV EBX,CallDesc
+@@10: MOV DispParams.rgvarg,ESP
+ MOVZX EAX,[EBX].TCallDesc.NamedArgCount
+ MOV DispParams.cNamedArgs,EAX
+ TEST EAX,EAX
+ JE @@12
+ MOV ESI,NamedArgDispIDs
+@@11: PUSH [ESI].Integer[EAX*4-4]
+ DEC EAX
+ JNE @@11
+@@12: MOVZX ECX,[EBX].TCallDesc.CallType
+ CMP ECX,DISPATCH_PROPERTYPUT
+ JNE @@20
+ PUSH DISPID_PROPERTYPUT
+ INC DispParams.cNamedArgs
+ CMP [EBX].TCallDesc.ArgTypes.Byte[0],varDispatch
+ JE @@13
+ CMP [EBX].TCallDesc.ArgTypes.Byte[0],varUnknown
+ JNE @@20
+@@13: MOV ECX,DISPATCH_PROPERTYPUTREF
+@@20: MOV DispParams.rgdispidNamedArgs,ESP
+ PUSH EDX { ArgErr }
+ LEA EAX,ExcepInfo
+ PUSH EAX { ExcepInfo }
+ PUSH ECX
+ PUSH EDX
+ CALL ClearExcepInfo
+ POP EDX
+ POP ECX
+ PUSH Result { VarResult }
+ LEA EAX,DispParams
+ PUSH EAX { Params }
+ PUSH ECX { Flags }
+ PUSH EDX { LocaleID }
+ PUSH OFFSET GUID_NULL { IID }
+ PUSH DispID { DispID }
+ MOV EAX,Dispatch
+ PUSH EAX
+ MOV EAX,[EAX]
+ CALL [EAX].Pointer[24]
+ TEST EAX,EAX
+ JE @@30
+ LEA EDX,ExcepInfo
+ MOV CL, 1
+ PUSH ECX
+ MOV ECX,[EBP+4]
+ JMP DispCallError
+@@30: MOV ESP,EDI
+ POP EDI
+ POP ESI
+ POP EBX
+end;
+
+procedure DispCallByID(Result: Pointer; const Dispatch: IDispatch;
+ DispDesc: PDispDesc; Params: Pointer); cdecl;
+asm
+ PUSH EBX
+ MOV EBX,DispDesc
+ XOR EAX,EAX
+ PUSH EAX
+ PUSH EAX
+ PUSH EAX
+ PUSH EAX
+ MOV EAX,ESP
+ PUSH EAX
+ LEA EAX,Params
+ PUSH EAX
+ PUSH EAX
+ PUSH [EBX].TDispDesc.DispID
+ LEA EAX,[EBX].TDispDesc.CallDesc
+ PUSH EAX
+ PUSH Dispatch
+ CALL DispCall
+ MOVZX EAX,[EBX].TDispDesc.ResType
+ MOV EBX,Result
+ JMP @ResultTable.Pointer[EAX*4]
+
+@ResultTable:
+ DD @ResEmpty
+ DD @ResNull
+ DD @ResSmallint
+ DD @ResInteger
+ DD @ResSingle
+ DD @ResDouble
+ DD @ResCurrency
+ DD @ResDate
+ DD @ResString
+ DD @ResDispatch
+ DD @ResError
+ DD @ResBoolean
+ DD @ResVariant
+ DD @ResUnknown
+ DD @ResDecimal
+ DD @ResError
+ DD @ResByte
+
+@ResSingle:
+ FLD [ESP+8].Single
+ JMP @ResDone
+
+@ResDouble:
+@ResDate:
+ FLD [ESP+8].Double
+ JMP @ResDone
+
+@ResCurrency:
+ FILD [ESP+8].Currency
+ JMP @ResDone
+
+@ResString:
+ MOV EAX,[EBX]
+ TEST EAX,EAX
+ JE @@1
+ PUSH EAX
+ CALL SysFreeString
+@@1: MOV EAX,[ESP+8]
+ MOV [EBX],EAX
+ JMP @ResDone
+
+@ResDispatch:
+@ResUnknown:
+ MOV EAX,[EBX]
+ TEST EAX,EAX
+ JE @@2
+ PUSH EAX
+ MOV EAX,[EAX]
+ CALL [EAX].Pointer[8]
+@@2: MOV EAX,[ESP+8]
+ MOV [EBX],EAX
+ JMP @ResDone
+
+@ResVariant:
+ MOV EAX,EBX
+ CALL System.@VarClear
+ MOV EAX,[ESP]
+ MOV [EBX],EAX
+ MOV EAX,[ESP+4]
+ MOV [EBX+4],EAX
+ MOV EAX,[ESP+8]
+ MOV [EBX+8],EAX
+ MOV EAX,[ESP+12]
+ MOV [EBX+12],EAX
+ JMP @ResDone
+
+@ResSmallint:
+@ResInteger:
+@ResBoolean:
+@ResByte:
+ MOV EAX,[ESP+8]
+
+@ResDecimal:
+@ResEmpty:
+@ResNull:
+@ResError:
+@ResDone:
+ ADD ESP,16
+ POP EBX
+end;
+
+var
+ ComClassManagerVar: TObject;
+ SaveInitProc: Pointer;
+ NeedToUninitialize: Boolean;
+
+function ComClassManager: TComClassManager;
+begin
+ if ComClassManagerVar = nil then
+ ComClassManagerVar := TComClassManager.Create;
+ Result := TComClassManager(ComClassManagerVar);
+end;
+
+procedure InitComObj;
+begin
+ if SaveInitProc <> nil then TProcedure(SaveInitProc);
+ if (CoInitFlags <> -1) and Assigned(KOLComObj.CoInitializeEx) then
+ begin
+ NeedToUninitialize := Succeeded(KOLComObj.CoInitializeEx(nil, CoInitFlags));
+ IsMultiThread := IsMultiThread or
+ ((CoInitFlags and COINIT_APARTMENTTHREADED) <> 0) or
+ (CoInitFlags = COINIT_MULTITHREADED); // this flag has value zero
+ end
+ else
+ NeedToUninitialize := Succeeded(CoInitialize(nil));
+end;
+
+
+initialization
+begin
+ LoadComExProcs;
+ VarDispProc := @VarDispInvoke;
+ DispCallByIDProc := @DispCallByID;
+ SafeCallErrorProc := @SafeCallError;
+ if not IsLibrary then
+ begin
+ SaveInitProc := InitProc;
+ InitProc := @InitComObj;
+ end;
+end;
+
+finalization
+begin
+ OleUninitializing := True;
+ ComClassManagerVar.Free;
+ SafeCallErrorProc := nil;
+ DispCallByIDProc := nil;
+ VarDispProc := nil;
+ if NeedToUninitialize then CoUninitialize;
+end;
+
+end.
diff --git a/plugins/Libs/kolmath.pas b/plugins/Libs/kolmath.pas
new file mode 100644
index 0000000000..9e06418343
--- /dev/null
+++ b/plugins/Libs/kolmath.pas
@@ -0,0 +1,1845 @@
+{=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+ KKKKK KKKKK OOOOOOOOO LLLLL
+ KKKKK KKKKK OOOOOOOOOOOOO LLLLL
+ KKKKK KKKKK OOOOO OOOOO LLLLL
+ KKKKK KKKKK OOOOO OOOOO LLLLL
+ KKKKKKKKKK OOOOO OOOOO LLLLL
+ KKKKK KKKKK OOOOO OOOOO LLLLL
+ KKKKK KKKKK OOOOO OOOOO LLLLL
+ KKKKK KKKKK OOOOOOOOOOOOO LLLLLLLLLLLLL
+ KKKKK KKKKK OOOOOOOOO LLLLLLLLLLLLL
+
+ Key Objects Library (C) 2000 by Kladov Vladimir.
+
+ mailto: vk@kolmck.net
+ Home: http://kolmck.net
+
+ =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-}
+{
+ This code is grabbed from standard math.pas unit,
+ provided by Borland Delphi. This unit is for working with
+ engineering (mathematical) functions. The main difference
+ is that err unit specially designed to handle exceptions
+ for KOL is used instead of SysUtils. This allows to make
+ size of the executable smaller for about 5K. though this
+ value is insignificant for project made with VCL, it can
+ be more than 15% of executable file size made with KOL.
+}
+
+{*******************************************************}
+{ }
+{ Borland Delphi Runtime Library }
+{ Math Unit }
+{ }
+{ Copyright (C) 1996,99 Inprise Corporation }
+{ }
+{*******************************************************}
+
+unit kolmath;
+
+{ This unit contains high-performance arithmetic, trigonometric, logorithmic,
+ statistical and financial calculation routines which supplement the math
+ routines that are part of the Delphi language or System unit. }
+
+{$N+,S-}
+
+{$I KOLDEF.INC}
+
+interface
+
+uses {$IFNDEF MATH_NOERR} err, {$ENDIF} kol;
+
+const { Ranges of the IEEE floating point types, including denormals }
+ MinSingle = 1.5e-45;
+ MaxSingle = 3.4e+38;
+ MinDouble = 5.0e-324;
+ MaxDouble = 1.7e+308;
+ MinExtended = 3.4e-4932;
+ MaxExtended = 1.1e+4932;
+ MinComp = -9.223372036854775807e+18;
+ MaxComp = 9.223372036854775807e+18;
+
+{-----------------------------------------------------------------------
+References:
+
+1) P.J. Plauger, "The Standard C Library", Prentice-Hall, 1992, Ch. 7.
+2) W.J. Cody, Jr., and W. Waite, "Software Manual For the Elementary
+ Functions", Prentice-Hall, 1980.
+3) Namir Shammas, "C/C++ Mathematical Algorithms for Scientists and Engineers",
+ McGraw-Hill, 1995, Ch 8.
+4) H.T. Lau, "A Numerical Library in C for Scientists and Engineers",
+ CRC Press, 1994, Ch. 6.
+5) "Pentium(tm) Processor User's Manual, Volume 3: Architecture
+ and Programming Manual", Intel, 1994
++6)Óîððåí Ìëàäøèé, "Àðèôìåòè÷åñêèå òðþêè äëÿ ïðîãðàììèñòîâ", èñïðàâëåííîå èçä.,
+ 2004
+
+All angle parameters and results of trig functions are in radians.
+
+Most of the following trig and log routines map directly to Intel 80387 FPU
+floating point machine instructions. Input domains, output ranges, and
+error handling are determined largely by the FPU hardware.
+Routines coded in assembler favor the Pentium FPU pipeline architecture.
+-----------------------------------------------------------------------}
+
+function EAbs( D: Double ): Double;
+function EMax( const Values: array of Double ): Double;
+function EMin( const Values: array of Double ): Double;
+function ESign( X: Extended ): Integer;
+function iMax( const Values: array of Integer ): Integer;
+function iMin( const Values: array of Integer ): Integer;
+function iSign( i: Integer ): Integer;
+
+{ Trigonometric functions }
+function ArcCos(X: Extended): Extended; { IN: |X| <= 1 OUT: [0..PI] radians }
+function ArcSin(X: Extended): Extended; { IN: |X| <= 1 OUT: [-PI/2..PI/2] radians }
+
+{ ArcTan2 calculates ArcTan(Y/X), and returns an angle in the correct quadrant.
+ IN: |Y| < 2^64, |X| < 2^64, X <> 0 OUT: [-PI..PI] radians }
+function ArcTan2(Y, X: Extended): Extended;
+
+{ SinCos is 2x faster than calling Sin and Cos separately for the same angle }
+procedure SinCos(Theta: Extended; var Sin, Cos: Extended) register;
+function Tan(X: Extended): Extended;
+function Cotan(X: Extended): Extended; { 1 / tan(X), X <> 0 }
+function Hypot(X, Y: Extended): Extended; { Sqrt(X**2 + Y**2) }
+
+{ Angle unit conversion routines }
+function DegToRad(Degrees: Extended): Extended; { Radians := Degrees * PI / 180}
+function RadToDeg(Radians: Extended): Extended; { Degrees := Radians * 180 / PI }
+function GradToRad(Grads: Extended): Extended; { Radians := Grads * PI / 200 }
+function RadToGrad(Radians: Extended): Extended; { Grads := Radians * 200 / PI }
+function CycleToRad(Cycles: Extended): Extended; { Radians := Cycles * 2PI }
+function RadToCycle(Radians: Extended): Extended;{ Cycles := Radians / 2PI }
+
+{ Hyperbolic functions and inverses }
+function Cosh(X: Extended): Extended;
+function Sinh(X: Extended): Extended;
+function Tanh(X: Extended): Extended;
+function ArcCosh(X: Extended): Extended; { IN: X >= 1 }
+function ArcSinh(X: Extended): Extended;
+function ArcTanh(X: Extended): Extended; { IN: |X| <= 1 }
+
+{ Logorithmic functions }
+function LnXP1(X: Extended): Extended; { Ln(X + 1), accurate for X near zero }
+function Log10(X: Extended): Extended; { Log base 10 of X}
+function Log2(X: Extended): Extended; { Log base 2 of X }
+function LogN(Base, X: Extended): Extended; { Log base N of X }
+
+{ Exponential functions }
+
+{ IntPower: Raise base to an integral power. Fast. }
+//function IntPower(Base: Extended; Exponent: Integer): Extended register;
+// -- already defined in kol.pas
+
+{ Power: Raise base to any power.
+ For fractional exponents, or |exponents| > MaxInt, base must be > 0. }
+function Power(Base, Exponent: Extended): Extended;
+{$IFNDEF _D6orHigher}
+function Trunc( X: Extended ): Int64;
+{$ENDIF}
+
+{ Miscellaneous Routines }
+
+{ Frexp: Separates the mantissa and exponent of X. }
+procedure Frexp(X: Extended; var Mantissa: Extended; var Exponent: Integer) register;
+
+{ Ldexp: returns X*2**P }
+function Ldexp(X: Extended; P: Integer): Extended register;
+
+{ Ceil: Smallest integer >= X, |X| < MaxInt }
+function Ceil(X: Extended):Integer;
+
+{ Floor: Largest integer <= X, |X| < MaxInt }
+function Floor(X: Extended): Integer;
+
+{ Poly: Evaluates a uniform polynomial of one variable at value X.
+ The coefficients are ordered in increasing powers of X:
+ Coefficients[0] + Coefficients[1]*X + ... + Coefficients[N]*(X**N) }
+function Poly(X: Extended; const Coefficients: array of Double): Extended;
+
+{-----------------------------------------------------------------------
+Statistical functions.
+
+Common commercial spreadsheet macro names for these statistical and
+financial functions are given in the comments preceding each function.
+-----------------------------------------------------------------------}
+
+{ Mean: Arithmetic average of values. (AVG): SUM / N }
+function Mean(const Data: array of Double): Extended;
+
+{ Sum: Sum of values. (SUM) }
+function Sum(const Data: array of Double): Extended register;
+function SumInt(const Data: array of Integer): Integer register;
+function SumOfSquares(const Data: array of Double): Extended;
+procedure SumsAndSquares(const Data: array of Double;
+ var Sum, SumOfSquares: Extended) register;
+
+{ MinValue: Returns the smallest signed value in the data array (MIN) }
+function MinValue(const Data: array of Double): Double;
+function MinIntValue(const Data: array of Integer): Integer;
+
+function Min(A,B: Integer): Integer;
+{$IFDEF _D4orHigher}
+overload;
+function Min(A,B: I64): I64; overload;
+function Min(A,B: Int64): Int64; overload;
+function Min(A,B: Single): Single; overload;
+function Min(A,B: Double): Double; overload;
+function Min(A,B: Extended): Extended; overload;
+{$ENDIF}
+
+{ MaxValue: Returns the largest signed value in the data array (MAX) }
+function MaxValue(const Data: array of Double): Double;
+function MaxIntValue(const Data: array of Integer): Integer;
+
+function Max(A,B: Integer): Integer;
+{$IFDEF _D4orHigher}
+overload;
+function Max(A,B: I64): I64; overload;
+function Max(A,B: Single): Single; overload;
+function Max(A,B: Double): Double; overload;
+function Max(A,B: Extended): Extended; overload;
+{$ENDIF}
+
+{ Standard Deviation (STD): Sqrt(Variance). aka Sample Standard Deviation }
+function StdDev(const Data: array of Double): Extended;
+
+{ MeanAndStdDev calculates Mean and StdDev in one call. }
+procedure MeanAndStdDev(const Data: array of Double; var Mean, StdDev: Extended);
+
+{ Population Standard Deviation (STDP): Sqrt(PopnVariance).
+ Used in some business and financial calculations. }
+function PopnStdDev(const Data: array of Double): Extended;
+
+{ Variance (VARS): TotalVariance / (N-1). aka Sample Variance }
+function Variance(const Data: array of Double): Extended;
+
+{ Population Variance (VAR or VARP): TotalVariance/ N }
+function PopnVariance(const Data: array of Double): Extended;
+
+{ Total Variance: SUM(i=1,N)[(X(i) - Mean)**2] }
+function TotalVariance(const Data: array of Double): Extended;
+
+{ Norm: The Euclidean L2-norm. Sqrt(SumOfSquares) }
+function Norm(const Data: array of Double): Extended;
+
+{ MomentSkewKurtosis: Calculates the core factors of statistical analysis:
+ the first four moments plus the coefficients of skewness and kurtosis.
+ M1 is the Mean. M2 is the Variance.
+ Skew reflects symmetry of distribution: M3 / (M2**(3/2))
+ Kurtosis reflects flatness of distribution: M4 / Sqr(M2) }
+procedure MomentSkewKurtosis(const Data: array of Double;
+ var M1, M2, M3, M4, Skew, Kurtosis: Extended);
+
+{ RandG produces random numbers with Gaussian distribution about the mean.
+ Useful for simulating data with sampling errors. }
+function RandG(Mean, StdDev: Extended): Extended;
+
+{-----------------------------------------------------------------------
+Financial functions. Standard set from Quattro Pro.
+
+Parameter conventions:
+
+From the point of view of A, amounts received by A are positive and
+amounts disbursed by A are negative (e.g. a borrower's loan repayments
+are regarded by the borrower as negative).
+
+Interest rates are per payment period. 11% annual percentage rate on a
+loan with 12 payments per year would be (11 / 100) / 12 = 0.00916667
+
+-----------------------------------------------------------------------}
+
+type
+ TPaymentTime = (ptEndOfPeriod, ptStartOfPeriod);
+
+{ Double Declining Balance (DDB) }
+function DoubleDecliningBalance(Cost, Salvage: Extended;
+ Life, Period: Integer): Extended;
+
+{ Future Value (FVAL) }
+function FutureValue(Rate: Extended; NPeriods: Integer; Payment, PresentValue:
+ Extended; PaymentTime: TPaymentTime): Extended;
+
+{ Interest Payment (IPAYMT) }
+function InterestPayment(Rate: Extended; Period, NPeriods: Integer; PresentValue,
+ FutureValue: Extended; PaymentTime: TPaymentTime): Extended;
+
+{ Interest Rate (IRATE) }
+function InterestRate(NPeriods: Integer;
+ Payment, PresentValue, FutureValue: Extended; PaymentTime: TPaymentTime): Extended;
+
+{ Internal Rate of Return. (IRR) Needs array of cash flows. }
+function InternalRateOfReturn(Guess: Extended;
+ const CashFlows: array of Double): Extended;
+
+{ Number of Periods (NPER) }
+function NumberOfPeriods(Rate, Payment, PresentValue, FutureValue: Extended;
+ PaymentTime: TPaymentTime): Extended;
+
+{ Net Present Value. (NPV) Needs array of cash flows. }
+function NetPresentValue(Rate: Extended; const CashFlows: array of Double;
+ PaymentTime: TPaymentTime): Extended;
+
+{ Payment (PAYMT) }
+function Payment(Rate: Extended; NPeriods: Integer;
+ PresentValue, FutureValue: Extended; PaymentTime: TPaymentTime): Extended;
+
+{ Period Payment (PPAYMT) }
+function PeriodPayment(Rate: Extended; Period, NPeriods: Integer;
+ PresentValue, FutureValue: Extended; PaymentTime: TPaymentTime): Extended;
+
+{ Present Value (PVAL) }
+function PresentValue(Rate: Extended; NPeriods: Integer;
+ Payment, FutureValue: Extended; PaymentTime: TPaymentTime): Extended;
+
+{ Straight Line depreciation (SLN) }
+function SLNDepreciation(Cost, Salvage: Extended; Life: Integer): Extended;
+
+{ Sum-of-Years-Digits depreciation (SYD) }
+function SYDDepreciation(Cost, Salvage: Extended; Life, Period: Integer): Extended;
+
+{type
+ EInvalidArgument = class(EMathError) end;}
+
+{------------------------------------------------------------------------------}
+{ Integer and logical functions }
+function IsPowerOf2( i: Integer ): Boolean;
+{* TRUE, åñëè ÷èñëî ÿâëÿåòñÿ ñòåïåíüþ ÷èñëà 2 }
+
+function Low1( i: Integer ): Integer;
+{* Âûäåëÿåò ìëàäøèé áèò 1 èç ÷èñëà i. }
+
+function Low0( i: Integer ): Integer;
+{* Âûäåëÿåò ìëàäøèé ñïðàâà áèò 0 èç ÷èñëà i, íàïðèìåð, 1100011 -> 100 }
+
+function count_1_bits_in_byte( x: Byte ): Byte;
+{* Ïîäñ÷èòûâàåò ÷èñëî åäèíè÷íûõ áèòîâ â áàéòå }
+
+function count_1_bits_in_dword( x: Integer ): Integer;
+{* Ïîäñ÷èòûâàåò ÷èñëî åäèíè÷íûõ áèòîâ â 32-áèòíîì }
+
+
+implementation
+
+{$IFNDEF _D2orD3}
+uses SysConst;
+{$ENDIF}
+
+function EAbs( D: Double ): Double;
+begin
+ Result := D;
+ if Result < 0.0 then
+ Result := -Result;
+end;
+
+function EMax( const Values: array of Double ): Double;
+var I: Integer;
+begin
+ Result := Values[ 0 ];
+ for I := 1 to High( Values ) do
+ if Result < Values[ I ] then Result := Values[ I ];
+end;
+
+function EMin( const Values: array of Double ): Double;
+var I: Integer;
+begin
+ Result := Values[ 0 ];
+ for I := 1 to High( Values ) do
+ if Result > Values[ I ] then Result := Values[ I ];
+end;
+
+function ESign( X: Extended ): Integer;
+begin
+ if X < 0 then Result := -1
+ else if X > 0 then Result := 1
+ else Result := 1;
+end;
+
+function iMax( const Values: array of Integer ): Integer;
+var I: Integer;
+begin
+ Result := Values[ 0 ];
+ for I := 1 to High( Values ) do
+ if Result < Values[ I ] then Result := Values[ I ];
+end;
+
+function iMin( const Values: array of Integer ): Integer;
+var I: Integer;
+begin
+ Result := Values[ 0 ];
+ for I := 1 to High( Values ) do
+ if Result > Values[ I ] then Result := Values[ I ];
+end;
+
+{$IFDEF PAS_VERSION}
+function iSign( i: Integer ): Integer;
+begin
+ if i < 0 then Result := -1
+ else if i > 0 then Result := 1
+ else Result := 0;
+end;
+{$ELSE}
+function iSign( i: Integer ): Integer;
+asm
+ XOR EDX, EDX
+ TEST EAX, EAX
+ JZ @@exit
+ MOV DL, 1
+ JG @@exit
+ OR EDX, -1
+@@exit:
+ XCHG EAX, EDX
+end;
+{$ENDIF}
+
+function Annuity2(R: Extended; N: Integer; PaymentTime: TPaymentTime;
+ var CompoundRN: Extended): Extended; Forward;
+function Compound(R: Extended; N: Integer): Extended; Forward;
+function RelSmall(X, Y: Extended): Boolean; Forward;
+
+type
+ TPoly = record
+ Neg, Pos, DNeg, DPos: Extended
+ end;
+
+const
+ MaxIterations = 15;
+
+{$IFNDEF MATH_NOERR}
+procedure ArgError(const Msg: string);
+begin
+ raise Exception.Create(e_Math_InvalidArgument, Msg);
+end;
+{$ENDIF}
+
+function DegToRad(Degrees: Extended): Extended; { Radians := Degrees * PI / 180 }
+begin
+ Result := Degrees * (PI / 180);
+end;
+
+function RadToDeg(Radians: Extended): Extended; { Degrees := Radians * 180 / PI }
+begin
+ Result := Radians * (180 / PI);
+end;
+
+function GradToRad(Grads: Extended): Extended; { Radians := Grads * PI / 200 }
+begin
+ Result := Grads * (PI / 200);
+end;
+
+function RadToGrad(Radians: Extended): Extended; { Grads := Radians * 200 / PI}
+begin
+ Result := Radians * (200 / PI);
+end;
+
+function CycleToRad(Cycles: Extended): Extended; { Radians := Cycles * 2PI }
+begin
+ Result := Cycles * (2 * PI);
+end;
+
+function RadToCycle(Radians: Extended): Extended;{ Cycles := Radians / 2PI }
+begin
+ Result := Radians / (2 * PI);
+end;
+
+function LnXP1(X: Extended): Extended;
+{ Return ln(1 + X). Accurate for X near 0. }
+asm
+ FLDLN2
+ MOV AX,WORD PTR X+8 { exponent }
+ FLD X
+ CMP AX,$3FFD { .4225 }
+ JB @@1
+ FLD1
+ FADD
+ FYL2X
+ JMP @@2
+@@1:
+ FYL2XP1
+@@2:
+ FWAIT
+end;
+
+{ Invariant: Y >= 0 & Result*X**Y = X**I. Init Y = I and Result = 1. }
+{function IntPower(X: Extended; I: Integer): Extended;
+var
+ Y: Integer;
+begin
+ Y := Abs(I);
+ Result := 1.0;
+ while Y > 0 do begin
+ while not Odd(Y) do
+ begin
+ Y := Y shr 1;
+ X := X * X
+ end;
+ Dec(Y);
+ Result := Result * X
+ end;
+ if I < 0 then Result := 1.0 / Result
+end;
+}
+(* -- already defined in kol.pas
+function IntPower(Base: Extended; Exponent: Integer): Extended;
+asm
+ mov ecx, eax
+ cdq
+ fld1 { Result := 1 }
+ xor eax, edx
+ sub eax, edx { eax := Abs(Exponent) }
+ jz @@3
+ fld Base
+ jmp @@2
+@@1: fmul ST, ST { X := Base * Base }
+@@2: shr eax,1
+ jnc @@1
+ fmul ST(1),ST { Result := Result * X }
+ jnz @@1
+ fstp st { pop X from FPU stack }
+ cmp ecx, 0
+ jge @@3
+ fld1
+ fdivrp { Result := 1 / Result }
+@@3:
+ fwait
+end;
+*)
+
+function Compound(R: Extended; N: Integer): Extended;
+{ Return (1 + R)**N. }
+begin
+ Result := IntPower(1.0 + R, N)
+end;
+
+function Annuity2(R: Extended; N: Integer; PaymentTime: TPaymentTime;
+ var CompoundRN: Extended): Extended;
+{ Set CompoundRN to Compound(R, N),
+ return (1+Rate*PaymentTime)*(Compound(R,N)-1)/R;
+}
+begin
+ if R = 0.0 then
+ begin
+ CompoundRN := 1.0;
+ Result := N;
+ end
+ else
+ begin
+ { 6.1E-5 approx= 2**-14 }
+ if EAbs(R) < 6.1E-5 then
+ begin
+ CompoundRN := Exp(N * LnXP1(R));
+ Result := N*(1+(N-1)*R/2);
+ end
+ else
+ begin
+ CompoundRN := Compound(R, N);
+ Result := (CompoundRN-1) / R
+ end;
+ if PaymentTime = ptStartOfPeriod then
+ Result := Result * (1 + R);
+ end;
+end; {Annuity2}
+
+
+procedure PolyX(const A: array of Double; X: Extended; var Poly: TPoly);
+{ Compute A[0] + A[1]*X + ... + A[N]*X**N and X * its derivative.
+ Accumulate positive and negative terms separately. }
+var
+ I: Integer;
+ Neg, Pos, DNeg, DPos: Extended;
+begin
+ Neg := 0.0;
+ Pos := 0.0;
+ DNeg := 0.0;
+ DPos := 0.0;
+ for I := High(A) downto Low(A) do
+ begin
+ DNeg := X * DNeg + Neg;
+ Neg := Neg * X;
+ DPos := X * DPos + Pos;
+ Pos := Pos * X;
+ if A[I] >= 0.0 then
+ Pos := Pos + A[I]
+ else
+ Neg := Neg + A[I]
+ end;
+ Poly.Neg := Neg;
+ Poly.Pos := Pos;
+ Poly.DNeg := DNeg * X;
+ Poly.DPos := DPos * X;
+end; {PolyX}
+
+
+function RelSmall(X, Y: Extended): Boolean;
+{ Returns True if X is small relative to Y }
+const
+ C1: Double = 1E-15;
+ C2: Double = 1E-12;
+begin
+ Result := EAbs(X) < (C1 + C2 * EAbs(Y))
+end;
+
+{ Math functions. }
+
+function ArcCos(X: Extended): Extended;
+begin
+ if X > 0.999999999999999 then
+ Result := 0 {èíà÷å -NAN !}
+ else
+ if X < -0.999999999999999 then
+ Result := PI
+ else
+ Result := ArcTan2(Sqrt(1 - X*X), X);
+end;
+
+function ArcSin(X: Extended): Extended;
+begin
+ Result := ArcTan2(X, Sqrt(1 - X*X))
+end;
+
+function ArcTan2(Y, X: Extended): Extended;
+asm
+ FLD Y
+ FLD X
+ FPATAN
+ FWAIT
+end;
+
+function Tan(X: Extended): Extended;
+{ Tan := Sin(X) / Cos(X) }
+asm
+ FLD X
+ FPTAN
+ FSTP ST(0) { FPTAN pushes 1.0 after result }
+ FWAIT
+end;
+
+function CoTan(X: Extended): Extended;
+{ CoTan := Cos(X) / Sin(X) = 1 / Tan(X) }
+asm
+ FLD X
+ FPTAN
+ FDIVRP
+ FWAIT
+end;
+
+function Hypot(X, Y: Extended): Extended;
+{ formula: Sqrt(X*X + Y*Y)
+ implemented as: |Y|*Sqrt(1+Sqr(X/Y)), |X| < |Y| for greater precision
+var
+ Temp: Extended;
+begin
+ X := Abs(X);
+ Y := Abs(Y);
+ if X > Y then
+ begin
+ Temp := X;
+ X := Y;
+ Y := Temp;
+ end;
+ if X = 0 then
+ Result := Y
+ else // Y > X, X <> 0, so Y > 0
+ Result := Y * Sqrt(1 + Sqr(X/Y));
+end;
+}
+asm
+ FLD Y
+ FABS
+ FLD X
+ FABS
+ FCOM
+ FNSTSW AX
+ TEST AH,$45
+ JNZ @@1 // if ST > ST(1) then swap
+ FXCH ST(1) // put larger number in ST(1)
+@@1: FLDZ
+ FCOMP
+ FNSTSW AX
+ TEST AH,$40 // if ST = 0, return ST(1)
+ JZ @@2
+ FSTP ST // eat ST(0)
+ JMP @@3
+@@2: FDIV ST,ST(1) // ST := ST / ST(1)
+ FMUL ST,ST // ST := ST * ST
+ FLD1
+ FADD // ST := ST + 1
+ FSQRT // ST := Sqrt(ST)
+ FMUL // ST(1) := ST * ST(1); Pop ST
+@@3: FWAIT
+end;
+
+
+procedure SinCos(Theta: Extended; var Sin, Cos: Extended);
+asm
+ FLD Theta
+ FSINCOS
+ FSTP tbyte ptr [edx] // Cos
+ FSTP tbyte ptr [eax] // Sin
+ FWAIT
+end;
+
+{ Extract exponent and mantissa from X }
+procedure Frexp(X: Extended; var Mantissa: Extended; var Exponent: Integer);
+{ Mantissa ptr in EAX, Exponent ptr in EDX }
+asm
+ FLD X
+ PUSH EAX
+ MOV dword ptr [edx], 0 { if X = 0, return 0 }
+
+ FTST
+ FSTSW AX
+ FWAIT
+ SAHF
+ JZ @@Done
+
+ FXTRACT // ST(1) = exponent, (pushed) ST = fraction
+ FXCH
+
+// The FXTRACT instruction normalizes the fraction 1 bit higher than
+// wanted for the definition of frexp() so we need to tweak the result
+// by scaling the fraction down and incrementing the exponent.
+
+ FISTP dword ptr [edx]
+ FLD1
+ FCHS
+ FXCH
+ FSCALE // scale fraction
+ INC dword ptr [edx] // exponent biased to match
+ FSTP ST(1) // discard -1, leave fraction as TOS
+
+@@Done:
+ POP EAX
+ FSTP tbyte ptr [eax]
+ FWAIT
+end;
+
+function Ldexp(X: Extended; P: Integer): Extended;
+ { Result := X * (2^P) }
+asm
+ PUSH EAX
+ FILD dword ptr [ESP]
+ FLD X
+ FSCALE
+ POP EAX
+ FSTP ST(1)
+ FWAIT
+end;
+
+function Ceil(X: Extended): Integer;
+begin
+ Result := Integer(Trunc(X));
+ if Frac(X) > 0 then
+ Inc(Result);
+end;
+
+function Floor(X: Extended): Integer;
+begin
+ Result := Integer(Trunc(X));
+ if Frac(X) < 0 then
+ Dec(Result);
+end;
+
+{ Conversion of bases: Log.b(X) = Log.a(X) / Log.a(b) }
+
+function Log10(X: Extended): Extended;
+ { Log.10(X) := Log.2(X) * Log.10(2) }
+asm
+ FLDLG2 { Log base ten of 2 }
+ FLD X
+ FYL2X
+ FWAIT
+end;
+
+function Log2(X: Extended): Extended;
+asm
+ FLD1
+ FLD X
+ FYL2X
+ FWAIT
+end;
+
+function LogN(Base, X: Extended): Extended;
+{ Log.N(X) := Log.2(X) / Log.2(N) }
+asm
+ FLD1
+ FLD X
+ FYL2X
+ FLD1
+ FLD Base
+ FYL2X
+ FDIV
+ FWAIT
+end;
+
+function Poly(X: Extended; const Coefficients: array of Double): Extended;
+{ Horner's method }
+var
+ I: Integer;
+begin
+ Result := Coefficients[High(Coefficients)];
+ for I := High(Coefficients)-1 downto Low(Coefficients) do
+ Result := Result * X + Coefficients[I];
+end;
+
+function Power(Base, Exponent: Extended): Extended;
+begin
+ if Exponent = 0.0 then
+ Result := 1.0 { n**0 = 1 }
+ else if (Base = 0.0) and (Exponent > 0.0) then
+ Result := 0.0 { 0**n = 0, n > 0 }
+ else if (Frac(Exponent) = 0.0) and (EAbs(Exponent) <= MaxInt) then
+ Result := IntPower(Base, Integer(Trunc(Exponent)))
+ else
+ Result := Exp(Exponent * Ln(Base))
+end;
+
+{$IFNDEF _D6orHigher}
+(*function Trunc1( X: Extended ): Int64;
+begin
+ Result := System.Trunc( X );
+end;
+asm
+ FLD qword ptr [ESP+4]
+ { -> FST(0) Extended argument }
+ { <- EDX:EAX Result }
+
+
+ SUB ESP,12
+ FNSTCW [ESP].Word // save
+ FNSTCW [ESP+2].Word // scratch
+ FWAIT
+ OR [ESP+2].Word, $0F00 // trunc toward zero, full precision
+ FLDCW [ESP+2].Word
+ FISTP qword ptr [ESP+4]
+ FWAIT
+ FLDCW [ESP].Word
+ POP ECX
+ POP EAX
+ POP EDX
+end;*)
+
+function Trunc( X: Extended ): Int64;
+begin
+ if Abs( X ) < 1 then Result := 0 else
+ if X < 0 then Result := -System.Trunc( -X )
+ else Result := System.Trunc( X );
+end;
+{$ENDIF}
+
+
+{ Hyperbolic functions }
+
+function CoshSinh(X: Extended; Factor: Double): Extended;
+begin
+ Result := Exp(X) / 2;
+ Result := Result + Factor / Result;
+end;
+
+function Cosh(X: Extended): Extended;
+begin
+ Result := CoshSinh(X, 0.25)
+end;
+
+function Sinh(X: Extended): Extended;
+begin
+ Result := CoshSinh(X, -0.25)
+end;
+
+const
+ MaxTanhDomain = 5678.22249441322; // Ln(MaxExtended)/2
+
+function Tanh(X: Extended): Extended;
+begin
+ if X > MaxTanhDomain then
+ Result := 1.0
+ else if X < -MaxTanhDomain then
+ Result := -1.0
+ else
+ begin
+ Result := Exp(X);
+ Result := Result * Result;
+ Result := (Result - 1.0) / (Result + 1.0)
+ end;
+end;
+
+function ArcCosh(X: Extended): Extended;
+begin
+ if X <= 1.0 then
+ Result := 0.0
+ else if X > 1.0e10 then
+ Result := Ln(2) + Ln(X)
+ else
+ Result := Ln(X + Sqrt((X - 1.0) * (X + 1.0)));
+end;
+
+function ArcSinh(X: Extended): Extended;
+var
+ Neg: Boolean;
+begin
+ if X = 0 then
+ Result := 0
+ else
+ begin
+ Neg := (X < 0);
+ X := EAbs(X);
+ if X > 1.0e10 then
+ Result := Ln(2) + Ln(X)
+ else
+ begin
+ Result := X*X;
+ Result := LnXP1(X + Result / (1 + Sqrt(1 + Result)));
+ end;
+ if Neg then Result := -Result;
+ end;
+end;
+
+function ArcTanh(X: Extended): Extended;
+var
+ Neg: Boolean;
+begin
+ if X = 0 then
+ Result := 0
+ else
+ begin
+ Neg := (X < 0);
+ X := EAbs(X);
+ if X >= 1 then
+ Result := MaxExtended
+ else
+ Result := 0.5 * LnXP1((2.0 * X) / (1.0 - X));
+ if Neg then Result := -Result;
+ end;
+end;
+
+{ Statistical functions }
+
+function Mean(const Data: array of Double): Extended;
+begin
+ Result := SUM(Data) / (High(Data) - Low(Data) + 1)
+end;
+
+function MinValue(const Data: array of Double): Double;
+var
+ I: Integer;
+begin
+ Result := Data[Low(Data)];
+ for I := Low(Data) + 1 to High(Data) do
+ if Result > Data[I] then
+ Result := Data[I];
+end;
+
+function MinIntValue(const Data: array of Integer): Integer;
+var
+ I: Integer;
+begin
+ Result := Data[Low(Data)];
+ for I := Low(Data) + 1 to High(Data) do
+ if Result > Data[I] then
+ Result := Data[I];
+end;
+
+{$IFDEF ASM_VERSION}
+function Min(A,B: Integer): Integer;
+asm
+ CMP EAX, EDX
+ JL @@1
+ XCHG EAX, EDX
+@@1:
+end;
+{$ELSE}
+function Min(A,B: Integer): Integer;
+begin
+ if A < B then
+ Result := A
+ else
+ Result := B;
+end;
+{$ENDIF}
+
+{$IFDEF _D4orHigher}
+function Min(A,B: I64): I64;
+begin
+ if Cmp64( A, B ) < 0 then
+ Result := A
+ else
+ Result := B;
+end;
+
+function Min(A,B: Int64): Int64;
+begin
+ if A < B then
+ Result := A
+ else
+ Result := B;
+end;
+
+function Min(A,B: Single): Single;
+begin
+ if A < B then
+ Result := A
+ else
+ Result := B;
+end;
+
+function Min(A,B: Double): Double;
+begin
+ if A < B then
+ Result := A
+ else
+ Result := B;
+end;
+
+function Min(A,B: Extended): Extended;
+begin
+ if A < B then
+ Result := A
+ else
+ Result := B;
+end;
+{$ENDIF}
+
+function MaxValue(const Data: array of Double): Double;
+var
+ I: Integer;
+begin
+ Result := Data[Low(Data)];
+ for I := Low(Data) + 1 to High(Data) do
+ if Result < Data[I] then
+ Result := Data[I];
+end;
+
+function MaxIntValue(const Data: array of Integer): Integer;
+var
+ I: Integer;
+begin
+ Result := Data[Low(Data)];
+ for I := Low(Data) + 1 to High(Data) do
+ if Result < Data[I] then
+ Result := Data[I];
+end;
+
+{$IFDEF ASM_VERSION}
+function Max(A,B: Integer): Integer;
+asm
+ CMP EAX, EDX
+ JG @@1
+ XCHG EAX, EDX
+@@1:
+end;
+{$ELSE}
+function Max(A,B: Integer): Integer;
+begin
+ if A > B then
+ Result := A
+ else
+ Result := B;
+end;
+{$ENDIF}
+
+{$IFDEF _D4orHigher}
+function Max(A,B: I64): I64;
+begin
+ if Cmp64( A, B ) > 0 then
+ Result := A
+ else
+ Result := B;
+end;
+
+function Max(A,B: Single): Single;
+begin
+ if A > B then
+ Result := A
+ else
+ Result := B;
+end;
+
+function Max(A,B: Double): Double;
+begin
+ if A > B then
+ Result := A
+ else
+ Result := B;
+end;
+
+function Max(A,B: Extended): Extended;
+begin
+ if A > B then
+ Result := A
+ else
+ Result := B;
+end;
+{$ENDIF}
+
+procedure MeanAndStdDev(const Data: array of Double; var Mean, StdDev: Extended);
+var
+ S: Extended;
+ N,I: Integer;
+begin
+ N := High(Data)- Low(Data) + 1;
+ if N = 1 then
+ begin
+ Mean := Data[0];
+ StdDev := Data[0];
+ Exit;
+ end;
+ Mean := Sum(Data) / N;
+ S := 0; // sum differences from the mean, for greater accuracy
+ for I := Low(Data) to High(Data) do
+ S := S + Sqr(Mean - Data[I]);
+ StdDev := Sqrt(S / (N - 1));
+end;
+
+procedure MomentSkewKurtosis(const Data: array of Double;
+ var M1, M2, M3, M4, Skew, Kurtosis: Extended);
+var
+ Sum, SumSquares, SumCubes, SumQuads, OverN, Accum, M1Sqr, S2N, S3N: Extended;
+ I: Integer;
+begin
+ OverN := 1 / (High(Data) - Low(Data) + 1);
+ Sum := 0;
+ SumSquares := 0;
+ SumCubes := 0;
+ SumQuads := 0;
+ for I := Low(Data) to High(Data) do
+ begin
+ Sum := Sum + Data[I];
+ Accum := Sqr(Data[I]);
+ SumSquares := SumSquares + Accum;
+ Accum := Accum*Data[I];
+ SumCubes := SumCubes + Accum;
+ SumQuads := SumQuads + Accum*Data[I];
+ end;
+ M1 := Sum * OverN;
+ M1Sqr := Sqr(M1);
+ S2N := SumSquares * OverN;
+ S3N := SumCubes * OverN;
+ M2 := S2N - M1Sqr;
+ M3 := S3N - (M1 * 3 * S2N) + 2*M1Sqr*M1;
+ M4 := (SumQuads * OverN) - (M1 * 4 * S3N) + (M1Sqr*6*S2N - 3*Sqr(M1Sqr));
+ Skew := M3 * Power(M2, -3/2); // = M3 / Power(M2, 3/2)
+ Kurtosis := M4 / Sqr(M2);
+end;
+
+function Norm(const Data: array of Double): Extended;
+begin
+ Result := Sqrt(SumOfSquares(Data));
+end;
+
+function PopnStdDev(const Data: array of Double): Extended;
+begin
+ Result := Sqrt(PopnVariance(Data))
+end;
+
+function PopnVariance(const Data: array of Double): Extended;
+begin
+ Result := TotalVariance(Data) / (High(Data) - Low(Data) + 1)
+end;
+
+function RandG(Mean, StdDev: Extended): Extended;
+{ Marsaglia-Bray algorithm }
+var
+ U1, S2: Extended;
+begin
+ repeat
+ U1 := 2*Random - 1;
+ S2 := Sqr(U1) + Sqr(2*Random-1);
+ until S2 < 1;
+ Result := Sqrt(-2*Ln(S2)/S2) * U1 * StdDev + Mean;
+end;
+
+function StdDev(const Data: array of Double): Extended;
+begin
+ Result := Sqrt(Variance(Data))
+end;
+
+procedure RaiseOverflowError; forward;
+
+function SumInt(const Data: array of Integer): Integer;
+{var
+ I: Integer;
+begin
+ Result := 0;
+ for I := Low(Data) to High(Data) do
+ Result := Result + Data[I]
+end; }
+asm // IN: EAX = ptr to Data, EDX = High(Data) = Count - 1
+ // loop unrolled 4 times, 5 clocks per loop, 1.2 clocks per datum
+ PUSH EBX
+ MOV ECX, EAX // ecx = ptr to data
+ MOV EBX, EDX
+ XOR EAX, EAX
+ AND EDX, not 3
+ AND EBX, 3
+ SHL EDX, 2
+ JMP @Vector.Pointer[EBX*4]
+@Vector:
+ DD @@1
+ DD @@2
+ DD @@3
+ DD @@4
+@@4:
+ ADD EAX, [ECX+12+EDX]
+ JO @@RaiseOverflowError
+@@3:
+ ADD EAX, [ECX+8+EDX]
+ JO @@RaiseOverflowError
+@@2:
+ ADD EAX, [ECX+4+EDX]
+ JO @@RaiseOverflowError
+@@1:
+ ADD EAX, [ECX+EDX]
+ JO @@RaiseOverflowError
+ SUB EDX,16
+ JNS @@4
+ POP EBX
+ RET
+@@RaiseOverflowError:
+ POP EBX
+ POP ECX
+ JMP RaiseOverflowError
+end;
+
+procedure RaiseOverflowError;
+begin
+ {$IFNDEF MATH_NOERR}
+ raise Exception.Create(e_IntOverflow, SIntOverflow);
+ {$ENDIF}
+end;
+
+function SUM(const Data: array of Double): Extended;
+{var
+ I: Integer;
+begin
+ Result := 0.0;
+ for I := Low(Data) to High(Data) do
+ Result := Result + Data[I]
+end; }
+asm // IN: EAX = ptr to Data, EDX = High(Data) = Count - 1
+ // Uses 4 accumulators to minimize read-after-write delays and loop overhead
+ // 5 clocks per loop, 4 items per loop = 1.2 clocks per item
+ FLDZ
+ MOV ECX, EDX
+ FLD ST(0)
+ AND EDX, not 3
+ FLD ST(0)
+ AND ECX, 3
+ FLD ST(0)
+ SHL EDX, 3 // count * sizeof(Double) = count * 8
+ JMP @Vector.Pointer[ECX*4]
+@Vector:
+ DD @@1
+ DD @@2
+ DD @@3
+ DD @@4
+@@4: FADD qword ptr [EAX+EDX+24] // 1
+ FXCH ST(3) // 0
+@@3: FADD qword ptr [EAX+EDX+16] // 1
+ FXCH ST(2) // 0
+@@2: FADD qword ptr [EAX+EDX+8] // 1
+ FXCH ST(1) // 0
+@@1: FADD qword ptr [EAX+EDX] // 1
+ FXCH ST(2) // 0
+ SUB EDX, 32
+ JNS @@4
+ FADDP ST(3),ST // ST(3) := ST + ST(3); Pop ST
+ FADD // ST(1) := ST + ST(1); Pop ST
+ FADD // ST(1) := ST + ST(1); Pop ST
+ FWAIT
+end;
+
+function SumOfSquares(const Data: array of Double): Extended;
+var
+ I: Integer;
+begin
+ Result := 0.0;
+ for I := Low(Data) to High(Data) do
+ Result := Result + Sqr(Data[I]);
+end;
+
+procedure SumsAndSquares(const Data: array of Double; var Sum, SumOfSquares: Extended);
+{var
+ I: Integer;
+begin
+ Sum := 0;
+ SumOfSquares := 0;
+ for I := Low(Data) to High(Data) do
+ begin
+ Sum := Sum + Data[I];
+ SumOfSquares := SumOfSquares + Data[I]*Data[I];
+ end;
+end; }
+asm // IN: EAX = ptr to Data
+ // EDX = High(Data) = Count - 1
+ // ECX = ptr to Sum
+ // Est. 17 clocks per loop, 4 items per loop = 4.5 clocks per data item
+ FLDZ // init Sum accumulator
+ PUSH ECX
+ MOV ECX, EDX
+ FLD ST(0) // init Sqr1 accum.
+ AND EDX, not 3
+ FLD ST(0) // init Sqr2 accum.
+ AND ECX, 3
+ FLD ST(0) // init/simulate last data item left in ST
+ SHL EDX, 3 // count * sizeof(Double) = count * 8
+ JMP @Vector.Pointer[ECX*4]
+@Vector:
+ DD @@1
+ DD @@2
+ DD @@3
+ DD @@4
+@@4: FADD // Sqr2 := Sqr2 + Sqr(Data4); Pop Data4
+ FLD qword ptr [EAX+EDX+24] // Load Data1
+ FADD ST(3),ST // Sum := Sum + Data1
+ FMUL ST,ST // Data1 := Sqr(Data1)
+@@3: FLD qword ptr [EAX+EDX+16] // Load Data2
+ FADD ST(4),ST // Sum := Sum + Data2
+ FMUL ST,ST // Data2 := Sqr(Data2)
+ FXCH // Move Sqr(Data1) into ST(0)
+ FADDP ST(3),ST // Sqr1 := Sqr1 + Sqr(Data1); Pop Data1
+@@2: FLD qword ptr [EAX+EDX+8] // Load Data3
+ FADD ST(4),ST // Sum := Sum + Data3
+ FMUL ST,ST // Data3 := Sqr(Data3)
+ FXCH // Move Sqr(Data2) into ST(0)
+ FADDP ST(3),ST // Sqr1 := Sqr1 + Sqr(Data2); Pop Data2
+@@1: FLD qword ptr [EAX+EDX] // Load Data4
+ FADD ST(4),ST // Sum := Sum + Data4
+ FMUL ST,ST // Sqr(Data4)
+ FXCH // Move Sqr(Data3) into ST(0)
+ FADDP ST(3),ST // Sqr1 := Sqr1 + Sqr(Data3); Pop Data3
+ SUB EDX,32
+ JNS @@4
+ FADD // Sqr2 := Sqr2 + Sqr(Data4); Pop Data4
+ POP ECX
+ FADD // Sqr1 := Sqr2 + Sqr1; Pop Sqr2
+ FXCH // Move Sum1 into ST(0)
+ MOV EAX, SumOfSquares
+ FSTP tbyte ptr [ECX] // Sum := Sum1; Pop Sum1
+ FSTP tbyte ptr [EAX] // SumOfSquares := Sum1; Pop Sum1
+ FWAIT
+end;
+
+function TotalVariance(const Data: array of Double): Extended;
+var
+ Sum, SumSquares: Extended;
+begin
+ SumsAndSquares(Data, Sum, SumSquares);
+ Result := SumSquares - Sqr(Sum)/(High(Data) - Low(Data) + 1);
+end;
+
+function Variance(const Data: array of Double): Extended;
+begin
+ Result := TotalVariance(Data) / (High(Data) - Low(Data))
+end;
+
+
+{ Depreciation functions. }
+
+function DoubleDecliningBalance(Cost, Salvage: Extended; Life, Period: Integer): Extended;
+{ dv := cost * (1 - 2/life)**(period - 1)
+ DDB = (2/life) * dv
+ if DDB > dv - salvage then DDB := dv - salvage
+ if DDB < 0 then DDB := 0
+}
+var
+ DepreciatedVal, Factor: Extended;
+begin
+ Result := 0;
+ if (Period < 1) or (Life < Period) or (Life < 1) or (Cost <= Salvage) then
+ Exit;
+
+ {depreciate everything in period 1 if life is only one or two periods}
+ if ( Life <= 2 ) then
+ begin
+ if ( Period = 1 ) then
+ DoubleDecliningBalance:=Cost-Salvage
+ else
+ DoubleDecliningBalance:=0; {all depreciation occurred in first period}
+ exit;
+ end;
+ Factor := 2.0 / Life;
+
+ DepreciatedVal := Cost * IntPower((1.0 - Factor), Period - 1);
+ {DepreciatedVal is Cost-(sum of previous depreciation results)}
+
+ Result := Factor * DepreciatedVal;
+ {Nominal computed depreciation for this period. The rest of the
+ function applies limits to this nominal value. }
+
+ {Only depreciate until total depreciation equals cost-salvage.}
+ if Result > DepreciatedVal - Salvage then
+ Result := DepreciatedVal - Salvage;
+
+ {No more depreciation after salvage value is reached. This is mostly a nit.
+ If Result is negative at this point, it's very close to zero.}
+ if Result < 0.0 then
+ Result := 0.0;
+end;
+
+function SLNDepreciation(Cost, Salvage: Extended; Life: Integer): Extended;
+{ Spreads depreciation linearly over life. }
+begin
+ {$IFNDEF MATH_NOERR}
+ if Life < 1 then ArgError('SLNDepreciation');
+ {$ENDIF}
+ Result := (Cost - Salvage) / Life
+end;
+
+function SYDDepreciation(Cost, Salvage: Extended; Life, Period: Integer): Extended;
+{ SYD = (cost - salvage) * (life - period + 1) / (life*(life + 1)/2) }
+{ Note: life*(life+1)/2 = 1+2+3+...+life "sum of years"
+ The depreciation factor varies from life/sum_of_years in first period = 1
+ downto 1/sum_of_years in last period = life.
+ Total depreciation over life is cost-salvage.}
+var
+ X1, X2: Extended;
+begin
+ Result := 0;
+ if (Period < 1) or (Life < Period) or (Cost <= Salvage) then Exit;
+ X1 := 2 * (Life - Period + 1);
+ X2 := Life * (Life + 1);
+ Result := (Cost - Salvage) * X1 / X2
+end;
+
+{ Discounted cash flow functions. }
+
+function InternalRateOfReturn(Guess: Extended; const CashFlows: array of Double): Extended;
+{
+Use Newton's method to solve NPV = 0, where NPV is a polynomial in
+x = 1/(1+rate). Split the coefficients into negative and postive sets:
+ neg + pos = 0, so pos = -neg, so -neg/pos = 1
+Then solve:
+ log(-neg/pos) = 0
+
+ Let t = log(1/(1+r) = -LnXP1(r)
+ then r = exp(-t) - 1
+Iterate on t, then use the last equation to compute r.
+}
+var
+ T, Y: Extended;
+ Poly: TPoly;
+ K, Count: Integer;
+
+ function ConditionP(const CashFlows: array of Double): Integer;
+ { Guarantees existence and uniqueness of root. The sign of payments
+ must change exactly once, the net payout must be always > 0 for
+ first portion, then each payment must be >= 0.
+ Returns: 0 if condition not satisfied, > 0 if condition satisfied
+ and this is the index of the first value considered a payback. }
+ var
+ X: Double;
+ I, K: Integer;
+ begin
+ K := High(CashFlows);
+ while (K >= 0) and (CashFlows[K] >= 0.0) do Dec(K);
+ Inc(K);
+ if K > 0 then
+ begin
+ X := 0.0;
+ I := 0;
+ while I < K do begin
+ X := X + CashFlows[I];
+ if X >= 0.0 then
+ begin
+ K := 0;
+ Break
+ end;
+ Inc(I)
+ end
+ end;
+ ConditionP := K
+ end;
+
+begin
+ InternalRateOfReturn := 0;
+ K := ConditionP(CashFlows);
+ {$IFNDEF MATH_NOERR}
+ if K < 0 then ArgError('InternalRateOfReturn');
+ {$ENDIF}
+ if K = 0 then
+ begin
+ {$IFNDEF MATH_NOERR}
+ if Guess <= -1.0 then ArgError('InternalRateOfReturn');
+ {$ENDIF}
+ T := -LnXP1(Guess)
+ end else
+ T := 0.0;
+ for Count := 1 to MaxIterations do
+ begin
+ PolyX(CashFlows, Exp(T), Poly);
+ {$IFNDEF MATH_NOERR}
+ if Poly.Pos <= Poly.Neg then ArgError('InternalRateOfReturn');
+ {$ENDIF}
+ if (Poly.Neg >= 0.0) or (Poly.Pos <= 0.0) then
+ begin
+ InternalRateOfReturn := -1.0;
+ Exit;
+ end;
+ with Poly do
+ Y := Ln(-Neg / Pos) / (DNeg / Neg - DPos / Pos);
+ T := T - Y;
+ if RelSmall(Y, T) then
+ begin
+ InternalRateOfReturn := Exp(-T) - 1.0;
+ Exit;
+ end
+ end;
+ {$IFNDEF MATH_NOERR}
+ ArgError('InternalRateOfReturn');
+ {$ENDIF}
+end;
+
+function NetPresentValue(Rate: Extended; const CashFlows: array of Double;
+ PaymentTime: TPaymentTime): Extended;
+{ Caution: The sign of NPV is reversed from what would be expected for standard
+ cash flows!}
+var
+ rr: Extended;
+ I: Integer;
+begin
+ {$IFNDEF MATH_NOERR}
+ if Rate <= -1.0 then ArgError('NetPresentValue');
+ {$ENDIF}
+ rr := 1/(1+Rate);
+ result := 0;
+ for I := High(CashFlows) downto Low(CashFlows) do
+ result := rr * result + CashFlows[I];
+ if PaymentTime = ptEndOfPeriod then result := rr * result;
+end;
+
+{ Annuity functions. }
+
+{---------------
+From the point of view of A, amounts received by A are positive and
+amounts disbursed by A are negative (e.g. a borrower's loan repayments
+are regarded by the borrower as negative).
+
+Given interest rate r, number of periods n:
+ compound(r, n) = (1 + r)**n "Compounding growth factor"
+ annuity(r, n) = (compound(r, n)-1) / r "Annuity growth factor"
+
+Given future value fv, periodic payment pmt, present value pv and type
+of payment (start, 1 , or end of period, 0) pmtTime, financial variables satisfy:
+
+ fv = -pmt*(1 + r*pmtTime)*annuity(r, n) - pv*compound(r, n)
+
+For fv, pv, pmt:
+
+ C := compound(r, n)
+ A := (1 + r*pmtTime)*annuity(r, n)
+ Compute both at once in Annuity2.
+
+ if C > 1E16 then A = C/r, so:
+ fv := meaningless
+ pv := -pmt*(pmtTime+1/r)
+ pmt := -pv*r/(1 + r*pmtTime)
+ else
+ fv := -pmt(1+r*pmtTime)*A - pv*C
+ pv := (-pmt(1+r*pmtTime)*A - fv)/C
+ pmt := (-pv*C-fv)/((1+r*pmtTime)*A)
+---------------}
+
+function PaymentParts(Period, NPeriods: Integer; Rate, PresentValue,
+ FutureValue: Extended; PaymentTime: TPaymentTime; var IntPmt: Extended):
+ Extended;
+var
+ Crn:extended; { =Compound(Rate,NPeriods) }
+ Crp:extended; { =Compound(Rate,Period-1) }
+ Arn:extended; { =AnnuityF(Rate,NPeriods) }
+
+begin
+ {$IFNDEF MATH_NOERR}
+ if Rate <= -1.0 then ArgError('PaymentParts');
+ {$ENDIF}
+ Crp:=Compound(Rate,Period-1);
+ Arn:=Annuity2(Rate,NPeriods,PaymentTime,Crn);
+ IntPmt:=(FutureValue*(Crp-1)-PresentValue*(Crn-Crp))/Arn;
+ PaymentParts:=(-FutureValue-PresentValue)*Crp/Arn;
+end;
+
+function FutureValue(Rate: Extended; NPeriods: Integer; Payment, PresentValue:
+ Extended; PaymentTime: TPaymentTime): Extended;
+var
+ Annuity, CompoundRN: Extended;
+begin
+ {$IFNDEF MATH_NOERR}
+ if Rate <= -1.0 then ArgError('FutureValue');
+ {$ENDIF}
+ Annuity := Annuity2(Rate, NPeriods, PaymentTime, CompoundRN);
+ {$IFNDEF MATH_NOERR}
+ if CompoundRN > 1.0E16 then ArgError('FutureValue');
+ {$ENDIF}
+ FutureValue := -Payment * Annuity - PresentValue * CompoundRN
+end;
+
+function InterestPayment(Rate: Extended; Period, NPeriods: Integer; PresentValue,
+ FutureValue: Extended; PaymentTime: TPaymentTime): Extended;
+var
+ Crp:extended; { compound(rate,period-1)}
+ Crn:extended; { compound(rate,nperiods)}
+ Arn:extended; { annuityf(rate,nperiods)}
+begin
+ {$IFNDEF MATH_NOERR}
+ if (Rate <= -1.0)
+ or (Period < 1) or (Period > NPeriods) then ArgError('InterestPayment');
+ {$ENDIF}
+ Crp:=Compound(Rate,Period-1);
+ Arn:=Annuity2(Rate,Nperiods,PaymentTime,Crn);
+ InterestPayment:=(FutureValue*(Crp-1)-PresentValue*(Crn-Crp))/Arn;
+end;
+
+function InterestRate(NPeriods: Integer;
+ Payment, PresentValue, FutureValue: Extended; PaymentTime: TPaymentTime): Extended;
+{
+Given:
+ First and last payments are non-zero and of opposite signs.
+ Number of periods N >= 2.
+Convert data into cash flow of first, N-1 payments, last with
+first < 0, payment > 0, last > 0.
+Compute the IRR of this cash flow:
+ 0 = first + pmt*x + pmt*x**2 + ... + pmt*x**(N-1) + last*x**N
+where x = 1/(1 + rate).
+Substitute x = exp(t) and apply Newton's method to
+ f(t) = log(pmt*x + ... + last*x**N) / -first
+which has a unique root given the above hypotheses.
+}
+var
+ X, Y, Z, First, Pmt, Last, T, ET, EnT, ET1: Extended;
+ Count: Integer;
+ Reverse: Boolean;
+
+ function LostPrecision(X: Extended): Boolean;
+ asm
+ XOR EAX, EAX
+ MOV BX,WORD PTR X+8
+ INC EAX
+ AND EBX, $7FF0
+ JZ @@1
+ CMP EBX, $7FF0
+ JE @@1
+ XOR EAX,EAX
+ @@1:
+ end;
+
+begin
+ Result := 0;
+ {$IFNDEF MATH_NOERR}
+ if NPeriods <= 0 then ArgError('InterestRate');
+ {$ENDIF}
+ Pmt := Payment;
+ if PaymentTime = ptEndOfPeriod then
+ begin
+ X := PresentValue;
+ Y := FutureValue + Payment
+ end
+ else
+ begin
+ X := PresentValue + Payment;
+ Y := FutureValue
+ end;
+ First := X;
+ Last := Y;
+ Reverse := False;
+ if First * Payment > 0.0 then
+ begin
+ Reverse := True;
+ T := First;
+ First := Last;
+ Last := T
+ end;
+ if first > 0.0 then
+ begin
+ First := -First;
+ Pmt := -Pmt;
+ Last := -Last
+ end;
+ {$IFNDEF MATH_NOERR}
+ if (First = 0.0) or (Last < 0.0) then ArgError('InterestRate');
+ {$ENDIF}
+ T := 0.0; { Guess at solution }
+ for Count := 1 to MaxIterations do
+ begin
+ EnT := Exp(NPeriods * T);
+ if {LostPrecision(EnT)} ent=(ent+1) then
+ begin
+ Result := -Pmt / First;
+ if Reverse then
+ Result := Exp(-LnXP1(Result)) - 1.0;
+ Exit;
+ end;
+ ET := Exp(T);
+ ET1 := ET - 1.0;
+ if ET1 = 0.0 then
+ begin
+ X := NPeriods;
+ Y := X * (X - 1.0) / 2.0
+ end
+ else
+ begin
+ X := ET * (Exp((NPeriods - 1) * T)-1.0) / ET1;
+ Y := (NPeriods * EnT - ET - X * ET) / ET1
+ end;
+ Z := Pmt * X + Last * EnT;
+ Y := Ln(Z / -First) / ((Pmt * Y + Last * NPeriods *EnT) / Z);
+ T := T - Y;
+ if RelSmall(Y, T) then
+ begin
+ if not Reverse then T := -T;
+ InterestRate := Exp(T)-1.0;
+ Exit;
+ end
+ end;
+ {$IFNDEF MATH_NOERR}
+ ArgError('InterestRate');
+ {$ENDIF}
+end;
+
+function NumberOfPeriods(Rate, Payment, PresentValue, FutureValue: Extended;
+ PaymentTime: TPaymentTime): Extended;
+
+{ If Rate = 0 then nper := -(pv + fv) / pmt
+ else cf := pv + pmt * (1 + rate*pmtTime) / rate
+ nper := LnXP1(-(pv + fv) / cf) / LnXP1(rate) }
+
+var
+ PVRPP: Extended; { =PV*Rate+Payment } {"initial cash flow"}
+ T: Extended;
+
+begin
+ {$IFNDEF MATH_NOERR}
+ if Rate <= -1.0 then ArgError('NumberOfPeriods');
+ {$ENDIF}
+
+{whenever both Payment and PaymentTime are given together, the PaymentTime has the effect
+ of modifying the effective Payment by the interest accrued on the Payment}
+
+ if ( PaymentTime=ptStartOfPeriod ) then
+ Payment:=Payment*(1+Rate);
+
+{if the payment exactly matches the interest accrued periodically on the
+ presentvalue, then an infinite number of payments are going to be
+ required to effect a change from presentvalue to futurevalue. The
+ following catches that specific error where payment is exactly equal,
+ but opposite in sign to the interest on the present value. If PVRPP
+ ("initial cash flow") is simply close to zero, the computation will
+ be numerically unstable, but not as likely to cause an error.}
+
+ PVRPP:=PresentValue*Rate+Payment;
+ {$IFNDEF MATH_NOERR}
+ if PVRPP=0 then ArgError('NumberOfPeriods');
+ {$ENDIF}
+
+ { 6.1E-5 approx= 2**-14 }
+ if ( EAbs(Rate)<6.1E-5 ) then
+ Result:=-(PresentValue+FutureValue)/PVRPP
+ else
+ begin
+
+{starting with the initial cash flow, each compounding period cash flow
+ should result in the current value approaching the final value. The
+ following test combines a number of simultaneous conditions to ensure
+ reasonableness of the cashflow before computing the NPER.}
+
+ T:= -(PresentValue+FutureValue)*Rate/PVRPP;
+ {$IFNDEF MATH_NOERR}
+ if T<=-1.0 then ArgError('NumberOfPeriods');
+ {$ENDIF}
+ Result := LnXP1(T) / LnXP1(Rate)
+ end;
+ NumberOfPeriods:=Result;
+end;
+
+function Payment(Rate: Extended; NPeriods: Integer; PresentValue, FutureValue:
+ Extended; PaymentTime: TPaymentTime): Extended;
+var
+ Annuity, CompoundRN: Extended;
+begin
+ {$IFNDEF MATH_NOERR}
+ if Rate <= -1.0 then ArgError('Payment');
+ {$ENDIF}
+ Annuity := Annuity2(Rate, NPeriods, PaymentTime, CompoundRN);
+ if CompoundRN > 1.0E16 then
+ Payment := -PresentValue * Rate / (1 + Integer(PaymentTime) * Rate)
+ else
+ Payment := (-PresentValue * CompoundRN - FutureValue) / Annuity
+end;
+
+function PeriodPayment(Rate: Extended; Period, NPeriods: Integer;
+ PresentValue, FutureValue: Extended; PaymentTime: TPaymentTime): Extended;
+var
+ Junk: Extended;
+begin
+ {$IFNDEF MATH_NOERR}
+ if (Rate <= -1.0) or (Period < 1) or (Period > NPeriods) then ArgError('PeriodPayment');
+ {$ENDIF}
+ PeriodPayment := PaymentParts(Period, NPeriods, Rate, PresentValue,
+ FutureValue, PaymentTime, Junk);
+end;
+
+function PresentValue(Rate: Extended; NPeriods: Integer; Payment, FutureValue:
+ Extended; PaymentTime: TPaymentTime): Extended;
+var
+ Annuity, CompoundRN: Extended;
+begin
+ {$IFNDEF MATH_NOERR}
+ if Rate <= -1.0 then ArgError('PresentValue');
+ {$ENDIF}
+ Annuity := Annuity2(Rate, NPeriods, PaymentTime, CompoundRN);
+ if CompoundRN > 1.0E16 then
+ PresentValue := -(Payment / Rate * Integer(PaymentTime) * Payment)
+ else
+ PresentValue := (-Payment * Annuity - FutureValue) / CompoundRN
+end;
+
+{------------------------------------------------------------------------------}
+
+function IsPowerOf2( i: Integer ): Boolean; { Result = (i <> 0) and (i and (i-1) = 0); }
+asm
+ OR EAX,EAX
+ JZ @@exit // 0 íå ÿâëÿåòñÿ ñòåïåíüþ ÷èñëà 2
+ LEA EDX, [EAX-1]
+ OR EAX,EDX
+ SETZ AL // ÷èñëî ÿâëÿåòñÿ ñòåïåíüþ 2, åñëè (i & (i-1)) = 0, ò.å. åñëè ïîñëå
+ // îáíóëåíèÿ ìëàäøåé 1 â ÷èñëå áîëüøå íå îñòàëîñü áèòîâ 1.
+@@exit:
+end;
+
+function Low1( i: Integer ): Integer; { Result := i and (-i); }
+asm
+ MOV EDX, EAX
+ NEG EAX
+ AND EAX, EDX
+end;
+
+function Low0( i: Integer ): Integer; { Result := -i and (i+1); }
+asm
+ LEA EDX, [EAX+1]
+ NEG EAX
+ AND EAX, EDX
+end;
+
+function count_1_bits_in_byte( x: Byte ): Byte;
+ asm
+ MOV CL, AL
+@@loop:
+ SHR CL, 1
+ JZ @@exit
+ SUB AL, CL
+ JMP @@loop
+@@exit:
+ end;
+
+function count_1_bits_in_dword( x: Integer ): Integer;
+ asm
+ MOV ECX, EAX
+ JMP @@go
+@@loop:
+ SUB EAX, ECX
+@@go:
+ SHR ECX, 1
+ JNZ @@loop
+ end;
+
+end.
diff --git a/plugins/Libs/make.bat b/plugins/Libs/make.bat
new file mode 100644
index 0000000000..39a182dae2
--- /dev/null
+++ b/plugins/Libs/make.bat
@@ -0,0 +1,14 @@
+@echo off
+set myopts=
+
+if /i '%2' == 'fpc' (
+ ..\FPC\bin\fpc.exe %myopts% %1 %3 %4 %5 %6 %7 %8 %9
+) else if /i '%2' == 'fpc64' (
+ ..\FPC\bin64\ppcrossx64.exe %myopts% %1 %3 %4 %5 %6 %7 %8 %9
+) else if /i '%2' == 'xe2' (
+ ..\XE2\bin\dcc32.exe %myopts% %1 %3 %4 %5 %6 %7 %8 %9
+) else if /i '%2' == 'xe64' (
+ ..\XE2\bin\dcc64.exe %myopts% %1 %3 %4 %5 %6 %7 %8 %9
+) else (
+ ..\delphi\dcc32 %myopts% %1 %2 %3 %4 %5 %6 %7 %8 %9
+)
diff --git a/plugins/Libs/visual_xp_styles.inc b/plugins/Libs/visual_xp_styles.inc
new file mode 100644
index 0000000000..5db52144c1
--- /dev/null
+++ b/plugins/Libs/visual_xp_styles.inc
@@ -0,0 +1,1448 @@
+// Name: KOL Addon - Visual XP Styles
+// Rev.: 1.99 + KOL 3.00.A
+// Date: 02 oct 2010
+// Author: MTsv DN
+// Thanks: mdw, Vladimir Kladov
+
+{$IFDEF _FPC}
+const
+ clGrey = TColor($808080);
+ clLtGrey = TColor($C0C0C0);
+ clDkGrey = TColor($808080);
+{$ENDIF}
+
+procedure ConvertBitmap2Grayscale(var Bmp: PBitmap);
+type
+ TRGBArray = array[0..32767] of TRGBTriple;
+ PRGBArray = ^TRGBArray;
+var
+ x, y, Gray: Integer;
+ Row: PRGBArray;
+ R, G, B : Byte;
+ TrColor : Integer;
+begin
+ Bmp.PixelFormat := pf24bit;
+ TrColor := Bmp.Pixels[Bmp.Width - 1, 0];
+ for y := 0 to Bmp.Height - 1 do
+ begin
+ Row := Bmp.ScanLine[y];
+ for x := 0 to Bmp.Width - 1 do
+ begin
+ R := LoByte(LoWord(TrColor));
+ G := HiByte(LoWord(TrColor));
+ B := LoByte(HiWord(TrColor));
+ if (Row[x].rgbtRed = R) and
+ (Row[x].rgbtGreen = G) and
+ (Row[x].rgbtBlue = B) then continue;
+ Gray := (Row[x].rgbtRed + Row[x].rgbtGreen + Row[x].rgbtBlue) div 3;
+ Row[x].rgbtRed := Gray;
+ Row[x].rgbtGreen := Gray;
+ Row[x].rgbtBlue := Gray;
+ end;
+ end;
+end;
+//********************* Creating font on Sender font base ********************//
+function CreateNewFont(Sender : PControl): HFont;
+const
+ CLEARTYPE_QUALITY = 5;
+var
+ fnWeight : Integer;
+ fnItalic, fnUnderline, fnStrikeOut,
+ fnQuality, fnPitch : DWORD;
+begin
+ // Font style
+ if Sender.Font.FontStyle = [fsBold] then fnWeight := 700 else fnWeight := 0;
+ if Sender.Font.FontStyle = [fsItalic] then fnItalic := DWORD(TRUE) else fnItalic := DWORD(FALSE);
+ if Sender.Font.FontStyle = [fsUnderline] then fnUnderline := DWORD(TRUE) else fnUnderline := DWORD(FALSE);
+ if Sender.Font.FontStyle = [fsStrikeOut] then fnStrikeOut := DWORD(TRUE) else fnStrikeOut := DWORD(FALSE);
+
+ // Font quality
+ case Sender.Font.FontQuality of
+ fqAntialiased: fnQuality := DWORD(ANTIALIASED_QUALITY);
+ {$IFDEF AUTO_REPLACE_CLEARTYPE}
+ fqClearType: fnQuality := DWORD(CLEARTYPE_QUALITY);
+ {$ELSE}
+ fqClearType: fnQuality := DWORD(ANTIALIASED_QUALITY);
+ {$ENDIF}
+ fqDraft: fnQuality := DWORD(DRAFT_QUALITY);
+ fqNonAntialiased: fnQuality := DWORD(NONANTIALIASED_QUALITY);
+ fqProof: fnQuality := DWORD(PROOF_QUALITY);
+ {fqDefault:} else fnQuality := DWORD(DEFAULT_QUALITY);
+ end;
+
+ // Font pitch
+ case Sender.Font.FontPitch of
+ fpFixed: fnPitch := DWORD(FIXED_PITCH);
+ fpVariable: fnPitch := DWORD(VARIABLE_PITCH);
+ {fpDefault:} else fnPitch := DWORD(DEFAULT_PITCH);
+ end;
+
+ Result := CreateFont(Sender.Font.FontHeight,
+ Sender.Font.FontWidth,
+ 0,
+ Sender.Font.FontOrientation,
+ fnWeight,
+ fnItalic,
+ fnUnderline,
+ fnStrikeOut,
+ Sender.Font.FontCharset,
+ OUT_DEFAULT_PRECIS,
+ CLIP_DEFAULT_PRECIS,
+ fnQuality,
+ fnPitch,
+ PKOLChar(Sender.Font.FontName));
+end;
+//***************************** Initializing themes **************************//
+function InitThemes : boolean;
+begin
+ Result := false;
+ ThemeLibrary := LoadLibrary(themelib);
+ if ThemeLibrary > 0 then
+ begin
+ OpenThemeData := GetProcAddress(ThemeLibrary, 'OpenThemeData');
+ DrawThemeBackground := GetProcAddress(ThemeLibrary, 'DrawThemeBackground');
+ IsThemeBackgroundPartiallyTransparent := GetProcAddress(ThemeLibrary, 'IsThemeBackgroundPartiallyTransparent');
+ DrawThemeParentBackground := GetProcAddress(ThemeLibrary, 'DrawThemeParentBackground');
+ DrawThemeText := GetProcAddress(ThemeLibrary, 'DrawThemeText');
+ CloseThemeData := GetProcAddress(ThemeLibrary, 'CloseThemeData');
+ IsThemeActive := GetProcAddress(ThemeLibrary, 'IsThemeActive');
+ IsAppThemed := GetProcAddress(ThemeLibrary, 'IsAppThemed');
+ GetThemeColor := GetProcAddress(ThemeLibrary, 'GetThemeColor');
+ Result := true;
+ end;
+end;
+//***************************** Deinitializing themes ************************//
+procedure DeinitThemes;
+begin
+ if ThemeLibrary > 0 then
+ begin
+ FreeLibrary(ThemeLibrary);
+ ThemeLibrary := 0;
+ OpenThemeData := nil;
+ DrawThemeBackground := nil;
+ IsThemeBackgroundPartiallyTransparent := nil;
+ DrawThemeParentBackground := nil;
+ CloseThemeData := nil;
+ IsAppThemed := nil;
+ IsThemeActive := nil;
+ GetThemeColor := nil;
+ end;
+end;
+//****************************** Checking themes *****************************//
+procedure CheckThemes;
+// Check Manifest file or resource
+ function IsManifestFilePresent : boolean;
+ begin
+ Result := false;
+ if FileExists(ParamStr(0) + '.manifest') then //dufa. â ñëó÷àå ñ DLL ExePath âåðíåò ïóòü äî íåå, à íå äî EXE
+ begin
+ Result := true;
+ exit;
+ end;
+ if FindResource(hInstance, MAKEINTRESOURCE(1), MakeIntResource(24)) <> 0 then
+ Result := true;
+ end;
+// Check activity themes
+ function UseThemes: Boolean;
+ begin
+ if (ThemeLibrary > 0) then Result := IsThemeActive
+ else Result := False;
+ end;
+begin
+ AppTheming := false;
+ if IsManifestFilePresent then
+ if InitThemes then
+ begin
+ if UseThemes then
+ AppTheming := true;
+ DeinitThemes;
+ end;
+end;
+//****************************** Drawing Splitter ****************************//
+procedure WndSplitterXPDraw( Dummy : Pointer; Sender: PControl; DC: HDC );
+const
+ Bit : Word = $FF;
+var
+ B, Brush : HBRUSH;
+ fDC : HDC;
+ Bmp : HBITMAP;
+begin
+ // Checking user owner-draw
+ if Assigned(Sender.EV.fOnPaint) and (@Sender.EV.fOnPaint <> @WndSplitterXPDraw) then
+ begin
+ Sender.EV.fOnPaint(Sender, DC);
+ exit;
+ end;
+
+ // Draw back layer
+ Brush := CreateSolidBrush(Color2RGB(Sender.fParent.Color));
+ fDC := SelectObject(DC, Brush);
+ FillRect(DC, Sender.ClientRect, Brush);
+ SelectObject(DC, fDC);
+ DeleteObject(Brush);
+
+ // Creating brush and pen
+ if {$IFDEF USE_FLAGS} G4_Pressed in Sender.fFlagsG4
+ {$ELSE} Sender.fPressed {$ENDIF} then
+ begin
+ Bmp := CreateBitmap(2, 2, 1, 1, @Bit);
+ B := CreatePatternBrush(Bmp);
+ fDC := SelectObject(DC, B);
+ // Drawing splitter
+ PatBlt (DC, 0, 0, Sender.Width, Sender.Height, PATINVERT);
+ // Destroying brush and pen
+ SelectObject(DC, fDC);
+ DeleteObject(B);
+ DeleteObject(Bmp);
+ end;
+end;
+//*************************** Drawing TabControl Page ************************//
+procedure WndTabXPDraw( Dummy : Pointer; Sender: PControl; DC: HDC );
+var
+ hThemes : THandle;
+ Color : COLORREF;
+ Brush : HBRUSH;
+ fDC : HDC;
+begin
+ // Checking user owner-draw
+ if Assigned(Sender.EV.fOnPaint) and (@Sender.EV.fOnPaint <> @WndTabXPDraw) then
+ begin
+ Sender.EV.fOnPaint(Sender, DC);
+ exit;
+ end;
+ hThemes := OpenThemeData(Sender.fHandle, 'TAB');
+ if hThemes <> 0 then
+ begin
+ GetThemeColor(hThemes, 10, 0, 3805, Color);
+ Sender.Color := Color2RGB(Color);
+ Brush := CreateSolidBrush(Color2RGB(Color));
+ fDC := SelectObject(DC, Brush);
+ FillRect(DC, Sender.ClientRect, Brush);
+ SelectObject(DC, fDC);
+ DeleteObject(Brush);
+ CloseThemeData(hThemes);
+ end;
+end;
+//*************************** Drawing Panel control **************************//
+procedure WndPanelXPResize( Dummy : Pointer; Sender: PObj );
+var
+ R : TRect;
+begin
+ R := PControl(Sender).ClientRect;
+ InvalidateRect(PControl(Sender).fHandle, @R, False);
+end;
+
+procedure WndPanelXPDraw( Dummy : Pointer; Sender: PControl; DC: HDC );
+var
+ RClient, RText : TRect;
+ LPos : DWORD;
+ S : KOLString;
+ F : HFONT;
+ fDC1, fDC2 : HDC;
+ hThemes : THandle;
+ TxtColor, Color : COLORREF;
+ Brush : HBRUSH;
+ Pen : HPEN;
+begin
+ // Checking user owner-draw
+ if Assigned(Sender.EV.fOnPaint) and (@Sender.EV.fOnPaint <> @WndPanelXPDraw) then
+ begin
+ Sender.EV.fOnPaint(Sender, DC);
+ exit;
+ end;
+
+ // Getting rects
+ RClient := Sender.ClientRect;
+ // Getting text and text flags
+ S := Sender.fCaption;
+ LPos := 0;
+ if S <> '' then
+ begin
+ case Sender.fVerticalAlign of
+ vaTop: LPos := DT_TOP;
+ vaCenter: LPos := DT_VCENTER;
+ vaBottom: LPos := DT_BOTTOM;
+ end;
+ case Sender.fTextAlign of
+ taLeft: LPos := LPos or DT_LEFT;
+ taCenter: LPos := LPos or DT_CENTER;
+ taRight: LPos := LPos or DT_RIGHT;
+ end;
+ end;
+
+ // Draw back layer
+ if (Sender.EdgeStyle = esTransparent) or
+ ({$IFDEF USE_FLAGS} (G2_Transparent in Sender.fFlagsG2)
+ {$ELSE} Sender.fTransparent {$ENDIF}) then else
+ begin
+ Brush := CreateSolidBrush(Color2RGB(Sender.fParent.Color));
+ fDC1 := SelectObject(DC, Brush);
+ FillRect(DC, RClient, Brush);
+
+ case Sender.EdgeStyle of
+ esRaised, esLowered:
+ begin
+ Sender.fStyle.Value := Sender.fStyle.Value and (not SS_SUNKEN) and (not WS_DLGFRAME);
+ Sender.fExStyle := Sender.fExStyle and (not WS_EX_STATICEDGE) or WS_EX_WINDOWEDGE;
+
+ Pen := CreatePen(PS_SOLID, 1, Color2RGB(clLtGrey));
+ fDC2 := SelectObject(DC, Pen);
+ RoundRect(DC, RClient.Left, RClient.Top,
+ RClient.Right, RClient.Bottom, 5, 5);
+ SelectObject(DC, fDC2);
+ DeleteObject(Pen);
+ end;
+ end;
+
+ SelectObject(DC, fDC1);
+ DeleteObject(Brush);
+ end;
+
+ if S <> '' then
+ begin
+ hThemes := OpenThemeData(Sender.fHandle, 'button');
+ Color := Sender.Font.Color;
+ if hThemes <> 0 then
+ begin
+ {$IFDEF USE_FLAGS}
+ if (F3_Disabled in Sender.fStyle.f3_Style) then
+ {$ELSE}
+ if not Sender.fEnabled then
+ {$ENDIF}
+ GetThemeColor(hThemes, 1, 4, 3803, Color);
+ CloseThemeData(hThemes);
+ end;
+ RText := MakeRect(2, 2, Sender.Width-2, Sender.Height-2);
+
+ // Create font
+ F := CreateNewFont(Sender);
+ fDC1 := SelectObject(DC, F);
+ // Draw text
+ SetBkMode(DC, TRANSPARENT);
+ TxtColor := SetTextColor(DC, Color2RGB(Color));
+ DrawText(DC, PKOLChar(S), Length(S), RText, LPos or DT_SINGLELINE);
+ // Backup color
+ SetTextColor(DC, Color2RGB(TxtColor));
+ SetBkMode(DC, OPAQUE);
+ // Destroying font
+ SelectObject(DC, fDC1);
+ DeleteObject(F);
+ end;
+end;
+//************************** Drawing GroupBox control ************************//
+procedure WndGroupBoxXPDraw( Dummy : Pointer; Sender: PControl; DC: HDC );
+var
+ hThemes : THandle;
+ RClient, RText, RClipMain, RClipLeft, RClipRight : TRect;
+ LPos, fState : DWORD;
+ S : KOLWideString;
+ F : HFONT;
+ fDC : HDC;
+ TxtColor, Color : COLORREF;
+ TextWidth, TextHeight : Integer;
+begin
+ // Checking user owner-draw
+ if Assigned(Sender.EV.fOnPaint) and (@Sender.EV.fOnPaint <> @WndGroupBoxXPDraw) then
+ begin
+ Sender.EV.fOnPaint(Sender, DC);
+ exit;
+ end;
+
+ // Getting text and text flags
+ LPos := 0;
+ case Sender.fVerticalAlign of
+ vaTop: LPos := DT_TOP;
+ vaCenter: LPos := DT_VCENTER;
+ vaBottom: LPos := DT_BOTTOM;
+ end;
+ case Sender.fTextAlign of
+ taLeft: LPos := LPos or DT_LEFT;
+ taCenter: LPos := LPos or DT_CENTER;
+ taRight: LPos := LPos or DT_RIGHT;
+ end;
+ S := KOLWideString( Sender.fCaption );
+
+ // Getting rects
+ TextWidth := Sender.Canvas.WTextWidth(S);
+ TextHeight := Sender.Canvas.WTextHeight(S);
+
+ RClient := Sender.ClientRect;
+ RClient.Left := RClient.Left - Sender.MarginLeft;
+ RClient.Top := RClient.Top - Sender.MarginTop + (TextHeight div 2);
+ RClient.Right := RClient.Right + Sender.MarginRight;
+ RClient.Bottom := RClient.Bottom + Sender.MarginBottom;
+
+ case Sender.fTextAlign of
+ taCenter:
+ begin
+ RText := MakeRect(((RClient.Right div 2) - (TextWidth div 2)) - 2,
+ RClient.Top-6,
+ ((RClient.Right div 2) + (TextWidth div 2)) + 2,
+ TextHeight + (RClient.Top-6));
+ RClipLeft := MakeRect(RClient.Left,
+ RClient.Top,
+ ((RClient.Right div 2) - (TextWidth div 2)) - 2,
+ TextHeight + (RClient.Top-6));
+ RClipRight := MakeRect(((RClient.Right div 2) + (TextWidth div 2)) + 2,
+ RClient.Top-6,
+ RClient.Right,
+ TextHeight + (RClient.Top-6));
+ end;
+ taRight:
+ begin
+ RText := MakeRect((RClient.Right-4) - TextWidth,
+ RClient.Top-6,
+ RClient.Right-4,
+ TextHeight + (RClient.Top-6));
+ RClipLeft := MakeRect(RClient.Left,
+ RClient.Top,
+ (RClient.Right-4) - TextWidth,
+ TextHeight + (RClient.Top-6));
+ RClipRight := MakeRect(RClient.Right-4,
+ RClient.Top-6,
+ RClient.Right,
+ TextHeight + (RClient.Top-6));
+ end;
+ else
+ RText := MakeRect(RClient.Left+4,
+ RClient.Top-6,
+ TextWidth + RClient.Left+4,
+ TextHeight + RClient.Top-6);
+ RClipLeft := MakeRect(RClient.Left,
+ RClient.Top,
+ RClient.Left+4,
+ TextHeight + RClient.Top-6);
+ RClipRight := MakeRect(TextWidth + RClient.Left+4,
+ RClient.Top-6,
+ RClient.Right,
+ TextHeight + RClient.Top-6);
+ end;
+ RClipMain := MakeRect(RClient.Left,
+ TextHeight + RClient.Top-6,
+ RClient.Right,
+ RClient.Bottom);
+ // Open themes
+ hThemes := OpenThemeData(Sender.fHandle, 'button');
+ if hThemes <> 0 then
+ begin
+ Sender.Color := Sender.fParent.fColor;
+ {$IFDEF USE_FLAGS}
+ if not (F3_Disabled in Sender.fStyle.f3_Style) then
+ {$ELSE}
+ if Sender.fEnabled then
+ {$ENDIF}
+ fState := 1 else fState := 2;
+ // Drawing GroupBox rect "step by step"
+ DrawThemeBackground(hThemes, DC, 4{BP_GROUPBOX}, fState{GBS_XXXXXX}, RClient, @RClipMain);
+ DrawThemeBackground(hThemes, DC, 4{BP_GROUPBOX}, fState{GBS_XXXXXX}, RClient, @RClipLeft);
+ DrawThemeBackground(hThemes, DC, 4{BP_GROUPBOX}, fState{GBS_XXXXXX}, RClient, @RClipRight);
+ // Drawing GroupBox text
+ {$IFDEF USE_FLAGS}
+ if F3_Disabled in Sender.fStyle.f3_Style then
+ {$ELSE}
+ if not Sender.fEnabled then
+ {$ENDIF}
+ GetThemeColor(hThemes, {WP_CAPTION} 1, {CS_DISABLED} 3, 3803, Color)
+ else GetThemeColor(hThemes, {WP_CAPTION} 1, {CS_ACTIVE} 1, 3803, Color);
+ // Close themes
+ CloseThemeData(hThemes);
+
+ // Create font
+ F := CreateNewFont(Sender);
+ fDC := SelectObject(DC, F);
+ // Draw text
+ SetBkMode(DC, TRANSPARENT);
+ TxtColor := SetTextColor(DC, Color2RGB(Color));
+ DrawTextW(DC, PWideChar(S), Length(S), RText, LPos or DT_SINGLELINE);
+ // Backup color
+ SetTextColor(DC, Color2RGB(TxtColor));
+ SetBkMode(DC, OPAQUE);
+ // Destroying font
+ SelectObject(DC, fDC);
+ DeleteObject(F);
+ end;
+end;
+//************************* Drawing CheckBox control *************************//
+procedure WndCheckBoxXPDraw( Dummy : Pointer; Sender: PControl; DC: HDC );
+var
+ hThemes : THandle;
+ RClient, RCheck, RText : TRect;
+ fState : DWORD;
+ W, H : Integer;
+ S : KOLString;
+ F : HFONT;
+ fDC : HDC;
+ Color : COLORREF;
+ TxtColor : COLORREF;
+ Brush : HBRUSH;
+begin
+ // Checking user owner-draw
+ if Assigned(Sender.EV.fOnPaint) and (@Sender.EV.fOnPaint <> @WndCheckBoxXPDraw) then
+ begin
+ Sender.EV.fOnPaint(Sender, DC);
+ exit;
+ end;
+
+ // Getting metrics
+ W := GetSystemMetrics( SM_CXMENUCHECK );
+ H := GetSystemMetrics( SM_CYMENUCHECK );
+ // Getting caption
+ S := Sender.fCaption;
+ // Getting rects
+ RClient := Sender.ClientRect;
+ RCheck := RClient;
+ RCheck.Right := RCheck.Left + W;
+ if {$IFDEF USE_FLAGS} G1_WordWrap in Sender.fFlagsG1
+ {$ELSE} Sender.fWordWrap {$ENDIF} then
+ RCheck.Top := RCheck.Top + Sender.Border
+ else
+ RCheck.Top := RCheck.Top + (RCheck.Bottom - RCheck.Top - H) div 2;
+ RCheck.Bottom := RCheck.Top + H;
+ RText := MakeRect(RCheck.Right + Sender.fMargin, RCheck.Top,
+ RClient.Right, RCheck.Bottom);
+ // Getting state
+ fState := 1; {CBS_UNCHECKEDNORMAL}
+ {$IFDEF USE_FLAGS}
+ if F3_Disabled in Sender.fStyle.f3_Style then
+ {$ELSE}
+ if not Sender.fEnabled then
+ {$ENDIF}
+ fState := 4 {CBS_UNCHECKEDDISABLED}
+ else
+ if {$IFDEF USE_FLAGS} G4_Hot in Sender.fFlagsG4
+ {$ELSE} Sender.fHot {$ENDIF} then
+ fState := 2; {CBS_UNCHECKEDHOT}
+ if {$IFDEF USE_FLAGS} G4_Pressed in Sender.fFlagsG4
+ {$ELSE} Sender.fPressed {$ENDIF} then
+ fState := 3{CBS_UNCHECKEDPRESSED};
+ case Sender.Check3 of
+ tsChecked : Inc( fState, 4 );
+ tsIndeterminate : Inc( fState, 8 );
+ end;
+
+ // Draw back layer
+ if {$IFDEF USE_FLAGS} not( G2_Transparent in Sender.fFlagsG2 )
+ {$ELSE} not Sender.fTransparent {$ENDIF} then
+ begin
+ Brush := CreateSolidBrush(Color2RGB(Sender.fParent.Color));
+ fDC := SelectObject(DC, Brush);
+ FillRect(DC, RClient, Brush);
+ SelectObject(DC, fDC);
+ DeleteObject(Brush);
+ end;
+
+ // Draw theme
+ Color := Sender.Font.Color;
+ hThemes := OpenThemeData(Sender.fHandle, 'button');
+ if hThemes <> 0 then
+ begin
+ {$IFDEF USE_FLAGS}
+ if F3_Disabled in Sender.fStyle.f3_Style then
+ {$ELSE}
+ if not Sender.fEnabled then
+ {$ENDIF}
+ GetThemeColor(hThemes, 1, 4, 3803, Color);
+ DrawThemeBackground(hThemes, DC, 3 {BP_CHECKBOX}, fState, RCheck, @RCheck);
+ CloseThemeData(hThemes);
+ end;
+
+ // Create font
+ F := CreateNewFont(Sender);
+ fDC := SelectObject(DC, F);
+ // Draw text
+ SetBkMode(DC, TRANSPARENT);
+ TxtColor := SetTextColor(DC, Color2RGB(Color));
+ DrawText(DC, PKOLChar(S), Length(S), RText, DT_LEFT or DT_VCENTER or DT_SINGLELINE);
+ // Destroying font
+ SetTextColor(DC, Color2RGB(TxtColor));
+ SetBkMode(DC, OPAQUE);
+ // Destroying object
+ SelectObject(DC, fDC);
+ DeleteObject(F);
+
+ // Draw focusrect
+ if GetFocus = Sender.fHandle then
+ begin
+ dec( RText.Left );
+ DrawFocusRect(DC, RText);
+ end;
+end;
+//************************* Drawing RadioBox control *************************//
+procedure WndRadioBoxXPDraw( Dummy : Pointer; Sender: PControl; DC: HDC );
+var
+ hThemes : THandle;
+ RClient, RDot, RText : TRect;
+ fState : DWORD;
+ W, H : Integer;
+ S : KOLString;
+ F : HFONT;
+ fDC : HDC;
+ Color, TxtColor : COLORREF;
+ Brush : HBRUSH;
+begin
+ // Checking user owner-draw
+ if Assigned(Sender.EV.fOnPaint) and (@Sender.EV.fOnPaint <> @WndRadioBoxXPDraw) then
+ begin
+ Sender.EV.fOnPaint(Sender, DC);
+ exit;
+ end;
+
+ // Getting metrics
+ W := GetSystemMetrics( SM_CXMENUCHECK );
+ H := GetSystemMetrics( SM_CYMENUCHECK );
+ // Getting caption
+ S := Sender.fCaption;
+ // Getting rects
+ RClient := Sender.ClientRect;
+ RDot := RClient;
+ RDot.Right := RDot.Left + W;
+ if {$IFDEF USE_FLAGS} G1_WordWrap in Sender.fFlagsG1
+ {$ELSE} Sender.fWordWrap {$ENDIF} then
+ RDot.Top := RDot.Top + Sender.Border
+ else
+ RDot.Top := RDot.Top + (RDot.Bottom - RDot.Top - H) div 2;
+ RDot.Bottom := RDot.Top + H;
+ RText := MakeRect(RDot.Right + Sender.Border, RDot.Top,
+ RClient.Right, RDot.Bottom);
+ // Getting state
+ fState := 1; {CBS_UNCHECKEDNORMAL}
+ {$IFDEF USE_FLAGS}
+ if F3_Disabled in Sender.fStyle.f3_Style then
+ {$ELSE}
+ if not Sender.fEnabled then
+ {$ENDIF}
+ fState := 4 {CBS_UNCHECKEDDISABLED}
+ else
+ if {$IFDEF USE_FLAGS} G4_Hot in Sender.fFlagsG4
+ {$ELSE} Sender.fHot {$ENDIF} then
+ fState := 2; {CBS_UNCHECKEDHOT}
+ if {$IFDEF USE_FLAGS} G4_Pressed in Sender.fFlagsG4
+ {$ELSE} Sender.fPressed {$ENDIF} then
+ fState := 3{CBS_UNCHECKEDPRESSED};
+ if Sender.Checked then
+ Inc( fState, 4 );
+
+ // Draw back layer
+ if {$IFDEF USE_FLAGS} not( G2_Transparent in Sender.fFlagsG2 )
+ {$ELSE} not Sender.fTransparent {$ENDIF} then
+ begin
+ Brush := CreateSolidBrush(Color2RGB(Sender.fParent.Color));
+ fDC := SelectObject(DC, Brush);
+ FillRect(DC, RClient, Brush);
+ SelectObject(DC, fDC);
+ DeleteObject(Brush);
+ end;
+
+ // Draw theme
+ Color := Sender.Font.Color;
+ hThemes := OpenThemeData(Sender.fHandle, 'button');
+ if hThemes <> 0 then
+ begin
+ {$IFDEF USE_FLAGS}
+ if F3_Disabled in Sender.fStyle.f3_Style then
+ {$ELSE}
+ if not Sender.fEnabled then
+ {$ENDIF}
+ GetThemeColor(hThemes, 1, 4, 3803, Color);
+ DrawThemeBackground(hThemes, DC, 2 {BP_RADIOBOX}, fState, RDot, @RDot);
+ CloseThemeData(hThemes);
+ end;
+
+ // Create font
+ F := CreateNewFont(Sender);
+ fDC := SelectObject(DC, F);
+ // Draw text
+ SetBkMode(DC, TRANSPARENT);
+ TxtColor := SetTextColor(DC, Color2RGB(Color));
+ DrawText(DC, PKOLChar(S), Length(S), RText, DT_LEFT or DT_VCENTER or DT_SINGLELINE);
+ // Destroying font
+ SetTextColor(DC, Color2RGB(TxtColor));
+ SetBkMode(DC, OPAQUE);
+ // Destroying object
+ SelectObject(DC, fDC);
+ DeleteObject(F);
+
+ // Draw focusrect
+ if GetFocus = Sender.fHandle then
+ begin
+ dec( RText.Left );
+ DrawFocusRect(DC, RText);
+ end;
+end;
+
+//******************** Drawing Button and BitButton control ******************//
+procedure WndButtonXPDraw( Dummy : Pointer; Sender: PControl; DC: HDC );
+var
+ hThemes : THandle;
+ F : HFONT;
+ fDC1, fDC2 : HDC;
+ RClient : TRect;
+ RText, R1 : TRect;
+ RIcon : TRect;
+ S : WideString;
+ fState, bStyle : DWORD;
+ Bmp : HBITMAP;
+ W, H : Integer;
+ HPos, VPos : DWORD;
+ Brush : HBRUSH;
+ Pen : HPEN;
+ SenderWidth, SenderHeight : integer;
+ Flags: DWORD;
+ _DC : HDC;
+ OldBmp: HBitmap;
+ ic : PIcon;
+ b : PBitmap;
+ i : integer;
+ il : PImageList;
+begin
+ // Checking user owner-draw
+ if Assigned(Sender.EV.fOnPaint)
+ and (TMethod(Sender.EV.fOnPaint).Code <> @WndButtonXPDraw) then
+ begin
+ Sender.EV.fOnPaint(Sender, DC);
+ exit;
+ end;
+ if Assigned(Sender.EV.fOnBitBtnDraw)
+ and (TMethod(Sender.EV.fOnBitBtnDraw).Code <> @DummyProc123_0) then
+ begin
+ fState := 0{PBS_NORMAL};
+ {$IFDEF USE_FLAGS}
+ if F3_Disabled in Sender.fStyle.f3_Style then
+ {$ELSE}
+ if not Sender.fEnabled then
+ {$ENDIF}
+ fState := 2{PBS_DISABLED}
+ else
+ if GetFocus = Sender.fHandle then
+ fState := 3{PBS_PRESSED}
+ else
+ if {$IFDEF USE_FLAGS} G4_Hot in Sender.fFlagsG4
+ {$ELSE} Sender.fHot {$ENDIF} then
+ fState := 4{PBS_HOT};
+ if {$IFDEF USE_FLAGS} G4_Pressed in Sender.fFlagsG4
+ {$ELSE} Sender.fPressed {$ENDIF} then
+ fState := 1{PBS_PRESSED};
+ Sender.EV.fOnBitBtnDraw(Sender, fState);
+ exit;
+ end;
+
+ // Getting rects
+ RClient := Sender.ClientRect;
+ RText := RClient;
+ // Calc bitmap rect
+ Bmp := Sender.DF.fGlyphBitmap;
+ HPos := 0; VPos := 0;
+ if Bmp <> 0 then
+ begin
+ SenderWidth := Sender.Width;
+ SenderHeight := Sender.Height;
+ W := Sender.DF.fGlyphWidth;
+ H := Sender.DF.fGlyphHeight;
+ if Sender.DF.fGlyphLayout in [ glyphLeft ] then
+ begin
+ RIcon := MakeRect((SenderWidth div 2) - (W + (W div 4)),
+ (SenderHeight div 2) - (H div 2),
+ W, SenderHeight);
+ RText.Left := (SenderWidth div 2) + (W div 4);
+ HPos := DT_LEFT;
+ VPos := DT_VCENTER;
+ end;
+ if Sender.DF.fGlyphLayout in [ glyphRight ] then
+ begin
+ RIcon := MakeRect((SenderWidth div 2) + (W div 4),
+ (SenderHeight div 2) - (H div 2),
+ W, SenderHeight);
+ RText.Right := (SenderWidth div 2) - (W div 4);
+ HPos := DT_RIGHT;
+ VPos := DT_VCENTER;
+ end;
+ if Sender.DF.fGlyphLayout in [ glyphOver ] then
+ begin
+ RIcon := MakeRect((SenderWidth div 2) - (W div 2),
+ (SenderHeight div 2) - (H div 2),
+ W, SenderHeight);
+ HPos := DT_CENTER;
+ VPos := DT_VCENTER;
+ end;
+ if Sender.DF.fGlyphLayout in [ glyphTop ] then
+ begin
+ RIcon := MakeRect((SenderWidth div 2) - (W div 2),
+ (SenderHeight div 2) - (H + (H div 4)),
+ W, SenderHeight);
+ RText.Top := (SenderHeight div 2) + (H div 4);
+ HPos := DT_CENTER;
+ VPos := DT_TOP;
+ end;
+ if Sender.DF.fGlyphLayout in [ glyphBottom ] then
+ begin
+ RIcon := MakeRect((SenderWidth div 2) - (W div 2),
+ (SenderHeight div 2) + (H div 4),
+ W, SenderHeight);
+ RText.Bottom := (SenderHeight div 2) - (H div 4);
+ HPos := DT_CENTER;
+ VPos := DT_BOTTOM;
+ end;
+ end else
+ begin
+ HPos := DT_CENTER;
+ VPos := DT_VCENTER;
+ RIcon := MakeRect(0, 0, 0, 0);
+ end;
+
+ // Getting caption
+ S := KOLWideString( Sender.fCaption );
+ // Getting state
+ fState := 1{PBS_NORMAL};
+ {$IFDEF USE_FLAGS}
+ if F3_Disabled in Sender.fStyle.f3_Style then
+ {$ELSE}
+ if not Sender.fEnabled then
+ {$ENDIF}
+ fState := 4{PBS_DISABLED}
+ else
+ if {$IFDEF USE_FLAGS} G4_Hot in Sender.fFlagsG4
+ {$ELSE} Sender.fHot {$ENDIF} then
+ fState := 2{PBS_HOT};
+ if {$IFDEF USE_FLAGS} G4_Pressed in Sender.fFlagsG4
+ {$ELSE} Sender.fPressed {$ENDIF} then
+ fState := 3{PBS_PRESSED};
+ // Opening themes
+ hThemes := OpenThemeData(Sender.fHandle, 'button');
+ if hThemes <> 0 then
+ begin
+ Brush := CreateSolidBrush(Color2RGB(Sender.fParent.Color));
+ fDC1 := SelectObject(DC, Brush);
+ FillRect(DC, RClient, Brush);
+ if (Sender.Flat) and (fState = 1{PBS_NORMAL}) then
+ begin
+ Pen := CreatePen(PS_SOLID, 1, clLtGrey);
+ fDC2 := SelectObject(DC, Pen);
+ RoundRect(DC, RClient.Left+2, RClient.Top+2, RClient.Right-2, RClient.Bottom-2, 3, 3);
+ SelectObject(DC, fDC2);
+ DeleteObject(Pen);
+ end
+ else
+ DrawThemeBackground(hThemes, DC, 1{BP_PUSHBUTTON}, fState, RClient, @RClient);
+ SelectObject(DC, fDC1);
+ DeleteObject(Brush);
+
+ if Bmp <> 0 then
+ begin
+ if bboImageList in Sender.DF.fBitBtnOptions then
+ begin
+ bStyle := ILD_TRANSPARENT;
+ {$IFDEF USE_FLAGS}
+ if not (F3_Disabled in Sender.fStyle.f3_Style) then
+ {$ELSE}
+ if Sender.fEnabled then
+ {$ENDIF}
+ i := Sender.BitBtnImgIdx
+ else
+ begin
+ ic := NewIcon;
+ ic.fSize := Sender.DF.fGlyphWidth;
+ ic.fHandle := ImageList_GetIcon(Bmp, Sender.BitBtnImgIdx, bStyle);
+ b := NewBitmap(ic.fSize, ic.fSize);
+ b.fHandle := ic.Convert2Bitmap(clBtnFace);
+ ConvertBitmap2Grayscale(b);
+ i := ImageList_Add(Bmp, b.fHandle, 0);
+ Free_And_Nil(b);
+ Free_And_Nil(ic);
+ end;
+ ImageList_Draw(Bmp, i, DC, RIcon.Left, RIcon.Top, bStyle);
+ end
+ else
+ begin
+ _DC := CreateCompatibleDC( 0 );
+ {$IFDEF USE_FLAGS}
+ if not (F3_Disabled in Sender.fStyle.f3_Style) then
+ {$ELSE}
+ if Sender.fEnabled then
+ {$ENDIF}
+ OldBmp := SelectObject( _DC, Bmp)
+ else
+ begin
+ bStyle := ILD_TRANSPARENT;
+ il := NewImageList(Sender.fParent);
+ il.HandleNeeded;
+ i := ImageList_Add(il.fHandle, Bmp, 0);
+ ic := NewIcon;
+ ic.fSize := Sender.DF.fGlyphWidth;
+ ic.fHandle := ImageList_GetIcon(il.fHandle, i, bStyle);
+ b := NewBitmap(ic.fSize, ic.fSize);
+ b.fHandle := ic.Convert2Bitmap(clBtnFace);
+ ConvertBitmap2Grayscale(b);
+ OldBmp := SelectObject( _DC, b.fHandle);
+ Free_And_Nil(b);
+ Free_And_Nil(ic);
+ Free_And_Nil(il);
+ end;
+ StretchBlt( DC, RIcon.Left, RIcon.Top, Sender.DF.fGlyphWidth, Sender.DF.fGlyphHeight,
+ _DC, 0, 0, Sender.DF.fGlyphWidth, Sender.DF.fGlyphHeight,
+ SRCCOPY);
+ SelectObject( _DC, OldBmp );
+ DeleteDC( _DC );
+ end;
+ end;
+ // Create font
+ F := CreateNewFont(Sender);
+ fDC1 := SelectObject(DC, F);
+ // Draw text
+ Flags := HPos or VPos;
+ R1 := RText;
+ if Sender.Style and BS_MULTILINE = 0 then
+ Flags := Flags or DT_SINGLELINE
+ else
+ begin
+ Flags := Flags and not DT_VCENTER or DT_WORDBREAK;
+ if VPos and DT_VCENTER <> 0 then
+ begin
+ DrawTextW(DC, PWideChar( S ), Length(S), R1, Flags or DT_CALCRECT);
+ OffsetRect( R1, 0,
+ ( (RText.Bottom - RText.Top) - (R1.Bottom - R1.Top) ) div 2 );
+ if HPos and DT_CENTER <> 0 then
+ OffsetRect( R1,
+ ( (RText.Right - RText.Left) - (R1.Right - R1.Left) ) div 2, 0 );
+ end;
+ end;
+ DrawThemeText(hThemes, DC, 1{BP_PUSHBUTTON}, fState, PWideChar(S), Length(S),
+ Flags, 0, R1);
+ // Destroying font
+ SelectObject(DC, fDC1);
+ DeleteObject(F);
+
+ CloseThemeData(hThemes);
+ end;
+
+ if (GetFocus = Sender.fHandle) and (bboFocusRect in Sender.DF.fBitBtnOptions) then
+ DrawFocusRect(DC, MakeRect(RClient.Left+4, RClient.Top+4, RClient.Right-4, RClient.Bottom-4));
+end;
+//************************* Control MouseEnter event *************************//
+{$IFDEF ASM_VERSION}
+procedure WndXPMouseEnter( Dummy : Pointer; Sender: PObj );
+asm
+ {$IFDEF USE_FLAGS}
+ OR [EDX].TControl.fFlagsG4, 1 shl G4_Hot
+ {$ELSE}
+ MOV [EDX].TControl.fHot, 1
+ {$ENDIF}
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EDX].TControl.EV
+ MOV ECX, [EAX].TEvents.fOnMouseEnter.TMethod.Code
+ {$ELSE}
+ MOV ECX, [EDX].TControl.EV.fOnMouseEnter.TMethod.Code
+ {$ENDIF}
+ JECXZ @@fin
+ CMP ECX, offset[WndXPMouseEnter]
+ JZ @@fin
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EAX].TEvents.fOnMouseEnter.TMethod.Data
+ {$ELSE}
+ MOV EAX, [EDX].TControl.EV.fOnMouseEnter.TMethod.Data
+ {$ENDIF}
+ CALL ECX
+@@fin:
+end;
+{$ELSE}
+procedure WndXPMouseEnter( Dummy : Pointer; Sender: PObj );
+begin
+ with PControl(Sender)^ do
+ begin
+ {$IFDEF USE_FLAGS}
+ fFlagsG4 := fFlagsG4 + [G4_Hot];
+ {$ELSE} fHot := true; {$ENDIF}
+ if Assigned(EV.fOnMouseEnter) and
+ (@EV.fOnMouseEnter <> @WndXPMouseEnter) then
+ EV.fOnMouseEnter(Sender);
+ end;
+end;
+{$ENDIF}
+//************************* Control MouseLeave event *************************//
+{$IFDEF ASM_VERSION}
+procedure WndXPMouseLeave( Dummy : Pointer; Sender: PObj );
+asm
+ {$IFDEF USE_FLAGS}
+ AND [EDX].TControl.fFlagsG4, not(1 shl G4_Hot)
+ {$ELSE}
+ MOV [EDX].TControl.fHot, 0
+ {$ENDIF}
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EDX].TControl.EV
+ MOV ECX, [EAX].TEvents.fOnMouseLeave.TMethod.Code
+ {$ELSE}
+ MOV ECX, [EDX].TControl.EV.fOnMouseLeave.TMethod.Code
+ {$ENDIF}
+ JECXZ @@fin
+ CMP ECX, offset[WndXPMouseLeave]
+ JZ @@fin
+ {$IFDEF EVENTS_DYNAMIC}
+ MOV EAX, [EAX].TEvents.fOnMouseLeave.TMethod.Data
+ {$ELSE}
+ MOV EAX, [EDX].TControl.EV.fOnMouseLeave.TMethod.Data
+ {$ENDIF}
+ CALL ECX
+@@fin:
+end;
+{$ELSE}
+procedure WndXPMouseLeave( Dummy : Pointer; Sender: PObj );
+begin
+ {$IFDEF USE_FLAGS}
+ PControl(Sender).fFlagsG4 :=
+ PControl(Sender).fFlagsG4 - [G4_Hot];
+ {$ELSE} PControl(Sender).fHot := false; {$ENDIF}
+ if Assigned(PControl(Sender).EV.fOnMouseLeave) and
+ (@PControl(Sender).EV.fOnMouseLeave <> @WndXPMouseLeave) then
+ PControl(Sender).EV.fOnMouseLeave(Sender);
+end;
+{$ENDIF}
+//*************************** Control Message event **************************//
+function WndXPMessage( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var
+ pt : TPoint;
+ Mouse: TMouseEventData;
+ dDC : HDC;
+begin
+ Result := false;
+
+ case Msg.message of
+ WM_LBUTTONDBLCLK:
+ begin
+ if Assigned(Sender.EV.fOnMouseDblClk) then
+ begin
+ Mouse.Button := mbLeft;
+ Mouse.StopHandling := false;
+ Mouse.R1 := 0;
+ Mouse.R2 := 0;
+ Mouse.Shift := 120;
+ Mouse.X := 0;
+ Mouse.Y := 0;
+ GetCursorPos(pt);
+ if ScreenToClient(Sender.fHandle, pt) then
+ begin
+ Mouse.X := pt.X;
+ Mouse.Y := pt.Y;
+ end;
+ Sender.EV.fOnMouseDblClk(Sender, Mouse);
+ end;
+ if {$IFDEF USE_FLAGS} not(G5_IsSplitter in Sender.fFlagsG5)
+ {$ELSE} not Sender.fIsSplitter {$ENDIF} then
+ Sender.Perform( WM_LBUTTONDOWN, Msg.wParam, Msg.lParam );
+ end;
+
+ WM_LBUTTONDOWN:
+ begin
+ if Assigned(Sender.EV.fOnMouseDown) then
+ begin
+ Mouse.Button := mbLeft;
+ Mouse.StopHandling := false;
+ Mouse.R1 := 0;
+ Mouse.R2 := 0;
+ Mouse.Shift := 120;
+ Mouse.X := 0;
+ Mouse.Y := 0;
+ GetCursorPos(pt);
+ if ScreenToClient(Sender.fHandle, pt) then
+ begin
+ Mouse.X := pt.X;
+ Mouse.Y := pt.Y;
+ end;
+ Sender.EV.fOnMouseDown(Sender, Mouse);
+ end;
+ {$IFDEF USE_FLAGS} Sender.fFlagsG4 := Sender.fFlagsG4 + [G4_Pressed];
+ {$ELSE} Sender.fPressed := true; {$ENDIF}
+ dDC := GetWindowDC(Msg.hWnd);
+ Sender.EV.fOnPaint(Sender, dDC);
+ ReleaseDC( Msg.hWnd, dDC ); // vampir_infernal 15.10.2008
+ end;
+
+ WM_LBUTTONUP:
+ begin
+ if Assigned(Sender.EV.fOnMouseUp) then
+ begin
+ Mouse.Button := mbLeft;
+ Mouse.StopHandling := false;
+ Mouse.R1 := 0;
+ Mouse.R2 := 0;
+ Mouse.Shift := 120;
+ Mouse.X := 0;
+ Mouse.Y := 0;
+ GetCursorPos(pt);
+ if ScreenToClient(Sender.fHandle, pt) then
+ begin
+ Mouse.X := pt.X;
+ Mouse.Y := pt.Y;
+ end;
+ Sender.EV.fOnMouseUp(Sender, Mouse);
+ end;
+ {$IFDEF USE_FLAGS} Sender.fFlagsG4 := Sender.fFlagsG4 - [G4_Pressed];
+ {$ELSE} Sender.fPressed := false; {$ENDIF}
+ dDC := GetWindowDC(Msg.hWnd);
+ Sender.EV.fOnPaint(Sender, dDC);
+ ReleaseDC( Msg.hWnd, dDC );
+ end;
+
+ WM_KEYDOWN:
+ begin
+ if Msg.wParam = VK_SPACE then
+ begin
+ if Assigned(Sender.EV.fOnKeyDown) then
+ Sender.EV.fOnKeyDown(Sender, Msg.wParam, GetShiftState);
+ {$IFDEF USE_FLAGS} Sender.fFlagsG4 := Sender.fFlagsG4 + [G4_Pressed];
+ {$ELSE} Sender.fPressed := true; {$ENDIF}
+ dDC := GetWindowDC(Msg.hWnd);
+ Sender.EV.fOnPaint(Sender, dDC);
+ ReleaseDC( Msg.hWnd, dDC );
+ end;
+ end;
+
+ WM_KEYUP:
+ begin
+ if Msg.wParam = VK_SPACE then
+ begin
+ if Assigned(Sender.EV.fOnKeyUp) then
+ Sender.EV.fOnKeyUp(Sender, Msg.wParam, GetShiftState);
+ {$IFDEF USE_FLAGS} Sender.fFlagsG4 := Sender.fFlagsG4 - [G4_Pressed];
+ {$ELSE} Sender.fPressed := false; {$ENDIF}
+ dDC := GetWindowDC(Msg.hWnd);
+ Sender.EV.fOnPaint(Sender, dDC);
+ ReleaseDC( Msg.hWnd, dDC );
+ end;
+ end;
+
+ WM_KILLFOCUS:
+ begin
+ {$IFDEF USE_FLAGS} Sender.fFlagsG4 := Sender.fFlagsG4 - [G4_Hot];
+ {$ELSE} Sender.fHot := false; {$ENDIF}
+ dDC := GetWindowDC(Msg.hWnd);
+ Sender.EV.fOnPaint(Sender, dDC);
+ ReleaseDC( Msg.hWnd, dDC );
+ end;
+
+ WM_SETFOCUS:
+ begin
+ {$IFDEF USE_FLAGS} Sender.fFlagsG4 := Sender.fFlagsG4 + [G4_Hot];
+ {$ELSE} Sender.fHot := TRUE; {$ENDIF}
+ dDC := GetWindowDC(Msg.hWnd);
+ Sender.EV.fOnPaint(Sender, dDC);
+ ReleaseDC( Msg.hWnd, dDC );
+ Result := true;
+ end;
+ end;
+end;
+//*************************** Events for CheckBox ****************************//
+procedure XP_Themes_For_CheckBox(Sender : PControl);
+begin
+ if AppTheming then
+ Sender.OnPaint := TOnPaint( MakeMethod( nil, @WndCheckBoxXPDraw ) );
+end;
+//*************************** Events for RadioBox ****************************//
+procedure XP_Themes_For_RadioBox(Sender : PControl);
+begin
+ if AppTheming then
+ Sender.OnPaint := TOnPaint( MakeMethod( nil, @WndRadioBoxXPDraw ) );
+end;
+//**************************** Events for Panel ******************************//
+procedure XP_Themes_For_Panel(Sender : PControl);
+begin
+ if AppTheming then
+ begin
+ if Sender.EdgeStyle = esTransparent then Sender.SetTransparent(True) else
+ begin
+ Sender.OnResize := TOnEvent( MakeMethod( nil, @WndPanelXPResize ) );
+ Sender.OnPaint := TOnPaint( MakeMethod( nil, @WndPanelXPDraw ) );
+ end;
+ end;
+end;
+//*************************** Events for Splitter ****************************//
+procedure XP_Themes_For_Splitter(Sender : PControl);
+begin
+ if AppTheming then
+ begin
+ Sender.AttachProc(WndXPMessage);
+ Sender.OnPaint := TOnPaint( MakeMethod( nil, @WndSplitterXPDraw ) );
+ end;
+end;
+//**************************** Events for Label ******************************//
+procedure XP_Themes_For_Label(Sender : PControl);
+begin
+ if AppTheming then Sender.SetTransparent(True);
+end;
+//************************** Events for GroupBox *****************************//
+procedure XP_Themes_For_GroupBox(Sender : PControl);
+begin
+ if AppTheming then
+ Sender.OnPaint := TOnPaint( MakeMethod( nil, @WndGroupBoxXPDraw ) );
+end;
+//************************** Events for TabPanel *****************************//
+procedure XP_Themes_For_TabPanel(Sender : PControl);
+begin
+ if AppTheming then
+ Sender.OnPaint := TOnPaint( MakeMethod( nil, @WndTabXPDraw ) );
+end;
+//********************* Events for Button and BitButton **********************//
+procedure XP_Themes_For_BitBtn(Sender : PControl);
+begin
+ if AppTheming then
+ begin
+ Sender.AttachProc(WndXPMessage);
+ Sender.OnMouseEnter := TOnEvent( MakeMethod( nil, @WndXPMouseEnter ) );
+ Sender.OnMouseLeave := TOnEvent( MakeMethod( nil, @WndXPMouseLeave ) );
+ Sender.OnPaint := TOnPaint( MakeMethod( nil, @WndButtonXPDraw ) );
+ end;
+end;
+//*********************** Deattach ownerdraw function ************************//
+procedure Deattach(Sender : PControl; PaintProc : Pointer);
+begin
+ if Sender.IsProcAttached(WndXPMessage) then
+ Sender.DetachProc(WndXPMessage);
+ if {$IFDEF NIL_EVENTS} Assigned(Sender.EV.fOnMouseEnter) and {$ENDIF}
+ (@Sender.EV.fOnMouseEnter = @WndXPMouseEnter)
+ and ({$IFDEF USE_FLAGS} not(G3_Flat in Sender.fFlagsG3)
+ {$ELSE} not Sender.fFlat {$ENDIF}) then
+ {$IFDEF NIL_EVENTS} Sender.EV.fOnMouseEnter := nil;
+ {$ELSE} TMethod( Sender.EV.fOnMouseEnter ).Code := @DummyObjProc;
+ {$ENDIF}
+ if {$IFDEF NIL_EVENTS} Assigned(Sender.EV.fOnMouseLeave) and {$ENDIF}
+ (@Sender.EV.fOnMouseLeave = @WndXPMouseLeave)
+ and ({$IFDEF USE_FLAGS} not(G3_Flat in Sender.fFlagsG3)
+ {$ELSE} not Sender.fFlat {$ENDIF}) then
+ {$IFDEF NIL_EVENTS} Sender.EV.fOnMouseLeave := nil;
+ {$ELSE} TMethod( Sender.EV.fOnMouseLeave ).Code := @DummyObjProc;
+ {$ENDIF}
+ if {$IFDEF NIL_EVENTS} Assigned(Sender.EV.fOnPaint) and {$ENDIF}
+ (@Sender.EV.fOnPaint = PaintProc) then
+ {$IFDEF NIL_EVENTS} Sender.EV.fOnPaint := nil;
+ {$ELSE} TMethod( Sender.EV.fOnPaint ).Code := @DummyObjProc;
+ {$ENDIF}
+end;
+//********************* Handling of message WM_THEMECHANGED ******************//
+function WndXP_WM_THEMECHANGED( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+begin
+ Result := false;
+
+ if Msg.message = $31A {WM_THEMECHANGED} then
+ begin
+ if AppTheming then DeinitThemes;
+ CheckThemes;
+ if AppTheming then
+ begin
+ InitThemes;
+ if ((Sender.fStyle.Value and BS_AUTOCHECKBOX) = BS_AUTOCHECKBOX) and
+ (Sender.SubClassName = 'obj_BUTTON') and
+ {$IFDEF USE_FLAGS}
+ ([G5_IsGroupbox, G5_IsSplitter, G5_IsBitBtn] * Sender.fFlagsG5 = [])
+ {$ELSE}
+ (Sender.fIsGroupBox = false) and
+ (Sender.fIsSplitter = false) and
+ (Sender.fIsBitBtn = false) {$ENDIF} then
+ begin
+ XP_Themes_For_CheckBox(Sender);
+ exit;
+ end;
+ if ((Sender.fStyle.Value and BS_AUTO3STATE) = BS_AUTO3STATE) and
+ (Sender.SubClassName = 'obj_BUTTON') and
+ {$IFDEF USE_FLAGS}
+ ([G5_IsGroupbox, G5_IsSplitter, G5_IsBitBtn] * Sender.fFlagsG5 = [])
+ {$ELSE}
+ (Sender.fIsGroupBox = false) and
+ (Sender.fIsSplitter = false) and
+ (Sender.fIsBitBtn = false) {$ENDIF} then
+ begin
+ XP_Themes_For_CheckBox(Sender);
+ exit;
+ end;
+ if ((Sender.fStyle.Value and BS_RADIOBUTTON) = BS_RADIOBUTTON) and
+ (Sender.SubClassName = 'obj_BUTTON') and
+ {$IFDEF USE_FLAGS}
+ ([G5_IsGroupbox, G5_IsSplitter, G5_IsBitBtn] * Sender.fFlagsG5 = [])
+ {$ELSE}
+ (Sender.fIsGroupBox = false) and
+ (Sender.fIsSplitter = false) and
+ (Sender.fIsBitBtn = false) {$ENDIF} then
+ begin
+ XP_Themes_For_RadioBox(Sender);
+ exit;
+ end;
+ if ((Sender.fStyle.Value and BS_GROUPBOX) = BS_GROUPBOX) and
+ (Sender.SubClassName = 'obj_BUTTON') and
+ {$IFDEF USE_FLAGS}
+ ([G5_IsGroupbox, G5_IsSplitter, G5_IsBitBtn] * Sender.fFlagsG5 =
+ [G5_IsGroupbox])
+ {$ELSE}
+ (Sender.fIsGroupBox = true) and
+ (Sender.fIsSplitter = false) and
+ (Sender.fIsBitBtn = false) {$ENDIF} then
+ begin
+ XP_Themes_For_GroupBox(Sender);
+ exit;
+ end;
+ if (Sender.SubClassName = 'obj_BUTTON') and
+ {$IFDEF USE_FLAGS}
+ ([G5_IsGroupbox, G5_IsSplitter] * Sender.fFlagsG5 = [])
+ {$ELSE}
+ (Sender.fIsGroupBox = false) and
+ (Sender.fIsSplitter = false) {$ENDIF} then
+ begin
+ XP_Themes_For_BitBtn(Sender);
+ exit;
+ end;
+ if (Sender.SubClassName = 'obj_STATIC') then
+ begin
+ if {$IFDEF USE_FLAGS} G1_IsStaticControl in Sender.fFlagsG1
+ {$ELSE} Sender.fIsStaticControl > 0 {$ENDIF} then
+ XP_Themes_For_Label(Sender)
+ else
+ begin
+ if {$IFDEF USE_FLAGS} G5_IsSplitter in Sender.fFlagsG5
+ {$ELSE} Sender.fIsSplitter {$ENDIF} then
+ XP_Themes_For_Splitter(Sender)
+ else
+ begin
+ if Sender.fParent.SubClassName = 'obj_SysTabControl32' then
+ XP_Themes_For_TabPanel(Sender)
+ else
+ XP_Themes_For_Panel(Sender);
+ end;
+ end;
+ exit;
+ end;
+ end else
+ begin
+ if ((Sender.fStyle.Value and BS_AUTOCHECKBOX) = BS_AUTOCHECKBOX) and
+ (Sender.SubClassName = 'obj_BUTTON') and
+ {$IFDEF USE_FLAGS}
+ ([G5_IsGroupbox, G5_IsSplitter, G5_IsBitBtn] * Sender.fFlagsG5 = [])
+ {$ELSE}
+ (Sender.fIsGroupBox = false) and
+ (Sender.fIsSplitter = false) and
+ (Sender.fIsBitBtn = false) {$ENDIF} then
+ begin
+ Deattach(Sender, @WndCheckBoxXPDraw);
+ exit;
+ end;
+ if ((Sender.fStyle.Value and BS_AUTO3STATE) = BS_AUTO3STATE) and
+ (Sender.SubClassName = 'obj_BUTTON') and
+ {$IFDEF USE_FLAGS}
+ ([G5_IsGroupbox, G5_IsSplitter, G5_IsBitBtn] * Sender.fFlagsG5 = [])
+ {$ELSE}
+ (Sender.fIsGroupBox = false) and
+ (Sender.fIsSplitter = false) and
+ (Sender.fIsBitBtn = false) {$ENDIF} then
+ begin
+ Deattach(Sender, @WndCheckBoxXPDraw);
+ exit;
+ end;
+ if ((Sender.fStyle.Value and BS_RADIOBUTTON) = BS_RADIOBUTTON) and
+ (Sender.SubClassName = 'obj_BUTTON') and
+ {$IFDEF USE_FLAGS}
+ ([G5_IsGroupbox, G5_IsSplitter, G5_IsBitBtn] * Sender.fFlagsG5 = [])
+ {$ELSE}
+ (Sender.fIsGroupBox = false) and
+ (Sender.fIsSplitter = false) and
+ (Sender.fIsBitBtn = false) {$ENDIF} then
+ begin
+ Deattach(Sender, @WndRadioBoxXPDraw);
+ exit;
+ end;
+ if ((Sender.fStyle.Value and BS_GROUPBOX) = BS_GROUPBOX) and
+ (Sender.SubClassName = 'obj_BUTTON') and
+ {$IFDEF USE_FLAGS}
+ ([G5_IsGroupbox, G5_IsSplitter, G5_IsBitBtn] * Sender.fFlagsG5 =
+ [G5_IsGroupbox])
+ {$ELSE}
+ (Sender.fIsGroupBox = true) and
+ (Sender.fIsSplitter = false) and
+ (Sender.fIsBitBtn = false) {$ENDIF} then
+ begin
+ Deattach(Sender, @WndGroupBoxXPDraw);
+ exit;
+ end;
+ if (Sender.SubClassName = 'obj_BUTTON') and
+ {$IFDEF USE_FLAGS}
+ ([G5_IsGroupbox, G5_IsSplitter] * Sender.fFlagsG5 = [])
+ {$ELSE}
+ (Sender.fIsGroupBox = false) and
+ (Sender.fIsSplitter = false) {$ENDIF} then
+ begin
+ Deattach(Sender, @WndButtonXPDraw);
+ exit;
+ end;
+ if (Sender.SubClassName = 'obj_STATIC') then
+ begin
+ if {$IFDEF USE_FLAGS} G1_IsStaticControl in Sender.fFlagsG1
+ {$ELSE} Sender.fIsStaticControl > 0 {$ENDIF} then
+ else
+ begin
+ if {$IFDEF USE_FLAGS} G5_IsSplitter in Sender.fFlagsG5
+ {$ELSE} Sender.fIsSplitter {$ENDIF} then
+ Deattach(Sender, @WndSplitterXPDraw)
+ else if Sender.fParent.SubClassName = 'obj_SysTabControl32' then
+ Deattach(Sender, @WndTabXPDraw)
+ else
+ begin
+ Deattach(Sender, @WndPanelXPDraw);
+ case Sender.EdgeStyle of
+ esRaised:
+ begin
+ Sender.fStyle.Value := Sender.fStyle.Value and (not SS_SUNKEN);
+ Sender.fExStyle := Sender.fExStyle and (not WS_EX_STATICEDGE);
+ Sender.fExStyle := Sender.fExStyle or WS_EX_WINDOWEDGE;
+ Sender.fStyle.Value := Sender.fStyle.Value or WS_DLGFRAME;
+ end;
+ esLowered:
+ begin
+ Sender.fStyle.Value := Sender.fStyle.Value and (not WS_DLGFRAME);
+ Sender.fExStyle := Sender.fExStyle or WS_EX_WINDOWEDGE;
+ Sender.fExStyle := Sender.fExStyle or WS_EX_STATICEDGE;
+ Sender.fStyle.Value := Sender.fStyle.Value or SS_SUNKEN;
+ end;
+ else
+ Sender.fStyle.Value := Sender.fStyle.Value and (not SS_SUNKEN) and (not WS_DLGFRAME);
+ Sender.fExStyle := Sender.fExStyle and (not WS_EX_STATICEDGE) or WS_EX_WINDOWEDGE;
+ end;
+ end;
+ end;
+ Sender.SetTransparent(
+ {$IFDEF USE_FLAGS} G2_ClassicTransparent in Sender.fFlagsG2
+ {$ELSE} Sender.fClassicTransparent {$ENDIF} );
+ exit;
+ end;
+ end;
+ end;
+end;
+//********************* Attaching to message WM_THEMECHANGED *****************//
+type TSenderProc = procedure(Sender: PControl);
+{$IFDEF ASM_VERSION}
+procedure Attach_WM_THEMECHANGED(Sender : PControl; const XP_Themes_for: TSenderProc);
+asm
+ {$IFDEF USE_FLAGS}
+ MOV CX, word ptr [EAX].TControl.fFlagsG2
+ AND CX, not(1 shl G3_ClassicTransparent)shl 8 or (1 shl G2_Transparent)
+ OR CL, CH
+ MOV [EAX].TControl.fFlagsG3, CL
+ {$ELSE}
+ MOV CL, [EAX].TControl.fTransparent
+ MOV [EAX].TControl.fClassicTransparent, CL
+ {$ENDIF}
+ PUSH EDX
+ PUSH EAX
+ MOV EDX, offset[WndXP_WM_THEMECHANGED]
+ CALL TControl.AttachProc
+ POP EAX
+ POP EDX
+ CALL EDX
+end;
+{$ELSE PASCAL}
+procedure Attach_WM_THEMECHANGED(Sender : PControl; const XP_Themes_for: TSenderProc);
+begin
+ {$IFDEF USE_FLAGS}
+ if G2_Transparent in Sender.fFlagsG2 then
+ Sender.fFlagsG3 := Sender.fFlagsG3 + [G3_ClassicTransparent]
+ else
+ Sender.fFlagsG3 := Sender.fFlagsG3 - [G3_ClassicTransparent];
+ {$ELSE} Sender.fClassicTransparent := Sender.fTransparent; {$ENDIF}
+ Sender.AttachProc(WndXP_WM_THEMECHANGED);
+ XP_Themes_for(Sender);
+end;
+{$ENDIF ASM_VERSION}
+//********************************* End File *********************************//
diff --git a/plugins/Libs/zlib/Infblock.obj b/plugins/Libs/zlib/Infblock.obj
new file mode 100644
index 0000000000..a685342d13
--- /dev/null
+++ b/plugins/Libs/zlib/Infblock.obj
Binary files differ
diff --git a/plugins/Libs/zlib/Infcodes.obj b/plugins/Libs/zlib/Infcodes.obj
new file mode 100644
index 0000000000..b5a3bbbfd8
--- /dev/null
+++ b/plugins/Libs/zlib/Infcodes.obj
Binary files differ
diff --git a/plugins/Libs/zlib/Infutil.obj b/plugins/Libs/zlib/Infutil.obj
new file mode 100644
index 0000000000..b85994288b
--- /dev/null
+++ b/plugins/Libs/zlib/Infutil.obj
Binary files differ
diff --git a/plugins/Libs/zlib/adler32.obj b/plugins/Libs/zlib/adler32.obj
new file mode 100644
index 0000000000..bbc8a4efd4
--- /dev/null
+++ b/plugins/Libs/zlib/adler32.obj
Binary files differ
diff --git a/plugins/Libs/zlib/compress.obj b/plugins/Libs/zlib/compress.obj
new file mode 100644
index 0000000000..32055a01e0
--- /dev/null
+++ b/plugins/Libs/zlib/compress.obj
Binary files differ
diff --git a/plugins/Libs/zlib/crc32.obj b/plugins/Libs/zlib/crc32.obj
new file mode 100644
index 0000000000..845e1c9b66
--- /dev/null
+++ b/plugins/Libs/zlib/crc32.obj
Binary files differ
diff --git a/plugins/Libs/zlib/deflate.obj b/plugins/Libs/zlib/deflate.obj
new file mode 100644
index 0000000000..3e6baa4c0e
--- /dev/null
+++ b/plugins/Libs/zlib/deflate.obj
Binary files differ
diff --git a/plugins/Libs/zlib/infback.obj b/plugins/Libs/zlib/infback.obj
new file mode 100644
index 0000000000..8b134cbfe2
--- /dev/null
+++ b/plugins/Libs/zlib/infback.obj
Binary files differ
diff --git a/plugins/Libs/zlib/inffast.obj b/plugins/Libs/zlib/inffast.obj
new file mode 100644
index 0000000000..21eba59885
--- /dev/null
+++ b/plugins/Libs/zlib/inffast.obj
Binary files differ
diff --git a/plugins/Libs/zlib/inflate.obj b/plugins/Libs/zlib/inflate.obj
new file mode 100644
index 0000000000..7e56b8283c
--- /dev/null
+++ b/plugins/Libs/zlib/inflate.obj
Binary files differ
diff --git a/plugins/Libs/zlib/inftrees.obj b/plugins/Libs/zlib/inftrees.obj
new file mode 100644
index 0000000000..793ddc2d83
--- /dev/null
+++ b/plugins/Libs/zlib/inftrees.obj
Binary files differ
diff --git a/plugins/Libs/zlib/trees.obj b/plugins/Libs/zlib/trees.obj
new file mode 100644
index 0000000000..d9a5b59ee8
--- /dev/null
+++ b/plugins/Libs/zlib/trees.obj
Binary files differ
diff --git a/plugins/Libs/zlib/uncompr.obj b/plugins/Libs/zlib/uncompr.obj
new file mode 100644
index 0000000000..7d72a142ef
--- /dev/null
+++ b/plugins/Libs/zlib/uncompr.obj
Binary files differ
diff --git a/plugins/QuickSearch/ico/default.ico b/plugins/QuickSearch/ico/default.ico
new file mode 100644
index 0000000000..4cd27f0623
--- /dev/null
+++ b/plugins/QuickSearch/ico/default.ico
Binary files differ
diff --git a/plugins/QuickSearch/ico/delete.ico b/plugins/QuickSearch/ico/delete.ico
new file mode 100644
index 0000000000..07fee6c512
--- /dev/null
+++ b/plugins/QuickSearch/ico/delete.ico
Binary files differ
diff --git a/plugins/QuickSearch/ico/down.ico b/plugins/QuickSearch/ico/down.ico
new file mode 100644
index 0000000000..d4fdb83bbf
--- /dev/null
+++ b/plugins/QuickSearch/ico/down.ico
Binary files differ
diff --git a/plugins/QuickSearch/ico/female.ico b/plugins/QuickSearch/ico/female.ico
new file mode 100644
index 0000000000..fe1cbd2bce
--- /dev/null
+++ b/plugins/QuickSearch/ico/female.ico
Binary files differ
diff --git a/plugins/QuickSearch/ico/item.ico b/plugins/QuickSearch/ico/item.ico
new file mode 100644
index 0000000000..80c3802c09
--- /dev/null
+++ b/plugins/QuickSearch/ico/item.ico
Binary files differ
diff --git a/plugins/QuickSearch/ico/male.ico b/plugins/QuickSearch/ico/male.ico
new file mode 100644
index 0000000000..ebd0420554
--- /dev/null
+++ b/plugins/QuickSearch/ico/male.ico
Binary files differ
diff --git a/plugins/QuickSearch/ico/new.ico b/plugins/QuickSearch/ico/new.ico
new file mode 100644
index 0000000000..ac80adb26e
--- /dev/null
+++ b/plugins/QuickSearch/ico/new.ico
Binary files differ
diff --git a/plugins/QuickSearch/ico/qs.ico b/plugins/QuickSearch/ico/qs.ico
new file mode 100644
index 0000000000..415a0bccd7
--- /dev/null
+++ b/plugins/QuickSearch/ico/qs.ico
Binary files differ
diff --git a/plugins/QuickSearch/ico/reload.ico b/plugins/QuickSearch/ico/reload.ico
new file mode 100644
index 0000000000..dc070c5083
--- /dev/null
+++ b/plugins/QuickSearch/ico/reload.ico
Binary files differ
diff --git a/plugins/QuickSearch/ico/up.ico b/plugins/QuickSearch/ico/up.ico
new file mode 100644
index 0000000000..56fde31eda
--- /dev/null
+++ b/plugins/QuickSearch/ico/up.ico
Binary files differ
diff --git a/plugins/QuickSearch/make.bat b/plugins/QuickSearch/make.bat
new file mode 100644
index 0000000000..36de9c338c
--- /dev/null
+++ b/plugins/QuickSearch/make.bat
@@ -0,0 +1,17 @@
+@echo off
+set myopts=-dMiranda
+set dprname=quicksearch.dpr
+
+..\delphi\brcc32.exe qs.rc -foqs.res
+
+if /i '%1' == 'fpc' (
+ ..\FPC\bin\fpc.exe %myopts% %dprname% %2 %3 %4 %5 %6 %7 %8 %9
+) else if /i '%1' == 'fpc64' (
+ ..\FPC\bin64\ppcrossx64.exe %myopts% %dprname% %2 %3 %4 %5 %6 %7 %8 %9
+) else if /i '%1' == 'xe2' (
+ ..\XE2\BIN\dcc32.exe %myopts% %dprname% %2 %3 %4 %5 %6 %7 %8 %9
+) else if /i '%1' == 'xe64' (
+ ..\XE2\BIN\dcc64.exe %myopts% %dprname% %2 %3 %4 %5 %6 %7 %8 %9
+) else (
+ ..\delphi\dcc32 %myopts% %dprname% %1 %2 %3 %4 %5 %6 %7 %8 %9
+)
diff --git a/plugins/QuickSearch/qs.rc b/plugins/QuickSearch/qs.rc
new file mode 100644
index 0000000000..d22a32c57d
--- /dev/null
+++ b/plugins/QuickSearch/qs.rc
@@ -0,0 +1,178 @@
+#include "resource.inc"
+
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+
+IDD_FRAME DIALOGEX 0, 0, 114, 16, 0
+STYLE DS_SETFONT | WS_CHILD | DS_FIXEDSYS | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ PUSHBUTTON "*", IDC_FRAME_OPEN, 2, 2, 12, 12
+ EDITTEXT IDC_FRAME_EDIT, 16, 2, 62, 12
+ PUSHBUTTON "<", IDC_FRAME_PREV, 80, 2, 15, 12
+ PUSHBUTTON ">", IDC_FRAME_NEXT, 97, 2, 15, 12
+}
+
+IDD_SCRIPT DIALOGEX 0, 0, 256, 82, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_VISIBLE | WS_THICKFRAME
+CAPTION "Script Editor"
+//EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ EDITTEXT IDC_EDIT_SCRIPT , 4, 4, 248, 56,
+ ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL
+ PUSHBUTTON "&OK" , IDOK , 106, 64, 46, 14
+ PUSHBUTTON "&Help" , IDHELP , 156, 64, 46, 14
+ PUSHBUTTON "C&ancel", IDCANCEL , 206, 64, 46, 14
+}
+
+IDD_DIALOG1 DIALOGEX 40, 40, 314, 240
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE | WS_BORDER
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "Ms Shell Dlg",0,0
+{
+ CONTROL "New" ,IDC_NEW ,"MButtonClass",WS_TABSTOP,216, 46,16,16,$18000000
+ CONTROL "Save" ,IDC_SETITEM,"MButtonClass",WS_TABSTOP,216, 64,16,16,$18000000
+ CONTROL "Up" ,IDC_UP ,"MButtonClass",WS_TABSTOP,216, 82,16,16,$18000000
+ CONTROL "Down" ,IDC_DN ,"MButtonClass",WS_TABSTOP,216,100,16,16,$18000000
+ CONTROL "Delete" ,IDC_DELETE ,"MButtonClass",WS_TABSTOP,216,118,16,16,$18000000
+ CONTROL "Default",IDC_DEFAULT,"MButtonClass",WS_TABSTOP,216,136,16,16,$18000000
+ CONTROL "Reload" ,IDC_RELOAD ,"MButtonClass",WS_TABSTOP,216, 2,16,16,$18000000
+
+ CONTROL "List2",IDC_LIST,"SysListView32",LVS_REPORT |
+ LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER |
+ WS_BORDER | WS_TABSTOP,0,2,213,150
+
+ CTEXT "Settings",-1 ,234, 2,77,12, SS_CENTERIMAGE
+ CONTROL "", -1, "STATIC", SS_ETCHEDHORZ, 234, 14, 77, 2
+
+ LTEXT "Title:",-1 ,236, 16,75,12, SS_CENTERIMAGE
+ EDITTEXT IDC_E_TITLE ,234, 28,77,14, ES_AUTOHSCROLL | WS_TABSTOP
+
+ LTEXT "Type:",-1 ,236, 44,75,12, SS_CENTERIMAGE
+ COMBOBOX IDC_C_VARTYPE ,234, 56,77,110, CBS_DROPDOWNLIST |
+ CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+
+ LTEXT "InfoType:",IDC_STAT_VARTYPE,236, 72,75,12, SS_CENTERIMAGE
+ COMBOBOX IDC_C_CNFTYPE ,234, 84,77,150, CBS_DROPDOWNLIST |
+ CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+ COMBOBOX IDC_C_RESULT ,234, 84,77,80, CBS_DROPDOWNLIST |
+ CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "Script",IDC_SCRIPT, 234, 84, 77, 12
+
+ LTEXT "Service:",IDC_STAT_SERVICE ,236,100,75,12, SS_CENTERIMAGE
+ LTEXT "Module:" ,IDC_STAT_MODULE ,236,100,75,12, SS_CENTERIMAGE
+ EDITTEXT IDC_E_MODULE ,234,112,77,14, ES_AUTOHSCROLL | WS_TABSTOP
+
+ LTEXT "wParam:",IDC_STAT_WPAR ,236,128,75,12, SS_CENTERIMAGE
+ LTEXT "Setting:",IDC_STAT_SETTING ,236,128,75,12, SS_CENTERIMAGE
+ EDITTEXT IDC_E_VAR ,234,140,77,14, ES_AUTOHSCROLL | WS_TABSTOP
+
+ LTEXT "wParam type:",-1,236,156,75,12, SS_CENTERIMAGE
+ COMBOBOX IDC_C_WPAR ,234,168,77,80, CBS_DROPDOWNLIST |
+ CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+
+ LTEXT "lParam:",-1,236,184,75,12, SS_CENTERIMAGE
+ EDITTEXT IDC_E_LPAR ,234,196,77,14, ES_AUTOHSCROLL | WS_TABSTOP
+
+ LTEXT "lParam type:",-1 ,236,212,75,12, SS_CENTERIMAGE
+ COMBOBOX IDC_C_LPAR ,234,224,77,80, CBS_DROPDOWNLIST |
+ CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+
+ CONTROL "Sort by Status",IDC_CH_SORTSTATUS,"Button",
+ BS_MULTILINE | BS_AUTOCHECKBOX | BS_FLAT | WS_TABSTOP,5,166,109,10
+ CONTROL "Only Users in List",IDC_CH_SHOWONLYUSERS,"Button",
+ BS_MULTILINE | BS_AUTOCHECKBOX | BS_FLAT | WS_TABSTOP,5,178,109,10
+ CONTROL "Auto Close mode",IDC_CH_AUTOCLOSE,"Button",
+ BS_MULTILINE | BS_AUTOCHECKBOX | BS_FLAT | WS_TABSTOP,5,190,109,10
+ CONTROL "Draw Grid",IDC_CH_DRAWGRID,"Button",
+ BS_MULTILINE | BS_AUTOCHECKBOX | BS_FLAT | WS_TABSTOP,5,202,109,10
+ CONTROL "Show Client Icons",IDC_CH_SHOWCLIENTICONS,"Button",
+ BS_MULTILINE | BS_AUTOCHECKBOX | BS_FLAT | WS_TABSTOP,5,214,109,10
+ CONTROL "Save search pattern",IDC_CH_SAVEPATTERN,"Button",
+ BS_MULTILINE | BS_AUTOCHECKBOX | BS_FLAT | WS_TABSTOP,5,226,109,10
+ GROUPBOX "",-1,0,154,116,84
+
+ CONTROL "Item in Main Menu",IDC_CH_SHOWINMENU,"Button",
+ BS_MULTILINE | BS_AUTOCHECKBOX | BS_FLAT | WS_TABSTOP,121,166,109,10
+ CONTROL "Button on TopToolBar",IDC_CH_ADDTOTOPTOOLBAR,"Button",
+ BS_MULTILINE | BS_AUTOCHECKBOX | BS_FLAT | WS_TABSTOP,121,178,109,10
+ CONTROL "Tool Window Style",IDC_CH_USETOOLSTYLE,"Button",
+ BS_MULTILINE | BS_AUTOCHECKBOX | BS_FLAT | WS_TABSTOP,121,190,109,10
+ CONTROL "Copy line to CSV",IDC_CH_SINGLECSV,"Button",
+ BS_MULTILINE | BS_AUTOCHECKBOX | BS_FLAT | WS_TABSTOP,121,202,109,10
+ CONTROL "CSV with headers",IDC_CH_EXPORTHEADERS,"Button",
+ BS_MULTILINE | BS_AUTOCHECKBOX | BS_FLAT | WS_TABSTOP,121,214,109,10
+ CONTROL "Skip minimized columns",IDC_CH_SKIPMINIMIZED,"Button",
+ BS_MULTILINE | BS_AUTOCHECKBOX | BS_FLAT | WS_TABSTOP,121,226,109,10
+ GROUPBOX "",-1,118,154,114,84
+ GROUPBOX "Additional Options",-1,0,154,232,84
+}
+
+IDI_QS ICON DISCARDABLE "ico\qs.ico"
+
+IDI_NEW ICON DISCARDABLE "ico\new.ico"
+IDI_ITEM ICON DISCARDABLE "ico\item.ico"
+IDI_UP ICON DISCARDABLE "ico\up.ico"
+IDI_DOWN ICON DISCARDABLE "ico\down.ico"
+IDI_DELETE ICON DISCARDABLE "ico\delete.ico"
+IDI_DEFAULT ICON DISCARDABLE "ico\default.ico"
+IDI_RELOAD ICON DISCARDABLE "ico\reload.ico"
+
+IDI_MALE ICON DISCARDABLE "ico\male.ico"
+IDI_FEMALE ICON DISCARDABLE "ico\female.ico"
+
+IDD_MAIN DIALOGEX 40, 40, 520, 240
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CAPTION | WS_VISIBLE | WS_BORDER | WS_SYSMENU |
+ WS_DLGFRAME | WS_OVERLAPPED | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_THICKFRAME
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "Ms Shell Dlg",0,0
+{
+ EDITTEXT IDC_E_SEARCHTEXT, 180,8,276,14, WS_TABSTOP// | ES_LOWERCASE
+ PUSHBUTTON "Close" , IDCANCEL , 462, 0,52,14, WS_TABSTOP
+ PUSHBUTTON "Refresh", IDC_REFRESH, 462,16,52,14, WS_TABSTOP
+
+ CONTROL "Show Offline contacts",IDC_CH_SHOWOFFLINE,"Button",
+ BS_AUTOCHECKBOX | BS_FLAT | BS_MULTILINE | BS_VCENTER | WS_TABSTOP,2,0,96,20
+ CONTROL "Colorize",IDC_CH_COLORIZE,"Button",
+ BS_AUTOCHECKBOX | BS_FLAT | BS_MULTILINE | BS_VCENTER | WS_TABSTOP,2,20,96,10
+// LTEXT "Search for:",-1, 4,4,52,14, SS_CENTERIMAGE
+ COMBOBOX IDC_CB_PROTOCOLS,100,8,76,80, CBS_DROPDOWNLIST |
+ CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
+ CONTROL "", IDC_LIST, "SysListView32", WS_BORDER | WS_TABSTOP | // LVS_SHAREIMAGELISTS | // LVS_EX_SUBITEMIMAGES |
+ LVS_SHOWSELALWAYS | LVS_REPORT | LVS_NOLABELWRAP,
+ 2, 32, 516, 194 //, WS_EX_CONTROLPARENT
+
+ CONTROL "", IDC_STATUSBAR, "msctls_statusbar32",
+ CCS_BOTTOM | 0x0900, // SBT_TOOLTIPS | SBARS_SIZEGRIP
+ 0,226,520,14
+}
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,4,1,18
+ PRODUCTVERSION 0,8,0,0
+ FILEFLAGSMASK $3F
+ FILEOS 4
+ FILETYPE 2
+ FILESUBTYPE 0
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "CompanyName",""
+ VALUE "Comments", "Plugin to quick search for nickname, firstname, lastname, email, uin in your contact list"0
+ VALUE "FileDescription", "Quick info search plugin for Miranda NG"0
+ VALUE "FileVersion", "1, 4, 1, 18 "0
+ VALUE "InternalName", "QuickSearchMod"0
+ VALUE "OriginalFilename", "quicksearch.dll"0
+ VALUE "ProductName", "QuickSearchMod Dynamic Link Library (DLL)"0
+ VALUE "ProductVersion", "0, 8, 0, 0 "0
+ VALUE "SpecialBuild", "2.07.2010 "0
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation",0,1200
+ END
+END
diff --git a/plugins/QuickSearch/qs.res b/plugins/QuickSearch/qs.res
new file mode 100644
index 0000000000..891c078829
--- /dev/null
+++ b/plugins/QuickSearch/qs.res
Binary files differ
diff --git a/plugins/QuickSearch/quicksearch.dpr b/plugins/QuickSearch/quicksearch.dpr
new file mode 100644
index 0000000000..699472b7b1
--- /dev/null
+++ b/plugins/QuickSearch/quicksearch.dpr
@@ -0,0 +1,244 @@
+{$include compilers.inc}
+{$IFDEF COMPILER_16_UP}
+ {$WEAKLINKRTTI ON}
+ {.$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([])}
+{$ENDIF}
+{$IMAGEBASE $13100000}
+library quicksearch;
+
+{$R qs.res}
+
+uses
+// FastMM4,
+ Windows,
+ Messages,
+ m_api,
+ sr_optdialog,
+ sr_global,
+ sr_window,
+ sr_frame,
+ mirutils,
+ common;
+
+var
+ opthook:cardinal;
+ onloadhook:cardinal;
+ onstatus,
+ ondelete,
+// onaccount,
+ onadd:cardinal;
+ servshow:cardinal;
+
+const
+ icohook:THANDLE = 0;
+
+function MirandaPluginInfoEx(mirandaVersion:DWORD):PPLUGININFOEX; cdecl;
+begin
+ result:=@PluginInfo;
+ PluginInfo.cbSize :=SizeOf(TPLUGININFOEX);
+ PluginInfo.shortName :='Quick Search Mod';
+ PluginInfo.version :=$01040112;
+ PluginInfo.description:=
+ 'This Plugin allow you to quick search for nickname,'+
+ 'firstname, lastname, email, uin in your contact list.'+
+ 'And now you may add any setting to display - for example'+
+ 'users version of miranda,group or city.';
+ PluginInfo.author :='Awkward, based on Bethoven sources';
+ PluginInfo.authorEmail:='panda75@bk.ru; awk1975@ya.ru';
+ PluginInfo.copyright :='(c) 2004,2005 Bethoven; 2006-2012 Awkward';
+ PluginInfo.homepage :='http://code.google.com/p/delphi-miranda-plugins/';
+ PluginInfo.flags :=UNICODE_AWARE;
+ PluginInfo.uuid :=MIID_QUICKSEARCH;
+end;
+
+function OnTTBLoaded(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+begin
+ addtotoolbar;
+ result:=0;
+end;
+
+function IconChanged(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ mi:TCListMenuItem;
+ ttb:TTBButton;
+begin
+ result:=0;
+ FillChar(mi,SizeOf(mi),0);
+ mi.cbSize:=sizeof(mi);
+ mi.flags :=CMIM_ICON;
+
+ mi.hIcon:=CallService(MS_SKIN2_GETICON,0,tlparam(QS_QS));
+ CallService(MS_CLIST_MODIFYMENUITEM,MainMenuItem,tlparam(@mi));
+
+// toptoolbar
+ if ServiceExists(MS_TTB_GETBUTTONOPTIONS)<>0 then
+ begin
+ CallService(MS_TTB_GETBUTTONOPTIONS,(hTTBButton shl 16)+TTBO_ALLDATA,TLPARAM(@ttb));
+ ttb.hIconUp:=CallService(MS_SKIN2_GETICON,0,TLPARAM(QS_QS));
+ ttb.hIconDn:=ttb.hIconUp;
+ CallService(MS_TTB_SETBUTTONOPTIONS,(hTTBButton shl 16)+TTBO_ALLDATA,TLPARAM(@ttb));
+ end;
+
+end;
+
+procedure RegisterIcons;
+var
+ sid:TSKINICONDESC;
+begin
+ FillChar(sid,SizeOf(TSKINICONDESC),0);
+ sid.cbSize :=SizeOf(TSKINICONDESC);
+ sid.cx :=16;
+ sid.cy :=16;
+ sid.szSection.a:=qs_module;
+
+ sid.hDefaultIcon :=LoadImage(hInstance,MAKEINTRESOURCE(IDI_QS),IMAGE_ICON,16,16,0);
+ sid.pszName :=QS_QS;
+ sid.szDescription.a:=qs_name;
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+
+ sid.hDefaultIcon :=LoadImage(hInstance,MAKEINTRESOURCE(IDI_NEW),IMAGE_ICON,16,16,0);
+ sid.pszName :=QS_NEW;
+ sid.szDescription.a:='New Column';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+
+ sid.hDefaultIcon :=LoadImage(hInstance,MAKEINTRESOURCE(IDI_ITEM),IMAGE_ICON,16,16,0);
+ sid.pszName :=QS_ITEM;
+ sid.szDescription.a:='Save Column';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+
+ sid.hDefaultIcon :=LoadImage(hInstance,MAKEINTRESOURCE(IDI_UP),IMAGE_ICON,16,16,0);
+ sid.pszName :=QS_UP;
+ sid.szDescription.a:='Column Up';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+
+ sid.hDefaultIcon :=LoadImage(hInstance,MAKEINTRESOURCE(IDI_DOWN),IMAGE_ICON,16,16,0);
+ sid.pszName :=QS_DOWN;
+ sid.szDescription.a:='Column Down';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+
+ sid.hDefaultIcon :=LoadImage(hInstance,MAKEINTRESOURCE(IDI_DELETE),IMAGE_ICON,16,16,0);
+ sid.pszName :=QS_DELETE;
+ sid.szDescription.a:='Delete Column';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+
+ sid.hDefaultIcon :=LoadImage(hInstance,MAKEINTRESOURCE(IDI_DEFAULT),IMAGE_ICON,16,16,0);
+ sid.pszName :=QS_DEFAULT;
+ sid.szDescription.a:='Default';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+
+ sid.hDefaultIcon :=LoadImage(hInstance,MAKEINTRESOURCE(IDI_RELOAD),IMAGE_ICON,16,16,0);
+ sid.pszName :=QS_RELOAD;
+ sid.szDescription.a:='Reload';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+
+ sid.hDefaultIcon :=LoadImage(hInstance,MAKEINTRESOURCE(IDI_MALE),IMAGE_ICON,16,16,0);
+ sid.pszName :=QS_MALE;
+ sid.szDescription.a:='Male';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+
+ sid.hDefaultIcon :=LoadImage(hInstance,MAKEINTRESOURCE(IDI_FEMALE),IMAGE_ICON,16,16,0);
+ sid.pszName :=QS_FEMALE;
+ sid.szDescription.a:='Female';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+
+ icohook:=HookEvent(ME_SKIN2_ICONSCHANGED,@IconChanged);
+end;
+
+function OnOptInitialise(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ odp:TOPTIONSDIALOGPAGE;
+begin
+ ZeroMemory(@odp,sizeof(odp));
+ odp.cbSize :=SizeOf(odp); //for 0.6+ compatibility
+ odp.Position :=900003000;
+ odp.hInstance :=hInstance;
+ odp.pszTemplate:=PAnsiChar(IDD_DIALOG1);
+ odp.szTitle.a :=qs_name;
+ odp.szGroup.a :='Contact List';
+ odp.pfnDlgProc :=@sr_optdialog.DlgProcOptions;
+ odp.flags :=ODPF_BOLDGROUPS;
+ Options_AddPage(wParam,@odp);
+// CallService(MS_OPT_ADDPAGE,wParam,tlparam(@odp));
+ Result:=0;
+end;
+
+function OpenSearchWindow(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+begin
+ result:=0;
+ if not opened then
+ OpenSrWindow(pointer(wParam),lParam)
+ else
+ BringToFront;
+end;
+
+function OnModulesLoaded(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+begin
+ UnhookEvent(onloadhook);
+
+ CallService(MS_DBEDIT_REGISTERSINGLEMODULE,twparam(qs_module),0);
+
+ RegisterIcons;
+ RegisterColors;
+
+ servshow:=CreateServiceFunction(QS_SHOWSERVICE,@OpenSearchWindow);
+ AddRemoveMenuItemToMainMenu;
+
+ reghotkeys;
+
+ onadd :=HookEvent(ME_DB_CONTACT_ADDED ,@OnContactAdded);
+ ondelete :=HookEvent(ME_DB_CONTACT_DELETED ,@OnContactDeleted);
+ onstatus :=HookEvent(ME_CLIST_CONTACTICONCHANGED,@OnStatusChanged);
+// onaccount:=HookEvent(ME_PROTO_ACCLISTCHANGED ,@OnAccountChanged);
+ HookEvent(ME_TTB_MODULELOADED,@OnTTBLoaded);
+
+ CreateFrame(0);
+ Result:=0;
+end;
+
+function Load():Integer;cdecl;
+begin
+ Result:=0;
+ Langpack_register;
+ opthook :=HookEvent(ME_OPT_INITIALISE ,@OnOptInitialise);
+ onloadhook:=HookEvent(ME_SYSTEM_MODULESLOADED,@OnModulesLoaded);
+ loadopt_db(true);
+end;
+
+function Unload:Integer;cdecl;
+begin
+ result:=0;
+ removetoolbar; //??
+ DestroyFrame;
+
+ DestroyServiceFunction(servshow);
+ UnhookEvent(opthook);
+ UnhookEvent(onadd);
+ UnhookEvent(ondelete);
+ UnhookEvent(onstatus);
+// UnhookEvent(onaccount);
+ if icohook<>0 then
+ UnhookEvent(icohook);
+
+// unreghotkeys;
+
+ CloseSrWindow;
+
+ clear_columns;
+end;
+
+exports
+ Load, Unload,
+ MirandaPluginInfoEx;
+
+begin
+end.
diff --git a/plugins/QuickSearch/quicksearch_history.txt b/plugins/QuickSearch/quicksearch_history.txt
new file mode 100644
index 0000000000..17cfce6852
--- /dev/null
+++ b/plugins/QuickSearch/quicksearch_history.txt
@@ -0,0 +1,110 @@
+Service:
+ wParam: 0, or pattern string
+ lParam: 0 - wParam has unicode string;
+ 1 - Ansi String (only Unicode plugin version)
+ 2 - wParam is contact handle to select (not realized)
+ service:"QuickSearch_PLUGIN/Show"
+
+History:
+
+1.4.1.19 ()
+ FastMM4 memory manager used (not native Delphi or miranda)
+ Fixed some Memory leaks
+1.4.1.18 (2 jul 2010)
+ Doubleclick/Enter on contact set as Clist doubleclick action
+ Group list sorted now
+ Fixed main window Refresh action (F5 and button)
+ Added frame button to open Main QS window
+1.4.1.17 (5 may 2010)
+ Added contact list frame
+ Fixed columns sort
+ Added right click on columns header processing
+ Filter applying just for visible columns now
+ Added tooltip for hidden columns info
+ Added several User Info fields
+ Added button and hotkey F5 to refresh content of main window
+ Partial code refactoring
+ Fixed again info showing in window with hidden columns
+1.4.0.16 (16 jul 2009)
+ Fixed info showing in window with hidden columns
+1.4.0.15 (13 jul 2009)
+ Fixed empty line for hidden contacts and contacts of disabled accounts
+1.4.0.14 (11 jul 2009)
+ Added checkbox for hide columns from showing
+1.4.0.13 (10 jul 2009)
+ Added context menu item to combine selected contacts to metacontact
+ Added new column type for metacontacts
+ Added colorization for metacontacts
+1.4.0.12 (jun 2009)
+ Fixed fields drawing in options dialog
+ Added colorisation for not-in-list contacts and on switched off or deleted accounts
+ Fixes for Statusbar
+ Fixes for Show/hide offline contacts
+1.4.0.11 (13 mar 2009)
+ Added column type "Variables script"
+ Added translation to column types
+ Fixed Show/hide offline contacts behaviour
+ Fixed one of unicode localization bugs
+ Fixed contact copying to Clipboard
+1.4.0.10 (19 oct 2008)
+ Some fixes
+ Some WinAPI/Delphi functions replaced by handmaked
+ Added support for Miranda core hotkeys (ver 8.0+)
+ Added:Drag&Drop (realtime) QS list column reordering
+ In option:list scrolling to newly added items
+ Added ANSI code part
+ Fixed: empty LastEvent field was at top of event list
+1.4.0.9 (11 sep 2008)
+ Fixed: Can crashed with content filter
+ Maked list filling faster
+ Changed: maximum column amount changing dynamically now
+ Source changed: AnsiChar replaced by AnsiChar
+ Removed some codeparts for Miranda v0.5 compatibility
+ Fixed: wrong QS window width resizing
+1.4.0.8 (28 aug 2008)
+ Fixed crash while adding contact with opened QS window
+1.4.0.7 (21 jul 2008)
+ Added icons for XStatus
+ Fixed unicode filter
+ Added internal caching
+(29 oct 2007)
+ Added tooltips for IM-client and gender columns
+ Changed QS window icon assign code
+ Changed group of contacts delete code
+ Added filter groups in "" (start quote must be first AnsiChar or after space)
+(19 may 2007)
+ Added option to save text search pattern
+1.4.0.6 (09 mar 2007)
+ Added: Option to skip 'minimized' columns while copying to Clipboard
+ Fixed: Abort context menu move contacts to first group in list
+ Ctrl-C (or &Copy context menu command) copying info with TAB separated fields
+ Added: new miranda API support
+1.4.0.5 (21 jan 2007)
+ Fixed: Contact menu do not execute action when Auto close mode is ON
+ Fixed: Gender icon not shown when icolib not installed
+ Partially solved problem with large system fonts
+ Fixed: changed menu/toolbar icon was not set at start
+ Added Gender reading from UserInfo, not only protocol
+1.4.0.4 (22 dec 2006)
+ Added Male/Female icon and special gender processing
+ Letters and BackSpace works in the list
+ Ctrl-C in list copying contact info to Clipboard
+ Added StatusBar
+ Slightly modified Option page
+ Added option to disable client icon
+ Added Updater support
+ Added IcoLib support
+1.4.0.3 (18 dec 2006)
+ Service was advanced
+ Fixed: Enter on edit field removes contact from group
+1.4.0.2 (17 dec 2006)
+ Added option to show/hide offline contacts
+ Added "Stay on top" option to sysmenu
+ Fixed: context menu shown only one time
+1.4.0.1 (17 dec 2006)
+ Fixed: Options not shown in Miranda before 0.6 version
+ Small code and interface changes
+ First item in list now focused automatically
+ Fixed: search pattern work only with small characters
+1.4.0.0 (14 dec 2006)
+ Initial release \ No newline at end of file
diff --git a/plugins/QuickSearch/resource.inc b/plugins/QuickSearch/resource.inc
new file mode 100644
index 0000000000..d31773d79e
--- /dev/null
+++ b/plugins/QuickSearch/resource.inc
@@ -0,0 +1,76 @@
+const
+ IDD_DIALOG1 = 101;
+ IDD_MAIN = 102;
+ IDD_SCRIPT = 103;
+ IDD_FRAME = 104;
+
+ IDI_QS = 107;
+
+ IDI_NEW = 110;
+ IDI_ITEM = 111;
+ IDI_UP = 112;
+ IDI_DOWN = 113;
+ IDI_DELETE = 114;
+ IDI_DEFAULT = 115;
+ IDI_RELOAD = 116;
+
+ IDI_MALE = 120;
+ IDI_FEMALE = 121;
+
+ IDC_FRAME_EDIT = 1025;
+ IDC_FRAME_PREV = 1026;
+ IDC_FRAME_NEXT = 1027;
+ IDC_FRAME_OPEN = 1028;
+
+ IDC_NEW = 1001;
+ IDC_DELETE = 1002;
+ IDC_UP = 1003;
+ IDC_DN = 1004;
+ IDC_DEFAULT = 1005;
+ IDC_SETITEM = 1006;
+ IDC_RELOAD = 1007;
+
+ IDC_E_TITLE = 1010;
+ IDC_E_MODULE = 1011;
+ IDC_E_VAR = 1012;
+ IDC_E_LPAR = 1013;
+ IDC_HOTKEYGLOB = 1014;
+ IDC_HOTKEYLOC = 1015;
+ IDC_HKGROUP = 1016;
+ IDC_STAT_GLOBAL = 1017;
+ IDC_STAT_LOCAL = 1018;
+ IDC_LIST = 1020;
+ IDC_C_VARTYPE = 1021;
+ IDC_C_CNFTYPE = 1022;
+ IDC_CB_MAIN = 1023;
+ IDC_C_WPAR = 1024;
+ IDC_C_LPAR = 1025;
+ IDC_C_RESULT = 1026;
+ IDC_CH_SORTSTATUS = 1030;
+ IDC_CH_SHOWINMENU = 1031;
+ IDC_CH_SHOWONLYUSERS = 1032;
+ IDC_CH_ADDTOTOPTOOLBAR = 1033;
+ IDC_CH_AUTOCLOSE = 1034;
+ IDC_CH_USETOOLSTYLE = 1035;
+ IDC_CH_HOOKCHANGES = 1036;
+ IDC_CH_DRAWGRID = 1037;
+ IDC_CH_SHOWCLIENTICONS = 1038;
+ IDC_STAT_SERVICE = 1040;
+ IDC_STAT_MODULE = 1041;
+ IDC_STAT_WPAR = 1042;
+ IDC_STAT_SETTING = 1043;
+ IDC_CH_SINGLECSV = 1044;
+ IDC_CH_EXPORTHEADERS = 1045;
+ IDC_CH_SKIPMINIMIZED = 1046;
+ IDC_CH_SAVEPATTERN = 1047;
+ IDC_STAT_VARTYPE = 1048;
+ IDC_SCRIPT = 1049;
+
+ IDC_E_SEARCHTEXT = 1001;
+ IDC_CH_SHOWOFFLINE = 1031;
+ IDC_CH_COLORIZE = 1032;
+ IDC_REFRESH = 1033;
+ IDC_STATUSBAR = 1034;
+ IDC_CB_PROTOCOLS = 1035;
+
+ IDC_EDIT_SCRIPT = 1025;
diff --git a/plugins/QuickSearch/sr_frame.pas b/plugins/QuickSearch/sr_frame.pas
new file mode 100644
index 0000000000..bf3e576554
--- /dev/null
+++ b/plugins/QuickSearch/sr_frame.pas
@@ -0,0 +1,343 @@
+unit sr_frame;
+
+interface
+
+uses windows;
+
+procedure CreateFrame(parent:HWND);
+procedure DestroyFrame;
+
+implementation
+
+uses commctrl,Messages,m_api,common,wrapper,mirutils,sr_global;
+
+{.$include resource.inc}
+
+const
+ frm_back:pAnsiChar = 'Frame background';
+const
+ FrameWnd:HWND = 0;
+ FrameId:integer = -1;
+ OldEditProc:pointer=nil;
+ pattern:pWideChar=nil;
+ current: THANDLE = 0;
+var
+ colorhook:THANDLE;
+ hbr:HBRUSH;
+ frm_bkg:TCOLORREF;
+
+function CheckContact(wnd:HWND; hContact:THANDLE):THANDLE;
+var
+ buf:array [0..127] of WideChar;
+begin
+ result:=0;
+
+ SendMessage(wnd,CLM_GETITEMTEXT,hContact,lparam(@buf));
+
+ if StrPosW(CharLowerW(@buf),pattern)<>nil then
+ result:=hContact;
+end;
+
+function GetNextContact(wnd:HWND; root,hContact:THANDLE; direction,skip:integer):THANDLE;
+var
+ tmp:THANDLE;
+// buf:array [0..127] of WideChar;
+begin
+ result:=0;
+ repeat
+
+ if skip=0 then
+ begin
+ case SendMessage(wnd,CLM_GETITEMTYPE,hContact,0) of
+ CLCIT_GROUP: begin
+ if SendMessage(wnd,CLM_GETEXPAND,hContact,0)=CLE_EXPAND then // expanded only
+ begin
+ tmp:=SendMessage(wnd,CLM_GETNEXTITEM,CLGN_CHILD,hContact);
+ if tmp<>0 then
+ begin
+ hContact:=tmp;
+ if direction=CLGN_PREVIOUS then
+ begin // set on last contact in group
+ repeat
+ tmp:=SendMessage(wnd,CLM_GETNEXTITEM,CLGN_NEXT,hContact);
+ if tmp<>0 then
+ hContact:=tmp
+ else
+ break;
+ until false;
+ end;
+ continue;
+ end;
+ end;
+ end;
+ CLCIT_CONTACT: begin
+ result:=CheckContact(wnd,hContact);
+ if result<>0 then
+ break;
+ end;
+ else // CLCIT_INVALID for example
+ break;
+ end;
+ end
+ else
+ skip:=0;
+
+ if hContact=HCONTACT_ISGROUP then
+// if (hContact=root) and (direction=CLGN_PREVIOUS) then // 1st group, no need to process "root=clist"
+ break;
+
+ tmp:=SendMessage(wnd,CLM_GETNEXTITEM,direction,hContact);
+ if tmp=0 then
+ begin
+ hContact:=SendMessage(wnd,CLM_GETNEXTITEM,CLGN_PARENT,hContact);
+ // not here but where group insert
+ skip:=1;
+ continue;
+ // hContact:=SendMessage(wnd,CLM_GETNEXTITEM,direction,hContact);
+ if hContact=0 then
+ break;
+ end
+ else
+ hContact:=tmp;
+ until false;
+end;
+
+function SearchContact(direction:integer;skip:integer=1):integer;
+var
+ root,hContact:THANDLE;
+ wnd:HWND;
+begin
+ result:=0;
+ if (pattern=nil) or (pattern^=#0) then
+ begin
+ current:=0; // next seach - from start
+ exit; // pattern empty
+ end;
+
+ wnd:=CallService(MS_CLUI_GETHWNDTREE,0,0);
+
+ root:=SendMessage(wnd,CLM_GETNEXTITEM,CLGN_ROOT,0);
+ if current=0 then
+ hContact:=root
+ else
+ hContact:=current;
+
+ result:=GetNextContact(wnd,root,hContact,direction,skip);
+
+ if result<>0 then
+ SetCListSelContact(result);
+end;
+
+function NewEditProc(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+begin
+// result:=0;
+ case hMessage of
+ WM_CHAR: if wParam=27 then
+ begin
+ // clear edit field
+ SendMessage(Dialog,WM_SETTEXT,0,0);
+ // go to top?
+ current:=0;
+ end;
+
+ WM_KEYDOWN: begin
+ case wParam of
+ VK_PRIOR,VK_UP: begin
+ current:=SearchContact(CLGN_PREVIOUS);
+ end;
+ VK_NEXT,VK_DOWN: begin
+ current:=SearchContact(CLGN_NEXT);
+ end;
+ end;
+ end;
+ end;
+ result:=CallWindowProc(OldEditProc,Dialog,hMessage,wParam,lParam);
+end;
+
+function QSDlgResizer(Dialog:HWND;lParam:LPARAM;urc:PUTILRESIZECONTROL):int; cdecl;
+begin
+ case urc^.wId of
+ IDC_FRAME_OPEN: result:=RD_ANCHORX_LEFT or RD_ANCHORY_CENTRE;
+ IDC_FRAME_EDIT: result:=RD_ANCHORX_WIDTH or RD_ANCHORY_CENTRE;
+ IDC_FRAME_PREV: result:=RD_ANCHORX_RIGHT or RD_ANCHORY_CENTRE;
+ IDC_FRAME_NEXT: result:=RD_ANCHORX_RIGHT or RD_ANCHORY_CENTRE;
+ else
+ result:=0;
+ end;
+end;
+
+function QSFrameProc(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+var
+ urd:TUTILRESIZEDIALOG;
+ tmp:THANDLE;
+ rc:TRECT;
+ ti:TTOOLINFOW;
+ hwndTooltip:HWND;
+begin
+ result:=0;
+ case hMessage of
+ WM_DESTROY: begin
+ DeleteObject(hbr);
+ end;
+
+ WM_INITDIALOG: begin
+ OldEditProc:=pointer(SetWindowLongPtrW(GetDlgItem(Dialog,IDC_FRAME_EDIT),
+ GWL_WNDPROC,LONG_PTR(@NewEditProc)));
+
+ hwndTooltip:=CreateWindowW(TOOLTIPS_CLASS,nil,TTS_ALWAYSTIP,
+ integer(CW_USEDEFAULT),integer(CW_USEDEFAULT),
+ integer(CW_USEDEFAULT),integer(CW_USEDEFAULT),
+ Dialog,0,hInstance,nil);
+
+ FillChar(ti,SizeOf(ti),0);
+ ti.cbSize :=sizeof(TOOLINFO);
+ ti.uFlags :=TTF_IDISHWND or TTF_SUBCLASS;
+ ti.hwnd :=Dialog;
+ ti.hinst :=hInstance;
+ ti.uId :=GetDlgItem(Dialog,IDC_FRAME_PREV);
+
+ ti.lpszText:=pWideChar(TranslateW('Previous item'));
+ SendMessage(hwndTooltip,TTM_ADDTOOLW,0,tlparam(@ti));
+ ti.uId :=GetDlgItem(Dialog,IDC_FRAME_NEXT);
+ ti.lpszText:=pWideChar(TranslateW('Next item'));
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,tlparam(@ti));
+ ti.uId :=GetDlgItem(Dialog,IDC_FRAME_OPEN);
+ ti.lpszText:=pWideChar(TranslateW('Open main window'));
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,tlparam(@ti));
+ end;
+
+ WM_SIZE: begin
+ FillChar(urd,SizeOf(TUTILRESIZEDIALOG),0);
+ urd.cbSize :=SizeOf(urd);
+ urd.hwndDlg :=Dialog;
+ urd.hInstance :=hInstance;
+ urd.lpTemplate:=MAKEINTRESOURCEA(IDD_FRAME);
+ urd.lParam :=0;
+ urd.pfnResizer:=@QSDlgResizer;
+ CallService(MS_UTILS_RESIZEDIALOG,0,tlparam(@urd));
+ end;
+
+ WM_ERASEBKGND: begin
+ GetClientRect(Dialog,rc);
+ FillRect(wParam,rc,hbr);
+ result:=1;
+ end;
+
+ WM_COMMAND: begin
+ case wParam shr 16 of
+ EN_CHANGE: begin
+ mFreeMem(pattern);
+ pattern:=GetDlgText(Dialog,IDC_FRAME_EDIT);
+ if pattern<>nil then
+ CharLowerW(pattern);
+
+ tmp:=SearchContact(CLGN_NEXT,0);
+ if tmp=0 then
+ tmp:=SearchContact(CLGN_PREVIOUS,0);
+ current:=tmp;
+ end;
+
+ BN_CLICKED: begin
+ tmp:=0;
+ case loword(wParam) of
+ IDC_FRAME_PREV: tmp:=SearchContact(CLGN_PREVIOUS);
+ IDC_FRAME_NEXT: tmp:=SearchContact(CLGN_NEXT);
+ IDC_FRAME_OPEN: CallService(QS_SHOWSERVICE,twparam(pattern),0);
+ end;
+ if tmp<>0 then current:=tmp;
+ end;
+ end;
+ end;
+
+ else
+ result:=DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+end;
+
+function ColorReload(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ cid:TColourID;
+begin
+ result:=0;
+ cid.cbSize:=SizeOf(cid);
+ StrCopy(cid.group,'QuickSearch');
+ StrCopy(cid.name ,frm_back);
+ frm_bkg:=CallService(MS_COLOUR_GETA,twparam(@cid),0);
+ DeleteObject(hbr);
+ hbr:=CreateSolidBrush(frm_bkg);
+
+ RedrawWindow(FrameWnd,nil,0,RDW_ERASE);
+end;
+
+procedure CreateFrame(parent:HWND);
+var
+ Frame:TCLISTFrame;
+ wnd:HWND;
+ tmp:cardinal;
+ tr:TRECT;
+ cid:TColourID;
+begin
+ if ServiceExists(MS_CLIST_FRAMES_ADDFRAME)=0 then
+ exit;
+ if parent=0 then
+ parent:=CallService(MS_CLUI_GETHWND,0,0);
+
+ if FrameWnd=0 then
+ FrameWnd:=CreateDialog(hInstance,MAKEINTRESOURCE(IDD_FRAME),parent,@QSFrameProc);
+
+ if FrameWnd<>0 then
+ begin
+ GetWindowRect(FrameWnd,tr);
+ FillChar(Frame,SizeOf(Frame),0);
+ with Frame do
+ begin
+ cbSize :=SizeOf(Frame);
+ hWnd :=FrameWnd;
+ hIcon :=0;
+ align :=alTop;
+ height :=tr.bottom-tr.top+2;
+ Flags :=F_VISIBLE or F_NOBORDER or F_UNICODE;
+ name.w :='Quick search';
+ TBName.w:='Quick search';
+ end;
+
+ FrameId:=CallService(MS_CLIST_FRAMES_ADDFRAME,wparam(@Frame),0);
+ if FrameId>=0 then
+ begin
+ CallService(MS_CLIST_FRAMES_UPDATEFRAME,FrameId, FU_FMPOS);
+
+ wnd:=CallService(MS_CLUI_GETHWND{MS_CLUI_GETHWNDTREE},0,0);
+ tmp:=SendMessage(wnd,CLM_GETEXSTYLE,0,0);
+ SendMessage(wnd,CLM_SETEXSTYLE,tmp or CLS_EX_SHOWSELALWAYS,0);
+
+ cid.cbSize:=SizeOf(cid);
+ cid.flags :=0;
+ StrCopy(cid.group,'QuickSearch');
+ StrCopy(cid.dbSettingsGroup,qs_module);
+
+ StrCopy(cid.name ,frm_back);
+ StrCopy(cid.setting,'frame_back');
+ cid.defcolour:=COLOR_3DFACE;
+ cid.order :=0;
+ ColourRegister(@cid);
+
+ colorhook:=HookEvent(ME_COLOUR_RELOAD,@ColorReload);
+ ColorReload(0,0);
+ end;
+ end;
+end;
+
+procedure DestroyFrame;
+begin
+ if FrameId>=0 then
+ begin
+ UnhookEvent(colorhook);
+ CallService(MS_CLIST_FRAMES_REMOVEFRAME,FrameId,0);
+ FrameId:=-1;
+ end;
+ DestroyWindow(FrameWnd);
+ FrameWnd:=0;
+ mFreeMem(pattern);
+end;
+
+end.
diff --git a/plugins/QuickSearch/sr_global.pas b/plugins/QuickSearch/sr_global.pas
new file mode 100644
index 0000000000..cd9e1d826e
--- /dev/null
+++ b/plugins/QuickSearch/sr_global.pas
@@ -0,0 +1,750 @@
+unit sr_global;
+
+interface
+uses windows,messages,commctrl,m_api,dbsettings,mirutils;
+
+const
+ // for QS window only
+ IDM_STAYONTOP = WM_USER+1;
+ WM_MYADDCONTACT = WM_USER+2;
+ WM_MYDELETECONTACT = WM_USER+3;
+
+ WM_MYSHOWHIDEITEM = WM_USER + 4;
+ WM_MYMOVEITEM = WM_USER + 5;
+const
+ wcUp = 1;
+ wcDown = 2;
+ wcHide = 3;
+ wcShow = 4;
+ wcDelete = 5;
+ wcInsert = 6;
+ wcChange = 7;
+ wcRefresh = 8;
+
+const
+ opened:boolean = false;
+const
+ QS_QS :PAnsiChar = 'QS_QS';
+ QS_NEW :PAnsiChar = 'QS_New';
+ QS_ITEM :PAnsiChar = 'QS_Item';
+ QS_UP :PAnsiChar = 'QS_Up';
+ QS_DOWN :PAnsiChar = 'QS_Down';
+ QS_DELETE :PAnsiChar = 'QS_Delete';
+ QS_DEFAULT:PAnsiChar = 'QS_Default';
+ QS_RELOAD :PAnsiChar = 'QS_Reload';
+ QS_MALE :PAnsiChar = 'QS_Male';
+ QS_FEMALE :PAnsiChar = 'QS_Female';
+const
+ qs_module :PAnsiChar = 'QuickSearch';
+ qs_name :PAnsiChar = 'Quick Search';
+ QS_SHOWSERVICE:PAnsiChar = 'QuickSearch_PLUGIN/Show';
+
+const
+ StatusSort = 1000;
+
+const
+ ptNumber = 0;
+ ptInteger = 1;
+ ptString = 2;
+ ptUnicode = 3;
+ ptCurrent = 4;
+
+const //types
+ ST_BYTE = 0;
+ ST_WORD = 1;
+ ST_INT = 2;
+ ST_STRING = 3;
+ ST_IP = 4;
+ ST_LASTSEEN = 5;
+ ST_CONTACTINFO = 6;
+ ST_LASTEVENT = 7;
+ ST_TIMESTAMP = 8;
+ ST_SERVICE = 9;
+ ST_SCRIPT = 10;
+ ST_METACONTACT = 11;
+
+ ST_MAXTYPE = 11;
+
+const
+ COL_ON = $0001; // Show column
+ COL_INIT = $0002; // No need to update
+ COL_XSTATUS = $0100;
+ COL_GENDER = $0200;
+ COL_CLIENT = $0400;
+
+{$include resource.inc}
+
+type
+ tserviceparam = record
+ case _type:word of
+ 0: (n:dword);
+ 1: (i:integer);
+ 2: (a:PAnsiChar);
+ 3: (w:PWideChar);
+ end;
+type
+ tcolumnitem=record
+ title :PWideChar;
+ module_name :PAnsiChar;
+ width :dword;
+ setting_type :dword; // ST_* constants
+ setting_cnftype:dword; // pt* constants
+ wparam :tserviceparam;
+ lparam :tserviceparam;
+ flags :dword; // COL_* constants
+ end;
+ tcolumnarray = array of tcolumnitem;
+ tqsopt=record
+ grrect :TRECT;
+ columns :tcolumnarray;//array of tcolumnitem;
+ numcolumns :integer;
+ columnsort :integer;
+ ascendsort :boolean;
+ sortbystatus :boolean;
+ drawgrid :boolean;
+ showinmenu :boolean;
+ showonlyinlist :boolean;
+ showintoptoolbar:boolean;
+ usetoolstyle :boolean;
+ closeafteraction:boolean;
+ stayontop :boolean;
+ showoffline :boolean;
+ showclienticons :boolean;
+ exportheaders :boolean;
+ singlecsv :boolean;
+ skipminimized :boolean;
+ savepattern :boolean;
+ colorize :boolean;
+ end;
+
+procedure reghotkeys;
+procedure unreghotkeys;
+
+procedure saveopt_wnd;
+procedure saveopt_db;
+procedure loadopt_db(full:boolean);
+procedure clear_columns;
+function new_column(after:integer=-1):integer;
+procedure delete_column(pos:integer);
+procedure loaddefaultcolumns;
+
+procedure AddRemoveMenuItemToMainMenu;
+procedure addtotoolbar;
+procedure removetoolbar;
+
+var
+ qsopt:tqsopt;
+
+const
+ MainMenuItem:integer=0;
+ hTTBButton :thandle=0;
+
+implementation
+
+uses common;
+
+const
+ HKN_GLOBAL:PAnsiChar = 'QS_Global';
+const
+ so_mbottom :PAnsiChar = 'mbottom';
+ so_mright :PAnsiChar = 'mright';
+ so_mtop :PAnsiChar = 'mtop';
+ so_mleft :PAnsiChar = 'mleft';
+ so_columnsort :PAnsiChar = 'columnsort';
+ so_sortbystatus :PAnsiChar = 'sortbystatus';
+ so_ascendsort :PAnsiChar = 'ascendsort';
+ so_showonlyinlist :PAnsiChar = 'showonlyinlist';
+ so_dontusetoolstyle:PAnsiChar = 'dontusetoolstyle';
+ so_showinmenu :PAnsiChar = 'showinmenu';
+ so_showintoptoolbar:PAnsiChar = 'showintoptoolbar';
+ so_closeafteraction:PAnsiChar = 'closeafteraction';
+ so_exportheaders :PAnsiChar = 'exportheaders';
+ so_singlecsv :PAnsiChar = 'singlecsv';
+ so_savepattern :PAnsiChar = 'savepattern';
+ so_numcolumns :PAnsiChar = 'numcolumns';
+ so_item :PAnsiChar = 'item';
+ so_drawgrid :PAnsiChar = 'drawgrid';
+ so_stayontop :PAnsiChar = 'stayontop';
+ so_showclienticons :PAnsiChar = 'showclienticons';
+ so_skipminimized :PAnsiChar = 'skipminimized';
+ so_showoffline :PAnsiChar = 'showoffline';
+ so_colorize :PAnsiChar = 'colorize';
+
+ so__title :PAnsiChar = '_title';
+ so__setting_type :PAnsiChar = '_setting_type';
+ so__setting_cnftype:PAnsiChar = '_setting_cnftype';
+ so__module_name :PAnsiChar = '_module_name';
+ so__wparam_type :PAnsiChar = '_wparam_type';
+ so__lparam_type :PAnsiChar = '_lparam_type';
+ so__wparam :PAnsiChar = '_wparam';
+ so__lparam :PAnsiChar = '_lparam';
+ so__width :PAnsiChar = '_width';
+ so__flags :PAnsiChar = '_flags';
+
+procedure reghotkeys;
+var
+ hkrec:HOTKEYDESC;
+begin
+ FillChar(hkrec,SizeOf(hkrec),0);
+ with hkrec do
+ begin
+ cbSize :=HOTKEYDESC_SIZE_V1;
+ pszName :=HKN_GLOBAL;
+ pszDescription.a:='QuickSearch window hotkey';
+ pszSection.a :=qs_name;
+ pszService :=QS_SHOWSERVICE;
+ DefHotKey :=(HOTKEYF_ALT shl 8) or VK_F3;
+ end;
+ CallService(MS_HOTKEY_REGISTER,0,lparam(@hkrec));
+end;
+
+procedure unreghotkeys;
+begin
+ CallService(MS_HOTKEY_UNREGISTER,0,lparam(HKN_GLOBAL));
+end;
+
+procedure removetoolbar;
+begin
+ if hTTBButton<>0 then
+ begin
+ if ServiceExists(MS_TTB_REMOVEBUTTON)>0 then
+ begin
+ CallService(MS_TTB_REMOVEBUTTON,WPARAM(hTTBButton),0);
+ hTTBButton:=0;
+ end;
+ end;
+end;
+
+procedure addtotoolbar;
+var
+ ttbopt:TTBButton;
+begin
+ removetoolbar;
+
+ if qsopt.showintoptoolbar then
+ begin
+ if ServiceExists(MS_TTB_ADDBUTTON)>0 then
+ begin
+ ZeroMemory(@ttbopt,sizeof(ttbopt));
+ ttbopt.cbSize :=sizeof(ttbopt);
+ ttbopt.pszService:=QS_SHOWSERVICE;
+ ttbopt.hIconUp :=CallService(MS_SKIN2_GETICON,0,lparam(QS_QS));
+ ttbopt.hIconDn :=ttbopt.hIconUp;
+ ttbopt.dwFlags :=TTBBF_VISIBLE;
+ ttbopt.name :=qs_module;
+ hTTBButton:=TopToolbar_AddButton(@ttbopt);
+ if hTTBButton=THANDLE(-1) then
+ hTTBButton:=0;
+ end;
+ end;
+end;
+
+procedure AddRemoveMenuItemToMainMenu;
+var
+ cmi:TCLISTMENUITEM;
+begin
+ if qsopt.showinmenu then
+ begin
+ if MainMenuItem<>0 then exit;
+ ZeroMemory(@cmi,sizeof(cmi));
+ cmi.cbSize :=sizeof(cmi) ;
+ cmi.szName.a :=qs_name;
+ cmi.position :=500050000;
+// cmi.pszPopupName:=nil;
+// cmi.flags :=0;
+ cmi.pszService :=QS_SHOWSERVICE;
+ cmi.hIcon :=CallService(MS_SKIN2_GETICON,0,lparam(QS_QS));
+ MainMenuItem :=Menu_AddMainMenuItem(@cmi);
+ end
+ else
+ begin
+ if (MainMenuItem<>0) and
+ (ServiceExists(MS_CLIST_REMOVEMAINMENUITEM)<>0) then
+ begin
+ CallService(MS_CLIST_REMOVEMAINMENUITEM,MainMenuItem,0);
+ MainMenuItem:=0;
+ end;
+ end;
+end;
+
+// -------- column functions ---------
+
+procedure clear_column(num:integer);
+begin
+ with qsopt.columns[num] do
+ begin
+ mFreeMem(title);
+ if setting_type<>ST_CONTACTINFO then
+ begin
+ mFreeMem(module_name);
+ if setting_type<>ST_SERVICE then
+ mFreeMem(wparam.a)
+ else
+ begin
+ if (wparam._type=ptString) or (wparam._type=ptUnicode) then mFreeMem(wparam.a);
+ if (lparam._type=ptString) or (lparam._type=ptUnicode) then mFreeMem(lparam.a);
+ end;
+ end;
+ end;
+end;
+
+procedure clear_columns;
+var
+ i:integer;
+begin
+ for i:=0 to qsopt.numcolumns-1 do
+ clear_column(i);
+ FillChar(qsopt.columns[0],Length(qsopt.columns),0);
+ qsopt.numcolumns:=0;
+end;
+
+procedure delete_column(pos:integer);
+begin
+ if (pos>=0) and (qsopt.numcolumns>0) then
+ begin
+ dec(qsopt.numcolumns);
+ clear_column(pos);
+ move(qsopt.columns[pos+1],qsopt.columns[pos],(qsopt.numcolumns-pos)*sizeof(tcolumnitem));
+ SetLength(qsopt.columns,qsopt.numcolumns);
+ end;
+end;
+
+function new_column(after:integer=-1):integer;
+begin
+ SetLength(qsopt.columns,qsopt.numcolumns+1);
+ FillChar(qsopt.columns[qsopt.numcolumns],SizeOf(tcolumnitem),0);
+ with qsopt.columns[qsopt.numcolumns] do
+ begin
+ StrDupW(title,'New column');
+ width:=64;
+ flags:=COL_ON;
+ end;
+ result:=qsopt.numcolumns;
+ inc(qsopt.numcolumns);
+end;
+
+procedure MakeTitle(var title; name:pAnsiChar);
+begin
+ FastAnsiToWide(name,pWideChar(title));
+end;
+
+procedure loaddefaultcolumns;
+begin
+ clear_columns;
+ qsopt.numcolumns:=15;
+ SetLength(qsopt.columns ,qsopt.numcolumns);
+ FillChar(qsopt.columns[0],qsopt.numcolumns*SizeOf(tcolumnitem),0);
+
+ // protocol
+ with qsopt.columns[0] do
+ begin
+ MakeTitle(title,'Protocol');
+ StrDup (module_name,MS_PROTO_GETCONTACTBASEPROTO);
+ width :=82;
+ setting_type :=ST_SERVICE;
+ setting_cnftype:=ptString;
+ wparam._type :=ptCurrent;
+ lparam._type :=ptNumber;
+ lparam.n :=0;
+ flags :=COL_ON;
+ end;
+
+ with qsopt.columns[1] do
+ begin
+ MakeTitle(title,'Real Protocol');
+ StrDup(module_name,'Protocol');
+ StrDup(wparam.a ,'p');
+ width :=82;
+ setting_type :=ST_STRING;
+ flags :=0;
+ end;
+
+ //gender
+ with qsopt.columns[2] do
+ begin
+ MakeTitle(title,'Gender');
+ width :=20;
+ setting_type :=ST_CONTACTINFO;
+ setting_cnftype:=CNF_GENDER;
+ flags :=COL_ON;
+ end;
+
+ //uin
+ with qsopt.columns[3] do
+ begin
+ MakeTitle(title,'UserID');
+ width :=80;
+ setting_type :=ST_CONTACTINFO;
+ setting_cnftype:=CNF_UNIQUEID;
+ flags :=COL_ON;
+ end;
+
+ //username(displayname)
+ with qsopt.columns[4] do
+ begin
+ MakeTitle(title,'Nickname');
+ StrDup(module_name,MS_CLIST_GETCONTACTDISPLAYNAME);
+ width :=76;
+ setting_type :=ST_SERVICE;
+ setting_cnftype:=ptUnicode;
+ wparam._type :=ptCurrent;
+ lparam._type :=ptNumber;
+ lparam.n :=2; // 0 for ANSI
+ flags :=COL_ON;
+ end;
+
+ //firstname
+ with qsopt.columns[5] do
+ begin
+ MakeTitle(title,'First name');
+ width :=68;
+ setting_type :=ST_CONTACTINFO;
+ setting_cnftype:=CNF_FIRSTNAME;
+ flags :=COL_ON;
+ end;
+
+ //lastname
+ with qsopt.columns[6] do
+ begin
+ MakeTitle(title,'Last name');
+ width :=66;
+ setting_type :=ST_CONTACTINFO;
+ setting_cnftype:=CNF_LASTNAME;
+ flags :=COL_ON;
+ end;
+
+ //group
+ with qsopt.columns[7] do
+ begin
+ MakeTitle(title,'Group');
+ width :=80;
+ StrDup(module_name,'CList');
+ StrDup(wparam.a ,'Group');
+ setting_type :=ST_STRING;
+ flags :=COL_ON;
+ end;
+
+ //email
+ with qsopt.columns[8] do
+ begin
+ MakeTitle(title,'E-mail');
+ width :=116;
+ setting_type :=ST_CONTACTINFO;
+ setting_cnftype:=CNF_EMAIL;
+ flags :=COL_ON;
+ end;
+
+ //miranda version
+ with qsopt.columns[9] do
+ begin
+ MakeTitle(title,'ClientID');
+ StrDup(wparam.a,'MirVer');
+ width :=60;
+ setting_type:=ST_STRING;
+ flags :=COL_ON;
+ end;
+
+ //IP version
+ with qsopt.columns[10] do
+ begin
+ MakeTitle(title,'Ext IP');
+ StrDup(module_name,'ICQ');
+ StrDup(wparam.a ,'IP');
+ width :=100;
+ setting_type:=ST_IP;
+ flags :=0;
+ end;
+
+ //LastSeen
+ with qsopt.columns[11] do
+ begin
+ MakeTitle(title,'LastSeen');
+ StrDup(module_name,'SeenModule');
+ width :=116;
+ setting_type:=ST_LASTSEEN;
+ flags :=0;
+ end;
+
+ //last event
+ with qsopt.columns[12] do
+ begin
+ MakeTitle(title,'Last Event');
+ width :=100;
+ setting_type:=ST_LASTEVENT;
+ flags :=0;
+ end;
+
+ //online since
+ with qsopt.columns[13] do
+ begin
+ MakeTitle(title,'Online since');
+ StrDup(module_name,'ICQ');
+ StrDup(wparam.a ,'LogonTS');
+ width :=100;
+ setting_type:=ST_TIMESTAMP;
+ flags :=0;
+ end;
+
+ //metacontacts
+ with qsopt.columns[14] do
+ begin
+ MakeTitle(title,'Metacontact');
+ width :=50;
+ setting_type:=ST_METACONTACT;
+ flags :=0;
+ end;
+end;
+
+// -------- save/load settings ---------
+
+procedure WriteInt(setting:PAnsiChar;value:int);
+begin
+ DBWriteDword(0,qs_module,setting,value)
+end;
+procedure WriteWord(setting:PAnsiChar;value:word);
+begin
+ DBWriteWord(0,qs_module,setting,value)
+end;
+procedure WriteStr(setting:PAnsiChar;value:PAnsiChar);
+begin
+ DBWriteString(0,qs_module,setting,value)
+end;
+procedure WriteUnicode(setting:PAnsiChar;value:PWideChar);
+begin
+ DBWriteUnicode(0,qs_module,setting,value)
+end;
+procedure WriteBool(setting:PAnsiChar;value:bool);
+begin
+ DBWriteByte(0,qs_module,setting,ord(value))
+end;
+
+procedure saveopt_wnd;
+var
+ i:integer;
+ buf:array [0..127] of AnsiChar;
+ p,pp:PAnsiChar;
+begin
+ WriteInt (so_mbottom ,qsopt.grrect.bottom);
+ WriteInt (so_mright ,qsopt.grrect.right);
+ WriteInt (so_mtop ,qsopt.grrect.top);
+ WriteInt (so_mleft ,qsopt.grrect.left);
+
+ WriteBool(so_showoffline,qsopt.showoffline);
+ WriteBool(so_colorize ,qsopt.colorize);
+
+ WriteInt (so_columnsort ,qsopt.columnsort);
+
+ pp:=StrCopyE(buf,so_item);
+ for i:=0 to qsopt.numcolumns-1 do
+ begin
+ p:=StrEnd(IntToStr(pp,i));
+ with qsopt.columns[i] do
+ begin
+ StrCopy(p,so__flags); WriteInt (buf,flags);
+ StrCopy(p,so__width); WriteWord(buf,width);
+ end;
+ end;
+end;
+
+{
+ "fast" writing"
+ order array - if column order only changed
+ column flags - if checkboxes changed only
+}
+procedure saveopt_db;
+var
+ i:integer;
+ buf:array [0..127] of AnsiChar;
+ p,pp:PAnsiChar;
+begin
+ WriteWord(so_numcolumns ,qsopt.numcolumns);
+
+ WriteBool(so_sortbystatus ,qsopt.sortbystatus);
+ WriteBool(so_showinmenu ,qsopt.showinmenu);
+// WriteInt (so_columnsort ,qsopt.columnsort);
+ WriteBool(so_ascendsort ,qsopt.ascendsort);
+ WriteBool(so_showonlyinlist ,qsopt.showonlyinlist);
+
+ WriteBool(so_showintoptoolbar,qsopt.showintoptoolbar);
+ WriteBool(so_dontusetoolstyle,not qsopt.usetoolstyle);
+ WriteBool(so_closeafteraction,qsopt.closeafteraction);
+ WriteBool(so_drawgrid ,qsopt.drawgrid);
+ WriteBool(so_stayontop ,qsopt.stayontop);
+ WriteBool(so_showclienticons ,qsopt.showclienticons);
+ WriteBool(so_exportheaders ,qsopt.exportheaders);
+ WriteBool(so_singlecsv ,qsopt.singlecsv);
+ WriteBool(so_skipminimized ,qsopt.skipminimized);
+ WriteBool(so_savepattern ,qsopt.savepattern);
+
+ pp:=StrCopyE(buf,so_item);
+ for i:=0 to qsopt.numcolumns-1 do
+ begin
+ p:=StrEnd(IntToStr(pp,i));
+ with qsopt.columns[i] do
+ begin
+ StrCopy(p,so__title);
+ WriteUnicode(buf,title);
+ case setting_type of
+ ST_SCRIPT: begin
+ StrCopy(p,so__wparam);
+ WriteUnicode(buf,wparam.w);
+ end;
+ ST_CONTACTINFO: begin
+ StrCopy(p,so__setting_cnftype); WriteWord(buf,setting_cnftype);
+ end;
+ ST_SERVICE: begin
+ StrCopy(p,so__module_name ); WriteStr (buf,module_name);
+ StrCopy(p,so__setting_cnftype); WriteWord(buf,setting_cnftype);
+ StrCopy(p,so__wparam_type ); WriteWord(buf,wparam._type);
+ StrCopy(p,so__lparam_type ); WriteWord(buf,lparam._type);
+ StrCopy(p,so__wparam);
+ case wparam._type of
+ ptNumber,
+ ptInteger: WriteInt (buf,wparam.n);
+ ptString : WriteStr (buf,wparam.a);
+ ptUnicode: WriteUnicode(buf,wparam.w);
+ end;
+ StrCopy(p,so__lparam);
+ case lparam._type of
+ ptNumber,
+ ptInteger: WriteInt (buf,lparam.n);
+ ptString : WriteStr (buf,lparam.a);
+ ptUnicode: WriteUnicode(buf,lparam.w);
+ end;
+ end;
+ else
+ StrCopy(p,so__module_name); WriteStr(buf,module_name);
+ StrCopy(p,so__wparam ); WriteStr(buf,wparam.a);
+ end;
+ StrCopy(p,so__setting_type); WriteWord(buf,setting_type);
+ StrCopy(p,so__flags ); WriteInt (buf,flags);
+ StrCopy(p,so__width ); WriteWord(buf,width);
+ end;
+ end;
+end;
+
+function GetInt(setting:PAnsiChar;default:integer):integer;
+begin
+ result:=DBReadDWord(0,qs_module,setting,default);
+end;
+function GetWord(setting:PAnsiChar;default:word):word;
+begin
+ result:=DBReadWord(0,qs_module,setting,default);
+end;
+function GetBool(setting:PAnsiChar;default:bool):bool;
+begin
+ result:=bool(DBReadByte(0,qs_module,setting,integer(default)));
+end;
+function GetStr(setting:PAnsiChar):PAnsiChar;
+begin
+ result:=DBReadString(0,qs_module,setting,nil);
+end;
+function GetUnicode(setting:PAnsiChar):PWideChar;
+begin
+ result:=DBReadUnicode(0,qs_module,setting,nil);
+end;
+
+{
+ if "fast"writing enabled
+ fill columns accordingly comumn order array
+}
+procedure loadopt_db(full:boolean);
+var
+ i:integer;
+ buf:array [0..127] of AnsiChar;
+ p,pp:PAnsiChar;
+begin
+ if full then
+ begin
+ zeromemory(@qsopt,sizeof(qsopt));
+
+ qsopt.grrect.bottom:=GetInt(so_mbottom,240);
+ qsopt.grrect.right :=GetInt(so_mright,550);
+ qsopt.grrect.top :=GetInt(so_mtop,0);
+ qsopt.grrect.left :=GetInt(so_mleft,0);
+
+ qsopt.columnsort :=GetInt (so_columnsort,StatusSort);
+ qsopt.sortbystatus :=GetBool(so_sortbystatus,true);
+ qsopt.ascendsort :=GetBool(so_ascendsort ,true);
+
+ qsopt.showonlyinlist :=GetBool(so_showonlyinlist ,false);
+ qsopt.usetoolstyle :=not GetBool(so_dontusetoolstyle,false);
+ qsopt.showinmenu :=GetBool(so_showinmenu ,true);
+ qsopt.showintoptoolbar:=GetBool(so_showintoptoolbar,true);
+ qsopt.closeafteraction:=GetBool(so_closeafteraction,false);
+ qsopt.drawgrid :=GetBool(so_drawgrid ,true);
+ qsopt.stayontop :=GetBool(so_stayontop ,false);
+ qsopt.singlecsv :=GetBool(so_singlecsv ,false);
+ qsopt.exportheaders :=GetBool(so_exportheaders ,false);
+ qsopt.showoffline :=GetBool(so_showoffline ,true);
+ qsopt.skipminimized :=GetBool(so_skipminimized ,true);
+ qsopt.savepattern :=GetBool(so_savepattern ,true);
+ qsopt.colorize :=GetBool(so_colorize ,true);
+
+ if ServiceExists(MS_FP_GETCLIENTICON)<>0 then
+ qsopt.showclienticons:=GetBool(so_showclienticons,true)
+ else
+ qsopt.showclienticons:=false;
+ end
+ else
+ clear_columns;
+
+ qsopt.numcolumns:=GetWord(so_numcolumns,0);
+ if qsopt.numcolumns=0 then
+ begin
+ loaddefaultcolumns;
+ saveopt_db;
+ end
+ else
+ begin
+ pp:=StrCopyE(buf,so_item);
+ SetLength(qsopt.columns,qsopt.numcolumns);
+ FillChar(qsopt.columns[0],SizeOf(tcolumnitem)*qsopt.numcolumns,0);
+ for i:=0 to qsopt.numcolumns-1 do
+ begin
+ p:=StrEnd(IntToStr(pp,i));
+ with qsopt.columns[i] do
+ begin
+ StrCopy(p,so__title);
+ title:=GetUnicode(buf);
+ StrCopy(p,so__setting_type); setting_type:=GetWord(buf,0);
+ case setting_type of
+ ST_SCRIPT: begin
+ StrCopy(p,so__wparam);
+ wparam.w:=GetUnicode(buf);
+ end;
+ ST_CONTACTINFO: begin
+ StrCopy(p,so__setting_cnftype); setting_cnftype:=GetWord(buf,0);
+ end;
+ ST_SERVICE: begin
+ StrCopy(p,so__setting_cnftype); setting_cnftype:=GetWord(buf,0);
+ StrCopy(p,so__module_name); module_name :=GetStr(buf);
+ StrCopy(p,so__wparam_type); wparam._type:=GetWord(buf,0);
+ StrCopy(p,so__lparam_type); lparam._type:=GetWord(buf,0);
+ StrCopy(p,so__wparam);
+ case wparam._type of
+ ptNumber,
+ ptInteger: wparam.n:=GetInt(buf,0);
+ ptString : wparam.a:=GetStr(buf);
+ ptUnicode: wparam.w:=GetUnicode(buf);
+ end;
+ StrCopy(p,so__lparam);
+ case lparam._type of
+ ptNumber,
+ ptInteger: lparam.n:=GetInt(buf,0);
+ ptString : lparam.a:=GetStr(buf);
+ ptUnicode: lparam.w:=GetUnicode(buf);
+ end;
+ end;
+ else
+ StrCopy(p,so__module_name); module_name:=GetStr(buf);
+ StrCopy(p,so__wparam ); wparam.a :=GetStr(buf);
+ end;
+ StrCopy(p,so__width); width:=GetWord(buf,20);
+ StrCopy(p,so__flags); flags:=GetInt (buf,COL_ON);
+ end;
+ end;
+ end;
+end;
+
+end.
diff --git a/plugins/QuickSearch/sr_optdialog.pas b/plugins/QuickSearch/sr_optdialog.pas
new file mode 100644
index 0000000000..7e7e711532
--- /dev/null
+++ b/plugins/QuickSearch/sr_optdialog.pas
@@ -0,0 +1,1041 @@
+unit sr_optdialog;
+
+interface
+uses windows;
+
+{.$include resource.inc}
+
+procedure OptChangeColumns(code:integer;column,data:integer);
+function DlgProcOptions(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+
+const
+ maindlg:HWND = 0;
+
+implementation
+
+uses messages,commctrl,sr_global,m_api,common,mirutils,wrapper,sr_window;
+
+var
+ OldListProc:pointer;
+const
+ curscript:pointer=nil;
+
+const
+ stByte :PAnsiChar = 'Byte';
+ stWord :PAnsiChar = 'Word';
+ stInt :PAnsiChar = 'Int';
+ stString :PAnsiChar = 'String';
+ stLastSeen :PAnsiChar = 'LastSeen';
+ stIP :PAnsiChar = 'IP';
+ stContactInfo:PAnsiChar = 'ContactInfo';
+ stLastEvent :PAnsiChar = 'LastEvent';
+ stTimeStamp :PAnsiChar = 'TimeStamp';
+ stService :PAnsiChar = 'Service';
+ stScript :PAnsiChar = 'Script';
+ stMetacontact:PAnsiChar = 'Metacontact';
+
+const
+ cnFirstName = 'FIRSTNAME' ;
+ cnLastName = 'LASTNAME' ;
+ cnNick = 'NICK' ;
+ cnCustomNick = 'CUSTOMNICK';
+ cnEmail = 'EMAIL' ;
+ cnCity = 'CITY' ;
+ cnState = 'STATE' ;
+ cnCountry = 'COUNTRY' ;
+ cnPhone = 'PHONE' ;
+ cnHomepage = 'HOMEPAGE' ;
+ cnAbout = 'ABOUT' ;
+ cnGender = 'GENDER' ;
+ cnAge = 'AGE' ;
+ cnFirstLast = 'FIRSTLAST' ;
+ cnUniqueID = 'UNIQUEID' ;
+ cnFax = 'FAX' ;
+ cnCellular = 'CELLULAR' ;
+ cnTimezone = 'TIMEZONE' ;
+ cnMyNotes = 'MYNOTES' ;
+ cnBirthday = 'BIRTHDAY' ;
+ cnBirthMonth = 'BIRTHMONTH';
+ cnBirthYear = 'BIRTHYEAR' ;
+ cnZIP = 'ZIP' ;
+ cnStreet = 'STREET' ;
+ cnLanguage1 = 'LANGUAGE1' ;
+ cnLanguage2 = 'LANGUAGE2' ;
+ cnLanguage3 = 'LANGUAGE3' ;
+ cnCoName = 'CONAME' ;
+
+const
+ strNotSelected = 'Not Selected';
+
+const
+ MaxControls = 13;
+ aIdElement:array [0..MaxControls-1] of integer = (
+ IDC_SCRIPT, IDC_STAT_VARTYPE, IDC_C_CNFTYPE, IDC_C_RESULT,
+ IDC_STAT_SERVICE, IDC_STAT_MODULE, IDC_E_MODULE,
+ IDC_STAT_WPAR, IDC_STAT_SETTING, IDC_E_VAR,
+ IDC_C_WPAR, IDC_E_LPAR, IDC_C_LPAR);
+
+ aShowElement:array [0..ST_MAXTYPE,0..MaxControls-1] of byte = (
+{ST_BYTE } ($00, $80, $80, $00, $00, $81, $81, $00, $81, $81, $80, $80, $80),
+{ST_WORD } ($00, $80, $80, $00, $00, $81, $81, $00, $81, $81, $80, $80, $80),
+{ST_INT } ($00, $80, $80, $00, $00, $81, $81, $00, $81, $81, $80, $80, $80),
+{ST_STRING } ($00, $80, $80, $00, $00, $81, $81, $00, $81, $81, $80, $80, $80),
+{ST_IP } ($00, $80, $80, $00, $00, $81, $81, $00, $81, $81, $80, $80, $80),
+{ST_LASTSEEN } ($00, $80, $80, $00, $00, $81, $81, $00, $81, $81, $80, $80, $80),
+{ST_CONTACTINFO} ($00, $81, $81, $00, $00, $80, $80, $00, $80, $80, $80, $80, $80),
+{ST_LASTEVENT } ($00, $80, $80, $00, $00, $80, $80, $00, $80, $80, $80, $80, $80),
+{ST_TIMESTAMP } ($00, $80, $80, $00, $00, $81, $81, $00, $81, $81, $80, $80, $80),
+{ST_SERVICE } ($00, $80, $00, $81, $81, $00, $81, $81, $00, $81, $81, $81, $81),
+{ST_SCRIPT } ($81, $00, $00, $00, $00, $80, $80, $00, $80, $80, $80, $80, $80),
+{ST_METACONTACT} ($00, $80, $80, $00, $00, $80, $80, $00, $80, $80, $80, $80, $80));
+
+procedure SetupControls(Dialog:HWND; aType:integer);
+var
+ i,j: cardinal;
+ wnd:HWND;
+begin
+ for i:=0 to MaxControls-1 do
+ begin
+ j :=aShowElement[aType][i];
+ wnd:=GetDlgItem(Dialog,aIdElement[i]);
+ EnableWindow(wnd,odd(j));
+ if shortint(j)<0 then
+ j:=SW_SHOW
+ else
+ j:=SW_HIDE;
+ ShowWindow(wnd,j);
+ end;
+end;
+
+function settype2str(settype:integer):pointer;
+var
+ tmp:array [0..127] of WideChar;
+begin
+ case settype of
+// ST_BYTE: result:=stByte;
+ ST_WORD: result:=stWord;
+ ST_INT: result:=stInt;
+ ST_STRING: result:=stString;
+ ST_LASTSEEN: result:=stLastSeen;
+ ST_IP: result:=stIP;
+ ST_CONTACTINFO: result:=stContactInfo;
+ ST_LASTEVENT: result:=stLastEvent;
+ ST_TIMESTAMP: result:=stTimeStamp;
+ ST_SERVICE: result:=stService;
+ ST_SCRIPT: result:=stScript;
+ ST_METACONTACT: result:=stMetacontact;
+ else
+ result:=stByte;
+ end;
+
+ FastAnsiToWideBuf(result,tmp);
+ StrDupW(pWideChar(result),TranslateW(tmp));
+end;
+
+function setcnftype2str(settype:integer):PWideChar;
+var
+ res:pWideChar;
+begin
+ case settype of
+// CNF_FIRSTNAME: result:=translate(cnFirstName);
+ CNF_LASTNAME: res:=cnLastName ;
+ CNF_NICK: res:=cnNick ;
+ CNF_CUSTOMNICK: res:=cnCustomNick;
+ CNF_EMAIL: res:=cnEmail ;
+ CNF_CITY: res:=cnCity ;
+ CNF_STATE: res:=cnState ;
+ CNF_COUNTRY: res:=cnCountry ;
+ CNF_PHONE: res:=cnPhone ;
+ CNF_HOMEPAGE: res:=cnHomepage ;
+ CNF_ABOUT: res:=cnAbout ;
+ CNF_GENDER: res:=cnGender ;
+ CNF_AGE: res:=cnAge ;
+ CNF_FIRSTLAST: res:=cnFirstLast ;
+ CNF_UNIQUEID: res:=cnUniqueID ;
+
+ CNF_FAX: res:=cnFax ;
+ CNF_CELLULAR: res:=cnCellular ;
+ CNF_TIMEZONE: res:=cnTimezone ;
+ CNF_MYNOTES: res:=cnMyNotes ;
+ CNF_BIRTHDAY: res:=cnBirthday ;
+ CNF_BIRTHMONTH: res:=cnBirthMonth;
+ CNF_BIRTHYEAR: res:=cnBirthYear ;
+ CNF_STREET: res:=cnStreet ;
+ CNF_ZIP: res:=cnZIP ;
+ CNF_LANGUAGE1: res:=cnLanguage1 ;
+ CNF_LANGUAGE2: res:=cnLanguage2 ;
+ CNF_LANGUAGE3: res:=cnLanguage3 ;
+ CNF_CONAME: res:=cnCoName ;
+ else
+ res:=cnFirstName;
+ end;
+ result:=TranslateW(res);
+end;
+
+procedure addcolumn(handle:hwnd;width:word;title:PAnsiChar);
+var
+ lvcol:LV_COLUMNW;
+ buf:array [0..127] of WideChar;
+begin
+ lvcol.mask:=LVCF_TEXT or LVCF_WIDTH;
+ lvcol.cx :=width;
+ lvcol.pszText:=TranslateW(FastAnsiToWideBuf(title,buf));
+ SendMessageW(handle,LVM_INSERTCOLUMNW,0,lparam(@lvcol));
+end;
+
+function getselecteditem(list:hwnd):integer;
+begin
+ result:=SendMessage(list,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
+end;
+
+function savecuritem(Dialog:hwnd):integer;
+var
+ listhwnd:hwnd;
+ selitem:integer;
+
+ procedure GetText(id,subpos:integer;var dst:PAnsiChar); overload;
+ var
+ tpc:array [0..1023] of AnsiChar;
+ begin
+ GetDlgItemTextA(Dialog,id,tpc,SizeOf(tpc));
+ mFreeMem(dst);
+ StrDup(dst,tpc);
+ LV_SetItem(listhwnd,dst,selitem,subpos);
+ end;
+
+ // now for Title only
+ procedure GetText(id,subpos:integer;var dst:PWideChar); overload;
+ var
+ tpc:array [0..1023] of WideChar;
+ begin
+ GetDlgItemTextW(Dialog,id,@tpc,SizeOf(tpc) div SizeOf(WideChar));
+ mFreeMem(dst);
+ StrDupW(dst,@tpc);
+ LV_SetItemW(listhwnd,dst,selitem,subpos);
+ end;
+
+ procedure setparam(var param:tserviceparam;cb,id:integer);
+ var
+ z:bool;
+ tpc:array [0..1023] of WideChar;
+ begin
+ param._type:=SendDlgItemMessage(Dialog,cb,CB_GETCURSEL,0,0);
+ case param._type of
+ ptNumber: param.n:=GetDlgItemInt(Dialog,id,z,false);
+ ptInteger: param.i:=GetDlgItemInt(Dialog,id,z,true);
+ ptString: begin
+ GetDlgItemTextA(Dialog,id,@tpc,SizeOf(tpc));
+ StrDup(param.a,@tpc);
+ end;
+ ptUnicode: begin
+ GetDlgItemTextW(Dialog,id,@tpc,SizeOf(tpc) div SizeOf(WideChar));
+ StrDupW(param.w,@tpc);
+ end;
+ end;
+ end;
+
+var
+ tpc :array [0..127] of WideChar;
+ tmpwnd:HWND;
+ oldtype,i:integer;
+ column:integer;
+begin
+ listhwnd:=GetDlgItem(Dialog,IDC_LIST);
+ selitem:=getselecteditem(listhwnd);
+//!! column:=LV_GetLParam(listhwnd,selitem);
+ column:=selitem;
+ result:=selitem;
+ if (selitem>=0) and (selitem<qsopt.numcolumns) then
+ begin
+ with qsopt.columns[column] do
+ begin
+ flags:=0;
+
+ if ListView_GetCheckState(listhwnd,selitem)=BST_CHECKED then
+ flags:=flags or COL_ON;
+
+ oldtype:=setting_type;
+
+ tmpwnd:=GetDlgItem(Dialog,IDC_C_VARTYPE);
+
+ setting_type:=SendMessage(tmpwnd,CB_GETITEMDATA,
+ SendMessage(tmpwnd,CB_GETCURSEL,0,0),0);
+
+ GetText(IDC_E_TITLE,1,title);
+
+ case oldtype of
+ ST_SCRIPT: begin
+ mFreeMem(wparam.a);
+ end;
+ ST_SERVICE: begin
+ if (wparam._type=ptString) or (wparam._type=ptUnicode) then mFreeMem(wparam.a);
+ if (lparam._type=ptString) or (lparam._type=ptUnicode) then mFreeMem(lparam.a);
+ end;
+ else
+ mFreeMem(wparam.a);
+ end;
+
+ case setting_type of
+ ST_METACONTACT: begin
+ LV_SetItemW(listhwnd,TranslateW('Metacontact'),selitem,2);
+ end;
+
+ ST_SCRIPT: begin
+ StrDupW(wparam.w,curscript);
+ LV_SetItemW(listhwnd,TranslateW('Script'),selitem,2);
+ end;
+
+ ST_CONTACTINFO: begin
+ FillChar(tpc,SizeOf(tpc),0);
+ tmpwnd:=GetDlgItem(Dialog,IDC_C_CNFTYPE);
+
+ i:=SendMessage(tmpwnd,CB_GETCURSEL,0,0);
+ setting_cnftype:=SendMessage(tmpwnd,CB_GETITEMDATA,i,0);
+
+ SendMessageW(tmpwnd,CB_GETLBTEXT,i,tlparam(@tpc));
+ LV_SetItemW(listhwnd,tpc,selitem,2);
+ end;
+
+ ST_SERVICE: begin
+ GetText(IDC_E_MODULE,3,module_name);
+ LV_SetItemW(listhwnd,TranslateW('Service'),selitem,2);
+//!! setitem(listhwnd,selitem,3,module_name);
+
+ setting_cnftype:=SendDlgItemMessage(Dialog,IDC_C_RESULT,CB_GETCURSEL,0,0);
+ setparam(wparam,IDC_C_WPAR,IDC_E_VAR);
+ setparam(lparam,IDC_C_LPAR,IDC_E_LPAR);
+ end;
+ else
+ GetText(IDC_E_MODULE,2,module_name);
+ GetText(IDC_E_VAR ,3,wparam.a)
+ end;
+ end;
+ end;
+end;
+
+procedure disable_elem(Dialog:hwnd;id:cardinal);
+begin
+ EnableWindow(GetDlgItem(Dialog,id),FALSE);
+end;
+
+procedure enable_elem(Dialog:hwnd;id:cardinal);
+begin
+ EnableWindow(GetDlgItem(Dialog,id),TRUE);
+end;
+
+procedure CheckDirection(Dialog:HWND;item:integer);
+begin
+ if item=0 then
+ disable_elem(Dialog,IDC_UP)
+ else
+ enable_elem(Dialog,IDC_UP);
+
+ if item=(qsopt.numcolumns-1) then
+ disable_elem(Dialog,IDC_DN)
+ else
+ enable_elem(Dialog,IDC_DN);
+end;
+
+procedure displcurinfo(Dialog:hwnd;column:integer);
+
+ procedure set_elem(const param:tserviceparam;cb,id:integer);
+ begin
+ SendDlgItemMessage(Dialog,cb,CB_SETCURSEL,param._type,0);
+ case param._type of
+ ptNumber: SetDlgItemInt (Dialog,id,param.n,false);
+ ptInteger: SetDlgItemInt (Dialog,id,param.i,true);
+ ptString: SetDlgItemTextA(Dialog,id,param.a);
+ ptUnicode: SetDlgItemTextW(Dialog,id,param.w);
+ else
+ SetDlgItemTextA(Dialog,id,'');
+ end;
+ EnableWindow(GetDlgItem(Dialog,id),param._type<>ptCurrent);
+ end;
+
+var
+ v:PWideChar;
+ i:int_ptr;
+ selpos:integer;
+begin
+ CheckDirection(Dialog,column);
+
+ selpos:=column;
+ if (selpos>=0) and (selpos<qsopt.numcolumns) then
+ begin
+ enable_elem(Dialog,IDC_E_TITLE);
+ enable_elem(Dialog,IDC_C_VARTYPE);
+ enable_elem(Dialog,IDC_DELETE);
+// enable_elem(Dialog,IDC_SETITEM);
+
+ with qsopt.columns[column] do
+ begin
+ SetupControls(Dialog,setting_type);
+ case setting_type of
+ ST_SCRIPT: begin
+ mFreeMem(curscript);
+ StrDupW(pWideChar(curscript),wparam.w);
+ end;
+ ST_SERVICE: begin
+ SendDlgItemMessage(Dialog,IDC_C_RESULT,CB_SETCURSEL,setting_cnftype,0);
+ SetDlgItemTextA(Dialog,IDC_E_MODULE,module_name);
+ set_elem(wparam,IDC_C_WPAR,IDC_E_VAR);
+ set_elem(lparam,IDC_C_LPAR,IDC_E_LPAR);
+ end;
+ ST_CONTACTINFO: begin
+ i:=int_ptr(setcnftype2str(setting_cnftype));
+ SendDlgItemMessageW(Dialog,IDC_C_CNFTYPE,CB_SELECTSTRING,twparam(-1),i);
+ end;
+ else
+ SetDlgItemTextA(Dialog,IDC_E_MODULE,module_name);
+ SetDlgItemTextA(Dialog,IDC_E_VAR,wparam.a);
+ end;
+
+ v:=settype2str(setting_type);
+ SetDlgItemTextW(Dialog,IDC_E_TITLE,title);
+ SendDlgItemMessageW(Dialog,IDC_C_VARTYPE,CB_SELECTSTRING,twparam(-1),tlparam(v));
+ mFreeMem(v);
+ end;
+ end
+ else
+ begin
+ disable_elem(Dialog,IDC_E_TITLE);
+ disable_elem(Dialog,IDC_C_VARTYPE);
+ disable_elem(Dialog,IDC_DELETE);
+ disable_elem(Dialog,IDC_SETITEM);
+
+ v:=TranslateW(strNotSelected);
+ SetDlgItemTextW(Dialog,IDC_E_TITLE ,v);
+ SetDlgItemTextW(Dialog,IDC_E_MODULE,v);
+ SetDlgItemTextW(Dialog,IDC_E_VAR ,v);
+ SetDlgItemTextW(Dialog,IDC_E_LPAR ,v);
+ SendDlgItemMessageW(Dialog,IDC_C_VARTYPE,CB_SELECTSTRING,twparam(-1),tlparam(v));
+ SendDlgItemMessageW(Dialog,IDC_C_CNFTYPE,CB_SELECTSTRING,twparam(-1),tlparam(v));
+ end;
+end;
+
+function add_column(list:HWND;i:integer):integer;
+var
+ li:LV_ITEMA;
+begin
+ result:=i;
+ zeromemory(@li,sizeof(li));
+ li.mask :=LVIF_PARAM;
+ li.lParam:=i;
+ li.iItem :=i;
+ SendMessage(list,LVM_INSERTITEM,0,lparam(@li));
+
+ with qsopt.columns[i] do
+ begin
+ ListView_SetCheckState(list,i,(flags and COL_ON)<>0);
+ LV_SetItemW(list,title,i,1);
+ case setting_type of
+ ST_METACONTACT: begin
+ LV_SetItemW(list,TranslateW('Metacontact'),i,2);
+ end;
+
+ ST_CONTACTINFO: begin
+ LV_SetItemW(list,setcnftype2str(setting_cnftype),i,2)
+ end;
+
+ ST_SCRIPT: begin
+ LV_SetItemW(list,TranslateW('Script'),i,2);
+ end;
+
+ ST_SERVICE: begin
+ LV_SetItemW(list,TranslateW('Service'),i,2);
+ LV_SetItem(list,module_name,i,3);
+ end;
+ else
+ LV_SetItem(list,module_name,i,2);
+ LV_SetItem(list,wparam.a,i,3);
+ end;
+ end;
+end;
+
+procedure update_list(list:hwnd);
+var
+ i:integer;
+begin
+ ListView_DeleteAllItems(list);
+ ListView_SetItemCount(list,qsopt.numcolumns);
+
+ for i:=0 to qsopt.numcolumns-1 do
+ begin
+ add_column(list,i);
+ end;
+//!! SortColumns(list);
+ ListView_SetItemState(list,0,LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+end;
+
+procedure addresulttypes(list:integer);
+begin
+ SendMessageW(list,CB_INSERTSTRING,ptNumber ,lparam(TranslateW('number value' )));
+ SendMessageW(list,CB_INSERTSTRING,ptInteger,lparam(TranslateW('integer value' )));
+ SendMessageW(list,CB_INSERTSTRING,ptString ,lparam(TranslateW('ANSI string' )));
+ SendMessageW(list,CB_INSERTSTRING,ptUnicode,lparam(TranslateW('Unicode string')));
+ SendMessage(list,CB_SETCURSEL,0,0);
+end;
+
+procedure addparamtypes(list:integer);
+begin
+ addresulttypes(list);
+ SendMessageW(list,CB_INSERTSTRING,ptCurrent,lparam(TranslateW('current contact')));
+end;
+
+procedure AddVal(list:HWND;param:integer);
+var
+ i:integer;
+ v:pointer;
+begin
+ v:=settype2str(param);
+ i:=SendMessageW(list,CB_ADDSTRING,0,lparam(v));
+ mFreeMem(v);
+ SendMessage(list,CB_SETITEMDATA,i,param);
+end;
+
+procedure addsettypes(list:hwnd);
+begin
+ AddVal(list,ST_BYTE);
+ AddVal(list,ST_WORD);
+ AddVal(list,ST_INT);
+ AddVal(list,ST_STRING);
+ AddVal(list,ST_LASTSEEN);
+ AddVal(list,ST_IP);
+ AddVal(list,ST_CONTACTINFO);
+ AddVal(list,ST_LASTEVENT);
+ AddVal(list,ST_TIMESTAMP);
+ AddVal(list,ST_SERVICE);
+ AddVal(list,ST_SCRIPT);
+ AddVal(list,ST_METACONTACT);
+ SendMessage(list,CB_SETCURSEL,0,0);
+end;
+
+procedure AddCnf(list:HWND;param:integer);
+begin
+ SendMessage(list,CB_SETITEMDATA,
+ SendMessageW(list,CB_ADDSTRING,0,lparam(setcnftype2str(param))),
+ param);
+// mFreeMem(str);
+end;
+
+procedure addsetcnftypes(list:hwnd);
+begin
+ AddCnf(list,CNF_FIRSTNAME);
+ AddCnf(list,CNF_LASTNAME);
+ AddCnf(list,CNF_NICK);
+ AddCnf(list,CNF_CUSTOMNICK);
+ AddCnf(list,CNF_EMAIL);
+ AddCnf(list,CNF_CITY);
+ AddCnf(list,CNF_STATE);
+ AddCnf(list,CNF_COUNTRY);
+ AddCnf(list,CNF_PHONE);
+ AddCnf(list,CNF_HOMEPAGE);
+ AddCnf(list,CNF_ABOUT);
+ AddCnf(list,CNF_GENDER);
+ AddCnf(list,CNF_AGE);
+ AddCnf(list,CNF_FIRSTLAST);
+ AddCnf(list,CNF_UNIQUEID);
+
+ AddCnf(list,CNF_FAX);
+ AddCnf(list,CNF_CELLULAR);
+ AddCnf(list,CNF_TIMEZONE);
+ AddCnf(list,CNF_MYNOTES);
+ AddCnf(list,CNF_BIRTHDAY);
+ AddCnf(list,CNF_BIRTHMONTH);
+ AddCnf(list,CNF_BIRTHYEAR);
+ AddCnf(list,CNF_STREET);
+ AddCnf(list,CNF_ZIP);
+ AddCnf(list,CNF_LANGUAGE1);
+ AddCnf(list,CNF_LANGUAGE2);
+ AddCnf(list,CNF_LANGUAGE3);
+ AddCnf(list,CNF_CONAME);
+
+ SendMessage(list,CB_SETCURSEL,0,0);
+end;
+
+procedure _GetIcon(idc:integer;ico:PAnsiChar);
+begin
+ SetButtonIcon(GetDlgItem(maindlg,idc),ico);
+end;
+
+function IconChanged(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+begin
+ result:=0;
+ _GetIcon(IDC_NEW ,QS_NEW);
+ _GetIcon(IDC_SETITEM,QS_ITEM);
+ _GetIcon(IDC_UP ,QS_UP);
+ _GetIcon(IDC_DN ,QS_DOWN);
+ _GetIcon(IDC_DELETE ,QS_DELETE);
+ _GetIcon(IDC_DEFAULT,QS_DEFAULT);
+ _GetIcon(IDC_RELOAD ,QS_RELOAD);
+end;
+
+function ScriptEdit(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+var
+ tmp:pointer;
+ vhi:TVARHELPINFO;
+begin
+ result:=0;
+ case hMessage of
+
+ WM_INITDIALOG: begin
+ TranslateDialogDefault(Dialog);
+ SetDlgItemTextW(Dialog,IDC_EDIT_SCRIPT,pWideChar(lParam));
+ end;
+
+ WM_COMMAND: begin
+ case wParam shr 16 of
+ BN_CLICKED: begin
+ case loword(wParam) of
+ IDHELP: SendMessage(Dialog,WM_HELP,0,0);
+ IDOK: begin
+ tmp:=GetDlgText(Dialog,IDC_EDIT_SCRIPT);
+ EndDialog(Dialog,tlparam(tmp));
+ end;
+ IDCANCEL: begin // clear result / restore old value
+ EndDialog(Dialog,0);
+ end;
+ end;
+ end;
+ end;
+ end;
+
+ WM_HELP: begin
+ FillChar(vhi,SizeOf(vhi),0);
+ with vhi do
+ begin
+ cbSize:=SizeOf(vhi);
+ flags:=VHF_NOINPUTDLG;
+ end;
+ CallService(MS_VARS_SHOWHELPEX,Dialog,tlparam(@vhi));
+ end;
+ end;
+end;
+
+function NewListProc(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):integer; stdcall;
+begin
+ result:=0;
+ if hMessage=WM_KEYDOWN then
+ begin
+ case wParam of
+ VK_INSERT: begin
+ PostMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDC_NEW,0);
+ exit;
+ end;
+ VK_DELETE: begin
+ PostMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDC_DELETE,0);
+ exit;
+ end;
+ VK_UP: begin
+ if (GetKeyState(VK_CONTROL) and $8000)<>0 then
+ begin
+ PostMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDC_UP,0);
+ exit;
+ end;
+ end;
+ VK_DOWN: begin
+ if (GetKeyState(VK_CONTROL) and $8000)<>0 then
+ begin
+ PostMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDC_DN,0);
+ exit;
+ end;
+ end;
+ end;
+ end;
+ result:=CallWindowProc(OldListProc,Dialog,hMessage,wParam,lParam);
+end;
+
+function DlgProcOptions(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+const
+ InitDlg:bool = true;
+ hook:THANDLE = 0;
+var
+// tpc:array [0..255] of AnsiChar;
+ itemsel:integer;
+ tmpwnd:HWND;
+ tmpbool:bool;
+ i:integer;
+// ti:TTOOLINFOW;
+// hwndTooltip:HWND;
+ hNew :hwnd;
+ hItem :hwnd;
+ hUp :hwnd;
+ hDown :hwnd;
+ hDelete :hwnd;
+ hDefault:hwnd;
+ hReload :hwnd;
+ listhwnd:hwnd;
+ tmpcol:tcolumnitem;
+begin
+ result:=0;
+
+ case hMessage of
+ WM_DESTROY: begin
+ // if closed by Cancel, with changes but without apply
+ loadopt_db(false); //!!!!
+ WndChangeColumns(wcRefresh);
+
+ mFreeMem(curscript);
+ if hook<>0 then
+ UnhookEvent(hook);
+ maindlg:=0;
+ end;
+
+ WM_INITDIALOG: begin
+ InitDlg:=true;
+ listhwnd:=GetDlgItem(Dialog,IDC_LIST);
+
+ SendMessageW(listhwnd,LVM_SETEXTENDEDLISTVIEWSTYLE,0,
+ SendMessageW(listhwnd,LVM_GETEXTENDEDLISTVIEWSTYLE,0,0) or
+ LVS_EX_FULLROWSELECT or LVS_EX_CHECKBOXES);
+
+ TranslateDialogDefault(Dialog);
+
+ addcolumn(listhwnd,95 ,'Setting');
+ addcolumn(listhwnd,105,'Module/InfoType');
+ addcolumn(listhwnd,85 ,'Title');
+ addcolumn(listhwnd,20 ,'#');
+ addsettypes (GetDlgItem(Dialog,IDC_C_VARTYPE));
+ addsetcnftypes(GetDlgItem(Dialog,IDC_C_CNFTYPE));
+ addparamtypes (GetDlgItem(Dialog,IDC_C_WPAR));
+ addparamtypes (GetDlgItem(Dialog,IDC_C_LPAR));
+ addresulttypes(GetDlgItem(Dialog,IDC_C_RESULT));
+
+ CheckDlgButton(Dialog,IDC_CH_SORTSTATUS ,ORD(qsopt.sortbystatus));
+ CheckDlgButton(Dialog,IDC_CH_SHOWINMENU ,ORD(qsopt.showinmenu));
+ CheckDlgButton(Dialog,IDC_CH_SHOWONLYUSERS ,ORD(qsopt.showonlyinlist));
+ CheckDlgButton(Dialog,IDC_CH_AUTOCLOSE ,ORD(qsopt.closeafteraction));
+ CheckDlgButton(Dialog,IDC_CH_ADDTOTOPTOOLBAR,ORD(qsopt.showintoptoolbar));
+ CheckDlgButton(Dialog,IDC_CH_USETOOLSTYLE ,ORD(qsopt.usetoolstyle));
+ CheckDlgButton(Dialog,IDC_CH_DRAWGRID ,ORD(qsopt.drawgrid));
+ CheckDlgButton(Dialog,IDC_CH_SINGLECSV ,ORD(qsopt.singlecsv));
+ CheckDlgButton(Dialog,IDC_CH_EXPORTHEADERS ,ORD(qsopt.exportheaders));
+ CheckDlgButton(Dialog,IDC_CH_SKIPMINIMIZED ,ORD(qsopt.skipminimized));
+ CheckDlgButton(Dialog,IDC_CH_SAVEPATTERN ,ORD(qsopt.savepattern));
+ if ServiceExists(MS_FP_GETCLIENTICON)<>0 then
+ CheckDlgButton(Dialog,IDC_CH_SHOWCLIENTICONS,ORD(qsopt.showclienticons))
+ else
+ EnableWindow(GetDlgItem(Dialog,IDC_CH_SHOWCLIENTICONS),false);
+
+ if ServiceExists(MS_TTB_ADDBUTTON)=0 then
+ EnableWindow(GetDlgItem(Dialog,IDC_CH_ADDTOTOPTOOLBAR),FALSE);
+{
+ hwndTooltip:=CreateWindowW(TOOLTIPS_CLASS,nil,TTS_ALWAYSTIP,
+ integer(CW_USEDEFAULT),integer(CW_USEDEFAULT),
+ integer(CW_USEDEFAULT),integer(CW_USEDEFAULT),
+ Dialog,0,hInstance,nil);
+}
+ hNew :=GetDlgItem(Dialog,IDC_NEW);
+ SendMessage(hNew,BUTTONADDTOOLTIP,TWPARAM(TranslateW('New')),BATF_UNICODE);
+ hItem :=GetDlgItem(Dialog,IDC_SETITEM);
+ SendMessage(hItem,BUTTONADDTOOLTIP,TWPARAM(TranslateW('Save Item')),BATF_UNICODE);
+ hUp :=GetDlgItem(Dialog,IDC_UP);
+ SendMessage(hUp,BUTTONADDTOOLTIP,TWPARAM(TranslateW('Up')),BATF_UNICODE);
+ hDown :=GetDlgItem(Dialog,IDC_DN);
+ SendMessage(hDown,BUTTONADDTOOLTIP,TWPARAM(TranslateW('Down')),BATF_UNICODE);
+ hDelete :=GetDlgItem(Dialog,IDC_DELETE);
+ SendMessage(hDelete,BUTTONADDTOOLTIP,TWPARAM(TranslateW('Delete')),BATF_UNICODE);
+ hDefault:=GetDlgItem(Dialog,IDC_DEFAULT);
+ SendMessage(hDefault,BUTTONADDTOOLTIP,TWPARAM(TranslateW('Default')),BATF_UNICODE);
+ hReload :=GetDlgItem(Dialog,IDC_RELOAD);
+ SendMessage(hReload,BUTTONADDTOOLTIP,TWPARAM(TranslateW('Reload')),BATF_UNICODE);
+{
+ FillChar(ti,SizeOf(ti),0);
+ ti.cbSize :=sizeof(TOOLINFO);
+ ti.uFlags :=TTF_IDISHWND or TTF_SUBCLASS;
+ ti.hwnd :=Dialog;
+ ti.hinst :=hInstance;
+ ti.uId :=hNew;
+ ti.lpszText:=TranslateW('New');
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,tlparam(@ti));
+ ti.uId :=hItem;
+ ti.lpszText:=TranslateW('Save Item');
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,tlparam(@ti));
+ ti.uId :=hUp;
+ ti.lpszText:=TranslateW('Up');
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,tlparam(@ti));
+ ti.uId :=hDown;
+ ti.lpszText:=TranslateW('Down');
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,tlparam(@ti));
+ ti.uId :=hDelete;
+ ti.lpszText:=TranslateW('Delete');
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,tlparam(@ti));
+ ti.uId :=hDefault;
+ ti.lpszText:=TranslateW('Default');
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,tlparam(@ti));
+ ti.uId :=hReload;
+ ti.lpszText:=TranslateW('Reload');
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,tlparam(@ti));
+}
+ SetButtonIcon(hNew ,QS_NEW);
+ SetButtonIcon(hItem, QS_ITEM);
+ SetButtonIcon(hUp ,QS_UP);
+ SetButtonIcon(hDown ,QS_DOWN);
+ SetButtonIcon(hDelete ,QS_DELETE);
+ SetButtonIcon(hDefault,QS_DEFAULT);
+ SetButtonIcon(hReload ,QS_RELOAD);
+
+ update_list(listhwnd);
+
+ maindlg:=Dialog;
+ hook:=HookEvent(ME_SKIN2_ICONSCHANGED,@IconChanged);
+
+ result:=1;
+
+ OldListProc:=pointer(SetWindowLongPtrW(listhwnd,GWL_WNDPROC,LONG_PTR(@NewListProc)));
+
+ InitDlg:=false;
+ end;
+
+ WM_NOTIFY: begin
+ case integer(PNMHdr(lParam)^.code) of
+
+ PSN_APPLY: begin
+ // checkboxes
+ listhwnd:=GetDlgItem(Dialog,IDC_LIST);
+
+ for i:=0 to SendMessage(listhwnd,LVM_GETITEMCOUNT,0,0)-1 do
+ begin
+ with qsopt.columns[i] do
+ begin
+ if ListView_GetCheckSTate(listhwnd,i)=0 then
+ flags:=flags and not COL_ON
+ else
+ flags:=flags or COL_ON;
+ end;
+ end;
+
+ disable_elem(Dialog,IDC_SETITEM);
+ savecuritem(Dialog);
+
+ qsopt.sortbystatus :=IsDlgButtonChecked(Dialog,IDC_CH_SORTSTATUS )<>BST_UNCHECKED;
+ qsopt.showonlyinlist :=IsDlgButtonChecked(Dialog,IDC_CH_SHOWONLYUSERS )<>BST_UNCHECKED;
+ qsopt.closeafteraction:=IsDlgButtonChecked(Dialog,IDC_CH_AUTOCLOSE )<>BST_UNCHECKED;
+ qsopt.usetoolstyle :=IsDlgButtonChecked(Dialog,IDC_CH_USETOOLSTYLE )<>BST_UNCHECKED;
+ qsopt.drawgrid :=IsDlgButtonChecked(Dialog,IDC_CH_DRAWGRID )<>BST_UNCHECKED;
+ qsopt.showclienticons :=IsDlgButtonChecked(Dialog,IDC_CH_SHOWCLIENTICONS)<>BST_UNCHECKED;
+ qsopt.singlecsv :=IsDlgButtonChecked(Dialog,IDC_CH_SINGLECSV )<>BST_UNCHECKED;
+ qsopt.exportheaders :=IsDlgButtonChecked(Dialog,IDC_CH_EXPORTHEADERS )<>BST_UNCHECKED;
+ qsopt.skipminimized :=IsDlgButtonChecked(Dialog,IDC_CH_SKIPMINIMIZED )<>BST_UNCHECKED;
+ qsopt.savepattern :=IsDlgButtonChecked(Dialog,IDC_CH_SAVEPATTERN )<>BST_UNCHECKED;
+
+ tmpbool:=IsDlgButtonChecked(Dialog,IDC_CH_SHOWINMENU)<>BST_UNCHECKED;
+ if qsopt.showinmenu<>tmpbool then
+ begin
+ qsopt.showinmenu:=tmpbool;
+ AddRemoveMenuItemToMainMenu;
+ end;
+ tmpbool:=IsDlgButtonChecked(Dialog,IDC_CH_ADDTOTOPTOOLBAR )<>BST_UNCHECKED;
+ if qsopt.showintoptoolbar<>tmpbool then
+ begin
+ qsopt.showintoptoolbar:=tmpbool;
+ addtotoolbar;
+ end;
+
+ saveopt_db;
+ result:=1;
+ end;
+
+ LVN_ITEMCHANGED: begin
+ if wParam=IDC_LIST then
+ begin
+ i:=(PNMLISTVIEW(lParam)^.uOldState and LVNI_FOCUSED)-
+ (PNMLISTVIEW(lParam)^.uNewState and LVNI_FOCUSED);
+ if i<0 then // new focus
+ begin
+ InitDlg:=true;
+ displcurinfo(Dialog,PNMLISTVIEW(lParam)^.iItem);
+{!!
+ displcurinfo(Dialog,
+ LV_GetLParam(PNMLISTVIEW(lParam)^.hdr.hwndFrom,
+ PNMLISTVIEW(lParam)^.iItem));
+}
+ InitDlg:=false;
+ result:=1;
+ end else if (i=0) and not InitDlg then
+ begin
+ if (PNMLISTVIEW(lParam)^.uOldState or PNMLISTVIEW(lParam)^.uNewState)=$3000 then
+ begin
+ i:=PNMLISTVIEW(lParam)^.uOldState-PNMLISTVIEW(lParam)^.uNewState;
+ if abs(i)=$1000 then
+ begin
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ if i<0 then
+ i:=wcShow
+ else
+ i:=wcHide;
+ WndChangeColumns(i,PNMLISTVIEW(lParam)^.iItem);
+ result:=1;
+ end;
+ end;
+ end;
+ end;
+ end;
+
+ end;
+ end;
+
+ WM_MYSHOWHIDEITEM: begin
+ InitDlg:=true;
+ ListView_SetCheckState(GetDlgItem(Dialog,IDC_LIST),wParam,lParam<>0);
+ InitDlg:=false;
+ end;
+
+ WM_MYMOVEITEM: begin
+ listhwnd:=GetDlgItem(Dialog,IDC_LIST);
+ LV_MoveItem(listhwnd,lParam,wParam);
+ itemsel:=wParam+lParam;
+ i:=SizeOf(tcolumnitem)*abs(integer(lParam));
+ move(qsopt.columns[wParam],tmpcol,SizeOf(tcolumnitem));
+
+ if integer(lParam)>0 then
+ move(qsopt.columns[wParam+1],qsopt.columns[wParam],i)
+ else
+ move(qsopt.columns[itemsel],qsopt.columns[itemsel+1],i);
+
+ move(tmpcol,qsopt.columns[itemsel],SizeOf(tcolumnitem));
+
+ CheckDirection(Dialog,getselecteditem(listhwnd));
+ end;
+
+ WM_COMMAND: begin
+ if ((wParam shr 16)=CBN_SELCHANGE) then
+ begin
+ case loword(wParam) of
+ IDC_C_VARTYPE: begin
+ i:=SendMessage(lParam,CB_GETITEMDATA,
+ SendMessage(lParam,CB_GETCURSEL,0,0),0);
+
+ SetupControls(Dialog,i);
+
+ EnableWindow(GetDlgItem(Dialog,IDC_E_VAR),
+ SendDlgItemMessage(Dialog,IDC_C_WPAR,CB_GETCURSEL,0,0)<>ptCurrent);
+ EnableWindow(GetDlgItem(Dialog,IDC_E_LPAR),
+ SendDlgItemMessage(Dialog,IDC_C_LPAR,CB_GETCURSEL,0,0)<>ptCurrent);
+ end;
+ IDC_C_WPAR: begin
+ EnableWindow(GetDlgItem(Dialog,IDC_E_VAR),
+ SendMessage(lParam,CB_GETCURSEL,0,0)<>ptCurrent);
+ end;
+ IDC_C_LPAR: begin
+ EnableWindow(GetDlgItem(Dialog,IDC_E_LPAR),
+ SendMessage(lParam,CB_GETCURSEL,0,0)<>ptCurrent);
+ end;
+ end;
+ end;
+
+ if not InitDlg then
+ case wParam shr 16 of
+ CBN_SELCHANGE,
+ BN_CLICKED,
+ EN_CHANGE: begin
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ enable_elem(Dialog,IDC_SETITEM);
+ end;
+ end;
+
+ listhwnd:=GetDlgItem(Dialog,IDC_LIST);
+ result:=1;
+ case loword(wParam) of
+ IDC_SCRIPT: begin
+ tmpwnd:=DialogBoxParamW(hInstance,MAKEINTRESOURCEW(IDD_SCRIPT),
+ Dialog,@ScriptEdit,tlparam(curscript));
+ if tmpwnd<>0 then
+ begin
+ curscript:=pointer(tmpwnd);
+ end;
+ end;
+
+ IDC_NEW: begin
+ InitDlg:=true;
+ WndChangeColumns(wcInsert,
+ add_column(listhwnd,new_column(getselecteditem(listhwnd))));
+
+ SendMessage(listhwnd,LVM_ENSUREVISIBLE,qsopt.numcolumns-1,0);
+ ListView_SetItemState(listhwnd,qsopt.numcolumns-1,
+ LVIS_FOCUSED+LVIS_SELECTED,LVIS_FOCUSED+LVIS_SELECTED);
+ InitDlg:=false;
+ end;
+
+ IDC_DELETE: begin
+ i:=getselecteditem(listhwnd);
+ SendMessage(listhwnd,LVM_DELETEITEM,i,0);
+ delete_column(i);
+ WndChangeColumns(wcDelete,i);
+// update_list(listhwnd);
+
+ if i=qsopt.numcolumns then dec(i);
+ ListView_SetItemState(listhwnd,i,
+ LVIS_FOCUSED+LVIS_SELECTED,LVIS_FOCUSED+LVIS_SELECTED);
+ end;
+
+ IDC_UP: begin
+ itemsel:=getselecteditem(listhwnd);
+ if itemsel>0 then
+ begin
+ LV_MoveItem(listhwnd,-1,itemsel);
+ move(qsopt.columns[itemsel] ,tmpcol ,SizeOf(tcolumnitem));
+ move(qsopt.columns[itemsel-1],qsopt.columns[itemsel] ,SizeOf(tcolumnitem));
+ move(tmpcol ,qsopt.columns[itemsel-1],SizeOf(tcolumnitem));
+ WndChangeColumns(wcUp,itemsel);
+ CheckDirection(Dialog,itemsel-1);
+ end;
+ end;
+
+ IDC_DN: begin
+ itemsel:=getselecteditem(listhwnd);
+ if (itemsel>=0) and (itemsel<(qsopt.numcolumns-1)) then
+ begin
+ LV_MoveItem(listhwnd,1,itemsel);
+ move(qsopt.columns[itemsel] ,tmpcol ,SizeOf(tcolumnitem));
+ move(qsopt.columns[itemsel+1],qsopt.columns[itemsel] ,SizeOf(tcolumnitem));
+ move(tmpcol ,qsopt.columns[itemsel+1],SizeOf(tcolumnitem));
+ WndChangeColumns(wcDown,itemsel);
+ CheckDirection(Dialog,itemsel+1);
+ end;
+ end;
+
+ IDC_SETITEM: begin
+ WndChangeColumns(wcChange,savecuritem(Dialog));
+ end;
+
+ IDC_DEFAULT: begin
+ InitDlg:=true;
+ loaddefaultcolumns;
+ update_list(listhwnd);
+ WndChangeColumns(wcRefresh);
+ InitDlg:=false;
+ end;
+
+ IDC_RELOAD: begin
+ InitDlg:=true;
+ loadopt_db(false);
+ update_list(listhwnd);
+ WndChangeColumns(wcRefresh);
+ InitDlg:=false;
+ end;
+ else
+ result:=0;
+ end;
+ end;
+// else
+// result:=DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+ DefWindowProc(Dialog,hMessage,wParam,lParam);
+end;
+
+procedure OptChangeColumns(code:integer;column,data:integer);
+begin
+ case code of
+ wcUp: begin
+ SendMessage(maindlg,WM_MYSHOWHIDEITEM,column,data);
+ end;
+
+ wcShow: begin
+ SendMessage(maindlg,WM_MYSHOWHIDEITEM,column,data);
+ end;
+ end;
+end;
+
+end.
diff --git a/plugins/QuickSearch/sr_window.pas b/plugins/QuickSearch/sr_window.pas
new file mode 100644
index 0000000000..5df340f575
--- /dev/null
+++ b/plugins/QuickSearch/sr_window.pas
@@ -0,0 +1,2943 @@
+unit sr_window;
+
+interface
+
+uses windows,m_api;
+
+function OpenSrWindow(apattern:PWideChar;flags:LPARAM):boolean;
+function BringToFront:integer;
+function CloseSrWindow:boolean;
+procedure WndChangeColumns(code:integer;column:integer=-1);
+
+function OnContactAdded (wParam:WPARAM;lParam:LPARAM):int;cdecl;
+function OnStatusChanged (wParam:WPARAM;lParam:LPARAM):int;cdecl;
+function OnContactDeleted(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+function OnAccountChanged(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+procedure RegisterColors;
+
+const
+ grid:HWND = 0;
+
+implementation
+
+uses messages,commctrl,sr_global,common,dbsettings,mirutils,
+ wrapper,sr_optdialog,protocols;
+
+const
+ flt_show_offline = $100;
+const
+ strCListDel:PAnsiChar='CList/DeleteContactCommand';
+const
+ LVS_EX_LABELTIP = $4000;
+const
+ hIconF :HICON = 0;
+ hIconM :HICON = 0;
+ mainwnd :HWND = 0;
+ StatusBar:HWND = 0;
+ sortcoldn:THANDLE = 0;
+ sortcolup:THANDLE = 0;
+ gridbrush:HBRUSH = 0;
+const
+ OldLVProc :pointer = nil;
+ OldProc :pointer = nil;
+ OldEditProc:pointer = nil;
+const
+ pattern:pWideChar = nil;
+const
+ QSF_INLIST = $0001; // in constant list
+ QSF_ACTIVE = $0002; // contact in listview
+ QSF_DELETED = $0004; // contact deleted
+ QSF_PATTERN = $0008; // pattern check passed
+ QSF_ACCDEL = $0010; // account deleted
+ QSF_ACCOFF = $0020; // account disabled
+ QSF_META = $0040; // contact is metacontact
+ QSF_SUBMETA = $0080; // contact is part of metacontact
+
+type
+ pQSRec = ^tQSRec;
+ tQSRec = record // cell
+ text:PWideChar;
+ data:uint_ptr;
+ end;
+ pQSFRec = ^tQSFRec;
+ tQSFRec = record // row
+ contact:THANDLE;
+ proto :uint_ptr;
+ flags :dword;
+ status :dword;
+ wparam :WPARAM;
+ lparam :LPARAM;
+ end;
+var
+ colorhook:THANDLE;
+ MainBuf:array of array of tQSRec;
+ FlagBuf:array of tQSFRec;
+ LastMeta:integer;
+ tstrMale,
+ tstrFemale,
+ tstrUnknown:PWideChar;
+ colorder:array of integer;
+ tablecolumns:integer;
+
+const
+ maxpattern = 8;
+var
+ patterns:array [0..maxpattern-1] of record
+ str:PWideChar;
+ res:bool;
+ end;
+const
+ patstr:PWideChar=nil;
+ numpattern:integer=0;
+
+const
+ TIMERID_HOVER = 10;
+const
+ TTShowed:bool=false;
+ TTInstalled:bool = false;
+
+const
+ bkg_norm:pAnsiChar = 'Normal background';
+ fgr_norm:pAnsiChar = 'Normal foreground';
+ bkg_odd :pAnsiChar = 'Odd background';
+ fgr_odd :pAnsiChar = 'Odd foreground';
+ bkg_dis :pAnsiChar = 'Disabled account background';
+ fgr_dis :pAnsiChar = 'Disabled account foreground';
+ bkg_del :pAnsiChar = 'Deleted account background';
+ fgr_del :pAnsiChar = 'Deleted account foreground';
+ bkg_hid :pAnsiChar = 'Hidden contact background';
+ fgr_hid :pAnsiChar = 'Hidden contact foreground';
+ bkg_meta:pAnsiChar = 'Metacontact background';
+ fgr_meta:pAnsiChar = 'Metacontact foreground';
+ bkg_sub :pAnsiChar = 'SubMetacontact background';
+ fgr_sub :pAnsiChar = 'SubMetacontact foreground';
+
+var
+ cbkg_norm,
+ cfgr_norm,
+ cbkg_odd,
+ cfgr_odd,
+ cbkg_dis,
+ cfgr_dis,
+ cbkg_del,
+ cfgr_del,
+ cbkg_hid,
+ cfgr_hid,
+ cbkg_meta,
+ cfgr_meta,
+ cbkg_sub,
+ cfgr_sub:TCOLORREF;
+ AdvFilter:cardinal;
+
+function GetQSColumn(item:integer):LPARAM;
+var
+ i:integer;
+begin
+ for i:=0 to qsopt.numcolumns-1 do
+ begin
+ if colorder[i]=item then
+ begin
+ result:=i;
+ exit;
+ end;
+ end;
+ result:=-1;
+end;
+{
+procedure SwitchOrder(var src,dst:array of dword);
+var
+ i:integer;
+begin
+ for i:=0 to HIGH(src) do
+ begin
+ dst[src[i]]:=i;
+ end;
+end;
+}
+procedure ShiftColumns(item,shift:integer); // item - table item, order - new screen order
+var
+ i,col:integer;
+ buf:tQSRec;
+ lsize,lshift:integer;
+begin
+ col:=-1;
+ lshift:=0;
+ for i:=0 to qsopt.numcolumns-1 do
+ begin
+ if (qsopt.columns[i].flags and COL_ON)<>0 then
+ begin
+ if shift=0 then
+ col:=i; // new position
+ dec(shift);
+ end;
+ if colorder[i]=item then
+ begin
+ lshift:=i; // column in buffer
+ end;
+ end;
+ item:=lshift;
+ shift:=col-item;
+
+ col:=colorder[item];
+ lsize:=sizeof(tQSRec)*abs(shift);
+ lshift:=item+shift;
+ if shift>0 then
+ begin
+ for i:=0 to HIGH(MainBuf) do
+ begin
+ buf:=MainBuf[i,item];
+ move(MainBuf[i,item+1],MainBuf[i,item],lsize);
+ MainBuf[i,lshift]:=buf;
+ end;
+
+ move(colorder[item+1],colorder[item],SizeOf(integer)*shift);
+ end
+ else // shift<0
+ begin
+ for i:=0 to HIGH(MainBuf) do
+ begin
+ buf:=MainBuf[i,item];
+ move(MainBuf[i,lshift],MainBuf[i,lshift+1],lsize);
+ MainBuf[i,lshift]:=buf;
+ end;
+
+ move(colorder[lshift],colorder[lshift+1],sizeof(integer)*(-shift));
+ end;
+ colorder[lshift]:=col;
+ OptChangeColumns(wcUp,item,shift);
+end;
+
+function GetColor(name:pAnsiChar):TCOLORREF;
+var
+ cid:TColourID;
+begin
+ cid.cbSize:=SizeOf(cid);
+ StrCopy(cid.group,'QuickSearch');
+ StrCopy(cid.name ,name);
+ result:=CallService(MS_COLOUR_GETA,lparam(@cid),0);
+end;
+
+procedure RegisterColors;
+var
+ cid:TColourID;
+begin
+ cid.cbSize:=SizeOf(cid);
+ cid.flags :=0;
+ StrCopy(cid.group,'QuickSearch');
+ StrCopy(cid.dbSettingsGroup,qs_module);
+
+ StrCopy(cid.name ,bkg_norm);
+ StrCopy(cid.setting,'back_norm');
+ cid.defcolour:=$00FFFFFF;
+ cid.order :=0;
+ ColourRegister(@cid);
+
+ StrCopy(cid.name ,fgr_norm);
+ StrCopy(cid.setting,'fore_norm');
+ cid.defcolour:=$00000000;
+ cid.order :=1;
+ ColourRegister(@cid);
+
+ StrCopy(cid.name ,bkg_odd);
+ StrCopy(cid.setting,'back_odd');
+ cid.defcolour:=$00EBE6DE;
+ cid.order :=2;
+ ColourRegister(@cid);
+
+ StrCopy(cid.name ,fgr_odd);
+ StrCopy(cid.setting,'fore_odd');
+ cid.defcolour:=$00000000;
+ cid.order :=3;
+ ColourRegister(@cid);
+
+ StrCopy(cid.name ,bkg_dis);
+ StrCopy(cid.setting,'back_dis');
+ cid.defcolour:=$008080FF;
+ cid.order :=4;
+ ColourRegister(@cid);
+
+ StrCopy(cid.name ,fgr_dis);
+ StrCopy(cid.setting,'fore_dis');
+ cid.defcolour:=$00000000;
+ cid.order :=5;
+ ColourRegister(@cid);
+
+ StrCopy(cid.name ,bkg_del);
+ StrCopy(cid.setting,'back_del');
+ cid.defcolour:=$008000FF;
+ cid.order :=6;
+ ColourRegister(@cid);
+
+ StrCopy(cid.name ,fgr_del);
+ StrCopy(cid.setting,'fore_del');
+ cid.defcolour:=$00000000;
+ cid.order :=7;
+ ColourRegister(@cid);
+
+ StrCopy(cid.name ,bkg_hid);
+ StrCopy(cid.setting,'back_hid');
+ cid.defcolour:=$0080FFFF;
+ cid.order :=8;
+ ColourRegister(@cid);
+
+ StrCopy(cid.name ,fgr_hid);
+ StrCopy(cid.setting,'fore_hid');
+ cid.defcolour:=$00000000;
+ cid.order :=9;
+ ColourRegister(@cid);
+
+ StrCopy(cid.name ,bkg_meta);
+ StrCopy(cid.setting,'back_meta');
+ cid.defcolour:=$00BAE699;
+ cid.order :=10;
+ ColourRegister(@cid);
+
+ StrCopy(cid.name ,fgr_meta);
+ StrCopy(cid.setting,'fore_meta');
+ cid.defcolour:=$00000000;
+ cid.order :=11;
+ ColourRegister(@cid);
+
+ StrCopy(cid.name ,bkg_sub);
+ StrCopy(cid.setting,'back_sub');
+ cid.defcolour:=$00B3CCC1;
+ cid.order :=12;
+ ColourRegister(@cid);
+
+ StrCopy(cid.name ,fgr_sub);
+ StrCopy(cid.setting,'fore_sub');
+ cid.defcolour:=$00000000;
+ cid.order :=13;
+ ColourRegister(@cid);
+end;
+
+function int2strw(i:integer):PWideChar;
+var
+ buf:array [0..31] of WideChar;
+begin
+ IntToStr(buf,i);
+ StrDupW(result,buf);
+end;
+
+function int2hexw(i:integer):PWideChar;
+var
+ buf:array [0..31] of WideChar;
+begin
+ IntToHex(buf,i);
+ StrDupW(result,buf);
+end;
+
+function BuildLastSeenTime(cont:THANDLE;modulename:PAnsiChar):PWideChar;
+
+var
+ buf:array [0..19] of WideChar;
+ pc:pWideChar;
+ year:integer;
+begin
+ pc:=@buf;
+ year:=DBReadWord(cont,modulename,'Year',0);
+ if year<>0 then
+ begin
+ IntToStr(pc,DBReadWord(cont,modulename,'Day',0),2);
+ inc(pc,2);
+ pc^:='.'; inc(pc);
+ IntToStr(pc,DBReadWord(cont,modulename,'Month',0),2);
+ inc(pc,2);
+ pc^:='.'; inc(pc);
+ IntToStr(pc,year,4);
+ inc(pc,4);
+ pc^:=' '; inc(pc);
+ pc^:='-'; inc(pc);
+ pc^:=' '; inc(pc);
+ IntToStr(pc,DBReadWord(cont,modulename,'Hours',0),2);
+ inc(pc,2);
+ pc^:=':'; inc(pc);
+ IntToStr(pc,DBReadWord(cont,modulename,'Minutes',0),2);
+
+ StrDupW(result,@buf);
+ end
+ else
+ result:=nil;
+{
+var
+ vars:array [0..4] of uint_ptr;
+begin
+ vars[2]:=DBReadWord(cont,modulename,'Year',0);
+ if vars[2]<>0 then
+ begin
+ mGetMem(result,20*SizeOf(WideChar));
+ vars[1]:=DBReadWord(cont,modulename,'Month' ,0);
+ vars[0]:=DBReadWord(cont,modulename,'Day' ,0);
+ vars[3]:=DBReadWord(cont,modulename,'Hours' ,0);
+ vars[4]:=DBReadWord(cont,modulename,'Minutes',0);
+ wvsprintfw(result,'%.2lu.%.2lu.%.4lu - %.2lu:%.2lu',@vars);
+ end
+ else
+ result:=nil;
+}
+end;
+
+function BuildLastSeenTimeInt(cont:thandle;modulename:PAnsiChar):cardinal;
+var
+ Day,Month,Year,Hours,Minutes:word;
+begin
+ Year:=DBReadWord(cont,modulename,'Year',0);
+ if Year<>0 then
+ begin
+ Month :=DBReadWord(cont,modulename,'Month' ,0);
+ Day :=DBReadWord(cont,modulename,'Day' ,0);
+ Hours :=DBReadWord(cont,modulename,'Hours' ,0);
+ Minutes:=DBReadWord(cont,modulename,'Minutes',0);
+ result:=Minutes+Hours*60+Day*60*24+Month*60*24*31+(Year-1980)*60*24*31*356; // was 366
+ end
+ else
+ result:=0;
+end;
+
+function IPtoStr(ip:dword):PWideChar;
+var
+ buf:array [0..16] of WideChar;
+ p:PWideChar;
+begin
+ p:=@buf;
+ IntToStr(buf,ip shr 24);
+ while p^<>#0 do inc(p); p^:='.'; inc(p);
+ IntToStr(p,(ip shr 16) and $FF);
+ while p^<>#0 do inc(p); p^:='.'; inc(p);
+ IntToStr(p,HIByte(ip));
+ while p^<>#0 do inc(p); p^:='.'; inc(p);
+ IntToStr(p,LOByte(ip));
+ StrDupW(result,buf);
+end;
+
+function TimeToStrW(data:dword):PWideChar;
+var
+ strdatetime:array [0..63] of WideChar;
+ dbtts:TDBTIMETOSTRING;
+begin
+ dbtts.cbDest :=sizeof(strdatetime);
+ dbtts.szDest.w :=@strdatetime;
+ dbtts.szFormat.w:='d - t';
+ CallService(MS_DB_TIME_TIMESTAMPTOSTRINGT,data,lparam(@dbtts));
+ StrDupW(result,strdatetime);
+end;
+
+function FindMeta(hMeta:THANDLE;var MetaNum:WPARAM):LPARAM;
+var
+ i:integer;
+begin
+ result:=0;
+ for i:=0 to HIGH(FlagBuf) do
+ begin
+ with FlagBuf[i] do
+ begin
+ if contact=hMeta then
+ begin
+ if wparam=0 then // new meta
+ begin
+ inc(LastMeta);
+ wparam :=LastMeta;
+ lparam :=0;
+ end;
+ MetaNum:=wparam;
+ inc(lparam);
+ result:=lparam;
+ break;
+ end;
+ end;
+ end;
+end;
+
+function DoMeta(hContact:THANDLE):pointer;
+var
+ pw:pWideChar;
+ i:integer;
+begin
+ result:=nil;
+ for i:=0 to HIGH(FlagBuf) do
+ begin
+ with FlagBuf[i] do
+ begin
+ if contact=hContact then
+ begin
+ if (flags and QSF_META)<>0 then // adding new meta count
+ begin
+ if wparam=0 then
+ begin
+ inc(LastMeta);
+ wparam:=LastMeta;
+// lparam:=0;
+ end;
+ end
+ else if (flags and QSF_SUBMETA)<>0 then
+ begin
+ lparam:=FindMeta(CallService(MS_MC_GETMETACONTACT,hContact,0),wparam);
+ end;
+
+ if wparam>0 then
+ begin
+ mGetMem(result,32);
+ pw:=result;
+ pw[0]:='[';
+ IntToStr(pw+1,wparam,3);
+ pw[4]:=']';
+ if lparam>0 then
+ begin
+ pw[5]:=' ';
+ IntToStr(pw+6,lparam);
+ end
+ else
+ pw[5]:=#0;
+ end;
+ break;
+ end;
+ end;
+ end;
+end;
+
+procedure LoadOneItem(hContact:THANDLE;num:integer;proto:integer; var res:tQSRec);
+var
+ cni:TCONTACTINFO;
+ dbei:TDBEVENTINFO;
+ hDbEvent:cardinal;
+ tmp:int_ptr;
+ protov:PAnsiChar;
+begin
+ FillChar(res,SizeOf(tQSRec),0);
+ res.data:=dword(-1);
+ res.text:=nil;
+ with qsopt.columns[num] do
+ begin
+
+ if module_name<>nil then
+ protov:=module_name
+ else
+ protov:=GetProtoName(proto);
+
+ case setting_type of
+ ST_METACONTACT: begin
+ res.text:=DoMeta(hContact);
+ end;
+
+ ST_SCRIPT: begin
+ res.text:=ParseVarString(wparam.w,hContact);
+ end;
+
+ ST_SERVICE: begin
+ if wparam._type=ptCurrent then wparam.n:=hContact;
+ if lparam._type=ptCurrent then lparam.n:=hContact;
+ tmp:=CallService(protov,wparam.n,lparam.n);
+ if int_ptr(tmp)=int_ptr(CALLSERVICE_NOTFOUND) then exit;
+ case setting_cnftype of
+ ptString: begin
+ AnsiToWide(PAnsiChar(tmp),res.text);
+ end;
+ ptUnicode: begin
+ StrDupW(res.text,PWideChar(tmp));
+ end;
+ ptNumber,ptInteger:begin
+ res.data:=tmp;
+ res.text:=int2strw(tmp);
+ end;
+ end;
+ end;
+
+ ST_CONTACTINFO: begin
+ FillChar(cni,SizeOf(cni),0);
+ cni.cbSize :=sizeof(cni);
+ cni.dwFlag:=setting_cnftype or CNF_UNICODE;
+ cni.hContact:=hContact;
+ cni.szProto :=GetProtoName(proto);
+ if CallService(MS_CONTACT_GETCONTACTINFO,0,tlparam(@cni))=0 then
+ begin
+ case cni._type of
+ CNFT_ASCIIZ: begin
+ if cni.retval.szVal.w<>nil then
+ begin
+ StrDupW(res.text,cni.retval.szVal.w);
+ mir_free(cni.retval.szVal.w);
+ end;
+ exit;
+ end;
+ CNFT_BYTE :begin
+ res.data:=cni.retval.bVal;
+ if setting_cnftype=CNF_GENDER then
+ begin
+ if not (res.data in [70,77]) then
+ res.data:=DBReadByte(hContact,'UserInfo','Gender',0);
+ exit;
+ end
+ end;
+ CNFT_WORD :res.data:=cni.retval.wVal;
+ CNFT_DWORD:res.data:=cni.retval.dVal;
+ end;
+ res.text:=int2strw(res.data);
+ end;
+ end;
+
+ ST_STRING: begin
+ res.text:=DBReadUnicode(hContact,protov,wparam.a,nil)
+ end;
+
+ ST_BYTE: begin
+ res.data:=DBReadByte(hContact,protov,wparam.a,0);
+ res.text:=int2strw(res.data);
+ end;
+
+ ST_WORD: begin
+ res.data:=DBReadWord(hContact,protov,wparam.a,0);
+ res.text:=int2strw(res.data);
+ end;
+
+ ST_INT: begin
+ if (module_name=nil) and (wparam.a=nil) then
+ begin
+ res.data:=hContact;
+ res.text:=int2hexw(res.data);
+ end
+ else
+ begin
+ res.data:=DBReadDWord(hContact,protov,wparam.a,0);
+ res.text:=int2strw(res.data);
+ end;
+ end;
+
+ ST_LASTSEEN: begin
+ res.data:=BuildLastSeenTimeInt(hContact,protov);
+ res.text:=BuildLastSeenTime (hContact,protov);
+ end;
+
+ ST_IP: begin
+ res.data:=DBReadDWord(hContact,protov,wparam.a,0);
+ res.text:=IPtoStr(res.data);
+ end;
+
+ ST_TIMESTAMP: begin
+ res.data:=DBReadDWord(hContact,protov,wparam.a,0);
+ if res.data<>0 then
+ res.text:=TimeToStrW(res.data);
+ end;
+
+ ST_LASTEVENT: begin
+ hDbEvent:=CallService(MS_DB_EVENT_FINDLAST,hContact,0);
+ if hDbEvent<>0 then
+ begin
+ ZeroMemory(@dbei,sizeof(dbei));
+ dbei.cbSize:=SizeOf(dbei);
+ CallService(MS_DB_EVENT_GET,hDbEvent,tlparam(@dbei));
+ res.data:=dbei.timestamp;
+ res.text:=TimeToStrW(res.data);
+ end
+ else
+ res.data:=0;
+ end;
+ end;
+ end;
+end;
+
+function CompareItem(lParam1,lParam2:LPARAM;SortType:LPARAM):int; stdcall;
+var
+ typ1,typ2:boolean;
+ res1,res2:pQSRec;
+ i1,i2:uint_ptr;
+begin
+ result:=0;
+ if SortType=StatusSort then //sort by status
+ begin
+ i1:=FlagBuf[lParam1].status;
+ i2:=FlagBuf[lParam2].status;
+ // offline - to the end
+ if i1=ID_STATUS_OFFLINE then i1:=ID_STATUS_OFFLINE+64;
+ if i2=ID_STATUS_OFFLINE then i2:=ID_STATUS_OFFLINE+64;
+ // not string parameters
+ typ1:=false;
+ typ2:=false;
+ end
+ else
+ begin
+ res1:=@MainBuf[lParam1,SortType];
+ res2:=@MainBuf[lParam2,SortType];
+ i1 := res1^.data;
+ i2 := res2^.data;
+ typ1:=i1=uint_ptr(-1);
+ typ2:=i2=uint_ptr(-1);
+
+ if (typ1 and typ2) then // string & string
+ begin
+ if (res2.text=nil) and (res1.text=nil) then // nil
+ result:=0
+ else if res2.text=nil then
+ result:=1
+ else if res1.text=nil then
+ result:=-1
+ else
+ result:=lstrcmpiw(res1.text,res2.text);
+ end
+ else if typ1 or typ2 then // string & num
+ begin
+ if typ1 then
+ result:=1
+ else
+ result:=-1;
+ end;
+ end;
+ if not (typ1 or typ2) then // not strings
+ begin
+ if i1>i2 then
+ result:=1
+ else if i1<i2 then
+ result:=-1
+ else
+ result:=0;
+ end;
+ if not qsopt.ascendsort then
+ result:=-result;
+end;
+
+function FindBufNumber(hContact:THANDLE):integer;
+var
+ i:integer;
+begin
+ for i:=0 to HIGH(FlagBuf) do
+ begin
+ if FlagBuf[i].contact=hContact then
+ begin
+ result:=i;
+ exit;
+ end;
+ end;
+ result:=-1;
+end;
+
+function FindItem(num:integer):integer;
+var
+ fi:LV_FINDINFO;
+begin
+ if num>=0 then
+ begin
+ FillChar(fi,SizeOf(fi),0);
+ fi.flags :=LVFI_PARAM;
+ fi.lParam:=num;
+ result:=SendMessage(grid,LVM_FINDITEM,wparam(-1),lparam(@fi));
+ end
+ else
+ result:=num;
+end;
+
+procedure AddContactToList(hContact:THANDLE;num:integer);
+var
+ i:integer;
+ li:LV_ITEMW;
+begin
+ FillChar(li,SizeOf(li),0);
+ li.iItem :=100000; //!! need append
+ li.mask :=LVIF_IMAGE or LVIF_PARAM;
+ li.iImage:=CallService(MS_CLIST_GETCONTACTICON,hContact,0);
+ li.lParam:=num;
+ li.iItem :=SendMessageW(grid,LVM_INSERTITEMW,0,lparam(@li));
+
+ li.iImage:=0;
+ for i:=0 to qsopt.numcolumns-1 do
+ begin
+ if (qsopt.columns[i].flags and COL_ON)<>0 then
+ begin
+ // Client icons preprocess
+ li.pszText :=MainBuf[num,i].text;
+ if (((qsopt.columns[i].flags and COL_CLIENT)<>0) and
+ (li.pszText<>NIL) and qsopt.showclienticons) OR
+ ((qsopt.columns[i].flags and (COL_XSTATUS or COL_GENDER))<>0) then
+ li.mask:=LVIF_IMAGE or LVIF_TEXT
+ else
+ li.mask:=LVIF_TEXT;
+ li.iSubItem:=colorder[i];
+ SendMessageW(grid,LVM_SETITEMW,0,lparam(@li));
+ end;
+ end;
+end;
+
+type
+ pSBDataRecord = ^tSBDataRecord;
+ tSBDataRecord = record
+ flags :cardinal;
+ total :cardinal; // in clist
+ found :cardinal; // by pattern
+ online:cardinal; // clist online
+ liston:cardinal; // pattern online
+ end;
+ tSBData = array [0..63] of tSBDataRecord;
+
+procedure DrawSBW(const SBData:tSBData);
+var
+ aPartPos:array [0..63 ] of integer;
+ buf :array [0..255] of WideChar;
+ fmtstr :array [0..255] of WideChar;
+ all:integer;
+ i,j:integer;
+ p,pc,po,pd,poff,pa:PWideChar;
+ rc:TRECT;
+ dc:HDC;
+ icon:HICON;
+ protocnt:integer;
+begin
+ p:=@buf;
+ p:=StrEndW(IntToStr(p,SBData[0].found));
+ p:=StrCopyEW(p,TranslateW(' users found ('));
+ p:=StrEndW(IntToStr(p,Length(FlagBuf)));
+ p:=StrCopyEW(p,TranslateW(') Online: '));
+ IntToStr(p,SBData[0].online);
+
+ dc:=GetDC(StatusBar);
+ DrawTextW(dc,pWidechar(@buf),-1,rc,DT_CALCRECT);
+ ReleaseDC(StatusBar,dc);
+ all:=rc.right-rc.left;
+ aPartPos[0]:=all;
+ protocnt:=GetNumProto;
+ i:=1;
+ while i<=protocnt do
+ begin
+ inc(all,55);
+ aPartPos[i]:=all;
+ inc(i);
+ end;
+ aPartPos[i]:=-1;
+ SendMessageW(StatusBar,SB_SETPARTS,protocnt+2,lparam(@aPartPos));
+ SendMessageW(StatusBar,SB_SETTEXTW,0,lparam(@buf));
+
+ po :=TranslateW('Online');
+ pd :=TranslateW('deleted');
+ poff:=TranslateW('off');
+ pa :=TranslateW('active');
+
+ for i:=1 to protocnt do
+ begin
+ if ((SBData[i].flags and (QSF_ACCDEL or QSF_ACCOFF))<>0) then
+ begin
+ icon:=CallService(MS_SKIN_LOADPROTOICON,0,ID_STATUS_OFFLINE);
+ end
+ else
+ begin
+ icon:=CallService(
+ MS_SKIN_LOADPROTOICON,wparam(GetProtoName(i)),ID_STATUS_ONLINE);
+ end;
+
+ FastAnsiToWideBuf(GetProtoName(i),fmtstr);
+
+ SendMessageW(StatusBar,SB_SETICON,i,icon);
+
+ j:=High(buf);//(SizeOf(buf) div SizeOf(WideChar))-1;
+ buf[j]:=#0;
+
+ // fill by spaces
+ p:=@buf[0];
+ while j>0 do
+ begin
+ dec(j);
+ p^:=' ';
+ inc(p);
+ end;
+
+ if (SBData[i].flags and QSF_ACCDEL)<>0 then
+ begin
+ buf [0]:='!';
+ pc:=pd;
+ end
+ else if (SBData[i].flags and QSF_ACCOFF)<>0 then
+ begin
+ buf [0]:='?';
+ pc:=poff
+ end
+ else
+ pc:=pa;
+
+ IntToStr(pWideChar(@buf[2]),SBData[i].found);
+ StrEndW(buf)^:=' ';
+ SendMessageW(StatusBar,SB_SETTEXTW,i,lparam(@buf));
+
+// create tooltip
+ p:=@buf;
+ p:=StrCopyEW(p,fmtstr); // Protocol
+ p^:=' '; inc(p);
+ p^:='('; inc(p);
+ p:=StrCopyEW(p,pc); // Protocol status
+ p^:=')'; inc(p);
+ p^:=':'; inc(p);
+ p^:=' '; inc(p);
+
+ with SBData[i] do
+ begin
+ p:=StrEndW(IntToStr(p,found));
+ p^:=' '; inc(p);
+ p^:='('; inc(p);
+ p:=StrEndW(IntToStr(p,total));
+ p^:=')'; inc(p);
+ p^:=';'; inc(p);
+ p^:=' '; inc(p);
+ p:=StrCopyEW(p,po);
+ p^:=' '; inc(p);
+ p:=StrEndW(IntToStr(p,liston));
+ p^:=' '; inc(p);
+ p^:='('; inc(p);
+ p:=StrEndW(IntToStr(p,online));
+ p^:=')'; inc(p);
+ end;
+ p^:=#0;
+ SendMessageW(StatusBar,SB_SETTIPTEXTW,i,lparam(@buf));
+ end;
+
+end;
+
+procedure UpdateSB;
+var
+ SBData: tSBData;
+ j:integer;
+ p:pSBDataRecord;
+begin
+ FillChar(SBData,SizeOf(SBData),0);
+
+ // for all contacts
+ for j:=0 to HIGH(FlagBuf) do
+ begin
+ p:=@SBData[FlagBuf[j].proto];
+ p^.flags:=FlagBuf[j].flags;
+
+ inc(p^.total);
+
+ if (p^.flags and QSF_ACTIVE)<>0 then
+ begin
+ inc(p^.found);
+ inc(SBData[0].found);
+ end;
+
+ if FlagBuf[j].status<>ID_STATUS_OFFLINE then
+ begin
+ inc(p^.online);
+ inc(SBData[0].online);
+ if (p^.flags and QSF_ACTIVE)<>0 then
+ begin
+ inc(p^.liston);
+ inc(SBData[0].liston);
+ end;
+ end;
+
+ end;
+
+ DrawSBW(SBData);
+end;
+
+procedure Sort;
+begin
+ if qsopt.columnsort>=tablecolumns then
+ qsopt.columnsort:=StatusSort;
+
+ SendMessage(grid,LVM_SORTITEMS,GetQSColumn(qsopt.columnsort),LPARAM(@CompareItem));
+// ListView_SortItems(grid,@CompareItem,GetQSColumn(qsopt.columnsort));
+
+ if (qsopt.columnsort<>StatusSort) and qsopt.sortbystatus then
+ SendMessage(grid,LVM_SORTITEMS,StatusSort,LPARAM(@CompareItem));
+// ListView_SortItems(grid,@CompareItem,StatusSort);
+end;
+
+procedure MakePatternW;
+var
+ wasquote:bool;
+ lpatptr:PWideChar;
+begin
+ numpattern:=0;
+ mFreeMem(patstr);
+ if (pattern<>nil) and (pattern^<>#0) then
+ begin
+ wasquote:=false;
+ StrDupW(patstr,pattern);
+ lpatptr:=patstr;
+ repeat
+ while lpatptr^=' ' do inc(lpatptr);
+ if lpatptr^<>#0 then
+ begin
+ if lpatptr^='"' then
+ begin
+ inc(lpatptr);
+ wasquote:=true;
+ end
+ else
+ begin
+ patterns[numpattern].str:=lpatptr;
+ inc(numpattern);
+ while lpatptr^<>#0 do
+ begin
+ if wasquote then
+ begin
+ if lpatptr^='"' then
+ begin
+ wasquote:=false;
+ break;
+ end;
+ end
+ else if lpatptr^=' ' then
+ break;
+ inc(lpatptr);
+ end;
+ if lpatptr^<>#0 then
+ begin
+ lpatptr^:=#0;
+ inc(lpatptr);
+ end;
+ end;
+ if numpattern=maxpattern then break;
+ end;
+ until lpatptr^=#0;
+ end;
+end;
+
+function CheckPatternW(cnt:integer):boolean;
+var
+ lstr:array [0..1023] of WideChar;
+ i,j:integer;
+begin
+ if numpattern>0 then
+ begin
+ for i:=0 to numpattern-1 do
+ patterns[i].res:=false;
+
+ for i:=0 to qsopt.numcolumns-1 do
+ begin
+ if ((qsopt.columns[i].flags and COL_ON)<>0) and
+ (MainBuf[cnt,i].text<>nil) then
+ begin
+ StrCopyW(lstr,MainBuf[cnt,i].text,HIGH(lstr));
+ CharLowerW(lstr);
+ for j:=0 to numpattern-1 do
+ if not patterns[j].res then
+ begin
+ if StrPosW(lstr,patterns[j].str)<>nil then //!!
+ patterns[j].res:=true;
+ end;
+ end;
+ end;
+
+ result:=true;
+ for i:=0 to numpattern-1 do
+ result:=result and patterns[i].res;
+ end
+ else
+ result:=true;
+end;
+
+procedure ProcessLine(num:integer;test:boolean=true);
+var
+ p:pQSFRec;
+ l:boolean;
+begin
+ p:=@FlagBuf[num];
+ if (p^.flags and QSF_DELETED)<>0 then
+ exit;
+
+ if qsopt.showonlyinlist then
+ begin
+ if (p^.flags and QSF_INLIST)=0 then
+ exit;
+ end;
+
+ if test then
+ begin
+ l:=CheckPatternW(num);
+ if l then
+ p^.flags:=p^.flags or QSF_PATTERN
+ else
+ p^.flags:=p^.flags and not QSF_PATTERN;
+ end
+ else
+ l:=(p^.flags and QSF_PATTERN)<>0;//true;
+
+ if l then
+ begin
+ if (p^.flags and QSF_ACTIVE)=0 then
+ begin
+ if (qsopt.showoffline) or (p^.status<>ID_STATUS_OFFLINE) then
+ begin
+ p^.flags:=p^.flags or QSF_ACTIVE;
+ AddContactToList(p^.contact,num);
+ end;
+ end
+ end
+ else
+ begin
+ if (p^.flags and QSF_ACTIVE)<>0 then
+ begin
+ p^.flags:=p^.flags and not QSF_ACTIVE;
+ ListView_DeleteItem(grid,FindItem(num));
+ end;
+ end;
+end;
+
+function AdvancedFilter:integer;
+var
+ p:pQSFRec;
+ i:integer;
+ show:boolean;
+begin
+ result:=0;
+
+ SendMessage(grid,WM_SETREDRAW,0,0);
+
+ for i:=0 to HIGH(FlagBuf) do
+ begin
+ p:=@FlagBuf[i];
+ // firstly = proto
+ show:=(LoByte(AdvFilter)=0) or (p^.proto=LoByte(AdvFilter));
+ // secondary = show/hide offline
+ show:=show and ((p^.status<>ID_STATUS_OFFLINE) or ((AdvFilter and flt_show_offline)<>0));
+
+ if (p^.flags and QSF_PATTERN)<>0 then
+ begin
+ if show then
+ begin
+ if (p^.flags and QSF_ACTIVE)=0 then
+ ProcessLine(i,false);
+ end
+ else
+ begin
+ p^.flags:=p^.flags and not QSF_ACTIVE;
+ ListView_DeleteItem(grid,FindItem(i));
+ end;
+ end;
+ end;
+
+ SendMessage(grid,WM_SETREDRAW,1,0);
+ InvalidateRect(grid,nil,false);
+
+ Sort;
+ UpdateSB;
+end;
+
+procedure FillGrid;
+var
+ cnt:integer;
+begin
+
+ SendMessage(grid,WM_SETREDRAW,0,0);
+
+ MakePatternW;
+
+ for cnt:=0 to HIGH(FlagBuf) do
+ ProcessLine(cnt);
+
+ SendMessage(grid,WM_SETREDRAW,1,0);
+ InvalidateRect(grid,nil,false);
+
+ Sort;
+ UpdateSB;
+
+ AdvancedFilter; //!!
+
+ ListView_SetItemState(grid,0,LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+end;
+
+procedure AddContact(num:integer;hContact:THANDLE);
+var
+ i:integer;
+ tmpstr:array [0..63] of AnsiChar;
+begin
+ FillChar(FlagBuf[num],SizeOf(tQSFRec),0);
+ with FlagBuf[num] do
+ begin
+ contact:=hContact;
+ flags :=0;
+ i:=IsContactActive(hContact,tmpstr);
+ proto:=FindProto(tmpstr);
+
+ case i of
+ -2: flags:=flags or QSF_ACCDEL; // deleted account
+ -1: flags:=flags or QSF_ACCOFF; // disabled account
+// 0 : ; // hidden contact
+ 1 : flags:=flags or QSF_META; // metacontact
+ 2 : flags:=flags or QSF_SUBMETA; // subMetacontact
+ end;
+ if i>0 then
+ flags:=flags or QSF_INLIST; // normal contact
+
+ if (proto=0) or (i<0) then
+ status:=ID_STATUS_OFFLINE
+ else
+ status:=DBReadWord(contact,GetProtoName(proto),'Status',ID_STATUS_OFFLINE);
+
+ for i:=0 to qsopt.numcolumns-1 do
+ if (qsopt.columns[i].flags and COL_ON)<>0 then
+ LoadOneItem(contact,i,proto,MainBuf[num,i]);
+ end;
+
+end;
+
+function PrepareToFill:boolean;
+var
+ cnt,cnt1:integer;
+ hContact:THANDLE;
+ i:integer;
+begin
+ result:=false;
+ if qsopt.numcolumns=0 then
+ exit;
+ // calculating contacts
+ cnt:=CallService(MS_DB_CONTACT_GETCOUNT,0,0);
+ if cnt=0 then
+ exit;
+
+ result:=true;
+ // Allocate mem
+ SetLength(MainBuf,cnt,qsopt.numcolumns);
+ SetLength(FlagBuf,cnt);
+
+ // filling buffer
+ LastMeta:=0;
+ cnt1:=0;
+ hContact:=CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ while hContact<>0 do
+ begin
+ //!! check account
+ AddContact(cnt1,hContact);
+ inc(cnt1);
+ if cnt1=cnt then break; // additional checking
+ hContact:=CallService(MS_DB_CONTACT_FINDNEXT,hContact,0);
+ end;
+ if cnt1<>cnt then
+ begin
+ SetLength(MainBuf,cnt1);
+ SetLength(FlagBuf,cnt1);
+ end;
+
+ SetLength(colorder,qsopt.numcolumns);
+ cnt:=0;
+ for i:=0 to qsopt.numcolumns-1 do
+ begin
+ if (qsopt.columns[i].flags and COL_ON)<>0 then
+ begin
+ colorder[i]:=cnt;
+ inc(cnt);
+ qsopt.columns[i].flags := qsopt.columns[i].flags or COL_INIT;
+ end
+ else
+ colorder[i]:=-1;
+ end;
+
+end;
+
+function GetFocusedhContact:THANDLE;
+var
+ i:integer;
+begin
+ i:=LV_GetLParam(grid);
+ if i=-1 then
+ result:=0
+ else
+ result:=FlagBuf[i].contact;
+end;
+
+procedure ShowContactMsgDlg(hContact:THANDLE);
+begin
+ if hContact<>0 then
+ begin
+ ShowContactDialog(hContact);
+ if qsopt.closeafteraction then DestroyWindow(mainwnd);
+ end;
+end;
+
+procedure DeleteOneContact(hContact:THANDLE);
+begin
+ if ServiceExists(strCListDel)>0 then
+ CallService(strCListDel,hContact,0)
+ else
+ CallService(MS_DB_CONTACT_DELETE,hContact,0);
+end;
+
+procedure DeleteByList;
+var
+ i,j:integer;
+begin
+ j:=ListView_GetItemCount(grid)-1;
+
+ i:=MessageBoxW(0,TranslateW('Do you really want to delete selected contacts?'),
+ TranslateW('Warning'),MB_OKCANCEL+MB_ICONWARNING);
+
+ if i=IDOK then
+ begin
+ SendMessage(grid,WM_SETREDRAW,0,0);
+ for i:=j downto 0 do
+ begin
+ if ListView_GetItemState(grid,i,LVIS_SELECTED)<>0 then
+ CallService(MS_DB_CONTACT_DELETE,FlagBuf[LV_GetLParam(grid,i)].contact,0);
+ end;
+ SendMessage(grid,WM_SETREDRAW,1,0);
+ end;
+end;
+
+procedure ConvertToMeta;
+var
+ i,j:integer;
+ hMeta:THANDLE;
+ tmp:THANDLE;
+begin
+ j:=ListView_GetItemCount(grid)-1;
+
+ hMeta:=0;
+ for i:=j downto 0 do // check
+ begin
+ if ListView_GetItemState(grid,i,LVIS_SELECTED)<>0 then
+ begin
+ tmp:=CallService(MS_MC_GETMETACONTACT,FlagBuf[LV_GetLParam(grid,i)].contact,0);
+ if tmp<>0 then
+ if hMeta=0 then
+ hMeta:=tmp
+ else if tmp<>hMeta then
+ begin
+ MessageBoxW(0,
+ TranslateW('Some of selected contacts in different metacontacts already'),
+ 'Quick Search',MB_ICONERROR);
+ exit;
+ end;
+ end;
+ end;
+
+ if hMeta<>0 then
+ begin
+ i:=MessageBoxW(0,
+ TranslateW('One or more contacts in same Meta already. Try to convert anyway?'),
+ 'Quick Search',MB_YESNO+MB_ICONWARNING);
+ if i<>IDYES then
+ exit;
+ end;
+
+ // convert if needed
+ for i:=j downto 0 do
+ begin
+ if ListView_GetItemState(grid,i,LVIS_SELECTED)<>0 then
+ begin
+ if hMeta=0 then
+ hMeta:=CallService(MS_MC_CONVERTTOMETA,FlagBuf[LV_GetLParam(grid,i)].contact,0)
+ else
+ CallService(MS_MC_ADDTOMETA,FlagBuf[LV_GetLParam(grid,i)].contact,hMeta);
+ end;
+ end;
+end;
+
+procedure MoveToGroup(group:PWideChar);
+var
+ i,j,grcol:integer;
+ contact:THANDLE;
+begin
+ j:=ListView_GetItemCount(grid)-1;
+ grcol:=-1;
+ for i:=0 to qsopt.numcolumns-1 do
+ begin
+ with qsopt.columns[i] do
+ if (setting_type=ST_STRING) and
+ (StrCmp(module_name,'CList')=0) and
+ (StrCmp(wparam.a ,'Group')=0) then
+ begin
+ if (flags and COL_ON)=0 then
+ flags:=flags and not COL_INIT
+ else
+ grcol:=i;
+ break;
+ end
+ end;
+ for i:=0 to j do
+ begin
+ if ListView_GetItemState(grid,i,LVIS_SELECTED)<>0 then
+ begin
+ contact:=FlagBuf[LV_GetLParam(grid,i)].contact;
+ DBWriteUnicode(contact,strCList,'Group',group);
+ if (not qsopt.closeafteraction) and (grcol>=0) then
+ begin
+ LoadOneItem(contact,grcol,0,MainBuf[i,grcol]);
+ end;
+ end;
+ end;
+ if (not qsopt.closeafteraction) and (grcol>=0) then
+ FillGrid;
+end;
+
+function IsColumnMinimized(num:integer):bool;
+begin
+ result:=ListView_GetColumnWidth(grid,num)<=10;
+end;
+
+procedure CopyMultiLinesW(num:integer);
+var
+ i,j,k:integer;
+ p,buf:PWideChar;
+ tmpcnt,cnt:integer;
+begin
+ cnt:=0;
+ if qsopt.exportheaders then
+ begin
+ k:=0;
+ while k<qsopt.numcolumns do
+ begin
+ if not (qsopt.skipminimized and IsColumnMinimized(k)) then
+ inc(cnt,StrLenW(TranslateW(qsopt.columns[k].title))+1);
+ Inc(k);
+ end;
+ if cnt>0 then
+ inc(cnt,2);
+ end;
+ j:=ListView_GetItemCount(grid)-1;
+ tmpcnt:=cnt;
+ for i:=0 to j do
+ begin
+ if ListView_GetItemState(grid,i,LVIS_SELECTED)<>0 then
+ begin
+ k:=0;
+ num:=LV_GetLParam(grid,i);
+ while k<qsopt.numcolumns do
+ begin
+ if not (qsopt.skipminimized and IsColumnMinimized(k)) then
+ inc(cnt,StrLenW(MainBuf[num,k].text)+1);
+ Inc(k);
+ end;
+ end;
+ if tmpcnt<>cnt then
+ inc(cnt,2);
+ end;
+ if cnt=0 then
+ exit;
+
+ inc(cnt);
+ mGetMem(buf,cnt*SizeOf(WideChar));
+ p:=buf;
+
+ if qsopt.exportheaders then
+ begin
+ k:=0;
+ while k<qsopt.numcolumns do
+ begin
+ if not (qsopt.skipminimized and IsColumnMinimized(k)) then
+ begin
+ StrCopyW(p,TranslateW(qsopt.columns[k].title));
+ p:=StrEndW(p);
+ p^:=#9;
+ inc(p);
+ end;
+ inc(k);
+ end;
+ (p-1)^:=#13;
+ p^ :=#10;
+ inc(p);
+ end;
+ for i:=0 to j do
+ begin
+ if ListView_GetItemState(grid,i,LVIS_SELECTED)<>0 then
+ begin
+ k:=0;
+ num:=LV_GetLParam(grid,i);
+ while k<qsopt.numcolumns do
+ begin
+ if not (qsopt.skipminimized and IsColumnMinimized(k)) then
+ begin
+ StrCopyW(p,MainBuf[num,k].text);
+ p:=StrEndW(p);
+ p^:=#9;
+ inc(p);
+ end;
+ inc(k);
+ end;
+ (p-1)^:=#13;
+ p^ :=#10;
+ inc(p);
+ end;
+ end;
+ p^:=#0;
+ CopyToClipboard(buf,false);
+ mFreeMem(buf);
+end;
+
+var
+ HintWnd:HWND;
+
+function NewLVProc(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+const
+ OldHItem :integer=0;
+ OldHSubItem:integer=0;
+var
+ buf :array [0..255] of WideChar; //!! for spec columns and patterns now only
+ buf1:array [0..127] of AnsiChar;
+ p,pp:PWideChar;
+ i,num,cnt:integer;
+ pinfo:LV_HITTESTINFO;
+ TI:TToolInfoW;
+ ics:TICQ_CUSTOM_STATUS;
+
+ info:TCLCINFOTIP;
+// qsr:tQSRec;
+ tmpCursor:TPOINT;
+begin
+ result:=0;
+ case hMessage of
+
+ WM_DESTROY: begin
+ if TTInstalled then
+ KillTimer(Dialog,TIMERID_HOVER);
+ end;
+
+ WM_LBUTTONDBLCLK: begin
+ ShowContactMsgDlg(GetFocusedhContact);
+ exit;
+ end;
+
+ WM_CHAR: begin
+ if wParam=27 then // ESC
+ begin
+ PostMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDCANCEL,0);
+ exit;
+ end;
+ case wParam of
+ 1: begin
+ ListView_SetItemState(grid,-1,LVIS_SELECTED,LVIS_SELECTED);
+ end;
+ // Ctrl-C
+ 3: begin
+ i:=ListView_GetSelectedCount(grid);
+ if (i>1) or qsopt.singlecsv then
+ begin
+ CopyMultiLinesW(i);
+ exit;
+ end;
+
+ cnt:=0;
+ num:=LV_GetLParam(grid,ListView_GetNextItem(grid,-1,LVNI_FOCUSED));
+ i:=0;
+ while i<qsopt.numcolumns do
+ begin
+ if not (qsopt.skipminimized and IsColumnMinimized(i)) then
+ begin
+ p:=TranslateW(qsopt.columns[i].title);
+ inc(cnt,StrLenW(p)+1);
+ if (StrEndW(p)-1)^<>':' then
+ inc(cnt);
+
+ inc(cnt,StrLenW(MainBuf[num,i].text)+2);
+ end;
+ end;
+ if cnt=0 then
+ exit;
+ mGetMem(pp,(cnt+1)*SizeOf(WideChar));
+ p:=pp;
+
+ i:=0;
+ while i<qsopt.numcolumns do
+ begin
+ if not (qsopt.skipminimized and IsColumnMinimized(i)) then
+ begin
+ StrCopyW(p,TranslateW(qsopt.columns[i].title));
+ p:=StrEndW(p);
+ if (p-1)^<>':' then
+ begin
+ p^:=':';
+ inc(p);
+ end;
+ p^:=' '; inc(p);
+ StrCopyW(p,MainBuf[num,i].text);
+ p:=StrEndW(p);
+ p^:=#13; (p+1)^:=#10; inc(p,2);
+ end;
+ end;
+ p^:=#0;
+
+ CopyToClipboard(pp,false);
+ mFreeMem(pp);
+ exit;
+ end;
+ // backspace
+ 8: begin
+ if pattern<>nil then
+ begin
+ StrCopyW(buf,pattern);
+ p:=StrEndW(buf);
+ (p-1)^:=#0;
+ SetDlgItemTextW(mainwnd,IDC_E_SEARCHTEXT,buf);
+ end;
+ end;
+ // letters
+ 32..127: begin
+ if pattern<>nil then
+ StrCopyW(buf,pattern)
+ else
+ buf[0]:=#0;
+ p:=StrEndW(buf);
+ p^:=WideChar(wParam);
+ (p+1)^:=#0;
+ SetDlgItemTextW(mainwnd,IDC_E_SEARCHTEXT,buf);
+ end;
+ end
+ end;
+
+ WM_TIMER: begin
+ if wParam=TIMERID_HOVER then
+ begin
+ KillTimer(Dialog,TIMERID_HOVER);
+ if GetForegroundWindow<>mainwnd then exit;
+ i:=LV_GetLParam(grid,OldHItem);
+ FillChar(info,SizeOf(info),0);
+ with info do
+ begin
+ cbSize :=SizeOf(info);
+ hItem :=FlagBuf[i].contact;
+ GetCursorPos(ptCursor);
+ tmpCursor :=ptCursor;
+{
+ ptCursor.x:=loword(lParam);
+ ptCursor.y:=hiword(lParam);
+}
+ SendMessage(grid,LVM_GETITEMRECT,OldHItem,tlparam(@rcItem));
+ ScreenToClient(grid,tmpCursor);
+ if not PtInRect(rcItem,tmpCursor) then exit;
+ end;
+// mGetMem(txt,16384*SizeOf(WideChar));
+{
+ p:=txt;
+ for cnt:=0 to HIGH(MainBuf[0]) do
+ begin
+ if (qsopt.columns[cnt].flags and COL_ON)=0 then
+ begin
+ LoadOneItem(info.hItem,cnt,FlagBuf[i].proto,qsr);
+ if qsr.text<>nil then
+ begin
+ if qsr.text^<>#0 then
+ begin
+//!! need: buffer free space check here
+num:=StrLenW(qsopt.columns[cnt].title)+StrLenW(qsr.text)+4;
+if (16384-num)>(p-txt) then
+begin
+
+ p:=StrCopyEW(p,qsopt.columns[cnt].title);
+ p^:=':'; inc(p); p^:=' '; inc(p);
+ p:=StrCopyEW(p,qsr.text);
+ p^:=#13; inc(p); p^:=#10; inc(p);
+end
+else
+begin
+ mFreeMem(qsr.text);
+ break;
+end;
+ end;
+ mFreeMem(qsr.text);
+ end;
+ end;
+ end;
+ p^:=#0;
+}
+ CallService(MS_TIPPER_SHOWTIPW,0{dword(txt)},tlparam(@info));
+// mFreeMem(txt);
+ TTShowed:=true;
+ end;
+ end;
+
+ WM_MOUSEMOVE: begin
+ pinfo.pt.x:=loword(lParam);
+ pinfo.pt.y:=hiword(lParam);
+ pinfo.flags:=0;
+ if integer(SendMessage(grid,LVM_SUBITEMHITTEST,0,tlparam(@pinfo)))<>-1 then
+ begin
+ if (pinfo.iItem<>OldHItem) or (pinfo.iSubItem<>OldHSubItem) then
+ begin
+ OldHSubItem:=pinfo.iSubItem;
+ OldHItem :=pinfo.iItem;
+
+ if TTInstalled then
+ begin
+ if TTShowed then
+ begin
+ TTShowed:=false;
+ CallService(MS_TIPPER_HIDETIP,0,0);
+ end;
+ KillTimer(Dialog, TIMERID_HOVER);
+ if OldHSubItem=0 then
+ begin
+ SetTimer(Dialog, TIMERID_HOVER, 450, nil);
+ exit;
+ end;
+ end;
+//!!
+ with TI do
+ begin
+ cbSize:=SizeOf(TI);
+ uFlags:=TTF_SUBCLASS+TTF_IDISHWND;
+ hWnd :=mainwnd;
+ uId :=Dialog;
+ hInst :=0;
+ end;
+
+ OldHSubItem:=GetQSColumn(OldHSubItem);
+ if (qsopt.columns[OldHSubItem].flags and
+ (COL_XSTATUS or COL_GENDER))<>0 then
+ begin
+ i:=LV_GetLParam(grid,OldHItem);
+// TTShowed:=true;
+ if (qsopt.columns[OldHSubItem].flags and COL_GENDER)<>0 then
+ begin
+ case MainBuf[i,OldHSubItem].data of
+ 77: TI.lpszText:=tstrMale;
+ 70: TI.lpszText:=tstrFemale;
+ else
+ TI.lpszText:=tstrUnknown;
+ end;
+ end
+ else // if (qsopt.columns[OldHSubItem].flags and COL_XSTATUS)<>0 then
+ begin
+ StrCopyW(buf,MainBuf[i,OldHSubItem].text);
+ ics.flags:=CSSF_DEFAULT_NAME or CSSF_MASK_NAME or CSSF_UNICODE;
+
+ StrCopy(StrCopyE(buf1,GetProtoName(FlagBuf[i].proto)),PS_ICQ_GETCUSTOMSTATUSEX);
+
+ i:=StrToInt(buf);
+ ics.wParam:=@i;
+ ics.cbSize:=SizeOf(ics);
+ ics.szName.w:=@buf;
+
+ CallService(buf1,0,tlparam(@ics));
+ TI.lpszText:=TranslateW(@buf);
+ end;
+ end
+ else
+ begin
+ TI.lpszText:=nil;
+// TTShowed:=false;
+ end;
+ SendMessageW(HintWnd,TTM_SETTOOLINFOW,0,tlparam(@TI));
+ end
+ end;
+ end;
+
+ WM_KEYUP: begin
+ case wParam of
+ VK_RETURN: begin
+ if ListView_GetSelectedCount(grid)=1 then
+ ShowContactMsgDlg(GetFocusedhContact);
+ exit;
+ end;
+ VK_INSERT: begin
+ CallService(MS_FINDADDFINDADD,0,0);
+ exit;
+ end;
+ VK_DELETE: begin
+ lParam:=ListView_GetSelectedCount(grid);
+ if lParam>1 then
+ DeleteByList
+ else if lParam=1 then
+ DeleteOneContact(GetFocusedhContact);
+ exit;
+ end;
+ VK_F5: begin
+ PostMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDC_REFRESH,0);
+ exit;
+ end;
+ end;
+ end;
+
+ WM_NOTIFY: begin
+ if integer(PNMHdr(lParam)^.code)=HDN_ENDDRAG then
+ begin
+ ShiftColumns(PHDNotify(lParam)^.Item,PHDNotify(lParam)^.pitem^.iOrder);
+ end;
+ end;
+
+ end;
+ result:=CallWindowProc(OldLVProc,Dialog,hMessage,wParam,lParam);
+end;
+
+procedure ColumnClick(wnd:HWND;num:integer);
+var
+ hdi:THDITEM;
+ header:HWND;
+begin
+ zeromemory(@hdi,sizeof(hdi));
+ hdi.mask:=HDI_BITMAP or HDI_FORMAT;
+ hdi.fmt :=HDF_LEFT or HDF_STRING;
+ header:=ListView_GetHeader(wnd);
+ SendMessage(header,HDM_SETITEM,qsopt.columnsort,lparam(@hdi));
+
+ if qsopt.columnsort<>num then
+ begin
+ qsopt.ascendsort:=true;
+ qsopt.columnsort:=num;
+ end
+ else
+ qsopt.ascendsort:=not qsopt.ascendsort;
+
+ if qsopt.ascendsort then
+ hdi.hbm:=sortcoldn
+ else
+ hdi.hbm:=sortcolup;
+ hdi.fmt:=HDF_LEFT or HDF_BITMAP or HDF_BITMAP_ON_RIGHT or HDF_STRING;
+ Header_SetItem(header,num,hdi);
+
+ Sort;
+end;
+
+procedure MakeColumnMenu;
+var
+ menu:HMENU;
+ flag,id:integer;
+ pt:TPOINT;
+begin
+ menu:=CreatePopupMenu;
+ if menu<>0 then
+ begin
+ for id:=0 to qsopt.numcolumns-1 do
+ begin
+ if (qsopt.columns[id].flags and COL_ON)<>0 then
+ flag:=MF_CHECKED or MF_STRING
+ else
+ flag:=MF_UNCHECKED or MF_STRING;
+ AppendMenuW(menu,flag,100+id,TranslateW(qsopt.columns[id].title));
+ end;
+ GetCursorPos(pt);
+ id:=integer(TrackPopupMenu(menu,TPM_RETURNCMD+TPM_NONOTIFY,pt.x,pt.y,0,mainwnd,nil));
+ if id>100 then
+ begin
+ dec(id,100);
+ with qsopt.columns[id] do
+ begin
+ flags:=flags xor COL_ON;
+ if (flags and COL_ON)<>0 then
+ flag:=wcShow
+ else
+ flag:=wcHide;
+ end;
+ WndChangeColumns(flag,id);
+ OptChangeColumns(wcShow,id,ord(flag=wcShow));
+ end;
+ DestroyMenu(menu);
+ end;
+end;
+
+function NewLVHProc(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+begin
+ case hMessage of
+
+ WM_RBUTTONUP: begin
+ result:=0;
+ exit;
+ end;
+
+ WM_RBUTTONDOWN: begin
+ MakeColumnMenu;
+ end;
+ end;
+ result:=CallWindowProc(OldProc,Dialog,hMessage,wParam,lParam);
+end;
+
+procedure SetCellColor(lplvcd:PNMLVCUSTOMDRAW;idx:integer);
+begin
+ if qsopt.colorize then
+ begin
+ with FlagBuf[idx] do
+ begin
+ if (flags and QSF_ACCDEL)<>0 then
+ begin
+ lplvcd^.clrTextBk:=cbkg_del;
+ lplvcd^.clrText :=cfgr_del;
+ end
+ else if (flags and QSF_ACCOFF)<>0 then
+ begin
+ lplvcd^.clrTextBk:=cbkg_dis;
+ lplvcd^.clrText :=cfgr_dis;
+ end
+ else if (flags and QSF_META)<>0 then
+ begin
+ lplvcd^.clrTextBk:=cbkg_meta;
+ lplvcd^.clrText :=cfgr_meta;
+ end
+ else if (flags and QSF_SUBMETA)<>0 then
+ begin
+ lplvcd^.clrTextBk:=cbkg_sub;
+ lplvcd^.clrText :=cfgr_sub;
+ end
+ else if (flags and QSF_INLIST)=0 then
+ begin
+ lplvcd^.clrTextBk:=cbkg_hid;
+ lplvcd^.clrText :=cfgr_hid;
+ end
+ else
+ idx:=-1;
+ end;
+ end
+ else
+ idx:=-1;
+ if idx<0 then
+ begin
+ if (not qsopt.drawgrid) and odd(lplvcd^.nmcd.dwItemSpec) then
+ begin
+ lplvcd^.clrTextBk:=cbkg_odd;
+ lplvcd^.clrText :=cfgr_odd;
+ end
+ else
+ begin
+ lplvcd^.clrTextBk:=cbkg_norm;
+ lplvcd^.clrText :=cfgr_norm;
+ end;
+ end;
+end;
+
+function ProcessCustomDraw(lParam:LPARAM):integer;
+var
+ lplvcd:PNMLVCUSTOMDRAW;
+ rc:TRECT;
+ h:HICON;
+ buf:array [0..255] of AnsiChar;
+ i,j,sub:integer;
+begin
+ lplvcd:=pointer(lParam);
+ result:=CDRF_DODEFAULT;
+ case lplvcd^.nmcd.dwDrawStage of
+ CDDS_PREPAINT: begin
+ result:=CDRF_NOTIFYITEMDRAW;
+ exit;
+ end;
+ CDDS_ITEMPREPAINT: begin
+ result:=CDRF_NOTIFYSUBITEMDRAW;
+
+ SetCellColor(lplvcd,LV_GetLParam(grid,lplvcd^.nmcd.dwItemSpec));
+
+ exit;
+ end;
+ CDDS_SUBITEM or CDDS_ITEMPREPAINT: begin
+
+ i:=LV_GetLParam(grid,lplvcd^.nmcd.dwItemSpec);
+ SetCellColor(lplvcd,i);
+ sub:=GetQSColumn(lplvcd^.iSubItem);
+ if (qsopt.columns[sub].flags and COL_GENDER)<>0 then
+ begin
+ ListView_GetSubItemRect(grid,lplvcd^.nmcd.dwItemSpec,lplvcd^.iSubItem,LVIR_ICON,@rc);
+
+ case MainBuf[i,sub].data of
+ 70: h:=hIconF;
+ 77: h:=hIconM;
+ else
+ h:=0;
+ end;
+ if h<>0 then
+ begin
+ DrawIconEx(lplvcd^.nmcd.hdc,rc.left+1,rc.top,h,16,16,0,0,DI_NORMAL);
+ end;
+ result:=CDRF_SKIPDEFAULT;
+ end
+ else if (qsopt.columns[sub].flags and COL_XSTATUS)<>0 then
+ begin
+ j:=StrToInt(MainBuf[i,sub].text);
+ if j>0 then
+ begin
+ StrCopy(StrCopyE(buf,GetProtoName(FlagBuf[i].proto)),PS_ICQ_GETCUSTOMSTATUSICON);
+ if ServiceExists(buf)<>0 then
+ begin
+ h:=CallService(buf,j,LR_SHARED);
+
+ ListView_GetSubItemRect(grid,lplvcd^.nmcd.dwItemSpec,lplvcd^.iSubItem,LVIR_ICON,@rc);
+ DrawIconEx(lplvcd^.nmcd.hdc,rc.left+1,rc.top,h,16,16,0,0,DI_NORMAL);
+ end;
+ end;
+ result:=CDRF_SKIPDEFAULT;
+ end
+ else if qsopt.showclienticons and
+ ((qsopt.columns[sub].flags and COL_CLIENT)<>0) then
+ result:=CDRF_NOTIFYPOSTPAINT;
+ end;
+ CDDS_SUBITEM or CDDS_ITEMPOSTPAINT: begin
+ sub:=GetQSColumn(lplvcd^.iSubItem);
+ if (qsopt.columns[sub].flags and COL_CLIENT)<>0 then
+ begin
+ i:=LV_GetLParam(grid,lplvcd^.nmcd.dwItemSpec);
+ FastWideToAnsiBuf(MainBuf[i,sub].text,buf);
+
+// ListView_GetItemTextA(grid,lplvcd^.nmcd.dwItemSpec,lplvcd^.iSubItem,buf,SizeOf(buf));
+ if buf[0]<>#0 then
+ begin
+ h:=CallService(MS_FP_GETCLIENTICON,tlparam(@buf),0);
+ ListView_GetSubItemRect(grid,lplvcd^.nmcd.dwItemSpec,lplvcd^.iSubItem,LVIR_ICON,@rc);
+ DrawIconEx(lplvcd^.nmcd.hdc,rc.left+1,rc.top,h,16,16,0,0,DI_NORMAL);
+ DestroyIcon(h);
+ end;
+ result:=CDRF_SKIPDEFAULT;
+ end;
+ end;
+ end;
+end;
+
+function NewEditProc(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+var
+ count,current,next,perpage:integer;
+ li:LV_ITEM;
+begin
+ result:=0;
+ case hMessage of
+ WM_CHAR: if wParam=27 then
+ begin
+ PostMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDCANCEL,0);
+ exit;
+ end;
+ WM_KEYUP: if wParam=VK_RETURN then
+ begin
+ if ListView_GetSelectedCount(grid)=1 then
+ ShowContactMsgDlg(GetFocusedhContact);
+ exit;
+ end;
+ WM_KEYDOWN: begin
+ count :=ListView_GetItemCount(grid);
+ current:=ListView_GetNextItem(grid,-1,LVNI_FOCUSED);
+ next:=-1;
+ if count>0 then
+ case wParam of
+ VK_NEXT,VK_PRIOR: begin
+ perpage:=ListView_GetCountPerPage(grid);
+ if wParam=VK_NEXT then
+ next:=Min(current+perpage,count)
+ else
+ next:=Max(current-perpage,0);
+ end;
+ VK_UP: begin
+ if current>0 then
+ next:=current-1
+ end;
+ VK_DOWN: begin
+ if current<count-1 then
+ next:=current+1
+ end;
+ VK_F5: begin
+ PostMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDC_REFRESH,0);
+ exit;
+ end;
+ end;
+ if next>=0 then
+ begin
+ li.statemask:=LVIS_SELECTED;
+ li.state:=0;
+ SendMessage(grid,LVM_SETITEMSTATE,twparam(-1),tlparam(@li));
+ ListView_SetItemState(grid,next,LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+ // ListView_EnsureVisible(grid,next,false);
+ SendMessage(grid,LVM_ENSUREVISIBLE,next,0);
+ result:=0;
+ exit;
+ end;
+ end;
+ end;
+ result:=CallWindowProc(OldEditProc,Dialog,hMessage,wParam,lParam);
+end;
+
+function ShowContactMenu(wnd:HWND;hContact:THANDLE):HMENU;
+var
+ pt:tpoint;
+begin
+ if hContact<>0 then
+ begin
+ GetCursorPos(pt);
+ result:=CallService(MS_CLIST_MENUBUILDCONTACT,hContact,0);
+ if result<>0 then
+ begin
+ TrackPopupMenu(result,0,pt.x,pt.y,0,wnd,nil);
+ DestroyMenu(result);
+ end;
+ end
+ else
+ result:=0;
+end;
+
+procedure ShowMultiPopup(cnt:integer);
+var
+ mmenu,grpmenu:HMENU;
+ i:integer;
+ buf:array [0..255] of WideChar;
+ p:PWideChar;
+ pt:TPOINT;
+begin
+ mmenu:=CreatePopupMenu;
+ if mmenu=0 then
+ exit;
+
+ StrCopyW(buf,TranslateW('Selected'));
+ p:=@buf;
+ while p^<>#0 do inc(p);
+ p^:=' '; inc(p);
+
+ IntToStr(p,cnt);
+
+ while p^<>#0 do inc(p);
+ p^:=' '; inc(p);
+ StrCopyW(p,TranslateW('contacts'));
+ AppendMenuW(mmenu,MF_DISABLED+MF_STRING,0,buf);
+ AppendMenuW(mmenu,MF_SEPARATOR,0,nil);
+ AppendMenuW(mmenu,MF_STRING,101,TranslateW('&Delete'));
+ AppendMenuW(mmenu,MF_STRING,102,TranslateW('&Copy'));
+ if ServiceExists(MS_MC_CONVERTTOMETA)<>0 then
+ AppendMenuW(mmenu,MF_STRING,103,TranslateW('C&onvert to Meta'));
+
+ grpmenu:=MakeGroupMenu(400);
+
+// grpmenu:=CallService(MS_CLIST_GROUPBUILDMENU,0,0);
+ AppendMenuW(mmenu,MF_POPUP,grpmenu,TranslateW('&Move to Group'));
+
+ GetCursorPos(pt);
+ i:=integer(TrackPopupMenu(mmenu,TPM_RETURNCMD+TPM_NONOTIFY,pt.x,pt.y,0,mainwnd,nil));
+ case i of
+ 101: DeleteByList;
+ 102: begin
+ CopyMultiLinesW(ListView_GetSelectedCount(grid))
+ end;
+ 103: ConvertToMeta;
+ else
+ if i>0 then
+ begin // move to group
+ if i=400 then // root group
+ buf[0]:=#0
+ else
+ begin
+ GetMenuStringW(grpmenu,i,buf,SizeOf(buf),MF_BYCOMMAND);
+ end;
+ MoveToGroup(buf);
+ end;
+ end;
+ DestroyMenu(mmenu);
+ if qsopt.closeafteraction then
+ CloseSrWindow;
+end;
+
+procedure addcolumn(handle:hwnd;num,width:integer;title:PWideChar);
+var
+ lvcol:LV_COLUMNW;
+begin
+ zeromemory(@lvcol,sizeof(lvcol));
+ lvcol.mask :=LVCF_TEXT or LVCF_WIDTH;
+ lvcol.pszText :=title;
+ lvcol.cx :=width;
+ SendMessageW(handle,LVM_INSERTCOLUMNW,num,lparam(@lvcol));
+end;
+{
+// from zero!!
+function GetNthByMask(const arr:tcolumnarray; num:cardinal; mask:dword):tcolumnitem;
+var
+ i:cardinal;
+begin
+ for i:=0 to HIGH(arr) do
+ begin
+ if (arr[i].flags and mask)<>0 then
+ begin
+ if num=0 then
+ begin
+ result:=arr[i];
+ exit;
+ end;
+ dec(num);
+ end;
+ end;
+ result:=arr[0];
+end;
+}
+function ColorReload(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+begin
+ result:=0;
+ cbkg_norm:=GetColor(bkg_norm);
+ cfgr_norm:=GetColor(fgr_norm);
+ cbkg_odd :=GetColor(bkg_odd);
+ cfgr_odd :=GetColor(fgr_odd);
+ cbkg_dis :=GetColor(bkg_dis);
+ cfgr_dis :=GetColor(fgr_dis);
+ cbkg_del :=GetColor(bkg_del);
+ cfgr_del :=GetColor(fgr_del);
+ cbkg_hid :=GetColor(bkg_hid);
+ cfgr_hid :=GetColor(fgr_hid);
+ cbkg_meta:=GetColor(bkg_meta);
+ cfgr_meta:=GetColor(fgr_meta);
+ cbkg_sub :=GetColor(bkg_sub);
+ cfgr_sub :=GetColor(fgr_sub);
+end;
+
+procedure ClearBuffers;
+var
+ w,h:integer;
+begin
+ for w:=0 to HIGH(MainBuf) do
+ for h:=0 to HIGH(MainBuf[0]) do
+ mFreeMem(MainBuf[w,h].text);
+end;
+
+procedure SetSpecialColumns(num:integer);
+begin
+ with qsopt.columns[num] do
+ begin
+ if (setting_type=ST_BYTE) and
+ (lstrcmpia(wparam.a,'XStatusId')=0) then
+ begin
+ flags:=flags or COL_XSTATUS;
+ end
+
+ else if (setting_type=ST_CONTACTINFO) and
+ (setting_cnftype=CNF_GENDER) then
+ begin
+ if hIconF=0 then hIconF:=CallService(MS_SKIN2_GETICON,0,tlparam(QS_FEMALE));
+ if hIconM=0 then hIconM:=CallService(MS_SKIN2_GETICON,0,tlparam(QS_MALE));
+ flags:=flags or COL_GENDER;
+ tstrMale :=TranslateW('Male');
+ tstrFemale :=TranslateW('Female');
+ tstrUnknown:=TranslateW('Unknown');
+ end
+
+ else if (wparam.a<>NIL) and // FingerPrint preprocess
+ (setting_type=ST_STRING) and
+ (lstrcmpia(wparam.a,'MirVer')=0) and
+ (ServiceExists(MS_FP_GETCLIENTICON)<>0) then
+ flags:=flags or COL_CLIENT;
+ end;
+end;
+
+function FindAddDlgResizer(Dialog:HWND;lParam:LPARAM;urc:PUTILRESIZECONTROL):int; cdecl;
+begin
+ case urc^.wId of
+ IDCANCEL: result:=RD_ANCHORX_RIGHT or RD_ANCHORY_TOP;
+ IDC_REFRESH: result:=RD_ANCHORX_RIGHT or RD_ANCHORY_TOP;
+ IDC_CH_SHOWOFFLINE: result:=RD_ANCHORX_LEFT or RD_ANCHORY_TOP;
+ IDC_CH_COLORIZE: result:=RD_ANCHORX_LEFT or RD_ANCHORY_TOP;
+ IDC_CB_PROTOCOLS: result:=RD_ANCHORX_LEFT or RD_ANCHORY_TOP;
+ IDC_E_SEARCHTEXT: result:=RD_ANCHORX_WIDTH or RD_ANCHORY_TOP;
+ IDC_LIST: result:=RD_ANCHORX_WIDTH or RD_ANCHORY_HEIGHT;
+ IDC_STATUSBAR: result:=RD_ANCHORX_WIDTH or RD_ANCHORY_BOTTOM;
+ else
+ result:=0;
+ end;
+end;
+
+procedure PrepareTable(reset:boolean=false);
+var
+ i:integer;
+ old:integer;
+begin
+ old:=tablecolumns;
+ tablecolumns:=0;
+ for i:=0 to qsopt.numcolumns-1 do
+ begin
+ with qsopt.columns[i] do
+ begin
+ if (flags and COL_ON)<>0 then
+ begin
+ addcolumn(grid,tablecolumns,width,TranslateW(title));
+ inc(tablecolumns);
+ end;
+
+ SetSpecialColumns(i);
+ end;
+ end;
+ if reset then
+ begin
+ for i:=old+tablecolumns-1 downto tablecolumns do
+ begin
+ SendMessage(grid,LVM_DELETECOLUMN,i,0);
+ end;
+ end;
+
+ ListView_DeleteAllItems(grid);
+ ListView_SetItemCount(grid,HIGH(FlagBuf)+1);
+end;
+
+procedure FillProtoCombo(cb:HWND);
+var
+ i:integer;
+ buf:array [0..63] of WideChar;
+begin
+ SendMessage(cb,CB_RESETCONTENT,0,0);
+ CB_AddStrDataW(cb,TranslateW('All'));
+ for i:=1 to GetNumProto do
+ begin
+ CB_AddStrDataW(cb,FastAnsiToWideBuf(GetProtoName(i),@buf),i);
+ end;
+ SendMessage(cb,CB_SETCURSEL,0,0);
+end;
+
+function QSMainWndProc(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+var
+ tmp:LONG_PTR;
+ tmph:THANDLE;
+ w,h:uint_ptr;
+ i:integer;
+ buf:array [0..255] of WideChar;
+ rc:TRECT;
+ pt:TPOINT;
+ smenu:HMENU;
+ TI:tToolInfoW;
+ urd:TUTILRESIZEDIALOG;
+begin
+ result:=0;
+ case hMessage of
+ WM_DESTROY: begin
+ UnhookEvent(colorhook);
+
+ StatusBar:=0;
+ DeleteObject(gridbrush);
+ GetWindowRect(Dialog,rc);
+
+ CopyRect(qsopt.grrect,rc);
+
+ // set column width - only for enabled columns
+ for tmp:=0 to qsopt.numcolumns-1 do
+ begin
+ if {(qsopt.columns[tmp].flags and COL_ON)<>0} colorder[tmp]>=0 then
+ begin
+ w:=ListView_GetColumnWidth(grid,colorder[tmp]);
+ if w<>0 then
+ qsopt.columns[tmp].width:=w;
+ end;
+ end;
+ saveopt_wnd;
+
+ ListView_SetImageList(grid,0,LVSIL_SMALL);
+ opened:=false;
+
+ tmp:=GetDC(grid);
+ h:=GetCurrentObject(tmp,OBJ_FONT);
+ SendMessage(grid,WM_SETFONT,0,1);
+ DeleteObject(h);
+ ReleaseDC(grid,tmp);
+
+ grid:=0;
+
+ if qsopt.savepattern then
+ DBWriteUnicode(0,qs_module,'pattern',pattern);
+
+ mFreeMem(patstr);
+ mFreeMem(pattern);
+
+ ClearBuffers;
+ end;
+
+ WM_INITDIALOG: begin
+
+ tmph:=GetModuleHandle(nil);
+ if sortcoldn=0 then
+ sortcoldn:=LoadImageA(tmph,PAnsiChar(240),IMAGE_BITMAP,0,0,LR_LOADMAP3DCOLORS);
+ if sortcolup=0 then
+ sortcolup:=LoadImageA(tmph,PAnsiChar(239),IMAGE_BITMAP,0,0,LR_LOADMAP3DCOLORS);
+
+ SetWindowTextW(Dialog,'Quick Search');
+
+ StatusBar:=GetDlgItem(Dialog,IDC_STATUSBAR);
+
+ smenu:=GetSystemMenu(Dialog,false);
+ InsertMenu (smenu,5,MF_BYPOSITION or MF_SEPARATOR,0,nil);
+ InsertMenuW(smenu,6,MF_BYPOSITION or MF_STRING,
+ IDM_STAYONTOP,TranslateW('Stay on Top'));
+
+ if qsopt.stayontop then
+ begin
+ CheckMenuItem(smenu,IDM_STAYONTOP,MF_BYCOMMAND or MF_CHECKED);
+ SetWindowPos(Dialog,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE or SWP_NOSIZE);
+ end;
+ AdvFilter:=0;
+ if qsopt.showoffline then
+ begin
+ CheckDlgButton(Dialog,IDC_CH_SHOWOFFLINE,ORD(qsopt.showoffline));
+ AdvFilter:=AdvFilter or flt_show_offline;
+ end;
+
+ if qsopt.colorize then
+ CheckDlgButton(Dialog,IDC_CH_COLORIZE,ORD(qsopt.colorize));
+
+ gridbrush:=CreateSolidBrush(RGB(222,230,235));
+
+ mainwnd:=Dialog;
+ tmp:=GetWindowLongPtrW(Dialog,GWL_EXSTYLE);
+ if qsopt.usetoolstyle then
+ tmp:=tmp or WS_EX_TOOLWINDOW
+ else
+ tmp:=tmp and not WS_EX_TOOLWINDOW;
+ SetWindowLongPtrW(Dialog,GWL_EXSTYLE,tmp);
+
+ SendMessage(Dialog,WM_SETICON,ICON_SMALL,//LoadIcon(hInstance,PAnsiChar(IDI_QS))
+ CallService(MS_SKIN2_GETICON,0,tlparam(QS_QS)));
+ grid:=GetDlgItem(Dialog,IDC_LIST);
+
+ ListView_SetImageList(grid,
+ CallService(MS_CLIST_GETICONSIMAGELIST,0,0),LVSIL_SMALL);
+
+ tmp:=LVS_EX_FULLROWSELECT or LVS_EX_SUBITEMIMAGES or LVS_EX_HEADERDRAGDROP or LVS_EX_LABELTIP;
+ if qsopt.drawgrid then
+ tmp:=tmp or LVS_EX_GRIDLINES;
+ SendMessage(grid,LVM_SETEXTENDEDLISTVIEWSTYLE,0,tmp);
+
+ OldLVProc :=pointer(SetWindowLongPtrW(grid,GWL_WNDPROC,LONG_PTR(@NewLVProc)));
+ OldEditProc:=pointer(SetWindowLongPtrW(GetDlgItem(Dialog,IDC_E_SEARCHTEXT),
+ GWL_WNDPROC,LONG_PTR(@NewEditProc)));
+
+ OldProc:=pointer(SetWindowLongPtrW(
+ SendMessage(grid,LVM_GETHEADER,0,0),
+ GWL_WNDPROC,LONG_PTR(@NewLVHProc)));
+
+ FillProtoCombo(GetDlgItem(Dialog,IDC_CB_PROTOCOLS));
+
+ PrepareTable;
+
+ if pattern<>nil then
+ begin
+ SetDlgItemTextW(Dialog,IDC_E_SEARCHTEXT,pattern)
+ end
+ else
+ begin
+ buf[0]:=#0;
+ SetDlgItemTextW(Dialog,IDC_E_SEARCHTEXT,@buf);
+ FillGrid;
+ end;
+
+ TranslateDialogDefault(Dialog);
+
+ SnapToScreen(qsopt.grrect);
+ with qsopt.grrect do
+ MoveWindow(Dialog,left,top,right-left,bottom-top,false);
+
+ with TI do
+ begin
+ cbSize :=SizeOf(TI);
+ uFlags :=TTF_SUBCLASS+TTF_IDISHWND;
+ hWnd :=Dialog;
+ uId :=grid;
+ hInst :=0;
+ lpszText :=nil;
+ end;
+ HintWnd:=CreateWindowExW(0,TOOLTIPS_CLASS,nil,0,
+ integer(CW_USEDEFAULT),integer(CW_USEDEFAULT),
+ integer(CW_USEDEFAULT),integer(CW_USEDEFAULT),
+ Dialog,0,HInstance,NIL);
+
+ SendMessageW(HintWnd,TTM_ADDTOOLW,0,tlparam(@TI));
+// SetWindowsHookEx(WH_KEYBOARD,@QSKbdHook,0,GetCurrentThreadId);
+ colorhook:=HookEvent(ME_COLOUR_RELOAD,@ColorReload);
+
+ opened:=true;
+ end;
+
+ WM_GETMINMAXINFO: begin
+ with PMINMAXINFO(lParam)^ do
+ begin
+ ptMinTrackSize.x:=300;
+ ptMinTrackSize.y:=160;
+ end;
+ end;
+
+ WM_SIZE: begin
+ SendMessage(StatusBar,WM_SIZE,0,0);
+ FillChar(urd,SizeOf(TUTILRESIZEDIALOG),0);
+ urd.cbSize :=SizeOf(urd);
+ urd.hwndDlg :=Dialog;
+ urd.hInstance :=hInstance;
+ urd.lpTemplate:=MAKEINTRESOURCEA(IDD_MAIN);
+ urd.lParam :=0;
+ urd.pfnResizer:=@FindAddDlgResizer;
+ CallService(MS_UTILS_RESIZEDIALOG,0,tlparam(@urd));
+ end;
+
+ WM_SYSCOMMAND: begin
+ if wParam=IDM_STAYONTOP then
+ begin
+ if qsopt.stayontop then
+ begin
+ h:=MF_BYCOMMAND or MF_UNCHECKED;
+ w:=HWND_NOTOPMOST;
+ end
+ else
+ begin
+ h:=MF_BYCOMMAND or MF_CHECKED;
+ w:=HWND_TOPMOST;
+ end;
+ CheckMenuItem(GetSystemMenu(Dialog,false),IDM_STAYONTOP,h);
+ SetWindowPos(Dialog,w,0,0,0,0,SWP_NOMOVE or SWP_NOSIZE);
+ qsopt.stayontop:=not qsopt.stayontop;
+ exit;
+ end;
+ end;
+
+ WM_CONTEXTMENU: begin
+ if wParam=tWPARAM(GetDlgItem(Dialog,IDC_LIST)) then
+ begin
+ w:=ListView_GetSelectedCount(grid);
+ if w>1 then
+ ShowMultiPopup(w)
+ else
+ ShowContactMenu(Dialog,GetFocusedhContact);
+ end;
+ end;
+
+ WM_MYADDCONTACT: begin
+ // refresh table to add contact
+ i:=Length(MainBuf);
+ SetLength(MainBuf,i+1);
+ SetLength(MainBuf[i],qsopt.numcolumns);
+ SetLength(FlagBuf,i+1);
+
+ AddContact(i,wParam);
+ ProcessLine(i);
+ Sort;
+ UpdateSB;
+ end;
+
+ WM_MYDELETECONTACT: begin
+ i:=FindBufNumber(wParam);
+ if i>=0 then
+ begin
+ FlagBuf[i].flags:=(FlagBuf[i].flags or QSF_DELETED) and not QSF_ACTIVE;
+ for w:=0 to HIGH(MainBuf[0]) do
+ mFreeMem(MainBuf[i,w].text);
+ i:=FindItem(i);
+ if i>=0 then
+ ListView_DeleteItem(grid,i);
+ UpdateSB;
+ end;
+ end;
+
+ WM_MEASUREITEM:
+ CallService(MS_CLIST_MENUMEASUREITEM,wParam,lParam);
+ WM_DRAWITEM:
+ CallService(MS_CLIST_MENUDRAWITEM,wParam,lParam);
+
+ WM_MOUSEMOVE: begin
+ if TTInstalled then
+ begin
+ GetWindowRect(grid,rc);
+ pt.x:=loword(lParam);
+ pt.y:=hiword(lParam);
+ ClientToScreen(Dialog,pt);
+ if not PtInRect(rc,pt) then
+ begin
+ if TTShowed then
+ begin
+ TTShowed:=false;
+ CallService(MS_TIPPER_HIDETIP,0,0);
+ end;
+ KillTimer(grid,TIMERID_HOVER);
+ end;
+ end;
+ end;
+
+ WM_KEYDOWN: begin
+ case wParam of
+ VK_F5: begin
+ PostMessage(Dialog,WM_COMMAND,(BN_CLICKED shl 16)+IDC_REFRESH,0);
+ exit;
+ end;
+ end;
+ end;
+
+ WM_COMMAND: begin
+ if opened and (CallService(MS_CLIST_MENUPROCESSCOMMAND,
+ MAKEWPARAM(LOWORD(wParam),MPCF_CONTACTMENU),
+ GetFocusedhContact)<>0) then
+ begin
+ if qsopt.closeafteraction then
+ CloseSrWindow;
+ exit;
+ end;
+
+ case wParam shr 16 of
+ CBN_SELCHANGE: begin
+ AdvFilter:=(AdvFilter and not $FF) or cardinal(CB_GetData(lParam));
+ AdvancedFilter;
+ end;
+
+ EN_CHANGE: begin
+ GetDlgItemTextW(Dialog,IDC_E_SEARCHTEXT,buf,sizeOf(buf));
+ mFreeMem(pattern);
+ StrDupW(pattern,buf);
+ if pattern<>nil then
+ CharLowerW(pattern);
+ FillGrid; //!!
+ end;
+
+ BN_CLICKED: begin
+ case loword(wParam) of
+ IDC_CH_SHOWOFFLINE: begin
+ qsopt.showoffline:=IsDlgButtonChecked(Dialog,IDC_CH_SHOWOFFLINE)<>BST_UNCHECKED;
+ if qsopt.showoffline then
+ AdvFilter:=AdvFilter or flt_show_offline
+ else
+ AdvFilter:=AdvFilter and not flt_show_offline;
+ AdvancedFilter;
+ end;
+
+ IDC_CH_COLORIZE: begin
+ qsopt.colorize:=IsDlgButtonChecked(Dialog,IDC_CH_COLORIZE)<>BST_UNCHECKED;
+ RedrawWindow(grid,nil,0,RDW_INVALIDATE);
+ end;
+
+ IDC_REFRESH: begin
+ ClearBuffers;
+ PrepareToFill;
+ PrepareTable(true);
+ FillGrid;
+ end;
+
+ IDCANCEL: CloseSrWindow();
+ end;
+ end;
+ end;
+ end;
+
+ WM_NOTIFY: begin
+ case integer(PNMHdr(lParam)^.code) of
+ LVN_COLUMNCLICK: begin
+ ColumnClick(PNMListView(lParam)^.hdr.hwndFrom,PNMListView(lParam)^.iSubItem);
+ end;
+ NM_CUSTOMDRAW: begin
+ if PNMHdr(lParam)^.hwndFrom=grid then
+ begin
+ SetWindowLongPtrW(Dialog,DWL_MSGRESULT,ProcessCustomDraw(lParam));
+ result:=1;
+ end;
+ end;
+ end;
+ end;
+
+// else
+// result:=DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+end;
+
+function CloseSrWindow:boolean;
+begin
+ result:=true;
+ if opened and (mainwnd<>0) then
+ begin
+ DestroyWindow(mainwnd);
+
+ opened:=false;
+ mainwnd:=0;
+ end;
+ FreeProtoList;
+end;
+
+function OpenSRWindow(apattern:PWideChar;flags:LPARAM):boolean;
+begin
+ result:=true;
+ if opened then
+ exit;
+
+ TTInstalled := ServiceExists(MS_TIPPER_SHOWTIP)<>0;
+ // too lazy to move pattern and flags to thread
+ if apattern<>nil then
+ begin
+ if flags=0 then
+ StrDupW(pattern,apattern)
+ else
+ AnsiToWide(PAnsiChar(apattern),pattern);
+ CharLowerW(pattern);
+ end
+ else if qsopt.savepattern then
+ pattern:=DBReadUnicode(0,qs_module,'pattern',nil)
+ else
+ pattern:=nil;
+
+ CreateProtoList;
+ if PrepareToFill then
+ begin
+//!! SetLength(colorder,qsopt.numcolumns);
+ ColorReload(0,0);
+ CreateDialogW(hInstance,PWideChar(IDD_MAIN),0,@QSMainWndProc);
+ end;
+end;
+
+function BringToFront:integer;
+var
+ wp:TWINDOWPLACEMENT;
+begin
+ result:=1;
+ wp.length:=SizeOf(TWINDOWPLACEMENT);
+ GetWindowPlacement(mainwnd,@wp);
+ if wp.showCmd=SW_SHOWMINIMIZED then
+ ShowWindow(mainwnd,SW_RESTORE);
+ SetForegroundWindow(mainwnd);
+end;
+
+procedure ChangeStatusPicture(row:integer; hContact:THANDLE;Pic:integer);
+var
+ li:LV_ITEM;
+begin
+ row:=FindItem(row);
+ if row>=0 then
+ begin
+ li.iItem :=row;
+ li.iSubItem:=0;
+ li.mask :=LVIF_IMAGE;
+ li.iImage :=Pic;//CallService(MS_CLIST_GETCONTACTICON,hContact,0);
+ SendMessage(grid,LVM_SETITEM,0,lparam(@li));
+ end;
+end;
+
+function OnStatusChanged(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ j:integer;
+ oldstat,newstat:integer;
+begin
+ result:=0;
+ if not opened then exit;
+
+ j:=FindBufNumber(wParam);
+ if j>=0 then
+ begin
+ oldstat:=FlagBuf[j].status;
+ newstat:=DBReadWord(wParam,GetProtoName(FlagBuf[j].proto),'Status',ID_STATUS_OFFLINE);
+ FlagBuf[j].status:=newstat;
+
+ if (oldstat<>ID_STATUS_OFFLINE) and (newstat<>ID_STATUS_OFFLINE) then
+ ChangeStatusPicture(j,wParam,lParam)
+ else if (oldstat=ID_STATUS_OFFLINE) {and (newstat<>ID_STATUS_OFFLINE)} then
+ begin
+ if qsopt.showoffline then
+ ChangeStatusPicture(j,wParam,lParam)
+ else
+ ProcessLine(j,false) // why false? need to filter!
+ end
+ else if {(oldstat<>ID_STATUS_OFFLINE) and} (newstat=ID_STATUS_OFFLINE) then
+ begin
+ if qsopt.showoffline then
+ ChangeStatusPicture(j,wParam,lParam)
+ else
+ begin
+ FlagBuf[j].flags:=FlagBuf[j].flags and not QSF_ACTIVE;
+ ListView_DeleteItem(grid,FindItem(j));
+ end;
+ end;
+
+ // refresh table to new filtering
+ if qsopt.sortbystatus then
+ Sort;
+ UpdateSB;
+ end;
+end;
+
+function OnContactAdded(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+begin
+ result:=0;
+ if opened then
+ PostMessage(mainwnd,WM_MYADDCONTACT,wParam,lParam);
+end;
+
+function OnContactDeleted(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+begin
+ result:=0;
+ if opened then
+ PostMessage(mainwnd,WM_MYDELETECONTACT,wParam,lParam);
+end;
+
+function OnAccountChanged(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+begin
+ result:=0;
+ if not opened then exit;
+ case wParam of
+ PRAC_ADDED: begin
+ end;
+ PRAC_REMOVED: begin
+ end;
+ PRAC_CHECKED: begin
+ with PPROTOACCOUNT(lParam)^ do
+ begin
+ if bIsEnabled<>0 then
+ begin
+ end
+ else
+ begin
+ end;
+ end;
+ end;
+ end;
+end;
+
+function ShiftIndex(idx:integer;var order:array of integer):pointer;
+var
+ i,n:integer;
+begin
+ n:=colorder[idx];
+ for i:=0 to qsopt.numcolumns-1 do
+ if order[i]>n then dec(order[i]);
+ result:=@order;
+end;
+
+procedure FillLVColumn(column,lvcolumn:integer);
+var
+ li:LV_ITEMW;
+ i:integer;
+begin
+ FillChar(li,SizeOf(li),0);
+ for i:=0 to ListView_GetItemCount(grid)-1 do
+ begin
+ li.iItem :=i;
+ li.mask :=LVIF_PARAM;
+ li.iSubItem:=0;
+ SendMessage(grid,LVM_GETITEM,0,lparam(@li));
+
+ li.pszText :=MainBuf[li.lParam,column].text;
+ // Client icons preprocess
+ if (((qsopt.columns[column].flags and COL_CLIENT)<>0) and
+ (li.pszText<>NIL) and qsopt.showclienticons) OR
+ ((qsopt.columns[column].flags and (COL_XSTATUS or COL_GENDER))<>0) then
+ li.mask:=LVIF_IMAGE or LVIF_TEXT
+ else
+ li.mask:=LVIF_TEXT;
+ li.iSubItem:=lvcolumn;
+ SendMessageW(grid,LVM_SETITEMW,0,lparam(@li));
+ end;
+end;
+
+procedure SetColumnOrder;
+var
+ i,col:integer;
+ lcol:array [0..99] of integer;
+begin
+ col:=0;
+ for i:=0 to qsopt.numcolumns-1 do
+ if colorder[i]>=0 then
+ begin
+ lcol[col]:=colorder[i];
+ inc(col);
+ end;
+
+ SendMessageW(grid,LVM_SETCOLUMNORDERARRAY,col,lparam(@lcol[0]));
+
+ InvalidateRect(grid,nil,false);
+end;
+
+procedure WndChangeColumns(code:integer;column:integer=-1);
+var
+ i,col:integer;
+ coldata:tQSRec;
+// lcol:array of integer;
+ incr:integer;
+begin
+ if (grid<>0) and
+ ((code=wcRefresh) or ((column>=0) and (column<qsopt.numcolumns))) then
+ begin
+ SendMessage(grid,WM_SETREDRAW,0,0);
+ case code of
+ wcUp,wcDown: begin
+ // changing buffer data
+ if code=wcUp then
+ incr:=-1
+ else
+ incr:=1;
+
+ // memory
+ for i:=0 to HIGH(MainBuf) do
+ begin
+ coldata :=MainBuf[i,column];
+ MainBuf[i,column ]:=MainBuf[i,column+incr];
+ MainBuf[i,column+incr]:=coldata;
+ end;
+
+ // index
+ col :=colorder[column];
+ colorder[column ]:=colorder[column+incr];
+ colorder[column+incr]:=col;
+
+ // check needs for screen resort (columns are AFTER resort)
+ if ((qsopt.columns[column ].flags and COL_ON)=COL_ON) and
+ ((qsopt.columns[column+incr].flags and COL_ON)=COL_ON) then // both visible
+ begin // need resort on screen
+ SetColumnOrder;
+ end;
+ SendMessage(grid,WM_SETREDRAW,1,0);
+ exit;
+ end;
+
+ wcHide: begin // hide column
+ // screen
+ SendMessage(grid,LVM_DELETECOLUMN,colorder[column],0);
+ dec(tablecolumns);
+ // index
+ ShiftIndex(column,colorder);
+ colorder[column]:=-1;
+ end;
+
+ wcDelete: begin // delete column
+ // screen
+ SendMessage(grid,LVM_DELETECOLUMN,colorder[column],0);
+ dec(tablecolumns);
+ // memory
+ for i:=0 to HIGH(MainBuf) do
+ begin
+ mFreeMem(MainBuf[i,column].text);
+ if column<qsopt.numcolumns then
+ move(MainBuf[i,column+1],MainBuf[i,column],SizeOf(tQSRec));
+ end;
+ SetLength(MainBuf,Length(MainBuf),qsopt.numcolumns); //!!!!
+ // index
+ if column<qsopt.numcolumns then
+ move(colorder[column+1],colorder[column],SizeOf(integer));
+ SetLength(colorder,qsopt.numcolumns);
+ end;
+
+ wcShow: begin // show column
+ // memory
+ if (qsopt.columns[column].flags and COL_INIT)=0 then
+ begin
+ for i:=0 to HIGH(MainBuf) do // contacts
+ begin
+ LoadOneItem(FlagBuf[i].contact,column,FlagBuf[i].proto,MainBuf[i,column]);
+ end;
+ qsopt.columns[column].flags:=qsopt.columns[column].flags or COL_INIT;
+ end;
+ // screen
+ with qsopt.columns[column] do // atm - to the end only
+ addcolumn(grid,tablecolumns,width,TranslateW(title));
+
+ // fill new column
+ FillLVColumn(column,tablecolumns);
+ // index
+ colorder[column]:=tablecolumns;
+ inc(tablecolumns);
+
+ SetColumnOrder;
+{
+ SetLength(lcol,tablecolumns);
+ col:=0;
+ for i:=0 to qsopt.numcolumns-1 do
+ if colorder[i]>=0 then
+ begin
+ lcol[col]:=colorder[i];
+ inc(col);
+ end;
+
+ SendMessageW(grid,LVM_SETCOLUMNORDERARRAY,tablecolumns,dword(@lcol[0]));
+
+ InvalidateRect(grid,nil,false);
+}
+ end;
+
+ wcInsert: begin // add column
+ // memory
+ SetLength(MainBuf,Length(MainBuf),qsopt.numcolumns+1); //!!!!
+ SetSpecialColumns(column);
+ // index
+ SetLength(colorder,qsopt.numcolumns+1);
+ // screen
+ if (qsopt.columns[column].flags and COL_ON)<>0 then
+ WndChangeColumns(wcShow,column)
+ else
+ colorder[column]:=-1;
+ end;
+
+ wcChange: begin // change column
+ for i:=0 to HIGH(MainBuf) do
+ begin
+ mFreeMem(MainBuf[i,column].text);
+ end;
+ SetSpecialColumns(column);
+ qsopt.columns[column].flags:=qsopt.columns[column].flags and not COL_INIT;
+ if (qsopt.columns[column].flags and COL_ON)<>0 then
+ FillLVColumn(column,colorder[column]);
+ end;
+
+ wcRefresh: begin // refresh all info
+ ClearBuffers;
+ PrepareToFill;
+ PrepareTable(true);
+ end;
+ end;
+ FillGrid;
+ SendMessage(grid,WM_SETREDRAW,1,0);
+ end;
+end;
+
+begin
+end.
diff --git a/plugins/Utils.pas/TextBlock.pas b/plugins/Utils.pas/TextBlock.pas
new file mode 100644
index 0000000000..13535b832a
--- /dev/null
+++ b/plugins/Utils.pas/TextBlock.pas
@@ -0,0 +1,335 @@
+unit TextBlock;
+
+interface
+
+uses KOL, windows;
+
+const
+ ppLeft = 0;
+ ppRight = 1;
+ //effects
+ effCut = 0;
+ effWrap = 1;
+ effRoll = 2;
+ effPong = 3;
+ effCenter = $100;
+
+type
+ pChunk = ^tChunk;
+ tChunk = record
+ _type:integer; // type
+ val :integer; // sign value or text length
+ txt :pWideChar; // text value pointer
+ add :integer; // offset for text effect
+ dir :integer; // ping-pong directon
+ end;
+ pChunkArray = ^tChunkArray;
+ tChunkArray = array [0..1000] of tChunk;
+
+type
+ pTextData = ^tTextData;
+ tTextData = record
+ // runtime data
+ UpdTimer :cardinal;
+ TextFont :HFONT;
+ NeedResize :Boolean;
+
+ // working data
+ TextChunk :pChunkArray;
+ Text :pWideChar; // for text chunks
+
+ TextColor :TCOLORREF;
+ BkColor :TCOLORREF;
+ TextLF :TLOGFONTW;
+
+ // options
+ TextEffect :dword;
+ RollStep :integer;
+ RollGap :integer;
+// RollTail :integer;
+ UpdInterval :cardinal;
+ end;
+
+const
+ MaxTxtScrollSpeed = 20;
+ awkTextPad = 4; // text block pad from frame border
+
+const
+ idx_effect = 0;
+ idx_rollstep = 1;
+ idx_rollgap = 2;
+ idx_timer = 3;
+ idx_txtcolor = 4;
+ idx_bkcolor = 5;
+ idx_font = 6;
+type
+ pTextBlock = ^tTextBlock;
+ tTextBlock = object(TControl)
+ private
+ procedure myCtrlResize(Sender: PObj);
+ procedure myTextPaint(Sender: PControl; DC: HDC);
+ procedure myMouseDown(Sender:PControl;var Mouse:TMouseEventData);
+
+ procedure ClearText;
+ function Split(src:pWideChar):pChunkArray;
+
+ procedure DrawChunks(dc:HDC;Chunk:pChunk;rc:TRECT;justpaint:boolean);
+ procedure DrawLines (dc:HDC;Chunk:pChunk;rc:TRECT;justpaint:boolean);
+
+ function GetEffect(idx:integer):integer;
+ procedure SetEffect(idx:integer;value:integer);
+
+ function GetText:pWideChar;
+ procedure SetText(value:pWideChar);
+
+ function GetFontData:TLOGFONTW;
+ procedure SetFontData(const value:TLOGFONTW);
+
+ public
+ procedure DrawText(DC: HDC; justpaint:boolean);
+
+ property Effects :integer index idx_effect read GetEffect write SetEffect;
+ property RollStep :integer index idx_rollstep read GetEffect write SetEffect;
+ property RollGap :integer index idx_rollgap read GetEffect write SetEffect;
+ property UpdateTime:integer index idx_timer read GetEffect write SetEffect;
+ property TextColor :integer index idx_txtcolor read GetEffect write SetEffect;
+ property BkColor :integer index idx_bkcolor read GetEffect write SetEffect;
+ property Font :integer index idx_font read GetEffect write SetEffect;
+
+ property FontData :TLOGFONTW read GetFontData write SetFontData;
+ property BlockText:pWideChar read GetText write SetText;
+ end;
+
+function MakeNewTextBlock(AOwner:PControl;BkColor:TCOLORREF):pTextBlock;
+
+implementation
+
+uses messages,common;
+
+{$include tb_chunk.inc}
+
+function tTextBlock.GetFontData:TLOGFONTW;
+begin
+ result:=pTextData(CustomData).TextLF;
+end;
+
+procedure tTextBlock.SetFontData(const value:TLOGFONTW);
+begin
+ move(value,pTextData(CustomData).TextLF,SizeOf(TLOGFONTW));
+end;
+
+function tTextBlock.GetEffect(idx:integer):integer;
+begin
+ with pTextData(CustomData)^ do
+ case idx of
+ idx_effect : result:=TextEffect;
+ idx_rollstep: result:=RollStep;
+ idx_rollgap : result:=RollGap;
+ idx_txtcolor: result:=TextColor;
+ idx_bkcolor : result:=BkColor;
+ idx_font : result:=0;
+ idx_timer : result:=UpdInterval;
+ else // it can't be really
+ result:=0;
+ end;
+end;
+
+procedure TimerProc(wnd:HWND;uMsg:uint;TB:pTextBlock;dwTime:dword); stdcall;
+var
+ DC:HDC;
+begin
+ DC:=GetDC(wnd);
+ TB.DrawText(DC,false);
+ ReleaseDC(wnd,DC);
+end;
+
+procedure tTextBlock.SetEffect(idx:integer;value:integer);
+var
+ DC:HDC;
+ OldFont:HFONT;
+begin
+ with pTextData(CustomData)^ do
+ case idx of
+ idx_effect : TextEffect :=value;
+ idx_rollstep: RollStep :=value;
+ idx_rollgap : RollGap :=value;
+ idx_txtcolor: TextColor :=value;
+ idx_bkcolor : BkColor :=value;
+ idx_font : begin
+ DC:=GetDC(0);
+ OldFont:=SelectObject(DC,value);
+ GetObject(GetCurrentObject(dc,OBJ_FONT),SizeOf(TLOGFONT),@TextLF);
+ SelectObject(DC,OldFont);
+ ReleaseDC(0,DC);
+ end;
+ idx_timer : begin
+ // stoptimer
+ if UpdTimer<>0 then
+ begin
+ KillTimer(0,UpdTimer);
+ UpdTimer:=0;
+ end;
+
+ UpdInterval:=value;
+ // starttimer
+ if UpdInterval>0 then
+ UpdTimer:=SetTimer(Self.GetWindowHandle,integer(@Self),(MaxTxtScrollSpeed+1-UpdInterval)*100,@TimerProc);
+ end;
+ end;
+end;
+
+procedure tTextBlock.ClearText;
+var
+ D:pTextData;
+begin
+ D:=CustomData;
+ if D.Text<>nil then
+ begin
+ DeleteChunks(D.TextChunk);
+ FreeMem(D.Text);
+ D.Text:=nil;
+ end;
+end;
+
+function tTextBlock.GetText:pWideChar;
+begin
+ result:=pTextData(CustomData)^.Text;
+end;
+
+procedure tTextBlock.SetText(value:pWideChar);
+var
+ D:pTextData;
+begin
+ D:=CustomData;
+ if (D.Text<>value) or
+ (StrCmpW(D.Text, value)<>0) then
+ begin
+ self.ClearText;
+ if (value<>nil) and (value^<>#0) then
+ begin
+ GetMem(D.Text,(StrLenW(value)+1)*SizeOf(WideChar));
+ WStrCopy(D.Text,value);
+ D.TextChunk:=Split(D.Text);
+
+ // start timer if was stopped
+ if (D.UpdTimer=0) and (D.UpdInterval>0) then
+ D.UpdTimer:=SetTimer(Self.GetWindowHandle,integer(@Self),
+ (MaxTxtScrollSpeed+1-D.UpdInterval)*100,@TimerProc);
+ end
+ else // stop timer for empty text
+ begin
+ if D.UpdTimer<>0 then
+ begin
+ KillTimer(0,D.UpdTimer);
+ D.UpdTimer:=0;
+ end;
+ end;
+ Invalidate;
+ end;
+end;
+
+procedure tTextBlock.DrawText(DC:HDC; justpaint:boolean);
+var
+ dst:TRECT;
+ D:pTextData;
+ MemDC:HDC;
+begin
+ D:=CustomData;
+ with D^ do
+ if TextChunk<>nil then
+ begin
+ CopyRect(dst,Self.BoundsRect);
+
+ MemDC:=CreateCompatibleDC(dc);
+ SetTextColor(MemDC,TextColor);
+ SelectObject(MemDC,CreateCompatibleBitmap(DC,dst.right,dst.bottom));
+ DeleteObject(SelectObject(MemDC,CreateFontIndirectW(D.TextLF)));
+
+ BitBlt(MemDC,dst.left,dst.top,dst.right-dst.left,dst.bottom-dst.top,
+ dc,dst.left,dst.top,SRCCOPY);
+
+ InflateRect(dst,-4,-2); // text padding from text block
+ DrawChunks(MemDC,@TextChunk[0],dst,justpaint); // i.e. only paint or roll
+ InflateRect(dst,4,2); // text padding from text block
+
+ BitBlt(dc,dst.left,dst.top,dst.right-dst.left,dst.bottom-dst.top,
+ MemDC,dst.left,dst.top,SRCCOPY);
+ DeleteDC(MemDC);
+ end;
+end;
+
+procedure tTextBlock.myTextPaint(Sender: PControl; DC: HDC);
+begin
+ DrawText(DC,true);
+end;
+
+procedure tTextBlock.myMouseDown(Sender:PControl;var Mouse:TMouseEventData);
+var
+ wnd:HWND;
+begin
+ wnd:=GetParent(GetParent(Sender.GetWindowHandle));
+ SendMessage(wnd,WM_SYSCOMMAND,
+ SC_MOVE or HTCAPTION,MAKELPARAM(Mouse.x,Mouse.y));
+end;
+
+// avoiding anchors problems
+procedure tTextBlock.myCtrlResize(Sender: PObj);
+var
+ tmp:integer;
+ D:pTextData;
+begin
+ D:=CustomData;
+ if D.NeedResize then
+ begin
+ D.NeedResize:=false;
+
+ tmp:=PControl(Sender).Parent.Width-2*awkTextPad;
+
+ if (PControl(Sender)^.Width)>tmp then
+ PControl(Sender)^.Width:=tmp;
+
+ D.NeedResize:=true;
+ end;
+end;
+
+procedure Destroy(dummy:PControl;sender:PObj);
+var
+ D:pTextData;
+begin
+ D:=PTextBlock(sender).CustomData;
+ if D.UpdTimer<>0 then
+ begin
+ KillTimer(0,D.UpdTimer);
+ D.UpdTimer:=0;
+ end;
+ PTextBlock(sender).ClearText;
+end;
+
+function MakeNewTextBlock(AOwner:PControl;BkColor:TCOLORREF):pTextBlock;
+var
+ D:pTextData;
+begin
+ result:=pTextBlock(NewPanel(AOwner,esNone));
+// result:=NewLabel(AOwner,'');
+// result:=NewLabelEffect(AOwner,'',0);
+ GetMem(D,SizeOf(tTextData));
+ FillChar(D^,SizeOf(tTextData),0);
+ result.CustomData :=D;
+ result.Transparent:=true;
+
+ result.SetSize(AOwner.Width-awkTextPad*2,40);
+ result.SetPosition(AOwner.Left+awkTextPad,awkTextPad);
+ result.Anchor(true,true,true,true);
+
+ result.OnResize :=result.myCtrlResize;
+ result.OnPaint :=result.myTextPaint;
+ result.OnMouseDown:=result.myMouseDown;
+ Result.OnDestroy:=TOnEvent(MakeMethod(nil,@Destroy));
+
+// result..InitFrame;
+ D.BkColor :=BkColor;
+ D.TextChunk :=nil;
+ D.NeedResize:=true;
+end;
+
+end.
diff --git a/plugins/Utils.pas/appcmdapi.pas b/plugins/Utils.pas/appcmdapi.pas
new file mode 100644
index 0000000000..b316838ba3
--- /dev/null
+++ b/plugins/Utils.pas/appcmdapi.pas
@@ -0,0 +1,97 @@
+unit appcmdapi;
+interface
+
+uses windows;
+
+const
+ APPCOMMAND_BROWSER_BACKWARD = 1; // Navigate backward.
+ APPCOMMAND_BROWSER_FORWARD = 2; // Navigate forward.
+ APPCOMMAND_BROWSER_REFRESH = 3; // Refresh page.
+ APPCOMMAND_BROWSER_STOP = 4; // Stop download.
+ APPCOMMAND_BROWSER_SEARCH = 5; // Open search.
+ APPCOMMAND_BROWSER_FAVORITES = 6; // Open favorites.
+ APPCOMMAND_BROWSER_HOME = 7; // Navigate home.
+ APPCOMMAND_VOLUME_MUTE = 8; // Mute the volume.
+ APPCOMMAND_VOLUME_DOWN = 9; // Lower the volume.
+ APPCOMMAND_VOLUME_UP = 10; // Raise the volume
+ APPCOMMAND_MEDIA_NEXTTRACK = 11; // Go to next track.
+ APPCOMMAND_MEDIA_PREVIOUSTRACK = 12; // Go to previous track.
+ APPCOMMAND_MEDIA_STOP = 13; // Stop playback.
+ APPCOMMAND_MEDIA_PLAY_PAUSE = 14; // Play or pause playback. If there are discrete Play
+ // and Pause buttons, applications should take action
+ // on this command as well as APPCOMMAND_MEDIA_PLAY and
+ // APPCOMMAND_MEDIA_PAUSE.
+ APPCOMMAND_LAUNCH_MAIL = 15; // Open mail.
+ APPCOMMAND_LAUNCH_MEDIA_SELECT = 16; // Go to Media Select mode
+ APPCOMMAND_MEDIA_SELECT = APPCOMMAND_LAUNCH_MEDIA_SELECT;
+ APPCOMMAND_LAUNCH_APP1 = 17; // Start App1.
+ APPCOMMAND_LAUNCH_APP2 = 18; // Start App2.
+ APPCOMMAND_BASS_DOWN = 19; // Decrease the bass.
+ APPCOMMAND_BASS_BOOST = 20; // Toggle the bass boost on and off.
+ APPCOMMAND_BASS_UP = 21; // Increase the bass.
+ APPCOMMAND_TREBLE_DOWN = 22; // Decrease the treble.
+ APPCOMMAND_TREBLE_UP = 23; // Increase the treble.
+
+ APPCOMMAND_MICROPHONE_VOLUME_MUTE = 24; // Windows XP: Mute the microphone.
+ APPCOMMAND_MICROPHONE_VOLUME_DOWN = 25; // Windows XP: Decrease microphone volume.
+ APPCOMMAND_MICROPHONE_VOLUME_UP = 26; // Windows XP: Increase microphone volume.
+ APPCOMMAND_HELP = 27; // Windows XP: Open the Help dialog.
+ APPCOMMAND_FIND = 28; // Windows XP: Open the Find dialog.
+ APPCOMMAND_NEW = 29; // Windows XP: Create a new window.
+ APPCOMMAND_OPEN = 30; // Windows XP: Open a window.
+ APPCOMMAND_CLOSE = 31; // Windows XP: Close the window (not the application).
+ APPCOMMAND_SAVE = 32; // Windows XP: Save current document.
+ APPCOMMAND_PRINT = 33; // Windows XP: Print current document.
+ APPCOMMAND_UNDO = 34; // Windows XP: Undo last action.
+ APPCOMMAND_REDO = 35; // Windows XP: Redo last action.
+ APPCOMMAND_COPY = 36; // Windows XP: Copy the selection.
+ APPCOMMAND_CUT = 37; // Windows XP: Cut the selection.
+ APPCOMMAND_PASTE = 38; // Windows XP: Paste
+ APPCOMMAND_REPLY_TO_MAIL = 39; // Windows XP: Reply to a mail message.
+ APPCOMMAND_FORWARD_MAIL = 40; // Windows XP: Forward a mail message.
+ APPCOMMAND_SEND_MAIL = 41; // Windows XP: Send a mail message.
+ APPCOMMAND_SPELL_CHECK = 42; // Windows XP: Initiate a spell check.
+ APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE = 43;
+ // Windows XP:Toggles between two modes of speech input: dictation and command/control
+ // (giving commands to an application or accessing menus).
+ APPCOMMAND_MIC_ON_OFF_TOGGLE = 44; // Windows XP: Toggle the microphone.
+ APPCOMMAND_CORRECTION_LIST = 45; // Windows XP: Brings up the correction list when
+ // a word is incorrectly identified during speech input.
+
+ APPCOMMAND_MEDIA_PLAY = 46; // Windows XP SP1: Begin playing at the current position.
+ // If already paused, it will resume. This is a direct
+ // PLAY command that has no state. If there are
+ // discrete Play and Pause buttons, applications should
+ // take action on this command as well as
+ // APPCOMMAND_MEDIA_PLAY_PAUSE.
+ APPCOMMAND_MEDIA_PAUSE = 47; // Windows XP SP1: Pause. If already paused, take no
+ // further action. This is a direct PAUSE command that
+ // has no state. If there are discrete Play and Pause
+ // buttons, applications should take action on this
+ // command as well as APPCOMMAND_MEDIA_PLAY_PAUSE.
+ APPCOMMAND_MEDIA_RECORD = 48; // Windows XP SP1: Begin recording the current stream.
+ APPCOMMAND_MEDIA_FAST_FORWARD = 49; // Windows XP SP1: Increase the speed of stream playback.
+ // This can be implemented in many ways, for example,
+ // using a fixed speed or toggling through a series of
+ // increasing speeds.
+ APPCOMMAND_MEDIA_REWIND = 50; // Windows XP SP1: Go backward in a stream at a higher
+ // rate of speed. This can be implemented in many ways,
+ // for example, using a fixed speed or toggling through
+ // a series of increasing speeds.
+ APPCOMMAND_MEDIA_CHANNEL_UP = 51; // Windows XP SP1: Increment the channel value.
+ APPCOMMAND_MEDIA_CHANNEL_DOWN = 52; // Windows XP SP1: Decrement the channel value.
+
+function SendMMCommand(wnd:HWND; cmd:integer):integer;
+
+implementation
+
+const
+ WM_APPCOMMAND = $0319;
+
+function SendMMCommand(wnd:HWND; cmd:integer):integer;
+begin
+// result:=ord(SendMessageW(wnd,WM_APPCOMMAND,wnd,cmd shl 16));
+ result:=ord(SendnotifyMessageW(wnd,WM_APPCOMMAND,wnd,cmd shl 16));
+end;
+
+end.
diff --git a/plugins/Utils.pas/base64.pas b/plugins/Utils.pas/base64.pas
new file mode 100644
index 0000000000..1819efbbd5
--- /dev/null
+++ b/plugins/Utils.pas/base64.pas
@@ -0,0 +1,108 @@
+unit Base64;
+
+interface
+
+uses windows;
+
+{ Base64 encode and decode a string }
+function BASE64Encode(src:pByte;len:integer):PAnsiChar;
+function BASE64Decode(src:PAnsiChar;var dst:pByte):integer;
+
+{******************************************************************************}
+{******************************************************************************}
+implementation
+
+uses common;
+
+const
+ base64chars{:array [0..63] of AnsiChar}:PAnsiChar =
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+
+function BASE64Encode(src:pByte;len:integer):PAnsiChar;
+var
+ dst:PAnsiChar;
+begin
+ if (src=nil) or (len<=0) then
+ begin
+ result:=nil;
+ exit;
+ end;
+ mGetMem(result,((len*4+11) div (12*4))+1);
+ dst:=result;
+
+ while len>0 do
+ begin
+ dst^:=base64chars[src^ shr 2]; inc(dst);
+ if len=1 then
+ begin
+ dst^:=base64chars[(src^ and 3) shl 4]; inc(dst);
+ dst^:='='; inc(dst);
+ dst^:='='; inc(dst);
+ break;
+ end;
+ dst^:=base64chars[((src^ and 3) shl 4) or (pbyte(PAnsiChar(src)+1)^ shr 4)]; inc(dst); inc(src);
+ if len=2 then
+ begin
+ dst^:=base64chars[(src^ and $F) shl 2]; inc(dst);
+ dst^:='='; inc(dst);
+ break;
+ end;
+ dst^:=base64chars[((src^ and $F) shl 2) or (pbyte(PAnsiChar(src)+1)^ shr 6)]; inc(dst); inc(src);
+ dst^:=base64chars[src^ and $3F]; inc(dst); inc(src);
+ dec(len,3);
+ end;
+ dst^:=#0;
+end;
+
+function Base64CharToInt(c:AnsiChar):byte;
+begin
+ case c of
+ 'A'..'Z': result:=ord(c)-ord('A');
+ 'a'..'z': result:=ord(c)-ord('a')+26;
+ '0'..'9': result:=ord(c)-ord('0')+52;
+ '+': result:=62;
+ '/': result:=63;
+ '=': result:=64;
+ else
+ result:=255;
+ end;
+end;
+
+function BASE64Decode(src:PAnsiChar;var dst:pByte):integer;
+var
+ slen:integer;
+ ptr:pByte;
+ b1,b2,b3,b4:byte;
+begin
+ if (src=nil) or (src^=#0) then
+ begin
+ result:=0;
+ dst:=nil;
+ exit;
+ end;
+ pAnsiChar(ptr):=src;
+ while ptr^<>0 do inc(ptr);
+ slen:=PAnsiChar(ptr)-src;
+ mGetMem(ptr,(slen*3) div 4);
+ dst:=ptr;
+ result:=0;
+ while slen>0 do
+ begin
+ b1:=Base64CharToInt(src^); inc(src);
+ b2:=Base64CharToInt(src^); inc(src);
+ b3:=Base64CharToInt(src^); inc(src);
+ b4:=Base64CharToInt(src^); inc(src);
+ dec(slen,4);
+ if (b1=255) or (b1=64) or (b2=255) or (b2=64) or (b3=255) or (b4=255) then
+ break;
+ ptr^:=(b1 shl 2) or (b2 shr 4); inc(ptr); inc(result);
+ if b3=64 then
+ break;
+ ptr^:=(b2 shl 4) or (b3 shr 2); inc(ptr); inc(result);
+ if b4=64 then
+ break;
+ ptr^:=b4 or (b3 shl 6); inc(ptr); inc(result);
+ end;
+end;
+
+end.
diff --git a/plugins/Utils.pas/cbex.pas b/plugins/Utils.pas/cbex.pas
new file mode 100644
index 0000000000..1c683dd3b8
--- /dev/null
+++ b/plugins/Utils.pas/cbex.pas
@@ -0,0 +1,79 @@
+unit CBEx;
+interface
+
+uses windows;
+
+// build combobox with xstatus icons and names
+
+function AddCBEx(wnd:HWND;proto:PAnsiChar):HWND;
+
+implementation
+
+uses messages,commctrl,m_api,common;
+
+function AddCBEx(wnd:HWND;proto:PAnsiChar):HWND;
+var
+ cbei:TCOMBOBOXEXITEMW;
+ total,cnt:integer;
+ il:HIMAGELIST;
+ icon:HICON;
+ buf,buf1:array [0..127] of AnsiChar;
+ b:array [0..63] of WideChar;
+ ics:TICQ_CUSTOM_STATUS;
+begin
+ result:=0;
+ SendMessage(wnd,CB_RESETCONTENT,0,0);
+ StrCopy(StrCopyE(buf,proto),PS_ICQ_GETCUSTOMSTATUSICON);
+
+ if ServiceExists(@buf)=0 then
+ exit;
+
+ il:=ImageList_Create(16,16,ILC_COLOR32 or ILC_MASK,0,1);
+ if il=0 then exit;
+
+ cnt:=0;
+ StrCopy(StrCopyE(buf1,proto),PS_ICQ_GETCUSTOMSTATUSEX);
+
+ cbei.mask:=CBEIF_IMAGE or CBEIF_SELECTEDIMAGE or CBEIF_TEXT; //!!
+ ics.cbSize :=SizEOf(ics);
+ ics.flags :=CSSF_STATUSES_COUNT;
+ ics.szName.w:=@b;
+ ics.wParam :=@total;
+ CallService(buf1,0,lParam(@ics));
+ ics.flags :=CSSF_DEFAULT_NAME or CSSF_MASK_NAME or CSSF_UNICODE;
+
+ while cnt<=total do
+ begin
+ if cnt=0 then
+ begin
+ ImageList_AddIcon(il,CallService(MS_SKIN_LOADICON,SKINICON_OTHER_SMALLDOT,0));
+ cbei.pszText:=TranslateW('None');
+ end
+ else
+ begin
+ icon:=CallService(@buf,cnt,LR_SHARED);
+ if icon=0 then break;
+ if ImageList_AddIcon(il,icon)=-1 then break;
+ ics.wParam:=@cnt;
+ CallService(@buf1,0,lparam(@ics));
+ cbei.pszText:=TranslateW(@b);
+ end;
+ cbei.iItem :=cnt;
+ cbei.iImage :=cnt;
+ cbei.iSelectedImage:=cnt;
+ if SendMessageW(wnd,CBEM_INSERTITEMW,0,lparam(@cbei))=-1 then break;
+ inc(cnt);
+// DestroyIcon(icon);
+ end;
+
+ if cnt=0 then
+ ImageList_Destroy(il)
+ else
+ begin
+ ImageList_Destroy(SendMessage(wnd,CBEM_SETIMAGELIST,0,il));
+ SendMessage(wnd,CB_SETCURSEL,0,0);
+ result:=wnd;
+ end;
+end;
+
+end.
diff --git a/plugins/Utils.pas/common.pas b/plugins/Utils.pas/common.pas
new file mode 100644
index 0000000000..88df058957
--- /dev/null
+++ b/plugins/Utils.pas/common.pas
@@ -0,0 +1,2409 @@
+{$INCLUDE compilers.inc}
+{$IFDEF Miranda}
+ {.$DEFINE Use_MMI}
+{$ENDIF}
+unit common;
+
+interface
+
+uses
+windows
+{$IFDEF Miranda}
+,m_api
+{$ENDIF}
+;
+
+procedure ShowDump(ptr:pbyte;len:integer);
+
+Const {- Character sets -}
+ sBinNum = ['0'..'1'];
+ sOctNum = ['0'..'7'];
+ sNum = ['0'..'9'];
+ sHexNum = ['0'..'9','A'..'F','a'..'f'];
+ sWord = ['0'..'9','A'..'Z','a'..'z','_',#128..#255];
+ sIdFirst = ['A'..'Z','a'..'z','_'];
+ sLatWord = ['0'..'9','A'..'Z','a'..'z','_'];
+ sWordOnly = ['A'..'Z','a'..'z'];
+ sSpace = [#9,' '];
+ sEmpty = [#9,#10,#13,' '];
+
+const
+ HexDigitChrLo: array [0..15] of AnsiChar = ('0','1','2','3','4','5','6','7',
+ '8','9','a','b','c','d','e','f');
+
+ HexDigitChr : array [0..15] of AnsiChar = ('0','1','2','3','4','5','6','7',
+ '8','9','A','B','C','D','E','F');
+
+const
+ mimecnt = 5;
+ mimes:array [0..mimecnt-1] of record
+ mime:PAnsiChar;
+ ext:array [0..3] of AnsiChar
+ end = (
+ (mime:'image/gif' ; ext:'GIF'),
+ (mime:'image/jpg' ; ext:'JPG'),
+ (mime:'image/jpeg'; ext:'JPG'),
+ (mime:'image/png' ; ext:'PNG'),
+ (mime:'image/bmp' ; ext:'BMP')
+);
+
+var
+ IsW2K,
+ IsVista,
+ IsAnsi:boolean;
+
+const
+ CP_UNICODE = 1200;
+ CP_REVERSEBOM = 65534;
+const
+ SIGN_UNICODE = $FEFF;
+ SIGN_REVERSEBOM = $FFFE;
+ SIGN_UTF8 = $BFBBEF;
+
+function BSwap(value:dword):dword;
+
+function Hash(s:pointer; len:integer{const Seed: LongWord=$9747b28c}): LongWord;
+
+function Encode(dst,src:pAnsiChar):PAnsiChar;
+function Decode(dst,src:pAnsiChar):PAnsiChar;
+function GetTextFormat(Buffer:pByte;sz:cardinal):integer;
+
+function IIF(cond:bool;ret1,ret2:integer ):integer; overload;
+function IIF(cond:bool;ret1,ret2:PAnsiChar):PAnsiChar; overload;
+function IIF(cond:bool;ret1,ret2:pWideChar):pWideChar; overload;
+function IIF(cond:bool;ret1,ret2:Extended ):Extended; overload;
+function IIF(cond:bool;ret1,ret2:tDateTime):tDateTime; overload;
+function IIF(cond:bool;ret1,ret2:pointer ):pointer; overload;
+function IIF(cond:bool;const ret1,ret2:string):string; overload;
+{$IFNDEF DELPHI_7_UP}
+function IIF(cond:bool;ret1,ret2:variant ):variant; overload;
+{$ENDIF}
+
+function GetImageType (buf:pByte;mime:PAnsiChar=nil):dword;
+function GetImageTypeW(buf:pByte;mime:PWideChar=nil):int64;
+
+procedure CopyToClipboard(txt:pointer; Ansi:bool);
+function PasteFromClipboard(Ansi:boolean;cp:dword=CP_ACP):pointer;
+
+function mGetMem (var dst;size:integer):pointer;
+procedure mFreeMem(var ptr);
+function mReallocMem(var dst; size:integer):pointer;
+
+// String processing
+function WideToCombo(src:PWideChar;var dst;cp:integer=CP_ACP):integer;
+
+function ChangeUnicode(str:PWideChar):PWideChar;
+function UTF8Len(src:PAnsiChar):integer;
+function WideToAnsi(src:PWideChar;var dst:PAnsiChar;cp:dword=CP_ACP):PAnsiChar;
+function AnsiToWide(src:PAnsiChar;var dst:PWideChar;cp:dword=CP_ACP):PWideChar;
+function AnsiToUTF8(src:PAnsiChar;var dst:PAnsiChar;cp:dword=CP_ACP):PAnsiChar;
+function UTF8ToAnsi(src:PAnsiChar;var dst:PAnsiChar;cp:dword=CP_ACP):PAnsiChar;
+function UTF8ToWide(src:PAnsiChar;var dst:PWideChar;len:cardinal=cardinal(-1)):PWideChar;
+function WideToUTF8(src:PWideChar;var dst:PAnsiChar):PAnsiChar;
+
+function CharWideToUTF8(src:WideChar;var dst:pAnsiChar):integer;
+function CharUTF8ToWide(src:pAnsiChar;pin:pinteger=nil):WideChar;
+function CharUTF8Len(src:pAnsiChar):integer;
+
+function FastWideToAnsiBuf(src:PWideChar;dst:PAnsiChar;len:cardinal=cardinal(-1)):PAnsiChar;
+function FastAnsiToWideBuf(src:PAnsiChar;dst:PWideChar;len:cardinal=cardinal(-1)):PWideChar;
+function FastWideToAnsi (src:PWideChar;var dst:PAnsiChar):PAnsiChar;
+function FastAnsiToWide (src:PAnsiChar;var dst:PWideChar):PWideChar;
+
+function UnEscape(buf:PAnsiChar):PAnsiChar;
+function Escape (buf:PAnsiChar):PAnsiChar;
+
+// ----- base strings functions -----
+function StrDup (var dst:PAnsiChar;src:PAnsiChar;len:cardinal=0):PAnsiChar;
+function StrDupW(var dst:PWideChar;src:PWideChar;len:cardinal=0):PWideChar;
+function StrDelete (aStr:PAnsiChar;pos,len:cardinal):PAnsiChar;
+function StrDeleteW(aStr:PWideChar;pos,len:cardinal):PWideChar;
+function StrInsert (substr,src:PAnsiChar;pos:cardinal):PAnsiChar;
+function StrInsertW(substr,src:PWideChar;pos:cardinal):PWideChar;
+function StrReplace (src,SubStr,NewStr:PAnsiChar):PAnsiChar;
+function StrReplaceW(src,SubStr,NewStr:pWideChar):PWideChar;
+function CharReplace (dst:pAnsiChar;old,new:AnsiChar):PAnsiChar;
+function CharReplaceW(dst:pWideChar;old,new:WideChar):PWideChar;
+function StrCmp (a,b:PAnsiChar;n:cardinal=0):integer;
+function StrCmpW(a,b:PWideChar;n:cardinal=0):integer;
+function StrEnd (const a:PAnsiChar):PAnsiChar;
+function StrEndW(const a:PWideChar):PWideChar;
+function StrScan (src:PAnsiChar;c:AnsiChar):PAnsiChar;
+function StrScanW(src:PWideChar;c:WideChar):PWideChar;
+function StrRScan (src:PAnsiChar;c:AnsiChar):PAnsiChar;
+function StrRScanW(src:PWideChar;c:WideChar):PWideChar;
+function StrLen (Str: PAnsiChar): Cardinal;
+function StrLenW(Str: PWideChar): Cardinal;
+function StrCat (Dest: PAnsiChar; const Source: PAnsiChar): PAnsiChar;
+function StrCatW(Dest: PWideChar; const Source: PWideChar): PWideChar;
+function StrCatE (Dest: PAnsiChar; const Source: PAnsiChar): PAnsiChar;
+function StrCatEW(Dest: PWideChar; const Source: PWideChar): PWideChar;
+function StrCopyE (dst:PAnsiChar;src:PAnsiChar;len:cardinal=0):PAnsiChar;
+function StrCopyEW(dst:PWideChar;src:PWideChar;len:cardinal=0):PWideChar;
+function StrCopy (dst:PAnsiChar;src:PAnsiChar;len:cardinal=0):PAnsiChar;
+function StrCopyW(dst:PWideChar;src:PWideChar;len:cardinal=0):PWideChar;
+function StrPos (const aStr, aSubStr: PAnsiChar): PAnsiChar;
+function StrPosW(const aStr, aSubStr: PWideChar): PWideChar;
+function StrIndex (const aStr, aSubStr: PAnsiChar):integer;
+function StrIndexW(const aStr, aSubStr: PWideChar):integer;
+
+procedure FillWord(var buf;count:cardinal;value:word); register;
+function CompareMem(P1, P2: Pointer; Length: Integer): Boolean;
+
+function Min(a,b:integer):integer;
+function Max(a,b:integer):integer;
+
+function Timestamp(Year,Month,Day:cardinal;Hour:cardinal=0;Minute:cardinal=0;Sec:cardinal=0):dword;
+function GetCurrentTime:dword;
+
+function TimeToInt(stime:PAnsiChar):integer; overload;
+function TimeToInt(stime:PWideChar):integer; overload;
+function IntToTime(dst:pWideChar;time:integer):pWideChar; overload;
+function IntToTime(dst:PAnsiChar;time:integer):PAnsiChar; overload;
+
+{
+ filesize to string conversion
+ value - filelength
+ divider - 1=byte; 1024=kbyte; 1024*1024 - Mbyte
+ prec - numbers after point (1-3)
+ post - 0=none
+ 1=(small)' bytes','kb','mb'
+ 2=(mix) ' Bytes','Kb','Mb'
+ 3=(caps) '' ,'KB','MB'
+ postfix calculated from 'divider' value
+}
+function IntToK(dst:pWideChar;value,divider,prec,post:integer):pWideChar;
+
+// string conversion
+function IntToHex(dst:pWideChar;Value:int64;Digits:integer=0):pWideChar; overload;
+function IntToHex(dst:PAnsiChar;Value:int64;Digits:integer=0):PAnsiChar; overload;
+function IntToStr(dst:pWideChar;Value:int64;Digits:integer=0):pWideChar; overload;
+function IntToStr(dst:PAnsiChar;Value:int64;Digits:integer=0):PAnsiChar; overload;
+function StrToInt(src:pWideChar):int64; overload;
+function StrToInt(src:PAnsiChar):int64; overload;
+function HexToInt(src:pWideChar;len:cardinal=$FFFF):int64; overload;
+function HexToInt(src:PAnsiChar;len:cardinal=$FFFF):int64; overload;
+
+// filename work
+function ChangeExt (src,ext:PAnsiChar):PAnsiChar;
+function ChangeExtW(src,ext:PWideChar):PWideChar;
+function Extract (s:PAnsiChar;name:Boolean=true):PAnsiChar;
+function ExtractW(s:pWideChar;name:Boolean=true):pWideChar;
+function GetExt(fname,dst:pWideChar;maxlen:dword=100):pWideChar; overload;
+function GetExt(fname,dst:PAnsiChar;maxlen:dword=100):PAnsiChar; overload;
+
+procedure UpperCase(src:pWideChar);
+procedure LowerCase(src:pWideChar);
+function GetPairChar(ch:AnsiChar):AnsiChar; overload;
+function GetPairChar(ch:WideChar):WideChar; overload;
+
+type
+ tSortProc = function (First,Second:integer):integer;
+ {0=equ; 1=1st>2nd; -1=1st<2nd }
+procedure ShellSort(size:integer;Compare,Swap:tSortProc);
+
+function isPathAbsolute(path:pWideChar):boolean; overload;
+function isPathAbsolute(path:PAnsiChar):boolean; overload;
+
+implementation
+
+// Murmur 2.0
+function Hash(s:pointer; len:integer{const Seed: LongWord=$9747b28c}): LongWord;
+var
+ hash: LongWord;
+ k: LongWord;
+ tmp,data: pByte;
+const
+ // 'm' and 'r' are mixing constants generated offline.
+ // They're not really 'magic', they just happen to work well.
+ m = $5bd1e995;
+ r = 24;
+begin
+ //The default seed, $9747b28c, is from the original C library
+
+ // Initialize the hash to a 'random' value
+ hash := {seed xor }len;
+
+ // Mix 4 bytes at a time into the hash
+ data := s;
+
+ while(len >= 4) do
+ begin
+ k := PLongWord(data)^;
+
+ k := k*m;
+ k := k xor (k shr r);
+ k := k*m;
+
+ hash := hash*m;
+ hash := hash xor k;
+
+ inc(data,4);
+ dec(len,4);
+ end;
+
+ // Handle the last few bytes of the input array
+ if len = 3 then
+ begin
+ tmp:=data;
+ inc(tmp,2);
+ hash := hash xor (LongWord(tmp^) shl 16);
+ end;
+ if len >= 2 then
+ begin
+ tmp:=data;
+ inc(tmp);
+ hash := hash xor (LongWord(tmp^) shl 8);
+ end;
+ if len >= 1 then
+ begin
+ hash := hash xor (LongWord(data^));
+ hash := hash * m;
+ end;
+
+ // Do a few final mixes of the hash to ensure the last few
+ // bytes are well-incorporated.
+ hash := hash xor (hash shr 13);
+ hash := hash * m;
+ hash := hash xor (hash shr 15);
+
+ Result := hash;
+end;
+
+function BSwap(value:dword):dword;
+ {$IFNDEF WIN64}
+begin
+ asm
+ mov eax,value
+ bswap eax
+ mov result,eax
+ end;
+ {$ELSE}
+begin
+ result:=((value and $000000FF) shl 6) +
+ ((value and $0000FF00) shl 2) +
+ ((value and $00FF0000) shr 2) +
+ ((value and $FF000000) shr 6);
+ {$ENDIF}
+end;
+
+function Encode(dst,src:pAnsiChar):PAnsiChar;
+begin
+ while src^<>#0 do
+ begin
+ if not (src^ in [' ','%','+','&','?',#128..#255]) then
+ dst^:=src^
+ else
+ begin
+ dst^:='%'; inc(dst);
+ dst^:=HexDigitChr[ord(src^) shr 4]; inc(dst);
+ dst^:=HexDigitChr[ord(src^) and $0F];
+ end;
+ inc(src);
+ inc(dst);
+ end;
+ dst^:=#0;
+ result:=dst;
+end;
+
+function Decode(dst,src:pAnsiChar):PAnsiChar;
+begin
+ while (src^<>#0) and (src^<>'&') do
+ begin
+ if (src^='%') and ((src+1)^ in sHexNum) and ((src+2)^ in sHexNum) then
+ begin
+ inc(src);
+ dst^:=AnsiChar(HexToInt(src,2));
+ inc(src);
+ end
+ else
+ dst^:=src^;
+ inc(dst);
+ inc(src);
+ end;
+ dst^:=#0;
+ result:=dst;
+end;
+
+const
+ IS_TEXT_UNICODE_ASCII16 = $1;
+ IS_TEXT_UNICODE_REVERSE_ASCII16 = $10;
+ IS_TEXT_UNICODE_STATISTICS = $2;
+ IS_TEXT_UNICODE_REVERSE_STATISTICS = $20;
+ IS_TEXT_UNICODE_CONTROLS = $4;
+ IS_TEXT_UNICODE_REVERSE_CONTROLS = $40;
+ IS_TEXT_UNICODE_SIGNATURE = $8;
+ IS_TEXT_UNICODE_REVERSE_SIGNATURE = $80;
+ IS_TEXT_UNICODE_ILLEGAL_CHARS = $100;
+ IS_TEXT_UNICODE_ODD_LENGTH = $200;
+ IS_TEXT_UNICODE_DBCS_LEADBYTE = $400;
+ IS_TEXT_UNICODE_NULL_BYTES = $1000;
+ IS_TEXT_UNICODE_UNICODE_MASK = $F;
+ IS_TEXT_UNICODE_REVERSE_MASK = $F0;
+ IS_TEXT_UNICODE_NOT_UNICODE_MASK = $F00;
+ IS_TEXT_UNICODE_NOT_ASCII_MASK = $F000;
+
+function IsTextUTF8(Buffer:pbyte;Length:integer):boolean;
+var
+ Ascii:boolean;
+ Octets:cardinal;
+ c:byte;
+begin
+ Ascii:=true;
+ Octets:=0;
+
+ if Length=0 then
+ Length:=-1;
+ repeat
+ if (Length=0) or (Buffer^=0) then
+ break;
+ dec(Length);
+ c:=Buffer^;
+ if (c and $80)<>0 then
+ Ascii:=false;
+ if Octets<>0 then
+ begin
+ if (c and $C0)<>$80 then
+ begin
+ result:=false;
+ exit;
+ end;
+ dec(Octets);
+ end
+ else
+ begin
+ if (c and $80)<>0 then
+ begin
+ while (c and $80)<>0 do
+ begin
+ c:=c shl 1;
+ inc(Octets);
+ end;
+ dec(Octets);
+ if Octets=0 then
+ begin
+ result:=false;
+ exit;
+ end;
+ end
+ end;
+ inc(Buffer);
+ until false;
+ result:= not ((Octets>0) or Ascii);
+end;
+
+function GetTextFormat(Buffer:pByte;sz:cardinal):integer;
+var
+ test:integer;
+begin
+ result:=-1;
+
+ if sz>=2 then
+ begin
+ if pword (Buffer)^ =SIGN_UNICODE then result := CP_UNICODE
+ else if pword (Buffer)^ =SIGN_REVERSEBOM then result := CP_REVERSEBOM
+ else if (sz>=4) and
+ ((pdword(Buffer)^ and $00FFFFFF)=SIGN_UTF8) then result := CP_UTF8;
+ end;
+
+ if result<0 then
+ begin
+ test:=
+ IS_TEXT_UNICODE_STATISTICS or
+ IS_TEXT_UNICODE_REVERSE_STATISTICS or
+ IS_TEXT_UNICODE_CONTROLS or
+ IS_TEXT_UNICODE_REVERSE_CONTROLS or
+ IS_TEXT_UNICODE_ILLEGAL_CHARS or
+ IS_TEXT_UNICODE_ODD_LENGTH or
+ IS_TEXT_UNICODE_NULL_BYTES;
+
+ if not odd(sz) and IsTextUnicode(Buffer,sz,@test) then
+ begin
+ if (test and (IS_TEXT_UNICODE_ODD_LENGTH or IS_TEXT_UNICODE_ILLEGAL_CHARS))=0 then
+ begin
+ if (test and (IS_TEXT_UNICODE_NULL_BYTES or
+ IS_TEXT_UNICODE_CONTROLS or
+ IS_TEXT_UNICODE_REVERSE_CONTROLS))<>0 then
+ begin
+ if (test and (IS_TEXT_UNICODE_CONTROLS or
+ IS_TEXT_UNICODE_STATISTICS))<>0 then
+ result:=CP_UNICODE
+ else if (test and (IS_TEXT_UNICODE_REVERSE_CONTROLS or
+ IS_TEXT_UNICODE_REVERSE_STATISTICS))<>0 then
+ result:=CP_REVERSEBOM;
+ end
+ end
+ end
+ else if IsTextUTF8(Buffer,sz) then
+ result:=CP_UTF8
+ else
+ result:=CP_ACP;
+ end;
+end;
+
+function IIF(cond:bool;ret1,ret2:integer):integer; overload;
+begin
+ if cond then result:=ret1 else result:=ret2;
+end;
+function IIF(cond:bool;ret1,ret2:PAnsiChar):PAnsiChar; overload;
+begin
+ if cond then result:=ret1 else result:=ret2;
+end;
+function IIF(cond:bool;ret1,ret2:pWideChar):pWideChar; overload;
+begin
+ if cond then result:=ret1 else result:=ret2;
+end;
+function IIF(cond:bool;ret1,ret2:Extended):Extended; overload;
+begin
+ if cond then result:=ret1 else result:=ret2;
+end;
+function IIF(cond:bool;ret1,ret2:tDateTime):tDateTime; overload;
+begin
+ if cond then result:=ret1 else result:=ret2;
+end;
+function IIF(cond:bool;ret1,ret2:pointer):pointer; overload;
+begin
+ if cond then result:=ret1 else result:=ret2;
+end;
+function IIF(cond:bool;const ret1,ret2:string):string; overload;
+begin
+ if cond then result:=ret1 else result:=ret2;
+end;
+{$IFNDEF DELPHI_7_UP}
+function IIF(cond:bool;ret1,ret2:variant):variant; overload;
+begin
+ if cond then result:=ret1 else result:=ret2;
+end;
+{$ENDIF}
+
+function GetImageType(buf:pByte;mime:PAnsiChar=nil):dword;
+var
+ i:integer;
+begin
+ result:=0;
+ if (mime<>nil) and (mime^<>#0) then
+ begin
+ for i:=0 to mimecnt-1 do
+ begin
+ if {lstrcmpia}StrCmp(mime,mimes[i].mime)=0 then
+ begin
+ result:=dword(mimes[i].ext);
+ exit;
+ end;
+ end;
+ end
+ else if buf<>nil then
+ begin
+ if (pdword(buf)^ and $F0FFFFFF)=$E0FFD8FF then result:=$0047504A // 'JPG'
+ else if pdword(buf)^=$38464947 then result:=$00464947 // 'GIF'
+ else if pdword(buf)^=$474E5089 then result:=$00474E50 // 'PNG'
+ else if pword (buf)^=$4D42 then result:=$00504D42 // 'BMP'
+ end;
+end;
+
+function GetImageTypeW(buf:pByte;mime:PWideChar=nil):int64;
+var
+ i:integer;
+ lmime:array [0..63] of AnsiChar;
+begin
+ result:=0;
+ if (mime<>nil) and (mime^<>#0) then
+ begin
+ FastWideToAnsiBuf(mime,lmime);
+ for i:=0 to mimecnt-1 do
+ begin
+ if {lstrcmpia}StrCmp(lmime,mimes[i].mime)=0 then
+ begin
+// result:=dword(mimes[i].ext);
+ FastAnsiToWideBuf(mimes[i].ext,PWideChar(@result));
+ exit;
+ end;
+ end;
+ end
+ else if buf<>nil then
+ begin
+ if (pdword(buf)^ and $F0FFFFFF)=$E0FFD8FF then result:=$000000470050004A // 'JPG'
+ else if pdword(buf)^=$38464947 then result:=$0000004600490047 // 'GIF'
+ else if pdword(buf)^=$474E5089 then result:=$00000047004E0050 // 'PNG'
+ else if pword (buf)^=$4D42 then result:=$00000050004D0042 // 'BMP'
+ end;
+end;
+
+procedure CopyToClipboard(txt:pointer; Ansi:bool);
+var
+ s:pointer;
+ fh:THANDLE;
+begin
+ if pointer(txt)=nil then
+ exit;
+ if Ansi then
+ begin
+ if PAnsiChar(txt)^=#0 then exit
+ end
+ else
+ if PWideChar(txt)^=#0 then exit;
+
+ if OpenClipboard(0) then
+ begin
+ if Ansi then
+ begin
+ fh:=GlobalAlloc(GMEM_MOVEABLE+GMEM_DDESHARE,(StrLen(PAnsiChar(txt))+1));
+ s:=GlobalLock(fh);
+ StrCopy(s,PAnsiChar(txt));
+ end
+ else
+ begin
+ fh:=GlobalAlloc(GMEM_MOVEABLE+GMEM_DDESHARE,
+ (StrLenW(PWideChar(txt))+1)*SizeOf(WideChar));
+ s:=GlobalLock(fh);
+ StrCopyW(s,PWideChar(txt));
+ end;
+ GlobalUnlock(fh);
+ EmptyClipboard;
+ if Ansi then
+ SetClipboardData(CF_TEXT,fh)
+ else
+ SetClipboardData(CF_UNICODETEXT,fh);
+ CloseClipboard;
+ end;
+end;
+
+function PasteFromClipboard(Ansi:boolean;cp:dword=CP_ACP):pointer;
+var
+ p:pWideChar;
+ fh:tHandle;
+begin
+ result:=nil;
+ if OpenClipboard(0) then
+ begin
+ if not Ansi then
+ begin
+ fh:=GetClipboardData(CF_UNICODETEXT);
+ if fh<>0 then
+ begin
+ p:=GlobalLock(fh);
+ StrDupW(pWideChar(result),p);
+ end
+ else
+ begin
+ fh:=GetClipboardData(CF_TEXT);
+ if fh<>0 then
+ begin
+ p:=GlobalLock(fh);
+ AnsiToWide(PAnsiChar(p),pWideChar(result),cp);
+ end;
+ end;
+ end
+ else
+ begin
+ fh:=GetClipboardData(CF_TEXT);
+ if fh<>0 then
+ begin
+ p:=GlobalLock(fh);
+ StrDup(PAnsiChar(result),PAnsiChar(p));
+ end;
+ end;
+ if fh<>0 then
+ GlobalUnlock(fh);
+ CloseClipboard;
+ end
+end;
+
+procedure CheckSystem;
+var
+ ovi:TOSVersionInfo;
+begin
+ ovi.dwOSVersionInfoSize:=SizeOf(TOSVersionInfo);
+ GetVersionEx(ovi);
+//VER_PLATFORM_WIN32_NT for 2KXP
+ with ovi do
+ begin
+ IsAnsi :=dwPlatformId=VER_PLATFORM_WIN32_WINDOWS;
+ IsW2K :=(dwMajorVersion=5) and (dwMinorVersion=0);
+ IsVista:=(dwMajorVersion=6) and (dwMinorVersion=0);
+ end;
+end;
+
+// --------- string conversion ----------
+
+function WideToCombo(src:PWideChar;var dst;cp:integer=CP_ACP):integer;
+var
+ pc:PAnsiChar;
+ i,j:Cardinal;
+begin
+ WideToAnsi(src,pc,cp);
+ j:=StrLen(pc)+1;
+ i:=j+(StrLenW(src)+1)*SizeOf(WideChar);
+ mGetMem(PAnsiChar(dst),i);
+ StrCopy(PAnsiChar(dst),pc);
+ mFreeMem(pc);
+ StrCopyW(pWideChar(PAnsiChar(dst)+j),src);
+ result:=i;
+end;
+
+function ChangeUnicode(str:PWideChar):PWideChar;
+var
+ i,len:integer;
+begin
+ result:=str;
+ if (str=nil) or (str^=#0) then
+ exit;
+ if (word(str^)=$FFFE) or (word(str^)=$FEFF) then
+ begin
+ len:=StrLenW(str);
+ if word(str^)=$FFFE then
+ begin
+ i:=len-1;
+ while i>0 do // str^<>#0
+ begin
+ pword(str)^:=swap(pword(str)^);
+ inc(str);
+ dec(i);
+ end;
+ end;
+ move((result+1)^,result^,len*SizeOf(WideChar));
+ end;
+end;
+
+function WideToAnsi(src:PWideChar;var dst:PAnsiChar; cp:dword=CP_ACP):PAnsiChar;
+var
+ len,l:integer;
+begin
+ if (src=nil) or (src^=#0) then
+ begin
+ mGetMem(result,SizeOf(AnsiChar));
+ result^:=#0;
+ end
+ else
+ begin
+ l:=StrLenW(src);
+ len:=WideCharToMultiByte(cp,0,src,l,NIL,0,NIL,NIL)+1;
+ mGetMem(result,len);
+ FillChar(result^,len,0);
+ WideCharToMultiByte(cp,0,src,l,result,len,NIL,NIL);
+ end;
+ dst:=result;
+end;
+
+function AnsiToWide(src:PAnsiChar;var dst:PWideChar; cp:dword=CP_ACP):PWideChar;
+var
+ len,l:integer;
+begin
+ if (src=nil) or (src^=#0) then
+ begin
+ mGetMem(result,SizeOf(WideChar));
+ result^:=#0;
+ end
+ else
+ begin
+ l:=StrLen(src);
+ len:=MultiByteToWideChar(cp,0,src,l,NIL,0)+1;
+ mGetMem(result,len*SizeOf(WideChar));
+ FillChar(result^,len*SizeOf(WideChar),0);
+ MultiByteToWideChar(cp,0,src,l,result,len);
+ end;
+ dst:=result;
+end;
+
+function AnsiToUTF8(src:PAnsiChar;var dst:PAnsiChar;cp:dword=CP_ACP):PAnsiChar;
+var
+ tmp:PWideChar;
+begin
+ AnsiToWide(src,tmp,cp);
+ result:=WideToUTF8(tmp,dst);
+ mFreeMem(tmp);
+end;
+
+function UTF8Len(src:PAnsiChar):integer; // w/o zero
+begin
+ result:=0;
+ if src<>nil then
+ begin
+ if (pdword(src)^ and $00FFFFFF)=SIGN_UTF8 then
+ inc(src,3);
+ while src^<>#0 do
+ begin
+ if (ord(src^) and $80)=0 then
+ else if (ord(src^) and $E0)=$E0 then
+ inc(src,2)
+ else
+ inc(src);
+ inc(result);
+ inc(src);
+ end;
+ end;
+end;
+
+function CalcUTF8Len(src:pWideChar):integer;
+begin
+ result:=0;
+ if src<>nil then
+ begin
+ while src^<>#0 do
+ begin
+ if src^<#$0080 then
+ else if src^<#$0800 then
+ inc(result)
+ else
+ inc(result,2);
+ inc(src);
+ inc(result);
+ end;
+ end;
+end;
+
+function CharWideToUTF8(src:WideChar;var dst:pAnsiChar):integer;
+begin
+ if src<#$0080 then
+ begin
+ dst^:=AnsiChar(src);
+ result:=1;
+ end
+ else if src<#$0800 then
+ begin
+ dst^:=AnsiChar($C0 or (ord(src) shr 6));
+ inc(dst);
+ dst^:=AnsiChar($80 or (ord(src) and $3F));
+ result:=2;
+ end
+ else
+ begin
+ dst^:=AnsiChar($E0 or (ord(src) shr 12));
+ inc(dst);
+ dst^:=AnsiChar($80 or ((ord(src) shr 6) and $3F));
+ inc(dst);
+ dst^:=AnsiChar($80 or (ord(src) and $3F));
+ result:=3;
+ end;
+ inc(dst); dst^:=#0;
+end;
+
+function CharUTF8ToWide(src:pAnsiChar;pin:pinteger=nil):WideChar;
+var
+ cnt:integer;
+ w:word;
+begin
+ if ord(src^)<$80 then
+ begin
+ w:=ord(src^);
+ cnt:=1;
+ end
+ else if (ord(src^) and $E0)=$E0 then
+ begin
+ w:=(ord(src^) and $1F) shl 12;
+ inc(src);
+ w:=w or (((ord(src^))and $3F) shl 6);
+ inc(src);
+ w:=w or (ord(src^) and $3F);
+ cnt:=3;
+ end
+ else
+ begin
+ w:=(ord(src^) and $3F) shl 6;
+ inc(src);
+ w:=w or (ord(src^) and $3F);
+ cnt:=2;
+ end;
+ if pin<>nil then
+ pin^:=cnt;
+ result:=WideChar(w);
+end;
+
+function CharUTF8Len(src:pAnsiChar):integer;
+begin
+{!!}
+ if (ord(src^) and $80)=0 then
+ result:=1
+ else if (ord(src^) and $E0)=$E0 then
+ result:=3
+ else
+ result:=2;
+{}
+end;
+
+function UTF8ToWide(src:PAnsiChar; var dst:PWideChar; len:cardinal=cardinal(-1)):PWideChar;
+var
+ w:word;
+ p:PWideChar;
+begin
+ mGetMem(dst,(UTF8Len(src)+1)*SizeOf(WideChar));
+ p:=dst;
+ if src<>nil then
+ begin
+ if (pdword(src)^ and $00FFFFFF)=SIGN_UTF8 then
+ inc(src,3);
+ while (src^<>#0) and (len>0) do
+ begin
+ if ord(src^)<$80 then
+ w:=ord(src^)
+ else if (ord(src^) and $E0)=$E0 then
+ begin
+ w:=(ord(src^) and $1F) shl 12;
+ inc(src); dec(len);
+ w:=w or (((ord(src^))and $3F) shl 6);
+ inc(src); dec(len);
+ w:=w or (ord(src^) and $3F);
+ end
+ else
+ begin
+ w:=(ord(src^) and $3F) shl 6;
+ inc(src); dec(len);
+ w:=w or (ord(src^) and $3F);
+ end;
+ p^:=WideChar(w);
+ inc(p);
+ inc(src); dec(len);
+ end;
+ end;
+ p^:=#0;
+ result:=dst;
+end;
+
+function UTF8ToAnsi(src:PAnsiChar;var dst:PAnsiChar;cp:dword=CP_ACP):PAnsiChar;
+var
+ tmp:pWideChar;
+begin
+ UTF8ToWide(src,tmp);
+ result:=WideToAnsi(tmp,dst,cp);
+ mFreeMem(tmp);
+end;
+
+function WidetoUTF8(src:PWideChar; var dst:PAnsiChar):PAnsiChar;
+var
+ p:PAnsiChar;
+begin
+ mGetMem(dst,CalcUTF8Len(src)+1);
+ p:=dst;
+ if src<>nil then
+ begin
+ while src^<>#0 do
+ begin
+ if src^<#$0080 then
+ p^:=AnsiChar(src^)
+ else if src^<#$0800 then
+ begin
+ p^:=AnsiChar($C0 or (ord(src^) shr 6));
+ inc(p);
+ p^:=AnsiChar($80 or (ord(src^) and $3F));
+ end
+ else
+ begin
+ p^:=AnsiChar($E0 or (ord(src^) shr 12));
+ inc(p);
+ p^:=AnsiChar($80 or ((ord(src^) shr 6) and $3F));
+ inc(p);
+ p^:=AnsiChar($80 or (ord(src^) and $3F));
+ end;
+ inc(p);
+ inc(src);
+ end;
+ end;
+ p^:=#0;
+ result:=dst;
+end;
+
+procedure FillWord(var buf;count:cardinal;value:word); register;
+{$IFNDEF WIN64}assembler;
+{
+ PUSH EDI
+ MOV EDI, ECX // Move Value To Write
+ MOV ECX, EDX // Move Number to ECX for countdown
+ MOV EDX, EAX // Move over buffer
+ MOV EAX, EDI // Value to Write needs to be here
+ MOV EDI, EDX // Pointer to Buffer[0]
+ REP STOSW
+ POP EDI
+}
+asm
+ push edi
+ mov edi,buf // destination
+ mov ax,value // value
+ mov ecx,count // count
+ rep stosw
+ pop edi
+{
+ push edi
+ mov edi,eax // destination
+ mov ax,cx // value
+ mov ecx,edx // count
+ rep stosw
+ pop edi
+}
+end;
+{$ELSE}
+var
+ ptr:pword;
+ i:integer;
+begin
+ ptr:=pword(@buf);
+ for i:=0 to count-1 do
+ begin
+ ptr^:=value;
+ inc(ptr);
+ end;
+end;
+{$ENDIF}
+// from SysUtils
+{ Delphi 7.0
+function CompareMem(P1, P2: Pointer; Length: Integer): Boolean; assembler;
+asm
+ PUSH ESI
+ PUSH EDI
+ MOV ESI,P1
+ MOV EDI,P2
+ MOV EDX,ECX
+ XOR EAX,EAX
+ AND EDX,3
+ SAR ECX,2
+ JS @@1 // Negative Length implies identity.
+ REPE CMPSD
+ JNE @@2
+ MOV ECX,EDX
+ REPE CMPSB
+ JNE @@2
+@@1: INC EAX
+@@2: POP EDI
+ POP ESI
+end;
+}
+{$IFNDEF WIN64}
+// Delphi 2009 realization
+function CompareMem(P1, P2: Pointer; Length: Integer): Boolean; assembler;
+asm
+ add eax, ecx
+ add edx, ecx
+ xor ecx, -1
+ add eax, -8
+ add edx, -8
+ add ecx, 9
+ push ebx
+ jg @Dword
+ mov ebx, [eax+ecx]
+ cmp ebx, [edx+ecx]
+ jne @Ret0
+ lea ebx, [eax+ecx]
+ add ecx, 4
+ and ebx, 3
+ sub ecx, ebx
+ jg @Dword
+@DwordLoop:
+ mov ebx, [eax+ecx]
+ cmp ebx, [edx+ecx]
+ jne @Ret0
+ mov ebx, [eax+ecx+4]
+ cmp ebx, [edx+ecx+4]
+ jne @Ret0
+ add ecx, 8
+ jg @Dword
+ mov ebx, [eax+ecx]
+ cmp ebx, [edx+ecx]
+ jne @Ret0
+ mov ebx, [eax+ecx+4]
+ cmp ebx, [edx+ecx+4]
+ jne @Ret0
+ add ecx, 8
+ jle @DwordLoop
+@Dword:
+ cmp ecx, 4
+ jg @Word
+ mov ebx, [eax+ecx]
+ cmp ebx, [edx+ecx]
+ jne @Ret0
+ add ecx, 4
+@Word:
+ cmp ecx, 6
+ jg @Byte
+ movzx ebx, word ptr [eax+ecx]
+ cmp bx, [edx+ecx]
+ jne @Ret0
+ add ecx, 2
+@Byte:
+ cmp ecx, 7
+ jg @Ret1
+ movzx ebx, byte ptr [eax+7]
+ cmp bl, [edx+7]
+ jne @Ret0
+@Ret1:
+ mov eax, 1
+ pop ebx
+ ret
+@Ret0:
+ xor eax, eax
+ pop ebx
+end;
+{$ELSE}
+function CompareMem(P1, P2: Pointer; Length: Integer): Boolean;
+ {$IFNDEF COMPILER_16_UP}
+begin
+ result:=CompareByte(P1,P2,Length)=0;
+ {$ELSE}
+var
+ i:integer;
+begin
+ for i:=0 to Length-1 do
+ begin
+ if pByte(p1)^<>pbyte(p2)^ then
+ begin
+ result:=false;
+ exit;
+ end;
+ inc(pbyte(p1));
+ inc(pbyte(p2));
+ end;
+ result:=true;
+ {$ENDIF}
+end;
+{$ENDIF}
+
+function Min(a,b:integer):integer;
+begin
+ if a>b then
+ result:=b
+ else
+ result:=a;
+end;
+
+function Max(a,b:integer):integer;
+begin
+ if a<b then
+ result:=b
+ else
+ result:=a;
+end;
+
+function mGetMem(var dst;size:integer):pointer;
+begin
+{$IFDEF Use_MMI}
+ pointer(dst):=mir_alloc(size)
+{$ELSE}
+ GetMem(pointer(dst),size);
+{$ENDIF}
+ result:=pointer(dst);
+end;
+
+procedure mFreeMem(var ptr);
+begin
+ if pointer(ptr)<>nil then
+ begin
+{$IFDEF UseMMI}
+ mir_free(pointer(ptr))
+{$ELSE}
+ FreeMem(pointer(ptr));
+{$ENDIF}
+ Pointer(ptr):=nil;
+ end;
+end;
+
+function mReallocMem(var dst; size:integer):pointer;
+begin
+{$IFDEF Use_MMI}
+ pointer(dst):=mir_realloc(pointer(dst),size)
+{$ELSE}
+ ReallocMem(pointer(dst),size);
+{$ENDIF}
+ result:=pointer(dst);
+end;
+
+function UnEscape(buf:PAnsiChar):PAnsiChar;
+begin
+ if (buf<>nil) and (buf^<>#0) then
+ begin
+ StrReplace(buf,PAnsiChar(#$7F'n'),PAnsiChar(#$0D#$0A));
+ StrReplace(buf,PAnsiChar(#$7F't'),PAnsiChar(#$09));
+ end;
+ result:=buf;
+end;
+
+function Escape(buf:PAnsiChar):PAnsiChar;
+var
+ i:integer;
+begin
+ i:=StrLen(buf);
+ if i<>0 then
+ begin
+ Move(buf^,(buf+1)^,i+1);
+ buf^:=#39;
+ (buf+i+1)^:=#39;
+ (buf+i+2)^:=#0;
+ StrReplace(buf,#$0D#$0A,#$7F'n');
+ StrReplace(buf,#$09,#$7F't');
+ end;
+ result:=buf;
+end;
+
+procedure ShellSort(size:integer;Compare,Swap:tSortProc);
+var
+ i,j,gap:longint;
+begin
+ gap:=size shr 1;
+ while gap>0 do
+ begin
+ for i:=gap to size-1 do
+ begin
+ j:=i-gap;
+ while (j>=0) and (Compare(j,UInt(j+gap))>0) do
+ begin
+ Swap(j,UInt(j+gap));
+ dec(j,gap);
+ end;
+ end;
+ gap:=gap shr 1;
+ end;
+end;
+
+const
+ Posts:array [0..8] of PWideChar =
+ (' bytes',' Bytes','','kb','Kb','KB','mb','Mb','MB');
+
+function IntToK(dst:pWidechar;value,divider,prec,post:integer):pWidechar;
+var
+ tmp:integer;
+ p:pWideChar;
+ ls:array [0..4] of WideChar;
+begin
+ result:=dst;
+ IntToStr(dst,value div divider);
+ if divider=1 then prec:=0;
+ while dst^<>#0 do inc(dst);
+ if prec<>0 then
+ begin
+ if prec=1 then prec:=10
+ else if prec=2 then prec:=100
+ else {if prec=3 then} prec:=1000;
+ tmp:=round(frac(value*1.0/divider)*prec);
+ dst^:='.'; inc(dst);
+ IntToStr(ls,tmp);
+ p:=ls;
+ while p^<>#0 do
+ begin
+ dst^:=p^; inc(dst); inc(p);
+ end;
+ dst^:=#0;
+ end;
+ if post<>0 then
+ begin
+ if divider=1 then
+ StrCatW(dst,Posts[post-1])
+ else
+ begin
+ if divider=1024 then tmp:=1
+ else {if divider=1024*1024 then} tmp:=2;
+ p:=Posts[tmp*3+post-1];
+ dst^:=p[0]; inc(dst);
+ dst^:=p[1]; inc(dst);
+ dst^:=#0;
+ end;
+ end;
+end;
+
+// ----- base string functions -----
+function StrDup(var dst:PAnsiChar;src:PAnsiChar;len:cardinal=0):PAnsiChar;
+var
+ l:cardinal;
+ p:pAnsiChar;
+begin
+ if (src=nil) or (src^=#0) then
+ dst:=nil
+ else
+ begin
+ if len=0 then
+ len:=high(cardinal);
+ p:=src;
+ l:=len;
+ while (p^<>#0) and (l>0) do
+ begin
+ inc(p); dec(l);
+ end;
+ l:=p-src;
+
+ mGetMem(dst,l+1);
+ move(src^, dst^,l);
+ dst[l]:=#0;
+ end;
+ result:=dst;
+end;
+
+function StrDupW(var dst:PWideChar;src:PWideChar;len:cardinal=0):PWideChar;
+var
+ l:cardinal;
+ p:pWideChar;
+begin
+ if (src=nil) or (src^=#0) then
+ dst:=nil
+ else
+ begin
+ if len=0 then
+ len:=high(cardinal);
+ p:=src;
+ l:=len;
+ while (p^<>#0) and (l>0) do
+ begin
+ inc(p); dec(l);
+ end;
+ l:=p-src;
+ mGetMem(dst,(l+1)*SizeOf(WideChar));
+ move(src^, dst^,l*SizeOf(WideChar));
+ dst[l]:=#0;
+ end;
+ result:=dst;
+end;
+
+function StrCopyE(dst:PAnsiChar;src:PAnsiChar;len:cardinal=0):PAnsiChar;
+var
+ l:cardinal;
+ p:pAnsiChar;
+begin
+ if dst<>nil then
+ begin
+ if (src=nil) or (src^=#0) then
+ dst^:=#0
+ else
+ begin
+ if len=0 then
+ len:=high(cardinal);
+ p:=src;
+ l:=len;
+ while (p^<>#0) and (l>0) do
+ begin
+ inc(p); dec(l);
+ end;
+ l:=p-src;
+ move(src^, dst^,l);
+ inc(dst,l);
+ dst^:=#0;
+ end;
+ end;
+ result:=dst;
+end;
+
+function StrCopyEW(dst:PWideChar;src:PWideChar;len:cardinal=0):PWideChar;
+var
+ l:cardinal;
+ p:pWideChar;
+begin
+ if dst<>nil then
+ begin
+ if (src=nil) or (src^=#0) then
+ dst^:=#0
+ else
+ begin
+ if len=0 then
+ len:=high(cardinal);
+ p:=src;
+ l:=len;
+ while (p^<>#0) and (l>0) do
+ begin
+ inc(p); dec(l);
+ end;
+ l:=p-src;
+ move(src^, dst^,l*SizeOf(WideChar));
+ inc(dst,l);
+ dst^:=#0;
+ end;
+ end;
+ result:=dst;
+end;
+
+function StrCopy(dst:PAnsiChar;src:PAnsiChar;len:cardinal=0):PAnsiChar;
+var
+ l:cardinal;
+ p:pAnsiChar;
+begin
+ if dst<>nil then
+ begin
+ if (src=nil) or (src^=#0) then
+ dst^:=#0
+ else
+ begin
+ if len=0 then
+ len:=high(cardinal);
+ p:=src;
+ l:=len;
+ while (p^<>#0) and (l>0) do
+ begin
+ inc(p); dec(l);
+ end;
+ l:=p-src;
+ move(src^, dst^,l);
+ dst[l]:=#0;
+ end;
+ end;
+ result:=dst;
+end;
+
+function StrCopyW(dst:PWideChar;src:PWideChar;len:cardinal=0):PWideChar;
+var
+ l:cardinal;
+ p:pWideChar;
+begin
+ if dst<>nil then
+ begin
+ if (src=nil) or (src^=#0) then
+ dst^:=#0
+ else
+ begin
+ if len=0 then
+ len:=high(cardinal);
+ p:=src;
+ l:=len;
+ while (p^<>#0) and (l>0) do
+ begin
+ inc(p); dec(l);
+ end;
+ l:=p-src;
+ move(src^, dst^,l*SizeOf(WideChar));
+ dst[l]:=#0;
+ end;
+ end;
+ result:=dst;
+end;
+
+function StrDelete(aStr:PAnsiChar;pos,len:cardinal):PAnsiChar;
+var
+ i:cardinal;
+begin
+ if len>0 then
+ begin
+ i:=StrLen(aStr);
+ if pos<i then
+ begin
+ if (pos+len)>i then
+ len:=i-pos;
+ StrCopy(aStr+pos,aStr+pos+len);
+ end;
+ end;
+ result:=aStr;
+end;
+
+function StrDeleteW(aStr:PWideChar;pos,len:cardinal):PWideChar;
+var
+ i:cardinal;
+begin
+ if len>0 then
+ begin
+ i:=StrLenW(aStr);
+ if pos<i then
+ begin
+ if (pos+len)>i then
+ len:=i-pos;
+ StrCopyW(aStr+pos,aStr+pos+len);
+ end;
+ end;
+ result:=aStr;
+end;
+
+function StrInsert(substr,src:PAnsiChar;pos:cardinal):PAnsiChar;
+var
+ i:cardinal;
+ p:PAnsiChar;
+begin
+ i:=StrLen(substr);
+ if i<>0 then
+ begin
+ p:=src+pos;
+ move(p^,(p+i)^,StrLen(src)-pos+1);
+ move(substr^,p^,i);
+ end;
+ result:=src;
+end;
+
+function StrInsertW(substr,src:PWideChar;pos:cardinal):PWideChar;
+var
+ i:cardinal;
+ p:PWideChar;
+begin
+ i:=StrLenW(substr);
+ if i<>0 then
+ begin
+ p:=src+pos;
+ move(p^,(p+i)^,(StrLenW(src)-pos+1)*SizeOf(PWideChar));
+ move(substr^,p^,i*SizeOf(WideChar));
+ end;
+ result:=src;
+end;
+
+function StrReplace(src,SubStr,NewStr:PAnsiChar):PAnsiChar;
+var
+ i,j,l:integer;
+ k:integer;
+ p:PAnsiChar;
+begin
+ result:=src;
+ p:=StrPos(src,SubStr);
+ if p=nil then exit;
+ i:=StrLen(SubStr);
+ j:=StrLen(NewStr);
+ l:=i-j;
+ repeat
+ if j=0 then
+ StrCopy(p,p+i)
+ else
+ begin
+ k:=StrLen(p)+1;
+ if l>0 then
+ move((p+l)^,p^,k-l)
+ else if l<>0 then
+ move(p^,(p-l)^,k);
+ move(NewStr^,p^,j); {new characters}
+ inc(p,j);
+ end;
+ p:=StrPos(p,SubStr);
+ if p=nil then break;
+ until false;
+end;
+
+function StrReplaceW(src,SubStr,NewStr:pWideChar):PWideChar;
+var
+ i,j,l:integer;
+ k:integer;
+ p:PWideChar;
+begin
+ result:=src;
+ p:=StrPosW(src,SubStr);
+ if p=nil then exit;
+ i:=StrLenW(SubStr);
+ j:=StrLenW(NewStr);
+ l:=i-j;
+ repeat
+ if j=0 then
+ StrCopyW(p,p+i)
+ else
+ begin
+ k:=(StrLenW(p)+1)*SizeOf(WideChar);
+ if l>0 then
+ move((p+l)^,p^,k-l*SizeOf(WideChar))
+ else if l<>0 then
+ move(p^,(p-l)^,k);
+ move(NewStr^,p^,j*SizeOf(WideChar)); {new characters}
+ inc(p,j);
+ end;
+ p:=StrPosW(p,SubStr);
+ if p=nil then break;
+ until false;
+end;
+
+function CharReplace(dst:pAnsiChar;old,new:AnsiChar):PAnsiChar;
+begin
+ result:=dst;
+ if dst<>nil then
+ begin
+ while dst^<>#0 do
+ begin
+ if dst^=old then dst^:=new;
+ inc(dst);
+ end;
+ end;
+end;
+
+function CharReplaceW(dst:pWideChar;old,new:WideChar):PWideChar;
+begin
+ result:=dst;
+ if dst<>nil then
+ begin
+ while dst^<>#0 do
+ begin
+ if dst^=old then dst^:=new;
+ inc(dst);
+ end;
+ end;
+end;
+
+function StrCmp(a,b:PAnsiChar;n:cardinal=0):integer; // CompareString
+begin
+ result:=0;
+ if (a=nil) and (b=nil) then
+ exit;
+ if (a=nil) or (b=nil) then
+ begin
+ result:=-1;
+ exit;
+ end;
+ repeat
+ result:=ord(a^)-ord(b^);
+ if (result<>0) or (a^=#0) then
+ break;
+ inc(a);
+ inc(b);
+ dec(n);
+ until n=0;
+end;
+
+function StrCmpW(a,b:PWideChar;n:cardinal=0):integer;
+begin
+ result:=0;
+ if (a=nil) and (b=nil) then
+ exit;
+ if (a=nil) or (b=nil) then
+ begin
+ result:=-1;
+ exit;
+ end;
+ repeat
+ result:=ord(a^)-ord(b^);
+ if (result<>0) or (a^=#0) then
+ break;
+ inc(a);
+ inc(b);
+ dec(n);
+ until n=0;
+end;
+
+function StrEnd(const a:PAnsiChar):PAnsiChar;
+begin
+ result:=a;
+ if result<>nil then
+ while result^<>#0 do inc(result);
+end;
+
+function StrEndW(const a:PWideChar):PWideChar;
+begin
+ result:=a;
+ if result<>nil then
+ while result^<>#0 do inc(result);
+end;
+
+function StrScan(src:PAnsiChar;c:AnsiChar):PAnsiChar;
+begin
+ if src<>nil then
+ begin
+ while (src^<>#0) and (src^<>c) do inc(src);
+ if src^<>#0 then
+ begin
+ result:=src;
+ exit;
+ end;
+ end;
+ result:=nil;
+end;
+
+function StrRScan(src:PAnsiChar;c:AnsiChar):PAnsiChar;
+begin
+ if src<>nil then
+ begin
+ result:=StrEnd(src);
+ while (result>=src) and (result^<>c) do dec(result);
+ if result<src then
+ result:=nil;
+ end
+ else
+ result:=nil;
+end;
+
+function StrScanW(src:PWideChar;c:WideChar):PWideChar;
+begin
+ if src<>nil then
+ begin
+ while (src^<>#0) and (src^<>c) do inc(src);
+ if src^<>#0 then
+ begin
+ result:=src;
+ exit;
+ end;
+ end;
+ result:=nil;
+end;
+
+function StrRScanW(src:PWideChar;c:WideChar):PWideChar;
+begin
+ if src<>nil then
+ begin
+ result:=StrEndW(src);
+ while (result>=src) and (result^<>c) do dec(result);
+ if result<src then
+ result:=nil;
+ end
+ else
+ result:=nil;
+end;
+
+function StrLen(Str: PAnsiChar): Cardinal;
+var
+ P : PAnsiChar;
+begin
+ P := Str;
+ if P<>nil then
+ while (P^ <> #0) do Inc(P);
+ Result := (P - Str);
+end;
+
+function StrLenW(Str: PWideChar): Cardinal;
+var
+ P : PWideChar;
+begin
+ P := Str;
+ if P<>nil then
+ while (P^ <> #0) do Inc(P);
+ Result := (P - Str);
+end;
+
+function StrCat(Dest: PAnsiChar; const Source: PAnsiChar): PAnsiChar;
+begin
+ if Dest<>nil then
+ StrCopy(StrEnd(Dest), Source);
+ Result := Dest;
+end;
+
+function StrCatW(Dest: PWideChar; const Source: PWideChar): PWideChar;
+begin
+ if Dest<>nil then
+ StrCopyW(StrEndW(Dest), Source);
+ Result := Dest;
+end;
+
+function StrCatE(Dest: PAnsiChar; const Source: PAnsiChar): PAnsiChar;
+begin
+ if Dest<>nil then
+ result:=StrCopyE(StrEnd(Dest), Source)
+ else
+ result:=nil;
+end;
+
+function StrCatEW(Dest: PWideChar; const Source: PWideChar): PWideChar;
+begin
+ if Dest<>nil then
+ result:=StrCopyEW(StrEndW(Dest), Source)
+ else
+ result:=nil;
+end;
+
+function StrPos(const aStr, aSubStr: PAnsiChar): PAnsiChar;
+var
+ Str, SubStr: PAnsiChar;
+ Ch: AnsiChar;
+begin
+ if (aStr = nil) or (aStr^ = #0) or (aSubStr = nil) or (aSubStr^ = #0) then
+ begin
+ Result := nil;
+ Exit;
+ end;
+ Result := aStr;
+ Ch := aSubStr^;
+ repeat
+ if Result^ = Ch then
+ begin
+ Str := Result;
+ SubStr := aSubStr;
+ repeat
+ Inc(Str);
+ Inc(SubStr);
+ if SubStr^ = #0 then exit;
+ if Str^ = #0 then
+ begin
+ Result := nil;
+ exit;
+ end;
+ if Str^ <> SubStr^ then break;
+ until (FALSE);
+ end;
+ Inc(Result);
+ until (Result^ = #0);
+ Result := nil;
+end;
+
+function StrIndex(const aStr, aSubStr: PAnsiChar):integer;
+var
+ p:pAnsiChar;
+begin
+ p:=StrPos(aStr,aSubStr);
+ if p=nil then
+ result:=0
+ else
+ result:=p-aStr+1;
+end;
+
+function StrPosW(const aStr, aSubStr: PWideChar): PWideChar;
+var
+ Str, SubStr: PWideChar;
+ Ch: WideChar;
+begin
+ if (aStr = nil) or (aStr^ = #0) or (aSubStr = nil) or (aSubStr^ = #0) then
+ begin
+ Result := nil;
+ Exit;
+ end;
+ Result := aStr;
+ Ch := aSubStr^;
+ repeat
+ if Result^ = Ch then
+ begin
+ Str := Result;
+ SubStr := aSubStr;
+ repeat
+ Inc(Str);
+ Inc(SubStr);
+ if SubStr^ = #0 then exit;
+ if Str^ = #0 then
+ begin
+ Result := nil;
+ exit;
+ end;
+ if Str^ <> SubStr^ then break;
+ until (FALSE);
+ end;
+ Inc(Result);
+ until (Result^ = #0);
+ Result := nil;
+end;
+
+function StrIndexW(const aStr, aSubStr: PWideChar):integer;
+var
+ p:pWideChar;
+begin
+ p:=StrPosW(aStr,aSubStr);
+ if p=nil then
+ result:=0
+ else
+ result:=(p-aStr)+1; //!!!!
+end;
+
+// ----- filenames -----
+
+function ChangeExt(src,ext:PAnsiChar):PAnsiChar;
+var
+ i,j:integer;
+begin
+ i:=StrLen(src);
+ j:=i;
+ while (i>0) and (src[i]<>'\') and (src[i]<>':') and (src[i]<>'.') do dec(i);
+ if src[i]<>'.' then
+ begin
+ i:=j;
+ src[i]:='.';
+ end;
+ if ext=nil then
+ ext:='';
+ StrCopy(src+i+1,ext);
+ result:=src;
+end;
+
+function ChangeExtW(src,ext:PWideChar):PWideChar;
+var
+ i,j:integer;
+begin
+ i:=StrLenW(src);
+ j:=i;
+ while (i>0) and (src[i]<>'\') and (src[i]<>':') and (src[i]<>'.') do dec(i);
+ if src[i]<>'.' then
+ begin
+ i:=j;
+ src[i]:='.';
+ end;
+ if ext=nil then
+ ext:='';
+ StrCopyW(src+i+1,ext);
+ result:=src;
+end;
+
+function Extract(s:PAnsiChar;name:Boolean=true):PAnsiChar;
+var
+ i:integer;
+begin
+ i:=StrLen(s)-1;
+// j:=i;
+ while (i>=0) and ((s[i]<>'\') and (s[i]<>'/')) do dec(i);
+ if name then
+ begin
+ StrDup(result,s+i+1);
+// mGetMem(result,(j-i+1));
+// StrCopy(result,s+i+1);
+ end
+ else
+ begin
+ StrDup(result,s,i+1);
+ end;
+end;
+
+function ExtractW(s:pWideChar;name:Boolean=true):pWideChar;
+var
+ i:integer;
+begin
+ i:=StrLenW(s)-1;
+// j:=i;
+ while (i>=0) and ((s[i]<>'\') and (s[i]<>'/')) do dec(i);
+ if name then
+ begin
+ StrDupW(result,s+i+1);
+// mGetMem(result,(j-i+1)*SizeOf(WideChar));
+// StrCopyW(result,s+i+1);
+ end
+ else
+ begin
+ StrDupW(result,s,i+1);
+ end;
+end;
+
+function GetExt(fname,dst:pWideChar;maxlen:dword=100):pWideChar;
+var
+ ppc,pc:PWideChar;
+begin
+ result:=dst;
+ dst^:=#0;
+ if fname<>nil then
+ begin
+ pc:=StrEndW(fname)-1;
+ while (pc>fname) and ((pc^='"') or (pc^=' ')) do dec(pc);
+ ppc:=pc+1;
+ while (pc>fname) and (pc^<>'.') do
+ begin
+ if maxlen=0 then exit;
+ if not (AnsiChar(pc^) in ['0'..'9','A'..'Z','_','a'..'z']) then exit;
+ dec(maxlen);
+ dec(pc); //empty name not allowed!
+ end;
+ if pc>fname then
+ begin
+ repeat
+ inc(pc);
+ if pc=ppc then
+ begin
+ dst^:=#0;
+ break;
+ end;
+ if (pc^>='a') and (pc^<='z') then
+ dst^:=WideChar(ord(pc^)-$20)
+ else
+ dst^:=pc^;
+ inc(dst);
+ until false;
+ end;
+ end;
+end;
+
+function GetExt(fname,dst:PAnsiChar;maxlen:dword=100):PAnsiChar;
+var
+ ppc,pc:PAnsiChar;
+begin
+ result:=dst;
+ dst^:=#0;
+ if fname<>nil then
+ begin
+ pc:=StrEnd(fname)-1;
+ while (pc>fname) and ((pc^='"') or (pc^=' ')) do dec(pc);
+ ppc:=pc+1;
+ while (pc>fname) and (pc^<>'.') do
+ begin
+ if maxlen=0 then exit;
+ if not (AnsiChar(pc^) in ['0'..'9','A'..'Z','_','a'..'z']) then exit;
+ dec(maxlen);
+ dec(pc); //empty name not allowed!
+ end;
+ if pc>fname then
+ begin
+ repeat
+ inc(pc);
+ if pc=ppc then
+ begin
+ dst^:=#0;
+ break;
+ end;
+ if (pc^>='a') and (pc^<='z') then
+ dst^:=AnsiChar(ord(pc^)-$20)
+ else
+ dst^:=pc^;
+ inc(dst);
+ until false;
+ end;
+ end;
+end;
+
+type
+ PDayTable = ^TDayTable;
+ TDayTable = array [0..11] of cardinal;
+
+const
+ MonthDays: array [Boolean] of TDayTable =
+ ((31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31),
+ (31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31));
+
+const
+ DateDelta = 693594;
+{ Days between TDateTime basis (12/31/1899) and Unix time_t basis (1/1/1970) }
+ UnixDateDelta = 25569;
+
+function IsLeapYear(Year:Word):Boolean;
+begin
+ Result:=(Year mod 4=0) and ((Year mod 100<>0) or (Year mod 400=0));
+end;
+
+function EncodeTime(Hour, Minute, Sec: cardinal): TDateTime;
+begin
+ result := (Hour*3600 + Minute*60 + Sec) / 86400;
+end;
+
+function EncodeDate(Year, Month, Day: cardinal):TDateTime;
+var
+ DayTable: PDayTable;
+begin
+ DayTable := @MonthDays[IsLeapYear(Year)];
+ dec(Month);
+ while Month>0 do
+ begin
+ dec(Month);
+ inc(Day,DayTable^[Month]);
+ end;
+
+ dec(Year);
+ result := Year * 365 + Year div 4 - Year div 100 + Year div 400 + Day - DateDelta;
+end;
+
+function Timestamp(Year,Month,Day:cardinal;Hour:cardinal=0;Minute:cardinal=0;Sec:cardinal=0):dword;
+var
+ t:tDateTime;
+begin
+ t := EncodeDate(Year, Month, Day);
+ if t >= 0 then
+ t := t + EncodeTime(Hour, Minute, Sec)
+ else
+ t := t - EncodeTime(Hour, Minute, Sec);
+ result:=Round((t - UnixDateDelta) * 86400)
+end;
+
+function GetCurrentTime:dword;
+var
+ st:tSystemTime;
+begin
+ GetSystemTime(st);
+ result:=Timestamp(st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond);
+end;
+
+function TimeToInt(stime:PAnsiChar):integer;
+var
+ hour,minute,sec,len,i:integer;
+begin
+ len:=StrLen(stime);
+ i:=0;
+ sec :=0;
+ minute:=0;
+ hour :=0;
+ while i<len do
+ begin
+ if (stime[i]<'0') or (stime[i]>'9') then
+ begin
+ if minute>0 then
+ hour:=minute;
+ minute:=sec;
+ sec:=0;
+ end
+ else
+ sec:=sec*10+ord(stime[i])-ord('0');
+ inc(i);
+ end;
+ result:=hour*3600+minute*60+sec;
+end;
+
+function TimeToInt(stime:PWideChar):integer;
+var
+ buf:array [0..63] of AnsiChar;
+begin
+ result:=TimeToInt(FastWideToAnsiBuf(stime,buf));
+end;
+
+function IntToTime(dst:PAnsiChar;time:integer):PAnsiChar;
+var
+ day,hour,minute,sec:array [0..7] of AnsiChar;
+ d,h:integer;
+begin
+ result:=dst;
+ h:=time div 3600;
+ dec(time,h*3600);
+ IntToStr(sec,(time mod 60),2);
+ d:=h div 24;
+ if d>0 then
+ begin
+ h:=h mod 24;
+ IntToStr(day,d);
+ dst^:=day[0]; inc(dst);
+ if day[1]<>#0 then // now only 99 days max
+ begin
+ dst^:=day[1]; inc(dst);
+ end;
+ dst^:=' '; inc(dst);
+ end;
+ if h>0 then
+ begin
+ IntToStr(hour,h);
+ IntToStr(minute,(time div 60),2);
+ dst^:=hour[0]; inc(dst);
+ if hour[1]<>#0 then
+ begin
+ dst^:=hour[1]; inc(dst);
+ end;
+ dst^:=':'; inc(dst);
+ dst^:=minute[0]; inc(dst);
+ dst^:=minute[1]; inc(dst);
+ end
+ else
+ begin
+ IntToStr(minute,time div 60);
+ dst^:=minute[0]; inc(dst);
+ if minute[1]<>#0 then
+ begin
+ dst^:=minute[1]; inc(dst);
+ end;
+ end;
+ dst^:=':'; inc(dst);
+ dst^:=sec[0]; inc(dst);
+ dst^:=sec[1]; inc(dst);
+ dst^:=#0;
+end;
+
+function IntToTime(dst:pWideChar;time:integer):pWideChar;
+var
+ buf:array [0..63] of AnsiChar;
+begin
+ result:=FastAnsiToWideBuf(IntToTime(buf,time),dst);
+end;
+
+function StrToInt(src:pWideChar):int64;
+var
+ sign:boolean;
+begin
+ result:=0;
+ if src<>nil then
+ begin
+ sign:=src^='-';
+ if sign then inc(src);
+ while src^<>#0 do
+ begin
+ if (src^>='0') and (src^<='9') then
+ result:=result*10+ord(src^)-ord('0')
+ else
+ break;
+ inc(src);
+ end;
+ if sign then result:=-result;
+ end;
+end;
+
+function StrToInt(src:PAnsiChar):int64;
+var
+ sign:boolean;
+begin
+ result:=0;
+ if src<>nil then
+ begin
+ sign:=src^='-';
+ if sign then inc(src);
+ while src^<>#0 do
+ begin
+ if (src^>='0') and (src^<='9') then
+ result:=result*10+ord(src^)-ord('0')
+ else
+ break;
+ inc(src);
+ end;
+ if sign then result:=-result;
+ end;
+end;
+
+function IntToStr(dst:PAnsiChar;Value:int64;Digits:integer=0):PAnsiChar;
+var
+ i:dword;
+begin
+ if Digits<=0 then
+ begin
+ if Value<0 then
+ Digits:=1
+ else
+ Digits:=0;
+ i:=ABS(Value);
+ repeat
+ i:=i div 10;
+ inc(Digits);
+ until i=0;
+ end;
+ dst[Digits]:=#0;
+ i:=ABS(Value);
+ repeat
+ dec(Digits);
+ dst[Digits]:=AnsiChar(ord('0')+(i mod 10));
+ i:=i div 10;
+ if (Value<0) and (Digits=1) then
+ begin
+ dst[0]:='-';
+ break;
+ end;
+ until Digits=0;
+ result:=dst;
+end;
+
+function IntToStr(dst:pWideChar;Value:int64;Digits:integer=0):pWideChar;
+var
+ i:dword;
+begin
+ if Digits<=0 then
+ begin
+ if Value<0 then
+ Digits:=1
+ else
+ Digits:=0;
+ i:=ABS(Value);
+ repeat
+ i:=i div 10;
+ inc(Digits);
+ until i=0;
+ end;
+ dst[Digits]:=#0;
+ i:=ABS(Value);
+ repeat
+ dec(Digits);
+ dst[Digits]:=WideChar(ord('0')+(i mod 10));
+ i:=i div 10;
+ if (Value<0) and (Digits=1) then
+ begin
+ dst[0]:='-';
+ break;
+ end;
+ until Digits=0;
+ result:=dst;
+end;
+
+function HexToInt(src:pWideChar;len:cardinal=$FFFF):int64;
+begin
+ result:=0;
+ while (src^<>#0) and (len>0) do
+ begin
+ if (src^>='0') and (src^<='9') then
+ result:=result*16+ord(src^)-ord('0')
+ else if ((src^>='A') and (src^<='F')) then
+ result:=result*16+ord(src^)-ord('A')+10
+ else if ((src^>='a') and (src^<='f')) then
+ result:=result*16+ord(src^)-ord('a')+10
+ else
+ break;
+ inc(src);
+ dec(len);
+ end;
+end;
+
+function HexToInt(src:PAnsiChar;len:cardinal=$FFFF):int64;
+begin
+ result:=0;
+ while (src^<>#0) and (len>0) do
+ begin
+ if (src^>='0') and (src^<='9') then
+ result:=result*16+ord(src^)-ord('0')
+ else if ((src^>='A') and (src^<='F')) then
+ result:=result*16+ord(src^)-ord('A')+10
+ else if ((src^>='a') and (src^<='f')) then
+ result:=result*16+ord(src^)-ord('a')+10
+ else
+ break;
+ inc(src);
+ dec(len);
+ end;
+end;
+
+function IntToHex(dst:pWidechar;Value:int64;Digits:integer=0):pWideChar;
+var
+ i:dword;
+begin
+ if Digits<=0 then
+ begin
+ Digits:=0;
+ i:=Value;
+ repeat
+ i:=i shr 4;
+ inc(Digits);
+ until i=0;
+ end;
+ dst[Digits]:=#0;
+ repeat
+ Dec(Digits);
+ dst[Digits]:=WideChar(HexDigitChr[Value and $F]);
+ Value:=Value shr 4;
+ until Digits=0;
+ result:=dst;
+end;
+
+function IntToHex(dst:PAnsiChar;Value:int64;Digits:integer=0):PAnsiChar;
+var
+ i:dword;
+begin
+ if Digits<=0 then
+ begin
+ Digits:=0;
+ i:=Value;
+ repeat
+ i:=i shr 4;
+ inc(Digits);
+ until i=0;
+ end;
+ dst[Digits]:=#0;
+ repeat
+ Dec(Digits);
+ dst[Digits]:=HexDigitChr[Value and $F];
+ Value:=Value shr 4;
+ until Digits=0;
+ result:=dst;
+end;
+
+procedure UpperCase(src:pWideChar);
+var
+ c:WideChar;
+begin
+ if src<>nil then
+ begin
+ while src^<>#0 do
+ begin
+ c:=src^;
+ if (c>='a') and (c<='z') then
+ src^:=WideChar(ord(c)-$20);
+ inc(src);
+ end;
+ end;
+end;
+
+procedure LowerCase(src:pWideChar);
+var
+ c:WideChar;
+begin
+ if src<>nil then
+ begin
+ while src^<>#0 do
+ begin
+ c:=src^;
+ if (c>='A') and (c<='Z') then
+ src^:=WideChar(ord(c)+$20);
+ inc(src);
+ end;
+ end;
+end;
+
+function GetPairChar(ch:AnsiChar):AnsiChar;
+begin
+ case ch of
+ '[': result:=']';
+ '<': result:='>';
+ '(': result:=')';
+ '{': result:='}';
+ else // ' and " too
+ result:=ch;
+ end;
+end;
+
+function GetPairChar(ch:WideChar):WideChar;
+begin
+ case ch of
+ '[': result:=']';
+ '<': result:='>';
+ '(': result:=')';
+ '{': result:='}';
+ else // ' and " too
+ result:=ch;
+ end;
+end;
+
+function FastWideToAnsiBuf(src:PWideChar;dst:PAnsiChar;len:cardinal=cardinal(-1)):PAnsiChar;
+begin
+ result:=dst;
+ if src<>nil then
+ begin
+ repeat
+ dst^:=AnsiChar(src^);
+ if src^=#0 then
+ break;
+ dec(len);
+ if len=0 then
+ begin
+ (dst+1)^:=#0;
+ break;
+ end;
+ inc(src);
+ inc(dst);
+ until false;
+ end
+ else
+ dst^:=#0;
+end;
+
+function FastWideToAnsi(src:PWideChar;var dst:PAnsiChar):PAnsiChar;
+begin
+ if src=nil then
+ dst:=nil
+ else
+ begin
+ mGetMem(dst,StrLenW(src)+1);
+ FastWideToAnsiBuf(src,dst);
+ end;
+ result:=dst;
+end;
+
+function FastAnsiToWideBuf(src:PAnsiChar;dst:PWideChar;len:cardinal=cardinal(-1)):PWideChar;
+begin
+ result:=dst;
+ if src<>nil then
+ begin
+ repeat
+ dst^:=WideChar(src^);
+ if src^=#0 then
+ break;
+ dec(len);
+ if len=0 then
+ begin
+ (dst+1)^:=#0;
+ break;
+ end;
+ inc(src);
+ inc(dst);
+ until false;
+ end
+ else
+ dst^:=#0;
+end;
+
+function FastAnsiToWide(src:PAnsiChar;var dst:PWideChar):PWideChar;
+begin
+ if src=nil then
+ dst:=nil
+ else
+ begin
+ mGetMem(dst,(StrLen(src)+1)*SizeOf(WideChar));
+ FastAnsiToWideBuf(src,dst);
+ end;
+ result:=dst;
+end;
+
+function isPathAbsolute(path:pWideChar):boolean;
+begin
+ result:=((path[1]=':') and (path[2]='\')) or ((path[0]='\') {and (path[1]='\')}) or
+ (StrPosW(path,'://')<>nil);
+end;
+
+function isPathAbsolute(path:pAnsiChar):boolean;
+begin
+ result:=((path[1]=':') and (path[2]='\')) or ((path[0]='\') {and (path[1]='\')}) or
+ (StrPos(path,'://')<>nil);
+end;
+
+procedure ShowDump(ptr:pbyte;len:integer);
+var
+ buf: array of Ansichar;
+ i:integer;
+ p:pAnsiChar;
+ p1:pByte;
+ cnt:integer;
+begin
+ SetLength(buf,len*4+1);
+ p:=@buf[0];
+ p1:=ptr;
+ cnt:=0;
+ for i:=0 to len-1 do
+ begin
+ IntToHex(p,p1^,2);
+ inc(p,2);
+ inc(p1);
+ inc(cnt);
+ if cnt=4 then
+ begin
+ cnt:=0;
+ p^:='.';
+ inc(p);
+ end;
+ end;
+ p^:=#0;
+ messageboxa(0,@buf[0],'',0);
+end;
+begin
+ CheckSystem;
+end.
diff --git a/plugins/Utils.pas/compilers.inc b/plugins/Utils.pas/compilers.inc
new file mode 100644
index 0000000000..95940246e1
--- /dev/null
+++ b/plugins/Utils.pas/compilers.inc
@@ -0,0 +1,778 @@
+//----------------------------------------------------------------------------------------------------------------------
+// Include file to determine which compiler is currently being used to build the project/component.
+// This file uses ideas from Brad Stowers DFS.inc file.
+//
+// Portions created by Mike Lischke are
+// Copyright (C) 1999-2005 Mike Lischke. All Rights Reserved.
+// Portions created by Jim Kueneman are
+// Copyright (C) 2005 Jim Kueneman. All Rights Reserved.
+//
+//----------------------------------------------------------------------------------------------------------------------
+//
+// This unit is released under the MIT license:
+// Copyright (c) 1999-2005 Mike Lischke (support@soft-gems.net, www.soft-gems.net).
+//
+// 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
+// OR COPYRIGHT HOLDERS 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.
+//
+// You are asked to give the author(s) the due credit. This means that you acknowledge the work of the author(s)
+// in the product documentation, about box, help or wherever a prominent place is.
+//
+//----------------------------------------------------------------------------------------------------------------------
+//
+// The following symbols are defined:
+//
+// - COMPILER_1 : Kylix/Delphi/BCB 1.x is the compiler.
+// - COMPILER_1_UP : Kylix/Delphi/BCB 1.x or higher is the compiler.
+// - COMPILER_2 : Kylix/Delphi 2.x or BCB 1.x is the compiler.
+// - COMPILER_2_UP : Kylix/Delphi 2.x or higher, or BCB 1.x or higher is the compiler.
+// - COMPILER_3 : Kylix/Delphi/BCB 3.x is the compiler.
+// - COMPILER_3_UP : Kylix/Delphi/BCB 3.x or higher is the compiler.
+// - COMPILER_4 : Kylix/Delphi/BCB 4.x is the compiler.
+// - COMPILER_4_UP : Kylix/Delphi/BCB 4.x or higher is the compiler.
+// - COMPILER_5 : Kylix/Delphi/BCB 5.x is the compiler.
+// - COMPILER_5_UP : Kylix/Delphi/BCB 5.x or higher is the compiler.
+// - COMPILER_6 : Kylix/Delphi/BCB 6.x is the compiler.
+// - COMPILER_6_UP : Kylix/Delphi/BCB 6.x or higher is the compiler.
+// - COMPILER_7 : Kylix/Delphi/BCB 7.x is the compiler.
+// - COMPILER_7_UP : Kylix/Delphi/BCB 7.x or higher is the compiler.
+// - COMPILER_8 : Kylix/Delphi/BCB 8.x is the compiler.
+// - COMPILER_8_UP : Kylix/Delphi/BCB 8.x or higher is the compiler.
+// - COMPILER_9 : Kylix/Delphi/BCB 9.x is the compiler.
+// - COMPILER_9_UP : Kylix/Delphi/BCB 9.x or higher is the compiler.
+// - COMPILER_10 : Kylix/Delphi/BCB 10.x is the compiler.
+// - COMPILER_10_UP : Kylix/Delphi/BCB 10.x or higher is the compiler.
+// - COMPILER_11 : Kylix/Delphi/BCB 11.x is the compiler.
+// - COMPILER_11_UP : Kylix/Delphi/BCB 11.x or higher is the compiler.
+// - COMPILER_12 : Kylix/Delphi/BCB 12.x is the compiler.
+// - COMPILER_12_UP : Kylix/Delphi/BCB 12.x or higher is the compiler.
+// - COMPILER_14 : Delphi/BCB 14.x is the compiler.
+// - COMPILER_14_UP : Delphi/BCB 14.x or higher is the compiler.
+// - COMPILER_15 : Delphi/BCB 15.x is the compiler. [XE]
+// - COMPILER_15_UP : Delphi/BCB 15.x or higher is the compiler. [XE]
+// - COMPILER_16 : Delphi/BCB 16.x is the compiler. [XE2]
+// - COMPILER_16_UP : Delphi/BCB 16.x or higher is the compiler. [XE2]
+//
+// Only defined if Windows is the target:
+// - CPPB : Any version of BCB is being used.
+// - CPPB_1 : BCB v1.x is being used.
+// - CPPB_3 : BCB v3.x is being used.
+// - CPPB_3_UP : BCB v3.x or higher is being used.
+// - CPPB_4 : BCB v4.x is being used.
+// - CPPB_4_UP : BCB v4.x or higher is being used.
+// - CPPB_5 : BCB v5.x is being used.
+// - CPPB_5_UP : BCB v5.x or higher is being used.
+// - CPPB_6 : BCB v6.x is being used.
+// - CPPB_6_UP : BCB v6.x or higher is being used.
+// - CPPB_XXX is not used any more, use the COMPILER_XXX defines
+//
+// Only defined if Windows is the target:
+// - DELPHI : Any version of Delphi is being used.
+// - DELPHI_1 : Delphi v1.x is being used.
+// - DELPHI_2 : Delphi v2.x is being used.
+// - DELPHI_2_UP : Delphi v2.x or higher is being used.
+// - DELPHI_3 : Delphi v3.x is being used.
+// - DELPHI_3_UP : Delphi v3.x or higher is being used.
+// - DELPHI_4 : Delphi v4.x is being used.
+// - DELPHI_4_UP : Delphi v4.x or higher is being used.
+// - DELPHI_5 : Delphi v5.x is being used.
+// - DELPHI_5_UP : Delphi v5.x or higher is being used.
+// - DELPHI_6 : Delphi v6.x is being used.
+// - DELPHI_6_UP : Delphi v6.x or higher is being used.
+// - DELPHI_7 : Delphi v7.x is being used.
+// - DELPHI_7_UP : Delphi v7.x or higher is being used.
+// - DELPHI_8 : Delphi v8.x is being used.
+// - DELPHI_8_UP : Delphi v8.x or higher is being used.
+// - DELPHI_9 : Delphi v9.x is being used.
+// - DELPHI_9_UP : Delphi v9.x or higher is being used.
+// - DELPHI_XXX is not used any more, use the COMPILER_XXX defines
+//
+// Only defined if Linux is the target:
+// - KYLIX : Any version of Kylix is being used.
+// - KYLIX_1 : Kylix 1.x is being used.
+// - KYLIX_1_UP : Kylix 1.x or higher is being used.
+// - KYLIX_2 : Kylix 2.x is being used.
+// - KYLIX_2_UP : Kylix 2.x or higher is being used.
+// - KYLIX_3 : Kylix 3.x is being used.
+// - KYLIX_3_UP : Kylix 3.x or higher is being used.
+//
+// Only defined if Linux is the target:
+// - QT_CLX : Trolltech's QT library is being used.
+//
+// Only defined if Delphi.NET is the target:
+// - DELPHI.NET : Any version of Delphi.NET is being used.
+// - DELPHI.NET_1 : Delphi.NET version 1.x is being used.
+// - DELPHI.NET_1_UP : Delphi.NET version 1.x is being used.
+//----------------------------------------------------------------------------------------------------------------------
+
+
+{$ifdef CLR} // The common language runtime symbol is only defined for the .NET platform.
+ {$define DELPHI.NET}
+ {$ifdef VER160}
+ {$define DELPHI.NET_1}
+ {$endif VER160}
+
+
+ // Compiler defines common to all .NET versions.
+ {$ifdef DELPHI.NET_1}
+ {$define DELHI.NET_1_UP}
+ {$endif DELPHI.NET_1}
+{$endif CLR}
+
+
+{$ifdef Win64}
+
+
+ // BDS XE2 (BDS 9.0) DELPHI and BCB are no longer defined, only COMPILER
+ {$ifdef VER230}
+ {$define COMPILER_16}
+ {$endif VER230}
+
+
+ {$ifdef COMPILER_16}
+ {$define COMPILER_16_UP}
+ {$endif}
+
+
+ {$ifdef BCB}
+ {$define CPPB}
+ {$else}
+ {$define DELPHI}
+ {$endif}
+
+
+{$endif}
+
+
+{$ifdef Win32}
+
+
+// Compiler defines not specific to a particlular platform.
+
+
+ // BDS XE2 (BDS 9.0) DELPHI and BCB are no longer defined, only COMPILER
+ {$ifdef VER230}
+ {$define COMPILER_16}
+ {$endif VER230}
+
+
+ // BDS XE (BDS 8.0) DELPHI and BCB are no longer defined, only COMPILER
+ {$ifdef VER220}
+ {$define COMPILER_15}
+ {$endif VER220}
+
+
+ // BDS 2010 (BDS 7.0) DELPHI and BCB are no longer defined, only COMPILER
+ {$ifdef VER210}
+ {$define COMPILER_14}
+ {$endif VER210}
+
+
+// No Compiler 13
+
+
+ // BDS 2009 (BDS 6.0) DELPHI and BCB are no longer defined, only COMPILER
+ {$ifdef VER200}
+ {$define COMPILER_12}
+ {$endif VER200}
+
+
+ // BDS 2007 (BDS 5.0) DELPHI and BCB are no longer defined, only COMPILER
+ {$ifdef VER190}
+ {$define COMPILER_11}
+ {$endif VER190}
+
+
+ // DELPHI and BCB are no longer defined, only COMPILER
+ {$ifdef VER180}
+ {$define COMPILER_10}
+ {$endif VER180}
+
+
+ {$ifdef VER170}
+ {$define COMPILER_9}
+ {$define DELPHI}
+ {$define DELPHI_9}
+ {$endif VER170}
+
+ {$ifdef VER160}
+ {$define COMPILER_8}
+ {$define DELPHI}
+ {$define DELPHI_8}
+ {$endif VER160}
+
+
+ {$ifdef VER150}
+ {$define COMPILER_7}
+ {$define DELPHI}
+ {$define DELPHI_7}
+ {$endif}
+
+
+ {$ifdef VER140}
+ {$define COMPILER_6}
+ {$ifdef BCB}
+ {$define CPPB}
+ {$define CPPB_6}
+ {$else}
+ {$define DELPHI}
+ {$define DELPHI_6}
+ {$endif}
+ {$endif}
+
+
+ {$ifdef VER130}
+ {$define COMPILER_5}
+ {$ifdef BCB}
+ {$define CPPB}
+ {$define CPPB_5}
+ {$else}
+ {$define DELPHI}
+ {$define DELPHI_5}
+ {$endif}
+ {$endif}
+
+
+ {$ifdef VER125}
+ {$define COMPILER_4}
+ {$define CPPB}
+ {$define CPPB_4}
+ {$endif}
+
+
+ {$ifdef VER120}
+ {$define COMPILER_4}
+ {$define DELPHI}
+ {$define DELPHI_4}
+ {$endif}
+
+
+ {$ifdef VER110}
+ {$define COMPILER_3}
+ {$define CPPB}
+ {$define CPPB_3}
+ {$endif}
+
+
+ {$ifdef VER100}
+ {$define COMPILER_3}
+ {$define DELPHI}
+ {$define DELPHI_3}
+ {$endif}
+
+
+ {$ifdef VER93}
+ {$define COMPILER_2} // C++ Builder v1 compiler is really v2
+ {$define CPPB}
+ {$define CPPB_1}
+ {$endif}
+
+
+ {$ifdef VER90}
+ {$define COMPILER_2}
+ {$define DELPHI}
+ {$define DELPHI_2}
+ {$endif}
+
+
+ {$ifdef VER80}
+ {$define COMPILER_1}
+ {$define DELPHI}
+ {$define DELPHI_1}
+ {$endif}
+
+
+ {$ifdef DELPHI_2}
+ {$define DELPHI_2_UP}
+ {$endif}
+
+
+ {$ifdef DELPHI_3}
+ {$define DELPHI_2_UP}
+ {$define DELPHI_3_UP}
+ {$endif}
+
+
+ {$ifdef DELPHI_4}
+ {$define DELPHI_2_UP}
+ {$define DELPHI_3_UP}
+ {$define DELPHI_4_UP}
+ {$endif}
+
+
+ {$ifdef DELPHI_5}
+ {$define DELPHI_2_UP}
+ {$define DELPHI_3_UP}
+ {$define DELPHI_4_UP}
+ {$define DELPHI_5_UP}
+ {$endif}
+
+
+ {$ifdef DELPHI_6}
+ {$define DELPHI_2_UP}
+ {$define DELPHI_3_UP}
+ {$define DELPHI_4_UP}
+ {$define DELPHI_5_UP}
+ {$define DELPHI_6_UP}
+ {$endif}
+
+
+ {$ifdef DELPHI_7}
+ {$define DELPHI_2_UP}
+ {$define DELPHI_3_UP}
+ {$define DELPHI_4_UP}
+ {$define DELPHI_5_UP}
+ {$define DELPHI_6_UP}
+ {$define DELPHI_7_UP}
+ {$endif}
+
+
+ {$ifdef DELPHI_8}
+ {$define DELPHI_2_UP}
+ {$define DELPHI_3_UP}
+ {$define DELPHI_4_UP}
+ {$define DELPHI_5_UP}
+ {$define DELPHI_6_UP}
+ {$define DELPHI_7_UP}
+ {$define DELPHI_8_UP}
+ {$endif}
+
+
+ {$ifdef DELPHI_9}
+ {$define DELPHI_2_UP}
+ {$define DELPHI_3_UP}
+ {$define DELPHI_4_UP}
+ {$define DELPHI_5_UP}
+ {$define DELPHI_6_UP}
+ {$define DELPHI_7_UP}
+ {$define DELPHI_8_UP}
+ {$define DELPHI_9_UP}
+ {$endif}
+
+
+ {$ifdef CPPB_3}
+ {$define CPPB_3_UP}
+ {$endif}
+
+
+ {$ifdef CPPB_4}
+ {$define CPPB_3_UP}
+ {$define CPPB_4_UP}
+ {$endif}
+
+
+ {$ifdef CPPB_5}
+ {$define CPPB_3_UP}
+ {$define CPPB_4_UP}
+ {$define CPPB_5_UP}
+ {$endif}
+
+
+ {$ifdef CPPB_6}
+ {$define CPPB_3_UP}
+ {$define CPPB_4_UP}
+ {$define CPPB_5_UP}
+ {$define CPPB_6_UP}
+ {$endif}
+
+
+ {$ifdef CPPB_3_UP}
+ // C++ Builder requires this if you use Delphi components in run-time packages.
+ {$ObjExportAll On}
+ {$endif}
+
+
+{$else (not Windows)}
+ // Linux is the target
+ {$define QT_CLX}
+
+
+ {$define KYLIX}
+
+
+ {$ifdef VER140}
+ {$define COMPILER_6}
+ {$ifdef conditionalexpressions}
+ {$if Declared(RTLVersion) and (RTLVersion = 14)}
+ {$define KYLIX_1}
+ {$ifend}
+
+
+ {$if Declared(RTLVersion) and (RTLVersion = 14.2)}
+ {$define KYLIX_2}
+ {$ifend}
+
+
+ {$if Declared(RTLVersion) and (RTLVersion = 14.5)}
+ {$define KYLIX_3}
+ {$ifend}
+ {$endif}
+ {$endif}
+
+
+ {$ifdef VER150}
+ {$define COMPILER_7}
+ {$define KYLIX_3}
+ {$endif}
+
+
+ {$ifdef VER140}
+ {$define COMPILER_6}
+ {$define KYLIX_2}
+ {$endif}
+
+
+ {$ifdef KYLIX_1}
+ {$define KYLIX_1_UP}
+ {$endif}
+
+
+ {$ifdef KYLIX_2}
+ {$define KYLIX_2_UP}
+ {$endif}
+
+
+ {$ifdef KYLIX_3}
+ {$define KYLIX_2_UP}
+ {$define KYLIX_3_UP}
+ {$endif}
+
+
+{$endif Win32}
+
+
+
+
+{$ifdef COMPILER_1}
+ {$define COMPILER_1_UP}
+{$endif}
+
+
+{$ifdef COMPILER_2}
+ {$define COMPILER_1_UP}
+ {$define COMPILER_2_UP}
+{$endif}
+
+
+{$ifdef COMPILER_3}
+ {$define COMPILER_1_UP}
+ {$define COMPILER_2_UP}
+ {$define COMPILER_3_UP}
+{$endif}
+
+
+{$ifdef COMPILER_4}
+ {$define COMPILER_1_UP}
+ {$define COMPILER_2_UP}
+ {$define COMPILER_3_UP}
+ {$define COMPILER_4_UP}
+{$endif}
+
+
+{$ifdef COMPILER_5}
+ {$define COMPILER_1_UP}
+ {$define COMPILER_2_UP}
+ {$define COMPILER_3_UP}
+ {$define COMPILER_4_UP}
+ {$define COMPILER_5_UP}
+{$endif}
+
+
+{$ifdef COMPILER_6}
+ {$define COMPILER_1_UP}
+ {$define COMPILER_2_UP}
+ {$define COMPILER_3_UP}
+ {$define COMPILER_4_UP}
+ {$define COMPILER_5_UP}
+ {$define COMPILER_6_UP}
+{$endif}
+
+
+{$ifdef COMPILER_7}
+ {$define COMPILER_1_UP}
+ {$define COMPILER_2_UP}
+ {$define COMPILER_3_UP}
+ {$define COMPILER_4_UP}
+ {$define COMPILER_5_UP}
+ {$define COMPILER_6_UP}
+ {$define COMPILER_7_UP}
+{$endif}
+
+
+{$ifdef COMPILER_8}
+ {$define COMPILER_1_UP}
+ {$define COMPILER_2_UP}
+ {$define COMPILER_3_UP}
+ {$define COMPILER_4_UP}
+ {$define COMPILER_5_UP}
+ {$define COMPILER_6_UP}
+ {$define COMPILER_7_UP}
+ {$define COMPILER_8_UP}
+{$endif}
+
+
+{$ifdef COMPILER_9}
+ {$define COMPILER_1_UP}
+ {$define COMPILER_2_UP}
+ {$define COMPILER_3_UP}
+ {$define COMPILER_4_UP}
+ {$define COMPILER_5_UP}
+ {$define COMPILER_6_UP}
+ {$define COMPILER_7_UP}
+ {$define COMPILER_8_UP}
+ {$define COMPILER_9_UP}
+{$endif}
+
+
+{$ifdef COMPILER_10}
+ {$define COMPILER_1_UP}
+ {$define COMPILER_2_UP}
+ {$define COMPILER_3_UP}
+ {$define COMPILER_4_UP}
+ {$define COMPILER_5_UP}
+ {$define COMPILER_6_UP}
+ {$define COMPILER_7_UP}
+ {$define COMPILER_8_UP}
+ {$define COMPILER_9_UP}
+ {$define COMPILER_10_UP}
+ // Backwards compatibility
+ {$define DELPHI_2_UP}
+ {$define DELPHI_3_UP}
+ {$define DELPHI_4_UP}
+ {$define DELPHI_5_UP}
+ {$define DELPHI_6_UP}
+ {$define DELPHI_7_UP}
+ {$define DELPHI_8_UP}
+ {$define DELPHI_9_UP}
+ {$define CPPB_3_UP}
+ {$define CPPB_4_UP}
+ {$define CPPB_5_UP}
+ {$define CPPB_6_UP}
+
+
+ {$ifdef BCB}
+ {$define CPPB}
+ {$else}
+ {$define DELPHI}
+ {$endif}
+
+
+{$endif}
+
+
+{$ifdef COMPILER_11}
+ {$define COMPILER_1_UP}
+ {$define COMPILER_2_UP}
+ {$define COMPILER_3_UP}
+ {$define COMPILER_4_UP}
+ {$define COMPILER_5_UP}
+ {$define COMPILER_6_UP}
+ {$define COMPILER_7_UP}
+ {$define COMPILER_8_UP}
+ {$define COMPILER_9_UP}
+ {$define COMPILER_10_UP}
+ {$define COMPILER_11_UP}
+ // Backwards compatibility
+ {$define DELPHI_2_UP}
+ {$define DELPHI_3_UP}
+ {$define DELPHI_4_UP}
+ {$define DELPHI_5_UP}
+ {$define DELPHI_6_UP}
+ {$define DELPHI_7_UP}
+ {$define DELPHI_8_UP}
+ {$define DELPHI_9_UP}
+ {$define CPPB_3_UP}
+ {$define CPPB_4_UP}
+ {$define CPPB_5_UP}
+ {$define CPPB_6_UP}
+
+
+ {$ifdef BCB}
+ {$define CPPB}
+ {$else}
+ {$define DELPHI}
+ {$endif}
+
+
+{$endif}
+
+
+
+
+{$ifdef COMPILER_12}
+ {$define COMPILER_1_UP}
+ {$define COMPILER_2_UP}
+ {$define COMPILER_3_UP}
+ {$define COMPILER_4_UP}
+ {$define COMPILER_5_UP}
+ {$define COMPILER_6_UP}
+ {$define COMPILER_7_UP}
+ {$define COMPILER_8_UP}
+ {$define COMPILER_9_UP}
+ {$define COMPILER_10_UP}
+ {$define COMPILER_11_UP}
+ {$define COMPILER_12_UP}
+ // Backwards compatibility
+ {$define DELPHI_2_UP}
+ {$define DELPHI_3_UP}
+ {$define DELPHI_4_UP}
+ {$define DELPHI_5_UP}
+ {$define DELPHI_6_UP}
+ {$define DELPHI_7_UP}
+ {$define DELPHI_8_UP}
+ {$define DELPHI_9_UP}
+ {$define CPPB_3_UP}
+ {$define CPPB_4_UP}
+ {$define CPPB_5_UP}
+ {$define CPPB_6_UP}
+
+
+ {$ifdef BCB}
+ {$define CPPB}
+ {$else}
+ {$define DELPHI}
+ {$endif}
+
+
+{$endif}
+
+
+
+
+{$ifdef COMPILER_14}
+ {$define COMPILER_1_UP}
+ {$define COMPILER_2_UP}
+ {$define COMPILER_3_UP}
+ {$define COMPILER_4_UP}
+ {$define COMPILER_5_UP}
+ {$define COMPILER_6_UP}
+ {$define COMPILER_7_UP}
+ {$define COMPILER_8_UP}
+ {$define COMPILER_9_UP}
+ {$define COMPILER_10_UP}
+ {$define COMPILER_11_UP}
+ {$define COMPILER_12_UP}
+ {$define COMPILER_14_UP}
+ // Backwards compatibility
+ {$define DELPHI_2_UP}
+ {$define DELPHI_3_UP}
+ {$define DELPHI_4_UP}
+ {$define DELPHI_5_UP}
+ {$define DELPHI_6_UP}
+ {$define DELPHI_7_UP}
+ {$define DELPHI_8_UP}
+ {$define DELPHI_9_UP}
+ {$define CPPB_3_UP}
+ {$define CPPB_4_UP}
+ {$define CPPB_5_UP}
+ {$define CPPB_6_UP}
+
+
+ {$ifdef BCB}
+ {$define CPPB}
+ {$else}
+ {$define DELPHI}
+ {$endif}
+
+
+{$endif}
+
+
+{$ifdef COMPILER_15}
+ {$define COMPILER_1_UP}
+ {$define COMPILER_2_UP}
+ {$define COMPILER_3_UP}
+ {$define COMPILER_4_UP}
+ {$define COMPILER_5_UP}
+ {$define COMPILER_6_UP}
+ {$define COMPILER_7_UP}
+ {$define COMPILER_8_UP}
+ {$define COMPILER_9_UP}
+ {$define COMPILER_10_UP}
+ {$define COMPILER_11_UP}
+ {$define COMPILER_12_UP}
+ {$define COMPILER_14_UP}
+ {$define COMPILER_15_UP}
+ // Backwards compatibility
+ {$define DELPHI_2_UP}
+ {$define DELPHI_3_UP}
+ {$define DELPHI_4_UP}
+ {$define DELPHI_5_UP}
+ {$define DELPHI_6_UP}
+ {$define DELPHI_7_UP}
+ {$define DELPHI_8_UP}
+ {$define DELPHI_9_UP}
+ {$define CPPB_3_UP}
+ {$define CPPB_4_UP}
+ {$define CPPB_5_UP}
+ {$define CPPB_6_UP}
+
+
+ {$ifdef BCB}
+ {$define CPPB}
+ {$else}
+ {$define DELPHI}
+ {$endif}
+
+
+{$endif}
+
+
+{$ifdef COMPILER_16}
+ {$define COMPILER_1_UP}
+ {$define COMPILER_2_UP}
+ {$define COMPILER_3_UP}
+ {$define COMPILER_4_UP}
+ {$define COMPILER_5_UP}
+ {$define COMPILER_6_UP}
+ {$define COMPILER_7_UP}
+ {$define COMPILER_8_UP}
+ {$define COMPILER_9_UP}
+ {$define COMPILER_10_UP}
+ {$define COMPILER_11_UP}
+ {$define COMPILER_12_UP}
+ {$define COMPILER_14_UP}
+ {$define COMPILER_15_UP}
+ {$define COMPILER_16_UP}
+ // Backwards compatibility
+ {$define DELPHI_2_UP}
+ {$define DELPHI_3_UP}
+ {$define DELPHI_4_UP}
+ {$define DELPHI_5_UP}
+ {$define DELPHI_6_UP}
+ {$define DELPHI_7_UP}
+ {$define DELPHI_8_UP}
+ {$define DELPHI_9_UP}
+ {$define CPPB_3_UP}
+ {$define CPPB_4_UP}
+ {$define CPPB_5_UP}
+ {$define CPPB_6_UP}
+
+
+ {$ifdef BCB}
+ {$define CPPB}
+ {$else}
+ {$define DELPHI}
+ {$endif}
+
+
+{$endif}
+
+{$UNDEF AllowInline}
+{$IFDEF FPC}
+ {$DEFINE AllowInline}
+{$ELSE}
+ {$IFDEF COMPILER_15_UP}
+ {$DEFINE AllowInline}
+ {$ENDIF}
+{$ENDIF}
diff --git a/plugins/Utils.pas/dbsettings.pas b/plugins/Utils.pas/dbsettings.pas
new file mode 100644
index 0000000000..9c8578b225
--- /dev/null
+++ b/plugins/Utils.pas/dbsettings.pas
@@ -0,0 +1,481 @@
+{$DEFINE UseCore}
+{$INCLUDE compilers.inc}
+unit dbsettings;
+interface
+
+uses windows,m_api;
+
+function DBReadByte (hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;default:byte =0):byte;
+function DBReadWord (hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;default:word =0):word;
+function DBReadDWord(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;default:dword=0):dword;
+
+function DBReadSetting (hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;dbv:PDBVARIANT):int_ptr;
+function DBReadSettingStr(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;dbv:PDBVARIANT):int_ptr;
+
+function DBReadStringLength(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar):integer;
+function DBReadString (hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;
+ default:PAnsiChar=nil;enc:integer=DBVT_ASCIIZ):PAnsiChar;
+function DBReadUTF8 (hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;default:PAnsiChar=nil):PAnsiChar;
+function DBReadUnicode(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;default:PWideChar=nil):PWideChar;
+
+function DBReadStruct (hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;
+ ptr:pointer;size:dword):Integer;
+function DBWriteStruct(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;
+ ptr:pointer;size:dword):Integer;
+
+function DBWriteSetting(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;dbv:PDBVARIANT):int_ptr;
+function DBWriteByte (hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;val:Byte ):int_ptr;
+function DBWriteWord (hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;val:Word ):int_ptr;
+function DBWriteDWord(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;val:dword):int_ptr;
+
+function DBWriteString (hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;
+ val:PAnsiChar;enc:integer=DBVT_ASCIIZ):int_ptr;
+function DBWriteUTF8 (hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;val:PAnsiChar):int_ptr;
+function DBWriteUnicode(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;val:PWideChar):int_ptr;
+
+function DBFreeVariant(dbv:PDBVARIANT):int_ptr;
+function DBDeleteSetting(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar):int_ptr;
+
+function DBDeleteGroup(hContact:THANDLE;szModule:PAnsiChar;prefix:pAnsiChar=nil):int_ptr;
+
+function DBDeleteModule(szModule:PAnsiChar):integer; // 0.8.0+
+
+function DBGetSettingType(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar):integer;
+
+implementation
+
+uses common;
+
+function DBReadByte(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;default:byte=0):byte;
+{$IFDEF UseCore}
+ {$IFDEF AllowInline}inline;{$ENDIF}
+begin
+ result:=db_get_b(hContact, szModule, szSetting, default);
+end;
+{$ELSE}
+var
+ dbv:TDBVARIANT;
+ cgs:TDBCONTACTGETSETTING;
+begin
+ cgs.szModule :=szModule;
+ cgs.szSetting:=szSetting;
+ cgs.pValue :=@dbv;
+ If CallService(MS_DB_CONTACT_GETSETTING,hContact,lParam(@cgs))<>0 then
+ Result:=default
+ else
+ Result:=dbv.bVal;
+end;
+{$ENDIF}
+
+function DBReadWord(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;default:word=0):word;
+{$IFDEF UseCore}
+ {$IFDEF AllowInline}inline;{$ENDIF}
+begin
+ result:=db_get_w(hContact, szModule, szSetting, default);
+end;
+{$ELSE}
+var
+ dbv:TDBVARIANT;
+ cgs:TDBCONTACTGETSETTING;
+begin
+ cgs.szModule :=szModule;
+ cgs.szSetting:=szSetting;
+ cgs.pValue :=@dbv;
+ If CallService(MS_DB_CONTACT_GETSETTING,hContact,lParam(@cgs))<>0 then
+ Result:=default
+ else
+ Result:=dbv.wVal;
+end;
+{$ENDIF}
+
+function DBReadDWord(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;default:dword=0):dword;
+{$IFDEF UseCore}
+ {$IFDEF AllowInline}inline;{$ENDIF}
+begin
+ result:=db_get_dw(hContact, szModule, szSetting, default);
+end;
+{$ELSE}
+var
+ dbv:TDBVARIANT;
+ cgs:TDBCONTACTGETSETTING;
+begin
+ cgs.szModule :=szModule;
+ cgs.szSetting:=szSetting;
+ cgs.pValue :=@dbv;
+ If CallService(MS_DB_CONTACT_GETSETTING,hContact,lParam(@cgs))<>0 then
+ Result:=default
+ else
+ Result:=dbv.dVal;
+end;
+{$ENDIF}
+
+function DBReadSetting(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;dbv:PDBVARIANT):int_ptr;
+{$IFDEF UseCore}
+ {$IFDEF AllowInline}inline;{$ENDIF}
+begin
+ result:=db_get(hContact, szModule, szSetting, dbv);
+end;
+{$ELSE}
+var
+ cgs:TDBCONTACTGETSETTING;
+begin
+ cgs.szModule :=szModule;
+ cgs.szSetting:=szSetting;
+ cgs.pValue :=dbv;
+ Result:=CallService(MS_DB_CONTACT_GETSETTING,hContact,lParam(@cgs));
+end;
+{$ENDIF}
+
+function DBReadSettingStr(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;dbv:PDBVARIANT):int_ptr;
+var
+ cgs:TDBCONTACTGETSETTING;
+begin
+ cgs.szModule :=szModule;
+ cgs.szSetting:=szSetting;
+ cgs.pValue :=dbv;
+ Result:=CallService(MS_DB_CONTACT_GETSETTING_STR,hContact,lParam(@cgs));
+end;
+
+function DBReadStringLength(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar):integer;
+var
+ cgs:TDBCONTACTGETSETTING;
+ dbv:TDBVARIANT;
+ i:int_ptr;
+begin
+ FillChar(dbv,SizeOf(dbv),0);
+ cgs.szModule :=szModule;
+ cgs.szSetting:=szSetting;
+ cgs.pValue :=@dbv;
+ i:=CallService(MS_DB_CONTACT_GETSETTING_STR,hContact,lParam(@cgs));
+ if (i<>0) or (dbv.szVal.a=nil) or (dbv.szVal.a^=#0) then
+ result:=0
+ else
+ result:=lstrlena(dbv.szVal.a);
+//!! if i=0 then
+ DBFreeVariant(@dbv);
+end;
+
+function DBReadString(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;
+ default:PAnsiChar=nil;enc:integer=DBVT_ASCIIZ):PAnsiChar;
+var
+ cgs:TDBCONTACTGETSETTING;
+ dbv:TDBVARIANT;
+ i:int_ptr;
+begin
+ FillChar(dbv,SizeOf(dbv),0);
+ cgs.szModule :=szModule;
+ cgs.szSetting:=szSetting;
+ cgs.pValue :=@dbv;
+ dbv._type :=enc;
+ i:=CallService(MS_DB_CONTACT_GETSETTING_STR,hContact,lParam(@cgs));
+ if i=0 then
+ default:=dbv.szVal.a;
+
+ if (default=nil) or (default^=#0) then
+ result:=nil
+ else
+ StrDup(result,default);
+
+//!! if i=0 then
+ DBFreeVariant(@dbv);
+end;
+
+function DBReadUTF8(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;default:PAnsiChar=nil):PAnsiChar;
+begin
+ result:=DBReadString(hContact,szModule,szSetting,default,DBVT_UTF8);
+end;
+
+function DBReadUnicode(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;default:PWideChar=nil):PWideChar;
+var
+ cgs:TDBCONTACTGETSETTING;
+ dbv:TDBVARIANT;
+ i:int_ptr;
+begin
+ FillChar(dbv,SizeOf(dbv),0);
+ cgs.szModule :=szModule;
+ cgs.szSetting:=szSetting;
+ cgs.pValue :=@dbv;
+ dbv._type :=DBVT_WCHAR;
+ i:=CallService(MS_DB_CONTACT_GETSETTING_STR,hContact,lParam(@cgs));
+ if i=0 then
+ default:=dbv.szVal.w;
+
+ if (default=nil) or (default^=#0) then
+ result:=nil
+ else
+ StrDupW(result,default);
+
+//!! if i=0 then
+ DBFreeVariant(@dbv);
+end;
+
+function DBReadStruct(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;
+ ptr:pointer;size:dword):Integer;
+var
+ dbv:TDBVariant;
+begin
+ FillChar(dbv,SizeOf(dbv),0);
+ dbv._type:=DBVT_BLOB;
+ dbv.pbVal:=nil;
+ if (DBReadSetting(0,szModule,szSetting,@dbv)=0) and
+ (dbv.pbVal<>nil) and (dbv.cpbVal=size) then
+ begin
+ move(dbv.pbVal^,ptr^,size);
+ DBFreeVariant(@dbv);
+ result:=1;
+ end
+ else
+ result:=0;
+end;
+
+function DBWriteStruct(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;
+ ptr:pointer;size:dword):Integer;
+{$IFDEF UseCore}
+ {$IFDEF AllowInline}inline;{$ENDIF}
+begin
+ result:=db_set_blob(hContact, szModule, szSetting, ptr, size);
+end;
+{$ELSE}
+var
+ cws:TDBCONTACTWRITESETTING;
+begin
+ cws.szModule :=szModule;
+ cws.szSetting :=szSetting;
+ cws.value._type :=DBVT_BLOB;
+ cws.value.pbVal :=ptr;
+ cws.value.cpbVal:=size;
+ result:=CallService(MS_DB_CONTACT_WRITESETTING,0,lParam(@cws));
+end;
+{$ENDIF}
+
+function DBWriteSetting(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;dbv:PDBVARIANT):int_ptr;
+var
+ cws: TDBCONTACTWRITESETTING;
+begin
+ cws.szModule :=szModule;
+ cws.szSetting :=szSetting;
+ move(dbv^,cws.value,SizeOf(TDBVARIANT));
+ Result := CallService(MS_DB_CONTACT_WRITESETTING, hContact, lParam(@cws));
+end;
+
+function DBWriteByte(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;val:Byte):int_ptr;
+{$IFDEF UseCore}
+ {$IFDEF AllowInline}inline;{$ENDIF}
+begin
+ result:=db_set_b(hContact, szModule, szSetting, val);
+end;
+{$ELSE}
+var
+ cws:TDBCONTACTWRITESETTING;
+begin
+ cws.szModule :=szModule;
+ cws.szSetting :=szSetting;
+ cws.value._type:=DBVT_BYTE;
+ cws.value.bVal :=val;
+ Result:=CallService(MS_DB_CONTACT_WRITESETTING,hContact,lParam(@cws));
+end;
+{$ENDIF}
+
+function DBWriteWord(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;val:Word):int_ptr;
+{$IFDEF UseCore}
+ {$IFDEF AllowInline}inline;{$ENDIF}
+begin
+ result:=db_set_w(hContact, szModule, szSetting, val);
+end;
+{$ELSE}
+var
+ cws:TDBCONTACTWRITESETTING;
+begin
+ cws.szModule :=szModule;
+ cws.szSetting :=szSetting;
+ cws.value._type:=DBVT_WORD;
+ cws.value.wVal :=val;
+ Result:=CallService(MS_DB_CONTACT_WRITESETTING,hContact,lParam(@cws));
+end;
+{$ENDIF}
+
+function DBWriteDWord(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;val:dword):int_ptr;
+{$IFDEF UseCore}
+ {$IFDEF AllowInline}inline;{$ENDIF}
+begin
+ result:=db_set_dw(hContact, szModule, szSetting, val);
+end;
+{$ELSE}
+var
+ cws:TDBCONTACTWRITESETTING;
+begin
+ cws.szModule :=szModule;
+ cws.szSetting :=szSetting;
+ cws.value._type:=DBVT_DWORD;
+ cws.value.dVal :=val;
+ Result:=CallService(MS_DB_CONTACT_WRITESETTING,hContact,lParam(@cws));
+end;
+{$ENDIF}
+
+function DBWriteString(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;
+ val:PAnsiChar;enc:integer=DBVT_ASCIIZ):int_ptr;
+{$IFDEF UseCore}
+ {$IFDEF AllowInline}inline;{$ENDIF}
+begin
+ result:=db_set_s(hContact, szModule, szSetting, val);
+end;
+{$ELSE}
+var
+ cws:TDBCONTACTWRITESETTING;
+ p:dword;
+begin
+ cws.szModule :=szModule;
+ cws.szSetting :=szSetting;
+ cws.value._type :=enc;
+ if val=nil then
+ begin
+ p:=0;
+ val:=@p;
+ end;
+ cws.value.szVal.a:=val;
+ Result:=CallService(MS_DB_CONTACT_WRITESETTING,hContact,lParam(@cws));
+end;
+{$ENDIF}
+
+function DBWriteUTF8(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;val:PAnsiChar):int_ptr;
+{$IFDEF UseCore}
+ {$IFDEF AllowInline}inline;{$ENDIF}
+begin
+ result:=db_set_utf(hContact, szModule, szSetting, val);
+end;
+{$ELSE}
+begin
+ result:=DBWriteString(hContact,szModule,szSetting,val,DBVT_UTF8);
+end;
+{$ENDIF}
+
+function DBWriteUnicode(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar;val:PWideChar):int_ptr;
+{$IFDEF UseCore}
+ {$IFDEF AllowInline}inline;{$ENDIF}
+begin
+ result:=db_set_ws(hContact, szModule, szSetting, val);
+end;
+{$ELSE}
+begin
+ result:=DBWriteString(hContact,szModule,szSetting,PAnsiChar(val),DBVT_WCHAR);
+{
+var
+ cws:TDBCONTACTWRITESETTING;
+begin
+ cws.szModule :=szModule;
+ cws.szSetting :=szSetting;
+ cws.value._type :=DBVT_WCHAR;
+ cws.value.szVal.w:=Val;
+ Result:=CallService(MS_DB_CONTACT_WRITESETTING,hContact,lParam(@cws));
+}
+end;
+{$ENDIF}
+
+function DBFreeVariant(dbv:PDBVARIANT):int_ptr;
+{$IFDEF UseCore}
+ {$IFDEF AllowInline}inline;{$ENDIF}
+begin
+ result:=db_free(dbv);
+end;
+{$ELSE}
+begin
+ Result:=CallService(MS_DB_CONTACT_FREEVARIANT,0,lParam(dbv));
+end;
+{$ENDIF}
+
+function DBDeleteSetting(hContact:THandle;szModule:PAnsiChar;szSetting:PAnsiChar):int_ptr;
+{$IFDEF UseCore}
+ {$IFDEF AllowInline}inline;{$ENDIF}
+begin
+ result:=db_unset(hContact, szModule, szSetting);
+end;
+{$ELSE}
+var
+ cgs:TDBCONTACTGETSETTING;
+begin
+ cgs.szModule :=szModule;
+ cgs.szSetting:=szSetting;
+ Result:=CallService(MS_DB_CONTACT_DELETESETTING,hContact,lParam(@cgs));
+end;
+{$ENDIF}
+
+type
+ ppchar = ^pAnsiChar;
+
+function EnumSettingsProc(const szSetting:PAnsiChar;lParam:LPARAM):int; cdecl;
+begin
+ lstrcpya(ppchar(lParam)^,szSetting);
+ while ppchar(lParam)^^<>#0 do inc(ppchar(lParam)^);
+ inc(ppchar(lParam)^);
+ result:=0;
+end;
+function EnumSettingsProcCalc(const szSetting:PAnsiChar;lParam:LPARAM):int; cdecl;
+begin
+ inc(pint_ptr(lParam)^,lstrlena(szSetting)+1);
+ result:=0;
+end;
+
+function DBDeleteGroup(hContact:THANDLE;szModule:PAnsiChar;prefix:pAnsiChar=nil):int_ptr;
+var
+ ces:TDBCONTACTENUMSETTINGS;
+ cgs:TDBCONTACTGETSETTING;
+ p:PAnsiChar;
+ num,len:integer;
+ ptr:pAnsiChar;
+begin
+ ces.szModule:=szModule;
+ num:=0;
+
+ ces.pfnEnumProc:=@EnumSettingsProcCalc;
+ ces.lParam :=lParam(@num);
+ ces.ofsSettings:=0;
+ CallService(MS_DB_CONTACT_ENUMSETTINGS,hContact,lparam(@ces));
+
+ GetMem(p,num+1);
+ ptr:=p;
+ ces.pfnEnumProc:=@EnumSettingsProc;
+ ces.lParam :=lparam(@ptr);
+ ces.ofsSettings:=0;
+ result:=CallService(MS_DB_CONTACT_ENUMSETTINGS,hContact,lparam(@ces));
+ ptr^:=#0;
+
+ cgs.szModule:=szModule;
+ ptr:=p;
+ if (prefix<>nil) and (prefix^<>#0) then
+ len:=StrLen(prefix)
+ else
+ len:=0;
+ while ptr^<>#0 do
+ begin
+ if (len=0) or (StrCmp(prefix,ptr,len)=0) then
+ begin
+ cgs.szSetting:=ptr;
+ CallService(MS_DB_CONTACT_DELETESETTING,hContact,lParam(@cgs));
+ end;
+ while ptr^<>#0 do inc(ptr);
+ inc(ptr);
+ end;
+ FreeMem(p);
+end;
+
+function DBDeleteModule(szModule:PAnsiChar):integer; // 0.8.0+
+begin
+ result:=0;
+ CallService(MS_DB_MODULE_DELETE,0,lParam(szModule));
+end;
+
+function DBGetSettingType(hContact:THANDLE;szModule:PAnsiChar;szSetting:PAnsiChar):integer;
+var
+ ldbv:TDBVARIANT;
+begin
+ if DBReadSetting(hContact,szModule,szSetting,@ldbv)=0 then
+ begin
+ result:=ldbv._type;
+ DBFreeVariant(@ldbv);
+ end
+ else
+ result:=DBVT_DELETED;
+end;
+
+begin
+end.
diff --git a/plugins/Utils.pas/i_card_const.inc b/plugins/Utils.pas/i_card_const.inc
new file mode 100644
index 0000000000..ee0e153a28
--- /dev/null
+++ b/plugins/Utils.pas/i_card_const.inc
@@ -0,0 +1,16 @@
+{resource constants}
+const
+ // dialogs
+// IDD_HELP = 1126;
+
+// Help dialog
+ IDC_HLP_SERVICE = 1025;
+ IDC_HLP_ALIAS = 1026;
+ IDC_HLP_PLUGIN = 1027;
+ IDC_HLP_WPARAM = 1028;
+ IDC_HLP_WPARAML = 1128;
+ IDC_HLP_LPARAM = 1029;
+ IDC_HLP_LPARAML = 1129;
+ IDC_HLP_RETURN = 1030;
+ IDC_HLP_EFFECT = 1031;
+ IDC_HLP_NOTE = 1040;
diff --git a/plugins/Utils.pas/i_struct_const.inc b/plugins/Utils.pas/i_struct_const.inc
new file mode 100644
index 0000000000..00e0b9324e
--- /dev/null
+++ b/plugins/Utils.pas/i_struct_const.inc
@@ -0,0 +1,44 @@
+{resource constants}
+const
+ // dialogs
+// IDD_STRUCTURE = 1027;
+// IDD_HELP = 1028;
+
+ // icons
+// IDI_NEW = 1125;
+// IDI_UP = 1126;
+// IDI_DOWN = 1127;
+// IDI_DELETE = 1128;
+
+ // Structure editor
+ IDC_DATA_FULL = 2001;
+ IDC_DATA_TYPE = 2002;
+ IDC_DATA_EDIT = 2005;
+ IDC_DATA_LEN = 2006;
+ IDC_DATA_HELP = 2007;
+
+ IDC_DATA_NEW = 2008;
+ IDC_DATA_DELETE = 2009;
+ IDC_DATA_UP = 2010;
+ IDC_DATA_DOWN = 2011;
+ IDC_DATA_CHANGE = 2012;
+ IDC_DATA_VARS = 2013;
+ IDC_DATA_ALIGN = 2014;
+ IDC_DATA_MMI = 2015;
+
+ IDC_DATA_SALGN = 2016;
+ IDC_DATA_SEP = 2017;
+ IDC_DATA_EDTN = 2018;
+
+ IDC_DATA_TMPL = 2019;
+ IDC_DATA_INFO = 2020;
+ IDC_DATA_PASTE = 2021;
+
+ IDC_DATA_SLEN = 2022;
+ IDC_VAR_HELP = 2023;
+
+ // Structure helper
+ IDC_HLP_NAME = 2030;
+ IDC_HLP_PLUGIN = 2031;
+ IDC_HLP_DESCR = 2032;
+ IDC_HLP_STRUCT = 2033;
diff --git a/plugins/Utils.pas/icobuttons.pas b/plugins/Utils.pas/icobuttons.pas
new file mode 100644
index 0000000000..fdac39e69e
--- /dev/null
+++ b/plugins/Utils.pas/icobuttons.pas
@@ -0,0 +1,392 @@
+unit IcoButtons;
+
+interface
+
+uses windows, KOL;
+
+const
+ AST_NORMAL = 0;
+ AST_HOVERED = 1;
+ AST_PRESSED = 2;
+
+type
+ tGetIconProc = function(action:integer;stat:integer=AST_NORMAL):cardinal;
+ tActionProc = function(action:integer):integer;
+
+type
+ pIcoButton = ^tIcoButton;
+ tIcoButton = object(TControl)
+ private
+ function GetGetIconProc:tGetIconProc;
+ procedure SetGetIconProc (val:tGetIconProc);
+ procedure SetDoActionProc(val:tActionProc);
+ procedure SetCheckFlag(val:boolean);
+ function GetCheckFlag:boolean;
+ procedure SetAction(val:integer);
+ function GetAction:integer;
+ function GetState:integer;
+ procedure myPaint(Sender: PControl; DC: HDC);
+ procedure myMouseDown (Sender:PControl; var Mouse:TMouseEventData);
+ procedure myMouseUp (Sender:PControl; var Mouse:TMouseEventData);
+ procedure myMouseEnter(Sender: PObj);
+ procedure myMouseLeave(Sender: PObj);
+ procedure myCtrlBtnClick(Sender: PObj);
+ public
+
+ procedure RefreshIcon;
+ property GetIconProc : tGetIconProc read GetGetIconProc write SetGetIconProc;
+ property DoActionProc: tActionProc write SetDoActionProc;
+
+ property AsCheckbox: boolean read GetCheckFlag write SetCheckFlag;
+ property Action : integer read GetAction write SetAction;
+ property State : integer read GetState;
+ end;
+
+function CreateIcoButton(AOwner: PControl; pGetIconProc:tGetIconProc;
+ pActionProc:tActionProc; action:integer=0; repeattime:integer=0):pIcoButton;
+
+function CreateIcoButtonHandle(AOwner: PControl; pActionProc:tActionProc;
+ ico_normal:HICON; ico_hovered:HICON=0; ico_pressed:HICON=0;
+ action:integer=0; repeattime:integer=0):pIcoButton;
+
+implementation
+
+uses messages;
+
+type
+ pIcoBtnData = ^tIcoBtnData;
+ tIcoBtnData = record
+ rptvalue:cardinal;
+ rpttimer:cardinal;
+ checking: boolean;
+
+ ico_normal :PIcon;
+ ico_hovered:PIcon;
+ ico_pressed:PIcon;
+ active :PIcon; // one of ico_*
+
+ Action:integer;
+
+ GetIcon : tGetIconProc;
+ DoAction: tActionProc;
+ end;
+
+function tIcoButton.GetGetIconProc:tGetIconProc;
+begin
+ result:=pIcoBtnData(CustomData).GetIcon;
+end;
+
+procedure tIcoButton.SetGetIconProc(val:tGetIconProc);
+begin
+ pIcoBtnData(CustomData).GetIcon:=val;
+end;
+
+procedure tIcoButton.SetDoActionProc(val:tActionProc);
+begin
+ pIcoBtnData(CustomData).DoAction:=val;
+end;
+
+procedure tIcoButton.SetCheckFlag(val:boolean);
+begin
+ pIcoBtnData(CustomData).checking:=val;
+end;
+
+function tIcoButton.GetCheckFlag:boolean;
+begin
+ result:=pIcoBtnData(CustomData).checking;
+end;
+
+procedure tIcoButton.SetAction(val:integer);
+begin
+ pIcoBtnData(CustomData).Action:=val;
+end;
+
+function tIcoButton.GetAction:integer;
+begin
+ result:=pIcoBtnData(CustomData).Action;
+end;
+
+function tIcoButton.GetState:integer;
+begin
+ with pIcoBtnData(CustomData)^ do
+ if active=ico_pressed then result:=AST_PRESSED
+ else if active=ico_hovered then result:=AST_HOVERED
+ else {if active=ico_normal then}result:=AST_NORMAL;
+end;
+
+procedure tIcoButton.myCtrlBtnClick(Sender: PObj);
+var
+ D: PIcoBtnData;
+begin
+ D:=PControl(Sender).CustomData;
+ if @D.DoAction<>nil then
+ D.DoAction(D.action);
+end;
+
+procedure tIcoButton.myMouseEnter(Sender: PObj);
+var
+ D: PIcoBtnData;
+begin
+ D:=PControl(Sender).CustomData;
+ if D.ico_hovered<>nil then
+ begin
+ D.active:=D.ico_hovered;
+ PControl(Sender).Update;
+// PControl(Sender).Parent.Update; //??
+ end;
+end;
+
+procedure tIcoButton.myMouseLeave(Sender: PObj);
+var
+ D: PIcoBtnData;
+begin
+ D:=PControl(Sender).CustomData;
+ if D.active=D.ico_hovered then //!!!! for case when mouse button pressed and mouse moved
+ D.active:=D.ico_normal;
+ PControl(Sender).Update;
+// PControl(Sender).Parent.Update; //??
+end;
+
+procedure TimerProc(wnd:HWND;uMsg:uint;idEvent:uint_ptr;dwTime:dword); stdcall;
+begin
+ PControl(IdEvent).OnClick(PControl(IdEvent));
+end;
+
+procedure tIcoButton.myMouseDown(Sender:PControl; var Mouse:TMouseEventData);
+var
+ D: PIcoBtnData;
+begin
+ if Mouse.Button<>mbLeft then exit;
+ D:=Sender.CustomData;
+ if D.checking then
+ begin
+ if D.active=D.ico_pressed then
+ D.active:=D.ico_normal
+ else
+ D.active:=D.ico_pressed;
+ end
+ else
+ begin
+ if D.ico_pressed<>nil then
+ D.active:=D.ico_pressed
+ else
+ Sender.SetPosition(Sender.Position.X-2,Sender.Position.Y-2);
+
+ if D.rptvalue<>0 then
+ begin
+ D.rpttimer:=SetTimer(Sender.Handle,dword(Sender),D.rptvalue,@TimerProc);
+// D.rpttimer:=SetTimer(Sender.GetWindowHandle,1,D.rptvalue,nil);
+ end;
+ end;
+ Sender.Update;
+end;
+
+procedure tIcoButton.myMouseUp(Sender:PControl; var Mouse:TMouseEventData);
+var
+ D: PIcoBtnData;
+ tp:TPOINT;
+begin
+ if Mouse.Button<>mbLeft then exit;
+ D:=Sender.CustomData;
+ if not D.checking then
+ begin
+ if D.rpttimer<>0 then
+ begin
+ KillTimer(Sender.Handle,D.rpttimer);
+ D.rpttimer:=0;
+ end;
+
+ if D.ico_pressed<>nil then
+ begin
+ tp.X:=Mouse.X;
+ tp.Y:=Mouse.Y;
+ // mouse still above button?
+ if (D.ico_hovered<>nil) and PtInRect(Sender.BoundsRect,tp) then
+ D.active:=D.ico_hovered
+ else
+ D.active:=D.ico_normal;
+ end
+ else
+ Sender.SetPosition(Sender.Position.X+2,Sender.Position.Y+2);
+ Sender.Update;
+ end;
+end;
+
+procedure Destroy(dummy:PControl;sender:PObj);
+var
+ D: PIcoBtnData;
+begin
+ D:=pIcoButton(sender).CustomData;
+ D.ico_normal.Free;
+ if D.ico_hovered<>nil then D.ico_hovered.Free;
+ if D.ico_pressed<>nil then D.ico_pressed.Free;
+
+ if D.rpttimer<>0 then
+ begin
+ KillTimer(0,D.rpttimer);
+ D.rpttimer:=0;
+ end;
+end;
+
+procedure tIcoButton.RefreshIcon;
+var
+ D: PIcoBtnData;
+begin
+ D:=CustomData;
+ if @D.GetIcon=nil then exit;
+
+ D.ico_normal.Handle:=D.GetIcon(D.action,AST_NORMAL);
+ D.ico_normal.ShareIcon:=true;
+ if D.ico_hovered<>nil then
+ begin
+ D.ico_hovered.Handle:=D.GetIcon(D.action,AST_HOVERED);
+ D.ico_hovered.ShareIcon:=true;
+ end;
+ if D.ico_pressed<>nil then
+ begin
+ D.ico_pressed.Handle:=D.GetIcon(D.action,AST_PRESSED);
+ D.ico_pressed.ShareIcon:=true;
+ end;
+end;
+
+procedure tIcoButton.myPaint(Sender: PControl; DC: HDC);
+var
+ D: PIcoBtnData;
+begin
+ D:=Sender.CustomData;
+ D.active.Draw(DC,0,0);
+end;
+
+function CreateIcoButton(AOwner: PControl; pGetIconProc:tGetIconProc;
+ pActionProc:tActionProc; action:integer=0; repeattime:integer=0):pIcoButton;
+var
+ ico:HICON;
+ D: PIcoBtnData;
+begin
+ // first, checking what icons are available
+ ico:=pGetIconProc(action,AST_NORMAL);
+ if ico=0 then
+ begin
+ result:=nil;
+ exit;
+ end;
+
+ Result:=pIcoButton(NewBitBtn(AOwner,'',[bboNoBorder,bboNoCaption],glyphOver,0,0));
+ if result=nil then exit;
+
+ Result.LikeSpeedButton.Flat:=true;
+ Result.Transparent:=true;
+
+ GetMem(D,SizeOf(TIcoBtnData));
+ Result.CustomData:=D;
+
+ Result.OnMouseDown :=Result.myMouseDown;
+ Result.OnMouseUp :=Result.myMouseUp;
+ Result.OnMouseEnter:=Result.myMouseEnter;
+ Result.OnMouseLeave:=Result.myMouseLeave;
+ Result.OnClick :=Result.myCtrlBtnClick;
+ Result.OnPaint :=Result.myPaint;
+
+ Result.AsCheckbox:=false;
+ Result.action:=action;
+
+ D.rptvalue:=repeattime;
+ D.rpttimer:=0;
+
+ Result.DoActionProc:=pActionProc;
+ Result.GetIconProc :=pGetIconProc;
+
+ D.ico_normal:=NewIcon;
+ D.ico_normal.Handle :=ico;
+ D.ico_normal.ShareIcon:=true;
+ D.active:=D.ico_normal;
+
+ ico:=D.GetIcon(action,AST_HOVERED);
+ if ico<>0 then
+ begin
+ D.ico_hovered:=NewIcon;
+ D.ico_hovered.Handle :=ico;
+ D.ico_hovered.ShareIcon:=true;
+ end
+ else
+ D.ico_hovered:=nil;
+ ico:=D.GetIcon(action,AST_PRESSED);
+ if ico<>0 then
+ begin
+ D.ico_pressed:=NewIcon;
+ D.ico_pressed.Handle :=ico;
+ D.ico_pressed.ShareIcon:=true;
+ end
+ else
+ D.ico_pressed:=nil;
+
+ Result.SetSize(16,16);
+ Result.SetPosition(0,0);
+ Result.OnDestroy:=TOnEvent(MakeMethod(nil,@DEstroy));
+end;
+
+function CreateIcoButtonHandle(AOwner: PControl; pActionProc:tActionProc;
+ ico_normal:HICON; ico_hovered:HICON=0; ico_pressed:HICON=0;
+ action:integer=0; repeattime:integer=0):pIcoButton;
+var
+ D: PIcoBtnData;
+begin
+ if ico_normal=0 then
+ begin
+ result:=nil;
+ exit;
+ end;
+
+ Result:=pIcoButton(NewBitBtn(AOwner,'',[bboNoBorder,bboNoCaption],glyphOver,0,0));
+ if result=nil then exit;
+ Result.LikeSpeedButton.Flat:=true;
+ Result.Transparent:=true;
+
+ GetMem(D,SizeOf(TIcoBtnData));
+ Result.CustomData:=D;
+
+ Result.OnMouseDown :=Result.myMouseDown;
+ Result.OnMouseUp :=Result.myMouseUp;
+ Result.OnMouseEnter:=Result.myMouseEnter;
+ Result.OnMouseLeave:=Result.myMouseLeave;
+ Result.OnClick :=Result.myCtrlBtnClick;
+ Result.OnPaint :=Result.myPaint;
+
+ Result.AsCheckbox:=false;
+ Result.action:=action;
+
+ D.rptvalue:=repeattime;
+ D.rpttimer:=0;
+
+ Result.GetIconProc :=nil;
+ Result.DoActionProc:=pActionProc;
+
+ D.ico_normal:=NewIcon;
+ D.ico_normal.Handle :=ico_normal;
+ D.ico_normal.ShareIcon:=true;
+ D.active:=D.ico_normal;
+
+ if ico_hovered<>0 then
+ begin
+ D.ico_hovered:=NewIcon;
+ D.ico_hovered.Handle :=ico_hovered;
+ D.ico_hovered.ShareIcon:=true;
+ end
+ else
+ D.ico_hovered:=nil;
+
+ if ico_pressed<>0 then
+ begin
+ D.ico_pressed:=NewIcon;
+ D.ico_pressed.Handle :=ico_pressed;
+ D.ico_pressed.ShareIcon:=true;
+ end
+ else
+ D.ico_pressed:=nil;
+
+ Result.SetSize(16,16);
+ Result.SetPosition(0,0);
+ Result.OnDestroy:=TOnEvent(MakeMethod(nil,@Destroy));
+end;
+
+end.
diff --git a/plugins/Utils.pas/io.pas b/plugins/Utils.pas/io.pas
new file mode 100644
index 0000000000..9a587c660f
--- /dev/null
+++ b/plugins/Utils.pas/io.pas
@@ -0,0 +1,249 @@
+unit io;
+
+interface
+uses windows;
+
+function Reset (fname:PWideChar):THANDLE; overload;
+function Reset (fname:PAnsiChar):THANDLE; overload;
+function ReWrite(fname:PWideChar):THANDLE; overload;
+function ReWrite(fname:PAnsiChar):THANDLE; overload;
+function Append (fname:PWideChar):THANDLE; overload;
+function Append (fname:PAnsiChar):THANDLE; overload;
+
+function GetFSize(name:PWideChar):dword; overload;
+function GetFSize(name:PAnsiChar):dword; overload;
+function FileExists(fname:PAnsiChar):Boolean; overload;
+function FileExists(fname:PWideChar):Boolean; overload;
+
+function Skip(f:THANDLE;count:integer):integer;
+function Seek(f:THANDLE;pos:integer):integer;
+function FilePos(f:THANDLE):dword;
+function FileSize(f:THANDLE):dword;
+function Eof(f:THANDLE):boolean;
+
+function BlockRead (f:THANDLE;var buf;size:integer):dword;
+function BlockWrite(f:THANDLE;var buf;size:integer):dword;
+
+function ForceDirectories(path:PAnsiChar):boolean; overload;
+function ForceDirectories(path:PWideChar):boolean; overload;
+function DirectoryExists(Directory:PAnsiChar):Boolean; overload;
+function DirectoryExists(Directory:PWideChar):Boolean; overload;
+
+implementation
+
+function Reset(fname:PWideChar):THANDLE;
+begin
+ result:=CreateFileW(fname,GENERIC_READ,FILE_SHARE_READ+FILE_SHARE_WRITE,nil,OPEN_EXISTING,0,0);
+end;
+
+function Reset(fname:PAnsiChar):THANDLE;
+begin
+ result:=CreateFileA(fname,GENERIC_READ,FILE_SHARE_READ+FILE_SHARE_WRITE,nil,OPEN_EXISTING,0,0);
+end;
+
+function Append(fname:PWideChar):THANDLE;
+begin
+ result:=CreateFileW(fname,GENERIC_WRITE,FILE_SHARE_READ,nil,OPEN_ALWAYS,0,0);
+ SetFilePointer(result,0,nil,FILE_END);
+end;
+
+function Append(fname:PAnsiChar):THANDLE;
+begin
+ result:=CreateFileA(fname,GENERIC_WRITE,FILE_SHARE_READ,nil,OPEN_ALWAYS,0,0);
+ SetFilePointer(result,0,nil,FILE_END);
+end;
+
+function ReWrite(fname:PWideChar):THANDLE; overload;
+begin
+ result:=CreateFileW(fname,GENERIC_WRITE,FILE_SHARE_READ,nil,CREATE_ALWAYS,0,0);
+end;
+
+function ReWrite(fname:PAnsiChar):THANDLE; overload;
+begin
+ result:=CreateFileA(fname,GENERIC_WRITE,FILE_SHARE_READ,nil,CREATE_ALWAYS,0,0);
+end;
+
+function Skip(f:THANDLE;count:integer):integer;
+begin
+ result:=SetFilePointer(f,count,nil,FILE_CURRENT);
+end;
+
+function Eof(f:THANDLE):boolean;
+begin
+ result:=FilePos(f)>=FileSize(f);
+end;
+
+function Seek(f:THANDLE;pos:integer):integer;
+begin
+ result:=SetFilePointer(f,pos,nil,FILE_BEGIN);
+end;
+
+function FilePos(f:THANDLE):dword;
+begin
+ result:=SetFilePointer(f,0,nil,FILE_CURRENT);
+end;
+
+function FileSize(f:THANDLE):dword;
+begin
+ result:=GetFileSize(f,nil);
+end;
+
+function BlockRead(f:THANDLE;var buf;size:integer):dword;
+begin
+ ReadFile(f,buf,size,result,nil);
+end;
+
+function BlockWrite(f:THANDLE;var buf;size:integer):dword;
+begin
+ WriteFile(f,buf,size,result,nil);
+end;
+
+function GetFSize(name:PWideChar):dword;
+var
+ lRec:WIN32_FIND_DATAW;
+ h:THANDLE;
+begin
+ h:=FindFirstFileW(name,lRec);
+ if h=THANDLE(INVALID_HANDLE_VALUE) then
+ result:=0
+ else
+ begin
+ result:=lRec.nFileSizeLow;
+ FindClose(h);
+ end;
+end;
+
+function GetFSize(name:PAnsiChar):dword;
+var
+ lRec:TWin32FindDataA;
+ h:THANDLE;
+begin
+ h:=FindFirstFileA(name,lRec);
+ if h=THANDLE(INVALID_HANDLE_VALUE) then
+ result:=0
+ else
+ begin
+ result:=lRec.nFileSizeLow;
+ FindClose(h);
+ end;
+end;
+
+function ForceDirectories(path:PAnsiChar):boolean;
+var
+ p,pc:PAnsiChar;
+ i:cardinal;
+ c:AnsiChar;
+begin
+ result:=true;
+ if DirectoryExists(path) then exit;
+ if (path<>nil) and (path^<>#0) then
+ begin
+ i:=lstrlena(path)+1;
+ GetMem(pc,i);
+ move(path^,pc^,i);
+ p:=pc;
+ if (p^ in ['A'..'Z','a'..'z']) and (p[1]=':') then inc(p,2);
+ if p^ in ['/','\'] then inc(p);
+ c:=#0;
+ while p^<>#0 do
+ begin
+ c:=' ';
+ if (p^ in ['/','\']) and (p[1]<>#0) then
+ begin
+ c:=p^;
+ p^:=#0;
+ if not CreateDirectoryA(pc,nil) then
+ begin
+ if GetLastError<>ERROR_ALREADY_EXISTS then
+ begin
+ result:=false;
+ FreeMem(pc);
+ exit;
+ end;
+ end;
+ p^:=c;
+ end;
+ inc(p);
+ end;
+ if (c<>#0) and (c=' ') then
+ if not CreateDirectoryA(pc,nil) then
+ result:=false;
+ FreeMem(pc);
+ end;
+end;
+
+function ForceDirectories(path:PWideChar):boolean;
+var
+ p,pc:PWideChar;
+ i:cardinal;
+ c:WideChar;
+begin
+ result:=true;
+ if DirectoryExists(path) then exit;
+ if (path<>nil) and (path^<>#0) then
+ begin
+ i:=(lstrlenw(path)+1)*SizeOf(WideChar);
+ GetMem(pc,i);
+ move(path^,pc^,i);
+ p:=pc;
+ if (((p^>='A') and (p^<='Z')) or ((p^>='a') and (p^<='z'))) and (p[1]=':') then inc(p,2);
+ if (p^='/') or (p^='\') then inc(p);
+ c:=#0;
+ while p^<>#0 do
+ begin
+ c:=' ';
+ if ((p^='/') or (p^='\')) and (p[1]<>#0) then
+ begin
+ c:=p^;
+ p^:=#0;
+ if not CreateDirectoryW(pc,nil) then
+ if GetLastError<>ERROR_ALREADY_EXISTS then
+ begin
+ result:=false;
+ FreeMem(pc);
+ exit;
+ end;
+ p^:=c;
+ end;
+ inc(p);
+ end;
+ if (c<>#0) and (c=' ') then
+ if not CreateDirectoryW(pc,nil) then
+ result:=false;
+ FreeMem(pc);
+ end;
+end;
+
+function DirectoryExists(Directory:PAnsiChar):Boolean;
+var
+ Code: Integer;
+begin
+ Code := GetFileAttributesA(Directory);
+ Result := (Code<>-1) and ((Code and FILE_ATTRIBUTE_DIRECTORY)<>0);
+end;
+
+function DirectoryExists(Directory:PWideChar):Boolean;
+var
+ Code: Integer;
+begin
+ Code := GetFileAttributesW(Directory);
+ Result := (Code<>-1) and ((Code and FILE_ATTRIBUTE_DIRECTORY)<>0);
+end;
+
+function FileExists(fname:PAnsiChar):Boolean;
+var
+ Code: Integer;
+begin
+ Code := GetFileAttributesA(fname);
+ Result := (Code<>-1) and ((Code and FILE_ATTRIBUTE_DIRECTORY)=0);
+end;
+
+function FileExists(fname:PWideChar):Boolean;
+var
+ Code: Integer;
+begin
+ Code := GetFileAttributesW(fname);
+ Result := (Code<>-1) and ((Code and FILE_ATTRIBUTE_DIRECTORY)=0);
+end;
+
+end.
diff --git a/plugins/Utils.pas/kolsizer.pas b/plugins/Utils.pas/kolsizer.pas
new file mode 100644
index 0000000000..28361a4585
--- /dev/null
+++ b/plugins/Utils.pas/kolsizer.pas
@@ -0,0 +1,538 @@
+unit KOLSizer;
+//
+// purpose: KOL control sizercontrol and design grid
+// author: © 2004, Thaddy de Koning
+// Remarks: Tnx in part to Marco Cantu for the sizer idea in DDH3
+// copyrighted freeware.
+//
+interface
+
+uses
+ Windows, Messages, Kol;
+
+const
+ DESIGNER_NORESIZE = 1;
+
+type
+ PDesigner=^TDesigner;
+ TDesigner=object(TStrlistEx)
+ private
+ fOwner:pControl;
+ fSpacing:Cardinal;
+ FOldPaint:TOnPaint;
+ fActive: boolean;
+ fSizer:PControl;
+ FOnControlChange: TonEvent;
+// FOnDblClick:TOnEvent;
+// FOnMouseDown:TOnMouse;
+ fCurrent: pControl;
+// FAction:integer;
+
+ procedure setactive(const Value: boolean);
+ function PrepareClassname(aControl: PControl): KOLString;
+ function UniqueName(aName: KOLString; flags:cardinal): KOLString;
+ procedure SetCurrent(const Value: pControl);
+ procedure InternalControlChange(sender:pObj);
+ procedure Setspacing(Space:cardinal = 8);
+ function GetFlags(aControl:pControl):cardinal;
+ protected
+ procedure init;virtual;
+ procedure DoKeyUp( Sender: PControl; var Key: Longint; Shift: DWORD);
+ procedure DoChar( Sender: PControl; var Key: KOLChar; Shift: DWORD);
+ public
+ destructor destroy;virtual;
+ procedure Connect(aName: KOLString; aControl: pControl; flags:cardinal=0);
+ procedure DisConnect(aControl: pControl);
+ procedure Paintgrid(sender:pControl;DC:HDC);
+
+ property Spacing:cardinal read fSpacing write setspacing;
+ property Active:boolean read fActive write setactive;
+// property Action:integer read FAction write Faction;
+ property Current:pControl read fCurrent write SetCurrent;
+ property OnControlChange:TOnEvent Read FOnControlChange write FOnControlChange;
+// property OnDblClick:TonEvent read fOnDblClick write FOnDblClick;
+// property OnMouseDown:TOnMouse read FOnMouseDown write FOnMouseDown;
+ end;
+
+function NewSizerControl(AControl: PControl;aDesigner:PDesigner;flags:cardinal=0):PControl;
+function NewDesigner(aOwner:pControl):pDesigner;
+
+implementation
+
+const
+ FlagDelimeterChar='@';
+
+const
+ // Size and move commands for SysCommand
+ SZ_LEFT = $F001;
+ SZ_RIGHT = $F002;
+ SZ_TOP = $F003;
+ SZ_TOPLEFT = $F004;
+ SZ_TOPRIGHT = $F005;
+ SZ_BOTTOM = $F006;
+ SZ_BOTTOMLEFT = $F007;
+ SZ_BOTTOMRIGHT = $F008;
+ SZ_MOVE = $F012;
+
+type
+ TPosInfo = record
+ Rect :Trect;
+ Pos :integer;
+ Direction:integer;
+ end;
+
+ PSizerdata=^ TSizerdata;
+ TSizerdata= object(Tobj)
+ FControl :PControl;
+ FPosInfo :array [0..7] of TPosInfo;
+ Szflags :cardinal;
+ Direction:longint;
+
+ procedure DoPaint(sender:pControl;DC:HDC);
+ end;
+
+ PHack =^ THack;
+ THack = object(Tcontrol)
+ end;
+
+var
+ LocalDesigner:PDesigner=nil;
+
+function DesignHandlerProc(Sender: PControl; var Msg: TMsg; var Rslt: Integer): Boolean;
+//var MouseData:TMouseEventData;
+begin
+ Result:=false;
+ case msg.message of
+{
+ WM_KEYUP,WM_SYSCHAR,WM_SYSKEYUP,
+ WM_CHAR: begin
+// if loword(msg.wParam)=VK_TAB then
+ Messagebox(0,'222','',0);
+ end;
+} WM_LBUTTONDOWN: begin
+ if LocalDesigner.fOwner<>Sender then LocalDesigner.Current:=Sender;
+ Result:=true;
+ {
+ if assigned(Localdesigner.OnMousedown) then
+ // Borrowed from KOL.pas
+ // enables us to pass on KOL mouse events back to the designer
+ // without having to connect to true KOL eventproperties.
+ with MouseData do
+ begin
+ Shift := Msg.wParam;
+ if GetKeyState(VK_MENU) < 0 then
+ Shift := Shift or MK_ALT;
+ X := LoWord(Msg.lParam);
+ Y := HiWord(Msg.lParam);
+ Button := mbNone;
+ StopHandling := true;
+ Rslt := 0; // needed ?
+ LocalDesigner.OnMousedown(sender,Mousedata);
+ Result:=true
+ end;
+ }
+ end
+ end;
+end;
+
+// TSizerControl methods
+function WndProcSizer( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
+var
+ Pt: TPoint;
+ i: Integer;
+ R:Trect;
+ Data:PSizerData;
+begin
+ Data:=PSizerData(Sender.CustomObj);
+ Result:=True;
+ with Sender^, Data^ do
+ begin
+ case msg.message of
+{
+ WM_KEYUP,WM_SYSCHAR,WM_SYSKEYUP,
+ WM_CHAR: begin
+// if loword(msg.wParam)=VK_TAB then
+ Messagebox(0,'111','',0);
+ end;
+}
+ WM_NCHITTEST: begin
+ Pt := MakePoint(loword(Msg.lparam), hiword(Msg.lparam));
+ Pt := Screen2Client (Pt);
+ Rslt:=0;
+ for i := 0 to 7 do
+ if PtInRect (FPosInfo [i].rect, Pt) then
+ begin
+ // The value of rslt is passed on and makes
+ // the system select the correct cursor
+ // without us having to do anything more.
+ Rslt :=FPosInfo[i].pos;
+ Direction:=FPosInfo[i].direction;
+ break;
+ end;
+ if Rslt = 0 then
+ Result:=False;
+ end;
+
+ WM_SIZE: begin
+ R := BoundsRect;
+ InflateRect (R, -2, -2);
+ Fcontrol.BoundsRect := R;
+ FPosInfo[0].rect:=MakeRect (0 ,0 ,5 ,5);
+ FPosInfo[1].rect:=MakeRect (Width div 2-3,0 ,Width div 2+2,5);
+ FPosInfo[2].rect:=MakeRect (Width-5 ,0 ,Width ,5);
+ FPosInfo[3].rect:=MakeRect (Width-5 ,Height div 2-3,Width ,Height div 2+2);
+ FPosInfo[4].rect:=MakeRect (Width-5 ,Height-5 ,Width ,Height);
+ FPosInfo[5].rect:=MakeRect (Width div 2-3,Height-5 ,Width div 2+2,Height);
+ FPosInfo[6].rect:=MakeRect (0 ,Height-5 ,5 ,Height);
+ FPosInfo[7].rect:=MakeRect (0 ,Height div 2-3,5 ,Height div 2+2);
+ end;
+
+ WM_NCLBUTTONDOWN: if (Szflags and DESIGNER_NORESIZE)=0 then
+ Perform (WM_SYSCOMMAND, Direction, 0);
+
+ WM_LBUTTONDOWN: Perform (WM_SYSCOMMAND, SZ_MOVE, 0);
+
+ WM_MOVE: begin
+ R := BoundsRect;
+ InflateRect (R, -2, -2);
+ fControl.Invalidate;
+ fControl.BoundsRect := R;
+ end;
+
+ else
+ Result:=false;
+ end;
+ end;
+end;
+
+function NewSizerControl(AControl: PControl;aDesigner:PDesigner;flags:cardinal):PControl;
+var
+ R: TRect;
+ Data:PSizerData;
+begin
+ New(Data,Create);
+ Result:={NewPanel(aControl,esNone);//}NewPaintBox(aControl);
+ Result.ExStyle:=Result.ExStyle or WS_EX_TRANSPARENT;
+// Result.TabStop:=true;
+// Result.OnChar:=aDesigner.DoChar;
+// Result.OnKeyDown:=aDesigner.DoKeyUp;
+// Result.OnKeyUp:=aDesigner.DoKeyUp;
+ if aDesigner.fowner<>aControl then
+ With result^, Data^ do
+ begin
+ Szflags := flags;
+ FControl := AControl;
+ // set the size and position
+ R := aControl.BoundsRect;
+ InflateRect (R, 2, 2);
+ BoundsRect := R;
+ // set the parent
+ Parent := aControl.Parent;
+ // create the list of positions
+ FPosInfo [0].pos := htTopLeft ; FPosInfo [0].direction := SZ_TOPLEFT;
+ FPosInfo [1].pos := htTop ; FPosInfo [1].direction := SZ_TOP;
+ FPosInfo [2].pos := htTopRight ; FPosInfo [2].direction := SZ_TOPRIGHT;
+ FPosInfo [3].pos := htRight ; FPosInfo [3].direction := SZ_RIGHT;
+ FPosInfo [4].pos := htBottomRight; FPosInfo [4].direction := SZ_BOTTOMRIGHT;
+ FPosInfo [5].pos := htBottom ; FPosInfo [5].direction := SZ_BOTTOM;
+ FPosInfo [6].pos := htBottomLeft ; FPosInfo [6].direction := SZ_BOTTOMLEFT;
+ FPosInfo [7].pos := htLeft ; FPosInfo [7].direction := SZ_LEFT;
+ CustomObj:=Data;
+ OnPaint:=DoPaint;
+ AttachProc(WndProcSizer);
+ Bringtofront;
+ Focused:=true
+ end;
+end;
+
+procedure TSizerData.DoPaint(sender:pControl;DC:HDC);
+var
+ i: Integer;
+begin
+ // I simply use the current pen and brush
+ for i := 0 to 7 do
+ with pSizerdata(sender.Customobj).FPosInfo[i].Rect do
+ Rectangle(DC, Left, Top, Right, Bottom);
+end;
+
+{ TDesigner }
+function NewDesigner(aOwner:pControl):pDesigner;
+begin
+ if Assigned(LocalDesigner) then
+ begin
+ result:=LocalDesigner;
+ end
+ else
+ begin
+ New(Result,Create);
+ with result^ do
+ begin
+ Fowner:=aOwner;
+ Connect('',Fowner);
+ FOldPaint:=Fowner.OnPaint;
+ LocalDesigner:=Result;
+ //Result.Current:=aOwner;
+ end
+ end
+end;
+
+procedure TDesigner.init;
+begin
+ inherited;
+ Fspacing:=8;
+end;
+
+procedure TDesigner.PaintGrid(Sender: pControl; DC: HDC);
+var
+ i, j: Integer;
+begin
+ i := 0;
+ j := 0;
+ Sender.Canvas.FillRect(Sender.Canvas.ClipRect);
+ if Assigned(FOldPaint) then FOldPaint(Sender,DC);
+ repeat
+ repeat
+ MoveToEx(Dc,i, j,nil);
+ LineTo(Dc,i + 1,j);
+ inc(i, fSpacing);
+ until i > Sender.ClientWidth;
+ i := 0;
+ inc(j, fSpacing);
+ until j > Sender.ClientHeight;
+end;
+
+procedure TDesigner.SetSpacing(Space: cardinal);
+begin
+ fSpacing:=Space;
+ fOwner.invalidate;
+end;
+
+destructor TDesigner.destroy;
+begin
+ SetActive(false);
+ FOwner.OnPaint:=FOldPaint;
+ inherited;
+end;
+
+//Note: Make shure that whatever happens, all pointers are nil or valid!
+// Took a long time to debug spurious crashes.
+// So this is not excessively safe.
+procedure TDesigner.SetActive(const Value: boolean);
+var
+ i:integer;
+begin
+ FActive := Value;
+ if FActive then
+ begin
+ fOwner.OnPaint:=PaintGrid;
+ if count > 1 then
+ begin
+ if Assigned(fCurrent) then
+ fSizer:=NewSizerControl(fCurrent,@self,GetFlags(fCurrent));
+ for i:=0 to count -1 do
+ if not PControl(Objects[i]).IsprocAttached(DesignHandlerProc) then
+ PControl(Objects[i]).AttachProc(DesignHandlerProc);
+ end;
+ end
+ else
+ begin
+ if count > 0 then // always coz Owner is first
+ for i:=0 to count -1 do
+ PControl(Objects[i]).DetachProc(DesignHandlerProc);
+ if Assigned(fSizer) then
+ begin
+ fSizer.free;
+ fSizer:=nil;
+ end;
+ fCurrent:=nil;
+ fOwner.OnPaint:=FOldPaint;
+ end;
+ fOwner.Invalidate;
+end;
+
+procedure TDesigner.Connect(aName: KOLString; aControl: pControl; flags:cardinal=0);
+begin
+ if (IndexOfObj(aControl) = -1) then
+ begin
+ if aName = '' then
+ aName := PrepareClassName(aControl);
+ AddObject(UniqueName(aName,flags), Cardinal(aControl));
+ InternalControlChange(aControl);
+ SetCurrent(aControl);
+ if Active then
+ if not aControl.IsprocAttached(DesignHandlerProc) then
+ aControl.AttachProc(DesignHandlerProc);
+ end;
+end;
+
+procedure TDesigner.DisConnect(aControl: pControl);
+var
+ index: Integer;
+begin
+ index := IndexOfObj(aControl);
+ if index = -1 then
+ exit;
+ Delete(index);
+
+ InternalControlChange(nil);
+end;
+
+function TDesigner.GetFlags(aControl:pControl):cardinal;
+var
+ idx,dummy:integer;
+ tmpstr:KOLString;
+begin
+ idx:=IndexOfObj(aControl);
+ tmpstr:=Items[idx];
+ idx:=IndexOfChar(tmpstr,FlagDelimeterChar);
+ if idx<0 then result:=0
+ else
+ begin
+ val(copy(tmpstr,idx+1,15),result,dummy);
+ end;
+end;
+
+procedure TDesigner.SetCurrent(const Value: pControl);
+begin
+ if Assigned(fSizer) then
+ begin
+ fSizer.free;
+ fsizer:=nil;
+ end;
+ if Value <> nil then
+ begin
+
+ fCurrent := Value;
+ if fActive and (fCurrent<>nil) and (fCurrent<>fOwner) then
+ fSizer:=NewSizerControl(Value,@self,GetFlags(Value));
+
+ InternalControlChange(Value);
+ end;
+end;
+
+procedure TDesigner.InternalControlChange(sender: pObj);
+begin
+ if fActive then
+ if Assigned(OnControlChange)then
+ FOnControlChange(sender);
+end;
+
+procedure TDesigner.DoChar( Sender: PControl; var Key: KOLChar; Shift: DWORD);
+begin
+// messagebox(0,'444','',0);
+end;
+
+procedure TDesigner.DoKeyUp(Sender: PControl; var Key: Integer; Shift: DWORD);
+
+ procedure DeleteControl(Index:integer);
+ var
+ i: Integer;
+ C:PControl;
+ begin
+ C:=PControl(Objects[index]);
+ // delete children, not owner
+ if C.ChildCount>0 then
+ for i:=C.ChildCount-1 downto 0 do
+ if C<>fOwner then DeleteControl(i);
+
+ if C<>fOwner then
+ begin
+ C.free;
+ Delete(0);
+ end;
+ end;
+
+var
+ i:integer;
+begin
+// if Key = VK_TAB then
+// messagebox(0,'333','',0);
+
+ if Key = VK_DELETE then
+ begin
+ i:=IndexOfObj(LocalDesigner.Current);
+ if i<>-1 then
+ begin
+ DeleteControl(i);
+ InternalControlChange(nil);
+ PostMessage(Sender.Handle,WM_CLOSE,0,0); //???
+ end;
+ end;
+end;
+
+ // Converts an object name to a Delphi compatible control name that
+ // is unique for the designer, i.e 'Button' becomes 'Button1',
+ // the next button becomes 'Button2', always unless the
+ // control is already named by the user in which case the name is preserved
+ // unless there are conficts. In that case the control is silently
+ // renamed with a digit suffix without raising exceptions.
+ // Deleted names are re-used.
+ // It's not a beauty but it works.
+ // (A severe case of programming 48 hours without sleep)
+
+function TDesigner.UniqueName(aName: KOLString; flags:cardinal): KOLString;
+var
+ I, J: Integer;
+ T: KOLString;
+begin
+ // Strip obj_ prefix and all other prefix+underscores from
+ // subclassname property: 'obj_BUTTON' becomes 'Button'
+ T := LowerCase(aName);
+ while T <> '' do aName := Parse(T, '_');
+
+// aName[1]:=UpCase(aName[1]);
+ //Propercase it
+ T := aName[1];
+ T := UpperCase(T);
+ aName[1] := T[1];
+
+ Result := aName;
+ // Add at least a 1 to the name if the last char
+ // is not a digit.
+ if not (AnsiChar(aName[length(aName)]) in ['0'..'9']) then
+ Result := Format('%s%d', [aName, 1]);
+ J := 1;
+ repeat
+ I := IndexOf(Result);
+ if I > -1 then
+ begin
+ inc(J);
+ Result := Format('%s%d', [aName, J]);
+ end;
+ until I = -1;
+ if flags<>0 then
+ begin
+ Str(flags,T);
+ Result:=Result+FlagDelimeterChar+T;
+ end;
+end;
+
+// This is probably not complete yet.
+function TDesigner.PrepareClassName(aControl: PControl): KOLString;
+begin
+ Result := aControl.subclassname;
+ with aControl^ do
+ if subClassname = 'obj_STATIC' then
+ begin
+ // Only place where panel and label differ
+ // consistently???
+ // why not aControl.SizeRedraw ??
+ if pHack(aControl).SizeRedraw = True then
+ Result := 'obj_LABEL'
+ else
+ Result := 'obj_PANEL'
+ end
+
+ else if subclassname = 'obj_BUTTON' then
+ begin
+ if Boolean(Style and BS_AUTOCHECKBOX) then Result := 'obj_CHECKBOX'
+ else if Boolean(style and BS_RADIOBUTTON ) then Result := 'obj_RADIOBOX'
+ else if Boolean(style and BS_OWNERDRAW ) then Result := 'obj_BITBTN'
+ else if Boolean(style and BS_GROUPBOX ) then Result := 'obj_GROUPBOX';
+ end
+
+ else if IndexOfStr(UpperCase(subclassname), 'RICHEDIT')>-1 then
+ Result := 'obj_RICHEDIT';
+end;
+
+end.
diff --git a/plugins/Utils.pas/mApiCardM.pas b/plugins/Utils.pas/mApiCardM.pas
new file mode 100644
index 0000000000..ff289168f3
--- /dev/null
+++ b/plugins/Utils.pas/mApiCardM.pas
@@ -0,0 +1,404 @@
+{service insertion code}
+unit mApiCardM;
+
+interface
+
+uses windows,messages;
+
+type
+ tmApiCard = class
+ private
+ function GetDescription:pAnsiChar;
+ function GetResultType :pAnsiChar;
+ procedure SetCurrentService(item:pAnsiChar);
+ public
+ constructor Create(fname:pAnsiChar; lparent:HWND=0);
+ destructor Free;
+ procedure FillList(combo:HWND; mode:integer=0);
+
+ function FillParams(wnd:HWND{;item:pAnsiChar};wparam:boolean):pAnsiChar;
+ procedure Show;//(item:pAnsiChar);
+
+ property Description:pAnsiChar read GetDescription;
+ property ResultType :pAnsiChar read GetResultType;
+ property Service :pAnsiChar write SetCurrentService;
+ property Event :pAnsiChar write SetCurrentService;
+ private
+ storage:pointer;
+ current:pointer;
+ namespace: array [0.. 63] of AnsiChar;
+ parent,
+ HelpWindow:HWND;
+ isServiceHelp:boolean;
+
+ procedure Update(item:pAnsiChar=nil);
+ end;
+
+function CreateServiceCard(parent:HWND=0):tmApiCard;
+function CreateEventCard (parent:HWND=0):tmApiCard;
+
+implementation
+
+uses common,io,m_api,mirutils,memini;
+
+{$r mApiCard.res}
+
+{$include i_card_const.inc}
+
+const
+ WM_UPDATEHELP = WM_USER+100;
+
+const
+ BufSize = 2048;
+
+const
+ ApiHlpFile = 'plugins\services.ini';
+{
+ ServiceHlpFile = 'plugins\services.ini';
+ EventsHlpFile = 'plugins\events.ini';
+}
+function tmApiCard.GetResultType:pAnsiChar;
+var
+ buf:array [0..2047] of AnsiChar;
+ p:pAnsiChar;
+begin
+ if storage<>nil then
+ begin
+ StrCopy(buf,GetParamSectionStr(current,'return',''));
+ p:=@buf;
+ while p^ in sWordOnly do inc(p);
+ p^:=#0;
+ StrDup(result,@buf);
+ end
+ else
+ result:=nil;
+end;
+
+function tmApiCard.GetDescription:pAnsiChar;
+begin
+ if storage<>nil then
+ begin
+ StrDup(result,GetParamSectionStr(current,'descr',''));
+ end
+ else
+ result:=nil;
+end;
+
+function tmApiCard.FillParams(wnd:HWND{;item:pAnsiChar};wparam:boolean):pAnsiChar;
+var
+ buf :array [0..2047] of AnsiChar;
+ bufw:array [0..2047] of WideChar;
+ j:integer;
+ p,pp,pc:PAnsiChar;
+ tmp:pWideChar;
+ paramname:pAnsiChar;
+begin
+ if storage=nil then
+ begin
+ result:=nil;
+ exit;
+ end;
+ if wparam then
+ paramname:='wparam'
+ else
+ paramname:='lparam';
+
+ StrCopy(buf,GetParamSectionStr(current,paramname,''));
+ StrDup(result,@buf);
+ SendMessage(wnd,CB_RESETCONTENT,0,0);
+ if buf[0]<>#0 then
+ begin
+ p:=@buf;
+ GetMem(tmp,BufSize*SizeOf(WideChar));
+ repeat
+ pc:=StrScan(p,'|');
+ if pc<>nil then
+ pc^:=#0;
+
+ if (p^ in ['0'..'9']) or ((p^='-') and (p[1] in ['0'..'9'])) then
+ begin
+ j:=0;
+ pp:=p;
+ repeat
+ bufw[j]:=WideChar(pp^);
+ inc(j); inc(pp);
+ until (pp^=#0) or (pp^=' ');
+ if pp^<>#0 then
+ begin
+ bufw[j]:=' '; bufw[j+1]:='-'; bufw[j+2]:=' '; inc(j,3);
+ FastAnsitoWideBuf(pp+1,tmp);
+ StrCopyW(bufw+j,TranslateW(tmp));
+ SendMessageW(wnd,CB_ADDSTRING,0,lparam(@bufw));
+ end
+ else
+ SendMessageA(wnd,CB_ADDSTRING,0,lparam(p));
+ end
+ else
+ begin
+ FastAnsitoWideBuf(p,tmp);
+ SendMessageW(wnd,CB_ADDSTRING,0,lparam(TranslateW(tmp)));
+ if (p=@buf) and (lstrcmpia(p,'structure')=0) then
+ break;
+ end;
+ p:=pc+1;
+ until pc=nil;
+ FreeMem(tmp);
+ end;
+ SendMessage(wnd,CB_SETCURSEL,0,0);
+end;
+
+procedure tmApiCard.FillList(combo:hwnd; mode:integer=0);
+var
+ tmpbuf:array [0..127] of AnsiChar;
+ p,pp,pc:PAnsiChar;
+begin
+ if storage<>nil then
+ begin
+ SendMessage(combo,CB_RESETCONTENT,0,0);
+ p:=GetSectionList(storage,namespace);
+ pp:=p;
+ while p^<>#0 do
+ begin
+ case mode of
+ 1: begin // just constant name
+ pc:=GetParamStr(storage,p,'alias',nil,namespace);
+ if pc=nil then
+ pc:=p;
+ end;
+ 2: begin // value (name)
+ pc:=StrCopyE(tmpbuf,p);
+ pc^:=' '; inc(pc);
+ pc^:='('; inc(pc);
+ pc:=StrCopyE(pc,GetParamStr(storage,p,'alias',nil,namespace));
+ pc^:=')'; inc(pc);
+ pc^:=#0;
+ pc:=@tmpbuf;
+ end;
+ 3: begin // name 'value'
+ pc:=@tmpbuf;
+ pc:=StrCopyE(pc,GetParamStr(storage,p,'alias',nil,namespace));
+ pc^:=' '; inc(pc);
+ pc^:=''''; inc(pc);
+ pc:=StrCopyE(pc,p);
+ pc^:=''''; inc(pc);
+ pc^:=#0;
+ pc:=@tmpbuf;
+ end;
+ else // just constant value
+ pc:=p;
+ end;
+ SendMessageA(combo,CB_ADDSTRING,0,lparam(pc));
+ while p^<>#0 do inc(p); inc(p);
+ end;
+ FreeSectionList(pp);
+ SendMessage(combo,CB_SETCURSEL,-1,0);
+ end;
+end;
+
+function ServiceHelpDlg(Dialog:HWnd;hMessage:uint;wParam:WPARAM;lParam:LPARAM):LRESULT; stdcall;
+var
+ buf:PAnsiChar;
+ tmp:PWideChar;
+ card:tmApiCard;
+begin
+ result:=0;
+ case hMessage of
+ WM_CLOSE: begin
+ card:=tmApiCard(GetWindowLongPtr(Dialog,GWLP_USERDATA{DWLP_USER}));
+ card.HelpWindow:=0;
+ DestroyWindow(Dialog); //??
+ end;
+
+ WM_INITDIALOG: begin
+ TranslateDialogDefault(Dialog);
+ result:=1;
+ end;
+
+ WM_COMMAND: begin
+ if (wParam shr 16)=BN_CLICKED then
+ begin
+ case loword(wParam) of
+ IDOK,IDCANCEL: begin
+ card:=tmApiCard(GetWindowLongPtr(Dialog,GWLP_USERDATA{DWLP_USER}));
+ card.HelpWindow:=0;
+ DestroyWindow(Dialog);
+ end;
+ end;
+ end;
+ end;
+
+ WM_UPDATEHELP: begin
+ with tmApiCard(lParam) do
+ begin
+ if (storage<>nil) and (lParam<>0) and (current<>nil) then
+ begin
+ GetMem(buf,BufSize);
+ GetMem(tmp,BufSize*SizeOf(WideChar));
+
+ SetDlgItemTextA(Dialog,IDC_HLP_SERVICE,GetSectionName(current));
+
+ SetDlgItemTextA(Dialog,IDC_HLP_ALIAS,
+ GetParamSectionStr(current,'alias',''));
+
+ FastAnsiToWideBuf(GetParamSectionStr(current,'return','Undefined'),tmp);
+ SetDlgItemTextW(Dialog,IDC_HLP_RETURN,TranslateW(tmp));
+
+ FastAnsiToWideBuf(GetParamSectionStr(current,'descr','Undefined'),tmp);
+ SetDlgItemTextW(Dialog,IDC_HLP_EFFECT,TranslateW(tmp));
+
+ FastAnsiToWideBuf(GetParamSectionStr(current,'plugin',''),tmp);
+ SetDlgItemTextW(Dialog,IDC_HLP_PLUGIN,TranslateW(tmp));
+
+ // Parameters
+ StrCopy(buf,GetParamSectionStr(current,'wparam','0'));
+ if StrScan(buf,'|')<>nil then
+ begin
+ ShowWindow(GetDlgItem(Dialog,IDC_HLP_WPARAML),SW_SHOW);
+ ShowWindow(GetDlgItem(Dialog,IDC_HLP_WPARAM ),SW_HIDE);
+ FillParams(GetDlgItem(Dialog,IDC_HLP_WPARAML),true);
+ end
+ else
+ begin
+ ShowWindow(GetDlgItem(Dialog,IDC_HLP_WPARAML),SW_HIDE);
+ ShowWindow(GetDlgItem(Dialog,IDC_HLP_WPARAM ),SW_SHOW);
+ FastAnsiToWideBuf(buf,tmp);
+ SetDlgItemTextW(Dialog,IDC_HLP_WPARAM,TranslateW(tmp));
+ end;
+
+ StrCopy(buf,GetParamSectionStr(current,'lparam','0'));
+ if StrScan(buf,'|')<>nil then
+ begin
+ ShowWindow(GetDlgItem(Dialog,IDC_HLP_LPARAML),SW_SHOW);
+ ShowWindow(GetDlgItem(Dialog,IDC_HLP_LPARAM ),SW_HIDE);
+ FillParams(GetDlgItem(Dialog,IDC_HLP_LPARAML),false);
+ end
+ else
+ begin
+ ShowWindow(GetDlgItem(Dialog,IDC_HLP_LPARAML),SW_HIDE);
+ ShowWindow(GetDlgItem(Dialog,IDC_HLP_LPARAM ),SW_SHOW);
+ FastAnsiToWideBuf(buf,tmp);
+ SetDlgItemTextW(Dialog,IDC_HLP_LPARAM,TranslateW(tmp));
+ end;
+
+ FreeMem(tmp);
+ FreeMem(buf);
+ end
+ else
+ begin
+ SetDlgItemTextW(Dialog,IDC_HLP_SERVICE,nil);
+ SetDlgItemTextW(Dialog,IDC_HLP_ALIAS ,nil);
+ SetDlgItemTextW(Dialog,IDC_HLP_PLUGIN ,nil);
+ SetDlgItemTextW(Dialog,IDC_HLP_RETURN ,nil);
+ SetDlgItemTextW(Dialog,IDC_HLP_EFFECT ,nil);
+ SetDlgItemTextW(Dialog,IDC_HLP_WPARAM ,nil);
+ SetDlgItemTextW(Dialog,IDC_HLP_LPARAM ,nil);
+ SendDlgItemMessage(Dialog,IDC_HLP_WPARAML,CB_RESETCONTENT,0,0);
+ SendDlgItemMessage(Dialog,IDC_HLP_LPARAML,CB_RESETCONTENT,0,0);
+ ShowWindow(GetDlgItem(Dialog,IDC_HLP_WPARAML),SW_HIDE);
+ ShowWindow(GetDlgItem(Dialog,IDC_HLP_LPARAML),SW_HIDE);
+ end;
+ end;
+ end;
+ end;
+end;
+
+procedure tmApiCard.SetCurrentService(item:pAnsiChar);
+begin
+ if (item=nil) or (item^=#0) then
+ current:=nil
+ else
+ current:=SearchSection(storage,item,namespace);
+end;
+
+procedure tmApiCard.Update(item:pAnsiChar=nil);
+begin
+ SendMessage(HelpWindow,WM_UPDATEHELP,0,LPARAM(self));
+end;
+
+procedure tmApiCard.Show;
+var
+ note,
+ title:pWideChar;
+begin
+ if HelpWindow=0 then
+ begin
+ HelpWindow:=CreateDialogW(hInstance,'IDD_MAPIHELP',//MAKEINTRESOURCEW(IDD_HELP),
+ parent,@ServiceHelpDlg);
+ if HelpWindow<>0 then
+ begin
+ SetWindowLongPtr(HelpWindow,GWLP_USERDATA{DWLP_USER},LONG_PTR(Self));
+ if isServiceHelp then
+ begin
+ title:='Miranda service help';
+ note :='''<proto>'' in service name will be replaced by protocol name for contact handle in parameter';
+ end
+ else
+ begin
+ title:='Miranda event help';
+ note :='';
+ end;
+ SendMessageW(HelpWindow,WM_SETTEXT,0,LPARAM(title));
+
+ SendMessageW(GetDlgItem(HelpWindow,IDC_HLP_NOTE),WM_SETTEXT,0,LPARAM(TranslateW(Note)));
+ end;
+ end
+ else
+ begin
+{
+ if parent<>GetParent(HelpWindow) then
+ SetParent(HelpWindow,parent);
+}
+ end;
+// if title<>nil then
+// SendMessageW(HelpWindow,WM_SETTEXT,0,TranslateW(title));
+
+ Update(current);
+end;
+
+constructor tmApiCard.Create(fname:pAnsiChar; lparent:HWND=0);
+var
+ IniFile: array [0..511] of AnsiChar;
+begin
+ inherited Create;
+
+ StrCopy(@IniFile,fname);
+ HelpWindow:=0;
+ current:=nil;
+ if fname<>nil then
+ begin
+ ConvertFileName(fname,@INIFile);
+ // CallService(MS_UTILS_PATHTOABSOLUTE,
+ // dword(PAnsiChar(ServiceHlpFile)),dword(INIFile));
+ if GetFSize(pAnsiChar(@INIFile))=0 then
+ begin
+ INIFile[0]:=#0;
+ end;
+ parent:=lparent;
+ end;
+ storage:=OpenStorage(@IniFile);
+end;
+
+destructor tmApiCard.Free;
+begin
+ CloseStorage(storage);
+// inherited;
+end;
+
+function CreateServiceCard(parent:HWND=0):tmApiCard;
+begin
+ result:=tmApiCard.Create(ApiHlpFile,parent);
+ result.isServiceHelp:=true;
+ StrCopy(result.namespace,'Service');
+end;
+
+function CreateEventCard(parent:HWND=0):tmApiCard;
+begin
+ result:=tmApiCard.Create(ApiHlpFile,parent);
+ result.isServiceHelp:=false;
+ StrCopy(result.namespace,'Event');
+end;
+
+
+//initialization
+//finalization
+end.
diff --git a/plugins/Utils.pas/mApicard.rc b/plugins/Utils.pas/mApicard.rc
new file mode 100644
index 0000000000..e4d1431e8f
--- /dev/null
+++ b/plugins/Utils.pas/mApicard.rc
@@ -0,0 +1,39 @@
+#include "i_card_const.inc"
+
+LANGUAGE 0,0
+
+IDD_MAPIHELP DIALOGEX 0, 0, 256, 174, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_CONTROLPARENT
+CAPTION "Service help"
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ DEFPUSHBUTTON "OK", IDOK, 4, 126, 26, 16
+
+ RTEXT "Name", -1 , 4, 4, 60, 14, SS_CENTERIMAGE
+ EDITTEXT IDC_HLP_SERVICE, 70, 5, 180, 12, ES_READONLY
+
+ RTEXT "Alias", -1 , 4, 20, 60, 14, SS_CENTERIMAGE
+ EDITTEXT IDC_HLP_ALIAS , 70, 21, 180, 12, ES_READONLY
+
+ RTEXT "Plugin", -1 , 4, 36, 60, 14, SS_CENTERIMAGE
+ EDITTEXT IDC_HLP_PLUGIN , 70, 37, 180, 12, ES_READONLY
+
+ RTEXT "wParam", -1 , 4, 52, 60, 14, SS_CENTERIMAGE
+ EDITTEXT IDC_HLP_WPARAM , 70, 53, 180, 12, ES_READONLY
+ COMBOBOX IDC_HLP_WPARAML, 70, 53, 180, 76, CBS_DROPDOWNLIST | WS_VSCROLL | CBS_AUTOHSCROLL
+
+ RTEXT "lParam", -1 , 4, 68, 60, 14, SS_CENTERIMAGE
+ EDITTEXT IDC_HLP_LPARAM , 70, 69, 180, 12, ES_READONLY
+ COMBOBOX IDC_HLP_LPARAML, 70, 69, 180, 76, CBS_DROPDOWNLIST | WS_VSCROLL | CBS_AUTOHSCROLL
+
+ RTEXT "Return", -1 , 4, 84, 60, 14, SS_CENTERIMAGE
+ EDITTEXT IDC_HLP_RETURN , 70, 85, 180, 12, ES_READONLY | ES_AUTOHSCROLL
+
+ RTEXT "Effect", -1 , 4, 100, 60, 14, SS_CENTERIMAGE
+ EDITTEXT IDC_HLP_EFFECT , 70, 100, 180, 42, ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL
+
+ CONTROL "", -1, "STATIC", SS_ETCHEDHORZ, 2, 146, 252, 2
+ LTEXT "'<proto>' in service name will be replaced by protocol name for contact handle in parameter",
+ IDC_HLP_NOTE, 4, 149, 248, 22
+}
diff --git a/plugins/Utils.pas/mApicard.res b/plugins/Utils.pas/mApicard.res
new file mode 100644
index 0000000000..7d6ed320b9
--- /dev/null
+++ b/plugins/Utils.pas/mApicard.res
Binary files differ
diff --git a/plugins/Utils.pas/make.bat b/plugins/Utils.pas/make.bat
new file mode 100644
index 0000000000..39a182dae2
--- /dev/null
+++ b/plugins/Utils.pas/make.bat
@@ -0,0 +1,14 @@
+@echo off
+set myopts=
+
+if /i '%2' == 'fpc' (
+ ..\FPC\bin\fpc.exe %myopts% %1 %3 %4 %5 %6 %7 %8 %9
+) else if /i '%2' == 'fpc64' (
+ ..\FPC\bin64\ppcrossx64.exe %myopts% %1 %3 %4 %5 %6 %7 %8 %9
+) else if /i '%2' == 'xe2' (
+ ..\XE2\bin\dcc32.exe %myopts% %1 %3 %4 %5 %6 %7 %8 %9
+) else if /i '%2' == 'xe64' (
+ ..\XE2\bin\dcc64.exe %myopts% %1 %3 %4 %5 %6 %7 %8 %9
+) else (
+ ..\delphi\dcc32 %myopts% %1 %2 %3 %4 %5 %6 %7 %8 %9
+)
diff --git a/plugins/Utils.pas/memini.pas b/plugins/Utils.pas/memini.pas
new file mode 100644
index 0000000000..b4f65093bb
--- /dev/null
+++ b/plugins/Utils.pas/memini.pas
@@ -0,0 +1,514 @@
+unit memini;
+
+interface
+
+function OpenStorage(fname:pAnsiChar):pointer;
+function OpenStorageBuf(buf:pAnsiChar):pointer;
+procedure CloseStorage(storage:pointer);
+
+function GetSectionList(storage:pointer;namespace:pAnsiChar=nil):pAnsiChar;
+procedure FreeSectionList(ptr:pAnsiChar);
+
+function GetParamStr(storage:pointer;section,param:pAnsiChar;default:pAnsiChar=nil;
+ namespace:pAnsiChar=nil):pAnsiChar;
+function GetParamInt(storage:pointer;section,param:pAnsiChar;default:integer=0;
+ namespace:pAnsiChar=nil):integer;
+
+function SearchSection(storage:pointer;section:pAnsiChar;namespace:pAnsiChar=nil):pointer;
+function GetSectionName(section:pointer):pAnsiChar;
+
+function GetParamSectionStr(section:pointer;param:pAnsiChar;default:pAnsiChar=nil):pAnsiChar;
+function GetParamSectionInt(section:pointer;param:pAnsiChar;default:integer=0):integer;
+
+implementation
+
+uses windows,io,common;
+
+const
+ arstep = 8;
+const
+ ns_separator = ':';
+ line_separator = '\';
+type
+ pParam = ^tParam;
+ tParam = record
+ hash :integer; // param name hash
+ name :pAnsiChar; // points to source (for write only)
+ value :pAnsiChar; // points to source? or modified
+ assign:boolean; // newly assigned value or in INI buffer
+ end;
+ pSection = ^tSection;
+ tSection = record
+ ns :integer; // namespace hash
+ code :integer; // section name hash
+ full :integer; // namespace+section name hash
+ fullname:pAnsiChar; // pointer to namespace:name
+ name :pAnsiChar; // pointer to name only
+
+ numparam:integer;
+ arParams:array of tParam;
+ end;
+ pStorage = ^tStorage;
+ tStorage = record
+ Name :pAnsiChar; // filename
+ buffer :pAnsiChar; // source (INI) text
+
+ numsect :integer;
+ arSection: array of tSection;
+ end;
+
+
+function HashOf(txt:pAnsiChar):integer;
+begin
+ result:=Hash(txt,StrLen(txt));
+{
+ result:=0;
+ while txt^<>#0 do
+ begin
+ result:=((result shl 2) or (result shr (SizeOf(result)*8-2))) xor Ord(UpCase(txt^));
+ inc(txt);
+ end;
+}
+end;
+
+// sections adds 1 by 1, without duplicate check
+procedure AddSection(data:pStorage;anamespace,aname:pAnsiChar);
+var
+ c:AnsiChar;
+begin
+ // search section with same name?
+
+ // add section
+ if data.numsect>High(data.arSection) then
+ SetLength(data.arSection,Length(data.arSection)+arstep);
+
+ FillChar(data.arSection[data.numsect],SizeOf(tSection),0);
+ with data.arSection[data.numsect] do
+ begin
+ fullname:=anamespace;
+ name :=aname;
+ full:=HashOf(anamespace);
+ if anamespace<>aname then
+ begin
+ c:=(aname-1)^;
+ (aname-1)^:=#0;
+ code:=HashOf(aname);
+ ns :=HashOf(anamespace);
+ (aname-1)^:=c;
+ end;
+ end;
+ inc(data.numsect);
+end;
+
+procedure AddParam(data:pStorage;aname,avalue:pAnsiChar;assignvalue:boolean);
+begin
+ // search param with same name?
+
+ with data.arSection[data.numsect-1] do
+ begin
+ // add param
+ if numparam>High(arParams) then
+ SetLength(arParams,Length(arParams)+arstep);
+
+ FillChar(arParams[numparam],SizeOf(tParam),0);
+ with arParams[numparam] do
+ begin
+ hash :=HashOf(aname);
+ name :=aname;
+ value :=avalue;
+ assign:=assignvalue;
+ end;
+ inc(numparam);
+ end;
+end;
+
+// quotes, multiline etc
+// result = pointer to non-parameter line
+// pointers: start of value, start of current line, end of value in line, end of current line
+function ProcessParamValue(var start:pAnsiChar):pAnsiChar;
+var
+ lineend,eol,dst,bov:pAnsiChar;
+ multiline,crlf:boolean;
+begin
+
+ dst:=start;
+ bov:=start;
+ result:=nil;
+ repeat
+ multiline:=false;
+ crlf :=false;
+ // skip starting spaces
+ while start^ in [#9,' '] do inc(start);
+
+ if start^ in [#0,#10,#13] then // empty value or end
+ begin
+ while start^ in [#10,#13] do inc(start);
+ exit;
+ end;
+
+ lineend:=start;
+ while not (lineend^ in [#0,#10,#13]) do inc(lineend);
+ eol:=lineend;
+ dec(lineend);
+ while lineend^ in [#9,' '] do dec(lineend);
+
+ if lineend^=line_separator then // multiline or part of value
+ begin
+ if (lineend-1)^ in [#9,' '] then // multiline
+ begin
+ dec(lineend);
+ multiline:=true;
+ while lineend^ in [#9,' '] do dec(lineend);
+ end
+ // double separator = multiline + crlf saving
+ else if ((lineend-1)^=line_separator) and ((lineend-2)^ in [#9,' ']) then
+ begin
+ dec(lineend,2);
+ multiline:=true;
+ crlf :=true;
+ while lineend^ in [#9,' '] do dec(lineend);
+ end;
+ end;
+ // lineend points to last char
+ // start points to first char
+ // eol points to end of line
+
+ //!! now just starting/ending quotes
+ if (start^ in ['''','"']) and (lineend^ in ['''','"']) then
+ begin
+ inc(start);
+ dec(lineend);
+ end;
+
+ while start<=lineend do
+ begin
+ dst^:=start^;
+ inc(dst);
+ inc(start);
+ end;
+ if crlf then
+ begin
+ dst^:=#13;
+ inc(dst);
+ dst^:=#10;
+ inc(dst);
+ end;
+ start:=eol;
+ while start^ in [#10,#13] do inc(start);
+
+ until not multiline;
+ dst^:=#0;
+ result:=bov;
+end;
+
+procedure TranslateData(data:pStorage);
+var
+ pc2,pc1,pc:pAnsiChar;
+begin
+ pc:=data^.buffer;
+ data.numsect:=0;
+ while pc^<>#0 do
+ begin
+ while pc^ in [#9,#10,#13,' '] do inc(pc);
+
+ // comment
+ if pc^=';' then
+ begin
+ // skip to next line (or end)
+ while not (pc^ in [#0,#10,#13]) do inc(pc);
+ // skip empty
+ while pc^ in [#9,#10,#13,' '] do inc(pc);
+ end
+ // section
+ else if pc^='[' then
+ begin
+
+ inc(pc);
+ //!! without #0 check
+ pc1:=pc;
+ pc2:=pc;
+ while pc^ in sLatWord do inc(pc);
+ // namespace
+ if pc^=ns_separator then
+ begin
+ inc(pc);
+ pc2:=pc;
+ end;
+ while pc^ <> ']' do inc(pc);
+ pc^:=#0; //!!
+
+ AddSection(data,pc1,pc2);
+ inc(pc);
+ end
+ // parameter
+ else if pc^ in sIdFirst then
+ begin
+ pc1:=pc;
+ // skip param name
+ while pc^ in sLatWord do inc(pc);
+ pc^:=#0; //!!
+ // skip spaces
+ while pc^ in [#9,' '] do inc(pc);
+ inc(pc); // must be "="
+ // skip spaces
+ while pc^ in [#9,' '] do inc(pc);
+// pc2:=pc;
+ // parameter can be quoted
+ // here need to cut spaces, comments but join next lines
+ pc2:=ProcessParamValue(pc);
+
+ AddParam(data,pc1,pc2,false);
+ end;
+ end;
+
+end;
+
+function OpenStorageBuf(buf:pAnsiChar):pointer;
+begin
+ result:=nil;
+ if (buf<>nil) and (buf^<>#0) then
+ begin
+ GetMem(result,SizeOf(tStorage));
+ FillChar(result^,SizeOf(tStorage),0);
+
+ StrDup(pStorage(result)^.buffer,buf);
+
+ TranslateData(pStorage(result));
+ end;
+end;
+
+function OpenStorage(fname:pAnsiChar):pointer;
+var
+ h:THANDLE;
+ size:integer;
+begin
+ result:=nil;
+ if FileExists(fname) then
+ begin
+ h:=Reset(fname);
+ if h<>THANDLE(INVALID_HANDLE_VALUE) then
+ begin
+ size:=FileSize(h);
+ if size>0 then
+ begin
+ GetMem(result,SizeOf(tStorage));
+ FillChar(result^,SizeOf(tStorage),0);
+
+ // save name too?
+ GetMem(pStorage(result)^.buffer,size+1);
+ BlockRead(h,pStorage(result)^.buffer^,size);
+ pStorage(result)^.buffer[size]:=#0;
+ end;
+ CloseHandle(h);
+ TranslateData(pStorage(result));
+ end;
+ end;
+end;
+
+procedure CloseStorage(storage:pointer);
+var
+ i:integer;
+begin
+ if storage=nil then exit;
+
+ with pStorage(storage)^ do
+ begin
+ if name<>nil then FreeMem(name);
+ // cycle by sections
+ for i:=0 to HIGH(arSection) do
+ SetLength(arSection[i].arParams,0);
+
+ SetLength(arSection,0);
+ FreeMem(buffer);
+ end;
+ FreeMem(storage);
+end;
+
+function GetSectionList(storage:pointer;namespace:pAnsiChar=nil):pAnsiChar;
+var
+ i,size,ns:integer;
+ pc:pAnsiChar;
+begin
+ if storage=nil then
+ begin
+ result:=nil;
+ exit;
+ end;
+
+ // calculate size
+ size:=0;
+ ns:=0;
+ if (namespace<>nil) and (namespace^<>#0) then
+ ns:=HashOf(namespace);
+
+ with pStorage(storage)^ do
+ begin
+ for i:=0 to HIGH(arSection) do
+ begin
+ if (namespace<>nil) and (namespace^<>#0) then
+ begin
+ if ns<>arSection[i].ns then
+ continue;
+ inc(size,StrLen(arSection[i].name)+1);
+ end
+ else
+ inc(size,StrLen(arSection[i].fullname)+1);
+ end;
+ inc(size);
+ // get memory
+ GetMem(pc,size);
+ result:=pc;
+ // fill
+ for i:=0 to HIGH(arSection) do
+ begin
+ if (namespace<>nil) and (namespace^<>#0) then
+ begin
+ if ns<>arSection[i].ns then
+ continue;
+ pc:=StrCopyE(pc,arSection[i].name);
+ end
+ else
+ pc:=StrCopyE(pc,arSection[i].fullname);
+ inc(pc);
+ end;
+ pc^:=#0;
+ end;
+end;
+
+procedure FreeSectionList(ptr:pAnsiChar);
+begin
+ FreeMem(ptr);
+end;
+
+function SearchSection(storage:pointer;section:pAnsiChar;namespace:pAnsiChar=nil):pointer;
+var
+ i:integer;
+ nsn,nss:integer;
+begin
+ result:=nil;
+ nss:=HashOf(section);
+ if namespace=nil then
+ begin
+ with pStorage(storage)^ do
+ for i:=0 to numsect-1 do
+ begin
+ if arSection[i].full=nss then
+ begin
+ result:=@arSection[i];
+ break;
+ end;
+ end;
+ end
+ else
+ begin
+ nsn:=HashOf(namespace);
+ with pStorage(storage)^ do
+ begin
+ for i:=0 to numsect-1 do
+ begin
+ if (arSection[i].ns=nsn) and (arSection[i].code=nss) then
+ begin
+ result:=@arSection[i];
+ break;
+ end;
+ end;
+ end;
+ end;
+end;
+
+function GetSectionName(section:pointer):pAnsiChar;
+begin
+ if section=nil then
+ result:=nil
+ else
+ result:=pSection(section).name;
+end;
+
+function SearchParameter(section:pointer;param:pAnsiChar):pointer;
+var
+ i:integer;
+ nsp:integer;
+begin
+ result:=nil;
+ if section<>nil then
+ begin
+ nsp:=HashOf(param);
+ with pSection(section)^ do
+ begin
+ for i:=0 to numparam-1 do
+ begin
+ if arParams[i].hash=nsp then
+ begin
+ result:=@arParams[i];
+ break;
+ end;
+ end;
+ end;
+ end;
+end;
+
+function GetParamSectionStr(section:pointer;param:pAnsiChar;default:pAnsiChar=nil):pAnsiChar;
+var
+ pn:pParam;
+begin
+ result:=default;
+
+ if section<>nil then
+ begin
+ pn:=SearchParameter(section,param);
+ if pn<>nil then
+ result:=pn.value //StrCopy(buffer,value,len-1);
+ end;
+end;
+
+function GetParamSectionInt(section:pointer;param:pAnsiChar;default:integer=0):integer;
+var
+ pn:pParam;
+begin
+ result:=default;
+
+ if section<>nil then
+ begin
+ pn:=SearchParameter(section,param);
+ if pn<>nil then
+ begin
+ if pn.value[0]='$' then
+ result:=HexToInt(pAnsiChar(@pn.value[1]))
+ else
+ result:=StrToInt(pn.value);
+ end;
+ end;
+end;
+
+
+function GetParamStr(storage:pointer;section,param:pAnsiChar;default:pAnsiChar=nil;
+ namespace:pAnsiChar=nil):pAnsiChar;
+var
+ sn:pSection;
+begin
+ if storage=nil then
+ begin
+ result:=default;
+ exit;
+ end;
+
+ sn:=SearchSection(storage,section,namespace);
+ result:=GetParamSectionStr(sn,param,default);
+end;
+
+function GetParamInt(storage:pointer;section,param:pAnsiChar;default:integer=0;
+ namespace:pAnsiChar=nil):integer;
+var
+ sn:pSection;
+begin
+ if storage=nil then
+ begin
+ result:=default;
+ exit;
+ end;
+
+ sn:=SearchSection(storage,section,namespace);
+ result:=GetParamSectionInt(sn,param,default);
+end;
+
+end.
diff --git a/plugins/Utils.pas/mirutils.pas b/plugins/Utils.pas/mirutils.pas
new file mode 100644
index 0000000000..b71de66708
--- /dev/null
+++ b/plugins/Utils.pas/mirutils.pas
@@ -0,0 +1,1163 @@
+{$Include compilers.inc}
+unit mirutils;
+
+interface
+
+uses windows,m_api;
+
+// icons
+function SetButtonIcon(btn:HWND;name:PAnsiChar):HICON;
+function RegisterSingleIcon(resname,ilname,descr,group:PAnsiChar):int;
+
+// others
+
+function ConvertFileName(src:pAnsiChar;hContact:THANDLE=0):pAnsiChar; overload;
+function ConvertFileName(src:pWideChar;hContact:THANDLE=0):pWideChar; overload;
+function ConvertFileName(src:pAnsiChar;dst:pAnsiChar;hContact:THANDLE=0):pAnsiChar; overload;
+function ConvertFileName(src:pWideChar;dst:pWideChar;hContact:THANDLE=0):pWideChar; overload;
+
+procedure ShowPopupW(text:pWideChar;title:pWideChar=nil);
+function GetAddonFileName(prefix,altname,path:PAnsiChar;ext:PAnsiChar):PAnsiChar;
+function TranslateA2W(sz:PAnsiChar):PWideChar;
+function MirandaCP:integer;
+
+function isVarsInstalled:bool;
+function ParseVarString(astr:pAnsiChar;aContact:THANDLE=0;extra:pAnsiChar=nil):pAnsiChar; overload;
+function ParseVarString(astr:pWideChar;aContact:THANDLE=0;extra:pWideChar=nil):pWideChar; overload;
+function ShowVarHelp(dlg:HWND;id:integer=0):integer;
+
+function IsChat(hContact:THANDLE):bool;
+procedure SendToChat(hContact:THANDLE;pszText:PWideChar);
+
+function LoadContact(group,setting:PAnsiChar):THANDLE;
+function SaveContact(hContact:THANDLE;group,setting:PAnsiChar):integer;
+
+function SetCListSelContact(hContact:THANDLE):THANDLE;
+function GetCListSelContact:THANDLE; {$IFDEF DELPHI_10_UP}inline;{$ENDIF}
+function GetContactProtoAcc(hContact:THANDLE):PAnsiChar;
+function IsMirandaUser(hContact:THANDLE):integer; // >0=Miranda; 0=Not miranda; -1=unknown
+procedure ShowContactDialog(hContact:THANDLE;DblClk:boolean=true;anystatus:boolean=true);
+function FindContactHandle(proto:pAnsiChar;const dbv:TDBVARIANT;is_chat:boolean=false):THANDLE;
+function WndToContact(wnd:hwnd):THANDLE; overload;
+function WndToContact:THANDLE; overload;
+function GetContactStatus(hContact:THANDLE):integer;
+// -2 - deleted account, -1 - disabled account, 0 - hidden
+// 1 - metacontact, 2 - submetacontact, positive - active
+// proto - ASSIGNED buffer
+function IsContactActive(hContact:THANDLE;proto:pAnsiChar=nil):integer;
+
+function CreateGroupW(name:pWideChar;hContact:THANDLE):integer;
+function CreateGroup (name:pAnsiChar;hContact:THANDLE):integer;
+
+function MakeGroupMenu(idxfrom:integer=100):HMENU;
+function GetNewGroupName(parent:HWND):pWideChar;
+
+const
+ MAX_REDIRECT_RECURSE = 4;
+
+function SendRequest(url:PAnsiChar;rtype:int;args:pAnsiChar=nil;hNetLib:THANDLE=0):pAnsiChar;
+
+function GetFile(url:PAnsiChar;save_file:PAnsiChar;
+ hNetLib:THANDLE=0;recurse_count:integer=0):bool; overload;
+// next is just wrapper
+function GetFile(url:PWideChar;save_file:PWideChar;
+ hNetLib:THANDLE=0;recurse_count:integer=0):bool; overload;
+
+function GetProxy(hNetLib:THANDLE):PAnsiChar;
+function LoadImageURL(url:pAnsiChar;size:integer=0):HBITMAP;
+
+implementation
+
+uses Messages,dbsettings,common,io,freeimage,syswin;
+
+const
+ clGroup = 'Group';
+// Save / Load contact
+const
+ opt_cproto = 'cproto';
+ opt_cuid = 'cuid';
+ opt_ischat = 'ischat';
+
+function SetButtonIcon(btn:HWND;name:PAnsiChar):HICON;
+begin
+ result:=CallService(MS_SKIN2_GETICON,0,LPARAM(name));
+ SendMessage(btn,BM_SETIMAGE,IMAGE_ICON,result);
+end;
+
+function ConvertFileName(src:pWideChar;dst:pWideChar;hContact:THANDLE=0):pWideChar; overload;
+var
+ pc:pWideChar;
+begin
+ result:=dst;
+ dst^:=#0;
+ if (src<>nil) and (src^<>#0) then
+ begin
+ if isVarsInstalled then
+ begin
+ pc:=ParseVarString(src,hContact);
+ src:=pc;
+ end
+ else
+ pc:=nil;
+ CallService(MS_UTILS_PATHTOABSOLUTEW,wparam(src),lparam(dst));
+ mFreeMem(pc);
+ end;
+end;
+
+function ConvertFileName(src:pWideChar;hContact:THANDLE=0):pWideChar; overload;
+var
+ buf1:array [0..511] of WideChar;
+begin
+ if (src<>nil) and (src^<>#0) then
+ StrDupW(result,ConvertFileName(src,buf1,hContact))
+ else
+ result:=nil;
+end;
+
+function ConvertFileName(src:pAnsiChar;dst:pAnsiChar;hContact:THANDLE=0):pAnsiChar; overload;
+var
+ pc:pAnsiChar;
+begin
+ result:=dst;
+ dst^:=#0;
+ if (src<>nil) and (src^<>#0) then
+ begin
+ if isVarsInstalled then
+ begin
+ pc:=ParseVarString(src,hContact);
+ src:=pc;
+ end
+ else
+ pc:=nil;
+ CallService(MS_UTILS_PATHTOABSOLUTE,wparam(src),lparam(dst));
+ mFreeMem(pc);
+ end;
+end;
+
+function ConvertFileName(src:pAnsiChar;hContact:THANDLE=0):pAnsiChar; overload;
+var
+ buf1:array [0..511] of AnsiChar;
+begin
+ if (src<>nil) and (src^<>#0) then
+ StrDup(result,ConvertFileName(src,buf1,hContact))
+ else
+ result:=nil;
+end;
+
+const
+ MirCP:integer=-1;
+
+function MirandaCP:integer;
+begin
+ if MirCP<0 then
+ MirCP:=CallService(MS_LANGPACK_GETCODEPAGE,0,0);
+ result:=MirCP;
+end;
+
+function IsChat(hContact:THANDLE):bool;
+begin
+ result:=DBReadByte(hContact,
+ PAnsiChar(CallService(MS_PROTO_GETCONTACTBASEPROTO,hContact,0)),
+ 'ChatRoom',0)=1;
+end;
+
+function isVarsInstalled:bool;
+begin
+ result:=ServiceExists(MS_VARS_FORMATSTRING)<>0;
+end;
+
+function ParseVarString(astr:pAnsiChar;aContact:THANDLE=0;extra:pAnsiChar=nil):pAnsiChar;
+var
+ tfi:TFORMATINFO;
+ tmp,pc:pAnsiChar;
+ dat:TREPLACEVARSDATA;
+begin
+ if ServiceExists(MS_UTILS_REPLACEVARS)<>0 then
+ begin
+ FillChar(dat,SizeOf(TREPLACEVARSDATA),0);
+ dat.cbSize :=SizeOf(TREPLACEVARSDATA);
+ pc:=pAnsiChar(CallService(MS_UTILS_REPLACEVARS,wparam(astr),lparam(@dat)));
+ astr:=pc;
+ end
+ else
+ pc:=nil;
+
+ if isVarsInstalled then
+ begin
+ FillChar(tfi,SizeOf(tfi),0);
+ with tfi do
+ begin
+ cbSize :=SizeOf(TFORMATINFO);
+ szFormat.a :=astr;
+ szExtraText.a:=extra;
+ hContact :=aContact;
+ end;
+ tmp:=pointer(CallService(MS_VARS_FORMATSTRING,wparam(@tfi),0));
+ StrDup(result,tmp);
+ CallService(MS_VARS_FREEMEMORY,wparam(tmp),0);
+ end
+ else
+ begin
+ StrDup(result,astr);
+ end;
+ mir_free(pc);
+end;
+
+function ParseVarString(astr:pWideChar;aContact:THANDLE=0;extra:pWideChar=nil):pWideChar;
+var
+ tfi:TFORMATINFO;
+ tmp,pc:pWideChar;
+ dat:TREPLACEVARSDATA;
+begin
+ if ServiceExists(MS_UTILS_REPLACEVARS)<>0 then
+ begin
+ FillChar(dat,SizeOf(TREPLACEVARSDATA),0);
+ dat.cbSize :=SizeOf(TREPLACEVARSDATA);
+ dat.dwflags:=RVF_UNICODE;
+ pc:=pWideChar(CallService(MS_UTILS_REPLACEVARS,wparam(astr),lparam(@dat)));
+ astr:=pc;
+ end
+ else
+ pc:=nil;
+
+ if isVarsInstalled then
+ begin
+ FillChar(tfi,SizeOf(tfi),0);
+ with tfi do
+ begin
+ cbSize :=SizeOf(TFORMATINFO);
+ flags :=FIF_UNICODE;
+ szFormat.w :=astr;
+ szExtraText.w:=extra;
+ hContact :=aContact;
+ end;
+ tmp:=pointer(CallService(MS_VARS_FORMATSTRING,wparam(@tfi),0));
+ StrDupW(result,tmp);
+ CallService(MS_VARS_FREEMEMORY,wparam(tmp),0);
+ end
+ else
+ begin
+ StrDupW(result,astr);
+ end;
+ mir_free(pc); // forced!
+// mFreeMem(pc);
+end;
+
+function ShowVarHelp(dlg:HWND;id:integer=0):integer;
+var
+ vhi:TVARHELPINFO;
+begin
+ FillChar(vhi,SizeOf(vhi),0);
+ with vhi do
+ begin
+ cbSize:=SizeOf(vhi);
+ if id=0 then
+ flags:=VHF_NOINPUTDLG
+ else
+ begin
+ flags :=VHF_FULLDLG or VHF_SETLASTSUBJECT;
+ hwndCtrl:=GetDlgItem(dlg,id);
+ end;
+ end;
+ result:=CallService(MS_VARS_SHOWHELPEX,dlg,lparam(@vhi));
+end;
+
+procedure ShowPopupW(text:pWideChar;title:pWideChar=nil);
+var
+ ppdu:TPOPUPDATAW;
+begin
+ FillChar(ppdu,SizeOf(TPOPUPDATAW),0);
+ if CallService(MS_POPUP_ISSECONDLINESHOWN,0,0)<>0 then
+ begin
+ StrCopyW(ppdu.lpwzText,text,MAX_SECONDLINE-1);
+ if title<>nil then
+ StrCopyW(ppdu.lpwzContactName,title,MAX_CONTACTNAME-1)
+ else
+ ppdu.lpwzContactName[0]:=' ';
+ end
+ else
+ begin
+ StrCopyW(ppdu.lpwzContactName,text,MAX_CONTACTNAME-1);
+ ppdu.lpwzText[0]:=' ';
+ end;
+ CallService(MS_POPUP_ADDPOPUPW,wparam(@ppdu),APF_NO_HISTORY);
+end;
+
+function TranslateA2W(sz:PAnsiChar):PWideChar;
+var
+ tmp:pWideChar;
+begin
+ mGetMem(tmp,(StrLen(sz)+1)*SizeOf(WideChar));
+ Result:=PWideChar(CallService(MS_LANGPACK_TRANSLATESTRING,LANG_UNICODE,
+ lParam(FastAnsiToWideBuf(sz,tmp))));
+ if Result<>tmp then
+ begin
+ StrDupW(Result,Result);
+ mFreeMem(tmp);
+ end;
+end;
+
+function GetContactProtoAcc(hContact:THANDLE):PAnsiChar;
+begin
+ if ServiceExists(MS_PROTO_GETCONTACTBASEACCOUNT)<>0 then
+ result:=PAnsiChar(CallService(MS_PROTO_GETCONTACTBASEACCOUNT,hContact,0))
+ else
+ result:=PAnsiChar(CallService(MS_PROTO_GETCONTACTBASEPROTO,hContact,0));
+end;
+
+function IsMirandaUser(hContact:THANDLE):integer; // >0=Miranda; 0=Not miranda; -1=unknown
+var
+ sz:PAnsiChar;
+begin
+ sz:=PAnsiChar(CallService(MS_PROTO_GETCONTACTBASEPROTO,hContact,0));
+ sz:=DBReadString(hContact,sz,'MirVer');
+ if sz<>nil then
+ begin
+ result:=int_ptr(StrPos(sz,'Miranda'));
+ mFreeMem(sz);
+ end
+ else
+ result:=-1;
+end;
+
+function SetCListSelContact(hContact:THANDLE):THANDLE;
+var
+ wnd:HWND;
+begin
+ wnd:=CallService(MS_CLUI_GETHWNDTREE,0,0);
+ result:=hContact;
+// hContact:=SendMessage(wnd,CLM_FINDCONTACT ,hContact,0);
+ SendMessage(wnd,CLM_SELECTITEM ,hContact,0);
+// SendMessage(wnd,CLM_ENSUREVISIBLE,hContact,0);
+end;
+
+function GetCListSelContact:THANDLE;
+begin
+ result:=SendMessageW(CallService(MS_CLUI_GETHWNDTREE,0,0),CLM_GETSELECTION,0,0);
+end;
+
+function LoadContact(group,setting:PAnsiChar):THANDLE;
+var
+ p,proto:pAnsiChar;
+ section:array [0..63] of AnsiChar;
+ dbv:TDBVARIANT;
+ is_chat:boolean;
+begin
+ p:=StrCopyE(section,setting);
+ StrCopy(p,opt_cproto); proto :=DBReadString(0,group,section);
+ StrCopy(p,opt_ischat); is_chat:=DBReadByte (0,group,section,0)<>0;
+ StrCopy(p,opt_cuid );
+ if is_chat then
+ dbv.szVal.W:=DBReadUnicode(0,group,section,@dbv)
+ else
+ DBReadSetting(0,group,section,@dbv);
+
+ result:=FindContactHandle(proto,dbv,is_chat);
+
+ mFreeMem(proto);
+ if not is_chat then
+ DBFreeVariant(@dbv)
+ else
+ mFreeMem(dbv.szVal.W);
+end;
+
+function SaveContact(hContact:THANDLE;group,setting:PAnsiChar):integer;
+var
+ p,proto,uid:pAnsiChar;
+ cws:TDBVARIANT;
+ section:array [0..63] of AnsiChar;
+ pw:pWideChar;
+ is_chat:boolean;
+begin
+ result:=0;
+ proto:=GetContactProtoAcc(hContact);
+ if proto<>nil then
+ begin
+ p:=StrCopyE(section,setting);
+ is_chat:=IsChat(hContact);
+ if is_chat then
+ begin
+ pw:=DBReadUnicode(hContact,proto,'ChatRoomID');
+ StrCopy(p,opt_cuid); DBWriteUnicode(0,group,section,pw);
+ mFreeMem(pw);
+ result:=1;
+ end
+ else
+ begin
+ uid:=pAnsiChar(CallProtoService(proto,PS_GETCAPS,PFLAG_UNIQUEIDSETTING,0));
+ if uid<>pAnsiChar(CALLSERVICE_NOTFOUND) then
+ begin
+ if DBReadSetting(hContact,proto,uid,@cws)=0 then
+ begin
+ StrCopy(p,opt_cuid); DBWriteSetting(0,group,section,@cws);
+ DBFreeVariant(@cws);
+ result:=1;
+ end;
+ end;
+ end;
+ if result<>0 then
+ begin
+ StrCopy(p,opt_cproto); DBWriteString(0,group,section,proto);
+ StrCopy(p,opt_ischat); DBWriteByte (0,group,section,ord(is_chat));
+ end;
+ end;
+end;
+
+function WndToContact(wnd:hwnd):THANDLE; overload;
+var
+ hContact:THANDLE;
+ mwid:TMessageWindowInputData;
+ mwod:TMessageWindowOutputData;
+begin
+ wnd:=GetParent(wnd); //!!
+ hContact:=CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ with mwid do
+ begin
+ cbSize:=SizeOf(mwid);
+ uFlags:=MSG_WINDOW_UFLAG_MSG_BOTH;
+ end;
+ mwod.cbSize:=SizeOf(mwod);
+ while hContact<>0 do
+ begin
+ mwid.hContact:=hContact;
+ if CallService(MS_MSG_GETWINDOWDATA,wparam(@mwid),lparam(@mwod))=0 then
+ begin
+ if {((mwod.uState and MSG_WINDOW_STATE_FOCUS)<>0) and} (mwod.hwndWindow=wnd) then
+ begin
+ result:=mwid.hContact;
+ exit;
+ end
+ end;
+ hContact:=CallService(MS_DB_CONTACT_FINDNEXT,hContact,0);
+ end;
+ result:=0;
+end;
+
+function WndToContact:THANDLE; overload;
+var
+ wnd:HWND;
+begin
+ wnd:=GetFocus;
+ if wnd=0 then
+ wnd:=WaitFocusedWndChild(GetForegroundWindow);
+ if wnd<>0 then
+ result:=WndToContact(wnd)
+ else
+ result:=0;
+ if result=0 then
+ result:=GetCListSelContact;
+end;
+
+function GetContactStatus(hContact:THANDLE):integer;
+var
+ szProto:PAnsiChar;
+begin
+ szProto:=PAnsiChar(CallService(MS_PROTO_GETCONTACTBASEPROTO,hContact,0));
+ if szProto=NIL then
+ result:=ID_STATUS_OFFLINE
+ else
+ result:=DBReadWord(hContact,szProto,'Status',ID_STATUS_OFFLINE);
+end;
+
+function CheckPath(filename,profilepath,path:PAnsiChar):PAnsiChar;
+var
+ buf:array [0..511] of AnsiChar;
+ f:THANDLE;
+ p:PAnsiChar;
+begin
+ result:=nil;
+ if profilepath<>nil then
+ StrCopy(buf,profilepath)
+ else
+ buf[0]:=#0;
+ StrCat(buf,filename);
+ f:=Reset(buf);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ begin
+ if path<>nil then
+ begin
+ CallService(MS_UTILS_PATHTOABSOLUTE,wparam(path),lparam(@buf));
+ p:=StrEnd(buf);
+ if p^<>'\' then
+ begin
+ p^:='\';
+ inc(p);
+ p^:=#0;
+ end;
+ end
+ else if profilepath=nil then
+ exit
+ else
+ buf[0]:=#0;
+ StrCat(buf,filename); //path\prefix+name
+ f:=Reset(buf);
+ end;
+ if f<>THANDLE(INVALID_HANDLE_VALUE) then
+ begin
+ CloseHandle(f);
+ StrDup(result,buf);
+ end;
+end;
+
+function GetAddonFileName(prefix,altname,path:PAnsiChar;ext:PAnsiChar):PAnsiChar;
+var
+ profilepath:array [0..511] of AnsiChar;
+ altfilename,filename:array [0..127] of AnsiChar;
+ p:PAnsiChar;
+begin
+ CallService(MS_DB_GETPROFILEPATH,300,lparam(@profilepath));
+ p:=StrEnd(profilepath);
+ p^:='\'; inc(p);
+ p^:=#0;
+ filename[0]:=#0;
+ altfilename[0]:=#0;
+ if prefix<>nil then
+ begin
+ StrCopy(filename,prefix);
+ p:=StrEnd(filename);
+ CallService(MS_DB_GETPROFILENAME,SizeOf(filename)-integer(p-pAnsiChar(@filename)),lparam(p));
+ ChangeExt(filename,ext);
+ result:=CheckPath(filename,profilepath,path);
+ end
+ else
+ result:=nil;
+
+ if (result=nil) and (altname<>nil) then
+ begin
+ StrCopy(altfilename,altname);
+ ChangeExt(altfilename,ext);
+ result:=CheckPath(altfilename,profilepath,path);
+ end;
+ if result=nil then
+ begin
+ if filename[0]<>#0 then
+ StrCat(profilepath,filename)
+ else
+ StrCat(profilepath,altfilename);
+ StrDup(result,profilepath);
+ end;
+end;
+
+procedure ShowContactDialog(hContact:THANDLE;DblClk:boolean=true;anystatus:boolean=true);
+var
+ pc:array [0..127] of AnsiChar;
+begin
+{
+CallService(MS_CLIST_CONTACTDOUBLECLICKED,hContact,0);
+}
+ if (hContact<>0) and (CallService(MS_DB_CONTACT_IS,hContact,0)<>0) then
+ begin
+ if StrCopy(pc,PAnsiChar(CallService(MS_PROTO_GETCONTACTBASEPROTO,hContact,0)))<>nil then
+ if DblClk or (DBReadByte(hContact,pc,'ChatRoom',0)=1) then // chat room
+ begin
+ if not anystatus then
+ begin
+ StrCat(pc,PS_GETSTATUS);
+ anystatus:=(CallService(pc,0,0)<>ID_STATUS_OFFLINE);
+ end;
+ if anystatus then
+ begin
+ CallService(MS_CLIST_CONTACTDOUBLECLICKED,hContact,0); //??
+ // if chat exist, open chat
+ // else create new session
+ end;
+ end
+ else
+ begin
+ if ServiceExists(MS_MSG_CONVERS)<>0 then // Convers compat.
+ CallService(MS_MSG_CONVERS,hContact,0)
+ else
+ CallService(MS_MSG_SENDMESSAGE,hContact,0)
+ end;
+ end;
+end;
+
+procedure SendChatText(pszID:pointer;pszModule:PAnsiChar;pszText:pointer);
+var
+ gcd:TGCDEST;
+ gce:TGCEVENT;
+begin
+ gcd.pszModule:=pszModule;
+ gcd.iType :=GC_EVENT_SENDMESSAGE;
+ gcd.szID.w :=pszID;
+
+ FillChar(gce,SizeOf(TGCEVENT),0);
+ gce.cbSize :=SizeOf(TGCEVENT);
+ gce.pDest :=@gcd;
+ gce.bIsMe :=true;
+ gce.szText.w:=pszText;
+ gce.dwFlags :=GCEF_ADDTOLOG+GC_UNICODE;
+ gce.time :=GetCurrentTime;
+
+ CallServiceSync(MS_GC_EVENT,0,lparam(@gce));
+end;
+
+procedure SendToChat(hContact:THANDLE;pszText:PWideChar);
+var
+ gci:TGC_INFO;
+ pszModule:PAnsiChar;
+ i,cnt:integer;
+begin
+ pszModule:=PAnsiChar(CallService(MS_PROTO_GETCONTACTBASEPROTO,hContact,0));
+ cnt:=CallService(MS_GC_GETSESSIONCOUNT,0,lparam(pszModule));
+ i:=0;
+ gci.pszModule:=pszModule;
+ while i<cnt do
+ begin
+ gci.iItem:=i;
+ gci.Flags:=GCI_BYINDEX+GCI_HCONTACT+GCI_ID;
+ CallService(MS_GC_GETINFO,0,lparam(@gci));
+ if gci.hContact=hContact then
+ begin
+ SendChatText(gci.pszID.w,pszModule,pszText);
+ break;
+ end;
+ inc(i);
+ end;
+end;
+
+function FindContactHandle(proto:pAnsiChar;const dbv:TDBVARIANT;is_chat:boolean=false):THANDLE;
+var
+ uid:pAnsiChar;
+ ldbv:TDBVARIANT;
+ hContact:THANDLE;
+ pw:pWideChar;
+begin
+ result:=0;
+ uid:=nil;
+ if not is_chat then
+ begin
+ uid:=pAnsiChar(CallProtoService(proto,PS_GETCAPS,PFLAG_UNIQUEIDSETTING,0));
+ if uid=pAnsiChar(CALLSERVICE_NOTFOUND) then exit;
+ end;
+
+ hContact:=CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ while hContact<>0 do
+ begin
+ if is_chat then
+ begin
+ if IsChat(hContact) then
+ begin
+ pw:=DBReadUnicode(hContact,proto,'ChatRoomID');
+ if StrCmpW(pw,dbv.szVal.W)=0 then result:=hContact;
+ mFreeMem(pw);
+ end
+ end
+ else
+ begin
+ if DBReadSetting(hContact,proto,uid,@ldbv)=0 then
+ begin
+ if dbv._type=ldbv._type then
+ begin
+ case dbv._type of
+// DBVT_DELETED: ;
+ DBVT_BYTE : if dbv.bVal=ldbv.bVal then result:=hContact;
+ DBVT_WORD : if dbv.wVal=ldbv.wVal then result:=hContact;
+ DBVT_DWORD : if dbv.dVal=ldbv.dVal then result:=hContact;
+ DBVT_UTF8,
+ DBVT_ASCIIZ : if StrCmp (dbv.szVal.A,ldbv.szVal.A)=0 then result:=hContact;
+ DBVT_WCHAR : if StrCmpW(dbv.szVal.W,ldbv.szVal.W)=0 then result:=hContact;
+ DBVT_BLOB : begin
+ if dbv.cpbVal = ldbv.cpbVal then
+ begin
+ if CompareMem(dbv.pbVal,ldbv.pbVal,dbv.cpbVal) then
+ result:=hContact;
+ end;
+ end;
+ end;
+ end;
+ DBFreeVariant(@ldbv);
+ end;
+ end;
+ // added 2011.04.20
+ if result<>0 then break;
+ hContact:=CallService(MS_DB_CONTACT_FINDNEXT,hContact,0);
+ end;
+end;
+
+function IsContactActive(hContact:THANDLE;proto:pAnsiChar=nil):integer;
+var
+ p:PPROTOACCOUNT;
+ dbv :TDBVARIANT;
+ dbcgs:TDBCONTACTGETSETTING;
+ name: array [0..31] of AnsiChar;
+begin
+
+ dbv._type :=DBVT_ASCIIZ;
+ dbv.szVal.a:=@name;
+ dbv.cchVal :=SizeOf(name);
+ dbcgs.pValue :=@dbv;
+ dbcgs.szModule :='Protocol';
+ dbcgs.szSetting:='p';
+
+ if CallService(MS_DB_CONTACT_GETSETTINGSTATIC,hContact,lparam(@dbcgs))=0 then
+ begin
+ result:=0;
+
+ if ServiceExists(MS_PROTO_GETACCOUNT)<>0 then
+ begin
+ p:=PPROTOACCOUNT(CallService(MS_PROTO_GETACCOUNT,0,lparam(dbv.szVal.a)));
+ if p=nil then
+ result:=-2 // deleted
+ else if (p^.bIsEnabled=0) or p^.bDynDisabled then
+ result:=-1; // disabled
+ end
+ else
+ begin
+ if CallService(MS_PROTO_ISPROTOCOLLOADED,0,lparam(dbv.szVal.a))=0 then
+ result:=-1;
+ end;
+
+ if (result=0) and (DBReadByte(hContact,strCList,'Hidden',0)=0) then
+ begin
+ result:=255;
+ if ServiceExists(MS_MC_GETMETACONTACT)<>0 then
+ begin
+ if CallService(MS_MC_GETMETACONTACT,hContact,0)<>0 then
+ result:=2;
+ if StrCmp(
+ PAnsiChar(CallService(MS_PROTO_GETCONTACTBASEPROTO,hContact,0)),
+ PAnsiChar(CallService(MS_MC_GETPROTOCOLNAME,0,0)))=0 then
+ result:=1;
+ end;
+ end;
+ if proto<>nil then
+ StrCopy(proto,dbv.szVal.a);
+ end
+ else
+ begin
+ result:=-2;
+ if proto<>nil then
+ proto^:=#0;
+ end;
+
+end;
+
+// Import plugin function adaptation
+function CreateGroupW(name:pWideChar;hContact:THANDLE):integer;
+var
+ groupId:integer;
+ groupIdStr:array [0..10] of AnsiChar;
+ dbv:TDBVARIANT;
+ cgs:TDBCONTACTGETSETTING;
+ grbuf:array [0..127] of WideChar;
+ p:pWideChar;
+begin
+ if (name=nil) or (name^=#0) then
+ begin
+ result:=0;
+ exit;
+ end;
+
+ StrCopyW(@grbuf[1],name);
+ grbuf[0]:=WideChar(1 or GROUPF_EXPANDED);
+
+ // Check for duplicate & find unused id
+ groupId:=0;
+ cgs.szModule:='CListGroups';
+ cgs.pValue :=@dbv;
+ repeat
+ dbv._type:=DBVT_WCHAR;
+ cgs.szSetting:=IntToStr(groupIdStr,groupId);
+ if CallService(MS_DB_CONTACT_GETSETTING_STR,0,lParam(@cgs))<>0 then
+ break;
+
+ if StrCmpW(dbv.szVal.w+1,@grbuf[1])=0 then
+ begin
+ if hContact<>0 then
+ DBWriteUnicode(hContact,strCList,clGroup,@grbuf[1]);
+
+ DBFreeVariant(@dbv);
+ result:=0;
+ exit;
+ end;
+
+ DBFreeVariant(@dbv);
+ inc(groupId);
+ until false;
+
+ DBWriteUnicode(0,'CListGroups',groupIdStr,grbuf);
+
+ if hContact<>0 then
+ DBWriteUnicode(hContact,strCList,clGroup,@grbuf[1]);
+
+ p:=StrRScanW(grbuf,'\');
+ if p<>nil then
+ begin
+ p^:=#0;
+ CreateGroupW(grbuf+1,0);
+ end;
+
+ result:=1;
+end;
+
+function CreateGroup(name:pAnsiChar;hContact:THANDLE):integer;
+var
+ groupId:integer;
+ groupIdStr:array [0..10] of AnsiChar;
+ dbv:TDBVARIANT;
+ cgs:TDBCONTACTGETSETTING;
+ grbuf:array [0..127] of AnsiChar;
+ p:pAnsiChar;
+begin
+ if (name=nil) or (name^=#0) then
+ begin
+ result:=0;
+ exit;
+ end;
+
+ StrCopy(@grbuf[1],name);
+ grbuf[0]:=CHAR(1 or GROUPF_EXPANDED);
+
+ // Check for duplicate & find unused id
+ groupId:=0;
+ cgs.szModule:='CListGroups';
+ cgs.pValue :=@dbv;
+ repeat
+ dbv._type:=DBVT_ASCIIZ;
+ cgs.szSetting:=IntToStr(groupIdStr,groupId);
+ if CallService(MS_DB_CONTACT_GETSETTING_STR,0,lParam(@cgs))<>0 then
+ break;
+
+ if StrCmp(dbv.szVal.a+1,@grbuf[1])=0 then
+ begin
+ if hContact<>0 then
+ DBWriteString(hContact,strCList,clGroup,@grbuf[1]);
+
+ DBFreeVariant(@dbv);
+ result:=0;
+ exit;
+ end;
+
+ DBFreeVariant(@dbv);
+ inc(groupId);
+ until false;
+
+ DBWriteString(0,'CListGroups',groupIdStr,grbuf);
+
+ if hContact<>0 then
+ DBWriteString(hContact,strCList,clGroup,@grbuf[1]);
+
+ p:=StrRScan(grbuf,'\');
+ if p<>nil then
+ begin
+ p^:=#0;
+ CreateGroup(grbuf+1,0);
+ end;
+
+ result:=1;
+end;
+
+function MyStrSort(para1:pointer; para2:pointer):int; cdecl;
+begin
+ result:=StrCmpW(pWideChar(para1),pWideChar(para2));
+end;
+
+function MakeGroupMenu(idxfrom:integer=100):HMENU;
+var
+ sl:TSortedList;
+ i:integer;
+ b:array [0..15] of AnsiChar;
+ p:pWideChar;
+begin
+ result:=CreatePopupMenu;
+ i:=0;
+ AppendMenuW(result,MF_STRING,idxfrom,TranslateW('<Root Group>'));
+ AppendMenuW(result,MF_SEPARATOR,0,nil);
+ FillChar(sl,SizeOf(sl),0);
+ sl.increment:=16;
+ sl.sortFunc:=@MyStrSort;
+ repeat
+ p:=DBReadUnicode(0,'CListGroups',IntToStr(b,i),nil);
+ if p=nil then break;
+ List_InsertPtr(@sl,p+1);
+ inc(i);
+ until false;
+ inc(idxfrom);
+ for i:=0 to sl.realCount-1 do
+ begin
+ AppendMenuW(result,MF_STRING,idxfrom+i,pWideChar(sl.Items[i]));
+ p:=pWideChar(sl.Items[i])-1;
+ mFreeMem(p);
+ end;
+ List_Destroy(@sl);
+end;
+
+function GetNewGroupName(parent:HWND):pWideChar;
+var
+ mmenu:HMENU;
+ i:integer;
+ buf:array [0..63] of WideChar;
+ pt:TPoint;
+begin
+ result:=nil;
+ mmenu:=MakeGroupMenu(100);
+ GetCursorPos(pt);
+ i:=integer(TrackPopupMenu(mmenu,TPM_RETURNCMD+TPM_NONOTIFY,pt.x,pt.y,0,parent,nil));
+ if i>100 then // no root or cancel
+ begin
+ GetMenuStringW(mmenu,i,buf,HIGH(buf)+1,MF_BYCOMMAND);
+ StrDupW(result,buf);
+ end;
+ DestroyMenu(mmenu);
+end;
+
+function SendRequest(url:PAnsiChar;rtype:int;args:pAnsiChar=nil;hNetLib:THANDLE=0):pAnsiChar;
+var
+ nlu:TNETLIBUSER;
+ req :TNETLIBHTTPREQUEST;
+ resp:PNETLIBHTTPREQUEST;
+ hTmpNetLib:THANDLE;
+ nlh:array [0..1] of TNETLIBHTTPHEADER;
+ buf:array [0..31] of AnsiChar;
+begin
+ result:=nil;
+
+ FillChar(req,SizeOf(req),0);
+ req.cbSize :=NETLIBHTTPREQUEST_V1_SIZE;//SizeOf(req);
+ req.requestType:=rtype;
+ req.szUrl :=url;
+ req.flags :=NLHRF_NODUMP or NLHRF_HTTP11;
+ if args<>nil then
+ begin
+ nlh[0].szName :='Content-Type';
+ nlh[0].szValue:='application/x-www-form-urlencoded';
+ nlh[1].szName :='Content-Length';
+ nlh[1].szValue:=IntToStr(buf,StrLen(args));
+ req.headers :=@nlh;
+ req.headersCount:=2;
+ req.pData :=args;
+ req.dataLength :=StrLen(args);
+ end;
+
+ if hNetLib=0 then
+ begin
+ FillChar(nlu,SizeOf(nlu),0);
+ nlu.cbSize :=SizeOf(nlu);
+ nlu.flags :=NUF_HTTPCONNS or NUF_NOHTTPSOPTION or NUF_OUTGOING or NUF_NOOPTIONS;
+ nlu.szSettingsModule:='dummy';
+ hTmpNetLib:=CallService(MS_NETLIB_REGISTERUSER,0,lparam(@nlu));
+ end
+ else
+ hTmpNetLib:=hNetLib;
+
+ resp:=pointer(CallService(MS_NETLIB_HTTPTRANSACTION,hTmpNetLib,lparam(@req)));
+
+ if resp<>nil then
+ begin
+ if resp^.resultCode=200 then
+ begin
+ StrDup(result,resp.pData,resp.dataLength);
+ end
+ else
+ begin
+ result:=pAnsiChar(int_ptr(resp^.resultCode and $0FFF));
+ end;
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT,0,lparam(resp));
+ end;
+
+ if (hNetLib=0) and (nlu.cbSize<>0) then
+ CallService(MS_NETLIB_CLOSEHANDLE,hTmpNetLib,0);
+end;
+
+(*
+static int __inline NLog(AnsiChar *msg) {
+ return CallService(MS_NETLIB_LOG, (WPARAM)hNetlibUser, (LPARAM)msg);
+}
+*)
+function GetFile(url:PAnsiChar;save_file:PAnsiChar;
+ hNetLib:THANDLE=0;recurse_count:integer=0):bool;
+var
+ nlu:TNETLIBUSER;
+ req :TNETLIBHTTPREQUEST;
+ resp:PNETLIBHTTPREQUEST;
+ hSaveFile:THANDLE;
+ i:integer;
+begin
+ result:=false;
+ if recurse_count>MAX_REDIRECT_RECURSE then
+ exit;
+ if (url=nil) or (url^=#0) or (save_file=nil) or (save_file^=#0) then
+ exit;
+
+ FillChar(req,SizeOf(req),0);
+ req.cbSize :=NETLIBHTTPREQUEST_V1_SIZE;//SizeOf(req);
+ req.requestType:=REQUEST_GET;
+ req.szUrl :=url;
+ req.flags :=NLHRF_NODUMP;
+
+
+ FillChar(nlu,SizeOf(nlu),0);
+ if hNetLib=0 then
+ begin
+ nlu.cbSize :=SizeOf(nlu);
+ nlu.flags :=NUF_HTTPCONNS or NUF_NOHTTPSOPTION or NUF_OUTGOING or NUF_NOOPTIONS;
+ nlu.szSettingsModule:='dummy';
+ hNetLib:=CallService(MS_NETLIB_REGISTERUSER,0,lparam(@nlu));
+ end;
+
+ resp:=pointer(CallService(MS_NETLIB_HTTPTRANSACTION,hNetLib,lparam(@req)));
+
+ if resp<>nil then
+ begin
+ if resp^.resultCode=200 then
+ begin
+ hSaveFile:=Rewrite(save_file);
+ if hSaveFile<>THANDLE(INVALID_HANDLE_VALUE) then
+ begin
+ BlockWrite(hSaveFile,resp^.pData^,resp^.dataLength);
+ CloseHandle(hSaveFile);
+ result:=true;
+ end
+ end
+ else if (resp.resultCode>=300) and (resp.resultCode<400) then
+ begin
+ // get new location
+ for i:=0 to resp^.headersCount-1 do
+ begin
+ //MessageBox(0,resp^.headers[i].szValue, resp^.headers[i].szName,MB_OK);
+ if StrCmp(resp^.headers^[i].szName,'Location')=0 then
+ begin
+ result:=GetFile(resp^.headers^[i].szValue,save_file,hNetLib,recurse_count+1);
+ break;
+ end
+ end;
+ end
+ else
+ begin
+{
+ _stprintf(buff, TranslateT("Failed to download \"%s\" - Invalid response, code %d"), plugin_name, resp->resultCode);
+
+ ShowError(buff);
+ AnsiChar *ts = GetAString(buff);
+ NLog(ts);
+}
+ end;
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT,0,lparam(resp));
+
+ if nlu.cbSize<>0 then
+ CallService(MS_NETLIB_CLOSEHANDLE,hNetLib,0);
+ end;
+end;
+
+function GetFile(url:PWideChar;save_file:PWideChar;
+ hNetLib:THANDLE=0;recurse_count:integer=0):bool;
+var
+ aurl,asave:array [0..MAX_PATH-1] of AnsiChar;
+begin
+ FastWideToAnsiBuf(url,aurl);
+ FastWideToAnsiBuf(save_file,asave);
+ result:=GetFile(aurl,asave,hNetLib,0);
+end;
+
+function GetProxy(hNetLib:THANDLE):PAnsiChar;
+var
+ nlus:TNETLIBUSERSETTINGS;
+ pc:PAnsiChar;
+ proxy:array [0..127] of AnsiChar;
+begin
+ result:=nil;
+ nlus.cbSize:=SizeOf(nlus);
+ if CallService(MS_NETLIB_GETUSERSETTINGS,hNetLib,lparam(@nlus))<>0 then
+ begin
+ if nlus.useProxy<>0 then
+ begin
+ if nlus.proxyType<>PROXYTYPE_IE then
+ begin
+ pc:=@proxy;
+ if nlus.szProxyServer<>nil then
+ begin
+ if nlus.useProxyAuth<>0 then
+ begin
+ if nlus.szProxyAuthUser<>nil then
+ begin
+ pc:=StrCopyE(proxy,nlus.szProxyAuthUser);
+ if nlus.szProxyAuthPassword<>nil then
+ begin
+ pc^:=':'; inc(pc);
+ pc:=StrCopyE(pc,nlus.szProxyAuthPassword);
+ end;
+ pc^:='@';
+ inc(pc);
+ end;
+ end;
+ pc:=StrCopyE(pc,nlus.szProxyServer);
+ if nlus.wProxyPort<>0 then
+ begin
+ pc^:=':'; inc(pc);
+ IntToStr(pc,nlus.wProxyPort);
+ end;
+ end;
+ StrDup(result,proxy);
+ end
+ else // use IE proxy
+ begin
+ mGetMem(result,1);
+ result^:=#0;
+ end;
+ end;
+ end;
+end;
+
+function LoadImageURL(url:pAnsiChar;size:integer=0):HBITMAP;
+var
+ nlu:TNETLIBUSER;
+ req :TNETLIBHTTPREQUEST;
+ resp:PNETLIBHTTPREQUEST;
+ hNetLib:THANDLE;
+ im:TIMGSRVC_MEMIO;
+begin
+ result:=0;
+ if (url=nil) or (url^=#0) then
+ exit;
+
+ FillChar(req,SizeOf(req),0);
+ req.cbSize :=NETLIBHTTPREQUEST_V1_SIZE;//SizeOf(req);
+ req.requestType:=REQUEST_GET;
+ req.szUrl :=url;
+ req.flags :=NLHRF_NODUMP;
+
+ FillChar(nlu,SizeOf(nlu),0);
+ nlu.cbSize :=SizeOf(nlu);
+ nlu.flags :=NUF_HTTPCONNS or NUF_NOHTTPSOPTION or NUF_OUTGOING or NUF_NOOPTIONS;
+ nlu.szSettingsModule:='dummy';
+ hNetLib:=CallService(MS_NETLIB_REGISTERUSER,0,lparam(@nlu));
+
+ resp:=pointer(CallService(MS_NETLIB_HTTPTRANSACTION,hNetLib,lparam(@req)));
+
+ if resp<>nil then
+ begin
+ if resp^.resultCode=200 then
+ begin
+ im.iLen :=resp.dataLength;
+ im.pBuf :=resp.pData;
+ im.flags:=size shl 16;
+ im.fif :=FIF_JPEG;
+ result :=CallService(MS_IMG_LOADFROMMEM,wparam(@im),0);
+// if result<>0 then
+// DeleteObject(SendMessage(wnd,STM_SETIMAGE,IMAGE_BITMAP,result)); //!!
+ end;
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT,0,lparam(resp));
+ end;
+ CallService(MS_NETLIB_CLOSEHANDLE,hNetLib,0);
+end;
+
+function RegisterSingleIcon(resname,ilname,descr,group:pAnsiChar):int;
+var
+ sid:TSKINICONDESC;
+begin
+ FillChar(sid,SizeOf(TSKINICONDESC),0);
+ sid.cbSize :=SizeOf(TSKINICONDESC);
+ sid.cx :=16;
+ sid.cy :=16;
+ sid.flags :=0;
+ sid.szSection.a:=group;
+
+ sid.hDefaultIcon :=LoadImageA(hInstance,resname,IMAGE_ICON,16,16,0);
+ sid.pszName :=ilname;
+ sid.szDescription.a:=descr;
+ result:=Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+end;
+
+end.
diff --git a/plugins/Utils.pas/msninfo.pas b/plugins/Utils.pas/msninfo.pas
new file mode 100644
index 0000000000..05fef4959b
--- /dev/null
+++ b/plugins/Utils.pas/msninfo.pas
@@ -0,0 +1,182 @@
+unit MSNInfo;
+
+interface
+
+type
+ pMSNInfo = ^tMSNInfo;
+ tMSNInfo = record
+ msnPlayer:pWideChar;
+ msnArtist:pWideChar;
+ msnTitle :pWideChar;
+ msnAlbum :pWideChar;
+ end;
+
+
+procedure StartMSNHook;
+procedure StopMSNHook;
+
+function GetMSNInfo:pMSNInfo;
+
+implementation
+
+uses windows, messages, common;
+
+const
+ HWND_MESSAGE = HWND(-3);
+
+const
+ MSNClassName = 'MsnMsgrUIManager';
+const
+ hMSNWindow:THANDLE = 0;
+
+const
+ RealMSNData:PWideChar = nil;
+ anMSNInfo:tMSNInfo =(
+ msnPlayer:nil;
+ msnArtist:nil;
+ msnTitle :nil;
+ msnAlbum :nil
+ );
+
+function GetMSNInfo:pMSNInfo;
+begin
+ if ((anMSNInfo.msnPlayer=nil) or (anMSNInfo.msnPlayer^=#0)) and
+ ((anMSNInfo.msnArtist=nil) or (anMSNInfo.msnArtist^=#0)) and
+ ((anMSNInfo.msnTitle =nil) or (anMSNInfo.msnTitle ^=#0)) and
+ ((anMSNInfo.msnAlbum =nil) or (anMSNInfo.msnAlbum ^=#0)) then
+ result:=nil
+ else
+ result:=@anMSNInfo;
+end;
+
+procedure ClearMSNInfo;
+begin
+ if RealMSNData<>nil then
+ begin
+ mFreeMem(RealMSNData);
+ RealMSNData:=nil;
+ end;
+ FillChar(anMSNInfo,SizeOf(anMSNInfo),0);
+ {FreeMem(anMSNInfo.msnPlayer);} //anMSNInfo.msnPlayer:=nil;
+ {FreeMem(anMSNInfo.msnArtist);} //anMSNInfo.msnArtist:=nil;
+ {FreeMem(anMSNInfo.msnTitle); } //anMSNInfo.msnTitle :=nil;
+ {FreeMem(anMSNInfo.msnAlbum); } //anMSNInfo.msnAlbum :=nil;
+end;
+
+procedure Split(pc:pWideChar);
+var
+ lpc:pWideChar;
+begin
+ // Player
+ anMSNInfo.msnPlayer:=pc;
+ while (pc^<>'\') or ((pc+1)^<>'0') do inc(pc);
+ pc^:=#0;
+ inc(pc,2);
+
+ // Type
+ lpc:=pc;
+
+ while (pc^<>'\') or ((pc+1)^<>'0') do inc(pc);
+ pc^:=#0;
+ inc(pc,2);
+
+ if StrCmpW(lpc,'Music')<>0 then
+ begin
+ anMSNInfo.msnPlayer:=nil;
+ exit;
+ end;
+
+ // Status
+ lpc:=pc;
+ if lpc^='0' then // stop track
+ begin
+ anMSNInfo.msnPlayer:=nil;
+ exit;
+ end;
+
+ while (pc^<>'\') or ((pc+1)^<>'0') do inc(pc);
+ pc^:=#0;
+ inc(pc,2);
+
+ // Format - just skip
+ while (pc^<>'\') or ((pc+1)^<>'0') do inc(pc);
+ pc^:=#0;
+ inc(pc,2);
+
+ // Artist
+ anMSNInfo.msnArtist:=pc;
+
+ while (pc^<>'\') or ((pc+1)^<>'0') do inc(pc);
+ pc^:=#0;
+ inc(pc,2);
+
+ // Title
+ anMSNInfo.msnTitle:=pc;
+
+ while (pc^<>'\') or ((pc+1)^<>'0') do inc(pc);
+ pc^:=#0;
+ inc(pc,2);
+
+ // Album
+ anMSNInfo.msnAlbum:=pc;
+
+ while (pc^<>'\') or ((pc+1)^<>'0') do inc(pc);
+ pc^:=#0;
+
+ // WMContentID - not needs
+end;
+
+function dlgMSNHook(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+var
+ pMyCDS:PCOPYDATASTRUCT;
+begin
+ result:=0;
+ case hMessage of
+ WM_COPYDATA: begin
+ pMyCDS:=PCOPYDATASTRUCT(lParam);
+ if pMyCDS^.dwData=1351 then // Media player info
+ begin
+ ClearMSNInfo;
+ Split(StrDupW(RealMSNData,pWideChar(pMyCDS^.lpData)));
+ end;
+ end;
+ else
+ result:=DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+end;
+
+procedure StartMSNhook;
+var
+ msnClass:TWNDCLASSW;
+ hMSNClass:THANDLE;
+begin
+ FillChar(msnClass,SizeOf(TWNDCLASS),0);
+
+ msnClass.hInstance :=hInstance;
+ msnClass.lpszClassName:=MSNClassName;
+ msnClass.lpfnWndProc :=@dlgMSNHook;
+ hMSNClass:=RegisterClassW(msnClass);
+
+ if (hMSNClass<>0) and (hMSNWindow=0) then
+ begin
+ hMSNWindow:=CreateWindowExW(0,PWideChar(hMSNClass),nil,0,1,1,1,1,HWND_MESSAGE,0,hInstance,nil);
+ end;
+ ClearMSNInfo;
+end;
+
+procedure StopMSNHook;
+begin
+ if hMSNWindow<>0 then
+ begin
+ DestroyWindow(hMSNWindow);
+ hMSNWindow:=0;
+
+ ClearMSNInfo;
+
+ UnRegisterClass(MSNClassName,hInstance);
+ end;
+end;
+
+//finalization
+// StopMSNHook;
+end.
diff --git a/plugins/Utils.pas/old/hotkeys.pas b/plugins/Utils.pas/old/hotkeys.pas
new file mode 100644
index 0000000000..32f6e201e5
--- /dev/null
+++ b/plugins/Utils.pas/old/hotkeys.pas
@@ -0,0 +1,574 @@
+{Hotkey and timer related functions}
+unit hotkeys;
+
+interface
+
+uses windows;
+
+type
+ AWKHotKeyProc = function(hotkey:integer):integer;
+
+function AddProc(aproc:AWKHotKeyProc;ahotkey:integer;global:bool=false):integer; overload;
+function AddProc(ahotkey:integer;wnd:HWND;aproc:AWKHotKeyProc ):integer; overload;
+function AddProc(ahotkey:integer;wnd:HWND;msg:uint_ptr ):integer; overload;
+function DelProc(hotkey:integer ):integer; overload;
+function DelProc(hotkey:integer;wnd:HWND):integer; overload;
+
+procedure InitHotKeys;
+procedure FreeHotKeys;
+
+implementation
+
+uses messages;
+
+const
+ HWND_MESSAGE = HWND(-3);
+
+var
+ CurThread:THANDLE;
+
+type
+ PKBDLLHOOKSTRUCT = ^TKBDLLHOOKSTRUCT;
+ TKBDLLHOOKSTRUCT = record
+ vkCode :dword;
+ scanCode :dword;
+ flags :dword;
+ time :dword;
+ dwExtraInfo:dword;
+ end;
+
+const
+ WH_KEYBOARD_LL = 13;
+ WM_MYMESSAGE = WM_USER +13;
+
+// const from commctrl module;
+const
+ HOTKEYF_SHIFT = $01;
+ HOTKEYF_CONTROL = $02;
+ HOTKEYF_ALT = $04;
+ HOTKEYF_EXT = $08;
+
+const
+ hkAssigned = 1;
+ hkGlobal = 2;
+ hkMessage = 4;
+const
+ kbHook:THANDLE=0;
+ hiddenwindow:HWND=0;
+ modifiers:dword=0;
+const
+ PageStep = 10;
+type
+ PHKRec = ^THKRec;
+ THKRec = record
+ proc :AWKHotKeyProc; // procedure
+ flags :integer; // options
+ handle:THANDLE; // thread or window?
+ atom :TATOM; // hotkey id
+ hotkey:integer; // hotkey
+ end;
+ PHKRecs = ^THKRecs;
+ THKRecs = array [0..15] of THKRec;
+
+const
+ NumRecs:integer=0;
+ MaxRecs:integer=10;
+ hkRecs:pHKRecs=nil;
+
+//----- simpler version of 'common' function -----
+
+const
+ HexDigitChr: array [0..15] of AnsiChar = ('0','1','2','3','4','5','6','7',
+ '8','9','A','B','C','D','E','F');
+
+function IntToHex(dst:PAnsiChar;Value:cardinal):PAnsiChar;
+var
+ Digits:integer;
+begin
+ dst[8]:=#0;
+ Digits:=8;
+ repeat
+ Dec(Digits);
+ dst[Digits]:=HexDigitChr[Value and $F];
+ Value:=Value shr 4;
+ until Digits=0;
+ result:=dst;
+end;
+
+//----- utils -----
+
+function GetAtom(hotkey:dword):dword;
+const
+ HKPrefix = 'awk_';
+var
+ p:array [0..15] of AnsiChar;
+begin
+ lstrcpya(p,HKPrefix);
+ IntToHex(p+Length(HKPrefix),hotkey);
+ result:=GlobalAddAtomA(p);
+end;
+
+function HotKeyDlgToHook(w:cardinal):cardinal; register;
+asm
+ movzx ecx,al
+ xor al,al
+ test ah,HOTKEYF_ALT
+ je @L1
+ or al,MOD_ALT
+@L1:
+ test ah,HOTKEYF_CONTROL
+ je @L2
+ or al,MOD_CONTROL
+@L2:
+ test ah,HOTKEYF_SHIFT
+ je @L3
+ or al,MOD_SHIFT
+@L3:
+ test ah,HOTKEYF_EXT
+ je @L4
+ or al,MOD_WIN
+@L4:
+ mov ch,al
+ mov eax,ecx
+{
+begin
+ result:=w and $FF;
+ if (w and (HOTKEYF_ALT shl 8))<>0 then result:=result or (MOD_ALT shl 8);
+ if (w and (HOTKEYF_CONTROL shl 8))<>0 then result:=result or (MOD_CONTROL shl 8);
+ if (w and (HOTKEYF_SHIFT shl 8))<>0 then result:=result or (MOD_SHIFT shl 8);
+ if (w and (HOTKEYF_EXT shl 8))<>0 then result:=result or (MOD_WIN shl 8);
+}
+end;
+
+function HotKeyHookToDlg(w:cardinal):cardinal; register;
+asm
+ movzx ecx,al
+ xor al,al
+ test ah,MOD_ALT
+ je @L1
+ or al,HOTKEYF_ALT
+@L1:
+ test ah,MOD_CONTROL
+ je @L2
+ or al,HOTKEYF_CONTROL
+@L2:
+ test ah,MOD_SHIFT
+ je @L3
+ or al,HOTKEYF_SHIFT
+@L3:
+ test ah,MOD_WIN
+ je @L4
+ or al,HOTKEYF_EXT
+@L4:
+ mov ch,al
+ mov eax,ecx
+{
+begin
+ result:=w and $FF;
+ if (w and (MOD_ALT shl 8))<>0 then result:=result or (HOTKEYF_ALT shl 8);
+ if (w and (MOD_CONTROL shl 8))<>0 then result:=result or (HOTKEYF_CONTROL shl 8);
+ if (w and (MOD_SHIFT shl 8))<>0 then result:=result or (HOTKEYF_SHIFT shl 8);
+ if (w and (MOD_WIN shl 8))<>0 then result:=result or (HOTKEYF_EXT shl 8);
+}
+end;
+
+//----- Hook -----
+
+function FindHotkey(keycode:integer;local:boolean):pointer;
+var
+ i:integer;
+ p:pHKRec;
+begin
+ i:=NumRecs;
+ p:=pointer(HKRecs);
+ while i>0 do
+ begin
+ dec(i);
+ with p^ do
+ begin
+ if (flags and hkAssigned)<>0 then
+ begin
+ if (local xor ((flags and hkGlobal)<>0)) then
+ begin
+ if hotkey=keycode then
+ begin
+ if handle<>0 then
+ begin
+ if GetFocus=handle then
+ begin
+ if (flags and hkMessage)<>0 then
+ begin
+ PostMessage(handle,wparam(@proc),keycode,0);
+ result:=pointer(-1);
+ end
+ else
+ result:=@proc;
+ exit;
+ end;
+ end
+ else
+ begin
+ result:=@proc;
+ exit;
+ end;
+ end;
+ end;
+ end;
+ end;
+ inc(p);
+ end;
+ result:=nil;
+end;
+
+function wmKeyboard_hook(code:integer;wParam:WPARAM;lParam:LPARAM):longint; stdcall;
+var
+ key:dword;
+ proc:pointer;
+begin
+ if (code=HC_ACTION) and
+ (lParam>0) and (LoWord(lParam)=1) then
+ begin
+ key:=0;
+ if (GetKeyState(VK_SHIFT ) and $8000)<>0 then key:=key or (MOD_SHIFT shl 8);
+ if (GetKeyState(VK_CONTROL) and $8000)<>0 then key:=key or (MOD_CONTROL shl 8);
+ if (GetKeyState(VK_MENU ) and $8000)<>0 then key:=key or (MOD_ALT shl 8);
+ if (GetKeyState(VK_LWIN ) and $8000)<>0 then key:=key or (MOD_WIN shl 8);
+ if (GetKeyState(VK_RWIN ) and $8000)<>0 then key:=key or (MOD_WIN shl 8);
+// if (GetKeyState(VK_APPS) and $8000)<>0 then
+// if (GetKeyState(VK_SLEEP) and $8000)<>0 then
+ key:=key or (cardinal(wParam) and $FF);
+ proc:=FindHotkey(key,true);
+ if proc<>nil then
+ begin
+ if proc<>pointer(-1) then
+ PostMessageA(hiddenwindow,WM_MYMESSAGE,key,windows.lparam(proc));
+ result:=1;
+ exit;
+ end;
+ end;
+ result:=CallNextHookEx(KbHook,code,wParam,lParam);
+end;
+
+function wmKeyboardLL_hook(code:integer;wParam:WPARAM;lParam:LPARAM):integer; stdcall;
+const
+ lastkey:dword=0;
+var
+ mask:dword;
+ key:dword;
+ proc:pointer;
+begin
+ if code=HC_ACTION then
+ begin
+ case PKBDLLHOOKSTRUCT(lParam)^.vkCode of
+ VK_MENU,
+ VK_LMENU,
+ VK_RMENU: mask:=MOD_ALT shl 8;
+ VK_LWIN,
+ VK_RWIN: mask:=MOD_WIN shl 8;
+ VK_SHIFT,
+ VK_LSHIFT,
+ VK_RSHIFT: mask:=MOD_SHIFT shl 8;
+ VK_CONTROL,
+ VK_LCONTROL,
+ VK_RCONTROL: mask:=MOD_CONTROL shl 8;
+ else
+ if (PKBDLLHOOKSTRUCT(lParam)^.flags and 128)=0 then
+ begin
+ // local only
+// maybe process will better choice?
+ if //(lastkey=0) and
+ (CurThread=GetWindowThreadProcessId(GetForegroundWindow,nil)) then
+ begin
+ key:=PKBDLLHOOKSTRUCT(lParam)^.vkCode or modifiers;
+ proc:=FindHotkey(key,true);
+ if proc<>nil then
+ begin
+ lastkey:=PKBDLLHOOKSTRUCT(lParam)^.vkCode;
+ if proc<>pointer(-1) then
+ PostMessageA(hiddenwindow,WM_MYMESSAGE,key,windows.lparam(proc));
+ result:=1;
+ exit;
+ end;
+ end;
+ end
+ else if (lastkey<>0) and (lastkey=PKBDLLHOOKSTRUCT(lParam)^.vkCode) then
+ begin
+ lastkey:=0;
+ result :=1;
+ exit;
+ end;
+ mask:=0;
+ end;
+ if mask<>0 then
+ begin
+ if (PKBDLLHOOKSTRUCT(lParam)^.flags and 128)=0 then
+ modifiers:=modifiers or mask
+ else
+ modifiers:=modifiers and not mask;
+ end
+ end;
+ result:=CallNextHookEx(KbHook,code,wParam,lParam);
+end;
+
+function HiddenWindProc(wnd:HWnd;msg:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+var
+ key:dword;
+begin
+ if Msg=WM_HOTKEY then
+ begin
+ key:=(lParam shr 16)+((lParam and $FF) shl 8);
+ result:=lresult(FindHotKey(key,false));
+ if result<>0 then
+ begin
+ result:=AWKHotKeyProc(result)(HotkeyHookToDlg(key));
+ exit;
+ end;
+ end
+ else if Msg=WM_MYMESSAGE then
+ begin
+ result:=AWKHotKeyProc(lParam)(HotkeyHookToDlg(wParam));
+ exit;
+ end;
+ result:=DefWindowProcA(wnd,msg,wparam,lparam);
+end;
+
+procedure DestroyHiddenWindow;
+begin
+ if hiddenwindow<>0 then
+ begin
+ DestroyWindow(hiddenwindow);
+ hiddenwindow:=0;
+ end;
+end;
+
+procedure CreateHiddenWindow;
+var
+ wnd:HWND;
+begin
+ if hiddenwindow=0 then
+ begin
+ wnd:=CreateWindowExA(0,'STATIC',nil,0,
+ 1,1,1,1,HWND_MESSAGE,0,hInstance,nil);
+ if wnd<>0 then
+ begin
+ SetWindowLongPtrA(wnd,GWL_WNDPROC,LONG_PTR(@HiddenWindProc));
+ hiddenwindow:=wnd;
+ end
+ end
+end;
+//----- interface -----
+
+function CheckTable(ahotkey:integer;global:bool):integer;
+var
+ tmp:pHKRecs;
+ i:integer;
+ p:pHKRec;
+begin
+ if HKRecs=nil then
+ begin
+ MaxRecs:=PageStep;
+ GetMem (HKRecs ,MaxRecs*SizeOf(THKRec));
+ FillChar(HKRecs^,MaxRecs*SizeOf(THKRec),0);
+ NumRecs:=0;
+ end;
+ // search existing
+ i:=0;
+ p:=pointer(HKRecs);
+ while i<NumRecs do
+ begin
+ if (p^.flags and hkAssigned)<>0 then
+ begin
+ if (p^.hotkey=ahotkey) and
+ (((p^.flags and hkGlobal)<>0) xor not global) then
+ break;
+ end;
+ inc(p);
+ inc(i);
+ end;
+ //search empty
+ if i=NumRecs then
+ begin
+ i:=0;
+ p:=pointer(HKRecs);
+ while i<NumRecs do
+ begin
+ if (p^.flags and hkAssigned)=0 then
+ break;
+ inc(p);
+ inc(i);
+ end;
+ end;
+ if i=NumRecs then // allocate if not found
+ begin
+ if NumRecs=MaxRecs then
+ begin
+ inc(MaxRecs,PageStep);
+ GetMem (tmp ,MaxRecs*SizeOf(THKRec));
+ FillChar(tmp^,MaxRecs*SizeOf(THKRec),0);
+ move(HKRecs^,tmp^,NumRecs*SizeOf(THKRec));
+ FreeMem(HKRecs);
+ HKRecs:=tmp;
+ end;
+ inc(NumRecs);
+ end;
+ if global then
+ HKRecs^[i].flags:=hkAssigned or hkGlobal
+ else
+ HKRecs^[i].flags:=hkAssigned;
+ HKRecs^[i].hotkey:=HotKeyDlgToHook(ahotkey);
+ result:=i;
+end;
+
+function AddProc(aproc:AWKHotKeyProc;ahotkey:integer;global:bool=false):integer;
+begin
+ result:=1;
+ if @aproc=nil then exit;
+
+ with HKRecs^[CheckTable(ahotkey,global)] do
+ begin
+ proc :=aproc;
+ handle:=0;
+ if global then
+ begin
+ atom:=GetAtom(hotkey);
+ if not RegisterHotKey(hiddenwindow,atom,((hotkey and $FF00) shr 8),(hotkey and $FF)) then
+ result:=0;
+ end;
+ end;
+end;
+
+// search needed
+function AddProcWin(ahotkey:integer;wnd:HWND):integer;
+begin
+ result:=CheckTable(ahotkey,false);
+ with HKRecs^[result] do
+ begin
+ handle:=wnd;
+ end;
+end;
+
+function AddProc(ahotkey:integer;wnd:HWND;aproc:AWKHotKeyProc):integer;
+begin
+ if @aproc=nil then
+ begin
+ result:=0;
+ exit;
+ end;
+
+ result:=AddProcWin(ahotkey,wnd);
+ if result<0 then
+ result:=0
+ else
+ begin
+ HKRecs^[result].proc:=@aproc;
+ end;
+end;
+
+function AddProc(ahotkey:integer;wnd:HWND;msg:uint_ptr):integer;
+begin
+ result:=AddProcWin(ahotkey,wnd);
+ if result<0 then
+ result:=0
+ else
+ begin
+ HKRecs^[result].flags:=HKRecs^[result].flags or hkMessage;
+ HKRecs^[result].proc:=pointer(msg);
+ end;
+end;
+
+function DelProc(hotkey:integer):integer;
+var
+ i:integer;
+ p:pHKRec;
+begin
+ hotkey:=HotKeyDlgToHook(hotkey); //!!
+ p:=pointer(HKRecs);
+ i:=NumRecs;
+ while i>0 do
+ begin
+ dec(i);
+ if ((p^.flags and hkAssigned)<>0) and (p^.handle=0) then
+ if p^.hotkey=hotkey then
+ begin
+ if (p^.flags and hkGlobal)<>0 then
+ begin
+ UnregisterHotKey(hiddenwindow,p^.atom);
+ GlobalDeleteAtom(p^.atom);
+ end;
+ p^.flags:=p^.flags and not hkAssigned;
+ result:=i;
+ exit;
+ end;
+ inc(p);
+ end;
+ result:=0;
+end;
+
+function DelProc(hotkey:integer;wnd:HWND):integer;
+var
+ i:integer;
+ p:pHKRec;
+begin
+ hotkey:=HotKeyDlgToHook(hotkey); //!!
+ p:=pointer(HKRecs);
+ i:=NumRecs;
+ while i>0 do
+ begin
+ dec(i);
+ if (p^.flags and hkAssigned)<>0 then
+ if (p^.handle=wnd) {and ((p^.flags and hkGlobal)=0)} then
+ begin
+ if (hotkey=0) or (hotkey=p^.hotkey) then
+ begin
+ p^.flags:=p^.flags and not hkAssigned;
+ result:=i;
+ exit;
+ end;
+ end;
+ inc(p);
+ end;
+ result:=0;
+end;
+
+procedure InitHotKeys;
+begin
+ MaxRecs:=10;
+ GetMem(HKRecs,SizeOf(THKRec)*MaxRecs);
+ FillChar(HKRecs^,SizeOf(THKRec)*MaxRecs,0);
+ NumRecs:=0;
+ CreateHiddenWindow;
+ kbhook:=SetWindowsHookExA(WH_KEYBOARD_LL,@wmKeyboardLL_hook,hInstance,0);
+
+ if KbHook=0 then
+ KbHook:=SetWindowsHookExA(WH_KEYBOARD,@wmKeyboard_hook,0,GetCurrentThreadId);
+end;
+
+procedure FreeHotKeys;
+var
+ i:integer;
+ p:pHKRec;
+begin
+ i:=NumRecs;
+ p:=pointer(HKRecs);
+ while i>0 do
+ begin
+ dec(i);
+ if (p^.flags and (hkAssigned or hkGlobal))=(hkAssigned or hkGlobal) then
+ begin
+ UnregisterHotKey(hiddenwindow,p^.atom);
+ GlobalDeleteAtom(p^.atom);
+ end;
+ inc(p);
+ end;
+ DestroyHiddenWindow;
+ if kbhook<>0 then
+ UnhookWindowsHookEx(kbhook);
+ FreeMem(HKRecs);
+ HKRecs:=nil;
+ MaxRecs:=0;
+ NumRecs:=0;
+end;
+
+initialization
+ CurThread:=GetCurrentThreadId();
+end. \ No newline at end of file
diff --git a/plugins/Utils.pas/old/ini.pas b/plugins/Utils.pas/old/ini.pas
new file mode 100644
index 0000000000..8746b51c53
--- /dev/null
+++ b/plugins/Utils.pas/old/ini.pas
@@ -0,0 +1,857 @@
+unit INI;
+
+interface
+
+uses windows;
+
+{+}function SetStorage(name:PAnsiChar;inINI:boolean):cardinal;
+{+}procedure FreeStorage(aHandle:cardinal);
+
+{+}procedure SetDefaultSection(aHandle:cardinal;name:PAnsiChar);
+{+}procedure SetCurrentSection(aHandle:cardinal;sect:PAnsiChar);
+
+{+}procedure FlushSettings(aHandle:cardinal);
+{+}procedure FlushSection(aHandle:cardinal);
+
+{+}procedure WriteNCInt(aHandle:cardinal;sect:PAnsiChar;param:PAnsiChar;value:integer);
+{+}procedure WriteNCStr(aHandle:cardinal;sect:PAnsiChar;param:PAnsiChar;value:PAnsiChar);
+
+{+}procedure WriteNCStruct(aHandle:cardinal;sect:PAnsiChar;param:PAnsiChar;ptr:pointer;size:integer);
+{*}procedure WriteStruct(aHandle:cardinal;param:PAnsiChar;ptr:pointer;size:integer);
+{+}function ReadStruct (aHandle:cardinal;param:PAnsiChar;ptr:pointer;size:integer):boolean;
+
+{+}procedure WriteFlag(aHandle:cardinal;param:PAnsiChar;value:integer);
+{+}procedure WriteInt (aHandle:cardinal;param:PAnsiChar;value:integer);
+{+}procedure WriteStr (aHandle:cardinal;param:PAnsiChar;value:PWideChar);
+procedure WriteAnsiStr(aHandle:cardinal;param:PAnsiChar;value:PAnsiChar);
+{+}function ReadFlag(aHandle:cardinal;param:PAnsiChar; default:integer):integer;
+{+}function ReadInt (aHandle:cardinal;param:PAnsiChar; default:integer):integer;
+procedure ReadStr (aHandle:cardinal;var dst:PWideChar;param:PAnsiChar;default:PWideChar);
+procedure ReadAnsiStr(aHandle:cardinal;var dst:PAnsiChar;param:PAnsiChar;default:PAnsiChar);
+
+procedure WriteSect(aHandle:cardinal;src:PAnsiChar);
+procedure ReadSect (aHandle:cardinal;var dst:PAnsiChar);
+
+{*}procedure ClearSection(aHandle:cardinal);
+{+}procedure DeleteParam(aHandle:cardinal;param:PAnsiChar);
+
+implementation
+
+uses common,io,m_api,dbsettings;
+
+type
+ PStorage = ^TStorage;
+ TStorage = record
+ SName :PAnsiChar;
+ SType :bool;
+ SHandle :THANDLE;
+ DefSection:PAnsiChar;
+ Section :Array [0..127] of AnsiChar;
+ ParOffset :integer;
+ Buffer :PAnsiChar;
+ INIBuffer :PAnsiChar;
+ end;
+ PStHeap = ^TStHeap;
+ TStHeap = array [0..10] of TStorage;
+
+const
+ Storage:PStHeap=nil;
+ NumStorage:cardinal=0;
+
+type
+ pbrec=^brec;
+ brec=record
+ ptr:PAnsiChar;
+ handle:cardinal;
+ end;
+
+const
+ DefDefSection:PAnsiChar = 'default';
+
+{+}function SetStorage(name:PAnsiChar;inINI:boolean):cardinal;
+var
+ i:integer;
+ tmp:PStHeap;
+begin
+ if Storage=nil then
+ begin
+ mGetMem(Storage,SizeOf(TStorage));
+ FillChar(Storage^,SizeOf(TStorage),0);
+ NumStorage:=1;
+ result:=0;
+ end
+ else
+ begin
+ integer(result):=-1;
+ for i:=0 to NumStorage-1 do
+ begin
+ if Storage^[i].SName=nil then // free cell
+ begin
+ result:=i;
+ break;
+ end;
+ end;
+ if integer(result)<0 then
+ begin
+ mGetMem(tmp,SizeOf(TStorage)*(NumStorage+1));
+ move(Storage^,tmp^,SizeOf(TStorage)*NumStorage);
+ mFreeMem(Storage);
+ Storage:=tmp;
+ FillChar(Storage^[NumStorage],SizeOf(TStorage),0);
+ result:=NumStorage;
+ inc(NumStorage);
+ end
+ end;
+ with Storage^[result] do
+ begin
+ StrDup(SName,name);
+ SType:=inINI;
+ end;
+end;
+
+{+}procedure FreeStorage(aHandle:cardinal);
+begin
+ if aHandle>=NumStorage then
+ exit;
+ with Storage^[aHandle] do
+ begin
+ mFreeMem(SName);
+ mFreeMem(DefSection);
+ mFreeMem(Buffer);
+ mFreeMem(INIBuffer);
+ end;
+end;
+
+{+}procedure WriteNCStruct(aHandle:cardinal;sect:PAnsiChar;param:PAnsiChar;ptr:pointer;size:integer);
+var
+ cws:TDBCONTACTWRITESETTING;
+ pn:array [0..127] of AnsiChar;
+ i:integer;
+begin
+ if aHandle>=NumStorage then
+ exit;
+ with Storage^[aHandle] do
+ if SType then
+ begin
+ if (sect=nil) or (sect^=#0) then
+ sect:=DefSection;
+ if sect=nil then
+ sect:=DefDefSection;
+ WritePrivateProfileStructA(sect,param,ptr,size,SName);
+ end
+ else
+ begin
+ if (sect<>nil) and (sect^<>#0) then
+ begin
+ i:=StrLen(sect);
+ move(sect^,pn,i);
+ pn[i]:='/';
+ inc(i);
+ end
+ else
+ i:=0;
+ StrCopy(pn+i,param);
+ cws.szModule :=SName;
+ cws.szSetting :=pn;
+ cws.value._type :=DBVT_BLOB;
+ cws.value.pbVal :=ptr;
+ cws.value.cpbVal:=size;
+ PluginLink^.CallService(MS_DB_CONTACT_WRITESETTING,0,lParam(@cws));
+ end
+end;
+
+{*}procedure WriteStruct(aHandle:cardinal;param:PAnsiChar;ptr:pointer;size:integer);
+const
+ hex:array [0..15] of AnsiChar = '0123456789ABCDEF';
+var
+ lptr:PAnsiChar;
+ buf,buf1:PAnsiChar;
+ i:integer;
+ crc:integer;
+ cws:TDBCONTACTWRITESETTING;
+begin
+ if aHandle>=NumStorage then
+ exit;
+ with Storage^[aHandle] do
+ if SType then
+ begin
+ mGetMem(buf,(size+1)*2);
+ crc:=0;
+ buf1:=buf;
+ for i:=0 to size-1 do
+ begin
+ inc(crc,PByte(ptr)^);
+ buf1^ :=hex[pbyte(ptr)^ shr 4];
+ (buf1+1)^:=hex[pbyte(ptr)^ and $0F];
+ inc(buf1,2);
+ inc(pbyte(ptr));
+ end;
+ buf1^ :=hex[(crc and $FF) shr 4];
+ (buf1+1)^:=hex[(crc and $0F)];
+
+ StrCat(Buffer,param);
+ lptr:=StrEnd(Buffer);
+ lptr^:='=';
+ inc(lptr);
+ move(buf^,lptr^,(size+1)*2);
+ mFreeMem(buf);
+ inc(lptr,(size+1)*2);
+ lptr^ :=#13;
+ (lptr+1)^:=#10;
+ (lptr+2)^:=#0;
+ end
+ else
+ begin
+ StrCopy(Section+ParOffset,param);
+ cws.szModule :=SName;
+ cws.szSetting :=Section;
+ cws.value._type :=DBVT_BLOB;
+ cws.value.pbVal :=ptr;
+ cws.value.cpbVal:=size;
+ PluginLink^.CallService(MS_DB_CONTACT_WRITESETTING,0,lParam(@cws));
+ end
+end;
+
+{+}function ReadStruct(aHandle:cardinal;param:PAnsiChar;ptr:pointer;size:integer):boolean;
+var
+ dbv:TDBVariant;
+begin
+ if aHandle>=NumStorage then
+ begin
+ result:=false;
+ exit;
+ end;
+ with Storage^[aHandle] do
+ if SType then
+ begin
+ result:=GetPrivateProfileStructA(Section,param,ptr,size,SName);
+ end
+ else
+ begin
+ dbv._type:=DBVT_BLOB;
+ dbv.pbVal:=nil;
+ StrCopy(Section+ParOffset,param);
+ if (DBReadSetting(0,SName,Section,@dbv)=0) and
+ (dbv.pbVal<>nil) and (dbv.cpbVal=size) then
+ begin
+ move(dbv.pbVal^,ptr^,size);
+ DBFreeVariant(@dbv);
+ result:=true;
+ end
+ else
+ result:=false;
+ end
+end;
+
+{+}procedure WriteNCInt(aHandle:cardinal;sect:PAnsiChar;param:PAnsiChar;value:integer);
+var
+ pn:array [0..127] of AnsiChar;
+ i:integer;
+begin
+ if aHandle>=NumStorage then
+ exit;
+ with Storage^[aHandle] do
+ if Stype then
+ begin
+ if (sect=nil) or (sect^=#0) then
+ sect:=DefSection;
+ if sect=nil then
+ sect:=DefDefSection;
+ WritePrivateProfileStringA(sect,param,IntToStr(pn,value),SName);
+ end
+ else
+ begin
+ if (sect<>nil) and (sect^<>#0) then
+ begin
+ i:=StrLen(sect);
+ move(sect^,pn,i);
+ pn[i]:='/';
+ inc(i);
+ end
+ else
+ i:=0;
+ StrCopy(pn+i,param);
+ DBWriteDWord(0,SName,pn,value)
+ end
+end;
+
+{+}procedure WriteNCStr(aHandle:cardinal;sect:PAnsiChar;param:PAnsiChar;value:PAnsiChar);
+var
+ pn:array [0..127] of AnsiChar;
+ i:integer;
+begin
+ if aHandle>=NumStorage then
+ exit;
+ with Storage^[aHandle] do
+ if SType then
+ begin
+ if (sect=nil) or (sect^=#0) then
+ sect:=DefSection;
+ if sect=nil then
+ sect:=DefDefSection;
+ WritePrivateProfileStringA(sect,param,value,SName);
+ end
+ else
+ begin
+ if (sect<>nil) and (sect^<>#0) then
+ begin
+ i:=StrLen(sect);
+ move(sect^,pn,i);
+ pn[i]:='/';
+ inc(i);
+ end
+ else
+ i:=0;
+ StrCopy(pn+i,param);
+ DBWriteString(0,SName,pn,value);
+ end
+end;
+
+{+}procedure SetDefaultSection(aHandle:cardinal;name:PAnsiChar);
+begin
+ if aHandle>=NumStorage then
+ exit;
+ with Storage^[aHandle] do
+ begin
+ mFreeMem(DefSection);
+ StrDup(DefSection,name);
+ end;
+end;
+
+{+}procedure SetCurrentSection(aHandle:cardinal;sect:PAnsiChar);
+begin
+ if aHandle>=NumStorage then
+ exit;
+ with Storage^[aHandle] do
+ begin
+ if sect=nil then
+ sect:=DefSection;
+ if sect=nil then
+ sect:='';
+ if SType then
+ begin
+ if (sect=nil) or (sect^=#0) then
+ sect:=DefDefSection;
+ StrCopy(Section,sect);
+ mGetMem(Buffer,16384);
+ Buffer^ :=#13;
+ (Buffer+1)^:=#10;
+ (Buffer+2)^:=#0;
+ end
+ else
+ begin
+ if sect<>nil then
+ begin
+ StrCopy(Section,sect);
+ ParOffset:=StrLen(Section);
+ Section[ParOffset]:='/';
+ inc(ParOffset);
+ end
+ else
+ ParOffset:=0;
+ end
+ end;
+end;
+
+{+}procedure FlushSettings(aHandle:cardinal);
+var
+ size:integer;
+ ptr:PAnsiChar;
+begin
+ if aHandle>=NumStorage then
+ exit;
+ with Storage^[aHandle] do
+ begin
+ if SType then
+ begin
+ if INIBuffer=nil then
+ exit;
+ ptr:=INIBuffer+1;
+ size:=StrLen(ptr);
+ seek(SHandle,0);
+ BlockWrite(SHandle,ptr^,size);
+ SetEndOfFile(SHandle);
+ mFreeMem(INIBuffer);
+ CloseHandle(SHandle);
+ end;
+ end;
+end;
+
+{+}procedure FlushSection(aHandle:cardinal);
+var
+ size,i:integer;
+ sect:array [0..127] of AnsiChar;
+ ptr1,ptr:PAnsiChar;
+begin
+ if aHandle>=NumStorage then
+ exit;
+ with Storage^[aHandle] do
+ if SType then
+ begin
+ if INIBuffer=nil then
+ begin
+ mGetMem(INIBuffer,32768);
+ INIBuffer[0]:=#10;
+ SHandle:=Reset(SName);
+ if thandle(SHandle)=INVALID_HANDLE_VALUE then
+ INIBuffer[1]:=#0
+ else
+ begin
+ size:=FileSize(SHandle);
+ INIBuffer[size+1]:=#0;
+ BlockRead(SHandle,(INIBuffer+1)^,size);
+ CloseHandle(SHandle);
+ end;
+ SHandle:=ReWrite(SName);
+ end;
+ // construct section name
+ sect[0]:=#10;
+ sect[1]:='[';
+ size:=StrLen(Section);
+ move(Section,sect[2],size);
+ sect[size+2]:=']';
+ sect[size+3]:=#0;
+ // search section
+ ptr:=StrPos(INIBuffer,sect);
+ // delete section
+ if ptr<>nil then
+ begin
+ ptr1:=ptr;
+//!! inc(ptr);
+ while (ptr^<>#0) and ((ptr^<>#10) or ((ptr+1)^<>'[')) do inc(ptr);
+ if ptr^<>#0 then
+ StrCopy(ptr1,ptr+1)
+ else
+ ptr1^:=#0;
+ end;
+ // append section
+ if (Buffer<>nil) and (StrLen(Buffer)>0) then
+ begin
+ i:=StrLen(INIBuffer);
+ if INIBuffer[i-1]<>#10 then
+ begin
+ INIBuffer[i] :=#13;
+ INIBuffer[i+1]:=#10;
+ inc(i,2);
+ end;
+ StrCopy(INIBuffer+i,sect+1);
+ StrCat(INIBuffer,Buffer);
+ end;
+ mFreeMem(Buffer);
+ end;
+end;
+
+{+}procedure WriteFlag(aHandle:cardinal;param:PAnsiChar;value:integer);
+var
+ ptr:PAnsiChar;
+begin
+ if aHandle>=NumStorage then
+ exit;
+ with Storage^[aHandle] do
+ if SType then
+ begin
+ StrCat(Buffer,param);
+ ptr:=StrEnd(Buffer);
+ ptr^:='=';
+ (ptr+1)^:=CHR((value and 1)+ORD('0'));
+ inc(ptr,2);
+ ptr^ :=#13;
+ (ptr+1)^:=#10;
+ (ptr+2)^:=#0;
+ end
+ else
+ begin
+ StrCopy(Section+ParOffset,param);
+ DBWriteByte(0,SName,Section,value)
+ end;
+end;
+
+{+}procedure WriteInt(aHandle:cardinal;param:PAnsiChar;value:integer);
+var
+ ptr:PAnsiChar;
+begin
+ if aHandle>=NumStorage then
+ exit;
+ with Storage^[aHandle] do
+ if SType then
+ begin
+ StrCat(Buffer,param);
+ ptr:=StrEnd(Buffer);
+ ptr^:='=';
+ IntToStr(ptr+1,value);
+ ptr:=StrEnd(Buffer);
+ ptr^ :=#13;
+ (ptr+1)^:=#10;
+ (ptr+2)^:=#0;
+ end
+ else
+ begin
+ StrCopy(Section+ParOffset,param);
+ DBWriteDWord(0,SName,Section,value)
+ end;
+end;
+
+procedure WriteStrInt(aHandle:cardinal;param:PAnsiChar;value:pointer;wide:bool);
+var
+ buf:array [0..2047] of AnsiChar;
+ ptr:PAnsiChar;
+ lval:PAnsiChar;
+begin
+ if aHandle>=NumStorage then
+ exit;
+ with Storage^[aHandle] do
+ if SType then
+ begin
+ StrCat(Buffer,param);
+ ptr:=StrEnd(Buffer);
+ ptr^:='=';
+ inc(ptr);
+ if (value<>nil) then
+ begin
+ buf[0]:=#0;
+ if wide then
+ begin
+ if PWideChar(value)^<>#0 then
+ begin
+ WideToUTF8(value,lval);
+ StrCopy(buf,lval,SizeOf(buf)-1);
+ mFreeMem(lval);
+ end
+ end
+ else if PAnsiChar(value)^<>#0 then
+ StrCopy(buf,value,SizeOf(buf)-1);
+ if buf[0]<>#0 then
+ begin
+ Escape(buf);
+ StrCopy(ptr,buf);
+ ptr:=StrEnd(Buffer);
+ end;
+ end;
+ ptr^ :=#13;
+ (ptr+1)^:=#10;
+ (ptr+2)^:=#0;
+ end
+ else
+ begin
+ StrCopy(Section+ParOffset,param);
+ if wide then
+ DBWriteUnicode(0,SName,Section,value)
+ else
+ DBWriteString(0,SName,Section,value)
+ end;
+end;
+
+{+}procedure WriteStr(aHandle:cardinal;param:PAnsiChar;value:PWideChar);
+begin
+ WriteStrInt(aHandle,param,value,true);
+end;
+
+{+}procedure WriteAnsiStr(aHandle:cardinal;param:PAnsiChar;value:PAnsiChar);
+begin
+ WriteStrInt(aHandle,param,value,false);
+end;
+
+{+}function ReadFlag(aHandle:cardinal; param:PAnsiChar; default:integer):integer;
+begin
+ if aHandle>=NumStorage then
+ begin
+ result:=default;
+ exit;
+ end;
+ with Storage^[aHandle] do
+ if SType then
+ begin
+ result:=GetPrivateProfileIntA(Section,param,default,SName)
+ end
+ else
+ begin
+ StrCopy(Section+ParOffset,param);
+ result:=DBReadByte(0,SName,Section,default)
+ end;
+end;
+
+{+}function ReadInt(aHandle:cardinal; param:PAnsiChar; default:integer):integer;
+begin
+ if aHandle>=NumStorage then
+ begin
+ result:=default;
+ exit;
+ end;
+ with Storage^[aHandle] do
+ if SType then
+ begin
+ result:=GetPrivateProfileIntA(Section,param,default,SName)
+ end
+ else
+ begin
+ StrCopy(Section+ParOffset,param);
+ result:=DBReadDWord(0,SName,Section,default)
+ end;
+end;
+
+procedure ReadStrInt(aHandle:cardinal;var dst;param:PAnsiChar;default:pointer;wide:bool);
+
+ function EnumSettingsProc(const szSetting:PAnsiChar;lParam:LPARAM):int; cdecl;
+ var
+ p:pbrec;
+ i:integer;
+ begin
+ p:=pbrec(lparam);
+ if StrCmp(Storage^[p^.handle].Section,szSetting,Storage^[p^.handle].ParOffset)=0 then
+ begin
+ i:=StrLen(szSetting)+1;
+ move(szSetting^,p^.ptr^,i);
+ inc(p^.ptr,i);
+ end;
+ result:=0;
+ end;
+
+var
+ buf:array [0..4095] of AnsiChar;
+ p:brec;
+ ces:TDBCONTACTENUMSETTINGS;
+ def:PAnsiChar;
+ i:integer;
+begin
+ if aHandle>=NumStorage then
+ begin
+ if wide then
+ StrDupW(pWideChar(dst),pWideChar(default))
+ else
+ StrDup(PAnsiChar(dst),PAnsiChar(default));
+ exit;
+ end;
+ with Storage^[aHandle] do
+ if SType then
+ begin
+ if wide then
+ begin
+ if default=nil then
+ StrDup(def,'')
+ else
+ WideToUTF8(default,def);
+ end
+ else
+ begin
+ if default=nil then
+ def:=''
+ else
+ def:=default;
+ end;
+ i:=GetPrivateProfileStringA(Section,param,def,buf,4095,SName)+1;
+ mFreeMem(def);
+ if param<>nil then
+ begin
+ if buf[0]<>#0 then
+ begin
+ Unescape(buf);
+ if wide then
+ UTF8ToWide(buf,pWideChar(dst))
+ else
+ StrDup(PAnsiChar(dst),buf);
+ end
+ else
+ PAnsiChar(dst):=nil;
+ end
+ else //!! full section
+ begin
+ mGetMem(dst,i);
+ move(buf,PAnsiChar(dst)^,i);
+ buf[i-1]:=#0;
+ end;
+ end
+ else
+ begin
+ if param<>nil then
+ begin
+ StrCopy(Section+ParOffset,param);
+ if wide then
+ pWideChar(dst):=DBReadUnicode(0,SName,Section,pWideChar(default))
+ else
+ PAnsiChar(dst):=DBReadString(0,SName,Section,PAnsiChar(default));
+ end
+ else
+ begin
+ p.ptr:=@buf;
+ p.handle:=aHandle;
+ FillChar(buf,SizeOf(buf),0);
+ ces.pfnEnumProc:=@EnumSettingsProc;
+ ces.lParam :=lparam(@p);
+ ces.szModule :=SName;
+ ces.ofsSettings:=0;
+ PluginLink^.CallService(MS_DB_CONTACT_ENUMSETTINGS,0,lparam(@ces));
+ mGetMem(dst,p.ptr-PAnsiChar(@buf)+1);
+ move(buf,PAnsiChar(dst)^,p.ptr-PAnsiChar(@buf)+1);
+ end;
+ end;
+end;
+
+procedure ReadStr(aHandle:cardinal;var dst:PWideChar;param:PAnsiChar;default:PWideChar);
+begin
+ ReadStrInt(aHandle,dst,param,default,true);
+end;
+
+procedure ReadAnsiStr(aHandle:cardinal;var dst:PAnsiChar;param:PAnsiChar;default:PAnsiChar);
+begin
+ ReadStrInt(aHandle,dst,param,default,false);
+end;
+
+{*}procedure ClearSection(aHandle:cardinal);
+
+ function EnumSettingsProc(const szSetting:PAnsiChar;lParam:LPARAM):int; cdecl;
+ var
+ db:TDBCONTACTGETSETTING;
+ begin
+ with Storage^[lParam] do
+ begin
+ db.szModule:=SName;
+ StrCopy(Section+ParOffset,szSetting);
+ db.szSetting:=Section;
+ end;
+ PluginLink^.CallService(MS_DB_CONTACT_DELETESETTING,0,tlparam(@db));
+ result:=0;
+ end;
+
+var
+ ces:TDBCONTACTENUMSETTINGS;
+begin
+ if aHandle>=NumStorage then
+ exit;
+ with Storage^[aHandle] do
+ if SType then
+ WritePrivateProfileStringA(Section,nil,nil,SName)
+ else
+ begin
+ ces.pfnEnumProc:=@EnumSettingsProc;
+ ces.lParam :=aHandle;
+ ces.szModule :=SName;
+ ces.ofsSettings:=0;
+ PluginLink^.CallService(MS_DB_CONTACT_ENUMSETTINGS,0,lparam(@ces));
+ end;
+end;
+
+{*}procedure WriteSect(aHandle:cardinal;src:PAnsiChar);
+var
+ p:PAnsiChar;
+begin
+ if aHandle>=NumStorage then
+ exit;
+ with Storage^[aHandle] do
+ if SType then
+ WritePrivateProfileSectionA(Section,src,SName)
+ else
+ begin
+ ClearSection(aHandle);
+ while src^<>#0 do
+ begin
+ // write as strings
+ p:=src;
+ while src^<>'=' do inc(src);
+ inc(src);
+ DBWriteString(0,SName,p,src);
+ while src^<>#0 do inc(src);
+ inc(src);
+ end;
+ end;
+end;
+
+procedure ReadSect(aHandle:cardinal;var dst:PAnsiChar);
+
+ function EnumSettingsProc(const szSetting:PAnsiChar;lParam:LPARAM):int; cdecl;
+ var
+ dbv:TDBVariant;
+ i:integer;
+ p:pbrec;
+ buf:array [0..31] of AnsiChar;
+ begin
+ result:=1;
+
+ p:=pbrec(lparam);
+ if (DBReadSetting(0,Storage^[p^.handle].Section,szSetting,@dbv)=0) then
+ begin
+ i:=StrLen(szSetting);
+ move(szSetting^,p^.ptr^,i);
+ inc(p^.ptr,i);
+ p^.ptr^:='=';
+ case dbv._type of
+ DBVT_ASCIIZ: begin
+ if dbv.szVal.a<>nil then
+ begin
+ i:=StrLen(dbv.szVal.a)+1;
+ move(dbv.szVal.a^,(p^.ptr+1)^,i);
+ DBFreeVariant(@dbv);
+ end
+ end;
+ DBVT_BYTE,DBVT_WORD,DBVT_DWORD: begin
+ case dbv._type of
+ DBVT_BYTE : i:=dbv.bVal;
+ DBVT_WORD : i:=dbv.wVal;
+ DBVT_DWORD: i:=dbv.dVal;
+ end;
+ i:=StrLen(IntToStr(buf,i))+1;
+ move(buf,(p^.ptr+1)^,i);
+ end;
+ else
+ exit;
+ end;
+ inc(p^.ptr,i{+1});
+ end;
+ end;
+
+var
+ buf:array [0..16383] of AnsiChar;
+ p:brec;
+ ces:TDBCONTACTENUMSETTINGS;
+ i:integer;
+begin
+ if aHandle>=NumStorage then
+ begin
+ dst:=nil;
+ exit;
+ end;
+ with Storage^[aHandle] do
+ begin
+ if SType then
+ begin
+ i:=GetPrivateProfileSectionA(Section,buf,SizeOf(buf),SName)+1;
+ end
+ else
+ begin
+ p.ptr:=@buf;
+ p.handle:=aHandle;
+ FillChar(buf,SizeOf(buf),0);
+
+ ces.pfnEnumProc:=@EnumSettingsProc;
+ ces.lParam :=lparam(@p);
+ ces.szModule :=SName;
+ ces.ofsSettings:=0;
+ PluginLink^.CallService(MS_DB_CONTACT_ENUMSETTINGS,0,lparam(@ces));
+ i:=p.ptr-PAnsiChar(@buf)+1;
+ end;
+ mGetMem(dst,i);
+ move(buf,dst^,i);
+ buf[i-1]:=#0;
+ end;
+end;
+
+{+}procedure DeleteParam(aHandle:cardinal;param:PAnsiChar);
+var
+ db:TDBCONTACTGETSETTING;
+begin
+ if aHandle>=NumStorage then
+ exit;
+ with Storage^[aHandle] do
+ begin
+ if SType then
+ WritePrivateProfileStringA(Section,param,nil,SName)
+ else
+ begin
+ StrCopy(Section+ParOffset,param);
+ db.szModule :=SName;
+ db.szSetting:=Section;
+ PluginLink^.CallService(MS_DB_CONTACT_DELETESETTING,0,lparam(@db));
+ end;
+ end;
+end;
+
+end.
diff --git a/plugins/Utils.pas/old/mApiCardC.pas b/plugins/Utils.pas/old/mApiCardC.pas
new file mode 100644
index 0000000000..507bc79d79
--- /dev/null
+++ b/plugins/Utils.pas/old/mApiCardC.pas
@@ -0,0 +1,399 @@
+{service insertion code}
+unit mApiCardC;
+
+interface
+
+uses windows,messages;
+
+type
+ tmApiCard = class
+ private
+ function GetDescription:pAnsiChar;
+ function GetResultType :pAnsiChar;
+ procedure SetCurrentService(item:pAnsiChar);
+ public
+ constructor Create(fname:pAnsiChar; lparent:HWND=0);
+// procedure Free;
+ procedure FillList(combo:HWND; mode:integer=0);
+
+ function FillParams(wnd:HWND{;item:pAnsiChar};wparam:boolean):pAnsiChar;
+ procedure Show;//(item:pAnsiChar);
+
+ property Description:pAnsiChar read GetDescription;
+ property ResultType :pAnsiChar read GetResultType;
+ property Service :pAnsiChar write SetCurrentService;
+ property Event :pAnsiChar write SetCurrentService;
+ private
+ current: array [0..127] of AnsiChar;
+ IniFile: array [0..511] of AnsiChar;
+ parent,
+ HelpWindow:HWND;
+ isServiceHelp:boolean;
+
+ procedure Update(item:pAnsiChar=nil);
+ end;
+
+function CreateServiceCard(parent:HWND=0):tmApiCard;
+function CreateEventCard (parent:HWND=0):tmApiCard;
+
+implementation
+
+uses common,io,m_api,mirutils;
+
+{$r mApiCard.res}
+
+{$include i_card_const.inc}
+
+const
+ WM_UPDATEHELP = WM_USER+100;
+
+const
+ BufSize = 2048;
+
+const
+ ServiceHlpFile = 'plugins\services.ini';
+ EventsHlpFile = 'plugins\events.ini';
+{
+procedure tmApiCard.Free;
+begin
+end;
+}
+function tmApiCard.GetResultType:pAnsiChar;
+var
+ buf:array [0..2047] of AnsiChar;
+ p:pAnsiChar;
+begin
+ if INIFile[0]<>#0 then
+ begin
+ GetPrivateProfileStringA(@current,'return','',buf,SizeOf(buf),@INIFile);
+ p:=@buf;
+ while p^ in sWordOnly do inc(p);
+ p^:=#0;
+ StrDup(result,@buf);
+ end
+ else
+ result:=nil;
+end;
+
+function tmApiCard.GetDescription:pAnsiChar;
+var
+ buf:array [0..2047] of AnsiChar;
+begin
+ if INIFile[0]<>#0 then
+ begin
+ GetPrivateProfileStringA(@current,'descr','',buf,SizeOf(buf),@INIFile);
+ StrDup(result,@buf);
+ end
+ else
+ result:=nil;
+end;
+
+function tmApiCard.FillParams(wnd:HWND{;item:pAnsiChar};wparam:boolean):pAnsiChar;
+var
+ buf :array [0..2047] of AnsiChar;
+ bufw:array [0..2047] of WideChar;
+ j:integer;
+ p,pp,pc:PAnsiChar;
+ tmp:pWideChar;
+ paramname:pAnsiChar;
+begin
+ if INIFile[0]=#0 then
+ begin
+ result:=nil;
+ exit;
+ end;
+ if wparam then
+ paramname:='wparam'
+ else
+ paramname:='lparam';
+ GetPrivateProfileStringA(@current,paramname,'',buf,SizeOf(buf),@INIFile);
+ StrDup(result,@buf);
+ SendMessage(wnd,CB_RESETCONTENT,0,0);
+ if buf[0]<>#0 then
+ begin
+ p:=@buf;
+ GetMem(tmp,BufSize*SizeOf(WideChar));
+ repeat
+ pc:=StrScan(p,'|');
+ if pc<>nil then
+ pc^:=#0;
+
+ if (p^ in ['0'..'9']) or ((p^='-') and (p[1] in ['0'..'9'])) then
+ begin
+ j:=0;
+ pp:=p;
+ repeat
+ bufw[j]:=WideChar(pp^);
+ inc(j); inc(pp);
+ until (pp^=#0) or (pp^=' ');
+ if pp^<>#0 then
+ begin
+ bufw[j]:=' '; bufw[j+1]:='-'; bufw[j+2]:=' '; inc(j,3);
+ FastAnsitoWideBuf(pp+1,tmp);
+ StrCopyW(bufw+j,TranslateW(tmp));
+ SendMessageW(wnd,CB_ADDSTRING,0,lparam(@bufw));
+ end
+ else
+ SendMessageA(wnd,CB_ADDSTRING,0,lparam(p));
+ end
+ else
+ begin
+ FastAnsitoWideBuf(p,tmp);
+ SendMessageW(wnd,CB_ADDSTRING,0,lparam(TranslateW(tmp)));
+ if (p=@buf) and (lstrcmpia(p,'structure')=0) then
+ break;
+ end;
+ p:=pc+1;
+ until pc=nil;
+ FreeMem(tmp);
+ end;
+ SendMessage(wnd,CB_SETCURSEL,0,0);
+end;
+
+procedure tmApiCard.FillList(combo:hwnd; mode:integer=0);
+var
+ buf:array [0..8191] of AnsiChar;
+ tmpbuf:array [0..127] of AnsiChar;
+ p,pc:PAnsiChar;
+begin
+ if INIFile[0]<>#0 then
+ begin
+ SendMessage(combo,CB_RESETCONTENT,0,0);
+ buf[0]:=#0;
+ GetPrivateProfileSectionNamesA(@buf,SizeOf(buf),@INIFile); // sections
+ p:=@buf;
+ while p^<>#0 do
+ begin
+ case mode of
+ 1: begin // just constant name
+ GetPrivateProfileStringA(p,'alias','',tmpbuf,127,@INIFile);
+ pc:=@tmpbuf;
+ end;
+ 2: begin // value (name)
+ pc:=StrCopyE(tmpbuf,p);
+ pc^:=' '; inc(pc);
+ pc^:='('; inc(pc);
+ GetPrivateProfileStringA(p,'alias','',pc,63,@INIFile);
+ pc:=StrEnd(tmpbuf);
+ pc^:=')'; inc(pc);
+ pc^:=#0;
+ pc:=@tmpbuf;
+ end;
+ 3: begin // name 'value'
+ GetPrivateProfileStringA(p,'alias','',tmpbuf,127,@INIFile);
+ pc:=StrEnd(tmpbuf);
+ pc^:=' '; inc(pc);
+ pc^:=''''; inc(pc);
+ pc:=StrCopyE(pc,p);
+ pc^:=''''; inc(pc);
+ pc^:=#0;
+ pc:=@tmpbuf;
+ end;
+ else // just constant value
+ pc:=p;
+ end;
+ SendMessageA(combo,CB_ADDSTRING,0,lparam(pc));
+ while p^<>#0 do inc(p); inc(p);
+ end;
+ SendMessage(combo,CB_SETCURSEL,-1,0);
+ end;
+end;
+
+function ServiceHelpDlg(Dialog:HWnd;hMessage:uint;wParam:WPARAM;lParam:LPARAM):LRESULT; stdcall;
+var
+ buf,p:PAnsiChar;
+ tmp:PWideChar;
+ card:tmApiCard;
+begin
+ result:=0;
+ case hMessage of
+ WM_CLOSE: begin
+ card:=tmApiCard(GetWindowLongPtr(Dialog,GWLP_USERDATA{DWLP_USER}));
+ card.HelpWindow:=0;
+ DestroyWindow(Dialog); //??
+ end;
+
+ WM_INITDIALOG: begin
+ TranslateDialogDefault(Dialog);
+ result:=1;
+ end;
+
+ WM_COMMAND: begin
+ if (wParam shr 16)=BN_CLICKED then
+ begin
+ case loword(wParam) of
+ IDOK,IDCANCEL: begin
+ card:=tmApiCard(GetWindowLongPtr(Dialog,GWLP_USERDATA{DWLP_USER}));
+ card.HelpWindow:=0;
+ DestroyWindow(Dialog);
+ end;
+ end;
+ end;
+ end;
+
+ WM_UPDATEHELP: begin
+ with tmApiCard(lParam) do
+ begin
+ if (INIFile[0]<>#0) and (lParam<>0) then
+ begin
+ GetMem(buf,BufSize);
+ GetMem(tmp,BufSize*SizeOf(WideChar));
+ SetDlgItemTextA(Dialog,IDC_HLP_SERVICE,@current);
+
+ GetPrivateProfileStringA(@current,'alias','',buf,BufSize,@INIFile);
+ SetDlgItemTextA(Dialog,IDC_HLP_ALIAS,buf);
+
+ GetPrivateProfileStringA(@current,'return','Undefined',buf,BufSize,@INIFile);
+ p:=buf;
+ // skip result type
+ // while p^ in sWordOnly do inc(p); if (p<>@buf) and (p^<>#0) then inc(p);
+ FastAnsiToWideBuf(p,tmp);
+ SetDlgItemTextW(Dialog,IDC_HLP_RETURN,TranslateW(tmp));
+
+ GetPrivateProfileStringA(@current,'descr','Undefined',buf,BufSize,@INIFile);
+ FastAnsiToWideBuf(buf,tmp);
+ SetDlgItemTextW(Dialog,IDC_HLP_EFFECT,TranslateW(tmp));
+
+ GetPrivateProfileStringA(@current,'plugin','',buf,BufSize,@INIFile);
+ FastAnsiToWideBuf(buf,tmp);
+ SetDlgItemTextW(Dialog,IDC_HLP_PLUGIN,TranslateW(tmp));
+ // Parameters
+ GetPrivateProfileStringA(@current,'wparam','0',buf,BufSize,@INIFile);
+ if StrScan(buf,'|')<>nil then
+ begin
+ ShowWindow(GetDlgItem(Dialog,IDC_HLP_WPARAML),SW_SHOW);
+ ShowWindow(GetDlgItem(Dialog,IDC_HLP_WPARAM ),SW_HIDE);
+ FillParams(GetDlgItem(Dialog,IDC_HLP_WPARAML),true);
+ end
+ else
+ begin
+ ShowWindow(GetDlgItem(Dialog,IDC_HLP_WPARAML),SW_HIDE);
+ ShowWindow(GetDlgItem(Dialog,IDC_HLP_WPARAM ),SW_SHOW);
+ FastAnsiToWideBuf(buf,tmp);
+ SetDlgItemTextW(Dialog,IDC_HLP_WPARAM,TranslateW(tmp));
+ end;
+
+ GetPrivateProfileStringA(@current,'lparam','0',buf,BufSize,@INIFile);
+ if StrScan(buf,'|')<>nil then
+ begin
+ ShowWindow(GetDlgItem(Dialog,IDC_HLP_LPARAML),SW_SHOW);
+ ShowWindow(GetDlgItem(Dialog,IDC_HLP_LPARAM ),SW_HIDE);
+ FillParams(GetDlgItem(Dialog,IDC_HLP_LPARAML),false);
+ end
+ else
+ begin
+ ShowWindow(GetDlgItem(Dialog,IDC_HLP_LPARAML),SW_HIDE);
+ ShowWindow(GetDlgItem(Dialog,IDC_HLP_LPARAM ),SW_SHOW);
+ FastAnsiToWideBuf(buf,tmp);
+ SetDlgItemTextW(Dialog,IDC_HLP_LPARAM,TranslateW(tmp));
+ end;
+
+ FreeMem(tmp);
+ FreeMem(buf);
+ end
+ else
+ begin
+ SetDlgItemTextW(Dialog,IDC_HLP_SERVICE,nil);
+ SetDlgItemTextW(Dialog,IDC_HLP_ALIAS ,nil);
+ SetDlgItemTextW(Dialog,IDC_HLP_PLUGIN ,nil);
+ SetDlgItemTextW(Dialog,IDC_HLP_RETURN ,nil);
+ SetDlgItemTextW(Dialog,IDC_HLP_EFFECT ,nil);
+ SetDlgItemTextW(Dialog,IDC_HLP_WPARAM ,nil);
+ SetDlgItemTextW(Dialog,IDC_HLP_LPARAM ,nil);
+ SendDlgItemMessage(Dialog,IDC_HLP_WPARAML,CB_RESETCONTENT,0,0);
+ SendDlgItemMessage(Dialog,IDC_HLP_LPARAML,CB_RESETCONTENT,0,0);
+ ShowWindow(GetDlgItem(Dialog,IDC_HLP_WPARAML),SW_HIDE);
+ ShowWindow(GetDlgItem(Dialog,IDC_HLP_LPARAML),SW_HIDE);
+ end;
+ end;
+ end;
+ end;
+end;
+
+procedure tmApiCard.SetCurrentService(item:pAnsiChar);
+begin
+ StrCopy(@current,item);
+end;
+
+procedure tmApiCard.Update(item:pAnsiChar=nil);
+begin
+ SendMessage(HelpWindow,WM_UPDATEHELP,0,LPARAM(self));
+end;
+
+procedure tmApiCard.Show;
+var
+ note,
+ title:pWideChar;
+begin
+ if HelpWindow=0 then
+ begin
+ HelpWindow:=CreateDialogW(hInstance,'IDD_MAPIHELP',//MAKEINTRESOURCEW(IDD_HELP),
+ parent,@ServiceHelpDlg);
+ if HelpWindow<>0 then
+ begin
+ SetWindowLongPtr(HelpWindow,GWLP_USERDATA{DWLP_USER},LONG_PTR(Self));
+ if isServiceHelp then
+ begin
+ title:='Miranda service help';
+ note :='''<proto>'' in service name will be replaced by protocol name for contact handle in parameter';
+ end
+ else
+ begin
+ title:='Miranda event help';
+ note :='';
+ end;
+ SendMessageW(HelpWindow,WM_SETTEXT,0,LPARAM(title));
+
+ SendMessageW(GetDlgItem(HelpWindow,IDC_HLP_NOTE),WM_SETTEXT,0,LPARAM(TranslateW(Note)));
+ end;
+ end
+ else
+ begin
+{
+ if parent<>GetParent(HelpWindow) then
+ SetParent(HelpWindow,parent);
+}
+ end;
+// if title<>nil then
+// SendMessageW(HelpWindow,WM_SETTEXT,0,TranslateW(title));
+
+ Update(@current);
+end;
+
+constructor tmApiCard.Create(fname:pAnsiChar; lparent:HWND=0);
+begin
+ inherited Create;
+
+ StrCopy(@IniFile,fname);
+ current[0]:=#0;
+ HelpWindow:=0;
+
+ if fname<>nil then
+ begin
+ ConvertFileName(fname,@INIFile);
+ // PluginLink^.CallService(MS_UTILS_PATHTOABSOLUTE,
+ // dword(PAnsiChar(ServiceHlpFile)),dword(INIFile));
+ if GetFSize(pAnsiChar(@INIFile))=0 then
+ begin
+ INIFile[0]:=#0;
+ end;
+ parent:=lparent;
+ end;
+end;
+
+function CreateServiceCard(parent:HWND=0):tmApiCard;
+begin
+ result:=tmApiCard.Create(ServiceHlpFile,parent);
+ result.isServiceHelp:=true;
+end;
+
+function CreateEventCard(parent:HWND=0):tmApiCard;
+begin
+ result:=tmApiCard.Create(EventsHlpFile,parent);
+ result.isServiceHelp:=false;
+end;
+
+
+//initialization
+//finalization
+end.
diff --git a/plugins/Utils.pas/playlist.pas b/plugins/Utils.pas/playlist.pas
new file mode 100644
index 0000000000..2ffe0143d6
--- /dev/null
+++ b/plugins/Utils.pas/playlist.pas
@@ -0,0 +1,480 @@
+{Playlist process}
+unit playlist;
+
+interface
+
+type
+ tPlaylist = class
+ private
+ fShuffle :boolean;
+ plSize :cardinal; // playlist entries
+ plCapacity:cardinal;
+ base :pWideChar;
+ name :pWideChar;
+ descr :pWideChar;
+ plStrings :array of PWideChar;
+ CurElement:cardinal;
+ PlOrder :array of cardinal;
+ CurOrder :cardinal;
+
+ procedure SetShuffle(value:boolean);
+ function GetShuffle:boolean;
+ procedure DoShuffle;
+
+ function GetTrackNumber:integer;
+ procedure SetTrackNumber(value:integer);
+
+ procedure AddLine(name,descr:pWideChar;new:boolean=true);
+ function ProcessElement(num:integer=-1):PWideChar; //virtual;
+
+ public
+ constructor Create(fname:pWideChar);
+ destructor Free;
+
+ procedure SetBasePath(path:pWideChar);
+
+ function GetSong(number:integer=-1):pWideChar;
+ function GetCount:integer;
+
+ function Next :pWideChar;
+ function Previous:pWideChar;
+
+ property Track :integer read GetTrackNumber write SetTrackNumber;
+ property Shuffle:boolean read GetShuffle write SetShuffle;
+ end;
+
+function isPlaylist(fname:pWideChar):integer;
+function CreatePlaylist(fname:pWideChar):tPlaylist;
+function CreatePlaylistBuf(buf:pointer;format:integer):tPlaylist;
+
+implementation
+
+uses windows, common, io, memini;//, m_api, mirutils;
+
+const
+ plSizeStart = 2048;
+ plSizeStep = 256;
+const
+ pltM3OLD = $100;
+ pltM3UTF = $200;
+
+type
+ tM3UPlaylist = class(tPlaylist)
+ private
+ public
+ constructor Create(fName:pWideChar);
+ constructor CreateBuf(buf:pointer);
+ end;
+
+ tPLSPlaylist = class(tPlaylist)
+ private
+ public
+ constructor Create(fname:pWideChar);
+ constructor CreateBuf(buf:pointer);
+ end;
+
+function isPlaylist(fname:pWideChar):integer;
+var
+ ext:array [0..7] of WideChar;
+begin
+ GetExt(fname,ext,7);
+ if StrCmpW(ext,'M3U',3)=0 then result:=1
+ else if StrCmpW(ext,'PLS' )=0 then result:=2
+ else result:=0;
+end;
+
+function CreatePlaylist(fname:pWideChar):tPlaylist;
+begin
+ case isPlaylist(fname) of
+ 1: result:=tM3UPlaylist.Create(fname);
+ 2: result:=tPLSPlaylist.Create(fname);
+ else result:=nil;
+ end;
+end;
+
+function CreatePlaylistBuf(buf:pointer;format:integer):tPlaylist;
+begin
+ case format of
+ 1: result:=tM3UPlaylist.CreateBuf(buf);
+ 2: result:=tPLSPlaylist.CreateBuf(buf);
+ else result:=nil;
+ end;
+end;
+
+//----- -----
+
+function SkipLine(var p:PWideChar):bool;
+begin
+ while p^>=' ' do inc(p);
+ while p^<=' ' do // Skip spaces too
+ begin
+ if p^=#0 then
+ begin
+ result:=false;
+ exit;
+ end;
+ p^:=#0;
+ inc(p);
+ end;
+ result:=true;
+end;
+
+constructor tM3UPlaylist.CreateBuf(buf:pointer);
+var
+ p:PAnsiChar;
+ pp,pd:pWideChar;
+ plBufW:pWideChar;
+ lname,ldescr:pWideChar;
+ finish:boolean;
+ pltNew:boolean;
+begin
+ inherited;
+
+ p:=buf;
+ if (pdword(p)^ and $00FFFFFF)=$00BFBBEF then
+ begin
+ inc(p,3);
+ UTF8ToWide(p,plBufW)
+ end
+ else
+ AnsiToWide(p,plBufW);
+
+ pp:=plBufW;
+ pltNew:=StrCmpW(pp,'#EXTM3U',7)=0;
+ if pltNew then SkipLine(pp);
+
+ ldescr:=nil;
+ finish:=false;
+ repeat
+ if pltNew then
+ begin
+ pd:=StrScanW(pp,',');
+ if pd<>nil then
+ begin
+ ldescr:=pd+1;
+ if not SkipLine(pp) then break;
+ end;
+ end;
+ lname:=pp;
+ finish:=SkipLine(pp);
+ AddLine(lname,ldescr);
+ until not finish;
+
+ mFreeMem(plBufW);
+end;
+
+constructor tM3UPlaylist.Create(fName:pWideChar);
+var
+ f:THANDLE;
+ i:integer;
+ plBuf:pAnsiChar;
+begin
+ f:=Reset(fName);
+
+ if f<>THANDLE(INVALID_HANDLE_VALUE) then
+ begin
+ i:=integer(FileSize(f));
+ if i=-1 then
+ i:=integer(GetFSize(fName));
+ if i<>-1 then
+ begin
+ mGetMem(plBuf,i+1);
+ BlockRead(f,plBuf^,i);
+ CloseHandle(f);
+ plBuf[i]:=#0;
+ CreateBuf(plBuf);
+ mFreeMem(plBuf);
+ end;
+ end;
+
+end;
+
+//----- -----
+
+constructor tPLSPlaylist.CreateBuf(buf:pointer);
+var
+ lname,ldescr:pWideChar;
+ section,storage,sectionlist:pointer;
+ ffile,ftitle:array [0..31] of AnsiChar;
+ f,t:pAnsiChar;
+ i,size:integer;
+begin
+ inherited;
+
+ storage:=OpenStorageBuf(buf);
+ if storage=nil then
+ exit;
+ sectionlist:=GetSectionList(storage);
+ section:=SearchSection(storage,sectionlist);
+ FreeSectionList(sectionlist);
+
+ size:=GetParamSectionInt(section,'NumberOfEntries');
+ f:=StrCopyE(ffile ,'File');
+ t:=StrCopyE(ftitle,'Title');
+ for i:=1 to size do
+ begin
+ IntToStr(f,i);
+ AnsiToWide(GetParamSectionStr(section,ffile),lname);
+
+ IntToStr(t,i);
+ AnsiToWide(GetParamSectionStr(section,ftitle),ldescr);
+
+ AddLine(lname,ldescr,false);
+ end;
+
+ CloseStorage(storage);
+end;
+
+constructor tPLSPlaylist.Create(fName:pWideChar);
+var
+ buf:pAnsiChar;
+ h:THANDLE;
+ size:integer;
+begin
+ if FileExists(fname) then
+ begin
+ h:=Reset(fname);
+ if h<>THANDLE(INVALID_HANDLE_VALUE) then
+ begin
+ size:=FileSize(h);
+ if size>0 then
+ begin
+ GetMem(buf,size+1);
+ BlockRead(h,buf^,size);
+ buf[size]:=#0;
+ CreateBuf(buf);
+ FreeMem(buf);
+ end;
+ CloseHandle(h);
+ end;
+ end;
+end;
+
+//----- -----
+
+constructor tPlaylist.Create(fName:pWideChar);
+begin
+// inherited;
+
+ CurElement:=0;
+ base:=nil;
+ name:=nil;
+ descr:=nil;
+ Shuffle:=false;
+ plSize:=0;
+
+ SetBasePath(fname);
+end;
+
+destructor tPlaylist.Free;
+var
+ i:integer;
+begin
+ PlOrder:=nil;
+
+ mFreeMem(base);
+ mFreeMem(name);
+ mFreeMem(descr);
+
+ for i:=0 to plSize-1 do
+ begin
+ mFreeMem(plStrings[i*2]);
+ mFreeMem(plStrings[i*2+1]);
+ end;
+ plStrings:=nil;
+
+// inherited;
+end;
+
+procedure tPlaylist.AddLine(name,descr:pWideChar;new:boolean=true);
+begin
+ if plCapacity=0 then
+ begin
+ plCapacity:=plSizeStart;
+ SetLength(plStrings,plSizeStart*2);
+ fillChar(plStrings[0],plSizeStart*2*SizeOf(pWideChar),0);
+ end
+ else if plSize=plCapacity then
+ begin
+ inc(plCapacity,plSizeStep);
+ SetLength(plStrings,plCapacity*2);
+ fillChar(plStrings[plSize],plSizeStep*2*SizeOf(pWideChar),0);
+ end;
+ if new then
+ begin
+ StrDupW(plStrings[plSize*2 ],name);
+ StrDupW(plStrings[plSize*2+1],descr);
+ end
+ else
+ begin
+ plStrings[plSize*2 ]:=name;
+ plStrings[plSize*2+1]:=descr;
+ end;
+ inc(plSize);
+end;
+
+procedure tPlaylist.SetBasePath(path:pWideChar);
+var
+ buf:array [0..MAX_PATH-1] of WideChar;
+ p,pp:pWideChar;
+begin
+ mFreeMem(base);
+
+ pp:=ExtractW(path,false);
+ p:=StrCopyEW(buf,pp);
+ mFreeMem(pp);
+
+ if ((p-1)^<>'\') and ((p-1)^<>'/') then
+ begin
+ if StrScanW(buf,'/')<>nil then
+ p^:='/'
+ else
+ p^:='\';
+ inc(p);
+ end;
+ p^:=#0;
+ StrDupW(base,buf);
+end;
+
+function tPlaylist.GetCount:integer;
+begin
+ result:=plSize;
+end;
+
+function tPlaylist.GetTrackNumber:integer;
+begin
+ if fShuffle then
+ result:=CurOrder
+ else
+ result:=CurElement;
+end;
+
+procedure tPlaylist.SetTrackNumber(value:integer);
+begin
+ if value<0 then
+ value:=0
+ else if value>=Integer(plSize) then
+ value:=plSize-1;
+
+ if fShuffle then
+ CurOrder:=value
+ else
+ CurElement:=value;
+end;
+
+function tPlaylist.ProcessElement(num:integer=-1):pWideChar;
+begin
+ if num<0 then
+ num:=Track
+ else if num>=integer(plSize) then
+ num:=plSize-1;
+ if fShuffle then
+ num:=PlOrder[num];
+
+ result:=plStrings[num*2];
+end;
+
+function tPlaylist.GetSong(number:integer=-1):PWideChar;
+var
+ buf:array [0..MAX_PATH-1] of WideChar;
+begin
+ result:=ProcessElement(number);
+
+ if (result<>nil) and not isPathAbsolute(result) and (base<>nil) then
+ begin
+ StrCopyW(StrCopyEW(buf,base),result);
+ StrDupW(result,buf);
+ end
+ else
+ StrDupW(result,result);
+end;
+
+procedure tPlaylist.SetShuffle(value:boolean);
+begin
+ if value then
+ begin
+// if not fShuffle then // need to set Shuffle
+ DoShuffle;
+ end;
+
+ fShuffle:=value;
+end;
+
+function tPlaylist.GetShuffle:boolean;
+begin
+ result:=fShuffle;
+end;
+
+procedure tPlaylist.DoShuffle;
+var
+ i,RandInx: cardinal;
+ SwapItem: cardinal;
+begin
+ SetLength(PlOrder,plSize);
+ Randomize;
+ for i:=0 to plSize-1 do
+ PlOrder[i]:=i;
+ if plSize>1 then
+ begin
+ for i:=0 to plSize-2 do
+ begin
+ RandInx:=cardinal(Random(plSize-i));
+ SwapItem:=PlOrder[i];
+ PlOrder[i ]:=PlOrder[RandInx];
+ PlOrder[RandInx]:=SwapItem;
+ end;
+ end;
+ CurOrder:=0;
+end;
+
+function tPlaylist.Next:PWideChar;
+begin
+ if plSize<>0 then
+ begin
+ if not Shuffle then
+ begin
+ inc(CurElement);
+ if CurElement=plSize then
+ CurElement:=0;
+ end
+ else // if mode=plShuffle then
+ begin
+ inc(CurOrder);
+ if CurOrder=plSize then
+ begin
+ DoShuffle;
+ CurOrder:=0;
+ end;
+ end;
+ result:=GetSong;
+ end
+ else
+ result:=nil;
+end;
+
+function tPlaylist.Previous:PWideChar;
+begin
+ if plSize<>0 then
+ begin
+ if not Shuffle then
+ begin
+ if CurElement=0 then
+ CurElement:=plSize;
+ Dec(CurElement);
+ end
+ else // if mode=plShuffle then
+ begin
+ if CurOrder=0 then
+ begin
+ DoShuffle;
+ CurOrder:=plSize;
+ end;
+ dec(CurOrder);
+ end;
+ result:=GetSong;
+ end
+ else
+ result:=nil;
+end;
+
+end.
diff --git a/plugins/Utils.pas/protocols.pas b/plugins/Utils.pas/protocols.pas
new file mode 100644
index 0000000000..8b585c39b4
--- /dev/null
+++ b/plugins/Utils.pas/protocols.pas
@@ -0,0 +1,610 @@
+unit protocols;
+
+interface
+
+uses windows,m_api;
+
+function FindProto(proto:PAnsiChar):uint_ptr;
+
+function GetStatusNum(status:integer):integer;
+function GetNumProto:cardinal;
+
+function GetProtoSetting(ProtoNum:uint_ptr;param:boolean=false):LPARAM;
+procedure SetProtoSetting(ProtoNum:uint_ptr;mask:LPARAM;param:boolean=false);
+
+function IsTunesSupported (ProtoNum:uint_ptr):bool;
+function IsXStatusSupported(ProtoNum:uint_ptr):bool;
+function IsChatSupported (ProtoNum:uint_ptr):bool;
+
+function GetProtoStatus (ProtoNum:uint_ptr):integer;
+function GetProtoStatusNum(ProtoNum:uint_ptr):integer;
+function GetProtoName (ProtoNum:uint_ptr):PAnsiChar;
+
+procedure FillProtoList (list:hwnd;withIcons:bool=false);
+procedure CheckProtoList (list:hwnd);
+procedure FillStatusList (proto:cardinal;list:hwnd;withIcons:bool=false);
+procedure CheckStatusList(list:hwnd;ProtoNum:cardinal);
+
+function CreateProtoList(deepscan:boolean=false):integer;
+procedure FreeProtoList;
+
+function SetStatus(proto:PAnsiChar;status:integer;txt:PAnsiChar=pointer(-1)):integer;
+function SetXStatus(proto:PAnsiChar;newstatus:integer;
+ txt:pWideChar=nil;title:pWideChar=nil):integer;
+function GetXStatus(proto:PAnsiChar;txt:pointer=nil;title:pointer=nil):integer;
+
+const
+ psf_online = $0001;
+ psf_invisible = $0002;
+ psf_shortaway = $0004;
+ psf_longaway = $0008;
+ psf_lightdnd = $0010;
+ psf_heavydnd = $0020;
+ psf_freechat = $0040;
+ psf_outtolunch = $0080;
+ psf_onthephone = $0100;
+ psf_enabled = $0800;
+ psf_all = $08FF;
+ // protocol properties
+ psf_chat = $1000;
+ psf_icq = $2000;
+ psf_tunes = $4000;
+ psf_deleted = $8000;
+
+implementation
+
+uses commctrl,common,dbsettings;
+
+const
+ defproto = '- default -';
+
+const
+ NumStatus = 10;
+ StatCodes:array [0..NumStatus-1] of integer=(
+ ID_STATUS_OFFLINE,
+ ID_STATUS_ONLINE,
+ ID_STATUS_INVISIBLE,
+ ID_STATUS_AWAY,
+ ID_STATUS_NA,
+ ID_STATUS_OCCUPIED,
+ ID_STATUS_DND,
+ ID_STATUS_FREECHAT,
+ ID_STATUS_OUTTOLUNCH,
+ ID_STATUS_ONTHEPHONE);
+const
+ StatNames:array [0..NumStatus-1] of PWideChar=(
+ 'Default'{'Offline'},'Online','Invisible','Away','N/A','Occupied','DND',
+ 'Free for chat','Out to lunch','On the Phone');
+
+type
+ pMyProto = ^tMyProto;
+ tMyProto = record
+ name :PAnsiChar; // internal account name
+ descr :PWideChar; // public account name
+// xstat :integer; // old ICQ XStatus
+ enabled :integer;
+ status :integer; // mask
+ param :LPARAM;
+ end;
+ pMyProtos = ^tMyProtos;
+ tMyProtos = array [0..100] of tMyProto;
+
+var
+ protos:pMyProtos;
+ NumProto:cardinal;
+ hAccounts:THANDLE;
+
+function FindProto(proto:PAnsiChar):uint_ptr;
+var
+ i:integer;
+begin
+ if uint_ptr(proto)<=100 then
+ begin
+ result:=uint_ptr(proto);
+ exit;
+ end;
+ for i:=1 to NumProto do
+ begin
+ if StrCmp(proto,protos^[i].name)=0 then
+ begin
+ result:=i;
+ exit;
+ end;
+ end;
+ result:=0;
+end;
+
+function IsTunesSupported(ProtoNum:uint_ptr):bool;
+begin
+ ProtoNum:=FindProto(PAnsiChar(ProtoNum));
+ if (ProtoNum<=NumProto) and ((protos^[ProtoNum].status and psf_tunes)<>0) then
+ result:=true
+ else
+ result:=false;
+end;
+
+function IsXStatusSupported(ProtoNum:uint_ptr):bool;
+begin
+ ProtoNum:=FindProto(PAnsiChar(ProtoNum));
+ if (ProtoNum<=NumProto) and ((protos^[ProtoNum].status and psf_icq)<>0) then
+ result:=true
+ else
+ result:=false;
+end;
+
+function IsChatSupported(ProtoNum:uint_ptr):bool;
+begin
+ ProtoNum:=FindProto(PAnsiChar(ProtoNum));
+ if (ProtoNum<=NumProto) and ((protos^[ProtoNum].status and psf_chat)<>0) then
+ result:=true
+ else
+ result:=false;
+end;
+
+function GetProtoSetting(ProtoNum:uint_ptr;param:boolean=false):LPARAM;
+begin
+ ProtoNum:=FindProto(PAnsiChar(ProtoNum));
+ if ProtoNum<=NumProto then
+ begin
+ if param then
+ result:=protos^[ProtoNum].param
+ else
+ result:=protos^[ProtoNum].enabled
+ end
+ else
+ result:=0;
+end;
+
+procedure SetProtoSetting(ProtoNum:uint_ptr;mask:LPARAM;param:boolean=false);
+begin
+ ProtoNum:=FindProto(PAnsiChar(ProtoNum));
+ if ProtoNum<=NumProto then
+ begin
+ if param then
+ protos^[ProtoNum].param:=mask
+ else
+ protos^[ProtoNum].enabled:=mask;
+ end;
+end;
+
+function GetStatusNum(status:integer):integer;
+var
+ i:integer;
+begin
+ for i:=0 to NumStatus-1 do
+ if StatCodes[i]=status then
+ begin
+ result:=i;
+ exit;
+ end;
+ result:=0; //-1
+end;
+
+function GetProtoStatus(ProtoNum:uint_ptr):integer;
+begin
+ ProtoNum:=FindProto(PAnsiChar(ProtoNum));
+ result:=CallProtoService(protos^[ProtoNum].name,PS_GETSTATUS,0,0);
+end;
+
+function GetProtoStatusNum(ProtoNum:uint_ptr):integer;
+begin
+ ProtoNum:=FindProto(PAnsiChar(ProtoNum));
+ result:=GetStatusNum(GetProtoStatus(ProtoNum));
+end;
+
+function GetNumProto:cardinal;
+begin
+ result:=NumProto;
+end;
+
+function GetProtoName(ProtoNum:uint_ptr):PAnsiChar;
+begin
+ if ProtoNum<=NumProto then
+ result:=protos^[ProtoNum].name
+ else
+ result:=nil;
+end;
+
+procedure FillProtoList(list:hwnd;withIcons:bool=false);
+var
+ item:TLVITEMW;
+ lvc:TLVCOLUMN;
+ i,NewItem:integer;
+ cli:PCLIST_INTERFACE;
+begin
+ FillChar(lvc,SizeOf(lvc),0);
+ ListView_SetExtendedListViewStyle(list, LVS_EX_CHECKBOXES);
+ if withIcons then
+ begin
+ cli:=PCLIST_INTERFACE(CallService(MS_CLIST_RETRIEVE_INTERFACE,0,0));
+ SetWindowLongPtrW(list,GWL_STYLE,
+ GetWindowLongPtrW(list,GWL_STYLE) or LVS_SHAREIMAGELISTS);
+ ListView_SetImageList(list,
+ CallService(MS_CLIST_GETICONSIMAGELIST,0,0),LVSIL_SMALL);
+ lvc.mask:=LVCF_FMT+LVCF_IMAGE
+ end
+ else
+ begin
+ cli:=nil;
+ lvc.mask:=LVCF_FMT;
+ end;
+
+ lvc.fmt :={LVCFMT_IMAGE or} LVCFMT_LEFT;
+ ListView_InsertColumn(list,0,lvc);
+
+ FillChar(item,SizeOf(item),0);
+ if withIcons then
+ item.mask:=LVIF_TEXT+LVIF_IMAGE
+ else
+ item.mask:=LVIF_TEXT;
+ for i:=0 to NumProto do
+ begin
+ item.iItem:=i;
+ item.pszText:=protos^[i].descr;
+ if withIcons and (i>0) then
+ item.iImage:=cli^.pfnIconFromStatusMode(protos^[i].name,ID_STATUS_ONLINE,0);
+ newItem:=SendMessageW(list,LVM_INSERTITEMW,0,lParam(@item));
+ if newItem>=0 then
+ ListView_SetCheckState(list,newItem,(protos^[i].enabled and psf_enabled)<>0)
+ end;
+ ListView_SetItemState (list,0,LVIS_FOCUSED or LVIS_SELECTED,LVIS_FOCUSED or LVIS_SELECTED);
+
+ ListView_SetColumnWidth(list,0,LVSCW_AUTOSIZE);
+end;
+
+procedure CheckProtoList(list:hwnd);
+var
+ i:integer;
+begin
+ for i:=1 to ListView_GetItemCount(list) do
+ begin
+ with protos^[i] do
+ if ListView_GetCheckState(list,i)=BST_CHECKED then
+ enabled:=enabled or psf_enabled
+ else
+ enabled:=enabled and not psf_enabled;
+ end;
+end;
+
+procedure FillStatusList(proto:cardinal;list:hwnd;withIcons:bool=false);
+
+ procedure AddString(num:integer;enabled:boolean;cli:PCLIST_INTERFACE);
+ var
+ item:LV_ITEMW;
+ NewItem:integer;
+ begin
+ FillChar(item,SizeOf(item),0);
+ item.iItem :=num;
+ item.lParam :=StatCodes[num];
+ if cli<>nil then
+ begin
+ item.mask :=LVIF_TEXT+LVIF_PARAM+LVIF_IMAGE;
+ item.iImage:=cli^.pfnIconFromStatusMode(protos^[proto].name,item.lParam,0);
+ end
+ else
+ item.mask :=LVIF_TEXT+LVIF_PARAM;
+ item.pszText:=TranslateW(StatNames[num]);
+ newItem:=SendMessageW(list,LVM_INSERTITEMW,0,lparam(@item));
+ if newItem>=0 then
+ ListView_SetCheckState(list,newItem,enabled);
+ end;
+
+var
+ lvc:TLVCOLUMN;
+ cli:PCLIST_INTERFACE;
+begin
+ if proto=0 then
+ withIcons:=false;
+ ListView_DeleteAllItems(list);
+ ListView_DeleteColumn(list,0);
+ FillChar(lvc,SizeOf(lvc),0);
+ ListView_SetExtendedListViewStyle(list, LVS_EX_CHECKBOXES);
+ if withIcons then
+ begin
+ cli:=PCLIST_INTERFACE(CallService(MS_CLIST_RETRIEVE_INTERFACE,0,0));
+ SetWindowLongPtrW(list,GWL_STYLE,
+ GetWindowLongPtrW(list,GWL_STYLE) or LVS_SHAREIMAGELISTS);
+ ListView_SetImageList(list,
+ CallService(MS_CLIST_GETICONSIMAGELIST,0,0),LVSIL_SMALL);
+ lvc.mask:=LVCF_FMT+LVCF_IMAGE
+ end
+ else
+ begin
+ cli:=nil;
+ SetWindowLongPtrW(list,GWL_STYLE,
+ GetWindowLongPtrW(list,GWL_STYLE) and not LVS_SHAREIMAGELISTS);
+// ListView_SetImageList(list,0,LVSIL_SMALL);
+ lvc.mask:=LVCF_FMT;
+ end;
+ lvc.fmt:={LVCFMT_IMAGE or} LVCFMT_LEFT;
+ ListView_InsertColumn(list,0,lvc);
+
+ AddString(0,true,nil);
+ ListView_SetItemState (list,0,LVIS_FOCUSED or LVIS_SELECTED,$000F);
+ with protos^[proto] do
+ begin
+ if (status and psf_online )<>0 then AddString(1,(enabled and psf_online )<>0,cli);
+ if (status and psf_invisible )<>0 then AddString(2,(enabled and psf_invisible )<>0,cli);
+ if (status and psf_shortaway )<>0 then AddString(3,(enabled and psf_shortaway )<>0,cli);
+ if (status and psf_longaway )<>0 then AddString(4,(enabled and psf_longaway )<>0,cli);
+ if (status and psf_lightdnd )<>0 then AddString(5,(enabled and psf_lightdnd )<>0,cli);
+ if (status and psf_heavydnd )<>0 then AddString(6,(enabled and psf_heavydnd )<>0,cli);
+ if (status and psf_freechat )<>0 then AddString(7,(enabled and psf_freechat )<>0,cli);
+ if (status and psf_outtolunch)<>0 then AddString(8,(enabled and psf_outtolunch)<>0,cli);
+ if (status and psf_onthephone)<>0 then AddString(9,(enabled and psf_onthephone)<>0,cli);
+ end;
+ ListView_SetColumnWidth(list,0,LVSCW_AUTOSIZE);
+end;
+
+procedure CheckStatusList(list:hwnd;ProtoNum:cardinal);
+
+ procedure SetStatusMask(stat:integer;state:bool);
+ var
+ i:integer;
+ begin
+ case stat of
+ ID_STATUS_ONLINE: i:=psf_online;
+ ID_STATUS_INVISIBLE: i:=psf_invisible;
+ ID_STATUS_AWAY: i:=psf_shortaway;
+ ID_STATUS_NA: i:=psf_longaway;
+ ID_STATUS_OCCUPIED: i:=psf_lightdnd;
+ ID_STATUS_DND: i:=psf_heavydnd;
+ ID_STATUS_FREECHAT: i:=psf_freechat;
+ ID_STATUS_OUTTOLUNCH: i:=psf_outtolunch;
+ ID_STATUS_ONTHEPHONE: i:=psf_onthephone;
+ else
+ exit;
+ end;
+ with protos^[ProtoNum] do
+ if state then
+ enabled:=enabled or i
+ else
+ enabled:=enabled and not i;
+ end;
+
+var
+ i:integer;
+ Item:TLVITEM;
+begin
+ for i:=1 to ListView_GetItemCount(list)-1 do //skip default
+ begin
+ Item.iItem:=i;
+ Item.mask:=LVIF_PARAM;
+ ListView_GetItem(list,Item);
+ SetStatusMask(Item.lParam,ListView_GetCheckState(list,i)=BST_CHECKED)
+ end;
+end;
+
+function AccListChanged(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ i:integer;
+begin
+ result:=0;
+ case wParam of
+ PRAC_ADDED: begin
+ end;
+ PRAC_CHANGED: begin
+ i:=FindProto(PPROTOACCOUNT(lParam).szModuleName);
+ if i>0 then
+ protos^[i].descr:=PPROTOACCOUNT(lParam).tszAccountName.w;
+ end;
+ PRAC_REMOVED: begin
+ end;
+ end;
+end;
+
+function CreateProtoList(deepscan:boolean=false):integer;
+var
+ protoCount,i:integer;
+ proto:^PPROTOACCOUNT;
+ buf:array [0..127] of AnsiChar;
+ flag:integer;
+ p:pAnsichar;
+// hContract:THANDLE;
+begin
+ CallService(MS_PROTO_ENUMACCOUNTS,wparam(@protoCount),lparam(@proto));
+
+ mGetMem(protos,(protoCount+1)*SizeOf(tMyProto)); // 0 - default
+ NumProto:=0;
+ with protos^[0] do
+ begin
+ name :=defproto;
+ descr :=defproto;
+ status :=-1;
+ enabled:=-1;
+ end;
+ for i:=1 to protoCount do
+ begin
+ // active and switched off (but not deleted)
+ inc(NumProto);
+ with protos^[NumProto] do
+ begin
+ name :=proto^^.szModuleName;
+ descr:=proto^^.tszAccountName.w;
+
+ enabled:=psf_all;//psf_enabled;
+ status :=0;
+// xstat :=-1;
+ flag:=CallProtoService(name,PS_GETCAPS,PFLAGNUM_2,0);
+ if (flag and PF2_ONLINE) <>0 then status:=status or psf_online;
+ if (flag and PF2_INVISIBLE) <>0 then status:=status or psf_invisible;
+ if (flag and PF2_SHORTAWAY) <>0 then status:=status or psf_shortaway;
+ if (flag and PF2_LONGAWAY) <>0 then status:=status or psf_longaway;
+ if (flag and PF2_LIGHTDND) <>0 then status:=status or psf_lightdnd;
+ if (flag and PF2_HEAVYDND) <>0 then status:=status or psf_heavydnd;
+ if (flag and PF2_FREECHAT) <>0 then status:=status or psf_freechat;
+ if (flag and PF2_OUTTOLUNCH)<>0 then status:=status or psf_outtolunch;
+ if (flag and PF2_ONTHEPHONE)<>0 then status:=status or psf_onthephone;
+
+ flag:=CallProtoService(name,PS_GETCAPS,PFLAGNUM_1,0);
+ if ((flag and PF1_CHAT)<>0) or
+ (DBReadByte(0,name,'CtcpChatAccept',13)<>13) or // IRC
+ (DBReadByte(0,name,'Jud',13)<>13) then // Jabber
+// flag:=CallProtoService(name,PS_GETCAPS,PFLAGNUM_1,0);
+// if (flag and PF1_CHAT)<>0 then
+ status:=status or psf_chat;
+ p:=StrCopyE(buf,name);
+ StrCopy(p,PS_ICQ_GETCUSTOMSTATUS);
+ if ServiceExists(buf)<>0 then
+ status:=status or psf_icq;
+
+ StrCopy(p,PS_SET_LISTENINGTO);
+ if ServiceExists(buf)<>0 then
+ status:=status or psf_tunes;
+
+ end;
+ inc(proto);
+ end;
+
+{
+ if deepscan then
+ begin
+ hContact:=CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ while hContact<>0 do
+ begin
+ i:=NumProto;
+ while i>0 do
+ begin
+ if StrCmp()=0 then
+ break;
+ dec(i);
+ end;
+
+ hContact:=CallService(MS_DB_CONTACT_FINDNEXT,hContact,0);
+ end;
+ end;
+}
+ result:=NumProto;
+
+ hAccounts:=HookEvent(ME_PROTO_ACCLISTCHANGED,@AccListChanged);
+end;
+
+procedure FreeProtoList;
+begin
+ UnhookEvent(hAccounts);
+ mFreeMem(protos);
+ NumProto:=0;
+end;
+
+function SetStatus(proto:PAnsiChar;status:integer;txt:PAnsiChar=pointer(-1)):integer;
+//var nas:TNAS_PROTOINFO;
+begin
+ if status>0 then
+ result:=CallProtoService(proto,PS_SETSTATUS,status,0)
+ else
+ result:=-1;
+ if txt<>PAnsiChar(-1) then
+ begin
+// if ServiceExists(MS_NAS_SETSTATEA)=0 then
+ result:=CallProtoService(proto,PS_SETAWAYMSG,abs(status),lparam(txt))
+(*
+ else
+ begin
+ {
+ nas.Msg.w:=mmi.malloc((StrLenW(txt)+1)*SizeOf(WideChar));
+ nas.Msg.w^:=#0;
+ StrCopyW(nas.Msg.w,txt);
+ }
+ StrDup(nas.Msg.a,txt);
+ nas.Flags :=0;
+ nas.cbSize :=SizeOf(nas);
+ nas.szProto:=proto;
+ nas.status :=abs(status){0};
+ result:=CallService(MS_NAS_SETSTATEA,LPARAM(@nas),1);
+ end;
+*)
+ end;
+end;
+
+function SetXStatus(proto:PAnsiChar;newstatus:integer;
+ txt:pWideChar=nil;title:pWideChar=nil):integer;
+var
+ ics:TICQ_CUSTOM_STATUS;
+begin
+ result:=0;
+ if IsXStatusSupported(uint_ptr(proto)) then
+ begin
+ with ics do
+ begin
+ cbSize:=SizeOf(ics);
+ flags:=CSSF_UNICODE;
+ if newstatus>=0 then
+ begin
+ flags:=flags or CSSF_MASK_STATUS;
+ status:=@newstatus;
+ end;
+ if title<>PWideChar(-1) then
+ begin
+ flags:=flags or CSSF_MASK_NAME;
+ szName.w:=title;
+ end;
+ if txt<>PWideChar(-1) then
+ begin
+ flags:=flags or CSSF_MASK_MESSAGE;
+ szMessage.w:=txt;
+ end;
+ end;
+ result:=CallProtoService(proto,PS_ICQ_SETCUSTOMSTATUSEX,0,lparam(@ics));
+ end;
+end;
+
+function GetXStatus(proto:PAnsiChar;txt:pointer=nil;title:pointer=nil):integer;
+var
+ buf:array [0..127] of AnsiChar;
+ pc:PAnsiChar;
+ param:array [0..63] of AnsiChar;
+
+// ics:TICQ_CUSTOM_STATUS;
+// i,j:integer;
+begin
+ result:=0;
+ if IsXStatusSupported(uint_ptr(proto)) then
+ begin
+{
+ with ics do
+ begin
+ cbSize:=SizeOf(ics);
+ flags:=CSSF_STR_SIZES;
+ wParam:=@i;
+ lParam:=@j;
+ end;
+ CallProtoService(0,PS_ICQ_GETCUSTOMSTATUSEX,0,dword(@ics));
+ if title<>nil then
+ mGetMem(title^,(i+1)*SizeOf(WideChar));
+ if txt<>nil then
+ mGetMem(txt^,(j+1)*SizeOf(WideChar));
+
+ with ics do
+ begin
+ cbSize:=SizeOf(ics);
+ flags:=CSSF_MASK_STATUS or CSSF_MASK_NAME or CSSF_MASK_MESSAGE or CSSF_UNICODE;
+ status:=@result;
+ szName.w :=pdword(title)^;
+ szMessage.w:=pdword(txt)^;
+ end;
+ CallProtoService(0,PS_ICQ_GETCUSTOMSTATUSEX,0,dword(@ics));
+}
+
+ StrCopy(buf,proto);
+ StrCat (buf,PS_ICQ_GETCUSTOMSTATUS);
+ result:=CallService(buf,0,0);
+ if (txt<>nil) or (title<>nil) then
+ begin
+ move('XStatus',param,7);
+ IntToStr(param+7,result);
+ pc:=strend(param);
+
+ if txt<>nil then
+ begin
+ StrCopy(pc,'Msg'); pWideChar(txt^):=DBReadUnicode(0,proto,param,nil);
+ end;
+ if title<>nil then
+ begin
+ StrCopy(pc,'Name'); pWideChar(title^):=DBReadUnicode(0,proto,param,nil);
+ end;
+ end;
+
+ end;
+end;
+
+end.
diff --git a/plugins/Utils.pas/sedit.pas b/plugins/Utils.pas/sedit.pas
new file mode 100644
index 0000000000..c216cecc09
--- /dev/null
+++ b/plugins/Utils.pas/sedit.pas
@@ -0,0 +1,1331 @@
+{structure editor}
+unit SEdit;
+
+interface
+
+uses windows;
+
+function EditStructure(struct:pAnsiChar;parent:HWND=0):pAnsiChar;
+
+implementation
+
+uses io,messages, commctrl, common, wrapper, strans, memini
+ {$IFDEF Miranda}, m_api, mirutils{$ENDIF};
+{
+ <STE_* set> <len> <data>
+}
+{$r structopts.res}
+{$include i_struct_const.inc}
+
+{$IFDEF Miranda}
+const
+ ACI_NEW :PAnsiChar = 'ACI_New';
+ ACI_UP :PAnsiChar = 'ACI_Up';
+ ACI_DOWN :PAnsiChar = 'ACI_Down';
+ ACI_DELETE :PAnsiChar = 'ACI_Delete';
+
+const
+ API_STRUCT_FILE = 'plugins\services.ini';
+ namespace = 'Structure';
+{$ENDIF}
+
+type
+ pint_ptr = ^int_ptr;
+ TWPARAM = WPARAM;
+ TLPARAM = LPARAM;
+
+const
+ col_alias=0;
+ col_type =1;
+ col_len =2;
+{$IFDEF Miranda}
+ col_flag =3;
+ col_data =4;
+{$ELSE}
+ col_data =3;
+{$ENDIF}
+var
+ OldLVProc:pointer;
+ storage:pointer;
+
+function GetTypeIndex(etype:integer):integer;
+var
+ j:integer;
+begin
+ j:=0;
+ while j<MaxStructTypes do
+ begin
+ if StructElems[j].typ=etype then break;
+ inc(j);
+ end;
+ if j<MaxStructTypes then
+ result:=j
+ else
+ result:=SST_UNKNOWN;
+end;
+
+procedure InsertString(wnd:HWND;num:dword;str:PAnsiChar);
+var
+ buf:array [0..127] of WideChar;
+begin
+ SendMessageW(wnd,CB_SETITEMDATA,
+ SendMessageW(wnd,CB_ADDSTRING,0,
+{$IFDEF Miranda}
+ lparam(TranslateW(FastAnsiToWideBuf(str,buf)))),
+{$ELSE}
+ lparam(FastAnsiToWideBuf(str,buf))),
+{$ENDIF}
+ num);
+end;
+
+{$IFDEF Miranda}
+procedure RegisterIcon(var sid:TSKINICONDESC;id,name:PAnsiChar;descr:PAnsiChar);
+var
+ buf:array [0..63] of WideChar;
+begin
+ sid.hDefaultIcon :=LoadImageA(hInstance,id,IMAGE_ICON,16,16,0);
+ sid.pszName :=name;
+ sid.szDescription.w:=FastAnsiToWideBuf(descr,buf);
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+end;
+
+procedure RegisterIcons;
+var
+ sid:TSKINICONDESC;
+begin
+ if CallService(MS_SKIN2_GETICON,0,LPARAM(ACI_NEW))<>0 then
+ exit;
+
+ FillChar(sid,SizeOf(TSKINICONDESC),0);
+ sid.cbSize :=SizeOf(TSKINICONDESC);
+ sid.cx :=16;
+ sid.cy :=16;
+ sid.flags :=SIDF_UNICODE;
+ sid.szSection.w:='Actions';
+
+ RegisterIcon(sid,'IDI_NEW' ,ACI_NEW ,'New');
+ RegisterIcon(sid,'IDI_DELETE' ,ACI_DELETE ,'Delete');
+ RegisterIcon(sid,'IDI_UP' ,ACI_UP ,'Up');
+ RegisterIcon(sid,'IDI_DOWN' ,ACI_DOWN ,'Down');
+end;
+{$ENDIF}
+procedure SetDataButtonIcons(Dialog:HWND);
+var
+ ti:TTOOLINFOW;
+ hwndTooltip:HWND;
+begin
+ hwndTooltip:=CreateWindowW(TOOLTIPS_CLASS,nil,TTS_ALWAYSTIP,
+ integer(CW_USEDEFAULT),integer(CW_USEDEFAULT),
+ integer(CW_USEDEFAULT),integer(CW_USEDEFAULT),
+ Dialog,0,hInstance,nil);
+ FillChar(ti,SizeOf(ti),0);
+ ti.cbSize :=sizeof(TOOLINFO);
+ ti.uFlags :=TTF_IDISHWND or TTF_SUBCLASS;
+ ti.hwnd :=dialog;
+ ti.hinst :=hInstance;
+ ti.uId :=GetDlgItem(Dialog,IDC_DATA_NEW);
+{$IFDEF Miranda}
+ ti.lpszText:=TranslateW('New');
+ SetButtonIcon(ti.uId,ACI_NEW);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+ ti.uId :=GetDlgItem(Dialog,IDC_DATA_UP);
+ ti.lpszText:=TranslateW('Up');
+ SetButtonIcon(ti.uId,ACI_UP);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+ ti.uId :=GetDlgItem(Dialog,IDC_DATA_DOWN);
+ ti.lpszText:=TranslateW('Down');
+ SetButtonIcon(ti.uId,ACI_DOWN);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+ ti.uId :=GetDlgItem(Dialog,IDC_DATA_DELETE);
+ ti.lpszText:=TranslateW('Delete');
+ SetButtonIcon(ti.uId,ACI_DELETE);
+{$ELSE}
+ ti.lpszText:='New';
+ SendMessageW(ti.uId, BM_SETIMAGE, IMAGE_ICON,
+ LoadImage(hInstance,'IDI_NEW',IMAGE_ICON,16,16,0));
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+ ti.uId :=GetDlgItem(Dialog,IDC_DATA_UP);
+ ti.lpszText:='Up';
+ SendMessageW(ti.uId, BM_SETIMAGE, IMAGE_ICON,
+ LoadImage(hInstance,'IDI_UP',IMAGE_ICON,16,16,0));
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+ ti.uId :=GetDlgItem(Dialog,IDC_DATA_DOWN);
+ ti.lpszText:='Down';
+ SendMessageW(ti.uId, BM_SETIMAGE, IMAGE_ICON,
+ LoadImage(hInstance,'IDI_DOWN',IMAGE_ICON,16,16,0));
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+ ti.uId :=GetDlgItem(Dialog,IDC_DATA_DELETE);
+ ti.lpszText:='Delete';
+ SendMessageW(ti.uId, BM_SETIMAGE, IMAGE_ICON,
+ LoadImage(hInstance,'IDI_DELETE',IMAGE_ICON,16,16,0));
+{$ENDIF}
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+end;
+
+function NewLVProc(Dialog:HWnd;hMessage:uint;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+begin
+ result:=0;
+ case hMessage of
+ WM_KEYDOWN: begin
+ if (lParam and (1 shl 30))=0 then
+ begin
+ case wParam of
+ VK_UP: begin
+ if (GetKeyState(VK_CONTROL) and $8000)<>0 then
+ begin
+ SendMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDC_DATA_UP,0);
+ exit;
+ end;
+ end;
+ VK_DOWN: begin
+ if (GetKeyState(VK_CONTROL) and $8000)<>0 then
+ begin
+ SendMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDC_DATA_DOWN,0);
+ exit;
+ end;
+ end;
+ VK_INSERT: begin
+ SendMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDC_DATA_NEW,0);
+ exit;
+ end;
+ VK_DELETE: begin
+ SendMessage(GetParent(Dialog),WM_COMMAND,(BN_CLICKED shl 16)+IDC_DATA_DELETE,0);
+ exit;
+ end;
+ end;
+ end;
+ end;
+ end;
+ result:=CallWindowProc(OldLVProc,Dialog,hMessage,wParam,lParam);
+end;
+
+function MakeLVStructList(list:HWND):HWND;
+var
+ lv:LV_COLUMNW;
+begin
+ SendMessage(list,LVM_SETUNICODEFORMAT,1,0);
+ SendMessage(list,LVM_SETEXTENDEDLISTVIEWSTYLE,
+ LVS_EX_FULLROWSELECT or LVS_EX_GRIDLINES or LVS_EX_CHECKBOXES,
+ LVS_EX_FULLROWSELECT or LVS_EX_GRIDLINES or LVS_EX_CHECKBOXES);
+
+ zeromemory(@lv,sizeof(lv));
+ lv.mask:=LVCF_TEXT or LVCF_WIDTH;
+ lv.cx :=22; lv.pszText:={$IFDEF Miranda}TranslateW{$ENDIF}('alias');
+ SendMessageW(list,LVM_INSERTCOLUMNW,col_alias,lparam(@lv)); // alias
+ lv.cx :=62; lv.pszText:={$IFDEF Miranda}TranslateW{$ENDIF}('type');
+ SendMessageW(list,LVM_INSERTCOLUMNW,col_type ,lparam(@lv)); // type
+ lv.cx :=32; lv.pszText:={$IFDEF Miranda}TranslateW{$ENDIF}('length');
+ SendMessageW(list,LVM_INSERTCOLUMNW,col_len ,lparam(@lv)); // length
+{$IFDEF Miranda}
+ lv.cx :=20; lv.pszText:={$IFDEF Miranda}TranslateW{$ENDIF}('');
+ SendMessageW(list,LVM_INSERTCOLUMNW,col_flag ,lparam(@lv)); // variables flag
+{$ENDIF}
+ lv.cx :=72; lv.pszText:={$IFDEF Miranda}TranslateW{$ENDIF}('data');
+ SendMessageW(list,LVM_INSERTCOLUMNW,col_data ,lparam(@lv)); // value
+
+ SendMessageW(list,LVM_SETCOLUMNWIDTH,col_data,LVSCW_AUTOSIZE_USEHEADER);
+
+ OldLVProc:=pointer(SetWindowLongPtrW(list,GWL_WNDPROC,long_ptr(@NewLVProc)));
+ result:=list;
+end;
+
+procedure FillDataTypeList(wnd:HWND);
+var
+ i:integer;
+begin
+ SendMessage(wnd,CB_RESETCONTENT,0,0);
+
+ for i:=0 to MaxStructTypes-1 do
+ InsertString(wnd,StructElems[i].typ,StructElems[i].full);
+
+ SendMessage(wnd,CB_SETCURSEL,0,0);
+end;
+
+procedure FillAlignTypeList(wnd:HWND);
+begin
+ SendMessage(wnd,CB_RESETCONTENT,0,0);
+
+ InsertString(wnd,0,'Native' );
+ InsertString(wnd,1,'Packed' );
+ InsertString(wnd,2,'2 bytes');
+ InsertString(wnd,4,'4 bytes');
+ InsertString(wnd,8,'8 bytes');
+
+ SendMessage(wnd,CB_SETCURSEL,0,0);
+end;
+
+//----- Data show -----
+
+function InsertLVLine(list:HWND):integer;
+var
+ li:TLVITEMW;
+begin
+ li.mask :=0;//LVIF_PARAM;
+ li.iItem :=SendMessage(list,LVM_GETNEXTITEM,-1,LVNI_FOCUSED)+1;
+ li.iSubItem:=0;
+ result:=SendMessageW(list,LVM_INSERTITEMW,0,lparam(@li));
+end;
+
+// fill table line by data from structure
+procedure FillLVLine(list:HWND;item:integer;const element:tOneElement);
+var
+ tmp1:array [0..31] of WideChar;
+ li:TLVITEMW;
+ i,llen:integer;
+ p,pc:pAnsiChar;
+ pw:pWideChar;
+begin
+ if (element.flags and EF_RETURN)<>0 then
+ ListView_SetCheckState(list,item,true);
+
+ li.iItem:=item;
+ li.mask:=LVIF_TEXT;
+
+ // type
+ p:=StructElems[GetTypeIndex(element.etype)].short;
+ llen:=0;
+ while p^<>#0 do
+ begin
+ tmp1[llen]:=WideChar(p^);
+ inc(p);
+ inc(llen);
+ end;
+ tmp1[llen]:=#0;
+ li.iSubItem:=col_type;
+ li.pszText :=@tmp1;
+ SendMessageW(list,LVM_SETITEMW,0,lparam(@li));
+
+ // flags
+{$IFDEF Miranda}
+ llen:=0;
+ if (element.flags and EF_SCRIPT)<>0 then
+ begin
+ tmp1[llen]:=char_script; inc(llen);
+ end;
+ if (element.flags and EF_MMI)<>0 then
+ begin
+ tmp1[llen]:=char_mmi; inc(llen);
+ end;
+ tmp1[llen]:=#0;
+ li.iSubItem:=col_flag;
+ li.pszText :=@tmp1;
+ SendMessageW(list,LVM_SETITEMW,0,lparam(@li));
+{$ENDIF}
+
+ // alias
+ if element.alias[0]<>#0 then
+ begin
+ pc:=@element.alias;
+ while pc^<>#0 do
+ begin
+ tmp1[llen]:=WideChar(pc^);
+ inc(llen);
+ inc(pc);
+ end;
+ tmp1[llen]:=#0;
+ li.iSubItem:=col_alias;
+ li.pszText :=@tmp1;
+ SendMessageW(list,LVM_SETITEMW,0,lparam(@li));
+ end;
+
+ case element.etype of
+ SST_LAST,SST_PARAM: begin
+ llen:=0;
+ end;
+
+ SST_BYTE,SST_WORD,SST_DWORD,
+ SST_QWORD,SST_NATIVE: begin
+{$IFDEF Miranda}
+ if (element.flags and EF_SCRIPT)<>0 then
+ begin
+ li.iSubItem:=col_data;
+ UTF8ToWide(element.text,pw);
+ llen:=StrLenW(pw);
+ li.pszText :=pw;
+ SendMessageW(list,LVM_SETITEMW,0,lparam(@li));
+ mFreeMem(pw);
+ end
+ else
+{$ENDIF}
+ begin
+ pc:=@element.svalue;
+ llen:=0;
+ while pc^<>#0 do
+ begin
+ tmp1[llen]:=WideChar(pc^);
+ inc(llen);
+ inc(pc);
+ end;
+ if llen>0 then //??
+ begin
+ tmp1[llen]:=#0;
+ li.iSubItem:=col_data;
+ li.pszText :=@tmp1;
+ SendMessageW(list,LVM_SETITEMW,0,lparam(@li));
+ end;
+ end;
+ end;
+
+ SST_BARR,SST_WARR,SST_BPTR,SST_WPTR: begin
+ // like for numbers, array length
+ if element.len>0 then //??
+ begin
+ IntToStr(tmp1,element.len);
+ li.iSubItem:=col_len;
+ li.pszText :=@tmp1;
+ SendMessageW(list,LVM_SETITEMW,0,lparam(@li));
+ end;
+
+ if element.text<>nil then
+ begin
+ li.iSubItem:=col_data;
+ if (element.etype in [SST_WARR,SST_WPTR])
+{$IFDEF Miranda}
+ or ((element.flags and EF_SCRIPT)<>0)
+{$ENDIF}
+ then
+ begin
+ UTF8ToWide(element.text,pw);
+ end
+ else
+ begin
+ AnsiToWide(element.text,pw);
+ end;
+ li.pszText :=pw;
+ llen:=StrLenW(pw);
+ SendMessageW(list,LVM_SETITEMW,0,lparam(@li));
+ mFreeMem(pw);
+ end;
+ end;
+ end;
+
+ i:=element.etype+(llen shl 16);
+ LV_SetLParam(list,i,item);
+
+ ListView_SetItemState(list,item,LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+end;
+
+// Fill table by structure
+procedure FillLVStruct(list:HWND;txt:PAnsiChar);
+var
+ p:pansiChar;
+ element:tOneElement;
+begin
+ SendMessage(list,LVM_DELETEALLITEMS,0,0);
+ if txt^ in sNum then
+ txt:=StrScan(txt,char_separator)+1;
+ while txt^<>#0 do
+ begin
+ p:=StrScan(txt,char_separator);
+ GetOneElement(txt,element,false);
+ FillLVLine(list,InsertLVLine(list),element);
+ FreeElement(element);
+
+ if p=nil then break;
+ txt:=p+1;
+ end;
+ ListView_SetItemState(list,0,LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+end;
+
+//----- Data save -----
+
+function GetLVRow(var dst:pAnsiChar;list:HWND;item:integer):integer;
+var
+ li:TLVITEMW;
+ buf:array [0..63] of WideChar;
+ pc:pWideChar;
+ pc1:pAnsiChar;
+ len:integer;
+ {$IFDEF Miranda}isScript:boolean;{$ENDIF}
+begin
+ li.iItem:=item;
+
+ // result value check and element type
+ li.mask :=LVIF_PARAM or LVIF_STATE;
+ li.iSubItem :=0;
+ li.stateMask :=LVIS_STATEIMAGEMASK;
+ SendMessageW(list,LVM_GETITEMW,item,lparam(@li));
+ result:=loword(li.lParam); // element type
+ len :=hiword(li.lParam); // text length
+
+ if (li.state shr 12)>1 then // "return" value
+ begin
+ dst^:=char_return;
+ inc(dst);
+ end;
+
+{$IFDEF Miranda}
+ li.mask :=LVIF_TEXT;
+ li.iSubItem :=col_flag;
+ li.cchTextMax:=32;
+ li.pszText :=@buf;
+ isScript:=false;
+ if SendMessage(list,LVM_GETITEMTEXTW,item,lparam(@li))>0 then
+ begin
+ if StrScanW(buf,char_script)<>nil then
+ begin
+ dst^:=char_script;
+ inc(dst);
+ isScript:=true;
+ end;
+
+ if StrScanW(buf,char_mmi)<>nil then
+ begin
+ dst^:=char_mmi;
+ inc(dst);
+ end;
+ end;
+{$ENDIF}
+{
+ // type text (can skip and use type code)
+ li.mask :=LVIF_TEXT;
+ li.cchTextMax:=HIGH(buf);
+ li.pszText :=@buf;
+ li.iSubItem :=col_type;
+ SendMessageW(list,LVM_GETITEMTEXTW,item,lparam(@li));
+ dst:=StrEnd(FastWideToAnsiBuf(@buf,dst));
+}
+ dst:=StrCopyE(dst,StructElems[GetTypeIndex(result)].short);
+ // alias
+ li.mask :=LVIF_TEXT;
+ li.cchTextMax:=HIGH(buf);
+ li.pszText :=@buf;
+
+ li.iSubItem :=col_alias;
+ if SendMessageW(list,LVM_GETITEMTEXTW,item,lparam(@li))>0 then
+ begin
+ dst^:=' '; inc(dst);
+ pc:=@buf;
+ while pc^<>#0 do
+ begin
+ dst^:=AnsiChar(pc^); inc(dst); inc(pc);
+ end;
+ end;
+
+ case result of
+ SST_LAST,SST_PARAM: exit;
+
+ SST_BYTE,SST_WORD,SST_DWORD,
+ SST_QWORD,SST_NATIVE: begin
+ li.iSubItem :=col_data;
+ li.cchTextMax:=32;
+{$IFDEF Miranda}
+ if isScript then
+ begin
+ mGetMem(pc,(len+1)*SizeOf(WideChar));
+ li.pszText:=pc;
+ end
+ else
+{$ENDIF}
+ li.pszText :=@buf;
+ if SendMessage(list,LVM_GETITEMTEXTW,item,lparam(@li))>0 then
+ begin
+ dst^:=' '; inc(dst);
+{$IFDEF Miranda}
+ if isScript then
+ begin
+ WideToUTF8(pc,pc1);
+ dst:=StrCopyE(dst,pc1);
+ mFreeMem(pc1);
+ mFreeMem(pc);
+ end
+ else
+{$ENDIF}
+ begin
+ pc:=@buf;
+ while pc^<>#0 do
+ begin
+ dst^:=AnsiChar(pc^); inc(dst); inc(pc);
+ end;
+// StrCopyW(dst,buf);
+ end;
+ end;
+ end;
+
+ SST_BARR,SST_WARR,SST_BPTR,SST_WPTR: begin
+ // length
+ li.iSubItem :=col_len;
+ li.cchTextMax:=32;
+ li.pszText :=@buf;
+ if SendMessage(list,LVM_GETITEMTEXTW,item,lparam(@li))>0 then
+ begin
+ dst^:=' '; inc(dst);
+ pc:=@buf;
+ while pc^<>#0 do
+ begin
+ dst^:=AnsiChar(pc^); inc(dst); inc(pc);
+ end;
+ end
+ else
+ dst:=StrEnd(IntToStr(dst,len));
+
+ if len>0 then
+ begin
+// dst:=StrEnd(dst);
+ li.iSubItem :=col_data;
+ li.cchTextMax:=len+1;
+ mGetMem(pc,(len+1)*SizeOf(WideChar));
+ li.pszText :=pc;
+ SendMessage(list,LVM_GETITEMTEXTW,item,lparam(@li));
+ if pc^<>#0 then
+ begin
+ dst^:=' '; inc(dst);
+ if (result in [SST_WARR,SST_WPTR])
+{$IFDEF Miranda}
+ or isScript
+{$ENDIF}
+ then
+ WideToUTF8(pc,pc1)
+ else
+ WideToAnsi(pc,pc1);
+
+ dst:=StrCopyE(dst,pc1);
+ mFreeMem(pc1);
+ end;
+ mFreeMem(pc);
+ end;
+ end;
+ end;
+// dst:=StrEnd(dst);
+end;
+
+function SaveStructure(list:HWND;align:integer):pAnsiChar;
+var
+ p:PAnsiChar;
+ i:integer;
+begin
+ mGetMem(p,32768);
+ result:=p;
+ FillChar(p^,32768,0);
+ IntToStr(result,align);
+ inc(result);
+ result^:=char_separator;
+ inc(result);
+
+ for i:=0 to SendMessage(list,LVM_GETITEMCOUNT,0,0)-1 do
+ begin
+ GetLVRow(result,list,i);
+ result^:=char_separator; inc(result);
+ end;
+ dec(result); result^:=#0;
+ i:=(result+2-p);
+ mGetMem(result,i);
+ move(p^,result^,i);
+ mFreeMem(p);
+end;
+{$IFDEF Miranda}
+function StructEditDlgResizer(Dialog:HWND;lParam:LPARAM;urc:PUTILRESIZECONTROL):int; cdecl;
+begin
+ case urc^.wId of
+ IDC_DATA_FULL: result:=RD_ANCHORX_WIDTH or RD_ANCHORY_HEIGHT;
+ IDC_DATA_TMPL: result:=RD_ANCHORX_WIDTH or RD_ANCHORY_BOTTOM;
+ IDC_DATA_EDIT: result:=RD_ANCHORX_RIGHT or RD_ANCHORY_HEIGHT;
+ IDC_DATA_EDTN: result:=RD_ANCHORX_RIGHT or RD_ANCHORY_TOP;
+ IDC_DATA_TYPE: result:=RD_ANCHORX_RIGHT or RD_ANCHORY_TOP;
+ IDC_DATA_LEN: result:=RD_ANCHORX_RIGHT or RD_ANCHORY_TOP;
+ IDC_DATA_SLEN: result:=RD_ANCHORX_RIGHT or RD_ANCHORY_TOP;
+
+ IDC_DATA_VARS: result:=RD_ANCHORX_RIGHT or RD_ANCHORY_BOTTOM;
+ IDC_DATA_MMI: result:=RD_ANCHORX_RIGHT or RD_ANCHORY_BOTTOM;
+
+ IDC_DATA_NEW: result:=RD_ANCHORX_RIGHT or RD_ANCHORY_TOP;
+ IDC_DATA_UP: result:=RD_ANCHORX_RIGHT or RD_ANCHORY_TOP;
+ IDC_DATA_DOWN: result:=RD_ANCHORX_RIGHT or RD_ANCHORY_TOP;
+ IDC_DATA_DELETE: result:=RD_ANCHORX_RIGHT or RD_ANCHORY_TOP;
+ IDC_DATA_INFO: result:=RD_ANCHORX_RIGHT or RD_ANCHORY_BOTTOM;
+ IDC_DATA_PASTE: result:=RD_ANCHORX_RIGHT or RD_ANCHORY_BOTTOM;
+
+ IDC_DATA_ALIGN : result:=RD_ANCHORX_RIGHT or RD_ANCHORY_TOP;
+ IDC_DATA_SALGN : result:=RD_ANCHORX_RIGHT or RD_ANCHORY_TOP;
+ IDC_DATA_SEP : result:=RD_ANCHORX_RIGHT or RD_ANCHORY_TOP;
+
+ IDC_DATA_CHANGE: result:=RD_ANCHORX_RIGHT or RD_ANCHORY_BOTTOM;
+ IDOK: result:=RD_ANCHORX_RIGHT or RD_ANCHORY_BOTTOM;
+ IDCANCEL: result:=RD_ANCHORX_RIGHT or RD_ANCHORY_BOTTOM;
+ else
+ result:=0;
+ end;
+end;
+{$ENDIF}
+procedure CheckReturns(wnd:HWND;item:integer);
+var
+ li:TLVITEMW;
+ i:integer;
+begin
+ li.mask :=LVIF_STATE;
+ li.iSubItem :=0;
+ li.stateMask:=LVIS_STATEIMAGEMASK;
+ li.state :=1 shl 12;
+ for i:=0 to SendMessageW(wnd,LVM_GETITEMCOUNT,0,0)-1 do
+ begin
+ if i<>item then
+ begin
+ SendMessageW(wnd,LVM_SETITEMSTATE,i,lparam(@li));
+{
+ li.iItem:=i;
+ SendMessageW(list,LVM_GETITEMSTATE,i,dword(@li));
+ if (li.state shr 12)>1 then
+ begin
+ li.state:=1 shl 12;
+ SendMessageW(wnd,LVM_SETITEMSTATE,i,dword(@li));
+ end;
+}
+ end;
+ end;
+end;
+
+// enable/disable navigation chain buttons
+procedure CheckList(Dialog:HWND; num:integer=-1);
+begin
+ if num<0 then
+ num:=SendDlgItemMessage(Dialog,IDC_DATA_FULL,LVM_GETNEXTITEM,WPARAM(-1),LVNI_FOCUSED);
+ EnableWindow(GetDlgItem(Dialog,IDC_DATA_UP),num>0);
+ EnableWindow(GetDlgItem(Dialog,IDC_DATA_DOWN),
+ (num+1)<SendDlgItemMessage(Dialog,IDC_DATA_FULL,LVM_GETITEMCOUNT,0,0));
+end;
+
+procedure FillLVData(Dialog:HWND;list:HWND;item:integer);
+var
+ buf:array [0..15] of WideChar;
+ i:integer;
+ p:pWideChar;
+ b,b1:boolean;
+ idcshow,idchide:integer;
+ li:TLVITEMW;
+ {$IFDEF Miranda}vflag,mflag,{$ENDIF}
+ len:integer;
+ wnd:HWND;
+begin
+ len:=LV_GetLParam(list,item);
+ i :=loword(len);
+ len:=hiword(len);
+ idcshow:=IDC_DATA_EDIT;
+ idchide:=IDC_DATA_EDTN;
+
+ buf[0]:=#0;
+ case i of
+ SST_BYTE,SST_WORD,SST_DWORD,
+ SST_QWORD,SST_NATIVE: begin
+ idchide:=IDC_DATA_EDIT;
+ idcshow:=IDC_DATA_EDTN;
+ b :=true;
+ b1:=false;
+ end;
+
+ SST_BARR,SST_WARR,SST_BPTR,SST_WPTR: begin
+ b :=true;
+ b1:=true;
+
+ li.iSubItem :=col_len;
+ li.cchTextMax:=15;
+ li.pszText :=@buf;
+ SendMessage(list,LVM_GETITEMTEXTW,item,lparam(@li));
+ end;
+ else
+ b :=false;
+ b1:=false;
+ end;
+ SetDlgItemTextW(Dialog,IDC_DATA_LEN,@buf);
+
+ p:=@buf;
+ li.cchTextMax:=15;
+ li.pszText :=@buf;
+ if b then
+ begin
+{$IFDEF Miranda}
+ li.iSubItem:=col_flag;
+ vflag:=BST_UNCHECKED;
+ i:=SW_HIDE;
+ mflag:=BST_UNCHECKED;
+ if SendMessage(list,LVM_GETITEMTEXTW,item,lparam(@li))>0 then
+ begin
+ if StrScanW(p,char_script)<>nil then
+ begin
+ b1:=true;
+ vflag:=BST_CHECKED;
+ i:=SW_SHOW;
+
+ idchide:=IDC_DATA_EDTN;
+ idcshow:=IDC_DATA_EDIT;
+ end;
+
+ if StrScanW(p,char_mmi)<>nil then
+ mflag:=BST_CHECKED;
+ end;
+ ShowWindow(GetDlgItem(Dialog,IDC_VAR_HELP),i);
+ CheckDlgButton(Dialog,IDC_DATA_VARS,vflag);
+ CheckDlgButton(Dialog,IDC_DATA_MMI ,mflag);
+{$ENDIF}
+ if b1 then
+ begin
+ mGetMem(p,(len+1)*SizeOf(WideChar));
+ li.cchTextMax:=len+1;
+ li.pszText :=p;
+ end;
+ li.iSubItem:=col_data;
+ SendMessage(list,LVM_GETITEMTEXTW,item,lparam(@li));
+ end;
+ SetDlgItemTextW(Dialog,idchide,'');
+ SetDlgItemTextW(Dialog,idcshow,p);
+
+ if b1 then
+ mFreeMem(p);
+
+ wnd:=GetDlgItem(Dialog,IDC_DATA_TYPE);
+ CB_SelectData(wnd,i);
+ SendMessage(Dialog,WM_COMMAND,(CBN_SELENDOK shl 16)+IDC_DATA_TYPE,wnd);
+end;
+
+// Fill table row by data from edit fields
+procedure FillLVRow(Dialog:hwnd;list:HWND;item:integer);
+var
+ ltype,j,idc:integer;
+{$IFDEF Miranda}
+ idx:integer;
+{$ENDIF}
+ wnd:HWND;
+ buf:array [0..63] of WideChar;
+ tmp:pWideChar;
+begin
+ // type
+ wnd:=GetDlgItem(Dialog,IDC_DATA_TYPE);
+ ltype:=SendMessage(wnd,CB_GETITEMDATA,SendMessage(wnd,CB_GETCURSEL,0,0),0);
+ j:=GetTypeIndex(ltype);
+
+ LV_SetItemW(list,FastAnsiToWideBuf(StructElems[j].short,buf),item,col_type);
+
+ // flags
+{$IFDEF Miranda}
+ idx:=0;
+ if IsDlgButtonChecked(Dialog,IDC_DATA_VARS)<>BST_UNCHECKED then
+ begin
+ buf[idx]:=char_script; inc(idx);
+ end;
+
+ if IsDlgButtonChecked(Dialog,IDC_DATA_MMI)<>BST_UNCHECKED then
+ begin
+ buf[idx]:=char_mmi; inc(idx);
+ end;
+ buf[idx]:=#0;
+ LV_SetItemW(list,@buf,item,col_flag);
+{$ENDIF}
+
+ // values
+ tmp:=nil;
+ case ltype of
+ SST_LAST,SST_PARAM: begin
+ j:=0;
+ end;
+
+ SST_BYTE,SST_WORD,SST_DWORD,
+ SST_QWORD,SST_NATIVE: begin
+{$IFDEF Miranda}
+ if IsDlgButtonChecked(Dialog,IDC_DATA_VARS)<>BST_UNCHECKED then
+ idc:=IDC_DATA_EDIT
+ else
+{$ENDIF}
+ idc:=IDC_DATA_EDTN;
+
+ tmp:=GetDlgText(Dialog,idc);
+ j:=StrLenW(tmp);
+ LV_SetItemW(list,tmp,item,col_data);
+ end;
+
+ SST_BARR,SST_WARR,SST_BPTR,SST_WPTR: begin
+
+ SendDlgItemMessageW(Dialog,IDC_DATA_LEN,WM_GETTEXT,15,lparam(@buf));
+ LV_SetItemW(list,buf,item,col_len);
+
+ tmp:=GetDlgText(Dialog,IDC_DATA_EDIT);
+ j:=StrLenW(tmp);
+ LV_SetItemW(list,tmp,item,col_data);
+ end;
+ end;
+ ltype:=ltype or (j shl 16);
+ mFreeMem(tmp);
+ LV_SetLParam(list,ltype,item);
+end;
+
+{$IFDEF Miranda}
+procedure FillTemplates(wnd:HWND;lstorage:pointer);
+var
+ p,pp:pAnsiChar;
+ i:integer;
+begin
+ SendMessage(wnd,CB_RESETCONTENT,0,0);
+
+ p:=GetSectionList(lstorage,namespace);
+ pp:=p;
+ i:=0;
+ while p^<>#0 do
+ begin
+ CB_AddStrData(wnd,p,int_ptr(SearchSection(lstorage,p,namespace)), i);
+
+ while p^<>#0 do inc(p); inc(p);
+ inc(i);
+ end;
+ FreeSectionList(pp);
+ if i>0 then
+ SendMessage(wnd,CB_SETCURSEL,0,0);
+end;
+{$ENDIF}
+
+procedure ReadableForm(wnd:HWND; struct:pAnsiChar);
+var
+ p,pc,buf:pAnsiChar;
+ element:tOneElement;
+begin
+ GetMem(buf,StrLen(struct)*2);
+ pc:=buf;
+
+ struct:=StrScan(struct,char_separator)+1;
+ while struct^<>#0 do
+ begin
+ p:=StrScan(struct,char_separator);
+ GetOneElement(struct,element,false);
+
+ pc:=StrCopyE(pc,element.talias);
+ if element.etype in [SST_BARR,SST_WARR] then
+ begin
+ pc^:=' '; inc(pc);
+ pc^:='['; inc(pc);
+ pc:=StrEnd(IntToStr(pc,element.len));
+ pc^:=']'; inc(pc);
+ end;
+ pc^:=' '; inc(pc);
+ pc:=StrCopyE(pc,element.alias);
+{
+// if (element.typ IN [SST_BYTE,SST_WORD,SST_DWORD, SST_QWORD, SST_NATIVE]) then
+ pc^:=' '; inc(pc);
+ pc^:='='; inc(pc);
+ pc^:=' '; inc(pc);
+ pc:=StrCopyE(pc,element.sValue);
+}
+ pc^:=#13; inc(pc);
+ pc^:=#10; inc(pc);
+
+ FreeElement(element);
+
+ if p=nil then break;
+ struct:=p+1;
+ end;
+ pc^:=#0;
+
+ SendMessageA(wnd,WM_SETTEXT,0,LPARAM(buf));
+ FreeMem(buf);
+end;
+
+function StructHelp(Dialog:HWnd;hMessage:uint;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+var
+ tmp:pWideChar;
+ pc:pAnsiChar;
+begin
+ result:=0;
+ case hMessage of
+ WM_CLOSE: begin
+ DestroyWindow(Dialog); //??
+ end;
+
+ WM_INITDIALOG: begin
+{$IFDEF Miranda}
+ TranslateDialogDefault(Dialog);
+{$ENDIF}
+ result:=1;
+
+ if lParam<>0 then
+ begin
+ SetDlgItemTextA(Dialog,IDC_HLP_NAME,GetSectionName(pointer(lParam)));
+
+ SetDlgItemTextA(Dialog,IDC_HLP_PLUGIN,GetParamSectionStr(pointer(lParam),'plugin'));
+
+ FastAnsiToWide(GetParamSectionStr(pointer(lParam),'descr','Undefined'),tmp);
+ SetDlgItemTextW(Dialog,IDC_HLP_DESCR,{$IFDEF Miranda}TranslateW{$ENDIF}(tmp));
+ mFreeMem(tmp);
+
+ pc:=GetParamSectionStr(pointer(lParam),'full',nil);
+ if pc=nil then
+ pc:=GetParamSectionStr(pointer(lParam),'short',nil);
+ if pc<>nil then
+ ReadableForm(GetDlgItem(Dialog,IDC_HLP_STRUCT),pc);
+ end;
+ end;
+
+ WM_COMMAND: begin
+ if (wParam shr 16)=BN_CLICKED then
+ begin
+ case loword(wParam) of
+ IDOK,IDCANCEL: begin
+ DestroyWindow(Dialog);
+ end;
+ end;
+ end;
+ end;
+ else
+//!! result:=DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+end;
+
+function StructEdit(Dialog:HWnd;hMessage:uint;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+var
+ wnd:HWND;
+ i:integer;
+ li:TLVITEMW;
+ b,b1:boolean;
+ idchide,idcshow:integer;
+{$IFDEF Miranda}
+ pc:pAnsiChar;
+ urd:TUTILRESIZEDIALOG;
+{$ELSE}
+ rc,rc1:TRECT;
+{$ENDIF}
+begin
+ result:=0;
+ case hMessage of
+
+ WM_DESTROY: begin
+{$IFDEF Miranda}
+ CloseStorage(storage);
+{$ENDIF}
+ end;
+
+ WM_INITDIALOG: begin
+ result:=1;
+{$IFDEF Miranda}
+ TranslateDialogDefault(Dialog);
+ RegisterIcons;
+ storage:=OpenStorage(API_STRUCT_FILE);
+ if storage<>nil then
+ FillTemplates(GetDlgItem(Dialog,IDC_DATA_TMPL),storage);
+ if isVarsInstalled then
+ SendDlgItemMessage(Dialog,IDC_VAR_HELP,BM_SETIMAGE,IMAGE_ICON,
+ CallService(MS_VARS_GETSKINITEM,0,VSI_HELPICON));
+{$ENDIF}
+ wnd:=GetDlgItem(Dialog,IDC_DATA_FULL);
+ MakeLVStructList(wnd);
+ SetDataButtonIcons(Dialog);
+ FillDataTypeList (GetDlgItem(Dialog,IDC_DATA_TYPE));
+ FillAlignTypeList(GetDlgItem(Dialog,IDC_DATA_ALIGN));
+ if lParam<>0 then
+ begin
+ FillLVStruct(wnd,pAnsiChar(lParam)) // fill lv with current structure
+ end
+ else
+ SendMessage(Dialog,WM_COMMAND,(CBN_SELCHANGE shl 16)+IDC_DATA_TYPE,
+ GetDlgItem(Dialog,IDC_DATA_TYPE));
+ CheckList(Dialog,-1);
+ SendMessage(Dialog,WM_COMMAND,(CBN_SELENDOK shl 16)+IDC_DATA_TYPE,
+ GetDlgItem(Dialog,IDC_DATA_TYPE));
+ end;
+
+ WM_GETMINMAXINFO: begin
+ with PMINMAXINFO(lParam)^ do
+ begin
+ ptMinTrackSize.x:=500;
+ ptMinTrackSize.y:=300;
+ end;
+ end;
+
+ WM_SIZE: begin
+{$IFDEF Miranda}
+ FillChar(urd,SizeOf(TUTILRESIZEDIALOG),0);
+ urd.cbSize :=SizeOf(urd);
+ urd.hwndDlg :=Dialog;
+ urd.hInstance :=hInstance;
+ urd.lpTemplate:='IDD_STRUCTURE';//MAKEINTRESOURCEA(IDD_STRUCTURE);
+ urd.lParam :=0;
+ urd.pfnResizer:=@StructEditDlgResizer;
+ CallService(MS_UTILS_RESIZEDIALOG,0,tlparam(@urd));
+{$ELSE}
+ GetWindowRect(Dialog,rc);
+
+ wnd:=GetDlgItem(Dialog,IDC_DATA_EDIT);
+ GetWindowRect(wnd,rc1);
+ SetWindowPos(wnd,0,0,0,rc.right-rc1.left-8,rc1.bottom-rc1.top,
+ SWP_NOMOVE or SWP_NOZORDER or SWP_SHOWWINDOW);
+
+ wnd:=GetDlgItem(Dialog,IDC_DATA_FULL);
+ GetWindowRect(wnd,rc1);
+ SetWindowPos(wnd,0,0,0,rc1.right-rc1.left, rc.bottom-rc1.top-8,
+ SWP_NOMOVE or SWP_NOZORDER or SWP_SHOWWINDOW);
+{$ENDIF}
+ end;
+
+ WM_COMMAND: begin
+ case wParam shr 16 of
+
+ CBN_SELENDOK{CBN_SELCHANGE}: begin
+ case loword(wParam) of
+{$IFDEF Miranda}
+ IDC_DATA_TMPL: begin
+ end;
+{$ENDIF}
+ IDC_DATA_TYPE: begin
+ i:=CB_GetData(lParam);
+ case i of
+ SST_LAST,SST_PARAM: begin
+ b :=false;
+ b1:=false;
+ end;
+
+ SST_BYTE,SST_WORD,SST_DWORD,
+ SST_QWORD,SST_NATIVE: begin
+ b :=true;
+ b1:=false;
+ end;
+
+ SST_BARR,SST_WARR,SST_BPTR,SST_WPTR: begin
+ b :=true;
+ b1:=true;
+ end;
+ else
+ b :=false;
+ b1:=false;
+ end;
+ EnableWindow(GetDlgItem(Dialog,IDC_DATA_EDIT),b);
+ EnableWindow(GetDlgItem(Dialog,IDC_DATA_EDTN),b);
+ EnableWindow(GetDlgItem(Dialog,IDC_DATA_LEN ),b1);
+
+ if b then
+ begin
+ if i IN [SST_BYTE,SST_WORD,SST_DWORD,SST_QWORD,SST_NATIVE] then
+ begin
+{$IFDEF Miranda}
+ if IsDlgButtonChecked(Dialog,IDC_DATA_VARS)<>BST_UNCHECKED then
+ begin
+ idchide:=IDC_DATA_EDTN;
+ idcshow:=IDC_DATA_EDIT;
+ end
+ else
+{$ENDIF}
+ begin
+ idchide:=IDC_DATA_EDIT;
+ idcshow:=IDC_DATA_EDTN;
+ end;
+ end
+ else
+ begin
+ idchide:=IDC_DATA_EDTN;
+ idcshow:=IDC_DATA_EDIT;
+ end;
+ ShowWindow(GetDlgItem(Dialog,idcshow),SW_SHOW);
+ ShowWindow(GetDlgItem(Dialog,idchide),SW_HIDE);
+ end;
+
+{$IFDEF Miranda}
+ if i IN [SST_PARAM,SST_LAST] then
+ ShowWindow(GetDlgItem(Dialog,IDC_DATA_VARS),SW_HIDE)
+ else
+ ShowWindow(GetDlgItem(Dialog,IDC_DATA_VARS),SW_SHOW);
+
+ if i IN [SST_BPTR,SST_WPTR] then
+ ShowWindow(GetDlgItem(Dialog,IDC_DATA_MMI),SW_SHOW)
+ else
+ ShowWindow(GetDlgItem(Dialog,IDC_DATA_MMI),SW_HIDE);
+{$ENDIF}
+ end;
+ end;
+ end;
+
+ BN_CLICKED: begin
+ case loword(wParam) of
+{$IFDEF Miranda}
+ IDC_DATA_INFO: begin
+ CreateDialogParamW(hInstance,'IDD_STRUCTHELP',//MAKEINTRESOURCEW(IDD_HELP),
+ 0{Dialog},@StructHelp,CB_GetData(GetDlgItem(Dialog,IDC_DATA_TMPL)));
+ end;
+
+ IDC_DATA_PASTE: begin
+ wnd:=GetDlgItem(Dialog,IDC_DATA_TMPL);
+ pc:=GetParamSectionStr(pointer(CB_GetData(wnd)),'full',nil);
+ if pc=nil then
+ pc:=GetParamSectionStr(pointer(CB_GetData(wnd)),'short',nil);
+ if pc<>nil then
+ begin
+ FillLVStruct(GetDlgItem(Dialog,IDC_DATA_FULL),pc); // fill lv with current structure
+ end;
+ end;
+
+ IDC_VAR_HELP: begin
+ ShowVarHelp(Dialog,IDC_DATA_EDIT);
+ end;
+ IDC_DATA_VARS: begin
+ if (not isVarsInstalled) or
+ (IsDlgbuttonChecked(Dialog,IDC_DATA_VARS)=BST_UNCHECKED) then
+ idcshow:=SW_HIDE
+ else
+ idcshow:=SW_SHOW;
+ ShowWindow(GetDlgItem(Dialog,IDC_VAR_HELP),idcshow);
+
+
+ i:=CB_GetData(GetDlgItem(Dialog,IDC_DATA_TYPE));
+ if i IN [SST_BYTE,SST_WORD,SST_DWORD,SST_QWORD,SST_NATIVE] then
+ begin
+ if IsDlgButtonChecked(Dialog,IDC_DATA_VARS)<>BST_UNCHECKED then
+ begin
+ idchide:=IDC_DATA_EDTN;
+ idcshow:=IDC_DATA_EDIT;
+ end
+ else
+ begin
+ idchide:=IDC_DATA_EDIT;
+ idcshow:=IDC_DATA_EDTN;
+ end;
+ ShowWindow(GetDlgItem(Dialog,idcshow),SW_SHOW);
+ ShowWindow(GetDlgItem(Dialog,idchide),SW_HIDE);
+ end;
+ end;
+{$ENDIF}
+ IDC_DATA_NEW: begin
+ wnd:=GetDlgItem(Dialog,IDC_DATA_FULL);
+ i:=InsertLVLine(wnd);
+ FillLVRow(Dialog,wnd,i);
+ EnableWindow(GetDlgItem(Dialog,IDC_DATA_DELETE),true);
+// CheckList(Dialog,i);
+// if SendMessage(wnd,LVM_GETITEMCOUNT,0,0)=1 then
+ begin
+ li.mask :=LVIF_STATE;
+ li.iItem :=i;
+ li.iSubItem :=0;
+ li.StateMask:=LVIS_FOCUSED+LVIS_SELECTED;
+ li.State :=LVIS_FOCUSED+LVIS_SELECTED;
+ SendMessageW(wnd,LVM_SETITEMW,0,tlparam(@li));
+ end;
+ CheckList(Dialog);
+ end;
+
+ IDC_DATA_DELETE: begin
+ wnd:=GetDlgItem(Dialog,IDC_DATA_FULL);
+ i:=SendMessage(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED); //??
+ if i<>-1 then
+ begin
+ SendMessage(wnd,LVM_DELETEITEM,i,0);
+ CheckList(Dialog,-1);
+ end;
+
+// SendMessageW(Dialog,LVM_DELETEITEM,ListView_GetNextItem(Dialog,-1,LVNI_FOCUSED),0);
+//select next and set field (auto?)
+{
+ i:=SendMessage(wnd,LVM_GETITEMCOUNT,0,0);
+ if i>0 then
+ begin
+ if next=i then
+ dec(next);
+ ListView_SetItemState(wnd,next,LVIS_FOCUSED or LVIS_SELECTED,
+ LVIS_FOCUSED or LVIS_SELECTED);
+}
+ end;
+
+ IDC_DATA_UP: begin
+ wnd:=GetDlgItem(Dialog,IDC_DATA_FULL);
+ li.iItem:=SendMessage(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
+// if li.iItem>0 then
+ LV_MoveItem(wnd,-1,li.iItem);
+ CheckList(Dialog);
+ end;
+
+ IDC_DATA_DOWN: begin
+ wnd:=GetDlgItem(Dialog,IDC_DATA_FULL);
+ li.iItem:=SendMessage(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
+// if li.iItem<(SendMessage(wnd,LVM_GETITEMCOUNT,0,0)-1) then
+ LV_MoveItem(wnd,1,li.iItem);
+ CheckList(Dialog);
+ end;
+
+ IDOK: begin // save result
+ EndDialog(Dialog,int_ptr(
+ SaveStructure(GetDlgItem(Dialog,IDC_DATA_FULL),
+ CB_GetData(GetDlgItem(Dialog,IDC_DATA_ALIGN))
+ )));
+ end;
+
+ IDCANCEL: begin // clear result / restore old value
+ EndDialog(Dialog,0);
+ end;
+
+ IDC_DATA_CHANGE: begin
+ wnd:=GetDlgItem(Dialog,IDC_DATA_FULL);
+ if SendMessage(wnd,LVM_GETITEMCOUNT,0,0)=0 then
+ begin
+ PostMessage(Dialog,hMessage,IDC_DATA_NEW,lParam);
+ exit;
+ end;
+ i:=SendMessage(wnd,LVM_GETNEXTITEM,-1,LVNI_FOCUSED); //??
+ if i<>-1 then
+ FillLVRow(Dialog,wnd,i);
+ end;
+
+ end;
+ end;
+ end;
+ end;
+
+ WM_NOTIFY: begin
+ if integer(PNMHdr(lParam)^.code)=PSN_APPLY then
+ begin
+ end
+ else if wParam=IDC_DATA_FULL then
+ begin
+ case integer(PNMHdr(lParam)^.code) of
+ LVN_ITEMCHANGED: begin
+ i:=(PNMLISTVIEW(lParam)^.uOldState and LVNI_FOCUSED)-
+ (PNMLISTVIEW(lParam)^.uNewState and LVNI_FOCUSED);
+ if i>0 then // old focus - do nothing
+ else if i<0 then // new focus - fill fields
+ begin
+ //save
+ FillLVData(Dialog,PNMHdr(lParam)^.hwndFrom,PNMLISTVIEW(lParam)^.iItem);
+ CheckList(Dialog,PNMLISTVIEW(lParam)^.iItem);
+ end
+ else
+ begin
+ if (PNMLISTVIEW(lParam)^.uOldState or PNMLISTVIEW(lParam)^.uNewState)=$3000 then
+ begin
+ if PNMLISTVIEW(lParam)^.uOldState=$1000 then // check
+ CheckReturns(GetDlgItem(Dialog,IDC_DATA_FULL),PNMLISTVIEW(lParam)^.iItem);
+ end;
+ end;
+ end;
+
+ LVN_ENDLABELEDITW: begin
+ with PLVDISPINFO(lParam)^ do
+ begin
+ if item.pszText<>nil then
+ begin
+ item.mask:=LVIF_TEXT;
+ SendMessageW(hdr.hWndFrom,LVM_SETITEMW,0,tlparam(@item));
+ result:=1;
+ end;
+ end;
+ end;
+
+ NM_DBLCLK: begin
+ if PNMListView(lParam)^.iItem>=0 then
+ begin
+ SendMessage(PNMHdr(lParam)^.hWndFrom,LVM_EDITLABEL,
+ PNMListView(lParam)^.iItem,0);
+ end;
+ end;
+
+ end;
+ end;
+ end;
+ else
+//!! result:=DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+end;
+
+function EditStructure(struct:pAnsiChar;parent:HWND=0):pAnsiChar;
+begin
+ InitCommonControls;
+
+ result:=pAnsiChar(uint_ptr(DialogBoxParamW(hInstance,'IDD_STRUCTURE',
+ parent,@StructEdit,LPARAM(struct))));
+
+ if uint_ptr(result)=uint_ptr(-1) then
+ result:=nil;
+end;
+
+end.
diff --git a/plugins/Utils.pas/strans.pas b/plugins/Utils.pas/strans.pas
new file mode 100644
index 0000000000..b70057c33c
--- /dev/null
+++ b/plugins/Utils.pas/strans.pas
@@ -0,0 +1,828 @@
+{}
+unit strans;
+
+interface
+
+uses windows{$IFDEF Miranda}, m_api{$ENDIF};
+// <align>|[<key>]<type> [(<type alias>)] [<alias>] [arr.len] [value]|
+const
+ char_separator = '|';
+ char_hex = '$';
+ char_return = '*';
+ char_script = '%';
+{$IFDEF Miranda}
+ char_mmi = '&';
+{$ENDIF}
+const
+ SST_BYTE = 0;
+ SST_WORD = 1;
+ SST_DWORD = 2;
+ SST_QWORD = 3;
+ SST_NATIVE = 4;
+ SST_BARR = 5;
+ SST_WARR = 6;
+ SST_BPTR = 7;
+ SST_WPTR = 8;
+ SST_LAST = 9;
+ SST_PARAM = 10;
+ SST_UNKNOWN = -1;
+const
+ EF_RETURN = $00000001;
+ EF_SCRIPT = $00000002;
+ EF_MMI = $00000004;
+ EF_LAST = $00000080;
+type
+ // int_ptr = to use aligned structure data at start
+ PStructResult = ^TStructResult;
+ TStructResult = record
+ typ :int_ptr;
+ len :int_ptr;
+ offset:int_ptr;
+ end;
+type
+ TStructType = record
+ typ :integer;
+ short:PAnsiChar;
+ full :PAnsiChar;
+ end;
+const
+ MaxStructTypes = 11;
+const
+ StructElems: array [0..MaxStructTypes-1] of TStructType = (
+ (typ:SST_BYTE ; short:'byte' ; full:'Byte'),
+ (typ:SST_WORD ; short:'word' ; full:'Word'),
+ (typ:SST_DWORD ; short:'dword' ; full:'DWord'),
+ (typ:SST_QWORD ; short:'qword' ; full:'QWord'),
+ (typ:SST_NATIVE; short:'native'; full:'NativeInt'),
+ (typ:SST_BARR ; short:'b.arr' ; full:'Byte Array'),
+ (typ:SST_WARR ; short:'w.arr' ; full:'Word Array'),
+ (typ:SST_BPTR ; short:'b.ptr' ; full:'Pointer to bytes'),
+ (typ:SST_WPTR ; short:'w.ptr' ; full:'Pointer to words'),
+{$IFDEF Miranda}
+ (typ:SST_LAST ; short:'last' ; full:'Last result'),
+ (typ:SST_PARAM ; short:'param' ; full:'Parameter')
+{$ELSE}
+ (typ:SST_LAST ; short:'last' ; full:'Parameter 1'),
+ (typ:SST_PARAM ; short:'param' ; full:'Parameter 2')
+{$ENDIF}
+ );
+
+
+type
+ tOneElement = record
+ etype :integer;
+ flags :integer; // EF_MMI,EF_SCRIPT,EF_RETURN
+ len :integer; // value length (for arrays and pointers)
+ align :integer;
+ alias :array [0..63] of AnsiChar;
+ talias:array [0..63] of AnsiChar; // type alias
+ svalue:array [0..31] of AnsiChar; // numeric value text
+ case boolean of
+ false: (value:int64);
+ true : (text :pointer);
+ end;
+
+
+function GetOneElement(txt:pAnsiChar;var res:tOneElement;
+ SizeOnly:boolean;num:integer=0):integer;
+procedure FreeElement(var element:tOneElement);
+
+{$IFDEF Miranda}
+const
+ rtInt = 1;
+ rtWide = 2;
+{$ENDIF}
+
+function MakeStructure(txt:pAnsiChar;aparam,alast:LPARAM
+ {$IFDEF Miranda}; restype:integer=rtInt{$ENDIF}):pointer;
+
+function GetStructureResult(var struct;atype:pinteger=nil;alen:pinteger=nil):int_ptr;
+
+procedure FreeStructure(var struct);
+
+implementation
+
+uses common{$IFDEF Miranda}, mirutils{$ENDIF};
+
+type
+ pint_ptr = ^int_ptr;
+ TWPARAM = WPARAM;
+ TLPARAM = LPARAM;
+
+type
+ pShortTemplate = ^tShortTemplate;
+ tShortTemplate = packed record
+ etype :byte;
+ flags :byte;
+ offset:word;
+ end;
+
+// adjust offset to field
+function AdjustSize(var summ:int_ptr;eleadjust:integer;adjust:integer):integer;
+var
+ rest,lmod:integer;
+begin
+ // packed, byte or array of byte
+ if adjust=0 then
+ adjust:={$IFDEF WIN32}4{$ELSE}8{$ENDIF}; // SizeOf(int_ptr);
+
+ if (adjust=1) or (eleadjust=1) then
+ else
+ begin
+ case adjust of
+ 2: begin
+ lmod:=2;
+ end;
+ 4: begin
+ if eleadjust>2 then
+ lmod:=4
+ else
+ lmod:=2;
+ end;
+ 8: begin
+ if eleadjust>4 then
+ lmod:=8
+ else if eleadjust>2 then
+ lmod:=4
+ else
+ lmod:=2;
+ end;
+ else
+ lmod:=2;
+ end;
+ rest:=summ mod lmod;
+ if rest>0 then
+ begin
+ summ:=summ+(lmod-rest);
+ end;
+ end;
+
+ result:=summ;
+end;
+
+procedure SkipSpace(var txt:pAnsiChar); {$IFDEF FPC}inline;{$ENDIF}
+begin
+ while (txt^ in [' ',#9]) do inc(txt);
+end;
+
+function GetOneElement(txt:pAnsiChar;var res:tOneElement;
+ SizeOnly:boolean;num:integer=0):integer;
+var
+ pc,pc1:pAnsiChar;
+ i,llen:integer;
+begin
+ FillChar(res,SizeOf(res),0);
+
+ if num>0 then // Skip needed element amount
+ begin
+ end;
+
+ SkipSpace(txt);
+ // process flags
+ while not (txt^ in sWordOnly) do
+ begin
+ case txt^ of
+ char_return: res.flags:=res.flags or EF_RETURN;
+{$IFDEF Miranda}
+ char_script: res.flags:=res.flags or EF_SCRIPT;
+ char_mmi : res.flags:=res.flags or EF_MMI;
+{$ENDIF}
+ end;
+ inc(txt);
+ end;
+
+ SkipSpace(txt);
+ // element type
+ pc:=txt;
+ llen:=0;
+ repeat
+ inc(pc);
+ inc(llen);
+ until pc^ IN [#0,#9,' ',char_separator];
+ // recogninze data type
+ i:=0;
+ while i<MaxStructTypes do
+ begin
+ if StrCmp(txt,StructElems[i].short,llen)=0 then //!!
+ break;
+ inc(i);
+ end;
+ if i>=MaxStructTypes then
+ begin
+ result :=SST_UNKNOWN;
+ res.etype:=SST_UNKNOWN;
+ exit;
+ end;
+ result:=StructElems[i].typ;
+ res.etype:=result;
+
+ if (not SizeOnly) or (result in [SST_WARR,SST_BARR,SST_WPTR,SST_BPTR]) then
+ begin
+ // type alias, inside parentheses
+ SkipSpace(pc);
+ if not (pc^ in [#0,char_separator]) then
+ begin
+ if (pc^='(') and ((pc+1)^ in sIdFirst) then
+ begin
+ inc(pc); // skip space and parenthesis
+ pc1:=@res.talias;
+ repeat
+ pc1^:=pc^;
+ inc(pc1);
+ inc(pc);
+ until (pc^=')'){ or (pc^=' ')} or (pc^=char_separator);
+ if pc^=')' then inc(pc);
+ end;
+ end;
+
+ // alias, starting from letter
+ // start: points to separator or space
+ SkipSpace(pc);
+ if not (pc^ in [#0,char_separator]) then
+ begin
+ if pc^ in sIdFirst then
+ begin
+// inc(pc); // skip space
+ pc1:=@res.alias;
+ repeat
+ pc1^:=pc^;
+ inc(pc1);
+ inc(pc);
+ until (pc^=' ') or (pc^=char_separator);
+ end;
+ end;
+
+ // next - values
+ // if has empty simple value, then points to next element but text-to-number will return 0 anyway
+// if pc^=' ' then inc(pc); // points to value or nothing if no args
+ SkipSpace(pc);
+ case result of
+ SST_LAST,SST_PARAM: ;
+
+ SST_BYTE,SST_WORD,SST_DWORD,SST_QWORD,SST_NATIVE: begin
+ begin
+ if (res.flags and EF_SCRIPT)=0 then
+ begin
+ pc1:=@res.svalue;
+ if pc^=char_hex then
+ begin
+ inc(pc);
+ while pc^ in sHexNum do
+ begin
+ pc1^:=pc^;
+ inc(pc1);
+ inc(pc);
+ end;
+ res.value:=HexToInt(res.svalue)
+ end
+ else
+ begin
+ while pc^ in sNum do
+ begin
+ pc1^:=pc^;
+ inc(pc1);
+ inc(pc);
+ end;
+ res.value:=StrToInt(res.svalue);
+ end;
+ end
+ else
+ begin
+ txt:=pc;
+ //!!
+ while not (pc^ in [#0,char_separator]) do inc(pc);
+ if txt<>pc then
+ StrDup(pAnsiChar(res.text),txt,pc-txt)
+ else
+ res.text:=nil;
+ end;
+ end;
+ end;
+
+ SST_BARR,SST_WARR,SST_BPTR,SST_WPTR: begin
+ // if pc^ in sNum then
+ res.len:=StrToInt(pc);
+ if not SizeOnly then
+ begin
+ while pc^ in sNum do inc(pc);
+
+ // skip space
+// if pc^=' ' then inc(pc);
+ SkipSpace(pc);
+
+ //!!
+ if not (pc^ in [#0,char_separator]) then
+ begin
+ txt:=pc;
+ while not (pc^ in [#0,char_separator]) do inc(pc);
+ if txt<>pc then
+ StrDup(pAnsiChar(res.text),txt,pc-txt)
+ else
+ res.text:=nil;
+ end;
+ end;
+ end;
+ end;
+ end;
+
+ case result of
+ SST_LAST,
+ SST_PARAM : begin res.len:=SizeOf(LPARAM); res.align:=SizeOf(LPARAM); end;
+ SST_BYTE : begin res.len:=1; res.align:=1; end;
+ SST_WORD : begin res.len:=2; res.align:=2; end;
+ SST_DWORD : begin res.len:=4; res.align:=4; end;
+ SST_QWORD : begin res.len:=8; res.align:=8; end;
+ SST_NATIVE: begin res.len:=SizeOf(LPARAM); res.align:=SizeOf(LPARAM); end; // SizeOf(NativeInt)
+ SST_BARR : res.align:=1;
+ SST_WARR : res.align:=2;
+ SST_BPTR : res.align:=SizeOf(pointer);
+ SST_WPTR : res.align:=SizeOf(pointer);
+ end;
+end;
+
+// within translation need to check array size limit
+// "limit" = array size, elements, not bytes!
+procedure TranslateBlob(dst:pByte;const element:tOneElement);
+var
+ datatype:integer;
+ clen,len:integer;
+ src:pAnsiChar;
+ srcw:pWideChar absolute src;
+ buf:array [0..9] of AnsiChar;
+ bufw:array [0..4] of WideChar absolute buf;
+begin
+ if element.text=nil then exit;
+
+ if element.etype in [SST_WARR,SST_WPTR] then
+ begin
+ if (element.flags and EF_SCRIPT)<>0 then
+ datatype:=2 // Wide to Wide (if script done)
+ else
+ datatype:=1; // UTF to Wide
+ end
+ else // Ansi to Ansi
+ datatype:=0;
+
+ pint64(@buf)^:=0;
+
+ src:=element.text;
+ case datatype of
+ 0: begin // Ansi source for ansi
+ len:=StrLen(src);
+ if (element.len<>0) and (element.len<len) then
+ len:=element.len;
+
+ if StrScan(src,char_hex)=nil then
+ begin
+ move(src^,dst^,len);
+ end
+ else
+ begin
+ while (src^<>#0) and (len>0) do
+ begin
+ if (src^=char_hex) and ((src+1)^ in sHexNum) and ((src+2)^ in sHexNum) then
+ begin
+ buf[0]:=(src+1)^;
+ buf[1]:=(src+2)^;
+ inc(src,2+1);
+ dst^:=HexToInt(buf);
+ end
+ else
+ begin
+ dst^:=ord(src^);
+ inc(src);
+ end;
+ inc(dst);
+ dec(len);
+ end;
+ end;
+ end;
+
+ 1: begin // UTF8 source for unicode
+ // char_hex not found in UTF8 as Ansi - no reason to check as UTF8
+ // right, if char_hex is in ASCI7 only
+{
+ if StrScan(src,char_hex)=nil then
+ begin
+ // convert UTF8 to Wide without zero
+ end
+ else
+}
+ begin
+ len:=element.len;
+ while (src^<>#0) and (len>0) do
+ begin
+ if (src^=char_hex) and
+ ((src+1)^ in sHexNum) and
+ ((src+2)^ in sHexNum) then
+ begin
+ buf[0]:=(src+1)^;
+ buf[1]:=(src+2)^;
+ if ((src+3)^ in sHexNum) and
+ ((src+4)^ in sHexNum) then
+ begin
+ buf[2]:=(src+3)^;
+ buf[3]:=(src+4)^;
+ pWord(dst)^:=HexToInt(buf);
+ inc(src,4+1);
+ inc(dst,2);
+ end
+ else
+ begin
+ buf[2]:=#0;
+ dst^:=HexToInt(buf);
+ inc(dst);
+ inc(src,2+1);
+ end;
+ end
+ else
+ begin
+ pWideChar(dst)^:=CharUTF8ToWide(src,@clen);
+ inc(src,clen{CharUTF8Len(src)});
+ inc(dst,2);
+ dec(len);
+ end;
+ end;
+ end;
+ end;
+{$IFDEF Miranda}
+ 2: begin // Unicode source for unicode
+ len:=StrLenW(srcw);
+ if (element.len<>0) and (element.len<len) then
+ len:=element.len;
+
+ if StrScanW(srcw,char_hex)=nil then
+ begin
+ move(srcw^,dst^,len*SizeOf(WideChar));
+ end
+ else
+ begin
+ while (srcw^<>#0) and (len>0) do
+ begin
+ if (srcw^=char_hex) and
+ (ord(srcw[1])<255) and (AnsiChar(srcw[1]) in sHexNum) and
+ (ord(srcw[2])<255) and (AnsiChar(srcw[2]) in sHexNum) then
+ begin
+ bufw[0]:=srcw[1];
+ bufw[1]:=srcw[2];
+ if (ord(srcw[3])<255) and (AnsiChar(srcw[3]) in sHexNum) and
+ (ord(srcw[4])<255) and (AnsiChar(srcw[4]) in sHexNum) then
+ begin
+ bufw[2]:=srcw[3];
+ bufw[3]:=srcw[4];
+ pWord(dst)^:=HexToInt(bufw);
+ inc(src,4);
+ end
+ else
+ begin
+ bufw[2]:=#0;
+ dst^:=HexToInt(bufw);
+ dec(dst);
+ inc(srcw,2);
+ end;
+ end
+ else
+ pWideChar(dst)^:=srcw^;
+ inc(srcw);
+ inc(dst,2);
+ dec(len);
+ end;
+ end;
+ end;
+{$ENDIF}
+ end;
+end;
+
+procedure FreeElement(var element:tOneElement);
+begin
+ case element.etype of
+ SST_PARAM,SST_LAST: begin
+ end;
+ SST_BYTE,SST_WORD,SST_DWORD,
+ SST_QWORD,SST_NATIVE: begin
+ if (element.flags and EF_SCRIPT)<>0 then
+ mFreeMem(element.text);
+ end;
+ SST_BARR,SST_WARR,
+ SST_BPTR,SST_WPTR: begin
+ mFreeMem(element.text);
+ end;
+ end;
+end;
+
+function MakeStructure(txt:pAnsiChar;aparam,alast:LPARAM
+ {$IFDEF Miranda}; restype:integer=rtInt{$ENDIF}):pointer;
+var
+ summ:int_ptr;
+ lsrc:pAnsiChar;
+ res:pByte;
+ ppc,p,pc:pAnsiChar;
+{$IFDEF Miranda}
+ buf:array [0..31] of WideChar;
+ pLast: pWideChar;
+ valuein,value:pWideChar;
+{$ENDIF}
+ amount,align:integer;
+ lmod,code,alen,ofs:integer;
+ element:tOneElement;
+ tmpl:pShortTemplate;
+ addsize:integer;
+begin
+ result:=nil;
+ if (txt=nil) or (txt^=#0) then
+ exit;
+
+ pc:=txt;
+
+ ppc:=pc;
+ summ:=0;
+
+ if pc^ in sNum then
+ begin
+ align:=ord(pc^)-ord('0');//StrToInt(pc);
+ lsrc:=StrScan(pc,char_separator)+1; // not just +2 for future features
+ end
+ else
+ begin
+ align:=0;
+ lsrc:=pc;
+ end;
+
+ code:=SST_UNKNOWN;
+ alen:=0;
+ ofs :=0;
+
+ amount:=0;
+ // size calculation
+ while lsrc^<>#0 do
+ begin
+ p:=StrScan(lsrc,char_separator);
+// if p<>nil then p^:=#0;
+
+ GetOneElement(lsrc,element,true);
+ AdjustSize(summ,element.align,align);
+
+ if ((element.flags and EF_RETURN)<>0) and (code=SST_UNKNOWN) then
+ begin
+ code:=element.etype;
+ alen:=element.len;
+ ofs :=summ;
+ end;
+
+ if (element.etype=SST_BPTR) or (element.etype=SST_WPTR) then
+ inc(summ,SizeOf(pointer))
+ else
+ inc(summ,element.len);
+
+ inc(amount);
+
+ if p=nil then break;
+ lsrc:=p+1;
+ end;
+
+ // memory allocation with result record and template
+ addsize:=SizeOF(TStructResult)+SizeOF(tShortTemplate)*amount+SizeOf(dword);
+ lmod:=addsize mod SizeOf(pointer);
+ if lmod<>0 then
+ inc(addsize,SizeOf(pointer)-lmod);
+
+ inc(summ,addsize);
+
+ mGetMem (tmpl,summ);
+ FillChar(tmpl^,summ,0);
+
+ res:=pByte(pAnsiChar(tmpl)+addsize-SizeOf(tStructResult)-SizeOf(dword));
+ pdword(res)^:=amount; inc(res,SizeOf(dword));
+ with PStructResult(res)^ do
+ begin
+ typ :=code;
+ len :=alen;
+ offset:=ofs;
+ end;
+
+ inc(res,SizeOf(tStructResult));
+ result:=res;
+
+ pc:=ppc;
+
+ // translation
+ if pc^ in sNum then
+ // pc:=pc+2;
+ pc:=StrScan(pc,char_separator)+1;
+
+ while pc^<>#0 do
+ begin
+ p:=StrScan(pc,char_separator);
+ GetOneElement(pc,element,false);
+
+ if (element.flags and EF_SCRIPT)<>0 then
+ begin
+{$IFDEF Miranda}
+ if restype=rtInt then
+ pLast:=IntToStr(buf,alast)
+ else
+ pLast:=pWideChar(alast);
+ // BPTR,BARR - Ansi
+ // WPTR,WARR - Unicode
+ // BYTE,WORD,DWORD,QWORD,NATIVE - ???
+ // in value must be converted to unicode/ansi but not UTF8
+ UTF8ToWide(element.text,valuein);
+ value:=ParseVarString(valuein,aparam,pLast);
+ mFreeMem(valuein);
+ case element.etype of
+ // Numbers - just get number values
+ SST_BYTE,
+ SST_WORD,
+ SST_DWORD,
+ SST_QWORD,
+ SST_NATIVE: begin
+ {
+ StrCopy(element.svalue,value,31);
+ element.value:=StrToInt(element.svalue);
+ }
+ element.value:=StrToInt(value);
+ mFreeMem(value);
+ end;
+ // Byte strings - replace Ansi value
+ SST_BARR,
+ SST_BPTR: begin
+ mFreeMem(element.text);
+ WideToAnsi(value,pAnsiChar(element.text),MirandaCP);
+ mFreeMem(value);
+ end;
+ // Wide strings - replace UTF8 by Wide
+ SST_WARR,
+ SST_WPTR: begin
+ // really, need to translate Wide to UTF8 again?
+ mFreeMem(element.text);
+ element.text:=value;
+ end;
+ end;
+{$ENDIF}
+ end;
+
+ AdjustSize(int_ptr(res),element.align,align);
+
+ tmpl^.etype :=element.etype;
+ tmpl^.flags :=element.flags;
+ tmpl^.offset:=uint_ptr(res)-uint_ptr(result);
+
+ case element.etype of
+ SST_LAST: begin
+ pint_ptr(res)^:=alast;
+ end;
+ SST_PARAM: begin
+ pint_ptr(res)^:=aparam;
+ end;
+ SST_BYTE: begin
+ pByte(res)^:=element.value;
+ end;
+ SST_WORD: begin
+ pWord(res)^:=element.value;
+ end;
+ SST_DWORD: begin
+ pDWord(res)^:=element.value;
+ end;
+ SST_QWORD: begin
+ pint64(res)^:=element.value;
+ end;
+ SST_NATIVE: begin
+ pint_ptr(res)^:=element.value;
+ end;
+ SST_BARR: begin
+ TranslateBlob(pByte(res),element);
+ end;
+ SST_WARR: begin
+ TranslateBlob(pByte(res),element);
+ end;
+ SST_BPTR: begin
+ if element.len=0 then
+ element.len:=StrLen(element.text);
+
+ if element.len=0 then
+ pint_ptr(res)^:=0
+ else
+ begin
+ inc(element.len); // with Zero at the end
+{$IFDEF Miranda}
+ if (element.flags and EF_MMI)<>0 then
+ lsrc:=mir_alloc(element.len*SizeOf(AnsiChar))
+ else
+{$ENDIF}
+ mGetMem (lsrc ,element.len*SizeOf(AnsiChar));
+ FillChar(lsrc^,element.len*SizeOf(AnsiChar),0);
+ TranslateBlob(pByte(lsrc),element);
+ pint_ptr(res)^:=uint_ptr(lsrc);
+ end;
+ end;
+ SST_WPTR: begin
+ if element.len=0 then
+ begin
+{$IFDEF Miranda}
+ if (element.flags and EF_SCRIPT)<>0 then
+ element.len:=StrLenW(element.text)
+ else
+{$ENDIF}
+ element.len:=UTF8Len(element.text);
+ end;
+
+ if element.len=0 then
+ pint_ptr(res)^:=0
+ else
+ begin
+ inc(element.len); // with Zero at the end
+{$IFDEF Miranda}
+ if (element.flags and EF_MMI)<>0 then
+ lsrc:=mir_alloc(element.len*SizeOf(WideChar))
+ else
+{$ENDIF}
+ mGetMem (lsrc ,element.len*SizeOf(WideChar));
+ FillChar(lsrc^,element.len*SizeOf(WideChar),0);
+//!!!!! variables script gives unicode, need to recognize it
+ TranslateBlob(pByte(lsrc),element);
+ pint_ptr(res)^:=uint_ptr(lsrc);
+ end;
+ end;
+ end;
+ if (element.etype=SST_BPTR) or (element.etype=SST_WPTR) then
+ inc(int_ptr(res),SizeOf(pointer))
+ else
+ inc(int_ptr(res),element.len);
+
+ FreeElement(element);
+ if p=nil then break;
+ pc:=p+1;
+ inc(tmpl);
+ end;
+ tmpl^.flags:=tmpl^.flags or EF_LAST;
+end;
+
+function GetStructureResult(var struct;atype:pinteger=nil;alen:pinteger=nil):int_ptr;
+var
+ loffset,ltype:integer;
+begin
+ with PStructResult(pAnsiChar(struct)-SizeOF(TStructResult))^ do
+ begin
+ ltype :=typ ;
+ loffset:=offset;
+ if atype<>nil then atype^:=typ;
+ if alen <>nil then alen ^:=len;
+ end;
+
+ case ltype of
+ SST_LAST : result:=0;
+ SST_PARAM: result:=0;
+
+ SST_BYTE : result:=pByte (pAnsiChar(struct)+loffset)^;
+ SST_WORD : result:=pWord (pAnsiChar(struct)+loffset)^;
+ SST_DWORD : result:=pDword (pAnsiChar(struct)+loffset)^;
+ SST_QWORD : result:=pint64 (pAnsiChar(struct)+loffset)^;
+ SST_NATIVE: result:=pint_ptr(pAnsiChar(struct)+loffset)^;
+
+ SST_BARR: result:=int_ptr(pAnsiChar(struct)+loffset); //??
+ SST_WARR: result:=int_ptr(pAnsiChar(struct)+loffset); //??
+
+ SST_BPTR: result:=pint_ptr(pAnsiChar(struct)+loffset)^; //??
+ SST_WPTR: result:=pint_ptr(pAnsiChar(struct)+loffset)^; //??
+ else
+ result:=0;
+ end;
+end;
+
+procedure FreeStructure(var struct);
+var
+ value:pAnsiChar;
+ tmpl:pShortTemplate;
+ num,lmod:integer;
+ tmp:pointer;
+begin
+ tmp:=pointer(pAnsiChar(struct)-SizeOF(TStructResult)-SizeOf(dword));
+ num:=pdword(tmp)^;
+ tmpl:=pointer(pAnsiChar(tmp)-num*SizeOf(tShortTemplate));
+ lmod:=uint_ptr(tmpl) mod SizeOf(pointer);
+ // align to pointer size border
+ if lmod<>0 then
+ tmpl:=pointer(pAnsiChar(tmpl)-(SizeOf(pointer)-lmod));
+
+ tmp:=tmpl;
+
+ repeat
+ case tmpl^.etype of
+ SST_BPTR,SST_WPTR: begin
+ //??
+ value:=pAnsiChar(pint_ptr(pAnsiChar(struct)+tmpl^.offset)^);
+{$IFDEF Miranda}
+ if (tmpl^.flags and EF_MMI)<>0 then
+ mir_free(value)
+ else
+{$ENDIF}
+ mFreeMem(value);
+ end;
+ end;
+ inc(tmpl);
+ until (tmpl^.flags and EF_LAST)<>0;
+
+ mFreeMem(tmp);
+end;
+
+end.
diff --git a/plugins/Utils.pas/structopts.rc b/plugins/Utils.pas/structopts.rc
new file mode 100644
index 0000000000..f9fa75e0d1
--- /dev/null
+++ b/plugins/Utils.pas/structopts.rc
@@ -0,0 +1,83 @@
+#include "i_struct_const.inc"
+
+LANGUAGE 0,0
+
+IDD_STRUCTURE DIALOGEX 0, 0, 348,184, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_VISIBLE | WS_THICKFRAME
+CAPTION "Structure Editor"
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ CONTROL "" , IDC_DATA_FULL, "SysListView32",
+ WS_BORDER | WS_TABSTOP |
+ LVS_SHOWSELALWAYS | LVS_EDITLABELS | // LVS_NOCOLUMNHEADER |
+ LVS_SINGLESEL | LVS_REPORT,
+ 2, 2, 160, 162, WS_EX_CONTROLPARENT
+ COMBOBOX IDC_DATA_TMPL , 2, 168, 140, 56, CBS_DROPDOWNLIST | WS_VSCROLL
+
+#ifdef Miranda
+ CONTROL "New" ,IDC_DATA_NEW ,"MButtonClass",WS_TABSTOP,166, 2,16,16,$18000000// | WS_GROUP
+ CONTROL "Up" ,IDC_DATA_UP ,"MButtonClass",WS_TABSTOP,166, 22,16,16,$18000000
+ CONTROL "Down" ,IDC_DATA_DOWN ,"MButtonClass",WS_TABSTOP,166, 40,16,16,$18000000
+ CONTROL "Delete",IDC_DATA_DELETE,"MButtonClass",WS_TABSTOP,166, 60,16,16,$18000000
+
+ CONTROL "?" ,IDC_DATA_INFO ,"MButtonClass",WS_TABSTOP,146,167,16,16,$18000000
+ CONTROL "!" ,IDC_DATA_PASTE ,"MButtonClass",WS_TABSTOP,166,167,16,16,$18000000
+#else
+ PUSHBUTTON "New" ,IDC_DATA_NEW ,166, 2,16,16, BS_ICON | BS_FLAT
+ PUSHBUTTON "Up" ,IDC_DATA_UP ,166, 22,16,16, BS_ICON | BS_FLAT
+ PUSHBUTTON "Down" ,IDC_DATA_DOWN ,166, 40,16,16, BS_ICON | BS_FLAT
+ PUSHBUTTON "Delete",IDC_DATA_DELETE,166, 60,16,16, BS_ICON | BS_FLAT
+
+ PUSHBUTTON "?" ,IDC_DATA_INFO ,146,167,16,16, BS_ICON | BS_FLAT
+ PUSHBUTTON "!" ,IDC_DATA_PASTE ,166,167,16,16, BS_ICON | BS_FLAT
+#endif
+
+ RTEXT "Data align",IDC_DATA_SALGN, 186, 2, 86, 14, SS_CENTERIMAGE
+ COMBOBOX IDC_DATA_ALIGN , 274, 2, 72, 56, CBS_DROPDOWNLIST | WS_VSCROLL
+ CONTROL "", IDC_DATA_SEP, "STATIC", SS_ETCHEDHORZ, 186, 20, 160, 2
+
+ COMBOBOX IDC_DATA_TYPE, 186, 24, 160, 96, CBS_DROPDOWNLIST | WS_VSCROLL
+ EDITTEXT IDC_DATA_LEN , 186, 40, 40, 11
+ LTEXT "Data length" , IDC_DATA_SLEN, 230, 40, 116, 11, SS_CENTERIMAGE
+ EDITTEXT IDC_DATA_EDIT, 186, 55, 160, 82,
+ ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN
+ EDITTEXT IDC_DATA_EDTN, 186, 55, 160, 11
+#ifdef Miranda
+ CONTROL "V" ,IDC_VAR_HELP ,"MButtonClass",WS_TABSTOP,328,137,16,16,$18000000
+ AUTOCHECKBOX "Use Variables", IDC_DATA_VARS, 186, 138, 142, 14
+ AUTOCHECKBOX "Use MMI" , IDC_DATA_MMI , 186, 152, 160, 14
+#endif
+
+ DEFPUSHBUTTON "&Change", IDC_DATA_CHANGE, 186, 168, 46, 14//, WS_GROUP
+ PUSHBUTTON "&OK" , IDOK , 250, 168, 46, 14
+ PUSHBUTTON "C&ancel", IDCANCEL , 300, 168, 46, 14
+
+}
+
+IDD_STRUCTHELP DIALOGEX 0, 0, 256, 174, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_CONTROLPARENT
+CAPTION "Structure help"
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ DEFPUSHBUTTON "OK", IDOK, 4, 154, 26, 16
+
+ RTEXT "Name", -1 , 4, 4, 60, 14, SS_CENTERIMAGE
+ EDITTEXT IDC_HLP_NAME, 70, 5, 180, 12, ES_READONLY
+
+ RTEXT "Plugin", -1 , 4, 20, 60, 14, SS_CENTERIMAGE
+ EDITTEXT IDC_HLP_PLUGIN , 70, 21, 180, 12, ES_READONLY
+
+ RTEXT "Descr", -1 , 4, 36, 60, 14, SS_CENTERIMAGE
+ EDITTEXT IDC_HLP_DESCR , 70, 36, 180, 42, ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL
+
+ CONTROL "", -1, "STATIC", SS_ETCHEDHORZ, 2, 80, 252, 2
+
+ EDITTEXT IDC_HLP_STRUCT , 70, 84, 180, 88, ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL
+}
+
+IDI_NEW ICON "ico\new.ico"
+IDI_UP ICON "ico\up.ico"
+IDI_DOWN ICON "ico\down.ico"
+IDI_DELETE ICON "ico\delete.ico"
diff --git a/plugins/Utils.pas/structopts.res b/plugins/Utils.pas/structopts.res
new file mode 100644
index 0000000000..65df5f8a67
--- /dev/null
+++ b/plugins/Utils.pas/structopts.res
Binary files differ
diff --git a/plugins/Utils.pas/syswin.pas b/plugins/Utils.pas/syswin.pas
new file mode 100644
index 0000000000..7cc646184a
--- /dev/null
+++ b/plugins/Utils.pas/syswin.pas
@@ -0,0 +1,725 @@
+unit syswin;
+{$include compilers.inc}
+
+interface
+
+uses windows;
+
+type
+ tFFWFilterProc = function(fname:pWideChar):boolean;
+
+const
+ ThreadTimeout = 50;
+const
+ gffdMultiThread = 1;
+ gffdOld = 2;
+
+function GetWorkOfflineStatus:integer;
+
+function SendString(wnd:HWND;astr:PWideChar):integer; overload;
+function SendString(wnd:HWND;astr:PAnsiChar):integer; overload;
+procedure ProcessMessages;
+function GetFocusedChild(wnd:HWND):HWND;
+function GetAssoc(key:PAnsiChar):PAnsiChar;
+function GetFileFromWnd(wnd:HWND;Filter:tFFWFilterProc;
+ flags:dword=gffdMultiThread+gffdOld;timeout:cardinal=ThreadTimeout):pWideChar;
+
+function WaitFocusedWndChild(Wnd:HWnd):HWnd;
+
+function ExecuteWaitW(AppPath:pWideChar; CmdLine:pWideChar=nil; DfltDirectory:PWideChar=nil;
+ Show:DWORD=SW_SHOWNORMAL; TimeOut:DWORD=0; ProcID:PDWORD=nil):dword;
+function ExecuteWait(AppPath:PAnsiChar; CmdLine:PAnsiChar=nil; DfltDirectory:PAnsiChar=nil;
+ Show:DWORD=SW_SHOWNORMAL; TimeOut:DWORD=0; ProcID:PDWORD=nil):dword;
+
+function GetEXEbyWnd(w:HWND; var dst:pWideChar):pWideChar; overload;
+function GetEXEbyWnd(w:HWND; var dst:PAnsiChar):PAnsiChar; overload;
+function IsExeRunning(exename:PWideChar):boolean; {hwnd}
+
+implementation
+
+uses
+ {$IFNDEF FPC}shellapi,{$ENDIF}
+{$IFDEF COMPILER_16_UP}
+ WinAPI.PsApi,
+{$ELSE}
+ psapi,
+{$ENDIF}
+ common,messages;
+
+{$IFDEF COMPILER_16_UP}
+type pqword = ^int64;
+{$ENDIF}
+
+function ExecuteWaitW(AppPath:pWideChar; CmdLine:pWideChar=nil; DfltDirectory:PWideChar=nil;
+ Show:DWORD=SW_SHOWNORMAL; TimeOut:DWORD=0; ProcID:PDWORD=nil):dword;
+var
+ Flags: DWORD;
+ {$IFDEF FPC}
+ Startup: StartupInfo;
+ {$ELSE}
+ Startup: StartupInfoW;
+ {$ENDIF}
+ ProcInf: TProcessInformation;
+ App: array [0..1023] of widechar;
+ p:PWideChar;
+ ext1,ext2:array [0..7] of widechar;
+begin
+ Result := cardinal(-1);
+ if FindExecutableW(AppPath,DfltDirectory,App)<=32 then
+ exit;
+ if lstrcmpiw(GetExt(AppPath,ext1,7),GetExt(App,ext2,7))<>0 then
+ CmdLine:=AppPath;
+ Flags := CREATE_NEW_CONSOLE;
+ if Show = SW_HIDE then
+ Flags := Flags or CREATE_NO_WINDOW;
+ FillChar(Startup, SizeOf(Startup),0);
+ with Startup do
+ begin
+ cb :=SizeOf(Startup);
+ wShowWindow:=Show;
+ dwFlags :=STARTF_USESHOWWINDOW;
+ end;
+ if ProcID <> nil then
+ ProcID^ := 0;
+ p:=StrEndW(App);
+ if (CmdLine<>nil) and (CmdLine^<>#0) then
+ begin
+ p^:=' ';
+ inc(p);
+ StrCopyW(p,CmdLine);
+ end;
+ if CreateProcessW(nil,App,nil,nil,FALSE,Flags,nil,DfltDirectory,Startup,ProcInf) then
+ begin
+ if TimeOut<>0 then
+ begin
+ if WaitForSingleObject(ProcInf.hProcess,TimeOut)=WAIT_OBJECT_0 then
+ begin
+ GetExitCodeProcess(ProcInf.hProcess,result);
+ CloseHandle(ProcInf.hProcess);
+ end
+ else
+ begin
+ result:=1;
+ if ProcID<>nil then
+ ProcID^:=ProcInf.hProcess;
+ end;
+ end
+ else
+ begin
+ GetExitCodeProcess(ProcInf.hProcess,result);
+ CloseHandle(ProcInf.hProcess);
+ end;
+ CloseHandle(ProcInf.hThread);
+ end;
+end;
+
+function ExecuteWait(AppPath:PAnsiChar; CmdLine:PAnsiChar=nil; DfltDirectory:PAnsiChar=nil;
+ Show:DWORD=SW_SHOWNORMAL; TimeOut:DWORD=0; ProcID:PDWORD=nil):dword;
+var
+ Flags: DWORD;
+ {$IFDEF FPC}
+ Startup: StartupInfo;
+ {$ELSE}
+ Startup: StartupInfoA;
+ {$ENDIF}
+ ProcInf: TProcessInformation;
+ App: array [0..1023] of AnsiChar;
+ p:PAnsiChar;
+ ext1,ext2:array [0..7] of AnsiChar;
+begin
+ Result := cardinal(-1);
+ if FindExecutableA(AppPath,DfltDirectory,App)<=32 then
+ exit;
+ if lstrcmpia(GetExt(AppPath,ext1,7),GetExt(App,ext2,7))<>0 then
+ CmdLine:=AppPath;
+ Flags := CREATE_NEW_CONSOLE;
+ if Show = SW_HIDE then
+ Flags := Flags or CREATE_NO_WINDOW;
+ FillChar(Startup, SizeOf(Startup),0);
+ with Startup do
+ begin
+ cb :=SizeOf(Startup);
+ wShowWindow:=Show;
+ dwFlags :=STARTF_USESHOWWINDOW;
+ end;
+ if ProcID <> nil then
+ ProcID^ := 0;
+ p:=StrEnd(App);
+ if (CmdLine<>nil) and (CmdLine^<>#0) then
+ begin
+ p^:=' ';
+ inc(p);
+ StrCopy(p,CmdLine);
+ end;
+ if CreateProcessA(nil,App,nil,nil,FALSE,Flags,nil,DfltDirectory,Startup,ProcInf) then
+ begin
+ if TimeOut<>0 then
+ begin
+ if WaitForSingleObject(ProcInf.hProcess,TimeOut)=WAIT_OBJECT_0 then
+ begin
+ GetExitCodeProcess(ProcInf.hProcess,result);
+ CloseHandle(ProcInf.hProcess);
+ end
+ else
+ begin
+ result:=1;
+ if ProcID<>nil then
+ ProcID^:=ProcInf.hProcess;
+ end;
+ end
+ else
+ begin
+ GetExitCodeProcess(ProcInf.hProcess,result);
+ CloseHandle(ProcInf.hProcess);
+ end;
+ CloseHandle(ProcInf.hThread);
+ end;
+end;
+
+//----- Information functions -----
+
+function GetWorkOfflineStatus:integer;
+var
+ lKey:HKEY;
+ len,typ:dword;
+begin
+ result:=0;
+ if RegOpenKeyEx(HKEY_CURRENT_USER,
+ 'Software\Microsoft\Windows\CurrentVersion\Internet Settings',0,
+ KEY_READ,lKey)=ERROR_SUCCESS then
+ begin
+ len:=4;
+ typ:=REG_DWORD;
+ if RegQueryValueEx(lKey,'GlobalUserOffline',NIL,@typ,@result,@len)=ERROR_SUCCESS then
+ ;
+ RegCloseKey(lKey);
+ end;
+end;
+
+function GetAssoc(key:PAnsiChar):PAnsiChar;
+var
+ lKey:HKEY;
+ tmpbuf:array [0..511] of AnsiChar;
+ len:integer;
+begin
+ result:=nil;
+ if RegOpenKeyExA(HKEY_CLASSES_ROOT,key,0,
+ KEY_READ,lKey)=ERROR_SUCCESS then
+ begin
+ len:=511;
+ if (RegQueryValueExA(lKey,NIL,NIL,NIL,@tmpbuf,@len)=ERROR_SUCCESS) then
+ begin
+ StrDup(result,tmpbuf);
+// only path
+// while result[len]<>'\' do dec(len);
+// StrCopy(result,result+2,len-3);
+ end;
+ RegCloseKey(lKey);
+ end;
+end;
+
+function GetFocusedChild(wnd:HWND):HWND;
+var
+ dwTargetOwner:DWORD;
+ dwThreadID:DWORD;
+ res:boolean;
+begin
+ dwTargetOwner:=GetWindowThreadProcessId(wnd,nil);
+ dwThreadID:=GetCurrentThreadId();
+ res:=false;
+ if (dwTargetOwner<>dwThreadID) then
+ res:=AttachThreadInput(dwThreadID,dwTargetOwner,TRUE);
+ result:=GetFocus;
+ if res then
+ AttachThreadInput(dwThreadID,dwTargetOwner,FALSE);
+end;
+
+function WaitFocusedWndChild(Wnd:HWnd):HWnd;
+var
+ T1,T2:Integer;
+ W:HWnd;
+begin
+ Sleep(50);
+ T1:=GetTickCount;
+ repeat
+ W:=GetTopWindow(Wnd);
+ if W=0 then W:=Wnd;
+ W:=GetFocusedChild(W);
+ if W<>0 then
+ begin
+ Wnd:=W;
+ break;
+ end;
+ T2:=GetTickCount;
+ if Abs(T1-T2)>100 then break;
+ until false;
+ Result:=Wnd;
+end;
+
+function SendString(wnd:HWND;astr:PWideChar):integer;
+var
+ s,s0:PWideChar;
+ style:integer;
+begin
+ result:=0;
+ if (astr=nil) or (astr^=#0) then exit;
+ if wnd=0 then
+ begin
+ wnd:=WaitFocusedWndChild(GetForegroundWindow);
+ if wnd=0 then Exit;
+ end;
+ style:=GetWindowLongW(wnd,GWL_STYLE);
+ if (style and (WS_DISABLED or ES_READONLY))=0 then
+ begin
+ StrDupW(s,astr); //??
+ s0:=s;
+ while s^<>#0 do
+ begin
+ if s^<>#10 then
+ PostMessageW(wnd,WM_CHAR,ord(s^),1);
+ Inc(s);
+ end;
+ mFreeMem(s0); //??
+ result:=1;
+ end;
+end;
+
+function SendString(wnd:HWND;astr:PAnsiChar):integer;
+var
+ s,s0:PAnsiChar;
+ style:integer;
+begin
+ result:=0;
+ if (astr=nil) or (astr^=#0) then exit;
+ if wnd=0 then
+ begin
+ wnd:=WaitFocusedWndChild(GetForegroundWindow);
+ if wnd=0 then Exit;
+ end;
+ style:=GetWindowLongA(wnd,GWL_STYLE);
+ if (style and (WS_DISABLED or ES_READONLY))=0 then
+ begin
+ StrDup(s,astr); //??
+ s0:=s;
+ while s^<>#0 do
+ begin
+ if s^<>#10 then
+ PostMessageA(wnd,WM_CHAR,ord(s^),1);
+ Inc(s);
+ end;
+ mFreeMem(s0); //??
+ result:=1;
+ end;
+end;
+
+procedure ProcessMessages;
+var
+ Unicode: Boolean;
+ MsgExists: Boolean;
+ Msg:tMsg;
+begin
+ repeat
+ if PeekMessageA(Msg,0,0,0,PM_NOREMOVE) then
+ begin
+ Unicode:=(Msg.hwnd<>0) and IsWindowUnicode(Msg.hwnd);
+ if Unicode then
+ MsgExists:=PeekMessageW(Msg,0,0,0,PM_REMOVE)
+ else
+ MsgExists:=PeekMessageA(Msg,0,0,0,PM_REMOVE);
+ if not MsgExists then break;
+
+ if Msg.Message<>WM_QUIT then
+ begin
+ TranslateMessage({$IFDEF FPC}@{$ENDIF}Msg);
+ if Unicode then
+ DispatchMessageW({$IFDEF FPC}@{$ENDIF}Msg)
+ else
+ DispatchMessageA({$IFDEF FPC}@{$ENDIF}Msg);
+ end;
+ end
+ else
+ break;
+ until false;
+end;
+
+//----- work with EXE -----
+
+function GetEXEbyWnd(w:HWND; var dst:pWideChar):pWideChar;
+var
+ hProcess:THANDLE;
+ ProcID:DWORD;
+ ModuleName: array [0..300] of WideChar;
+begin
+ dst:=nil;
+ GetWindowThreadProcessId(w,@ProcID);
+ if ProcID<>0 then
+ begin
+ hProcess:=OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ,False,ProcID);
+ if hProcess<>0 then
+ begin
+ ModuleName[0]:=#0;
+ GetModuleFilenameExW(hProcess,0,ModuleName,SizeOf(ModuleName));
+ StrDupW(dst,ModuleName);
+ CloseHandle(hProcess);
+ end;
+ end;
+ result:=dst;
+end;
+
+function GetEXEbyWnd(w:HWND; var dst:PAnsiChar):PAnsiChar;
+var
+ hProcess:THANDLE;
+ ProcID:DWORD;
+ ModuleName: array [0..300] of AnsiChar;
+begin
+ dst:=nil;
+ GetWindowThreadProcessId(w,@ProcID);
+ if ProcID<>0 then
+ begin
+ hProcess:=OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ,False,ProcID);
+ if hProcess<>0 then
+ begin
+ ModuleName[0]:=#0;
+ GetModuleFilenameExA(hProcess,0,ModuleName,SizeOf(ModuleName));
+ StrDup(dst,ModuleName);
+ CloseHandle(hProcess);
+ end;
+ end;
+ result:=dst;
+end;
+
+function IsExeRunning(exename:PWideChar):boolean;{hwnd}
+const
+ nCount = 4096;
+var
+ Processes:array [0..nCount-1] of dword;
+ nProcess:dword;
+ hProcess:THANDLE;
+ ModuleName: array [0..300] of WideChar;
+ i:integer;
+begin
+ result:=false;
+ EnumProcesses(pointer(@Processes),nCount*SizeOf(DWORD),nProcess);
+ nProcess:=(nProcess div 4)-1;
+ for i:=2 to nProcess do //skip Idle & System
+ begin
+ hProcess:=OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ,
+ False,Processes[i]);
+ if hProcess<>0 then
+ begin
+ GetModuleFilenameExW(hProcess,0,ModuleName,SizeOf(ModuleName));
+ result:=lstrcmpiw(ExtractW(ModuleName,true),exename)=0;
+ CloseHandle(hProcess);
+ if result then exit;
+ end;
+ end;
+end;
+
+//----- work with handles -----
+function GetProcessHandleCount(hProcess:THANDLE;var pdwHandleCount:dword):bool; stdcall; external 'kernel32.dll';
+
+function NtQueryObject(ObjectHandle:THANDLE;ObjectInformationClass:integer;
+ ObjectInformation:pointer;Length:ulong;var ResultLength:longint):cardinal; stdcall; external 'ntdll.dll';
+
+const
+ ObjectNameInformation = 1; // +4 bytes
+ ObjectTypeInformation = 2; // +$60 bytes
+const
+ STATUS_INFO_LENGTH_MISMATCH = $C0000004;
+
+function TranslatePath(fn:PWideChar):PWideChar;
+const
+ LANPrefix:PWideChar = '\Device\LanmanRedirector\';
+var
+ szTemp:array [0..511] of WideChar;
+ szName:array [0..511] of WideChar;
+ p:PWideChar;
+ uNameLen:word;
+ szTempFile:array [0..511] of WideChar;
+begin
+ if StrPosW(fn,LANPrefix)=fn then
+ begin
+ uNameLen:=StrLenW(LANPrefix);
+ mGetMem(result,(StrLenW(fn)-uNameLen+3)*SizeOf(WideChar));
+ result[0]:='\';
+ result[1]:='\';
+ StrCopyW(result+2,fn+uNameLen);
+ exit;
+ end;
+ if GetLogicalDriveStringsW(255,@szTemp)<>0 then
+ begin
+ p:=szTemp;
+ repeat
+ p[2]:=#0;
+ if QueryDosDeviceW(p,szName,255)<>0 then
+ begin
+ uNameLen:=StrLenW(szName)+1;
+ if uNameLen<255 then
+ begin
+ StrCopyW(szTempFile,fn,uNameLen-1);
+ if lstrcmpiw(szTempFile,szName)=0 then
+ begin
+ mGetMem(result,(StrLenW(fn+uNameLen)+4)*SizeOf(WideChar));
+ result[0]:=WideChar(ORD(p[0]));
+ result[1]:=':';
+ result[2]:='\';
+ StrCopyW(result+3,fn+uNameLen);
+ exit;
+ end;
+ end;
+ end;
+ inc(p,4);
+ until p^=#0;
+ end;
+ StrDupW(result,fn);
+end;
+
+const
+ maxhandles = 15;
+var
+ har,hold:array [0..maxhandles-1] of PWideChar;
+ harcnt:integer;
+const
+ oldcnt:integer=0;
+
+procedure ArSwitch(idx:integer);
+var
+ h:pWideChar;
+begin
+//clear old
+ while oldcnt>0 do
+ begin
+ dec(oldcnt);
+ FreeMem(hold[oldcnt]);
+ end;
+//copy new to old
+ move(har,hold,SizeOf(har));
+ oldcnt:=harcnt;
+// move active to begin
+ if idx<>0 then
+ begin
+ h :=hold[idx];
+ hold[idx]:=hold[0];
+ hold[0] :=h;
+ end;
+end;
+
+function CheckHandles(ReturnNew:bool):integer;
+var
+ i,j:integer;
+ flg:boolean;
+begin
+ result:=0;
+ if oldcnt=0 then //first time
+ begin
+ ArSwitch(0);
+ exit;
+ end;
+ i:=0;
+ if ReturnNew then
+ begin
+ while i<harcnt do
+ begin
+ flg:=false;
+ j:=0;
+ while j<oldcnt do
+ begin
+ if StrCmpW(har[i],hold[j])=0 then
+ begin
+ flg:=true; //old=new
+ break;
+ end;
+ inc(j);
+ end;
+ if not flg then // new!!
+ begin
+ ArSwitch(i);
+ exit;
+ end;
+ inc(i);
+ end;
+ end
+ else
+ begin
+ while i<oldcnt do
+ begin
+ j:=0;
+ while j<harcnt do
+ begin
+ if StrCmpW(hold[i],har[j])=0 then
+ begin
+ ArSwitch(j);
+ exit;
+ end;
+ inc(j);
+ end;
+ inc(i);
+ end;
+ end;
+ ArSwitch(0);
+ result:=-1;
+end;
+
+const
+ MaxHandle = $2000;
+
+type
+ ptrec = ^trec;
+ trec = record
+ handle:thandle;
+ fname:pWideChar;
+ end;
+
+type
+ pint_ptr = ^int_ptr;
+
+function GetName(param:pointer):integer; //stdcall;
+const
+ BufSize = $800;
+ // depends of record align
+ offset=SizeOf(Pointer) div 2; // 4 for win64, 2 for win32
+var
+ TmpBuf:array [0..BufSize-1] of WideChar;
+var
+ dummy:longint;
+ size:integer;
+ pc:pWideChar;
+begin
+ result:=0;
+
+ if NtQueryObject(ptrec(param)^.handle,ObjectNameInformation,
+ @TmpBuf,BufSize*SizeOf(WideChar),dummy)=0 then
+ begin
+ // UNICODE_STRING: 2b - length, 2b - maxlen, (align), next - pWideChar
+ size:=pword(@TmpBuf)^; // length in bytes
+ if size>=0 then
+ begin
+ GetMem(ptrec(param)^.fname,size+SizeOf(WideChar)); // length in bytes
+
+ pc:=pWideChar(pint_ptr(@TmpBuf[offset])^);
+ move(pc^,ptrec(param)^.fname^,size); // can be without zero
+ pword(pAnsiChar(ptrec(param)^.fname)+size)^:=0;
+ end
+ else
+ ptrec(param)^.fname:=nil;
+ end;
+end;
+
+function TestHandle(Handle:THANDLE;MultiThread:bool;timeout:cardinal):pWideChar;
+var
+ hThread:THANDLE;
+ rec:trec;
+// dummy:longint;
+ res:{$IFDEF COMPILER_16_UP}Longword{$ELSE}uint_ptr{$ENDIF};
+begin
+ result:=nil;
+{
+ // check what it - file
+ if (NtQueryObject(Handle,ObjectTypeInformation,
+ @TmpBuf,BufSize*SizeOf(WideChar),dummy)<>0) or
+ (StrCmpW(TmpBuf+$30,'File')<>0) then
+ Exit;
+}
+ // check what it disk file
+//!!! need to check again
+ if GetFileType(Handle)<>FILE_TYPE_DISK then exit;
+
+ rec.handle:=Handle;
+ rec.fname:=nil;
+
+ if not MultiThread then
+ begin
+ GetName(@rec);
+ result:=rec.fname;
+ end
+ else
+ begin
+ hThread:=BeginThread(nil,0,@GetName,@rec,0,res);
+ if WaitForSingleObject(hThread,timeout)=WAIT_TIMEOUT then
+ begin
+ TerminateThread(hThread,0);
+ end
+ else
+ result:=rec.fname;
+ CloseHandle(hThread);
+ end;
+end;
+
+function GetFileFromWnd(wnd:HWND;Filter:tFFWFilterProc;
+ flags:dword=gffdMultiThread+gffdOld;timeout:cardinal=ThreadTimeout):pWideChar;
+var
+ hProcess,h:THANDLE;
+ pid:THANDLE;
+ i:THANDLE;
+ c:THANDLE;
+ handles:dword;
+ pc:pWideChar;
+begin
+ result:=nil;
+ GetWindowThreadProcessId(wnd,@c);
+ pid:=OpenProcess(//PROCESS_VM_READ or
+ PROCESS_DUP_HANDLE or PROCESS_QUERY_INFORMATION {or PROCESS_QUERY_LIMITED_INFORMATION},
+ true,c);
+ if pid=0 then exit;
+ harcnt:=0;
+ if GetProcessHandleCount(pid,handles) then
+ begin
+ handles:=handles*4; // count no matter, check "every 4th" handle
+// Handles:=Handles*SizeOf(THANDLE);
+ hProcess:=GetCurrentProcess;
+ i:=SIZEOF(THANDLE); // skip first
+
+ while true do
+ begin
+ if DuplicateHandle(pid,i,hProcess,@h,GENERIC_READ,false,0) then
+ begin
+ pc:=TestHandle(h,(flags and gffdMultiThread)<>0,timeout);
+ if pc<>nil then
+ begin
+ // if GetFileType(h)=FILE_TYPE_DISK then
+ begin
+ if (@Filter=nil) or Filter(pc) and (harcnt<maxhandles) then
+ begin
+ har[harcnt]:=pc;
+ inc(harcnt);
+ end
+ else
+ FreeMem(pc);
+ end;
+ end;
+ CloseHandle(h);
+ end
+ else
+ begin
+// inc(handles,SizeOf(THANDLE)); //????skip empty number and non-duplicates
+ inc(handles,4); //????skip empty number and non-duplicates
+ if handles>MaxHandle then break; //file not found
+ end;
+ inc(i,4);
+//!! inc(i,SizeOf(THANDLE));
+ if i>handles then
+ break;
+ end;
+ end;
+
+ CloseHandle(pid);
+ if harcnt>0 then
+ begin
+ CheckHandles((flags and gffdOld)=0);
+ result:=TranslatePath(hold[0]);
+ end
+end;
+
+procedure ClearHandles;
+begin
+ while oldcnt>0 do
+ begin
+ dec(oldcnt);
+ FreeMem(hold[oldcnt]);
+ end;
+end;
+
+initialization
+finalization
+ ClearHandles;
+end.
diff --git a/plugins/Utils.pas/tb_chunk.inc b/plugins/Utils.pas/tb_chunk.inc
new file mode 100644
index 0000000000..e3fa2ac068
--- /dev/null
+++ b/plugins/Utils.pas/tb_chunk.inc
@@ -0,0 +1,640 @@
+{Text Chunk processing: frame text output}
+
+const
+ colors:array [0..15] of dword = (
+ $00FFFFFF,$00000000,$007F0000,$00009300,
+ $000000FF,$0000007F,$009C009C,$00007FFC,
+ $0000FFFF,$0000FC00,$00939300,$00FFFF00,
+ $00FC0000,$00FF00FF,$007F7F7F,$00D2D2D2
+ );
+
+const // chunk type
+ CT_TEXT = $01;
+ CT_TAB = $09;
+ CT_SPACE = $20;
+ CT_NEWLINE = $0D;
+
+const // macro codes
+ ctOpenBold = $0001;
+ ctCloseBold = $0002;
+ ctOpenItalic = $0004;
+ ctCloseItalic = $0008;
+ ctOpenUnderline = $0010;
+ ctCloseUnderline = $0020;
+ ctOpenTextColor = $0040;
+ ctCloseTextColor = $0080;
+ ctOpenBkColor = $0100;
+ ctCloseBkColor = $0200;
+ ctRGB = $1000; // special code for RGB color values
+
+ ctFontChanging =
+ ctOpenBold or ctCloseBold or
+ ctOpenItalic or ctCloseItalic or
+ ctOpenUnderline or ctCloseUnderline;
+
+procedure ProcessMacro(dc:hdc;Chunk:pChunk);
+var
+ lf:TLOGFONT;
+ i:integer;
+begin
+ if dc=0 then
+ exit;
+ if Chunk._Type=CT_NEWLINE then
+ exit;
+
+ case Chunk^._type shr 16 of
+
+ ctCloseTextColor: begin
+ SetTextColor(dc,Chunk^.add);
+ end;
+
+ ctCloseBkColor: begin
+ SetBkColor(dc,Chunk^.add);
+ SetBkMode (dc,TRANSPARENT);
+ end;
+
+ ctOpenTextColor: begin
+ case Chunk^.val of
+ 0: i:=Chunk^.add; //back
+ 1..16: i:=colors[Chunk^.val-1];
+ else
+ i:=Chunk^.dir; // text
+ end;
+ SetTextColor(dc,i);
+ end;
+
+ ctOpenBkColor: begin
+ SetBkMode(dc,OPAQUE);
+ case Chunk^.val of
+ 0: i:=Chunk^.add; // back
+ 1..16: i:=colors[Chunk^.val-1];
+ else
+ i:=Chunk^.dir; // text
+ end;
+ SetBkColor(dc,i);
+ end;
+
+ ctOpenTextColor or ctRGB: begin
+ SetTextColor(dc,Chunk^.val);
+ end;
+
+ ctOpenBkColor or ctRGB: begin
+ SetBkMode(dc,OPAQUE);
+ SetBkColor(dc,Chunk^.val);
+ end;
+
+ else
+ begin
+ GetObject(GetCurrentObject(dc,OBJ_FONT),SizeOf(lf),@lf);
+ case Chunk^._type shr 16 of
+ ctOpenBold : lf.lfWeight :=FW_BOLD;
+ ctCloseBold : lf.lfWeight :=FW_NORMAL;
+ ctOpenItalic : lf.lfItalic :=1;
+ ctCloseItalic : lf.lfItalic :=0;
+ ctOpenUnderline : lf.lfUnderline:=1;
+ ctCloseUnderline: lf.lfUnderline:=0;
+ end;
+ DeleteObject(SelectObject(dc,CreateFontIndirect(lf)));
+ end;
+ end;
+end;
+
+function Macro(var src:pWideChar;var Chunk:pChunk;TextColor,BkColor:TCOLORREF):boolean;
+const
+ NumMacro = 10;
+ macros:array [0..NumMacro-1] of record txt:pWideChar; len:integer; code:integer; end = (
+ (txt:'{b}' ; len:3; code:ctOpenBold ),
+ (txt:'{/b}' ; len:4; code:ctCloseBold ),
+ (txt:'{i}' ; len:3; code:ctOpenItalic ),
+ (txt:'{/i}' ; len:4; code:ctCloseItalic ),
+ (txt:'{u}' ; len:3; code:ctOpenUnderline ),
+ (txt:'{/u}' ; len:4; code:ctCloseUnderline),
+ (txt:'{/cf}'; len:5; code:ctCloseTextColor),
+ (txt:'{/bg}'; len:5; code:ctCloseBkColor ),
+ (txt:'{cf' ; len:3; code:ctOpenTextColor ),
+ (txt:'{bg' ; len:3; code:ctOpenBkColor ));
+var
+ pc,pc1:pWideChar;
+ typ,i,lval,ldir,ladd:integer;
+ c:WideChar;
+begin
+ result:=false;
+ if src^<>'{' then exit;
+ pc:=src;
+ lval:=0;
+ ldir:=ppLeft;
+ ladd:=0;
+ typ :=0;
+ for i:=0 to NumMacro-1 do
+ begin
+ if StrCmpW(pc,macros[i].txt,macros[i].len)=0 then
+ begin
+ typ:=macros[i].code;
+ case typ of
+ ctOpenBkColor,
+ ctOpenTextColor: begin
+ inc(pc,macros[i].len);
+ if (pc^='#') or ((pc^>='0') and (pc^<='9')) then
+ begin
+ pc1:=pc;
+ if pc^='#' then
+ repeat
+ inc(pc1);
+ c:=pc1^;
+ until ((c<'0') or (c>'9')) and
+ ((c<'A') or (c>'F')) and
+ ((c<'a') or (c>'f'))
+ else
+ repeat
+ inc(pc1);
+ until (pc1^<'0') or (pc1^>'9');
+ if pc1^='}' then
+ begin
+ result:=true;
+ ldir:=TextColor;
+ ladd:=BkColor;
+ if (pc^='#') then // RGB
+ begin
+ typ:=typ or ctRGB;
+ lval:=HexToInt(pc+1);
+ end
+ else
+ begin
+ lval:=StrToInt(pc) mod 18;
+ end;
+ src:=pc1+1;
+ end;
+ end;
+ end;
+
+ ctCloseTextColor: begin
+ ladd:=TextColor;
+ inc(src,macros[i].len);
+ result:=true;
+ end;
+
+ ctCloseBkColor: begin
+ ladd:=BkColor;
+ inc(src,macros[i].len);
+ result:=true;
+ end;
+ else
+ inc(src,macros[i].len);
+ result:=true;
+ end;
+(*
+ if (typ=ctOpenBkColor) or (typ=ctOpenTextColor) then // processing color codes
+ begin
+ inc(pc,macros[i].len);
+ if (pc^='#') or ((pc^>='0') and (pc^<='9')) then
+ begin
+ pc1:=pc;
+ if pc^='#' then
+ repeat
+ inc(pc1);
+ c:=pc1^;
+ until ((c<'0') or (c>'9')) and
+ ((c<'A') or (c>'F')) and
+ ((c<'a') or (c>'f'))
+ else
+ repeat
+ inc(pc1);
+ until (pc1^<'0') or (pc1^>'9');
+ if pc1^='}' then
+ begin
+ result:=true;
+ if (pc^='#') then // RGB
+ begin
+ typ:=typ or ctRGB;
+ lval:=HexToInt(pc+1);
+ end
+ else
+ begin
+ lval:=StrToInt(pc) mod 18;
+ end;
+ src:=pc1+1;
+ end;
+ end;
+ end
+ else
+ begin
+ inc(src,macros[i].len);
+ result:=true;
+ end;
+*)
+ break;
+ end;
+ end;
+ if result then
+ begin
+ with Chunk^ do
+ begin
+ _type:=typ shl 16;
+ val :=lval;
+ dir :=ldir;
+ add :=ladd;
+ end;
+ inc(Chunk);
+ end;
+end;
+
+function CreateTextChunk(var Chunk:pChunk;src:pWideChar):pWideChar;
+var
+ i:integer;
+begin
+ result:=src;
+ while ((result^>='A') and (result^<='Z')) or
+ ((result^>='a') and (result^<='z')) or
+ ((result^>='0') and (result^<='9')) or
+ (ORD(result^)>127) do
+ inc(result);
+ i:=result-src;
+ if i>0 then // if no text (but what is this then?)
+ begin
+ with Chunk^ do
+ begin
+ _type:=CT_TEXT;
+ dir :=ppLeft;
+ txt :=src;
+ val :=i;
+ end;
+ inc(Chunk);
+ end;
+end;
+
+function CreateSignChunk(var Chunk:pChunk;src:PWideChar):PWideChar;
+begin
+ with Chunk^ do
+ begin
+ _type:=ord(src^);
+ add :=0;
+ dir :=ppLeft;
+ val :=1;
+ end;
+ result:=src;
+ inc(result);
+ inc(Chunk);
+end;
+
+procedure MeasureChunk(dc:HDC;Chunk:pChunk;var sz:TSIZE;block:Boolean);
+var
+ p:pWideChar;
+begin
+ if ((Chunk^._type shr 16)=0) and (Chunk^._type<>CT_NEWLINE) then
+ begin
+ if Chunk^._type=CT_TEXT then
+ p:=Chunk^.txt
+ else
+ begin
+ p:=PWideChar(@Chunk^._type);
+ end;
+ GetTextExtentPoint32W(dc,p,Chunk^.val,sz);
+ end
+ else
+ begin
+ if block and ((Chunk._type and ctFontChanging)<>0) then
+ ProcessMacro(dc,Chunk);
+ sz.cx:=0;
+ sz.cy:=0;
+ end;
+end;
+
+procedure MeasureLine(dc:HDC;Chunk:pChunk;var sz:TSIZE;limit:integer=4096);
+var
+ csz:TSIZE;
+// fnt1:HFONT;
+ lf:TLOGFONT;
+ txtcolor,bkcolor:COLORREF;
+ bkmode:integer;
+begin
+ sz.cx:=0;
+ sz.cy:=0;
+{
+ fnt1:=SelectObject(dc,CreateFontIndirect(FrameLF));
+
+ DeleteObject(SelectObject(dc,fnt1));
+}
+ txtcolor:=GetTextColor(dc);
+ bkcolor :=GetBkColor(dc);
+ bkmode :=GetBkMode(dc);
+ GetObject(GetCurrentObject(dc,OBJ_FONT),SizeOf(lf),@lf);
+
+ while (Chunk^._type<>0) and (Chunk^._type<>CT_NEWLINE) do
+ begin
+ MeasureChunk(dc,Chunk,csz,true);
+ if (sz.cx+csz.cx)<limit then
+ begin
+ inc(sz.cx,csz.cx);
+ if sz.cy<csz.cy then
+ sz.cy:=csz.cy;
+ end
+//!!
+ else if limit<>4096 then
+ break;
+ inc(Chunk);
+ end;
+
+ DeleteObject(SelectObject(dc,CreateFontIndirect(lf)));
+ SetTextColor(dc,txtcolor);
+ SetBkColor (dc,bkcolor);
+ SetBkMode (dc,bkmode);
+end;
+
+procedure DrawChunk(dc:HDC;Chunk:pChunk;rc:TRECT);
+var
+ p:pWideChar;
+begin
+ if (Chunk^._type shr 16)=0 then
+ begin
+ if Chunk^._type=CT_TEXT then
+ p:=Chunk^.txt
+ else
+ p:=PWideChar(@Chunk^._type);
+
+ DrawTextW(dc,p,Chunk^.val,rc,
+ DT_LEFT or DT_TOP or DT_SINGLELINE or DT_NOPREFIX or DT_EXPANDTABS)
+ end
+ else
+ ProcessMacro(dc,Chunk); //!! textcolor, bkcolor
+end;
+
+procedure DrawLine(dc:HDC;var Chunk:pChunk;rc:TRECT);
+var
+ sz:TSIZE;
+begin
+ while (Chunk^._type<>0) and (Chunk^._type<>CT_NEWLINE) do
+ begin
+ MeasureChunk(dc,Chunk,sz,false);
+ if (rc.left<rc.right) and ((rc.left+sz.cx)>0) then
+ DrawChunk(dc,Chunk,rc)
+ else if (Chunk^._type shr 16)<>0 then
+ ProcessMacro(dc,Chunk); //!! textcolor, bkcolor
+ inc(rc.left,sz.cx);
+ inc(Chunk);
+ end;
+end;
+
+procedure tTextBlock.DrawLines(dc:HDC;Chunk:pChunk;rc:TRECT;justpaint:boolean);
+var
+ sz:TSIZE;
+ rc1:TRECT;
+ w:integer;
+ rgn:HRGN;
+ ch:pChunk;
+ D:pTextData;
+begin
+ D:=pTextData(CustomData);
+// InflateRect(rc,-10,-3);
+ rgn:=CreateRectRgnIndirect(rc);
+ CopyRect(rc1,rc);
+ SelectClipRgn(dc,rgn);
+ w:=rc.Right-rc.left;
+ while Chunk^._type<>0 do
+ begin
+ MeasureLine(dc,Chunk,sz);
+ if sz.cx>0 then
+ begin
+ rc1.left:=rc.left;
+ if sz.cx<w then
+ begin
+ //!!
+ rc1.top:=rc.top;
+ if (D.TextEffect and effCenter)<>0 then
+ inc(rc1.left,(w-sz.cx) div 2);
+
+ DrawLine(dc,Chunk,rc1)
+ end
+ else
+ begin
+ rc1.top:=rc.top;
+ if Lo(D.TextEffect)=effRoll then
+ begin
+// direction!!
+// sz - linesize ; w - frame width, chunk^add = chunk size
+ inc(sz.cx,D.RollGap);
+ rc1.left:=rc.left-Chunk^.add;
+ if (sz.cx-Chunk^.add)<w then
+ begin
+ ch:=Chunk;
+ DrawLine(dc,ch,rc1);
+ rc1.left:=rc1.left+sz.cx;
+ end;
+ if not justpaint then
+ begin
+ inc(Chunk^.add,D.RollStep);
+ if Chunk^.add>=sz.cx then
+ Chunk^.add:=0;
+ end;
+{
+ inc(sz.cx,RollGap);
+ rc1.left:=rc.left-Chunk^.add;
+ if (sz.cx-Chunk^.add)<w then
+ begin
+ ch:=Chunk;
+ DrawLine(dc,ch,rc1);
+ rc1.left:=rc1.left+sz.cx;
+ end;
+ if not justpaint then
+ begin
+ inc(Chunk^.add,RollStep);
+ if Chunk^.add>=sz.cx then
+ Chunk^.add:=0;
+ end;
+}
+ end
+ else
+ begin
+ if not justpaint then
+ begin
+ if Chunk^.dir=ppLeft then
+ begin
+ inc(Chunk^.add,D.RollStep);
+ if (sz.cx-Chunk^.add)<(w-D.RollGap) then
+ begin
+ Chunk^.dir:=ppRight;
+ end;
+ end
+ else
+ begin
+ dec(Chunk^.add,D.RollStep);
+ if Chunk^.add<=-D.RollGap then
+ begin
+ Chunk^.dir:=ppLeft;
+ end;
+ end;
+ end;
+ rc1.left:=rc.left-Chunk^.add;
+ end;
+ DrawLine(dc,Chunk,rc1) // with offset
+ end;
+ end
+ else
+ DrawChunk(dc,Chunk,rc1);
+ inc(rc.top,sz.cy);
+
+ if rc.top>rc.bottom then
+ break;
+ if Chunk^._type<>0 then
+ inc(Chunk);
+ end;
+ SelectClipRgn(dc,0);
+ DeleteObject(rgn);
+end;
+
+procedure tTextBlock.DrawChunks(dc:HDC;Chunk:pChunk;rc:TRECT;justpaint:boolean);
+var
+ sz:TSIZE;
+ rc1:TRECT;
+ h:integer;
+ w:integer;
+ D:pTextData;
+begin
+ D:=pTextData(CustomData);
+
+ SetBkMode(dc,Windows.TRANSPARENT);
+ case Lo(D.TextEffect) of
+ effRoll,effPong: begin
+ DrawLines(dc,Chunk,rc,justpaint);
+ end;
+ else
+ CopyRect(rc1,rc);
+ w:=rc.right-rc.left;
+ h:=0;
+ //!!
+ if (D.TextEffect and effCenter)<>0 then
+ begin
+ MeasureLine(dc,Chunk,sz,w);
+ inc(rc1.left,(w-sz.cx) div 2);
+ end;
+ while Chunk^._type<>0 do
+ begin
+ MeasureChunk(dc,Chunk,sz,false);
+ if sz.cx>0 then
+ begin
+ rc1.right:=rc1.left+sz.cx;
+ if rc1.right>rc.right then //!!!
+ begin
+ case Lo(D.TextEffect) of
+ effCut: begin
+
+ if rc1.left<rc.right then
+ begin
+ rc1.right:=rc.right;
+ DrawChunk(dc,Chunk,rc1);
+ end;
+
+ inc(Chunk);
+ inc(rc1.left,sz.cx);
+ continue;
+ end;
+
+ effWrap: begin
+ if sz.cx>=w then
+ begin
+ while (Chunk<>nil) and (Chunk^._type<>CT_NEWLINE) do
+ begin
+ if (Chunk^._type shr 16)<>0 then
+ ProcessMacro(dc,Chunk); //!! textcolor, bkcolor
+ inc(Chunk);
+ end;
+ if Chunk=nil then
+ exit;
+ end;
+ inc(rc1.top,h);
+ rc1.left:=rc.left;
+ //!!
+ if (D.TextEffect and effCenter)<>0 then
+ begin
+ MeasureLine(dc,Chunk,sz,w);
+ inc(rc1.left,(w-sz.cx) div 2);
+ continue;
+ end;
+ end;
+ end;
+ end;
+ rc1.bottom:=rc1.top+sz.cy;
+ if rc1.bottom>rc.bottom then
+ begin
+ break;
+ end;
+ DrawChunk(dc,Chunk,rc1);
+ inc(rc1.left,sz.cx);
+ if h<sz.cy then
+ h:=sz.cy;
+ end
+ else
+ begin
+ if Chunk^._type=CT_NEWLINE then
+ begin
+ inc(rc1.top,h);
+ rc1.left:=rc.left;
+ //!!
+ if (D.TextEffect and effCenter)<>0 then
+ begin
+ inc(Chunk);
+ MeasureLine(dc,Chunk,sz,w);
+ // if sz.cx<w then
+ inc(rc1.left,(w-sz.cx) div 2);
+ continue;
+ end;
+ end
+ else
+ ProcessMacro(dc,Chunk); //!! textcolor, bkcolor
+ // DrawChunk(dc,Chunk,rc1);
+ end;
+ inc(Chunk);
+ end;
+ end;
+end;
+
+function tTextBlock.Split(src:pWideChar):pChunkArray;
+var
+ Chunk:pChunk;
+ i:integer;
+begin
+ result:=nil;
+ if (src=nil) or (src^=#0) then exit;
+
+ i:=(StrLenW(src)+1)*SizeOf(tChunk); // last = 0 (powered finalization)
+ GetMem(result,i);
+ FillChar(result^,i,0);
+ Chunk:=@result[0];
+
+ while src^<>#0 do
+ begin
+ // signes
+ while not (((src^>='A') and (src^<='Z')) or
+ ((src^>='a') and (src^<='z')) or
+ ((src^>='0') and (src^<='9'))) do
+ begin
+ if (ORD(src^)>127) or (src^='{') then
+ break;
+ if src^<>#10 then
+ src:=CreateSignChunk(Chunk,src)
+ else
+ inc(src);
+ if src^=#0 then exit;
+ end;
+ // [b][/b][i][/i][u][/u][cf][/cf][bg][/bg]
+ if Macro(src,Chunk,pTextData(CustomData).TextColor,pTextData(CustomData).BkColor) then
+ begin
+ end
+ // "{" sign
+ else if src^='{' then // if not macro
+ begin
+ src:=CreateSignChunk(Chunk,src);
+ end
+ // Unicode/text
+ else
+ begin
+ src:=CreateTextChunk(Chunk,src);
+ end;
+ end;
+end;
+
+procedure DeleteChunks(var Chunk:pChunkArray);
+begin
+ if Chunk<>nil then
+ FreeMem(Chunk);
+ Chunk:=nil;
+end;
diff --git a/plugins/Utils.pas/utils.pas b/plugins/Utils.pas/utils.pas
new file mode 100644
index 0000000000..8c16e03f81
--- /dev/null
+++ b/plugins/Utils.pas/utils.pas
@@ -0,0 +1,44 @@
+unit Utils;
+
+interface
+
+uses windows;
+
+function SaveTemporaryW(ptr:pointer;size:dword;ext:PWideChar=nil):pWideChar;
+function SaveTemporary (ptr:pointer;size:dword;ext:PAnsiChar=nil):PAnsiChar;
+
+implementation
+
+uses common,io;
+
+function SaveTemporaryW(ptr:pointer;size:dword;ext:PWideChar=nil):pWideChar;
+var
+ buf,buf1:array [0..MAX_PATH-1] of WideChar;
+ f:THANDLE;
+begin
+ GetTempPathW(MAX_PATH,buf);
+ GetTempFileNameW(buf,'wat',GetCurrentTime,buf1);
+ ChangeExtW(buf1,ext);
+
+ f:=ReWrite(buf1);
+ BlockWrite(f,pByte(ptr)^,size);
+ CloseHandle(f);
+ StrDupW(result,buf1);
+end;
+
+function SaveTemporary(ptr:pointer;size:dword;ext:PAnsiChar=nil):PAnsiChar;
+var
+ buf,buf1:array [0..MAX_PATH-1] of AnsiChar;
+ f:THANDLE;
+begin
+ GetTempPathA(SizeOf(buf),buf);
+ GetTempFileNameA(buf,'wat',GetCurrentTime,buf1);
+ ChangeExt(buf1,ext);
+
+ f:=ReWrite(buf1);
+ BlockWrite(f,pByte(ptr)^,size);
+ CloseHandle(f);
+ StrDup(result,buf1);
+end;
+
+end. \ No newline at end of file
diff --git a/plugins/Utils.pas/wrapdlgs.pas b/plugins/Utils.pas/wrapdlgs.pas
new file mode 100644
index 0000000000..fed2490f85
--- /dev/null
+++ b/plugins/Utils.pas/wrapdlgs.pas
@@ -0,0 +1,130 @@
+{$include compilers.inc}
+unit wrapdlgs;
+
+interface
+
+uses Windows;
+
+function SelectDirectory(Caption:PAnsiChar;var Directory:PAnsiChar;
+ Parent:HWND=0):Boolean; overload;
+function SelectDirectory(Caption:PWideChar;var Directory:PWideChar;
+ Parent:HWND=0):Boolean; overload;
+
+implementation
+
+uses common, messages;
+
+type
+ PSHItemID = ^TSHItemID;
+ TSHItemID = packed record
+ cb: Word; { Size of the ID (including cb itself) }
+ abID: array[0..0] of Byte; { The item ID (variable length) }
+ end;
+
+ PItemIDList = ^TItemIDList;
+ TItemIDList = record
+ mkid: TSHItemID;
+ end;
+
+ TBrowseInfoA = record
+ hwndOwner: HWND;
+ pidlRoot: PItemIDList;
+ pszDisplayName: PAnsiChar; { Return display name of item selected. }
+ lpszTitle: PAnsiChar; { text to go in the banner over the tree. }
+ ulFlags: UINT; { Flags that control the return stuff }
+ lpfn: Pointer; //TFNBFFCallBack;
+ lParam: LPARAM; { extra info that's passed back in callbacks }
+ iImage: Integer; { output var: where to return the Image index. }
+ end;
+ TBrowseInfoW = record
+ hwndOwner: HWND;
+ pidlRoot: PItemIDList;
+ pszDisplayName: PWideChar; { Return display name of item selected. }
+ lpszTitle: PWideChar; { text to go in the banner over the tree. }
+ ulFlags: UINT; { Flags that control the return stuff }
+ lpfn: Pointer; //TFNBFFCallBack;
+ lParam: LPARAM; { extra info that's passed back in callbacks }
+ iImage: Integer; { output var: where to return the Image index. }
+ end;
+
+function SHBrowseForFolderA(var lpbi: TBrowseInfoA): PItemIDList; stdcall;
+ external 'shell32.dll' name 'SHBrowseForFolderA';
+function SHBrowseForFolderW(var lpbi: TBrowseInfoW): PItemIDList; stdcall;
+ external 'shell32.dll' name 'SHBrowseForFolderW';
+function SHGetPathFromIDListA(pidl: PItemIDList; pszPath: PAnsiChar): BOOL; stdcall;
+ external 'shell32.dll' name 'SHGetPathFromIDListA';
+function SHGetPathFromIDListW(pidl: PItemIDList; pszPath: PWideChar): BOOL; stdcall;
+ external 'shell32.dll' name 'SHGetPathFromIDListW';
+procedure CoTaskMemFree(pv: Pointer); stdcall; external 'ole32.dll'
+ name 'CoTaskMemFree';
+
+const
+ BIF_RETURNONLYFSDIRS = $0001; { For finding a folder to start document searching }
+ BIF_DONTGOBELOWDOMAIN = $0002; { For starting the Find Computer }
+ BIF_STATUSTEXT = $0004;
+ BIF_RETURNFSANCESTORS = $0008;
+ BIF_EDITBOX = $0010;
+ BIF_VALIDATE = $0020; { insist on valid result (or CANCEL) }
+ BIF_NEWDIALOGSTYLE = $0040; { Use the new dialog layout with the ability to resize }
+ { Caller needs to call OleInitialize() before using this API (c) JVCL }
+ BIF_BROWSEFORCOMPUTER = $1000; { Browsing for Computers. }
+ BIF_BROWSEFORPRINTER = $2000; { Browsing for Printers }
+ BIF_BROWSEINCLUDEFILES = $4000; { Browsing for Everything }
+
+ BFFM_INITIALIZED = 1;
+ BFFM_SELCHANGED = 2;
+
+ BFFM_SETSTATUSTEXT = WM_USER + 100;
+ BFFM_ENABLEOK = WM_USER + 101;
+ BFFM_SETSELECTION = WM_USER + 102;
+ BFFM_SETSELECTIONW = WM_USER + 103;
+
+function SelectDirectory(Caption:PAnsiChar;var Directory:PAnsiChar;Parent:HWND=0):Boolean;
+var
+ BrowseInfo:TBrowseInfoA;
+ Buffer:array [0..MAX_PATH-1] of AnsiChar;
+ ItemIDList:PItemIDList;
+begin
+ Result:=False;
+ FillChar(BrowseInfo,SizeOf(BrowseInfo),0);
+
+ BrowseInfo.hwndOwner :=Parent;
+ BrowseInfo.pszDisplayName:=@Buffer;
+ BrowseInfo.lpszTitle :=Caption;
+ BrowseInfo.ulFlags :=BIF_RETURNONLYFSDIRS or BIF_NEWDIALOGSTYLE;
+
+ ItemIDList:=ShBrowseForFolderA(BrowseInfo);
+ if ItemIDList<>nil then
+ begin
+ ShGetPathFromIDListA(ItemIDList,Buffer);
+ StrDup(Directory,Buffer);
+ CoTaskMemFree(ItemIDList);
+ result:=true;
+ end;
+end;
+
+function SelectDirectory(Caption:PWideChar;var Directory:PWideChar;Parent:HWND=0):Boolean;
+var
+ BrowseInfo:TBrowseInfoW;
+ Buffer:array [0..MAX_PATH-1] of WideChar;
+ ItemIDList:PItemIDList;
+begin
+ Result:=False;
+ FillChar(BrowseInfo,SizeOf(BrowseInfo),0);
+
+ BrowseInfo.hwndOwner :=Parent;
+ BrowseInfo.pszDisplayName:=@Buffer;
+ BrowseInfo.lpszTitle :=Caption;
+ BrowseInfo.ulFlags :=BIF_RETURNONLYFSDIRS or BIF_NEWDIALOGSTYLE;
+
+ ItemIDList:=ShBrowseForFolderW(BrowseInfo);
+ if ItemIDList<>nil then
+ begin
+ ShGetPathFromIDListW(ItemIDList,Buffer);
+ StrDupW(Directory,Buffer);
+ CoTaskMemFree(ItemIDList);
+ result:=true;
+ end;
+end;
+
+end.
diff --git a/plugins/Utils.pas/wrapper.pas b/plugins/Utils.pas/wrapper.pas
new file mode 100644
index 0000000000..91f7b23b0b
--- /dev/null
+++ b/plugins/Utils.pas/wrapper.pas
@@ -0,0 +1,513 @@
+{$include compilers.inc}
+unit wrapper;
+
+interface
+
+uses windows;
+
+function CreateHiddenWindow(proc:pointer=nil):HWND;
+
+function DoInitCommonControls(dwICC:DWORD):boolean;
+
+function GetScreenRect:TRect;
+procedure SnapToScreen(var rc:TRect;dx:integer=0;dy:integer=0{;
+ minw:integer=240;minh:integer=100});
+
+function GetDlgText(Dialog:HWND;idc:integer;getAnsi:boolean=false):pointer; overload;
+function GetDlgText(wnd:HWND;getAnsi:boolean=false):pointer; overload;
+
+function StringToGUID(const astr:PAnsiChar):TGUID; overload;
+function StringToGUID(const astr:PWideChar):TGUID; overload;
+
+// Comboboxes
+function CB_SelectData(cb:HWND;data:dword):lresult; overload;
+function CB_SelectData(Dialog:HWND;id:cardinal;data:dword):lresult; overload;
+function CB_GetData (cb:HWND;idx:integer=-1):lresult;
+function CB_AddStrData (cb:HWND;astr:pAnsiChar;data:integer=0;idx:integer=-1):HWND;
+function CB_AddStrDataW(cb:HWND;astr:pWideChar;data:integer=0;idx:integer=-1):HWND;
+
+// CommCtrl - ListView
+Procedure ListView_GetItemTextA(hwndLV:hwnd;i:WPARAM;iSubItem:integer;pszText:Pointer;cchTextMax:integer);
+Procedure ListView_GetItemTextW(hwndLV:hwnd;i:WPARAM;iSubItem:integer;pszText:Pointer;cchTextMax:integer);
+function LV_GetLParam (list:HWND;item:integer=-1):lresult;
+function LV_SetLParam (list:HWND;lParam:LPARAM;item:integer=-1):lresult;
+function LV_ItemAtPos(wnd:HWND;pt:TPOINT;var SubItem:dword):Integer; overload;
+function LV_ItemAtPos(wnd:HWND;x,y:integer;var SubItem:dword):Integer; overload;
+procedure LV_SetItem (handle:hwnd;str:PAnsiChar;item:integer;subitem:integer=0);
+procedure LV_SetItemW(handle:hwnd;str:PWideChar;item:integer;subitem:integer=0);
+function LV_MoveItem(list:hwnd;direction:integer;item:integer=-1):integer;
+function LV_GetColumnCount(list:HWND):lresult;
+function LV_CheckDirection(list:HWND):integer; // bit 0 - can move up, bit 1 - down
+
+// CommDLG - Dialogs
+function ShowDlg (dst:PAnsiChar;fname:PAnsiChar=nil;Filter:PAnsiChar=nil;open:boolean=true):boolean;
+function ShowDlgW(dst:PWideChar;fname:PWideChar=nil;Filter:PWideChar=nil;open:boolean=true):boolean;
+
+implementation
+
+uses messages,common,commctrl,commdlg;
+
+const
+ EmptyGUID:TGUID = '{00000000-0000-0000-0000-000000000000}';
+
+{$IFNDEF FPC}
+const
+ LVM_SORTITEMSEX = LVM_FIRST + 81;
+{$ENDIF}
+
+{$IFNDEF DELPHI_7_UP}
+const
+ SM_XVIRTUALSCREEN = 76;
+ SM_YVIRTUALSCREEN = 77;
+ SM_CXVIRTUALSCREEN = 78;
+ SM_CYVIRTUALSCREEN = 79;
+{$ENDIF}
+
+//----- Hidden Window functions -----
+const
+ HWND_MESSAGE = HWND(-3);
+const
+ hiddenwindow:HWND = 0;
+ hwndcount:integer=0;
+
+function HiddenWindProc(wnd:HWnd; msg:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+begin
+ if msg=WM_CLOSE then
+ begin
+ dec(hwndcount);
+ if hwndcount>0 then // not all references gone
+ begin
+ result:=0;
+ exit
+ end
+ else
+ hiddenwindow:=0
+ end;
+
+ result:=DefWindowProcW(wnd,msg,wParam,lParam);
+end;
+
+function CreateHiddenWindow(proc:pointer=nil):HWND;
+begin
+ if proc=nil then
+ begin
+ if hiddenwindow<>0 then
+ begin
+ result:=hiddenwindow;
+ inc(hwndcount); // one reference more
+ end
+ else
+ begin
+ result:=CreateWindowExW(0,'STATIC',nil,0,
+ 1,1,1,1,HWND_MESSAGE,0,hInstance,nil);
+ if result<>0 then
+ SetWindowLongPtrW(result,GWL_WNDPROC,LONG_PTR(@HiddenWindProc));
+
+ hiddenwindow:=result;
+ end
+ end
+ else
+ begin
+ result:=CreateWindowExW(0,'STATIC',nil,0,
+ 1,1,1,1,HWND_MESSAGE,0,hInstance,nil);
+ if result<>0 then
+ SetWindowLongPtrW(result,GWL_WNDPROC,LONG_PTR(proc));
+ end;
+end;
+//----- End of hidden window functions -----
+
+function DoInitCommonControls(dwICC:DWORD):boolean;
+var
+ ICC: TInitCommonControlsEx;
+begin
+ if dwICC=0 then
+ dwICC:=ICC_STANDARD_CLASSES or ICC_WIN95_CLASSES;
+ ICC.dwSize:= Sizeof(ICC);
+ ICC.dwICC := dwICC;
+ result:=InitCommonControlsEx(ICC);
+end;
+
+function GetScreenRect:TRect;
+begin
+ result.left := GetSystemMetrics( SM_XVIRTUALSCREEN );
+ result.top := GetSystemMetrics( SM_YVIRTUALSCREEN );
+ result.right := GetSystemMetrics( SM_CXVIRTUALSCREEN ) + result.left;
+ result.bottom:= GetSystemMetrics( SM_CYVIRTUALSCREEN ) + result.top;
+end;
+
+procedure SnapToScreen(var rc:TRect;dx:integer=0;dy:integer=0{;
+ minw:integer=240;minh:integer=100});
+var
+ rect:TRect;
+begin
+ rect:=GetScreenRect;
+ if rc.right >rect.right then rc.right :=rect.right -dx;
+ if rc.bottom>rect.bottom then rc.bottom:=rect.bottom-dy;
+ if rc.left <rect.left then rc.left :=rect.left;
+ if rc.top <rect.top then rc.top :=rect.top;
+end;
+
+function GetDlgText(wnd:HWND;getAnsi:boolean=false):pointer;
+var
+ a:cardinal;
+begin
+ result:=nil;
+ if getAnsi then
+ begin
+ a:=SendMessageA(wnd,WM_GETTEXTLENGTH,0,0)+1;
+ if a>1 then
+ begin
+ mGetMem(PAnsiChar(result),a);
+ SendMessageA(wnd,WM_GETTEXT,a,lparam(result));
+ end;
+ end
+ else
+ begin
+ a:=SendMessageW(wnd,WM_GETTEXTLENGTH,0,0)+1;
+ if a>1 then
+ begin
+ mGetMem(pWideChar(result),a*SizeOf(WideChar));
+ SendMessageW(wnd,WM_GETTEXT,a,lparam(result));
+ end;
+ end;
+end;
+
+function GetDlgText(Dialog:HWND;idc:integer;getAnsi:boolean=false):pointer;
+begin
+ result:=GetDlgText(GetDlgItem(Dialog,idc),getAnsi);
+end;
+
+//----- Combobox functions -----
+
+function CB_SelectData(cb:HWND;data:dword):lresult; overload;
+var
+ i:integer;
+begin
+ result:=0;
+ for i:=0 to SendMessage(cb,CB_GETCOUNT,0,0)-1 do
+ begin
+ if data=dword(SendMessage(cb,CB_GETITEMDATA,i,0)) then
+ begin
+ result:=i;
+ break;
+ end;
+ end;
+ result:=SendMessage(cb,CB_SETCURSEL,result,0);
+end;
+
+function CB_SelectData(Dialog:HWND;id:cardinal;data:dword):lresult; overload;
+begin
+ result:=CB_SelectData(GetDlgItem(Dialog,id),data);
+end;
+
+function CB_GetData(cb:HWND;idx:integer=-1):lresult;
+begin
+ if idx<0 then
+ idx:=SendMessage(cb,CB_GETCURSEL,0,0);
+ if idx<0 then
+ result:=0
+ else
+ result:=SendMessage(cb,CB_GETITEMDATA,idx,0);
+end;
+
+function CB_AddStrData(cb:HWND;astr:pAnsiChar;data:integer=0;idx:integer=-1):HWND;
+begin
+ result:=cb;
+ if idx<0 then
+ idx:=SendMessageA(cb,CB_ADDSTRING,0,lparam(astr))
+ else
+ idx:=SendMessageA(cb,CB_INSERTSTRING,idx,lparam(astr));
+ SendMessageA(cb,CB_SETITEMDATA,idx,data);
+end;
+
+function CB_AddStrDataW(cb:HWND;astr:pWideChar;data:integer=0;idx:integer=-1):HWND;
+begin
+ result:=cb;
+ if idx<0 then
+ idx:=SendMessageW(cb,CB_ADDSTRING,0,lparam(astr))
+ else
+ idx:=SendMessageW(cb,CB_INSERTSTRING,idx,lparam(astr));
+ SendMessage(cb,CB_SETITEMDATA,idx,data);
+end;
+
+function StringToGUID(const astr:PAnsiChar):TGUID;
+var
+ i:integer;
+begin
+ result:=EmptyGUID;
+ if StrLen(astr)<>38 then exit;
+ result.D1:=HexToInt(PAnsiChar(@astr[01]),8);
+ result.D2:=HexToInt(PAnsiChar(@astr[10]),4);
+ result.D3:=HexToInt(PAnsiChar(@astr[15]),4);
+
+ result.D4[0]:=HexToInt(PAnsiChar(@astr[20]),2);
+ result.D4[1]:=HexToInt(PAnsiChar(@astr[22]),2);
+ for i:=2 to 7 do
+ begin
+ result.D4[i]:=HexToInt(PAnsiChar(@astr[21+i*2]),2);
+ end;
+end;
+
+function StringToGUID(const astr:PWideChar):TGUID;
+var
+ i:integer;
+begin
+ result:=EmptyGUID;
+ if StrLenW(astr)<>38 then exit;
+ result.D1:=HexToInt(pWideChar(@astr[01]),8);
+ result.D2:=HexToInt(pWideChar(@astr[10]),4);
+ result.D3:=HexToInt(pWideChar(@astr[15]),4);
+
+ result.D4[0]:=HexToInt(pWideChar(@astr[20]),2);
+ result.D4[1]:=HexToInt(pWideChar(@astr[22]),2);
+ for i:=2 to 7 do
+ begin
+ result.D4[i]:=HexToInt(pWideChar(@astr[21+i*2]),2);
+ end;
+end;
+
+//----- ListView functions -----
+
+Procedure ListView_GetItemTextA(hwndLV:hwnd;i:WPARAM;iSubItem:integer;pszText:Pointer;cchTextMax:integer);
+Var
+ lvi:LV_ITEMA;
+Begin
+ lvi.iSubItem :=iSubItem;
+ lvi.cchTextMax:=cchTextMax;
+ lvi.pszText :=pszText;
+ SendMessageA(hwndLV,LVM_GETITEMTEXT,i,LPARAM(@lvi));
+end;
+
+Procedure ListView_GetItemTextW(hwndLV:hwnd;i:WPARAM;iSubItem:integer;pszText:Pointer;cchTextMax:integer);
+Var
+ lvi:LV_ITEMW;
+Begin
+ lvi.iSubItem :=iSubItem;
+ lvi.cchTextMax:=cchTextMax;
+ lvi.pszText :=pszText;
+ SendMessageW(hwndLV,LVM_GETITEMTEXT,i,LPARAM(@lvi));
+end;
+
+procedure LV_SetItem(handle:hwnd;str:PAnsiChar;item:integer;subitem:integer=0);
+var
+ li:LV_ITEMA;
+begin
+// zeromemory(@li,sizeof(li));
+ li.mask :=LVIF_TEXT;
+ li.pszText :=str;
+ li.iItem :=item;
+ li.iSubItem:=subitem;
+ SendMessageA(handle,LVM_SETITEMA,0,lparam(@li));
+end;
+
+procedure LV_SetItemW(handle:hwnd;str:PWideChar;item:integer;subitem:integer=0);
+var
+ li:LV_ITEMW;
+begin
+// zeromemory(@li,sizeof(li));
+ li.mask :=LVIF_TEXT;
+ li.pszText :=str;
+ li.iItem :=item;
+ li.iSubItem:=subitem;
+ SendMessageW(handle,LVM_SETITEMW,0,lparam(@li));
+end;
+
+function LV_GetLParam(list:HWND;item:integer=-1):lresult;
+var
+ li:LV_ITEMW;
+begin
+ if item<0 then
+ begin
+ item:=SendMessage(list,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
+ if item<0 then
+ begin
+ result:=-1;
+ exit;
+ end;
+ end;
+ li.iItem :=item;
+ li.mask :=LVIF_PARAM;
+ li.iSubItem:=0;
+ SendMessageW(list,LVM_GETITEMW,0,lparam(@li));
+ result:=li.lParam;
+end;
+
+function LV_SetLParam(list:HWND;lParam:LPARAM;item:integer=-1):lresult;
+var
+ li:LV_ITEMW;
+begin
+ if item<0 then
+ begin
+ item:=SendMessage(list,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
+ if item<0 then
+ begin
+ result:=-1;
+ exit;
+ end;
+ end;
+ li.iItem :=item;
+ li.mask :=LVIF_PARAM;
+ li.lParam :=lParam;
+ li.iSubItem:=0;
+ SendMessageW(list,LVM_SETITEMW,0,windows.lparam(@li));
+ result:=lParam;
+end;
+
+function LV_ItemAtPos(wnd:HWND;Pt:TPOINT;var SubItem:dword):Integer;
+var
+ HTI:LV_HITTESTINFO;
+begin
+ HTI.pt.x := pt.X;
+ HTI.pt.y := pt.Y;
+ SendMessage(wnd,LVM_SUBITEMHITTEST,0,lparam(@HTI));
+ Result :=HTI.iItem;
+ if @SubItem<>nil then
+ SubItem:=HTI.iSubItem;
+end;
+
+function LV_ItemAtPos(wnd:HWND;x,y:integer;var SubItem:dword):Integer; overload;
+var
+ HTI:LV_HITTESTINFO;
+begin
+ HTI.pt.x := x;
+ HTI.pt.y := y;
+ SendMessage(wnd,LVM_SUBITEMHITTEST,0,lparam(@HTI));
+ Result :=HTI.iItem;
+ if @SubItem<>nil then
+ SubItem:=HTI.iSubItem;
+end;
+
+function LV_Compare(lParam1,lParam2,param:LPARAM):integer; stdcall;
+var
+ olditem,neibor:integer;
+begin
+ result:=lParam1-lParam2;
+ neibor :=hiword(param);
+ olditem:=loword(param);
+ if neibor>olditem then
+ begin
+ if (lParam1=olditem) and (lParam2<=neibor) then
+ result:=1;
+ end
+ else
+ begin
+ if (lParam2=olditem) and (lParam1>=neibor) then
+ result:=1;
+ end;
+end;
+
+function LV_MoveItem(list:hwnd;direction:integer;item:integer=-1):integer;
+begin
+ if ((direction>0) and (item=(SendMessage(list,LVM_GETITEMCOUNT,0,0)-1))) or
+ ((direction<0) and (item=0)) then
+ begin
+ result:=item;
+ exit;
+ end;
+
+ if item<0 then
+ item:=SendMessage(list,LVM_GETNEXTITEM,-1,LVNI_FOCUSED);
+ SendMessageW(list,LVM_SORTITEMSEX,wparam(item)+(wparam(item+direction) shl 16),lparam(@LV_Compare));
+ result:=item+direction;
+end;
+
+function LV_GetColumnCount(list:HWND):lresult;
+begin
+ result:=SendMessage(SendMessage(list,LVM_GETHEADER,0,0),HDM_GETITEMCOUNT,0,0);
+end;
+
+function LV_CheckDirection(list:HWND):integer;
+var
+ i,cnt{,selcnt}:integer;
+ stat,first,last,focus: integer;
+begin
+ first :=-1;
+ last :=-1;
+ focus :=-1;
+ cnt :=SendMessage(list,LVM_GETITEMCOUNT,0,0)-1;
+// selcnt:=SendMessage(list,LVM_GETSELECTEDCOUNT,0,0);
+ for i:=0 to cnt do
+ begin
+ stat:=SendMessage(list,LVM_GETITEMSTATE,i,LVIS_SELECTED or LVIS_FOCUSED);
+ if (stat and LVIS_SELECTED)<>0 then
+ begin
+ if (stat and LVIS_FOCUSED)<>0 then
+ focus:=i;
+ if first<0 then first:=i;
+ last:=i;
+ end;
+ end;
+ result:=0;
+ if focus<0 then
+ focus:=first;
+ if focus>=0 then
+ result:=result or ((focus+1) shl 16);
+ if first>0 then // at least one selected and not first
+ begin
+ result:=(result or 1){ or (first+1) shl 16};
+ end;
+ if (last>=0) and (last<cnt) then
+ result:=result or 2;
+end;
+
+//----- CommDlg procedures -----
+
+function ShowDlg(dst:PAnsiChar;fname:PAnsiChar=nil;Filter:PAnsiChar=nil;open:boolean=true):boolean;
+var
+ NameRec:OpenFileNameA;
+begin
+ FillChar(NameRec,SizeOf(NameRec),0);
+ with NameRec do
+ begin
+ LStructSize:=SizeOf(NameRec);
+ if fname=nil then
+ dst[0]:=#0
+ else if fname<>dst then
+ StrCopy(dst,fname);
+// lpstrInitialDir:=dst;
+ if Filter<>nil then
+ begin
+ lpstrDefExt:=StrEnd(Filter)+1;
+ inc(lpstrDefExt,2); // skip "*."
+ end;
+ lpStrFile :=dst;
+ lpStrFilter:=Filter;
+ NMaxFile :=511;
+ Flags :=OFN_EXPLORER or OFN_OVERWRITEPROMPT;// or OFN_HIDEREADONLY;
+ end;
+ if open then
+ result:=GetOpenFileNameA({$IFDEF FPC}@{$ENDIF}NameRec)
+ else
+ result:=GetSaveFileNameA({$IFDEF FPC}@{$ENDIF}NameRec);
+end;
+
+function ShowDlgW(dst:PWideChar;fname:PWideChar=nil;Filter:PWideChar=nil;open:boolean=true):boolean;
+var
+ NameRec:OpenFileNameW;
+begin
+ FillChar(NameRec,SizeOf(NameRec),0);
+ with NameRec do
+ begin
+ LStructSize:=SizeOf(NameRec);
+ if fname=nil then
+ dst[0]:=#0
+ else if fname<>dst then
+ StrCopyW(dst,fname);
+// lpstrInitialDir:=dst;
+ if Filter<>nil then
+ begin
+ lpstrDefExt:=StrEndW(Filter)+1;
+ inc(lpstrDefExt,2); // skip "*."
+ end;
+ lpStrFile :=dst;
+ lpStrFilter:=Filter;
+ NMaxFile :=511;
+ Flags :=OFN_EXPLORER or OFN_OVERWRITEPROMPT;// or OFN_HIDEREADONLY;
+ end;
+ if open then
+ result:=GetOpenFileNameW({$IFDEF FPC}@{$ENDIF}NameRec)
+ else
+ result:=GetSaveFileNameW({$IFDEF FPC}@{$ENDIF}NameRec)
+end;
+
+end.
diff --git a/plugins/Utils.pas/zwrapper.pas b/plugins/Utils.pas/zwrapper.pas
new file mode 100644
index 0000000000..7ccffafb14
--- /dev/null
+++ b/plugins/Utils.pas/zwrapper.pas
@@ -0,0 +1,58 @@
+unit zwrapper;
+
+interface
+
+function ZDecompressBuf(const inBuffer: Pointer; inSize: Integer; out outBuffer: Pointer; out outSize: Integer; outEstimate: Integer): Integer;
+
+implementation
+
+uses zlib;
+
+function ZDecompressBuf(const inBuffer: Pointer; inSize: Integer; out outBuffer: Pointer; out outSize: Integer; outEstimate: Integer): Integer;
+var
+ zstream : TZStreamRec;
+ delta : Integer;
+begin
+ FillChar(zstream, SizeOf(TZStreamRec), 0);
+
+ delta := (inSize + 255) and not 255;
+
+ if outEstimate = 0 then outSize := delta
+ else outSize := outEstimate;
+ Result := Z_OK;
+ GetMem(outBuffer, outSize);
+ try
+ zstream.next_in := inBuffer;
+ zstream.avail_in := inSize;
+ zstream.next_out := outBuffer;
+ zstream.avail_out := outSize;
+
+ Result := InflateInit(zstream);
+ if Result < 0 then Exit;
+
+ try
+ Result := inflate(zstream, Z_NO_FLUSH);
+ if Result < 0 then Exit;
+
+ while (Result <> Z_STREAM_END) do begin
+ Inc(outSize, delta);
+ ReallocMem(outBuffer, outSize);
+
+ zstream.next_out := {$IFDEF FPC}PBytef{$ENDIF}(pByte(outBuffer) + zstream.total_out);
+ zstream.avail_out := delta;
+ Result := inflate(zstream, Z_NO_FLUSH);
+ if Result < 0 then Exit;
+ end;
+ finally
+ inflateEnd(zstream);
+ end;
+
+ ReallocMem(outBuffer, zstream.total_out);
+ outSize := zstream.total_out;
+
+ finally
+ if Result < 0 then FreeMem(outBuffer);
+ end;
+end;
+
+end. \ No newline at end of file
diff --git a/plugins/Watrack/HlpDlg.pas b/plugins/Watrack/HlpDlg.pas
new file mode 100644
index 0000000000..c420345bd4
--- /dev/null
+++ b/plugins/Watrack/HlpDlg.pas
@@ -0,0 +1,83 @@
+{help dialogs}
+unit HlpDlg;
+
+interface
+
+uses windows;
+
+const
+ sFormatHelp:PWideChar = 'Text format codes'#13#10'{b}text{/b}'#9'bold'#13#10+
+ '{i}text{/i}'#9'italic'#13#10'{u}text{/u}'#9'undeline'#13#10+
+ '{cf##}text{/cf}'#9'text color'#13#10'{bg##}text{/bg}'#9+
+ 'background color'#13#10'text - user text'#13#10+
+ '## - color number (1-16)'#13#10'Color 0 is background color'#13#10+
+ 'Color 17 is default text color';
+
+function ShowColorHelpDlg(parent:HWND):integer;
+
+implementation
+
+uses messages,m_api;
+
+{$include res\i_const.inc}
+
+const
+ colors:array [0..15] of dword = (
+ $00FFFFFF,$00000000,$007F0000,$00009300,
+ $000000FF,$0000007F,$009C009C,$00007FFC,
+ $0000FFFF,$0000FC00,$00939300,$00FFFF00,
+ $00FC0000,$00FF00FF,$007F7F7F,$00D2D2D2
+ );
+
+const
+ COLORDLG = 'COLOR';
+
+function ColorHelpDlg(Dialog:HWnd;hMessage,wParam,lParam:DWord):integer; stdcall;
+var
+ ps:tPaintStruct;
+ br:hBrush;
+ dc:hDC;
+ rc:tRect;
+ i,j:integer;
+begin
+ case hMessage of
+ WM_INITDIALOG: begin
+ TranslateDialogDefault(Dialog);
+ end;
+ WM_COMMAND:
+ if (wParam shr 16)=BN_CLICKED then
+ case loword(wParam) of
+ IDOK, IDCANCEL: DestroyWindow(Dialog);
+ end;
+ WM_PAINT: begin
+ dc:=BeginPaint(Dialog,ps);
+ SetBkColor(dc,GetSysColor(COLOR_BTNFACE));
+ for i:=0 to 1 do
+ begin
+ for j:=0 to 7 do
+ begin
+ with rc do
+ begin
+ left :=32+i*66;
+ top :=10+j*18;
+ right :=56+i*66;
+ bottom:=24+j*18;
+ end;
+ br:=CreateSolidBrush(colors[i*8+j]);
+ Rectangle(dc,rc.left-1,rc.top-1,rc.right+1,rc.bottom+1);
+ FillRect(dc,rc,br);
+ DeleteObject(br);
+ end;
+ end;
+ EndPaint(Dialog,ps);
+ end;
+ end;
+ result:=0;
+end;
+
+function ShowColorHelpDlg(parent:HWND):integer;
+begin
+ result:=CreateDialogW(hInstance,COLORDLG,parent,@ColorHelpDlg);
+end;
+
+end. \ No newline at end of file
diff --git a/plugins/Watrack/docs/const.php b/plugins/Watrack/docs/const.php
new file mode 100644
index 0000000000..f34156280e
--- /dev/null
+++ b/plugins/Watrack/docs/const.php
@@ -0,0 +1,157 @@
+<?php
+
+// SongInfo offsets
+
+define("wato_artist" ,0);
+define("wato_title" ,wato_artist +4);
+define("wato_album" ,wato_title +4);
+define("wato_genre" ,wato_album +4);
+define("wato_comment" ,wato_genre +4);
+define("wato_year" ,wato_comment +4);
+define("wato_mfile" ,wato_year +4);
+define("wato_kbps" ,wato_mfile +4);
+define("wato_khz" ,wato_kbps +4);
+define("wato_channels",wato_khz +4);
+define("wato_track" ,wato_channels+4);
+define("wato_total" ,wato_track +4);
+define("wato_time" ,wato_total +4);
+define("wato_wndtext" ,wato_time +4);
+define("wato_player" ,wato_wndtext +4);
+define("wato_plyver" ,wato_player +4);
+define("wato_icon" ,wato_plyver +4);
+define("wato_fsize" ,wato_icon +4);
+define("wato_vbr" ,wato_fsize +4);
+define("wato_status" ,wato_vbr +4);
+define("wato_plwnd" ,wato_status +4);
+define("wato_codec" ,wato_plwnd +4);
+define("wato_width" ,wato_codec +4);
+define("wato_height" ,wato_width +4);
+define("wato_fps" ,wato_height +4);
+define("wato_date" ,wato_fps +4);
+define("wato_txtver" ,wato_date +4);
+define("wato_lyric" ,wato_txtver +4);
+define("wato_cover" ,wato_lyric +4);
+define("wato_volume" ,wato_cover +4);
+define("wato_url" ,wato_volume +4);
+
+// player control commands
+
+define("WAT_CTRL_PREV" ,1);
+define("WAT_CTRL_PLAY" ,2);
+define("WAT_CTRL_PAUSE",3);
+define("WAT_CTRL_STOP" ,4);
+define("WAT_CTRL_NEXT" ,5);
+define("WAT_CTRL_VOLDN",6);
+define("WAT_CTRL_VOLUP",7);
+define("WAT_CTRL_SEEK" ,8);
+
+// hook service actions
+
+define("WAT_ACT_REGISTER" ,1);
+define("WAT_ACT_UNREGISTER",2);
+define("WAT_ACT_DISABLE" ,3);
+define("WAT_ACT_ENABLE" ,4);
+define("WAT_ACT_GETSTATUS" ,5); // not found/enabled/disabled
+define("WAT_ACT_SETACTIVE" ,6);
+define("WAT_ACT_REPLACE" ,0x10000); // can be combined with WAT_REGISTERFORMAT
+
+// result codes
+
+define("WAT_RES_UNKNOWN" ,-2);
+define("WAT_RES_NOTFOUND",-1);
+define("WAT_RES_ERROR" ,WAT_RES_NOTFOUND);
+define("WAT_RES_OK" ,0);
+define("WAT_RES_DISABLED",1);
+define("WAT_RES_ENABLED" ,WAT_RES_OK);
+define("WAT_RES_NEWFILE" ,3);
+
+// hook service options
+
+define("WAT_OPT_DISABLED" ,0x00000001); // registered but disabled
+define("WAT_OPT_ONLYONE" ,0x00000002); // can't be overwriten
+define("WAT_OPT_PLAYERINFO" ,0x00000004); // song info from player
+define("WAT_OPT_WINAMPAPI" ,0x00000008); // Winamp API support
+define("WAT_OPT_CHECKTIME" ,0x00000010); // check file time for changes
+define("WAT_OPT_VIDEO" ,0x00000020); // only for format registering used
+define("WAT_OPT_LAST" ,0x00000040); // (internal)
+define("WAT_OPT_FIRST" ,0x00000080); // (internal)
+define("WAT_OPT_TEMPLATE" ,0x00000100); // (internal)
+define("WAT_OPT_IMPLANTANT" ,0x00000200); // use process implantation
+define("WAT_OPT_HASURL" ,0x00000400); // (player registration) URL field present
+define("WAT_OPT_CHANGES" ,0x00000800); // obtain only chaged values
+ // (volume, status, window text, elapsed time)
+
+define("WAT_OPT_APPCOMMAND" ,0x00001000); // Special (multimedia) key support
+define("WAT_OPT_CHECKALL" ,0x00002000); // Check all players
+define("WAT_OPT_KEEPOLD" ,0x00004000); // Keep Old opened file
+define("WAT_OPT_MULTITHREAD",0x00008000); // Use multithread scan
+define("WAT_OPT_SINGLEINST" ,0x00010000); // Single player instance
+// services
+
+define("MS_WAT_GETMUSICINFO" ,"WATrack/GetMusicInfo");
+define("MS_WAT_GETFILEINFO" ,"WATrack/GetFileInfo");
+define("MS_WAT_SHOWMUSICINFO","WATrack/ShowMusicInfo");
+define("MS_WAT_MAKEREPORT" ,"WATrack/MakeReport");
+define("MS_WAT_PACKLOG" ,"WATrack/PackLog");
+define("MS_WAT_ADDTOLOG" ,"WATrack/AddToLog");
+define("MS_WAT_PLUGINSTATUS" ,"WATrack/PluginStatus");
+define("MS_WAT_PRESSBUTTON" ,"WATrack/PressButton");
+define("MS_WAT_REPLACETEXT" ,"WATrack/ReplaceText");
+
+// events
+
+define("ME_WAT_MODULELOADED" ,"WATrack/ModuleLoaded");
+define("ME_WAT_NEWSTATUS" ,"WATrack/NewStatus");
+
+// hook services
+
+define("MS_WAT_FORMAT" ,"WATrack/Format");
+define("MS_WAT_WINAMPINFO" ,"WATrack/WinampInfo");
+define("MS_WAT_WINAMPCOMMAND","WATrack/WinampCommand");
+define("MS_WAT_PLAYER" ,"WATrack/Player");
+
+// GetMuscInfo flags
+define("WAT_INF_UNICODE",0);
+define("WAT_INF_ANSI" ,1);
+define("WAT_INF_UTF8" ,2);
+define("WAT_INF_CHANGES",0x100);
+
+// player status
+
+define("WAT_PLS_NORMAL" ,WAT_RES_OK);
+define("WAT_PLS_NOMUSIC" ,WAT_RES_DISABLED);
+define("WAT_PLS_NOTFOUND",WAT_RES_NOTFOUND);
+
+// media status
+
+define("WAT_MES_STOPPED",0);
+define("WAT_MES_PLAYING",1);
+define("WAT_MES_PAUSED" ,2);
+define("WAT_MES_UNKNOWN",-1);
+
+// event types for History
+
+define("EVENTTYPE_WAT_REQUEST",9601);
+define("EVENTTYPE_WAT_ANSWER" ,9602);
+define("EVENTTYPE_WAT_ERROR" ,9603);
+define("EVENTTYPE_WAT_MESSAGE",9604);
+
+// Status events
+define("WAT_EVENT_PLAYERSTATUS",1);
+define("WAT_EVENT_NEWTRACK" ,2);
+define("WAT_EVENT_PLUGINSTATUS",3);
+define("WAT_EVENT_NEWPLAYER" ,4);
+define("WAT_EVENT_NEWTEMPLATE" ,5);
+
+define("TM_MESSAGE" ,0); // privat message
+define("TM_CHANNEL" ,1); // chat
+define("TM_STAT_TITLE",2); // xstatus title
+define("TM_STAT_TEXT" ,3); // [x]status text
+define("TM_POPTITLE" ,4); // popup title
+define("TM_POPTEXT" ,5); // popup text
+define("TM_EXPORT" ,6); // other app
+define("TM_FRAMEINFO" ,7); // frame
+
+define("TM_SETTEXT" ,0x100); // only for service
+define("TM_GETTEXT" ,0); // only for service
+?>
diff --git a/plugins/Watrack/docs/m_music.h b/plugins/Watrack/docs/m_music.h
new file mode 100644
index 0000000000..0246fabe34
--- /dev/null
+++ b/plugins/Watrack/docs/m_music.h
@@ -0,0 +1,386 @@
+#ifndef M_MUSIC
+#define M_MUSIC
+
+#define MIID_WATRACK {0xfc6c81f4, 0x837e, 0x4430, {0x96, 0x01, 0xa0, 0xaa, 0x43, 0x17, 0x7a, 0xe3}}
+
+typedef struct tSongInfoA {
+ CHAR* artist;
+ CHAR* title;
+ CHAR* album;
+ CHAR* genre;
+ CHAR* comment;
+ CHAR* year;
+ CHAR* mfile; // media file
+ DWORD kbps;
+ DWORD khz;
+ DWORD channels;
+ DWORD track;
+ DWORD total; // music length
+ DWORD time; // elapsed time
+ CHAR* wndtext; // window title
+ CHAR* player; // player name
+ DWORD plyver; // player version
+ HANDLE icon; // player icon
+ DWORD fsize; // media file size
+ DWORD vbr;
+ int status; // WAT_MES_* const
+ HWND plwnd; // player window
+ // video part
+ DWORD codec;
+ DWORD width;
+ DWORD height;
+ DWORD fps;
+ __int64 date;
+ CHAR* txtver;
+ CHAR* lyric;
+ CHAR* cover;
+ DWORD volume;
+ CHAR* url; // player homepage
+} SONGINFOA, *LPSONGINFOA;
+
+typedef struct tSongInfo {
+ WCHAR* artist;
+ WCHAR* title;
+ WCHAR* album;
+ WCHAR* genre;
+ WCHAR* comment;
+ WCHAR* year;
+ WCHAR* mfile; // media file
+ DWORD kbps;
+ DWORD khz;
+ DWORD channels;
+ DWORD track;
+ DWORD total; // music length
+ DWORD time; // elapsed time
+ WCHAR* wndtext; // window title
+ WCHAR* player; // player name
+ DWORD* plyver; // player version
+ HANDLE icon; // player icon
+ DWORD fsize; // media file size
+ DWORD vbr;
+ int status; // WAT_MES_* const
+ HWND plwnd; // player window
+ // video part
+ DWORD codec;
+ DWORD width;
+ DWORD height;
+ DWORD fps;
+ __int64 date;
+ WCHAR* txtver;
+ WCHAR* lyric;
+ WCHAR* cover; // cover path
+ DWORD volume;
+ WCHAR* url; // player homepage
+} SONGINFO, *LPSONGINFO;
+
+#if defined(_UNICODE)
+ #define WAT_INF_TCHAR WAT_INF_UNICODE
+ #define SongInfoT tSongInfo
+#else
+ #define WAT_INF_TCHAR WAT_INF_ANSI
+ #define SongInfoT tSongInfoA
+#endif
+
+ // result codes
+#define WAT_RES_UNKNOWN -2
+#define WAT_RES_NOTFOUND -1
+#define WAT_RES_ERROR WAT_RES_NOTFOUND
+#define WAT_RES_OK 0
+#define WAT_RES_ENABLED WAT_RES_OK
+#define WAT_RES_DISABLED 1
+ // internal
+#define WAT_RES_NEWFILE 3
+#define WAT_RES_NEWPLAYER 4
+
+// result for MS_WAT_GETMUSICINFO service
+#define WAT_PLS_NORMAL WAT_RES_OK
+#define WAT_PLS_NOMUSIC WAT_RES_DISABLED
+#define WAT_PLS_NOTFOUND WAT_RES_NOTFOUND
+
+#define WAT_INF_UNICODE 0
+#define WAT_INF_ANSI 1
+#define WAT_INF_UTF8 2
+#define WAT_INF_CHANGES 0x100
+
+/*
+ wParam : WAT_INF_* constant
+ lParam : pointer to LPSONGINGO (Unicode) or LPSONGINFOA (ANSI/UTF8)
+ Affects: Fill structure by currently played music info
+ returns: WAT_PLS_* constant
+ note: pointer will be point to global SONGINFO structure of plugin
+ warning: Non-Unicode data filled only by request
+ if lParam=0 only internal SongInfo structure will be filled
+ Example:
+ LPSONGINFO p;
+ PluginLink->CallService(MS_WAT_GETMUSICINFO,0,(DWORD)&p);
+*/
+#define MS_WAT_GETMUSICINFO "WATrack/GetMusicInfo"
+
+/*
+ wParam:0
+ lParam : pointer to pSongInfo (Unicode)
+ Affects: Fill structure by info from file named in SongInfo.mfile
+ returns: 0, if success
+ note: fields, which values can't be obtained, leaves old values.
+ you must free given strings by miranda mmi.free
+*/
+#define MS_WAT_GETFILEINFO "WATrack/GetFileInfo"
+
+/*
+ wParam: encoding (WAT_INF_* consts, 0 = WAT_INF_UNICODE)
+ lParam: codepage (0 = ANSI)
+ Returns Global unicode SongInfo pointer or tranlated to Ansi/UTF8 structure
+*/
+#define MS_WAT_RETURNGLOBAL "WATrack/GetMainStructure"
+
+//!! DON'T CHANGE THESE VALUES!
+#define WAT_CTRL_FIRST 1
+
+#define WAT_CTRL_PREV 1
+#define WAT_CTRL_PLAY 2
+#define WAT_CTRL_PAUSE 3
+#define WAT_CTRL_STOP 4
+#define WAT_CTRL_NEXT 5
+#define WAT_CTRL_VOLDN 6
+#define WAT_CTRL_VOLUP 7
+#define WAT_CTRL_SEEK 8 // lParam is new position (sec)
+
+#define WAT_CTRL_LAST 8
+
+/*
+ wParam: button code (WAT_CTRL_* const)
+ lParam: 0, or value (see WAT_CTRL_* const comments)
+ Affects: emulate player button pressing
+ returns: 0 if unsuccesful
+*/
+#define MS_WAT_PRESSBUTTON "WATrack/PressButton"
+
+/*
+ Get user's Music Info
+*/
+#define MS_WAT_GETCONTACTINFO "WATrack/GetContactInfo"
+
+// ------------ Plugin/player status ------------
+
+/*
+ wParam: 1 - switch off plugin
+ 0 - switch on plugin
+ -1 - switch plugin status
+ 2 - get plugin version
+ other - get plugin status
+ lParam: 0
+ Affects: Switch plugin status to enabled or disabled
+ returns: old plugin status, 0, if was enabled
+*/
+#define MS_WAT_PLUGINSTATUS "WATrack/PluginStatus"
+
+// ---------- events ------------
+
+/*ME_WAT_MODULELOADED
+ wParam: 0, lParam: 0
+*/
+#define ME_WAT_MODULELOADED "WATrack/ModuleLoaded"
+
+#define WAT_EVENT_PLAYERSTATUS 1 //lParam: WAT_PLS_* const
+#define WAT_EVENT_NEWTRACK 2 //lParam: LPSONGINFO
+#define WAT_EVENT_PLUGINSTATUS 3 //lParam: 0-enabled; 1-dis.temporary; 2-dis.permanent
+#define WAT_EVENT_NEWPLAYER 4 //
+#define WAT_EVENT_NEWTEMPLATE 5 //lParam: TM_* constant
+
+/*ME_WAT_NEWSTATUS
+ Plugin or player status changed:
+ wParam: type of event (see above)
+ lParam: value
+*/
+#define ME_WAT_NEWSTATUS "WATrack/NewStatus"
+
+// ---------- Popup module ------------
+
+/*
+ wParam: not used
+ lParam: not used
+ Affects: Show popup or Info window with current music information
+ note: Only Info window will be showed if Popup plugin disabled
+*/
+#define MS_WAT_SHOWMUSICINFO "WATrack/ShowMusicInfo"
+
+// --------- Statistic (report) module -------------
+
+/*
+ wParam: pointer to log file name or NULL
+ lParam: pointer to report file name or NULL
+ Affects: Create report from log and run it (if option is set)
+ returns: 0 if unsuccesful
+ note: if wParam or lParam is a NULL then file names from options are used
+*/
+#define MS_WAT_MAKEREPORT "WATrack/MakeReport"
+
+/*
+ wParam, lParam - not used
+ Affects: pack statistic file
+*/
+#define MS_WAT_PACKLOG = "WATrack/PackLog"
+
+/*
+ wParam: not used
+ lParam: pointer to SongInfo
+*/
+#define MS_WAT_ADDTOLOG = "WATrack/AddToLog"
+
+// ----------- Formats and players -----------
+
+// media file status
+
+#define WAT_MES_STOPPED 0
+#define WAT_MES_PLAYING 1
+#define WAT_MES_PAUSED 2
+#define WAT_MES_UNKNOWN -1
+
+#define WAT_ACT_REGISTER 1
+#define WAT_ACT_UNREGISTER 2
+#define WAT_ACT_DISABLE 3
+#define WAT_ACT_ENABLE 4
+#define WAT_ACT_GETSTATUS 5 // not found/enabled/disabled
+#define WAT_ACT_SETACTIVE 6
+#define WAT_ACT_REPLACE 0x10000 // can be combined with WAT_REGISTERFORMAT
+
+ // flags
+#define WAT_OPT_DISABLED 0x00001 // format registered but disabled
+#define WAT_OPT_ONLYONE 0x00002 // format can't be overwriten
+#define WAT_OPT_PLAYERINFO 0x00004 // song info from player
+#define WAT_OPT_WINAMPAPI 0x00008 // Winamp API support
+#define WAT_OPT_CHECKTIME 0x00010 // check file time for changes
+#define WAT_OPT_VIDEO 0x00020 // only for format registering used
+#define WAT_OPT_LAST 0x00040 // (internal)
+#define WAT_OPT_FIRST 0x00080 // (internal)
+#define WAT_OPT_TEMPLATE 0x00100 // (internal)
+#define WAT_OPT_IMPLANTANT 0x00200 // use process implantation
+#define WAT_OPT_HASURL 0x00400 // (player registration) URL field present
+#define WAT_OPT_CHANGES 0x00800 // obtain only chaged values
+ // (volume, status, window text, elapsed time)
+#define WAT_OPT_APPCOMMAND 0x01000 // Special (multimedia) key support
+#define WAT_OPT_CHECKALL 0x02000 // Check all players
+#define WAT_OPT_KEEPOLD 0x04000 // Keep Old opened file
+#define WAT_OPT_MULTITHREAD 0x08000 // Use multithread scan
+#define WAT_OPT_SINGLEINST 0x10000 // Single player instance
+#define WAT_OPT_PLAYERDATA 0x20000 // (internal) to obtain player data
+
+
+typedef BOOL (__cdecl *LPREADFORMATPROC)(LPSONGINFO Info);
+
+typedef struct tMusicFormat {
+ LPREADFORMATPROC proc;
+ CHAR ext[8];
+ UINT flags;
+} MUSICFORMAT, *LPMUSICFORMAT;
+
+/*
+ wParam: action
+ lParam: pointer to MUSICFORMAT if wParam = WAT_ACT_REGISTER,
+ else - pointer to extension string (ANSI)
+ returns: see result codes
+*/
+#define MS_WAT_FORMAT "WATrack/Format"
+
+/*
+ wParam - pointer to SONGINFO structure (plwind field must be initialized)
+ lParam - flags
+ Affects: trying to fill SongInfo using Winamp API
+*/
+#define MS_WAT_WINAMPINFO "WATrack/WinampInfo"
+
+/*
+ wParam: window
+ lParam: LoWord - command; HiWord - value
+*/
+#define MS_WAT_WINAMPCOMMAND "WATrack/WinampCommand"
+
+typedef int (__cdecl *LPINITPROC) ();
+typedef int (__cdecl *LPDEINITPROC) ();
+typedef int (__cdecl *LPSTATUSPROC) (HWND wnd);
+typedef WCHAR (__cdecl *LPNAMEPROC) (HWND wnd, int flags);
+typedef HWND (__cdecl *LPCHECKPROC) (HWND wnd,int flags);
+typedef int (__cdecl *LPINFOPROC) (LPSONGINFO Info, int flags);
+typedef int (__cdecl *LPCOMMANDPROC)(HWND wnd, int command, int value);
+
+typedef struct tPlayerCell {
+ CHAR* Desc; // Short player name
+ UINT flags;
+ HICON Icon; // can be 0. for registration only
+ LPINITPROC Init; // LPINITPROC; can be NULL. initialize any data
+ LPDEINITPROC DeInit; // LPDEINITPROC; can be NULL. finalize player processing
+ LPCHECKPROC Check; // check player
+ LPSTATUSPROC GetStatus; // tStatusProc; can be NULL. get player status
+ LPNAMEPROC GetName; // can be NULL. get media filename
+ LPINFOPROC GetInfo; // can be NULL. get info from player
+ LPCOMMANDPROC Command; // can be NULL. send command to player
+ CHAR* URL; // only if WAT_OPT_HASURL flag present
+ WCHAR* Notes; // any tips, notes etc for this player
+} PLAYERCELL, *LPPLAYERCELL;
+
+/*
+ wParam: action
+ lParam: pointer to PLAYERCELL if wParam = WAT_ACT_REGISTER,
+ else - pointer to player description string (ANSI)
+ returns: player window handle or value>0 if found
+ note: If you use GetName or GetInfo field, please, do not return empty
+ filename even when mediafile is remote!
+*/
+#define MS_WAT_PLAYER "WATrack/Player"
+
+// --------- Last FM ---------
+
+/*
+ Toggle LastFM scrobbling status
+ wParam,lParam=0
+ Returns: previous state
+*/
+#define MS_WAT_LASTFM "WATrack/LastFM"
+
+/*
+ Get Info based on currently played song
+ wParam: pLastFMInfo
+ lParam: int language (first 2 bytes - 2-letters language code)
+*/
+typedef struct tLastFMInfo {
+ UINT request; // 0 - artist, 1 - album, 2 - track
+ WCHAR* artist; // artist
+ WCHAR* album; // album or similar artists for Artist info request
+ WCHAR* title; // track title
+ WCHAR* tags; // tags
+ WCHAR* info; // artist bio or wiki article
+ WCHAR* image; // photo/cover link
+ WCHAR* similar;
+ WCHAR* release;
+ UINT trknum;
+}PLASTFMINFO, *LPLASTFMINFO;
+
+#define MS_WAT_LASTFMINFO "WATrack/LastFMInfo"
+
+// --------- Templates ----------
+
+/*
+ wParam: 0 (standard Info) or pSongInfo
+ lParam: Unicode template
+ returns: New Unicode (replaced) string
+*/
+#define MS_WAT_REPLACETEXT "WATrack/ReplaceText"
+
+/*
+ event types for History
+ Blob structure for EVENTTYPE_WAT_ANSWER:
+ Uniciode artist#0title#0album#0answer
+*/
+#define EVENTTYPE_WAT_REQUEST 9601
+#define EVENTTYPE_WAT_ANSWER 9602
+#define EVENTTYPE_WAT_ERROR 9603
+#define EVENTTYPE_WAT_MESSAGE 9604
+
+/*
+ wParam: 0 or parent window
+ lParam: 0
+ note: Shows Macro help window with edit aliases ability
+*/
+#define MS_WAT_MACROHELP "WATrack/MacroHelp"
+
+#endif
diff --git a/plugins/Watrack/docs/m_music.inc b/plugins/Watrack/docs/m_music.inc
new file mode 100644
index 0000000000..1850de52ba
--- /dev/null
+++ b/plugins/Watrack/docs/m_music.inc
@@ -0,0 +1,404 @@
+{$IFNDEF M_MUSIC}
+{$DEFINE M_MUSIC}
+
+// defined in interfaces.inc
+//const MIID_WATRACK:MUUID='{FC6C81F4-837E-4430-9601-A0AA43177AE3}';
+
+type
+ pSongInfoA = ^tSongInfoA;
+ tSongInfoA = packed record
+ artist :PAnsiChar;
+ title :PAnsiChar;
+ album :PAnsiChar;
+ genre :PAnsiChar;
+ comment :PAnsiChar;
+ year :PAnsiChar;
+ mfile :PAnsiChar; // media file
+ kbps :dword;
+ khz :dword;
+ channels :dword;
+ track :dword;
+ total :dword; // music length
+ time :dword; // elapsed time
+ wndtext :PAnsiChar; // window title
+ player :PAnsiChar; // player name
+ plyver :dword; // player version
+ icon :THANDLE; // player icon
+ fsize :dword; // media file size
+ vbr :dword;
+ status :integer; // WAT_MES_* const
+ plwnd :HWND; // player window
+ // video part
+ codec :dword;
+ width :dword;
+ height :dword;
+ fps :dword;
+ date :int64;
+ txtver :PAnsiChar;
+ lyric :PAnsiChar;
+ cover :PAnsiChar;
+ volume :dword;
+ url :PAnsiChar; // player homepage
+ winampwnd:HWND;
+ end;
+type
+ pSongInfo=^tSongInfo;
+ tSongInfo = packed record
+ artist :pWideChar;
+ title :pWideChar;
+ album :pWideChar;
+ genre :pWideChar;
+ comment :pWideChar;
+ year :pWideChar;
+ mfile :pWideChar; // media file
+ kbps :dword;
+ khz :dword;
+ channels :dword;
+ track :dword;
+ total :dword; // music length
+ time :dword; // elapsed time
+ wndtext :pWideChar; // window title
+ player :pWideChar; // player name
+ plyver :dword; // player version
+ icon :THANDLE; // player icon
+ fsize :dword; // media file size
+ vbr :dword;
+ status :integer; // WAT_MES_* const
+ plwnd :HWND; // player window
+ // video part
+ codec :dword;
+ width :dword;
+ height :dword;
+ fps :dword;
+ date :int64;
+ txtver :pWideChar;
+ lyric :pWideChar;
+ cover :pWideChar; // cover path
+ volume :dword;
+ url :PWideChar; // player homepage
+ winampwnd:HWND;
+ end;
+ pSongInfoW = pSongInfo;
+ tSongInfoW = tSongInfo;
+
+const
+ // result codes
+ WAT_RES_UNKNOWN = -2;
+ WAT_RES_NOTFOUND = -1;
+ WAT_RES_ERROR = WAT_RES_NOTFOUND;
+ WAT_RES_OK = 0;
+ WAT_RES_ENABLED = WAT_RES_OK;
+ WAT_RES_DISABLED = 1;
+ // internal
+ WAT_RES_NEWFILE = 3;
+ WAT_RES_NEWPLAYER = 4;
+
+// result for MS_WAT_GETMUSICINFO service
+const
+ WAT_PLS_NORMAL = WAT_RES_OK;
+ WAT_PLS_NOMUSIC = WAT_RES_DISABLED;
+ WAT_PLS_NOTFOUND = WAT_RES_NOTFOUND;
+
+const
+ WAT_INF_UNICODE = 0;
+ WAT_INF_ANSI = 1;
+ WAT_INF_UTF8 = 2;
+ WAT_INF_CHANGES = $100;
+
+const
+ MS_WAT_INSERT:PAnsiChar = 'WATrack/Insert';
+ MS_WAT_EXPORT:PAnsiChar = 'WATrack/Export';
+
+const
+{
+ wParam : WAT_INF_* constant
+ lParam : pointer to pSongInfo (Unicode) or pSongInfoA (ANSI/UTF8)
+ Affects: Fill structure by currently played music info
+ returns: WAT_PLS_* constant
+ note: pointer will be point to global SongInfo structure of plugin
+ warning: Non-Unicode data filled only by request
+ if lParam=0 only internal SongInfo structure will be filled
+ Example:
+ var p:pSongInfo;
+ PluginLink^.CallService(MS_WAT_GETMUSICINFO,0,dword(@p));
+}
+ MS_WAT_GETMUSICINFO:PAnsiChar = 'WATrack/GetMusicInfo';
+{
+ wParam:0
+ lParam : pointer to pSongInfo (Unicode)
+ Affects: Fill structure by info from file named in SongInfo.mfile
+ returns: 0, if success
+ note: fields, which values can't be obtained, leaves old values.
+ you must free given strings by miranda mmi.free
+}
+ MS_WAT_GETFILEINFO:PAnsiChar = 'WATrack/GetFileInfo';
+
+{
+ wParam: encoding (WAT_INF_* consts, 0 = WAT_INF_UNICODE)
+ lParam: codepage (0 = ANSI)
+ Returns Global unicode SongInfo pointer or tranlated to Ansi/UTF8 structure
+}
+ MS_WAT_RETURNGLOBAL:PAnsiChar = 'WATrack/GetMainStructure';
+
+//!! DON'T CHANGE THESE VALUES!
+const
+ WAT_CTRL_FIRST = 1;
+
+ WAT_CTRL_PREV = 1;
+ WAT_CTRL_PLAY = 2;
+ WAT_CTRL_PAUSE = 3;
+ WAT_CTRL_STOP = 4;
+ WAT_CTRL_NEXT = 5;
+ WAT_CTRL_VOLDN = 6;
+ WAT_CTRL_VOLUP = 7;
+ WAT_CTRL_SEEK = 8; // lParam is new position (sec)
+
+ WAT_CTRL_LAST = 8;
+
+{
+ wParam: button code (WAT_CTRL_* const)
+ lParam: 0, or value (see WAT_CTRL_* const comments)
+ Affects: emulate player button pressing
+ returns: 0 if unsuccesful
+}
+ MS_WAT_PRESSBUTTON:PAnsiChar = 'WATrack/PressButton';
+
+{
+ Get user's Music Info
+}
+ MS_WAT_GETCONTACTINFO:PAnsiChar = 'WATrack/GetContactInfo';
+
+// ------------ Plugin/player status ------------
+
+{
+ wParam: 1 - switch off plugin
+ 0 - switch on plugin
+ -1 - switch plugin status
+ 2 - get plugin version
+ other - get plugin status
+ lParam: 0
+ Affects: Switch plugin status to enabled or disabled
+ returns: version, old plugin status, 0, if was enabled
+}
+ MS_WAT_PLUGINSTATUS:PAnsiChar = 'WATrack/PluginStatus';
+
+ ME_WAT_MODULELOADED:PAnsiChar = 'WATrack/ModuleLoaded';
+
+const
+ WAT_EVENT_PLAYERSTATUS = 1; // WAT_PLS_* const
+ WAT_EVENT_NEWTRACK = 2; // SongInfo ptr
+ WAT_EVENT_PLUGINSTATUS = 3; // 0-enabled; 1-dis.temporary; 2-dis.permanent
+ WAT_EVENT_NEWPLAYER = 4; //
+ WAT_EVENT_NEWTEMPLATE = 5; // TM_* constant
+
+{
+ Plugin or player status changed:
+ wParam: type of event (see above)
+ lParam: value
+}
+ ME_WAT_NEWSTATUS:PAnsiChar = 'WATrack/NewStatus';
+
+// ---------- Popup module ------------
+
+{
+ wParam: not used
+ lParam: not used
+ Affects: Show popup or Info window with current music information
+ note: Only Info window will be showed if Popup plugin disabled
+}
+ MS_WAT_SHOWMUSICINFO:PAnsiChar = 'WATrack/ShowMusicInfo';
+
+// --------- Statistic (report) module -------------
+
+{
+ wParam: pointer to log file name or NIL
+ lParam: pointer to report file name or NIL
+ Affects: Create report from log and run it (if option is set)
+ returns: 0 if unsuccesful
+ note: if wParam or lParam is a NIL then file names from options are used
+}
+ MS_WAT_MAKEREPORT :PAnsiChar = 'WATrack/MakeReport';
+// MS_WAT_MAKEREPORTW:PAnsiChar = 'WATrack/MakeReportW';
+
+{
+ wParam, lParam - not used
+ Affects: pack statistic file
+}
+ MS_WAT_PACKLOG:PAnsiChar = 'WATrack/PackLog';
+
+{
+ wParam: not used
+ lParam: pointer to SongInfo
+}
+ MS_WAT_ADDTOLOG:PAnsiChar = 'WATrack/AddToLog';
+
+// ----------- Formats and players -----------
+
+// media file status
+
+const
+ WAT_MES_STOPPED = 0;
+ WAT_MES_PLAYING = 1;
+ WAT_MES_PAUSED = 2;
+ WAT_MES_UNKNOWN = -1;
+
+const
+ WAT_ACT_REGISTER = 1;
+ WAT_ACT_UNREGISTER = 2;
+ WAT_ACT_DISABLE = 3;
+ WAT_ACT_ENABLE = 4;
+ WAT_ACT_GETSTATUS = 5; // not found/enabled/disabled
+ WAT_ACT_SETACTIVE = 6;
+ WAT_ACT_REPLACE = $10000; // can be combined with WAT_REGISTERFORMAT
+
+const
+ // flags
+ WAT_OPT_DISABLED = $00000001; // registered but disabled
+ WAT_OPT_ONLYONE = $00000002; // can't be overwriten
+ WAT_OPT_PLAYERINFO = $00000004; // song info from player
+ WAT_OPT_WINAMPAPI = $00000008; // Winamp API support
+ WAT_OPT_CHECKTIME = $00000010; // check file time for changes
+ WAT_OPT_VIDEO = $00000020; // only for format registering used
+ WAT_OPT_LAST = $00000040; // (internal-Winamp Clone) put to the end of queue
+ WAT_OPT_FIRST = $00000080; // (internal)
+ WAT_OPT_TEMPLATE = $00000100; // (internal)
+ WAT_OPT_IMPLANTANT = $00000200; // use process implantation
+ WAT_OPT_HASURL = $00000400; // (player registration) URL field present
+ WAT_OPT_CHANGES = $00000800; // obtain only chaged values
+ // (volume, status, window text, elapsed time)
+ WAT_OPT_APPCOMMAND = $00001000; // Special (multimedia) key support
+ WAT_OPT_CHECKALL = $00002000; // Check all players
+ WAT_OPT_KEEPOLD = $00004000; // Keep Old opened file
+ WAT_OPT_MULTITHREAD = $00008000; // Use multithread scan
+ WAT_OPT_SINGLEINST = $00010000; // Single player instance
+ WAT_OPT_PLAYERDATA = $00020000; // (internal) to obtain player data
+
+type
+ tReadFormatProc = function(var Info:tSongInfo):boolean; cdecl;
+ pMusicFormat = ^tMusicFormat;
+ tMusicFormat = packed record
+ proc :tReadFormatProc;
+ ext :array [0..7] of AnsiChar;
+ flags:cardinal;
+ end;
+
+const
+{
+ wParam: action
+ lParam: pointer to tMusicFormat if wParam = WAT_ACT_REGISTER,
+ else - pointer to extension string (ANSI)
+ returns: see result codes
+}
+ MS_WAT_FORMAT:PAnsiChar = 'WATrack/Format';
+
+{
+ wParam: pointer to SongInfo structure (plwind field must be initialized)
+ lParam: flags
+ Affects: trying to fill SongInfo using Winamp API
+}
+ MS_WAT_WINAMPINFO:PAnsiChar = 'WATrack/WinampInfo';
+
+{
+ wParam: window
+ lParam: LoWord - command; HiWord - value
+}
+ MS_WAT_WINAMPCOMMAND:PAnsiChar = 'WATrack/WinampCommand';
+
+type
+ tInitProc = function():integer;cdecl;
+ tDeInitProc = function():integer;cdecl;
+ tStatusProc = function(wnd:HWND):integer;cdecl;
+ tNameProc = function(wnd:HWND;flags:integer):pWideChar;cdecl;
+ tCheckProc = function(wnd:HWND;flags:integer):HWND;cdecl;
+ tInfoProc = function(var SongInfo:tSongInfo;flags:integer):integer;cdecl;
+ tCommandProc = function(wnd:HWND;command:integer;value:integer):integer;cdecl;
+
+ pPlayerCell = ^tPlayerCell;
+ tPlayerCell = packed record
+ Desc :PAnsiChar; // Short player name
+ flags :cardinal;
+ Icon :HICON; // can be 0. for registration only
+ Init :pointer; // tInitProc; can be NIL. initialize any data
+ DeInit :pointer; // tDeInitProc; can be NIL. finalize player processing
+ Check :pointer; // tCheckProc; check player
+ GetStatus:pointer; // tStatusProc; can be NIL. get player status
+ GetName :pointer; // tNameProc; can be NIL. get media filename
+ GetInfo :pointer; // tInfoProc; can be NIL. get info from player
+ Command :pointer; // tCommandProc; can be NIL. send command to player
+ URL :PAnsiChar; // only if WAT_OPT_HASURL flag present
+ Notes :PWideChar; // any tips, notes etc for this player
+ end;
+
+const
+{
+ wParam: action
+ lParam: pointer to tPlayerCell if wParam = WAT_ACT_REGISTER,
+ else - pointer to player description string (ANSI)
+ returns: player window handle or value>0 if found
+ note: If you use GetName or GetInfo field, please, do not return empty
+ filename even when mediafile is remote!
+}
+ MS_WAT_PLAYER:PAnsiChar = 'WATrack/Player';
+
+// --------- Last FM ---------
+
+{
+ Toggle LastFM scrobbling status
+ wParam,lParam=0
+ Returns: previous state
+}
+const
+ MS_WAT_LASTFM:pAnsiChar = 'WATrack/LastFM';
+
+{
+ Get Info based on currently played song
+ wParam: pLastFMInfo
+ lParam: int language (first 2 bytes - 2-letters language code)
+}
+type
+ pLastFMInfo = ^tLastFMInfo;
+ tLastFMInfo = packed record
+ request:cardinal; // 0 - artist, 1 - album, 2 - track
+ artist :pWideChar; // artist
+ album :pWideChar; // album or similar artists for Artist info request
+ title :pWideChar; // track title
+ tags :pWideChar; // tags
+ info :pWideChar; // artist bio or wiki article
+ image :pAnsiChar; // photo/cover link
+ similar:pWideChar;
+ release:pWideChar;
+ trknum :cardinal;
+ end;
+const
+ MS_WAT_LASTFMINFO:pAnsiChar = 'WATrack/LastFMInfo';
+
+// --------- Templates ----------
+
+const
+{
+ wParam: 0 (standard Info) or pSongInfo
+ lParam: Unicode template
+ returns: New Unicode (replaced) string
+}
+ MS_WAT_REPLACETEXT:PAnsiChar = 'WATrack/ReplaceText';
+
+{
+ event types for History
+ Blob structure for EVENTTYPE_WAT_ANSWER:
+ Uniciode artist#0title#0album#0answer
+}
+const
+ EVENTTYPE_WAT_REQUEST = 9601;
+ EVENTTYPE_WAT_ANSWER = 9602;
+ EVENTTYPE_WAT_ERROR = 9603;
+ EVENTTYPE_WAT_MESSAGE = 9604;
+
+const
+{
+ wParam: 0 or parent window
+ lParam: 0
+ note: Shows Macro help window with edit aliases ability
+}
+ MS_WAT_MACROHELP:pAnsiChar = 'WATrack/MacroHelp';
+
+{$ENDIF M_MUSIC}
diff --git a/plugins/Watrack/docs/sampledll.dpr b/plugins/Watrack/docs/sampledll.dpr
new file mode 100644
index 0000000000..908fe60597
--- /dev/null
+++ b/plugins/Watrack/docs/sampledll.dpr
@@ -0,0 +1,139 @@
+library testdll;
+
+uses m_api, Windows,common;
+
+{$include m_helpers.inc}
+{$include m_music.inc}
+
+const
+ PluginInfo:TPLUGININFOEX=(
+ cbSize :sizeof(TPLUGININFOEX);
+ shortName :'Plugin Template';
+ version :$00000001;
+ description:'The long description of your plugin, to go in the plugin options dialog';
+ author :'J. Random Hacker';
+ authorEmail:'noreply@sourceforge.net';
+ copyright :'(c) 2003 J. Random Hacker';
+ homepage :'http://miranda-icq.sourceforge.net/';
+ flags :UNICODE_AWARE;
+ replacesDefaultModule:0;
+ uuid:'{00000000-0000-0000-0000-000000000000}'
+ );
+var
+ PluginInterfaces:array [0..1] of MUUID;
+
+var
+ hook:integer;
+ oldproc:tReadFormatProc;
+
+// -------- format --------
+Function mp3proc(var dst:tSongInfo):boolean;cdecl;
+begin
+{
+ MP3 Handler here
+}
+//messagebox(0,'ok','',0);
+// Example for old handler
+ if (int(@oldproc)<>WAT_RES_OK) and (int(@oldproc)<>WAT_RES_ERROR) then
+ result:=oldproc(dst)
+ else
+ result:=true;
+end;
+
+// ---------- Player ----------
+var
+ plwnd:HWND;
+
+function Check(flags:integer):HWND;cdecl;
+begin
+ result:=1;
+ plwnd:=12;
+end;
+
+function GetFileName:pWideChar;cdecl;
+begin
+ result:=nil;
+end;
+
+function GetInfo(var SongInfo:tSongInfo;flags:integer):integer;cdecl;
+begin
+ PluginLink^.CallService(MS_WAT_WINAMPINFO,integer(@SongInfo),flags);
+ SongInfo.plyver:=$1234;
+ result:=0;
+end;
+
+function Command(command:integer;value:integer):integer;cdecl;
+begin
+ result:=PluginLink^.CallService(MS_WAT_WINAMPCOMMAND,plwnd,
+ command+(value shl 16));
+end;
+
+function OnWATLoaded(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ tmp:tMusicFormat;
+ tmp1:tPlayerCell;
+begin
+ PluginLink^.UnhookEvent(hook);
+
+ FillChar(tmp,SizeOf(tMusicFormat),0);
+ lstrcpy(tmp.ext,'MP3');
+ tmp.proc:=mp3proc;
+ oldproc:=tReadFormatProc(PluginLink^.CallService(MS_WAT_FORMAT,
+ WAT_ACT_REGISTER+WAT_ACT_REPLACE,dword(@tmp)));
+
+ FillChar(tmp1,SizeOf(tPlayerCell),0);
+ tmp1.desc :='Sampler';
+ tmp1.flags :=0;// WAT_OPT_WINAMPAPI
+ tmp1.Check :=@Check;
+ tmp1.GetInfo:=@GetInfo;
+ tmp1.Command:=@Command;
+ tmp1.GetName:=@GetFileName;
+ PluginLink^.CallService(MS_WAT_PLAYER,WAT_ACT_REGISTER+WAT_ACT_REPLACE,dword(@tmp1));
+ result:=0;
+end;
+
+function OnModuleLoaded(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+begin
+ hook:=HookEvent(ME_WAT_MODULELOADED,@OnWATLoaded);
+ result:=0;
+end;
+
+function MirandaPluginInfo(mirandaVersion:DWORD):PPLUGININFO; cdecl;
+begin
+ result:=@PluginInfo;
+ PluginInfo.cbSize:=SizeOf(TPLUGININFO);
+end;
+
+function MirandaPluginInfoEx(mirandaVersion:DWORD):PPLUGININFOEX; cdecl;
+begin
+ result:=@PluginInfo;
+ PluginInfo.cbSize:=SizeOf(TPLUGININFOEX);
+end;
+
+function Load(link: PPLUGINLINK): int; cdecl;
+begin
+ PLUGINLINK := Pointer(link);
+ InitMMI;
+ Result:=0;
+ hook:=HookEvent(ME_SYSTEM_MODULESLOADED,@OnModuleLoaded);
+end;
+
+function Unload: int; cdecl;
+begin
+ Result:=0;
+end;
+
+function MirandaPluginInterfaces:PMUUID; cdecl;
+begin
+ PluginInterfaces[0]:=PluginInfo.uuid;
+ PluginInterfaces[1]:=MIID_LAST;
+ result:=@PluginInterfaces;
+end;
+
+exports
+ Load, Unload,
+ MirandaPluginInfo
+ ,MirandaPluginInterfaces,MirandaPluginInfoEx;
+
+begin
+end.
diff --git a/plugins/Watrack/docs/wat.php b/plugins/Watrack/docs/wat.php
new file mode 100644
index 0000000000..d15049dd72
--- /dev/null
+++ b/plugins/Watrack/docs/wat.php
@@ -0,0 +1,28 @@
+<?php
+
+include_once 'const.php';
+
+function mbot_load()
+{
+ mb_SchReg("ex/1min","* * *",'every_1min',1,1);
+}
+
+function every_1min()
+{
+ $songinfo = 0;
+ if(mb_SysCallService(MS_WAT_GETMUSICINFO, WAT_INF_ANSI,
+ mb_SysGetPointer($songinfo))!=WAT_PLS_NORMAL)
+ return 0;
+
+ $artist_pos = mb_SysGetNumber($songinfo+wato_artist, 4);
+ $artist = mb_SysGetString($artist_pos);
+
+ $title_pos = mb_SysGetNumber($songinfo+wato_title, 4);
+ $title = mb_SysGetString($title_pos);
+
+ $newtext = "mp3: $artist - $title";
+
+ mb_MsgBox($newtext, "MBot", 4);
+ return 0;
+}
+?> \ No newline at end of file
diff --git a/plugins/Watrack/docs/watrack_history.txt b/plugins/Watrack/docs/watrack_history.txt
new file mode 100644
index 0000000000..bea80d4e85
--- /dev/null
+++ b/plugins/Watrack/docs/watrack_history.txt
@@ -0,0 +1,519 @@
+0.0.6.12 (17 nov 2009)
+ Added Last.FM scrobbling support
+0.0.6.11 (14 sep 2009)
+ Added watrack parts admin page
+ Fixed cover reading from OGG files
+ Fixed mRadio work when no avatar plugin presents
+ Fixed wrong LastFM recognition
+0.0.6.10 (28 aug 2008)
+ Added basic LastFM support
+ Added hidden option 'xstatpause',ms (word type) to make pause between XStatus changing
+ Added option to emulate multimedia keys
+ Added avatar as cover for mRadio contacts support
+0.0.6.9 (28 jan 2008)
+ Fixes for FLV format
+ Added option to assign 'music' and 'video' xstatuses
+ Changed Handle checking (to avoid Thread handle leak)
+ First tab setting notes shows on mouse hover (not click)
+0.0.6.8 (29 oct 2007)
+ Added two options to alter filename search method
+ Fixed: mRadio track changing not catched
+0.0.6.7 (26 aug 2007)
+ Added lyric support for APev2 tags, WMA
+ Added cover support for APev2 tags, WMA, OGG, SPX and fLaC
+ Optimized APEv2, OGG, fLaC and WMA reading code
+ Fixed ID3v2.4 tag reading code
+ Optimized mediafile handle checking
+0.0.6.6 (9 aug 2007)
+ AIMP player support code changed
+ Fixed some memoryleaks
+0.0.6.5 (20 jun 2007)
+ 'Use existing XStatus' options changes only message text, not title
+ Optimized FLV reading code
+ Fixed and optimized MPG reading code
+0.0.6.4 (13 jun 2007)
+ Added AIMP control
+0.0.6.3 (10 jun 2007)
+ Added: mRadio %version% macro support
+ Fixed: in rare case covers can be renamed and deleted
+ Fixed: %cover% value can be wrong if cover not present
+ Fixed: %volume% value for mRadio not obtaining
+0.0.6.2 (9 jun 2007)
+ Small players code fixes
+ Commented some thread code (due to AIMP 1.77 changes)
+ Fixed version number
+ Added seek ability for local mediafiles
+0.0.6.1 (7 jun 2007)
+ Added extended control for mRadio Mod
+ Added compatibility with AIMP 1.77
+ Added service to obtain not only played file info
+ Fixed: frame not change cover picture obtained from mediafile tags
+(01 jun 2007)
+ Added %nstatus% macro (like %status% but w/o translation)
+(29 may 2007)
+ Very small fixes (for mRadio too)
+(25 may 2007)
+ Added ability to show popups only by request, not new track
+(20 may 2007)
+ Changed: added threads for popups and statistic
+ Changed: Music/player Info obtaining code
+ Fixed: QCD player recognizing and processing
+(16 may 2007)
+ Added actions support for Popup Plus 2.1.0.5
+(15 may 2007)
+ Added %playerhome% (Player homepage url) macro
+(11 may 2007)
+ Small fixes
+ Statistic code optimized for speed
+(09 may 2007)
+ Added two buttons to check player/format list
+ AIMP player now processed separately
+ Changed code for played filename obtaining
+0.0.6.0 (05 may 2007)
+ Many small codechanges and fixes
+ Music info request can be send by ASKWAT text sending (received as message)
+ Settings saved only in profile db now
+0.0.5.0 (18 feb 2007)
+ Fixed: cover is showed only when backround picture name is not empty
+ Cover files deleted at exit now
+ Added volume field (see readme) to SongInfo structure.
+ Fixed: Cover from ID3v2 tag saved incorrectly
+ Fixed: Miranda crashed when shutdowned
+ Code cleaning and optimization
+0.0.4.30 (24 dec.2006)
+ Changed internal code library
+ Fixed comment reading from ID3v2 tag
+0.0.4.29 (29 nov 2006)
+ Small Fixes
+ Changes for external icon support for player.ini file
+ Fixed: ini file modified constantly if player not found
+0.0.4.28 (16 nov 2006)
+ Other bugfixes
+ Fixed: Contact menu handler chain was broken
+0.0.4.27 (15 nov 2006)
+ Small player handles fixes
+ Fixed: crash with WAV file samplesize<8 bit
+ Added options to choose Music info sharing ability
+ Added context menu music info request
+ Fixed bug with Frame redraw/resizing
+ Added external player definition
+0.0.4.26 (7 nov 2006)
+ Added album sort mode to report
+ Fixed some report errors
+ Changes for old miranda versions (before 0.6) compatibility
+0.0.4.25 (31 oct 2006)
+ INI file (not database) used by default
+ Winamp video width and height recognizing returned (for test)
+ Fixed: log file rewrited always
+ Fixed: Report creating from Options tab was wrong
+ Fixed: "Export default" button on options tab not work
+0.0.4.24 (29 oct 2006)
+ Report code was rewritten to template support
+ Current settings saved when saving place changed (switch at last!)
+ Loading settings from profile database works now normally
+ Fixed: Player name case conversion was after WAT_EVENT_NEWTRACK sending
+ Width and Height recognition through Winamp API disables due to wrong return values
+0.0.4.23 (15 oct 2006)
+ Save in INI/profile base file option returned
+ API changes
+ ID3v2 tag reading code was changed
+ Rewritten and reorganized internal structure
+ mBot script sample added
+0.0.4.22 (17 sep 2006)
+ INI-file structure changes (frame, report and popup options is separated)
+ CyberLink PowerDVD recognizing added
+ ALSong player recognizing added
+ MediaInfo obtained at plugin start now (if refresh time is not 0)
+ First version of new Player API is finished
+ Underscore replaces by spaces only in text macros (not media filename)
+ Fixed: wrong action on "Use Frame" checkbox
+0.0.4.21 (28 aug 2006) (test)
+ Miranda memory manager used now
+ Fixed: ID3v2 tags cannot process Unicode strings with reverse byte order
+ Media format API rewrited, new service added
+ Added event notifier for plugin loading
+ Added event notifier for plugin/player status changing
+0.0.4.20 (20 aug 2006)
+ Only songs with known length are addings to log
+ Added frame text centering
+ Small bugfixes
+ Fixed: Mirada crashes while report creating changed from menu
+ Added mRadio plugin support (test)
+ Fixed: sometimes trackbar is hidden
+ Added SongBird player support
+0.0.4.19 (11 aug 2006)
+ XStatus 'Watching pro7 on TV' works like 'Listening to music' XStatus but for video
+ Show/hide trakbar option is separated now
+ Sources changed for delphi7_up compatibility
+ Fixed: Sometimes plugin show hotkey registration error
+ Fixed: Timer stopped when Options page is open
+ Added some additional settings for frame text scrolling
+ Fixed: iTunes volume control does not work
+ Added menu item and toolbar button to disable plugin
+ Fixed error with unknown format tag in the frame template
+ Added WiFiRadio player support
+ Added option to hide frame when player not found
+0.0.4.18 (14 jul 2006)
+ Added GOM player detection
+ {cf#nnnnnn} and {bg#nnnnnn} is define color with hex trucolor value 'nnnnnn'
+ Added frame text format support
+ Added pushed/hovered button status support if watrack_buttons.dll is used
+0.0.4.17 (10 jul 2006)
+ Fixed: frame button not unpress when pressed button mouse moves out of frame
+ Added option to use or not gap between frame buttons
+ Option tabs changed back to theme ability
+ Added "skin" trackbar ability
+0.0.4.16 (9 jul 2006)
+ Small interface changes
+ Volume buttons on the frame changed and can be hided now
+ '\t' combination (Tabulation) changed to '{tab}' tag format
+ Fixed avoid '\n' combination conflict in templates
+ Holding mouse button on volume control make continuous decremet/increment
+ Fixed some bugs
+ Added frame transparency
+0.0.4.15 (5 jul 2006)
+ Added right align frame picture option
+ Fixed: text insertion hotkey do not always register
+ Service for player control is created
+ Added "Next track" to popup click options
+ If timer value greater than 499, time signify as milliseconds
+0.0.4.14 (3 jul 2006)
+ Added bottom align frame picture option
+ Fixed: Miranda freezes on W2K when some radiobuttons clicked
+ Returned modified old code for Foobar recognize
+0.0.4.13 (29 jun 2006)
+ Updater data changed for new hosting compatibility
+ Added support \t in templates (expanded when used)
+ Frame text now can be edited
+ Frame background can be picture (with effects: center, tile, stretch)
+ Options saves in profile directory as default (if old settings not found)
+ Foobar2000 recognizing code changed
+ Jabber chat recognized now
+ Fixed: 'Try to use OLE interface' option not changed
+ Added frame bitmap background support
+ Fixed: control icons in IcoLib not shows then watrack_icons.dll is not found
+ Volume control added
+ New Frame options added
+0.0.4.12 (18 jun 2006)
+ Fixed (i hope) bug with info refresh while MediaMonkey finished
+ Added frame support with IcoLib support
+ Added control procedures for some players
+ Small speed optimization
+ Introduced option to enable/disable players OLE interface
+0.0.4.11 (14 jun 2006)
+ Fixed FLV bug
+ Added MPEG file support (only MPEG audio stream)
+0.0.4.10 test (11 jun 2006)
+ New macro %txtver% (text version number representation) was introduced
+ Small fixes
+ Imroved Foobar2000 support if foo_comserver2 plugin is present
+0.0.4.9 (06 jun 2006)
+ Added partial support for MOV and 3GP file formats
+ Added support for ID3v2 tag v.2
+ Added option to skip some plugin error messages (mainly for hotkeys)
+ INI file is not rewritten now if default player was not changed
+ Added QuickTime player support
+0.0.4.8 (31 may 2006)
+ Added New options to insert text in text field of other apps
+ Code compiled with KOL (smaller size)
+0.0.4.7
+ Version numeration changed for more Updater compatibility. Last number is for betas.
+ Trying to use XP theming in TABs
+ Changed XStatus recovery code
+0.0.4.6 (23 may 2006)
+ Variables and NewAwaySys works now with Unicode
+ Small interface changes
+ Added FLVPlayer support
+ Added option to check file date/time changes while playing
+ Added option to set XStatus regardless of ICQ status
+ Added option to replace underline with spaces
+ Fixed NAS service call
+ Added partial FLV format support
+ Fixed: some unicode named files can't be processed
+ Macro help removed from resource
+0.0.4.5 (11 may 2006)
+ Changed Macro help window - with localisation support now
+ Added NewAwaySys (unicode) support for status text changings
+ Added MPlayer support
+ Fixed MusikCube player code
+ Fixed template formatting in IRC channel
+ Added partial MKV, WMV and ASF formats support
+ Changed RM reading code to video info compatibility
+ Added AVI file support (test version)
+ Added OGM file support
+ %fps%, %width%, %height% and %codec% macro added
+ OGG file info reading changes
+ Fixes for large files
+ Finally fixed bug with Popup templates
+ Options dialog changed again (Templates and Protocols tabs is merged)
+0.0.4.4 (24 apr 2006)
+ Fixed: Template macro help not work
+ Changed WavPack reading code for v.4* compatibility
+ Fixed bug with INI file name processing
+ Status message now uses 'Status Text' template (was 'Status Title')
+ MediaMonkey player class now partially uses OLE interface
+ Music XStatus can be cleared when miranda starts without music
+ Watrack.ini used if private INI file is absent
+ Notes in first option tab is Unicode now
+ Changed work with statuses (normal and extended)
+ Changed plugin behaviour when Miranda started without played song
+ Default template texts changed
+ Fixed: unused strings was keeped in INI file.
+ Fixed: can crash when Variables plugin not exists
+ Fixed: protocols enabling-disabling not works properly
+ Fixed: Crashes when music played and templates changed
+ MediaMonkey recognized as separate player now, not Winamp clone
+ All settings now saved in INI-file
+ Template option dialog was totally rewrited
+0.0.4.3 (26 mar 2006)
+ Added option to return Variables value in ANSI
+ Small save-related changes
+ Added 'AudioPlayer' support
+ Player icons moved to separate file (must be in icons or plugins directory)
+ Added player window handle to SongInfo structure
+ Added popup option to activate player window
+ Fixed: wrong 'Total logged music time' value
+ Added Core Media Player support
+0.0.4.2 (03 mar 2006)
+ Fixed RM file reading
+ Fixed ID3v2 tag reading (UTF8 encoding)
+ Small option dialog interface changes
+ Localisation sample file included (russian)
+ psapi.dll included (needed when plugin can't get song filename)
+ Fixed WMA tag reading
+ Fixed LA player recognition (wrong player status was returned)
+ Old XStatus restored now if player not found
+ Changed: if XStatus title is empty, it is cleared now
+ Changed %music% command translation code
+0.0.4.1 (26 feb 2006)
+ Deleted ICQ XStatus old API code
+ Added Creative Media Source support
+ Added 'version' resource
+ Status and popup templates are separated now
+ Added Media Commander Express player support
+ Added IcoLib support
+ Added VLC player support
+ Added Helium Music Manager support
+ Added Musicmatch Jukebox player support
+ Added %music% command with BB-code formatting in message dialog
+ Added option for player title formatting
+ Fixed error with filename in File info called from popup
+ Added support for J.River Media Center
+ Fixed situation when player is stopped
+ Fixed: 511 byte per string only saved in INI-file
+ Fixed player status for unknown filetypes
+ Small fix for MP4/M4A reading
+ Added partial M4A file format tag support
+ Added option to get unknown music format info from player
+ Changed WMA tag reading code to obtain number-type values
+ All resource language is Neutral now
+ Changed MusicCubeOne and SAPS players recognition
+ When 'no-music text' is empty and player stopped status message cleared
+ When 'Keep Status' is 'on' status text cleared
+ Song time can show more than 99 hours now
+ Changed recognizing song filename when Apollo is 'stopped'
+ Added Pluton player support
+ Fixed: Clearing 'Music' XStatus doesn't work with new ICQ API
+0.0.4.0 (29 jan 2006)
+ Added option to disable log
+ Changed XStatus setting code to compatibles with new ICQ API
+ Fixed: Popup service function works only with TopToolBar
+ Vorbis comment tag names now case insensitive
+ Changed FLAC reading code. Now with Vorbis comments
+ Current music format saves and checks firstly
+ Fixed: 'Save settings in INI-file' option cleared if Options dialog opens
+ Nearby twin record in statistic file is ignored now
+ Relative report or statistic file path calculated now from Miranda directory
+ Corrected status changing when player stopped
+ Added %status% macro (works with not all players!!!)
+ Added option to save CSS file separately
+ Added automatic report file extension adding/changing
+ Added VBR/CBR text to File Info from pupup
+ Added %year% macro help (i just forgot)
+ Empty report file not created if log file is invalid
+ Added menu item and service function for report creating
+ Added support for SAPS player
+ Added support for Zoom player
+ Changed QCDPlayer code to support new QMPlayer
+0.0.3.8 (19 jan 2006)
+ Fixed MP3 VBR reading bug
+ Added simple statistic (UTF8 enc.) (just for fun)
+ Added %vbr% macro and option for it
+ Added template when player not found
+ Changed Options interface
+ Changed Service function interface (please, see comments!)
+ Fixed wrong %total% value when 'Variables' plugin used
+ Added MusicCube One player support
+ OGG tags now translated as UTF8
+ Fixed some ID3v2 tag reading issues and errors
+ Fixed track number recognize in APEv2 tag
+ Active Player places on first place (save in settings - AI! :) )
+ Now MP3 files checked firstly
+ Changed Foobar2000 main window search procedure
+ Deep code changes (translating to Unicode strings and other)
+0.0.3.7 (26 dec 2005)
+ Added file size text formatting
+ Corrected formatted text output
+ Fixed WMA reading code (again)
+ Fixed internal ANSI to Unicode transformation
+ Changed Options dialog
+0.0.3.6 (21 dec 2005)
+ Added Unicode support
+ Fixed wrong WMA tag reading
+ Advances PopUp settings
+ Toolbar button for popup window added
+ Small code optimisation
+ Fixed chat or message window recognition
+ Partially avoiding WMP10 playlist bug
+ Added XMPlayer support
+ Added macros %size% (file size) and %type% (file ext.)
+0.0.3.5 (05 dec 2005)
+ Now Status not processes and Popup not shows when player is stopped
+ Popup settings dialog created
+ Added BSPlayer support
+0.0.3.4 (28 nov 2005)
+ Some changes for Service function (mainly description, see header)
+ Fixed (i hope): Miranda crushes sometimes by pressing hotkey
+ 'No-music text' can processed by Variables plugin
+ Now Status changing works in separated protocols
+ Fixed: Popup shows not always
+ Status template now multiline
+ Added player icon in Popup window
+0.0.3.3 (24 nov 2005)
+ Changed Foobar2000 recognition for new versions
+ Remove elapsed time recongition within Foobar2000 (sorry!)
+ Now with player shutdown XStatus changes to 'none' and no sets anymore
+ Added simply PopUp support (Status Template value)
+ Added option to change XStatus only for existing Music xstatus
+ Added option to keep or not Music XStatus on player exit
+ Fixed: Miranda can crush while use Variables plugin
+0.0.3.2 (19 nov 2005)
+ File renamed for avoiding Updater plugin crash
+ Fixed: format tag at the end of template not translated
+ Fixed variables help
+ Added support for ViPlay3 player
+ Data for 'Variables' plugin updated by timer
+ Added ability to switch off some players
+ Added ability to change processed status mode
+ Added ability to save settings in INI-file
+ Settings saved only from Options dialog, not on exit
+ Some code changes
+0.0.3.0 (29 oct 2005)
+ Genre name table moved to resource
+ ICQ XStatus changes only for non-offline user mode
+ Fixed: 'new line' code inserted at the end
+0.0.2.9 (27 oct 2005)
+ Fixed: Stupid bug - text not inserted into CHAT window
+ Temporary: text formatting used in ordinal message window too (only for testing)
+0.0.2.8 (27 oct 2005)
+ Added support for RA/RM format (experimental)
+ Changed text formatting algorithm (test for Asian text, please!)
+ Created version archive on http://awkward.front.ru/archive/
+ Try to use simple Updater plugin compatibility
+0.0.2.7 (25 oct 2005)
+ Fixed: Status message not changed by timer
+ Added: experimental Chat window text formatting (see readme)
+0.0.2.6 (23 oct 2005)
+ Fixed some bugs.
+ Other small changes.
+0.0.2.5 (23 oct 2005)
+ Modified Option Dialog and added translation ability
+ Added partial support for AAC file format
+ Added support for MP4 with MP3-coding format
+ Added support for MusikCube player
+ Added alternative ID3v2 tag frames
+0.0.2.4 (19 oct 2005)
+ Changed hotkey processing (not global hook now)
+ Changed some players recognition
+ Changed Music info interface (see include file)
+ Added support for 'Variables' plugin (not fully tested)
+ Added support for MediaMonkey Player
+ Added support for RealPlayer
+ Added support for MPC file format (only 'MPC' extension)
+ Added partial support for SPX file format (not tested)
+ Added partial support for OFR file format (not tested)
+ Now Status sets only if changed
+0.0.2.2 (10 oct 2005)
+ (Not fully tested, save previous version!)
+ Now HotKey work only with own Miranda copy
+ New tag processing algorithm
+ Added WAV format support
+ Added experimental APE,TTA and FLAC formats support (not tested)
+ Added iTunes and PlayNow! players recognize
+ Some internal changes
+ Macro %samplerate% and %bitrate% (%khz% and %kbps% synonims) added
+ Fixed: unwanted XStatus changing to 'Music'
+0.0.1.8 (05 oct 2005)
+ Fixed: Sometime Foobar2000 main window not recognized properly
+ Small speed optimisation
+ Added LightAlloy recognize
+ Empty "No-Music text" keep original Miranda status messages now
+ Fixed: in Apollo and OGG-file Bitrate was Bps, not KBps,
+ Samplerate was Hz, not KHz
+0.0.1.7 (04 oct 2005)
+ Added automatic Music info refresh by timer
+ Added option to NOT insert music info into message window
+ Fixed: crash on some comments in ID3v2
+0.0.1.6 (03 oct 2005)
+ Changed Options dialog
+ Added simple Away-message support (only by Hotkey pressing)
+ Macro %year% now work
+0.0.1.5 (02 oct 2005)
+ Support for Unicode value in ID3v2
+ Add WMA tag support
+ Fixed: macro %genre% not translated
+ Fixed: Winamp window title processing
+0.0.1.4 (29 sep 2005)
+ Added JetAudio support
+ Fixed: VBR MP3 length not propely calculated
+ Fixed music file name recognition
+ Added APEv2+ID3v1 at one time tag support
+0.0.1.3 (28 sep 2005)
+ Now get music info from file on local network
+0.0.1.2 (27 sep 2005)
+ Changed 'year' field type in music info
+ Added APEv2 tag support for MP3
+ Remove Win98-code part
+ Other small changes
+0.0.1.1 (25 sep 2005)
+ Test version.
+ win2KXP-only compatible.
+ Service function added.
+ MP3 IDv2 tag data read fixed
+ Some other bugs fixed (sorry, not all!).
+0.0.1.0 (25 sep 2005)
+ Test version: new music file search algorithm (MP3 OGG WMA only)
+ Windows media player wrapper not needed now
+0.0.0.9 (24 sep 2005)
+ correct MP3 file processing with padding at start
+ cutting template strings tail fixed again :(
+ add %percent% macro
+ Modified source code uploaded
+0.0.0.8 (22 sep 2005)
+ cutting template strings tail (sometime) fixed
+ %wndtext% macro turn "on" again :)
+0.0.0.7 (19 sep 2005)
+ MP3 tag support extended
+ OGG file info (not length, sorry!) added
+ music info readed at one time now (for Winamp-like API - at request too)
+ Apollo samplerate show changed
+ %comment% macro added
+0.0.0.6 (18 sep 2005)
+ Macro %track% value changed
+ audio MPEG 2 & 2,5 and layer 1 & 2 file support added
+ Some code cleaning
+ Correct data view from MP3 Tag
+ Changed Artist-Title determination algorithm
+ Changed Winamp search algorithm
+0.0.0.5 (14 sep 2005)
+ Work improved, more players added
+ Default macro processing added
+ Fix some bugs
+ FooBar without Winamp wrapper supported (not full)
+ MP3 tag v1 partial support added
+0.0.0.3 (12 sep 2005)
+ New option interface
+ New engine for many player support
+ Macro support added
+0.0.0.1 (07 sep 2005)
+ First release
diff --git a/plugins/Watrack/docs/watrack_readme.txt b/plugins/Watrack/docs/watrack_readme.txt
new file mode 100644
index 0000000000..ef2ec81e98
--- /dev/null
+++ b/plugins/Watrack/docs/watrack_readme.txt
@@ -0,0 +1,110 @@
+For what:
+ Insert Played music info in message window or Status text. ICQ extended
+ status supported.
+
+Requirements:
+ Windows 2K/XP (SP2 better)
+ Miranda 0.6.x
+
+Supported Players:
+ Winamp (http://www.winamp.com/)
+ Apollo (http://www.iki.fi/hy/apollo/)
+ 1by1 (http://www.mpesch3.de/)
+ Media Player Classic (http://gabest.org/)
+ Window Media Player (http://www.microsoft.com/windows/windowsmedia/players.aspx)
+ FooBar2000 (http://www.foobar2000.org/)
+ LightAlloy (http://www.softella.com/)
+ Cowon JetAudio (http://www.jetaudio.com/)
+ Quintessential Player (http://quinnware.com/)
+ iTunes (http://www.itunes.com/)
+ MediaMonkey (http://www.mediamonkey.com/)
+ Real Player (http://www.real.com/)
+ MusikCube (http://www.musikcube.com/)
+ BSPlayer (http://www.bsplayer.org/)
+ MusicCube One (http://www.rodi.dk/musiccubeone)
+ Zoom Player (http://www.inmatrix.com/)
+ Pluton (http://pluton.oss.ru/)
+ J. River Media Center (http://www.jrmediacenter.com/)
+ Musicmatch Jukebox (http://wwwp.musicmatch.com/)
+ VideoLAN media player (http://www.videolan.org/)
+ mRadio miranda plugin (http://miranda.kom.pl/dev/bankrut/)
+ ALSong&ALShow (http://www.altools.net/)
+
+ and others. List of other players see in player.ini
+
+Supported Formats:
+ MP3, OGG, WMA, WAV, APE, TTA, AAC, FLA/FLAC, MPC, OFR/OFS, SPX, MP4, M4A,
+ ASF, WMV, AVI, MKV, OGM, RA/RM/RAM, FLV, MOV, 3GP, MPEG/MPG
+
+In Chat (and maybe message) window, you can use text formatting:
+{b}text{/b} - 'bold' text
+{i}text{/i} - 'italic' text
+{u}text{/u} - 'undeline' text
+{cf##}text{/cf} - text with color ## (0-15)
+{bg##}text{/bg} - text with background color ## (0-15)
+Sample:/me {b}listen{/b} {cf5}{i}%artist%{/i}{/cf} - {bg10}{u}"%title%"{/u}{/bg}
+
+Macros:
+ %album% - album
+ %artist% - artist
+ %bitrate% - bitrate
+ %channels% - number of channels
+ %codec% - video codec like 0x30355844 (DX50)
+ %comment% - comment from tag
+ %cover% - cover file name
+ %file% - media file name
+ %fps% - 100*FPS (Frames Per Second) for video files
+ %height% - video height in pixels
+ %genre% - genre
+ %kbps% - bitrate
+ %khz% - samplerate
+ %length% - total song length (sec)
+ %lyric% - lyric text
+ %mono% - "mono"/"stereo"
+ %nstatus%' - player status (stopped,playing,paused) - nontranslated
+ %percent% - %time% / %total% * 100%
+ %player% - player
+ %samplerate% - samplerate
+ %size% - media file size
+ %status%' - player status (stopped,playing,paused)
+ %time% - current song position (sec)
+ %title% - song title
+ %total% - total song length (sec)
+ %track% - track number
+ %txtver% - player version in text format
+ %type% - media file type (extension)
+ %vbr% - 'VBR' if VBR :)
+ %version% - player version
+ %width% - video width in pixels
+ %wndtext% - title from player window (usually "artist" - "title")
+ %year% - song year (from tag)
+
+Notes:
+ - Volume field has a original volume value in hiword and scaled to 0-15 range
+ in loword. Not all players supported.
+ - Frame background picture transparence is not implemented
+ - To obtain more information from foobar2000 player, you must download plugin
+ http://foosion.foobar2000.org/0.9/foo_comserver2-0.7-setup.exe
+ or use foo_winampspam plugin
+ - FileInfo (called from popup) not shows Video file properties
+ - FPS saves as FPS*100
+ - Frame text output is Left-to-Right only now
+
+Known BUGs:
+ - if Frame text uses %time% macro, text can't scroll
+ - Foobar seeking with foo_winamp_spam may not work
+ - Miranda can freeze if Foobar OLE interface used
+ - Player can start again if OLE checks while it shutdown
+ - ICQLite and ICQ2003 users cannot see XStatus text changes
+ - in MP3 with VBR total song time sometime is not properly calculated
+ - default color of formated text is color of text at insert position
+ - some macros don't work with some players. Replaced by '' and 0
+ - 1by1 player can show wrong elapsed time value
+ - some players version not properly displayed
+ - bad MP3 headers not properly handled
+ - plugin can show wrong song when Player scans directory for music files
+
+PS. To compile plugin you must use something like this:
+ dcc32 -$A+ -$H+ watrack.dpr
+
+All comments, errors & wishes please send to awkward@land.ru or panda75@bk.ru
diff --git a/plugins/Watrack/formats/fmt_aac.pas b/plugins/Watrack/formats/fmt_aac.pas
new file mode 100644
index 0000000000..db8e03b153
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_aac.pas
@@ -0,0 +1,93 @@
+{AAC file process}
+unit fmt_AAC;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadAAC(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,tags,srv_format;
+
+const
+ SampleRates:array [0..15] of dword = (
+ 96000,88200,64000,48000,44100,32000,24000,22050,
+ 16000,12000,11025,8000,0,0,0,0);
+
+procedure ReadADIFheader(f:THANDLE;var Info:tSongInfo);
+var
+ buf:array [0..29] of byte;
+ bs,sf_idx,skip:dword;
+begin
+ BlockRead(f,buf,30);
+ if (buf[0] and $80)<>0 then
+ skip:=9
+ else
+ skip:=0;
+ Info.kbps:=(((buf[0+skip] and $0F) shl 19)+(buf[1+skip] shl 11)+
+ (buf[2+skip] shl 3)+{or}((buf[3+skip] and $E0){shr 5})) div 1000;
+ bs:=buf[0+skip] and $10;
+ if bs=0 then
+ sf_idx:=(buf[7+skip] and $78) shr 3
+ else
+ sf_idx:=((buf[4+skip] and $07) shl 1)+((buf[5+skip] and $80) shr 7);
+ Info.khz:=SampleRates[sf_idx];
+end;
+
+procedure ReadADTSheader(var Info:tSongInfo;sign:dword);
+type
+ l2b=record
+ b:array [0..3] of byte;
+ end;
+var
+ sr_idx:integer;
+begin
+ Info.channels:=((l2b(sign).b[2] and $01) shl 2)+
+ ((l2b(sign).b[3] and $C0) shr 6);
+ sr_idx:=(l2b(sign).b[2] and $3C) shr 2;
+ Info.khz:=SampleRates[sr_idx] div 1000;
+end;
+
+function ReadAAC(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ sign:dword;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ ReadID3v2(f,Info);
+ BlockRead(f,sign,4);
+ Info.khz:=44;
+ Info.kbps:=128;
+ Info.channels:=2;
+ if (lobyte(sign)=$FF) and ((hibyte(sign) and $F6)=$F0) then
+ ReadADTSheader(Info,sign)
+ else if sign=$46494441 then // 'ADIF'
+ ReadADIFheader(f,Info);
+
+ ReadAPEv2(f,Info);
+ ReadID3v1(f,Info);
+ CloseHandle(f);
+ result:=true;
+end;
+
+var
+ LocalFormatLink:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLink.Next:=FormatLink;
+
+ LocalFormatLink.This.proc :=@ReadAAC;
+ LocalFormatLink.This.ext :='AAC';
+ LocalFormatLink.This.flags:=0;
+
+ FormatLink:=@LocalFormatLink;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_ape.pas b/plugins/Watrack/formats/fmt_ape.pas
new file mode 100644
index 0000000000..fbabdac19c
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_ape.pas
@@ -0,0 +1,137 @@
+{APE file}
+unit fmt_APE;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadAPE(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+
+uses windows,common,io,tags,srv_format;
+
+const
+ defID = $2043414D;
+type
+(* Old Version ?
+ tMonkeyHeader = record
+ ID :dword; { Always "MAC " }
+ VersionID :word; { Version number * 1000 (3.91 = 3910) }
+ CompressionID :word; { Compression level code }
+ Flags :word; { Any format flags }
+ Channels :word; { Number of channels }
+ SampleRate :dword; { Sample rate (hz) }
+ HeaderBytes :dword; { Header length (without header ID) }
+ TerminatingBytes:dword; { Extended data }
+ Frames :dword; { Number of frames in the file }
+ FinalSamples :dword; { Number of samples in the final frame }
+ PeakLevel :dword; { Peak level (if stored) }
+ SeekElements :dword; { Number of seek elements (if stored) }
+ end;
+*)
+ tMonkeyHeader = packed record
+ ID :dword; // should equal 'MAC '
+ VersionID :dword; // version number * 1000 (3.81 = 3810)
+ nDescriptorBytes :dword; // descriptor bytes
+ nHeaderBytes :dword; // APEHeader bytes
+ nSeekTableBytes :dword; // bytes of the seek table
+ nHeaderDataBytes :dword; // header data bytes (from original file)
+ nFrameDataBytes :dword; // bytes of APE frame data
+ nFrameDataBytesHi:dword; // the high order number of APE frame data bytes
+ nTerminatingBytes:dword; // the terminating data of the file (w/o tag data)
+ cFileMD5:array [0..15] of byte;
+ end;
+type
+ tAPEHeader = packed record
+ nCompressionLevel:word; // the compression level
+ nFormatFlags :word; // any format flags (for future use)
+ nBlocksPerFrame :dword; // the number of audio blocks in one frame
+ nFinalFrameBlocks:dword; // the number of audio blocks in the final frame
+ nTotalFrames :dword; // the total number of frames
+ nBitsPerSample :word; // the bits per sample (typically 16)
+ nChannels :word; // the number of channels (1 or 2)
+ nSampleRate :dword; // the sample rate (typically 44100)
+ end;
+
+const
+ MONKEY_COMPRESSION_FAST = 1000; // Fast (poor)
+ MONKEY_COMPRESSION_NORMAL = 2000; // Normal (good)
+ MONKEY_COMPRESSION_HIGH = 3000; // High (very good)
+ MONKEY_COMPRESSION_EXTRA_HIGH = 4000; // Extra high (best)
+const
+ MONKEY_FLAG_8_BIT = 1; // Audio 8-bit
+ MONKEY_FLAG_CRC = 2; // New CRC32 error detection
+ MONKEY_FLAG_PEAK_LEVEL = 4; // Peak level stored
+ MONKEY_FLAG_24_BIT = 8; // Audio 24-bit
+ MONKEY_FLAG_SEEK_ELEMENTS = 16; // Number of seek elements stored
+ MONKEY_FLAG_WAV_NOT_STORED = 32; // WAV header not stored
+
+function ReadAPE(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ hdr:tMonkeyHeader;
+ hdr1:tAPEHeader;
+ blocks:dword;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ ReadID3v2(f,Info);
+ BlockRead(f,hdr ,SizeOf(tMonkeyHeader));
+ BlockRead(f,hdr1,SizeOf(tAPEHeader)); //hdr.nHeaderBytes
+ if hdr1.nTotalFrames=0 then
+ blocks:=0
+ else
+ blocks:=(hdr1.nTotalFrames-1)*hdr1.nBlocksPerFrame+hdr1.nFinalFrameBlocks;
+ Info.khz :=hdr1.nSampleRate div 1000;
+ if hdr1.nSampleRate<>0 then
+ Info.total :=blocks div hdr1.nSampleRate;
+ Info.channels:=hdr1.nChannels;
+// Info.kbps:=Info.khz*deep*Info.channels/1152
+// Info.kbps:=(blocks*Info.channels*hdr1.nBitsPerSample) div (Info.total*8000);
+// Info.kbps :=((hdr1.nBitsPerSample div 8)*hdr1.nSamplerate) div 1000;
+(* Old version ?
+ if (hdr.ID<>DefID) or (hdr.SampleRate=0) or (hdr.Channels=0) then
+ exit;
+ if (hdr.VersionID>=3900) or
+ ((hdr.VersionID>=3800) and
+ (hdr.CompressionID=MONKEY_COMPRESSION_EXTRA_HIGH)) then
+ tmp:=73728
+ else
+ tmp:=9216;
+ tmp:=(hdr.Frames-1)*tmp+hdr.FinalSamples;
+ Info.total :=tmp div hdr.SampleRate;
+ Info.khz :=hdr.SampleRate div 1000;
+ Info.channels:=hdr.Channels;
+
+ Info.kbps:=tmp;//samples
+ if (hdr.Flags and MONKEY_FLAG_8_BIT)<>0 then tmp:=8
+ else if (hdr.Flags and MONKEY_FLAG_24_BIT)<>0 then tmp:=24
+ else tmp:=16;
+ Info.kbps:=((Info.kbps*tmp*hdr.Channels) div Info.Total) div 1000;
+*)
+ ReadAPEv2(f,Info);
+ ReadID3v1(f,Info);
+ CloseHandle(f);
+ result:=true;
+end;
+
+var
+ LocalFormatLink:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLink.Next:=FormatLink;
+
+ LocalFormatLink.This.proc :=@ReadAPE;
+ LocalFormatLink.This.ext :='APE';
+ LocalFormatLink.This.flags:=0;
+
+ FormatLink:=@LocalFormatLink;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_avi.pas b/plugins/Watrack/formats/fmt_avi.pas
new file mode 100644
index 0000000000..3646236cf7
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_avi.pas
@@ -0,0 +1,295 @@
+{AVI file format}
+unit fmt_AVI;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadAVI(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,srv_format;
+
+type
+ FOURCC = array [0..3] of AnsiChar;
+type
+ tChunkHeader = packed record
+ case byte of
+ 0: (Lo,Hi:dword); {Common}
+ 1: (ID:FOURCC; {RIFF}
+ Length:dword);
+ end;
+
+const
+ sRIFF = $46464952;
+ sLIST = $5453494C;
+ savih = $68697661; { avi header }
+ sstrf = $66727473; { stream format }
+ sstrh = $68727473; { stream header }
+const
+ smovi = $69766F6D; {movi list type}
+const
+ svids = $73646976; {video}
+ sauds = $73647561; {audio}
+const
+ sIART = $54524149; {director}
+ sICMT = $544D4349; {comment}
+ sICRD = $44524349; {creation date}
+ sIGNR = $524E4749; {genre}
+ sINAM = $4D414E49; {title}
+ sIPRT = $54525049; {part}
+ sIPRO = $4F525049; {produced by}
+ sISBJ = $4A425349; {subject description}
+
+type
+ tWaveFormatEx = packed record
+ wFormatTag :word;
+ nChannels :word;
+ nSamplesPerSec :dword;
+ nAvgBytesPerSec:dword;
+ nBlockAlign :word;
+ wBitsPerSample :word;
+ cbSize :word;
+
+ Reserved1 :word;
+ wID :word;
+ fwFlags :word;
+ nBlockSize :word;
+ nFramesPerBlock:word;
+ nCodecDelay :word; {ms}
+ end;
+
+type
+ tMainAVIHeader = packed record {avih}
+ dwMicroSecPerFrame :dword;
+ dwMaxBytesPerSec :dword;
+ dwPaddingGranularity :dword;
+ dwFlags :dword;
+ dwTotalFrames :dword; { # frames in first movi list}
+ dwInitialFrames :dword;
+ dwStreams :dword;
+ dwSuggestedBufferSize:dword;
+ dwWidth :dword;
+ dwHeight :dword;
+ dwScale :dword;
+ dwRate :dword;
+ dwStart :dword;
+ dwLength :dword;
+ end;
+
+type
+ TAVIExtHeader = packed record {dmlh}
+ dwGrandFrames:dword; {total number of frames in the file}
+ dwFuture:array[0..60] of dword;
+ end;
+
+type
+ tAVIStreamHeader = packed record {strh}
+ fccType :FOURCC; {vids|auds}
+ fccHandler :FOURCC;
+ dwFlags :dword;
+ wPriority :word;
+ wLanguage :word;
+ dwInitialFrames :dword;
+ dwScale :dword;
+ dwRate :dword;
+ dwStart :dword;
+ dwLength :dword;
+ dwSuggestedBufferSize:dword;
+ dwQuality :dword;
+ dwSampleSize :dword;
+ rcFrame: packed record
+ left :word;
+ top :word;
+ right :word;
+ bottom:word;
+ end;
+ end;
+
+var
+ vora:dword;
+
+procedure Skip(f:THANDLE;bytes:dword);
+var
+ i:dword;
+begin
+ i:=FilePos(f);
+ if bytes=0 then
+ begin
+ if odd(i) then
+ Seek(f,i+1);
+ end
+ else
+ Seek(f,i+bytes+(bytes mod 2));
+end;
+
+procedure ProcessVideoFormat(f:THANDLE;Size:dword;var Info:tSongInfo);
+var
+ bih:BitmapInfoHeader;
+begin
+ BlockRead(f,bih,SizeOf(bih));
+ Info.codec :=bih.biCompression;
+ Info.width :=bih.biWidth;
+ Info.height:=bih.biHeight;
+ Skip(f,Size-SizeOf(bih));
+end;
+
+procedure ProcessAudioFormat(f:THANDLE;Size:dword;var Info:tSongInfo);
+{WAVEFORMATEX or PCMWAVEFORMAT}
+var
+ AF:tWaveFormatEx;
+begin
+ BlockRead(f,AF,SizeOf(AF));
+ Info.channels:=AF.nChannels;
+ Info.khz :=AF.nSamplesPerSec div 1000;
+ Info.kbps :=(AF.nAvgBytesPerSec*8) div 1000;
+ Skip(f,Size-SizeOf(AF));
+end;
+
+function ProcessASH(f:THANDLE;var Info:tSongInfo):dword;
+var
+ ASH:tAVIStreamHeader;
+begin
+ BlockRead(f,ASH,SizeOf(ASH));
+ with ASH do
+ begin
+ if dword(fccType)=svids then
+ begin
+ if ASH.dwScale<>0 then
+ Info.fps:=(ASH.dwRate*100) div ASH.dwScale;
+ if Info.fps<>0 then
+ Info.total:=(ASH.dwLength*100) div Info.fps;
+ ProcessASH:=1
+ end
+ else if dword(fccType)=sauds then ProcessASH:=2
+ else ProcessASH:=0;
+ end;
+end;
+
+procedure ProcessMAH(f:THANDLE;var Info:tSongInfo);
+var
+ MAH:tMainAVIHeader;
+begin
+ BlockRead(f,MAH,SizeOf(MAH));
+// Info.width:=MAH.dwWidth;
+// Info.height:=MAH.dwHeight;
+// Info.fps:=100000000 div MAH.dwMicroSecPerFrame;
+end;
+
+function ProcessChunk(f:THANDLE;var Info:tSongInfo):dword;
+var
+ lTotal:dword;
+ Chunk:tChunkHeader;
+ cType:FOURCC;
+ ls:PAnsiChar;
+begin
+ Skip(f,0);
+ if (BlockRead(f,Chunk,SizeOF(Chunk))=0) or (Chunk.Lo=0) then
+ begin
+ result:=FileSize(f);
+ Seek(f,FileSize(f));
+ exit;
+ end;
+ result:=Chunk.Length+SizeOf(Chunk);
+ case Chunk.Lo of
+ sRIFF,sLIST: begin
+ BlockRead(f,cType,SizeOf(cType));
+ if dword(cType)=smovi then
+ Skip(f,Chunk.Length-SizeOf(cType)) // result:=FileSize(f)
+ else
+ begin
+ lTotal:=SizeOf(FOURCC);
+ while lTotal<Chunk.Length do
+ inc(lTotal,ProcessChunk(f,Info));
+ end;
+ end;
+ sIART,sICMT,sICRD,sIGNR,sINAM,sIPRT,sIPRO,sISBJ: begin
+ mGetMem(ls,Chunk.Length);
+ BlockRead(f,ls^,Chunk.Length);
+ case Chunk.Lo of
+ sIART: begin
+ AnsiToWide(ls,Info.artist);
+ end;
+ sICMT: begin
+ if Info.comment=NIL then
+ AnsiToWide(ls,Info.comment);
+ end;
+ sICRD: begin
+ AnsiToWide(ls,Info.year);
+ end;
+ sIGNR: begin
+ AnsiToWide(ls,Info.genre);
+ end;
+ sINAM: begin
+ AnsiToWide(ls,Info.title);
+ end;
+ sIPRT: begin
+ Info.track:=StrToInt(ls);
+ end;
+ sIPRO: begin
+ if Info.artist=NIL then
+ AnsiToWide(ls,Info.artist);
+ end;
+ sISBJ: begin
+ AnsiToWide(ls,Info.comment);
+ end;
+ end;
+ mFreeMem(ls);
+ end;
+ savih: begin
+ ProcessMAH(f,Info);
+ end;
+ sstrh: begin
+ vora:=ProcessASH(f,Info);
+ end;
+ sstrf: begin
+ case vora of
+ 1: ProcessVideoFormat(f,Chunk.Hi,Info);
+ 2: ProcessAudioFormat(f,Chunk.Hi,Info);
+ else
+ end;
+ end;
+ else
+ Skip(f,Chunk.Length);
+ end;
+end;
+
+function ReadAVI(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ ProcessChunk(f,Info);
+ CloseHandle(f);
+ result:=true;
+end;
+
+var
+ LocalFormatLinkAVI,
+ LocalFormatLinkDIVX:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLinkAVI.Next:=FormatLink;
+
+ LocalFormatLinkAVI.This.proc :=@ReadAVI;
+ LocalFormatLinkAVI.This.ext :='AVI';
+ LocalFormatLinkAVI.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkAVI;
+
+ LocalFormatLinkDIVX.Next:=FormatLink;
+
+ LocalFormatLinkDIVX.This.proc :=@ReadAVI;
+ LocalFormatLinkDIVX.This.ext :='DIVX';
+ LocalFormatLinkDIVX.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkDIVX;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_dummy.pas b/plugins/Watrack/formats/fmt_dummy.pas
new file mode 100644
index 0000000000..e691def279
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_dummy.pas
@@ -0,0 +1,46 @@
+{OFR file}
+unit fmt_Dummy;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadDummy(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,tags,srv_format;
+
+function ReadDummy(var Info:tSongInfo):boolean; cdecl;
+{
+var
+ f:THANDLE;
+}
+begin
+{
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+
+ CloseHandle(f);
+}
+ result:=true;
+end;
+
+var
+ LocalFormatLinkCUE:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLinkCUE.Next:=FormatLink;
+
+ LocalFormatLinkCUE.This.proc :=@ReadDummy;
+ LocalFormatLinkCUE.This.ext :='CUE';
+ LocalFormatLinkCUE.This.flags:=WAT_OPT_CONTAINER;
+
+ FormatLink:=@LocalFormatLinkCUE;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_flv.pas b/plugins/Watrack/formats/fmt_flv.pas
new file mode 100644
index 0000000000..b8e23f5809
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_flv.pas
@@ -0,0 +1,334 @@
+{FLV file format}
+unit fmt_FLV;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadFLV(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,srv_format;
+
+type
+ tFLVHeader = packed record
+ Signature:array [0..2] of AnsiChar; // FLV
+ Version :byte;
+ flags :byte;
+ Offset :dword; // reversed byte order
+ end;
+type
+ tFLVStream = packed record
+ PreviousTagSize :dword;
+ TagType :byte;
+ BodyLength :array [0..2] of byte;
+ Timestamp :array [0..2] of byte;
+ TimestampExtended:byte;
+ StreamID :array [0..2] of byte;
+// Body
+ end;
+{
+ twork = record
+ endptr:PAnsiChar;
+ Info :pSongInfo;
+ key :PAnsiChar;
+ len :cardinal;
+ end;
+}
+// FLVTagTypes
+const
+ FLV_AUDIO = 8;
+ FLV_VIDEO = 9;
+ FLV_META = 18;
+
+const
+ BufSize = 128*1024;
+
+type
+ pArr = ^tArr;
+ tArr = array [0..7] of byte;
+
+ transform=packed record
+ case byte of
+ 0: (txt:array [0..3] of AnsiChar);
+ 1: (num:dword);
+ end;
+ trecode=packed record
+ case byte of
+ 0: (i:int64);
+ 1: (d:double);
+ end;
+
+function Reverse(buf:int64;len:integer):int64;
+var
+ i:integer;
+begin
+ result:=0;
+ for i:=0 to len-1 do
+ result:=(result shl 8)+tArr(buf)[i];
+end;
+
+function ProcessValue(var ptr:PAnsiChar;key:pAnsiChar;var Info:tSongInfo):integer;
+var
+ tmp:int64;
+ i,len:integer;
+ recode:trecode;
+ code:integer;
+ codec:transform;
+ value:array [0..63] of AnsiChar;
+begin
+ result:=1;
+ code:=ord(ptr^); // type of value
+ ptr^:=#0; // finalization for key name
+ inc(ptr); // value data pointer
+
+ case code of // v10.1 specification
+
+ // Numeric, Double (8 bytes)
+ 0: begin
+ move(ptr^,tmp,8);
+ recode.i:=Reverse(tmp,8);
+ i:=trunc(recode.d);
+ if StrCmp(key,'duration' )=0 then Info.total :=i
+ else if StrCmp(key,'totalduration')=0 then Info.total :=i
+ else if StrCmp(key,'width' )=0 then Info.width :=i
+ else if StrCmp(key,'height' )=0 then Info.height:=i
+ else if StrCmp(key,'audiodatarate')=0 then Info.kbps :=i
+ else if StrCmp(key,'framerate' )=0 then Info.fps :=trunc(recode.d*100)
+ else if StrCmp(key,'audiosize' )=0 then
+ begin
+ if Info.kbps=0 then
+ Info.kbps:=trunc((recode.d*8)/(Info.total*1000))
+ end
+ else if StrCmp(key,'videocodecid')=0 then
+ begin
+ case i of
+ 2: codec.txt:='H263';
+ 3: codec.txt:='Scrn';
+ 4,5: codec.txt:='VP6 ';
+ 6: codec.txt:='Src2';
+ 7: codec.txt:='AVC ';
+ end;
+ Info.codec:=codec.num;
+ end;
+
+ inc(ptr,8);
+ end;
+
+ // Boolean, UI8
+ 1: begin
+ if StrCmp(key,'stereo')=0 then Info.channels:=ORD(ptr^)+1;
+
+ inc(ptr);
+ end;
+
+ // String
+ 2: begin
+ i:=Reverse(pWord(ptr)^,2); inc(ptr,2);
+
+ if StrCmp(key,'creationdate')=0 then
+ begin
+ move(ptr^,value[0],i);
+ value[i]:=#0;
+ AnsiToWide(value,Info.year);
+ end;
+
+ inc(ptr,i);
+ end;
+
+ // Object
+ 3: begin
+ repeat
+ len:=Reverse(pWord(ptr)^,2); inc(ptr,2); // key name length
+ key:=ptr; inc(ptr,len); // key name
+
+ result:=ProcessValue(ptr,key,Info);
+ until result<=0;
+
+ if result<0 then
+ result:=1;
+ end;
+
+ // Movie clip, reserved
+ 4: begin
+ end;
+
+ // NULL
+ 5: begin
+ end;
+
+ // Undefined
+ 6: begin
+ end;
+
+ // reference, UI16
+ 7: begin
+ inc(ptr,2);
+ end;
+
+ // ECMA array
+ 8: begin
+ i:=pdword(ptr)^; inc(ptr,4);
+ i:=Reverse(i,4);
+ while i>0 do
+ begin
+
+ len:=Reverse(pWord(ptr)^,2); inc(ptr,2); // key name length
+ key:=ptr; inc(ptr,len); // key name
+
+ result:=ProcessValue(ptr,key,Info);
+
+ if result=0 then break
+ else if result<0 then
+ begin
+ result:=1;
+ break;
+ end;
+ dec(i);
+ end;
+ end;
+
+ // Object end marker, UI8[3]=0,0,9
+ 9: begin
+ result:=-1;
+ inc(ptr,3);
+ end;
+
+ 10: // array, 4 bytes - num of elements, elements
+ begin
+ i:=pdword(ptr)^; inc(ptr,4);
+ i:=Reverse(i,4);
+ while i>0 do
+ begin
+ result:=ProcessValue(ptr,nil,Info);
+ if result=0 then exit
+ else if result<0 then
+ begin
+ result:=1;
+ break;
+ end;
+ dec(i);
+ end;
+ end;
+
+ // Date, Double + UI16 (UTC)
+ 11: begin
+ inc(ptr,8);
+ inc(ptr,2);
+ end;
+
+ // LongString, 4 bytes = len, len - string
+ 12: begin
+ i:=pdword(ptr)^; inc(ptr,4);
+ i:=Reverse(i,4);
+
+ inc(ptr,i);
+ end;
+
+ end;
+end;
+
+function ReadFLV(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ codec:transform;
+ FLVHdr:tFLVHeader;
+ StrmHdr:tFLVStream;
+ i,len:integer;
+ buf,pp,p,endbuf:PAnsiChar;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+
+ mGetMem(buf,BufSize);
+ endbuf:=buf+BlockRead(f,buf^,BufSize);
+ p:=buf;
+ CloseHandle(f);
+ move(p^,FLVHdr,SizeOf(tFLVHeader));
+ if (FLVHdr.Signature[0]='F') and (FLVHdr.Signature[1]='L') and
+ (FLVHdr.Signature[2]='V') and (FLVHdr.Version=1) then
+ begin
+ inc(p,SizeOf(tFLVHeader));
+ result:=true;
+ while (p<endbuf) and ((FLVHdr.flags and 5)<>0) do
+ begin
+ move(p^,StrmHdr,SizeOf(tFLVStream));
+ inc(p,SizeOf(tFLVStream));
+ len:=(StrmHdr.BodyLength[0] shl 16)+(StrmHdr.BodyLength[1] shl 8)+
+ StrmHdr.BodyLength[2];
+ pp:=p;
+ case StrmHdr.TagType of
+
+ FLV_AUDIO: begin
+ Info.channels:=(ord(p^) and 1)+1;
+ // samplesize is (S_Byte and 2) shr 1 = 8 or 16
+ case (ord(p^) and $C) shr 2 of
+ 0: Info.khz:=5;
+ 1: Info.khz:=11;
+ 2: Info.khz:=22;
+ 3: Info.khz:=44;
+ end;
+ FLVHdr.flags:=FLVHdr.flags and not 4;
+ end;
+
+ FLV_VIDEO: begin
+ case ord(p^) and $0F of
+ 2: codec.txt:='H263';
+ 3: codec.txt:='Scrn';
+ 4,5: codec.txt:='VP6 ';
+ 6: codec.txt:='Src2';
+ 7: codec.txt:='AVC ';
+ end;
+ Info.codec:=codec.num;
+ FLVHdr.flags:=FLVHdr.flags and not 1;
+ end;
+
+ FLV_META: begin
+ if (StrmHdr.TagType and $40)=0 then // not encripted
+ begin
+ if pByte(p)^=2 then // string
+ begin
+ Inc(p);
+ i:=Reverse(pWord(p)^,2); inc(p,2);
+ if StrCmp(p,'onMetaData',i)=0 then // Metadata processing start
+ begin
+ inc(p,i);
+ ProcessValue(p,nil,Info); // metadata, no need key name, our info
+ // checking for video
+ if Info.codec<>0 then
+ FLVHdr.flags:=FLVHdr.flags and not 1;
+ // checking for audio
+ if (Info.khz<>0) and (Info.channels<>0) then
+ FLVHdr.flags:=FLVHdr.flags and not 4;
+ // break; // if metainfo is enough
+ end;
+ end;
+ end;
+ end;
+
+ end;
+ p:=pp+len;
+ end;
+ end;
+ mFreeMem(buf);
+end;
+
+var
+ LocalFormatLink:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLink.Next:=FormatLink;
+
+ LocalFormatLink.This.proc :=@ReadFLV;
+ LocalFormatLink.This.ext :='FLV';
+ LocalFormatLink.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLink;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_m4a.pas b/plugins/Watrack/formats/fmt_m4a.pas
new file mode 100644
index 0000000000..1bfcf2309a
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_m4a.pas
@@ -0,0 +1,378 @@
+{M4A code template}
+unit fmt_M4A;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadM4A(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+
+uses windows,common,io,srv_format,
+{$IFDEF KOL_MCK}KolZlibBzip{$ELSE}Zlib,zwrapper{$ENDIF};
+
+type
+ mp4Atom = record
+ len:dword;
+ name:dword;
+ end;
+
+const
+ at_moov = $766F6F6D;
+ at_mvhd = $6468766D;
+ at_udta = $61746475;
+ at_meta = $6174656D;
+ at_ilst = $74736C69;
+ at_cmov = $766F6D63;
+ at_dcom = $6D6F6364;
+ at_cmvd = $64766D63; // 4 - unpacked size, data
+ at_trak = $6B617274;
+ at_tkhd = $64686B74; // not needed
+ at_mdia = $6169646D;
+ at_minf = $666E696D;
+ at_smhd = $64686D73;
+ at_vmhd = $64686D76;
+ at_stbl = $6C627473;
+ at_stsd = $64737473;
+
+const
+ atm_nam = $6D616EA9; // title
+ atm_ART = $545241A9; // artist
+ atm_wrt = $747277A9; // writer
+ atm_alb = $626C61A9; // album
+ atm_day = $796164A9; // date
+ atm_cmt = $746D63A9; // comment
+ atm_gen = $6E6567A9; // alt.genre
+ atm_gnre = $65726E67; // genre
+ atm_trkn = $6E6B7274; // track number
+// atm_zlib = $62696C7A;
+
+type
+ pstsd = ^tstsd;
+ tstsd = packed record
+ version :byte;
+ flags :array [0..2] of byte;
+ NumEntries :dword;
+ SampleDescSize:dword; // $56
+ DataFormat :dword;
+ reserved :array [0..5] of byte;
+ RefIndex :word;
+ Ver :word;
+ Revision :word;
+ Vendor :dword;
+ Temporal :dword;
+ Spacial :dword;
+ Width :word;
+ Height :word;
+ HRes :dword; //single;
+ VRes :dword;
+ DataSize :dword;
+ FrameCount :word;
+ CompNameLen :byte;
+ Compressor :array [0..18] of AnsiChar;
+ ColorDepth :word;
+ ColorTableID :word;
+ end;
+ pastsd = ^astsd;
+ astsd = packed record
+ Version :byte;
+ Flags :array [0..2] of byte;
+ NumEntires :dword;
+ DescSize :dword;
+ CodingName :array[0..3] of AnsiChar;
+ Reserved :array[0..5] of Byte;
+ RefIndex :Word;
+ Reserved_ :array[0..1] of dword;
+ ChannelCount:Word;
+ SampleSize :Word;
+ Pre_defined :Word;
+ Reserved___ :Word;
+ Samplerate :dword;
+ end;
+ pmvhd = ^mvhd;
+ mvhd = packed record
+ Version:byte;
+ flags:array [0..2] of byte;
+ Creation:dword;
+ Modification:dword;
+ TimeScale:dword;
+ Duration:dword;
+ end;
+
+procedure ReadAtom(f:THANDLE;var atom:mp4Atom);
+begin
+ BlockRead(f,atom.len,4);
+ if atom.len>0 then
+ begin
+ BlockRead(f,atom.name,4);
+ atom.len:=BSwap(atom.len);
+ end
+ else
+ begin
+ atom.name:=0;
+ atom.len:=8;
+ end;
+end;
+
+procedure GetAtom(var p:pbyte;var atom:mp4Atom);
+begin
+ atom.len:=pdword(p)^;
+ inc(p,4);
+ if atom.len>0 then
+ begin
+ atom.name:=pdword(p)^;
+ inc(p,4);
+ atom.len:=BSwap(atom.len);
+ end
+ else
+ begin
+ atom.name:=0;
+ atom.len:=8;
+ end;
+end;
+
+function SetTree(from:mp4Atom;var p:pbyte;path:PAnsiChar;var parent:pbyte):integer;
+var
+ atom:mp4Atom;
+ len:cardinal;
+ saved:pbyte;
+begin
+ saved:=p;
+ len:=0;
+ dec(from.len,SizeOf(from));
+ parent:=p;
+ repeat
+ GetAtom(p,atom);
+ if atom.name=pdword(path)^ then
+ begin
+ inc(path,4);
+ if path^<>#0 then
+ begin
+ parent:=p;
+ inc(path);
+ len:=0;
+ from.len:=atom.len-SizeOf(atom);
+ end
+ else
+ begin
+ result:=atom.len;
+ exit;
+ end;
+ end
+ else
+ begin
+ inc(p,atom.len-SizeOf(atom));
+ inc(len,atom.len);
+ end;
+ until len>=from.len;
+ result:=-1;
+ p:=saved;
+end;
+
+function ReadInt(var p:pbyte):dword;
+var
+ len:integer;
+begin
+ len:=pdword(p)^;
+ inc(p,4);
+ len:=BSwap(len);
+ if len>0 then
+ inc(p,4); // 'data'
+ inc(p,4); // type?
+ inc(p,4); // encoding?
+ dec(len,8+8);
+ if len>4 then len:=4;
+ if len=2 then
+ begin
+ result:=p^*$100;
+ inc(p);
+ inc(result,p^);
+ inc(p);
+ end
+ else
+ begin
+ result:=BSwap(pdword(p)^);
+ inc(p,4);
+ end;
+end;
+
+procedure ReadProp(var p:pbyte;var prop:pWideChar);
+var
+ len:integer;
+ ltmp:PAnsiChar;
+ c:byte;
+begin
+ len:=pdword(p)^;
+ inc(p,4);
+ len:=BSwap(len);
+ if len>0 then
+ inc(p,4); // 'data'
+ inc(p,4); // type?
+ inc(p,4); // encoding?
+ dec(len,8+8);
+ ltmp:=pointer(p);
+ inc(p,len);
+ c:=p^;
+ p^:=0;
+ UTF8ToWide(ltmp,prop);
+ p^:=c;
+end;
+
+function ReadM4A(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ atom:mp4Atom;
+ cursize,parentsize:integer;
+ par,buf,p,pn,finish:pbyte;
+ size:integer;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ cursize:=0;
+ parentsize:=FileSize(f);
+ repeat
+ ReadAtom(f,atom);
+ if atom.name=at_moov then
+ begin
+ mGetMem(buf,atom.len);
+ BlockRead(f,buf^,atom.len);
+ p:=buf;
+ finish:=pByte(PAnsiChar(p)+atom.len-SizeOf(atom));
+ repeat
+ GetAtom(p,atom);
+ pn:=PByte(PAnsiChar(p)+atom.len-SizeOf(atom));
+
+ if atom.name=at_cmov then
+ begin
+ size:=SetTree(atom,p,'cmvd',par);
+ if size>0 then
+ begin
+ ZDecompressBuf(PAnsiChar(p)+4,size-SizeOf(mp4Atom),
+ pointer(pn),size,BSwap(pdword(p)^));
+ mFreeMem(buf);
+ buf:=pn;
+ p:=buf;
+ GetAtom(p,atom); //must be 'moov'
+ finish:=PByte(PAnsiChar(p)+atom.len-SizeOf(atom));
+ continue;
+ end;
+ end;
+
+ if atom.name=at_mvhd then
+ begin
+ if pmvhd(p)^.TimeScale<>0 then
+ Info.total:=BSwap(pmvhd(p)^.Duration) div BSwap(pmvhd(p)^.TimeScale);
+ end;
+ if atom.name=at_udta then
+ begin
+ size:=SetTree(atom,p,'meta.ilst',par);
+ if size>0 then
+ begin
+ cursize:=0;
+ repeat
+ GetAtom(p,atom);
+ if atom.name=atm_nam then ReadProp(p,Info.title)
+ else if atom.name=atm_ART then ReadProp(p,Info.artist)
+// else if atom.name=atm_wrt then ReadProp(p,Info.title)
+ else if atom.name=atm_alb then ReadProp(p,Info.album)
+ else if atom.name=atm_day then ReadProp(p,Info.year)
+ else if atom.name=atm_cmt then ReadProp(p,Info.comment)
+// else if atom.name=atm_gen then ReadProp(p,Info.genre)
+ else if atom.name=atm_gnre then Info.genre:=GenreName(ReadInt(p)-1)
+ else if atom.name=atm_trkn then Info.track:=ReadInt(p)
+ else
+ inc(p,atom.len-SizeOf(mp4Atom));
+ inc(cursize,atom.len);
+ until cursize>=size;
+ end;
+ end;
+ // video properties
+ if atom.name=at_trak then
+ begin
+ if SetTree(atom,p,'mdia.minf.vmhd',par)>0 then
+ begin
+ p:=par;
+ if SetTree(atom,p,'stbl.stsd',par)>0 then
+ begin
+ Info.width :=swap(pstsd(p)^.Width);
+ Info.height:=swap(pstsd(p)^.Height);
+ Info.codec :=pstsd(p)^.DataFormat;
+ end;
+ end
+ // audio props
+ else if SetTree(atom,p,'mdia.minf.smhd',par)>0 then
+ begin
+ p:=par;
+ if SetTree(atom,p,'stbl.stsd',par)>0 then
+ begin
+ Info.khz:=(BSwap(pastsd(p)^.Samplerate) shr 16) div 1000;
+ Info.channels:=swap(pastsd(p)^.ChannelCount);
+ end;
+ p:=par;
+ if SetTree(atom,p,'stsz',par)>0 then
+ begin
+ if pdword(PAnsiChar(p)+4)^=0 then
+ Info.vbr:=1;
+ end;
+ end;
+ end;
+ p:=pn;
+ until PAnsiChar(p)>=PAnsiChar(finish);
+ mFreeMem(buf);
+ break;
+ end
+ else
+ Skip(f,atom.len-SizeOf(mp4Atom));
+ inc(cursize,atom.len);
+ until cursize>=parentsize;
+ CloseHandle(f);
+end;
+
+var
+ LocalFormatLinkM4A,
+ LocalFormatLinkMP4,
+ LocalFormatLinkMOV,
+ LocalFormatLink3GP:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLinkM4A.Next:=FormatLink;
+
+ LocalFormatLinkM4A.This.proc :=@ReadM4A;
+ LocalFormatLinkM4A.This.ext :='M4A';
+ LocalFormatLinkM4A.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkM4A;
+
+ LocalFormatLinkMP4.Next:=FormatLink;
+
+ LocalFormatLinkMP4.This.proc :=@ReadM4A;
+ LocalFormatLinkMP4.This.ext :='MP4';
+ LocalFormatLinkMP4.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkMP4;
+
+ LocalFormatLinkMOV.Next:=FormatLink;
+
+ LocalFormatLinkMOV.This.proc :=@ReadM4A;
+ LocalFormatLinkMOV.This.ext :='MOV';
+ LocalFormatLinkMOV.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkMOV;
+
+ LocalFormatLink3GP.Next:=FormatLink;
+
+ LocalFormatLink3GP.This.proc :=@ReadM4A;
+ LocalFormatLink3GP.This.ext :='3GP';
+ LocalFormatLink3GP.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLink3GP;
+
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_mkv.pas b/plugins/Watrack/formats/fmt_mkv.pas
new file mode 100644
index 0000000000..df01a82f59
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_mkv.pas
@@ -0,0 +1,235 @@
+{MKV file process}
+unit fmt_MKV;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadMKV(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,srv_format;
+
+const
+ idEBML = $A45DFA3;
+ idSegment = $8538067;
+ idInfo = $549A966;
+ idTimecodeScale = $AD7B1;
+ idDuration = $489;
+ idTracks = $654AE6B;
+ idTrackEntry = $2E;
+ idTrackType = $3;
+ idCodecPrivate = $23A2;
+ idName = $136E;
+ idVideo = $60;
+ idAudio = $61;
+ idPixelWidth = $30;
+ idPixelHeight = $3A;
+ idDefaultDuration = $3E383;
+ idSamplingFrequency = $35;
+ idChannels = $1F;
+ idCluster = $F43B675;
+
+function GetNumber(var ptr:pbyte):int64;
+begin
+ if (ptr^ and $80)<>0 then
+ result:=ptr^ and $7F
+ else if (ptr^ and $40)<>0 then
+ begin
+ result:=(ptr^ and $3F) shl 8; inc(ptr);
+ result:=result+ptr^;
+ end
+ else if (ptr^ and $20)<>0 then
+ begin
+ result:=(ptr^ and $1F) shl 16; inc(ptr);
+ result:=result+(ptr^ shl 8); inc(ptr);
+ result:=result+ptr^;
+ end
+ else if (ptr^ and $10)<>0 then
+ begin
+ result:=(ptr^ and $0F) shl 24; inc(ptr);
+ result:=result+(ptr^ shl 16); inc(ptr);
+ result:=result+(ptr^ shl 8); inc(ptr);
+ result:=result+ptr^;
+ end
+ else if (ptr^ and $08)<>0 then
+ begin
+ result:=int64(ptr^ and $07) shl 32; inc(ptr);
+ result:=result+(ptr^ shl 24); inc(ptr);
+ result:=result+(ptr^ shl 16); inc(ptr);
+ result:=result+(ptr^ shl 8); inc(ptr);
+ result:=result+ptr^;
+ end
+ else if (ptr^ and $04)<>0 then
+ begin
+ result:=int64(ptr^ and $03) shl 40; inc(ptr);
+ result:=result+(int64(ptr^) shl 32); inc(ptr);
+ result:=result+(ptr^ shl 24); inc(ptr);
+ result:=result+(ptr^ shl 16); inc(ptr);
+ result:=result+(ptr^ shl 8); inc(ptr);
+ result:=result+ptr^;
+ end
+ else if (ptr^ and $02)<>0 then
+ begin
+ result:=int64(ptr^ and $01) shl 48; inc(ptr);
+ result:=result+(int64(ptr^) shl 40); inc(ptr);
+ result:=result+(int64(ptr^) shl 32); inc(ptr);
+ result:=result+(ptr^ shl 24); inc(ptr);
+ result:=result+(ptr^ shl 16); inc(ptr);
+ result:=result+(ptr^ shl 8); inc(ptr);
+ result:=result+ptr^;
+ end
+ else if (ptr^ and $01)<>0 then
+ begin
+ inc(ptr);
+ result:= (int64(ptr^) shl 48); inc(ptr);
+ result:=result+(int64(ptr^) shl 40); inc(ptr);
+ result:=result+(int64(ptr^) shl 32); inc(ptr);
+ result:=result+(ptr^ shl 24); inc(ptr);
+ result:=result+(ptr^ shl 16); inc(ptr);
+ result:=result+(ptr^ shl 8); inc(ptr);
+ result:=result+ptr^;
+ end
+ else
+ result:=0;
+ inc(ptr);
+end;
+
+function GetInt(var ptr:pbyte;len:integer):int64;
+var
+ i:integer;
+begin
+ result:=0;
+ for i:=0 to len-1 do
+ begin
+ result:=(result shl 8)+ptr^;
+ inc(ptr);
+ end;
+end;
+
+function GetFloat(var ptr:pbyte):single;
+var
+ i:dword;
+ f:single absolute i;
+begin
+ i:=( ptr^ shl 24); inc(ptr);
+ inc(i,ptr^ shl 16); inc(ptr);
+ inc(i,ptr^ shl 8); inc(ptr);
+ inc(i,ptr^); inc(ptr);
+ result:=f;
+end;
+
+function ReadMKV(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ id,len:integer;
+ ptr:pByte;
+ buf:array [0..16383] of byte;
+ trktype,scale:integer;
+ ls:PAnsiChar;
+ tmp:integer;
+ lTotal:real;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ BlockRead(f,buf,SizeOf(buf));
+ ptr:=@buf;
+ trktype:=0;
+ lTotal:=0;
+ scale:=1;
+ repeat
+ id :=GetNumber(ptr);
+ len:=GetNumber(ptr);
+ if id=idEBML then // just check
+ begin
+ result:=true;
+ inc(ptr,len);
+ end
+ else if id=idCluster then
+ break
+ else if id=idSegment then // do nothing
+ else if id=idInfo then // do nothing
+ else if id=idTracks then // do nothing
+ else if id=idTrackEntry then // do nothing
+ else if id=idVideo then // do nothing
+ else if id=idAudio then // do nothing
+ else if id=idTimecodeScale then
+ scale:=GetInt(ptr,len)
+ else if id=idDuration then
+ lTotal:=GetFloat(ptr)
+ else if id=idTrackType then
+ begin
+ tmp:=trktype;
+ trktype:=GetInt(ptr,len); // 1-video,2-audio
+ if (tmp=2) and (trktype=2) then
+ break;
+ end
+ else if (id=idCodecPrivate) and (trktype=1) then
+ begin
+ inc(ptr,16);
+ // 4 - ? (40=size included?)
+ // width,height
+ // 2 - ?
+ // 2 - bitperpixel?
+ Info.codec:=ptr^; inc(ptr);
+ Info.codec:=Info.codec+(ptr^ shl 8 ); inc(ptr);
+ Info.codec:=Info.codec+(ptr^ shl 16); inc(ptr);
+ Info.codec:=Info.codec+(ptr^ shl 24);
+ inc(ptr,len-19);
+ end
+ else if (id=idName) and (Info.title=NIL) then
+ begin
+ mGetMem(ls,len+1);
+ move(ptr^,ls^,len);
+ ls[len]:=#0;
+ AnsiToWide(ls,Info.title);
+ mFreeMem(ls);
+ inc(ptr,len);
+ end
+ else if id=idPixelWidth then
+ Info.width:=GetInt(ptr,len)
+ else if id=idPixelHeight then
+ Info.height:=GetInt(ptr,len)
+ else if id=idDefaultDuration then
+ begin
+ if trktype=1 then
+ begin
+ Info.fps:=(GetInt(ptr,len) div 1000);
+ if Info.fps<>0 then
+ Info.fps:=100000000 div Info.fps;
+ end
+ else
+ begin
+ GetInt(ptr,len);
+ end;
+ end
+ else if id=idSamplingFrequency then
+ Info.khz:=round(GetFloat(ptr)) div 1000
+ else if id=idChannels then
+ Info.channels:=GetInt(ptr,len)
+ else
+ inc(ptr,len);
+ until pAnsiChar(ptr)>=(PAnsiChar(@buf)+SizeOf(buf));
+ Info.total:=trunc(lTotal/(1000000000/scale));
+ CloseHandle(f);
+end;
+
+var
+ LocalFormatLink:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLink.Next:=FormatLink;
+
+ LocalFormatLink.This.proc :=@ReadMKV;
+ LocalFormatLink.This.ext :='MKV';
+ LocalFormatLink.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLink;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_mp3.pas b/plugins/Watrack/formats/fmt_mp3.pas
new file mode 100644
index 0000000000..5d6c94745a
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_mp3.pas
@@ -0,0 +1,460 @@
+{MP3 file process}
+unit fmt_MP3;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadMP3(var Info:tSongInfo):boolean; cdecl;
+function ReadMPG(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,tags,srv_format;
+
+const
+ ScanSize = 16*1024; // block size to search header
+type
+ tMP3FrameHdr = record
+ Version :integer;
+ Layer :cardinal;
+ Bitrate :cardinal;
+ Samplerate:cardinal;
+ Channel :cardinal; //Stereo, Joint, Dual, Mono
+ Length :cardinal;
+ CRC :boolean;
+ _Private :boolean;
+ Copyright :boolean;
+ Original :boolean;
+ isVBR :boolean;
+ end;
+
+// ........ ........ 111..... 11111111 syncword
+// ........ ........ ...xx... ........ version (11=1, 10=2, 00=2.5)
+// ........ ........ .....xx. ........ layer (01=III, 10=II, 11=I)
+// ........ ........ .......x ........ crc (0=yes, 1=no)
+// xx...... ........ ........ ........ mode (00=stereo, 10=joint, 01=dual, 11=mono)
+// ..xx.... ........ ........ ........ mode ext (only for joint stereo)
+// ....x... ........ ........ ........ copyright (0=no, 1=yes)
+// .....x.. ........ ........ ........ original (0=orig, 1=copy)
+// ......xx ........ ........ ........ emphasis (not 10)
+// ........ xxxx.... ........ ........ bitrate (not 0000 nor 1111)
+// ........ ....xx.. ........ ........ sampling rate (not 11)
+// ........ ......x. ........ ........ padded (0=no, 1=yes)
+// ........ .......x ........ ........ private bit
+
+const
+ btable:array [0..1,0..2,0..15] of word = (
+ ( //MPEG 2 & 2.5
+ (0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,0), //Layer III
+ (0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,0), //Layer II
+ (0, 32, 48, 56, 64, 80, 96,112,128,144,160,176,192,224,256,0) //Layer I
+ ),( //MPEG 1
+ (0, 32, 40, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,0), //Layer III
+ (0, 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384,0), //Layer II
+ (0, 32, 64, 96,128,160,192,224,256,288,320,352,384,416,448,0) //Layer I
+ )
+ );
+ stable: array [0..3,0..2] of word = (
+ (32000, 16000, 8000), //MPEG 2.5
+ ( 0, 0, 0), //reserved
+ (22050, 24000, 16000), //MPEG 2
+ (44100, 48000, 32000) //MPEG 1
+ );
+
+procedure TranslateFrameHdr(const block:array of byte;var MP3FrameHdr:tMP3FrameHdr);
+begin
+ FillChar(MP3FrameHdr,SizeOf(MP3FrameHdr),0);
+ if block[0]=$FF then
+ begin
+ with MP3FrameHdr do
+ begin
+ Version :=(block[1] and $18) shr 3;
+ Layer :=(block[1] and $06) shr 1;
+ CRC :=not Odd(block[1]);
+ Bitrate :=btable[Version and 1][Layer-1][block[2] shr 4];
+ Samplerate:=stable[Version][(block[2] and $0C) shr 2];
+ _Private :=odd(block[2]);
+ Channel :=block[3] shr 6;
+ Copyright :=((block[3] and $08) shr 3)<>0;
+ Original :=((block[3] and $04) shr 2)<>0;
+ end;
+ end;
+end;
+
+procedure CheckVBR(f:THANDLE; var hdr:tMP3FrameHdr);
+var
+ pos,apos:cardinal;
+ sign:longint;
+ frames:longint;
+begin
+ pos:=FilePos(f);
+ hdr.Length:=0;
+ if hdr.Version=3 then
+ begin
+ if hdr.Channel=3 then
+ apos:=17
+ else
+ apos:=32;
+ end
+ else if hdr.Channel=3 then
+ apos:=9
+ else
+ apos:=17;
+ Skip(f,apos);
+ BlockRead(f,sign,4);
+ hdr.isVBR:=sign=$676E6958; //Xing
+//calculate length
+ if hdr.isVBR then
+ begin
+ if hdr.Samplerate<>0 then
+ begin
+// Seek(f,pos+36);
+ BlockRead(f,sign,4);
+ if (sign and $01000000)<>0 then
+ begin
+ BlockRead(f,frames,4);
+ frames:=BSwap(frames);
+ hdr.Length:=Round((1152/hdr.Samplerate)*frames/(4-hdr.Version)); //!
+ end;
+ end;
+ end
+ else if hdr.Bitrate<>0 then
+ hdr.Length:=((8*(FileSize(f)-(pos-4))) div 1000) div hdr.Bitrate;
+end;
+
+function SearchStart(f:THANDLE; var l:array of byte):Boolean;
+var
+ CurPos:longint;
+ Buf:array [0..ScanSize] of byte;
+ i,j:integer;
+begin
+ CurPos:=FilePos(f)-4;
+ Seek(f,CurPos);
+ j:=BlockRead(f,Buf,ScanSize);
+ i:=0;
+ while i<j do
+ begin
+ if (i<(j-2)) and (Buf[i]=$FF) and //FF FB E4
+ ((Buf[i+1] and $E0)=$E0) and
+ ((Buf[i+2] and $F0)<>$F0) then
+ begin
+ Seek(f,CurPos+i);
+ BlockRead(f,l,4);
+ result:=true;
+ Exit;
+ end;
+ inc(i);
+ end;
+ result:=false;
+end;
+
+function ReadMP3(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ l:array [0..3] of byte;
+ hdr:tMP3FrameHdr;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ ReadID3v2(f,Info);
+ BlockRead(f,l,4);
+// if l[0]<>$FF then
+ if not SearchStart(f,l) then
+ Exit;
+ TranslateFrameHdr(l,hdr);
+ CheckVBR(f,hdr);
+ Info.kbps :=hdr.Bitrate;
+ Info.khz :=hdr.Samplerate div 1000;
+ Info.total:=hdr.Length;
+ if hdr.Channel=3 then
+ Info.channels:=1
+ else
+ Info.channels:=2;
+ Info.vbr:=ord(hdr.isVBR);
+
+ ReadAPEv2(f,Info);
+ ReadID3v1(f,Info);
+ CloseHandle(f);
+ result:=true;
+end;
+
+const
+ mpgAudio = 1;
+ mpgVideo = 2;
+ mpgVersion = 4;
+
+type
+ l2b=array [0..3] of byte;
+
+function ReadDWord(var p:pAnsiChar;endptr:pAnsiChar):integer;
+begin
+ if (p+4)<endptr then
+ begin
+ result:=pDWord(p)^;
+ inc(p,4);
+ end
+ else
+ result:=-1;
+end;
+
+function ReadWord(var p:pAnsiChar;endptr:pAnsiChar):integer;
+begin
+ if (p+2)<endptr then
+ begin
+ result:=pWord(p)^;
+ inc(p,2);
+ end
+ else
+ result:=-1;
+end;
+
+function ReadByte(var p:pAnsiChar;endptr:pAnsiChar):integer;
+begin
+ if p<endptr then
+ begin
+ result:=pByte(p)^;
+ inc(p);
+ end
+ else
+ result:=-1;
+end;
+
+function ChunkRead(var p:pAnsiChar;endptr:pAnsiChar):integer;
+var
+ i:integer;
+begin
+ repeat
+ if ReadByte(p,endptr)=0 then
+ if ReadByte(p,endptr)=0 then
+ begin
+ repeat
+ i:=ReadByte(p,endptr);
+ until i<>0;
+ if i=1 then
+ begin
+ result:=ReadByte(p,endptr) or $100;
+ exit;
+ end;
+ end;
+ until p>=endptr;
+ result:=0;
+end;
+
+const
+ BufSize = 256*1024;
+
+function ReadMPG(var Info:tSongInfo):boolean; cdecl;
+var
+ endptr,buf,p:PAnsiChar;
+ f:THANDLE;
+ BlockType:integer;
+ l:dword;
+ w:word;
+ b:byte;
+ flag:integer;
+ version,layer:integer;
+// vbitrate:integer;
+// FrmCnt:integer;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ flag:=mpgAudio+mpgVideo+mpgVersion;
+
+ mGetMem(buf,BufSize);
+ endptr:=buf+BlockRead(f,buf^,BufSize);
+ CloseHandle(f);
+ p:=buf;
+// FrmCnt:=0;
+ while (flag<>0) and (p<endptr) do
+ begin
+ BlockType:=ChunkRead(p,endptr);
+ case BlockType of
+ $1BA: begin // pack
+ if (flag and mpgVersion)<>0 then
+ begin
+ flag:=flag and not mpgVersion;
+ if (ReadByte(p,endptr) and $C0)=$40 then
+ Info.codec:=$3247504D // MPG2
+ else
+ Info.codec:=$4745504D; // MPEG
+ end;
+ end;
+ $1B3: begin // Video
+ if (flag and mpgVideo)<>0 then
+ begin
+ l:=ReadDWord(p,endptr);
+ flag:=flag and not mpgVideo;
+ Info.width :=((l2b(l)[1] and $F0) shr 4)+(l2b(l)[0] shl 4);
+ Info.height:=((l2b(l)[1] and $0F) shl 8)+l2b(l)[2];
+ case l2b(l)[3] and $F of
+ 1: Info.fps:=2397;
+ 2: Info.fps:=2400;
+ 3: Info.fps:=2500;
+ 4: Info.fps:=2997;
+ 5: Info.fps:=3000;
+ 6: Info.fps:=5000;
+ 7: Info.fps:=5994;
+ 8: Info.fps:=6000;
+ end;
+// BlockRead(f,l,4);
+// vbitrate:=(l2b(l)[0] shl 10)+(l2b(l)[1] shl 2)+(l2b(l)[2] shr 6);
+ end;
+ end;
+ 0,$1B7,$1B9: break;
+{
+ $1E0: begin
+ BlockRead(f,w,2);
+ w:=swap(w);
+ mGetMem(buf,w);
+ BlockRead(f,buf^,w);
+ p:=buf;
+ for l:=0 to w-4 do
+ begin
+ if pdword(p)^=$00010000 then
+ begin
+ inc(FrmCnt);
+ inc(p,4);
+ end
+ else
+ inc(p);
+ end;
+ mFreeMem(buf);
+ end;
+}
+ $1C0: begin // audio
+ w:=swap(ReadWord(p,endptr));
+ if flag and mpgAudio<>0 then
+ begin
+ flag:=flag and not mpgAudio;
+ b:=ReadByte(p,endptr);
+ dec(w);
+ if (b and $C0)=$80 then
+ begin
+ b:=ReadByte(p,endptr);
+ l:=ReadByte(p,endptr);
+ dec(w,2);
+ if (b and $80)<>0 then
+ begin
+ inc(p,5);
+ dec(w,5);
+ dec(l,5);
+ if (b and $40)<>0 then
+ begin
+ inc(p,5);
+ dec(w,5);
+ dec(l,5);
+ end;
+ end;
+ if l>0 then
+ begin
+ inc(p,l);
+ dec(w,l);
+ end;
+ end
+ else
+ begin
+ while (b and $80)<>0 do
+ begin
+ dec(w);
+ if w=0 then break;
+ b:=ReadByte(p,endptr);
+ end;
+ if (b and $40)<>0 then
+ begin
+ inc(p);
+ b:=ReadByte(p,endptr);
+ dec(w,2);
+ end;
+ if (b and $20)<>0 then
+ begin
+ inc(p,4);
+ dec(w,4);
+ if (b and $10)<>0 then
+ begin
+ inc(p,5);
+ dec(w,5);
+ end;
+ end;
+ end;
+ l:=ReadDWord(p,endptr);
+ version:=(l2b(l)[1] and $18) shr 3;
+ layer :=(l2b(l)[1] and $06) shr 1;
+ Info.kbps :=btable[version and 1][layer-1][l2b(l)[2] shr 4];
+ Info.khz :=(stable[version][(l2b(l)[2] and $0C) shr 2]) div 1000;
+ Info.channels:=l2b(l)[3] shr 6;
+ if Info.channels=3 then
+ Info.channels:=1
+ else
+ Info.channels:=2;
+// if w>0 then inc(p,w);
+ end;
+// else
+ inc(p,w);
+ end;
+{
+ $1B5: begin
+ BlockRead(f,l,4);
+ if (l2b(l)[0] and $F0)=$10 then
+ begin
+ vbitrate:=vbitrate+
+ ((((l2b(l)[2] and $1F) shl 7)+(l2b(l)[3] shr 1)) shl 18);
+ end;
+ end;
+}
+{
+ $1BD: begin
+ end;
+}
+ $1C1..$1DF, // audio
+//?? $1E0,
+ $1E1..$1EF, // video
+ $1BB{,$1BD},$1BE,$1BF: begin // system,private,padding,private
+ inc(p,swap(ReadWord(p,endptr)));
+ end;
+ end;
+ end;
+// vbitrate:=(vbitrate*400) div 1000;
+// Info.total:=(FrmCnt*100) div Info.fps;
+ mFreeMem(buf);
+ result:=true;
+end;
+
+var
+ LocalFormatLinkMP3,
+ LocalFormatLinkMPG,
+ LocalFormatLinkMPEG:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLinkMP3.Next:=FormatLink;
+
+ LocalFormatLinkMP3.This.proc :=@ReadMP3;
+ LocalFormatLinkMP3.This.ext :='MP3';
+ LocalFormatLinkMP3.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkMP3;
+
+ LocalFormatLinkMPG.Next:=FormatLink;
+
+ LocalFormatLinkMPG.This.proc :=@ReadMPG;
+ LocalFormatLinkMPG.This.ext :='MPG';
+ LocalFormatLinkMPG.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkMPG;
+
+ LocalFormatLinkMPEG.Next:=FormatLink;
+
+ LocalFormatLinkMPEG.This.proc :=@ReadMPG;
+ LocalFormatLinkMPEG.This.ext :='MPEG';
+ LocalFormatLinkMPEG.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkMPEG;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_mpc.pas b/plugins/Watrack/formats/fmt_mpc.pas
new file mode 100644
index 0000000000..7d8671dde6
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_mpc.pas
@@ -0,0 +1,90 @@
+{MPC file format}
+unit fmt_MPC;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadMPC(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,tags,srv_format;
+
+const
+ DefID = $002B504D;// 'MP+'
+
+function ReadMPC(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ tmp:array [0..5] of dword;
+ samples,TotalFrames:dword;
+ lastframe:dword;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ ReadID3v2(f,Info);
+
+ BlockRead(f,tmp,SizeOf(tmp));
+ if ((tmp[0] and $FFFFFF)=DefID) and
+ (((tmp[0] shr 24) and $0F)>=7) then // sv7-sv8
+ begin
+ Info.kbps:=0;
+ if (tmp[2] and 2)<>0 then
+ Info.channels:=2
+ else
+ Info.channels:=1;
+ case (tmp[2] and $3000) shr 12 of //C000-14?
+ 00: Info.khz:=44100;
+ 01: Info.khz:=48000;
+ 02: Info.khz:=37800;
+ 03: Info.khz:=32000;
+ end;
+ lastframe:=(tmp[5] and $FFF) shr 1;
+ samples:=tmp[1]*1152+lastframe;
+ end
+ else
+ begin //4-6
+ if not ((tmp[0] and $1FFF) and $3FF) in [4..6] then
+ exit;
+ Info.khz:=44100;
+ Info.kbps:=tmp[1] and $1F;
+ if ((tmp[0] and $1FFF) and $3FF)=4 then
+ TotalFrames:=loword(tmp[2])
+ else
+ TotalFrames:=tmp[2];
+ samples:=TotalFrames*1152;
+ end;
+
+ if Info.khz<>0 then
+ Info.total:=samples div Info.khz;
+ Info.khz:=Info.khz div 1000;
+ if (Info.kbps=0) and (samples<>0) then
+// if fs=samples*channels*deep/8 then kbps=khz*deep*channels/1152
+// Info.kbps:=(Info.khz*8)*taginfo.FileSize/1152/samples;
+
+ Info.kbps:=(Info.khz div 8)*FileSize(f) div samples; //!!
+ ReadAPEv2(f,Info);
+ ReadID3v1(f,Info);
+ CloseHandle(f);
+ result:=true;
+end;
+
+var
+ LocalFormatLink:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLink.Next:=FormatLink;
+
+ LocalFormatLink.This.proc :=@ReadMPC;
+ LocalFormatLink.This.ext :='MPC';
+ LocalFormatLink.This.flags:=0;
+
+ FormatLink:=@LocalFormatLink;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_ofr.pas b/plugins/Watrack/formats/fmt_ofr.pas
new file mode 100644
index 0000000000..73d58b68ff
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_ofr.pas
@@ -0,0 +1,74 @@
+{OFR file}
+unit fmt_OFR;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadOFR(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,tags,srv_format;
+
+type
+ tMain = packed record
+ ID :dword; // 'OFR '
+ Size :dword; //15
+ SamplesLo :dword;
+ SamplesHi :word;
+ SampleType :byte;
+ ChannelsMap:byte;
+ Samplerate :dword;
+ Encoder :word;
+ Compression:byte;
+ end;
+
+function ReadOFR(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ Hdr:tMain;
+ Samples:int64;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ ReadID3v2(f,Info);
+ BlockRead(f,Hdr,SizeOf(Hdr));
+ Samples:=Hdr.SamplesLo+Hdr.SamplesHi*$10000;
+ Info.channels:=Hdr.ChannelsMap+1;
+ Info.khz :=Hdr.Samplerate div 1000;
+ Info.total :=(Samples div Info.channels)*Info.khz;
+
+ ReadAPEv2(f,Info);
+ ReadID3v1(f,Info);
+ CloseHandle(f);
+ result:=true;
+end;
+
+var
+ LocalFormatLinkOFR,
+ LocalFormatLinkOFS:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLinkOFR.Next:=FormatLink;
+
+ LocalFormatLinkOFR.This.proc :=@ReadOFR;
+ LocalFormatLinkOFR.This.ext :='OFR';
+ LocalFormatLinkOFR.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkOFR;
+
+ LocalFormatLinkOFS.Next:=FormatLink;
+
+ LocalFormatLinkOFS.This.proc :=@ReadOFR;
+ LocalFormatLinkOFS.This.ext :='OFS';
+ LocalFormatLinkOFS.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkOFS;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_ogg.pas b/plugins/Watrack/formats/fmt_ogg.pas
new file mode 100644
index 0000000000..4b05b80c2c
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_ogg.pas
@@ -0,0 +1,522 @@
+{OGG, SPX and FLAC file formats}
+unit fmt_OGG;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadOGG(var Info:tSongInfo):boolean; cdecl;
+function ReadSPX(var Info:tSongInfo):boolean; cdecl;
+function ReadfLaC(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,tags,srv_format,base64,utils;
+
+const
+ OGGSign = $5367674F; //OggS
+const
+ SpeexID = 'Speex ';
+type
+ tSPEXHeader = packed record
+ speex_string :array [0..7] of AnsiChar;
+ speex_version :array [0..19] of AnsiChar;
+ speex_version_id:dword;
+ header_size :dword; //sizeof(tSPEXHeader)
+ rate :dword;
+ mode :dword;
+ bitstrm_version :dword;
+ nb_channels :dword;
+ bitrate :dword;
+ frame_size :dword;
+ vbr :dword;
+ fpp :dword; //frames_per_packet
+ extra_headers :dword;
+ reserved1 :dword;
+ reserved2 :dword;
+ end;
+type
+ pOGGHdr = ^tOGGHdr;
+ tOGGHdr = packed record
+ ID :dword;
+ Version :byte;
+ HdrType :byte;
+ Granule :Int64; // absolute position
+ BitStrmSN:dword;
+ PageSeqN :dword;
+ CRC :dword;
+ PageSegs :byte;
+ end;
+const
+ strmOGG = 1;
+ strmOGM = 2;
+const
+ VideoD = $65646976;
+ VideoW = $006F;
+ VorbisD = $62726F76;
+ VorbisW = $7369;
+type
+ tOGMInfo = packed record
+ padding :word; // 0
+ codec :dword;
+ size :dword;
+ time_unit :int64; // 1/10000000 sec
+ samples_per_unit:int64; // fps = 10000000*spu/time_unit
+ default_len :dword; // 1
+ buffersize :dword;
+ bit_per_sample :dword;
+ width :dword;
+ height :dword;
+ dummy :dword; // 0
+ end;
+
+//const VorbisStream:array [0..5] of byte = ($76,$6F,$72,$62,$69,$73); // 'vorbis'
+
+type
+ tOGGInfo = packed record
+ version :dword;
+ Channels :byte;
+ samplerate:dword;
+ maxkbps :dword;
+ nominal :dword;
+ minkbps :dword;
+ BlockSizes:byte;
+ dummy :byte;
+ end;
+
+//--------------- fLaC section ---------------
+const
+ fLaCSign = $43614C66; //fLaC
+{
+0 : STREAMINFO
+1 : PADDING
+2 : APPLICATION
+3 : SEEKTABLE
+4 : VORBIS_COMMENT
+5 : CUESHEET
+}
+type
+ MetaHdr = packed record
+ blocktype:byte;
+ blocklen:array [0..2] of byte;
+ end;
+type
+ StreamInfo = packed record
+ MinBlockSize:word;
+ MaxBlocksize:word;
+ MinFrameSize:array [0..2] of byte;
+ MaxFrameSize:array [0..2] of byte;
+ heap:array [0..7] of byte;
+ MD5:array [0..15] of byte;
+ end;
+
+procedure OGGGetComment(ptr:PAnsiChar;size:integer;var Info:tSongInfo);
+var
+ clen,alen,len,values:dword;
+ ls:PAnsiChar;
+ value:PAnsiChar;
+ cover:pByte;
+ ext:dword;
+ extw:int64;
+ c:AnsiChar;
+begin
+ inc(ptr,pdword(ptr)^+4); //vendor
+ values:=pdword(ptr)^; inc(ptr,4);
+ ext:=0;
+ cover:=nil;
+ clen:=0;
+ while values>0 do
+ begin
+ len:=pdword(ptr)^;
+ if len>cardinal(size) then
+ break;
+ dec(size,len);
+ inc(ptr,4);
+ ls:=ptr;
+ c:=ls[len];
+ ls[len]:=#0;
+ alen:=StrScan(ls,'=')-ls+1;
+ if alen>0 then
+ begin
+ ls[alen-1]:=#0;
+ value:=ls+alen;
+
+ if (Info.title =nil) and (lstrcmpia(ls,'TITLE' )=0) then UTF8ToWide(value,Info.title)
+ else if (Info.artist =nil) and (lstrcmpia(ls,'ARTIST' )=0) then UTF8ToWide(value,Info.artist)
+ else if (Info.album =nil) and (lstrcmpia(ls,'ALBUM' )=0) then UTF8ToWide(value,Info.album)
+ else if (Info.genre =nil) and (lstrcmpia(ls,'GENRE' )=0) then UTF8ToWide(value,Info.genre)
+ else if (Info.year =nil) and (lstrcmpia(ls,'DATE' )=0) then UTF8ToWide(value,Info.year)
+ else if (Info.comment=nil) and (lstrcmpia(ls,'COMMENT')=0) then UTF8ToWide(value,Info.comment)
+ else if (Info.lyric =nil) and (lstrcmpia(ls,'LYRICS' )=0) then UTF8ToWide(value,Info.lyric)
+
+ else if (Info.track=0) and (lstrcmpia(ls,'TRACKNUMBER')=0) then Info.track:=StrToInt(value)
+
+ else if (cover=nil) and (lstrcmpia(ls,'COVERART')=0) then clen:=Base64Decode(value,cover)
+ else if lstrcmpia(ls,'COVERARTMIME')=0 then ext:=GetImageType(nil,value);
+ end;
+ dec(values);
+ inc(ptr,len);
+ ptr^:=c;
+ end;
+
+ if cover<>nil then
+ begin
+ if ext=0 then
+ ext:=GetImageType(cover);
+ if ext<>0 then
+ begin
+ FastAnsiToWideBuf(PAnsiChar(@ext),pWideChar(@extw));
+ Info.cover:=SaveTemporaryW(cover,clen,PWideChar(@extw));
+ end;
+ mFreeMem(cover);
+ end;
+
+end;
+
+function CalcSize(num:integer;var arr:array of byte):integer;
+var
+ i:integer;
+begin
+ result:=0;
+ for i:=0 to num-1 do
+ begin
+ inc(result,arr[i]);
+ if arr[i]<$FF then break;
+ end;
+end;
+
+function ReadSPX(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ OGGHdr:tOGGHdr;
+ SPXHdr:tSPEXHeader;
+ buf:array [0..255] of byte;
+ ptr:PAnsiChar;
+ size:integer;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ BlockRead(f,OGGHdr,SizeOf(tOGGHdr));
+ Skip(f,OGGHdr.PageSegs);
+ if OGGHdr.ID=OGGSign then
+ begin
+ BlockRead(f,SPXHdr,SizeOf(SPXHdr));
+ if SPXHdr.speex_string<>SpeexID then
+ begin
+ CloseHandle(f);
+ exit;
+ end;
+ Info.khz:=SPXHdr.rate div 1000;
+ Info.vbr:=SPXHdr.vbr;
+ if integer(SPXHdr.bitrate)<>-1 then
+ Info.kbps:=SPXHdr.bitrate div 1000;
+
+ BlockRead(f,OGGHdr,SizeOf(tOGGHdr));
+ BlockRead(f,buf,OGGHdr.PageSegs);
+ size:=CalcSize(OGGHdr.PageSegs,buf);
+ GetMem(ptr,size+1);
+ BlockRead(f,ptr^,size);
+ OGGGetComment(ptr,size,Info);
+ FreeMem(ptr);
+
+ result:=true;
+ end;
+ CloseHandle(f);
+end;
+
+function Compare(const sign:array of byte):integer;
+type
+ conv=packed record
+ d:dword;w:word;
+ end;
+var
+ p:^conv;
+begin
+ p:=@sign;
+ if (p^.d=VideoD) and (p^.w=VideoW) then
+ result:=strmOGM
+ else if (p^.d=VorbisD) and (p^.w=VorbisW) then
+ result:=strmOGG
+ else
+ result:=0;
+end;
+
+function ReadOGG(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ OGGHdr:tOGGHdr;
+ tmp:packed record
+ paktype:byte;
+ sign:array [0..5] of byte;
+ end;
+ OGGInfo:tOGGInfo;
+ OGMInfo:tOGMInfo;
+ fpos:dword;
+ SPXHdr:tSPEXHeader;
+ i,j:integer;
+ DataIndex:integer;
+ buf:array [0..255] of byte;
+ fsize:dword;
+ done:integer;
+ ptr:PAnsiChar;
+ size:integer;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ tmp.paktype:=0;
+ fsize:=FileSize(f);
+ done:=0;
+ while (done<>3) and (tmp.paktype<>5) and (FilePos(f)<fsize) do
+ begin
+ BlockRead(f,OGGHdr,SizeOf(tOGGHdr));
+ if OGGHdr.ID=OGGSign then
+ begin
+ BlockRead(f,buf,OGGHdr.PageSegs);
+ size:=CalcSize(OGGHdr.PageSegs,buf);
+// sum pages to size obtain and number of groups
+// for i:=0 to OGGHdr.PageSegs-1 do
+// only first fragment
+ begin
+ fpos:=FilePos(f);
+ BlockRead(f,tmp,SizeOf(tmp));
+ if tmp.paktype=5 then
+ break;
+ if tmp.paktype=1 then
+ begin
+ case Compare(tmp.sign) of
+ strmOGG: begin
+ BlockRead(f,OGGInfo,SizeOf(OGGInfo));
+ if integer(OGGInfo.nominal)>0 then
+ Info.kbps :=OGGInfo.nominal div 1000;
+ Info.khz :=OGGInfo.samplerate;
+ Info.channels:=OGGInfo.Channels;
+ done:=done or 1;
+ end;
+ strmOGM: begin
+ BlockRead(f,OGMInfo,SizeOf(OGMInfo));
+ Info.codec :=OGMInfo.codec;
+ Info.fps :=round(((10000000*OGMInfo.samples_per_unit) / OGMInfo.time_unit)*100);
+ Info.width :=OGMInfo.width;
+ Info.height:=OGMInfo.height;
+ done:=done or 1;
+ end;
+ end;
+ end
+ else if tmp.paktype=ORD('S') then //maybe SPX
+ begin
+ Seek(f,fpos);
+ BlockRead(f,SPXHdr,SizeOf(SPXHdr));
+ if SPXHdr.speex_string<>SpeexID then
+ begin
+ CloseHandle(f);
+ exit;
+ end;
+ Info.khz:=SPXHdr.rate div 1000;
+ if integer(SPXHdr.bitrate)<>-1 then
+ Info.kbps:=SPXHdr.bitrate div 1000;
+ done:=done or 1;
+ end
+ else if tmp.paktype=3 then
+ begin
+ GetMem(ptr,size+1);
+ BlockRead(f,ptr^,size);
+ OGGGetComment(ptr,size,Info);
+ FreeMem(ptr);
+ done:=done or 2;
+ end
+ else
+ continue;
+ result:=true;
+ end;
+ end;
+ end;
+ // try to get length
+ DataIndex:=FileSize(f)-10;
+ for i:=1 to 50 do
+ begin
+ dec(DataIndex,SizeOf(buf)-10);
+ Seek(f,DataIndex);
+ BlockRead(f,buf,SizeOf(buf));
+ { Get number of PCM samples from last Ogg packet header }
+ j:=SizeOf(buf)-10;
+ repeat
+ if pOGGHdr(@buf[j])^.ID=OGGSign then
+ begin
+ if j>(SizeOf(buf)-SizeOf(tOGGHdr)) then
+ begin
+ Seek(f,DataIndex+j);
+ BlockRead(f,buf,SizeOf(tOGGHdr));
+ j:=0;
+ end;
+ if Info.fps>0 then
+ begin
+ Info.total:=(pOGGHdr(@buf[j])^.Granule*100) div Info.fps;
+ end
+ else if Info.khz<>0 then
+ Info.total:=pOGGHdr(@buf[j])^.Granule div Info.khz;
+ break;
+ end;
+ dec(j);
+ until j=0;
+ if Info.total>0 then break;
+ end;
+ Info.khz:=Info.khz div 1000;
+ CloseHandle(f);
+end;
+
+function ReadfLaC(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ data64:int64;
+ hdr:MetaHdr;
+ frm:StreamInfo;
+ id:dword;
+ flag:integer;
+ size:dword;
+ buf,ptr:PAnsiChar;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ ReadID3v2(f,Info);
+ BlockRead(f,id,SizeOf(id));
+ if id=fLaCSign then
+ begin
+ flag:=0;
+ repeat
+ BlockRead(f,hdr,SizeOf(hdr));
+ size:=hdr.blocklen[2]+(hdr.blocklen[1] shl 8)+(hdr.blocklen[0] shl 16);
+ case (hdr.blocktype and $7F) of
+ 0: begin
+ if flag=0 then
+ begin
+ BlockRead(f,frm,SizeOf(frm));
+ //samplerate eg.44100
+ Info.khz:=((frm.heap[0] shl 12)+(frm.heap[1] shl 4)+(frm.heap[2] shr 4));
+ Info.channels:=((frm.heap[2] and $F) shr 1)+1;
+ //bits per SAMPLE now
+ Info.kbps:=(frm.heap[2] and 1) shl 4+(frm.heap[3] shr 4)+1;
+ data64:=((frm.heap[3] and $F) shl 32)+(frm.heap[4] shl 24)+
+ (frm.heap[5] shl 16)+(frm.heap[6] shl 8)+frm.heap[7];
+
+ if (data64<>0) and (Info.khz<>0) then
+ Info.total:=data64 div Info.khz;
+ Info.kbps:=Info.kbps*8;
+Info.kbps:=trunc(FileSize(f)*8/1000);
+ Info.khz:=Info.khz div 1000;
+ flag:=1;
+ end;
+ end;
+ 4: begin
+ GetMem(buf,size);
+ BlockRead(f,buf^,size);
+ OGGGetComment(buf,size,Info);
+ FreeMem(buf);
+ end;
+ 6: begin
+ if Info.cover=nil then
+ begin
+ GetMem(buf,size);
+ BlockRead(f,buf^,size);
+ ptr:=buf;
+ id:=BSwap(pdword(ptr)^);
+ case id of
+ 0,3,4,6: begin
+ inc(ptr,4);
+ id:=BSwap(pdword(ptr)^); // mime size
+ inc(ptr,4);
+ flag:=GetImageType(nil,ptr);
+ inc(ptr,id+4*5); // width, height, depth etc.
+ id:=BSwap(pdword(ptr)^); // image size
+ inc(ptr,4);
+ if flag=0 then
+ flag:=GetImageType(pByte(ptr));
+ FastAnsiToWideBuf(PAnsiChar(@flag),pWideChar(@data64));
+ Info.cover:=SaveTemporaryW(ptr,id,PWideChar(@data64));
+ end;
+ end;
+ FreeMem(buf);
+ end
+ else
+ Skip(f,size);
+ end
+ else
+ begin
+ if (hdr.blocktype and $80)<>0 then
+ break;
+ Skip(f,size);
+ end;
+ end;
+ until (hdr.blocktype and $80)<>0;
+ end;
+ ReadID3v1(f,Info);
+ CloseHandle(f);
+ result:=true;
+end;
+
+var
+ LocalFormatLinkOGG,
+ LocalFormatLinkOGA,
+ LocalFormatLinkOGM,
+ LocalFormatLinkSPX,
+ LocalFormatLinkFLA,
+ LocalFormatLinkFLAC:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLinkOGG.Next:=FormatLink;
+
+ LocalFormatLinkOGG.This.proc :=@ReadOGG;
+ LocalFormatLinkOGG.This.ext :='OGG';
+ LocalFormatLinkOGG.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkOGG;
+
+ LocalFormatLinkOGA.Next:=FormatLink;
+
+ LocalFormatLinkOGA.This.proc :=@ReadOGG;
+ LocalFormatLinkOGA.This.ext :='OGA';
+ LocalFormatLinkOGA.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkOGA;
+
+ LocalFormatLinkOGM.Next:=FormatLink;
+
+ LocalFormatLinkOGM.This.proc :=@ReadOGG;
+ LocalFormatLinkOGM.This.ext :='OGM';
+ LocalFormatLinkOGM.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkOGM;
+
+ LocalFormatLinkSPX.Next:=FormatLink;
+
+ LocalFormatLinkSPX.This.proc :=@ReadSPX;
+ LocalFormatLinkSPX.This.ext :='SPX';
+ LocalFormatLinkSPX.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkSPX;
+
+ LocalFormatLinkFLA.Next:=FormatLink;
+
+ LocalFormatLinkFLA.This.proc :=@ReadfLaC;
+ LocalFormatLinkFLA.This.ext :='FLA';
+ LocalFormatLinkFLA.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkFLA;
+
+ LocalFormatLinkFLAC.Next:=FormatLink;
+
+ LocalFormatLinkFLAC.This.proc :=@ReadfLaC;
+ LocalFormatLinkFLAC.This.ext :='FLAC';
+ LocalFormatLinkFLAC.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkFLAC;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_real.pas b/plugins/Watrack/formats/fmt_real.pas
new file mode 100644
index 0000000000..8d5f5bf72d
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_real.pas
@@ -0,0 +1,335 @@
+{Real file}
+unit fmt_Real;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadReal(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,tags,srv_format;
+
+const
+ blk_RMF = $464D522E; // '.RMF'
+ blkPROP = $504F5250; // 'PROP'
+ blkCONT = $544E4F43; // 'CONT' - content
+ blkMDPR = $5250444D; // 'MDPR'
+ blkDATA = $41544144; // 'DATA'
+ blkINDX = $58444E49; // 'INDX'
+ blkRMMD = $444D4D52; // 'RMMD' - comment block
+ blkRMJD = $444A4D52; // 'RMJD'
+ blkRMJE = $454A4D52; // 'RMJE'
+type
+ tChunk = packed record
+ ID:dword;
+ Len:dword; //with Chunk;
+ end;
+
+type
+ pPROP = ^tPROP;
+ tPROP = packed record
+ w1 :word;
+ l1,l2 :dword;
+ l3,l4 :dword;
+ un1 :dword; // or 2+2
+ filetotal :dword; // msec
+ l5 :dword;
+ InfoDataSize:dword;
+ Infosize :dword;
+ w2 :word; // always 2 ?
+ w :word; // chunks+1?
+ end;
+
+procedure SkipStr(var p:PAnsiChar;alen:integer);
+var
+ len:integer;
+begin
+ if alen=2 then
+ len:=(ord(p[0]) shl 8)+ord(p[1])
+ else
+ len:=ord(p[0]);
+ inc(p,alen);
+// if len>0 then
+ inc(p,len);
+end;
+
+function ReadStr(var p:PAnsiChar;alen:integer):PAnsiChar;
+var
+ len:integer;
+begin
+ if alen=2 then
+ len:=(ord(p[0]) shl 8)+ord(p[1])
+ else
+ len:=ord(p[0]);
+ inc(p,alen);
+ if len>0 then
+ begin
+ mGetMem(result,len+1);
+ move(p^,result^,len);
+ result[len]:=#0;
+ inc(p,len);
+ end
+ else
+ result:=nil;
+end;
+
+function GetWord(var p:PAnsiChar):word;
+begin
+ result:=(ord(p[0]) shl 8)+ord(p[1]);
+ inc(p,2);
+end;
+
+function GetLong(var p:PAnsiChar):dword;
+begin
+ result:=(ord(p[0]) shl 24)+(ord(p[1]) shl 16)+(ord(p[2]) shl 8)+ord(p[3]);
+ inc(p,4);
+end;
+
+function ReadReal(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ chunk:tChunk;
+ p,buf:PAnsiChar;
+ ls:PAnsiChar;
+ ver:integer;
+ fsize:cardinal;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ fsize:=FileSize(f);
+ while FilePos(f)<fsize do
+ begin
+ BlockRead(f,chunk,SizeOf(chunk));
+ chunk.Len:=BSwap(chunk.Len);
+ if (not (AnsiChar(chunk.ID and $FF) in ['A'..'Z','a'..'z','.'])) or
+ (chunk.Len<SizeOf(chunk)) then
+ break;
+ if (chunk.ID=blkPROP) or (chunk.ID=blkCONT) or (chunk.ID=blkMDPR) then
+ begin
+ mGetMem(buf,chunk.Len-SizeOf(chunk));
+ p:=buf;
+ BlockRead(f,buf^,chunk.Len-SizeOf(chunk));
+ if chunk.ID=blkPROP then
+ begin
+ inc(p,22);
+{
+ GetWord(p); // 0
+ GetLong(p); // min total bps?
+ GetLong(p); // max total bps?
+ GetLong(p); // a samples?
+ GetLong(p); // b samples?
+ GetLong(p); // c (samplesize?)
+}
+ Info.total:=GetLong(p) div 1000;
+{
+ GetLong(p); // X
+ GetLong(p); // used data size (w/o INDX and tags)
+ GetLong(p); // offset to DATA chunk
+ GetWord(p); // number of MDPR chunks
+ GetWord(p); // 2-9, 3-11
+}
+ end
+ else if chunk.ID=blkCONT then
+ begin
+ SkipStr(p,2); // rating?
+ ls:=ReadStr(p,2); // title
+ AnsiToWide(ls,Info.title);
+ mFreeMem(ls);
+ ls:=ReadStr(p,2); // author
+ AnsiToWide(ls,Info.artist);
+ mFreeMem(ls);
+{
+ SkipStr(p,2); // copyright
+ SkipStr(p,2); // description
+}
+ end
+ else if chunk.ID=blkMDPR then
+ begin //stream or logical info
+ GetLong(p); // MDPR block number (from 0)
+ if Info.kbps=0 then
+ Info.kbps:=GetLong(p) div 1000 // a stream bps
+ else
+ GetLong(p); // a stream bps
+ inc(p,24);
+{
+ GetLong(p); // a stream bps
+ GetLong(p); // b smp
+ GetLong(p); // b smp
+ GetLong(p); // 0
+ GetLong(p); // X
+ GetLong(p); //StreamLen
+}
+ SkipStr(p,1); //BlockName - usually 'Audio Stream'
+ ls:=ReadStr(p,1); //BlockMime
+ if StrCmp(ls,'audio/x-pn-realaudio')=0 then
+ begin
+ inc(p,20);
+{
+ GetLong(p); // stream dataLen;
+ GetLong(p); // type = $2E$72$61$FD
+ GetWord(p); // binary version? [4/5]
+ GetWord(p); // 0
+ GetLong(p); // last byte = ASC version $2E$72$61$($30+ver.)
+ GetWord(p);
+ GetWord(p);
+}
+ ver:=GetWord(p); // =version?
+ inc(p,30);
+{
+ GetLong(p); // datalen incl +2 (ver?)
+ GetWord(p); // ? 18,19,1,7
+ GetWord(p); // 0
+ GetWord(p); // un1
+ GetLong(p); //
+ GetLong(p); //
+ GetLong(p); //
+ GetWord(p); //
+ GetWord(p); // un2=un1
+ GetWord(p); //
+ GetWord(p); // 60 [93] (0 for ra4)
+}
+ if ver=5 then
+ begin
+ Info.khz:=GetLong(p) div 1000;
+ inc(p,8);
+{
+ GetLong(p); // equ KHZ
+ GetLong(p); // bits/channel?
+}
+ Info.channels:=GetWord(p);
+{
+ GetLong(p); // 'genr'
+ GetLong(p); // codec name
+ GetWord(p); // $01 $07
+ GetLong(p); // 0
+ GetWord(p); // channel data len (16-stereo,8-mono)
+ GetWord(p); // $01 $00
+ GetWord(p); // $00 $03[mono-2]
+ GetWord(p); // $04 [mono-2] $00
+ GetWord(p); //
+ if Info.channels=2 then
+ begin
+ GetLong(p); // 0
+ GetWord(p); // 01
+ GetWord(p); // 03
+ end
+}
+ end
+ else
+ begin
+ Info.khz:=GetWord(p) div 1000;
+ GetLong(p); // bits/channel?
+ Info.channels:=GetWord(p);
+{
+ SkipStr(p,1); // codec
+ SkipStr(p,1); // codec
+ GetWord(p); // $01 $07
+ inc(p,5);
+}
+ end
+ end;
+{
+ if StrCmp(tmpstr,'logical-fileinfo')=0 then
+ begin
+ GetLong(p); // a block len w/o
+ GetLong(p); // a block len with
+ GetLong(p); // 0
+ GetLong(p); // number of nodes
+ for i:=0 to Nodes-1 do
+ begin
+ GetLong(p); // node len with len dword
+ GetWord(p);
+ SkipStr(p,1); // node name
+ GetLong(p); // value type? 2 - asciiz
+ SkipStr(p,2); //node value
+ end;
+ end;
+}
+
+// if StrCmp(tmpstr,'Video Stream')=0 then
+ if StrCmp(ls,'video/x-pn-realvideo')=0 then
+ begin
+ GetLong(p); //stream dataLen;
+ Info.kbps:=GetLong(p); //override kbps
+ GetLong(p); //VIDO=vidtype
+ Info.codec:=ord(p[0])+(ord(p[1]) shl 8)+
+ (ord(p[2]) shl 16)+(ord(p[3]) shl 24); //codec ex.'RV30'
+ inc(p,4);
+ Info.width:=GetWord(p); //width
+ Info.height:=GetWord(p); //height
+ GetWord(p); //fps or colordeep
+ GetWord(p); //alt.width ?
+ GetWord(p); //alt. height ?
+ Info.fps:=GetWord(p)*100; //fps
+ {}
+ end;
+
+ mFreeMem(ls);
+ end;
+ mFreeMem(buf);
+ end
+ else if chunk.ID=blkRMMD then //comment
+ begin
+ Skip(f,chunk.Len-SizeOf(chunk));
+{
+ BlockRead(f,chunk,SizeOf(chunk)); //RJMD
+ chunk.len:=BSwap(chunk.len);
+ BlockRead(f,tmplong,4);
+
+
+ BlockRead(f,chunk,SizeOf(chunk)); //RMJE
+ chunk.len:=BSwap(chunk.len);
+}
+ end
+ else
+ begin
+ if chunk.ID=blk_RMF then
+ if FilePos(f)<>SizeOf(chunk) then // channels-1: ofs=$0A
+ break;
+ Skip(f,chunk.Len-SizeOf(chunk));
+ end;
+ end;
+ ReadID3v1(f,Info);
+ CloseHandle(f);
+ result:=true;
+end;
+
+var
+ LocalFormatLinkRM,
+ LocalFormatLinkRA,
+ LocalFormatLinkRAM:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLinkRM.Next:=FormatLink;
+
+ LocalFormatLinkRM.This.proc :=@ReadReal;
+ LocalFormatLinkRM.This.ext :='RM';
+ LocalFormatLinkRM.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkRM;
+
+ LocalFormatLinkRA.Next:=FormatLink;
+
+ LocalFormatLinkRA.This.proc :=@ReadReal;
+ LocalFormatLinkRA.This.ext :='RA';
+ LocalFormatLinkRA.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkRA;
+
+ LocalFormatLinkRAM.Next:=FormatLink;
+
+ LocalFormatLinkRAM.This.proc :=@ReadReal;
+ LocalFormatLinkRAM.This.ext :='RAM';
+ LocalFormatLinkRAM.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkRAM;
+end;
+
+initialization
+ InitLink;
+
+end.
diff --git a/plugins/Watrack/formats/fmt_tta.pas b/plugins/Watrack/formats/fmt_tta.pas
new file mode 100644
index 0000000000..c13b329fe2
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_tta.pas
@@ -0,0 +1,65 @@
+{TTA file}
+unit fmt_TTA;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadTTA(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,tags,srv_format;
+
+const
+ TTA1_SIGN = $31415454;
+type
+ tTTAHeader = packed record
+ id :dword;
+ format :word;
+ channels :word;
+ bitspersample:word;
+ samplerate :dword;
+ datalength :dword;
+ crc32 :dword;
+ end;
+
+function ReadTTA(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ hdr:tTTAHeader;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ ReadID3v2(f,Info);
+ BlockRead(f,hdr,SizeOf(tTTAHeader));
+ if hdr.id<>TTA1_SIGN then
+ exit;
+ Info.channels:=hdr.channels;
+ Info.khz :=hdr.samplerate;
+ Info.kbps :=hdr.bitspersample div 1000; //!!
+ if hdr.samplerate<>0 then
+ Info.total:=hdr.datalength div hdr.samplerate;
+ ReadID3v1(f,Info);
+ CloseHandle(f);
+ result:=true;
+end;
+
+var
+ LocalFormatLink:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLink.Next:=FormatLink;
+
+ LocalFormatLink.This.proc :=@ReadTTA;
+ LocalFormatLink.This.ext :='TTA';
+ LocalFormatLink.This.flags:=0;
+
+ FormatLink:=@LocalFormatLink;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_wav.pas b/plugins/Watrack/formats/fmt_wav.pas
new file mode 100644
index 0000000000..98d8e18fb8
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_wav.pas
@@ -0,0 +1,146 @@
+{WAV processing}
+unit fmt_WAV;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadWAV(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,tags,srv_format;
+
+const
+ wavRIFF = $46464952;
+ wavWAVE = $45564157;
+ wavfmt_ = $20746D66;
+ wavfact = $74636166;
+ wavdata = $61746164;
+type
+ tWAVChunk = packed record
+ id :dword;
+ size:dword;
+ end;
+type
+ tWAVFormatChunk = packed record
+ Codec :word;
+ Channels :word;
+ SampleRate :dword;
+ AvgBPS :dword;
+ BlockAlign :word;
+ BitsPerSample:word;
+ end;
+
+const
+ WavPackID = $6B707677;
+type
+// ckID :dword; // "wvpk"
+// ckSize :dword; // size of entire frame (minus 8, of course)
+ tWavPackHeader = packed record
+ version :word; // 0x403 for now
+ track_no :byte; // track number (0 if not used, like now)
+ index_no :byte; // track sub-index (0 if not used, like now)
+ total_samples:dword; // for entire file (-1 if unknown)
+ block_index :dword; // index of first sample in block (to file begin)
+ block_samples:dword; // # samples in This block
+ flags :dword; // various flags for id and decoding
+ crc :dword; // crc for actual decoded data
+ end;
+
+function ReadWAV(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ chunk:tWAVChunk;
+ fmtchunk:tWAVFormatChunk;
+ tmp:dword;
+ WPH:tWavPackHeader;
+ fsize:dword;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ BlockRead(f,chunk,SizeOf(chunk));
+ if chunk.id=WavPackID then
+ begin
+ BlockRead(f,WPH,SizeOf(tWavPackHeader));
+ BlockRead(f,tmp,2); //!! $1621 33,22
+ BlockRead(f,chunk,SizeOf(chunk));
+ end
+ else
+ begin
+ WPH.version:=0;
+ integer(WPH.total_samples):=-1;
+ end;
+ if chunk.id<>wavRIFF then
+ exit;
+ BlockRead(f,chunk,SizeOf(dword));
+ if chunk.id<>wavWAVE then
+ exit;
+ BlockRead(f,chunk,SizeOf(chunk));
+ if chunk.id<>wavfmt_ then
+ exit;
+ BlockRead(f,fmtchunk,SizeOf(tWAVFormatChunk));
+ Info.channels:=fmtchunk.Channels;
+ Info.khz :=fmtchunk.SampleRate div 1000;
+ if chunk.size>SizeOf(tWAVFormatChunk) then
+ Skip(f,chunk.size-SizeOf(tWAVFormatChunk));
+ fsize:=FileSize(f);
+ while FilePos(f)<fsize do
+ begin
+ BlockRead(f,chunk,SizeOf(chunk));
+ if chunk.id=wavfact then
+ begin
+ BlockRead(f,tmp,4);
+ break;
+ end;
+ if chunk.id=wavdata then
+ begin
+ tmp:=chunk.size;
+ break;
+ end;
+ Skip(f,chunk.size);
+ end;
+ if WPH.version<>0 then
+ begin
+ ReadAPEv2(f,Info);
+ ReadID3v1(f,Info);
+ end;
+ if integer(WPH.total_samples)=-1 then
+ if (fmtchunk.BitsPerSample<>0) and (fmtchunk.Channels<>0) then
+ WPH.total_samples:=(tmp*8) div (fmtchunk.Channels*fmtchunk.BitsPerSample);
+ if fmtchunk.SampleRate<>0 then
+ Info.total:= WPH.total_samples div fmtchunk.SampleRate;
+ if Info.total<>0 then
+ Info.kbps:=tmp*8 div Info.total div 1000;
+
+ CloseHandle(f);
+ result:=true;
+end;
+
+var
+ LocalFormatLinkWAV,
+ LocalFormatLinkWV:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLinkWAV.Next:=FormatLink;
+
+ LocalFormatLinkWAV.This.proc :=@ReadWAV;
+ LocalFormatLinkWAV.This.ext :='WAV';
+ LocalFormatLinkWAV.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkWAV;
+
+ LocalFormatLinkWV.Next:=FormatLink;
+
+ LocalFormatLinkWV.This.proc :=@ReadWAV;
+ LocalFormatLinkWV.This.ext :='WV';
+ LocalFormatLinkWV.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkWV;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/fmt_wma.pas b/plugins/Watrack/formats/fmt_wma.pas
new file mode 100644
index 0000000000..ed575147ac
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_wma.pas
@@ -0,0 +1,438 @@
+{WMA file format}
+unit fmt_WMA;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadWMA(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,srv_format,utils;
+
+const
+ ASF_Header_Object :tGUID='{75B22630-668E-11CF-A6D9-00AA0062CE6C}';
+
+ ASF_Header_Extension_Object :tGUID='{5FBF03B5-A92E-11CF-8EE3-00C00C205365}';
+ ASF_Content_Description_Object :tGUID='{75B22633-668E-11CF-A6D9-00AA0062CE6C}';
+ ASF_Extended_Content_Description_Object:tGUID='{D2D0A440-E307-11D2-97F0-00A0C95EA850}';
+ ASF_File_Properties_Object :tGUID='{8CABDCA1-A947-11CF-8EE4-00C00C205365}';
+ ASF_Stream_Properties_Object :tGUID='{B7DC0791-A9B7-11CF-8EE6-00C00C205365}';
+
+ ASF_Metadata_Library_Object :tGUID='{44231C94-9498-49D1-A141-1D134E457054}';
+ ASF_Audio_Media :tGUID='{F8699E40-5B4D-11CF-A8FD-00805F5C442B}';
+ ASF_Video_Media :tGUID='{BC19EFC0-5B4D-11CF-A8FD-00805F5C442B}';
+
+type
+ tSize=Int64;
+
+function CompareGUID(const guid1,guid2:tGUID):boolean;
+var
+ i:integer;
+ p1,p2:PAnsiChar;
+begin
+ p1:=PAnsiChar(@guid1);
+ p2:=PAnsiChar(@guid2);
+ for i:=0 to 15 do
+ begin
+ if p1^<>p2^ then
+ begin
+ result:=false;
+ exit;
+ end;
+ inc(p1);
+ inc(p2);
+ end;
+ result:=true;
+end;
+
+function ReadGUID(var buf:PAnsiChar; var guid:pGUID):dword;
+var
+ size:tSize;
+begin
+ guid:=pointer(buf);
+ inc(buf,SizeOf(tGUID));
+ move(buf^,size,SizeOf(size));
+ inc(buf,SizeOf(size));
+ result:=size-SizeOf(tGUID)-SizeOf(size);
+end;
+
+procedure ReadWMATagStr(var dst:pWideChar;ptr:PAnsiChar;alen:word);
+begin
+ if pword(ptr)^<>0 then
+ begin
+ mGetMem(dst,alen);
+ move(pWideChar(ptr{+2})^,dst^,alen);
+ end;
+end;
+
+function ReadWMATagStr1(var dst:pWideChar;var ptr:PAnsiChar;value:boolean=true):integer;
+var
+ len,typ:word;
+begin
+ if value then
+ begin
+ typ:=pword(ptr)^;
+ inc(ptr,2); //value type
+ end
+ else
+ typ:=0;
+ len:=pword(ptr)^;
+ result:=-1;
+ dst:=nil;
+ if len<>0 then
+ begin
+ if typ=0 then
+ begin
+ mGetMem(dst,len);
+ move(PAnsiChar(ptr+2)^,PAnsiChar(dst)^,len);
+ end
+ else
+ begin
+ result:=pword(ptr+2)^;
+ if typ<5 then
+ result:=pword(ptr+4)^*$10000+result;
+ end;
+ end;
+ inc(ptr,len+2);
+end;
+
+procedure ProcessPicture(ptr:PAnsiChar;var Info:tSongInfo);
+var
+ extw:int64;
+ aSize:dword;
+begin
+ if Info.cover<>nil then exit;
+ case ptr^ of
+ #0,#3,#4,#6: ;
+ else
+ exit;
+ end;
+ inc(ptr);
+ aSize:=pdword(ptr)^; inc(ptr,4);
+ extw:=GetImageTypeW(nil,pWideChar(ptr));
+ while pWideChar(ptr)^<>#0 do inc(ptr,2); inc(ptr,2); // mime
+ while pWideChar(ptr)^<>#0 do inc(ptr,2); inc(ptr,2); // descr
+
+ if extw=0 then
+ extw:=GetImageTypeW(pByte(ptr));
+ Info.cover:=SaveTemporaryW(ptr,aSize,pWideChar(@extw));
+end;
+
+procedure ReadHdrExtended(ptr:PAnsiChar;size:dword;var Info:tSongInfo);
+var
+ buf:PAnsiChar;
+ ls:pWideChar;
+ cnt,tmp:integer;
+ tmpguid:pGUID;
+ lsize:dword;
+begin
+ inc(ptr,SizeOf(tGUID)+2);
+ size:=pdword(ptr)^; inc(ptr,4);
+ while size>0 do
+ begin
+ if Info.cover<>nil then break;
+ lsize:=ReadGUID(ptr,tmpguid);
+ dec(size,lsize+SizeOf(tGUID)+SizeOf(tSize));
+ if CompareGUID(tmpguid^,ASF_Metadata_Library_Object) then
+ begin
+ buf:=ptr;
+ cnt:=pdword(buf)^; inc(buf,2);
+ while cnt>0 do
+ begin
+ inc(buf,4); // lang & stream
+ {tmp:=pword (buf)^;} inc(buf,2); // namelen
+ {tmp:=pword (buf)^;} inc(buf,2); // datatype
+ tmp:=pdword(buf)^; inc(buf,4); // datalen
+ ls:=PWideChar(buf);
+ while pWideChar(buf)^<>#0 do inc(buf,2); inc(buf,2);
+ if lstrcmpiw(ls,'WM/Picture')=0 then
+ begin
+ ProcessPicture(buf,Info);
+ inc(buf,tmp);
+ end;
+ dec(cnt);
+ end;
+ end;
+ inc(ptr,lsize);
+ end;
+end;
+
+procedure ReadExtended(ptr:PAnsiChar;size:dword;var Info:tSongInfo);
+var
+ ls,ls1,ls2:pWideChar;
+ cnt,tmp:integer;
+begin
+ cnt:=pword(ptr)^; inc(ptr,2);
+ while cnt>0 do
+ begin
+ dec(cnt);
+ ReadWMATagStr1(ls,ptr,false);
+ if lstrcmpiw(ls,'WM/AlbumTitle')=0 then
+ ReadWMATagStr1(Info.album,ptr)
+ else if (Info.lyric=nil) and (lstrcmpiw(ls,'WM/Lyrics')=0) then
+ ReadWMATagStr1(Info.lyric,ptr)
+ else if (Info.lyric=nil) and (lstrcmpiw(ls,'WM/Lyrics_Synchronised')=0) then
+ begin
+ inc(ptr,2+2);
+ inc(ptr); // timestamp type
+ if ptr^=#1 then // lyric
+ begin
+ inc(ptr);
+ tmp:=pdword(ptr)^; inc(ptr,4);
+ mGetMem(ls2,tmp);
+ Info.lyric:=ls2;
+ ls1:=pWideChar(ptr);
+ inc(ptr,tmp);
+ while ls1^<>#0 do // description
+ begin
+ inc(ls1);
+ dec(tmp,SizeOf(WideChar));
+ end;
+ inc(ls1);
+ dec(tmp,SizeOf(WideChar));
+ while tmp>0 do
+ begin
+ if PAnsiChar(ls1)^=#$0A then
+ begin
+ inc(PAnsiChar(ls1));
+ ls2^:=#$0A;
+ dec(tmp);
+ inc(ls2);
+ end;
+ while ls1^<>#0 do
+ begin
+ ls2^:=ls1^; inc(ls2); inc(ls1);
+ dec(tmp,SizeOf(WideChar));
+ end;
+ inc(ls1,1+2); // terminator + timestamp
+ dec(tmp,SizeOf(WideChar)+4);
+ end;
+ ls2^:=#0;
+// ptr:=PAnsiChar(ls1);
+ end
+ end
+ else if lstrcmpiw(ls,'WM/Genre')=0 then
+ ReadWMATagStr1(Info.genre,ptr)
+ else if lstrcmpiw(ls,'WM/Year')=0 then
+ begin
+ tmp:=ReadWMATagStr1(Info.year,ptr);
+ if tmp<>-1 then
+ IntToStr(Info.year,tmp);
+ end
+ else if lstrcmpiw(ls,'WM/Track')=0 then
+ begin
+ tmp:=ReadWMATagStr1(ls1,ptr);
+ if tmp=-1 then
+ begin
+ Info.track:=StrToInt(ls1)+1;
+ mFreeMem(ls1);
+ end
+ else
+ Info.track:=tmp;
+ end
+ else if lstrcmpiw(ls,'WM/TrackNumber')=0 then
+ begin
+ tmp:=ReadWMATagStr1(ls1,ptr);
+ if tmp=-1 then
+ begin
+ Info.track:=StrToInt(ls1);
+ mFreeMem(ls1);
+ end
+ else
+ Info.track:=tmp;
+ end
+ else if lstrcmpiw(ls,'WM/Picture')=0 then
+ begin
+ inc(ptr,2); // data type
+ tmp:=pword(ptr)^; inc(ptr,2);
+ ProcessPicture(ptr,Info);
+ inc(ptr,tmp);
+ end
+ else
+ inc(ptr,4+pword(ptr+2)^);
+ mFreeMem(ls);
+ end;
+end;
+
+procedure ReadFileProp(ptr:PAnsiChar;var Info:tSongInfo);
+type
+ pFileProp = ^tFileProp;
+ tFileProp = packed record
+ FileGUID :tGUID;
+ FileSize :tSize;
+ Creation :tSize;
+ Packets :tSize;
+ Play :tSize;
+ Send :tSize;
+ PreRoll :tSize;
+ Flags :dword;
+ minpacket :dword;
+ maxpacket :dword;
+ maxbitrate:dword;
+ end;
+begin
+ Info.total:=pFileProp(ptr)^.Play div 10000000;
+end;
+
+procedure ReadStreamProp(ptr:PAnsiChar;size:dword;var Info:tSongInfo);
+type
+ pAudio = ^tAudio;
+ tAudio=packed record // WAVEFORMATEX
+ Codec :word;
+ Channels :word;
+ Samples :dword;
+ AvgBPS :dword;
+ BlockAlign :word;
+ BitsPerSample:word;
+ size :word;
+ end;
+ pVideo = ^tVideo;
+ tVideo = packed record
+ width :dword;
+ height :dword;
+ reserved:byte;
+ size :word;
+ bitmap :BITMAPINFOHEADER;
+ end;
+ Prefix = packed record
+ StreamType :tGUID;
+ ECGUID :tGUID; // Error Correction
+ TimeOffset :int64;
+ DataLength :dword;
+ ECDataLength:dword;
+ Flags :word;
+ Reserved :dword;
+ end;
+
+var
+ tmpguid:pGUID;
+begin
+ tmpguid:=pointer(ptr);
+ inc(ptr,SizeOf(Prefix)); //ofset to Type-Specific Data
+ if CompareGUID(tmpguid^,ASF_Audio_Media) then
+ begin
+ Info.channels:=pAudio(ptr)^.Channels;
+ Info.khz :=pAudio(ptr)^.Samples div 1000;
+ Info.kbps :=(pAudio(ptr)^.AvgBPS*8) div 1000;
+ end
+ else if CompareGUID(tmpguid^,ASF_Video_Media) then
+ begin
+ Info.width :=pVideo(ptr)^.bitmap.biWidth; // pVideo(ptr)^.width
+ Info.height:=pVideo(ptr)^.bitmap.biHeight; // pVideo(ptr)^.height
+ Info.codec :=pVideo(ptr)^.bitmap.biCompression;
+ end
+end;
+
+procedure ReadContent(ptr:PAnsiChar;var Info:tSongInfo);
+type
+ pContent = ^tContent;
+ tContent = packed record
+ TitleLength :word;
+ AuthorLength :word;
+ CopyrightLength :word;
+ DescriptionLength:word;
+ RatingLength :word;
+ end;
+var
+ cont:pContent;
+begin
+ cont:=pointer(ptr);
+ inc(ptr,SizeOf(tContent));
+ if cont^.TitleLength>0 then //title
+ begin
+ ReadWMATagStr(Info.title,ptr,cont^.TitleLength);
+ inc(ptr,cont^.TitleLength);
+ end;
+ if cont^.AuthorLength>0 then //artist
+ begin
+ ReadWMATagStr(Info.artist,ptr,cont^.AuthorLength);
+ inc(ptr,cont^.AuthorLength);
+ end;
+ inc(ptr,cont^.CopyrightLength); //copyright
+ if cont^.DescriptionLength>0 then //comment
+ ReadWMATagStr(Info.comment,ptr,cont^.DescriptionLength);
+end;
+
+function ReadWMA(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ tmpguid:pGUID;
+ size:int64;
+ buf1,buf2:PAnsiChar;
+ HdrObjects:dword;
+ base:tGUID;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+
+ BlockRead(f,base,SizeOf(tGUID));
+ if CompareGUID(base,ASF_Header_Object) then
+ begin
+ BlockRead(f,size,SizeOf(size));
+ dec(size,SizeOf(tGUID)+SizeOf(size));
+
+ GetMem(buf1,size);
+ buf2:=buf1;
+ BlockRead(f,buf1^,size);
+ HdrObjects:=pdword(buf2)^; inc(buf2,6);
+ while HdrObjects>0 do
+ begin
+ size:=ReadGUID(buf2,tmpguid);
+ if CompareGUID(tmpguid^,ASF_Content_Description_Object) then
+ ReadContent(buf2,Info)
+ else if CompareGUID(tmpguid^,ASF_Extended_Content_Description_Object) then
+ ReadExtended(buf2,size,Info)
+ else if CompareGUID(tmpguid^,ASF_Header_Extension_Object) then
+ ReadHdrExtended(buf2,size,Info)
+ else if CompareGUID(tmpguid^,ASF_File_Properties_Object) then
+ ReadFileProp(buf2,Info)
+ else if CompareGUID(tmpguid^,ASF_Stream_Properties_Object) then
+ ReadStreamProp(buf2,size,Info);
+ inc(buf2,size);
+ dec(HdrObjects);
+ end;
+ FreeMem(buf1);
+
+ result:=true;
+ end;
+ CloseHandle(f);
+end;
+
+var
+ LocalFormatLinkWMA,
+ LocalFormatLinkWMV,
+ LocalFormatLinkASF:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLinkWMA.Next:=FormatLink;
+
+ LocalFormatLinkWMA.This.proc :=@ReadWMA;
+ LocalFormatLinkWMA.This.ext :='WMA';
+ LocalFormatLinkWMA.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkWMA;
+
+ LocalFormatLinkWMV.Next:=FormatLink;
+
+ LocalFormatLinkWMV.This.proc :=@ReadWMA;
+ LocalFormatLinkWMV.This.ext :='WMV';
+ LocalFormatLinkWMV.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkWMV;
+
+ LocalFormatLinkASF.Next:=FormatLink;
+
+ LocalFormatLinkASF.This.proc :=@ReadWMA;
+ LocalFormatLinkASF.This.ext :='ASF';
+ LocalFormatLinkASF.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkASF;
+end;
+
+initialization
+ InitLink;
+end.
diff --git a/plugins/Watrack/formats/tag_apev2.inc b/plugins/Watrack/formats/tag_apev2.inc
new file mode 100644
index 0000000000..34ab7f2ad7
--- /dev/null
+++ b/plugins/Watrack/formats/tag_apev2.inc
@@ -0,0 +1,124 @@
+{APE tag}
+{$IFDEF Interface}
+function ReadAPEv2(buf:PAnsiChar;var Info:tSongInfo;count:integer=0):longint; overload;
+function ReadAPEv2(f:THANDLE;var Info:tSongInfo):longint; overload;
+{$ELSE}
+const
+ APESign = 'APETAGEX';
+type
+ pAPEHeader = ^tAPEHeader;
+ tAPEHeader = packed record
+ ID:array [0..7] of AnsiChar;
+ Version:dword;
+ TagSize:dword; //footer + all items
+ ItemCount:dword;
+ TagFlags:dword;
+ Reserved:array [0..7] of byte;
+ end;
+
+procedure ReadAPEValue(const buf:PAnsiChar;var dst:pWideChar;ver:dword);
+begin
+ if dst=nil then
+ if ver>1000 then
+ UTF8ToWide(buf,dst)
+ else
+ AnsiToWide(buf,dst);
+end;
+
+function ReadAPEv2(buf:PAnsiChar;var Info:tSongInfo;count:integer=0):longint;
+var
+ APE:pAPEHeader;
+ len:integer;
+ ptr,key:PAnsiChar;
+ flag:dword;
+ cf:THANDLE;
+ buf0,buf1:array [0..MAX_PATH-1] of AnsiChar;
+ b:AnsiChar;
+// extw:array [0..7] of WideChar;
+begin
+ result:=0;
+ APE:=pointer(buf);
+ if APE.ID=APESign then
+ begin
+ inc(buf,SizeOf(tAPEHeader));
+ count:=APE.ItemCount;
+ end;
+ while count>0 do
+ begin
+ len :=pdword(buf)^; inc(buf,4);
+ flag:=pdword(buf)^; inc(buf,4);
+ key:=buf;
+ while buf^<>#0 do inc(buf); inc(buf);
+
+ ptr:=buf+len;
+ b:=ptr^;
+ ptr^:=#0;
+ if lstrcmpia(key,'TITLE' )=0 then ReadAPEValue(buf,Info.title ,APE.Version)
+ else if lstrcmpia(key,'ARTIST' )=0 then ReadAPEValue(buf,Info.artist ,APE.Version)
+ else if lstrcmpia(key,'ALBUM' )=0 then ReadAPEValue(buf,Info.album ,APE.Version)
+ else if lstrcmpia(key,'COMMENT')=0 then ReadAPEValue(buf,Info.comment,APE.Version)
+ else if lstrcmpia(key,'GENRE' )=0 then ReadAPEValue(buf,Info.genre ,APE.Version)
+ else if lstrcmpia(key,'YEAR' )=0 then ReadAPEValue(buf,Info.year ,APE.Version)
+ else if lstrcmpia(key,'TRACK' )=0 then if Info.track=0 then Info.track:=StrToInt(buf)
+ else if lstrcmpia(key,'LYRICS' )=0 then ReadAPEValue(buf,Info.lyric ,APE.Version)
+ //!! must preserve multipart lyric
+ else if (lstrcmpia(key,'Cover Art (Front)')=0) or
+ (lstrcmpia(key,'Cover Art (Back)' )=0) or
+ (lstrcmpia(key,'APIC' )=0) then
+ begin
+ if Info.cover=nil then
+ begin
+ while buf^<>#0 do inc(buf); inc(buf); // point to data now
+ flag:=GetImageType(pByte(buf));
+ if flag<>0 then
+ begin
+{
+ FastAnsiToWideBuf(PAnsiChar(@flag),pWideChar(@extw));
+ Info.Cover:=SaveTemporaryW(buf,ptr-buf,PWideChar(@extw));
+}
+ GetTempPathA(SizeOf(buf0),buf0);
+ GetTempFileNameA(buf0,'wat',GetCurrentTime,buf1);
+ ChangeExt(buf1,PAnsiChar(@flag));
+
+ cf:=ReWrite(PAnsiChar(@buf1));
+ BlockWrite(cf,buf^,ptr-buf);
+ CloseHandle(cf);
+ AnsiToWide(PAnsiChar(@buf1),Info.cover);
+ end;
+ end;
+ end;
+ ptr^:=b;
+ buf:=ptr;
+ dec(count);
+ end;
+end;
+
+function ReadAPEv2(f:THANDLE;var Info:tSongInfo):longint;
+var
+ APE:tAPEHeader;
+ buf:PAnsiChar;
+ fpos:dword;
+ TagID:array [1..3] of AnsiChar;
+begin
+ result:=0;
+ fpos:=FileSize(f);
+ Seek(f,fpos-SizeOf(TID3v1Tag));
+ BlockRead(f,TagID,3);
+ if TagID=TAG1Sign then
+ dec(fpos,SizeOf(TID3v1Tag));
+ Seek(f,fpos-SizeOf(APE));
+ BlockRead(f,APE,SizeOf(APE));
+ // footer must be copied as header
+ if APE.ID=APESign then
+ begin
+ if (APE.TagFlags and $20000000)=0 then //Footer
+ begin
+ Seek(f,fpos-APE.TagSize{-SizeOf(APE)});// without header but with footer
+ GetMem(buf,APE.TagSize);
+ BlockRead(f,buf^,APE.TagSize);
+ result:=ReadAPEv2(buf,Info,APE.ItemCount);
+ FreeMem(buf);
+ end;
+ end;
+end;
+{$ENDIF}
diff --git a/plugins/Watrack/formats/tag_id3v1.inc b/plugins/Watrack/formats/tag_id3v1.inc
new file mode 100644
index 0000000000..bd1db906bb
--- /dev/null
+++ b/plugins/Watrack/formats/tag_id3v1.inc
@@ -0,0 +1,175 @@
+{ID3v1 tag}
+{$IFDEF Interface}
+const
+ TAG1Sign = 'TAG';
+type
+ TID3v1Tag = packed record
+ ID: array [0..2] of AnsiChar;
+ Title: array [0..29] of AnsiChar;
+ Artist: array [0..29] of AnsiChar;
+ Album: array [0..29] of AnsiChar;
+ Year: array [0..3] of AnsiChar;
+ Comment: array [0..28] of AnsiChar;
+ Track: byte;
+ Genre: byte;
+ end;
+
+function ReadID3v1(f:THANDLE; var Info:tSongInfo):longint;
+{$ELSE}
+const
+ Lyric1End = 'LYRICSEND';
+ LyricStart = 'LYRICSBEGIN';
+ Lyric2End = 'LYRICS200';
+ LyricEndLen = Length(Lyric1End);
+const
+ fIND = $494E44;
+ fLYR = $4C5952;
+ fEAL = $45414C;
+ fEAR = $454152;
+ fETT = $455454;
+ fIMG = $494D47;
+ fINF = $494E46;
+
+procedure ID3v1_TagCorrect(var dst:pWideChar;const tag:array of AnsiChar);
+var
+ i:integer;
+ s:array [0..31] of AnsiChar;
+begin
+ i:=High(tag);
+ move(tag,s,i+1);
+ while (i>0) and (tag[i]<=' ') do dec(i);
+ if i>0 then
+ begin
+ s[i+1]:=#0;
+ AnsiToWide(s,dst);
+ end;
+end;
+
+procedure ID3v1_GetField(ptr:PAnsiChar; var dst:pWideChar; len:integer);
+var
+ txtfield:array [0..250] of AnsiChar;
+begin
+ if dst=nil then
+ begin
+ move(ptr^,txtfield,len);
+ txtfield[len]:=#0;
+ AnsiToWide(txtfield,dst);
+ end;
+end;
+
+procedure ID3v1_CheckLyric(var Info:tSongInfo;f:THANDLE;ofs:integer);
+const
+ maxlen = 5100;
+var
+ tagHdr:array [0..9] of AnsiChar;
+ buf:array [0..maxlen] of AnsiChar;
+ ptr,ptr1:PAnsiChar;
+ i,size:integer;
+ field:dword;
+ c:dword;
+begin
+ Seek(f,ofs);
+ BlockRead(f,tagHdr,LyricEndLen);
+ tagHdr[9]:=#0;
+ if StrCmp(tagHdr,Lyric1End,LyricEndLen)=0 then
+ begin
+ if Info.lyric=nil then
+ begin
+ Seek(f,ofs-maxlen);
+ BlockRead(f,buf,maxlen);
+ buf[maxlen]:=#0;
+ ptr:=@buf;
+ for i:=0 to maxlen-Length(LyricStart) do
+ begin
+ if ptr^='L' then
+ if StrCmp(ptr,LyricStart,Length(LyricStart))=0 then
+ begin
+ AnsiToWide(ptr+Length(LyricStart),Info.lyric);
+ break;
+ end;
+ inc(ptr);
+ end;
+ end;
+ end
+ else if StrCmp(tagHdr,Lyric2End,LyricEndLen)=0 then
+ begin
+ Seek(f,ofs-6);
+ BlockRead(f,buf,6);
+ size:=StrToInt(buf);
+ if size<ofs then
+ begin
+ Seek(f,ofs-size-6);
+ mGetMem(ptr,size+1);
+ BlockRead(f,ptr^,size);
+ if StrCmp(ptr,LyricStart,Length(LyricStart))=0 then
+ begin
+ ptr1:=ptr+Length(LyricStart);
+
+ while ptr1<ptr+size do
+ begin
+ field:=(ORD(ptr1^) shl 16)+(ORD((ptr+1)^) shl 8)+ORD((ptr1+2)^);
+ inc(ptr1,3);
+ move(ptr1^,buf,5);
+ buf[5]:=#0;
+ i:=StrToInt(buf);
+ inc(ptr1,5);
+ case field of
+ fLYR: if Info.lyric=nil then
+ begin
+ c:=pword(ptr1+i)^;
+ pword(ptr1+i)^:=0;
+ if (pword(ptr1)^=$FFFE) or ((pword(ptr1)^=$FEFF)) then
+ begin
+ StrDupW(Info.lyric,pWidechar(ptr1));
+ ChangeUnicode(Info.lyric);
+ end
+ else
+ begin
+ AnsiToWide(ptr1,Info.lyric);
+ end;
+ pword(ptr1+i)^:=c;
+ end;
+ fEAL: ID3v1_GetField(ptr1,Info.album,i);
+ fEAR: ID3v1_GetField(ptr1,Info.artist,i);
+ fETT: ID3v1_GetField(ptr1,Info.title,i);
+// fINF:
+// fIMG:
+ end;
+ inc(ptr1,i);
+ end;
+ end;
+ mFreeMem(ptr);
+ end;
+ end;
+end;
+
+function ReadID3v1(f:THANDLE; var Info:tSongInfo):longint;
+var
+ tag:TID3v1Tag;
+ ofs:integer;
+begin
+ result:=0;
+ ofs:=FileSize(f)-SizeOf(tag);
+ Seek(f,ofs);
+ BlockRead(f,tag,SizeOf(tag));
+ if tag.ID=TAG1Sign then
+ begin
+ if Info.album =nil then ID3v1_TagCorrect(Info.album ,tag.Album);
+ if Info.artist =nil then ID3v1_TagCorrect(Info.artist ,tag.Artist);
+ if Info.title =nil then ID3v1_TagCorrect(Info.title ,tag.Title);
+ if Info.comment=nil then ID3v1_TagCorrect(Info.comment,tag.Comment);
+ if Info.year =nil then ID3v1_TagCorrect(Info.year ,tag.Year);
+ if Info.genre =nil then Info.genre:=GenreName(tag.Genre);
+ if Info.track=0 then
+ begin
+ Info.track:=tag.Track;
+ if Info.track >=32 then Info.track:=0;
+ end;
+ dec(ofs,9);
+ result:=1;
+ end
+ else
+ inc(ofs,SizeOf(tag)-9);
+ ID3v1_CheckLyric(Info,f,ofs); // +skipAPEtag
+end;
+{$ENDIF}
diff --git a/plugins/Watrack/formats/tag_id3v2.inc b/plugins/Watrack/formats/tag_id3v2.inc
new file mode 100644
index 0000000000..b1f833ea2a
--- /dev/null
+++ b/plugins/Watrack/formats/tag_id3v2.inc
@@ -0,0 +1,545 @@
+{ID3v2 tag}
+
+{$IFDEF Interface}
+function ReadID3v2(f:THANDLE; var Info:tSongInfo):longint;
+{$ELSE}
+const
+ frmTRK = $4B5254;
+ frmTT2 = $325454;
+ frmTP1 = $315054;
+ frmTAL = $4C4154;
+ frmTYE = $455954;
+ frmCOM = $4D4F43;
+ frmTCO = $4F4354;
+// frmTCM = $;'; New: 'TCOM'),
+// frmTEN = $;'; New: 'TENC'),
+// frmTCR = $;'; New: 'TCOP'),
+// frmWXX = $;'; New: 'WXXX'),
+ frmTT1 = $315454;
+// frmTLA = $;'; New: 'TLAN'),
+ frmTOA = $414F54;
+ frmULT = $544C55;
+ frmSLT = $544C53;
+ frmTXX = $585854;
+ frmPIC = $434950;
+
+ frmTIT1 = $31544954; // Content group description
+ frmTIT2 = $32544954; // Title/songname/content description
+ frmTIT3 = $33544954; // Subtitle/Description refinement
+ frmTALB = $424C4154; // Album/Movie/Show title
+ frmTOAL = $4C414F54; // Original album/movie/show title
+ frmTRCK = $4B435254; // Track number/Position in set
+ frmTYER = $52455954; // Year
+ frmTDRC = $43524454; // Year
+ frmTORY = $59524F54; // Original release year
+ frmTPE1 = $31455054; // Lead performer(s)/Soloist(s)
+ frmTPE2 = $32455054; // Band/orchestra/accompaniment
+ frmTPE3 = $33455054; // Conductor/performer refinement
+ frmTPE4 = $34455054; // Interpreted, remixed, or otherwise modified by
+ frmTOPE = $45504F54; // Original artist(s)/performer(s)
+ frmTCON = $4E4F4354; // Content type
+ frmCOMM = $4D4D4F43; // Comments
+ frmUSLT = $544C5355; // Unsynchronised lyrics
+ frmSYLT = $544C5953; // Synchronised lyrics
+ frmTXXX = $58585854; // User defined text
+ frmAPIC = $43495041; // Attached picture
+const
+ TAG2Sign = 'ID3';
+const
+ ExtIDHdrMask=$40;
+ FooterPresent=$10;
+type
+ TID3v2TagHdr = packed record
+ ID :array [0..2] of AnsiChar;
+ Version:word;
+ Flags :byte;
+ TagSize:dword;
+ end;
+ PID3v2TagHdr = ^TID3v2TagHdr;
+type
+ tID3v2FrameHdr = packed record
+ ID:dword;
+ Size:dword;
+ Flags:word;
+ end;
+ pID3v2FrameHdr = ^tID3v2FrameHdr;
+ tID3v2FrameHdrOld = packed record
+ ID : array [0..2] of byte; { Frame ID }
+ Size: array [0..2] of Byte; { Size excluding header }
+ end;
+ pID3v2FrameHdrOld = ^tID3v2FrameHdrOld;
+
+var
+ Unsync:boolean;
+
+function ID3v2_Correct(data:dword):dword;
+type
+ l2b=packed record
+ b:array [0..3] of byte;
+ end;
+begin
+ result:=l2b(data).b[3];
+ inc(result,dword(l2b(data).b[0]) shl 21);
+ inc(result,dword(l2b(data).b[1]) shl 14);
+ inc(result,dword(l2b(data).b[2]) shl 7);
+end;
+
+procedure ID3v2_ReadTagStr1(var dst:PWideChar;ptr:PAnsiChar;alen:integer;enc:integer);
+var
+ buf:PAnsiChar;
+begin
+ if (enc=0) or (enc=3) then // ANSI or UTF8
+ begin
+ if ptr^=#0 then
+ alen:=0
+ else
+ while (alen>0) and (ptr[alen-1]=#0) do dec(alen);
+
+ if alen>0 then
+ begin
+{
+ if enc=0 then
+ begin
+ StrDup(buf,ptr,alen);
+ AnsiToWide(buf,dst)
+ mFreeMem(buf);
+ end
+ else
+ UTF8ToWide(buf,dst,alen);
+}
+ StrDup(buf,ptr,alen);
+ if enc=0 then
+ AnsiToWide(buf,dst)
+ else
+ UTF8ToWide(buf,dst);
+ mFreeMem(buf);
+ end
+ end
+ else {if enc<3 then} //Unicode
+ begin
+ if pword(ptr)^>0 then
+ begin
+ alen:=alen div SizeOf(WideChar);
+
+ StrDupW(dst,pWideChar(ptr),alen);
+ ChangeUnicode(dst);
+ end;
+ end;
+end;
+
+procedure ID3v2_ReadTagStr(var dst:PWideChar;ptr:PAnsiChar;alen:integer);
+var
+ enc:byte;
+begin
+ enc:=ORD(ptr^);
+ inc(ptr);
+ dec(alen);
+ if alen>0 then
+ ID3v2_ReadTagStr1(dst,ptr,alen,enc)
+ else
+ dst:=nil;
+end;
+
+procedure ID3v2_CheckLyric(tag:integer; var dst:PWideChar;ptr:PAnsiChar;len:integer);
+var
+ org,org1:PAnsiChar;
+ orgw,ptrw:pWideChar;
+ buf:array [0..127] of AnsiChar;
+ enc:byte;
+begin
+ if dst<>NIL then exit;
+ enc:=ord(ptr^);
+ inc(ptr);
+ if tag=frmUSLT then
+ begin
+ org:=ptr;
+ inc(ptr,3); // language
+ if (enc=0) or (enc=3) then
+ begin
+ while ptr^<>#0 do inc(ptr);
+ inc(ptr);
+ end
+ else
+ begin
+ while pWord(ptr)^<>0 do inc(ptr,2);
+ inc(ptr,2);
+ end;
+ dec(len,ptr-org);
+ ID3v2_ReadTagStr1(dst,ptr,len,enc);
+ end
+ else if tag=frmSYLT then
+ begin
+ inc(ptr,4);
+ if ptr^<>#1 then exit; // 1 - lyric
+ inc(ptr);
+ mGetMem(dst,len-6);
+ FillChar(dst^,len-6,0);
+
+ if (enc=0) or (enc=3) then
+ begin
+ while ptr^<>#0 do
+ begin
+ inc(ptr);
+ dec(len);
+ end;
+ inc(ptr);
+ dec(len);
+ org:=PAnsiChar(dst);
+ while len>0 do
+ begin
+ while ptr^<>#0 do
+ begin
+ org^:=ptr^; inc(org); inc(ptr);
+ dec(len);
+ end;
+ inc(ptr,1+4); // terminator+timestamp
+ dec(len,1+4);
+ end;
+ org:=PAnsiChar(dst);
+ if enc=0 then
+ AnsiToWide(org,dst)
+ else
+ UTF8ToWide(org,dst);
+ mFreeMem(org);
+ end
+ else
+ begin
+ orgw:=dst;
+ ptrw:=pWideChar(ptr);
+ while ptrw^<>#0 do
+ begin
+ inc(ptrw);
+ dec(len,SizeOf(WideChar));
+ end;
+ inc(ptrw);
+ dec(len,SizeOf(WideChar));
+ while len>0 do
+ begin
+ while ptrw^<>#0 do
+ begin
+ orgw^:=ptrw^; inc(orgw); inc(ptrw);
+ dec(len,SizeOf(WideChar));
+ end;
+ inc(ptrw,1+2); // terminator + timestamp
+ dec(len,SizeOf(WideChar)+4);
+ end;
+ end;
+ end
+ else if tag=frmTXXX then
+ begin
+ FillChar(buf,SizeOf(buf),0);
+ org1:=ptr;
+ if (enc=0) or (enc=3) then
+ begin
+ org:=@buf;
+ while ptr^<>#0 do
+ begin
+ org^:=ptr^;
+ inc(org);
+ inc(ptr);
+ end;
+ inc(ptr);
+ if StrCmp(buf,'LYRICS')<>0 then
+ exit;
+ end
+ else
+ begin
+ orgw:=@buf;
+ ptrw:=pWideChar(ptr);
+ while ptrw^<>#0 do
+ begin
+ orgw^:=ptrw^;
+ inc(orgw);
+ inc(ptrw);
+ end;
+ inc(ptrw);
+ if StrCmpW(pWideChar(@buf),'LYRICS')<>0 then
+ exit;
+ ptr:=PAnsiChar(ptrw);
+ end;
+ dec(len,ptr-org1);
+ ID3v2_ReadTagStr1(dst,ptr,len,enc);
+ end;
+end;
+
+procedure ID3v2_CheckCover(tag:integer; var dst:pWideChar;ptr:PAnsiChar;len:integer);
+var
+ org:PAnsiChar;
+ ext:dword;
+ extw:int64;
+ enc:byte;
+begin
+ if dst<>nil then exit;
+ org:=ptr;
+ enc:=ord(ptr^); inc(ptr);
+ if (pdword(ptr)^ and $FFFFFF)=$3E2D2D then exit; // as '-->'
+ if tag=frmAPIC then
+ begin
+ ext:=GetImageType(nil,ptr);
+ repeat inc(ptr) until ptr^=#0; inc(ptr);
+ end
+ else
+ begin
+ ext:=pdword(ptr)^ and $FFFFFF;
+ inc(ptr,3);
+ end;
+
+ if not ord(ptr^) in [0,3,4,6] then exit;
+ inc(ptr);
+ if (enc=0) or (enc=3) then
+ begin
+ while ptr^<>#0 do inc(ptr);
+ inc(ptr);
+ end
+ else
+ begin
+ while pWord(ptr)^<>0 do inc(ptr,2);
+ inc(ptr,2);
+ end;
+ dec(len,ptr-org);
+
+ if ext=0 then
+ ext:=GetImageType(pByte(ptr));
+ if ext<>0 then
+ begin
+ FastAnsiToWideBuf(PAnsiChar(@ext),pWideChar(@extw));
+ dst:=SaveTemporaryW(ptr,len,PWideChar(@extw));
+ end;
+end;
+
+function ID3v2_PreReadTag(var frm:tID3v2FrameHdr;var src:PAnsiChar;ver:integer):PAnsiChar;
+var
+ i:cardinal;
+ dst:PAnsiChar;
+begin
+ mGetMem(result,frm.Size);
+ if Unsync or ((frm.Flags and $0200)<>0) then
+ begin
+ dst:=result;
+ i:=0;
+ while i<frm.Size do
+ begin
+ dst^:=src^;
+ inc(src);
+ if (dst^=#$FF) and (src^=#0) then
+ begin
+ inc(src);
+ if ver=4 then inc(i);
+ end;
+ inc(dst);
+ inc(i);
+ end
+ end
+ else
+ begin
+ move(src^,result^,frm.Size);
+ inc(src,frm.Size);
+ end;
+end;
+
+procedure ID3v2_ReadTag2(ver:integer;tag:PAnsiChar;Size:integer;var Info:tSongInfo);
+type
+ a=array [0..3] of byte;
+var
+ Frm:tID3v2FrameHdr;
+ FrmOld:tID3v2FrameHdrOld;
+ tmp:integer;
+ ls:pWideChar;
+ lp:PAnsiChar;
+ ptr,buf:PAnsiChar;
+ fArtist,fTitle,fAlbum:integer;
+ enc:byte;
+begin
+ lp:=tag+Size;
+ fArtist:=0;
+ fTitle :=0;
+ fAlbum :=0;
+ while tag<lp do
+ begin
+ case ver of
+ 1,2: begin
+ move(tag^,FrmOld,SizeOf(FrmOld));
+ Frm.Flags:=0;
+ Frm.ID:=FrmOld.ID[0]+(FrmOld.ID[1] shl 8)+(FrmOld.ID[2] shl 16);
+ Frm.Size:=(FrmOld.Size[0] shl 16)+(FrmOld.Size[1] shl 8)+FrmOld.Size[2];
+ inc(tag,SizeOf(tID3v2FrameHdrOld));
+ end;
+ 3: begin
+ move(tag^,Frm,SizeOf(Frm));
+ Frm.Size:=BSwap(Frm.Size);
+ inc(tag,SizeOf(tID3v2FrameHdr));
+ end;
+ 4: begin
+ move(tag^,Frm,SizeOf(Frm));
+ Frm.Size:=ID3v2_Correct(Frm.Size);
+ inc(tag,SizeOf(tID3v2FrameHdr));
+ if (Frm.Flags and $0100)<>0 then
+ begin
+ Frm.Size:=ID3v2_Correct(pdword(tag)^);
+ inc(tag,4);
+ end;
+ end;
+ end;
+
+ if Frm.ID=0 then
+ break;
+ if Frm.Size=0 then
+ continue;
+ if (tag+Frm.Size)>lp then
+ break;
+ buf:=ID3v2_PreReadTag(Frm,tag,ver);
+
+ enc:=ord(buf^);
+ case enc of // set priority
+ 0: enc:=1;
+ 1,2: enc:=3;
+ 3: enc:=3; // or 2 if you want
+ end;
+ case Frm.ID of
+ frmUSLT,frmULT: ID3v2_CheckLyric(frmUSLT,Info.lyric,buf,Frm.Size);
+ frmSYLT,frmSLT: ID3v2_CheckLyric(frmSYLT,Info.lyric,buf,Frm.Size);
+ frmTXX,frmTXXX: ID3v2_CheckLyric(frmTXXX,Info.lyric,buf,Frm.Size);
+ frmAPIC,frmPIC: ID3v2_CheckCover(Frm.ID ,Info.cover,buf,Frm.Size);
+
+ frmTPE1,frmTP1: begin
+ if fArtist<(enc+10) then
+ begin
+ fArtist:=enc+10;
+ mFreeMem(Info.artist);
+ ID3v2_ReadTagStr(Info.artist,buf,Frm.Size);
+ end
+ end;
+ frmTIT2,frmTT2: begin
+ if fTitle<(enc+10) then
+ begin
+ fTitle:=enc+10;
+ mFreeMem(Info.title);
+ ID3v2_ReadTagStr(Info.title,buf,Frm.Size);
+ end
+ end;
+ frmTALB,frmTAL: begin
+ if fAlbum<(enc+10) then
+ begin
+ fAlbum:=enc+10;
+ mFreeMem(Info.album);
+ ID3v2_ReadTagStr(Info.album,buf,Frm.Size);
+ end
+ end;
+ frmTYER,frmTDRC,frmTYE: begin
+ if Info.year<>nil then
+ mFreeMem(Info.year);
+ ID3v2_ReadTagStr(Info.year,buf,Frm.Size);
+ end;
+
+ frmTOPE,frmTPE2,frmTOA,frmTPE4: begin
+ if fArtist<enc then
+ begin
+ fArtist:=enc;
+ mFreeMem(Info.artist);
+ ID3v2_ReadTagStr(Info.artist,buf,Frm.Size);
+ end;
+ end;
+ frmTIT1,frmTIT3,frmTT1: begin
+ if fTitle<enc then
+ begin
+ fTitle:=enc;
+ mFreeMem(Info.title);
+ ID3v2_ReadTagStr(Info.title,buf,Frm.Size);
+ end;
+ end;
+ frmTOAL: begin
+ if fAlbum<enc then
+ begin
+ fAlbum:=enc;
+ mFreeMem(Info.album);
+ ID3v2_ReadTagStr(Info.album,buf,Frm.Size);
+ end;
+ end;
+ frmTORY: begin
+ if Info.year=nil then ID3v2_ReadTagStr(Info.year,buf,Frm.Size);
+ end;
+
+ frmTCON,frmTCO: begin
+ if Info.genre=nil then
+ begin
+ ID3v2_ReadTagStr(Info.genre,buf,Frm.Size);
+
+ if Info.genre<>nil then
+ if Info.genre[0]='(' then
+ begin
+ tmp:=StrScanW(Info.genre,')')-Info.genre+1;
+ if tmp=integer(StrLenW(Info.genre)) then
+ begin
+ ls:=GenreName(StrToInt(Info.genre+1));
+ mFreeMem(Info.genre);
+ Info.genre:=ls;
+ end
+ else if tmp>0 then
+ StrCopyW(Info.genre,Info.genre+tmp);
+ end;
+ end;
+ end;
+ frmCOMM,frmCOM: begin //!!
+ if Info.comment=nil then
+ begin
+ ptr:=buf;
+ inc(ptr,3+1); // language
+ if (buf^=#0) or (buf^=#3) then
+ begin
+ while ptr^<>#0 do inc(ptr);
+ inc(ptr);
+ end
+ else
+ begin
+ while pWord(ptr)^<>0 do inc(ptr,2);
+ inc(ptr,2);
+ end;
+ dec(Frm.Size,ptr-buf);
+ ID3v2_ReadTagStr1(Info.comment,ptr,Frm.Size,ord(buf^));
+ end;
+ end;
+ frmTRCK,frmTRK: begin
+ if Info.track=0 then
+ begin
+ ID3v2_ReadTagStr(ls,buf,Frm.Size);
+ Info.track:=StrToInt(ls);
+ mFreeMem(ls);
+ end;
+ end;
+ end;
+ mFreeMem(buf);
+ end;
+end;
+
+function ReadID3v2(f:THANDLE; var Info:tSongInfo):longint;
+var
+ TagHdr:TID3v2TagHdr;
+ Tag2:PAnsiChar;
+ ExtTagSize:dword;
+begin
+ BlockRead(f,TagHdr,SizeOf(TagHdr));
+ if TagHdr.ID=TAG2Sign then
+ begin
+ TagHdr.TagSize:=ID3v2_Correct(TagHdr.TagSize);
+ Unsync:=(TagHdr.Flags and $80)<>0;
+ result:=TagHdr.TagSize;
+// if TagHdr.Version>2 then
+ begin
+ GetMem(Tag2,TagHdr.TagSize);
+ BlockRead(f,Tag2^,TagHdr.TagSize);
+ ID3v2_ReadTag2(TagHdr.Version,Tag2,TagHdr.TagSize,Info);
+ FreeMem(Tag2);
+ end;
+ if (TagHdr.Flags and ExtIDHdrMask)<>0 then
+ begin
+ BlockRead(f,ExtTagSize,SizeOf(ExtTagSize));
+ inc(result,4+ExtTagSize);
+ end;
+ if (TagHdr.Flags and FooterPresent)<>0 then
+ inc(result,10);
+ end
+ else
+ result:=0;
+ Seek(f,result);
+end;
+{$ENDIF}
diff --git a/plugins/Watrack/formats/tags.pas b/plugins/Watrack/formats/tags.pas
new file mode 100644
index 0000000000..fbe0576c59
--- /dev/null
+++ b/plugins/Watrack/formats/tags.pas
@@ -0,0 +1,21 @@
+unit tags;
+{$include compilers.inc}
+interface
+
+uses wat_api,windows;
+
+{$DEFINE Interface}
+{$include tag_id3v2.inc}
+{$include tag_id3v1.inc}
+{$include tag_apev2.inc}
+
+implementation
+
+uses common,io,utils;
+
+{$UNDEF Interface}
+{$include tag_id3v2.inc}
+{$include tag_id3v1.inc}
+{$include tag_apev2.inc}
+
+end. \ No newline at end of file
diff --git a/plugins/Watrack/global.pas b/plugins/Watrack/global.pas
new file mode 100644
index 0000000000..14d915a973
--- /dev/null
+++ b/plugins/Watrack/global.pas
@@ -0,0 +1,86 @@
+{WATrack global datas}
+unit Global;
+
+interface
+
+uses windows,messages,wat_api;
+
+const
+ hwndTooltip:HWND=0;
+
+var
+ UserCP:dword;
+
+const
+ DLGED_INIT = $1000; // dialog init, not activate Apply button
+
+const
+ dsWait = -1;
+ dsEnabled = 0;
+ dsTemporary = 1;
+ dsPermanent = 2;
+
+// --- type definition ---
+type
+ pwModule = ^twModule;
+ twModule = record
+ Next :pwModule;
+ Init :function(aGetStatus:boolean=false):integer;
+ DeInit :procedure(aSetDisable:boolean);
+ AddOption :function(var tmpl:pAnsiChar;var proc:pointer;var name:pAnsiChar):integer;
+ ModuleName:pWideChar;
+ ModuleStat:integer; // filling by the way
+ Button :HWND; // checkboxes for switch on/off
+// AddOption:function(parent:HWND;var Dlg:integer;var name:pWideChar):integer;
+ end;
+
+const
+ PluginName = 'Winamp Track';
+ PluginShort:PAnsiChar = 'WATrack';
+
+const
+ ModuleLink:pwModule=nil;
+
+const
+ DisablePlugin :integer=0;
+ hHookWATStatus:THANDLE=0;
+
+// --- global functions ---
+
+procedure MakeHint (wnd:HWND;id:integer;txt:pAnsiChar);
+procedure MakeHintW(wnd:HWND;id:integer;txt:pWideChar);
+
+implementation
+
+uses common,commctrl,mirutils,m_api;//,templates;
+
+procedure MakeHint(wnd:HWND;id:integer;txt:pAnsiChar);
+var
+ ti:TTOOLINFOW;
+begin
+// FillChar(ti,SizeOf(ti),0);
+ ti.cbSize :=sizeof(TTOOLINFOW);
+ ti.uFlags :=TTF_IDISHWND or TTF_SUBCLASS;
+ ti.hwnd :=wnd;
+ ti.hinst :=hInstance;
+ ti.uId :=GetDlgItem(wnd,id);
+ ti.lpszText:=TranslateA2W(txt);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+ mFreeMem(ti.lpszText);
+end;
+
+procedure MakeHintW(wnd:HWND;id:integer;txt:pWideChar);
+var
+ ti:TTOOLINFOW;
+begin
+// FillChar(ti,SizeOf(ti),0);
+ ti.cbSize :=sizeof(TTOOLINFOW);
+ ti.uFlags :=TTF_IDISHWND or TTF_SUBCLASS;
+ ti.hwnd :=wnd;
+ ti.hinst :=hInstance;
+ ti.uId :=GetDlgItem(wnd,id);
+ ti.lpszText:=TranslateW(txt);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+end;
+
+end.
diff --git a/plugins/Watrack/i_cover.inc b/plugins/Watrack/i_cover.inc
new file mode 100644
index 0000000000..48f515882a
--- /dev/null
+++ b/plugins/Watrack/i_cover.inc
@@ -0,0 +1,90 @@
+{any utils}
+function GetCover(var dst:pWideChar;mfile:pWideChar):boolean;
+var
+ line,line1:array [0..511] of WideChar;
+ p,p1:PWideChar;
+ i:integer;
+ fdata:WIN32_FIND_DATAW;
+ hTmp:THANDLE;
+ wr,wr1:pWideChar;
+begin
+ result:=false;
+ dst:=nil;
+ if (CoverPaths=nil) or (CoverPaths^=#0) then exit;
+ p:=CoverPaths;
+ repeat
+ p1:=p;
+ while p^>=' ' do inc(p);
+ i:=p-p1;
+ if i>0 then
+ begin
+ move(p1^,line,i*SizeOf(WideChar));
+ line[i]:=#0;
+ if ServiceExists(MS_WAT_REPLACETEXT)<>0 then
+ wr:=pWideChar(CallService(MS_WAT_REPLACETEXT,0,lparam(@line)))
+ else
+ wr:=@line;
+
+ if isPathAbsolute(wr) then
+ begin
+ hTmp:=FindFirstFileW(wr,fdata);
+ end
+ else
+ begin
+ wr1:=ExtractW(mfile,false);
+ StrCopyW(line,wr1);
+ mFreeMem(wr1);
+ StrCatW(line,wr);
+ hTmp:=FindFirstFileW(line,fdata);
+ end;
+ if dword(hTmp)<>INVALID_HANDLE_VALUE then
+ begin
+ wr1:=ExtractW(line,false);
+ StrCopyW(line1,wr1);
+ mFreeMem(wr1);
+ StrCatW(line1,pWideChar(@fdata.cFileName));
+
+ GetFullPathNameW(line1,SizeOf(line) div SizeOf(WideChar),line,wr1);
+ StrDupW(dst,line);
+ result:=true;
+ FindClose(hTmp); //!!
+ end;
+ if wr<>@line then mFreeMem(wr);
+ if result then break;
+ end;
+ while p^<' ' do
+ begin
+ if p^=#0 then break;
+ inc(p);
+ end;
+ until p^=#0;
+end;
+
+function GetLyric(var dst:pWideChar;mfile:pWideChar):boolean;
+var
+ buf:array [0..511] of WideChar;
+ f:THANDLE;
+ size:integer;
+ tmp:PAnsiChar;
+begin
+ StrCopyW(buf,mfile);
+ ChangeExtW(buf,'txt');
+ f:=Reset(buf);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ begin
+ dst:=nil;
+ result:=false;
+ exit;
+ end;
+ size:=FileSize(f);
+ if size>0 then
+ begin
+ mGetMem(tmp,size+1);
+ BlockRead(f,tmp^,size);
+ tmp[size]:=#0;
+ AnsiToWide(tmp,dst);
+ mFreeMem(tmp);
+ end;
+ CloseHandle(f);
+ result:=true;
+end;
diff --git a/plugins/Watrack/i_gui.inc b/plugins/Watrack/i_gui.inc
new file mode 100644
index 0000000000..dc79632b1e
--- /dev/null
+++ b/plugins/Watrack/i_gui.inc
@@ -0,0 +1,114 @@
+{some visual stuff}
+
+function OnTTBLoaded(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ ttb:m_api.TTBButton;
+begin
+ UnhookEvent(onloadhook);
+
+ FillChar(ttb,SizeOf(ttb),0);
+ ttb.cbSize :=SizeOf(ttb);
+ ttb.dwFlags:=TTBBF_VISIBLE or TTBBF_SHOWTOOLTIP;
+
+ // plugin status button
+ if DisablePlugin<>dsPermanent then
+ ttb.dwFlags:=ttb.dwFlags or TTBBF_PUSHED;
+
+ ttb.hIconDn :=CallService(MS_SKIN2_GETICON,0,tlparam(IcoBtnEnable));
+ ttb.hIconUp :=CallService(MS_SKIN2_GETICON,0,tlparam(IcoBtnDisable));
+ ttb.wParamUp :=1;
+// ttb.wParamDown :=0;
+ ttb.pszService:=MS_WAT_PLUGINSTATUS;
+ ttb.name :='WATrack status';
+// ttb.tooltipDn:='Disable Plugin';
+// ttb.tooltipUp:='Enable Plugin';
+ ttbState:=TopToolbar_AddButton(@ttb);
+ if ttbState=THANDLE(-1) then
+ ttbState:=0
+ else
+ CallService(MS_TTB_SETBUTTONOPTIONS,(ttbState shl 16)+TTBO_TIPNAME,
+ tlparam(Translate('Disable Plugin')));
+ result:=0;
+end;
+
+procedure CreateMenus;
+var
+ mi:TCListMenuItem;
+begin
+ FillChar(mi, sizeof(mi), 0);
+ mi.cbSize :=sizeof(mi);
+ mi.szPopupName.a:=PluginShort;
+
+ mi.hIcon :=CallService(MS_SKIN2_GETICON,0,lparam(IcoBtnEnable));
+ mi.szName.a :='Disable Plugin';
+ mi.pszService :=MS_WAT_PLUGINSTATUS;
+ mi.popupPosition:=MenuDisablePos;
+ hMenuDisable:=Menu_AddMainMenuItem(@mi);
+end;
+
+procedure ChangeMenuIcons(f1:cardinal);
+var
+ mi:tClistMenuItem;
+ p:PAnsiChar;
+begin
+ FillChar(mi,sizeof(mi),0);
+ mi.cbSize:=sizeof(mi);
+ mi.flags :=CMIM_NAME+CMIM_FLAGS+CMIM_ICON+f1;
+ if f1<>0 then
+ begin
+ mi.hIcon :=CallService(MS_SKIN2_GETICON,0,lparam(IcoBtnDisable));
+ mi.szName.a:='Enable Plugin';
+ end
+ else
+ begin
+ mi.hIcon :=CallService(MS_SKIN2_GETICON,0,lparam(IcoBtnEnable));
+ mi.szName.a:='Disable Plugin';
+ end;
+ CallService(MS_CLIST_MODIFYMENUITEM,hMenuDisable,lparam(@mi));
+
+ if ServiceExists(MS_TTB_SETBUTTONSTATE)<>0 then
+ begin
+ if f1<>0 then
+ begin
+ p:='Enable Plugin';
+ CallService(MS_TTB_SETBUTTONSTATE,ttbState,TTBST_RELEASED)
+ end
+ else
+ begin
+ p:='Disable Plugin';
+ CallService(MS_TTB_SETBUTTONSTATE,ttbState,TTBST_PUSHED);
+ end;
+ CallService(MS_TTB_SETBUTTONOPTIONS,(ttbState shl 16)+TTBO_TIPNAME,
+ lparam(Translate(p)));
+ end;
+end;
+
+function IconChanged(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ mi:TCListMenuItem;
+ ttb:m_api.TTBButton;
+begin
+ result:=0;
+ FillChar(mi,SizeOf(mi),0);
+ mi.cbSize:=sizeof(mi);
+ mi.flags :=CMIM_ICON;
+
+ mi.hIcon:=CallService(MS_SKIN2_GETICON,0,tlparam(IcoBtnEnable));
+ CallService(MS_CLIST_MODIFYMENUITEM,hMenuDisable,tlparam(@mi));
+
+// toptoolbar
+ if ServiceExists(MS_TTB_GETBUTTONOPTIONS)<>0 then
+ begin
+{
+ CallService(MS_TTB_GETBUTTONOPTIONS,(ttbInfo shl 16)+TTBO_ALLDATA,tlparam(@ttb));
+ ttb.hIconUp:=CallService(MS_SKIN2_GETICON,0,tlparam(IcoBtnInfo));
+ ttb.hIconDn:=ttb.hIconUp;
+ CallService(MS_TTB_SETBUTTONOPTIONS,(ttbInfo shl 16)+TTBO_ALLDATA,tlparam(@ttb));
+}
+ CallService(MS_TTB_GETBUTTONOPTIONS,(ttbState shl 16)+TTBO_ALLDATA,tlparam(@ttb));
+ ttb.hIconDn:=CallService(MS_SKIN2_GETICON,0,tlparam(IcoBtnEnable));
+ ttb.hIconUp:=CallService(MS_SKIN2_GETICON,0,tlparam(IcoBtnDisable));
+ CallService(MS_TTB_SETBUTTONOPTIONS,(ttbState shl 16)+TTBO_ALLDATA,tlparam(@ttb));
+ end;
+
+end;
diff --git a/plugins/Watrack/i_opt_0.inc b/plugins/Watrack/i_opt_0.inc
new file mode 100644
index 0000000000..ab17d6ec0d
--- /dev/null
+++ b/plugins/Watrack/i_opt_0.inc
@@ -0,0 +1,91 @@
+{special tab: parts settings}
+
+function DlgProcOptions0(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):LRESULT; stdcall;
+const
+ hasApply:boolean=false;
+var
+ i:integer;
+ ptr:pwModule;
+ wnd:HWND;
+ rc:TRECT;
+begin
+ result:=0;
+ case hMessage of
+ WM_DESTROY: begin
+ if hasApply then
+ begin
+ ptr:=ModuleLink;
+ while ptr<>nil do
+ begin
+ if ptr^.ModuleName<>nil then
+ begin
+ i:=SendMessageW(ptr^.Button,BM_GETCHECK,0,0);
+ if (i=BST_CHECKED) xor (ptr^.ModuleStat<>0) then
+ begin
+ if i=BST_CHECKED then
+ begin
+ ptr^.ModuleStat:=1;
+ if @ptr^.Init<>nil then
+ if ptr^.Init(false)=0 then
+ ptr^.ModuleStat:=0;
+ end
+ else
+ begin
+ ptr^.ModuleStat:=0;
+ if @ptr^.DeInit<>nil then
+ ptr^.DeInit(true);
+ end;
+ end;
+// if ptr^.ModuleStat then
+ end;
+ ptr:=ptr^.Next;
+ end;
+ end;
+ end;
+
+ WM_INITDIALOG: begin
+
+ hasApply:=false;
+
+ ptr:=ModuleLink;
+ i:=0;
+ while ptr<>nil do
+ begin
+ if ptr^.ModuleName<>nil then
+ begin
+ ptr^.Button:=CreateWindowW('BUTTON',TranslateW(ptr^.ModuleName),
+ WS_CHILD+WS_VISIBLE+BS_AUTOCHECKBOX,
+ 14,20+i*20,150,14,Dialog,0,hInstance,nil);
+ SendMessageW(ptr^.Button,WM_SETFONT,GetStockObject(DEFAULT_GUI_FONT),0);
+ if ptr^.ModuleStat<>0 then
+ SendMessageW(ptr^.Button,BM_SETCHECK,BST_CHECKED,0);
+ inc(i);
+ end;
+ ptr:=ptr^.Next;
+ end;
+ if i>0 then
+ begin
+ wnd:=GetDlgItem(Dialog,IDC_MODULEGROUP);
+ GetWindowRect(wnd,rc);
+ SetWindowPos (wnd,0,0,0,rc.Right-rc.Left,(i+1)*20,
+ SWP_NOMOVE+SWP_NOZORDER+SWP_NOACTIVATE);
+ end;
+
+ TranslateDialogDefault(Dialog);
+ end;
+
+ WM_COMMAND: begin
+ if (wParam shr 16)=BN_CLICKED then
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+
+ WM_NOTIFY: begin
+ if integer(PNMHdr(lParam)^.code)=PSN_APPLY then
+ begin
+ hasApply:=true;
+ end;
+ end;
+ else
+ {result:=}DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+end;
diff --git a/plugins/Watrack/i_opt_1.inc b/plugins/Watrack/i_opt_1.inc
new file mode 100644
index 0000000000..a41d07d554
--- /dev/null
+++ b/plugins/Watrack/i_opt_1.inc
@@ -0,0 +1,256 @@
+{common options}
+const
+ ChkFmtStr:array [0..3] of pWideChar = ('None','Audio','Video','All');
+
+type
+ TCPTABLE = packed record
+ cpId :cardinal;
+ cpName:PAnsiChar;
+ end;
+const
+ cpNum = 15;
+ cpTable:array [0..cpNum-1] of TCPTABLE = (
+ (cpId: 874;cpName:'Thai'),
+ (cpId: 932;cpName:'Japanese'),
+ (cpId: 936;cpName:'Simplified Chinese'),
+ (cpId: 949;cpName:'Korean'),
+ (cpId: 950;cpName:'Traditional Chinese'),
+ (cpId:1250;cpName:'Central European'),
+ (cpId:1251;cpName:'Cyrillic'),
+ (cpId:1252;cpName:'Latin I'),
+ (cpId:1253;cpName:'Greek'),
+ (cpId:1254;cpName:'Turkish'),
+ (cpId:1255;cpName:'Hebrew'),
+ (cpId:1256;cpName:'Arabic'),
+ (cpId:1257;cpName:'Baltic'),
+ (cpId:1258;cpName:'Vietnamese'),
+ (cpId:1361;cpName:'Korean (Johab)'));
+
+var
+ hCpCombo:hwnd;
+
+function FillCpCombo(astr:PAnsiChar):boolean; stdcall;
+var
+ i:integer;
+ cp:cardinal;
+ iIndex:integer;
+ buf:array [0..63] of WideChar;
+begin
+ result:=true; // MUST be at start
+ cp:=StrToInt(astr);
+ i:=0;
+ while i<cpNum do
+ begin
+ if cpTable[i].cpId=cp then
+ begin
+ iIndex:=SendMessageW(hCpCombo,CB_ADDSTRING,0,
+ lparam(TranslateW(FastAnsiToWideBuf(cpTable[i].cpName,buf))));
+ SendMessage(hCpCombo,CB_SETITEMDATA,iIndex,cpTable[i].cpId);
+ break;
+ end;
+ inc(i);
+ end;
+end;
+
+function DlgProcOptions1(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+const
+ btnChkPlayer:bool=false;
+ btnChkFormat:integer=0;
+var
+ tmp:longbool;
+ i,j:cardinal;
+ wnd:HWND;
+ pldescr:array [0..27] of AnsiChar;
+ pldescw:array [0..27] of WideChar;
+ p:pWideChar;
+begin
+ result:=0;
+ case hMessage of
+ WM_INITDIALOG: begin
+ SetWindowTextW(GetDlgItem(Dialog,IDC_CHK_PLAYER),'None');
+ SetWindowTextW(GetDlgItem(Dialog,IDC_CHK_FORMAT),ChkFmtStr[0]);
+ TranslateDialogDefault(Dialog);
+ DefFillPlayerList(GetDlgItem(Dialog,IDC_PLAYERLIST));
+ DefFillFormatList(GetDlgItem(Dialog,IDC_FORMATLIST));
+
+ i:=mTimer;
+ if (i>=1000) and (i mod 1000=0) then
+ i:=i div 1000;
+
+ MakeHint(Dialog,IDC_TIMER,
+ 'Refresh time (sec) is time to refresh music info, statistic and'+
+ ' status messages. If zero, automatic refresh is disabled. If '+
+ 'value greater than 499, time signify as milliseconds.');
+ SetDlgItemInt(Dialog,IDC_TIMER,i,false);
+
+ MakeHint(Dialog,IDC_CHECKTIME,
+ 'Check file date and time to tag updates while playing.');
+ CheckDlgButton(Dialog,IDC_CHECKTIME,CheckTime);
+
+ MakeHint(Dialog,IDC_IMPLANTANT,
+ 'Use player process injection to obtain info easier. Can provoke antivirus '+
+ 'or firewall alarm.');
+ CheckDlgButton(Dialog,IDC_IMPLANTANT,UseImplant);
+
+ MakeHint(Dialog,IDC_MTHCHECK,
+ 'Use this option if WATrack freeze while player running. Slower processing.');
+ CheckDlgButton(Dialog,IDC_MTHCHECK,MTHCheck);
+
+ MakeHint(Dialog,IDC_TIMEOUT,
+ 'Timeout (msec) for separate thread handles checking.');
+ SetDlgItemInt(Dialog,IDC_TIMEOUT,TimeoutForThread,false);
+
+ MakeHint(Dialog,IDC_KEEPOLD,
+ 'Keep opened file as active, not newly founded.');
+ CheckDlgButton(Dialog,IDC_KEEPOLD,KeepOld);
+
+ MakeHint(Dialog,IDC_CHECKALL,
+ 'Check all marked players for active (started and playing) or stop at first founded');
+ CheckDlgButton(Dialog,IDC_CHECKALL,CheckAll);
+
+// MakeHint(Dialog,IDC_COVERFN,
+// 'Cover filename searching templates');
+ SetDlgItemTextW(Dialog,IDC_COVERFN,CoverPaths);
+
+ MakeHint(Dialog,IDC_APPCOMMAND,
+ 'Emulate multimedia keys presses to control palyer');
+ CheckDlgButton(Dialog,IDC_APPCOMMAND,mmkeyemu);
+
+ hCpCombo:=GetDlgItem(Dialog,IDC_CODEPAGE);
+ EnumSystemCodePages(@FillCpCombo,CP_INSTALLED);
+ SendDlgItemMessageW(Dialog,IDC_CODEPAGE,CB_INSERTSTRING,0,
+ tlparam(TranslateW('System default codepage')));
+
+ CB_SelectData(Dialog,IDC_CODEPAGE,UserCP);
+
+ if UserCP=0 then
+ i:=0
+ else
+ begin
+ i:=SendDlgItemMessage(Dialog,IDC_CODEPAGE,CB_GETCOUNT,0,0)-1;
+ while i>0 do
+ begin
+ if dword(SendDlgItemMessage(Dialog,IDC_CODEPAGE,CB_GETITEMDATA,dword(i),0))=UserCP then
+ break;
+ dec(i);
+ end
+ end;
+ SendDlgItemMessage(Dialog,IDC_CODEPAGE,CB_SETCURSEL,i,0);
+
+ result:=0;
+ end;
+
+ WM_HELP: begin
+ with pHelpInfo(lParam)^ do
+ begin
+ if (iContextType=HELPINFO_WINDOW) and (iCtrlId=IDC_PLAYERLIST) then
+ begin
+ ListView_GetItemTextA(hItemHandle,
+ SendMessage(hItemHandle,LVM_GETNEXTITEM,-1,LVNI_FOCUSED),0,
+ @pldescr,HIGH(pldescr));
+ p:=GetPlayerNote(pldescr);
+ if p=nil then
+ p:='No any special notes for this player';
+ MessageBoxW(0,TranslateW(p),FastAnsiToWideBuf(pldescr,pldescw),0);
+ end;
+ end;
+ end;
+
+ WM_COMMAND: begin
+ case wParam shr 16 of
+ EN_CHANGE,
+ BN_CLICKED,
+ CBN_SELCHANGE: begin
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ result:=1;
+ end;
+ end;
+ if (wParam shr 16)=BN_CLICKED then
+ begin
+ case loword(wParam) of
+ IDC_CHK_PLAYER: begin
+ btnChkPlayer:=not btnChkPlayer;
+ if btnChkPlayer then
+ begin
+ SetWindowTextW(GetDlgItem(Dialog,IDC_CHK_PLAYER),TranslateW('All'));
+ tmp:=false;
+ end
+ else
+ begin
+ SetWindowTextW(GetDlgItem(Dialog,IDC_CHK_PLAYER),TranslateW('None'));
+ tmp:=true;
+ end;
+ wnd:=GetDlgItem(Dialog,IDC_PLAYERLIST);
+ j:=ListView_GetItemCount(wnd)-1;
+ for i:=0 to j do
+ ListView_SetCheckState(wnd,i,tmp);
+ end;
+
+ IDC_CHK_FORMAT: begin
+ wnd:=GetDlgItem(Dialog,IDC_FORMATLIST);
+ j:=ListView_GetItemCount(wnd)-1;
+
+ tmp:=btnChkFormat=3;
+ for i:=0 to j do
+ begin
+ if (btnChkFormat=1) or (btnChkFormat=2) then
+ begin
+ if (LV_GetLParam(wnd,i) and WAT_OPT_VIDEO)<>0 then
+ tmp:=btnChkFormat=2
+ else
+ tmp:=btnChkFormat=1;
+ end;
+ ListView_SetCheckState(wnd,i,tmp);
+ end;
+ inc(btnChkFormat);
+ if btnChkFormat=4 then btnChkFormat:=0;
+ SetWindowTextW(GetDlgItem(Dialog,IDC_CHK_FORMAT),TranslateW(ChkFmtStr[btnChkFormat]));
+ end;
+ end;
+ end;
+ end;
+
+ WM_NOTIFY: begin
+ case integer(PNMHdr(lParam)^.code) of
+ LVN_ITEMCHANGED: begin
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+
+ PSN_APPLY: begin
+ UserCP:=CB_GetData(GetDlgItem(Dialog,IDC_CODEPAGE));
+ i:=GetDlgItemInt(Dialog,IDC_TIMER,tmp,false);
+ if i<500 then
+ i:=i*1000;
+ if i<>mTimer then
+ begin
+ mTimer:=i;
+ StopTimer;
+ StartTimer;
+ end;
+
+ TimeoutForThread:=GetDlgItemInt(Dialog,IDC_TIMEOUT,tmp,false);
+ if TimeoutForThread>=100 then
+ TimeoutForThread:=SysWin.ThreadTimeout;
+
+ CheckTime :=IsDlgButtonChecked(Dialog,IDC_CHECKTIME);
+ UseImplant:=IsDlgButtonChecked(Dialog,IDC_IMPLANTANT);
+ MTHCheck :=IsDlgButtonChecked(Dialog,IDC_MTHCHECK);
+ KeepOld :=IsDlgButtonChecked(Dialog,IDC_KEEPOLD);
+ mmkeyemu :=IsDlgButtonChecked(Dialog,IDC_APPCOMMAND);
+ CheckAll :=IsDlgButtonChecked(Dialog,IDC_CHECKALL);
+
+ mFreeMem(CoverPaths);
+ CoverPaths:=GetDlgText(Dialog,IDC_COVERFN);
+
+ DefCheckPlayerList(GetDlgItem(Dialog,IDC_PLAYERLIST));
+ DefCheckFormatList(GetDlgItem(Dialog,IDC_FORMATLIST));
+ saveopt;
+
+ result:=1;
+ end;
+ end;
+ end;
+ else
+ {result:=}DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+end;
diff --git a/plugins/Watrack/i_opt_dlg.inc b/plugins/Watrack/i_opt_dlg.inc
new file mode 100644
index 0000000000..e97a3df5f5
--- /dev/null
+++ b/plugins/Watrack/i_opt_dlg.inc
@@ -0,0 +1,57 @@
+{$include i_opt_0.inc}
+{$include i_opt_1.inc}
+
+function OnOptInitialise(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ odp:TOPTIONSDIALOGPAGE;
+ ptr:pwModule;
+ tmpl:pAnsiChar;
+ name:pansiChar;
+ proc:pointer;
+ i:integer;
+begin
+ if hwndTooltip<>0 then
+ DestroyWindow(hwndTooltip);
+ hwndTooltip:=CreateWindowW(TOOLTIPS_CLASS,nil,TTS_ALWAYSTIP,
+ integer(CW_USEDEFAULT),integer(CW_USEDEFAULT),
+ integer(CW_USEDEFAULT),integer(CW_USEDEFAULT),
+ 0,0,hInstance,nil);
+
+ SendMessage(hwndTooltip,TTM_SETMAXTIPWIDTH,0,300);
+
+ FillChar(odp,SizeOf(odp),0);
+ odp.cbSize :=SizeOf(odp);
+ odp.Position :=900003000;
+ odp.hInstance :=hInstance;
+ odp.szTitle.a :=PluginName;
+ odp.szGroup.a :='Plugins';
+
+ odp.flags :=ODPF_BOLDGROUPS or ODPF_EXPERTONLY;
+ odp.pszTemplate:='PARTS';
+ odp.pfnDlgProc :=@DlgProcOptions0;
+ odp.szTab.a :='Modules';
+ CallService(MS_OPT_ADDPAGE,wParam,tlparam(@odp));
+
+ odp.flags :=ODPF_BOLDGROUPS;
+ odp.pszTemplate:='BASIC';
+ odp.pfnDlgProc :=@DlgProcOptions1;
+ odp.szTab.a :='Basic';
+ CallService(MS_OPT_ADDPAGE,wParam,tlparam(@odp));
+
+ ptr:=ModuleLink;
+ while ptr<>nil do
+ begin
+ if (ptr^.ModuleStat<>0) and (@ptr^.AddOption<>nil) then
+ begin
+ i:=ptr^.AddOption(tmpl,proc,name);
+ odp.pszTemplate:=tmpl;
+ odp.pfnDlgProc :=proc;
+ odp.szTab.a :=name;
+ CallService(MS_OPT_ADDPAGE,wParam,tlparam(@odp));
+ if i>0 then continue;
+ end;
+ ptr:=ptr^.Next;
+ end;
+
+ result:=0;
+end;
diff --git a/plugins/Watrack/i_options.inc b/plugins/Watrack/i_options.inc
new file mode 100644
index 0000000000..060751966c
--- /dev/null
+++ b/plugins/Watrack/i_options.inc
@@ -0,0 +1,171 @@
+{Database options}
+const
+ DefaultXStatus = $080B; // TV + Music
+const
+ defhotkey = (HOTKEYF_CONTROL+HOTKEYF_ALT )*256+VK_F5;
+ definshotkey = (HOTKEYF_CONTROL+HOTKEYF_SHIFT)*256+VK_F7;
+
+const
+ defcoverpaths = 'cover.jpg'#13#10'..\cover.jpg'#13#10'*.jpg'#13#10'..\*.jpg';
+
+const
+ WATFormats:PAnsiChar = 'formats/';
+const
+ WATPlayers :PAnsiChar = 'players/';
+
+const
+ opt_disable :PAnsiChar = 'disableplugin';
+
+ opt_InsHotKey :PAnsiChar = 'inshotkey';
+ opt_HotKey :PAnsiChar = 'hotkey';
+ opt_Timer :PAnsiChar = 'timer';
+ opt_UserCP :PAnsiChar = 'usercp';
+ opt_CheckTime :PAnsiChar = 'checktime';
+ opt_coverpaths:PAnsiChar = 'coverpaths';
+ opt_Implantant:PAnsiChar = 'useimplantant';
+ opt_MTHCheck :PAnsiChar = 'mthcheck';
+ opt_KeepOld :PAnsiChar = 'keepold';
+ opt_mmkeyemu :PAnsiChar = 'mmkeyemu';
+ opt_CheckAll :PAnsiChar = 'checkall';
+
+ opt_ThTimeout :PAnsiChar = 'thtimeout';
+
+procedure _loadopt;
+begin
+ DisablePlugin:=DBReadByte(0,PluginShort,opt_disable,0);
+ if DisablePlugin<>dsPermanent then
+ DisablePlugin:=dsEnabled;
+
+ inshotkey :=DBReadWord(0,PluginShort,opt_InsHotKey ,definshotkey);
+ globhotkey :=DBReadWord(0,PluginShort,opt_HotKey ,defhotkey);
+ CheckTime :=DBReadByte(0,PluginShort,opt_CheckTime ,BST_CHECKED);
+ UseImplant :=DBReadByte(0,PluginShort,opt_Implantant,BST_UNCHECKED);
+ MTHCheck :=DBReadByte(0,PluginShort,opt_MTHCheck ,BST_CHECKED);
+ KeepOld :=DBReadByte(0,PluginShort,opt_KeepOld ,BST_UNCHECKED);
+ CheckAll :=DBReadByte(0,PluginShort,opt_CheckAll ,BST_UNCHECKED);
+ mTimer :=DBReadWord(0,PluginShort,opt_Timer ,3000);
+ if mTimer<500 then
+ mTimer:=mTimer*1000;
+ UserCP :=DBReadWord(0,PluginShort,opt_UserCP ,CP_ACP);
+ CoverPaths :=DBReadUnicode(0,PluginShort,opt_coverpaths,defcoverpaths);
+
+ mmkeyemu :=DBReadByte (0,PluginShort,opt_mmkeyemu ,BST_UNCHECKED);
+
+ TimeoutForThread:=DBReadByte(0,PluginShort,opt_ThTimeout,SysWin.ThreadTimeout);
+end;
+
+procedure _saveopt;
+begin
+ DBWriteWord(0,PluginShort,opt_InsHotKey ,inshotkey);
+ DBWriteWord(0,PluginShort,opt_HotKey ,globhotkey);
+ DBWriteByte(0,PluginShort,opt_ThTimeout ,TimeoutForThread);
+ DBWriteByte(0,PluginShort,opt_CheckTime ,CheckTime);
+ DBWriteByte(0,PluginShort,opt_Implantant,UseImplant);
+ DBWriteByte(0,PluginShort,opt_MTHCheck ,MTHCheck);
+ DBWriteByte(0,PluginShort,opt_KeepOld ,KeepOld);
+ DBWriteByte(0,PluginShort,opt_CheckAll ,CheckAll);
+ DBWriteWord(0,PluginShort,opt_Timer ,mTimer);
+ DBWriteWord(0,PluginShort,opt_UserCP ,UserCP);
+
+ DBWriteUnicode(0,PluginShort,opt_coverpaths,CoverPaths);
+
+ DBWriteByte (0,PluginShort,opt_mmkeyemu ,mmkeyemu);
+end;
+
+function enumwp(desc:PAnsiChar;lParam:LPARAM):bool; stdcall;
+var
+ i:integer;
+ buf:array [0..63] of AnsiChar;
+begin
+ i:=CallService(MS_WAT_PLAYER,WAT_ACT_GETSTATUS,tlparam(desc));
+ if i=WAT_RES_ENABLED then
+ i:=1
+ else
+ i:=0;
+ StrCopy(StrCopyE(buf,WATPlayers),desc);
+ DBWriteByte(0,PluginShort,buf,i);
+ result:=true;
+end;
+
+procedure WritePlayers;
+begin
+ EnumPlayers(@enumwp,0);
+end;
+
+function enumrp(desc:PAnsiChar;lParam:LPARAM):bool; stdcall;
+var
+ i:integer;
+ buf:array [0..63] of AnsiChar;
+begin
+ StrCopy(StrCopyE(buf,WATPlayers),desc);
+ i:=DBReadByte(0,PluginShort,buf,1);
+ if i=1 then
+ i:=WAT_ACT_ENABLE
+ else
+ i:=WAT_ACT_DISABLE;
+ CallService(MS_WAT_PLAYER,i,tlparam(desc));
+ result:=true;
+end;
+
+procedure ReadPlayers;
+begin
+ EnumPlayers(@enumrp,0);
+{!! p:=DBReadString(0,PluginShort,opt_DefPlayer,nil);
+ CallService(MS_WAT_PLAYER,WAT_ACT_SETACTIVE,dword(p));
+ mFreeMem(p);
+}
+end;
+
+function enumwf(ext:PAnsiChar;lParam:LPARAM):bool; stdcall;
+var
+ i:integer;
+ buf:array [0..63] of AnsiChar;
+begin
+ i:=CallService(MS_WAT_FORMAT,WAT_ACT_GETSTATUS,tlparam(ext));
+ if i=WAT_RES_ENABLED then
+ i:=1
+ else
+ i:=0;
+ StrCopy(StrCopyE(buf,WATFormats),ext);
+ DBWriteByte(0,PluginShort,buf,i);
+ result:=true;
+end;
+
+procedure WriteFormats;
+begin
+ EnumFormats(@enumwf,0);
+end;
+
+function enumrf(ext:PAnsiChar;lParam:LPARAM):bool; stdcall;
+var
+ i:integer;
+ buf:array [0..63] of AnsiChar;
+begin
+ StrCopy(StrCopyE(buf,WATFormats),ext);
+ i:=DBReadByte(0,PluginShort,buf,1);
+ if i=1 then
+ i:=WAT_ACT_ENABLE
+ else
+ i:=WAT_ACT_DISABLE;
+ CallService(MS_WAT_FORMAT,i,tlparam(ext));
+ result:=true;
+end;
+
+procedure ReadFormats;
+begin
+ EnumFormats(@enumrf,0);
+end;
+
+procedure saveopt;
+begin
+ _saveopt;
+ WriteFormats;
+ WritePlayers;
+end;
+
+procedure loadopt;
+begin
+ _loadopt;
+ ReadPlayers;
+ ReadFormats;
+end;
diff --git a/plugins/Watrack/i_timer.inc b/plugins/Watrack/i_timer.inc
new file mode 100644
index 0000000000..f37092291b
--- /dev/null
+++ b/plugins/Watrack/i_timer.inc
@@ -0,0 +1,26 @@
+{Timer related procedures}
+
+procedure TimerProc(wnd:HWND;uMsg:uint;idEvent:uint_ptr;dwTime:dword); stdcall;
+begin
+ case DisablePlugin of
+ dsEnabled : CallService(MS_WAT_GETMUSICINFO,WAT_INF_CHANGES,0);
+ dsTemporary: DisablePlugin:=dsWait;
+ end;
+end;
+
+procedure StartTimer;
+begin
+ if mTimer>0 then
+ hTimer:=SetTimer(0,0,mTimer,@TimerProc)
+ else
+ hTimer:=0;
+end;
+
+procedure StopTimer;
+begin
+ if hTimer<>0 then
+ begin
+ KillTimer(0,hTimer);
+ hTimer:=0;
+ end;
+end;
diff --git a/plugins/Watrack/i_vars.inc b/plugins/Watrack/i_vars.inc
new file mode 100644
index 0000000000..955170fdfa
--- /dev/null
+++ b/plugins/Watrack/i_vars.inc
@@ -0,0 +1,37 @@
+{variables}
+var
+ SongInfoA:tSongInfoA;
+ SongInfo :tSongInfo;
+ WorkSI :tSongInfo;
+var
+ hEvent:THANDLE;
+ hGFI,
+ hWI,
+ hGMI,
+ hPS,
+ hPB,
+ hWATI,
+ hWC,
+ hFMT,
+ hPLR,
+ hRGS,
+ wsic,
+ hHookWATLoaded:THANDLE;
+ opthook:cardinal;
+ onloadhook:cardinal;
+ hHookShutdown:cardinal;
+ inshotkey:cardinal;
+ globhotkey:cardinal;
+ hTimer:cardinal;
+ ttbState,
+ hMenuDisable:THANDLE;
+var
+ CoverPaths:PWideChar;
+ MTHCheck,
+ KeepOld,
+ UseImplant,
+ CheckAll,
+ CheckTime:dword;
+ mmkeyemu: dword;
+ mTimer:dword;
+ TimeoutForThread:cardinal;
diff --git a/plugins/Watrack/icons/GO/GoAsm.Exe b/plugins/Watrack/icons/GO/GoAsm.Exe
new file mode 100644
index 0000000000..cb0f7e8c92
--- /dev/null
+++ b/plugins/Watrack/icons/GO/GoAsm.Exe
Binary files differ
diff --git a/plugins/Watrack/icons/GO/GoLink.exe b/plugins/Watrack/icons/GO/GoLink.exe
new file mode 100644
index 0000000000..01e2964d6d
--- /dev/null
+++ b/plugins/Watrack/icons/GO/GoLink.exe
Binary files differ
diff --git a/plugins/Watrack/icons/GO/GoRC.exe b/plugins/Watrack/icons/GO/GoRC.exe
new file mode 100644
index 0000000000..10ea26c30f
--- /dev/null
+++ b/plugins/Watrack/icons/GO/GoRC.exe
Binary files differ
diff --git a/plugins/Watrack/icons/GO/icons.bat b/plugins/Watrack/icons/GO/icons.bat
new file mode 100644
index 0000000000..e45dfd4ab9
--- /dev/null
+++ b/plugins/Watrack/icons/GO/icons.bat
@@ -0,0 +1,8 @@
+:@echo off
+GoRC /r /d incpath="%2" icons.rc
+:GoRC /r icons.rc
+GoAsm watrack_buttons.asm
+GoLink /dll watrack_buttons.obj icons.res
+del *.obj
+del *.res
+move watrack_buttons.dll ..\..\..\bin \ No newline at end of file
diff --git a/plugins/Watrack/icons/GO/icons.rc b/plugins/Watrack/icons/GO/icons.rc
new file mode 100644
index 0000000000..b50bba6a22
--- /dev/null
+++ b/plugins/Watrack/icons/GO/icons.rc
@@ -0,0 +1,58 @@
+#include "waticons.h"
+LANGUAGE 0,0
+
+IDI_PREV_NORMAL ICON "previous.ico"
+IDI_PLAY_NORMAL ICON "play.ico"
+IDI_PAUSE_NORMAL ICON "pause.ico"
+IDI_STOP_NORMAL ICON "stop.ico"
+IDI_NEXT_NORMAL ICON "next.ico"
+
+IDI_PREV_HOVERED ICON "previous_hovered.ico"
+IDI_PLAY_HOVERED ICON "play_hovered.ico"
+IDI_PAUSE_HOVERED ICON "pause_hovered.ico"
+IDI_STOP_HOVERED ICON "stop_hovered.ico"
+IDI_NEXT_HOVERED ICON "next_hovered.ico"
+
+IDI_PREV_PRESSED ICON "previous_pressed.ico"
+IDI_PLAY_PRESSED ICON "play_pressed.ico"
+IDI_PAUSE_PRESSED ICON "pause_pressed.ico"
+IDI_STOP_PRESSED ICON "stop_pressed.ico"
+IDI_NEXT_PRESSED ICON "next_pressed.ico"
+
+IDI_VOLDN_NORMAL ICON "volume_down.ico"
+IDI_VOLUP_NORMAL ICON "volume_up.ico"
+IDI_VOLDN_HOVERED ICON "volume_down_hovered.ico"
+IDI_VOLUP_HOVERED ICON "volume_up_hovered.ico"
+IDI_VOLDN_PRESSED ICON "volume_down_pressed.ico"
+IDI_VOLUP_PRESSED ICON "volume_up_pressed.ico"
+
+IDI_SLIDER_NORMAL ICON "slider.ico"
+IDI_SLIDER_HOVERED ICON "slider_hovered.ico"
+IDI_SLIDER_PRESSED ICON "slider_pressed.ico"
+
+IDI_PLUGIN_ENABLE ICON "enable.ico"
+IDI_PLUGIN_DISABLE ICON "disable.ico"
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 0,0,0,2
+ PRODUCTVERSION 0,0,0,2
+ FILEFLAGSMASK 0x3F
+ FILEOS 4
+ FILETYPE 2
+ FILESUBTYPE 0
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "FileDescription", "Solid indexed and truecolor"
+ VALUE "FileVersion", "0.0.0.2"
+ VALUE "OriginalFilename", "watrack_buttons.dll"
+ VALUE "ProductName", "WATrack buttons icons"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation",0,1200
+ END
+END
diff --git a/plugins/Watrack/icons/GO/waticons.h b/plugins/Watrack/icons/GO/waticons.h
new file mode 100644
index 0000000000..bad8cadea2
--- /dev/null
+++ b/plugins/Watrack/icons/GO/waticons.h
@@ -0,0 +1,35 @@
+#define IDI_PREV_NORMAL 1
+#define IDI_PREV_HOVERED 2
+#define IDI_PREV_PRESSED 3
+
+#define IDI_PLAY_NORMAL 4
+#define IDI_PLAY_HOVERED 5
+#define IDI_PLAY_PRESSED 6
+
+#define IDI_PAUSE_NORMAL 7
+#define IDI_PAUSE_HOVERED 8
+#define IDI_PAUSE_PRESSED 9
+
+#define IDI_STOP_NORMAL 10
+#define IDI_STOP_HOVERED 11
+#define IDI_STOP_PRESSED 12
+
+#define IDI_NEXT_NORMAL 13
+#define IDI_NEXT_HOVERED 14
+#define IDI_NEXT_PRESSED 15
+
+#define IDI_VOLDN_NORMAL 16
+#define IDI_VOLDN_HOVERED 17
+#define IDI_VOLDN_PRESSED 18
+
+#define IDI_VOLUP_NORMAL 19
+#define IDI_VOLUP_HOVERED 20
+#define IDI_VOLUP_PRESSED 21
+
+#define IDI_SLIDER_NORMAL 22
+#define IDI_SLIDER_HOVERED 23
+#define IDI_SLIDER_PRESSED 24
+
+
+#define IDI_PLUGIN_ENABLE 100
+#define IDI_PLUGIN_DISABLE 101
diff --git a/plugins/Watrack/icons/GO/watrack_buttons.asm b/plugins/Watrack/icons/GO/watrack_buttons.asm
new file mode 100644
index 0000000000..27ed04d7b5
--- /dev/null
+++ b/plugins/Watrack/icons/GO/watrack_buttons.asm
@@ -0,0 +1,5 @@
+.code
+start:
+ mov al, 1
+ ret
+
diff --git a/plugins/Watrack/icons/MASM/icons.bat b/plugins/Watrack/icons/MASM/icons.bat
new file mode 100644
index 0000000000..9cb2911d33
--- /dev/null
+++ b/plugins/Watrack/icons/MASM/icons.bat
@@ -0,0 +1,8 @@
+@echo off
+if /i '%1' == 'buttons' (set iconres=icons) else set iconres=iconspl
+porc /i%2 %iconres%.rc /Foicons.res
+poasm watrack.asm
+polink /DLL /RELEASE /NODEFAULTLIB /NOENTRY /NOLOGO /OUT:watrack_%1.dll watrack.obj icons.res
+del *.obj
+del *.res
+move watrack_%1.dll ..\..\..\bin \ No newline at end of file
diff --git a/plugins/Watrack/icons/MASM/icons.rc b/plugins/Watrack/icons/MASM/icons.rc
new file mode 100644
index 0000000000..b50bba6a22
--- /dev/null
+++ b/plugins/Watrack/icons/MASM/icons.rc
@@ -0,0 +1,58 @@
+#include "waticons.h"
+LANGUAGE 0,0
+
+IDI_PREV_NORMAL ICON "previous.ico"
+IDI_PLAY_NORMAL ICON "play.ico"
+IDI_PAUSE_NORMAL ICON "pause.ico"
+IDI_STOP_NORMAL ICON "stop.ico"
+IDI_NEXT_NORMAL ICON "next.ico"
+
+IDI_PREV_HOVERED ICON "previous_hovered.ico"
+IDI_PLAY_HOVERED ICON "play_hovered.ico"
+IDI_PAUSE_HOVERED ICON "pause_hovered.ico"
+IDI_STOP_HOVERED ICON "stop_hovered.ico"
+IDI_NEXT_HOVERED ICON "next_hovered.ico"
+
+IDI_PREV_PRESSED ICON "previous_pressed.ico"
+IDI_PLAY_PRESSED ICON "play_pressed.ico"
+IDI_PAUSE_PRESSED ICON "pause_pressed.ico"
+IDI_STOP_PRESSED ICON "stop_pressed.ico"
+IDI_NEXT_PRESSED ICON "next_pressed.ico"
+
+IDI_VOLDN_NORMAL ICON "volume_down.ico"
+IDI_VOLUP_NORMAL ICON "volume_up.ico"
+IDI_VOLDN_HOVERED ICON "volume_down_hovered.ico"
+IDI_VOLUP_HOVERED ICON "volume_up_hovered.ico"
+IDI_VOLDN_PRESSED ICON "volume_down_pressed.ico"
+IDI_VOLUP_PRESSED ICON "volume_up_pressed.ico"
+
+IDI_SLIDER_NORMAL ICON "slider.ico"
+IDI_SLIDER_HOVERED ICON "slider_hovered.ico"
+IDI_SLIDER_PRESSED ICON "slider_pressed.ico"
+
+IDI_PLUGIN_ENABLE ICON "enable.ico"
+IDI_PLUGIN_DISABLE ICON "disable.ico"
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 0,0,0,2
+ PRODUCTVERSION 0,0,0,2
+ FILEFLAGSMASK 0x3F
+ FILEOS 4
+ FILETYPE 2
+ FILESUBTYPE 0
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "FileDescription", "Solid indexed and truecolor"
+ VALUE "FileVersion", "0.0.0.2"
+ VALUE "OriginalFilename", "watrack_buttons.dll"
+ VALUE "ProductName", "WATrack buttons icons"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation",0,1200
+ END
+END
diff --git a/plugins/Watrack/icons/MASM/iconspl.rc b/plugins/Watrack/icons/MASM/iconspl.rc
new file mode 100644
index 0000000000..3b98761d8d
--- /dev/null
+++ b/plugins/Watrack/icons/MASM/iconspl.rc
@@ -0,0 +1,83 @@
+LANGUAGE 0,0
+// Winamp clone
+WANY ICON "wany.ico"
+// 1BY1
+1BY1 ICON "1by1.ico"
+// ALShow
+ALSHOW ICON "alshow.ico"
+// ALSong
+ALSONG ICON "alsong.ico"
+// Apollo
+APOLLO ICON "apollo.ico"
+// AudioPlayer
+AUDIO ICON "audio.ico"
+// BSPlayer
+BS ICON "bs.ico"
+// Core Media Player
+CMP ICON "cmp.ico"
+// Creative Music Source
+CMS ICON "cms.ico"
+// Cowon JetAudio
+COWON ICON "cowon.ico"
+// FLVPlayer
+FLV ICON "flv.ico"
+// Foobar
+FOOBAR ICON "foobar.ico"
+//GOMPlayer
+GOM ICON "gom.ico"
+// Helium
+HELIUM ICON "helium.ico"
+// iTunes
+ITUNES ICON "itunes.ico"
+// JRiver Media Center
+JRMC ICON "jrmc.ico"
+// LightAlloy
+LA ICON "la.ico"
+// Last.fm Player
+LFM ICON "lfm.ico"
+// MusicCube One
+MCONE ICON "mcone.ico"
+// MusikCube
+MCUBE ICON "mcube.ico"
+// Media Commander Express
+MCX ICON "mcx.ico"
+// Music Match Jukebox
+MMATCH ICON "mmatch.ico"
+// Media Monkey
+MMONKEY ICON "mmonkey.ico"
+// Media Player Classic
+MPC ICON "mpc.ico"
+// MPlayer
+MPLAYER ICON "mplayer.ico"
+// MRadio
+MRADIO ICON "mradio.ico"
+// PlayNow
+PLAY ICON "play.ico"
+// Pluton
+PLUTON ICON "pluton.ico"
+// CyberLink PowerDVD
+POWERDVD ICON "powerdvd.ico"
+// Quintessential Player
+QCD ICON "qcd.ico"
+// Quicktime Player
+QT ICON "qt.ico"
+// Real Player Gold
+REAL ICON "real.ico"
+// SAPS
+SAPS ICON "saps.ico"
+// SongBird
+SONGBIRD ICON "songbird.ico"
+// VideoLAN media player
+VLC ICON "vlc.ico"
+// ViPlay
+VP3 ICON "vp3.ico"
+// WiFiRadio
+WIFI ICON "wifi.ico"
+// Winamp
+WINAMP ICON "winamp.ico"
+// Windows Media Player
+WMP ICON "wmp.ico"
+// XM Player
+XM ICON "xm.ico"
+// Zoom Player
+ZOOM ICON "zoom.ico"
diff --git a/plugins/Watrack/icons/MASM/poasm.exe b/plugins/Watrack/icons/MASM/poasm.exe
new file mode 100644
index 0000000000..a5062d4823
--- /dev/null
+++ b/plugins/Watrack/icons/MASM/poasm.exe
Binary files differ
diff --git a/plugins/Watrack/icons/MASM/polink.exe b/plugins/Watrack/icons/MASM/polink.exe
new file mode 100644
index 0000000000..2338218457
--- /dev/null
+++ b/plugins/Watrack/icons/MASM/polink.exe
Binary files differ
diff --git a/plugins/Watrack/icons/MASM/porc.dll b/plugins/Watrack/icons/MASM/porc.dll
new file mode 100644
index 0000000000..18390000ee
--- /dev/null
+++ b/plugins/Watrack/icons/MASM/porc.dll
Binary files differ
diff --git a/plugins/Watrack/icons/MASM/porc.exe b/plugins/Watrack/icons/MASM/porc.exe
new file mode 100644
index 0000000000..ae28631b40
--- /dev/null
+++ b/plugins/Watrack/icons/MASM/porc.exe
Binary files differ
diff --git a/plugins/Watrack/icons/MASM/waticons.h b/plugins/Watrack/icons/MASM/waticons.h
new file mode 100644
index 0000000000..bad8cadea2
--- /dev/null
+++ b/plugins/Watrack/icons/MASM/waticons.h
@@ -0,0 +1,35 @@
+#define IDI_PREV_NORMAL 1
+#define IDI_PREV_HOVERED 2
+#define IDI_PREV_PRESSED 3
+
+#define IDI_PLAY_NORMAL 4
+#define IDI_PLAY_HOVERED 5
+#define IDI_PLAY_PRESSED 6
+
+#define IDI_PAUSE_NORMAL 7
+#define IDI_PAUSE_HOVERED 8
+#define IDI_PAUSE_PRESSED 9
+
+#define IDI_STOP_NORMAL 10
+#define IDI_STOP_HOVERED 11
+#define IDI_STOP_PRESSED 12
+
+#define IDI_NEXT_NORMAL 13
+#define IDI_NEXT_HOVERED 14
+#define IDI_NEXT_PRESSED 15
+
+#define IDI_VOLDN_NORMAL 16
+#define IDI_VOLDN_HOVERED 17
+#define IDI_VOLDN_PRESSED 18
+
+#define IDI_VOLUP_NORMAL 19
+#define IDI_VOLUP_HOVERED 20
+#define IDI_VOLUP_PRESSED 21
+
+#define IDI_SLIDER_NORMAL 22
+#define IDI_SLIDER_HOVERED 23
+#define IDI_SLIDER_PRESSED 24
+
+
+#define IDI_PLUGIN_ENABLE 100
+#define IDI_PLUGIN_DISABLE 101
diff --git a/plugins/Watrack/icons/MASM/watrack.asm b/plugins/Watrack/icons/MASM/watrack.asm
new file mode 100644
index 0000000000..27ed04d7b5
--- /dev/null
+++ b/plugins/Watrack/icons/MASM/watrack.asm
@@ -0,0 +1,5 @@
+.code
+start:
+ mov al, 1
+ ret
+
diff --git a/plugins/Watrack/icons/TASM/RLINK32.DLL b/plugins/Watrack/icons/TASM/RLINK32.DLL
new file mode 100644
index 0000000000..17c21b29e1
--- /dev/null
+++ b/plugins/Watrack/icons/TASM/RLINK32.DLL
Binary files differ
diff --git a/plugins/Watrack/icons/TASM/TASM32.EXE b/plugins/Watrack/icons/TASM/TASM32.EXE
new file mode 100644
index 0000000000..edf16463b9
--- /dev/null
+++ b/plugins/Watrack/icons/TASM/TASM32.EXE
Binary files differ
diff --git a/plugins/Watrack/icons/TASM/TLINK32.EXE b/plugins/Watrack/icons/TASM/TLINK32.EXE
new file mode 100644
index 0000000000..11ced1158c
--- /dev/null
+++ b/plugins/Watrack/icons/TASM/TLINK32.EXE
Binary files differ
diff --git a/plugins/Watrack/icons/TASM/brcc32.exe b/plugins/Watrack/icons/TASM/brcc32.exe
new file mode 100644
index 0000000000..88795df846
--- /dev/null
+++ b/plugins/Watrack/icons/TASM/brcc32.exe
Binary files differ
diff --git a/plugins/Watrack/icons/TASM/icons.bat b/plugins/Watrack/icons/TASM/icons.bat
new file mode 100644
index 0000000000..5dd69d1433
--- /dev/null
+++ b/plugins/Watrack/icons/TASM/icons.bat
@@ -0,0 +1,9 @@
+@echo off
+if /i '%1' == 'buttons' (set iconres=icons) else set iconres=iconspl
+brcc32 %iconres%.rc -i%2 -foicons.res
+tasm32 watrack.asm
+tlink32 -Tpd watrack.obj,watrack_%1.dll,,,,icons.res
+del *.map
+del *.obj
+del *.res
+move watrack_%1.dll ..\..\..\bin \ No newline at end of file
diff --git a/plugins/Watrack/icons/TASM/icons.rc b/plugins/Watrack/icons/TASM/icons.rc
new file mode 100644
index 0000000000..586210d05a
--- /dev/null
+++ b/plugins/Watrack/icons/TASM/icons.rc
@@ -0,0 +1,58 @@
+#include "waticons.h"
+LANGUAGE 0,0
+
+IDI_PREV_NORMAL ICON "previous.ico"
+IDI_PLAY_NORMAL ICON "play.ico"
+IDI_PAUSE_NORMAL ICON "pause.ico"
+IDI_STOP_NORMAL ICON "stop.ico"
+IDI_NEXT_NORMAL ICON "next.ico"
+
+IDI_PREV_HOVERED ICON "previous_hovered.ico"
+IDI_PLAY_HOVERED ICON "play_hovered.ico"
+IDI_PAUSE_HOVERED ICON "pause_hovered.ico"
+IDI_STOP_HOVERED ICON "stop_hovered.ico"
+IDI_NEXT_HOVERED ICON "next_hovered.ico"
+
+IDI_PREV_PRESSED ICON "previous_pressed.ico"
+IDI_PLAY_PRESSED ICON "play_pressed.ico"
+IDI_PAUSE_PRESSED ICON "pause_pressed.ico"
+IDI_STOP_PRESSED ICON "stop_pressed.ico"
+IDI_NEXT_PRESSED ICON "next_pressed.ico"
+
+IDI_VOLDN_NORMAL ICON "volume_down.ico"
+IDI_VOLUP_NORMAL ICON "volume_up.ico"
+IDI_VOLDN_HOVERED ICON "volume_down_hovered.ico"
+IDI_VOLUP_HOVERED ICON "volume_up_hovered.ico"
+IDI_VOLDN_PRESSED ICON "volume_down_pressed.ico"
+IDI_VOLUP_PRESSED ICON "volume_up_pressed.ico"
+
+IDI_SLIDER_NORMAL ICON "slider.ico"
+IDI_SLIDER_HOVERED ICON "slider_hovered.ico"
+IDI_SLIDER_PRESSED ICON "slider_pressed.ico"
+
+IDI_PLUGIN_ENABLE ICON "enable.ico"
+IDI_PLUGIN_DISABLE ICON "disable.ico"
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 0,0,0,2
+ PRODUCTVERSION 0,0,0,2
+ FILEFLAGSMASK $3F
+ FILEOS 4
+ FILETYPE 2
+ FILESUBTYPE 0
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "FileDescription", "Solid indexed and truecolor"
+ VALUE "FileVersion", "0.0.0.2"
+ VALUE "OriginalFilename", "watrack_buttons.dll"
+ VALUE "ProductName", "WATrack buttons icons"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation",0,1200
+ END
+END
diff --git a/plugins/Watrack/icons/TASM/iconspl.rc b/plugins/Watrack/icons/TASM/iconspl.rc
new file mode 100644
index 0000000000..ab7a362869
--- /dev/null
+++ b/plugins/Watrack/icons/TASM/iconspl.rc
@@ -0,0 +1,61 @@
+LANGUAGE 0,0
+
+Player_1BY1 ICON "1by1.ico"
+Player_AIMP ICON "aimp.ico"
+Player_ALSHOW ICON "alshow.ico"
+Player_ALSONG ICON "alsong.ico"
+Player_APOLLO ICON "apollo.ico"
+Player_ASHAMPOO_MEDIA_PLAYER ICON "Ashampoo Media Player.ico"
+Player_AUDIOPLAYER ICON "audio.ico"
+Player_BEHOLDTV ICON "BeholdTV.ico"
+Player_BILLY ICON "Billy.ico"
+Player_BSPLAYER ICON "bsplayer.ico"
+Player_CORE_MEDIA_PLAYER ICON "Core Media Player.ico"
+Player_COWON_JETAUDIO ICON "JetAudio.ico"
+Player_CREATIVE_MEDIA_SOURCE ICON "cms.ico"
+Player_CRYSTAL_PLAYER ICON "Crystal Player.ico"
+Player_CYBERLINK_POWERDVD ICON "Cyberlink PowerDVD.ico"
+Player_EVIL_PLAYER ICON "Evil Player.ico"
+Player_FLVPLAYER ICON "flv.ico"
+Player_FOOBAR2000 ICON "foobar2000.ico"
+Player_GOMPLAYER ICON "GOMPlayer.ico"
+Player_HELIUM_MUSIC_MANAGER ICON "Helium Music Manager.ico"
+Player_ITUNES ICON "iTunes.ico"
+Player_J_RIVER_MEDIA_CENTER ICON "J.River Media Center.ico"
+Player_KMPLAYER ICON "KMPlayer.ico"
+Player_LAST_FM ICON "lastfm.ico"
+Player_LIGHTALLOY ICON "LA.ico"
+Player_MEDIA_COMMANDER_EXPRESS ICON "mcx.ico"
+Player_MEDIAMONKEY ICON "MediaMonkey.ico"
+Player_MOREAMP ICON "MoreAmp.ico"
+Player_MPC ICON "MPC.ico"
+Player_MPLAYER ICON "MPlayer.ico"
+Player_MUSICCUBE_ONE ICON "mcone.ico"
+Player_MUSICMATCH_JUKEBOX ICON "mmatch.ico"
+Player_MUSIKCUBE ICON "MusikCube.ico"
+Player_PLUTON ICON "pluton.ico"
+Player_QCD ICON "QCDPlayer.ico"
+Player_QUICKTIME_PLAYER ICON "QuickTime Player.ico"
+Player_RADLIGHT ICON "RadLight.ico"
+Player_REAL_PLAYER ICON "Real Player.ico"
+Player_SAPS ICON "saps.ico"
+Player_SONGBIRD ICON "SongBird.ico"
+Player_SPIDER_PLAYER ICON "Spider Player.ico"
+Player_ULTRA_PLAYER ICON "Ultra player.ico"
+Player_VIDEOLAN_PLAYER ICON "VLC.ico"
+Player_VIPLAY ICON "vp3.ico"
+Player_VUPLAYER ICON "VUPlayer.ico"
+Player_WIFIRADIO_PLAYER ICON "wifi.ico"
+Player_WINAMP ICON "winamp.ico"
+Player_WINAMP_CLONE ICON "wany.ico"
+Player_WINDVD ICON "WinDVD.ico"
+Player_WMP ICON "WMP 9.ico"
+Player_XMPLAY ICON "XMPlay.ico"
+Player_ZOOM ICON "Zoom Player.ico"
+
+/*
+Player_ ICON ""
+Player_ ICON ""
+Player_ ICON ""
+Player_ ICON ""
+*/
diff --git a/plugins/Watrack/icons/TASM/rw32core.dll b/plugins/Watrack/icons/TASM/rw32core.dll
new file mode 100644
index 0000000000..29ec016027
--- /dev/null
+++ b/plugins/Watrack/icons/TASM/rw32core.dll
Binary files differ
diff --git a/plugins/Watrack/icons/TASM/waticons.h b/plugins/Watrack/icons/TASM/waticons.h
new file mode 100644
index 0000000000..bad8cadea2
--- /dev/null
+++ b/plugins/Watrack/icons/TASM/waticons.h
@@ -0,0 +1,35 @@
+#define IDI_PREV_NORMAL 1
+#define IDI_PREV_HOVERED 2
+#define IDI_PREV_PRESSED 3
+
+#define IDI_PLAY_NORMAL 4
+#define IDI_PLAY_HOVERED 5
+#define IDI_PLAY_PRESSED 6
+
+#define IDI_PAUSE_NORMAL 7
+#define IDI_PAUSE_HOVERED 8
+#define IDI_PAUSE_PRESSED 9
+
+#define IDI_STOP_NORMAL 10
+#define IDI_STOP_HOVERED 11
+#define IDI_STOP_PRESSED 12
+
+#define IDI_NEXT_NORMAL 13
+#define IDI_NEXT_HOVERED 14
+#define IDI_NEXT_PRESSED 15
+
+#define IDI_VOLDN_NORMAL 16
+#define IDI_VOLDN_HOVERED 17
+#define IDI_VOLDN_PRESSED 18
+
+#define IDI_VOLUP_NORMAL 19
+#define IDI_VOLUP_HOVERED 20
+#define IDI_VOLUP_PRESSED 21
+
+#define IDI_SLIDER_NORMAL 22
+#define IDI_SLIDER_HOVERED 23
+#define IDI_SLIDER_PRESSED 24
+
+
+#define IDI_PLUGIN_ENABLE 100
+#define IDI_PLUGIN_DISABLE 101
diff --git a/plugins/Watrack/icons/TASM/watrack.asm b/plugins/Watrack/icons/TASM/watrack.asm
new file mode 100644
index 0000000000..cab1fa249f
--- /dev/null
+++ b/plugins/Watrack/icons/TASM/watrack.asm
@@ -0,0 +1,8 @@
+.386
+.model flat, stdcall
+.code
+start:
+ mov al, 1
+ ret
+end start
+end \ No newline at end of file
diff --git a/plugins/Watrack/icons/iconsets/players/1by1.ico b/plugins/Watrack/icons/iconsets/players/1by1.ico
new file mode 100644
index 0000000000..e8f3e4ae82
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/1by1.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/AIMP.ico b/plugins/Watrack/icons/iconsets/players/AIMP.ico
new file mode 100644
index 0000000000..a0f99889b9
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/AIMP.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/Ashampoo Media Player.ico b/plugins/Watrack/icons/iconsets/players/Ashampoo Media Player.ico
new file mode 100644
index 0000000000..8496bf2eb0
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/Ashampoo Media Player.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/BeholdTV.ico b/plugins/Watrack/icons/iconsets/players/BeholdTV.ico
new file mode 100644
index 0000000000..8430b7723c
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/BeholdTV.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/Billy.ico b/plugins/Watrack/icons/iconsets/players/Billy.ico
new file mode 100644
index 0000000000..655f2d97f6
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/Billy.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/Core Media Player.ico b/plugins/Watrack/icons/iconsets/players/Core Media Player.ico
new file mode 100644
index 0000000000..f5edff45d5
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/Core Media Player.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/Crystal Player.ico b/plugins/Watrack/icons/iconsets/players/Crystal Player.ico
new file mode 100644
index 0000000000..0f0a8d894b
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/Crystal Player.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/Cyberlink PowerDVD.ico b/plugins/Watrack/icons/iconsets/players/Cyberlink PowerDVD.ico
new file mode 100644
index 0000000000..7a26e8c05c
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/Cyberlink PowerDVD.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/Evil Player.ico b/plugins/Watrack/icons/iconsets/players/Evil Player.ico
new file mode 100644
index 0000000000..d11cda81c5
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/Evil Player.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/GOMPlayer.ico b/plugins/Watrack/icons/iconsets/players/GOMPlayer.ico
new file mode 100644
index 0000000000..9a2f0fb960
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/GOMPlayer.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/Helium Music Manager.ico b/plugins/Watrack/icons/iconsets/players/Helium Music Manager.ico
new file mode 100644
index 0000000000..abdc4ef2b0
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/Helium Music Manager.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/J.River Media Center.ico b/plugins/Watrack/icons/iconsets/players/J.River Media Center.ico
new file mode 100644
index 0000000000..2a092c2c7e
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/J.River Media Center.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/JetAudio.ico b/plugins/Watrack/icons/iconsets/players/JetAudio.ico
new file mode 100644
index 0000000000..9e3f536fca
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/JetAudio.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/KMPlayer.ico b/plugins/Watrack/icons/iconsets/players/KMPlayer.ico
new file mode 100644
index 0000000000..aca7ba1514
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/KMPlayer.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/LastFM.ico b/plugins/Watrack/icons/iconsets/players/LastFM.ico
new file mode 100644
index 0000000000..934e7090e2
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/LastFM.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/MediaMonkey.ico b/plugins/Watrack/icons/iconsets/players/MediaMonkey.ico
new file mode 100644
index 0000000000..490e5c10b9
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/MediaMonkey.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/MoreAmp.ico b/plugins/Watrack/icons/iconsets/players/MoreAmp.ico
new file mode 100644
index 0000000000..8b8e560495
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/MoreAmp.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/MusikCube.ico b/plugins/Watrack/icons/iconsets/players/MusikCube.ico
new file mode 100644
index 0000000000..36d266a3d0
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/MusikCube.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/QCDPlayer.ico b/plugins/Watrack/icons/iconsets/players/QCDPlayer.ico
new file mode 100644
index 0000000000..385282a192
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/QCDPlayer.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/Quicktime Player.ico b/plugins/Watrack/icons/iconsets/players/Quicktime Player.ico
new file mode 100644
index 0000000000..35268906c1
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/Quicktime Player.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/RadLight.ico b/plugins/Watrack/icons/iconsets/players/RadLight.ico
new file mode 100644
index 0000000000..be5a1c1499
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/RadLight.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/Real Player.ico b/plugins/Watrack/icons/iconsets/players/Real Player.ico
new file mode 100644
index 0000000000..99dc2d4f34
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/Real Player.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/Spider Player.ico b/plugins/Watrack/icons/iconsets/players/Spider Player.ico
new file mode 100644
index 0000000000..c649b35d8b
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/Spider Player.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/Ultra player.ico b/plugins/Watrack/icons/iconsets/players/Ultra player.ico
new file mode 100644
index 0000000000..4a54035ecc
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/Ultra player.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/VUPlayer.ico b/plugins/Watrack/icons/iconsets/players/VUPlayer.ico
new file mode 100644
index 0000000000..1648195b15
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/VUPlayer.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/WMP 9.ico b/plugins/Watrack/icons/iconsets/players/WMP 9.ico
new file mode 100644
index 0000000000..124a27aea0
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/WMP 9.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/WinDVD.ico b/plugins/Watrack/icons/iconsets/players/WinDVD.ico
new file mode 100644
index 0000000000..79cd1c7391
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/WinDVD.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/XMPlay.ico b/plugins/Watrack/icons/iconsets/players/XMPlay.ico
new file mode 100644
index 0000000000..40952b26a1
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/XMPlay.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/Zoom Player.ico b/plugins/Watrack/icons/iconsets/players/Zoom Player.ico
new file mode 100644
index 0000000000..fa62cb0d68
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/Zoom Player.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/alshow.ico b/plugins/Watrack/icons/iconsets/players/alshow.ico
new file mode 100644
index 0000000000..d01d8846a3
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/alshow.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/alsong.ico b/plugins/Watrack/icons/iconsets/players/alsong.ico
new file mode 100644
index 0000000000..b7585afbfe
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/alsong.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/apollo.ico b/plugins/Watrack/icons/iconsets/players/apollo.ico
new file mode 100644
index 0000000000..b8a76d4569
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/apollo.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/audio.ico b/plugins/Watrack/icons/iconsets/players/audio.ico
new file mode 100644
index 0000000000..7ad8262c54
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/audio.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/bsplayer.ico b/plugins/Watrack/icons/iconsets/players/bsplayer.ico
new file mode 100644
index 0000000000..9d40734e4d
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/bsplayer.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/cms.ico b/plugins/Watrack/icons/iconsets/players/cms.ico
new file mode 100644
index 0000000000..7b71b4cad5
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/cms.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/flv.ico b/plugins/Watrack/icons/iconsets/players/flv.ico
new file mode 100644
index 0000000000..ba0d4cba83
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/flv.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/foobar2000.ico b/plugins/Watrack/icons/iconsets/players/foobar2000.ico
new file mode 100644
index 0000000000..a1b0e4b7a1
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/foobar2000.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/itunes.ico b/plugins/Watrack/icons/iconsets/players/itunes.ico
new file mode 100644
index 0000000000..d4694d2969
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/itunes.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/la.ico b/plugins/Watrack/icons/iconsets/players/la.ico
new file mode 100644
index 0000000000..27c045931c
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/la.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/mcone.ico b/plugins/Watrack/icons/iconsets/players/mcone.ico
new file mode 100644
index 0000000000..be73f7cd30
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/mcone.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/mcx.ico b/plugins/Watrack/icons/iconsets/players/mcx.ico
new file mode 100644
index 0000000000..3532fa5da2
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/mcx.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/mmatch.ico b/plugins/Watrack/icons/iconsets/players/mmatch.ico
new file mode 100644
index 0000000000..ffd2b8ed0a
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/mmatch.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/mpc.ico b/plugins/Watrack/icons/iconsets/players/mpc.ico
new file mode 100644
index 0000000000..4e5d09966d
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/mpc.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/mplayer.ico b/plugins/Watrack/icons/iconsets/players/mplayer.ico
new file mode 100644
index 0000000000..219d59b7b9
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/mplayer.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/pluton.ico b/plugins/Watrack/icons/iconsets/players/pluton.ico
new file mode 100644
index 0000000000..10397ba1f8
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/pluton.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/saps.ico b/plugins/Watrack/icons/iconsets/players/saps.ico
new file mode 100644
index 0000000000..41263a4ef7
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/saps.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/songbird.ico b/plugins/Watrack/icons/iconsets/players/songbird.ico
new file mode 100644
index 0000000000..2876b1ca46
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/songbird.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/vlc.ico b/plugins/Watrack/icons/iconsets/players/vlc.ico
new file mode 100644
index 0000000000..7caef3b91e
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/vlc.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/vp3.ico b/plugins/Watrack/icons/iconsets/players/vp3.ico
new file mode 100644
index 0000000000..b8227fe7e5
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/vp3.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/wany.ico b/plugins/Watrack/icons/iconsets/players/wany.ico
new file mode 100644
index 0000000000..4349576c82
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/wany.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/wifi.ico b/plugins/Watrack/icons/iconsets/players/wifi.ico
new file mode 100644
index 0000000000..022cd7adee
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/wifi.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/players/winamp.ico b/plugins/Watrack/icons/iconsets/players/winamp.ico
new file mode 100644
index 0000000000..7dc5a2a3d9
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/players/winamp.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/disable.ico b/plugins/Watrack/icons/iconsets/true+256-solid/disable.ico
new file mode 100644
index 0000000000..390f0852a2
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/disable.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/enable.ico b/plugins/Watrack/icons/iconsets/true+256-solid/enable.ico
new file mode 100644
index 0000000000..0e20d3a616
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/enable.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/next.ico b/plugins/Watrack/icons/iconsets/true+256-solid/next.ico
new file mode 100644
index 0000000000..5d85a0cbfb
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/next.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/next_hovered.ico b/plugins/Watrack/icons/iconsets/true+256-solid/next_hovered.ico
new file mode 100644
index 0000000000..58dfc4e78b
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/next_hovered.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/next_pressed.ico b/plugins/Watrack/icons/iconsets/true+256-solid/next_pressed.ico
new file mode 100644
index 0000000000..304e67c42c
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/next_pressed.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/pause.ico b/plugins/Watrack/icons/iconsets/true+256-solid/pause.ico
new file mode 100644
index 0000000000..b719b08229
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/pause.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/pause_hovered.ico b/plugins/Watrack/icons/iconsets/true+256-solid/pause_hovered.ico
new file mode 100644
index 0000000000..6f83916a8f
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/pause_hovered.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/pause_pressed.ico b/plugins/Watrack/icons/iconsets/true+256-solid/pause_pressed.ico
new file mode 100644
index 0000000000..8455a55231
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/pause_pressed.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/play.ico b/plugins/Watrack/icons/iconsets/true+256-solid/play.ico
new file mode 100644
index 0000000000..e7b8c0360b
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/play.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/play_hovered.ico b/plugins/Watrack/icons/iconsets/true+256-solid/play_hovered.ico
new file mode 100644
index 0000000000..973e042a78
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/play_hovered.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/play_pressed.ico b/plugins/Watrack/icons/iconsets/true+256-solid/play_pressed.ico
new file mode 100644
index 0000000000..75c3119eb3
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/play_pressed.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/previous.ico b/plugins/Watrack/icons/iconsets/true+256-solid/previous.ico
new file mode 100644
index 0000000000..0b38110f84
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/previous.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/previous_hovered.ico b/plugins/Watrack/icons/iconsets/true+256-solid/previous_hovered.ico
new file mode 100644
index 0000000000..b1e25de6b0
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/previous_hovered.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/previous_pressed.ico b/plugins/Watrack/icons/iconsets/true+256-solid/previous_pressed.ico
new file mode 100644
index 0000000000..0b0accd00e
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/previous_pressed.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/slider.ico b/plugins/Watrack/icons/iconsets/true+256-solid/slider.ico
new file mode 100644
index 0000000000..785bd748af
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/slider.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/slider_hovered.ico b/plugins/Watrack/icons/iconsets/true+256-solid/slider_hovered.ico
new file mode 100644
index 0000000000..0e20d3a616
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/slider_hovered.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/slider_pressed.ico b/plugins/Watrack/icons/iconsets/true+256-solid/slider_pressed.ico
new file mode 100644
index 0000000000..390f0852a2
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/slider_pressed.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/stop.ico b/plugins/Watrack/icons/iconsets/true+256-solid/stop.ico
new file mode 100644
index 0000000000..444302bd67
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/stop.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/stop_hovered.ico b/plugins/Watrack/icons/iconsets/true+256-solid/stop_hovered.ico
new file mode 100644
index 0000000000..00eadcab2a
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/stop_hovered.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/stop_pressed.ico b/plugins/Watrack/icons/iconsets/true+256-solid/stop_pressed.ico
new file mode 100644
index 0000000000..81ae38790f
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/stop_pressed.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/volume_down.ico b/plugins/Watrack/icons/iconsets/true+256-solid/volume_down.ico
new file mode 100644
index 0000000000..44e89abcb3
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/volume_down.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/volume_down_hovered.ico b/plugins/Watrack/icons/iconsets/true+256-solid/volume_down_hovered.ico
new file mode 100644
index 0000000000..082c9c1bf9
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/volume_down_hovered.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/volume_down_pressed.ico b/plugins/Watrack/icons/iconsets/true+256-solid/volume_down_pressed.ico
new file mode 100644
index 0000000000..ca48da4d03
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/volume_down_pressed.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/volume_up.ico b/plugins/Watrack/icons/iconsets/true+256-solid/volume_up.ico
new file mode 100644
index 0000000000..ebe32ac085
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/volume_up.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/volume_up_hovered.ico b/plugins/Watrack/icons/iconsets/true+256-solid/volume_up_hovered.ico
new file mode 100644
index 0000000000..76bb840dd8
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/volume_up_hovered.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true+256-solid/volume_up_pressed.ico b/plugins/Watrack/icons/iconsets/true+256-solid/volume_up_pressed.ico
new file mode 100644
index 0000000000..b9a0e2c9d1
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true+256-solid/volume_up_pressed.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/next.ico b/plugins/Watrack/icons/iconsets/true-solid-faith/next.ico
new file mode 100644
index 0000000000..b716875893
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/next.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/next_hovered.ico b/plugins/Watrack/icons/iconsets/true-solid-faith/next_hovered.ico
new file mode 100644
index 0000000000..6a41ff71d4
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/next_hovered.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/next_pressed.ico b/plugins/Watrack/icons/iconsets/true-solid-faith/next_pressed.ico
new file mode 100644
index 0000000000..5766059f10
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/next_pressed.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/pause.ico b/plugins/Watrack/icons/iconsets/true-solid-faith/pause.ico
new file mode 100644
index 0000000000..612030f3f2
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/pause.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/pause_hovered.ico b/plugins/Watrack/icons/iconsets/true-solid-faith/pause_hovered.ico
new file mode 100644
index 0000000000..681f7e4a24
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/pause_hovered.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/pause_pressed.ico b/plugins/Watrack/icons/iconsets/true-solid-faith/pause_pressed.ico
new file mode 100644
index 0000000000..d1f4421b20
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/pause_pressed.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/play.ico b/plugins/Watrack/icons/iconsets/true-solid-faith/play.ico
new file mode 100644
index 0000000000..e35113cced
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/play.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/play_hovered.ico b/plugins/Watrack/icons/iconsets/true-solid-faith/play_hovered.ico
new file mode 100644
index 0000000000..591e186361
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/play_hovered.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/play_pressed.ico b/plugins/Watrack/icons/iconsets/true-solid-faith/play_pressed.ico
new file mode 100644
index 0000000000..029c30076b
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/play_pressed.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/previous.ico b/plugins/Watrack/icons/iconsets/true-solid-faith/previous.ico
new file mode 100644
index 0000000000..4f57e43af3
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/previous.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/previous_hovered.ico b/plugins/Watrack/icons/iconsets/true-solid-faith/previous_hovered.ico
new file mode 100644
index 0000000000..c456ca427f
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/previous_hovered.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/previous_pressed.ico b/plugins/Watrack/icons/iconsets/true-solid-faith/previous_pressed.ico
new file mode 100644
index 0000000000..68aa16a1c9
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/previous_pressed.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/slider.ico b/plugins/Watrack/icons/iconsets/true-solid-faith/slider.ico
new file mode 100644
index 0000000000..1f5df0b577
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/slider.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/stop.ico b/plugins/Watrack/icons/iconsets/true-solid-faith/stop.ico
new file mode 100644
index 0000000000..c17432d894
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/stop.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/stop_hovered.ico b/plugins/Watrack/icons/iconsets/true-solid-faith/stop_hovered.ico
new file mode 100644
index 0000000000..899030c9c7
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/stop_hovered.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/stop_pressed.ico b/plugins/Watrack/icons/iconsets/true-solid-faith/stop_pressed.ico
new file mode 100644
index 0000000000..65f3c17517
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/stop_pressed.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/ver.res b/plugins/Watrack/icons/iconsets/true-solid-faith/ver.res
new file mode 100644
index 0000000000..aafbd00b59
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/ver.res
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/volume_down.ico b/plugins/Watrack/icons/iconsets/true-solid-faith/volume_down.ico
new file mode 100644
index 0000000000..95ce39a7d7
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/volume_down.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/volume_down_hovered.ico b/plugins/Watrack/icons/iconsets/true-solid-faith/volume_down_hovered.ico
new file mode 100644
index 0000000000..a1195d1034
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/volume_down_hovered.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/volume_down_pressed.ico b/plugins/Watrack/icons/iconsets/true-solid-faith/volume_down_pressed.ico
new file mode 100644
index 0000000000..9ebd49660b
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/volume_down_pressed.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/volume_up.ico b/plugins/Watrack/icons/iconsets/true-solid-faith/volume_up.ico
new file mode 100644
index 0000000000..e7875083af
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/volume_up.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/volume_up_hovered.ico b/plugins/Watrack/icons/iconsets/true-solid-faith/volume_up_hovered.ico
new file mode 100644
index 0000000000..94332014fa
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/volume_up_hovered.ico
Binary files differ
diff --git a/plugins/Watrack/icons/iconsets/true-solid-faith/volume_up_pressed.ico b/plugins/Watrack/icons/iconsets/true-solid-faith/volume_up_pressed.ico
new file mode 100644
index 0000000000..edfab79326
--- /dev/null
+++ b/plugins/Watrack/icons/iconsets/true-solid-faith/volume_up_pressed.ico
Binary files differ
diff --git a/plugins/Watrack/icons/make-buttons.bat b/plugins/Watrack/icons/make-buttons.bat
new file mode 100644
index 0000000000..dbca3ae634
--- /dev/null
+++ b/plugins/Watrack/icons/make-buttons.bat
@@ -0,0 +1,4 @@
+:@echo off
+if /i '%1' == '' (set asm=tasm) else set asm=%1
+if /i '%2' == '' (set iconpack=true+256-solid) else set iconpack=%2
+call make.bat buttons %iconpack% %asm% \ No newline at end of file
diff --git a/plugins/Watrack/icons/make-players.bat b/plugins/Watrack/icons/make-players.bat
new file mode 100644
index 0000000000..ec90aa49bb
--- /dev/null
+++ b/plugins/Watrack/icons/make-players.bat
@@ -0,0 +1,2 @@
+@echo off
+call make.bat icons players tasm \ No newline at end of file
diff --git a/plugins/Watrack/icons/make.bat b/plugins/Watrack/icons/make.bat
new file mode 100644
index 0000000000..95f90dcca6
--- /dev/null
+++ b/plugins/Watrack/icons/make.bat
@@ -0,0 +1,16 @@
+:first parameter - 'icons' or 'buttons' - type of iconpack ()
+:second parameter - iconpack name (for buttons mainly)
+:third parameter - assembler? (tasm)
+:@echo off
+if /i '%1' == '' (set pack=buttons) else set pack=%1
+if '%pack%' == 'icons' goto players
+if /i '%2' == '' (set iconpack=true+256-solid) else set iconpack=%2
+goto next
+:players
+if /i '%2' == '' (set iconpack=players) else set iconpack=%2
+:next
+if /i '%3' == '' (set asm=tasm) else set asm=%3
+:@echo off
+cd %asm%
+call icons.bat %pack% ..\iconsets\%iconpack% %4 %5 %6 %7 %8 %9
+cd ..\
diff --git a/plugins/Watrack/icons/waticons.inc b/plugins/Watrack/icons/waticons.inc
new file mode 100644
index 0000000000..0c3c423f0e
--- /dev/null
+++ b/plugins/Watrack/icons/waticons.inc
@@ -0,0 +1,35 @@
+const
+ IDI_PREV_NORMAL = 1;
+ IDI_PREV_HOVERED = 2;
+ IDI_PREV_PRESSED = 3;
+
+ IDI_PLAY_NORMAL = 4;
+ IDI_PLAY_HOVERED = 5;
+ IDI_PLAY_PRESSED = 6;
+
+ IDI_PAUSE_NORMAL = 7;
+ IDI_PAUSE_HOVERED = 8;
+ IDI_PAUSE_PRESSED = 9;
+
+ IDI_STOP_NORMAL = 10;
+ IDI_STOP_HOVERED = 11;
+ IDI_STOP_PRESSED = 12;
+
+ IDI_NEXT_NORMAL = 13;
+ IDI_NEXT_HOVERED = 14;
+ IDI_NEXT_PRESSED = 15;
+
+ IDI_VOLDN_NORMAL = 16;
+ IDI_VOLDN_HOVERED = 17;
+ IDI_VOLDN_PRESSED = 18;
+
+ IDI_VOLUP_NORMAL = 19;
+ IDI_VOLUP_HOVERED = 20;
+ IDI_VOLUP_PRESSED = 21;
+
+ IDI_SLIDER_NORMAL = 22;
+ IDI_SLIDER_HOVERED = 23;
+ IDI_SLIDER_PRESSED = 24;
+
+ IDI_PLUGIN_ENABLE = 100;
+ IDI_PLUGIN_DISABLE = 101;
diff --git a/plugins/Watrack/kolframe/frm.rc b/plugins/Watrack/kolframe/frm.rc
new file mode 100644
index 0000000000..50180b4b53
--- /dev/null
+++ b/plugins/Watrack/kolframe/frm.rc
@@ -0,0 +1,84 @@
+#include "frm_rc.inc"
+
+LANGUAGE 0,0
+
+FRAME DIALOGEX 0, 0, 304, 226, 0
+STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ AUTOCHECKBOX "Use buttons gap" , IDC_BTNGAP , 3, 44, 146, 14, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+
+ LTEXT "Frame refresh time", -1, 47, 2, 100, 14, SS_CENTERIMAGE
+ EDITTEXT IDC_TIMER , 3, 3, 40, 12, ES_RIGHT | ES_NUMBER
+
+ AUTOCHECKBOX "Hide when no player", IDC_HIDEFRAMEPLAYER, 3, 16, 146, 14, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+ AUTOCHECKBOX "Hide when no music" , IDC_HIDEFRAMEMUSIC , 3, 30, 146, 14, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+
+ AUTOCHECKBOX "Show info in the frame" , IDC_SHOWTEXT , 155, 2, 144, 14, BS_VCENTER | BS_MULTILINE | BS_NOTIFY | BS_RIGHT | BS_LEFTTEXT
+ AUTOCHECKBOX "Show controls in the frame", IDC_SHOWCTRLS, 155, 16, 144, 14, BS_VCENTER | BS_MULTILINE | BS_NOTIFY | BS_RIGHT | BS_LEFTTEXT
+ AUTOCHECKBOX "Show volume controls" , IDC_SHOWVOLUM, 155, 30, 144, 14, BS_VCENTER | BS_MULTILINE | BS_NOTIFY | BS_RIGHT | BS_LEFTTEXT
+ AUTOCHECKBOX "Show trackbar" , IDC_SHOWBAR , 155, 44, 144, 14, BS_VCENTER | BS_MULTILINE | BS_NOTIFY | BS_RIGHT | BS_LEFTTEXT
+
+ AUTOCHECKBOX "Use Picture",IDC_FRMUSEPIC,3,65,91,14,BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+ EDITTEXT IDC_FRMBKPIC, 96, 65, 185, 14
+ PUSHBUTTON "...", IDC_FRMPICBTN, 284, 65, 16, 14
+
+ GROUPBOX "Picture transform", -1, 2, 82, 298, 59
+ AUTOCHECKBOX "Center horizontally" , IDC_CENTERX , 6, 91, 96, 16, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+ AUTOCHECKBOX "Center vertically" , IDC_CENTERY , 6, 107, 96, 16, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+ AUTOCHECKBOX "Bottom" , IDC_BOTTOM , 6, 123, 96, 16, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+ AUTOCHECKBOX "Tile horizontally" , IDC_TILEX , 104, 91, 96, 16, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+ AUTOCHECKBOX "Tile vertically" , IDC_TILEY , 104, 107, 96, 16, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+ AUTOCHECKBOX "Right" , IDC_RIGHT , 104, 123, 96, 16, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+ AUTOCHECKBOX "Stretch to width" , IDC_STRETCHX, 202, 91, 96, 16, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+ AUTOCHECKBOX "Stretch to height" , IDC_STRETCHY, 202, 107, 96, 16, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+ AUTOCHECKBOX "Stretch proportionally", IDC_PROP , 202, 123, 96, 16, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+
+ CTEXT "Cover padding", -1, 6, 152, 142, 14, SS_CENTERIMAGE
+ EDITTEXT IDC_PADDING_TOP , 64, 168, 24, 14, ES_RIGHT | ES_NUMBER
+ EDITTEXT IDC_PADDING_LEFT , 50, 184, 24, 14, ES_RIGHT | ES_NUMBER
+ EDITTEXT IDC_PADDING_RIGHT , 78, 184, 24, 14, ES_RIGHT | ES_NUMBER
+ EDITTEXT IDC_PADDING_BOTTOM, 64, 200, 24, 14, ES_RIGHT | ES_NUMBER
+
+ AUTOCHECKBOX "Manual element placement", IDC_MANUALPLACE, 155,144,144,16, BS_VCENTER | BS_MULTILINE | BS_RIGHT | BS_LEFTTEXT
+
+// TBS_TOOLTIPS
+ CONTROL "",IDC_FRMALPHA,"msctls_trackbar32", TBS_BOTTOM|TBS_NOTICKS|$100,160,168,68,11
+ LTEXT "Alpha",-1,230,168,75,11, SS_CENTERIMAGE
+
+ CONTROL "",IDC_FRMCOLOR, "ColourPicker", WS_TABSTOP, 162, 181, 14, 14
+ LTEXT "Background color",-1,178,181,126,14, SS_CENTERIMAGE
+
+ AUTOCHECKBOX "Use cover instead of picture", IDC_USECOVER, 155, 196, 142, 16, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+}
+
+FRAME2 DIALOGEX 0, 0, 304, 226, 0
+STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ PUSHBUTTON "Choose font...", IDC_FRMFONT, 84, 90, 64, 12
+
+ GROUPBOX "Text effect",-1,154,6,142,76
+ AUTORADIOBUTTON "Cut", IDC_EFF_CUT , 158, 16, 136, 12, NOT WS_TABSTOP
+ AUTORADIOBUTTON "Wrap", IDC_EFF_WRAP, 158, 28, 136, 12, NOT WS_TABSTOP
+ AUTORADIOBUTTON "Roll", IDC_EFF_ROLL, 158, 40, 136, 12, NOT WS_TABSTOP
+ AUTORADIOBUTTON "PingPong", IDC_EFF_PONG, 158, 52, 136, 12, NOT WS_TABSTOP
+ AUTOCHECKBOX "Align text to center", IDC_ALCENTER, 158, 66, 136, 12, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+
+ GROUPBOX "Text movement", -1, 6, 6, 142, 76, WS_TABSTOP
+ LTEXT "Text rotation speed (1-20)", -1, 32, 17, 114, 12, SS_CENTERIMAGE
+ LTEXT "Scroll step" , -1, 32, 33, 114, 12, SS_CENTERIMAGE
+ LTEXT "Scroll gap" , -1, 32, 49, 114, 12, SS_CENTERIMAGE
+// LTEXT "Minimum scroll tail" , -1, 32, 65, 114, 12, SS_CENTERIMAGE
+ EDITTEXT IDC_TIMER , 10, 17, 20, 12, ES_RIGHT | ES_NUMBER
+ EDITTEXT IDC_ROLLSTEP, 10, 33, 20, 12, ES_RIGHT | ES_NUMBER
+ EDITTEXT IDC_ROLLGAP , 10, 49, 20, 12, ES_RIGHT | ES_NUMBER
+// EDITTEXT IDC_ROLLTAIL, 10, 65, 20, 12, ES_RIGHT | ES_NUMBER
+
+ CONTROL "M" ,IDC_MACRO_HELP ,"MButtonClass",WS_TABSTOP,284,156,16,16,$18000000
+ RTEXT "Frame Text", -1 , 4, 163, 274, 10
+ EDITTEXT IDC_FRAME_TEXT , 4, 174, 296, 48,
+ ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
+}
diff --git a/plugins/Watrack/kolframe/frm.res b/plugins/Watrack/kolframe/frm.res
new file mode 100644
index 0000000000..b978cdb07e
--- /dev/null
+++ b/plugins/Watrack/kolframe/frm.res
Binary files differ
diff --git a/plugins/Watrack/kolframe/frm_data.inc b/plugins/Watrack/kolframe/frm_data.inc
new file mode 100644
index 0000000000..52535d15b2
--- /dev/null
+++ b/plugins/Watrack/kolframe/frm_data.inc
@@ -0,0 +1,37 @@
+{Frame data}
+
+const
+ //show controls
+ scButtons = $0001;
+ scTrackBar = $0002;
+ scText = $0004;
+ scVolume = $0008;
+ scGap = $0010;
+ scAll = $000F;
+
+ ppLeft = 0;
+ ppRight = 1;
+ //effects
+ effCut = 0;
+ effWrap = 1;
+ effRoll = 2;
+ effPong = 3;
+
+ // Back bitmap mode
+ frbkCenterX = $0001;
+ frbkCenterY = $0002;
+ frbkCenter = frbkCenterX or frbkCenterY;
+ frbkTileX = $0004;
+ frbkTileY = $0008;
+ frbkTile = frbkTileX or frbkTileY;
+ frbkStretchX = $0010;
+ frbkStretchY = $0020;
+ frbkStretch = frbkStretchX or frbkStretchY;
+ frbkProportional = $0040;
+ frbkBottom = $0080;
+ frbkRight = $0100;
+
+const
+ numbuttons = 7;
+ VolBtnDist = 6;
+ BtnGap = 3;
diff --git a/plugins/Watrack/kolframe/frm_designer.inc b/plugins/Watrack/kolframe/frm_designer.inc
new file mode 100644
index 0000000000..f12c01f05d
--- /dev/null
+++ b/plugins/Watrack/kolframe/frm_designer.inc
@@ -0,0 +1,164 @@
+{Frame designer}
+const
+ // trackbar
+ opt_tbleft :pAnsiChar='frame/designer/trackbar/left';
+ opt_tbtop :pAnsiChar='frame/designer/trackbar/top';
+ opt_tbwidth :pAnsiChar='frame/designer/trackbar/width';
+ opt_tbheight:pAnsiChar='frame/designer/trackbar/height';
+ // text block
+ opt_tleft :pAnsiChar='frame/designer/text/left';
+ opt_ttop :pAnsiChar='frame/designer/text/top';
+ opt_twidth :pAnsiChar='frame/designer/text/width';
+ opt_theight :pAnsiChar='frame/designer/text/height';
+ // buttons (with number)
+ opt_bleft :pAnsiChar='frame/designer/buttons/left';
+ opt_btop :pAnsiChar='frame/designer/buttons/top';
+
+procedure TWATFrame.DesignerSaveSettings;
+var
+ i:integer;
+ D:PWATFrameData;
+ lleft,ltop:array [0..63] of AnsiChar;
+ pleft,ptop:PAnsiChar;
+begin
+ D:=CustomData;
+ if D.TrackBar<>nil then
+ begin
+ DBWriteWord(0,PluginShort,opt_tbleft ,D.Trackbar.Left);
+ DBWriteWord(0,PluginShort,opt_tbtop ,D.Trackbar.Top);
+ DBWriteWord(0,PluginShort,opt_tbwidth ,D.Trackbar.Width);
+ DBWriteWord(0,PluginShort,opt_tbheight,D.Trackbar.Height);
+ end;
+
+ if D.TextBlock<>nil then
+ begin
+ DBWriteWord(0,PluginShort,opt_tleft ,D.TextBlock.Left);
+ DBWriteWord(0,PluginShort,opt_ttop ,D.TextBlock.Top);
+ DBWriteWord(0,PluginShort,opt_twidth ,D.TextBlock.Width);
+ DBWriteWord(0,PluginShort,opt_theight,D.TextBlock.Height);
+ end;
+
+ if (D.ShowControls and scButtons)<>0 then
+ begin
+ if D.btnarray[0]<>nil then
+ begin
+ pleft:=StrCopyE(lleft,opt_bleft);
+ ptop :=StrCopyE(ltop ,opt_btop);
+ for i:=0 to HIGH(D.btnarray) do
+ begin
+ IntToStr(pleft,i); DBWriteWord(0,PluginShort,lleft,D.btnarray[i].Left);
+ IntToStr(ptop ,i); DBWriteWord(0,PluginShort,ltop ,D.btnarray[i].Top);
+ end;
+ end;
+ end;
+end;
+
+procedure TWATFrame.DesignerLoadSettings;
+var
+ i:integer;
+ D:PWATFrameData;
+ lleft,ltop:array [0..63] of AnsiChar;
+ pleft,ptop:PAnsiChar;
+ for_check:integer;
+begin
+ D:=CustomData;
+
+ if (D.TrackBar<>nil) and
+ ((D.ShowControls and scTrackbar)<>0) and
+ ((D.Loaded and scTrackbar)=0) then
+ begin
+ D.Loaded:=D.Loaded or scTrackbar;
+ for_check:=DBReadWord(0,PluginShort,opt_tbwidth);
+ if for_check<>0 then
+ begin
+ D.Trackbar.SetPosition(
+ DBReadWord(0,PluginShort,opt_tbleft),
+ DBReadWord(0,PluginShort,opt_tbtop));
+ D.Trackbar.SetSize(
+ for_check,
+ {18}DBReadWord(0,PluginShort,opt_tbheight));
+ end;
+ end;
+
+ if (D.TextBlock<>nil) and
+ ((D.ShowControls and scText)<>0) and
+ ((D.Loaded and scText)=0) then
+ begin
+ D.Loaded:=D.Loaded or scText;
+ for_check:=DBReadWord(0,PluginShort,opt_twidth);
+ if for_check<>0 then
+ begin
+ D.TextBlock.SetPosition(
+ DBReadWord(0,PluginShort,opt_tleft),
+ DBReadWord(0,PluginShort,opt_ttop));
+ D.TextBlock.SetSize(
+ for_check,
+ DBReadWord(0,PluginShort,opt_theight));
+ end;
+ end;
+
+ if ((D.ShowControls and scButtons)<>0) and
+ ((D.Loaded and scButtons)=0) then
+ begin
+ if D.btnarray[0]<>nil then
+ begin
+ D.Loaded:=D.Loaded or scButtons;
+ pleft:=StrCopyE(lleft,opt_bleft);
+ pleft^:='0'; (pleft+1)^:=#0;
+ for_check:=SmallInt(DBReadWord(0,PluginShort,lleft,word(-1)));
+ if for_check>=0 then
+ begin
+ ptop :=StrCopyE(ltop,opt_btop);
+ for i:=0 to HIGH(D.btnarray) do
+ begin
+ IntToStr(pleft,i);
+ IntToStr(ptop ,i);
+ D.btnarray[i].SetPosition(
+ DBReadWord(0,PluginShort,lleft,word(-1)),
+ DBReadWord(0,PluginShort,ltop ,word(-1)));
+ end;
+ end;
+ end;
+ end;
+end;
+
+procedure TWATFrame.CreateDesigner(Sender:PControl;var Mouse:TMouseEventData);
+var
+ D:PWATFrameData;
+begin
+ D:=CustomData;
+ if not D.ManualPlacement then exit;
+
+ if D.Designer=nil then
+ D.Designer:=NewDesigner(@self);
+
+ if not D.Designer.Active then
+ begin
+ // Trackbar
+ if D.Trackbar<>nil then
+ begin
+ D.Trackbar.Anchor(false,false,false,false);
+ D.Designer.Connect('Trackbar',D.Trackbar);
+ end;
+ // TextBlock
+ if D.TextBlock<>nil then
+ begin
+ D.TextBlock.Anchor(false,false,false,false);
+ D.Designer.Connect('Panel',D.TextBlock);
+ end;
+ // Icons
+ if (D.ShowControls and scButtons)<>0 then MakeButtonsDesigner;
+
+ D.Designer.Active:=true;
+ end
+ else
+ begin
+ D.Designer.Active:=False;
+
+ DesignerSaveSettings;
+ if D.Trackbar <>nil then D.Designer.Disconnect(D.Trackbar);
+ if D.TextBlock<>nil then D.Designer.Disconnect(D.TextBlock);
+ if (D.ShowControls and scButtons)<>0 then FreeButtonsDesigner;
+ Sender.Update;
+ end;
+end;
diff --git a/plugins/Watrack/kolframe/frm_dlg1.inc b/plugins/Watrack/kolframe/frm_dlg1.inc
new file mode 100644
index 0000000000..19f590af66
--- /dev/null
+++ b/plugins/Watrack/kolframe/frm_dlg1.inc
@@ -0,0 +1,283 @@
+{Frame}
+const
+ FSC_BACKGROUND = 1;
+ FSC_BEHAVIOUR = 2;
+ FSC_SHOW = 4;
+// FSC_ALPHA = 8;
+
+function MakePicFilter:PWideChar;
+var
+ buf:array [0..255] of WideChar;
+ size:integer;
+ pc:pWideChar;
+begin
+ FillChar(buf,SizeOf(buf),0);
+ pc:=StrCopyEW(StrCopyEW(buf,TranslateW('All Bitmaps')),' (*.bmp;*.jpg;*.gif;*.png)');
+ pc:=StrCopyEW(pc+1,'*.BMP;*.RLE;*.JPG;*.JPEG;*.GIF;*.PNG');
+ size:=(pc+2-@buf)*SizeOf(WideChar);
+ mGetMem(result,size);
+ move(buf,result^,size);
+end;
+
+procedure SwitchBk(Dialog:hwnd);
+var
+ en:boolean;
+begin
+ en:=IsDlgButtonChecked(Dialog,IDC_FRMUSEPIC)<>BST_UNCHECKED;
+ EnableWindow(GetDlgItem(Dialog,IDC_FRMBKPIC ),en);
+ EnableWindow(GetDlgItem(Dialog,IDC_FRMPICBTN),en);
+ EnableWindow(GetDlgItem(Dialog,IDC_CENTERX ),en);
+ EnableWindow(GetDlgItem(Dialog,IDC_CENTERY ),en);
+ EnableWindow(GetDlgItem(Dialog,IDC_TILEX ),en);
+ EnableWindow(GetDlgItem(Dialog,IDC_TILEY ),en);
+ EnableWindow(GetDlgItem(Dialog,IDC_STRETCHX ),en);
+ EnableWindow(GetDlgItem(Dialog,IDC_STRETCHY ),en);
+ EnableWindow(GetDlgItem(Dialog,IDC_PROP ),en);
+ EnableWindow(GetDlgItem(Dialog,IDC_BOTTOM ),en);
+ EnableWindow(GetDlgItem(Dialog,IDC_RIGHT ),en);
+ EnableWindow(GetDlgItem(Dialog,IDC_USECOVER ),en);
+end;
+
+function FrameViewDlg(Dialog:HWnd; hMessage,wParam,lParam:DWord):integer; stdcall;
+const
+ DlgInited:boolean=false;
+var
+ tmp:cardinal;
+ buf1:PAnsiChar;
+ buf:PAnsiChar;
+ p:PAnsiChar;
+ tmpb:longbool;
+ pcw,tmpPicName:pWideChar;
+ D:PWATFrameData;
+begin
+ result:=0;
+ case hMessage of
+ WM_INITDIALOG: begin
+ DlgInited:=false;
+
+ TranslateDialogDefault(Dialog);
+ D:=FrameCtrl.CustomData;
+
+ SendDlgItemMessage(Dialog,IDC_FRMALPHA,TBM_SETRANGE,0,MAKELONG(0,255));
+ SendDlgItemMessage(Dialog,IDC_FRMALPHA,TBM_SETPOS,1,D.FrmAlpha);
+
+ if (D.ShowControls and scButtons )<>0 then CheckDlgButton(Dialog,IDC_SHOWCTRLS,BST_CHECKED);
+ if (D.ShowControls and scTrackBar)<>0 then CheckDlgButton(Dialog,IDC_SHOWBAR ,BST_CHECKED);
+ if (D.ShowControls and scText )<>0 then CheckDlgButton(Dialog,IDC_SHOWTEXT ,BST_CHECKED);
+ if (D.ShowControls and scVolume )<>0 then CheckDlgButton(Dialog,IDC_SHOWVOLUM,BST_CHECKED);
+ if (D.ShowControls and scGap )<>0 then CheckDlgButton(Dialog,IDC_BTNGAP ,BST_CHECKED);
+
+ MakeHint(Dialog,IDC_MANUALPLACE,
+ 'Use manual frame elements placement. Doubleclick on free space to start designer.');
+ CheckDlgButton(Dialog,IDC_MANUALPLACE,ORD(D.ManualPlacement));
+
+ CheckDlgButton(Dialog,IDC_HIDEFRAMEPLAYER,ORD(D.HideNoPlayer));
+ CheckDlgButton(Dialog,IDC_HIDEFRAMEMUSIC ,ORD(D.HideNoMusic));
+ EnableWindow(GetDlgItem(Dialog,IDC_HIDEFRAMEMUSIC),D.HideNoPlayer);
+
+ SendDlgItemMessage(Dialog,IDC_FRMCOLOR,CPM_SETCOLOUR,0,D.BkColor);
+ SendDlgItemMessage(Dialog,IDC_FRMCOLOR,CPM_SETDEFAULTCOLOUR,0,GetSysColor(COLOR_BTNFACE));
+ SetDlgItemInt(Dialog,IDC_FRMCOLOR,D.BkColor,false);
+
+ if D.UseBkPicture then
+ CheckDlgButton(Dialog,IDC_FRMUSEPIC,ORD(D.UseBkPicture));
+ if D.BkDefFile=nil then
+ p:=''
+ else
+ p:=D.BkDefFile;
+ SetDlgItemTextA(Dialog,IDC_FRMBKPIC,p);
+ CheckDlgButton(Dialog,IDC_USECOVER,ORD(D.UseCover));
+
+ tmp:=D.UpdInterval;
+ if (tmp>=1000) and (tmp mod 1000=0) then
+ tmp:=tmp div 1000;
+ SetDlgItemInt(Dialog,IDC_TIMER,tmp,false);
+
+ SetDlgItemInt(Dialog,IDC_PADDING_LEFT ,D.padding.left ,false);
+ SetDlgItemInt(Dialog,IDC_PADDING_TOP ,D.padding.top ,false);
+ SetDlgItemInt(Dialog,IDC_PADDING_RIGHT ,D.padding.right ,false);
+ SetDlgItemInt(Dialog,IDC_PADDING_BOTTOM,D.padding.bottom,false);
+
+ if (D.BkMode and frbkCenterX )<>0 then CheckDlgButton(Dialog,IDC_CENTERX ,BST_CHECKED);
+ if (D.BkMode and frbkCenterY )<>0 then CheckDlgButton(Dialog,IDC_CENTERY ,BST_CHECKED);
+ if (D.BkMode and frbkTileX )<>0 then CheckDlgButton(Dialog,IDC_TILEX ,BST_CHECKED);
+ if (D.BkMode and frbkTileY )<>0 then CheckDlgButton(Dialog,IDC_TILEY ,BST_CHECKED);
+ if (D.BkMode and frbkStretchX )<>0 then CheckDlgButton(Dialog,IDC_STRETCHX,BST_CHECKED);
+ if (D.BkMode and frbkStretchY )<>0 then CheckDlgButton(Dialog,IDC_STRETCHY,BST_CHECKED);
+ if (D.BkMode and frbkProportional)<>0 then CheckDlgButton(Dialog,IDC_PROP ,BST_CHECKED);
+ if (D.BkMode and frbkBottom )<>0 then CheckDlgButton(Dialog,IDC_BOTTOM ,BST_CHECKED);
+ if (D.BkMode and frbkRight )<>0 then CheckDlgButton(Dialog,IDC_RIGHT ,BST_CHECKED);
+
+ SwitchBk(Dialog);
+
+ DlgInited:=true;
+ end;
+
+ WM_HSCROLL: begin
+ if DlgInited then
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+
+ WM_COMMAND: begin
+ if (wParam shr 16)=BN_CLICKED then
+ begin
+ case LoWord(wParam) of
+ IDC_HIDEFRAMEPLAYER: begin
+ EnableWindow(GetDlgItem(Dialog,IDC_HIDEFRAMEMUSIC),
+ IsDlgButtonChecked(Dialog,IDC_HIDEFRAMEPLAYER)<>BST_UNCHECKED);
+ end;
+
+ IDC_FRMUSEPIC: begin
+ SwitchBk(Dialog);
+ end;
+
+ IDC_FRMPICBTN: begin
+ pcw:=MakePicFilter;
+ tmpPicName:=GetDlgText(Dialog,IDC_FRMBKPIC);
+ mGetMem(buf,1024);
+ if ShowDlgW(pWideChar(buf),tmpPicName,pcw) then
+ SetDlgItemTextW(Dialog,IDC_FRMBKPIC,pWideChar(buf));
+ mFreeMem(buf);
+ mFreeMem(tmpPicName);
+ mFreeMem(pcw);
+ end;
+ end;
+ end;
+
+ if DlgInited then
+ if ((wParam shr 16)=BN_CLICKED) or
+ ((wParam shr 16)=EN_CHANGE) or
+ ((wParam shr 16)=CPN_COLOURCHANGED) then
+ begin
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+ end;
+
+ WM_NOTIFY: begin
+ if integer(PNMHdr(lParam)^.code)=PSN_APPLY then
+ begin
+ D:=FrameCtrl.CustomData;
+ // Alpha channel
+ tmp:=SendDlgItemMessage(Dialog,IDC_FRMALPHA,TBM_GETPOS,0,0);
+ if tmp<>Cardinal(D.FrmAlpha) then
+ begin
+ D.FrmAlpha:=tmp;
+ if D.FrameId>=0 then
+ FrameCtrl.SetAlpha(D.FrmAlpha);
+ end;
+
+ // show/hide controls
+ tmp:=0;
+ if IsDlgButtonChecked(Dialog,IDC_SHOWCTRLS)<>BST_UNCHECKED then tmp:=tmp or scButtons;
+ if IsDlgButtonChecked(Dialog,IDC_SHOWBAR )<>BST_UNCHECKED then tmp:=tmp or scTrackBar;
+ if IsDlgButtonChecked(Dialog,IDC_SHOWTEXT )<>BST_UNCHECKED then tmp:=tmp or scText;
+ if IsDlgButtonChecked(Dialog,IDC_SHOWVOLUM)<>BST_UNCHECKED then tmp:=tmp or scVolume;
+ if IsDlgButtonChecked(Dialog,IDC_BTNGAP )<>BST_UNCHECKED then tmp:=tmp or scGap;
+ D.ShowControls:=tmp;
+
+ tmp:=IsDlgButtonChecked(Dialog,IDC_MANUALPLACE);
+ // From Auto to Manual = keep position
+ if (tmp<>BST_UNCHECKED) and not D.ManualPlacement then D.Loaded:=$FFFF;
+ D.ManualPlacement:=tmp<>BST_UNCHECKED;
+
+ // Frame background
+ tmpb:=IsDlgButtonChecked(Dialog,IDC_USECOVER)<>BST_UNCHECKED;
+ if tmpb<>D.UseCover then
+ begin
+ D.UseCover:=tmpb;
+ end;
+ tmpb:=IsDlgButtonChecked(Dialog,IDC_FRMUSEPIC)<>BST_UNCHECKED;
+ if tmpb<>D.UseBkPicture then
+ begin
+ D.UseBkPicture:=tmpb;
+ end;
+ tmp:=SendDlgItemMessage(Dialog,IDC_FRMCOLOR,CPM_GETCOLOUR,0,0);
+ if tmp<>D.BkColor then
+ begin
+ D.BkColor:=tmp;
+ end;
+
+ mGetMem(buf1,1024{*SizeOf(WideChar)});
+ buf1^:=#0;
+ buf:=GetDlgText(Dialog,IDC_FRMBKPIC,true);
+ CallService(MS_UTILS_PATHTORELATIVE,dword(buf),dword(buf1));
+ if StrCmp(buf1,D.BkDefFile)<>0 then
+ begin
+ mFreeMem(D.BkDefFile);
+ StrDup(D.BkDefFile,buf1);
+ end;
+ mFreeMem(buf);
+ mFreeMem(buf1);
+
+ // Picture effects
+ tmp:=0;
+ if IsDlgButtonchecked(Dialog,IDC_CENTERX )<>BST_UNCHECKED then tmp:=tmp or frbkCenterX;
+ if IsDlgButtonchecked(Dialog,IDC_CENTERY )<>BST_UNCHECKED then tmp:=tmp or frbkCenterY;
+ if IsDlgButtonchecked(Dialog,IDC_BOTTOM )<>BST_UNCHECKED then tmp:=tmp or frbkBottom;
+ if IsDlgButtonchecked(Dialog,IDC_RIGHT )<>BST_UNCHECKED then tmp:=tmp or frbkRight;
+ if IsDlgButtonchecked(Dialog,IDC_TILEX )<>BST_UNCHECKED then tmp:=tmp or frbkTileX;
+ if IsDlgButtonchecked(Dialog,IDC_TILEY )<>BST_UNCHECKED then tmp:=tmp or frbkTileY;
+ if IsDlgButtonchecked(Dialog,IDC_STRETCHX)<>BST_UNCHECKED then tmp:=tmp or frbkStretchX;
+ if IsDlgButtonchecked(Dialog,IDC_STRETCHY)<>BST_UNCHECKED then tmp:=tmp or frbkStretchY;
+ if IsDlgButtonchecked(Dialog,IDC_PROP )<>BST_UNCHECKED then tmp:=tmp or frbkProportional;
+
+ if tmp<>D.BkMode then
+ begin
+ D.BkMode:=tmp;
+ end;
+
+ // Hide frame option
+ tmpb :=IsDlgButtonChecked(Dialog,IDC_HIDEFRAMEPLAYER)<>BST_UNCHECKED;
+ if tmpb<>D.HideNoPlayer then
+ begin
+ D.HideNoPlayer:=tmpb;
+ end;
+ tmpb:=IsDlgButtonChecked(Dialog,IDC_HIDEFRAMEMUSIC)<>BST_UNCHECKED;
+ if tmpb<>D.HideNoMusic then
+ begin
+ D.HideNoMusic:=tmpb;
+ end;
+
+ // Padding
+ tmp:=GetDlgItemInt(Dialog,IDC_PADDING_LEFT,tmpb,false);
+ if integer(tmp)<>D.padding.left then
+ begin
+ D.padding.left:=tmp;
+ end;
+ tmp:=GetDlgItemInt(Dialog,IDC_PADDING_TOP,tmpb,false);
+ if integer(tmp)<>D.padding.top then
+ begin
+ D.padding.top:=tmp;
+ end;
+ tmp:=GetDlgItemInt(Dialog,IDC_PADDING_RIGHT,tmpb,false);
+ if integer(tmp)<>D.padding.right then
+ begin
+ D.padding.right:=tmp;
+ end;
+ tmp:=GetDlgItemInt(Dialog,IDC_PADDING_BOTTOM,tmpb,false);
+ if integer(tmp)<>D.padding.bottom then
+ begin
+ D.padding.bottom:=tmp;
+ end;
+
+ tmp:=GetDlgItemInt(Dialog,IDC_TIMER,tmpb,false);
+ if tmp>0 then
+ begin
+ if tmp<100 then
+ tmp:=tmp*1000;
+ if tmp<200 then
+ tmp:=200;
+ end;
+
+ if tmp<>D.UpdInterval then
+ begin
+ D.UpdInterval:=tmp;
+ end;
+
+ FrameCtrl.SaveSettings;
+ end;
+ end;
+ else
+ {result:=}DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+end;
diff --git a/plugins/Watrack/kolframe/frm_dlg2.inc b/plugins/Watrack/kolframe/frm_dlg2.inc
new file mode 100644
index 0000000000..6325e05fcd
--- /dev/null
+++ b/plugins/Watrack/kolframe/frm_dlg2.inc
@@ -0,0 +1,172 @@
+{Frame text}
+const
+ MaxTxtScrollSpeed = 20;
+
+function FrameTextDlg(Dialog:HWnd; hMessage,wParam,lParam:DWord):integer; stdcall;
+const
+ DlgInited :boolean=false;
+ TemplateChanged:Boolean=false;
+var
+ tmp,tmp1:integer;
+ tmpb:longbool;
+ pcf:TCHOOSEFONT;
+ lf:LOGFONTW;
+begin
+ result:=0;
+
+ case hMessage of
+ WM_DESTROY: begin
+ // new - if Apply, old - if cancel
+ if FrameCtrl<>nil then // for case when FrameCtrl was destryed already
+ if PWATFrameData(FrameCtrl.CustomData).TextBlock<>nil then
+ PWATFrameData(FrameCtrl.CustomData).TextBlock.FontData:=TextLF;
+ end;
+
+ WM_INITDIALOG: begin
+ DlgInited:=false;
+ TranslateDialogDefault(Dialog);
+ with PWATFrameData(FrameCtrl.CustomData).TextBlock^ do
+ begin
+ case LoByte(Effects) of
+ effWrap: tmp:=IDC_EFF_WRAP;
+ effRoll: tmp:=IDC_EFF_ROLL;
+ effPong: tmp:=IDC_EFF_PONG;
+ else // like effCut
+ tmp:=IDC_EFF_CUT;
+ end;
+ CheckDlgButton(Dialog,tmp,BST_CHECKED);
+
+ SetDlgItemInt(Dialog,IDC_TIMER,UpdateTime,false);
+
+ SetDlgItemInt(Dialog,IDC_ROLLSTEP,RollStep,false);
+ SetDlgItemInt(Dialog,IDC_ROLLGAP ,RollGap ,false);
+ // SetDlgItemInt(Dialog,IDC_ROLLTAIL,RollTail,false);
+
+ CheckDlgButton(Dialog,IDC_ALCENTER,ord((Effects and effCenter)<>0));
+
+ SetDlgItemTextW(Dialog,IDC_FRAME_TEXT,PWATFrameData(FrameCtrl.CustomData).Template);
+ end;
+
+ SendDlgItemMessage(Dialog,IDC_MACRO_HELP,BM_SETIMAGE,IMAGE_ICON,
+ CallService(MS_SKIN_LOADICON,SKINICON_OTHER_HELP,0));
+
+ TemplateChanged:=false;
+ DlgInited:=true;
+ end;
+
+ WM_COMMAND: begin
+ case (wParam shr 16) of
+ EN_CHANGE: begin
+ if Loword(wParam)=IDC_FRAME_TEXT then
+ TemplateChanged:=True;
+ end;
+
+ BN_CLICKED: begin
+ case LoWord(wParam) of
+ IDC_MACRO_HELP: CallService(MS_WAT_MACROHELP,Dialog,0);
+ IDC_FRMFONT: begin
+ with PWATFrameData(FrameCtrl.CustomData).TextBlock^ do
+ begin
+ lf:=FontData;
+ // lf:=TextLF;
+ FillChar(pcf,sizeOf(pcf),0);
+ with pcf do
+ begin
+ lStructSize:=SizeOf(pcf);
+ lpLogFont:=@lf;
+ Flags:=CF_EFFECTS+CF_FORCEFONTEXIST+CF_LIMITSIZE+CF_NOVERTFONTS+
+ CF_SCREENFONTS+CF_INITTOLOGFONTSTRUCT;
+ rgbColors:=TextColor;
+ nSizeMin:=6;
+ nSizeMax:=32;
+ end;
+ if ChooseFont(pcf) then
+ begin
+ FontData:=lf; // paint directly
+ TextColor:=pcf.rgbColors;
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end
+ else
+ exit;
+ end;
+ end;
+ end;
+ end;
+ end;
+
+ if DlgInited then
+ case wParam shr 16 of
+ BN_CLICKED,
+ EN_CHANGE: begin
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+ end;
+ end;
+
+ WM_NOTIFY: begin
+ if integer(PNMHdr(lParam)^.code)=PSN_APPLY then
+ begin
+// redraw:=false;
+ with PWATFrameData(FrameCtrl.CustomData).TextBlock^ do
+ begin
+ tmp:=GetDlgItemInt(Dialog,IDC_TIMER,tmpb,false);
+ if tmp>MaxTxtScrollSpeed then
+ tmp:=MaxTxtScrollSpeed;
+
+ if tmp<>UpdateTime then
+ begin
+ UpdateTime:=tmp;
+{
+ if UpdTimer<>0 then
+ KillTimer(FrameWnd,UpdTimer);
+ if (UpdInterval>0) and (FrameWnd<>0) then
+ UpdTimer:=SetTimer(FrameWnd,TMR_TEXT,(MaxTxtScrollSpeed+1-UpdInterval)*100,nil)
+ else
+ UpdTimer:=0;
+}
+ end;
+
+ // Text effects
+ if IsDlgButtonChecked(Dialog,IDC_EFF_CUT )<>BST_UNCHECKED then tmp:=effCut
+ else if IsDlgButtonChecked(Dialog,IDC_EFF_WRAP)<>BST_UNCHECKED then tmp:=effWrap
+ else if IsDlgButtonChecked(Dialog,IDC_EFF_ROLL)<>BST_UNCHECKED then tmp:=effRoll
+ else if IsDlgButtonChecked(Dialog,IDC_EFF_PONG)<>BST_UNCHECKED then tmp:=effPong;
+ if IsDlgButtonChecked(Dialog,IDC_ALCENTER)<>BST_UNCHECKED then
+ tmp:=tmp or effCenter;
+ Effects:=tmp;
+
+ tmp1:=GetDlgItemInt(Dialog,IDC_ROLLSTEP,tmpb,false);
+ if tmp1<>RollStep then
+ begin
+ RollStep:=tmp1;
+ end;
+ tmp1:=GetDlgItemInt(Dialog,IDC_ROLLGAP ,tmpb,false);
+ if tmp1<>RollGap then
+ begin
+ RollGap:=tmp1;
+ end;
+ {
+ tmp1:=GetDlgItemInt(Dialog,IDC_ROLLTAIL,tmpb,false);
+ if tmp1<>RollTail then
+ begin
+ RollTail:=tmp1;
+ end;
+ }
+
+ if TemplateChanged then
+ begin
+ mFreeMem(PWATFrameData(FrameCtrl.CustomData).Template);
+ PWATFrameData(FrameCtrl.CustomData).Template:=GetDlgText(Dialog,IDC_FRAME_TEXT);
+ end;
+
+ TextLF:=FontData; // OK - saving for future?
+
+ SaveTextSettings(TemplateChanged);
+ TemplateChanged:=false;
+ end;
+ end;
+ end;
+ else
+ {result:=}DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+end;
diff --git a/plugins/Watrack/kolframe/frm_frame.inc b/plugins/Watrack/kolframe/frm_frame.inc
new file mode 100644
index 0000000000..8c3034327c
--- /dev/null
+++ b/plugins/Watrack/kolframe/frm_frame.inc
@@ -0,0 +1,497 @@
+{Frame + background}
+
+const
+ WS_EX_LAYERED = $00080000;
+
+function SetLayeredWindowAttributes(Hwnd: THandle; crKey: COLORREF; bAlpha: Byte; dwFlags: DWORD): Boolean; stdcall;
+ external user32 name 'SetLayeredWindowAttributes';
+
+const
+ defFrameText = '%artist% - %title%';
+
+const
+// opt_HiddenByMe:PAnsiChar = 'frame/hiddenbyme';
+ opt_ShowCtrls :PAnsiChar = 'frame/showcontrols';
+ opt_FrmUsePic :PAnsiChar = 'frame/frmusepic';
+ opt_FrmUseCvr :PAnsiChar = 'frame/frmusecover';
+ opt_FrmBkColor:PAnsiChar = 'frame/frmbkcolor';
+ opt_FrmBkPic :PAnsiChar = 'frame/frmbkpic';
+ opt_FrmBkMode :PAnsiChar = 'frame/frmbkmode';
+ opt_FrmAlpha :PAnsiChar = 'frame/frmalpha';
+ opt_HideFrameM:PAnsiChar = 'frame/hideframem';
+ opt_HideFrameP:PAnsiChar = 'frame/hideframep';
+ opt_FrmTimer :PAnsiChar = 'frame/frametimer';
+ opt_PadLeft :PAnsiChar = 'frame/paddingleft';
+ opt_PadTop :PAnsiChar = 'frame/paddingtop';
+ opt_PadRight :PAnsiChar = 'frame/paddingright';
+ opt_PadBottom :PAnsiChar = 'frame/paddingbottom';
+ opt_Manual :PAnsiChar = 'frame/manualplacement';
+
+procedure TWATFrame.ResetFrame;
+var
+ D:PWATFrameData;
+begin
+ D:=CustomData;
+ if D.Trackbar <>nil then ResetTrackbar(D.Trackbar);
+ if D.TextBlock<>nil then D.TextBlock.BlockText:=nil;
+// frame back to default
+ RefreshPicture(nil);
+end;
+
+procedure FrameTimerProc(wnd:HWND;uMsg:uint;idEvent:uint_ptr;dwTime:dword); stdcall;
+var
+ psi:pSongInfo;
+ D:PWATFrameData;
+begin
+ D:=FrameCtrl.CustomData;
+
+ if IsFrameHidden(D.FrameId) then
+ begin
+ if not D.wasHidden then
+ begin
+ D.wasHidden:=true;
+ D.TextBlock.UpdateTime:=0;
+ end;
+ exit;
+ end
+ else if D.wasHidden and (D.TextBlock.UpdateTime=0) then
+ begin
+ D.wasHidden:=false;
+ if (D.ShowControls and scText)<>0 then
+ D.TextBlock.UpdateTime:=DBReadWord(0,PluginShort,opt_TxtTimer,10);
+ end;
+
+ if D.Trackbar<>nil then
+ begin
+ if (CallService(MS_WAT_GETMUSICINFO,WAT_INF_CHANGES,dword(@psi))<>WAT_PLS_NOTFOUND) then
+ begin
+ SetTrackBarPosition(D.Trackbar,(psi^.time*1000) div D.UpdInterval)
+ end;
+ end;
+
+ UpdateTextBlock(D,false); // false - check for %percent%/%time%
+
+ FrameCtrl.Update;
+end;
+
+procedure TWATFrame.AdjustFrame;
+var
+ h:integer;
+ D:PWATFrameData;
+begin
+ D:=CustomData;
+
+ if D.ManualPlacement then
+ begin
+ if D.Trackbar <>nil then D.Trackbar .Anchor(false,false,false,false);
+ if D.TextBlock<>nil then D.TextBlock.Anchor(false,false,false,false);
+ DesignerLoadSettings;
+ exit;
+ end;
+
+ h:=Height; // or need to get FRAME height
+
+ if D.Trackbar<>nil then
+ begin
+ D.Trackbar.SetSize(Width-16,18);
+ dec(h,D.Trackbar.Height);
+ D.Trackbar.SetPosition(8,h);
+ D.Trackbar.Anchor(true,false,true,true);
+ end;
+
+ if (D.ShowControls and scButtons)<>0 then
+ begin
+ AdjustButtons(h-16-BtnGap);
+ dec(h,16+2*BtnGap);
+ end;
+
+ if D.TextBlock<>nil then
+ begin
+ D.TextBlock.Top :=awkTextPad;
+ D.TextBlock.Height:=h-D.TextBlock.Top;
+ D.TextBlock.Anchor(true,true,true,true);
+ end;
+end;
+
+procedure TWATFrame.SaveSettings;
+var
+ D:PWATFrameData;
+begin
+ D:=CustomData;
+ DBWriteByte (0,PluginShort,opt_Manual ,ord(D.ManualPlacement));
+ DBWriteByte (0,PluginShort,opt_HideFrameM,ord(D.HideNoMusic));
+ DBWriteByte (0,PluginShort,opt_HideFrameP,ord(D.HideNoPlayer));
+ DBWriteByte (0,PluginShort,opt_FrmUsePic ,ord(D.UseBkPicture));
+ DBWriteByte (0,PluginShort,opt_FrmUseCvr ,ord(D.UseCover));
+ DBWriteDWord (0,PluginShort,opt_FrmBkColor,D.BkColor);
+ DBWriteWord (0,PluginShort,opt_FrmBkMode ,D.BkMode);
+ DBWriteDWord (0,PluginShort,opt_ShowCtrls ,D.ShowControls);
+ DBWriteByte (0,PluginShort,opt_FrmAlpha ,D.FrmAlpha);
+ DBWriteWord (0,PluginShort,opt_FrmTimer ,D.UpdInterval);
+ DBWriteWord (0,PluginShort,opt_PadLeft ,D.padding.left);
+ DBWriteWord (0,PluginShort,opt_PadTop ,D.padding.top);
+ DBWriteWord (0,PluginShort,opt_PadRight ,D.padding.right);
+ DBWriteWord (0,PluginShort,opt_PadBottom ,D.padding.bottom);
+ DBWriteString(0,PluginShort,opt_FrmBkPic ,D.BkDefFile);
+
+ CheckControls;
+ AdjustFrame;
+ RefreshPicture;
+ InvalidateRect(FrameCtrl.GetWindowHandle,nil,true);
+ FrameCtrl.Update;
+
+ if D.UpdTimer<>0 then // FrameWnd MUST be present
+ begin
+ KillTimer(0,D.UpdTimer);
+ D.UpdTimer:=0;
+ end;
+ if D.UpdInterval>0 then
+ begin
+ D.UpdTimer:=SetTimer(0,0,D.UpdInterval,@FrameTimerProc);
+ end;
+end;
+
+procedure TWATFrame.LoadSettings;
+var
+ D:PWATFrameData;
+begin
+ D:=CustomData;
+ D.ManualPlacement:=DBReadByte (0,PluginShort,opt_Manual ,0)<>0;
+ D.HideNoMusic :=DBReadByte (0,PluginShort,opt_HideFrameM,0)<>0;
+ D.HideNoPlayer :=DBReadByte (0,PluginShort,opt_HideFrameP,0)<>0;
+ D.UseBkPicture :=DBReadByte (0,PluginShort,opt_FrmUsePic ,0)<>0;
+ D.UseCover :=DBReadByte (0,PluginShort,opt_FrmUseCvr ,0)<>0;
+ D.BkColor :=DBReadDWord(0,PluginShort,opt_FrmBkColor,$00E0E0E0);
+ D.BkMode :=DBReadWord (0,PluginShort,opt_FrmBkMode ,frbkCenter);
+ D.ShowControls :=DBReadDWord(0,PluginShort,opt_ShowCtrls ,scAll);
+ D.FrmAlpha :=DBReadByte (0,PluginShort,opt_FrmAlpha ,255);
+
+ D.UpdInterval:=DBReadWord(0,PluginShort,opt_FrmTimer,200);
+ if D.UpdInterval<100 then
+ D.UpdInterval:=D.UpdInterval*1000;
+
+ D.padding.left :=DBReadWord(0,PluginShort,opt_PadLeft ,0);
+ D.padding.top :=DBReadWord(0,PluginShort,opt_PadTop ,0);
+ D.padding.right :=DBReadWord(0,PluginShort,opt_PadRight ,0);
+ D.padding.bottom:=DBReadWord(0,PluginShort,opt_PadBottom,0);
+
+ D.BkDefFile:=DBReadString(0,PluginShort,opt_FrmBkPic,nil);
+ //!!!! saving NOT in TextBlock
+ D.Template:=DBReadUnicode(0,PluginShort,opt_FrameText,DefFrameText);
+end;
+
+{$include i_bitmap.inc}
+
+procedure TWATFrame.SetAlpha(value:integer);
+const
+ LWA_COLORKEY = $00000001;
+ LWA_ALPHA = $00000002;
+var
+ wnd:HWND;
+ x:cardinal;
+begin
+ if IsFrameFloated(PWATFrameData(CustomData).FrameId) then
+ begin
+ wnd:=GetParent(FrameCtrl.GetWindowHandle);
+ x:=GetWindowLongW(wnd,GWL_EXSTYLE);
+ if value<>255 then
+ begin
+ if (x and WS_EX_LAYERED)=0 then
+ SetWindowLongW(wnd,GWL_EXSTYLE,x or WS_EX_LAYERED);
+ SetLayeredWindowAttributes(wnd,0,value,LWA_ALPHA);
+ end
+ else if (x and WS_EX_LAYERED)<>0 then
+ SetWindowLongW(wnd,GWL_EXSTYLE,x and not WS_EX_LAYERED);
+ end;
+end;
+
+procedure TWATFrame.FrameResize(Sender: PObj);
+var
+ tmpBmp:HBITMAP;
+ D:PWATFrameData;
+begin
+ D:=CustomData;
+ if D.BkDC<>0 then
+ begin
+ tmpBmp:=GetCurrentObject(D.BkDC,OBJ_BITMAP);
+ DeleteDC(D.BkDC);
+ D.BkDC:=0;
+ DeleteObject(tmpBmp);
+ end;
+ AdjustFrame;
+end;
+
+procedure BkTimerProc(wnd:HWND;uMsg:uint;idEvent:uint_ptr;dwTime:dword); stdcall;
+var
+ D:PWATFrameData;
+begin
+ D:=FrameCtrl.CustomData;
+ KillTimer(0,D.BkTimer);
+ D.BkTimer:=0;
+ DeleteObject(D.BkBitmap);
+ D.BkBitmap:=0;
+end;
+
+procedure TWATFrame.RefreshPicture(cover:PAnsiChar=nil);
+var
+ D:PWATFrameData;
+begin
+ D:=CustomData;
+ if D.BkBitmap<>0 then
+ BkTimerProc(0,0,0,0); // remove old picture
+
+ FrameResize(nil); // clear frame bitmap buffer
+
+ if D.UseBkPicture then
+ D.BkBitmap:=LoadBkPicture(cover,true,D.BkDefFile);
+
+ if D.BkBitmap=HBITMAP(-1) then // same file
+ D.BkBitmap:=0;
+ Update;
+end;
+
+procedure TWATFrame.Paint(Sender: PControl; DC: HDC);
+var
+ rc: TRect;
+ br:HBRUSH;
+ D:PWATFrameData;
+begin
+ D:=CustomData;
+ GetClientRect(Sender.Handle,rc);
+ if D.UseBkPicture then
+ begin
+ if D.BkDC=0 then
+ begin
+ if D.BkBitmap=0 then
+ begin
+ if (D.BkFile<>nil) and (D.BkFile^<>#0) then
+ D.BkBitmap:=CallService(MS_UTILS_LOADBITMAP,0,dword(D.BkFile));
+ end;
+
+ if D.BkBitmap<>0 then
+ begin
+ PreparePicture(dc,rc);
+ D.BkTimer:=SetTimer(0,0,10000,@BkTimerProc);
+ end;
+ end;
+ if D.BkDC<>0 then
+ begin
+ BitBlt(dc,rc.left,rc.top,rc.right-rc.left,rc.bottom-rc.top,
+ D.BkDC,rc.left,rc.top,SRCCOPY);
+ exit;
+ end;
+ end;
+
+ InflateRect(rc,1,1);
+ br:=CreateSolidBrush(D.BkColor);
+ FillRect(dc,rc,br);
+ DeleteObject(br);
+end;
+
+// JUST LOAD picture, no matter, which transforms
+// Backname = from settings, Covername = from data (higher priority)
+// -1 - same file, 0 - can't load, other - new bitmap
+function TWATFrame.LoadBkPicture(CoverFName:PAnsiChar;check:boolean=false;
+ BackFName:PAnsiChar=nil):integer;
+var
+ tmpstr:PAnsiChar;
+ D:PWATFrameData;
+begin
+ result:=0;
+ D:=CustomData;
+
+ // check the same file, ie only 'next pic'
+ if (CoverFName<>nil) and (CoverFName^<>#0) then
+ begin
+ if check and (StrCmp(CoverFName,D.BkFile)=0) then
+ begin
+ result:=-1;
+ Exit;
+ end;
+
+ result:=CallService(MS_UTILS_LOADBITMAP,0,dword(CoverFName));
+ if result<>0 then
+ begin
+ mFreeMem(D.BkFile);
+ StrDup(D.BkFile,CoverFName);
+ Exit;
+ end;
+ end;
+
+ if (BackFName<>nil) and (BackFName^<>#0) then
+ begin
+ tmpstr:=ParseVarString(BackFName);
+ if (tmpstr<>nil) and (tmpstr^<>#0) then
+ begin
+ if (not check) or (StrCmp(tmpstr,D.BkFile)<>0) then
+ begin
+ result:=CallService(MS_UTILS_LOADBITMAP,0,dword(tmpstr));
+ if result<>0 then
+ begin
+ mFreeMem(D.BkFile);
+ StrDup(D.BkFile,tmpstr);
+ end;
+ end
+ else
+ result:=-1;
+ end;
+ mFreeMem(tmpstr);
+ end;
+end;
+
+procedure TWATFrame.ClearBitmapData;
+var
+ D:PWATFrameData;
+ tmpBmp:HBITMAP;
+begin
+ D:=CustomData;
+
+ if D.BkTimer<>0 then
+ begin
+ KillTimer(0,D.BkTimer);
+ D.BkTimer:=0;
+ end;
+ if D.BkDC<>0 then
+ begin
+ tmpBmp:=GetCurrentObject(D.BkDC,OBJ_BITMAP);
+ DeleteDC(D.BkDC);
+ D.BkDC:=0;
+ DeleteObject(tmpBmp);
+ end;
+ if D.BkBitmap<>0 then
+ begin
+ DeleteObject(D.BkBitmap);
+ D.BkBitmap:=0;
+ end;
+ mFreeMem(D.BkFile);
+end;
+
+procedure TWATFrame.MyDestroy(sender:PObj);
+var
+ D:PWATFrameData;
+begin
+ D:=CustomData;
+ if D.UpdTimer<>0 then
+ begin
+ KillTimer(0,D.UpdTimer);
+ D.UpdTimer:=0;
+ end;
+
+ mFreeMem(D.Template);
+ mFreeMem(D.BkDefFile);
+ ClearBitmapData;
+
+ if D.Designer<>nil then
+ begin
+ D.Designer.Free;
+ D.Designer:=nil;
+ end;
+
+end;
+
+procedure TWATFrame.RefreshAllFrameIcons;
+var
+ D:PWATFrameData;
+begin
+ D:=CustomData;
+ if (D.ShowControls and scButtons)<>0 then RefreshButtonIcons;
+ if D.Trackbar<>nil then RefreshTrackbarIcons(D.Trackbar);
+end;
+
+procedure TWATFrame.CheckControls;
+var
+ D:PWATFrameData;
+ psi:pSongInfo;
+begin
+ D:=CustomData;
+
+ if (D.ShowControls and scTrackBar)<>0 then
+ begin
+ if D.Trackbar=nil then
+ begin
+ RegisterButtonIcons;
+ D.Trackbar:=MakeNewTrackbar(@self);
+ // for case when TB creating after track start (fastest way)
+ // can use (CallService(MS_WAT_GETMUSICINFO,WAT_INF_CHANGES,dword(@psi))<>WAT_PLS_NOTFOUND)
+ psi:=pointer(CallService(MS_WAT_RETURNGLOBAL,WAT_INF_UNICODE,1));
+ TrackbarSetRange(D.Trackbar,D.UpdInterval,psi^.total);
+ end;
+ end
+ else if D.Trackbar<>nil then
+ begin
+ D.Trackbar.Free;
+ D.Trackbar:=nil;
+ end;
+
+ if (D.ShowControls and scButtons)<>0 then
+ begin
+ if D.btnarray[0]=nil then
+ begin
+ if RegisterButtonIcons then
+ MakeNewButtonGroup;
+ end
+ end
+ else if D.btnarray[0]<>nil then
+ FreeButtons;
+{
+ if (D.ShowControls and scText)<>0 then
+ begin
+}
+ if D.TextBlock=nil then
+ begin
+ D.TextBlock:=MakeTextBlock(@self,D.BkColor);
+ end;
+
+ if (D.ShowControls and scText)<>0 then
+ D.TextBlock.UpdateTime:=DBReadWord(0,PluginShort,opt_TxtTimer,10);
+{
+ end
+ else if D.TextBlock<>nil then
+ begin
+ D.TextBlock.Free;
+ D.TextBlock:=nil;
+ end;
+}
+ if D.UseBkPicture then
+ begin
+ D.BkBitmap:=LoadBkPicture(nil,true,D.BkDefFile);
+ if D.BkBitmap=HBITMAP(-1) then
+ D.BkBitmap:=0;
+ end
+ else
+ ClearBitmapData;
+end;
+
+function CreateFrameWindow(parent:HWND):THANDLE;
+var
+ D:PWATFrameData;
+begin
+ result:=0;
+
+ FrameCtrl:=PWATFrame(NewAlienPanel(parent,esNone));
+ if FrameCtrl<>nil then
+ begin
+ GetMem (D ,SizeOf(TWATFrameData));
+ FillChar(D^,SizeOf(TWATFrameData),0); // clear all including buttons
+ with FrameCtrl^ do
+ begin
+ CustomData:=D;
+ LoadSettings;
+
+ result:=GetWindowHandle;
+
+ CheckControls;
+
+ MinWidth :=80;
+ MinHeight:=30;
+
+ OnPaint :=FrameCtrl.Paint;
+ OnResize :=FrameCtrl.FrameResize;
+// OnMouseDown :=TOnMouse(MakeMethod(nil, @MouseDown));
+ OnMouseDblClk:=FrameCtrl.CreateDesigner;
+ end;
+ FrameCtrl.OnDestroy:=FrameCtrl.MyDestroy;
+// theoretically, must get Resize here.... or after
+// FrameCtrl.AdjustFrame;
+ end;
+end;
diff --git a/plugins/Watrack/kolframe/frm_icogroup.inc b/plugins/Watrack/kolframe/frm_icogroup.inc
new file mode 100644
index 0000000000..2b6e9de6f9
--- /dev/null
+++ b/plugins/Watrack/kolframe/frm_icogroup.inc
@@ -0,0 +1,115 @@
+{Panel = group of icons}
+
+procedure TWATFrame.AdjustButtons(atop:integer);
+var
+ i,lWidth,lOffs,gap:integer;
+ lleft:integer;
+ D:PWATFrameData;
+begin
+ D:=CustomData;
+
+ if D.btnarray[0]=nil then exit;
+
+ if (D.ShowControls and scGap)<>0 then
+ gap:=BtnGap
+ else
+ gap:=0;
+
+ D.btnarray[0].Visible:=(D.ShowControls and scVolume)<>0;
+ D.btnarray[1].Visible:=(D.ShowControls and scVolume)<>0;
+
+ lWidth:=(Length(D.btnarray)-2)*(16+gap)-gap;
+ if (D.ShowControls and scVolume)<>0 then
+ Inc(lWidth, VolBtnDist+2*(16+gap));
+ lLeft:=(Width-lWidth) div 2;
+
+ lOffs:=0;
+ if (D.ShowControls and scVolume)<>0 then
+ begin
+ D.btnarray[0].Left:=lleft+lOffs;
+ D.btnarray[0].Top :=atop;
+ Inc(lOffs,16+gap);
+ D.btnarray[1].Left:=lleft+lOffs;
+ D.btnarray[1].Top :=atop;
+ Inc(lOffs,16+gap+VolBtnDist);
+ end;
+
+ for i:=2 to HIGH(D.btnarray) do
+ begin
+ D.btnarray[i].Left:=lleft+lOffs;
+ D.btnarray[i].Top :=atop;
+ Inc(lOffs,16+gap);
+ end;
+
+end;
+
+procedure TWATFrame.RefreshButtonIcons;
+var
+ i:integer;
+ D:PWATFrameData;
+begin
+ D:=CustomData;
+
+ if D.btnarray[0]<>nil then exit;
+
+ for i:=0 to HIGH(D.btnarray) do
+ D.btnarray[i].RefreshIcon;
+end;
+
+procedure TWATFrame.FreeButtonsDesigner;
+var
+ i:integer;
+ D:PWATFrameData;
+begin
+ D:=CustomData;
+
+ if D.btnarray[0]<>nil then exit;
+
+ for i:=0 to HIGH(D.btnarray) do
+ D.Designer.Disconnect(D.btnarray[i]);
+end;
+
+procedure TWATFrame.MakeButtonsDesigner;
+var
+ i:integer;
+ D:PWATFrameData;
+begin
+ D:=CustomData;
+
+ if D.btnarray[0]<>nil then exit;
+
+ for i:=0 to HIGH(D.btnarray) do
+ D.Designer.Connect('Button',D.btnarray[i],DESIGNER_NORESIZE);
+end;
+
+procedure TWATFrame.FreeButtons;
+var
+ i:integer;
+ D:PWATFrameData;
+begin
+ D:=CustomData;
+
+ if D.btnarray[0]<>nil then exit;
+
+ for i:=0 to HIGH(D.btnarray) do
+ begin
+ D.btnarray[i].Free;
+ D.btnarray[i]:=nil;
+ end;
+end;
+
+procedure TWATFrame.MakeNewButtonGroup;
+var
+ D:PWATFrameData;
+begin
+ D:=CustomData;
+
+ D.btnarray[0]:=CreateIcoButton(@self,waticons.GetIcon,DoAction,WAT_CTRL_VOLDN,300);
+ D.btnarray[1]:=CreateIcoButton(@self,waticons.GetIcon,DoAction,WAT_CTRL_VOLUP,300);
+ D.btnarray[2]:=CreateIcoButton(@self,waticons.GetIcon,DoAction,WAT_CTRL_PREV,2000);
+ D.btnarray[3]:=CreateIcoButton(@self,waticons.GetIcon,DoAction,WAT_CTRL_PLAY);
+ D.btnarray[4]:=CreateIcoButton(@self,waticons.GetIcon,DoAction,WAT_CTRL_PAUSE);
+ D.btnarray[5]:=CreateIcoButton(@self,waticons.GetIcon,DoAction,WAT_CTRL_STOP);
+ D.btnarray[6]:=CreateIcoButton(@self,waticons.GetIcon,DoAction,WAT_CTRL_NEXT,2000);
+end;
+
diff --git a/plugins/Watrack/kolframe/frm_rc.inc b/plugins/Watrack/kolframe/frm_rc.inc
new file mode 100644
index 0000000000..25ac437b3b
--- /dev/null
+++ b/plugins/Watrack/kolframe/frm_rc.inc
@@ -0,0 +1,56 @@
+{DLG 5 - frame}
+const
+ IDC_HELP_FORMAT = 1025;
+ IDC_HELP_COLOR = 1026;
+ IDC_HELP_VARIABLES = 1027;
+const
+ IDC_TIMER = 1028;
+ IDC_SHOWCTRLS = 1032;
+ IDC_SHOWTEXT = 1033;
+ IDC_SHOWVOLUM = 1034;
+ IDC_FRMCOLOR = 1036;
+ IDC_FRMUSEPIC = 1037;
+ IDC_FRMBKPIC = 1038;
+ IDC_FRMPICBTN = 1039;
+ IDC_CENTERX = 1040;
+ IDC_CENTERY = 1041;
+ IDC_TILEX = 1042;
+ IDC_TILEY = 1043;
+ IDC_STRETCHX = 1044;
+ IDC_STRETCHY = 1045;
+ IDC_PROP = 1046;
+ IDC_BOTTOM = 1047;
+ IDC_RIGHT = 1048;
+ IDC_FRMALPHA = 1049;
+ IDC_TRACKBAR = 1050;
+ IDC_BTNGAP = 1051;
+ IDC_SHOWBAR = 1052;
+ IDC_USECOVER = 1053;
+
+ IDC_HIDEFRAMEMUSIC = 1054;
+ IDC_HIDEFRAMEPLAYER = 1055;
+
+ IDC_PADDING_LEFT = 1056;
+ IDC_PADDING_TOP = 1057;
+ IDC_PADDING_RIGHT = 1058;
+ IDC_PADDING_BOTTOM = 1059;
+
+ IDC_MANUALPLACE = 1060;
+
+{DLG 51 - frame 2}
+ IDC_FRMTXT = 1031;
+ IDC_EFF_CUT = 1032;
+ IDC_EFF_WRAP = 1033;
+ IDC_EFF_ROLL = 1034;
+ IDC_EFF_PONG = 1035;
+ IDC_ROLLSTEP = 1036;
+ IDC_ROLLGAP = 1037;
+// IDC_ROLLTAIL = 1038;
+ IDC_ALCENTER = 1039;
+ IDC_FRMFONT = 1040;
+
+ IDC_FRAME_TEXT = 1041;
+ IDC_MACRO_HELP = 1042;
+
+{Frame}
+ IDC_FRM_POS = 1032;
diff --git a/plugins/Watrack/kolframe/frm_text.inc b/plugins/Watrack/kolframe/frm_text.inc
new file mode 100644
index 0000000000..66c00a1b2b
--- /dev/null
+++ b/plugins/Watrack/kolframe/frm_text.inc
@@ -0,0 +1,90 @@
+{Text}
+
+const
+ TextLF:TLOGFONTW = (
+ lfHeight :-10;
+ lfWidth :0;
+ lfEscapement :0;
+ lfOrientation :0;
+ lfWeight :FW_DONTCARE;
+ lfItalic :0;
+ lfUnderline :0;
+ lfStrikeOut :0;
+ lfCharSet :DEFAULT_CHARSET;
+ lfOutPrecision :OUT_DEFAULT_PRECIS;
+ lfClipPrecision :CLIP_DEFAULT_PRECIS;
+ lfQuality :DEFAULT_QUALITY;
+ lfPitchAndFamily:DEFAULT_PITCH or FF_DONTCARE{;
+ lfFaceName :#0});
+
+const
+ opt_FrmTxtClr :PAnsiChar = 'frame/frametextcolor';
+ opt_FrmFont :PAnsiChar = 'frame/framefont';
+ opt_FrmEffect :PAnsiChar = 'frame/txteffect';
+ opt_RollStep :PAnsiChar = 'frame/rollstep';
+ opt_RollGap :PAnsiChar = 'frame/rollgap';
+// opt_RollTail :PAnsiChar = 'frame/rolltail';
+ opt_AlgnCenter:PAnsiChar = 'frame/aligncenter';
+ opt_TxtTimer :PAnsiChar = 'frame/texttimer';
+ opt_FrameText :PAnsiChar = 'frame/frametext';
+
+procedure UpdateTextBlock(D:PWATFrameData;force:boolean);
+var
+ tmp:pWideChar;
+begin
+ if (D.ShowControls and scText)=0 then exit;
+ if D.TextBlock=nil then exit;
+
+ if not force then
+ begin
+ if (StrPosW(D.Template,'%percent%')=nil) and
+ (StrPosW(D.Template,'%time%' )=nil) then // need to |remake
+ exit;
+ end;
+ tmp:=pWideChar(CallService(MS_WAT_REPLACETEXT,0,dword(D.Template)));
+ D.TextBlock.BlockText:=tmp;
+ mFreeMem(tmp);
+end;
+
+procedure SaveTextSettings(withtemplate:boolean);
+var
+ D:PWATFrameData;
+begin
+ D:=FrameCtrl.CustomData;
+ if D.TextBlock=nil then exit;
+
+// DBWriteByte (0,PluginShort,opt_RollTail ,RollTail);
+ DBWriteDWord(0,PluginShort,opt_FrmTxtClr,D.TextBlock.TextColor); // reaction on chunk?
+ DBWriteByte (0,PluginShort,opt_RollStep ,D.TextBlock.RollStep);
+ DBWriteByte (0,PluginShort,opt_RollGap ,D.TextBlock.RollGap);
+ DBWriteWord (0,PluginShort,opt_FrmEffect,D.TextBlock.Effects);
+ DBWriteWord (0,PluginShort,opt_TxtTimer ,D.TextBlock.UpdateTime);
+
+ DBWriteStruct(0,PluginShort,opt_FrmFont,@TextLF,SizeOf(TLOGFONT));
+
+ if withtemplate then
+ begin
+ DBWriteUnicode(0,PluginShort,opt_FrameText,D.Template);
+ UpdateTextBlock(D,true);
+ end;
+end;
+
+procedure LoadTextSettings(TB:pTextBlock);
+begin
+ if TB=nil then exit;
+// RollTail :=DBReadByte (0,PluginShort,opt_RollTail ,20);
+ TB.RollStep :=DBReadByte (0,PluginShort,opt_RollStep ,2);
+ TB.RollGap :=DBReadByte (0,PluginShort,opt_RollGap ,16);
+ TB.TextColor :=DBReadDWord(0,PluginShort,opt_FrmTxtClr,0);
+ TB.Effects :=DBReadWord (0,PluginShort,opt_FrmEffect,effCut or effCenter);
+ DBReadStruct(0,PluginShort,opt_FrmFont,@TextLF,SizeOf(TextLF));
+ TB.FontData :=TextLF;
+ TB.UpdateTime:=DBReadWord (0,PluginShort,opt_TxtTimer ,10);
+end;
+
+function MakeTextBlock(AOwner:PControl;BkColor:TCOLORREF):pTextBlock;
+begin
+ result:=MakeNewTextBlock(AOwner,BkColor);
+// result.OnMouseDown:=TOnMouse(MakeMethod(nil, @MouseDown));
+ LoadTextSettings(result);
+end;
diff --git a/plugins/Watrack/kolframe/frm_trackbar.inc b/plugins/Watrack/kolframe/frm_trackbar.inc
new file mode 100644
index 0000000000..d6e08c56a7
--- /dev/null
+++ b/plugins/Watrack/kolframe/frm_trackbar.inc
@@ -0,0 +1,229 @@
+{Trackbar}
+type
+ pAWKTrackbar = ^tAWKTrackbar;
+ tAWKTrackbar = object(TObj)
+ total:integer;
+ UpdInterval:integer;
+ OldMouseDown,
+ OldMouseUp:TOnMouse;
+
+ procedure CtrlResize(Sender: PObj);
+ procedure Erase(Sender: PControl; DC: HDC);
+ procedure Paint(Sender: PControl; DC: HDC);
+ procedure Scroll(Sender:PTrackbar; Code:Integer);
+ procedure PressButton (Sender: PControl;var Mouse: TMouseEventData);
+ procedure UnPressButton(Sender: PControl;var Mouse: TMouseEventData);
+ procedure DragButton (Sender: PControl;var Mouse: TMouseEventData);
+ end;
+
+procedure ResetTrackbar(Trackbar:PControl);
+begin
+ if Trackbar=nil then exit;
+
+ with pTrackbar(Trackbar)^ do
+ begin
+ RangeMin:=0;
+ RangeMax:=0;
+ Position:=0;
+ end;
+end;
+
+procedure TrackbarSetRange(Trackbar:PTrackbar;timer:integer;total:integer=-1);
+var
+ D:pAWKTrackbar;
+ lpercent:real;
+begin
+ if Trackbar=nil then exit;
+
+ with Trackbar^ do
+ begin
+ D:=pointer(CustomObj);
+ if total<0 then // changing timer only
+ begin
+ total:=D.total;
+ if RangeMax>0 then
+ lpercent:=position/RangeMax
+ else
+ lpercent:=0;
+ end
+ else // for new track
+ begin
+ D.total:=total;
+ lpercent:=0;
+ end;
+ D.UpdInterval:=timer;
+ total:=(total*1000) div timer;
+ RangeMax:=total;
+ LineSize:=total div 100;
+ PageSize:=total div 10;
+ Position:=round(lpercent*total);
+ end;
+end;
+
+procedure SetTrackbarPosition(Trackbar:PTrackbar;pos:integer);
+begin
+ if Trackbar=nil then exit;
+//?? if Sender.ChildCount=0 then exit;
+ if pIcoButton(Trackbar.Children[0]).State<>AST_PRESSED then
+ Trackbar.Position:=pos;
+
+ Trackbar.Update;
+end;
+
+function CoordToPos(Trackbar:PTrackbar;x:integer):integer;
+var
+ range:integer;
+ rmin,rmax:integer;
+ offsetthumb,width:integer;
+ rc:TRect;
+begin
+ result:=0;
+ if Trackbar=nil then exit;
+
+ rmin:=Trackbar.RangeMin;
+ rmax:=Trackbar.RangeMax;
+ range:=rmax-rmin; // logic width
+
+ offsetthumb:=Trackbar.ThumbLen div 2;
+
+ rc:=Trackbar.ChannelRect;
+ width:= (rc.right-rc.left)-(offsetthumb*2)-1;
+ result:=(range*(x-rc.left-offsetthumb)) div width;
+
+ inc(result,rmin);
+ if result>rmax then
+ result:=rmax
+ else if result<rmin then
+ result:=rmin;
+end;
+
+procedure tAWKTrackbar.PressButton(Sender: PControl;var Mouse: TMouseEventData);
+begin
+ pAWKTrackbar(Sender.Parent.CustomObj).OldMouseDown(Sender,Mouse);
+
+ pIcoButton(Sender)^.Action:=PTrackbar(Sender.Parent).Position;
+end;
+
+procedure tAWKTrackbar.UnPressButton(Sender: PControl;var Mouse: TMouseEventData);
+begin
+ pAWKTrackbar(Sender.Parent.CustomObj).OldMouseUp(Sender,Mouse);
+
+ CallService(MS_WAT_PRESSBUTTON,WAT_CTRL_SEEK,
+ pIcoButton(Sender)^.Action*pAWKTrackbar(Sender.Parent.CustomObj).UpdInterval div 1000);
+ pIcoButton(Sender)^.Action:=-1;
+end;
+
+procedure tAWKTrackbar.DragButton(Sender: PControl;var Mouse: TMouseEventData);
+var
+ pos:integer;
+begin
+ with pIcoButton(Sender)^ do
+ if State=AST_PRESSED then
+ begin
+ pos:=CoordToPos(PTrackbar(Sender.Parent),Sender.Left+Mouse.X);
+ if Action<>pos then
+ begin
+ Action:=pos;
+ PTrackbar(Sender.Parent).Position:=pos;
+ end;
+ end;
+end;
+
+procedure tAWKTrackbar.Scroll(Sender:PTrackbar; Code:Integer);
+begin
+ if code=TB_ENDTRACK then
+ begin
+ CallService(MS_WAT_PRESSBUTTON,WAT_CTRL_SEEK,
+ Sender.Position*pAWKTrackbar(Sender.CustomObj).UpdInterval div 1000);
+ end;
+end;
+
+procedure tAWKTrackbar.CtrlResize(Sender: PObj);
+var
+ tmp:integer;
+begin
+ tmp:=PControl(Sender).Parent.Width-16;
+ if (PTrackbar(Sender)^.Width)>tmp then
+ PTrackbar(Sender)^.Width:=tmp;
+ //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ // need to move slider here
+// PControl(Sender).Update;
+end;
+
+procedure tAWKTrackbar.Erase(Sender: PControl; DC: HDC);
+begin
+// Sender.Parent.Update;
+end;
+
+procedure tAWKTrackbar.Paint(Sender: PControl; DC: HDC);
+var
+ rc, rc1:TRECT;
+ w:integer;
+begin
+ SendMessage(Sender.Handle,TBM_GETTHUMBRECT,0,dword(@rc));
+
+ w:=rc.right-rc.left;
+ if w<>16 then
+ rc.left:=rc.left+(w div 2)-8;
+
+ copyRect(rc1,Sender.BoundsRect);
+ rc1.Right :=rc1.Right-rc1.Left-8;
+ rc1.Left :=4;
+ rc1.Top :=7{((rc1.Bottom-rc1.Top) div 2)-2};
+ rc1.Bottom:=rc1.Top+4;
+ DrawEdge(DC,rc1,EDGE_SUNKEN,BF_RECT or BF_ADJUST);
+ if Sender.ChildCount>0 then
+ Sender.Children[0].Left:=rc.Left
+ else
+ begin
+ rc.right:=rc.left+8;
+ DrawFrameControl(DC,rc,DFC_BUTTON,DFCS_BUTTONPUSH);
+ end;
+end;
+
+procedure RefreshTrackbarIcons(Owner:PControl);
+begin
+ if Owner.ChildCount>0 then
+ pIcoButton(Owner.Children[0]).RefreshIcon;
+end;
+
+function MakeNewTrackBar(AOwner:PControl):PTrackbar;
+var
+ D:pAWKTrackbar;
+ btn:pIcoButton;
+begin
+ New(D, Create);
+ result:=NewTrackbar(AOwner,[trbNoTicks,trbBoth,trbNoBorder],D.Scroll);
+
+ with result^ do
+ begin
+ Transparent:=true;
+ CustomObj:=D;
+ SetSize(AOwner.Width-16,18);
+ SetPosition({AOwner.Left+}8,{AOwner.Top+}AOwner.Height-18);
+ Anchor(true,false,true,true);
+ ThumbLen:=16;
+
+ RangeMin:=0;
+ RangeMax:=100;
+
+ btn:=CreateIcoButton(result,GetIcon,DoAction,WAT_CTRL_SEEK);
+
+ if btn<>nil then
+ begin
+ btn.ResetEvent(idx_fOnClick);
+ D.OldMouseDown:=btn.OnMouseDown;
+ btn.OnMouseDown:=D.PressButton;
+ D.OldMouseUp:=btn.OnMouseUp;
+ btn.OnMouseUp :=D.UnPressButton;
+ btn.OnMouseMove:=D.DragButton;
+ end;
+
+ OnResize :=D.CtrlResize;
+ OnEraseBkGnd:=D.Erase;
+ OnPaint :=D.Paint;
+
+// OnScroll :=D.Scroll;
+ end;
+
+end;
diff --git a/plugins/Watrack/kolframe/frm_vars.inc b/plugins/Watrack/kolframe/frm_vars.inc
new file mode 100644
index 0000000000..88dd71f0d1
--- /dev/null
+++ b/plugins/Watrack/kolframe/frm_vars.inc
@@ -0,0 +1,80 @@
+{Frame variables}
+var
+ sic,
+ PlStatusHook:cardinal;
+
+ HiddenByMe:bool;
+
+ FrameHeight:dword;
+
+type
+ PWATFrameData = ^TWATFrameData;
+ TWATFrameData = record
+ BkDC :HDC; // "buffer" DC and associated bitmap
+ BkBitmap :HBITMAP; // original bitmap
+ BkFile :PAnsiChar; // original bitmap filename
+ BkDefFile :PAnsiChar; // default background picture filename
+ BkMode :cardinal;
+ BkTimer :cardinal; // timer to free original picture buffer
+ BkColor :TCOLORREF;
+ padding :TRect;
+
+ ShowControls:dword;
+ UseBkPicture:bool;
+ UseCover :bool;
+ HideNoMusic :bool;
+ HideNoPlayer:bool;
+
+ WasHidden :bool;
+ // not sure what will keep here
+ FrameId :integer;
+ FrmAlpha :integer;
+ UpdInterval :cardinal;
+ UpdTimer :cardinal;
+
+ Template :PWideChar;
+
+ Trackbar :PTrackbar;
+ TextBlock:PTextBlock;
+
+ // Designer section
+ Designer :PDesigner;
+ ManualPlacement:bool;
+ Loaded :dword;
+
+ btnarray:array [0..numbuttons-1] of pIcoButton;
+ end;
+
+ PWATFrame = ^TWATFrame;
+ TWATFrame = object(TControl)
+ procedure Paint(Sender: PControl; DC: HDC);
+ procedure FrameResize(Sender: PObj);
+ procedure RefreshAllFrameIcons;
+ procedure ResetFrame;
+
+ procedure CreateDesigner(Sender:PControl;var Mouse:TMouseEventData);
+ procedure DesignerSaveSettings;
+ procedure DesignerLoadSettings;
+
+ procedure PreparePicture(dc:HDC;rc:TRECT);
+ function LoadBkPicture(CoverFName:PAnsiChar;check:boolean=false;
+ BackFName:PAnsiChar=nil):integer;
+ procedure SaveSettings;
+ procedure LoadSettings;
+ procedure CheckControls;
+ procedure SetAlpha(value:integer);
+ procedure RefreshPicture(cover:PAnsiChar=nil);
+ procedure ClearBitmapData;
+ procedure AdjustFrame;
+ procedure MyDestroy(Sender:PObj);
+
+ procedure AdjustButtons(atop:integer);
+ procedure RefreshButtonIcons;
+ procedure FreeButtonsDesigner;
+ procedure MakeButtonsDesigner;
+ procedure FreeButtons;
+ procedure MakeNewButtonGroup;
+ end;
+
+var
+ FrameCtrl:PWATFrame;
diff --git a/plugins/Watrack/kolframe/i_bitmap.inc b/plugins/Watrack/kolframe/i_bitmap.inc
new file mode 100644
index 0000000000..bee15fdda7
--- /dev/null
+++ b/plugins/Watrack/kolframe/i_bitmap.inc
@@ -0,0 +1,290 @@
+{}
+procedure CalcRect(var src,dst:TRECT;mode:dword);
+var
+ dh, dw:integer;
+begin
+ if (Mode and frbkStretch)=frbkStretch then
+ begin
+ if (Mode and frbkProportional)<>0 then
+ begin
+ if (dst.right*src.bottom)>(src.right*dst.bottom) then
+ begin
+ dh:=dst.bottom;
+ dw:=dh*src.right div src.bottom
+ end
+ else
+ begin
+ dw:=dst.right;
+ dh:=dw*src.bottom div src.right;
+ end;
+ end
+ else
+ begin
+ dw:=dst.right;
+ dh:=dst.bottom;
+ end;
+ end
+ else if (Mode and frbkStretchX)<>0 then
+ begin
+ dw:=dst.right;
+ if (Mode and frbkProportional)<>0 then
+ dh:=dw*src.bottom div src.right
+ else
+ dh:=src.bottom;
+ end
+ else if (Mode and frbkStretchY)<>0 then
+ begin
+ dh:=dst.bottom;
+ if (Mode and frbkProportional)<>0 then
+ dw:=dh*src.right div src.bottom
+ else
+ dw:=src.right;
+ end
+ else
+ begin
+ dw:=src.right;
+ dh:=src.bottom;
+ end;
+
+ if (Mode and frbkBottom)<>0 then
+ begin
+ if dh<=dst.bottom then
+ begin
+ dst.top:=(dst.bottom-dh);
+ end
+ else
+ begin
+ src.top:=(dh-dst.bottom);
+ dh:=dst.bottom;
+ src.bottom:=src.top+dh;
+ end;
+ end;
+
+ if (Mode and frbkRight)<>0 then
+ begin
+ if dw<=dst.right then
+ begin
+ dst.left:=(dst.right-dw);
+ end
+ else
+ begin
+ src.left:=(dw-dst.right);
+ dw:=dst.right;
+ src.right:=src.left+dw;
+ end;
+ end;
+
+ if (Mode and frbkCenterX)<>0 then
+ begin
+ if dw<=dst.right then
+ begin
+ dst.left:=(dst.right-dw) div 2;
+ end
+ else
+ begin
+ src.left:=(dw-dst.right) div 2;
+ dw:=dst.right;
+ src.right:=src.left+dw;
+ end;
+ end;
+
+ if (Mode and frbkCenterY)<>0 then
+ begin
+ if dh<=dst.bottom then
+ begin
+ dst.top:=(dst.bottom-dh) div 2;
+ end
+ else
+ begin
+ src.top:=(dh-dst.bottom) div 2;
+ dh:=dst.bottom;
+ src.bottom:=src.top+dh;
+ end;
+ end;
+ dst.right:=dst.left+dw;
+ dst.bottom:=dst.top+dh;
+end;
+
+function CreateDIB32(dc:HDC;w,h:integer):HBITMAP;
+var
+ pt:pointer;
+ bi:TBITMAPINFO;
+begin
+ FillChar(bi,SizeOf(TBITMAPINFO),0);
+ bi.bmiHeader.biSize :=SizeOf(TBITMAPINFOHEADER);
+ bi.bmiHeader.biWidth :=w;
+ bi.bmiHeader.biHeight :=h;
+ bi.bmiHeader.biPlanes :=1;
+ bi.bmiHeader.biBitCount:=32;
+ result:=CreateDIBSection(dc,bi,DIB_RGB_COLORS,pt,0,0);
+end;
+
+procedure PreMultiplyChanells(hbmp:HBITMAP);
+type
+ tPixel=array [0..3] of Byte;
+var
+ bmp:windows.TBITMAP;
+ flag:bool;
+ pBitmapBits:PByte;
+ Len:dword;
+ bh,bw,y,x,z:integer;
+ pPixel:^tPixel;
+ alpha:dword;
+//f:THANDLE;
+begin
+ GetObject(hbmp,SizeOf(bmp),@bmp);
+ bh:=bmp.bmHeight;
+ bw:=bmp.bmWidth;
+ z:=bw*4;
+ Len:=bh*z;
+
+ mGetMem(pBitmapBits,Len);
+ GetBitmapBits(hbmp,Len,pBitmapBits);
+ flag:=true;
+ for y:=0 to bh-1 do
+ begin
+ pointer(pPixel):=PAnsiChar(pBitmapBits)+z*y;
+
+ for x:=0 to bw-1 do
+ begin
+ if pPixel^[3]<>0 then
+ flag:=false
+ else
+ pPixel^[3]:=255;
+ inc(pByte(pPixel),4);
+ end
+ end;
+
+ if not flag then
+ begin
+ GetBitmapBits(hbmp,Len,pBitmapBits); // alpha not changed
+ for y:=0 to bh-1 do
+ begin
+ pointer(pPixel):=PAnsiChar(pBitmapBits)+z*y;
+
+ for x:=0 to bw-1 do
+ begin
+ alpha:=pPixel^[3];
+ if alpha<255 then
+ begin
+ pPixel^[0]:=dword(pPixel^[0])*alpha div 255;
+ pPixel^[1]:=dword(pPixel^[1])*alpha div 255;
+ pPixel^[2]:=dword(pPixel^[2])*alpha div 255;
+ end;
+ inc(pByte(pPixel),4);
+ end
+ end;
+ end;
+ SetBitmapBits(hbmp,Len,pBitmapBits);
+ mFreeMem(pBitmapBits);
+end;
+
+function FixBitmap(dc:HDC;var hBmp:HBITMAP):HBITMAP;
+var
+ dc24,dc32:HDC;
+ hBitmap32,obmp24,obmp32:HBITMAP;
+ bmpInfo:windows.TBITMAP;
+begin
+ GetObject(hBmp,SizeOf(bmpInfo),@bmpInfo);
+ if bmpInfo.bmBitsPixel<>32 then
+ begin
+ dc32:=CreateCompatibleDC(dc);
+ dc24:=CreateCompatibleDC(dc);
+ hBitmap32:=CreateDIB32(dc,bmpInfo.bmWidth,bmpInfo.bmHeight);
+ obmp24:=SelectObject(dc24,hBmp);
+ obmp32:=SelectObject(dc32,hBitmap32);
+ BitBlt(dc32,0,0,bmpInfo.bmWidth,bmpInfo.bmHeight,dc24,0,0,SRCCOPY);
+ DeleteObject(SelectObject(dc24,obmp24));
+ SelectObject(dc32,obmp32);
+ DeleteDC(dc24);
+ DeleteDC(dc32);
+ hBmp:=hBitmap32;
+ end;
+ PreMultiplyChanells(hBmp);
+ result:=hBmp;
+end;
+
+procedure TWATFrame.PreparePicture(dc:HDC;rc:TRECT);
+var
+ bmpinfo:windows.TBITMAP;
+ src,dst:TRECT;
+ x,y,w,h,dh:integer;
+ br:HBRUSH;
+ hdcbmp:HDC;
+ bf:BLENDFUNCTION;
+ hOld:THANDLE;
+ D:PWATFrameData;
+begin
+ D:=CustomData;
+ D.BkDC:=CreateCompatibleDC(dc);
+
+ FixBitmap(dc,D.BkBitmap);
+
+ DeleteObject(SelectObject(D.BkDC,CreateDIB32(dc,rc.right-rc.left,rc.bottom-rc.top)));
+
+ //fill empty space by BK color
+ br:=CreateSolidBrush(D.BkColor);
+ FillRect(D.BkDC,rc,br);
+ DeleteObject(br);
+
+ CopyRect(dst,rc);
+ hdcbmp:=CreateCompatibleDC(D.BkDC);
+ GetObject(D.BkBitmap,SizeOf(bmpinfo),@bmpinfo);
+ hOld:=SelectObject(hdcbmp,D.BkBitmap);
+
+ SetRect(src,0,0,bmpinfo.bmWidth,bmpinfo.bmHeight);
+
+ if (D.padding.top+D.padding.bottom)<(dst.bottom-dst.top) then
+ dec(dst.bottom,D.padding.top+D.padding.bottom);
+ if (D.padding.left+D.padding.right)<(dst.right-dst.left) then
+ dec(dst.right,D.padding.left+D.padding.right);
+
+ CalcRect(src,dst,D.BkMode); // calculate final picture rect
+
+ w:=1;
+ if (D.BkMode and frbkTileX)<>0 then
+ begin
+ x:=dst.right;
+ while x<rc.right do
+ begin
+ inc(w);
+ inc(x,dst.right);
+ end;
+ end;
+ h:=1;
+ if (D.BkMode and frbkTileY)<>0 then
+ begin
+ y:=dst.bottom;
+ while y<rc.bottom do
+ begin
+ inc(h);
+ inc(y,dst.bottom);
+ end;
+ end;
+
+ bf.BlendOp :=AC_SRC_OVER;
+ bf.BlendFlags :=0;
+ bf.SourceConstantAlpha:=255;
+ bf.AlphaFormat :=1; // AC_SRC_ALPHA introduced in delphi 7
+
+ x:=dst.left+D.padding.left;
+ if x<dst.right then
+ while w>0 do
+ begin
+ dh:=h;
+ y:=dst.top+D.padding.top;
+ if y<dst.bottom then
+ while dh>0 do
+ begin
+ Windows.AlphaBlend(D.BkDC,x,y,dst.right-dst.left,dst.bottom-dst.top,
+ hdcbmp,src.left,src.top,src.right-src.left,src.bottom-src.top,bf);
+ inc(y,dst.bottom);
+ dec(dh);
+ end;
+ inc(x,dst.right);
+ dec(w);
+ end;
+
+ SelectObject(hdcbmp,hOld);
+ DeleteDC(hdcbmp);
+end;
diff --git a/plugins/Watrack/kolframe/kolframe.pas b/plugins/Watrack/kolframe/kolframe.pas
new file mode 100644
index 0000000000..615991d810
--- /dev/null
+++ b/plugins/Watrack/kolframe/kolframe.pas
@@ -0,0 +1,327 @@
+{CList frame}
+unit KOLFrame;
+
+interface
+
+implementation
+
+uses windows,kol,commdlg,messages,common,commctrl, KOLCCtrls,
+ wat_api,wrapper,global,m_api,dbsettings,waticons,mirutils,
+ icobuttons,textblock,kolsizer;
+
+{$R frm.res}
+
+{$include frm_data.inc}
+{$include frm_vars.inc}
+
+procedure MouseDown(DummySelf, Sender:PControl;var Mouse:TMouseEventData);
+var
+ wnd:HWND;
+begin
+ wnd:=GetParent(Sender.GetWindowHandle);
+ SendMessage(wnd,WM_SYSCOMMAND,
+ SC_MOVE or HTCAPTION,MAKELPARAM(Mouse.x,Mouse.y));
+end;
+
+// ---------------- frame functions ----------------
+
+procedure SetFrameTitle(title:pointer;icon:HICON;addflag:integer=FO_UNICODETEXT);
+var
+ D:PWATFrameData;
+begin
+ D:=FrameCtrl.CustomData;
+ CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS,
+ (D.FrameId shl 16)+FO_TBNAME+addflag,dword(title));
+ CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS,(D.FrameId shl 16)+FO_ICON,icon);
+ CallService(MS_CLIST_FRAMES_UPDATEFRAME,D.FrameId,FU_TBREDRAW);
+end;
+
+// -----------------------
+
+function IsFrameMinimized(FrameId:integer):bool;
+begin
+ result:=(CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS,
+ (FrameId shl 16)+FO_FLAGS,0) and F_UNCOLLAPSED)=0;
+end;
+
+function IsFrameFloated(FrameId:integer):bool;
+begin
+ result:=CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS,
+ (FrameId shl 16)+FO_FLOATING,0)>0;
+end;
+
+function IsFrameHidden(FrameId:integer):bool;
+begin
+ result:=(CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS,
+ (FrameId shl 16)+FO_FLAGS,0) and F_VISIBLE)=0;
+end;
+
+procedure HideFrame(FrameId:integer);
+begin
+ if not IsFrameHidden(FrameId) then
+ begin
+ CallService(MS_CLIST_FRAMES_SHFRAME,FrameId,0);
+ HiddenByMe:=true;
+ end;
+end;
+
+function ShowFrame(FrameId:integer):integer;
+begin
+ result:=0;
+ if IsFrameHidden(FrameId) then
+ if HiddenByMe then
+ begin
+ CallService(MS_CLIST_FRAMES_SHFRAME,FrameId,0);
+ HiddenByMe:=false;
+ end
+ else
+ result:=1;
+end;
+
+{$include frm_rc.inc}
+{$include frm_icogroup.inc}
+{$include frm_trackbar.inc}
+{$include frm_text.inc}
+{$include frm_frame.inc}
+{$include frm_designer.inc}
+
+{$include frm_dlg1.inc}
+{$include frm_dlg2.inc}
+
+// ---------------- basic frame functions ----------------
+
+function NewPlStatus(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+const
+ needToChange:boolean=true;
+var
+ bufw:array [0..511] of WideChar;
+// FrameWnd:HWND;
+ Cover:pAnsiChar;
+ D:PWATFrameData;
+begin
+ result:=0;
+// FrameWnd:=FrameCtrl.Form.GetWindowHandle;
+ D:=FrameCtrl.CustomData;
+
+ case wParam of
+ WAT_EVENT_PLAYERSTATUS: begin
+ case Integer(loword(lParam)) of
+ WAT_PLS_NORMAL : exit;
+ WAT_PLS_NOMUSIC : begin
+ if D.HideNoMusic then
+ HideFrame(D.FrameId)
+ else
+ ShowFrame(D.FrameId); // if was hidden with "no player"
+ end;
+ WAT_PLS_NOTFOUND: begin
+ if D.HideNoPlayer then
+ HideFrame(D.FrameId);
+
+ SetFrameTitle(PluginShort,0,0); // frame update code there
+ end;
+ end;
+ FrameCtrl.ResetFrame;
+ end;
+
+ WAT_EVENT_NEWTRACK: begin
+ // cover
+ if D.UseCover then
+ if (pSongInfo(lParam)^.Cover<>nil) and (pSongInfo(lParam)^.Cover^<>#0) then
+ begin
+ GetShortPathNameW(pSongInfo(lParam)^.Cover,bufw,SizeOf(bufw));
+ WideToAnsi(bufw,Cover);
+ FrameCtrl.RefreshPicture(Cover);
+ mFreeMem(Cover);
+ end;
+
+ // trackbar
+ TrackbarSetRange(D.Trackbar,D.UpdInterval,pSongInfo(lParam)^.total);
+
+ if (D.UpdTimer=0) and (D.UpdInterval>0) then
+ D.UpdTimer:=SetTimer(0,0,D.UpdInterval,@FrameTimerProc);
+
+ // text
+ UpdateTextBlock(D,true);
+
+ ShowFrame(D.FrameId);
+ end;
+
+ WAT_EVENT_NEWPLAYER: begin
+ SetFrameTitle(pSongInfo(lParam)^.player,pSongInfo(lParam)^.icon);
+ // new player must call "no music" at least, so we have chance to show frame
+ end;
+
+ WAT_EVENT_PLUGINSTATUS: begin
+ case lParam of
+ dsEnabled: begin
+ ShowFrame(D.FrameId);
+ // plus - start frame and text timers
+ if D.UpdInterval>0 then
+ D.UpdTimer:=SetTimer(0,0,D.UpdInterval,@FrameTimerProc);
+ end;
+
+ dsPermanent: begin
+ HideFrame(D.FrameId);
+
+ // plus - stop frame and text timers
+ if D.UpdTimer<>0 then
+ begin
+ KillTimer(0,D.UpdTimer);
+ D.UpdTimer:=0;
+ end;
+ end;
+ end;
+ end;
+ end;
+end;
+
+function IconChanged(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+begin
+ result:=0;
+ if PWATFrameData(FrameCtrl.CustomData).FrameId<>0 then
+ begin
+ FrameCtrl.RefreshAllFrameIcons;
+ ShowWindow(FrameCtrl.GetWindowHandle,SW_HIDE);
+ ShowWindow(FrameCtrl.GetWindowHandle,SW_SHOW);
+ end;
+end;
+
+//??const opt_FrmHeight :PAnsiChar = 'frame/frmheight';
+
+function CreateFrame(parent:HWND):boolean;
+var
+ CLFrame:TCLISTFrame;
+ rc:TRECT;
+ FrameWnd:HWND;
+begin
+ result:=false;
+ if ServiceExists(MS_CLIST_FRAMES_ADDFRAME)=0 then
+ exit;
+
+ if parent=0 then
+ parent:=CallService(MS_CLUI_GETHWND,0,0);
+
+ FrameWnd:=CreateFrameWindow(parent);
+
+ if FrameWnd<>0 then
+ begin
+ FillChar(CLFrame,SizeOf(CLFrame),0);
+ with CLFrame do
+ begin
+ cbSize :=SizeOf(CLFrame);
+ hWnd :=FrameWnd;
+ hIcon :=0;
+ align :=alTop;
+ GetClientRect(FrameWnd,rc);
+//?? height :=DBReadWord(0,PluginShort,opt_FrmHeight,rc.bottom-rc.top);
+ Flags :=0;//{F_VISIBLE or} F_SHOWTB;
+ name.a :=PluginShort;
+ TBName.a:=PluginShort;
+ end;
+ FrameHeight:=CLFrame.height;
+
+ PWATFrameData(FrameCtrl.CustomData).FrameId:=CallService(MS_CLIST_FRAMES_ADDFRAME,dword(@CLFrame),0);
+ if PWATFrameData(FrameCtrl.CustomData).FrameId>=0 then
+ begin
+ plStatusHook:=HookEvent(ME_WAT_NEWSTATUS,@NewPlStatus);
+ end;
+ end;
+ result:=FrameWnd<>0;
+end;
+
+procedure DestroyFrame;
+var
+ id:Integer;
+begin
+ if (FrameCtrl<>nil) and (PWATFrameData(FrameCtrl.CustomData).FrameId>=0) then
+ begin
+ UnhookEvent(plStatusHook);
+
+ id:=PWATFrameData(FrameCtrl.CustomData).FrameId;
+ FrameCtrl.Free;
+ FrameCtrl:=nil;
+ CallService(MS_CLIST_FRAMES_REMOVEFRAME,Id,0);
+ end;
+end;
+
+const
+ opt_ModStatus:PAnsiChar = 'module/frame';
+
+function GetModStatus:integer;
+begin
+ result:=DBReadByte(0,PluginShort,opt_ModStatus,1);
+end;
+
+procedure SetModStatus(stat:integer);
+begin
+ DBWriteByte(0,PluginShort,opt_modStatus,stat);
+end;
+
+// ---------------- base interface procedures ----------------
+
+function InitProc(aGetStatus:boolean=false):integer;
+begin
+ FrameCtrl:=nil;
+ result:=0;
+ if aGetStatus then
+ begin
+ if GetModStatus=0 then
+ exit;
+ end
+ else
+ SetModStatus(1);
+
+ result:=ord(CreateFrame(0));
+ if result<>0 then
+ sic:=HookEvent(ME_SKIN2_ICONSCHANGED,@IconChanged);
+end;
+
+procedure DeInitProc(aSetDisable:boolean);
+begin
+ if aSetDisable then
+ SetModStatus(0);
+
+ if sic<>0 then UnhookEvent(sic);
+ sic:=0;
+ DestroyFrame;
+end;
+
+function AddOptionsPage(var tmpl:pAnsiChar;var proc:pointer;var name:PAnsiChar):integer;
+const
+ count:integer=2;
+begin
+ if count=0 then
+ count:=2;
+ if count=2 then
+ begin
+ tmpl:='FRAME';
+ proc:=@FrameViewDlg;
+ name:='Frame (main)';
+ end
+ else
+ begin
+ tmpl:='FRAME2';
+ proc:=@FrameTextDlg;
+ name:='Frame (text)';
+ end;
+
+ dec(count);
+ result:=count;
+end;
+
+var
+ Frame:twModule;
+
+procedure Init;
+begin
+ Frame.Next :=ModuleLink;
+ Frame.Init :=@InitProc;
+ Frame.DeInit :=@DeInitProc;
+ Frame.AddOption :=@AddOptionsPage;
+ Frame.ModuleName:='Frame';
+ ModuleLink :=@Frame;
+end;
+
+begin
+ Init;
+end.
diff --git a/plugins/Watrack/lastfm/i_const.inc b/plugins/Watrack/lastfm/i_const.inc
new file mode 100644
index 0000000000..9a0365e506
--- /dev/null
+++ b/plugins/Watrack/lastfm/i_const.inc
@@ -0,0 +1,17 @@
+const
+ IDC_LOGIN = 1025;
+ IDC_PASS = 1026;
+ IDC_TRIES = 1027;
+
+ IDC_INFO_ARTIST = 1030;
+ IDC_INFO_TRACK = 1031;
+ IDC_INFO_ALBUM = 1032;
+ IDC_LANGUAGE = 1033;
+
+ IDC_DATA_PIC = 1040;
+ IDC_DATA_ARTIST = 1041;
+ IDC_DATA_ALBUM = 1042;
+ IDC_DATA_TRACK = 1043;
+ IDC_DATA_TAGS = 1044;
+ IDC_DATA_INFO = 1045;
+ IDC_ALT = 1046;
diff --git a/plugins/Watrack/lastfm/i_last_api.inc b/plugins/Watrack/lastfm/i_last_api.inc
new file mode 100644
index 0000000000..73328366ba
--- /dev/null
+++ b/plugins/Watrack/lastfm/i_last_api.inc
@@ -0,0 +1,599 @@
+{}
+
+const
+ client_id = 'wat';//'wat'; 'tst'
+ client_ver = '1.0';
+ api_key = '51f5d25159da31b0814609c3a12900e2';
+
+const
+ defreq = 'http://post.audioscrobbler.com/?hs=true&p=1.2.1&c=<client-id>&v=<client-ver>&u=<user>&t=<timestamp>&a=<auth>';
+
+function GetMD5Str(digest:TMD5Hash; buf:pAnsiChar):PAnsiChar;
+begin
+ buf[00]:=HexDigitChrLo[digest[00] shr 4]; buf[01]:=HexDigitChrLo[digest[00] and $0F];
+ buf[02]:=HexDigitChrLo[digest[01] shr 4]; buf[03]:=HexDigitChrLo[digest[01] and $0F];
+ buf[04]:=HexDigitChrLo[digest[02] shr 4]; buf[05]:=HexDigitChrLo[digest[02] and $0F];
+ buf[06]:=HexDigitChrLo[digest[03] shr 4]; buf[07]:=HexDigitChrLo[digest[03] and $0F];
+ buf[08]:=HexDigitChrLo[digest[04] shr 4]; buf[09]:=HexDigitChrLo[digest[04] and $0F];
+ buf[10]:=HexDigitChrLo[digest[05] shr 4]; buf[11]:=HexDigitChrLo[digest[05] and $0F];
+ buf[12]:=HexDigitChrLo[digest[06] shr 4]; buf[13]:=HexDigitChrLo[digest[06] and $0F];
+ buf[14]:=HexDigitChrLo[digest[07] shr 4]; buf[15]:=HexDigitChrLo[digest[07] and $0F];
+ buf[16]:=HexDigitChrLo[digest[08] shr 4]; buf[17]:=HexDigitChrLo[digest[08] and $0F];
+ buf[18]:=HexDigitChrLo[digest[09] shr 4]; buf[19]:=HexDigitChrLo[digest[09] and $0F];
+ buf[20]:=HexDigitChrLo[digest[10] shr 4]; buf[21]:=HexDigitChrLo[digest[10] and $0F];
+ buf[22]:=HexDigitChrLo[digest[11] shr 4]; buf[23]:=HexDigitChrLo[digest[11] and $0F];
+ buf[24]:=HexDigitChrLo[digest[12] shr 4]; buf[25]:=HexDigitChrLo[digest[12] and $0F];
+ buf[26]:=HexDigitChrLo[digest[13] shr 4]; buf[27]:=HexDigitChrLo[digest[13] and $0F];
+ buf[28]:=HexDigitChrLo[digest[14] shr 4]; buf[29]:=HexDigitChrLo[digest[14] and $0F];
+ buf[30]:=HexDigitChrLo[digest[15] shr 4]; buf[31]:=HexDigitChrLo[digest[15] and $0F];
+ buf[32]:=#0;
+ result:=@buf;
+end;
+
+function GetMD5(const data;datalen:integer;var digest:TMD5Hash):TMD5Hash;
+begin
+ FillChar(digest,16,0);
+
+ mir_md5_hash(pmir_md5_byte_t(data),datalen,digest);
+
+ result:=digest;
+end;
+
+function HandShake(login, password:PAnsiChar; notify:bool=false):bool;
+var
+ buf:array [0..32] of AnsiChar;
+ digest:TMD5Hash;
+ stat:mir_md5_state_t;
+ timestamp:array [0..31] of AnsiChar;
+ request:array [0..511] of AnsiChar;
+ tmp,res:pAnsiChar;
+begin
+ result:=false;
+ GetMD5Str(GetMD5(password,StrLen(password),digest),buf);
+ mir_md5_init(@stat);
+ mir_md5_append(@stat,@buf,32);
+ IntToStr(timestamp,GetCurrentTime);
+ mir_md5_append(@stat,@timestamp,StrLen(timestamp));
+ mir_md5_finish(@stat,digest);
+ GetMD5Str(digest,buf);
+ StrCopy(request,defreq);
+ StrReplace(request,'<client-id>' ,client_id);
+ StrReplace(request,'<client-ver>',client_ver);
+ StrReplace(request,'<user>' ,login);
+ StrReplace(request,'<timestamp>' ,timestamp);
+ StrReplace(request,'<auth>' ,buf);
+
+ res:=SendRequest(request,REQUEST_GET);
+ if (res<>nil) and (uint_ptr(res)>$0FFF) then
+ begin
+ if StrCmp(CharReplace(res,#10,#0),'OK')=0 then
+ begin
+ result:=true;
+ tmp:=StrEnd(res)+1; StrDup(session_id,tmp);
+ tmp:=StrEnd(tmp)+1; StrDup(np_url ,tmp);
+ tmp:=StrEnd(tmp)+1; StrDup(sub_url ,tmp);
+ end
+ else if notify then
+ begin
+ tmp:=StrCopyE(request,Translate('Last.fm error: '));
+ if StrCmp(res,'BANNED' )=0 then StrCopy(tmp,Translate('Client is banned'))
+ else if StrCmp(res,'BADAUTH' )=0 then StrCopy(tmp,Translate('Bad Auth. Check login and password'))
+ else if StrCmp(res,'BADTIME' )=0 then StrCopy(tmp,Translate('Bad TimeStamp'))
+ else if StrCmp(res,'FAILED',6)=0 then StrCopy(tmp,res+7);
+ CallService(MS_POPUP_SHOWMESSAGEW,wparam(@request),SM_ERROR);
+ end;
+ mFreeMem(res);
+ end;
+end;
+
+function encode(dst,src:pAnsiChar):PAnsiChar;
+begin
+ while src^<>#0 do
+ begin
+ if not (src^ in [' ','%','+','&','?',#128..#255]) then
+ dst^:=src^
+ else
+ begin
+ dst^:='%'; inc(dst);
+ dst^:=HexDigitChr[ord(src^) shr 4]; inc(dst);
+ dst^:=HexDigitChr[ord(src^) and $0F];
+ end;
+ inc(src);
+ inc(dst);
+ end;
+ dst^:=#0;
+ result:=dst;
+end;
+
+function SendNowPlaying:integer;
+var
+ si:pSongInfoA;
+ buf :array [0..31 ] of AnsiChar;
+ args :array [0..1023] of AnsiChar;
+ res,pc:PAnsiChar;
+begin
+ result:=-1;
+ if session_id<>nil then
+ begin
+ si:=pointer(CallService(MS_WAT_RETURNGLOBAL,WAT_INF_UTF8,0));
+
+ pc:=@args;
+ pc:=StrCopyE(pc,'s='); pc:=StrCopyE(pc,session_id); //'?s='
+ pc:=StrCopyE(pc,'&a=');
+ if si^.artist=nil then pc:=StrCopyE(pc,'Unknown')
+ else pc:=encode(pc,si^.artist);
+ pc:=StrCopyE(pc,'&t=');
+ if si^.title =nil then pc:=StrCopyE(pc,'Unknown')
+ else pc:=encode(pc,si^.title);
+ pc:=StrCopyE(pc,'&l='); if si^.total>0 then pc:=StrCopyE(pc,IntToStr(buf,si^.total));
+ pc:=StrCopyE(pc,'&b='); pc:=encode(pc,si^.album);
+ pc:=StrCopyE(pc,'&n=');
+ if si^.track<>0 then
+ {pc:=}StrCopyE(pc,IntToStr(buf,si^.track));
+
+ res:=SendRequest(np_url,REQUEST_POST,args);
+ if (res<>nil) and (uint_ptr(res)>$0FFF) then
+ begin
+ if StrCmp(CharReplace(res,#10,#0),'OK')=0 then
+ result:=1
+ else if StrCmp(res,'BADSESSION')=0 then
+ result:=-1;
+ mFreeMem(res);
+ end;
+ end;
+end;
+
+function Scrobble:integer;
+var
+ si:pSongInfoA;
+ buf,timestamp:array [0..31] of AnsiChar;
+ args :array [0..1023] of AnsiChar;
+ res,pc:PAnsiChar;
+begin
+ result:=-1;
+ if session_id<>nil then
+ begin
+ si:=pointer(CallService(MS_WAT_RETURNGLOBAL,WAT_INF_UTF8,0));
+ IntToStr(timestamp,GetCurrentTime);
+
+ pc:=@args;
+ pc:=StrCopyE(pc,'s=' ); pc:=StrCopyE(pc,session_id);
+ pc:=StrCopyE(pc,'&a[0]=');
+ if si^.artist=nil then pc:=StrCopyE(pc,'Unknown')
+ else pc:=encode(pc,si^.artist);
+ pc:=StrCopyE(pc,'&t[0]=');
+ if si^.title =nil then pc:=StrCopyE(pc,'Unknown')
+ else pc:=encode(pc,si^.title);
+ pc:=StrCopyE(pc,'&i[0]='); pc:=StrCopyE(pc,timestamp);
+ pc:=StrCopyE(pc,'&r[0]=&m[0]=');
+ pc:=StrCopyE(pc,'&l[0]=');
+ if si^.total>0 then
+ begin
+ pc:=StrCopyE(pc,IntToStr(buf,si^.total));
+ pc:=StrCopyE(pc,'&o[0]=P');
+ end
+ else
+ begin
+ pc:=StrCopyE(pc,'&o[0]=R');
+ end;
+ pc:=StrCopyE(pc,'&b[0]='); pc:=encode(pc,si^.album);
+ pc:=StrCopyE(pc,'&n[0]=');
+ if si^.track<>0 then
+ {pc:=}StrCopyE(pc,IntToStr(buf,si^.track));
+
+ res:=SendRequest(sub_url,REQUEST_POST,args);
+ if (res<>nil) and (uint_ptr(res)>$0FFF) then
+ begin
+ if StrCmp(CharReplace(res,#10,#0),'OK')=0 then
+ result:=1
+ else if StrCmp(res,'BADSESSION')=0 then
+ begin
+ result:=-1;
+ end
+ else if StrCmp(res,'FAILED',6)=0 then
+ begin
+ StrCopy(StrCopyE(args,Translate('Last.fm error: ')),res+7);
+ CallService(MS_POPUP_SHOWMESSAGE,wparam(@args),SM_NOTIFY);
+ result:=0;
+ end;
+ mFreeMem(res);
+ end;
+ end;
+end;
+
+//----- Get Info service functions -----
+
+function FullEncode(dst,src:pAnsiChar):PAnsiChar;
+begin
+ while src^<>#0 do
+ begin
+ if src^ in ['A'..'Z','a'..'z','0'..'9'] then
+ dst^:=src^
+ else
+ begin
+ dst^:='%'; inc(dst);
+ dst^:=HexDigitChr[ord(src^) shr 4]; inc(dst);
+ dst^:=HexDigitChr[ord(src^) and $0F];
+ end;
+ inc(src);
+ inc(dst);
+ end;
+ dst^:=#0;
+ result:=dst;
+end;
+
+var
+ xmlparser:XML_API_W;
+
+function FixInfo(info:pWideChar):pWideChar;
+var
+ pc,ppc:pWideChar;
+ cnt:cardinal;
+ need:boolean;
+begin
+ pc:=info;
+ cnt:=0;
+ need:=false;
+ while pc^<>#0 do
+ begin
+ if pc^=#$0D then
+ begin
+ inc(cnt);
+ inc(pc);
+ if pc^<>#$0A then
+ need:=true;
+ end
+ else
+ inc(pc);
+ end;
+ if need then
+ begin
+ mGetMem(result,(StrLenW(info)+1+cnt)*SizeOf(WideChar));
+ pc:=info;
+ ppc:=result;
+ while pc^<>#0 do
+ begin
+ ppc^:=pc^;
+ if pc^=#$0D then
+ begin
+ inc(ppc);
+ ppc^:=#$0A;
+ end;
+ inc(pc);
+ inc(ppc);
+ end;
+ ppc^:=#0;
+ end
+ else
+ StrDupW(result,info);
+end;
+
+function GetArtistInfo(var data:tLastFMInfo;lang:integer):int;
+var
+ si:pSongInfo;
+ res,pc:pAnsiChar;
+ request:array [0..1023] of AnsiChar;
+ root,actnode,node,nnode:HXML;
+ i:integer;
+ pcw,p,pp:PWideChar;
+ artist:pAnsiChar;
+begin
+ result:=0;
+ if data.artist=nil then
+ begin
+ si:=pointer(CallService(MS_WAT_RETURNGLOBAL,WAT_INF_UNICODE,0));
+ pWideChar(artist):=si^.artist;
+ end
+ else
+ pWideChar(artist):=data.artist;
+ if artist=nil then
+ exit;
+ WideToUTF8(pWideChar(artist),artist);
+ pc:=FullEncode(StrCopyE(request,
+ 'http://ws.audioscrobbler.com/2.0/?method=artist.getinfo&api_key='+api_key+'&artist='),
+ artist);
+ mFreeMem(artist);
+ if lang<>0 then
+ StrCopyE(StrCopyE(pc,'&lang='),pAnsiChar(@lang));
+ res:=SendRequest(request,REQUEST_GET);
+ if (res<>nil) and (uint_ptr(res)>$0FFF) then
+ begin
+ UTF8ToWide(res,pcw);
+ mFreeMem(res);
+ xmlparser.cbSize:={XML_API_SIZEOF_V1;//}SizeOf(XML_API_W);
+ CallService(MS_SYSTEM_GET_XI,0,lparam(@xmlparser));
+ with xmlparser do
+ begin
+ i:=StrLenW(pcw)*SizeOf(WideChar);
+ root:=parseString(pcw,@i,nil);
+
+ actnode:=getChild(getChild(root,0),0); // "artist"
+
+ if data.artist=nil then
+ StrDupW(data.artist,getText(GetNthChild(actnode,'name',0)));
+
+ i:=0;
+ repeat
+ node:=GetNthChild(actnode,'image',i);
+ if node=0 then break;
+ if StrCmpW(GetAttrValue(node,'size'),'medium')=0 then
+ begin
+ WideToUTF8(GetText(node),data.image);
+ break;
+ end;
+ inc(i);
+ until false;
+
+ // bio
+ p:=StrPosW(pcw,'<content><![CDATA[');
+ if p<>nil then
+ begin
+ inc(p,18);
+ pp:=StrPosW(p,']]');
+ if pp<> nil then pp^:=#0;
+ data.info:=FixInfo(p);
+ end;
+
+ // similar
+ i:=0;
+ pcw:=pWideChar(@request); pcw^:=#0;
+ node:=GetNthChild(actnode,'similar',0);
+ repeat
+ nnode:=GetNthChild(GetNthChild(node,'artist',i),'name',0);
+ if nnode=0 then break;
+ if pcw<>@request then
+ begin
+ pcw^:=','; inc(pcw);
+ pcw^:=' '; inc(pcw);
+ end;
+ pcw:=StrCopyEW(pcw,GetText(nnode));
+ inc(i);
+ until false;
+ pcw:=#0;
+ StrDupW(data.similar,pWideChar(@request));
+
+ // tags
+ i:=0;
+ pcw:=pWideChar(@request); pcw^:=#0;
+ node:=GetNthChild(actnode,'tags',0);
+ repeat
+ nnode:=GetNthChild(GetNthChild(node,'tag',i),'name',0);
+ if nnode=0 then break;
+ if pcw<>@request then
+ begin
+ pcw^:=','; inc(pcw);
+ pcw^:=' '; inc(pcw);
+ end;
+ pcw:=StrCopyEW(pcw,GetText(nnode));
+ inc(i);
+ until false;
+ pcw:=#0;
+ StrDupW(data.tags,pWideChar(@request));
+ DestroyNode(root);
+ mFreeMem(pcw);
+ end;
+ end;
+end;
+
+function GetAlbumInfo(var data:tLastFMInfo;lang:integer):int;
+var
+ si:pSongInfo;
+ res,pc:pAnsiChar;
+ request:array [0..1023] of AnsiChar;
+ root,actnode,node,nnode:HXML;
+ i:integer;
+ p,pp,pcw:PWideChar;
+ album,artist:pAnsiChar;
+begin
+ result:=0;
+ si:=nil;
+ if data.album=nil then
+ begin
+ si:=pointer(CallService(MS_WAT_RETURNGLOBAL,WAT_INF_UNICODE,0));
+ pWideChar(album):=si^.album;
+ end
+ else
+ pWideChar(album):=data.album;
+ if album=nil then
+ exit;
+ WideToUTF8(pWideChar(album),album);
+ pc:=FullEncode(StrCopyE(request,
+ 'http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key='+api_key+'&album='),
+ album);
+ mFreeMem(album);
+ if data.artist=nil then
+ begin
+ if si=nil then
+ si:=pointer(CallService(MS_WAT_RETURNGLOBAL,WAT_INF_UNICODE,0));
+ pWideChar(artist):=si^.artist;
+ end
+ else
+ pWideChar(artist):=data.artist;
+ if artist<>nil then
+ begin
+ WideToUTF8(pWideChar(artist),artist);
+ pc:=FullEncode(StrCopyE(pc,'&artist='),artist);
+ mFreeMem(artist);
+ end;
+
+ if lang<>0 then
+ StrCopyE(StrCopyE(pc,'&lang='),pAnsiChar(@lang));
+
+ res:=SendRequest(request,REQUEST_GET);
+ if res<>nil then
+ begin
+ UTF8ToWide(res,pcw);
+ mFreeMem(res);
+ xmlparser.cbSize:={XML_API_SIZEOF_V1;//}SizeOf(XML_API_W);
+ CallService(MS_SYSTEM_GET_XI,0,lparam(@xmlparser));
+ with xmlparser do
+ begin
+ i:=StrLenW(pcw)*SizeOf(WideChar);
+ root:=parseString(pcw,@i,nil);
+
+ actnode:=getChild(getChild(root,0),0); // "album"
+
+ if data.album=nil then
+ StrDupW(data.album,getText(GetNthChild(actnode,'name',0)));
+ StrDupW(data.release,getText(GetNthChild(actnode,'releasedate',0)));
+ if data.artist=nil then
+ StrDupW(data.artist,getText(GetNthChild(actnode,'artist',0)));
+
+ i:=0;
+ repeat
+ node:=GetNthChild(actnode,'image',i);
+ if node=0 then break;
+ if StrCmpW(GetAttrValue(node,'size'),'medium')=0 then
+ begin
+ WideToUTF8(GetText(node),data.image);
+ break;
+ end;
+ inc(i);
+ until false;
+
+ p:=StrPosW(pcw,'<content><![CDATA[');
+ if p<>nil then
+ begin
+ inc(p,18);
+ pp:=StrPosW(p,']]');
+ if pp<> nil then pp^:=#0;
+ data.info:=FixInfo(p);
+ end;
+
+ // tags
+ i:=0;
+ pcw:=pWideChar(@request); pcw^:=#0;
+ node:=GetNthChild(actnode,'toptags',0);
+ repeat
+ nnode:=GetNthChild(GetNthChild(node,'tag',i),'name',0);
+ if nnode=0 then break;
+ if pcw<>@request then
+ begin
+ pcw^:=','; inc(pcw);
+ pcw^:=' '; inc(pcw);
+ end;
+ pcw:=StrCopyEW(pcw,GetText(nnode));
+ inc(i);
+ until false;
+ pcw:=#0;
+ StrDupW(data.tags,pWideChar(@request));
+
+ DestroyNode(root);
+ mFreeMem(pcw);
+ end;
+ end;
+end;
+
+function GetTrackInfo(var data:tLastFMInfo;lang:integer):int;
+var
+ si:pSongInfo;
+ res,pc:pAnsiChar;
+ request:array [0..1023] of AnsiChar;
+ root,actnode,node,anode:HXML;
+ i:integer;
+ p,pp,pcw:PWideChar;
+ title,artist:pAnsiChar;
+begin
+ result:=0;
+ si:=nil;
+ if data.album=nil then
+ begin
+ si:=pointer(CallService(MS_WAT_RETURNGLOBAL,WAT_INF_UNICODE,0));
+ pWideChar(title):=si^.title;
+ end
+ else
+ pWideChar(title):=data.title;
+ if title=nil then
+ exit;
+ WideToUTF8(pWideChar(title),title);
+ pc:=FullEncode(StrCopyE(request,
+ 'http://ws.audioscrobbler.com/2.0/?method=track.getinfo&api_key='+api_key+'&track='),
+ title);
+ mFreeMem(title);
+ if data.artist=nil then
+ begin
+ if si=nil then
+ si:=pointer(CallService(MS_WAT_RETURNGLOBAL,WAT_INF_UNICODE,0));
+ pWideChar(artist):=si^.artist;
+ end
+ else
+ pWideChar(artist):=data.artist;
+ if artist<>nil then
+ begin
+ WideToUTF8(pWideChar(artist),artist);
+ pc:=FullEncode(StrCopyE(pc,'&artist='),artist);
+ mFreeMem(artist);
+ end;
+
+ if lang<>0 then
+ StrCopyE(StrCopyE(pc,'&lang='),pAnsiChar(@lang));
+
+ res:=SendRequest(request,REQUEST_GET);
+ if res<>nil then
+ begin
+ UTF8ToWide(res,pcw);
+ mFreeMem(res);
+ xmlparser.cbSize:={XML_API_SIZEOF_V1;//}SizeOf(XML_API_W);
+ CallService(MS_SYSTEM_GET_XI,0,lparam(@xmlparser));
+ with xmlparser do
+ begin
+ i:=StrLenW(pcw)*SizeOf(WideChar);
+ root:=parseString(pcw,@i,nil);
+
+ actnode:=getChild(getChild(root,0),0); // "track"
+ if data.artist=nil then
+ StrDupW(data.artist,getText(GetNthChild(GetNthChild(actnode,'artist',0),'name',0)));
+
+ anode:=GetNthChild(actnode,'album',i);
+
+ if data.album=nil then
+ StrDupW(data.album,getText(GetNthChild(anode,'title',0)));
+
+ data.trknum:=StrToInt(getAttrValue(anode,'position'));
+ if data.title=nil then
+ StrDupW(data.title,getText(GetNthChild(actnode,'name',0)));
+
+ i:=0;
+ repeat
+ node:=GetNthChild(anode,'image',i);
+ if node=0 then break;
+ if StrCmpW(GetAttrValue(node,'size'),'medium')=0 then
+ begin
+ WideToUTF8(GetText(node),data.image);
+ break;
+ end;
+ inc(i);
+ until false;
+
+ p:=StrPosW(pcw,'<content><![CDATA[');
+ if p<>nil then
+ begin
+ inc(p,18);
+ pp:=StrPosW(p,']]');
+ if pp<> nil then pp^:=#0;
+ data.info:=FixInfo(p);
+ end;
+
+ // tags
+ i:=0;
+ pcw:=pWideChar(@request); pcw^:=#0;
+ node:=GetNthChild(actnode,'toptags',0);
+ repeat
+ anode:=GetNthChild(GetNthChild(node,'tag',i),'name',0);
+ if anode=0 then break;
+ if pcw<>@request then
+ begin
+ pcw^:=','; inc(pcw);
+ pcw^:=' '; inc(pcw);
+ end;
+ pcw:=StrCopyEW(pcw,GetText(anode));
+ inc(i);
+ until false;
+ pcw:=#0;
+ StrDupW(data.tags,pWideChar(@request));
+
+ DestroyNode(root);
+ mFreeMem(pcw);
+ end;
+ end;
+end;
diff --git a/plugins/Watrack/lastfm/i_last_dlg.inc b/plugins/Watrack/lastfm/i_last_dlg.inc
new file mode 100644
index 0000000000..b72545843e
--- /dev/null
+++ b/plugins/Watrack/lastfm/i_last_dlg.inc
@@ -0,0 +1,120 @@
+{}
+const
+ MaxLangs = 11;
+ LangArray:array [0..MaxLangs-1] of record
+ code:array [0..1] of AnsiChar;
+ name:pWideChar;
+ end= (
+ (code:#0#0 ; name: 'no language';),
+ (code:'zh' ; name: 'Chinese' ;),
+ (code:'en' ; name: 'English' ;),
+ (code:'fr' ; name: 'French' ;),
+ (code:'de' ; name: 'German' ;),
+ (code:'hi' ; name: 'Hindi' ;),
+ (code:'it' ; name: 'Italian' ;),
+ (code:'ja' ; name: 'Japanese' ;),
+ (code:'pt' ; name: 'Portuguese' ;),
+ (code:'ru' ; name: 'Russian' ;),
+ (code:'es' ; name: 'Spanish' ;)
+ );
+
+procedure ClearInfo(dlg:HWND);
+begin
+ SetDlgItemTextW(dlg,IDC_DATA_ARTIST,'');
+ SetDlgItemTextW(dlg,IDC_DATA_ALBUM ,'');
+ SetDlgItemTextW(dlg,IDC_DATA_TRACK ,'');
+ SetDlgItemTextW(dlg,IDC_DATA_TAGS ,'');
+ SetDlgItemTextW(dlg,IDC_DATA_INFO ,'');
+end;
+
+procedure ClearData(var data:tLastFMInfo);
+begin
+ mFreeMem(data.artist);
+ mFreeMem(data.album);
+ mFreeMem(data.title);
+ mFreeMem(data.tags);
+ mFreeMem(data.info);
+ mFreeMem(data.image);
+end;
+
+function DlgProcOptions(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+var
+ tmp:longbool;
+ bmp,wnd:HWND;
+ lang:integer;
+ data:tLastFMInfo;
+begin
+ result:=0;
+ case hMessage of
+ WM_INITDIALOG: begin
+ TranslateDialogDefault(Dialog);
+ SetDlgItemTextA(Dialog,IDC_LOGIN,lfm_login);
+ SetDlgItemTextA(Dialog,IDC_PASS ,lfm_password);
+ SetDlgItemInt (Dialog,IDC_TRIES,lfm_tries,false);
+ wnd:=GetDlgItem(Dialog,IDC_LANGUAGE);
+ for lang:=0 to MaxLangs-1 do
+ with LangArray[lang] do
+ CB_AddStrDataW(wnd,TranslateW(name),
+ ord(code[0])+(ord(code[1]) shl 8),lang);
+ CB_SelectData(wnd,lfm_lang);
+ end;
+
+ WM_COMMAND: begin
+ case wParam shr 16 of
+ BN_CLICKED: begin
+ ClearInfo(Dialog);
+ FillChar(data,SizeOf(data),0);
+ lfm_lang:=CB_GetData(GetDlgItem(Dialog,IDC_LANGUAGE));
+
+ if loword(wParam)=IDC_INFO_ARTIST then
+ begin
+ SetDlgItemTextW(Dialog,IDC_ALT,TranslateW('Similar artists'));
+ GetArtistInfo(data,lfm_lang);
+ SetDlgItemTextW(Dialog,IDC_DATA_ALBUM,data.similar);
+ end
+ else
+ begin
+ SetDlgItemTextW(Dialog,IDC_ALT,TranslateW('Album'));
+ if loword(wParam)=IDC_INFO_TRACK then
+ GetTrackInfo (data,lfm_lang)
+ else
+ GetAlbumInfo (data,lfm_lang);
+ SetDlgItemTextW(Dialog,IDC_DATA_ALBUM,data.album);
+ end;
+
+ SetDlgItemTextW(Dialog,IDC_DATA_ARTIST,data.artist);
+ SetDlgItemTextW(Dialog,IDC_DATA_TRACK ,data.title);
+ SetDlgItemTextW(Dialog,IDC_DATA_TAGS ,data.tags);
+ SetDlgItemTextW(Dialog,IDC_DATA_INFO ,data.info);
+ bmp:=LoadImageURL(data.image,64);
+ if bmp<>0 then
+ DeleteObject(SendDlgItemMessage(Dialog,IDC_DATA_PIC,STM_SETIMAGE,IMAGE_BITMAP,bmp));
+
+ ClearData(data);
+ end;
+ EN_CHANGE:
+ case loword(wParam) of
+ IDC_LOGIN,IDC_PASS,IDC_TRIES:
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+ end;
+ end;
+
+ WM_NOTIFY: begin
+ if integer(PNMHdr(lParam)^.code)=PSN_APPLY then
+ begin
+ mFreeMem(lfm_login ); lfm_login :=GetDlgText(Dialog,IDC_LOGIN,true);
+ mFreeMem(lfm_password); lfm_password:=GetDlgText(Dialog,IDC_PASS ,true);
+ mFreeMem(session_id);
+ mFreeMem(np_url);
+ mFreeMem(sub_url);
+ lfm_tries:=GetDlgItemInt(Dialog,IDC_TRIES,tmp,false);
+ lfm_lang:=CB_GetData(GetDlgItem(Dialog,IDC_LANGUAGE));
+
+ SaveOpt;
+ end;
+ end;
+ else
+ {result:=}DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+end;
diff --git a/plugins/Watrack/lastfm/i_last_opt.inc b/plugins/Watrack/lastfm/i_last_opt.inc
new file mode 100644
index 0000000000..f18b590a87
--- /dev/null
+++ b/plugins/Watrack/lastfm/i_last_opt.inc
@@ -0,0 +1,44 @@
+{}
+const
+ optLogin :pAnsiChar='lfm/login';
+ optPassword:pAnsiChar='lfm/password';
+ optTries :pAnsiChar='lfm/tries';
+ optScrobble:pAnsiChar='lfm/scrobble';
+ optLanguage:pAnsiChar='lfm/language';
+
+procedure SaveOpt;
+var
+ tmppass:array [0..255] of AnsiChar;
+begin
+ if lfm_password<>nil then
+ begin
+ StrCopy(tmppass,lfm_password);
+ CallService(MS_DB_CRYPT_ENCODESTRING,StrLen(tmppass)+1,lparam(@tmppass));
+ end;
+ DBWriteString(0,PluginShort,optPassword,tmppass);
+ DBWriteString(0,PluginShort,optLogin ,lfm_login);
+ DBWriteByte (0,PluginShort,optTries ,lfm_tries);
+ DBWriteByte (0,PluginShort,optScrobble,lfm_on and 1);
+ DBWriteWord (0,PluginShort,optLanguage,lfm_lang);
+end;
+
+procedure LoadOpt;
+begin
+ lfm_lang :=DBReadWord(0,PluginShort,optLanguage,0);
+ lfm_tries:=DBReadByte(0,PluginShort,optTries ,3);
+ lfm_on :=DBReadByte(0,PluginShort,optScrobble,0);
+ mFreeMem(lfm_login ); lfm_login :=DBReadString(0,PluginShort,optLogin);
+ mFreeMem(lfm_password); lfm_password:=DBReadString(0,PluginShort,optPassword);
+ if lfm_password<>nil then
+ CallService(MS_DB_CRYPT_DECODESTRING,StrLen(lfm_password)+1,lparam(lfm_password));
+ if (lfm_login=nil) or (lfm_password=nil) then
+ CallService(MS_POPUP_SHOWMESSAGEW,
+ wparam(TranslateW('Don''t forget to enter Login and Password to use Last.fm service')),
+ SM_WARNING);
+end;
+
+procedure FreeOpt;
+begin
+ mFreeMem(lfm_login);
+ mFreeMem(lfm_password);
+end;
diff --git a/plugins/Watrack/lastfm/lastfm.ico b/plugins/Watrack/lastfm/lastfm.ico
new file mode 100644
index 0000000000..6ed701e2a5
--- /dev/null
+++ b/plugins/Watrack/lastfm/lastfm.ico
Binary files differ
diff --git a/plugins/Watrack/lastfm/lastfm.pas b/plugins/Watrack/lastfm/lastfm.pas
new file mode 100644
index 0000000000..7f2d90b0dd
--- /dev/null
+++ b/plugins/Watrack/lastfm/lastfm.pas
@@ -0,0 +1,300 @@
+unit lastfm;
+{$include compilers.inc}
+interface
+{$Resource lastfm.res}
+implementation
+
+uses windows, messages, commctrl,
+ common,
+ m_api,dbsettings,wrapper,mirutils,
+ wat_api,global;
+
+const
+ opt_ModStatus:PAnsiChar = 'module/lastfm';
+const
+ IcoLastFM:pAnsiChar = 'WATrack_lasfm';
+var
+ lfm_tries:integer;
+ sic:THANDLE;
+ slastinf:THANDLE;
+ slast:THANDLE;
+const
+ lfm_lang :integer=0;
+ lfm_on :integer=0;
+ hMenuLast :HMENU = 0;
+ lfm_login :pAnsiChar=nil;
+ lfm_password:pAnsiChar=nil;
+ session_id :pAnsiChar=nil;
+ np_url :pAnsiChar=nil;
+ sub_url :pAnsiChar=nil;
+
+function GetModStatus:integer;
+begin
+ result:=DBReadByte(0,PluginShort,opt_ModStatus,1);
+end;
+
+procedure SetModStatus(stat:integer);
+begin
+ DBWriteByte(0,PluginShort,opt_ModStatus,stat);
+end;
+
+{$i i_const.inc}
+{$i i_last_opt.inc}
+{$i i_last_api.inc}
+
+procedure ThScrobble(param:LPARAM); cdecl;
+var
+ count:integer;
+ npisok:bool;
+begin
+ count:=lfm_tries;
+ npisok:=false;
+ while count>0 do
+ begin
+ if not npisok then
+ npisok:=SendNowPlaying>=0;
+ if Scrobble>=0 then break;
+ HandShake(lfm_login,lfm_password, count=1); // just last time
+ dec(count);
+ end;
+ if count=0 then ;
+end;
+
+const
+ hTimer:THANDLE=0;
+
+procedure TimerProc(wnd:HWND;uMsg:uint;idEvent:uint_ptr;dwTime:dword); stdcall;
+begin
+ if hTimer<>0 then
+ begin
+ KillTimer(0,hTimer);
+ hTimer:=0;
+ end;
+
+ if (lfm_login <>nil) and (lfm_login^ <>#0) and
+ (lfm_password<>nil) and (lfm_password^<>#0) then
+ CloseHandle(mir_forkthread(@ThScrobble,nil));
+end;
+
+function NewPlStatus(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ flag:integer;
+ mi:TCListMenuItem;
+begin
+ result:=0;
+ case wParam of
+ WAT_EVENT_NEWTRACK: begin
+ if hTimer<>0 then
+ begin
+ KillTimer(0,hTimer);
+ hTimer:=0;
+ end;
+ if lfm_on=0 then
+ hTimer:=SetTimer(0,0,30000,@TimerProc)
+ end;
+
+ WAT_EVENT_PLUGINSTATUS: begin
+ case lParam of
+ dsEnabled: begin
+ lfm_on:=lfm_on and not 2;
+ flag:=0;
+ end;
+ dsPermanent: begin
+ lfm_on:=lfm_on or 2;
+ if hTimer<>0 then
+ begin
+ KillTimer(0,hTimer);
+ hTimer:=0;
+ end;
+ flag:=CMIF_GRAYED;
+ end;
+ else // like 1
+ exit
+ end;
+ FillChar(mi,sizeof(mi),0);
+ mi.cbSize:=sizeof(mi);
+ mi.flags :=CMIM_FLAGS+flag;
+ CallService(MS_CLIST_MODIFYMENUITEM,hMenuLast,tlparam(@mi));
+ end;
+
+ WAT_EVENT_PLAYERSTATUS: begin
+ case Integer(loword(lParam)) of
+ WAT_PLS_NOMUSIC,WAT_PLS_NOTFOUND: begin
+ if hTimer<>0 then
+ begin
+ KillTimer(0,hTimer);
+ hTimer:=0;
+ end;
+ end;
+ end;
+ end;
+ end;
+end;
+
+{$i i_last_dlg.inc}
+
+function IconChanged(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ mi:TCListMenuItem;
+begin
+ result:=0;
+ FillChar(mi,SizeOf(mi),0);
+ mi.cbSize:=sizeof(mi);
+ mi.flags :=CMIM_ICON;
+ mi.hIcon :=CallService(MS_SKIN2_GETICON,0,tlparam(IcoLastFM));
+ CallService(MS_CLIST_MODIFYMENUITEM,hMenuLast,tlparam(@mi));
+end;
+
+function SrvLastFMInfo(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ data:tLastFMInfo;
+begin
+ case wParam of
+ 0: result:=GetArtistInfo(data,lParam);
+ 1: result:=GetAlbumInfo (data,lParam);
+ 2: result:=GetTrackInfo (data,lParam);
+ else
+ result:=0;
+ end;
+end;
+
+function SrvLastFM(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ mi:TCListMenuItem;
+begin
+ FillChar(mi,sizeof(mi),0);
+ mi.cbSize:=sizeof(mi);
+ mi.flags :=CMIM_NAME;
+ if odd(lfm_on) then
+ begin
+ mi.szName.a:='Disable scrobbling';
+ lfm_on:=lfm_on and not 1;
+ end
+ else
+ begin
+ mi.szName.a:='Enable scrobbling';
+ lfm_on:=lfm_on or 1;
+ if hTimer<>0 then
+ begin
+ KillTimer(0,hTimer);
+ hTimer:=0;
+ end;
+ end;
+ CallService(MS_CLIST_MODIFYMENUITEM,hMenuLast,tlparam(@mi));
+ result:=ord(not odd(lfm_on));
+end;
+
+procedure CreateMenus;
+var
+ mi:TCListMenuItem;
+ sid:TSKINICONDESC;
+begin
+ FillChar(sid,SizeOf(TSKINICONDESC),0);
+ sid.cbSize:=SizeOf(TSKINICONDESC);
+ sid.cx:=16;
+ sid.cy:=16;
+ sid.szSection.a:='WATrack';
+
+ sid.hDefaultIcon :=LoadImage(hInstance,'IDI_LAST',IMAGE_ICON,16,16,0);
+ sid.pszName :=IcoLastFM;
+ sid.szDescription.a:='LastFM';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+
+ FillChar(mi, sizeof(mi), 0);
+ mi.cbSize :=sizeof(mi);
+ mi.szPopupName.a:=PluginShort;
+
+ mi.hIcon :=CallService(MS_SKIN2_GETICON,0,lparam(IcoLastFM));
+ mi.szName.a :='Disable scrobbling';
+ mi.pszService :=MS_WAT_LASTFM;
+ mi.popupPosition:=500050000;
+ hMenuLast:=Menu_AddMainMenuItem(@mi);
+end;
+
+// ------------ base interface functions -------------
+
+function AddOptionsPage(var tmpl:pAnsiChar;var proc:pointer;var name:PAnsiChar):integer;
+begin
+ tmpl:='LASTFM';
+ proc:=@DlgProcOptions;
+ name:='LastFM';
+ result:=0;
+end;
+
+var
+ plStatusHook:THANDLE;
+
+function InitProc(aGetStatus:boolean=false):integer;
+begin
+ slastinf:=CreateServiceFunction(MS_WAT_LASTFMINFO,@SrvLastFMInfo);
+ if aGetStatus then
+ begin
+ if GetModStatus=0 then
+ begin
+ result:=0;
+ exit;
+ end;
+ end
+ else
+ begin
+ SetModStatus(1);
+ lfm_on:=lfm_on and not 4;
+ end;
+ result:=1;
+
+ LoadOpt;
+
+ slast:=CreateServiceFunction(MS_WAT_LASTFM,@SrvLastFM);
+ if hMenuLast=0 then
+ CreateMenus;
+ sic:=HookEvent(ME_SKIN2_ICONSCHANGED,@IconChanged);
+ if (lfm_on and 4)=0 then
+ plStatusHook:=HookEvent(ME_WAT_NEWSTATUS,@NewPlStatus);
+end;
+
+procedure DeInitProc(aSetDisable:boolean);
+begin
+ if aSetDisable then
+ SetModStatus(0)
+ else
+ DestroyServiceFunction(slastinf);
+
+ CallService(MS_CLIST_REMOVEMAINMENUITEM,hMenuLast,0);
+ hMenuLast:=0;
+ DestroyServiceFunction(slast);
+ UnhookEvent(plStatusHook);
+ UnhookEvent(sic);
+
+ if hTimer<>0 then
+ begin
+ KillTimer(0,hTimer);
+ hTimer:=0;
+ end;
+
+ FreeOpt;
+
+ mFreeMem(session_id);
+ mFreeMem(np_url);
+ mFreeMem(sub_url);
+
+ lfm_on:=lfm_on or 4;
+end;
+
+var
+ last:twModule;
+
+procedure Init;
+begin
+ last.Next :=ModuleLink;
+ last.Init :=@InitProc;
+ last.DeInit :=@DeInitProc;
+ last.AddOption:=@AddOptionsPage;
+ last.ModuleName:='Last.FM';
+ ModuleLink :=@last;
+
+end;
+
+begin
+ Init;
+end.
diff --git a/plugins/Watrack/lastfm/lastfm.rc b/plugins/Watrack/lastfm/lastfm.rc
new file mode 100644
index 0000000000..320eebe8cc
--- /dev/null
+++ b/plugins/Watrack/lastfm/lastfm.rc
@@ -0,0 +1,38 @@
+#include "i_const.inc"
+
+LANGUAGE 0,0
+
+LASTFM DIALOGEX 0, 0, 304, 226, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ LTEXT "Login" , -1, 108, 2, 70, 16, SS_CENTERIMAGE
+ EDITTEXT IDC_LOGIN , 4, 4, 100, 12,
+ LTEXT "Password", -1, 108, 18, 70, 16, SS_CENTERIMAGE
+ EDITTEXT IDC_PASS , 4, 20, 100, 12, ES_PASSWORD
+ LTEXT "Attempts", -1, 40, 34, 64, 16, SS_CENTERIMAGE
+ EDITTEXT IDC_TRIES , 4, 36, 32, 12, ES_RIGHT | ES_NUMBER
+
+ LTEXT "Content language", -1, 80, 52, 76, 16, SS_CENTERIMAGE
+ COMBOBOX IDC_LANGUAGE, 160, 53, 74, 96, CBS_DROPDOWNLIST | WS_VSCROLL
+
+ PUSHBUTTON "Get Artist Info", IDC_INFO_ARTIST, 4, 166, 72, 16
+ PUSHBUTTON "Get Track Info" , IDC_INFO_TRACK , 4, 186, 72, 16
+ PUSHBUTTON "Get Album Info" , IDC_INFO_ALBUM , 4, 206, 72, 16
+
+ CONTROL "", IDC_DATA_PIC, "STATIC", SS_BITMAP | WS_BORDER, 236, 2, 64, 64
+ RTEXT "Artist", -1 , 0, 70, 76, 16, SS_CENTERIMAGE
+ EDITTEXT IDC_DATA_ARTIST, 80, 71, 220, 14, ES_READONLY | ES_AUTOHSCROLL
+ RTEXT "Album",IDC_ALT, 0, 86, 76, 16, SS_CENTERIMAGE
+ EDITTEXT IDC_DATA_ALBUM , 80, 87, 220, 14, ES_READONLY | ES_AUTOHSCROLL
+ RTEXT "Track" , -1 , 0, 102, 76, 16, SS_CENTERIMAGE
+ EDITTEXT IDC_DATA_TRACK , 80, 103, 220, 14, ES_READONLY | ES_AUTOHSCROLL
+ RTEXT "Tags" , -1 , 0, 118, 76, 16, SS_CENTERIMAGE
+ EDITTEXT IDC_DATA_TAGS , 80, 119, 220, 14, ES_READONLY | ES_AUTOHSCROLL
+ RTEXT "Info" , -1 , 0, 134, 76, 16, SS_CENTERIMAGE
+ EDITTEXT IDC_DATA_INFO , 80, 135, 220, 88,
+ ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL | WS_VSCROLL
+}
+
+IDI_LAST ICON "lastfm.ico"
diff --git a/plugins/Watrack/lastfm/lastfm.res b/plugins/Watrack/lastfm/lastfm.res
new file mode 100644
index 0000000000..0ed756fb33
--- /dev/null
+++ b/plugins/Watrack/lastfm/lastfm.res
Binary files differ
diff --git a/plugins/Watrack/lst_formats.inc b/plugins/Watrack/lst_formats.inc
new file mode 100644
index 0000000000..19db34ee40
--- /dev/null
+++ b/plugins/Watrack/lst_formats.inc
@@ -0,0 +1,16 @@
+,fmt_dummy in 'formats\fmt_dummy.pas'
+,fmt_mpc in 'formats\fmt_mpc.pas'
+,fmt_ofr in 'formats\fmt_ofr.pas'
+,fmt_tta in 'formats\fmt_tta.pas'
+,fmt_real in 'formats\fmt_real.pas'
+,fmt_ape in 'formats\fmt_ape.pas'
+,fmt_wav in 'formats\fmt_wav.pas'
+,fmt_flv in 'formats\fmt_flv.pas'
+,fmt_aac in 'formats\fmt_aac.pas'
+,fmt_mkv in 'formats\fmt_mkv.pas'
+,fmt_m4a in 'formats\fmt_m4a.pas'
+,fmt_wma in 'formats\fmt_wma.pas'
+,fmt_avi in 'formats\fmt_avi.pas'
+,fmt_ogg in 'formats\fmt_ogg.pas'
+,fmt_mp3 in 'formats\fmt_mp3.pas'
+,tags in 'formats\tags.pas'
diff --git a/plugins/Watrack/lst_players.inc b/plugins/Watrack/lst_players.inc
new file mode 100644
index 0000000000..759b06cf51
--- /dev/null
+++ b/plugins/Watrack/lst_players.inc
@@ -0,0 +1,17 @@
+,pl_apollo in 'players\pl_apollo.pas'
+,pl_behold in 'players\pl_behold.pas'
+,pl_mradio in 'players\pl_mradio.pas'
+,pl_lastfm in 'players\pl_lastfm.pas'
+,pl_1by1 in 'players\pl_1by1.pas'
+,pl_bs in 'players\pl_bs.pas'
+,pl_la in 'players\pl_la.pas'
+,pl_mmonkey in 'players\pl_mmonkey.pas'
+,pl_itunes in 'players\pl_itunes.pas'
+,pl_cowon in 'players\pl_cowon.pas'
+,pl_vlc in 'players\pl_vlc.pas'
+// keep file for check in future
+//,pl_wmp in 'players\pl_wmp.pas'
+,pl_mpc in 'players\pl_mpc.pas'
+,pl_aimp in 'players\pl_aimp.pas'
+,pl_foobar in 'players\pl_foobar.pas'
+,pl_winamp in 'players\pl_winamp.pas'
diff --git a/plugins/Watrack/m_music.inc b/plugins/Watrack/m_music.inc
new file mode 100644
index 0000000000..aba0bd27f6
--- /dev/null
+++ b/plugins/Watrack/m_music.inc
@@ -0,0 +1,419 @@
+{$IFNDEF M_MUSIC}
+{$DEFINE M_MUSIC}
+
+// defined in interfaces.inc
+//const MIID_WATRACK:MUUID='{FC6C81F4-837E-4430-9601-A0AA43177AE3}';
+
+type
+ pSongInfoA = ^tSongInfoA;
+ tSongInfoA = record
+ artist :PAnsiChar;
+ title :PAnsiChar;
+ album :PAnsiChar;
+ genre :PAnsiChar;
+ comment :PAnsiChar;
+ year :PAnsiChar;
+ mfile :PAnsiChar; // media file
+ kbps :dword;
+ khz :dword;
+ channels :dword;
+ track :dword;
+ total :dword; // music length
+ time :dword; // elapsed time
+ wndtext :PAnsiChar; // window title
+ player :PAnsiChar; // player name
+ plyver :dword; // player version
+ icon :THANDLE; // player icon
+ fsize :dword; // media file size
+ vbr :dword;
+ status :integer; // WAT_MES_* const
+ plwnd :HWND; // player window
+ // video part
+ codec :dword;
+ width :dword;
+ height :dword;
+ fps :dword;
+ date :int64;
+ txtver :PAnsiChar;
+ lyric :PAnsiChar;
+ cover :PAnsiChar;
+ volume :dword;
+ url :PAnsiChar; // player homepage
+ winampwnd:HWND;
+ end;
+type
+ pSongInfo=^tSongInfo;
+ tSongInfo = record
+ artist :pWideChar;
+ title :pWideChar;
+ album :pWideChar;
+ genre :pWideChar;
+ comment :pWideChar;
+ year :pWideChar;
+ mfile :pWideChar; // media file
+ kbps :dword;
+ khz :dword;
+ channels :dword;
+ track :dword;
+ total :dword; // music length
+ time :dword; // elapsed time
+ wndtext :pWideChar; // window title
+ player :pWideChar; // player name
+ plyver :dword; // player version
+ icon :THANDLE; // player icon
+ fsize :dword; // media file size
+ vbr :dword;
+ status :integer; // WAT_MES_* const
+ plwnd :HWND; // player window
+ // video part
+ codec :dword;
+ width :dword;
+ height :dword;
+ fps :dword;
+ date :int64;
+ txtver :pWideChar;
+ lyric :pWideChar;
+ cover :pWideChar; // cover path
+ volume :dword;
+ url :PWideChar; // player homepage
+ winampwnd:HWND;
+ end;
+ pSongInfoW = pSongInfo;
+ tSongInfoW = tSongInfo;
+
+const
+ // result codes
+ WAT_RES_UNKNOWN = -2;
+ WAT_RES_NOTFOUND = -1;
+ WAT_RES_ERROR = WAT_RES_NOTFOUND;
+ WAT_RES_OK = 0;
+ WAT_RES_ENABLED = WAT_RES_OK;
+ WAT_RES_DISABLED = 1;
+ // internal
+ WAT_RES_NEWFILE = 3;
+ WAT_RES_NEWPLAYER = 4;
+
+// result for MS_WAT_GETMUSICINFO service
+const
+ WAT_PLS_NORMAL = WAT_RES_OK;
+ WAT_PLS_NOMUSIC = WAT_RES_DISABLED;
+ WAT_PLS_NOTFOUND = WAT_RES_NOTFOUND;
+
+const
+ WAT_INF_UNICODE = 0;
+ WAT_INF_ANSI = 1;
+ WAT_INF_UTF8 = 2;
+ WAT_INF_CHANGES = $100;
+
+const
+ MS_WAT_INSERT:PAnsiChar = 'WATrack/Insert';
+ MS_WAT_EXPORT:PAnsiChar = 'WATrack/Export';
+
+const
+{
+ wParam : WAT_INF_* constant
+ lParam : pointer to pSongInfo (Unicode) or pSongInfoA (ANSI/UTF8)
+ Affects: Fill structure by currently played music info
+ returns: WAT_PLS_* constant
+ note: pointer will be point to global SongInfo structure of plugin
+ warning: Non-Unicode data filled only by request
+ if lParam=0 only internal SongInfo structure will be filled
+ Example:
+ var p:pSongInfo;
+ CallService(MS_WAT_GETMUSICINFO,0,dword(@p));
+}
+ MS_WAT_GETMUSICINFO:PAnsiChar = 'WATrack/GetMusicInfo';
+{
+ wParam:0
+ lParam : pointer to pSongInfo (Unicode)
+ Affects: Fill structure by info from file named in SongInfo.mfile
+ returns: 0, if success
+ note: fields, which values can't be obtained, leaves old values.
+ you must free given strings by miranda mir_free
+}
+ MS_WAT_GETFILEINFO:PAnsiChar = 'WATrack/GetFileInfo';
+
+{
+ wParam: encoding (WAT_INF_* consts, 0 = WAT_INF_UNICODE)
+ lParam: codepage (0 = ANSI)
+ Returns Global unicode SongInfo pointer or tranlated to Ansi/UTF8 structure
+}
+ MS_WAT_RETURNGLOBAL:PAnsiChar = 'WATrack/GetMainStructure';
+
+//!! DON'T CHANGE THESE VALUES!
+const
+ WAT_CTRL_FIRST = 1;
+
+ WAT_CTRL_PREV = 1;
+ WAT_CTRL_PLAY = 2;
+ WAT_CTRL_PAUSE = 3;
+ WAT_CTRL_STOP = 4;
+ WAT_CTRL_NEXT = 5;
+ WAT_CTRL_VOLDN = 6;
+ WAT_CTRL_VOLUP = 7;
+ WAT_CTRL_SEEK = 8; // lParam is new position (sec)
+
+ WAT_CTRL_LAST = 8;
+
+{
+ wParam: button code (WAT_CTRL_* const)
+ lParam: 0, or value (see WAT_CTRL_* const comments)
+ Affects: emulate player button pressing
+ returns: 0 if unsuccesful
+}
+ MS_WAT_PRESSBUTTON:PAnsiChar = 'WATrack/PressButton';
+
+{
+ Get user's Music Info
+}
+ MS_WAT_GETCONTACTINFO:PAnsiChar = 'WATrack/GetContactInfo';
+
+// ------------ Plugin/player status ------------
+
+{
+ wParam: 1 - switch off plugin
+ 0 - switch on plugin
+ -1 - switch plugin status
+ 2 - get plugin version
+ other - get plugin status
+ lParam: 0
+ Affects: Switch plugin status to enabled or disabled
+ returns: version, old plugin status, 0, if was enabled
+}
+ MS_WAT_PLUGINSTATUS:PAnsiChar = 'WATrack/PluginStatus';
+
+ ME_WAT_MODULELOADED:PAnsiChar = 'WATrack/ModuleLoaded';
+
+const
+ WAT_EVENT_PLAYERSTATUS = 1; // WAT_PLS_* in loword, WAT_MES_* in hiword
+ WAT_EVENT_NEWTRACK = 2; // SongInfo ptr
+ WAT_EVENT_PLUGINSTATUS = 3; // 0-enabled; 1-dis.temporary; 2-dis.permanent
+ WAT_EVENT_NEWPLAYER = 4; //
+ WAT_EVENT_NEWTEMPLATE = 5; // TM_* constant
+
+{
+ Plugin or player status changed:
+ wParam: type of event (see above)
+ lParam: value
+}
+ ME_WAT_NEWSTATUS:PAnsiChar = 'WATrack/NewStatus';
+
+// ---------- Popup module ------------
+
+{
+ wParam: not used
+ lParam: not used
+ Affects: Show popup or Info window with current music information
+ note: Only Info window will be showed if Popup plugin disabled
+}
+ MS_WAT_SHOWMUSICINFO:PAnsiChar = 'WATrack/ShowMusicInfo';
+
+// --------- Statistic (report) module -------------
+
+{
+ wParam: pointer to log file name or NIL
+ lParam: pointer to report file name or NIL
+ Affects: Create report from log and run it (if option is set)
+ returns: 0 if unsuccesful
+ note: if wParam or lParam is a NIL then file names from options are used
+}
+ MS_WAT_MAKEREPORT :PAnsiChar = 'WATrack/MakeReport';
+// MS_WAT_MAKEREPORTW:PAnsiChar = 'WATrack/MakeReportW';
+
+{
+ wParam, lParam - not used
+ Affects: pack statistic file
+}
+ MS_WAT_PACKLOG:PAnsiChar = 'WATrack/PackLog';
+
+{
+ wParam: not used
+ lParam: pointer to SongInfo
+}
+ MS_WAT_ADDTOLOG:PAnsiChar = 'WATrack/AddToLog';
+
+// ----------- Formats and players -----------
+
+// media file status
+
+const
+ WAT_MES_STOPPED = 0;
+ WAT_MES_PLAYING = 1;
+ WAT_MES_PAUSED = 2;
+ WAT_MES_UNKNOWN = -1;
+
+const
+ WAT_ACT_REGISTER = 1;
+ WAT_ACT_UNREGISTER = 2;
+ WAT_ACT_DISABLE = 3;
+ WAT_ACT_ENABLE = 4;
+ WAT_ACT_GETSTATUS = 5; // not found/enabled/disabled
+ WAT_ACT_SETACTIVE = 6;
+ WAT_ACT_REPLACE = $10000; // can be combined with WAT_REGISTERFORMAT
+
+const
+ // flags
+ WAT_OPT_DISABLED = $00000001; // [formats,players,options] registered but disabled
+ WAT_OPT_ONLYONE = $00000002; // [formats,players] code can't be overwriten
+ WAT_OPT_PLAYERINFO = $00000004; // [players] song info from player
+ WAT_OPT_WINAMPAPI = $00000008; // [players] Winamp API support
+ WAT_OPT_CHECKTIME = $00000010; // [options] check file time for changes
+ WAT_OPT_VIDEO = $00000020; // [formats,options] format is video
+ WAT_OPT_LAST = $00000040; // (internal-Winamp Clone) put to the end of queue
+ WAT_OPT_FIRST = $00000080; // (internal)
+ WAT_OPT_TEMPLATE = $00000100; // (internal)
+ WAT_OPT_IMPLANTANT = $00000200; // [options] use process implantation
+ WAT_OPT_HASURL = $00000400; // [players] URL field present
+ WAT_OPT_CHANGES = $00000800; // (internal) obtain only chaged values
+ // (volume, status, window text, elapsed time)
+ WAT_OPT_APPCOMMAND = $00001000; // [options] Special (multimedia) key support
+ WAT_OPT_CHECKALL = $00002000; // [options] Check all players
+ WAT_OPT_KEEPOLD = $00004000; // [options] Keep Old opened file
+ WAT_OPT_MULTITHREAD = $00008000; // [options] Use multithread scan
+ WAT_OPT_SINGLEINST = $00010000; // [players] Single player instance
+ WAT_OPT_PLAYERDATA = $00020000; // (internal) to obtain player data
+ WAT_OPT_CONTAINER = $00040000; // [formats] format is container (need to check full)
+
+type
+ tReadFormatProc = function(var Info:tSongInfo):boolean; cdecl;
+ pMusicFormat = ^tMusicFormat;
+ tMusicFormat = record
+ proc :tReadFormatProc;
+ ext :array [0..7] of AnsiChar;
+ flags:cardinal;
+ end;
+
+const
+{
+ wParam: action
+ lParam: pointer to tMusicFormat if wParam = WAT_ACT_REGISTER,
+ else - pointer to extension string (ANSI)
+ returns: see result codes
+}
+ MS_WAT_FORMAT:PAnsiChar = 'WATrack/Format';
+
+{
+ wParam: pointer to SongInfo structure (plwind field must be initialized)
+ lParam: flags
+ Affects: trying to fill SongInfo using Winamp API
+}
+ MS_WAT_WINAMPINFO:PAnsiChar = 'WATrack/WinampInfo';
+
+{
+ wParam: window
+ lParam: LoWord - command; HiWord - value
+}
+ MS_WAT_WINAMPCOMMAND:PAnsiChar = 'WATrack/WinampCommand';
+
+type
+ tInitProc = function():integer;cdecl;
+ tDeInitProc = function():integer;cdecl;
+ tStatusProc = function(wnd:HWND):integer;cdecl;
+ tNameProc = function(wnd:HWND;flags:integer):pWideChar;cdecl;
+ tCheckProc = function(wnd:HWND;flags:integer):HWND;cdecl;
+ tInfoProc = function(var SongInfo:tSongInfo;flags:integer):integer;cdecl;
+ tCommandProc = function(wnd:HWND;command:integer;value:integer):integer;cdecl;
+
+ pPlayerCell = ^tPlayerCell;
+ tPlayerCell = record
+ Desc :PAnsiChar; // Short player name
+ flags :cardinal;
+ Icon :HICON; // can be 0. for registration only
+ Init :pointer; // tInitProc; can be NIL. initialize any data
+ DeInit :pointer; // tDeInitProc; can be NIL. finalize player processing
+ Check :pointer; // tCheckProc; check player
+ GetStatus:pointer; // tStatusProc; can be NIL. get player status
+ GetName :pointer; // tNameProc; can be NIL. get media filename
+ GetInfo :pointer; // tInfoProc; can be NIL. get info from player
+ Command :pointer; // tCommandProc; can be NIL. send command to player
+ URL :PAnsiChar; // only if WAT_OPT_HASURL flag present
+ Notes :PWideChar; // any tips, notes etc for this player
+ end;
+
+const
+{
+ wParam: action
+ lParam: pointer to tPlayerCell if wParam = WAT_ACT_REGISTER,
+ else - pointer to player description string (ANSI)
+ returns: player window handle or value>0 if found
+ note: If you use GetName or GetInfo field, please, do not return empty
+ filename even when mediafile is remote!
+}
+ MS_WAT_PLAYER:PAnsiChar = 'WATrack/Player';
+
+// --------- MyShows.ru ---------
+
+{
+ Toggle MyShows scrobbling status
+ wParam,lParam=0
+ Returns: previous state
+}
+const
+ MS_WAT_MYSHOWS:pAnsiChar = 'WATrack/MyShows';
+
+
+const
+ MS_WAT_MYSHOWSINFO:pAnsiChar = 'WATrack/MyShowsInfo';
+
+// --------- Last FM ---------
+
+{
+ Toggle LastFM scrobbling status
+ wParam,lParam=0
+ Returns: previous state
+}
+const
+ MS_WAT_LASTFM:pAnsiChar = 'WATrack/LastFM';
+
+{
+ Get Info based on currently played song
+ wParam: pLastFMInfo
+ lParam: int language (first 2 bytes - 2-letters language code)
+}
+type
+ pLastFMInfo = ^tLastFMInfo;
+ tLastFMInfo = record
+ request:cardinal; // 0 - artist, 1 - album, 2 - track
+ artist :pWideChar; // artist
+ album :pWideChar; // album or similar artists for Artist info request
+ title :pWideChar; // track title
+ tags :pWideChar; // tags
+ info :pWideChar; // artist bio or wiki article
+ image :pAnsiChar; // photo/cover link
+ similar:pWideChar;
+ release:pWideChar;
+ trknum :cardinal;
+ end;
+const
+ MS_WAT_LASTFMINFO:pAnsiChar = 'WATrack/LastFMInfo';
+
+// --------- Templates ----------
+
+const
+{
+ wParam: 0 (standard Info) or pSongInfo
+ lParam: Unicode template
+ returns: New Unicode (replaced) string
+}
+ MS_WAT_REPLACETEXT:PAnsiChar = 'WATrack/ReplaceText';
+
+{
+ event types for History
+ Blob structure for EVENTTYPE_WAT_ANSWER:
+ Uniciode artist#0title#0album#0answer
+}
+const
+ EVENTTYPE_WAT_REQUEST = 9601;
+ EVENTTYPE_WAT_ANSWER = 9602;
+ EVENTTYPE_WAT_ERROR = 9603;
+ EVENTTYPE_WAT_MESSAGE = 9604;
+
+const
+{
+ wParam: 0 or parent window
+ lParam: 0
+ note: Shows Macro help window with edit aliases ability
+}
+ MS_WAT_MACROHELP:pAnsiChar = 'WATrack/MacroHelp';
+
+{$ENDIF M_MUSIC}
diff --git a/plugins/Watrack/macros.pas b/plugins/Watrack/macros.pas
new file mode 100644
index 0000000000..cdbe52991e
--- /dev/null
+++ b/plugins/Watrack/macros.pas
@@ -0,0 +1,93 @@
+{to Variables plugin and Help dialog}
+unit macros;
+
+interface
+
+type
+ pvar = ^tvar;
+ tvar = packed record
+ name :PWideChar;
+ alias:PWideChar;
+ help :PAnsiChar;
+ end;
+
+// --- data ---
+const
+ numvars = 35;
+
+ mn_wndtext = 0;
+ mn_artist = 1;
+ mn_title = 2;
+ mn_album = 3;
+ mn_genre = 4;
+ mn_file = 5;
+ mn_kbps = 6;
+ mn_bitrate = 7;
+ mn_track = 8;
+ mn_channels = 9;
+ mn_mono = 10;
+ mn_khz = 11;
+ mn_samplerate = 12;
+ mn_total = 13;
+ mn_length = 14;
+ mn_year = 15;
+ mn_time = 16;
+ mn_percent = 17;
+ mn_comment = 18;
+ mn_player = 19;
+ mn_version = 20;
+ mn_size = 21;
+ mn_type = 22;
+ mn_vbr = 23;
+ mn_status = 24;
+ mn_fps = 25;
+ mn_codec = 26;
+ mn_width = 27;
+ mn_height = 28;
+ mn_txtver = 29;
+ mn_lyric = 30;
+ mn_cover = 31;
+ mn_volume = 32;
+ mn_playerhome = 33;
+ mn_nstatus = 34;
+ vars:array [0..numvars-1] of tvar = (
+{00} (name:'wndtext' ;alias:nil;help:'player window title'),
+{01} (name:'artist' ;alias:nil;help:'artist'),
+{02} (name:'title' ;alias:nil;help:'song title'),
+{03} (name:'album' ;alias:nil;help:'album'),
+{04} (name:'genre' ;alias:nil;help:'genre'),
+{05} (name:'file' ;alias:nil;help:'media file name'),
+{06} (name:'kbps' ;alias:nil;help:'bitrate'),
+{07} (name:'bitrate' ;alias:nil;help:nil),
+{08} (name:'track' ;alias:nil;help:'track number'),
+{09} (name:'channels' ;alias:nil;help:'number of channels'),
+{10} (name:'mono' ;alias:nil;help:'"mono"/"stereo"'),
+{11} (name:'khz' ;alias:nil;help:'samplerate'),
+{12} (name:'samplerate';alias:nil;help:nil),
+{13} (name:'total' ;alias:nil;help:'total song length (sec)'),
+{14} (name:'length' ;alias:nil;help:nil),
+{15} (name:'year' ;alias:nil;help:'song year (date)'),
+{16} (name:'time' ;alias:nil;help:'current song position (sec)'),
+{17} (name:'percent' ;alias:nil;help:'time/length * 100%'),
+{18} (name:'comment' ;alias:nil;help:'comment from tag'),
+{19} (name:'player' ;alias:nil;help:'player name'),
+{20} (name:'version' ;alias:nil;help:'player version'),
+{21} (name:'size' ;alias:nil;help:'media file size'),
+{22} (name:'type' ;alias:nil;help:'media file type'),
+{23} (name:'vbr' ;alias:nil;help:'VBR or not (empty)'),
+{24} (name:'status' ;alias:nil;help:'player status (stopped,playing,paused)'),
+{25} (name:'fps' ;alias:nil;help:'FPS (frames per second), video only'),
+{26} (name:'codec' ;alias:nil;help:'codec, video only'),
+{27} (name:'width' ;alias:nil;help:'width, video only'),
+{28} (name:'height' ;alias:nil;help:'height, video only'),
+{29} (name:'txtver' ;alias:nil;help:'player version in text format'),
+{30} (name:'lyric' ;alias:nil;help:'Lyric from ID3v2 tag'),
+{31} (name:'cover' ;alias:nil;help:'Cover file path'),
+{32} (name:'volume' ;alias:nil;help:'Player volume (0-15)'),
+{33} (name:'playerhome';alias:nil;help:'Player homepage URL'),
+{34} (name:'nstatus' ;alias:nil;help:'player status (not translated)')
+ );
+
+implementation
+
+end. \ No newline at end of file
diff --git a/plugins/Watrack/make.bat b/plugins/Watrack/make.bat
new file mode 100644
index 0000000000..cfaf1df3e1
--- /dev/null
+++ b/plugins/Watrack/make.bat
@@ -0,0 +1,26 @@
+@echo off
+set myopts=-dMiranda
+set dprname=watrack.dpr
+
+..\delphi\brcc32.exe res\watrack.rc -fores\watrack.res
+..\delphi\brcc32.exe lastfm\lastfm.rc -folastfm\lastfm.res
+..\delphi\brcc32.exe myshows\myshows.rc -fomyshows\myshows.res
+..\delphi\brcc32.exe players\mradio.rc -foplayers\mradio.res
+..\delphi\brcc32.exe kolframe\frm.rc -fokolframe\frm.res
+..\delphi\brcc32.exe popup\popup.rc -fopopup\popup.res
+..\delphi\brcc32.exe proto\proto.rc -foproto\proto.res
+..\delphi\brcc32.exe stat\stat.rc -fostat\stat.res
+..\delphi\brcc32.exe status\status.rc -fostatus\status.res
+..\delphi\brcc32.exe templates\templates.rc -fotemplates\templates.res
+
+if /i '%1' == 'fpc' (
+ ..\FPC\bin\fpc.exe %myopts% %dprname% %2 %3 %4 %5 %6 %7 %8 %9
+) else if /i '%1' == 'fpc64' (
+ ..\FPC\bin64\ppcrossx64.exe %myopts% %dprname% %2 %3 %4 %5 %6 %7 %8 %9
+) else if /i '%1' == 'xe2' (
+ ..\XE2\BIN\dcc32.exe%myopts% %dprname% %2 %3 %4 %5 %6 %7 %8 %9
+) else if /i '%1' == 'xe64' (
+ ..\XE2\BIN\dcc64.exe %myopts% %dprname% %2 %3 %4 %5 %6 %7 %8 %9
+) else (
+ ..\delphi\dcc32 -b -dKOL_MCK -dUNICODE_CTRLS %myopts% %dprname% %1 %2 %3 %4 %5 %6 %7 %8 %9
+)
diff --git a/plugins/Watrack/myrtf.pas b/plugins/Watrack/myrtf.pas
new file mode 100644
index 0000000000..7a6bf2255f
--- /dev/null
+++ b/plugins/Watrack/myrtf.pas
@@ -0,0 +1,219 @@
+{RTF related code}
+unit MyRTF;
+{$include compilers.inc}
+
+interface
+uses windows;
+
+procedure SendRTF(wnd:hwnd;txt:PWideChar;isUnicode:Boolean;CP:integer=CP_ACP);
+
+implementation
+uses richedit,common,messages,m_api;
+
+const
+ RTFBufferSize = 16384;
+const
+ CTableHdr = '{\colortbl';
+const
+(*
+ ColorTable = '{\colortbl;'+
+ '\red255\green255\blue255;'+
+ '\red0\green0\blue0;'+
+ '\red0\green0\blue127;'+
+ '\red0\green147\blue0;'+
+ '\red255\green0\blue0;'+
+ '\red127\green0\blue0;'+
+ '\red156\green0\blue156;'+
+ '\red252\green127\blue0;'+
+ '\red255\green255\blue0;'+
+ '\red0\green252\blue0;'+
+ '\red0\green147\blue147;'+
+ '\red0\green255\blue255;'+
+ '\red0\green0\blue252;'+
+ '\red255\green0\blue255;'+
+ '\red127\green127\blue127;'+
+ '\red210\green210\blue210;}';
+*)
+ ColorTableD =
+ '\red255\green255\blue255;'+
+ '\red0\green0\blue0;'+
+ '\red0\green0\blue127;'+
+ '\red0\green147\blue0;'+
+ '\red255\green0\blue0;'+
+ '\red127\green0\blue0;'+
+ '\red156\green0\blue156;'+
+ '\red252\green127\blue0;'+
+ '\red255\green255\blue0;'+
+ '\red0\green252\blue0;'+
+ '\red0\green147\blue147;'+
+ '\red0\green255\blue255;'+
+ '\red0\green0\blue252;'+
+ '\red255\green0\blue255;'+
+ '\red127\green127\blue127;'+
+ '\red210\green210\blue210;';
+
+function StreamWriteCallback(dwCookie:dword_ptr;pbBuff:PAnsiChar;cb:long;var pcb:long):dword;stdcall;
+begin
+ pcb:=StrLen(PAnsiChar(dwCookie));
+ if cb<pcb then pcb:=cb;
+ move(PAnsiChar(dwCookie)^,pbBuff^,pcb);
+ result:=0;
+end;
+
+procedure WriteRTF(wnd:hwnd;const pszText:PAnsiChar);
+var
+ stream:TEDITSTREAM;
+begin
+ FillChar(stream,SizeOf(stream),0);
+ stream.pfnCallback:=@StreamWriteCallback;
+ stream.dwCookie :=dword_ptr(pszText);
+ SendMessage(wnd,EM_STREAMIN,SF_RTF or SFF_PLAINRTF or SFF_SELECTION,lparam(@stream));
+end;
+
+function StreamReadCallback(dwCookie:dword_ptr;pbBuff:PAnsiChar;cb:long;var pcb:long):dword;stdcall;
+type
+ pdword_ptr=^dword_ptr;
+begin
+ pcb:=cb;
+ move(pbBuff^,PAnsiChar(pdword_ptr(dwCookie)^)^,pcb);
+// PAnsiChar(pdword(dwCookie)^)[pcb]:=#0;
+ result:=0;
+end;
+
+procedure ReadRTF(wnd:hwnd;var dst:PAnsiChar);
+var
+ stream:TEDITSTREAM;
+begin
+ FillChar(stream,SizeOf(stream),0);
+ stream.pfnCallback:=@StreamReadCallback;
+ stream.dwCookie:=dword_ptr(@dst);
+ SendMessage(wnd,EM_STREAMOUT,SF_RTF+SFF_SELECTION,lparam(@stream));
+end;
+
+procedure ReplaceTag(src:PAnsiChar;what,new:PAnsiChar;recurse:boolean);
+var
+ i:integer;
+ block:boolean;
+ p:pAnsiChar;
+begin
+ block:=what^='{';
+ repeat
+ p:=StrPos(src,what);
+ if p<>nil then
+ begin
+ src:=p;
+ if src[StrLen(what)] in ['A'..'Z','a'..'z'] then
+ begin
+ inc(src);
+ continue;
+ end;
+ i:=1;
+ if block then
+ begin
+ while src[i]<>'}' do inc(i); inc(i);
+ end
+ else
+ begin
+ while not (src[i] in ['}',' ','\',';',#13]) do
+ inc(i);
+ end;
+ StrCopy(src,src+i);
+ if new<>nil then
+ StrInsert(new,src,0);
+ end
+ else
+ break;
+ if not recurse then break;
+ until false;
+end;
+
+procedure ReplaceTags(var src:PAnsiChar);
+var
+ i:integer;
+begin
+ ReplaceTag(src,'\b' ,nil,false);
+ ReplaceTag(src,'\i' ,nil,false);
+ ReplaceTag(src,'\ul' ,nil,false);
+ if (StrPos(src,'\{cf')<>nil) or (StrPos(src,'\{bg')<>nil) then
+ begin
+ ReplaceTag(src,'\cf' ,nil,false);
+ ReplaceTag(src,'\highlight',nil,false);
+ StrReplace(src,'\{/cf\}','\cf17 ');
+ StrReplace(src,'\{/bg\}','\highlight0 ');
+ i:=StrIndex(src,CTableHdr);
+ StrInsert(ColorTableD,src,i+integer(StrLen(CTableHdr))+1);
+ ReplaceTag(src,'\pard','\pard\cf17',false);
+ end;
+
+ StrReplace(src,'\{b\}' ,'\b1 ');
+ StrReplace(src,'\{/b\}' ,'\b0 ');
+ StrReplace(src,'\{i\}' ,'\i1 ');
+ StrReplace(src,'\{/i\}' ,'\i0 ');
+ StrReplace(src,'\{u\}' ,'\ul ');
+ StrReplace(src,'\{/u\}' ,'\ul0 ');
+
+ repeat
+ i:=StrIndex(src,'\{cf');
+ if i>0 then
+ begin
+ StrCopy(src+i,src+i+1);
+ i:=StrIndex(src,'\}');
+ if i>0 then
+ begin
+ StrCopy(src+i,src+i+1);
+ src[i-1]:=' ';
+ end;
+ end;
+ until i=0;
+ repeat
+ i:=StrIndex(src,'\{bg');
+ if i>0 then
+ begin
+ StrCopy(src+i,src+i+3);
+ StrInsert('highlight',src,i);
+ i:=StrIndex(src,'\}');
+ if i>0 then
+ begin
+ StrCopy(src+i,src+i+1);
+ src[i-1]:=' ';
+ end;
+ end;
+ until i=0;
+end;
+
+function CharCount(p:PWideChar):integer;
+begin
+ result:=0;
+ while p^<>#0 do
+ begin
+ if p^=#10 then inc(result);
+ inc(p);
+ end;
+end;
+
+procedure SendRTF(wnd:hwnd;txt:PWideChar;isUnicode:Boolean;CP:integer=CP_ACP);
+var
+ tmp:PAnsiChar;
+ sstart:integer;
+ ls:PAnsiChar;
+begin
+ SendMessage(wnd,EM_GETSEL,wparam(@sstart),0);
+ if isUnicode then
+ SendMessagew(wnd,EM_REPLACESEL,0,lparam(txt))
+ else
+ begin
+ SendMessageA(wnd,EM_REPLACESEL,0,lparam(WideToAnsi(txt,ls,CP)));
+ mFreeMem(ls);
+ end;
+
+ SendMessage(wnd,EM_SETSEL,sstart,sstart+integer(StrLenW(txt))-CharCount(txt));
+ mGetMem(tmp,RTFBufferSize);
+ FillChar(tmp^,RTFBufferSize,0);
+ ReadRTF(wnd,tmp);
+ ReplaceTags(tmp);
+ WriteRTF(wnd,tmp);
+ mFreeMem(tmp);
+ SendMessage(wnd,EM_SETSEL,-1,0);
+end;
+
+end.
diff --git a/plugins/Watrack/myshows/i_const.inc b/plugins/Watrack/myshows/i_const.inc
new file mode 100644
index 0000000000..47c4b52618
--- /dev/null
+++ b/plugins/Watrack/myshows/i_const.inc
@@ -0,0 +1,14 @@
+const
+ IDC_LOGIN = 1025;
+ IDC_PASS = 1026;
+ IDC_TRIES = 1027;
+ IDC_TIME = 1028;
+ IDC_SCROBPOS = 1029;
+
+ IDC_INFO_SERIES = 1039;
+ IDC_DATA_PIC = 1040;
+ IDC_DATA_SERIES = 1041;
+ IDC_DATA_EPISODE = 1042;
+ IDC_DATA_TAGS = 1044;
+ IDC_DATA_INFO = 1045;
+ IDC_KINOPOISK = 1046;
diff --git a/plugins/Watrack/myshows/i_cookies.inc b/plugins/Watrack/myshows/i_cookies.inc
new file mode 100644
index 0000000000..1258490199
--- /dev/null
+++ b/plugins/Watrack/myshows/i_cookies.inc
@@ -0,0 +1,91 @@
+{}
+const
+ cookies:pAnsiChar=nil;
+
+function ExtractCookies(resp:PNETLIBHTTPREQUEST):integer;
+var
+ cnt,len:integer;
+ p,pc:pAnsiChar;
+begin
+ result:=0;
+
+ mFreeMem(cookies);
+ mGetMem(cookies,1024);
+
+ pc:=cookies;
+ for cnt:=0 to resp^.headersCount-1 do
+ begin
+ with resp^.headers[cnt] do
+ if StrCmp(szName,'Set-Cookie')=0 then
+ begin
+ len:=0;
+ p:=szValue;
+ while (p^<>#0) and (p^<>';') do
+ begin
+ inc(p);
+ inc(len);
+ end;
+ if pc<>cookies then
+ begin
+ pc^:=';'; inc(pc);
+ pc^:=' '; inc(pc);
+ end;
+ pc:=StrCopyE(pc,szValue,len);
+ inc(result);
+ end;
+ end;
+end;
+
+function SendRequestCookies(url:PAnsiChar;useCookies:boolean):pAnsiChar;
+var
+ nlu:TNETLIBUSER;
+ req :TNETLIBHTTPREQUEST;
+ resp:PNETLIBHTTPREQUEST;
+ hTmpNetLib:THANDLE;
+ nlh:array [0..10] of TNETLIBHTTPHEADER;
+begin
+ result:=nil;
+
+ FillChar(req,SizeOf(req),0);
+ req.cbSize :=NETLIBHTTPREQUEST_V1_SIZE;//SizeOf(req);
+ req.requestType:=REQUEST_GET;
+ req.szUrl :=url;
+ req.flags :=NLHRF_NODUMP or NLHRF_HTTP11;
+
+ if useCookies and (cookies<>nil) then
+ begin
+ nlh[0].szName :='Cookie';
+ nlh[0].szValue:=cookies;
+
+ req.headers :=@nlh;
+ req.headersCount:=1;
+ end;
+
+ FillChar(nlu,SizeOf(nlu),0);
+ nlu.cbSize :=SizeOf(nlu);
+ nlu.flags :=NUF_HTTPCONNS or NUF_NOHTTPSOPTION or NUF_OUTGOING or NUF_NOOPTIONS;
+ nlu.szSettingsModule:='dummy';
+ hTmpNetLib:=CallService(MS_NETLIB_REGISTERUSER,0,lparam(@nlu));
+
+ resp:=pointer(CallService(MS_NETLIB_HTTPTRANSACTION,hTmpNetLib,lparam(@req)));
+
+ if resp<>nil then
+ begin
+ if resp^.resultCode=200 then
+ begin
+ if resp.pData<>nil then
+ StrDup(result,resp.pData,resp.dataLength)
+ else
+ result:=PAnsiChar(200);
+ if not useCookies then
+ ExtractCookies(resp);
+ end
+ else
+ begin
+ result:=pAnsiChar(int_ptr(resp^.resultCode and $0FFF));
+ end;
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT,0,lparam(resp));
+ end;
+
+ CallService(MS_NETLIB_CLOSEHANDLE,hTmpNetLib,0);
+end;
diff --git a/plugins/Watrack/myshows/i_myshows_api.inc b/plugins/Watrack/myshows/i_myshows_api.inc
new file mode 100644
index 0000000000..572cc3ee31
--- /dev/null
+++ b/plugins/Watrack/myshows/i_myshows_api.inc
@@ -0,0 +1,247 @@
+{}
+//type tDigest = array [0..15] of byte;
+(*
+const
+ client_id = 'wat';//'wat'; 'tst'
+ client_ver = '1.0';
+ api_key = '51f5d25159da31b0814609c3a12900e2';
+*)
+{$include i_cookies.inc}
+
+const API_URL = 'http://api.myshows.ru/';
+
+const
+ defreq = API_URL+'profile/login?login=<login>&password=<password>';
+
+procedure ShowError(code:integer);
+var
+ buf:array [0..511] of WideChar;
+ ppc:pWideChar;
+begin
+ case code of
+ 401: begin // Òðåáóåòñÿ àâòîðèçàöèÿ
+ ppc:='Authorization required';
+ end;
+ 403: begin // Èìÿ ïîëüçîâàòåëÿ èëè ïàðîëü íå ïîäîøëè
+ ppc:='User name of password wrong';
+ end;
+ 404: begin // Íå íàéäåíî, íåïðàâèëüíûå ïàðàìåòðû
+ ppc:='Not found / wrong parameters';
+ end;
+ 500: begin // ïàðàìåòð çàïðîñà îòñóòñòâóåò
+ ppc:='Wrong query parameters';
+ end;
+ else
+ ppc:='something wrong!';
+ end;
+ StrCopyW(StrCopyEW(buf,'MyShows: '),TranslateW(ppc));
+
+ if ServiceExists(MS_POPUP_SHOWMESSAGEW)<>0 then
+ CallService(MS_POPUP_SHOWMESSAGEW,TWPARAM(@buf),SM_WARNING)
+ else
+ MessageBoxW(0,@buf,'ERROR',MB_ICONERROR)
+end;
+
+function GetMD5Str(digest:TMD5Hash; buf:pAnsiChar):PAnsiChar;
+begin
+ buf[00]:=HexDigitChrLo[digest[00] shr 4]; buf[01]:=HexDigitChrLo[digest[00] and $0F];
+ buf[02]:=HexDigitChrLo[digest[01] shr 4]; buf[03]:=HexDigitChrLo[digest[01] and $0F];
+ buf[04]:=HexDigitChrLo[digest[02] shr 4]; buf[05]:=HexDigitChrLo[digest[02] and $0F];
+ buf[06]:=HexDigitChrLo[digest[03] shr 4]; buf[07]:=HexDigitChrLo[digest[03] and $0F];
+ buf[08]:=HexDigitChrLo[digest[04] shr 4]; buf[09]:=HexDigitChrLo[digest[04] and $0F];
+ buf[10]:=HexDigitChrLo[digest[05] shr 4]; buf[11]:=HexDigitChrLo[digest[05] and $0F];
+ buf[12]:=HexDigitChrLo[digest[06] shr 4]; buf[13]:=HexDigitChrLo[digest[06] and $0F];
+ buf[14]:=HexDigitChrLo[digest[07] shr 4]; buf[15]:=HexDigitChrLo[digest[07] and $0F];
+ buf[16]:=HexDigitChrLo[digest[08] shr 4]; buf[17]:=HexDigitChrLo[digest[08] and $0F];
+ buf[18]:=HexDigitChrLo[digest[09] shr 4]; buf[19]:=HexDigitChrLo[digest[09] and $0F];
+ buf[20]:=HexDigitChrLo[digest[10] shr 4]; buf[21]:=HexDigitChrLo[digest[10] and $0F];
+ buf[22]:=HexDigitChrLo[digest[11] shr 4]; buf[23]:=HexDigitChrLo[digest[11] and $0F];
+ buf[24]:=HexDigitChrLo[digest[12] shr 4]; buf[25]:=HexDigitChrLo[digest[12] and $0F];
+ buf[26]:=HexDigitChrLo[digest[13] shr 4]; buf[27]:=HexDigitChrLo[digest[13] and $0F];
+ buf[28]:=HexDigitChrLo[digest[14] shr 4]; buf[29]:=HexDigitChrLo[digest[14] and $0F];
+ buf[30]:=HexDigitChrLo[digest[15] shr 4]; buf[31]:=HexDigitChrLo[digest[15] and $0F];
+ buf[32]:=#0;
+ result:=@buf;
+end;
+
+function GetMD5(const data;datalen:integer;var digest:TMD5Hash):TMD5Hash;
+begin
+ FillChar(digest,16,0);
+
+ mir_md5_hash(pmir_md5_byte_t(data),datalen,digest);
+
+ result:=digest;
+end;
+
+function Handshake(login, password:PAnsiChar):boolean;
+var
+ buf:array [0..32] of AnsiChar;
+ digest:TMD5Hash;
+ request:array [0..511] of AnsiChar;
+ res:pAnsiChar;
+ stat:mir_md5_state_t;
+begin
+ result:=false;
+ GetMD5Str(GetMD5(password,StrLen(password),digest),buf);
+ mir_md5_init(@stat);
+ mir_md5_append(@stat,@buf,32);
+ mir_md5_finish(@stat,digest);
+ StrCopy(request,defreq);
+ StrReplace(request,'<login>' ,login);
+ StrReplace(request,'<password>',buf);
+
+ res:=SendRequestCookies(request,false);
+// res:=SendRequest(request,REQUEST_GET);
+ if res<>nil then
+ begin
+ if uint_ptr(res)<$0FFF then
+ begin
+ ShowError(int_ptr(res));
+ end
+ else
+ begin
+ result:=true;
+ mFreeMem(res);
+ end;
+ end;
+end;
+
+function Encode(dst,src:pAnsiChar):PAnsiChar;
+begin
+ while src^<>#0 do
+ begin
+ if not (src^ in [' ','%','+','&','?',#128..#255]) then
+ dst^:=src^
+ else
+ begin
+ dst^:='%'; inc(dst);
+ dst^:=HexDigitChr[ord(src^) shr 4]; inc(dst);
+ dst^:=HexDigitChr[ord(src^) and $0F];
+ end;
+ inc(src);
+ inc(dst);
+ end;
+ dst^:=#0;
+ result:=dst;
+end;
+
+function SendMSRequest(request:pAnsiChar;doShowError:boolean):boolean;
+var
+ res:pAnsiChar;
+begin
+ result:=true;
+ res:=SendRequestCookies(request,true);
+ if (uint_ptr(res)<>200) and (uint_ptr(res)<$0FFF) then
+ begin
+//!! if int_ptr(res)=401 then
+ begin
+ Handshake(msh_login,msh_password);
+
+ res:=SendRequestCookies(request,true);
+ end;
+ if (uint_ptr(res)<$0FFF) then
+ if (uint_ptr(res)<>200) and doShowError then
+ begin
+ ShowError(int_ptr(res));
+ result:=false;
+ end;
+ end;
+end;
+
+function Scrobble(show:boolean):boolean;
+var
+ si:pSongInfoA;
+ buf:array [0..511] of AnsiChar;
+// bufw:array [0..511] of WideChar;
+ res,pc:PAnsiChar;
+ {img,}shId,epId:pAnsiChar;
+// imgw:pWideChar;
+ json:TJSONSERVICEINTERFACE;
+ jn,jroot:PJSONNODE;
+begin
+ result:=false;
+
+ si:=pointer(CallService(MS_WAT_RETURNGLOBAL,WAT_INF_UTF8,0));
+ Encode(buf,si.mfile);
+ pc:=Extract(buf,true);
+
+ // Episode search by filename
+ StrCopy(StrCopyE(buf,API_URL+'shows/search/file/?q='),pc);
+ mFreeMem(pc);
+ res:=SendRequest(buf,REQUEST_GET);
+ if uint_ptr(res)>$0FFF then
+ begin
+ CallService(MS_JSON_GETINTERFACE,wparam(@json),0);
+
+ jroot:=json.parse(res);
+
+ jn:=json.get(jroot,'show');
+ shId:=json.as_string(json.get(jn,'id'));
+
+ jn:=json.get(jn,'episodes');
+ epId:=json.name(json.at(jn,0));
+{
+kinopoiskId
+image
+ruTitle
+episodes:{:{id:
+}
+ end
+ else
+ begin
+ if show and (res<>nil) then
+ ShowError(int_ptr(res));
+ exit;
+ end;
+
+ // Show mark as "watching"
+ StrCopy(StrCopyE(StrCopyE(buf,API_URL+'profile/shows/'),shId),'/watching');
+ if SendMSRequest(buf,show) then
+ begin
+ // Episode check
+ StrCopy(StrCopyE(buf,API_URL+'profile/episodes/check/'),epId);
+ // StrCopy(request,API_URL+'profile/shows/');
+ if SendMSRequest(buf,show) then
+ begin
+{
+ if si.cover=nil then
+ begin
+ jn:=json.get(jroot,'show');
+ img:=json.as_string(json.get(jn,'image'));
+ si:=pointer(CallService(MS_WAT_RETURNGLOBAL,WAT_INF_UNICODE,0));
+ FastAnsiToWide(img,pSongInfoW(si)^.cover);
+ json.free(img);
+ end;
+}
+ //!! add option to show it??
+ if ServiceExists(MS_POPUP_SHOWMESSAGE)<>0 then
+ begin
+ json.free(shId);
+ json.free(epId);
+
+ jn:=json.get(jroot,'show');
+ shId:=json.as_string(json.get(jn,'title'));
+
+ jn:=json.get(jn,'episodes');
+ epId:=json.as_string(json.get(jn,'title'));
+
+ StrCopy(
+ StrCopyE(
+ StrCopyE(
+ StrCopyE(
+ StrCopyE(buf,'Show "'),
+ shId),
+ '"'#13#10'episode "'),
+ epId),
+ '" checked');
+ CallService(MS_POPUP_SHOWMESSAGE,TWPARAM(@buf),SM_NOTIFY);
+ end;
+ result:=true;
+ end;
+ end;
+ json.free(shId);
+ json.free(epId);
+
+ json.delete_(jroot);
+end;
+
diff --git a/plugins/Watrack/myshows/i_myshows_dlg.inc b/plugins/Watrack/myshows/i_myshows_dlg.inc
new file mode 100644
index 0000000000..13740d5a34
--- /dev/null
+++ b/plugins/Watrack/myshows/i_myshows_dlg.inc
@@ -0,0 +1,111 @@
+{}
+
+const
+ kinopoisk_info = 'http://www.kinopoisk.ru/level/1/film/';
+
+procedure ClearInfo(dlg:HWND);
+begin
+ SetDlgItemTextW(dlg,IDC_DATA_SERIES ,'');
+ SetDlgItemTextW(dlg,IDC_DATA_EPISODE,'');
+ SetDlgItemTextW(dlg,IDC_DATA_TAGS ,'');
+ SetDlgItemTextW(dlg,IDC_DATA_TAGS ,'');
+ SetDlgItemTextW(dlg,IDC_DATA_INFO ,'');
+end;
+
+function DlgProcOptions(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+const
+ inited:bool=false;
+var
+ tmp:longbool;
+// bmp,wnd:HWND;
+// buf:array [0..255] of AnsiChar;
+begin
+ result:=0;
+ case hMessage of
+ WM_INITDIALOG: begin
+ inited:=false;
+ TranslateDialogDefault(Dialog);
+
+ SetDlgItemTextA(Dialog,IDC_LOGIN,msh_login);
+ SetDlgItemTextA(Dialog,IDC_PASS ,msh_password);
+ SetDlgItemInt (Dialog,IDC_TRIES,msh_tries,false);
+// SetDlgItemInt (Dialog,IDC_TIME ,msh_timeout,false);
+// ClearInfo(Dialog);
+// EnableWindow(GetDlgItem(Dialog,IDC_KINOPOISK),false);
+
+ SendDlgItemMessage(Dialog,IDC_SCROBPOS,TBM_SETRANGE,0,MAKELONG(0,100));
+ SendDlgItemMessage(Dialog,IDC_SCROBPOS,TBM_SETPOS,1,msh_scrobpos);
+ inited:=true;
+ end;
+
+ WM_HSCROLL: begin
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+
+ WM_COMMAND: begin
+ if inited then
+ begin
+ (*
+ case Loword(wParam) of
+ IDC_KINOPOISK: begin
+ StrCopy(StrCopyE(buf,kinopoisk_info),MSData.kinopoisk_id);
+ CallService(MS_UTILS_OPENURL,TWPARAM(True),TLPARAM(@buf));
+ result:=1;
+ exit;
+ end;
+ end;
+ *)
+ case wParam shr 16 of
+ BN_CLICKED: begin
+ (*
+ case LoWord(wParam) of
+ IDC_INFO_SERIES: begin
+ ClearInfo(Dialog);
+ ClearData;
+
+ SetDlgItemTextW(Dialog,IDC_DATA_SERIES ,MSData.series);
+ SetDlgItemTextW(Dialog,IDC_DATA_EPISODE,MSData.episode);
+ // SetDlgItemTextW(Dialog,IDC_DATA_TAGS ,data.genre);
+ SetDlgItemTextW(Dialog,IDC_DATA_INFO ,MSData.info);
+
+ bmp:=LoadImageURL(MSData.image,80);
+ if bmp<>0 then
+ DeleteObject(SendDlgItemMessage(Dialog,IDC_DATA_PIC,STM_SETIMAGE,IMAGE_BITMAP,bmp));
+
+ EnableWindow(GetDligItem(Dialog,IDC_KINOPOISK),true);
+ end;
+ *)
+ end;
+
+ EN_CHANGE: begin
+ case loword(wParam) of
+ IDC_LOGIN,IDC_PASS,IDC_TRIES{,IDC_TIME}:
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+ end;
+ end;
+ end;
+ end;
+
+ WM_NOTIFY: begin
+ case integer(PNMHdr(lParam)^.code) of
+ PSN_APPLY: begin
+ msh_scrobpos:=SendDlgItemMessage(Dialog,IDC_SCROBPOS,TBM_GETPOS,0,0);
+ msh_tries :=GetDlgItemInt(Dialog,IDC_TRIES,tmp,false);
+ mFreeMem(msh_login ); msh_login :=GetDlgText(Dialog,IDC_LOGIN,true);
+ mFreeMem(msh_password); msh_password:=GetDlgText(Dialog,IDC_PASS ,true);
+ {
+ mFreeMem(session_id);
+ mFreeMem(np_url);
+ mFreeMem(sub_url);
+ }
+ // msh_timeout:=GetDlgItemInt(Dialog,IDC_TIME ,tmp,false);
+
+ SaveOpt;
+ end;
+ end;
+ end;
+ else
+ {result:=}DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+end;
diff --git a/plugins/Watrack/myshows/i_myshows_opt.inc b/plugins/Watrack/myshows/i_myshows_opt.inc
new file mode 100644
index 0000000000..f3287aba05
--- /dev/null
+++ b/plugins/Watrack/myshows/i_myshows_opt.inc
@@ -0,0 +1,47 @@
+{}
+const
+ optLogin :pAnsiChar='myshows/login';
+ optPassword:pAnsiChar='myshows/password';
+ optTries :pAnsiChar='myshows/tries';
+// optTimeout :PAnsiChar='myshows/timeout';
+ optScrobPos:pAnsiChar='myshows/scrobpos';
+ optScrobble:pAnsiChar='myshows/scrobble';
+
+procedure SaveOpt;
+var
+ tmppass:array [0..255] of AnsiChar;
+begin
+ if msh_password<>nil then
+ begin
+ StrCopy(tmppass,msh_password);
+ CallService(MS_DB_CRYPT_ENCODESTRING,StrLen(tmppass)+1,LPARAM(@tmppass));
+ end;
+ DBWriteString(0,PluginShort,optPassword,tmppass);
+ DBWriteString(0,PluginShort,optLogin ,msh_login);
+ DBWriteByte (0,PluginShort,optTries ,msh_tries);
+ DBWriteByte (0,PluginShort,optScrobPos,msh_scrobpos);
+// DBWriteWord (0,PluginShort,optTries ,msh_timeout);
+ DBWriteByte (0,PluginShort,optScrobble,msh_on and 1);
+end;
+
+procedure LoadOpt;
+begin
+// msh_timeout :=DBReadWord(0,PluginShort,optTimeout ,0);
+ msh_scrobpos:=DBReadByte(0,PluginShort,optScrobPos,30);
+ msh_tries :=DBReadByte(0,PluginShort,optTries ,3);
+ msh_on :=DBReadByte(0,PluginShort,optScrobble,0);
+ mFreeMem(msh_login ); msh_login :=DBReadString(0,PluginShort,optLogin);
+ mFreeMem(msh_password); msh_password:=DBReadString(0,PluginShort,optPassword);
+ if msh_password<>nil then
+ CallService(MS_DB_CRYPT_DECODESTRING,StrLen(msh_password)+1,LPARAM(msh_password));
+ if (msh_login=nil) or (msh_password=nil) then
+ CallService(MS_POPUP_SHOWMESSAGEW,
+ WPARAM(TranslateW('Don''t forget to enter Login and Password to use MyShows service')),
+ SM_WARNING);
+end;
+
+procedure FreeOpt;
+begin
+ mFreeMem(msh_login);
+ mFreeMem(msh_password);
+end;
diff --git a/plugins/Watrack/myshows/myshows.ico b/plugins/Watrack/myshows/myshows.ico
new file mode 100644
index 0000000000..ab34e43a20
--- /dev/null
+++ b/plugins/Watrack/myshows/myshows.ico
Binary files differ
diff --git a/plugins/Watrack/myshows/myshows.pas b/plugins/Watrack/myshows/myshows.pas
new file mode 100644
index 0000000000..ce07ee0d68
--- /dev/null
+++ b/plugins/Watrack/myshows/myshows.pas
@@ -0,0 +1,333 @@
+unit myshows;
+{$include compilers.inc}
+interface
+{$Resource myshows.res}
+implementation
+
+uses windows, messages, commctrl,
+ common,
+ m_api,dbsettings,wrapper, mirutils,
+ wat_api,global;
+
+const
+ DefTimerValue = 10*60*1000; // 10 minutes
+const
+ opt_ModStatus:PAnsiChar = 'module/myshows';
+const
+ IcoMyShows:pAnsiChar = 'WATrack_myshows';
+type
+ tMyShowsData = record
+ series :PAnsiChar;
+ series_id :PAnsiChar;
+ kinopoisk_id:PAnsiChar;
+ episode :PAnsiChar;
+ episode_id :PAnsiChar;
+ info :PAnsiChar;
+ image :PAnsiChar;
+ end;
+var
+ msh_tries,
+// msh_timeout,
+ msh_scrobpos:integer;
+ sic:THANDLE;
+// slastinf:THANDLE;
+ slast:THANDLE;
+ MSData:tMyShowsData;
+const
+ msh_on :integer=0;
+ hMenuMyShows:HMENU = 0;
+ msh_login :pAnsiChar=nil;
+ msh_password:pAnsiChar=nil;
+ session_id :pAnsiChar=nil;
+ np_url :pAnsiChar=nil;
+ sub_url :pAnsiChar=nil;
+
+procedure ClearData;
+begin
+ mFreeMem(MSData.series);
+ mFreeMem(MSData.series_id);
+ mFreeMem(MSData.kinopoisk_id);
+ mFreeMem(MSData.episode);
+ mFreeMem(MSData.episode_id);
+ mFreeMem(MSData.info);
+ mFreeMem(MSData.image);
+ FillChar(MSData,SizeOf(MSData),0);
+end;
+
+function GetModStatus:integer;
+begin
+ result:=DBReadByte(0,PluginShort,opt_ModStatus,1);
+end;
+
+procedure SetModStatus(stat:integer);
+begin
+ DBWriteByte(0,PluginShort,opt_ModStatus,stat);
+end;
+
+{$i i_const.inc}
+{$i i_myshows_opt.inc}
+{$i i_myshows_api.inc}
+
+procedure ThScrobble(param:LPARAM); cdecl;
+var
+ count:integer;
+begin
+ count:=msh_tries;
+ repeat
+ dec(count);
+ if Scrobble(count<=0) then break;
+ until count<=0;
+end;
+
+const
+ hTimer:THANDLE=0;
+
+procedure TimerProc(wnd:HWND;uMsg:uint;idEvent:uint_ptr;dwTime:dword); stdcall;
+begin
+ if hTimer<>0 then
+ begin
+ KillTimer(0,hTimer);
+ hTimer:=0;
+ end;
+
+ if (msh_login <>nil) and (msh_login^ <>#0) and
+ (msh_password<>nil) and (msh_password^<>#0) then
+ CloseHandle(mir_forkthread(@ThScrobble,nil));
+end;
+
+function NewPlStatus(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ flag:integer;
+ mi:TCListMenuItem;
+ timervalue:integer;
+begin
+ result:=0;
+ case wParam of
+ WAT_EVENT_NEWTRACK: begin
+ if hTimer<>0 then
+ KillTimer(0,hTimer);
+ // need to use half of movie len if presents
+ if msh_on=0 then
+ begin
+ if pSongInfo(lParam).width>0 then // for video only
+ begin
+ if ServiceExists(MS_JSON_GETINTERFACE)<>0 then
+ begin
+ timervalue:=integer(pSongInfo(lParam).total)*10*msh_scrobpos; // 1000(msec) div 100(%)
+ if timervalue=0 then
+ timervalue:=DefTimerValue;
+ hTimer:=SetTimer(0,0,timervalue,@TimerProc);
+ end;
+ end;
+ end;
+ end;
+
+ WAT_EVENT_PLUGINSTATUS: begin
+ case lParam of
+ dsEnabled: begin
+ msh_on:=msh_on and not 2;
+ flag:=0;
+ end;
+ dsPermanent: begin
+ msh_on:=msh_on or 2;
+ if hTimer<>0 then
+ begin
+ KillTimer(0,hTimer);
+ hTimer:=0;
+ end;
+ flag:=CMIF_GRAYED;
+ end;
+ else // like 1
+ exit
+ end;
+ FillChar(mi,sizeof(mi),0);
+ mi.cbSize:=sizeof(mi);
+ mi.flags :=CMIM_FLAGS+flag;
+ CallService(MS_CLIST_MODIFYMENUITEM,hMenuMyShows,tlParam(@mi));
+ end;
+
+ WAT_EVENT_PLAYERSTATUS: begin
+ case Integer(loword(lParam)) of
+ WAT_PLS_NOMUSIC,WAT_PLS_NOTFOUND: begin
+ if hTimer<>0 then
+ begin
+ KillTimer(0,hTimer);
+ hTimer:=0;
+ end;
+ end;
+ end;
+ end;
+ end;
+end;
+
+{$i i_myshows_dlg.inc}
+
+function IconChanged(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ mi:TCListMenuItem;
+begin
+ result:=0;
+ FillChar(mi,SizeOf(mi),0);
+ mi.cbSize:=sizeof(mi);
+ mi.flags :=CMIM_ICON;
+ mi.hIcon :=CallService(MS_SKIN2_GETICON,0,tLParam(IcoMyShows));
+ CallService(MS_CLIST_MODIFYMENUITEM,hMenuMyShows,tlParam(@mi));
+end;
+
+(* kinopoisk link, cover, series?
+function SrvMyShowsInfo(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+//var
+// data:tMyShowsInfo;
+begin
+ result:=0;
+{
+ case wParam of
+ 0: result:=GetArtistInfo(data,lParam);
+ 1: result:=GetAlbumInfo (data,lParam);
+ 2: result:=GetTrackInfo (data,lParam);
+ else
+ result:=0;
+ end;
+}
+end;
+*)
+function SrvMyShows(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ mi:TCListMenuItem;
+begin
+ FillChar(mi,sizeof(mi),0);
+ mi.cbSize:=sizeof(mi);
+ mi.flags :=CMIM_NAME;
+ if odd(msh_on) then
+ begin
+ mi.szName.a:='Disable scrobbling';
+ msh_on:=msh_on and not 1;
+ end
+ else
+ begin
+ mi.szName.a:='Enable scrobbling';
+ msh_on:=msh_on or 1;
+ if hTimer<>0 then
+ begin
+ KillTimer(0,hTimer);
+ hTimer:=0;
+ end;
+ end;
+ CallService(MS_CLIST_MODIFYMENUITEM,hMenuMyShows,tlParam(@mi));
+ result:=ord(not odd(msh_on));
+end;
+
+procedure CreateMenus;
+var
+ mi:TCListMenuItem;
+ sid:TSKINICONDESC;
+begin
+ FillChar(sid,SizeOf(TSKINICONDESC),0);
+ sid.cbSize:=SizeOf(TSKINICONDESC);
+ sid.cx:=16;
+ sid.cy:=16;
+ sid.szSection.a:='WATrack';
+
+ sid.hDefaultIcon :=LoadImage(hInstance,'IDI_MYSHOWS',IMAGE_ICON,16,16,0);
+ sid.pszName :=IcoMyShows;
+ sid.szDescription.a:='MyShows';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+
+ FillChar(mi, sizeof(mi), 0);
+ mi.cbSize :=sizeof(mi);
+ mi.szPopupName.a:=PluginShort;
+
+ mi.hIcon :=CallService(MS_SKIN2_GETICON,0,tlParam(IcoMyShows));
+ mi.szName.a :='Disable scrobbling';
+ mi.pszService :=MS_WAT_MYSHOWS;
+ mi.popupPosition:=500050000;
+ hMenuMyShows:=Menu_AddMainMenuItem(@mi);
+end;
+
+// ------------ base interface functions -------------
+
+function AddOptionsPage(var tmpl:pAnsiChar;var proc:pointer;var name:PAnsiChar):integer;
+begin
+ tmpl:='MYSHOWS';
+ proc:=@DlgProcOptions;
+ name:='MyShows';
+ result:=0;
+end;
+
+var
+ plStatusHook:THANDLE;
+
+function InitProc(aGetStatus:boolean=false):integer;
+begin
+// slastinf:=CreateServiceFunction(MS_WAT_MYSHOWSINFO,@SrvMyShowsInfo);
+ if aGetStatus then
+ begin
+ if GetModStatus=0 then
+ begin
+ result:=0;
+ exit;
+ end;
+ end
+ else
+ begin
+ SetModStatus(1);
+ msh_on:=msh_on and not 4;
+ end;
+ result:=1;
+
+ LoadOpt;
+
+ slast:=CreateServiceFunction(MS_WAT_MYSHOWS,@SrvMyShows);
+ if hMenuMyShows=0 then
+ CreateMenus;
+ sic:=HookEvent(ME_SKIN2_ICONSCHANGED,@IconChanged);
+ if (msh_on and 4)=0 then
+ plStatusHook:=HookEvent(ME_WAT_NEWSTATUS,@NewPlStatus);
+end;
+
+procedure DeInitProc(aSetDisable:boolean);
+begin
+ if aSetDisable then
+ SetModStatus(0)
+ else
+;// DestroyServiceFunction(slastinf);
+
+ DestroyServiceFunction(slast);
+ UnhookEvent(plStatusHook);
+ UnhookEvent(sic);
+
+ if hTimer<>0 then
+ begin
+ KillTimer(0,hTimer);
+ hTimer:=0;
+ end;
+
+ FreeOpt;
+
+ mFreeMem(session_id);
+ mFreeMem(np_url);
+ mFreeMem(sub_url);
+
+ msh_on:=msh_on or 4;
+
+ mFreeMem(cookies); //!!
+end;
+
+var
+ mmyshows:twModule;
+
+procedure Init;
+begin
+ mmyshows.Next :=ModuleLink;
+ mmyshows.Init :=@InitProc;
+ mmyshows.DeInit :=@DeInitProc;
+ mmyshows.AddOption :=@AddOptionsPage;
+ mmyshows.ModuleName:='MyShows.ru';
+ ModuleLink :=@mmyshows;
+
+end;
+
+begin
+ Init;
+end.
diff --git a/plugins/Watrack/myshows/myshows.rc b/plugins/Watrack/myshows/myshows.rc
new file mode 100644
index 0000000000..adc05a23b7
--- /dev/null
+++ b/plugins/Watrack/myshows/myshows.rc
@@ -0,0 +1,41 @@
+#include "i_const.inc"
+
+LANGUAGE 0,0
+
+MYSHOWS DIALOGEX 0, 0, 304, 226, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ LTEXT "Login" , -1, 108, 2, 70, 16, SS_CENTERIMAGE
+ EDITTEXT IDC_LOGIN , 4, 4, 100, 12,
+ LTEXT "Password" , -1, 108, 18, 70, 16, SS_CENTERIMAGE
+ EDITTEXT IDC_PASS , 4, 20, 100, 12, ES_PASSWORD
+ LTEXT "Attempts" , -1, 40, 34, 64, 16, SS_CENTERIMAGE
+ EDITTEXT IDC_TRIES , 4, 36, 32, 12, ES_RIGHT | ES_NUMBER
+// LTEXT "Timeout, ms", -1, 40, 50, 64, 16, SS_CENTERIMAGE
+// EDITTEXT IDC_TIME , 4, 52, 32, 12, ES_RIGHT | ES_NUMBER
+
+ CONTROL "",IDC_SCROBPOS,"msctls_trackbar32", TBS_BOTTOM|TBS_NOTICKS|$100,120,45,94,11
+ CTEXT "Scrobble at",-1,120,35,94,11, SS_CENTERIMAGE
+/*
+ PUSHBUTTON "Get Series Info", IDC_INFO_SERIES, 4, 206, 72, 16
+
+ CONTROL "", IDC_DATA_PIC, "STATIC", SS_BITMAP | WS_BORDER, 220, 2, 80, 80
+
+ RTEXT "Show", -1 , 0, 86, 76, 16, SS_CENTERIMAGE
+ EDITTEXT IDC_DATA_SERIES , 80, 87, 220, 14, ES_READONLY | ES_AUTOHSCROLL
+ RTEXT "Episode",-1 , 0, 102, 76, 16, SS_CENTERIMAGE
+ EDITTEXT IDC_DATA_EPISODE , 80, 103, 220, 14, ES_READONLY | ES_AUTOHSCROLL
+ RTEXT "Genres", -1 , 0, 118, 76, 16, SS_CENTERIMAGE
+ EDITTEXT IDC_DATA_TAGS , 80, 119, 220, 14, ES_READONLY | ES_AUTOHSCROLL
+ RTEXT "Info" , -1 , 0, 134, 76, 16, SS_CENTERIMAGE
+ EDITTEXT IDC_DATA_INFO , 80, 135, 220, 75,
+ ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL | WS_VSCROLL
+
+ CONTROL "Kinopoisk info page", IDC_KINOPOISK, "Hyperlink",
+ WS_CHILD | WS_TABSTOP | WS_DISABLED | 0x1, 80, 212, 220, 10
+*/
+}
+
+IDI_MYSHOWS ICON "myshows.ico"
diff --git a/plugins/Watrack/myshows/myshows.res b/plugins/Watrack/myshows/myshows.res
new file mode 100644
index 0000000000..1b888d6762
--- /dev/null
+++ b/plugins/Watrack/myshows/myshows.res
Binary files differ
diff --git a/plugins/Watrack/player.ini b/plugins/Watrack/player.ini
new file mode 100644
index 0000000000..16572c983e
--- /dev/null
+++ b/plugins/Watrack/player.ini
@@ -0,0 +1,283 @@
+;[name]
+;text0= for stopped (empty) player?
+;class=
+;text=
+;class1=
+;text1=
+;file=
+;flags=
+;url=
+;notes=
+;
+;prefix=
+;postfix=
+
+[ALShow]
+class='ALShowShellWindow'
+class1='ALShowMainWindow'
+url='http://www.altools.net/'
+[ALSong]
+class='ALSongKernelWindow'
+url='http://www.altools.net/'
+
+[ArezMedia]
+text='ArezMedia v8.1'
+class1='Media Event Sink Window'
+text1='Media Event Sink Window'
+file='AREZMEDIA.EXE'
+url='http://www.arezsoft.co.uk/'
+
+[Ashampoo Media Player]
+test='Ashampoo Media Player+'
+class='AMPMainWindow class'
+class1='AMP MainOwnerWindow Class'
+url='http://www2.ashampoo.com/webcache/html/1/product_2_0014___USD.htm'
+
+[AudioPlayer]
+class='AudioPlayer'
+url='http://audioplayer.sourceforge.net/'
+
+[Billy]
+class='TAppBilly'
+url='http://sheepfriends.com/?page=billy'
+
+[BizzOn]
+class='BizzOn v2.x'
+flags=8
+url='http://bizzon.nm.ru/'
+
+[Core Media Player]
+class='TApplication'
+file='COREPLAYER.EXE'
+url='http://www.tcmp.org/'
+
+[Creative Media Source]
+class='CT_MEDIASOURCE'
+url='http://www.creative.com/'
+
+[Crystal Player]
+class='CrystalPlayerClass'
+class1='CrystalClassOwner'
+url='http://crystalplayer.com/'
+
+[Cyberlink PowerDVD]
+class='Class of CyberLink Universal Player'
+text='CommandWindow'
+url='http://www.cyberlink.com/'
+
+[Daum PotPlayer]
+class='PotPlayer'
+notes='"Daum PotPlayer" text for stopped player?'
+
+[dBpowerAMP]
+class='dBpowerAMP Audio Player'
+text='dBpowerAMP Audio Player'
+class1='dBpowerAMP Core'
+text1='dBpowerAMP Core'
+url='http://www.dbpoweramp.com/'
+
+[Evil Player]
+class='TApplication'
+text='Evil Player'
+;title: Evil Player - 00:00 / 00:00
+;class='TForm1'
+;file='EVIL_PLAYER.EXE'
+url='http://www.hakeem.gigahost.dk/'
+
+[FLVPlayer]
+text='SW3 Player Engine [ Debug Window ]'
+url='http://www.martijndevisser.com/'
+
+;Fucker Player
+[FPlayer]
+class='TForm1'
+class1='TApplication'
+file='FPLAYER 3.0.EXE'
+url='http://www.kuzbassproduction.narod.ru/'
+
+[Freebox player]
+class='ThunderRT6FormDC'
+text='Freebox Player'
+class1='ThunderRT6Main'
+text1='MP3 Freebox'
+url='http://www.freeboxjukebox.com/'
+
+[J.River Media Center]
+class='MJFrame'
+class1='J. River Display Window'
+url='http://www.jrmediacenter.com/'
+
+[HKDC]
+text='HKDC_HangWnd'
+file='HKDC.exe'
+
+[Helium Music Manager]
+class='THeliumMainForm'
+url='http://www.helium-music-manager.com/'
+
+[IPOP GOMPlayer]
+class='GomPlayer1.x'
+url='http://gomplayer.com/'
+
+[KMPlayer]
+class='TApplication'
+file='KMPLAYER.EXE'
+flags=8
+url='http://www.kmplayer.com'
+Notes='Winamp API used to get more info'
+
+[MPlayer]
+class='MPlayer - The Movie Player'
+url='http://www.mplayerhq.hu/'
+
+[Media Commander Express]
+class='TApplication'
+file='MCX.EXE'
+url='http://skynet.hut1.ru/'
+
+[Media Library Master]
+class='TMainForm'
+class1='TApplication'
+file='MLMASTER.EXE'
+url='http://www.grafmstudio.narod.ru/mlmaster.htm'
+
+[MoreAmp]
+class='wxWindowClassNR'
+file='MOREAMP.EXE'
+url='http://sourceforge.net/projects/moreamp/'
+
+[MusicCube One]
+class='TfMain'
+text='MusicCubeOne'
+prefix='MusicCubeOne - '
+url='http://www.rodi.dk/musiccubeone'
+
+[Musicmatch Jukebox]
+class='MMJB:MAINWND'
+postfix=' - Musicmatch Jukebox'
+url='http://wwwp.musicmatch.com/'
+
+[MusikCube]
+class='musikCubeWindow1.0'
+postfix=']'
+url='http://www.musikcube.com/'
+
+[PlayNow]
+class='TForm1'
+file='Play Now!.exe'
+url='http://www.playnow.nightmail.ru/'
+
+[Pluton]
+class='TApplication'
+file='PLUTON.EXE'
+url='http://pluton.oss.ru/'
+
+[QCD]
+file='QMPLAYER.EXE'
+;file='QCDPLAYER.EXE'
+text='PlayerCanvas'
+flags=8
+url='http://quinnware.com/'
+
+[Quicktime Player]
+class='QuickTimePlayerMain'
+url='http://www.quicktime.com/'
+
+[RadLight]
+class='TVideoForm'
+class1='TApplication'
+file='RadLight.exe'
+url='http://radlight.da.ru/'
+
+[Real Player]
+class='GeminiWindowClass'
+prefix='RealPlayer: '
+url='http://www.real.com/'
+
+[SAPS]
+class='ThunderRT6Main'
+text='SAPS'
+file='SAPS.EXE'
+url='http://www.troupware.com/'
+
+[SongBird]
+class='SongbirdMessageWindow'
+url='http://songbirdnest.com/'
+
+[Spider Player]
+class='TSpiderMainForm'
+text='Spider Audio Player'
+class1='TApplication'
+;text1=%artist% - %title% 'Spider Player 1.80'
+file='SPIDER.EXE'
+url='http://spider-player.com/'
+
+[Storm Player Exotic]
+class='TMainStormForm'
+class1='TApplication'
+text1='Storm Player Exotic 2.0'
+url='http://splayer.nm.ru/'
+
+[Suamp]
+class='Suamp'
+class1='TApplication' ; with title (if scrolling)
+file='suamp.exe'
+url='http://www.suamp.ro/'
+
+[TotalMedia Theatre 3]
+text='ArcSoft TotalMedia Theatre 3'
+class='TotalMedia2FDVDPlayerFrame'
+
+[Ultra player]
+class='UltraPlayerMainWindowClass88667'
+url='http://www.ultraplayer.com/'
+
+[ViPlay]
+class='ViPlay3'
+url='http://www.urusoft.net/'
+
+[VUPlayer]
+class='VUPlayerClass'
+url='http://www.vuplayer.com/'
+
+[WiFiRadio Player]
+class='WasabiIPC_WiFi Radio'
+url='http://pspmx.com/wifiradio/'
+
+[WMP]
+class='WMPlayerApp'
+class1='Media Player 2'
+flags=69632
+url='http://www.microsoft.com/windows/windowsmedia/players.aspx'
+
+[WinDVD]
+class='WinDVDClass'
+;text='Player'
+url='http://www.intervideo.com/'
+
+[WxMusik]
+class='wxWindowClassNR'
+;text='wxMusik 0.4.2'
+file='WXMUSIK.EXE'
+url='http://musik.berlios.de/'
+
+[XAMP]
+class='TfrmMp3'
+class1='TApplication'
+text1='XAMP'
+;file='MP3.EXE'
+url='http://www.darksoftware.narod.ru/'
+
+[XMPlay]
+class='XMPLAY-MAIN'
+flags=8
+url='http://www.un4seen.com/'
+
+;[ZINF]
+;url='http://www.zinf.org/'
+
+[Zoom]
+class='TApplication'
+file='ZPLAYER.EXE'
+postfix=' - Zoom Player'
+url='http://www.inmatrix.com/'
diff --git a/plugins/Watrack/players/mradio.ico b/plugins/Watrack/players/mradio.ico
new file mode 100644
index 0000000000..7993fce57b
--- /dev/null
+++ b/plugins/Watrack/players/mradio.ico
Binary files differ
diff --git a/plugins/Watrack/players/mradio.rc b/plugins/Watrack/players/mradio.rc
new file mode 100644
index 0000000000..ce858cbe82
--- /dev/null
+++ b/plugins/Watrack/players/mradio.rc
@@ -0,0 +1,3 @@
+LANGUAGE 0,0
+
+ICO_MRADIO ICON "mradio.ico"
diff --git a/plugins/Watrack/players/mradio.res b/plugins/Watrack/players/mradio.res
new file mode 100644
index 0000000000..52d06f147d
--- /dev/null
+++ b/plugins/Watrack/players/mradio.res
Binary files differ
diff --git a/plugins/Watrack/players/pl_1by1.pas b/plugins/Watrack/players/pl_1by1.pas
new file mode 100644
index 0000000000..630b825363
--- /dev/null
+++ b/plugins/Watrack/players/pl_1by1.pas
@@ -0,0 +1,84 @@
+{1by1 player}
+unit pl_1by1;
+{$include compilers.inc}
+
+interface
+
+implementation
+uses windows,common,wrapper,srv_player,wat_api;
+
+const
+ ObOClass = '1by1WndClass';
+ ObOTitle = '1by1 - The Directory Player';
+
+function Check(wnd:HWND;flags:integer):HWND;cdecl;
+begin
+ if wnd<>0 then
+ begin
+ result:=0;
+ exit;
+ end;
+ result:=FindWindow(ObOClass,NIL);
+end;
+
+{
+ need to set 'Elapsed time in title bar'
+ and 'Show : instead of ' as minute char'
+}
+function GetElapsedTime(wnd:HWND):integer;
+var
+ s,p:PAnsiChar;
+begin
+ result:=0;
+ s:=GetDlgText(wnd,true);
+ if s<>nil then
+ begin
+ if (s^>='0') and (s^<='9') then
+ begin
+ p:=StrScan(s,' ');
+ if p<>nil then
+ p^:=#0;
+ result:=TimeToInt(s)
+ end;
+ mFreeMem(s);
+ end;
+end;
+
+function GetInfo(var SongInfo:tSongInfo;flags:integer):integer;cdecl;
+begin
+ result:=0;
+ if (flags and WAT_OPT_CHANGES)<>0 then
+ SongInfo.time:=GetElapsedTime(SongInfo.plwnd);
+end;
+
+const
+ plRec:tPlayerCell=(
+ Desc :'1by1';
+ flags :WAT_OPT_HASURL;
+ Icon :0;
+ Init :nil;
+ DeInit :nil;
+ Check :@Check;
+ GetStatus:nil;
+ GetName :nil;
+ GetInfo :@GetInfo;
+ Command :nil;
+ URL :'http://www.mpesch3.de/';
+ Notes :'To get elapsed time, needs to set "Elapsed time in title bar" and '#13#10+
+ '"Show : instead of '#39' as minute char" in player settings "Display" tab.'
+);
+
+var
+ LocalPlayerLink:twPlayer;
+
+procedure InitLink;
+begin
+ LocalPlayerLink.Next:=PlayerLink;
+ LocalPlayerLink.This:=@plRec;
+ PlayerLink :=@LocalPlayerLink;
+end;
+
+initialization
+// ServicePlayer(WAT_ACT_REGISTER,dword(@plRec));
+ InitLink;
+end.
diff --git a/plugins/Watrack/players/pl_aimp.pas b/plugins/Watrack/players/pl_aimp.pas
new file mode 100644
index 0000000000..17c52bc5b6
--- /dev/null
+++ b/plugins/Watrack/players/pl_aimp.pas
@@ -0,0 +1,376 @@
+{AIMP player}
+unit pl_AIMP;
+{$include compilers.inc}
+
+interface
+
+implementation
+uses windows,messages,common,srv_player,wat_api,winampapi;
+
+const
+ WM_AIMP_COMMAND = WM_USER + $75;
+ WM_AIMP_GET_VERSION = 4;
+ WM_AIMP_STATUS_GET = 1;
+ WM_AIMP_STATUS_SET = 2;
+ WM_AIMP_CALLFUNC = 3;
+const
+ AIMP_STS_Player = 4;
+ AIMP_STS_VOLUME = 1;
+ AIMP_STS_POS = 31;
+const
+ AIMP_PLAY = 15;
+ AIMP_PAUSE = 16;
+ AIMP_STOP = 17;
+ AIMP_NEXT = 18;
+ AIMP_PREV = 19;
+
+const
+ AIMP2_RemoteClass:PAnsiChar = 'AIMP2_RemoteInfo';
+const
+ AIMP2_RemoteFileSize = 2048;
+
+type
+ PAIMP2FileInfo = ^TAIMP2FileInfo;
+ TAIMP2FileInfo = packed record
+ cbSizeOF :dword;
+ //
+ nActive :LONGBOOL;
+ nBitRate :dword;
+ nChannels :dword;
+ nDuration :dword;
+ nFileSize :Int64;
+ nRating :dword;
+ nSampleRate :dword;
+ nTrackID :dword;
+ //
+ nAlbumLen :dword;
+ nArtistLen :dword;
+ nDateLen :dword;
+ nFileNameLen:dword;
+ nGenreLen :dword;
+ nTitleLen :dword;
+ //
+ sAlbum :dword; // size of pointer for 32 bit system
+ sArtist :dword;
+ sDate :dword;
+ sFileName :dword;
+ sGenre :dword;
+ sTitle :dword;
+ end;
+
+function Check(wnd:HWND;flags:integer):HWND;cdecl;
+begin
+ if wnd<>0 then
+ begin
+ result:=0;
+ exit;
+ end;
+ result:=FindWindowA(AIMP2_RemoteClass,AIMP2_RemoteClass);
+end;
+
+function GetVersionText(ver:integer):pWideChar;
+begin
+ if (ver and $F00)<>0 then
+ begin
+ mGetMem(result,8*SizeOf(WideChar));
+ result[0]:=WideChar((ver div 1000)+ORD('0'));
+ ver:=ver mod 1000;
+ result[1]:='.';
+ result[2]:=WideChar((ver div 100)+ORD('0'));
+ ver:=ver mod 100;
+ result[3]:='.';
+ result[4]:=WideChar((ver div 10)+ORD('0'));
+ result[5]:='.';
+ result[6]:=WideChar((ver mod 10)+ORD('0'));
+ result[7]:=#0;
+ end
+ else
+ result:=nil;
+end;
+
+function GetVersion(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_AIMP_COMMAND,WM_AIMP_GET_VERSION,0);
+end;
+
+function GetStatus(wnd:HWND):integer; cdecl;
+begin
+ result:=SendMessage(wnd,WM_AIMP_COMMAND,WM_AIMP_STATUS_GET,AIMP_STS_Player);
+end;
+
+function GetVolume(wnd:HWND):cardinal;
+begin
+ result:=SendMessage(wnd,WM_AIMP_COMMAND,WM_AIMP_STATUS_GET,AIMP_STS_VOLUME);
+ result:=(result shl 16)+round((result shl 4)/100);
+end;
+
+procedure SetVolume(wnd:HWND;value:cardinal);
+begin
+ SendMessage(wnd,WM_AIMP_COMMAND,WM_AIMP_STATUS_SET,
+ (AIMP_STS_VOLUME shl 16)+((value*100) shr 4));
+end;
+
+function VolDn(wnd:HWND):integer;
+var
+ val:dword;
+begin
+ result:=GetVolume(wnd);
+ val:=loword(result);
+ if val>0 then
+ SetVolume(wnd,val-1);
+end;
+
+function VolUp(wnd:HWND):integer;
+var
+ val:dword;
+begin
+ result:=GetVolume(wnd);
+ val:=loword(result);
+ if val<16 then
+ SetVolume(wnd,val+1);
+end;
+
+function GetFileName(wnd:HWND;flags:integer):pWideChar;cdecl;
+var
+ FFile:THANDLE;
+ pStr:pointer;
+ s:integer;
+ p:PAnsiChar;
+ pw,pw1:pWideChar;
+begin
+ result:=nil;
+ s:=AIMP2_RemoteFileSize;
+ p:=AIMP2_RemoteClass;
+ FFile:=OpenFileMappingA(FILE_MAP_READ,True,p);
+ pStr:=MapViewOfFile(FFile,FILE_MAP_READ,0,0,s);
+ try
+ if pStr<>nil then
+ begin
+ with PAIMP2FileInfo(pStr)^ do
+ begin
+ StrDupW(result,
+ pWideChar(PAnsiChar(pStr)+SizeOf(TAIMP2FileInfo)+
+ (nAlbumLen+nArtistLen+nDateLen)*SizeOf(WideChar)),
+ nFileNameLen);
+ // Delete rest index (like "filename.cue:3")
+ pw :=StrRScanW(result,':');
+ if pw<>nil then
+ begin
+ pw1:=StrScanW (result,':');
+ if pw<>pw1 then
+ pw^:=#0;
+ end;
+ end;
+ end;
+ finally
+ UnmapViewOfFile(pStr);
+ CloseHandle(FFile);
+ end;
+end;
+
+procedure TranslateRadio(var SongInfo:tSongInfo);
+var
+ pc,pc1:pWideChar;
+begin
+{
+ artist - album - title (radio)
+}
+ with SongInfo do
+ begin
+ if (artist=nil) and (title<>nil) then
+ begin
+ // Radio title
+ if (StrEndW(title)-1)^=')' then
+ begin
+ pc:=StrRScanW(title,'(');
+ if (pc<>nil) and (pc>title) and ((pc-1)^=' ') then
+ begin
+ if comment=nil then
+ begin
+ StrDupW(comment,pc+1);
+ (StrEndW(comment)-1)^:=#0;
+ end;
+ (pc-1)^:=#0;
+ end;
+ end;
+ // artist - title
+ pc:=StrPosW(title,' - ');
+ if pc<>nil then
+ begin
+ if artist=nil then
+ begin
+ pc^:=#0;
+ inc(pc,3);
+ StrDupW(artist,title);
+ end;
+ // artist - album - title
+ pc1:=StrPosW(pc,' - ');
+ if pc1<>nil then
+ begin
+ if album=nil then
+ begin
+ pc1^:=#0;
+ StrDupW(album,pc);
+ pc:=pc1+3;
+ end;
+ end;
+ pc1:=title;
+ StrDupW(title,pc);
+ mFreeMem(pc1);
+ end;
+ end;
+ end;
+end;
+
+function GetInfo(var SongInfo:tSongInfo;flags:integer):integer;cdecl;
+var
+ FFile:THANDLE;
+ s:integer;
+ p:PAnsiChar;
+ pStr:PAIMP2FileInfo;
+begin
+ result:=0;
+ if (flags and WAT_OPT_PLAYERDATA)<>0 then
+ begin
+ if SongInfo.plyver=0 then
+ begin
+ SongInfo.plyver:=GetVersion (SongInfo.plwnd);
+ SongInfo.txtver:=GetVersionText(SongInfo.plyver);
+ end;
+ if SongInfo.winampwnd=0 then
+ SongInfo.winampwnd:=WinampFindWindow(SongInfo.plwnd);
+ exit;
+ end;
+
+ if SongInfo.winampwnd<>0 then
+ WinampGetInfo(int_ptr(@SongInfo),flags);
+
+ if (flags and WAT_OPT_CHANGES)=0 then
+ begin
+ s:=AIMP2_RemoteFileSize;
+ p:=AIMP2_RemoteClass;
+ FFile:=OpenFileMappingA(FILE_MAP_READ,True,p);
+ pStr:=MapViewOfFile(FFile,FILE_MAP_READ,0,0,s);
+ try
+ if pStr<>nil then
+ begin
+ with SongInfo do
+ begin
+ with pStr^ do
+ begin
+ if channels=0 then channels:=nChannels;
+ if kbps =0 then kbps :=nBitRate div 1000;
+ if khz =0 then khz :=nSampleRate div 1000;
+ if total =0 then total :=nduration;
+ if fsize =0 then fsize :=nFileSize;
+ if track =0 then track :=nTrackID;
+
+ with PAIMP2FileInfo(pStr)^ do
+ begin
+ if (artist=nil) and (nArtistLen>0) then
+ begin
+ StrDupW(artist,
+ pWideChar(PAnsiChar(pStr)+SizeOf(TAIMP2FileInfo))+
+ nAlbumLen,nArtistLen);
+ end;
+ if (album=nil) and (nAlbumLen>0) then
+ begin
+ StrDupW(album,
+ pWideChar(PAnsiChar(pStr)+SizeOf(TAIMP2FileInfo)),
+ nAlbumLen);
+ end;
+ if (title=nil) and (nTitleLen>0) then
+ begin
+ StrDupW(title,
+ pWideChar(PAnsiChar(pStr)+SizeOf(TAIMP2FileInfo))+
+ nAlbumLen+nArtistLen+nDateLen+nFileNameLen+nGenreLen,
+ nTitleLen);
+ end;
+ if (year=nil) and (nDateLen>0) then
+ begin
+ StrDupW(year,
+ pWideChar(PAnsiChar(pStr)+SizeOf(TAIMP2FileInfo))+
+ nAlbumLen+nArtistLen,
+ nDateLen);
+ end;
+ if (genre=nil) and (nGenreLen>0) then
+ begin
+ StrDupW(genre,
+ pWideChar(PAnsiChar(pStr)+SizeOf(TAIMP2FileInfo))+
+ nAlbumLen+nArtistLen+nDateLen+nFileNameLen,
+ nGenreLen);
+ end;
+
+ if StrPosW(mfile,'://')<>nil then
+ TranslateRadio(SongInfo);
+ end;
+ end;
+ end;
+ end;
+ finally
+ UnmapViewOfFile(pStr);
+ CloseHandle(FFile);
+ end;
+ end
+ else // request AIMP changed data: volume
+ begin
+ SongInfo.time:=SendMessage(SongInfo.plwnd,WM_AIMP_COMMAND,WM_AIMP_STATUS_GET,AIMP_STS_POS);
+ SongInfo.volume:=GetVolume(SongInfo.plwnd);
+ end;
+end;
+
+function Command(wnd:HWND;cmd:integer;value:integer):integer;cdecl;
+var
+ WinampWindow:HWND;
+begin
+ WinampWindow:=WinampFindWindow(wnd);
+ if WinampWindow<>0 then
+ result:=WinampCommand(WinampWindow,cmd+(value shl 16))
+ else
+ begin
+ result:=0;
+ case cmd of
+ WAT_CTRL_PREV : SendMessage(wnd,WM_AIMP_COMMAND,WM_AIMP_CALLFUNC,AIMP_PREV);
+ WAT_CTRL_PLAY : SendMessage(wnd,WM_AIMP_COMMAND,WM_AIMP_CALLFUNC,AIMP_PLAY);
+ WAT_CTRL_PAUSE: SendMessage(wnd,WM_AIMP_COMMAND,WM_AIMP_CALLFUNC,AIMP_PAUSE);
+ WAT_CTRL_STOP : SendMessage(wnd,WM_AIMP_COMMAND,WM_AIMP_CALLFUNC,AIMP_STOP);
+ WAT_CTRL_NEXT : SendMessage(wnd,WM_AIMP_COMMAND,WM_AIMP_CALLFUNC,AIMP_NEXT);
+ WAT_CTRL_VOLDN: result:=VolDn(wnd);
+ WAT_CTRL_VOLUP: result:=VolUp(wnd);
+ WAT_CTRL_SEEK : begin
+ SendMessage(wnd,WM_AIMP_COMMAND,WM_AIMP_STATUS_SET,
+ (AIMP_STS_POS shl 16)+value);
+ end;
+ end;
+ end;
+end;
+
+const
+ plRec:tPlayerCell=(
+ Desc :'AIMP';
+ flags :WAT_OPT_APPCOMMAND or WAT_OPT_HASURL;
+ Icon :0;
+ Init :nil;
+ DeInit :nil;
+ Check :@Check;
+ GetStatus:@GetStatus;
+ GetName :@GetFileName;
+ GetInfo :@GetInfo;
+ Command :@Command;
+ URL :'http://www.aimp.ru/';
+ Notes :nil);
+
+var
+ LocalPlayerLink:twPlayer;
+
+procedure InitLink;
+begin
+ LocalPlayerLink.Next:=PlayerLink;
+ LocalPlayerLink.This:=@plRec;
+ PlayerLink :=@LocalPlayerLink;
+end;
+
+initialization
+// ServicePlayer(WAT_ACT_REGISTER,dword(@plRec));
+ InitLink;
+end.
diff --git a/plugins/Watrack/players/pl_apollo.pas b/plugins/Watrack/players/pl_apollo.pas
new file mode 100644
index 0000000000..360b23ab89
--- /dev/null
+++ b/plugins/Watrack/players/pl_apollo.pas
@@ -0,0 +1,263 @@
+{Apollo player}
+unit pl_Apollo;
+{$include compilers.inc}
+
+interface
+
+implementation
+uses windows,winampapi,messages,common,srv_player,wat_api;
+
+const
+ ApolloClass = 'Apollo - Main Window';
+
+const
+ WM_APOLLO_COMMAND = WM_USER+3;
+ APOLLO_GETVERSION = 0;
+ APOLLO_GETSTATUS = 1;
+ APOLLO_GETPLAYLISTPOSITION = 16;
+ APOLLO_GETCURRENTTRACKNUMBER = 17;
+ APOLLO_SETPLAYBACKPOSITION = 18;
+ APOLLO_GETPLAYBACKPOSITION = 19;
+ APOLLO_GETPLAYBACKCOUNTDOWN = 33;
+ APOLLO_GETCURRENTLYPLAYEDFILENAME = 24;
+ APOLLO_GETCURRENTLYPLAYEDTITLE = 25;
+ APOLLO_GETPLAYLISTENTRY = 26;
+ APOLLO_GETPLAYLISTTITLE = 27;
+// APOLLO_OPENURL = 4;
+ APOLLO_OPENFILE = 2;
+ APOLLO_PREVIOUSTRACK = 10;
+ APOLLO_STOP = 11;
+ APOLLO_PLAY = 12;
+ APOLLO_PAUSE = 13;
+ APOLLO_NEXTTRACK = 14;
+ APOLLO_SETVOLUME = 20;
+ APOLLO_GETVOLUME = 21;
+
+function Check(wnd:HWND;flags:integer):HWND;cdecl;
+begin
+ if wnd<>0 then
+ begin
+ result:=0;
+ exit;
+ end;
+ result:=FindWindow(ApolloClass,NIL)
+end;
+
+function GetVersion(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_APOLLO_COMMAND,APOLLO_GETVERSION,0);
+end;
+
+function GetVersionText(ver:integer):PWideChar;
+begin
+ mGetMem(result,5*SizeOf(WideChar));
+ IntToStr(result,ver);
+end;
+
+function GetStatus(wnd:HWND):integer; cdecl;
+begin
+ result:=SendMessage(wnd,WM_APOLLO_COMMAND,APOLLO_GETSTATUS,0);
+ if result>1 then
+ result:=2;
+end;
+
+function GetFileName(wnd:HWND;flags:integer):pWideChar;cdecl;
+var
+ tmpwnd:hwnd;
+ ps:array [0..255] of AnsiChar;
+begin
+ if GetStatus(wnd)<>WAT_MES_STOPPED then
+ begin
+ tmpwnd:=SendMessage(wnd,WM_APOLLO_COMMAND,APOLLO_GETCURRENTLYPLAYEDFILENAME,0);
+ ps[0]:=#0;
+ SendMessageA(tmpwnd,WM_GETTEXT,255,lparam(@ps));
+ if ps[0]<>#0 then
+ begin
+ mGetMem(result,(StrLen(ps)+1)*SizeOf(WideChar));
+ AnsiToWide(ps,result);
+ exit;
+ end;
+ end;
+ result:=nil;
+end;
+
+function GetWndText(wnd:HWND):pWideChar;
+var
+ tmpwnd:hwnd;
+ ps:array [0..255] of AnsiChar;
+begin
+ tmpwnd:=SendMessage(wnd,WM_APOLLO_COMMAND,APOLLO_GETCURRENTLYPLAYEDTITLE,0);
+ SendMessageA(tmpwnd,WM_GETTEXT,255,lparam(@ps));
+ mGetMem(result,(StrLen(ps)+1)*SizeOf(WideChar));
+ AnsiToWide(ps,result);
+end;
+
+function Play(wnd:HWND;fname:PWideChar=nil):integer;
+var
+ cds:COPYDATASTRUCT;
+begin
+ if (fname<>nil) and (fname^<>#0) then
+ begin
+ cds.dwData:=APOLLO_OPENFILE;
+ WideToAnsi(fname,PAnsiChar(cds.lpData));
+ cds.cbData:=StrLen(PAnsiChar(cds.lpData))+1;
+ SendMessage(wnd,WM_COPYDATA,0,lparam(@cds));
+ mFreeMem(cds.lpData);
+ end;
+ result:=SendMessage(wnd,WM_APOLLO_COMMAND,APOLLO_PLAY,0);
+end;
+
+function Pause(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_APOLLO_COMMAND,APOLLO_PAUSE,0);
+end;
+
+function Stop(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_APOLLO_COMMAND,APOLLO_STOP,0);
+end;
+
+function Next(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_APOLLO_COMMAND,APOLLO_NEXTTRACK,0);
+end;
+
+function Prev(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_APOLLO_COMMAND,APOLLO_PREVIOUSTRACK,0);
+end;
+
+function GetVolume(wnd:HWND):cardinal;
+begin
+ result:=SendMessage(wnd,WM_APOLLO_COMMAND,APOLLO_GETVOLUME,0);
+ result:=(result shl 16)+(result shr 12);
+end;
+
+procedure SetVolume(wnd:HWND;value:cardinal);
+begin
+ SendMessage(wnd,WM_APOLLO_COMMAND,APOLLO_SETVOLUME,value shl 12);
+end;
+
+function VolDn(wnd:HWND):integer;
+var
+ val:integer;
+begin
+ result:=GetVolume(wnd);
+ val:=loword(result);
+ if val>0 then
+ SetVolume(wnd,val-1);
+end;
+
+function VolUp(wnd:HWND):integer;
+var
+ val:integer;
+begin
+ result:=GetVolume(wnd);
+ val:=loword(result);
+ if val<16 then
+ SetVolume(wnd,val+1);
+end;
+
+function Seek(wnd:HWND;value:integer):integer;
+begin
+ result:=SendMessage(wnd,WM_APOLLO_COMMAND,APOLLO_GETPLAYBACKPOSITION,0);
+ if value>0 then
+ SendMessage(wnd,WM_APOLLO_COMMAND,APOLLO_SETPLAYBACKPOSITION,value);
+end;
+
+function GetRemoteTitle(wnd:HWND):pWideChar;
+var
+ tmpwnd:hwnd;
+ ps:array [0..255] of AnsiChar;
+ num:integer;
+begin
+ num :=SendMessage(wnd,WM_APOLLO_COMMAND,APOLLO_GETPLAYLISTPOSITION,0);
+ tmpwnd:=SendMessage(wnd,WM_APOLLO_COMMAND,APOLLO_GETPLAYLISTTITLE ,num);
+ SendMessageA(tmpwnd,WM_GETTEXT,255,lparam(@ps));
+ mGetMem(result,(StrLen(ps)+1)*SizeOf(WideChar));
+ AnsiToWide(ps,result);
+end;
+
+function GetInfo(var SongInfo:tSongInfo;flags:integer):integer;cdecl;
+begin
+ result:=0;
+
+ if (flags and WAT_OPT_PLAYERDATA)<>0 then
+ begin
+ if SongInfo.plyver=0 then
+ begin
+ SongInfo.plyver:=GetVersion (SongInfo.plwnd);
+ SongInfo.txtver:=GetVersionText(SongInfo.plyver);
+ end;
+ if SongInfo.winampwnd=0 then
+ SongInfo.winampwnd:=WinampFindWindow(SongInfo.plwnd);
+ exit;
+ end;
+
+ if SongInfo.winampwnd<>0 then
+ result:=WinampGetInfo(int_ptr(@SongInfo),flags);
+
+ if (flags and WAT_OPT_CHANGES)<>0 then
+ begin
+ with SongInfo do
+ begin
+ wndtext:=GetWndText(plwnd);
+ volume :=GetVolume (plwnd);
+ end
+ end
+ else
+ begin
+ with SongInfo do
+ begin
+ if (status<>WAT_MES_STOPPED) and
+ (mfile<>nil) and (StrPosW(mfile,'://')<>nil) and (album=nil) then
+ album:=GetRemoteTitle(plwnd);
+ end;
+ end;
+end;
+
+function Command(wnd:HWND;cmd:integer;value:int_ptr):integer;cdecl;
+begin
+ case cmd of
+ WAT_CTRL_PREV : result:=Prev(wnd);
+ WAT_CTRL_PLAY : result:=Play(wnd,pWideChar(value));
+ WAT_CTRL_PAUSE: result:=Pause(wnd);
+ WAT_CTRL_STOP : result:=Stop(wnd);
+ WAT_CTRL_NEXT : result:=Next(wnd);
+ WAT_CTRL_VOLDN: result:=VolDn(wnd);
+ WAT_CTRL_VOLUP: result:=VolUp(wnd);
+ WAT_CTRL_SEEK : result:=Seek(wnd,value);
+ else
+ result:=0;
+ end;
+end;
+
+const
+ plRec:tPlayerCell=(
+ Desc :'Apollo';
+ flags :WAT_OPT_HASURL;
+ Icon :0;
+ Init :nil;
+ DeInit :nil;
+ Check :@Check;
+ GetStatus:@GetStatus;
+ GetName :@GetFileName;
+ GetInfo :@GetInfo;
+ Command :@Command;
+ URL :'http://www.iki.fi/hy/apollo/';
+ Notes :nil);
+
+var
+ LocalPlayerLink:twPlayer;
+
+procedure InitLink;
+begin
+ LocalPlayerLink.Next:=PlayerLink;
+ LocalPlayerLink.This:=@plRec;
+ PlayerLink :=@LocalPlayerLink;
+end;
+
+initialization
+// ServicePlayer(WAT_ACT_REGISTER,dword(@plRec));
+ InitLink;
+end.
diff --git a/plugins/Watrack/players/pl_behold.pas b/plugins/Watrack/players/pl_behold.pas
new file mode 100644
index 0000000000..977a021034
--- /dev/null
+++ b/plugins/Watrack/players/pl_behold.pas
@@ -0,0 +1,175 @@
+{BeholderTV}
+unit pl_behold;
+{$include compilers.inc}
+
+interface
+
+implementation
+
+uses windows,messages,common,wrapper,srv_player,wat_api;
+
+const
+ WM_BHCMD = WM_USER+200;
+ WMBH_CHNLUP = WM_USER+203; // Ïåðåêëþ÷èòü íà ñëåäóþùèé êàíàë
+ WMBH_CHNLDOWN = WM_USER+204; // Ïåðåêëþ÷èòü íà ïðåäûäóùèé êàíàë
+ WMBH_VOLUMEUP = WM_USER+210; // Óâåëè÷èòü âûáðàííûé óðîâåíü
+ WMBH_VOLUMEDOWN = WM_USER+211; // Óìåíüøèòü âûáðàííûé óðîâåíü
+ WMBH_FREEZE = WM_USER+232; // Òðèããåð ñòîï-êàäðà
+ WMBH_SETVOLUME = WM_USER+280; // Óñòàíîâèòü óðîâåíü ãðîìêîñòè (LParam = 0..65535)
+ WMBH_GETVOLUME = WM_USER+281; // Ïîëó÷èòü òåêóùèé óðîâåíü ãðîìêîñòè (èñïîëüçîâàòü SendMessage, Result = 0..65535)
+ WMBH_GETVERSION = WM_USER+285; // Ïîëó÷èòü íîâåð âåðñèè ÏÎ (èñïîëüçîâàòü SendMessage)
+
+const
+ TitleWndClass = 'TApplication';
+ EXEName = 'BEHOLDTV.EXE';
+
+var
+ TitleWnd:HWND;
+
+function enumproc(wnd:HWND; lParam:LPARAM):bool; stdcall;
+var
+ buf:array [0..64] of AnsiChar;
+begin
+ result:=true;
+ if GetClassNameA(wnd,buf,63)<>0 then
+ begin
+ if StrCmp(buf,TitleWndClass)=0 then
+ begin
+ TitleWnd:=wnd;
+ result:=false;
+ end
+ end;
+end;
+
+function Check(wnd:HWND;flags:integer):HWND;cdecl;
+begin
+ if wnd<>0 then
+ begin
+ result:=0;
+ exit;
+ end;
+ result:=FindWindow('TMain','BeholdTV');
+ if result<>0 then
+ EnumThreadWindows(GetWindowThreadProcessId(result,nil),@enumproc,0);
+end;
+
+function GetVersion(wnd:HWND):integer;
+begin
+ result:=DWORD(SendMessage(wnd,WM_BHCMD,WMBH_GETVERSION,0));
+ result:=((result shr 16) shl 8)+LoWord(result);
+end;
+
+function GetVersionText(ver:integer):PWideChar; //!!
+begin
+ mGetMem(result,10*SizeOf(WideChar));
+ IntToStr(result+1,ver);
+ result[0]:=result[1];
+ result[1]:='.';
+end;
+
+function GetFileName(wnd:HWND;flags:integer):pWideChar;cdecl;
+begin
+ result:=nil;
+end;
+
+function Pause(wnd:HWND):integer;
+begin
+ result:=0;
+ PostMessage(wnd,WM_BHCMD,WMBH_FREEZE,0);
+end;
+
+function Next(wnd:HWND):integer;
+begin
+ result:=0;
+ PostMessage(wnd,WM_BHCMD,WMBH_CHNLUP,0);
+end;
+
+function Prev(wnd:HWND):integer;
+begin
+ result:=0;
+ PostMessage(wnd,WM_BHCMD,WMBH_CHNLDOWN,0);
+end;
+
+function GetVolume(wnd:HWND):cardinal;
+begin
+ result:=WORD(SendMessage(wnd,WM_BHCMD,WMBH_GETVOLUME,0));
+ result:=(result shl 16)+(result shr 12);
+end;
+
+procedure SetVolume(wnd:HWND;value:cardinal);
+begin
+ SendMessage(wnd,WM_BHCMD,WMBH_SETVOLUME,value shl 12);
+end;
+
+function VolDn(wnd:HWND):integer;
+begin
+ result:=WORD(SendMessage(wnd,WM_BHCMD,WMBH_VOLUMEDOWN,0));
+end;
+
+function VolUp(wnd:HWND):integer;
+begin
+ result:=WORD(SendMessage(wnd,WM_BHCMD,WMBH_VOLUMEUP,0));
+end;
+
+function GetInfo(var SongInfo:tSongInfo;flags:integer):integer;cdecl;
+begin
+ result:=0;
+ if (flags and WAT_OPT_PLAYERDATA)<>0 then
+ begin
+ if SongInfo.plyver=0 then
+ begin
+ SongInfo.plyver:=GetVersion (SongInfo.plwnd);
+ SongInfo.txtver:=GetVersionText(SongInfo.plyver);
+ end;
+ exit;
+ end;
+
+ if (flags and WAT_OPT_CHANGES)<>0 then
+ SongInfo.wndtext:=GetDlgText(TitleWnd);
+end;
+
+function Command(wnd:HWND;cmd:integer;value:integer):integer;cdecl;
+begin
+ case cmd of
+ WAT_CTRL_PREV : result:=Prev(wnd);
+// WAT_CTRL_PLAY : result:=Play(wnd,pWideChar(value));
+ WAT_CTRL_PAUSE: result:=Pause(wnd);
+// WAT_CTRL_STOP : result:=Stop(wnd);
+ WAT_CTRL_NEXT : result:=Next(wnd);
+ WAT_CTRL_VOLDN: result:=VolDn(wnd);
+ WAT_CTRL_VOLUP: result:=VolUp(wnd);
+// WAT_CTRL_SEEK : result:=Seek(wnd,value);
+ else
+ result:=0;
+ end;
+end;
+
+const
+ plRec:tPlayerCell=(
+ Desc :'BeholdTV';
+ flags :WAT_OPT_HASURL;
+ Icon :0;
+ Init :nil;
+ DeInit :nil;
+ Check :@Check;
+ GetStatus:nil;
+ GetName :@GetFileName;
+ GetInfo :@GetInfo;
+ Command :@Command;
+ URL :nil;
+ Notes :'Still experimental, no tested. Can work not properly');
+
+var
+ LocalPlayerLink:twPlayer;
+
+procedure InitLink;
+begin
+ LocalPlayerLink.Next:=PlayerLink;
+ LocalPlayerLink.This:=@plRec;
+ PlayerLink :=@LocalPlayerLink;
+end;
+
+initialization
+// ServicePlayer(WAT_ACT_REGISTER,dword(@plRec));
+ InitLink;
+end.
diff --git a/plugins/Watrack/players/pl_bs.pas b/plugins/Watrack/players/pl_bs.pas
new file mode 100644
index 0000000000..7fd2e9c5e5
--- /dev/null
+++ b/plugins/Watrack/players/pl_bs.pas
@@ -0,0 +1,252 @@
+{BSPlayer player}
+unit pl_BS;
+{$include compilers.inc}
+
+interface
+
+implementation
+uses windows,messages,common,srv_player,wat_api;
+
+const
+ HWND_MESSAGE = HWND(-3);
+const
+ BSPlayerClass = 'BSPlayer';
+const
+ WM_BSP_CMD = WM_USER+2;
+ BSP_GETVERSION = $10000;
+ BSP_GetMovLen = $10100;
+ BSP_GetMovPos = $10101;
+ BSP_GetStatus = $10102;
+ BSP_Seek = $10103;
+
+// BSP_LoadPlaylist = $1010C;
+ BSP_SetVol = $10104;
+ BSP_GetVol = $10105;
+ BSP_OpenFile = $10108;
+ BSP_GetFileName = $1010B;
+
+ BSP_VolUp = 1;
+ BSP_VolDown = 2;
+ BSP_Play = 20;
+ BSP_Pause = 21;
+ BSP_Stop = 22;
+ BSP_Prev = 25;
+ BSP_Next = 28;
+
+const
+ bspwnd:HWND = 0;
+
+function HiddenWindProc(wnd:HWnd; msg:UINT;wParam:WPARAM;lParam:LPARAM):integer; stdcall;
+begin
+ result:=DefWindowProc(wnd,msg,wParam,lParam);
+end;
+
+function Check(wnd:HWND;flags:integer):HWND;cdecl;
+begin
+ if wnd<>0 then
+ begin
+ result:=0;
+ exit;
+ end;
+ result:=FindWindow(BSPlayerClass,NIL);
+ if result=0 then
+ begin
+ if bspwnd<>0 then
+ begin
+ DestroyWindow(bspwnd);
+
+ bspwnd:=0;
+ end;
+ end
+ else if bspwnd=0 then
+ begin
+ bspwnd:=CreateWindowExW(0,'STATIC',nil,0,1,1,1,1,HWND_MESSAGE,0,hInstance,nil);
+ if bspwnd<>0 then
+ setwindowlongPtrW(bspwnd,GWL_WNDPROC,LONG_PTR(@HiddenWindProc));
+ end;
+end;
+
+function GetVersion(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_BSP_CMD,BSP_GETVERSION,0)
+end;
+
+function GetVersionText(ver:integer):pWideChar;
+begin
+ mGetMem(result,11*SizeOf(WideChar));
+ IntToHex(result,ver shr 24,2);
+ result[2]:='.';
+ IntToHex(result+3,(ver shr 16) and $FF,2);
+ result[5]:='.';
+ IntToHex(result+6,ver and $FFFF);
+end;
+
+function GetElapsedTime(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_BSP_CMD,BSP_GetMovPos,0) div 1000;
+end;
+
+function GetTotalTime(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_BSP_CMD,BSP_GetMovLen,0) div 1000;
+end;
+
+function GetStatus(wnd:HWND):integer; cdecl;
+begin
+ result:=SendMessage(wnd,WM_BSP_CMD,BSP_GetStatus,0);
+ if result=1 then result:=2
+ else if result=2 then result:=1;
+ if result>2 then result:=2;
+end;
+
+function GetFileName(wnd:HWND;flags:integer):pWideChar;
+var
+ cds:tcopyDataStruct;
+ buf:array [0..255] of AnsiChar;
+ adr:pointer;
+begin
+ adr:=@buf;
+ cds.dwData:=BSP_GetFileName;
+ cds.lpData:=@adr;
+ cds.cbData:=4;
+ SendMessage(wnd,WM_COPYDATA,bspwnd,lparam(@cds));
+
+ AnsiToWide(buf,result);
+end;
+
+function Play(wnd:HWND;fname:PWideChar=nil):integer;
+var
+ cds:COPYDATASTRUCT;
+begin
+ if (fname<>nil) and (fname^<>#0) then
+ begin
+ cds.dwData:=BSP_OpenFile;
+ WideToAnsi(fname,PAnsiChar(cds.lpData));
+ cds.cbData:=StrLen(PAnsiChar(cds.lpData))+1;
+ SendMessage(wnd,WM_COPYDATA,0{!!!},lparam(@cds));
+ mFreeMem(cds.lpData);
+ end;
+ result:=SendMessage(wnd,WM_BSP_CMD,BSP_Play,0);
+end;
+
+function Pause(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_BSP_CMD,BSP_Pause,0);
+end;
+
+function Stop(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_BSP_CMD,BSP_Stop,0);
+end;
+
+function Next(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_BSP_CMD,BSP_Next,0);
+end;
+
+function Prev(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_BSP_CMD,BSP_Prev,0);
+end;
+
+function VolDn(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_BSP_CMD,BSP_VolDown,0);
+end;
+
+function VolUp(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_BSP_CMD,BSP_VolUp,0);
+end;
+
+function GetVolume(wnd:HWND):cardinal;
+begin
+ result:=SendMessage(wnd,WM_BSP_CMD,BSP_GetVol,0);
+ result:=(result shl 16)+((result shl 4) div 25);
+end;
+
+procedure SetVolume(wnd:HWND;value:cardinal);
+begin
+ SendMessage(wnd,WM_BSP_CMD,BSP_SetVol,(value*25) shr 4);
+end;
+
+function Seek(wnd:HWND;value:integer):integer;
+begin
+ result:=SendMessage(wnd,WM_BSP_CMD,BSP_GetMovPos,0) div 1000;
+ SendMessage(wnd,WM_BSP_CMD,BSP_Seek,value*1000);
+end;
+
+function GetInfo(var SongInfo:tSongInfo;flags:integer):integer;cdecl;
+begin
+ result:=0;
+ if (flags and WAT_OPT_PLAYERDATA)<>0 then
+ begin
+ if SongInfo.plyver=0 then
+ begin
+ SongInfo.plyver:=GetVersion (SongInfo.plwnd);
+ SongInfo.txtver:=GetVersionText(SongInfo.plyver);
+ end;
+ exit;
+ end;
+
+ with SongInfo do
+ begin
+ if (flags and WAT_OPT_CHANGES)<>0 then
+ begin
+ volume:=GetVolume(plwnd);
+ if status<>WAT_MES_STOPPED then
+ time:=GetElapsedTime(plwnd);
+ end
+ else
+ begin
+ if total=0 then
+ total:=GetTotalTime(plwnd);
+ end;
+ end;
+end;
+
+function Command(wnd:HWND;cmd:integer;value:int_ptr):integer;cdecl;
+begin
+ case cmd of
+ WAT_CTRL_PREV : result:=Prev (wnd);
+ WAT_CTRL_PLAY : result:=Play (wnd,pWideChar(value));
+ WAT_CTRL_PAUSE: result:=Pause(wnd);
+ WAT_CTRL_STOP : result:=Stop (wnd);
+ WAT_CTRL_NEXT : result:=Next (wnd);
+ WAT_CTRL_VOLDN: result:=VolDn(wnd);
+ WAT_CTRL_VOLUP: result:=VolUp(wnd);
+ WAT_CTRL_SEEK : result:=Seek (wnd,value);
+ else
+ result:=0;
+ end;
+end;
+
+const
+ plRec:tPlayerCell=(
+ Desc :'BSPlayer';
+ flags :WAT_OPT_HASURL;
+ Icon :0;
+ Init :nil;
+ DeInit :nil;
+ Check :@Check;
+ GetStatus:@GetStatus;
+ GetName :@GetFileName;
+ GetInfo :@GetInfo;
+ Command :@Command;
+ URL :'http://www.bsplayer.org/';
+ Notes :nil);
+
+var
+ LocalPlayerLink:twPlayer;
+
+procedure InitLink;
+begin
+ LocalPlayerLink.Next:=PlayerLink;
+ LocalPlayerLink.This:=@plRec;
+ PlayerLink :=@LocalPlayerLink;
+end;
+
+initialization
+// ServicePlayer(WAT_ACT_REGISTER,dword(@plRec));
+ InitLink;
+end.
diff --git a/plugins/Watrack/players/pl_cowon.pas b/plugins/Watrack/players/pl_cowon.pas
new file mode 100644
index 0000000000..0977ea4b20
--- /dev/null
+++ b/plugins/Watrack/players/pl_cowon.pas
@@ -0,0 +1,392 @@
+{COWON JetAudio player}
+unit pl_cowon;
+{$include compilers.inc}
+
+interface
+
+implementation
+uses windows,winampapi,wrapper,messages,common,srv_player,wat_api;
+
+const
+ HWND_MESSAGE = HWND(-3);
+const
+ HOSTWND_CLASS = 'TLB_JETAUDIO';
+
+ CowonClass = 'COWON Jet-Audio MainWnd Class';
+ CowonTitle = 'Afx:400000:8:0:0:'; // for example 'Afx:400000:8:0:0:18c300fd'
+ ControlClass = 'COWON Jet-Audio Remocon Class';
+ ControlName = 'Jet-Audio Remote Control';
+ PluginClass = 'Streamzap.WMX';
+ PluginName = 'Jet-Audio Remote Plugin';
+const
+ MCI_STRING_OFFSET = 512;
+ MCI_MODE_NOT_READY = MCI_STRING_OFFSET + 12;
+ MCI_MODE_STOP = MCI_STRING_OFFSET + 13;
+ MCI_MODE_PLAY = MCI_STRING_OFFSET + 14;
+ MCI_MODE_RECORD = MCI_STRING_OFFSET + 15;
+ MCI_MODE_SEEK = MCI_STRING_OFFSET + 16;
+ MCI_MODE_PAUSE = MCI_STRING_OFFSET + 17;
+ MCI_MODE_OPEN = MCI_STRING_OFFSET + 18;
+
+ WM_REMOCON_GETSTATUS = WM_APP+740;
+ WM_REMOCON_SENDCOMMAND = WM_APP+741;
+
+ JRC_ID_STOP = 5102;
+ JRC_ID_PLAY = 5104; // Track Number (>=1). Use 0 for normal playback.
+ JRC_ID_PREV_TRACK = 5107;
+ JRC_ID_NEXT_TRACK = 5108;
+ JRC_ID_VOL_DOWN = 5134;
+ JRC_ID_VOL_UP = 5135;
+ JRC_ID_SET_VOLUME = 5180; // Volume Value (0 - 32)
+ JRC_ID_SEEK = 5192; // New position (second)
+ JRC_ID_RANDOMMODE = 5117;
+
+ GET_STATUS_STATUS = 1;
+ GET_STATUS_CUR_TRACK = 8;
+ GET_STATUS_CUR_TIME = 9;
+ GET_STATUS_MAX_TIME = 10;
+ GET_STATUS_TRACK_FILENAME = 11;
+ GET_STATUS_TRACK_TITLE = 12;
+ GET_STATUS_TRACK_ARTIST = 13;
+
+ GET_STATUS_VOLUME = 127;
+
+ JRC_COPYDATA_ID_ALBUMNAME = $1000;
+ JRC_COPYDATA_ID_GETVER = $1002;
+ JRC_COPYDATA_ID_TRACK_FILENAME = $3000;
+ JRC_COPYDATA_ID_TRACK_TITLE = $3001;
+ JRC_COPYDATA_ID_TRACK_ARTIST = $3002;
+
+ PLAY_NORMAL = 0;
+ PLAY_RANDOM = 1;
+
+ GET_STATUS_JETAUDIO_VER1 = 995;
+ GET_STATUS_JETAUDIO_VER2 = 996;
+ GET_STATUS_JETAUDIO_VER3 = 997;
+
+const
+ titlewnd:HWND = 0;
+ hostwnd :HWND = 0;
+ tmpstr :pWideChar=nil;
+
+function HiddenWindProc(wnd:HWnd; msg:UINT;wParam:WPARAM;lParam:LPARAM):integer; stdcall;
+var
+ cds:PCOPYDATASTRUCT;
+begin
+ case msg of
+ WM_COPYDATA: begin
+ cds:=PCOPYDATASTRUCT(lParam);
+ case cds^.dwData of
+ JRC_COPYDATA_ID_TRACK_FILENAME,
+ JRC_COPYDATA_ID_TRACK_TITLE ,
+ JRC_COPYDATA_ID_TRACK_ARTIST : AnsiToWide(cds^.lpData,tmpstr);
+ end;
+ result:=1;
+ exit;
+ end;
+ end;
+ result:=DefWindowProc(wnd,msg,wParam,lParam);
+end;
+
+function Init:integer;cdecl;
+begin
+ hostwnd:=CreateWindowExW(0,'STATIC',nil,0,1,1,1,1,HWND_MESSAGE,0,hInstance,nil);
+ if hostwnd<>0 then
+ SetWindowLongPtrW(hostwnd,GWL_WNDPROC,LONG_PTR(@HiddenWindProc));
+ result:=hostwnd;
+end;
+
+function DeInit:integer;cdecl;
+begin
+ result:=0;
+ if hostwnd<>0 then
+ begin
+ DestroyWindow(hostwnd);
+ hostwnd:=0;
+ end;
+end;
+
+function chwnd(awnd:hwnd;param:pdword):boolean; stdcall;
+var
+ s:array [0..255] of AnsiChar;
+begin
+ result:=true;
+ if GetClassNameA(awnd,s,255)>0 then
+ begin
+ if StrCmp(s,CowonTitle,Length(CowonTitle))=0 then
+ begin
+ param^:=awnd;
+ result:=false;
+ end;
+ end;
+end;
+
+function Check(wnd:HWND;flags:integer):HWND;cdecl;
+begin
+ if wnd<>0 then
+ begin
+ result:=0;
+ exit;
+ end;
+ result:=FindWindow(ControlClass,ControlName);
+{
+ if result=0 then
+ result:=FindWindow(PluginClass,PluginName);
+}
+ if (result<>0) {and (result<>wnd)} then
+ if EnumWindows(@chwnd,int_ptr(@titlewnd)) then
+ titlewnd:=0;
+end;
+
+function GetWndText:pWideChar;
+var
+ p:pWideChar;
+begin
+ result:=nil;
+ if titlewnd<>0 then
+ begin
+ result:=GetDlgText(titlewnd);
+ if result<>nil then
+ begin
+ if StrScanW(result,'[')<>nil then
+ begin
+ p:=StrScanW(result,']');
+ if p<>nil then
+ begin
+ StrCopyW(result,p+1);
+ end;
+ end;
+ end;
+ end;
+end;
+
+function GetVersion(wnd:HWND):integer;
+begin
+ result:= SendMessage(wnd,WM_REMOCON_GETSTATUS,0,GET_STATUS_JETAUDIO_VER1);
+ result:=(result shl 8)+SendMessage(wnd,WM_REMOCON_GETSTATUS,0,GET_STATUS_JETAUDIO_VER2);
+ result:=(result shl 8)+SendMessage(wnd,WM_REMOCON_GETSTATUS,0,GET_STATUS_JETAUDIO_VER3);
+end;
+
+function GetVersionText(ver:integer):PWideChar;
+{var
+ ver:integer;
+begin
+ ver:=GetVersion;
+ mGetMem(result,11*SizeOf(WideChar));
+ IntToHex(result,ver shr 16,2);
+ result[2]:='.';
+ IntToHex(result+3,(ver shr 8) and $FF,2);
+ result[5]:='.';
+ IntToHex(result+6,ver and $FF);
+}
+var
+ s:array [0..31] of WideChar;
+ i:integer;
+begin
+ i:=StrLenW(IntToStr(s,ver shr 16));
+ s[i]:='.';
+ i:=integer(StrLenW(IntToStr(pWideChar(@s[i+1]),(ver shr 8) and $FF)))+i+1;
+ s[i]:='.';
+ IntToStr(pWideChar(@s[i+1]),ver and $FF);
+ StrDupW(result,PWideChar(@s));
+end;
+
+function GetStatus(wnd:HWND):integer; cdecl;
+begin
+ result:=SendMessage(wnd,WM_REMOCON_GETSTATUS,0,GET_STATUS_STATUS);
+ case result of
+ MCI_MODE_STOP : result:=WAT_MES_STOPPED;
+ MCI_MODE_PAUSE: result:=WAT_MES_PAUSED;
+ MCI_MODE_PLAY : result:=WAT_MES_PLAYING;
+ else
+ result:=WAT_MES_UNKNOWN;
+ end;
+end;
+
+function GetFileName(wnd:HWND;flags:integer):pWideChar;cdecl;
+begin
+ SendMessage(wnd,WM_REMOCON_GETSTATUS,hostwnd,GET_STATUS_TRACK_FILENAME);
+ result:=tmpstr;
+end;
+
+function GetArtist(wnd:HWND):pWideChar;
+begin
+ SendMessage(wnd,WM_REMOCON_GETSTATUS,hostwnd,GET_STATUS_TRACK_ARTIST);
+ result:=tmpstr;
+end;
+
+function GetTitle(wnd:HWND):pWideChar;
+begin
+ SendMessage(wnd,WM_REMOCON_GETSTATUS,hostwnd,GET_STATUS_TRACK_TITLE);
+ result:=tmpstr;
+end;
+
+function GetTrack(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_REMOCON_GETSTATUS,0,GET_STATUS_CUR_TRACK);
+end;
+
+function GetTotalTime(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_REMOCON_GETSTATUS,0,GET_STATUS_MAX_TIME) div 1000;
+end;
+
+function GetElapsedTime(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_REMOCON_GETSTATUS,0,GET_STATUS_CUR_TIME) div 1000;
+end;
+
+function Play(wnd:HWND;fname:PWideChar=nil):integer;
+begin
+ result:=SendMessage(wnd,WM_REMOCON_SENDCOMMAND,0,JRC_ID_PLAY);
+end;
+
+function Pause(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_REMOCON_SENDCOMMAND,0,JRC_ID_PLAY);
+end;
+
+function Stop(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_REMOCON_SENDCOMMAND,0,JRC_ID_STOP);
+end;
+
+function Next(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_REMOCON_SENDCOMMAND,0,JRC_ID_NEXT_TRACK);
+end;
+
+function Prev(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_REMOCON_SENDCOMMAND,0,JRC_ID_PREV_TRACK);
+end;
+
+function VolDn(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_REMOCON_SENDCOMMAND,0,JRC_ID_VOL_DOWN);
+end;
+
+function VolUp(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_REMOCON_SENDCOMMAND,0,JRC_ID_VOL_UP);
+end;
+
+function GetVolume(wnd:HWND):cardinal;
+begin
+ result:=SendMessage(wnd,WM_REMOCON_GETSTATUS,0,GET_STATUS_VOLUME);
+ result:=(result shl 16)+(result shr 1);
+end;
+
+procedure SetVolume(wnd:HWND;value:cardinal);
+begin
+ SendMessage(wnd,WM_REMOCON_SENDCOMMAND,0,JRC_ID_SET_VOLUME+(value shl 17));
+end;
+
+function Seek(wnd:HWND;value:integer):integer;
+begin
+ result:=SendMessage(wnd,WM_REMOCON_SENDCOMMAND,0,JRC_ID_SEEK+(value shl 16));
+end;
+{
+function Shuffle(setOn:integer):integer;
+begin
+ result:=SendMessage(wnd,WM_REMOCON_GETSTATUS,0,GETSTATUS_COMPONENT_RANDOMMODE);
+ SendMessage(wnd,WM_REMOCON_SENDCOMMAND,JRC_ID_RANDOMMODE+(SetOn shl 16));
+end;
+}
+
+function GetInfo(var SongInfo:tSongInfo;flags:integer):integer;cdecl;
+var
+ p:pWideChar;
+begin
+ result:=0;
+// result:=CallService(MS_WAT_WINAMPINFO,integer(@SongInfo),flags);
+// result:=WinampGetInfo(integer(@SongInfo),flags);
+ if (flags and WAT_OPT_PLAYERDATA)<>0 then
+ begin
+ if SongInfo.plyver=0 then
+ begin
+ SongInfo.plyver:=GetVersion (SongInfo.plwnd);
+ SongInfo.txtver:=GetVersionText(SongInfo.plyver);
+ end;
+ exit;
+ end;
+
+ with SongInfo do
+ begin
+ if (flags and WAT_OPT_CHANGES)<>0 then
+ begin
+ wndtext:=GetWndText;
+ volume :=GetVolume(plwnd);
+ if status<>WAT_MES_STOPPED then
+// if time=0 then
+ time:=GetElapsedTime(plwnd);
+ end
+ else
+ begin
+
+ if artist=NIL then
+ begin
+ artist:=GetArtist(plwnd);
+ if artist^=#0 then
+ mFreeMem(artist);
+ end;
+ if title =NIL then
+ begin
+ title:=GetTitle(plwnd);
+ p:=ExtractW(mfile,true);
+ if (title^=#0) or (StrCmpW(title,p)=0) then
+ mFreeMem(title);
+ mFreeMem(p);
+ end;
+
+ if total=0 then
+ total:=GetTotalTime(plwnd);
+ end;
+ end;
+end;
+
+function Command(wnd:HWND;cmd:integer;value:int_ptr):integer;cdecl;
+begin
+ case cmd of
+ WAT_CTRL_PREV : result:=Prev (wnd);
+ WAT_CTRL_PLAY : result:=Play (wnd,pWideChar(value));
+ WAT_CTRL_PAUSE: result:=Pause(wnd);
+ WAT_CTRL_STOP : result:=Stop (wnd);
+ WAT_CTRL_NEXT : result:=Next (wnd);
+ WAT_CTRL_VOLDN: result:=VolDn(wnd);
+ WAT_CTRL_VOLUP: result:=VolUp(wnd);
+ WAT_CTRL_SEEK : result:=Seek (wnd,value);
+ else
+ result:=0;
+ end;
+end;
+
+const
+ plRec:tPlayerCell=(
+ Desc :'Cowon JetAudio';
+ flags :WAT_OPT_HASURL;
+ Icon :0;
+ Init :@Init;
+ DeInit :@DeInit;
+ Check :@Check;
+ GetStatus:@GetStatus;
+ GetName :@GetFileName;
+ GetInfo :@GetInfo;
+ Command :@Command;
+ URL :'http://www.jetaudio.com/';
+ Notes :nil);
+
+var
+ LocalPlayerLink:twPlayer;
+
+procedure InitLink;
+begin
+ LocalPlayerLink.Next:=PlayerLink;
+ LocalPlayerLink.This:=@plRec;
+ PlayerLink :=@LocalPlayerLink;
+end;
+
+initialization
+// ServicePlayer(WAT_ACT_REGISTER,dword(@plRec));
+ InitLink;
+end.
diff --git a/plugins/Watrack/players/pl_foobar.pas b/plugins/Watrack/players/pl_foobar.pas
new file mode 100644
index 0000000000..f861db6327
--- /dev/null
+++ b/plugins/Watrack/players/pl_foobar.pas
@@ -0,0 +1,534 @@
+{Foobar2000 player}
+unit pl_Foobar;
+{$include compilers.inc}
+
+interface
+
+implementation
+uses {$IFDEF KOL_MCK}err,{$ENDIF}
+ windows,common,syswin,wrapper,srv_player,messages,wat_api,winampapi
+ {$IFDEF DELPHI_7_UP}
+ ,variants
+ {$ENDIF}
+ {$IFDEF KOL_MCK}
+ ,kolcomobj
+ {$ELSE}
+ ,ComObj
+ {$ENDIF}
+;
+
+const
+ COMName:PAnsiChar = 'Foobar2000.Application.0.7';
+const
+ dummywnd = 'uninteresting';
+const
+ FooExe = 'FOOBAR2000.EXE';
+ FooPrefix = 'foobar2000 v';
+const
+ FooBarClassExt = '{97E27FAA-C0B3-4b8e-A693-ED7881E99FC1}';
+ FooBarClassNew = '{DA7CD0DE-1602-45e6-89A1-C2CA151E008E}';
+ FooBarClassAdd = '{E7076D1C-A7BF-4f39-B771-BCBE88F2A2A8}';
+(*
+ class1='{B73733CA-9B0A-4f53-93FA-AC95D4FF2166}';
+ text1='Cthulhu fhtagn!';
+
+ '{53229DFC-A273-45cd-A3A4-161FA9FC6414}';
+ '{641C2469-355C-4d6f-9663-E714382DA462}';
+*)
+var
+ WinampWindow:HWND;
+
+function proc(awnd:hwnd;param:pdword):boolean; stdcall;
+var
+ s:array [0..255] of AnsiChar;
+begin
+ result:=true;
+ if (awnd<>param^) and (GetClassNameA(awnd,s,255)>0) then
+ begin
+ s[Length(FooBarClassNew)]:=#0;
+ if (StrCmp(s,FooBarClassExt)=0) or
+ (StrCmp(s,FooBarClassNew)=0) or
+ (StrCmp(s,FooBarClassAdd)=0) then
+ begin
+ GetWindowTextA(awnd,s,255);
+ if StrCmp(s,dummywnd)<>0 then
+ param^:=awnd;
+// if WinampWindow<>0 then
+// result:=false;
+ end
+
+ else if (WinampWindow=0) and (StrCmp(s,WinampClass)=0) then
+ begin
+ WinampWindow:=awnd;
+ end;
+
+ end;
+end;
+
+function Check(wnd:HWND;flags:integer):HWND;cdecl;
+var
+ tmp,EXEName:PAnsiChar;
+ lwnd:HWND;
+ ltmp:bool;
+begin
+ if wnd<>0 then
+ begin
+ result:=0;
+ exit;
+ end;
+ lwnd:=0;
+ repeat
+ lwnd:=FindWindowEx(0,lwnd,nil,dummywnd);
+ if lwnd=0 then
+ break;
+ tmp:=Extract(GetEXEByWnd(lwnd,EXEName),true);
+ mFreeMem(EXEName);
+ ltmp:=lstrcmpia(tmp,FooExe)=0;
+ mFreeMem(tmp);
+ if ltmp then
+ begin
+ WinampWindow:=0;
+ EnumThreadWindows(GetWindowThreadProcessId(lwnd,nil),@proc,int_ptr(@lwnd));
+ break;
+ end;
+ until false;
+ result:=lwnd;
+end;
+
+function GetYear(const v:variant):PWideChar;
+begin
+ try
+ StrDupW(result,PWideChar(WideString(v.Playback.FormatTitle('[%year%]'))));
+ except
+ result:=nil;
+ end;
+end;
+
+function GetArtist(const v:variant):PWideChar;
+begin
+ try
+ StrDupW(result,PWideChar(WideString(v.Playback.FormatTitle('[%artist%]'))));
+ except
+ result:=nil;
+ end;
+end;
+
+function GetTitle(const v:variant):PWideChar;
+begin
+ try
+ StrDupW(result,PWideChar(WideString(v.Playback.FormatTitle('[%title%]'))));
+ except
+ result:=nil;
+ end;
+end;
+
+function GetAlbum(const v:variant):PWideChar;
+begin
+ try
+ StrDupW(result,PWideChar(WideString(v.Playback.FormatTitle('[%album%]'))));
+ except
+ result:=nil;
+ end;
+end;
+
+function GetGenre(const v:variant):PWideChar;
+begin
+ try
+ StrDupW(result,PWideChar(WideString(v.Playback.FormatTitle('[%genre%]'))));
+ except
+ result:=nil;
+ end;
+end;
+
+function GetBitrate(const v:variant):integer;
+begin
+ try
+ result:=v.Playback.FormatTitle('%bitrate%');
+ except
+ result:=0;
+ end;
+end;
+
+function GetSamplerate(const v:variant):integer;
+begin
+ try
+ result:=v.Playback.FormatTitle('%samplerate%');
+ except
+ result:=0;
+ end;
+end;
+
+function GetChannels(const v:variant):integer;
+var
+ s:WideString;
+begin
+ result:=0;
+ try
+ s:=v.Playback.FormatTitle('%channels%');
+ if StrCmpW(pWideChar(s),'mono')=0 then
+ result:=1
+ else if StrCmpW(pWideChar(s),'Stereo')=0 then
+ result:=2;
+ except
+ end;
+end;
+
+function GetCodec(const v:variant):integer;
+var
+ s:WideString;
+ i:integer;
+begin
+ result:=0;
+ try
+ s:=v.Playback.FormatTitle('%codec%');
+ i:=Length(s);
+ if i>0 then result:=ORD(s[1]);
+ if i>1 then result:=result+(ORD(s[2]) shl 8);
+ if i>2 then result:=result+(ORD(s[3]) shl 16);
+ if i>3 then result:=result+(ORD(s[4]) shl 24);
+ except
+ end;
+end;
+
+function SplitVersion(p:pWideChar):integer;
+begin
+ result:=StrToInt(p);
+ while (p^>='0') and (p^<='9') do inc(p); inc(p);
+ result:=result*16+StrToInt(p);
+ while (p^>='0') and (p^<='9') do inc(p); inc(p);
+ result:=result*16+StrToInt(p);
+ while (p^>='0') and (p^<='9') do inc(p);
+ if p^<>#0 then inc(p);
+ if (p^>='0') and (p^<='9') then
+ begin
+ result:=result*16+StrToInt(p);
+ while (p^>='0') and (p^<='9') do inc(p);
+ end;
+ if p^<>#0 then
+ begin
+ inc(p);
+ while (p^<>#0) and (p^<>' ') do inc(p);
+ if p^=' ' then
+ begin
+ inc(p);
+ result:=result*16+StrToInt(p);
+ end;
+ end;
+end;
+
+function GetVersion(const ver:pWideChar):integer;
+begin
+ if (ver=nil) or (ver^=#0) then
+ result:=0
+ else
+ result:=SplitVersion(ver);
+end;
+
+function GetVersionText(const v:variant):PWideChar;
+begin
+ try
+ StrDupW(result,PWideChar(WideString(v.Name))+length(FooPrefix));
+ except
+ result:=nil;
+ end;
+end;
+
+function GetTotalTime(const v:variant):integer;
+begin
+ try
+ result:=v.Playback.Length;
+ except
+ result:=0;
+ end;
+end;
+
+function GetElapsedTime(const v:variant):integer;
+begin
+ try
+ result:=v.Playback.Position;
+ except
+ result:=0;
+ end;
+end;
+
+function GetStatus(wnd:HWND):integer; cdecl;
+var
+ tmp:boolean;
+ v:variant;
+ winampwnd:HWND;
+begin
+ try
+ result:=WAT_MES_STOPPED;
+ v:=GetActiveOleObject(COMName);
+ tmp:=v.Playback.IsPaused;
+ if tmp then
+ result:=WAT_MES_PAUSED
+ else
+ begin
+ tmp:=v.Playback.IsPlaying;
+ if tmp then
+ result:=WAT_MES_PLAYING;
+ end;
+ except
+ winampwnd:=WinampFindWindow(wnd);
+ if winampwnd<>0 then
+ result:=WinampGetStatus(winampwnd)
+ else
+ result:=WAT_MES_UNKNOWN;
+ end;
+ v:=null;
+end;
+
+function GetWndText(wnd:HWND):pWideChar;
+var
+ i:integer;
+begin
+ result:=GetDlgText(wnd);
+ if result<>nil then
+ begin
+ i:=StrIndexW(result,'[foobar');
+ if i<>0 then
+ begin
+ dec(i);
+ repeat
+ dec(i);
+ if ord(result[i])>ord(' ') then break;
+ until i<0;
+ result[i+1]:=#0;//if at end
+ end;
+ end;
+end;
+
+function GetFileName(wnd:HWND;flags:integer):PWideChar;cdecl;
+var
+ v:variant;
+begin
+ try
+ v:=GetActiveOleObject(COMName);
+// v:=CreateOleObject(COMName);
+ StrDupW(result,PWideChar(WideString(v.Playback.FormatTitle('%path%'))));
+ except
+ result:=nil;
+ end;
+ v:=Null;
+end;
+
+function Play(const v:variant;fname:PWideChar=nil):integer;
+begin
+ try
+ result:=v.Playback.Start(false);
+ except
+ result:=0;
+ end;
+end;
+
+function Pause(const v:variant):integer;
+begin
+ try
+ result:=v.Playback.Pause;
+ except
+ result:=0;
+ end;
+end;
+
+function Stop(const v:variant):integer;
+begin
+ try
+ result:=v.Playback.Stop;
+ except
+ result:=0;
+ end;
+end;
+
+function Next(const v:variant):integer;
+begin
+ try
+ result:=v.Playback.Next;
+ except
+ result:=0;
+ end;
+end;
+
+function Prev(const v:variant):integer;
+begin
+ try
+ result:=v.Playback.Previous;
+ except
+ result:=0;
+ end;
+end;
+
+function GetVolume(const v:variant):cardinal;
+begin
+ try
+ result:=v.Playback.Settings.Volume+100;
+ result:=(result shl 16)+round((result shl 4) / 100);
+ except
+ result:=0;
+ end;
+end;
+
+procedure SetVolume(const v:variant;value:cardinal);
+begin
+ try
+ v.Playback.Settings.Volume:=integer(((loword(value)*100) shr 4)-100);
+ except
+ end;
+end;
+
+function VolDn(const v:variant):integer;
+var
+ val:integer;
+begin
+ result:=GetVolume(v);
+ val:=loword(result);
+ if val>0 then
+ SetVolume(v,val-1);
+end;
+
+function VolUp(const v:variant):integer;
+var
+ val:integer;
+begin
+ result:=GetVolume(v);
+ val:=loword(result);
+ if val<16 then
+ SetVolume(v,val+1);
+end;
+
+function Seek(const v:variant;value:integer):integer;
+begin
+ try
+ result:=v.Playback.Position;
+ if (value>0) and (v.Playback.CanSeek) and (value<v.Playback.Length) then
+ v.Playback.Seek(value)
+ else
+ result:=0;
+ except
+ result:=0;
+ end;
+end;
+
+function GetInfo(var SongInfo:tSongInfo;flags:integer):integer;cdecl;
+var
+ v:variant;
+begin
+ result:=0;
+ with SongInfo do
+ begin
+ try
+ v:=GetActiveOleObject(COMName);
+// v:=CreateOleObject(COMName);
+ if (flags and WAT_OPT_PLAYERDATA)<>0 then
+ begin
+ if SongInfo.plyver=0 then
+ begin
+ SongInfo.txtver:=GetVersionText(v);
+ SongInfo.plyver:=GetVersion(txtver);
+ end;
+ end
+ else if (flags and WAT_OPT_CHANGES)<>0 then
+ begin
+ volume:=GetVolume(v);
+ if status<>WAT_MES_STOPPED then
+ time:=GetElapsedTime(v);
+ end
+ else
+ begin
+ if kbps =0 then kbps :=GetBitrate(v);
+ if khz =0 then khz :=GetSamplerate(v);
+ if channels=0 then channels:=GetChannels(v);
+ if codec =0 then codec :=GetCodec(v);
+ if total =0 then total :=GetTotalTime(v);
+ if year =NIL then year :=GetYear(v);
+ if artist =NIL then artist :=GetArtist(v);
+ if title =NIL then title :=GetTitle(v);
+ if album =NIL then album :=GetAlbum(v);
+ if genre =NIL then genre :=GetGenre(v);
+ end;
+ except
+ SongInfo.winampwnd:=WinampWindow;
+ if SongInfo.winampwnd<>0 then
+ begin
+ result:=WinampGetInfo(int_ptr(@SongInfo),flags);
+ end;
+ end;
+ v:=Null;
+ if (flags and WAT_OPT_CHANGES)<>0 then
+ wndtext:=GetWndText(SongInfo.plwnd);
+ end;
+end;
+
+function Command(wnd:HWND;cmd:integer;value:int_ptr):integer;cdecl;
+//var
+// c:integer;
+var
+ v:Variant;
+begin
+ result:=0;
+ try
+ v:=GetActiveOleObject(COMName);
+ case cmd of
+ WAT_CTRL_PREV : result:=Prev (v);
+ WAT_CTRL_PLAY : result:=Play (v,pWideChar(value));
+ WAT_CTRL_PAUSE: result:=Pause(v);
+ WAT_CTRL_STOP : result:=Stop (v);
+ WAT_CTRL_NEXT : result:=Next (v);
+ WAT_CTRL_VOLDN: result:=VolDn(v);
+ WAT_CTRL_VOLUP: result:=VolUp(v);
+ WAT_CTRL_SEEK : result:=Seek (v,value);
+ end;
+ except
+ if WinampWindow<>0 then
+ result:=WinampCommand(WinampWindow,cmd+(value shl 16))
+{
+ else
+ begin
+ case cmd of
+ WAT_CTRL_PREV : c:=ORD('B');
+ WAT_CTRL_PLAY : c:=ORD('C');
+ WAT_CTRL_PAUSE: c:=ORD('X');
+ WAT_CTRL_STOP : c:=ORD('Z');
+ WAT_CTRL_NEXT : c:=ORD('V');
+ WAT_CTRL_VOLDN: c:=VK_SUBTRACT;
+ WAT_CTRL_VOLUP: c:=VK_ADD;
+ else
+ exit;
+ end;
+ PostMessageW(wnd,WM_KEYDOWN,c,1);
+ end;
+}
+ end;
+ v:=Null;
+end;
+
+const
+ plRec:tPlayerCell=(
+ Desc :'foobar2000';
+ flags :WAT_OPT_SINGLEINST or WAT_OPT_HASURL;
+ Icon :0;
+ Init :nil;
+ DeInit :nil;
+ Check :@Check;
+ GetStatus:@GetStatus;
+ GetName :@GetFileName;
+ GetInfo :@GetInfo;
+ Command :@Command;
+ URL :'http://www.foobar2000.org/';
+ Notes :'For more full info WinampSpam or foo_comserver (more powerful) '#13#10+
+ 'components needs to be installed.');
+
+var
+ LocalPlayerLink:twPlayer;
+
+procedure InitLink;
+begin
+ LocalPlayerLink.Next:=PlayerLink;
+ LocalPlayerLink.This:=@plRec;
+ PlayerLink :=@LocalPlayerLink;
+end;
+
+initialization
+// ServicePlayer(WAT_ACT_REGISTER,dword(@plRec));
+ InitLink;
+end.
diff --git a/plugins/Watrack/players/pl_itunes.pas b/plugins/Watrack/players/pl_itunes.pas
new file mode 100644
index 0000000000..f43e6bac91
--- /dev/null
+++ b/plugins/Watrack/players/pl_itunes.pas
@@ -0,0 +1,392 @@
+{iTunes player}
+unit pl_iTunes;
+{$include compilers.inc}
+
+interface
+
+implementation
+uses windows,common,srv_player,wat_api
+ {$IFDEF DELPHI_7_UP}
+ ,variants
+ {$ENDIF}
+ {$IFDEF KOL_MCK}
+ ,kolcomobj
+ {$ELSE}
+ ,ComObj
+ {$ENDIF}
+;
+
+const
+ iTunesClass = 'iTunes';
+ iTunesTitle = 'iTunes';
+ COMName = 'iTunes.Application';
+
+function Check(wnd:HWND;flags:integer):HWND;cdecl;
+begin
+ if wnd<>0 then
+ begin
+ result:=0;
+ exit;
+ end;
+ result:=FindWindow(iTunesClass,iTunesTitle);
+end;
+
+function GetFileName(wnd:HWND;flags:integer):PWideChar;cdecl;
+var
+ v:variant;
+begin
+ try
+ v:=CreateOleObject(COMName);
+ StrDupW(result,PWideChar(WideString(v.CurrentTrack.Location)));
+ except
+ result:=nil;
+ end;
+ v:=Null;
+end;
+
+function SplitVersion(p:pWideChar):integer;
+begin
+ result:=StrToInt(p);
+ while (p^>='0') and (p^<='9') do inc(p); inc(p);
+ result:=result*16+StrToInt(p);
+ while (p^>='0') and (p^<='9') do inc(p); inc(p);
+ result:=(result*16+StrToInt(p))*16;
+ while (p^>='0') and (p^<='9') do inc(p); inc(p);
+ result:=result*16+StrToInt(p);
+end;
+
+function GetVersion(const ver:pWideChar):integer;
+begin
+ if (ver<>nil) and (ver^<>#0) then
+ result:=SplitVersion(ver)
+ else
+ result:=0;
+end;
+
+function GetVersionText(const v:variant):PWideChar;
+begin
+ try
+ StrDupW(result,PWideChar(WideString(v.Version)));
+ except
+ result:=nil;
+ end;
+end;
+
+function GetTotalTime(const v:variant):integer;
+begin
+ try
+ result:=v.CurrentTrack.Duration;
+ except
+ result:=0;
+ end;
+end;
+
+function GetElapsedTime(const v:variant):integer;
+begin
+ try
+ result:=v.PlayerPosition;
+ except
+ result:=0;
+ end;
+end;
+
+function GetStatus(wnd:HWND):integer; cdecl;
+var
+ tmp:integer;
+ v:variant;
+begin
+ try
+ v:=CreateOleObject(COMName);
+ tmp:=v.PlayerState;
+ if tmp=1 then
+ result:=WAT_MES_PLAYING
+ else
+ result:=WAT_MES_STOPPED;
+ except
+ result:=WAT_MES_UNKNOWN;
+ end;
+ v:=Null;
+end;
+
+function GetKbps(const v:variant):integer;
+begin
+ try
+ result:=v.CurrentTrack.BitRate;
+ except
+ result:=0;
+ end;
+end;
+
+function GetKhz(const v:variant):integer;
+begin
+ try
+ result:=v.CurrentTrack.SampleRate;
+ except
+ result:=0;
+ end;
+end;
+
+function GetTrack(const v:variant):integer;
+begin
+ try
+ result:=v.CurrentTrack.TrackNumber;
+ except
+ result:=0;
+ end;
+end;
+
+function GetAlbum(const v:variant):pWideChar;
+begin
+ try
+ StrDupW(result,PWideChar(WideString(v.CurrentTrack.Album)));
+ except
+ result:=nil;
+ end;
+end;
+
+function GetYear(const v:variant):pWideChar;
+begin
+ try
+ StrDupW(result,PWideChar(WideString(v.CurrentTrack.Year)));
+ except
+ result:=nil;
+ end;
+end;
+
+function GetGenre(const v:variant):pWideChar;
+begin
+ try
+ StrDupW(result,PWideChar(WideString(v.CurrentTrack.Genre)));
+ except
+ result:=nil;
+ end;
+end;
+
+function GetArtist(const v:variant):pWideChar;
+begin
+ try
+ StrDupW(result,PWideChar(WideString(v.CurrentTrack.Artist)));
+ except
+ result:=nil;
+ end;
+end;
+
+function GetTitle(const v:variant):pWideChar;
+begin
+ try
+ StrDupW(result,PWideChar(WideString(v.CurrentStreamTitle)));
+ except
+ result:=nil;
+ end;
+end;
+
+function GetComment(const v:variant):pWideChar;
+begin
+ try
+ StrDupW(result,PWideChar(WideString(v.CurrentTrack.Comment)));
+ except
+ result:=nil;
+ end;
+end;
+
+function GetWndText(const v:variant):pWideChar;
+begin
+ try
+ StrDupW(result,PWideChar(WideString(v.Windows.Name)));
+ except
+ result:=nil;
+ end;
+end;
+
+function Play(const v:variant;fname:PWideChar=nil):integer;
+begin
+ try
+// v.PlayFile(fname);
+ v.BackTrack;
+ result:=v.Play;
+ except
+ result:=0;
+ end;
+end;
+
+function Pause(const v:variant):integer;
+begin
+ try
+ result:=v.PlayPause;
+ except
+ result:=0;
+ end;
+end;
+
+function Stop(const v:variant):integer;
+begin
+ try
+ result:=v.Stop;
+ except
+ result:=0;
+ end;
+end;
+
+function Next(const v:variant):integer;
+begin
+ try
+ result:=v.NextTrack;
+ except
+ result:=0;
+ end;
+end;
+
+function Prev(const v:variant):integer;
+begin
+ try
+ result:=v.PreviousTrack;
+ except
+ result:=0;
+ end;
+end;
+
+function Seek(const v:variant;value:integer):integer;
+begin
+ try
+ result:=v.PlayerPosition;
+ if value>0 then
+ v.PlayerPosition:=value
+ else
+ result:=0;
+ except
+ result:=0;
+ end;
+end;
+
+function GetVolume(const v:variant):cardinal;
+begin
+ try
+ result:=v.SoundVolume;
+ result:=(result shl 16)+round((result shl 4)/100);
+ except
+ result:=0;
+ end;
+end;
+
+procedure SetVolume(const v:variant;value:cardinal);
+begin
+ try
+ v.SoundVolume:=integer((value*100) shr 4);
+ except
+ end;
+end;
+
+function VolDn(const v:variant):integer;
+var
+ val:integer;
+begin
+ result:=GetVolume(v);
+ val:=loword(result);
+ if val>0 then
+ SetVolume(v,val-1);
+end;
+
+function VolUp(const v:variant):integer;
+var
+ val:integer;
+begin
+ result:=GetVolume(v);
+ val:=loword(result);
+ if val<16 then
+ SetVolume(v,val+1);
+end;
+
+function GetInfo(var SongInfo:tSongInfo;flags:integer):integer;cdecl;
+var
+ v:variant;
+begin
+ result:=0;
+ with SongInfo do
+ begin
+ try
+ v:=CreateOleObject(COMName);
+ if (flags and WAT_OPT_PLAYERDATA)<>0 then
+ begin
+ if plyver=0 then
+ begin
+ txtver:=GetVersionText(v);
+ plyver:=GetVersion(txtver);
+ end;
+ end
+ else if (flags and WAT_OPT_CHANGES)<>0 then
+ begin
+ volume:=GetVolume(v);
+ if status<>WAT_MES_STOPPED then
+ time:=GetElapsedTime(v);
+ end
+ else
+ begin
+ if total =0 then total :=GetTotalTime(v);
+ if track =0 then track :=GetTrack(v);
+ if year =NIL then year :=GetYear(v);
+ if genre =NIL then genre :=GetGenre(v);
+ if artist =NIL then artist :=GetArtist(v);
+ if album =NIL then album :=GetAlbum(v);
+ if comment=NIL then comment:=GetComment(v);
+ if kbps =0 then kbps :=GetKbps(v);
+ if khz =0 then khz :=GetKhz(v);
+ end;
+// wndtext:=GetWndText(v);
+ except
+ end;
+ v:=Null;
+// if title=NIL then title:=GetTitle; // only for streaming audio
+ end;
+end;
+
+function Command(wnd:HWND;cmd:integer;value:int_ptr):integer;cdecl;
+var
+ v:Variant;
+begin
+ result:=0;
+ try
+ v:=CreateOleObject(COMName);
+ case cmd of
+ WAT_CTRL_PREV : result:=Prev (v);
+ WAT_CTRL_PLAY : result:=Play (v,pWideChar(value));
+ WAT_CTRL_PAUSE: result:=Pause(v);
+ WAT_CTRL_STOP : result:=Stop (v);
+ WAT_CTRL_NEXT : result:=Next (v);
+ WAT_CTRL_VOLDN: result:=VolDn(v);
+ WAT_CTRL_VOLUP: result:=VolUp(v);
+ WAT_CTRL_SEEK : result:=Seek (v,value);
+ end;
+ except
+ end;
+ v:=Null;
+end;
+
+const
+ plRec:tPlayerCell=(
+ Desc :'iTunes';
+ flags :WAT_OPT_SINGLEINST or WAT_OPT_HASURL;
+ Icon :0;
+ Init :nil;
+ DeInit :nil;
+ Check :@Check;
+ GetStatus:@GetStatus;
+ GetName :@GetFileName;
+ GetInfo :@GetInfo;
+ Command :@Command;
+ URL :'http://www.itunes.com/';
+ Notes :nil);
+
+var
+ LocalPlayerLink:twPlayer;
+
+procedure InitLink;
+begin
+ LocalPlayerLink.Next:=PlayerLink;
+ LocalPlayerLink.This:=@plRec;
+ PlayerLink :=@LocalPlayerLink;
+end;
+
+initialization
+// ServicePlayer(WAT_ACT_REGISTER,dword(@plRec));
+ InitLink;
+end.
diff --git a/plugins/Watrack/players/pl_la.pas b/plugins/Watrack/players/pl_la.pas
new file mode 100644
index 0000000000..1945cf6580
--- /dev/null
+++ b/plugins/Watrack/players/pl_la.pas
@@ -0,0 +1,141 @@
+{LightAlloy player}
+unit pl_la;
+{$include compilers.inc}
+
+interface
+
+implementation
+uses windows,messages,common,srv_player,wat_api;
+
+const
+ LAClass = 'LightAlloyFront';
+const
+ WM_LACMD = WM_APP + 2504;
+ LAC_VERSION = 000;
+
+ LAC_FILE_OPEN = 050;
+ LAC_PLAYBACK_STOP = 100;
+ LAC_PLAYBACK_PLAY = 101;
+ LAC_PLAYBACK_STOP_PLAY = 102;
+ LAC_PLAYLIST_NEXT = 250;
+ LAC_PLAYLIST_PREV = 251;
+ LAC_PLAYLIST_PLAY = 252;
+ LAC_SOUND_VOLUME_INC = 401;
+ LAC_SOUND_VOLUME_DEC = 402;
+
+function Check(wnd:HWND;flags:integer):HWND;cdecl;
+begin
+ if wnd<>0 then
+ begin
+ result:=0;
+ exit;
+ end;
+ result:=FindWindow(LAClass,NIL);
+end;
+
+function GetVersion(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_LACMD,LAC_VERSION,0)
+end;
+
+function Play(wnd:HWND;fname:PWideChar=nil):integer;
+var
+ cds:COPYDATASTRUCT;
+begin
+ if (fname<>nil) and (fname^<>#0) then
+ begin
+ cds.dwData:=LAC_FILE_OPEN;
+ WideToAnsi(fname,PAnsiChar(cds.lpData));
+ cds.cbData:=StrLen(PAnsiChar(cds.lpData))+1;
+ SendMessage(wnd,WM_COPYDATA,0,lparam(@cds));
+ mFreeMem(cds.lpData);
+ end;
+ result:=SendMessage(wnd,WM_LACMD,LAC_PLAYLIST_PLAY,0) // LAC_PLAYLIST_PLAY
+end;
+
+function Pause(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_LACMD,LAC_PLAYBACK_STOP_PLAY,0)
+end;
+
+function Stop(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_LACMD,LAC_PLAYBACK_STOP,0)
+end;
+
+function Next(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_LACMD,LAC_PLAYLIST_NEXT,0)
+end;
+
+function Prev(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_LACMD,LAC_PLAYLIST_PREV,0)
+end;
+
+function VolDn(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_LACMD,LAC_SOUND_VOLUME_DEC,0);
+end;
+
+function VolUp(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_LACMD,LAC_SOUND_VOLUME_INC,0);
+end;
+
+function GetInfo(var SongInfo:tSongInfo;flags:integer):integer;cdecl;
+begin
+ result:=0;
+ if (flags and WAT_OPT_PLAYERDATA)<>0 then
+ begin
+ if SongInfo.plyver=0 then
+ begin
+ SongInfo.plyver:=GetVersion(SongInfo.plwnd);
+ end;
+ end;
+end;
+
+function Command(wnd:HWND;cmd:integer;value:int_ptr):integer;cdecl;
+begin
+ case cmd of
+ WAT_CTRL_PREV : result:=Prev (wnd);
+ WAT_CTRL_PLAY : result:=Play (wnd,pWideChar(value));
+ WAT_CTRL_PAUSE: result:=Pause(wnd);
+ WAT_CTRL_STOP : result:=Stop (wnd);
+ WAT_CTRL_NEXT : result:=Next (wnd);
+ WAT_CTRL_VOLDN: result:=VolDn(wnd);
+ WAT_CTRL_VOLUP: result:=VolUp(wnd);
+ else
+ result:=0;
+ end;
+end;
+
+const
+ plRec:tPlayerCell=(
+ Desc :'LightAlloy';
+ flags :WAT_OPT_HASURL;
+ Icon :0;
+ Init :nil;
+ DeInit :nil;
+ Check :@Check;
+ GetStatus:nil;
+ GetName :nil;
+ GetInfo :@GetInfo;
+ Command :@Command;
+ URL :'http://www.softella.com/';
+ Notes :nil);
+
+var
+ LocalPlayerLink:twPlayer;
+
+procedure InitLink;
+begin
+ LocalPlayerLink.Next:=PlayerLink;
+ LocalPlayerLink.This:=@plRec;
+ PlayerLink :=@LocalPlayerLink;
+end;
+
+initialization
+// ServicePlayer(WAT_ACT_REGISTER,dword(@plRec));
+ InitLink;
+end.
diff --git a/plugins/Watrack/players/pl_lastfm.pas b/plugins/Watrack/players/pl_lastfm.pas
new file mode 100644
index 0000000000..56ed3c7d4b
--- /dev/null
+++ b/plugins/Watrack/players/pl_lastfm.pas
@@ -0,0 +1,129 @@
+{Last.fm Player}
+unit pl_LastFM;
+
+interface
+
+implementation
+
+uses windows,common,messages,syswin,srv_player,wat_api;
+
+
+const
+ LFMName = 'Last.fm Player';
+ LFMText = 'Last.fm';
+ LFMClass = 'QWidget';
+
+const
+ UserName:pWideChar=nil;
+
+function Check(wnd:HWND;aflags:integer):HWND;cdecl;
+var
+ tmp,EXEName:PAnsiChar;
+begin
+ if wnd<>0 then
+ begin
+ result:=0;
+ exit;
+ end;
+ result:=FindWindow(LFMClass,nil{LFMName});
+ if result<>0 then
+ begin
+ tmp:=Extract(GetEXEByWnd(result,EXEName),true);
+ if lstrcmpia(tmp,'LASTFM.EXE')<>0 then
+ result:=0;
+ mFreeMem(tmp);
+ mFreeMem(EXEName);
+ if result<>0 then
+ result:=GetWindow(result,GW_OWNER);
+ end;
+ if result=0 then
+ mFreeMem(UserName);
+end;
+
+function GetWndText(wnd:HWND):pWideChar;
+var
+ ps:array [0..255] of WideChar;
+ p:pWideChar;
+begin
+ SendMessageW(wnd,WM_GETTEXT,255,lparam(@ps));
+ p:=StrPosW(ps,' | ');
+ if p<>nil then
+ begin
+ mFreeMem(UserName);
+ StrDupW(UserName,p+3);
+ p^:=#0;
+ end;
+ StrDupW(result,ps);
+end;
+
+function GetFileName(wnd:HWND;flags:integer):PWideChar;cdecl;
+var
+ buf:array [0..1023] of WideChar;
+ p:pWideChar;
+begin
+// lstrcpyw(buf,'http://');
+buf[0]:=#0;
+ p:=GetWndText(wnd);
+ StrCatW(buf,p);
+ StrCatW(buf,'.mp3');
+ StrDupW(result,buf);
+ mFreeMem(p);
+end;
+
+function GetStatus(wnd:HWND):integer; cdecl;
+var
+ txt:pWideChar;
+begin
+ txt:=GetWndText(wnd);
+ if StrCmpW(txt,LFMText,Length(LFMText))<>0 then
+ result:=WAT_MES_PLAYING
+ else
+ result:=WAT_MES_STOPPED;
+ mFreeMem(txt);
+end;
+
+function GetInfo(var SongInfo:tSongInfo;aflags:integer):integer;cdecl;
+begin
+ result:=0;
+ with SongInfo do
+ begin
+ fsize:=1;
+ if (aflags and WAT_OPT_CHANGES)<>0 then
+ begin
+ wndtext:=GetWndText(plwnd);
+ end
+ else
+ begin
+ end;
+ end;
+end;
+
+const
+ plRec:tPlayerCell=(
+ Desc :'Last.fm';
+ flags :WAT_OPT_LAST or WAT_OPT_SINGLEINST or WAT_OPT_HASURL;
+ Icon :0;
+ Init :nil;
+ DeInit :nil;
+ Check :@Check;
+ GetStatus:@GetStatus;
+ GetName :@GetFileName;
+ GetInfo :@GetInfo;
+ Command :nil;
+ URL :'http://www.lastfm.com/';
+ Notes :'Works by window title analysing only');
+
+var
+ LocalPlayerLink:twPlayer;
+
+procedure InitLink;
+begin
+ LocalPlayerLink.Next:=PlayerLink;
+ LocalPlayerLink.This:=@plRec;
+ PlayerLink :=@LocalPlayerLink;
+end;
+
+initialization
+// ServicePlayer(WAT_ACT_REGISTER,dword(@plRec));
+ InitLink;
+end.
diff --git a/plugins/Watrack/players/pl_mmonkey.pas b/plugins/Watrack/players/pl_mmonkey.pas
new file mode 100644
index 0000000000..bb826b9851
--- /dev/null
+++ b/plugins/Watrack/players/pl_mmonkey.pas
@@ -0,0 +1,181 @@
+{MediaMonkey player}
+unit pl_mmonkey;
+{$include compilers.inc}
+
+interface
+
+implementation
+uses windows,messages,winampapi,common,srv_player,wat_api
+ {$IFDEF DELPHI_7_UP}
+ ,variants
+ {$ENDIF}
+ {$IFDEF KOL_MCK}
+ ,kolcomobj
+ {$ELSE}
+ ,ComObj
+ {$ENDIF}
+;
+
+const
+ COMName:PAnsiChar = 'SongsDB.SDBApplication';
+
+const
+ WM_WA_IPC = WM_USER;
+ IPC_GETVERSION = 0;
+
+{
+const
+ MMonkeyName = 'MediaMonkey';
+}
+function Check(wnd:HWND;flags:integer):HWND;cdecl;
+//var
+// i:integer;
+{
+ EXEName:pWideChar;
+ tmp:pWideChar;
+}
+// v:Variant;
+begin
+ if wnd<>0 then
+ begin
+ result:=0;
+ exit;
+ end;
+ result:=FindWindow('TFMainWindow','MediaMonkey');
+ if result=0 then
+ result:=FindWindow('TFMainWindow.UnicodeClass','MediaMonkey');
+{
+ wnd:=FindWindow(WinAmpClass,NIL);
+ if wnd<>0 then
+ begin
+ if (SendMessage(wnd,WM_WA_IPC,0,IPC_GETVERSION) and $FF0F)<>$990B then
+ wnd:=result;
+ end;
+}
+{
+ wnd:=FindWindow(WinAmpClass,NIL);
+ if wnd<>0 then
+ begin
+ i:=SendMessage(wnd,WM_WA_IPC,0,IPC_GETVERSION) and $FF0F;
+ if i=$990B then
+ begin
+
+ try
+// v:=GetActiveOleObject(COMName);
+ v:=CreateOleObject(COMName);
+ if not v.IsRunning then
+ wnd:=0;
+ except
+ end;
+ v:=Null;
+
+ end
+ else
+ wnd:=0;
+ end;
+ result:=wnd;
+{
+ begin
+ EXEName:=GetEXEByWnd(wnd);
+ tmp:=Extract(EXEName,true);
+ mFreeMem(EXEName);
+ result:=StrCmpW(tmp,MMonkeyName,length(MMonkeyName))=0;
+ mFreeMem(tmp);
+ end;
+}
+end;
+
+function GetVersion(const v:variant):integer;
+begin
+ try
+ result:=(v.VersionHi shl 8)+(v.VersionLo shl 4)+v.VersionRelease;
+ except
+ result:=0;
+ end;
+end;
+
+function GetVersionText(const v:variant):PWideChar;
+begin
+ try
+ StrDupW(result,PWideChar(WideString(v.VersionString)));
+ except
+ result:=nil;
+ end;
+end;
+
+function GetFileName(wnd:HWND;flags:integer):pWideChar;cdecl;
+var
+ v:Variant;
+begin
+ try
+// SDB:=GetActiveOleObject(COMName);
+ v:=CreateOleObject(COMName);
+ StrDupW(result,PWideChar(WideString(v.Player.CurrentSong.Path)));
+ except
+ result:=nil;
+ end;
+ v:=Null;
+end;
+
+function GetStatus(wnd:HWND):integer; cdecl;
+begin
+ result:=WinampGetStatus(wnd)
+end;
+
+function GetInfo(var SongInfo:tSongInfo;flags:integer):integer;cdecl;
+var
+ v:variant;
+begin
+ if (flags and WAT_OPT_PLAYERDATA)<>0 then
+ begin
+ if SongInfo.plyver=0 then
+ begin
+ try
+ v:=CreateOleObject(COMName);
+ with SongInfo do
+ begin
+ plyver:=GetVersion(v);
+ txtver:=GetVersionText(v);
+ end;
+ except
+ end;
+ v:=Null;
+ end;
+ end;
+ result:=WinampGetInfo(int_ptr(@SongInfo),flags);
+end;
+
+function Command(wnd:HWND;cmd:integer;value:integer):integer;cdecl;
+begin
+ result:=WinampCommand(wnd,cmd+(value shl 16));
+end;
+
+const
+ plRec:tPlayerCell=(
+ Desc :'MediaMonkey';
+ flags :WAT_OPT_SINGLEINST or WAT_OPT_HASURL;
+ Icon :0;
+ Init :nil;
+ DeInit :nil;
+ Check :@Check;
+ GetStatus:@GetStatus;
+ GetName :@GetFileName;
+ GetInfo :@GetInfo;
+ Command :@Command;
+ URL :'http://www.mediamonkey.com/';
+ Notes :nil);
+
+var
+ LocalPlayerLink:twPlayer;
+
+procedure InitLink;
+begin
+ LocalPlayerLink.Next:=PlayerLink;
+ LocalPlayerLink.This:=@plRec;
+ PlayerLink :=@LocalPlayerLink;
+end;
+
+initialization
+// ServicePlayer(WAT_ACT_REGISTER,dword(@plRec));
+ InitLink;
+end.
diff --git a/plugins/Watrack/players/pl_mpc.pas b/plugins/Watrack/players/pl_mpc.pas
new file mode 100644
index 0000000000..a2127cea76
--- /dev/null
+++ b/plugins/Watrack/players/pl_mpc.pas
@@ -0,0 +1,117 @@
+{Media Player Classic}
+unit pl_MPC;
+{$include compilers.inc}
+
+interface
+
+implementation
+uses windows,common,wrapper,srv_player,wat_api;
+
+const
+ MPCClass98 = 'MediaPlayerClassicA';
+ MPCClassXP = 'MediaPlayerClassicW';
+ MPCTail = ' - Media Player Classic';
+
+function Check(wnd:HWND;flags:integer):HWND;cdecl;
+begin
+ result:=FindWindowEx(0,wnd,MPCClassXP,NIL);
+ if result=0 then
+ result:=FindWindowEx(0,wnd,MPCClass98,NIL);
+end;
+
+function chwnd(awnd:hwnd;Param:pdword):boolean; stdcall;
+var
+ s:array [0..31] of AnsiChar;
+ i:integer;
+begin
+ FillChar(s,SizeOf(s),0);
+ GetWindowTextA(awnd,s,30);
+ i:=StrIndex(PAnsiChar(@s),' / ');
+ if i<>0 then
+ begin
+ if Param^=0 then
+ begin
+ s[i-1]:=#0;
+ Param^:=TimeToInt(s);
+ end
+ else
+ begin
+ Param^:=TimeToInt(s+i+2);
+ end;
+ result:=false;
+ end
+ else
+ result:=true;
+end;
+
+function GetElapsedTime(wnd:HWND):integer;
+begin
+ result:=0;
+ if EnumChildWindows(wnd,@chwnd,int_ptr(@result)) then
+ result:=0;
+end;
+
+function GetTotalTime(wnd:HWND):integer;
+begin
+ result:=1;
+ if EnumChildWindows(wnd,@chwnd,int_ptr(@result)) then
+ result:=0;
+end;
+
+function GetWndText(wnd:HWND):pWidechar;
+var
+ p:pWideChar;
+begin
+ result:=GetDlgText(wnd);
+ if result<>nil then
+ begin
+ p:=StrPosW(result,MPCTail);
+ if p<>nil then
+ p^:=#0;
+ end;
+end;
+
+function GetInfo(var SongInfo:tSongInfo;flags:integer):integer;cdecl;
+begin
+ result:=0;
+ with SongInfo do
+ begin
+ if (flags and WAT_OPT_CHANGES)<>0 then
+ begin
+ time :=GetElapsedTime(SongInfo.plwnd);
+ wndtext:=GetWndText(SongInfo.plwnd);
+ end
+ else if total=0 then
+ total:=GetTotalTime(SongInfo.plwnd);
+ end;
+end;
+
+const
+ plRec:tPlayerCell=(
+ Desc :'MPC';
+ flags :WAT_OPT_HASURL;
+ Icon :0;
+ Init :nil;
+ DeInit :nil;
+ Check :@Check;
+ GetStatus:nil;
+ GetName :nil;
+ GetInfo :@GetInfo;
+ Command :nil;
+ URL :'http://gabest.org/';
+ Notes :nil);
+
+var
+ LocalPlayerLink:twPlayer;
+
+procedure InitLink;
+begin
+ LocalPlayerLink.Next:=PlayerLink;
+ LocalPlayerLink.This:=@plRec;
+ PlayerLink :=@LocalPlayerLink;
+end;
+
+initialization
+// ServicePlayer(WAT_ACT_REGISTER,dword(@plRec));
+ InitLink;
+end.
diff --git a/plugins/Watrack/players/pl_mradio.pas b/plugins/Watrack/players/pl_mradio.pas
new file mode 100644
index 0000000000..3d97037fbb
--- /dev/null
+++ b/plugins/Watrack/players/pl_mradio.pas
@@ -0,0 +1,345 @@
+{mRadio protocol support}
+unit pl_mradio;
+{$include compilers.inc}
+{$r mradio.res}
+interface
+
+implementation
+uses m_api,dbsettings,windows,common,srv_player,wat_api,io;
+
+const
+ strUnknown:PwideChar = 'Unknown';
+ playername:PAnsiChar = 'mRadio';
+const
+ CurrentStation:THANDLE=THANDLE(-1);
+const
+ ChangesHook:THANDLE=0;
+const
+ MS_RADIO_COMMAND = 'mRadio/Command';
+ MS_RADIO_SETVOL = 'mRadio/SetVol';
+ ME_RADIO_STATUS:PAnsiChar = 'mRadio/Status';
+const
+ MRC_STOP = 0;
+ MRC_PLAY = 1;
+ MRC_PAUSE = 2;
+ MRC_PREV = 3;
+ MRC_NEXT = 4;
+ MRC_STATUS = 5;
+ MRC_SEEK = 6;
+const
+ RD_STATUS_NOSTATION = 0; // no active station found
+ RD_STATUS_PLAYING = 1; // media is playing
+ RD_STATUS_PAUSED = 2; // media is paused
+ RD_STATUS_STOPPED = 3; // media is stopped (only for playlists)
+ RD_STATUS_CONNECT = 4; // plugin try to connect to the station
+ RD_STATUS_ABORT = 5; // plugin want to abort while try to connect
+ // next is for events only
+ RD_STATUS_POSITION = 107; // position was changed
+ RD_STATUS_MUTED = 108; // Mute/Unmute command was sent
+ RD_STATUS_RECORD = 109; // "Record" action called
+ RD_STATUS_NEWTRACK = 110; // new track/station
+ RD_STATUS_NEWTAG = 111; // tag data changed
+ RD_STATUS_NEWSTATION = 112; // new station (contact)
+ // next command is for users
+ RD_STATUS_GET = 6; // to get current status
+const
+ prevfile:PWideChar=nil;
+
+function ClearmRadio:integer; cdecl;
+begin
+ result:=0;
+ if ChangesHook>0 then
+ begin
+ UnhookEvent(ChangesHook);
+ ChangesHook:=0;
+ CurrentStation:=THANDLE(-1);
+ end;
+ mFreeMem(prevfile);
+end;
+
+function SettingsChanged(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+begin
+ result:=0;
+ case wParam of
+ // clear station
+ RD_STATUS_NOSTATION: CurrentStation:=THANDLE(-1);
+ // get new url
+ RD_STATUS_NEWSTATION: CurrentStation:=lParam;
+ end;
+end;
+
+function Fill:integer;
+var
+ i:integer;
+begin
+ CurrentStation:=CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ while CurrentStation<>0 do
+ begin
+ i:=DBReadWord(CurrentStation,playername,'Status',WORD(-1));
+ if i=ID_STATUS_ONLINE then
+ begin
+ result:=1;
+ exit;
+ end;
+ CurrentStation:=CallService(MS_DB_CONTACT_FINDNEXT,CurrentStation,0);
+ end;
+ result:=WAT_RES_NOTFOUND;
+end;
+
+function InitmRadio:integer;
+begin
+ if ChangesHook=0 then
+ begin
+ ChangesHook:=HookEvent(ME_RADIO_STATUS,@SettingsChanged);
+ result:=Fill;
+ end
+ else if (CurrentStation<>0) and (CurrentStation<>THANDLE(-1)) then
+ result:=1
+ else
+ result:=WAT_RES_NOTFOUND;
+end;
+
+function Check(wnd:HWND;flags:integer):HWND;cdecl;
+begin
+ if CallProtoService(playername,PS_GETSTATUS,0,0)=ID_STATUS_ONLINE then
+ begin
+ result:=InitmRadio
+ end
+ else
+ begin
+ result:=HWND(WAT_RES_NOTFOUND);
+ ClearmRadio;
+ end;
+end;
+
+function GetKbps:integer;
+var
+ pc:PWideChar;
+begin
+ pc:=DBReadUnicode(CurrentStation,playername,'Bitrate','0');
+ result:=StrToInt(pc);
+ mFreeMem(pc);
+end;
+
+function GetFileName(wnd:HWND;flags:integer):pWideChar; cdecl;
+begin
+ result:=DBReadUnicode(0,playername,'ActiveURL',nil)
+end;
+
+function GetGenre:pWideChar;
+begin
+ result:=DBReadUnicode(CurrentStation,playername,'Genre',nil);
+end;
+
+function GetWndText:pWideChar;
+begin
+ result:=DBReadUnicode(CurrentStation,strCList,'StatusMsg',strUnknown);
+end;
+
+function GetTitle:pWideChar;
+begin
+ result:=DBReadUnicode(0,playername,'Title');
+ if result=nil then
+ result:=DBReadUnicode(CurrentStation,strCList,'StatusMsg',strUnknown);
+end;
+
+function GetArtist:pWideChar;
+begin
+ result:=DBReadUnicode(0,playername,'Artist');
+ if result=nil then
+ result:=DBReadUnicode(CurrentStation,strCList,'MyHandle',strUnknown);
+end;
+
+function GetVolume:cardinal;
+begin
+ result:=DBReadByte(0,playername,'Volume',0);
+ result:=(result shl 16)+round((result shl 4)/100);
+end;
+
+procedure SetVolume(value:cardinal);
+begin
+ CallService(MS_RADIO_SETVOL,(value*100) shr 4,0);
+end;
+
+function VolDn:integer;
+var
+ val:dword;
+begin
+ result:=GetVolume;
+ val:=loword(result);
+ if val>0 then
+ SetVolume(val-1);
+end;
+
+function VolUp:integer;
+var
+ val:dword;
+begin
+ result:=GetVolume;
+ val:=loword(result);
+ if val<16 then
+ SetVolume(val+1);
+end;
+
+function GetCover:pWideChar;
+var
+ ptr:PavatarCacheEntry;
+begin
+ result:=nil;
+ if ServiceExists(MS_AV_GETAVATARBITMAP)<>0 then
+ begin
+ ptr:=PavatarCacheEntry(CallService(MS_AV_GETAVATARBITMAP,CurrentStation,0));
+ if ptr<>nil then
+ AnsiToWide(ptr^.szFilename,result)
+ end;
+end;
+
+function GetVersionText(ver:dword):pWideChar;
+var
+ s:array [0..31] of WideChar;
+ p:pWideChar;
+begin
+ p:=@s;
+ IntToStr(p,ver shr 12);
+ while p^<>#0 do inc(p);
+ p^:='.';
+ IntToStr(p+1,(ver shr 8) and $F);
+ while p^<>#0 do inc(p);
+ p^:='.';
+ IntToStr(p+1,(ver shr 4) and $F);
+ while p^<>#0 do inc(p);
+ p^:='.';
+ IntToStr(p+1,ver and $F);
+ StrDupW(result,PWideChar(@s));
+end;
+
+function GetStatus:integer; cdecl;
+begin
+ if CurrentStation<>0 then
+ begin
+ result:=WAT_MES_PLAYING;
+ case CallService(MS_RADIO_COMMAND,MRC_STATUS,RD_STATUS_GET) of
+ RD_STATUS_PAUSED : result:=WAT_MES_PAUSED;
+ RD_STATUS_STOPPED: begin
+ result:=WAT_MES_STOPPED;
+ mFreeMem(prevfile);
+ end;
+ RD_STATUS_NOSTATION,
+ RD_STATUS_ABORT : result:=WAT_MES_UNKNOWN;
+ end;
+ end
+ else
+ result:=WAT_MES_STOPPED;
+end;
+
+function GetInfo(var SongInfo:tSongInfo;flags:integer):integer;cdecl;
+var
+ lfile:pWideChar;
+ isRemote:bool;
+ isChanging:bool;
+begin
+ result:=0;
+ if (flags and WAT_OPT_PLAYERDATA)<>0 then
+ begin
+ if SongInfo.plyver=0 then
+ begin
+ SongInfo.plyver:=DBReadDWord(0,playername,'version');
+ SongInfo.txtver:=GetVersionText(SongInfo.plyver);
+ end;
+ end
+ else if CurrentStation<>0 then
+ with SongInfo do
+ begin
+ if (flags and WAT_OPT_CHANGES)<>0 then
+ begin
+ volume:=GetVolume;
+ mFreeMem(wndtext);
+ wndtext:=GetWndText;
+ time:=CallService(MS_RADIO_COMMAND,MRC_SEEK,-1);
+ end
+ else
+ begin
+ lfile:=GetFileName(plwnd,flags);
+ isRemote:=StrPosW(lfile,'://')<>nil;
+ if (prevfile=nil) or isRemote or (StrCmpW(prevfile,lfile)<>0) then
+ begin
+ ClearTrackInfo(SongInfo,false);
+ mfile:=lfile;
+ mFreeMem(prevfile);
+ StrDupW(prevfile,mfile);
+ isChanging:=true;
+ end
+ else
+ begin
+ isChanging:=false;
+ mFreeMem(lfile);
+ end;
+
+ if not isRemote then
+ begin
+ if isChanging then
+ begin
+ CallService(MS_WAT_GETFILEINFO,0,lparam(@SongInfo));
+ fsize:=GetFSize(mfile);
+ mFreeMem(prevfile);
+ StrDupW(prevfile,mfile);
+ end;
+ end;
+//!!
+ if kbps =0 then kbps :=GetKbps;
+ if genre =nil then genre :=GetGenre;
+ if title =nil then title :=GetTitle;
+ if artist=nil then artist:=GetArtist;
+ if cover =nil then cover :=GetCover;
+ end;
+ end;
+end;
+
+function Command(wnd:HWND;cmd:integer;value:integer):integer;cdecl;
+begin
+ result:=0;
+ case cmd of
+ WAT_CTRL_PREV : result:=CallService(MS_RADIO_COMMAND,MRC_PREV,0);
+ WAT_CTRL_PLAY : result:=CallService(MS_RADIO_COMMAND,MRC_PLAY,0);
+ WAT_CTRL_PAUSE: result:=CallService(MS_RADIO_COMMAND,MRC_PAUSE,0);
+ WAT_CTRL_STOP : result:=CallService(MS_RADIO_COMMAND,MRC_STOP,0);
+ WAT_CTRL_NEXT : result:=CallService(MS_RADIO_COMMAND,MRC_NEXT,0);
+ WAT_CTRL_VOLDN: result:=VolDn;
+ WAT_CTRL_VOLUP: result:=VolUp;
+ WAT_CTRL_SEEK : result:=CallService(MS_RADIO_COMMAND,MRC_SEEK,value);
+ end;
+end;
+
+const
+ plRec:tPlayerCell=(
+ Desc :'mRadio';
+ flags :WAT_OPT_PLAYERINFO or WAT_OPT_SINGLEINST or WAT_OPT_HASURL or WAT_OPT_LAST;
+ Icon :0;
+ Init :nil;
+ DeInit :@ClearmRadio;
+ Check :@Check;
+ GetStatus:@GetStatus;
+ GetName :@GetFileName;
+ GetInfo :@GetInfo;
+ Command :@Command;
+ URL :'https://code.google.com/p/delphi-miranda-plugins/';
+ Notes :nil);
+
+var
+ LocalPlayerLink:twPlayer;
+
+procedure InitLink;
+begin
+ if plRec.Icon=0 then
+ plRec.Icon:=LoadImage(hInstance,'ICO_MRADIO',IMAGE_ICON,16,16,0);
+
+ LocalPlayerLink.Next:=PlayerLink;
+ LocalPlayerLink.This:=@plRec;
+ PlayerLink :=@LocalPlayerLink;
+end;
+
+initialization
+ InitLink;
+finalization
+ if plRec.Icon<>0 then DestroyIcon(plRec.Icon);
+end.
diff --git a/plugins/Watrack/players/pl_vlc.pas b/plugins/Watrack/players/pl_vlc.pas
new file mode 100644
index 0000000000..9d3a2ad57d
--- /dev/null
+++ b/plugins/Watrack/players/pl_vlc.pas
@@ -0,0 +1,380 @@
+{Video Lan player}
+unit pl_VLC;
+{$include compilers.inc}
+
+interface
+
+implementation
+uses windows,common,srv_player,wat_api,syswin,wrapper
+ {$IFDEF DELPHI_7_UP}
+ ,variants
+ {$ENDIF}
+ {$IFDEF KOL_MCK}
+ ,kolcomobj
+ {$ELSE}
+ ,ComObj
+ {$ENDIF}
+;
+
+{
+ procedure play; safecall;
+ procedure pause; safecall;
+ procedure stop; safecall;
+ procedure playlistNext; safecall;
+ procedure playlistPrev; safecall;
+ property Playing: WordBool read Get_Playing;
+ property Position: Single read Get_Position write Set_Position;
+ property Time: SYSINT read Get_Time write Set_Time;
+ property Length: SYSINT read Get_Length;
+ (0)1-97(100)
+ property Volume: SYSINT read Get_Volume write Set_Volume;
+ property VersionInfo: WideString read Get_VersionInfo;
+}
+
+const
+// GuidOld: TGUID = '{E23FE9C6-778E-49D4-B537-38FCDE4887D8}';
+ VLCClass = 'wxWindowClassNR';
+ VLCName = 'VLC media player';
+ COMName = 'VideoLAN.VLCPlugin.1'; // IVLCControl
+
+// GuidNew: TGUID = '{9BE31822-FDAD-461B-AD51-BE1D1C159921}';
+ VLCClassSkin = 'SkinWindowClass';
+ VLCClassNew = 'QWidget';
+ VLCEXEName = 'VLC.EXE';
+ COMNameNew = 'VideoLAN.VLCPlugin2'; // IVLCControl2
+
+function Check(wnd:HWND;flags:integer):HWND;cdecl;
+var
+ tmp,EXEName:PAnsiChar;
+begin
+ if wnd<>0 then
+ begin
+ result:=0;
+ exit;
+ end;
+ result:=FindWindow(VLCClass,VLCName);
+ if result=0 then
+ result:=FindWindow(VLCClassSkin,nil); // VLCName
+ if result=0 then
+ result:=FindWindow(VLCClassNew,nil);
+ if result<>0 then
+ begin
+ tmp:=Extract(GetEXEByWnd(result,EXEName),true);
+ if lstrcmpia(tmp,VLCEXEName)<>0 then
+ result:=0;
+ mFreeMem(tmp);
+ mFreeMem(EXEName);
+ end;
+{ if result<>0 then
+ begin
+ tmp:=Extract(GetEXEByWnd(result,EXEName),true);
+ if lstrcmpia(tmp,'VLC.EXE')<>0 then
+ result:=0;
+ mFreeMem(tmp);
+ mFreeMem(EXEName);
+ end;
+}
+end;
+
+function SplitVersion(p:pWideChar):integer;
+begin
+ result:=StrToInt(p);
+ while (p^>='0') and (p^<='9') do inc(p); inc(p);
+ result:=result*16+StrToInt(p);
+ while (p^>='0') and (p^<='9') do inc(p); inc(p);
+ result:=(result*16+StrToInt(p))*16;
+ while (p^>='0') and (p^<='9') do inc(p); inc(p);
+ result:=result*16+StrToInt(p);
+end;
+
+function GetVersion(const ver:pWideChar):integer;
+begin
+ try
+ result:=SplitVersion(ver);
+ except
+ result:=0;
+ end;
+end;
+
+function GetVersionText(const v:variant):PWideChar;
+begin
+ try
+ StrDupW(result,PWideChar(WideString(v.VersionInfo)));
+ except
+ result:=nil;
+ end;
+end;
+
+function GetWndText(wnd:HWND):pWideChar;
+var
+ p:pWideChar;
+begin
+ result:=GetDlgText(wnd);
+{
+need to clear " - lalala VLC" at the end
+}
+ if result<>nil then
+ begin
+ p:=StrRScanW(result,'-');
+ if p<>nil then // found
+ begin
+ if (p>result) and ((p-1)^=' ') and ((p+1)^=' ') then
+ (p-1)^:=#0;
+ end;
+ end;
+end;
+
+{
+function GetTotalTime:integer;
+var
+ v:variant;
+begin
+ try
+ v:=CreateOleObject(COMName);
+ result:=v.Length;
+ except
+ result:=inherited GetTotalTime;
+ end;
+ v:=Null;
+end;
+
+function GetElapsedTime:integer;
+var
+ v:variant;
+begin
+ try
+ v:=CreateOleObject(COMName);
+ result:=v.Time;
+ except
+ result:=inherited GetElapsedTime;
+ end;
+ v:=Null;
+end;
+
+function GetStatus:integer; cdecl;
+var
+ v:variant;
+ tmp:boolean;
+begin
+ try
+ v:=CreateOleObject(COMName);
+ tmp:=v.Playing;
+ if tmp then
+ result:=WAT_MES_PLAYING
+ else
+ result:=WAT_MES_STOPPED;
+ except
+ result:=inherited GetStatus;
+ end;
+ v:=Null;
+end;
+
+function Play(fname:PWideChar=nil):integer;
+var
+ v:variant;
+begin
+ try
+ v:=CreateOleObject(COMName);
+ result:=v.play;
+ except
+ result:=inherited Play(fname);
+ end;
+ v:=Null;
+end;
+
+function Pause:integer;
+var
+ v:variant;
+begin
+ try
+ v:=CreateOleObject(COMName);
+ result:=v.pause;
+ except
+ result:=inherited Pause;
+ end;
+ v:=Null;
+end;
+
+function Stop:integer;
+var
+ v:variant;
+begin
+ try
+ v:=CreateOleObject(COMName);
+ result:=v.stop;
+ except
+ result:=inherited Stop;
+ end;
+ v:=Null;
+end;
+
+function Next:integer;
+var
+ v:variant;
+begin
+ try
+ v:=CreateOleObject(COMName);
+ result:=v.playlistNext;
+ except
+ result:=inherited Next;
+ end;
+ v:=Null;
+end;
+
+function Prev:integer;
+var
+ v:variant;
+begin
+ try
+ v:=CreateOleObject(COMName);
+ result:=v.playlistPrev;
+ except
+ result:=inherited Prev;
+ end;
+ v:=Null;
+end;
+
+function Seek(value:integer):integer;
+var
+ v:variant;
+begin
+ try
+ v:=CreateOleObject(COMName);
+ result:=v.Position;
+ if value>0 then
+ v.Position:=value
+ else
+ result:=0;
+ except
+ result:=inherited Seek(value);
+ end;
+ v:=Null;
+end;
+
+function GetVolume:cardinal;
+var
+ v:variant;
+begin
+ try
+ v:=CreateOleObject(COMName);
+ result:=v.Volume;
+ result:=(result shl 16)+((result shl 4) div 100);
+ except
+ result:=0;
+ end;
+ v:=Null;
+end;
+
+procedure SetVolume(value:cardinal);
+var
+ v:variant;
+begin
+ try
+ v:=CreateOleObject(COMName);
+ v.Volume:=(value*100) shr 4;
+ end;
+ except
+ result:=inherited SetVolume(value);
+ end;
+ v:=Null;
+end;
+
+function VolDn:integer;
+var
+ val:integer;
+begin
+ result:=GetVolume;
+ val:=loword(result);
+ if val>0 then
+ SetVolume(val-1);
+end;
+
+function VolUp:integer;
+var
+ val:integer;
+begin
+ result:=GetVolume;
+ val:=loword(result);
+ if val<16 then
+ SetVolume(val+1);
+end;
+
+}
+function GetInfo(var SongInfo:tSongInfo;flags:integer):integer;cdecl;
+var
+ v:variant;
+begin
+ result:=0;
+ if (flags and WAT_OPT_PLAYERDATA)<>0 then
+ begin
+ if SongInfo.plyver=0 then
+ begin
+ try
+ try
+ v:=CreateOleObject(COMName);
+ except
+ try
+ v:=CreateOleObject(COMNameNew);
+ except
+ v:=Null;
+ end;
+ end;
+ if v<>Null then
+ with SongInfo do
+ begin
+ txtver:=GetVersionText(v);
+ plyver:=GetVersion(txtver);
+ end;
+ except
+ end;
+ v:=Null;
+ if (flags and WAT_OPT_CHANGES)<>0 then
+ SongInfo.wndtext:=GetWndText(SongInfo.plwnd);
+ end;
+ end;
+end;
+{
+function Command(wnd:HWND;cmd:integer;value:integer):integer;cdecl;
+begin
+ result:=0;
+ case cmd of
+ WAT_CTRL_PREV : result:=Prev;
+ WAT_CTRL_PLAY : result:=Play(pWideChar(value));
+ WAT_CTRL_PAUSE: result:=Pause;
+ WAT_CTRL_STOP : result:=Stop;
+ WAT_CTRL_NEXT : result:=Next;
+ WAT_CTRL_VOLDN: result:=VolDn;
+ WAT_CTRL_VOLUP: result:=VolUp;
+ WAT_CTRL_SEEK : result:=Seek(value);
+ end;
+end;
+}
+const
+ plRec:tPlayerCell=(
+ Desc :'VideoLAN player';
+ flags :WAT_OPT_HASURL;
+ Icon :0;
+ Init :nil;
+ DeInit :nil;
+ Check :@Check;
+ GetStatus:nil;
+ GetName :nil;
+ GetInfo :@GetInfo;
+ Command :nil;
+ URL :'http://www.videolan.org/';
+ Notes :nil);
+
+var
+ LocalPlayerLink:twPlayer;
+
+procedure InitLink;
+begin
+ LocalPlayerLink.Next:=PlayerLink;
+ LocalPlayerLink.This:=@plRec;
+ PlayerLink :=@LocalPlayerLink;
+end;
+
+initialization
+// ServicePlayer(WAT_ACT_REGISTER,dword(@plRec));
+ InitLink;
+end.
diff --git a/plugins/Watrack/players/pl_winamp.pas b/plugins/Watrack/players/pl_winamp.pas
new file mode 100644
index 0000000000..254475e09a
--- /dev/null
+++ b/plugins/Watrack/players/pl_winamp.pas
@@ -0,0 +1,170 @@
+{Winamp-like - base class}
+unit pl_WinAmp;
+{$include compilers.inc}
+
+interface
+
+implementation
+
+uses winampapi,windows,common,messages,syswin,srv_player,wat_api;
+
+{
+#define IPC_GETPLAYLISTTITLE 212
+/* (requires Winamp 2.04+, only usable from plug-ins (not external apps))
+** char *name=SendMessage(hwnd_winamp,WM_WA_IPC,index,IPC_GETPLAYLISTTITLE);
+**
+** IPC_GETPLAYLISTTITLE gets the title of the playlist entry [index].
+** returns a pointer to it. returns NULL on error.
+*/
+}
+// class = BaseWindow_RootWnd
+// title = Main Window
+
+// ---------- check player ------------
+
+function Check(wnd:HWND;aflags:integer):HWND;cdecl;
+var
+ tmp,EXEName:PAnsiChar;
+begin
+ result:=FindWindowEx(0,wnd,WinampClass,NIL);
+ if result<>0 then
+ begin
+ tmp:=Extract(GetEXEByWnd(result,EXEName),true);
+ if lstrcmpia(tmp,'WINAMP.EXE')<>0 then
+ result:=0;
+ mFreeMem(tmp);
+ mFreeMem(EXEName);
+ end;
+end;
+
+function WAnyCheck(wnd:HWND;aflags:integer):HWND;cdecl;
+begin
+ result:=FindWindowEx(0,wnd,WinampClass,NIL);
+end;
+
+// ----------- Get info ------------
+
+function GetStatus(wnd:HWND):integer; cdecl;
+begin
+ result:=WinampGetStatus(wnd)
+end;
+
+function GetWidth(wnd:HWND):integer;
+begin
+ result:=LOWORD(SendMessage(wnd,WM_WA_IPC,3,IPC_GETINFO));
+end;
+
+function GetHeight(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_WA_IPC,3,IPC_GETINFO) shr 16;
+end;
+
+function GetInfo(var SongInfo:tSongInfo;aflags:integer):integer;cdecl;
+begin
+ SongInfo.winampwnd:=SongInfo.plwnd;
+ result:=WinampGetInfo(int_ptr(@SongInfo),aflags);
+ with SongInfo do
+ begin
+ if (aflags and WAT_OPT_CHANGES)<>0 then
+ wndtext:=WinampGetWindowText(winampwnd)
+ else
+ begin
+{
+ if ((loword(plyver) shr 12)>=5) and
+ (SendMessage(wnd,WM_WA_IPC,0,IPC_IS_PLAYING_VIDEO)>1) then
+ begin
+ if width =0 then width :=GetWidth(wnd);
+ if height=0 then Height:=GetHeight(wnd);
+ end;
+}
+ end;
+ end;
+end;
+
+// ------- Commands ----------
+
+function Command(wnd:HWND;cmd:integer;value:integer):integer;cdecl;
+begin
+ result:=WinampCommand(wnd,cmd+(value shl 16));
+end;
+
+// ------- In-process code -------
+
+function GetFileName(wnd:HWND;flags:integer):pWideChar;cdecl;
+var
+ fpos,fname:int_ptr;
+ pid:dword;
+ op:THANDLE;
+ buf:array [0..1023] of AnsiChar;
+ tmp:{$IFDEF FPC}LongWord{$ELSE}ulong_ptr{$ENDIF};
+begin
+ result:=nil;
+ if (flags and WAT_OPT_IMPLANTANT)<>0 then
+ begin
+ if SendMessage(wnd,WM_WA_IPC,0,IPC_ISPLAYING)<>WAT_MES_STOPPED then
+ begin
+ fpos :=SendMessage(wnd,WM_USER,0 ,IPC_GETLISTPOS);
+ fname:=SendMessage(wnd,WM_USER,fpos,IPC_GETPLAYLISTFILE);
+ GetWindowThreadProcessId(wnd,@pid);
+ op:=OpenProcess(PROCESS_VM_READ,false,pid);
+ if op<>0 then
+ begin
+ ReadProcessMemory(op,PByte(fname),@buf,SizeOf(buf),tmp);
+ CloseHandle(op);
+ if tmp>0 then
+ AnsiToWide(buf,result);
+ end;
+ end;
+ end;
+end;
+
+const
+ plRec:tPlayerCell=(
+ Desc :'Winamp';
+ flags :WAT_OPT_ONLYONE or WAT_OPT_WINAMPAPI or WAT_OPT_HASURL;
+ Icon :0;
+ Init :nil;
+ DeInit :nil;
+ Check :@Check;
+ GetStatus:@GetStatus;
+ GetName :@GetFileName;
+ GetInfo :@GetInfo;
+ Command :@Command;
+ URL :'http://www.winamp.com/';
+ Notes :nil);
+
+const
+ plRecClone:tPlayerCell=(
+ Desc :'Winamp Clone';
+ flags :WAT_OPT_ONLYONE or WAT_OPT_WINAMPAPI or WAT_OPT_LAST;
+ Icon :0;
+ Init :nil;
+ DeInit :nil;
+ Check :@WAnyCheck;
+ GetStatus:@GetStatus;
+ GetName :nil;
+ GetInfo :@WinampGetInfo;
+ Command :@Command;
+ URL :nil;
+ Notes :'All "unknown" players using Winamp API');
+
+var
+ LocalPlayerLink,
+ LocalPlayerLinkC:twPlayer;
+
+procedure InitLink;
+begin
+ LocalPlayerLink.Next:=PlayerLink;
+ LocalPlayerLink.This:=@plRec;
+ PlayerLink :=@LocalPlayerLink;
+
+ LocalPlayerLinkC.Next:=PlayerLink;
+ LocalPlayerLinkC.This:=@plRecClone;
+ PlayerLink :=@LocalPlayerLinkC;
+end;
+
+initialization
+// ServicePlayer(WAT_ACT_REGISTER,dword(@plRec));
+// ServicePlayer(WAT_ACT_REGISTER,dword(@plRecClone));
+ InitLink;
+end.
diff --git a/plugins/Watrack/players/pl_wmp.pas b/plugins/Watrack/players/pl_wmp.pas
new file mode 100644
index 0000000000..971cb28ac6
--- /dev/null
+++ b/plugins/Watrack/players/pl_wmp.pas
@@ -0,0 +1,128 @@
+{Windows Media Player}
+unit pl_WMP;
+{$include compilers.inc}
+
+interface
+
+implementation
+uses windows,common,messages,srv_player,wat_api
+ {$IFDEF DELPHI_7_UP}
+ ,variants
+ {$ENDIF}
+ {$IFDEF KOL_MCK}
+ ,kolcomobj
+ {$ELSE}
+ ,ComObj
+ {$ENDIF}
+;
+
+const
+ WMPOld:boolean=false;
+
+const
+// CLASS_MP :TGUID = '{22D6F312-B0F6-11D0-94AB-0080C74C7E95}';
+// CLASS_WMP:TGUID = '{6BF52A52-394A-11D3-B153-00C04F79FAA6}'; CLASS_WindowsMediaPlayer
+ MPCOMName = 'MediaPlayer.MediaPlayer.1';
+ WMPCOMName = 'WMPlayer.OCX.7';
+const
+ MPClass = 'Media Player 2';
+ WMPClass = 'WMPlayerApp';
+
+function Check(wnd:HWND;flags:integer):HWND;cdecl;
+begin
+ result:=FindWindowEx(0,wnd,MPClass,NIL);
+ if result=0 then
+ begin
+ result:=FindWindowEx(0,wnd,WMPClass,NIL); //?
+ WMPOld:=false;
+ end
+ else
+ WMPOld:=true;
+end;
+
+{ Version detect
+ fHasWMP64 = (WMP64.FileName="") ' WMP64 was create above via OBJECT tag else this returns False.
+ fHasWMP7 = (WMP7.URL = "") ' WMP7 or later was create above via OBJECT tag else this returns False.
+}
+
+const
+ MPVersion:PWideChar = '6.4';
+
+function GetVersionText(flags:integer):PWideChar;
+var
+ v:variant;
+begin
+ if WMPOld then
+ StrDupW(result,MPVersion)
+ else
+ begin
+ try
+ v:=CreateOleObject(WMPCOMName);
+ StrDupW(result,pWideChar(Widestring(v.versionInfo)));
+ except
+ result:=nil;
+ end;
+// VarClear(v);
+ v:=Null;
+ end;
+end;
+
+function GetInfo(var SongInfo:tSongInfo;flags:integer):integer;cdecl;
+begin
+ result:=0;
+ if (flags and WAT_OPT_PLAYERDATA)<>0 then
+ begin
+ if SongInfo.txtver=nil then
+ SongInfo.txtver:=GetVersionText(flags);
+ end;
+end;
+
+{
+function Command(wnd:HWND;cmd:integer;value:integer):integer;cdecl;
+var
+ c:integer;
+begin
+ result:=0;
+ case cmd of
+// WAT_CTRL_PREV : c:=VK_B;
+// WAT_CTRL_PLAY : c:=VK_C;
+// WAT_CTRL_PAUSE: c:=VK_X;
+// WAT_CTRL_STOP : c:=VK_Z;
+// WAT_CTRL_NEXT : c:=VK_V;
+ WAT_CTRL_VOLDN: c:=VK_F9;
+ WAT_CTRL_VOLUP: c:=VK_F10;
+ else
+ exit;
+ end;
+ PostMessageW(wnd,WM_KEYDOWN,c,1);
+end;
+}
+const
+ plRec:tPlayerCell=(
+ Desc :'WMP';
+ flags :WAT_OPT_APPCOMMAND or WAT_OPT_SINGLEINST;
+ Icon :0;
+ Init :nil;
+ DeInit :nil;
+ Check :@Check;
+ GetStatus:nil;
+ GetName :nil;
+ GetInfo :@GetInfo;
+ Command :nil;
+ URL :'http://www.microsoft.com/windows/windowsmedia/players.aspx';
+ Notes :nil);
+
+var
+ LocalPlayerLink:twPlayer;
+
+procedure InitLink;
+begin
+ LocalPlayerLink.Next:=PlayerLink;
+ LocalPlayerLink.This:=@plRec;
+ PlayerLink :=@LocalPlayerLink;
+end;
+
+initialization
+// ServicePlayer(WAT_ACT_REGISTER,dword(@plRec));
+ InitLink;
+end.
diff --git a/plugins/Watrack/plugins/watrack_mpd/Makefile b/plugins/Watrack/plugins/watrack_mpd/Makefile
new file mode 100644
index 0000000000..02c89bd6d3
--- /dev/null
+++ b/plugins/Watrack/plugins/watrack_mpd/Makefile
@@ -0,0 +1,12 @@
+all:
+ i686-pc-mingw32-gcc -c -DBUILD_DLL -D UNICODE -D _UNICODE *.c -I../../include -I/usr/i686-pc-mingw32/usr/include -I. -w -mwin32 -mwindows -mdll -march=i686 -msse -O2 -pipe
+ i686-pc-mingw32-windres -i watrack_mpd.rc -o resources.o
+ i686-pc-mingw32-gcc -shared -o watrack_mpd.dll *.o -Wl,-O1,-s
+ upx -9 watrack_mpd.dll
+
+clean:
+ rm *.o
+
+clean-all:
+ rm *.o *.dll
+
diff --git a/plugins/Watrack/plugins/watrack_mpd/res/watrack_mpd.rc b/plugins/Watrack/plugins/watrack_mpd/res/watrack_mpd.rc
new file mode 100644
index 0000000000..17aaad10e4
--- /dev/null
+++ b/plugins/Watrack/plugins/watrack_mpd/res/watrack_mpd.rc
@@ -0,0 +1,112 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "src/resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Russian resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS)
+#ifdef _WIN32
+LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
+#pragma code_page(1251)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // Russian resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_OPT_WA_MPD DIALOGEX 0, 0, 268, 214
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+FONT 8, "MS Shell Dlg", 400, 0, 0x0
+BEGIN
+ EDITTEXT IDC_SERVER,71,18,72,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_PORT,72,38,72,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_PASSWORD,71,58,73,14,ES_PASSWORD | ES_AUTOHSCROLL
+ LTEXT "MPD Host",IDC_STATIC,7,20,32,8
+ LTEXT "Port",IDC_STATIC,7,42,14,8
+ LTEXT "Password",IDC_STATIC,7,63,32,8
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_OPT_WA_MPD, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 261
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 207
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/plugins/Watrack/plugins/watrack_mpd/src/commonheaders.h b/plugins/Watrack/plugins/watrack_mpd/src/commonheaders.h
new file mode 100644
index 0000000000..85eb73a335
--- /dev/null
+++ b/plugins/Watrack/plugins/watrack_mpd/src/commonheaders.h
@@ -0,0 +1,43 @@
+// Copyright © 2008 sss, chaos.persei
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#ifndef COMMONHEADERS_H
+#define COMMONHEADERS_H
+#include <windows.h>
+#include <tchar.h>
+#include <malloc.h>
+#include <time.h>
+
+
+#define MIRANDA_VER 0x0800
+#include <newpluginapi.h>
+#include <m_netlib.h>
+//#include <m_clist.h>
+//#include <m_skin.h>
+#include <m_database.h>
+//#include <m_protosvc.h>
+//#include <m_protocols.h>
+//#include <m_system.h>
+#include <m_options.h>
+#include <m_langpack.h>
+
+#include "resource.h"
+
+#include "constants.h"
+#include "globals.h"
+#include "main.h"
+#include "m_music.h"
+#include "utilities.h"
+#endif
diff --git a/plugins/Watrack/plugins/watrack_mpd/src/constants.h b/plugins/Watrack/plugins/watrack_mpd/src/constants.h
new file mode 100644
index 0000000000..2ac58a4d43
--- /dev/null
+++ b/plugins/Watrack/plugins/watrack_mpd/src/constants.h
@@ -0,0 +1,4 @@
+#ifndef CONSTANTS_H
+#define CONSTANTS_H
+#define szModuleName "Watrack_MPD"
+#endif
diff --git a/plugins/Watrack/plugins/watrack_mpd/src/globals.h b/plugins/Watrack/plugins/watrack_mpd/src/globals.h
new file mode 100644
index 0000000000..edc5bd10c3
--- /dev/null
+++ b/plugins/Watrack/plugins/watrack_mpd/src/globals.h
@@ -0,0 +1,11 @@
+#ifndef GLOBALS_H
+#define GLOBALS_H
+HANDLE ghNetlibUser;
+HANDLE ghConnection;
+HANDLE ghPacketReciever;
+BOOL bWatrackService;
+TCHAR *gbHost, *gbPassword;
+WORD gbPort;
+BOOL Connected;
+int gbState;
+#endif
diff --git a/plugins/Watrack/plugins/watrack_mpd/src/init.c b/plugins/Watrack/plugins/watrack_mpd/src/init.c
new file mode 100644
index 0000000000..4d4f9eba29
--- /dev/null
+++ b/plugins/Watrack/plugins/watrack_mpd/src/init.c
@@ -0,0 +1,112 @@
+// Copyright © 2009-2010 sss
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "commonheaders.h"
+
+
+#define PLUGIN_NAME "Watrack_MPD"
+
+HINSTANCE hInst;
+BOOL bWatrackService = FALSE;
+int hLangpack = 0;
+static int OnModulesLoaded(WPARAM wParam,LPARAM lParam);
+extern char *date();
+extern int WaMpdOptInit(WPARAM wParam,LPARAM lParam);
+
+
+PLUGININFOEX pluginInfo={
+ sizeof(PLUGININFOEX),
+ 0,
+ PLUGIN_MAKE_VERSION(0,0,0,4),
+ "Music Player Daemon support for watrack",
+ "sss, others..",
+ "sss123next@list.ru",
+ "© 2009 sss, others...",
+ "http://sss.chaoslab.ru:81/tracker/mim_plugs/",
+ 1, //unicode
+ { 0x692e87d0, 0x6c71, 0x4cdc, { 0x9e, 0x36, 0x2b, 0x2d, 0x69, 0xfb, 0xdc, 0x4c } }
+
+};
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
+{
+ hInst=hinstDLL;
+ return TRUE;
+}
+
+__declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ {
+ static char plugname[52];
+ strcpy(plugname, PLUGIN_NAME" [");
+ strcat(plugname, date());
+ strcat(plugname, " ");
+ strcat(plugname, __TIME__);
+ strcat(plugname, "]");
+ pluginInfo.shortName = plugname;
+ }
+ return &pluginInfo;
+}
+
+static const MUUID interfaces[] = {MIID_SERVICEMODE, MIID_LAST};
+__declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+int __declspec(dllexport) Load()
+{
+ mir_getLP(&pluginInfo);
+ HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded);
+ return 0;
+}
+
+void InitVars()
+{
+ gbPort = DBGetContactSettingWord(NULL, szModuleName, "Port", 6600);
+ gbPassword = (TCHAR*)malloc(64*sizeof(TCHAR));
+ gbHost = (TCHAR*)malloc(128*sizeof(TCHAR));
+ gbHost = UniGetContactSettingUtf(NULL, szModuleName, "Server", _T("127.0.0.1"));
+ gbPassword = UniGetContactSettingUtf(NULL, szModuleName, "Password", _T(""));
+}
+
+
+extern void RegisterPlayer();
+static int OnModulesLoaded(WPARAM wParam,LPARAM lParam)
+{
+ HANDLE hHookOptionInit;
+ NETLIBUSER nlu = {0};
+ nlu.cbSize = sizeof(nlu);
+ nlu.flags = (NUF_OUTGOING | NUF_HTTPCONNS);
+ nlu.szDescriptiveName = "Watrack MPD connection";
+ nlu.szSettingsModule = PLUGIN_NAME;
+ ghNetlibUser = (HANDLE)CallService(MS_NETLIB_REGISTERUSER, 0, (LPARAM)&nlu);
+ InitVars();
+ hHookOptionInit = HookEvent(ME_OPT_INITIALISE, WaMpdOptInit);
+ if (ServiceExists("WATrack/Player"))
+ bWatrackService = TRUE;
+ RegisterPlayer();
+
+ return 0;
+}
+
+
+int __declspec(dllexport) Unload(void)
+{
+ free(gbHost);
+ free(gbPassword);
+ return 0;
+}
diff --git a/plugins/Watrack/plugins/watrack_mpd/src/m_music.h b/plugins/Watrack/plugins/watrack_mpd/src/m_music.h
new file mode 100644
index 0000000000..4b33881f06
--- /dev/null
+++ b/plugins/Watrack/plugins/watrack_mpd/src/m_music.h
@@ -0,0 +1,355 @@
+#ifndef M_MUSIC
+#define M_MUSIC
+
+#define MIID_WATRACK {0xfc6c81f4, 0x837e, 0x4430, {0x96, 0x01, 0xa0, 0xaa, 0x43, 0x17, 0x7a, 0xe3}}
+
+typedef struct tSongInfoA {
+ char *artist;
+ char *title;
+ char *album;
+ char *genre;
+ char *comment;
+ char *year;
+ char *mfile; // media file
+ int kbps;
+ int khz;
+ int channels;
+ int track;
+ int total; // music length
+ int time; // elapsed time
+ char *wndtext; // window title
+ char *player; // player name
+ int plyver; // player version
+ HANDLE icon; // player icon
+ int fsize; // media file size
+ int vbr;
+ int status; // player status: 0 - stopped; 1 - playing; 2 - paused
+ HWND plwnd; // player window
+ // video part
+ int codec;
+ int width;
+ int height;
+ int fps;
+ __int64 date;
+ char *txtver;
+ char *lyric;
+ char *cover;
+ int volume;
+ char *url;
+} SONGINFOA, *LPSONGINFOA;
+
+typedef struct tSongInfo {
+ wchar_t *artist;
+ wchar_t *title;
+ wchar_t *album;
+ wchar_t *genre;
+ wchar_t *comment;
+ wchar_t *year;
+ wchar_t *mfile; // media file
+ int kbps;
+ int khz;
+ int channels;
+ int track;
+ int total; // music length
+ int time; // elapsed time
+ wchar_t *wndtext; // window title
+ wchar_t *player; // player name
+ int plyver; // player version
+ HANDLE icon; // player icon
+ int fsize; // media file size
+ int vbr;
+ int status; // player status: 0 - stopped; 1 - playing; 2 - paused
+ HWND plwnd; // player window
+ // video part
+ int codec;
+ int width;
+ int height;
+ int fps;
+ __int64 date;
+ wchar_t *txtver;
+ // not implemented yet
+ wchar_t *lyric;
+ wchar_t *cover;
+ int volume;
+ wchar_t *url;
+} SONGINFO, *LPSONGINFO;
+
+#if defined(_UNICODE)
+ #define WAT_INF_TCHAR WAT_INF_UNICODE
+ #define SongInfoT tSongInfo
+#else
+ #define WAT_INF_TCHAR WAT_INF_ANSI
+ #define SongInfoT tSongInfoA
+#endif
+
+ // result codes
+#define WAT_RES_UNKNOWN -2
+#define WAT_RES_NOTFOUND -1
+#define WAT_RES_ERROR WAT_RES_NOTFOUND
+#define WAT_RES_OK 0
+#define WAT_RES_ENABLED WAT_RES_OK
+#define WAT_RES_DISABLED 1
+ // internal
+#define WAT_RES_NEWFILE 3
+
+#define WAT_PLS_NORMAL WAT_RES_OK
+#define WAT_PLS_NOMUSIC WAT_RES_DISABLED
+#define WAT_PLS_NOTFOUND WAT_RES_NOTFOUND
+
+#define WAT_INF_UNICODE 0
+#define WAT_INF_ANSI 1
+#define WAT_INF_UTF8 2
+#define WAT_INF_CHANGES 0x100
+
+/*
+ wParam : WAT_INF_* constant
+ lParam : pointer to LPSONGINGO (Unicode) or LPSONGINFOA (ANSI/UTF8)
+ Affects: Fill structure by currently played music info
+ returns: WAT_PLS_* constant
+ note: pointer will be point to global SONGINFO structure of plugin
+ warning: Non-Unicode data filled only by request
+ if lParam=0 only internal SongInfo structure will be filled
+ Example:
+ LPSONGINFO p;
+ PluginLink->CallService(MS_WAT_GETMUSICINFO,0,(DWORD)&p);
+*/
+
+#define MS_WAT_GETMUSICINFO "WATrack/GetMusicInfo"
+
+/*
+ wParam:0
+ lParam : pointer to pSongInfo (Unicode)
+ Affects: Fill structure by info from file named in SongInfo.mfile
+ returns: 0, if success
+ note: fields, which values can't be obtained, leaves old values.
+ you must free given strings by miranda mmi.free
+*/
+#define MS_WAT_GETFILEINFO "WATrack/GetFileInfo"
+
+/*
+ wParam: encoding (WAT_INF_* consts, 0 = WAT_INF_UNICODE)
+ lParam: codepage (0 = ANSI)
+ Returns Global unicode SongInfo pointer or tranlated to Ansi/UTF8 structure
+*/
+#define MS_WAT_RETURNGLOBAL "WATrack/GetMainStructure"
+
+#define WAT_CTRL_PREV 1
+#define WAT_CTRL_PLAY 2
+#define WAT_CTRL_PAUSE 3
+#define WAT_CTRL_STOP 4
+#define WAT_CTRL_NEXT 5
+#define WAT_CTRL_VOLDN 6
+#define WAT_CTRL_VOLUP 7
+#define WAT_CTRL_SEEK 8 // lParam is new position (sec)
+/*
+ wParam: button code (WAT_CTRL_* const)
+ lParam: 0, or value (see WAT_CTRL_* const comments)
+ Affects: emulate player button pressing
+ returns: 0 if unsuccesful
+*/
+#define MS_WAT_PRESSBUTTON "WATrack/PressButton"
+
+/*
+ Get user's Music Info
+*/
+#define MS_WAT_GETCONTACTINFO = "WATrack/GetContactInfo"
+
+// ------------ Plugin/player status ------------
+
+/*
+ wParam: 1 - switch off plugin
+ 0 - switch on plugin
+ -1 - switch plugin status
+ 2 - get plugin version
+ other - get plugin status
+ lParam: 0
+ Affects: Switch plugin status to enabled or disabled
+ returns: version, old plugin status, 0, if was enabled
+*/
+
+#define MS_WAT_PLUGINSTATUS "WATrack/PluginStatus"
+
+#define ME_WAT_MODULELOADED "WATrack/ModuleLoaded"
+
+#define WAT_EVENT_PLAYERSTATUS 1 // 0-normal; 1-no music (possibly stopped); 2-not found
+#define WAT_EVENT_NEWTRACK 2
+#define WAT_EVENT_PLUGINSTATUS 3 // 0-enabled; 1-dis.temporary; 2-dis.permanent
+#define WAT_EVENT_NEWPLAYER 4 //
+#define WAT_EVENT_NEWTEMPLATE 5 // TM_* constant
+
+/*
+ Plugin or player status changed:
+ wParam: type of event (see above)
+ lParam: value
+*/
+#define ME_WAT_NEWSTATUS "WATrack/NewStatus"
+
+// ---------- Popup module ------------
+
+/*
+ wParam: not used
+ lParam: not used
+ Affects: Show popup or Info window with current music information
+ note: Only Info window will be showed if Popup plugin disabled
+*/
+
+#define MS_WAT_SHOWMUSICINFO "WATrack/ShowMusicInfo"
+
+// --------- Statistic (report) module -------------
+
+/*
+ wParam: pointer to log file name or NULL
+ lParam: pointer to report file name or NULL
+ Affects: Create report from log and run it (if option is set)
+ returns: 0 if unsuccesful
+ note: if wParam or lParam is a NULL then file names from options are used
+*/
+#define MS_WAT_MAKEREPORT "WATrack/MakeReport"
+
+/*
+ wParam, lParam - not used
+ Affects: pack statistic file
+*/
+#define MS_WAT_PACKLOG = "WATrack/PackLog"
+
+/*
+ wParam: not used
+ lParam: pointer to SongInfo
+*/
+#define MS_WAT_ADDTOLOG = "WATrack/AddToLog"
+
+// ----------- Formats and players -----------
+
+// media file status
+
+#define WAT_MES_STOPPED 0
+#define WAT_MES_PLAYING 1
+#define WAT_MES_PAUSED 2
+#define WAT_MES_UNKNOWN -1
+
+#define WAT_ACT_REGISTER 1
+#define WAT_ACT_UNREGISTER 2
+#define WAT_ACT_DISABLE 3
+#define WAT_ACT_ENABLE 4
+#define WAT_ACT_GETSTATUS 5 // not found/enabled/disabled
+#define WAT_ACT_SETACTIVE 6
+#define WAT_ACT_REPLACE 0x10000 // can be combined with WAT_REGISTERFORMAT
+
+ // flags
+#define WAT_OPT_DISABLED 0x00000001 // format registered but disabled
+#define WAT_OPT_ONLYONE 0x00000002 // format can't be overwriten
+#define WAT_OPT_PLAYERINFO 0x00000004 // song info from player
+#define WAT_OPT_WINAMPAPI 0x00000008 // Winamp API support
+#define WAT_OPT_CHECKTIME 0x00000010 // check file time for changes
+#define WAT_OPT_VIDEO 0x00000020 // only for format registering used
+#define WAT_OPT_LAST 0x00000040 // (internal)
+#define WAT_OPT_FIRST 0x00000080 // (internal)
+#define WAT_OPT_TEMPLATE 0x00000100 // (internal)
+#define WAT_OPT_IMPLANTANT 0x00000200 // use process implantation
+#define WAT_OPT_HASURL 0x00000400 // (player registration) URL field present
+#define WAT_OPT_CHANGES 0x00000800 // obtain only chaged values
+ // (volume, status, window text, elapsed time)
+#define WAT_OPT_APPCOMMAND 0x00001000 // Special (multimedia) key support
+#define WAT_OPT_CHECKALL 0x00002000 // Check all players
+#define WAT_OPT_KEEPOLD 0x00004000 // Keep Old opened file
+#define WAT_OPT_MULTITHREAD 0x00008000 // Use multithread scan
+#define WAT_OPT_SINGLEINST 0x00010000 // Single player instance
+
+
+typedef BOOL (__cdecl *LPREADFORMATPROC)(LPSONGINFO Info);
+
+typedef struct tMusicFormat {
+ LPREADFORMATPROC proc;
+ char ext[8];
+ int flags;
+} MUSICFORMAT, *LPMUSICFORMAT;
+
+/*
+ wParam: action
+ lParam: pointer to MUSICFORMAT if wParam = WAT_ACT_REGISTER,
+ else - pointer to extension string (ANSI)
+ returns: see result codes
+*/
+
+#define MS_WAT_FORMAT "WATrack/Format"
+
+/*
+ wParam - pointer to SONGINFO structure (plwind field must be initialized)
+ lParam - flags
+*/
+
+#define MS_WAT_WINAMPINFO "WATrack/WinampInfo"
+
+/*
+ wParam: window
+ lParam: LoWord - command; HiWord - value
+*/
+
+#define MS_WAT_WINAMPCOMMAND "WATrack/WinampCommand"
+
+int tInitProc();
+int tDeInitProc();
+int tStatusProc();
+
+typedef int (__cdecl *LPINITPROC)();
+typedef int (__cdecl *LPDEINITPROC)();
+typedef int (__cdecl *LPSTATUSPROC)(HWND wnd);
+typedef wchar_t (__cdecl *LPNAMEPROC)(HWND wnd, int flags);
+typedef HWND (__cdecl *LPCHECKPROC)(HWND wnd, int flags);
+typedef int (__cdecl *LPGETSTATUSPROC) (HWND wnd);
+typedef int (__cdecl *LPINFOPROC)(LPSONGINFO Info, int flags);
+typedef int (__cdecl *LPCOMMANDPROC)(HWND wnd, int command, int value);
+
+typedef struct tPlayerCell {
+ char *Desc;
+ int flags;
+ HICON Icon; // can be 0. for registration only
+ LPINITPROC Init;
+ LPDEINITPROC DeInit;
+ LPCHECKPROC Check; // check player
+ LPGETSTATUSPROC GetStatus;
+ LPNAMEPROC GetName; // can be NULL. get media filename
+ LPINFOPROC GetInfo; // can be NULL. get info from player
+ LPCOMMANDPROC Command; // can be NULL. send command to player
+ char *URL; // only if WAT_OPT_HASURL flag present
+ wchar_t *Notes;
+} PLAYERCELL, *LPPLAYERCELL;
+
+/*
+ wParam: action
+ lParam: pointer to PLAYERCELL if wParam = WAT_ACT_REGISTER,
+ else - pointer to player description string (ANSI)
+ returns: player window handle or value>0 if found
+ note: If you use GetName or GetInfo field, please, do not return empty
+ filename even when mediafile is remote!
+*/
+
+#define MS_WAT_PLAYER "WATrack/Player"
+
+// --------- Templates ----------
+
+/*
+ wParam: not used
+ lParam: Unicode template
+ returns: New Unicode (replaced) string
+*/
+#define MS_WAT_REPLACETEXT "WATrack/ReplaceText"
+
+/*
+ event types for History
+ Blob structure for EVENTTYPE_WAT_ANSWER:
+ Uniciode artist#0title#0album#0answer
+*/
+#define EVENTTYPE_WAT_REQUEST 9601
+#define EVENTTYPE_WAT_ANSWER 9602
+#define EVENTTYPE_WAT_ERROR 9603
+#define EVENTTYPE_WAT_MESSAGE 9604
+
+/*
+ wParam: 0 or parent window
+ lParam: 0
+ note: Shows Macro help window with edit aliases ability
+*/
+#define MS_WAT_MACROHELP "WATrack/MacroHelp"
+
+#endif
diff --git a/plugins/Watrack/plugins/watrack_mpd/src/main.c b/plugins/Watrack/plugins/watrack_mpd/src/main.c
new file mode 100644
index 0000000000..17a8c29d64
--- /dev/null
+++ b/plugins/Watrack/plugins/watrack_mpd/src/main.c
@@ -0,0 +1,433 @@
+// Copyright © 2008 sss, chaos.persei
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+
+
+#include "commonheaders.h"
+
+
+//maybe add mutex ?
+void Start(void *param);
+int Parser();
+
+LPINITPROC Init()
+{
+ mir_forkthread(&Start, 0);
+ return 0;
+}
+void Stop();
+LPDEINITPROC DeInit()
+{
+ Stop();
+ return 0;
+}
+LPCHECKPROC CheckPlayer(HWND wnd, int flags)
+{
+ if(!ghConnection)
+ {
+ mir_forkthread(&Start, 0);
+ return 0;
+ }
+ if(Parser())
+ return (LPCHECKPROC)WAT_MES_STOPPED;
+ if(Connected)
+ return (LPCHECKPROC)WAT_MES_PLAYING;
+ return 0;
+}
+LPGETSTATUSPROC GetStatus()
+{
+ if(!ghConnection)
+ {
+ mir_forkthread(&Start, 0);
+ return 0;
+ }
+ if(Parser())
+ return (LPGETSTATUSPROC)-1;
+ return (LPGETSTATUSPROC)(gbState);
+}
+LPNAMEPROC GetFileName(HWND wnd, int flags)
+{
+ if(!ghConnection)
+ {
+ mir_forkthread(&Start, 0);
+ return 0;
+ }
+ return 0;
+}
+SONGINFO SongInfo = {0};
+
+LPINFOPROC GetPlayerInfo(LPSONGINFO info, int flags)
+{
+ if(!ghConnection)
+ {
+ mir_forkthread(&Start, 0);
+ return 0;
+ }
+ if(Parser())
+ return (LPINFOPROC)-1;
+/*
+
+ info->channels = SongInfo.channels;
+ info->codec = SongInfo.codec;
+ info->comment = SongInfo.comment;
+ info->cover = SongInfo.cover;
+ info->date = SongInfo.date;
+ info->fps = SongInfo.fps;
+ info->fsize = SongInfo.fsize;
+
+ info->icon = SongInfo.icon;
+ info->kbps = SongInfo.kbps;
+ info->khz = SongInfo.khz;
+ info->lyric = SongInfo.lyric;
+ info->mfile = SongInfo.mfile;
+ info->player = SongInfo.player;
+ info->plyver = SongInfo.plyver;
+ info->status = SongInfo.status;
+ info->time = SongInfo.time;
+ info->title = SongInfo.title;
+ info->total = SongInfo.total;
+ info->track = SongInfo.track;*/
+ info->total = SongInfo.total;
+ info->time = SongInfo.time;
+ info->mfile = SongInfo.mfile;
+ info->txtver = SongInfo.txtver;
+ info->title = SongInfo.title;
+ info->artist = SongInfo.artist;
+ info->genre = SongInfo.genre;
+ info->album = SongInfo.album;
+ info->year = SongInfo.year;
+ info->kbps = SongInfo.kbps;
+ info->track = SongInfo.track;
+ info->khz = SongInfo.khz;
+ info->volume = SongInfo.volume;
+/* info->url = SongInfo.url; //??
+ info->vbr = SongInfo.vbr;
+ info->volume = SongInfo.volume;
+ */
+ return 0;
+}
+LPCOMMANDPROC SendCommand(HWND wnd, int command, int value)
+{
+ switch (command)
+ {
+ case WAT_CTRL_PREV:
+ Netlib_Send(ghConnection, "previous\n", strlen("previous\n"), 0);
+ break;
+ case WAT_CTRL_PLAY: //add resuming support
+ if(gbState != WAT_MES_PAUSED)
+ Netlib_Send(ghConnection, "play\n", strlen("play\n"), 0);
+ else
+ Netlib_Send(ghConnection, "pause 0\n", strlen("pause 0\n"), 0);
+ break;
+ case WAT_CTRL_PAUSE:
+ Netlib_Send(ghConnection, "pause 1\n", strlen("pause 1\n"), 0);
+ break;
+ case WAT_CTRL_STOP:
+ Netlib_Send(ghConnection, "stop\n", strlen("stop\n"), 0);
+ break;
+ case WAT_CTRL_NEXT:
+ Netlib_Send(ghConnection, "next\n", strlen("next\n"), 0);
+ break;
+ case WAT_CTRL_VOLDN:
+ break;
+ case WAT_CTRL_VOLUP:
+ break;
+ case WAT_CTRL_SEEK:
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+void RegisterPlayer()
+{
+ if(!bWatrackService)
+ return;
+ {
+ PLAYERCELL player = {0};
+ player.Desc = "Music Player Daemon";
+ player.Check = (LPCHECKPROC)CheckPlayer;
+ player.Init = (LPINITPROC)Init;
+ player.DeInit = (LPDEINITPROC)DeInit;
+ player.GetStatus = (LPGETSTATUSPROC)GetStatus;
+ player.Command = (LPCOMMANDPROC)SendCommand;
+ player.flags = (WAT_OPT_HASURL|WAT_OPT_SINGLEINST|WAT_OPT_PLAYERINFO);
+ player.GetName = (LPNAMEPROC)GetFileName;
+ player.GetInfo = (LPINFOPROC)GetPlayerInfo;
+// player.Icon = //TODO:implement icon support
+ player.Notes = _T("mpd is a nice music player for *nix which have not any gui, just daemon.\nuses very small amount of ram, cpu.");
+ player.URL = "http://www.musicpd.org";
+ CallService(MS_WAT_PLAYER, (WPARAM)WAT_ACT_REGISTER, (LPARAM)&player);
+ }
+}
+void ReStart(void *data);
+int Parser()
+{
+ static NETLIBPACKETRECVER nlpr = {0};
+ char *ptr;
+ char tmp[256];
+ int i;
+ char *buf;
+ static char ver[16];
+ nlpr.cbSize = sizeof(nlpr);
+ nlpr.dwTimeout = 5;
+ if(!ghConnection)
+ {
+ mir_forkthread(&Start, 0);
+ }
+ if(ghConnection)
+ {
+ int recvResult;
+/* do
+ {
+ recvResult = CallService(MS_NETLIB_GETMOREPACKETS,(WPARAM)ghPacketReciever, (LPARAM)&nlpr);
+ if(recvResult == SOCKET_ERROR)
+ {
+ ReStart();
+ return 1;
+ }
+ }
+ while(recvResult > 0);*/
+ if(!Connected)
+ {
+ char tmp[128];
+ char *tmp2 = mir_t2a(gbPassword);
+ recvResult = CallService(MS_NETLIB_GETMOREPACKETS,(WPARAM)ghPacketReciever, (LPARAM)&nlpr);
+ if(recvResult == SOCKET_ERROR)
+ {
+ mir_forkthread(&ReStart, 0);
+// ReStart();
+ return 1;
+ }
+ if(strlen(tmp2) > 2)
+ {
+ strcpy(tmp, "password ");
+ strcat(tmp, tmp2);
+ strcat(tmp, "\n");
+ Netlib_Send(ghConnection, tmp, strlen(tmp), 0);
+ recvResult = CallService(MS_NETLIB_GETMOREPACKETS,(WPARAM)ghPacketReciever, (LPARAM)&nlpr);
+ if(recvResult == SOCKET_ERROR)
+ {
+ mir_forkthread(&ReStart, 0);
+ return 1;
+ }
+ }
+ mir_free(tmp2);
+ }
+ Netlib_Send(ghConnection, "status\n", strlen("status\n"), 0);
+ recvResult = CallService(MS_NETLIB_GETMOREPACKETS,(WPARAM)ghPacketReciever, (LPARAM)&nlpr);
+ if(recvResult == SOCKET_ERROR)
+ {
+ mir_forkthread(&ReStart, 0);
+ return 1;
+ }
+ Netlib_Send(ghConnection, "currentsong\n", strlen("currentsong\n"), 0);
+ recvResult = CallService(MS_NETLIB_GETMOREPACKETS,(WPARAM)ghPacketReciever, (LPARAM)&nlpr);
+ if(recvResult == SOCKET_ERROR)
+ {
+ mir_forkthread(&ReStart, 0);
+ return 1;
+ }
+ nlpr.bytesUsed = nlpr.bytesAvailable;
+ }
+ buf = nlpr.buffer;
+ if(ptr = strstr(buf, "MPD"))
+ {
+ Connected = TRUE;
+ ptr = &ptr[4];
+ for(i = 0; ((ptr[i] != '\n') && (ptr[i] != '\0')); i++)
+ tmp[i] = ptr[i];
+ tmp[i] = '\0';
+ strcpy(ver, tmp);
+ SongInfo.txtver = (TCHAR*)mir_utf8decodeW(tmp);
+ }
+ else
+ SongInfo.txtver = (TCHAR*)mir_utf8decodeW(ver);
+ if(ptr = strstr(buf, "file:"))
+ {
+ ptr = &ptr[6];
+ for(i = 0; ((ptr[i] != '\n') && (ptr[i] != '\0')); i++)
+ tmp[i] = ptr[i];
+ tmp[i] = '\0';
+ SongInfo.mfile = (TCHAR*)mir_utf8decodeW(tmp);
+ }
+ else
+ SongInfo.mfile = _T("");
+ if(ptr = strstr(buf, "Time:"))
+ {
+ ptr = &ptr[6];
+ for(i = 0; ((ptr[i] != '\n') && (ptr[i] != '\0')); i++)
+ tmp[i] = ptr[i];
+ tmp[i] = '\0';
+ SongInfo.total = atoi(tmp);
+ }
+ else if(!SongInfo.total)
+ SongInfo.total = 0;
+ if(ptr = strstr(buf, "time:"))
+ {
+ ptr = &ptr[6];
+ for(i = 0; ((ptr[i] != '\n') && (ptr[i] != '\0')); i++)
+ tmp[i] = ptr[i];
+ tmp[i] = '\0';
+ SongInfo.time = atoi(tmp);
+ }
+ else if(!SongInfo.time)
+ SongInfo.time = 0;
+ if(ptr = strstr(buf, "Title:"))
+ {
+ ptr = &ptr[7];
+ for(i = 0; ((ptr[i] != '\n') && (ptr[i] != '\0')); i++)
+ tmp[i] = ptr[i];
+ tmp[i] = '\0';
+ SongInfo.title = (TCHAR*)mir_utf8decodeW(tmp);
+ }
+ else
+ SongInfo.title = _T("Unknown track");
+ if(ptr = strstr(buf, "Artist:"))
+ {
+ ptr = &ptr[8];
+ for(i = 0; ((ptr[i] != '\n') && (ptr[i] != '\0')); i++)
+ tmp[i] = ptr[i];
+ tmp[i] = '\0';
+ SongInfo.artist = (TCHAR*)mir_utf8decodeW(tmp);
+ }
+ else
+ SongInfo.artist = _T("Unknown artist");
+ if(ptr = strstr(buf, "Genre:"))
+ {
+ ptr = &ptr[7];
+ for(i = 0; ((ptr[i] != '\n') && (ptr[i] != '\0')); i++)
+ tmp[i] = ptr[i];
+ tmp[i] = '\0';
+ SongInfo.genre = (TCHAR*)mir_utf8decodeW(tmp);
+ }
+ else
+ SongInfo.genre = _T("Unknown genre");
+ if(ptr = strstr(buf, "Album:"))
+ {
+ ptr = &ptr[7];
+ for(i = 0; ((ptr[i] != '\n') && (ptr[i] != '\0')); i++)
+ tmp[i] = ptr[i];
+ tmp[i] = '\0';
+ SongInfo.album = (TCHAR*)mir_utf8decodeW(tmp);
+ }
+ else
+ SongInfo.album = _T("Unknown album");
+ if(ptr = strstr(buf, "Date:"))
+ {
+ ptr = &ptr[6];
+ for(i = 0; ((ptr[i] != '\n') && (ptr[i] != '\0')); i++)
+ tmp[i] = ptr[i];
+ tmp[i] = '\0';
+ SongInfo.year = (TCHAR*)mir_utf8decodeW(tmp);
+ }
+ else
+ SongInfo.year = _T("Unknown year");
+ if(ptr = strstr(buf, "volume:"))
+ {
+ ptr = &ptr[8];
+ for(i = 0; ((ptr[i] != '\n') && (ptr[i] != '\0')); i++)
+ tmp[i] = ptr[i];
+ tmp[i] = '\0';
+ SongInfo.volume = atoi(tmp);
+ }
+ else if(!SongInfo.volume)
+ SongInfo.volume = 0;
+ if(ptr = strstr(buf, "audio:"))
+ {
+ ptr = &ptr[7];
+ for(i = 0; ((ptr[i] != '\n') && (ptr[i] != '\0')); i++)
+ tmp[i] = ptr[i];
+ tmp[i] = '\0';
+ SongInfo.khz = atoi(tmp);
+ }
+ else if(!SongInfo.khz)
+ SongInfo.khz = 0;
+ if(ptr = strstr(buf, "bitrate:"))
+ {
+ ptr = &ptr[9];
+ for(i = 0; ((ptr[i] != '\n') && (ptr[i] != '\0')); i++)
+ tmp[i] = ptr[i];
+ tmp[i] = '\0';
+ SongInfo.kbps = atoi(tmp);
+ }
+ else if(!SongInfo.kbps)
+ SongInfo.kbps = 0;
+
+ if(ptr = strstr(buf, "Track:"))
+ {
+ ptr = &ptr[7];
+ for(i = 0; ((ptr[i] != '\n') && (ptr[i] != '\0')); i++)
+ tmp[i] = ptr[i];
+ tmp[i] = '\0';
+ SongInfo.track = atoi(tmp);
+ }
+ else if(!SongInfo.track)
+ SongInfo.track = 0;
+ if(ptr = strstr(buf, "state:"))
+ {
+ ptr = &ptr[7];
+ for(i = 0; ((ptr[i] != '\n') && (ptr[i] != '\0')); i++)
+ tmp[i] = ptr[i];
+ tmp[i] = '\0';
+ if(strstr(tmp, "play"))
+ gbState = WAT_MES_PLAYING;
+ if(strstr(tmp, "pause"))
+ gbState = WAT_MES_PAUSED;
+ if(strstr(tmp, "stop"))
+ gbState = WAT_MES_STOPPED;
+ }
+ else if(!gbState)
+ gbState = WAT_MES_UNKNOWN;
+ return 0;
+}
+
+
+void Start(void* param)
+{
+ NETLIBOPENCONNECTION nloc = {0};
+ char *tmp = (char*)mir_u2a(gbHost);
+ nloc.cbSize = sizeof(nloc);
+ nloc.szHost = tmp;
+ nloc.timeout = 5;
+ nloc.wPort = gbPort;
+ Connected = FALSE;
+ ghConnection = NetLib_CreateConnection(ghNetlibUser, &nloc);
+ if(ghConnection)
+ ghPacketReciever = (HANDLE)CallService(MS_NETLIB_CREATEPACKETRECVER,(WPARAM)ghConnection,2048);
+}
+void Stop()
+{
+ if(ghPacketReciever)
+ Netlib_CloseHandle(ghPacketReciever);
+ if(ghConnection)
+ Netlib_CloseHandle(ghConnection);
+ if(ghNetlibUser && (ghNetlibUser != INVALID_HANDLE_VALUE))
+ CallService(MS_NETLIB_SHUTDOWN,(WPARAM)ghNetlibUser,0);
+}
+void ReStart(void *param)
+{
+ if(ghPacketReciever)
+ Netlib_CloseHandle(ghPacketReciever);
+ if(ghConnection)
+ Netlib_CloseHandle(ghConnection);
+ Sleep(500);
+ mir_forkthread(&Start, 0);
+}
+
diff --git a/plugins/Watrack/plugins/watrack_mpd/src/main.h b/plugins/Watrack/plugins/watrack_mpd/src/main.h
new file mode 100644
index 0000000000..4dcd949c11
--- /dev/null
+++ b/plugins/Watrack/plugins/watrack_mpd/src/main.h
@@ -0,0 +1,24 @@
+// Copyright © 2008 sss, chaos.persei
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#ifndef MAIN_H
+#define MAIN_H
+
+//TCHAR* __stdcall UniGetContactSettingUtf(HANDLE hContact, const char *szModule,const char* szSetting, TCHAR* szDef);
+//const TCHAR *stristr( const TCHAR *str, const TCHAR *substr);
+
+#endif
+
+
diff --git a/plugins/Watrack/plugins/watrack_mpd/src/options.c b/plugins/Watrack/plugins/watrack_mpd/src/options.c
new file mode 100644
index 0000000000..ef607d78ed
--- /dev/null
+++ b/plugins/Watrack/plugins/watrack_mpd/src/options.c
@@ -0,0 +1,91 @@
+// Copyright © 2008 sss, chaos.persei
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "commonheaders.h"
+#include <uxtheme.h>
+
+HINSTANCE hInst;
+static INT_PTR CALLBACK DlgProcWaMpdOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+int WaMpdOptInit(WPARAM wParam,LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp = { 0 };
+ odp.cbSize = sizeof(odp);
+ odp.hInstance = hInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_WA_MPD);
+ odp.ptszTitle = LPGENT("Winamp Track");
+ odp.ptszGroup = LPGENT("Plugins");
+ odp.ptszTab = LPGENT("Watrack MPD");
+ odp.flags=ODPF_BOLDGROUPS|ODPF_TCHAR;
+ odp.pfnDlgProc = DlgProcWaMpdOpts;
+ Options_AddPage(wParam, &odp);
+ return 0;
+}
+
+
+static INT_PTR CALLBACK DlgProcWaMpdOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+ SetDlgItemInt(hwndDlg, IDC_PORT, DBGetContactSettingWord(NULL, szModuleName, "Port", 6600), FALSE);
+ SetDlgItemText(hwndDlg, IDC_SERVER, UniGetContactSettingUtf(NULL, szModuleName, "Server", _T("127.0.0.1")));
+ SetDlgItemText(hwndDlg, IDC_PASSWORD, UniGetContactSettingUtf(NULL, szModuleName, "Password", _T("")));
+ return TRUE;
+ }
+
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+
+ }
+
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+
+ case WM_NOTIFY:
+ {
+ switch (((LPNMHDR)lParam)->code)
+ {
+
+ case PSN_APPLY:
+ {
+ TCHAR szText[128];
+ DBWriteContactSettingWord(NULL, szModuleName, "Port", (WORD)GetDlgItemInt(hwndDlg, IDC_PORT, NULL, FALSE));
+ gbPort = (WORD)GetDlgItemInt(hwndDlg, IDC_PORT, NULL, FALSE);
+ GetDlgItemText(hwndDlg, IDC_SERVER, szText, sizeof(szText));
+ DBWriteContactSettingTString(NULL, szModuleName, "Server", szText);
+ _tcscpy(gbHost, szText);
+ GetDlgItemText(hwndDlg, IDC_PASSWORD, szText, sizeof(szText));
+ DBWriteContactSettingTString(NULL, szModuleName, "Password", szText);
+ _tcscpy(gbPassword, szText);
+ return TRUE;
+ }
+ }
+ }
+ break;
+ }
+
+ return FALSE;
+}
+
+
+
diff --git a/plugins/Watrack/plugins/watrack_mpd/src/resource.h b/plugins/Watrack/plugins/watrack_mpd/src/resource.h
new file mode 100644
index 0000000000..15f3d472f1
--- /dev/null
+++ b/plugins/Watrack/plugins/watrack_mpd/src/resource.h
@@ -0,0 +1,32 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by watrack_mpd.rc
+//
+#define IDD_OPT_DOS 104
+#define IDD_OPT_WA_MPD 104
+#define IDC_FOR_ONLINE 1003
+#define IDC_MSG_NUMBER 1004
+#define IDC_MSG_TEXT 1005
+#define IDC_MSG_RATE 1006
+#define IDC_ENABLE_CUSTOM_MESSAGE 1007
+#define IDC_FOR_ONLINE2 1008
+#define IDC_CLIENT_BASED 1008
+#define IDC_MESSAGE_SIZE 1009
+#define IDC_MSG_SIZE 1009
+#define IDC_MSG_SIZE2 1010
+#define IDC_CHAR_COUNT 1010
+#define IDC_SERVER 1011
+#define IDC_PORT 1012
+#define IDC_EDIT3 1013
+#define IDC_PASSWORD 1013
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1016
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/plugins/Watrack/plugins/watrack_mpd/src/utilities.c b/plugins/Watrack/plugins/watrack_mpd/src/utilities.c
new file mode 100644
index 0000000000..444a6d210d
--- /dev/null
+++ b/plugins/Watrack/plugins/watrack_mpd/src/utilities.c
@@ -0,0 +1,126 @@
+// Copyright © 2008 sss, chaos.persei
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+#include "commonheaders.h"
+
+/*HANDLE CreateThreadEx(pThreadFuncEx AFunc, void* arg, DWORD* pThreadID)
+{
+ FORK_THREADEX_PARAMS params;
+ DWORD dwThreadId;
+ HANDLE hThread;
+
+ params.pFunc = AFunc;
+ params.arg = arg;
+ params.iStackSize = 0;
+ params.threadID = &dwThreadId;
+ hThread = (HANDLE)CallService(MS_SYSTEM_FORK_THREAD_EX, 0, (LPARAM)&params);
+ if (pThreadID)
+ *pThreadID = dwThreadId;
+
+ return hThread;
+}*/
+
+TCHAR* __stdcall UniGetContactSettingUtf(HANDLE hContact, const char *szModule,const char* szSetting, TCHAR* szDef)
+{
+ DBVARIANT dbv = {DBVT_DELETED};
+ TCHAR* szRes;
+ if (DBGetContactSettingTString(hContact, szModule, szSetting, &dbv))
+ return _tcsdup(szDef);
+ if(dbv.pszVal)
+ szRes = _tcsdup(dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ return szRes;
+}
+
+// case-insensitive _tcsstr
+/*#define NEWTSTR_ALLOCA(A) (A==NULL)?NULL:_tcscpy((TCHAR*)alloca(sizeof(TCHAR)*(_tcslen(A)+1)),A)
+const TCHAR *stristr( const TCHAR *str, const TCHAR *substr)
+{
+ TCHAR *p;
+ TCHAR *str_up = NEWTSTR_ALLOCA(str);
+ TCHAR *substr_up = NEWTSTR_ALLOCA(substr);
+
+ CharUpperBuff(str_up, lstrlen(str_up));
+ CharUpperBuff(substr_up, lstrlen(substr_up));
+
+ p = _tcsstr(str_up, substr_up);
+ return p ? (str + (p - str_up)) : NULL;
+}*/
+
+char *date()
+{
+ static char d[11];
+ char *tmp = __DATE__, m[4], mn[3] = "01";
+ m[0]=tmp[0];
+ m[1]=tmp[1];
+ m[2]=tmp[2];
+ if(strstr(m,"Jan"))
+ strcpy(mn,"01");
+ else if(strstr(m,"Feb"))
+ strcpy(mn,"02");
+ else if(strstr(m,"Mar"))
+ strcpy(mn,"03");
+ else if(strstr(m,"Apr"))
+ strcpy(mn,"04");
+ else if(strstr(m,"May"))
+ strcpy(mn,"05");
+ else if(strstr(m,"Jun"))
+ strcpy(mn,"06");
+ else if(strstr(m,"Jul"))
+ strcpy(mn,"07");
+ else if(strstr(m,"Aug"))
+ strcpy(mn,"08");
+ else if(strstr(m,"Sep"))
+ strcpy(mn,"09");
+ else if(strstr(m,"Oct"))
+ strcpy(mn,"10");
+ else if(strstr(m,"Nov"))
+ strcpy(mn,"11");
+ else if(strstr(m,"Dec"))
+ strcpy(mn,"12");
+ d[0]=tmp[7];
+ d[1]=tmp[8];
+ d[2]=tmp[9];
+ d[3]=tmp[10];
+ d[4]='.';
+ d[5]=mn[0];
+ d[6]=mn[1];
+ d[7]='.';
+ if (tmp[4] == ' ')
+ d[8] = '0';
+ else
+ d[8]=tmp[4];
+ d[9]=tmp[5];
+ return d;
+}
+HANDLE NetLib_CreateConnection(HANDLE hUser, NETLIBOPENCONNECTION* nloc) //from icq )
+{
+ HANDLE hConnection;
+
+ nloc->cbSize = sizeof(NETLIBOPENCONNECTION);
+ nloc->flags |= NLOCF_V2;
+
+ hConnection = (HANDLE)CallService(MS_NETLIB_OPENCONNECTION, (WPARAM)hUser, (LPARAM)nloc);
+ if (!hConnection && (GetLastError() == 87))
+ { // this ensures, an old Miranda will be able to connect also
+ nloc->cbSize = NETLIBOPENCONNECTION_V1_SIZE;
+ hConnection = (HANDLE)CallService(MS_NETLIB_OPENCONNECTION, (WPARAM)hConnection, (LPARAM)nloc);
+ }
+ return hConnection;
+}
+
+
diff --git a/plugins/Watrack/plugins/watrack_mpd/src/utilities.h b/plugins/Watrack/plugins/watrack_mpd/src/utilities.h
new file mode 100644
index 0000000000..8cef30fb25
--- /dev/null
+++ b/plugins/Watrack/plugins/watrack_mpd/src/utilities.h
@@ -0,0 +1,6 @@
+#ifndef UTILITIES_H
+#define UTILITIES_H
+HANDLE NetLib_CreateConnection(HANDLE hUser, NETLIBOPENCONNECTION* nloc);
+HANDLE CreateThreadEx(pThreadFuncEx AFunc, void* arg, DWORD* pThreadID);
+TCHAR* __stdcall UniGetContactSettingUtf(HANDLE hContact, const char *szModule,const char* szSetting, TCHAR* szDef);
+#endif
diff --git a/plugins/Watrack/plugins/watrack_mpd/watrack_mpd.vcxproj b/plugins/Watrack/plugins/watrack_mpd/watrack_mpd.vcxproj
new file mode 100644
index 0000000000..8f8de22140
--- /dev/null
+++ b/plugins/Watrack/plugins/watrack_mpd/watrack_mpd.vcxproj
@@ -0,0 +1,299 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="src\init.c" />
+ <ClCompile Include="src\main.c" />
+ <ClCompile Include="src\options.c" />
+ <ClCompile Include="src\utilities.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="src\commonheaders.h" />
+ <ClInclude Include="src\constants.h" />
+ <ClInclude Include="src\globals.h" />
+ <ClInclude Include="src\main.h" />
+ <ClInclude Include="src\m_music.h" />
+ <ClInclude Include="src\resource.h" />
+ <ClInclude Include="src\utilities.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="res\watrack_mpd.rc" />
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{F29D0C8D-141A-43CF-86B2-34A04653F8D4}</ProjectGuid>
+ <RootNamespace>watrack_mpd</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">
+ <CharacterSet>Unicode</CharacterSet>
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Configuration)\Plugins\</OutDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Configuration)\Obj\$(ProjectName)\</IntDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(Configuration)\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\</OutDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\Debug\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\</IntDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\Debug\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental>
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Midl>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ <TypeLibraryName>.\Release Unicode/testplug.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Full</Optimization>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <AdditionalIncludeDirectories>..\..\include;..\..\plugins\ExternalAPI;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;FULL_BUILD;%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0809</Culture>
+ </ResourceCompile>
+ <Link>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <DataExecutionPrevention>
+ </DataExecutionPrevention>
+ <ImportLibrary>.\Release Unicode/testplug.lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <AdditionalLibraryDirectories>$(SolutionDir)\lib</AdditionalLibraryDirectories>
+ </Link>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Release Unicode/testplug.bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Midl>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TypeLibraryName>.\Release Unicode/testplug.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Full</Optimization>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <AdditionalIncludeDirectories>..\..\include;..\..\plugins\ExternalAPI;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;FULL_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <PrecompiledHeaderOutputFile>.\Release Unicode/testplug.pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\Release Unicode/</AssemblerListingLocation>
+ <ObjectFileName>.\Release Unicode/</ObjectFileName>
+ <ProgramDataBaseFileName>.\Release Unicode/</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0809</Culture>
+ </ResourceCompile>
+ <Link>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <DataExecutionPrevention>
+ </DataExecutionPrevention>
+ <ImportLibrary>.\Release Unicode/testplug.lib</ImportLibrary>
+ <AdditionalLibraryDirectories>$(SolutionDir)\lib</AdditionalLibraryDirectories>
+ <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);mir_core64.lib;Miranda64.lib</AdditionalDependencies>
+ </Link>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Release Unicode/testplug.bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Midl>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ <TypeLibraryName>.\Debug/testplug.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>X:\install\git\miranda\miranda-im\miranda\include;../../include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;TESTPLUG_EXPORTS;_CRT_SECURE_NO_WARNINGS;FULL_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <PrecompiledHeaderOutputFile>.\Debug/testplug.pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\Debug/</AssemblerListingLocation>
+ <ObjectFileName>.\Debug/</ObjectFileName>
+ <ProgramDataBaseFileName>.\Debug/</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0809</Culture>
+ </ResourceCompile>
+ <Link>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <DataExecutionPrevention>
+ </DataExecutionPrevention>
+ <ImportLibrary>.\Debug/testplug.lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Debug/testplug.bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Midl>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TypeLibraryName>.\Debug/testplug.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>X:\install\git\miranda\miranda-im\miranda\include;../../include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;TESTPLUG_EXPORTS;_CRT_SECURE_NO_WARNINGS;FULL_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <PrecompiledHeaderOutputFile>.\Debug/testplug.pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\Debug/</AssemblerListingLocation>
+ <ObjectFileName>.\Debug/</ObjectFileName>
+ <ProgramDataBaseFileName>.\Debug/</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0809</Culture>
+ </ResourceCompile>
+ <Link>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <DataExecutionPrevention>
+ </DataExecutionPrevention>
+ <ImportLibrary>.\Debug/testplug.lib</ImportLibrary>
+ </Link>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Debug/testplug.bsc</OutputFile>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/plugins/Watrack/plugins/watrack_mpd/watrack_mpd.vcxproj.filters b/plugins/Watrack/plugins/watrack_mpd/watrack_mpd.vcxproj.filters
new file mode 100644
index 0000000000..67721aee48
--- /dev/null
+++ b/plugins/Watrack/plugins/watrack_mpd/watrack_mpd.vcxproj.filters
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Sources">
+ <UniqueIdentifier>{2c37ce60-87c1-44a6-85ff-147b48dd84f4}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Headers">
+ <UniqueIdentifier>{1d016f21-3dfc-4617-a16d-2543db03d939}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Resources">
+ <UniqueIdentifier>{67db7959-3d43-439b-ae9b-8223911c651d}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="src\init.c">
+ <Filter>Sources</Filter>
+ </ClCompile>
+ <ClCompile Include="src\main.c">
+ <Filter>Sources</Filter>
+ </ClCompile>
+ <ClCompile Include="src\options.c">
+ <Filter>Sources</Filter>
+ </ClCompile>
+ <ClCompile Include="src\utilities.c">
+ <Filter>Sources</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="src\commonheaders.h">
+ <Filter>Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="src\constants.h">
+ <Filter>Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="src\globals.h">
+ <Filter>Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="src\m_music.h">
+ <Filter>Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="src\main.h">
+ <Filter>Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="src\resource.h">
+ <Filter>Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="src\utilities.h">
+ <Filter>Headers</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="res\watrack_mpd.rc">
+ <Filter>Resources</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/plugins/Watrack/popup/pop_dlg.inc b/plugins/Watrack/popup/pop_dlg.inc
new file mode 100644
index 0000000000..129b538780
--- /dev/null
+++ b/plugins/Watrack/popup/pop_dlg.inc
@@ -0,0 +1,179 @@
+{PopUp Option Dialog}
+
+// PopUp options
+const
+ DLGPOPUP = 'POPUP';
+
+function DlgPopUpOpt(Dialog:HWnd;hMessage:Uint;wParam:WPARAM;lParam:LPARAM):LRESULT; stdcall;
+const
+ dlginit:boolean=false;
+var
+ tmp:longbool;
+ ppd:PPOPUPDATAW;
+ fore,back:HWND;
+begin
+ result:=0;
+ case hMessage of
+ WM_INITDIALOG: begin
+ TranslateDialogDefault(Dialog);
+ dlginit:=false;
+
+ SetDlgItemTextW(Dialog,IDC_POPUP_TITLE,PopTitle);
+ SetDlgItemTextW(Dialog,IDC_POPUP_TEXT ,PopText);
+
+ CheckDlgButton(Dialog,IDC_SHOWFILE ,PopUpFile);
+ CheckDlgButton(Dialog,IDC_REQUEST ,PopRequest);
+
+ CheckDlgButton(Dialog,IDC_ACTLEFTCLOSE ,ord(LoByte(PopUpAction)=0));
+ CheckDlgButton(Dialog,IDC_ACTLEFTINFO ,ord(LoByte(PopUpAction)=1));
+ CheckDlgButton(Dialog,IDC_ACTLEFTPLAYER ,ord(LoByte(PopUpAction)=2));
+ CheckDlgButton(Dialog,IDC_ACTLEFTNEXT ,ord(LoByte(PopUpAction)=3));
+ CheckDlgButton(Dialog,IDC_ACTRIGHTCLOSE ,ord(HiByte(PopUpAction)=0));
+ CheckDlgButton(Dialog,IDC_ACTRIGHTINFO ,ord(HiByte(PopUpAction)=1));
+ CheckDlgButton(Dialog,IDC_ACTRIGHTPLAYER,ord(HiByte(PopUpAction)=2));
+ CheckDlgButton(Dialog,IDC_ACTRIGHTNEXT ,ord(HiByte(PopUpAction)=3));
+
+ CheckDlgButton(Dialog,IDC_USEBUTTONS,PopUpButtons);
+
+ SetDlgItemInt (Dialog,IDC_DELAY,PopUpPause,false);
+
+ if PopUpDelay<0 then
+ CheckDlgButton(Dialog,IDC_DELAYPERM,BST_CHECKED)
+ else if PopUpDelay=0 then
+ CheckDlgButton(Dialog,IDC_DELAYDEF,BST_CHECKED)
+ else
+ CheckDlgButton(Dialog,IDC_DELAYCUST,BST_CHECKED);
+ if PopUpDelay<=0 then
+ EnableWindow(GetDlgItem(Dialog,IDC_DELAY),false);
+
+ SendDlgItemMessage(Dialog,IDC_MACRO_HELP,BM_SETIMAGE,IMAGE_ICON,
+ CallService(MS_SKIN_LOADICON,SKINICON_OTHER_HELP,0));
+
+ fore:=GetDlgItem(Dialog,IDC_FORE);
+ back:=GetDlgItem(Dialog,IDC_BACK);
+ SendMessage(fore,CPM_SETCOLOUR,0,PopUpFore);
+ SendMessage(fore,CPM_SETDEFAULTCOLOUR,0,GetSysColor(COLOR_BTNTEXT));
+ SendMessage(back,CPM_SETCOLOUR,0,PopUpBack);
+ SendMessage(back,CPM_SETDEFAULTCOLOUR,0,GetSysColor(COLOR_BTNFACE));
+ SetDlgItemInt(Dialog,IDC_FORE,PopUpFore,false);
+ SetDlgItemInt(Dialog,IDC_BACK,PopUpBack,false);
+ if PopUpColor<2 then
+ begin
+ EnableWindow(fore,false);
+ EnableWindow(back,false);
+ end;
+ case PopUpColor of
+ 0: CheckDlgButton(Dialog,IDC_COLORDEF ,BST_CHECKED);
+ 1: CheckDlgButton(Dialog,IDC_COLORWIN ,BST_CHECKED);
+ 2: CheckDlgButton(Dialog,IDC_COLORCUST,BST_CHECKED);
+ end;
+ dlginit:=true;
+ end;
+
+ WM_COMMAND: begin
+ if (wParam shr 16)=BN_CLICKED then
+ begin
+ fore:=GetDlgItem(Dialog,IDC_FORE);
+ back:=GetDlgItem(Dialog,IDC_BACK);
+ case LoWord(wParam) of
+ IDC_MACRO_HELP: CallService(MS_WAT_MACROHELP,Dialog,0);
+ IDC_TEST: begin
+ mGetMem(ppd,SizeOf(TPOPUPDATAW));
+ FillChar(ppd^,SizeOf(ppd^),0);
+ ppd^.lchIcon:=LoadSkinnedIcon(SKINICON_OTHER_MIRANDA);
+ StrCopyW(ppd^.lpwzContactName,TranslateW('popup test'));
+ StrCopyW(ppd^.lpwzText,TranslateW('second line'));
+ if IsDlgButtonChecked(Dialog,IDC_COLORDEF)=BST_CHECKED then
+ begin
+ ppd^.colorBack:=0;
+ ppd^.colorText:=0;
+ end
+ else if IsDlgButtonChecked(Dialog,IDC_COLORWIN)=BST_CHECKED then
+ begin
+ ppd^.colorBack:=GetSysColor(COLOR_BTNFACE);
+ ppd^.colorText:=GetSysColor(COLOR_BTNTEXT);
+ end
+ else
+ begin
+ ppd^.colorBack:=SendMessage(back,CPM_GETCOLOUR,0,0);
+ ppd^.colorText:=SendMessage(fore,CPM_GETCOLOUR,0,0);
+ end;
+ CallService(MS_POPUP_ADDPOPUPW,twparam(ppd),0);
+ mFreeMem(ppd);
+ end;
+ IDC_DELAYCUST:
+ EnableWindow(GetDlgItem(Dialog,IDC_DELAY),true);
+ IDC_DELAYDEF,IDC_DELAYPERM:
+ EnableWindow(GetDlgItem(Dialog,IDC_DELAY),false);
+ IDC_COLORCUST: begin
+ EnableWindow(fore,true);
+ EnableWindow(back,true);
+ end;
+ IDC_COLORDEF,IDC_COLORWIN: begin
+ EnableWindow(fore,false);
+ EnableWindow(back,false);
+ end;
+ end;
+ end;
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ result:=0;
+ end;
+
+ WM_NOTIFY: begin
+ if dlginit then
+ begin
+ if integer(PNMHdr(lParam)^.code)=PSN_APPLY then
+ begin
+
+ mFreeMem(PopTitle);
+ mFreeMem(PopText);
+ PopTitle:=GetDlgText(Dialog,IDC_POPUP_TITLE);
+ PopText :=GetDlgText(Dialog,IDC_POPUP_TEXT);
+
+ PopUpButtons:=IsDlgButtonChecked(Dialog,IDC_USEBUTTONS);
+
+ PopUpFile :=IsDlgButtonChecked(Dialog,IDC_SHOWFILE);
+ PopRequest:=IsDlgButtonChecked(Dialog,IDC_REQUEST);
+//color
+ if IsDlgButtonChecked(Dialog,IDC_COLORDEF)=BST_CHECKED then
+ PopUpColor:=0
+ else if IsDlgButtonChecked(Dialog,IDC_COLORWIN)=BST_CHECKED then
+ PopUpColor:=1
+ else
+ begin
+ PopUpColor:=2;
+ PopUpFore:=SendDlgItemMessage(Dialog,IDC_FORE,CPM_GETCOLOUR,0,0);
+ PopUpBack:=SendDlgItemMessage(Dialog,IDC_BACK,CPM_GETCOLOUR,0,0);
+ end;
+//pause
+ if IsDlgButtonChecked(Dialog,IDC_DELAYDEF)=BST_CHECKED then
+ PopUpDelay:=0
+ else if IsDlgButtonChecked(Dialog,IDC_DELAYPERM)=BST_CHECKED then
+ PopUpDelay:=-1
+ else
+ begin
+ PopUpDelay:=1;
+ PopUpPause:=GetDlgItemInt(Dialog,IDC_DELAY,tmp,false);
+ end;
+//action
+ if IsDlgButtonChecked(Dialog,IDC_ACTLEFTINFO)=BST_CHECKED then
+ PopUpAction:=1
+ else if IsDlgButtonChecked(Dialog,IDC_ACTLEFTPLAYER)=BST_CHECKED then
+ PopUpAction:=2
+ else if IsDlgButtonChecked(Dialog,IDC_ACTLEFTNEXT)=BST_CHECKED then
+ PopUpAction:=3
+ else
+ PopUpAction:=0;
+ if IsDlgButtonChecked(Dialog,IDC_ACTRIGHTINFO)=BST_CHECKED then
+ inc(PopUpAction,$100)
+ else if IsDlgButtonChecked(Dialog,IDC_ACTRIGHTPLAYER)=BST_CHECKED then
+ inc(PopUpAction,$200)
+ else if IsDlgButtonChecked(Dialog,IDC_ACTRIGHTNEXT)=BST_CHECKED then
+ inc(PopUpAction,$300);
+
+ savepopup;
+ end;
+ end;
+ end;
+ end;
+end;
diff --git a/plugins/Watrack/popup/pop_opt.inc b/plugins/Watrack/popup/pop_opt.inc
new file mode 100644
index 0000000000..591063923e
--- /dev/null
+++ b/plugins/Watrack/popup/pop_opt.inc
@@ -0,0 +1,81 @@
+{Popup options saving-loading}
+
+const
+ defPopupTitle = 'Now listening to';
+ defPopupText = '%artist% - %title%';
+ defAltPopupTitle = 'Now ?ifgreater(%width%,0,watching,listening to)';
+ defAltPopupText = '%artist% - %title%'#13#10'?iflonger(%album%,0, (from "%album%"),)';
+const
+ opt_ModStatus :PAnsiChar = 'module/popups';
+
+ opt_PopUpFile :PAnsiChar = 'popup/file';
+ opt_PopUpAction :PAnsiChar = 'popup/action';
+ opt_PopUpFore :PAnsiChar = 'popup/fore';
+ opt_PopUpBack :PAnsiChar = 'popup/back';
+ opt_PopUpPause :PAnsiChar = 'popup/time';
+ opt_PopUpDelay :PAnsiChar = 'popup/delay';
+ opt_PopUpColor :PAnsiChar = 'popup/color';
+ opt_ByRequest :PAnsiChar = 'popup/byrequest';
+ opt_PopTitle :PAnsiChar = 'popup/poptitle';
+ opt_PopText :PAnsiChar = 'popup/poptext';
+ opt_PopUpButtons:PAnsiChar = 'popup/usebuttons';
+
+ spref = 'strings/';
+
+function GetModStatus:integer;
+begin
+ result:=DBReadByte(0,PluginShort,opt_ModStatus,1);
+end;
+
+procedure SetModStatus(stat:integer);
+begin
+ DBWriteByte(0,PluginShort,opt_ModStatus,stat);
+end;
+
+procedure loadpopup;
+var
+ def1,def2:pWideChar;
+begin
+ PopUpButtons:=DBReadByte (0,PluginShort,opt_PopUpButtons,BST_CHECKED);
+ PopUpFile :=DBReadByte (0,PluginShort,opt_PopUpFile ,BST_CHECKED);
+ PopUpPause :=DBReadByte (0,PluginShort,opt_PopUpPause ,0);
+ PopUpDelay :=DBReadByte (0,PluginShort,opt_PopUpDelay ,0);
+ PopUpAction :=DBReadWord (0,PluginShort,opt_PopUpAction ,0);
+ PopUpColor :=DBReadByte (0,PluginShort,opt_PopUpColor ,0);
+ PopUpFore :=DBReadDWord(0,PluginShort,opt_PopUpFore ,GetSysColor(COLOR_BTNTEXT));
+ PopUpBack :=DBReadDWord(0,PluginShort,opt_PopUpBack ,GetSysColor(COLOR_BTNFACE));
+ PopRequest :=DBReadByte (0,PluginShort,opt_ByRequest ,BST_UNCHECKED);
+ if isVarsInstalled then
+ begin
+ def1:=defAltPopupTitle;
+ def2:=defAltPopupText;
+ end
+ else
+ begin
+ def1:=defPopupTitle;
+ def2:=defPopupText;
+ end;
+ PopTitle:=DBReadUnicode(0,PluginShort,opt_PopTitle,def1);
+ PopText :=DBReadUnicode(0,PluginShort,opt_PopText ,def2);
+end;
+
+procedure savepopup;
+begin
+ DBWriteByte (0,PluginShort,opt_PopUpButtons,PopUpButtons);
+ DBWriteByte (0,PluginShort,opt_PopUpFile ,PopUpFile);
+ DBWriteByte (0,PluginShort,opt_PopUpPause ,PopUpPause);
+ DBWriteByte (0,PluginShort,opt_PopUpDelay ,PopUpDelay);
+ DBWriteWord (0,PluginShort,opt_PopUpAction ,PopUpAction);
+ DBWriteByte (0,PluginShort,opt_PopUpColor ,PopUpColor);
+ DBWriteDWord (0,PluginShort,opt_PopUpFore ,PopUpFore);
+ DBWriteDWord (0,PluginShort,opt_PopUpBack ,PopUpBack);
+ DBWriteByte (0,PluginShort,opt_ByRequest ,PopRequest);
+ DBWriteUnicode(0,PluginShort,opt_PopTitle,PopTitle);
+ DBWriteUnicode(0,PluginShort,opt_PopText ,PopText);
+end;
+
+procedure freepopup;
+begin
+ mFreeMem(PopTitle);
+ mFreeMem(PopText);
+end;
diff --git a/plugins/Watrack/popup/pop_rc.inc b/plugins/Watrack/popup/pop_rc.inc
new file mode 100644
index 0000000000..4e8298d209
--- /dev/null
+++ b/plugins/Watrack/popup/pop_rc.inc
@@ -0,0 +1,34 @@
+{POPUP DLG}
+const
+ IDC_DELAY = 1026;
+ IDC_DELAYDEF = 1027;
+ IDC_DELAYCUST = 1028;
+ IDC_DELAYPERM = 1029;
+ IDC_COLORDEF = 1030;
+ IDC_BACK = 1031;
+ IDC_FORE = 1032;
+ IDC_COLORWIN = 1033;
+ IDC_COLORCUST = 1034;
+ IDC_SHOWFILE = 1035;
+ IDC_TEST = 1036;
+ IDC_ACTLEFTCLOSE = 1040;
+ IDC_ACTLEFTINFO = 1041;
+ IDC_ACTRIGHTCLOSE = 1042;
+ IDC_ACTRIGHTINFO = 1043;
+ IDC_ONKEY = 1044;
+ IDC_KEYEMPTY = 1050;
+ IDC_KEYCUSTOM = 1051;
+ IDC_KEYMSG = 1052;
+ IDC_KEYNOMSG = 1055;
+ IDC_ACTLEFTPLAYER = 1056;
+ IDC_ACTRIGHTPLAYER = 1057;
+ IDC_ACTLEFTNEXT = 1058;
+ IDC_ACTRIGHTNEXT = 1059;
+ IDC_REQUEST = 1060;
+ IDC_STAT_HKBOX = 1061;
+ IDC_POPUP_TITLE = 1062;
+ IDC_POPUP_TEXT = 1063;
+ IDC_MACRO_HELP = 1064;
+ IDC_USEBUTTONS = 1065;
+
+ BTN_INFO = 9;
diff --git a/plugins/Watrack/popup/pop_vars.inc b/plugins/Watrack/popup/pop_vars.inc
new file mode 100644
index 0000000000..4a845aaadc
--- /dev/null
+++ b/plugins/Watrack/popup/pop_vars.inc
@@ -0,0 +1,27 @@
+{popup variables}
+const
+ ActionList:PPOPUPACTION=nil;
+var
+ PopTitle,
+ PopText:pWideChar;
+ PopRequest,
+ PopUpFile:dword;
+ PopUpColor:dword;
+ PopUpFore,
+ PopUpBack:cardinal;
+ PopUpPause:cardinal;
+ PopUpDelay:integer;
+ PopUpAction:cardinal;
+ PopUpButtons:cardinal;
+
+ DisablePlugin:integer;
+ IsPopup2Present:boolean;
+ IsFreeImagePresent:boolean;
+var
+ hMenuInfo :THANDLE;
+ ssmi,sic,
+ plStatusHook:THANDLE;
+ PopupPresent:Bool;
+ onttbhook,
+ opthook:THANDLE;
+ ttbInfo:THANDLE; \ No newline at end of file
diff --git a/plugins/Watrack/popup/popup.rc b/plugins/Watrack/popup/popup.rc
new file mode 100644
index 0000000000..ae05c73860
--- /dev/null
+++ b/plugins/Watrack/popup/popup.rc
@@ -0,0 +1,55 @@
+#include "pop_rc.inc"
+
+LANGUAGE 0,0
+
+POPUP DIALOGEX 0, 0, 304, 226, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 400, 0
+{
+ AUTOCHECKBOX "Show by request only", IDC_REQUEST, 156, 0, 144, 18, BS_MULTILINE
+
+ GROUPBOX "Colors", -1, 4, 18, 144, 74
+ CTEXT "Background", -1, 24, 64, 50, 8
+ CTEXT "Text" , -1, 84, 64, 50, 8
+ AUTORADIOBUTTON "Default colors", IDC_COLORDEF , 12, 28, 88, 10, NOT WS_TABSTOP
+ AUTORADIOBUTTON "Windows colors", IDC_COLORWIN , 12, 40, 88, 10, NOT WS_TABSTOP
+ AUTORADIOBUTTON "Custom colors" , IDC_COLORCUST, 12, 52, 88, 10, NOT WS_TABSTOP
+ CONTROL "", IDC_BACK, "ColourPicker", WS_TABSTOP, 24, 75, 50, 14
+ CONTROL "", IDC_FORE, "ColourPicker", WS_TABSTOP, 84, 75, 50, 14
+
+ GROUPBOX "Actions", -1, 156, 18, 144, 74
+ RTEXT "Close" , -1, 162, 40, 50, 10
+ RTEXT "Info" , -1, 162, 53, 50, 10
+ RTEXT "Show player", -1, 162, 66, 50, 10
+ RTEXT "Next track" , -1, 162, 79, 50, 10
+
+ CTEXT "Left click", -1, 208, 26, 40, 16
+ AUTORADIOBUTTON "", IDC_ACTLEFTCLOSE , 224, 40, 10, 10, NOT WS_TABSTOP
+ AUTORADIOBUTTON "", IDC_ACTLEFTINFO , 224, 53, 10, 10, NOT WS_TABSTOP
+ AUTORADIOBUTTON "", IDC_ACTLEFTPLAYER, 224, 66, 10, 10, NOT WS_TABSTOP
+ AUTORADIOBUTTON "", IDC_ACTLEFTNEXT , 224, 79, 10, 10, NOT WS_TABSTOP
+ CTEXT "Right click", -1, 248, 26, 40, 16
+ AUTORADIOBUTTON "", IDC_ACTRIGHTCLOSE , 262, 40, 10, 10, NOT WS_TABSTOP
+ AUTORADIOBUTTON "", IDC_ACTRIGHTINFO , 262, 53, 10, 10, NOT WS_TABSTOP
+ AUTORADIOBUTTON "", IDC_ACTRIGHTPLAYER, 262, 66, 10, 10, NOT WS_TABSTOP
+ AUTORADIOBUTTON "", IDC_ACTRIGHTNEXT , 262, 79, 10, 10, NOT WS_TABSTOP
+
+ GROUPBOX "Delay", -1, 4, 96, 144, 48
+ EDITTEXT IDC_DELAY, 86, 117, 36, 12, ES_AUTOHSCROLL | ES_NUMBER
+ LTEXT "sec", -1, 126, 118, 12, 8
+ AUTORADIOBUTTON "Default" , IDC_DELAYDEF , 12, 106, 128, 10, NOT WS_TABSTOP
+ AUTORADIOBUTTON "Custom" , IDC_DELAYCUST, 12, 118, 72, 10, NOT WS_TABSTOP
+ AUTORADIOBUTTON "Permanent", IDC_DELAYPERM, 12, 130, 128, 10, NOT WS_TABSTOP
+
+ AUTOCHECKBOX "Show file name in info" , IDC_SHOWFILE , 156, 94 , 144, 17, BS_MULTILINE
+ AUTOCHECKBOX "Use popup action buttons", IDC_USEBUTTONS, 156, 111, 144, 17, BS_MULTILINE
+ PUSHBUTTON "Test", IDC_TEST, 156, 128, 48, 16
+
+ CONTROL "M", IDC_MACRO_HELP ,"MButtonClass",WS_TABSTOP,278,138,16,16,$18000000
+ CTEXT "Popup Title / Text", -1, 6, 146, 270, 10
+ EDITTEXT IDC_POPUP_TITLE, 6, 156, 290, 14, ES_AUTOHSCROLL
+ EDITTEXT IDC_POPUP_TEXT , 6, 174, 290, 48, ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
+}
+
+BTN_INFO ICON "wat_info.ico"
diff --git a/plugins/Watrack/popup/popup.res b/plugins/Watrack/popup/popup.res
new file mode 100644
index 0000000000..707791399b
--- /dev/null
+++ b/plugins/Watrack/popup/popup.res
Binary files differ
diff --git a/plugins/Watrack/popup/popups.pas b/plugins/Watrack/popup/popups.pas
new file mode 100644
index 0000000000..381d33dab3
--- /dev/null
+++ b/plugins/Watrack/popup/popups.pas
@@ -0,0 +1,542 @@
+{PopUp support}
+unit Popups;
+{$include compilers.inc}
+interface
+{$Resource popup.res}
+implementation
+
+uses windows,messages,commctrl,
+ wat_api,waticons,global,
+ wrapper,common,m_api,dbsettings,mirutils;
+
+const
+ MenuInfoPos = 500050002;
+ PluginName = 'Winamp Track';
+const
+ IcoBtnInfo:PAnsiChar='WATrack_Info';
+const
+ HKN_POPUP:PAnsiChar = 'WAT_Popup';
+
+{$include pop_rc.inc}
+{$include pop_vars.inc}
+{$include pop_opt.inc}
+
+const
+ MainTmpl = 'artist: %ls'#13#10'title: "%ls"'#13#10'album: "%ls"'#13#10+
+ 'genre: %ls'#13#10'comment: %ls'#13#10'year: %ls'#13#10'track: %lu'#13#10+
+ 'bitrate: %lukbps %ls'#13#10'samplerate: %luKHz'#13#10+
+ 'channels: %lu'#13#10'length: %ls'#13#10'player: "%ls" v.%ls';
+ AddTmpl = #13#10'file: "%ls"'#13#10'size: %lu bytes';
+
+procedure ShowMusicInfo(si:pSongInfo);
+var
+ Tmpl:array [0..255] of WideChar;
+ buf:pWideChar;
+ lvars:array [0..15] of uint_ptr;
+ s:array [0..31] of WideChar;
+ p:PWideChar;
+begin
+ mGetMem(buf,16384);
+ with si^ do
+ begin
+ lvars[0]:=uint_ptr(artist);
+ lvars[1]:=uint_ptr(title);
+ lvars[2]:=uint_ptr(album);
+ lvars[3]:=uint_ptr(genre);
+ lvars[4]:=uint_ptr(comment);
+ lvars[5]:=uint_ptr(year);
+ lvars[6]:=track;
+ lvars[7]:=kbps;
+ if vbr>0 then
+ p:='VBR'
+ else
+ p:='CBR';
+ lvars[8]:=uint_ptr(p);
+ lvars[9]:=khz;
+ lvars[10]:=channels;
+ lvars[11]:=uint_ptr(IntToTime(s,total));
+ lvars[12]:=uint_ptr(player);
+ lvars[13]:=uint_ptr(txtver);
+ end;
+ StrCopyW(Tmpl,TranslateW(MainTmpl));
+ if PopUpFile=BST_CHECKED then
+ begin
+ lvars[14]:=uint_ptr(si^.mfile);
+ lvars[15]:=si^.fsize;
+ StrCatW(Tmpl,TranslateW(AddTmpl));
+ end;
+
+ wvsprintfw(buf,Tmpl,@lvars);
+ MessageBoxW(0,buf,PluginName,MB_OK);
+ mFreeMem(buf);
+end;
+
+function DumbPopupDlgProc(Wnd:hwnd;msg:uint;wParam:integer;lParam:longint):integer;stdcall;
+var
+ si:pSongInfo;
+ h:HBITMAP;
+begin
+ case msg of
+ WM_COMMAND,WM_CONTEXTMENU: begin
+ if msg=WM_CONTEXTMENU then
+ wParam:=HiByte(PopUpAction)
+ else
+ wParam:=LoByte(PopUpAction);
+ si:=pointer(CallService(MS_WAT_RETURNGLOBAL,0,0));
+ case wParam of
+ 1: ShowMusicInfo(si);
+ 2: ShowWindow(si^.plwnd,SW_RESTORE);
+ 3: CallServiceSync(MS_WAT_PRESSBUTTON,WAT_CTRL_NEXT,0);
+ end;
+ SendMessage(Wnd,UM_DESTROYPOPUP,0,0);
+ result:=1;
+ end;
+ UM_POPUPACTION: begin
+// if wParam<>0 then
+ result:=CallServiceSync(MS_WAT_PRESSBUTTON,lParam,0);
+ end;
+ UM_FREEPLUGINDATA: begin
+ h:=0;
+ h:=CallService(MS_POPUP_GETPLUGINDATA,Wnd,h);
+ if h<>0 then
+ DeleteObject(h);
+ result:=0;
+ end;
+ else
+ result:=DefWindowProc(Wnd,msg,wParam,lParam);
+ end;
+end;
+
+function MakeAction(var anAct:TPOPUPACTION;action:integer):PPOPUPACTION;
+begin
+ result:=@anAct;
+ anAct.cbSize :=SizeOf(TPOPUPACTION);
+ anAct.lchIcon:=GetIcon(action);
+ anAct.flags :=PAF_ENABLED;
+ anAct.wParam :=1;
+ anAct.lParam :=action;
+ StrCopy(StrCopyE(anAct.lpzTitle,'Watrack/'),GetIconDescr(action));
+end;
+
+function MakeActions:PPOPUPACTION;
+type
+ anacts = array [0..6] of TPOPUPACTION;
+var
+ actions:^anacts;
+begin
+ if PopUpButtons<>BST_UNCHECKED then
+ begin
+ mGetMem(actions,SizeOf(anacts));
+ result:=PPOPUPACTION(actions);
+ FillChar(actions^,SizeOf(actions^),0);
+ MakeAction(actions[0],WAT_CTRL_PREV);
+ MakeAction(actions[1],WAT_CTRL_PLAY);
+ MakeAction(actions[2],WAT_CTRL_PAUSE);
+ MakeAction(actions[3],WAT_CTRL_STOP);
+ MakeAction(actions[4],WAT_CTRL_NEXT);
+ MakeAction(actions[5],WAT_CTRL_VOLDN);
+ MakeAction(actions[6],WAT_CTRL_VOLUP);
+ end
+ else
+ result:=nil;
+end;
+
+procedure ThShowPopup(si:pSongInfo); cdecl;
+var
+ ppdu:PPOPUPDATAW;
+ title,descr:pWideChar;
+ flag:dword;
+ ppd2:PPOPUPDATA2;
+ Icon:HICON;
+ sec:integer;
+ cb,ct:TCOLORREF;
+ line:boolean;
+ tmp:pAnsiChar;
+begin
+ line:=CallService(MS_POPUP_ISSECONDLINESHOWN,0,0)<>0;
+
+ descr:=PWideChar(CallService(MS_WAT_REPLACETEXT,0,lparam(PopText)));
+ if line then
+ title:=PWideChar(CallService(MS_WAT_REPLACETEXT,0,lparam(PopTitle)))
+ else
+ title:=nil;
+
+ if (descr<>nil) or (title<>nil) then
+ begin
+ if si^.icon<>0 then
+ Icon:=si^.icon
+ else
+ Icon:=LoadSkinnedIcon(SKINICON_OTHER_MIRANDA);
+ if PopUpDelay<0 then
+ sec:=-1
+ else if PopUpDelay>0 then
+ sec:=PopUpPause
+ else
+ sec:=0;
+ case PopUpColor of
+ 0: begin
+ cb:=0;
+ ct:=0;
+ end;
+ 1: begin
+ cb:=GetSysColor(COLOR_BTNFACE);
+ ct:=GetSysColor(COLOR_BTNTEXT);
+ end;
+ 2: begin
+ cb:=PopUpBack;
+ ct:=PopUpFore;
+ end;
+ else
+ cb:=0;
+ ct:=0;
+ end;
+
+ if IsPopup2Present then
+ begin
+ mGetMem (ppd2 ,SizeOf(TPOPUPDATA2));
+ FillChar(ppd2^,SizeOf(TPOPUPDATA2),0);
+ with ppd2^ do
+ begin
+ cbSize :=SizeOf(TPOPUPDATA2);
+ flags :=PU2_UNICODE;
+ lchIcon :=Icon;
+ colorBack :=cb;
+ colorText :=ct;
+ PluginWindowProc:=@DumbPopupDlgProc;
+
+ if line then
+ begin
+ pzTitle.w:=title;
+ pzText .w:=descr;
+ end
+ else
+ pzTitle.w:=descr;
+
+ if ActionList=nil then
+ flag:=0
+ else
+ begin
+ flag :=APF_NEWDATA;
+ actionCount:=7;
+ lpActions :=ActionList;
+ end;
+
+ if si.cover<>nil then
+ begin
+ if IsFreeImagePresent then
+ hbmAvatar:=CallService(MS_IMG_LOAD,wparam(si.cover),IMGL_WCHAR)
+ else
+ hbmAvatar:=0;
+ if hbmAvatar=0 then
+ begin
+ WideToAnsi(si.cover,tmp);
+ hbmAvatar:=CallService(MS_UTILS_LOADBITMAP,0,lparam(tmp));
+ mFreeMem(tmp);
+ end;
+ end;
+ PluginData:=pointer(hbmAvatar);
+ end;
+ CallService(MS_POPUP_ADDPOPUP2,wparam(ppd2),flag);
+ mFreeMem(ppd2);
+ end
+ else
+ begin
+ mGetMem (ppdu ,SizeOf(TPOPUPDATAW));
+ FillChar(ppdu^,SizeOf(TPOPUPDATAW),0);
+ with ppdu^ do
+ begin
+ if line then
+ begin
+ if title<>nil then
+ StrCopyW(lpwzContactName,title,MAX_CONTACTNAME-1)
+ else
+ lpwzContactName[0]:=' ';
+ if descr<>nil then
+ StrCopyW(lpwzText,descr,MAX_SECONDLINE-1)
+ else
+ lpwzText[0]:=' ';
+ end
+ else
+ begin
+ StrCopyW(ppdu^.lpwzContactName,title,MAX_CONTACTNAME-1);
+ lpwzText[0]:=' ';
+ end;
+
+ lchIcon :=Icon;
+ PluginWindowProc:=@DumbPopupDlgProc;
+ iSeconds :=sec;
+ colorBack :=cb;
+ colorText :=ct;
+
+ // if ServiceExists(MS_POPUP_REGISTERACTIONS)=0 then
+ if ActionList=nil then
+ flag:=0
+ else
+ begin
+ flag :=APF_NEWDATA;
+ icbSize :=SizeOf(TPOPUPDATAW);
+ actionCount:=7;
+ lpActions :=ActionList;
+ end;
+ end;
+ CallService(MS_POPUP_ADDPOPUPW,wparam(ppdu),flag);
+ mFreeMem(ppdu);
+ end;
+ mFreeMem(title);
+ mFreeMem(descr);
+ end;
+end;
+
+procedure ShowPopUp(si:pSongInfo);
+begin
+ CloseHandle(mir_forkthread(@ThShowPopup,si));
+end;
+
+// --------------- Services and Hooks ----------------
+
+function OpenPopUp(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ si:pSongInfo;
+begin
+ result:=0;
+ if DisablePlugin<>dsEnabled then
+ exit;
+ if CallService(MS_WAT_GETMUSICINFO,0,tlparam(@si))=WAT_PLS_NORMAL then
+ begin
+ if PopupPresent then
+ ShowPopUp(si)
+ else
+ ShowMusicInfo(si);
+ end;
+end;
+
+procedure regpophotkey;
+var
+ hkrec:HOTKEYDESC;
+begin
+ if DisablePlugin=dsPermanent then
+ exit;
+ FillChar(hkrec,SizeOf(hkrec),0);
+ with hkrec do
+ begin
+ cbSize :=HOTKEYDESC_SIZE_V1;
+ pszName :=HKN_POPUP;
+ pszDescription.a:='WATrack popup hotkey';
+ pszSection.a :=PluginName;
+ pszService :=MS_WAT_SHOWMUSICINFO;
+ DefHotKey:=((HOTKEYF_ALT or HOTKEYF_CONTROL) shl 8) or VK_F7 or HKF_MIRANDA_LOCAL;
+ end;
+ CallService(MS_HOTKEY_REGISTER,0,lparam(@hkrec));
+end;
+
+{$include pop_dlg.inc}
+
+function NewPlStatus(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ mi:TCListMenuItem;
+ flag:integer;
+begin
+ result:=0;
+ case wParam of
+ WAT_EVENT_NEWTRACK: begin
+ if PopupPresent and (PopRequest=BST_UNCHECKED) then
+ ShowPopUp(pSongInfo(lParam));
+ end;
+ WAT_EVENT_PLUGINSTATUS: begin
+ DisablePlugin:=lParam;
+ case lParam of
+ dsEnabled: begin
+ flag:=0;
+ end;
+ dsPermanent: begin
+ flag:=CMIF_GRAYED;
+ end;
+ else // like 1
+ exit
+ end;
+ FillChar(mi,sizeof(mi),0);
+ mi.cbSize:=sizeof(mi);
+ mi.flags :=CMIM_FLAGS+flag;
+ CallService(MS_CLIST_MODIFYMENUITEM,hMenuInfo,tlparam(@mi));
+ end;
+ end;
+end;
+
+function IconChanged(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ mi:TCListMenuItem;
+begin
+ result:=0;
+ FillChar(mi,SizeOf(mi),0);
+ mi.cbSize:=sizeof(mi);
+ mi.flags :=CMIM_ICON;
+ mi.hIcon :=CallService(MS_SKIN2_GETICON,0,tlparam(IcoBtnInfo));
+ CallService(MS_CLIST_MODIFYMENUITEM,hMenuInfo,tlparam(@mi));
+ if ActionList<>nil then
+ begin
+ mFreeMem(ActionList);
+ ActionList:=MakeActions;
+ CallService(MS_POPUP_REGISTERACTIONS,twparam(ActionList),7);
+ end;
+end;
+
+function OnOptInitialise(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ odp:TOPTIONSDIALOGPAGE;
+begin
+ FillChar(odp,SizeOf(odp),0);
+ odp.cbSize :=OPTIONPAGE_OLD_SIZE2; //for 0.5 compatibility
+ odp.flags :=ODPF_BOLDGROUPS;
+ odp.Position :=900003000;
+ odp.hInstance :=hInstance;
+ odp.szTitle.a :=PluginName;
+
+ odp.szGroup.a :='PopUps';
+ odp.pszTemplate:=DLGPOPUP;
+ odp.pfnDlgProc :=@DlgPopUpOpt;
+ CallService(MS_OPT_ADDPAGE,wParam,tlparam(@odp));
+ result:=0;
+end;
+
+function OnTTBLoaded(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ ttb:TTBButton;
+begin
+ result:=0;
+ if onttbhook<>0 then
+ UnhookEvent(onttbhook);
+ // get info button
+ FillChar(ttb,SizeOf(ttb),0);
+ ttb.cbSize :=SizeOf(ttb);
+ ttb.dwFlags :=TTBBF_VISIBLE{ or TTBBF_SHOWTOOLTIP};
+ ttb.hIconUp :=CallService(MS_SKIN2_GETICON,0,tlparam(IcoBtnInfo));
+ ttb.hIconDn :=ttb.hIconUp;
+ ttb.pszService:=MS_WAT_SHOWMUSICINFO;
+ ttb.name :='Music Info';
+ ttbInfo:=TopToolbar_AddButton(@ttb);
+ if ttbInfo=THANDLE(-1) then
+ ttbInfo:=0;
+end;
+
+// ------------ base interface functions -------------
+
+function InitProc(aGetStatus:boolean=false):integer;
+var
+ mi:TCListMenuItem;
+ sid:TSKINICONDESC;
+begin
+ if aGetStatus then
+ begin
+ if GetModStatus=0 then
+ begin
+ result:=0;
+ exit;
+ end;
+ end
+ else
+ SetModStatus(1);
+ result:=1;
+
+ ssmi:=CreateServiceFunction(MS_WAT_SHOWMUSICINFO,@OpenPopUp);
+
+ FillChar(sid,SizeOf(TSKINICONDESC),0);
+ sid.cbSize:=SizeOf(TSKINICONDESC);
+ sid.cx:=16;
+ sid.cy:=16;
+ sid.szSection.a:='WATrack';
+ sid.hDefaultIcon :=LoadImage(hInstance,MAKEINTRESOURCE(BTN_INFO),IMAGE_ICON,16,16,0);
+ sid.pszName :=IcoBtnInfo;
+ sid.szDescription.a:='Music Info';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+ sic:=HookEvent(ME_SKIN2_ICONSCHANGED,@IconChanged);
+
+ FillChar(mi,SizeOf(mi),0);
+ mi.cbSize :=SizeOf(mi);
+ mi.szPopupName.a:=PluginShort;
+ mi.hIcon :=CallService(MS_SKIN2_GETICON,0,lparam(IcoBtnInfo));
+ mi.szName.a :='Music Info';
+ mi.pszService :=MS_WAT_SHOWMUSICINFO;
+ mi.popupPosition:=MenuInfoPos;
+ hMenuInfo :=Menu_AddMainMenuItem(@mi);
+
+ if ServiceExists(MS_POPUP_ADDPOPUPW)<>0 then
+ begin
+ IsFreeImagePresent:=ServiceExists(MS_IMG_LOAD )<>0;
+ IsPopup2Present :=ServiceExists(MS_POPUP_ADDPOPUP2)<>0;
+ PopupPresent:=true;
+ opthook:=HookEvent(ME_OPT_INITIALISE,@OnOptInitialise);
+ loadpopup;
+ regpophotkey;
+
+ ActionList:=nil;
+ if ServiceExists(MS_POPUP_REGISTERACTIONS)<>0 then
+ begin
+ if RegisterButtonIcons then
+ begin
+ ActionList:=MakeActions;
+ if ActionList<>nil then
+ CallService(MS_POPUP_REGISTERACTIONS,wparam(ActionList),7);
+ end;
+ end;
+ end
+ else
+ begin
+ PopupPresent:=false;
+ end;
+
+ plStatusHook:=HookEvent(ME_WAT_NEWSTATUS,@NewPlStatus);
+
+ if ServiceExists(MS_TTB_ADDBUTTON)>0 then
+ begin
+ onttbhook:=0;
+ OnTTBLoaded(0,0);
+ if ttbInfo=0 then
+ onttbhook:=HookEvent(ME_TTB_MODULELOADED,@OnTTBLoaded);
+ end
+ else
+ ttbInfo:=0;
+end;
+
+procedure DeInitProc(aSetDisable:boolean);
+begin
+ if aSetDisable then
+ SetModStatus(0);
+
+ CallService(MS_CLIST_REMOVEMAINMENUITEM,hMenuInfo,0);
+ UnhookEvent(plStatusHook);
+ DestroyServiceFunction(ssmi);
+ UnhookEvent(sic);
+
+ freepopup;
+
+ if ttbInfo<>0 then
+ begin
+ if ServiceExists(MS_TTB_REMOVEBUTTON)>0 then
+ CallService(MS_TTB_REMOVEBUTTON,WPARAM(ttbInfo),0);
+ ttbInfo:=0;
+ end;
+
+ if PopupPresent then
+ begin
+ UnhookEvent(opthook);
+ mFreeMem(ActionList);
+ end;
+end;
+
+var
+ Popup:twModule;
+
+procedure Init;
+begin
+ Popup.Next :=ModuleLink;
+ Popup.Init :=@InitProc;
+ Popup.DeInit :=@DeInitProc;
+ Popup.AddOption :=nil;
+ Popup.ModuleName:='PopUps';
+ ModuleLink :=@Popup;
+end;
+
+begin
+ Init;
+end.
diff --git a/plugins/Watrack/popup/wat_info.ico b/plugins/Watrack/popup/wat_info.ico
new file mode 100644
index 0000000000..70d54c8bac
--- /dev/null
+++ b/plugins/Watrack/popup/wat_info.ico
Binary files differ
diff --git a/plugins/Watrack/proto/i_proto_dlg.inc b/plugins/Watrack/proto/i_proto_dlg.inc
new file mode 100644
index 0000000000..3c467c79ac
--- /dev/null
+++ b/plugins/Watrack/proto/i_proto_dlg.inc
@@ -0,0 +1,144 @@
+{Misc}
+
+procedure SetAllContactStat(hwndList:HWND);
+var
+ hContact,hItem:THANDLE;
+begin
+ hContact:=CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ while hContact<>0 do
+ begin
+ hItem:=SendMessage(hwndList,CLM_FINDCONTACT,hContact,0);
+ if hItem<>0 then
+ begin
+ SendMessage(hwndList,CLM_SETCHECKMARK,hItem,
+ DBReadByte(hContact,strCList,ShareOptText,0));
+ end;
+ hContact:=CallService(MS_DB_CONTACT_FINDNEXT,hContact,0);
+ end;
+end;
+
+procedure SaveAllContactStat(hwndList:HWND);
+var
+ hContact,hItem:THANDLE;
+begin
+ hContact:=CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ while hContact<>0 do
+ begin
+ hItem:=SendMessage(hwndList,CLM_FINDCONTACT,hContact,0);
+ if hItem<>0 then
+ begin
+ if SendMessage(hwndList,CLM_GETCHECKMARK,hItem,0)<>0 then
+ DBWriteByte(hContact,strCList,ShareOptText,1)
+ else
+ DBDeleteSetting(hContact,strCList,ShareOptText);
+ end;
+ hContact:=CallService(MS_DB_CONTACT_FINDNEXT,hContact,0);
+ end;
+end;
+
+procedure ResetListOptions(hwndList:HWND);
+var
+ i:integer;
+begin
+ SendMessage(hwndList,CLM_SETBKBITMAP ,0,0);
+ SendMessage(hwndList,CLM_SETBKCOLOR ,GetSysColor(COLOR_WINDOW),0);
+ SendMessage(hwndList,CLM_SETGREYOUTFLAGS,0,0);
+ SendMessage(hwndList,CLM_SETLEFTMARGIN ,2,0);
+ SendMessage(hwndList,CLM_SETINDENT ,10,0);
+ for i:=0 to FONTID_MAX do
+ SendMessage(hwndList,CLM_SETTEXTCOLOR,i,GetSysColor(COLOR_WINDOWTEXT));
+ SetWindowLongPtr(hwndList,GWL_STYLE,GetWindowLongPtr(hwndList,GWL_STYLE) or CLS_SHOWHIDDEN);
+end;
+
+procedure SetHistMask(Dlg:HWND);
+begin
+ CheckDlgButton(Dlg,IDC_IN_REQUEST ,ORD((HistMask and hmInRequest )<>0));
+ CheckDlgButton(Dlg,IDC_OUT_REQUEST,ORD((HistMask and hmOutRequest)<>0));
+ CheckDlgButton(Dlg,IDC_IN_INFO ,ORD((HistMask and hmInInfo )<>0));
+ CheckDlgButton(Dlg,IDC_OUT_INFO ,ORD((HistMask and hmOutInfo )<>0));
+ CheckDlgButton(Dlg,IDC_IN_ERROR ,ORD((HistMask and hmInError )<>0));
+ CheckDlgButton(Dlg,IDC_OUT_ERROR ,ORD((HistMask and hmOutError )<>0));
+ CheckDlgButton(Dlg,IDC_IREQUEST ,ORD((HistMask and hmIRequest )<>0));
+ CheckDlgButton(Dlg,IDC_ISEND ,ORD((HistMask and hmISend )<>0));
+end;
+
+procedure SaveHistMask(Dlg:HWND);
+begin
+ HistMask:=0;
+ if IsDlgButtonChecked(Dlg,IDC_IN_REQUEST )<>BST_UNCHECKED then HistMask:=HistMask or hmInRequest;
+ if IsDlgButtonChecked(Dlg,IDC_OUT_REQUEST)<>BST_UNCHECKED then HistMask:=HistMask or hmOutRequest;
+ if IsDlgButtonChecked(Dlg,IDC_IN_INFO )<>BST_UNCHECKED then HistMask:=HistMask or hmInInfo;
+ if IsDlgButtonChecked(Dlg,IDC_OUT_INFO )<>BST_UNCHECKED then HistMask:=HistMask or hmOutInfo;
+ if IsDlgButtonChecked(Dlg,IDC_IN_ERROR )<>BST_UNCHECKED then HistMask:=HistMask or hmInError;
+ if IsDlgButtonChecked(Dlg,IDC_OUT_ERROR )<>BST_UNCHECKED then HistMask:=HistMask or hmOutError;
+ if IsDlgButtonChecked(Dlg,IDC_IREQUEST )<>BST_UNCHECKED then HistMask:=HistMask or hmIRequest;
+ if IsDlgButtonChecked(Dlg,IDC_ISEND )<>BST_UNCHECKED then HistMask:=HistMask or hmISend;
+end;
+
+function DlgProcOptions(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+const
+ Changed:integer=0;
+// hItemAll:THANDLE=0;
+var
+// cii:TCLCINFOITEM;
+ hList:HWND;
+begin
+ result:=0;
+ case hMessage of
+ WM_INITDIALOG: begin
+ TranslateDialogDefault(Dialog);
+ Changed:=DLGED_INIT;
+
+ hList:=GetDlgItem(Dialog,IDC_SHARE);
+ ResetListOptions(hList);
+ SendMessage(hList,CLM_SETUSEGROUPS ,1,0);
+ SendMessage(hList,CLM_SETHIDEEMPTYGROUPS,1,0);
+
+// SendMessage(hList,CLM_SETEXTRACOLUMNS,2,0);
+{
+ FillChar(cii,SizeOf(cii),0);
+ cii.cbSize :=SizeOf(cii);
+ cii.flags :=CLCIIF_GROUPFONT or CLCIIF_CHECKBOX;
+ cii.pszText.w:=TranslateW('** All contacts **');
+ hItemAll:=SendMessage(hList,CLM_ADDINFOITEM,0,dword(@cii));
+}
+ SetAllContactStat(hList);
+ SetHistMask(Dialog);
+
+ SetDlgItemTextW(Dialog,IDC_PROTO_TEXT,ProtoText);
+
+ Changed:=0;
+ end;
+
+ WM_COMMAND: begin
+ if Changed<>DLGED_INIT then
+ begin
+ case wParam shr 16 of
+ BN_CLICKED,EN_CHANGE: SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+ end;
+ end;
+
+ WM_NOTIFY: begin
+ if Changed<>DLGED_INIT then
+ begin
+ if PNMHDR(lParam)^.idFrom=IDC_SHARE then
+ if integer(PNMHdr(lParam)^.code)=CLN_CHECKCHANGED then
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+
+ if integer(PNMHdr(lParam)^.code)=PSN_APPLY then
+ begin
+ mFreeMem(ProtoText);
+ ProtoText:=GetDlgText(Dialog,IDC_PROTO_TEXT);
+
+ SaveAllContactStat(GetDlgItem(Dialog,IDC_SHARE));
+ SaveHistMask(Dialog);
+
+ WriteOptions;
+ end;
+ end;
+ end;
+ else
+ {result:=}DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+end;
diff --git a/plugins/Watrack/proto/i_proto_opt.inc b/plugins/Watrack/proto/i_proto_opt.inc
new file mode 100644
index 0000000000..c1e2cbf36d
--- /dev/null
+++ b/plugins/Watrack/proto/i_proto_opt.inc
@@ -0,0 +1,35 @@
+{}
+const
+ defProtoText = '%artist% - %title%';
+const
+ opt_ModStatus:PAnsiChar = 'module/protocol';
+
+ opt_histmask :PAnsiChar = 'historymask';
+ opt_prototext:PAnsiChar = 'prototext';
+
+function GetModStatus:integer;
+begin
+ result:=DBReadByte(0,PluginShort,opt_ModStatus,1);
+end;
+
+procedure SetModStatus(stat:integer);
+begin
+ DBWriteByte(0,PluginShort,opt_ModStatus,stat);
+end;
+
+procedure ReadOptions;
+begin
+ HistMask :=DBReadWord (0,PluginShort,opt_histmask,0);
+ ProtoText:=DBReadUnicode(0,PluginShort,opt_prototext,defProtoText);
+end;
+
+procedure WriteOptions;
+begin
+ DBWriteWord (0,PluginShort,opt_histmask ,HistMask);
+ DBWriteUnicode(0,PluginShort,opt_prototext,ProtoText);
+end;
+
+procedure FreeOptions;
+begin
+ mFreeMem(ProtoText);
+end;
diff --git a/plugins/Watrack/proto/i_proto_rc.inc b/plugins/Watrack/proto/i_proto_rc.inc
new file mode 100644
index 0000000000..f2d56ad329
--- /dev/null
+++ b/plugins/Watrack/proto/i_proto_rc.inc
@@ -0,0 +1,17 @@
+const
+ IDC_EXPORT = 1031;
+ IDC_SHARE = 1033;
+ IDC_IN_REQUEST = 1034;
+ IDC_OUT_REQUEST = 1035;
+ IDC_IN_INFO = 1036;
+ IDC_OUT_INFO = 1037;
+ IDC_IN_ERROR = 1038;
+ IDC_OUT_ERROR = 1039;
+ IDC_IREQUEST = 1040;
+ IDC_ISEND = 1041;
+ IDC_STAT_EXPORT = 1042;
+ IDC_STAT_TEXP = 1043;
+ IDC_STAT_LINE = 1044;
+ IDC_PROTO_TEXT = 1045;
+
+BTN_CONTEXT = 130;
diff --git a/plugins/Watrack/proto/proto.pas b/plugins/Watrack/proto/proto.pas
new file mode 100644
index 0000000000..254a02bac6
--- /dev/null
+++ b/plugins/Watrack/proto/proto.pas
@@ -0,0 +1,564 @@
+{Statistic}
+unit proto;
+{$include compilers.inc}
+interface
+{$Resource proto.res}
+implementation
+
+uses
+ windows,messages,commctrl,
+ common,m_api,mirutils,dbsettings,wrapper,
+ global,wat_api;
+
+{$include i_proto_rc.inc}
+
+const
+ ShareOptText = 'ShareMusic';
+const
+ IcoBtnContext:PAnsiChar='WATrack_Context';
+const
+ MenuUserInfoPos = 500050000;
+
+const
+ wpRequest = 'WAT###0_';
+ wpAnswer = 'WAT###1_';
+ wpError = 'WAT###2_';
+ wpMessage = 'WAT###3_';
+ wpRequestNew = 'ASKWAT';
+
+const
+ SendRequestText:PAnsiChar =
+ 'WATrack internal info - sorry!';
+{
+ 'If you see this message, probably you have no "WATrack" plugin installed or uses old '+
+ 'version. See http://miranda-im.org/download/details.php?action=viewfile&id=2345 or '+
+ 'http://awkward.miranda.im/ (beta versions) for more information and download.';
+}
+const
+ hmInRequest = $0001;
+ hmOutRequest = $0002;
+ hmInInfo = $0004;
+ hmOutInfo = $0008;
+ hmInError = $0010;
+ hmOutError = $0020;
+ hmIRequest = $0040;
+ hmISend = $0080;
+
+var
+ hSRM,
+ hGCI,
+ icchangedhook,
+ hAddUserHook,
+ hContactMenuItem,
+ contexthook:THANDLE;
+ ProtoText:pWideChar;
+ HistMask:cardinal;
+
+{$include i_proto_opt.inc}
+{$include i_proto_dlg.inc}
+
+procedure AddEvent(hContact:THANDLE;atype,flag:integer;data:pointer;size:integer;time:dword=0);
+var
+ dbeo:TDBEVENTINFO;
+begin
+ FillChar(dbeo,SizeOf(dbeo),0);
+ with dbeo do
+ begin
+ cbSize :=SizeOf(dbeo);
+ eventType:=atype;
+ szModule :=PluginShort;
+ if data=nil then
+ begin
+ PAnsiChar(data):='';
+ size:=1;
+ end;
+ pBlob :=data;
+ cbBlob :=size;
+ flags :=flag;
+ if time<>0 then
+ Timestamp:=time
+ else
+ Timestamp:=GetCurrentTime;
+ end;
+ CallService(MS_DB_EVENT_ADD,hContact,lparam(@dbeo));
+end;
+
+{SEND-time text translation}
+(*
+const
+ BufSize = 16384;
+
+function FormatToBBW(src:PWideChar):PWideChar;
+var
+ buf:array [0..32] of WideChar;
+ p:PWideChar;
+ i,j:integer;
+begin
+ result:=src;
+ StrReplaceW(src,'{b}' ,'[b]');
+ StrReplaceW(src,'{/b}' ,'[/b]');
+ StrReplaceW(src,'{u}' ,'[u]');
+ StrReplaceW(src,'{/u}' ,'[/u]');
+ StrReplaceW(src,'{i}' ,'[i]');
+ StrReplaceW(src,'{/i}' ,'[/i]');
+ StrReplaceW(src,'{/cf}','[/color]');
+ StrReplaceW(src,'{/bg}','');
+ StrCopyW(buf,'[color=');
+ repeat
+ i:=StrPosW(src,'{cf');
+ if i=0 then break;
+ j:=i;
+ dec(i);
+ while (src[j]<>#0) and (src[j]<>'}') do inc(j);
+ if src[j]='}' then inc(j);
+ case StrToInt(src+i+3) of
+ 4,10: p:='green]';
+ 5,6: p:='red]';
+ 7,14: p:='magenta]';
+ 3,11,
+ 12,13: p:='blue]';
+ 8,9: p:='yellow]';
+ 2,15: p:='black]';
+ else
+ {1,16:} p:='white]';
+ end;
+ StrCopyW(buf+7,p);
+ StrCopyW(src+i,src+j);
+ StrInsertW(buf,src,i);
+ until false;
+ repeat
+ i:=StrIndex(src,'{bg');
+ if i=0 then break;
+ j:=i;
+ dec(i);
+ while (src[j]<>#0) and (src[j]<>'}') do inc(j);
+ if src[j]='}' then inc(j);
+ StrCopyW(src+i,src+j);
+ until false;
+end;
+
+function SendMessageProcW(wParam:WPARAM; lParam:LPARAM):integer; cdecl;
+var
+ ccs:PCCSDATA;
+ uns,s,ss:pWideChar;
+ p:PAnsiChar;
+ present:boolean;
+ i:integer;
+begin
+ if DisablePlugin<>dsPermanent then
+ begin
+ ccs:=PCCSDATA(lParam);
+ if ccs^.wParam=0 then
+ present:=StrPos('%music%',PAnsiChar(ccs^.lParam))<>nil
+ else // not needed?
+ begin
+ uns:=PWideChar(ccs^.lParam+StrLen(PAnsiChar(ccs^.lParam))+1);
+ present:=StrPos(uns,'%music%')<>nil;
+ end;
+
+ if present then
+ begin
+ if CallService(MS_WAT_GETMUSICINFO,0,0)=WAT_PLS_NOTFOUND then
+ s:=nil
+ else
+ begin
+ if SimpleMode<>BST_UNCHECKED then
+ i:=0
+ else
+ i:=CallService(MS_PROTO_GETCONTACTBASEPROTO,ccs^.hContact,0);
+ s:=GetMacros(TM_MESSAGE,i);
+ end;
+ // if s<>nil then // for empty strings
+ begin
+ mGetMem(ss,BufSize*SizeOf(pWideChar));
+ FillChar(ss^,BufSize*SizeOf(pWideChar),0);
+ if ccs^.wParam=0 then
+ AnsiToWide(PAnsiChar(ccs^.lParam),uns,UserCP);
+ StrCopyW(ss,uns);
+ if ccs^.wParam=0 then
+ mFreeMem(uns);
+ StrReplaceW(ss,'%music%',s);
+ mFreeMem(s);
+ if StrPos(ss,'{')<>nil then
+ FormatToBBW(ss);
+ s:=PWideChar(ccs^.lParam);
+ WideToAnsi(ss,p,UserCP);
+ if ccs^.wParam=0 then
+ begin
+ ccs^.lParam:=dword(p);
+ end
+ else
+ begin
+ move(PAnsiChar(ss)^,(PAnsiChar(ss)+StrLen(p)+1)^,
+ (StrLenW(ss)+1)*SizeOf(WideChar));
+ StrCopy(PAnsiChar(ss),p);
+ ccs^.lParam:=dword(ss);
+ end;
+ result:=CallService(MS_PROTO_CHAINSEND,wParam,lParam);
+ mFreeMem(p);
+ ccs^.lParam:=dword(s);
+ mFreeMem(ss);
+ exit;
+ end;
+ end;
+ end;
+ result:=CallService(MS_PROTO_CHAINSEND,wParam,lParam);
+end;
+*)
+
+function ReceiveMessageProcW(wParam:WPARAM; lParam:LPARAM):integer; cdecl;
+const
+ bufsize = 4096*SizeOf(WideChar);
+var
+ ccs:PCCSDATA;
+ s:pWideChar;
+ buf:PWideChar;
+ base64:TNETLIBBASE64;
+// pos_artist,pos_title,pos_album:PwideChar;
+ pos_template:pWideChar;
+ curpos:pWideChar;
+ encbuf:pWideChar;
+ i:integer;
+ textpos:PWideChar;
+ pc:PAnsiChar;
+ isNewRequest:bool;
+ si:pSongInfo;
+begin
+ ccs:=PCCSDATA(lParam);
+ result:=0;
+ mGetMem(buf,bufsize);
+
+ isNewRequest:=StrCmp(PPROTORECVEVENT(ccs^.lParam)^.szMessage.a,
+ wpRequestNew,Length(wpRequestNew))=0;
+
+ if isNewRequest or
+ (StrCmp(PPROTORECVEVENT(ccs^.lParam)^.szMessage.a,
+ wpRequest,Length(wpRequest))=0) then
+ begin
+ StrCopy(PAnsiChar(buf),PAnsiChar(CallService(MS_PROTO_GETCONTACTBASEPROTO,ccs^.hContact,0)));
+ i:=DBReadWord(ccs^.hContact,PAnsiChar(buf),'ApparentMode');
+ StrCat(PAnsiChar(buf),PS_GETSTATUS);
+ if (i=ID_STATUS_OFFLINE) or
+ ((i=0) and (CallService(PAnsiChar(buf),0,0)=ID_STATUS_INVISIBLE)) then
+ begin
+ result:=CallService(MS_PROTO_CHAINRECV,wParam,lParam);
+ end
+ else if DBReadByte(ccs^.hContact,strCList,ShareOptText,0)<>0 then
+// or (NotListedAllow and (DBReadByte(ccs^.hContact,strCList,'NotOnList',0))
+ begin
+ if (HistMask and hmInRequest)<>0 then
+ AddEvent(ccs^.hContact,EVENTTYPE_WAT_REQUEST,DBEF_READ,nil,0,
+ PPROTORECVEVENT(ccs^.lParam)^.Timestamp);
+ if GetContactStatus(ccs^.hContact)<>ID_STATUS_OFFLINE then
+ begin
+//!! Request Answer
+ curpos:=nil;
+ if DisablePlugin<>dsPermanent then
+ begin
+ if CallService(MS_WAT_GETMUSICINFO,0,0)=WAT_PLS_NOTFOUND then
+ begin
+ s:=#0#0#0'No player found at this time';
+ textpos:=s+3;
+ end
+ else
+ begin
+ if not isNewRequest then
+ begin
+ FillChar(buf^,bufsize,0);
+ si:=pSongInfo(CallService(MS_WAT_RETURNGLOBAL,0,0));
+ StrCopyW(buf ,si^.artist); curpos:=StrEndW(buf)+1;
+ StrCopyW(curpos,si^.title); curpos:=StrEndW(curpos)+1;
+ StrCopyW(curpos,si^.album); curpos:=StrEndW(curpos)+1;
+ end
+ else
+ curpos:=buf;
+//!! check to DisableTemporary
+
+ s:=PWideChar(CallService(MS_WAT_REPLACETEXT,0,tlparam(ProtoText)));
+ textpos:=StrCopyW(curpos,s);
+ mFreeMem(s);
+ curpos:=StrEndW(curpos)+1;
+ end;
+ end
+ else
+ begin
+ s:=#0#0#0'Sorry, but i don''t use WATrack right now!';
+ textpos:=s+3;
+ end;
+// encode
+ if not isNewRequest then
+ begin
+ if curpos<>nil then
+ begin
+ base64.pbDecoded:=PByte(buf);
+ base64.cbDecoded:=PAnsiChar(curpos)-PAnsiChar(buf);
+ end
+ else
+ begin
+ base64.pbDecoded:=PByte(s);
+ base64.cbDecoded:=(StrLenW(textpos)+3+1)*SizeOf(PWideChar);
+ end;
+ base64.cchEncoded:=Netlib_GetBase64EncodedBufferSize(base64.cbDecoded);
+ mGetMem(encbuf,base64.cchEncoded+1+Length(wpAnswer));
+ base64.pszEncoded:=PAnsiChar(encbuf)+Length(wpAnswer);
+ StrCopy(PAnsiChar(encbuf),wpAnswer);
+ CallService(MS_NETLIB_BASE64ENCODE,0,tlparam(@base64));
+ if (HistMask and hmOutInfo)<>0 then
+ AddEvent(ccs^.hContact,EVENTTYPE_WAT_ANSWER,DBEF_SENT,
+ base64.pbDecoded,base64.cbDecoded);
+ CallContactService(ccs^.hContact,PSS_MESSAGE,0,tlparam(encbuf));
+ end
+ else
+ begin
+ i:=WideToCombo(textpos,encbuf,UserCP);
+ if (HistMask and hmOutInfo)<>0 then
+ AddEvent(ccs^.hContact,EVENTTYPE_WAT_MESSAGE,DBEF_SENT,encbuf,i);
+// if CallContactService(ccs^.hContact,PSS_MESSAGEW,PREF_UNICODE,dword(encbuf))=
+// ACKRESULT_FAILED then
+ CallContactService(ccs^.hContact,PSS_MESSAGE,PREF_UNICODE,tlparam(encbuf));
+ end;
+ mFreeMem(encbuf);
+ end;
+ end
+ else
+ begin
+ if (HistMask and hmIRequest)<>0 then
+ AddEvent(ccs^.hContact,EVENTTYPE_WAT_REQUEST,DBEF_READ,nil,0,
+ PPROTORECVEVENT(ccs^.lParam)^.Timestamp);
+ if (HistMask and hmISend)<>0 then
+ begin
+//!! Request Error Answer
+ if isNewRequest then
+ pc:=PAnsiChar(buf)
+ else
+ begin
+ StrCopy(PAnsiChar(buf),wpError);
+ pc:=PAnsiChar(buf)+Length(wpError);
+ end;
+ StrCopy(pc,'Sorry, but you have no permission to obtain this info!');
+ CallContactService(ccs^.hContact,PSS_MESSAGE,0,tlparam(buf));
+ if (HistMask and hmOutError)<>0 then
+ begin
+ AddEvent(ccs^.hContact,EVENTTYPE_WAT_ERROR,DBEF_SENT,nil,0,
+ PPROTORECVEVENT(ccs^.lParam)^.Timestamp);
+ end;
+ end;
+ end;
+ end
+ else if StrCmp(PPROTORECVEVENT(ccs^.lParam)^.szMessage.a,wpAnswer,Length(wpAnswer))=0 then
+ begin
+// decode
+ base64.pszEncoded:=PPROTORECVEVENT(ccs^.lParam)^.szMessage.a+Length(wpAnswer);
+ base64.cchEncoded:=StrLen(base64.pszEncoded);
+ base64.cbDecoded :=Netlib_GetBase64DecodedBufferSize(base64.cchEncoded);
+ mGetMem(base64.pbDecoded,base64.cbDecoded);
+
+ CallService(MS_NETLIB_BASE64DECODE,0,tlparam(@base64));
+
+ curpos:=pWideChar(base64.pbDecoded); // pos_artist:=curpos;
+ while curpos^<>#0 do inc(curpos); inc(curpos); // pos_title :=curpos;
+ while curpos^<>#0 do inc(curpos); inc(curpos); // pos_album :=curpos;
+ while curpos^<>#0 do inc(curpos); inc(curpos);
+ pos_template:=curpos;
+
+ if (HistMask and hmInInfo)<>0 then
+ AddEvent(ccs^.hContact,EVENTTYPE_WAT_ANSWER,DBEF_READ,
+ base64.pbDecoded,base64.cbDecoded,
+ PPROTORECVEVENT(ccs^.lParam)^.Timestamp);
+// Action
+
+ StrCopyW(buf,TranslateW('Music Info from '));
+ StrCatW (buf,PWideChar(CallService(MS_CLIST_GETCONTACTDISPLAYNAME,ccs^.hContact,GCDNF_UNICODE)));
+
+ MessageBoxW(0,TranslateW(pos_template),buf,MB_ICONINFORMATION);
+
+ mFreeMem(base64.pbDecoded);
+ end
+ else if StrCmp(PPROTORECVEVENT(ccs^.lParam)^.szMessage.a,wpError,Length(wpError))=0 then
+ begin
+ if (HistMask and hmInError)<>0 then
+ AddEvent(ccs^.hContact,EVENTTYPE_WAT_ERROR,DBEF_READ,nil,0,
+ PPROTORECVEVENT(ccs^.lParam)^.Timestamp);
+{
+ AnsiToWide(PAnsiChar(CallService(MS_CLIST_GETCONTACTDISPLAYNAME,ccs^.hContact,0)),s);
+ StrCopyW(buf,s);
+ StrCatW (buf,TranslateW(' answer you'));
+ mFreeMem(s);
+}
+ MessageBoxA(0,Translate(PPROTORECVEVENT(ccs^.lParam)^.szMessage.a+Length(wpError)),
+ Translate('You Get Error'),MB_ICONERROR);
+ end
+ else
+ result:=CallService(MS_PROTO_CHAINRECV,wParam,lParam);
+ mFreeMem(buf);
+end;
+
+function SendRequest(hContact:WPARAM;lParam:LPARAM):integer; cdecl;
+var
+ buf:array [0..2047] of AnsiChar;
+begin
+ result:=0;
+ StrCopy(buf,wpRequest);
+ StrCopy(buf+Length(wpRequest),SendRequestText);
+ CallContactService(hContact,PSS_MESSAGE,0,tlparam(@buf));
+ if (HistMask and hmOutRequest)<>0 then
+ AddEvent(hContact,EVENTTYPE_WAT_REQUEST,DBEF_SENT,nil,0);
+end;
+
+procedure RegisterContacts;
+var
+ hContact:integer;
+begin
+ hContact:=CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ while hContact<>0 do
+ begin
+ if not IsChat(hContact) then
+ CallService(MS_PROTO_ADDTOCONTACT,hContact,lparam(PluginShort));
+ hContact:=CallService(MS_DB_CONTACT_FINDNEXT,hContact,0);
+ end;
+end;
+
+function HookAddUser(hContact:WPARAM;lParam:LPARAM):integer; cdecl;
+begin
+ result:=0;
+ if not IsChat(hContact) then
+ CallService(MS_PROTO_ADDTOCONTACT,hContact,tlparam(PluginShort));
+end;
+
+function OnContactMenu(hContact:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ mi:TCListMenuItem;
+begin
+ FillChar(mi,SizeOf(mi),0);
+ mi.cbSize:=sizeof(mi);
+ if IsMirandaUser(hContact)<=0 then
+ mi.flags:=CMIF_NOTOFFLINE or CMIF_NOTOFFLIST or CMIM_FLAGS or CMIF_HIDDEN
+ else
+ mi.flags:=CMIF_NOTOFFLINE or CMIF_NOTOFFLIST or CMIM_FLAGS;
+ CallService(MS_CLIST_MODIFYMENUITEM,hContactMenuItem,tlparam(@mi));
+ result:=0;
+end;
+
+procedure SetProtocol;
+var
+ desc:TPROTOCOLDESCRIPTOR;
+begin
+ desc.cbSize:=PROTOCOLDESCRIPTOR_V3_SIZE;//SizeOf(desc);
+ desc.szName:=PluginShort;
+ desc._type :=PROTOTYPE_TRANSLATION;
+
+ CallService(MS_PROTO_REGISTERMODULE,0,lparam(@desc));
+// CreateProtoServiceFunction(PluginShort,PSS_MESSAGE ,@SendMessageProcW);
+// CreateProtoServiceFunction(PluginShort,PSS_MESSAGEW,@SendMessageProcW);
+ hSRM:=CreateProtoServiceFunction(PluginShort,PSR_MESSAGE ,@ReceiveMessageProcW);
+// CreateProtoServiceFunction(PluginShort,PSR_MESSAGEW,@ReceiveMessageProcW);
+end;
+
+function IconChanged(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ mi:TCListMenuItem;
+begin
+ result:=0;
+ FillChar(mi,SizeOf(mi),0);
+ mi.cbSize:=sizeof(mi);
+ mi.flags :=CMIM_ICON;
+
+ mi.hIcon:=CallService(MS_SKIN2_GETICON,0,tlparam(IcoBtnContext));
+ CallService(MS_CLIST_MODIFYMENUITEM,hContactMenuItem,tlparam(@mi));
+end;
+
+procedure RegisterIcons;
+var
+ sid:TSKINICONDESC;
+begin
+ FillChar(sid,SizeOf(TSKINICONDESC),0);
+ sid.cbSize:=SizeOf(TSKINICONDESC);
+ sid.cx:=16;
+ sid.cy:=16;
+ sid.szSection.a:=PluginShort;
+
+ sid.hDefaultIcon :=LoadImage(hInstance,MAKEINTRESOURCE(BTN_CONTEXT),IMAGE_ICON,16,16,0);
+ sid.pszName :=IcoBtnContext;
+ sid.szDescription.a:='Context Menu';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+//!!
+ icchangedhook:=HookEvent(ME_SKIN2_ICONSCHANGED,@IconChanged);
+end;
+
+// ------------ base interface functions -------------
+
+function InitProc(aGetStatus:boolean=false):integer;
+var
+ mi:TCListMenuItem;
+begin
+ if aGetStatus then
+ begin
+ if GetModStatus=0 then
+ begin
+ result:=0;
+ exit;
+ end;
+ end
+ else
+ SetModStatus(1);
+ result:=1;
+
+ ReadOptions;
+ RegisterIcons;
+
+ FillChar(mi, sizeof(mi), 0);
+ mi.cbSize :=sizeof(mi);
+ mi.szPopupName.a:=PluginShort;
+ mi.flags :=CMIF_NOTOFFLINE or CMIF_NOTOFFLIST;
+// mi.popupPosition:=MenuUserInfoPos;
+ mi.hIcon :=CallService(MS_SKIN2_GETICON,0,lparam(IcoBtnContext));
+ mi.szName.a :='Get user''s Music Info';
+ mi.pszService :=MS_WAT_GETCONTACTINFO;
+ hContactMenuItem:=Menu_AddContactMenuItem(@mi);
+
+ SetProtocol;
+ RegisterContacts;
+ hGCI:=CreateServiceFunction(MS_WAT_GETCONTACTINFO,@SendRequest);
+ contexthook :=HookEvent(ME_CLIST_PREBUILDCONTACTMENU,@OnContactMenu);
+ hAddUserHook:=HookEvent(ME_DB_CONTACT_ADDED ,@HookAddUser);
+end;
+
+procedure DeInitProc(aSetDisable:boolean);
+begin
+ if aSetDisable then
+ SetModStatus(0);
+
+ UnhookEvent(hAddUserHook);
+ UnhookEvent(contexthook);
+ UnhookEvent(icchangedhook);
+
+ DestroyServiceFunction(hSRM);
+ DestroyServiceFunction(hGCI);
+ mFreeMem(ProtoText);
+end;
+
+function AddOptionsPage(var tmpl:pAnsiChar;var proc:pointer;var name:PAnsiChar):integer;
+begin
+ tmpl:='MISC';
+ proc:=@DlgProcOptions;
+ name:='Misc';
+ result:=0;
+end;
+
+var
+ vproto:twModule;
+
+procedure Init;
+begin
+ vproto.Next :=ModuleLink;
+ vproto.Init :=@InitProc;
+ vproto.DeInit :=@DeInitProc;
+ vproto.AddOption :=@AddOptionsPage;
+ vproto.ModuleName:='Protocol';
+ ModuleLink :=@vproto;
+end;
+
+begin
+ Init;
+end.
diff --git a/plugins/Watrack/proto/proto.rc b/plugins/Watrack/proto/proto.rc
new file mode 100644
index 0000000000..dcf5be2faa
--- /dev/null
+++ b/plugins/Watrack/proto/proto.rc
@@ -0,0 +1,36 @@
+#include "i_proto_rc.inc"
+
+LANGUAGE 0,0
+
+MISC DIALOGEX 0, 0, 304, 226, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ CONTROL "",IDC_SHARE, "CListControl", WS_TABSTOP | 0x3CA, 4, 4, 144, 180, WS_EX_CLIENTEDGE
+
+ CTEXT "Save events in database",-1, 154, 4, 144, 12, SS_CENTERIMAGE
+ LTEXT "Input" ,-1, 154, 16, 70, 12, SS_CENTERIMAGE
+ RTEXT "Output" ,-1, 226, 16, 70, 12, SS_CENTERIMAGE
+
+ CTEXT "Music Info Request",-1, 170, 30, 112, 14, SS_CENTERIMAGE
+ AUTOCHECKBOX "", IDC_IN_REQUEST , 154, 30, 14, 14, BS_VCENTER | BS_NOTIFY
+ AUTOCHECKBOX "", IDC_OUT_REQUEST , 284, 30, 14, 14, BS_VCENTER | BS_NOTIFY | BS_RIGHT | BS_LEFTTEXT
+
+ CTEXT "Music Info" ,-1, 170, 44, 112, 14, SS_CENTERIMAGE
+ AUTOCHECKBOX "", IDC_IN_INFO , 154, 44, 14, 14, BS_VCENTER | BS_NOTIFY
+ AUTOCHECKBOX "", IDC_OUT_INFO , 284, 44, 14, 14, BS_VCENTER | BS_NOTIFY | BS_RIGHT | BS_LEFTTEXT
+
+ CTEXT "Request Error" ,-1, 170, 58, 112, 14, SS_CENTERIMAGE
+ AUTOCHECKBOX "", IDC_IN_ERROR , 154, 58, 14, 14, BS_VCENTER | BS_NOTIFY
+ AUTOCHECKBOX "", IDC_OUT_ERROR , 284, 58, 14, 14, BS_VCENTER | BS_NOTIFY | BS_RIGHT | BS_LEFTTEXT
+
+ AUTOCHECKBOX "Save ignored requests" , IDC_IREQUEST, 154, 76, 144, 14, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+ AUTOCHECKBOX "Answer to ignored requests", IDC_ISEND , 154, 90, 144, 14, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+
+ RTEXT "User music info text", -1, 154, 154, 142, 10
+ RTEXT "(%artist%, %title%, %album% and %year% macros can be used only)", -1, 154, 164, 142, 22
+ EDITTEXT IDC_PROTO_TEXT, 4, 186, 296, 36, ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
+}
+
+BTN_CONTEXT ICON "wat_context.ico"
diff --git a/plugins/Watrack/proto/proto.res b/plugins/Watrack/proto/proto.res
new file mode 100644
index 0000000000..7899ef50cc
--- /dev/null
+++ b/plugins/Watrack/proto/proto.res
Binary files differ
diff --git a/plugins/Watrack/proto/wat_context.ico b/plugins/Watrack/proto/wat_context.ico
new file mode 100644
index 0000000000..37d8413c66
--- /dev/null
+++ b/plugins/Watrack/proto/wat_context.ico
Binary files differ
diff --git a/plugins/Watrack/res/i_const.inc b/plugins/Watrack/res/i_const.inc
new file mode 100644
index 0000000000..b1cafde523
--- /dev/null
+++ b/plugins/Watrack/res/i_const.inc
@@ -0,0 +1,27 @@
+const
+ IDC_FIRST = 1025;
+
+{DLG 1 - base}
+const
+ IDC_CODEPAGE = 1025;
+ IDC_TIMER = 1026;
+ IDC_CHECKTIME = 1027;
+ IDC_IMPLANTANT = 1028;
+ IDC_MTHCHECK = 1029;
+ IDC_KEEPOLD = 1030;
+ IDC_STAT_TIMER = 1031;
+ IDC_COVERFN = 1032;
+ IDC_PLAYERLIST = 1033;
+ IDC_FORMATLIST = 1034;
+ IDC_CHK_PLAYER = 1035;
+ IDC_CHK_FORMAT = 1036;
+ IDC_APPCOMMAND = 1037;
+ IDC_CHECKALL = 1038;
+ IDC_TIMEOUT = 1039;
+
+{DLG 0 - modules}
+ IDC_MODULEGROUP = 1025;
+
+{Icons - same as in waticons.inc}
+IDI_PLUGIN_ENABLE = 100;
+IDI_PLUGIN_DISABLE = 101;
diff --git a/plugins/Watrack/res/wat_disable.ico b/plugins/Watrack/res/wat_disable.ico
new file mode 100644
index 0000000000..390f0852a2
--- /dev/null
+++ b/plugins/Watrack/res/wat_disable.ico
Binary files differ
diff --git a/plugins/Watrack/res/wat_enable.ico b/plugins/Watrack/res/wat_enable.ico
new file mode 100644
index 0000000000..0e20d3a616
--- /dev/null
+++ b/plugins/Watrack/res/wat_enable.ico
Binary files differ
diff --git a/plugins/Watrack/res/watrack.rc b/plugins/Watrack/res/watrack.rc
new file mode 100644
index 0000000000..2d3e6585d2
--- /dev/null
+++ b/plugins/Watrack/res/watrack.rc
@@ -0,0 +1,104 @@
+#include "i_const.inc"
+
+LANGUAGE 0,0
+
+BASIC DIALOGEX 0, 0, 304, 226, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ CTEXT "Unicode to Ansi translation codepage:", -1, 192, 2, 108, 20
+ COMBOBOX IDC_CODEPAGE, 192, 22, 108, 56, CBS_DROPDOWNLIST | WS_VSCROLL | NOT WS_TABSTOP
+ CTEXT "Refresh time, sec", IDC_STAT_TIMER, 226, 36, 76, 16, SS_CENTERIMAGE
+ EDITTEXT IDC_TIMER, 192, 38, 32, 12, ES_RIGHT | ES_NUMBER
+ AUTOCHECKBOX "Check file time" , IDC_CHECKTIME , 192, 52, 108, 16, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+ AUTOCHECKBOX "Keep old file" , IDC_KEEPOLD , 192, 68, 108, 16, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+ AUTOCHECKBOX "Emulate Multimedia keys" , IDC_APPCOMMAND, 192, 84, 108, 16, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+ AUTOCHECKBOX "Use process implantation" , IDC_IMPLANTANT, 192, 100, 108, 16, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+ AUTOCHECKBOX "Check all players" , IDC_CHECKALL , 192, 116, 108, 16, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+ AUTOCHECKBOX "Other thread handle check" , IDC_MTHCHECK , 192, 132, 108, 16, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+
+ EDITTEXT IDC_TIMEOUT , 192, 149, 20, 12, ES_RIGHT | ES_NUMBER
+ LTEXT "Timeout, ms", -1, 214, 148, 86, 14, SS_CENTERIMAGE
+
+ CONTROL "", IDC_PLAYERLIST, "SysListView32",
+ WS_BORDER | WS_TABSTOP | LVS_NOCOLUMNHEADER | LVS_SHOWSELALWAYS | LVS_SINGLESEL | LVS_REPORT,
+ 2, 18, 120, 190, WS_EX_CONTROLPARENT
+ CONTROL "", IDC_FORMATLIST, "SysListView32",
+ WS_BORDER | WS_TABSTOP | LVS_NOCOLUMNHEADER | LVS_SHOWSELALWAYS | LVS_SINGLESEL | LVS_REPORT,
+ 125, 18, 60, 190, WS_EX_CONTROLPARENT
+ CTEXT "Formats", -1, 125, 4, 64, 12
+ CTEXT "Players list\n(F1 for note)", -1, 2, 0, 120, 18
+ CTEXT "Check", -1, 2, 210, 72, 12, SS_CENTERIMAGE
+ PUSHBUTTON "None", IDC_CHK_PLAYER, 74, 210, 48, 12
+ PUSHBUTTON "None", IDC_CHK_FORMAT, 125, 210, 60, 12
+
+ CTEXT "Cover filenames", -1, 192, 164, 108, 10
+ EDITTEXT IDC_COVERFN, 192, 174, 108, 48, ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
+}
+
+COLOR DIALOGEX 0, 0, 96, 116, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_VISIBLE | WS_CAPTION
+EXSTYLE WS_EX_CONTROLPARENT
+CAPTION "Color codes"
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ DEFPUSHBUTTON "OK", IDOK, 35, 98, 26, 16, NOT WS_TABSTOP
+ RTEXT "1", -1, 4, 6, 10, 10
+ RTEXT "2", -1, 4, 17, 10, 10
+ RTEXT "3", -1, 4, 28, 10, 10
+ RTEXT "4", -1, 4, 39, 10, 10
+ RTEXT "5", -1, 4, 50, 10, 10
+ RTEXT "6", -1, 4, 61, 10, 10
+ RTEXT "7", -1, 4, 72, 10, 10
+ RTEXT "8", -1, 4, 83, 10, 10
+ RTEXT "9", -1, 46, 6, 10, 10
+ RTEXT "10", -1, 46, 17, 10, 10
+ RTEXT "11", -1, 46, 28, 10, 10
+ RTEXT "12", -1, 46, 39, 10, 10
+ RTEXT "13", -1, 46, 50, 10, 10
+ RTEXT "14", -1, 46, 61, 10, 10
+ RTEXT "15", -1, 46, 72, 10, 10
+ RTEXT "16", -1, 46, 83, 10, 10
+}
+
+IDI_PLUGIN_ENABLE ICON "wat_enable.ico"
+IDI_PLUGIN_DISABLE ICON "wat_disable.ico"
+
+PARTS DIALOGEX 0, 0, 304, 226, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ GROUPBOX "Switch ON these modules", IDC_MODULEGROUP, 2, 2, 188, 218, WS_TABSTOP
+}
+
+LANGUAGE 0,0
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 0,0,6,12
+ PRODUCTVERSION 0,0,6,12
+ FILEFLAGSMASK $3F
+ FILEOS 4
+ FILETYPE 2
+ FILESUBTYPE 0
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "CompanyName",""
+ VALUE "Comments", "Plugin to get, insert to messages and show currently played song info"
+ VALUE "FileDescription", "WATrack plugin for Miranda NG"
+ VALUE "FileVersion", "0, 0, 6, 12 "0
+ VALUE "InternalName", "WATrack"
+ VALUE "OriginalFilename", "watrack.dll"
+ VALUE "ProductName", " WATrack Dynamic Link Library (DLL)"
+ VALUE "ProductVersion", "0, 0, 6, 12 "0
+ VALUE "SpecialBuild", "17.11.2009 "0
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation",0,1200
+ END
+END
diff --git a/plugins/Watrack/res/watrack.res b/plugins/Watrack/res/watrack.res
new file mode 100644
index 0000000000..3c2d4932df
--- /dev/null
+++ b/plugins/Watrack/res/watrack.res
Binary files differ
diff --git a/plugins/Watrack/srv_format.pas b/plugins/Watrack/srv_format.pas
new file mode 100644
index 0000000000..aa892bb7da
--- /dev/null
+++ b/plugins/Watrack/srv_format.pas
@@ -0,0 +1,394 @@
+{format service}
+unit srv_format;
+
+interface
+
+uses windows,wat_api;
+
+procedure DefFillFormatList (hwndList:hwnd);
+procedure DefCheckFormatList(hwndList:hwnd);
+function ProcessFormatLink:integer;
+
+function GetFileFormatInfo(var dst:tSongInfo):integer;
+function CheckExt(fname:pWideChar):integer;
+
+function DeleteKnownExt(src:pWideChar):pWideChar;
+function KnownFileType(fname:PWideChar):boolean;
+function isContainer(fname:PWideChar):boolean;
+
+function ServiceFormat(wParam:WPARAM;lParam:LPARAM):integer;cdecl;
+procedure RegisterFormat(ext:PAnsiChar;proc:tReadFormatProc;flags:dword=0);
+procedure ClearFormats;
+
+type
+ MusEnumProc = function(param:PAnsiChar;lParam:LPARAM):bool;stdcall;
+
+function EnumFormats(param:MusEnumProc;lParam:LPARAM):bool;
+
+type
+ pwFormat = ^twFormat;
+ twFormat = record
+ This:tMusicFormat;
+ Next:pwFormat;
+ end;
+
+const
+ FormatLink:pwFormat=nil;
+
+implementation
+
+uses
+ CommCtrl,common;
+type
+ pFmtArray = ^tFmtArray;
+ tFmtArray = array [0..10] of tMusicFormat;
+
+const
+ StartSize = 32;
+ Step = 8;
+
+const
+ fmtLink:pFmtArray=nil;
+ FmtNum:integer=0;
+ FmtMax:integer=0;
+
+function ProcessFormatLink:integer;
+var
+ ptr:pwFormat;
+begin
+ result:=0;
+ ptr:=FormatLink;
+ while ptr<>nil do
+ begin
+ RegisterFormat(@ptr.This.ext,ptr.This.proc,ptr.This.flags);
+ inc(result);
+ ptr:=ptr^.Next;
+ end;
+end;
+
+function EnumFormats(param:MusEnumProc;lParam:LPARAM):bool;
+var
+ tmp:pFmtArray;
+ i,j:integer;
+ s:array [0..8] of AnsiChar;
+begin
+ if (FmtNum>0) and (@param<>nil) then
+ begin
+ GetMem(tmp,FmtNum*SizeOf(tMusicFormat));
+ move(fmtLink^,tmp^,FmtNum*SizeOf(tMusicFormat));
+ i:=0;
+ j:=FmtNum;
+ s[8]:=#0;
+ repeat
+ move(tmp^[i].ext,s,8);
+ if not param(s,lParam) then break;
+ inc(i);
+ until i=j;
+ FreeMem(tmp);
+ result:=true;
+ end
+ else
+ result:=false;
+end;
+
+function FindFormat(ext:PAnsiChar):integer;
+var
+ i:integer;
+ ss:array [0..7] of AnsiChar;
+begin
+ i:=0;
+ int64(ss):=0;
+ StrCopy(ss,ext,7);
+ while i<FmtNum do
+ begin
+ if int64(fmtLink^[i].ext)=int64(ss) then
+ begin
+ result:=i;
+ exit;
+ end;
+ inc(i);
+ end;
+ result:=WAT_RES_NOTFOUND;
+end;
+
+procedure DefFillFormatList(hwndList:hwnd);
+var
+ item:LV_ITEMA;
+ lvc:LV_COLUMN;
+ newItem:integer;
+ i:integer;
+ p:pMusicFormat;
+begin
+ FillChar(item,SizeOf(item),0);
+ FillChar(lvc,SizeOf(lvc),0);
+ ListView_SetExtendedListViewStyle(hwndList, LVS_EX_CHECKBOXES);
+ lvc.mask:=LVCF_FMT;
+ lvc.fmt :={LVCFMT_IMAGE or} LVCFMT_LEFT;
+ ListView_InsertColumn(hwndList,0,lvc);
+
+ item.mask:=LVIF_TEXT or LVIF_PARAM;
+ item.iItem:=1000;
+ i:=0;
+ while i<FmtNum do
+ begin
+ p:=@fmtLink^[i];
+ item.pszText:=@p^.ext;
+ item.lParam := p^.flags;
+ newItem:=SendMessageA(hwndList,LVM_INSERTITEMA,0,LPARAM(@item));
+ if newItem>=0 then
+ begin
+ if (p^.flags and WAT_OPT_DISABLED)=0 then
+ ListView_SetCheckState(hwndList,newItem,TRUE);
+ end;
+ inc(i);
+ end;
+ ListView_SetColumnWidth(hwndList,0,LVSCW_AUTOSIZE);
+end;
+
+procedure DefCheckFormatList(hwndList:hwnd);
+var
+ i,j,k:integer;
+ item:LV_ITEMA;
+ szTemp:array [0..109] of AnsiChar;
+ p:pMusicFormat;
+begin
+ FillChar(item,SizeOf(item),0);
+ item.mask:=LVIF_TEXT;
+ item.pszText:=@szTemp;
+ item.cchTextMax:=100;
+ k:=ListView_GetItemCount(hwndList)-1;
+ for i:=0 to k do
+ begin
+ item.iItem:=i;
+ SendMessageA(hwndList,LVM_GETITEMA,0,LPARAM(@item));
+ j:=FindFormat(item.pszText);
+ if j<>WAT_RES_NOTFOUND then // always?
+ begin
+ p:=@fmtLink^[j];
+ if ListView_GetCheckState(hwndList,i)=0 then
+ p^.flags:=p^.flags or WAT_OPT_DISABLED
+ else
+ p^.flags:=p^.flags and not WAT_OPT_DISABLED;
+ end;
+ end;
+end;
+
+function DeleteKnownExt(src:pWideChar):pWideChar;
+var
+ s :array [0..7] of WideChar;
+ ss:array [0..7] of AnsiChar;
+ i,j:integer;
+begin
+ GetExt(src,s);
+ if s[0]<>#0 then
+ begin
+ int64(ss):=0;
+ i:=0;
+ while (s[i]<>#0) and (i<8) do
+ begin
+ ss[i]:=AnsiChar(s[i]);
+ inc(i);
+ end;
+ j:=0;
+ while j<FmtNum do
+ begin
+ if int64(fmtLink^[j].ext)=int64(ss) then
+ begin
+ i:=StrLenW(s);
+ src[integer(StrLenW(src))-i-1]:=#0;
+ break;
+ end;
+ inc(j);
+ end;
+ end;
+ result:=src;
+end;
+
+function KnownFileType(fname:PWideChar):boolean;
+var
+ i:integer;
+ s :array [0..7] of WideChar;
+ ss:array [0..7] of AnsiChar;
+begin
+ result:=false;
+ if (fname=nil) or (fname^=#0) then
+ exit;
+ GetExt(fname,s);
+ int64(ss):=0;
+ if s[0]<>#0 then
+ begin
+ i:=0;
+ while (s[i]<>#0) and (i<8) do
+ begin
+ ss[i]:=AnsiChar(s[i]);
+ inc(i);
+ end;
+ i:=0;
+ while i<FmtNum do
+ begin
+ if (int64(fmtLink^[i].ext)=int64(ss)) then
+ begin
+ if ((fmtLink^[i].flags and WAT_OPT_DISABLED)=0) then
+ result:=true;
+ break;
+ end;
+ inc(i);
+ end;
+ end;
+end;
+
+function isContainer(fname:PWideChar):boolean;
+begin
+ if CheckExt(fname)=WAT_RES_OK then
+ begin
+ result:=(fmtLink^[0].flags and WAT_OPT_CONTAINER)<>0;
+ end
+ else
+ result:=false;
+end;
+
+function GetFileFormatInfo(var dst:tSongInfo):integer;
+begin
+ result:=CheckExt(dst.mfile);
+ if result=WAT_RES_OK then
+ begin
+ fmtLink^[0].proc(dst);
+ end;
+end;
+
+function CheckExt(fname:pWideChar):integer;
+var
+ i:integer;
+ tmp:tMusicFormat;
+ ls:array [0..7] of WideChar;
+ ss:array [0..7] of AnsiChar;
+begin
+ GetExt(fname,ls);
+ i:=0;
+ int64(ss):=0;
+ while (ls[i]<>#0) and (i<8) do
+ begin
+ ss[i]:=AnsiChar(ls[i]);
+ inc(i);
+ end;
+ i:=0;
+ while i<FmtNum do
+ begin
+ if (int64(fmtLink^[i].ext)=int64(ss)) then
+ begin
+ if ((fmtLink^[i].flags and WAT_OPT_DISABLED)=0) then
+ begin
+ if i>0 then
+ begin
+ tmp:=fmtLink^[i];
+ move(fmtLink^[0],fmtLink^[1],SizeOf(tMusicFormat)*i);
+ fmtLink^[0]:=tmp;
+ end;
+ result:=WAT_RES_OK;
+ exit;
+ end
+ else
+ break;
+ end;
+ inc(i);
+ end;
+ result:=WAT_RES_NOTFOUND;
+end;
+
+function ServiceFormat(wParam:WPARAM;lParam:LPARAM):integer;cdecl;
+var
+ p:integer;
+ nl:pFmtArray;
+begin
+ result:=WAT_RES_NOTFOUND;
+ if LoWord(wParam)<>WAT_ACT_REGISTER then
+ p:=FindFormat(PAnsiChar(lParam))
+ else
+ p:=0;
+ case LoWord(wParam) of
+ WAT_ACT_REGISTER: begin
+ if @pMusicFormat(lParam)^.proc=nil then
+ exit;
+ p:=FindFormat(pMusicFormat(lParam)^.ext);
+ if (p=WAT_RES_NOTFOUND) or ((wParam and WAT_ACT_REPLACE)<>0) then
+ begin
+ if (p<>WAT_RES_NOTFOUND) and ((fmtLink^[p].flags and WAT_OPT_ONLYONE)<>0) then
+ exit;
+ if FmtNum=FmtMax then // expand array when append
+ begin
+ if FmtMax=0 then
+ FmtMax:=StartSize
+ else
+ inc(FmtMax,Step);
+ GetMem(nl,FmtMax*SizeOf(tMusicFormat));
+ if fmtLink<>nil then
+ begin
+ move(fmtLink^,nl^,FmtNum*SizeOf(tMusicFormat));
+ FreeMem(fmtLink);
+ end;
+ fmtLink:=nl;
+ end;
+ if p=WAT_RES_NOTFOUND then
+ begin
+ p:=FmtNum;
+ result:=WAT_RES_OK;
+ inc(FmtNum);
+ end
+ else
+ result:=int_ptr(@fmtLink^[p].proc);
+ move(pMusicFormat(lParam)^,fmtLink^[p],SizeOf(tMusicFormat));// fill
+ end;
+ end;
+ WAT_ACT_UNREGISTER: begin
+ if p<>WAT_RES_NOTFOUND then
+ begin
+ dec(FmtNum);
+ if p<FmtNum then // last
+ Move(fmtLink^[p+1],fmtLink^[p],SizeOf(tMusicFormat)*(FmtNum-p));
+ result:=WAT_RES_OK;
+ end;
+ end;
+ WAT_ACT_DISABLE: begin
+ if p<>WAT_RES_NOTFOUND then
+ begin
+ fmtLink^[p].flags:=fmtLink^[p].flags or WAT_OPT_DISABLED;
+ result:=WAT_RES_DISABLED
+ end;
+ end;
+ WAT_ACT_ENABLE: begin
+ if p<>WAT_RES_NOTFOUND then
+ begin
+ fmtLink^[p].flags:=fmtLink^[p].flags and not WAT_OPT_DISABLED;
+ result:=WAT_RES_ENABLED
+ end;
+ end;
+ WAT_ACT_GETSTATUS: begin
+ if p<>WAT_RES_NOTFOUND then
+ begin
+ if (fmtLink^[p].flags and WAT_OPT_DISABLED)<>0 then
+ result:=WAT_RES_DISABLED
+ else
+ result:=WAT_RES_ENABLED;
+ end;
+ end;
+ end;
+end;
+
+procedure RegisterFormat(ext:PAnsiChar;proc:tReadFormatProc;flags:dword=0);
+var
+ tmp:tMusicFormat;
+begin
+ FillChar(tmp,SizeOf(tMusicFormat),0);
+ StrCopy (tmp.ext,ext,7);
+ tmp.proc:=proc;
+ tmp.flags:=flags;
+ ServiceFormat(WAT_ACT_REGISTER,LPARAM(@tmp));
+end;
+
+procedure ClearFormats;
+begin
+ if FmtNum>0 then
+ FreeMem(fmtLink);
+end;
+
+end.
diff --git a/plugins/Watrack/srv_player.pas b/plugins/Watrack/srv_player.pas
new file mode 100644
index 0000000000..60fd6534f4
--- /dev/null
+++ b/plugins/Watrack/srv_player.pas
@@ -0,0 +1,1220 @@
+{player service}
+unit srv_player;
+
+interface
+
+uses windows,common,wat_api;
+
+function GetPlayerNote(name:PAnsiChar):pWideChar;
+
+function SetPlayerIcons(fname:pAnsiChar):integer;
+
+function LoadFromFile(fname:PAnsiChar):integer;
+function ProcessPlayerLink:integer;
+
+function ServicePlayer(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+function SendCommand (wParam:WPARAM;lParam:LPARAM;flags:integer):int_ptr;
+
+procedure ClearPlayers;
+
+// options procedures
+procedure DefFillPlayerList (hwndList:hwnd);
+procedure DefCheckPlayerList(hwndList:hwnd);
+
+type
+ MusEnumProc = function(param:PAnsiChar;lParam:LPARAM):bool;stdcall;
+
+function EnumPlayers(param:MusEnumProc;lParam:LPARAM):bool;
+
+// "Get info" procedures
+function CheckPlayers (var dst:tSongInfo;flags:cardinal):integer;
+function CheckFile (var dst:tSongInfo;flags:cardinal;timeout:cardinal):integer;
+function GetChangingInfo(var dst:tSongInfo;flags:cardinal):integer;
+function GetInfo (var dst:tSongInfo;flags:cardinal):integer;
+
+// support procedures
+procedure ClearSongInfoData(var dst:tSongInfo;withFree:bool);
+procedure ClearPlayerInfo (var dst:tSongInfo;withFree:bool);
+procedure ClearFileInfo (var dst:tSongInfo;withFree:bool);
+procedure ClearChangingInfo(var dst:tSongInfo;withFree:bool);
+procedure ClearTrackInfo (var dst:tSongInfo;withFree:bool);
+
+procedure CopyPlayerInfo (const src:tSongInfo;var dst:tSongInfo);
+procedure CopyFileInfo (const src:tSongInfo;var dst:tSongInfo);
+procedure CopyChangingInfo(const src:tSongInfo;var dst:tSongInfo);
+procedure CopyTrackInfo (const src:tSongInfo;var dst:tSongInfo);
+
+type
+ pwPlayer = ^twPlayer;
+ twPlayer = record
+ This:pPlayerCell;
+ Next:pwPlayer;
+ end;
+
+const
+ PlayerLink:pwPlayer=nil;
+
+implementation
+
+uses
+ shellapi,CommCtrl
+ ,appcmdapi,io,syswin,wrapper,srv_format,winampapi,msninfo,memini;
+
+type
+ pPlyArray = ^tPlyArray;
+ tPlyArray = array [0..10] of tPlayerCell;
+
+type
+ pTmplCell = ^tTmplCell;
+ tTmplCell = record
+ p_class,
+ p_text :PAnsiChar;
+ p_class1,
+ p_text1 :PAnsiChar;
+ p_file :PAnsiChar;
+ p_prefix :pWideChar;
+ p_postfix:pWideChar;
+ end;
+
+const
+ StartSize = 32;
+ Step = 8;
+ buflen = 2048;
+
+const
+ plyLink:pPlyArray=nil;
+ PlyNum:integer=0;
+ PlyMax:integer=0;
+
+function ProcessPlayerLink:integer;
+var
+ ptr:pwPlayer;
+begin
+ ptr:=PlayerLink;
+ result:=0;
+ while ptr<>nil do
+ begin
+ ServicePlayer(WAT_ACT_REGISTER,lparam(ptr.This));
+ ptr:=ptr^.Next;
+ inc(result);
+ end;
+end;
+
+function SetPlayerIcons(fname:pAnsiChar):integer;
+var
+ i,j:integer;
+ buf:array [0..255] of AnsiChar;
+ p,pp:pAnsiChar;
+ lhIcon:HICON;
+begin
+ result:=LoadLibraryA(fname);
+ if result<>0 then
+ begin
+ p:=StrCopyE(buf,'Player_');
+ i:=0;
+ while i<PlyNum do
+ begin
+ with plyLink^[i] do
+ begin
+ pp:=p;
+ for j:=0 to StrLen(Desc)-1 do
+ begin
+ if Desc[j] in sLatWord then
+ pp^:=UpCase(Desc[j])
+ else
+ pp^:='_';
+ inc(pp);
+ end;
+ pp^:=#0;
+ lhIcon:=LoadImageA(result,buf,IMAGE_ICON,16,16,0);
+ if lhIcon>0 then
+ begin
+ if Icon<>0 then
+ DestroyIcon(Icon);
+ Icon:=lhIcon;
+ end;
+ end;
+ inc(i);
+ end;
+ FreeLibrary(result);
+ end;
+end;
+
+function EnumPlayers(param:MusEnumProc;lParam:LPARAM):bool;
+var
+ tmp:pPlyArray;
+ i,j:integer;
+begin
+ if (PlyNum>0) and (@param<>nil) then
+ begin
+ GetMem(tmp,PlyNum*SizeOf(tPlayerCell));
+ move(PlyLink^,tmp^,PlyNum*SizeOf(tPlayerCell));
+ i:=0;
+ j:=PlyNum;
+ repeat
+ if not param(tmp^[i].Desc,lParam) then break;
+ inc(i);
+ until i=j;
+ FreeMem(tmp);
+ result:=true;
+ end
+ else
+ result:=false;
+end;
+
+procedure PreProcess; // BASS to start
+var
+ i:integer;
+ tmp:tPlayerCell;
+begin
+ i:=1;
+ while i<(PlyNum-1) do
+ begin
+ if (plyLink^[i].flags and WAT_OPT_FIRST)<>0 then
+ begin
+ tmp:=plyLink^[i];
+ move(plyLink^[0],plyLink^[1],SizeOf(tPlayerCell)*i);
+ plyLink^[0]:=tmp;
+{
+ move(plyLink^[i],tmp,SizeOf(tPlayerCell));
+ move(plyLink^[0],plyLink^[1],SizeOf(tPlayerCell)*i);
+ move(tmp,plyLink^[0],SizeOf(tPlayerCell));
+}
+ break;
+ end;
+ inc(i);
+ end;
+ if (plyLink^[0].flags and WAT_OPT_LAST)<>0 then
+ begin
+ tmp:=plyLink^[0];
+ move(plyLink^[1],plyLink^[0],SizeOf(tPlayerCell)*(PlyNum-1));
+ plyLink^[PlyNum-1]:=tmp;
+{
+ move(plyLink^[0],tmp,SizeOf(tPlayerCell));
+ move(plyLink^[1],plyLink^[0],SizeOf(tPlayerCell)*(PlyNum-1));
+ move(tmp,plyLink^[PlyNum-1],SizeOf(tPlayerCell));
+}
+ end;
+end;
+
+procedure PostProcess; // Winamp clone to the end
+var
+ i,j:integer;
+ tmp:tPlayerCell;
+begin
+ i:=1;
+ j:=PlyNum-1;
+ while i<j do
+ begin
+ if (plyLink^[i].flags and WAT_OPT_LAST)<>0 then
+ begin
+ tmp:=plyLink^[i];
+ move(plyLink^[i+1],plyLink^[i],SizeOf(tPlayerCell)*(PlyNum-i-1));
+ plyLink^[PlyNum-1]:=tmp;
+{
+ move(plyLink^[i],tmp,SizeOf(tPlayerCell));
+ move(plyLink^[i+1],plyLink^[i],SizeOf(tPlayerCell)*(PlyNum-i-1));
+ move(tmp,plyLink^[PlyNum-1],SizeOf(tPlayerCell));
+}// break;
+ i:=1;
+ dec(j);
+ continue;
+ end;
+ inc(i);
+ end;
+end;
+
+function FindPlayer(desc:PAnsiChar):integer;
+var
+ i:integer;
+begin
+ if (desc<>nil) and (desc^<>#0) then
+ begin
+ i:=0;
+ while i<PlyNum do
+ begin
+ if lstrcmpia(plyLink^[i].Desc,desc)=0 then
+ begin
+ result:=i;
+ exit;
+ end;
+ inc(i);
+ end;
+ end;
+ result:=WAT_RES_NOTFOUND;
+end;
+
+function GetPlayerNote(name:PAnsiChar):pWideChar;
+var
+ i:integer;
+begin
+ i:=FindPlayer(name);
+ if i>=0 then
+ result:=plyLink^[i].Notes
+ else
+ result:=nil;
+end;
+
+procedure DefFillPlayerList(hwndList:hwnd);
+var
+ item:LV_ITEMA;
+ lvc:TLVCOLUMN;
+ i,newItem:integer;
+
+ il:HIMAGELIST; //!!
+begin
+ FillChar(item,SizeOf(item),0);
+ FillChar(lvc,SizeOf(lvc),0);
+ ListView_SetExtendedListViewStyle(hwndList, LVS_EX_CHECKBOXES);
+ lvc.mask:=LVCF_FMT or LVCF_WIDTH;
+
+ lvc.fmt:=LVCFMT_LEFT;
+ lvc.cx:=160;
+ ListView_InsertColumn(hwndList,0,lvc);
+
+ item.mask:=LVIF_TEXT or LVIF_IMAGE; //!!
+ i:=0;
+
+ il:=ImageList_Create(16,16,ILC_COLOR32 or ILC_MASK,0,1); //!!
+ while i<PlyNum do
+ begin
+ item.iImage:=ImageList_AddIcon(il,plyLink^[i].Icon);
+ item.iItem:=i;
+ item.pszText:=plyLink^[i].Desc;
+ newItem:=SendMessageA(hwndList,LVM_INSERTITEMA,0,lparam(@item));
+ if newItem>=0 then
+ begin
+ if (plyLink^[i].flags and WAT_OPT_DISABLED)=0 then
+ ListView_SetCheckState(hwndList,newItem,TRUE);
+ end;
+ inc(i);
+ end;
+ ImageList_Destroy(SendMessage(hwndList,LVM_SETIMAGELIST,LVSIL_SMALL,il)); //!!
+// ListView_SetColumnWidth(hwndList,0,LVSCW_AUTOSIZE);
+end;
+
+procedure DefCheckPlayerList(hwndList:hwnd);
+var
+ i,j,k:integer;
+ item:LV_ITEMA;
+ szTemp:array [0..109] of AnsiChar;
+ p:pPlayerCell;
+begin
+ FillChar(item,SizeOf(item),0);
+ item.mask :=LVIF_TEXT;
+ item.pszText :=@szTemp;
+ item.cchTextMax:=100;
+ k:=ListView_GetItemCount(hwndList)-1;
+ for i:=0 to k do
+ begin
+ item.iItem:=i;
+ SendMessageA(hwndList,LVM_GETITEMA,0,lparam(@item));
+ j:=FindPlayer(item.pszText);
+ if j<>WAT_RES_NOTFOUND then
+ begin
+ p:=@plyLink^[j];
+ if ListView_GetCheckState(hwndList,i)=0 then
+ p^.flags:=p^.flags or WAT_OPT_DISABLED
+ else
+ p^.flags:=p^.flags and not WAT_OPT_DISABLED;
+ end;
+ end;
+end;
+
+procedure ClearTemplate(tmpl:pTmplCell);
+begin
+ with tmpl^ do
+ begin
+ mFreeMem(p_class);
+ mFreeMem(p_text);
+ mFreeMem(p_class1);
+ mFreeMem(p_text1);
+ mFreeMem(p_file);
+ mFreeMem(p_prefix);
+ mFreeMem(p_postfix);
+ end;
+ FreeMem(tmpl);
+end;
+
+function ServicePlayer(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+var
+ p:integer;
+ i:integer;
+ nl:pPlyArray;
+ tmp:tPlayerCell;
+begin
+ result:=WAT_RES_ERROR;
+ if LoWord(wParam)=WAT_ACT_REGISTER then
+ begin
+ if pPlayerCell(lParam)^.Check=nil then
+ exit;
+ p:=0;
+ end
+ else
+ p:=FindPlayer(PAnsiChar(lParam));
+ case LoWord(wParam) of
+
+ WAT_ACT_REGISTER: begin
+ p:=FindPlayer(pPlayerCell(lParam)^.Desc);
+ if (p=WAT_RES_NOTFOUND) or ((wParam and WAT_ACT_REPLACE)<>0) then
+ begin
+ if (p<>WAT_RES_NOTFOUND) and ((plyLink^[p].flags and WAT_OPT_ONLYONE)<>0) then
+ exit;
+
+ if p=WAT_RES_NOTFOUND then
+ begin
+ p:=PlyNum;
+ result:=WAT_RES_OK;
+ inc(PlyNum);
+
+ if PlyNum>PlyMax then // expand array when append
+ begin
+ if PlyMax=0 then
+ PlyMax:=StartSize
+ else
+ inc(PlyMax,Step);
+ GetMem(nl,PlyMax*SizeOf(tPlayerCell));
+ if plyLink<>nil then
+ begin
+ move(plyLink^,nl^,PlyNum*SizeOf(tPlayerCell));
+ FreeMem(plyLink);
+ end;
+ plyLink:=nl;
+ end;
+ FillChar(plyLink^[p],SizeOf(tPlayerCell),0);
+// doubling notes
+ if (pPlayerCell(lParam)^.Notes<>nil) and
+ ((pPlayerCell(lParam)^.flags and WAT_OPT_TEMPLATE)=0) then
+ begin
+ i:=(StrLenW(pPlayerCell(lParam)^.Notes)+1)*SizeOf(WideChar);
+ GetMem(plyLink^[p].Notes,i);
+ move(pPlayerCell(lParam)^.Notes^,plyLink^[p].Notes^,i);
+ end
+ else
+ plyLink^[p].Notes:=pPlayerCell(lParam)^.Notes;
+
+// doubling description
+ i:=StrLen(pPlayerCell(lParam)^.Desc)+1;
+ GetMem(plyLink^[p].Desc,i);
+ move(pPlayerCell(lParam)^.Desc^,plyLink^[p].Desc^,i);
+
+// doubling url
+
+ if pPlayerCell(lParam)^.URL<>nil then
+ begin
+ with plyLink^[p] do
+ begin
+ i:=StrLen(pPlayerCell(lParam)^.URL)+1;
+ GetMem(URL,i);
+ move(pPlayerCell(lParam)^.URL^,URL^,i);
+ end;
+ end
+ else
+ plyLink^[p].URL:=nil;
+
+ end
+ else // existing player
+ begin
+ if (plyLink^[p].flags and WAT_OPT_TEMPLATE)=0 then
+ result:=int_ptr(plyLink^[p].Check)
+ else
+ begin // remove any info from templates
+ result:=WAT_RES_OK;
+ ClearTemplate(pTmplCell(plyLink^[p].Check));
+ end;
+ end;
+ // fill info
+ with plyLink^[p] do
+ begin
+ flags:=pPlayerCell(lParam)^.flags;
+ if URL<>nil then
+ flags:=flags or WAT_OPT_HASURL;
+ if pPlayerCell(lParam)^.Icon<>0 then
+ begin
+ if icon<>0 then
+ DestroyIcon(icon);
+ icon:=CopyIcon(pPlayerCell(lParam)^.Icon);
+ end;
+ Init :=pPlayerCell(lParam)^.Init;
+ DeInit :=pPlayerCell(lParam)^.DeInit;
+ Check :=pPlayerCell(lParam)^.Check;
+ GetStatus:=pPlayerCell(lParam)^.GetStatus;
+ GetName :=pPlayerCell(lParam)^.GetName;
+ GetInfo :=pPlayerCell(lParam)^.GetInfo;
+ Command :=pPlayerCell(lParam)^.Command;
+ if Init<>nil then
+ tInitProc(Init);
+ end;
+
+// PreProcess;
+ PostProcess;
+ end;
+ end;
+
+ WAT_ACT_UNREGISTER: begin
+ if p<>WAT_RES_NOTFOUND then
+ begin
+ dec(PlyNum);
+ if plyLink^[p].DeInit<>nil then
+ tDeInitProc(plyLink^[p].DeInit);
+ FreeMem(plyLink^[p].Desc);
+ if (plyLink^[p].flags and WAT_OPT_TEMPLATE)<>0 then
+ ClearTemplate(pTmplCell(plyLink^[p].Check));
+ if p<PlyNum then // not last
+ Move(plyLink^[p+1],plyLink^[p],SizeOf(tPlayerCell)*(PlyNum-p));
+ result:=WAT_RES_OK;
+ end;
+ end;
+
+ WAT_ACT_DISABLE: begin
+ if p<>WAT_RES_NOTFOUND then
+ begin
+ plyLink^[p].flags:=plyLink^[p].flags or WAT_OPT_DISABLED;
+ result:=WAT_RES_DISABLED
+ end;
+ end;
+
+ WAT_ACT_ENABLE: begin
+ if p<>WAT_RES_NOTFOUND then
+ begin
+ plyLink^[p].flags:=plyLink^[p].flags and not WAT_OPT_DISABLED;
+ result:=WAT_RES_ENABLED
+ end;
+ end;
+
+ WAT_ACT_GETSTATUS: begin
+ if p<>WAT_RES_NOTFOUND then
+ begin
+ if (plyLink^[p].flags and WAT_OPT_DISABLED)<>0 then
+ result:=WAT_RES_DISABLED
+ else
+ result:=WAT_RES_ENABLED;
+ end;
+ end;
+
+ WAT_ACT_SETACTIVE: begin
+ if p>0 then
+ begin
+ tmp:=plyLink^[p];
+ move(plyLink^[0],plyLink^[1],SizeOf(tPlayerCell)*p);
+ plyLink^[0]:=tmp;
+{
+ move(plyLink^[p],tmp ,SizeOf(tPlayerCell));
+ move(plyLink^[0],plyLink^[1],SizeOf(tPlayerCell)*p);
+ move(tmp ,plyLink^[0],SizeOf(tPlayerCell));
+}
+ end;
+// PreProcess;
+// PostProcess;
+ end;
+
+ end;
+end;
+
+function LoadFromFile(fname:PAnsiChar):integer;
+var
+ buf:pAnsiChar;
+ ptr:PAnsiChar;
+ NumPlayers:integer;
+ pcell:pTmplCell;
+ rec:tPlayerCell;
+ st,sec:pointer;
+begin
+ result:=0;
+ st:=OpenStorage(fname);
+ if st=nil then exit;
+
+ buf:=GetSectionList(st);
+ ptr:=buf;
+ NumPlayers:=0;
+ while ptr^<>#0 do
+ begin
+ sec:=SearchSection(st,ptr);
+
+ FillChar(rec,SizeOf(rec),0);
+
+ GetMem(pcell,SizeOf(tTmplCell));
+ StrDup(pcell^.p_class ,GetParamSectionStr(sec,'class' ));
+ StrDup(pcell^.p_text ,GetParamSectionStr(sec,'text' ));
+ StrDup(pcell^.p_class1,GetParamSectionStr(sec,'class1'));
+ StrDup(pcell^.p_text1 ,GetParamSectionStr(sec,'text1' ));
+ StrDup(pcell^.p_file ,GetParamSectionStr(sec,'file' ));
+
+ AnsiToWide(GetParamSectionStr(sec,'prefix' ),pcell^.p_prefix );
+ AnsiToWide(GetParamSectionStr(sec,'postfix'),pcell^.p_postfix);
+
+ rec.URL :=GetParamSectionStr(sec,'url');
+ rec.Desc :=ptr;
+ rec.flags:=GetParamSectionInt(sec,'flags') or WAT_OPT_TEMPLATE;
+ rec.Check:=pointer(pcell);
+
+ UTF8ToWide(GetParamSectionStr(sec,'notes'),rec.Notes);
+
+ ServicePlayer(WAT_ACT_REGISTER,lparam(@rec));
+
+ inc(NumPlayers);
+ while ptr^<>#0 do inc(ptr);
+ inc(ptr);
+ end;
+
+ FreeSectionList(buf);
+ CloseStorage(st);
+ result:=NumPlayers;
+end;
+
+function CheckTmpl(lwnd:HWND;cell:pTmplCell;flags:integer):HWND;
+var
+ tmp,EXEName:PAnsiChar;
+ ltmp,lcycle:boolean;
+ lclass,ltext:PAnsiChar;
+begin
+ lclass:=cell.p_class;
+ ltext :=cell.p_text;
+ lcycle:=false;
+ repeat
+ result:=lwnd;
+ if (lclass<>nil) or (ltext<>nil) then
+ repeat
+ result:=FindWindowExA(0,result,lclass,ltext);
+ if result=0 then
+ break;
+// check filename
+ if cell.p_file<>NIL then
+ begin
+ tmp:=Extract(GetEXEByWnd(result,EXEName),true);
+ mFreeMem(EXEName);
+ ltmp:=lstrcmpia(tmp,cell.p_file)=0;
+ mFreeMem(tmp);
+ if not ltmp then
+ continue;
+ end;
+ exit;
+ until false;
+ if lcycle then break;
+ lclass:=cell.p_class1;
+ ltext :=cell.p_text1;
+ if (lclass=nil) and (ltext=nil) then break;
+ lcycle:=not lcycle;
+ until false;
+end;
+
+// find active player
+function CheckAllPlayers(flags:integer;var status:integer; var PlayerChanged:bool):integer;
+const
+ PrevPlayerName:PAnsiChar=nil;
+var
+ stat,act,oldstat,i,j:integer;
+ tmp:tPlayerCell;
+ wwnd,lwnd:HWND;
+begin
+ i:=0;
+ result:=WAT_RES_NOTFOUND;
+ PlayerChanged:=true;
+ PreProcess;
+ oldstat:=-1;
+ act:=-1;
+ stat:=WAT_MES_UNKNOWN;
+ wwnd:=0;
+ while i<PlyNum do
+ begin
+ if (plyLink^[i].flags and WAT_OPT_DISABLED)=0 then
+ begin
+
+ lwnd:=0;
+ repeat
+ wwnd:=0;
+ stat:=WAT_MES_UNKNOWN;
+ if (plyLink^[i].flags and WAT_OPT_TEMPLATE)<>0 then
+ begin
+ lwnd:=CheckTmpl(lwnd,plyLink^[i].Check,plyLink^[i].flags);
+// find "Winamp" window
+ if (lwnd<>dword(WAT_RES_NOTFOUND)) and (lwnd<>0) and
+ ((plyLink^[i].flags and WAT_OPT_WINAMPAPI)<>0) then
+ begin
+ wwnd:=WinampFindWindow(lwnd);
+ if wwnd<>0 then
+ stat:=WinampGetStatus(wwnd);
+ end;
+ end
+ else
+ begin
+ with plyLink^[i] do
+ begin
+ lwnd:=tCheckProc(Check)(lwnd,flags);
+ if (lwnd<>dword(WAT_RES_NOTFOUND)) and (lwnd<>0) and (GetStatus<>nil) then
+ stat:=tStatusProc(GetStatus)(lwnd);
+ end;
+ end;
+ if (lwnd<>dword(WAT_RES_NOTFOUND)) and (lwnd<>0) then
+ begin
+ if (stat=WAT_MES_PLAYING) or ((flags and WAT_OPT_CHECKALL)=0) then
+ begin
+ act :=i;
+ result:=lwnd;
+ break;
+ end
+ else
+ begin
+ case stat of
+ WAT_MES_STOPPED: j:=00;
+ WAT_MES_UNKNOWN: j:=10;
+ WAT_MES_PAUSED : j:=20;
+ else
+ j:=00;
+ end;
+ if oldstat<j then
+ begin
+ oldstat:=j;
+ act :=i;
+ result :=lwnd;
+ end;
+ end;
+ end
+ else
+ break;
+ if (plyLink^[i].flags and WAT_OPT_SINGLEINST)<>0 then
+ break;
+ until false;
+ if (result<>WAT_RES_NOTFOUND) and (result<>0) and
+ ((stat=WAT_MES_PLAYING) or ((flags and WAT_OPT_CHECKALL)=0)) then
+ break;
+ end;
+ inc(i);
+ end;
+
+ if act>=0 then
+ begin
+ if result=1 then result:=0 //!! for example, mradio
+ else if wwnd<>0 then
+ result:=wwnd;
+ if act>0 then // to first position
+ begin
+ tmp:=plyLink^[act];
+ move(plyLink^[0],plyLink^[1],SizeOf(tPlayerCell)*act);
+ plyLink^[0]:=tmp;
+{
+ move(plyLink^[act],tmp ,SizeOf(tPlayerCell));
+ move(plyLink^[0 ],plyLink^[1],SizeOf(tPlayerCell)*act);
+ move(tmp ,plyLink^[0],SizeOf(tPlayerCell));
+}
+ end;
+ if PrevPlayerName=plyLink^[0].Desc then
+ PlayerChanged:=false
+ else
+ PrevPlayerName:=plyLink^[0].Desc;
+ status:=stat;
+ end
+ else
+ begin
+ PrevPlayerName:=nil;
+ status:=WAT_PLS_NOTFOUND+WAT_MES_UNKNOWN shl 16;
+ end;
+ PostProcess;
+end;
+
+function TranslateToApp(code:integer):integer;
+begin
+ case code of
+ WAT_CTRL_PREV : result:=APPCOMMAND_MEDIA_PREVIOUSTRACK;
+ WAT_CTRL_PLAY : begin
+ if IsW2K then // Win2k+ only
+ result:=APPCOMMAND_MEDIA_PLAY_PAUSE
+ else
+ result:=APPCOMMAND_MEDIA_PLAY;
+ end;
+ WAT_CTRL_PAUSE: result:=APPCOMMAND_MEDIA_PLAY_PAUSE;
+ WAT_CTRL_STOP : result:=APPCOMMAND_MEDIA_STOP;
+ WAT_CTRL_NEXT : result:=APPCOMMAND_MEDIA_NEXTTRACK;
+ WAT_CTRL_VOLDN: result:=APPCOMMAND_VOLUME_DOWN;
+ WAT_CTRL_VOLUP: result:=APPCOMMAND_VOLUME_UP;
+ else
+ result:=-1;
+ end;
+end;
+
+function SendCommand(wParam:WPARAM;lParam:LPARAM;flags:integer):int_ptr;
+var
+ dummy:bool;
+ wnd:HWND;
+ lstat:integer;
+begin
+ result:=WAT_RES_ERROR;
+ wnd:=CheckAllPlayers(flags,lstat,dummy);
+ if wnd<>dword(WAT_RES_NOTFOUND) then
+ if plyLink^[0].Command<>nil then
+ result:=tCommandProc(plyLink^[0].Command)(wnd,wParam,lParam)
+ else if (plyLink^[0].flags and WAT_OPT_WINAMPAPI)<>0 then
+ result:=WinampCommand(wnd,wParam+(lParam shl 16))
+ else if (flags and WAT_OPT_APPCOMMAND)<>0 then
+ begin
+ result:=TranslateToApp(wParam);
+ if result>=0 then
+ result:=SendMMCommand(wnd,result);
+ end;
+end;
+
+// Get Info (default)
+
+function GetSeparator(str:pWideChar):dword;
+begin
+ result:=StrIndexW(str,' '#$2013' ');
+ if result=0 then
+ result:=StrIndexW(str,' - ');
+ if result<>0 then
+ begin
+ result:=result-1 + (3 SHL 16);
+ exit;
+ end;
+ result:=StrIndexW(str,#$2013);
+ if result=0 then
+ result:=StrIndexW(str,'-');
+ if result>0 then
+ result:=result-1 + (1 SHL 16);
+end;
+
+function DefGetTitle(wnd:HWND;fname,wndtxt:pWideChar):pWideChar;
+var
+ i:integer;
+ tmp:pWideChar;
+begin
+ if fname<>nil then
+ tmp:=DeleteKnownExt(ExtractW(fname,true))
+ else
+ tmp:=wndtxt;
+ if tmp=nil then
+ begin
+ result:=nil;
+ exit;
+ end;
+ StrDupW(result,tmp);
+ i:=GetSeparator(result);
+ if i>0 then
+ StrCopyW(result,result+LoWord(i)+HiWord(i));
+ if fname<>nil then
+ mFreeMem(tmp);
+end;
+
+function DefGetArtist(wnd:HWND;fname,wndtxt:pWideChar):pWideChar;
+var
+ i:integer;
+ tmp:pWideChar;
+begin
+ if fname<>nil then
+ tmp:=DeleteKnownExt(ExtractW(fname,true))
+ else
+ tmp:=wndtxt;
+ if tmp=nil then
+ begin
+ result:=nil;
+ exit;
+ end;
+ StrDupW(result,tmp);
+ i:=GetSeparator(result);
+ if i>0 then
+ result[LoWord(i)]:=#0;
+ if fname<>nil then
+ mFreeMem(tmp);
+end;
+
+function DefGetVersionText(ver:integer):pWideChar;
+begin
+ if ver<>0 then
+ begin
+ mGetMem(result,10*SizeOf(WideChar));
+ IntToHex(result,ver);
+ end
+ else
+ result:=nil;
+end;
+
+function DefGetWndText(wnd:HWND):pWideChar;
+var
+ p:pWideChar;
+begin
+ if wnd<>0 then
+ begin
+ result:=GetDlgText(wnd);
+ if result<>nil then
+ begin
+ if (plyLink^[0].flags and WAT_OPT_TEMPLATE)<>0 then
+ begin
+ with pTmplCell(plyLink^[0].Check)^ do
+ begin
+ if p_prefix<>nil then
+ begin
+ p:=StrPosW(result,p_prefix);
+ if p=result then
+ StrCopyW(result,result+StrLenW(p_prefix));
+ end;
+ if p_postfix<>nil then
+ begin
+ p:=StrPosW(result,p_postfix);
+ if p<>nil then
+ p^:=#0;
+ end;
+ end;
+ end;
+ end;
+ end
+ else
+ result:=nil;
+end;
+
+procedure ClearSongInfoData(var dst:tSongInfo;withFree:bool);
+begin
+ ClearPlayerInfo (dst,withFree);
+ ClearChangingInfo(dst,withFree);
+ ClearFileInfo (dst,withFree);
+ ClearTrackInfo (dst,withFree);
+end;
+
+procedure CopyChangingInfo(const src:tSongInfo;var dst:tSongInfo);
+begin
+ dst.time :=src.time;
+ dst.volume :=src.volume;
+ dst.wndtext:=src.wndtext;
+end;
+
+procedure ClearChangingInfo(var dst:tSongInfo;withFree:bool);
+begin
+ dst.time :=0;
+ dst.volume:=0;
+
+ if withFree then
+ mFreeMem(dst.wndtext)
+ else
+ dst.wndtext:=nil;
+end;
+
+procedure CopyFileInfo(const src:tSongInfo;var dst:tSongInfo);
+begin
+ dst.fsize:=src.fsize;
+ dst.date :=src.date;
+ dst.mfile:=src.mfile;
+end;
+
+procedure ClearFileInfo(var dst:tSongInfo;withFree:bool);
+begin
+ if withFree then
+ mFreeMem(dst.mfile)
+ else
+ dst.mfile:=nil;
+ dst.fsize:=0;
+ dst.date :=0;
+end;
+
+procedure CopyPlayerInfo(const src:tSongInfo;var dst:tSongInfo);
+begin
+ dst.player :=src.player;
+ dst.txtver :=src.txtver;
+ dst.url :=src.url;
+ dst.icon :=src.icon;
+ dst.plyver :=src.plyver;
+ dst.plwnd :=src.plwnd;
+ dst.winampwnd:=src.winampwnd;
+end;
+
+procedure ClearPlayerInfo(var dst:tSongInfo;withFree:bool);
+begin
+ if withFree then
+ begin
+ mFreeMem(dst.player);
+ mFreeMem(dst.txtver);
+ mFreeMem(dst.url);
+ if dst.icon<>0 then
+ DestroyIcon(dst.icon);
+ end
+ else
+ begin
+ dst.player:=nil;
+ dst.txtver:=nil;
+ dst.url :=nil;
+ end;
+ dst.icon :=0;
+ dst.plyver :=0;
+ dst.plwnd :=0;
+ dst.winampwnd:=0;
+end;
+
+procedure CopyTrackInfo(const src:tSongInfo;var dst:tSongInfo);
+begin
+ dst.artist :=src.artist;
+ dst.title :=src.title;
+ dst.album :=src.album;
+ dst.genre :=src.genre;
+ dst.comment :=src.comment;
+ dst.year :=src.year;
+ dst.lyric :=src.lyric;
+ dst.cover :=src.cover;
+ dst.kbps :=src.kbps;
+ dst.khz :=src.khz;
+ dst.channels:=src.channels;
+ dst.track :=src.track;
+ dst.total :=src.total;
+ dst.vbr :=src.vbr;
+ dst.codec :=src.codec;
+ dst.width :=src.width;
+ dst.height :=src.height;
+ dst.fps :=src.fps;
+end;
+
+procedure ClearTrackInfo(var dst:tSongInfo;withFree:bool);
+begin
+ if withFree then
+ begin
+ mFreeMem(dst.artist);
+ mFreeMem(dst.title);
+ mFreeMem(dst.album);
+ mFreeMem(dst.genre);
+ mFreeMem(dst.comment);
+ mFreeMem(dst.year);
+ mFreeMem(dst.lyric);
+ mFreeMem(dst.cover);
+ end
+ else
+ begin
+ dst.artist :=nil;
+ dst.title :=nil;
+ dst.album :=nil;
+ dst.genre :=nil;
+ dst.comment:=nil;
+ dst.year :=nil;
+ dst.lyric :=nil;
+ dst.cover :=nil;
+ end;
+ dst.kbps :=0;
+ dst.khz :=0;
+ dst.channels:=0;
+ dst.track :=0;
+ dst.total :=0;
+ dst.vbr :=0;
+ dst.codec :=0;
+ dst.width :=0;
+ dst.height :=0;
+ dst.fps :=0;
+end;
+
+function CheckPlayers(var dst:tSongInfo;flags:cardinal):integer;
+var
+ PlayerChanged:bool;
+ fname:pWideChar;
+begin
+ result:=CheckAllPlayers(flags,dst.status,PlayerChanged);
+
+ if result<>WAT_RES_NOTFOUND then
+ begin
+ if PlayerChanged then
+ begin
+ ClearPlayerInfo(dst,false);
+ AnsiToWide(plyLink^[0].Desc,dst.player);
+ dst.plwnd:=result;
+ FastAnsiToWide(plyLink^[0].URL,dst.url);
+ if plyLink^[0].icon<>0 then
+ dst.icon:=CopyIcon(plyLink^[0].icon)
+ else if result<>0 then
+ begin
+ if GetEXEByWnd(dst.plwnd,fname)<>nil then
+ begin
+ dst.icon:=ExtractIconW(hInstance,fname,0);
+ if dst.icon=1 then
+ dst.icon:=0;
+ if dst.icon<>0 then
+ plyLink^[0].icon:=CopyIcon(dst.icon);
+ mFreeMem(fname);
+ end;
+ end;
+
+ if plyLink^[0].GetInfo<>nil then
+ tInfoProc(plyLink^[0].GetInfo)(dst,flags or WAT_OPT_PLAYERDATA)
+ else if (plyLink^[0].flags and WAT_OPT_WINAMPAPI)<>0 then
+ WinampGetInfo(wparam(@dst),flags or WAT_OPT_PLAYERDATA);
+
+ if (plyLink^[0].flags and WAT_OPT_PLAYERINFO)=0 then
+ if dst.txtver=NIL then dst.txtver:=DefGetVersionText(dst.plyver);
+
+ result:=WAT_RES_NEWPLAYER;
+ end
+ else
+ begin
+ dst.plwnd:=result; // to prevent same player, another instance
+ result:=WAT_RES_OK;
+ end
+ end;
+end;
+
+function CheckFile(var dst:tSongInfo;flags:cardinal;timeout:cardinal):integer;
+var
+ fname:pWideChar;
+ tmp:integer;
+ remote,FileChanged:boolean;
+ f:THANDLE;
+ ftime:int64;
+begin
+ if plyLink^[0].GetName<>nil then
+ fname:=tNameProc(plyLink^[0].GetName)(dst.plwnd,flags)
+ else
+ fname:=nil;
+
+ if (fname=nil) and (dst.plwnd<>0) then
+ begin
+ tmp:=0;
+ if (flags and WAT_OPT_MULTITHREAD)<>0 then tmp:=tmp or gffdMultiThread;
+ if (flags and WAT_OPT_KEEPOLD )<>0 then tmp:=tmp or gffdOld;
+ fname:=GetFileFromWnd(dst.plwnd,KnownFileType,tmp,timeout);
+ end;
+
+ if fname<>nil then
+ begin
+ remote:=StrPosW(fname,'://')<>nil;
+ // file changing time (local/lan only)
+ if not remote then
+ begin
+ f:=Reset(fname);
+
+ if f<>THANDLE(INVALID_HANDLE_VALUE) then
+ begin
+ GetFileTime(f,nil,nil,@ftime);
+ CloseHandle(f);
+ end;
+ end;
+ // same file
+ if (dst.mfile<>nil) and (lstrcmpiw(dst.mfile,fname)=0) then
+ begin
+ if (not remote) and ((flags and WAT_OPT_CHECKTIME)<>0) then
+ FileChanged:=dst.date<>ftime
+ else
+ FileChanged:=false;
+ end
+ else // new filename
+ begin
+ FileChanged:=true;
+ end;
+
+ // if not proper ext (we don't working with it)
+ //!!!! check for remotes
+ if (not remote) and (CheckExt(fname)=WAT_RES_NOTFOUND) then
+ begin
+ mFreeMem(fname);
+ result:=WAT_RES_NOTFOUND;
+ exit;
+ end;
+ if FileChanged {or isContainer(fname)} then
+ begin
+ ClearFileInfo(dst,false);
+ dst.mfile:=fname; //!! must be when format recognized or remote
+ dst.date:=ftime; //!!
+ dst.fsize:=GetFSize(dst.mfile);
+ result:=WAT_RES_NEWFILE;
+ end
+ else
+ begin
+ result:=WAT_RES_OK;
+ mFreeMem(fname);
+ end;
+ end
+ else
+ begin
+ result:=WAT_RES_NOTFOUND;
+ end;
+end;
+
+// Get Info - main procedure
+function GetChangingInfo(var dst:tSongInfo;flags:cardinal):integer;
+begin
+ result:=WAT_RES_OK;
+
+ ClearChangingInfo(dst,false);
+
+ if plyLink^[0].GetInfo<>nil then
+ tInfoProc(plyLink^[0].GetInfo)(dst,flags or WAT_OPT_CHANGES)
+ else if (plyLink^[0].flags and WAT_OPT_WINAMPAPI)<>0 then
+ WinampGetInfo(wparam(@dst),flags or WAT_OPT_CHANGES);
+
+ if (plyLink^[0].flags and WAT_OPT_PLAYERINFO)=0 then
+ if dst.wndtext=NIL then dst.wndtext:=DefGetWndText(dst.plwnd);
+end;
+
+function GetInfo(var dst:tSongInfo;flags:cardinal):integer;
+var
+ oldartist,oldtitle:pWideChar;
+ fname:pWideChar;
+ remote:boolean;
+ lmsnInfo:pMSNInfo;
+begin
+ result:=WAT_RES_OK;
+ remote:=StrPosW(dst.mfile,'://')<>nil;
+
+// if remote or ((plyLink^[0].flags and WAT_OPT_PLAYERINFO)<>0) then
+ oldartist:=dst.artist; oldtitle:=dst.title;
+
+ ClearTrackInfo(dst,false);
+
+ // info from player
+ if plyLink^[0].GetInfo<>nil then
+ tInfoProc(plyLink^[0].GetInfo)(dst,flags and not WAT_OPT_CHANGES)
+ else if (plyLink^[0].flags and WAT_OPT_WINAMPAPI)<>0 then
+ WinampGetInfo(wparam(@dst),flags and not WAT_OPT_CHANGES);
+ // info from file
+ GetFileFormatInfo(dst);
+
+ if (plyLink^[0].flags and WAT_OPT_PLAYERINFO)=0 then
+ with dst do
+ begin
+ if remote then
+ fname:=nil
+ else
+ fname:=mfile;
+
+ lmsnInfo:=GetMSNInfo;
+
+ if lmsnInfo<>nil then
+ begin
+ if artist=NIL then StrDupW(artist,lmsnInfo.msnArtist);
+ if title =NIL then StrDupW(title ,lmsnInfo.msnTitle);
+ if album =NIL then StrDupW(album ,lmsnInfo.msnAlbum);
+ end;
+
+ if artist=NIL then artist:=DefGetArtist(plwnd,fname,wndtext);
+ if title =NIL then title :=DefGetTitle (plwnd,fname,wndtext);
+ end;
+ if remote or ((plyLink^[0].flags and WAT_OPT_PLAYERINFO)<>0) or
+ isContainer(dst.mfile) then
+ begin
+ if (oldartist=oldtitle) or
+ ((oldartist<>nil) and (StrCmpW(dst.artist,oldartist)<>0)) or
+ ((oldtitle <>nil) and (StrCmpW(dst.title ,oldtitle )<>0)) then
+ begin
+ result:=WAT_RES_NEWFILE;
+ end;
+ end;
+end;
+
+procedure ClearPlayers;
+begin
+ if PlyNum>0 then
+ begin
+ repeat
+ dec(PlyNum);
+ with plyLink^[PlyNum] do
+ begin
+ if DeInit<>nil then
+ tDeInitProc(DeInit);
+ FreeMem(Desc);
+ if URL<>nil then
+ FreeMem(URL);
+ if icon<>0 then
+ DestroyIcon(icon);
+ if (flags and WAT_OPT_TEMPLATE)<>0 then
+ begin
+ ClearTemplate(pTmplCell(Check));
+ mFreeMem(Notes);
+ end
+ else if Notes<>nil then
+ FreeMem(Notes);
+ end;
+ until PlyNum=0;
+ FreeMem(plyLink);
+ end;
+end;
+
+end.
diff --git a/plugins/Watrack/stat/default.tmpl b/plugins/Watrack/stat/default.tmpl
new file mode 100644
index 0000000000..0e1920fc5d
--- /dev/null
+++ b/plugins/Watrack/stat/default.tmpl
@@ -0,0 +1,89 @@
+const
+ IntTmpl:PAnsiChar=
+'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">'#13#10+
+'<HTML>'#13#10+
+'<HEAD>'#13#10+
+'<TITLE>Report on %currenttime%</TITLE>'#13#10+
+'<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />'#13#10+
+'<style>'#13#10+
+'table {background:#E0E0E0;padding:4}'#13#10+
+'* {padding:2; margin:0;font-family: arial}'#13#10+
+'td, div {font-size:10pt;border:1 solid black}'#13#10+
+'h2 {padding: 26 0 10}'#13#10+
+'.label, .num, .time {background:#C0C0C0;padding-left:4}'#13#10+
+'.tdbar {width:50%;padding:0 4 0 0;border-width:0}'#13#10+
+'.bar {background:#9BA298;padding-left:4;margin:0 4 0 0}'#13#10+
+'.num {padding:0 10; width:20px}'#13#10+
+'.time {text-align:center;width:20%}'#13#10+
+'</style>'#13#10+
+'</HEAD>'#13#10+
+'<BODY>'#13#10+
+'<center><h1>Report</h1><br/><h3>created %currenttime%</h3>'#13#10+
+'%block_freqartist%'#13#10+
+'<h2>Most popular artists:</h2><table style="width:90%">'#13#10+
+'%start%'#13#10+
+'<tr><td class="num">%num%.</td>'#13#10+
+'<td class="label">%artist%</td>'#13#10+
+'<td class="tdbar"><div class="bar" style="width:%percent%%;">%count%</div></td>'#13#10+
+'</tr>'#13#10+
+'%end%'#13#10+
+'</table>'#13#10+
+'%block_end%'#13#10+
+'%block_freqsongs%'#13#10+
+'<h2>Most frequently played songs:</h2><table style="width:90%">'#13#10+
+'%start%'#13#10+
+'<tr><td class="num">%num%.</td>'#13#10+
+'<td class="label">%artist% - %title% (%album%)</td>'#13#10+
+'<td class="tdbar"><div class="bar" style="width:%percent%%;">%count%</div></td>'#13#10+
+'</tr>'#13#10+
+'%end%'#13#10+
+'</table>'#13#10+
+'%block_end%'#13#10+
+'%block_freqalbum%'#13#10+
+'<h2>Most frequently played albums:</h2><table style="width:90%">'#13#10+
+'%start%'#13#10+
+'<tr><td class="num">%num%.</td>'#13#10+
+'<td class="label">%album%</td>'#13#10+
+'<td class="tdbar"><div class="bar" style="width:%percent%%;">%count%</div></td>'#13#10+
+'</tr>'#13#10+
+'%end%'#13#10+
+'</table>'#13#10+
+'%block_end%'#13#10+
+'%block_lastsongs%'#13#10+
+'<h2>Last played songs:</h2><table style="width:90%">'#13#10+
+'%start%'#13#10+
+'<tr><td class="num">%num%.</td>'#13#10+
+'<td class="time">[%date%]</td>'#13#10+
+'<td class="label">%artist% - %title%</td>'#13#10+
+'</tr>'#13#10+
+'%end%'#13#10+
+'</table>'#13#10+
+'%block_end%'#13#10+
+'%block_songtime%'#13#10+
+'<h2>Longest songs:</h2><table style="width:90%">'#13#10+
+'%start%'#13#10+
+'<tr><td class="num">%num%.</td>'#13#10+
+'<td class="label">%artist% - %title%</td>'#13#10+
+'<td class="tdbar"><div class="bar" style="width:%percent%%;">%length%</div></td>'#13#10+
+'</tr>'#13#10+
+'%end%'#13#10+
+'</table>'#13#10+
+'%block_end%'#13#10+
+'%block_freqpath%'#13#10+
+'<h2>Most frequently used paths:</h2><table style="width:90%">'#13#10+
+'%start%'#13#10+
+'<tr><td class="num">%num%.</td>'#13#10+
+'<td class="label">%path%</td>'#13#10+
+'<td class="tdbar"><div class="bar" style="width:%percent%%;">%count%</div></td>'#13#10+
+'</tr>'#13#10+
+'%end%'#13#10+
+'</table>'#13#10+
+'%block_end%'#13#10+
+'<h2>Total played time is:</h2><table style="width:90%">'#13#10+
+'<tr><td class="label">Total logged music time - %totaltime%</td>'#13#10+
+'<td class="label">Total logged music files - %totalfiles%</td>'#13#10+
+'</tr></table>'#13#10+
+''#13#10+
+'</center>'#13#10+
+'</BODY>'#13#10+
+'</HTML>'#13#10;
diff --git a/plugins/Watrack/stat/report.inc b/plugins/Watrack/stat/report.inc
new file mode 100644
index 0000000000..0cd4fcfb22
--- /dev/null
+++ b/plugins/Watrack/stat/report.inc
@@ -0,0 +1,315 @@
+{$include default.tmpl}
+function ReadTemplate(fname:PAnsiChar;var buf:PAnsiChar):integer;
+var
+ f:THANDLE;
+ size:integer;
+begin
+ if (fname=nil) or (fname^=#0) then
+ f:=INVALID_HANDLE_VALUE
+ else
+ f:=Reset(fname);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ result:=0
+ else
+ begin
+ size:=FileSize(f);
+ mGetMem(buf,size+1);
+ buf[size+1]:=#0;
+ BlockRead(f,buf^,size);
+ CloseHandle(f);
+ result:=size;
+ end;
+end;
+
+function StatOut(report,log,template:PAnsiChar):boolean;
+
+const
+ bufsize = 16384;
+var
+ fout:THANDLE;
+ tt,tf:array [0..15] of AnsiChar;
+ timebuf:array [0..17] of AnsiChar; // for current date / time
+ outbuf:PAnsiChar;
+ outpos:PAnsiChar;
+
+ procedure OutChar(var pc:PAnsiChar);
+ begin
+ outpos^:=pc^;
+ inc(pc);
+ inc(outpos);
+ if (outpos-outbuf)=bufsize then
+ begin
+ BlockWrite(fout,outbuf^,bufsize);
+ outpos:=outbuf;
+ end;
+ end;
+
+ procedure OutStr(pc:PAnsiChar);
+ begin
+ while pc^<>#0 do
+ OutChar(pc);
+ end;
+
+ procedure OutputBlock(var start:PAnsiChar;var Root:pCells;asortmode:integer);
+ const
+ blocksize = 8192;
+ var
+ i,max,cnt,len:integer;
+ items:cardinal;
+ Cell:pStatCell;
+ ls,ls1:array [0..511] of AnsiChar;
+ block:array [0..blocksize-1] of AnsiChar;
+ begin
+ len:=StrIndex(start,'%end%');
+ if len=0 then
+ len:=StrLen(start)
+ else
+ dec(len);
+ if len>6143 then
+ err('Template block too large');
+
+ Resort(Root,asortmode);
+
+ case asortmode of
+ stArtist,stAlbum,stPath: begin
+
+ Cell:=Root^.Cells[0];
+ max:=Cell^.Count;
+ if asortmode=stPath then OnlyPath(ls,Cell^.MFile); // speed optimization
+
+ for i:=0 to Root^.Count-1 do
+ begin
+ with Root^.Cells[i]^ do
+ begin
+ AltCount:=0;
+ if asortmode=stArtist then cnt:=lstrcmpia(Cell^.Artist,Artist)
+ else if asortmode=stAlbum then cnt:=lstrcmpia(Cell^.Album,Album)
+ else cnt:=lstrcmpia(ls,OnlyPath(ls1,MFile));
+ if cnt=0 then
+ inc(max,Count)
+ else
+ begin
+ Cell^.AltCount:=max;
+ Cell:=Root^.Cells[i];
+ if asortmode=stPath then OnlyPath(ls,Cell^.MFile); // speed optimization
+ max:=Count;
+ end;
+ end;
+ end;
+ Cell^.AltCount:=max;
+
+ Resort(Root,stAltCount);
+ if (asortmode=stAlbum) and (Root^.Cells[0]^.Album^=#0) then
+ begin
+ if Root^.Count>1 then
+ max:=Root^.Cells[1]^.AltCount
+ else
+ max:=0;
+ end
+ else
+ max:=Root^.Cells[0]^.AltCount;
+ end;
+ stCount: begin
+ max:=Root^.Cells[0]^.Count;
+ end;
+ stLength: begin
+ max:=Root^.Cells[0]^.Length;
+ end;
+ else
+ max:=1;
+ end;
+
+ items:=1;
+ if ReportItems>0 then
+ for i:=0 to Root^.Count-1 do
+ begin
+ with Root^.Cells[i]^ do
+ begin
+ if (asortmode=stAlbum) and (Album^=#0) then continue;
+ case asortmode of
+ stArtist,
+ stAlbum,
+ stPath : cnt:=AltCount;
+ stCount : cnt:=Count;
+ stLength: cnt:=Length;
+ else
+ cnt:=1;
+ end;
+ if cnt=0 then break;
+ move(start^,block,len);
+ block[len]:=#0;
+ StrReplace(block,'%date%' ,ShowTime(ls,LastTime));
+ StrReplace(block,'%length%' ,IntToTime(ls,Length));
+ StrReplace(block,'%artist%' ,Artist);
+ StrReplace(block,'%title%' ,Title);
+ StrReplace(block,'%album%' ,Album);
+ StrReplace(block,'%file%' ,MFile);
+ StrReplace(block,'%path%' ,OnlyPath(ls,MFile));
+ StrReplace(block,'%num%' ,IntToStr(ls,items));
+ StrReplace(block,'%currenttime%',timebuf);
+ StrReplace(block,'%totaltime%' ,tt);
+ StrReplace(block,'%totalfiles%' ,tf);
+ StrReplace(block,'%percent%' ,IntToStr(ls,round(cnt*100/max)));
+ StrReplace(block,'%count%' ,IntToStr(ls,cnt));
+ OutStr(block);
+ end;
+ if items=ReportItems then break;
+ inc(items);
+ end;
+ inc(start,len+5);
+ end;
+
+var
+ TmplBuf:PAnsiChar;
+ ptr:PAnsiChar;
+ i,j,k:integer;
+ size:integer;
+ lsortmode:integer;
+ MyTime:TSYSTEMTIME;
+ Root:pCells;
+ b1,tmp:PAnsiChar;
+begin
+ result:=false;
+ GetLocalTime(MyTime);
+ ShowTime(timebuf,PackTime(MyTime));
+
+ Lock:=true;
+ Root:=BuildTree(log,b1);
+ if Root<>nil then
+ begin
+ Resort(Root,stArtist);
+ Lock:=false;
+ size:=ReadTemplate(template,TmplBuf);
+ if size=0 then
+ begin
+ StrDup(TmplBuf,IntTmpl);
+ size:=StrLen(IntTmpl);
+ end;
+ ptr:=TmplBuf;
+ fout:=Rewrite(report);
+ if fout=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ mGetMem(outbuf,bufsize);
+ outpos:=outbuf;
+
+ i:=0;
+ k:=0;
+ for j:=0 to Root^.Count-1 do
+ begin
+ inc(k);
+ with Root^.Cells[j]^ do
+ inc(i,Length*Count);
+ end;
+ IntToTime(tt,i); // total time
+ IntToStr(tf,k); // total files
+
+ lsortmode:=stDate;
+ while (ptr-TmplBuf)<size do
+ begin
+ while (ptr^<>'%') and (ptr^<>#0) do
+ OutChar(ptr);
+ if ptr^=#0 then break;
+ if StrCmp(ptr,'%block_',7)=0 then
+ begin
+ if ptr>@TmplBuf then
+ begin
+ if (ptr-1)^<' ' then
+ k:=-1;
+ end;
+ inc(ptr,7);
+ if StrCmp(ptr,'end%',4)=0 then
+ begin
+ i:=4;
+ end
+ else if StrCmp(ptr,'freqartist%',11)=0 then
+ begin
+ lsortmode:=stArtist;
+ i:=11;
+ end
+ else if StrCmp(ptr,'freqsongs%',10)=0 then
+ begin
+ lsortmode:=stCount;
+ i:=10;
+ end
+ else if StrCmp(ptr,'freqalbum%',10)=0 then
+ begin
+ lsortmode:=stAlbum;
+ i:=10;
+ end
+ else if StrCmp(ptr,'lastsongs%',10)=0 then
+ begin
+ lsortmode:=stDate;
+ i:=10;
+ end
+ else if StrCmp(ptr,'songtime%',9)=0 then
+ begin
+ lsortmode:=stLength;
+ i:=9;
+ end
+ else if StrCmp(ptr,'freqpath%',9)=0 then
+ begin
+ lsortmode:=stPath;
+ i:=9;
+ end
+ else
+ begin
+ OutChar(ptr);
+ continue;
+ end;
+ inc(ptr,i);
+ if k<0 then
+ begin
+ while (ptr^<' ') and (ptr^<>#0) do inc(ptr);
+ k:=0;
+ end;
+ if (ReportMask and lsortmode)=0 then
+ begin
+ tmp:=StrPos(ptr,'%block_end%');
+ if tmp<>nil then
+ ptr:=tmp+11
+ else
+ break;
+ end;
+ end
+ else if StrCmp(ptr,'%start%',7)=0 then
+ begin
+ if ptr>@TmplBuf then
+ begin
+ if (ptr-1)^<' ' then
+ k:=-1;
+ end;
+ inc(ptr,7);
+ if k<0 then
+ begin
+ while (ptr^<' ') and (ptr^<>#0) do inc(ptr);
+ k:=0;
+ end;
+ OutputBlock(ptr,Root,lsortmode);
+ end
+ else if StrCmp(ptr,'%currenttime%',13)=0 then
+ begin
+ inc(ptr,13);
+ OutStr(timebuf);
+ end
+ else if StrCmp(ptr,'%totalfiles%',12)=0 then
+ begin
+ inc(ptr,12);
+ OutStr(tf);
+ end
+ else if StrCmp(ptr,'%totaltime%',11)=0 then
+ begin
+ inc(ptr,11);
+ OutStr(tt);
+ end
+ else
+ OutChar(ptr);
+ end;
+ BlockWrite(fout,outbuf^,outpos-outbuf);
+ CloseHandle(fout);
+ mFreeMem(outbuf);
+ mFreeMem(TmplBuf);
+ ClearStatCells(Root);
+ result:=true;
+ end;
+ mFreeMem(b1);
+end;
diff --git a/plugins/Watrack/stat/stat.rc b/plugins/Watrack/stat/stat.rc
new file mode 100644
index 0000000000..9bedcabf3d
--- /dev/null
+++ b/plugins/Watrack/stat/stat.rc
@@ -0,0 +1,50 @@
+#include "stat_rc.inc"
+
+LANGUAGE 0,0
+
+STATS DIALOGEX 0, 0, 304, 226, 0
+STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ PUSHBUTTON "Delete", IDC_CLEAR , 242, 11, 56, 14
+ LTEXT "Statistic log file", -1 , 16, 17, 148, 12
+ EDITTEXT IDC_STATNAME , 6, 29, 214, 14
+ PUSHBUTTON "...", IDC_SNBUTTON , 222, 29, 16, 14
+ PUSHBUTTON "Sort", IDC_SORTFILE , 242, 29, 56, 14
+ RTEXT "Autosort period, days",-1 , 124, 45, 142, 12, SS_CENTERIMAGE
+ EDITTEXT IDC_AUTOSORT , 270, 45, 28, 12, ES_RIGHT | ES_NUMBER
+ LTEXT "Report file", -1 , 16, 47, 148, 12
+ EDITTEXT IDC_REPNAME , 6, 59, 214, 14
+ PUSHBUTTON "...", IDC_RNBUTTON , 222, 59, 16, 14
+ PUSHBUTTON "Report", IDC_REPORT , 242, 59, 56, 14
+ LTEXT "Template file", -1 , 16, 77, 148, 12
+ EDITTEXT IDC_TMPLNAME , 6, 89, 214, 14
+ PUSHBUTTON "...", IDC_TNBUTTON , 222, 89, 16, 14
+ PUSHBUTTON "Export default", IDC_EXPORTDEF, 242, 89, 56, 14
+
+ CONTROL "", -1, "STATIC", SS_ETCHEDHORZ, 4, 107, 296, 2
+
+ GROUPBOX "Show in report", -1, 6, 111, 144, 84
+ AUTOCHECKBOX "Freq. songs" , IDC_FREQART , 12, 121, 136, 12, BS_VCENTER
+ AUTOCHECKBOX "Freq. artists" , IDC_FREQSONG , 12, 133, 136, 12, BS_VCENTER
+ AUTOCHECKBOX "Freq. album" , IDC_FREQALBUM, 12, 145, 136, 12, BS_VCENTER
+ AUTOCHECKBOX "Freq. paths" , IDC_FREQPATH , 12, 157, 136, 12, BS_VCENTER
+ AUTOCHECKBOX "Last played songs", IDC_LASTSONG , 12, 169, 136, 12, BS_VCENTER
+ AUTOCHECKBOX "Song time" , IDC_SONGTIME , 12, 181, 136, 12, BS_VCENTER
+
+ GROUPBOX "Sort log file", -1, 154, 111, 144, 84
+ AUTORADIOBUTTON "by Title" , IDC_BYTITLE , 158, 121, 136, 12, NOT WS_TABSTOP
+ AUTORADIOBUTTON "by Date" , IDC_BYDATE , 158, 133, 136, 12, NOT WS_TABSTOP
+ AUTORADIOBUTTON "by Count" , IDC_BYCOUNT , 158, 145, 136, 12, NOT WS_TABSTOP
+ AUTORADIOBUTTON "by Path" , IDC_BYPATH , 158, 157, 136, 12, NOT WS_TABSTOP
+ AUTORADIOBUTTON "by Length" , IDC_BYLENGTH , 158, 169, 136, 12, NOT WS_TABSTOP
+ AUTOCHECKBOX "Reverse order", IDC_DIRECTION, 158, 181, 136, 12, BS_VCENTER | BS_MULTILINE
+
+ LTEXT "Report Items", -1, 38, 208, 112, 12, SS_CENTERIMAGE
+ EDITTEXT IDC_ITEMS, 6, 208, 28, 12, ES_RIGHT | ES_NUMBER
+ AUTOCHECKBOX "Open report" , IDC_RUNREPORT, 154, 196, 146, 12, BS_VCENTER
+ AUTOCHECKBOX "Add report file ext.", IDC_ADDEXT , 154, 208, 146, 12, BS_VCENTER | BS_MULTILINE
+}
+
+BTN_REPORT ICON "wat_report.ico"
diff --git a/plugins/Watrack/stat/stat.res b/plugins/Watrack/stat/stat.res
new file mode 100644
index 0000000000..eca192d5ba
--- /dev/null
+++ b/plugins/Watrack/stat/stat.res
Binary files differ
diff --git a/plugins/Watrack/stat/stat_data.inc b/plugins/Watrack/stat/stat_data.inc
new file mode 100644
index 0000000000..ec539a3ecd
--- /dev/null
+++ b/plugins/Watrack/stat/stat_data.inc
@@ -0,0 +1,16 @@
+{statistic data}
+const
+ MenuReportPos = 500050001;
+
+const
+ smDirect = 1;
+ smReverse = 2;
+
+const
+ stArtist = $0001;
+ stCount = $0002;
+ stPath = $0004;
+ stDate = $0008;
+ stLength = $0010;
+ stAltCount = $0020;
+ stAlbum = $0040;
diff --git a/plugins/Watrack/stat/stat_dlg.inc b/plugins/Watrack/stat/stat_dlg.inc
new file mode 100644
index 0000000000..64a9b97f7c
--- /dev/null
+++ b/plugins/Watrack/stat/stat_dlg.inc
@@ -0,0 +1,223 @@
+{Statistic Dialog}
+
+{$include stat_rc.inc}
+
+procedure SetReportMask(Dlg:hwnd);
+begin
+ ReportMask:=0;
+ if IsDlgButtonChecked(Dlg,IDC_FREQART)=BST_CHECKED then
+ ReportMask:=ReportMask or stArtist;
+ if IsDlgButtonChecked(Dlg,IDC_FREQSONG)=BST_CHECKED then
+ ReportMask:=ReportMask or stCount;
+ if IsDlgButtonChecked(Dlg,IDC_FREQPATH)=BST_CHECKED then
+ ReportMask:=ReportMask or stPath;
+ if IsDlgButtonChecked(Dlg,IDC_LASTSONG)=BST_CHECKED then
+ ReportMask:=ReportMask or stDate;
+ if IsDlgButtonChecked(Dlg,IDC_SONGTIME)=BST_CHECKED then
+ ReportMask:=ReportMask or stLength;
+ if IsDlgButtonChecked(Dlg,IDC_FREQALBUM)=BST_CHECKED then
+ ReportMask:=ReportMask or stAlbum;
+end;
+
+procedure EnableItems(Dlg:hwnd;enable:boolean);
+begin
+ EnableWindow(GetDlgItem(Dlg,IDC_STATNAME) ,enable);
+ EnableWindow(GetDlgItem(Dlg,IDC_SNBUTTON) ,enable);
+ EnableWindow(GetDlgItem(Dlg,IDC_SORTFILE) ,enable);
+ EnableWindow(GetDlgItem(Dlg,IDC_REPORT) ,enable);
+ EnableWindow(GetDlgItem(Dlg,IDC_CLEAR) ,enable);
+ EnableWindow(GetDlgItem(Dlg,IDC_BYTITLE) ,enable);
+ EnableWindow(GetDlgItem(Dlg,IDC_BYDATE) ,enable);
+ EnableWindow(GetDlgItem(Dlg,IDC_BYCOUNT) ,enable);
+ EnableWindow(GetDlgItem(Dlg,IDC_BYPATH) ,enable);
+ EnableWindow(GetDlgItem(Dlg,IDC_BYLENGTH) ,enable);
+ EnableWindow(GetDlgItem(Dlg,IDC_DIRECTION),enable);
+ EnableWindow(GetDlgItem(Dlg,IDC_REPNAME) ,enable);
+ EnableWindow(GetDlgItem(Dlg,IDC_RNBUTTON) ,enable);
+ EnableWindow(GetDlgItem(Dlg,IDC_ITEMS) ,enable);
+ EnableWindow(GetDlgItem(Dlg,IDC_FREQART) ,enable);
+ EnableWindow(GetDlgItem(Dlg,IDC_FREQSONG) ,enable);
+ EnableWindow(GetDlgItem(Dlg,IDC_FREQALBUM),enable);
+ EnableWindow(GetDlgItem(Dlg,IDC_FREQPATH) ,enable);
+ EnableWindow(GetDlgItem(Dlg,IDC_LASTSONG) ,enable);
+ EnableWindow(GetDlgItem(Dlg,IDC_SONGTIME) ,enable);
+ EnableWindow(GetDlgItem(Dlg,IDC_RUNREPORT),enable);
+ EnableWindow(GetDlgItem(Dlg,IDC_ADDEXT) ,enable);
+ EnableWindow(GetDlgItem(Dlg,IDC_AUTOSORT) ,enable);
+end;
+
+function DlgProcOptions(Dialog:HWnd; hMessage:dword;wParam:WPARAM;lParam:LPARAM):LRESULT; stdcall;
+const
+ changed:boolean=false;
+var
+ buf,buf1:array [0..511] of AnsiChar;
+ tmp:longbool;
+ p:PAnsiChar;
+ f:THANDLE;
+begin
+ result:=0;
+ case hMessage of
+ WM_INITDIALOG: begin
+ TranslateDialogDefault(Dialog);
+ SetDlgItemInt(Dialog,IDC_ITEMS,ReportItems,false);
+ if ReportName=nil then
+ p:=''
+ else
+ p:=ReportName;
+ SetDlgItemTextA(Dialog,IDC_REPNAME,p);
+ if StatName=nil then
+ p:=''
+ else
+ p:=StatName;
+ SetDlgItemTextA(Dialog,IDC_STATNAME,p);
+ if TmplName=nil then
+ p:=''
+ else
+ p:=TmplName;
+ SetDlgItemTextA(Dialog,IDC_TMPLNAME,p);
+ SetDlgItemInt(Dialog,IDC_AUTOSORT,AutoSort,false);
+ CheckDlgButton(Dialog,IDC_RUNREPORT,RunReport);
+ CheckDlgButton(Dialog,IDC_ADDEXT,DoAddExt);
+
+ CheckDlgButton(Dialog,IDC_BYTITLE ,ord(SortMode=stArtist));
+ CheckDlgButton(Dialog,IDC_BYDATE ,ord(SortMode=stDate));
+ CheckDlgButton(Dialog,IDC_BYCOUNT ,ord(SortMode=stCount));
+ CheckDlgButton(Dialog,IDC_BYPATH ,ord(SortMode=stPath));
+ CheckDlgButton(Dialog,IDC_BYLENGTH,ord(SortMode=stLength));
+
+ if Direction=smReverse then
+ CheckDlgButton(Dialog,IDC_DIRECTION,BST_CHECKED);
+
+ if (ReportMask and stArtist)<>0 then
+ CheckDlgButton(Dialog,IDC_FREQART,BST_CHECKED);
+ if (ReportMask and stAlbum)<>0 then
+ CheckDlgButton(Dialog,IDC_FREQALBUM,BST_CHECKED);
+ if (ReportMask and stCount)<>0 then
+ CheckDlgButton(Dialog,IDC_FREQSONG,BST_CHECKED);
+ if (ReportMask and stPath)<>0 then
+ CheckDlgButton(Dialog,IDC_FREQPATH,BST_CHECKED);
+ if (ReportMask and stDate)<>0 then
+ CheckDlgButton(Dialog,IDC_LASTSONG,BST_CHECKED);
+ if (ReportMask and stLength)<>0 then
+ CheckDlgButton(Dialog,IDC_SONGTIME,BST_CHECKED);
+ result:=0;
+ changed:=false;
+ end;
+
+ WM_COMMAND: begin
+ if (wParam shr 16)=BN_CLICKED then
+ begin
+ case loword(wParam) of
+ IDC_BYTITLE : SortMode:=stArtist;
+ IDC_BYDATE : SortMode:=stDate;
+ IDC_BYCOUNT : SortMode:=stCount;
+ IDC_BYPATH : SortMode:=stPath;
+ IDC_BYLENGTH: SortMode:=stLength;
+ IDC_RUNREPORT: RunReport :=IsDlgButtonChecked(Dialog,IDC_RUNREPORT);
+ IDC_ADDEXT: DoAddExt :=IsDlgButtonChecked(Dialog,IDC_ADDEXT);
+ IDC_DIRECTION: begin
+ if IsDlgButtonChecked(Dialog,IDC_DIRECTION)=BST_CHECKED then
+ Direction:=smReverse
+ else
+ Direction:=smDirect;
+ end;
+
+ IDC_CLEAR: begin
+ DeleteFileA(StatName);
+ exit;
+ end;
+ IDC_SNBUTTON: begin
+ if ShowDlg(buf,StatName) then
+ SetDlgItemTextA(Dialog,IDC_STATNAME,buf);
+ end;
+ IDC_TNBUTTON: begin
+ if ShowDlg(buf,TmplName) then
+ SetDlgItemTextA(Dialog,IDC_TMPLNAME,buf);
+ end;
+ IDC_RNBUTTON: begin
+ if ShowDlg(buf,ReportName) then
+ SetDlgItemTextA(Dialog,IDC_REPNAME,buf);
+ end;
+ IDC_SORTFILE: begin
+ GetDlgItemTextA(Dialog,IDC_STATNAME,buf,511);
+ if buf[0]<>#0 then
+ SortFile(buf,SortMode,Direction);
+ exit;
+ end;
+ IDC_EXPORTDEF: begin
+ if ShowDlg(buf,TmplName) then
+ begin
+ f:=Rewrite(buf);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ err('Can''t create file')
+ else
+ begin
+ BlockWrite(f,IntTmpl^,StrLen(IntTmpl));
+ CloseHandle(f);
+ end;
+ end;
+ exit;
+ end;
+ IDC_REPORT: begin
+ ReportItems:=GetDlgItemInt(Dialog,IDC_ITEMS,tmp,false);
+ if ReportItems=0 then
+ ReportItems:=1;
+ GetDlgItemTextA(Dialog,IDC_REPNAME,buf1,511);
+ GetDlgItemTextA(Dialog,IDC_TMPLNAME,buf,511);
+ SetReportMask(Dialog);
+ CallService(MS_WAT_MAKEREPORT,TWPARAM(@buf),TLPARAM(@buf1));
+ end;
+ end;
+ end;
+ if ((wParam shr 16)=EN_CHANGE) or ((wParam shr 16)=BN_CLICKED) then
+ begin
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ changed:=true;
+ end;
+ result:=1;
+ end;
+
+ WM_NOTIFY: begin
+ if (integer(PNMHdr(lParam)^.code)=PSN_APPLY) and changed then
+ begin
+ GetDlgItemTextA(Dialog,IDC_STATNAME,buf,511);
+ mFreeMem(StatName);
+ if buf[0]<>#0 then
+ begin
+ buf1[0]:=#0;
+ CallService(MS_UTILS_PATHTORELATIVE,TWPARAM(@buf),TLPARAM(@buf1));
+ StrDup(StatName,buf1);
+ end;
+
+ GetDlgItemTextA(Dialog,IDC_REPNAME,buf,511);
+ mFreeMem(ReportName);
+ if buf[0]<>#0 then
+ begin
+ buf1[0]:=#0;
+ CallService(MS_UTILS_PATHTORELATIVE,TWPARAM(@buf),TLPARAM(@buf1));
+ StrDup(ReportName,buf1);
+ end;
+
+ GetDlgItemTextA(Dialog,IDC_TMPLNAME,buf,511);
+ mFreeMem(TmplName);
+ if buf[0]<>#0 then
+ begin
+ buf1[0]:=#0;
+ CallService(MS_UTILS_PATHTORELATIVE,TWPARAM(@buf),TLPARAM(@buf1));
+ StrDup(TmplName,buf1);
+ end;
+
+ AutoSort:=GetDlgItemInt(Dialog,IDC_AUTOSORT,tmp,false);
+ ReportItems:=GetDlgItemInt(Dialog,IDC_ITEMS,tmp,false);
+ if ReportItems=0 then
+ ReportItems:=1;
+ SetReportMask(Dialog);
+ result:=1;
+ savestat;
+ changed:=false;
+ end;
+ end;
+ else
+ {result:=}DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+end;
diff --git a/plugins/Watrack/stat/stat_opt.inc b/plugins/Watrack/stat/stat_opt.inc
new file mode 100644
index 0000000000..8d8bed6bb5
--- /dev/null
+++ b/plugins/Watrack/stat/stat_opt.inc
@@ -0,0 +1,62 @@
+{statistic load/save options}
+const
+ opt_ModStatus :PAnsiChar = 'module/statistic';
+
+ opt_StatName :PAnsiChar = 'report/statname';
+ opt_RepName :PAnsiChar = 'report/repname';
+ opt_TmplName :PAnsiChar = 'report/tmplname';
+ opt_SortMode :PAnsiChar = 'report/sortmode';
+ opt_ReportMask:PAnsiChar = 'report/reportmask';
+ opt_ReportItem:PAnsiChar = 'report/reportitems';
+ opt_Direction :PAnsiChar = 'report/direction';
+ opt_RunReport :PAnsiChar = 'report/runreport';
+ opt_AddExt :PAnsiChar = 'report/addext';
+ opt_AutoSort :PAnsiChar = 'report/autosort';
+ opt_LastSort :PAnsiChar = 'report/lastsort';
+
+function GetModStatus:integer;
+begin
+ result:=DBReadByte(0,PluginShort,opt_ModStatus,1);
+end;
+
+procedure SetModStatus(stat:integer);
+begin
+ DBWriteByte(0,PluginShort,opt_ModStatus,stat);
+end;
+
+procedure loadstat;
+begin
+ ReportName :=DBReadString(0,PluginShort,opt_RepName ,nil);
+ StatName :=DBReadString(0,PluginShort,opt_StatName,nil);
+ TmplName :=DBReadString(0,PluginShort,opt_TmplName,nil);
+ DoAddExt :=DBReadByte (0,PluginShort,opt_AddExt ,BST_CHECKED);
+ RunReport :=DBReadByte (0,PluginShort,opt_RunReport ,BST_UNCHECKED);
+ Direction :=DBReadByte (0,PluginShort,opt_Direction ,smDirect);
+ SortMode :=DBReadByte (0,PluginShort,opt_SortMode ,stArtist);
+ ReportItems:=DBReadWord (0,PluginShort,opt_ReportItem,10);
+ ReportMask :=DBReadWord (0,PluginShort,opt_ReportMask,$FFFF);
+ AutoSort :=DBReadByte (0,PluginShort,opt_AutoSort ,1);
+ LastSort :=DBReadDWord (0,PluginShort,opt_LastSort ,0);
+end;
+
+procedure savestat;
+begin
+ DBWriteString(0,PluginShort,opt_RepName ,ReportName);
+ DBWriteString(0,PluginShort,opt_StatName,StatName);
+ DBWriteString(0,PluginShort,opt_TmplName,TmplName);
+ DBWriteByte (0,PluginShort,opt_AddExt ,DoAddExt);
+ DBWriteByte (0,PluginShort,opt_RunReport ,RunReport);
+ DBWriteByte (0,PluginShort,opt_Direction ,Direction);
+ DBWriteByte (0,PluginShort,opt_SortMode ,SortMode);
+ DBWriteWord (0,PluginShort,opt_ReportItem ,ReportItems);
+ DBWriteWord (0,PluginShort,opt_ReportMask ,ReportMask);
+ DBWriteByte (0,PluginShort,opt_AutoSort ,AutoSort);
+// DBWriteDWord (0,PluginShort,opt_LastSort ,LastSort);
+end;
+
+procedure FreeStat;
+begin
+ mFreeMem(ReportName);
+ mFreeMem(StatName);
+ mFreeMem(TmplName);
+end;
diff --git a/plugins/Watrack/stat/stat_rc.inc b/plugins/Watrack/stat/stat_rc.inc
new file mode 100644
index 0000000000..ff697e95f7
--- /dev/null
+++ b/plugins/Watrack/stat/stat_rc.inc
@@ -0,0 +1,29 @@
+const
+ IDC_STATNAME = 1026;
+ IDC_SNBUTTON = 1027;
+ IDC_SORTFILE = 1028;
+ IDC_REPORT = 1029;
+ IDC_BYTITLE = 1030;
+ IDC_BYDATE = 1031;
+ IDC_BYCOUNT = 1032;
+ IDC_BYPATH = 1033;
+ IDC_DIRECTION = 1034;
+ IDC_REPNAME = 1035;
+ IDC_RNBUTTON = 1036;
+ IDC_ITEMS = 1037;
+ IDC_CLEAR = 1038;
+ IDC_FREQART = 1040;
+ IDC_FREQSONG = 1041;
+ IDC_FREQPATH = 1042;
+ IDC_LASTSONG = 1043;
+ IDC_SONGTIME = 1044;
+ IDC_TMPLNAME = 1045;
+ IDC_TNBUTTON = 1046;
+ IDC_EXPORTDEF = 1047;
+ IDC_RUNREPORT = 1048;
+ IDC_ADDEXT = 1049;
+ IDC_FREQALBUM = 1050;
+ IDC_AUTOSORT = 1051;
+ IDC_BYLENGTH = 1052;
+
+ BTN_REPORT = 12;
diff --git a/plugins/Watrack/stat/stat_vars.inc b/plugins/Watrack/stat/stat_vars.inc
new file mode 100644
index 0000000000..ccc7c0c5b2
--- /dev/null
+++ b/plugins/Watrack/stat/stat_vars.inc
@@ -0,0 +1,21 @@
+{statistic variables}
+var
+ SortMode:dword;
+ ReportMask:dword;
+ ReportItems:cardinal;
+ Direction:cardinal;
+ RunReport:cardinal;
+ DoAddExt:cardinal;
+ AutoSort:cardinal;
+ LastSort:dword;
+const
+ StatName :PAnsiChar=nil;
+ ReportName:PAnsiChar=nil;
+ TmplName :PAnsiChar=nil;
+var
+ hPackLog,
+ hMakeReport,
+ hAddToLog,
+ plStatusHook,
+ sic,
+ hMenuReport:THANDLE;
diff --git a/plugins/Watrack/stat/statlog.pas b/plugins/Watrack/stat/statlog.pas
new file mode 100644
index 0000000000..50af34508d
--- /dev/null
+++ b/plugins/Watrack/stat/statlog.pas
@@ -0,0 +1,650 @@
+{Statistic}
+unit StatLog;
+{$include compilers.inc}
+interface
+{$Resource stat.res}
+implementation
+
+uses windows,messages,shellapi,commctrl
+ ,wrapper,io,wat_api,common,global,m_api,dbsettings,mirutils;
+
+{$include stat_data.inc}
+{$include stat_vars.inc}
+{$include stat_opt.inc}
+
+type
+ pStatCell = ^tStatCell;
+ tStatCell = record
+ Count :integer;
+ AltCount :integer;
+ LastTime :dword;
+ Length :integer;
+ Artist :PAnsiChar;
+ Title :PAnsiChar;
+ MFile :PAnsiChar;
+ Album :PAnsiChar;
+ next :pStatCell; // only for fill
+ end;
+
+type
+ pCells = ^tCells;
+ tCells = record
+ Count:integer;
+ Cells:array [0..1] of pStatCell
+ end;
+
+const
+ IcoBtnReport:PAnsiChar='WATrack_Report';
+const
+ DelimChar = '|';
+const
+ buflen = 2048;
+
+const
+ Lock:boolean=false;
+
+procedure err(str:PWideChar);
+begin
+ MessageBoxW(0,TranslateW(str),TranslateW('Music Statistic'),MB_OK);
+end;
+
+function OnlyPath(dst,src:PAnsiChar):PAnsiChar;
+var
+ i:integer;
+begin
+ i:=StrLen(src)-1;
+ while (i>0) and (src[i]<>'\') do dec(i);
+ StrCopy(dst,src,i);
+ result:=dst;
+end;
+
+function PackTime(aTime:TSYSTEMTIME):dword;
+begin
+ with aTime do
+ result:=wSecond+
+ (wMinute shl 06)+
+ (wHour shl 12)+
+ (wDay shl 17)+
+ (wMonth shl 22)+
+ (((wYear-2000) and $3F) shl 26);
+end;
+
+procedure UnPackTime(aTime:dword;var MyTime:TSYSTEMTIME);
+begin
+ with MyTime do
+ begin
+ wYear :=(aTime shr 26)+2000;
+ wMonth :=(aTime shr 22) and $0F;
+ wDay :=(aTime shr 17) and $1F;
+ wHour :=(aTime shr 12) and $1F;
+ wMinute:=(aTime shr 6 ) and $3F;
+ wSecond:=aTime and $3F;
+ end;
+end;
+
+function ShowTime(buf:PAnsiChar;aTime:dword):PAnsiChar;
+var
+ MyTime:TSYSTEMTIME;
+begin
+ UnPackTime(aTime,MyTime);
+ with MyTime do
+ begin
+ IntToStr(buf ,wDay ,2);
+ IntToStr(buf+3 ,wMonth ,2);
+ IntToStr(buf+6 ,wYear ,2);
+ IntToStr(buf+9 ,wHour ,2);
+ IntToStr(buf+12,wMinute,2);
+ IntToStr(buf+15,wSecond,2);
+ end;
+ buf[2] :='.'; buf[5] :='.'; buf[8] :=' ';
+ buf[11]:=':'; buf[14]:=':'; buf[17]:=#0;
+ result:=buf;
+end;
+
+function AppendStr(src:PAnsiChar;var dst:PAnsiChar):PAnsiChar; overload;
+begin
+ dst^:=DelimChar; inc(dst);
+ while src^<>#0 do
+ begin
+ dst^:=src^;
+ inc(dst);
+ inc(src);
+ end;
+ result:=dst;
+end;
+
+function AppendStr(src:PWideChar;var dst:PAnsiChar):PAnsiChar; overload;
+var
+ p,lp:PAnsiChar;
+begin
+ dst^:=DelimChar; inc(dst);
+ lp:=WideToUTF8(src,p);
+ while lp^<>#0 do
+ begin
+ dst^:=lp^;
+ inc(dst);
+ inc(lp);
+ end;
+ mFreeMem(p);
+ result:=dst;
+end;
+
+procedure AppendStat(fname:PAnsiChar;si:pSongInfo);
+var
+ f:THANDLE;
+ MyTime:TSYSTEMTIME;
+ buf:array [0..buflen-1] of char;
+ lp:PAnsiChar;
+begin
+ if Lock then
+ exit;
+ if (si^.artist=NIL) and (si^.title=NIL) and
+ (si^.album =NIL) and (si^.mfile=NIL) then
+ exit;
+ f:=Append(fname);
+// if dword(f)=INVALID_HANDLE_VALUE then f:=Rewrite(fname);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then exit;
+ FillChar(buf,SizeOf(buf),0);
+ lp:=@buf;
+ buf[0]:='1'; buf[1]:=DelimChar; inc(lp,2); // Count
+
+ GetLocalTime(MyTime);
+ IntToStr(lp,PackTime(MyTime),9);
+ inc(lp,9);
+ lp^:=DelimChar;
+ inc(lp);
+ IntToStr(lp,si^.total); while lp^<>#0 do inc(lp);
+
+ AppendStr(si^.artist,lp);
+ AppendStr(si^.title ,lp);
+ AppendStr(si^.mfile ,lp);
+ AppendStr(si^.album ,lp);
+
+ lp^:=#$0D; inc(lp); lp^:=#$0A;
+ BlockWrite(f,buf,lp-PAnsiChar(@buf)+1);
+ CloseHandle(f);
+end;
+
+procedure OutputStat(fname:PAnsiChar;aCells:pCells);
+var
+ f:THANDLE;
+ buf:array [0..2047] of char;
+ lp:PAnsiChar;
+ i:integer;
+begin
+ f:=Rewrite(fname);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ for i:=0 to aCells^.Count-1 do
+ begin
+ lp:=@buf;
+ with aCells^.Cells[i]^ do
+ begin
+ IntToStr(buf,Count); while lp^<>#0 do inc(lp);
+ lp^:=DelimChar; inc(lp);
+ IntToStr(lp,LastTime,9); inc(lp,9);
+ lp^:=DelimChar; inc(lp);
+ IntToStr(lp,Length); while lp^<>#0 do inc(lp);
+ AppendStr(Artist,lp);
+ AppendStr(Title ,lp);
+ AppendStr(MFile ,lp);
+ AppendStr(Album ,lp);
+
+ lp^:=#$0D; inc(lp); lp^:=#$0A;
+ BlockWrite(f,buf,lp-PAnsiChar(@buf)+1);
+ end;
+ end;
+ CloseHandle(f);
+end;
+
+function CutStr(var src:PAnsiChar):PAnsiChar;
+begin
+ result:=src;
+ while (src^<>DelimChar) and (src^>=' ') do inc(src);
+ src^:=#0;
+ inc(src);
+end;
+
+procedure ClearStatCells(aCells:pCells);
+begin
+ with aCells^ do
+ while Count>0 do
+ begin
+ dec(Count);
+ mFreeMem(Cells[Count]);
+ end;
+ mFreeMem(aCells);
+end;
+
+function FillCell(src:PAnsiChar):pStatCell;
+var
+ Cell:pStatCell;
+begin
+ mGetMem(Cell,SizeOf(tStatCell));
+ FillChar(Cell^,SizeOf(tStatCell),0);
+ Cell^.Count :=StrToInt(src);
+ while src^<>DelimChar do inc(src); inc(src);
+ Cell^.LastTime:=StrToInt(src);
+ while src^<>DelimChar do inc(src); inc(src);
+ Cell^.Length :=StrToInt(src);
+ while src^<>DelimChar do inc(src); inc(src);
+ Cell^.Artist:=CutStr(src);
+ Cell^.Title :=CutStr(src);
+ Cell^.MFile :=CutStr(src);
+ Cell^.Album :=CutStr(src);
+
+ result:=Cell;
+end;
+
+function Compare(C1,C2:pStatCell; SortType:integer):integer;
+var
+ ls,ls1:array [0..511] of AnsiChar;
+begin
+ case SortType of
+ stArtist: begin
+ result:=lstrcmpia(C1^.Artist,C2^.Artist);
+ if result=0 then
+ result:=lstrcmpia(C1^.Title,C2^.Title);
+ if result=0 then
+ result:=lstrcmpia(C1^.Album,C2^.Album);
+ end;
+ stAlbum: result:=lstrcmpia(C1^.Album,C2^.Album);
+ stPath : result:=lstrcmpia(OnlyPath(ls,C1^.MFile),OnlyPath(ls1,C2^.MFile));
+ stDate : result:=C2^.LastTime-C1^.LastTime;
+ stCount : result:=C2^.Count-C1^.Count;
+ stLength : result:=C2^.Length-C1^.Length;
+ stAltCount: result:=C2^.AltCount-C1.AltCount;
+ else
+ result:=0;
+ end;
+end;
+
+function SwapProc(var Root:pCells;First,Second:integer):integer;
+var
+ p:pStatCell;
+begin
+ p:=Root^.Cells[First];
+ Root^.Cells[First]:=Root^.Cells[Second];
+ Root^.Cells[Second]:=p;
+ result:=0;
+end;
+
+procedure Resort(var Root:pCells;sort:integer;adirection:integer=smDirect);
+
+ function CompareProc(First,Second:integer):integer;
+ begin
+ result:=Compare(Root^.cells[First],Root^.cells[Second],sort);
+ if direction=smReverse then
+ result:=-result;
+ end;
+
+var
+ i,j,gap:longint;
+begin
+ gap:=Root^.Count shr 1;
+ while gap>0 do
+ begin
+ for i:=gap to Root^.Count-1 do
+ begin
+ j:=i-gap;
+ while (j>=0) and (CompareProc(j,UInt(j+gap))>0) do
+ begin
+ SwapProc(Root,j,UInt(j+gap));
+ dec(j,gap);
+ end;
+ end;
+ gap:=gap shr 1;
+ end;
+// now pack doubles
+end;
+
+function BuildTree(fname:PAnsiChar;var buffer:PAnsiChar):pCells;
+var
+ f:THANDLE;
+ i,cnt:integer;
+ FirstCell,CurCell,Cell:pStatCell;
+ lRec:TWin32FindDataA;//WIN32_FIND_DATAA;
+ h:THANDLE;
+ p,p1,p2:PAnsiChar;
+ ls,buf:PAnsiChar;
+ arr:pCells;
+begin
+ result:=nil;
+ buffer:=nil;
+ h:=FindFirstFileA(fname,lRec);
+ if h=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ i:=lRec.nFileSizeLow;
+ FindClose(h);
+ if i<22 then
+ Exit;
+ f:=Reset(fname);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ mGetMem(buffer,i+1);
+ p:=buffer;
+ BlockRead(f,p^,i);
+ CloseHandle(f);
+ p1:=p;
+ p2:=p+i;
+ FirstCell:=nil;
+ mGetMem(buf,buflen);
+ buf^:=#0;
+ cnt:=0;
+ while p<p2 do
+ begin
+ while p^<>#$0D do inc(p);
+ i:=p-p1;
+ p^:=#0;
+ if i>=20 then //min log template + min fname [d:\.e]
+ begin
+ ls:=p1;
+// skip duplicates one-by-one
+ while ls^<>DelimChar do inc(ls); inc(ls); // Count
+ while ls^<>DelimChar do inc(ls); inc(ls); // time
+ while ls^<>DelimChar do inc(ls); inc(ls); // length
+ if StrCmp(buf,ls)<>0 then
+ begin
+ inc(cnt);
+ StrCopy(buf,ls);
+ Cell:=FillCell(p1);
+
+ if FirstCell=nil then
+ begin
+ FirstCell:=Cell;
+ CurCell :=FirstCell;
+ end
+ else
+ begin
+ CurCell^.next:=Cell;
+ CurCell:=Cell;
+ end;
+ end;
+ end;
+ inc(p,2); p1:=p;
+ end;
+ mFreeMem(buf);
+ // Fill array
+ if cnt>0 then
+ begin
+ mGetMem(arr,SizeOf(integer)+cnt*SizeOf(pStatCell));
+ arr^.Count:=cnt;
+ CurCell:=FirstCell;
+ i:=0;
+ while CurCell<>nil do
+ begin
+ arr^.Cells[i]:=CurCell;
+ CurCell:=CurCell.next;
+ inc(i);
+ end;
+ result:=arr;
+ // sort & pack
+ Resort(arr,stArtist);
+
+ i:=1;
+ Cell:=arr^.Cells[0];
+ while i<arr^.Count do
+ begin
+ with arr^.Cells[i]^ do
+ if (lstrcmpia(Cell^.Artist,Artist)=0) and
+ (lstrcmpia(Cell^.Title,Title)=0) and
+ (lstrcmpia(Cell^.Album,Album)=0) then
+ begin
+ if Cell^.LastTime<LastTime then
+ Cell^.LastTime:=LastTime;
+ inc(Cell^.Count,Count);
+ dec(arr^.Count);
+ if i<arr^.Count then
+ move(arr^.Cells[i+1],arr^.Cells[i],SizeOf(pStatCell)*(arr^.Count-i));
+ continue;
+ end
+ else
+ Cell:=arr^.Cells[i];
+ inc(i);
+ end;
+
+ end;
+end;
+
+procedure SortFile(fname:PAnsiChar;mode:integer;adirection:integer);
+var
+ Root:pCells;
+ buf:PAnsiChar;
+ buf1:array [0..511] of AnsiChar;
+begin
+ Lock:=true;
+ ConvertFileName(fname,buf1);
+// CallService(MS_UTILS_PATHTOABSOLUTE,dword(fname),dword(@buf1));
+ Root:=BuildTree(buf1,buf);
+ if Root<>nil then
+ begin
+ if (mode<>stArtist) or (adirection<>smDirect) then
+ Resort(Root,mode,adirection);
+ OutputStat(buf1,Root);
+ ClearStatCells(Root);
+ end;
+ mFreeMem(buf);
+ Lock:=false;
+end;
+
+{$include report.inc}
+
+// --------------- service functions -----------------
+
+function ThAddToLog(param:pdword):dword; stdcall;
+begin
+ result:=0;
+end;
+
+procedure ThPackLog(param:pdword); cdecl;
+begin
+ SortFile(StatName,SortMode,Direction);
+end;
+
+function ThMakeReport(param:pdword):dword; stdcall;
+begin
+ result:=0;
+end;
+
+function AddToLog(wParam:WPARAM;lParam:LPARAM):integer;cdecl;
+var
+ fname:PAnsiChar;
+ log:array [0..511] of AnsiChar;
+begin
+ result:=0;
+ if (StatName=nil) or (StatName[0]=#0) then
+ exit;
+ if wParam=0 then
+ fname:=StatName
+ else
+ fname:=PAnsiChar(wParam);
+ ConvertFileName(fname,log);
+// CallService(MS_UTILS_PATHTOABSOLUTE,dword(fname),dword(@log));
+ AppendStat(log,pSongInfo(lParam));
+end;
+
+function PackLog(wParam:WPARAM;lParam:LPARAM):integer;cdecl;
+begin
+ result:=0;
+ CloseHandle(mir_forkthread(@ThPackLog,nil));
+end;
+
+function MakeReport(wParam:WPARAM;lParam:LPARAM):integer;cdecl;
+var
+ report,log,template:array [0..511] of AnsiChar;
+ l,r:PAnsiChar;
+begin
+ result:=0;
+ if CallService(MS_WAT_PLUGINSTATUS,2,0)=WAT_RES_DISABLED then
+ exit;
+ if (wParam<>0) and (wParam<>MenuReportPos) then
+ l:=PAnsiChar(wParam)
+ else
+ l:=TmplName;
+ if PAnsiChar(lParam)<>nil then r:=PAnsiChar(lParam) else r:=ReportName;
+ if (r=nil) or (r^=#0) then
+ err('Report file name not defined')
+ else if (StatName=nil) or (StatName^=#0) then
+ err('Log file name not defined')
+ else
+ begin
+ ConvertFileName(r,report);
+ ConvertFileName(l,template);
+ ConvertFileName(StatName,log);
+// CallService(MS_UTILS_PATHTOABSOLUTE,dword(r),dword(@report));
+// CallService(MS_UTILS_PATHTOABSOLUTE,dword(l),dword(@template));
+// CallService(MS_UTILS_PATHTOABSOLUTE,dword(StatName),dword(@log));
+ if DoAddExt=BST_CHECKED then
+ ChangeExt(report,'htm');
+ if StatOut(report,log,template) then
+ begin
+ if RunReport=BST_CHECKED then
+ begin
+ ShellExecuteA(0,nil{'open'},report,nil,nil,SW_SHOWNORMAL);
+ end;
+ result:=1;
+ end
+ else
+ err('Oops, something wrong!');
+ end;
+end;
+
+{$include stat_dlg.inc}
+
+function NewPlStatus(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ flag:integer;
+ mi:tClistMenuItem;
+ CurTime:dword;
+begin
+ result:=0;
+ case wParam of
+ WAT_EVENT_NEWTRACK: begin
+ if (StatName<>nil) and (StatName[0]<>#0) then
+ begin
+ AppendStat(StatName,pSongInfo(lParam));
+ if AutoSort>0 then
+ begin
+ CurTime:=GetCurrentTime;
+ if (CurTime-LastSort)>=(86400*AutoSort) then
+ begin
+ SortFile(StatName,SortMode,Direction); //PackLog(0,0);
+ LastSort:=CurTime;
+ DBWriteDWord(0,PluginShort,opt_LastSort,LastSort);
+ end;
+ end;
+ end;
+ end;
+ WAT_EVENT_PLUGINSTATUS: begin
+ case lParam of
+ 0: flag:=0;
+ 2: flag:=CMIF_GRAYED;
+ else // like 1
+ exit
+ end;
+ FillChar(mi,sizeof(mi),0);
+ mi.cbSize:=sizeof(mi);
+ mi.flags :=CMIM_FLAGS+flag;
+ CallService(MS_CLIST_MODIFYMENUITEM,hMenuReport,tlparam(@mi));
+ end;
+ end;
+end;
+
+function IconChanged(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ mi:TCListMenuItem;
+begin
+ result:=0;
+ FillChar(mi,SizeOf(mi),0);
+ mi.cbSize:=sizeof(mi);
+ mi.flags :=CMIM_ICON;
+ mi.hIcon :=CallService(MS_SKIN2_GETICON,0,tlparam(IcoBtnReport));
+ CallService(MS_CLIST_MODIFYMENUITEM,hMenuReport,tlparam(@mi));
+end;
+
+// ------------ base interface functions -------------
+
+function InitProc(aGetStatus:boolean=false):integer;
+var
+ mi:TCListMenuItem;
+ sid:TSKINICONDESC;
+begin
+ if aGetStatus then
+ begin
+ if GetModStatus=0 then
+ begin
+ result:=0;
+ exit;
+ end;
+ end
+ else
+ SetModStatus(1);
+ result:=1;
+
+ hPackLog :=CreateServiceFunction(MS_WAT_PACKLOG ,@PackLog);
+ hMakeReport:=CreateServiceFunction(MS_WAT_MAKEREPORT,@MakeReport);
+ hAddToLog :=CreateServiceFunction(MS_WAT_ADDTOLOG ,@AddToLog);
+ loadstat;
+
+ FillChar(sid,SizeOf(TSKINICONDESC),0);
+ sid.cbSize:=SizeOf(TSKINICONDESC);
+ sid.cx:=16;
+ sid.cy:=16;
+ sid.szSection.a:='WATrack';
+ sid.hDefaultIcon :=LoadImage(hInstance,MAKEINTRESOURCE(BTN_REPORT),IMAGE_ICON,16,16,0);
+ sid.pszName :=IcoBtnReport;
+ sid.szDescription.a:='Create Report';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+ sic:=HookEvent(ME_SKIN2_ICONSCHANGED,@IconChanged);
+
+ FillChar(mi, sizeof(mi), 0);
+ mi.cbSize :=sizeof(mi);
+ mi.flags :=0;
+ mi.szPopupName.a:=PluginShort;
+ mi.hIcon :=CallService(MS_SKIN2_GETICON,0,tlparam(IcoBtnReport));
+ mi.szName.a :='Create WATrack report';
+ mi.pszService :=MS_WAT_MAKEREPORT;
+ mi.popupPosition:=MenuReportPos;
+ hMenuReport :=Menu_AddMainMenuItem(@mi);
+ plStatusHook:=HookEvent(ME_WAT_NEWSTATUS,@NewPlStatus);
+end;
+
+procedure DeInitProc(aSetDisable:boolean);
+begin
+ if aSetDisable then
+ SetModStatus(0);
+
+ CallService(MS_CLIST_REMOVEMAINMENUITEM,hMenuReport,0);
+ UnhookEvent(plStatusHook);
+ UnhookEvent(sic);
+ DestroyServiceFunction(hPackLog);
+ DestroyServiceFunction(hMakeReport);
+ DestroyServiceFunction(hAddToLog);
+ FreeStat;
+end;
+
+function AddOptionsPage(var tmpl:pAnsiChar;var proc:pointer;var name:PAnsiChar):integer;
+begin
+ tmpl:='STATS';
+ proc:=@DlgProcOptions;
+ name:='Statistics';
+ result:=0;
+end;
+
+var
+ Stat:twModule;
+
+procedure Init;
+begin
+ Stat.Next :=ModuleLink;
+ Stat.Init :=@InitProc;
+ Stat.DeInit :=@DeInitProc;
+ Stat.AddOption :=@AddOptionsPage;
+ Stat.ModuleName:='Statistic';
+ ModuleLink :=@Stat;
+end;
+
+begin
+ Init;
+end.
diff --git a/plugins/Watrack/stat/wat_report.ico b/plugins/Watrack/stat/wat_report.ico
new file mode 100644
index 0000000000..8e9c02fb93
--- /dev/null
+++ b/plugins/Watrack/stat/wat_report.ico
Binary files differ
diff --git a/plugins/Watrack/status/i_hotkey.inc b/plugins/Watrack/status/i_hotkey.inc
new file mode 100644
index 0000000000..3ad23ae656
--- /dev/null
+++ b/plugins/Watrack/status/i_hotkey.inc
@@ -0,0 +1,62 @@
+{main hotkey code}
+function InsertProc(wParam:WPARAM;lParam:LPARAM):int_ptr; cdecl;
+var
+ CurWin:HWND;
+ s:pWideChar;
+ p:PAnsiChar;
+ isUnicode:boolean;
+ i:integer;
+ j:integer;
+ tt:tTemplateType;
+begin
+ result:=0;
+ if DisablePlugin=dsPermanent then
+ exit;
+ if Loword(LastStatus)<>WAT_PLS_NORMAL then
+ exit;
+// i:=CallService(MS_WAT_GETMUSICINFO,0,0);
+ if UseMessages=BST_CHECKED then
+ begin
+ CurWin:=GetFocus;
+ if CurWin<>0 then
+ begin
+// j:=WndToContact(WaitFocusedWndChild(GetForegroundwindow){GetFocus});
+ j:=WndToContact(CurWin);
+ p:=GetContactProtoAcc(j);
+// p:=PAnsiChar(CallService(MS_PROTO_GETCONTACTBASEPROTO,j,0));
+ if DBReadByte(j,p,'ChatRoom',0)=1 then
+ begin
+ isUnicode:=false;
+ tt:=tmpl_chat;
+ end
+ else
+ begin
+ isUnicode:=true;
+ tt:=tmpl_pm;
+ end;
+ if SimpleMode<>BST_UNCHECKED then
+ i:=0
+ else
+ i:=FindProto(p);
+ s:=GetMacros(tt,i);
+ // not empty and not disabled
+ if (s<>nil) and (uint_ptr(s)<>uint_ptr(-1)) then
+ begin
+ if StrScanW(s,'{')<>nil then
+ SendRTF(CurWin,s,isUnicode,UserCP)
+ else
+ begin
+ if isUnicode then
+ SendMessageW(CurWin,EM_REPLACESEL,0,tlparam(s))
+ else
+ begin
+ SendMessageA(CurWin,EM_REPLACESEL,0,tlparam(WideToAnsi(s,p,UserCP)));
+ mFreeMem(p);
+ end;
+ end;
+ mFreeMem(s);
+ end;
+ result:=1;
+ end;
+ end;
+end;
diff --git a/plugins/Watrack/status/i_opt_11.inc b/plugins/Watrack/status/i_opt_11.inc
new file mode 100644
index 0000000000..55279d3156
--- /dev/null
+++ b/plugins/Watrack/status/i_opt_11.inc
@@ -0,0 +1,459 @@
+{Templates}
+const
+ DLGED_INIT = $1000; // dialog init, not activate Apply button
+ DLGED_PROT = $0800; // proto changed
+ DLGED_STAT = $0400; // status changed
+ DLGED_PACK = $0200; // needed string packing
+ DLGED_CHGD = $0100; // something changed
+ DLGED_MSG = $0001; // message template changed
+ DLGED_STTT = $0004; // status template changed
+ DLGED_CHNL = $0008; // channel template changed
+ DLGED_XTTL = $0010; // xstatus title changed
+ DLGED_XTTT = $0020; // xstatus template changed
+ DLGED_TUNE = $0040; // tunes template changed
+ DLGED_BASE = DLGED_MSG +DLGED_STTT+DLGED_CHNL+
+ DLGED_TUNE+DLGED_XTTL+DLGED_XTTT;
+
+const
+ maxShowControls = 7;
+ aListFields:array [0..maxShowControls-1] of integer= (
+ IDC_STATUS_TEXT,
+ IDC_XSTATUS_TITLE, IDC_XSTATUS_TEXT,
+ IDC_LISTENING_TEXT,
+ IDC_STAT_ENABLE,IDC_XSTAT_ENABLE,IDC_TUNES_ENABLE);
+
+ aShowFields: array [0..2,0..maxShowControls-1] of integer = (
+{status} (SW_SHOW, SW_HIDE, SW_HIDE, SW_HIDE, SW_SHOW, SW_HIDE, SW_HIDE),
+{xstatus} (SW_HIDE, SW_SHOW, SW_SHOW, SW_HIDE, SW_HIDE, SW_SHOW, SW_HIDE),
+{tunes} (SW_HIDE, SW_HIDE, SW_HIDE, SW_SHOW, SW_HIDE, SW_HIDE, SW_SHOW));
+
+var
+ CurProto,
+ CurStatus:integer;
+
+procedure RedrawFields(Dialog:hwnd;proto:integer=-1);
+var
+ wnd1,wnd:HWND;
+ p:pWideChar;
+ i:integer;
+begin
+ Changed:=Changed or DLGED_INIT;
+ if proto<0 then
+ proto:=CurProto;
+
+ SetDlgItemTextW(Dialog,IDC_EDIT_MSG,
+ GetTemplateStr(tmpl_pm,proto,CurStatus));
+
+ p:=GetTemplateStr(tmpl_stext,proto,CurStatus);
+
+ wnd:=GetDlgItem(Dialog,IDC_STATUS_TEXT);
+
+ SendMessageW(wnd,WM_SETTEXT,0,lparam(p));
+ if IsTemplateActive(tmpl_stext,proto,CurStatus) then
+ begin
+ EnableWindow(wnd,true);
+ i:=BST_CHECKED;
+ end
+ else
+ begin
+ EnableWindow(wnd,false);
+ i:=BST_UNCHECKED;
+ end;
+ CheckDlgButton(Dialog,IDC_STAT_ENABLE,i);
+
+ if IsXStatusSupported(CurProto) then
+ begin
+ wnd :=GetDlgItem(Dialog,IDC_XSTATUS_TITLE);
+ wnd1:=GetDlgItem(Dialog,IDC_XSTATUS_TEXT);
+ p:=GetTemplateStr(tmpl_xtitle,proto,CurStatus);
+ SendMessageW(wnd ,WM_SETTEXT,0,lparam(p));
+ SendMessageW(wnd1,WM_SETTEXT,0,
+ lparam(GetTemplateStr(tmpl_xtext,proto,CurStatus)));
+
+ if IsTemplateActive(tmpl_xtitle,proto,CurStatus) then
+ begin
+ EnableWindow(wnd ,true);
+ EnableWindow(wnd1,true);
+ i:=BST_CHECKED;
+ end
+ else
+ begin
+ EnableWindow(wnd ,false);
+ EnableWindow(wnd1,false);
+ i:=BST_UNCHECKED;
+ end;
+ CheckDlgButton(Dialog,IDC_XSTAT_ENABLE,i);
+ end;
+
+ if IsTunesSupported(CurProto) then
+ begin
+ p:=GetTemplateStr(tmpl_tunes,proto,CurStatus);
+ wnd:=GetDlgItem(Dialog,IDC_LISTENING_TEXT);
+ SendMessageW(wnd,WM_SETTEXT,0,lparam(p));
+ if IsTemplateActive(tmpl_tunes,proto,CurStatus) then
+ begin
+ EnableWindow(wnd,true);
+ i:=BST_CHECKED;
+ end
+ else
+ begin
+ EnableWindow(wnd,false);
+ i:=BST_UNCHECKED;
+ end;
+ CheckDlgButton(Dialog,IDC_TUNES_ENABLE,i);
+ end;
+
+ if IsChatSupported(CurProto) then
+ SetDlgItemTextW(Dialog,IDC_EDIT_CHANNEL,
+ GetTemplateStr(tmpl_chat,proto,CurStatus));
+
+ Changed:=Changed and not DLGED_INIT;
+end;
+
+procedure SetScreenFull(Dialog:hwnd);
+var
+ show:integer;
+ buf:array [0..127] of AnsiChar;
+ endis:boolean;
+ wnd:HWND;
+begin
+ Changed:=Changed or DLGED_INIT;
+ CurStatus:=0;
+
+ CheckDlgButton(Dialog,IDC_IRC_USER ,BST_CHECKED);
+ CheckDlgButton(Dialog,IDC_IRC_CHANNEL,BST_UNCHECKED);
+ ShowWindow(GetDlgItem(Dialog,IDC_EDIT_MSG ),SW_SHOW);
+ ShowWindow(GetDlgItem(Dialog,IDC_EDIT_CHANNEL),SW_HIDE);
+
+ if IsChatSupported(CurProto) then
+ show:=SW_SHOW
+ else
+ show:=SW_HIDE;
+
+ ShowWindow(GetDlgItem(Dialog,IDC_IRC_USER ),show);
+ ShowWindow(GetDlgItem(Dialog,IDC_IRC_CHANNEL),show);
+
+// wnd:=GetDlgItem(Dialog,IDC_PROTOLIST);
+
+//!!!! SendMessage(wnd,CB_SETCURSEL,0,0); //???
+
+// SendMessageA(wnd,LVM_GETITEMTEXTA,);
+// ListView_GetItemTextA(wnd,CurProto,0,@buf,SizeOf(buf));
+
+ StrCopy(buf,GetProtoName(CurProto));
+ StrCat(buf,PS_ICQ_GETCUSTOMSTATUSICON);
+ endis:=ServiceExists(buf)<>0;
+
+ EnableWindow(GetDlgItem(Dialog,IDC_CBEX ),endis);
+ EnableWindow(GetDlgItem(Dialog,IDC_XSTAT_AUDIO),endis);
+ EnableWindow(GetDlgItem(Dialog,IDC_XSTAT_VIDEO),endis);
+ if endis then
+ begin
+ CheckDlgButton(Dialog,IDC_XSTAT_AUDIO,BST_CHECKED);
+ CheckDlgButton(Dialog,IDC_XSTAT_VIDEO,BST_UNCHECKED);
+ end;
+
+ wnd:=GetDlgItem(Dialog,IDC_STATUSLIST);
+ FillStatusList(CurProto,wnd,true);
+ SendMessage(wnd,CB_SETCURSEL,0,0);
+ RedrawFields(Dialog);
+end;
+
+procedure SetTemplate(Dialog:hwnd;idc:integer;Tmpl:tTemplateType);
+begin
+ SetTemplateStr (GetDlgText(Dialog,ABS(idc)),Tmpl,CurProto,CurStatus);
+ SetTemplateActive(idc>0 ,Tmpl,CurProto,CurStatus);
+end;
+
+procedure SaveChanges(Dialog:hwnd);
+var
+ i,j:integer;
+begin
+ if (Changed and DLGED_BASE)<>0 then
+ begin
+ if (Changed and DLGED_MSG )<>0 then SetTemplate(Dialog,IDC_EDIT_MSG ,tmpl_pm);
+ if (Changed and DLGED_CHNL)<>0 then SetTemplate(Dialog,IDC_EDIT_CHANNEL,tmpl_chat);
+
+
+ if (Changed and DLGED_STTT)<>0 then
+ begin
+ if IsDlgButtonChecked(Dialog,IDC_STAT_ENABLE)<>BST_UNCHECKED then
+ i:=IDC_STATUS_TEXT
+ else
+ i:=-IDC_STATUS_TEXT;
+ SetTemplate(Dialog,i,tmpl_stext);
+ end;
+
+ if (Changed and DLGED_TUNE)<>0 then
+ begin
+ if IsDlgButtonChecked(Dialog,IDC_TUNES_ENABLE)<>BST_UNCHECKED then
+ i:=IDC_LISTENING_TEXT
+ else
+ i:=-IDC_LISTENING_TEXT;
+ SetTemplate(Dialog,i,tmpl_tunes);
+ end;
+
+ if (Changed and (DLGED_XTTL or DLGED_XTTT))<>0 then
+ begin
+ if IsDlgButtonChecked(Dialog,IDC_XSTAT_ENABLE)<>BST_UNCHECKED then
+ begin
+ i:=IDC_XSTATUS_TITLE;
+ j:=IDC_XSTATUS_TEXT;
+ end
+ else
+ begin
+ i:=-IDC_XSTATUS_TITLE;
+ j:=-IDC_XSTATUS_TEXT;
+ end;
+ if (Changed and DLGED_XTTL)<>0 then SetTemplate(Dialog,i,tmpl_xtitle);
+ if (Changed and DLGED_XTTT)<>0 then SetTemplate(Dialog,j,tmpl_xtext);
+ end;
+
+ Changed:=Changed and (not DLGED_BASE);
+ end;
+end;
+
+function SaveCBExValue(Dialog:HWnd;direct:boolean):cardinal;
+var
+ wnd:HWND;
+ i,j,shift:cardinal;
+begin
+ wnd:=GetDlgItem(Dialog,IDC_CBEX);
+ i:=GetProtoSetting(CurProto,true);
+
+ if (IsDlgButtonChecked(Dialog,IDC_XSTAT_VIDEO)<>BST_UNCHECKED) xor direct then
+ shift:=0
+ else
+ shift:=8;
+
+ j:=SendMessage(wnd,CB_GETCURSEL,0,0);
+ i:=(i and ($FFFF0000 or ($FF shl (8-shift)))) or (j shl shift);
+
+ SetProtoSetting(CurProto,i,true);
+
+ result:=(i shr (8-shift)) and $FF;
+end;
+
+procedure FillCBType(Dialog:hwnd;proto:pAnsiChar=nil);
+var
+ wnd:HWND;
+ j:integer;
+begin
+ wnd:=GetDlgItem(Dialog,IDC_CBSTATYPE);
+ SendMessage(wnd,CB_RESETCONTENT,0,0);
+
+ CB_AddStrDataW(wnd,TranslateW('Status'),0);
+ if IsXStatusSupported(uint_ptr(proto)) then CB_AddStrDataW(wnd,TranslateW('XStatus'),1);
+ if IsTunesSupported (uint_ptr(proto)) then CB_AddStrDataW(wnd,TranslateW('Tunes' ),2);
+
+ SendMessage(wnd,CB_SETCURSEL,0,0);
+
+ for j:=0 to maxShowControls-1 do
+ ShowWindow(GetDlgItem(Dialog,aListFields[j]),aShowFields[0][j]);
+end;
+
+function DlgProcOptions11(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+var
+ Item:LV_ITEMA;
+ buf:array [0..127] of AnsiChar;
+ i,j:integer;
+ wnd:HWND;
+ b:boolean;
+begin
+ result:=0;
+ case hMessage of
+ WM_DESTROY: begin
+ ListView_SetImageList(GetDlgItem(Dialog,IDC_STATUSLIST),0,LVSIL_SMALL);
+ ListView_SetImageList(GetDlgItem(Dialog,IDC_PROTOLIST ),0,LVSIL_SMALL);
+ end;
+
+ WM_INITDIALOG: begin
+ TranslateDialogDefault(Dialog);
+ Changed:=DLGED_INIT;
+ FillProtoList(GetDlgItem(Dialog,IDC_PROTOLIST),true);
+ CurProto :=0;
+ SetScreenFull(Dialog);
+ FillCBType(Dialog,nil);
+ SendMessage(Dialog,WM_COMMAND,(CBN_SELCHANGE shl 16)+IDC_CBSTATYPE,
+ GetDlgItem(Dialog,IDC_CBSTATYPE));
+ result:=0;
+ Changed:=0;
+ end;
+
+ WM_COMMAND: begin
+ case wParam shr 16 of
+ EN_CHANGE: begin
+ if (Changed and DLGED_INIT)=0 then
+ begin
+ Changed:=Changed or DLGED_CHGD or DLGED_PACK;
+ case loword(wParam) of
+ IDC_EDIT_MSG : Changed:=Changed or DLGED_MSG;
+ IDC_EDIT_CHANNEL : Changed:=Changed or DLGED_CHNL;
+ IDC_STATUS_TEXT : Changed:=Changed or DLGED_STTT;
+ IDC_XSTATUS_TITLE : Changed:=Changed or DLGED_XTTL;
+ IDC_XSTATUS_TEXT : Changed:=Changed or DLGED_XTTT;
+ IDC_LISTENING_TEXT: Changed:=Changed or DLGED_TUNE;
+ end;
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+ end;
+
+ CBN_SELCHANGE: begin
+ case loword(wParam) of
+ IDC_CBSTATYPE: begin
+ i:=CB_GetData(lParam);
+ for j:=0 to maxShowControls-1 do
+ ShowWindow(GetDlgItem(Dialog,aListFields[j]),aShowFields[i][j]);
+ end;
+
+ IDC_CBEX: begin
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+ end;
+ end;
+
+ BN_CLICKED: begin
+ case LoWord(wParam) of
+ IDC_CMD_DEFAULT: begin
+ RedrawFields(Dialog,0);
+ Changed:=Changed or DLGED_CHGD or DLGED_BASE;
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+ IDC_CMD_RESET: begin
+ RedrawFields(Dialog);
+ end;
+
+ IDC_STAT_ENABLE, IDC_XSTAT_ENABLE, IDC_TUNES_ENABLE: begin
+ case LoWord(wParam) of
+ IDC_STAT_ENABLE: begin
+ Changed:=Changed or DLGED_STTT;
+ EnableWindow(GetDlgItem(Dialog,IDC_STATUS_TEXT),
+ IsDlgButtonChecked(Dialog,IDC_STAT_ENABLE)<>BST_UNCHECKED);
+ end;
+ IDC_XSTAT_ENABLE: begin
+ b:=IsDlgButtonChecked(Dialog,IDC_XSTAT_ENABLE)<>BST_UNCHECKED;
+ EnableWindow(GetDlgItem(Dialog,IDC_XSTATUS_TITLE),b);
+ EnableWindow(GetDlgItem(Dialog,IDC_XSTATUS_TEXT ),b);
+ Changed:=Changed or DLGED_XTTL;
+ Changed:=Changed or DLGED_XTTT;
+ end;
+ IDC_TUNES_ENABLE: begin
+ Changed:=Changed or DLGED_TUNE;
+ EnableWindow(GetDlgItem(Dialog,IDC_LISTENING_TEXT),
+ IsDlgButtonChecked(Dialog,IDC_TUNES_ENABLE)<>BST_UNCHECKED);
+ end;
+ end;
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+
+ IDC_XSTAT_VIDEO, IDC_XSTAT_AUDIO: begin
+ SendDlgItemMessage(Dialog,IDC_CBEX,CB_SETCURSEL,SaveCBExValue(Dialog,false),0);
+ end;
+ IDC_HELP_COLOR: begin
+ ShowColorHelpDlg(Dialog);
+ exit;
+ end;
+ IDC_HELP_FORMAT: begin
+ MessageBoxW(0,TranslateW(sFormatHelp),TranslateW('Format text Info'),0);
+ exit;
+ end;
+ IDC_HELP_VARIABLES: begin
+ CallService(MS_WAT_MACROHELP,Dialog,0);
+ exit;
+ end;
+ IDC_IRC_USER: begin
+// CheckDlgButton(Dialog,IDC_IRC_USER ,BST_CHECKED);
+// CheckDlgButton(Dialog,IDC_IRC_CHANNEL,BST_UNCHECKED);
+ ShowWindow(GetDlgItem(Dialog,IDC_EDIT_MSG ),SW_SHOW);
+ ShowWindow(GetDlgItem(Dialog,IDC_EDIT_CHANNEL),SW_HIDE);
+ end;
+ IDC_IRC_CHANNEL: begin
+// CheckDlgButton(Dialog,IDC_IRC_USER ,BST_UNCHECKED);
+// CheckDlgButton(Dialog,IDC_IRC_CHANNEL,BST_CHECKED);
+ ShowWindow(GetDlgItem(Dialog,IDC_EDIT_CHANNEL),SW_SHOW);
+ ShowWindow(GetDlgItem(Dialog,IDC_EDIT_MSG ),SW_HIDE);
+ end;
+ else
+ SaveChanges(Dialog); //??
+ end;
+ end;
+ end;
+
+ end;
+
+ WM_HELP: begin
+ case PHELPINFO(lParam).iCtrlId of
+ IDC_EDIT_MSG,IDC_EDIT_CHANNEL,IDC_XSTATUS_TITLE,
+ IDC_STATUS_TEXT,IDC_XSTATUS_TEXT,IDC_LISTENING_TEXT:
+ CallService(MS_WAT_MACROHELP,Dialog,0);
+ end;
+ end;
+
+ WM_NOTIFY: begin
+ if (Changed and DLGED_INIT)<>0 then
+ exit;
+ if integer(PNMLISTVIEW(lParam)^.hdr.code)=LVN_ITEMCHANGED then
+ begin
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ if ((PNMLISTVIEW(lParam)^.uNewState and LVIS_FOCUSED)<>0) then
+ begin
+ SaveChanges(Dialog);
+
+ if PNMLISTVIEW(lParam)^.hdr.idFrom=IDC_PROTOLIST then
+ begin
+ CheckStatusList(GetDlgItem(Dialog,IDC_STATUSLIST),CurProto);
+ SaveCBExValue(Dialog,true);
+ CurProto:=PNMLISTVIEW(lParam)^.iItem;
+ SetScreenFull(Dialog);
+// ListView_GetItemTextA(PNMLISTVIEW(lParam)^.hdr.hwndFrom,CurProto,0,@buf,SizeOf(buf));
+ StrCopy(buf,GetProtoName(CurProto));
+ wnd:=GetDlgItem(Dialog,IDC_CBEX);
+ FillCBType(Dialog,buf);
+ if AddCBEx(wnd,buf)<>0 then
+ begin
+ i:=GetProtoSetting(CurProto,true);
+ SendMessage(wnd,CB_SETCURSEL,i and $FF,0);
+ end;
+ end
+
+ else //IDC_STATUSLIST
+ begin
+ Item.iItem:=PNMLISTVIEW(lParam)^.iItem;
+ Item.mask:=LVIF_PARAM;
+ SendMessageA(PNMLISTVIEW(lParam)^.hdr.hwndFrom,LVM_GETITEMA,0,tlparam(@Item));
+// ListView_GetItemA(PNMLISTVIEW(lParam)^.hdr.hwndFrom,Item);
+ CurStatus:=GetStatusNum(Item.lParam);
+ RedrawFields(Dialog);
+ end;
+ end
+
+ else if PNMLISTVIEW(lParam)^.uNewState<>0 then
+ begin
+ if PNMLISTVIEW(lParam)^.hdr.idFrom=IDC_PROTOLIST then
+ Changed:=Changed or DLGED_PROT or DLGED_CHGD
+ else
+ Changed:=Changed or DLGED_STAT or DLGED_CHGD;
+ end;
+ end
+
+ else if integer(PNMHdr(lParam)^.code)=PSN_APPLY then
+ begin
+ if (Changed and DLGED_PROT)<>0 then
+ begin
+ CheckProtoList(GetDlgItem(Dialog,IDC_PROTOLIST));
+ Changed:=Changed and not DLGED_PROT;
+ end;
+ if (Changed and DLGED_STAT)<>0 then
+ begin
+ CheckStatusList(GetDlgItem(Dialog,IDC_STATUSLIST),CurProto);
+ Changed:=Changed and not DLGED_STAT;
+ end;
+ SaveCBExValue(Dialog,true);
+ SaveChanges(Dialog);
+ SaveTemplates;
+ end;
+ end;
+ else
+ {result:=}DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+end;
diff --git a/plugins/Watrack/status/i_opt_12.inc b/plugins/Watrack/status/i_opt_12.inc
new file mode 100644
index 0000000000..7414ecf9d7
--- /dev/null
+++ b/plugins/Watrack/status/i_opt_12.inc
@@ -0,0 +1,108 @@
+{Templates}
+
+procedure SetScreenLite(Dialog:HWnd);
+var
+ p:pWideChar;
+begin
+ Changed:=Changed or DLGED_INIT;
+
+ p:=GetTemplateStr(tmpl_pm ,0,0);
+ SetDlgItemTextW(Dialog,IDC_EDIT_MSG ,p);
+ p:=GetTemplateStr(tmpl_xtitle,0,0);
+ SetDlgItemTextW(Dialog,IDC_XSTATUS_TITLE,p);
+ p:=GetTemplateStr(tmpl_stext ,0,0);
+ SetDlgItemTextW(Dialog,IDC_STATUS_TEXT ,p);
+ p:=GetTemplateStr(tmpl_chat ,0,0);
+ SetDlgItemTextW(Dialog,IDC_EDIT_CHANNEL ,p);
+
+ Changed:=Changed and not DLGED_INIT;
+end;
+
+procedure SetTemplateLite(Dialog:HWnd;idc:integer;Tmpl:tTemplateType);
+begin
+ SetTemplateStr(GetDlgText(Dialog,idc),Tmpl,0,0);
+end;
+
+procedure SaveChangesLite(Dialog:HWnd);
+begin
+ if (Changed and DLGED_BASE)<>0 then
+ begin
+ if (Changed and DLGED_MSG )<>0 then SetTemplateLite(Dialog,IDC_EDIT_MSG ,tmpl_pm);
+ if (Changed and DLGED_CHNL)<>0 then SetTemplateLite(Dialog,IDC_EDIT_CHANNEL,tmpl_chat);
+ if (Changed and DLGED_XTTL)<>0 then
+ begin
+ SetTemplateLite(Dialog,IDC_XSTATUS_TITLE,tmpl_xtitle);
+ end;
+ if (Changed and DLGED_STTT)<>0 then
+ begin
+ SetTemplateLite(Dialog,IDC_STATUS_TEXT,tmpl_stext);
+ SetTemplateLite(Dialog,IDC_STATUS_TEXT,tmpl_xtext);
+ SetTemplateLite(Dialog,IDC_STATUS_TEXT,tmpl_tunes);
+ end;
+
+ Changed:=Changed and (not DLGED_BASE);
+ SaveTemplates;
+ end;
+end;
+
+function DlgProcOptions12(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lparam; stdcall;
+begin
+ result:=0;
+ case hMessage of
+ WM_INITDIALOG: begin
+ TranslateDialogDefault(Dialog);
+ SetScreenLite(Dialog);
+ result:=0;
+ Changed:=0;
+ end;
+
+ WM_COMMAND: begin
+ if (Changed and DLGED_INIT)=0 then
+ begin
+ case wParam shr 16 of
+ EN_CHANGE: begin
+ Changed:=Changed or DLGED_CHGD or DLGED_PACK;
+ case loword(wParam) of
+ IDC_EDIT_MSG : Changed:=Changed or DLGED_MSG;
+ IDC_XSTATUS_TITLE : Changed:=Changed or DLGED_XTTL;
+ IDC_STATUS_TEXT : Changed:=Changed or DLGED_STTT;
+ IDC_EDIT_CHANNEL : Changed:=Changed or DLGED_CHNL;
+ end;
+ end;
+ BN_CLICKED: begin
+ case LoWord(wParam) of
+ IDC_CMD_RESET: begin
+ SetScreenLite(Dialog);
+ end;
+ IDC_HELP_COLOR: begin
+ ShowColorHelpDlg(Dialog);
+ exit;
+ end;
+ IDC_HELP_FORMAT: begin
+ MessageBoxW(0,TranslateW(sFormatHelp),TranslateW('Format text Info'),0);
+ exit;
+ end;
+ IDC_HELP_VARIABLES: begin
+ CallService(MS_WAT_MACROHELP,Dialog,0);
+ exit;
+ end;
+ end;
+ end;
+ else
+ exit;
+ end;
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+ end;
+
+ WM_HELP: CallService(MS_WAT_MACROHELP,Dialog,0);
+
+ WM_NOTIFY: begin
+ if integer(PNMHdr(lParam)^.code)=PSN_APPLY then
+ SaveChangesLite(Dialog);
+ end;
+
+ else
+ {result:=}DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+end;
diff --git a/plugins/Watrack/status/i_opt_3.inc b/plugins/Watrack/status/i_opt_3.inc
new file mode 100644
index 0000000000..3defc68b54
--- /dev/null
+++ b/plugins/Watrack/status/i_opt_3.inc
@@ -0,0 +1,106 @@
+{format specific}
+
+function DlgProcOptions3(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+const
+ dlginit:boolean=false;
+var
+ wnd:HWND;
+ b:boolean;
+begin
+ result:=0;
+ case hMessage of
+ WM_INITDIALOG: begin
+ dlginit:=false;
+ TranslateDialogDefault(Dialog);
+
+ MakeHint(Dialog,IDC_SIMPLEMODE,
+ 'If this option is "ON", one templates will be used with all '+
+ 'protocols, protocol and player (media) statuses. Template option page will be '+
+ 'changed next time.');
+ CheckDlgButton(Dialog,IDC_SIMPLEMODE,SimpleMode);
+ MakeHint(Dialog,IDC_INDEPENDED,
+ 'If this option is "ON", XStatus doesn''t depend of protocol status.');
+ CheckDlgButton(Dialog,IDC_INDEPENDED,XIndepended);
+ MakeHint(Dialog,IDC_USESTATUS,
+ 'If this option is "ON", status text will be replaced by music info.');
+ CheckDlgButton(Dialog,IDC_USESTATUS,UseStatus);
+ MakeHint(Dialog,IDC_USEMSGS,
+ 'If this option is "ON", you can paste music info to your '+
+ 'message window pressing hotkey.');
+ CheckDlgButton(Dialog,IDC_USEMSGS,UseMessages);
+ MakeHint(Dialog,IDC_KEEPSTATUS,
+ 'If this option is "ON", XStatus not changed when player shutdowned.');
+ CheckDlgButton(Dialog,IDC_KEEPSTATUS,KeepStatus);
+ MakeHint(Dialog,IDC_CLEARXSTAT,
+ 'xStatus will cleared before text changing and restored with new text later.');
+ CheckDlgButton(Dialog,IDC_CLEARXSTAT,ClearXStat);
+ MakeHint(Dialog,IDC_EXTSTATUS,
+ 'If this option is "ON", XStatus will be changed to "Music" and '+
+ 'status text will be replaced by music info.');
+ CheckDlgButton(Dialog,IDC_EXTSTATUS ,UseExtStatus);
+ MakeHint(Dialog,IDC_LISTENINGTO,
+ 'If this option is "ON", "Listening To" protocol property will be filled '+
+ 'by music info.');
+ CheckDlgButton(Dialog,IDC_LISTENINGTO,UseListeningTo);
+
+ wnd:=GetDlgItem(Dialog,IDC_SETXSTATUS);
+// SendMessage(wnd,CB_RESETCONTENT,0,0);
+ CB_AddStrDataW(wnd,TranslateW('any XStatus is set' ),0,0);
+ CB_AddStrDataW(wnd,TranslateW('''Music'' status is set' ),1,1);
+ CB_AddStrDataW(wnd,TranslateW('XStatus is empty or ''Music'''),2,2);
+ CB_SelectData(wnd,XStatusSet);
+
+ SendMessage(Dialog,WM_COMMAND,(BN_CLICKED shl 16)+IDC_EXTSTATUS,
+ GetDlgItem(Dialog,IDC_EXTSTATUS));
+
+ dlginit:=true;
+ result:=0;
+ end;
+
+ WM_COMMAND: begin
+ if ((wParam shr 16)=BN_CLICKED) and (LoWord(wParam)=IDC_EXTSTATUS) then
+ begin
+ b:=IsDlgButtonchecked(Dialog,IDC_EXTSTATUS)<>BST_UNCHECKED;
+ EnableWindow(GetDlgItem(Dialog,IDC_INDEPENDED),b);
+ EnableWindow(GetDlgItem(Dialog,IDC_CLEARXSTAT),b);
+// EnableWindow(GetDlgItem(Dialog,IDC_OLDXSTATUS),b);
+// EnableWindow(GetDlgItem(Dialog,IDC_ONLYMUSIC ),b);
+ EnableWindow(GetDlgItem(Dialog,IDC_SETXSTATUS),b);
+ EnableWindow(GetDlgItem(Dialog,IDC_KEEPSTATUS),b);
+ end;
+
+ case wParam shr 16 of
+ CBN_SELCHANGE,
+ EN_CHANGE,
+ BN_CLICKED: SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+ result:=1;
+ end;
+
+ WM_NOTIFY: begin
+ if dlginit then
+ begin
+ case integer(PNMHdr(lParam)^.code) of
+ LVN_ITEMCHANGED: begin
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+
+ PSN_APPLY: begin
+ SimpleMode :=IsDlgButtonChecked(Dialog,IDC_SIMPLEMODE);
+ XIndepended :=IsDlgButtonChecked(Dialog,IDC_INDEPENDED);
+ UseMessages :=IsDlgButtonChecked(Dialog,IDC_USEMSGS);
+ UseStatus :=IsDlgButtonChecked(Dialog,IDC_USESTATUS);
+ UseExtStatus :=IsDlgButtonChecked(Dialog,IDC_EXTSTATUS);
+ KeepStatus :=IsDlgButtonChecked(Dialog,IDC_KEEPSTATUS);
+ ClearXStat :=IsDlgButtonChecked(Dialog,IDC_CLEARXSTAT);
+ UseListeningTo:=IsDlgButtonChecked(Dialog,IDC_LISTENINGTO);
+ XStatusSet :=CB_GetData(GetDlgItem(Dialog,IDC_SETXSTATUS));
+ SaveOpt;
+ end;
+ end;
+ end;
+ end;
+ else
+ {result:=}DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+end;
diff --git a/plugins/Watrack/status/i_opt_status.inc b/plugins/Watrack/status/i_opt_status.inc
new file mode 100644
index 0000000000..09e0c208ce
--- /dev/null
+++ b/plugins/Watrack/status/i_opt_status.inc
@@ -0,0 +1,49 @@
+{}
+const
+ opt_ModStatus :PAnsiChar = 'module/statuses';
+
+ opt_UseStatus :PAnsiChar = 'usestatus';
+ opt_UseExtStat :PAnsiChar = 'useextstat';
+ opt_UseMsgs :PAnsiChar = 'usemsgs';
+ opt_XStatusSet :PAnsiChar = 'xstatusset';
+ opt_KeepStatus :PAnsiChar = 'keepstatus';
+ opt_Independed :PAnsiChar = 'independed';
+ opt_ClearXStat :PAnsiChar = 'clearxstat';
+ opt_SimplMode :PAnsiChar = 'simplemode';
+ opt_ListeningTo:PAnsiChar = 'listeningto';
+
+function GetModStatus:integer;
+begin
+ result:=DBReadByte(0,PluginShort,opt_ModStatus,1);
+end;
+
+procedure SetModStatus(stat:integer);
+begin
+ DBWriteByte(0,PluginShort,opt_ModStatus,stat);
+end;
+
+procedure LoadOpt;
+begin
+ SimpleMode :=DBReadByte(0,PluginShort,opt_SimplMode ,BST_CHECKED);
+ UseStatus :=DBReadByte(0,PluginShort,opt_UseStatus ,BST_CHECKED);
+ UseExtStatus :=DBReadByte(0,PluginShort,opt_UseExtStat ,BST_CHECKED);
+ UseMessages :=DBReadByte(0,PluginShort,opt_UseMsgs ,BST_CHECKED);
+ KeepStatus :=DBReadByte(0,PluginShort,opt_KeepStatus ,BST_UNCHECKED);
+ XIndepended :=DBReadByte(0,PluginShort,opt_Independed ,BST_CHECKED);
+ ClearXStat :=DBReadByte(0,PluginShort,opt_ClearXStat ,BST_UNCHECKED);
+ UseListeningTo:=DBReadByte(0,PluginShort,opt_ListeningTo,BST_UNCHECKED);
+ XStatusSet :=DBReadByte(0,PluginShort,opt_XStatusSet ,1);
+end;
+
+procedure SaveOpt;
+begin
+ DBWriteByte(0,PluginShort,opt_SimplMode ,SimpleMode);
+ DBWriteByte(0,PluginShort,opt_UseStatus ,UseStatus);
+ DBWriteByte(0,PluginShort,opt_UseExtStat ,UseExtStatus);
+ DBWriteByte(0,PluginShort,opt_UseMsgs ,UseMessages);
+ DBWriteByte(0,PluginShort,opt_KeepStatus ,KeepStatus);
+ DBWriteByte(0,PluginShort,opt_Independed ,XIndepended);
+ DBWriteByte(0,PluginShort,opt_ClearXStat ,ClearXStat);
+ DBWriteByte(0,PluginShort,opt_ListeningTo,UseListeningTo);
+ DBWriteByte(0,PluginShort,opt_XStatusSet ,XStatusSet);
+end;
diff --git a/plugins/Watrack/status/i_opt_tmpl.inc b/plugins/Watrack/status/i_opt_tmpl.inc
new file mode 100644
index 0000000000..9f5ea87907
--- /dev/null
+++ b/plugins/Watrack/status/i_opt_tmpl.inc
@@ -0,0 +1,244 @@
+{Save/load options}
+
+const
+ opt_numstr:PAnsiChar = 'template/numstr';
+const
+ ppref = 'proto/';
+ spref = 'strings/';
+
+procedure SaveTemplates;
+var
+ i,lProtoStatus:cardinal;
+ lTmplType:tTemplateType;
+ p:PAnsiChar;
+ buf:PAnsiChar;
+ NumProto:cardinal;
+ tmpl:pStrTemplate;
+ tmp:SmallInt;
+ setting:array [0..63] of AnsiChar;
+ pset:PAnsiChar;
+begin
+ DBWriteWord(0,PluginShort,opt_numstr,NumString);
+ StrCopy(setting,spref);
+ pset:=StrEnd(setting);
+ for i:=1 to NumString do
+ begin
+ IntToStr(pset,i);
+ DBWriteUnicode(0,PluginShort,setting,strings^[i].text);
+ end;
+
+ NumProto:=GetNumProto;
+ mGetMem(buf,16384);
+ for i:=0 to NumProto do
+ begin
+ pset:=StrCopyE(setting,ppref);
+ pset:=StrCopyE(pset,GetProtoName(i));
+
+ StrCopy(pset,'/XStatus');
+ DBWriteWord(0,PluginShort,setting,GetProtoSetting(i,true));
+ inc(pset);
+ if i<>0 then
+ begin
+ StrCopy(pset,'enabled');
+ DBWriteWord(0,PluginShort,setting,GetProtoSetting(i));
+ end;
+
+ p:=buf;
+ tmpl:=@StrTemplates^[i];
+ pset[3]:=#0;
+ for lProtoStatus:=0 to NumStatus-1 do
+ for lTmplType:=tmpl_first to tmpl_last do
+ begin
+ tmp:=tmpl^[lProtoStatus,lTmplType];
+ if tmp=0 then
+ begin
+ if p<>buf then
+ begin
+ p^:=',';
+ inc(p);
+ end;
+ p^:='0'; inc(p); // for compatibility
+ p^:=AnsiChar(lProtoStatus +ORD('0')); inc(p);
+ p^:=AnsiChar(ORD(lTmplType)+ORD('0')); inc(p);
+ end
+ else if (tmp<>0) and (tmp<>smallint(dubtmpl)) then
+ begin
+ pset[0]:='0'; // for compatibility
+ pset[1]:=AnsiChar(lProtoStatus +ORD('0'));
+ pset[2]:=AnsiChar(ORD(lTmplType)+ORD('0'));
+ DBWriteWord(0,PluginShort,setting,word(tmp));
+ end;
+ end;
+ if p<>buf then
+ begin
+ p^:=#0;
+ StrCopy(pset,'empty');
+ DBWriteString(0,PluginShort,setting,buf);
+ end;
+ end;
+ mFreeMem(buf);
+end;
+
+procedure InitDefault;
+var
+ tmpl:pStrTemplate;
+begin
+ NumString:=8;
+ mGetMem (strings ,SizeOf(tMyString)*NumString);
+ FillChar(strings^,SizeOf(tMyString)*NumString,0);
+
+ if isVarsInstalled then
+ begin
+ StrDupW(strings^[1].text,defAltTemplate);
+ StrDupW(strings^[4].text,defAltChannelText);
+ end
+ else
+ begin
+ StrDupW(strings^[1].text,defTemplate);
+ StrDupW(strings^[4].text,defChannelText);
+ end;
+ StrDupW(strings^[2].text,defStatusTitle);
+ StrDupW(strings^[3].text,defStatusText);
+
+ tmpl:=@StrTemplates^[DefaultTemplate];
+ if tmpl^[0,tmpl_first]=smallint(dubtmpl) then
+ begin
+ // music played
+ tmpl^[0,tmpl_pm ]:=1;
+ tmpl^[0,tmpl_chat ]:=4;
+ tmpl^[0,tmpl_xtitle]:=2;
+ tmpl^[0,tmpl_stext ]:=3;
+ tmpl^[0,tmpl_xtext ]:=3;
+ end;
+end;
+
+function EnumSettingsProc(const szSetting:PAnsiChar;lParam:LPARAM):int; cdecl;
+var
+ p:^PAnsiChar;
+ i:cardinal;
+ pp:AnsiChar;
+begin
+ if StrCmp(ppref,szSetting,Length(ppref))=0 then
+ begin
+ i:=StrLen(szSetting)+1;
+ pp:=szSetting[i-2];
+ if (pp>='0') and (pp<='9') then
+ begin
+ p:=pointer(lParam);
+ move(szSetting^,p^^,i);
+ inc(p^,i);
+ end;
+ end;
+ result:=0;
+end;
+
+function EnumTemplates:PAnsiChar;
+var
+ ces:TDBCONTACTENUMSETTINGS;
+ p:PAnsiChar;
+begin
+ mGetMem(result,16384);
+ result^:=#0;
+ p:=result;
+ ces.pfnEnumProc:=@EnumSettingsProc;
+ ces.lParam :=lparam(@p);
+ ces.szModule :=PluginShort;
+ ces.ofsSettings:=0;
+ CallService(MS_DB_CONTACT_ENUMSETTINGS,0,lparam(@ces));
+end;
+
+procedure LoadTemplates;
+var
+ buf:PAnsiChar;
+ lProtoStatus,i,j:cardinal;
+ lTmplType:tTemplateType;
+ p:PAnsiChar;
+ pc:PAnsiChar;
+ NumProto:cardinal;
+ tmpl:pStrTemplate;
+ setting:array [0..63] of AnsiChar;
+ pset:PAnsiChar;
+begin
+ NumString:=DBReadWord(0,PluginShort,opt_numstr,0);
+ if NumString>0 then
+ begin
+ mGetMem (strings ,SizeOf(tMyString)*NumString);
+ FillChar(strings^,SizeOf(tMyString)*NumString,0);
+ StrCopy(setting,spref);
+ pset:=StrEnd(setting);
+ for i:=1 to NumString do
+ begin
+ IntToStr(pset,i);
+ strings^[i].text:=DBReadUnicode(0,PluginShort,setting,nil);
+ end;
+
+ NumProto:=GetNumProto;
+ buf:=EnumTemplates;
+ for i:=0 to NumProto do
+ begin
+ pset:=StrCopyE(setting,ppref);
+ pset:=StrCopyE(pset,GetProtoName(i));
+
+ StrCopy(pset,'/XStatus');
+ j:=DBReadWord(0,PluginShort,setting,$080B);
+{!!
+ if j=0 then
+ j:=DefaultXStatus;
+}
+ SetProtoSetting(i,j,true);
+
+ inc(pset);
+ if i<>0 then
+ begin
+ StrCopy(pset,'enabled');
+ SetProtoSetting(i,DBReadWord(0,PluginShort,setting,psf_all));
+ end;
+
+ tmpl:=@StrTemplates^[i];
+ StrCopy(pset,'empty');
+ pc:=DBReadString(0,PluginShort,setting,nil);
+ if pc<>nil then
+ begin
+ p:=pc;
+ if (p^>='0') and (p^<='9') then
+ while p^<>#0 do
+ begin
+ lProtoStatus := ORD(p[1])-ORD('0');
+ lTmplType :=tTemplateType(ORD(p[2])-ORD('0'));
+ tmpl^[lProtoStatus,lTmplType]:=0;
+ inc(p,3);
+ if p^=',' then
+ inc(p);
+ end;
+ mFreeMem(pc);
+ end;
+
+ pc:=buf;
+ pset^:=#0;
+ j:=StrLen(setting);
+ pset[3]:=#0;
+ while pc^<>#0 do
+ begin
+ if StrCmp(pc,setting,j)=0 then // only proper proto
+ begin
+ pc:=StrEnd(pc);
+ lProtoStatus := ORD((pc-2)^)-ORD('0');
+ lTmplType :=tTemplateType(ORD((pc-1)^)-ORD('0'));
+
+ pset[0]:='0';
+ pset[1]:=AnsiChar(lProtoStatus +ORD('0'));
+ pset[2]:=AnsiChar(ORD(lTmplType)+ORD('0'));
+
+ tmpl^[lProtoStatus,lTmplType]:=
+ DBReadWord(0,PluginShort,setting,dubtmpl);
+ end
+ else
+ pc:=StrEnd(pc);
+ inc(pc);
+ end;
+ end;
+ mFreeMem(buf);
+ end
+ else
+ InitDefault;
+end;
diff --git a/plugins/Watrack/status/i_st_rc.inc b/plugins/Watrack/status/i_st_rc.inc
new file mode 100644
index 0000000000..c17768c2e7
--- /dev/null
+++ b/plugins/Watrack/status/i_st_rc.inc
@@ -0,0 +1,45 @@
+{DLG 1 - common}
+const
+ IDC_USEMSGS = 2025;
+ IDC_HOTKEYGLOB = 2026;
+ IDC_STAT_HOTKEY = 2027;
+ IDC_USESTATUS = 2028;
+ IDC_EXTSTATUS = 2029;
+// IDC_ONLYMUSIC = 2030;
+ IDC_KEEPSTATUS = 2031;
+ IDC_SIMPLEMODE = 2032;
+// IDC_NOTES = 2033;
+ IDC_INDEPENDED = 2034;
+// IDC_OLDXSTATUS = 2035;
+ IDC_CLEARXSTAT = 2036;
+ IDC_LISTENINGTO = 2037;
+
+ IDC_SETXSTATUS = 2030;
+
+{DLG 2 - templates}
+const
+ IDC_HELP_FORMAT = 1025;
+ IDC_HELP_COLOR = 1027;
+ IDC_HELP_VARIABLES = 1028;
+ {special}
+ IDC_EDIT_MSG = 2032;
+ IDC_STATUS_TEXT = 2034;
+ IDC_EDIT_CHANNEL = 2035;
+ IDC_XSTATUS_TITLE = 2036;
+ IDC_XSTATUS_TEXT = 2037;
+ IDC_LISTENING_TEXT = 2038;
+ IDC_STAT_ENABLE = 2039;
+ IDC_CBSTATYPE = 2040;
+ IDC_XSTAT_ENABLE = 2041;
+ IDC_TUNES_ENABLE = 2042;
+
+ IDC_PROTOLIST = 1037;
+ IDC_STATUSLIST = 1038;
+ IDC_IRC_CHANNEL = 1041;
+ IDC_IRC_USER = 1042;
+ IDC_CMD_RESET = 1044;
+ IDC_CMD_DEFAULT = 1045;
+
+ IDC_CBEX = 1046;
+ IDC_XSTAT_VIDEO = 1047;
+ IDC_XSTAT_AUDIO = 1048;
diff --git a/plugins/Watrack/status/i_st_vars.inc b/plugins/Watrack/status/i_st_vars.inc
new file mode 100644
index 0000000000..e1696c3621
--- /dev/null
+++ b/plugins/Watrack/status/i_st_vars.inc
@@ -0,0 +1,26 @@
+{}
+const
+{
+ OldStatusText:array [0..15] of pWideChar =
+ (nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil);
+}
+ OldXStatus:array [0..31] of byte =
+ (255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255);
+var
+ UseListeningTo:cardinal;
+ SimpleMode :cardinal;
+ UseStatus :cardinal;
+ UseExtStatus :cardinal;
+ UseMessages :cardinal;
+ KeepStatus :cardinal;
+ XIndepended :cardinal;
+ XStatusSet :cardinal;
+ ClearXStat :cardinal;
+ hINS :THANDLE;
+// hLTo :THANDLE;
+ plStatusHook :THANDLE;
+
+const
+ Changed:cardinal=0;
+ LastStatus:integer=WAT_PLS_NOTFOUND;
diff --git a/plugins/Watrack/status/i_status.inc b/plugins/Watrack/status/i_status.inc
new file mode 100644
index 0000000000..a7e3d2e5ef
--- /dev/null
+++ b/plugins/Watrack/status/i_status.inc
@@ -0,0 +1,223 @@
+{Status and XStatus processing}
+
+// XStatus
+const
+ xsnum = 31;
+
+function ListenProc(wParam:WPARAM;lParam:LPARAM):int_ptr; cdecl;
+begin
+ // ignoring incoming ListeningTo info, uses internal
+ if PLISTENINGTOINFO(lParam).cbSize=SizeOf(TLISTENINGTOINFO) then
+ begin
+ result:=int_ptr(GetMacros(tmpl_tunes,0)); // need real proto number here
+ if result=-1 then result:=0;
+ end
+ else
+ begin
+ result:=CallService(MS_WAT_REPLACETEXT,0,wParam);
+ end;
+end;
+
+function IsOurStatus(protomask,status:dword):boolean;
+var
+ mask:dword;
+begin
+{ if status=ID_STATUS_OFFLINE then mask:=M_STAT_OFFLINE
+ else }
+ if status=ID_STATUS_ONLINE then mask:=psf_online
+ else if status=ID_STATUS_INVISIBLE then mask:=psf_invisible
+ else if status=ID_STATUS_AWAY then mask:=psf_shortaway
+ else if status=ID_STATUS_NA then mask:=psf_longaway
+ else if status=ID_STATUS_DND then mask:=psf_heavydnd
+ else if status=ID_STATUS_OCCUPIED then mask:=psf_lightdnd
+ else if status=ID_STATUS_FREECHAT then mask:=psf_freechat
+ else if status=ID_STATUS_ONTHEPHONE then mask:=psf_onthephone
+ else if status=ID_STATUS_OUTTOLUNCH then mask:=psf_outtolunch
+ else mask:=0;
+ result:=(protomask and mask)<>0;
+end;
+
+function NewPlStatus(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ si:pSongInfo;
+ i,j:integer;
+ xstat:integer;
+ txt,title:pWideChar;
+ proto,ls:PAnsiChar;
+ mask,status:dword;
+ todo,isvideo:bool;
+ oldstatus:integer;
+ staudio,stvideo:integer;
+ lti:TLISTENINGTOINFO;
+ buf,buf1:array [0..31] of WideChar;
+ doClear:boolean;
+begin
+ result:=0;
+ si:=pSongInfo(lParam);
+
+ case wParam of
+ WAT_EVENT_NEWTRACK: begin
+ isvideo:=pSongInfo(lParam)^.width<>0;
+ doClear:=false;
+ end;
+
+ WAT_EVENT_PLAYERSTATUS: begin
+ LastStatus:=lParam;
+ if (loword(lParam)=WAT_PLS_NORMAL) then
+ exit;
+ doClear:=true;
+ end;
+ else
+ exit; // not accept template changes
+ end;
+
+ i:=GetNumProto;
+ for j:=1 to i do
+ begin
+ proto:=GetProtoName(j);
+ mask:=GetProtoSetting(j);
+
+ if (SimpleMode<>BST_UNCHECKED) or ((mask and psf_enabled)<>0) then
+ begin
+
+ if (UseStatus<>BST_UNCHECKED) or (UseExtStatus<>BST_UNCHECKED) then
+ begin
+ status:=CallProtoService(proto,PS_GETSTATUS,0,0);
+ todo :=(SimpleMode<>BST_UNCHECKED) or IsOurStatus(mask,status);
+ end
+ else
+ begin
+ status:=ID_STATUS_ONLINE;
+ todo :=true;
+ end;
+
+ if (UseListeningTo<>BST_UNCHECKED) and
+ IsTunesSupported(j) then
+// (ProtoServiceExists(proto,PS_SET_LISTENINGTO)<>0) then
+ begin
+ if doClear then
+ CallProtoService(proto,PS_SET_LISTENINGTO,0,0)
+ else if (wParam=WAT_EVENT_NEWTRACK) then
+ begin
+ lti.cbSize :=SizeOf(lti);
+ if si.width<>0 then
+ lti.szType.W:='Video'
+ else
+ lti.szType.W:='Music';
+ lti.szArtist.W:=si.artist;
+ lti.szAlbum .W:=si.album;
+ lti.szTitle .W:=si.title;
+ lti.szTrack .W:=IntToStr(buf1,si.track);
+ lti.szYear .W:=si.year;
+ lti.szGenre .W:=si.genre;
+ lti.szLength.W:=IntToStr(buf,si.total);
+ lti.szPlayer.W:=si.player;
+ lti.dwFlags :=LTI_UNICODE;
+
+ CallProtoService(proto,PS_SET_LISTENINGTO,0,tlparam(@lti));
+ end;
+ end;
+
+ if UseStatus<>BST_UNCHECKED then
+ if todo then
+ begin
+ if SimpleMode<>BST_UNCHECKED then
+ txt:=GetMacros(tmpl_stext,0)
+ else
+ txt:=GetMacros(tmpl_stext,j);
+ if uint_ptr(txt)<>uint_ptr(-1) then
+ begin
+ if (txt=nil) or (txt^=#0) then
+ ls:=nil
+ else
+ WideToAnsi(txt,ls,UserCP);
+ SetStatus(proto,-status,ls);
+ mFreeMem(ls);
+ mFreeMem(txt);
+ end;
+ end;
+
+ if UseExtStatus<>BST_UNCHECKED then
+ begin
+ if todo or (XIndepended<>BST_UNCHECKED) then
+ begin
+ if IsXStatusSupported(j) then
+ begin
+ if doClear then // player status changed to no music/no player
+ begin
+
+ if KeepStatus=BST_UNCHECKED then
+ begin
+ // just restoring savedstatus if was. no text changing
+ if OldXStatus[j]<>255 then
+ begin
+ oldstatus:=OldXStatus[j];
+ OldXStatus[j]:=255;
+ SetXStatus(proto,oldstatus,pWideChar(-1),pWideChar(-1));
+ end;
+ end;
+
+ end
+ else
+ begin
+ if SimpleMode<>BST_UNCHECKED then
+ txt:=GetMacros(tmpl_xtext,0)
+ else
+ txt:=GetMacros(tmpl_xtext,j);
+ if uint_ptr(txt)<>uint_ptr(-1) then // status template presents
+ begin
+ // XStatus for audio/video
+ if SimpleMode<>BST_UNCHECKED then
+ begin
+ stvideo := 8;
+ staudio := 11;
+ end
+ else
+ begin
+ mask:=GetProtoSetting(j,true);
+ staudio:=mask and $FF;
+ stvideo:=(mask shr 8) and $FF;
+ end;
+ // Check, what we able to do something
+ oldstatus:=GetXStatus(proto,nil,nil);
+ if XStatusSet<>0 then // no matter which xstatus
+ begin
+ if not ((oldstatus=staudio) or (oldstatus=stvideo) or // music
+ ((oldstatus=0) and (XStatusSet=2))) then // empty
+ begin
+ mFreeMem(txt);
+ continue; //!! do nothing!
+ end;
+ end;
+ if isvideo then
+ xstat:=stvideo
+ else
+ xstat:=staudio;
+
+ if xstat=0 then // not choosed, keep old (current)
+ xstat:=oldstatus
+ else
+ begin
+ if OldXStatus[j]=255 then
+ OldXStatus[j]:=oldstatus;
+ end;
+
+ if ClearXStat<>BST_UNCHECKED then
+ SetXStatus(proto,0);
+
+ if SimpleMode<>BST_UNCHECKED then
+ title:=GetMacros(tmpl_xtitle,0)
+ else
+ title:=GetMacros(tmpl_xtitle,j);
+ SetXStatus(proto,xstat,txt,title);
+ mFreeMem(title);
+ mFreeMem(txt);
+ end;
+ end;
+ end;
+ end;
+ end;
+
+ end;
+ end;
+end;
diff --git a/plugins/Watrack/status/status.pas b/plugins/Watrack/status/status.pas
new file mode 100644
index 0000000000..b5db16bb1c
--- /dev/null
+++ b/plugins/Watrack/status/status.pas
@@ -0,0 +1,142 @@
+{Statistic}
+unit Status;
+{$include compilers.inc}
+interface
+{$Resource status.res}
+implementation
+
+uses
+ windows,messages,commctrl,
+ common,m_api,mirutils,protocols,dbsettings,wrapper,
+ global,wat_api,hlpdlg,CBEx,myRTF,Tmpl;
+
+const
+ HKN_INSERT:PansiChar = 'WAT_Insert';
+
+procedure reghotkey;
+var
+ hkrec:HOTKEYDESC;
+begin
+// if DisablePlugin=dsPermanent then
+// exit;
+ FillChar(hkrec,SizeOf(hkrec),0);
+ with hkrec do
+ begin
+ cbSize :=HOTKEYDESC_SIZE_V1;
+ pszName :=HKN_INSERT;
+ pszDescription.a:='Global WATrack hotkey';
+ pszSection.a :=PluginName;
+ pszService :=MS_WAT_INSERT;
+ DefHotKey :=((HOTKEYF_ALT or HOTKEYF_CONTROL) shl 8) or VK_F5;
+// lParam :=0;
+ end;
+ CallService(MS_HOTKEY_REGISTER,0,lparam(@hkrec));
+end;
+
+{$include i_st_vars.inc}
+{$include i_st_rc.inc}
+{$include i_opt_status.inc}
+{$include i_hotkey.inc}
+{$include i_status.inc}
+{$include i_opt_3.inc}
+{$include i_opt_11.inc}
+{$include i_opt_12.inc}
+
+// ------------ base interface functions -------------
+
+var
+ mStatus:twModule;
+
+function InitProc(aGetStatus:boolean=false):integer;
+begin
+ if aGetStatus then
+ begin
+ if GetModStatus=0 then
+ begin
+ result:=0;
+ exit;
+ end;
+ end
+ else
+ SetModStatus(1);
+ result:=1;
+
+ LoadOpt;
+ CreateProtoList;
+ CreateTemplates;
+ hINS:=CreateServiceFunction(MS_WAT_INSERT,@InsertProc);
+ reghotkey;
+ plStatusHook:=HookEvent(ME_WAT_NEWSTATUS,@NewPlStatus);
+
+// mStatus.ModuleStat:=1;
+
+// if ServiceExists(MS_LISTENINGTO_GETPARSEDTEXT)<>0 then
+// hLTo:=CreateServiceFunction(MS_LISTENINGTO_GETPARSEDTEXT,@ListenProc);
+end;
+
+procedure DeInitProc(aSetDisable:boolean);
+var
+ j:integer;
+begin
+ if aSetDisable then
+ SetModStatus(0);
+
+ for j:=1 to GetNumProto do
+ begin
+ if (SimpleMode<>BST_UNCHECKED) or ((GetProtoSetting(j) and psf_enabled)<>0) then
+ CallProtoService(GetProtoName(j),PS_SET_LISTENINGTO,0,0);
+ end;
+// DestroyServiceFunction(hLTo);
+ DestroyServiceFunction(hINS);
+ UnhookEvent(plStatusHook);
+ FreeProtoList;
+ FreeTemplates;
+
+// mStatus.ModuleStat:=0;
+end;
+
+function AddOptionsPage(var tmpl:pAnsiChar;var proc:pointer;var name:PAnsiChar):integer;
+const
+ count:integer=2;
+begin
+ if count=0 then
+ count:=2;
+ case count of
+ 2: begin
+ tmpl:='COMMON';
+ proc:=@DlgProcOptions3;
+ name:='Status (common)';
+ end;
+ 1: begin
+ if SimpleMode=BST_UNCHECKED then
+ begin
+ tmpl:='TEMPLATE11';
+ proc:=@DlgProcOptions11;
+ end
+ else
+ begin
+ tmpl:='TEMPLATE12';
+ proc:=@DlgProcOptions12;
+ end;
+ name:='Status (templates)';
+ end
+ end;
+
+ dec(count);
+ result:=count;
+end;
+
+procedure Init;
+begin
+ mStatus.Next :=ModuleLink;
+ mStatus.Init :=@InitProc;
+ mStatus.DeInit :=@DeInitProc;
+ mStatus.AddOption :=@AddOptionsPage;
+ mStatus.ModuleName:='Statuses';
+// mStatus.ModuleStat:=0;
+ ModuleLink :=@mStatus;
+end;
+
+begin
+ Init;
+end.
diff --git a/plugins/Watrack/status/status.rc b/plugins/Watrack/status/status.rc
new file mode 100644
index 0000000000..9a1c78c476
--- /dev/null
+++ b/plugins/Watrack/status/status.rc
@@ -0,0 +1,88 @@
+#include "i_st_rc.inc"
+
+LANGUAGE 0,0
+
+COMMON DIALOGEX 0, 0, 304, 226, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ GROUPBOX "Options", -1, 2, 2, 188, 218, WS_TABSTOP
+ AUTOCHECKBOX "Simple Template mode", IDC_SIMPLEMODE , 6, 12, 182, 14, BS_VCENTER | BS_MULTILINE// | BS_NOTIFY
+
+ AUTOCHECKBOX "Insert in messages" , IDC_USEMSGS , 6, 34, 182, 14, BS_VCENTER | BS_MULTILINE// | BS_NOTIFY
+ AUTOCHECKBOX "Use status messages" , IDC_USESTATUS , 6, 50, 182, 14, BS_VCENTER | BS_MULTILINE// | BS_NOTIFY
+ AUTOCHECKBOX "Use listening info" , IDC_LISTENINGTO, 6, 66, 182, 14, BS_VCENTER | BS_MULTILINE// | BS_NOTIFY
+
+ AUTOCHECKBOX "Use XStatus" , IDC_EXTSTATUS , 6, 86, 182, 14, BS_VCENTER | BS_MULTILINE// | BS_NOTIFY
+ AUTOCHECKBOX "Independed XStatus" , IDC_INDEPENDED, 14, 102, 174, 14, BS_VCENTER | BS_MULTILINE// | BS_NOTIFY
+ AUTOCHECKBOX "Clear xStatus before set new one" , IDC_CLEARXSTAT, 14, 118, 174, 14, BS_VCENTER | BS_MULTILINE// | BS_NOTIFY
+ AUTOCHECKBOX "Keep 'Music' XStatus" , IDC_KEEPSTATUS, 14, 134, 174, 14, BS_VCENTER | BS_MULTILINE// | BS_NOTIFY
+// AUTOCHECKBOX "Use existing XStatus" , IDC_OLDXSTATUS, 14, 134, 174, 14, BS_VCENTER | BS_MULTILINE// | BS_NOTIFY
+// AUTOCHECKBOX "Only if 'Music' status was set", IDC_ONLYMUSIC , 14, 150, 174, 14, BS_VCENTER | BS_MULTILINE// | BS_NOTIFY
+ LTEXT "Set XStatus when...", -1, 14, 150, 174, 14
+ COMBOBOX IDC_SETXSTATUS, 14, 166, 174, 96, CBS_DROPDOWNLIST | WS_VSCROLL
+}
+
+TEMPLATE11 DIALOGEX 0, 0, 304, 226, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ PUSHBUTTON "Color", IDC_HELP_COLOR , 184, 1, 26, 12
+ PUSHBUTTON "Format", IDC_HELP_FORMAT , 213, 1, 32, 12
+ PUSHBUTTON "Variables", IDC_HELP_VARIABLES, 248, 1, 52, 12
+
+ CTEXT "Protocols", -1, 6, 2, 80, 10
+ CONTROL "", IDC_PROTOLIST, "SysListView32", WS_BORDER | WS_TABSTOP | LVS_NOCOLUMNHEADER | LVS_SHOWSELALWAYS | LVS_SINGLESEL | LVS_REPORT, 6, 12, 80, 92, WS_EX_CONTROLPARENT
+ CTEXT "Statuses", -1, 90, 2, 92, 10
+ CONTROL "", IDC_STATUSLIST, "SysListView32", WS_BORDER | WS_TABSTOP | LVS_NOCOLUMNHEADER | LVS_SHOWSELALWAYS | LVS_SINGLESEL | LVS_REPORT, 90, 12, 92, 92, WS_EX_CONTROLPARENT
+
+ AUTORADIOBUTTON "User message", IDC_IRC_USER , 196, 51, 102, 12, NOT WS_TABSTOP | WS_GROUP
+ AUTORADIOBUTTON "Channel message", IDC_IRC_CHANNEL, 196, 63, 102, 12, NOT WS_TABSTOP
+
+ PUSHBUTTON "Reset", IDC_CMD_RESET , 184, 103, 56, 12
+ PUSHBUTTON "Default", IDC_CMD_DEFAULT, 242, 103, 56, 12
+
+ LTEXT "Template", -1, 16, 106, 166, 10, SS_CENTERIMAGE
+ EDITTEXT IDC_EDIT_MSG , 6, 116, 290, 44, ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL
+ EDITTEXT IDC_EDIT_CHANNEL, 6, 116, 290, 44, ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL
+
+ AUTOCHECKBOX "Enable Status message" , IDC_STAT_ENABLE , 6, 161, 174, 12, BS_VCENTER | BS_MULTILINE
+ AUTOCHECKBOX "Enable XStatus message", IDC_XSTAT_ENABLE, 6, 161, 174, 12, BS_VCENTER | BS_MULTILINE
+ AUTOCHECKBOX "Enable Tunes message" , IDC_TUNES_ENABLE, 6, 161, 174, 12, BS_VCENTER | BS_MULTILINE
+ COMBOBOX IDC_CBSTATYPE,196,161,100,56,CBS_DROPDOWNLIST | WS_VSCROLL | NOT WS_TABSTOP
+ EDITTEXT IDC_STATUS_TEXT , 6, 175, 290, 45, ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL
+ EDITTEXT IDC_XSTATUS_TITLE , 6, 175, 290, 14, ES_AUTOHSCROLL
+ EDITTEXT IDC_XSTATUS_TEXT , 6, 192, 290, 28, ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL
+ EDITTEXT IDC_LISTENING_TEXT , 6, 175, 290, 45, ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL
+
+ AUTORADIOBUTTON "Audio", IDC_XSTAT_AUDIO, 186, 75, 54, 12, NOT WS_TABSTOP | BS_RIGHT | BS_LEFTTEXT | WS_GROUP
+ AUTORADIOBUTTON "Video", IDC_XSTAT_VIDEO, 244, 75, 54, 12, NOT WS_TABSTOP
+
+ CONTROL "", IDC_CBEX, "ComboBoxEx32",
+ WS_GROUP | WS_TABSTOP | WS_VSCROLL | CBS_AUTOHSCROLL | CBS_DROPDOWNLIST, 186, 88, 112, 100
+}
+
+TEMPLATE12 DIALOGEX 0, 0, 304, 226, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ PUSHBUTTON "Color", IDC_HELP_COLOR , 184, 4, 26, 12
+ PUSHBUTTON "Format", IDC_HELP_FORMAT , 213, 4, 32, 12
+ PUSHBUTTON "Variables", IDC_HELP_VARIABLES, 248, 4, 52, 12
+
+ PUSHBUTTON "Reset", IDC_CMD_RESET, 6, 4, 56, 12
+ LTEXT "This is simplified version of template editor. This templates will be used with all protocols, protocol and player (media) statuses",
+ -1, 6,20,290,22
+
+ CTEXT "Template", -1, 6, 42, 290, 10
+ EDITTEXT IDC_EDIT_MSG, 6, 52, 290, 48, ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL
+ CTEXT "Chat Template", -1, 6, 102, 290, 10
+ EDITTEXT IDC_EDIT_CHANNEL, 6, 112, 290, 48, ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL
+
+ CTEXT "[X]Status Title / Text", -1, 6, 165, 290, 10
+ EDITTEXT IDC_XSTATUS_TITLE, 6, 175, 290, 14, ES_AUTOHSCROLL
+ EDITTEXT IDC_STATUS_TEXT , 6, 192, 290, 28, ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
+}
diff --git a/plugins/Watrack/status/status.res b/plugins/Watrack/status/status.res
new file mode 100644
index 0000000000..fba2526583
--- /dev/null
+++ b/plugins/Watrack/status/status.res
Binary files differ
diff --git a/plugins/Watrack/status/tmpl.pas b/plugins/Watrack/status/tmpl.pas
new file mode 100644
index 0000000000..4adcd20bdf
--- /dev/null
+++ b/plugins/Watrack/status/tmpl.pas
@@ -0,0 +1,304 @@
+unit Tmpl;
+
+interface
+// ----- main data -----
+type
+ tTemplateType = (
+ tmpl_pm ,tmpl_chat,
+ tmpl_xtitle,tmpl_xtext,
+ tmpl_stext ,
+ tmpl_tunes);
+const
+ tmpl_first = tmpl_pm;
+ tmpl_last = tmpl_tunes;
+{
+const
+ TMPL_EMPTY = 0;
+ TMPL_PARENT = $4000;
+ TMPL_INACTIVE = ;
+}
+procedure CreateTemplates;
+procedure FreeTemplates;
+procedure SaveTemplates;
+
+function SetTemplateActive(active:boolean;aType:tTemplateType;proto:cardinal=0;
+ ProtoStatus:integer=0):boolean;
+function IsTemplateActive(aType:tTemplateType;proto:cardinal=0;
+ ProtoStatus:integer=0):boolean;
+function GetTemplateStr(aType:tTemplateType;proto:cardinal=0;
+ ProtoStatus:integer=0):PWideChar;
+function SetTemplateStr(aStr:PWideChar;aType:tTemplateType;proto:cardinal=0;
+ ProtoStatus:integer=0):integer;
+
+function GetMacros(TmplType:tTemplateType;proto:integer):pWideChar;
+
+implementation
+
+uses common, m_api, windows, dbsettings, mirutils, protocols,wat_api,global;
+
+const
+ dubtmpl = $4000;
+const
+ DefaultTemplate = 0;
+ NumStatus = 10;
+
+type
+ pStrTemplate = ^tStrTemplate;
+ tStrTemplate = array [0..NumStatus-1,tTemplateType] of SmallInt;
+
+type
+ pMyString = ^tMyString;
+ tMyString = record
+ count:cardinal; // link count
+ text :pWideChar;
+ end;
+ pMyStrArray = ^tMyStrArray;
+ tMyStrArray = array [1..1000] of tMyString;
+
+type
+ tTmpl = integer;
+ pStrTemplates = ^tStrTemplates;
+ tStrTemplates = array [0..100] of tStrTemplate;
+
+const
+ NumTemplates:cardinal=0;
+ StrTemplates:pStrTemplates=nil;
+
+var
+ strings:pMyStrArray;
+ NumString:cardinal;
+
+const
+ defTemplate = 'I am listening to %artist% - "%title%"';
+ defChannelText = '/me listening to %artist% - "%title%"';
+ defStatusTitle = 'Now listening to';
+ defStatusText = '%artist% - %title%';
+
+ defAltTemplate = 'I am listening to %artist% - %title%?iflonger(%album%,0, (from "%album%"),)';
+ defAltChannelText = '/me listening to %artist% - %title%?iflonger(%album%,0, (from "%album%"),)';
+
+// ----- procedures -----
+{$include i_opt_tmpl.inc}
+
+function AddString(var newstr:PWideChar):cardinal;
+var
+ i:cardinal;
+ tmp:pMyStrArray;
+begin
+ for i:=1 to NumString do // search in table
+ begin
+ if StrCmpW(newstr,strings^[i].text)=0 then
+ begin
+ result:=i;
+ mFreeMem(newstr);
+ exit;
+ end;
+ end;
+ Inc(NumString);
+ mGetMem(tmp,SizeOf(tMyString)*NumString);
+ move(strings^,tmp^,SizeOf(tMyString)*(NumString-1));
+ mFreeMem(strings);
+ strings:=tmp;
+ tmp^[NumString].count:=0;
+ tmp^[NumString].text:=newstr;
+ result:=NumString;
+end;
+
+procedure PackStrings;
+var
+ i,j:integer;
+ OldNumString:cardinal;
+ lTmplType:tTemplateType;
+ lProtoStatus:cardinal;
+ tmp:pMyStrArray;
+ NumProto:integer;
+ tmpl:pStrTemplate;
+begin
+ // clear counters
+ for i:=1 to NumString do
+ strings^[i].count:=0;
+ // counts strings
+ NumProto:=GetNumProto;
+ for i:=0 to NumProto do
+ begin
+ tmpl:=@StrTemplates^[i];
+ for lProtoStatus:=0 to NumStatus-1 do
+ for lTmplType:=tmpl_first to tmpl_last do
+ begin
+ j:=tmpl^[lProtoStatus,lTmplType];
+ if j>0 then
+ inc(strings^[j].count);
+ end;
+ end;
+ // delete strings
+ i:=1;
+ OldNumString:=NumString;
+
+ if DisablePlugin=dsEnabled then
+ DisablePlugin:=dsTemporary;
+
+ while Cardinal(i)<=NumString do
+ begin
+ if strings^[i].count=0 then
+ begin
+ mFreeMem(strings^[i].text);
+ if cardinal(i)<NumString then
+ begin
+ // shift strings
+ move(strings^[i+1],strings^[i],SizeOf(tMyString)*(NumString-cardinal(i)));
+ // shift protos
+ for j:=0 to NumProto do
+ begin
+ tmpl:=@StrTemplates^[j];
+ for lProtoStatus:=0 to NumStatus-1 do
+ for lTmplType:=tmpl_first to tmpl_last do
+ begin
+ if tmpl^[lProtoStatus,lTmplType]>i then
+ dec(tmpl^[lProtoStatus,lTmplType]);
+ end;
+ end;
+ end;
+ dec(NumString);
+ continue;
+ end;
+ inc(i);
+ end;
+ if OldNumString<>NumString then
+ begin
+ mGetMem(tmp,SizeOf(tMyString)*NumString);
+ move(strings^,tmp^,SizeOf(tMyString)*NumString);
+ mFreeMem(strings);
+ strings:=tmp;
+ end;
+
+// if DisablePlugin<0 then
+// SetTitle;
+ if DisablePlugin<>dsPermanent then
+ DisablePlugin:=dsEnabled;
+
+end;
+
+function SetTemplateActive(active:boolean;aType:tTemplateType;proto:cardinal=0;
+ ProtoStatus:integer=0):boolean;
+var
+ res:smallint;
+begin
+ if proto>NumTemplates then
+ proto:=0;
+
+ res:=ABS(StrTemplates^[proto][ProtoStatus,aType]);
+ if not active then res:=-res;
+ StrTemplates^[proto][ProtoStatus,aType]:=res;
+ result:=res>0;
+end;
+
+function IsTemplateActive(aType:tTemplateType;proto:cardinal=0;
+ ProtoStatus:integer=0):boolean;
+begin
+ if proto>NumTemplates then
+ proto:=0;
+
+ result:=StrTemplates^[proto][ProtoStatus,aType]>0;
+end;
+
+function GetTmplString(num:integer):pWideChar;
+begin
+ if (num>0) and (Cardinal(num)<=NumString) then
+ result:=strings^[num].text
+ else
+ result:=nil;
+end;
+
+function GetTemplateStr(aType:tTemplateType;proto:cardinal=0;
+ ProtoStatus:integer=0):PWideChar;
+var
+ i:smallint;
+begin
+ if proto>NumTemplates then
+ proto:=0;
+
+ i:=abs(StrTemplates^[proto ][ProtoStatus,aType]);
+ if i=smallint(dubtmpl) then begin i:=abs(StrTemplates^[proto ][0 ,aType]);
+ if i=smallint(dubtmpl) then begin i:=abs(StrTemplates^[DefaultTemplate][ProtoStatus,aType]);
+ if i=smallint(dubtmpl) then i:=abs(StrTemplates^[DefaultTemplate][0 ,aType]); end; end;
+ if i=smallint(dubtmpl) then
+ i:=0;
+
+ result:=GetTmplString(ABS(i)); //normalize
+end;
+
+function SetTemplateStr(aStr:PWideChar;aType:tTemplateType;proto:cardinal=0;
+ ProtoStatus:integer=0):integer;
+var
+ tmpl:pStrTemplate;
+ tmp,tmp1:smallint;
+begin
+ tmpl:=@StrTemplates^[proto];
+
+ if (aStr=nil) or (aStr^=#0) then
+ result:=0
+ else
+ result:=AddString(aStr);
+
+ tmp1:=result;
+ tmp:=tmpl^[0,aType];
+ if tmp1=tmp then
+ tmp1:=smallint(dubtmpl)
+ else if tmp=smallint(dubtmpl) then
+ begin
+ if tmp1=tmpl^[0,aType] then
+ tmp1:=smallint(dubtmpl);
+ end;
+ tmpl^[ProtoStatus,aType]:=tmp1;
+end;
+
+procedure CreateTemplates;
+var
+ i:integer;
+begin
+ NumTemplates:=GetNumProto;
+ // Size in bytes
+ i:=SizeOf(tStrTemplate)*(NumTemplates+1);
+ mGetMem(StrTemplates,i);
+ // size in words
+ FillWord(StrTemplates^,i div 2,dubtmpl);
+ LoadTemplates;
+end;
+
+procedure FreeTemplates;
+begin
+ mFreeMem(StrTemplates);
+ while NumString>0 do
+ begin
+ mFreeMem(strings^[NumString].text);
+ dec(NumString);
+ end;
+ mFreeMem(strings);
+end;
+
+function GetMacros(TmplType:tTemplateType;proto:integer):pWideChar;
+var
+ r:PWideChar;
+ status:integer;
+begin
+ if proto=0 then
+ r:=GetTemplateStr(TmplType,0,0)
+ else
+ begin
+ status:=GetProtoStatusNum(proto);
+ if IsTemplateActive(TmplType,proto,status) then
+ r:=GetTemplateStr(TmplType,proto,status)
+ else
+ begin
+ result:=pWideChar(-1);
+ exit;
+ end;
+ end;
+
+ if r=nil then
+ result:=nil
+ else
+ result:=pWideChar(CallService(MS_WAT_REPLACETEXT,0,lparam(r)));
+end;
+
+end.
diff --git a/plugins/Watrack/templates/i_expkey.inc b/plugins/Watrack/templates/i_expkey.inc
new file mode 100644
index 0000000000..f6594acebb
--- /dev/null
+++ b/plugins/Watrack/templates/i_expkey.inc
@@ -0,0 +1,34 @@
+{main hotkey code}
+const
+ HKN_EXPORT:PAnsiChar = 'WAT_Export';
+
+function ExportProc(wParam:WPARAM;lParam:LPARAM):int_ptr; cdecl;
+var
+ p:pWideChar;
+begin
+ result:=0;
+ if DisablePlugin<>dsPermanent then
+ begin
+ p:=pointer(CallService(MS_WAT_REPLACETEXT,0,tlparam(ExportText)));
+ SendString(0,p);
+ mFreeMem(p);
+ end;
+end;
+
+procedure reginshotkey;
+var
+ hkrec:HOTKEYDESC;
+begin
+ FillChar(hkrec,SizeOf(hkrec),0);
+ with hkrec do
+ begin
+ cbSize :=HOTKEYDESC_SIZE_V1;
+ pszName :=HKN_EXPORT;
+ pszDescription.a:='WATrack data insert hotkey';
+ pszSection.a :=PluginName;
+ pszService :=MS_WAT_EXPORT;
+ DefHotKey :=((HOTKEYF_ALT or HOTKEYF_SHIFT) shl 8) or VK_F7;
+// lParam :=0;
+ end;
+ CallService(MS_HOTKEY_REGISTER,0,lparam(@hkrec));
+end;
diff --git a/plugins/Watrack/templates/i_macro.inc b/plugins/Watrack/templates/i_macro.inc
new file mode 100644
index 0000000000..68c0be6e33
--- /dev/null
+++ b/plugins/Watrack/templates/i_macro.inc
@@ -0,0 +1,149 @@
+{Macro help dialog}
+
+procedure SaveAliases;
+var
+ buf:array [0..31] of AnsiChar;
+ i:integer;
+ p:PAnsiChar;
+begin
+ p:=StrCopyE(buf,'alias/');
+ for i:=0 to numvars-1 do
+ begin
+// if vars[i].alias<>nil then
+ DBWriteUnicode(0,PluginShort,IntToStr(p,i),vars[i].alias);
+ end;
+end;
+
+procedure LoadAliases;
+var
+ buf:array [0..31] of AnsiChar;
+ i:integer;
+ p:PAnsiChar;
+begin
+ p:=StrCopyE(buf,'alias/');
+ for i:=0 to numvars-1 do
+ vars[i].alias:=DBReadUnicode(0,PluginShort,IntToStr(p,i),nil);
+end;
+
+procedure FreeAliases;
+var
+ i:integer;
+begin
+ for i:=0 to numvars-1 do
+ mFreeMem(vars[i].alias);
+end;
+
+function MacroHelpDlg(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):integer; stdcall;
+const
+ Changed:bool=false;
+var
+ i:integer;
+ itemw:LV_ITEMW;
+ lvc:LV_COLUMN;
+ wnd:hwnd;
+ ws:PWideChar;
+ s:pAnsiChar;
+ rc:TRECT;
+begin
+ result:=0;
+ case hMessage of
+
+ WM_INITDIALOG: begin
+ FillChar(itemw,SizeOf(itemw),0);
+ FillChar(lvc ,SizeOf(lvc) ,0);
+ wnd:=GetDlgItem(Dialog,IDC_MACROHELP);
+ SendMessage(wnd,LVM_SETUNICODEFORMAT,1,0);
+ lvc.mask:=LVCF_FMT;
+ lvc.fmt :=LVCFMT_LEFT;
+ ListView_InsertColumn(wnd,0,lvc);
+ itemw.mask:=LVIF_TEXT;
+ for i:=0 to numvars-1 do
+ begin
+ itemw.iItem:=i;
+ ws:=vars[i].alias;
+ if ws=nil then
+ ws:=vars[i].name;
+ itemw.pszText:=ws;
+ SendMessageW(wnd,LVM_INSERTITEMW,0,tlparam(@itemw));
+ end;
+ ListView_SetColumnWidth(wnd,0,LVSCW_AUTOSIZE);
+ ListView_InsertColumn(wnd,1,lvc);
+ itemw.iSubItem:=1;
+ s:=nil;
+ for i:=0 to numvars-1 do
+ begin
+ itemw.iItem:=i;
+ if vars[i].help<>nil then
+ s:=vars[i].help;
+ itemw.pszText:=TranslateA2W(s);
+ SendMessageW(wnd,LVM_SETITEMTEXTW,i,tlparam(@itemw));
+ mFreeMem(itemw.pszText);
+ end;
+ ListView_SetColumnWidth(wnd,1,LVSCW_AUTOSIZE);
+ result:=1;
+ Changed:=false;
+ TranslateDialogDefault(Dialog);
+ end;
+
+ WM_SIZE: begin
+ GetClientRect(Dialog,rc);
+ InflateRect(rc,-8,-8);
+ MoveWindow(GetDlgItem(Dialog,IDC_MACROHELP),
+ rc.left,rc.top,rc.right-rc.left,rc.bottom-rc.top,true);
+ end;
+
+ WM_COMMAND: begin
+ if (wParam shr 16)=BN_CLICKED then
+ case loword(wParam) of
+ IDOK, IDCANCEL: DestroyWindow(Dialog);
+ end;
+ end;
+
+ WM_DESTROY: begin
+ if Changed then
+ begin
+ SaveAliases;
+ Changed:=false;
+ RegisterVariables;
+ end;
+ end;
+
+ WM_NOTIFY: begin
+ if wParam=IDC_MACROHELP then
+ begin
+ case integer(PNMHdr(lParam)^.code) of
+ LVN_ENDLABELEDITW: begin
+ with PLVDISPINFO(lParam)^ do
+ begin
+ if item.pszText<>nil then
+ begin
+ item.mask:=LVIF_TEXT;
+ if pWideChar(item.pszText)^=#0 then
+ pWideChar(item.pszText):=vars[item.iItem].name;
+ SendMessageW(hdr.hWndFrom,LVM_SETITEMW,0,tlparam(@item));
+ mFreeMem(vars[item.iItem].alias);
+ StrDupW(vars[item.iItem].alias,pWideChar(item.pszText));
+ result:=1;
+ end;
+ Changed:=true;
+ end;
+ end;
+
+ NM_DBLCLK: begin
+ if PNMListView(lParam)^.iItem>=0 then
+ begin
+ SendMessage(PNMHdr(lParam)^.hWndFrom,LVM_EDITLABEL,
+ PNMListView(lParam)^.iItem,0);
+ end;
+ end;
+ end;
+ end;
+ end;
+
+ end;
+end;
+
+function WATMacroHelp(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+begin
+ result:=CreateDialogParamW(hInstance,'MACRO',wParam,@MacroHelpDlg,0);
+end;
diff --git a/plugins/Watrack/templates/i_opt_it.inc b/plugins/Watrack/templates/i_opt_it.inc
new file mode 100644
index 0000000000..a9ce27ebea
--- /dev/null
+++ b/plugins/Watrack/templates/i_opt_it.inc
@@ -0,0 +1,50 @@
+{}
+const
+ opt_LoCaseType:PAnsiChar = 'locase';
+ opt_FSPrec :PAnsiChar = 'precision';
+ opt_FSizePost :PAnsiChar = 'fsizepost';
+ opt_FSizeMode :PAnsiChar = 'fsizemode';
+ opt_WriteCBR :PAnsiChar = 'writecbr';
+ opt_ReplaceSpc:PAnsiChar = 'replacespc';
+ opt_PlayerCaps:PAnsiChar = 'playercaps';
+ opt_ExportText:PAnsiChar = 'exporttext';
+
+ opt_export :PAnsiChar = 'template/export';
+ spref = 'strings/';
+
+procedure LoadOpt;
+var
+ setting:array [0..63] of AnsiChar;
+begin
+ PlayerCaps :=DBReadByte (0,PluginShort,opt_PlayerCaps,0);
+ LoCaseType :=DBReadByte (0,PluginShort,opt_LoCaseType,BST_UNCHECKED);
+ ReplaceSpc :=DBReadByte (0,PluginShort,opt_ReplaceSpc,BST_CHECKED);
+ FSPrecision:=DBReadByte (0,PluginShort,opt_FSPrec ,0);
+ FSizePost :=DBReadByte (0,PluginShort,opt_FSizePost ,0);
+ FSizeMode :=DBReadDWord (0,PluginShort,opt_FSizeMode ,1);
+ WriteCBR :=DBReadByte (0,PluginShort,opt_WriteCBR ,0);
+ if DBGetSettingType(0,PluginShort,opt_ExportText)=DBVT_DELETED then
+ begin
+ IntToStr(StrCopyE(setting,spref),DBReadWord(0,PluginShort,opt_export,3));
+ ExportText:=DBReadUnicode(0,PluginShort,setting,nil);
+ end
+ else
+ ExportText:=DBReadUnicode(0,PluginShort,opt_ExportText);
+end;
+
+procedure SaveOpt;
+begin
+ DBWriteByte (0,PluginShort,opt_PlayerCaps,PlayerCaps);
+ DBWriteByte (0,PluginShort,opt_LoCaseType,LoCaseType);
+ DBWriteByte (0,PluginShort,opt_ReplaceSpc,ReplaceSpc);
+ DBWriteByte (0,PluginShort,opt_FSPrec ,FSPrecision);
+ DBWriteByte (0,PluginShort,opt_FSizePost ,FSizePost);
+ DBWriteDWord (0,PluginShort,opt_FSizeMode ,FSizeMode);
+ DBWriteByte (0,PluginShort,opt_WriteCBR ,WriteCBR);
+ DBWriteUnicode(0,PluginShort,opt_ExportText,ExportText);
+end;
+
+procedure FreeOpt;
+begin
+ mFreeMem(ExportText);
+end;
diff --git a/plugins/Watrack/templates/i_text.inc b/plugins/Watrack/templates/i_text.inc
new file mode 100644
index 0000000000..fa0c966728
--- /dev/null
+++ b/plugins/Watrack/templates/i_text.inc
@@ -0,0 +1,135 @@
+{}
+procedure Replace(dst:pWideChar;macro:integer;value:PWideChar);
+var
+ buf:array [0..63] of WideChar;
+ pc:pWideChar;
+begin
+ buf[0]:='%';
+ pc:=vars[macro].alias;
+ if pc=nil then
+ pc:=vars[macro].name;
+ StrCopyW(buf+1,pc);
+ pc:=StrEndW(buf);
+ pc^:='%';
+ (pc+1)^:=#0;
+ StrReplaceW(dst,buf,value);
+end;
+
+function ReplaceAll(s:PWideChar):pWideChar;
+var
+ tmp:integer;
+ pp,p:pWideChar;
+ ws:array [0..127] of WideChar;
+ ls:pWideChar;
+ i:integer;
+ tmpstr:pWideChar;
+ Info:pSongInfo;
+begin
+ Info:=pointer(CallService(MS_WAT_RETURNGLOBAL,0,0));
+ mGetMem(ls,32768);
+ StrCopyW(ls,s);
+ StrReplaceW(ls,'{tab}',#9);
+
+ StrCopyW(ws,Info^.player);
+ case PlayerCaps of
+ 1: LowerCase(ws);
+ 2: UpperCase(ws);
+ end;
+ Replace(ls,mn_player,ws);
+ Replace(ls,mn_file ,Info^.mfile);
+ Replace(ls,mn_year ,Info^.year);
+ Replace(ls,mn_genre ,Info^.genre);
+ GetExt(Info^.mfile,ws);
+ if LoCaseType=BST_CHECKED then
+ LowerCase(ws)
+ else
+ UpperCase(ws);
+ Replace(ls,mn_type ,ws);
+ Replace(ls,mn_track,IntToStr(ws,Info^.track));
+// codec
+ ws[0]:=WideChar( Info^.codec and $FF);
+ ws[1]:=WideChar((Info^.codec shr 8) and $FF);
+ ws[2]:=WideChar((Info^.codec shr 16) and $FF);
+ ws[3]:=WideChar((Info^.codec shr 24) and $FF);
+ ws[4]:=#0;
+ //fps
+ IntToStr(ws,Info^.fps div 100);
+ i:=0;
+ repeat
+ inc(i);
+ until ws[i]=#0;
+ ws[i]:='.';
+ IntToStr(pWideChar(@ws[i+1]),Info^.fps mod 100);
+ Replace(ls,mn_fps ,ws);
+ Replace(ls,mn_txtver ,Info^.txtver);
+ Replace(ls,mn_height ,IntToStr(ws,Info^.height));
+ Replace(ls,mn_width ,IntToStr(ws,Info^.width));
+ Replace(ls,mn_kbps ,IntToStr(ws,Info^.kbps));
+ Replace(ls,mn_bitrate,ws);
+ if Info^.vbr<>0 then
+ p:=chVBR
+ else if WriteCBR=0 then
+ p:=nil
+ else
+ p:=chCBR;
+ Replace(ls,mn_vbr ,p);
+ Replace(ls,mn_khz ,IntToStr(ws,Info^.khz));
+ Replace(ls,mn_samplerate,ws);
+ Replace(ls,mn_channels ,IntToStr(ws,Info^.channels));
+ case Info^.channels of
+ 1: p:=chMono;
+ 2: p:=chStereo;
+ 5,6: p:=ch51;
+ end;
+ Replace(ls,mn_mono,p);
+ Replace(ls,mn_size,
+ IntToK(ws,Info^.fsize,FSizeMode,FSPrecision,FSizePost));
+ Replace(ls,mn_length,IntToTime(ws,Info^.total));
+ Replace(ls,mn_total ,ws);
+ case Info^.status of
+ WAT_MES_PLAYING: pp:=splPlaying;
+ WAT_MES_PAUSED : pp:=splPaused;
+ else
+ {WAT_MES_STOPPED:} pp:=splStopped;
+ end;
+ Replace(ls,mn_status,TranslateW(pp));
+ Replace(ls,mn_nstatus,pp);
+ Replace(ls,mn_lyric ,Info^.lyric);
+ Replace(ls,mn_cover ,Info^.cover);
+ Replace(ls,mn_volume,IntToStr(ws,loword(Info^.volume)));
+
+ mGetMem(tmpstr,32767);
+
+ StrCopyW(tmpstr,Info^.artist);
+ if ReplaceSpc=BST_CHECKED then CharReplaceW(tmpstr ,'_',' ');
+ Replace(ls,mn_artist,tmpstr);
+
+ StrCopyW(tmpstr,Info^.title);
+ if ReplaceSpc=BST_CHECKED then CharReplaceW(tmpstr ,'_',' ');
+ Replace(ls,mn_title,tmpstr);
+
+ StrCopyW(tmpstr,Info^.album);
+ if ReplaceSpc=BST_CHECKED then CharReplaceW(tmpstr ,'_',' ');
+ Replace(ls,mn_album,tmpstr);
+
+ StrCopyW(tmpstr,Info^.comment);
+ if ReplaceSpc=BST_CHECKED then CharReplaceW(tmpstr ,'_',' ');
+ Replace(ls,mn_comment,tmpstr);
+
+ StrCopyW(tmpstr,Info^.wndtext);
+ if ReplaceSpc=BST_CHECKED then CharReplaceW(tmpstr ,'_',' ');
+ Replace(ls,mn_wndtext,tmpstr);
+
+ mFreeMem(tmpstr);
+
+ Replace(ls,mn_version,IntToHex(ws,Info^.plyver));
+ Replace(ls,mn_time ,IntToTime(ws,Info^.time));
+ if Info^.total>0 then
+ tmp:=(Info^.time*100) div Info^.total
+ else
+ tmp:=0;
+ Replace(ls,mn_percent,IntToStr(ws,tmp));
+ Replace(ls,mn_playerhome,Info^.url);
+
+ result:=ls;
+end;
diff --git a/plugins/Watrack/templates/i_tmpl_dlg.inc b/plugins/Watrack/templates/i_tmpl_dlg.inc
new file mode 100644
index 0000000000..185f91d608
--- /dev/null
+++ b/plugins/Watrack/templates/i_tmpl_dlg.inc
@@ -0,0 +1,117 @@
+{}
+function DlgProcOptions(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):integer; stdcall;
+begin
+ result:=0;
+ case hMessage of
+ WM_INITDIALOG: begin
+ TranslateDialogDefault(Dialog);
+
+ if not isVarsInstalled then
+ ShowWindow(GetDlgItem(Dialog,IDC_VAR_HELP),SW_HIDE)
+ else
+ SendDlgItemMessage(Dialog,IDC_VAR_HELP,BM_SETIMAGE,IMAGE_ICON,
+ CallService(MS_VARS_GETSKINITEM,0,VSI_HELPICON));
+
+ SendDlgItemMessage(Dialog,IDC_MACRO_HELP,BM_SETIMAGE,IMAGE_ICON,
+ CallService(MS_SKIN_LOADICON,SKINICON_OTHER_HELP,0));
+
+ MakeHint(Dialog,IDC_REPLACESPC,
+ 'Replaces "_" (underscores) globally in pasted os status text,'+
+ ' sometimes may be useful');
+ CheckDlgButton(Dialog,IDC_REPLACESPC,ReplaceSpc);
+
+ CheckDlgButton(Dialog,IDC_LOCASE,LoCaseType);
+
+ CheckDlgButton(Dialog,IDC_FSIZEBYTE,ord(FSizeMode=1));
+ CheckDlgButton(Dialog,IDC_FSIZEKILO,ord(FSizeMode=1024));
+ CheckDlgButton(Dialog,IDC_FSIZEMEGA,ord(FSizeMode=1024*1024));
+
+ SetDlgItemInt (Dialog,IDC_PRECISION,FSPrecision,false);
+ CheckDlgButton(Dialog,IDC_POSTNONE ,ord(FSizePost=0));
+ CheckDlgButton(Dialog,IDC_POSTSMALL,ord(FSizePost=1));
+ CheckDlgButton(Dialog,IDC_POSTMIX ,ord(FSizePost=2));
+ CheckDlgButton(Dialog,IDC_POSTLARGE,ord(FSizePost=3));
+
+ CheckDlgButton(Dialog,IDC_ALLCAP ,ord(PlayerCaps=2));
+ CheckDlgButton(Dialog,IDC_SMALLCAP,ord(PlayerCaps=1));
+ CheckDlgButton(Dialog,IDC_MIXCAP ,ord(PlayerCaps=0));
+
+ CheckDlgButton(Dialog,IDC_WRITECBR1,ord(WriteCBR=0));
+ CheckDlgButton(Dialog,IDC_WRITECBR2,ord(WriteCBR<>0));
+
+ SetDlgItemTextW(Dialog,IDC_EXPORT_TEXT,ExportText);
+ end;
+
+ WM_COMMAND: begin
+ if (wParam shr 16)=BN_CLICKED then
+ begin
+ case loword(wParam) of
+ IDC_VAR_HELP : ShowVarHelp (Dialog);
+ IDC_MACRO_HELP: CallService(MS_WAT_MACROHELP,Dialog,0);
+ IDC_ALLCAP: begin
+ CheckDlgButton(Dialog,IDC_ALLCAP ,BST_CHECKED);
+ CheckDlgButton(Dialog,IDC_SMALLCAP,BST_UNCHECKED);
+ CheckDlgButton(Dialog,IDC_MIXCAP ,BST_UNCHECKED);
+ end;
+ IDC_SMALLCAP: begin
+ CheckDlgButton(Dialog,IDC_ALLCAP ,BST_UNCHECKED);
+ CheckDlgButton(Dialog,IDC_SMALLCAP,BST_CHECKED);
+ CheckDlgButton(Dialog,IDC_MIXCAP ,BST_UNCHECKED);
+ end;
+ IDC_MIXCAP: begin
+ CheckDlgButton(Dialog,IDC_ALLCAP ,BST_UNCHECKED);
+ CheckDlgButton(Dialog,IDC_SMALLCAP,BST_UNCHECKED);
+ CheckDlgButton(Dialog,IDC_MIXCAP ,BST_CHECKED);
+ end;
+ end;
+ end;
+ if ((wParam shr 16)=EN_CHANGE) or ((wParam shr 16)=BN_CLICKED) then
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ result:=1;
+ end;
+
+ WM_NOTIFY: begin
+ if integer(PNMHdr(lParam)^.code)=PSN_APPLY then
+ begin
+ ReplaceSpc:=IsDlgButtonChecked(Dialog,IDC_REPLACESPC);
+ LoCaseType:=IsDlgButtonChecked(Dialog,IDC_LOCASE);
+ if IsDlgButtonChecked(Dialog,IDC_WRITECBR1)=BST_CHECKED then
+ WriteCBR:=0
+ else //if IsDlgButtonChecked(Dialog,IDC_WRITECBR2)=BST_CHECKED then
+ WriteCBR:=1;
+ if IsDlgButtonChecked(Dialog,IDC_FSIZEBYTE)=BST_CHECKED then
+ FSizeMode:=1
+ else if IsDlgButtonChecked(Dialog,IDC_FSIZEKILO)=BST_CHECKED then
+ FSizeMode:=1024
+ else// if IsDlgButtonChecked(Dialog,IDC_FSIZEMEGA)=BST_CHECKED then
+ FSizeMode:=1024*1024;
+
+ if IsDlgButtonChecked(Dialog,IDC_MIXCAP)=BST_CHECKED then
+ PlayerCaps:=0
+ else if IsDlgButtonChecked(Dialog,IDC_SMALLCAP)=BST_CHECKED then
+ PlayerCaps:=1
+ else// if IsDlgButtonChecked(Dialog,IDC_ALLCAP)=BST_CHECKED then
+ PlayerCaps:=2;
+
+ if IsDlgButtonChecked(Dialog,IDC_POSTNONE)=BST_CHECKED then
+ FSizePost:=0
+ else if IsDlgButtonChecked(Dialog,IDC_POSTSMALL)=BST_CHECKED then
+ FSizePost:=1
+ else if IsDlgButtonChecked(Dialog,IDC_POSTMIX)=BST_CHECKED then
+ FSizePost:=2
+ else// if IsDlgButtonChecked(Dialog,IDC_POSTLARGE)=BST_CHECKED then
+ FSizePost:=3;
+ FSPrecision:=GetDlgItemInt(Dialog,IDC_PRECISION,pbool(nil)^,false);
+ if FSPrecision>3 then
+ FSPrecision:=3;
+
+ mFreeMem(ExportText);
+ ExportText:=GetDlgText(Dialog,IDC_EXPORT_TEXT);
+
+ SaveOpt;
+ end;
+ end;
+ else
+ {result:=}DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+end;
diff --git a/plugins/Watrack/templates/i_tmpl_rc.inc b/plugins/Watrack/templates/i_tmpl_rc.inc
new file mode 100644
index 0000000000..5b444a45be
--- /dev/null
+++ b/plugins/Watrack/templates/i_tmpl_rc.inc
@@ -0,0 +1,21 @@
+const
+ IDC_PRECISION = 1025;
+ IDC_FSIZEBYTE = 1026;
+ IDC_FSIZEKILO = 1027;
+ IDC_FSIZEMEGA = 1028;
+ IDC_POSTNONE = 1029;
+ IDC_POSTSMALL = 1030;
+ IDC_POSTMIX = 1031;
+ IDC_POSTLARGE = 1032;
+ IDC_LOCASE = 1033;
+ IDC_WRITECBR1 = 1034;
+ IDC_WRITECBR2 = 1035;
+ IDC_ALLCAP = 1036;
+ IDC_MIXCAP = 1037;
+ IDC_SMALLCAP = 1038;
+ IDC_EXPORT_TEXT = 1039;
+ IDC_REPLACESPC = 1040;
+ IDC_MACRO_HELP = IDHELP;//1041;
+ IDC_VAR_HELP = 1042;
+
+ IDC_MACROHELP = 1025;
diff --git a/plugins/Watrack/templates/i_variables.inc b/plugins/Watrack/templates/i_variables.inc
new file mode 100644
index 0000000000..a43c77b8c2
--- /dev/null
+++ b/plugins/Watrack/templates/i_variables.inc
@@ -0,0 +1,185 @@
+{Variables support}
+function GetField(ai:PARGUMENTSINFO):int_ptr; cdecl;
+var
+ i,j:integer;
+ res,ws:pWideChar;
+ s:array [0..31] of WideChar;
+ rs:boolean;
+ si:pSongInfo;
+begin
+ i:=0;
+ repeat
+ ws:=vars[i].alias;
+ if ws=nil then
+ ws:=vars[i].name;
+ if lstrcmpiw(PWideChar(ai^.argv^),ws)=0 then
+ break;
+ inc(i);
+ until i=numvars;
+ ws:=nil;
+ j:=-1;
+ rs:=true;
+ si:=pointer(CallService(MS_WAT_RETURNGLOBAL,0,0));
+ case i of
+ mn_wndtext: ws:=si^.wndtext;
+ mn_artist : ws:=si^.artist;
+ mn_title : ws:=si^.title;
+ mn_album : ws:=si^.album;
+ mn_genre : ws:=si^.genre;
+ mn_file : begin ws:=si^.mfile; rs:=false; end;
+ mn_year : ws:=si^.year;
+ mn_comment: ws:=si^.comment;
+ mn_player : begin
+ StrCopyW(s,si^.player);
+ case PlayerCaps of
+ 1: LowerCase(s);
+ 2: UpperCase(s);
+ end;
+ ws:=@s;
+ end;
+ mn_lyric : ws:=si^.lyric;
+ mn_cover : ws:=si^.cover;
+ mn_txtver : ws:=si^.txtver;
+ mn_type: begin
+ GetExt(si^.mfile,s);
+ if LoCaseType=BST_CHECKED then
+ LowerCase(s);
+// else
+// UpperCase(s);
+ ws:=@s;
+ end;
+ mn_size: begin
+ IntToK(s,si^.fsize,FSizeMode,FSPrecision,FSizePost);
+ ws:=@s;
+ end;
+ mn_fps: begin
+ IntToStr(s,si^.fps div 100);
+ ws:=@s;
+ while ws^<>#0 do inc(ws);
+ ws^:='.';
+ IntToStr(ws+1,si^.fps mod 100);
+ ws:=@s;
+ end;
+ mn_codec: begin
+ s[0]:=WideChar( si^.codec and $FF);
+ s[1]:=WideChar((si^.codec shr 8) and $FF);
+ s[2]:=WideChar((si^.codec shr 16) and $FF);
+ s[3]:=WideChar((si^.codec shr 24) and $FF);
+ s[4]:=#0;
+ ws:=@s;
+ end;
+ mn_vbr: if si^.vbr<>0 then
+ ws:=chVBR
+ else if WriteCBR<>0 then
+ ws:=chCBR;
+ mn_status: case si^.status of
+ WAT_MES_STOPPED: ws:=TranslateW(splStopped);
+ WAT_MES_PLAYING: ws:=TranslateW(splPlaying);
+ WAT_MES_PAUSED : ws:=TranslateW(splPaused);
+ end;
+ mn_nstatus: case si^.status of
+ WAT_MES_STOPPED: ws:=splStopped;
+ WAT_MES_PLAYING: ws:=splPlaying;
+ WAT_MES_PAUSED : ws:=splPaused;
+ end;
+ mn_mono: begin
+ case si^.channels of
+ 1: ws:=chMono;
+ 2: ws:=chStereo;
+ 5,6: ws:=ch51;
+ end;
+ end;
+ mn_playerhome: ws:=si^.url;
+ else
+ begin
+ case i of
+ mn_volume : j:=loword(si^.volume);
+ mn_width : j:=si^.width;
+ mn_height : j:=si^.height;
+ mn_kbps,
+ mn_bitrate : j:=si^.kbps;
+ mn_khz,
+ mn_samplerate: j:=si^.khz;
+ mn_channels : j:=si^.channels;
+ mn_track : j:=si^.track;
+ mn_percent: begin
+ if si^.total>0 then
+ j:=(si^.time*100) div si^.total
+ else
+ j:=0;
+ end;
+ else
+ begin
+ case i of
+ mn_total,
+ mn_length : IntToTime(s,si^.total);
+ mn_time : IntToTime(s,si^.time);
+ mn_version: IntToHex (s,si^.plyver);
+ else
+ result:=0;
+ exit;
+ end;
+ ws:=@s;
+ end;
+ end;
+ end;
+ end;
+ if (ws=nil) and (j>=0) then
+ begin
+ IntToStr(s,j);
+ ws:=@s;
+ end;
+
+ StrDupW(ws,ws);
+ If rs and (ReplaceSpc=BST_CHECKED) then
+ CharReplaceW(ws,'_',' ');
+ i:=StrLenW(ws);
+ mGetMem(res,(i+1)*SizeOf(WideChar));
+ if ws<>nil then
+ begin
+ StrCopyW(res,ws);
+ mFreeMem(ws);
+ end
+ else
+ res[0]:=#0;
+ result:=int_ptr(res);
+end;
+
+function FreeField(szReturn:PAnsiChar):int; cdecl;
+begin
+ mFreeMem(szReturn);
+ result:=1;
+end;
+
+procedure RegisterVariables;
+const
+ Prefix:PAnsiChar = 'WATrack'#9;
+var
+ rt:TTOKENREGISTER;
+ i,j:integer;
+ s:array [0..127] of AnsiChar;
+ p:pvar;
+begin
+ if not isVarsInstalled then
+ exit;
+
+ rt.cbSize :=SizeOf(rt);
+ rt.memType :=TR_MEM_OWNER;
+ rt.flags :=TRF_FIELD or TRF_CLEANUP or
+ TRF_UNICODE or TRF_PARSEFUNC or TRF_CLEANUPFUNC;
+ rt.szService :=@GetField;
+ rt.szCleanupService:=@FreeField;
+ j:=StrLen(Prefix);
+ move(Prefix^,s,j);
+ rt.szHelpText:=@s;
+ for i:=0 to numvars-1 do
+ begin
+ p:=@vars[i];
+ rt.szTokenString.w:=p.alias;
+ if rt.szTokenString.w=nil then
+ rt.szTokenString.w:=p.name;
+ if p.help<>nil then
+ StrCopy(s+j,p.help);
+ CallService(MS_VARS_REGISTERTOKEN,0,lparam(@rt));
+ end;
+end;
diff --git a/plugins/Watrack/templates/templates.pas b/plugins/Watrack/templates/templates.pas
new file mode 100644
index 0000000000..ec56580852
--- /dev/null
+++ b/plugins/Watrack/templates/templates.pas
@@ -0,0 +1,113 @@
+unit templates;
+{$include compilers.inc}
+interface
+{$Resource templates.res}
+implementation
+
+uses
+ messages,windows,commctrl,
+ common,syswin,wrapper,
+ m_api,dbsettings,mirutils,
+ wat_api,global,macros;
+
+const
+ splStopped:PWideChar = 'stopped';
+ splPlaying:PWideChar = 'playing';
+ splPaused :PWideChar = 'paused';
+ chMono :PWideChar = 'mono';
+ chStereo :PWideChar = 'stereo';
+ ch51 :PWideChar = '5.1';
+ chVBR :PWideChar = 'VBR';
+ chCBR :PWideChar = 'CBR';
+
+const
+ LoCaseType :integer=0;
+ WriteCBR :integer=0;
+ ReplaceSpc :integer=0;
+ FSizeMode :integer=1024*1024;
+ FSizePost :integer=2;
+ FSPrecision :integer=2;
+ PlayerCaps :integer=0;
+ ExportText:pWideChar=nil;
+
+{$include i_tmpl_rc.inc}
+{$include i_variables.inc}
+{$include i_macro.inc}
+{$include i_text.inc}
+{$include i_opt_it.inc}
+{$include i_tmpl_dlg.inc}
+{$include i_expkey.inc}
+
+function WATReplaceText(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+var
+ p:pWideChar;
+begin
+ if (lParam<>0) and (pWideChar(lParam)^<>#0) then
+ begin
+ if isVarsInstalled then
+ result:=int_ptr(ParseVarString(pWideChar(lParam)))
+ else
+ result:=int_ptr(ReplaceAll(pWideChar(lParam)));
+ if (result<>0) and (pWideChar(result)^=#0) then
+ begin
+ p:=PWideChar(result);
+ mFreeMem(p);
+ result:=0;
+ end;
+ end
+ else
+ result:=0;
+end;
+
+// ------------ base interface functions -------------
+
+function AddOptionsPage(var tmpl:pAnsiChar;var proc:pointer;var name:PAnsiChar):integer;
+begin
+ tmpl:='FORMAT';
+ proc:=@DlgProcOptions;
+ name:='Templates';
+ result:=0;
+end;
+
+var
+ hEXP,
+ hMacro,
+ hReplace:THANDLE;
+
+function InitProc(aGetStatus:boolean=false):integer;
+begin
+ result:=1;
+ hEXP :=CreateServiceFunction(MS_WAT_EXPORT ,@ExportProc);
+ hReplace:=CreateServiceFunction(MS_WAT_REPLACETEXT,@WATReplaceText);
+ hMacro :=CreateServiceFunction(MS_WAT_MACROHELP ,@WATMacroHelp);
+ LoadOpt;
+ LoadAliases;
+ RegisterVariables;
+ reginshotkey;
+end;
+
+procedure DeInitProc(aSetDisable:boolean);
+begin
+ DestroyServiceFunction(hReplace);
+ DestroyServiceFunction(hEXP);
+ DestroyServiceFunction(hMacro);
+ FreeAliases;
+ FreeOpt;
+end;
+
+var
+ Tmpl:twModule;
+
+procedure Init;
+begin
+ Tmpl.Next :=ModuleLink;
+ Tmpl.Init :=@InitProc;
+ Tmpl.DeInit :=@DeInitProc;
+ Tmpl.AddOption :=@AddOptionsPage;
+ Tmpl.ModuleName:=nil;
+ ModuleLink :=@Tmpl;
+end;
+
+begin
+ Init;
+end.
diff --git a/plugins/Watrack/templates/templates.rc b/plugins/Watrack/templates/templates.rc
new file mode 100644
index 0000000000..f7e8ff1f20
--- /dev/null
+++ b/plugins/Watrack/templates/templates.rc
@@ -0,0 +1,51 @@
+#include "i_tmpl_rc.inc"
+
+LANGUAGE 0,0
+
+FORMAT DIALOGEX 0, 0, 304, 226, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ GROUPBOX "Options", -1, 2, 2, 188, 161, WS_TABSTOP
+ AUTOCHECKBOX "Replace underlines with spaces", IDC_REPLACESPC, 6, 12, 182, 14, BS_VCENTER | BS_MULTILINE | BS_NOTIFY
+
+ EDITTEXT IDC_PRECISION, 192, 60, 20, 12, ES_RIGHT | ES_NUMBER
+ GROUPBOX "File size", -1, 192, 2, 64, 54
+ AUTORADIOBUTTON "Bytes" , IDC_FSIZEBYTE, 196, 14, 50, 10, NOT WS_TABSTOP
+ AUTORADIOBUTTON "Kilobytes", IDC_FSIZEKILO, 196, 28, 50, 10, NOT WS_TABSTOP
+ AUTORADIOBUTTON "Megabytes", IDC_FSIZEMEGA, 196, 42, 50, 10, NOT WS_TABSTOP
+ LTEXT "Precision", -1, 216, 62, 84, 12
+ GROUPBOX "Postfix", -1, 258, 2, 42, 54
+ AUTORADIOBUTTON "none", IDC_POSTNONE , 262, 12, 30, 10, NOT WS_TABSTOP
+ AUTORADIOBUTTON "kb" , IDC_POSTSMALL, 262, 23, 30, 10, NOT WS_TABSTOP
+ AUTORADIOBUTTON "Kb" , IDC_POSTMIX , 262, 34, 30, 10, NOT WS_TABSTOP
+ AUTORADIOBUTTON "KB" , IDC_POSTLARGE, 262, 45, 30, 10, NOT WS_TABSTOP
+// CONTROL "", -1, "STATIC", SS_ETCHEDHORZ, 192, 77, 108, 2
+ GROUPBOX "VBR macro", -1, 192, 77, 108, 34
+ AUTORADIOBUTTON "VBR or empty", IDC_WRITECBR1, 198, 87, 96, 10, NOT WS_TABSTOP
+ AUTORADIOBUTTON "VBR or CBR" , IDC_WRITECBR2, 198, 99, 96, 10, NOT WS_TABSTOP
+ GROUPBOX "Player name letters", -1, 192, 115, 108, 48
+ RADIOBUTTON "All uppercase", IDC_ALLCAP , 198, 127, 96, 10, NOT WS_TABSTOP
+ RADIOBUTTON "Do not change", IDC_MIXCAP , 198, 139, 96, 10, NOT WS_TABSTOP
+ RADIOBUTTON "All lowercase", IDC_SMALLCAP, 198, 151, 96, 10, NOT WS_TABSTOP
+
+ AUTOCHECKBOX "lowercase %type%", IDC_LOCASE, 193,164, 108, 14
+
+ LTEXT "Export text template",-1, 6, 164 ,142, 14, SS_CENTERIMAGE
+ EDITTEXT IDC_EXPORT_TEXT, 4, 180, 296, 42,
+ ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
+// PUSHBUTTON "V", IDC_VAR_HELP ,154,163,16,16
+// PUSHBUTTON "M", IDC_MACRO_HELP,172,163,16,16
+ CONTROL "V" ,IDC_VAR_HELP ,"MButtonClass",WS_TABSTOP,154,163,16,16,$18000000
+ CONTROL "M" ,IDC_MACRO_HELP ,"MButtonClass",WS_TABSTOP,172,163,16,16,$18000000
+}
+
+MACRO DIALOGEX 0, 0, 240, 176, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_SIZEBOX
+EXSTYLE WS_EX_CONTROLPARENT
+CAPTION "WATrack Macro Info"
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ CONTROL "", IDC_MACROHELP, "SysListView32", WS_BORDER | WS_TABSTOP | LVS_NOCOLUMNHEADER | LVS_EDITLABELS | LVS_SHOWSELALWAYS | LVS_SINGLESEL | LVS_REPORT, 6, 6, 228, 164, WS_EX_CONTROLPARENT
+}
diff --git a/plugins/Watrack/templates/templates.res b/plugins/Watrack/templates/templates.res
new file mode 100644
index 0000000000..d23e804915
--- /dev/null
+++ b/plugins/Watrack/templates/templates.res
Binary files differ
diff --git a/plugins/Watrack/wat_api.pas b/plugins/Watrack/wat_api.pas
new file mode 100644
index 0000000000..52fd0f2650
--- /dev/null
+++ b/plugins/Watrack/wat_api.pas
@@ -0,0 +1,183 @@
+unit wat_api;
+
+interface
+
+uses windows;
+
+{$Include m_music.inc}
+
+function GenreName(idx:cardinal):pWideChar;
+
+implementation
+
+uses common;
+
+const
+ MAX_MUSIC_GENRES = 148;
+
+Genres:array [0..MAX_MUSIC_GENRES-1] of PWideChar = (
+{0} 'Blues',
+{1} 'Classic Rock',
+{2} 'Country',
+{3} 'Dance',
+{4} 'Disco',
+{5} 'Funk',
+{6} 'Grunge',
+{7} 'Hip-Hop',
+{8} 'Jazz',
+{9} 'Metal',
+{10} 'New Age',
+{11} 'Oldies',
+{12} 'Other',
+{13} 'Pop',
+{14} 'R&B',
+{15} 'Rap',
+{16} 'Reggae',
+{17} 'Rock',
+{18} 'Techno',
+{19} 'Industrial',
+{20} 'Alternative',
+{21} 'Ska',
+{22} 'Death Metal',
+{23} 'Pranks',
+{24} 'Soundtrack',
+{25} 'Euro-Techno',
+{26} 'Ambient',
+{27} 'Trip-Hop',
+{28} 'Vocal',
+{29} 'Jazz+Funk',
+{30} 'Fusion',
+{31} 'Trance',
+{32} 'Classical',
+{33} 'Instrumental',
+{34} 'Acid',
+{35} 'House',
+{36} 'Game',
+{37} 'Sound Clip',
+{38} 'Gospel',
+{39} 'Noise',
+{40} 'AlternRock',
+{41} 'Bass',
+{42} 'Soul',
+{43} 'Punk',
+{44} 'Space',
+{45} 'Meditative',
+{46} 'Instrumental Pop',
+{47} 'Instrumental Rock',
+{48} 'Ethnic',
+{49} 'Gothic',
+{50} 'Darkwave',
+{51} 'Techno-Industrial',
+{52} 'Electronic',
+{53} 'Pop-Folk',
+{54} 'Eurodance',
+{55} 'Dream',
+{56} 'Southern Rock',
+{57} 'Comedy',
+{58} 'Cult',
+{59} 'Gangsta',
+{60} 'Top 40',
+{61} 'Christian Rap',
+{62} 'Pop/Funk',
+{63} 'Jungle',
+{64} 'Native American',
+{65} 'Cabaret',
+{66} 'New Wave',
+{67} 'Psychadelic',
+{68} 'Rave',
+{69} 'Showtunes',
+{70} 'Trailer',
+{71} 'Lo-Fi',
+{72} 'Tribal',
+{73} 'Acid Punk',
+{74} 'Acid Jazz',
+{75} 'Polka',
+{76} 'Retro',
+{77} 'Musical',
+{78} 'Rock & Roll',
+{79} 'Hard Rock',
+{80} 'Folk',
+{81} 'Folk-Rock',
+{82} 'National Folk',
+{83} 'Swing',
+{84} 'Fast Fusion',
+{85} 'Bebob',
+{86} 'Latin',
+{87} 'Revival',
+{88} 'Celtic',
+{89} 'Bluegrass',
+{90} 'Avantgarde',
+{91} 'Gothic Rock',
+{92} 'Progressive Rock',
+{93} 'Psychedelic Rock',
+{94} 'Symphonic Rock',
+{95} 'Slow Rock',
+{96} 'Big Band',
+{97} 'Chorus',
+{98} 'Easy Listening',
+{99} 'Acoustic',
+{100} 'Humour',
+{101} 'Speech',
+{102} 'Chanson',
+{103} 'Opera',
+{104} 'Chamber Music',
+{105} 'Sonata',
+{106} 'Symphony',
+{107} 'Booty Brass',
+{108} 'Primus',
+{109} 'Porn Groove',
+{110} 'Satire',
+{111} 'Slow Jam',
+{112} 'Club',
+{113} 'Tango',
+{114} 'Samba',
+{115} 'Folklore',
+{116} 'Ballad',
+{117} 'Poweer Ballad',
+{118} 'Rhytmic Soul',
+{119} 'Freestyle',
+{120} 'Duet',
+{121} 'Punk Rock',
+{122} 'Drum Solo',
+{123} 'A Capela',
+{124} 'Euro-House',
+{125} 'Dance Hall',
+{126} 'Goa',
+{127} 'Drum & Bass',
+{128} 'Club-House',
+{129} 'Hardcore',
+{130} 'Terror',
+{131} 'Indie',
+{132} 'BritPop',
+{133} 'Negerpunk',
+{134} 'Polsk Punk',
+{135} 'Beat',
+{136} 'Christian Gangsta Rap',
+{137} 'Heavy Metal',
+{138} 'Black Metal',
+{139} 'Crossover',
+{140} 'Contemporary Christian',
+{141} 'Christian Rock',
+{142} 'Merengue',
+{143} 'Salsa',
+{144} 'Trash Metal',
+{145} 'Anime',
+{146} 'JPop',
+{147} 'Synthpop');
+
+function GenreName(idx:cardinal):pWideChar;
+begin
+ if idx<MAX_MUSIC_GENRES then
+ begin
+ StrDupW(result,Genres[idx]);
+{
+ mGetMem(result,64*SizeOf(WideChar));
+ LoadStringW(hInstance,idx,result,64);
+}
+// result:=Genres[idx];
+ end
+ else
+ result:=nil;
+end;
+
+end.
diff --git a/plugins/Watrack/waticons.inc b/plugins/Watrack/waticons.inc
new file mode 100644
index 0000000000..0c3c423f0e
--- /dev/null
+++ b/plugins/Watrack/waticons.inc
@@ -0,0 +1,35 @@
+const
+ IDI_PREV_NORMAL = 1;
+ IDI_PREV_HOVERED = 2;
+ IDI_PREV_PRESSED = 3;
+
+ IDI_PLAY_NORMAL = 4;
+ IDI_PLAY_HOVERED = 5;
+ IDI_PLAY_PRESSED = 6;
+
+ IDI_PAUSE_NORMAL = 7;
+ IDI_PAUSE_HOVERED = 8;
+ IDI_PAUSE_PRESSED = 9;
+
+ IDI_STOP_NORMAL = 10;
+ IDI_STOP_HOVERED = 11;
+ IDI_STOP_PRESSED = 12;
+
+ IDI_NEXT_NORMAL = 13;
+ IDI_NEXT_HOVERED = 14;
+ IDI_NEXT_PRESSED = 15;
+
+ IDI_VOLDN_NORMAL = 16;
+ IDI_VOLDN_HOVERED = 17;
+ IDI_VOLDN_PRESSED = 18;
+
+ IDI_VOLUP_NORMAL = 19;
+ IDI_VOLUP_HOVERED = 20;
+ IDI_VOLUP_PRESSED = 21;
+
+ IDI_SLIDER_NORMAL = 22;
+ IDI_SLIDER_HOVERED = 23;
+ IDI_SLIDER_PRESSED = 24;
+
+ IDI_PLUGIN_ENABLE = 100;
+ IDI_PLUGIN_DISABLE = 101;
diff --git a/plugins/Watrack/waticons.pas b/plugins/Watrack/waticons.pas
new file mode 100644
index 0000000000..5b61af18dd
--- /dev/null
+++ b/plugins/Watrack/waticons.pas
@@ -0,0 +1,202 @@
+unit WATIcons;
+interface
+
+uses wat_api;
+
+const // to not load icobuttons module
+ AST_NORMAL = 0;
+ AST_HOVERED = 1;
+ AST_PRESSED = 2;
+
+// main Enable/Disable icons
+const // name in icolib
+ IcoBtnEnable :PAnsiChar='WATrack_Enabled';
+ IcoBtnDisable:PAnsiChar='WATrack_Disabled';
+
+function RegisterIcons:boolean;
+
+// frame button icons
+function RegisterButtonIcons:boolean;
+function GetIcon(action:integer;stat:integer=AST_NORMAL):cardinal;
+function DoAction(action:integer):integer;
+function GetIconDescr(action:integer):pAnsiChar;
+{
+const
+ AST_NORMAL = 0;
+ AST_HOVERED = 1;
+ AST_PRESSED = 2;
+}
+implementation
+
+uses m_api,windows;
+
+{$include waticons.inc}
+
+const
+ ICOCtrlName = 'watrack_buttons.dll';
+
+const
+ IconsLoaded:bool = false;
+
+function DoAction(action:integer):integer;
+begin
+ result:=CallService(MS_WAT_PRESSBUTTON,action,0);
+end;
+
+function RegisterIcons:boolean;
+var
+ sid:TSKINICONDESC;
+ buf:array [0..511] of AnsiChar;
+ hIconDLL:THANDLE;
+begin
+ result:=true;
+ sid.szDefaultFile.a:='icons\'+ICOCtrlName;
+// ConvertFileName(sid.szDefaultFile.a,buf);
+ CallService(MS_UTILS_PATHTOABSOLUTE,wparam(sid.szDefaultFile),lparam(@buf));
+
+ hIconDLL:=LoadLibraryA(buf);
+ if hIconDLL=0 then // not found
+ begin
+ sid.szDefaultFile.a:='plugins\'+ICOCtrlName;
+// ConvertFileName(sid.szDefaultFile.a,buf);
+ CallService(MS_UTILS_PATHTOABSOLUTE,wparam(sid.szDefaultFile),lparam(@buf));
+ hIconDLL:=LoadLibraryA(buf);
+ end;
+
+ if hIconDLL=0 then
+ hIconDLL:=hInstance;
+
+ FillChar(sid,SizeOf(TSKINICONDESC),0);
+ sid.cbSize:=SizeOf(TSKINICONDESC);
+ sid.cx:=16;
+ sid.cy:=16;
+ sid.szSection.a:='WATrack';
+
+ sid.hDefaultIcon :=LoadImage(hIconDLL,
+ MAKEINTRESOURCE(IDI_PLUGIN_ENABLE),IMAGE_ICON,16,16,0);
+ sid.pszName :=IcoBtnEnable;
+ sid.szDescription.a:='Plugin Enabled';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+
+ sid.hDefaultIcon :=LoadImage(hIconDLL,
+ MAKEINTRESOURCE(IDI_PLUGIN_DISABLE),IMAGE_ICON,16,16,0);
+ sid.pszName :=IcoBtnDisable;
+ sid.szDescription.a:='Plugin Disabled';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+
+ if hIconDLL<>hInstance then
+ FreeLibrary(hIconDLL);
+end;
+
+type
+ PAWKIconButton = ^TAWKIconButton;
+ TAWKIconButton = record
+ descr:PAnsiChar;
+ name :PAnsiChar;
+ id :int_ptr;
+ end;
+const
+ CtrlIcoLib:array [WAT_CTRL_FIRST..WAT_CTRL_LAST,AST_NORMAL..AST_PRESSED] of
+ TAWKIconButton = (
+ ((descr:'Prev' ;name:'WATrack_Prev' ; id:IDI_PREV_NORMAL),
+ (descr:'Prev Hovered' ;name:'WATrack_PrevH' ; id:IDI_PREV_HOVERED),
+ (descr:'Prev Pushed' ;name:'WATrack_PrevP' ; id:IDI_PREV_PRESSED)),
+
+ ((descr:'Play' ;name:'WATrack_Play' ; id:IDI_PLAY_NORMAL),
+ (descr:'Play Hovered' ;name:'WATrack_PlayH' ; id:IDI_PLAY_HOVERED),
+ (descr:'Play Pushed' ;name:'WATrack_PlayP' ; id:IDI_PLAY_PRESSED)),
+
+ ((descr:'Pause' ;name:'WATrack_Pause' ; id:IDI_PAUSE_NORMAL),
+ (descr:'Pause Hovered' ;name:'WATrack_PauseH' ; id:IDI_PAUSE_HOVERED),
+ (descr:'Pause Pushed' ;name:'WATrack_PauseP' ; id:IDI_PAUSE_PRESSED)),
+
+ ((descr:'Stop' ;name:'WATrack_Stop' ; id:IDI_STOP_NORMAL),
+ (descr:'Stop Hovered' ;name:'WATrack_StopH' ; id:IDI_STOP_HOVERED),
+ (descr:'Stop Pushed' ;name:'WATrack_StopP' ; id:IDI_STOP_PRESSED)),
+
+ ((descr:'Next' ;name:'WATrack_Next' ; id:IDI_NEXT_NORMAL),
+ (descr:'Next Hovered' ;name:'WATrack_NextH' ; id:IDI_NEXT_HOVERED),
+ (descr:'Next Pushed' ;name:'WATrack_NextP' ; id:IDI_NEXT_PRESSED)),
+
+ ((descr:'Volume Down' ;name:'WATrack_VolDn' ; id:IDI_VOLDN_NORMAL),
+ (descr:'Volume Down Hovered';name:'WATrack_VolDnH' ; id:IDI_VOLDN_HOVERED),
+ (descr:'Volume Down Pushed' ;name:'WATrack_VolDnP' ; id:IDI_VOLDN_PRESSED)),
+
+ ((descr:'Volume Up' ;name:'WATrack_VolUp' ; id:IDI_VOLUP_NORMAL),
+ (descr:'Volume Up Hovered' ;name:'WATrack_VolUpH' ; id:IDI_VOLUP_HOVERED),
+ (descr:'Volume Up Pushed' ;name:'WATrack_VolUpP' ; id:IDI_VOLUP_PRESSED)),
+
+ ((descr:'Slider' ;name:'WATrack_Slider' ; id:IDI_SLIDER_NORMAL),
+ (descr:'Slider Hovered' ;name:'WATrack_SliderH'; id:IDI_SLIDER_HOVERED),
+ (descr:'Slider Pushed' ;name:'WATrack_SliderP'; id:IDI_SLIDER_PRESSED))
+ );
+
+function RegisterButtonIcons:boolean;
+var
+ sid:TSKINICONDESC;
+ buf:array [0..511] of AnsiChar;
+ hIconDLL:THANDLE;
+ i,j:integer;
+ path:pAnsiChar;
+begin
+ if not IconsLoaded then
+ begin
+ path:='icons\'+ICOCtrlName;
+// ConvertFileName(sid.szDefaultFile.a,buf);
+ CallService(MS_UTILS_PATHTOABSOLUTE,wparam(path),lparam(@buf));
+
+ hIconDLL:=LoadLibraryA(buf);
+ if hIconDLL=0 then // not found
+ begin
+ sid.szDefaultFile.a:='plugins\'+ICOCtrlName;
+// ConvertFileName(sid.szDefaultFile.a,buf);
+ CallService(MS_UTILS_PATHTOABSOLUTE,wparam(path),lparam(@buf));
+ hIconDLL:=LoadLibraryA(buf);
+ end;
+
+ if hIconDLL<>0 then
+ begin
+ FreeLibrary(hIconDLL);
+ FillChar(sid,SizeOf(sid),0);
+ sid.flags:=0;
+ sid.cbSize:=SizeOf(TSKINICONDESC);
+ sid.cx:=16;
+ sid.cy:=16;
+
+ sid.szSection.a :='WATrack/Frame Controls';
+ sid.szDefaultFile.a:=path;
+ i:=WAT_CTRL_FIRST;
+ repeat
+ j:=AST_NORMAL;
+ repeat
+ // increment from 1 by order, so - just decrease number (for iconpack import)
+ sid.iDefaultIndex :=CtrlIcoLib[i][j].id-1;
+ sid.pszName :=CtrlIcoLib[i][j].name;
+ sid.szDescription.a:=CtrlIcoLib[i][j].descr;
+
+ Skin_AddIcon(@sid);
+ Inc(j);
+ until j>AST_PRESSED;
+ Inc(i);
+ until i>WAT_CTRL_LAST;
+ IconsLoaded:=true;
+ end;
+ end;
+
+ result:=IconsLoaded;
+end;
+
+function GetIcon(action:integer;stat:integer):cardinal;
+begin
+ result:=CallService(MS_SKIN2_GETICON,0,
+ lparam(CtrlIcoLib[action][stat].name));
+end;
+
+function GetIconDescr(action:integer):pAnsiChar;
+begin
+ result:=CtrlIcoLib[action][AST_NORMAL].descr;
+end;
+
+end.
diff --git a/plugins/Watrack/watrack.dpr b/plugins/Watrack/watrack.dpr
new file mode 100644
index 0000000000..2702c4852f
--- /dev/null
+++ b/plugins/Watrack/watrack.dpr
@@ -0,0 +1,675 @@
+{$include compilers.inc}
+{$IFDEF COMPILER_16_UP}
+ {$WEAKLINKRTTI ON}
+ {.$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([])}
+{$ENDIF}
+{$IMAGEBASE $13000000}
+library WATrack;
+uses
+ // FastMM not compatible with FPC, internal for delphi xe
+// {$IFNDEF COMPILER_16_UP}{$IFNDEF FPC}fastmm4,{$ENDIF}{$ENDIF}
+ m_api,dbsettings,activex,winampapi,
+ Windows,messages,commctrl,//uxtheme,
+ srv_format,srv_player,wat_api,wrapper,
+ common,syswin,HlpDlg,mirutils
+ ,global,waticons,io,macros, msninfo
+ ,myshows in 'myshows\myshows.pas'
+ ,lastfm in 'lastfm\lastfm.pas'
+ ,statlog in 'stat\statlog.pas'
+ ,popups in 'popup\popups.pas'
+ ,proto in 'proto\proto.pas'
+ ,status in 'status\status.pas'
+ ,tmpl in 'status\tmpl.pas'
+ ,templates in 'templates\templates.pas'
+{$IFDEF KOL_MCK}
+ ,kolframe in 'kolframe\kolframe.pas'
+{$ENDIF}
+ {$include lst_players.inc}
+ {$include lst_formats.inc}
+;
+
+{$include res\i_const.inc}
+
+{$Resource res\watrack.res}
+
+{$include i_vars.inc}
+
+const
+ MenuDisablePos = 500050000;
+
+function MirandaPluginInfoEx(mirandaVersion:DWORD):PPLUGININFOEX; cdecl;
+begin
+ result:=@PluginInfo;
+ PluginInfo.cbSize :=SizeOf(TPLUGININFOEX);
+ PluginInfo.shortName :=PluginName;
+ PluginInfo.version :=$0000060C;
+ PluginInfo.description:='Paste played music info into message window or status text';
+ PluginInfo.author :='Awkward';
+ PluginInfo.authorEmail:='panda75@bk.ru; awk1975@ya.ru';
+ PluginInfo.copyright :='(c) 2005-2012 Awkward';
+ PluginInfo.homepage :='http://code.google.com/p/delphi-miranda-plugins/';
+ PluginInfo.flags :=UNICODE_AWARE;
+ PluginInfo.uuid :=MIID_WATRACK;
+end;
+
+{$include i_options.inc}
+{$include i_timer.inc}
+{$include i_gui.inc}
+{$include i_opt_dlg.inc}
+{$include i_cover.inc}
+
+function ReturnInfo(enc:WPARAM;cp:LPARAM=CP_ACP):pointer;
+begin
+ if enc<>WAT_INF_UNICODE then
+ begin
+ ClearSongInfoData(tSongInfo(SongInfoA),true);
+ move(SongInfo,SongInfoA,SizeOf(tSongInfo));
+ with SongInfoA do
+ begin
+ FastWideToAnsi(SongInfo.url,url);
+ if enc=WAT_INF_ANSI then
+ begin
+ WideToAnsi(SongInfo.artist ,artist ,cp);
+ WideToAnsi(SongInfo.title ,title ,cp);
+ WideToAnsi(SongInfo.album ,album ,cp);
+ WideToAnsi(SongInfo.genre ,genre ,cp);
+ WideToAnsi(SongInfo.comment,comment,cp);
+ WideToAnsi(SongInfo.year ,year ,cp);
+ WideToAnsi(SongInfo.mfile ,mfile ,cp);
+ WideToAnsi(SongInfo.wndtext,wndtext,cp);
+ WideToAnsi(SongInfo.player ,player ,cp);
+ WideToAnsi(SongInfo.txtver ,txtver ,cp);
+ WideToAnsi(SongInfo.lyric ,lyric ,cp);
+ WideToAnsi(SongInfo.cover ,cover ,cp);
+ WideToAnsi(SongInfo.url ,url ,cp);
+ end
+ else
+ begin
+ WideToUTF8(SongInfo.artist ,artist);
+ WideToUTF8(SongInfo.title ,title);
+ WideToUTF8(SongInfo.album ,album);
+ WideToUTF8(SongInfo.genre ,genre);
+ WideToUTF8(SongInfo.comment,comment);
+ WideToUTF8(SongInfo.year ,year);
+ WideToUTF8(SongInfo.mfile ,mfile);
+ WideToUTF8(SongInfo.wndtext,wndtext);
+ WideToUTF8(SongInfo.player ,player);
+ WideToUTF8(SongInfo.txtver ,txtver);
+ WideToUTF8(SongInfo.lyric ,lyric);
+ WideToUTF8(SongInfo.cover ,cover);
+ WideToUTF8(SongInfo.url ,url);
+ end;
+ end;
+ result:=@SongInfoA;
+ end
+ else
+ result:=@SongInfo;
+end;
+
+function WATReturnGlobal(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+begin
+ if wParam=0 then wParam:=WAT_INF_UNICODE;
+ if lParam=0 then lParam:=MirandaCP;
+
+ result:=int_ptr(ReturnInfo(wParam,lParam));
+end;
+
+function WATGetFileInfo(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+// si:TSongInfo;
+ dst:pSongInfo;
+ extw:array [0..7] of WideChar;
+ f:THANDLE;
+ p:PWideChar;
+begin
+ result:=1;
+ if (lParam=0) or (pSongInfo(lParam).mfile=nil) then exit;
+ dst:=pointer(lParam);
+ StrDupW(p,dst^.mfile);
+ ClearTrackInfo(dst^,false); //!!!!
+ dst^.mfile:=p;
+// FillChar(dst,SizeOf(dst),0);
+// FillChar(si,SizeOf(si),0);
+{
+ if flags and WAT_INF_ANSI<>0 then
+ AnsiToWide(dst^.mfile,si.mfile)
+ else if flags and WAT_INF_UTF<>0 then
+ UTFToWide(dst^.mfile,si.mfile)
+ else
+ si.mfile:=dst^.mfile;
+}
+ f:=Reset(dst^.mfile);
+ if dword(f)<>INVALID_HANDLE_VALUE then
+ GetFileTime(f,nil,nil,@dst^.date);
+ CloseHandle(f);
+ dst^.fsize:=GetFSize(dst^.mfile);
+ GetExt(dst^.mfile,extw);
+ if GetFileFormatInfo(dst^)<>WAT_RES_NOTFOUND then
+ begin
+ with dst^ do
+ begin
+ if (cover=nil) or (cover^=#0) then
+ GetCover(cover,mfile);
+ if (lyric=nil) or (lyric^=#0) then
+ GetLyric(lyric,mfile);
+ end;
+ result:=0;
+// ReturnInfo(si,dst,wParam and $FF);
+ end;
+end;
+
+function WATGetMusicInfo(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+type
+ ppointer = ^pointer;
+const
+ giused:cardinal=0;
+var
+ flags:cardinal;
+ buf:PWideChar;
+ OldPlayerStatus:integer;
+ stat:integer;
+ newplayer:bool;
+begin
+ result:=WAT_RES_NOTFOUND;
+ if DisablePlugin=dsPermanent then
+ exit;
+
+ //----- Return old info if main timer -----
+ if giused<>0 then
+ begin
+ result:=WAT_RES_OK;
+ if lParam<>0 then
+ ppointer(lParam)^:=ReturnInfo(wParam and $FF);
+ exit;
+ end;
+
+ giused:=1;
+
+ OldPlayerStatus:=WorkSI.status;
+
+ //----- Checking player -----
+ // get player status too
+ flags:=0;
+ if CheckAll<>BST_UNCHECKED then flags:=flags or WAT_OPT_CHECKALL;
+ // no need old data, clear
+// ClearPlayerInfo(WorkSI,false);
+ result:=CheckPlayers(WorkSI,flags);
+ if result=WAT_RES_NEWPLAYER then
+ begin
+ newplayer:=true;
+ NotifyEventHooks(hHookWATStatus,WAT_EVENT_NEWPLAYER,tlparam(@WorkSI));
+ result:=WAT_RES_OK;
+ end
+ else // !!!! (need to add) must remember about same player, another instance
+ newplayer:=false;
+
+ // Checking player status
+ if result=WAT_RES_OK then
+ begin
+ if not newplayer then //!!cheat
+ SongInfo.plwnd:=WorkSI.plwnd;
+
+ // player stopped - no need file info
+ if WorkSI.status=WAT_MES_STOPPED then
+ begin
+ ClearFileInfo (WorkSI,false);
+ ClearChangingInfo(WorkSI,false);
+ ClearTrackInfo (WorkSI,false);
+
+ if Hiword(OldPlayerStatus)<>WAT_MES_STOPPED then
+ begin
+ NotifyEventHooks(hHookWATStatus,WAT_EVENT_PLAYERSTATUS,WAT_MES_STOPPED);
+ end;
+
+ ClearFileInfo (SongInfo,true);
+ ClearChangingInfo(SongInfo,true);
+ ClearTrackInfo (SongInfo,true);
+ if newplayer then
+ begin
+ ClearPlayerInfo(SongInfo,true);
+ CopyPlayerInfo (WorkSI,SongInfo);
+ end;
+ WorkSI.status:=(WAT_MES_STOPPED shl 16) or (WAT_PLS_NOMUSIC and $FFFF);
+ SongInfo.status:=WorkSI.status;
+ end
+ else
+ begin
+ //----- Get file (no file, new file, maybe new) -----
+ // file info will be replaced (name most important only)
+ flags:=0;
+ if CheckTime <>BST_UNCHECKED then flags:=flags or WAT_OPT_CHECKTIME;
+ if UseImplant<>BST_UNCHECKED then flags:=flags or WAT_OPT_IMPLANTANT;
+ if MTHCheck <>BST_UNCHECKED then flags:=flags or WAT_OPT_MULTITHREAD;
+ if KeepOld <>BST_UNCHECKED then flags:=flags or WAT_OPT_KEEPOLD;
+
+ // requirement - old file name
+ result:=CheckFile(WorkSI,flags,TimeoutForThread);
+
+ // here - place for Playerstatus event
+ // high word - song status (play, pause,stop, nothing)
+ // low word - player status (normal,no music, nothing)
+ case WorkSI.status of
+ WAT_MES_PLAYING,
+ WAT_MES_PAUSED: stat:=WAT_PLS_NORMAL;
+ WAT_MES_UNKNOWN: // depends of file search
+ begin
+ if result=WAT_RES_NOTFOUND then
+ stat:=WAT_PLS_NOMUSIC
+ else
+ stat:=WAT_PLS_NORMAL;
+ end;
+ else // really, this way blocked already
+ {WAT_MES_STOPPED:} stat:=WAT_PLS_NOMUSIC;
+ end;
+ WorkSI.status:=(WorkSI.status shl 16) or (stat and $FFFF);
+
+ if OldPlayerStatus<>WorkSI.status then
+ begin
+ NotifyEventHooks(hHookWATStatus,WAT_EVENT_PLAYERSTATUS,WorkSI.status);
+ end;
+
+ // no playing file - clear all file info
+ if stat=WAT_PLS_NOMUSIC then
+ begin
+ ClearFileInfo (WorkSI,false);
+ ClearChangingInfo(WorkSI,false);
+ ClearTrackInfo (WorkSI,false);
+
+ ClearFileInfo (SongInfo,true);
+ ClearChangingInfo(SongInfo,true);
+ ClearTrackInfo (SongInfo,true);
+
+ if newplayer then
+ begin
+ ClearPlayerInfo(SongInfo,true);
+ CopyPlayerInfo (WorkSI,SongInfo);
+ end;
+ SongInfo.status:=WorkSI.status;
+ end;
+ // now time for changes (window text, volume)
+ // just when music presents
+ if stat=WAT_PLS_NORMAL then
+ begin
+ GetChangingInfo(WorkSI,flags);
+ // full info requires
+ // "no music" case blocked
+ if (result=WAT_RES_NEWFILE) or // new file
+ ((result=WAT_RES_OK) and // if not new but...
+ (((wParam and WAT_INF_CHANGES)=0) or // ... ask for full info
+ (StrPosW(WorkSI.mfile,'://')<>nil) or // ... or remote file
+ isContainer(WorkSI.mfile))) then // ... or container like CUE
+ begin
+ // requirement: old artist/title for remote files
+ stat:=GetInfo(WorkSI,flags);
+
+ // covers
+ if (WorkSI.cover=nil) or (WorkSI.cover^=#0) then
+ GetCover(WorkSI.cover,WorkSI.mfile)
+ else
+ begin
+ mGetMem(buf,MAX_PATH*SizeOf(WideChar));
+ GetTempPathW(MAX_PATH,buf);
+ if StrCmpW(buf,WorkSI.cover,StrLenW(buf))=0 then
+ begin
+ GetExt(WorkSI.cover,StrCatEW(buf,'\wat_cover.'));
+ DeleteFileW(buf);
+ MoveFileW(WorkSI.cover,buf);
+ mFreeMem(WorkSI.cover);
+ WorkSI.cover:=buf;
+ end
+ else
+ mFreeMem(buf);
+ end;
+ // lyric
+ if (WorkSI.lyric=nil) or (WorkSI.lyric^=#0) then
+ GetLyric(WorkSI.lyric,WorkSI.mfile);
+
+// file info will be updated anyway, so - just update it
+ if result=WAT_RES_NEWFILE then
+ begin
+ ClearFileInfo(SongInfo,true);
+ CopyFileInfo (WorkSI,SongInfo);
+ end;
+ ClearTrackInfo(SongInfo,true);
+ CopyTrackInfo (WorkSI,SongInfo);
+
+ if newplayer then
+ begin
+ ClearPlayerInfo(SongInfo,true);
+ CopyPlayerInfo (WorkSI,SongInfo);
+ end;
+ ClearChangingInfo(SongInfo,true);
+ CopyChangingInfo (WorkSI,SongInfo);
+ SongInfo.status:=WorkSI.status;
+
+ if stat=WAT_RES_NEWFILE then
+ result:=WAT_RES_NEWFILE;
+
+ if result=WAT_RES_NEWFILE then
+ NotifyEventHooks(hHookWATStatus,WAT_EVENT_NEWTRACK,tlparam(@SongInfo));
+ end
+ else // just changing infos
+ begin
+ if newplayer then
+ begin
+ ClearPlayerInfo(SongInfo,true);
+ CopyPlayerInfo (WorkSI,SongInfo);
+ end;
+ ClearChangingInfo(SongInfo,true);
+ CopyChangingInfo (WorkSI,SongInfo);
+ SongInfo.status:=WorkSI.status;
+ end;
+ end;
+ end;
+
+ if lParam<>0 then
+ ppointer(lParam)^:=ReturnInfo(wParam and $FF);
+ end
+ //----- Player not found -----
+ else
+ begin
+ if OldPlayerStatus<>WorkSI.status then
+ begin
+ ClearSongInfoData(WorkSI,false); // player info must be empty anyway
+ ClearSongInfoData(SongInfo,true);
+ SongInfo.status:=WAT_PLS_NOTFOUND+WAT_MES_UNKNOWN shl 16;
+
+ NotifyEventHooks(hHookWATStatus,WAT_EVENT_PLAYERSTATUS,
+ WAT_PLS_NOTFOUND+WAT_MES_UNKNOWN shl 16);
+ end;
+
+{
+ if OldPlayerStatus<>WorkSI.status then
+ begin
+ NotifyEventHooks(hHookWATStatus,WAT_EVENT_PLAYERSTATUS,
+ WAT_PLS_NOTFOUND+WAT_MES_UNKNOWN shl 16);
+ end;
+
+ ClearSongInfoData(WorkSI,false); // player info must be empty anyway
+ WorkSI.status:=WAT_PLS_NOTFOUND+WAT_MES_UNKNOWN shl 16;
+
+ ClearSongInfoData(SongInfo,true);
+ SongInfo.status:=WAT_PLS_NOTFOUND+WAT_MES_UNKNOWN shl 16;
+}
+
+ if lParam<>0 then
+ ppointer(lParam)^:=nil;
+ end;
+
+ giused:=0;
+end;
+
+function PressButton(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+var
+ flags:integer;
+begin
+ if DisablePlugin=dsPermanent then
+ result:=0
+ else
+ begin
+ flags:=0;
+ if UseImplant<>BST_UNCHECKED then flags:=flags or WAT_OPT_IMPLANTANT;
+ if mmkeyemu <>BST_UNCHECKED then flags:=flags or WAT_OPT_APPCOMMAND;
+ if CheckAll <>BST_UNCHECKED then flags:=flags or WAT_OPT_CHECKALL;
+ result:=SendCommand(wParam,lParam,flags);
+ end;
+end;
+
+function WATPluginStatus(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ f1:integer;
+begin
+ if wParam=2 then
+ begin
+ result:=PluginInfo.version;
+ exit;
+ end;
+ if DisablePlugin=dsPermanent then
+ result:=1
+ else
+ result:=0;
+ if (integer(wParam)<0) or (wParam=MenuDisablePos) then
+ begin
+ if result=0 then
+ wParam:=1
+ else
+ wParam:=0;
+ end;
+ case wParam of
+ 0: begin
+ if DisablePlugin=dsPermanent then //??
+ begin
+ StartTimer;
+ DisablePlugin:=dsEnabled;
+ end;
+ f1:=0;
+ end;
+ 1: begin
+ StopTimer;
+ DisablePlugin:=dsPermanent;
+ f1:=CMIF_CHECKED;
+ end;
+ else
+ exit;
+ end;
+ DBWriteByte(0,PluginShort,opt_disable,DisablePlugin);
+
+ ChangeMenuIcons(f1);
+
+ NotifyEventHooks(hHookWATStatus,WAT_EVENT_PLUGINSTATUS,DisablePlugin);
+end;
+
+function WaitAllModules(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ ptr:pwModule;
+begin
+ result:=0;
+
+ CallService(MS_SYSTEM_REMOVEWAIT,wParam,0);
+
+ ptr:=ModuleLink;
+ while ptr<>nil do
+ begin
+ if @ptr^.Init<>nil then
+ ptr^.ModuleStat:=ptr^.Init(true);
+ ptr:=ptr^.Next;
+ end;
+
+ if mTimer<>0 then
+ TimerProc(0,0,0,0);
+
+ StartTimer;
+
+ NotifyEventHooks(hHookWATLoaded,0,0);
+ CloseHandle(hEvent);
+end;
+
+function OnModulesLoaded(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ p:PAnsiChar;
+begin
+ UnhookEvent(onloadhook);
+
+ CallService(MS_DBEDIT_REGISTERSINGLEMODULE,twparam(PluginShort),0);
+
+ hTimer:=0;
+
+ OleInitialize(nil);
+
+ if RegisterIcons then
+ wsic:=HookEvent(ME_SKIN2_ICONSCHANGED,@IconChanged)
+ else
+ wsic:=0;
+
+ CreateMenus;
+
+ if ServiceExists(MS_TTB_ADDBUTTON)<>0 then
+ onloadhook:=HookEvent(ME_TTB_MODULELOADED,@OnTTBLoaded)
+ else
+ ttbState:=0;
+
+ ProcessFormatLink;
+ ProcessPlayerLink;
+ p:=GetAddonFileName(nil,'player','plugins','ini');
+ if p<>nil then
+ begin
+ LoadFromFile(p);
+ mFreeMem(p);
+ end;
+
+ p:=GetAddonFileName(nil,'watrack_icons','icons','dll');
+ if p<>nil then
+ begin
+ SetPlayerIcons(p);
+ mFreeMem(p);
+ end;
+
+ IsMultiThread:=true;
+
+ hEvent:=CreateEvent(nil,true,true,nil);
+ if hEvent<>0 then
+ begin
+ p:='WAT_INIT';
+ hWATI:=CreateServiceFunction(p,@WaitAllModules);
+ CallService(MS_SYSTEM_WAITONHANDLE,hEvent,tlparam(p));
+ end;
+
+ loadopt;
+ if DisablePlugin=dsPermanent then
+ CallService(MS_WAT_PLUGINSTATUS,1,0);
+
+ StartMSNHook;
+
+ result:=0;
+end;
+
+procedure FreeVariables;
+begin
+ ClearSongInfoData(SongInfo ,true);
+ ClearSongInfoData(tSongInfo(SongInfoA),true);
+ ClearSongInfoData(WorkSI ,false); // not necessary really
+ mFreeMem(CoverPaths);
+ ClearFormats;
+ ClearPlayers;
+end;
+
+procedure FreeServices;
+begin
+ DestroyServiceFunction(hGFI);
+ DestroyServiceFunction(hRGS);
+
+ DestroyServiceFunction(hWI);
+ DestroyServiceFunction(hGMI);
+ DestroyServiceFunction(hPS);
+ DestroyServiceFunction(hPB);
+ DestroyServiceFunction(hWATI);
+ DestroyServiceFunction(hWC);
+
+ DestroyServiceFunction(hFMT);
+ DestroyServiceFunction(hPLR);
+end;
+
+function PreShutdown(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ buf:array [0..511] of WideChar;
+ fdata:WIN32_FIND_DATAW;
+ fi:THANDLE;
+ p:PWideChar;
+ ptr:pwModule;
+begin
+ StopMSNHook;
+
+ NotifyEventHooks(hHookWATStatus,WAT_EVENT_PLAYERSTATUS,WAT_PLS_NOTFOUND);
+
+ if hwndTooltip<>0 then
+ DestroyWindow(hwndTooltip);
+
+ if ttbState<>0 then
+ begin
+ if ServiceExists(MS_TTB_REMOVEBUTTON)>0 then
+ CallService(MS_TTB_REMOVEBUTTON,TWPARAM(ttbState),0);
+ ttbState:=0;
+ end;
+
+ StopTimer;
+ ptr:=ModuleLink;
+ while ptr<>nil do
+ begin
+ if @ptr^.DeInit<>nil then
+ ptr^.DeInit(false);
+ ptr:=ptr^.Next;
+ end;
+
+// UnhookEvent(plStatusHook);
+ UnhookEvent(hHookShutdown);
+ UnhookEvent(opthook);
+ if wsic<>0 then UnhookEvent(wsic);
+
+ FreeServices;
+ FreeVariables;
+
+ DestroyHookableEvent(hHookWATLoaded);
+ DestroyHookableEvent(hHookWATStatus);
+
+ OleUnInitialize;
+
+ //delete cover files
+ buf[0]:=#0;
+ GetTempPathW(511,buf);
+ p:=StrEndW(buf);
+ StrCopyW(p,'wat_cover.*');
+
+ fi:=FindFirstFileW(buf,fdata);
+ if fi<>THANDLE(INVALID_HANDLE_VALUE) then
+ begin
+ repeat
+ StrCopyW(p,fdata.cFileName);
+ DeleteFileW(buf);
+ until not FindNextFileW(fi,fdata);
+ FindClose(fi);
+ end;
+
+ result:=0;
+end;
+
+function Load():int; cdecl;
+begin
+ result:=0;
+ Langpack_register;
+
+ DisablePlugin:=dsPermanent;
+
+ hHookWATLoaded:=CreateHookableEvent(ME_WAT_MODULELOADED);
+ hHookWATStatus:=CreateHookableEvent(ME_WAT_NEWSTATUS);
+ hHookShutdown :=HookEvent(ME_SYSTEM_OKTOEXIT,@PreShutdown);
+ opthook :=HookEvent(ME_OPT_INITIALISE ,@OnOptInitialise);
+
+ hGFI:=CreateServiceFunction(MS_WAT_GETFILEINFO ,@WATGetFileInfo);
+ hRGS:=CreateServiceFunction(MS_WAT_RETURNGLOBAL ,@WATReturnGlobal);
+
+ hGMI:=CreateServiceFunction(MS_WAT_GETMUSICINFO ,@WATGetMusicInfo);
+ hPS :=CreateServiceFunction(MS_WAT_PLUGINSTATUS ,@WATPluginStatus);
+ hPB :=CreateServiceFunction(MS_WAT_PRESSBUTTON ,@PressButton);
+ hWI :=CreateServiceFunction(MS_WAT_WINAMPINFO ,@WinampGetInfo);
+ hWC :=CreateServiceFunction(MS_WAT_WINAMPCOMMAND,@WinampCommand);
+
+ hFMT:=CreateServiceFunction(MS_WAT_FORMAT,@ServiceFormat);
+ hPLR:=CreateServiceFunction(MS_WAT_PLAYER,@ServicePlayer);
+
+ FillChar(SongInfoA,SizeOf(SongInfoA),0);
+ FillChar(SongInfo ,SizeOf(SongInfo ),0);
+ FillChar(WorkSI ,SizeOf(SongInfo ),0);
+ onloadhook:=HookEvent(ME_SYSTEM_MODULESLOADED,@OnModulesLoaded);
+end;
+
+function Unload:int; cdecl;
+begin
+ result:=0;
+end;
+
+exports
+ Load, Unload,
+ MirandaPluginInfoEx;
+
+begin
+end.
diff --git a/plugins/Watrack/winampapi.pas b/plugins/Watrack/winampapi.pas
new file mode 100644
index 0000000000..e53e88a247
--- /dev/null
+++ b/plugins/Watrack/winampapi.pas
@@ -0,0 +1,277 @@
+{Winamp-like - base class}
+unit winampapi;
+{$include compilers.inc}
+
+interface
+
+uses windows,messages;
+
+const
+ WinampClass = 'Winamp v1.x';
+ WinampTail = ' - Winamp';
+
+function WinampGetStatus(wnd:HWND):integer;
+function WinampGetWindowText(wnd:HWND):pWideChar;
+function WinampFindWindow(wnd:HWND):HWND;
+function WinampCommand(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+function WinampGetInfo(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+
+const
+ WM_WA_IPC = WM_USER;
+ IPC_GETVERSION = 0;
+ IPC_ISPLAYING = 104;
+ IPC_GETINFO = 126;
+ IPC_GETOUTPUTTIME = 105;
+ IPC_WRITEPLAYLIST = 120;
+ IPC_GETLISTLENGTH = 124;
+ IPC_GETLISTPOS = 125;
+ IPC_ISFULLSTOP = 400; //!!
+ IPC_INETAVAILABLE = 242; //!!
+ IPC_GETPLAYLISTFILE = 211;
+
+ IPC_IS_PLAYING_VIDEO = 501;
+
+ IPC_PLAYFILE = 100;
+ IPC_STARTPLAY = 102;
+ IPC_SETVOLUME = 122; // -666 returns the current volume.
+ IPC_GET_SHUFFLE = 250;
+ IPC_SET_SHUFFLE = 252;
+ IPC_JUMPTOTIME = 106;
+
+const
+ WINAMP_PREV = 40044;
+ WINAMP_PLAY = 40045;
+ WINAMP_PAUSE = 40046;
+ WINAMP_STOP = 40047;
+ WINAMP_NEXT = 40048;
+ WINAMP_VOLUMEUP = 40058; // turns the volume up a little
+ WINAMP_VOLUMEDOWN = 40059; // turns the volume down a little
+
+implementation
+
+uses common,wat_api;
+
+function WinampFindWindow(wnd:HWND):HWND;
+var
+ pr,pr1:dword;
+begin
+ GetWindowThreadProcessId(wnd,@pr);
+ result:=0;
+ repeat
+ result:=FindWindowEx(0,result,WinampClass,nil);
+ if result<>0 then
+ begin
+ GetWindowThreadProcessId(result,@pr1);
+ if pr=pr1 then
+ break;
+ end
+ else
+ break;
+ until false;
+end;
+
+// ----------- Get player info ------------
+
+function GetVersion(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_WA_IPC,0,IPC_GETVERSION);
+end;
+
+function GetVersionText(wnd:HWND):pWideChar;
+var
+ ver:integer;
+ s:array [0..31] of WideChar;
+ p:pWideChar;
+begin
+ ver:=GetVersion(wnd);
+ p:=@s;
+ IntToStr(p,ver shr 12);
+ while p^<>#0 do inc(p);
+ p^:='.';
+ IntToStr(p+1,(ver shr 4) and $F);
+ while p^<>#0 do inc(p);
+ p^:='.';
+ IntToStr(p+1,ver and $F);
+ StrDupW(result,PWideChar(@s));
+end;
+
+function WinampGetStatus(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_WA_IPC,0,IPC_ISPLAYING);
+ // 0 - stopped, 1 - playing
+ if result>1 then
+ result:=WAT_MES_PAUSED;
+{
+ if result=0 then // !! only for remote media!
+ begin
+ result:=SendMessage(wnd,WM_WA_IPC,0,IPC_ISFULLSTOP);
+ if result<>0 then
+ result:=WAT_MES_STOPPED
+ else
+ result:=WAT_MES_PLAYING;
+ end;
+}
+end;
+
+function WinampGetWindowText(wnd:HWND):pWideChar;
+var
+ a:cardinal;
+ pc:pWideChar;
+begin
+ a:=GetWindowTextLengthW(wnd);
+ mGetMem(result,(a+1)*SizeOf(WideChar));
+ if GetWindowTextW(wnd,result,a+1)>0 then
+ begin
+ pc:=StrPosW(result,WinampTail);
+ if pc<>nil then
+ begin
+ pc^:=#0;
+ pc:=StrPosW(result,'. ');
+ if pc<>nil then
+ StrCopyW(result,pc+2);
+ end;
+ end;
+end;
+
+// --------- Get file info ----------
+
+function GetKbps(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_WA_IPC,1,IPC_GETINFO);
+ if result>1000 then
+ result:=result div 1000;
+end;
+
+function GetKhz(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_WA_IPC,0,IPC_GETINFO);
+ if result>1000 then
+ result:=result div 1000;
+end;
+
+function GetChannels(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_WA_IPC,2,IPC_GETINFO);
+end;
+
+function GetTotalTime(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_WA_IPC,1,IPC_GETOUTPUTTIME);
+end;
+
+function GetElapsedTime(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_WA_IPC,0,IPC_GETOUTPUTTIME) div 1000;
+end;
+
+function GetVolume(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_WA_IPC,-666,IPC_SETVOLUME);
+ result:=(result shl 16)+(result shr 4);
+end;
+
+function WinampGetInfo(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+var
+ wnd:HWND;
+begin
+ result:=0;
+ with pSongInfo(wParam)^ do
+ begin
+ if winampwnd<>0 then
+ wnd:=winampwnd
+ else
+ wnd:=plwnd;
+
+ if (lParam and WAT_OPT_PLAYERDATA)<>0 then
+ begin
+ if plyver=0 then
+ begin
+ plyver:=GetVersion(wnd);
+ txtver:=GetVersionText(wnd);
+ end;
+ end
+ else if (lParam and WAT_OPT_CHANGES)<>0 then
+ begin
+ volume:=GetVolume(wnd);
+ if status<>WAT_MES_STOPPED then
+ time:=GetElapsedTime(wnd);
+// wndtext:=WinampGetWindowText(wnd);
+ end
+ else
+ begin
+ if kbps =0 then kbps :=GetKbps(wnd);
+ if khz =0 then khz :=GetKhz(wnd);
+ if channels=0 then channels:=GetChannels(wnd);
+ if total =0 then total :=GetTotalTime(wnd);
+ end;
+ end;
+end;
+
+// ------- Commands ----------
+
+function Play(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_COMMAND,WINAMP_PLAY,0);
+end;
+
+function Pause(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_COMMAND,WINAMP_PAUSE,0);
+end;
+
+function Stop(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_COMMAND,WINAMP_STOP,0);
+end;
+
+function Next(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_COMMAND,WINAMP_NEXT,0);
+end;
+
+function Prev(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_COMMAND,WINAMP_PREV,0);
+end;
+
+function VolDn(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_COMMAND,WINAMP_VOLUMEDOWN,0);
+end;
+
+function VolUp(wnd:HWND):integer;
+begin
+ result:=SendMessage(wnd,WM_COMMAND,WINAMP_VOLUMEUP,0);
+end;
+
+procedure SetVolume(wnd:HWND;value:cardinal);
+begin
+ SendMessage(wnd,WM_WA_IPC,value shl 4,IPC_SETVOLUME);
+end;
+
+function Seek(wnd:HWND;value:integer):integer;
+begin
+ result:=SendMessage(wnd,WM_WA_IPC,0,IPC_GETOUTPUTTIME) div 1000;
+ SendMessage(wnd,WM_WA_IPC,value*1000,IPC_JUMPTOTIME);
+end;
+
+function WinampCommand(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+var
+ wnd:HWND;
+begin
+ wnd:=wParam;
+ case LoWord(lParam) of
+ WAT_CTRL_PREV : result:=Prev (wnd);
+ WAT_CTRL_PLAY : result:=Play (wnd);
+ WAT_CTRL_PAUSE: result:=Pause(wnd);
+ WAT_CTRL_STOP : result:=Stop (wnd);
+ WAT_CTRL_NEXT : result:=Next (wnd);
+ WAT_CTRL_VOLDN: result:=VolDn(wnd);
+ WAT_CTRL_VOLUP: result:=VolUp(wnd);
+ WAT_CTRL_SEEK : result:=Seek (wnd,lParam shr 16);
+ else
+ result:=0;
+ end;
+end;
+
+end.
diff --git a/plugins/mRadio/activex.pp b/plugins/mRadio/activex.pp
new file mode 100644
index 0000000000..d36762ccf1
--- /dev/null
+++ b/plugins/mRadio/activex.pp
@@ -0,0 +1,4173 @@
+Unit ActiveX;
+
+//+-------------------------------------------------------------------------
+//
+// Microsoft Windows
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+// File: objidl.idl
+//
+// Header translation by Marco van de Voort for Free Pascal Platform
+// SDK dl'ed January 2002
+//
+//--------------------------------------------------------------------------
+
+{$Mode objfpc}
+
+{$ifndef NO_SMART_LINK}
+{$smartlink on}
+{$endif}
+
+Interface
+
+Uses variants,Windows,ctypes,types;
+
+
+type
+{ extra types }
+ TOleChar = Types.TOleChar;
+ POleStr = Types.POleStr;
+ PPOleStr = Types.PPOleStr;
+ TBStr = POleStr;
+ TBStrList = array[0..(high(integer) div sizeof(TBSTR))-1] of TBstr;
+ PBStrList = ^TBStrList;
+ POleStrList = ^TOleStrList;
+ TOleStrList = array[0..(high(integer) div sizeof(POleStr))-1] of POleStr;
+
+ PBStr = ^TBStr;
+ TOleEnum = type LongWord;
+ LargeInt = Types.LargeInt;
+ LargeUInt = Types.LargeUInt;
+ PLargeInt = Types.PLargeInt;
+ PLargeUInt = Types.PLargeUInt;
+ FMTID = TGUID;
+ pFMTID = pGUID;
+
+ { Glue types, should be linked to the proper windows unit types}
+ Size_t = DWord; {??, probably, like Unix, typecastable to pointer?!?}
+ OleChar = WChar;
+ LPOLESTR = ^OLECHAR;
+ POLECHAR = LPOLESTR;
+ PLPOLESTR = ^LPOLESTR;
+ PROPID = ULONG;
+ TPROPID= PROPID;
+ PPROPID= ^PROPID;
+ VARIANT_BOOL = wordbool;
+ _VARIANT_BOOL = VARIANT_BOOL;
+ PVARIANT_BOOL = ^VARIANT_BOOL;
+ CY = CURRENCY;
+ DATE = DOUBLE;
+ BSTR = POLESTR;
+ TOleDate = DATE;
+ POleDate = ^TOleDate;
+ OLE_HANDLE = UINT;
+ LPOLE_HANDLE = ^OLE_HANDLE;
+ OLE_COLOR = DWORD;
+ LPOLE_COLOR = ^OLE_COLOR;
+ TOleHandle = OLE_HANDLE;
+ POleHandle = LPOLE_HANDLE;
+ TOleColor = OLE_COLOR;
+ POleColor = LPOle_Color;
+
+CONST
+ GUID_NULL : TGUID = '{00000000-0000-0000-0000-000000000000}';
+
+ // bit flags for IExternalConnection
+CONST
+ EXTCONN_STRONG = $0001; // strong connection
+ EXTCONN_WEAK = $0002; // weak connection (table, container)
+ EXTCONN_CALLABLE = $0004; // table .vs. callable
+ {Bind Flags}
+ BIND_MAYBOTHERUSER = 1;
+ BIND_JUSTTESTEXISTENCE = 2;
+
+
+ MKSYS_NONE = 0;
+ MKSYS_GENERICCOMPOSITE = 1;
+ MKSYS_FILEMONIKER = 2;
+ MKSYS_ANTIMONIKER = 3;
+ MKSYS_ITEMMONIKER = 4;
+ MKSYS_POINTERMONIKER = 5;
+// MKSYS_URLMONIKER = 6;
+ MKSYS_CLASSMONIKER = 7;
+ MKSYS_OBJREFMONIKER = 8;
+ MKSYS_SESSIONMONIKER = 9;
+
+ // system moniker types; returned from IsSystemMoniker.
+
+ MKRREDUCE_ONE = 3 SHL 16;
+ MKRREDUCE_TOUSER = 2 SHL 16;
+ MKRREDUCE_THROUGHUSER = 1 SHL 16;
+ MKRREDUCE_ALL = 0;
+
+ // Storage element types
+ STGTY_STORAGE = 1;
+ STGTY_STREAM = 2;
+ STGTY_LOCKBYTES = 3;
+ STGTY_PROPERTY = 4;
+
+ STREAM_SEEK_SET = 0;
+ STREAM_SEEK_CUR = 1;
+ STREAM_SEEK_END = 2;
+
+ LOCK_WRITE = 1;
+ LOCK_EXCLUSIVE = 2;
+ LOCK_ONLYONCE = 4;
+
+ //Advise Flags
+ ADVF_NODATA = 1;
+ ADVF_PRIMEFIRST = 2;
+ ADVF_ONLYONCE = 4;
+ ADVF_DATAONSTOP = 64;
+ ADVFCACHE_NOHANDLER = 8;
+ ADVFCACHE_FORCEBUILTIN = 16;
+ ADVFCACHE_ONSAVE = 32;
+
+
+//****************************************************************************
+//* Notification Interfaces
+//****************************************************************************/
+
+ TYMED_HGLOBAL = 1;
+ TYMED_FILE = 2;
+ TYMED_ISTREAM = 4;
+ TYMED_ISTORAGE = 8;
+ TYMED_GDI = 16;
+ TYMED_MFPICT = 32;
+ TYMED_ENHMF = 64;
+ TYMED_NULL = 0;
+
+// Object Definitions for EnumObjects()
+ OBJ_PEN = 1;
+ OBJ_BRUSH = 2;
+ OBJ_DC = 3;
+ OBJ_METADC = 4;
+ OBJ_PAL = 5;
+ OBJ_FONT = 6;
+ OBJ_BITMAP = 7;
+ OBJ_REGION = 8;
+ OBJ_METAFILE = 9;
+ OBJ_MEMDC = 10;
+ OBJ_EXTPEN = 11;
+ OBJ_ENHMETADC = 12;
+ OBJ_ENHMETAFILE = 13;
+ DATADIR_GET = 1;
+ DATADIR_SET = 2;
+
+// call type used by IMessageFilter::HandleIncomingMessage
+
+ CALLTYPE_TOPLEVEL = 1; // toplevel call - no outgoing call
+ CALLTYPE_NESTED = 2; // callback on behalf of previous outgoing call - should always handle
+ CALLTYPE_ASYNC = 3; // aysnchronous call - can NOT be rejected
+ CALLTYPE_TOPLEVEL_CALLPENDING = 4; // new toplevel call with new LID
+ CALLTYPE_ASYNC_CALLPENDING = 5; // async call - can NOT be rejected
+
+// status of server call - returned by IMessageFilter::HandleIncomingCall
+// and passed to IMessageFilter::RetryRejectedCall
+
+ SERVERCALL_ISHANDLED = 0;
+ SERVERCALL_REJECTED = 1;
+ SERVERCALL_RETRYLATER = 2;
+
+// Pending type indicates the level of nesting
+
+ PENDINGTYPE_TOPLEVEL = 1; // toplevel call
+ PENDINGTYPE_NESTED = 2; // nested call
+
+// return values of MessagePending
+
+ PENDINGMSG_CANCELCALL = 0; // cancel the outgoing call
+ PENDINGMSG_WAITNOPROCESS = 1; // wait for the return and don't dispatch the message
+ PENDINGMSG_WAITDEFPROCESS = 2; // wait and dispatch the message
+
+ EOAC_NONE = $0;
+ EOAC_MUTUAL_AUTH = $1;
+ EOAC_STATIC_CLOAKING = $20;
+ EOAC_DYNAMIC_CLOAKING = $40;
+ EOAC_ANY_AUTHORITY = $80;
+ EOAC_MAKE_FULLSIC = $100;
+ EOAC_DEFAULT = $800;
+
+ // These are only valid for CoInitializeSecurity
+ EOAC_SECURE_REFS = $2;
+ EOAC_ACCESS_CONTROL = $4;
+ EOAC_APPID = $8;
+ EOAC_DYNAMIC = $10;
+ EOAC_REQUIRE_FULLSIC = $200;
+ EOAC_AUTO_IMPERSONATE = $400;
+ EOAC_NO_CUSTOM_MARSHAL = $2000;
+ EOAC_DISABLE_AAA = $1000;
+
+//****************************************************************************
+//* ICOMThreadingInfo and enums
+//****************************************************************************/
+
+ APTTYPE_CURRENT = -1;
+ APTTYPE_STA = 0;
+ APTTYPE_MTA = 1;
+ APTTYPE_NA = 2;
+ APTTYPE_MAINSTA = 3;
+
+ THDTYPE_BLOCKMESSAGES = 0;
+ THDTYPE_PROCESSMESSAGES = 1;
+
+ DCOM_NONE = $0;
+ DCOM_CALL_COMPLETE = $1;
+ DCOM_CALL_CANCELED = $2;
+
+ COMBND_RPCTIMEOUT = $1; // Rpc transport-specific timeout.
+
+//************************* Misc Enums wtypes.h ***********************************/
+
+// Common typdefs used in API paramaters, gleamed from compobj.h
+
+// memory context values; passed to CoGetMalloc
+
+Const
+ MEMCTX_TASK = 1; // task (private) memory
+ MEMCTX_SHARED = 2; // shared memory (between processes)
+ MEMCTX_MACSYSTEM = 3; // on the mac, the system heap
+ // these are mostly for internal use...
+ MEMCTX_UNKNOWN = -1; // unknown context (when asked about it)
+ MEMCTX_SAME = -2; // same context (as some other pointer)
+
+
+// For IRunningObjectTable::Register
+ ROTFLAGS_REGISTRATIONKEEPSALIVE = $1;
+ ROTFLAGS_ALLOWANYCLIENT = $2;
+
+// Maximum size of comparison buffer for IROTData::GetComparisonData
+ ROT_COMPARE_MAX = 2048;
+
+
+// class context: used to determine what scope and kind of class object to use
+// NOTE: this is a bitwise enum
+
+ CLSCTX_INPROC_SERVER = $0001; // server dll (runs in same process as caller)
+ CLSCTX_INPROC_HANDLER = $0002; // handler dll (runs in same process as caller)
+ CLSCTX_LOCAL_SERVER = $0004; // server exe (runs on same machine; diff proc)
+ CLSCTX_INPROC_SERVER16 = $0008; // 16-bit server dll (runs in same process as caller)
+ CLSCTX_REMOTE_SERVER = $0010; // remote server exe (runs on different machine)
+ CLSCTX_INPROC_HANDLER16 = $0020; // 16-bit handler dll (runs in same process as caller)
+ CLSCTX_INPROC_SERVERX86 = $0040; // Wx86 server dll (runs in same process as caller)
+ CLSCTX_INPROC_HANDLERX86 = $0080; // Wx86 handler dll (runs in same process as caller)
+ CLSCTX_ESERVER_HANDLER = $0100; // handler dll (runs in the server process)
+ CLSCTX_RESERVED =$0200; // reserved
+ CLSCTX_NO_CODE_DOWNLOAD = $0400; // disallow code download from the Directory Service (if any) or the internet -rahulth
+ CLSCTX_NO_WX86_TRANSLATION = $0800;
+ CLSCTX_NO_CUSTOM_MARSHAL = $1000;
+ CLSCTX_ENABLE_CODE_DOWNLOAD = $2000; // allow code download from the Directory Service (if any) or the internet
+ CLSCTX_NO_FAILURE_LOG = $04000; // do not log messages about activation failure (should one occur) to Event Log
+ CLSCTX_DISABLE_AAA = $08000; // Disable EOAC_DISABLE_AAA capability for this activation only
+ CLSCTX_ENABLE_AAA = $10000; // Enable EOAC_DISABLE_AAA capability for this activation only
+ CLSCTX_FROM_DEFAULT_CONTEXT = $20000; // Begin this activation from the default context of the current apartment
+ CLSCTX_INPROC = (CLSCTX_INPROC_SERVER OR CLSCTX_INPROC_HANDLER);
+// With DCOM, CLSCTX_REMOTE_SERVER should be included
+ CLSCTX_ALL = (CLSCTX_INPROC_SERVER OR CLSCTX_INPROC_HANDLER OR CLSCTX_LOCAL_SERVER {$ifdef Remote} OR CLSCTX_REMOTE_SERVER {$endif});
+ CLSCTX_SERVER = (CLSCTX_INPROC_SERVER OR CLSCTX_LOCAL_SERVER {$ifdef Remote} OR CLSCTX_REMOTE_SERVER {$endif});
+
+
+// marshaling flags; passed to CoMarshalInterface
+ MSHLFLAGS_NORMAL = 0; // normal marshaling via proxy/stub
+ MSHLFLAGS_TABLESTRONG = 1; // keep object alive; must explicitly release
+ MSHLFLAGS_TABLEWEAK = 2; // doesn't hold object alive; still must release
+ MSHLFLAGS_NOPING = 4; // remote clients dont 'ping' to keep objects alive
+ MSHLFLAGS_RESERVED1 = 8; // reserved
+ MSHLFLAGS_RESERVED2 = 16; // reserved
+ MSHLFLAGS_RESERVED3 = 32; // reserved
+ MSHLFLAGS_RESERVED4 = 64; // reserved
+
+// marshal context: determines the destination context of the marshal operation
+
+ MSHCTX_LOCAL = 0; // unmarshal context is local (eg.shared memory)
+ MSHCTX_NOSHAREDMEM = 1; // unmarshal context has no shared memory access
+ MSHCTX_DIFFERENTMACHINE = 2; // unmarshal context is on a different machine
+ MSHCTX_INPROC = 3; // unmarshal context is on different thread
+ MSHCTX_CROSSCTX = 4; // unmarshal context is on different context
+
+// #########################################################################
+//
+// VARTYPE
+//
+// #########################################################################
+
+
+{
+ VARENUM usage key,
+
+ * [V] - may appear in a VARIANT
+ * [T] - may appear in a TYPEDESC
+ * [P] - may appear in an OLE property set
+ * [S] - may appear in a Safe Array
+
+
+ VT_EMPTY [V] [P] nothing
+ VT_NULL [V] [P] SQL style Null
+ VT_I2 [V][T][P][S] 2 byte signed int
+ VT_I4 [V][T][P][S] 4 byte signed int
+ VT_R4 [V][T][P][S] 4 byte real
+ VT_R8 [V][T][P][S] 8 byte real
+ VT_CY [V][T][P][S] currency
+ VT_DATE [V][T][P][S] date
+ VT_BSTR [V][T][P][S] OLE Automation string
+ VT_DISPATCH [V][T] [S] IDispatch *
+ VT_ERROR [V][T][P][S] SCODE
+ VT_BOOL [V][T][P][S] True=-1, False=0
+ VT_VARIANT [V][T][P][S] VARIANT *
+ VT_UNKNOWN [V][T] [S] IUnknown *
+ VT_DECIMAL [V][T] [S] 16 byte fixed point
+ VT_RECORD [V] [P][S] user defined type
+ VT_I1 [V][T][P][s] signed char
+ VT_UI1 [V][T][P][S] unsigned char
+ VT_UI2 [V][T][P][S] unsigned short
+ VT_UI4 [V][T][P][S] unsigned long
+ VT_I8 [T][P] signed 64-bit int
+ VT_UI8 [T][P] unsigned 64-bit int
+ VT_INT [V][T][P][S] signed machine int
+ VT_UINT [V][T] [S] unsigned machine int
+ VT_INT_PTR [T] signed machine register size width
+ VT_UINT_PTR [T] unsigned machine register size width
+ VT_VOID [T] C style void
+ VT_HRESULT [T] Standard return type
+ VT_PTR [T] pointer type
+ VT_SAFEARRAY [T] (use VT_ARRAY in VARIANT)
+ VT_CARRAY [T] C style array
+ VT_USERDEFINED [T] user defined type
+ VT_LPSTR [T][P] null terminated string
+ VT_LPWSTR [T][P] wide null terminated string
+ VT_FILETIME [P] FILETIME
+ VT_BLOB [P] Length prefixed bytes
+ VT_STREAM [P] Name of the stream follows
+ VT_STORAGE [P] Name of the storage follows
+ VT_STREAMED_OBJECT [P] Stream contains an object
+ VT_STORED_OBJECT [P] Storage contains an object
+ VT_VERSIONED_STREAM [P] Stream with a GUID version
+ VT_BLOB_OBJECT [P] Blob contains an object
+ VT_CF [P] Clipboard format
+ VT_CLSID [P] A Class ID
+ VT_VECTOR [P] simple counted array
+ VT_ARRAY [V] SAFEARRAY*
+ VT_BYREF [V] void* for local use
+ VT_BSTR_BLOB Reserved for system use
+}
+
+// VARENUM's
+
+ VT_EMPTY = 0;
+ VT_NULL = 1;
+ VT_I2 = 2;
+ VT_I4 = 3;
+ VT_R4 = 4;
+ VT_R8 = 5;
+ VT_CY = 6;
+ VT_DATE = 7;
+ VT_BSTR = 8;
+ VT_DISPATCH = 9;
+ VT_ERROR = 10;
+ VT_BOOL = 11;
+ VT_VARIANT = 12;
+ VT_UNKNOWN = 13;
+ VT_DECIMAL = 14;
+// VBA reserves 15 for future use
+ VT_I1 = 16;
+ VT_UI1 = 17;
+ VT_UI2 = 18;
+ VT_UI4 = 19;
+ VT_I8 = 20;
+ VT_UI8 = 21;
+ VT_INT = 22;
+ VT_UINT = 23;
+ VT_VOID = 24;
+ VT_HRESULT = 25;
+ VT_PTR = 26;
+ VT_SAFEARRAY = 27;
+ VT_CARRAY = 28;
+ VT_USERDEFINED = 29;
+ VT_LPSTR = 30;
+ VT_LPWSTR = 31;
+// VBA reserves 32-35 for future use
+ VT_RECORD = 36;
+ VT_INT_PTR = 37;
+ VT_UINT_PTR = 38;
+
+ VT_FILETIME = 64;
+ VT_BLOB = 65;
+ VT_STREAM = 66;
+ VT_STORAGE = 67;
+ VT_STREAMED_OBJECT = 68;
+ VT_STORED_OBJECT = 69;
+ VT_BLOB_OBJECT = 70;
+ VT_CF = 71;
+ VT_CLSID = 72;
+ VT_VERSIONED_STREAM = 73;
+
+ VT_BSTR_BLOB = $0fff;
+
+ VT_VECTOR = $1000;
+ VT_ARRAY = $2000;
+ VT_BYREF = $4000;
+ VT_RESERVED = $8000;
+
+ VT_ILLEGAL = $ffff;
+ VT_ILLEGALMASKED = $0fff;
+ VT_TYPEMASK = $0fff;
+
+
+
+//
+// Common typedefs for paramaters used in data view API's, gleamed
+// from dvobj.h
+//
+
+// Data/View aspect; specifies the desired aspect of the object when
+// drawing or getting data.
+
+ DVASPECT_CONTENT = 1;
+ DVASPECT_THUMBNAIL = 2;
+ DVASPECT_ICON = 4;
+ DVASPECT_DOCPRINT = 8;
+
+//****** Storage types *************************************************
+
+
+// Storage commit types
+
+ STGC_DEFAULT = 0;
+ STGC_OVERWRITE = 1;
+ STGC_ONLYIFCURRENT = 2;
+ STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE = 4;
+ STGC_CONSOLIDATE = 8;
+
+ STGMOVE_MOVE = 0;
+ STGMOVE_COPY = 1;
+ STGMOVE_SHALLOWCOPY = 2;
+
+ STATFLAG_DEFAULT = 0;
+ STATFLAG_NONAME = 1;
+ STATFLAG_NOOPEN = 2;
+
+// #########################################################################
+//
+// Constants for the call context
+//
+
+ WDT_INPROC_CALL = ULONG($48746457);
+ WDT_REMOTE_CALL = ULONG($52746457);
+ WDT_INPROC64_CALL = ULONG($50746457);
+
+ COLE_DEFAULT_PRINCIPAL {: pOleStr?} = pOleStr(-1);
+ COLE_DEFAULT_AUTHINFO {: pointer?} = pointer(-1);
+
+// DISPID reserved to indicate an \"unknown\" name
+// only reserved for data members (properties); reused as a method dispid below
+
+ DISPID_UNKNOWN = -1;
+
+// DISPID reserved for the \"value\" property
+
+ DISPID_VALUE = 0;
+
+// The following DISPID is reserved to indicate the param")
+// that is the right-hand-side (or \"put\" value) of a PropertyPut")
+
+ DISPID_PROPERTYPUT = -3;
+
+// DISPID reserved for the standard \"NewEnum\" method
+
+ DISPID_NEWENUM = -4;
+
+// DISPID reserved for the standard \"Evaluate\" method
+
+ DISPID_EVALUATE = -5;
+ DISPID_CONSTRUCTOR = -6;
+ DISPID_DESTRUCTOR = -7;
+ DISPID_COLLECT = -8;
+
+ DISPATCH_METHOD = $1;
+ DISPATCH_PROPERTYGET = $2;
+ DISPATCH_PROPERTYPUT = $4;
+ DISPATCH_PROPERTYPUTREF = $8;
+
+ USERCLASSTYPE_FULL = 1;
+ USERCLASSTYPE_SHORT = 2;
+ USERCLASSTYPE_APPNAME = 3;
+
+// The range -500 through -999 is reserved for Controls
+// The range 0x80010000 through 0x8001FFFF is reserved for Controls
+// The range -5000 through -5499 is reserved for ActiveX Accessability
+// The range -2000 through -2499 is reserved for VB5
+// The range -3900 through -3999 is reserved for Forms
+// The range -5500 through -5550 is reserved for Forms
+// The remainder of the negative DISPIDs are reserved for future use
+
+
+ DESCKIND_NONE = 0;
+ DESCKIND_FUNCDESC = 1;
+ DESCKIND_VARDESC = 2;
+ DESCKIND_TYPECOMP = 3;
+ DESCKIND_IMPLICITAPPOBJ = 4;
+ DESCKIND_MAX = 5;
+
+
+ SYS_WIN16 = 0;
+ SYS_WIN32 = 1;
+ SYS_MAC = 2;
+ SYS_WIN64 = 3;
+
+ REGKIND_DEFAULT = 0;
+ REGKIND_REGISTER = 1;
+ REGKIND_NONE = 2;
+
+ INTERFACESAFE_FOR_UNTRUSTED_CALLER = $00000001; // Caller of interface may be untrusted
+ INTERFACESAFE_FOR_UNTRUSTED_DATA = $00000002; // Data passed into interface may be untrusted
+ INTERFACE_USES_DISPEX = $00000004; // Object knows to use IDispatchEx
+ INTERFACE_USES_SECURITY_MANAGER = $00000008; // Object knows to use IInternetHostSecurityManager
+
+ LIBFLAG_FRESTRICTED = $01;
+ LIBFLAG_FCONTROL = $02;
+ LIBFLAG_FHIDDEN = $04;
+ LIBFLAG_FHASDISKIMAGE = $08;
+ TYPEFLAG_FAPPOBJECT = $01;
+ TYPEFLAG_FCANCREATE = $02;
+ TYPEFLAG_FLICENSED = $04;
+ TYPEFLAG_FPREDECLID = $08;
+ TYPEFLAG_FHIDDEN = $10;
+ TYPEFLAG_FCONTROL = $20;
+ TYPEFLAG_FDUAL = $40;
+ TYPEFLAG_FNONEXTENSIBLE = $80;
+ TYPEFLAG_FOLEAUTOMATION = $100;
+ TYPEFLAG_FRESTRICTED = $200;
+ TYPEFLAG_FAGGREGATABLE = $400;
+ TYPEFLAG_FREPLACEABLE = $800;
+ TYPEFLAG_FDISPATCHABLE = $1000;
+ TYPEFLAG_FREVERSEBIND = $2000;
+ TYPEFLAG_FPROXY = $4000;
+
+ FUNCFLAG_FRESTRICTED = $1;
+ FUNCFLAG_FSOURCE = $2;
+ FUNCFLAG_FBINDABLE = $4;
+ FUNCFLAG_FREQUESTEDIT = $8;
+ FUNCFLAG_FDISPLAYBIND = $10;
+ FUNCFLAG_FDEFAULTBIND = $20;
+ FUNCFLAG_FHIDDEN = $40;
+ FUNCFLAG_FUSESGETLASTERROR = $80;
+ FUNCFLAG_FDEFAULTCOLLELEM = $100;
+ FUNCFLAG_FUIDEFAULT = $200;
+ FUNCFLAG_FNONBROWSABLE = $400;
+ FUNCFLAG_FREPLACEABLE = $800;
+ FUNCFLAG_FIMMEDIATEBIND = $1000;
+
+ VARFLAG_FREADONLY = $1;
+ VARFLAG_FSOURCE = $2;
+ VARFLAG_FBINDABLE = $4;
+ VARFLAG_FREQUESTEDIT = $8;
+ VARFLAG_FDISPLAYBIND = $10;
+ VARFLAG_FDEFAULTBIND = $20;
+ VARFLAG_FHIDDEN = $40;
+ VARFLAG_FRESTRICTED = $80;
+ VARFLAG_FDEFAULTCOLLELEM = $100;
+ VARFLAG_FUIDEFAULT = $200;
+ VARFLAG_FNONBROWSABLE = $400;
+ VARFLAG_FREPLACEABLE = $800;
+ VARFLAG_FIMMEDIATEBIND = $1000;
+
+ FADF_AUTO = USHORT($0001); // array is allocated on the stack
+ FADF_STATIC = USHORT($0002); // array is staticly allocated
+ FADF_EMBEDDED = USHORT($0004); // array is embedded in a structure
+ FADF_FIXEDSIZE = USHORT($0010); // may not be resized or reallocated
+ FADF_RECORD = USHORT($0020); // an array of records
+ FADF_HAVEIID = USHORT($0040); // with FADF_DISPATCH, FADF_UNKNOWN
+ // array has an IID for interfaces
+ FADF_HAVEVARTYPE = USHORT($0080); // array has a VT type
+ FADF_BSTR = USHORT($0100); // an array of BSTRs
+ FADF_UNKNOWN = USHORT($0200); // an array of IUnknown*
+ FADF_DISPATCH = USHORT($0400); // an array of IDispatch*
+ FADF_VARIANT = USHORT($0800); // an array of VARIANTs
+ FADF_RESERVED = USHORT($F008); // reserved bits
+
+// IMPLTYPE Flags
+
+ IMPLTYPEFLAG_FDEFAULT = USHORT($1);
+ IMPLTYPEFLAG_FSOURCE = USHORT($2);
+ IMPLTYPEFLAG_FRESTRICTED = USHORT($4);
+ IMPLTYPEFLAG_FDEFAULTVTABLE = USHORT($8);
+
+ PARAMFLAG_NONE = USHORT($00);
+ PARAMFLAG_FIN = USHORT($01);
+ PARAMFLAG_FOUT = USHORT($02);
+ PARAMFLAG_FLCID = USHORT($04);
+ PARAMFLAG_FRETVAL = USHORT($08);
+ PARAMFLAG_FOPT = USHORT($10);
+ PARAMFLAG_FHASDEFAULT = USHORT($20);
+ PARAMFLAG_FHASCUSTDATA = USHORT($40);
+
+ VAR_PERINSTANCE = 0;
+ VAR_STATIC = 1;
+ VAR_CONST = 2;
+ VAR_DISPATCH = 3;
+
+ // notification messages used by the dynamic typeinfo protocol.
+
+ CHANGEKIND_ADDMEMBER = 0;
+ CHANGEKIND_DELETEMEMBER = 1;
+ CHANGEKIND_SETNAMES = 2;
+ CHANGEKIND_SETDOCUMENTATION = 3;
+ CHANGEKIND_GENERAL = 4;
+ CHANGEKIND_INVALIDATE = 5;
+ CHANGEKIND_CHANGEFAILED = 6;
+ CHANGEKIND_MAX = 7;
+
+ INVOKE_FUNC = 1;
+ INVOKE_PROPERTYGET = 2;
+ INVOKE_PROPERTYPUT = 4;
+ INVOKE_PROPERTYPUTREF = 8;
+
+ TKIND_ENUM = 0;
+ TKIND_RECORD = 1;
+ TKIND_MODULE = 2;
+ TKIND_INTERFACE = 3;
+ TKIND_DISPATCH = 4;
+ TKIND_COCLASS = 5;
+ TKIND_ALIAS = 6;
+ TKIND_UNION = 7;
+ TKIND_MAX = 8; // end of enum marker
+
+ SF_ERROR = VT_ERROR;
+ SF_I1 = VT_I1;
+ SF_I2 = VT_I2;
+ SF_I4 = VT_I4;
+ SF_I8 = VT_I8;
+ SF_BSTR = VT_BSTR;
+ SF_UNKNOWN = VT_UNKNOWN;
+ SF_DISPATCH = VT_DISPATCH;
+ SF_VARIANT = VT_VARIANT;
+ SF_RECORD = VT_RECORD;
+ SF_HAVEIID = VT_UNKNOWN OR VT_RESERVED;
+ IDLFLAG_NONE = PARAMFLAG_NONE;
+ IDLFLAG_FIN = PARAMFLAG_FIN;
+ IDLFLAG_FOUT = PARAMFLAG_FOUT;
+ IDLFLAG_FLCID = PARAMFLAG_FLCID;
+ IDLFLAG_FRETVAL = PARAMFLAG_FRETVAL;
+
+ CC_FASTCALL = 0;
+ CC_CDECL = 1;
+ CC_MSCPASCAL = 2;
+ CC_PASCAL = CC_MSCPASCAL;
+ CC_MACPASCAL = 3;
+ CC_STDCALL = 4;
+ CC_FPFASTCALL = 5;
+ CC_SYSCALL = 6;
+ CC_MPWCDECL = 7;
+ CC_MPWPASCAL = 8;
+ CC_MAX = 9; // end of enum marker
+
+ FUNC_VIRTUAL = 0;
+ FUNC_PUREVIRTUAL = 1;
+ FUNC_NONVIRTUAL = 2;
+ FUNC_STATIC = 3;
+ FUNC_DISPATCH = 4;
+
+// objbase.h
+
+ MARSHALINTERFACE_MIN = 500; // minimum number of bytes for interface marshl
+
+//
+// Common typedefs for paramaters used in Storage API's, gleamed from storage.h
+// Also contains Storage error codes, which should be moved into the storage
+// idl files.
+//
+
+
+ CWCSTORAGENAME = 32;
+
+// Storage instantiation modes
+ STGM_DIRECT = $00000000;
+ STGM_TRANSACTED = $00010000;
+ STGM_SIMPLE = $08000000;
+ STGM_READ = $00000000;
+ STGM_WRITE = $00000001;
+ STGM_READWRITE = $00000002;
+ STGM_SHARE_DENY_NONE = $00000040;
+ STGM_SHARE_DENY_READ = $00000030;
+ STGM_SHARE_DENY_WRITE = $00000020;
+ STGM_SHARE_EXCLUSIVE = $00000010;
+ STGM_PRIORITY = $00040000;
+ STGM_DELETEONRELEASE = $04000000;
+ STGM_NOSCRATCH = $00100000; {WINNT+}
+ STGM_CREATE = $00001000;
+ STGM_CONVERT = $00020000;
+ STGM_FAILIFTHERE = $00000000;
+ STGM_NOSNAPSHOT = $00200000;
+ STGM_DIRECT_SWMR = $00400000; { Win2000+}
+
+// flags for internet asyncronous and layout docfile
+ ASYNC_MODE_COMPATIBILITY = $00000001;
+ ASYNC_MODE_DEFAULT = $00000000;
+
+ STGTY_REPEAT = $00000100;
+ STG_TOEND = $FFFFFFFF;
+
+ STG_LAYOUT_SEQUENTIAL = $00000000;
+ STG_LAYOUT_INTERLEAVED = $00000001;
+
+ STGFMT_STORAGE = 0;
+ STGFMT_NATIVE = 1;
+ STGFMT_FILE = 3;
+ STGFMT_ANY = 4;
+ STGFMT_DOCFILE = 5;
+
+// This is a legacy define to allow old component to builds
+ STGFMT_DOCUMENT = 0;
+
+
+// COM initialization flags; passed to CoInitialize.
+ COINIT_APARTMENTTHREADED = $2; // Apartment model
+
+ // These constants are only valid on Windows NT 4.0
+
+ COINIT_MULTITHREADED = $0; // OLE calls objects on any thread.
+ COINIT_DISABLE_OLE1DDE = $4; // Don't use DDE for Ole1 support.
+ COINIT_SPEED_OVER_MEMORY = $8; // Trade memory for speed.
+
+ SMEXF_SERVER = $01; // server side aggregated std marshaler
+ SMEXF_HANDLER = $02; // client side (handler) agg std marshaler
+
+ COWAIT_WAITALL = 1;
+ COWAIT_ALERTABLE = 2;
+
+ DOCMISC_CANCREATEMULTIPLEVIEWS = 1;
+ DOCMISC_SUPPORTCOMPLEXRECTANGLES = 2;
+ DOCMISC_CANTOPENEDIT = 4; // fails the IOleDocumentView::Open method
+ DOCMISC_NOFILESUPPORT = 8; // does not support read/writing to a file
+
+ PID_DICTIONARY = PROPID($00000000);
+ PID_CODEPAGE = PROPID($00000001);
+ PID_FIRST_USABLE = PROPID($00000002);
+ PID_FIRST_NAME_DEFAULT = PROPID($00000fff);
+ PID_LOCALE = PROPID($80000000);
+ PID_MODIFY_TIME = PROPID($80000001);
+ PID_SECURITY = PROPID($80000002);
+ PID_BEHAVIOR = PROPID($80000003);
+ PID_ILLEGAL = PROPID($ffffffff);
+ PID_MIN_READONLY = PROPID($80000000);
+ PID_MAX_READONLY = PROPID($bfffffff);
+ PIDDI_THUMBNAIL = DWORD($00000002);// VT_BLOB
+ PIDSI_TITLE = DWORD($00000002); // VT_LPSTR
+ PIDSI_SUBJECT = DWORD($00000003); // VT_LPSTR
+ PIDSI_AUTHOR = DWORD($00000004); // VT_LPSTR
+ PIDSI_KEYWORDS = DWORD($00000005); // VT_LPSTR
+ PIDSI_COMMENTS = DWORD($00000006); // VT_LPSTR
+ PIDSI_TEMPLATE = DWORD($00000007); // VT_LPSTR
+ PIDSI_LASTAUTHOR = DWORD($00000008); // VT_LPSTR
+ PIDSI_REVNUMBER = DWORD($00000009); // VT_LPSTR
+ PIDSI_EDITTIME = DWORD($0000000a); // VT_FILETIME
+ PIDSI_LASTPRINTED = DWORD($0000000b); // VT_FILETIME
+ PIDSI_CREATE_DTM = DWORD($0000000c); // VT_FILETIME
+ PIDSI_LASTSAVE_DTM = DWORD($0000000d); // VT_FILETIME
+ PIDSI_PAGECOUNT = DWORD($0000000e); // VT_I4
+ PIDSI_WORDCOUNT = DWORD($0000000f); // VT_I4
+ PIDSI_CHARCOUNT = DWORD($00000010); // VT_I4
+ PIDSI_THUMBNAIL = DWORD($00000011); // VT_CF
+ PIDSI_APPNAME = DWORD($00000012); // VT_LPSTR
+ PIDSI_DOC_SECURITY = DWORD($00000013); // VT_I4
+ PIDDSI_CATEGORY = $00000002; // VT_LPSTR
+ PIDDSI_PRESFORMAT = $00000003; // VT_LPSTR
+ PIDDSI_BYTECOUNT = $00000004; // VT_I4
+ PIDDSI_LINECOUNT = $00000005; // VT_I4
+ PIDDSI_PARCOUNT = $00000006; // VT_I4
+ PIDDSI_SLIDECOUNT = $00000007; // VT_I4
+ PIDDSI_NOTECOUNT = $00000008; // VT_I4
+ PIDDSI_HIDDENCOUNT = $00000009; // VT_I4
+ PIDDSI_MMCLIPCOUNT = $0000000A; // VT_I4
+ PIDDSI_SCALE = $0000000B; // VT_BOOL
+ PIDDSI_HEADINGPAIR = $0000000C; // VT_VARIANT |VT_VECTOR
+ PIDDSI_DOCPARTS = $0000000D; // VT_LPSTR |VT_VECTOR
+ PIDDSI_MANAGER = $0000000E; // VT_LPSTR
+ PIDDSI_COMPANY = $0000000F; // VT_LPSTR
+ PIDDSI_LINKSDIRTY = $00000010; // VT_BOOL
+
+// FMTID_MediaFileSummaryInfo - Property IDs
+
+ PIDMSI_EDITOR = DWord($00000002); // VT_LPWSTR
+ PIDMSI_SUPPLIER = DWord($00000003); // VT_LPWSTR
+ PIDMSI_SOURCE = DWord($00000004); // VT_LPWSTR
+ PIDMSI_SEQUENCE_NO = DWord($00000005); // VT_LPWSTR
+ PIDMSI_PROJECT = DWord($00000006); // VT_LPWSTR
+ PIDMSI_STATUS = DWord($00000007); // VT_UI4
+ PIDMSI_OWNER = DWord($00000008); // VT_LPWSTR
+ PIDMSI_RATING = DWord($00000009); // VT_LPWSTR
+ PIDMSI_PRODUCTION = DWord($0000000A); // VT_FILETIME (UTC)
+ PIDMSI_COPYRIGHT = DWord($0000000B); // VT_LPWSTR
+ PRSPEC_INVALID = ULONG($ffffffff);
+ PRSPEC_LPWSTR = ULONG(0);
+ PRSPEC_PROPID = ULONG(1);
+ PROPSETFLAG_DEFAULT = DWORD(0);
+ PROPSETFLAG_NONSIMPLE = DWORD(1);
+ PROPSETFLAG_ANSI = DWORD(2);
+
+TYPE
+ VARTYPE = USHORT;
+
+//TypeInfo stuff.
+
+ DISPID = Long ;
+ SCODE = Long;
+ pSCODE = ^SCODE;
+ lpDISPID = ^DISPID;
+ MEMBERID = DispId;
+ HREFTYPE = DWord;
+ TResultList = array[0..high(integer) div 4-50] of HResult;
+ PResultList = ^TResultList;
+
+ PSYSINT = ^SYSINT;
+ SYSINT = LongInt;
+ PSYSUINT = ^SYSUINT;
+ SYSUINT = LongWord;
+
+// Enums
+ VARKIND = DWord;
+ DESCKIND = DWord;
+ SYSKIND = DWord;
+ TSYSKIND = SYSKIND;
+ REGKIND = DWord;
+ TREGKIND = REGKIND;
+ FUNCKIND = DWord;
+ CHANGEKIND = DWord;
+ CALLCONV = DWord;
+
+ PIDMSI_STATUS_VALUE = (
+ PIDMSI_STATUS_NORMAL = 0,
+ PIDMSI_STATUS_NEW,
+ PIDMSI_STATUS_PRELIM,
+ PIDMSI_STATUS_DRAFT,
+ PIDMSI_STATUS_INPROGRESS,
+ PIDMSI_STATUS_EDIT,
+ PIDMSI_STATUS_REVIEW,
+ PIDMSI_STATUS_PROOF,
+ PIDMSI_STATUS_FINAL,
+ PIDMSI_STATUS_OTHER = $7FFF
+ );
+ TPIDMSI_STATUS_VALUE= PIDMSI_STATUS_Value;
+
+
+
+ PCOAUTHIDENTITY = ^TCOAUTHIDENTITY;
+ _COAUTHIDENTITY = Record
+ User : PUSHORT;
+ UserLength : ULONG;
+ Domain : PUSHORT;
+ DomainLength : ULong;
+ Password : PUSHORT;
+ PassWordLength : ULong;
+ Flags : ULong;
+ End;
+
+ COAUTHIDENTITY = _COAUTHIDENTITY;
+ TCOAUTHIDENTITY = _COAUTHIDENTITY;
+
+ PCOAUTHINFO = ^TCOAUTHINFO;
+ COAuthInfo = Record
+ AuthnSvc : DWord;
+ AuthzSvc : DWord;
+ ServerPrincName : LPWSTR;
+ AuthnLevel : DWord;
+ ImpersonationLevel : DWord;
+ AuthIdentityData : PCOAUTHIDENTITY;
+ Capabilities : DWord;
+ END;
+ TCOAUTHINFO = COAUTHINFO;
+
+ PCOSERVERINFO = ^TCOSERVERINFO;
+ _COSERVERINFO = Record
+ dwReserved1 : DWord;
+ pwszName : LPWSTR;
+ pAuthInfo : PCoAuthInfo;
+ dwReserved2 : DWord;
+ end;
+ TCOSERVERINFO = _COSERVERINFO;
+ PMultiQI = ^Multi_QI;
+ tagMULTI_QI = Record
+ iid: piid; // pass this one in
+ itf: IUnknown; // get these out (you must set to NULL before calling)
+ hr : HResult;
+ END;
+ MULTI_QI = TagMULTI_QI;
+ PMulti_QI = PMultiQI;
+ TMultiQI = tagMULTI_QI;
+
+ PMultiQIArray = ^TMultiQIArray;
+ TMultiQIArray = array[0..65535] of TMultiQI;
+
+
+ HContext = Pointer;
+ ApartmentID = DWord;
+
+
+//****** Critical Section Wrappers ***********************************
+
+// LCID = WORD;
+// LANGID = USHORT;
+
+// #######################################################################
+//
+// User marshal support for Windows data types.
+
+//
+// Frequently used helpers: sized blobs
+//
+// Never put [user_marshal] or [wire_marshal] on the helpers directly.
+//
+
+// Flagged blobs.
+
+ _FLAGGED_BYTE_BLOB = Record
+ fFlags : ULong;
+ clSize : ULong;
+ abdata : array[0..0] of byte;
+ End;
+ FLAGGED_BYTE_BLOB = _FLAGGED_BYTE_BLOB;
+ UP_FLAGGED_BYTE_BLOB = ^FLAGGED_BYTE_BLOB;
+
+ _FLAGGED_WORD_BLOB = Record
+ fFlags : ULong;
+ clSize : ULong;
+ abdata : array[0..0] of USHORT;
+ End;
+ FLAGGED_WORD_BLOB = _FLAGGED_WORD_BLOB;
+ UP_FLAGGED_WORD_BLOB = ^FLAGGED_WORD_BLOB;
+
+ _FLAGGED_DWORD_BLOB = Record
+ fFlags : ULong;
+ clSize : ULong;
+ abdata : array[0..0] of ULONG;
+ End;
+ FLAGGED_DWORD_BLOB = _FLAGGED_DWORD_BLOB;
+ FLAGGED_UP_DWORD_BLOB = ^FLAGGED_DWORD_BLOB;
+
+// Simple blobs.
+
+ _BYTE_BLOB = Record
+ clSize : ULong;
+ abdata : array[0..0] of byte;
+ End;
+ BYTE_BLOB = _BYTE_BLOB;
+ UP_BYTE_BLOB = ^BYTE_BLOB;
+
+ _WORD_BLOB = Record
+ clSize : ULong;
+ abdata : array[0..0] of USHORT;
+ End;
+ WORD_BLOB = _WORD_BLOB;
+ UP_WORD_BLOB = ^WORD_BLOB;
+
+ _DWORD_BLOB = Record
+ clSize : ULong;
+ abdata : array[0..0] of ULONG;
+ End;
+ DWORD_BLOB = _DWORD_BLOB;
+ UP_DWORD_BLOB = ^DWORD_BLOB;
+
+// Frequently used helpers with sized pointers.
+
+ _BYTE_SIZEDARR = Record
+ clsize : ULong;
+ Data : PByte;
+ End;
+ BYTE_SIZEDARR = _BYTE_SIZEDARR;
+
+ _SHORT_SIZEDARR = Record
+ clsize : ULong;
+ Data : PSHORT;
+ End;
+ SHORT_SIZEDARR = _SHORT_SIZEDARR;
+
+ _LONG_SIZEDARR = Record
+ clsize : ULong;
+ Data : PLONG;
+ End;
+ LONG_SIZEDARR = _LONG_SIZEDARR;
+ HYPER = LONGLONG;
+ PHYPER = ^HYPER;
+ _HYPER_SIZEDARR = Record
+ clsize : ULong;
+ Data : PHYPER;
+ End;
+ HYPER_SIZEDARR = _HYPER_SIZEDARR;
+
+
+// #########################################################################
+//
+// CLIPFORMAT
+//
+
+ userCLIPFORMAT = Record
+ FContext : Long;
+ CASE INTEGER OF
+ 0 : (dwvalue : DWORD);
+ 1 : (szName : poleStr);
+ End;
+
+ wireCLIPFORMAT = ^userCLIPFORMAT;
+
+
+// #########################################################################
+//
+// Good for most of the gdi handles.
+
+ _GDI_NONREMOTE = Record
+ FContext : Long;
+ Case Integer Of
+ 0 : (HInProc : Long);
+ 1 : (HRemote : DWORD_BLOB);
+ END;
+ GDI_NONREMOTE = _GDI_NONREMOTE;
+
+// #########################################################################
+//
+// HGLOBAL
+//
+// A global may be Null or may be non-NULL with 0 length.
+
+ _userHGLOBAL = Record
+ FContext : Long;
+ CASE Integer OF
+ 0 : (hInproc : Long);
+ 1 : (hRemote : UP_FLAGGED_BYTE_BLOB);
+ 2 : (hInproc64: int64);
+ End;
+ userHGlobal = _userHGLOBAL;
+ wireHGLOBAL = ^userHGLOBAL;
+
+// #########################################################################
+//
+// HMETAFILE
+//
+ _userHMETAFILE = Record
+ fContext : Long;
+ Case Integer OF
+ 0 : (hInproc : Long);
+ 1 : (hRemote : up_byte_blob);
+ 2 : (hInProc64 : Int64);
+ End;
+ userHMETAFILE = _userHMETAFILE;
+ puserHMETAFILE = ^userHMETAFILE;
+
+// #########################################################################
+//
+// HMETAFILEPICT
+//
+
+ _remoteMETAFILEPICT = Record
+ mm : Long;
+ xExt : Long;
+ yExt : Long;
+ mgf : puserHMETAFILE;
+ End;
+
+ remoteMETAFILEPICT = _remoteMETAFILEPICT;
+ premoteMETAFILEPICT = ^remoteMETAFILEPICT;
+
+ _userHMETAFILEPICT = Record
+ fContext : Long;
+ Case Integer OF
+ 0 : (hInproc : Long);
+ 1 : (hRemote : premoteMETAFILEPICT);
+ 2 : (hInProc64 : Int64);
+ End;
+ userHMETAFILEPICT = _userHMETAFILEPICT;
+
+
+// #########################################################################
+//
+// HENHMETAFILE
+//
+
+ _userHENHMETAFILE = Record
+ fContext : Long;
+ Case Integer OF
+ 0 : (hInproc : Long);
+ 1 : (hRemote : up_byte_blob);
+ 2 : (hInProc64 : Int64);
+ End;
+ userHENHMETAFILE = _userHENHMETAFILE;
+ puserHENHMETAFILE = ^userHENHMETAFILE;
+
+// #########################################################################
+//
+// HBITMAP
+//
+
+// RemHBITMAP was just a byte blob, but the whole bitmap structure was copied
+// at the beginning of the buffer.
+
+// So, we take BITMAP fields from wingdi.x
+
+
+ _userBITMAP = Record
+ bmType,
+ bmWidth,
+ bmHeight,
+ bmWidthBytes : Long;
+ bmPlanes,
+ bmBitsPixel : Word;
+ cvsize : ULONG;
+ buffer : pbyte;
+ End;
+
+ userBITMAP = _userBITMAP;
+ puserBITMAP = ^userBITMAP;
+
+ _userHBITMAP = Record
+ fContext : Long;
+ Case Integer OF
+ 0 : (hInproc : Long);
+ 1 : (hRemote : puserBITMAP);
+ 2 : (hInProc64 : Int64);
+ End;
+ userHBITMAP = _userHBITMAP;
+ puserHBITMAP = ^userHBITMAP;
+
+
+// #########################################################################
+//
+// HPALETTE
+//
+
+// PALETTEENTRY is in wingdi.x, it is a struct with 4 bytes.
+// LOGPALETTE is in wingdi.x, it is a conf struct with paletteentries and
+// a version field
+
+ _userHpalette = Record
+ fContext : Long;
+ Case Integer OF
+ 0 : (hInproc : Long);
+ 1 : (hRemote : logpalette);
+ 2 : (hInProc64 : Int64);
+ End;
+ userHpalette = _userHpalette;
+ puserHpalette = ^userHpalette;
+
+// #########################################################################
+//
+// Handles passed locally as longs.
+//
+
+ _RemotableHandle = Record
+ fContext : Long;
+ Case Integer OF
+ 0 : (hInproc : Long);
+ 1 : (hRemote : Long);
+ End;
+ RemotableHandle = _RemotableHandle;
+
+
+ wireHWND = ^RemotableHandle;
+ wireHMENU = ^RemotableHandle;
+ wireHACCEL = ^RemotableHandle;
+ wireHBRUSH = ^RemotableHandle;
+ wireHFONT = ^RemotableHandle;
+ wireHDC = ^RemotableHandle;
+ wireHICON = ^RemotableHandle;
+ HCursor = HICON;
+
+
+ tagTEXTMETRICW = Record
+ tmHeight,
+ tmAscent,
+ tmDescent,
+ tmInternalLeading,
+ tmExternalLeading,
+ tmAveCharWidth,
+ tmMaxCharWidth,
+ tmWeight,
+ tmOverhang,
+ tmDigitizedAspectX,
+ tmDigitizedAspectY : Long;
+ tmFirstChar,
+ tmLastChar,
+ tmDefaultChar,
+ tmBreakChar : WCHAR;
+ tmItalic,
+ tmUnderlined,
+ tmStruckOut,
+ tmPitchAndFamily,
+ tmCharSet : BYTE;
+ End;
+
+ TEXTMETRICW = tagTEXTMETRICW;
+ PTEXTMETRICW = ^TEXTMETRICW;
+ LPTEXTMETRICW = PTEXTMETRICW;
+ wireHBITMAP = ^userHBITMAP;
+ wireHPALETTE = ^userHPALETTE;
+ wireHENHMETAFILE = ^userHENHMETAFILE;
+ wireHMETAFILE = ^userHMETAFILE;
+ wireHMETAFILEPICT = ^userHMETAFILEPICT;
+ HMetaFilePict = Pointer;
+ HLOCAL = HGLOBAL;
+// Date = Double;
+
+{****************************************************************************
+ * Binding Interfaces
+ ****************************************************************************}
+
+ tagBIND_OPTS = Record
+ cvStruct, // sizeof(BIND_OPTS)
+ grfFlags,
+ grfMode,
+ dwTickCountDeadline : DWord;
+ End;
+ TBind_Opts = tagBIND_OPTS;
+ PBind_Opts = ^TBind_Opts;
+ TBindOpts = tagBIND_OPTS;
+ PBindOpts = ^TBindOpts;
+ Bind_Opts = tagBind_opts;
+
+ tagBIND_OPTS2_CPP = Record
+ dwTrackFlags,
+ dwClassContext : Dword;
+ Locale : LCID;
+ ServerInfo : pCoServerInfo;
+ End;
+
+ TBind_Opts2_CPP = tagBIND_OPTS2_CPP;
+ PBind_Opts2_CPP = ^TBind_Opts2_CPP;
+
+
+ tagBind_OPTS2 = Record
+ cvStruct, // sizeof(BIND_OPTS)
+ grfFlags,
+ grfMode,
+ dwTickCountDeadline : DWord;
+ dwTrackFlags,
+ dwClassContext : DWord;
+ Locale : LCID;
+ ServerInfo : pCoServerInfo;
+ End;
+
+ TBind_Opts2 = tagBIND_OPTS2;
+ PBind_Opts2 = ^TBind_Opts2;
+
+// ****************************************************************************
+// * Structured Storage Interfaces
+// ****************************************************************************
+
+
+
+ tagSTATSTG = types.tagSTATSTG;
+
+ TStatStg = tagSTATSTG;
+ PStatStg = types.PStatStg;
+ STATSTG = TStatStg;
+
+{ TagRemSNB = Record
+ ulCntStr : ULong;
+ ulCntChar : ULong;
+ [size_is(ulCntChar)] OLECHAR rgString[];
+ End;
+ RemSNB=TagRemSNB
+ WireSNB=^RemSNB}
+ SNB = ^PoleStr;
+ tagDVTARGETDEVICE = Record
+ tdSize : DWord;
+ tdDriverNameOffset,
+ tdDeviceNameOffset,
+ tdPortNameOffset,
+ tdExtDevmodeOffset : Word;
+ Data : Record End;
+ End;
+
+ DVTARGETDEVICE = TagDVTARGETDEVICE;
+ PDVTARGETDEVICE = ^tagDVTARGETDEVICE;
+ LPCLIPFORMAT = ^TCLIPFORMAT;
+ TCLIPFORMAT = Word;
+ CLIPFORMAT = TCLIPFORMAT;
+ PClipFormat = LPCLIPFORMAT;
+
+ tagFORMATETC = Record
+ CfFormat : Word {TCLIPFORMAT};
+ Ptd : PDVTARGETDEVICE;
+ dwAspect : DWORD;
+ lindex : Long;
+ tymed : DWORD;
+ End;
+ FORMATETC = TagFORMATETC;
+ TFORMATETC = FORMATETC;
+ LPFORMATETC = ^FORMATETC;
+ PFormatEtc = LPFORMATETC;
+
+ // Stats for data; used by several enumerations and by at least one
+ // implementation of IDataAdviseHolder; if a field is not used, it
+ // will be NULL.
+
+
+ tagRemSTGMEDIUM = Record
+ tymed : DWord;
+ dwHandleType : DWord;
+ pData,
+ pUnkForRelease,
+ cbData : ULong;
+ Data : Record end;
+ End;
+
+ RemSTGMEDIUM = TagRemSTGMedium;
+
+ TagSTGMEDIUM = Record
+ Tymed : DWord;
+ Case Integer Of
+ 0 : (HBITMAP : hBitmap; PUnkForRelease : Pointer {IUnknown});
+ 1 : (HMETAFILEPICT : hMetaFilePict );
+ 2 : (HENHMETAFILE : hEnhMetaFile );
+ 3 : (HGLOBAL : hGlobal );
+ 4 : (lpszFileName : LPOLESTR );
+ 5 : (pstm : Pointer{IStream} );
+ 6 : (pstg : Pointer{IStorage} );
+ End;
+ USTGMEDIUM = TagSTGMEDIUM;
+ STGMEDIUM = USTGMEDIUM;
+ TStgMedium = TagSTGMEDIUM;
+ PStgMedium = ^TStgMedium;
+
+//
+// wireSTGMEDIUM
+//
+// These flags are #defined (not enumerated) in wingdi.
+// We need to repeat #defines to avoid conflict in the generated file.
+//
+
+ _GDI_OBJECT = Record
+ ObjectType : DWord;
+ Case Integer Of
+ 0 : (HBitmap : WireHBITMAP);
+ 1 : (hPalette: wireHPALETTE);
+ 2 : (hGeneric: wireHGLOBAL);
+ END;
+ GDI_OBJECT = _GDI_OBJECT;
+
+
+ _userSTGMEDIUM = Record
+ tymed : DWORD;
+ Case Integer OF
+ 0 : (hMetaFilePict : wireHMETAFILEPICT;punkforrelease:Pointer {IUnknown});
+ 1 : (hHEnhMetaFile : wireHENHMETAFILE);
+ 2 : (hGdiHandle : ^GDI_OBJECT);
+ 3 : (HGlobal : wireHGLOBAL);
+ 4 : (lpszFileName : LPOLESTR);
+ 5 : (pstm : ^BYTE_BLOB);
+ 6 : (pstg : ^BYTE_BLOB);
+ END;
+
+ userSTGMEDIUM = _userSTGMEDIUM;
+
+
+ LPSTGMEDIUM = ^STGMEDIUM;
+
+ _userFLAG_STGMEDIUM = Record
+ ContextFlags,
+ fPassOwnership : Long;
+ stgmed : userSTGMEDIUM;
+ End;
+
+ userFLAG_STGMEDIUM = _userFLAG_STGMEDIUM;
+
+ wireFLAG_STGMEDIUM = ^userFLAG_STGMEDIUM;
+
+
+ _FLAG_STGMEDIUM = Record
+ ContextFlags,
+ fPassOwnership : Long;
+ Stgmed : STGMEDIUM;
+ End;
+ FLAG_STGMEDIUM = _FLAG_STGMEDIUM;
+
+
+ VARIANTARG = VARIANT;
+ LPVARIANT = ^VARIANT;
+ LPVARIANTARG = ^VARIANT;
+
+// parameter description
+
+ tagPARAMDESCEX = Record
+ cBytes : ULong; // size of this structure
+ varDefaultValue: VariantARG; // default value of this parameter
+ End;
+
+ PARAMDESCEX = tagPARAMDESCEX;
+ LPPARAMDESCEX = ^PARAMDESCEX;
+
+ tagPARAMDESC = Record
+ pparamdescex: LPPARAMDESCEX ; // valid if PARAMFLAG_FHASDEFAULT bit is set
+ wParamFlags : UShort ; // IN, OUT, etc
+ End;
+
+ PARAMDESC = tagPARAMDESC;
+ LPPARAMDESC = ^PARAMDESC;
+
+
+ tagSAFEARRAYBOUND = Record
+ cElements : ULong;
+ lLbound : Long;
+ End;
+ SAFEARRAYBOUND = tagSAFEARRAYBOUND;
+ LPSAFEARRAYBOUND = ^SAFEARRAYBOUND;
+
+ tagSAFEARRAY = record
+ cDims: USHORT;
+ fFeatures: USHORT;
+ cbElements: ULONG;
+ cLocks: ULONG;
+ pvData: PVOID;
+ rgsabound: array[0..0] of SAFEARRAYBOUND;
+ end;
+ TSafeArray = tagSAFEARRAY;
+ SAFEARRAY = TSafeArray;
+ PSafeArray = ^TSafeArray;
+
+// additional interface information about the incoming call
+ tagINTERFACEINFO = Record
+ Unk : Pointer {IUnknown}; // the pointer to the object
+ IID : Tiid; // interface id
+ wMethod : WORD; // interface method
+ End;
+
+ INTERFACEINFO = tagINTERFACEINFO;
+ LPINTERFACEINFO = ^INTERFACEINFO;
+ RPCOLEDATAREP = ULong;
+ tagRPCOLEMESSAGE = Record
+ Reserved1 : Pointer;
+ DataRepresentation : RPCOLEDATAREP;
+ Buffer : Pointer;
+ cbBuffer,
+ IMethod : ULong;
+ Reserved2 : Array[0..4] Of Pointer;
+ rpcFlags : ULong;
+ End;
+
+ RPCOLEMESSAGE = tagRPCOLEMESSAGE;
+ PRPCOLEMESSAGE = ^RPCOLEMESSAGE;
+
+ tagStorageLayout = Record
+ LayoutType : Dword;
+ pwcsElementName : POleStr;
+ cOffset,
+ cBytes : Large_Integer;
+ End;
+
+ StorageLayout = tagStorageLayout;
+
+ tagSTATDATA = Record
+ // field used by:
+ FORMATETC : Tformatetc; // EnumAdvise, EnumData (cache), EnumFormats
+ advf : DWord; // EnumAdvise, EnumData (cache)
+ padvSink : Pointer {IAdviseSink}; // EnumAdvise
+ dwConnection: DWord; // EnumAdvise
+ End;
+ STATDATA = TagStatData;
+ LPStatData = ^StatData;
+
+ pARRAYDESC = ^ARRAYDESC;
+ pTYPEDESC = ^TYPEDESC;
+ tagTYPEKIND = Dword;
+ TYPEKIND = tagTYPEKIND;
+ TTYPEKIND = TYPEKIND;
+ INVOKEKIND = Dword;
+ tagTYPEDESC = Record
+ Case Integer OF
+ VT_PTR,
+ VT_SAFEARRAY : (lptdesc : PTYPEDESC;vt : VARTYPE);
+ VT_CARRAY : (lpadesc : PARRAYDESC);
+ VT_USERDEFINED : (hreftype : HREFTYPE);
+ End;
+ TYPEDESC = tagTYPEDESC;
+
+
+
+
+ tagARRAYDESC = Record
+ tdescElem : TYPEDESC; // element type
+ cDims : USHORT;
+ rgbounds : ARRAY [0..0] OF SAFEARRAYBOUND; // dimension count
+ End;
+
+ ARRAYDESC = tagARRAYDESC;
+
+ tagIDLDESC = Record
+ dwReserved : pULONG;
+ wIDLFlags : USHORT; // IN, OUT, etc
+ End;
+ IDLDESC = tagIDLDESC;
+ LPIDLDESC = ^IDLDESC;
+
+
+ tagELEMDESC = Record
+ tdesc : TYPEDESC;
+ case Integer Of
+ 0 : (idldesc : IDLDESC);
+ 1 : (paramdesc : PARAMDESC);
+ END;
+
+ ELEMDESC = tagELEMDESC;
+ LPELEMDESC = ^ELEMDESC;
+ tagVARDESC = Record
+ memId : MEMBERID;
+ lpstrSchema : pOleStr;
+ CASE Integer OF
+ VAR_PERINSTANCE,
+ VAR_DISPATCH,
+ VAR_STATIC : (oInst : ULong; // offset of variable within the instance
+ ElemdescVar : ELEMDESC;
+ wVarFlags : WORD;
+ varkind : VARKIND);
+ VAR_CONST : (lpvarValue : PVARIANT); // the value of the constant
+ End;
+ VARDESC = tagVARDESC;
+ LPVARDESC = ^VARDESC;
+ pVARDESC = LPVARDESC;
+ tagDISPPARAMS = Record
+ rgvarg : lpVARIANTARG;
+ rgdispidNamedArgs : lpDISPID;
+ cArgs,
+ cNamedArgs : UINT;
+ End;
+ DISPPARAMS = tagDISPPARAMS;
+ TDispParams = tagDISPPARAMS;
+ PDispParams = ^TDispParams;
+
+ PExcepInfo = ^TExcepInfo;
+ TFNDeferredFillIn = function(info : PExcepInfo): HRESULT;stdcall;
+ tagEXCEPINFO = Record
+ wCode, // An error code describing the error.
+ wReserved : Word;
+ Source, // A source of the exception
+ Description, // A description of the error
+ HelpFile : WideString; // Fully qualified drive, path, and file name
+ dwHelpContext : ULONG; // help context of topic within the help file
+ // We can use ULONG_PTR here, because EXCEPINFO is marshalled by RPC
+ // RPC will marshal pfnDeferredFillIn.
+ pvReserved : pointer;
+ pfnDeferredFillIn : TFNDeferredFillIn;
+ SCODE : scode;
+ End;
+
+ EXCEPINFO = tagEXCEPINFO;
+ TExcepInfo = tagEXCEPINFO;
+
+ tagTYPEATTR = Record
+ GUID : Tguid; // the GUID of the TypeInfo
+ LCID : lcid; // locale of member names and doc strings
+ dwReserved : DWord;
+ memidConstructor, // ID of constructor, MEMBERID_NIL if none
+ memidDestructor : MemberID; // ID of destructor, MEMBERID_NIL if none
+ lpstrSchema : pOleStr;
+
+ cbSizeInstance : ULong; // the size of an instance of this type
+ typekind : TYPEKIND; // the kind of type this typeinfo describes
+ cFuncs, // number of functions
+ cVars, // number of variables / data members
+ cImplTypes, // number of implemented interfaces
+ cbSizeVft, // the size of this types virtual func table
+ cbAlignment, { specifies the alignment requirements for
+ an instance of this type,
+ 0 = align on 64k boundary
+ 1 = byte align
+ 2 = word align
+ 4 = dword align... }
+ wTypeFlags,
+ wMajorVerNum, // major version number
+ wMinorVerNum : Word; // minor version number
+ tdescAlias : TYPEDESC; { if typekind == TKIND_ALIAS this field
+ specifies the type for which this type
+ is an alias }
+ idldescType : IDLDESC; // IDL attributes of the described type
+ END;
+ TYPEATTR = tagTYPEATTR;
+
+ LPTYPEATTR = ^TYPEATTR;
+ PTYPEAttr = LPTYPEATTR;
+
+ tagTLIBATTR = Record
+ GUID : guid;
+ LCID : lcid;
+ SYSKIND : syskind;
+ wMajorVerNum,
+ wMinorVerNum,
+ wLibFlags : Word
+ End;
+
+ TLIBATTR = tagTLIBATTR;
+ LPTLIBATTR = ^tagTLIBATTR;
+ PTLIBAttr = LPTLIBATTR;
+
+ LPFUNCDESC = ^FUNCDESC;
+ PFUNCDESC = LPFUNCDESC;
+
+ tagFUNCDESC = Record
+ memid : MEMBERID;
+ lprgscode : PResultList;
+ lprgelemdescParam : lpELEMDESC; // array of param types
+ FUNCKIND : funckind;
+ invkind : INVOKEKIND;
+ callconv : CALLCONV;
+ cParams,
+ cParamsOpt,
+ oVft,
+ cScodes : SHORT;
+ elemdescFunc : ELEMDESC;
+ wFuncFlags : WORD;
+ End;
+ FUNCDESC = tagFUNCDESC;
+
+
+ tagBINDPTR = Record
+ case integer Of
+ 0 : (lpfuncdesc : LPFUNCDESC);
+ 1 : (lpvardesc : LPVARDESC);
+ 2 : (lptcomp : Pointer {ITypeComp} );
+ End;
+ BINDPTR = tagBINDPTR;
+ LPBINDPTR = ^BINDPTR;
+
+ tagCUSTDATAITEM = Record
+ GUID : TGuid; // guid identifying this custom data item
+ varValue : VARIANTARG; // value of this custom data item
+ End;
+
+ CUSTDATAITEM = tagCUSTDATAITEM;
+
+ LPCUSTDATAITEM = ^CUSTDATAITEM;
+
+ tagCUSTDATA = Record
+ cCustData : DWord; // number of custom data items in rgCustData
+ prgCustData : LPCUSTDATAITEM; // array of custom data items
+ End;
+
+ CUSTDATA = tagCUSTDATA;
+ LPCUSTDATA = ^CUSTDATA;
+
+ PPROPVARIANT = ^TPROPVARIANT;
+
+
+
+ tagPROPSPEC = record
+ ulKind : ULONG ;
+ case boolean of
+ false : ( propid:propid);
+ true : (lpwstr: LPOLEStr);
+ end;
+
+ PROPSPEC= tagPROPSPEC;
+ TPROPSPEC = PROPSPEC;
+ PPROPSPEC = ^TPROPSPEC;
+
+ tagSTATPROPSTG = record
+ lpwstrName : LPOLESTR ;
+ propid:PROPID ;
+ vt : VARTYPE;
+ end;
+ STATPROPSTG = tagSTATPROPSTG;
+ TSTATPROPSTG = STATPROPSTG;
+ PSTATPROPSTG = ^TSTATPROPSTG;
+
+ tagSTATPROPSETSTG = record
+ fmtid : FMTID;
+ clsid : CLSID;
+ grfFlags : DWORD;
+ mtime : FILETIME;
+ ctime : FILETIME;
+ atime : FILETIME;
+ dwOSVersion : DWORD;
+ end;
+ STATPROPSETSTG = tagSTATPROPSETSTG;
+ TSTATPROPSETSTG = STATPROPSETSTG;
+ PSTATPROPSETSTG = ^STATPROPSETSTG;
+
+ tagVersionedStream = record
+ guidVersion : TGUID;
+ pStream : pointer; {IStream}
+ end;
+ VERSIONEDSTREAM = tagVersionedStream;
+ TVERSIONEDSTREAM = tagVersionedStream;
+ LPVERSIONEDSTREAM = tagVersionedStream;
+ PVERSIONEDSTREAM = ^TagVersionedStream;
+
+
+ LPSAFEARRAY = ^SAFEARRAY;
+ tagDEC = record // simpler remoting variant without nested unions. see wtypes.h
+ wReserved : ushort;
+ scale,
+ sign : byte;
+ hi32 : ULONG;
+ lo64 : ULONGLONG;
+ end;
+ TDECIMAL=tagDEC;
+ PDecimal=^TDECIMAL;
+
+ tagCAC = record
+ cElems : ULONG;
+ pElems : pCHAR;
+ end;
+ CAC = tagCAC;
+ TCAC = tagCAC;
+ tagCAUB = record
+ cElems : ULONG;
+ pElems : pUCHAR;
+ end;
+ CAUB = tagCAUB;
+ TCAUB = tagCAUB;
+ tagCAI = record
+ cElems : ULONG;
+ pElems : pSHORT;
+ end;
+ CAI = tagCAI;
+ TCAI = tagCAI;
+ tagCAUI = record
+ cElems : ULONG;
+ pElems : pUSHORT;
+ end;
+ CAUI = tagCAUI;
+ TCAUI = tagCAUI;
+ tagCAL = record
+ cElems : ULONG;
+ pElems : pLONG;
+ end;
+ CAL = tagCAL;
+ TCAL = tagCAL;
+ tagCAUL = record
+ cElems : ULONG;
+ pElems : pULONG;
+ end;
+ CAUL = tagCAUL;
+ TCAUL = tagCAUL;
+ tagCAFLT = record
+ cElems : ULONG;
+ pElems : pSingle;
+ end;
+ CAFLT = tagCAFLT;
+ TCAFLT = tagCAFLT;
+ tagCADBL = record
+ cElems : ULONG;
+ pElems : ^DOUBLE;
+ end;
+ CADBL = tagCADBL;
+ TCADBL = tagCADBL;
+ tagCACY = record
+ cElems : ULONG;
+ pElems : ^CY;
+ end;
+ CACY = tagCACY;
+ TCACY = tagCACY;
+ tagCADATE = record
+ cElems : ULONG;
+ pElems : ^DATE;
+ end;
+ CADATE = tagCADATE;
+ TCADATE = tagCADATE;
+ tagCABSTR = record
+ cElems : ULONG;
+ pElems : ^BSTR;
+ end;
+ CABSTR = tagCABSTR;
+ TCABSTR = tagCABSTR;
+ tagCABSTRBLOB = record
+ cElems : ULONG;
+ pElems : ^BSTRBLOB;
+ end;
+ CABSTRBLOB = tagCABSTRBLOB;
+ TCABSTRBLOB = tagCABSTRBLOB;
+ tagCABOOL = record
+ cElems : ULONG;
+ pElems : ^VARIANT_BOOL;
+ end;
+ CABOOL = tagCABOOL;
+ TCABOOL = tagCABOOL;
+ tagCASCODE = record
+ cElems : ULONG;
+ pElems : ^SCODE;
+ end;
+ CASCODE = tagCASCODE;
+ TCASCODE = tagCASCODE;
+ tagCAPROPVARIANT = record
+ cElems : ULONG;
+ pElems : ^PROPVARIANT;
+ end;
+ CAPROPVARIANT = tagCAPROPVARIANT;
+ TCAPROPVARIANT = tagCAPROPVARIANT;
+ tagCAH = record
+ cElems : ULONG;
+ pElems : ^LARGE_INTEGER;
+ end;
+ CAH = tagCAH;
+ TCAH = tagCAH;
+ tagCAUH = record
+ cElems : ULONG;
+ pElems : ^ULARGE_INTEGER;
+ end;
+ CAUH = tagCAUH;
+ TCAUH = tagCAUH;
+ tagCALPSTR = record
+ cElems : ULONG;
+ pElems : ^LPSTR;
+ end;
+ CALPSTR = tagCALPSTR;
+ TCALPSTR = tagCALPSTR;
+ tagCALPWSTR = record
+ cElems : ULONG;
+ pElems : ^LPWSTR;
+ end;
+ CALPWSTR = tagCALPWSTR;
+ TCALPWSTR = tagCALPWSTR;
+ tagCAFILETIME = record
+ cElems : ULONG;
+ pElems : ^FILETIME;
+ end;
+ CAFILETIME = tagCAFILETIME;
+ TCAFILETIME = tagCAFILETIME;
+ tagCACLIPDATA = record
+ cElems : ULONG;
+ pElems : ^CLIPDATA;
+ end;
+ CACLIPDATA = tagCACLIPDATA;
+ TCACLIPDATA = tagCACLIPDATA;
+ tagCACLSID = record
+ cElems : ULONG;
+ pElems : ^CLSID;
+ end;
+ CACLSID = tagCACLSID;
+ TCACLSID = tagCACLSID;
+
+ PROPVAR_PAD1 = WORD;
+ PROPVAR_PAD2 = WORD;
+ PROPVAR_PAD3 = WORD;
+
+// Forward interfaces.
+
+ IStream = Types.IStream;
+ IMoniker = Interface;
+ IEnumMoniker = Interface;
+ IEnumString = Interface;
+ IRunningObjectTable = Interface;
+ IStorage = Interface;
+ IEnumSTATSTG = Interface;
+ IAdviseSink = Interface;
+ IBindCtx = Interface;
+ IAsyncManager = Interface;
+ ICallFactory = Interface;
+ ISynchronize = Interface;
+ ITypeLib = Interface;
+ IPropertyStorage = Interface;
+ IEnumSTATPROPSETSTG = interface;
+
+ TPROPVARIANT = record
+ vt : VARTYPE;
+ wReserved1 : PROPVAR_PAD1;
+ wReserved2 : PROPVAR_PAD2;
+ wReserved3 : PROPVAR_PAD3;
+ case longint of
+ 0 : ( cVal : CHAR );
+ 1 : ( bVal : UCHAR );
+ 2 : ( iVal : SHORT );
+ 3 : ( uiVal : USHORT );
+ 4 : ( lVal : LONG );
+ 5 : ( ulVal : ULONG );
+ 6 : ( intVal : longINT );
+ 7 : ( uintVal : UINT );
+ 8 : ( hVal : LARGE_INTEGER );
+ 9 : ( uhVal : ULARGE_INTEGER );
+ 10 : ( fltVal : SINGLE );
+ 11 : ( dblVal : DOUBLE );
+ 12 : ( boolVal : VARIANT_BOOL );
+ 13 : ( bool : _VARIANT_BOOL );
+ 14 : ( scode : SCODE );
+ 15 : ( cyVal : CY );
+ 16 : ( date : DATE );
+ 17 : ( filetime : FILETIME );
+ 18 : ( puuid : ^CLSID );
+ 19 : ( pclipdata : ^CLIPDATA );
+ 20 : ( bstrVal : BSTR );
+ 21 : ( bstrblobVal : BSTRBLOB );
+ 22 : ( blob : BLOB );
+ 23 : ( pszVal : LPSTR );
+ 24 : ( pwszVal : LPWSTR );
+ 25 : ( punkVal : pointer; { IUnknown to avoid Data types which require initialization/finalization can't be used in variant records});
+ 26 : ( pdispVal : pointer; {IDispatch} );
+ 27 : ( pStream : pointer {IStream} );
+ 28 : ( pStorage : pointer{IStorage} );
+ 29 : ( pVersionedStream : LPVERSIONEDSTREAM );
+ 30 : ( parray : LPSAFEARRAY );
+ 31 : ( cac : CAC );
+ 32 : ( caub : CAUB );
+ 33 : ( cai : CAI );
+ 34 : ( caui : CAUI );
+ 35 : ( cal : CAL );
+ 36 : ( caul : CAUL );
+ 37 : ( cah : CAH );
+ 38 : ( cauh : CAUH );
+ 39 : ( caflt : CAFLT );
+ 40 : ( cadbl : CADBL );
+ 41 : ( cabool : CABOOL );
+ 42 : ( cascode : CASCODE );
+ 43 : ( cacy : CACY );
+ 44 : ( cadate : CADATE );
+ 45 : ( cafiletime : CAFILETIME );
+ 46 : ( cauuid : CACLSID );
+ 47 : ( caclipdata : CACLIPDATA );
+ 48 : ( cabstr : CABSTR );
+ 49 : ( cabstrblob : CABSTRBLOB );
+ 50 : ( calpstr : CALPSTR );
+ 51 : ( calpwstr : CALPWSTR );
+ 52 : ( capropvar : CAPROPVARIANT );
+ 53 : ( pcVal : pCHAR );
+ 54 : ( pbVal : pUCHAR );
+ 55 : ( piVal : pSHORT );
+ 56 : ( puiVal : pUSHORT );
+ 57 : ( plVal : pLONG );
+ 58 : ( pulVal : pULONG );
+ 59 : ( pintVal : plongint );
+ 60 : ( puintVal : pUINT );
+ 61 : ( pfltVal : psingle );
+ 62 : ( pdblVal : pDOUBLE );
+ 63 : ( pboolVal : ^VARIANT_BOOL );
+ 64 : ( pdecVal : pDECIMAL );
+ 65 : ( pscode : ^SCODE );
+ 66 : ( pcyVal : ^CY );
+ 67 : ( pdate : ^DATE );
+ 68 : ( pbstrVal : ^TBSTR );
+ 69 : ( ppunkVal : ^IUnknown );
+ 70 : ( ppdispVal : ^IDispatch );
+ 71 : ( pparray : ^LPSAFEARRAY );
+ 72 : ( pvarVal : ^PROPVARIANT );
+ end;
+ PROPVARIANT=TPROPVARIANT;
+ TagPROPVARIANT = TPROPVARIANT;
+// Unknwn.idl
+
+// IUnknown is in classesh.inc
+
+
+
+ AsyncIUnknown = Interface(IUnknown)
+ ['{000e0000-0000-0000-C000-000000000046}']
+ Function Begin_QueryInterface(Const riid : TIID): HResult; StdCall;
+ Function Finish_QueryInterface(Out ppvObject : Pointer):HResult;StdCall;
+ Function Begin_AddRef:HResult;StdCall;
+ Function Finish_AddRef:ULong;StdCall;
+ Function Begin_Release:HResult;StdCall;
+ Function Finish_Release:ULong;StdCall;
+ End;
+
+ IClassFactory = Interface(IUnknown)
+ ['{00000001-0000-0000-C000-000000000046}']
+ Function CreateInstance(Const unkOuter:IUnknown;Const riid : TIID;Out vObject):HResult; StdCall;
+ Function LockServer(fLock : Bool):HResult;StdCall;
+ End;
+
+ PLicInfo = ^TLicInfo;
+ tagLICINFO = record
+ cbLicInfo : ULONG;
+ fRuntimeKeyAvail : BOOL;
+ fLicVerified : BOOL;
+ end;
+ TLicInfo = tagLICINFO;
+ LICINFO = TLicInfo;
+
+ IClassFactory2 = interface(IClassFactory)
+ ['{B196B28F-BAB4-101A-B69C-00AA00341D07}']
+ function GetLicInfo(var licInfo: TLicInfo): HResult; stdcall;
+ function RequestLicKey(dwResrved: DWORD; out bstrKey: WideString): HResult; stdcall;
+ function CreateInstanceLic(const unkOuter: IUnknown; const unkReserved: IUnknown;
+ const iid: TIID; const bstrKey: WideString; out vObject): HResult; stdcall;
+ end;
+
+// objidl.idl
+
+{****************************************************************************
+ * Component Object Interfaces
+ ****************************************************************************}
+
+ IMarshal = Interface(IUnknown)
+ ['{00000003-0000-0000-C000-000000000046}']
+ Function GetUnmarshalClass ( Const riid: TIID; pv:Pointer; Const dwDestContext:DWord;
+ pvDestContext:Pointer; Const mshlflags:DWORD;out LCid : TCLSID ):HResult;Stdcall;
+ Function GetMarshalSizeMax ( Const Riid: TIID; {in, unique} pv:Pointer; Const dwDestContext : DWord;
+ {in, unique} pvDestContext:Pointer; Const mshlflags : DWord; out pSize : PDWord ): HResult;Stdcall;
+ Function MarshalInterface ( Const {in, unique} pStm: IStream; Const riid: TIID; {in, unique} pv:Pointer;
+ Const dwDestContext:DWord; {in, unique} pvDestContext:Pointer; Const mshlflags:DWord ): HRESULT;Stdcall;
+ Function UnmarshalInterface ( {[in, unique]} Const pStm:IStream; Const riid: TIID;
+ out ppv ): HResult;Stdcall;
+ Function ReleaseMarshalData ( {[in, unique]} Const Strm: IStream ):HResult;Stdcall;
+ Function DisconnectObject ( Const dwReserved:DWord ):HRESULT;Stdcall;
+ END;
+
+
+ IMarshal2 = Interface(IMarshal)
+ ['{000001cf-0000-0000-C000-000000000046}']
+ End;
+
+ IMalloc = Interface(IUnknown)
+ ['{00000002-0000-0000-C000-000000000046}']
+ Function Alloc(cb :size_t):Pointer; Stdcall;
+ Function Realloc (pv :pointer;cb:size_t):Pointer;stdcall;
+ Procedure Free({[in]} pv: pointer); Stdcall;
+ Function GetSize(pv:pointer):size_t;stdcall;
+ Function DidAlloc(pv:pointer):Longint;stdcall;
+ procedure HeapMinimize; stdcall;
+ End;
+
+ IMallocSpy = Interface(IUnknown)
+ ['{0000001d-0000-0000-C000-000000000046}']
+
+ Function PreAlloc(cbrequest:Size_t):Longint; StdCall;
+ function PostAlloc(Pactual:Pointer):Pointer;StdCall;
+ Function PreFree(pRequest:Pointer;fSpyed:bool):pointer;StdCall;
+ Procedure PostFree(fspyed:Bool);Stdcall;
+ Function PreRealloc(pRequest:Pointer;cbRequest:Size_t;Out ppNewRequest:Pointer;
+ fSpyed:Bool):Size_t;Stdcall;
+ Function PostRealloc(pactual:Pointer;fspyed:Bool):pointer;Stdcall;
+ Function PreGetSize(pRequest:pointer;fSpyed:Bool):Pointer;StdCall;
+ Function PostGetSize(cbactual:Size_t;fSpyed:Bool):Size_t;StdCall;
+ Function PreDidAlloc(pRequest:pointer;fSpyed:Bool):pointer;stdcall;
+ Function PostDidAlloc(pRequest:pointer;fSpyed:Bool;Factual:Longint):pointer;stdcall;
+ Procedure PreHeapMinimize;StdCall;
+ Procedure PostHeapMinimize;StdCall;
+ End;
+
+ IStdMarshalInfo = Interface(IUnknown)
+ ['{00000018-0000-0000-C000-000000000046}']
+ Function GetClassForHandler (dwDestContext : DWord;pvDestContext:pointer;out Clsid : Pclsid ):HResult;Stdcall;
+ End;
+
+
+ IExternalConnection = Interface(IUnknown)
+ ['{00000019-0000-0000-C000-000000000046}']
+ Function AddConnection (ExtConn: DWord; Reserved: DWord):DWord;Stdcall;
+ Function ReleaseConnection(extconn: DWord; Reserved: Dword;FLastReleaseCloses: Bool):DWord;StdCall;
+ End;
+
+
+ IMultiQI = Interface(IUnknown)
+ ['{00000020-0000-0000-C000-000000000046}']
+//if (__midl >= 500)
+// async_uuid(000e0020-0000-0000-C000-000000000046)
+//endif
+ Function QueryMultipleInterfaces(cMQIs:Ulong;pMQIs:pMultiQI):HResult;StdCall;
+ END;
+
+ IInternalUnknown=Interface(IUnknown)
+ ['{00000021-0000-0000-C000-000000000046}']
+ Function QueryInternalInterface(const riid:TIID;Out ppv:Pointer):HResult;StdCall;
+ END;
+
+
+ IEnumUnknown = Interface(IUnknown)
+ ['{00000100-0000-0000-C000-000000000046}']
+ // pointer_default(unique)
+ Function Next(Celt:Ulong;out rgelt;out pCeltFetched:pulong):HRESULT;StdCall;
+// HRESULT RemoteNext( [in] ULONG celt, [out, size_is(celt), length_is( *pceltFetched)] IUnknown **rgelt, [out] ULONG *pceltFetched);
+ Function Skip(Celt:Ulong):HResult;StdCall;
+ Function Reset():HResult;
+ Function Close(Out ppenum: IEnumUnknown):HResult;
+ END;
+
+
+ IBindCtx = Interface (IUnknown)
+ ['{0000000e-0000-0000-C000-000000000046}']
+ Function RegisterObjectBound(Const punk:IUnknown):HResult; stdCall;
+ Function RevokeObjectBound (Const Punk:IUnknown):HResult; stdCall;
+ Function ReleaseBoundObjects :HResult; StdCall;
+ Function SetBindOptions(Const bindOpts:TBind_Opts):HResult; stdCall;
+// Function RemoteSetBindOptions(Const bind_opts: TBind_Opts2):HResult;StdCall;
+ Function GetBindOptions(var BindOpts:TBind_Opts):HResult; stdCall;
+// Function RemoteGetBindOptions(Var bind_opts: TBind_Opts2):HResult;StdCall;
+ Function GetRunningObjectTable(Out rot : IRunningObjectTable):Hresult; StdCall;
+ Function RegisterObjectParam(Const pszkey:LPOleStr;const punk:IUnknown):HResult;
+ Function GetObjectParam(Const pszkey:LPOleStr; out punk: IUnknown):HResult; StdCall;
+ Function EnumObjectParam (out enum:IEnumString):Hresult;StdCall;
+ Function RevokeObjectParam(pszKey:LPOleStr):HResult;StdCall;
+ End;
+
+
+ IEnumMoniker = Interface (IUnknown)
+ ['{00000102-0000-0000-C000-000000000046}']
+ Function Next(celt:ULong; out Elt;out celftfetched: ULong):HResult; StdCall;
+// Function RemoteNext(Celt:ULong; Out rgelt;out celtfetched :ULong):Hresult; StdCall;
+ Function Skip(celt:Ulong):HResult; StdCall;
+ Function Reset:HResult; StdCall;
+ Function Close(out penum:IEnumMoniker):HResult;StdCall;
+ End;
+
+
+ IRunnableObject = Interface(IUnknown)
+ ['{00000126-0000-0000-C000-000000000046}']
+ Function GetRunningClass(Out clsid:Tclsid):Hresult; StdCall;
+ Function Run(Const pb: IBindCtx):HResult; StdCall;
+ Function IsRunning:Bool; StdCall;
+// Function RemoteIsRunning:Bool; StdCall;
+ Function LockRunning(FLock,FLastUnlockClose:BOOL):HResult; StdCall;
+ Function SetContainedObject(fContained:Bool):Hresult;Stdcall;
+ End;
+
+ IRunningObjectTable = Interface (IUnknown)
+ ['{00000010-0000-0000-C000-000000000046}']
+ Function Register (grfFlags :DWord;const unkobject:IUnknown;Const mkObjectName:IMoniker;Out dwregister:DWord):HResult;StdCall;
+ Function Revoke (dwRegister:DWord):HResult; StdCall;
+ Function IsRunning (Const mkObjectName: IMoniker):HResult;StdCall;
+ Function GetObject (Const mkObjectName: IMoniker; Out punkObject:IUnknown):HResult; StdCall;
+ Function NoteChangeTime(dwRegister :DWord;Const FileTime: TFileTime):HResult;StdCall;
+ Function GetTimeOfLastChange(Const mkObjectName:IMoniker;Out filetime:TFileTime):HResult; StdCall;
+ Function EnumRunning (Out enumMoniker: IEnumMoniker):HResult; StdCall;
+ End;
+
+ IPersist = Interface (IUnknown)
+ ['{0000010c-0000-0000-C000-000000000046}']
+ Function GetClassId(out clsid:TClsId):HResult; StdCall;
+ End;
+
+ IPersistStream = Interface(IPersist)
+ ['{00000109-0000-0000-C000-000000000046}']
+ Function IsDirty:HResult; StdCall;
+ Function Load(Const stm: IStream):HResult; StdCall;
+ Function Save(Const stm: IStream;fClearDirty:Bool):HResult;StdCall;
+ Function GetSizeMax(Out cbSize:ULarge_Integer):HResult; StdCall;
+ End;
+
+ PIMoniker = ^IMoniker;
+ IMoniker = Interface (IPersistStream)
+ ['{0000000f-0000-0000-C000-000000000046}']
+ Function BindToObject (const pbc:IBindCtx;const mktoleft:IMoniker; const RiidResult:TIID;Out vresult):HResult;StdCall;
+// Function RemoteBindToObject (const pbc:IBindCtx;const mktoleft:IMoniker;const RiidResult:TIID;Out vresult):HResult;StdCall;
+ Function BindToStorage(Const Pbc:IBindCtx;Const mktoLeft:IMoniker; const Riid:TIID;Out vobj):HResult; StdCall;
+// Function RemoteBindToStorage(Const Pbc:IBindCtx;Const mktoLeft:IMoniker;const Riid:TIID;Out vobj):HResult; StdCall;
+ Function Reduce (const pbc:IBindCtx; dwReduceHowFar:DWord; mktoLeft: PIMoniker; Out mkReduced:IMoniker):HResult; StdCall;
+ Function ComposeWith(Const MkRight:IMoniker;fOnlyIfNotGeneric:BOOL; OUT mkComposite:IMoniker):HResult; StdCall;
+ Function Enum(fForward:Bool;Out enumMoniker:IEnumMoniker):HResult;StdCall;
+ Function IsEqual(Const mkOtherMoniker:IMoniker):HResult;StdCall;
+ Function Hash (Out dwHash:Dword):HResult;StdCall;
+ Function IsRunning(Const bc:IBindCtx;Const MkToLeft:IMoniker;Const mknewlyRunning:IMoniker):HResult;StdCall;
+ Function GetTimeOfLastChange(Const bc:IBindCtx;Const mkToLeft:IMoniker; out ft : FileTime):HResult; StdCall;
+ Function Inverse(out mk : IMoniker):HResult; StdCall;
+ Function CommonPrefixWith (Const mkOther:IMoniker):HResult; StdCall;
+ Function RelativePathTo(Const mkother:IMoniker; Out mkRelPath : IMoniker):HResult;StdCall;
+ Function GetDisplayName(Const bc:IBindCtx;const mktoleft:IMoniker;Out szDisplayName: pOleStr):HResult; StdCall;
+ Function ParseDisplayName(Const bc:IBindCtx;Const mkToLeft:IMoniker;szDisplayName:POleStr;out cheaten:ULong;out mkOut:IMoniker):HResult; StdCall;
+ Function IsSystemMoniker(Out dwMkSys:DWord):HResult;StdCall;
+ End;
+
+ IROTData = Interface (IUnknown)
+ ['{f29f6bc0-5021-11ce-aa15-00006901293f}']
+ Function GetComparisonData(out data; cbMax:ULong;out cbData:ULong):HResult;StdCall;
+ End;
+
+
+ IEnumString = Interface (IUnknown)
+ ['{00000101-0000-0000-C000-000000000046}']
+ Function Next(Celt:ULong;Out xcelt;Out Celtfetched:ULong):HResult; StdCall;
+// Function RemoteNext(Celt:ULong; Out celt;Out Celtfetched:ULong):HResult; StdCall;
+ Function Skip (Celt:ULong):Hresult;StdCall;
+ Function Reset:HResult;StdCall;
+ Function Clone(Out penum:IEnumString):HResult;StdCall;
+ End;
+
+ ISequentialStream = Types.ISequentialStream;
+ (*interface(IUnknown)
+ ['{0c733a30-2a1c-11ce-ade5-00aa0044773d}']
+ function Read(pv : Pointer;cb : ULONG;pcbRead : PULONG) : HRESULT;stdcall;
+ function Write(pv : Pointer;cb : ULONG;pcbWritten : PULONG): HRESULT;stdcall;
+ end;
+ *)
+
+ (* defined above by pulling it in from types IStream = interface(ISequentialStream)
+ ['{0000000C-0000-0000-C000-000000000046}']
+ function Seek(dlibMove : LargeInt; dwOrigin: Longint;
+ out libNewPosition : LargeInt): HResult; stdcall;
+ function SetSize(libNewSize : LargeInt) : HRESULT;stdcall;
+ function CopyTo(stm: IStream;cb : LargeInt;out cbRead : LargeInt;
+ out cbWritten: LargeInt) : HRESULT;stdcall;
+ function Commit(grfCommitFlags : Longint) : HRESULT; stdcall;
+ function Revert : HRESULT; stdcall;
+ function LockRegion(libOffset : LargeInt;cb : LargeInt;
+ dwLockType: Longint) : HRESULT;stdcall;
+ function UnlockRegion(libOffset: LargeInt;cb: LargeInt;
+ dwLockType: Longint) : HRESULT;stdcall;
+ Function Stat(out statstg : TStatStg; grfStatFlag: Longint): HRESULT;stdcall;
+ function Clone(out stm : IStream) : HRESULT; stdcall;
+ end;
+ *)
+ IEnumSTATSTG = Interface (IUnknown)
+ ['{0000000d-0000-0000-C000-000000000046}']
+ Function Next (Celt:ULong;Out xcelt;pceltfetched : PUlong):HResult; StdCall;
+// Function RemoteNext(Celt:Ulong; Out Celt;pceltfetched : PUlong);
+ Function Skip(Celt:ULong):HResult; StdCall;
+ Function Reset:HResult; StdCall;
+ Function Clone(Out penum:IEnumStatSTG):HResult; StdCall;
+ End;
+
+ IStorage = Interface (IUnknown)
+ ['{0000000b-0000-0000-C000-000000000046}']
+ Function CreateStream(pwcsname:POleStr;GrfMode,Reserved1,Reserved2 : DWord; Out stm : IStream):HResult; StdCall;
+ Function OpenStream(pwcsname:POleStr;Reserved1:Pointer;GrfMode,Reserved2 : DWord; Out stm : IStream):HResult; StdCall;
+// Function RemouteOpenStream(pwcsname:POleStr;cbReserved1:ULong;reserved1:pbyte;GrfMode,Reserved2 : DWord; Out stm : IStream):HResult; StdCall;
+ Function CreateStorage(pwcsname:POleStr;GrfMode,Reserved1,Reserved2 : DWord; Out stm : IStorage):HResult; StdCall;
+ Function OpenStorage(pwcsname:POleStr;Const stgPriority:IStorage;grfmode : DWord;Const SNBExclude :SNB;reserved:DWord;Out stm : IStorage):HResult; StdCall;
+ Function CopyTo(ciidExclude:DWord; rgiidexclude:piid; const snbexclude:SNB;const pstg : IStorage):HResult;StdCall;
+ Function MoveElementTo(wcsName:POleStr;Const pstgDest : IStorage;
+ wcvsNewName:POleStr; GrfFlags:DWord):Hresult; StdCall;
+ Function Commit(grfCommitFlags:Dword):Hresult; StdCall;
+ Function Revert:HResult; StdCall;
+ Function EnumElements(Reserved1 :Dword;Reserved2:Pointer;Reserved3:DWord;Out penum:IEnumStatStg):HResult;StdCall;
+ Function RemoteEnumElements(Reserved1 :Dword;cbReserved2:ULong;Reserved2:pbyte;reserved3:DWord;Out penum:IEnumStatStg):HResult;StdCall;
+ Function DestroyElement(wcsName: POleStr):HResult;StdCall;
+ Function RenameElement(wcsoldName: POleStr;wcsnewName: POleStr):HResult;StdCall;
+ Function SetElementTimes(wcsName:POleStr; Const pctime,patime,pmtime : FileTime):HResult;StdCall;
+ Function SetClass(Const ClasId: TClsID):HResult;StdCall;
+ Function SetStateBits(grfStateBits:DWord;grfMask:DWord):HResult;StdCall;
+ Function Stat(Out pStatStg:StatStg;grfStatFlag:DWord):HResult;StdCall;
+ End;
+
+ IPersistFile = Interface (IPersist)
+ ['{0000010b-0000-0000-C000-000000000046}']
+ Function IsDirty:HResult;StdCall;
+ Function Load(FileName:POleStr;dwMode:DWord):HResult;StdCall;
+ Function Save(FileName:POleStr;fremember:Bool):HResult;StdCall;
+ Function SaveCompleted(FileName:POleStr):HResult;StdCall;
+ Function GetCurFIle(Out FileName:POleStr):HResult;StdCall;
+ End;
+
+
+ IPersistStorage = Interface (IPersist)
+ ['{0000010a-0000-0000-C000-000000000046}']
+ Function IsDirty:HResult;StdCall;
+ Function InitNew(const pstg:IStorage):HResult;StdCall;
+ Function Load(const pstg:IStorage):HResult;StdCall;
+ Function Save(const pstg:IStorage;FSameAsLoad:Boolean):HResult;StdCall;
+ Function SaveCompleted(const pstg:IStorage):HResult;StdCall;
+ Function HandsOffStorage:HResult;StdCall;
+ End;
+
+ ILockBytes = Interface (IUnknown)
+ ['{0000000a-0000-0000-C000-000000000046}']
+ Function ReadAt(ulOffset:ULarge_Integer;pv:Pointer;cb:Ulong; Out pcbRead:ULong):HResult; StdCall;
+// Function RemoteReadAt(ulOffset:ULarge_Integer;pv:Pointer;cb:Ulong; Out pcbRead:ULong):HResult; StdCall;
+ Function WriteAt(ulOffset:ULarge_Integer;pv:Pointer;cb:Ulong; Out pcbWritten:ULong):HResult; StdCall;
+// Function RemoteWriteAt(ulOffset:ULarge_Integer;pv:Pointer;cb:Ulong; Out pcbWritten:ULong):HResult; StdCall;
+ Function Flush:HResult;StdCall;
+ Function SetSize(cb:ULarge_Integer):HResult;StdCall;
+ Function LockRegion(LibOffSet:ULarge_Integer;cb:ULarge_Integer;dwLockType:DWord):HResult;StdCall;
+ Function UnlockRegion(LibOffSet:ULarge_Integer;cb:ULarge_Integer;dwLockType:DWord):HResult;StdCall;
+ Function Stat(Out pstatstg:STATSTG;grfstatFlag:DWord):HResult;StdCall;
+ End;
+
+
+ IEnumFORMATETC = Interface (IUnknown)
+ ['{00000103-0000-0000-C000-000000000046}']
+ Function Next(Celt:ULong;Out Rgelt:FormatEtc;pceltFetched:pULong=nil):HResult; StdCall;
+// Function RemoteNext(Celt:ULong;Out Rgelt:FormatEtc; pceltFetched:pULong=nil):HResult; StdCall;
+ Function Skip(Celt:ULong):HResult;StdCall;
+ Function Reset:HResult;StdCall;
+ Function Clone(out penum:IEnumFORMATETC):HResult;StdCall;
+ End;
+
+ IEnumSTATDATA = Interface (IUnknown)
+ ['{00000105-0000-0000-C000-000000000046}']
+ Function Next(Celt:ULong;Out Rgelt:statdata; pceltFetched:pULong=nil):HResult; StdCall;
+// Function RemoteNext(Celt:ULong;Out Rgelt:statdata;Out pceltFetched:ULong):HResult; StdCall;
+ Function Skip(Celt:ULong):HResult;StdCall;
+ Function Reset:HResult;StdCall;
+ Function Clone(out penum:IEnumstatdata):HResult;StdCall;
+ End;
+
+
+
+ IRootStorage = Interface (IUnknown)
+ ['{00000012-0000-0000-C000-000000000046}']
+ Function SwitchToFile(pszfile:PoleStr):HResult;StdCall;
+ End;
+
+
+
+ IAdviseSink = Interface (IUnknown)
+ ['{0000010f-0000-0000-C000-000000000046}']
+ {$ifdef midl500} ['{00000150-0000-0000-C000-000000000046}'] {$endif}
+ Procedure OnDataChange (Const pformatetc : Formatetc;const pstgmed : STGMEDIUM); StdCall;
+ Procedure OnViewChange (dwAspect : DWord; lindex : Long); StdCall;
+ Procedure OnRename (Const pmk : IMoniker); StdCall;
+ Procedure OnSave; StdCall;
+ Procedure OnClose; StdCall;
+ End;
+
+ IAdviseSink2 = Interface (IAdviseSink)
+ ['{00000125-0000-0000-C000-000000000046}']
+ Procedure OnLinkSrcChange(Const Pmk: IMoniker); StdCall;
+ End;
+
+
+ IDataObject = Interface (IUnknown)
+ ['{0000010e-0000-0000-C000-000000000046}']
+ Function GetData(Const formatetcIn : FORMATETC;Out medium : STGMEDIUM):HRESULT; STDCALL;
+ Function GetDataHere(CONST pformatetc : FormatETC; Out medium : STGMEDIUM):HRESULT; STDCALL;
+ Function QueryGetData(const pformatetc : FORMATETC):HRESULT; STDCALL;
+ Function GetCanonicalFormatEtc(const pformatetcIn : FORMATETC;Out pformatetcOut : FORMATETC):HResult; STDCALl;
+ Function SetData (Const pformatetc : FORMATETC;const medium:STGMEDIUM;FRelease : BOOL):HRESULT; StdCall;
+ Function EnumFormatEtc(dwDirection : DWord; OUT enumformatetcpara : IENUMFORMATETC):HRESULT; StdCall;
+ Function DAdvise(const formatetc : FORMATETC;advf :DWORD; CONST AdvSink : IAdviseSink;OUT dwConnection:DWORD):HRESULT;StdCall;
+ Function DUnadvise(dwconnection :DWord) :HRESULT;StdCall;
+ Function EnumDAdvise(Out enumAdvise : IEnumStatData):HResult;StdCall;
+ End;
+
+ IDataAdviseHolder = Interface (IUnknown)
+ ['{00000110-0000-0000-C000-000000000046}']
+ Function Advise (CONST pdataObject : IDataObject;CONST fetc:FORMATETC;advf : DWORD;Const pAdvise:IAdviseSink;Out DwConnection:DWord):HResult; StdCall;
+ Function Unadvise (dwConnection:Dword):HResult; StdCall;
+ Function EnumAdvise(out penumAdvise : IEnumStatData):HResult;StdCall;
+ Function SendOnDataChange(const pDataObject :IDataObject;DwReserved,advf : DWord):HResult; StdCall;
+ End;
+
+
+
+
+ IMessageFilter = Interface (IUnknown)
+ ['{00000016-0000-0000-C000-000000000046}']
+ Function HandleInComingCall(dwCallType :DWord;htaskCaller : HTASK; dwTickCount: DWORD;CONST sinterfaceinfo:InterfaceInfo):DWord; StdCall;
+ Function RetryRejectedCall (htaskCallee:HTASK; dwTickCount : DWord; dwRejectType : Dword):DWord; StdCall;
+ Function MessagePending (htaskCallee:HTASK; dwTickCount : DWord; dwPendingType : Dword):DWord; StdCall;
+ End;
+
+//****************************************************************************
+//* Object Remoting Interfaces
+//****************************************************************************
+
+
+
+ IRpcChannelBuffer = Interface (IUnknown)
+ ['{D5F56B60-593B-101A-B569-08002B2DBF7A}']
+ Function GetBuffer (Const pMesasge : RPCOLEMESSAGE;Const riid :TIId):HResult; StdCall;
+ Function SendReceive(Var pMessage : RPCOLEMESSAGE; Out PStatus : ULong):HResult; StdCall;
+ Function FreeBuffer(Const pMessage : RPCOLEMESSAGE):HResult; StdCall;
+ Function GetDestCTX(Out dwDestContext : DWord;Out pvDestContext : Pointer):HResult; StdCall;
+ Function IsConnected:HResult; StdCall;
+ End;
+
+ IRpcChannelBuffer2 = Interface (IRpcChannelBuffer)
+ ['{594f31d0-7f19-11d0-b194-00a0c90dc8bf}']
+ Function GetProtocolVersion(Var dwVersion : DWord):HResult; StdCall;
+ End;
+
+
+ IAsyncRpcChannelBuffer = Interface (IRpcChannelBuffer2)
+ ['{a5029fb6-3c34-11d1-9c99-00c04fb998aa}']
+ Function Send(Var Msg: RPCOLEMESSAGE;Const pSync : ISynchronize;Out PulStatus : ULong):HResult; StdCall;
+ Function Receive(Var Msg: RPCOLEMESSAGE;Out PulStatus : ULong):HResult; StdCall;
+ Function GetDestCTXEx(Out MSG : RPCOLEMESSAGE;Out vDestContext : DWord;Out pvDestContext : Pointer ):HResult;StdCall;
+ End;
+
+ IRpcChannelBuffer3 = Interface (IRpcChannelBuffer2)
+ ['{25B15600-0115-11d0-BF0D-00AA00B8DFD2}']
+ Function Send(Var msg : RPCOLEMESSAGE;Out ulStatus : ULONG):HResult; StdCall;
+ Function Receive(Var msg : RPCOLEMESSAGE;ulSize : ULong;Out ulStatus : ULONG):HResult; StdCall;
+ Function Cancel (Const msg : RPCOLEMESSAGE):HResult; StdCall;
+ Function GetCallContext(Const msg : RPCOLEMESSAGE; Const riid : TIID; Out pInterface : Pointer):HResult; StdCall;
+ Function GetDestCTXEx(Const Msg : RPCOLEMESSAGE;Out vDestContext : DWord;Out pvDestContext : Pointer ):HResult;StdCall;
+ Function GetState(Const Msg : RPCOLEMESSAGE;Out State: DWord):HResult;StdCall;
+ Function RegisterAsync(Const Msg : RPCOLEMESSAGE;Const asyncmgr : IAsyncManager):HResult;StdCall;
+ End;
+
+ IRpcSyntaxNegotiate = Interface (IUnknown)
+ ['{58a08519-24c8-4935-b482-3fd823333a4f}']
+ Function NegotiateSyntax ( Var msg : RPCOLEMESSAGE):HResult; StdCall;
+ End;
+
+
+
+
+ IRpcProxyBuffer = Interface (IUnknown)
+ ['{D5F56A34-593B-101A-B569-08002B2DBF7A}']
+ Function Connect(Const rpcchannelbuffer : IRpcChannelBuffer):HResult; StdCall;
+ Procedure Disconnect;
+ End;
+
+ IRpcStubBuffer = Interface (IUnknown)
+ ['{D5F56AFC-593B-101A-B569-08002B2DBF7A}']
+ Function COnnect ( Const UnkServer : IUnknown):HResult; StdCall;
+ Procedure Disconnect; StdCall;
+ Function Invoke(Const rpcmsg : RPCOLEMESSAGE;Const RpcChanBuf : IRpcChannelBuffer):HResult; StdCall;
+ Function IsIIDSupported (Const riid : TIID):Pointer {IRpcStubBuffer}; StdCall;
+ Function CountRefs :ULong; StdCall;
+ Function DebugServerQueryInterface(CONST pv : Pointer):HResult; StdCall;
+ Procedure DebugServerRelease (pv : Pointer); StdCall;
+ End;
+
+ IPSFactoryBuffer = Interface (IUnknown)
+ ['{D5F569D0-593B-101A-B569-08002B2DBF7A}']
+ Function CreateProxy(Const UnkOuter : IUnknown;const riid : TIID; Out proxy: IRpcProxyBuffer; Out Pv :Pointer):HResult; StdCall;
+ Function CreateStub (Const riid : TIID; Const UnkServer : IUnknown; Out pstub : IRpcStubBuffer):HResult; StdCall;
+ End;
+
+{$ifdef NT4_greater_Or_DCOM}
+// This interface is only valid on Windows NT 4.0
+
+// This structure contains additional data for hooks. As a backward
+// compatability hack, the entire structure is passed in place of the
+// RIID parameter on all hook methods. Thus the IID must be the first
+// parameter. As a forward compatability hack the second field is the
+// current size of the structure.
+
+ SChannelHookCallInfo= Record;
+ IID : iid;
+ cbSize : Dword;
+ uCausality : GUID;
+ dwServerPid,
+ iMethod : DWord;
+ pObject : Pointer;
+ End;
+
+
+
+ IChannelHook = Interface (IUnknown)
+ ['{1008c4a0-7613-11cf-9af1-0020af6e72f4}']
+ Procedure ClientGetSize(Const uExtent : TGuid; CONST riid : TIID; Out datasize :ULong); StdCall;
+ Procedure ClientFillBuffer(Const uExtent : TGuid; CONST riid : TIID; Var datasize :ULong;Buffer :Pointer); StdCall;
+ Procedure ClientNotify(Const uExtent : TGuid; CONST riid : TIID; datasize :ULong;Buffer :Pointer;hrfault:HResult); StdCall;
+ Procedure ServerNotify(Const uExtent : TGuid; CONST riid : TIID; datasize :ULong;Buffer :Pointer;DataRep:DWord); StdCall;
+ Procedure ServerGetSize(Const uExtent : TGuid; CONST riid : TIID;hrFault :HResult; Out datasize :ULong); StdCall;
+ Procedure ServerFillBuffer(Const uExtent : TGuid; CONST riid : TIID; Var datasize :ULong;Buffer :Pointer;HrFault:HResult); StdCall;
+ End;
+{$Endif}
+
+
+// Well-known Property Set Format IDs
+//FMTID_SummaryInformation = {CONST} FMTID;
+//FMTID_DocSummaryInformation = {CONST} FMTID;
+//FMTID_UserDefinedProperties = {CONST} FMTID;
+//FMTID_DiscardableInformation = {CONST} FMTID;
+//FMTID_ImageSummaryInformation = {CONST} FMTID;
+//FMTID_AudioSummaryInformation = {CONST} FMTID;
+//FMTID_VideoSummaryInformation = {CONST} FMTID;
+//FMTID_MediaFileSummaryInformation = {CONST} FMTID;
+
+
+//****************************************************************************
+// * Connection Point Interfaces
+// ****************************************************************************/
+
+//#ifdef __INCLUDE_CPIFS
+ IConnectionPointContainer = Interface;
+//interface IConnectionPoint;
+//interface IEnumConnections;
+ IEnumConnectionPoints = Interface;
+ IEnumConnections = Interface;
+
+
+ IConnectionPoint = Interface (IUnknown)
+ ['{B196B286-BAB4-101A-B69C-00AA00341D07}']
+ Function GetConnectionInterface(out piid : TIID):HResult;StdCall;
+ Function GetConnectionPointContainer(CPC : IConnectionPointContainer):HResult;StdCall;
+ Function Advise(unkSink : IUnknown;Out dwCookie : DWord):HResult;StdCall;
+ Function UnAdvise(dwCookie : DWord):HResult;StdCall;
+ Function EnumConnection(out pEnum : IEnumConnections):HResult;stdCall;
+ End;
+
+ IConnectionPointContainer = Interface (IUnknown)
+ ['{B196B284-BAB4-101A-B69C-00AA00341D07}']
+ Function EnumConnectionPoints(out pEnum : IEnumConnectionPoints):HResult;StdCall;
+ Function FindConnectionPoint(Const RIID : TIID;Out ppcp : IConnectionPoint):HResult;StdCall;
+ End;
+
+ tagCONNECTDATA = Record
+ unk : Pointer; {IUnknown}
+ dwCookie : DWord;
+ End;
+ ConnectData = tagCONNECTDATA;
+
+ IEnumConnections = Interface (IUnknown)
+ ['{B196B287-BAB4-101A-B69C-00AA00341D07}']
+ Function Next(cConnections : ULong; Out rgcd : ConnectData; lpcFetched : pULong=nil):HResult;StdCall;
+ Function Skip(cConnections : ULong):HResult;StdCall;
+ Function Reset:HResult;StdCall;
+ Function Clone(Out pEnum : IEnumConnections):HResult; StdCall;
+ End;
+
+
+ IEnumConnectionPoints = Interface (IUnknown)
+ ['{B196B285-BAB4-101A-B69C-00AA00341D07}']
+ Function Next(cConnections : ULong; Out rgpcm : IConnectionPoint; lpcFetched : pULong=nil):HResult;StdCall;
+ Function Skip(cConnections : ULong):HResult;StdCall;
+ Function Reset:HResult;StdCall;
+ Function Clone(Out pEnum : IEnumConnectionPoints):HResult;StdCall;
+ End;
+
+
+
+ tagSOLE_AUTHENTICATION_SERVICE = Record
+ dwAuthnSvc,
+ dwAuthzSvc : DWord;
+ pPrincipalName : POleStr;
+ hr : HResult;
+ End;
+ SOLE_AUTHENTICATION_SERVICE = tagSOLE_AUTHENTICATION_SERVICE;
+ PSOLE_AUTHENTICATION_SERVICE = ^SOLE_AUTHENTICATION_SERVICE;
+
+ tagSOLE_AUTHENTICATION_INFO = Record
+ dwAuthnSvc,
+ dwAuthzSvc : DWord;
+ AuthInfo : Pointer;
+ End;
+ SOLE_AUTHENTICATION_INFO = tagSOLE_AUTHENTICATION_INFO;
+ PSOLE_AUTHENTICATION_INFO = ^SOLE_AUTHENTICATION_INFO;
+
+ tagSOLE_AUTHENTICATION_LIST = Record
+ cAuthInfo : DWord;
+ AuthInfo : PSOLE_AUTHENTICATION_INFO;
+ End;
+ SOLE_AUTHENTICATION_LIST = tagSOLE_AUTHENTICATION_LIST;
+ PSOLE_AUTHENTICATION_LIST = ^SOLE_AUTHENTICATION_LIST;
+
+{$ifdef WINNT_DCOM}
+
+ IClientSecurity = Interface (IUnknown)
+ ['{0000013D-0000-0000-C000-000000000046}']
+ Function QueryBlanket (Proxy : IUnknown;Out AuthnSvc,AuthzSvc : Dword;Out ServerPrincName:pOleStr;Out AuthnLevel,ImpLevel:Dword; Out AuthInfo : Pointer; Out Capabilities : Dword):HResult;StdCall;
+ Function SetBlanket (Proxy : IUnknown;AuthnSvc,AuthzSvc : Dword;ServerPrincName:pOleStr;AuthnLevel,ImpLevel:Dword;AuthInfo : Pointer;Capabilities : Dword):HResult;StdCall;
+ Function CopyProxy (Proxy : IUnknown;Out pcopy:IUnknown):HResult;StdCall;
+ End;
+
+ IServerSecurity = Interface (IUnknown)
+ ['{0000013E-0000-0000-C000-000000000046}']
+ Function QueryBlanket ( out authnSvc,AuthzSvc : DWord; Out pServerPrincName : pOleStr; Out AuthnLevel, ImpLevel; :DWord; out Privs : Pointer; Var Capabilities :DWord):HResult;StdCall;
+ Function ImpersonateClient:HResult;StdCall;
+ Function RevertToSelf:HResult;StdCall;
+ Function IsImpersonating:Bool;StdCall;
+ End;
+
+ IClassActivator = Interface (IUnknown)
+ ['{00000140-0000-0000-C000-000000000046}']
+ Function GetClassObject(Const rclsif : TClsID; ClassContext : DWord; locale : LCID; Const ridd : TIID; Out pv : Pointer):HResult;StdCall;
+ End;
+
+
+ IRpcOptions = Interface (IUnknown)
+ ['{00000144-0000-0000-C000-000000000046}']
+ Function xSet (prx : IUnknown;dwProperty : DWord; dwValue:ULONG_PTR):HResult; StdCall;
+ Function Query (prx : IUnknown;dwProperty:Dword; dwValue:ULONG_PTR):HResult; StdCall;
+ End;
+
+{$endif} {DCOM}
+
+ IFillLockBytes = Interface (IUnknown)
+ ['{99caf010-415e-11cf-8814-00aa00b569f5}']
+ Function FillAppend(const pv : Pointer;cb:ULong; Out PcbWritten : ULong):HResult;StdCall;
+ Function FillAt(ulOffset : ULarge_INTEGER;Const pv : Pointer;cb :ULong; Out pcbWritten:ULong):HResult;StdCall;
+ Function SetFillSize ( ulSize :ULarge_Integer):HResult;StdCall;
+ Function Terminate (bCanceled :Bool):HResult;StdCall;
+ End;
+
+ IProgressNotify = Interface (IUnknown)
+ ['{a9d758a0-4617-11cf-95fc-00aa00680db4}']
+ Function OnProgress (ProgressCurrent,ProgressMaximum :Dword; FAccurate,Fowner : Bool):HResult;StdCall;
+ End;
+
+ ILayoutStorage = Interface (IUnknown)
+ ['{0e6d4d90-6738-11cf-9608-00aa00680db4}']
+ {The methods in this interface all had "__stdcall" as modifier, while the other classes don't. ?!?!?}
+ Function LayoutScript ( xStorageLayout : StorageLayout;nEntries,glfInterleaveFlag : Dword) :HResult; StdCall;
+ Function BeginMonitor:HResult;StdCall;
+ Function EndMonitor:HResult;StdCall;
+ Function ReLayourDocFile(pwcsNewDFName :pOleStr):HResult;StdCall;
+ Function ReLayoutDocfileOnILockBytes(LockBytes : ILockBytes):Hresult;StdCall;
+ End;
+
+ IBlockingLock = Interface (IUnknown)
+ ['{30f3d47a-6447-11d1-8e3c-00c04fb9386d}']
+ Function Lock (dwTimeOut : DWord) : HResult;Stdcall;
+ Function Unlock : HResult;Stdcall;
+ End;
+
+ ITimeAndNoticeControl = Interface (IUnknown)
+ ['{bc0bf6ae-8878-11d1-83e9-00c04fc2c6d4}']
+ Function SuppressChanges(res1,res2 : Dword):HResult;StdCall;
+ End;
+
+ IOplockStorage = Interface (IUnknown)
+ ['{8d19c834-8879-11d1-83e9-00c04fc2c6d4}']
+ Function CreateStorageEx(wcsName : LPCWSTR;grfMode,StgFmt,GrfAtrrs :Dword;Const riid :Tiid; Out ppstgOpen : Pointer):HResult;StdCall;
+ Function OpenStorageEx(wcsName : LPCWSTR;grfMode,StgFmt,GrfAtrrs :Dword;Const riid :Tiid; Out ppstgOpen : Pointer):HResult;StdCall;
+ End;
+
+ ISurrogate = Interface (IUnknown)
+ ['{00000022-0000-0000-C000-000000000046}']
+ Function LoadDllServer (Const ClsId : TClsId):HResult;StdCall;
+ Function FreeSurrogate:HResult;StdCall;
+ End;
+
+ IGlobalInterfaceTable = Interface (IUnknown)
+ ['{00000146-0000-0000-C000-000000000046}']
+ Function RegisterInterfaceInGlobal(unk :IUnknown;Const riid : TIID; Out dwcookie :DWord):HResult;StdCall;
+ Function RevokeInterfaceFromGlobal (dwCookie :DWord):HResult;StdCall;
+ Function GetInterfaceFromGlobal (dwCookie :DWord;Const riid : TIID;out pv : Pointer):HResult;StdCall;
+ End;
+
+ IDirectWriterLock = Interface (IUnknown)
+ ['{0e6d4d92-6738-11cf-9608-00aa00680db4}']
+ Function WaitForWriteAccess (dwTimeOut : DWORD):HResult;StdCall;
+ Function ReleaseWriteAccess:HResult;StdCall;
+ Function HaveWriteAccess:HResult;StdCall;
+ End;
+
+ ISynchronize = Interface (IUnknown)
+ ['{00000030-0000-0000-C000-000000000046}']
+ Function Wait (dwFlags : DWord; dwMilliSeconds : DWord):HResult;StdCall;
+ Function Signal : HResult;StdCall;
+ Function Reset : HResult;StdCall;
+ End;
+
+ ISynchronizeHandle = Interface (IUnknown)
+ ['{00000031-0000-0000-C000-000000000046}']
+ Function GetHandle(Out ph : Handle):HResult;StdCall;
+ End;
+
+ ISynchronizeEvent = Interface (ISynchronizeHandle)
+ ['{00000032-0000-0000-C000-000000000046}']
+ Function SetEventHandle (Const ph : Handle):HResult; StdCall;
+ End;
+
+ ISynchronizeContainer = Interface (IUnknown)
+ ['{00000033-0000-0000-C000-000000000046}']
+ Function AddSynchronize(pSync : ISynchronize):HResult; StdCall;
+ Function WaitMultiple(dwFlags : Dword; dwTimeOut : Dword; Out pSync : ISynchronize):HResult;StdCall;
+ End;
+
+ ISynchronizeMutex = Interface (ISynchronize)
+ ['{00000025-0000-0000-C000-000000000046}']
+ Function ReleaseMutex:HResult; StdCall;
+ End;
+
+ ICancelMethodCalls = Interface (IUnknown)
+ ['{00000029-0000-0000-C000-000000000046}']
+ Function Cancel(ulSeconds : ULong):HResult; StdCall;
+ Function TestCancel:HResult;StdCall;
+ End;
+
+ IAsyncManager = Interface (IUnknown)
+ ['{0000002A-0000-0000-C000-000000000046}']
+ Function CompleteCall (xResult : HResult):HResult;StdCall;
+ Function GetCallContext(Const iid :TIID; Out pInterface : Pointer):HResult;StdCall;
+ Function GetState(Out pulStateFlags : ULong):HResult;StdCall;
+ End;
+
+ ICallFactory = Interface (IUnknown)
+ ['{1c733a30-2a1c-11ce-ade5-00aa0044773d}']
+ Function CreateCall(Const riid:TIID;CtrUnk : IUnknown;Const Riid2:TIID;Out Unknown : IUnknown):HResult;StdCall;
+ End;
+
+ IRpcHelper = Interface (IUnknown)
+ ['{00000149-0000-0000-C000-000000000046}']
+ Function GetDCOMProtocolVersion(Out ComVersion :DWord):HResult;StdCall;
+ Function GettIIDFromOBJREF(ObjRef : Pointer;Out xIID : piid):HResult;StdCall;
+ End;
+
+ IReleaseMarshalBuffers = Interface (IUnknown)
+ ['{eb0cb9e8-7996-11d2-872e-0000f8080859}']
+ Function ReleaseMarshalBuffer(const pnsg : RPCOLEMESSAGE;dwFlags:DWord;Const pchn : IUnknown):HResult; StdCall;
+ End;
+
+ IWaitMultiple = Interface (IUnknown)
+ ['{0000002B-0000-0000-C000-000000000046}']
+ Function WaitMulitple(TImeout :DWord;out psync : ISynchronize):HResult; StdCall;
+ Function AddSynchronize (const psync : ISynchronize):HResult;StdCall;
+ End;
+
+ IUrlMon = Interface (IUnknown)
+ ['{00000026-0000-0000-C000-000000000046}']
+ Function AsyncGetClassBits(CONST rclsif : TClsID; psztype,pzext : lpcwstr; dwfileversionMS,dwFileVersionLS : DWord; pzcodebase : LPCWSTR; Const pbc : IBindCTX; dwclasscontext : DWord; const Riid:TIID; flags :DWORD):HResult; StdCall;
+ End;
+
+ IForegroundTransfer = Interface (IUnknown)
+ ['{00000145-0000-0000-C000-000000000046}']
+ Function AllowForegroundTransfer(lpvReserved:Pointer):HResult; StdCall;
+ End;
+
+ IAddrTrackingControl = Interface (IUnknown)
+ ['{00000147-0000-0000-C000-000000000046}']
+ Function EnableCOMDynamicAddrTracking:HResult; StdCall;
+ Function DisableCOMDynamicAddrTracking:HResult; StdCall;
+ End;
+
+ IAddrExclusionControl = Interface (IUnknown)
+ ['{00000148-0000-0000-C000-000000000046}']
+ Function GetCurrentAddrExclusionList(Const riid : TIID;out Enumerator : Pointer):HResult;StdCall;
+ Function UpdateAddrExclusionList(Enumerator : IUnknown):HResult;StdCall;
+ End;
+
+//****************************************************************************
+//* Pipe interfaces
+//****************************************************************************/
+
+// Doesn't look translatable. See objidl.idl
+
+//****************************************************************************
+//* Thumbnail generator interface
+//****************************************************************************/
+
+ IThumbnailExtractor = Interface (IUnknown)
+ ['{969dc708-5c76-11d1-8d86-0000f804b057}']
+ Function ExtractThumbnail (pStg : IStorage; uLength,UHeight : ULong; Out uloutputlength,Height :ULong; Out OutputBitmap : HBITMAP): HResult; StdCall;
+ Function OnFileUpdated (pStg : IStorage):HResult;
+ End;
+
+//****************************************************************************
+//* Dummy Interface to force inclusion of HICON and HDC in proxy/stub code....
+//****************************************************************************/
+
+ IDummyHICONIncluder = Interface (IUnknown)
+ ['{947990de-cc28-11d2-a0f7-00805f858fb1}']
+ Function Dummy (h1 : HICON; H2 :HDC):HResult;
+ End;
+
+ IComThreadingInfo = Interface (IUnknown)
+ ['{000001ce-0000-0000-C000-000000000046}']
+ Function GetCurrentApartmentType(out pAptType : DWord {APTTTYPE}):HResult;
+ Function GetCurrentThreadType(Out ThreadType : Dword {THDTTYPE}):HResult;StdCall;
+ Function GetCurrentLogicalThreadID(Out guidlogicalThreadId : TGUID):HResult;StdCall;
+ Function SetCurrentLogicalThreadID(Const guidlogicalThreadId : TGUID):HResult;StdCall;
+ End;
+
+ IProcessInitControl = Interface (IUnknown)
+ ['{72380d55-8d2b-43a3-8513-2b6ef31434e9}']
+ Function ResetInitializerTimeout(dwSecondsRemaining:DWord):HResult; StdCall;
+ End;
+
+
+// Interfaces from OAIDL.IDL
+
+ ITypeInfo = Interface;
+
+ ICreateTypeInfo = Interface (IUnknown)
+ ['{00020405-0000-0000-C000-000000000046}']
+ Function SetGuid(CONST guid: TGUID):HResult;StdCall;
+ Function SetTypeFlags(uTypeFlags: UINT):HResult;StdCall;
+ Function SetDocString(pStrDoc: pOleStr):HResult;StdCall;
+ Function SetHelpContext(dwHelpContext: DWORD):HResult;StdCall;
+ Function SetVersion(wMajorVerNum: WORD; wMinorVerNum: WORD):HResult;StdCall;
+ Function AddRefTypeInfo(CONST pTInfo: ITypeInfo; CONST phRefType: HREFTYPE):HResult;StdCall;
+ Function AddFuncDesc(index: UINT; CONST pFuncDesc: FUNCDESC):HResult;StdCall;
+ Function AddImplType(index: UINT; hRefType: HREFTYPE):HResult;StdCall;
+ Function SetImplTypeFlags(index: UINT; implTypeFlags: WINT):HResult;StdCall;
+ Function SetAlignment(cbAlignment: WORD):HResult;StdCall;
+ Function SetSchema(pStrSchema: pOleStr):HResult;StdCall;
+ Function AddVarDesc(index: UINT; CONST pVarDesc: VARDESC):HResult;StdCall;
+ Function SetFuncAndParamNames(index: UINT; CONST rgszNames: pOleStr; cNames: UINT):HResult;StdCall;
+ Function SetVarName(index: UINT; szName: pOleStr):HResult;StdCall;
+ Function SetTypeDescAlias(CONST pTDescAlias: TYPEDESC):HResult;StdCall;
+ Function DefineFuncAsDllEntry(index: UINT; szDllName: pOleStr; szProcName: pOleStr):HResult;StdCall;
+ Function SetFuncDocString(index: UINT; szDocString: pOleStr):HResult;StdCall;
+ Function SetVarDocString(index: UINT; szDocString: pOleStr):HResult;StdCall;
+ Function SetFuncHelpContext(index: UINT; dwHelpContext: DWORD):HResult;StdCall;
+ Function SetVarHelpContext(index: UINT; dwHelpContext: DWORD):HResult;StdCall;
+ Function SetMops(index: UINT; Const bstrMops: WideString):HResult;StdCall;
+ Function SetTypeIdldesc(CONST pIdlDesc: IDLDESC):HResult;StdCall;
+ Function LayOut():HResult;StdCall;
+ End;
+
+ ICreateTypeInfo2 = Interface (ICreateTypeInfo)
+ ['{0002040E-0000-0000-C000-000000000046}']
+ Function DeleteFuncDesc(index: UINT):HResult;StdCall;
+ Function DeleteFuncDescByMemId(memid: MEMBERID; invKind: INVOKEKIND):HResult;StdCall;
+ Function DeleteVarDesc(index: UINT):HResult;StdCall;
+ Function DeleteVarDescByMemId(memid: MEMBERID):HResult;StdCall;
+ Function DeleteImplType(index: UINT):HResult;StdCall;
+ Function SetCustData(CONST guid: TGUID; CONST pVarVal: VARIANT):HResult;StdCall;
+ Function SetFuncCustData(index: UINT; CONST guid: TGUID; CONST pVarVal: VARIANT):HResult;StdCall;
+ Function SetParamCustData(indexFunc: UINT; indexParam: UINT; CONST guid: TGUID; CONST pVarVal: VARIANT):HResult;StdCall;
+ Function SetVarCustData(index: UINT; CONST guid: TGUID; CONST pVarVal: VARIANT):HResult;StdCall;
+ Function SetImplTypeCustData(index: UINT; CONST guid: TGUID; CONST pVarVal: VARIANT):HResult;StdCall;
+ Function SetHelpStringContext(dwHelpStringContext: ULONG):HResult;StdCall;
+ Function SetFuncHelpStringContext(index: UINT; dwHelpStringContext: ULONG):HResult;StdCall;
+ Function SetVarHelpStringContext(index: UINT; dwHelpStringContext: ULONG):HResult;StdCall;
+ Function Invalidate():HResult;StdCall;
+ Function SetName(szName: pOleStr):HResult;StdCall;
+ End;
+
+ ICreateTypeLib = Interface (IUnknown)
+ ['{00020406-0000-0000-C000-000000000046}']
+ Function CreateTypeInfo(szName: pOleStr; tkind: TYPEKIND; OUT ppCTInfo: ICreateTypeInfo):HResult;StdCall;
+ Function SetName(szName: pOleStr):HResult;StdCall;
+ Function SetVersion(wMajorVerNum: WORD; wMinorVerNum: WORD):HResult;StdCall;
+ Function SetGuid(CONST guid: TGUID):HResult;StdCall;
+ Function SetDocString(szDoc: pOleStr):HResult;StdCall;
+ Function SetHelpFileName(szHelpFileName: pOleStr):HResult;StdCall;
+ Function SetHelpContext(dwHelpContext: DWORD):HResult;StdCall;
+ Function SetLcid(lcid: LCID):HResult;StdCall;
+ Function SetLibFlags(uLibFlags: UINT):HResult;StdCall;
+ Function SaveAllChanges():HResult;StdCall;
+ End;
+
+ ICreateTypeLib2 = Interface (ICreateTypeLib)
+ ['{0002040F-0000-0000-C000-000000000046}']
+ Function DeleteTypeInfo(szName: pOleStr):HResult;StdCall;
+ Function SetCustData(CONST guid: TGUID; CONST pVarVal: VARIANT):HResult;StdCall;
+ Function SetHelpStringContext(dwHelpStringContext: ULONG):HResult;StdCall;
+ Function SetHelpStringDll(szFileName: pOleStr):HResult;StdCall;
+ End;
+
+ IEnumVARIANT = Interface (IUnknown)
+ ['{00020404-0000-0000-C000-000000000046}']
+ {$ifndef Call_as}
+ Function Next(celt: ULONG; OUT rgVar: VARIANT; pCeltFetched: pULONG=nil):HResult;StdCall;
+ {$else}
+ Function Next(celt: ULONG; OUT rgVar: VARIANT; pCeltFetched: pULONG=nil):HResult;StdCall;
+ {$endif}
+ Function Skip(celt: ULONG):HResult;StdCall;
+ Function Reset():HResult;StdCall;
+ Function Clone(OUT ppEnum: IEnumVARIANT):HResult;StdCall;
+ End;
+
+ ITypeComp = Interface (IUnknown)
+ ['{00020403-0000-0000-C000-000000000046}']
+ {$ifndef Call_as}
+ Function Bind(szName: pOleStr; lHashVal: ULONG; wFlags: WORD; OUT ppTInfo: ITypeInfo; OUT pDescKind: DESCKIND; OUT pBindPtr: BINDPTR):HResult;StdCall;
+ Function BindType(szName: pOleStr; lHashVal: ULONG; OUT ppTInfo: ITypeInfo; OUT ppTComp: ITypeComp):HResult;StdCall;
+ {$else}
+ Function Bind(szName: pOleStr; lHashVal: ULONG; wFlags: WORD; OUT ppTInfo: ITypeInfo; OUT pDescKind: DESCKIND; OUT ppFuncDesc: LPFUNCDESC; OUT ppVarDesc: LPVARDESC; O
+ Function BindType(szName: pOleStr; lHashVal: ULONG; OUT ppTInfo: ITypeInfo):HResult;StdCall;
+ {$endif}
+ End;
+
+ ITypeInfo = Interface (IUnknown)
+ ['{00020401-0000-0000-C000-000000000046}']
+ {$ifndef Call_as}
+ Function GetTypeAttr(OUT ppTypeAttr: lpTYPEATTR):HResult;StdCall;
+ {$else}
+ Function GetTypeAttr(OUT ppTypeAttr: LPTYPEATTR; OUT pDummy: CLEANLOCALSTORAGE):HResult;StdCall;
+ {$endif}
+ Function GetTypeComp(OUT ppTComp: ITypeComp):HResult;StdCall;
+ {$ifndef Call_as}
+ Function GetFuncDesc(index: UINT; OUT ppFuncDesc: lpFUNCDESC):HResult;StdCall;
+ Function GetVarDesc(index: UINT; OUT ppVarDesc: lpVARDESC):HResult;StdCall;
+ Function GetNames(memid: MEMBERID; rgBstrNames: PBStrList; cMaxNames: UINT; OUT pcNames: UINT):HResult;StdCall;
+ {$else}
+ Function GetFuncDesc(index: UINT; OUT ppFuncDesc: LPFUNCDESC; OUT pDummy: CLEANLOCALSTORAGE):HResult;StdCall;
+ Function GetVarDesc(index: UINT; OUT ppVarDesc: LPVARDESC; OUT pDummy: CLEANLOCALSTORAGE):HResult;StdCall;
+ Function GetNames(memid: MEMBERID; rgBstrNames: PBStrList; cMaxNames: UINT; OUT pcNames: UINT):HResult;StdCall;
+ {$endif}
+ Function GetRefTypeOfImplType(index: UINT; OUT pRefType: HREFTYPE):HResult;StdCall;
+ Function GetImplTypeFlags(index: UINT; OUT pImplTypeFlags: WINT):HResult;StdCall;
+ {$ifndef Call_as}
+ Function GetIDsOfNames(CONST rgszNames: pOleStr; cNames: UINT; OUT pMemId: MEMBERID):HResult;StdCall;
+ {$else}
+ Function LocalGetIDsOfNames():HResult;StdCall;
+ {$endif}
+ {$ifndef Call_as}
+ Function Invoke(pvInstance: Pointer; memid: MEMBERID; wFlags: WORD; VAR pDispParams: DISPPARAMS; OUT pVarResult: VARIANT; OUT pExcepInfo: EXCEPINFO; OUT puArgErr: UINT):HResult;StdCall;
+ {$else}
+ Function LocalInvoke ():HResult;StdCall;
+ {$endif}
+ {$ifndef Call_as}
+ //Function GetDocumentation(memid: MEMBERID; OUT pBstrName: WideString; OUT pBstrDocString: WideString; OUT pdwHelpContext: DWORD; OUT pBstrHelpFile: WideString):HResult;StdCall;
+ Function GetDocumentation(memid: MEMBERID; pBstrName: PWideString; pBstrDocString: PWideString; pdwHelpContext: PDWORD; pBstrHelpFile: PWideString):HResult;StdCall;
+ {$else}
+ Function GetDocumentation(memid: MEMBERID; refPtrFlags: DWORD; OUT pBstrName: WideString; OUT pBstrDocString: WideString; OUT pdwHelpContext: DWORD; OUT pBstrHelpFile: WideString):HResult;StdCall;
+ {$endif}
+
+ {$ifndef Call_as}
+ Function GetDllEntry(memid: MEMBERID; invKind: INVOKEKIND; OUT pBstrDllName: WideString; OUT pBstrName: WideString; OUT pwOrdinal: WORD):HResult;StdCall;
+ {$else}
+ Function GetDllEntry(memid: MEMBERID; invKind: INVOKEKIND; refPtrFlags: DWORD; OUT pBstrDllName: WideString; OUT pBstrName: WideString; OUT pwOrdinal: WORD):HResult;StdCall;
+ {$endif}
+
+ Function GetRefTypeInfo(hRefType: HREFTYPE; OUT ppTInfo: ITypeInfo):HResult;StdCall;
+
+ {$ifndef Call_as}
+ Function AddressOfMember(memid: MEMBERID; invKind: INVOKEKIND; OUT ppv: Pointer):HResult;StdCall;
+ {$else}
+ Function LocalAddressOfMember():HResult;StdCall;
+ {$endif}
+
+ {$ifndef Call_as}
+ Function CreateInstance(CONST pUnkOuter: IUnknown; CONST riid: TIID; OUT ppvObj: Pointer):HResult;StdCall;
+ {$else}
+ Function CreateInstance(CONST riid: TIID; OUT ppvObj: pIUnknown):HResult;StdCall;
+ {$endif}
+ Function GetMops(memid: MEMBERID; OUT pBstrMops: WideString):HResult;StdCall;
+ {$ifndef Call_as}
+ Function GetContainingTypeLib(OUT ppTLib: ITypeLib; OUT pIndex: UINT):HResult;StdCall;
+ {$else}
+ Function GetContainingTypeLib(OUT ppTLib: ITypeLib; OUT pIndex: UINT):HResult;StdCall;
+ {$endif}
+ {$ifndef Call_as}
+ Procedure ReleaseTypeAttr( pTypeAttr: pTypeAttr); StdCall;
+ {$else}
+ Function ReleaseTypeAttr():HResult;StdCall;
+ {$endif}
+
+ {$ifndef Call_as}
+ Procedure ReleaseFuncDesc( pFuncDesc : lpFUNCDESC); StdCall;
+ {$else}
+ Function LocalReleaseFuncDesc():HResult;StdCall;
+ {$endif}
+ {$ifndef Call_as}
+ Procedure ReleaseVarDesc( pVarDesc : lpVarDesc); stdcall;
+ {$else}
+ Function LocalReleaseVarDesc():HResult;StdCall;
+ {$endif}
+ End;
+
+ ITypeInfo2 = Interface (ITypeInfo)
+ ['{00020412-0000-0000-C000-000000000046}']
+ Function GetTypeKind(OUT xpTypeKind: TYPEKIND):HResult;StdCall;
+ Function GetTypeFlags(OUT pTypeFlags: ULONG):HResult;StdCall;
+ Function GetFuncIndexOfMemId(memid: MEMBERID; invKind: INVOKEKIND; OUT pFuncIndex: UINT):HResult;StdCall;
+ Function GetVarIndexOfMemId(memid: MEMBERID; OUT pVarIndex: UINT):HResult;StdCall;
+ Function GetCustData(CONST guid: TGUID; OUT pVarVal: VARIANT):HResult;StdCall;
+ Function GetFuncCustData(index: UINT; CONST guid: TGUID; OUT pVarVal: VARIANT):HResult;StdCall;
+ Function GetParamCustData(indexFunc: UINT; indexParam: UINT; CONST guid: TGUID; OUT pVarVal: VARIANT):HResult;StdCall;
+ Function GetVarCustData(index: UINT; CONST guid: TGUID; OUT pVarVal: VARIANT):HResult;StdCall;
+ Function GetImplTypeCustData(index: UINT; CONST guid: TGUID; OUT pVarVal: VARIANT):HResult;StdCall;
+ {$ifndef Call_as}
+ Function GetDocumentation2(memid: MEMBERID; lcid: LCID; pbstrHelpString: PWideString; pdwHelpStringContext: PDWORD; pbstrHelpStringDll: PWideString):HResult;StdCall;
+ {$else}
+ Function GetDocumentation2(memid: MEMBERID; lcid: LCID; refPtrFlags: DWORD; pbstrHelpString: PWideString; pdwHelpStringContext: PDWORD; pbstrHelpStringDll: PWideString):HResult;StdCall;
+ {$endif}
+ Function GetAllCustData(OUT pCustData: CUSTDATA):HResult;StdCall;
+ Function GetAllFuncCustData(index: UINT; OUT pCustData: CUSTDATA):HResult;StdCall;
+ Function GetAllParamCustData(indexFunc: UINT; indexParam: UINT; OUT pCustData: CUSTDATA):HResult;StdCall;
+ Function GetAllVarCustData(index: UINT; OUT pCustData: CUSTDATA):HResult;StdCall;
+ Function GetAllImplTypeCustData(index: UINT; OUT pCustData: CUSTDATA):HResult;StdCall;
+ End;
+
+ ITypeLib = Interface (IUnknown)
+ ['{00020402-0000-0000-C000-000000000046}']
+ {$ifndef Call_as}
+ Function GetTypeInfoCount:UINT; StdCall;
+ {$else}
+ Function GetTypeInfoCount(OUT pcTInfo: UINT):HResult;StdCall;
+ {$endif}
+ Function GetTypeInfo(index: UINT; OUT ppTInfo: ITypeInfo):HResult;StdCall;
+ Function GetTypeInfoType(index: UINT; OUT pTKind: TYPEKIND):HResult;StdCall;
+ Function GetTypeInfoOfGuid(CONST guid: TGUID; OUT ppTinfo: ITypeInfo):HResult;StdCall;
+ {$ifndef Call_as}
+ Function GetLibAttr(OUT ppTLibAttr: lpTLIBATTR):HResult;StdCall;
+ {$else}
+ Function GetLibAttr(OUT ppTLibAttr: LPTLIBATTR; OUT pDummy: CLEANLOCALSTORAGE):HResult;StdCall;
+ {$endif}
+
+ Function GetTypeComp(OUT ppTComp: ITypeComp):HResult;StdCall;
+ {$ifndef Call_as}
+ Function GetDocumentation(index: WINT; pBstrName: PWideString; pBstrDocString: PWideString; pdwHelpContext: PDWORD; pBstrHelpFile: PWideString):HResult;StdCall;
+ {$else}
+ Function GetDocumentation(index: WINT; refPtrFlags: DWORD; pBstrName: PWideString; pBstrDocString: PWideString; pdwHelpContext: PDWORD; pBstrHelpFile: PWideString):HResult;StdCall;
+ {$endif}
+
+ {$ifndef Call_as}
+ Function IsName(szNameBuf: pOleStr; lHashVal: ULONG; OUT pfName: BOOL):HResult;StdCall;
+ {$else}
+ Function IsName(szNameBuf: pOleStr; lHashVal: ULONG; OUT pfName: BOOL; OUT pBstrLibName: WideString):HResult;StdCall;
+ {$endif}
+ {$ifndef Call_as}
+ Function FindName(szNameBuf: pOleStr; lHashVal: ULONG; OUT ppTInfo: ITypeInfo; OUT rgMemId: MEMBERID; VAR pcFound: USHORT):HResult;StdCall;
+ {$else}
+ Function FindName(szNameBuf: pOleStr; lHashVal: ULONG; OUT ppTInfo: ITypeInfo; OUT rgMemId: MEMBERID; VAR pcFound: USHORT; OUT pBstrLibName: WideString):HResult;StdCall;
+ {$endif}
+ {$ifndef Call_as}
+ Procedure ReleaseTLibAttr( pTLibAttr : LPTLIBATTR); StdCall;
+ {$else}
+ Function LocalReleaseTLibAttr:HResult;StdCall;
+ {$endif}
+ End;
+
+ ITypeLib2 = Interface (ITypeLib)
+ ['{00020411-0000-0000-C000-000000000046}']
+ Function GetCustData(CONST guid: TGUID; OUT pVarVal: VARIANT):HResult;StdCall;
+ {$ifndef Call_as}
+ Function GetLibStatistics(OUT pcUniqueNames: ULONG; OUT pcchUniqueNames: ULONG):HResult;StdCall;
+ {$else}
+ Function GetLibStatistics(OUT pcUniqueNames: ULONG; OUT pcchUniqueNames: ULONG):HResult;StdCall;
+ {$endif}
+ {$ifndef Call_as}
+ Function GetDocumentation2(index: WINT; lcid: LCID; pbstrHelpString: PWideString; pdwHelpStringContext: PDWORD; pbstrHelpStringDll: PWideString):HResult;StdCall;
+ {$else}
+ Function GetDocumentation2(index: WINT; lcid: LCID; refPtrFlags: DWORD; pbstrHelpString: PWideString; pdwHelpStringContext: PDWORD; pbstrHelpStringDll: PWideString):HResult;StdCall;
+ {$endif}
+ Function GetAllCustData(OUT pCustData: CUSTDATA):HResult;StdCall;
+ End;
+
+ ITypeChangeEvents= Interface (IUnknown)
+ ['{00020410-0000-0000-C000-000000000046}']
+ Function RequestTypeChange(changeKind: CHANGEKIND; CONST pTInfoBefore: ITypeInfo; pStrName: pOleStr; OUT pfCancel: WINT):HResult;StdCall;
+ Function AfterTypeChange(changeKind: CHANGEKIND; CONST pTInfoAfter: ITypeInfo; pStrName: pOleStr):HResult;StdCall;
+ End;
+
+ IErrorInfo= Interface (IUnknown)
+ ['{1CF2B120-547D-101B-8E65-08002B2BD119}']
+ Function GetGUID(OUT pGUID: TGUID):HResult;StdCall;
+ Function GetSource(OUT pBstrSource: WideString):HResult;StdCall;
+ Function GetDescription(OUT pBstrDescription: WideString):HResult;StdCall;
+ Function GetHelpFile(OUT pBstrHelpFile: WideString):HResult;StdCall;
+ Function GetHelpContext(OUT pdwHelpContext: DWORD):HResult;StdCall;
+ End;
+
+ ICreateErrorInfo= Interface (IUnknown)
+ ['{22F03340-547D-101B-8E65-08002B2BD119}']
+ Function SetGUID(CONST rguid: TGUID):HResult;StdCall;
+ Function SetSource(szSource: pOleStr):HResult;StdCall;
+ Function SetDescription(szDescription: pOleStr):HResult;StdCall;
+ Function SetHelpFile(szHelpFile: pOleStr):HResult;StdCall;
+ Function SetHelpContext(dwHelpContext: DWORD):HResult;StdCall;
+ End;
+
+ ISupportErrorInfo= Interface (IUnknown)
+ ['{DF0B3D60-548F-101B-8E65-08002B2BD119}']
+ Function InterfaceSupportsErrorInfo(CONST riid: TIID):HResult;StdCall;
+ End;
+
+ ITypeFactory = Interface (IUnknown)
+ ['{0000002E-0000-0000-C000-000000000046}']
+ Function CreateFromTypeInfo(CONST pTypeInfo: ITypeInfo; CONST riid: TIID; OUT ppv: IUnknown):HResult;StdCall;
+ End;
+
+ ITypeMarshal = Interface (IUnknown)
+ ['{0000002D-0000-0000-C000-000000000046}']
+ Function Size(pvType: Pointer; dwDestContext: DWORD; pvDestContext: Pointer; OUT pSize: ULONG):HResult;StdCall;
+ Function Marshal(pvType: Pointer; dwDestContext: DWORD; pvDestContext: Pointer; cbBufferLength: ULONG; OUT pBuffer: BYTE; OUT pcbWritten: ULONG):HResult;StdCall;
+ Function Unmarshal(pvType: Pointer; dwFlags: DWORD; cbBufferLength: ULONG; CONST pBuffer: BYTE; OUT pcbRead: ULONG):HResult;StdCall;
+ Function Free(pvType: Pointer):HResult;StdCall;
+ End;
+
+ IRecordInfo = Interface(IUnknown)
+ ['{0000002F-0000-0000-C000-000000000046}']
+ Function RecordInit(pvNew: Pointer):HResult;StdCall;
+ Function RecordClear(pvExisting: Pointer):HResult;StdCall;
+ Function RecordCopy(pvExisting: Pointer; pvNew: Pointer):HResult;StdCall;
+ Function GetGuid(OUT pguid: TGUID):HResult;StdCall;
+ Function GetName(OUT pbstrName: WideString):HResult;StdCall;
+ Function GetSize(OUT pcbSize: ULONG):HResult;StdCall;
+ Function GetTypeInfo(OUT ppTypeInfo: ITypeInfo):HResult;StdCall;
+ Function GetField(pvData: Pointer; szFieldName: pOleStr; OUT pvarField: VARIANT):HResult;StdCall;
+ Function GetFieldNoCopy(pvData: Pointer; szFieldName: pOleStr; OUT pvarField: VARIANT; OUT ppvDataCArray: Pointer):HResult;StdCall;
+ Function PutField(wFlags: ULONG; pvData: Pointer; szFieldName: pOleStr; CONST pvarField: VARIANT):HResult;StdCall;
+ Function PutFieldNoCopy(wFlags: ULONG; pvData: Pointer; szFieldName: pOleStr; CONST pvarField: VARIANT):HResult;StdCall;
+ Function GetFieldNames(VAR pcNames: ULONG; OUT rgBstrNames: WideString):HResult;StdCall;
+ Function IsMatchingType(CONST pRecordInfo : IRecordInfo):Bool;StdCall;
+ Function RecordCreate : Pointer; StdCall;
+ Function RecordCreateCopy(pvSource: Pointer; OUT ppvDest: Pointer):HResult;StdCall;
+ Function RecordDestroy(pvRecord: Pointer):HResult;StdCall;
+ End;
+
+ IErrorLog = Interface (IUnknown)
+ ['{3127CA40-446E-11CE-8135-00AA004BB851}']
+ Function AddError(pszPropName: pOleStr; CONST pExcepInfo: EXCEPINFO):HResult;StdCall;
+ End;
+
+
+ IPropertyBag = Interface (IUnknown)
+ ['{55272A00-42CB-11CE-8135-00AA004BB851}']
+ {$ifndef Call_as}
+ Function Read(pszPropName: pOleStr; VAR pVar: VARIANT; CONST pErrorLog: IErrorLog):HResult;StdCall;
+ {$else}
+ Function Read(pszPropName: pOleStr; OUT pVar: VARIANT; CONST pErrorLog: IErrorLog; varType: DWORD; CONST pUnkObj: IUnknown):HResult;StdCall;
+ {$endif}
+ Function Write(pszPropName: pOleStr; CONST pVar: VARIANT):HResult;StdCall;
+ End;
+
+ IEnumGUID = interface(IUnknown)
+ ['{0002E000-0000-0000-C000-000000000046}']
+ Function Next(celt: UINT; OUT rgelt: TGUID; pceltFetched: pUINT=nil):HResult;StdCall;
+ Function Skip(celt:UINT):HResult;StdCall;
+ Function Reset: HResult;StdCall;
+ Function Clone(out ppenum: IEnumGUID):HResult;StdCall;
+ End;
+
+ IBindHost = interface(IUnknown)
+ ['{FC4801A1-2BA9-11CF-A229-00AA003D7352}']
+ End;
+
+ IServiceProvider = interface(IUnknown)
+ ['{6D5140C1-7436-11CE-8034-00AA006009FA}']
+ Function QueryService(CONST rsid, iid: TGuid; OUT Obj):HResult;StdCall;
+ End;
+
+ PServiceProvider = ^IServiceProvider;
+
+ IParseDisplayName = interface(IUnknown)
+ ['{0000011A-0000-0000-C000-000000000046}']
+ Function ParseDisplayName(CONST bc: IBindCtx; pszDisplayName: POleStr;OUT chEaten: Longint; OUT mkOut: IMoniker): HResult;StdCall;
+ End;
+
+ IOleContainer = interface(IParseDisplayName)
+ ['{0000011B-0000-0000-C000-000000000046}']
+ Function EnumObjects(grfFlags: Longint; OUT Enum: IEnumUnknown):HResult;StdCall;
+ Function LockContainer(fLock: BOOL):HResult;StdCall;
+ End;
+
+ IOleClientSite = interface(IUnknown)
+ ['{00000118-0000-0000-C000-000000000046}']
+ Function SaveObject: HResult;StdCall;
+ Function GetMoniker(dwAssign: Longint; dwWhichMoniker: Longint;OUT mk: IMoniker):HResult;StdCall;
+ Function GetContainer(OUT container: IOleContainer):HResult;StdCall;
+ Function ShowObject:HResult;StdCall;
+ Function OnShowWindow(fShow: BOOL):HResult;StdCall;
+ Function RequestNewObjectLayout:HResult;StdCall;
+ End;
+
+ IOleWindow = interface(IUnknown)
+ ['{00000114-0000-0000-C000-000000000046}']
+ function GetWindow(out wnd: HWnd): HResult; stdcall;
+ function ContextSensitiveHelp(fEnterMode: BOOL): HResult; stdcall;
+ end;
+
+
+ tagOleMenuGroupWidths = record
+ width : array[0..5] Of LONG;
+ end;
+ OLEMENUGROUPWIDTHS = tagOleMenuGroupWidths;
+ TOleMenuGroupWidths = tagOleMenuGroupWidths;
+ LPOLEMENUGROUPWIDTHS = ^OLEMENUGROUPWIDTHS;
+ POleMenuGroupWidths = LPOLEMENUGROUPWIDTHS;
+
+
+ IProvideClassInfo = Interface (IUnknown)
+ ['{B196B283-BAB4-101A-B69C-00AA00341D07}']
+ function GetClassInfo(out pptti : ITypeInfo):HResult; StdCall;
+ end;
+
+
+ IProvideClassInfo2 = Interface (IProvideClassInfo)
+ ['{A6BC3AC0-DBAA-11CE-9DE3-00AA004BB851}']
+ function GetGUID(dwguid:DWord;out pguid:TGUID):HResult; StdCall;
+ end;
+
+{ ******************************************************************************************************************
+ stuff from objbase.h
+ ****************************************************************************************************************** }
+
+ tagOIFI = record
+ cb: UINT;
+ fMDIApp: BOOL;
+ hwndFrame: HWND;
+ haccel: HAccel;
+ cAccelEntries: UINT;
+ end;
+ TOleInPlaceFrameInfo = tagOIFI;
+ POleInPlaceFrameInfo = ^TOleInPlaceFrameInfo;
+ LPOleInPlaceFrameInfo = POleInPlaceFrameInfo;
+ OLEINPLACEFRAMEINFO = tagOIFI;
+
+
+{ redefinitions }
+ function CoCreateGuid(out _para1:TGUID):HRESULT;stdcall;external 'ole32.dll' name 'CoCreateGuid';
+
+{ additional definitions }
+{$ifndef wince}
+ function IsEqualGUID(const guid1,guid2 : TGUID) : Boolean;stdcall;external 'ole32.dll' name 'IsEqualGUID';
+ function IsEqualIID(const iid1,iid2 : TIID) : Boolean;stdcall;external 'ole32.dll' name 'IsEqualGUID';
+ function IsEqualCLSID(const clsid1,clsid2 : TCLSID) : Boolean;stdcall;external 'ole32.dll' name 'IsEqualGUID';
+{$endif wince}
+
+{ OleIdl.h }
+type
+ IOleInPlaceActiveObject = interface;
+
+ IOleAdviseHolder = interface(IUnknown)
+ ['{00000111-0000-0000-C000-000000000046}']
+ function Advise(const advise: IAdviseSink; out dwConnection: DWORD): HResult;StdCall;
+ function Unadvise(dwConnection: DWORD): HResult;StdCall;
+ function EnumAdvise(out enumAdvise: IEnumStatData): HResult;StdCall;
+ function SendOnRename(const mk: IMoniker): HResult;StdCall;
+ function SendOnSave: HResult;StdCall;
+ function SendOnClose: HResult;StdCall;
+ end;
+
+ IEnumOLEVERB = interface(IUnknown)
+ ['{00000104-0000-0000-C000-000000000046}']
+ function Next(celt: ULONG; out elt; pceltFetched: PULONG=nil): HResult;StdCall;
+ function Skip(celt: ULONG): HResult;StdCall;
+ function Reset: HResult;StdCall;
+ function Clone(out ppenum: IEnumOLEVERB): HResult;StdCall;
+ end;
+
+ IDropSource = interface(IUnknown)
+ ['{00000121-0000-0000-C000-000000000046}']
+ function QueryContinueDrag(fEscapePressed: BOOL;
+ grfKeyState: Longint):HResult;StdCall;
+ function GiveFeedback(dwEffect: Longint): HResult;StdCall;
+ end;
+
+ IOleObject = interface(IUnknown)
+ ['{00000112-0000-0000-C000-000000000046}']
+ function SetClientSite(const clientSite: IOleClientSite): HResult;StdCall;
+ function GetClientSite(out clientSite: IOleClientSite): HResult;StdCall;
+ function SetHostNames(szContainerApp: POleStr; szContainerObj: POleStr): HResult;StdCall;
+ function Close(dwSaveOption: DWORD): HResult;StdCall;
+ function SetMoniker(dwWhichMoniker: DWORD; const mk: IMoniker): HResult;StdCall;
+ function GetMoniker(dwAssign: DWORD; dwWhichMoniker: DWORD; out mk: IMoniker): HResult;StdCall;
+ function InitFromData(const dataObject: IDataObject; fCreation: BOOL; dwReserved: DWORD): HResult;StdCall;
+ function GetClipboardData(dwReserved: DWORD; out dataObject: IDataObject): HResult;StdCall;
+ function DoVerb(iVerb: LONG; msg: PMsg; const activeSite: IOleClientSite; lindex: LONG; hwndParent: HWND; const posRect: TRect): HResult;StdCall;
+ function EnumVerbs(out enumOleVerb: IEnumOleVerb): HResult;StdCall;
+ function Update: HResult;StdCall;
+ function IsUpToDate: HResult;StdCall;
+ function GetUserClassID(out clsid: TCLSID): HResult;StdCall;
+ function GetUserType(dwFormOfType: DWORD; out pszUserType: POleStr): HResult;StdCall;
+ function SetExtent(dwDrawAspect: DWORD; const size: TPoint): HResult;StdCall;
+ function GetExtent(dwDrawAspect: DWORD; out size: TPoint): HResult;StdCall;
+ function Advise(const advSink: IAdviseSink; out dwConnection: Longint): HResult;StdCall;
+ function Unadvise(dwConnection: DWORD): HResult;StdCall;
+ function EnumAdvise(out enumAdvise: IEnumStatData): HResult;StdCall;
+ function GetMiscStatus(dwAspect: DWORD; out dwStatus: DWORD): HResult;StdCall;
+ function SetColorScheme(const logpal: TLogPalette): HResult;StdCall;
+ end;
+
+ IDropTarget = interface(IUnknown)
+ ['{00000122-0000-0000-C000-000000000046}']
+ function DragEnter(const dataObj: IDataObject; grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HResult;StdCall;
+ function DragOver(grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HResult;StdCall;
+ function DragLeave: HResult;StdCall;
+ function Drop(const dataObj: IDataObject; grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD):HResult;StdCall;
+ end;
+
+ IOleInPlaceUIWindow = interface(IOleWindow)
+ ['{00000115-0000-0000-C000-000000000046}']
+ function GetBorder(out rectBorder: TRect):HResult;StdCall;
+ function RequestBorderSpace(const borderwidths: TRect):HResult;StdCall;
+ function SetBorderSpace(const borderwidths: TRect):HResult;StdCall;
+ function SetActiveObject(const activeObject: IOleInPlaceActiveObject;pszObjName: POleStr):HResult;StdCall;
+ end;
+
+ IOleInPlaceActiveObject = interface(IOleWindow)
+ ['{00000117-0000-0000-C000-000000000046}']
+ function TranslateAccelerator(var msg: TMsg):HResult;StdCall;
+ function OnFrameWindowActivate(fActivate: BOOL):HResult;StdCall;
+ function OnDocWindowActivate(fActivate: BOOL):HResult;StdCall;
+ function ResizeBorder(const rcBorder: TRect; const uiWindow: IOleInPlaceUIWindow; fFrameWindow: BOOL):HResult;StdCall;
+ function EnableModeless(fEnable: BOOL):HResult;StdCall;
+ end;
+
+ IOleInPlaceFrame = interface(IOleInPlaceUIWindow)
+ ['{00000116-0000-0000-C000-000000000046}']
+ function InsertMenus(hmenuShared: HMenu; var menuWidths: TOleMenuGroupWidths): HResult;StdCall;
+ function SetMenu(hmenuShared: HMenu; holemenu: HMenu; hwndActiveObject: HWnd): HResult;StdCall;
+ function RemoveMenus(hmenuShared: HMenu): HResult;StdCall;
+ function SetStatusText(pszStatusText: POleStr): HResult;StdCall;
+ function EnableModeless(fEnable: BOOL): HResult;StdCall;
+ function TranslateAccelerator(var msg: TMsg; wID: Word): HResult;StdCall;
+ end;
+
+ IOleLink = interface(IUnknown)
+ ['{0000011d-0000-0000-C000-000000000046}']
+ function SetUpdateOptions(dwupdateopt:dword):HResult; stdcall;
+ function GetUpdateOptions(dwupdateopt:pdword):HResult; stdcall;
+ function SetSourceMoniker(pmk : IMoniker;const clsid: TCLSID):HRESULT; stdcall;
+ function GetSourceMoniker(out pmk : IMoniker):HRESULT; stdcall;
+ function SetSourceDisplayName(ppszDisplayName:lpolestr):HResult; stdcall;
+ function GetSourceDisplayName(out ppszDisplayName:lpolestr):HResult; stdcall;
+ function BindToSource(bindflags:DWord;pbc: IBindCTX):HResult; stdcall;
+ function BindIfRunning:HResult; stdcall;
+ function GetBoundSource(out ppunk: IUnKnown):HResult; stdcall;
+ function UnbindSource:HResult; stdcall;
+ function Update(pbc:IBindCtx):HResult; stdcall;
+ end;
+
+ IOleInPlaceSite = interface(IOleWindow)
+ ['{00000119-0000-0000-C000-000000000046}']
+ function CanInPlaceActivate : HResult;
+ function OnInPlaceActivate : HResult;
+ function OnUIActivate : HResult;
+ function GetWindowContext(out ppframe:IOleInPlaceFrame;out ppdoc:IOleInPlaceUIWindow;lprcposrect:LPRECT;lprccliprect:LPRECT;lpframeinfo:LPOLEINPLACEFRAMEINFO):hresult; stdcall;
+ function Scroll(scrollExtant:TSIZE):hresult; stdcall;
+ function OnUIDeactivate(fUndoable:BOOL):hresult; stdcall;
+ function OnInPlaceDeactivate :hresult; stdcall;
+ function DiscardUndoState :hresult; stdcall;
+ function DeactivateAndUndo :hresult; stdcall;
+ function OnPosRectChange(lprcPosRect:LPRect):hresult; stdcall;
+ end;
+
+ IOleInPlaceObject = interface(IOleWindow)
+ ['{00000113-0000-0000-C000-000000000046}']
+ function InPlaceDeactivate : HResult;
+ function UIDeactivate : HResult;
+ function SetObjectRects(lprcPosRect:LPRect;lprcClipRect:LPRect):hresult; stdcall;
+ function ReactivateAndUndo : HResult;
+ end;
+
+ IOleDocumentView = interface(IUnknown)
+ ['{b722bcc6-4e68-101b-a2bc-00aa00404770}']
+ function SetInPlaceSite(ppipsite:IOleInPlaceSite):hresult; stdcall;
+ function GetInPlaceSite(out ppipsite:IOleInPlaceSite):hresult; stdcall;
+ function GetDocument(out ppipsite:Iunknown):hresult; stdcall;
+ function SetRect(prcview:LPRect):hresult; stdcall;
+ function Getrect(prcView:LPRect):hresult; stdcall;
+ function SetRectComplex(prcview:LPRect;prcHScroll:LPRect;prcVScroll:LPRect;prcSizeBox:LPRect):hresult; stdcall;
+ function Show(fshow:Bool) :hresult; stdcall;
+ function UIActivate(fUIActive :BOOL): HResult;
+ function Open :hresult; stdcall;
+ function Closeview(dwreserved:DWORD):hresult; stdcall;
+ function SaveViewState(pstm:IStream):hresult; stdcall;
+ function ApplyViewState(pstm:IStream):hresult; stdcall;
+ function Clone(pipsitenew: IOleInPlaceSite;out ppviewNew:IOleDocumentView):HResult;
+ end;
+
+ IEnumOleDocumentViews = Interface(IUnknown)
+ ['{b722bcc8-4e68-101b-a2bc-00aa00404770}']
+ function Next (CViews:ULONG; out rgpview:IOleDocumentView;pcfetched:pulong):hresult; stdcall;
+ function Skip (CViews:ULong):hresult; stdcall;
+ function Reset:HResult; stdcall;
+ function Clone (out ppenum :IEnumOleDocumentViews) :HResult; stdcall;
+ end;
+
+ IOleDocument = interface(IUnknown)
+ ['{b722bcc5-4e68-101b-a2bc-00aa00404770}']
+ function CreateView(pipsite:IOleInPlaceSite;pstm:IStream;dwReserved:DWord;out ppview : IOleDocumentView):hresult; stdcall;
+ function GetDocMiscStatus(pdwstatus:PDWord):hresult; stdcall;
+ function EnumViews(out ppenum:IEnumOleDocumentViews;out ppview:IOleDocumentView):hresult; stdcall;
+ end;
+
+ IOleDocumentSite = interface(IUnknown)
+ ['{b722bcc7-4e68-101b-a2bc-00aa00404770}']
+ function ActivateMe(pviewtoactivate:IOleDocumentView):hresult; stdcall;
+ end;
+
+ IContinueCallback = interface(IUnknown)
+ ['{b722bcca-4e68-101b-a2bc-00aa00404770}']
+ function FContinue:HResult;Stdcall;
+ function FContinuePrinting( nCntPrinted:LONG;nCurPage:Long;pwzprintstatus:polestr):HResult;Stdcall;
+ end;
+
+
+{ ObjSafe.idl}
+ IObjectSafety = interface(IUnknown)
+ ['{CB5BDC81-93C1-11cf-8F20-00805F2CD064}']
+ function GetInterfaceSafetyOptions(const riid:Tiid; out pdwsupportedoptions: dword;out pdwenabledoptions: dword):HRESULT; stdcall;
+ function SetInterfaceSafetyOptions(const riid:Tiid; const dwoptionsetmask: dword;const dwenabledoptions : dword):HRESULT; stdcall;
+ end;
+
+ TContinueCallback = function (dwcontinue:ULONG_PTR):BOOL; stdcall;
+
+
+ IViewObject = interface(IUnknown)
+ ['{0000010d-0000-0000-C000-000000000046}']
+ function Draw(dwDrawAspect:DWord;LIndex:Long;pvaspect:pointer;ptd:PDVTARGETDEVICE;hdcTargetDev:HDC; hdcDraw:HDC;lprcBounds:PRECTL;lprcWBounds:PRECTL;pfncontinue:TContinueCallback;dwcontinue:ULONG_PTR):HResult; stdcall;
+ function GetColorSet(wDrawAspect:DWord;LIndex:Long;pvaspect:pointer;ptd:PDVTARGETDEVICE;hdcTargetDev:HDC;var ppcolorset:PLogPalette):HREsult; stdcall;
+ function Freeze(dwDrawAspect:DWord;LIndex:Long;pvaspect:pointer;pdwfreeze:pdword):HResult;stdcall;
+ function Unfreeze(dwfreeze:dword):HResult; stdcall;
+ function SetAdvise(aspects:DWORD;advf:DWORD;padvSink:IAdviseSink):HRESULT;stdcall;
+ function Getadvise(paspects:pdword;padvf:pdword;out ppadvsink: IADviseSink):HRESULT;stdcall;
+ end;
+
+ IViewObject2 = interface(IViewObject)
+ ['{00000127-0000-0000-C000-000000000046}']
+ function GetExtent(dwDrawAspect:dword;lindex:DWord;ptd:pDVTARGETDEVICE;lpsizel:LPSIZEL):HRESULT;stdcall;
+ end;
+
+
+
+{ COMCAT}
+
+Const CATDESC_MAX = 128;
+
+Type
+ CATID = TGUID;
+ TCATID = TGUID;
+ PCATID = PGUID;
+ tagCATEGORYINFO = packed record
+ catid : CATID;
+ LCID : lcid;
+ szDescription : array[0..CATDESC_MAX-1] of WideChar;
+ end;
+
+ CATEGORYINFO = tagCATEGORYINFO;
+ TCATEGORYINFO = tagCATEGORYINFO;
+ LPCATEGORYINFO = ^tagCATEGORYINFO;
+ PCATEGORYINFO = LPCATEGORYINFO;
+
+ IEnumCLSID = IEnumGUID;
+ IEnumCategoryInfo = interface(IUnknown)
+ ['{0002E011-0000-0000-C000-000000000046}']
+ function Next(celt: ULONG; out rgelt: TCategoryInfo; out pceltFetched: ULONG): HResult; stdcall;
+ function Skip(celt:ULONG):HResult; StdCall;
+ function Reset:HResult; StdCall;
+ function CLone(Out ppenum : IEnumCategoryInfo):HResult;StdCall;
+ end;
+
+ ICatRegister = interface (IUnknown)
+ ['{0002E012-0000-0000-C000-000000000046}']
+ function RegisterCategories (cCategories:ULONG;rgCategoryInfo : PCategoryInfo):HResult;Stdcall;
+ function UnRegisterCategories (cCategories:ULONG;PCatid :PCATID):HResult;Stdcall;
+ function RegisterClassImplCategories (const rclsid:TGUID;cCategories:ULONG; rgCatid :PCATID):HResult;Stdcall;
+ function UnRegisterClassImplCategories (const rclsid:TGUID;cCategories:ULONG; rgCatid :PCATID):HResult;Stdcall;
+ function RegisterClassReqCategories (const rclsid:TGUID;cCategories:ULONG; rgCatid :PCATID):HResult;Stdcall;
+ function UnRegisterClassReqCategories (const rclsid:TGUID;cCategories:ULONG; rgCatid :PCATID):HResult;Stdcall;
+ end;
+
+ ICatInformation = interface(IUnknown)
+ ['{0002E013-0000-0000-C000-000000000046}']
+ function EnumCategories(lcid:lcid;out ppenumCategoryInfo : IEnumCategoryInfo):HResult; StdCall;
+ function GetCategoryDesc(rcatid:PCATID;lcid:LCID;out pszdesc:lpwstr):HResult; StdCall;
+ function EnumClassesOfCategories(cImplemented : ULong; rgcatidImpl:PCATID;cRequired:ULong; rgcatidreq:PCATID; out ppenumclsid : IEnumClsID):HResult; StdCall;
+ function ISClassOfCategories(rclsid:pclsid;cImplemented:ULong;rgcatidimpl:PCATID;CRequired:ULONG;rgcatidreq : pcatid):HResult; StdCall;
+ function EnumImplCategoriesOfClass(rclsid:pclsid;out ppenumclsid : IEnumClsID):HResult; StdCall;
+ function EnumReqCategoriesOfClass(rclsid:pclsid;out ppenumclsid : IEnumClsID):HResult; StdCall;
+ end;
+
+ IPropertySetStorage = Interface(IUnknown)
+ ['{0000013A-0000-0000-C000-000000000046}']
+ function Create(const rfmtid:FMTID; const pclsid:CLSID; grfFlags:DWORD; grfMode:DWORD; out ppprstg:IPropertyStorage):HRESULT; StdCall;
+ function Open(const fmtid:FMTID; grfMode:DWORD; out ppprstg:IPropertyStorage):HRESULT; StdCall;
+ function Delete(const rfmtid:FMTID):HRESULT; StdCall;
+ function Enum(out ppenum:IEnumSTATPROPSETSTG):HRESULT; StdCall;
+ end;
+
+ IEnumSTATPROPSTG = interface( IUnknown)
+ ['{00000139-0000-0000-C000-000000000046}']
+ function Next(celt:ULONG; var rgelt:STATPROPSTG; pceltFetched:pULONG):HRESULT; StdCall;
+ function Skip(celt:ULONG):HRESULT; StdCall;
+ function Reset:HRESULT; StdCall;
+ function Clone(out ppenum:IEnumSTATPROPSTG):HRESULT; StdCall;
+ end;
+
+ IEnumSTATPROPSETSTG = interface( IUnknown)
+ ['{0000013B-0000-0000-C000-000000000046}']
+ function Next(celt:ULONG; var rgelt:STATPROPSETSTG; pceltFetched:pULONG):HRESULT; StdCall;
+ function Skip(celt:ULONG):HRESULT; StdCall;
+ function Reset:HRESULT; StdCall;
+ function Clone(out ppenum:IEnumSTATPROPSETSTG):HRESULT; StdCall;
+ end;
+
+ IPropertyStorage = interface(IUnknown)
+ ['{00000138-0000-0000-C000-000000000046}']
+ function ReadMultiple(cpspec:ULONG; rgpspec:pPROPSPEC; rgpropvar:pPROPVARIANT):HRESULT; StdCall;
+ function WriteMultiple(cpspec:ULONG; rgpspec:pPROPSPEC; rgpropvar:pPROPVARIANT; propidNameFirst:PROPID):HRESULT; StdCall;
+ function DeleteMultiple(cpspec:ULONG; rgpspec:pPROPSPEC):HRESULT; StdCall;
+ function ReadPropertyNames(cpspec:ULONG; rgpropid:pPROPID; rgpropvar:plpolestr):HRESULT; StdCall;
+ function WritePropertyNames(cpspec:ULONG; rgpspec:pPROPID; rgpropvar:plpolestr):HRESULT; StdCall;
+ function DeletePropertyNames(cpspec:ULONG; rgpspec:pPROPid):HRESULT; StdCall;
+ function Commit(grfCommitFlags:DWORD):HRESULT; StdCall;
+ function Revert:HRESULT; StdCall;
+ function Enum(out ppenum:IEnumSTATPROPSTG):HRESULT; StdCall;
+ function SetTimes(pctime:PFILETIME; patime:PFILETIME; pmtime:PFILETIME):HRESULT; StdCall;
+ function SetClass(clsid:pCLSID):HRESULT; StdCall;
+ function Stat(pstatpsstg:pSTATPROPSETSTG):HRESULT; StdCall;
+ end;
+
+{ ole2.h }
+
+ type
+ WINOLEAPI = HResult;
+ TLCID = DWORD; // is this needed (duplicate from windows?)
+
+ const
+ OLEIVERB_PRIMARY = 0;
+ OLEIVERB_SHOW = -(1);
+ OLEIVERB_OPEN = -(2);
+ OLEIVERB_HIDE = -(3);
+ OLEIVERB_UIACTIVATE = -(4);
+ OLEIVERB_INPLACEACTIVATE = -(5);
+ OLEIVERB_DISCARDUNDOSTATE = -(6);
+ { for OleCreateEmbeddingHelper flags; roles low word; options high word }
+ EMBDHLP_INPROC_HANDLER = $0000;
+ EMBDHLP_INPROC_SERVER = $0001;
+ EMBDHLP_CREATENOW = $00000000;
+ EMBDHLP_DELAYCREATE = $00010000;
+ { extended create function flags }
+ OLECREATE_LEAVERUNNING = $00000001;
+ { pull the MIDL generated header }
+{$ifndef wince}
+ function OleBuildVersion:DWORD;stdcall;external 'ole32.dll' name 'OleBuildVersion';
+{$endif wince}
+ { helper functions }
+ function ReadClassStg(pStg:IStorage; pclsid:PCLSID):WINOLEAPI;stdcall;external 'ole32.dll' name 'ReadClassStg';
+ function WriteClassStg(pStg:IStorage;const rclsid:TCLSID):WINOLEAPI;stdcall;external 'ole32.dll' name 'WriteClassStg';
+ function ReadClassStm(pStm:IStream; pclsid:PCLSID):WINOLEAPI;stdcall;external 'ole32.dll' name 'ReadClassStm';
+ function WriteClassStm(pStm:IStream;const rclsid:TCLSID):WINOLEAPI;stdcall;external 'ole32.dll' name 'WriteClassStm';
+{$ifndef wince}
+ function WriteFmtUserTypeStg(pstg:IStorage; cf:CLIPFORMAT; lpszUserType:LPOLESTR):WINOLEAPI;stdcall;external 'ole32.dll' name 'WriteFmtUserTypeStg';
+ function ReadFmtUserTypeStg(pstg:IStorage; pcf:PCLIPFORMAT;out lplpszUserType:POleStr):WINOLEAPI;stdcall;external 'ole32.dll' name 'ReadFmtUserTypeStg';
+
+ { init/term }
+ function OleInitialize(pvReserved:LPVOID):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleInitialize';
+ procedure OleUninitialize;stdcall;external 'ole32.dll' name 'OleUninitialize';
+
+ { APIs to query whether (Embedded/Linked) object can be created from
+ the data object }
+ function OleQueryLinkFromData(pSrcDataObject:IDataObject):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleQueryLinkFromData';
+ function OleQueryCreateFromData(pSrcDataObject:IDataObject):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleQueryCreateFromData';
+{$endif wince}
+ { Object creation APIs }
+ function OleCreate(const rclsid:TCLSID; const riid:TIID;
+ renderopt:DWORD; pFormatEtc:LPFORMATETC; pClientSite:IOleClientSite;
+ pStg:IStorage; out ppvObj):WINOLEAPI;stdcall;external 'ole32.dll' name
+ 'OleCreate';
+{$ifndef wince}
+ function OleCreateEx(const rclsid:TCLSID; const riid:TIID; dwFlags:DWORD; renderopt:DWORD; cFormats:ULONG;
+ rgAdvf:PDWORD; rgFormatEtc:LPFORMATETC; lpAdviseSink:IAdviseSink; rgdwConnection:PDWORD; pClientSite:IOleClientSite;
+ pStg:IStorage; out ppvObj):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleCreateEx';
+
+ function OleCreateFromData(pSrcDataObj:IDataObject; const riid:TIID; renderopt:DWORD; pFormatEtc:LPFORMATETC; pClientSite:IOleClientSite;
+ pStg:IStorage; out ppvObj):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleCreateFromData';
+
+ function OleCreateFromDataEx(pSrcDataObj:IDataObject; const riid:TIID; dwFlags:DWORD; renderopt:DWORD; cFormats:ULONG;
+ rgAdvf:PDWORD; rgFormatEtc:LPFORMATETC; lpAdviseSink:IAdviseSink; rgdwConnection:PDWORD; pClientSite:IOleClientSite;
+ pStg:IStorage; out ppvObj):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleCreateFromDataEx';
+
+ function OleCreateLinkFromData(pSrcDataObj:IDataObject; const riid:TIID; renderopt:DWORD; pFormatEtc:LPFORMATETC; pClientSite:IOleClientSite;
+ pStg:IStorage; out ppvObj):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleCreateLinkFromData';
+
+ function OleCreateLinkFromDataEx(pSrcDataObj:IDataObject; const riid:TIID; dwFlags:DWORD; renderopt:DWORD; cFormats:ULONG;
+ rgAdvf:PDWORD; rgFormatEtc:LPFORMATETC; lpAdviseSink:IAdviseSink; rgdwConnection:PDWORD; pClientSite:IOleClientSite;
+ pStg:IStorage; out ppvObj):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleCreateLinkFromDataEx';
+
+ function OleCreateStaticFromData(pSrcDataObj:IDataObject; const iid:TIID; renderopt:DWORD; pFormatEtc:LPFORMATETC; pClientSite:IOleClientSite;
+ pStg:IStorage; out ppvObj):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleCreateStaticFromData';
+
+ function OleCreateLink(pmkLinkSrc:IMoniker; const riid:TIID; renderopt:DWORD; lpFormatEtc:LPFORMATETC; pClientSite:IOleClientSite;
+ pStg:IStorage; out ppvObj):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleCreateLink';
+
+ function OleCreateLinkEx(pmkLinkSrc:IMoniker; const riid:TIID; dwFlags:DWORD; renderopt:DWORD; cFormats:ULONG;
+ rgAdvf:PDWORD; rgFormatEtc:LPFORMATETC; lpAdviseSink:IAdviseSink; rgdwConnection:PDWORD; pClientSite:IOleClientSite;
+ pStg:IStorage; out ppvObj):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleCreateLinkEx';
+
+ function OleCreateLinkToFile(lpszFileName:POleStr; const riid:TIID; renderopt:DWORD; lpFormatEtc:LPFORMATETC; pClientSite:IOleClientSite;
+ pStg:IStorage; out ppvObj):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleCreateLinkToFile';
+
+ function OleCreateLinkToFileEx(lpszFileName:POleStr; const riid:TIID; dwFlags:DWORD; renderopt:DWORD; cFormats:ULONG;
+ rgAdvf:PDWORD; rgFormatEtc:LPFORMATETC; lpAdviseSink:IAdviseSink; rgdwConnection:PDWORD; pClientSite:IOleClientSite;
+ pStg:IStorage; out ppvObj):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleCreateLinkToFileEx';
+
+ function OleCreateFromFile(const rclsid:TCLSID; lpszFileName:POleStr; const riid:TIID; renderopt:DWORD; lpFormatEtc:LPFORMATETC;
+ pClientSite:IOleClientSite; pStg:IStorage; out ppvObj):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleCreateFromFile';
+
+ function OleCreateFromFileEx(const rclsid:TCLSID; lpszFileName:POleStr; const riid:TIID; dwFlags:DWORD; renderopt:DWORD;
+ cFormats:ULONG; rgAdvf:PDWORD; rgFormatEtc:LPFORMATETC; lpAdviseSink:IAdviseSink; rgdwConnection:PDWORD;
+ pClientSite:IOleClientSite; pStg:IStorage; out ppvObj):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleCreateFromFileEx';
+
+ function OleLoad(pStg:IStorage; const riid:TIID; pClientSite:IOleClientSite; out ppvObj):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleLoad';
+ function OleLoadFromStream(pStm:IStream; const iidInterface:TIID; out ppvObj):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleLoadFromStream';
+ function OleSaveToStream(pPStm:IPersistStream; pStm:IStream):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleSaveToStream';
+ function OleNoteObjectVisible(pUnknown:IUnknown; fVisible:BOOL):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleNoteObjectVisible';
+{$endif wince}
+ function OleSave(pPS:IPersistStorage; pStg:IStorage; fSameAsLoad:BOOL):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleSave';
+ function OleSetContainedObject(pUnknown:IUnknown; fContained:BOOL):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleSetContainedObject';
+
+ { Drag/Drop APIs }
+{$ifndef wince}
+ function RegisterDragDrop(hwnd:HWND; pDropTarget:IDropTarget):WINOLEAPI;stdcall;external 'ole32.dll' name 'RegisterDragDrop';
+ function RevokeDragDrop(hwnd:HWND):WINOLEAPI;stdcall;external 'ole32.dll' name 'RevokeDragDrop';
+ function DoDragDrop(pDataObj:IDataObject; pDropSource:IDropSource; dwOKEffects:DWORD; pdwEffect:LPDWORD):WINOLEAPI;stdcall;external 'ole32.dll' name 'DoDragDrop';
+
+ { Clipboard APIs }
+ function OleSetClipboard(pDataObj:IDataObject):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleSetClipboard';
+ function OleGetClipboard(out ppDataObj:IDataObject):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleGetClipboard';
+ function OleFlushClipboard:WINOLEAPI;stdcall;external 'ole32.dll' name 'OleFlushClipboard';
+ function OleIsCurrentClipboard(pDataObj:IDataObject):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleIsCurrentClipboard';
+{$endif wince}
+
+type
+ HOLEMENU = HMenu;
+
+ { InPlace Editing APIs }
+{$ifndef wince}
+ function OleCreateMenuDescriptor(hmenuCombined:HMENU; lpMenuWidths:LPOLEMENUGROUPWIDTHS):HOLEMENU;stdcall;external 'ole32.dll' name 'OleCreateMenuDescriptor';
+ function OleDestroyMenuDescriptor(holemenu:HOLEMENU):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleDestroyMenuDescriptor';
+ function OleTranslateAccelerator(lpFrame:IOleInPlaceFrame; lpFrameInfo:TOleInPlaceFrameInfo; lpmsg:LPMSG):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleTranslateAccelerator';
+{$endif wince}
+ function OleSetMenuDescriptor(holemenu:HOLEMENU; hwndFrame:HWND; hwndActiveObject:HWND; lpFrame:IOleInPlaceFrame; lpActiveObj:IOleInPlaceActiveObject):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleSetMenuDescriptor';
+
+ { Helper APIs }
+{$ifndef wince}
+ function OleDuplicateData(hSrc:HANDLE; cfFormat:CLIPFORMAT; uiFlags:UINT):HANDLE;stdcall;external 'ole32.dll' name 'OleDuplicateData';
+ function OleLockRunning(pUnknown:IUnknown; fLock:BOOL; fLastUnlockCloses:BOOL):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleLockRunning';
+ function OleCreateDefaultHandler(const clsid:TCLSID; pUnkOuter:IUnknown; const riid:TIID; out lplpObj):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleCreateDefaultHandler';
+ function OleCreateEmbeddingHelper(const clsid:TCLSID; pUnkOuter:IUnknown; flags:DWORD; pCF:IClassFactory; const riid:TIID;
+ out lplpObj):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleCreateEmbeddingHelper';
+ function IsAccelerator(hAccel:HACCEL; cAccelEntries:longint; lpMsg:LPMSG; lpwCmd:PWORD):BOOL;stdcall;external 'ole32.dll' name 'IsAccelerator';
+{$endif wince}
+ function OleDraw(pUnknown:IUnknown; dwAspect:DWORD; hdcDraw:HDC;const lprcBounds:TRect):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleDraw';
+ function OleRun(pUnknown:IUnknown):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleRun';
+ function OleIsRunning(pObject:IOleObject):BOOL;stdcall;external 'ole32.dll' name 'OleIsRunning';
+
+ procedure ReleaseStgMedium(var _para1:STGMEDIUM);stdcall;external 'ole32.dll' name 'ReleaseStgMedium';
+ procedure ReleaseStgMedium(_para1:LPSTGMEDIUM);stdcall;external 'ole32.dll' name 'ReleaseStgMedium';
+ function CreateOleAdviseHolder(out ppOAHolder:IOleAdviseHolder):WINOLEAPI;stdcall;external 'ole32.dll' name 'CreateOleAdviseHolder';
+
+ { Icon extraction Helper APIs }
+{$ifndef wince}
+ function OleGetIconOfFile(lpszPath:LPOLESTR; fUseFileAsLabel:BOOL):HGLOBAL;stdcall;external 'ole32.dll' name 'OleGetIconOfFile';
+ function OleGetIconOfClass(const rclsid:TCLSID; lpszLabel:LPOLESTR; fUseTypeAsLabel:BOOL):HGLOBAL;stdcall;external 'ole32.dll' name 'OleGetIconOfClass';
+ function OleMetafilePictFromIconAndLabel(hIcon:HICON; lpszLabel:LPOLESTR; lpszSourceFile:LPOLESTR; iIconIndex:UINT):HGLOBAL;stdcall;external 'ole32.dll' name 'OleMetafilePictFromIconAndLabel';
+{$endif wince}
+
+ { Registration Database Helper APIs }
+{$ifndef wince}
+ function OleRegGetUserType(const clsid:TCLSID; dwFormOfType:DWORD;out pszUserType:POleStr):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleRegGetUserType';
+ function OleRegGetMiscStatus(const clsid:TCLSID; dwAspect:DWORD; pdwStatus:PDWORD):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleRegGetMiscStatus';
+ function OleRegEnumFormatEtc(const clsid:TCLSID; dwDirection:DWORD;out ppenum:IEnumFormatEtc):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleRegEnumFormatEtc';
+ function OleRegEnumVerbs(const clsid:TCLSID;out ppenum:IEnumOLEVERB):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleRegEnumVerbs';
+{$endif wince}
+
+{$ifdef _MAC}
+ { WlmOLE helper APIs }
+
+ function WlmOleCheckoutMacInterface(pUnk:IUnknown; out ppv):WINOLEAPI;stdcall;external 'ole32.dll' name 'WlmOleCheckoutMacInterface';
+
+ function WlmOleCheckinMacInterface(pUnk:IUnknown):WINOLEAPI;stdcall;external 'ole32.dll' name 'WlmOleCheckinMacInterface';
+
+ function WlmOleWrapMacInterface(pUnk:IUnknown; const riid:TIID; out ppv):WINOLEAPI;stdcall;external 'ole32.dll' name 'WlmOleWrapMacInterface';
+
+ function WlmOleUnwrapMacInterface(pv:LPVOID):WINOLEAPI;stdcall;external 'ole32.dll' name 'WlmOleUnwrapMacInterface';
+
+ function WlmOleCheckoutWinInterface(pUnk:LPVOID; ppv:PIUnknown):WINOLEAPI;stdcall;external 'ole32.dll' name 'WlmOleCheckoutWinInterface';
+
+ function WlmOleCheckinWinInterface(pUnk:LPVOID):WINOLEAPI;stdcall;external 'ole32.dll' name 'WlmOleCheckinWinInterface';
+
+ function WlmOleWrapWinInterface(pUnk:LPVOID; const riid:TIID; ppv:PIUnknown):WINOLEAPI;stdcall;external 'ole32.dll' name 'WlmOleWrapWinInterface';
+
+ function WlmOleUnwrapWinInterface(pv:LPVOID):WINOLEAPI;stdcall;external 'ole32.dll' name 'WlmOleUnwrapWinInterface';
+
+ procedure WlmOleVersion;stdcall;external 'ole32.dll' name 'WlmOleVersion';
+
+ procedure WlmOleSetInPlaceWindow(hwnd:HWND);stdcall;external 'ole32.dll' name 'WlmOleSetInPlaceWindow';
+
+ { typedef HRESULT (STDAPICALLTYPE* OLEWRAPPROC) (TIID riid, LPVOID* ppvWin, LPVOID* ppvMac); }
+ function WlmOleRegisterUserWrap(procNew:OLEWRAPPROC; pprocOld:POLEWRAPPROC):WINOLEAPI;stdcall;external 'ole32.dll' name 'WlmOleRegisterUserWrap';
+
+{$endif}
+ { OLE 1.0 conversion APIS }
+ {**** OLE 1.0 OLESTREAM declarations ************************************ }
+
+ type
+ LPOLESTREAM = ^_OLESTREAM;
+ _OLESTREAMVTBL = record
+ Get : function (p : POleStr;out o;dw : DWORD) : DWORD;
+ Put : function (p : POleStr;const o;dw : DWORD) : DWORD;
+ end;
+ OLESTREAMVTBL = _OLESTREAMVTBL;
+
+ LPOLESTREAMVTBL = OLESTREAMVTBL;
+
+ _OLESTREAM = record
+ lpstbl : LPOLESTREAMVTBL;
+ end;
+ OLESTREAM = _OLESTREAM;
+(* Const before type ignored *)
+
+{$ifndef wince}
+ function OleConvertOLESTREAMToIStorage(_lpolestream:LPOLESTREAM; pstg:IStorage; ptd:PDVTARGETDEVICE):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleConvertOLESTREAMToIStorage';
+ function OleConvertIStorageToOLESTREAM(pstg:IStorage; lpolestream:LPOLESTREAM):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleConvertIStorageToOLESTREAM';
+{$endif wince}
+
+ { Storage Utility APIs }
+ function GetHGlobalFromILockBytes(plkbyt:ILockBytes;out phglobal:HGLOBAL):WINOLEAPI;stdcall;external 'ole32.dll' name 'GetHGlobalFromILockBytes';
+ function CreateStreamOnHGlobal(hGlobal:HGLOBAL; fDeleteOnRelease:BOOL;out stm:IStream):WINOLEAPI;stdcall;external 'ole32.dll' name 'CreateStreamOnHGlobal';
+{$ifndef wince}
+ function CreateILockBytesOnHGlobal(hGlobal:HGLOBAL; fDeleteOnRelease:BOOL;out pplkbyt:ILockBytes):WINOLEAPI;stdcall;external 'ole32.dll' name 'CreateILockBytesOnHGlobal';
+ function GetHGlobalFromStream(pstm:IStream;out phglobal:HGLOBAL):WINOLEAPI;stdcall;external 'ole32.dll' name 'GetHGlobalFromStream';
+{$endif wince}
+
+ { ConvertTo APIS }
+{$ifndef wince}
+ function OleDoAutoConvert(pStg:IStorage; pClsidNew:LPCLSID):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleDoAutoConvert';
+ function OleGetAutoConvert(const clsidOld:TCLSID; pClsidNew:LPCLSID):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleGetAutoConvert';
+ function OleSetAutoConvert(const clsidOld:TCLSID; clsidNew:TCLSID):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleSetAutoConvert';
+ function GetConvertStg(pStg:IStorage):WINOLEAPI;stdcall;external 'ole32.dll' name 'GetConvertStg';
+ function SetConvertStg(pStg:IStorage; fConvert:BOOL):WINOLEAPI;stdcall;external 'ole32.dll' name 'SetConvertStg';
+
+ { Presentation data to OLESTREAM }
+ { format }
+ { width }
+ { height }
+ { size bytes }
+ { bits }
+ function OleConvertIStorageToOLESTREAMEx(pstg:IStorage; cfFormat:CLIPFORMAT; lWidth:LONG; lHeight:LONG; dwSize:DWORD;
+ pmedium:LPSTGMEDIUM; polestm:LPOLESTREAM):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleConvertIStorageToOLESTREAMEx';
+
+ { Presentation data from OLESTREAM }
+ { format }
+ { width }
+ { height }
+ { size bytes }
+ function OleConvertOLESTREAMToIStorageEx(polestm:LPOLESTREAM; pstg:IStorage; pcfFormat:PCLIPFORMAT; plwWidth:PLONG; plHeight:PLONG;
+ pdwSize:PDWORD; pmedium:LPSTGMEDIUM):WINOLEAPI;stdcall;external 'ole32.dll' name 'OleConvertOLESTREAMToIStorageEx';
+{$endif wince}
+
+const
+ DROPEFFECT_NONE = 0;
+ DROPEFFECT_COPY = 1;
+ DROPEFFECT_MOVE = 2;
+ DROPEFFECT_LINK = 4;
+ DROPEFFECT_SCROLL = dword($80000000);
+
+
+type
+ BORDERWIDTHS = TRect;
+ LPBORDERWIDTHS = PRect;
+ LPCBORDERWIDTHS = PRect;
+
+ TBorderWidths = TRect;
+ PBorderWidths = PRect;
+
+ function CoInitializeEx(_para1:LPVOID; _para2:DWORD):HRESULT;stdcall; external 'ole32.dll' name 'CoInitializeEx';
+ procedure CoUninitialize;stdcall; external 'ole32.dll' name 'CoUninitialize';
+ function CoGetClassObject(const _para1:TCLSID; _para2:DWORD; _para3:PVOID; const _para4:TIID; out _para5):HRESULT;stdcall; external 'ole32.dll' name 'CoGetClassObject';
+ function CoLoadLibrary(_para1:LPOLESTR; _para2:BOOL):THandle;stdcall; external 'ole32.dll' name 'CoLoadLibrary';
+ procedure CoFreeLibrary(_para1:THandle);stdcall; external 'ole32.dll' name 'CoFreeLibrary';
+ procedure CoFreeUnusedLibraries;stdcall; external 'ole32.dll' name 'CoFreeUnusedLibraries';
+ function CoCreateInstance(const _para1:TCLSID; _para2:IUnknown; _para3:DWORD;const _para4:TIID;out _para5):HRESULT;stdcall; external 'ole32.dll' name 'CoCreateInstance';
+ function StringFromCLSID(const _para1:TCLSID; out _para2:POLESTR):HRESULT;stdcall; external 'ole32.dll' name 'StringFromCLSID';
+ function CLSIDFromString(_para1:LPOLESTR; _para2:LPCLSID):HRESULT;stdcall; external 'ole32.dll' name 'CLSIDFromString';
+ function StringFromIID(const _para1:TIID; out _para2:POLESTR):HRESULT;stdcall; external 'ole32.dll' name 'StringFromIID';
+ function ProgIDFromCLSID(para:PCLSID; out _para2:POLESTR):HRESULT;stdcall; external 'ole32.dll' name 'ProgIDFromCLSID';
+ function ProgIDFromCLSID(const _para1:TCLSID; out _para2:POLESTR):HRESULT;stdcall; external 'ole32.dll' name 'ProgIDFromCLSID';
+ function CLSIDFromProgID(_para1:POLESTR; _para2:LPCLSID):HRESULT;stdcall; external 'ole32.dll' name 'CLSIDFromProgID';
+ function CLSIDFromProgID(_para1:POLESTR; out _para2:TCLSID):HRESULT;stdcall; external 'ole32.dll' name 'CLSIDFromProgID';
+ function StringFromGUID2(const _para1:TGUID; _para2:LPOLESTR; _para3:longint):longint;stdcall; external 'ole32.dll' name 'StringFromGUID2';
+ function CoCreateGuid(_para1:PGUID):HRESULT;stdcall; external 'ole32.dll' name 'CoCreateGuid';
+{$ifndef wince}
+ function CoBuildVersion:DWORD;stdcall; external 'ole32.dll' name 'CoBuildVersion';
+ function CoInitialize(_para1:PVOID):HRESULT;stdcall; external 'ole32.dll' name 'CoInitialize';
+ function CoGetMalloc(_para1:DWORD; out _para2:IMalloc):HRESULT;stdcall; external 'ole32.dll' name 'CoGetMalloc';
+ function CoGetCurrentProcess:DWORD;stdcall; external 'ole32.dll' name 'CoGetCurrentProcess';
+ function CoRegisterMallocSpy(_para1:IMallocSpy):HRESULT;stdcall; external 'ole32.dll' name 'CoRegisterMallocSpy';
+ function CoRevokeMallocSpy:HRESULT;stdcall; external 'ole32.dll' name 'CoRevokeMallocSpy';
+ function CoCreateStandardMalloc(_para1:DWORD; out _para2:IMalloc):HRESULT;stdcall; external 'ole32.dll' name 'CoGetMalloc';
+ function CoRegisterClassObject(const _para1:TCLSID; _para2:IUnknown; _para3:DWORD; _para4:DWORD; _para5:PDWORD):HRESULT;stdcall; external 'ole32.dll' name 'CoRegisterClassObject';
+ function CoRevokeClassObject(_para1:DWORD):HRESULT;stdcall; external 'ole32.dll' name 'CoRevokeClassObject';
+ function CoGetMarshalSizeMax(_para1:PULONG;const _para2:TIID; _para3:IUnknown; _para4:DWORD; _para5:PVOID;
+ _para6:DWORD):HRESULT;stdcall; external 'ole32.dll' name 'CoGetMarshalSizeMax';
+ function CoMarshalInterface(_para1:IStream;const _para2:TIID; _para3:IUnknown; _para4:DWORD; _para5:PVOID;
+ _para6:DWORD):HRESULT;stdcall; external 'ole32.dll' name 'CoMarshalInterface';
+ function CoUnmarshalInterface(_para1:IStream;const _para2:TIID; out _para3):HRESULT;stdcall; external 'ole32.dll' name 'CoUnmarshalInterface';
+ function CoMarshalHresult(_para1:IStream; _para2:HRESULT):HRESULT;stdcall; external 'ole32.dll' name 'CoMarshalHresult';
+ function CoUnmarshalHresult(_para1:IStream; _para2:HRESULT):HRESULT;stdcall; external 'ole32.dll' name 'CoUnmarshalHresult';
+ function CoReleaseMarshalData(_para1:IStream):HRESULT;stdcall; external 'ole32.dll' name 'CoReleaseMarshalData';
+ function CoDisconnectObject(_para1:IUnknown; _para2:DWORD):HRESULT;stdcall; external 'ole32.dll' name 'CoDisconnectObject';
+ function CoLockObjectExternal(_para1:IUnknown; _para2:BOOL; _para3:BOOL):HRESULT;stdcall; external 'ole32.dll' name 'CoLockObjectExternal';
+ function CoGetStandardMarshal(const _para1:TIID; _para2:IUnknown; _para3:DWORD; _para4:PVOID; _para5:DWORD;
+ out _para6:IMarshal):HRESULT;stdcall; external 'ole32.dll' name 'CoGetStandardMarshal';
+ function CoGetStdMarshalEx(_para1:IUnknown; _para2:DWORD; out _para3:IUnknown):HRESULT;stdcall; external 'ole32.dll' name 'CoGetStdMarshalEx';
+ function CoIsHandlerConnected(_para1:IUnknown):BOOL;stdcall; external 'ole32.dll' name 'CoIsHandlerConnected';
+ function CoHasStrongExternalConnections(_para1:IUnknown):BOOL;stdcall; external 'ole32.dll' name 'CoHasStrongExternalConnections';
+ function CoMarshalInterThreadInterfaceInStream(const _para1:TIID; _para2:IUnknown; out _para3:IStream):HRESULT;stdcall; external 'ole32.dll' name 'CoMarshalInterThreadInterfaceInStream';
+ function CoGetInterfaceAndReleaseStream(_para1:IStream;const _para2:TIID; out _para3):HRESULT;stdcall; external 'ole32.dll' name 'CoGetInterfaceAndReleaseStream';
+ function CoCreateFreeThreadedMarshaler(_para1:IUnknown; out _para2:IUnknown):HRESULT;stdcall; external 'ole32.dll' name 'CoCreateFreeThreadedMarshaler';
+ procedure CoFreeAllLibraries;stdcall; external 'ole32.dll' name 'CoFreeAllLibraries';
+ function CoCreateInstanceEx(const _para1:TCLSID; _para2:IUnknown; _para3:DWORD; _para4:PCOSERVERINFO; _para5:DWORD;
+ _para6:PMULTI_QI):HRESULT;stdcall; external 'ole32.dll' name 'CoCreateInstanceEx';
+ function IIDFromString(_para1:LPOLESTR; out _para2:TIID):HRESULT;stdcall; external 'ole32.dll' name 'IIDFromString';
+ function CoIsOle1Class(const _para1:TCLSID):BOOL;stdcall; external 'ole32.dll' name 'CoIsOle1Class';
+ function CoFileTimeToDosDateTime(_para1:PFILETIME; _para2:LPWORD; _para3:LPWORD):BOOL;stdcall; external 'ole32.dll' name 'CoFileTimeToDosDateTime';
+ function CoDosDateTimeToFileTime(_para1:WORD; _para2:WORD; _para3:PFILETIME):BOOL;stdcall; external 'ole32.dll' name 'CoDosDateTimeToFileTime';
+ function CoFileTimeNow(_para1:PFILETIME):HRESULT;stdcall; external 'ole32.dll' name 'CoFileTimeNow';
+ function CoRegisterMessageFilter(_para1:IMessageFilter;out _para2:IMessageFilter):HRESULT;stdcall; external 'ole32.dll' name 'CoRegisterMessageFilter';
+ function CoGetTreatAsClass(const _para1:TCLSID; _para2:LPCLSID):HRESULT;stdcall; external 'ole32.dll' name 'CoGetTreatAsClass';
+ function CoTreatAsClass(const _para1:TCLSID; const _para2:TCLSID):HRESULT;stdcall; external 'ole32.dll' name 'CoTreatAsClass';
+{$endif wince}
+
+ type
+ LPFNGETCLASSOBJECT = function (const _para1:TCLSID; const _para2:TIID;out _para3):HRESULT;stdcall;
+ LPFNCANUNLOADNOW = function:HRESULT;stdcall;
+
+{$ifndef wince}
+ function DllGetClassObject(const _para1:TCLSID; const _para2:TIID; out _para3):HRESULT;stdcall; external 'ole32.dll' name 'DllGetClassObject';
+ function DllCanUnloadNow:HRESULT;stdcall; external 'ole32.dll' name 'DllCanUnloadNow';
+{$endif wince}
+ function CoTaskMemAlloc(_para1:ULONG):PVOID;stdcall; external 'ole32.dll' name 'CoTaskMemAlloc';
+ function CoTaskMemRealloc(_para1:PVOID; _para2:ULONG):PVOID;stdcall; external 'ole32.dll' name 'CoTaskMemRealloc';
+ procedure CoTaskMemFree(_para1:PVOID);stdcall; external 'ole32.dll' name 'CoTaskMemFree';
+
+{$ifndef wince}
+ function CreateDataAdviseHolder(_para1:IDataAdviseHolder):HRESULT;stdcall; external 'ole32.dll' name 'CreateDataAdviseHolder';
+ function CreateDataCache(_para1:IUnknown; const _para2:TCLSID; const _para3:TIID; out _para4):HRESULT;stdcall; external 'ole32.dll' name 'CreateDataCache';
+{$endif wince}
+
+(* Const before type ignored *)
+ function StgCreateDocfile(_para1:POLESTR; _para2:DWORD; _para3:DWORD; out _para4:IStorage):HRESULT;stdcall; external 'ole32.dll' name 'StgCreateDocfile';
+ function StgCreateDocfileOnILockBytes(_para1:ILockBytes; _para2:DWORD; _para3:DWORD; out _para4:IStorage):HRESULT;stdcall; external 'ole32.dll' name 'StgCreateDocfileOnILockBytes';
+
+(* Const before type ignored *)
+ function StgOpenStorage(_para1:POLESTR; _para2:IStorage; _para3:DWORD; _para4:SNB; _para5:DWORD;
+ out _para6:IStorage):HRESULT;stdcall; external 'ole32.dll' name 'StgOpenStorage';
+ function StgOpenStorageOnILockBytes(_para1:ILockBytes; _para2:IStorage; _para3:DWORD; _para4:SNB; _para5:DWORD;
+ out _para6:IStorage):HRESULT;stdcall; external 'ole32.dll' name 'StgOpenStorageOnILockBytes';
+{$ifndef wince}
+ function StgIsStorageFile(_para1:POLESTR):HRESULT;stdcall; external 'ole32.dll' name 'StgIsStorageFile';
+ function StgIsStorageILockBytes(_para1:ILockBytes):HRESULT;stdcall; external 'ole32.dll' name 'StgIsStorageILockBytes';
+ function StgSetTimes(_para1:POLESTR; _para2:PFILETIME; _para3:PFILETIME; _para4:PFILETIME):HRESULT;stdcall; external 'ole32.dll' name 'StgSetTimes';
+ function CoGetObject(pszname:lpwstr; bndop:PBind_Opts; const riid:TIID; out ppv):HRESULT; stdcall; external 'ole32.dll' name 'CoGetObject';
+ function BindMoniker(_para1:IMoniker; _para2:DWORD; const _para3:TIID; out _para4):HRESULT;stdcall; external 'ole32.dll' name 'BindMoniker';
+ function MkParseDisplayName(_para1:IBindCtx; _para2:POLESTR; out _para3:PULONG; out _para4:IMoniker):HRESULT;stdcall; external 'ole32.dll' name 'MkParseDisplayName';
+ function MonikerRelativePathTo(_para1:IMoniker; _para2:IMoniker; out _para3:IMoniker; _para4:BOOL):HRESULT;stdcall; external 'ole32.dll' name 'MonikerRelativePathTo';
+ function MonikerCommonPrefixWith(_para1:IMoniker; _para2:IMoniker; _para3:PIMoniker):HRESULT;stdcall; external 'ole32.dll' name 'MonikerCommonPrefixWith';
+{$endif wince}
+ function CreateBindCtx(_para1:DWORD;out _para2:IBindCtx):HRESULT;stdcall; external 'ole32.dll' name 'CreateBindCtx';
+ function GetClassFile(_para1:POLESTR; out _para2:TCLSID):HRESULT;stdcall; external 'ole32.dll' name 'GetClassFile';
+{$ifndef wince}
+ function CreateGenericComposite(_para1:IMoniker; _para2:IMoniker; out _para3:IMoniker):HRESULT;stdcall; external 'ole32.dll' name 'CreateGenericComposite';
+ function CreateFileMoniker(_para1:POLESTR; out _para2:IMoniker):HRESULT;stdcall; external 'ole32.dll' name 'CreateFileMoniker';
+ function CreateItemMoniker(_para1:POLESTR; _para2:POLESTR;out _para3:IMoniker):HRESULT;stdcall; external 'ole32.dll' name 'CreateItemMoniker';
+ function CreateAntiMoniker(_para1:PIMoniker):HRESULT;stdcall; external 'ole32.dll' name 'CreateAntiMoniker';
+ function CreatePointerMoniker(_para1:IUnknown; out _para2:IMoniker):HRESULT;stdcall; external 'ole32.dll' name 'CreatePointerMoniker';
+ function GetRunningObjectTable(_para1:DWORD; _para2:IRunningObjectTable):HRESULT;stdcall; external 'ole32.dll' name 'GetRunningObjectTable';
+ function CoInitializeSecurity(_para1:PSECURITY_DESCRIPTOR; _para2:LONG; _para3:PSOLE_AUTHENTICATION_SERVICE; _para4:pointer; _para5:DWORD;
+ _para6:DWORD; _para7:pointer; _para8:DWORD; _para9:pointer):HRESULT;stdcall; external 'ole32.dll' name 'CoInitializeSecurity';
+ function CoGetCallContext(const _para1:TIID; _para2:Ppointer):HRESULT;stdcall; external 'ole32.dll' name 'CoGetCallContext';
+ function CoQueryProxyBlanket(_para1:IUnknown; _para2:PDWORD; _para3:PDWORD; _para4:POLESTR; _para5:PDWORD;
+ _para6:PDWORD; _para7:Pointer; _para8:PDWORD):HRESULT;stdcall; external 'ole32.dll' name 'CoQueryProxyBlanket';
+ function CoSetProxyBlanket(_para1:IUnknown; _para2:DWORD; _para3:DWORD; _para4:POLESTR; _para5:DWORD;
+ _para6:DWORD; _para7:pointer; _para8:DWORD):HRESULT;stdcall; external 'ole32.dll' name 'CoSetProxyBlanket';
+ function CoCopyProxy(_para1:IUnknown; var _para2:IUnknown):HRESULT;stdcall; external 'ole32.dll' name 'CoCopyProxy';
+ function CoQueryClientBlanket(_para1:PDWORD; _para2:PDWORD; _para3:POLESTR; _para4:PDWORD; _para5:PDWORD;
+ _para6:pointer; _para7:PDWORD):HRESULT;stdcall; external 'ole32.dll' name 'CoQueryClientBlanket';
+ function CoImpersonateClient:HRESULT;stdcall; external 'ole32.dll' name 'CoImpersonateClient';
+ function CoRevertToSelf:HRESULT;stdcall; external 'ole32.dll' name 'CoRevertToSelf';
+ function CoQueryAuthenticationServices(_para1:PDWORD; _para2:PSOLE_AUTHENTICATION_SERVICE):HRESULT;stdcall; external 'ole32.dll' name 'CoQueryAuthenticationServices';
+ function CoSwitchCallContext(_para1:IUnknown; var _para2:IUnknown):HRESULT;stdcall; external 'ole32.dll' name 'CoSwitchCallContext';
+ function CoGetInstanceFromFile(_para1:PCOSERVERINFO; _para2:PCLSID; _para3:IUnknown; _para4:DWORD; _para5:DWORD;
+ _para6:POLESTR; _para7:DWORD; _para8:PMULTI_QI):HRESULT;stdcall; external 'ole32.dll' name 'CoGetInstanceFromFile';
+ function CoGetInstanceFromIStorage(_para1:PCOSERVERINFO; _para2:PCLSID; _para3:IUnknown; _para4:DWORD; _para5:IStorage;
+ _para6:DWORD; _para7:PMULTI_QI):HRESULT;stdcall; external 'ole32.dll' name 'CoGetInstanceFromIStorage';
+{$endif wince}
+
+ type
+ TDispID = DISPID;
+
+ TDispIDList = array[0..65535] of TDispID;
+ PDispIDList = ^TDispIDList;
+
+ REFIID = TIID;
+ TREFIID = TIID;
+
+{$ifndef wince}
+ function SetErrorInfo(dwReserved:ULONG;errinfo:IErrorInfo):HResult;stdcall; external 'ole32.dll' name 'SetErrorInfo';
+ function GetErrorInfo(dwReserved:ULONG;out errinfo:IErrorInfo):HResult;stdcall; external 'ole32.dll' name 'GetErrorInfo';
+ function CreateErrorInfo(out errinfo:ICreateErrorInfo):HResult;stdcall; external 'ole32.dll' name 'CreateErrorInfo';
+{$endif wince}
+
+ const
+ oleaut32dll = 'oleaut32.dll';
+
+ function SysAllocString(psz: pointer): TBStr; stdcall; external oleaut32dll name 'SysAllocString';
+ function SysAllocStringLen(psz: pointer; len:dword): Integer; stdcall; external oleaut32dll name 'SysAllocStringLen';
+ procedure SysFreeString(bstr:pointer); stdcall; external oleaut32dll name 'SysFreeString';
+ function SysStringLen(bstr:pointer):UINT; stdcall; external oleaut32dll name 'SysStringLen';
+ function SysStringByteLen(bstr:pointer):UINT; stdcall; external oleaut32dll name 'SysStringByteLen';
+ function SysReAllocString(var bstr:pointer;psz: pointer): Integer; stdcall; external oleaut32dll name 'SysReAllocString';
+ function SysReAllocStringLen(var bstr:pointer;psz: pointer; len:dword): Integer; stdcall; external oleaut32dll name 'SysReAllocStringLen';
+
+ { Active object registration API }
+{$ifndef wince}
+ const
+ ACTIVEOBJECT_STRONG = 0;
+ ACTIVEOBJECT_WEAK = 1;
+
+ function RegisterActiveObject(unk: IUnknown; const clsid: TCLSID; dwFlags: DWORD; out dwRegister: culong): HResult; stdcall; external oleaut32dll name 'RegisterActiveObject';
+ function RevokeActiveObject(dwRegister: culong; pvReserved: Pointer) : HResult; stdcall; external oleaut32dll name 'RevokeActiveObject';
+ function GetActiveObject(const clsid: TCLSID; pvReserved: Pointer; out unk: IUnknown) : HResult; stdcall; external oleaut32dll name 'GetActiveObject';
+{$endif wince}
+
+function Succeeded(Res: HResult) : Boolean;inline;
+function Failed(Res: HResult) : Boolean;inline;
+function ResultCode(Res: HResult) : Longint;inline;
+function ResultFacility(Res: HResult): Longint;inline;
+function ResultSeverity(Res: HResult): Longint;inline;
+function MakeResult(Severity, Facility, Code: Longint): HResult;inline;
+
+function LoadTypeLib(szfile : lpolestr; var pptlib: ITypelib):HResult; stdcall; external oleaut32dll name 'LoadTypeLib';
+function LoadRegTypeLib(const rguid:TGUID;wVerMajor:ushort;wVerMinor:ushort;_lcid:lcid;out pptlib:ITypeLib):HResult; stdcall; external oleaut32dll name 'LoadRegTypeLib';
+function RegisterTypeLib(const ptrlib :ITypeLib;szfullpath:lpolestr;szhelpdir:lpolestr):HResult; stdcall; external oleaut32dll name 'RegisterTypeLib';
+function CreateTypeLib2(sysk:TSysKind;szfile:lpolestr;out ppctlib:ICreateTypeLib2):HResult; stdcall; external oleaut32dll name 'CreateTypeLib2';
+function DispInvoke(this:pointer;const ptinfo: ITypeInfo;dispidMember:TDISPID;wflags:ushort;pparams:pDISPParams;var pvarresult:OLEVARIANT;pexcepinfo:EXCEPINFO;puArgErr:puint):HRESULT; stdcall; external oleaut32dll name 'CreateTypeLib2';
+{$ifndef wince}
+function LoadTypeLibEx(szfile : lpolestr; regk:tregkind; var pptlib: ITypelib):HResult; stdcall; external oleaut32dll name 'LoadTypeLibEx';
+function QueryPathOfRegTypeLib(const guid:TGUID;wVerMajor:ushort;wVerMinor:ushort;_lcid:lcid;lpbstr:LPolestr):HResult; stdcall; external oleaut32dll name 'QueryPathOfRegTypeLib';
+function UnRegisterTypeLib(const libid:TGUID; wVerMajor:ushort;wVerMinor:ushort;_lcid:lcid;sysk:TSysKind):HResult; stdcall; external oleaut32dll name 'UnRegisterTypeLib';
+function CreateTypeLib(sysk:TSysKind;szfile:lpolestr;out ppctlib:ICreateTypeLib):HResult; stdcall; external oleaut32dll name 'CreateTypeLib';
+
+function DosDateTimeToVariantTime( wDosDate: ushort; wDosTime:ushort;pvtime:pdouble):longint; stdcall; external oleaut32dll name 'DosDateTimeToVariantTime';
+function VariantTimeToDosDateTime( vtime:DOUBLE;pwdosdate:PUSHORT;pwDosTime:PUSHORT):longint; stdcall; external oleaut32dll name 'VariantTimeToDosDateTime';
+{$endif wince}
+
+function SystemTimeToVariantTime(var lpsystemtime:TSystemTime;out pvtime: TOleDate):LONGINT; stdcall; external oleaut32dll name 'SystemTimeToVariantTime';
+function VariantTimeToSystemTime(vtime:TOleDate; out lpsystemtime: TSystemTime):LONGINT; stdcall; external oleaut32dll name 'VariantTimeToSystemTime';
+
+
+{--------------------------------------------------------------------- }
+{ VARTYPE Coercion API }
+{--------------------------------------------------------------------- }
+{ Note: The routines that convert *from* a string are defined
+ * to take a OLECHAR* rather than a BSTR because no allocation is
+ * required, and this makes the routines a bit more generic.
+ * They may of course still be passed a BSTR as the strIn param.
+ }
+
+function VarUI1FromI2(sIn:SHORT; var pbOut:BYTE):HResult;stdcall;external oleaut32dll name 'VarUI1FromI2';
+function VarUI1FromI4(lIn:LONG; var pbOut:BYTE):HResult;stdcall;external oleaut32dll name 'VarUI1FromI4';
+function VarUI1FromI8(i64In:LONG64; pbOut:PBYTE):HResult;stdcall;external oleaut32dll name 'VarUI1FromI8';
+function VarUI1FromR4(fltIn:Single; var pbOut:BYTE):HResult;stdcall;external oleaut32dll name 'VarUI1FromR4';
+function VarUI1FromR8(dblIn:DOUBLE; var pbOut:BYTE):HResult;stdcall;external oleaut32dll name 'VarUI1FromR8';
+function VarUI1FromCy(cyIn:CY; var pbOut:BYTE):HResult;stdcall;external oleaut32dll name 'VarUI1FromCy';
+function VarUI1FromDate(dateIn:DATE; var pbOut:BYTE):HResult;stdcall;external oleaut32dll name 'VarUI1FromDate';
+function VarUI1FromStr(strIn:pOLECHAR; lcid:LCID; dwFlags:ULONG; var pbOut:BYTE):HResult;stdcall;external oleaut32dll name 'VarUI1FromStr';
+function VarUI1FromDisp(pdispIn:IDispatch; lcid:LCID; var pbOut:BYTE):HResult;stdcall;external oleaut32dll name 'VarUI1FromDisp';
+function VarUI1FromBool(boolIn:VARIANT_BOOL; var pbOut:BYTE):HResult;stdcall;external oleaut32dll name 'VarUI1FromBool';
+function VarUI1FromI1(cIn:CHAR; var pbOut:BYTE):HResult;stdcall;external oleaut32dll name 'VarUI1FromI1';
+function VarUI1FromUI2(uiIn:USHORT; var pbOut:BYTE):HResult;stdcall;external oleaut32dll name 'VarUI1FromUI2';
+function VarUI1FromUI4(ulIn:ULONG; var pbOut:BYTE):HResult;stdcall;external oleaut32dll name 'VarUI1FromUI4';
+function VarUI1FromUI8(ui64In:ULONG64; pbOut:PBYTE):HResult;stdcall;external oleaut32dll name 'VarUI1FromUI8';
+function VarUI1FromDec(var pdecIn:TDecimal; var pbOut:BYTE):HResult;stdcall;external oleaut32dll name 'VarUI1FromDec';
+function VarI2FromUI1(bIn:BYTE; var psOut:SHORT):HResult;stdcall;external oleaut32dll name 'VarI2FromUI1';
+function VarI2FromI4(lIn:LONG; var psOut:SHORT):HResult;stdcall;external oleaut32dll name 'VarI2FromI4';
+
+function VarI2FromI8(i64In:LONG64; psOut:PSHORT):HResult;stdcall;external oleaut32dll name 'VarI2FromI8';
+function VarI2FromR4(fltIn:Single; var psOut:SHORT):HResult;stdcall;external oleaut32dll name 'VarI2FromR4';
+function VarI2FromR8(dblIn:DOUBLE; var psOut:SHORT):HResult;stdcall;external oleaut32dll name 'VarI2FromR8';
+function VarI2FromCy(cyIn:CY; var psOut:SHORT):HResult;stdcall;external oleaut32dll name 'VarI2FromCy';
+function VarI2FromDate(dateIn:DATE; var psOut:SHORT):HResult;stdcall;external oleaut32dll name 'VarI2FromDate';
+function VarI2FromStr(strIn:pOLECHAR; lcid:LCID; dwFlags:ULONG; var psOut:SHORT):HResult;stdcall;external oleaut32dll name 'VarI2FromStr';
+function VarI2FromDisp(pdispIn:IDispatch; lcid:LCID; var psOut:SHORT):HResult;stdcall;external oleaut32dll name 'VarI2FromDisp';
+function VarI2FromBool(boolIn:VARIANT_BOOL; var psOut:SHORT):HResult;stdcall;external oleaut32dll name 'VarI2FromBool';
+function VarI2FromI1(cIn:CHAR; var psOut:SHORT):HResult;stdcall;external oleaut32dll name 'VarI2FromI1';
+function VarI2FromUI2(uiIn:USHORT; var psOut:SHORT):HResult;stdcall;external oleaut32dll name 'VarI2FromUI2';
+function VarI2FromUI4(ulIn:ULONG; var psOut:SHORT):HResult;stdcall;external oleaut32dll name 'VarI2FromUI4';
+
+function VarI2FromUI8(ui64In:ULONG64; psOut:PSHORT):HResult;stdcall;external oleaut32dll name 'VarI2FromUI8';
+function VarI2FromDec(var pdecIn:TDecimal; var psOut:SHORT):HResult;stdcall;external oleaut32dll name 'VarI2FromDec';
+function VarI4FromUI1(bIn:BYTE; var plOut:LONG):HResult;stdcall;external oleaut32dll name 'VarI4FromUI1';
+function VarI4FromI2(sIn:SHORT; var plOut:LONG):HResult;stdcall;external oleaut32dll name 'VarI4FromI2';
+
+function VarI4FromI8(i64In:LONG64; plOut:PLONG):HResult;stdcall;external oleaut32dll name 'VarI4FromI8';
+function VarI4FromR4(fltIn:Single; var plOut:LONG):HResult;stdcall;external oleaut32dll name 'VarI4FromR4';
+function VarI4FromR8(dblIn:DOUBLE; var plOut:LONG):HResult;stdcall;external oleaut32dll name 'VarI4FromR8';
+function VarI4FromCy(cyIn:CY; var plOut:LONG):HResult;stdcall;external oleaut32dll name 'VarI4FromCy';
+function VarI4FromDate(dateIn:DATE; var plOut:LONG):HResult;stdcall;external oleaut32dll name 'VarI4FromDate';
+function VarI4FromStr(strIn:pOLECHAR; lcid:LCID; dwFlags:ULONG; var plOut:LONG):HResult;stdcall;external oleaut32dll name 'VarI4FromStr';
+function VarI4FromDisp(dispIn:IDispatch; lcid:LCID; var plOut:LONG):HResult;stdcall;external oleaut32dll name 'VarI4FromDisp';
+function VarI4FromBool(boolIn:VARIANT_BOOL; var plOut:LONG):HResult;stdcall;external oleaut32dll name 'VarI4FromBool';
+function VarI4FromI1(cIn:CHAR; var plOut:LONG):HResult;stdcall;external oleaut32dll name 'VarI4FromI1';
+function VarI4FromUI2(uiIn:USHORT; var plOut:LONG):HResult;stdcall;external oleaut32dll name 'VarI4FromUI2';
+function VarI4FromUI4(ulIn:ULONG; var plOut:LONG):HResult;stdcall;external oleaut32dll name 'VarI4FromUI4';
+
+function VarI4FromUI8(ui64In:ULONG64; plOut:PLONG):HResult;stdcall;external oleaut32dll name 'VarI4FromUI8';
+function VarI4FromDec(var pdecIn:TDecimal; var plOut:LONG):HResult;stdcall;external oleaut32dll name 'VarI4FromDec';
+function VarI4FromInt(intIn:cint; var plOut:LONG):HResult;stdcall;external oleaut32dll name 'VarI4FromInt';
+
+function VarI8FromUI1(bIn:BYTE; pi64Out:PLONG64):HResult;stdcall;external oleaut32dll name 'VarI8FromUI1';
+function VarI8FromI2(sIn:SHORT; pi64Out:PLONG64):HResult;stdcall;external oleaut32dll name 'VarI8FromI2';
+function VarI8FromI4(lIn:LONG; pi64Out:PLONG64):HResult;stdcall;external oleaut32dll name 'VarI8FromI4';
+function VarI8FromR4(fltIn:Single; pi64Out:PLONG64):HResult;stdcall;external oleaut32dll name 'VarI8FromR4';
+function VarI8FromR8(dblIn:DOUBLE; pi64Out:PLONG64):HResult;stdcall;external oleaut32dll name 'VarI8FromR8';
+function VarI8FromCy(cyIn:CY; pi64Out:PLONG64):HResult;stdcall;external oleaut32dll name 'VarI8FromCy';
+function VarI8FromDate(dateIn:DATE; pi64Out:PLONG64):HResult;stdcall;external oleaut32dll name 'VarI8FromDate';
+function VarI8FromStr(strIn:POLECHAR; lcid:LCID; dwFlags:dword; pi64Out:PLONG64):HResult;stdcall;external oleaut32dll name 'VarI8FromStr';
+function VarI8FromDisp(pdispIn:IDispatch; lcid:LCID; pi64Out:PLONG64):HResult;stdcall;external oleaut32dll name 'VarI8FromDisp';
+function VarI8FromBool(boolIn:VARIANT_BOOL; pi64Out:PLONG64):HResult;stdcall;external oleaut32dll name 'VarI8FromBool';
+function VarI8FromI1(cIn:CHAR; pi64Out:PLONG64):HResult;stdcall;external oleaut32dll name 'VarI8FromI1';
+
+function VarI8FromUI2(uiIn:USHORT; pi64Out:PLONG64):HResult;stdcall;external oleaut32dll name 'VarI8FromUI2';
+function VarI8FromUI4(ulIn:ULONG; pi64Out:PLONG64):HResult;stdcall;external oleaut32dll name 'VarI8FromUI4';
+function VarI8FromUI8(ui64In:ULONG64; pi64Out:PLONG64):HResult;stdcall;external oleaut32dll name 'VarI8FromUI8';
+function VarI8FromDec(var pdecIn:TDecimal; pi64Out:PLONG64):HResult;stdcall;external oleaut32dll name 'VarI8FromDec';
+function VarI8FromInt(intIn:cint; pi64Out:PLONG64):HResult;stdcall;external oleaut32dll name 'VarI8FromInt';
+
+{******************* }
+function VarR4FromUI1(bIn:BYTE; var pfltOut:Single):HResult;stdcall;external oleaut32dll name 'VarR4FromUI1';
+function VarR4FromI2(sIn:SHORT; var pfltOut:Single):HResult;stdcall;external oleaut32dll name 'VarR4FromI2';
+function VarR4FromI4(lIn:LONG; var pfltOut:Single):HResult;stdcall;external oleaut32dll name 'VarR4FromI4';
+
+function VarR4FromI8(i64In:LONG64; pfltOut:PSingle):HResult;stdcall;external oleaut32dll name 'VarR4FromI8';
+function VarR4FromR8(dblIn:DOUBLE; var pfltOut:Single):HResult;stdcall;external oleaut32dll name 'VarR4FromR8';
+function VarR4FromCy(cyIn:CY; var pfltOut:Single):HResult;stdcall;external oleaut32dll name 'VarR4FromCy';
+function VarR4FromDate(dateIn:DATE; var pfltOut:Single):HResult;stdcall;external oleaut32dll name 'VarR4FromDate';
+function VarR4FromStr(strIn:pOLECHAR; lcid:LCID; dwFlags:ULONG; var pfltOut:Single):HResult;stdcall;external oleaut32dll name 'VarR4FromStr';
+function VarR4FromDisp(pdispIn:IDispatch; lcid:LCID; var pfltOut:Single):HResult;stdcall;external oleaut32dll name 'VarR4FromDisp';
+function VarR4FromBool(boolIn:VARIANT_BOOL; var pfltOut:Single):HResult;stdcall;external oleaut32dll name 'VarR4FromBool';
+function VarR4FromI1(cIn:CHAR; var pfltOut:Single):HResult;stdcall;external oleaut32dll name 'VarR4FromI1';
+function VarR4FromUI2(uiIn:USHORT; var pfltOut:Single):HResult;stdcall;external oleaut32dll name 'VarR4FromUI2';
+function VarR4FromUI4(ulIn:ULONG; var pfltOut:Single):HResult;stdcall;external oleaut32dll name 'VarR4FromUI4';
+
+function VarR4FromUI8(ui64In:ULONG64; pfltOut:PSingle):HResult;stdcall;external oleaut32dll name 'VarR4FromUI8';
+function VarR4FromDec(var pdecIn:TDecimal; var pfltOut:Single):HResult;stdcall;external oleaut32dll name 'VarR4FromDec';
+function VarR8FromUI1(bIn:BYTE; var pdblOut:DOUBLE):HResult;stdcall;external oleaut32dll name 'VarR8FromUI1';
+function VarR8FromI2(sIn:SHORT; var pdblOut:DOUBLE):HResult;stdcall;external oleaut32dll name 'VarR8FromI2';
+function VarR8FromI4(lIn:LONG; var pdblOut:DOUBLE):HResult;stdcall;external oleaut32dll name 'VarR8FromI4';
+
+function VarR8FromI8(i64In:LONG64; pdblOut:PDOUBLE):HResult;stdcall;external oleaut32dll name 'VarR8FromI8';
+function VarR8FromR4(fltIn:Single; var pdblOut:DOUBLE):HResult;stdcall;external oleaut32dll name 'VarR8FromR4';
+function VarR8FromCy(cyIn:CY; var pdblOut:DOUBLE):HResult;stdcall;external oleaut32dll name 'VarR8FromCy';
+function VarR8FromDate(dateIn:DATE; var pdblOut:DOUBLE):HResult;stdcall;external oleaut32dll name 'VarR8FromDate';
+function VarR8FromStr(strIn:pOLECHAR; lcid:LCID; dwFlags:ULONG; var pdblOut:DOUBLE):HResult;stdcall;external oleaut32dll name 'VarR8FromStr';
+function VarR8FromDisp(pdispIn:IDispatch; lcid:LCID; var pdblOut:DOUBLE):HResult;stdcall;external oleaut32dll name 'VarR8FromDisp';
+function VarR8FromBool(boolIn:VARIANT_BOOL; var pdblOut:DOUBLE):HResult;stdcall;external oleaut32dll name 'VarR8FromBool';
+function VarR8FromI1(cIn:CHAR; var pdblOut:DOUBLE):HResult;stdcall;external oleaut32dll name 'VarR8FromI1';
+function VarR8FromUI2(uiIn:USHORT; var pdblOut:DOUBLE):HResult;stdcall;external oleaut32dll name 'VarR8FromUI2';
+function VarR8FromUI4(ulIn:ULONG; var pdblOut:DOUBLE):HResult;stdcall;external oleaut32dll name 'VarR8FromUI4';
+
+function VarR8FromUI8(ui64In:ULONG64; pdblOut:PDOUBLE):HResult;stdcall;external oleaut32dll name 'VarR8FromUI8';
+function VarR8FromDec(var pdecIn:TDecimal; var pdblOut:DOUBLE):HResult;stdcall;external oleaut32dll name 'VarR8FromDec';
+function VarDateFromUI1(bIn:BYTE; var pdateOut:DATE):HResult;stdcall;external oleaut32dll name 'VarDateFromUI1';
+function VarDateFromI2(sIn:SHORT; var pdateOut:DATE):HResult;stdcall;external oleaut32dll name 'VarDateFromI2';
+function VarDateFromI4(lIn:LONG; var pdateOut:DATE):HResult;stdcall;external oleaut32dll name 'VarDateFromI4';
+
+function VarDateFromI8(i64In:LONG64; pdateOut:PDATE):HResult;stdcall;external oleaut32dll name 'VarDateFromI8';
+function VarDateFromR4(fltIn:Single; var pdateOut:DATE):HResult;stdcall;external oleaut32dll name 'VarDateFromR4';
+function VarDateFromR8(dblIn:DOUBLE; var pdateOut:DATE):HResult;stdcall;external oleaut32dll name 'VarDateFromR8';
+function VarDateFromCy(cyIn:CY; var pdateOut:DATE):HResult;stdcall;external oleaut32dll name 'VarDateFromCy';
+function VarDateFromStr(strIn:pOLECHAR; lcid:LCID; dwFlags:ULONG; var pdateOut:DATE):HResult;stdcall;external oleaut32dll name 'VarDateFromStr';
+
+function VarDateFromDisp(pdispIn:IDispatch; lcid:LCID; var pdateOut:DATE):HResult;stdcall;external oleaut32dll name 'VarDateFromDisp';
+function VarDateFromBool(boolIn:VARIANT_BOOL; var pdateOut:DATE):HResult;stdcall;external oleaut32dll name 'VarDateFromBool';
+function VarDateFromI1(cIn:CHAR; var pdateOut:DATE):HResult;stdcall;external oleaut32dll name 'VarDateFromI1';
+function VarDateFromUI2(uiIn:USHORT; var pdateOut:DATE):HResult;stdcall;external oleaut32dll name 'VarDateFromUI2';
+function VarDateFromUI4(ulIn:ULONG; var pdateOut:DATE):HResult;stdcall;external oleaut32dll name 'VarDateFromUI4';
+
+function VarDateFromUI8(ui64In:ULONG64; pdateOut:PDATE):HResult;stdcall;external oleaut32dll name 'VarDateFromUI8';
+function VarDateFromDec(var pdecIn:TDecimal; var pdateOut:DATE):HResult;stdcall;external oleaut32dll name 'VarDateFromDec';
+function VarCyFromUI1(bIn:BYTE; var pcyOut:CY):HResult;stdcall;external oleaut32dll name 'VarCyFromUI1';
+function VarCyFromI2(sIn:SHORT; var pcyOut:CY):HResult;stdcall;external oleaut32dll name 'VarCyFromI2';
+function VarCyFromI4(lIn:LONG; var pcyOut:CY):HResult;stdcall;external oleaut32dll name 'VarCyFromI4';
+
+function VarCyFromI8(i64In:LONG64; pcyOut:PCurrency):HResult;stdcall;external oleaut32dll name 'VarCyFromI8';
+function VarCyFromR4(fltIn:Single; var pcyOut:CY):HResult;stdcall;external oleaut32dll name 'VarCyFromR4';
+function VarCyFromR8(dblIn:DOUBLE; var pcyOut:CY):HResult;stdcall;external oleaut32dll name 'VarCyFromR8';
+function VarCyFromDate(dateIn:DATE; var pcyOut:CY):HResult;stdcall;external oleaut32dll name 'VarCyFromDate';
+function VarCyFromStr(strIn:pOLECHAR; lcid:LCID; dwFlags:ULONG; var pcyOut:CY):HResult;stdcall;external oleaut32dll name 'VarCyFromStr';
+function VarCyFromDisp(pdispIn:IDispatch; lcid:LCID; var pcyOut:CY):HResult;stdcall;external oleaut32dll name 'VarCyFromDisp';
+function VarCyFromBool(boolIn:VARIANT_BOOL; var pcyOut:CY):HResult;stdcall;external oleaut32dll name 'VarCyFromBool';
+function VarCyFromI1(cIn:CHAR; var pcyOut:CY):HResult;stdcall;external oleaut32dll name 'VarCyFromI1';
+function VarCyFromUI2(uiIn:USHORT; var pcyOut:CY):HResult;stdcall;external oleaut32dll name 'VarCyFromUI2';
+function VarCyFromUI4(ulIn:ULONG; var pcyOut:CY):HResult;stdcall;external oleaut32dll name 'VarCyFromUI4';
+
+function VarCyFromUI8(ui64In:ULONG64; pcyOut:PCurrency):HResult;stdcall;external oleaut32dll name 'VarCyFromUI8';
+function VarCyFromDec(var pdecIn:TDecimal; var pcyOut:CY):HResult;stdcall;external oleaut32dll name 'VarCyFromDec';
+function VarBstrFromUI1(bVal:BYTE; lcid:LCID; dwFlags:ULONG; var pbstrOut:BSTR):HResult;stdcall;external oleaut32dll name 'VarBstrFromUI1';
+function VarBstrFromI2(iVal:SHORT; lcid:LCID; dwFlags:ULONG; var pbstrOut:BSTR):HResult;stdcall;external oleaut32dll name 'VarBstrFromI2';
+function VarBstrFromI4(lIn:LONG; lcid:LCID; dwFlags:ULONG; var pbstrOut:BSTR):HResult;stdcall;external oleaut32dll name 'VarBstrFromI4';
+
+function VarBstrFromI8(i64In:LONG64; lcid:LCID; dwFlags:dword; pbstrOut:PBSTR):HResult;stdcall;external oleaut32dll name 'VarBstrFromI8';
+function VarBstrFromR4(fltIn:Single; lcid:LCID; dwFlags:ULONG; var pbstrOut:BSTR):HResult;stdcall;external oleaut32dll name 'VarBstrFromR4';
+function VarBstrFromR8(dblIn:DOUBLE; lcid:LCID; dwFlags:ULONG; var pbstrOut:BSTR):HResult;stdcall;external oleaut32dll name 'VarBstrFromR8';
+function VarBstrFromCy(cyIn:CY; lcid:LCID; dwFlags:ULONG; var pbstrOut:BSTR):HResult;stdcall;external oleaut32dll name 'VarBstrFromCy';
+function VarBstrFromDate(dateIn:DATE; lcid:LCID; dwFlags:ULONG; var pbstrOut:BSTR):HResult;stdcall;external oleaut32dll name 'VarBstrFromDate';
+function VarBstrFromDisp(pdispIn:IDispatch; lcid:LCID; dwFlags:ULONG; var pbstrOut:BSTR):HResult;stdcall;external oleaut32dll name 'VarBstrFromDisp';
+function VarBstrFromBool(boolIn:VARIANT_BOOL; lcid:LCID; dwFlags:ULONG; var pbstrOut:BSTR):HResult;stdcall;external oleaut32dll name 'VarBstrFromBool';
+function VarBstrFromI1(cIn:CHAR; lcid:LCID; dwFlags:ULONG; var pbstrOut:BSTR):HResult;stdcall;external oleaut32dll name 'VarBstrFromI1';
+function VarBstrFromUI2(uiIn:USHORT; lcid:LCID; dwFlags:ULONG; var pbstrOut:BSTR):HResult;stdcall;external oleaut32dll name 'VarBstrFromUI2';
+function VarBstrFromUI4(ulIn:ULONG; lcid:LCID; dwFlags:ULONG; var pbstrOut:BSTR):HResult;stdcall;external oleaut32dll name 'VarBstrFromUI4';
+
+function VarBstrFromUI8(ui64In:ULONG64; lcid:LCID; dwFlags:dword; pbstrOut:PBSTR):HResult;stdcall;external oleaut32dll name 'VarBstrFromUI8';
+function VarBstrFromDec(var pdecIn:TDecimal; lcid:LCID; dwFlags:ULONG; var pbstrOut:BSTR):HResult;stdcall;external oleaut32dll name 'VarBstrFromDec';
+function VarBoolFromUI1(bIn:BYTE; var pboolOut:VARIANT_BOOL):HResult;stdcall;external oleaut32dll name 'VarBoolFromUI1';
+function VarBoolFromI2(sIn:SHORT; var pboolOut:VARIANT_BOOL):HResult;stdcall;external oleaut32dll name 'VarBoolFromI2';
+function VarBoolFromI4(lIn:LONG; var pboolOut:VARIANT_BOOL):HResult;stdcall;external oleaut32dll name 'VarBoolFromI4';
+
+function VarBoolFromI8(i64In:LONG64; pboolOut:PVARIANT_BOOL):HResult;stdcall;external oleaut32dll name 'VarBoolFromI8';
+function VarBoolFromR4(fltIn:Single; var pboolOut:VARIANT_BOOL):HResult;stdcall;external oleaut32dll name 'VarBoolFromR4';
+function VarBoolFromR8(dblIn:DOUBLE; var pboolOut:VARIANT_BOOL):HResult;stdcall;external oleaut32dll name 'VarBoolFromR8';
+function VarBoolFromDate(dateIn:DATE; var pboolOut:VARIANT_BOOL):HResult;stdcall;external oleaut32dll name 'VarBoolFromDate';
+function VarBoolFromCy(cyIn:CY; var pboolOut:VARIANT_BOOL):HResult;stdcall;external oleaut32dll name 'VarBoolFromCy';
+function VarBoolFromStr(strIn:pOLECHAR; lcid:LCID; dwFlags:ULONG; var pboolOut:VARIANT_BOOL):HResult;stdcall;external oleaut32dll name 'VarBoolFromStr';
+function VarBoolFromDisp(pdispIn:IDispatch; lcid:LCID; var pboolOut:VARIANT_BOOL):HResult;stdcall;external oleaut32dll name 'VarBoolFromDisp';
+function VarBoolFromI1(cIn:CHAR; var pboolOut:VARIANT_BOOL):HResult;stdcall;external oleaut32dll name 'VarBoolFromI1';
+function VarBoolFromUI2(uiIn:USHORT; var pboolOut:VARIANT_BOOL):HResult;stdcall;external oleaut32dll name 'VarBoolFromUI2';
+function VarBoolFromUI4(ulIn:ULONG; var pboolOut:VARIANT_BOOL):HResult;stdcall;external oleaut32dll name 'VarBoolFromUI4';
+
+function VarBoolFromUI8(i64In:ULONG64; pboolOut:PVARIANT_BOOL):HResult;stdcall;external oleaut32dll name 'VarBoolFromUI8';
+function VarBoolFromDec(var pdecIn:TDecimal; var pboolOut:VARIANT_BOOL):HResult;stdcall;external oleaut32dll name 'VarBoolFromDec';
+function VarI1FromUI1(bIn:BYTE; pcOut:pCHAR):HResult;stdcall;external oleaut32dll name 'VarI1FromUI1';
+function VarI1FromI2(uiIn:SHORT; pcOut:pCHAR):HResult;stdcall;external oleaut32dll name 'VarI1FromI2';
+function VarI1FromI4(lIn:LONG; pcOut:pCHAR):HResult;stdcall;external oleaut32dll name 'VarI1FromI4';
+function VarI1FromI8(i64In:LONG64; pcOut:pCHAR):HResult;stdcall;external oleaut32dll name 'VarI1FromI8';
+function VarI1FromR4(fltIn:Single; pcOut:pCHAR):HResult;stdcall;external oleaut32dll name 'VarI1FromR4';
+function VarI1FromR8(dblIn:DOUBLE; pcOut:pCHAR):HResult;stdcall;external oleaut32dll name 'VarI1FromR8';
+function VarI1FromDate(dateIn:DATE; pcOut:pCHAR):HResult;stdcall;external oleaut32dll name 'VarI1FromDate';
+function VarI1FromCy(cyIn:CY; pcOut:pCHAR):HResult;stdcall;external oleaut32dll name 'VarI1FromCy';
+function VarI1FromStr(strIn:pOLECHAR; lcid:LCID; dwFlags:ULONG; pcOut:pCHAR):HResult;stdcall;external oleaut32dll name 'VarI1FromStr';
+function VarI1FromDisp(pdispIn:IDispatch; lcid:LCID; pcOut:pCHAR):HResult;stdcall;external oleaut32dll name 'VarI1FromDisp';
+function VarI1FromBool(boolIn:VARIANT_BOOL; pcOut:pCHAR):HResult;stdcall;external oleaut32dll name 'VarI1FromBool';
+function VarI1FromUI2(uiIn:USHORT; pcOut:pCHAR):HResult;stdcall;external oleaut32dll name 'VarI1FromUI2';
+function VarI1FromUI4(ulIn:ULONG; pcOut:pCHAR):HResult;stdcall;external oleaut32dll name 'VarI1FromUI4';
+function VarI1FromUI8(i64In:ULONG64; pcOut:pCHAR):HResult;stdcall;external oleaut32dll name 'VarI1FromUI8';
+function VarI1FromDec(var pdecIn:TDecimal; pcOut:pCHAR):HResult;stdcall;external oleaut32dll name 'VarI1FromDec';
+function VarUI2FromUI1(bIn:BYTE; var puiOut:USHORT):HResult;stdcall;external oleaut32dll name 'VarUI2FromUI1';
+function VarUI2FromI2(uiIn:SHORT; var puiOut:USHORT):HResult;stdcall;external oleaut32dll name 'VarUI2FromI2';
+function VarUI2FromI4(lIn:LONG; var puiOut:USHORT):HResult;stdcall;external oleaut32dll name 'VarUI2FromI4';
+
+function VarUI2FromI8(i64In:LONG64; var puiOut:USHORT):HResult;stdcall;external oleaut32dll name 'VarUI2FromI8';
+function VarUI2FromR4(fltIn:Single; var puiOut:USHORT):HResult;stdcall;external oleaut32dll name 'VarUI2FromR4';
+function VarUI2FromR8(dblIn:DOUBLE; var puiOut:USHORT):HResult;stdcall;external oleaut32dll name 'VarUI2FromR8';
+function VarUI2FromDate(dateIn:DATE; var puiOut:USHORT):HResult;stdcall;external oleaut32dll name 'VarUI2FromDate';
+function VarUI2FromCy(cyIn:CY; var puiOut:USHORT):HResult;stdcall;external oleaut32dll name 'VarUI2FromCy';
+function VarUI2FromStr(strIn:pOLECHAR; lcid:LCID; dwFlags:ULONG; var puiOut:USHORT):HResult;stdcall;external oleaut32dll name 'VarUI2FromStr';
+function VarUI2FromDisp(pdispIn:IDispatch; lcid:LCID; var puiOut:USHORT):HResult;stdcall;external oleaut32dll name 'VarUI2FromDisp';
+function VarUI2FromBool(boolIn:VARIANT_BOOL; var puiOut:USHORT):HResult;stdcall;external oleaut32dll name 'VarUI2FromBool';
+function VarUI2FromI1(cIn:CHAR; var puiOut:USHORT):HResult;stdcall;external oleaut32dll name 'VarUI2FromI1';
+function VarUI2FromUI4(ulIn:ULONG; var puiOut:USHORT):HResult;stdcall;external oleaut32dll name 'VarUI2FromUI4';
+function VarUI2FromUI8(i64In:ULONG64; var puiOut:USHORT):HResult;stdcall;external oleaut32dll name 'VarUI2FromUI8';
+function VarUI2FromDec(var pdecIn:TDecimal; var puiOut:USHORT):HResult;stdcall;external oleaut32dll name 'VarUI2FromDec';
+function VarUI4FromUI1(bIn:BYTE; var pulOut:ULONG):HResult;stdcall;external oleaut32dll name 'VarUI4FromUI1';
+function VarUI4FromI2(uiIn:SHORT; var pulOut:ULONG):HResult;stdcall;external oleaut32dll name 'VarUI4FromI2';
+function VarUI4FromI4(lIn:LONG; var pulOut:ULONG):HResult;stdcall;external oleaut32dll name 'VarUI4FromI4';
+function VarUI4FromI8(i64In:LONG64; var plOut:ULONG):HResult;stdcall;external oleaut32dll name 'VarUI4FromI8';
+function VarUI4FromR4(fltIn:Single; var pulOut:ULONG):HResult;stdcall;external oleaut32dll name 'VarUI4FromR4';
+function VarUI4FromR8(dblIn:DOUBLE; var pulOut:ULONG):HResult;stdcall;external oleaut32dll name 'VarUI4FromR8';
+function VarUI4FromDate(dateIn:DATE; var pulOut:ULONG):HResult;stdcall;external oleaut32dll name 'VarUI4FromDate';
+function VarUI4FromCy(cyIn:CY; var pulOut:ULONG):HResult;stdcall;external oleaut32dll name 'VarUI4FromCy';
+function VarUI4FromStr(strIn:pOLECHAR; lcid:LCID; dwFlags:ULONG; var pulOut:ULONG):HResult;stdcall;external oleaut32dll name 'VarUI4FromStr';
+function VarUI4FromDisp(pdispIn:IDispatch; lcid:LCID; var pulOut:ULONG):HResult;stdcall;external oleaut32dll name 'VarUI4FromDisp';
+function VarUI4FromBool(boolIn:VARIANT_BOOL; var pulOut:ULONG):HResult;stdcall;external oleaut32dll name 'VarUI4FromBool';
+function VarUI4FromI1(cIn:CHAR; var pulOut:ULONG):HResult;stdcall;external oleaut32dll name 'VarUI4FromI1';
+function VarUI4FromUI2(uiIn:USHORT; var pulOut:ULONG):HResult;stdcall;external oleaut32dll name 'VarUI4FromUI2';
+function VarUI4FromUI8(ui64In:ULONG64; var plOut:ULONG):HResult;stdcall;external oleaut32dll name 'VarUI4FromUI8';
+function VarUI4FromDec(var pdecIn:TDecimal; var pulOut:ULONG):HResult;stdcall;external oleaut32dll name 'VarUI4FromDec';
+
+{**************************************** }
+function VarUI8FromUI1(bIn:BYTE; pi64Out:PULONG64):HResult;stdcall;external oleaut32dll name 'VarUI8FromUI1';
+function VarUI8FromI2(sIn:SHORT; pi64Out:PULONG64):HResult;stdcall;external oleaut32dll name 'VarUI8FromI2';
+function VarUI8FromI4(lIn:LONG; pi64Out:PULONG64):HResult;stdcall;external oleaut32dll name 'VarUI8FromI4';
+function VarUI8FromI8(ui64In:LONG64; pi64Out:PULONG64):HResult;stdcall;external oleaut32dll name 'VarUI8FromI8';
+function VarUI8FromR4(fltIn:Single; pi64Out:PULONG64):HResult;stdcall;external oleaut32dll name 'VarUI8FromR4';
+function VarUI8FromR8(dblIn:DOUBLE; pi64Out:PULONG64):HResult;stdcall;external oleaut32dll name 'VarUI8FromR8';
+function VarUI8FromCy(cyIn:CY; pi64Out:PULONG64):HResult;stdcall;external oleaut32dll name 'VarUI8FromCy';
+function VarUI8FromDate(dateIn:DATE; pi64Out:PULONG64):HResult;stdcall;external oleaut32dll name 'VarUI8FromDate';
+function VarUI8FromStr(strIn:POLECHAR; lcid:LCID; dwFlags:dword; pi64Out:PULONG64):HResult;stdcall;external oleaut32dll name 'VarUI8FromStr';
+function VarUI8FromDisp(pdispIn:IDispatch; lcid:LCID; pi64Out:PULONG64):HResult;stdcall;external oleaut32dll name 'VarUI8FromDisp';
+function VarUI8FromBool(boolIn:VARIANT_BOOL; pi64Out:PULONG64):HResult;stdcall;external oleaut32dll name 'VarUI8FromBool';
+function VarUI8FromI1(cIn:CHAR; pi64Out:PULONG64):HResult;stdcall;external oleaut32dll name 'VarUI8FromI1';
+function VarUI8FromUI2(uiIn:USHORT; pi64Out:PULONG64):HResult;stdcall;external oleaut32dll name 'VarUI8FromUI2';
+function VarUI8FromUI4(ulIn:ULONG; pi64Out:PULONG64):HResult;stdcall;external oleaut32dll name 'VarUI8FromUI4';
+function VarUI8FromDec(var pdecIn:TDecimal; pi64Out:PULONG64):HResult;stdcall;external oleaut32dll name 'VarUI8FromDec';
+function VarUI8FromInt(intIn:cint; pi64Out:PULONG64):HResult;stdcall;external oleaut32dll name 'VarUI8FromInt';
+
+implementation
+
+function Succeeded(Res: HResult) : Boolean;inline;
+ begin
+ Result := Res and $80000000 = 0;
+ end;
+
+
+function Failed(Res: HResult) : Boolean;inline;
+ begin
+ Result := Res and $80000000 <> 0;
+ end;
+
+
+function ResultCode(Res: HResult) : Longint;inline;
+ begin
+ Result := Res and $0000FFFF;
+ end;
+
+
+function ResultFacility(Res: HResult): Longint;inline;
+ begin
+ Result := (Res shr 16) and $00001FFF;
+ end;
+
+
+function ResultSeverity(Res: HResult): Longint;inline;
+ begin
+ Result := Res shr 31;
+ end;
+
+
+function MakeResult(Severity, Facility, Code: Longint): HResult;inline;
+ begin
+ Result := (Severity shl 31) or (Facility shl 16) or Code;
+ end;
+
+
+end.
diff --git a/plugins/mRadio/i_bass.inc b/plugins/mRadio/i_bass.inc
new file mode 100644
index 0000000000..77f0d3650a
--- /dev/null
+++ b/plugins/mRadio/i_bass.inc
@@ -0,0 +1,940 @@
+{BASS dll code}
+const
+ signMP3 = $FBFF;
+ signID3 = $00334449;
+ signOGG = $5367674F;
+ OGGHdrSize = 26; // +1=NumSegments
+const
+ BASSName = 'bass.dll';
+ StationHeader:PByte=nil;
+const
+ basspath:PWideChar=nil;
+var
+ hdrlen:integer;
+ syncMETA,
+ syncOGG,
+ syncWMA,
+ syncWMA1,
+ syncEND:HSYNC;
+ SaveHeader:bool;
+
+procedure SetSndVol(arg:integer);
+begin
+ if arg<0 then
+ arg:=gVolume
+ else
+ gVolume:=arg;
+
+ if chan<>0 then
+ begin
+ if arg<0 then arg:=0;
+ BASS_ChannelSetAttribute(chan,BASS_ATTRIB_VOL,arg/100);
+ end;
+end;
+
+procedure BassError(text:PWideChar);
+begin
+ MessageboxW(0,TranslateW(text),TranslateW('Sorry!'),MB_ICONERROR)
+end;
+
+procedure ErrorCustom(text:pWideChar=nil);
+var
+ buf:array [0..255] of WideChar;
+ idx:integer;
+ pcw:pWideChar;
+begin
+ idx:=BASS_ErrorGetCode();
+ if (idx<0) or (idx>BASS_ERROR_MAXNUMBER) then
+ begin
+ if text=nil then
+ pcw:='Unknown error'
+ else
+ pcw:=text;
+ end
+ else
+ pcw:=FastAnsiToWideBuf(BASS_ERRORS[idx],@buf);
+
+ MessageBoxW(0,TranslateW(pcw),
+ TranslateW('Oops! BASS error'),MB_ICONERROR)
+end;
+
+procedure EQ_OFF;
+var
+ i:dword;
+begin
+ if chan<>0 then
+ for i:=0 to 9 do
+ BASS_ChannelRemoveFX(chan,eq[i].fx);
+ isEQ_OFF:=BST_CHECKED;
+end;
+
+procedure EQ_ON;
+var
+ i:dword;
+begin
+ if chan<>0 then
+ begin
+ for i:=0 to 9 do
+ eq[i].fx:=BASS_ChannelSetFX(chan,BASS_FX_DX8_PARAMEQ,1);
+ for i:=0 to 9 do
+ BASS_FXSetParameters(eq[i].fx,@eq[i].param);
+ isEQ_OFF:=BST_UNCHECKED;
+ end;
+end;
+
+function MyLoadBASS(root:PwideChar;custom:pWideChar):bool;
+var
+ pc:PWideChar;
+ buf:array [0..MAX_PATH-1] of WideChar;
+begin
+ result:=true;
+ mGetMem(basspath,1024);
+ pc:=StrCopyEW(basspath,custom);
+ if (pc-1)^<>'\' then
+ begin
+ pc^:='\';
+ inc(pc);
+ end;
+ StrCopyW(pc,BASSName);
+ if not Load_BASSDLL(basspath) then
+ begin
+ pc:=StrCopyW(StrCopyEW(basspath,root),BASSName);
+ if not Load_BASSDLL(basspath) then
+ begin
+ pc:=StrCopyW(StrCopyEW(pc,'plugins\'),BASSName);
+ if not Load_BASSDLL(basspath) then
+ begin
+ pc:=StrCopyW(StrCopyEW(pc,'bass\'),BASSName);
+ if not Load_BASSDLL(basspath) then
+ begin
+ if (custom<>nil) and (custom^<>#0) then
+ BassError('BASS.DLL not found!');
+ result:=false;
+ end;
+ end;
+ end;
+ end;
+
+ if (not result) and ((custom=nil) or (custom^=#0)) then
+ begin
+ if MessageboxA(0,Translate('BASS.DLL not found! Choose BASS.dll path manually'),
+ cPluginName,MB_YESNO)=IDYES then
+ begin
+ pc := nil;
+ if SelectDirectory(TranslateW('Choose BASS.dll path'),pc,0) then
+ begin
+ CallService(MS_UTILS_PATHTORELATIVEW,wparam(pc),lparam(@buf));
+ pc:=StrCopyEW(basspath,buf);
+ if (pc-1)^<>'\' then
+ begin
+ pc^:='\';
+ inc(pc);
+ end;
+ pc^:=#0;
+ DBWriteUnicode(0,PluginName,optBASSPath,basspath);
+ StrCopyW(pc,BASSName);
+
+ if not Load_BASSDLL(basspath) then
+ BassError('BASS.DLL not found!')
+ else
+ result:=true;
+ end;
+
+ end;
+ end;
+
+ if result then
+ begin
+ if (BASS_GetVersion shr 16)<BASSVERSION then
+ begin
+ Unload_BASSDLL;
+ result:=false;
+ BassError('Wrong version of BASS.DLL');
+ end
+ else
+ begin
+ pc^:=#0; // cut BASSName
+ exit;
+ end;
+ end;
+ mFreeMem(basspath);
+end;
+
+procedure MyStopBASS;
+begin
+ if ActiveContact<>0 then
+ begin
+ if syncMETA<>0 then
+ begin
+ BASS_ChannelRemoveSync(chan,syncMETA);
+ syncMETA:=0
+ end;
+ if syncEND<>0 then
+ begin
+ BASS_ChannelRemoveSync(chan,syncEND);
+ syncEND:=0
+ end;
+ if syncWMA<>0 then
+ begin
+ BASS_ChannelRemoveSync(chan,syncWMA);
+ syncWMA:=0
+ end;
+ if syncOGG<>0 then
+ begin
+ BASS_ChannelRemoveSync(chan,syncOGG);
+ syncOGG:=0
+ end;
+ if syncWMA1<>0 then
+ begin
+ BASS_ChannelRemoveSync(chan,syncWMA1);
+ syncWMA1:=0
+ end;
+ end;
+end;
+
+procedure MyFreeBASS;
+begin
+ MyStopBASS;
+ BASS_Free;
+ BASS_PluginFree(0);
+end;
+
+procedure StopStation;
+begin
+ if chan<>0 then
+ BASS_StreamFree(chan); // close old stream
+ chan:=0;
+ mFreeMem(StationHeader);
+ mFreeMem(ActiveURLw);
+ DBDeleteSetting(ActiveContact,strCList,optStatusMsg);
+ MyStopBASS;
+end;
+
+function MyInitBASS:int;
+var
+ fd:TWin32FindDataW;
+ fh:THANDLE;
+ buf:array [0..MAX_PATH-1] of WideChar;
+// buf1:array [0..31] of WideChar;
+ pc:PWideChar;
+ p:Bool;
+begin
+ if Inited then
+ begin
+ result:=1;
+ exit;
+ end;
+
+ Inited:=true;
+ BASS_Free;
+ p:=BASS_Init(-1,44100,BASS_DEVICE_3D,0,nil);
+ if not p then
+ p:=BASS_Init(-1,44100,0,0,nil);
+ if not p then
+ p:=BASS_Init(1,44100,0,0,nil);
+ if not p then
+ begin
+ ErrorCustom('Can''t initialize device');
+ result:=0;
+ end
+ else
+ begin
+ pc:=StrCopyW(StrCopyEW(buf,basspath),'bass*.dll');
+ fh:=FindFirstFileW(buf,fd);
+ if fh<>INVALID_HANDLE_VALUE then
+ begin
+ repeat
+ StrCopyW(pc,fd.cFileName);
+ if BASS_PluginLoad(pAnsiChar(@buf),BASS_UNICODE)=0 then
+ until not FindNextFileW(fh,fd);
+ FindClose(fh);
+ end;
+ // enable ASX processing (if WMA loaded)
+ BASS_SetConfig(BASS_CONFIG_NET_PLAYLIST, 2); // 2 - enable internet and local playlists
+
+ fh:=DBReadByte(0,PluginName,optEAXType,0);
+ if fh=0 then
+ BASS_SetEAXParameters(-1,0,-1,-1)
+ else
+ BASS_SetEAXPreset(EAXItems[fh].code);
+ result:=1;
+ end;
+end;
+
+function GetMusicFormat:PAnsiChar;
+var
+ bci:BASS_CHANNELINFO;
+begin
+ BASS_ChannelGetInfo(chan,bci);
+ case bci.ctype of
+ BASS_CTYPE_STREAM_OGG: result:='OGG';
+ BASS_CTYPE_STREAM_MP1,
+ BASS_CTYPE_STREAM_MP2,
+ BASS_CTYPE_STREAM_MP3: result:='MP3';
+ BASS_CTYPE_STREAM_WMA,
+ BASS_CTYPE_STREAM_WMA_MP3: result:='WMA';
+ {BASS_CTYPE_STREAM_AAC,}$10b00: result:='AAC';
+ {BASS_CTYPE_STREAM_MP4:}$10b01: result:='MP4';
+ {BASS_CTYPE_STREAM_AC3:}$11000: result:='AC3';
+ else
+ result:=nil;
+ end;
+end;
+
+function GetFileExt(buf:pWideChar;sign:pointer):pWideChar;
+var
+ pc:pAnsiChar;
+begin
+ result:=buf;
+ pc:=GetMusicFormat;
+ if pc=nil then
+ begin
+ StrCopyW(buf,'sav');
+ if sign<>nil then
+ begin
+ if pdword(sign)^=signOGG then
+ StrCopyW(buf,'ogg')
+ else if ((pdword(sign)^ and $00FFFFFF)=signID3) or (pword(sign)^=signMP3) then
+ StrCopyW(buf,'mp3');
+ end;
+ end
+ else
+ begin
+ FastAnsiToWideBuf(pc,buf);
+ LowerCase(buf);
+ end;
+end;
+
+function MakeFileName(sign:pointer):pWideChar;
+var
+ p,pcw:PWideChar;
+ buf:pWideChar;
+begin
+// allocate buffer
+ mGetMem(buf,MAX_PATH*SizeOf(WideChar));
+// path
+ if recpath<>nil then
+ begin
+ ConvertFileName(recpath,buf,ActiveContact);
+// pcw:=ParseVarString(recpath,ActiveContact);
+// CallService(MS_UTILS_PATHTOABSOLUTEW,dword(pcw),dword(buf));
+// mFreeMem(pcw);
+ if not ForceDirectories(buf) then
+ begin
+ result:=nil;
+ exit;
+ end;
+ pcw:=StrEndW(buf);
+ if (pcw-1)^<>'\' then
+ begin
+ pcw^:='\';
+ inc(pcw);
+ end;
+ end
+ else
+ pcw:=buf;
+// name
+//!!
+ p:=MakeMessage;
+ pcw:=StrCopyEW(pcw,p);
+ mFreeMem(p);
+ if (pcw=buf) or ((pcw-1)^='\') then
+ pcw:=StrEndW(IntToHex(pcw,GetCurrentTime));
+// ext
+ pcw^:='.'; inc(pcw);
+ GetFileExt(pcw,sign);
+
+ result:=buf;
+end;
+
+procedure StatusProc(buffer:Pointer;len,user:DWORD); stdcall;
+var
+ pc:pWideChar;
+ pb:PByte;
+ i,sum:integer;
+ flag:bool;
+ doRecord:bool;
+begin
+ flag:=true;
+ doRecord:=CallService(MS_RADIO_COMMAND,MRC_RECORD,LPARAM(-1))<>0;
+ if (buffer<>nil) and (len<>0) and SaveHeader then
+ begin
+ SaveHeader:=false;
+ if pdword(buffer)^=signOGG then // if header ALL in buffer
+ begin
+ pb:=buffer;
+ flag:=false;
+ repeat
+ inc(pb,OGGHdrSize);
+ i:=pb^; //patterns
+ sum:=0;
+ inc(pb);
+ while i>0 do
+ begin
+ inc(sum,pb^);
+ inc(pb);
+ dec(i);
+ end;
+ inc(pb,sum); //here must be next sign
+ flag:=not flag;
+ until not flag;
+ hdrlen:=PAnsiChar(pb)-PAnsiChar(buffer);
+ mGetMem(StationHeader,hdrlen);
+ move(buffer^,StationHeader^,hdrlen);
+ end;
+ end;
+ if (buffer=nil) or not doRecord then // end of stream or stop record
+ begin
+ if not doRecord or (doContRec=BST_UNCHECKED) then
+ if hRecord<>0 then
+ begin
+ if buffer<>nil then // write tail
+ BlockWrite(hRecord,buffer^,len);
+ CloseHandle(hRecord);
+ hRecord:=0;
+ end;
+ end
+ else
+ begin
+ if len=0 then // HTTP or ICY tags
+ begin
+{
+while PAnsiChar(buffer)^<>#0 do
+begin
+messagebox(0,PAnsiChar(buffer),'ICY-HTTP',0);
+while PAnsiChar(buffer)^<>#0 do inc(PAnsiChar(buffer)); inc(PAnsiChar(buffer));
+end;
+}
+ end
+ else
+ begin
+ if doRecord then
+ begin
+ if hRecord=0 then
+ begin
+ pc:=MakeFileName(StationHeader);
+ if pc<>nil then
+ hRecord:=Rewrite(pc)
+ else
+ hRecord:=THANDLE(INVALID_HANDLE_VALUE);
+ if hRecord=THANDLE(INVALID_HANDLE_VALUE) then
+ hRecord:=0
+ else if flag and (StationHeader<>nil) then
+ begin
+ BlockWrite(hRecord,StationHeader^,hdrlen);
+// permissible to skip to the next Page (OggS) but this is not necessary
+ end;
+ mFreeMem(pc);
+ end;
+ if hRecord<>0 then
+ BlockWrite(hRecord,buffer^,len);
+ end;
+ end;
+ end;
+end;
+
+{$IFDEF Debug}
+procedure logmeta(tag,a,b:pansiChar);
+var
+ f:thandle;
+ p:pansichar;
+begin
+ f:=Append(pansichar('mradio.log'));
+ BlockWrite(f,tag^,StrLen(tag));
+ p:=#13#10; BlockWrite(f,p^,StrLen(p));
+ BlockWrite(f,a^,StrLen(a));
+ p:=#13#10; BlockWrite(f,p^,StrLen(p));
+ BlockWrite(f,b^,StrLen(b));
+ p:=#13#10; BlockWrite(f,p^,StrLen(p));
+ p:=#13#10; BlockWrite(f,p^,StrLen(p));
+ CloseHandle(f);
+end;
+{$ENDIF}
+
+function DoMeta(meta:PAnsiChar;TagType:int_ptr):Boolean;
+var
+ pcw:pWideChar;
+ buf:array [0..511] of AnsiChar;
+ artist,title:PAnsiChar;
+ oldartist,oldtitle:pAnsiChar;
+ ppc,pc:pAnsiChar;
+ idx,lcp:integer;
+ needtofree:Boolean;
+ CurDescrW:PWideChar;
+ old:boolean;
+
+// tag:PAnsiChar;
+ gotartist,gottitle:boolean; // indicate what we got artist/title
+begin
+ result:=false;
+
+ if meta=nil then
+ meta:=BASS_ChannelGetTags(chan,TagType);
+ if meta<>nil then
+ begin
+ // for cases when artist or title presents but empty
+ gotartist:=false;
+ gottitle :=false;
+ needtofree:=false;
+ lcp:=CP_UTF8;
+ buf[0]:=#0;
+ artist:=nil;
+ title :=nil;
+ CurDescrW:=nil;
+
+//tag:=meta;
+ case TagType of
+ BASS_TAG_WMA_META: begin
+ pc:=StrPos(meta,'data=');
+ if pc=meta then
+ begin
+ pc:=StrPos(meta,'artist=');
+ if pc<>nil then
+ begin
+ gotartist:=true;
+ mGetMem(artist,256);
+ Decode(artist,pc+7);
+ end;
+
+ pc:=StrPos(meta,'title=');
+ if pc<>nil then
+ begin
+ gottitle:=true;
+ mGetMem(title,256);
+ Decode(title,pc+6);
+ end;
+
+ pc:=StrPos(meta,'album=');
+ if pc<>nil then
+ begin
+ end;
+
+ pc:=StrPos(meta,'duration=');
+ if pc<>nil then
+ begin
+ end;
+
+ if not gotartist then
+ begin
+ if not gottitle then
+ begin
+ pc:=StrPos(meta,'caption=');
+ if pc<>nil then
+ begin
+ gottitle:=true;
+ mGetMem(title,256);
+ Decode(title,pc+8);
+ end;
+ end;
+
+ // analize title/caption for artist-title
+ if gottitle then
+ begin
+ pc:=StrPos(title,' - ');
+ if pc=nil then
+ pc:=StrScan(title,'-');
+ if pc<>nil then
+ begin
+ artist:=title;
+ if pc^=' ' then
+ title:=pc+3
+ else
+ title:=pc+1;
+ pc^:=#0;
+ CurDescrW:=pWideChar(artist);
+ end
+ else
+ CurDescrW:=pWideChar(title);
+ end;
+ end
+ else
+ needtofree:=true;
+
+ // to avoid mem leak and wrong tag process
+ result:=true;
+ end;
+ StatusProc(nil,0,0); // split records here
+ end;
+
+ BASS_TAG_META: begin
+//tag:='SHOUTCAST';
+ // SHOUTCAST StreamTitle='xxx';StreamUrl='xxx';
+ // "Station=xyz" meta tag="Trackinfo"
+ pc:=StrPos(meta,'StreamTitle=');
+ if pc<>nil then
+ begin
+ inc(pc,13);
+ ppc:=StrScan(pc,';');
+ if (ppc-pc-1)>0 then
+ begin
+ StrCopy(buf,pc,ppc-pc-1);
+ lcp:=GetTextFormat(@buf,ppc-pc-1);
+ end;
+ end;
+ if buf[0]<>#0 then
+ begin
+ case lcp of
+ CP_UTF8: UTF8ToWide(buf,CurDescrW);
+ CP_ACP : AnsiToWide(buf,CurDescrW,MirandaCP);
+ end;
+ end;
+
+ gottitle:=true;
+ title:=pAnsiChar(CurDescrW);
+ pcw:=StrPosW(CurDescrW,' - ');
+ if pcw=nil then
+ pcw:=StrScanW(CurDescrW,'-');
+ if pcw<>nil then
+ begin
+ artist:=pAnsiChar(CurDescrW);
+ if pcw^=' ' then
+ title:=pAnsiChar(pcw+3)
+ else
+ title:=pAnsiChar(pcw+1);
+ pcw^:=#0;
+ end;
+
+ lcp:=CP_UNICODE;
+
+ StatusProc(nil,0,0); // split records here
+ result:=true;
+ end;
+
+ BASS_TAG_ID3: begin // not realized, anyway - at the end of track
+ end;
+
+ BASS_TAG_ID3V2: begin
+ end;
+
+ BASS_TAG_APE, // not sure, need to check. maybe better process BASS_TAG_APEBINARY
+ BASS_TAG_WMA,
+ BASS_TAG_OGG: begin
+//tag:='OGG';
+ while meta^<>#0 do
+ begin
+ CharLowerA(StrCopy(buf,meta,10));
+ if StrCmp(buf,'title',5)=0 then
+ begin
+ title:=meta+6;
+ gottitle:=true;
+ end
+ else if StrCmp(buf,'artist',6)=0 then
+ begin
+ artist:=meta+7;
+ gotartist:=true;
+ end;
+ if gotartist and gottitle then
+ break;
+ while meta^<>#0 do inc(meta); inc(meta);
+ end;
+
+ if (not gotartist) and gottitle then
+ begin
+ pc:=StrPos(title,' - ');
+ if pc=nil then
+ pc:=StrScan(title,'-');
+ if pc<>nil then
+ begin
+ needtofree:=true;
+ StrDup(artist,title,pc-title);
+ if pc^=' ' then
+ idx:=3
+ else
+ idx:=1;
+ StrDup(title,pc+idx);
+ end;
+ end;
+ buf[0]:=#0;
+ end;
+ end;
+
+ old:=true;
+ if gotartist or gottitle then
+ begin
+ // check for old
+ oldartist:=nil;
+ oldtitle :=nil;
+ case lcp of
+ CP_UTF8: begin
+ if gotartist then
+ begin
+ oldartist:=DBReadUTF8(0,PluginName,optArtist);
+ if StrCmp(artist,oldartist)<>0 then
+ old:=false;
+ end;
+
+ if old and gottitle then
+ begin
+ oldtitle:=DBReadUTF8(0,PluginName,optTitle);
+ if StrCmp(title,oldtitle)<>0 then
+ old:=false;
+ end;
+
+ if not old then
+ begin
+ DBWriteUTF8(0,PluginName,optArtist,artist);
+ DBWriteUTF8(0,PluginName,optTitle ,title);
+ end;
+ end;
+
+ CP_UNICODE:begin
+ if gotartist then
+ begin
+ oldartist:=pAnsiChar(DBReadUnicode(0,PluginName,optArtist));
+ if StrCmpW(pWideChar(artist),pWideChar(oldartist))<>0 then
+ old:=false;
+ end;
+
+ if old and gottitle then
+ begin
+ oldtitle:=pAnsiChar(DBReadUnicode(0,PluginName,optTitle));
+ if StrCmpW(pWideChar(title),pWideChar(oldtitle))<>0 then
+ old:=false;
+ end;
+
+ if not old then
+ begin
+ DBWriteUnicode(0,PluginName,optArtist,pWideChar(artist));
+ DBWriteUnicode(0,PluginName,optTitle ,pWideChar(title));
+ end;
+ end;
+ end;
+{$IFDEF Debug}
+logmeta(tag,artist,title);
+{$ENDIF}
+ mFreeMem(oldartist);
+ mFreeMem(oldtitle);
+ mFreeMem(CurDescrW);
+ if needtofree then
+ begin
+ mFreeMem(artist);
+ mFreeMem(title );
+ end;
+ end;
+
+ if not old then
+ CallService(MS_RADIO_COMMAND,MRC_STATUS,RD_STATUS_NEWTAG);
+ end;
+end;
+
+procedure MetaSync(handle:HSYNC;channel,data:dword;user:pointer); stdcall;
+//var tagtype:Integer;
+begin
+(*
+ if handle=syncOGG then tagtype:=BASS_TAG_OGG
+ else if handle=syncWMA then tagtype:=BASS_TAG_WMA
+ else if handle=syncWMA1 then tagtype:=BASS_TAG_WMA_META
+ else {if handle=syncMETA then} tagtype:=BASS_TAG_META;
+*)
+ DoMeta(nil{PAnsiChar(data)},int_ptr(user){tagtype});
+end;
+
+procedure EndSync(handle:HSYNC;channel,data:dword;user:pointer); stdcall;
+var
+ lContact:cardinal;
+begin
+ if RemoteSong then
+ begin
+ lContact:=ActiveContact;
+ CallService(MS_RADIO_COMMAND,MRC_STOP,0);
+ CallService(MS_RADIO_COMMAND,MRC_PLAY,lContact)
+ end
+ else if plist<>nil then CallService(MS_RADIO_COMMAND,MRC_NEXT,0)
+ else if doLoop=BST_UNCHECKED then CallService(MS_RADIO_COMMAND,MRC_STOP,0);
+end;
+
+type
+ tICYField = record
+ name :PAnsiChar;
+ branch:PAnsiChar;
+ option:PAnsiChar;
+ end;
+const
+ NumICYFields = 4;
+ ICYFields: array [0..NumICYFields-1] of tICYField = (
+ (name:'icy-name:' ; branch:'CList' ; option:'MyHandle'),
+ (name:'icy-genre:' ; branch:cPluginName ; option:'Genre'),
+ (name:'icy-br:' ; branch:cPluginName ; option:'Bitrate'),
+ (name:'icy-description'; branch:cPluginName ; option:'About')
+ );
+
+procedure ProcessStationData;
+var
+ icy:PAnsiChar;
+ i,len:integer;
+begin
+ // get the broadcast name and bitrate
+ icy:=BASS_ChannelGetTags(chan,BASS_TAG_ICY);
+ if icy=nil then
+ icy:=BASS_ChannelGetTags(chan,BASS_TAG_HTTP); // no ICY tags, try HTTP
+ if icy<>nil then
+ begin
+ while icy^<>#0 do
+ begin
+ for i:=0 to NumICYFields-1 do
+ begin
+ with ICYFields[i] do
+ begin
+ len:=StrLen(name);
+ if StrCmp(icy,name,len)=0 then
+ begin
+ if DBReadStringLength(ActiveContact,branch,option)=0 then
+ DBWriteString(ActiveContact,branch,option,icy+len);
+ break;
+ end;
+ end;
+ end;
+ while icy^<>#0 do inc(icy); inc(icy);
+ end;
+ end;
+end;
+
+procedure OpenURL(url:PWideChar); cdecl;
+var
+ len,progress:DWORD;
+ flags:dword;
+ i:integer;
+ EAXUsed:bool;
+ ansiurl:array [0..511] of AnsiChar;
+begin
+ if plist=nil then
+ CallService(MS_RADIO_COMMAND,MRC_STATUS,RD_STATUS_CONNECT);
+
+ EAXUsed:=DBReadByte(0,PluginName,optEAXType,0)<>0;
+
+{}
+ for i:=0 to NumTries-1 do
+ begin
+ if EAXUsed then
+ flags:=BASS_STREAM_STATUS or BASS_SAMPLE_3D or BASS_SAMPLE_MONO
+ else
+ begin
+ if ForcedMono<>BST_UNCHECKED then
+ flags:=BASS_STREAM_STATUS or BASS_SAMPLE_MONO
+ else
+ flags:=BASS_STREAM_STATUS;
+ end;
+
+ flags:=flags or BASS_UNICODE;
+ if RemoteSong then
+ begin
+ SaveHeader:=true;
+ chan:=BASS_StreamCreateURL(url,0,flags,@StatusProc,nil)
+ end
+ else
+ begin
+ if (plist=nil) and (doLoop<>BST_UNCHECKED) then
+ flags:=flags or BASS_SAMPLE_LOOP;
+ chan:=BASS_StreamCreateFile(FALSE,url,0,0,flags);
+ end;
+
+ if (chan=0) and EAXUsed then
+ begin
+ flags:=flags and not (BASS_SAMPLE_3D or BASS_SAMPLE_MONO);
+ if ForcedMono<>BST_UNCHECKED then
+ flags:=flags or BASS_SAMPLE_MONO;
+
+ if RemoteSong then
+ chan:=BASS_StreamCreateURL({ansi}url,0,flags,@StatusProc,nil)
+ else
+ chan:=BASS_StreamCreateFile(FALSE,url,0,0,flags);
+ end;
+
+ if (chan=0) and RemoteSong then
+ begin
+ if BASS_ErrorGetCode=BASS_ERROR_FILEOPEN then
+ begin
+ flags:=flags and not BASS_UNICODE;
+ chan:=BASS_StreamCreateURL(FastWideToAnsiBuf(url,ansiurl),0,flags,@StatusProc,nil)
+ end;
+ end;
+
+ if chan<>0 then break;
+ end;
+{}
+
+ if chan=0 then
+ begin
+ if (CallService(MS_RADIO_COMMAND,MRC_STATUS,RD_STATUS_GET)=RD_STATUS_ABORT) or
+ (plist=nil) then
+ begin
+ CallService(MS_RADIO_COMMAND,MRC_STOP,1);
+ end
+ else if plist<>nil then
+ CallService(MS_RADIO_COMMAND,MRC_NEXT,0);
+ end
+ else
+ begin
+
+ CallService(MS_RADIO_COMMAND,MRC_STATUS,RD_STATUS_NEWTRACK);
+
+ if RemoteSong then
+ begin
+ if isEQ_OFF=BST_UNCHECKED then
+ EQ_ON;
+
+{$IFDEF CHANGE_NAME_BUFFERED}
+ icy:=DBReadString(ActiveContact,strCList,optMyHandle);
+ mGetMem(url,StrLen(icy)+6);
+ StrCopy(url+6,icy);
+ mFreeMem(icy);
+ url[0]:='[';
+ url[1]:=#0;
+ url[4]:=']';
+ url[5]:=' ';
+{$ENDIF}
+ progress:=0;
+ repeat
+ if CallService(MS_RADIO_COMMAND,MRC_STATUS,RD_STATUS_GET)=RD_STATUS_ABORT then
+ begin
+ CallService(MS_RADIO_COMMAND,MRC_STOP,1);
+ exit;
+ end;
+ len:=BASS_StreamGetFilePosition(chan,BASS_FILEPOS_END);
+ if len=DW_ERROR then
+ break;
+ progress:=BASS_StreamGetFilePosition(chan, BASS_FILEPOS_DOWNLOAD);
+ if progress=dword(-1) then
+ break;
+ progress:=(progress-
+ BASS_StreamGetFilePosition(chan,BASS_FILEPOS_CURRENT))*100 div len;
+ // percentage of buffer filled
+{$IFDEF CHANGE_NAME_BUFFERED}
+ IntToStr(url+1,progress,2);
+ url[3]:='%';
+ DBWriteString(ActiveContact,strCList,optMyHandle,url);
+{$ENDIF}
+ until progress>sPreBuf;
+{$IFDEF CHANGE_NAME_BUFFERED}
+ if url[1]<>#0 then
+ DBWriteString(ActiveContact,strCList,optMyHandle,url+6);
+ mFreeMem(url);
+{$ENDIF}
+
+ ProcessStationData; // process ICY-headers
+
+ // get the stream title and set sync for subsequent titles
+ DoMeta(nil,BASS_TAG_META);
+
+ syncMETA:=BASS_ChannelSetSync(chan,BASS_SYNC_META ,0,@MetaSync,pointer(BASS_TAG_META));
+ syncOGG :=BASS_ChannelSetSync(chan,BASS_SYNC_OGG_CHANGE,0,@MetaSync,pointer(BASS_TAG_OGG));
+ syncWMA :=BASS_ChannelSetSync(chan,BASS_SYNC_WMA_CHANGE,0,@MetaSync,pointer(BASS_TAG_WMA));
+ syncWMA1:=BASS_ChannelSetSync(chan,BASS_SYNC_WMA_META ,0,@MetaSync,pointer(BASS_TAG_WMA_META));
+ end
+ else
+ begin
+ if not DoMeta(nil,BASS_TAG_OGG) then
+ if not DoMeta(nil,BASS_TAG_ID3V2) then
+ if not DoMeta(nil,BASS_TAG_ID3) then
+ if not DoMeta(nil,BASS_TAG_APE) then
+ ;
+
+ end;
+
+ syncEND:=BASS_ChannelSetSync(chan,BASS_SYNC_END,0,@EndSync,nil);
+
+ SetSndVol(-1);
+ // play it!
+ BASS_ChannelPlay(chan,FALSE);
+ end;
+end;
diff --git a/plugins/mRadio/i_cc.inc b/plugins/mRadio/i_cc.inc
new file mode 100644
index 0000000000..deb993ec0c
--- /dev/null
+++ b/plugins/mRadio/i_cc.inc
@@ -0,0 +1,433 @@
+{}
+
+function GetStatusText(status:integer;toCList:boolean=false):PWideChar;
+begin
+ case status of
+ RD_STATUS_PAUSED : result:='paused';
+ RD_STATUS_STOPPED: if toCList then result:=nil else result:='stopped';
+ RD_STATUS_CONNECT: result:='connecting';
+ RD_STATUS_ABORT : result:='aborting';
+ RD_STATUS_PLAYING: if toCList then result:=nil else result:='playing';
+ else
+ result:=nil;
+ end;
+end;
+
+procedure ConstructMsg(artist,title:PWideChar;status:integer=-1);
+var
+ buf:PWideChar;
+ tstrlen,astrlen,statuslen:integer;
+ sstatus:PWideChar;
+ p:pWideChar;
+begin
+ astrlen:=StrLenW(artist);
+
+ tstrlen:=0;
+ // if need to remove duplicate
+ if (title<>nil) and (title^<>#0) then
+ begin
+ if (astrlen=0) or (StrCmpW(artist,title)<>0) then
+ tstrlen:=StrLenW(title);
+ end;
+ if (astrlen>0) and (tstrlen>0) then
+ inc(tstrlen,3);
+
+ statuslen:=0;
+ if status>=0 then
+ begin
+ sstatus:=TranslateW(GetStatusText(status,true));
+ if sstatus<>nil then
+ statuslen:=StrLenW(sstatus)+3;
+ end
+ else
+ sstatus:=nil;
+
+ if (astrlen+tstrlen+statuslen)>0 then
+ begin
+ mGetMem(buf,(astrlen+tstrlen+statuslen+1)*SizeOf(WideChar));
+ p:=buf;
+ if astrlen>0 then
+ p:=StrCopyEW(p,artist);
+
+ if tstrlen>0 then
+ begin
+ if astrlen>0 then
+ begin
+ p^:=' '; inc(p);
+ p^:='-'; inc(p);
+ p^:=' '; inc(p);
+ end;
+ p:=StrCopyEW(p,title);
+ end;
+
+ if statuslen>0 then
+ begin
+ p^:=' '; inc(p);
+ p^:='('; inc(p);
+ p:=StrCopyEW(p,sstatus);
+ p^:=')'; inc(p);
+ p^:=#0;
+ end;
+
+ DBWriteUnicode(ActiveContact,strCList,optStatusMsg,buf);
+ mFreeMem(buf);
+ end
+ else
+ DBDeleteSetting(ActiveContact,strCList,optStatusMsg);
+end;
+
+{$IFDEF Debug}
+procedure log(a,b:lparam);
+var
+ f:thandle;
+ buf:array [0..31] of ansichar;
+ p:pansichar;
+begin
+ f:=Append(pansichar('mradio.log'));
+ p:=IntToStr(buf,a); BlockWrite(f,p^,StrLen(p));
+ p:=#13#10; BlockWrite(f,p^,StrLen(p));
+ p:=IntToStr(buf,b); BlockWrite(f,p^,StrLen(p));
+ p:=#13#10; BlockWrite(f,p^,StrLen(p));
+ p:=#13#10; BlockWrite(f,p^,StrLen(p));
+ CloseHandle(f);
+end;
+{$ENDIF}
+
+function ControlCenter(code:WPARAM;arg:LPARAM):int_ptr; cdecl;
+const
+ PlayStatus:int_ptr=RD_STATUS_NOSTATION;
+ doRecord:boolean=false;
+var
+ tmpbuf,buf1:array [0..MAX_PATH-1] of WideChar;
+ plFile:pWideChar;
+ plLocal:boolean;
+ artist,title:pWideChar;
+begin
+ result:=0;
+{$IFDEF Debug}
+ log(code,arg);
+{$ENDIF}
+ case code of
+ MRC_PLAY : begin
+ // play new station?
+ if arg<>0 then
+ begin
+ ActiveURLw:=DBReadUnicode(arg,PluginName,optStationURL);
+ // no URL for this contact
+ if (ActiveURLw=nil) or (ActiveURLw^=#0) then exit;
+
+ ActiveContact:=arg;
+ RemoteSong:=StrPosW(ActiveURLw,'://')<>nil;
+
+ if isPlaylist(ActiveURLw)<>0 then
+ begin
+
+ if RemoteSong then
+ begin
+ GetTempPathW(MAX_PATH,tmpbuf);
+ GetTempFileNameW(tmpbuf,'mr',GetCurrentTime,buf1);
+ if not GetFile(ActiveURLw,buf1,hNetLib) then
+ begin
+ exit;
+ end;
+ plFile:=@buf1;
+ plLocal:=false;
+ end
+ else
+ begin
+ plFile:=ActiveURLw;
+ plLocal:=true;
+ end;
+
+ plist:=CreatePlaylist(plFile);
+ if not plLocal then
+ DeleteFileW(plFile);
+
+ if plist<>nil then
+ begin
+ if plist.GetCount=0 then
+ begin
+ plist.Free;
+ plist:=nil;
+ ActiveContact:=0;
+ exit;
+ end;
+ plist.Shuffle:=doShuffle<>BST_UNCHECKED;
+
+ if not plist.Shuffle then
+ begin
+ if PlayFirst=BST_UNCHECKED then
+ plist.Track:=DBReadWord(ActiveContact,PluginName,optCurElement);
+ end;
+
+ mFreeMem(ActiveURLw);
+ ActiveURLw:=plist.GetSong;
+ end;
+ RemoteSong:=StrPosW(ActiveURLw,'://')<>nil; // coz activeuRLw can be changed
+ end
+ else
+ plist:=nil;
+
+ if (ActiveURLw<>nil) and (ActiveURLw^<>#0) then
+ begin
+ if RemoteSong then
+ begin
+ if GetWorkOfflineStatus<>0 then
+ begin
+ BassError('Can''t connect to net. Please, clear ''Work offline'' option in Internet settings');
+ ControlCenter(MRC_STATUS,RD_STATUS_NOSTATION);
+ exit;
+ end;
+ end;
+ ControlCenter(MRC_STATUS,RD_STATUS_NEWSTATION);
+ if Assigned(plist) then
+ ControlCenter(MRC_STATUS,RD_STATUS_CONNECT);
+ CloseHandle(mir_forkthread(@OpenURL,ActiveURLw));
+ end;
+ end
+ // play current from start
+ else if chan<>0 then
+ begin
+ BASS_ChannelPlay(chan,true);
+ code:=MRC_STATUS;
+ arg :=RD_STATUS_PLAYING;
+ end
+ // play playlist entry?
+ else if Assigned(plist) and (ActiveURLw<>nil) and (ActiveURLw^<>#0) then
+ begin
+ CloseHandle(mir_forkthread(@OpenURL,ActiveURLw));
+ end;
+ end;
+
+ MRC_RECORD: begin
+ if arg=LPARAM(-1) then
+ result:=ord(doRecord)
+ else
+ begin
+ case arg of
+ 1: doRecord:=true; // force start
+ 2: doRecord:=false; // force stop
+ else
+ doRecord:=not doRecord;
+ end;
+ result:=ord(doRecord);
+
+ code:=MRC_STATUS;
+ arg :=RD_STATUS_RECORD+(result shl 16);
+ end;
+ end;
+
+ MRC_PAUSE: begin
+ if chan<>0 then
+ begin
+ case ControlCenter(MRC_STATUS,RD_STATUS_GET) of
+ RD_STATUS_PLAYING: begin
+ BASS_ChannelPause(chan);
+ arg:=1;
+ end;
+ RD_STATUS_PAUSED: begin
+ BASS_ChannelPlay(chan,false);
+ arg:=0;
+ end;
+ end;
+ code:=MRC_STATUS;
+ arg :=RD_STATUS_PAUSED+(arg shl 16);
+ end
+ else
+ begin
+ ActiveContact:=LoadContact(PluginName,optLastStn);
+ if ActiveContact<>0 then
+ ControlCenter(MRC_PLAY,ActiveContact);
+ end;
+ end;
+
+ MRC_STOP: begin
+ ControlCenter(MRC_RECORD,2);
+
+ code:=MRC_STATUS;
+ // stop playlist track but not station
+ if Assigned(plist) and (arg=0) then
+ begin
+ if chan<>0 then
+ BASS_ChannelStop(chan);
+
+ arg:=RD_STATUS_STOPPED;
+ end
+ else // forced or "normal" stop
+ begin
+ if Assigned(plist) then
+ begin
+ DBWriteWord(ActiveContact,PluginName,optCurElement,plist.Track);
+ plist.Free;
+ plist:=nil;
+ end;
+ ControlCenter(MRC_STATUS,RD_STATUS_STOPPED);
+ // Save station for next autoplay
+ SaveContact(ActiveContact,PluginName,optLastStn);
+ StopStation;
+
+ arg:=RD_STATUS_NOSTATION;
+ end;
+ end;
+
+ MRC_MUTE: begin
+ CallService(MS_RADIO_MUTE,0,arg);
+ code:=MRC_STATUS;
+ arg :=RD_STATUS_MUTED;
+ end;
+
+ MRC_NEXT: begin
+ if Assigned(plist) then
+ begin
+ StopStation;
+ ActiveURLw:=plist.Next;
+ ControlCenter(MRC_PLAY,0);
+ end;
+ end;
+
+ MRC_PREV: begin
+ if Assigned(plist) then
+ begin
+ StopStation;
+ ActiveURLw:=plist.Previous;
+ ControlCenter(MRC_PLAY,0)
+ end;
+ end;
+
+ MRC_SEEK: begin
+ if not RemoteSong and (chan<>0) then
+ begin
+ if integer(arg)=-1 then // get position
+ begin
+ result:=trunc(BASS_ChannelBytes2Seconds(chan,BASS_ChannelGetPosition(chan,BASS_POS_BYTE)));
+ if result<0 then
+ result:=0;
+ end
+ else
+ begin
+ BASS_ChannelSetPosition(chan,BASS_ChannelSeconds2Bytes(chan,arg),BASS_POS_BYTE);
+
+ code:=MRC_STATUS;
+ arg :=RD_STATUS_POSITION+(arg shl 16);
+ end;
+ end;
+ end;
+ end;
+
+ if code=MRC_STATUS then
+ begin
+ if arg=RD_STATUS_GET then
+ result:=PlayStatus
+ else
+ begin
+
+ code:=(arg and $FFFF);
+ arg :=hiword(arg);
+ // this is just events
+ case code of
+ RD_STATUS_MUTED: begin
+ end;
+
+ RD_STATUS_RECORD: begin
+ end;
+
+ RD_STATUS_POSITION: begin
+ end;
+
+ else
+ // these statuses are for events and some tasks
+ artist:=nil;
+ title :=nil;
+ case code of
+ RD_STATUS_NOSTATION: begin
+ SetStatus(ActiveContact,ID_STATUS_OFFLINE);
+ ActiveContact:=0;
+
+ DBDeleteSetting(0,PluginName,optActiveURL);
+
+ PlayStatus:=RD_STATUS_NOSTATION;
+ // empty message
+ end;
+
+ RD_STATUS_STOPPED: begin
+ if ActiveContact<>0 then //!! fools proof
+ DBDeleteSetting(ActiveContact,strCList,optStatusMsg);
+
+ DBDeleteSetting(0,PluginName,optTitle);
+ DBDeleteSetting(0,PluginName,optArtist);
+
+ PlayStatus:=RD_STATUS_STOPPED;
+ // empty message
+ end;
+
+ RD_STATUS_ABORT: begin
+ PlayStatus:=RD_STATUS_ABORT;
+ // status as message
+ end;
+
+ RD_STATUS_CONNECT: begin
+ SetStatus(ActiveContact,ID_STATUS_AWAY);
+ PlayStatus:=RD_STATUS_CONNECT;
+ // status as message
+ end;
+
+ RD_STATUS_NEWSTATION: begin
+ arg:=ActiveContact;
+ PlayStatus:=RD_STATUS_PLAYING;
+ end;
+
+ RD_STATUS_NEWTRACK: begin
+ SetStatus(ActiveContact,ID_STATUS_ONLINE);
+ DBWriteUnicode(0,PluginName,optActiveURL,ActiveURLw);
+
+ DBWriteString(0,PluginName,optActiveCodec,GetMusicFormat);
+ arg :=lparam(ActiveURLw);
+
+ // for case when tags was in meta
+ artist:=DBReadUnicode(0,PluginName,optArtist);
+ title :=DBReadUnicode(0,PluginName,optTitle);
+ PlayStatus:=RD_STATUS_PLAYING;
+ // status as message
+ end;
+
+ RD_STATUS_NEWTAG: begin
+ // must be updated tags
+ artist:=DBReadUnicode(0,PluginName,optArtist);
+ title :=DBReadUnicode(0,PluginName,optTitle);
+
+ PlayStatus:=RD_STATUS_PLAYING; // maybe keep RD_STATUS_NEWTAG?
+ end;
+
+ RD_STATUS_PLAYING: begin
+ SetStatus(ActiveContact,ID_STATUS_ONLINE);
+ PlayStatus:=RD_STATUS_PLAYING;
+ // status as message
+ end;
+
+ RD_STATUS_PAUSED: begin
+ if arg=0 then
+ begin
+ PlayStatus:=RD_STATUS_PLAYING;
+ if StrPosW(ActiveURLw,'://')=nil then //local only
+ begin
+ artist:=DBReadUnicode(0,PluginName,optArtist);
+ title :=DBReadUnicode(0,PluginName,optTitle);
+ end;
+ end
+ else
+ PlayStatus:=RD_STATUS_PAUSED;
+ // status as message
+ end;
+ else
+ exit;
+ end;
+ ConstructMsg(artist,title,PlayStatus);
+ mFreeMem(artist);
+ mFreeMem(title);
+ end;
+
+ NotifyEventHooks(hhRadioStatus,code,arg);
+ end;
+
+ end;
+end;
diff --git a/plugins/mRadio/i_frame.inc b/plugins/mRadio/i_frame.inc
new file mode 100644
index 0000000000..89de119d0e
--- /dev/null
+++ b/plugins/mRadio/i_frame.inc
@@ -0,0 +1,242 @@
+{mRadio frame}
+const
+ frm_back:pAnsiChar = 'Frame background';
+const
+ FrameWnd:HWND = 0;
+ FrameId:integer = -1;
+ OldEditProc:pointer=nil;
+ pattern:pWideChar=nil;
+ current: THANDLE = 0;
+var
+ FrameCtrl:PControl;
+ colorhook:THANDLE;
+ hbr:HBRUSH;
+ frm_bkg:TCOLORREF;
+
+function RadioFrameProc( Sender: PControl; var Msg: TMsg; var Rslt:Integer ): boolean;
+begin
+ result:=false;
+ rslt:=0;
+
+ case Msg.message of
+
+ WM_DESTROY: begin
+ hVolFrmCtrl:=0;
+ DeleteObject(hbr);
+ end;
+
+ WM_SHOWWINDOW: begin
+ Rslt:=1;
+ with Sender^ do
+ hVolFrmCtrl:=Children[1].Handle;
+ end;
+
+ WM_CTLCOLORSTATIC: begin
+// SetBkColor(msg.wParam, frm_bkg);
+ Rslt:=hbr;
+ result:=True;
+ end;
+
+ WM_CONTEXTMENU: begin
+ CallService('mRadio/Settings',0,0);
+ end;
+
+ WM_HSCROLL: begin
+ case loword(msg.wParam) of
+ SB_THUMBTRACK: begin
+// gVolume:=Hiword(msg.wParam);
+ Service_RadioSetVolume(Hiword(msg.wParam){gVolume},2);
+ end;
+ SB_ENDSCROLL: begin
+// gVolume:=SendMessage(msg.lParam,TBM_GETPOS,0,0);
+ Service_RadioSetVolume({Hiword(msg.wParam)}SendMessage(msg.lParam,TBM_GETPOS,0,0){gVolume},2)
+ end;
+ end;
+ end;
+
+ end;
+end;
+
+function DoAction(action:integer):integer;
+begin
+ result:=Service_RadioMute(0,1);
+end;
+
+function ColorReload(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ cid:TColourID;
+// wnd:HWND;
+// i:integer;
+begin
+ result:=0;
+ cid.cbSize:=SizeOf(cid);
+ StrCopy(cid.group,cPluginName);
+ StrCopy(cid.name ,frm_back);
+ frm_bkg:=CallService(MS_COLOUR_GETA,dword(@cid),0);
+ if hbr<>0 then DeleteObject(hbr);
+ hbr:=CreateSolidBrush(frm_bkg);
+
+ FrameCtrl.Children[1].Width := FrameCtrl.Children[1].Width-1;
+ FrameCtrl.Children[1].Width := FrameCtrl.Children[1].Width+1;
+
+ FrameCtrl.InvalidateEx;
+ FrameCtrl.Update;
+end;
+
+function GetIcon(action:integer;stat:integer=AST_NORMAL):cardinal;
+begin
+ case stat of
+ AST_NORMAL : result:=CallService(MS_SKIN2_GETICON,0,dword(IcoBtnOn));
+ AST_PRESSED: result:=CallService(MS_SKIN2_GETICON,0,dword(IcoBtnOff));
+ else
+ result:=0;
+ end;
+end;
+
+procedure MyErase(DummySelf, Sender:PControl; DC:HDC);
+begin
+end;
+
+procedure MyEraseFrame(DummySelf, Sender:PControl; DC:HDC);
+var
+ rc:TRECT;
+begin
+ GetClientRect(Sender.Handle, rc);
+ Sender.Canvas.Brush.Color := frm_bkg;
+ Sender.Canvas.FillRect(rc);
+end;
+
+function CreateFrameWindow(parent:HWND):THANDLE;
+var
+ TB:PTrackbar;
+begin
+ result:=0;
+
+ FrameCtrl:=NewAlienPanel(parent,esNone);
+ if FrameCtrl<>nil then
+ begin
+ result:=FrameCtrl.GetWindowHandle;
+ with FrameCtrl^ do
+ begin
+ // Button
+ btnMute:=CreateIcoButton(FrameCtrl,GetIcon,DoAction,IDC_RADIO_MUTE);
+ with btnMute^ do
+ begin
+ SetSize(16,16);
+ SetPosition(FrameCtrl.Width-18,0);
+ Anchor(false,true,true,false);
+
+ AsCheckbox:=true;
+ end;
+
+ // Trackbar
+ TB:=NewTrackbar(FrameCtrl,[trbTooltips,trbNoTicks,trbBoth,trbNoBorder],nil);
+ with TB^ do
+ begin
+ SetSize(FrameCtrl.Width-24,18);
+ SetPosition(2,0);
+ Anchor(true,true,true,false);
+
+ RangeMin:=0;
+ RangeMax:=100;
+ PageSize:=20;
+
+ OnEraseBkGnd:=TOnPaint(MakeMethod(nil, @MyErase));
+ end;
+
+ AttachProc(@RadioFrameProc);
+ end;
+
+ with FrameCtrl^ do
+ begin
+ MinWidth :=80;
+ MinHeight:=20;
+ Height:=18;
+ Anchor(true,true,true,true);
+// OnMessage:=TOnMessage(MakeMethod(nil, @MyEraseFrame));
+ OnEraseBkGnd:=TOnPaint(MakeMethod(nil, @MyEraseFrame));
+{
+ OnPaint :=FrameCtrl.Paint;
+ OnResize :=FrameCtrl.FrameResize;
+ OnMouseDown:=FrameCtrl.MouseDown;
+}
+ end;
+ end;
+end;
+
+procedure CreateFrame(parent:HWND);
+var
+ Frame:TCLISTFrame;
+// wnd:HWND;
+// tmp:cardinal;
+ tr:TRECT;
+ cid:TColourID;
+begin
+ if ServiceExists(MS_CLIST_FRAMES_ADDFRAME)=0 then
+ exit;
+ if parent=0 then
+ parent:=CallService(MS_CLUI_GETHWND,0,0);
+
+ FrameWnd:=CreateFrameWindow(parent);
+
+ if FrameWnd<>0 then
+ begin
+ GetWindowRect(FrameWnd,tr);
+ FillChar(Frame,SizeOf(Frame),0);
+ with Frame do
+ begin
+ cbSize :=SizeOf(Frame);
+ hWnd :=FrameWnd;
+ hIcon :=0;
+ align :=alTop;
+ height :=tr.bottom-tr.top+2;
+ if IsAnsi then
+ begin
+ Flags :=F_VISIBLE or F_NOBORDER;
+ name.a :=cPluginName;
+ TBName.a:=cPluginName + ' volume control';
+ end
+ else
+ begin
+ Flags :=F_VISIBLE or F_NOBORDER or F_UNICODE;
+ name.w :=cPluginName;
+ TBName.w:=cPluginName + ' volume control';
+ end;
+ end;
+
+ FrameId:=CallService(MS_CLIST_FRAMES_ADDFRAME,dword(@Frame),0);
+ if FrameId>=0 then
+ begin
+ CallService(MS_CLIST_FRAMES_UPDATEFRAME,FrameId, FU_FMPOS);
+
+ cid.cbSize:=SizeOf(cid);
+ cid.flags :=0;
+ StrCopy(cid.group,cPluginName);
+ StrCopy(cid.dbSettingsGroup,cPluginName);
+
+ StrCopy(cid.name ,frm_back);
+ StrCopy(cid.setting,'frame_back');
+ cid.defcolour:=COLOR_3DFACE;
+ cid.order :=0;
+ ColourRegister(@cid);
+
+ hbr:=0;
+ colorhook:=HookEvent(ME_COLOUR_RELOAD,@ColorReload);
+ ColorReload(0,0);
+ Service_RadioSetVolume(gVolume,0);
+ end;
+ end;
+end;
+
+procedure DestroyFrame;
+begin
+ if FrameId>=0 then
+ begin
+ UnhookEvent(colorhook);
+ CallService(MS_CLIST_FRAMES_REMOVEFRAME,FrameId,0);
+ FrameId:=-1;
+ end;
+ FrameCtrl.Free;
+ // DestroyWindow(FrameWnd);
+ FrameWnd:=0;
+end;
diff --git a/plugins/mRadio/i_frameapi.inc b/plugins/mRadio/i_frameapi.inc
new file mode 100644
index 0000000000..002e2fc142
--- /dev/null
+++ b/plugins/mRadio/i_frameapi.inc
@@ -0,0 +1,234 @@
+{mRadio frame}
+const
+ frm_back:pAnsiChar = 'Frame background';
+const
+ FrameWnd:HWND = 0;
+ FrameId:integer = -1;
+var
+ colorhook:THANDLE;
+ hbr:HBRUSH;
+ frm_bkg:TCOLORREF;
+ OldBtnWndProc:pointer;
+ OldSliderWndProc:pointer;
+
+function QSDlgResizer(Dialog:HWND;lParam:LPARAM;urc:PUTILRESIZECONTROL):int; cdecl;
+begin
+ case urc^.wId of
+ IDC_RADIO_MUTE: result:=RD_ANCHORX_RIGHT or RD_ANCHORY_CENTRE;
+ IDC_RADIO_VOL : result:=RD_ANCHORX_WIDTH or RD_ANCHORY_CENTRE;
+ else
+ result:=0;
+ end;
+end;
+
+function SliderWndProc(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+begin
+ if hMessage=WM_ERASEBKGND then
+ result:=1
+ else
+ result:=CallWindowProc(OldSliderWndProc, Dialog, hMessage, wParam, lParam);
+end;
+
+function BtnWndProc(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+begin
+ if hMessage=WM_ERASEBKGND then
+ result:=1
+ else
+ result:=CallWindowProc(OldBtnWndProc, Dialog, hMessage, wParam, lParam);
+end;
+
+function RadioFrameProc(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+var
+ urd:TUTILRESIZEDIALOG;
+ rc:TRECT;
+ ti:TTOOLINFOW;
+ hwndTooltip:HWND;
+begin
+ result:=0;
+ case hMessage of
+ WM_DESTROY: begin
+ hVolFrmCtrl :=0;
+ hMuteFrmCtrl:=0;
+ DeleteObject(hbr);
+ end;
+
+ WM_INITDIALOG: begin
+ hMuteFrmCtrl:=GetDlgItem(Dialog,IDC_RADIO_MUTE);
+ SendMessage(hMuteFrmCtrl, BUTTONSETASFLATBTN,0,0);
+ SetButtonIcon(hMuteFrmCtrl,IcoBtnOn);
+
+ hwndTooltip:=CreateWindowW(TOOLTIPS_CLASS,nil,TTS_ALWAYSTIP,
+ integer(CW_USEDEFAULT),integer(CW_USEDEFAULT),
+ integer(CW_USEDEFAULT),integer(CW_USEDEFAULT),
+ Dialog,0,hInstance,nil);
+
+ FillChar(ti,SizeOf(ti),0);
+ ti.cbSize :=sizeof(TOOLINFO);
+ ti.uFlags :=TTF_IDISHWND or TTF_SUBCLASS;
+ ti.hwnd :=Dialog;
+ ti.hinst :=hInstance;
+ ti.uId :=hMuteFrmCtrl;
+ ti.lpszText:=pWideChar(TranslateW('Mute'));
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,tlparam(@ti));
+
+ hVolFrmCtrl:=GetDlgItem(Dialog,IDC_RADIO_VOL);
+ SendMessage(hVolFrmCtrl,TBM_SETRANGE,0,MAKELONG(0,100));
+ SendMessage(hVolFrmCtrl,TBM_SETPAGESIZE,0,20);
+ SendMessage(hVolFrmCtrl,TBM_SETPOS,1,gVolume);
+
+ OldSliderWndProc:=pointer(SetWindowLongPtrW(hVolFrmCtrl,GWL_WNDPROC,LONG_PTR(@SliderWndProc)));
+ OldBtnWndProc :=pointer(SetWindowLongPtrW(hVolFrmCtrl,GWL_WNDPROC,LONG_PTR(@BtnWndProc)));
+ end;
+
+ WM_SIZE: begin
+ FillChar(urd,SizeOf(TUTILRESIZEDIALOG),0);
+ urd.cbSize :=SizeOf(urd);
+ urd.hwndDlg :=Dialog;
+ urd.hInstance :=hInstance;
+ urd.lpTemplate:=MAKEINTRESOURCEA(IDD_FRAME);
+ urd.lParam :=0;
+ urd.pfnResizer:=@QSDlgResizer;
+ CallService(MS_UTILS_RESIZEDIALOG,0,tlparam(@urd));
+ end;
+
+ WM_ERASEBKGND: begin
+ GetClientRect(Dialog,rc);
+ FillRect(wParam,rc,hbr);
+ result:=1;
+ end;
+
+ WM_CTLCOLORSTATIC: begin
+ if THANDLE(lParam)=hVolFrmCtrl then
+ begin
+ SetBkColor(wParam, frm_bkg);
+ result:=hbr;
+ end;
+ end;
+
+ WM_CONTEXTMENU: begin
+ CallService(cPluginName + '/Settings',0,0);
+ end;
+
+ WM_COMMAND: begin
+ case wParam shr 16 of
+
+ BN_CLICKED: begin
+ case loword(wParam) of
+ IDC_RADIO_MUTE: begin
+ Service_RadioMute(0,1);
+ end;
+ end;
+ end;
+ end;
+ end;
+
+ WM_HSCROLL: begin
+// gVolume:=SendMessage(lParam,TBM_GETPOS,0,0);
+ Service_RadioSetVolume(SendMessage(lParam,TBM_GETPOS,0,0){gVolume},2)
+ end;
+
+ else
+ result:=DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+end;
+
+function ColorReload(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ cid:TColourID;
+ wnd:HWND;
+begin
+ result:=0;
+ cid.cbSize:=SizeOf(cid);
+ StrCopy(cid.group,cPluginName);
+ StrCopy(cid.name ,frm_back);
+ frm_bkg:=CallService(MS_COLOUR_GETA,twparam(@cid),0);
+ if hbr<>0 then DeleteObject(hbr);
+ hbr:=CreateSolidBrush(frm_bkg);
+
+ wnd:=GetFocus();
+ InvalidateRect(hVolFrmCtrl,nil,true);
+ SetFocus(hVolFrmCtrl);
+ RedrawWindow(FrameWnd,nil,0,RDW_INVALIDATE or RDW_ALLCHILDREN or RDW_ERASE);
+ SetFocus(wnd);
+end;
+
+procedure CreateFrame(parent:HWND);
+var
+ Frame:TCLISTFrame;
+ wnd:HWND;
+ tmp:cardinal;
+ tr:TRECT;
+ cid:TColourID;
+begin
+ if ServiceExists(MS_CLIST_FRAMES_ADDFRAME)=0 then
+ exit;
+ if parent=0 then
+ parent:=CallService(MS_CLUI_GETHWND,0,0);
+
+ if FrameWnd=0 then
+ FrameWnd:=CreateDialog(hInstance,MAKEINTRESOURCE(IDD_FRAME),parent,@RadioFrameProc);
+
+ if FrameWnd<>0 then
+ begin
+ GetWindowRect(FrameWnd,tr);
+ FillChar(Frame,SizeOf(Frame),0);
+ with Frame do
+ begin
+ cbSize :=SizeOf(Frame);
+ hWnd :=FrameWnd;
+ hIcon :=0;
+ align :=alTop;
+ height :=tr.bottom-tr.top+2;
+ if IsAnsi then
+ begin
+ Flags :=F_VISIBLE or F_NOBORDER;
+ name.a :=cPluginName;
+ TBName.a:=cPluginName + ' volume control';
+ end
+ else
+ begin
+ Flags :=F_VISIBLE or F_NOBORDER or F_UNICODE;
+ name.w :=cPluginName;
+ TBName.w:=cPluginName + ' volume control';
+ end;
+ end;
+
+ FrameId:=CallService(MS_CLIST_FRAMES_ADDFRAME,wparam(@Frame),0);
+ if FrameId>=0 then
+ begin
+ CallService(MS_CLIST_FRAMES_UPDATEFRAME,FrameId, FU_FMPOS);
+
+ wnd:=CallService(MS_CLUI_GETHWND{MS_CLUI_GETHWNDTREE},0,0);
+ tmp:=SendMessage(wnd,CLM_GETEXSTYLE,0,0);
+ SendMessage(wnd,CLM_SETEXSTYLE,tmp or CLS_EX_SHOWSELALWAYS,0);
+
+ cid.cbSize:=SizeOf(cid);
+ cid.flags :=0;
+ StrCopy(cid.group,cPluginName);
+ StrCopy(cid.dbSettingsGroup,cPluginName);
+
+ StrCopy(cid.name ,frm_back);
+ StrCopy(cid.setting,'frame_back');
+ cid.defcolour:=COLOR_3DFACE;
+ cid.order :=0;
+ ColourRegister(@cid);
+
+ hbr:=0;
+ colorhook:=HookEvent(ME_COLOUR_RELOAD,@ColorReload);
+ ColorReload(0,0);
+ Service_RadioSetVolume(gVolume,0);
+ end;
+ end;
+end;
+
+procedure DestroyFrame;
+begin
+ if FrameId>=0 then
+ begin
+ UnhookEvent(colorhook);
+ CallService(MS_CLIST_FRAMES_REMOVEFRAME,FrameId,0);
+ FrameId:=-1;
+ end;
+ DestroyWindow(FrameWnd);
+ FrameWnd:=0;
+end;
diff --git a/plugins/mRadio/i_hotkey.inc b/plugins/mRadio/i_hotkey.inc
new file mode 100644
index 0000000000..04271fe824
--- /dev/null
+++ b/plugins/mRadio/i_hotkey.inc
@@ -0,0 +1,61 @@
+{}
+const
+ hRadioHotkey:THANDLE=0;
+const
+ MS_RADIO_HOTKEY:PAnsiChar = 'mRadio/Hotkey';
+
+ HKN_PLAYPAUSE :PAnsiChar = 'mRadio_PlayPause';
+ HKN_STOP :PAnsiChar = 'mRadio_Stop';
+ HKN_MUTE :PAnsiChar = 'mRadio_Mute';
+
+ DefRadioKeyMute = ((HOTKEYF_EXT or HOTKEYF_SHIFT) shl 8) or ORD('M');
+ DefRadioKeyPlay = ((HOTKEYF_EXT or HOTKEYF_SHIFT) shl 8) or ORD('P');
+ DefRadioKeyStop = ((HOTKEYF_EXT or HOTKEYF_SHIFT) shl 8) or ORD('S');
+
+function mRadio_Hotkey(wParam:WPARAM;lParam:LPARAM):int_ptr; cdecl;
+begin
+ if lParam=MRC_MUTE then
+ result:=Service_RadioMute(0,0)
+ else
+ result:=CallService(MS_RADIO_COMMAND,lParam,0);
+end;
+
+procedure RegisterHotKey;
+var
+ hkrec:HOTKEYDESC;
+begin
+ hRadioHotkey:=CreateServiceFunction(MS_RADIO_HOTKEY,@mRadio_Hotkey);
+
+ FillChar(hkrec,SizeOf(hkrec),0);
+
+ hkrec.cbSize :=HOTKEYDESC_SIZE_V1;
+ hkrec.pszSection.a :=PluginName;
+ hkrec.pszService :=MS_RADIO_HOTKEY;
+
+ hkrec.DefHotKey :=DefRadioKeyPlay;
+ hkrec.pszName :=HKN_PLAYPAUSE;
+ hkrec.pszDescription.a:='Play/Pause';
+ hkrec.lParam :=MRC_PAUSE;
+ CallService(MS_HOTKEY_REGISTER,0,lparam(@hkrec));
+
+ hkrec.DefHotKey :=DefRadioKeyStop;
+ hkrec.pszName :=HKN_STOP;
+ hkrec.pszDescription.a:='Stop';
+ hkrec.lParam :=MRC_STOP;
+ CallService(MS_HOTKEY_REGISTER,0,lparam(@hkrec));
+
+ hkrec.DefHotKey :=DefRadioKeyMute;
+ hkrec.pszName :=HKN_MUTE;
+ hkrec.pszDescription.a:='Mute';
+ hkrec.lParam :=MRC_MUTE;
+ CallService(MS_HOTKEY_REGISTER,0,lparam(@hkrec));
+end;
+
+procedure UnregisterHotKey;
+begin
+ CallService(MS_HOTKEY_UNREGISTER,0,lparam(HKN_PLAYPAUSE));
+ CallService(MS_HOTKEY_UNREGISTER,0,lparam(HKN_STOP));
+ CallService(MS_HOTKEY_UNREGISTER,0,lparam(HKN_MUTE));
+
+ DestroyServiceFunction(hRadioHotkey);
+end;
diff --git a/plugins/mRadio/i_myservice.inc b/plugins/mRadio/i_myservice.inc
new file mode 100644
index 0000000000..b8338a0d81
--- /dev/null
+++ b/plugins/mRadio/i_myservice.inc
@@ -0,0 +1,178 @@
+{My services}
+
+function Service_RadioPlayStop(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ p:PAnsiChar;
+ lnew:bool;
+ hContact:THANDLE;
+ cni:TCONTACTINFO;
+ i:integer;
+begin
+ result:=0;
+ if lParam=0 then
+ begin
+ if wParam=0 then
+ wParam:=GetCListSelContact;
+ p:=GetContactProtoAcc(wParam);
+ if (p=nil) or (StrCmp(p,PluginName)<>0) then
+ exit;
+ hContact:=wParam;
+ end
+ // wParam = station name
+ else
+ begin
+ FillChar(cni,SizeOf(cni),0);
+ cni.cbSize :=sizeof(cni);
+ if lParam=1 then
+ cni.dwFlag:=CNF_DISPLAY
+ else
+ cni.dwFlag:=CNF_DISPLAY or CNF_UNICODE;
+ cni.szProto :=PluginName;
+
+ hContact:=CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ while hContact<>0 do
+ begin
+ p:=PAnsiChar(CallService(MS_PROTO_GETCONTACTBASEPROTO,hContact,0));
+ if (p<>nil) and (StrCmp(p,PluginName)=0) then
+ begin
+ cni.hContact:=hContact;
+ if CallService(MS_CONTACT_GETCONTACTINFO,0,tlparam(@cni))=0 then
+ begin
+ if lParam=1 then
+ i:=StrCmp(pAnsiChar(wParam),cni.retval.szVal.a)
+ else
+ i:=StrCmpW(pWideChar(wParam),cni.retval.szVal.w);
+ mir_free(cni.retval.szVal.w);
+ if i=0 then
+ break;
+ end;
+ end;
+ hContact:=CallService(MS_DB_CONTACT_FINDNEXT,hContact,0);
+ end;
+ end;
+
+ if hContact<>0 then
+ begin
+ result:=1;
+ if PluginStatus=ID_STATUS_OFFLINE then
+ Service_SetStatus(ID_STATUS_ONLINE,0);
+
+ case CallService(MS_RADIO_COMMAND,MRC_STATUS,RD_STATUS_GET) of
+ RD_STATUS_CONNECT: begin //break while connect
+ CallService(MS_RADIO_COMMAND,MRC_STATUS,RD_STATUS_ABORT);
+ exit;
+ end;
+ RD_STATUS_ABORT: exit;
+ end;
+
+ lnew:=ActiveContact<>hContact;
+
+ if ActiveContact<>0 then
+ CallService(MS_RADIO_COMMAND,MRC_STOP,1);
+
+ if lnew then
+ CallService(MS_RADIO_COMMAND,MRC_PLAY,hContact);
+ end;
+end;
+
+function Service_RadioSettings(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ ood:TOPENOPTIONSDIALOG;
+begin
+ result:=0;
+ ood.cbSize:=SizeOf(ood);
+ ood.pszGroup:='Network';
+ ood.pszPage :=PluginName;
+ ood.pszTab :=Translate('Advanced');
+ CallService(MS_OPT_OPENOPTIONS,0,tlparam(@ood));
+end;
+
+function Service_RadioRecord(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+begin
+ if lParam<>0 then lParam:=TLPARAM(-1) else lParam:=0;
+
+ result:=CallService(MS_RADIO_COMMAND,MRC_RECORD,lParam);
+end;
+
+function Service_RadioGetVolume(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+begin
+ result:=gVolume;
+end;
+
+function Service_RadioSetVolume(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+begin
+ result:=Service_RadioGetVolume(0,0);
+ SetSndVol(wParam);
+
+ DBWriteByte(0,PluginName,optVolume,ABS(wParam));
+ if lParam<>2 then // not from Frame
+ if hVolFrmCtrl<>0 then
+ SendMessage(hVolFrmCtrl,TBM_SETPOS,1,ABS(wParam));
+
+ if lParam<>1 then // not from Settings
+ if hVolCtrl<>0 then
+ SendMessage(hVolCtrl,TBM_SETPOS,1,ABS(wParam));
+
+end;
+
+function Service_RadioMute(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+{$IFDEF KOL_MCK}
+ med:TMouseEventData;
+{$ELSE}
+ tmp:pAnsiChar;
+{$ENDIF}
+begin
+ if gVolume=0 then
+ gVolume:=-1
+ else
+ gVolume:=-gVolume;
+ Result:=Service_RadioSetVolume(gVolume,0);
+ if hVolFrmCtrl<>0 then
+ EnableWindow(hVolFrmCtrl,gVolume>=0);
+
+{$IFDEF KOL_MCK}
+ if lParam=0 then // 0 - from Service, not frame
+ begin
+ if btnMute<>nil then
+ begin
+ FillChar(med,SizeOf(med),0);
+ med.Button:=mbLeft;
+ med.StopHandling:=true;
+ btnMute.OnMouseDown(btnMute,med);
+ // btnMute.Click; // without click processing
+ btnMute.OnMouseUp (btnMute,med);
+ btnMute.Invalidate;
+ end
+ end;
+{$ELSE}
+ if hMuteFrmCtrl<>0 then
+ begin
+ if gVolume<0 then
+ tmp:=IcoBtnOff
+ else
+ tmp:=IcoBtnOn;
+ SetButtonIcon(hMuteFrmCtrl,tmp);
+ end;
+{$ENDIF}
+end;
+
+function Service_EqOnOff(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+begin
+ result:=ord(isEQ_OFF=BST_UNCHECKED);
+ case wParam of
+ 0: begin
+ if isEQ_OFF=BST_UNCHECKED then
+ EQ_OFF
+ else
+ EQ_ON;
+ end;
+ 1: EQ_ON;
+ else
+ EQ_OFF;
+ end;
+ if eq[0].wnd<>0 then // if options opened
+ begin
+ CheckDlgButton(GetParent(eq[0].wnd),IDC_EQOFF,isEQ_OFF);
+ end;
+end;
diff --git a/plugins/mRadio/i_optdlg.inc b/plugins/mRadio/i_optdlg.inc
new file mode 100644
index 0000000000..e047fe20a1
--- /dev/null
+++ b/plugins/mRadio/i_optdlg.inc
@@ -0,0 +1,962 @@
+{Options dialog}
+const
+ OldEQPreset:integer=-1;
+const
+ optOldPreset:pAnsiChar='oldpreset';
+ optPresets :pAnsiChar='numpreset';
+ optPreset :pAnsiChar='preset_';
+ optPreDescr :pAnsiChar='predescr_';
+
+const
+ PresetMax = 17;
+
+procedure InitPresets;
+type
+ tP = array [0..9] of AnsiChar;
+begin
+ StrDupW(Presets[00].name,'Acoustic' ); tP(Presets[00].preset):=#$01#$02#$00#$00#$00#$00#$00#$01#$01#$03;
+ StrDupW(Presets[01].name,'Blues' ); tP(Presets[01].preset):=#$01#$02#$01#$00#$00#$00#$00#$00#$00#$FE;
+ StrDupW(Presets[02].name,'Classical'); tP(Presets[02].preset):=#$01#$04#$01#$00#$00#$00#$00#$00#$01#$01;
+ StrDupW(Presets[03].name,'Country' ); tP(Presets[03].preset):=#$00#$00#$01#$01#$00#$00#$01#$01#$01#$01;
+ StrDupW(Presets[04].name,'Dance' ); tP(Presets[04].preset):=#$04#$03#$02#$01#$FF#$FF#$02#$04#$05#$05;
+ StrDupW(Presets[05].name,'Folk' ); tP(Presets[05].preset):=#$FF#$00#$00#$01#$01#$00#$01#$01#$01#$00;
+ StrDupW(Presets[06].name,'Grunge' ); tP(Presets[06].preset):=#$01#$00#$FF#$00#$00#$02#$01#$FF#$FE#$FD;
+ StrDupW(Presets[07].name,'Jazz' ); tP(Presets[07].preset):=#$00#$01#$02#$03#$03#$01#$01#$03#$04#$05;
+ StrDupW(Presets[08].name,'Metall' ); tP(Presets[08].preset):=#$FE#$00#$00#$00#$00#$03#$02#$04#$06#$05;
+ StrDupW(Presets[09].name,'New Age' ); tP(Presets[09].preset):=#$03#$03#$00#$00#$00#$00#$00#$00#$02#$02;
+ StrDupW(Presets[10].name,'Opera' ); tP(Presets[10].preset):=#$00#$01#$02#$04#$01#$02#$00#$00#$00#$00;
+ StrDupW(Presets[11].name,'Rap' ); tP(Presets[11].preset):=#$00#$04#$04#$00#$00#$00#$01#$04#$05#$07;
+ StrDupW(Presets[12].name,'Reggae' ); tP(Presets[12].preset):=#$03#$00#$FD#$00#$05#$00#$02#$03#$04#$05;
+ StrDupW(Presets[13].name,'Rock' ); tP(Presets[13].preset):=#$00#$02#$04#$00#$00#$00#$02#$04#$05#$06;
+ StrDupW(Presets[14].name,'Speech' ); tP(Presets[14].preset):=#$FE#$02#$00#$00#$00#$00#$FF#$FE#$FD#$FC;
+ StrDupW(Presets[15].name,'Swing' ); tP(Presets[15].preset):=#$FF#$00#$00#$02#$02#$00#$02#$02#$03#$03;
+ StrDupW(Presets[16].name,'Techno' ); tP(Presets[16].preset):=#$05#$08#$FF#$FE#$FD#$FF#$04#$06#$06#$06;
+end;
+
+procedure LoadPresets;
+var
+ num,preset,descr:array [0..63] of AnsiChar;
+ i:integer;
+ p,pd:pAnsiChar;
+begin
+ p :=StrCopyE(preset,optPreset);
+ pd:=StrCopyE(descr ,optPreDescr);
+
+ i:=DBReadByte(0,PluginName,optPresets);
+ if i=0 then
+ begin
+ SetLength(Presets,PresetMax);
+ InitPresets;
+ end
+ else
+ begin
+ OldEQPreset:=integer(DBReadByte(0,PluginName,optOldPreset,byte(-1)));
+ SetLength(Presets,i);
+ for i:=0 to HIGH(Presets) do
+ begin
+ StrCopy(p ,IntToStr(num,i)); DBReadStruct (0,PluginName,preset,@Presets[i].preset,10);
+ StrCopy(pd,num); Presets[i].name:=DBReadUnicode(0,PluginName,descr);
+ end;
+ end;
+end;
+
+procedure SavePresets;
+var
+ num,preset,descr:array [0..63] of AnsiChar;
+ i,j:integer;
+ p,pd:pAnsiChar;
+begin
+ p :=StrCopyE(preset,optPreset);
+ pd:=StrCopyE(descr ,optPreDescr);
+ j:=DBReadByte(0,PluginName,optPresets);
+ for i:=0 to HIGH(Presets) do
+ begin
+ StrCopy(p ,IntToStr(num,i)); DBWriteStruct (0,PluginName,preset,@Presets[i].preset,10);
+ StrCopy(pd,num); DBWriteUnicode(0,PluginName,descr ,Presets[i].name);
+ end;
+ while j>Length(Presets) do
+ begin
+ dec(j);
+ StrCopy(p ,IntToStr(num,j)); DBDeleteSetting(0,PluginName,preset);
+ StrCopy(pd,num); DBDeleteSetting(0,PluginName,descr);
+ end;
+ DBWriteByte(0,PluginName,optOldPreset,OldEQPreset);
+ DBWriteByte(0,PluginName,optPresets ,Length(Presets));
+end;
+
+procedure FreePresets;
+var
+ i:integer;
+begin
+ for i:=0 to HIGH(Presets) do
+ mFreeMem(Presets[i].name);
+end;
+
+function ImportOneStation(group:PAnsiChar;section:pointer):int;
+var
+ p:pWideChar;
+ pc:pAnsiChar;
+begin
+ result:=0;
+ pc:=GetParamSectionStr(section,'URL');
+ if pc<>nil then
+ begin
+ result:=CallService(MS_DB_CONTACT_ADD,0,0);
+ if result<>0 then
+ begin
+ CallService(MS_PROTO_ADDTOCONTACT,result,lparam(PluginName));
+ DBWriteString(result,PluginName,optStationURL,pc);
+ DBWriteString(result,PluginName,optFirstName ,pc);
+
+ pc:=GetParamSectionStr(section,optBitrate,'0');
+ DBWriteString(result,PluginName,optBitrate,pc);
+ DBWriteWord (result,PluginName,optAge ,StrToInt(pc));
+
+ pc:=GetParamSectionStr(section,'Name',GetSectionName(section));
+ DBWriteString(result,strCList ,optMyHandle,pc);
+ DBWriteString(result,PluginName,optNick ,pc);
+
+ pc:=GetParamSectionStr(section,optGenre,'unknown');
+ DBWriteString(result,PluginName,optGenre ,pc);
+ DBWriteString(result,PluginName,optLastName,pc);
+
+ SetStatus(result,ID_STATUS_OFFLINE);
+
+ if group=nil then
+ group:=GetParamSectionStr(section,optGroup);
+
+ AnsiToWide(group,p,MirandaCP);
+ CreateGroupW(p,result);
+ mFreeMem(p);
+ CallService(MS_IGNORE_IGNORE,result,IGNOREEVENT_ALL);
+ end;
+ end;
+end;
+
+function ImportAll(wParam:WPARAM;lParam:LPARAM):int; cdecl;
+var
+ dst:array [0..MAX_PATH-1] of AnsiChar;
+ pc:pAnsiChar;
+ lstorage,section,list:pointer;
+begin
+ result:=0;
+ if lParam<>0 then
+ StrCopy(dst,PAnsiChar(lParam));
+ if (lParam<>0) or ShowDlg(dst,'radio.ini',nil,true) then
+ begin
+ lstorage:=OpenStorage(dst);
+ if lstorage<>nil then
+ begin
+ list:=GetSectionList(lstorage);
+
+ pc:=list;
+ while pc^<>#0 do
+ begin
+ section:=SearchSection(lstorage,pc);
+ if ImportOneStation(pAnsiChar(wParam),section)<>0 then inc(result);
+ while pc^<>#0 do inc(pc);
+ inc(pc);
+ end;
+
+ FreeSectionList(list);
+
+ CloseStorage(lstorage);
+ end;
+ end;
+end;
+
+procedure ExportRadioContact(num:integer;fname:PAnsiChar;hContact:THANDLE);
+var
+ pc:pAnsiChar;
+ section:array [0..15] of AnsiChar;
+begin
+ IntToStr(section,num);
+ pc:=DBReadString(hContact,strCList,optMyHandle);
+ WritePrivateProfileStringA(section,'Name',pc,fname);
+ mFreeMem(pc);
+
+ pc:=DBReadString(hContact,PluginName,optStationURL);
+ WritePrivateProfileStringA(section,'URL',pc,fname);
+ mFreeMem(pc);
+
+ pc:=DBReadString(hContact,PluginName,optGenre);
+ if pc<>nil then
+ begin
+ WritePrivateProfileStringA(section,optGenre,pc,fname);
+ mFreeMem(pc);
+ end;
+
+ pc:=DBReadString(hContact,PluginName,optBitrate);
+ if pc<>nil then
+ begin
+ WritePrivateProfileStringA(section,optBitrate,pc,fname);
+ mFreeMem(pc);
+ end;
+
+ pc:=DBReadString(hContact,strCList,optGroup);
+ if pc<>nil then
+ begin
+ WritePrivateProfileStringA(section,optGroup,pc,fname);
+ mFreeMem(pc);
+ end;
+end;
+
+function ExportAll(wParam:WPARAM;lParam:LPARAM):int; cdecl;
+var
+ dst:array [0..MAX_PATH-1] of AnsiChar;
+ hContact:THANDLE;
+begin
+ result:=0;
+ if lParam<>0 then
+ StrCopy(dst,PAnsiChar(lParam));
+ if (lParam<>0) or ShowDlg(dst,'radio.ini',nil,false) then
+ begin
+ if (wParam<>0) and (CallService(MS_DB_CONTACT_IS,wParam,0)<>0) then
+ begin
+ result:=1;
+ ExportRadioContact(result,dst,wParam)
+ end
+ else
+ begin
+ hContact:=CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ while hContact<>0 do
+ begin
+ if StrCmp(PAnsiChar(CallService(MS_PROTO_GETCONTACTBASEPROTO,hContact,0)),
+ PluginName)=0 then
+ begin
+ inc(result);
+ ExportRadioContact(result,dst,hContact);
+ end;
+ hContact:=CallService(MS_DB_CONTACT_FINDNEXT,hContact,0);
+ end;
+ end;
+ end;
+end;
+
+function MakeFilter(dst,descr,full,filter:PWideChar;show:bool=true):pWideChar;
+var
+ p:PWideChar;
+begin
+ if full<>nil then
+ begin
+ p:=StrEndW(full);
+ p^:=';';
+ StrCopyW(p+1,filter);
+ end;
+
+ dst:=StrCopyEW(dst,TranslateW(descr));
+ if show then
+ begin
+ dst^ :=' ';
+ (dst+1)^:='(';
+ dst:=StrCopyEW(dst+2,filter);
+ dst^:=')';
+ inc(dst);
+ dst^:=#0;
+ end;
+ inc(dst);
+ result:=StrCopyEW(dst,filter)+1;
+end;
+
+function ConstructFilter:pointer;
+var
+ pc:pWideChar;
+ ph:PDWord;
+ Info:PBASS_PLUGININFO;
+ i:integer;
+ full:array [0..511] of WideChar;
+ tmpbuf1,tmpbuf2:array [0..127] of WideChar;
+begin
+ mGetMem(pc,4096);
+// FillChar(pc^,4096,0);
+ result:=pc;
+ full[0]:=#0;
+ pc:=MakeFilter(pc,'All files' ,nil ,'*.*',false);
+ pc:=MakeFilter(pc,'Playlist files',full,'*.pls;*.m3u;*.m3u8;*.asx');
+ pc:=MakeFilter(pc,'BASS built-in' ,full,'*.mp3;*.mp2;*.mp1;*.ogg;*.wav;*.aif');
+
+ ph:=pointer(BASS_PluginGetInfo(0));
+ if ph<>nil then
+ begin
+ while ph^<>0 do
+ begin
+ Info:=BASS_PluginGetInfo(ph^);
+ for i:=0 to Info^.formatc-1 do
+//!! need to translate Ansi -> wide
+ with Info^.Formats[i] do
+ begin
+ pc:=MakeFilter(pc,FastAnsiToWideBuf(name,tmpbuf1),full,FastAnsiToWideBuf(exts,tmpbuf2));
+ end;
+ inc(ph);
+ end;
+ end;
+ pc:=MakeFilter(pc,'All supported formats',nil,full,false);
+ pc^:=#0;
+end;
+
+procedure SetButtonIcons(Dialog:HWND);
+var
+ ti:TTOOLINFOW;
+ hwndTooltip:HWND;
+begin
+ hwndTooltip:=CreateWindowW(TOOLTIPS_CLASS,nil,TTS_ALWAYSTIP,
+ integer(CW_USEDEFAULT),integer(CW_USEDEFAULT),
+ integer(CW_USEDEFAULT),integer(CW_USEDEFAULT),
+ Dialog,0,hInstance,nil);
+
+ FillChar(ti,SizeOf(ti),0);
+ ti.cbSize :=sizeof(TOOLINFO);
+ ti.uFlags :=TTF_IDISHWND or TTF_SUBCLASS;
+ ti.hwnd :=Dialog;
+ ti.hinst :=hInstance;
+
+ ti.uId :=GetDlgItem(Dialog,IDC_EQ_ADD);
+ ti.lpszText:=TranslateW('Add');
+
+ SetButtonIcon(ti.uId,IcoBtnAdd);
+
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+
+ ti.uId :=GetDlgItem(Dialog,IDC_EQ_DEL);
+ ti.lpszText:=TranslateW('Delete');
+ SetButtonIcon(ti.uId,IcoBtnDel);
+ SendMessageW(hwndTooltip,TTM_ADDTOOLW,0,lparam(@ti));
+end;
+
+function DlgProcOpt(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+const
+ DlgInit:integer=1;
+var
+ buf:array [0..MAX_PATH-1] of WideChar;
+ psr:TPROTOSEARCHRESULT;
+ dst:pWideChar;
+ i:dword;
+// info:BASS_CHANNELINFO;
+ vhi:TVARHELPINFO;
+ wnd:HWND;
+ p:pWideChar;
+begin
+ result:=0;
+ case hMessage of
+ WM_DESTROY: begin
+ DBWriteByte(0,PluginName,optLoop ,doLoop);
+ DBWriteByte(0,PluginName,optShuffle ,doShuffle);
+ DBWriteByte(0,PluginName,optContRec ,doContRec);
+ DBWriteByte(0,PluginName,optPlayFirst,PlayFirst);
+ DBWriteByte(0,PluginName,optConnect ,AuConnect);
+ DBWriteByte(0,PluginName,optAutoMute ,AuMute);
+ DBWriteByte(0,PluginName,optOffline ,AsOffline);
+
+ DBWriteUnicode(0,PluginName,optStatusTmpl,StatusTmpl);
+ end;
+
+ WM_INITDIALOG: begin
+ DlgInit:=1;
+ TranslateDialogDefault(Dialog);
+
+ wnd:=GetDlgItem(Dialog,IDC_HLP_VARS);
+ if isVarsInstalled then
+ begin
+ SendMessage(wnd,BM_SETIMAGE,IMAGE_ICON,
+ CallService(MS_VARS_GETSKINITEM,0,VSI_HELPICON));
+ end
+ else
+ ShowWindow(wnd,SW_HIDE);
+
+ if recpath<>nil then
+ p:=recpath
+ else
+ begin
+ buf[0]:=#0;
+ p:=@buf;
+ end;
+ SetDlgItemTextW(Dialog,IDC_ED_RECPATH,p);
+
+ CheckDlgButton(Dialog,IDC_LOOP ,doLoop);
+ CheckDlgButton(Dialog,IDC_SHUFFLE ,doShuffle);
+ CheckDlgButton(Dialog,IDC_CONTREC ,doContRec);
+ CheckDlgButton(Dialog,IDC_PLAYFIRST,PlayFirst);
+ CheckDlgButton(Dialog,IDC_CONNECT ,AuConnect);
+ CheckDlgButton(Dialog,IDC_AUTOMUTE ,AuMute);
+ CheckDlgButton(Dialog,IDC_OFFLINE ,AsOffline);
+
+ SetDlgItemTextW(Dialog,IDC_STATUS,StatusTmpl);
+
+ DlgInit:=0;
+ end;
+
+ WM_COMMAND: begin
+ case wParam shr 16 of
+ EN_CHANGE: begin
+ if DlgInit=0 then
+ case loword(wParam) of
+ IDC_STATION,IDC_STATIONURL,IDC_GENRE,IDC_BITRATE: ;
+ else
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+ end;
+
+ BN_CLICKED: begin
+ case loword(wParam) of
+
+ IDC_IMPORT: begin
+ ImportAll(0,0);
+ end;
+
+ IDC_EXPORT: begin
+ ExportAll(0,0);
+ end;
+
+ IDC_HLP_VARS: begin
+ FillChar(vhi,SizeOf(vhi),0);
+ with vhi do
+ begin
+ cbSize :=SizeOf(vhi);
+ flags :=VHF_FULLDLG or VHF_SETLASTSUBJECT;
+ hwndCtrl :=GetDlgItem(Dialog,IDC_STATUS);
+ szSubjectDesc:='test your variables';
+ end;
+ CallService(MS_VARS_SHOWHELPEX,Dialog,tlparam(@vhi));
+ end;
+
+ IDC_BN_URLPATH: begin
+
+ dst:=ConstructFilter;
+ if ShowDlgW(@buf,nil,dst) then
+ SetDlgItemTextW(Dialog,IDC_STATIONURL,@buf);
+ mFreeMem(dst);
+ end;
+
+ IDC_BN_RECPATH: begin
+ dst:=nil;
+ if SelectDirectory(pWideChar(nil),dst,Dialog) then
+ begin
+ CallService(MS_UTILS_PATHTORELATIVEW,twparam(dst),tlparam(@buf));
+ SetDlgItemTextW(Dialog,IDC_ED_RECPATH,pWideChar(@buf));
+ mFreeMem(dst);
+ end;
+ end;
+
+ IDC_LOOP,IDC_SHUFFLE,IDC_CONTREC,IDC_CONNECT,IDC_OFFLINE,IDC_AUTOMUTE:
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+
+ IDC_ADD_LIST,IDC_ADD_INI: begin
+ if GetWindowTextLength(GetDlgItem(Dialog,IDC_STATIONURL))>0 then
+ begin
+ FillChar(psr,SizeOf(psr),0);
+ with psr do
+ begin
+ if loword(wParam)=IDC_ADD_LIST then
+ begin
+ GetDlgItemTextW(Dialog,IDC_STATIONURL,@buf,MAX_PATH);
+ StrDupW(firstname.w,@buf);
+ buf[0]:=#0;
+
+ GetDlgItemTextW(Dialog,IDC_STATION,@buf,MAX_PATH);
+ if buf[0]<>#0 then
+ StrDupW(nick.w,@buf)
+ else
+ StrDupW(nick.w,firstname.w);
+ buf[0]:=#0;
+
+ GetDlgItemTextW(Dialog,IDC_GENRE,@buf,MAX_PATH);
+ StrDupW(lastname.w,@buf);
+ buf[0]:=#0;
+
+ GetDlgItemTextW(Dialog,IDC_BITRATE,@buf,MAX_PATH);
+ StrDupW(email.w,@buf);
+
+ i:=Service_AddToList(0,tlparam(@psr));
+{
+ GetDlgItemTextW(Dialog,IDC_STATION,@buf,SizeOf(buf));
+ DBWriteUnicode(i,strCList,optMyHandle,@buf);
+
+ GetDlgItemTextW(Dialog,IDC_STATIONURL,@buf,SizeOf(buf));
+ DBWriteUnicode(i,PluginName,optStationURL,@buf);
+}
+ // "changing" station group
+ dst:=GetNewGroupName(Dialog);
+ if dst<>nil then
+ DBWriteUnicode(i,strCList,optGroup,dst)
+ else
+ DBDeleteSetting(i,strCList,optGroup);
+
+ end
+ else if loword(wParam)=IDC_ADD_INI then
+ begin
+ GetDlgItemTextA(Dialog,IDC_STATIONURL,PAnsiChar(@buf),SizeOf(buf));
+ StrDup(firstname.a,PAnsiChar(@buf));
+ PAnsiChar(@buf)^:=#0;
+
+ GetDlgItemTextA(Dialog,IDC_STATION,PAnsiChar(@buf),SizeOf(buf));
+ if PAnsiChar(@buf)^<>#0 then
+ StrDup(nick.a,@buf)
+ else
+ StrDup(nick.a,firstname.a);
+ PAnsiChar(@buf)^:=#0;
+
+ GetDlgItemTextA(Dialog,IDC_GENRE,PAnsiChar(@buf),SizeOf(buf));
+ StrDup(lastname.a,@buf);
+ PAnsiChar(@buf)^:=#0;
+
+ GetDlgItemTextA(Dialog,IDC_BITRATE,PAnsiChar(@buf),SizeOf(buf));
+ StrDup(email.a,@buf);
+
+ if WritePrivateProfileStringA(firstname.a,'URL',firstname.a,storage) then
+ begin
+ WritePrivateProfileStringA(firstname.a,'Name' ,nick.a ,storage);
+ WritePrivateProfileStringA(firstname.a,optGenre ,lastname.a ,storage);
+ WritePrivateProfileStringA(firstname.a,optBitrate,email.a ,storage);
+ end
+ else
+ begin
+ WritePrivateProfileStringA(firstname.a,'URL' ,firstname.a,storagep);
+ WritePrivateProfileStringA(firstname.a,'Name' ,nick.a ,storagep);
+ WritePrivateProfileStringA(firstname.a,optGenre ,lastname.a ,storagep);
+ WritePrivateProfileStringA(firstname.a,optBitrate,email.a ,storagep);
+ end;
+
+ end;
+ mFreeMem(nick);
+ mFreeMem(firstname);
+ mFreeMem(lastname);
+ mFreeMem(email);
+ end;
+ end;
+ end;
+
+ end;
+ end;
+
+ end;
+ end;
+
+ WM_NOTIFY: begin
+ if integer(PNMHdr(lParam)^.code)=PSN_APPLY then
+ begin
+
+ doShuffle:=IsDlgButtonChecked(Dialog,IDC_SHUFFLE);
+ doContRec:=IsDlgButtonChecked(Dialog,IDC_CONTREC);
+ PlayFirst:=IsDlgButtonChecked(Dialog,IDC_PLAYFIRST);
+ AuConnect:=IsDlgButtonChecked(Dialog,IDC_CONNECT);
+ AuMute :=IsDlgButtonChecked(Dialog,IDC_AUTOMUTE);
+ AsOffline:=IsDlgButtonChecked(Dialog,IDC_OFFLINE);
+
+ mFreeMem(StatusTmpl);
+ StatusTmpl:=GetDlgText(Dialog,IDC_STATUS);
+
+ doLoop:=IsDlgButtonChecked(Dialog,IDC_LOOP);
+ if chan<>0 then
+ begin
+{
+ BASS_ChannelGetInfo(chan,info);
+ if doLoop<>BST_UNCHECKED then
+ info.flags:=info.flags or BASS_SAMPLE_LOOP
+ else
+ info.flags:=info.flags and not BASS_SAMPLE_LOOP;
+}
+ BASS_ChannelFlags(chan,ord(doLoop<>BST_UNCHECKED),BASS_SAMPLE_LOOP);
+ end;
+
+ mFreeMem(recpath);
+ buf[0]:=#0;
+ GetDlgItemTextW(Dialog,IDC_ED_RECPATH,@buf,SizeOf(buf) div SizeOf(WideChar));
+ if buf[0]<>#0 then
+ begin
+ mGetMem(recpath,MAX_PATH*SizeOf(WideChar));
+ recpath^:=#0;
+ CallService(MS_UTILS_PATHTORELATIVEW,twparam(@buf),tlparam(recpath));
+ end;
+ DBWriteUnicode(0,PluginName,optRecPath,recpath);
+ end;
+ end;
+
+// else
+// result:=DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+end;
+
+function DlgProcOptTech(Dialog:HWnd;hMessage:uint;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+const
+ DlgInit:integer=1;
+var
+ i:integer;
+ hEAXCombo:THANDLE;
+ ltmp:longbool;
+// info:BASS_CHANNELINFO;
+ buf1:array [0..4] of AnsiChar;
+ wnd:HWND;
+ buf:array [0..MAX_PATH-1] of WideChar;
+ dst:pWideChar;
+begin
+ result:=0;
+ case hMessage of
+ WM_DESTROY: begin
+ hVolCtrl:=0;
+
+ buf1[0]:='E';
+ buf1[1]:='Q';
+ buf1[2]:='_';
+ buf1[4]:=#0;
+ for i:=0 to 9 do
+ begin
+ buf1[3]:=AnsiChar(ORD('0')+i);
+ DBWriteByte(0,PluginName,buf1,trunc(eq[i].param.fGain)+15);
+ eq[i].wnd:=0;
+ end;
+ DBWriteByte(0,PluginName,optEQ_OFF ,isEQ_OFF);
+ end;
+
+ WM_INITDIALOG: begin
+ DlgInit:=1;
+ TranslateDialogDefault(Dialog);
+
+ SetButtonIcons(Dialog);
+ hVolCtrl:=GetDlgItem(Dialog,IDC_VOLUME);
+ SendMessage(hVolCtrl,TBM_SETRANGE,0,(100 shl 16)+0);
+ SendMessage(hVolCtrl,TBM_SETPOS,1,ABS(gVolume));
+
+ SetDlgItemInt(Dialog,IDC_BUFFER ,sBuffer ,false);
+ SetDlgItemInt(Dialog,IDC_PREBUF ,sPreBuf ,false);
+ SetDlgItemInt(Dialog,IDC_TIMEOUT,sTimeout,false);
+ SetDlgItemInt(Dialog,IDC_TRIES ,NumTries,false);
+
+ CheckDlgButton(Dialog,IDC_MONO,ForcedMono);
+
+ hEAXCombo:=GetDlgItem(Dialog,IDC_EAXTYPE);
+ for i:=0 to EAX_ENVIRONMENT_COUNT do
+ SendMessageW(hEAXCombo,CB_ADDSTRING,0,tlparam(TranslateW(EAXItems[i].name)));
+ SendMessage(hEAXCombo,CB_SETCURSEL,DBReadByte(0,PluginName,optEAXType,0),0);
+
+ wnd:=GetDlgItem(Dialog,IDC_PRESET);
+ for i:=0 to HIGH(Presets) do
+ SendMessageW(wnd,CB_ADDSTRING,0,tlparam(Presets[i].name));
+ SendMessage(wnd,CB_SETCURSEL,OldEQPreset,0);
+
+ for i:=0 to 9 do
+ begin
+ eq[i].wnd:=GetDlgItem(Dialog,IDC_EQ00+i);
+ SendMessage(eq[i].wnd,TBM_SETRANGE,1,(16 shl 16)-15);
+ SendMessage(eq[i].wnd,TBM_SETTIC,0,0);
+ SendMessage(eq[i].wnd,TBM_SETPOS,1,-trunc(eq[i].param.fGain));
+ SendDlgItemMessageA(Dialog,IDC_0+i,WM_SETTEXT,0,tlparam(eq[i].text));
+ EnableWindow(eq[i].wnd,isEQ_OFF=BST_UNCHECKED);
+ end;
+
+ CheckDlgButton(Dialog,IDC_EQOFF,isEQ_OFF);
+
+ dst:=DBReadUnicode(0,PluginName,optBASSPath,nil);
+ SetDlgItemTextW(Dialog,IDC_BASSPATH,dst);
+ mFreeMem(dst);
+
+ DlgInit:=0;
+ end;
+
+ WM_COMMAND: begin
+ case wParam shr 16 of
+ EN_CHANGE: begin
+ if DlgInit=0 then
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ end;
+
+ BN_CLICKED: begin
+ case loword(wParam) of
+
+ IDC_BASSPTHBTN: begin
+ dst:=nil;
+ if SelectDirectory(pWideChar(nil),dst,Dialog) then
+ begin
+ CallService(MS_UTILS_PATHTORELATIVEW,twparam(dst),tlparam(@buf));
+ SetDlgItemTextW(Dialog,IDC_BASSPATH,pWideChar(@buf));
+ mFreeMem(dst);
+ end;
+ end;
+
+ IDC_EQOFF: begin
+ isEQ_OFF:=IsDlgButtonChecked(Dialog,IDC_EQOFF);
+ if isEQ_OFF=BST_UNCHECKED then
+ EQ_ON
+ else
+ EQ_OFF;
+
+ for i:=0 to 9 do
+ EnableWindow(eq[i].wnd,isEQ_OFF=BST_UNCHECKED);
+ end;
+
+ IDC_ZERO: begin
+ OldEQPreset:=-1;
+ for i:=0 to 9 do
+ begin
+ eq[i].param.fGain:=0;
+ SendMessage(eq[i].wnd,TBM_SETPOS,1,0);
+ if (chan<>0) and (isEQ_OFF=BST_UNCHECKED) then
+ BASS_FXSetParameters(eq[i].fx,@eq[i].param);
+ end;
+ end;
+
+ IDC_EQ_ADD: begin
+ SetLength(Presets,Length(Presets)+1);
+
+ for i:=0 to 9 do
+ Presets[HIGH(Presets)].preset[i]:=-SendMessage(eq[i].wnd,TBM_GETPOS,0,0);
+
+ wnd:=GetDlgItem(Dialog,IDC_PRESET);
+ Presets[HIGH(Presets)].name:=GetDlgText(wnd,false);
+ if Presets[HIGH(Presets)].name=nil then
+ StrDupW(Presets[HIGH(Presets)].name,'New');
+ OldEQPreset:=SendMessage(wnd,CB_SETCURSEL,
+ SendMessageW(wnd,CB_ADDSTRING,0,tlparam(Presets[HIGH(Presets)].name)),0);
+ end;
+
+ IDC_EQ_DEL: begin
+ wnd:=GetDlgItem(Dialog,IDC_PRESET);
+ i:=SendMessage(wnd,CB_GETCURSEL,0,0);
+ if (i>=0) and (i<=HIGH(Presets)) then
+ begin
+ SendMessage(wnd,CB_DELETESTRING,i,0);
+ mFreeMem(Presets[i].name);
+ move(Presets[i+1],Presets[i],(HIGH(Presets)-i)*SizeOf(tPreset));
+ SetLength(Presets,Length(Presets)-1);
+ OldEQPreset:=-1;
+ SendMessage(wnd,CB_SETCURSEL,-1,0);
+ end;
+ end;
+
+ end;
+ end;
+
+ CBN_SELCHANGE: begin
+ SendMessage(GetParent(Dialog),PSM_CHANGED,0,0);
+ case loword(wParam) of
+ IDC_PRESET: begin
+ OldEQPreset:=SendDlgItemMessage(Dialog,IDC_PRESET,CB_GETCURSEL,0,0);
+ for i:=0 to 9 do
+ begin
+ SendMessage(eq[i].wnd,TBM_SETPOS,1,-Presets[OldEQPreset].preset[i]);
+ eq[i].param.fGain:=Presets[OldEQPreset].preset[i];
+ if (chan<>0) and (isEQ_OFF=BST_UNCHECKED) then
+ BASS_FXSetParameters(eq[i].fx,@eq[i].param);
+ end;
+ end;
+ IDC_EAXTYPE: begin
+{
+ i:=SendDlgItemMessage(Dialog,IDC_EAXTYPE,CB_GETCURSEL,0,0);
+ DBWriteByte(0,PluginName,optEAXType,i);
+ if i=0 then
+ BASS_SetEAXParameters(-1,0,-1,-1)
+ else
+ BASS_SetEAXPreset(EAXItems[i].code);
+}
+ end;
+ end;
+ end;
+ end;
+ end;
+
+ WM_VSCROLL: begin
+ for i:=0 to 9 do
+ begin
+ if HWND(lParam)=eq[i].wnd then
+ begin
+ eq[i].param.fGain:=-SendMessage(lParam,TBM_GETPOS,0,0);
+ if (chan<>0) and (isEQ_OFF=BST_UNCHECKED) then
+ BASS_FXSetParameters(eq[i].fx,@eq[i].param);
+ OldEQPreset:=-1;
+ break;
+ end;
+ end;
+ end;
+
+ WM_HSCROLL: begin
+ Service_RadioSetVolume(SendMessage(lParam,TBM_GETPOS,0,0),1)
+ end;
+
+ WM_NOTIFY: begin
+ if integer(PNMHdr(lParam)^.code)=PSN_APPLY then
+ begin
+ SavePresets;
+
+//!! bass path saving here
+ dst:=GetDlgText(Dialog,IDC_BASSPATH);
+ DBWriteUnicode(0,PluginName,optBASSPath,dst);
+ mFreeMem(dst);
+
+ ForcedMono:=IsDlgButtonChecked(Dialog,IDC_MONO);
+ DBWriteByte(0,PluginName,optForcedMono,ForcedMono);
+
+ i:=SendDlgItemMessage(Dialog,IDC_EAXTYPE,CB_GETCURSEL,0,0);
+ DBWriteByte(0,PluginName,optEAXType,i);
+ if i=0 then
+ BASS_SetEAXParameters(-1,0,-1,-1)
+ else
+ BASS_SetEAXPreset(EAXItems[i].code);
+
+ NumTries:=GetDlgItemInt(Dialog,IDC_TRIES,ltmp,false);
+ if NumTries<1 then NumTries:=1;
+ DBWriteByte(0,PluginName,optNumTries,NumTries);
+
+ i:=GetDlgItemInt(Dialog,IDC_PREBUF,ltmp,false);
+ if i>100 then i:=100;
+ if cardinal(i)<>sPreBuf then
+ begin
+ sPreBuf:=i;
+ BASS_SetConfig(BASS_CONFIG_NET_PREBUF,i);
+ DBWriteWord(0,PluginName,optPreBuf,sPreBuf);
+ end;
+
+ i:=GetDlgItemInt(Dialog,IDC_BUFFER,ltmp,false);
+ if i>20000 then i:=20000;
+ if cardinal(i)<>sBuffer then
+ begin
+ sBuffer:=i;
+ BASS_SetConfig(BASS_CONFIG_NET_BUFFER,i);
+ DBWriteWord(0,PluginName,optBuffer,sBuffer);
+ end;
+
+ i:=GetDlgItemInt(Dialog,IDC_TIMEOUT,ltmp,false);
+ if i>30000 then i:=30000;
+ if cardinal(i)<>sTimeout then
+ begin
+ sTimeout:=i;
+ BASS_SetConfig(BASS_CONFIG_NET_TIMEOUT,i);
+ DBWriteWord(0,PluginName,optTimeout,sTimeout);
+ end;
+
+ end;
+ end;
+
+// else
+// result:=DefWindowProc(Dialog,hMessage,wParam,lParam);
+ end;
+end;
+
+function OnOptInitialise(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ odp:TOPTIONSDIALOGPAGE;
+begin
+ FillChar(odp,SizeOf(odp),0);
+ odp.cbSize :=SizeOf(odp);
+ odp.flags :=ODPF_BOLDGROUPS;
+ odp.Position :=900003000;
+ odp.hInstance :=hInstance;
+ odp.szGroup.a :='Network';
+ odp.szTitle.a :=PluginName;
+
+ odp.pszTemplate:=MAKEINTRESOURCEA(IDD_SETTING);
+ odp.pfnDlgProc :=@DlgProcOpt;
+ odp.szTab.a :='Common';
+ CallService(MS_OPT_ADDPAGE,wParam,tlparam(@odp));
+
+ odp.pszTemplate:=MAKEINTRESOURCEA(IDD_SETTING_TECH);
+ odp.pfnDlgProc :=@DlgProcOptTech;//!!
+ odp.szTab.a :=Translate('Advanced');
+ CallService(MS_OPT_ADDPAGE,wParam,tlparam(@odp));
+
+ result:=0;
+end;
+
+// checking proto in several places for speed, not size economy
+function OnSettingsChanged(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ buf:array [0..MAX_PATH-1] of AnsiChar;
+ i:integer;
+ pc:PAnsiChar;
+begin
+ result:=0;
+
+ with PDBCONTACTWRITESETTING(lParam)^ do
+ begin
+
+ if AuMute<>BST_UNCHECKED then
+ begin
+ if StrCmp(szModule,'Skin')=0 then
+ begin
+ if StrCmp(szSetting,'UseSound')=0 then
+ begin
+ // Mute
+ if value.bVal=0 then
+ begin
+ if gVolume>=0 then
+ Service_RadioMute(0,0);
+ end
+ // Unmute
+ else
+ begin
+ if gVolume<0 then
+ Service_RadioMute(0,0);
+ end;
+ end;
+
+ exit;
+ end
+ end;
+
+ // works only if called AFTER changes
+ if StrCmp(szModule,strCList)=0 then
+ begin
+ if StrCmp(szSetting,optMyHandle)=0 then
+ begin
+ if value._type=DBVT_DELETED then
+ begin
+ if StrCmp(PAnsiChar(CallService(MS_PROTO_GETCONTACTBASEPROTO,wParam,0)),
+ PluginName)<>0 then exit;
+
+ pc:=DBReadString(wParam,PluginName,optNick);
+ DBWriteString(wParam,strCList,optMyHandle,pc);
+ mFreeMem(pc);
+ end;
+ end;
+ exit;
+ end;
+
+ if StrCmp(szModule,'UserInfo')<>0 then exit;
+
+ if StrCmp(szSetting,optAge)=0 then
+ begin
+ if StrCmp(PAnsiChar(CallService(MS_PROTO_GETCONTACTBASEPROTO,wParam,0)),
+ PluginName)<>0 then exit;
+ if value._type=DBVT_DELETED then
+ i:=DBReadWord(wParam,PluginName,optAge)
+ else
+ i:=value.wVal;
+ DBWriteString(wParam,PluginName,optBitrate,IntToStr(buf,i));
+ exit;
+ end;
+
+ case value._type of
+ DBVT_DELETED,
+ DBVT_ASCIIZ ,
+ DBVT_WCHAR ,
+ DBVT_UTF8 :
+ if StrCmp(PAnsiChar(CallService(MS_PROTO_GETCONTACTBASEPROTO,wParam,0)),
+ PluginName)<>0 then exit;
+ else
+ exit;
+ end;
+
+ case value._type of
+ DBVT_DELETED: pc:=DBReadString(wParam,PluginName,szSetting);
+ DBVT_ASCIIZ : pc:=value.szVal.a;
+ DBVT_WCHAR : WideToAnsi(value.szVal.w,pc,MirandaCP);
+ DBVT_UTF8 : UTF8ToAnsi(value.szVal.a,pc,MirandaCP);
+ end;
+
+ if StrCmp(szSetting,optFirstName)=0 then DBWriteString(wParam,PluginName,optStationURL,pc)
+ else if StrCmp(szSetting,optNick )=0 then DBWriteString(wParam,strCList,optMyHandle,pc)
+ else if StrCmp(szSetting,optLastName )=0 then DBWriteString(wParam,PluginName,optGenre,pc);
+
+ if value._type<>DBVT_ASCIIZ then
+ mFreeMem(pc);
+ end;
+end;
+
+function OnContactDeleted(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+begin
+ result:=0;
+ if ActiveContact<>THANDLE(wParam) then exit;
+ ControlCenter(MRC_STOP,wParam);
+
+{ if StrCmp(PAnsiChar(CallService(MS_PROTO_GETCONTACTBASEPROTO,wParam,0)),
+ PluginName)<>0 then exit;
+}
+end;
diff --git a/plugins/mRadio/i_search.inc b/plugins/mRadio/i_search.inc
new file mode 100644
index 0000000000..d4330e84d1
--- /dev/null
+++ b/plugins/mRadio/i_search.inc
@@ -0,0 +1,444 @@
+{search station code}
+(*
+procedure SetAvatar(hContact:THANDLE);
+var
+ success:boolean;
+ fname:pAnsiChar;
+ url:pWideChar;
+ buf,buf1,buf2:array [0..MAX_PATH-1] of AnsiChar;
+ ext:array [0..15] of AnsiChar;
+ section:pAnsiChar;
+ pc:pAnsiChar;
+ i:integer;
+begin
+ // get url
+ url:=DBReadUnicode(hContact,PluginName,optStationURL);
+ // translate to Ansi
+ FastWideToAnsiBuf(url,buf);
+ mFreeMem(url);
+
+ // search in INI
+ i:=GetFSize(storage);
+ if i=0 then
+ i:=32767;
+ mGetMem(pc,i+1);
+ pc^:=#0;
+ GetPrivateProfileSectionNamesA(pc,i,storage);
+ section:=pc;
+
+ while section^<>#0 do
+ begin
+ GetPrivateProfileStringA(section,'URL','',buf1,SizeOf(buf1),storage);
+ if StrCmp(@buf,@buf1)=0 then
+ break;
+
+ while section^<>#0 do inc(section);
+ inc(section);
+ end;
+ mFreeMem(pc);
+
+ if section^<>#0 then
+ begin
+ // get avatar link
+ buf[0]:=#0;
+ GetPrivateProfileStringA(section,'Avatar','',buf,SizeOf(buf),storage);
+
+ if buf[0]<>#0 then
+ begin
+ // Here we trying to get Avatar chache directory
+ // (create it if needs)
+ // and copy (load) owr avatars there
+ // in : buf = source avatar path
+ // out: fname = destination (file name in cache)
+ // rule for name is?..
+
+
+ if StrPos(buf,'://')=nil then
+ begin
+ fname:=@buf;
+ success:=FileExists(fname);
+ // need to copy this file to avatar cache
+ end
+ else
+ begin
+ // download file
+{
+ GetTempPathA(MAX_PATH,pAnsiChar(@buf1));
+ pc:=extract(pAnsiChar(@buf),true);
+ StrCat(pAnsiChar(@buf1),pc);
+ mFreeMem(pc);
+}
+ //mrAvt
+
+ GetExt(pAnsiChar(@buf),pAnsiChar(@ext));
+ GetTempPathA(MAX_PATH,pAnsiChar(@buf2));
+ GetTempFileNameA(pAnsiChar(@buf2),'mrAvt',GetCurrentTime,pAnsiChar(@buf1));
+ ChangeExt(pAnsiChar(@buf1),PAnsiChar(@ext));
+ //
+ fname:=@buf1;
+ success:=GetFile(pAnsiChar(@buf),fname);
+ end;
+
+ if success then
+ CallService(MS_AV_SETAVATAR,hContact,LPARAM(fname));
+ end;
+ end;
+end;
+*)
+type
+ TMySearchFilter = record
+ lStation,
+ lStationURL,
+ lGenre:PAnsiChar;
+ lBitrate:integer;
+ lBitrateMode:integer;
+ end;
+
+const
+ AckHandle = 427;
+
+// Since mRadio is unicode version only now, translate all strings to Wide
+// coz f*cking jinn will set unicode flag in any cases
+procedure LoadOneStation(section:pointer;const filter:TMySearchFilter);
+var
+ bitrate:integer;
+ l:bool;
+ columns:array [0..3] of TCHAR;
+ csr:CUSTOMSEARCHRESULTS;
+ pc:pAnsiChar;
+ buf:array [0..127] of AnsiChar;
+begin
+ pc:=GetParamSectionStr(section,'URL');
+ if pc<>nil then
+ begin
+ if (filter.lStationURL=nil) or (StrPos(CharLowerA(pc),filter.lStationURL)<>nil) then
+ begin
+ FillChar(csr,SizeOf(csr),0);
+ csr.psr.cbSize:=SizeOf(csr.psr);
+ csr.psr.Flags:=PSR_UNICODE;
+ AnsiToWide(pc,csr.psr.firstname.w,MirandaCP);
+ pc:=GetParamSectionStr(section,optBitrate,'0');
+ bitrate:=StrToInt(pc);
+ if (bitrate<>0) and (filter.lBitrate<>0) then
+ begin
+ if filter.lBitrateMode<0 then l:=bitrate<=filter.lBitrate
+ else if filter.lBitrateMode=0 then l:=bitrate =filter.lBitrate
+ else{if filter.lBitrateMode>0} l:=bitrate>=filter.lBitrate;
+ end
+ else
+ l:=true;
+ if l then
+ begin
+ AnsiToWide(pc,csr.psr.email.w,MirandaCP);
+
+ StrCopy(buf,GetParamSectionStr(section,'Name',GetSectionName(section)),127);
+ if (filter.lStation=nil) or (StrPos(CharLowerA(@buf),filter.lStation)<>nil) then
+ begin
+ AnsiToWide(@buf,csr.psr.nick.w,MirandaCP);
+
+ StrCopy(buf,GetParamSectionStr(section,optGenre,'unknown'),127);
+ if (filter.lGenre=nil) or (StrPos(CharLowerA(@buf),filter.lGenre)<>nil) then
+ AnsiToWide(@buf,csr.psr.lastname.w,MirandaCP)
+ else
+ l:=false;
+ end
+ else
+ l:=false;
+ if l then
+ begin
+ columns[0].w:=csr.psr.nick.w; // Station name
+ columns[1].w:=csr.psr.firstname.w; // URL
+ columns[2].w:=csr.psr.lastname.w; // Genre
+ columns[3].w:=csr.psr.email.w; // Bitrate
+
+ csr.nSize :=SizeOf(csr);
+ csr.nFieldCount:=4;
+ csr.szFields :=@columns;
+ ProtoBroadcastAck(PluginName,0,ACKTYPE_SEARCH,ACKRESULT_SEARCHRESULT,AckHandle,lparam(@csr));
+ end;
+ end;
+ // initial value - nil, so we don't worry
+ mFreeMem(csr.psr.nick);
+ mFreeMem(csr.psr.firstname);
+ mFreeMem(csr.psr.lastname);
+ mFreeMem(csr.psr.email);
+ end;
+ end;
+end;
+
+procedure ProcessSearch(var filter:TMySearchFilter;ini:PAnsiChar);
+var
+ pc:PAnsiChar;
+ csr:CUSTOMSEARCHRESULTS;
+ columns:array [0..3] of TCHAR;
+ lstorage,section,list:pointer;
+begin
+ columns[0].w:='Station Name';
+ columns[1].w:='Station URL';
+ columns[2].w:='Genre';
+ columns[3].w:='Bitrate';
+
+ csr.nSize :=SizeOf(csr);
+ csr.nFieldCount:=4;
+ csr.szFields :=@columns;
+ csr.psr.cbSize :=0;
+ ProtoBroadcastAck(PluginName,0,ACKTYPE_SEARCH,ACKRESULT_SEARCHRESULT,AckHandle,lparam(@csr));
+
+ lstorage:=OpenStorage(ini);
+ if lstorage<>nil then
+ begin
+ list:=GetSectionList(lstorage);
+ pc:=list;
+
+ while pc^<>#0 do
+ begin
+ section:=SearchSection(lstorage,pc);
+ LoadOneStation(section,filter); //!!
+ while pc^<>#0 do inc(pc);
+ inc(pc);
+ end;
+
+ FreeSectionList(list);
+ CloseStorage(lstorage);
+ end;
+
+ ProtoBroadcastAck(PluginName,0,ACKTYPE_SEARCH,ACKRESULT_SUCCESS,AckHandle,0);
+ mFreeMem(filter.lStation);
+ mFreeMem(filter.lStationURL);
+ mFreeMem(filter.lGenre);
+end;
+
+procedure BasicSearch(name:PAnsiChar); cdecl;
+var
+ filter:TMySearchFilter;
+ ini:array [0..MAX_PATH-1] of AnsiChar;
+begin
+ FillChar(filter,SizeOf(filter),0);
+ StrCopy(ini,storage);
+
+ StrDup(filter.lStation,name);
+ if filter.lStation<>nil then
+ CharLowerA(filter.lStation);
+
+ ProcessSearch(filter,ini);
+end;
+
+procedure ExtSearch(wnd:HWND); cdecl;
+var
+ filter:TMySearchFilter;
+ ltmp:longbool;
+ ini1,ini:array [0..MAX_PATH-1] of AnsiChar;
+begin
+ FillChar(filter,SizeOf(filter),0);
+ ini1[0]:=#0;
+ GetDlgItemTextA(wnd,IDC_CUSTOMINI,@ini1,SizeOf(ini1));
+ if ini1[0]=#0 then
+ StrCopy(ini,storage)
+ else
+ ConvertFileName(ini1,ini);
+
+ with filter do
+ begin
+// CallService(MS_UTILS_PATHTOABSOLUTE,dword(@ini1),dword(@ini));
+ lBitrate:=GetDlgItemInt(wnd,IDC_BITRATE,ltmp,false);
+ if IsDlgButtonChecked(wnd,IDC_LT)=BST_CHECKED then lBitrateMode:=-1
+ else if IsDlgButtonChecked(wnd,IDC_EQ)=BST_CHECKED then lBitrateMode:=0
+ else{if IsDlgButtonChecked(lParam,IDC_GT)=BST_CHECKED} lBitrateMode:=1;
+
+ lStation:=GetDlgText(wnd,IDC_STATION,true);
+ if lStation<>nil then
+ CharLowerA(lStation);
+
+ lStationURL:=GetDlgText(wnd,IDC_STATIONURL,true);
+ if lStationURL<>nil then
+ CharLowerA(lStationURL);
+
+ lGenre:=GetDlgText(wnd,IDC_GENRE,true);
+ if lGenre<>nil then
+ CharLowerA(lGenre);
+ end;
+
+ ProcessSearch(filter,ini);
+end;
+
+function Service_SearchBasic(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+begin
+ if lParam=0 then
+ result:=0
+ else
+ begin
+ result:=AckHandle;
+ CloseHandle(mir_forkthread(@BasicSearch,StrDup(PAnsiChar(lParam),PAnsiChar(lParam))));
+ end;
+end;
+
+function Service_SearchByAdvanced(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+begin
+ if lParam=0 then
+ result:=0
+ else
+ begin
+ result:=AckHandle;
+ CloseHandle(mir_forkthread(@ExtSearch,pointer(lParam)));
+ end;
+end;
+
+function Service_GetCaps(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl; forward;
+
+function ExtSearchProc(Dialog:HWnd;hMessage:UINT;wParam:WPARAM;lParam:LPARAM):lresult; stdcall;
+var
+ buf:array [0..MAX_PATH-1] of AnsiChar;
+begin
+ result:=0;
+ case hMessage of
+ WM_INITDIALOG: begin
+ CheckDlgButton(Dialog,IDC_EQ,BST_CHECKED);
+ TranslateDialogDefault(Dialog);
+ end;
+
+ WM_COMMAND: if (wParam shr 16)=BN_CLICKED then
+ begin
+ if loword(wParam)=IDOK then
+ begin
+ SendMessage(GetParent(Dialog),WM_COMMAND,IDOK+(BN_CLICKED) shl 16,
+ GetDlgItem(GetParent(Dialog),IDOK));
+ end
+ else if loword(wParam)=IDC_BN_INIPATH then
+ begin
+ if ShowDlg(@buf,storage,'*.ini'#0'*.ini'#0#0) then
+ SetDlgItemTextA(Dialog,IDC_CUSTOMINI,@buf);
+ end;
+ end;
+ end;
+end;
+
+function Service_ExtSearchUI(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+type
+ PDLGTEMPLATEEX = ^TDLGTEMPLATEEX;
+ TDLGTEMPLATEEX = packed record
+ dlgVer:word;
+ signature:word;
+ helpID:dword;
+ exStyle:dword;
+ style:dword;
+ cDlgItems:word;
+ x:word;
+ y:word;
+ cx:word;
+ cy:word;
+{
+ sz_Or_Ord menu;
+ sz_Or_Ord windowClass;
+ title:array [0..titleLen] of WideChar;
+ pointsize:word;
+ weight:word;
+ italic:byte;
+ charset:byte;
+ typeface:array [0..stringLen] of WideChar;
+}
+ end;
+
+var
+ hr:HRSRC;
+ pdte:PDLGTEMPLATEEX;
+begin
+ result:=0;
+ if lParam<>0 then
+ begin
+ hr:=FindResource(hInstance,MAKEINTRESOURCE(IDD_SEARCH),RT_DIALOG);
+ if hr<>0 then
+ begin
+ pdte:=PDLGTEMPLATEEX(LoadResource(hInstance,hr));
+ if pdte<>nil then
+ begin
+ if (Service_GetCaps(PFLAGNUM_1,0) and PF1_EXTSEARCHUI)<>0 then
+ pdte^.style:=(pdte^.style and not WS_CHILD) or WS_POPUP or WS_BORDER;
+ result:=CreateDialogIndirect(hInstance,
+ PDlgTemplate(pdte){$IFNDEF FPC}^{$ENDIF},lParam,@ExtSearchProc);
+ end;
+ end;
+ end;
+end;
+
+function Service_AddToList(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+var
+ hContact:THANDLE;
+ p:PWideChar;
+ lurl:pWideChar;
+begin
+ result:=0;
+ if lParam<>0 then
+ begin
+ with PPROTOSEARCHRESULT(lParam)^ do
+ begin
+ if id.w<>nil then
+ lurl:=id.w
+ else
+ lurl:=firstname.w;
+
+ if lurl<>nil then
+ begin
+{
+// find contact
+ hContact:=CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ while hContact<>0 do
+ begin
+ if StrCmp(PAnsiChar(CallService(MS_PROTO_GETCONTACTBASEPROTO,hContact,0)),
+ PluginName)=0 then
+ begin
+ p:=DBReadString(hContact,PluginName,optStationURL);
+ l:=StrCmp(p,lurl)=0;
+ mFreeMem(p);
+ if l then
+ begin
+ DBDeleteSetting(hContact,strCList,'NotOnList');
+ DBDeleteSetting(hContact,strCList,'Hidden');
+ result:=hContact;
+ exit;
+ end;
+ end;
+ hContact:=CallService(MS_DB_CONTACT_FINDNEXT,hContact,0);
+ end;
+// if not found
+}
+ hContact:=CallService(MS_DB_CONTACT_ADD,0,0);
+ if hContact<>0 then
+ begin
+ CallService(MS_PROTO_ADDTOCONTACT,hContact,tlparam(PluginName));
+ // URL
+ DBWriteUnicode(hContact,PluginName,optStationURL,lurl);
+ DBWriteUnicode(hContact,PluginName,optFirstName ,lurl);
+
+ // Name
+ if nick.w=nil then
+ p:=lurl
+ else
+ p:=nick.w;
+ DBWriteUnicode(hContact,strCList ,optMyHandle,p);
+ DBWriteUnicode(hContact,PluginName,optNick ,p);
+
+ // Bitrate
+ if email.w<>nil then
+ begin
+ DBWriteWord (hContact,PluginName,optAge ,StrToInt(email.w));
+ DBWriteUnicode(hContact,PluginName,optBitrate,email.w);
+ end;
+
+ // Genre
+ if lastname.w<>nil then
+ begin
+ DBWriteUnicode(hContact,PluginName,optGenre ,lastname.w);
+ DBWriteUnicode(hContact,PluginName,optLastName,lastname.w);
+ end;
+
+ SetStatus(hContact,ID_STATUS_OFFLINE);
+
+// SetAvatar(hContact);
+
+ CallService(MS_IGNORE_IGNORE,hContact,IGNOREEVENT_USERONLINE{IGNOREEVENT_ALL});
+ result:=hContact;
+ end;
+ end;
+ end;
+ end;
+end;
diff --git a/plugins/mRadio/i_service.inc b/plugins/mRadio/i_service.inc
new file mode 100644
index 0000000000..4f12544a81
--- /dev/null
+++ b/plugins/mRadio/i_service.inc
@@ -0,0 +1,241 @@
+{services}
+
+function Service_GetCaps(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+begin
+ case wParam of
+ PFLAGNUM_1:
+ result:=PF1_EXTSEARCH or PF1_MODEMSGRECV or PF1_ADDSEARCHRES or PF1_BASICSEARCH;
+ PFLAGNUM_2:
+ result:=PF2_ONLINE or PF2_INVISIBLE or PF2_SHORTAWAY;
+ PFLAGNUM_3:
+ result:=PF2_ONLINE;// or PF2_INVISIBLE or PF2_SHORTAWAY;
+ PFLAGNUM_4:
+ result:=PF4_NOCUSTOMAUTH or PF4_AVATARS;
+ PFLAG_UNIQUEIDTEXT:
+ result:=int_ptr(Translate('Radio station URL'));
+// PFLAG_UNIQUEIDSETTING:
+// result:=int_ptr(optStationURL)
+ else
+ result:=0;
+ end
+end;
+
+function Service_GetName(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+begin
+ if lParam<>0 then
+ StrCopy(PAnsiChar(lParam),Translate(PluginName),wParam);
+ result:=0;
+end;
+
+procedure GetAwayMsgProc(hContact:THANDLE); cdecl;
+var
+ buf,p:PWideChar;
+begin
+ if isVarsInstalled then
+ begin
+ buf:=ParseVarString(StatusTmpl,ActiveContact);
+ end
+ else
+ begin
+ mGetMem(buf,1024);
+ StrCopyW(buf,StatusTmpl);
+ if StrPosW(buf,'%radio_name%')<>nil then
+ begin
+ p:=DBReadUnicode(ActiveContact,strCList,optMyHandle);
+ StrReplaceW(buf,'%radio_name%',p);
+ mFreeMem(p);
+ end;
+ if StrPosW(buf,'%radio_url%')<>nil then
+ begin
+ p:=DBReadUnicode(ActiveContact,PluginName,optStationURL);
+ StrReplaceW(buf,'%radio_url%',p);
+ mFreeMem(p);
+ end;
+ if StrPosW(buf,'%radio_active%')<>nil then
+ begin
+ StrReplaceW(buf,'%radio_active%',ActiveURLw);
+ end;
+ if StrPosW(buf,'%radio_genre%')<>nil then // saved as String
+ begin
+ p:=DBReadUnicode(ActiveContact,PluginName,optGenre);
+ StrReplaceW(buf,'%radio_genre%',p);
+ mFreeMem(p);
+ end;
+ if StrPosW(buf,'%radio_bitrate%')<>nil then // saved as String
+ begin
+ p:=DBReadUnicode(ActiveContact,PluginName,optBitrate);
+ StrReplaceW(buf,'%radio_bitrate%',p);
+ mFreeMem(p);
+ end;
+ if StrPosW(buf,'%radio_title%')<>nil then
+ begin
+ p:=MakeMessage;
+ StrReplaceW(buf,'%radio_title%',p);
+ mFreeMem(p);
+ end;
+ if StrPosW(buf,'%radio_codec%')<>nil then
+ begin
+ p:=DBReadUnicode(ActiveContact,PluginName,optActiveCodec);
+ StrReplaceW(buf,'%radio_codec%',p);
+ mFreeMem(p);
+ end;
+ if StrPosW(buf,'%radio_status%')<>nil then
+ begin
+ StrReplaceW(buf,'%radio_status%',
+ TranslateW(GetStatusText(
+ CallService(MS_RADIO_COMMAND,MRC_STATUS,RD_STATUS_GET))));
+ end;
+ end;
+
+ ProtoBroadcastAck(PluginName,hContact,ACKTYPE_AWAYMSG,ACKRESULT_SUCCESS,AckHandle,lParam(buf));
+ mFreeMem(buf);
+end;
+
+function Service_GetAwayMsg(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+var
+ ccs:PCCSDATA;
+begin
+ ccs:=PCCSDATA(lParam);
+ if (ccs^.hContact<>0) and (PluginStatus=ID_STATUS_ONLINE) and
+ (DBReadWord(ccs^.hContact,PluginName,optStatus,ID_STATUS_OFFLINE)=ID_STATUS_ONLINE) then
+ begin
+ CloseHandle(mir_forkthread(@GetAwayMsgProc,pointer(ccs^.hContact)));
+ result:=AckHandle;
+ end
+ else
+ result:=0;
+end;
+
+function Service_GetStatus(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+begin
+ result:=PluginStatus;
+end;
+
+function Service_LoadIcon(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+begin
+ case loword(wParam) of
+ PLI_PROTOCOL: result:=LoadImage(hInstance,MAKEINTRESOURCE(IDI_MAIN),IMAGE_ICON,16,16,LR_SHARED);
+// PLI_ONLINE : result:=0;
+// PLI_OFFLINE : result:=0;
+ else
+ result:=0;
+ end;
+end;
+
+function Service_SetStatus(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl;
+var
+ OldStatus:integer;
+begin
+ result:=0;
+ OldStatus:=PluginStatus;
+ if wParam<>ID_STATUS_OFFLINE then
+ wParam:=ID_STATUS_ONLINE;
+
+ if wParam=PluginStatus then
+ exit;
+
+ mFreeMem(proxy);
+ PluginStatus:=ID_STATUS_OFFLINE;
+
+ if wParam<>ID_STATUS_OFFLINE then
+ begin
+ if MyInitBASS<>0 then
+ begin
+ PluginStatus:=ID_STATUS_ONLINE;
+
+ proxy:=GetProxy(hNetLib);
+ BASS_SetConfigPtr(BASS_CONFIG_NET_PROXY,proxy);
+
+ if (ActiveContact<>0) and (AuConnect<>0) then
+ CallService(MS_RADIO_COMMAND,MRC_PLAY,ActiveContact);
+ end;
+ end
+ else //offline
+ begin
+ CallService(MS_RADIO_COMMAND,MRC_STOP,0);
+// StopStation;
+ end;
+ SetStatus(0,ID_STATUS_OFFLINE);
+
+ ProtoBroadcastAck(PluginName,0,ACKTYPE_STATUS,ACKRESULT_SUCCESS,OldStatus,PluginStatus);
+end;
+
+function CreateProtoService(serviceName:PAnsiChar;pFunc:pointer):THANDLE;
+var
+ temp:array [0..MAXMODULELABELLENGTH-1] of AnsiChar;
+begin
+ StrCopy(StrCopyE(temp,PluginName),serviceName);
+ result:=CreateServiceFunction(temp,pFunc);
+end;
+
+var
+ prh0,prh1,prh2,prh3,prh4,prh5,prh6,prh7,prh8,prh9:THANDLE;
+
+procedure DestroyProtoServices;
+begin
+ DestroyServiceFunction(prh0);
+ DestroyServiceFunction(prh1);
+ DestroyServiceFunction(prh2);
+ DestroyServiceFunction(prh3);
+ DestroyServiceFunction(prh4);
+ DestroyServiceFunction(prh5);
+ DestroyServiceFunction(prh6);
+ DestroyServiceFunction(prh7);
+ DestroyServiceFunction(prh8);
+ DestroyServiceFunction(prh9);
+end;
+
+procedure CreateProtoServices;
+begin
+ prh0:=CreateProtoService(PS_GETCAPS ,@Service_GetCaps);
+ prh1:=CreateProtoService(PS_ADDTOLIST ,@Service_AddToList);
+ prh2:=CreateProtoService(PS_CREATEADVSEARCHUI,@Service_ExtSearchUI);
+ prh3:=CreateProtoService(PS_SEARCHBYADVANCED ,@Service_SearchByAdvanced);
+ prh4:=CreateProtoService(PS_BASICSEARCH ,@Service_SearchBasic);
+ prh5:=CreateProtoService(PS_GETNAME ,@Service_GetName);
+ prh6:=CreateProtoService(PS_LOADICON ,@Service_LoadIcon);
+ prh7:=CreateProtoService(PS_GETSTATUS ,@Service_GetStatus);
+ prh8:=CreateProtoService(PS_SETSTATUS ,@Service_SetStatus);
+ prh9:=CreateProtoService(PSS_GETAWAYMSG ,@Service_GetAwayMsg);
+(*
+{
+ Asks protocol for the status message for a status
+ wParam=(WORD) 0 for current status or a status id
+ lParam=SGMA_xxx
+ Returns status msg or NULL if there is none. The protocol have to handle only the current
+ status. Handling messages for other statuses is optional.
+ Remember to mir_free the return value
+}
+ SGMA_UNICODE = 1; // return Unicode status
+
+ PS_GETMYAWAYMSG = '/GetMyAwayMsg';
+
+*)
+(* {
+ wParam : 0
+ lParam : Pointer to a null terminated string containing an ID to search for
+ Affect : Send a basic search request, see notes
+ Returns: A handle to the search request or NULL(0) on failure
+ Notes : All protocols identify users uniquely by a single field
+ this service will search by that field.
+ -
+ All search replies (even protocol-spec extended searches)
+ are replied by a series of ack's,-
+ -
+ Result acks are a series of:
+ type=ACKTYPE_SEARCH, result=ACKRESULT_DATA, lParam=Pointer to a TPROTOSEARCHRESULT structure
+ -
+ ending ack:
+ type=ACKTYPE_SEARCH, result=ACKRESULT_SUCCESS, lParam=0
+ -
+ The pointers in the structure are not guaranteed to be
+ valid after the ack is complete.
+ -
+ The structure to reply with search results can be extended
+ per protocol basis (see below)
+
+ }
+ PS_BASICSEARCH = '/BasicSearch';
+ PS_BASICSEARCHW = '/BasicSearchW';
+*)
+end;
diff --git a/plugins/mRadio/i_tray.inc b/plugins/mRadio/i_tray.inc
new file mode 100644
index 0000000000..096e288691
--- /dev/null
+++ b/plugins/mRadio/i_tray.inc
@@ -0,0 +1,228 @@
+{}
+var
+ trayradioparent:THANDLE;
+ trayparent:THANDLE;
+ traymute :THANDLE;
+ trayplay :THANDLE;
+ srvtrayplaypause:THANDLE;
+ srvtraystop:THANDLE;
+const
+ trayStations:TSortedList = (items:nil; realCount:0; limit:0; increment:8; sortFunc: nil);
+
+type
+ pTrayRadioStation = ^tTrayRadioStation;
+ tTrayRadioStation = record
+ name :pWideChar;
+ hContact:THANDLE;
+ service :THANDLE;
+ menuitem:THANDLE;
+ presents:int; // 0 - not used, 1 - ok, 2 - new
+ end;
+
+function MyStrSort(para1:pointer; para2:pointer):int; cdecl;
+begin
+ result:=StrCmpW(pTrayRadioStation(para1).name,pTrayRadioStation(para2).name);
+end;
+
+function ChooseStation(wParam:WPARAM;lParam,lParam1:LPARAM):int; cdecl;
+begin
+ result:=Service_RadioPlayStop(lParam1,0);
+end;
+
+procedure MakeStationsMenu;
+var
+ hContact:Cardinal;
+ p:pWideChar;
+ i,idx:integer;
+ tmp:pTrayRadioStation;
+ srch:tTrayRadioStation;
+ pc:pAnsiChar;
+ buf:array [0..63] of AnsiChar;
+ mi:TCListMenuItem;
+begin
+ trayStations.sortFunc:=@MyStrSort;
+
+ // clear presents flag
+ if trayStations.realCount>0 then
+ for i:=0 to trayStations.realCount-1 do
+ pTrayRadioStation(trayStations.Items[i]).presents:=0;
+
+ // Fill list
+ FillChar(srch,SizeOf(srch),0);
+ hContact:=CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ while hContact<>0 do
+ begin
+ if StrCmp(PAnsiChar(CallService(MS_PROTO_GETCONTACTBASEPROTO,hContact,0)),cPluginName)=0 then
+ begin
+ p:=DBReadUnicode(hContact,strCList,'MyHandle',nil);
+ if p<>nil then
+ begin
+ srch.name:=p;
+ // search in list
+ if List_GetIndex(@trayStations,@srch,@idx)<>0 then
+ // found - set mark
+ pTrayRadioStation(trayStations.Items[idx]).presents:=1
+ else // add if not found
+ begin
+ mGetMem(tmp,SizeOf(tTrayRadioStation));
+ tmp.name :=p;
+ tmp.presents:=2;
+ tmp.hContact:=hContact;
+ List_InsertPtr(@trayStations,tmp);
+ end;
+ end;
+ end;
+ hContact:=CallService(MS_DB_CONTACT_FINDNEXT,hContact,0);
+ end;
+
+ // delete obsolete elements
+ for i:=trayStations.realCount-1 downto 0 do
+ begin
+ tmp:=pTrayRadioStation(trayStations.Items[i]);
+ if tmp.presents=0 then
+ begin
+ mFreeMem(tmp.name);
+ DestroyServiceFunction(tmp.service);
+ CallService(MS_CLIST_REMOVETRAYMENUITEM,tmp.menuitem,0);
+ mFreeMem(tmp);
+ List_Remove(@trayStations,i);
+ end;
+ end;
+
+ // build menu from sorted list
+ FillChar(mi,SizeOf(mi),0);
+ mi.cbSize:=sizeof(mi);
+ mi.Flags :=CMIF_KEEPUNTRANSLATED or CMIF_UNICODE or CMIF_ROOTHANDLE;
+ mi.szPopupName:=TChar(trayparent);
+ pc:=StrCopyE(@buf,'mRadio/Choose');
+ for i:=0 to trayStations.realCount-1 do
+ begin
+ tmp:=pTrayRadioStation(trayStations.Items[i]);
+ if tmp.presents=2 then
+ begin
+ IntToStr(pc,tmp.hContact);
+ tmp.service:=CreateServiceFunctionParam(@buf,@ChooseStation,tmp.hContact);
+ mi.position :=i;
+ mi.pszService:=@buf;
+ mi.szName.w :=tmp.name;
+ tmp.menuitem:=Menu_AddTrayMenuItem(@mi);
+ end;
+ end;
+end;
+
+function TrayPlayPause(wParam:WPARAM;lParam:LPARAM):int_ptr; cdecl;
+begin
+ result:=CallService(MS_RADIO_COMMAND,MRC_PAUSE,0);
+end;
+
+function TrayStop(wParam:WPARAM;lParam:LPARAM):int_ptr; cdecl;
+begin
+ result:=CallService(MS_RADIO_COMMAND,MRC_STOP,0);
+end;
+
+procedure CreateTrayMenu();
+var
+ mi:TCListMenuItem;
+ playstr:pWideChar;
+begin
+ FillChar(mi, sizeof(mi), 0);
+ mi.cbSize :=sizeof(mi);
+ mi.flags :=CMIF_UNICODE;
+ mi.szName.w:=cPluginName;
+ mi.hIcon :=CallService(MS_SKIN2_GETICON,0,lparam(IcoBtnSettings));
+ trayradioparent:=Menu_AddTrayMenuItem(@mi);
+
+ FillChar(mi, sizeof(mi), 0);
+ mi.cbSize :=sizeof(mi);
+ mi.szPopupName:=TChar(trayradioparent);
+
+ if gVolume<0 then
+ mi.flags:=CMIF_UNICODE or CMIF_ROOTHANDLE or CMIF_CHECKED
+ else
+ mi.flags:=CMIF_UNICODE or CMIF_ROOTHANDLE;
+ mi.szName.w :='Mute';
+ mi.pszService:=MS_RADIO_MUTE;
+ mi.position :=1;
+ traymute:=Menu_AddTrayMenuItem(@mi);
+
+ mi.flags:=CMIF_UNICODE or CMIF_ROOTHANDLE;
+ if CallService(MS_RADIO_COMMAND,MRC_STATUS,RD_STATUS_GET)<>RD_STATUS_PLAYING then
+ playstr:='Play'
+ else
+ playstr:='Pause';
+ mi.szName.w :=playstr;
+ mi.position :=2;
+ srvtrayplaypause:=CreateServiceFunction('mRadio/TrayPlayPause',@TrayPlayPause);
+ mi.pszService:='mRadio/TrayPlayPause';
+ trayplay:=Menu_AddTrayMenuItem(@mi);
+
+ mi.szName.w :='Stop';
+ mi.position :=3;
+ srvtraystop:=CreateServiceFunction('mRadio/TrayStop',@TrayStop);
+ mi.pszService:='mRadio/TrayStop';
+ Menu_AddTrayMenuItem(@mi);
+
+ mi.szName.w :='Play Station';
+ mi.position :=1000;
+ mi.pszService:=nil;
+ trayparent:=Menu_AddTrayMenuItem(@mi);
+end;
+
+function TrayPrebuild(wParam:WPARAM;lParam:LPARAM):int; cdecl;
+var
+ mi:tClistMenuItem;
+ playstr:pWideChar;
+begin
+ FillChar(mi,sizeof(mi),0);
+ mi.cbSize:=sizeof(mi);
+ if gVolume<0 then
+ mi.flags:=CMIM_FLAGS or CMIF_CHECKED
+ else
+ mi.flags:=CMIM_FLAGS;
+ CallService(MS_CLIST_MODIFYMENUITEM,traymute,tlparam(@mi));
+
+ mi.flags:=CMIM_NAME or CMIF_UNICODE;
+ if CallService(MS_RADIO_COMMAND,MRC_STATUS,RD_STATUS_GET)<>RD_STATUS_PLAYING then
+ playstr:='Play'
+ else
+ playstr:='Pause';
+ mi.szName.w:=playstr;
+ CallService(MS_CLIST_MODIFYMENUITEM,trayplay,tlparam(@mi));
+
+ MakeStationsMenu();
+ result:=0;
+end;
+
+procedure CreateMIMTrayMenu;
+begin
+ if ServiceExists(MS_CLIST_ADDTRAYMENUITEM)<>0 then
+// if hiddenwindow<>0 then
+ begin
+ CreateTrayMenu();
+ MakeStationsMenu();
+ HookEvent(ME_CLIST_PREBUILDTRAYMENU,@TrayPrebuild);
+ end;
+end;
+
+procedure RemoveTrayItems;
+var
+ i:integer;
+ tmp:pTrayRadioStation;
+begin
+ // remove stations
+ for i:=trayStations.realCount-1 downto 0 do
+ begin
+ tmp:=pTrayRadioStation(trayStations.Items[i]);
+ mFreeMem(tmp.name);
+ DestroyServiceFunction(tmp.service);
+// CallService(MS_CLIST_REMOVETRAYMENUITEM,tmp.menuitem,0);
+ mFreeMem(tmp);
+ end;
+ List_Destroy(@trayStations);
+ DestroyServiceFunction(srvtrayplaypause);
+ DestroyServiceFunction(srvtraystop);
+
+ if ServiceExists(MS_CLIST_REMOVETRAYMENUITEM)<>0 then
+ CallService(MS_CLIST_REMOVETRAYMENUITEM,trayradioparent,0);
+
+end;
diff --git a/plugins/mRadio/i_tray_api.inc b/plugins/mRadio/i_tray_api.inc
new file mode 100644
index 0000000000..9339a10613
--- /dev/null
+++ b/plugins/mRadio/i_tray_api.inc
@@ -0,0 +1,125 @@
+{}
+type
+ pTrayRadioStation = ^tTrayRadioStation;
+ tTrayRadioStation = record
+ name:pWideChar;
+ handle:THANDLE;
+ end;
+
+function MyStrSort(para1:pointer; para2:pointer):int; cdecl;
+begin
+ result:=StrCmpW(pTrayRadioStation(para1).name,pTrayRadioStation(para2).name);
+end;
+
+function MakeStationsMenu:HMENU;
+var
+ hContact:Cardinal;
+ sl:TSortedList;
+ p:pWideChar;
+ i:integer;
+ flag:integer;
+ tmp:pTrayRadioStation;
+begin
+ result:=CreatePopupMenu;
+ if result<>0 then
+ begin
+ FillChar(sl,SizeOf(sl),0);
+ sl.increment:=16;
+ sl.sortFunc:=@MyStrSort;
+ hContact:=CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ while hContact<>0 do
+ begin
+ if StrCmp(PAnsiChar(CallService(MS_PROTO_GETCONTACTBASEPROTO,hContact,0)),cPluginName)=0 then
+ begin
+ p:=DBReadUnicode(hContact,strCList,'MyHandle',nil);
+ if p<>nil then
+ begin
+ mGetMem(tmp,SizeOf(tTrayRadioStation));
+ tmp.name:=p;
+ tmp.handle:=hContact;
+ List_InsertPtr(@sl,tmp);
+ end;
+ end;
+ hContact:=CallService(MS_DB_CONTACT_FINDNEXT,hContact,0);
+ end;
+
+ for i:=0 to sl.realCount-1 do
+ begin
+ if (i=0) or ((i mod 20)<>0) then
+ flag:=MF_STRING
+ else
+ flag:=MF_STRING or MF_MENUBARBREAK;
+ tmp:=sl.Items[i];
+ AppendMenuW(result,flag,tmp.handle,tmp.name);
+ mFreeMem(tmp.name);
+ mFreeMem(tmp);
+ end;
+ List_Destroy(@sl);
+ end;
+end;
+
+function CreateTrayMenu(wParam:WPARAM;lParam:LPARAM):int; cdecl;
+const
+ startid = 100;
+var
+ menu:HMENU;
+ flag,id:integer;
+ pt:TPOINT;
+ playstr:pWideChar;
+begin
+ id:=0;
+ menu:=CreatePopupMenu;
+ if menu<>0 then
+ begin
+ if gVolume<0 then
+ flag:=MF_STRING+MF_CHECKED
+ else
+ flag:=MF_STRING+MF_UNCHECKED;
+
+ if CallService(MS_RADIO_COMMAND,MRC_STATUS,RD_STATUS_GET)<>RD_STATUS_PAUSED then
+ playstr:='Pause'
+ else
+ playstr:='Play';
+
+ AppendMenuW(menu,flag ,startid+1,TranslateW('Mute'));
+ AppendMenuW(menu,MF_STRING,startid+2,TranslateW(playstr));
+ AppendMenuW(menu,MF_STRING,startid+3,TranslateW('Stop'));
+ AppendMenuW(menu,MF_SEPARATOR,0,nil);
+ AppendMenuW(menu,MF_POPUP,MakeStationsMenu,TranslateW('Play Station'));
+ GetCursorPos(pt);
+ id:=integer(TrackPopupMenu(menu,TPM_RETURNCMD+TPM_NONOTIFY,pt.x,pt.y,0,hiddenwindow,nil));
+ case id of
+ 0: ; // nothing
+ startid+1: begin // mute
+ Service_RadioMute(0,0);
+ end;
+ startid+2: begin // play/pause
+ CallService(MS_RADIO_COMMAND,MRC_PAUSE,0);
+ end;
+ startid+3: begin // stop
+ CallService(MS_RADIO_COMMAND,MRC_STOP,0);
+ end;
+ else // choose station
+ Service_RadioPlayStop(id,0);
+ end;
+ DestroyMenu(menu);
+ end;
+ result:=id;
+end;
+
+procedure CreateMIMTrayMenu;
+var
+ mi:TCListMenuItem;
+begin
+ if ServiceExists(MS_CLIST_ADDTRAYMENUITEM)<>0 then
+// if hiddenwindow<>0 then
+ begin
+ FillChar(mi, sizeof(mi), 0);
+ mi.cbSize :=sizeof(mi);
+ mi.pszService:=MS_RADIO_TRAYMENU;
+ mi.szName.a :=cPluginName;
+ mi.hIcon :=CallService(MS_SKIN2_GETICON,0,lparam(IcoBtnSettings));
+ Menu_AddTrayMenuItem(@mi);
+ end;
+end;
+
diff --git a/plugins/mRadio/i_variables.inc b/plugins/mRadio/i_variables.inc
new file mode 100644
index 0000000000..fead4afad8
--- /dev/null
+++ b/plugins/mRadio/i_variables.inc
@@ -0,0 +1,82 @@
+{Variables support}
+const
+ numvars = 8;
+type
+ tvar = packed record
+ name :PWideChar;
+ help :PAnsiChar;
+ end;
+const
+ vars:array [0..numvars-1] of tvar = (
+ (name:'radio_name' ;help:'Station Name'),
+ (name:'radio_url' ;help:'Station/playlist URL'),
+ (name:'radio_active' ;help:'Currently played URL'),
+ (name:'radio_genre' ;help:'Genre'),
+ (name:'radio_bitrate';help:'Bitrate'),
+ (name:'radio_title' ;help:'Current stream title'),
+ (name:'radio_codec' ;help:'Currently used decoder'),
+ (name:'radio_status' ;help:'Current status'));
+// contact,protocol,host,port,file/path
+
+function GetField(ai:PARGUMENTSINFO):pWideChar; cdecl;
+var
+ i:integer;
+ res:PWideChar;
+begin
+ res:=nil;
+ if ActiveContact<>0 then
+ begin
+ i:=0;
+ repeat
+ if lstrcmpiw(PWideChar(ai^.argv^),vars[i].name)=0 then
+ break;
+ inc(i);
+ until i=numvars;
+ case i of
+ 0: res:=DBReadUnicode(ActiveContact,strCList ,optMyHandle);
+ 1: res:=DBReadUnicode(ActiveContact,PluginName,optStationURL);
+ 2: StrDupW(res,ActiveURLw);
+ 3: res:=DBReadUnicode(ActiveContact,PluginName,optGenre);
+ 4: res:=DBReadUnicode(ActiveContact,PluginName,optBitrate);
+ 5: res:=MakeMessage;
+ 6: res:=DBReadUnicode(0,PluginName,optActiveCodec);
+ 7: StrDupW(res,TranslateW(GetStatusText(CallService(MS_RADIO_COMMAND,MRC_STATUS,RD_STATUS_GET))));
+ end;
+ end;
+ if res=nil then
+ begin
+ mGetMem(res,2);
+ res^:=#0;
+ end;
+ result:=res;
+end;
+
+function FreeField(szReturn:PWideChar):int; cdecl;
+begin
+ mFreeMem(szReturn);
+ result:=1;
+end;
+
+procedure RegisterVariables;
+const
+ Prefix:PAnsiChar = 'Radio'#9;
+var
+ rt:TTOKENREGISTER;
+ i:integer;
+ s:array [0..127] of AnsiChar;
+ p:pAnsiChar;
+begin
+ rt.cbSize :=SizeOf(rt);
+ rt.memType :=TR_MEM_OWNER;
+ rt.szService :=@GetField;
+ rt.szCleanupService:=@FreeField;
+ rt.flags:=TRF_FIELD or TRF_CLEANUP or TRF_PARSEFUNC or TRF_CLEANUPFUNC or TRF_UNICODE;
+ p:=StrCopyE(s,Prefix);
+ rt.szHelpText:=@s;
+ for i:=0 to numvars-1 do
+ begin
+ rt.szTokenString.w:=vars[i].name;
+ StrCopy(p,vars[i].help);
+ CallService(MS_VARS_REGISTERTOKEN,0,lparam(@rt));
+ end;
+end;
diff --git a/plugins/mRadio/i_vars.inc b/plugins/mRadio/i_vars.inc
new file mode 100644
index 0000000000..26e7024cc1
--- /dev/null
+++ b/plugins/mRadio/i_vars.inc
@@ -0,0 +1,184 @@
+{used variables}
+
+{$include m_radio.inc}
+
+const
+ optActiveCodec:PAnsiChar = 'ActiveCodec';
+ optLastStn :PAnsiChar = 'LastStation';
+ optConnect :PAnsiChar = 'AutoConnect';
+ optAutoMute :PAnsiChar = 'AutoMute';
+ optEQ_OFF :PAnsiChar = 'eqoff';
+ optStatusMsg :PAnsiChar = 'StatusMsg';
+ optCurElement :PAnsiChar = 'LastPlayed';
+ optPlayFirst :PAnsiChar = 'PlayFromFirst';
+ optActiveURL :PAnsiChar = 'ActiveURL';
+ optContRec :PAnsiChar = 'ContRec';
+ optLoop :PAnsiChar = 'Loop';
+ optShuffle :PAnsiChar = 'Shuffle';
+ optRecPath :PAnsiChar = 'RecordPath';
+ optStatus :PAnsiChar = 'Status';
+ optVolume :PAnsiChar = 'Volume';
+ optBuffer :PAnsiChar = 'Buffer';
+ optPreBuf :PAnsiChar = 'PreBuf';
+ optTimeout :PAnsiChar = 'Timeout';
+ optVersion :PAnsiChar = 'version';
+ optStatusTmpl :PAnsiChar = 'StatusTmpl';
+ optNumTries :PAnsiChar = 'NumTries';
+ optOffline :PAnsiChar = 'asOffline';
+ // mRadio compatibility
+ optStationURL :PAnsiChar = 'StationURL';
+ optMyHandle :PAnsiChar = 'MyHandle';
+ optGenre :PAnsiChar = 'Genre';
+ optBitrate :PAnsiChar = 'Bitrate';
+ // UserInfo compatibility
+ optFirstName :PAnsiChar = 'FirstName';
+ optNick :PAnsiChar = 'Nick';
+ optLastName :PAnsiChar = 'LastName';
+ optAge :PAnsiChar = 'Age';
+ // 3D sound support
+ optEAXType :PAnsiChar = 'EAXtype';
+ optForcedMono :PAnsiChar = 'ForcedMono';
+
+ optGroup :PAnsiChar = 'Group';
+ optBASSPath :PAnsiChar = 'BASSpath';
+ optTitle :PAnsiChar = 'Title';
+ optArtist :PAnsiChar = 'Artist';
+
+var
+ hhRadioStatus,
+ // service handles
+ hsPlayStop,
+ hsRecord,
+ hsSettings,
+ hsSetVol,
+ hsGetVol,
+ hsMute,
+ hsCommand,
+ hsExport,
+ hsImport,
+ hsTrayMenu,
+ hsEqOnOff,
+
+ hNetLib,
+ hDblClick,
+ hHookShutdown,
+ hCMenuItemRec,
+ hCMenuItemPlay,
+ contexthook,
+ opthook,
+ onsetting,
+ ondelete,
+ onloadhook:THANDLE;
+ hiddenwindow:HWND;
+var
+ plist:tPlaylist;
+// plFile:pWideChar; // playlist file name (for delete after using?)
+// plLocal:boolean; // true - no need to delete playlist
+var
+ RemoteSong:bool;
+ gVolume:integer;
+ NumTries:cardinal;
+ doLoop:cardinal;
+ PlayFirst:cardinal;
+ doShuffle:cardinal;
+ ForcedMono:cardinal;
+ doContRec:cardinal;
+ AuConnect:cardinal;
+ AuMute:cardinal;
+ AsOffline:cardinal;
+ isEQ_OFF:cardinal;
+ PluginStatus:integer;
+ storagep,storage:PAnsiChar;
+ recpath:pWideChar;
+ sBuffer,
+ sTimeout,
+ sPreBuf:cardinal;
+const
+ hVolCtrl :HWND=0;
+ hVolFrmCtrl :HWND=0;
+const
+ {$IFDEF KOL_MCK}
+ btnMute:pIcoButton = nil;
+ {$ELSE}
+ hMuteFrmCtrl:HWND=0;
+ {$ENDIF}
+const
+ Inited:boolean=false;
+const
+ StatusTmpl:pWideChar = nil;
+ proxy:pAnsiChar = nil;
+type
+ tEQRec = record
+ fx :HFX;
+ wnd :HWND;
+ param :BASS_DX8_PARAMEQ;
+ text :PAnsiChar;
+ end;
+var
+ eq:array [0..9] of tEQRec = (
+ (fx:0;wnd:0;param:(fCenter:80 ;fBandwidth:18;fGain:0);text:'80'),
+ (fx:0;wnd:0;param:(fCenter:170 ;fBandwidth:18;fGain:0);text:'170'),
+ (fx:0;wnd:0;param:(fCenter:310 ;fBandwidth:18;fGain:0);text:'310'),
+ (fx:0;wnd:0;param:(fCenter:600 ;fBandwidth:18;fGain:0);text:'600'),
+ (fx:0;wnd:0;param:(fCenter:1000 ;fBandwidth:18;fGain:0);text:'1k'),
+ (fx:0;wnd:0;param:(fCenter:3000 ;fBandwidth:18;fGain:0);text:'3k'),
+ (fx:0;wnd:0;param:(fCenter:6000 ;fBandwidth:18;fGain:0);text:'6k'),
+ (fx:0;wnd:0;param:(fCenter:12000;fBandwidth:18;fGain:0);text:'12k'),
+ (fx:0;wnd:0;param:(fCenter:14000;fBandwidth:18;fGain:0);text:'14k'),
+ (fx:0;wnd:0;param:(fCenter:16000;fBandwidth:18;fGain:0);text:'16k'));
+const
+ IcoBtnSettings:PAnsiChar = 'Radio_Setting';
+ IcoBtnOn :PAnsiChar = 'Radio_On';
+ IcoBtnOff :PAnsiChar = 'Radio_Off';
+ IcoBtnRecUp :PAnsiChar = 'Radio_RecUp';
+ IcoBtnRecDn :PAnsiChar = 'Radio_RecDn';
+ IcoBtnAdd :PAnsiChar = 'Radio_Add';
+ IcoBtnDel :PAnsiChar = 'Radio_Del';
+const
+ hRecord :THANDLE = 0;
+ chan :HSTREAM = 0;
+ ActiveContact:THANDLE = 0;
+ ActiveURLw :PWideChar = nil;
+
+type
+ TEAXItem = record
+ name:PWideChar;
+ code:dword;
+ end;
+const
+ EAXItems:array [0..EAX_ENVIRONMENT_COUNT] of TEAXItem=(
+ (name:'Off' ; code:0),
+ (name:'Generic' ; code:EAX_ENVIRONMENT_GENERIC),
+ (name:'Padded Cell' ; code:EAX_ENVIRONMENT_PADDEDCELL),
+ (name:'Room' ; code:EAX_ENVIRONMENT_ROOM),
+ (name:'Bathroom' ; code:EAX_ENVIRONMENT_BATHROOM),
+ (name:'Living Room' ; code:EAX_ENVIRONMENT_LIVINGROOM),
+ (name:'Stone Room' ; code:EAX_ENVIRONMENT_STONEROOM),
+ (name:'Auditorium' ; code:EAX_ENVIRONMENT_AUDITORIUM),
+ (name:'Concert Hall' ; code:EAX_ENVIRONMENT_CONCERTHALL),
+ (name:'Cave' ; code:EAX_ENVIRONMENT_CAVE),
+ (name:'Arena' ; code:EAX_ENVIRONMENT_ARENA),
+ (name:'Hangar' ; code:EAX_ENVIRONMENT_HANGAR),
+ (name:'Carpeted Hallway'; code:EAX_ENVIRONMENT_CARPETEDHALLWAY),
+ (name:'Hallway' ; code:EAX_ENVIRONMENT_HALLWAY),
+ (name:'Stone Corridor' ; code:EAX_ENVIRONMENT_STONECORRIDOR),
+ (name:'Alley' ; code:EAX_ENVIRONMENT_ALLEY),
+ (name:'Forrest' ; code:EAX_ENVIRONMENT_FOREST),
+ (name:'City' ; code:EAX_ENVIRONMENT_CITY),
+ (name:'Mountains' ; code:EAX_ENVIRONMENT_MOUNTAINS),
+ (name:'Quarry' ; code:EAX_ENVIRONMENT_QUARRY),
+ (name:'Plain' ; code:EAX_ENVIRONMENT_PLAIN),
+ (name:'Parking Lot' ; code:EAX_ENVIRONMENT_PARKINGLOT),
+ (name:'Sewer Pipe' ; code:EAX_ENVIRONMENT_SEWERPIPE),
+ (name:'Under Water' ; code:EAX_ENVIRONMENT_UNDERWATER),
+ (name:'Drugged' ; code:EAX_ENVIRONMENT_DRUGGED),
+ (name:'Dizzy' ; code:EAX_ENVIRONMENT_DIZZY),
+ (name:'Psychotic' ; code:EAX_ENVIRONMENT_PSYCHOTIC));
+
+type
+ tPreset = record
+ name :PWideChar;
+ preset:array [0..9] of shortint;
+ end;
+var
+ Presets: array of tPreset;
diff --git a/plugins/mRadio/i_visual.inc b/plugins/mRadio/i_visual.inc
new file mode 100644
index 0000000000..77012b55f2
--- /dev/null
+++ b/plugins/mRadio/i_visual.inc
@@ -0,0 +1,115 @@
+{Visual part}
+function OnContactMenu(hContact:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ mi:TCListMenuItem;
+begin
+ FillChar(mi,SizeOf(mi),0);
+ mi.cbSize:=sizeof(mi);
+ if StrCmp(PAnsiChar(CallService(MS_PROTO_GETCONTACTBASEPROTO,hContact,0)),
+ PluginName)<>0 then
+ begin
+ mi.flags:=CMIM_FLAGS or CMIF_HIDDEN;
+ CallService(MS_CLIST_MODIFYMENUITEM,hCMenuItemPlay,tlparam(@mi));
+ end
+ else
+ begin
+ // play/Stop
+ mi.flags:=CMIM_FLAGS or CMIM_ICON or CMIM_NAME;
+ if THANDLE(hContact)<>ActiveContact then
+ begin
+ mi.szName.a:='Start broadcasting';
+ mi.hIcon:=CallService(MS_SKIN2_GETICON,0,tlparam(IcoBtnOn));
+ end
+ else
+ begin
+ mi.szName.a:='Stop broadcasting';
+ mi.hIcon:=CallService(MS_SKIN2_GETICON,0,tlparam(IcoBtnOff));
+ end;
+ CallService(MS_CLIST_MODIFYMENUITEM,hCMenuItemPlay,tlparam(@mi));
+
+ // record
+ mi.flags:=CMIM_FLAGS or CMIM_ICON or CMIM_NAME;
+ if Service_RadioRecord(0,1)<>0 then
+ begin
+ mi.szName.a:='Stop record';
+ mi.hIcon:=CallService(MS_SKIN2_GETICON,0,tlparam(IcoBtnRecDn));
+ end
+ else
+ begin
+ mi.szName.a:='Start record';
+ mi.hIcon:=CallService(MS_SKIN2_GETICON,0,tlparam(IcoBtnRecUp));
+ end;
+ end;
+ CallService(MS_CLIST_MODIFYMENUITEM,hCMenuItemRec,tlparam(@mi));
+ result:=0;
+end;
+
+procedure CreateMenu;
+var
+ mi:TCListMenuItem;
+begin
+ FillChar(mi, sizeof(mi), 0);
+ mi.cbSize :=sizeof(mi);
+// mi.popupPosition:=MenuUserInfoPos;
+ mi.pszService:=MS_RADIO_RECORD;
+ mi.szName.a :='Start/Stop Record';
+ hCMenuItemRec:=Menu_AddContactMenuItem(@mi);
+
+//!! mi.flags :=CMIF_NOTOFFLINE or CMIF_NOTOFFLIST;
+ mi.hIcon :=CallService(MS_SKIN2_GETICON,0,lparam(IcoBtnOn));
+ mi.pszService :=MS_RADIO_PLAYSTOP;
+ mi.szName.a :='Start/Stop broadcasting';
+ hCMenuItemPlay:=Menu_AddContactMenuItem(@mi);
+end;
+
+procedure RegisterIcons;
+var
+ sid:TSKINICONDESC;
+begin
+ FillChar(sid,SizeOf(TSKINICONDESC),0);
+ sid.cbSize:=SizeOf(TSKINICONDESC);
+ sid.cx:=16;
+ sid.cy:=16;
+ sid.szSection.a:='Protocols/mRadio';
+ sid.hDefaultIcon :=LoadImage(hInstance,MAKEINTRESOURCE(BTN_RECUP),IMAGE_ICON,16,16,0);
+ sid.pszName :=IcoBtnRecUp;
+ sid.szDescription.a:='Start record';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+
+ sid.hDefaultIcon :=LoadImage(hInstance,MAKEINTRESOURCE(BTN_RECDN),IMAGE_ICON,16,16,0);
+ sid.pszName :=IcoBtnRecDn;
+ sid.szDescription.a:='Stop record';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+
+ sid.hDefaultIcon :=LoadImage(hInstance,MAKEINTRESOURCE(IDI_MAIN),IMAGE_ICON,16,16,0);
+ sid.pszName :=IcoBtnSettings;
+ sid.szDescription.a:='Settings';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+
+ sid.hDefaultIcon :=LoadImage(hInstance,MAKEINTRESOURCE(IDI_ON),IMAGE_ICON,16,16,0);
+ sid.pszName :=IcoBtnOn;
+ sid.szDescription.a:='Broadcast ON';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+
+ sid.hDefaultIcon :=LoadImage(hInstance,MAKEINTRESOURCE(IDI_OFF),IMAGE_ICON,16,16,0);
+ sid.pszName :=IcoBtnOff;
+ sid.szDescription.a:='Broadcast OFF';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+
+ sid.hDefaultIcon :=LoadImage(hInstance,MAKEINTRESOURCE(IDI_ADD),IMAGE_ICON,16,16,0);
+ sid.pszName :=IcoBtnAdd;
+ sid.szDescription.a:='Add EQ preset';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+
+ sid.hDefaultIcon :=LoadImage(hInstance,MAKEINTRESOURCE(IDI_DEL),IMAGE_ICON,16,16,0);
+ sid.pszName :=IcoBtnDel;
+ sid.szDescription.a:='Delete EQ preset';
+ Skin_AddIcon(@sid);
+ DestroyIcon(sid.hDefaultIcon);
+end;
diff --git a/plugins/mRadio/ico/delete.ico b/plugins/mRadio/ico/delete.ico
new file mode 100644
index 0000000000..eea851da19
--- /dev/null
+++ b/plugins/mRadio/ico/delete.ico
Binary files differ
diff --git a/plugins/mRadio/ico/mradio.ico b/plugins/mRadio/ico/mradio.ico
new file mode 100644
index 0000000000..e95c6fe21a
--- /dev/null
+++ b/plugins/mRadio/ico/mradio.ico
Binary files differ
diff --git a/plugins/mRadio/ico/new.ico b/plugins/mRadio/ico/new.ico
new file mode 100644
index 0000000000..73937210e0
--- /dev/null
+++ b/plugins/mRadio/ico/new.ico
Binary files differ
diff --git a/plugins/mRadio/ico/off.ico b/plugins/mRadio/ico/off.ico
new file mode 100644
index 0000000000..041e55bb9e
--- /dev/null
+++ b/plugins/mRadio/ico/off.ico
Binary files differ
diff --git a/plugins/mRadio/ico/on.ico b/plugins/mRadio/ico/on.ico
new file mode 100644
index 0000000000..35bf39e0b9
--- /dev/null
+++ b/plugins/mRadio/ico/on.ico
Binary files differ
diff --git a/plugins/mRadio/ico/recoff.ico b/plugins/mRadio/ico/recoff.ico
new file mode 100644
index 0000000000..4bb6000ef0
--- /dev/null
+++ b/plugins/mRadio/ico/recoff.ico
Binary files differ
diff --git a/plugins/mRadio/ico/recon.ico b/plugins/mRadio/ico/recon.ico
new file mode 100644
index 0000000000..d8007b1c8c
--- /dev/null
+++ b/plugins/mRadio/ico/recon.ico
Binary files differ
diff --git a/plugins/mRadio/m_radio.h b/plugins/mRadio/m_radio.h
new file mode 100644
index 0000000000..53381ab524
--- /dev/null
+++ b/plugins/mRadio/m_radio.h
@@ -0,0 +1,131 @@
+#ifndef M_RADIO
+#define M_RADIO
+
+#ifndef MIID_MRADIO
+#define MIID_MRADIO {0xeebc474c, 0xb0ad, 0x470f, {0x99, 0xa8, 0x9d, 0xd9, 0x21, 0x0c, 0xe2, 0x33}}
+#endif
+
+// command codes
+#define MRC_STOP 0
+#define MRC_PLAY 1 // lParam is radio contact handle
+#define MRC_PAUSE 2
+#define MRC_PREV 3
+#define MRC_NEXT 4
+#define MRC_STATUS 5 // lParam is RD_STATUS_* value (RD_STATUS_GET only now)
+#define MRC_SEEK 6 // lParam is value in sec; -1 mean obtain current position
+#define MRC_RECORD 7 // lParam is 0 - switch; 1 - on; 2 - off
+
+/* RD_STATUS_* constands
+ [C]used as command [E]used as event
+ [-]do not use [+]used as command and event
+*/
+#define RD_STATUS_NOSTATION 0 // [E] no active station found
+#define RD_STATUS_PLAYING 1 // [-] media is playing
+#define RD_STATUS_PAUSED 2 // [E] media is paused
+#define RD_STATUS_STOPPED 3 // [E] media is stopped (only for playlists)
+#define RD_STATUS_CONNECT 4 // [E] plugin try to connect to the station
+#define RD_STATUS_ABORT 5 // [E] plugin want to abort while try to connect
+#define RD_STATUS_GET 6 // [C] to get current status
+// next is for events only +0.0.2.1
+#define RD_STATUS_POSITION 107 // [E] position was changed
+#define RD_STATUS_MUTED 108 // [E] Mute/Unmute command was sent
+#define RD_STATUS_RECORD 109 // [E] "Record" action called
+#define RD_STATUS_NEWTRACK 110 // [E] new track/station
+#define RD_STATUS_NEWTAG 111 // [E] tag data changed
+#define RD_STATUS_NEWSTATION 112 // [E] new station (contact)
+
+/*
+ Open radio Options, if Main Options window not opened
+ wParam: 0
+ lParam: 0
+*/
+#define MS_RADIO_SETTINGS "mRadio/Settings"
+/*
+ Switch 'record' mode
+ +0.0.1.x (deprecatet) !!!
+ wParam: 0 - switch mode; else - get record status
+ lParam: 0
+ +0.0.2.x
+ wParam: not used
+ lParam: 0 - switch mode; else - get record status
+ Return: Current status: 1 - record is ON, 0 - OFF
+*/
+#define MS_RADIO_RECORD "mRadio/REC"
+
+/*
+ Set current radio volume
+ wParam: volume (0-100)
+ lParam: must be 0
+ Return: previous value
+*/
+#define MS_RADIO_SETVOL "mRadio/SetVol"
+
+/*
+ Get current radio volume
+ wParam: 0
+ lParam: 0
+ Return: volime value (negative if muted)
+*/
+#define MS_RADIO_GETVOL "mRadio/GetVol"
+
+/*
+ wParam,lParam = 0
+*/
+#define MS_RADIO_MUTE "mRadio/Mute"
+
+/*
+ Send command to mRadio
+ wParam: command (see MRC_* constant)
+ lParam: value (usually 0)
+ Return: return value (now for status only)
+*/
+#define MS_RADIO_COMMAND "mRadio/Command"
+
+/*
+ Starting or stopping radio station
+ wParam: Radio contact handle (lParam=0) or Station name
+ lParam: 0 - wParam is handle, 1 - ANSI, else - unicode
+*/
+#define MS_RADIO_PLAYSTOP "mRadio/PlayStop"
+
+/* +0.0.1.4
+ wParam: station handle (0 - all)
+ lParam: nil (through dialog, radio.ini by default) or ansi string with filename
+ Return: exported stations amount
+*/
+#define MS_RADIO_EXPORT "mRadio/Export"
+
+/* +0.0.1.4
+ wParam: group to import radio or 0
+ lParam: nil (through dialog, radio.ini by default) or ansi string with filename
+ Return: imported stations amount
+*/
+#define MS_RADIO_IMPORT "mRadio/Import"
+
+/*
+ wParam: 0 - switch; 1 - switch on; -1 - switch off
+ lParam: 0
+ Return: last state (0 - was off, 1 - was on)
+*/
+#define MS_RADIO_EQONOFF "mRadio/EqOnOff"
+
+//////event/////
+
+/* +0.0.1.4 (deprecatet only used in 0.0.1.4+)
+ wParam:
+ MRC_STOP , LParam - 0
+ MRC_PLAY , LParam - url
+ MRC_PAUSE , LParam - 0 (pause) / 1 (play)
+ MRC_SEEK , LParam - lParam is value in sec
+ MRC_RECORD , LParam - 0 (stop) / 1 (record)
+
+ +0.0.2.1 new event constants !!
+ wParam: RD_STATUS_* (see constants)
+ RD_STATUS_NEWSTATION , lParam: contact handle
+ RD_STATUS_NEWTRACK , lParam: URL (unicode)
+ RD_STATUS_PAUSED , lParam: 1 - pause, 0 - continued
+ RD_STATUS_RECORD , lParam: 0 - off, 1 - on
+*/
+#define ME_RADIO_STATUS "mRadio/Status"
+
+#endif
diff --git a/plugins/mRadio/m_radio.inc b/plugins/mRadio/m_radio.inc
new file mode 100644
index 0000000000..0d8775723f
--- /dev/null
+++ b/plugins/mRadio/m_radio.inc
@@ -0,0 +1,126 @@
+{$IFNDEF M_RADIO}
+{$DEFINE M_RADIO}
+{command codes}
+
+// defined in interfaces.inc
+//const MIID_MRADIO:MUUID='{EEBC474C-B0AD-470F-99A8-9DD9210CE233}';
+
+const
+ MRC_STOP = 0;
+ MRC_PLAY = 1; // lParam is radio contact handle
+ MRC_PAUSE = 2;
+ MRC_PREV = 3;
+ MRC_NEXT = 4;
+ MRC_STATUS = 5; // lParam is RD_STATUS_* value (RD_STATUS_GET only now)
+ MRC_SEEK = 6; // lParam is value in sec; -1 mean obtain current position
+ MRC_RECORD = 7; // lParam is 0 - switch; 1 - on; 2 - off
+ MRC_MUTE = 8;
+
+const
+ // Plugin status (result of RD_STATUS_GET)
+ RD_STATUS_NOSTATION = 0; // no active station found
+ RD_STATUS_PLAYING = 1; // media is playing
+ RD_STATUS_PAUSED = 2; // media is paused
+ RD_STATUS_STOPPED = 3; // media is stopped (only for playlists)
+ RD_STATUS_CONNECT = 4; // plugin try to connect to the station
+ RD_STATUS_ABORT = 5; // plugin want to abort while try to connect
+ // next is for events only
+ RD_STATUS_POSITION = 107; // position was changed
+ RD_STATUS_MUTED = 108; // Mute/Unmute command was sent
+ RD_STATUS_RECORD = 109; // "Record" action called
+ RD_STATUS_NEWTRACK = 110; // new track/station
+ RD_STATUS_NEWTAG = 111; // tag data changed
+ RD_STATUS_NEWSTATION = 112; // new station (contact)
+ // next command is for users
+ RD_STATUS_GET = 6; // to get current status
+
+const
+{
+ Open radio Options, if Main Options window not opened
+ wParam: 0
+ lParam: 0
+}
+ MS_RADIO_SETTINGS:PAnsiChar = 'mRadio/Settings';
+{
+ Switch 'record' mode
+ wParam: not used
+ lParam: 0 - switch mode; else - get record status
+ Return: Current status: 1 - record is ON, 0 - OFF
+}
+ MS_RADIO_RECORD:PAnsiChar = 'mRadio/REC';
+
+{
+ Set current radio volume
+ wParam: volume (0-100)
+ lParam: must be 0
+ Return: previous value
+}
+ MS_RADIO_SETVOL:PAnsiChar = 'mRadio/SetVol';
+
+{
+ Get current radio volume
+ wParam: 0
+ lParam: 0
+ Return: volime value (negative if muted)
+}
+ MS_RADIO_GETVOL:PAnsiChar = 'mRadio/GetVol';
+
+{
+ wParam,lParam = 0
+}
+ MS_RADIO_MUTE:PAnsiChar = 'mRadio/Mute';
+
+{
+ Send command to mRadio
+ wParam: command (see MRC_* constant)
+ lParam: value (usually 0)
+ Return: return value (now for status only)
+}
+ MS_RADIO_COMMAND:PAnsiChar = 'mRadio/Command';
+
+{
+ Starting or stopping radio station
+ wParam: Radio contact handle (lParam=0) or Station name
+ lParam: 0 - wParam is handle, 1 - ANSI, else - unicode
+}
+ MS_RADIO_PLAYSTOP:PAnsiChar = 'mRadio/PlayStop';
+
+{
+ wParam: station handle (0 - all)
+ lParam: nil (through dialog, radio.ini by default) or ansi string with filename
+ Return: exported stations amount
+}
+ MS_RADIO_EXPORT:PAnsiChar = 'mRadio/Export';
+
+{
+ wParam: group to import radio or 0
+ lParam: nil (through dialog, radio.ini by default) or ansi string with filename
+ Return: imported stations amount
+}
+ MS_RADIO_IMPORT:PAnsiChar = 'mRadio/Import';
+
+{
+ wParam: RD_STATUS_* constants
+ lParam: argument
+ RD_STATUS_NEWSTATION - contact handle
+ RD_STATUS_NEWTRACK - URL (unicode)
+ RD_STATUS_PAUSED - 1 - pause, 0 - continued
+ RD_STATUS_RECORD -,0 - off, 1 - on
+}
+ ME_RADIO_STATUS:PAnsiChar = 'mRadio/Status';
+
+{
+ wParam: 0 - switch; 1 - switch on; -1 - switch off
+ lParam: 0
+ Return: last state (0 - was off, 1 - was on)
+}
+ MS_RADIO_EQONOFF:PAnsiChar = 'mRadio/EqOnOff';
+
+{
+ wParam: 0
+ lParam: 0
+ Return: 0, if cancelled, 101 - "mute", 102 - "play/pause", 103 - "stop" or station handle
+}
+ MS_RADIO_TRAYMENU:PAnsiChar = 'mRadio/MakeTrayMenu';
+
+{$ENDIF}
diff --git a/plugins/mRadio/make.bat b/plugins/mRadio/make.bat
new file mode 100644
index 0000000000..d77976f0b3
--- /dev/null
+++ b/plugins/mRadio/make.bat
@@ -0,0 +1,17 @@
+@echo off
+set myopts=-dMiranda
+set dprname=mradio.dpr
+
+..\delphi\brcc32.exe mradio.rc -fomradio.res
+
+if /i '%1' == 'fpc' (
+ ..\FPC\bin\fpc.exe %myopts% %dprname% %2 %3 %4 %5 %6 %7 %8 %9
+) else if /i '%1' == 'fpc64' (
+ ..\FPC\bin64\ppcrossx64.exe %myopts% %dprname% %2 %3 %4 %5 %6 %7 %8 %9
+) else if /i '%1' == 'xe2' (
+ ..\XE2\BIN\dcc32.exe -b -dUNICODE_CTRLS -dKOL_MCK %myopts% %dprname% %2 %3 %4 %5 %6 %7 %8 %9
+) else if /i '%1' == 'xe64' (
+ ..\XE2\BIN\dcc64.exe -b -dUNICODE_CTRLS -dKOL_MCK %myopts% %dprname% %2 %3 %4 %5 %6 %7 %8 %9
+) else (
+ ..\delphi\dcc32 -b -dUNICODE_CTRLS -dKOL_MCK %myopts% %dprname% %1 %2 %3 %4 %5 %6 %7 %8 %9
+) \ No newline at end of file
diff --git a/plugins/mRadio/mr_rc.inc b/plugins/mRadio/mr_rc.inc
new file mode 100644
index 0000000000..41998194b6
--- /dev/null
+++ b/plugins/mRadio/mr_rc.inc
@@ -0,0 +1,87 @@
+const
+ IDD_SEARCH = 101;
+ IDD_SETTING = 102;
+ IDD_SETTING_TECH = 103;
+ IDD_FRAME = 104;
+
+ IDC_GENRE = 1025;
+ IDC_BITRATE = 1026;
+ IDC_STATION = 1027;
+ IDC_LT = 1029;
+ IDC_EQ = 1030;
+ IDC_GT = 1031;
+
+ IDC_LOOP = 1032;
+ IDC_SHUFFLE = 1033;
+ IDC_CONTREC = 1034;
+ IDC_PLAYFIRST = 1035;
+ IDC_EQOFF = 1036;
+ IDC_BN_INIPATH = 1037;
+ IDC_BN_URLPATH = 1038;
+ IDC_CUSTOMINI = 1039;
+
+ IDC_VOLUME = 1040;
+ IDC_STATIONURL = 1041;
+ IDC_ADD_LIST = 1042;
+ IDC_ADD_INI = 1043;
+ IDC_BN_RECPATH = 1044;
+ IDC_ED_RECPATH = 1045;
+ IDC_BN_RECORD = 1046;
+ IDC_EAXTYPE = 1047;
+ IDC_USEEAX = 1048;
+ IDC_BUFFER = 1049;
+ IDC_PREBUF = 1050;
+ IDC_TIMEOUT = 1051;
+ IDC_PRESET = 1052;
+ IDC_TRIES = 1053;
+ IDC_MONO = 1054;
+
+ IDC_BASSPATH = 1100;
+ IDC_BASSPTHBTN = 1101;
+
+ IDC_ZERO = 1060;
+ IDC_EQ00 = 1061;
+ IDC_EQ01 = 1062;
+ IDC_EQ02 = 1063;
+ IDC_EQ03 = 1064;
+ IDC_EQ04 = 1065;
+ IDC_EQ05 = 1066;
+ IDC_EQ06 = 1067;
+ IDC_EQ07 = 1068;
+ IDC_EQ08 = 1069;
+ IDC_EQ09 = 1070;
+ IDC_0 = 1161;
+ IDC_1 = 1162;
+ IDC_2 = 1163;
+ IDC_3 = 1164;
+ IDC_4 = 1165;
+ IDC_5 = 1166;
+ IDC_6 = 1167;
+ IDC_7 = 1168;
+ IDC_8 = 1169;
+ IDC_9 = 1170;
+
+ IDC_CONNECT = 1072;
+ IDC_OFFLINE = 1073;
+ IDC_AUTOMUTE = 1074;
+
+ IDC_STATUS = 1075;
+ IDC_HLP_VARS = 1076;
+
+ IDC_IMPORT = 1077;
+ IDC_EXPORT = 1078;
+
+ IDC_EQ_ADD = 1079;
+ IDC_EQ_DEL = 1080;
+
+ IDC_RADIO_MUTE = 1025;
+ IDC_RADIO_VOL = 1026;
+
+ BTN_RECUP = 202;
+ BTN_RECDN = 203;
+
+ IDI_MAIN = 302;
+ IDI_ON = 303;
+ IDI_OFF = 304;
+ IDI_ADD = 305;
+ IDI_DEL = 306;
diff --git a/plugins/mRadio/mradio.dpr b/plugins/mRadio/mradio.dpr
new file mode 100644
index 0000000000..56c2b7dba9
--- /dev/null
+++ b/plugins/mRadio/mradio.dpr
@@ -0,0 +1,350 @@
+{.$DEFINE CHANGE_NAME_BUFFERED}
+{$include compilers.inc}
+{$IFDEF COMPILER_16_UP}
+ {$WEAKLINKRTTI ON}
+ {.$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([])}
+{$ENDIF}
+{$IMAGEBASE $13300000}
+library mradio;
+
+uses
+// FastMM4,
+ {$IFDEF KOL_MCK}kol,icobuttons,KOLCCtrls,{$ENDIF}Windows,messages,commctrl
+ ,common,io,wrapper,wrapdlgs,syswin
+ ,Dynamic_Bass,dynbasswma
+ ,m_api,dbsettings,mirutils,playlist,memini;
+
+{$include mr_rc.inc}
+{$r mradio.res}
+
+{$include i_vars.inc}
+
+const
+ cPluginName = 'mRadio';
+const
+ PluginName:PAnsiChar = cPluginName;
+
+function MakeMessage:pWideChar;
+var
+ p,artist,title:pWideChar;
+ len:cardinal;
+begin
+ artist:=DBReadUnicode(0,PluginName,optArtist);
+ title :=DBReadUnicode(0,PluginName,optTitle);
+ len:=StrLenW(artist);
+ if (artist<>nil) and (title<>nil) then
+ inc(len,3);
+ inc(len,StrLenW(title));
+
+ if len>0 then
+ begin
+ mGetMem(result,(len+1)*SizeOf(WideChar));
+ p:=result;
+ if artist<>nil then
+ begin
+ p:=StrCopyEW(p,artist);
+ if title<>nil then
+ p:=StrCopyEW(p,' - ');
+ mFreeMem(artist);
+ end;
+ if title<>nil then
+ begin
+ StrCopyW(p,title);
+ mFreeMem(title);
+ end;
+ end
+ else
+ result:=nil;
+end;
+
+procedure SetStatus(hContact:THANDLE;status:integer);
+begin
+// if Status=ID_STATUS_OFFLINE then
+// MyStopBass;
+
+ if status=ID_STATUS_OFFLINE then
+ begin
+ if (AsOffline=BST_UNCHECKED) or (PluginStatus<>ID_STATUS_OFFLINE) then
+ status:=ID_STATUS_INVISIBLE;
+ end;
+
+ if hContact=0 then
+ begin
+ hContact:=CallService(MS_DB_CONTACT_FINDFIRST,0,0);
+ while hContact<>0 do
+ begin
+ if StrCmp(PAnsiChar(CallService(MS_PROTO_GETCONTACTBASEPROTO,hContact,0)),PluginName)=0 then
+ begin
+ DBWriteWord(hContact,PluginName,optStatus,status);
+ end;
+ hContact:=CallService(MS_DB_CONTACT_FINDNEXT,hContact,0);
+ end;
+ end
+ else
+ DBWriteWord(hContact,PluginName,optStatus,status);
+end;
+
+{$include i_search.inc}
+{$include i_bass.inc}
+{$include i_cc.inc}
+{$include i_variables.inc}
+{$include i_service.inc}
+{$include i_myservice.inc}
+{$include i_hotkey.inc}
+{$IFDEF KOL_MCK}
+ {$include i_frame.inc}
+{$ELSE}
+ {$include i_frameapi.inc}
+{$ENDIF}
+{$include i_tray.inc}
+{$include i_visual.inc}
+{$include i_optdlg.inc}
+
+function MirandaPluginInfoEx(mirandaVersion:DWORD):PPLUGININFOEX; cdecl;
+begin
+ result:=@PluginInfo;
+ PluginInfo.cbSize :=SizeOf(TPLUGININFOEX);
+ PluginInfo.shortName :='mRadio Mod';
+ PluginInfo.version :=$00000202;
+ PluginInfo.description:='This plugin plays and records Internet radio streams.'+
+ ' Also local media files can be played.';
+ PluginInfo.author :='Awkward';
+ PluginInfo.authorEmail:='panda75@bk.ru; awk1975@ya.ru';
+ PluginInfo.copyright :='(c) 2007-2012 Awkward';
+ PluginInfo.homepage :='http://code.google.com/p/delphi-miranda-plugins/';
+ PluginInfo.flags :=UNICODE_AWARE;
+ PluginInfo.uuid :=MIID_MRADIO;
+end;
+
+function OnModulesLoaded(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+var
+ nlu:TNETLIBUSER;
+ szTemp:array [0..255] of AnsiChar;
+ i:integer;
+begin
+ UnhookEvent(onloadhook);
+
+ DBWriteDWord(0,PluginName,optVersion,PluginInfo.version);
+
+ szTemp[0]:='E';
+ szTemp[1]:='Q';
+ szTemp[2]:='_';
+ szTemp[4]:=#0;
+ for i:=0 to 9 do
+ begin
+ szTemp[3]:=AnsiChar(ORD('0')+i);
+ eq[i].param.fGain:=DBReadByte(0,PluginName,szTemp,15)-15;
+ end;
+ LoadPresets;
+
+ RegisterIcons;
+ CreateMenu;
+ CreateMIMTrayMenu;
+
+ FillChar(nlu,SizeOf(nlu),0);
+ StrCopy(szTemp,Translate('%s server connection'));
+ StrReplace(szTemp,'%s',PluginName);
+ nlu.szDescriptiveName.a:=szTemp;
+ nlu.cbSize :=SizeOf(nlu);
+ nlu.flags :=NUF_HTTPCONNS or NUF_NOHTTPSOPTION or NUF_OUTGOING;
+ nlu.szSettingsModule :=PluginName;
+ hNetLib:=CallService(MS_NETLIB_REGISTERUSER,0,tlparam(@nlu));
+
+// CallService(MS_RADIO_COMMAND,MRC_RECORD,2); record off - not so necessary
+
+ recpath:=DBReadUnicode(0,PluginName,optRecPath);
+
+ sPreBuf:=DBReadWord(0,PluginName,optPreBuf,75);
+ BASS_SetConfig(BASS_CONFIG_NET_PREBUF,sPreBuf);
+
+ sBuffer:=DBReadWord(0,PluginName,optBuffer,5000);
+ BASS_SetConfig(BASS_CONFIG_NET_BUFFER,sBuffer);
+
+ sTimeout:=DBReadWord(0,PluginName,optTimeout,5000);
+ BASS_SetConfig(BASS_CONFIG_NET_TIMEOUT,sTimeout);
+
+ doLoop :=DBReadByte(0,PluginName,optLoop);
+ doShuffle :=DBReadByte(0,PluginName,optShuffle);
+ doContRec :=DBReadByte(0,PluginName,optContRec);
+ PlayFirst :=DBReadByte(0,PluginName,optPlayFirst);
+ isEQ_OFF :=DBReadByte(0,PluginName,optEQ_OFF);
+ AuConnect :=DBReadByte(0,PluginName,optConnect);
+ AuMute :=DBReadByte(0,PluginName,optAutoMute);
+ AsOffline :=DBReadByte(0,PluginName,optOffline);
+ gVolume :=DBReadByte(0,PluginName,optVolume,50);
+ NumTries :=DBReadByte(0,PluginName,optNumTries,1);
+ ForcedMono:=DBReadByte(0,PluginName,optForcedMono);
+ if NumTries<1 then NumTries:=1;
+
+ SetStatus(0,ID_STATUS_OFFLINE);
+
+ StatusTmpl:=DBReadUnicode(0,PluginName,optStatusTmpl,'%radio_title%');
+
+ CallService(MS_RADIO_COMMAND,MRC_STATUS,RD_STATUS_NOSTATION);
+
+ IsMultiThread:=true;
+
+ RegisterVariables;
+
+ if AuConnect<>BST_UNCHECKED then
+ ActiveContact:=LoadContact(PluginName,optLastStn)
+ else
+ ActiveContact:=0;
+
+ onsetting:=HookEvent(ME_DB_CONTACT_SETTINGCHANGED,@OnSettingsChanged);
+ ondelete :=HookEvent(ME_DB_CONTACT_DELETED ,@OnContactDeleted);
+ randomize;
+ CreateFrame(0);
+
+ RegisterHotKey;
+
+ result:=0;
+end;
+
+function PreShutdown(wParam:WPARAM;lParam:LPARAM):int;cdecl;
+{
+var
+ buf:array [0..MAX_PATH-1] of AnsiChar;
+ fdata:WIN32_FIND_DATAA;
+ p:pAnsiChar;
+ fi:THANDLE;
+}
+begin
+ RemoveTrayItems;
+
+ CallService(MS_RADIO_COMMAND,MRC_STOP,1);
+ UnregisterHotKey;
+
+ DestroyProtoServices;
+ DestroyWindow(hiddenwindow);
+ DestroyFrame();
+ MyFreeBASS;
+ DBWriteByte(0,PluginName,optVolume,gVolume);
+
+ DestroyServiceFunction(hsTrayMenu);
+ DestroyServiceFunction(hsPlayStop);
+ DestroyServiceFunction(hsRecord);
+ DestroyServiceFunction(hsSettings);
+ DestroyServiceFunction(hsSetVol);
+ DestroyServiceFunction(hsGetVol);
+ DestroyServiceFunction(hsMute);
+ DestroyServiceFunction(hsCommand);
+ DestroyServiceFunction(hsEqOnOff);
+
+ DestroyServiceFunction(hsExport);
+ DestroyServiceFunction(hsImport);
+
+ DestroyHookableEvent(hhRadioStatus);
+
+ UnhookEvent(onsetting);
+ UnhookEvent(ondelete);
+ UnhookEvent(hHookShutdown);
+ UnhookEvent(hDblClick);
+ UnhookEvent(opthook);
+ UnhookEvent(contexthook);
+
+ CallService(MS_NETLIB_CLOSEHANDLE,hNetLib,0);
+ mFreeMem(storage);
+ mFreeMem(storagep);
+ mFreeMem(recpath);
+ mFreeMem(StatusTmpl);
+ mFreeMem(basspath);
+ FreePresets;
+{
+ //delete cover files
+ buf[0]:=#0;
+ GetTempPathA(MAX_PATH,buf);
+ p:=StrEnd(buf);
+ StrCopy(p,'mrAvt*.*');
+
+ fi:=FindFirstFileA(buf,fdata);
+ if fi<>THANDLE(INVALID_HANDLE_VALUE) then
+ begin
+ repeat
+ StrCopy(p,fdata.cFileName);
+ DeleteFileA(buf);
+ until not FindNextFileA(fi,fdata);
+ FindClose(fi);
+ end;
+}
+ result:=0;
+end;
+
+function Load(): int; cdecl;
+var
+ desc:TPROTOCOLDESCRIPTOR;
+ szTemp:array [0..MAX_PATH-1] of WideChar;
+ pc:pWideChar;
+ custom:pWideChar;
+begin
+ Langpack_register;
+
+ GetModuleFileNameW(0,szTemp,MAX_PATH-1);
+ pc:=StrEndW(szTemp);
+ repeat
+ dec(pc);
+ until pc^='\';
+ inc(pc);
+ pc^:=#0;
+
+ custom:=DBReadUnicode(0,PluginName,optBASSPath,nil);
+
+ if MyLoadBASS(szTemp,custom) then
+ begin
+ StrCopyW(pc,'plugins\mradio.ini');
+// StrDup(storage,szTemp);
+ FastWideToAnsi(szTemp,storage);
+ mGetMem(storagep,MAX_PATH+32);
+ CallService(MS_DB_GETPROFILEPATH,MAX_PATH-1,lparam(storagep));
+ StrCat(storagep,'\mradio.ini');
+
+ FillChar(desc,SizeOf(desc),0);
+ desc.cbSize:=PROTOCOLDESCRIPTOR_V3_SIZE;//SizeOf(desc);
+ desc.szName:=PluginName;
+ desc._type :=PROTOTYPE_PROTOCOL;
+ CallService(MS_PROTO_REGISTERMODULE,0,lparam(@desc));
+
+ hhRadioStatus:=CreateHookableEvent(ME_RADIO_STATUS);
+
+ hsPlayStop:=CreateServiceFunction(MS_RADIO_PLAYSTOP,@Service_RadioPlayStop);
+ hsRecord :=CreateServiceFunction(MS_RADIO_RECORD ,@Service_RadioRecord);
+ hsSettings:=CreateServiceFunction(MS_RADIO_SETTINGS,@Service_RadioSettings);
+ hsSetVol :=CreateServiceFunction(MS_RADIO_SETVOL ,@Service_RadioSetVolume);
+ hsGetVol :=CreateServiceFunction(MS_RADIO_GETVOL ,@Service_RadioGetVolume);
+ hsMute :=CreateServiceFunction(MS_RADIO_MUTE ,@Service_RadioMute);
+ hsCommand :=CreateServiceFunction(MS_RADIO_COMMAND ,@ControlCenter);
+ hsEqOnOff :=CreateServiceFunction(MS_RADIO_EQONOFF ,@Service_EqOnOff);
+
+ hiddenwindow:=CreateHiddenWindow;
+ hsTrayMenu:=CreateServiceFunction(MS_RADIO_TRAYMENU,@CreateTrayMenu);
+
+ hsExport :=CreateServiceFunction(MS_RADIO_EXPORT ,@ExportAll);
+ hsImport :=CreateServiceFunction(MS_RADIO_IMPORT ,@ImportAll);
+
+
+ CreateProtoServices;
+ onloadhook :=HookEvent(ME_SYSTEM_MODULESLOADED ,@OnModulesLoaded);
+ hHookShutdown:=HookEvent(ME_SYSTEM_OKTOEXIT ,@PreShutdown);
+ hDblClick :=HookEvent(ME_CLIST_DOUBLECLICKED ,@Service_RadioPlayStop{@DblClickProc});
+ opthook :=HookEvent(ME_OPT_INITIALISE ,@OnOptInitialise);
+ contexthook :=HookEvent(ME_CLIST_PREBUILDCONTACTMENU,@OnContactMenu);
+
+ PluginStatus:=ID_STATUS_OFFLINE;
+ end;
+ mFreeMem(custom);
+
+ Result:=0;
+end;
+
+function Unload: int; cdecl;
+begin
+ Unload_BASSDLL;
+ Result:=0;
+end;
+
+exports
+ Load, Unload,
+ MirandaPluginInfoEx;
+
+begin
+end.
diff --git a/plugins/mRadio/mradio.rc b/plugins/mRadio/mradio.rc
new file mode 100644
index 0000000000..91e976fa35
--- /dev/null
+++ b/plugins/mRadio/mradio.rc
@@ -0,0 +1,182 @@
+#include "mr_rc.inc"
+
+LANGUAGE 0,0
+
+IDD_SETTING DIALOGEX 0, 0, 304, 226, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ GROUPBOX "Add Station",-1,1,1,200,108, WS_TABSTOP
+ LTEXT "Station Name",IDC_STATIC,6,12,192,8
+ EDITTEXT IDC_STATION,4,22,194,11,ES_AUTOHSCROLL
+ LTEXT "Station URL (required)",IDC_STATIC,6,36,178,8
+ PUSHBUTTON "...", IDC_BN_URLPATH, 184,47,14,11, BS_BOTTOM
+ EDITTEXT IDC_STATIONURL,4,47,178,11,ES_AUTOHSCROLL
+ LTEXT "Genre",IDC_STATIC,6,60,192,8
+ EDITTEXT IDC_GENRE,4,71,194,11,ES_AUTOHSCROLL
+ LTEXT "Bitrate",IDC_STATIC,6,84,46,8
+ EDITTEXT IDC_BITRATE,4,94,48,11, ES_RIGHT | ES_NUMBER
+ CTEXT "Add station",IDC_STATIC,74,84,124,8
+ PUSHBUTTON "To list", IDC_ADD_LIST, 74,94,60,11
+ PUSHBUTTON "To INI" , IDC_ADD_INI , 138,94,60,11
+
+ GROUPBOX "Record",-1,1,114,200,34, WS_TABSTOP
+ CTEXT "Record path",IDC_STATIC,4,122,194,8,SS_CENTERIMAGE
+ EDITTEXT IDC_ED_RECPATH,4,132,178,12,ES_AUTOHSCROLL
+ PUSHBUTTON "...", IDC_BN_RECPATH, 184,132,14,12
+
+ GROUPBOX "Status Message",-1,1,152,200,70, WS_TABSTOP
+ CONTROL "V", IDC_HLP_VARS, "MButtonClass",WS_TABSTOP, 4,162,16,16,$18000000
+ RTEXT "Status message template",IDC_STATIC,22,170,174,10
+ EDITTEXT IDC_STATUS,4,180,194,40,
+ ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN
+
+ AUTOCHECKBOX "Shuffle playlist" ,IDC_SHUFFLE , 204, 4,98,22, BS_VCENTER | BS_MULTILINE
+ AUTOCHECKBOX "Play from first" ,IDC_PLAYFIRST, 204, 28,98,22, BS_VCENTER | BS_MULTILINE
+ AUTOCHECKBOX "Loop single media" ,IDC_LOOP , 204, 52,98,22, BS_VCENTER | BS_MULTILINE
+ AUTOCHECKBOX "Continuous record" ,IDC_CONTREC , 204, 76,98,22, BS_VCENTER | BS_MULTILINE
+ AUTOCHECKBOX "Autoconnect last station" ,IDC_CONNECT , 204,100,98,22, BS_VCENTER | BS_MULTILINE
+ AUTOCHECKBOX "Protocol depending status" ,IDC_OFFLINE , 204,124,98,22, BS_VCENTER | BS_MULTILINE
+ AUTOCHECKBOX "Mute with Miranda" ,IDC_AUTOMUTE , 204,148,98,22, BS_VCENTER | BS_MULTILINE
+
+ PUSHBUTTON "Import File", IDC_IMPORT, 204,188,98,16, BS_MULTILINE
+ PUSHBUTTON "Export All" , IDC_EXPORT, 204,206,98,16, BS_MULTILINE
+}
+
+IDD_SETTING_TECH DIALOGEX 0, 0, 304, 226, 0
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+{
+ CTEXT "Use EAX",IDC_USEEAX,2,34,72,16,SS_CENTERIMAGE
+ COMBOBOX IDC_EAXTYPE,75,34,96,75,CBS_DROPDOWNLIST | WS_VSCROLL
+
+ AUTOCHECKBOX "Forced Mono",IDC_MONO,2,52,169,16, BS_RIGHT | BS_VCENTER | BS_LEFTTEXT
+
+ CONTROL "",IDC_VOLUME,"msctls_trackbar32",TBS_NOTICKS | WS_TABSTOP|$100,19,12,129,16
+ CTEXT "min",IDC_STATIC,5,15,18,8
+ CTEXT "max",IDC_STATIC,150,15,18,8
+ GROUPBOX "Volume",-1,2,2,170,28, WS_TABSTOP
+
+ GROUPBOX "",-1,174,2,128,73, WS_TABSTOP
+ LTEXT "Buffer, ms (5000)",IDC_STATIC,210,8,90,16,SS_CENTERIMAGE
+ EDITTEXT IDC_BUFFER,178,10,30,12, ES_RIGHT | ES_NUMBER
+ LTEXT "PreBuf, % (75%)",IDC_STATIC,210,24,90,16,SS_CENTERIMAGE
+ EDITTEXT IDC_PREBUF,178,26,30,12, ES_RIGHT | ES_NUMBER
+ LTEXT "Timeout, ms (5000)",IDC_STATIC,210,40,90,16,SS_CENTERIMAGE
+ EDITTEXT IDC_TIMEOUT,178,42,30,12, ES_RIGHT | ES_NUMBER
+ LTEXT "Tries to connect",IDC_STATIC,210,56,90,16,SS_CENTERIMAGE
+ EDITTEXT IDC_TRIES,178,58,30,12, ES_RIGHT | ES_NUMBER
+
+ LTEXT "BASS library path (empty for default)",IDC_STATIC,6,76,276,14,SS_CENTERIMAGE
+ EDITTEXT IDC_BASSPATH, 2,90,284,11,ES_AUTOHSCROLL
+ PUSHBUTTON "...", IDC_BASSPTHBTN, 288,90,14,11, BS_BOTTOM
+
+ PUSHBUTTON "OFF", IDC_EQOFF,6 ,154,22,12,BS_PUSHLIKE | BS_CHECKBOX | BS_DEFPUSHBUTTON
+ PUSHBUTTON "0" ,IDC_ZERO ,9 ,171,14,13
+ RTEXT "15" ,IDC_STATIC,13,141,16,10
+ RTEXT "-15",IDC_STATIC,13,199,16,10
+
+ COMBOBOX IDC_PRESET,2,116,150,75,CBS_DROPDOWN | WS_VSCROLL
+ CONTROL "+", IDC_EQ_ADD, "MButtonClass",WS_TABSTOP, 154,114,16,16,$18000000
+ CONTROL "-", IDC_EQ_DEL, "MButtonClass",WS_TABSTOP, 174,114,16,16,$18000000
+ LTEXT "Equalizer presets",IDC_STATIC,194,114,106,16,SS_CENTERIMAGE
+
+ CTEXT "",IDC_0,34,211,16,9
+ CONTROL "",IDC_EQ00,"msctls_trackbar32",TBS_LEFT|TBS_VERT|WS_TABSTOP, 29,136,22,75
+ CTEXT "",IDC_1,61,211,16,9
+ CONTROL "",IDC_EQ01,"msctls_trackbar32",TBS_LEFT|TBS_VERT|WS_TABSTOP, 55,136,22,75
+ CTEXT "",IDC_2,87,211,16,9
+ CONTROL "",IDC_EQ02,"msctls_trackbar32",TBS_LEFT|TBS_VERT|WS_TABSTOP, 81,136,22,75
+ CTEXT "",IDC_3,113,211,16,9
+ CONTROL "",IDC_EQ03,"msctls_trackbar32",TBS_LEFT|TBS_VERT|WS_TABSTOP,107,136,22,75
+ CTEXT "",IDC_4,139,211,16,9
+ CONTROL "",IDC_EQ04,"msctls_trackbar32",TBS_LEFT|TBS_VERT|WS_TABSTOP,133,136,22,75
+ CTEXT "",IDC_5,165,211,16,9
+ CONTROL "",IDC_EQ05,"msctls_trackbar32",TBS_LEFT|TBS_VERT|WS_TABSTOP,159,136,22,75
+ CTEXT "",IDC_6,191,211,16,9
+ CONTROL "",IDC_EQ06,"msctls_trackbar32",TBS_LEFT|TBS_VERT|WS_TABSTOP,185,136,22,75
+ CTEXT "",IDC_7,217,211,16,9
+ CONTROL "",IDC_EQ07,"msctls_trackbar32",TBS_LEFT|TBS_VERT|WS_TABSTOP,211,136,22,75
+ CTEXT "",IDC_8,243,211,16,9
+ CONTROL "",IDC_EQ08,"msctls_trackbar32",TBS_LEFT|TBS_VERT|WS_TABSTOP,237,136,22,75
+ CTEXT "",IDC_9,269,211,16,9
+ CONTROL "",IDC_EQ09,"msctls_trackbar32",TBS_LEFT|TBS_VERT|WS_TABSTOP,263,136,22,75
+ GROUPBOX "Equalizer",IDC_STATIC,2,130,298,92
+}
+
+IDD_SEARCH DIALOGEX 0, 0, 110, 140
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+BEGIN
+ LTEXT "Station",IDC_STATIC,8,1,98,8
+ EDITTEXT IDC_STATION,4,11,102,12,ES_AUTOHSCROLL
+ LTEXT "URL",IDC_STATIC,8,27,98,8
+ EDITTEXT IDC_STATIONURL,4,37,102,12,ES_AUTOHSCROLL
+ LTEXT "Genre",IDC_STATIC,8,53,98,8
+ EDITTEXT IDC_GENRE,4,63,102,12,ES_AUTOHSCROLL
+ LTEXT "Bitrate",IDC_STATIC,8,79,72,8
+ EDITTEXT IDC_BITRATE,4,89,76,12, ES_RIGHT | ES_NUMBER
+
+ AUTORADIOBUTTON "<",IDC_LT,86,80,20,10
+ AUTORADIOBUTTON "=",IDC_EQ,86,91,20,10
+ AUTORADIOBUTTON ">",IDC_GT,86,102,20,10
+
+ LTEXT "Custom INI file",IDC_STATIC,8,112,98,8
+ EDITTEXT IDC_CUSTOMINI,4,122,84,12,ES_AUTOHSCROLL
+ PUSHBUTTON "...",IDC_BN_INIPATH, 90,122,14,12
+END
+
+// just for frame API version
+IDD_FRAME DIALOGEX 0, 0, 114, 16, 0
+STYLE DS_SETFONT | WS_CHILD | DS_FIXEDSYS | WS_VISIBLE
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0
+BEGIN
+ CONTROL "" ,IDC_RADIO_VOL ,"msctls_trackbar32", TBS_BOTTOM|TBS_NOTICKS|$100,0,2,98,11
+// CONTROL "*",IDC_RADIO_MUTE,"MButtonClass" ,WS_TABSTOP,100,1,12,12//,$18000000
+ PUSHBUTTON "*" ,IDC_RADIO_MUTE, 100,1,12,12,
+// BS_OWNERDRAW
+ BS_FLAT | BS_ICON | BS_PUSHLIKE | BS_CHECKBOX | BS_DEFPUSHBUTTON | BS_CENTER | BS_VCENTER
+END
+
+
+IDI_MAIN ICON "ico\mradio.ico"
+IDI_ON ICON "ico\on.ico"
+IDI_OFF ICON "ico\off.ico"
+IDI_ADD ICON "ico\new.ico"
+IDI_DEL ICON "ico\delete.ico"
+BTN_RECUP ICON "ico\recon.ico"
+BTN_RECDN ICON "ico\recoff.ico"
+
+LANGUAGE 0,0
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 0,0,2,2
+ PRODUCTVERSION 0,0,8,0
+ FILEFLAGSMASK $3F
+ FILEOS 4
+ FILETYPE 2
+ FILESUBTYPE 0
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "CompanyName",""0
+ VALUE "Comments", "Plugin to play Internet radio"0
+ VALUE "FileDescription", "mRadio Mod plugin for Miranda NG"0
+ VALUE "FileVersion", "0, 0, 2, 2 "0
+ VALUE "InternalName", "mRadio Mod"0
+ VALUE "OriginalFilename", "mradio.dll"0
+ VALUE "ProductName", "mRadio Mod Dynamic Link Library (DLL)"0
+ VALUE "ProductVersion", "0, 0, 8, 0 "0
+ VALUE "SpecialBuild", "10.05.2012 "0
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation",0,1200
+ END
+END
diff --git a/plugins/mRadio/mradio.res b/plugins/mRadio/mradio.res
new file mode 100644
index 0000000000..d70c64c58e
--- /dev/null
+++ b/plugins/mRadio/mradio.res
Binary files differ
diff --git a/plugins/mRadio/readme.txt b/plugins/mRadio/readme.txt
new file mode 100644
index 0000000000..ea111e1056
--- /dev/null
+++ b/plugins/mRadio/readme.txt
@@ -0,0 +1,147 @@
+Description:
+------------
+This plugin plays and records Internet radio streams. Also local media files can be played.
+
+This is not 'clear' mod of mRadio plugin from Bankrut.
+So, some things can be different or even uncomplete.
+
+Notes:
+------
+1) Supported formats
+BASS library support plugins, so format list can be expanded.
+BASS.dll give support for MP3 and OGG formats
+BASSWMA.dll give WMA/ASX stream support
+PLS (ansi), M3U (old and new format, ansi), M3U8 (old and new format utf8) supported.
+This is plugin processing and now all strings converted to ansi.
+
+2) Tooltip templates
+Tipper template:
+Common status message (not only mRadio): %sys:status_msg%
+also you can check CList/StatusMsg or StationURL values.
+
+mToolTip templates:
+[Station_Codec]
+DBSub=mRadio
+DBSet=ActiveCodec
+
+[Station_URL]
+DBSub=mRadio
+DBSet=StationURL
+
+[Station_Bitrate]
+DBSub=mRadio
+DBSet=Bitrate
+
+[Station_Genre]
+DBSub=mRadio
+DBSet=Genre
+
+3) Different info
+ WMA stations don't recorded, only MP3 and OGG stream record tested.
+ Not all metatags supported.
+ Manual proxy settings supported.
+ BASS Plugins loading ONLY when protocol going Online
+
+Changelog:
+----------
+0.0.2.1 ()
+ 64 bit compatibility fix
+ new event notifications
+0.0.2.0 (19 mar 2011)
+ Adapted for FreePascal and 64 bit support
+0.0.1.7 ()
+ Added internet radio reconnect on track end
+ Added button to import all radiostations from file
+ Added frame right-click action "Open mradio settings"
+ Improved frame coloration
+ Added Frame (1st try)
+ Added another WMA stream processing
+ Added option for several tries to connect radiostation
+0.0.1.6 (20 sep 2010)
+ Changed streams/files tag process
+0.0.1.5 (6 sep 2010)
+ Fixed simple form of M3U playlist reading
+ Improved compatibility with Watrack plugin
+ Restored ANSI URL processing if no Unicode possible (especially for BASSWMA)
+ Fixed group choosing for "Add to List" of new stations
+ Fixed some memory leaks
+ Changed proxy processing to ability to use IE settings (through miranda proxy settings)
+ Fixed crash when contact goin offline (Offline as offline setting)
+ Code changed to BASS 2.4.6 support (unicode URL support)
+0.0.1.4 ()
+ Added event ME_RADIO_STATUS
+ Added Variables plugin parsing for record filename
+ Added ability to choose BASS.dll placement
+ Radio stopping now if active contact deleting
+ Added services for import/export stations
+0.0.1.3 (15 oct 2009)
+ Radio record filename must calc from media info
+ Radio record fixed
+0.0.1.2 (15 oct 2009)
+ Added equalizer preset work
+ "Record" crash fixed
+ Added changing icon in contact menu for start/stop broadcasting
+0.0.1.1 (10 oct 2009)
+ Added option to export all radio station from list to choosed INI-file
+0.0.1.0 ()
+ Fixed UTF8 station track info showing
+ Dropped ANSI and pre-0.7 Miranda IM version support
+0.0.0.15 (14 jan 2008)
+ Toolbar button to switch sound on/off affect now on mRadio sound
+ Added 'radio_codec' variable
+ Added GUID to header files
+ Added option to show offline stations as Offline (not invisible)
+ Added option to autconnect last played station
+0.0.0.14 ( nov 2007)
+ Fixed: record directory not created if it not exists
+ Fixed: Database hook error
+ Station choosing change plugin status from Offline to Online
+ Fixed: last played track wrongly restored at start
+0.0.0.13 (24 jun 2007)
+ Added notification for 'Work Offline' IE mode
+ New option for network timeout
+0.0.0.12 (20 jun 2007)
+ Small fixes
+ Added Unicode for file select dialog in Options
+0.0.0.11 (18 jun 2007)
+ Added test unicode support (can work wrong)
+ Added ability to change INI-file for station search
+ Changed search interface for Miranda version 0.7+
+ Added button to switch equalizer off
+ Options interface changed
+0.0.0.10 (13 jun 2007)
+ Added partial SHOUTcast metatags support
+ Added playing station status info
+0.0.0.9 (10 jun 2007)
+ Added support old and new ANSI and UTF8 M3U (M3U8) playlists
+ Added relative pathnames in playlist support
+0.0.0.8 (9 jun 2007)
+ Added partial updater support
+ Small fixes
+ Added new option for playlist
+0.0.0.7 (7 jun 2007)
+ Fixed: equalizer values was reversed
+ Added PLS and M3U playlist support
+ Code changes
+0.0.0.6 (3 jun 2007)
+ Fixed: Global volume (not music only) used
+ Fixed: Can't break slowly connected stations
+0.0.0.5 (2 jun 2007)
+ Fixed: Double click contact dialog can't open
+ Changed: OGG station stream saving
+0.0.0.4 (1 jun 2007)
+ Small Radio station search dialog adaptation for 0.7 and 0.6 Miranda versions
+ Added volume control synchronization with SndVol
+ Added option to repeat media
+ Added global 'Record' menu item to contact menu
+ Fixed: exit while online Radio status make crash
+ Added: Equalizer
+0.0.0.3 (30 may 2007)
+ Added UserInfoEx (station editing) compatibility
+ Code cleaning and optimization
+ Convert to (2 in 1)
+0.0.0.2 (30 may 2007)
+ Added Variables plugin support
+ Added Buffer size changing ability
+0.0.0.1 (29 may 2007)
+ First release
diff --git a/plugins/mRadio/variants.pas b/plugins/mRadio/variants.pas
new file mode 100644
index 0000000000..c7c8fdc824
--- /dev/null
+++ b/plugins/mRadio/variants.pas
@@ -0,0 +1,7 @@
+unit variants;
+
+interface
+
+implementation
+
+end. \ No newline at end of file