📄 searchresultpage.cpp
字号:
/*
This file is part of KCeasy (http://www.kceasy.com)
Copyright (C) 2002-2004 Markus Kern <mkern@kceasy.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
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.
*/
//---------------------------------------------------------------------------
#include <vcl.h>
#include <KCeasy.h>
#pragma hdrstop
#include <Clipbrd.hpp>
#include <Math.hpp>
#include "SearchResultPage.h"
#include "SearchPage.h"
#include "BrowserPage.h"
#include "LibraryPage.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "VTHeaderPopup"
#pragma resource "*.dfm"
#pragma link "VirtualTrees"
//TSearchResultFrame *SearchResultFrame;
//---------------------------------------------------------------------------
// number of times user can try 'Search Again', -1 means unlimited
const int MaxSearchAgainCount = -1;
//---------------------------------------------------------------------------
__fastcall TSearchResultForm::TSearchResultForm(TComponent* Owner,
TSearch* NSearch, AnsiString& NTabCaption)
: Search(NSearch),
TabCaption(NTabCaption),
TForm(Owner),
NoOfResults(0),
NoOfUniqueResults(0),
NoOfMultiSourceResults(0),
UseSystemIcons(true),
ShowSingleSources(true),
ShowRawUserNames(true),
SearchAgainCount(0),
BeFunny(false)
{
// don't translate filter until doing so doesn't brake it
TP_Ignore(this,"FilterKeyCombo.Items");
TranslateComponent(this);
ResultTree->NodeDataSize = sizeof(TResultNodeData);
if(Config->GetValueInt("search/allow_multiselect")) {
ResultTree->TreeOptions->SelectionOptions << toMultiSelect;
ResultTree->TreeOptions->PaintOptions >> toHideFocusRect;
}
// load column positions specific for this search realm
VTSetColumnSettings(ResultTree,Config->GetValue(GetTreeColumnKey(Search->GetRealm())));
StopSearchBtn->Enabled = false;
SearchAgainBtn->Enabled = false;
DownloadBtn->Enabled = false;
QueryStatic->DoubleBuffered = true;
// load availability bitmap and split it up
Graphics::TBitmap* AvailBmp = new Graphics::TBitmap();
AvailBmp->LoadFromResourceName((unsigned int)HInstance,"AVAILABILITY_BMP");
for(int i=0; i<AVAILABILITY_BMP_LEN; i++) {
AvailBitmaps[i] = new Graphics::TBitmap();
AvailBitmaps[i]->Width = AvailBmp->Width;
AvailBitmaps[i]->Height = 14;
TRect DstRect(0,0,AvailBmp->Width,AvailBitmaps[i]->Height);
TRect SrcRect(0,i*14,AvailBmp->Width,i*14+14);
AvailBitmaps[i]->Canvas->CopyRect(DstRect,AvailBmp->Canvas,SrcRect);
AvailBitmaps[i]->TransparentMode = tmAuto;
AvailBitmaps[i]->Transparent = true;
}
delete AvailBmp;
if(Search->GetNetwork() == "")
// TRANSLATOR: "QUERY on NETWORK" in search result header.
RealmStatic->Caption = AnsiString::Format(_("%s on %s"),
// TRANSLATOR: All Networks in search result header.
ARRAYOFCONST((TSearch::RealmToString(Search->GetRealm()), _("All Networks"))));
else
// TRANSLATOR: "QUERY on NETWORK" in search result header.
RealmStatic->Caption = AnsiString::Format(_("%s on %s"),ARRAYOFCONST((TSearch::RealmToString(Search->GetRealm()),Search->GetNetwork().c_str())));
QueryStatic->Caption = Search->GetQuery().c_str();
ResultCountLbl->Caption = AnsiString::Format("%d / %d",ARRAYOFCONST((NoOfMultiSourceResults, NoOfUniqueResults)));
MarkLocalFiles = Config->GetValueInt("search/highlight_shared");
MarkDownloads = Config->GetValueInt("search/highlight_downloads");
UseSystemIcons = Config->GetValueInt("gui/use_system_file_icons");
ShowRawUserNames = Config->GetValueInt("search/show_raw_user_names");
ShowSingleSources = !Config->GetValueInt("search/filter_single_source_replies");
SortByAlphaNum = Config->GetValueInt("search/sort_by_alphanum");
// load filter settings
FilterPanel->Visible = Config->GetValueInt("search/enable_filtering");
list<string> LastFilter = string_split(Config->GetValue("search/last_filter"),"|");
if(LastFilter.size() == 2) {
FilterKeyCombo->ItemIndex = FilterKeyCombo->Items->IndexOf(LastFilter.front().c_str());
if(FilterKeyCombo->ItemIndex == -1)
FilterKeyCombo->ItemIndex = 0;
FilterEdit->Text = LastFilter.back().c_str();
}
// are we funny today?
char* FunnyAppName = Base64Encode(BRAND_APP_NAME,sizeof(BRAND_APP_NAME)-1);
BeFunny = stricmp(FunnyAppName,BRAND_APP_NAME_BASE64);
delete[] FunnyAppName;
}
__fastcall TSearchResultForm::~TSearchResultForm()
{
for(int i=0; i<AVAILABILITY_BMP_LEN; i++)
delete AvailBitmaps[i];
}
//---------------------------------------------------------------------------
bool __fastcall TSearchResultForm::EngineCallback(TCallbackInfo* CbInfo)
{
switch(CbInfo->Code) {
case CbcStateChange:
if(Engine->IsOffline()) {
ResultTree->Clear();
}
return true;
case CbcNewSearch:
return true;
case CbcSearchRemoved:
// clear tree so we don't reference results which are already removed
if(((TSearch*)CbInfo->Data1) != Search)
return false;
ResultTree->Clear();
return true;
case CbcSearchBegin: {
if(((TSearch*)CbInfo->Data1) != Search)
return false;
StopSearchBtn->Enabled = true;
if(Search->GetNetwork() == "")
// TRANSLATOR: "QUERY on NETWORK" in search result header.
RealmStatic->Caption = AnsiString::Format(_("%s on %s"),
// TRANSLATOR: All Networks in search result header.
ARRAYOFCONST((TSearch::RealmToString(Search->GetRealm()), _("All Networks"))));
else
// TRANSLATOR: "QUERY on NETWORK" in search result header.
RealmStatic->Caption = AnsiString::Format(_("%s on %s"), ARRAYOFCONST((TSearch::RealmToString(Search->GetRealm()),Search->GetNetwork().c_str())));
QueryStatic->Caption = Search->GetQuery().c_str();
ResultCountLbl->Caption = AnsiString::Format("%d / %d", ARRAYOFCONST((NoOfMultiSourceResults, NoOfUniqueResults)));
return true;
}
case CbcSearchResult: {
if(((TSearch*)CbInfo->Data1) != Search)
return false;
list<TSearchResult*>* ResultList = ((list<TSearchResult*> *)CbInfo->Data2);
ResultTree->BeginUpdate();
list<TSearchResult*>::iterator ritr = ResultList->begin();
for(;ritr != ResultList->end(); ++ritr) {
TSearchResult* Result = (*ritr);
// this is what i mean by funny
if(BeFunny && RandomRange(0,20) != 10)
continue;
// group same files if a hash is present
TVirtualNode* ParentNode = NULL;
if(Result->GetHashes()->Size() != 0) {
for(ParentNode = ResultTree->GetFirst(); ParentNode; ParentNode=ResultTree->GetNextSibling(ParentNode))
if(((TResultNodeData*)ResultTree->GetNodeData(ParentNode))->Result->GetHashes()->Equals(Result->GetHashes()))
break;
}
if(ParentNode && !ResultTree->HasChildren[ParentNode])
NoOfMultiSourceResults++;
TVirtualNode* Node = ResultTree->AddChild(ParentNode);
TResultNodeData* ResultNodeData = (TResultNodeData*)ResultTree->GetNodeData(Node);
// call constructor using placement new
new(ResultNodeData) TResultNodeData();
ResultNodeData->Result = Result;
if(UseSystemIcons)
ResultNodeData->ImageIndex = MainForm->IconManager->GetIconIndex(Result->FileName);
else
ResultNodeData->ImageIndex = MainForm->IconManager->GetIconIndex(Result->FileType);
ResultNodeData->NetworkImageIndex = MainForm->IconManager->GetNetworkIconIndex(Result->Network);
// normalize availability
ResultNodeData->EffectiveAvail = Result->Availability > 0 ? 1 : 0;
ResultNodeData->TimeFound = TDateTime::CurrentDateTime();
if(ParentNode) {
TResultNodeData* ParentResultNodeData = (TResultNodeData*)ResultTree->GetNodeData(ParentNode);
ResultNodeData->Downloading = ParentResultNodeData->Downloading;
ResultNodeData->LocalFile = ParentResultNodeData->LocalFile;
if(ResultTree->IsVisible[ParentNode] == false)
ResultTree->IsVisible[ParentNode] = !IsNodeFiltered(Node);
// update parent availability
ParentResultNodeData->EffectiveAvail += ResultNodeData->EffectiveAvail > 0 ? 1 : 0;
// add result's ip to parent
ParentResultNodeData->AddIP(Result->GetUser()->GetIP());
} else {
ResultTree->IsVisible[Node] = ShowSingleSources && !IsNodeFiltered(Node);
NoOfUniqueResults++;
// is this a local file?
ResultNodeData->LocalFile = MarkLocalFiles && Engine->GetShares()->GetFileByHashes(Result->GetHashes()) != NULL;
// is this a file we are currently downloading?
ResultNodeData->Downloading = MarkDownloads && Engine->GetDownloadByHashes(Result->GetHashes()) != NULL;
// add ip to ourselves since we are parent
ResultNodeData->AddIP(Result->GetUser()->GetIP());
}
NoOfResults++;
// if new result has meta data and parent node has not exchang the two
TResultNodeData* ParentResultNodeData = (TResultNodeData*)ResultTree->GetNodeData(ParentNode);
if(ParentNode && ParentResultNodeData->Result->MetaData.Empty() &&
!ResultNodeData->Result->MetaData.Empty())
{
// make new node sibling of parent
ResultTree->MoveTo(Node,ParentNode,amInsertAfter,false);
// make all children of parent children of new node
ResultTree->MoveTo(ParentNode,Node,amAddChildFirst,true);
// make parent node child of new node
ResultTree->MoveTo(ParentNode,Node,amAddChildFirst,false);
// HACKHACK: brittle code ahead
// update availability
ResultNodeData->EffectiveAvail = ParentResultNodeData->EffectiveAvail;
ParentResultNodeData->EffectiveAvail = ParentResultNodeData->Result->Availability > 0 ? 1 : 0;
// update IP stuff
#if 0
ResultNodeData->IPs = ParentResultNodeData->IPs;
ResultNodeData->IPCount = ParentResultNodeData->IPCount;
ResultNodeData->UniqueToTotal = ParentResultNodeData->UniqueToTotal;
ParentResultNodeData->IPs.clear();
#endif
}
} // for each result
ResultTree->EndUpdate();
// update results count
ResultCountLbl->Caption = AnsiString::Format("%d / %d", ARRAYOFCONST((NoOfMultiSourceResults, NoOfUniqueResults)));
SearchForm->SetTabCaption(this,TabCaption + AnsiString::Format(" (%d)", ARRAYOFCONST((NoOfResults))));
return true;
}
case CbcSearchEnd:
if(((TSearch*)CbInfo->Data1) != Search)
return false;
StopSearchBtn->Enabled = false;
// enabled search again button if we are below the limit
if(MaxSearchAgainCount == -1 || SearchAgainCount < MaxSearchAgainCount) {
SearchAgainBtn->Enabled = true;
SearchAgainCount++;
}
// update results count
ResultCountLbl->Caption = AnsiString::Format("%d / %d", ARRAYOFCONST((NoOfMultiSourceResults, NoOfUniqueResults)));
SearchForm->SetTabCaption(this,TabCaption + AnsiString::Format(" (%d)", ARRAYOFCONST((NoOfResults))));
return true;
}
return false;
}
//---------------------------------------------------------------------------
void __fastcall TSearchResultForm::CloseSearchBtnClick(TObject *Sender)
{
Engine->RemoveSearch(Search); // may send us cached results
ResultTree->Clear();
SearchForm->CloseSearch(this);
}
void __fastcall TSearchResultForm::StopSearchBtnClick(TObject *Sender)
{
Search->Cancel();
}
void __fastcall TSearchResultForm::SearchAgainBtnClick(TObject *Sender)
{
Search->Start();
SearchAgainBtn->Enabled = false;
}
void __fastcall TSearchResultForm::DownloadBtnClick(
TObject *Sender)
{
DownloadMnuClick(NULL);
}
void __fastcall TSearchResultForm::ChangeHeaderBtnClick(TObject *Sender)
{
TPoint Pt = TPoint(ChangeHeaderBtn->Left + ChangeHeaderBtn->Width,
ChangeHeaderBtn->Top + ChangeHeaderBtn->Height);
Pt = ChangeHeaderBtn->Parent->ClientToScreen(Pt);
ResultTreeHeaderPopup->PopupComponent = ResultTree;
ResultTreeHeaderPopup->Alignment = paRight;
ResultTreeHeaderPopup->Popup(Pt.x,Pt.y);
}
//---------------------------------------------------------------------------
void __fastcall TSearchResultForm::ResultTreeFreeNode(
TBaseVirtualTree *Sender, PVirtualNode Node)
{
// call TResultNodeData destructor
((TResultNodeData*)Sender->GetNodeData(Node))->~TResultNodeData();
}
//---------------------------------------------------------------------------
void __fastcall TSearchResultForm::ResultTreeGetText(
TBaseVirtualTree *Sender, PVirtualNode Node, TColumnIndex Column,
TVSTTextType TextType, WideString &CellText)
{
if(TextType != ttNormal)
return;
TSearchResult* Result = ((TResultNodeData*)Sender->GetNodeData(Node))->Result;
switch(Column) {
case 0: // file name
CellText = Result->FileName.c_str(); break;
case 1: // media type
switch(Result->FileType) {
// TRANSLATOR: Media type in search results.
case FTAudio: CellText = _("Audio"); break;
// TRANSLATOR: Media type in search results.
case FTVideo: CellText = _("Video"); break;
// TRANSLATOR: Media type in search results.
case FTImage: CellText = _("Image"); break;
// TRANSLATOR: Media type in search results.
case FTDocument: CellText = _("Document"); break;
// TRANSLATOR: Media type in search results.
case FTSoftware: CellText = _("Software"); break;
// TRANSLATOR: Media type in search results.
case FTTorrent: CellText = _("Torrent"); break;
// TRANSLATOR: Media type in search results.
case FTUnknown: CellText = _("Unknown"); break;
}
break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -