diff --git a/src/identicasearch.cpp b/src/identicasearch.cpp index 805fe54e..9d88a810 100644 --- a/src/identicasearch.cpp +++ b/src/identicasearch.cpp @@ -1,205 +1,217 @@ /* This file is part of choqoK, the KDE micro-blogging client Copyright (C) 2008-2009 Mehrdad Momeny 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) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. 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; if not, see http://www.gnu.org/licenses/ */ #include "identicasearch.h" #include #include #include #include #include #include #include -IdenticaSearch::IdenticaSearch( const QString searchUrl, QObject *parent ) : - Search(searchUrl, parent) +#include "backend.h" + +IdenticaSearch::IdenticaSearch( Account* account, const QString searchUrl, QObject *parent ) : + Search(account, searchUrl, parent) { kDebug(); - mSearchTypes[ToUser] = i18n( "Dents To This User" ); - mSearchTypes[FromUser] = i18n( "Dents From This User" ); - mSearchTypes[ReferenceGroup] = i18n( "Dents Including This Group" ); - mSearchTypes[ReferenceHashtag] = i18n( "Dents Including This Hashtag" ); + mSearchTypes[ToUser].first = i18n( "Dents To This User" ); + mSearchTypes[ToUser].second = false; + + mSearchTypes[FromUser].first = i18n( "Dents From This User" ); + mSearchTypes[FromUser].second = false; + + mSearchTypes[ReferenceGroup].first = i18n( "Dents Including This Group" ); + mSearchTypes[ReferenceGroup].second = false; + + mSearchTypes[ReferenceHashtag].first = i18n( "Dents Including This Hashtag" ); + mSearchTypes[ReferenceHashtag].second = false; } IdenticaSearch::~IdenticaSearch() { kDebug(); } -KUrl IdenticaSearch::buildUrl( QString query, int option, uint sinceStatusId ) +KUrl IdenticaSearch::buildUrl( QString query, int option, uint sinceStatusId, uint count, uint page ) { kDebug(); Q_UNUSED(sinceStatusId); + Q_UNUSED(count); + Q_UNUSED(page); QString baseUrl = mSearchUrl; QString formattedQuery; switch ( option ) { case ToUser: formattedQuery = query + "/replies/rss"; break; case FromUser: formattedQuery = query + "/rss"; break; case ReferenceGroup: formattedQuery = "group/" + query + "/rss"; break; case ReferenceHashtag: formattedQuery = "tag/" + query + "/rss"; break; default: formattedQuery = query + "/rss"; break; }; - kDebug() << "Search URL: " << baseUrl << formattedQuery; - - KUrl url( baseUrl + formattedQuery ); + KUrl url; + url.setUrl( baseUrl + formattedQuery ); return url; } -void IdenticaSearch::requestSearchResults( QString query, int option, uint sinceStatusId ) +void IdenticaSearch::requestSearchResults( QString query, int option, uint sinceStatusId, uint count, uint page ) { kDebug(); + Q_UNUSED(count); + Q_UNUSED(page); KUrl url = buildUrl( query, option, sinceStatusId ); KIO::StoredTransferJob *job = KIO::storedGet( url, KIO::Reload, KIO::HideProgressInfo ); if( !job ) { kDebug() << "Cannot create a http GET request!"; emit error( i18n( "Unable to fetch search results." ) ); return; } mSinceStatusId = sinceStatusId; connect( job, SIGNAL( result( KJob* ) ), this, SLOT( searchResultsReturned( KJob* ) ) ); job->start(); } void IdenticaSearch::searchResultsReturned( KJob* job ) { kDebug(); if( job == 0 ) { kDebug() << "job is a null pointer"; emit error( i18n( "Unable to fetch search results." ) ); return; } if( job->error() ) { kError() << "Error: " << job->errorString(); emit error( i18n( "Unable to fetch search results. ERROR: %1", job->errorString() ) ); return; } KIO::StoredTransferJob *jj = qobject_cast( job ); QList* statusList = parseRss( jj->data() ); emit searchResultsReceived( *statusList ); } QList* IdenticaSearch::parseRss( const QByteArray &buffer ) { kDebug(); QDomDocument document; QList *statusList = new QList; document.setContent( buffer ); QDomElement root = document.documentElement(); if ( root.tagName() != "rdf:RDF" ) { kDebug() << "There is no rdf:RDF element in RSS feed " << buffer.data(); return statusList; } QDomNode node = root.firstChild(); QString timeStr; while ( !node.isNull() ) { if ( node.toElement().tagName() != "item" ) { node = node.nextSibling(); continue; } Status status; QDomAttr statusIdAttr = node.toElement().attributeNode( "rdf:about" ); uint statusId = 0; sscanf( qPrintable( statusIdAttr.value() ), qPrintable( mSearchUrl + "notice/%d" ), &statusId ); if( statusId <= mSinceStatusId ) { node = node.nextSibling(); continue; } status.statusId = statusId; QDomNode itemNode = node.firstChild(); while( !itemNode.isNull() ) { if( itemNode.toElement().tagName() == "title" ) { QString content = itemNode.toElement().text(); int nameSep = content.indexOf( ':', 0 ); QString screenName = content.left( nameSep ); QString statusText = content.right( content.size() - nameSep - 2 ); status.user.screenName = screenName; status.content = statusText; } else if ( itemNode.toElement().tagName() == "dc:date" ) { int year, month, day, hour, minute, second; sscanf( qPrintable( itemNode.toElement().text() ), "%d-%d-%dT%d:%d:%d%*s", &year, &month, &day, &hour, &minute, &second); QDateTime recognized( QDate( year, month, day), QTime( hour, minute, second ) ); recognized.setTimeSpec( Qt::UTC ); status.creationDateTime = recognized; } else if ( itemNode.toElement().tagName() == "dc:creator" ) { status.user.name = itemNode.toElement().text(); } else if ( itemNode.toElement().tagName() == "sioc:has_creator" ) { QDomAttr userIdAttr = itemNode.toElement().attributeNode( "rdf:resource" ); int id = 0; sscanf( qPrintable( userIdAttr.value() ), qPrintable( mSearchUrl + "user/%d" ), &id ); status.user.userId = id; } else if ( itemNode.toElement().tagName() == "laconica:postIcon" ) { QDomAttr imageAttr = itemNode.toElement().attributeNode( "rdf:resource" ); status.user.profileImageUrl = imageAttr.value(); } itemNode = itemNode.nextSibling(); } status.isDMessage = false; status.isFavorited = false; status.isTruncated = false; status.replyToStatusId = 0; statusList->insert( 0, status ); node = node.nextSibling(); } return statusList; } diff --git a/src/identicasearch.h b/src/identicasearch.h index 26ba9c40..5bac8174 100644 --- a/src/identicasearch.h +++ b/src/identicasearch.h @@ -1,59 +1,63 @@ /* This file is part of choqoK, the KDE micro-blogging client Copyright (C) 2008-2009 Mehrdad Momeny 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) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. 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; if not, see http://www.gnu.org/licenses/ */ #ifndef IDENTICASEARCH_H #define IDENTICASEARCH_H #include #include "search.h" #include "datacontainers.h" /** Twitter.com search API implementation. @author Stephen Henderson */ class IdenticaSearch : public Search { Q_OBJECT public: enum SearchType { ToUser = 0, FromUser, ReferenceGroup, ReferenceHashtag }; - explicit IdenticaSearch( const QString searchUrl = QString(), QObject *parent=0 ); + explicit IdenticaSearch( Account* account, const QString searchUrl = QString(), QObject *parent=0 ); virtual ~IdenticaSearch(); private: - virtual KUrl buildUrl( QString query, int option, uint sinceStatusId = 0 ); + virtual KUrl buildUrl( QString query, int option, uint sinceStatusId = 0, uint count = 0, uint page = 1 ); QList* parseRss( const QByteArray &buffer ); public slots: - virtual void requestSearchResults( QString query, int option, uint sinceStatusId = 0 ); + virtual void requestSearchResults( QString query, + int option, + uint sinceStatusId = 0, + uint count = 0, + uint page = 1 ); protected slots: virtual void searchResultsReturned( KJob *job ); }; #endif diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 15a8f86a..68ce14ad 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,483 +1,486 @@ /* This file is part of choqoK, the KDE micro-blogging client Copyright (C) 2008-2009 Mehrdad Momeny 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) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. 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; if not, see http://www.gnu.org/licenses/ */ #include "mainwindow.h" #include "settings.h" #include "timelinewidget.h" #include "constants.h" #include "accounts.h" #include "accountmanager.h" #include "accountswizard.h" #include "searchwindow.h" #include "systrayicon.h" #include "quicktwit.h" #include "statuswidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include MainWindow::MainWindow() : KXmlGuiWindow() { kDebug(); quickWidget = 0; timelineTimer = new QTimer( this ); this->setAttribute( Qt::WA_DeleteOnClose, false ); mainWidget = new KTabWidget( this ); setCentralWidget( mainWidget ); sysIcon = new SysTrayIcon(this); // setupQuickTweet(); setupActions(); statusBar()->show(); notify( i18n( "Initializing choqoK, please be patient..." ) ); setupGUI(); // timelineTimer->setInterval ( Settings::updateInterval() *60000 ); // timelineTimer->start(); if ( Settings::notifyType() == SettingsBase::NoNotify ) mPrevNotifyType = 1; else mPrevNotifyType = Settings::notifyType(); if ( Settings::updateInterval() > 0 ) mPrevUpdateInterval = Settings::updateInterval(); else mPrevUpdateInterval = 10; connect( timelineTimer, SIGNAL( timeout() ), this, SIGNAL( updateTimeLines() ) ); connect( AccountManager::self(), SIGNAL( accountAdded( const Account& ) ), this, SLOT( addAccountTimeLine( const Account& ) ) ); connect( AccountManager::self(), SIGNAL( accountRemoved( const QString& ) ), this, SLOT( removeAccountTimeLine( const QString& ) ) ); settingsChanged(); QPoint pos = Settings::position(); if(pos.x() != -1 && pos.y() != -1) { move(pos); } QTimer::singleShot( 0, this, SLOT( loadAccounts() ) ); } MainWindow::~MainWindow() { qApp->setStyleSheet(QString()); //crashes if qApp->styleSheet() != QString(), maybe Qt/KDE bug? kDebug(); } void MainWindow::setupActions() { KStandardAction::quit( qApp, SLOT( quit() ), actionCollection() ); connect( qApp, SIGNAL( aboutToQuit() ), this, SLOT( quitApp() ) ); KAction *prefs = KStandardAction::preferences( this, SLOT( optionsPreferences() ), actionCollection() ); KAction *actUpdate = new KAction( KIcon( "view-refresh" ), i18n( "Update timelines" ), this ); actionCollection()->addAction( QLatin1String( "update_timeline" ), actUpdate ); actUpdate->setShortcut( Qt::Key_F5 ); actUpdate->setGlobalShortcutAllowed( true ); KShortcut updateGlobalShortcut( Qt::CTRL | Qt::META | Qt::Key_F5 ); // updateGlobalShortcut.setAlternate ( Qt::MetaModifier | Qt::Key_F5 ); actUpdate->setGlobalShortcut( updateGlobalShortcut ); connect( actUpdate, SIGNAL( triggered( bool ) ), this, SIGNAL( updateTimeLines() ) ); connect( actUpdate, SIGNAL( triggered( bool ) ), this, SIGNAL( updateSearchResults() ) ); KAction *newTwit = new KAction( KIcon( "document-new" ), i18n( "Quick Tweet" ), this ); actionCollection()->addAction( QLatin1String( "choqok_new_twit" ), newTwit ); newTwit->setShortcut( KShortcut( Qt::CTRL | Qt::Key_T ) ); newTwit->setGlobalShortcutAllowed( true ); KShortcut quickTwitGlobalShortcut( Qt::CTRL | Qt::META | Qt::Key_T ); newTwit->setGlobalShortcut( quickTwitGlobalShortcut ); connect( newTwit, SIGNAL( triggered(bool) ), this, SLOT( postQuickTwit() ) ); KAction *newSearch = new KAction( KIcon( "edit-find" ), i18n( "Search" ), this ); actionCollection()->addAction( QLatin1String( "choqok_search" ), newSearch ); newSearch->setShortcut( KShortcut( Qt::CTRL | Qt::Key_F ) ); newSearch->setGlobalShortcutAllowed( false ); connect( newSearch, SIGNAL( triggered( bool ) ), this, SLOT( search() ) ); KAction *markRead = new KAction( KIcon( "mail-mark-read" ), i18n( "Mark All As Read" ), this ); actionCollection()->addAction( QLatin1String( "choqok_mark_read" ), markRead ); markRead->setShortcut( KShortcut( Qt::CTRL | Qt::Key_R ) ); actUpdate->setGlobalShortcutAllowed( false ); connect( markRead, SIGNAL( triggered( bool ) ), this, SIGNAL( setUnreadStatusesToReadState() ) ); KAction *showMain = new KAction( this ); actionCollection()->addAction( QLatin1String( "toggle_mainwin" ), showMain ); KShortcut toggleMainGlobalShortcut( Qt::CTRL | Qt::META | Qt::Key_C ); showMain->setGlobalShortcutAllowed( true ); showMain->setGlobalShortcut( toggleMainGlobalShortcut/*, KAction::DefaultShortcut, KAction::NoAutoloading*/ ); showMain->setText( i18n( "Minimize" ) ); connect( showMain, SIGNAL( triggered( bool ) ), this, SLOT( toggleMainWindow() ) ); KAction *enableUpdates = new KAction( i18n( "Enable update timer" ), this ); enableUpdates->setCheckable( true ); actionCollection()->addAction( QLatin1String( "choqok_enable_updates" ), enableUpdates ); enableUpdates->setShortcut( KShortcut( Qt::CTRL | Qt::Key_U ) ); enableUpdates->setGlobalShortcutAllowed( true ); connect( enableUpdates, SIGNAL( toggled( bool ) ), this, SLOT( setTimeLineUpdatesEnabled( bool ) ) ); KAction *enableNotify = new KAction( i18n( "Enable notifications" ), this ); enableNotify->setCheckable( true ); actionCollection()->addAction( QLatin1String( "choqok_enable_notify" ), enableNotify ); enableNotify->setShortcut( KShortcut( Qt::CTRL | Qt::Key_N ) ); enableNotify->setGlobalShortcutAllowed( true ); connect( enableNotify, SIGNAL( toggled( bool ) ), this, SLOT( setNotificationsEnabled( bool ) ) ); ///SysTray Actions: sysIcon->contextMenu()->addAction( newTwit ); // sysIcon->contextMenu()->addAction( newSearch ); sysIcon->contextMenu()->addAction( actUpdate ); sysIcon->contextMenu()->addSeparator(); connect( enableUpdates, SIGNAL( toggled( bool ) ), sysIcon, SLOT( setTimeLineUpdatesEnabled( bool ) ) ); sysIcon->contextMenu()->addAction( enableUpdates ); sysIcon->setTimeLineUpdatesEnabled( enableUpdates->isChecked() ); sysIcon->show(); sysIcon->contextMenu()->addAction( enableNotify ); sysIcon->contextMenu()->addAction( prefs ); } void MainWindow::setupQuickTweet() { quickWidget = new QuickTwit( this ); connect( quickWidget, SIGNAL( sigNotify( const QString&, const QString&, const QString& ) ), this, SLOT( systemNotify( const QString&, const QString&, const QString& ) ) ); connect( quickWidget, SIGNAL( sigStatusUpdated( bool ) ), sysIcon, SLOT( slotStatusUpdated( bool ) ) ); } void MainWindow::postQuickTwit() { if(!quickWidget) setupQuickTweet(); if ( quickWidget->isVisible() ) { quickWidget->hide(); } else { quickWidget->showFocusedOnNewStatusField(); } } void MainWindow::systemNotify( const QString &title, const QString &message, const QString &iconUrl ) { if ( Settings::notifyType() == SettingsBase::KNotify ) {//KNotify KNotification *notif = new KNotification( "notify", this ); notif->setText( message ); // notify->setPixmap(mainWin-); notif->setFlags( KNotification::RaiseWidgetOnActivation | KNotification::Persistent ); notif->sendEvent(); QTimer::singleShot( Settings::notifyInterval()*1000, notif, SLOT( close() ) ); } else if ( Settings::notifyType() == SettingsBase::LibNotify ) {//Libnotify! QString msg = message; msg = msg.replace( "
", "\n" ); QString libnotifyCmd = QString( "notify-send -t " ) + QString::number( Settings::notifyInterval() * 1000 ) + QString( " -u low -i " + iconUrl + " \"" ) + title + QString( "\" \"" ) + msg + QString( "\"" ); QProcess::execute( libnotifyCmd ); } } void MainWindow::hideEvent( QHideEvent * event ) { Q_UNUSED(event); if( !this->isVisible() ) { emit setUnreadStatusesToReadState(); } } void MainWindow::optionsPreferences() { kDebug(); if ( KConfigDialog::showDialog( "settings" ) ) { return; } KConfigDialog *dialog = new KConfigDialog( this, "settings", Settings::self() ); QWidget *generalSettingsDlg = new QWidget; ui_prefs_base.setupUi( generalSettingsDlg ); dialog->addPage( generalSettingsDlg, i18n( "General" ), "configure" ); Accounts *accountsSettingsDlg = new Accounts( this ); dialog->addPage( accountsSettingsDlg, i18n( "Accounts" ), "user-properties" ); QWidget *appearsSettingsDlg = new QWidget; ui_appears_base.setupUi( appearsSettingsDlg ); dialog->addPage( appearsSettingsDlg, i18n( "Appearances" ), "format-stroke-color" ); connect( dialog, SIGNAL( settingsChanged( QString ) ), this, SLOT( settingsChanged() ) ); dialog->setAttribute( Qt::WA_DeleteOnClose ); dialog->show(); } void MainWindow::search(int type, const QString& query) { kDebug(); TimeLineWidget * tmp = qobject_cast( mainWidget->widget( mainWidget->currentIndex() ) ); SearchWindow* searchWin = new SearchWindow( tmp->currentAccount(), 0 ); connect( searchWin, SIGNAL( forwardReply( const QString&, uint, bool ) ), tmp, SLOT( prepareReply( const QString&, uint, bool ) ) ); connect( searchWin, SIGNAL( forwardFavorited( uint, bool ) ), tmp->getBackend(), SLOT( requestFavorited( uint, bool ) ) ); connect( this, SIGNAL( updateSearchResults() ), searchWin, SLOT( updateSearchResults() ) ); connect( timelineTimer, SIGNAL( timeout() ), searchWin, SLOT( autoUpdateSearchResults() ) ); + connect( this, SIGNAL( updateSearchLimits() ), searchWin, SLOT( updateNumPages() ) ); searchWin->init(type, query); } void MainWindow::settingsChanged() { kDebug(); if ( AccountManager::self()->accounts().count() < 1 ) { if ( KMessageBox::questionYesNo( this, i18n( "In order to use this app you need at \ least one account on Identi.ca or \ Twitter.com services.
Would you like to add your account now?
" ) ) == KMessageBox::Yes ) { AccountsWizard *dia = new AccountsWizard( QString(), this ); dia->setAttribute( Qt::WA_DeleteOnClose ); dia->show(); } } if ( Settings::isCustomUi() ) { StatusWidget::setStyle( Settings::newStatusForeColor() , Settings::newStatusBackColor(), Settings::defaultForeColor() , Settings::defaultBackColor()); } else { QPalette p = window()->palette(); StatusWidget::setStyle( p.color(QPalette::WindowText) , p.color(QPalette::Window).lighter() , p.color(QPalette::WindowText) , p.color(QPalette::Window)); } // qApp->setStyleSheet(StatusWidget::getColoredStyle()); int count = mainWidget->count(); for ( int i = 0; i < count; ++i ) { qobject_cast( mainWidget->widget( i ) )->settingsChanged(); } if ( Settings::notifyType() == SettingsBase::NoNotify ) { actionCollection()->action( "choqok_enable_notify" )->setChecked( false ); } else { actionCollection()->action( "choqok_enable_notify" )->setChecked( true ); } if ( Settings::updateInterval() > 0 ) { timelineTimer->setInterval( Settings::updateInterval() *60000 ); timelineTimer->start(); // kDebug()<<"timelineTimer started"; actionCollection()->action( "choqok_enable_updates" )->setChecked( true ); } else { timelineTimer->stop(); // kDebug()<<"timelineTimer stoped"; actionCollection()->action( "choqok_enable_updates" )->setChecked( false ); } + + emit updateSearchLimits(); } void MainWindow::notify( const QString &message, bool isPermanent ) { if ( isPermanent ) { statusBar()->showMessage( message ); } else { statusBar()->showMessage( message, TIMEOUT ); } } void MainWindow::quitApp() { kDebug(); Settings::setPosition( pos() ); timelineTimer->stop(); Settings::self()->writeConfig(); deleteLater(); } void MainWindow::loadConfigurations() { kDebug(); } void MainWindow::disableApp() { kDebug(); timelineTimer->stop(); // kDebug()<<"timelineTimer stoped"; actionCollection()->action( "update_timeline" )->setEnabled( false ); actionCollection()->action( "choqok_new_twit" )->setEnabled( false ); actionCollection()->action( "choqok_search" )->setEnabled( false ); actionCollection()->action( "choqok_mark_read" )->setEnabled( false ); } void MainWindow::enableApp() { kDebug(); if ( Settings::updateInterval() > 0 ) { timelineTimer->start(); // kDebug()<<"timelineTimer started"; } actionCollection()->action( "update_timeline" )->setEnabled( true ); actionCollection()->action( "choqok_new_twit" )->setEnabled( true ); actionCollection()->action( "choqok_search" )->setEnabled( true ); actionCollection()->action( "choqok_mark_read" )->setEnabled( true ); } void MainWindow::addAccountTimeLine( const Account & account, bool isStartup ) { kDebug() << "Alias: " << account.alias() << "Service :" << account.serviceName(); TimeLineWidget *widget = new TimeLineWidget( account, this ); widget->layout()->setContentsMargins( 0, 0, 0, 0 ); connect( widget, SIGNAL( sigSetUnread( int ) ), sysIcon, SLOT( slotSetUnread( int ) ) ); connect( widget, SIGNAL( systemNotify( const QString&, const QString&, const QString& ) ), this, SLOT( systemNotify( const QString&, const QString&, const QString& ) ) ); connect( widget, SIGNAL( notify( const QString&, bool ) ), this, SLOT( notify( const QString&, bool ) ) ); connect( widget, SIGNAL( showMe() ), this, SLOT( showTimeLine() ) ); // connect(widget, SIGNAL(sigStatusUpdated(bool)), this, SIGNAL(sigStatusUpdated(bool))); connect( this, SIGNAL( updateTimeLines() ), widget, SLOT( updateTimeLines() ) ); connect( this, SIGNAL( abortPostNewStatus() ), widget, SLOT( abortPostNewStatus() ) ); connect( this, SIGNAL( setUnreadStatusesToReadState() ), widget, SLOT( setUnreadStatusesToReadState() ) ); connect( widget, SIGNAL( sigSetUnreadOnMainWin( int ) ), this, SLOT( setNumOfUnreadOnMainWin( int ) ) ); connect( widget, SIGNAL(sigSearch(int,QString)),this,SLOT(search(int,QString))); mainWidget->addTab( widget, account.alias() ); if ( !isStartup ) { QTimer::singleShot( 500, widget, SLOT( updateTimeLines() ) ); QTimer::singleShot( 1000, widget, SLOT(reloadFriendsList()) ); } enableApp(); } void MainWindow::loadAccounts() { kDebug(); QList ac = AccountManager::self()->accounts(); QListIterator it( ac ); while ( it.hasNext() ) { Account current = it.next(); addAccountTimeLine( current, true ); } if ( ac.count() > 0 ) { enableApp(); } else { disableApp(); } } void MainWindow::removeAccountTimeLine( const QString & alias ) { kDebug(); int count = mainWidget->count(); for ( int i = 0; i < count; ++i ) { TimeLineWidget * tmp = qobject_cast( mainWidget->widget( i ) ); if ( tmp->currentAccount().alias() == alias ) { mainWidget->removeTab( i ); if ( mainWidget->count() < 1 ) disableApp(); // tmp->setRemoved( true ); tmp->deleteLater(); return; } } } void MainWindow::setNumOfUnreadOnMainWin( int unread ) { // kDebug()<( sender() ); QString text; if ( unread <= 0 ) { text = subWidget->currentAccount().alias(); } else { text = i18nc( "account, unread","%1(%2)", subWidget->currentAccount().alias(), unread ); } mainWidget->setTabText( mainWidget->indexOf( subWidget ), text ); } void MainWindow::showTimeLine() { mainWidget->setCurrentWidget( qobject_cast( sender() ) ); if ( !this->isVisible() ) this->show(); this->raise(); } void MainWindow::setTimeLineUpdatesEnabled( bool isEnabled ) { kDebug(); if ( isEnabled ) { if( mPrevUpdateInterval > 0 ) Settings::setUpdateInterval( mPrevUpdateInterval ); timelineTimer->start( Settings::updateInterval() *60000 ); // kDebug()<<"timelineTimer started"; } else { mPrevUpdateInterval = Settings::updateInterval(); timelineTimer->stop(); // kDebug()<<"timelineTimer stoped"; Settings::setUpdateInterval( 1 ); } } void MainWindow::setNotificationsEnabled( bool isEnabled ) { kDebug(); if ( isEnabled ) { Settings::setNotifyType( (SettingsBase::NotifyType) mPrevNotifyType ); } else { mPrevNotifyType = Settings::notifyType(); Settings::setNotifyType( SettingsBase::NoNotify ); } } void MainWindow::toggleMainWindow() { if( this->isVisible() ) hide(); else show(); } #include "mainwindow.moc" diff --git a/src/mainwindow.h b/src/mainwindow.h index 7facdd18..a586c329 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -1,117 +1,118 @@ /* This file is part of choqoK, the KDE micro-blogging client Copyright (C) 2008-2009 Mehrdad Momeny 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) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. 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; if not, see http://www.gnu.org/licenses/ */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include "datacontainers.h" #include "account.h" #include "ui_prefs_base.h" // #include "ui_accounts_base.h" #include "ui_appears_base.h" #include "searchwindow.h" static const int TIMEOUT = 5000; class QTimer; class QuickTwit; class SysTrayIcon; class KTabWidget; /** * This class serves as the main window for choqoK. It handles the * menus, toolbars, and status bars. * * @short Main window class * @author Mehrdad Momeny */ class MainWindow : public KXmlGuiWindow { Q_OBJECT public: /** * Default Constructor */ MainWindow(); /** * Default Destructor */ virtual ~MainWindow(); signals: void updateTimeLines(); void updateSearchResults(); + void updateSearchLimits(); // void sigSetUnread( int unread ); void abortPostNewStatus(); void setUnreadStatusesToReadState(); public slots: void systemNotify( const QString &title, const QString &message, const QString &iconUrl ); void search(int type = 0, const QString &search = QString()); protected slots: void optionsPreferences(); // void search(); void settingsChanged(); void notify( const QString &message, bool isPermanent = false ); void quitApp(); void setNumOfUnreadOnMainWin( int unread ); void showTimeLine(); protected: void hideEvent( QHideEvent * event ); void checkUnreadStatuses( int numOfNewStatusesReciened ); private: void setupActions(); void setupQuickTweet(); void setDefaultDirection(); void disableApp(); void enableApp(); void loadConfigurations(); private slots: void loadAccounts(); void addAccountTimeLine( const Account &account, bool isStartup = false ); void removeAccountTimeLine( const QString &alias ); void setTimeLineUpdatesEnabled( bool isEnabled ); void setNotificationsEnabled( bool isEnabled ); void postQuickTwit(); void toggleMainWindow(); private: KTabWidget *mainWidget; QTimer *timelineTimer; Ui::prefs_base ui_prefs_base; Ui::appears_base ui_appears_base; QString currentUsername;// used for undresanding of username changes! int mPrevNotifyType; int mPrevUpdateInterval; SysTrayIcon *sysIcon; QuickTwit *quickWidget; }; #endif diff --git a/src/search.cpp b/src/search.cpp index d097d032..200e0ba8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1,64 +1,75 @@ /* This file is part of choqoK, the KDE micro-blogging client Copyright (C) 2008-2009 Mehrdad Momeny 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) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. 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; if not, see http://www.gnu.org/licenses/ */ #include "search.h" +//#include "account.h" -Search::Search( const QString searchUrl, QObject *parent ) +Search::Search( Account* account, const QString searchUrl, QObject *parent ) : QObject( parent ) { + mAccount = account; mSearchUrl = searchUrl; /** * TODO Support multiple pages on search results! instead of just first 20 latest results! */ } Search::~Search() { mSinceStatusId = 0; } -QMap Search::getSearchTypes() +QMap > Search::getSearchTypes() { return mSearchTypes; } -KUrl Search::buildUrl( QString query, int option, uint sinceStatusId ) +KUrl Search::buildUrl( QString query, int option, uint sinceStatusId, uint count, uint page ) { Q_UNUSED(query); Q_UNUSED(option); Q_UNUSED(sinceStatusId); + Q_UNUSED(count); + Q_UNUSED(page); return KUrl(); } -void Search::requestSearchResults( QString query, int option, uint sinceStatusId ) +void Search::requestSearchResults( QString query, int option, uint sinceStatusId, uint count, uint page ) { Q_UNUSED(query); Q_UNUSED(option); - Q_UNUSED(sinceStatusId) + Q_UNUSED(sinceStatusId); + Q_UNUSED(count); + Q_UNUSED(page); } void Search::searchResultsReturned( KJob* job ) { Q_UNUSED(job); } + +void Search::singleStatusReturned( KJob* job ) +{ + Q_UNUSED(job); +} diff --git a/src/search.h b/src/search.h index 9c75c857..18f94dad 100644 --- a/src/search.h +++ b/src/search.h @@ -1,69 +1,79 @@ /* This file is part of choqoK, the KDE micro-blogging client Copyright (C) 2008-2009 Mehrdad Momeny 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) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. 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; if not, see http://www.gnu.org/licenses/ */ #ifndef SEARCH_H #define SEARCH_H #include #include #include +#include #include #include "datacontainers.h" class KJob; +class Account; /** Base class for search feature. @author Stephen Henderson */ class Search : public QObject { Q_OBJECT public: - explicit Search( const QString searchUrl = QString(), QObject *parent=0 ); + explicit Search( Account* account, const QString searchUrl = QString(), QObject *parent=0 ); virtual ~Search(); - QMap getSearchTypes(); + QMap > getSearchTypes(); private: - virtual KUrl buildUrl( QString query, int option, uint sinceStatusId = 0 ); + virtual KUrl buildUrl( QString query, int option, uint sinceStatusId = 0, uint count = 0, uint page = 1 ); public slots: - virtual void requestSearchResults( QString query, int option, uint sinceStatusId = 0 ); + virtual void requestSearchResults( QString query, + int option, + uint sinceStatusId = 0, + uint count = 0, + uint page = 1 ); protected slots: virtual void searchResultsReturned( KJob *job ); + virtual void singleStatusReturned( KJob* job ); signals: void searchResultsReceived( QList &statusList ); void error( QString message ); protected: - QMap mSearchTypes; + // The QString in the QPair is a human readable string describing what the type searches for. The boolean value + // determines whether or not the search type is traversable (if the forward and back buttons should be displayed). + QMap > mSearchTypes; uint mSinceStatusId; QString mSearchUrl; + Account* mAccount; }; #endif diff --git a/src/searchwidget_base.ui b/src/searchwidget_base.ui index 1ab18445..78db385e 100644 --- a/src/searchwidget_base.ui +++ b/src/searchwidget_base.ui @@ -1,145 +1,267 @@ - + + searchwidget_base - - + + Qt::NonModal - + 0 0 - 320 - 400 + 330 + 201 - - + + 0 0 - + choqoK - + true - - - 1 + + + 4 - - - - + + + + 0 0 - + QFrame::NoFrame - + QFrame::Plain - + true - - + + 0 0 - 318 - 289 + 322 + 71 - - + + 0 - + 0 - - + + 1 - - + + Qt::Vertical - + 20 0 - - - + + + 75 true - + No Search Results - + Qt::AlignCenter - - - + + + Auto-Update Results - - - + + + 140 - + Enter search query here - + true - - + + + + + + + + 0 + 0 + + + + + 28 + 28 + + + + + 28 + 28 + + + + Previous Page + + + + + + + + + + + 0 + 0 + + + + + 28 + 28 + + + + + 28 + 28 + + + + Next Page + + + + + + false + + + + + + + + 0 + 0 + + + + + 28 + 28 + + + + + 28 + 28 + + + + Refresh + + + + + + + + + + + 0 + 0 + + + + + 40 + 28 + + + + + 40 + 28 + + + + 1 + + + 32767 + + + true + + + Qt::AlignCenter + + - + qPixmapFromMimeSource KComboBox QComboBox
kcombobox.h
KLineEdit QLineEdit
klineedit.h
diff --git a/src/searchwindow.cpp b/src/searchwindow.cpp index 167ff7f7..32f5a062 100644 --- a/src/searchwindow.cpp +++ b/src/searchwindow.cpp @@ -1,306 +1,428 @@ /* This file is part of choqoK, the KDE micro-blogging client Copyright (C) 2008-2009 Mehrdad Momeny 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) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. 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; if not, see http://www.gnu.org/licenses/ */ #include "searchwindow.h" #include "statuswidget.h" #include "settings.h" #include #include #include #include #include #include #include #include "twittersearch.h" #include "identicasearch.h" SearchWindow::SearchWindow( const Account &account, QWidget* parent ) : QWidget( parent ) { kDebug(); mAccount = account; + intValidator = new QIntValidator( 1, 1500 / Settings::countOfStatusesOnMain(), this ); + setAttribute( Qt::WA_DeleteOnClose ); switch( mAccount.serviceType() ) { case Account::Twitter: - mSearch = new TwitterSearch( QString(), this ); + mSearch = new TwitterSearch( &mAccount, QString(), this ); break; case Account::Identica: case Account::Laconica: - mSearch = new IdenticaSearch( mAccount.homepage(), this ); + mSearch = new IdenticaSearch( &mAccount, mAccount.homepage(), this ); break; default: mSearch = 0; break; } ui.setupUi( this ); resize( Settings::searchWindowSize() ); move( Settings::searchWindowPosition() ); setWindowTitle( i18nc( "Search in service", "%1 Search", mAccount.alias() ) ); } void SearchWindow::init(int type, const QString & query) { kDebug(); if( mSearch ) { connect( mSearch, SIGNAL( searchResultsReceived( QList< Status>& ) ), this, SLOT( searchResultsReceived ( QList< Status >& ) ) ); connect( mSearch, SIGNAL( error( QString ) ), this, SLOT( error( QString ) ) ); connect( ui.txtSearch, SIGNAL( returnPressed() ), this, SLOT( search() ) ); + connect( ui.txtPage, SIGNAL( returnPressed() ), this, SLOT( pageChange() ) ); + + page = 1; + lastValidPage = 1; + + ui.btnRefresh->setIcon( KIcon( "view-refresh" ) ); + ui.btnBack->setIcon( KIcon( "go-previous" ) ); + ui.btnForward->setIcon( KIcon( "go-next" ) ); + + ui.btnRefresh->setEnabled( false ); + ui.btnBack->setEnabled( false ); + ui.btnForward->setEnabled( false ); + + ui.txtPage->setValidator( intValidator ); + + connect( ui.btnRefresh, SIGNAL( clicked( bool ) ), this, SLOT( refresh() ) ); + connect( ui.btnBack, SIGNAL( clicked( bool ) ), this, SLOT( goBack() ) ); + connect( ui.btnForward, SIGNAL( clicked( bool ) ), this, SLOT( goForward() ) ); + + showNavigation( false ); show(); resetSearchArea(type,query); } else { kDebug() << "Service has no search implementation"; KMessageBox::error( this, i18n( "This service has no search feature." ) ); close(); } } SearchWindow::~SearchWindow() { kDebug(); Settings::setSearchWindowPosition(pos()); Settings::setSearchWindowSize(size()); if( mSearch ) mSearch->deleteLater(); + intValidator->deleteLater(); } void SearchWindow::error( QString message ) { ui.lblStatus->setText( i18n( "Failed, %1", message ) ); lastSearchQuery.clear(); } void SearchWindow::searchResultsReceived(QList &statusList ) { kDebug(); int count = statusList.count(); if ( count == 0 ) { kDebug() << "Status list is empty"; - ui.lblStatus->setText( i18n( "No search results." ) ); + ui.lblStatus->setText( i18n( "No search results on page %1.", QString::number( page ) ) ); + ui.btnForward->setEnabled( false ); + if( page > 1 ) + page = lastValidPage; + ui.btnRefresh->setEnabled( false ); } else { - ui.lblStatus->setText( i18n( "Search Results Received!" ) ); + + // Sets up the navigation for the current page + if( mSearch->getSearchTypes()[lastSearchType].second ) { + showNavigation( true ); + if( page == 1 ) + { + ui.chkAutoUpdate->setEnabled( true ); + ui.btnBack->setEnabled( false ); + ui.btnForward->setEnabled( true ); + } + else { + if ( (int)page == intValidator->top() ) + ui.btnForward->setEnabled( false ); + else + ui.btnForward->setEnabled( true ); + ui.chkAutoUpdate->setEnabled( false ); + clearSearchResults(); + ui.btnBack->setEnabled( true ); + } + } else { + showNavigation( false ); + } + + ui.lblStatus->setText( i18n( "Search results for page %1.", QString::number( page ) ) ); addNewStatusesToUi( statusList ); ui.searchScroll->verticalScrollBar()->setSliderPosition( 0 ); + lastValidPage = page; + ui.btnRefresh->setEnabled( true ); } + + ui.txtPage->setText( QString::number( lastValidPage ) ); ui.txtSearch->setEnabled( true ); } void SearchWindow::search() { kDebug(); + page = 1; + if ( ui.txtSearch->text().size() > 140 ) { ui.lblStatus->setText( i18n( "Search text size is more than 140 characters." ) ); return; } - ui.txtSearch->setEnabled( false ); clearSearchResults(); + + ui.txtSearch->setEnabled( false ); ui.lblStatus->setText( i18n( "Searching..." ) ); + ui.chkAutoUpdate->setCheckState( Qt::Unchecked ); mSearch->requestSearchResults( ui.txtSearch->text(), - ui.comboSearchType->currentIndex(), 0 ); + ui.comboSearchType->currentIndex(), + 0, + Settings::countOfStatusesOnMain() ); lastSearchQuery = ui.txtSearch->text(); lastSearchType = ui.comboSearchType->currentIndex(); setWindowTitle( i18nc( "Search in service", "%1 Search (%2)", mAccount.serviceName(), lastSearchQuery ) ); } void SearchWindow::updateSearchResults() { kDebug(); - if( isVisible() && !lastSearchQuery.isNull() ) + if( isVisible() && !lastSearchQuery.isNull() && page == 1 ) { uint sinceStatusId = 0; if( listResults.count() ) sinceStatusId = listResults.last()->currentStatus().statusId; ui.lblStatus->setText( i18n( "Searching..." ) ); mSearch->requestSearchResults( lastSearchQuery, lastSearchType, - sinceStatusId ); + sinceStatusId, + Settings::countOfStatusesOnMain(), + page ); } } void SearchWindow::autoUpdateSearchResults() { kDebug(); if( ui.chkAutoUpdate->isChecked() ) updateSearchResults(); } void SearchWindow::addNewStatusesToUi( QList &statusList ) { kDebug(); // This will make all statuses prior to the update marked as read // and deleted if there are more than Settings::countOfStatusesOnMain. // The reasoning for this is that there's a distinct possibility of // a searching racking up thousands of unread messages depending on // the query which would go undeleted as unread messages. The other // option to avoid this would be to enforce a strict message limit // regardless of whether or not they were marked as read. markStatusesAsRead(); QList::const_iterator it = statusList.constBegin(); QList::const_iterator endIt = statusList.constEnd(); for( ; it != endIt; ++it ) { StatusWidget *wt = new StatusWidget( &mAccount, this ); connect( wt, SIGNAL( sigReply( const QString&, uint, bool ) ), this, SIGNAL( forwardReply( const QString&, uint, bool ) ) ); connect( wt, SIGNAL( sigFavorite( uint, bool ) ), this, SIGNAL( forwardFavorited( uint, bool ) ) ); connect (wt,SIGNAL(sigSearch(int,QString)),this,SLOT(updateSearchArea(int,QString))); wt->setAttribute( Qt::WA_DeleteOnClose ); wt->setCurrentStatus( *it ); wt->setUnread( StatusWidget::WithoutNotify ); listResults.append( wt ); ui.searchLayout->insertWidget( 0, wt ); } updateStatusList(); } void SearchWindow::updateStatusList() { kDebug(); int toBeDelete = listResults.count() - Settings::countOfStatusesOnMain(); if ( toBeDelete > 0 ) { for ( int i = 0; i < toBeDelete; ++i ) { StatusWidget* wt = listResults.at( i ); if ( !wt->isRead() ) break; listResults.removeAt( i ); --i; --toBeDelete; wt->close(); } } } void SearchWindow::clearSearchResults() { kDebug(); int count = listResults.count(); for ( int i = 0; i < count; ++i ) { StatusWidget* wt = listResults.first(); listResults.removeFirst(); wt->close(); } } void SearchWindow::markStatusesAsRead() { kDebug(); int count = listResults.count(); for ( int i = 0;i < count; ++i ) { listResults[i]->setRead(); } // qApp->setStyleSheet( qApp->styleSheet() ); } void SearchWindow::setAccount( const Account &account ) { disconnect( mSearch, SIGNAL( searchResultsReceived( QList< Status>& ) ), this, SLOT( searchResultsReceived ( QList< Status >& ) ) ); disconnect( mSearch, SIGNAL( error( QString ) ), this, SLOT( error( QString ) ) ); mAccount = account; resetSearchArea(); connect( mSearch, SIGNAL( searchResultsReceived( QList< Status>& ) ), this, SLOT( searchResultsReceived ( QList< Status >& ) ) ); connect( mSearch, SIGNAL( error( QString ) ), this, SLOT( error( QString ) ) ); } void SearchWindow::resetSearchArea(int type, const QString & query) { kDebug(); ui.txtSearch->setText( query ); ui.txtSearch->setEnabled( true ); ui.comboSearchType->clear(); ui.chkAutoUpdate->setChecked( false ); ui.lblStatus->setText( i18n( "No Search Results" ) ); - QMap searchTypes = mSearch->getSearchTypes(); + QMap > searchTypes = mSearch->getSearchTypes(); for( int i = 0; i < searchTypes.count(); ++i ) { - ui.comboSearchType->insertItem( i, searchTypes[i] ); + ui.comboSearchType->insertItem( i, searchTypes[i].first ); } if(!query.isEmpty()) { kDebug()<setCurrentIndex(type); search(); } } void SearchWindow::updateSearchArea(int type, const QString& query) { ui.txtSearch->setText(query); if(!query.isEmpty()) { ui.comboSearchType->setCurrentIndex(type); search(); } } - void SearchWindow::keyPressEvent( QKeyEvent* e ) { if ( e->key() == Qt::Key_F5 ) { // emit updateTimeLines(); - updateSearchResults(); + refresh(); e->accept(); } else if ( e->modifiers() == Qt::CTRL && e->key() == Qt::Key_R ) { markStatusesAsRead(); } else { QWidget::keyPressEvent( e ); } } + +void SearchWindow::refresh() +{ + page = 1; + clearSearchResults(); + updateSearchResults(); +} + +void SearchWindow::goForward() +{ + ++page; + + ui.lblStatus->setText( i18n( "Fetching Next Page..." ) ); + mSearch->requestSearchResults( lastSearchQuery, + lastSearchType, + 0, + Settings::countOfStatusesOnMain(), + page ); +} + +void SearchWindow::goBack() +{ + if (page == 1 ) { + ui.btnBack->setEnabled( false ); + return; + } + --page; + + ui.lblStatus->setText( i18n( "Fetching Previous Page..." ) ); + mSearch->requestSearchResults( lastSearchQuery, + lastSearchType, + 0, + Settings::countOfStatusesOnMain(), + page ); +} + +void SearchWindow::pageChange() +{ + page = ui.txtPage->text().toUInt(); + ui.lblStatus->setText( i18n( "Fetching Page %1...", QString::number( page ) ) ); + mSearch->requestSearchResults( lastSearchQuery, + lastSearchType, + 0, + Settings::countOfStatusesOnMain(), + page ); +} + +void SearchWindow::showNavigation( bool showNav ) +{ + ui.btnBack->setVisible( showNav ); + ui.btnForward->setVisible( showNav ); + ui.txtPage->setVisible( showNav ); +} + +void SearchWindow::updateNumPages() +{ + intValidator->setTop( 1500 / Settings::countOfStatusesOnMain() ); + page = 1; + lastValidPage = 1; + updateSearchResults(); +} diff --git a/src/searchwindow.h b/src/searchwindow.h index 503cd7ca..a520bcc6 100644 --- a/src/searchwindow.h +++ b/src/searchwindow.h @@ -1,95 +1,104 @@ /* This file is part of choqoK, the KDE micro-blogging client Copyright (C) 2008-2009 Mehrdad Momeny 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) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. 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; if not, see http://www.gnu.org/licenses/ */ #ifndef SEARCHWINDOW_H #define SEARCHWINDOW_H #include #include +#include #include "ui_searchwidget_base.h" #include "account.h" #include "search.h" class QLabel; class StatusTextEdit; class StatusWidget; /** The Search Widget For any microblogging service compatible with the Twitter Search API @author Stephen Henderson */ class SearchWindow : public QWidget { Q_OBJECT public: explicit SearchWindow(const Account &account, QWidget* parent = 0 ); ~SearchWindow(); void clearSearchResults(); void updateStatusList(); void setAccount(const Account &account); void init(int type = 0, const QString & query = QString()); protected: virtual void keyPressEvent( QKeyEvent *e ); public slots: void updateSearchResults(); void autoUpdateSearchResults(); + void updateNumPages(); protected slots: void search(); void searchResultsReceived( QList &stautsList ); void error( QString message ); signals: void forwardReply( const QString &username, uint statusId, bool dMsg ); void forwardFavorited( uint statusId, bool isFavorite ); // void updateTimeLines(); private: void addNewStatusesToUi( QList &statusList ); void resetSearchArea(int type = 0, const QString & query = QString()); void markStatusesAsRead(); private slots: void updateSearchArea(int type = 0, const QString & query = QString()); + void refresh(); + void goForward(); + void goBack(); + void pageChange(); + void showNavigation( bool showNav ); protected: QList listResults; private: Account mAccount; Search* mSearch; Ui::searchwidget_base ui; - + uint page; + uint lastValidPage; QString lastSearchQuery; int lastSearchType; + QIntValidator* intValidator; }; #endif diff --git a/src/statuswidget.cpp b/src/statuswidget.cpp index 0af76d0b..ce064091 100644 --- a/src/statuswidget.cpp +++ b/src/statuswidget.cpp @@ -1,479 +1,480 @@ /* This file is part of choqoK, the KDE micro-blogging client Copyright (C) 2008-2009 Mehrdad Momeny 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) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. 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; if not, see http://www.gnu.org/licenses/ */ #include "statuswidget.h" #include "settings.h" #include "mediamanager.h" #include "backend.h" #include #include #include "mainwindow.h" #include #include #include #include #include "identicasearch.h" #include "twittersearch.h" #include static const int _15SECS = 15000; static const int _MINUTE = 60000; static const int _HOUR = 60*_MINUTE; const QString StatusWidget::baseText("
%2%3
%4
"); const QString StatusWidget::baseStyle("QFrame.StatusWidget {border: 1px solid rgb(150,150,150);\ border-radius:5px;}\ QFrame.StatusWidget[read=false] {color: %1; background-color: %2}\ QFrame.StatusWidget[read=true] {color: %3; background-color: %4}"); QString StatusWidget::style; QRegExp StatusWidget::mUrlRegExp("(https?://[^\\s<>'\"]+[^!,\\.\\s<>'\"\\]])"); // "borrowed" from microblog plasmoid QRegExp StatusWidget::mUserRegExp("([\\s]|^)@([^\\s\\W]+)", Qt::CaseInsensitive); QRegExp StatusWidget::mHashtagRegExp("([\\s]|^)#([^\\s\\W]+)", Qt::CaseInsensitive); QRegExp StatusWidget::mGroupRegExp("([\\s]|^)!([^\\s\\W]+)", Qt::CaseInsensitive); void StatusWidget::setStyle(const QColor& color, const QColor& back, const QColor& read, const QColor& readBack) { style = baseStyle.arg(getColorString(color),getColorString(back),getColorString(read),getColorString(readBack)); } StatusWidget::StatusWidget( const Account *account, QWidget *parent ) : KTextBrowser( parent ),mIsRead(true),mCurrentAccount(account),isBaseStatusShowed(false) { setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Fixed); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setupUi(); setOpenLinks(false); timer.start( _MINUTE ); connect( &timer, SIGNAL( timeout() ), this, SLOT( updateSign() ) ); connect(this,SIGNAL(anchorClicked(QUrl)),this,SLOT(checkAnchor(QUrl))); } void StatusWidget::checkAnchor(const QUrl & url) { QString scheme = url.scheme(); Account::Service s = mCurrentAccount->serviceType(); int type = 0; if( scheme == "group" && ( s == Account::Identica || s == Account::Laconica ) ) { type = IdenticaSearch::ReferenceGroup; } else if(scheme == "tag") { switch(s) { case Account::Identica: case Account::Laconica: type = IdenticaSearch::ReferenceHashtag; break; case Account::Twitter: type = TwitterSearch::ReferenceHashtag; } } else if(scheme == "user") { KMenu menu; // menu.addTitle(i18n("Search")); KAction * from = new KAction(KIcon("edit-find-user"),i18n("from %1",url.host()),&menu); KAction * to = new KAction(KIcon("meeting-attending"),i18n("replies to %1",url.host()),&menu); menu.addAction(from); menu.addAction(to); QAction * ret; KAction *cont; switch(s) { case Account::Identica: case Account::Laconica: from->setData(IdenticaSearch::FromUser); to->setData(IdenticaSearch::ToUser); break; case Account::Twitter: cont = new KAction(KIcon("user-properties"),i18n("including %1",url.host()),&menu); menu.addAction(cont); from->setData(TwitterSearch::FromUser); to->setData(TwitterSearch::ToUser); cont->setData(TwitterSearch::ReferenceUser); } ret = menu.exec(QCursor::pos()); if(ret == 0) return; type = ret->data().toInt(); } else if( scheme == "status" ) { if(isBaseStatusShowed) return; Backend *b = new Backend(new Account(*mCurrentAccount), this); connect( b, SIGNAL( singleStatusReceived( Status ) ), this, SLOT( baseStatusReceived(Status) ) ); b->requestSingleStatus( url.host().toInt() ); return; } else { KToolInvocation::invokeBrowser(url.toString()); return; } emit sigSearch(type,url.host()); } void StatusWidget::setupUi() { QGridLayout * buttonGrid = new QGridLayout; btnReply = getButton( "btnReply",i18nc( "@info:tooltip", "Reply" ), "edit-undo" ); btnRemove = getButton( "btnRemove",i18nc( "@info:tooltip", "Remove" ), "edit-delete" ); btnFavorite = getButton( "btnFavorite",i18nc( "@info:tooltip", "Favorite" ), "rating" ); btnFavorite->setCheckable(true); buttonGrid->setRowStretch(0,100); buttonGrid->setColumnStretch(4,100); buttonGrid->setMargin(0); buttonGrid->setSpacing(0); buttonGrid->addWidget(btnReply,1,0); buttonGrid->addWidget(btnRemove,1,1); buttonGrid->addWidget(btnFavorite,1,2); document()->addResource(QTextDocument::ImageResource,QUrl("icon://web"),KIcon("applications-internet").pixmap(8)); setLayout(buttonGrid); connect( btnReply, SIGNAL( clicked( bool ) ), this, SLOT( requestReply() ) ); connect( btnFavorite, SIGNAL( clicked( bool ) ), this, SLOT( setFavorite( bool ) ) ); connect( btnRemove, SIGNAL( clicked( bool ) ), this, SLOT( requestDestroy() ) ); connect(this,SIGNAL(textChanged()),this,SLOT(setHeight())); } void StatusWidget::enterEvent(QEvent* event) { if ( !mCurrentStatus.isDMessage ) btnFavorite->setVisible( true ); if ( mCurrentStatus.user.userId != mCurrentAccount->userId() ) btnReply->setVisible( true ); else btnRemove->setVisible( true ); KTextBrowser::enterEvent(event); } void StatusWidget::leaveEvent(QEvent* event) { btnRemove->setVisible(false); btnFavorite->setVisible(false); btnReply->setVisible(false); KTextBrowser::leaveEvent(event); } KPushButton * StatusWidget::getButton(const QString & objName, const QString & toolTip, const QString & icon) { KPushButton * button = new KPushButton(KIcon(icon),QString()); button->setObjectName(objName); button->setToolTip(toolTip); button->setIconSize(QSize(8,8)); button->setMinimumSize(QSize(20, 20)); button->setMaximumSize(QSize(20, 20)); button->setFlat(true); button->setVisible(false); return button; } StatusWidget::~StatusWidget() { } void StatusWidget::setFavorite( bool isFavorite ) { emit sigFavorite( mCurrentStatus.statusId, isFavorite ); } Status StatusWidget::currentStatus() const { return mCurrentStatus; } void StatusWidget::setCurrentStatus( const Status newStatus ) { mCurrentStatus = newStatus; updateUi(); } void StatusWidget::updateUi() { if ( mCurrentStatus.isDMessage ) { btnFavorite->setVisible( false ); } else if ( mCurrentStatus.user.userId == mCurrentAccount->userId() ) { btnReply->setVisible( false ); } else { btnRemove->setVisible( false ); } mStatus = prepareStatus(mCurrentStatus.content); mSign = generateSign(); if( mStatus.isRightToLeft() ) { QTextOption options(document()->defaultTextOption()); options.setTextDirection( Qt::RightToLeft ); document()->setDefaultTextOption(options); } setUserImage(); setUiStyle(); updateSign(); updateFavoriteUi(); } void StatusWidget::setHeight() { document()->setTextWidth(width()-2); int h = document()->size().toSize().height()+2; setMinimumHeight(h); setMaximumHeight(h); } QString StatusWidget::formatDateTime( const QDateTime &time ) { int seconds = time.secsTo( QDateTime::currentDateTime() ); if ( seconds <= 15 ) { timer.setInterval( _15SECS ); return i18n( "Just now" ); } if ( seconds <= 45 ) { timer.setInterval( _15SECS ); return i18np( "1 sec ago", "%1 secs ago", seconds ); } int minutes = ( seconds - 45 + 59 ) / 60; if ( minutes <= 45 ) { timer.setInterval( _MINUTE ); return i18np( "1 min ago", "%1 mins ago", minutes ); } int hours = ( seconds - 45 * 60 + 3599 ) / 3600; if ( hours <= 18 ) { timer.setInterval( _MINUTE * 15 ); return i18np( "1 hour ago", "%1 hours ago", hours ); } timer.setInterval( _HOUR ); int days = ( seconds - 18 * 3600 + 24 * 3600 - 1 ) / ( 24 * 3600 ); return i18np( "1 day ago", "%1 days ago", days ); } void StatusWidget::requestReply() { kDebug(); emit sigReply( mCurrentStatus.user.screenName, mCurrentStatus.statusId, currentStatus().isDMessage ); } QString StatusWidget::generateSign() { QString sign; sign = "" + mCurrentStatus.user.screenName + " homepage() + mCurrentStatus.user.screenName + "\" title=\"" + mCurrentStatus.user.description + "\"> - "; sign += "statusUrl( mCurrentStatus.statusId, mCurrentStatus.user.screenName ) + "\" title=\"" + mCurrentStatus.creationDateTime.toString() + "\">%1"; if ( mCurrentStatus.isDMessage ) { if( mCurrentStatus.replyToUserId == mCurrentAccount->userId() ) { sign.prepend( "From " ); } else { sign.prepend( "To " ); } } else { - sign += " - " + mCurrentStatus.source; + if( !mCurrentStatus.source.isNull() ) + sign += " - " + mCurrentStatus.source; if ( mCurrentStatus.replyToStatusId > 0 ) { QString link = mCurrentAccount->statusUrl( mCurrentStatus.replyToStatusId, mCurrentStatus.user.screenName ); sign += " - " + i18n("in reply to")+ " "; } } return sign; } void StatusWidget::updateSign() { setHtml( baseText.arg( mImage, mStatus, mSign.arg( formatDateTime( mCurrentStatus.creationDateTime ) ) ) ); } void StatusWidget::requestDestroy() { emit sigDestroy( mCurrentStatus.statusId ); } QString StatusWidget::prepareStatus( const QString &text ) { if(text.isEmpty() && ( mCurrentAccount->serviceType() == Account::Identica || mCurrentAccount->serviceType() == Account::Laconica ) ){ Backend *b = new Backend(new Account(*mCurrentAccount), this); connect(b, SIGNAL(singleStatusReceived( Status )), this, SLOT(missingStatusReceived( Status ))); b->requestSingleStatus(mCurrentStatus.statusId); return text; } QString status = text; ///TODO Adding smile support! /* if(Settings::isSmilysEnabled()){ while((j = s.indexOf(':', i)) != -1){ if(s[j+1]==')' && s[j+2]==')') ; else switch(s[j+1]){ case 'D': break; case ')': break; case '(': break; case 'o': case 'O': break; case '*': case 'x': break; case '|': break; case '/': break; }; } }*/ status.replace( '<', "<" ); status.replace( '>', ">" ); status.replace( " www.", " http://www." ); if ( status.startsWith( QLatin1String("www.") ) ) status.prepend( "http://" ); status.replace(mUrlRegExp,"\\1"); status.replace(mUserRegExp,"\\1@\\2 "); if ( mCurrentAccount->serviceType() == Account::Identica || mCurrentAccount->serviceType() == Account::Laconica ) { status.replace(mGroupRegExp,"\\1!\\2 "); status.replace(mHashtagRegExp,"\\1#\\2 "); } else { status.replace(mHashtagRegExp,"\\1#\\2"); } return status; } QString StatusWidget::getColorString(const QColor& color) { return "rgb(" + QString::number(color.red()) + ',' + QString::number(color.green()) + ',' + QString::number(color.blue()) + ')'; } void StatusWidget::setUnread( Notify notifyType ) { mIsRead = false; if ( notifyType == WithNotify ) { QString iconUrl = MediaManager::self()->getImageLocalPathIfExist( mCurrentStatus.user.profileImageUrl ); QString name = mCurrentStatus.user.screenName; QString msg = mCurrentStatus.content; if ( Settings::notifyType() == SettingsBase::KNotify ) { KNotification *notify = new KNotification( "new-status-arrived", parentWidget() ); notify->setText( QString( "" + name + ":
" + msg + "
" ) ); notify->setPixmap( QPixmap( iconUrl ) ); notify->setFlags( KNotification::RaiseWidgetOnActivation | KNotification::Persistent ); notify->setActions( i18n( "Reply" ).split( ',' ) ); connect( notify, SIGNAL( action1Activated() ), this , SLOT( requestReply() ) ); notify->sendEvent(); QTimer::singleShot( Settings::notifyInterval()*1000, notify, SLOT( close() ) ); } else if ( Settings::notifyType() == SettingsBase::LibNotify ) { QString libnotifyCmd = QString( "notify-send -t " ) + QString::number( Settings::notifyInterval() * 1000 ) + QString( " -u low -i " + iconUrl + " \"" ) + name + QString( "\" \"" ) + msg + QString( "\"" ); QProcess::execute( libnotifyCmd ); } } } void StatusWidget::setRead(bool read) { mIsRead = read; setUiStyle(); } void StatusWidget::setUiStyle() { setStyleSheet( style ); } void StatusWidget::updateFavoriteUi() { btnFavorite->setChecked(mCurrentStatus.isFavorited); } bool StatusWidget::isRead() const { return mIsRead; } void StatusWidget::setUserImage() { connect( MediaManager::self(), SIGNAL( imageFetched( const QString &, const QString & ) ), this, SLOT( userImageLocalPathFetched( const QString&, const QString& ) ) ); MediaManager::self()->getImageLocalPathDownloadAsyncIfNotExists( mCurrentAccount->serviceName() + mCurrentStatus.user.screenName , mCurrentStatus.user.profileImageUrl ); } void StatusWidget::userImageLocalPathFetched( const QString &remotePath, const QString &localPath ) { if ( remotePath == mCurrentStatus.user.profileImageUrl ) { mImage = ""; updateSign(); disconnect( MediaManager::self(), SIGNAL( imageFetched( const QString &, const QString & ) ), this, SLOT( userImageLocalPathFetched( const QString&, const QString& ) ) ); } } void StatusWidget::missingStatusReceived( Status status ) { if( mCurrentStatus.statusId == mCurrentStatus.statusId ){ mCurrentStatus = status; updateUi(); sender()->deleteLater(); } } void StatusWidget::resizeEvent(QResizeEvent* event) { setHeight(); KTextBrowser::resizeEvent(event); } void StatusWidget::baseStatusReceived( Status status ) { if(isBaseStatusShowed) return; isBaseStatusShowed = true; QString color; if( Settings::isCustomUi() ) { color = Settings::defaultForeColor().lighter().name(); } else { color = this->palette().dark().color().name(); } QString baseStatusText = "

"; baseStatusText += "" + status.user.screenName + " : "; baseStatusText += prepareStatus( status.content ) + "

"; mStatus.prepend( baseStatusText ); updateSign(); } #include "statuswidget.moc" diff --git a/src/twittersearch.cpp b/src/twittersearch.cpp index 304640d6..28bbdf8b 100644 --- a/src/twittersearch.cpp +++ b/src/twittersearch.cpp @@ -1,210 +1,223 @@ /* This file is part of choqoK, the KDE micro-blogging client Copyright (C) 2008-2009 Mehrdad Momeny 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) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. 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; if not, see http://www.gnu.org/licenses/ */ #include "twittersearch.h" #include #include #include #include #include #include #include -TwitterSearch::TwitterSearch( const QString searchUrl, QObject *parent ) : - Search(searchUrl, parent) +#include "backend.h" + +TwitterSearch::TwitterSearch( Account* account, const QString searchUrl, QObject *parent ) : + Search(account, searchUrl, parent) { kDebug(); - mSearchTypes[CustomSearch] = i18n( "Custom Search" ); - mSearchTypes[ToUser] = i18n( "Tweets To This User" ); - mSearchTypes[FromUser] = i18n( "Tweets From This User" ); - mSearchTypes[ReferenceUser] = i18n( "Tweets Including This User's Name" ); - mSearchTypes[ReferenceHashtag] = i18n( "Tweets Including This Hashtag" ); + mSearchTypes[CustomSearch].first = i18n( "Custom Search" ); + mSearchTypes[CustomSearch].second = true; + + mSearchTypes[ToUser].first = i18n( "Tweets To This User" ); + mSearchTypes[ToUser].second = true; + + mSearchTypes[FromUser].first = i18n( "Tweets From This User" ); + mSearchTypes[FromUser].second = true; + + mSearchTypes[ReferenceUser].first = i18n( "Tweets Including This User's Name" ); + mSearchTypes[ReferenceUser].second = true; + + mSearchTypes[ReferenceHashtag].first = i18n( "Tweets Including This Hashtag" ); + mSearchTypes[ReferenceHashtag].second = true; } TwitterSearch::~TwitterSearch() { kDebug(); } -KUrl TwitterSearch::buildUrl( QString query, int option, uint sinceStatusId ) +KUrl TwitterSearch::buildUrl( QString query, int option, uint sinceStatusId, uint count, uint page ) { kDebug(); QString baseUrl = "http://search.twitter.com/search.atom?"; if( sinceStatusId ) baseUrl += "since_id=" + QString::number( sinceStatusId ) + '&'; + if( count && count <= 100 ) + baseUrl += "rpp=" + QString::number( count ) + "&"; + baseUrl += "page=" + QString::number( page ) + "&"; baseUrl += "q="; - kDebug() << "Search URL: " << baseUrl; - QString formattedQuery; switch ( option ) { case CustomSearch: formattedQuery = query; break; case ToUser: formattedQuery = "to:" + query; break; case FromUser: formattedQuery = "from:" + query; break; case ReferenceUser: formattedQuery = '@' + query; break; case ReferenceHashtag: formattedQuery = "%23" + query; break; default: formattedQuery = query; break; }; - KUrl url( baseUrl + formattedQuery ); + KUrl url; + url.setUrl( baseUrl + formattedQuery ); return url; } -void TwitterSearch::requestSearchResults( QString query, int option, uint sinceStatusId ) +void TwitterSearch::requestSearchResults( QString query, int option, uint sinceStatusId, uint count, uint page ) { kDebug(); - KUrl url = buildUrl( query, option, sinceStatusId ); + KUrl url = buildUrl( query, option, sinceStatusId, count, page ); KIO::StoredTransferJob *job = KIO::storedGet( url, KIO::Reload, KIO::HideProgressInfo ); if( !job ) { kDebug() << "Cannot create a http GET request!"; emit error( i18n( "Unable to fetch search results." ) ); return; } connect( job, SIGNAL( result( KJob* ) ), this, SLOT( searchResultsReturned( KJob* ) ) ); job->start(); } void TwitterSearch::searchResultsReturned( KJob* job ) { kDebug(); if( job == 0 ) { kDebug() << "job is a null pointer"; emit error( i18n( "Unable to fetch search results." ) ); return; } if( job->error() ) { kError() << "Error: " << job->errorString(); emit error( i18n( "Unable to fetch search results. ERROR: %1", job->errorString() ) ); return; } KIO::StoredTransferJob *jj = qobject_cast( job ); QList* statusList = parseAtom( jj->data() ); emit searchResultsReceived( *statusList ); } QList* TwitterSearch::parseAtom( const QByteArray &buffer ) { kDebug(); QDomDocument document; QList *statusList = new QList; document.setContent( buffer ); QDomElement root = document.documentElement(); if ( root.tagName() != "feed" ) { kDebug() << "There is no feed element in Atom feed " << buffer.data(); return statusList; } QDomNode node = root.firstChild(); QString timeStr; while ( !node.isNull() ) { if ( node.toElement().tagName() != "entry" ) { node = node.nextSibling(); continue; } QDomNode entryNode = node.firstChild(); Status status; status.isDMessage = false; while ( !entryNode.isNull() ) { if ( entryNode.toElement().tagName() == "id" ) { // Fomatting example: "tag:search.twitter.com,2005:1235016836" int id = 0; sscanf( qPrintable( entryNode.toElement().text() ), "tag:search.twitter.com,%*d:%d", &id); status.statusId = id; } else if ( entryNode.toElement().tagName() == "published" ) { // Formatting example: "2009-02-21T19:42:39Z" // Need to extract date in similar fashion to dateFromString int year, month, day, hour, minute, second; sscanf( qPrintable( entryNode.toElement().text() ), "%d-%d-%dT%d:%d:%d%*s", &year, &month, &day, &hour, &minute, &second); QDateTime recognized( QDate( year, month, day), QTime( hour, minute, second ) ); recognized.setTimeSpec( Qt::UTC ); status.creationDateTime = recognized; } else if ( entryNode.toElement().tagName() == "title" ) { status.content = entryNode.toElement().text(); } else if ( entryNode.toElement().tagName() == "twitter:source" ) { status.source = entryNode.toElement().text(); } else if ( entryNode.toElement().tagName() == "link" && entryNode.toElement().attributeNode( "rel" ).value() == "image") { QDomAttr imageAttr = entryNode.toElement().attributeNode( "href" ); status.user.profileImageUrl = imageAttr.value(); } else if ( entryNode.toElement().tagName() == "author") { QDomNode userNode = entryNode.firstChild(); while ( !userNode.isNull() ) { if ( userNode.toElement().tagName() == "name" ) { QString fullName = userNode.toElement().text(); int bracketPos = fullName.indexOf( " ", 0 ); QString screenName = fullName.left( bracketPos ); QString name = fullName.right ( fullName.size() - bracketPos - 2 ); name.chop( 1 ); status.user.name = name; status.user.screenName = screenName; } userNode = userNode.nextSibling(); } } entryNode = entryNode.nextSibling(); } status.isFavorited = false; status.isTruncated = false; status.replyToStatusId = 0; statusList->insert( 0, status ); node = node.nextSibling(); } return statusList; } diff --git a/src/twittersearch.h b/src/twittersearch.h index e1b86f2c..99f2cc5c 100644 --- a/src/twittersearch.h +++ b/src/twittersearch.h @@ -1,59 +1,63 @@ /* This file is part of choqoK, the KDE micro-blogging client Copyright (C) 2008-2009 Mehrdad Momeny 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) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. 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; if not, see http://www.gnu.org/licenses/ */ #ifndef TWITTERSEARCH_H #define TWITTERSEARCH_H #include #include "search.h" #include "datacontainers.h" /** Twitter.com search API implementation. @author Stephen Henderson */ class TwitterSearch : public Search { Q_OBJECT public: enum SearchType { CustomSearch = 0, ToUser, FromUser, ReferenceUser, ReferenceHashtag }; - explicit TwitterSearch( const QString searchUrl = QString(), QObject *parent=0 ); + explicit TwitterSearch( Account* account, const QString searchUrl = QString(), QObject *parent=0 ); virtual ~TwitterSearch(); private: - virtual KUrl buildUrl( QString query, int option, uint sinceStatusId = 0 ); + virtual KUrl buildUrl( QString query, int option, uint sinceStatusId = 0, uint count = 0, uint page = 1 ); QList* parseAtom( const QByteArray &buffer ); public slots: - virtual void requestSearchResults( QString query, int option, uint sinceStatusId = 0 ); + virtual void requestSearchResults( QString query, + int option, + uint sinceStatusId = 0, + uint count = 0, + uint page = 1 ); protected slots: virtual void searchResultsReturned( KJob *job ); }; #endif