/* This file is part of the KDE project Copyright (C) 2005 by Tobi Vollebregt <tobivollebregt@gmail.com> Copyright (C) 2004 by Vinay Khaitan <vkhaitan@iitk.ac.in> Copyright (C) 2004 Arend van Beelen jr. <arend@auton.nl> 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 <unistd.h> #include <dcopclient.h> #include <tdeapplication.h> #include <tdeaction.h> #include <tdeconfig.h> #include <kdebug.h> #include <kdesktopfile.h> #include <kgenericfactory.h> #include <tdeglobal.h> #include <tdehtml_part.h> #include <kiconloader.h> #include <klineedit.h> #include <tdelocale.h> #include <kmimetype.h> #include <kprocess.h> #include <kprotocolinfo.h> #include <tdeprotocolmanager.h> #include <kstandarddirs.h> #include <kurifilter.h> #include <tdeio/job.h> #include <tdeparts/mainwindow.h> #include <tdeparts/partmanager.h> #include <tqpainter.h> #include <tqpopupmenu.h> #include <tqtimer.h> #include <tqstyle.h> #include <tqwhatsthis.h> #include "searchbar.h" typedef KGenericFactory<SearchBarPlugin> 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<p>" "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<KParts::MainWindow*>(TQT_TQWIDGET(parent)); //Grab the part manager. Don't know of any other way, and neither does Tronical, so.. KParts::PartManager *partMan = static_cast<KParts::PartManager*>(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( TQT_BASE_OBJECT(o)==TQT_BASE_OBJECT(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<KLineEdit*>(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<TDEHTMLPart*>(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() { #ifdef USE_QT4 m_searchCombo->setFocus(); #else // USE_QT4 TQFocusEvent::setReason( TQFocusEvent::Shortcut ); m_searchCombo->setFocus(); TQFocusEvent::resetReason(); #endif // USE_QT4 } 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"