/* i_complete.cpp * * Copyright (c) 1998, Zdenek Kabelac * * You may distribute under the terms of either the GNU General Public * License or the Artistic License, as specified in the README file. * */ #include "fte.h" #ifdef CONFIG_I_COMPLETE #define STRCOMPLETE "Complete Word: [" #define STRNOCOMPLETE "No word for completition..." #define LOCALE_SORT #ifdef LOCALE_SORT #if defined(__IBMCPP__) static int _LNK_CONV CmpStr(const void *p1, const void *p2) { #else static int CmpStr(const void *p1, const void *p2) { #endif //printf("%s %s %d\n", *(char **)p1, *(char **)p2, // strcoll(*(char **)p1, *(char **)p2)); return strcoll(*(char **)p1, *(char **)p2); } #endif /** * Create Sorted list of possible word extensions */ ExComplete::ExComplete(EBuffer *B): ExView() { Buffer = B; Orig = Buffer->CP; WordBegin = NULL; WordFixed = WordPos = WordsLast = 0; Words = new char *[MAXCOMPLETEWORDS + 2]; if (Words != NULL) RefreshComplete(); } ExComplete::~ExComplete() { // fprintf(stderr, "W %p %p %p %d\n", Words, WordContinue, WordBegin, WordsLast); if (WordBegin != NULL) delete WordBegin; if (Words != NULL) { for(int i = 0; i < WordsLast; i++) delete Words[i]; delete Words; } } void ExComplete::Activate(int gotfocus) { ExView::Activate(gotfocus); } int ExComplete::BeginMacro() { return 1; } void ExComplete::HandleEvent(TEvent &Event) { unsigned long kb = kbCode(Event.Key.Code); int DoQuit = 0; int i = 0; if (WordsLast < 2) { if ((WordsLast == 1) && (kb != kbEsc)) { DoQuit = 1; } else { EndExec(0); Event.What = evNone; } } else if (Event.What == evKeyDown) { switch(kb) { case kbPgUp: case kbLeft: // if there would not be locale sort, we could check only // the next string, but with `locale sort` this is impossible!! // this loop is little inefficient but it's quite short & nice for (i = WordPos; i-- > 0;) if (strncmp(Words[WordPos], Words[i], WordFixed) == 0) { WordPos = i; break; } Event.What = evNone; break; case kbPgDn: case kbRight: for(i = WordPos; i++ < WordsLast - 1;) if (strncmp(Words[WordPos], Words[i], WordFixed) == 0) { WordPos = i; break; } Event.What = evNone; break; case kbHome: for (i = 0; i < WordPos; i++) if (strncmp(Words[WordPos], Words[i], WordFixed) == 0) WordPos = i; Event.What = evNone; break; case kbEnd: for (i = WordsLast - 1; i > WordPos; i--) if (strncmp(Words[WordPos], Words[i], WordFixed) == 0) WordPos = i; Event.What = evNone; break; case kbTab: while (WordPos < WordsLast - 1) { WordPos++; if (strncmp(Words[WordPos], Words[WordPos - 1], WordFixed + 1)) break; } Event.What = evNone; break; case kbTab | kfShift: while (WordPos > 0) { WordPos--; if (strncmp(Words[WordPos], Words[WordPos + 1], WordFixed + 1)) break; } Event.What = evNone; break; case kbIns: case kbUp: FixedUpdate(1); Event.What = evNone; break; case kbBackSp: case kbDel: case kbDown: FixedUpdate(-1); Event.What = evNone; break; case kbEsc: EndExec(0); Event.What = evNone; break; case kbEnter: case kbSpace: case kbTab | kfCtrl: DoQuit = 1; break; default: if (CheckASCII(Event.Key.Code)) { char *s = new char[WordFixed + 2]; if (s != NULL) { if (WordFixed > 0) strncpy(s, Words[WordPos], WordFixed); s[WordFixed] = (unsigned char)(Event.Key.Code & 0xFF); s[WordFixed + 1] = 0; for (int i = 0; i < WordsLast; i++) if (strncmp(s, Words[i], WordFixed + 1) == 0) { WordPos = i; if (WordFixedCount == 1) DoQuit = 1; else FixedUpdate(1); break; } delete s; } Event.What = evNone; } break; } } if (DoQuit) { int rc = 0; int l = strlen(Words[WordPos]); if (Buffer->InsText(Buffer->VToR(Orig.Row), Orig.Col, l, Words[WordPos], 1) && Buffer->SetPos(Orig.Col + l, Orig.Row)) { Buffer->Draw(Buffer->VToR(Orig.Row), Buffer->VToR(Orig.Row)); rc = 1; } EndExec(rc); Event.What = evNone; } } void ExComplete::UpdateView() { if (Next) { Next->UpdateView(); } } void ExComplete::RepaintView() { if (Next) { Next->RepaintView(); } } void ExComplete::UpdateStatus() { RepaintStatus(); } void ExComplete::RepaintStatus() { TDrawBuffer B; int W, H; // Currently use this fixed colors - maybe there are some better defines?? #define COM_NORM 0x17 #define COM_ORIG 0x1C #define COM_HIGH 0x1E #define COM_MARK 0x2E #define COM_ERR 0x1C ConQuerySize(&W, &H); MoveCh(B, ' ', COM_NORM, W); if ((WordsLast > 0) && (WordBegin != NULL) && (Words != NULL) && (Words[WordPos]) != NULL) { const char *sc = STRCOMPLETE; int p = sizeof(STRCOMPLETE) - 1; if (W < 35) { // if the width is quite small sc += p - 1; // jump to last character p = 1; } MoveStr(B, 0, W, sc, COM_NORM, W); // int cur = p; MoveStr(B, p, W, WordBegin, COM_ORIG, W); p += strlen(WordBegin); int l = strlen(Words[WordPos]); if (WordFixed > 0) { MoveStr(B, p, W, Words[WordPos], COM_MARK, W); p += WordFixed; l -= WordFixed; } MoveStr(B, p, W, Words[WordPos] + WordFixed, (WordFixedCount == 1) ? COM_ORIG : COM_HIGH, W); p += l; char s[100]; sprintf(s, "] (T:%d/%d S:%d)", WordPos + 1, WordsLast, WordFixedCount); MoveStr(B, p, W, s, COM_NORM, W); // ConSetCursorPos(cur + WordFixed, H - 1); } else MoveStr(B, 0, W, STRNOCOMPLETE, COM_ERR, W); ConPutBox(0, H - 1, W, 1, B); ConShowCursor(); } int ExComplete::RefreshComplete() { if ((Buffer->CP.Col == 0) || (Buffer->SetPos(Buffer->CP.Col, Buffer->CP.Row) == 0)) return 0; PELine L = Buffer->VLine(Buffer->CP.Row); int C = Buffer->CP.Col; int P = Buffer->CharOffset(L, C); if (!P || P > L->Count) return 0; int P1 = P; while ((P > 0) && CheckASCII(L->Chars[P - 1])) P--; int wlen = P1 - P; if (!wlen) return 0; WordBegin = new char[wlen + 1]; if (WordBegin == NULL) return 0; strncpy(WordBegin, L->Chars + P, wlen); WordBegin[wlen] = 0; // fprintf(stderr, "Calling %d %s\n", wlen, WordBegin); // Search words in TAGS TagComplete(Words, &WordsLast, MAXCOMPLETEWORDS, WordBegin); // fprintf(stderr, "Located %d words\n", WordsLast); // these words are already sorted // Search words in current TEXT Buffer->Match.Col = Buffer->Match.Row = 0; // this might look strange but it is necessary to catch // the first word at position 0,0 for match :-) unsigned long mask = SEARCH_NOPOS | SEARCH_WORDBEG; while (Buffer->FindStr(L->Chars + P, wlen, mask) == 1) { mask |= SEARCH_NEXT; PELine M = Buffer->RLine(Buffer->Match.Row); int X = Buffer->CharOffset(M, Buffer->Match.Col); if ((L->Chars == M->Chars) && (P == X)) continue; int XL = X; while ((XL < M->Count) && CheckASCII(M->Chars[XL])) XL++; int len = XL - X - wlen; if (len == 0) continue; char *s = new char[len + 1]; if (s != NULL) { strncpy(s, M->Chars + X + wlen, len); s[len] = 0; int c = 1, H = 0, L = 0, R = WordsLast; // using sort to insert only unique words while (L < R) { H = (L + R) / 2; c = strcmp(s, Words[H]); if (c < 0) R = H; else if (c > 0) L = H + 1; else break; } if (c != 0) { // Loop exited without finding the word. Instead, // it found the spot where the new should be inserted. WordsLast++; int i = WordsLast; while (i > L) { Words[i] = Words[i-1]; i--; } Words[i] = s; if (WordsLast >= MAXCOMPLETEWORDS) break; } else { // word was already listed, free duplicate. delete s; } } } Buffer->Match.Row = Buffer->Match.Col = -1; Buffer->MatchLen = Buffer->MatchCount = 0; #ifdef LOCALE_SORT // sort by current locales qsort(Words, WordsLast, sizeof(Words[0]), CmpStr); #endif FixedUpdate(0); //for(int i = 0; i < WordsLast; i++) //if (Words[i]) //fprintf(stderr, "%3d:\t%10p\t%s\n", i, Words[i], Words[i]); //fprintf(stderr, "Words %3d\n", WordsLast); return WordsLast; } void ExComplete::FixedUpdate(int add) { if (add < 0) { if (WordFixed > 0) WordFixed += add; } else if (add > 0) { if (strlen(Words[WordPos]) > WordFixed) WordFixed += add; } if (WordFixed > 0) { WordFixedCount = 0; for(int i = 0; i < WordsLast; i++) if (strncmp(Words[WordPos], Words[i], WordFixed) == 0) WordFixedCount++; } else WordFixedCount = WordsLast; } /* // Well this was my first idea - but these menus are unusable int menu = NewMenu("CW"); int n; n = NewItem(menu, "Word1"); Menus[menu].Items[n].Cmd = ExFind; n = NewItem(menu, "Word2"); Menus[menu].Items[n].Cmd = ExMoveLineStart; n = NewItem(menu, "Word3"); Menus[menu].Items[n].Cmd = ExMovePageEnd; printf(">%d ****\n", View->MView->Win->Parent->PopupMenu("CW")); */ #endif