namespace GUI { static void match_entry(const char* key, void* value, struct Completer* self) { const char* possible_text; possible_text = key; if(!self->fEntryNeedle || strncmp(possible_text, self->fEntryNeedle, strlen(self->fEntryNeedle)) == 0) /* match */ { (*self->fMatches)[GCx_strdup(key)] = NULL; } } static void Completer_accept_match(struct Completer* self, const char* match, bool B_automatic_space) { const char* entry_text; char* new_text; int i; int pos; entry_text = self->fEntryText; if(!self->fEntryNeedle || !entry_text || !match) /* huh */ return; new_text = (char*) calloc(1, strlen(entry_text) + strlen(match) + 2); /* too much */ // FIXME for(i = 0; i < self->fEntryNeedlePos; ++i) new_text[i] = entry_text[i]; /* i = self->fEntryNeedlePos */ new_text[i] = 0; strcat(new_text, match); while(entry_text[i] && Scanners::symbol_char_P(entry_text[i])) ++i; if(B_automatic_space && entry_text[i] != ' ') strcat(new_text, " "); /* TODO remove? */ pos = strlen(new_text); strcat(new_text, &entry_text[i]); Completer_accept_match_GUI(self, new_text, pos); } static bool in_all_keys_P(Values::Hashtable& keys, int i, char c) { Values::Hashtable::const_iterator end_iter = keys.end(); for(Values::Hashtable::const_iterator iter = keys.begin(); iter != end_iter; ++iter) { const char* key = iter->first; if(key[i] != c) return(false); } return(true); } static const char* strrchrset(const char* haystack, const char* needles, const char* frontier) { const char* match = NULL; for(; *haystack && haystack < frontier; ++haystack) if(strchr(needles, *haystack)) match = haystack; return(match); } static void Completer_complete_internal(struct Completer* self, const char* entry_text, int pos) { const char* entry_needle; self->fMatches->clear(); if(self->fEntryNeedle) { //free(self->fEntryNeedle); self->fEntryNeedle = NULL; } /* TODO take caret position into account */ entry_needle = strrchrset(entry_text, " ()\"", entry_text + pos); /* TODO only the stuff BEFORE the cursor */ if(!entry_needle) entry_needle = entry_text; else ++entry_needle; self->fEntryNeedle = GCx_strdup(entry_needle); self->fEntryNeedle[pos - (entry_needle - entry_text)] = 0; // cut afterwards! zB pos==108, fEntryNeedle="small" self->fEntryNeedlePos = entry_needle - entry_text; #ifdef _WIN32 { Values::Hashtable::const_iterator end_iter = self->fHaystack->end(); for(Values::Hashtable::const_iterator iter = self->fHaystack->begin(); iter != end_iter; ++iter) match_entry(iter->first, NULL, self); } #else g_hash_table_foreach(self->fHaystack, (GHFunc) match_entry, self); #endif if(self->fMatches->size() <= 1) { /* unambiguous match or non-match */ const char* key = !self->fMatches->empty() ? self->fMatches->begin()->first : NULL; if(key) Completer_accept_match(self, key, TRUE); } else { /* ambiguous */ /*assert(g_hash_table_size(self->fMatches) >= 2);*/ char common_prefix[100]; const char* firstKeyName = !self->fMatches->empty() ? self->fMatches->begin()->first : NULL; int i; /* TODO show combo box, if that's actually needed... */ for(i = 0; i < 100 - 1 && firstKeyName[i]; ++i) { if(in_all_keys_P(*self->fMatches, i, firstKeyName[i])) common_prefix[i] = firstKeyName[i]; else break; } common_prefix[i] = 0; if(common_prefix[0]) Completer_accept_match(self, common_prefix, FALSE); } } }; /* end namespace GUI */