2 This file is part of Blogilo, A KDE Blogging Client
4 Copyright (C) 2008-2010 Mehrdad Momeny <mehrdad.momeny@gmail.com>
5 Copyright (C) 2008-2010 Golnaz Nilieh <g382nilieh@gmail.com>
6 Copyright (C) 2013-2016 Laurent Montel <montel@kde.org>
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of
11 the License or (at your option) version 3 or any later version
12 accepted by the membership of KDE e.V. (or its successor approved
13 by the membership of KDE e.V.), which shall act as a proxy
14 defined in Section 14 of version 3 of the license.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, see http://www.gnu.org/licenses/
25 #include "mainwindow.h"
29 #include "postentry.h"
30 #include "addeditblog.h"
32 #include "bilbomedia.h"
34 #include "bilboblog.h"
35 #include "blogsettings.h"
36 #include "poststabwidget.h"
37 #include "uploadmediadialog.h"
38 #include "configuredialog.h"
40 #include "ui_advancedsettingsbase.h"
41 #include "ui_settingsbase.h"
42 #include "ui_editorsettingsbase.h"
44 #include "Libkdepim/ProgressDialog"
45 #include <Libkdepim/StatusbarProgressWidget>
46 #include <Libkdepim/ProgressStatusBarWidget>
48 #include <qtabwidget.h>
49 #include <KStatusNotifierItem>
50 #include <qstatusbar.h>
51 #include <KToggleAction>
52 #include <kactioncollection.h>
53 #include <KActionMenu>
55 #include <kconfigdialog.h>
56 #include "blogilo_debug.h"
57 #include <kmessagebox.h>
58 #include <KLocalizedString>
59 #include <KSelectAction>
60 #include <kimagefilepreview.h>
64 #include <QDockWidget>
65 #include <QProgressBar>
68 #include <QDesktopServices>
72 MainWindow::MainWindow()
74 activePost(Q_NULLPTR
),
75 systemTray(Q_NULLPTR
),
76 previousActivePostIndex(-1),
79 mCurrentBlogId(__currentBlogId
)
81 setWindowTitle(i18n("Blogilo"));
83 tabPosts
= new PostsTabWidget(this);
84 setCentralWidget(tabPosts
);
85 connect(tabPosts
, &PostsTabWidget::createNewPost
, this, &MainWindow::slotCreateNewPost
);
86 connect(tabPosts
, &PostsTabWidget::closeTabClicked
, this, &MainWindow::slotCloseTabClicked
);
87 connect(tabPosts
, &PostsTabWidget::tabCloseRequested
, this, &MainWindow::slotRemovePostEntry
);
88 connect(tabPosts
, &PostsTabWidget::tabRemoveAllExclude
, this, &MainWindow::slotRemoveAllExclude
);
90 toolbox
= new Toolbox(this);
91 toolboxDock
= new QDockWidget(i18n("Toolbox"), this);
92 toolboxDock
->setAllowedAreas(Qt::RightDockWidgetArea
| Qt::LeftDockWidgetArea
);
93 toolboxDock
->setFeatures(QDockWidget::AllDockWidgetFeatures
);
94 toolboxDock
->setWidget(toolbox
);
96 toolboxDock
->setObjectName(QStringLiteral("dock_toolbox"));
97 toolbox
->setObjectName(QStringLiteral("toolbox"));
99 this->addDockWidget(Qt::RightDockWidgetArea
, toolboxDock
);
101 // then, setup our actions
105 // a call to KXmlGuiWindow::setupGUI() populates the GUI
106 // with actions, using KXMLGUI.
107 // It also applies the saved mainwindow settings, if any, and ask the
108 // mainwindow to automatically save settings if changed: window size,
109 // toolbar position, icon size, etc.
112 toolbox
->setVisible(Settings::showToolboxOnStart());
113 actionCollection()->action(QStringLiteral("toggle_toolbox"))->setChecked(Settings::showToolboxOnStart());
117 connect(tabPosts
, &PostsTabWidget::currentChanged
, this, &MainWindow::slotActivePostChanged
);
118 connect(toolbox
, &Toolbox::sigEntrySelected
, this, &MainWindow::slotNewPostOpened
);
119 connect(toolbox
, &Toolbox::sigError
, this, &MainWindow::slotError
);
120 connect(toolbox
, &Toolbox::sigBusy
, this, &MainWindow::slotBusy
);
122 foreach (BilboBlog
*blog
, DBMan::self()->blogList()) {
123 QAction
*act
= new QAction(blog
->title(), blogs
);
124 act
->setCheckable(true);
125 act
->setData(blog
->id());
126 blogs
->addAction(act
);
128 connect(blogs
, static_cast<void (KSelectAction::*)(QAction
*)>(&KSelectAction::triggered
), this, &MainWindow::currentBlogChanged
);
129 QTimer::singleShot(0, this, SLOT(loadTempPosts()));
132 MainWindow::~MainWindow()
136 void MainWindow::setupStatusBar()
140 void MainWindow::slotUploadFileDone(const QString
&serviceName
, const QString
&link
)
142 Q_UNUSED(serviceName
);
143 KMessageBox::information(this, i18n("File uploaded. You can access to it at this url %1", link
), i18n("Upload File"));
146 void MainWindow::slotUploadFileFailed(const QString
&serviceName
, const QString
&filename
)
148 Q_UNUSED(serviceName
);
150 KMessageBox::error(this, i18n("Error during upload."), i18n("Upload File"));
153 void MainWindow::slotActionFailed(const QString
&serviceName
, const QString
&error
)
155 KMessageBox::error(this, i18n("%1 return an error '%2'", serviceName
, error
), i18n("Error"));
158 void MainWindow::slotCloseTabClicked()
160 const int currentIndex
= tabPosts
->currentIndex();
161 if (currentIndex
!= -1) {
162 slotRemovePostEntry(currentIndex
);
166 void MainWindow::closeEvent(QCloseEvent
*event
)
169 if (!DBMan::self()->clearTempEntries()) {
170 qCDebug(BLOGILO_LOG
) << "Could not erase temp_post table: " << DBMan::self()->lastErrorText();
172 const int count
= tabPosts
->count();
174 toolbox
->getFieldsValue(activePost
->currentPost());
175 for (int i
= 0; i
< count
; ++i
) {
176 PostEntry
*pst
= qobject_cast
<PostEntry
*>(tabPosts
->widget(i
));
177 DBMan::self()->saveTempEntry(*pst
->currentPost(), pst
->currentPostBlogId());
183 void MainWindow::setupActions()
185 KStandardAction::quit(qApp
, SLOT(quit()), actionCollection());
187 KStandardAction::preferences(this, SLOT(optionsPreferences()), actionCollection());
189 // custom menu and menu item
190 QAction
*actNewPost
= new QAction(QIcon::fromTheme(QStringLiteral("document-new")), i18n("New Post"), this);
191 actionCollection()->addAction(QStringLiteral("new_post"), actNewPost
);
192 actionCollection()->setDefaultShortcut(actNewPost
, QKeySequence(Qt::CTRL
+ Qt::Key_N
));
193 connect(actNewPost
, &QAction::triggered
, this, &MainWindow::slotCreateNewPost
);
195 QAction
*actAddBlog
= new QAction(QIcon::fromTheme(QStringLiteral("list-add")), i18n("Add Blog..."), this);
196 actionCollection()->addAction(QStringLiteral("add_blog"), actAddBlog
);
197 connect(actAddBlog
, &QAction::triggered
, this, &MainWindow::addBlog
);
199 QAction
*actPublish
= new QAction(QIcon::fromTheme(QStringLiteral("arrow-up")), i18n("Submit..."), this);
200 actionCollection()->addAction(QStringLiteral("publish_post"), actPublish
);
201 connect(actPublish
, &QAction::triggered
, this, &MainWindow::slotPublishPost
);
203 QAction
*actUpload
= new QAction(QIcon::fromTheme(QStringLiteral("upload-media")), i18n("Upload Media..."), this);
204 actionCollection()->addAction(QStringLiteral("upload_media"), actUpload
);
205 connect(actUpload
, &QAction::triggered
, this, &MainWindow::uploadMediaObject
);
207 QAction
*actSaveLocally
= new QAction(QIcon::fromTheme(QStringLiteral("document-save")), i18n("Save Locally"), this);
208 actionCollection()->addAction(QStringLiteral("save_locally"), actSaveLocally
);
209 actionCollection()->setDefaultShortcut(actSaveLocally
, QKeySequence(Qt::CTRL
+ Qt::Key_S
));
210 connect(actSaveLocally
, &QAction::triggered
, this, &MainWindow::slotSavePostLocally
);
212 KToggleAction
*actToggleToolboxVisible
= new KToggleAction(i18n("Show Toolbox"), this);
213 actionCollection()->addAction(QStringLiteral("toggle_toolbox"), actToggleToolboxVisible
);
214 actionCollection()->setDefaultShortcut(actToggleToolboxVisible
, QKeySequence(Qt::CTRL
+ Qt::Key_T
));
215 connect(actToggleToolboxVisible
, &KToggleAction::toggled
, this, &MainWindow::slotToggleToolboxVisible
);
216 connect(toolboxDock
, &QDockWidget::visibilityChanged
, this, &MainWindow::slotToolboxVisibilityChanged
);
218 blogs
= new KSelectAction(this);
219 actionCollection()->addAction(QStringLiteral("blogs_list"), blogs
);
221 QAction
*actOpenBlog
= new QAction(QIcon::fromTheme(QStringLiteral("applications-internet")), i18n("Open in browser"), this);
222 actionCollection()->addAction(QStringLiteral("open_blog_in_browser"), actOpenBlog
);
223 actOpenBlog
->setToolTip(i18n("Open current blog in browser"));
224 connect(actOpenBlog
, &QAction::triggered
, this, &MainWindow::slotOpenCurrentBlogInBrowser
);
228 void MainWindow::loadTempPosts()
230 QMap
<BilboPost
*, int> tempList
= DBMan::self()->listTempPosts();
231 const int count
= tempList
.count();
233 QMap
<BilboPost
*, int>::ConstIterator it
= tempList
.constBegin();
234 QMap
<BilboPost
*, int>::ConstIterator endIt
= tempList
.constEnd();
235 for (; it
!= endIt
; ++it
) {
236 createPostEntry(it
.value(), (*it
.key()));
241 // activePost = qobject_cast<PostEntry*>( tabPosts->currentWidget() );
242 previousActivePostIndex
= 0;
244 setCurrentBlog(activePost
->currentPostBlogId());
248 void MainWindow::setCurrentBlog(int blog_id
)
250 qCDebug(BLOGILO_LOG
) << blog_id
;
252 blogs
->setCurrentItem(-1);
253 toolbox
->setCurrentBlogId(blog_id
);
254 // actionCollection()->action("publish_post")->setEnabled( false );
257 const int count
= blogs
->items().count();
258 for (int i
= 0; i
< count
; ++i
) {
259 if (blogs
->action(i
)->data().toInt() == blog_id
) {
260 blogs
->setCurrentItem(i
);
261 currentBlogChanged(blogs
->action(i
));
267 void MainWindow::currentBlogChanged(QAction
*act
)
270 if (mCurrentBlogId
== act
->data().toInt()) {
273 mCurrentBlogId
= act
->data().toInt();
274 // __currentBlogId = mCurrentBlogId;
276 // actionCollection()->action("publish_post")->setEnabled( true );
277 activePost
->setCurrentPostBlogId(mCurrentBlogId
);
279 // actionCollection()->action("publish_post")->setEnabled( false );
281 blogs
->setToolTip(DBMan::self()->blogList().value(mCurrentBlogId
)->blogUrl());
285 activePost
->setCurrentPostBlogId(mCurrentBlogId
);
288 toolbox
->setCurrentBlogId(mCurrentBlogId
);
291 void MainWindow::slotCreateNewPost()
293 tabPosts
->setCurrentWidget(createPostEntry(mCurrentBlogId
, BilboPost()));
294 if (mCurrentBlogId
== -1) {
295 if (!blogs
->items().isEmpty()) {
296 blogs
->setCurrentItem(0);
297 currentBlogChanged(blogs
->action(0));
300 if (this->isVisible() == false) {
305 void MainWindow::optionsPreferences()
307 // The preference dialog is derived from prefs_base.ui
309 // compare the names of the widgets in the .ui file
310 // to the names of the variables in the .kcfg file
311 //avoid having 2 dialogs shown
312 if (KConfigDialog::showDialog(QStringLiteral("settings"))) {
315 ConfigureDialog
*dialog
= new ConfigureDialog(this, QStringLiteral("settings"), Settings::self());
316 connect(dialog
, &ConfigureDialog::blogAdded
, this, &MainWindow::slotBlogAdded
);
317 connect(dialog
, &ConfigureDialog::blogEdited
, this, &MainWindow::slotBlogEdited
);
318 connect(dialog
, &ConfigureDialog::blogRemoved
, this, &MainWindow::slotBlogRemoved
);
319 connect(dialog
, &KConfigDialog::settingsChanged
, this, &MainWindow::settingsChanged
);
320 connect(dialog
, &KConfigDialog::settingsChanged
, this, &MainWindow::slotSettingsChanged
);
321 connect(dialog
, &ConfigureDialog::dialogDestroyed
, this, &MainWindow::slotDialogDestroyed
);
322 connect(dialog
, &ConfigureDialog::configurationChanged
, this, &MainWindow::slotSettingsChanged
);
326 void MainWindow::slotSettingsChanged()
331 void MainWindow::slotDialogDestroyed(QObject
*win
)
333 const QSize size
= qobject_cast
<QWidget
*>(win
)->size();
334 const QString name
= win
->objectName();
335 if (name
== QLatin1String("settings")) {
336 Settings::setConfigWindowSize(size
);
340 void MainWindow::addBlog()
342 AddEditBlog
*addEditBlogWindow
= new AddEditBlog(-1, this);
343 addEditBlogWindow
->setWindowModality(Qt::ApplicationModal
);
344 addEditBlogWindow
->setAttribute(Qt::WA_DeleteOnClose
);
345 connect(addEditBlogWindow
, &AddEditBlog::sigBlogAdded
, this, &MainWindow::slotBlogAdded
);
346 addEditBlogWindow
->show();
349 void MainWindow::slotBlogAdded(const BilboBlog
&blog
)
351 QAction
*act
= new QAction(blog
.title(), blogs
);
352 act
->setCheckable(true);
353 act
->setData(blog
.id());
354 blogs
->addAction(act
);
355 blogs
->setCurrentAction(act
);
356 currentBlogChanged(act
);
357 toolbox
->slotReloadCategoryList();
358 toolbox
->slotUpdateEntries(20);
361 void MainWindow::slotBlogEdited(const BilboBlog
&blog
)
363 const int count
= blogs
->actions().count();
364 for (int i
= 0; i
< count
; ++i
) {
365 if (blogs
->action(i
)->data().toInt() == blog
.id()) {
366 blogs
->action(i
)->setText(blog
.title());
372 void MainWindow::slotBlogRemoved(int blog_id
)
374 const int count
= blogs
->actions().count();
375 for (int i
= 0; i
< count
; ++i
) {
376 if (blogs
->action(i
)->data().toInt() == blog_id
) {
377 if (blogs
->currentItem() == i
) {
378 blogs
->setCurrentItem(i
- 1);
379 currentBlogChanged(blogs
->action(i
- 1));
381 blogs
->removeAction(blogs
->action(i
));
382 if (blogs
->currentItem() == -1) {
383 toolbox
->clearFields();
390 void MainWindow::setupSystemTray()
392 if (Settings::enableSysTrayIcon()) {
394 systemTray
= new KStatusNotifierItem(this);
395 systemTray
->setIconByName(QStringLiteral("blogilo"));
396 systemTray
->setToolTip(QStringLiteral("blogilo"), i18n("Blogilo"), i18n("A KDE Blogging Client"));
397 systemTray
->contextMenu()->addAction(actionCollection()->action(QStringLiteral("new_post")));
398 systemTray
->setCategory(KStatusNotifierItem::ApplicationStatus
);
399 systemTray
->setStatus(KStatusNotifierItem::Active
);
401 } else if (systemTray
) {
402 systemTray
->deleteLater();
403 systemTray
= Q_NULLPTR
;
407 void MainWindow::slotPostTitleChanged(const QString
&title
)
409 tabPosts
->setTabText(tabPosts
->currentIndex(), title
);
412 void MainWindow::slotToggleToolboxVisible(bool isVisible
)
414 toolboxDock
->setVisible(isVisible
);
417 void MainWindow::slotToolboxVisibilityChanged(bool)
419 actionCollection()->action(QStringLiteral("toggle_toolbox"))->setChecked(toolboxDock
->isVisibleTo(this));
422 void MainWindow::slotActivePostChanged(int index
)
424 qCDebug(BLOGILO_LOG
) << "new post index: " << index
<< "\tPrev Index: " << previousActivePostIndex
;
426 activePost
= qobject_cast
<PostEntry
*>(tabPosts
->widget(index
));
427 PostEntry
*prevActivePost
= qobject_cast
<PostEntry
*>(tabPosts
->widget(previousActivePostIndex
));
428 int activePostBlogId
= -1;
429 int prevPostBlogId
= -1;
431 if ((prevActivePost
!= Q_NULLPTR
) && (index
!= previousActivePostIndex
)) {
432 prevPostBlogId
= prevActivePost
->currentPostBlogId();
433 toolbox
->getFieldsValue(prevActivePost
->currentPost());
434 prevActivePost
->setCurrentPostBlogId(mCurrentBlogId
);
438 activePostBlogId
= activePost
->currentPostBlogId();
439 if (activePostBlogId
!= -1 && activePostBlogId
!= prevPostBlogId
) {
440 setCurrentBlog(activePostBlogId
);
442 toolbox
->setFieldsValue(activePost
->currentPost());
444 qCCritical(BLOGILO_LOG
) << "ActivePost is NULL! tabPosts Current index is: " << tabPosts
->currentIndex();
446 previousActivePostIndex
= index
;
449 void MainWindow::slotPublishPost()
451 if (mCurrentBlogId
== -1) {
452 KMessageBox::sorry(this, i18n("You have to select a blog to publish this post to."));
455 if (!activePost
|| tabPosts
->currentIndex() == -1) {
456 KMessageBox::sorry(this, i18n("There is no open post to submit."));
459 toolbox
->getFieldsValue(activePost
->currentPost());
460 activePost
->submitPost(mCurrentBlogId
, *activePost
->currentPost());
463 void MainWindow::slotRemoveAllExclude(int pos
)
465 for (int i
= tabPosts
->count() - 1; i
>= 0; --i
) {
469 PostEntry
*widget
= qobject_cast
<PostEntry
*>(tabPosts
->widget(i
));
477 DBMan::self()->removeTempEntry(*widget
->currentPost());
478 tabPosts
->removeTab(tabPosts
->indexOf(widget
));
481 if (tabPosts
->count() < 1) {
482 activePost
= Q_NULLPTR
;
483 toolbox
->resetFields();
484 // actionCollection()->action("publish_post")->setEnabled( false );
488 void MainWindow::slotRemovePostEntry(int pos
)
492 PostEntry
*widget
= qobject_cast
<PostEntry
*>(tabPosts
->widget(pos
));
501 DBMan::self()->removeTempEntry(*widget
->currentPost());
502 tabPosts
->removeTab(tabPosts
->indexOf(widget
));
505 if (tabPosts
->count() < 1) {
506 activePost
= Q_NULLPTR
;
507 toolbox
->resetFields();
508 // actionCollection()->action("publish_post")->setEnabled( false );
512 void MainWindow::slotNewPostOpened(BilboPost
&newPost
, int blog_id
)
515 QWidget
*w
= createPostEntry(blog_id
, newPost
);
516 tabPosts
->setCurrentWidget(w
);
519 void MainWindow::slotSavePostLocally()
522 if (activePost
&& (tabPosts
->count() > 0)) {
523 toolbox
->getFieldsValue(activePost
->currentPost());
524 activePost
->saveLocally();
525 toolbox
->reloadLocalPosts();
529 void MainWindow::slotError(const QString
&errorMessage
)
531 qCDebug(BLOGILO_LOG
) << "Error message: " << errorMessage
;
532 KMessageBox::detailedError(this, i18n("An error occurred in the last transaction."), errorMessage
);
533 statusBar()->clearMessage();
537 void MainWindow::writeConfigs()
540 if (toolboxDock
->isVisible()) {
541 Settings::setShowToolboxOnStart(true);
543 Settings::setShowToolboxOnStart(false);
547 void MainWindow::keyPressEvent(QKeyEvent
*event
)
549 if (event
->modifiers() == Qt::CTRL
) {
550 switch (event
->key()) {
552 toolbox
->setCurrentPage(0);
555 toolbox
->setCurrentPage(1);
558 toolbox
->setCurrentPage(2);
561 toolbox
->setCurrentPage(3);
564 toolbox
->setCurrentPage(4);
567 slotRemovePostEntry(tabPosts
->currentIndex());
570 KXmlGuiWindow::keyPressEvent(event
);
576 void MainWindow::postManipulationDone(bool isError
, const QString
&customMessage
)
580 KMessageBox::detailedError(this, i18n("Submitting post failed"), customMessage
);
582 PostEntry
*entry
= qobject_cast
<PostEntry
*>(sender());
584 if (KMessageBox::questionYesNo(this, i18n("%1\nDo you want to keep the post open?", customMessage
),
585 QString(), KStandardGuiItem::yes(), KStandardGuiItem::no(), QStringLiteral("KeepPostOpen")) == KMessageBox::No
) {
586 slotRemovePostEntry(tabPosts
->indexOf(entry
));
588 toolbox
->setFieldsValue(entry
->currentPost());
591 toolbox
->slotLoadEntriesFromDB(mCurrentBlogId
);
594 toolbox
->unsetCursor();
597 void MainWindow::slotBusy(bool isBusy
)
599 qCDebug(BLOGILO_LOG
) << "isBusy=" << isBusy
<< "\tbusyNumber=" << busyNumber
;
601 this->setCursor(Qt::BusyCursor
);
602 toolbox
->setCursor(Qt::BusyCursor
);
605 progress
= new QProgressBar(statusBar());
606 progress
->setMinimum(0);
607 progress
->setMaximum(0);
608 progress
->setFixedSize(250, 17);
609 statusBar()->addPermanentWidget(progress
);
613 if (busyNumber
< 1) {
615 toolbox
->unsetCursor();
617 statusBar()->removeWidget(progress
);
618 progress
->deleteLater();
619 progress
= Q_NULLPTR
;
626 QWidget
*MainWindow::createPostEntry(int blog_id
, const BilboPost
&post
)
629 PostEntry
*temp
= new PostEntry(this);
630 temp
->setAttribute(Qt::WA_DeleteOnClose
);
631 temp
->setCurrentPost(post
);
632 temp
->setCurrentPostBlogId(blog_id
);
634 connect(temp
, &PostEntry::postTitleChanged
, this, &MainWindow::slotPostTitleChanged
);
635 connect(temp
, &PostEntry::postPublishingDone
, this, &MainWindow::postManipulationDone
);
636 connect(this, &MainWindow::settingsChanged
, temp
, &PostEntry::settingsChanged
);
637 connect(temp
, &PostEntry::showStatusMessage
, this, &MainWindow::slotShowStatusMessage
);
639 connect(temp
, &PostEntry::sigBusy
, this, &MainWindow::slotBusy
);
641 tabPosts
->addTab(temp
, post
.title());
645 void MainWindow::slotShowStatusMessage(const QString
&message
, bool isPermanent
)
647 statusBar()->showMessage(message
, (isPermanent
? 0 : TIMEOUT
));
650 void MainWindow::uploadMediaObject()
652 UploadMediaDialog
*uploadDlg
= new UploadMediaDialog(this);
653 connect(uploadDlg
, &UploadMediaDialog::sigBusy
, this, &MainWindow::slotBusy
);
654 if (mCurrentBlogId
== -1) {
655 uploadDlg
->init(Q_NULLPTR
);
657 uploadDlg
->init(DBMan::self()->blog(mCurrentBlogId
));
661 void MainWindow::slotOpenCurrentBlogInBrowser()
663 if (mCurrentBlogId
> -1) {
664 QUrl
url(DBMan::self()->blog(mCurrentBlogId
)->blogUrl());
666 QDesktopServices::openUrl(url
);
668 KMessageBox::sorry(this, i18n("Cannot find current blog URL."));
672 ///else show a message to the user saying that a blog should be selected before.