/* * Copyright 2009 Andrew Stromme * Copyright 2012 Jeremy Whiting * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, 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 Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "rememberthemilk-plasmoid.h" // Qt Includes #include #include #include // KDE Includes #include #include #include // Plasma Includes #include #include #include #include #include #include #include #include // Local #include "ui_authenticate.h" #include "ui_general.h" #include "taskmodel.h" #include "tasksortfilter.h" #include RememberTheMilkPlasmoid::RememberTheMilkPlasmoid(QObject* parent, const QVariantList& args) : Plasma::PopupApplet(parent, args), m_authenticated(false), m_sortBy(SortDue), m_taskEditor(0), m_engine(0), m_taskService(0), m_graphicsWidget(0), m_authService(0), m_authWidgetUi(new Ui::AuthWidget), m_generalOptionsUi(new Ui::GeneralWidget) { setBackgroundHints(DefaultBackground); setPopupIcon("view-pim-journal"); // TODO: Create/Find better item m_authWidget = new QWidget(); m_authWidgetUi->setupUi(m_authWidget); m_authWidgetUi->webView->hide(); m_generalOptions = new QWidget(); connect(m_authWidgetUi->signupLabel, SIGNAL(leftClickedUrl(QString)), KToolInvocation::self(), SLOT(invokeBrowser(QString))); m_generalOptionsUi->setupUi(m_generalOptions); m_generalOptionsUi->sortType->addItem(i18n("Due Date")); m_generalOptionsUi->sortType->addItem(i18n("Priority")); } RememberTheMilkPlasmoid::~RememberTheMilkPlasmoid() { if (hasFailedToLaunch()) { kDebug() << "failed to launch"; } delete m_authWidget; delete m_authWidgetUi; delete m_generalOptions; delete m_generalOptionsUi; } void RememberTheMilkPlasmoid::init() { setAspectRatioMode(Plasma::IgnoreAspectRatio); graphicsWidget(); if (!m_engine || !m_engine->isValid()) { return Plasma::Applet::init(); } m_engine->connectSource("Auth", this); m_authService = m_engine->serviceForSource("Auth"); m_authService->setParent(this); connect(m_authService, SIGNAL(finished(Plasma::ServiceJob*)), SLOT(jobFinished(Plasma::ServiceJob*))); configChanged(); Plasma::Applet::init(); setAssociatedApplicationUrls(KUrl("http://rememberthemilk.com")); } void RememberTheMilkPlasmoid::startAuth() { disconnect(m_authWidgetUi->authenticateButton, SIGNAL(clicked(bool)), this, SLOT(startAuth())); connect(m_authWidgetUi->authenticateButton, SIGNAL(clicked(bool)), this, SLOT(continueAuth())); m_authWidgetUi->authenticateButton->setText(i18n("Click to finish authentication")); KConfigGroup cg = m_authService->operationDescription("StartLogin"); Plasma::ServiceJob* authJob = m_authService->startOperationCall(cg); connect(authJob, SIGNAL(finished(KJob*)), SLOT(authJobFinished(KJob*))); busyUntil(authJob); busyUntil(0); // Sets busy until we manually call jobFinished(0). Busy until first tasks refresh m_authenticated = false; } void RememberTheMilkPlasmoid::authJobFinished(KJob* job) { Plasma::ServiceJob *authJob = qobject_cast(job); QString url = authJob->result().toString(); m_authWidgetUi->webView->setUrl(QUrl(url)); m_authWidgetUi->webView->show(); } void RememberTheMilkPlasmoid::continueAuth() { KConfigGroup cg = m_authService->operationDescription("Login"); Plasma::ServiceJob* authJob = m_authService->startOperationCall(cg); busyUntil(authJob); busyUntil(0); // Sets busy until we manually call jobFinished(0). Busy until first tasks refresh m_authenticated = false; } void RememberTheMilkPlasmoid::configAccepted() { switch(m_generalOptionsUi->sortType->currentIndex()) { case 0: setSortBy(SortDue); break; case 1: setSortBy(SortPriority); break; } } void RememberTheMilkPlasmoid::configFinished() { m_authWidget->setParent(0); // We don't want these widgets destroyed along with the configuration window m_generalOptions->setParent(0); // They are reused later } void RememberTheMilkPlasmoid::createConfigurationInterface(KConfigDialog* parent) { connect(parent, SIGNAL(finished()), this, SLOT(configFinished())); connect(parent, SIGNAL(applyClicked()), this, SLOT(configAccepted())); connect(parent, SIGNAL(okClicked()), this, SLOT(configAccepted())); connect(m_authWidgetUi->authenticateButton, SIGNAL(clicked(bool)), this, SLOT(startAuth())); m_generalOptionsUi->sortType->setCurrentIndex(m_sortBy); KPageWidgetItem* general = parent->addPage(m_generalOptions, i18n("General"), "configure", i18n("General Configuration Options")); KPageWidgetItem* auth = parent->addPage(m_authWidget, i18n("Authentication"), icon(), i18n("Remember The Milk Authentication")); if (!m_authenticated) parent->setCurrentPage(auth); else parent->setCurrentPage(general); connect(m_generalOptionsUi->sortType, SIGNAL(currentIndexChanged(int)), parent, SLOT(settingsModified())); connect(this, SIGNAL(authenticated()), parent, SLOT(settingsModified())); } void RememberTheMilkPlasmoid::setSortBy(SortBy sortBy) { m_filterModel->setSortBy(sortBy); m_model->setDropType(sortBy); switch (sortBy) { case SortPriority: config().writeEntry("SortBy", "Priority"); m_delegate->setSmallTasks(false); break; case SortDue: config().writeEntry("SortBy", "Date"); m_delegate->setSmallTasks(true); break; } m_sortBy = sortBy; } void RememberTheMilkPlasmoid::dataUpdated(const QString& name, const Plasma::DataEngine::Data& data) { if (name == "Auth") { m_authenticated = data.value("ValidToken").toBool(); kDebug() << "Auth: " << m_authenticated; m_authWidgetUi->webView->hide(); if (m_authenticated) { m_authWidgetUi->authStatus->setText(i18n("Authenticated")); m_authWidgetUi->kled->setState(KLed::On); m_authWidgetUi->kled->setColor(Qt::green); } else { m_authWidgetUi->authStatus->setText(i18n("Not Authenticated")); m_authWidgetUi->kled->setState(KLed::Off); m_authWidgetUi->kled->setColor(Qt::red); m_busyUntil.clear(); // no longer trying to authenticate setBusy(false); } if (m_authenticated) { setConfigurationRequired(false); m_token = data.value("Token").toString(); config().writeEntry("token", m_token); m_engine->connectSource("Lists", this); m_engine->connectSource("Tasks", this); busyUntil(0); emit authenticated(); } else if (m_categoriesBar->count() == 1 && m_lists.isEmpty()) m_categoriesBar->nativeWidget()->setTabText(0, i18n("Login Failed. Please try again.")); } else if (name == "Lists") { if (m_categoriesBar->count() == 1 && m_lists.isEmpty()) m_categoriesBar->removeTab(0); // remove "refreshing" tab; //while (m_categoriesBar->count()) // m_categoriesBar->removeTab(0); //m_lists.clear(); //FIXME: What happens when a list is deleted? foreach(const QString &key, data.keys()) { if (!m_lists.contains(key.toULongLong())) { m_categoriesBar->addTab(data.value(key).toString()); m_lists.append(key.toULongLong()); } m_model->listUpdate(key.toULongLong()); } int current = config().readEntry("CurrentList").toInt(); if (m_categoriesBar->count() >= current) { m_categoriesBar->setCurrentIndex(current); listChanged(current); // doesn't always update.. i.e if current is actually the current } } else if (name == "Tasks") { if (data.keys().count() > 0) jobFinished(0); // No longer busy. } } void RememberTheMilkPlasmoid::listChanged(int pos) { if (pos < 0 || pos >= m_lists.size()) return; config().writeEntry("CurrentList", pos); m_model->switchToList(m_lists.at(pos)); } void RememberTheMilkPlasmoid::createTask(const QString& task) { if (!m_taskService) { m_taskService = m_engine->serviceForSource("Tasks"); m_taskService->setParent(this); connect(m_taskService, SIGNAL(finished(Plasma::ServiceJob*)), SLOT(jobFinished(Plasma::ServiceJob*))); } KConfigGroup cg = m_taskService->operationDescription("create"); cg.writeEntry("task", task); kDebug() << "QString::number(m_model->currentList()->id)"; cg.writeEntry("listid", m_model->currentList()->id); busyUntil(m_taskService->startOperationCall(cg)); } void RememberTheMilkPlasmoid::onTaskEditorHide() { m_tasksView->nativeWidget()->setEnabled(true); m_priorityLabel->setText(i18n("Remember The Milk Tasks")); } void RememberTheMilkPlasmoid::showTaskEditor(QModelIndex index) { if (index.data(Qt::RTMItemType).toInt() != RTMTaskItem) // We have a header rather than a task. return; m_taskEditor->setModelIndex(index); m_taskEditor->hide(); m_taskEditor->show(); m_tasksView->nativeWidget()->setEnabled(false); m_taskEditor->setEnabled(true); m_taskEditor->startAnimation(m_tasksView->size()); m_priorityLabel->setText(i18n("Editing Task: ") + index.data(Qt::RTMNameRole).toString()); geometryChanged(); } void RememberTheMilkPlasmoid::busyUntil(Plasma::ServiceJob* job) { setBusy(true); m_busyUntil.append(job); } void RememberTheMilkPlasmoid::jobFinished(Plasma::ServiceJob* job) { if (m_busyUntil.contains(job)) { m_busyUntil.removeAll(job); } if (m_busyUntil.isEmpty()) setBusy(false); } void RememberTheMilkPlasmoid::resizeEvent(QGraphicsSceneResizeEvent* event) { if (m_taskEditor) m_taskEditor->resize(m_tasksView->size()); Plasma::Applet::resizeEvent(event); } QGraphicsWidget* RememberTheMilkPlasmoid::graphicsWidget() { if (m_graphicsWidget) return m_graphicsWidget; m_engine = dataEngine("rtm"); if (!m_engine || !m_engine->isValid()) { setFailedToLaunch(true, i18n("Failed to load the Remember The Milk DataEngine")); return m_graphicsWidget; } m_graphicsWidget = new QGraphicsWidget(this); m_model = new TaskModel(m_engine, this); connect(m_model, SIGNAL(jobStarted(Plasma::ServiceJob*)), SLOT(busyUntil(Plasma::ServiceJob*))); connect(m_model, SIGNAL(jobFinished(Plasma::ServiceJob*)), SLOT(jobFinished(Plasma::ServiceJob*))); m_priorityLabel = new Plasma::Label(this); m_priorityLabel->setAlignment(Qt::AlignHCenter); m_priorityLabel->setText(i18n("Remember The Milk Tasks")); m_categoriesBar = new Plasma::TabBar(this); m_categoriesBar->addTab(i18n("No Data Yet. Refreshing...")); connect(m_categoriesBar, SIGNAL(currentChanged(int)), SLOT(listChanged(int))); QPalette palette; palette.setBrush(QPalette::Background, Plasma::Theme::defaultTheme()->color(Plasma::Theme::BackgroundColor)); palette.setBrush(QPalette::Text, Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor)); m_delegate = new TaskItemDelegate(this); m_filterModel = new TaskSortFilter(m_model, this); connect(m_model, SIGNAL(modelUpdated()), m_filterModel, SLOT(listChanged())); m_tasksView = new Plasma::TreeView(this); m_tasksView->installEventFilter(m_delegate); m_tasksView->setModel(m_filterModel); m_tasksView->nativeWidget()->setItemDelegate(m_delegate); m_tasksView->nativeWidget()->header()->setHidden(true); m_tasksView->nativeWidget()->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); m_tasksView->nativeWidget()->setRootIsDecorated(false); m_tasksView->nativeWidget()->setIndentation(0); m_tasksView->nativeWidget()->viewport()->setAutoFillBackground(false); m_tasksView->nativeWidget()->viewport()->setPalette( palette ); m_tasksView->nativeWidget()->setSelectionMode(QAbstractItemView::SingleSelection); m_tasksView->nativeWidget()->setDragEnabled(true); m_tasksView->nativeWidget()->setAcceptDrops(true); m_tasksView->nativeWidget()->viewport()->setAcceptDrops(true); m_tasksView->nativeWidget()->setDropIndicatorShown(false); m_tasksView->nativeWidget()->setDragDropMode(QAbstractItemView::DragDrop); m_tasksView->nativeWidget()->setSortingEnabled(true); m_tasksView->nativeWidget()->sortByColumn(0, Qt::AscendingOrder); m_tasksView->nativeWidget()->expandAll(); connect(m_tasksView->nativeWidget(), SIGNAL(clicked(QModelIndex)), this, SLOT(showTaskEditor(QModelIndex))); m_searchLine = new Plasma::LineEdit(this); m_searchLine->nativeWidget()->setClearButtonShown(true); m_searchLine->nativeWidget()->setClickMessage(i18n("Filter Tasks...")); connect(m_searchLine->nativeWidget(), SIGNAL(textChanged(QString)), m_filterModel, SLOT(setFilterWildcard(QString))); m_addTaskLine = new Plasma::LineEdit(this); // must wait for plasma theming (4.3?) to get a themed lineedit m_addTaskLine->nativeWidget()->setClearButtonShown(true); m_addTaskLine->nativeWidget()->setClickMessage(i18n("Create New Task...")); connect(m_addTaskLine->nativeWidget(), SIGNAL(returnPressed(QString)), m_addTaskLine->nativeWidget(), SLOT(clear())); connect(m_addTaskLine->nativeWidget(), SIGNAL(returnPressed(QString)), this, SLOT(createTask(QString))); kDebug() << "Creating layout"; m_mainLayout = new QGraphicsLinearLayout(Qt::Vertical, m_graphicsWidget); m_mainLayout->addItem(m_priorityLabel); m_mainLayout->addItem(m_categoriesBar); m_mainLayout->addItem(m_searchLine); m_mainLayout->addItem(m_tasksView); m_mainLayout->addItem(m_addTaskLine); m_graphicsWidget->setLayout(m_mainLayout); m_graphicsWidget->setMinimumSize(250, 300); m_graphicsWidget->setPreferredSize(300, 500); m_taskEditor = new TaskEditor(m_engine, m_tasksView); m_taskEditor->hide(); connect(m_taskEditor, SIGNAL(requestDiscardChanges()), this, SLOT(onTaskEditorHide())); connect(m_taskEditor, SIGNAL(requestSaveChanges()), this, SLOT(onTaskEditorHide())); connect(m_taskEditor, SIGNAL(jobStarted(Plasma::ServiceJob*)), SLOT(busyUntil(Plasma::ServiceJob*))); connect(m_taskEditor, SIGNAL(jobFinished(Plasma::ServiceJob*)), SLOT(jobFinished(Plasma::ServiceJob*))); return m_graphicsWidget; } void RememberTheMilkPlasmoid::configChanged() { KConfigGroup cg = config(); m_token = cg.readEntry("token"); kDebug() << "Token from config: " << m_token; if (m_token.isNull()) setConfigurationRequired(true, i18n("Authentication to Remember The Milk needed")); else { KConfigGroup cg = m_authService->operationDescription("AuthWithToken"); cg.writeEntry("token", m_token); busyUntil(m_authService->startOperationCall(cg)); busyUntil(0); // Sets busy until we manually call jobFinished(0). Busy until first tasks refresh } QString sortBy = config().readEntry("SortBy").toLower(); kDebug() << "Config says sort by " << sortBy; if (sortBy == "date" || sortBy == "due") setSortBy(SortDue); else if (sortBy == "priority") setSortBy(SortPriority); else setSortBy(SortDue); // Default } #include "moc_rememberthemilk-plasmoid.cpp"