ultimatepp/uppsrc/ide/Navigator.cpp
Mirek Fidler 34ff691308 sizeof(wchar) is changed to 4 (32 bits) to support non BMP unicode characters
This might bring some incompatibilities in the code that expects wchar to be 16 bit, which
  escpecially involves dealing with Win32 (and to lesser extend MacOS) APIs, so if your application
  is doing that, please check all instances of WCHAR (UniChar on MacOS) or even wchar
  especially type casts.

  To support host APIs, char16 is introduced (but there is no 16-bit String varian).

  Use ToSystemCharsetW, FromSystemCharsetW to convert texts to Win32 API.

- Support of drawing non-BMP characters in GUI
- Vastly improved character font replacement code (when drawing characters missing with requested font, replacement font is used)
- Last instances of Win32 ANSI calls (those ending with A) are removed
- UTF handling routines are refactored and their's naming is unified
- RTF is now being able to handle non-BMP characters (RTF is used as clipboard format for RichText)

Other minor changes:

- fixed TryRealloc issue
- improved MemoryCheck
- Removed MemoryAlloc48/MemoryFree48
- In theide Background parsing should less often cause delays in the main thread
2021-12-02 12:03:19 +01:00

752 lines
20 KiB
C++

#include "ide.h"
#define LTIMING(x) // RTIMING(x)
String FormatNest(String nest)
{
int q = nest.Find("@");
if(q >= 0) {
nest.Trim(q);
nest << "[anonymous]";
}
return nest;
}
int CharFilterNavigator(int c)
{
return c == ':' ? '.' : IsAlNum(c) || c == '_' || c == '.' ? c : 0;
}
void PaintTeXt(Draw& w, int& x, int y, const String& text, Font font, Color ink)
{
static int maxascent = MaxAscent(StdFont());
Size sz = GetTextSize(text, font);
w.DrawText(x, y + maxascent - font.GetAscent(), text, font, ink);
x += sz.cx;
}
int DrawFileName0(Draw& w, const Rect& r, const String& h, Color ink, int x)
{
if(h.GetCount() == 0)
return 0;
int q = h.Find("\xff");
String ns;
String fn = h;
if(q >= 0) {
ns = h.Mid(0, q) + ' ';
fn = h.Mid(q + 1);
}
String s = GetFileName(GetFileFolder(h)) + "/";
x += r.left;
if(ns.GetCount()) {
PaintTeXt(w, x, r.top, ns, StdFont().Bold(), ink);
PaintTeXt(w, x, r.top, "(", StdFont(), ink);
}
PaintTeXt(w, x, r.top, s, StdFont(), ink);
s = GetFileName(h);
PaintTeXt(w, x, r.top, s, StdFont().Bold(), ink);
if(ns.GetCount())
PaintTeXt(w, x, r.top, ")", StdFont(), ink);
return x - r.left;
}
Size GetDrawFileNameSize(const String& h)
{
NilDraw w;
return Size(DrawFileName0(w, Size(999999, 999999), h, Null, 0), StdFont().Bold().GetCy());
}
void DrawFileName(Draw& w, const Rect& r, const String& h, Color ink)
{
DrawFileName0(w, r, h, ink, min(r.GetWidth() - GetDrawFileNameSize(h).cx, 0));
}
int PaintFileName(Draw& w, const Rect& r, String h, Color ink)
{
if(*h == '\xff')
h.Remove(0, 1);
return DrawFileName0(w, r, h, ink, 0);
}
Navigator::Navigator()
: navidisplay(litem)
{
scope_display.navigator = this;
scope.NoHeader();
scope.AddColumn().SetDisplay(scope_display);
scope.NoWantFocus();
scope.WhenSel = THISBACK(Scope);
scope.WhenLeftDouble = THISBACK(ScopeDblClk);
list.NoHeader();
list.AddRowNumColumn().SetDisplay(navidisplay);
list.SetLineCy(max(16, GetStdFontCy()));
list.NoWantFocus();
list.WhenLeftClick = THISBACK(NavigatorClick);
list.WhenSel = THISBACK(SyncNavLines);
list.WhenLineEnabled = THISBACK(ListLineEnabled);
navlines.NoHeader().NoWantFocus();
navlines.WhenLeftClick = THISBACK(GoToNavLine);
navlines.AddColumn().SetDisplay(Single<LineDisplay>());
search <<= THISBACK(TriggerSearch);
search.SetFilter(CharFilterNavigator);
search.WhenEnter = THISBACK(NavigatorEnter);
sortitems.Image(BrowserImg::Sort());
sortitems <<= THISBACK(NaviSort);
sorting = false;
dlgmode = false;
}
void Navigator::SyncCursor()
{
String k = "(" + GetKeyDesc(IdeKeys::AK_GOTO().key[0]) + ") ";
search.NullText("Symbol/lineno " + k);
search.Tip(IsNull(search) ? String() : "Clear " + k);
if(!navigating && theide->editfile.GetCount()) {
navlines.KillCursor();
int q = linefo.Find(GetSourceFileIndex(theide->editfile));
if(q < 0)
return;
navigating = true;
SortedVectorMap<int, int>& m = linefo[q];
q = m.FindUpperBound(GetCurrentLine() + 1) - 1;
if(q >= 0 && q < m.GetCount())
list.SetCursor(m[q]);
navigating = false;
}
SyncLines();
if(scope.IsCursor())
scope.RefreshRow(scope.GetCursor());
}
void Navigator::SyncLines()
{
if(IsNull(theide->editfile) || navigating)
return;
int ln = GetCurrentLine() + 1;
int fi = GetSourceFileIndex(theide->editfile);
int q = -1;
for(int i = 0; i < navlines.GetCount(); i++) {
const NavLine& l = navlines.Get(i, 0).To<NavLine>();
if(l.file == fi && l.line <= ln && i < navlines.GetCount())
q = i;
}
if(dlgmode)
navlines.GoBegin();
else
if(q >= 0)
navlines.SetCursor(q);
}
void Navigator::SyncNavLines()
{
int sc = navlines.GetScroll();
navlines.Clear();
int ii = list.GetCursor();
if(ii >= 0 && ii < litem.GetCount()) {
Vector<NavLine> l = GetNavLines(*litem[ii]);
for(int i = 0; i < l.GetCount(); i++) {
String p = GetSourceFilePath(l[i].file);
navlines.Add(RawToValue(l[i]));
}
navlines.ScrollTo(sc);
SyncLines();
}
}
int Navigator::LineDisplay::DoPaint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style, int x) const
{
w.DrawRect(r, paper);
const NavLine& l = q.To<NavLine>();
x += r.left;
String p = GetSourceFilePath(l.file);
int y = r.top + (r.GetHeight() - StdFont().GetCy()) / 2;
PaintTeXt(w, x, y, GetFileName(GetFileFolder(p)) + "/", StdFont(), ink);
PaintTeXt(w, x, y, GetFileName(p), StdFont().Bold(), ink);
PaintTeXt(w, x, y, " (", StdFont(), ink);
PaintTeXt(w, x, y, AsString(l.line), StdFont().Bold(), ink);
PaintTeXt(w, x, y, ")", StdFont(), ink);
return x - r.left;
}
void Navigator::LineDisplay::Paint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style) const
{
DoPaint(w, r, q, ink, paper, style, min(r.GetWidth() - GetStdSize(q).cx, 0));
}
Size Navigator::LineDisplay::GetStdSize(const Value& q) const
{
NilDraw w;
return Size(DoPaint(w, Size(999999, 999999), q, White(), White(), 0, 0), StdFont().Bold().GetCy());
}
void Navigator::GoToNavLine()
{
if(dlgmode)
return;
int ii = navlines.GetClickPos().y;
if(ii >= 0 && ii < navlines.GetCount() && theide) {
const NavLine& l = navlines.Get(ii, 0).To<NavLine>();
theide->GotoPos(GetSourceFilePath(l.file), l.line);
}
}
bool Navigator::NavLine::operator<(const NavLine& b) const
{
String p1 = GetSourceFilePath(file);
String p2 = GetSourceFilePath(b.file);
return CombineCompare/*(!impl, !b.impl)*/
(GetFileExt(p2), GetFileExt(p1)) // .h > .c
(GetFileName(p1), GetFileName(p2))
(p1, p2)
(line, b.line) < 0;
}
Vector<Navigator::NavLine> Navigator::GetNavLines(const NavItem& m)
{
CodeBaseLock __;
Vector<NavLine> l;
int q = CodeBase().Find(m.nest);
if(q < 0 || IsNull(m.qitem))
return l;
const Array<CppItem>& a = CodeBase()[q];
for(int i = 0; i < a.GetCount(); i++) {
const CppItem& mm = a[i];
if(mm.qitem == m.qitem) {
NavLine& nl = l.Add();
nl.impl = mm.impl;
nl.file = mm.file;
nl.line = mm.line;
}
}
Sort(l);
return l;
}
void Navigator::Navigate()
{
if(navigating)
return;
navigating = true;
int ii = list.GetCursor();
if(theide && ii >= 0 && ii < litem.GetCount()) {
int ln = GetCurrentLine() + 1;
const NavItem& m = *litem[ii];
if(m.kind == KIND_LINE || IsNull(search)) {
theide->GotoPos(Null, m.line);
if(m.kind == KIND_LINE) { // Go to line - restore file view
search.Clear();
Search();
navigating = false;
}
SyncCursor();
}
else
if(m.kind == KIND_SRCFILE) {
theide->AddHistory();
theide->EditFile(m.ptype);
theide->AddHistory();
}
else {
Vector<NavLine> l = GetNavLines(m);
int q = l.GetCount() - 1;
for(int i = 0; i < l.GetCount(); i++)
if(GetSourceFilePath(l[i].file) == NormalizeSourcePath(theide->editfile) && l[i].line == ln) {
q = (i + l.GetCount() + 1) % l.GetCount();
break;
}
if(q >= 0 && q < l.GetCount()) {
String path = GetSourceFilePath(l[q].file);
if(!theide->GotoDesignerFile(path, m.nest, m.name, l[q].line))
theide->GotoPos(path, l[q].line);
}
}
}
navigating = false;
}
void Navigator::ScopeDblClk()
{
if(!scope.IsCursor())
return;
String h = scope.GetKey();
if((byte)*h == 0xff)
theide->GotoPos(h.Mid(1), 1);
else {
list.GoBegin();
Navigate();
}
}
void Navigator::NavigatorClick()
{
if(dlgmode)
return;
int q = list.GetClickPos().y;
if(q >= 0 && q < list.GetCount())
Navigate();
}
void Navigator::NavigatorEnter()
{
if(list.GetCount()) {
list.GoBegin();
Navigate();
}
}
void Ide::ToggleNavigator()
{
editor.Navigator(!editor.navigator);
}
void Ide::SearchCode()
{
if(!editor.navigator)
editor.Navigator(true);
if(!IsNull(~editor.search)) {
editor.search.Clear();
editor.Search();
editor.SetFocus();
}
else {
String h = editor.GetWord();
if(h.GetCount()) {
editor.search <<= h;
editor.search.SelectAll();
editor.Search();
}
editor.search.SetFocus();
}
}
void Ide::SwitchHeader() {
int c = filelist.GetCursor();
if(c < 0) return;
String currfile = filelist[c];
const char *ext = GetFileExtPos(currfile);
if(!stricmp(ext, ".h") || !stricmp(ext, ".hpp")
|| !stricmp(ext, ".lay") || !stricmp(ext, ".iml")) {
int f = filelist.Find(ForceExt(currfile, ".cpp"));
if(f < 0) f = filelist.Find(ForceExt(currfile, ".c"));
if(f < 0) f = filelist.Find(ForceExt(currfile, ".cc"));
if(f >= 0) filelist.SetCursor(f);
}
}
void Navigator::NavItem::Set(const CppItem& m)
{
qitem = m.qitem;
name = m.name;
uname = m.uname;
natural = m.natural;
type = m.type;
pname = m.pname;
ptype = m.ptype;
tname = m.tname;
ctname = m.ctname;
access = m.access;
kind = m.kind;
at = m.at;
line = m.line;
file = m.file;
impl = m.impl;
pass = false;
}
void Navigator::NavigatorDisplay::PaintBackground(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style) const
{
int ii = q;
if(ii < 0 || ii >= item.GetCount())
return;
const NavItem& m = *item[ii];
bool focuscursor = (style & (FOCUS|CURSOR)) == (FOCUS|CURSOR) || (style & SELECT);
if(findarg(m.kind, KIND_FILE, KIND_NEST) >= 0)
w.DrawRect(r, focuscursor ? paper : m.kind == KIND_NEST ? Blend(SColorMark, SColorPaper, 220)
: SColorFace);
else
w.DrawRect(r, paper);
}
int Navigator::NavigatorDisplay::DoPaint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style) const
{
int ii = q;
if(ii < 0 || ii >= item.GetCount())
return 0;
const NavItem& m = *item[ii];
bool focuscursor = (style & (FOCUS|CURSOR)) == (FOCUS|CURSOR) || (style & SELECT);
int x = r.left;
int ry = r.top + r.GetHeight() / 2;
int y = ry - Draw::GetStdFontCy() / 2;
if(findarg(m.kind, KIND_FILE, KIND_NEST) >= 0) {
w.DrawRect(r, focuscursor ? paper : m.kind == KIND_NEST ? Blend(SColorMark, SColorPaper, 220)
: SColorFace);
if(findarg(m.kind, KIND_FILE) >= 0)
return PaintFileName(w, r, m.type, ink);
String h = FormatNest(m.type);
w.DrawText(x, y, h, StdFont().Bold(), ink);
return GetTextSize(h, StdFont().Bold()).cx;
}
w.DrawRect(r, paper);
if(m.kind == KIND_SRCFILE)
return PaintFileName(w, r, m.type, ink);
if(m.kind == KIND_LINE) {
w.DrawText(x, y, m.type, StdFont().Bold(), ink);
return GetTextSize(m.type, StdFont().Bold()).cx;
}
PaintCppItemImage(w, x, ry, m.access, m.kind, focuscursor);
x += Zx(15);
Vector<ItemTextPart> n = ParseItemNatural(m.name, m.natural, m.ptype, m.pname, m.type,
m.tname, m.ctname, ~m.natural + m.at);
int starti = 0;
for(int i = 0; i < n.GetCount(); i++)
if(n[i].type == ITEM_NAME) {
starti = i;
break;
}
PaintText(w, x, y, m.natural, n, starti, n.GetCount(), focuscursor, ink, false);
if(starti) {
const char *h = " : ";
w.DrawText(x, y, h, BrowserFont(), SColorText);
x += GetTextSize(h, BrowserFont()).cx;
}
PaintText(w, x, y, m.natural, n, 0, starti, focuscursor, ink, false);
return x;
}
void Navigator::NavigatorDisplay::Paint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style) const
{
DoPaint(w, r, q, ink, paper, style);
}
Size Navigator::NavigatorDisplay::GetStdSize(const Value& q) const
{
NilDraw w;
return Size(DoPaint(w, Size(999999, 999999), q, White(), White(), 0), Draw::GetStdFontCy());
}
void Navigator::TriggerSearch()
{
search_trigger.KillSet(100, THISBACK(Search));
}
void Navigator::NavGroup(bool local)
{
CodeBaseLock __;
for(int i = 0; i < nitem.GetCount(); i++) {
NavItem& m = nitem[i];
String g = m.nest;
if(m.kind == TYPEDEF)
g.Trim(max(g.ReverseFind("::"), 0));
if(IsNull(g) || CodeBase().namespaces.Find(m.nest) >= 0) {
if(g.GetCount()) // We want to show the namespace
g << '\xff';
else
g.Clear();
g << GetSourceFilePath(m.decl_file);
g = '\xff' + g;
}
if(!local)
g = (char)(m.pass + 10) + g;
if(local)
if(gitem.GetCount() && gitem.TopKey() == g)
gitem.Top().Add(&m);
else
gitem.Add(g).Add(&m);
else
gitem.GetAdd(g).Add(&m);
}
}
force_inline
bool Navigator::SortByLines(const NavItem *a, const NavItem *b)
{
return CombineCompare(a->decl_file, b->decl_file)(a->decl_line, b->decl_line) < 0;
}
force_inline
bool Navigator::SortByNames(const NavItem *a, const NavItem *b)
{
return CombineCompare(a->name, b->name)(a->qitem, b->qitem) < 0;
}
void Navigator::Search()
{
CodeBaseLock __;
sortitems.Check(sorting);
int sc = scope.GetScroll();
String key = scope.GetKey();
String s = TrimBoth(~search);
String search_name, search_nest;
bool wholeclass = false;
bool both = false;
navigator_global = false;
if(s.Find('.') >= 0) {
Vector<String> h = Split((String)~search, '.');
if(*s.Last() == '.')
search_nest = Join(h, "::");
else {
search_name = h.Pop();
if(h.GetCount())
search_nest = Join(h, "::");
}
wholeclass = *s == '.' && search_nest.GetCount();
}
else {
search_name = search_nest = ~search;
both = true;
}
s = Join(Split(s, '.'), "::") + (s.EndsWith(".") ? "::" : "");
int lineno = StrInt(s);
gitem.Clear();
nitem.Clear();
if(IsNull(theide->editfile))
return;
int fileii = GetSourceFileIndex(theide->editfile);
if(!IsNull(lineno)) {
NavItem& m = nitem.Add();
m.type = "Go to line " + AsString(lineno);
m.kind = KIND_LINE;
m.line = lineno;
gitem.Add(Null).Add(&m);
}
else
if(IsNull(s) && !sorting) {
const CppBase& b = CodeBase();
bool sch = GetFileExt(theide->editfile) == ".sch";
for(int i = 0; i < b.GetCount(); i++) {
String nest = b.GetKey(i);
const Array<CppItem>& ci = b[i];
for(int j = 0; j < ci.GetCount(); j++) {
const CppItem& m = ci[j];
if(m.file == fileii && (!sch || !m.IsData() || m.type != "SqlId")) {
NavItem& n = nitem.Add();
n.Set(m);
n.nest = nest;
n.decl_line = m.line;
n.decl_file = m.file;
n.decl = !m.impl;
NavLine& l = n.linefo.Add();
l.impl = m.impl;
l.file = m.file;
l.line = m.line;
}
}
}
Sort(nitem, FieldRelation(&NavItem::line, StdLess<int>()));
NavGroup(true);
}
else {
navigator_global = true;
const CppBase& b = CodeBase();
String usearch_nest = ToUpper(search_nest);
String usearch_name = ToUpper(search_name);
ArrayMap<String, NavItem> imap;
bool local = sorting && IsNull(s);
VectorMap<String, int> nest_pass;
for(int pass = -1; pass < 2; pass++) {
for(int i = 0; i < b.GetCount(); i++) {
String nest = b.GetKey(i);
bool foundnest = (wholeclass ? pass < 0 ? false :
pass ? ToUpper(nest) == usearch_nest
: nest == search_nest
: pass < 0 ? nest == search_nest :
(pass ? ToUpper(nest).Find(usearch_nest) >= 0
: nest.StartsWith(search_nest)))
&& nest.Find('@') < 0;
if(local || foundnest || both) {
const Array<CppItem>& ci = b[i];
for(int j = 0; j < ci.GetCount(); j++) {
const CppItem& m = ci[j];
if(local ? m.file == fileii
: m.uname.Find('@') < 0 && (pass < 0 ? m.name == search_name :
pass ? m.uname.Find(usearch_name) >= 0
: m.name.StartsWith(search_name))
|| both && foundnest) {
String key = nest + '\1' + m.qitem;
int q = nest_pass.Find(nest);
int p = pass;
if(q < 0) // We do not want classes to be split based on pass
nest_pass.Add(nest, pass);
else
p = nest_pass[q];
q = imap.Find(key);
if(q < 0) {
NavItem& ni = imap.Add(key);
ni.Set(m);
ni.nest = nest;
ni.decl_line = ni.line;
ni.decl_file = ni.file;
ni.decl = !ni.impl;
ni.pass = p;
NavLine& l = ni.linefo.Add();
l.impl = m.impl;
l.file = m.file;
l.line = m.line;
}
else {
NavItem& mm = imap[q];
if(!m.impl &&
(!mm.decl
|| CombineCompare(mm.decl_file, m.file)(mm.decl_line, m.line) < 0)) {
mm.decl = true;
mm.decl_line = m.line;
mm.decl_file = m.file;
mm.natural = m.natural;
}
NavLine& l = mm.linefo.Add();
l.impl = m.impl;
l.file = m.file;
l.line = m.line;
}
}
}
}
}
}
nitem = imap.PickValues();
NavGroup(false);
SortByKey(gitem);
Vector<String> keys = gitem.PickKeys();
Vector<Vector<NavItem *> > values = gitem.PickValues();
IndexSort(keys, values);
for(int i = 0; i < keys.GetCount(); i++)
keys[i].Remove(0);
VectorMap<String, Vector<NavItem *> > h(pick(keys), pick(values));
gitem = pick(h);
for(int i = 0; i < gitem.GetCount(); i++)
Sort(gitem[i], sorting ? SortByNames : SortByLines);
const Workspace& wspc = GetIdeWorkspace();
String s = ToUpper(TrimBoth(~search));
for(int i = 0; i < wspc.GetCount(); i++) {
const Package& p = wspc.GetPackage(i);
for(int j = 0; j < p.GetCount(); j++) {
if(!p[j].separator && ToUpper(p[j]).Find(s) >= 0) {
NavItem& m = nitem.Add();
m.kind = KIND_SRCFILE;
m.type = wspc[i] + "/" + p[j];
m.ptype = SourcePath(wspc[i], p[j]);
m.line = 0;
gitem.GetAdd("<files>").Add(&m);
}
}
}
}
scope.Clear();
scope.Add(Null);
Index<String> done;
for(int i = 0; i < gitem.GetCount(); i++) {
String s = gitem.GetKey(i);
if(done.Find(s) < 0) {
done.Add(s);
scope.Add(s);
}
}
scope.ScrollTo(sc);
if(!navigator_global || !scope.FindSetCursor(key))
scope.GoBegin();
}
int Navigator::ScopeDisplay::DoPaint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style) const
{
w.DrawRect(r, paper);
if(IsNull(q)) {
const char *txt = "* ";
int x = 0;
w.DrawText(r.left, r.top, txt, StdFont().Bold().Italic(),
style & CURSOR ? ink : HighlightSetup::GetHlStyle(HighlightSetup::INK_KEYWORD).color);
x += GetTextSize(txt, StdFont().Bold().Italic()).cx;
int ii = navigator->list.GetCursor();
if(ii >= 0 && ii < navigator->litem.GetCount()) {
const NavItem& m = *navigator->litem[ii];
String txt = m.nest;
if(IsCppCode(m.kind))
txt << "::" << m.name;
w.DrawText(r.left + x, r.top, txt, StdFont().Bold(), ink);
x += GetTextSize(txt, StdFont().Bold()).cx;
}
return x;
}
String h = q;
if(*h == '\xff')
return PaintFileName(w, r, h, ink);
else
h = FormatNest(h);
w.DrawText(r.left, r.top, h, StdFont(), ink);
return GetTextSize(h, StdFont()).cx;
}
void Navigator::ScopeDisplay::Paint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style) const
{
DoPaint(w, r, q, ink, paper, style);
}
Size Navigator::ScopeDisplay::GetStdSize(const Value& q) const
{
NilDraw w;
return Size(DoPaint(w, Size(999999, 999999), q, White(), White(), 0), StdFont().Bold().GetCy());
}
void Navigator::Scope()
{
LTIMING("FINALIZE");
litem.Clear();
nest_item.Clear();
linefo.Clear();
bool all = scope.GetCursor() <= 0;
String sc = scope.GetKey();
for(int i = 0; i < gitem.GetCount(); i++) {
String grp = gitem.GetKey(i);
int kind = KIND_NEST;
if(*grp == '\xff')
kind = KIND_FILE;
if(all) {
NavItem& m = nest_item.Add();
m.kind = kind;
m.type = FormatNest(grp);
litem.Add(&m);
}
else
if(grp != sc)
continue;
const Vector<NavItem *>& ia = gitem[i];
for(int i = 0; i < ia.GetCount(); i++) {
NavItem *m = ia[i];
for(int j = 0; j < m->linefo.GetCount(); j++)
linefo.GetAdd(m->linefo[j].file).Add(m->linefo[j].line, litem.GetCount());
litem.Add(m);
}
}
int lsc = list.GetScroll();
list.Clear();
list.SetVirtualCount(litem.GetCount());
list.ScrollTo(lsc);
}
void Navigator::ListLineEnabled(int i, bool& b)
{
if(i >= 0 && i < litem.GetCount()) {
int kind = litem[i]->kind;
if(findarg(kind, KIND_FILE, KIND_NEST) >= 0)
b = false;
}
}
void Navigator::NaviSort()
{
sorting = !sorting;
Search();
}