{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)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,'')<>nil then begin mGetMem(tmpc1,8192); StrCopyW(tmpc1,msgtitle); StrReplaceW(tmpc1,'',tmpc); end else tmpc1:=msgtitle; if StrPosW(msgtext,'')<>nil then begin mGetMem(tmpc2,8192); StrCopyW(tmpc2,msgtext); StrReplaceW(tmpc2,'',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); 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; 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 i0 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 i0 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;