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:uint_ptr; protov:PAnsiChar; begin FillChar(res,SizeOf(tQSRec),0); res.data:=uint_ptr(-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:=uint_ptr(CallService(protov,wparam.n,lparam.n)); if tmp=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:=db_event_last(hContact); if hDbEvent<>0 then begin ZeroMemory(@dbei,sizeof(dbei)); dbei.cbSize:=SizeOf(dbei); db_event_get(hDbEvent, @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=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:=db_find_first(); while hContact<>0 do begin //!! check account AddContact(cnt1,hContact); inc(cnt1); if cnt1=cnt then break; // additional checking hContact:=db_find_next(hContact); 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 k0 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 kcnt 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 k0 then begin k:=0; num:=LV_GetLParam(grid,i); while k1) 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':' 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':' 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{twparam(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_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_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) and (ServiceExists(MS_FP_GETCLIENTICON)<>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=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; begin SendMessage(cb,CB_RESETCONTENT,0,0); CB_AddStrDataW(cb,TranslateW('All')); for i:=1 to GetNumProto do begin CB_AddStrDataW(cb,GetProtoAccName(i),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=0 then begin lcol[col]:=colorder[i]; inc(col); end; SendMessageW(grid,LVM_SETCOLUMNORDERARRAY,tablecolumns,tlparam(@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.