kde-playground/kdepim/kjots/kjotswidget.cpp
2015-04-14 21:49:29 +00:00

1851 lines
60 KiB
C++

/*
This file is part of KJots.
Copyright (C) 1997 Christoph Neerfeld <Christoph.Neerfeld@home.ivm.de>
Copyright (C) 2002, 2003 Aaron J. Seigo <aseigo@kde.org>
Copyright (C) 2003 Stanislav Kljuhhin <crz@hot.ee>
Copyright (C) 2005-2006 Jaison Lee <lee.jaison@gmail.com>
Copyright (C) 2007-2009 Stephen Kelly <steveire@gmail.com>
This library 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 of the License, or (at your
option) any later version.
This library 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 Library General Public
License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#include "kjotswidget.h"
// Qt
#include <QHBoxLayout>
#include <QInputDialog>
#include <QSplitter>
#include <QStackedWidget>
#include <QTextCursor>
#include <QTextDocument>
#include <QTextDocumentFragment>
#include <QTimer>
#include <QPrintDialog>
#include <QPainter>
#include <QPointer>
#include <QPrinter>
#include <QAbstractTextDocumentLayout>
#include <QDBusConnection>
// Akonadi
#include <akonadi/control.h>
#include <akonadi/collectiondeletejob.h>
#include <akonadi/collectioncreatejob.h>
#include <akonadi/changerecorder.h>
#include <akonadi/entitydisplayattribute.h>
#include <akonadi/entitytreeview.h>
#include <akonadi/etmviewstatesaver.h>
#include <akonadi/item.h>
#include <Akonadi/ItemCreateJob>
#include <Akonadi/ItemDeleteJob>
#include <akonadi/itemfetchjob.h>
#include <akonadi/itemfetchscope.h>
#include "akonadi/entityorderproxymodel.h"
#include "akonadi_next/note.h"
#include "akonadi_next/notecreatorandselector.h"
// Grantlee
#include <grantlee/template.h>
#include <grantlee/engine.h>
#include <grantlee/context.h>
// KDE
#include <KAction>
#include <KActionCollection>
#include <KBookmarkMenu>
#include <KFileDialog>
#include <KFind>
#include <KFindDialog>
#include <KLocalizedString>
#include <KMessageBox>
#include <KReplaceDialog>
#include <kselectionproxymodel.h>
#include <KStandardDirs>
#include <KGlobalSettings>
#include <KXMLGUIClient>
#include <KProcess>
#include <KPrintPreview>
// KMime
#include <KMime/KMimeMessage>
// KJots
#include "kjotsbookmarks.h"
#include "kjotssortproxymodel.h"
#include "kjotsmodel.h"
#include "kjotsedit.h"
#include "kjotstreeview.h"
#include "kjotsconfigdlg.h"
#include "kjotsreplacenextdialog.h"
#include "KJotsSettings.h"
#include "kjotslockjob.h"
#include <kdebug.h>
#include <memory>
#include "noteshared/attributes/notelockattribute.h"
#include "localresourcecreator.h"
#include <krandom.h>
#include "kjotsbrowser.h"
#ifndef KDE_USE_FINAL
Q_DECLARE_METATYPE(QTextDocument*)
Q_DECLARE_METATYPE(QTextCursor)
#endif
using namespace Akonadi;
using namespace Grantlee;
KJotsWidget::KJotsWidget( QWidget * parent, KXMLGUIClient *xmlGuiClient, Qt::WindowFlags f )
: QWidget( parent, f ), m_xmlGuiClient( xmlGuiClient )
{
Akonadi::Control::widgetNeedsAkonadi( this );
KConfigGroup migrationCfg( KGlobal::config(), "General" );
const bool autoCreate = migrationCfg.readEntry( "AutoCreateResourceOnStart", true );
migrationCfg.writeEntry("AutoCreateResourceOnStart", autoCreate);
migrationCfg.sync();
if (autoCreate) {
LocalResourceCreator *creator = new LocalResourceCreator( this );
creator->createIfMissing();
}
m_splitter = new QSplitter( this );
m_splitter->setStretchFactor(1, 1);
m_splitter->setOpaqueResize( KGlobalSettings::opaqueResize() );
QHBoxLayout *layout = new QHBoxLayout( this );
layout->setMargin( 0 );
KStandardDirs KStd;
m_templateEngine = new Engine(this);
m_templateEngine->setPluginPaths( KStd.findDirs( "lib", QString() ) );
m_loader = FileSystemTemplateLoader::Ptr( new FileSystemTemplateLoader() );
m_loader->setTemplateDirs( KStd.findDirs( "data", QLatin1String( "kjots/themes" ) ) );
m_loader->setTheme( QLatin1String( "default" ) );
m_templateEngine->addTemplateLoader( m_loader );
treeview = new KJotsTreeView( xmlGuiClient, m_splitter );
ItemFetchScope scope;
scope.fetchFullPayload( true ); // Need to have full item when adding it to the internal data structure
scope.fetchAttribute< EntityDisplayAttribute >();
scope.fetchAttribute< NoteShared::NoteLockAttribute >();
ChangeRecorder *monitor = new ChangeRecorder( this );
monitor->fetchCollection( true );
monitor->setItemFetchScope( scope );
monitor->setCollectionMonitored( Collection::root() );
monitor->setMimeTypeMonitored( Akonotes::Note::mimeType() );
m_kjotsModel = new KJotsModel( monitor, this );
m_sortProxyModel = new KJotsSortProxyModel( this );
m_sortProxyModel->setSourceModel( m_kjotsModel );
m_orderProxy = new EntityOrderProxyModel( this );
m_orderProxy->setSourceModel( m_sortProxyModel );
KConfigGroup cfg( KGlobal::config(), "KJotsEntityOrder" );
m_orderProxy->setOrderConfig( cfg );
treeview->setModel( m_orderProxy );
treeview->setSelectionMode( QAbstractItemView::ExtendedSelection );
treeview->setEditTriggers( QAbstractItemView::DoubleClicked );
connect( treeview->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(selectionChanged(QItemSelection,QItemSelection)) );
selProxy = new KSelectionProxyModel( treeview->selectionModel(), this );
selProxy->setSourceModel( treeview->model() );
// TODO: Write a QAbstractItemView subclass to render kjots selection.
connect( selProxy, SIGNAL(dataChanged(QModelIndex,QModelIndex)), SLOT(renderSelection()) );
connect( selProxy, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(renderSelection()) );
connect( selProxy, SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(renderSelection()) );
stackedWidget = new QStackedWidget( m_splitter );
KActionCollection *actionCollection = xmlGuiClient->actionCollection();
editor = new KJotsEdit( treeview->selectionModel(), stackedWidget );
editor->createActions( actionCollection );
stackedWidget->addWidget( editor );
layout->addWidget( m_splitter );
browser = new KJotsBrowser( treeview->selectionModel(), stackedWidget );
stackedWidget->addWidget( browser );
stackedWidget->setCurrentWidget( browser );
KAction *action;
action = actionCollection->addAction( QLatin1String("go_next_book") );
action->setText( i18n( "Next Book" ) );
action->setIcon( KIcon( QLatin1String("go-down") ) );
action->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_D ) );
connect( action, SIGNAL(triggered()), SLOT(nextBook()) );
connect( this, SIGNAL(canGoNextBookChanged(bool)), action, SLOT(setEnabled(bool)) );
action = actionCollection->addAction( QLatin1String("go_prev_book") );
action->setText( i18n( "Previous Book" ) );
action->setIcon( KIcon( QLatin1String("go-up") ) );
action->setShortcut( QKeySequence( Qt::CTRL + Qt::SHIFT + Qt::Key_D ) );
connect( action, SIGNAL(triggered()), SLOT(prevBook()) );
connect( this, SIGNAL(canGoPreviousBookChanged(bool)), action, SLOT(setEnabled(bool)) );
action = actionCollection->addAction( QLatin1String("go_next_page") );
action->setText( i18n( "Next Page" ) );
action->setIcon( KIcon( QLatin1String("go-next") ) );
action->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_PageDown ) );
connect( action, SIGNAL(triggered()), SLOT(nextPage()));
connect( this, SIGNAL(canGoNextPageChanged(bool)), action, SLOT(setEnabled(bool)) );
action = actionCollection->addAction( QLatin1String("go_prev_page") );
action->setText( i18n( "Previous Page" ) );
action->setIcon( KIcon( QLatin1String("go-previous") ) );
action->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_PageUp ) );
connect( action, SIGNAL(triggered()), SLOT(prevPage()) );
connect( this, SIGNAL(canGoPreviousPageChanged(bool)), action, SLOT(setEnabled(bool)) );
action = actionCollection->addAction( QLatin1String("new_page") );
action->setText( i18n( "&New Page" ) );
action->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_N ) );
action->setIcon( KIcon( QLatin1String("document-new") ) );
connect( action, SIGNAL(triggered()), SLOT(newPage()) );
action = actionCollection->addAction(QLatin1String("new_book"));
action->setText( i18n( "New &Book..." ) );
action->setShortcut( QKeySequence( Qt::CTRL + Qt::SHIFT + Qt::Key_N ) );
action->setIcon( KIcon( QLatin1String("address-book-new") ) );
connect( action, SIGNAL(triggered()), SLOT(newBook()) );
action = actionCollection->addAction( QLatin1String("del_page") );
action->setText( i18n( "&Delete Page" ) );
action->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_Delete ) );
action->setIcon( KIcon( QLatin1String("edit-delete-page") ) );
connect( action, SIGNAL(triggered()), SLOT(deletePage()) );
action = actionCollection->addAction( QLatin1String("del_folder") );
action->setText( i18n( "Delete Boo&k" ) );
action->setShortcut( QKeySequence( Qt::CTRL + Qt::SHIFT + Qt::Key_Delete ) );
action->setIcon( KIcon( QLatin1String("edit-delete") ) );
connect( action, SIGNAL(triggered()), SLOT(deleteBook()) );
action = actionCollection->addAction( QLatin1String("del_mult") );
action->setText( i18n( "Delete Selected" ) );
action->setIcon( KIcon( QLatin1String("edit-delete") ) );
connect( action, SIGNAL(triggered()), SLOT(deleteMultiple()) );
action = actionCollection->addAction( QLatin1String("manual_save") );
action->setText( i18n( "Manual Save" ) );
action->setIcon( KIcon( QLatin1String("document-save") ) );
action->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_S ) );
action = actionCollection->addAction( QLatin1String("auto_bullet") );
action->setText( i18n( "Auto Bullets" ) );
action->setIcon( KIcon( QLatin1String("format-list-unordered") ) );
action->setCheckable( true );
action = actionCollection->addAction( QLatin1String("auto_decimal") );
action->setText( i18n( "Auto Decimal List" ) );
action->setIcon( KIcon( QLatin1String("format-list-ordered") ) );
action->setCheckable( true );
action = actionCollection->addAction( QLatin1String("manage_link") );
action->setText( i18n( "Link" ) );
action->setIcon( KIcon( QLatin1String("insert-link") ) );
action = actionCollection->addAction( QLatin1String("insert_checkmark") );
action->setText( i18n( "Insert Checkmark" ) );
action->setIcon( KIcon( QLatin1String("checkmark") ) );
action->setEnabled( false );
action = actionCollection->addAction( QLatin1String("rename_entry") );
action->setText( i18n( "Rename..." ) );
action->setIcon( KIcon( QLatin1String("edit-rename") ) );
action->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_M ) );
action = actionCollection->addAction( QLatin1String("insert_date") );
action->setText( i18n( "Insert Date" ) );
action->setShortcut( QKeySequence( Qt::CTRL + Qt::SHIFT + Qt::Key_I ) );
action->setIcon( KIcon( QLatin1String("view-calendar-time-spent") ) );
action = actionCollection->addAction( QLatin1String("change_color") );
action->setIcon( KIcon( QLatin1String("format-fill-color") ) );
action->setText( i18n( "Change Color..." ) );
action = actionCollection->addAction( QLatin1String("copy_link_address") );
action->setText( i18n( "Copy Link Address" ) );
action = actionCollection->addAction( QLatin1String("lock") );
action->setText(i18n( "Lock Selected" ) );
action->setIcon( KIcon( QLatin1String("emblem-locked") ) );
connect( action, SIGNAL(triggered()), SLOT(actionLock()) );
action = actionCollection->addAction( QLatin1String("unlock") );
action->setText( i18n( "Unlock Selected" ) );
action->setIcon( KIcon( QLatin1String("emblem-unlocked") ) );
connect( action, SIGNAL(triggered()), SLOT(actionUnlock()) );
action = actionCollection->addAction( QLatin1String("sort_children_alpha") );
action->setText( i18n( "Sort children alphabetically" ) );
connect( action, SIGNAL(triggered()), SLOT(actionSortChildrenAlpha()) );
action = actionCollection->addAction( QLatin1String("sort_children_by_date") );
action->setText( i18n( "Sort children by creation date" ) );
connect( action, SIGNAL(triggered()), SLOT(actionSortChildrenByDate()) );
action = KStandardAction::cut( editor, SLOT(cut()), actionCollection );
connect( editor, SIGNAL(copyAvailable(bool)), action, SLOT(setEnabled(bool)) );
action->setEnabled( false );
action = KStandardAction::copy( this, SLOT(copy()), actionCollection );
connect( editor, SIGNAL(copyAvailable(bool)), action, SLOT(setEnabled(bool)) );
connect( browser, SIGNAL(copyAvailable(bool)), action, SLOT(setEnabled(bool)) );
action->setEnabled( false );
KStandardAction::pasteText( editor, SLOT(paste()), actionCollection );
KStandardAction::undo( editor, SLOT(undo()), actionCollection );
KStandardAction::redo( editor, SLOT(redo()), actionCollection );
KStandardAction::selectAll( editor, SLOT(selectAll()), actionCollection );
action = actionCollection->addAction( QLatin1String("copyIntoTitle") );
action->setText( i18n( "Copy &into Page Title" ) );
action->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_T ) );
action->setIcon( KIcon( QLatin1String("edit-copy") ) );
connect( action, SIGNAL(triggered()), SLOT(copySelectionToTitle()) );
connect( editor, SIGNAL(copyAvailable(bool)), action, SLOT(setEnabled(bool)) );
action->setEnabled( false );
action = actionCollection->addAction( QLatin1String("paste_plain_text") );
action->setText( i18nc( "@action Paste the text in the clipboard without rich text formatting.", "Paste Plain Text" ) );
connect( action, SIGNAL(triggered()), editor, SLOT(pastePlainText()) );
KStandardAction::preferences( this, SLOT(configure()), actionCollection );
bookmarkMenu = actionCollection->add<KActionMenu>( QLatin1String("bookmarks") );
bookmarkMenu->setText( i18n( "&Bookmarks" ) );
KJotsBookmarks* bookmarks = new KJotsBookmarks( treeview );
/*KBookmarkMenu *bmm =*/ new KBookmarkMenu(
KBookmarkManager::managerForFile( KStandardDirs::locateLocal( "data",QLatin1String("kjots/bookmarks.xml") ), QLatin1String("kjots") ),
bookmarks, bookmarkMenu->menu(), actionCollection );
// "Add bookmark" and "make text bold" actions have conflicting shortcuts (ctrl + b)
// Make add_bookmark use ctrl+shift+b to resolve that.
KAction *bm_action = qobject_cast<KAction *>(actionCollection->action(QLatin1String("add_bookmark")));
Q_ASSERT(bm_action);
bm_action->setShortcut( Qt::CTRL + Qt::SHIFT + Qt::Key_B );
KStandardAction::find( this, SLOT(onShowSearch()), actionCollection );
action = KStandardAction::findNext( this, SLOT(onRepeatSearch()), actionCollection );
action->setEnabled(false);
KStandardAction::replace( this, SLOT(onShowReplace()), actionCollection );
action = actionCollection->addAction( QLatin1String("save_to") );
action->setText( i18n( "Rename..." ) );
action->setIcon( KIcon( QLatin1String("edit-rename") ) );
action->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_M ) );
KActionMenu *exportMenu = actionCollection->add<KActionMenu>( QLatin1String("save_to") );
exportMenu->setText( i18n( "Export" ) );
exportMenu->setIcon( KIcon( QLatin1String("document-export") ) );
action = actionCollection->addAction( QLatin1String("save_to_ascii") );
action->setText( i18n( "To Text File..." ) );
action->setIcon( KIcon( QLatin1String("text-plain") ) );
connect( action, SIGNAL(triggered()), SLOT(exportSelectionToPlainText()) );
exportMenu->menu()->addAction( action );
action = actionCollection->addAction( QLatin1String("save_to_html") );
action->setText( i18n( "To HTML File..." ) );
action->setIcon( KIcon( QLatin1String("text-html") ) );
connect( action, SIGNAL(triggered()), SLOT(exportSelectionToHtml()) );
exportMenu->menu()->addAction( action );
action = actionCollection->addAction( QLatin1String("save_to_book") );
action->setText( i18n( "To Book File..." ) );
action->setIcon( KIcon( QLatin1String("x-office-address-book") ) );
connect( action, SIGNAL(triggered()), SLOT(exportSelectionToXml()) );
exportMenu->menu()->addAction( action );
KStandardAction::print(this, SLOT(printSelection()), actionCollection);
KStandardAction::printPreview(this, SLOT(printPreviewSelection()), actionCollection);
if ( !KJotsSettings::splitterSizes().isEmpty() )
{
m_splitter->setSizes( KJotsSettings::splitterSizes() );
}
QTimer::singleShot( 0, this, SLOT(delayedInitialization()) );
connect( treeview->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(updateMenu()) );
connect( treeview->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(updateCaption()) );
connect( m_kjotsModel, SIGNAL(modelAboutToBeReset()), SLOT(saveState()));
connect( m_kjotsModel, SIGNAL(modelReset()), SLOT(restoreState()));
restoreState();
QDBusConnection::sessionBus().registerObject( QLatin1String("/KJotsWidget"), this, QDBusConnection::ExportScriptableContents );
}
KJotsWidget::~KJotsWidget()
{
saveState();
}
void KJotsWidget::restoreState()
{
ETMViewStateSaver *saver = new ETMViewStateSaver;
saver->setView( treeview );
KConfigGroup cfg( KGlobal::config(), "TreeState" );
saver->restoreState( cfg );
}
void KJotsWidget::saveState()
{
ETMViewStateSaver saver;
saver.setView( treeview );
KConfigGroup cfg( KGlobal::config(), "TreeState" );
saver.saveState( cfg );
cfg.sync();
}
void KJotsWidget::delayedInitialization()
{
migrateNoteData( QLatin1String("kjotsmigrator") );
// Disable nigration of data from KNotes as that app still exists in 4.5.
// migrateNoteData( "kres-migrator", "notes" );
//TODO: Save previous searches in settings file?
searchDialog = new KFindDialog ( this, 0, QStringList(), false );
QGridLayout *layout = new QGridLayout(searchDialog->findExtension());
layout->setMargin(0);
searchAllPages = new QCheckBox(i18n("Search all pages"), searchDialog->findExtension());
layout->addWidget(searchAllPages, 0, 0);
connect(searchDialog, SIGNAL(okClicked()), this, SLOT(onStartSearch()) );
connect(searchDialog, SIGNAL(cancelClicked()), this, SLOT(onEndSearch()) );
connect(treeview->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(onUpdateSearch()) );
connect(searchDialog, SIGNAL(optionsChanged()), SLOT(onUpdateSearch()) );
connect(searchAllPages, SIGNAL(stateChanged(int)), SLOT(onUpdateSearch()) );
replaceDialog = new KReplaceDialog ( this, 0, searchHistory, replaceHistory, false );
QGridLayout *layout2 = new QGridLayout(replaceDialog->findExtension());
layout2->setMargin(0);
replaceAllPages = new QCheckBox(i18n("Search all pages"), replaceDialog->findExtension());
layout2->addWidget(replaceAllPages, 0, 0);
connect(replaceDialog, SIGNAL(okClicked()), this, SLOT(onStartReplace()) );
connect(replaceDialog, SIGNAL(cancelClicked()), this, SLOT(onEndReplace()) );
connect(replaceDialog, SIGNAL(optionsChanged()), SLOT(onUpdateReplace()) );
connect(replaceAllPages, SIGNAL(stateChanged(int)), SLOT(onUpdateReplace()) );
// Actions are enabled or disabled based on whether the selection is a single page, a single book
// multiple selections, or no selection.
//
// The entryActions are enabled for all single pages and single books, and the multiselectionActions
// are enabled when the user has made multiple selections.
//
// Some actions are in neither (eg, new book) and are available even when there is no selection.
//
// Some actions are in both, so that they are available for valid selections, but not available
// for invalid selections (eg, print/find are disabled when there is no selection)
KActionCollection *actionCollection = m_xmlGuiClient->actionCollection();
// Actions for a single item selection.
entryActions.insert( actionCollection->action(QLatin1String(KStandardAction::name(KStandardAction::Find))) );
entryActions.insert( actionCollection->action(QLatin1String(KStandardAction::name(KStandardAction::Print))) );
entryActions.insert( actionCollection->action(QLatin1String("rename_entry")) );
entryActions.insert( actionCollection->action(QLatin1String("change_color")) );
entryActions.insert( actionCollection->action(QLatin1String("save_to")) );
entryActions.insert( actionCollection->action(QLatin1String("copy_link_address")) );
// Actions that are used only when a page is selected.
pageActions.insert( actionCollection->action(QLatin1String(KStandardAction::name(KStandardAction::Cut))) );
pageActions.insert( actionCollection->action(QLatin1String(KStandardAction::name(KStandardAction::Paste))) );
pageActions.insert( actionCollection->action(QLatin1String(KStandardAction::name(KStandardAction::Replace))) );
pageActions.insert( actionCollection->action(QLatin1String("del_page")) );
pageActions.insert( actionCollection->action(QLatin1String("insert_date")) );
pageActions.insert( actionCollection->action(QLatin1String("auto_bullet")) );
pageActions.insert( actionCollection->action(QLatin1String("auto_decimal")) );
pageActions.insert( actionCollection->action(QLatin1String("manage_link")) );
pageActions.insert( actionCollection->action(QLatin1String("insert_checkmark")) );
// Actions that are used only when a book is selected.
bookActions.insert( actionCollection->action(QLatin1String("save_to_book")) );
bookActions.insert( actionCollection->action(QLatin1String("del_folder")) );
bookActions.insert( actionCollection->action(QLatin1String("sort_children_alpha")) );
bookActions.insert( actionCollection->action(QLatin1String("sort_children_by_date")) );
// Actions that are used when multiple items are selected.
multiselectionActions.insert( actionCollection->action(QLatin1String(KStandardAction::name(KStandardAction::Find))) );
multiselectionActions.insert( actionCollection->action(QLatin1String(KStandardAction::name(KStandardAction::Print))));
multiselectionActions.insert( actionCollection->action(QLatin1String("del_mult")) );
multiselectionActions.insert( actionCollection->action(QLatin1String("save_to")) );
multiselectionActions.insert( actionCollection->action(QLatin1String("change_color")) );
m_autosaveTimer = new QTimer(this);
updateConfiguration();
connect(m_autosaveTimer, SIGNAL(timeout()), editor, SLOT(savePage()));
connect(treeview->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), m_autosaveTimer, SLOT(start()) );
treeview->delayedInitialization();
editor->delayedInitialization( m_xmlGuiClient->actionCollection() );
browser->delayedInitialization();
connect( treeview->itemDelegate(), SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)),
SLOT(bookshelfEditItemFinished(QWidget*,QAbstractItemDelegate::EndEditHint)) );
connect( editor, SIGNAL(currentCharFormatChanged(QTextCharFormat)),
SLOT(currentCharFormatChanged(QTextCharFormat)) );
updateMenu();
}
void KJotsWidget::bookshelfEditItemFinished( QWidget *, QAbstractItemDelegate::EndEditHint )
{
// Make sure the editor gets focus again after naming a new book/page.
activeEditor()->setFocus();
}
void KJotsWidget::currentCharFormatChanged(const QTextCharFormat & fmt)
{
QString selectedAnchor = fmt.anchorHref();
if (selectedAnchor != activeAnchor)
{
activeAnchor = selectedAnchor;
if (!selectedAnchor.isEmpty())
{
QTextCursor c(editor->textCursor());
editor->selectLinkText(&c);
QString selectedText = c.selectedText();
if (!selectedText.isEmpty())
{
emit activeAnchorChanged(selectedAnchor, selectedText);
}
} else {
emit activeAnchorChanged(QString(), QString());
}
}
}
void KJotsWidget::migrateNoteData( const QString &migrator, const QString &type )
{
// Akonadi migration
KConfig config( migrator + QLatin1String("rc") );
KConfigGroup migrationCfg( &config, "Migration" );
const bool enabled = migrationCfg.readEntry( "Enabled", true );
const bool completed = migrationCfg.readEntry( "Completed", false );
const int currentVersion = migrationCfg.readEntry( "Version", 0 );
const int targetVersion = migrationCfg.readEntry( "TargetVersion", 1 );
if ( enabled && !completed && currentVersion < targetVersion ) {
kDebug() << "Performing Akonadi migration. Good luck!";
KProcess proc;
QStringList args = QStringList() << QLatin1String("--interactive-on-change");
if ( !type.isEmpty() )
args << QLatin1String("--type") << type;
const QString path = KStandardDirs::findExe( migrator );
proc.setProgram( path, args );
proc.start();
bool result = proc.waitForStarted();
if ( result ) {
result = proc.waitForFinished();
}
if ( result && proc.exitCode() == 0 ) {
kDebug() << "Akonadi migration has been successful";
} else {
// exit code 1 means it is already running, so we are probably called by a migrator instance
kError() << "Akonadi migration failed!";
kError() << "command was: " << proc.program();
kError() << "exit code: " << proc.exitCode();
kError() << "stdout: " << proc.readAllStandardOutput();
kError() << "stderr: " << proc.readAllStandardError();
}
migrationCfg.writeEntry( "Version", targetVersion );
migrationCfg.writeEntry( "Completed", true );
migrationCfg.sync();
}
}
inline QTextEdit* KJotsWidget::activeEditor() {
if ( browser->isVisible() ) {
return browser;
} else {
return editor;
}
}
void KJotsWidget::updateMenu()
{
QModelIndexList selection = treeview->selectionModel()->selectedRows();
int selectionSize = selection.size();
if ( !selectionSize ) {
// no (meaningful?) selection
foreach ( QAction* action, multiselectionActions )
action->setEnabled(false);
foreach ( QAction* action, entryActions )
action->setEnabled(false);
foreach ( QAction* action, bookActions )
action->setEnabled(false);
foreach ( QAction* action, pageActions )
action->setEnabled(false);
editor->setActionsEnabled( false );
} else if ( selectionSize > 1 ) {
foreach ( QAction* action, entryActions )
action->setEnabled(false);
foreach ( QAction* action, bookActions )
action->setEnabled(false);
foreach ( QAction* action, pageActions )
action->setEnabled(false);
foreach ( QAction* action, multiselectionActions )
action->setEnabled(true);
editor->setActionsEnabled( false );
} else {
foreach ( QAction* action, multiselectionActions )
action->setEnabled(false);
foreach ( QAction* action, entryActions )
action->setEnabled(true);
QModelIndex idx = selection.at( 0 );
Collection col = idx.data( KJotsModel::CollectionRole ).value<Collection>();
if ( col.isValid() ) {
foreach ( QAction* action, pageActions )
action->setEnabled(false);
const bool colIsRootCollection = ( col.parentCollection() == Collection::root() );
foreach ( QAction* action, bookActions ) {
if (action->objectName() == QLatin1String("del_folder") && colIsRootCollection ) {
action->setEnabled( false );
} else {
action->setEnabled( true );
}
}
editor->setActionsEnabled( false );
} else {
foreach ( QAction* action, pageActions ) {
if (action->objectName() == QLatin1String(name( KStandardAction::Cut )) ) {
action->setEnabled( activeEditor()->textCursor().hasSelection() );
} else {
action->setEnabled( true );
}
}
foreach ( QAction* action, bookActions )
action->setEnabled( false );
editor->setActionsEnabled( true );
}
}
}
void KJotsWidget::copy() {
activeEditor()->copy();
}
void KJotsWidget::configure()
{
// create a new preferences dialog...
KJotsConfigDlg *dialog = new KJotsConfigDlg( i18n( "Settings" ), this );
connect( dialog, SIGNAL(configCommitted()), SLOT(updateConfiguration()) );
dialog->show();
}
void KJotsWidget::updateConfiguration()
{
if (KJotsSettings::autoSave())
{
m_autosaveTimer->setInterval(KJotsSettings::autoSaveInterval()*1000*60);
m_autosaveTimer->start();
} else
m_autosaveTimer->stop();
}
void KJotsWidget::copySelectionToTitle()
{
QString newTitle( editor->textCursor().selectedText() );
if ( !newTitle.isEmpty() ) {
QModelIndexList rows = treeview->selectionModel()->selectedRows();
if ( rows.size() != 1 )
return;
QModelIndex idx = rows.at( 0 );
treeview->model()->setData( idx, newTitle );
}
}
void KJotsWidget::deleteMultiple()
{
QModelIndexList selectedRows = treeview->selectionModel()->selectedRows();
if ( KMessageBox::questionYesNo( this,
i18n( "Do you really want to delete all selected books and pages?" ),
i18n("Delete?"), KStandardGuiItem::del(), KStandardGuiItem::cancel(),
QString(), KMessageBox::Dangerous ) != KMessageBox::Yes )
return;
foreach ( const QModelIndex &index, selectedRows ) {
bool ok;
qlonglong id = index.data( EntityTreeModel::ItemIdRole ).toLongLong(&ok);
Q_ASSERT(ok);
if ( id >= 0 )
{
new ItemDeleteJob( Item( id ), this );
}
else {
id = index.data( EntityTreeModel::CollectionIdRole ).toLongLong(&ok);
Q_ASSERT(ok);
if ( id >= 0 )
new CollectionDeleteJob( Collection( id ), this );
}
}
}
void KJotsWidget::deletePage()
{
QModelIndexList selectedRows = treeview->selectionModel()->selectedRows();
if ( selectedRows.size() != 1 )
return;
const QModelIndex idx = selectedRows.at( 0 );
Item item = idx.data( EntityTreeModel::ItemRole ).value<Item>();
if ( !item.isValid() )
return;
if( item.hasAttribute<NoteShared::NoteLockAttribute>() ) {
KMessageBox::information(topLevelWidget(),
i18n("This page is locked. You can only delete it when you first unlock it."),
i18n("Item is locked"));
return;
}
if ( KMessageBox::warningContinueCancel(topLevelWidget(),
i18nc("remove the page, by title", "<qt>Are you sure you want to delete the page <strong>%1</strong>?</qt>", idx.data().toString()),
i18n("Delete"), KStandardGuiItem::del(), KStandardGuiItem::cancel(), QLatin1String("DeletePageWarning")) == KMessageBox::Cancel) {
return;
}
(void) new Akonadi::ItemDeleteJob( item, this );
}
void KJotsWidget::deleteBook()
{
QModelIndexList selectedRows = treeview->selectionModel()->selectedRows();
if ( selectedRows.size() != 1 )
return;
const QModelIndex idx = selectedRows.at( 0 );
Collection col = idx.data( EntityTreeModel::CollectionRole ).value<Collection>();
if ( !col.isValid() )
return;
if ( col.parentCollection() == Collection::root() )
return;
if( col.hasAttribute<NoteShared::NoteLockAttribute>() ) {
KMessageBox::information(topLevelWidget(),
i18n("This book is locked. You can only delete it when you first unlock it."),
i18n("Item is locked"));
return;
}
if ( KMessageBox::warningContinueCancel(topLevelWidget(),
i18nc("remove the book, by title", "<qt>Are you sure you want to delete the book <strong>%1</strong>?</qt>", idx.data().toString()),
i18n("Delete"), KStandardGuiItem::del(), KStandardGuiItem::cancel(), QLatin1String("DeleteBookWarning")) == KMessageBox::Cancel) {
return;
}
(void) new Akonadi::CollectionDeleteJob( col, this );
}
void KJotsWidget::newBook()
{
QModelIndexList selectedRows = treeview->selectionModel()->selectedRows();
if ( selectedRows.size() != 1 )
return;
Collection col = selectedRows.at( 0 ).data( EntityTreeModel::CollectionRole ).value<Collection>();
if ( !col.isValid() )
return;
Collection newCollection;
newCollection.setParentCollection( col );
QString title = i18nc( "The default name for new books.", "New Book" );
newCollection.setName( KRandom::randomString( 10 ) );
newCollection.setContentMimeTypes( QStringList() << Akonadi::Collection::mimeType() << Akonotes::Note::mimeType() );
Akonadi::EntityDisplayAttribute *eda = new Akonadi::EntityDisplayAttribute();
eda->setIconName( QLatin1String("x-office-address-book") );
eda->setDisplayName( title );
newCollection.addAttribute( eda );
Akonadi::CollectionCreateJob *job = new Akonadi::CollectionCreateJob( newCollection );
connect( job, SIGNAL(result(KJob*)), this, SLOT(newBookResult(KJob*)) );
}
void KJotsWidget::newPage()
{
QModelIndexList selectedRows = treeview->selectionModel()->selectedRows();
if ( selectedRows.size() != 1 )
return;
Item item = selectedRows.at( 0 ).data( EntityTreeModel::ItemRole ).value<Item>();
Collection col;
if ( item.isValid() )
{
col = selectedRows.at( 0 ).data( EntityTreeModel::ParentCollectionRole ).value<Collection>();
} else {
col = selectedRows.at( 0 ).data( EntityTreeModel::CollectionRole ).value<Collection>();
}
if ( !col.isValid() )
return;
doCreateNewPage(col);
}
void KJotsWidget::doCreateNewPage(const Collection &collection)
{
Akonotes::NoteCreatorAndSelector *creatorAndSelector = new Akonotes::NoteCreatorAndSelector(treeview->selectionModel());
creatorAndSelector->createNote(collection);
}
void KJotsWidget::newPageResult( KJob* job )
{
if ( job->error() )
kDebug() << job->errorString();
}
void KJotsWidget::newBookResult( KJob* job )
{
if ( job->error() ) {
kDebug() << job->errorString();
return;
}
Akonadi::CollectionCreateJob *createJob = qobject_cast<Akonadi::CollectionCreateJob*>(job);
if ( !createJob )
return;
const Collection collection = createJob->collection();
if ( !collection.isValid() )
return;
doCreateNewPage(collection);
}
QString KJotsWidget::renderSelectionToHtml()
{
QHash<QString, QVariant> hash;
QList<QVariant> objectList;
const int rows = selProxy->rowCount();
const int column = 0;
for ( int row = 0; row < rows; ++row )
{
QModelIndex idx = selProxy->index( row, column, QModelIndex() );
QObject *obj = idx.data(KJotsModel::GrantleeObjectRole).value<QObject*>();
KJotsEntity *kjotsEntity = qobject_cast<KJotsEntity*>(obj);
kjotsEntity->setIndex(idx);
objectList << QVariant::fromValue(static_cast<QObject *>(kjotsEntity));
}
hash.insert( QLatin1String( "entities" ), objectList);
hash.insert( QLatin1String( "i18n_TABLE_OF_CONTENTS" ),
i18nc("Header for 'Table of contents' section of rendered output", "Table of contents") );
Context c(hash);
Template t = m_templateEngine->loadByName( QLatin1String( "template.html" ) );
QString result = t->render(&c);
// TODO: handle errors.
return result;
}
QString KJotsWidget::renderSelectionToPlainText()
{
QHash<QString, QVariant> hash;
QList<QVariant> objectList;
const int rows = selProxy->rowCount();
const int column = 0;
for ( int row = 0; row < rows; ++row )
{
QModelIndex idx = selProxy->index( row, column, QModelIndex() );
QObject *obj = idx.data(KJotsModel::GrantleeObjectRole).value<QObject*>();
KJotsEntity *kjotsEntity = qobject_cast<KJotsEntity*>(obj);
kjotsEntity->setIndex(idx);
objectList << QVariant::fromValue(static_cast<QObject *>(kjotsEntity));
}
hash.insert( QLatin1String( "entities" ), objectList);
hash.insert( QLatin1String( "i18n_TABLE_OF_CONTENTS" ),
i18nc("Header for 'Table of contents' section of rendered output", "Table of contents") );
Context c(hash);
Template t = m_templateEngine->loadByName( QLatin1String( "template.txt" ) );
QString result = t->render(&c);
// TODO: handle errors.
return result;
}
QString KJotsWidget::renderSelectionToXml()
{
QHash<QString, QVariant> hash;
QList<QVariant> objectList;
const int rows = selProxy->rowCount();
const int column = 0;
for ( int row = 0; row < rows; ++row )
{
QModelIndex idx = selProxy->index( row, column, QModelIndex() );
QObject *obj = idx.data(KJotsModel::GrantleeObjectRole).value<QObject*>();
KJotsEntity *kjotsEntity = qobject_cast<KJotsEntity*>(obj);
kjotsEntity->setIndex(idx);
objectList << QVariant::fromValue(static_cast<QObject *>(kjotsEntity));
}
hash.insert( QLatin1String( "entities" ), objectList);
Context c(hash);
QString currentTheme = m_loader->themeName();
m_loader->setTheme( QLatin1String("xml_output") );
Template t = m_templateEngine->loadByName( QLatin1String( "template.xml" ) );
QString result = t->render(&c);
m_loader->setTheme(currentTheme);
return result;
}
void KJotsWidget::renderSelection()
{
const int rows = selProxy->rowCount();
// If the selection is a single page, present it for editing...
if (rows == 1)
{
QModelIndex idx = selProxy->index( 0, 0, QModelIndex());
QTextDocument *document = idx.data( KJotsModel::DocumentRole ).value<QTextDocument*>();
if ( document )
{
editor->setDocument( document );
QTextCursor textCursor = document->property( "textCursor" ).value<QTextCursor>();
if ( !textCursor.isNull() )
editor->setTextCursor( textCursor );
stackedWidget->setCurrentWidget( editor );
editor->setFocus();
return;
} // else fallthrough
}
// ... Otherwise, render the selection read-only.
QTextDocument doc;
QTextCursor cursor(&doc);
browser->setHtml( renderSelectionToHtml() );
stackedWidget->setCurrentWidget( browser );
}
QString KJotsWidget::getThemeFromUser()
{
return QString();
#if 0
bool ok;
QString text = QInputDialog::getText(this, i18n("Change Theme"),
tr("Theme name:"), QLineEdit::Normal,
m_loader->themeName(), &ok);
if (!ok || text.isEmpty())
{
return QLatin1String("default");
}
return text;
#endif
}
void KJotsWidget::changeTheme()
{
#if 0
m_loader->setTheme(getThemeFromUser());
renderSelection();
#endif
}
void KJotsWidget::exportSelectionToHtml()
{
QString currentTheme = m_loader->themeName();
QString themeName = getThemeFromUser();
if (themeName.isEmpty())
{
themeName = QLatin1String( "default" );
}
m_loader->setTheme(themeName);
QString filename = KFileDialog::getSaveFileName();
if (!filename.isEmpty())
{
QFile exportFile ( filename );
if ( !exportFile.open(QIODevice::WriteOnly | QIODevice::Text) ) {
m_loader->setTheme(currentTheme);
KMessageBox::error(0, i18n("<qt>Error opening internal file.</qt>"));
return;
}
exportFile.write(renderSelectionToHtml().toUtf8());
exportFile.close();
}
m_loader->setTheme(currentTheme);
}
void KJotsWidget::exportSelectionToPlainText()
{
QString currentTheme = m_loader->themeName();
m_loader->setTheme( QLatin1String("plain_text") );
QString filename = KFileDialog::getSaveFileName();
if (!filename.isEmpty())
{
QFile exportFile ( filename );
if ( !exportFile.open(QIODevice::WriteOnly | QIODevice::Text) ) {
m_loader->setTheme(currentTheme);
KMessageBox::error(0, i18n("<qt>Error opening internal file.</qt>"));
return;
}
exportFile.write(renderSelectionToPlainText().toUtf8());
exportFile.close();
}
m_loader->setTheme(currentTheme);
}
void KJotsWidget::exportSelectionToXml()
{
QString currentTheme = m_loader->themeName();
m_loader->setTheme( QLatin1String("xml_output") );
QString filename = KFileDialog::getSaveFileName();
if (!filename.isEmpty())
{
QFile exportFile ( filename );
if ( !exportFile.open(QIODevice::WriteOnly | QIODevice::Text) ) {
m_loader->setTheme(currentTheme);
KMessageBox::error(0, i18n("<qt>Error opening internal file.</qt>"));
return;
}
exportFile.write(renderSelectionToXml().toUtf8());
exportFile.close();
}
m_loader->setTheme(currentTheme);
}
void KJotsWidget::printPreviewSelection()
{
QPrinter printer(QPrinter::HighResolution);
printer.setDocName(QLatin1String("KJots_Print"));
printer.setFullPage(false);
printer.setCreator(QLatin1String("KJots"));
KPrintPreview previewdlg( &printer, 0 );
print(printer);
previewdlg.exec();
}
void KJotsWidget::printSelection()
{
QPrinter printer(QPrinter::HighResolution);
printer.setDocName(QLatin1String("KJots_Print"));
printer.setFullPage(false);
printer.setCreator(QLatin1String("KJots"));
//Not supported in Qt?
//printer->setPageSelection(QPrinter::ApplicationSide);
//KPrinter::pageList() only works with ApplicationSide. ApplicationSide
//requires min/max pages. How am I supposed to tell how many pages there
//are before I setup the printer?
QPointer<QPrintDialog> printDialog = new QPrintDialog(&printer, this);
QAbstractPrintDialog::PrintDialogOptions options = printDialog->enabledOptions();
options &= ~QAbstractPrintDialog::PrintPageRange;
if (activeEditor()->textCursor().hasSelection())
options |= QAbstractPrintDialog::PrintSelection;
printDialog->setEnabledOptions(options);
printDialog->setWindowTitle(i18n("Send To Printer"));
if (printDialog->exec() == QDialog::Accepted) {
print(printer);
}
delete printDialog;
}
void KJotsWidget::print(QPrinter &printer)
{
QTextDocument printDocument;
if ( printer.printRange() == QPrinter::Selection )
{
printDocument.setHtml( activeEditor()->textCursor().selection().toHtml() );
} else {
//QTextCursor printCursor ( &printDocument );
QString currentTheme = m_loader->themeName();
m_loader->setTheme( QLatin1String("default") );
printDocument.setHtml( renderSelectionToHtml() );
m_loader->setTheme( currentTheme );
}
QPainter p(&printer);
// Check that there is a valid device to print to.
if (p.isActive()) {
QTextDocument *doc = &printDocument;
QRectF body = QRectF(QPointF(0, 0), doc->pageSize());
QPointF pageNumberPos;
QAbstractTextDocumentLayout *layout = doc->documentLayout();
layout->setPaintDevice(p.device());
const int dpiy = p.device()->logicalDpiY();
const int margin = (int) ((2/2.54)*dpiy); // 2 cm margins
QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
fmt.setMargin(margin);
doc->rootFrame()->setFrameFormat(fmt);
body = QRectF(0, 0, p.device()->width(), p.device()->height());
pageNumberPos = QPointF(body.width() - margin,
body.height() - margin
+ QFontMetrics(doc->defaultFont(), p.device()).ascent()
+ 5 * p.device()->logicalDpiY() / 72);
doc->setPageSize(body.size());
int docCopies = printer.numCopies();
for (int copy = 0; copy < docCopies; ++copy) {
int lastPage = layout->pageCount();
for ( int page = 1; page <= lastPage ; ++page ) {
p.save();
p.translate(body.left(), body.top() - (page - 1) * body.height());
QRectF view(0, (page - 1) * body.height(), body.width(), body.height());
QAbstractTextDocumentLayout *layout = doc->documentLayout();
QAbstractTextDocumentLayout::PaintContext ctx;
p.setClipRect(view);
ctx.clip = view;
// don't use the system palette text as default text color, on HP/UX
// for example that's white, and white text on white paper doesn't
// look that nice
ctx.palette.setColor(QPalette::Text, Qt::black);
layout->draw(&p, ctx);
if (!pageNumberPos.isNull()) {
p.setClipping(false);
p.setFont(QFont(doc->defaultFont()));
const QString pageString = QString::number(page);
p.drawText(qRound(pageNumberPos.x() - p.fontMetrics().width(pageString)),
qRound(pageNumberPos.y() + view.top()),
pageString);
}
p.restore();
if ( (page+1) <= lastPage ) {
printer.newPage();
}
}
}
}
}
void KJotsWidget::selectNext( int role, int step )
{
QModelIndexList list = treeview->selectionModel()->selectedRows();
Q_ASSERT( list.size() == 1 );
QModelIndex idx = list.at( 0 );
const int column = idx.column();
QModelIndex sibling = idx.sibling( idx.row() + step, column );
while ( sibling.isValid() )
{
if ( sibling.data( role ).toInt() >= 0 )
{
treeview->selectionModel()->select( sibling, QItemSelectionModel::SelectCurrent );
return;
}
sibling = sibling.sibling( sibling.row() + step, column );
}
kWarning() << "No valid selection";
}
void KJotsWidget::nextBook()
{
return selectNext( EntityTreeModel::CollectionIdRole, 1 );
}
void KJotsWidget::nextPage()
{
return selectNext( EntityTreeModel::ItemIdRole, 1 );
}
void KJotsWidget::prevBook()
{
return selectNext( EntityTreeModel::CollectionIdRole, -1 );
}
void KJotsWidget::prevPage()
{
return selectNext( EntityTreeModel::ItemIdRole, -1 );
}
bool KJotsWidget::canGo( int role, int step ) const
{
QModelIndexList list = treeview->selectionModel()->selectedRows();
if ( list.size() != 1 )
{
return false;
}
QModelIndex currentIdx = list.at( 0 );
const int column = currentIdx.column();
Q_ASSERT( currentIdx.isValid() );
QModelIndex sibling = currentIdx.sibling( currentIdx.row() + step, column );
while ( sibling.isValid() && sibling != currentIdx )
{
if ( sibling.data( role ).toInt() >= 0 )
return true;
sibling = sibling.sibling( sibling.row() + step, column );
}
return false;
}
bool KJotsWidget::canGoNextPage() const
{
return canGo( EntityTreeModel::ItemIdRole, 1 );
}
bool KJotsWidget::canGoPreviousPage() const
{
return canGo( EntityTreeModel::ItemIdRole, -1 );
}
bool KJotsWidget::canGoNextBook() const
{
return canGo( EntityTreeModel::CollectionIdRole, 1 );
}
bool KJotsWidget::canGoPreviousBook() const
{
return canGo( EntityTreeModel::CollectionIdRole, -1 );
}
void KJotsWidget::selectionChanged( const QItemSelection &selected, const QItemSelection &deselected )
{
Q_UNUSED( selected );
emit canGoNextBookChanged( canGoPreviousBook() );
emit canGoNextPageChanged( canGoNextPage() );
emit canGoPreviousBookChanged( canGoPreviousBook() );
emit canGoPreviousPageChanged( canGoPreviousPage() );
if ( deselected.size() == 1 )
{
editor->document()->setProperty( "textCursor", QVariant::fromValue( editor->textCursor() ) );
if ( editor->document()->isModified() )
{
treeview->model()->setData( deselected.indexes().first(), QVariant::fromValue( editor->document() ), KJotsModel::DocumentRole );
}
}
}
/*!
Shows the search dialog when "Find" is selected.
*/
void KJotsWidget::onShowSearch()
{
onUpdateSearch();
QTextEdit *browserOrEditor = activeEditor();
if ( browserOrEditor->textCursor().hasSelection() ) {
searchDialog->setHasSelection(true);
long dialogOptions = searchDialog->options();
dialogOptions |= KFind::SelectedText;
searchDialog->setOptions(dialogOptions);
} else {
searchDialog->setHasSelection(false);
}
searchDialog->setFindHistory(searchHistory);
searchDialog->show();
onUpdateSearch();
}
/*!
Updates the search dialog if the user is switching selections while it is open.
*/
void KJotsWidget::onUpdateSearch()
{
if ( searchDialog->isVisible() ) {
long searchOptions = searchDialog->options();
if ( searchOptions & KFind::SelectedText ) {
searchAllPages->setCheckState( Qt::Unchecked );
searchAllPages->setEnabled( false );
} else {
searchAllPages->setEnabled( true );
}
if ( searchAllPages->checkState() == Qt::Checked ) {
searchOptions &= ~KFind::SelectedText;
searchDialog->setOptions( searchOptions );
searchDialog->setHasSelection( false );
} else {
if ( activeEditor()->textCursor().hasSelection() ) {
searchDialog->setHasSelection( true );
}
}
if ( activeEditor()->textCursor().hasSelection() ) {
if ( searchAllPages->checkState() == Qt::Unchecked ) {
searchDialog->setHasSelection( true );
}
} else {
searchOptions &= ~KFind::SelectedText;
searchDialog->setOptions( searchOptions );
searchDialog->setHasSelection( false );
}
}
}
/*!
Called when the user presses OK in the search dialog.
*/
void KJotsWidget::onStartSearch()
{
QString searchPattern = searchDialog->pattern();
if ( !searchHistory.contains ( searchPattern ) ) {
searchHistory.prepend( searchPattern );
}
QTextEdit *browserOrEditor = activeEditor();
QTextCursor cursor = browserOrEditor->textCursor();
long searchOptions = searchDialog->options();
if ( searchOptions & KFind::FromCursor ) {
searchPos = cursor.position();
searchBeginPos = 0;
cursor.movePosition( QTextCursor::End );
searchEndPos = cursor.position();
} else {
if ( searchOptions & KFind::SelectedText ) {
searchBeginPos = cursor.selectionStart();
searchEndPos = cursor.selectionEnd();
} else {
searchBeginPos = 0;
cursor.movePosition( QTextCursor::End );
searchEndPos = cursor.position();
}
if ( searchOptions & KFind::FindBackwards ) {
searchPos = searchEndPos;
} else {
searchPos = searchBeginPos;
}
}
m_xmlGuiClient->actionCollection()->action( QLatin1String(KStandardAction::name( KStandardAction::FindNext )) )->setEnabled( true );
onRepeatSearch();
}
/*!
Called when user chooses "Find Next"
*/
void KJotsWidget::onRepeatSearch()
{
if ( search( false ) == 0 ) {
KMessageBox::sorry( 0, i18n( "<qt>No matches found.</qt>" ) );
m_xmlGuiClient->actionCollection()->action( QLatin1String(KStandardAction::name( KStandardAction::FindNext )) )->setEnabled( false );
}
}
/*!
Called when user presses Cancel in find dialog.
*/
void KJotsWidget::onEndSearch()
{
m_xmlGuiClient->actionCollection()->action( QLatin1String(KStandardAction::name( KStandardAction::FindNext )) )->setEnabled( false );
}
/*!
Shows the replace dialog when "Replace" is selected.
*/
void KJotsWidget::onShowReplace()
{
Q_ASSERT( editor->isVisible() );
if ( editor->textCursor().hasSelection() ) {
replaceDialog->setHasSelection( true );
long dialogOptions = replaceDialog->options();
dialogOptions |= KFind::SelectedText;
replaceDialog->setOptions( dialogOptions );
} else {
replaceDialog->setHasSelection( false );
}
replaceDialog->setFindHistory( searchHistory );
replaceDialog->setReplacementHistory( replaceHistory );
replaceDialog->show();
onUpdateReplace();
}
/*!
Updates the replace dialog if the user is switching selections while it is open.
*/
void KJotsWidget::onUpdateReplace()
{
if ( replaceDialog->isVisible() ) {
long replaceOptions = replaceDialog->options();
if ( replaceOptions & KFind::SelectedText ) {
replaceAllPages->setCheckState( Qt::Unchecked );
replaceAllPages->setEnabled( false );
} else {
replaceAllPages->setEnabled( true );
}
if ( replaceAllPages->checkState() == Qt::Checked ) {
replaceOptions &= ~KFind::SelectedText;
replaceDialog->setOptions( replaceOptions );
replaceDialog->setHasSelection( false );
} else {
if ( activeEditor()->textCursor().hasSelection() ) {
replaceDialog->setHasSelection( true );
}
}
}
}
/*!
Called when the user presses OK in the replace dialog.
*/
void KJotsWidget::onStartReplace()
{
QString searchPattern = replaceDialog->pattern();
if ( !searchHistory.contains ( searchPattern ) ) {
searchHistory.prepend( searchPattern );
}
QString replacePattern = replaceDialog->replacement();
if ( !replaceHistory.contains ( replacePattern ) ) {
replaceHistory.prepend( replacePattern );
}
QTextCursor cursor = editor->textCursor();
long replaceOptions = replaceDialog->options();
if ( replaceOptions & KFind::FromCursor ) {
replacePos = cursor.position();
replaceBeginPos = 0;
cursor.movePosition( QTextCursor::End );
replaceEndPos = cursor.position();
} else {
if ( replaceOptions & KFind::SelectedText ) {
replaceBeginPos = cursor.selectionStart();
replaceEndPos = cursor.selectionEnd();
} else {
replaceBeginPos = 0;
cursor.movePosition( QTextCursor::End );
replaceEndPos = cursor.position();
}
if ( replaceOptions & KFind::FindBackwards ) {
replacePos = replaceEndPos;
} else {
replacePos = replaceBeginPos;
}
}
replaceStartPage = treeview->selectionModel()->selectedRows().first();
//allow KReplaceDialog to exit so the user can see.
QTimer::singleShot( 0, this, SLOT(onRepeatReplace()) );
}
/*!
Only called after onStartReplace. Kept the name scheme for consistancy.
*/
void KJotsWidget::onRepeatReplace()
{
KJotsReplaceNextDialog *dlg = 0;
QString searchPattern = replaceDialog->pattern();
QString replacePattern = replaceDialog->replacement();
int found = 0;
int replaced = 0;
long replaceOptions = replaceDialog->options();
if ( replaceOptions & KReplaceDialog::PromptOnReplace ) {
dlg = new KJotsReplaceNextDialog( this );
}
forever {
if ( !search( true ) ) {
break;
}
QTextCursor cursor = editor->textCursor();
if ( !cursor.hasSelection() ) {
break;
} else {
++found;
}
QString replacementText = replacePattern;
if ( replaceOptions & KReplaceDialog::BackReference ) {
QRegExp regExp ( searchPattern, ( replaceOptions & Qt::CaseSensitive ) ?
Qt::CaseSensitive : Qt::CaseInsensitive, QRegExp::RegExp2 );
regExp.indexIn(cursor.selectedText());
int capCount = regExp.numCaptures();
for ( int i=0; i <= capCount; ++i ) {
QString c = QString::fromLatin1( "\\%1" ).arg( i );
replacementText.replace( c, regExp.cap( i ) );
}
}
if ( replaceOptions & KReplaceDialog::PromptOnReplace ) {
dlg->setLabel( cursor.selectedText(), replacementText );
if ( !dlg->exec() ) {
break;
}
if ( dlg->answer() != KDialog::User2 ) {
cursor.insertText( replacementText );
editor->setTextCursor( cursor );
++replaced;
}
if ( dlg->answer() == KDialog::User1 ) {
replaceOptions |= ~KReplaceDialog::PromptOnReplace;
}
} else {
cursor.insertText( replacementText );
editor->setTextCursor( cursor );
++replaced;
}
}
if ( replaced == found )
{
KMessageBox::information( 0, i18np( "<qt>Replaced 1 occurrence.</qt>", "<qt>Replaced %1 occurrences.</qt>", replaced ) );
}
else if ( replaced < found )
{
KMessageBox::information( 0,
i18np( "<qt>Replaced %2 of 1 occurrence.</qt>", "<qt>Replaced %2 of %1 occurrences.</qt>", found, replaced ) );
}
if ( dlg ) {
delete dlg;
}
}
/*!
Called when user presses Cancel in replace dialog. Just a placeholder for now.
*/
void KJotsWidget::onEndReplace()
{
}
/*!
Searches for the given pattern, with the given options. This is huge and
unwieldly function, but the operation we're performing is huge and unwieldly.
*/
int KJotsWidget::search( bool replacing )
{
int rc = 0;
int *beginPos = replacing ? &replaceBeginPos : &searchBeginPos;
int *endPos = replacing ? &replaceEndPos : &searchEndPos;
long options = replacing ? replaceDialog->options() : searchDialog->options();
QString pattern = replacing ? replaceDialog->pattern() : searchDialog->pattern();
int *curPos = replacing ? &replacePos : &searchPos;
QModelIndex startPage = replacing ? replaceStartPage : treeview->selectionModel()->selectedRows().first();
bool allPages = false;
QCheckBox *box = replacing ? replaceAllPages : searchAllPages;
if ( box->isEnabled() && box->checkState() == Qt::Checked ) {
allPages = true;
}
QTextDocument::FindFlags findFlags = 0;
if ( options & Qt::CaseSensitive ) {
findFlags |= QTextDocument::FindCaseSensitively;
}
if ( options & KFind::WholeWordsOnly ) {
findFlags |= QTextDocument::FindWholeWords;
}
if ( options & KFind::FindBackwards ) {
findFlags |= QTextDocument::FindBackward;
}
// We will find a match or return 0
int attempts = 0;
forever {
++attempts;
QTextEdit *browserOrEditor = activeEditor();
QTextDocument *theDoc = browserOrEditor->document();
QTextCursor cursor;
if ( options & KFind::RegularExpression ) {
QRegExp regExp ( pattern, ( options & Qt::CaseSensitive ) ?
Qt::CaseSensitive : Qt::CaseInsensitive, QRegExp::RegExp2 );
cursor = theDoc->find( regExp, *curPos, findFlags );
} else {
cursor = theDoc->find( pattern, *curPos, findFlags );
}
if ( cursor.hasSelection() ) {
if ( cursor.selectionStart() >= *beginPos && cursor.selectionEnd() <= *endPos ) {
browserOrEditor->setTextCursor( cursor );
browserOrEditor->ensureCursorVisible();
*curPos = ( options & KFind::FindBackwards ) ?
cursor.selectionStart() : cursor.selectionEnd();
rc = 1;
break;
}
}
//No match. Determine what to do next.
if ( replacing && !( options & KFind::FromCursor ) && !allPages ) {
break;
}
if ( ( options & KFind::FromCursor ) && !allPages ) {
if ( KMessageBox::questionYesNo( this,
i18n("<qt>End of search area reached. Do you want to wrap around and continue?</qt>")) ==
KMessageBox::No ) {
rc = 3;
break;
}
}
if ( allPages ) {
if ( options & KFind::FindBackwards ) {
if ( canGoPreviousPage() )
prevPage();
} else {
if ( canGoNextPage() )
nextPage();
}
if ( startPage == treeview->selectionModel()->selectedRows().first() ) {
rc = 0;
break;
}
*beginPos = 0;
cursor = editor->textCursor();
cursor.movePosition( QTextCursor::End );
*endPos = cursor.position();
*curPos = ( options & KFind::FindBackwards ) ? *endPos : *beginPos;
continue;
}
// By now, we should have figured out what to do. In all remaining cases we
// will automatically loop and try to "find next" from the top/bottom, because
// I like this behavior the best.
if ( attempts <= 1 ) {
*curPos = ( options & KFind::FindBackwards ) ? *endPos : *beginPos;
} else {
// We've already tried the loop and failed to find anything. Bail.
rc = 0;
break;
}
}
return rc;
}
void KJotsWidget::updateCaption()
{
emit captionChanged( treeview->captionForSelection( QLatin1String(" / ") ) );
}
void KJotsWidget::dataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight )
{
QModelIndexList rows = treeview->selectionModel()->selectedRows();
if ( rows.size() != 1 )
return;
QItemSelection changed( topLeft, bottomRight );
if ( changed.contains( rows.first() ) )
{
emit captionChanged( treeview->captionForSelection( QLatin1String(" / ") ) );
}
}
bool KJotsWidget::queryClose()
{
KJotsSettings::setSplitterSizes(m_splitter->sizes());
KJotsSettings::self()->writeConfig();
m_orderProxy->saveOrder();
return true;
}
void KJotsWidget::actionLock()
{
QModelIndexList selection = treeview->selectionModel()->selectedRows();
if ( selection.isEmpty() )
return;
Collection::List collections;
Item::List items;
foreach ( const QModelIndex &idx, selection )
{
Collection col = idx.data( EntityTreeModel::CollectionRole ).value<Collection>();
if ( col.isValid() )
{
collections << col;
} else {
Item item = idx.data( EntityTreeModel::ItemRole ).value<Item>();
if ( item.isValid() )
items << item;
}
}
if ( collections.isEmpty() && items.isEmpty() )
return;
KJotsLockJob *job = new KJotsLockJob(collections, items, this);
}
void KJotsWidget::actionUnlock()
{
QModelIndexList selection = treeview->selectionModel()->selectedRows();
if ( selection.isEmpty() )
return;
Collection::List collections;
Item::List items;
foreach ( const QModelIndex &idx, selection )
{
Collection col = idx.data( EntityTreeModel::CollectionRole ).value<Collection>();
if ( col.isValid() )
{
collections << col;
} else {
Item item = idx.data( EntityTreeModel::ItemRole ).value<Item>();
if ( item.isValid() )
items << item;
}
}
if ( collections.isEmpty() && items.isEmpty() )
return;
KJotsLockJob *job = new KJotsLockJob(collections, items, KJotsLockJob::UnlockJob, this);
}
void KJotsWidget::actionSortChildrenAlpha()
{
QModelIndexList selection = treeview->selectionModel()->selectedRows();
foreach( const QModelIndex &index, selection )
{
const QPersistentModelIndex persistent( index );
m_sortProxyModel->sortChildrenAlphabetically( m_orderProxy->mapToSource( index ) );
m_orderProxy->clearOrder( persistent );
}
}
void KJotsWidget::actionSortChildrenByDate()
{
QModelIndexList selection = treeview->selectionModel()->selectedRows();
foreach( const QModelIndex &index, selection )
{
const QPersistentModelIndex persistent( index );
m_sortProxyModel->sortChildrenByCreationTime( m_orderProxy->mapToSource( index ) );
m_orderProxy->clearOrder( persistent );
}
}