/* This file is part of the KDE project Copyright (C) 2005 by Tobi Vollebregt Copyright (C) 2004 by Vinay Khaitan Copyright (C) 2004 Arend van Beelen jr. 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. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "searchbar.h" typedef KGenericFactory SearchBarPluginFactory; K_EXPORT_COMPONENT_FACTORY(libsearchbarplugin, SearchBarPluginFactory("searchbarplugin")) SearchBarPlugin::SearchBarPlugin(TQObject *parent, const char *name, const TQStringList &) : KParts::Plugin(parent, name), m_searchCombo(0), m_searchMode(UseSearchProvider), m_urlEnterLock(false), m_gsTimer(this), m_googleMode(GoogleOnly) { m_searchCombo = new SearchBarCombo(0L, "search combo"); m_searchCombo->setDuplicatesEnabled(false); m_searchCombo->setMaxCount(5); m_searchCombo->setFixedWidth(180); m_searchCombo->setLineEdit(new KLineEdit(m_searchCombo)); m_searchCombo->lineEdit()->installEventFilter(this); m_searchCombo->listBox()->setFocusProxy(m_searchCombo); m_popupMenu = 0; m_googleMenu = 0; m_searchComboAction = new KWidgetAction(m_searchCombo, i18n("Search Bar"), 0, 0, 0, actionCollection(), "toolbar_search_bar"); m_searchComboAction->setShortcutConfigurable(false); connect(m_searchCombo, TQT_SIGNAL(activated(const TQString &)), TQT_SLOT(startSearch(const TQString &))); connect(m_searchCombo, TQT_SIGNAL(iconClicked()), TQT_SLOT(showSelectionMenu())); TQWhatsThis::add(m_searchCombo, i18n("Search Bar

" "Enter a search term. Click on the icon to change search mode or provider.")); new TDEAction( i18n( "Focus Searchbar" ), CTRL+Key_S, this, TQT_SLOT(focusSearchbar()), actionCollection(), "focus_search_bar"); configurationChanged(); KParts::MainWindow *mainWin = static_cast(TQT_TQWIDGET(parent)); //Grab the part manager. Don't know of any other way, and neither does Tronical, so.. KParts::PartManager *partMan = static_cast(mainWin->child(0, "KParts::PartManager")); if (partMan) { connect(partMan, TQT_SIGNAL(activePartChanged(KParts::Part*)), TQT_SLOT (partChanged (KParts::Part*))); partChanged(partMan->activePart()); } connect(this, TQT_SIGNAL(gsCompleteDelayed()), TQT_SLOT(gsStartDelay())); connect(&m_gsTimer, TQT_SIGNAL(timeout()), TQT_SLOT(gsMakeCompletionList())); connect(m_searchCombo->listBox(), TQT_SIGNAL(highlighted(const TQString&)), TQT_SLOT(gsSetCompletedText(const TQString&))); connect(m_searchCombo, TQT_SIGNAL(activated(const TQString&)), TQT_SLOT(gsPutTextInBox(const TQString&))); } SearchBarPlugin::~SearchBarPlugin() { TDEConfig *config = kapp->config(); config->setGroup("SearchBar"); config->writeEntry("Mode", (int) m_searchMode); config->writeEntry("CurrentEngine", m_currentEngine); config->writeEntry("GoogleSuggestMode", m_googleMode); delete m_searchCombo; m_searchCombo = 0L; } TQChar delimiter() { TDEConfig config( "kuriikwsfilterrc", true, false ); config.setGroup( "General" ); return config.readNumEntry( "KeywordDelimiter", ':' ); } bool SearchBarPlugin::eventFilter(TQObject *o, TQEvent *e) { if( o==m_searchCombo->lineEdit() && e->type() == TQEvent::KeyPress ) { TQKeyEvent *k = (TQKeyEvent *)e; TQString text = k->text(); if(!text.isEmpty()) { if(k->key() != TQt::Key_Return && k->key() != Key_Enter && k->key() != Key_Escape) { emit gsCompleteDelayed(); } } if(k->state() & ControlButton) { if(k->key()==Key_Down) { nextSearchEntry(); return true; } if(k->key()==Key_Up) { previousSearchEntry(); return true; } } else { if (k->key() == Key_Up || k->key() == Key_Down) { if(m_searchCombo->listBox()->isVisible()) { tqApp->sendEvent(m_searchCombo->listBox(), e); return true; } } } if (k->key() == Key_Enter || k->key() == Key_Return) { /*- Fix a bug which caused the searchbar to search for the completed input instead of the literal input when enter was pressed and the listbox was visible. if(m_searchCombo->listBox()->isVisible()) { tqApp->sendEvent(m_searchCombo->listBox(),e); }*/ } if (k->key() == Key_Escape) { m_searchCombo->listBox()->hide(); if (m_searchCombo->lineEdit()->hasSelectedText()) { m_searchCombo->lineEdit()->setText(m_searchCombo->currentText().left(m_searchCombo->lineEdit()->selectionStart())); } m_gsTimer.stop(); } } return false; } void SearchBarPlugin::nextSearchEntry() { if(m_searchMode == FindInThisPage) { m_searchMode = UseSearchProvider; if(m_searchEngines.count()) { m_currentEngine = *m_searchEngines.at(0); } else { m_currentEngine = "google"; } } else { TQStringList::ConstIterator it = m_searchEngines.find(m_currentEngine); it++; if(it==m_searchEngines.end()) { m_searchMode = FindInThisPage; } else { m_currentEngine = *it; } } setIcon(); } void SearchBarPlugin::previousSearchEntry() { if(m_searchMode == FindInThisPage) { m_searchMode = UseSearchProvider; if(m_searchEngines.count()) { m_currentEngine = *m_searchEngines.fromLast(); } else { m_currentEngine = "google"; } } else { TQStringList::ConstIterator it = m_searchEngines.find(m_currentEngine); if(it==m_searchEngines.begin()) { m_searchMode = FindInThisPage; } else { it--; m_currentEngine = *it; } } setIcon(); } void SearchBarPlugin::startSearch(const TQString &_search) { if(m_urlEnterLock || _search.isEmpty() || !m_part) return; m_gsTimer.stop(); m_searchCombo->listBox()->hide(); TQString search = _search.section('(', 0, 0).stripWhiteSpace(); if(m_searchMode == FindInThisPage) { m_part->findText(search, 0); m_part->findTextNext(); } else if(m_searchMode == UseSearchProvider) { m_urlEnterLock = true; KService::Ptr service; KURIFilterData data; TQStringList list; list << "kurisearchfilter" << "kuriikwsfilter"; service = KService::serviceByDesktopPath(TQString("searchproviders/%1.desktop").arg(m_currentEngine)); if (service) { const TQString searchProviderPrefix = *(service->property("Keys").toStringList().begin()) + delimiter(); data.setData( searchProviderPrefix + search ); } if(!service || !KURIFilter::self()->filterURI(data, list)) { data.setData( TQString::fromLatin1( "google" ) + delimiter() + search ); KURIFilter::self()->filterURI( data, list ); } if(TDEApplication::keyboardMouseState() & TQt::ControlButton) { KParts::URLArgs args; args.setNewTab(true); emit m_part->browserExtension()->createNewWindow( data.uri(), args ); } else { emit m_part->browserExtension()->openURLRequest(data.uri()); } } if(m_searchCombo->text(0).isEmpty()) { m_searchCombo->changeItem(m_searchIcon, search, 0); } else { if(m_searchCombo->findHistoryItem(search) == -1) { m_searchCombo->insertItem(m_searchIcon, search, 0); } } m_searchCombo->setCurrentText(""); m_urlEnterLock = false; } void SearchBarPlugin::setIcon() { TQString hinttext; if(m_searchMode == FindInThisPage) { m_searchIcon = SmallIcon("edit-find"); hinttext = i18n("Find in This Page"); } else { TQString providername; KService::Ptr service; KURIFilterData data; TQStringList list; list << "kurisearchfilter" << "kuriikwsfilter"; service = KService::serviceByDesktopPath(TQString("searchproviders/%1.desktop").arg(m_currentEngine)); if (service) { const TQString searchProviderPrefix = *(service->property("Keys").toStringList().begin()) + delimiter(); data.setData( searchProviderPrefix + "some keyword" ); } if (service && KURIFilter::self()->filterURI(data, list)) { TQString iconPath = locate("cache", KMimeType::favIconForURL(data.uri()) + ".png"); if(iconPath.isEmpty()) { m_searchIcon = SmallIcon("enhanced_browsing"); } else { m_searchIcon = TQPixmap(iconPath); } providername = service->name(); } else { m_searchIcon = SmallIcon("google"); providername = "Google"; } hinttext = i18n("%1 Search").arg(providername);; } static_cast(m_searchCombo->lineEdit())->setClickMessage(hinttext); // Create a bit wider icon with arrow TQPixmap arrowmap = TQPixmap(m_searchIcon.width()+5,m_searchIcon.height()+5); arrowmap.fill(m_searchCombo->lineEdit()->backgroundColor()); TQPainter p( &arrowmap ); p.drawPixmap(0, 2, m_searchIcon); TQStyle::SFlags arrowFlags = TQStyle::Style_Default; m_searchCombo->style().tqdrawPrimitive(TQStyle::PE_ArrowDown, &p, TQRect(arrowmap.width()-6, arrowmap.height()-6, 6, 5), m_searchCombo->colorGroup(), arrowFlags, TQStyleOption() ); p.end(); m_searchIcon = arrowmap; m_searchCombo->setIcon(m_searchIcon); } void SearchBarPlugin::showSelectionMenu() { if(!m_popupMenu) { KService::Ptr service; TQPixmap icon; KURIFilterData data; TQStringList list; list << "kurisearchfilter" << "kuriikwsfilter"; m_popupMenu = new TQPopupMenu(m_searchCombo, "search selection menu"); m_popupMenu->insertItem(SmallIcon("edit-find"), i18n("Find in This Page"), this, TQT_SLOT(useFindInThisPage()), 0, 999); m_popupMenu->insertSeparator(); int i=-1; for (TQStringList::ConstIterator it = m_searchEngines.begin(); it != m_searchEngines.end(); ++it ) { i++; service = KService::serviceByDesktopPath(TQString("searchproviders/%1.desktop").arg(*it)); if(!service) { continue; } const TQString searchProviderPrefix = *(service->property("Keys").toStringList().begin()) + delimiter(); data.setData( searchProviderPrefix + "some keyword" ); if(KURIFilter::self()->filterURI(data, list)) { TQString iconPath = locate("cache", KMimeType::favIconForURL(data.uri()) + ".png"); if(iconPath.isEmpty()) { icon = SmallIcon("enhanced_browsing"); } else { icon = TQPixmap( iconPath ); } m_popupMenu->insertItem(icon, service->name(), i); } } m_popupMenu->insertSeparator(); m_googleMenu = new TDESelectAction(i18n("Use Google Suggest"), SmallIconSet("ktip"), 0, TQT_TQOBJECT(this), TQT_SLOT(selectGoogleSuggestMode()), TQT_TQOBJECT(m_popupMenu)); TQStringList google_modes; google_modes << i18n("For Google Only") << i18n("For All Searches") << i18n("Never"); m_googleMenu->setItems(google_modes); m_googleMenu->plug(m_popupMenu); m_popupMenu->insertItem(SmallIcon("enhanced_browsing"), i18n("Select Search Engines..."), this, TQT_SLOT(selectSearchEngines()), 0, 1000); connect(m_popupMenu, TQT_SIGNAL(activated(int)), TQT_SLOT(useSearchProvider(int))); } m_googleMenu->setCurrentItem(m_googleMode); m_popupMenu->popup(m_searchCombo->mapToGlobal(TQPoint(0, m_searchCombo->height() + 1)), 0); } void SearchBarPlugin::useFindInThisPage() { m_searchMode = FindInThisPage; setIcon(); } void SearchBarPlugin::useSearchProvider(int id) { if(id>900) { // Not a search engine entry selected return; } m_searchMode = UseSearchProvider; m_currentEngine = *m_searchEngines.at(id); setIcon(); } void SearchBarPlugin::selectSearchEngines() { TDEProcess *process = new TDEProcess; *process << "tdecmshell" << "ebrowsing"; connect(process, TQT_SIGNAL(processExited(TDEProcess *)), TQT_SLOT(searchEnginesSelected(TDEProcess *))); if(!process->start()) { kdDebug(1202) << "Couldn't invoke tdecmshell." << endl; delete process; } } void SearchBarPlugin::searchEnginesSelected(TDEProcess *process) { if(!process || process->exitStatus() == 0) { TDEConfig *config = kapp->config(); config->setGroup("SearchBar"); config->writeEntry("CurrentEngine", m_currentEngine); config->sync(); configurationChanged(); } delete process; } void SearchBarPlugin::configurationChanged() { TDEConfig *config = new TDEConfig("kuriikwsfilterrc"); config->setGroup("General"); TQString engine = config->readEntry("DefaultSearchEngine", "google"); TQStringList favoriteEngines; favoriteEngines << "google" << "google_groups" << "google_news" << "webster" << "dmoz" << "wikipedia"; favoriteEngines = config->readListEntry("FavoriteSearchEngines", favoriteEngines); delete m_popupMenu; m_popupMenu = 0; m_searchEngines.clear(); m_searchEngines << engine; for (TQStringList::ConstIterator it = favoriteEngines.begin(); it != favoriteEngines.end(); ++it ) if(*it!=engine) m_searchEngines << *it; delete config; if(engine.isEmpty()) { m_providerName = "Google"; } else { KDesktopFile file("searchproviders/" + engine + ".desktop", true, "services"); m_providerName = file.readName(); } config = kapp->config(); config->setGroup("SearchBar"); m_searchMode = (SearchModes) config->readNumEntry("Mode", (int) UseSearchProvider); m_currentEngine = config->readEntry("CurrentEngine", engine); m_googleMode=(GoogleMode)config->readNumEntry("GoogleSuggestMode", GoogleOnly); if ( m_currentEngine.isEmpty() ) m_currentEngine = "google"; setIcon(); } void SearchBarPlugin::partChanged(KParts::Part *newPart) { m_part = ::tqqt_cast(newPart); //Delay since when destroying tabs part 0 gets activated for a bit, before the proper part TQTimer::singleShot(0, this, TQT_SLOT(updateComboVisibility())); } void SearchBarPlugin::updateComboVisibility() { if (m_part.isNull() || !m_searchComboAction->isPlugged()) { m_searchCombo->setPluginActive(false); m_searchCombo->hide(); } else { m_searchCombo->setPluginActive(true); m_searchCombo->show(); } } void SearchBarPlugin::focusSearchbar() { TQFocusEvent::setReason( TQFocusEvent::Shortcut ); m_searchCombo->setFocus(); TQFocusEvent::resetReason(); } SearchBarCombo::SearchBarCombo(TQWidget *parent, const char *name) : KHistoryCombo(parent, name), m_pluginActive(true) { connect(this, TQT_SIGNAL(cleared()), TQT_SLOT(historyCleared())); } const TQPixmap &SearchBarCombo::icon() const { return m_icon; } void SearchBarCombo::setIcon(const TQPixmap &icon) { m_icon = icon; if(count() == 0) { insertItem(m_icon, 0); } else { for(int i = 0; i < count(); i++) { changeItem(m_icon, text(i), i); } } } int SearchBarCombo::findHistoryItem(const TQString &searchText) { for(int i = 0; i < count(); i++) { if(text(i) == searchText) { return i; } } return -1; } void SearchBarCombo::mousePressEvent(TQMouseEvent *e) { int x0 = TQStyle::visualRect( style().querySubControlMetrics( TQStyle::CC_ComboBox, this, TQStyle::SC_ComboBoxEditField ), this ).x(); if(e->x() > x0 + 2 && e->x() < lineEdit()->x()) { emit iconClicked(); e->accept(); } else { KHistoryCombo::mousePressEvent(e); } } void SearchBarCombo::historyCleared() { setIcon(m_icon); } void SearchBarCombo::setPluginActive(bool pluginActive) { m_pluginActive = pluginActive; } void SearchBarCombo::show() { if(m_pluginActive) { KHistoryCombo::show(); } } // Google Suggest code void SearchBarPlugin::selectGoogleSuggestMode() { m_googleMode = (GoogleMode)m_googleMenu->currentItem(); TDEConfig *config = kapp->config(); config->setGroup("SearchBar"); config->writeEntry("GoogleSuggestMode", m_googleMode); config->sync(); } // adapted and modified by Tobi Vollebregt // original code from Googlebar by Vinay Khaitan void SearchBarPlugin::gsStartDelay() { m_gsTimer.stop(); m_searchCombo->listBox()->hide(); // FIXME: make configurable m_gsTimer.start(500, true); } void SearchBarPlugin::gsMakeCompletionList() { if ((m_googleMode==GoogleOnly && m_currentEngine != "google") || m_googleMode==Never) return; if (!m_searchCombo->currentText().isEmpty()) { TDEIO::TransferJob* tj = TDEIO::get(KURL("http://www.google.com/complete/search?hl=en&js=true&qu=" + m_searchCombo->currentText()), false, false); connect(tj, TQT_SIGNAL(data(TDEIO::Job*, const TQByteArray&)), this, TQT_SLOT(gsDataArrived(TDEIO::Job*, const TQByteArray&))); connect(tj, TQT_SIGNAL(result(TDEIO::Job*)), this, TQT_SLOT(gsJobFinished(TDEIO::Job*))); } } void SearchBarPlugin::gsDataArrived(TDEIO::Job*, const TQByteArray& data) { m_gsData += TQString::fromUtf8(data.data()); } static TQString reformatNumber(const TQString& number) { static const char suffix[] = { 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' }; TQString s = number.stripWhiteSpace(); uint c = 0; for (int i = s.length() - 1; i > 0 && s[i] == '0'; --i) ++c; c /= 3; if (c >= sizeof(suffix)/sizeof(suffix[0])) c = sizeof(suffix)/sizeof(suffix[0]) - 1; s = s.left(s.length() - c * 3) + suffix[c]; return s; } void SearchBarPlugin::gsJobFinished(TDEIO::Job* job) { if (((TDEIO::TransferJob*)job)->error() == 0) { TQString temp; temp = m_gsData.mid(m_gsData.find('(') + 1, m_gsData.findRev(')') - m_gsData.find('(') - 1); temp = temp.mid(temp.find('(') + 1, temp.find(')') - temp.find('(') - 1); temp.remove('"'); TQStringList compList1 = TQStringList::split(',', temp); temp = m_gsData.mid(m_gsData.find(')') + 1, m_gsData.findRev(')') - m_gsData.find('(') - 1); temp = temp.mid(temp.find('(') + 1, temp.find(')') - temp.find('(') - 1); temp.remove('"'); temp.remove(','); temp.remove('s'); TQStringList compList2 = TQStringList::split("reult", temp); TQStringList finalList; for(uint j = 0; j < compList1.count(); j++) { if (m_googleMode!=ForAll || m_currentEngine == "google") finalList.append(compList1[j].stripWhiteSpace() + " (" + reformatNumber(compList2[j]) + ")"); else finalList.append(compList1[j].stripWhiteSpace()); } //store text so that we can restore it if it gets erased after GS returns no results temp = m_searchCombo->currentText(); m_searchCombo->listBox()->clear(); m_searchCombo->listBox()->insertStringList(finalList); m_searchCombo->setIcon(m_searchIcon); //restore text m_searchCombo->lineEdit()->setText(temp); if (finalList.count() != 0 && !m_gsTimer.isActive()) { m_searchCombo->popup(); } } m_gsData = ""; } void SearchBarPlugin::gsSetCompletedText(const TQString& text) { TQString currentText; if (m_searchCombo->lineEdit()->hasSelectedText()) currentText = m_searchCombo->currentText().left(m_searchCombo->lineEdit()->selectionStart()); else currentText = m_searchCombo->currentText(); if (currentText == text.left(currentText.length())) { m_searchCombo->lineEdit()->setText(text.left(text.find('(') - 1)); m_searchCombo->lineEdit()->setCursorPosition(currentText.length()); m_searchCombo->lineEdit()->setSelection(currentText.length(), m_searchCombo->currentText().length() - currentText.length()); } } void SearchBarPlugin::gsPutTextInBox(const TQString& text) { m_searchCombo->lineEdit()->setText(text.section('(', 0, 0).stripWhiteSpace()); } #include "searchbar.moc"