diff --git a/libs/main/KoTemplateCreateDia.cpp b/libs/main/KoTemplateCreateDia.cpp index 496943d7b27..2d43de69124 100644 --- a/libs/main/KoTemplateCreateDia.cpp +++ b/libs/main/KoTemplateCreateDia.cpp @@ -1,538 +1,539 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Reginald Stadlbauer 2000 Werner Trobin Copyright (C) 2004 Nicolas GOUTTE 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include // ODF thumbnail extent static const int thumbnailExtent = 128; class KoTemplateCreateDiaPrivate { public: KoTemplateCreateDiaPrivate(const QString &filePath, const QPixmap &thumbnail) : m_filePath(filePath) , m_thumbnail(thumbnail) { m_tree=0; m_name=0; m_default=0; m_custom=0; m_select=0; m_preview=0; m_groups=0; m_add=0; m_remove=0; m_defaultTemplate=0; } ~KoTemplateCreateDiaPrivate() { delete m_tree; } KoTemplateTree *m_tree; QLineEdit *m_name; QRadioButton *m_default, *m_custom; QPushButton *m_select; QLabel *m_preview; QString m_customFile; QPixmap m_customPixmap; QTreeWidget *m_groups; QPushButton *m_add, *m_remove; QCheckBox *m_defaultTemplate; QString m_filePath; QPixmap m_thumbnail; bool m_changed; }; /**************************************************************************** * * Class: koTemplateCreateDia * ****************************************************************************/ KoTemplateCreateDia::KoTemplateCreateDia(const QString &templatesResourcePath, const QString &filePath, const QPixmap &thumbnail, QWidget *parent) : KoDialog(parent) , d(new KoTemplateCreateDiaPrivate(filePath, thumbnail)) { setButtons( KoDialog::Ok|KoDialog::Cancel ); setDefaultButton( KoDialog::Ok ); setCaption( i18n( "Create Template" ) ); setModal( true ); setObjectName( "template create dia" ); QWidget *mainwidget=mainWidget(); QHBoxLayout *mbox=new QHBoxLayout( mainwidget ); QVBoxLayout* leftbox = new QVBoxLayout(); mbox->addLayout( leftbox ); QLabel *label=new QLabel(i18nc("Template name", "Name:"), mainwidget); QHBoxLayout *namefield=new QHBoxLayout(); leftbox->addLayout( namefield ); namefield->addWidget(label); d->m_name=new QLineEdit(mainwidget); d->m_name->setFocus(); connect(d->m_name, SIGNAL(textChanged(QString)), this, SLOT(slotNameChanged(QString))); namefield->addWidget(d->m_name); label=new QLabel(i18n("Group:"), mainwidget); leftbox->addWidget(label); d->m_groups = new QTreeWidget(mainwidget); leftbox->addWidget(d->m_groups); d->m_groups->setColumnCount(1); d->m_groups->setHeaderHidden(true); d->m_groups->setRootIsDecorated(true); d->m_groups->setSortingEnabled(true); d->m_tree = new KoTemplateTree(templatesResourcePath, true); fillGroupTree(); d->m_groups->sortItems(0, Qt::AscendingOrder); QHBoxLayout *bbox=new QHBoxLayout(); leftbox->addLayout( bbox ); d->m_add=new QPushButton(i18n("&Add Group..."), mainwidget); connect(d->m_add, SIGNAL(clicked()), this, SLOT(slotAddGroup())); bbox->addWidget(d->m_add); d->m_remove=new QPushButton(i18n("&Remove"), mainwidget); connect(d->m_remove, SIGNAL(clicked()), this, SLOT(slotRemove())); bbox->addWidget(d->m_remove); QVBoxLayout *rightbox=new QVBoxLayout(); mbox->addLayout( rightbox ); QGroupBox *pixbox = new QGroupBox(i18n("Picture"), mainwidget); rightbox->addWidget(pixbox); QVBoxLayout *pixlayout=new QVBoxLayout(pixbox ); d->m_default=new QRadioButton(i18n("&Preview"), pixbox); d->m_default->setChecked(true); connect(d->m_default, SIGNAL(clicked()), this, SLOT(slotDefault())); pixlayout->addWidget(d->m_default); QHBoxLayout *custombox=new QHBoxLayout(); d->m_custom=new QRadioButton(i18n("Custom:"), pixbox); d->m_custom->setChecked(false); connect(d->m_custom, SIGNAL(clicked()), this, SLOT(slotCustom())); custombox->addWidget(d->m_custom); d->m_select=new QPushButton(i18n("&Select..."), pixbox); connect(d->m_select, SIGNAL(clicked()), this, SLOT(slotSelect())); custombox->addWidget(d->m_select); custombox->addStretch(1); pixlayout->addLayout(custombox); d->m_preview=new QLabel(pixbox); // setPixmap() -> auto resize? pixlayout->addWidget(d->m_preview, 0, Qt::AlignCenter); pixlayout->addStretch(1); d->m_defaultTemplate = new QCheckBox( i18n("Use the new template as default"), mainwidget ); d->m_defaultTemplate->setChecked( true ); // QT5TODO: think about restoring this // d->m_defaultTemplate->setToolTip( i18n("Use the new template every time %1 starts",componentData.aboutData()->programName() ) ); rightbox->addWidget( d->m_defaultTemplate ); enableButtonOk(false); d->m_changed=false; updatePixmap(); connect(d->m_groups, SIGNAL(itemSelectionChanged()), this, SLOT(slotSelectionChanged())); d->m_remove->setEnabled(d->m_groups->currentItem()); connect(this,SIGNAL(okClicked()),this,SLOT(slotOk())); } KoTemplateCreateDia::~KoTemplateCreateDia() { delete d; } void KoTemplateCreateDia::slotSelectionChanged() { const QTreeWidgetItem* item = d->m_groups->currentItem(); d->m_remove->setEnabled( item ); if ( ! item ) return; if ( item->parent() != nullptr ) { d->m_name->setText( item->text( 0 ) ); } } void KoTemplateCreateDia::createTemplate(const QString &templatesResourcePath, const char *suffix, KoDocument *document, QWidget *parent) { QTemporaryFile *tempFile = new QTemporaryFile(QDir::tempPath() + QLatin1String("/") + qAppName() + QLatin1String("_XXXXXX") + suffix); //Check that creation of temp file was successful if (!tempFile->open()) { delete tempFile; qWarning("Creation of temporary file to store template failed."); return; } const QString fileName = tempFile->fileName(); tempFile->close(); // need to close on Windows before we can open it again to save delete tempFile; // now the file has disappeared and we can create a new file with the generated name document->saveNativeFormat(fileName); const QPixmap thumbnail = document->generatePreview(QSize(thumbnailExtent, thumbnailExtent)); KoTemplateCreateDia *dia = new KoTemplateCreateDia(templatesResourcePath, fileName, thumbnail, parent); dia->exec(); delete dia; QDir d; d.remove(fileName); } static void saveAsQuadraticPng(const QPixmap &pixmap, const QString &fileName) { QImage icon = pixmap.toImage(); icon = icon.convertToFormat(QImage::Format_ARGB32); const int iconExtent = qMax(icon.width(), icon.height()); icon = icon.copy((icon.width() - iconExtent) / 2, (icon.height() - iconExtent) / 2, iconExtent, iconExtent); icon.save(fileName, "PNG"); } void KoTemplateCreateDia::slotOk() { // get the current item, if there is one... QTreeWidgetItem *item = d->m_groups->currentItem(); if(!item) item = d->m_groups->topLevelItem(0); if(!item) { // safe :) d->m_tree->writeTemplateTree(); slotButtonClicked( KoDialog::Cancel ); return; } // is it a group or a template? anyway - get the group :) if(item->parent() != nullptr) item=item->parent(); if(!item) { // *very* safe :P d->m_tree->writeTemplateTree(); slotButtonClicked( KoDialog::Cancel ); return; } KoTemplateGroup *group=d->m_tree->find(item->text(0)); if(!group) { // even safer d->m_tree->writeTemplateTree(); slotButtonClicked( KoDialog::Cancel ); return; } if(d->m_name->text().isEmpty()) { d->m_tree->writeTemplateTree(); slotButtonClicked( KoDialog::Cancel ); return; } // copy the tmp file and the picture the app provides QString dir = KoResourcePaths::saveLocation("data", d->m_tree->templatesResourcePath()); dir+=group->name(); QString templateDir=dir+"/.source/"; QString iconDir=dir+"/.icon/"; QString file=KoTemplates::trimmed(d->m_name->text()); QString tmpIcon=".icon/"+file; tmpIcon+=".png"; QString icon=iconDir+file; icon+=".png"; // try to find the extension for the template file :P const int pos = d->m_filePath.lastIndexOf(QLatin1Char('.')); QString ext; if ( pos > -1 ) ext = d->m_filePath.mid(pos); else qWarning(/*30004*/) << "Template extension not found!"; QUrl dest; dest.setPath(templateDir+file+ext); if (QFile::exists( dest.toLocalFile())) { do { file.prepend( '_' ); dest.setPath( templateDir + file + ext ); tmpIcon=".icon/"+file+".png"; icon=iconDir+file+".png"; } while ( KIO::NetAccess::exists( dest, KIO::NetAccess::DestinationSide, this ) ); } bool ignore = false; debugMain <<"Trying to create template:" << d->m_name->text() <<"URL=" <<".source/"+file+ext <<" ICON=" << tmpIcon; KoTemplate *t=new KoTemplate(d->m_name->text(), QString(), ".source/"+file+ext, tmpIcon, "", "", "", "", "", false, false, true); if(!group->add(t)) { KoTemplate *existingTemplate=group->find(d->m_name->text()); if(existingTemplate && !existingTemplate->isHidden()) { if(KMessageBox::warningYesNo(this, i18n("Do you really want to overwrite" " the existing '%1' template?",existingTemplate->name()))==KMessageBox::Yes) group->add(t, true); else { delete t; return; } } else ignore = true; } QDir dummyDir; if(!dummyDir.mkpath(templateDir) || !dummyDir.mkpath(iconDir)) { d->m_tree->writeTemplateTree(); slotButtonClicked( KoDialog::Cancel ); return; } QUrl orig; orig.setPath(d->m_filePath); // don't overwrite the hidden template file with a new non-hidden one if ( !ignore ) { QFile::copy(d->m_filePath, dest.path()); // save the picture as icon // (needs to be square, otherwise KIconLoader dpes nasty changes) if(d->m_default->isChecked() && !d->m_thumbnail.isNull()) { saveAsQuadraticPng(d->m_thumbnail, icon); } else if(!d->m_customPixmap.isNull()) { saveAsQuadraticPng(d->m_customPixmap, icon); } else { qWarning(/*30004*/) << "Could not save the preview picture!"; } } // if there's a .directory file, we copy this one, too bool ready=false; QStringList tmp=group->dirs(); for(QStringList::ConstIterator it=tmp.constBegin(); it!=tmp.constEnd() && !ready; ++it) { if((*it).contains(dir)==0) { orig.setPath( (*it)+".directory" ); // Check if we can read the file if( KIO::NetAccess::exists(orig, KIO::NetAccess::SourceSide, this) ) { dest.setPath( dir+"/.directory" ); // We copy the file with overwrite KIO::FileCopyJob *job = KIO::file_copy( orig, dest, -1, KIO::Overwrite | KIO::HideProgressInfo); job->exec(); ready=true; } } } d->m_tree->writeTemplateTree(); if ( d->m_defaultTemplate->isChecked() ) { KConfigGroup grp(KSharedConfig::openConfig(), "TemplateChooserDialog"); grp.writeEntry( "LastReturnType", "Template" ); grp.writePathEntry( "FullTemplateName", dir + '/' + t->file() ); grp.writePathEntry( "AlwaysUseTemplate", dir + '/' + t->file() ); } } void KoTemplateCreateDia::slotDefault() { d->m_default->setChecked(true); d->m_custom->setChecked(false); updatePixmap(); } void KoTemplateCreateDia::slotCustom() { d->m_default->setChecked(false); d->m_custom->setChecked(true); if(d->m_customFile.isEmpty()) slotSelect(); else updatePixmap(); } void KoTemplateCreateDia::slotSelect() { d->m_default->setChecked(false); d->m_custom->setChecked(true); QString name = KIconDialog::getIcon(); if( name.isEmpty() ) { if(d->m_customFile.isEmpty()) { d->m_default->setChecked(true); d->m_custom->setChecked(false); } return; } const QString path = KIconLoader::global()->iconPath(name, -thumbnailExtent); d->m_customFile = path; d->m_customPixmap=QPixmap(); updatePixmap(); } void KoTemplateCreateDia::slotNameChanged(const QString &name) { if( ( name.trimmed().isEmpty() || !d->m_groups->topLevelItem(0) ) && !d->m_changed ) enableButtonOk(false); else enableButtonOk(true); } void KoTemplateCreateDia::slotAddGroup() { bool ok=false; const QString name = QInputDialog::getText(this, i18n("Add Group"), i18n("Enter group name:"), QLineEdit::Normal, QString(), &ok); if(!ok) return; KoTemplateGroup *group=d->m_tree->find(name); if(group && !group->isHidden()) { KMessageBox::information( this, i18n("This name is already used."), i18n("Add Group") ); return; } QString dir = KoResourcePaths::saveLocation("data", d->m_tree->templatesResourcePath()); dir+=name; KoTemplateGroup *newGroup=new KoTemplateGroup(name, dir, 0, true); d->m_tree->add(newGroup); QTreeWidgetItem *item = new QTreeWidgetItem(d->m_groups, QStringList() << name); d->m_groups->setCurrentItem(item); d->m_groups->sortItems(0, Qt::AscendingOrder); d->m_name->setFocus(); enableButtonOk(true); d->m_changed=true; } void KoTemplateCreateDia::slotRemove() { QTreeWidgetItem *item = d->m_groups->currentItem(); if(!item) return; QString what; QString removed; if (item->parent() == nullptr) { what = i18n("Do you really want to remove that group?"); removed = i18n("Remove Group"); } else { what = i18n("Do you really want to remove that template?"); removed = i18n("Remove Template"); } if(KMessageBox::warningContinueCancel(this, what, - removed,KGuiItem(i18n("&Delete"), koIconName("edit-delete")))==KMessageBox::Cancel) { + removed,KStandardGuiItem::del())==KMessageBox::Cancel) { d->m_name->setFocus(); return; } if(item->parent() == nullptr) { KoTemplateGroup *group=d->m_tree->find(item->text(0)); if(group) group->setHidden(true); } else { bool done=false; QList groups = d->m_tree->groups(); QList::const_iterator it = groups.constBegin(); for(; it != groups.constEnd() && !done; ++it) { KoTemplate *t = (*it)->find(item->text(0)); if(t) { t->setHidden(true); done=true; } } } delete item; item=0; enableButtonOk(true); d->m_name->setFocus(); d->m_changed=true; } void KoTemplateCreateDia::updatePixmap() { if(d->m_default->isChecked() && !d->m_thumbnail.isNull()) d->m_preview->setPixmap(d->m_thumbnail); else if(d->m_custom->isChecked() && !d->m_customFile.isEmpty()) { if(d->m_customPixmap.isNull()) { debugMain <<"Trying to load picture" << d->m_customFile; // use the code in KoTemplate to load the image... hacky, I know :) KoTemplate t("foo", "bar", QString(), d->m_customFile); d->m_customPixmap=t.loadPicture(); } else qWarning(/*30004*/) << "Trying to load picture"; if(!d->m_customPixmap.isNull()) d->m_preview->setPixmap(d->m_customPixmap); else d->m_preview->setText(i18n("Could not load picture.")); } else d->m_preview->setText(i18n("No picture available.")); } void KoTemplateCreateDia::fillGroupTree() { foreach(KoTemplateGroup *group, d->m_tree->groups()) { if(group->isHidden()) continue; QTreeWidgetItem *groupItem=new QTreeWidgetItem(d->m_groups, QStringList() << group->name()); foreach(KoTemplate *t, group->templates()) { if(t->isHidden()) continue; (void)new QTreeWidgetItem(groupItem, QStringList() << t->name()); } } } diff --git a/plugins/textediting/thesaurus/Thesaurus.cpp b/plugins/textediting/thesaurus/Thesaurus.cpp index de8f324c684..a5b92705064 100644 --- a/plugins/textediting/thesaurus/Thesaurus.cpp +++ b/plugins/textediting/thesaurus/Thesaurus.cpp @@ -1,769 +1,769 @@ /* This file is part of the KDE project * Copyright (C) 2001,2002,2003 Daniel Naber * Copyright (C) 2007 Fredy Yanardi * * 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. */ /* Copyright (C) 2001,2002,2003 Daniel Naber * This is a thesaurus based on a subset of WordNet. It also offers an * almost complete WordNet 1.7 frontend (WordNet is a powerful lexical * database/thesaurus) */ /* TODO: -Be more verbose if the result is empty -See the TODO's in the source below -If no match was found, use KSpell to offer alternative spellings? -Don't start WordNet before its tab is activated? -Maybe remove more uncommon words. However, the "polysemy/familiarity count" is sometimes very low for quite common word, e.g. "sky". -Fix "no mimesource" warning of QTextBrowser? Seems really harmless. NOT TODO: -Add part of speech information -- I think this would blow up the filesize too much */ #include "Thesaurus.h" #include "ThesaurusDebug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include Thesaurus::Thesaurus() { m_standAlone = false; m_thesProc = new KProcess; m_wnProc = new KProcess; m_dialog = new KoDialog(0); m_dialog->setButtons(KoDialog::Help | KoDialog::Ok | KoDialog::Cancel); m_dialog->setDefaultButton(KoDialog::Ok); m_dialog->setHelp(QString(), "thesaurus"); m_dialog->resize(600, 400); KConfigGroup cfg = KSharedConfig::openConfig()->group(""); m_dataFile = cfg.readEntry("datafile"); if (m_dataFile.isEmpty()) m_dataFile = KoResourcePaths::findResource("data", "calligra/thesaurus/thesaurus.txt"); setCaption(); m_noMatch = i18n("(No match)"); m_historyPos = 1; QWidget *page = new QWidget(); m_dialog->setMainWidget(page); QVBoxLayout *topLayout = new QVBoxLayout(page); QHBoxLayout *row1 = new QHBoxLayout; topLayout->addLayout(row1); m_edit = new KHistoryComboBox(page); QLabel editLabel(i18n("&Search for:"), page); editLabel.setBuddy(m_edit); m_search = new QPushButton(i18n("S&earch"), page); connect(m_search, SIGNAL(clicked()), this, SLOT(slotFindTerm())); row1->addWidget(&editLabel, 0); row1->addWidget(m_edit, 1); row1->addWidget(m_search, 0); m_back = new QToolButton(page); m_back->setIcon(koIcon("go-previous")); m_back->setToolTip(i18nc("@action:button Go back to the previous word in history", "Back")); row1->addWidget(m_back, 0); m_forward = new QToolButton(page); m_forward->setIcon(koIcon("go-next")); m_forward->setToolTip(i18nc("@action:button Go forward to the next word in history", "Forward")); row1->addWidget(m_forward, 0); QPushButton *lang = new QPushButton(i18n("Change Language..."), page); connect(lang, SIGNAL(clicked()), this, SLOT(slotChangeLanguage())); row1->addWidget(lang, 0); connect(m_back, SIGNAL(clicked()), this, SLOT(slotBack())); connect(m_forward, SIGNAL(clicked()), this, SLOT(slotForward())); m_tabWidget = new QTabWidget(page); topLayout->addWidget(m_tabWidget); // // Thesaurus Tab // QWidget *thesWidget = new QWidget(m_tabWidget); m_tabWidget->addTab(thesWidget, i18n("&Thesaurus")); QHBoxLayout *thesLayout = new QHBoxLayout; thesWidget->setLayout(thesLayout); QGroupBox *synGroupBox = new QGroupBox(i18n("Synonyms"), thesWidget); QHBoxLayout *synLayout = new QHBoxLayout(); synGroupBox->setLayout(synLayout); m_synListWidget = new QListWidget(synGroupBox); synLayout->addWidget(m_synListWidget); thesLayout->addWidget(synGroupBox); QGroupBox *hyperGroupBox = new QGroupBox(i18n("More General Words"), thesWidget); QHBoxLayout *hyperLayout = new QHBoxLayout(); hyperGroupBox->setLayout(hyperLayout); m_hyperListWidget = new QListWidget(hyperGroupBox); hyperLayout->addWidget(m_hyperListWidget); thesLayout->addWidget(hyperGroupBox); QGroupBox *hypoGroupBox = new QGroupBox(i18n("More Specific Words"), thesWidget); QHBoxLayout *hypoLayout = new QHBoxLayout(); hypoGroupBox->setLayout(hypoLayout); m_hypoListWidget = new QListWidget(hypoGroupBox); hypoLayout->addWidget(m_hypoListWidget); thesLayout->addWidget(hypoGroupBox); // single click -- keep display unambiguous by removing other selections: connect(m_synListWidget, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(slotSetReplaceTermSyn(QListWidgetItem*))); connect(m_hyperListWidget, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(slotSetReplaceTermHyper(QListWidgetItem*))); connect(m_hypoListWidget, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(slotSetReplaceTermHypo(QListWidgetItem*))); // double click -- set the double clicked item as the new search term connect(m_synListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(slotFindTermFromList(QListWidgetItem*))); connect(m_hyperListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(slotFindTermFromList(QListWidgetItem*))); connect(m_hypoListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(slotFindTermFromList(QListWidgetItem*))); // // WordNet Tab // QWidget *wnWidget = new QWidget(m_tabWidget); m_tabWidget->addTab(wnWidget, i18n("&Wordnet")); QVBoxLayout *wnLayout = new QVBoxLayout; wnWidget->setLayout(wnLayout); m_wnComboBox = new KComboBox(wnWidget); m_wnComboBox->setEditable(false); wnLayout->addWidget(m_wnComboBox); connect(m_wnComboBox, SIGNAL(activated(int)), this, SLOT(slotFindTerm())); m_resultTextBrowser = new QTextBrowser(wnWidget); m_resultTextBrowser->setReadOnly(true); connect(m_resultTextBrowser, SIGNAL(anchorClicked(QUrl)), this, SLOT(slotFindTermFromUrl(QUrl))); wnLayout->addWidget(m_resultTextBrowser); // Connect for the history box m_edit->setTrapReturnKey(true); // Do not use Return as default key... connect(m_edit, SIGNAL(returnPressed()), this, SLOT(slotFindTerm())); connect(m_edit, SIGNAL(activated(int)), this, SLOT(slotGotoHistory(int))); QHBoxLayout *row2 = new QHBoxLayout( /*m_top_layout*/ ); topLayout->addLayout(row2); m_replaceLineEdit = new QLineEdit(page); m_replaceLabel = new QLabel(i18n("&Replace with:"), page); m_replaceLabel->setBuddy(m_replaceLineEdit); row2->addWidget(m_replaceLabel, 0); row2->addWidget(m_replaceLineEdit, 1); // Set focus m_edit->setFocus(); updateNavButtons(); connect(m_dialog, SIGNAL(accepted()), this, SLOT(process())); connect(m_dialog, SIGNAL(rejected()), this, SLOT(dialogClosed())); } Thesaurus::~Thesaurus() { KConfigGroup cfg = KSharedConfig::openConfig()->group(""); cfg.writeEntry("datafile", m_dataFile); // FIXME?: this hopefully fixes the problem of a wrong cursor // and a crash (when closing e.g. konqueror) when the thesaurus dialog // gets close while it was still working and showing the wait cursor // QApplication::restoreOverrideCursor(); delete m_thesProc; delete m_wnProc; delete m_dialog; } void Thesaurus::finishedWord(QTextDocument *document, int cursorPosition) { Q_UNUSED(document); Q_UNUSED(cursorPosition); } void Thesaurus::finishedParagraph(QTextDocument *document, int cursorPosition) { Q_UNUSED(document); Q_UNUSED(cursorPosition); } void Thesaurus::startingSimpleEdit(QTextDocument *document, int cursorPosition) { Q_UNUSED(document); Q_UNUSED(cursorPosition); } void Thesaurus::checkSection(QTextDocument *document, int startPosition, int endPosition) { if (endPosition == -1 && startPosition == -1) { // standalone m_standAlone = true; if (document) m_word = document->toPlainText(); m_dialog->showButton(KoDialog::Ok, false); - m_dialog->setButtonGuiItem(KoDialog::Cancel, - KGuiItem(i18nc("@action:button Close thesaurus dialog", "&Close"), koIconName("dialog-cancel"))); + m_dialog->setButtonGuiItem(KoDialog::Cancel, KStandardGuiItem::close()); m_replaceLineEdit->setEnabled(false); m_replaceLabel->setEnabled(false); } else { // called from an application, e.g. Words QTextCursor cursor(document); cursor.setPosition(startPosition); cursor.setPosition(endPosition, QTextCursor::KeepAnchor); m_word = cursor.selectedText(); m_document = document; m_startPosition = startPosition; m_dialog->setButtonGuiItem(KoDialog::Ok, KGuiItem(i18n("&Replace"), koIconName("dialog-ok"))); slotFindTerm(m_word.trimmed()); m_replaceLineEdit->setText(m_word.trimmed()); } m_dialog->show(); } void Thesaurus::process() { QString replacement = m_replaceLineEdit->text().trimmed(); if (replacement == m_word.trimmed()) return; emit startMacro(i18n("Replace Word")); QTextCursor cursor(m_document); cursor.setPosition(m_startPosition); cursor.setPosition(m_startPosition + m_word.trimmed().length(), QTextCursor::KeepAnchor); cursor.insertText(replacement); emit stopMacro(); } void Thesaurus::dialogClosed() { if (!m_standAlone) return; KConfigGroup cfg = KSharedConfig::openConfig()->group(""); cfg.writeEntry("datafile", m_dataFile); } void Thesaurus::slotChangeLanguage() { QString filename = QFileDialog::getOpenFileName(0, QString(), KoResourcePaths::findResource("data", "calligra/thesaurus/thesaurus.txt")); if (!filename.isNull()) { m_dataFile = filename; setCaption(); } } void Thesaurus::setCaption() { QFileInfo info(m_dataFile); m_dialog->setCaption(i18n("Related Words - %1" , info.fileName() ) ); } // Enable or disable back and forward button void Thesaurus::updateNavButtons() { if (m_historyPos <= 1) // 1 = first position m_back->setEnabled(false); else m_back->setEnabled(true); if (m_historyPos >= m_edit->count()) m_forward->setEnabled(false); else m_forward->setEnabled(true); } // Go to an item from the editbale combo box. void Thesaurus::slotGotoHistory(int index) { m_historyPos = m_edit->count() - index; slotFindTerm(m_edit->itemText(index), false); } // Triggered when the back button is clicked. void Thesaurus::slotBack() { m_historyPos--; int pos = m_edit->count() - m_historyPos; m_edit->setCurrentIndex(pos); slotFindTerm(m_edit->itemText(pos), false); } // Triggered when the forward button is clicked. void Thesaurus::slotForward() { m_historyPos++; int pos = m_edit->count() - m_historyPos; m_edit->setCurrentIndex(pos); slotFindTerm(m_edit->itemText(pos), false); } // Triggered when a word is selected in the list box. void Thesaurus::slotSetReplaceTermSyn(QListWidgetItem *item) { m_hyperListWidget->clearSelection(); m_hypoListWidget->clearSelection(); if (!item) return; m_replaceLineEdit->setText(item->text()); } void Thesaurus::slotSetReplaceTermHyper(QListWidgetItem *item) { m_synListWidget->clearSelection(); m_hypoListWidget->clearSelection(); if (!item) return; m_replaceLineEdit->setText(item->text()); } void Thesaurus::slotSetReplaceTermHypo(QListWidgetItem *item) { m_synListWidget->clearSelection(); m_hyperListWidget->clearSelection(); if (!item) return; m_replaceLineEdit->setText(item->text()); } // Triggered when Return is pressed. void Thesaurus::slotFindTerm() { slotFindTerm(m_edit->currentText()); } // Triggered when a list item is qreal-clicked. void Thesaurus::slotFindTermFromList(QListWidgetItem *item) { slotFindTerm(item->text()); } // Triggered when a url is clicked void Thesaurus::slotFindTermFromUrl(const QUrl &url) { slotFindTerm(url.toString()); m_replaceLineEdit->setText(url.toString()); } // Triggered when a word is clicked void Thesaurus::slotFindTerm(const QString &term, bool addToHistory) { // slotSetReplaceTerm(term); if (term.startsWith(QLatin1String("http://"))) { (void) new KRun(QUrl::fromUserInput(term),0L); } else { if (addToHistory && m_edit->itemText(0) != term) { m_edit->insertItem(0, term); m_historyPos = m_edit->count(); m_edit->setCurrentIndex(0); } updateNavButtons(); findTermThesaurus(term); findTermWordnet(term); } } // // Thesaurus // void Thesaurus::findTermThesaurus(const QString &searchTerm) { if (!QFile::exists(m_dataFile)) { KMessageBox::error(0, i18n("The thesaurus file '%1' was not found. " "Please use 'Change Language...' to select a thesaurus file.", m_dataFile)); return; } // Find only whole words. Looks clumsy, but this way we don't have to rely on // features that might only be in certain versions of grep: QString searchTermTmp = ';' + searchTerm.trimmed() + ';'; m_thesProc->setOutputChannelMode(KProcess::SeparateChannels); m_thesProc->clearProgram(); m_thesProc->setReadChannel(QProcess::StandardOutput); *m_thesProc << "grep" << "-i" << searchTermTmp; *m_thesProc << m_dataFile; QStringList syn; QStringList hyper; QStringList hypo; m_thesProc->start(); if (!m_thesProc->waitForFinished()) { KMessageBox::error(0, i18n("Error: Failed to execute grep.")); return; } if (!m_thesProc->waitForReadyRead()) { } QByteArray byteArray = m_thesProc->readAllStandardOutput(); QString stdoutString(byteArray); const QStringList lines = stdoutString.split(QChar('\n')); foreach(const QString& line, lines) { if (line.startsWith(QLatin1String(" "))) { // ignore license (two spaces) continue; } int sep_pos = line.indexOf('#'); QString synPart = line.left(sep_pos); QString hyperPart = line.right(line.length() - sep_pos - 1); const QStringList synTmp = synPart.split(QChar(';')); const QStringList hyperTmp = hyperPart.split(QChar(';')); if (synTmp.filter(searchTerm, Qt::CaseInsensitive).size() > 0) { // match on the left side of the '#' -- synonyms foreach(const QString& term, synTmp) { // add if it's not the searchTerm itself and if it's not yet in the list if (term.toLower() != searchTerm.toLower() && syn.contains(term) == 0 && !term.isEmpty()) { syn.append(term); } } foreach(const QString& term, hyperTmp) { if (term.toLower() != searchTerm.toLower() && hyper.contains(term) == 0 && !term.isEmpty()) { hyper.append(term); } } } if (hyperTmp.filter(searchTerm, Qt::CaseInsensitive).size() > 0) { // match on the right side of the '#' -- hypernyms foreach(const QString& term, synTmp) { if (term.toLower() != searchTerm && hypo.contains(term) == 0 && !term.isEmpty()) { hypo.append(term); } } } } m_synListWidget->clear(); if (syn.size() > 0) { syn.sort(); m_synListWidget->addItems(syn); m_synListWidget->setEnabled(true); } else { m_synListWidget->addItem(m_noMatch); m_synListWidget->setEnabled(false); } m_hyperListWidget->clear(); if (hyper.size() > 0) { hyper.sort(); m_hyperListWidget->addItems(hyper); m_hyperListWidget->setEnabled(true); } else { m_hyperListWidget->addItem(m_noMatch); m_hyperListWidget->setEnabled(false); } m_hypoListWidget->clear(); if (hypo.size() > 0) { hypo.sort(); m_hypoListWidget->addItems(hypo); m_hypoListWidget->setEnabled(true); } else { m_hypoListWidget->addItem(m_noMatch); m_hypoListWidget->setEnabled(false); } } // // WordNet // void Thesaurus::findTermWordnet(const QString &term) { m_wnProc->setOutputChannelMode(KProcess::SeparateChannels); m_wnProc->clearProgram(); *m_wnProc << "wn"; *m_wnProc << term; // get all results: nouns, verbs, adjectives, adverbs (see below for order): if (m_wnComboBox->currentIndex() == -1 || m_wnComboBox->currentIndex() == 0) { *m_wnProc << "-synsn" << "-synsv" << "-synsa" << "-synsr"; m_mode = other; } else if (m_wnComboBox->currentIndex() == 1) { *m_wnProc << "-simsv"; m_mode = other; } else if (m_wnComboBox->currentIndex() == 2) { *m_wnProc << "-antsn" << "-antsv" << "-antsa" << "-antsr"; m_mode = other; } else if (m_wnComboBox->currentIndex() == 3) { *m_wnProc << "-hypon" << "-hypov"; m_mode = other; } else if (m_wnComboBox->currentIndex() == 4) { *m_wnProc << "-meron"; m_mode = other; } else if (m_wnComboBox->currentIndex() == 5) { *m_wnProc << "-holon"; m_mode = other; } else if (m_wnComboBox->currentIndex() == 6) { // e.g. "size -> large/small" *m_wnProc << "-attrn" << "-attra"; m_mode = other; } else if (m_wnComboBox->currentIndex() == 7) { // e.g. "kill -> die" *m_wnProc << "-causv"; m_mode = other; } else if (m_wnComboBox->currentIndex() == 8) { // e.g. "walk -> step" *m_wnProc << "-entav"; m_mode = other; } else if (m_wnComboBox->currentIndex() == 9) { *m_wnProc << "-famln" << "-famlv" << "-famla" << "-famlr"; m_mode = other; } else if (m_wnComboBox->currentIndex() == 10) { *m_wnProc << "-framv"; m_mode = other; } else if (m_wnComboBox->currentIndex() == 11) { *m_wnProc << "-grepn" << "-grepv" << "-grepa" << "-grepr"; m_mode = grep; } else if (m_wnComboBox->currentIndex() == 12) { *m_wnProc << "-over"; m_mode = other; } *m_wnProc << "-g"; // "Display gloss" int current = m_wnComboBox->currentIndex() != -1 ? m_wnComboBox->currentIndex() : 0; // remember current position m_wnComboBox->clear(); // warning: order matters! // 0: m_wnComboBox->insertItem(-1, i18n("Synonyms/Hypernyms - Ordered by Frequency")); m_wnComboBox->insertItem(-1, i18n("Synonyms - Ordered by Similarity of Meaning (verbs only)")); m_wnComboBox->insertItem(-1, i18n("Antonyms - Words with Opposite Meanings")); m_wnComboBox->insertItem(-1, i18n("Hyponyms - ... is a (kind of) %1", m_edit->currentText())); m_wnComboBox->insertItem(-1, i18n("Meronyms - %1 has a ...", m_edit->currentText())); // 5: m_wnComboBox->insertItem(-1, i18n("Holonyms - ... has a %1", m_edit->currentText())); m_wnComboBox->insertItem(-1, i18n("Attributes")); m_wnComboBox->insertItem(-1, i18n("Cause To (for some verbs only)")); m_wnComboBox->insertItem(-1, i18n("Verb Entailment (for some verbs only)")); m_wnComboBox->insertItem(-1, i18n("Familiarity & Polysemy Count")); // 10: m_wnComboBox->insertItem(-1, i18n("Verb Frames (examples of use)")); m_wnComboBox->insertItem(-1, i18n("List of Compound Words")); m_wnComboBox->insertItem(-1, i18n("Overview of Senses")); /** NOT todo: * -Hypernym tree: layout is difficult, you can get the same information * by following links * -Coordinate terms (sisters): just go to synset and then use hyponyms * -Has Part Meronyms, Has Substance Meronyms, Has Member Meronyms, * Member of Holonyms, Substance of Holonyms, Part of Holonyms: * these are just subsets of Meronyms/Holonyms * -hmern, hholn: these are just compact versions, you can get the * same information by following some links */ /** TODO?: * -pert (e.g. nuclear -> nuclues, but "=>" are nested, difficult to display) * -nomn(n|v), e.g. deny -> denial, but this doesn't seem to work? */ m_wnComboBox->setCurrentIndex(current); // reset previous position if (m_wnProc->state() == QProcess::Running) { // should never happen debugThesaurus <<"Warning: findTerm(): process is already running?!"; return; } m_wnProc->start(); if (!m_wnProc->waitForFinished()) { m_resultTextBrowser->setHtml(i18n("Error: Failed to execute WordNet program 'wn'. " "WordNet has to be installed on your computer if you want to use it, " "and 'wn' has to be in your PATH. " "You can get WordNet at " "http://wordnet.princeton.edu/. Note that WordNet only supports " "the English language.")); m_wnComboBox->setEnabled(false); return; } QString stdoutString = m_wnProc->readAllStandardOutput(); if (stdoutString.isEmpty()) { m_resultTextBrowser->setHtml(i18n("No match for '%1'.", m_edit->currentText())); } else { // render in a table, each line one row: const QStringList lines = stdoutString.split(QChar('\n'), QString::SkipEmptyParts); QString result = "\n"; // TODO in Qt > 3.01: try without the following line (it's necessary to ensure the // first column is really always quite small): result += "\n"; uint ct = 0; for (QStringList::ConstIterator it = lines.begin(); it != lines.end(); ++it) { QString l = (*it); // Remove some lines: QRegExp re("^\\d+( of \\d+)? senses? of \\w+"); if (re.indexIn(l) != -1) { continue; } // Escape XML: l.replace('&', "&"); l.replace('<', "<"); l.replace('>', ">"); // TODO?: // move "=>" in own column? l = formatLine(l); // Table layout: result += ""; if (l.startsWith(' ')) { result += "\t"; l = l.trimmed(); result += "\t"; } else { l = l.trimmed(); result += ""; } result += "\n"; ct++; } result += "\n
" + l + "" + l + "
\n"; m_resultTextBrowser->setHtml(result); //debugThesaurus << result; } } // // Tools // // Format lines using Qt's simple richtext. QString Thesaurus::formatLine(const QString &line) const { QString l = line; if (l == "--------------") return QString("
"); QRegExp re; re.setPattern("^(\\d+\\.)(.*)$"); if (re.indexIn(l) != -1) { l = "" +re.cap(1)+ "" +re.cap(2); return l; } re.setPattern("^.* of (noun|verb|adj|adv) .*"); if (re.indexIn(l) != -1) { l = "" +re.cap()+ "\n\n"; return l; } if (m_mode == grep) { l = l.trimmed(); return QString("" +l+ ""); } re.setPattern("^(Sense \\d+)"); if (re.indexIn(l) != -1) { l = "" +re.cap()+ "\n"; return l; } re.setPattern("(.*)(Also See->)(.*)"); // Example: first sense of verb "keep" if (re.indexIn(l) != -1) { l = re.cap(1); l += re.cap(2); const QStringList links = re.cap(3).split(QChar(';'), QString::SkipEmptyParts); for (QStringList::ConstIterator it = links.begin(); it != links.end(); ++it) { QString link = (*it); if (it != links.begin()) { l += ", "; } link = link.trimmed(); link.remove(QRegExp("#\\d+")); l += "" + link + ""; } l.prepend (' '); // indent in table } re.setPattern("(.*)(=>|HAS \\w+:|PART OF:)(.*) --"); re.setMinimal(true); // non-greedy if (re.indexIn(l) != -1) { int dash_pos = l.indexOf("--"); QString line_end = l.mid(dash_pos+2, l.length()-dash_pos); l = re.cap(1); l += re.cap(2) + ' '; const QStringList links = re.cap(3).split(QChar(','), QString::SkipEmptyParts); for (QStringList::ConstIterator it = links.begin(); it != links.end(); ++it) { QString link = (*it); if (it != links.begin()) { l += ", "; } link = link.trimmed(); l += "" +link+ ""; } l += "" +line_end+ ""; l.prepend(' '); // indent in table return l; } re.setMinimal(false); // greedy again return l; } diff --git a/sheets/part/View.cpp b/sheets/part/View.cpp index 72fc26685b8..b85064bced9 100644 --- a/sheets/part/View.cpp +++ b/sheets/part/View.cpp @@ -1,2187 +1,2188 @@ /* This file is part of the KDE project Copyright 2006-2007 Stefan Nikolaus Copyright 2005-2006 Raphael Langerhorst Copyright 2002-2005 Ariya Hidayat Copyright 1999-2003 Laurent Montel Copyright 2002-2003 Norbert Andres Copyright 2002-2003 Philipp Mueller Copyright 2002-2003 John Dailey Copyright 1999-2003 David Faure Copyright 1999-2001 Simon Hausmann Copyright 1998-2000 Torben Weis Copyright 2010 Boudewijn Rempt 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. */ // Local #include "View.h" #include "TabBar.h" // standard C/C++ includes #include #include #include // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef QT_NO_SQL #include #endif #include #include #include #include #include // KF5 includes #include #include #include #include #include #include +#include // Calligra includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Sheets includes #include "SheetsDebug.h" #include "ApplicationSettings.h" #include "BindingManager.h" #include "CalculationSettings.h" #include "CanvasResources.h" #include "CellStorage.h" #include "Damages.h" #include "DependencyManager.h" #include "Doc.h" #include "Factory.h" #include "HeaderFooter.h" #include "LoadingInfo.h" #include "Canvas.h" #include "Global.h" #include "Headers.h" #include "HeaderWidgets.h" #include "Localization.h" #include "Map.h" #include "NamedAreaManager.h" #include "PrintSettings.h" #include "RecalcManager.h" #include "RowColumnFormat.h" #include "ShapeApplicationData.h" #include "Sheet.h" #include "SheetPrint.h" #include "Style.h" #include "StyleManager.h" #include "StyleStorage.h" #include "ToolRegistry.h" #include "Util.h" #include "ValueCalc.h" #include "ValueConverter.h" #include "PrintJob.h" #include "ElapsedTime_p.h" // commands #include "commands/CopyCommand.h" #include "commands/DefinePrintRangeCommand.h" #include "commands/SheetCommands.h" // dialogs #include "dialogs/PageLayoutDialog.h" #include "dialogs/PreferenceDialog.h" #include "dialogs/ShowDialog.h" #include "dialogs/SheetPropertiesDialog.h" // ui #include "ui/CellView.h" #include "ui/MapViewModel.h" #include "ui/RightToLeftPaintingStrategy.h" #include "ui/Selection.h" #include "ui/SheetView.h" #include "ui/PixmapCachingSheetView.h" // D-Bus #ifndef QT_NO_DBUS #include "interfaces/ViewAdaptor.h" #include #endif using namespace Calligra::Sheets; class ViewActions; class Q_DECL_HIDDEN View::Private { public: View* view; Doc* doc; // the active sheet, may be 0 // this is the sheet which has the input focus Sheet* activeSheet; MapViewModel* mapViewModel; QHash > sheetViews; // GUI elements QWidget *frame; Canvas *canvas; KoCanvasController* canvasController; KoZoomController* zoomController; KoZoomHandler* zoomHandler; RowHeaderWidget *rowHeader; ColumnHeaderWidget *columnHeader; SelectAllButtonWidget* selectAllButton; QScrollBar *horzScrollBar; QScrollBar *vertScrollBar; TabBar *tabBar; QLabel* calcLabel; QGridLayout* viewLayout; QGridLayout* tabScrollBarLayout; // all UI actions ViewActions* actions; // if true, Calligra Sheets is still loading the document // don't try to refresh the view bool loading; // selection/marker Selection* selection; QMap savedAnchors; QMap savedMarkers; QMap savedOffsets; void initActions(); void adjustActions(bool mode); // On timeout this will execute the status bar operation (e.g. SUM). // This is delayed to speed up the selection. QTimer statusBarOpTimer; QTimer *scrollTimer; }; class ViewActions { public: // sheet/workbook operations QAction * sheetProperties; QAction * insertSheet; QAction * duplicateSheet; QAction * deleteSheet; QAction * renameSheet; QAction * hideSheet; QAction * showSheet; //Shape manipulation QAction * deleteShape; // page layout QAction * paperLayout; QAction * resetPrintRange; KToggleAction* showPageOutline; // recalculation QAction * recalcWorksheet; QAction * recalcWorkbook; // protection KToggleAction* protectSheet; KToggleAction* protectDoc; // navigation QAction * nextSheet; QAction * prevSheet; QAction * firstSheet; QAction * lastSheet; // misc QAction * createTemplate; KSelectAction* shapeAnchor; // settings KToggleAction* showColumnHeader; KToggleAction* showRowHeader; KToggleAction* showHorizontalScrollBar; KToggleAction* showVerticalScrollBar; KToggleAction* showStatusBar; KToggleAction* showTabBar; QAction * preference; // running calculation KToggleAction* calcNone; KToggleAction* calcMin; KToggleAction* calcMax; KToggleAction* calcAverage; KToggleAction* calcCount; KToggleAction* calcSum; KToggleAction* calcCountA; }; void View::Private::initActions() { actions = new ViewActions; KActionCollection* ac = view->actionCollection(); // -- sheet/workbook actions -- actions->sheetProperties = new QAction(i18n("Sheet Properties..."), view); ac->addAction("sheetProperties", actions->sheetProperties); connect(actions->sheetProperties, SIGNAL(triggered(bool)), view, SLOT(sheetProperties())); actions->sheetProperties->setToolTip(i18n("Modify current sheet's properties")); actions->insertSheet = new QAction(koIcon("insert-table"), i18n("Sheet"), view); actions->insertSheet->setIconText(i18n("Insert Sheet")); actions->insertSheet->setToolTip(i18n("Insert a new sheet")); ac->addAction("insertSheet", actions->insertSheet); connect(actions->insertSheet, SIGNAL(triggered(bool)), view, SLOT(insertSheet())); actions->duplicateSheet = new QAction(/*koIcon("inserttable"),*/ i18n("Duplicate Sheet"), view); actions->duplicateSheet->setToolTip(i18n("Duplicate the selected sheet")); ac->addAction("duplicateSheet", actions->duplicateSheet); connect(actions->duplicateSheet, SIGNAL(triggered(bool)), view, SLOT(duplicateSheet())); actions->deleteSheet = new QAction(koIcon("edit-delete"), i18n("Sheet"), view); actions->deleteSheet->setIconText(i18n("Remove Sheet")); actions->deleteSheet->setToolTip(i18n("Remove the active sheet")); ac->addAction("deleteSheet", actions->deleteSheet); connect(actions->deleteSheet, SIGNAL(triggered(bool)), view, SLOT(deleteSheet())); actions->renameSheet = new QAction(i18n("Rename Sheet..."), view); ac->addAction("renameSheet", actions->renameSheet); connect(actions->renameSheet, SIGNAL(triggered(bool)), view, SLOT(slotRename())); actions->renameSheet->setToolTip(i18n("Rename the active sheet")); actions->showSheet = new QAction(i18n("Show Sheet..."), view); ac->addAction("showSheet", actions->showSheet); connect(actions->showSheet, SIGNAL(triggered(bool)), view, SLOT(showSheet())); actions->showSheet->setToolTip(i18n("Show a hidden sheet")); actions->hideSheet = new QAction(i18n("Hide Sheet"), view); ac->addAction("hideSheet", actions->hideSheet); connect(actions->hideSheet, SIGNAL(triggered(bool)), view, SLOT(hideSheet())); actions->hideSheet->setToolTip(i18n("Hide the active sheet")); actions->paperLayout = new QAction(i18n("Page Layout..."), view); ac->addAction("paperLayout", actions->paperLayout); connect(actions->paperLayout, SIGNAL(triggered(bool)), view, SLOT(paperLayoutDlg())); actions->paperLayout->setToolTip(i18n("Specify the layout of the spreadsheet for a printout")); actions->resetPrintRange = new QAction(i18n("Reset Print Range"), view); ac->addAction("resetPrintRange", actions->resetPrintRange); connect(actions->resetPrintRange, SIGNAL(triggered(bool)), view, SLOT(resetPrintRange())); actions->resetPrintRange->setToolTip(i18n("Reset the print range in the current sheet")); actions->showPageOutline = new KToggleAction(i18n("Page Outline"), view); actions->showPageOutline->setToolTip(i18n("Show on the spreadsheet where the page boundary will be")); ac->addAction("showPageOutline", actions->showPageOutline); connect(actions->showPageOutline, SIGNAL(toggled(bool)), view, SLOT(togglePageOutline(bool))); actions->recalcWorksheet = new QAction(i18n("Recalculate Sheet"), view); actions->recalcWorksheet->setIcon(koIcon("view-refresh")); actions->recalcWorksheet->setIconText(i18n("Recalculate")); ac->addAction("RecalcWorkSheet", actions->recalcWorksheet); actions->recalcWorksheet->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_F9)); connect(actions->recalcWorksheet, SIGNAL(triggered(bool)), view, SLOT(recalcWorkSheet())); actions->recalcWorksheet->setToolTip(i18n("Recalculate the value of every cell in the current worksheet")); actions->recalcWorkbook = new QAction(i18n("Recalculate Document"), view); actions->recalcWorkbook->setIcon(koIcon("view-refresh")); actions->recalcWorkbook->setIconText(i18n("Recalculate")); ac->addAction("RecalcWorkBook", actions->recalcWorkbook); actions->recalcWorkbook->setShortcut(QKeySequence(Qt::Key_F9)); connect(actions->recalcWorkbook, SIGNAL(triggered(bool)), view, SLOT(recalcWorkBook())); actions->recalcWorkbook->setToolTip(i18n("Recalculate the value of every cell in all worksheets")); actions->protectSheet = new KToggleAction(i18n("Protect &Sheet..."), view); ac->addAction("protectSheet", actions->protectSheet); actions->protectSheet->setToolTip(i18n("Protect the sheet from being modified")); connect(actions->protectSheet, SIGNAL(triggered(bool)), view, SLOT(toggleProtectSheet(bool))); actions->protectDoc = new KToggleAction(i18n("Protect &Document..."), view); ac->addAction("protectDoc", actions->protectDoc); actions->protectDoc->setToolTip(i18n("Protect the document from being modified")); connect(actions->protectDoc, SIGNAL(triggered(bool)), view, SLOT(toggleProtectDoc(bool))); // -- misc actions -- actions->createTemplate = new QAction(i18n("&Create Template From Document..."), view); ac->addAction("createTemplate", actions->createTemplate); connect(actions->createTemplate, SIGNAL(triggered(bool)), view, SLOT(createTemplate())); actions->shapeAnchor = new KSelectAction(i18n("Anchor"), view); actions->shapeAnchor->addAction(i18n("Cell")); actions->shapeAnchor->addAction(i18n("Page")); actions->shapeAnchor->setEnabled(false); actions->shapeAnchor->setToolTip(i18n("Switch shape anchoring")); ac->addAction("shapeAnchor", actions->shapeAnchor); connect(actions->shapeAnchor, SIGNAL(triggered(QString)), view, SLOT(setShapeAnchoring(QString))); // -- navigation actions -- actions->nextSheet = new QAction(koIcon("go-next"), i18n("Next Sheet"), view); actions->nextSheet->setIconText(i18n("Next")); actions->nextSheet->setToolTip(i18n("Move to the next sheet")); ac->addAction("go_next", actions->nextSheet); actions->nextSheet->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_PageDown)); connect(actions->nextSheet, SIGNAL(triggered(bool)), view, SLOT(nextSheet())); actions->prevSheet = new QAction(koIcon("go-previous"), i18n("Previous Sheet"), view); actions->prevSheet->setIconText(i18n("Previous")); actions->prevSheet->setToolTip(i18n("Move to the previous sheet")); ac->addAction("go_previous", actions->prevSheet); actions->prevSheet->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_PageUp)); connect(actions->prevSheet, SIGNAL(triggered(bool)), view, SLOT(previousSheet())); actions->firstSheet = new QAction(koIcon("go-first"), i18n("First Sheet"), view); actions->firstSheet->setIconText(i18n("First")); actions->firstSheet->setToolTip(i18n("Move to the first sheet")); ac->addAction("go_first", actions->firstSheet); connect(actions->firstSheet, SIGNAL(triggered(bool)), view, SLOT(firstSheet())); actions->lastSheet = new QAction(koIcon("go-last"), i18n("Last Sheet"), view); actions->lastSheet->setIconText(i18nc("Move to the last sheet", "Last")); actions->lastSheet->setToolTip(i18n("Move to the last sheet")); ac->addAction("go_last", actions->lastSheet); connect(actions->lastSheet, SIGNAL(triggered(bool)), view, SLOT(lastSheet())); // -- settings actions -- actions->showColumnHeader = new KToggleAction(i18n("Column Header"), view); actions->showColumnHeader->setToolTip(i18n("Show the column header")); ac->addAction("showColumnHeader", actions->showColumnHeader); connect(actions->showColumnHeader, SIGNAL(toggled(bool)), view, SLOT(showColumnHeader(bool))); actions->showRowHeader = new KToggleAction(i18n("Row Header"), view); actions->showRowHeader->setToolTip(i18n("Show the row header")); ac->addAction("showRowHeader", actions->showRowHeader); connect(actions->showRowHeader, SIGNAL(toggled(bool)), view, SLOT(showRowHeader(bool))); actions->showHorizontalScrollBar = new KToggleAction(i18n("Horizontal Scrollbar"), view); actions->showHorizontalScrollBar->setToolTip(i18n("Show the horizontal scrollbar")); ac->addAction("showHorizontalScrollBar", actions->showHorizontalScrollBar); connect(actions->showHorizontalScrollBar, SIGNAL(toggled(bool)), view, SLOT(showHorizontalScrollBar(bool))); actions->showVerticalScrollBar = new KToggleAction(i18n("Vertical Scrollbar"), view); actions->showVerticalScrollBar->setToolTip(i18n("Show the vertical scrollbar")); ac->addAction("showVerticalScrollBar", actions->showVerticalScrollBar); connect(actions->showVerticalScrollBar, SIGNAL(toggled(bool)), view, SLOT(showVerticalScrollBar(bool))); actions->showStatusBar = new KToggleAction(i18n("Status Bar"), view); actions->showStatusBar->setToolTip(i18n("Show the status bar")); ac->addAction("showStatusBar", actions->showStatusBar); connect(actions->showStatusBar, SIGNAL(toggled(bool)), view, SLOT(showStatusBar(bool))); actions->showTabBar = new KToggleAction(i18n("Tab Bar"), view); actions->showTabBar->setToolTip(i18n("Show the tab bar")); ac->addAction("showTabBar", actions->showTabBar); connect(actions->showTabBar, SIGNAL(toggled(bool)), view, SLOT(showTabBar(bool))); actions->preference = KStandardAction::preferences(view, SLOT(preference()), view); actions->preference->setToolTip(i18n("Set various Calligra Sheets options")); ac->addAction("preference", actions->preference); QAction *notifyAction = KStandardAction::configureNotifications(view, SLOT(optionsNotifications()), view); ac->addAction("configureNotifications", notifyAction); // -- calculation actions -- // QActionGroup* groupCalc = new QActionGroup(view); actions->calcNone = new KToggleAction(i18n("None"), view); ac->addAction("menu_none", actions->calcNone); connect(actions->calcNone, SIGNAL(toggled(bool)), view, SLOT(menuCalc(bool))); actions->calcNone->setToolTip(i18n("No calculation")); actions->calcNone->setActionGroup(groupCalc); actions->calcSum = new KToggleAction(i18n("Sum"), view); ac->addAction("menu_sum", actions->calcSum); connect(actions->calcSum, SIGNAL(toggled(bool)), view, SLOT(menuCalc(bool))); actions->calcSum->setToolTip(i18n("Calculate using sum")); actions->calcSum->setActionGroup(groupCalc); actions->calcMin = new KToggleAction(i18n("Min"), view); ac->addAction("menu_min", actions->calcMin); connect(actions->calcMin, SIGNAL(toggled(bool)), view, SLOT(menuCalc(bool))); actions->calcMin->setToolTip(i18n("Calculate using minimum")); actions->calcMin->setActionGroup(groupCalc); actions->calcMax = new KToggleAction(i18n("Max"), view); ac->addAction("menu_max", actions->calcMax); connect(actions->calcMax, SIGNAL(toggled(bool)), view, SLOT(menuCalc(bool))); actions->calcMax->setToolTip(i18n("Calculate using maximum")); actions->calcMax->setActionGroup(groupCalc); actions->calcAverage = new KToggleAction(i18n("Average"), view); ac->addAction("menu_average", actions->calcAverage); connect(actions->calcAverage, SIGNAL(toggled(bool)), view, SLOT(menuCalc(bool))); actions->calcAverage->setToolTip(i18n("Calculate using average")); actions->calcAverage->setActionGroup(groupCalc); actions->calcCount = new KToggleAction(i18n("Count"), view); ac->addAction("menu_count", actions->calcCount); connect(actions->calcCount, SIGNAL(toggled(bool)), view, SLOT(menuCalc(bool))); actions->calcCount->setToolTip(i18n("Calculate using the count")); actions->calcCount->setActionGroup(groupCalc); actions->calcCountA = new KToggleAction(i18n("CountA"), view); ac->addAction("menu_counta", actions->calcCountA); connect(actions->calcCountA, SIGNAL(toggled(bool)), view, SLOT(menuCalc(bool))); actions->calcCountA->setToolTip(i18n("Calculate using the countA")); actions->calcCountA->setActionGroup(groupCalc); //Shape actions actions->deleteShape = new QAction(koIcon("edit-delete"), i18n("Delete"), view); actions->deleteShape->setShortcut(QKeySequence("Del")); connect(actions->deleteShape, SIGNAL(triggered()), view, SLOT(editDeleteSelection())); connect(canvas->toolProxy(), SIGNAL(selectionChanged(bool)), actions->deleteShape, SLOT(setEnabled(bool))); ac->addAction("edit_delete", actions->deleteShape); // -- special action, only for developers -- // ac->addAssociatedWidget(view->canvasWidget()); foreach(QAction* action, ac->actions()) { action->setShortcutContext(Qt::WidgetWithChildrenShortcut); } } void View::Private::adjustActions(bool mode) { actions->recalcWorkbook->setEnabled(mode); actions->recalcWorksheet->setEnabled(mode); actions->paperLayout->setEnabled(mode); actions->resetPrintRange->setEnabled(mode); actions->deleteSheet->setEnabled(mode); actions->calcMin->setEnabled(mode); actions->calcMax->setEnabled(mode); actions->calcAverage->setEnabled(mode); actions->calcCount->setEnabled(mode); actions->calcCountA->setEnabled(mode); actions->calcSum->setEnabled(mode); actions->calcNone->setEnabled(mode); if (mode && !view->doc()->map()->isProtected()) actions->renameSheet->setEnabled(true); else actions->renameSheet->setEnabled(false); actions->showColumnHeader->setChecked(view->doc()->map()->settings()->showColumnHeader()); actions->showRowHeader->setChecked(view->doc()->map()->settings()->showRowHeader()); actions->showHorizontalScrollBar->setChecked(view->doc()->map()->settings()->showHorizontalScrollBar()); actions->showVerticalScrollBar->setChecked(view->doc()->map()->settings()->showVerticalScrollBar()); actions->showStatusBar->setChecked(view->doc()->map()->settings()->showStatusBar()); actions->showTabBar->setChecked(view->doc()->map()->settings()->showTabBar()); if (activeSheet) selection->update(); } /***************************************************************************** * * View * *****************************************************************************/ View::View(KoPart *part, QWidget *_parent, Doc *_doc) : KoView(part, _doc, _parent) , d(new Private) { ElapsedTime et("View constructor"); d->view = this; d->doc = _doc; d->activeSheet = 0; d->loading = true; setComponentName(Factory::global().componentName(), Factory::global().componentDisplayName()); setXMLFile("calligrasheets.rc"); // GUI Initializations initView(); d->initActions(); const QList pluginFactories = KoPluginLoader::instantiatePluginFactories(QStringLiteral("calligrasheets/extensions")); foreach (KPluginFactory* factory, pluginFactories) { QObject *object = factory->create(this, QVariantList()); KXMLGUIClient *clientPlugin = dynamic_cast(object); if (clientPlugin) { insertChildClient(clientPlugin); } else { // not our/valid plugin, so delete the created object object->deleteLater(); } } // Connect updateView() signal to View::update() in order to repaint its // child widgets: the column/row headers and the select all button. // Connect to Canvas::update() explicitly as it lives in the viewport // of the KoCanvasController. connect(doc(), SIGNAL(updateView()), this, SLOT(update())); connect(doc(), SIGNAL(updateView()), d->canvas, SLOT(update())); connect(doc()->map(), SIGNAL(sheetAdded(Sheet*)), this, SLOT(addSheet(Sheet*))); connect(doc()->map(), SIGNAL(sheetRemoved(Sheet*)), this, SLOT(removeSheet(Sheet*))); connect(doc()->map(), SIGNAL(sheetRevived(Sheet*)), this, SLOT(addSheet(Sheet*))); connect(doc()->map(), SIGNAL(damagesFlushed(QList)), this, SLOT(handleDamages(QList))); if (statusBar()) { connect(doc()->map(), SIGNAL(statusMessage(QString,int)), statusBar(), SLOT(showMessage(QString,int))); } connect(&d->statusBarOpTimer, SIGNAL(timeout()), this, SLOT(calcStatusBarOp())); // Delay the setting of the initial position, because we need to have // a sensible widget size, which is not always the case from the beginning // of the View's lifetime. // Therefore, initialPosition(), the last operation in the "View loading" // process, is called from resizeEvent(). The loading flag will be unset // at the end of initialPosition(). #ifndef QT_NO_DBUS new ViewAdaptor(this); #endif d->scrollTimer = new QTimer(this); connect(d->scrollTimer, SIGNAL(timeout()), this, SLOT(slotAutoScroll())); initialPosition(); d->canvas->setFocus(); } View::~View() { selection()->emitCloseEditor(true); // save changes // if (d->calcLabel) disconnect(d->calcLabel,SIGNAL(pressed(int)),this,SLOT(statusBarClicked(int))); d->selection->emitCloseEditor(false); d->selection->endReferenceSelection(false); d->activeSheet = 0; // set the active sheet to 0 so that when during destruction // of embedded child documents possible repaints in Sheet are not // performed. // delete the sheetView's after calling d->selection->emitCloseEditor cause the // emitCloseEditor may trigger over the Selection::emitChanged a Canvas::scrollToCell // which in turn needs the sheetview's to access the sheet itself. qDeleteAll(d->sheetViews.values()); delete d->scrollTimer; delete d->selection; d->selection = 0; delete d->calcLabel; delete d->actions; delete d->zoomHandler; // NOTE sebsauer: first unregister the event-handler, then delete the canvas and then we are save to // call removeCanvasController without crashing. //d->canvasController->canvas()->canvasWidget()->removeEventFilter(d->canvasController); //delete d->canvasController->canvas(); // NOTE sebsauer: We need to remove the canvasController right before deleting it and // nothing needs to be done in between cause flake does first delete the TableTool // on removeCanvasController and the proxytool which points to that TableTool later // while the canvasController is destroyed. That means, that we will have a dangling // pointer in the KoToolProxy that points to the KoToolBase the time in between. KoToolManager::instance()->removeCanvasController(d->canvasController); // NOTE Stefan: Delete the Canvas explicitly, even if it has this view as // parent. Otherwise, it leads to crashes, because it tries to // access this View in some events (Bug #126492). // The KoCanvasController takes ownership of the Canvas and does the deletion. delete d->canvasController; delete d; } Doc* View::doc() const { return d->doc; } // should be called only once, from the constructor /* * Central part is the canvas, row header and vertical scrollbar. * Bottom part is the tab bar and horizontal scrollbar. * * Note that canvas must the one to be created, since other * widgets might depend on it. */ void View::initView() { d->viewLayout = new QGridLayout(this); d->viewLayout->setMargin(0); d->viewLayout->setSpacing(0); // Setup the Canvas and its controller. d->canvas = new Canvas(this); KoCanvasControllerWidget *canvasController = new KoCanvasControllerWidget(actionCollection(), this); d->canvasController = canvasController; d->canvasController->setCanvas(d->canvas); d->canvasController->setCanvasMode(KoCanvasController::Spreadsheet); canvasController->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); canvasController->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Setup the map model. d->mapViewModel = new MapViewModel(d->doc->map(), d->canvas, this); connect(d->mapViewModel, SIGNAL(addCommandRequested(KUndo2Command*)), doc(), SLOT(addCommand(KUndo2Command*))); connect(d->mapViewModel, SIGNAL(activeSheetChanged(Sheet*)), this, SLOT(setActiveSheet(Sheet*))); // Setup the selection. d->selection = new Selection(d->canvas); connect(d->selection, SIGNAL(changed(Region)), this, SLOT(slotChangeSelection(Region))); connect(d->selection, SIGNAL(changed(Region)), this, SLOT(slotScrollChoice(Region))); connect(d->selection, SIGNAL(aboutToModify(Region)), this, SLOT(aboutToModify(Region))); connect(d->selection, SIGNAL(modified(Region)), this, SLOT(refreshSelection(Region))); connect(d->selection, SIGNAL(visibleSheetRequested(Sheet*)), this, SLOT(setActiveSheet(Sheet*))); connect(d->selection, SIGNAL(refreshSheetViews()), this, SLOT(refreshSheetViews())); connect(d->selection, SIGNAL(updateAccessedCellRange(Sheet*,QPoint)), this, SLOT(updateAccessedCellRange(Sheet*,QPoint))); connect(this, SIGNAL(documentReadWriteToggled(bool)), d->selection, SIGNAL(documentReadWriteToggled(bool))); connect(this, SIGNAL(sheetProtectionToggled(bool)), d->selection, SIGNAL(sheetProtectionToggled(bool))); // Let the selection pointer become a canvas resource. QVariant variant; variant.setValue(d->selection); d->canvas->resourceManager()->setResource(CanvasResource::Selection, variant); variant.setValue(doc()->map()->bindingManager()); // Load the Calligra Sheets Tools ToolRegistry::instance()->loadTools(); if (mainWindow()) { KoToolManager::instance()->addController(d->canvasController); KoToolManager::instance()->registerTools(actionCollection(), d->canvasController); KoModeBoxFactory modeBoxFactory(canvasController, qApp->applicationName(), i18n("Tools")); QDockWidget* modeBox = mainWindow()->createDockWidget(&modeBoxFactory); mainWindow()->dockerManager()->removeToolOptionsDocker(); dynamic_cast(modeBox)->setObservedCanvas(d->canvas); // Setup the tool options dock widget manager. //connect(canvasController, SIGNAL(toolOptionWidgetsChanged(QList >)), // mainWindow()->dockerManager(), SLOT(newOptionWidgets(QList >))); } // Setup the zoom controller. d->zoomHandler = new KoZoomHandler(); d->zoomController = new KoZoomController(d->canvasController, d->zoomHandler, actionCollection(), 0, this); d->zoomController->zoomAction()->setZoomModes(KoZoomMode::ZOOM_CONSTANT); QWidget *zoomWidget = d->zoomController->zoomAction()->createWidget(statusBar()); zoomWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); addStatusBarItem(zoomWidget, 0, true); connect(d->zoomController, SIGNAL(zoomChanged(KoZoomMode::Mode,qreal)), this, SLOT(viewZoom(KoZoomMode::Mode,qreal))); d->columnHeader = new ColumnHeaderWidget(this, d->canvas, this); d->rowHeader = new RowHeaderWidget(this, d->canvas , this); d->columnHeader->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); d->rowHeader->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); d->selectAllButton = new SelectAllButtonWidget(d->canvas); d->selectAllButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); d->canvas->setFocusPolicy(Qt::StrongFocus); QWidget::setFocusPolicy(Qt::StrongFocus); setFocusProxy(d->canvas); // Vert. Scroll Bar d->calcLabel = 0; d->vertScrollBar = new QScrollBar(this); canvasController->setVerticalScrollBar(d->vertScrollBar); connect(d->vertScrollBar, SIGNAL(valueChanged(int)), canvasController, SLOT(updateCanvasOffsetY())); d->vertScrollBar->setOrientation(Qt::Vertical); d->vertScrollBar->setSingleStep(60); //just random guess based on what feels okay d->vertScrollBar->setPageStep(60); //This should be controlled dynamically, depending on how many rows are shown QWidget* bottomPart = new QWidget(this); d->tabScrollBarLayout = new QGridLayout(bottomPart); d->tabScrollBarLayout->setMargin(0); d->tabScrollBarLayout->setSpacing(0); d->tabScrollBarLayout->setColumnStretch(1, 1); d->tabBar = new TabBar(0); d->tabScrollBarLayout->addWidget(d->tabBar, 0, 0); d->horzScrollBar = new QScrollBar(0); canvasController->setHorizontalScrollBar(d->horzScrollBar); connect(d->horzScrollBar, SIGNAL(valueChanged(int)), canvasController, SLOT(updateCanvasOffsetX())); d->tabScrollBarLayout->addWidget(d->horzScrollBar, 0, 1, 2, 1, Qt::AlignVCenter); d->horzScrollBar->setOrientation(Qt::Horizontal); d->horzScrollBar->setSingleStep(60); //just random guess based on what feels okay d->horzScrollBar->setPageStep(60); connect(d->tabBar, SIGNAL(tabChanged(QString)), this, SLOT(changeSheet(QString))); connect(d->tabBar, SIGNAL(tabMoved(unsigned,unsigned)), this, SLOT(moveSheet(unsigned,unsigned))); connect(d->tabBar, SIGNAL(contextMenu(QPoint)), this, SLOT(popupTabBarMenu(QPoint))); connect(d->tabBar, SIGNAL(doubleClicked()), this, SLOT(slotRename())); int extent = this->style()->pixelMetric(QStyle::PM_ScrollBarExtent); if (style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents)) { extent += style()->pixelMetric(QStyle::PM_DefaultFrameWidth) * 2; } d->viewLayout->setColumnStretch(1, 10); d->viewLayout->setRowStretch(2, 10); d->viewLayout->addWidget(d->selectAllButton, 1, 0); d->viewLayout->addWidget(d->columnHeader, 1, 1, 1, 1); d->viewLayout->addWidget(d->rowHeader, 2, 0); d->viewLayout->addWidget(canvasController, 2, 1); d->viewLayout->addWidget(d->vertScrollBar, 1, 2, 2, 1, Qt::AlignHCenter); d->viewLayout->addWidget(bottomPart, 3, 0, 1, 2); d->viewLayout->setColumnMinimumWidth(2, extent); d->viewLayout->setRowMinimumHeight(3, extent); QStatusBar * sb = statusBar(); d->calcLabel = sb ? new QLabel(sb) : 0; if (d->calcLabel) { d->calcLabel->setContextMenuPolicy(Qt::CustomContextMenu); addStatusBarItem(d->calcLabel, 0); connect(d->calcLabel , SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(statusBarClicked(QPoint))); } // signal slot connect(d->canvas, SIGNAL(documentSizeChanged(QSize)), d->canvasController->proxyObject, SLOT(updateDocumentSize(QSize))); connect(d->canvasController->proxyObject, SIGNAL(moveDocumentOffset(QPoint)), d->canvas, SLOT(setDocumentOffset(QPoint))); connect(d->canvas->shapeManager(), SIGNAL(selectionChanged()), this, SLOT(shapeSelectionChanged())); } Canvas* View::canvasWidget() const { return d->canvas; } KoZoomController *View::zoomController() const { return d->zoomController; } KoCanvasController* View::canvasController() const { return d->canvasController; } ColumnHeaderWidget* View::columnHeader()const { return d->columnHeader; } RowHeaderWidget* View::rowHeader()const { return d->rowHeader; } QScrollBar* View::horzScrollBar()const { return d->horzScrollBar; } QScrollBar* View::vertScrollBar()const { return d->vertScrollBar; } TabBar* View::tabBar() const { return d->tabBar; } KoZoomHandler* View::zoomHandler() const { return d->zoomHandler; } bool View::isLoading() const { return d->loading; } Selection* View::selection() const { return d->selection; } Sheet* View::activeSheet() const { return d->activeSheet; } void View::sheetDestroyed(QObject* obj) { if (Sheet *sheet = dynamic_cast(obj)) { Q_ASSERT(d->sheetViews.contains(sheet)); d->sheetViews.remove(sheet); // The SheetView will be proper destroyed already cause it's a QObject-child of the sheet. } } SheetView* View::sheetView(const Sheet* sheet) const { SheetView *sheetView = d->sheetViews.value(sheet); if (!sheetView) { debugSheetsRender << "View: Creating SheetView for" << sheet->sheetName(); sheetView = new SheetView(sheet); d->sheetViews.insert(sheet, sheetView); sheetView->setViewConverter(zoomHandler()); connect(sheetView, SIGNAL(visibleSizeChanged(QSizeF)), d->canvas, SLOT(setDocumentSize(QSizeF))); connect(sheetView, SIGNAL(visibleSizeChanged(QSizeF)), d->zoomController, SLOT(setDocumentSize(QSizeF))); connect(sheet, SIGNAL(visibleSizeChanged()), sheetView, SLOT(updateAccessedCellRange())); connect(sheet, SIGNAL(destroyed(QObject*)), this, SLOT(sheetDestroyed(QObject*))); } return sheetView; } void View::refreshSheetViews() { QList sheets = d->sheetViews.keys(); QList< QPointer > sheetViews = d->sheetViews.values(); foreach(const Sheet *sheet, d->sheetViews.keys()) { disconnect(sheet, SIGNAL(destroyed(QObject*)), this, SLOT(sheetDestroyed(QObject*))); } foreach (SheetView *sheetView, sheetViews) { disconnect(sheetView, SIGNAL(visibleSizeChanged(QSizeF)), d->canvas, SLOT(setDocumentSize(QSizeF))); disconnect(sheetView, SIGNAL(visibleSizeChanged(QSizeF)), d->zoomController, SLOT(setDocumentSize(QSizeF))); disconnect(sheetView->sheet(), SIGNAL(visibleSizeChanged()), sheetView, SLOT(updateAccessedCellRange())); } qDeleteAll(sheetViews); d->sheetViews.clear(); foreach(const Sheet *sheet, d->doc->map()->sheetList()) { sheet->cellStorage()->invalidateStyleCache(); } } void View::refreshSelection(const Region& region) { doc()->map()->addDamage(new CellDamage(activeSheet(), region, CellDamage::Appearance)); } void View::aboutToModify(const Region& region) { Q_UNUSED(region); selection()->emitCloseEditor(true); // save changes } void View::initConfig() { KSharedConfigPtr config = Factory::global().config(); const KConfigGroup parameterGroup = config->group("Parameters"); const bool configFromDoc = doc()->configLoadFromFile(); if (!configFromDoc) { doc()->map()->settings()->setShowHorizontalScrollBar(parameterGroup.readEntry("Horiz ScrollBar", true)); doc()->map()->settings()->setShowVerticalScrollBar(parameterGroup.readEntry("Vert ScrollBar", true)); } doc()->map()->settings()->setShowColumnHeader(parameterGroup.readEntry("Column Header", true)); doc()->map()->settings()->setShowRowHeader(parameterGroup.readEntry("Row Header", true)); if (!configFromDoc) doc()->map()->settings()->setCompletionMode((KCompletion::CompletionMode)parameterGroup.readEntry("Completion Mode", (int)(KCompletion::CompletionAuto))); doc()->map()->settings()->setMoveToValue((Calligra::Sheets::MoveTo)parameterGroup.readEntry("Move", (int)(Bottom))); doc()->map()->settings()->setIndentValue(parameterGroup.readEntry("Indent", 10.0)); doc()->map()->settings()->setTypeOfCalc((MethodOfCalc)parameterGroup.readEntry("Method of Calc", (int)(SumOfNumber))); if (!configFromDoc) doc()->map()->settings()->setShowTabBar(parameterGroup.readEntry("Tabbar", true)); doc()->map()->settings()->setShowStatusBar(parameterGroup.readEntry("Status bar", true)); changeNbOfRecentFiles(parameterGroup.readEntry("NbRecentFile", 10)); //autosave value is stored as a minute. //but default value is stored as seconde. doc()->setAutoSave(parameterGroup.readEntry("AutoSave", KoDocument::defaultAutoSave() / 60)*60); doc()->setBackupFile(parameterGroup.readEntry("BackupFile", true)); const KConfigGroup colorGroup = config->group("KSpread Color"); doc()->map()->settings()->setGridColor(colorGroup.readEntry("GridColor", QColor(Qt::lightGray))); doc()->map()->settings()->changePageOutlineColor(colorGroup.readEntry("PageOutlineColor", QColor(Qt::red))); initCalcMenu(); calcStatusBarOp(); } void View::changeNbOfRecentFiles(int _nb) { if (mainWindow()) mainWindow()->setMaxRecentItems(_nb); } void View::initCalcMenu() { switch (doc()->map()->settings()->getTypeOfCalc()) { case SumOfNumber: d->actions->calcSum->setChecked(true); break; case Min: d->actions->calcMin->setChecked(true); break; case Max: d->actions->calcMax->setChecked(true); break; case Average: d->actions->calcAverage->setChecked(true); break; case Count: d->actions->calcCount->setChecked(true); break; case CountA: d->actions->calcCountA->setChecked(true); break; case NoneCalc: d->actions->calcNone->setChecked(true); break; default : d->actions->calcSum->setChecked(true); break; } } void View::recalcWorkBook() { doc()->map()->recalcManager()->recalcMap(); } void View::recalcWorkSheet() { if (!activeSheet()) return; doc()->map()->recalcManager()->recalcSheet(activeSheet()); } void View::shapeSelectionChanged() { const KoSelection* selection = d->canvas->shapeManager()->selection(); const QList shapes = selection->selectedShapes(KoFlake::StrippedSelection); if (shapes.isEmpty()) { d->actions->shapeAnchor->setEnabled(false); return; } d->actions->shapeAnchor->setEnabled(true); // start with the first shape const KoShape* shape = shapes[0]; const ShapeApplicationData* data = dynamic_cast(shape->applicationData()); if (!data) { // Container children do not have the application data set, deselect the anchoring action. d->actions->shapeAnchor->setCurrentAction(0); return; } bool anchoredToCell = data->isAnchoredToCell(); d->actions->shapeAnchor->setCurrentAction(anchoredToCell ? i18n("Cell") : i18n("Page")); // go on with the remaining shapes for (int i = 1; i < shapes.count(); ++i) { shape = shapes[i]; data = dynamic_cast(shape->applicationData()); Q_ASSERT(data); if (anchoredToCell != data->isAnchoredToCell()) { // If the anchoring differs between shapes, deselect the anchoring action and stop here. d->actions->shapeAnchor->setCurrentAction(0); break; } } } void View::editDeleteSelection() { d->canvas->toolProxy()->deleteSelection(); } void View::initialPosition() { // Loading completed, pick initial worksheet foreach(Sheet* sheet, doc()->map()->sheetList()) { addSheet(sheet); } // Set the initial X and Y offsets for the view (OpenDocument loading) const LoadingInfo* loadingInfo = doc()->map()->loadingInfo(); if (loadingInfo->fileFormat() == LoadingInfo::OpenDocument) { d->savedAnchors = loadingInfo->cursorPositions(); d->savedMarkers = loadingInfo->cursorPositions(); d->savedOffsets = loadingInfo->scrollingOffsets(); } Sheet* sheet = loadingInfo->initialActiveSheet(); if (!sheet) { //activate first table which is not hiding sheet = doc()->map()->visibleSheets().isEmpty() ? 0 : doc()->map()->findSheet(doc()->map()->visibleSheets().first()); if (!sheet) { sheet = doc()->map()->sheet(0); if (sheet) { sheet->setHidden(false); QString tabName = sheet->sheetName(); d->tabBar->addTab(tabName); } } } setActiveSheet(sheet); d->mapViewModel->setActiveSheet(sheet); // Set the initial X and Y offsets for the view (Native format loading) if (loadingInfo->fileFormat() == LoadingInfo::NativeFormat) { const QPoint offset = zoomHandler()->documentToView(loadingInfo->scrollingOffsets()[sheet]).toPoint(); d->canvas->setDocumentOffset(offset); d->horzScrollBar->setValue(offset.x()); d->vertScrollBar->setValue(offset.y()); // Set the initial position for the marker as stored in the XML file, // (1,1) otherwise const QPoint marker = loadingInfo->cursorPositions()[sheet]; d->selection->initialize((marker.x() <= 0 || marker.y() <= 0) ? QPoint(1, 1) : marker); } updateShowSheetMenu(); // Initialize shape anchoring action. shapeSelectionChanged(); initConfig(); d->canvas->setFocus(); QTimer::singleShot(50, this, SLOT(finishLoading())); } void View::finishLoading() { // finish the "View Loading" process d->loading = false; doc()->map()->deleteLoadingInfo(); setHeaderMinima(); // Activate the cell tool. if (mainWindow()) KoToolManager::instance()->switchToolRequested("KSpreadCellToolId"); } void View::updateReadWrite(bool readwrite) { // inform the cell tool emit documentReadWriteToggled(readwrite); const QList actions = actionCollection()->actions(); for (int i = 0; i < actions.count(); ++i) { // The action collection contains also the flake tool actions. Skip them. if (actions[i]->parent() == this) actions[i]->setEnabled(readwrite); } if (!doc() || !doc()->map() || doc()->map()->isProtected()) { d->actions->showSheet->setEnabled(false); d->actions->hideSheet->setEnabled(false); } else { d->actions->showSheet->setEnabled(true); d->actions->hideSheet->setEnabled(true); } d->actions->showPageOutline->setEnabled(true); d->tabBar->setReadOnly(doc()->map()->isProtected()); } void View::createTemplate() { KoTemplateCreateDia::createTemplate(doc()->documentPart()->templatesResourcePath(), ".ots", doc(), this); } void View::setActiveSheet(Sheet* sheet, bool updateSheet) { // It can happen that our tabBar->activeTab() is not in sync with our activeSheet() if // setActiveSheet() was previously called before the delayed View::initialPosition() // was called and had a change to proper TabBar::setTabs(). if (sheet == d->activeSheet && (!sheet || d->tabBar->activeTab() == sheet->sheetName())) return; if (d->activeSheet != 0 && !d->selection->referenceSelectionMode()) { selection()->emitCloseEditor(true); // save changes saveCurrentSheetSelection(); } const Sheet* oldSheet = d->activeSheet; d->activeSheet = sheet; if (d->activeSheet == 0) { return; } // flake // Change the active shape controller and its shapes. d->canvas->shapeController()->setShapeControllerBase(d->activeSheet); // and then update the toolmanager separately KoToolManager::instance()->updateShapeControllerBase(d->activeSheet, d->canvas->canvasController()); d->canvas->shapeManager()->setShapes(d->activeSheet->shapes()); // Tell the Canvas about the new visible sheet size. sheetView(d->activeSheet)->updateAccessedCellRange(); // If there was no sheet before or the layout directions differ. if (!oldSheet || oldSheet->layoutDirection() != d->activeSheet->layoutDirection()) { // Propagate the layout direction to the canvas and horz. scrollbar. const Qt::LayoutDirection direction = d->activeSheet->layoutDirection(); d->canvas->setLayoutDirection(direction); d->horzScrollBar->setLayoutDirection(direction); // Replace the painting strategy for painting shapes. KoShapeManager *const shapeManager = d->canvas->shapeManager(); KoShapeManagerPaintingStrategy *paintingStrategy = 0; if (direction == Qt::LeftToRight) { paintingStrategy = new KoShapeManagerPaintingStrategy(shapeManager); } else { paintingStrategy = new RightToLeftPaintingStrategy(shapeManager, d->canvas); } shapeManager->setPaintingStrategy(paintingStrategy); } // If there was no sheet before or the formula visibilities differ. if (!oldSheet || oldSheet->getShowFormula() != d->activeSheet->getShowFormula()) { const bool showFormulas = d->activeSheet->getShowFormula(); stateChanged("show_formulas", showFormulas ? StateNoReverse : StateReverse); } // Restore the old scrolling offset. QMap::ConstIterator it3 = d->savedOffsets.constFind(d->activeSheet); if (it3 != d->savedOffsets.constEnd()) { const QPoint offset = zoomHandler()->documentToView(*it3).toPoint(); d->canvas->setDocumentOffset(offset); d->horzScrollBar->setValue(offset.x()); d->vertScrollBar->setValue(offset.y()); } // tell the resource manager of the newly active page d->canvas->resourceManager()->setResource(KoCanvasResourceManager::CurrentPage, QVariant(sheet->map()->indexOf(sheet) + 1)); // Always repaint the visible cells. d->canvas->update(); d->rowHeader->update(); d->columnHeader->update(); d->selectAllButton->update(); // Prevents an endless loop, if called by the TabBar. if (updateSheet) { d->tabBar->setActiveTab(d->activeSheet->sheetName()); } if (d->selection->referenceSelectionMode()) { d->selection->setActiveSheet(d->activeSheet); return; } /* see if there was a previous selection on this other sheet */ QMap::ConstIterator it = d->savedAnchors.constFind(d->activeSheet); QMap::ConstIterator it2 = d->savedMarkers.constFind(d->activeSheet); // restore the old anchor and marker const QPoint newAnchor = (it == d->savedAnchors.constEnd()) ? QPoint(1, 1) : *it; const QPoint newMarker = (it2 == d->savedMarkers.constEnd()) ? QPoint(1, 1) : *it2; d->selection->clear(); d->selection->setActiveSheet(d->activeSheet); d->selection->setOriginSheet(d->activeSheet); d->selection->initialize(QRect(newMarker, newAnchor)); d->actions->showPageOutline->blockSignals(true); d->actions->showPageOutline->setChecked(d->activeSheet->isShowPageOutline()); d->actions->showPageOutline->blockSignals(false); d->actions->protectSheet->blockSignals(true); d->actions->protectSheet->setChecked(d->activeSheet->isProtected()); d->actions->protectSheet->blockSignals(false); d->actions->protectDoc->blockSignals(true); d->actions->protectDoc->setChecked(doc()->map()->isProtected()); d->actions->protectDoc->blockSignals(false); d->adjustActions(!d->activeSheet->isProtected()); const bool protect = d->activeSheet->isProtected(); stateChanged("sheet_is_protected", protect ? StateNoReverse : StateReverse); // Auto calculation state for the INFO function. const bool autoCalc = d->activeSheet->isAutoCalculationEnabled(); d->doc->map()->calculationSettings()->setAutoCalculationEnabled(autoCalc); calcStatusBarOp(); } void View::changeSheet(const QString& _name) { if (activeSheet()->sheetName() == _name) return; Sheet *t = doc()->map()->findSheet(_name); if (!t) { debugSheets << "Unknown sheet" << _name; return; } setActiveSheet(t, false /* False: Endless loop because of setActiveTab() => do the visual area update manually*/); d->mapViewModel->setActiveSheet(t); } void View::moveSheet(unsigned sheet, unsigned target) { if (doc()->map()->isProtected()) return; QStringList vs = doc()->map()->visibleSheets(); if (target >= (uint) vs.count()) doc()->map()->moveSheet(vs[ sheet ], vs[ vs.count()-1 ], false); else doc()->map()->moveSheet(vs[ sheet ], vs[ target ], true); d->tabBar->moveTab(sheet, target); } void View::sheetProperties() { // sanity check, shouldn't happen if (doc()->map()->isProtected()) return; if (d->activeSheet->isProtected()) return; bool directionChanged = false; bool formulaVisibilityChanged = false; QPointer dlg = new SheetPropertiesDialog(this); dlg->setLayoutDirection(d->activeSheet->layoutDirection()); dlg->setAutoCalculationEnabled(d->activeSheet->isAutoCalculationEnabled()); dlg->setShowGrid(d->activeSheet->getShowGrid()); dlg->setShowPageOutline(d->activeSheet->isShowPageOutline()); dlg->setShowFormula(d->activeSheet->getShowFormula()); dlg->setHideZero(d->activeSheet->getHideZero()); dlg->setShowFormulaIndicator(d->activeSheet->getShowFormulaIndicator()); dlg->setShowCommentIndicator(d->activeSheet->getShowCommentIndicator()); dlg->setColumnAsNumber(d->activeSheet->getShowColumnNumber()); dlg->setLcMode(d->activeSheet->getLcMode()); dlg->setCapitalizeFirstLetter(d->activeSheet->getFirstLetterUpper()); if (dlg->exec()) { SheetPropertiesCommand* command = new SheetPropertiesCommand(d->activeSheet); if (d->activeSheet->layoutDirection() != dlg->layoutDirection()) directionChanged = true; if (d->activeSheet->getShowFormula() != dlg->showFormula()) { formulaVisibilityChanged = true; } command->setLayoutDirection(dlg->layoutDirection()); command->setAutoCalculationEnabled(dlg->autoCalc()); command->setShowGrid(dlg->showGrid()); command->setShowPageOutline(dlg->showPageOutline()); command->setShowFormula(dlg->showFormula()); command->setHideZero(dlg->hideZero()); command->setShowFormulaIndicator(dlg->showFormulaIndicator()); command->setShowCommentIndicator(dlg->showCommentIndicator()); command->setColumnAsNumber(dlg->columnAsNumber()); command->setLcMode(dlg->lcMode()); command->setCapitalizeFirstLetter(dlg->capitalizeFirstLetter()); doc()->addCommand(command); } delete dlg; if (directionChanged) { // the scrollbar and hborder remain reversed otherwise d->canvas->setLayoutDirection(d->activeSheet->layoutDirection()); // for scrolling d->horzScrollBar->setLayoutDirection(d->activeSheet->layoutDirection()); d->columnHeader->update(); // Replace the painting strategy for painting shapes. KoShapeManager *const shapeManager = d->canvas->shapeManager(); KoShapeManagerPaintingStrategy *paintingStrategy = 0; if (d->activeSheet->layoutDirection() == Qt::LeftToRight) { paintingStrategy = new KoShapeManagerPaintingStrategy(shapeManager); } else { paintingStrategy = new RightToLeftPaintingStrategy(shapeManager, d->canvas); } shapeManager->setPaintingStrategy(paintingStrategy); } if (formulaVisibilityChanged) { const bool showFormulas = d->activeSheet->getShowFormula(); stateChanged("show_formulas", showFormulas ? StateNoReverse : StateReverse); sheetView(d->activeSheet)->invalidate(); d->canvas->update(); } } void View::insertSheet() { if (doc()->map()->isProtected()) { KMessageBox::error(0, i18n("You cannot change a protected sheet.")); return; } selection()->emitCloseEditor(true); // save changes Sheet * t = doc()->map()->createSheet(); KUndo2Command* command = new AddSheetCommand(t); doc()->addCommand(command); setActiveSheet(t); if (doc()->map()->visibleSheets().count() > 1) { d->actions->deleteSheet->setEnabled(true); d->actions->hideSheet->setEnabled(true); } } void View::duplicateSheet() { if (doc()->map()->isProtected()) { KMessageBox::error(this, i18n("You cannot change a protected sheet.")); return; } DuplicateSheetCommand* command = new DuplicateSheetCommand(); command->setSheet(activeSheet()); doc()->addCommand(command); if (doc()->map()->visibleSheets().count() > 1) { d->actions->deleteSheet->setEnabled(true); d->actions->hideSheet->setEnabled(true); } } void View::hideSheet() { if (!d->activeSheet) return; if (doc()->map()->visibleSheets().count() == 1) { KMessageBox::error(this, i18n("You cannot hide the last visible sheet.")); return; } QStringList vs = doc()->map()->visibleSheets(); int i = vs.indexOf(d->activeSheet->sheetName()) - 1; if (i < 0) i = 1; QString sn = vs[i]; KUndo2Command* command = new HideSheetCommand(activeSheet()); doc()->addCommand(command); d->tabBar->removeTab(d->activeSheet->sheetName()); d->tabBar->setActiveTab(sn); } void View::showSheet() { if (!d->activeSheet) return; ShowDialog dialog(this, d->selection); dialog.exec(); } void View::copyAsText() { if (!d->activeSheet) return; QMimeData* mimeData = new QMimeData(); mimeData->setText(CopyCommand::saveAsPlainText(*selection())); QApplication::clipboard()->setMimeData(mimeData); } void View::setShapeAnchoring(const QString& mode) { const KoSelection* selection = d->canvas->shapeManager()->selection(); const QList shapes = selection->selectedShapes(KoFlake::StrippedSelection); for (int i = 0; i < shapes.count(); ++i) { const KoShape* shape = shapes[i]; ShapeApplicationData* data = dynamic_cast(shape->applicationData()); Q_ASSERT(data); data->setAnchoredToCell(mode == i18n("Cell")); } } void View::toggleProtectDoc(bool mode) { if (!doc() || !doc()->map()) return; bool success; if (mode) { success = doc()->map()->showPasswordDialog(this, ProtectableObject::Lock, i18n("Protect Document")); } else { success = doc()->map()->showPasswordDialog(this, ProtectableObject::Unlock, i18n("Unprotect Document")); } if (!success) { d->actions->protectDoc->setChecked(!mode); return; } doc()->setModified(true); stateChanged("map_is_protected", mode ? StateNoReverse : StateReverse); d->tabBar->setReadOnly(doc()->map()->isProtected()); } void View::toggleProtectSheet(bool mode) { if (!d->activeSheet) return; bool success; if (mode) { success = activeSheet()->showPasswordDialog(this, ProtectableObject::Lock, i18n("Protect Sheet")); } else { success = activeSheet()->showPasswordDialog(this, ProtectableObject::Unlock, i18n("Unprotect Sheet")); } if (!success) { d->actions->protectSheet->setChecked(!mode); return; } doc()->setModified(true); d->adjustActions(!mode); // The sheet protection change may hide/unhide some values or formulas, // so the cached visual data has become invalid. refreshSheetViews(); d->canvas->update(); // inform the cell tool emit sheetProtectionToggled(mode); } void View::togglePageOutline(bool mode) { if (!d->activeSheet) return; d->activeSheet->setShowPageOutline(mode); } void View::viewZoom(KoZoomMode::Mode mode, qreal zoom) { Q_UNUSED(zoom) #ifdef NDEBUG Q_UNUSED(mode); #endif Q_ASSERT(mode == KoZoomMode::ZOOM_CONSTANT); selection()->emitCloseEditor(true); // save changes setHeaderMinima(); d->canvas->update(); d->columnHeader->update(); d->rowHeader->update(); d->selectAllButton->update(); } void View::showColumnHeader(bool enable) { doc()->map()->settings()->setShowColumnHeader(enable); d->columnHeader->setVisible(enable); d->selectAllButton->setVisible(enable && d->rowHeader->isVisible()); } void View::showRowHeader(bool enable) { doc()->map()->settings()->setShowRowHeader(enable); d->rowHeader->setVisible(enable); d->selectAllButton->setVisible(enable && d->columnHeader->isVisible()); } void View::showHorizontalScrollBar(bool enable) { doc()->map()->settings()->setShowHorizontalScrollBar(enable); d->horzScrollBar->setVisible(enable); } void View::showVerticalScrollBar(bool enable) { doc()->map()->settings()->setShowVerticalScrollBar(enable); d->vertScrollBar->setVisible(enable); } void View::showStatusBar(bool enable) { doc()->map()->settings()->setShowStatusBar(enable); if (statusBar()) { statusBar()->setVisible(enable); } } void View::showTabBar(bool enable) { doc()->map()->settings()->setShowTabBar(enable); d->tabBar->setVisible(enable); } void View::optionsNotifications() { #ifndef QT_NO_DBUS KNotifyConfigWidget::configure(this); #endif } void View::preference() { PreferenceDialog dialog(this); dialog.exec(); } void View::nextSheet() { Sheet * t = doc()->map()->nextSheet(activeSheet()); if (!t) { debugSheets << "Unknown sheet"; return; } selection()->emitCloseEditor(true); // save changes setActiveSheet(t); d->tabBar->setActiveTab(t->sheetName()); d->tabBar->ensureVisible(t->sheetName()); } void View::previousSheet() { Sheet * t = doc()->map()->previousSheet(activeSheet()); if (!t) { debugSheets << "Unknown sheet"; return; } selection()->emitCloseEditor(true); // save changes setActiveSheet(t); d->tabBar->setActiveTab(t->sheetName()); d->tabBar->ensureVisible(t->sheetName()); } void View::firstSheet() { Sheet *t = doc()->map()->sheet(0); if (!t) { debugSheets << "Unknown sheet"; return; } selection()->emitCloseEditor(true); // save changes setActiveSheet(t); d->tabBar->setActiveTab(t->sheetName()); d->tabBar->ensureVisible(t->sheetName()); } void View::lastSheet() { Sheet *t = doc()->map()->sheet(doc()->map()->count() - 1); if (!t) { debugSheets << "Unknown sheet"; return; } selection()->emitCloseEditor(true); // save changes setActiveSheet(t); d->tabBar->setActiveTab(t->sheetName()); d->tabBar->ensureVisible(t->sheetName()); } void View::keyPressEvent(QKeyEvent *event) { #ifndef NDEBUG if ((event->modifiers() & Qt::ControlModifier) && (event->modifiers() & Qt::ShiftModifier)) { if (event->key() == Qt::Key_V) { // Ctrl+Shift+V to show debug (similar to Words) d->activeSheet->printDebug(); } } #endif QWidget::keyPressEvent(event); } int View::leftBorder() const { return (int)(((RowHeader*)d->rowHeader)->width()); } int View::rightBorder() const { return d->vertScrollBar->width(); } int View::topBorder() const { return (int)(((ColumnHeader*)d->columnHeader)->height()); } int View::bottomBorder() const { return d->horzScrollBar->height(); } void View::setHeaderMinima() { if (d->loading) // "View Loading" not finished yet return; QFont font(KoGlobal::defaultFont()); QFontMetricsF fm(font, 0); qreal h = fm.height() + 3; qreal w = fm.width(QString::fromLatin1("99999")) + 3; d->columnHeader->setMinimumHeight(qRound(h)); d->rowHeader->setMinimumWidth(qRound(w)); d->selectAllButton->setMinimumHeight(qRound(h)); d->selectAllButton->setMinimumWidth(qRound(w)); } void View::paperLayoutDlg() { selection()->emitCloseEditor(true); // save changes SheetPrint* print = d->activeSheet->print(); KoPageLayout pl = print->settings()->pageLayout(); /* const HeaderFooter *const headerFooter = print->headerFooter(); HeadFoot hf; hf.headLeft = headerFooter->localizeHeadFootLine(headerFooter->headLeft()); hf.headRight = headerFooter->localizeHeadFootLine(headerFooter->headRight()); hf.headMid = headerFooter->localizeHeadFootLine(headerFooter->headMid()); hf.footLeft = headerFooter->localizeHeadFootLine(headerFooter->footLeft()); hf.footRight = headerFooter->localizeHeadFootLine(headerFooter->footRight()); hf.footMid = headerFooter->localizeHeadFootLine(headerFooter->footMid()); */ PageLayoutDialog dialog(this, d->activeSheet); dialog.exec(); } void View::resetPrintRange() { DefinePrintRangeCommand* command = new DefinePrintRangeCommand(); command->setText(kundo2_i18n("Reset Print Range")); command->setSheet(activeSheet()); command->add(Region(QRect(QPoint(1, 1), QPoint(KS_colMax, KS_rowMax)), activeSheet())); doc()->addCommand(command); } void View::deleteSheet() { if (doc()->map()->count() <= 1 || (doc()->map()->visibleSheets().count() <= 1)) { KMessageBox::sorry(this, i18n("You cannot delete the only sheet."), i18n("Remove Sheet")); return; } int ret = KMessageBox::warningContinueCancel(this, i18n("You are about to remove the active sheet.\nDo you want to continue?"), - i18n("Remove Sheet"), KGuiItem(i18n("&Delete"), koIconName("edit-delete"))); + i18n("Remove Sheet"), KStandardGuiItem::del()); if (ret == KMessageBox::Continue) { selection()->emitCloseEditor(false); // discard changes doc()->setModified(true); Sheet * tbl = activeSheet(); KUndo2Command* command = new RemoveSheetCommand(tbl); doc()->addCommand(command); } } void View::slotRename() { Sheet * sheet = activeSheet(); if (sheet->isProtected()) { KMessageBox::error(0, i18n("You cannot change a protected sheet.")); return; } bool ok; QString activeName = sheet->sheetName(); QString newName = QInputDialog::getText(this, i18n("Rename Sheet"), i18n("Enter name:"), QLineEdit::Normal, activeName, &ok); if (!ok) return; if ((newName.trimmed()).isEmpty()) { // Sheet name is empty. KMessageBox::information(this, i18n("Sheet name cannot be empty."), i18n("Change Sheet Name")); // Recursion slotRename(); } else if (newName != activeName) { // Sheet name changed. // Is the name already used if (doc()->map()->findSheet(newName)) { KMessageBox::information(this, i18n("This name is already used."), i18n("Change Sheet Name")); // Recursion slotRename(); return; } KUndo2Command* command = new RenameSheetCommand(sheet, newName); doc()->addCommand(command); doc()->setModified(true); } } //------------------------------------------------ // // Document signals // //------------------------------------------------ void View::slotChangeSelection(const Calligra::Sheets::Region& changedRegion) { if (!changedRegion.isValid()) return; if (d->selection->referenceSelectionMode()) { doc()->map()->addDamage(new SelectionDamage(changedRegion)); debugSheetsFormula << "Choice:" << *selection(); return; } // delayed recalculation of the operation shown in the status bar d->statusBarOpTimer.setSingleShot(true); d->statusBarOpTimer.start(250); if (!d->loading && !doc()->map()->isLoading()) { doc()->map()->addDamage(new SelectionDamage(changedRegion)); } d->rowHeader->update(); d->columnHeader->update(); d->selectAllButton->update(); if (d->selection->isColumnSelected() || d->selection->isRowSelected()) { return; } d->canvas->validateSelection(); } void View::slotScrollChoice(const Calligra::Sheets::Region& changedRegion) { if (!selection()->referenceSelectionMode() || !changedRegion.isValid()) { return; } } void View::calcStatusBarOp() { Sheet * sheet = activeSheet(); ValueCalc* calc = doc()->map()->calc(); Value val; QString prefix = ""; MethodOfCalc tmpMethod = doc()->map()->settings()->getTypeOfCalc(); if (sheet && tmpMethod != NoneCalc) { Value range = sheet->cellStorage()->valueRegion(*d->selection); switch (tmpMethod) { case SumOfNumber: val = calc->sum(range); prefix = i18n("Sum: "); break; case Average: val = calc->avg(range); prefix = i18n("Average: "); break; case Min: val = calc->min(range); prefix = i18n("Min: "); break; case Max: val = calc->max(range); prefix = i18n("Max: "); break; case CountA: val = Value(calc->count(range)); prefix = i18n("Count: "); break; case Count: val = Value(calc->count(range, false)); prefix = i18n("CountA: "); case NoneCalc: break; default: break; } if ((range.columns() > 1) || (range.rows() > 1)) { QString size = i18n("%1x%2", range.columns(), range.rows()); prefix = prefix.size() ? size + ", " + prefix : size; } } QString res = doc()->map()->converter()->asString(val).asString(); QString tmp; if (res.length()) tmp = prefix + res; if (d->calcLabel) d->calcLabel->setText(QString(' ') + tmp + ' '); } void View::statusBarClicked(const QPoint&) { QPoint mousepos = QCursor::pos(); if (factory()) if (QMenu* menu = dynamic_cast(factory()->container("calc_popup" , this))) menu->popup(mousepos); } void View::menuCalc(bool) { if (d->actions->calcMin->isChecked()) { doc()->map()->settings()->setTypeOfCalc(Min); } else if (d->actions->calcMax->isChecked()) { doc()->map()->settings()->setTypeOfCalc(Max); } else if (d->actions->calcCount->isChecked()) { doc()->map()->settings()->setTypeOfCalc(Count); } else if (d->actions->calcAverage->isChecked()) { doc()->map()->settings()->setTypeOfCalc(Average); } else if (d->actions->calcSum->isChecked()) { doc()->map()->settings()->setTypeOfCalc(SumOfNumber); } else if (d->actions->calcCountA->isChecked()) { doc()->map()->settings()->setTypeOfCalc(CountA); } else if (d->actions->calcNone->isChecked()) doc()->map()->settings()->setTypeOfCalc(NoneCalc); calcStatusBarOp(); } QWidget* View::canvas() const { return d->canvas; } void View::popupTabBarMenu(const QPoint & _point) { if (!factory()) return; if (d->tabBar) { QMenu* const menu = static_cast(factory()->container("menupage_popup", this)); if (!menu) return; QAction* insertSheet = new QAction(koIcon("insert-table"), i18n("Insert Sheet"), this); insertSheet->setToolTip(i18n("Remove the active sheet")); connect(insertSheet, SIGNAL(triggered(bool)), this, SLOT(insertSheet())); menu->insertAction(d->actions->duplicateSheet, insertSheet); QAction* deleteSheet = new QAction(koIcon("delete_table"), i18n("Remove Sheet"), this); deleteSheet->setToolTip(i18n("Remove the active sheet")); connect(deleteSheet, SIGNAL(triggered(bool)), this, SLOT(deleteSheet())); menu->insertAction(d->actions->hideSheet, deleteSheet); bool state = (doc()->map()->visibleSheets().count() > 1); if (d->activeSheet && d->activeSheet->isProtected()) { deleteSheet->setEnabled(false); d->actions->hideSheet->setEnabled(false); d->actions->showSheet->setEnabled(false); } else { deleteSheet->setEnabled(state); d->actions->hideSheet->setEnabled(state); d->actions->showSheet->setEnabled(doc()->map()->hiddenSheets().count() > 0); } if (!doc() || !doc()->map() || doc()->map()->isProtected()) { insertSheet->setEnabled(false); deleteSheet->setEnabled(false); d->actions->renameSheet->setEnabled(false); d->actions->showSheet->setEnabled(false); d->actions->hideSheet->setEnabled(false); } menu->exec(_point); menu->removeAction(insertSheet); menu->removeAction(deleteSheet); delete insertSheet; delete deleteSheet; } } void View::updateBorderButton() { if (d->activeSheet) d->actions->showPageOutline->setChecked(d->activeSheet->isShowPageOutline()); } void View::addSheet(Sheet *sheet) { if (!sheet->isHidden()) { d->tabBar->addTab(sheet->sheetName()); } const bool state = (doc()->map()->visibleSheets().count() > 1); d->actions->deleteSheet->setEnabled(state); d->actions->hideSheet->setEnabled(state); // Connect some signals connect(sheet, SIGNAL(shapeAdded(Sheet*,KoShape*)), d->mapViewModel, SLOT(addShape(Sheet*,KoShape*))); connect(sheet, SIGNAL(shapeRemoved(Sheet*,KoShape*)), d->mapViewModel, SLOT(removeShape(Sheet*,KoShape*))); } void View::removeSheet(Sheet *sheet) { d->tabBar->removeTab(sheet->sheetName()); setActiveSheet(doc()->map()->sheet(0)); const bool state = (doc()->map()->visibleSheets().count() > 1); d->actions->deleteSheet->setEnabled(state); d->actions->hideSheet->setEnabled(state); // Disconnect signals. disconnect(sheet, 0, d->mapViewModel, 0); } QColor View::borderColor() const { return d->canvas->resourceManager()->foregroundColor().toQColor(); } void View::updateShowSheetMenu() { if (d->activeSheet) { if (d->activeSheet->map()->isProtected()) d->actions->showSheet->setEnabled(false); else d->actions->showSheet->setEnabled(doc()->map()->hiddenSheets().count() > 0); } } QPoint View::markerFromSheet(Sheet* sheet) const { QMap::ConstIterator it = d->savedMarkers.constFind(sheet); QPoint newMarker = (it == d->savedMarkers.constEnd()) ? QPoint(1, 1) : *it; return newMarker; } QPointF View::offsetFromSheet(Sheet* sheet) const { QMap::ConstIterator it = d->savedOffsets.constFind(sheet); QPointF offset = (it == d->savedOffsets.constEnd()) ? QPointF() : *it; return offset; } void View::saveCurrentSheetSelection() { /* save the current selection on this sheet */ if (d->activeSheet != 0) { d->savedAnchors.remove(d->activeSheet); d->savedAnchors.insert(d->activeSheet, d->selection->anchor()); debugSheetsUI << " Current scrollbar vert value:" << d->vertScrollBar->value(); debugSheetsUI << "Saving marker pos:" << d->selection->marker(); d->savedMarkers.remove(d->activeSheet); d->savedMarkers.insert(d->activeSheet, d->selection->marker()); d->savedOffsets.remove(d->activeSheet); d->savedOffsets.insert(d->activeSheet, QPointF(d->canvas->xOffset(), d->canvas->yOffset())); } } void View::handleDamages(const QList& damages) { QRegion paintRegion; enum { Nothing, Everything, Clipped } paintMode = Nothing; QList::ConstIterator end(damages.end()); for (QList::ConstIterator it = damages.begin(); it != end; ++it) { Damage* damage = *it; if (!damage) continue; if (damage->type() == Damage::Cell) { CellDamage* cellDamage = static_cast(damage); debugSheetsDamage << "Processing\t" << *cellDamage; Sheet* const damagedSheet = cellDamage->sheet(); if (cellDamage->changes() & CellDamage::Appearance) { const Region& region = cellDamage->region(); sheetView(damagedSheet)->invalidateRegion(region); paintMode = Everything; } continue; } if (damage->type() == Damage::Sheet) { SheetDamage* sheetDamage = static_cast(damage); debugSheetsDamage << *sheetDamage; const SheetDamage::Changes changes = sheetDamage->changes(); if (changes & (SheetDamage::Name | SheetDamage::Shown)) { d->tabBar->setTabs(doc()->map()->visibleSheets()); paintMode = Everything; } if (changes & (SheetDamage::Shown | SheetDamage::Hidden)) { updateShowSheetMenu(); paintMode = Everything; } // The following changes only affect the active sheet. if (sheetDamage->sheet() != d->activeSheet) { continue; } if (changes.testFlag(SheetDamage::ContentChanged)) { update(); paintMode = Everything; } if (changes.testFlag(SheetDamage::PropertiesChanged)) { sheetView(d->activeSheet)->invalidate(); paintMode = Everything; } if (sheetDamage->changes() & SheetDamage::ColumnsChanged) columnHeader()->update(); if (sheetDamage->changes() & SheetDamage::RowsChanged) rowHeader()->update(); continue; } if (damage->type() == Damage::Selection) { SelectionDamage* selectionDamage = static_cast(damage); debugSheetsDamage << "Processing\t" << *selectionDamage; const Region region = selectionDamage->region(); if (paintMode == Clipped) { const QRectF rect = canvasWidget()->cellCoordinatesToView(region.boundingRect()); paintRegion += rect.toRect().adjusted(-3, -3, 4, 4); } else { paintMode = Everything; } continue; } debugSheetsDamage << "Unhandled\t" << *damage; } // At last repaint the dirty cells. if (paintMode == Clipped) { canvas()->update(paintRegion); } else if (paintMode == Everything) { canvas()->update(); } } KoPrintJob * View::createPrintJob() { if (!activeSheet()) return 0; // About to print; close the editor. selection()->emitCloseEditor(true); // save changes return new PrintJob(this); } void View::updateAccessedCellRange(Sheet* sheet, const QPoint &location) { sheetView(sheet)->updateAccessedCellRange(location); } void View::enableAutoScroll() { d->scrollTimer->start(50); } void View::disableAutoScroll() { d->scrollTimer->stop(); } int View::autoScrollAcceleration(int offset) const { if (offset < 40) return offset; else return offset*offset / 40; } void View::slotAutoScroll() { QPoint scrollDistance; bool actuallyDoScroll = false; QPoint pos(mapFromGlobal(QCursor::pos())); //Provide progressive scrolling depending on the mouse position if (pos.y() < topBorder()) { scrollDistance.setY((int) - autoScrollAcceleration(- pos.y() + topBorder())); actuallyDoScroll = true; } else if (pos.y() > height() - bottomBorder()) { scrollDistance.setY((int) autoScrollAcceleration(pos.y() - height() + bottomBorder())); actuallyDoScroll = true; } if (pos.x() < leftBorder()) { scrollDistance.setX((int) - autoScrollAcceleration(- pos.x() + leftBorder())); actuallyDoScroll = true; } else if (pos.x() > width() - rightBorder()) { scrollDistance.setX((int) autoScrollAcceleration(pos.x() - width() + rightBorder())); actuallyDoScroll = true; } if (actuallyDoScroll) { pos = canvas()->mapFrom(this, pos); QMouseEvent* event = new QMouseEvent(QEvent::MouseMove, pos, Qt::NoButton, Qt::NoButton, QApplication::keyboardModifiers()); QApplication::postEvent(canvas(), event); emit autoScroll(scrollDistance); } }