diff --git a/commands/kpCommandHistoryBase.cpp b/commands/kpCommandHistoryBase.cpp index 580c0a84..c2d9f268 100644 --- a/commands/kpCommandHistoryBase.cpp +++ b/commands/kpCommandHistoryBase.cpp @@ -1,750 +1,751 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define DEBUG_KP_COMMAND_HISTORY 0 #include "kpCommandHistoryBase.h" #include #include #include #include #include #include #include #include #include #include #include #include "kpCommand.h" #include "kpLogCategories.h" #include "environments/commands/kpCommandEnvironment.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "mainWindow/kpMainWindow.h" #include "tools/kpTool.h" //--------------------------------------------------------------------- //template static void ClearPointerList (QLinkedList *listPtr) { if (!listPtr) return; qDeleteAll (listPtr->begin (), listPtr->end ()); listPtr->clear (); } struct kpCommandHistoryBasePrivate { }; kpCommandHistoryBase::kpCommandHistoryBase (bool doReadConfig, KActionCollection *ac) : d (new kpCommandHistoryBasePrivate ()) { m_actionUndo = new KToolBarPopupAction(KDE::icon("edit-undo"), undoActionText (), this); ac->addAction (KStandardAction::name (KStandardAction::Undo), m_actionUndo); ac->setDefaultShortcuts (m_actionUndo, KStandardShortcut::shortcut (KStandardShortcut::Undo)); connect (m_actionUndo, SIGNAL(triggered(bool)), this, SLOT (undo())); m_actionRedo = new KToolBarPopupAction(KDE::icon("edit-redo"), redoActionText (), this); ac->addAction (KStandardAction::name (KStandardAction::Redo), m_actionRedo); ac->setDefaultShortcuts (m_actionRedo, KStandardShortcut::shortcut (KStandardShortcut::Redo)); connect (m_actionRedo, SIGNAL(triggered(bool)), this, SLOT (redo())); m_actionUndo->setEnabled (false); m_actionRedo->setEnabled (false); connect (m_actionUndo->menu (), SIGNAL (triggered(QAction*)), this, SLOT (undoUpToNumber(QAction*))); connect (m_actionRedo->menu (), SIGNAL (triggered(QAction*)), this, SLOT (redoUpToNumber(QAction*))); m_undoMinLimit = 10; m_undoMaxLimit = 500; m_undoMaxLimitSizeLimit = 16 * 1048576; m_documentRestoredPosition = 0; if (doReadConfig) readConfig (); } kpCommandHistoryBase::~kpCommandHistoryBase () { ::ClearPointerList (&m_undoCommandList); ::ClearPointerList (&m_redoCommandList); delete d; } // public int kpCommandHistoryBase::undoLimit () const { return undoMinLimit (); } // public void kpCommandHistoryBase::setUndoLimit (int limit) { setUndoMinLimit (limit); } // public int kpCommandHistoryBase::undoMinLimit () const { return m_undoMinLimit; } // public void kpCommandHistoryBase::setUndoMinLimit (int limit) { #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::setUndoMinLimit(" << limit << ")" << endl; #endif if (limit < 1 || limit > 5000/*"ought to be enough for anybody"*/) { qCCritical(kpLogCommands) << "kpCommandHistoryBase::setUndoMinLimit(" << limit << ")" << endl; return; } if (limit == m_undoMinLimit) return; m_undoMinLimit = limit; trimCommandListsUpdateActions (); } // public int kpCommandHistoryBase::undoMaxLimit () const { return m_undoMaxLimit; } // public void kpCommandHistoryBase::setUndoMaxLimit (int limit) { #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::setUndoMaxLimit(" << limit << ")" << endl; #endif if (limit < 1 || limit > 5000/*"ought to be enough for anybody"*/) { qCCritical(kpLogCommands) << "kpCommandHistoryBase::setUndoMaxLimit(" << limit << ")" << endl; return; } if (limit == m_undoMaxLimit) return; m_undoMaxLimit = limit; trimCommandListsUpdateActions (); } // public kpCommandSize::SizeType kpCommandHistoryBase::undoMaxLimitSizeLimit () const { return m_undoMaxLimitSizeLimit; } // public void kpCommandHistoryBase::setUndoMaxLimitSizeLimit (kpCommandSize::SizeType sizeLimit) { #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::setUndoMaxLimitSizeLimit(" << sizeLimit << ")" << endl; #endif if (sizeLimit < 0 || sizeLimit > (500 * 1048576)/*"ought to be enough for anybody"*/) { qCCritical(kpLogCommands) << "kpCommandHistoryBase::setUndoMaxLimitSizeLimit(" << sizeLimit << ")" << endl; return; } if (sizeLimit == m_undoMaxLimitSizeLimit) return; m_undoMaxLimitSizeLimit = sizeLimit; trimCommandListsUpdateActions (); } // public void kpCommandHistoryBase::readConfig () { #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::readConfig()"; #endif KConfigGroup cfg (KSharedConfig::openConfig (), kpSettingsGroupUndoRedo); setUndoMinLimit (cfg.readEntry (kpSettingUndoMinLimit, undoMinLimit ())); setUndoMaxLimit (cfg.readEntry (kpSettingUndoMaxLimit, undoMaxLimit ())); setUndoMaxLimitSizeLimit ( cfg.readEntry (kpSettingUndoMaxLimitSizeLimit, undoMaxLimitSizeLimit ())); trimCommandListsUpdateActions (); } // public void kpCommandHistoryBase::writeConfig () { #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::writeConfig()"; #endif KConfigGroup cfg (KSharedConfig::openConfig (), kpSettingsGroupUndoRedo); cfg.writeEntry (kpSettingUndoMinLimit, undoMinLimit ()); cfg.writeEntry (kpSettingUndoMaxLimit, undoMaxLimit ()); cfg.writeEntry ( kpSettingUndoMaxLimitSizeLimit, undoMaxLimitSizeLimit ()); cfg.sync (); } // public void kpCommandHistoryBase::addCommand (kpCommand *command, bool execute) { #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::addCommand(" << command << ",execute=" << execute << ")" << endl; #endif if (execute) command->execute (); m_undoCommandList.push_front (command); ::ClearPointerList (&m_redoCommandList); #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\tdocumentRestoredPosition=" << m_documentRestoredPosition << endl; #endif if (m_documentRestoredPosition != INT_MAX) { if (m_documentRestoredPosition > 0) m_documentRestoredPosition = INT_MAX; else m_documentRestoredPosition--; #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\t\tdocumentRestoredPosition=" << m_documentRestoredPosition << endl; #endif } trimCommandListsUpdateActions (); } // public void kpCommandHistoryBase::clear () { #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::clear()"; #endif ::ClearPointerList (&m_undoCommandList); ::ClearPointerList (&m_redoCommandList); m_documentRestoredPosition = 0; updateActions (); } //--------------------------------------------------------------------- // protected slot void kpCommandHistoryBase::undoInternal () { #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::undoInternal()"; #endif kpCommand *undoCommand = nextUndoCommand (); if (!undoCommand) return; undoCommand->unexecute (); m_undoCommandList.erase (m_undoCommandList.begin ()); m_redoCommandList.push_front (undoCommand); #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\tdocumentRestoredPosition=" << m_documentRestoredPosition << endl; #endif if (m_documentRestoredPosition != INT_MAX) { m_documentRestoredPosition++; if (m_documentRestoredPosition == 0) emit documentRestored (); #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\t\tdocumentRestoredPosition=" << m_documentRestoredPosition << endl; #endif } } //--------------------------------------------------------------------- // protected slot void kpCommandHistoryBase::redoInternal () { #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::redoInternal()"; #endif kpCommand *redoCommand = nextRedoCommand (); if (!redoCommand) return; redoCommand->execute (); m_redoCommandList.erase (m_redoCommandList.begin ()); m_undoCommandList.push_front (redoCommand); #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\tdocumentRestoredPosition=" << m_documentRestoredPosition << endl; #endif if (m_documentRestoredPosition != INT_MAX) { m_documentRestoredPosition--; if (m_documentRestoredPosition == 0) emit documentRestored (); #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\t\tdocumentRestoredPosition=" << m_documentRestoredPosition << endl; #endif } } //--------------------------------------------------------------------- // public slot virtual void kpCommandHistoryBase::undo () { #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::undo()"; #endif undoInternal (); trimCommandListsUpdateActions (); } //--------------------------------------------------------------------- // public slot virtual void kpCommandHistoryBase::redo () { #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::redo()"; #endif redoInternal (); trimCommandListsUpdateActions (); } //--------------------------------------------------------------------- // public slot virtual void kpCommandHistoryBase::undoUpToNumber (QAction *which) { #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::undoUpToNumber(" << which << ")"; #endif for (int i = 0; i <= which->data().toInt() && !m_undoCommandList.isEmpty (); i++) { undoInternal (); } trimCommandListsUpdateActions (); } // public slot virtual void kpCommandHistoryBase::redoUpToNumber (QAction *which) { #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::redoUpToNumber(" << which << ")"; #endif for (int i = 0; i <= which->data().toInt() && !m_redoCommandList.isEmpty (); i++) { redoInternal (); } trimCommandListsUpdateActions (); } // protected QString kpCommandHistoryBase::undoActionText () const { kpCommand *undoCommand = nextUndoCommand (); if (undoCommand) return i18n ("&Undo: %1", undoCommand->name ()); else return i18n ("&Undo"); } // protected QString kpCommandHistoryBase::redoActionText () const { kpCommand *redoCommand = nextRedoCommand (); if (redoCommand) return i18n ("&Redo: %1", redoCommand->name ()); else return i18n ("&Redo"); } // protected QString kpCommandHistoryBase::undoActionToolTip () const { kpCommand *undoCommand = nextUndoCommand (); if (undoCommand) return i18n ("Undo: %1", undoCommand->name ()); else return i18n ("Undo"); } // protected QString kpCommandHistoryBase::redoActionToolTip () const { kpCommand *redoCommand = nextRedoCommand (); if (redoCommand) return i18n ("Redo: %1", redoCommand->name ()); else return i18n ("Redo"); } // protected void kpCommandHistoryBase::trimCommandListsUpdateActions () { #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::trimCommandListsUpdateActions()"; #endif trimCommandLists (); updateActions (); } // protected void kpCommandHistoryBase::trimCommandList (QLinkedList *commandList) { #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::trimCommandList()"; QTime timer; timer.start (); #endif if (!commandList) { qCCritical(kpLogCommands) << "kpCommandHistoryBase::trimCommandList() passed 0 commandList" << endl; return; } #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\tsize=" << commandList->size () << " undoMinLimit=" << m_undoMinLimit << " undoMaxLimit=" << m_undoMaxLimit << " undoMaxLimitSizeLimit=" << m_undoMaxLimitSizeLimit << endl; #endif if ((int) commandList->size () <= m_undoMinLimit) { #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\t\tsize under undoMinLimit - done"; #endif return; } #if DEBUG_KP_COMMAND_HISTORY && 0 qCDebug(kpLogCommands) << "\tsize over undoMinLimit - iterating thru cmds:"; #endif QLinkedList ::iterator it = commandList->begin (); int upto = 0; kpCommandSize::SizeType sizeSoFar = 0; while (it != commandList->end ()) { bool advanceIt = true; if (sizeSoFar <= m_undoMaxLimitSizeLimit) { sizeSoFar += (*it)->size (); } #if DEBUG_KP_COMMAND_HISTORY && 0 qCDebug(kpLogCommands) << "\t\t" << upto << ":" << " name='" << (*it)->name () << "' size=" << (*it)->size () << " sizeSoFar=" << sizeSoFar << endl; #endif if (upto >= m_undoMinLimit) { if (upto >= m_undoMaxLimit || sizeSoFar > m_undoMaxLimitSizeLimit) { #if DEBUG_KP_COMMAND_HISTORY && 0 qCDebug(kpLogCommands) << "\t\t\tkill"; #endif delete (*it); it = m_undoCommandList.erase (it); advanceIt = false; } } if (advanceIt) it++; upto++; } #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\ttook " << timer.elapsed () << "ms"; #endif } // protected void kpCommandHistoryBase::trimCommandLists () { #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::trimCommandLists()"; #endif trimCommandList (&m_undoCommandList); trimCommandList (&m_redoCommandList); #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\tdocumentRestoredPosition=" << m_documentRestoredPosition << endl; #endif if (m_documentRestoredPosition != INT_MAX) { #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\t\tundoCmdList.size=" << m_undoCommandList.size () << " redoCmdList.size=" << m_redoCommandList.size () << endl; #endif if (m_documentRestoredPosition > (int) m_redoCommandList.size () || -m_documentRestoredPosition > (int) m_undoCommandList.size ()) { #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\t\t\tinvalidate documentRestoredPosition"; #endif m_documentRestoredPosition = INT_MAX; } } } static void populatePopupMenu (QMenu *popupMenu, const QString &undoOrRedo, const QLinkedList &commandList) { if (!popupMenu) return; popupMenu->clear (); QLinkedList ::const_iterator it = commandList.begin (); int i = 0; while (i < 10 && it != commandList.end ()) { QAction *action = new QAction(i18n ("%1: %2", undoOrRedo, (*it)->name ()), popupMenu); action->setData(i); popupMenu->addAction (action); - i++, it++; + i++; + it++; } if (it != commandList.end ()) { // TODO: maybe have a scrollview show all the items instead, like KOffice in KDE 3 // LOCOMPAT: should be centered text. popupMenu->addSection (i18np ("%1 more item", "%1 more items", commandList.size () - i)); } } // protected void kpCommandHistoryBase::updateActions () { #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::updateActions()"; #endif m_actionUndo->setEnabled ((bool) nextUndoCommand ()); // Don't want to keep changing toolbar text. // TODO: As a bad side-effect, the menu doesn't have "Undo: " // anymore. In any case, the KDE4 KToolBarPopupAction // sucks in menus as it forces the clicking of a submenu. IMO, // there should be no submenu in the menu. //m_actionUndo->setText (undoActionText ()); // But in icon mode, a tooltip with context is useful. m_actionUndo->setToolTip (undoActionToolTip ()); #if DEBUG_KP_COMMAND_HISTORY QTime timer; timer.start (); #endif populatePopupMenu (m_actionUndo->menu (), i18n ("Undo"), m_undoCommandList); #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\tpopuplatePopupMenu undo=" << timer.elapsed () << "ms" << endl;; #endif m_actionRedo->setEnabled ((bool) nextRedoCommand ()); // Don't want to keep changing toolbar text. // TODO: As a bad side-effect, the menu doesn't have "Undo: " // anymore. In any case, the KDE4 KToolBarPopupAction // sucks in menus as it forces the clicking of a submenu. IMO, // there should be no submenu in the menu. //m_actionRedo->setText (redoActionText ()); // But in icon mode, a tooltip with context is useful. m_actionRedo->setToolTip (redoActionToolTip ()); #if DEBUG_KP_COMMAND_HISTORY timer.restart (); #endif populatePopupMenu (m_actionRedo->menu (), i18n ("Redo"), m_redoCommandList); #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "\tpopuplatePopupMenu redo=" << timer.elapsed () << "ms" << endl; #endif } // public kpCommand *kpCommandHistoryBase::nextUndoCommand () const { if (m_undoCommandList.isEmpty ()) return nullptr; return m_undoCommandList.first (); } // public kpCommand *kpCommandHistoryBase::nextRedoCommand () const { if (m_redoCommandList.isEmpty ()) return nullptr; return m_redoCommandList.first (); } // public void kpCommandHistoryBase::setNextUndoCommand (kpCommand *command) { #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::setNextUndoCommand(" << command << ")" << endl; #endif if (m_undoCommandList.isEmpty ()) return; delete *m_undoCommandList.begin (); *m_undoCommandList.begin () = command; trimCommandListsUpdateActions (); } // public slot virtual void kpCommandHistoryBase::documentSaved () { #if DEBUG_KP_COMMAND_HISTORY qCDebug(kpLogCommands) << "kpCommandHistoryBase::documentSaved()"; #endif m_documentRestoredPosition = 0; } diff --git a/dialogs/imagelib/kpDocumentMetaInfoDialog.cpp b/dialogs/imagelib/kpDocumentMetaInfoDialog.cpp index 1a6929c8..00a18645 100644 --- a/dialogs/imagelib/kpDocumentMetaInfoDialog.cpp +++ b/dialogs/imagelib/kpDocumentMetaInfoDialog.cpp @@ -1,766 +1,767 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define DEBUG_KP_DOCUMENT_META_INFO_DIALOG 0 #include "kpDocumentMetaInfoDialog.h" #include "kpDefs.h" #include "imagelib/kpDocumentMetaInfo.h" #include #include #include "kpLogCategories.h" #include #include #include #include #include #include #include #include #include #include struct kpDocumentMetaInfoDialogPrivate { const kpDocumentMetaInfo *originalMetaInfoPtr; QDoubleSpinBox *horizDpiInput, *vertDpiInput; QSpinBox *horizOffsetInput, *vertOffsetInput; QTableWidget *fieldsTableWidget; QPushButton *fieldsAddRowButton, *fieldsDeleteRowButton, *fieldsResetButton; }; // (shared by all dialogs, across all main windows, in a KolourPaint instance) static int LastWidth = -1, LastHeight = -1; // sync: You must keep DpiMinStep = 10 ^ (-DpiPrecision). // // You can increase the precision to reduce the chance of inadvertently changing // the resolution when converting from kpDocumentMetaInfo's "dots per meter" // to our "dots per inch" and back. It would be bad if simply going into this // dialog and pressing OK changed the resolution (it's unlikely but I still think // it might happen with the current precision). // TODO: On a related note, for many particular resolutions, if the user enters // one of them into the UI, presses OK and then comes back to the dialog, // s/he is presented with a different resolution to the one typed in. // Maybe make DotsPerMeter[XY] be of type "double" instead of "int" to // solve this problem? // // Of course, if you increase the precision too much, the minimum step will // become so small that it will start experiencing floating point inaccuracies // esp. since we use it for the "DpiUnspecified" hack. static const int DpiPrecision = 3; static const double DpiMinStep = 0.001; static const double DpiLegalMin = kpDocumentMetaInfo::MinDotsPerMeter / KP_INCHES_PER_METER; // Out of range represents unspecified DPI. static const double DpiUnspecified = ::DpiLegalMin - ::DpiMinStep; static const double DpiInputMin = ::DpiUnspecified; static const double DpiInputMax = kpDocumentMetaInfo::MaxDotsPerMeter / KP_INCHES_PER_METER; // The increment the DPI spinboxes jump by when they're clicked. // // We make this relatively big since people don't usually just increase their // DPIs by 1 or so -- they are usually changing from say, 72, to 96. // // Obviously, making it equal to DpiMinStep is too slow a UI. Therefore, with // our big setting, the user will still have to manually change the value in // the spinbox, using the keyboard, after all their clicking to ensure it is // exactly the value they want. static const double DpiInputStep = 10; // TODO: Halve groupbox layout margins in every other file since it doesn't // seem to be need in Qt4. kpDocumentMetaInfoDialog::kpDocumentMetaInfoDialog ( const kpDocumentMetaInfo *docMetaInfo, QWidget *parent) : QDialog (parent), d (new kpDocumentMetaInfoDialogPrivate ()) { d->originalMetaInfoPtr = docMetaInfo; setWindowTitle (i18nc ("@title:window", "Document Properties")); QDialogButtonBox * buttons = new QDialogButtonBox (QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); connect (buttons, SIGNAL (accepted()), this, SLOT (accept())); connect (buttons, SIGNAL (rejected()), this, SLOT (reject())); QWidget *baseWidget = new QWidget (this); QVBoxLayout *dialogLayout = new QVBoxLayout (this); dialogLayout->addWidget (baseWidget); dialogLayout->addWidget (buttons); // // DPI Group Box // Q_ASSERT (::DpiInputMin < ::DpiInputMax); QGroupBox *dpiGroupBox = new QGroupBox(i18n("Dots &Per Inch (DPI)"), baseWidget); d->horizDpiInput = new QDoubleSpinBox(dpiGroupBox); d->horizDpiInput->setRange(::DpiInputMin, ::DpiInputMax); d->horizDpiInput->setValue(0.0); d->horizDpiInput->setSingleStep(::DpiInputStep); d->horizDpiInput->setDecimals(::DpiPrecision); d->horizDpiInput->setSpecialValueText(i18n("Unspecified")); QLabel *dpiXLabel = new QLabel ( i18nc ("Horizontal DPI 'x' Vertical DPI", " x "), dpiGroupBox); dpiXLabel->setAlignment (Qt::AlignCenter); d->vertDpiInput = new QDoubleSpinBox(dpiGroupBox); d->vertDpiInput->setRange(::DpiInputMin, ::DpiInputMax); d->vertDpiInput->setValue(0.0); d->vertDpiInput->setSingleStep(::DpiInputStep); d->vertDpiInput->setDecimals(::DpiPrecision); d->vertDpiInput->setSpecialValueText(i18n("Unspecified")); QGridLayout *dpiLay = new QGridLayout(dpiGroupBox); dpiLay->addWidget(new QLabel(i18n("Horizontal:")), 0, 0, Qt::AlignHCenter); dpiLay->addWidget(d->horizDpiInput, 1, 0); dpiLay->addWidget(dpiXLabel, 0, 1); dpiLay->addWidget(new QLabel(i18n("Vertical:")), 0, 2, Qt::AlignHCenter); dpiLay->addWidget(d->vertDpiInput, 1, 2); dpiLay->setRowStretch(2, 1); dpiGroupBox->setWhatsThis ( i18n ( "" "

Dots Per Inch (DPI) specifies the number of pixels" " of the image that should be printed inside one inch (2.54cm).

" "

The higher the image's DPI, the smaller the printed image." " Note that your printer is unlikely to produce high" " quality prints if you increase this to more than 300 or 600 DPI," " depending on the printer.

" "

If you would like to print the image so that it is the same" " size as it is displayed on the screen, set the image's DPI" " values to be the same as the screen's.

" // TODO: This is currently not true! // See "96dpi" TODO in kpMainWindow::sendPixmapToPrinter(). // This also why we don't try to report the current screen DPI // for the above paragraph. "

If either DPI value is Unspecified, the image will also" " be printed to be the same size as on the screen.

" "

Not all image formats support DPI values. If the format you" " save in does not support them, they will not be saved.

" "
" )); // // Offset Group Box // QGroupBox *offsetGroupBox = new QGroupBox(i18n ("O&ffset"), baseWidget); d->horizOffsetInput = new QSpinBox; d->horizOffsetInput->setRange(kpDocumentMetaInfo::MinOffset, kpDocumentMetaInfo::MaxOffset); d->vertOffsetInput = new QSpinBox; d->vertOffsetInput->setRange(kpDocumentMetaInfo::MinOffset, kpDocumentMetaInfo::MaxOffset); QGridLayout *offsetLay = new QGridLayout(offsetGroupBox); offsetLay->addWidget(new QLabel(i18n("Horizontal:")), 0, 0, Qt::AlignHCenter); offsetLay->addWidget(d->horizOffsetInput, 1, 0); offsetLay->addWidget(new QLabel(i18n("Vertical:")), 0, 1, Qt::AlignHCenter); offsetLay->addWidget(d->vertOffsetInput, 1, 1); offsetLay->setRowStretch (2, 1); offsetGroupBox->setWhatsThis ( i18n ( "" "

The Offset is the relative position where this image" " should be placed, compared to other images.

" "

Not all image formats support the Offset feature." " If the format you save in does not support it, the values" " specified here will not be saved.

" "
" )); // // Fields Group Box // QGroupBox *fieldsGroupBox = new QGroupBox (i18n ("&Text Fields"), baseWidget); d->fieldsTableWidget = new QTableWidget (fieldsGroupBox); d->fieldsTableWidget->setEditTriggers(QAbstractItemView::AllEditTriggers); connect (d->fieldsTableWidget, SIGNAL (currentCellChanged(int,int,int,int)), SLOT (slotFieldsCurrentCellChanged(int,int,int,int))); connect (d->fieldsTableWidget, SIGNAL (itemChanged(QTableWidgetItem*)), SLOT (slotFieldsItemChanged(QTableWidgetItem*))); d->fieldsAddRowButton = new QPushButton (i18n ("&Add Row"), fieldsGroupBox); connect (d->fieldsAddRowButton, SIGNAL (clicked()), SLOT (slotFieldsAddRowButtonClicked())); d->fieldsDeleteRowButton = new QPushButton (i18n ("&Delete Row"), fieldsGroupBox); connect (d->fieldsDeleteRowButton, SIGNAL (clicked()), SLOT (slotFieldsDeleteRowButtonClicked())); d->fieldsResetButton = new QPushButton (i18n ("&Reset"), fieldsGroupBox); connect (d->fieldsResetButton, SIGNAL (clicked()), SLOT (setUIToOriginalMetaInfo())); QHBoxLayout *fieldsButtonsLayout = new QHBoxLayout (); fieldsButtonsLayout->addWidget (d->fieldsAddRowButton); fieldsButtonsLayout->addWidget (d->fieldsDeleteRowButton); fieldsButtonsLayout->addStretch (); fieldsButtonsLayout->addWidget (d->fieldsResetButton); QVBoxLayout *fieldsLayout = new QVBoxLayout (fieldsGroupBox); fieldsLayout->addWidget (d->fieldsTableWidget); fieldsLayout->addLayout (fieldsButtonsLayout); fieldsGroupBox->setWhatsThis ( i18n ( "" "

Text Fields provide extra information about the image." " This is probably a comment area that you can freely write any text in.

" "

However, this is format-specific so the fields could theoretically be" " computer-interpreted data - that you should not modify -" " but this is unlikely.

" "

Not all image formats support Text Fields. If the format" " you save in does not support them, they will not be saved.

" "
" )); // // Global Layout // QGridLayout *baseLayout = new QGridLayout (baseWidget); baseLayout->setMargin (0); // Col 0 baseLayout->addWidget (dpiGroupBox, 0, 0); baseLayout->addWidget (offsetGroupBox, 1, 0); // Col 1 baseLayout->addWidget (fieldsGroupBox, 0, 1, 2/*row span*/, 1/*col span*/); baseLayout->setColumnStretch (1, 1/*stretch*/); // // Remaining UI Setup // setUIToOriginalMetaInfo (); if (::LastWidth > 0 && ::LastHeight > 0) resize (::LastWidth, ::LastHeight); } //--------------------------------------------------------------------- kpDocumentMetaInfoDialog::~kpDocumentMetaInfoDialog () { - ::LastWidth = width (), ::LastHeight = height (); + ::LastWidth = width (); + ::LastHeight = height (); delete d; } //--------------------------------------------------------------------- // private void kpDocumentMetaInfoDialog::editCell (int r, int c) { d->fieldsTableWidget->setCurrentCell (r, c); d->fieldsTableWidget->editItem (d->fieldsTableWidget->item (r, c)); } //--------------------------------------------------------------------- // private slot void kpDocumentMetaInfoDialog::setUIToOriginalMetaInfo () { // Set DPI spinboxes. d->horizDpiInput->setValue (d->originalMetaInfoPtr->dotsPerMeterX () / KP_INCHES_PER_METER); d->vertDpiInput->setValue (d->originalMetaInfoPtr->dotsPerMeterY () / KP_INCHES_PER_METER); // Set Offset spinboxes. d->horizOffsetInput->setValue (d->originalMetaInfoPtr->offset ().x ()); d->vertOffsetInput->setValue (d->originalMetaInfoPtr->offset ().y ()); // Set Text Fields. // // Block itemChanged() signal as slotFieldsItemChanged() should not get called // when rows are half-created. const bool b = d->fieldsTableWidget->blockSignals (true); { d->fieldsTableWidget->clear (); d->fieldsTableWidget->setRowCount (d->originalMetaInfoPtr->textKeys ().size ()); d->fieldsTableWidget->setColumnCount (2); QStringList fieldsHeader; fieldsHeader << i18n ("Key") << i18n ("Value"); d->fieldsTableWidget->setHorizontalHeaderLabels (fieldsHeader); int row = 0; foreach (const QString &key, d->originalMetaInfoPtr->textKeys ()) { d->fieldsTableWidget->setItem (row, 0/*1st col*/, new QTableWidgetItem (key)); d->fieldsTableWidget->setItem (row, 1/*2nd col*/, new QTableWidgetItem (d->originalMetaInfoPtr->text (key))); row++; } fieldsAppendEmptyRow (); } d->fieldsTableWidget->blockSignals (b); editCell (0/*row*/, 0/*col*/); enableFieldsDeleteRowButtonIfShould (); } //--------------------------------------------------------------------- // public bool kpDocumentMetaInfoDialog::isNoOp () const { return (metaInfo () == *d->originalMetaInfoPtr); } //--------------------------------------------------------------------- // public kpDocumentMetaInfo kpDocumentMetaInfoDialog::originalMetaInfo () const { return *d->originalMetaInfoPtr; } // public kpDocumentMetaInfo kpDocumentMetaInfoDialog::metaInfo ( QString *errorMessage) const { if (errorMessage) { // No errors to start with. *errorMessage = QString (); } kpDocumentMetaInfo ret; if (d->horizDpiInput->value () < ::DpiLegalMin) ret.setDotsPerMeterX (0/*unspecified*/); else ret.setDotsPerMeterX (qRound (d->horizDpiInput->value () * KP_INCHES_PER_METER)); if (d->vertDpiInput->value () < ::DpiLegalMin) ret.setDotsPerMeterY (0/*unspecified*/); else ret.setDotsPerMeterY (qRound (d->vertDpiInput->value () * KP_INCHES_PER_METER)); ret.setOffset (QPoint (d->horizOffsetInput->value (), d->vertOffsetInput->value ())); for (int r = 0; r < d->fieldsTableWidget->rowCount (); r++) { const QString key = d->fieldsTableWidget->item (r, 0)->text (); const QString value = d->fieldsTableWidget->item (r, 1)->text (); // Empty key? if (key.isEmpty ()) { // Empty value too? if (value.isEmpty ()) { // Ignore empty row. continue; } // Value without a key? else { if (errorMessage) { *errorMessage = ki18n ("The text value \"%1\" on line %2 requires a key.") .subs (value).subs (r + 1/*count from 1*/).toString (); // Print only 1 error message per method invocation. errorMessage = nullptr; } // Ignore. continue; } } // Duplicate key? if (ret.textKeys ().contains (key)) { if (errorMessage) { int q; for (q = 0; q < r; q++) { if (d->fieldsTableWidget->item (q, 0)->text () == key) break; } Q_ASSERT (q != r); *errorMessage = ki18n ("All text keys must be unique. The text key \"%1\"" " on lines %2 and %3 are identical.") .subs (key) .subs (q + 1/*count from 1*/) .subs (r + 1/*count from 1*/) .toString (); // Print only 1 error message per method invocation. errorMessage = nullptr; } // Ignore this duplicate - keep the first value of the key. continue; } ret.setText (key, value); } // for (r = 0; r < table widget rows; r++) { return ret; } // private void kpDocumentMetaInfoDialog::fieldsUpdateVerticalHeader () { QStringList vertLabels; for (int r = 1; r <= d->fieldsTableWidget->rowCount (); r++) vertLabels << QString::number (r); d->fieldsTableWidget->setVerticalHeaderLabels (vertLabels); } // private void kpDocumentMetaInfoDialog::fieldsAddEmptyRow (int atRow) { // Block itemChanged() signal as slotFieldsItemChanged() should not get called // when rows are half-created. const bool b = d->fieldsTableWidget->blockSignals (true); { d->fieldsTableWidget->insertRow (atRow); d->fieldsTableWidget->setItem (atRow, 0, new QTableWidgetItem (QString ())); d->fieldsTableWidget->setItem (atRow, 1, new QTableWidgetItem (QString ())); } d->fieldsTableWidget->blockSignals (b); // Hack around Qt's failure to redraw these sometimes. fieldsUpdateVerticalHeader (); enableFieldsDeleteRowButtonIfShould (); } // private void kpDocumentMetaInfoDialog::fieldsAppendEmptyRow () { fieldsAddEmptyRow (d->fieldsTableWidget->rowCount ()); } // private bool kpDocumentMetaInfoDialog::isFieldsRowDeleteable (int row) const { // Can't delete no row and can't delete last (always blank) row, which // is used to make it easy for the user to add rows without pressing // the "Add" button explicitly. return (row >= 0 && row < d->fieldsTableWidget->rowCount () - 1); } // private void kpDocumentMetaInfoDialog::fieldsDeleteRow (int r) { #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "kpDocumentMetaInfoDialog::fieldsDeleteRow(" << "row=" << r << ")" << " currentRow=" << d->fieldsTableWidget->currentRow () << endl; #endif Q_ASSERT (isFieldsRowDeleteable (r)); if (r == d->fieldsTableWidget->currentRow ()) { // Assertion follows from previous assertion. const int newRow = r + 1; #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "\tnewRow=" << newRow; #endif Q_ASSERT (newRow < d->fieldsTableWidget->rowCount ()); int newCol = d->fieldsTableWidget->currentColumn (); #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "\tnewCol=" << newCol; #endif if (newCol != 0 && newCol != 1) { newCol = 0; #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "\t\tcorrecting to " << newCol; #endif } // WARNING: You must call this _before_ deleting the row. Else, you'll // trigger a Qt bug where if the editor is active on the row to // be deleted, it might crash. To reproduce, move this line to // after removeRow() (and subtract 1 from newRow) and // press the "Delete Row" button several times in succession // very quickly. // // TODO: This usually results in a redraw error if the scrollbar scrolls // after deleting the 2nd last row. Qt bug. editCell (newRow, newCol); } d->fieldsTableWidget->removeRow (r); fieldsUpdateVerticalHeader (); enableFieldsDeleteRowButtonIfShould (); } // private void kpDocumentMetaInfoDialog::enableFieldsDeleteRowButtonIfShould () { #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "kpDocumentMetaInfoDialog::enableFieldsDeleteRowButtonIfShould()"; #endif const int r = d->fieldsTableWidget->currentRow (); #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "\tr=" << r; #endif d->fieldsDeleteRowButton->setEnabled (isFieldsRowDeleteable (r)); } // private slot void kpDocumentMetaInfoDialog::slotFieldsCurrentCellChanged (int row, int col, int oldRow, int oldCol) { #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "kpDocumentMetaInfoDialog::slotFieldsCurrentCellChanged(" << "row=" << row << ",col=" << col << ",oldRow=" << oldRow << ",oldCol=" << oldCol << ")" << endl; #endif (void) row; (void) col; (void) oldRow; (void) oldCol; enableFieldsDeleteRowButtonIfShould (); } //--------------------------------------------------------------------- // private slot void kpDocumentMetaInfoDialog::slotFieldsItemChanged (QTableWidgetItem *it) { #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "kpDocumentMetaInfoDialog::slotFieldsItemChanged(" << "item=" << it << ") rows=" << d->fieldsTableWidget->rowCount () << endl; #endif const int r = d->fieldsTableWidget->row (it); #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "\tr=" << r; #endif Q_ASSERT (r >= 0 && r < d->fieldsTableWidget->rowCount ()); const QString key = d->fieldsTableWidget->item (r, 0)->text (); #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << " key='" << key << "'"; #endif const QString value = d->fieldsTableWidget->item (r, 1)->text (); #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << " value='" << value << "'"; #endif // At the last row? if (r == d->fieldsTableWidget->rowCount () - 1) { // Typed some text? if (!key.isEmpty () || !value.isEmpty ()) { // LOTODO: If we're called due to the cell's text being finalized // as a result of the user pressing the "Add Row" button, // should this really append a row since // slotFieldsAddRowButtonClicked() button is going to add // another one? That's two rows when the user only clicked // that button once! fieldsAppendEmptyRow (); } } // Deletes a row if it is emptied of text. #if 0 // At any normal row? else { // Emptied all the text? if (key.isEmpty () && value.isEmpty ()) { // This crashes when the user tabs away after the text is deleted. // We could use a single shot but that's asking for trouble depending // on what happens between us and the single shot. // // In any case, disabling this makes us more consistent with // "Add Row" which allows us to add empty rows. //fieldsDeleteRow (r); } } #endif } //--------------------------------------------------------------------- // private slot void kpDocumentMetaInfoDialog::slotFieldsAddRowButtonClicked () { #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "kpDocumentMetaInfoDialog::slotFieldsAddRowButtonClicked()" << endl; #endif const int r = d->fieldsTableWidget->currentRow (); #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "\tr=" << r; #endif // (if no row is selected, r = -1) fieldsAddEmptyRow (r + 1); // Edit the key of this new row (column 0). // No one edits the value first (column 1). editCell ((r + 1)/*row*/, 0/*col*/); } //--------------------------------------------------------------------- // private slot void kpDocumentMetaInfoDialog::slotFieldsDeleteRowButtonClicked () { #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "kpDocumentMetaInfoDialog::slotFieldsDeleteRowButtonClicked()" << endl; #endif const int r = d->fieldsTableWidget->currentRow (); #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG qCDebug(kpLogDialogs) << "\tr=" << r; #endif Q_ASSERT (isFieldsRowDeleteable (r)); fieldsDeleteRow (r); } // private slot virtual [base QDialog] void kpDocumentMetaInfoDialog::accept () { // Validate text fields. QString errorMessage; (void) metaInfo (&errorMessage); if (!errorMessage.isEmpty ()) { KMessageBox::sorry (this, errorMessage, i18nc ("@title:window", "Invalid Text Fields")); return; } QDialog::accept (); } diff --git a/dialogs/imagelib/transforms/kpTransformRotateDialog.cpp b/dialogs/imagelib/transforms/kpTransformRotateDialog.cpp index 97a89c46..0b486f98 100644 --- a/dialogs/imagelib/transforms/kpTransformRotateDialog.cpp +++ b/dialogs/imagelib/transforms/kpTransformRotateDialog.cpp @@ -1,309 +1,310 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define DEBUG_KP_TOOL_ROTATE 0 #include "kpTransformRotateDialog.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "pixmapfx/kpPixmapFX.h" #include "tools/kpTool.h" #include "environments/dialogs/imagelib/transforms/kpTransformDialogEnvironment.h" #include "views/manager/kpViewManager.h" #include "kpLogCategories.h" #include #include #include #include #include #include #include #include #include #include #include #include // private static int kpTransformRotateDialog::s_lastWidth = -1, kpTransformRotateDialog::s_lastHeight = -1; // private static bool kpTransformRotateDialog::s_lastIsClockwise = true; int kpTransformRotateDialog::s_lastAngleCustom = 0; kpTransformRotateDialog::kpTransformRotateDialog (bool actOnSelection, kpTransformDialogEnvironment *_env, QWidget *parent) : kpTransformPreviewDialog (kpTransformPreviewDialog::AllFeatures, false/*don't reserve top row*/, actOnSelection ? i18nc ("@title:window", "Rotate Selection") : i18nc ("@title:window", "Rotate Image"), i18n ("After rotate:"), actOnSelection, _env, parent) { s_lastAngleCustom = 0; createDirectionGroupBox (); createAngleGroupBox (); if (s_lastWidth > 0 && s_lastHeight > 0) resize (s_lastWidth, s_lastHeight); slotAngleCustomRadioButtonToggled (m_angleCustomRadioButton->isChecked ()); slotUpdate (); } kpTransformRotateDialog::~kpTransformRotateDialog () { - s_lastWidth = width (), s_lastHeight = height (); + s_lastWidth = width (); + s_lastHeight = height (); } // private void kpTransformRotateDialog::createDirectionGroupBox () { QGroupBox *directionGroupBox = new QGroupBox (i18n ("Direction"), mainWidget ()); addCustomWidget (directionGroupBox); QLabel *antiClockwisePixmapLabel = new QLabel (directionGroupBox); antiClockwisePixmapLabel->setPixmap (UserIcon ("image_rotate_anticlockwise")); QLabel *clockwisePixmapLabel = new QLabel (directionGroupBox); clockwisePixmapLabel->setPixmap (UserIcon ("image_rotate_clockwise")); m_antiClockwiseRadioButton = new QRadioButton (i18n ("Cou&nterclockwise"), directionGroupBox); m_clockwiseRadioButton = new QRadioButton (i18n ("C&lockwise"), directionGroupBox); m_antiClockwiseRadioButton->setChecked (!s_lastIsClockwise); m_clockwiseRadioButton->setChecked (s_lastIsClockwise); QGridLayout *directionLayout = new QGridLayout (directionGroupBox ); directionLayout->addWidget (antiClockwisePixmapLabel, 0, 0, Qt::AlignCenter); directionLayout->addWidget (clockwisePixmapLabel, 0, 1, Qt::AlignCenter); directionLayout->addWidget (m_antiClockwiseRadioButton, 1, 0, Qt::AlignCenter); directionLayout->addWidget (m_clockwiseRadioButton, 1, 1, Qt::AlignCenter); connect (m_antiClockwiseRadioButton, SIGNAL (toggled(bool)), this, SLOT (slotUpdate())); connect (m_clockwiseRadioButton, SIGNAL (toggled(bool)), this, SLOT (slotUpdate())); } // private void kpTransformRotateDialog::createAngleGroupBox () { QGroupBox *angleGroupBox = new QGroupBox (i18n ("Angle"), mainWidget ()); addCustomWidget (angleGroupBox); m_angle90RadioButton = new QRadioButton (i18n ("90 °rees"), angleGroupBox); m_angle180RadioButton = new QRadioButton (i18n ("180 d&egrees"), angleGroupBox); m_angle270RadioButton = new QRadioButton (i18n ("270 de&grees"), angleGroupBox); m_angleCustomRadioButton = new QRadioButton (i18n ("C&ustom:"), angleGroupBox); m_angleCustomInput = new QSpinBox; m_angleCustomInput->setMinimum(-359); m_angleCustomInput->setMaximum(+359); m_angleCustomInput->setValue(s_lastAngleCustom); QLabel *degreesLabel = new QLabel (i18n ("degrees"), angleGroupBox); m_angleCustomRadioButton->setChecked (true); QGridLayout *angleLayout = new QGridLayout (angleGroupBox ); angleLayout->addWidget (m_angle90RadioButton, 0, 0, 1, 3); angleLayout->addWidget (m_angle180RadioButton, 1, 0, 1, 3); angleLayout->addWidget (m_angle270RadioButton, 2, 0, 1, 3); angleLayout->addWidget (m_angleCustomRadioButton, 3, 0); angleLayout->addWidget (m_angleCustomInput, 3, 1); angleLayout->addWidget (degreesLabel, 3, 2); angleLayout->setColumnStretch (1, 2); // Stretch Custom Angle Input connect (m_angle90RadioButton, SIGNAL (toggled(bool)), this, SLOT (slotUpdate())); connect (m_angle180RadioButton, SIGNAL (toggled(bool)), this, SLOT (slotUpdate())); connect (m_angle270RadioButton, SIGNAL (toggled(bool)), this, SLOT (slotUpdate())); connect (m_angleCustomRadioButton, SIGNAL (toggled(bool)), this, SLOT (slotAngleCustomRadioButtonToggled(bool))); connect (m_angleCustomRadioButton, SIGNAL (toggled(bool)), this, SLOT (slotUpdate())); connect (m_angleCustomInput, SIGNAL (valueChanged(int)), this, SLOT (slotUpdate())); } // public virtual [base kpTransformPreviewDialog] bool kpTransformRotateDialog::isNoOp () const { return (angle () == 0); } // public int kpTransformRotateDialog::angle () const { int retAngle; if (m_angle90RadioButton->isChecked ()) retAngle = 90; else if (m_angle180RadioButton->isChecked ()) retAngle = 180; else if (m_angle270RadioButton->isChecked ()) retAngle = 270; else // if (m_angleCustomRadioButton->isChecked ()) retAngle = m_angleCustomInput->value (); if (m_antiClockwiseRadioButton->isChecked ()) retAngle *= -1; if (retAngle < 0) retAngle += ((0 - retAngle) / 360 + 1) * 360; if (retAngle >= 360) retAngle -= ((retAngle - 360) / 360 + 1) * 360; return retAngle; } // private virtual [base kpTransformPreviewDialog] QSize kpTransformRotateDialog::newDimensions () const { QMatrix matrix = kpPixmapFX::rotateMatrix (m_oldWidth, m_oldHeight, angle ()); QRect rect = matrix.mapRect (QRect (0, 0, m_oldWidth, m_oldHeight)); return rect.size (); } // private virtual [base kpTransformPreviewDialog] QImage kpTransformRotateDialog::transformPixmap (const QImage &image, int targetWidth, int targetHeight) const { return kpPixmapFX::rotate (image, angle (), m_environ->backgroundColor (m_actOnSelection), targetWidth, targetHeight); } // private slot void kpTransformRotateDialog::slotAngleCustomRadioButtonToggled (bool isChecked) { m_angleCustomInput->setEnabled (isChecked); if (isChecked) m_angleCustomInput->setFocus(); } // private slot virtual [base kpTransformPreviewDialog] void kpTransformRotateDialog::slotUpdate () { s_lastIsClockwise = m_clockwiseRadioButton->isChecked (); s_lastAngleCustom = m_angleCustomInput->value (); kpTransformPreviewDialog::slotUpdate (); } // private slot virtual [base QDialog] void kpTransformRotateDialog::accept () { KLocalizedString message; QString caption, continueButtonText; if (document ()->selection ()) { if (!document ()->textSelection ()) { message = ki18n ("

Rotating the selection to %1x%2" " may take a substantial amount of memory." " This can reduce system" " responsiveness and cause other application resource" " problems.

" "

Are you sure you want to rotate the selection?

"); caption = i18nc ("@title:window", "Rotate Selection?"); continueButtonText = i18n ("Rotat&e Selection"); } } else { message = ki18n ("

Rotating the image to %1x%2" " may take a substantial amount of memory." " This can reduce system" " responsiveness and cause other application resource" " problems.

" "

Are you sure you want to rotate the image?

"); caption = i18nc ("@title:window", "Rotate Image?"); continueButtonText = i18n ("Rotat&e Image"); } const int newWidth = newDimensions ().width (); const int newHeight = newDimensions ().height (); if (kpTool::warnIfBigImageSize (m_oldWidth, m_oldHeight, newWidth, newHeight, message.subs (newWidth).subs (newHeight).toString (), caption, continueButtonText, this)) { QDialog::accept (); } } diff --git a/dialogs/imagelib/transforms/kpTransformSkewDialog.cpp b/dialogs/imagelib/transforms/kpTransformSkewDialog.cpp index 94a318b7..4548311b 100644 --- a/dialogs/imagelib/transforms/kpTransformSkewDialog.cpp +++ b/dialogs/imagelib/transforms/kpTransformSkewDialog.cpp @@ -1,293 +1,294 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define DEBUG_KP_TOOL_SKEW 0 #define DEBUG_KP_TOOL_SKEW_DIALOG 0 #include "dialogs/imagelib/transforms/kpTransformSkewDialog.h" #include #include #include #include #include #include #include #include #include #include "kpLogCategories.h" #include #include #include "kpDefs.h" #include "document/kpDocument.h" #include "pixmapfx/kpPixmapFX.h" #include "tools/kpTool.h" #include "environments/dialogs/imagelib/transforms/kpTransformDialogEnvironment.h" // private static int kpTransformSkewDialog::s_lastWidth = -1, kpTransformSkewDialog::s_lastHeight = -1; // private static int kpTransformSkewDialog::s_lastHorizontalAngle = 0, kpTransformSkewDialog::s_lastVerticalAngle = 0; kpTransformSkewDialog::kpTransformSkewDialog (bool actOnSelection, kpTransformDialogEnvironment *_env, QWidget *parent) : kpTransformPreviewDialog (kpTransformPreviewDialog::AllFeatures, false/*don't reserve top row*/, actOnSelection ? i18nc ("@title:window", "Skew Selection") : i18nc ("@title:window", "Skew Image"), i18n ("After skew:"), actOnSelection, _env, parent) { // Too confusing - disable for now s_lastHorizontalAngle = s_lastVerticalAngle = 0; createAngleGroupBox (); if (s_lastWidth > 0 && s_lastHeight > 0) resize (s_lastWidth, s_lastHeight); slotUpdate (); m_horizontalSkewInput->setFocus (); } kpTransformSkewDialog::~kpTransformSkewDialog () { - s_lastWidth = width (), s_lastHeight = height (); + s_lastWidth = width (); + s_lastHeight = height (); } // private void kpTransformSkewDialog::createAngleGroupBox () { QGroupBox *angleGroupBox = new QGroupBox (i18n ("Angle"), mainWidget ()); addCustomWidget (angleGroupBox); QLabel *horizontalSkewPixmapLabel = new QLabel (angleGroupBox); horizontalSkewPixmapLabel->setPixmap (UserIcon ("image_skew_horizontal")); QLabel *horizontalSkewLabel = new QLabel (i18n ("&Horizontal:"), angleGroupBox); m_horizontalSkewInput = new QSpinBox; m_horizontalSkewInput->setValue(s_lastHorizontalAngle); m_horizontalSkewInput->setMinimum(-89); m_horizontalSkewInput->setMaximum(+89); QLabel *horizontalSkewDegreesLabel = new QLabel (i18n ("degrees"), angleGroupBox); QLabel *verticalSkewPixmapLabel = new QLabel (angleGroupBox); verticalSkewPixmapLabel->setPixmap (UserIcon ("image_skew_vertical")); QLabel *verticalSkewLabel = new QLabel (i18n ("&Vertical:"), angleGroupBox); m_verticalSkewInput = new QSpinBox; m_verticalSkewInput->setValue(s_lastVerticalAngle); m_verticalSkewInput->setMinimum(-89); m_verticalSkewInput->setMaximum(+89); QLabel *verticalSkewDegreesLabel = new QLabel (i18n ("degrees"), angleGroupBox); horizontalSkewLabel->setBuddy (m_horizontalSkewInput); verticalSkewLabel->setBuddy (m_verticalSkewInput); QGridLayout *angleLayout = new QGridLayout (angleGroupBox); angleLayout->addWidget (horizontalSkewPixmapLabel, 0, 0); angleLayout->addWidget (horizontalSkewLabel, 0, 1); angleLayout->addWidget (m_horizontalSkewInput, 0, 2, Qt::AlignVCenter); angleLayout->addWidget (horizontalSkewDegreesLabel, 0, 3); angleLayout->addWidget (verticalSkewPixmapLabel, 1, 0); angleLayout->addWidget (verticalSkewLabel, 1, 1); angleLayout->addWidget (m_verticalSkewInput, 1, 2, Qt::AlignVCenter); angleLayout->addWidget (verticalSkewDegreesLabel, 1, 3); connect (m_horizontalSkewInput, SIGNAL (valueChanged(int)), this, SLOT (slotUpdate())); connect (m_verticalSkewInput, SIGNAL (valueChanged(int)), this, SLOT (slotUpdate())); } // private virtual [base kpTransformPreviewDialog] QSize kpTransformSkewDialog::newDimensions () const { kpDocument *doc = document (); Q_ASSERT (doc); QMatrix skewMatrix = kpPixmapFX::skewMatrix (doc->image (), horizontalAngleForPixmapFX (), verticalAngleForPixmapFX ()); QRect skewRect = skewMatrix.mapRect (doc->rect (m_actOnSelection)); return QSize (skewRect.width (), skewRect.height ()); } // private virtual [base kpTransformPreviewDialog] QImage kpTransformSkewDialog::transformPixmap (const QImage &image, int targetWidth, int targetHeight) const { return kpPixmapFX::skew (image, horizontalAngleForPixmapFX (), verticalAngleForPixmapFX (), m_environ->backgroundColor (m_actOnSelection), targetWidth, targetHeight); } // private void kpTransformSkewDialog::updateLastAngles () { s_lastHorizontalAngle = horizontalAngle (); s_lastVerticalAngle = verticalAngle (); } // private slot virtual [base kpTransformPreviewDialog] void kpTransformSkewDialog::slotUpdate () { updateLastAngles (); kpTransformPreviewDialog::slotUpdate (); } // public int kpTransformSkewDialog::horizontalAngle () const { return m_horizontalSkewInput->value (); } // public int kpTransformSkewDialog::verticalAngle () const { return m_verticalSkewInput->value (); } // public static int kpTransformSkewDialog::horizontalAngleForPixmapFX (int hangle) { return -hangle; } // public static int kpTransformSkewDialog::verticalAngleForPixmapFX (int vangle) { return -vangle; } // public int kpTransformSkewDialog::horizontalAngleForPixmapFX () const { return kpTransformSkewDialog::horizontalAngleForPixmapFX (horizontalAngle ()); } // public int kpTransformSkewDialog::verticalAngleForPixmapFX () const { return kpTransformSkewDialog::verticalAngleForPixmapFX (verticalAngle ()); } // public virtual [base kpTransformPreviewDialog] bool kpTransformSkewDialog::isNoOp () const { return (horizontalAngle () == 0) && (verticalAngle () == 0); } // private slot virtual [base QDialog] void kpTransformSkewDialog::accept () { KLocalizedString message; QString caption, continueButtonText; if (document ()->selection ()) { if (!document ()->textSelection ()) { message = ki18n ("

Skewing the selection to %1x%2" " may take a substantial amount of memory." " This can reduce system" " responsiveness and cause other application resource" " problems.

" "

Are you sure you want to skew the selection?

"); caption = i18nc ("@title:window", "Skew Selection?"); continueButtonText = i18n ("Sk&ew Selection"); } } else { message = ki18n ("

Skewing the image to %1x%2" " may take a substantial amount of memory." " This can reduce system" " responsiveness and cause other application resource" " problems.

" "

Are you sure you want to skew the image?

"); caption = i18nc ("@title:window", "Skew Image?"); continueButtonText = i18n ("Sk&ew Image"); } const int newWidth = newDimensions ().width (); const int newHeight = newDimensions ().height (); if (kpTool::warnIfBigImageSize (m_oldWidth, m_oldHeight, newWidth, newHeight, message.subs (newWidth).subs (newHeight).toString (), caption, continueButtonText, this)) { QDialog::accept (); } } diff --git a/document/kpDocument.cpp b/document/kpDocument.cpp index 137b9ee8..b2c339fa 100644 --- a/document/kpDocument.cpp +++ b/document/kpDocument.cpp @@ -1,464 +1,466 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define DEBUG_KP_DOCUMENT 0 #include "kpDocument.h" #include "kpDocumentPrivate.h" #include "layers/selections/kpAbstractSelection.h" #include "layers/selections/image/kpAbstractImageSelection.h" #include "imagelib/kpColor.h" #include "widgets/toolbars/kpColorToolBar.h" #include "kpDefs.h" #include "environments/document/kpDocumentEnvironment.h" #include "document/kpDocumentSaveOptions.h" #include "imagelib/kpDocumentMetaInfo.h" #include "imagelib/effects/kpEffectReduceColors.h" #include "pixmapfx/kpPixmapFX.h" #include "tools/kpTool.h" #include "widgets/toolbars/kpToolToolBar.h" #include "lgpl/generic/kpUrlFormatter.h" #include #include "kpLogCategories.h" #include // kdelibs4support #include #include #include #include #include #include #include #include #include #include #include #include //--------------------------------------------------------------------- kpDocument::kpDocument (int w, int h, kpDocumentEnvironment *environ) : QObject (), m_constructorWidth (w), m_constructorHeight (h), m_isFromURL (false), m_savedAtLeastOnceBefore (false), m_saveOptions (new kpDocumentSaveOptions ()), m_metaInfo (new kpDocumentMetaInfo ()), m_modified (false), m_selection (nullptr), m_oldWidth (-1), m_oldHeight (-1), d (new kpDocumentPrivate ()) { #if DEBUG_KP_DOCUMENT && 0 qCDebug(kpLogDocument) << "kpDocument::kpDocument (" << w << "," << h << ")"; #endif m_image = new kpImage(w, h, QImage::Format_ARGB32_Premultiplied); m_image->fill(QColor(Qt::white).rgb()); d->environ = environ; } //--------------------------------------------------------------------- kpDocument::~kpDocument () { delete d; delete m_image; delete m_saveOptions; delete m_metaInfo; delete m_selection; } //--------------------------------------------------------------------- // public kpDocumentEnvironment *kpDocument::environ () const { return d->environ; } //--------------------------------------------------------------------- // public void kpDocument::setEnviron (kpDocumentEnvironment *environ) { d->environ = environ; } //--------------------------------------------------------------------- // public bool kpDocument::savedAtLeastOnceBefore () const { return m_savedAtLeastOnceBefore; } //--------------------------------------------------------------------- // public QUrl kpDocument::url () const { return m_url; } //--------------------------------------------------------------------- // public void kpDocument::setURL (const QUrl &url, bool isFromURL) { m_url = url; m_isFromURL = isFromURL; } //--------------------------------------------------------------------- // public bool kpDocument::isFromURL (bool checkURLStillExists) const { if (!m_isFromURL) return false; if (!checkURLStillExists) return true; return (!m_url.isEmpty () && KIO::NetAccess::exists (m_url, KIO::NetAccess::SourceSide/*open*/, d->environ->dialogParent ())); } //--------------------------------------------------------------------- // public QString kpDocument::prettyUrl () const { return kpUrlFormatter::PrettyUrl (m_url); } //--------------------------------------------------------------------- // public QString kpDocument::prettyFilename () const { return kpUrlFormatter::PrettyFilename (m_url); } //--------------------------------------------------------------------- // public const kpDocumentSaveOptions *kpDocument::saveOptions () const { return m_saveOptions; } //--------------------------------------------------------------------- // public void kpDocument::setSaveOptions (const kpDocumentSaveOptions &saveOptions) { *m_saveOptions = saveOptions; } //--------------------------------------------------------------------- // public const kpDocumentMetaInfo *kpDocument::metaInfo () const { return m_metaInfo; } //--------------------------------------------------------------------- // public void kpDocument::setMetaInfo (const kpDocumentMetaInfo &metaInfo) { *m_metaInfo = metaInfo; } //--------------------------------------------------------------------- /* * Properties */ void kpDocument::setModified (bool yes) { if (yes == m_modified) return; m_modified = yes; if (yes) emit documentModified (); } //--------------------------------------------------------------------- bool kpDocument::isModified () const { return m_modified; } //--------------------------------------------------------------------- bool kpDocument::isEmpty () const { return url ().isEmpty () && !isModified (); } //--------------------------------------------------------------------- int kpDocument::constructorWidth () const { return m_constructorWidth; } //--------------------------------------------------------------------- int kpDocument::width (bool ofSelection) const { if (ofSelection && m_selection) return m_selection->width (); else return m_image->width (); } //--------------------------------------------------------------------- int kpDocument::oldWidth () const { return m_oldWidth; } //--------------------------------------------------------------------- void kpDocument::setWidth (int w, const kpColor &backgroundColor) { resize (w, height (), backgroundColor); } //--------------------------------------------------------------------- int kpDocument::constructorHeight () const { return m_constructorHeight; } //--------------------------------------------------------------------- int kpDocument::height (bool ofSelection) const { if (ofSelection && m_selection) return m_selection->height (); else return m_image->height (); } //--------------------------------------------------------------------- int kpDocument::oldHeight () const { return m_oldHeight; } //--------------------------------------------------------------------- void kpDocument::setHeight (int h, const kpColor &backgroundColor) { resize (width (), h, backgroundColor); } //--------------------------------------------------------------------- QRect kpDocument::rect (bool ofSelection) const { if (ofSelection && m_selection) return m_selection->boundingRect (); else return m_image->rect (); } //--------------------------------------------------------------------- // public kpImage kpDocument::getImageAt (const QRect &rect) const { return kpPixmapFX::getPixmapAt (*m_image, rect); } //--------------------------------------------------------------------- // public void kpDocument::setImageAt (const kpImage &image, const QPoint &at) { #if DEBUG_KP_DOCUMENT && 0 qCDebug(kpLogDocument) << "kpDocument::setImageAt (image (w=" << image.width () << ",h=" << image.height () << "), x=" << at.x () << ",y=" << at.y () << endl; #endif kpPixmapFX::setPixmapAt (m_image, at, image); slotContentsChanged (QRect (at.x (), at.y (), image.width (), image.height ())); } //--------------------------------------------------------------------- // public kpImage kpDocument::image (bool ofSelection) const { kpImage ret; if (ofSelection) { kpAbstractImageSelection *imageSel = imageSelection (); Q_ASSERT (imageSel); ret = imageSel->baseImage (); } else ret = *m_image; return ret; } //--------------------------------------------------------------------- // public kpImage *kpDocument::imagePointer () const { return m_image; } //--------------------------------------------------------------------- // public void kpDocument::setImage (const kpImage &image) { - m_oldWidth = width (), m_oldHeight = height (); + m_oldWidth = width (); + m_oldHeight = height (); *m_image = image; if (m_oldWidth == width () && m_oldHeight == height ()) slotContentsChanged (image.rect ()); else slotSizeChanged (width (), height ()); } //--------------------------------------------------------------------- // public void kpDocument::setImage (bool ofSelection, const kpImage &image) { if (ofSelection) { kpAbstractImageSelection *imageSel = imageSelection (); // Have to have an image selection in order to set its pixmap. Q_ASSERT (imageSel); imageSel->setBaseImage (image); } else setImage (image); } //--------------------------------------------------------------------- void kpDocument::fill (const kpColor &color) { #if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "kpDocument::fill ()"; #endif m_image->fill(color.toQRgb()); slotContentsChanged (m_image->rect ()); } //--------------------------------------------------------------------- void kpDocument::resize (int w, int h, const kpColor &backgroundColor) { #if DEBUG_KP_DOCUMENT qCDebug(kpLogDocument) << "kpDocument::resize (" << w << "," << h << ")"; #endif - m_oldWidth = width (), m_oldHeight = height (); + m_oldWidth = width (); + m_oldHeight = height (); #if DEBUG_KP_DOCUMENT && 1 qCDebug(kpLogDocument) << "\toldWidth=" << m_oldWidth << " oldHeight=" << m_oldHeight << endl; #endif if (w == m_oldWidth && h == m_oldHeight) return; kpPixmapFX::resize (m_image, w, h, backgroundColor); slotSizeChanged (width (), height ()); } //--------------------------------------------------------------------- void kpDocument::slotContentsChanged (const QRect &rect) { setModified (); emit contentsChanged (rect); } //--------------------------------------------------------------------- void kpDocument::slotSizeChanged (int newWidth, int newHeight) { setModified (); emit sizeChanged (newWidth, newHeight); emit sizeChanged (QSize (newWidth, newHeight)); } //--------------------------------------------------------------------- void kpDocument::slotSizeChanged (const QSize &newSize) { slotSizeChanged (newSize.width (), newSize.height ()); } //--------------------------------------------------------------------- diff --git a/imagelib/effects/blitz.cpp b/imagelib/effects/blitz.cpp index d1983d84..26de8e70 100644 --- a/imagelib/effects/blitz.cpp +++ b/imagelib/effects/blitz.cpp @@ -1,665 +1,668 @@ // functions taken from qimageblitz (no longer maintained) /* Copyright (C) 1998, 1999, 2001, 2002, 2004, 2005, 2007 Daniel M. Duley (C) 2004 Zack Rusin (C) 2000 Josef Weidendorfer (C) 1999 Geert Jansen (C) 1998, 1999 Christian Tibirna (C) 1998, 1999 Dirk Mueller Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Portions of this software were originally based on ImageMagick's algorithms. ImageMagick is copyrighted under the following conditions: Copyright (C) 2003 ImageMagick Studio, a non-profit organization dedicated to making software imaging solutions freely available. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files ("ImageMagick"), to deal in ImageMagick without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of ImageMagick, and to permit persons to whom the ImageMagick is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of ImageMagick. The software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall ImageMagick Studio be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with ImageMagick or the use or other dealings in ImageMagick. Except as contained in this notice, the name of the ImageMagick Studio shall not be used in advertising or otherwise to promote the sale, use or other dealings in ImageMagick without prior written authorization from the ImageMagick Studio. */ #include "blitz.h" #include #include #define M_SQ2PI 2.50662827463100024161235523934010416269302368164062 #define M_EPSILON 1.0e-6 #define CONVOLVE_ACC(weight, pixel) \ r+=((weight))*(qRed((pixel))); g+=((weight))*(qGreen((pixel))); \ b+=((weight))*(qBlue((pixel))); //-------------------------------------------------------------------------------- inline QRgb convertFromPremult(QRgb p) { int alpha = qAlpha(p); return(!alpha ? 0 : qRgba(255*qRed(p)/alpha, 255*qGreen(p)/alpha, 255*qBlue(p)/alpha, alpha)); } //-------------------------------------------------------------------------------- inline QRgb convertToPremult(QRgb p) { unsigned int a = p >> 24; unsigned int t = (p & 0xff00ff) * a; t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8; t &= 0xff00ff; p = ((p >> 8) & 0xff) * a; p = (p + ((p >> 8) & 0xff) + 0x80); p &= 0xff00; p |= t | (a << 24); return(p); } //-------------------------------------------------------------------------------- // These are used as accumulators typedef struct { quint32 red, green, blue, alpha; } IntegerPixel; typedef struct { // Yes, a normal pixel can be used instead but this is easier to read // and no shifts to get components. quint8 red, green, blue, alpha; } CharPixel; typedef struct { quint32 red, green, blue, alpha; } HistogramListItem; bool equalize(QImage &img) { if(img.isNull()) return(false); HistogramListItem *histogram; IntegerPixel *map; IntegerPixel intensity, high, low; CharPixel *equalize_map; int i, count; QRgb pixel, *dest; unsigned char r, g, b; if(img.depth() < 32){ img = img.convertToFormat(img.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32); } count = img.width()*img.height(); map = new IntegerPixel[256]; histogram = new HistogramListItem[256]; equalize_map = new CharPixel[256]; // form histogram memset(histogram, 0, 256*sizeof(HistogramListItem)); dest = (QRgb *)img.bits(); if(img.format() == QImage::Format_ARGB32_Premultiplied){ for(i=0; i < count; ++i, ++dest){ pixel = convertFromPremult(*dest); histogram[qRed(pixel)].red++; histogram[qGreen(pixel)].green++; histogram[qBlue(pixel)].blue++; histogram[qAlpha(pixel)].alpha++; } } else{ for(i=0; i < count; ++i){ pixel = *dest++; histogram[qRed(pixel)].red++; histogram[qGreen(pixel)].green++; histogram[qBlue(pixel)].blue++; histogram[qAlpha(pixel)].alpha++; } } // integrate the histogram to get the equalization map memset(&intensity, 0, sizeof(IntegerPixel)); for(i=0; i < 256; ++i){ intensity.red += histogram[i].red; intensity.green += histogram[i].green; intensity.blue += histogram[i].blue; map[i] = intensity; } low = map[0]; high = map[255]; memset(equalize_map, 0, 256*sizeof(CharPixel)); for(i=0; i < 256; ++i){ if(high.red != low.red) equalize_map[i].red=(unsigned char) ((255*(map[i].red-low.red))/(high.red-low.red)); if(high.green != low.green) equalize_map[i].green=(unsigned char) ((255*(map[i].green-low.green))/(high.green-low.green)); if(high.blue != low.blue) equalize_map[i].blue=(unsigned char) ((255*(map[i].blue-low.blue))/(high.blue-low.blue)); } // stretch the histogram and write dest = (QRgb *)img.bits(); if(img.format() == QImage::Format_ARGB32_Premultiplied){ for(i=0; i < count; ++i, ++dest){ pixel = convertFromPremult(*dest); r = (low.red != high.red) ? equalize_map[qRed(pixel)].red : qRed(pixel); g = (low.green != high.green) ? equalize_map[qGreen(pixel)].green : qGreen(pixel); b = (low.blue != high.blue) ? equalize_map[qBlue(pixel)].blue : qBlue(pixel); *dest = convertToPremult(qRgba(r, g, b, qAlpha(pixel))); } } else{ for(i=0; i < count; ++i){ pixel = *dest; r = (low.red != high.red) ? equalize_map[qRed(pixel)].red : qRed(pixel); g = (low.green != high.green) ? equalize_map[qGreen(pixel)].green : qGreen(pixel); b = (low.blue != high.blue) ? equalize_map[qBlue(pixel)].blue : qBlue(pixel); *dest++ = qRgba(r, g, b, qAlpha(pixel)); } } delete[] histogram; delete[] map; delete[] equalize_map; return(true); } //-------------------------------------------------------------------------------- QImage Blitz::blur(QImage &img, int radius) { QRgb *p1, *p2; int x, y, w, h, mx, my, mw, mh, mt, xx, yy; int a, r, g, b; int *as, *rs, *gs, *bs; if(radius < 1 || img.isNull() || img.width() < (radius << 1)) return(img); w = img.width(); h = img.height(); if(img.depth() < 8) img = img.convertToFormat(QImage::Format_Indexed8); QImage buffer(w, h, img.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32); as = new int[w]; rs = new int[w]; gs = new int[w]; bs = new int[w]; QVector colorTable; if(img.format() == QImage::Format_Indexed8) colorTable = img.colorTable(); for(y = 0; y < h; y++){ my = y - radius; mh = (radius << 1) + 1; if(my < 0){ mh += my; my = 0; } if((my + mh) > h) mh = h - my; p1 = (QRgb *)buffer.scanLine(y); memset(as, 0, w * sizeof(int)); memset(rs, 0, w * sizeof(int)); memset(gs, 0, w * sizeof(int)); memset(bs, 0, w * sizeof(int)); if(img.format() == QImage::Format_ARGB32_Premultiplied){ QRgb pixel; for(yy = 0; yy < mh; yy++){ p2 = (QRgb *)img.scanLine(yy + my); for(x = 0; x < w; x++, p2++){ pixel = convertFromPremult(*p2); as[x] += qAlpha(pixel); rs[x] += qRed(pixel); gs[x] += qGreen(pixel); bs[x] += qBlue(pixel); } } } else if(img.format() == QImage::Format_Indexed8){ QRgb pixel; unsigned char *ptr; for(yy = 0; yy < mh; yy++){ ptr = (unsigned char *)img.scanLine(yy + my); for(x = 0; x < w; x++, ptr++){ pixel = colorTable[*ptr]; as[x] += qAlpha(pixel); rs[x] += qRed(pixel); gs[x] += qGreen(pixel); bs[x] += qBlue(pixel); } } } else{ for(yy = 0; yy < mh; yy++){ p2 = (QRgb *)img.scanLine(yy + my); for(x = 0; x < w; x++, p2++){ as[x] += qAlpha(*p2); rs[x] += qRed(*p2); gs[x] += qGreen(*p2); bs[x] += qBlue(*p2); } } } for(x = 0; x < w; x++){ a = r = g = b = 0; mx = x - radius; mw = (radius << 1) + 1; if(mx < 0){ mw += mx; mx = 0; } if((mx + mw) > w) mw = w - mx; mt = mw * mh; for(xx = mx; xx < (mw + mx); xx++){ a += as[xx]; r += rs[xx]; g += gs[xx]; b += bs[xx]; } a = a / mt; r = r / mt; g = g / mt; b = b / mt; *p1++ = qRgba(r, g, b, a); } } delete[] as; delete[] rs; delete[] gs; delete[] bs; return(buffer); } //-------------------------------------------------------------------------------- int defaultConvolveMatrixSize(float radius, float sigma, bool quality) { int i, matrix_size; float normalize, value; float sigma2 = sigma*sigma*2.0; float sigmaSQ2PI = M_SQ2PI*sigma; int max = quality ? 65535 : 255; if(sigma == 0.0){ qWarning("Blitz::defaultConvolveMatrixSize(): Zero sigma is invalid!"); return(5); } if(radius > 0.0) return((int)(2.0*std::ceil(radius)+1.0)); matrix_size = 5; do{ normalize = 0.0; for(i=(-matrix_size/2); i <= (matrix_size/2); ++i) normalize += std::exp(-((float) i*i)/sigma2) / sigmaSQ2PI; i = matrix_size/2; value = std::exp(-((float) i*i)/sigma2) / sigmaSQ2PI / normalize; matrix_size += 2; } while((int)(max*value) > 0); matrix_size-=4; return(matrix_size); } //-------------------------------------------------------------------------------- QImage convolve(QImage &img, int matrix_size, float *matrix) { int i, x, y, w, h, matrix_x, matrix_y; int edge = matrix_size/2; QRgb *dest, *src, *s, **scanblock; float *m, *normalize_matrix, normalize; if(!(matrix_size % 2)){ qWarning("Blitz::convolve(): kernel width must be an odd number!"); return(img); } w = img.width(); h = img.height(); if(w < 3 || h < 3){ qWarning("Blitz::convolve(): Image is too small!"); return(img); } if(img.format() == QImage::Format_ARGB32_Premultiplied) img = img.convertToFormat(QImage::Format_ARGB32); else if(img.depth() < 32){ img = img.convertToFormat(img.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32); } QImage buffer(w, h, img.format()); scanblock = new QRgb* [matrix_size]; normalize_matrix = new float[matrix_size*matrix_size]; // create normalized matrix normalize = 0.0; for(i=0; i < matrix_size*matrix_size; ++i) normalize += matrix[i]; if(std::abs(normalize) <= M_EPSILON) normalize = 1.0; normalize = 1.0/normalize; for(i=0; i < matrix_size*matrix_size; ++i){ normalize_matrix[i] = normalize*matrix[i]; } // apply { // // // Non-MMX version // // float r, g, b; for(y=0; y < h; ++y){ src = (QRgb *)img.scanLine(y); dest = (QRgb *)buffer.scanLine(y); // Read in scanlines to pixel neighborhood. If the scanline is outside // the image use the top or bottom edge. for(x=y-edge, i=0; x <= y+edge; ++i, ++x){ scanblock[i] = (QRgb *) img.scanLine((x < 0) ? 0 : (x > h-1) ? h-1 : x); } // Now we are about to start processing scanlines. First handle the // part where the pixel neighborhood extends off the left edge. for(x=0; x-edge < 0 ; ++x){ r = g = b = 0.0; m = normalize_matrix; for(matrix_y = 0; matrix_y < matrix_size; ++matrix_y){ s = scanblock[matrix_y]; matrix_x = -edge; while(x+matrix_x < 0){ CONVOLVE_ACC(*m, *s); ++matrix_x; ++m; } while(matrix_x <= edge){ CONVOLVE_ACC(*m, *s); ++matrix_x; ++m; ++s; } } r = r < 0.0 ? 0.0 : r > 255.0 ? 255.0 : r+0.5; g = g < 0.0 ? 0.0 : g > 255.0 ? 255.0 : g+0.5; b = b < 0.0 ? 0.0 : b > 255.0 ? 255.0 : b+0.5; *dest++ = qRgba((unsigned char)r, (unsigned char)g, (unsigned char)b, qAlpha(*src++)); } // Okay, now process the middle part where the entire neighborhood // is on the image. for(; x+edge < w; ++x){ m = normalize_matrix; r = g = b = 0.0; for(matrix_y = 0; matrix_y < matrix_size; ++matrix_y){ s = scanblock[matrix_y] + (x-edge); for(matrix_x = -edge; matrix_x <= edge; ++matrix_x, ++m, ++s){ CONVOLVE_ACC(*m, *s); } } r = r < 0.0 ? 0.0 : r > 255.0 ? 255.0 : r+0.5; g = g < 0.0 ? 0.0 : g > 255.0 ? 255.0 : g+0.5; b = b < 0.0 ? 0.0 : b > 255.0 ? 255.0 : b+0.5; *dest++ = qRgba((unsigned char)r, (unsigned char)g, (unsigned char)b, qAlpha(*src++)); } // Finally process the right part where the neighborhood extends off // the right edge of the image for(; x < w; ++x){ r = g = b = 0.0; m = normalize_matrix; for(matrix_y = 0; matrix_y < matrix_size; ++matrix_y){ s = scanblock[matrix_y]; s += x-edge; matrix_x = -edge; while(x+matrix_x < w){ CONVOLVE_ACC(*m, *s); - ++matrix_x, ++m, ++s; + ++matrix_x; + ++m; + ++s; } --s; while(matrix_x <= edge){ CONVOLVE_ACC(*m, *s); - ++matrix_x, ++m; + ++matrix_x; + ++m; } } r = r < 0.0 ? 0.0 : r > 255.0 ? 255.0 : r+0.5; g = g < 0.0 ? 0.0 : g > 255.0 ? 255.0 : g+0.5; b = b < 0.0 ? 0.0 : b > 255.0 ? 255.0 : b+0.5; *dest++ = qRgba((unsigned char)r, (unsigned char)g, (unsigned char)b, qAlpha(*src++)); } } } delete[] scanblock; delete[] normalize_matrix; return(buffer); } //-------------------------------------------------------------------------------- QImage Blitz::gaussianSharpen(QImage &img, float radius, float sigma) { if(sigma == 0.0){ qWarning("Blitz::gaussianSharpen(): Zero sigma is invalid!"); return(img); } int matrix_size = defaultConvolveMatrixSize(radius, sigma, true); int len = matrix_size*matrix_size; float alpha, *matrix = new float[len]; float sigma2 = sigma*sigma*2.0; float sigmaPI2 = 2.0*M_PI*sigma*sigma; int half = matrix_size/2; int x, y, i=0, j=half; float normalize=0.0; for(y=(-half); y <= half; ++y, --j){ for(x=(-half); x <= half; ++x, ++i){ alpha = std::exp(-((float)x*x+y*y)/sigma2); matrix[i] = alpha/sigmaPI2; normalize += matrix[i]; } } matrix[i/2]=(-2.0)*normalize; QImage result(convolve(img, matrix_size, matrix)); delete[] matrix; return(result); } //-------------------------------------------------------------------------------- QImage Blitz::emboss(QImage &img, float radius, float sigma) { if(sigma == 0.0){ qWarning("Blitz::emboss(): Zero sigma is invalid!"); return(img); } int matrix_size = defaultConvolveMatrixSize(radius, sigma, true); int len = matrix_size*matrix_size; float alpha, *matrix = new float[len]; float sigma2 = sigma*sigma*2.0; float sigmaPI2 = 2.0*M_PI*sigma*sigma; int half = matrix_size/2; int x, y, i=0, j=half; for(y=(-half); y <= half; ++y, --j){ for(x=(-half); x <= half; ++x, ++i){ alpha = std::exp(-((float)x*x+y*y)/sigma2); matrix[i]=((x < 0) || (y < 0) ? -8.0 : 8.0)*alpha/sigmaPI2; if(x == j) matrix[i]=0.0; } } QImage result(convolve(img, matrix_size, matrix)); delete[] matrix; equalize(result); return(result); } //-------------------------------------------------------------------------------- QImage& Blitz::flatten(QImage &img, const QColor &ca, const QColor &cb) { if(img.isNull()) return(img); if(img.depth() == 1) { img.setColor(0, ca.rgb()); img.setColor(1, cb.rgb()); return(img); } int r1 = ca.red(); int r2 = cb.red(); int g1 = ca.green(); int g2 = cb.green(); int b1 = ca.blue(); int b2 = cb.blue(); int min = 0, max = 255; QRgb *data, *end; QVector cTable; if(img.format() == QImage::Format_Indexed8){ cTable = img.colorTable(); data = (unsigned int *)cTable.data(); end = data + img.colorCount(); } else{ data = (unsigned int *)img.scanLine(0); end = data + (img.width()*img.height()); } // get minimum and maximum graylevel QRgb *ptr = data; int mean; if(img.format() != QImage::Format_ARGB32_Premultiplied){ while(ptr != end){ mean = (qRed(*ptr) + qGreen(*ptr) + qBlue(*ptr)) / 3; min = qMin(min, mean); max = qMax(max, mean); ++ptr; } } else{ QRgb pixel; while(ptr != end){ pixel = convertFromPremult(*ptr); mean = (qRed(pixel) + qGreen(pixel) + qBlue(pixel)) / 3; min = qMin(min, mean); max = qMax(max, mean); ++ptr; } } // conversion factors float sr = ((float) r2 - r1) / (max - min); float sg = ((float) g2 - g1) / (max - min); float sb = ((float) b2 - b1) / (max - min); if(img.format() != QImage::Format_ARGB32_Premultiplied){ while(data != end){ mean = (qRed(*data) + qGreen(*data) + qBlue(*data)) / 3; *data = qRgba((unsigned char)(sr * (mean - min) + r1 + 0.5), (unsigned char)(sg * (mean - min) + g1 + 0.5), (unsigned char)(sb * (mean - min) + b1 + 0.5), qAlpha(*data)); ++data; } } else{ QRgb pixel; while(data != end){ pixel = convertFromPremult(*data); mean = (qRed(pixel) + qGreen(pixel) + qBlue(pixel)) / 3; *data = convertToPremult(qRgba((unsigned char)(sr * (mean - min) + r1 + 0.5), (unsigned char)(sg * (mean - min) + g1 + 0.5), (unsigned char)(sb * (mean - min) + b1 + 0.5), qAlpha(*data))); ++data; } } if(img.format() == QImage::Format_Indexed8) img.setColorTable(cTable); return(img); } //-------------------------------------------------------------------------------- diff --git a/imagelib/kpFloodFill.cpp b/imagelib/kpFloodFill.cpp index 9468a9fe..c0e04578 100644 --- a/imagelib/kpFloodFill.cpp +++ b/imagelib/kpFloodFill.cpp @@ -1,413 +1,415 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define DEBUG_KP_FLOOD_FILL 0 #include "kpFloodFill.h" #include #include #include #include #include #include "kpLogCategories.h" #include "kpColor.h" #include "kpImage.h" #include "kpDefs.h" #include "pixmapfx/kpPixmapFX.h" #include "tools/kpTool.h" //--------------------------------------------------------------------- class kpFillLine { public: kpFillLine (int y = -1, int x1 = -1, int x2 = -1) : m_y (y), m_x1 (x1), m_x2 (x2) { } static kpCommandSize::SizeType size () { return sizeof (kpFillLine); } int m_y, m_x1, m_x2; }; //--------------------------------------------------------------------- static kpCommandSize::SizeType FillLinesListSize (const QLinkedList &fillLines) { return (fillLines.size () * kpFillLine::size ()); } //--------------------------------------------------------------------- struct kpFloodFillPrivate { // // Copy of whatever was passed to the constructor. // kpImage *imagePtr; int x, y; kpColor color; int processedColorSimilarity; // // Set by Step 1. // kpColor colorToChange; // // Set by Step 2. // QLinkedList fillLines; QList < QLinkedList > fillLinesCache; QRect boundingRect; bool prepared; }; //--------------------------------------------------------------------- kpFloodFill::kpFloodFill (kpImage *image, int x, int y, const kpColor &color, int processedColorSimilarity) : d (new kpFloodFillPrivate ()) { d->imagePtr = image; - d->x = x, d->y = y; - d->color = color, d->processedColorSimilarity = processedColorSimilarity; + d->x = x; + d->y = y; + d->color = color; + d->processedColorSimilarity = processedColorSimilarity; d->prepared = false; } //--------------------------------------------------------------------- kpFloodFill::~kpFloodFill () { delete d; } //--------------------------------------------------------------------- // public kpColor kpFloodFill::color () const { return d->color; } //--------------------------------------------------------------------- // public int kpFloodFill::processedColorSimilarity () const { return d->processedColorSimilarity; } //--------------------------------------------------------------------- // public kpCommandSize::SizeType kpFloodFill::size () const { kpCommandSize::SizeType fillLinesCacheSize = 0; foreach (const QLinkedList &linesList, d->fillLinesCache) { fillLinesCacheSize += ::FillLinesListSize (linesList); } return ::FillLinesListSize(d->fillLines) + kpCommandSize::QImageSize(d->imagePtr) + fillLinesCacheSize; } //--------------------------------------------------------------------- // public void kpFloodFill::prepareColorToChange () { if (d->colorToChange.isValid ()) return; #if DEBUG_KP_FLOOD_FILL && 1 qCDebug(kpLogImagelib) << "kpFloodFill::prepareColorToChange()"; #endif d->colorToChange = kpPixmapFX::getColorAtPixel (*d->imagePtr, QPoint (d->x, d->y)); } //--------------------------------------------------------------------- // public kpColor kpFloodFill::colorToChange () { prepareColorToChange (); return d->colorToChange; } //--------------------------------------------------------------------- // Derived from the zSprite2 Graphics Engine // private kpColor kpFloodFill::pixelColor (int x, int y, bool *beenHere) const { if (beenHere) *beenHere = false; Q_ASSERT (y >= 0 && y < (int) d->fillLinesCache.count ()); foreach (const kpFillLine &line, d->fillLinesCache [y]) { if (x >= line.m_x1 && x <= line.m_x2) { if (beenHere) *beenHere = true; return d->color; } } return kpPixmapFX::getColorAtPixel (*(d->imagePtr), QPoint (x, y)); } //--------------------------------------------------------------------- // private bool kpFloodFill::shouldGoTo (int x, int y) const { bool beenThere; const kpColor col = pixelColor (x, y, &beenThere); return (!beenThere && col.isSimilarTo (d->colorToChange, d->processedColorSimilarity)); } //--------------------------------------------------------------------- // private int kpFloodFill::findMinX (int y, int x) const { for (;;) { if (x < 0) return 0; if (shouldGoTo (x, y)) x--; else return x + 1; } } //--------------------------------------------------------------------- // private int kpFloodFill::findMaxX (int y, int x) const { for (;;) { if (x > d->imagePtr->width () - 1) return d->imagePtr->width () - 1; if (shouldGoTo (x, y)) x++; else return x - 1; } } //--------------------------------------------------------------------- // private void kpFloodFill::addLine (int y, int x1, int x2) { #if DEBUG_KP_FLOOD_FILL && 0 qCDebug(kpLogImagelib) << "kpFillCommand::fillAddLine (" << y << "," << x1 << "," << x2 << ")" << endl; #endif d->fillLines.append (kpFillLine (y, x1, x2)); d->fillLinesCache [y].append ( kpFillLine (y/*OPT: can determine from array index*/, x1, x2)); d->boundingRect = d->boundingRect.united (QRect (QPoint (x1, y), QPoint (x2, y))); } //--------------------------------------------------------------------- // private void kpFloodFill::findAndAddLines (const kpFillLine &fillLine, int dy) { // out of bounds? if (fillLine.m_y + dy < 0 || fillLine.m_y + dy >= d->imagePtr->height ()) return; for (int xnow = fillLine.m_x1; xnow <= fillLine.m_x2; xnow++) { // At current position, right colour? if (shouldGoTo (xnow, fillLine.m_y + dy)) { // Find minimum and maximum x values int minxnow = findMinX (fillLine.m_y + dy, xnow); int maxxnow = findMaxX (fillLine.m_y + dy, xnow); // Draw line addLine (fillLine.m_y + dy, minxnow, maxxnow); // Move x pointer xnow = maxxnow; } } } //--------------------------------------------------------------------- // public void kpFloodFill::prepare () { if (d->prepared) return; #if DEBUG_KP_FLOOD_FILL && 1 qCDebug(kpLogImagelib) << "kpFloodFill::prepare()"; #endif prepareColorToChange (); d->boundingRect = QRect (); #if DEBUG_KP_FLOOD_FILL && 1 qCDebug(kpLogImagelib) << "\tperforming NOP check"; #endif // get the color we need to replace if (d->processedColorSimilarity == 0 && d->color == d->colorToChange) { // need to do absolutely nothing (this is a significant optimization // for people who randomly click a lot over already-filled areas) d->prepared = true; // sync with all "return true"'s return; } #if DEBUG_KP_FLOOD_FILL && 1 qCDebug(kpLogImagelib) << "\tcreating fillLinesCache"; #endif // ready cache for (int i = 0; i < d->imagePtr->height (); i++) d->fillLinesCache.append (QLinkedList ()); #if DEBUG_KP_FLOOD_FILL && 1 qCDebug(kpLogImagelib) << "\tcreating fill lines"; #endif // draw initial line addLine (d->y, findMinX (d->y, d->x), findMaxX (d->y, d->x)); for (QLinkedList ::ConstIterator it = d->fillLines.begin (); it != d->fillLines.end (); ++it) { #if DEBUG_KP_FLOOD_FILL && 0 qCDebug(kpLogImagelib) << "Expanding from y=" << (*it).m_y << " x1=" << (*it).m_x1 << " x2=" << (*it).m_x2 << endl; #endif // // Make more lines above and below current line. // // WARNING: Adds to end of "fillLines" (the linked list we are iterating // through). Therefore, "fillLines" must remain a linked list // - you cannot change it into a vector. Also, do not use // "foreach" for this loop as that makes a copy of the linked // list at the start and won't see new lines. // findAndAddLines (*it, -1); findAndAddLines (*it, +1); } #if DEBUG_KP_FLOOD_FILL && 1 qCDebug(kpLogImagelib) << "\tfinalising memory usage"; #endif // finalize memory usage d->fillLinesCache.clear (); d->prepared = true; // sync with all "return true"'s } //--------------------------------------------------------------------- // public QRect kpFloodFill::boundingRect () { prepare (); return d->boundingRect; } //--------------------------------------------------------------------- // public void kpFloodFill::fill() { prepare(); QApplication::setOverrideCursor(Qt::WaitCursor); QPainter painter(d->imagePtr); // by definition, flood fill with a fully transparent color erases the pixels // and sets them to be fully transparent if ( d->color.isTransparent() ) painter.setCompositionMode(QPainter::CompositionMode_Clear); painter.setPen(d->color.toQColor()); foreach (const kpFillLine &l, d->fillLines) { if ( l.m_x1 == l.m_x2 ) painter.drawPoint(l.m_x1, l.m_y); else painter.drawLine(l.m_x1, l.m_y, l.m_x2, l.m_y); } QApplication::restoreOverrideCursor(); } //--------------------------------------------------------------------- diff --git a/layers/tempImage/kpTempImage.cpp b/layers/tempImage/kpTempImage.cpp index dc832e31..a1084e04 100644 --- a/layers/tempImage/kpTempImage.cpp +++ b/layers/tempImage/kpTempImage.cpp @@ -1,217 +1,218 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "layers/tempImage/kpTempImage.h" #include "pixmapfx/kpPixmapFX.h" #include "views/manager/kpViewManager.h" #include //--------------------------------------------------------------------- kpTempImage::kpTempImage (bool isBrush, RenderMode renderMode, const QPoint &topLeft, const kpImage &image) : m_isBrush (isBrush), m_renderMode (renderMode), m_topLeft (topLeft), m_image (image), m_width (image.width ()), m_height (image.height ()), m_userFunction (nullptr), m_userData (nullptr) { // Use below constructor for that. Q_ASSERT (renderMode != UserFunction); } //--------------------------------------------------------------------- kpTempImage::kpTempImage (bool isBrush, const QPoint &topLeft, UserFunctionType userFunction, void *userData, int width, int height) : m_isBrush (isBrush), m_renderMode (UserFunction), m_topLeft (topLeft), m_width (width), m_height (height), m_userFunction (userFunction), m_userData (userData) { Q_ASSERT (m_userFunction); } //--------------------------------------------------------------------- kpTempImage::kpTempImage (const kpTempImage &rhs) : m_isBrush (rhs.m_isBrush), m_renderMode (rhs.m_renderMode), m_topLeft (rhs.m_topLeft), m_image (rhs.m_image), m_width (rhs.m_width), m_height (rhs.m_height), m_userFunction (rhs.m_userFunction), m_userData (rhs.m_userData) { } //--------------------------------------------------------------------- kpTempImage &kpTempImage::operator= (const kpTempImage &rhs) { if (this == &rhs) return *this; m_isBrush = rhs.m_isBrush; m_renderMode = rhs.m_renderMode; m_topLeft = rhs.m_topLeft; m_image = rhs.m_image; - m_width = rhs.m_width, m_height = rhs.m_height; + m_width = rhs.m_width; + m_height = rhs.m_height; m_userFunction = rhs.m_userFunction; m_userData = rhs.m_userData; return *this; } //--------------------------------------------------------------------- // public bool kpTempImage::isBrush () const { return m_isBrush; } //--------------------------------------------------------------------- // public kpTempImage::RenderMode kpTempImage::renderMode () const { return m_renderMode; } //--------------------------------------------------------------------- // public QPoint kpTempImage::topLeft () const { return m_topLeft; } //--------------------------------------------------------------------- // public kpImage kpTempImage::image () const { return m_image; } //--------------------------------------------------------------------- // public kpTempImage::UserFunctionType kpTempImage::userFunction () const { return m_userFunction; } //--------------------------------------------------------------------- // public void *kpTempImage::userData () const { return m_userData; } //--------------------------------------------------------------------- // public bool kpTempImage::isVisible (const kpViewManager *vm) const { return m_isBrush ? (bool) vm->viewUnderCursor () : true; } //--------------------------------------------------------------------- // public QRect kpTempImage::rect () const { return QRect (m_topLeft.x (), m_topLeft.y (), m_width, m_height); } //--------------------------------------------------------------------- // public int kpTempImage::width () const { return m_width; } //--------------------------------------------------------------------- // public int kpTempImage::height () const { return m_height; } //--------------------------------------------------------------------- // public bool kpTempImage::paintMayAddMask () const { return (m_renderMode == SetImage || m_renderMode == UserFunction); } //--------------------------------------------------------------------- // public void kpTempImage::paint (kpImage *destImage, const QRect &docRect) const { const QPoint REL_TOP_LEFT = m_topLeft - docRect.topLeft (); switch (m_renderMode) { case SetImage: { kpPixmapFX::setPixmapAt(destImage, REL_TOP_LEFT, m_image); break; } case PaintImage: { kpPixmapFX::paintPixmapAt(destImage, REL_TOP_LEFT, m_image); break; } case UserFunction: { m_userFunction(destImage, REL_TOP_LEFT, m_userData); break; } } } //--------------------------------------------------------------------- diff --git a/mainWindow/kpMainWindow_Tools.cpp b/mainWindow/kpMainWindow_Tools.cpp index 7915efbb..a78ed172 100644 --- a/mainWindow/kpMainWindow_Tools.cpp +++ b/mainWindow/kpMainWindow_Tools.cpp @@ -1,805 +1,805 @@ /* Copyright (c) 2003-2007 Clarence Dang Copyright (c) 2011 Martin Koller All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "mainWindow/kpMainWindow.h" #include "kpMainWindowPrivate.h" #include #include #include #include #include #include "kpLogCategories.h" #include #include "widgets/toolbars/kpColorToolBar.h" #include "commands/kpCommandHistory.h" #include "document/kpDocument.h" #include "layers/selections/image/kpImageSelectionTransparency.h" #include "tools/kpTool.h" #include "tools/kpToolAction.h" #include "tools/flow/kpToolBrush.h" #include "tools/flow/kpToolColorEraser.h" #include "tools/kpToolColorPicker.h" #include "tools/polygonal/kpToolCurve.h" #include "tools/selection/image/kpToolEllipticalSelection.h" #include "tools/rectangular/kpToolEllipse.h" #include "tools/flow/kpToolEraser.h" #include "tools/kpToolFloodFill.h" #include "tools/selection/image/kpToolFreeFormSelection.h" #include "tools/polygonal/kpToolLine.h" #include "tools/flow/kpToolPen.h" #include "tools/polygonal/kpToolPolygon.h" #include "tools/polygonal/kpToolPolyline.h" #include "tools/rectangular/kpToolRectangle.h" #include "tools/selection/image/kpToolRectSelection.h" #include "tools/rectangular/kpToolRoundedRectangle.h" #include "environments/tools/selection/kpToolSelectionEnvironment.h" #include "tools/flow/kpToolSpraycan.h" #include "tools/selection/text/kpToolText.h" #include "widgets/toolbars/kpToolToolBar.h" #include "widgets/toolbars/options/kpToolWidgetOpaqueOrTransparent.h" #include "tools/kpToolZoom.h" #include "commands/imagelib/transforms/kpTransformResizeScaleCommand.h" #include "kpViewScrollableContainer.h" #include "views/kpZoomedView.h" //--------------------------------------------------------------------- // private kpToolSelectionEnvironment *kpMainWindow::toolSelectionEnvironment () { if (!d->toolSelectionEnvironment) d->toolSelectionEnvironment = new kpToolSelectionEnvironment (this); return d->toolSelectionEnvironment; } //--------------------------------------------------------------------- // private kpToolEnvironment *kpMainWindow::toolEnvironment () { // It's fine to return a more complex environment than required. return toolSelectionEnvironment (); } //--------------------------------------------------------------------- // private void kpMainWindow::setupToolActions () { kpToolSelectionEnvironment *toolSelEnv = toolSelectionEnvironment (); kpToolEnvironment *toolEnv = toolEnvironment (); d->tools.append (d->toolFreeFormSelection = new kpToolFreeFormSelection (toolSelEnv, this)); d->tools.append (d->toolRectSelection = new kpToolRectSelection (toolSelEnv, this)); d->tools.append (d->toolEllipticalSelection = new kpToolEllipticalSelection (toolSelEnv, this)); d->tools.append (d->toolText = new kpToolText (toolSelEnv, this)); d->tools.append (d->toolLine = new kpToolLine (toolEnv, this)); d->tools.append (d->toolPen = new kpToolPen (toolEnv, this)); d->tools.append (d->toolEraser = new kpToolEraser (toolEnv, this)); d->tools.append (d->toolBrush = new kpToolBrush (toolEnv, this)); d->tools.append (d->toolFloodFill = new kpToolFloodFill (toolEnv, this)); d->tools.append (d->toolColorPicker = new kpToolColorPicker (toolEnv, this)); d->tools.append (d->toolColorEraser = new kpToolColorEraser (toolEnv, this)); d->tools.append (d->toolSpraycan = new kpToolSpraycan (toolEnv, this)); d->tools.append (d->toolRoundedRectangle = new kpToolRoundedRectangle (toolEnv, this)); d->tools.append (d->toolRectangle = new kpToolRectangle (toolEnv, this)); d->tools.append (d->toolPolygon = new kpToolPolygon (toolEnv, this)); d->tools.append (d->toolEllipse = new kpToolEllipse (toolEnv, this)); d->tools.append (d->toolPolyline = new kpToolPolyline (toolEnv, this)); d->tools.append (d->toolCurve = new kpToolCurve (toolEnv, this)); d->tools.append (d->toolZoom = new kpToolZoom (toolEnv, this)); KActionCollection *ac = actionCollection (); d->actionPrevToolOptionGroup1 = ac->addAction ("prev_tool_option_group_1"); d->actionPrevToolOptionGroup1->setText (i18n ("Previous Tool Option (Group #1)")); ac->setDefaultShortcuts (d->actionPrevToolOptionGroup1, kpTool::shortcutForKey (Qt::Key_1)); connect (d->actionPrevToolOptionGroup1, SIGNAL (triggered(bool)), SLOT (slotActionPrevToolOptionGroup1())); d->actionNextToolOptionGroup1 = ac->addAction ("next_tool_option_group_1"); d->actionNextToolOptionGroup1->setText (i18n ("Next Tool Option (Group #1)")); ac->setDefaultShortcuts (d->actionNextToolOptionGroup1, kpTool::shortcutForKey (Qt::Key_2)); connect (d->actionNextToolOptionGroup1, SIGNAL (triggered(bool)), SLOT (slotActionNextToolOptionGroup1())); d->actionPrevToolOptionGroup2 = ac->addAction ("prev_tool_option_group_2"); d->actionPrevToolOptionGroup2->setText (i18n ("Previous Tool Option (Group #2)")); ac->setDefaultShortcuts (d->actionPrevToolOptionGroup2, kpTool::shortcutForKey (Qt::Key_3)); connect (d->actionPrevToolOptionGroup2, SIGNAL (triggered(bool)), SLOT (slotActionPrevToolOptionGroup2())); d->actionNextToolOptionGroup2 = ac->addAction ("next_tool_option_group_2"); d->actionNextToolOptionGroup2->setText (i18n ("Next Tool Option (Group #2)")); ac->setDefaultShortcuts (d->actionNextToolOptionGroup2, kpTool::shortcutForKey (Qt::Key_4)); connect (d->actionNextToolOptionGroup2, SIGNAL (triggered(bool)), SLOT (slotActionNextToolOptionGroup2())); // // Implemented in this file (kpMainWindow_Tools.cpp), not // kpImageWindow_Image.cpp since they're really setting tool options. // d->actionDrawOpaque = ac->add ("image_draw_opaque"); d->actionDrawOpaque->setText (i18n ("&Draw Opaque")); connect (d->actionDrawOpaque, SIGNAL (triggered(bool)), SLOT (slotActionDrawOpaqueToggled())); d->actionDrawColorSimilarity = ac->addAction ("image_draw_color_similarity"); d->actionDrawColorSimilarity->setText (i18n ("Draw With Color Similarity...")); connect (d->actionDrawColorSimilarity, SIGNAL (triggered(bool)), SLOT (slotActionDrawColorSimilarity())); } //--------------------------------------------------------------------- // private void kpMainWindow::createToolBox () { d->toolToolBar = new kpToolToolBar(QLatin1String("Tool Box"), 2/*columns/rows*/, this); d->toolToolBar->setWindowTitle(i18n("Tool Box")); connect (d->toolToolBar, SIGNAL (sigToolSelected(kpTool*)), this, SLOT (slotToolSelected(kpTool*))); connect (d->toolToolBar, SIGNAL (toolWidgetOptionSelected()), this, SLOT (updateToolOptionPrevNextActionsEnabled())); connect (d->toolToolBar->toolWidgetOpaqueOrTransparent (), SIGNAL (isOpaqueChanged(bool)), SLOT (updateActionDrawOpaqueChecked())); updateActionDrawOpaqueChecked (); foreach (kpTool *tool, d->tools) d->toolToolBar->registerTool(tool); // (from config file) readLastTool (); } //--------------------------------------------------------------------- // private void kpMainWindow::enableToolsDocumentActions (bool enable) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::enableToolsDocumentsAction(" << enable << ")"; #endif d->toolActionsEnabled = enable; if (enable && !d->toolToolBar->isEnabled ()) { kpTool *previousTool = d->toolToolBar->previousTool (); // select tool for enabled Tool Box if (previousTool) d->toolToolBar->selectPreviousTool (); else { if (d->lastToolNumber >= 0 && d->lastToolNumber < (int) d->tools.count ()) d->toolToolBar->selectTool (d->tools.at (d->lastToolNumber)); else d->toolToolBar->selectTool (d->toolPen); } } else if (!enable && d->toolToolBar->isEnabled ()) { // don't have a disabled Tool Box with a checked Tool d->toolToolBar->selectTool (nullptr); } d->toolToolBar->setEnabled (enable); foreach (kpTool *tool, d->tools) { kpToolAction *action = tool->action(); if (!enable && action->isChecked()) action->setChecked(false); action->setEnabled(enable); } updateToolOptionPrevNextActionsEnabled (); updateActionDrawOpaqueEnabled (); } //--------------------------------------------------------------------- // private slot void kpMainWindow::updateToolOptionPrevNextActionsEnabled () { const bool enable = d->toolActionsEnabled; d->actionPrevToolOptionGroup1->setEnabled (enable && d->toolToolBar->shownToolWidget (0) && d->toolToolBar->shownToolWidget (0)->hasPreviousOption ()); d->actionNextToolOptionGroup1->setEnabled (enable && d->toolToolBar->shownToolWidget (0) && d->toolToolBar->shownToolWidget (0)->hasNextOption ()); d->actionPrevToolOptionGroup2->setEnabled (enable && d->toolToolBar->shownToolWidget (1) && d->toolToolBar->shownToolWidget (1)->hasPreviousOption ()); d->actionNextToolOptionGroup2->setEnabled (enable && d->toolToolBar->shownToolWidget (1) && d->toolToolBar->shownToolWidget (1)->hasNextOption ()); } //--------------------------------------------------------------------- // private slot void kpMainWindow::updateActionDrawOpaqueChecked () { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::updateActionDrawOpaqueChecked()"; #endif const bool drawOpaque = (d->toolToolBar->toolWidgetOpaqueOrTransparent ()->selectedRow () == 0); #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tdrawOpaque=" << drawOpaque; #endif d->actionDrawOpaque->setChecked (drawOpaque); } //--------------------------------------------------------------------- // private void kpMainWindow::updateActionDrawOpaqueEnabled () { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::updateActionDrawOpaqueEnabled()"; #endif const bool enable = d->toolActionsEnabled; #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tenable=" << enable << " tool=" << (tool () ? tool ()->objectName () : 0) << " (is selection=" << toolIsASelectionTool () << ")" << endl; #endif d->actionDrawOpaque->setEnabled (enable && toolIsASelectionTool ()); } //--------------------------------------------------------------------- // public QActionGroup *kpMainWindow::toolsActionGroup () { if (!d->toolsActionGroup) d->toolsActionGroup = new QActionGroup (this); return d->toolsActionGroup; } //--------------------------------------------------------------------- // public kpTool *kpMainWindow::tool () const { return d->toolToolBar ? d->toolToolBar->tool () : nullptr; } //--------------------------------------------------------------------- // public bool kpMainWindow::toolHasBegunShape () const { kpTool *currentTool = tool (); return (currentTool && currentTool->hasBegunShape ()); } //--------------------------------------------------------------------- // public bool kpMainWindow::toolIsASelectionTool (bool includingTextTool) const { kpTool *currentTool = tool (); return ((currentTool == d->toolFreeFormSelection) || (currentTool == d->toolRectSelection) || (currentTool == d->toolEllipticalSelection) || (currentTool == d->toolText && includingTextTool)); } //--------------------------------------------------------------------- // public bool kpMainWindow::toolIsTextTool () const { return (tool () == d->toolText); } //--------------------------------------------------------------------- // private void kpMainWindow::toolEndShape () { if (toolHasBegunShape ()) tool ()->endShapeInternal (); } //--------------------------------------------------------------------- // public kpImageSelectionTransparency kpMainWindow::imageSelectionTransparency () const { kpToolWidgetOpaqueOrTransparent *oot = d->toolToolBar->toolWidgetOpaqueOrTransparent (); Q_ASSERT (oot); return kpImageSelectionTransparency (oot->isOpaque (), backgroundColor (), d->colorToolBar->colorSimilarity ()); } //--------------------------------------------------------------------- // public void kpMainWindow::setImageSelectionTransparency (const kpImageSelectionTransparency &transparency, bool forceColorChange) { #if DEBUG_KP_MAIN_WINDOW && 1 qCDebug(kpLogMainWindow) << "kpMainWindow::setImageSelectionTransparency() isOpaque=" << transparency.isOpaque () << " color=" << (transparency.transparentColor ().isValid () ? (int *) transparency.transparentColor ().toQRgb () : 0) << " forceColorChange=" << forceColorChange << endl; #endif kpToolWidgetOpaqueOrTransparent *oot = d->toolToolBar->toolWidgetOpaqueOrTransparent (); Q_ASSERT (oot); d->settingImageSelectionTransparency++; oot->setOpaque (transparency.isOpaque ()); if (transparency.isTransparent () || forceColorChange) { d->colorToolBar->setColor (1, transparency.transparentColor ()); d->colorToolBar->setColorSimilarity (transparency.colorSimilarity ()); } d->settingImageSelectionTransparency--; } //--------------------------------------------------------------------- // public int kpMainWindow::settingImageSelectionTransparency () const { return d->settingImageSelectionTransparency; } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotToolSelected (kpTool *tool) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::slotToolSelected (" << tool << ")"; #endif kpTool *previousTool = d->toolToolBar ? d->toolToolBar->previousTool () : nullptr; if (previousTool) { disconnect (previousTool, SIGNAL (movedAndAboutToDraw(QPoint,QPoint,int,bool*)), this, SLOT (slotDragScroll(QPoint,QPoint,int,bool*))); disconnect (previousTool, SIGNAL (endedDraw(QPoint)), this, SLOT (slotEndDragScroll())); disconnect (previousTool, SIGNAL (cancelledShape(QPoint)), this, SLOT (slotEndDragScroll())); disconnect (previousTool, SIGNAL (userMessageChanged(QString)), this, SLOT (recalculateStatusBarMessage())); disconnect (previousTool, SIGNAL (userShapePointsChanged(QPoint,QPoint)), this, SLOT (recalculateStatusBarShape())); disconnect (previousTool, SIGNAL (userShapeSizeChanged(QSize)), this, SLOT (recalculateStatusBarShape())); disconnect (d->colorToolBar, SIGNAL (colorsSwapped(kpColor,kpColor)), previousTool, SLOT (slotColorsSwappedInternal(kpColor,kpColor))); disconnect (d->colorToolBar, SIGNAL (foregroundColorChanged(kpColor)), previousTool, SLOT (slotForegroundColorChangedInternal(kpColor))); disconnect (d->colorToolBar, SIGNAL (backgroundColorChanged(kpColor)), previousTool, SLOT (slotBackgroundColorChangedInternal(kpColor))); disconnect (d->colorToolBar, SIGNAL (colorSimilarityChanged(double,int)), previousTool, SLOT (slotColorSimilarityChangedInternal(double,int))); } if (tool) { connect (tool, SIGNAL (movedAndAboutToDraw(QPoint,QPoint,int,bool*)), this, SLOT (slotDragScroll(QPoint,QPoint,int,bool*))); connect (tool, SIGNAL (endedDraw(QPoint)), this, SLOT (slotEndDragScroll())); connect (tool, SIGNAL (cancelledShape(QPoint)), this, SLOT (slotEndDragScroll())); connect (tool, SIGNAL (userMessageChanged(QString)), this, SLOT (recalculateStatusBarMessage())); connect (tool, SIGNAL (userShapePointsChanged(QPoint,QPoint)), this, SLOT (recalculateStatusBarShape())); connect (tool, SIGNAL (userShapeSizeChanged(QSize)), this, SLOT (recalculateStatusBarShape())); recalculateStatusBar (); connect (d->colorToolBar, SIGNAL (colorsSwapped(kpColor,kpColor)), tool, SLOT (slotColorsSwappedInternal(kpColor,kpColor))); connect (d->colorToolBar, SIGNAL (foregroundColorChanged(kpColor)), tool, SLOT (slotForegroundColorChangedInternal(kpColor))); connect (d->colorToolBar, SIGNAL (backgroundColorChanged(kpColor)), tool, SLOT (slotBackgroundColorChangedInternal(kpColor))); connect (d->colorToolBar, SIGNAL (colorSimilarityChanged(double,int)), tool, SLOT (slotColorSimilarityChangedInternal(double,int))); saveLastTool (); } updateToolOptionPrevNextActionsEnabled (); updateActionDrawOpaqueEnabled (); } //--------------------------------------------------------------------- // private void kpMainWindow::readLastTool () { KConfigGroup cfg (KSharedConfig::openConfig (), kpSettingsGroupTools); d->lastToolNumber = cfg.readEntry (kpSettingLastTool, -1); } //--------------------------------------------------------------------- // private int kpMainWindow::toolNumber () const { int number = 0; for (QList ::const_iterator it = d->tools.constBegin (); it != d->tools.constEnd (); ++it) { if (*it == tool ()) return number; number++; } return -1; } //--------------------------------------------------------------------- // private void kpMainWindow::saveLastTool () { int number = toolNumber (); if (number < 0 || number >= (int) d->tools.count ()) return; KConfigGroup cfg (KSharedConfig::openConfig (), kpSettingsGroupTools); cfg.writeEntry (kpSettingLastTool, number); cfg.sync (); } //--------------------------------------------------------------------- // private bool kpMainWindow::maybeDragScrollingMainView () const { return (tool () && d->mainView && tool ()->viewUnderStartPoint () == d->mainView); } //--------------------------------------------------------------------- // private slot bool kpMainWindow::slotDragScroll (const QPoint &docPoint, const QPoint &docLastPoint, int zoomLevel, bool *scrolled) { Q_UNUSED(docPoint) Q_UNUSED(docLastPoint) #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::slotDragScroll() maybeDragScrolling=" << maybeDragScrollingMainView () << endl; #endif if (maybeDragScrollingMainView ()) { return d->scrollView->beginDragScroll(zoomLevel, scrolled); } else { return false; } } //--------------------------------------------------------------------- // private slot bool kpMainWindow::slotEndDragScroll () { // (harmless if haven't started drag scroll) return d->scrollView->endDragScroll (); } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotBeganDocResize () { toolEndShape (); recalculateStatusBarShape (); } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotContinuedDocResize (const QSize &) { recalculateStatusBarShape (); } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotCancelledDocResize () { recalculateStatusBar (); } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotEndedDocResize (const QSize &size) { #define DOC_RESIZE_COMPLETED() \ { \ d->docResizeToBeCompleted = false; \ recalculateStatusBar (); \ } // Prevent statusbar updates d->docResizeToBeCompleted = true; - d->docResizeWidth = (size.width () > 0 ? size.width () : 1), + d->docResizeWidth = (size.width () > 0 ? size.width () : 1); d->docResizeHeight = (size.height () > 0 ? size.height () : 1); if (d->docResizeWidth == d->document->width () && d->docResizeHeight == d->document->height ()) { DOC_RESIZE_COMPLETED (); return; } // Blank status to avoid confusion if dialog comes up setStatusBarMessage (); setStatusBarShapePoints (); setStatusBarShapeSize (); if (kpTool::warnIfBigImageSize (d->document->width (), d->document->height (), d->docResizeWidth, d->docResizeHeight, i18n ("

Resizing the image to" " %1x%2 may take a substantial amount of memory." " This can reduce system" " responsiveness and cause other application resource" " problems.

" "

Are you sure you want to resize the" " image?

", d->docResizeWidth, d->docResizeHeight), i18nc ("@title:window", "Resize Image?"), i18n ("R&esize Image"), this)) { d->commandHistory->addCommand ( new kpTransformResizeScaleCommand ( false/*doc, not sel*/, d->docResizeWidth, d->docResizeHeight, kpTransformResizeScaleCommand::Resize, commandEnvironment ())); saveDefaultDocSize (QSize (d->docResizeWidth, d->docResizeHeight)); } DOC_RESIZE_COMPLETED (); #undef DOC_RESIZE_COMPLETED } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotDocResizeMessageChanged (const QString &string) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::slotDocResizeMessageChanged(" << string << ") docResizeToBeCompleted=" << d->docResizeToBeCompleted << endl; #else (void) string; #endif if (d->docResizeToBeCompleted) return; recalculateStatusBarMessage (); } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotActionPrevToolOptionGroup1 () { if (!d->toolToolBar->shownToolWidget (0)) return; // We don't call toolEndShape() here because we want #23 in the file BUGS // to later work. d->toolToolBar->shownToolWidget (0)->selectPreviousOption (); updateToolOptionPrevNextActionsEnabled (); } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotActionNextToolOptionGroup1 () { if (!d->toolToolBar->shownToolWidget (0)) return; // We don't call toolEndShape() here because we want #23 in the file BUGS // to later work. d->toolToolBar->shownToolWidget (0)->selectNextOption (); updateToolOptionPrevNextActionsEnabled (); } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotActionPrevToolOptionGroup2 () { if (!d->toolToolBar->shownToolWidget (1)) return; // We don't call toolEndShape() here because we want #23 in the file BUGS // to later work. d->toolToolBar->shownToolWidget (1)->selectPreviousOption (); updateToolOptionPrevNextActionsEnabled (); } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotActionNextToolOptionGroup2 () { if (!d->toolToolBar->shownToolWidget (1)) return; // We don't call toolEndShape() here because we want #23 in the file BUGS // to later work. d->toolToolBar->shownToolWidget (1)->selectNextOption (); updateToolOptionPrevNextActionsEnabled (); } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotActionDrawOpaqueToggled () { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::slotActionDrawOpaqueToggled()"; #endif toolEndShape (); // TODO: How does this differ to setImageSelectionTransparency()? // ("kpToolWidgetBase::" is to access one overload shadowed by the override // of the other overload) d->toolToolBar->toolWidgetOpaqueOrTransparent ()->kpToolWidgetBase::setSelected ( (d->actionDrawOpaque->isChecked () ? 0/*row 0 = opaque*/ : 1/*row 1 = transparent*/), 0/*column*/); } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotActionDrawColorSimilarity () { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::slotActionDrawColorSimilarity()"; #endif toolEndShape (); d->colorToolBar->openColorSimilarityDialog (); } //--------------------------------------------------------------------- // public slots #define SLOT_TOOL(toolName) \ void kpMainWindow::slotTool##toolName () \ { \ if (!d->toolToolBar) \ return; \ \ if (tool () == d->tool##toolName) \ return; \ \ d->toolToolBar->selectTool (d->tool##toolName); \ } SLOT_TOOL (RectSelection) SLOT_TOOL (EllipticalSelection) SLOT_TOOL (FreeFormSelection) SLOT_TOOL (Text) diff --git a/mainWindow/kpMainWindow_View_Zoom.cpp b/mainWindow/kpMainWindow_View_Zoom.cpp index e32cf24e..840991f9 100644 --- a/mainWindow/kpMainWindow_View_Zoom.cpp +++ b/mainWindow/kpMainWindow_View_Zoom.cpp @@ -1,704 +1,707 @@ // REFACTOR: Clean up bits of this file /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "mainWindow/kpMainWindow.h" #include "kpMainWindowPrivate.h" #include "kpLogCategories.h" #include #include #include #include #include #include #include "kpDefs.h" #include "document/kpDocument.h" #include "kpThumbnail.h" #include "tools/kpTool.h" #include "widgets/toolbars/kpToolToolBar.h" #include "views/kpUnzoomedThumbnailView.h" #include "views/manager/kpViewManager.h" #include "kpViewScrollableContainer.h" #include "generic/kpWidgetMapper.h" #include "views/kpZoomedView.h" #include "views/kpZoomedThumbnailView.h" static int ZoomLevelFromString (const QString &stringIn) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow_View.cpp:ZoomLevelFromString(" << stringIn << ")"; #endif // Remove any non-digits kdelibs sometimes adds behind our back :( e.g.: // // 1. kdelibs adds accelerators to actions' text directly // 2. ',' is automatically added to change "1000%" to "1,000%" QString string = stringIn; string.remove (QRegExp ("[^0-9]")); #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\twithout non-digits='" << string << "'"; #endif // Convert zoom level to number. bool ok = false; int zoomLevel = string.toInt (&ok); #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tzoomLevel=" << zoomLevel; #endif if (!ok || zoomLevel < kpView::MinZoomLevel || zoomLevel > kpView::MaxZoomLevel) return 0; // error else return zoomLevel; } //--------------------------------------------------------------------- static QString ZoomLevelToString (int zoomLevel) { return i18n ("%1%", zoomLevel); } //--------------------------------------------------------------------- // private void kpMainWindow::setupViewMenuZoomActions () { KActionCollection *ac = actionCollection (); d->actionActualSize = KStandardAction::actualSize (this, SLOT (slotActualSize()), ac); d->actionFitToPage = KStandardAction::fitToPage (this, SLOT (slotFitToPage()), ac); d->actionFitToWidth = KStandardAction::fitToWidth (this, SLOT (slotFitToWidth()), ac); d->actionFitToHeight = KStandardAction::fitToHeight (this, SLOT (slotFitToHeight()), ac); d->actionZoomIn = KStandardAction::zoomIn (this, SLOT (slotZoomIn()), ac); d->actionZoomOut = KStandardAction::zoomOut (this, SLOT (slotZoomOut()), ac); d->actionZoom = ac->add ("view_zoom_to"); d->actionZoom->setText (i18n ("&Zoom")); connect (d->actionZoom, SIGNAL (triggered(QAction*)), SLOT (slotZoom())); d->actionZoom->setEditable (true); // create the zoom list for the 1st call to zoomTo() below d->zoomList.append (10); d->zoomList.append (25); d->zoomList.append (33); d->zoomList.append (50); d->zoomList.append (67); d->zoomList.append (75); d->zoomList.append (100); d->zoomList.append (200); d->zoomList.append (300); d->zoomList.append (400); d->zoomList.append (600); d->zoomList.append (800); d->zoomList.append (1000); d->zoomList.append (1200); d->zoomList.append (1600); } //--------------------------------------------------------------------- // private void kpMainWindow::enableViewMenuZoomDocumentActions (bool enable) { d->actionActualSize->setEnabled (enable); d->actionFitToPage->setEnabled (enable); d->actionFitToWidth->setEnabled (enable); d->actionFitToHeight->setEnabled (enable); d->actionZoomIn->setEnabled (enable); d->actionZoomOut->setEnabled (enable); d->actionZoom->setEnabled (enable); // TODO: for the time being, assume that we start at zoom 100% // with no grid // This function is only called when a new document is created // or an existing document is closed. So the following will // always be correct: zoomTo (100); } //--------------------------------------------------------------------- // private void kpMainWindow::sendZoomListToActionZoom () { QStringList items; const QList ::ConstIterator zoomListEnd (d->zoomList.end ()); for (QList ::ConstIterator it = d->zoomList.constBegin (); it != zoomListEnd; ++it) { items << ::ZoomLevelToString (*it); } // Work around a KDE bug - KSelectAction::setItems() enables the action. // David Faure said it won't be fixed because it's a feature used by // KRecentFilesAction. bool e = d->actionZoom->isEnabled (); d->actionZoom->setItems (items); if (e != d->actionZoom->isEnabled ()) d->actionZoom->setEnabled (e); } //--------------------------------------------------------------------- // private void kpMainWindow::zoomToPre (int zoomLevel) { // We're called quite early in the init process and/or when there might // not be a document or a view so we have a lot of "if (ptr)" guards. #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::zoomToPre(" << zoomLevel << ")"; #endif zoomLevel = qBound (kpView::MinZoomLevel, zoomLevel, kpView::MaxZoomLevel); // mute point since the thumbnail suffers from this too #if 0 else if (d->mainView && d->mainView->zoomLevelX () % 100 == 0 && zoomLevel % 100) { if (KMessageBox::warningContinueCancel (this, i18n ("Setting the zoom level to a value that is not a multiple of 100% " "results in imprecise editing and redraw glitches.\n" "Do you really want to set to zoom level to %1%?", zoomLevel), QString()/*caption*/, i18n ("Set Zoom Level to %1%", zoomLevel), "DoNotAskAgain_ZoomLevelNotMultipleOf100") != KMessageBox::Continue) { zoomLevel = d->mainView->zoomLevelX (); } } #endif int index = 0; QList ::Iterator it = d->zoomList.begin (); - while (index < (int) d->zoomList.count () && zoomLevel > *it) - it++, index++; + while (index < d->zoomList.count () && zoomLevel > *it) + { + it++; + index++; + } if (zoomLevel != *it) d->zoomList.insert (it, zoomLevel); // OPT: We get called twice on startup. sendZoomListToActionZoom() is very slow. sendZoomListToActionZoom (); #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tsetCurrentItem(" << index << ")"; #endif d->actionZoom->setCurrentItem (index); #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tcurrentItem=" << d->actionZoom->currentItem () << " action=" << d->actionZoom->action (d->actionZoom->currentItem ()) << " checkedAction" << d->actionZoom->selectableActionGroup ()->checkedAction () << endl;; #endif if (viewMenuDocumentActionsEnabled ()) { d->actionActualSize->setEnabled (zoomLevel != 100); d->actionZoomIn->setEnabled (d->actionZoom->currentItem () < (int) d->zoomList.count () - 1); d->actionZoomOut->setEnabled (d->actionZoom->currentItem () > 0); } // TODO: Is this actually needed? if (d->viewManager) d->viewManager->setQueueUpdates (); if (d->scrollView) { d->scrollView->setUpdatesEnabled (false); } } //--------------------------------------------------------------------- // private void kpMainWindow::zoomToPost () { #if DEBUG_KP_MAIN_WINDOW && 1 qCDebug(kpLogMainWindow) << "kpMainWindow::zoomToPost()"; #endif if (d->mainView) { actionShowGridUpdate (); updateMainViewGrid (); // Since Zoom Level KSelectAction on ToolBar grabs focus after changing // Zoom, switch back to the Main View. // TODO: back to the last view d->mainView->setFocus (); } // The view magnified and moved beneath the cursor if (tool ()) tool ()->somethingBelowTheCursorChanged (); if (d->scrollView) { // TODO: setUpdatesEnabled() should really return to old value // - not necessarily "true" d->scrollView->setUpdatesEnabled (true); } if (d->viewManager && d->viewManager->queueUpdates ()/*just in case*/) d->viewManager->restoreQueueUpdates (); setStatusBarZoom (d->mainView ? d->mainView->zoomLevelX () : 0); #if DEBUG_KP_MAIN_WINDOW && 1 qCDebug(kpLogMainWindow) << "kpMainWindow::zoomToPost() done"; #endif } //--------------------------------------------------------------------- // private void kpMainWindow::zoomTo (int zoomLevel, bool centerUnderCursor) { zoomToPre (zoomLevel); if (d->scrollView && d->mainView) { #if DEBUG_KP_MAIN_WINDOW && 1 qCDebug(kpLogMainWindow) << "\tscrollView contentsX=" << d->scrollView->horizontalScrollBar()->value () << " contentsY=" << d->scrollView->verticalScrollBar()->value () << " contentsWidth=" << d->scrollView->widget()->width () << " contentsHeight=" << d->scrollView->widget()->height () << " visibleWidth=" << d->scrollView->viewport()->width () << " visibleHeight=" << d->scrollView->viewport()->height () << " oldZoomX=" << d->mainView->zoomLevelX () << " oldZoomY=" << d->mainView->zoomLevelY () << " newZoom=" << zoomLevel << endl; #endif // TODO: when changing from no scrollbars to scrollbars, Qt lies about // visibleWidth() & visibleHeight() (doesn't take into account the // space taken by the would-be scrollbars) until it updates the // scrollview; hence the centering is off by about 5-10 pixels. // TODO: Use visibleRect() for greater accuracy? // Or use kpAbstractScrollAreaUtils::EstimateUsableArea() // instead of ScrollView::visible{Width,Height}(), as // per zoomToRect()? int viewX, viewY; bool targetDocAvail = false; double targetDocX = -1, targetDocY = -1; if (centerUnderCursor && d->viewManager && d->viewManager->viewUnderCursor ()) { kpView *const vuc = d->viewManager->viewUnderCursor (); QPoint viewPoint = vuc->mouseViewPoint (); // vuc->transformViewToDoc() returns QPoint which only has int // accuracy so we do X and Y manually. targetDocX = vuc->transformViewToDocX (viewPoint.x ()); targetDocY = vuc->transformViewToDocY (viewPoint.y ()); targetDocAvail = true; if (vuc != d->mainView) viewPoint = vuc->transformViewToOtherView (viewPoint, d->mainView); viewX = viewPoint.x (); viewY = viewPoint.y (); } else { viewX = d->scrollView->horizontalScrollBar()->value () + qMin (d->mainView->width (), d->scrollView->viewport()->width ()) / 2; viewY = d->scrollView->verticalScrollBar()->value () + qMin (d->mainView->height (), d->scrollView->viewport()->height ()) / 2; } int newCenterX = viewX * zoomLevel / d->mainView->zoomLevelX (); int newCenterY = viewY * zoomLevel / d->mainView->zoomLevelY (); // Do the zoom. d->mainView->setZoomLevel (zoomLevel, zoomLevel); #if DEBUG_KP_MAIN_WINDOW && 1 qCDebug(kpLogMainWindow) << "\tvisibleWidth=" << d->scrollView->viewport()->width () << " visibleHeight=" << d->scrollView->viewport()->height () << endl; qCDebug(kpLogMainWindow) << "\tnewCenterX=" << newCenterX << " newCenterY=" << newCenterY << endl; #endif d->scrollView->horizontalScrollBar()->setValue(newCenterX - (d->scrollView->viewport()->width() / 2)); d->scrollView->verticalScrollBar()->setValue(newCenterY - (d->scrollView->viewport()->height() / 2)); if (centerUnderCursor && targetDocAvail && d->viewManager && d->viewManager->viewUnderCursor ()) { // Move the mouse cursor so that it is still above the same // document pixel as before the zoom. kpView *const vuc = d->viewManager->viewUnderCursor (); #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tcenterUnderCursor: reposition cursor; viewUnderCursor=" << vuc->objectName () << endl; #endif const double viewX = vuc->transformDocToViewX (targetDocX); const double viewY = vuc->transformDocToViewY (targetDocY); // Rounding error from zooming in and out :( // TODO: do everything in terms of tool doc points in type "double". const QPoint viewPoint ((int) viewX, (int) viewY); #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\t\tdoc: (" << targetDocX << "," << targetDocY << ")" << " viewUnderCursor: (" << viewX << "," << viewY << ")" << endl; #endif if (vuc->visibleRegion ().contains (viewPoint)) { const QPoint globalPoint = kpWidgetMapper::toGlobal (vuc, viewPoint); #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\t\tglobalPoint=" << globalPoint; #endif // TODO: Determine some sane cursor flashing indication - // cursor movement is convenient but not conventional. // // Major problem: if using QApplication::setOverrideCursor() // and in some stage of flash and window quits. // // Or if using kpView::setCursor() and change tool. QCursor::setPos (globalPoint); } // e.g. Zoom to 200%, scroll mainView to bottom-right. // Unzoomed Thumbnail shows top-left portion of bottom-right of // mainView. // // Aim cursor at bottom-right of thumbnail and zoom out with // CTRL+Wheel. // // If mainView is now small enough to largely not need scrollbars, // Unzoomed Thumbnail scrolls to show _top-left_ portion // _of top-left_ of mainView. // // Unzoomed Thumbnail no longer contains the point we zoomed out // on top of. else { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\t\twon't move cursor - would get outside view" << endl; #endif // TODO: Sane cursor flashing indication that indicates // that the normal cursor movement didn't happen. } } #if DEBUG_KP_MAIN_WINDOW && 1 qCDebug(kpLogMainWindow) << "\t\tcheck (contentsX=" << d->scrollView->horizontalScrollBar()->value () << ",contentsY=" << d->scrollView->verticalScrollBar()->value () << ")" << endl; #endif } zoomToPost (); } //--------------------------------------------------------------------- // private void kpMainWindow::zoomToRect (const QRect &normalizedDocRect, bool accountForGrips, bool careAboutWidth, bool careAboutHeight) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::zoomToRect(normalizedDocRect=" << normalizedDocRect << ",accountForGrips=" << accountForGrips << ",careAboutWidth=" << careAboutWidth << ",careAboutHeight=" << careAboutHeight << ")"; #endif // You can't care about nothing. Q_ASSERT (careAboutWidth || careAboutHeight); // The size of the scroll view minus the current or future scrollbars. const QSize usableScrollArea (d->scrollView->maximumViewportSize().width() - d->scrollView->verticalScrollBar()->sizeHint().width(), d->scrollView->maximumViewportSize().height() - d->scrollView->horizontalScrollBar()->sizeHint().height()); #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "size=" << d->scrollView->maximumViewportSize() << "scrollbar w=" << d->scrollView->verticalScrollBar()->sizeHint().width() << "usableSize=" << usableScrollArea; #endif // Handle rounding error, mis-estimating the scroll view size and // cosmic rays. We do this because we really don't want unnecessary // scrollbars. This seems to need to be at least 2 for slotFitToWidth() // and friends. // least 2. // TODO: I might have fixed this but check later. const int slack = 0; // The grip and slack are in view coordinates but are never zoomed. const int viewWidth = usableScrollArea.width () - (accountForGrips ? kpGrip::Size : 0) - slack; const int viewHeight = usableScrollArea.height () - (accountForGrips ? kpGrip::Size : 0) - slack; // We want the selected document rectangle to fill the scroll view. // // The integer arithmetic rounds down, rather than to the nearest zoom // level, as rounding down guarantees that the view, at the zoom level, // will fit inside x . const int zoomX = careAboutWidth ? qMax (1, viewWidth * 100 / normalizedDocRect.width ()) : INT_MAX; const int zoomY = careAboutHeight ? qMax (1, viewHeight * 100 / normalizedDocRect.height ()) : INT_MAX; // Since kpView only supports identical horizontal and vertical zooms, // choose the one that will show the greatest amount of document // content. const int zoomLevel = qMin (zoomX, zoomY); #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tzoomX=" << zoomX << " zoomY=" << zoomY << " -> zoomLevel=" << zoomLevel << endl; #endif zoomToPre (zoomLevel); { d->mainView->setZoomLevel (zoomLevel, zoomLevel); const QPoint viewPoint = d->mainView->transformDocToView (normalizedDocRect.topLeft ()); d->scrollView->horizontalScrollBar()->setValue(viewPoint.x()); d->scrollView->verticalScrollBar()->setValue(viewPoint.y()); } zoomToPost (); } //--------------------------------------------------------------------- // public slot void kpMainWindow::slotActualSize () { zoomTo (100); } //--------------------------------------------------------------------- // public slot void kpMainWindow::slotFitToPage () { if ( d->document ) { zoomToRect ( d->document->rect (), true/*account for grips*/, true/*care about width*/, true/*care about height*/); } } //--------------------------------------------------------------------- // public slot void kpMainWindow::slotFitToWidth () { if ( d->document ) { const QRect docRect ( 0/*x*/, (int) d->mainView->transformViewToDocY (d->scrollView->verticalScrollBar()->value ())/*maintain y*/, d->document->width (), 1/*don't care about height*/); zoomToRect ( docRect, true/*account for grips*/, true/*care about width*/, false/*don't care about height*/); } } //--------------------------------------------------------------------- // public slot void kpMainWindow::slotFitToHeight () { if ( d->document ) { const QRect docRect ( (int) d->mainView->transformViewToDocX (d->scrollView->horizontalScrollBar()->value ())/*maintain x*/, 0/*y*/, 1/*don't care about width*/, d->document->height ()); zoomToRect ( docRect, true/*account for grips*/, false/*don't care about width*/, true/*care about height*/); } } //--------------------------------------------------------------------- // public void kpMainWindow::zoomIn (bool centerUnderCursor) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::zoomIn(centerUnderCursor=" << centerUnderCursor << ") currentItem=" << d->actionZoom->currentItem () << endl; #endif const int targetItem = d->actionZoom->currentItem () + 1; if (targetItem >= (int) d->zoomList.count ()) return; d->actionZoom->setCurrentItem (targetItem); #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tnew currentItem=" << d->actionZoom->currentItem (); #endif zoomAccordingToZoomAction (centerUnderCursor); } //--------------------------------------------------------------------- // public void kpMainWindow::zoomOut (bool centerUnderCursor) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::zoomOut(centerUnderCursor=" << centerUnderCursor << ") currentItem=" << d->actionZoom->currentItem () << endl; #endif const int targetItem = d->actionZoom->currentItem () - 1; if (targetItem < 0) return; d->actionZoom->setCurrentItem (targetItem); #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "\tnew currentItem=" << d->actionZoom->currentItem (); #endif zoomAccordingToZoomAction (centerUnderCursor); } //--------------------------------------------------------------------- // public slot void kpMainWindow::slotZoomIn () { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::slotZoomIn ()"; #endif zoomIn (false/*don't center under cursor*/); } //--------------------------------------------------------------------- // public slot void kpMainWindow::slotZoomOut () { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::slotZoomOut ()"; #endif zoomOut (false/*don't center under cursor*/); } //--------------------------------------------------------------------- // public void kpMainWindow::zoomAccordingToZoomAction (bool centerUnderCursor) { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::zoomAccordingToZoomAction(centerUnderCursor=" << centerUnderCursor << ") currentItem=" << d->actionZoom->currentItem () << " currentText=" << d->actionZoom->currentText () << endl; #endif // This might be a new zoom level the user has typed in. zoomTo (::ZoomLevelFromString (d->actionZoom->currentText ()), centerUnderCursor); } //--------------------------------------------------------------------- // private slot void kpMainWindow::slotZoom () { #if DEBUG_KP_MAIN_WINDOW qCDebug(kpLogMainWindow) << "kpMainWindow::slotZoom () index=" << d->actionZoom->currentItem () << " text='" << d->actionZoom->currentText () << "'" << endl; #endif zoomAccordingToZoomAction (false/*don't center under cursor*/); } //--------------------------------------------------------------------- diff --git a/tools/kpTool.cpp b/tools/kpTool.cpp index 8f6bfff7..92813a21 100644 --- a/tools/kpTool.cpp +++ b/tools/kpTool.cpp @@ -1,259 +1,262 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // // Tool initialisation and basic accessors. // #define DEBUG_KP_TOOL 0 #include "kpTool.h" #include "kpToolPrivate.h" #include #include #include "kpLogCategories.h" #include #include "imagelib/kpColor.h" #include "widgets/toolbars/kpColorToolBar.h" #include "kpDefs.h" #include "tools/kpToolAction.h" #include "environments/tools/kpToolEnvironment.h" #include "widgets/toolbars/kpToolToolBar.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" #undef environ // macro on win32 //--------------------------------------------------------------------- kpTool::kpTool(const QString &text, const QString &description, int key, kpToolEnvironment *environ, QObject *parent, const QString &name) : QObject(parent), d(new kpToolPrivate()) { d->key = key; d->action = nullptr; d->ignoreColorSignals = 0; - d->shiftPressed = false, d->controlPressed = false, d->altPressed = false; // set in beginInternal() + d->shiftPressed = false; + d->controlPressed = false; + d->altPressed = false; // set in beginInternal() d->beganDraw = false; - d->text = text, d->description = description; + d->text = text; + d->description = description; d->began = false; d->viewUnderStartPoint = nullptr; d->userShapeStartPoint = KP_INVALID_POINT; d->userShapeEndPoint = KP_INVALID_POINT; d->userShapeSize = KP_INVALID_SIZE; d->environ = environ; setObjectName(name); initAction(); } //--------------------------------------------------------------------- kpTool::~kpTool () { // before destructing, stop using the tool if (d->began) endInternal (); delete d->action; delete d; } //--------------------------------------------------------------------- // private void kpTool::initAction () { KActionCollection *ac = d->environ->actionCollection (); Q_ASSERT (ac); d->action = new kpToolAction(text(), objectName(), shortcutForKey(d->key), this, SIGNAL(actionActivated()), ac, objectName()); // Make tools mutually exclusive by placing them in the same group. d->action->setActionGroup(d->environ->toolsActionGroup ()); d->action->setWhatsThis(d->description); connect(d->action, SIGNAL(changed()), this, SIGNAL (actionToolTipChanged())); } //--------------------------------------------------------------------- // public QString kpTool::text () const { return d->text; } //--------------------------------------------------------------------- static bool KeyIsText (int key) { // TODO: should work like !QKeyEvent::text().isEmpty() return !(key & (Qt::KeyboardModifierMask ^ Qt::ShiftModifier)); } //--------------------------------------------------------------------- // public static QString kpTool::toolTipForTextAndShortcut (const QString &text, const QList &shortcut) { foreach(const QKeySequence &seq, shortcut) { if (seq.count () == 1 && ::KeyIsText (seq [0])) return i18nc (" ()", "%1 (%2)", text, seq.toString ().toUpper ()); } return text; } //--------------------------------------------------------------------- QString kpTool::toolTip () const { return toolTipForTextAndShortcut(d->text, d->action->shortcuts()); } //--------------------------------------------------------------------- // public static QList kpTool::shortcutForKey (int key) { QList shortcut; if (key) { shortcut.append (QKeySequence (key)); // (CTRL+, ALT+, CTRL+ALT+, CTRL+SHIFT+ // all clash with global KDE shortcuts) shortcut.append (QKeySequence (Qt::ALT + Qt::SHIFT + key)); } return shortcut; } //--------------------------------------------------------------------- // public kpToolAction *kpTool::action () const { return d->action; } //--------------------------------------------------------------------- kpDocument *kpTool::document () const { return d->environ->document (); } //--------------------------------------------------------------------- kpViewManager *kpTool::viewManager () const { return d->environ->viewManager (); } //--------------------------------------------------------------------- kpToolToolBar *kpTool::toolToolBar () const { return d->environ->toolToolBar (); } //--------------------------------------------------------------------- kpColor kpTool::color (int which) const { return d->environ->color (which); } //--------------------------------------------------------------------- kpColor kpTool::foregroundColor () const { return color (0); } //--------------------------------------------------------------------- kpColor kpTool::backgroundColor () const { return color (1); } //--------------------------------------------------------------------- // TODO: Some of these might not be common enough. // Just put in kpToolEnvironment? double kpTool::colorSimilarity () const { return d->environ->colorSimilarity (); } int kpTool::processedColorSimilarity () const { return d->environ->processedColorSimilarity (); } kpColor kpTool::oldForegroundColor () const { return d->environ->oldForegroundColor (); } kpColor kpTool::oldBackgroundColor () const { return d->environ->oldBackgroundColor (); } double kpTool::oldColorSimilarity () const { return d->environ->oldColorSimilarity (); } kpCommandHistory *kpTool::commandHistory () const { return d->environ->commandHistory (); } kpToolEnvironment *kpTool::environ () const { return d->environ; } diff --git a/tools/kpTool_KeyboardEvents.cpp b/tools/kpTool_KeyboardEvents.cpp index 92ba121d..9e1a032b 100644 --- a/tools/kpTool_KeyboardEvents.cpp +++ b/tools/kpTool_KeyboardEvents.cpp @@ -1,412 +1,413 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // // Tool reaction to view keyboard input. // #define DEBUG_KP_TOOL 0 // TODO: reduce number of includes #include "tools/kpTool.h" #include "kpToolPrivate.h" #include #include #include #include #include #include "kpLogCategories.h" #include #include "imagelib/kpColor.h" #include "widgets/toolbars/kpColorToolBar.h" #include "kpDefs.h" #include "pixmapfx/kpPixmapFX.h" #include "tools/kpToolAction.h" #include "environments/tools/kpToolEnvironment.h" #include "widgets/toolbars/kpToolToolBar.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" //--------------------------------------------------------------------- void kpTool::seeIfAndHandleModifierKey (QKeyEvent *e) { switch (e->key ()) { case 0: case Qt::Key_unknown: #if DEBUG_KP_TOOL && 0 qCDebug(kpLogTools) << "kpTool::seeIfAndHandleModifierKey() picked up unknown key!"; #endif // HACK: around Qt bug: if you hold a modifier before you start the // program and then release it over the view, // Qt reports it as the release of an unknown key // Qt4 update: I don't think this happens anymore... // --- fall thru and update all modifiers --- case Qt::Key_Alt: case Qt::Key_Shift: case Qt::Key_Control: #if DEBUG_KP_TOOL && 0 qCDebug(kpLogTools) << "kpTool::setIfAndHandleModifierKey() accepting"; #endif keyUpdateModifierState (e); e->accept (); break; } } //--------------------------------------------------------------------- // Returns in and the direction the arrow key "e->key()" is // pointing in or (0,0) if it's not a recognised arrow key. void kpTool::arrowKeyPressDirection (const QKeyEvent *e, int *dx, int *dy) { int dxLocal = 0, dyLocal = 0; switch (e->key ()) { - case Qt::Key_Home: dxLocal = -1, dyLocal = -1; break; - case Qt::Key_Up: dyLocal = -1; break; - case Qt::Key_PageUp: dxLocal = +1, dyLocal = -1; break; + case Qt::Key_Home: dxLocal = -1; dyLocal = -1; break; + case Qt::Key_Up: dyLocal = -1; break; + case Qt::Key_PageUp: dxLocal = +1; dyLocal = -1; break; - case Qt::Key_Left: dxLocal = -1; break; - case Qt::Key_Right: dxLocal = +1; break; + case Qt::Key_Left: dxLocal = -1; break; + case Qt::Key_Right: dxLocal = +1; break; - case Qt::Key_End: dxLocal = -1, dyLocal = +1; break; - case Qt::Key_Down: dyLocal = +1; break; - case Qt::Key_PageDown: dxLocal = +1, dyLocal = +1; break; + case Qt::Key_End: dxLocal = -1; dyLocal = +1; break; + case Qt::Key_Down: dyLocal = +1; break; + case Qt::Key_PageDown: dxLocal = +1; dyLocal = +1; break; } if (dx) *dx = dxLocal; if (dy) *dy = dyLocal; } //--------------------------------------------------------------------- void kpTool::seeIfAndHandleArrowKeyPress (QKeyEvent *e) { int dx, dy; arrowKeyPressDirection (e, &dx, &dy); if (dx == 0 && dy == 0) return; kpView * const view = viewUnderCursor (); if (!view) return; const QPoint oldPoint = view->mapFromGlobal (QCursor::pos ()); #if DEBUG_KP_TOOL && 0 qCDebug(kpLogTools) << "\toldPoint=" << oldPoint << " dx=" << dx << " dy=" << dy << endl; #endif const int viewIncX = (dx ? qMax (1, view->zoomLevelX () / 100) * dx : 0); const int viewIncY = (dy ? qMax (1, view->zoomLevelY () / 100) * dy : 0); int newViewX = oldPoint.x () + viewIncX; int newViewY = oldPoint.y () + viewIncY; #if DEBUG_KP_TOOL && 0 qCDebug(kpLogTools) << "\tnewPoint=" << QPoint (newViewX, newViewY); #endif // Make sure we really moved at least one doc point (needed due to // rounding error). if (view->transformViewToDoc (QPoint (newViewX, newViewY)) == view->transformViewToDoc (oldPoint)) { - newViewX += viewIncX, newViewY += viewIncY; + newViewX += viewIncX; + newViewY += viewIncY; #if DEBUG_KP_TOOL && 0 qCDebug(kpLogTools) << "\tneed adjust for doc - newPoint=" << QPoint (newViewX, newViewY) << endl; #endif } // TODO: visible width/height (e.g. with scrollbars) const int x = qMin (qMax (newViewX, 0), view->width () - 1); const int y = qMin (qMax (newViewY, 0), view->height () - 1); // QCursor::setPos conveniently causes mouseMoveEvents QCursor::setPos (view->mapToGlobal (QPoint (x, y))); e->accept (); } //--------------------------------------------------------------------- bool kpTool::isDrawKey (int key) { return (key == Qt::Key_Enter || key == Qt::Key_Return || key == Qt::Key_Insert || key == Qt::Key_Clear/*Numpad 5 Key*/ || key == Qt::Key_L); } //--------------------------------------------------------------------- void kpTool::seeIfAndHandleBeginDrawKeyPress (QKeyEvent *e) { if (e->isAutoRepeat ()) return; if (!isDrawKey (e->key ())) return; #if DEBUG_KP_TOOL && 0 qCDebug(kpLogTools) << "kpTool::seeIfAndHandleBeginDrawKeyPress() accept"; #endif // TODO: wrong for dragging lines outside of view (for e.g.) kpView * const view = viewUnderCursor (); if (!view) return; // TODO: what about the modifiers? QMouseEvent me (QEvent::MouseButtonPress, view->mapFromGlobal (QCursor::pos ()), Qt::LeftButton, Qt::LeftButton/*button state after event*/, Qt::NoModifier); mousePressEvent (&me); e->accept (); } void kpTool::seeIfAndHandleEndDrawKeyPress (QKeyEvent *e) { #if DEBUG_KP_TOOL && 0 qCDebug(kpLogTools) << "kpTool::setIfAndHandleEndDrawKeyPress() key=" << e->key () << " isAutoRepeat=" << e->isAutoRepeat () << " isDrawKey=" << isDrawKey (e->key ()) << " view=" << viewUnderCursor () << endl; #endif if (e->isAutoRepeat ()) return; if (!isDrawKey (e->key ())) return; #if DEBUG_KP_TOOL && 0 qCDebug(kpLogTools) << "kpTool::seeIfAndHandleEndDrawKeyPress() accept"; #endif kpView * const view = viewUnderCursor (); if (!view) return; // TODO: what about the modifiers? QMouseEvent me (QEvent::MouseButtonRelease, view->mapFromGlobal (QCursor::pos ()), Qt::LeftButton, Qt::NoButton/*button state after event*/, Qt::NoModifier); mouseReleaseEvent (&me); e->accept (); } //--------------------------------------------------------------------- void kpTool::keyPressEvent (QKeyEvent *e) { #if DEBUG_KP_TOOL && 0 qCDebug(kpLogTools) << "kpTool::keyPressEvent() key=" << (int *) e->key () << " stateAfter: modifiers=" << (int *) (int) e->modifiers () << " isAutoRep=" << e->isAutoRepeat () << endl; #endif e->ignore (); seeIfAndHandleModifierKey (e); if (e->isAccepted ()) return; seeIfAndHandleArrowKeyPress (e); if (e->isAccepted ()) return; seeIfAndHandleBeginDrawKeyPress (e); if (e->isAccepted ()) return; switch (e->key ()) { case Qt::Key_Delete: d->environ->deleteSelection (); break; case Qt::Key_Escape: if (hasBegunDraw ()) { cancelShapeInternal (); e->accept (); } break; } } //--------------------------------------------------------------------- void kpTool::keyReleaseEvent (QKeyEvent *e) { #if DEBUG_KP_TOOL && 0 qCDebug(kpLogTools) << "kpTool::keyReleaseEvent() key=" << (int *) e->key () << " stateAfter: modifiers=" << (int *) (int) e->modifiers () << " isAutoRep=" << e->isAutoRepeat () << endl; #endif e->ignore (); seeIfAndHandleModifierKey (e); if (e->isAccepted ()) return; seeIfAndHandleEndDrawKeyPress (e); if (e->isAccepted ()) return; } //--------------------------------------------------------------------- // private void kpTool::keyUpdateModifierState (QKeyEvent *e) { #if DEBUG_KP_TOOL && 0 qCDebug(kpLogTools) << "kpTool::keyUpdateModifierState() e->key=" << (int *) e->key (); qCDebug(kpLogTools) << "\tshift=" << (e->modifiers () & Qt::ShiftModifier) << " control=" << (e->modifiers () & Qt::ControlModifier) << " alt=" << (e->modifiers () & Qt::AltModifier) << endl; #endif if (e->key () & (Qt::Key_Alt | Qt::Key_Shift | Qt::Key_Control)) { #if DEBUG_KP_TOOL && 0 qCDebug(kpLogTools) << "\t\tmodifier changed - use e's claims"; #endif setShiftPressed (e->modifiers () & Qt::ShiftModifier); setControlPressed (e->modifiers () & Qt::ControlModifier); setAltPressed (e->modifiers () & Qt::AltModifier); } // See seeIfAndHandleModifierKey() for why this code path exists. else { #if DEBUG_KP_TOOL && 0 qCDebug(kpLogTools) << "\t\tmodifiers not changed - figure out the truth"; #endif const Qt::KeyboardModifiers keyState = QApplication::keyboardModifiers (); setShiftPressed (keyState & Qt::ShiftModifier); setControlPressed (keyState & Qt::ControlModifier); setAltPressed (keyState & Qt::AltModifier); } } //--------------------------------------------------------------------- void kpTool::notifyModifierStateChanged () { if (careAboutModifierState ()) { if (d->beganDraw) draw (d->currentPoint, d->lastPoint, normalizedRect ()); else { d->currentPoint = calculateCurrentPoint (); d->currentViewPoint = calculateCurrentPoint (false/*view point*/); hover (d->currentPoint); } } } //--------------------------------------------------------------------- void kpTool::setShiftPressed (bool pressed) { if (pressed == d->shiftPressed) return; d->shiftPressed = pressed; notifyModifierStateChanged (); } //--------------------------------------------------------------------- void kpTool::setControlPressed (bool pressed) { if (pressed == d->controlPressed) return; d->controlPressed = pressed; notifyModifierStateChanged (); } //--------------------------------------------------------------------- void kpTool::setAltPressed (bool pressed) { if (pressed == d->altPressed) return; d->altPressed = pressed; notifyModifierStateChanged (); } //--------------------------------------------------------------------- diff --git a/tools/rectangular/kpToolRectangularBase.cpp b/tools/rectangular/kpToolRectangularBase.cpp index b79a1717..b6819477 100644 --- a/tools/rectangular/kpToolRectangularBase.cpp +++ b/tools/rectangular/kpToolRectangularBase.cpp @@ -1,389 +1,390 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define DEBUG_KP_TOOL_RECTANGULAR_BASE 0 #include "tools/rectangular/kpToolRectangularBase.h" #include #include "kpLogCategories.h" #include #include "imagelib/kpColor.h" #include "commands/kpCommandHistory.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "imagelib/kpPainter.h" #include "pixmapfx/kpPixmapFX.h" #include "layers/tempImage/kpTempImage.h" #include "environments/tools/kpToolEnvironment.h" #include "commands/tools/rectangular/kpToolRectangularCommand.h" #include "widgets/toolbars/kpToolToolBar.h" #include "widgets/toolbars/options/kpToolWidgetFillStyle.h" #include "widgets/toolbars/options/kpToolWidgetLineWidth.h" #include "views/kpView.h" #include "views/manager/kpViewManager.h" //--------------------------------------------------------------------- struct kpToolRectangularBasePrivate { kpToolRectangularBase::DrawShapeFunc drawShapeFunc; kpToolWidgetLineWidth *toolWidgetLineWidth; kpToolWidgetFillStyle *toolWidgetFillStyle; QRect toolRectangleRect; }; //--------------------------------------------------------------------- kpToolRectangularBase::kpToolRectangularBase ( const QString &text, const QString &description, DrawShapeFunc drawShapeFunc, int key, kpToolEnvironment *environ, QObject *parent, const QString &name) : kpTool (text, description, key, environ, parent, name), d (new kpToolRectangularBasePrivate ()) { d->drawShapeFunc = drawShapeFunc; - d->toolWidgetLineWidth = nullptr, d->toolWidgetFillStyle = nullptr; + d->toolWidgetLineWidth = nullptr; + d->toolWidgetFillStyle = nullptr; } //--------------------------------------------------------------------- kpToolRectangularBase::~kpToolRectangularBase () { delete d; } //--------------------------------------------------------------------- // private slot virtual void kpToolRectangularBase::slotLineWidthChanged () { if (hasBegunDraw ()) updateShape (); } //--------------------------------------------------------------------- // private slot virtual void kpToolRectangularBase::slotFillStyleChanged () { if (hasBegunDraw ()) updateShape (); } //--------------------------------------------------------------------- // private QString kpToolRectangularBase::haventBegunDrawUserMessage () const { return i18n ("Drag to draw."); } //--------------------------------------------------------------------- // virtual void kpToolRectangularBase::begin () { #if DEBUG_KP_TOOL_RECTANGULAR_BASE qCDebug(kpLogTools) << "kpToolRectangularBase::begin ()"; #endif kpToolToolBar *tb = toolToolBar (); Q_ASSERT (tb); #if DEBUG_KP_TOOL_RECTANGULAR_BASE qCDebug(kpLogTools) << "\ttoolToolBar=" << tb; #endif d->toolWidgetLineWidth = tb->toolWidgetLineWidth (); connect (d->toolWidgetLineWidth, SIGNAL (lineWidthChanged(int)), this, SLOT (slotLineWidthChanged())); d->toolWidgetLineWidth->show (); d->toolWidgetFillStyle = tb->toolWidgetFillStyle (); connect (d->toolWidgetFillStyle, SIGNAL (fillStyleChanged(kpToolWidgetFillStyle::FillStyle)), this, SLOT (slotFillStyleChanged())); d->toolWidgetFillStyle->show (); viewManager ()->setCursor (QCursor (Qt::ArrowCursor)); setUserMessage (haventBegunDrawUserMessage ()); } //--------------------------------------------------------------------- // virtual void kpToolRectangularBase::end () { #if DEBUG_KP_TOOL_RECTANGULAR_BASE qCDebug(kpLogTools) << "kpToolRectangularBase::end ()"; #endif if (d->toolWidgetLineWidth) { disconnect (d->toolWidgetLineWidth, SIGNAL (lineWidthChanged(int)), this, SLOT (slotLineWidthChanged())); d->toolWidgetLineWidth = nullptr; } if (d->toolWidgetFillStyle) { disconnect (d->toolWidgetFillStyle, SIGNAL (fillStyleChanged(kpToolWidgetFillStyle::FillStyle)), this, SLOT (slotFillStyleChanged())); d->toolWidgetFillStyle = nullptr; } viewManager ()->unsetCursor (); } //--------------------------------------------------------------------- void kpToolRectangularBase::applyModifiers () { QRect rect = normalizedRect (); #if DEBUG_KP_TOOL_RECTANGULAR_BASE qCDebug(kpLogTools) << "kpToolRectangularBase::applyModifiers(" << rect << ") shift=" << shiftPressed () << " ctrl=" << controlPressed () << endl; #endif // user wants to startPoint () == center if (controlPressed ()) { int xdiff = qAbs (startPoint ().x () - currentPoint ().x ()); int ydiff = qAbs (startPoint ().y () - currentPoint ().y ()); rect = QRect (startPoint ().x () - xdiff, startPoint ().y () - ydiff, xdiff * 2 + 1, ydiff * 2 + 1); } // user wants major axis == minor axis: // rectangle --> square // rounded rectangle --> rounded square // ellipse --> circle if (shiftPressed ()) { if (!controlPressed ()) { if (rect.width () < rect.height ()) { if (startPoint ().y () == rect.y ()) rect.setHeight (rect.width ()); else rect.setY (rect.bottom () - rect.width () + 1); } else { if (startPoint ().x () == rect.x ()) rect.setWidth (rect.height ()); else rect.setX (rect.right () - rect.height () + 1); } } // have to maintain the center else { if (rect.width () < rect.height ()) { QPoint center = rect.center (); rect.setHeight (rect.width ()); rect.moveCenter (center); } else { QPoint center = rect.center (); rect.setWidth (rect.height ()); rect.moveCenter (center); } } } d->toolRectangleRect = rect; } //--------------------------------------------------------------------- void kpToolRectangularBase::beginDraw () { setUserMessage (cancelUserMessage ()); } //--------------------------------------------------------------------- // private kpColor kpToolRectangularBase::drawingForegroundColor () const { return color (mouseButton ()); } //--------------------------------------------------------------------- // private kpColor kpToolRectangularBase::drawingBackgroundColor () const { const kpColor foregroundColor = color (mouseButton ()); const kpColor backgroundColor = color (1 - mouseButton ()); return d->toolWidgetFillStyle->drawingBackgroundColor ( foregroundColor, backgroundColor); } //--------------------------------------------------------------------- // private void kpToolRectangularBase::updateShape () { kpImage image = document ()->getImageAt (d->toolRectangleRect); // Invoke shape drawing function passed in ctor. (*d->drawShapeFunc) (&image, 0, 0, d->toolRectangleRect.width (), d->toolRectangleRect.height (), drawingForegroundColor (), d->toolWidgetLineWidth->lineWidth (), drawingBackgroundColor ()); kpTempImage newTempImage (false/*always display*/, kpTempImage::SetImage/*render mode*/, d->toolRectangleRect.topLeft (), image); viewManager ()->setFastUpdates (); viewManager ()->setTempImage (newTempImage); viewManager ()->restoreFastUpdates (); } //--------------------------------------------------------------------- void kpToolRectangularBase::draw (const QPoint &, const QPoint &, const QRect &) { applyModifiers (); updateShape (); // Recover the start and end points from the transformed & normalized d->toolRectangleRect // S. or S or SC or S == C // .C C if (currentPoint ().x () >= startPoint ().x () && currentPoint ().y () >= startPoint ().y ()) { setUserShapePoints (d->toolRectangleRect.topLeft (), d->toolRectangleRect.bottomRight ()); } // .C or C // S. S else if (currentPoint ().x () >= startPoint ().x () && currentPoint ().y () < startPoint ().y ()) { setUserShapePoints (d->toolRectangleRect.bottomLeft (), d->toolRectangleRect.topRight ()); } // .S or CS // C. else if (currentPoint ().x () < startPoint ().x () && currentPoint ().y () >= startPoint ().y ()) { setUserShapePoints (d->toolRectangleRect.topRight (), d->toolRectangleRect.bottomLeft ()); } // C. // .S else { setUserShapePoints (d->toolRectangleRect.bottomRight (), d->toolRectangleRect.topLeft ()); } } //--------------------------------------------------------------------- void kpToolRectangularBase::cancelShape () { viewManager ()->invalidateTempImage (); setUserMessage (i18n ("Let go of all the mouse buttons.")); } //--------------------------------------------------------------------- void kpToolRectangularBase::releasedAllButtons () { setUserMessage (haventBegunDrawUserMessage ()); } //--------------------------------------------------------------------- void kpToolRectangularBase::endDraw (const QPoint &, const QRect &) { applyModifiers (); // TODO: flicker // Later: So why can't we use kpViewManager::setQueueUpdates()? Check SVN // log to see if this method was not available at the time of the // TODO, hence justifying the TODO. // Later2: kpToolPolygonalBase, and perhaps, other shapes will have the // same problem. viewManager ()->invalidateTempImage (); environ ()->commandHistory ()->addCommand ( new kpToolRectangularCommand ( text (), d->drawShapeFunc, d->toolRectangleRect, drawingForegroundColor (), d->toolWidgetLineWidth->lineWidth (), drawingBackgroundColor (), environ ()->commandEnvironment ())); setUserMessage (haventBegunDrawUserMessage ()); } //--------------------------------------------------------------------- diff --git a/views/kpView.cpp b/views/kpView.cpp index 9821f444..454c620f 100644 --- a/views/kpView.cpp +++ b/views/kpView.cpp @@ -1,671 +1,672 @@ /* Copyright (c) 2003-2007 Clarence Dang Copyright (c) 2005 Kazuki Ohta Copyright (c) 2010 Tasuku Suzuki All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define DEBUG_KP_VIEW 0 #define DEBUG_KP_VIEW_RENDERER ((DEBUG_KP_VIEW && 1) || 0) #include "kpView.h" #include "kpViewPrivate.h" #include #include #include #include #include #include #include #include "kpLogCategories.h" #include "kpDefs.h" #include "document/kpDocument.h" #include "layers/selections/text/kpTextSelection.h" #include "tools/kpTool.h" #include "widgets/toolbars/kpToolToolBar.h" #include "views/manager/kpViewManager.h" #include "kpViewScrollableContainer.h" //--------------------------------------------------------------------- // public static const int kpView::MinZoomLevel = 1; const int kpView::MaxZoomLevel = 3200; //--------------------------------------------------------------------- kpView::kpView (kpDocument *document, kpToolToolBar *toolToolBar, kpViewManager *viewManager, kpView *buddyView, kpViewScrollableContainer *scrollableContainer, QWidget *parent) : QWidget (parent), d (new kpViewPrivate ()) { d->document = document; d->toolToolBar = toolToolBar; d->viewManager = viewManager; d->buddyView = buddyView; d->scrollableContainer = scrollableContainer; - d->hzoom = 100, d->vzoom = 100; + d->hzoom = 100; + d->vzoom = 100; d->origin = QPoint (0, 0); d->showGrid = false; d->isBuddyViewScrollableContainerRectangleShown = false; // Don't waste CPU drawing default background since its overridden by // our fully opaque drawing. In reality, this seems to make no // difference in performance. setAttribute(Qt::WA_OpaquePaintEvent, true); setFocusPolicy (Qt::WheelFocus); setMouseTracking (true); // mouseMoveEvent's even when no mousebtn down setAttribute (Qt::WA_KeyCompression, true); } //--------------------------------------------------------------------- kpView::~kpView () { setHasMouse (false); delete d; } //--------------------------------------------------------------------- // public kpDocument *kpView::document () const { return d->document; } //--------------------------------------------------------------------- // protected kpAbstractSelection *kpView::selection () const { return document () ? document ()->selection () : nullptr; } //--------------------------------------------------------------------- // protected kpTextSelection *kpView::textSelection () const { return document () ? document ()->textSelection () : nullptr; } //--------------------------------------------------------------------- // public kpToolToolBar *kpView::toolToolBar () const { return d->toolToolBar; } // protected kpTool *kpView::tool () const { return toolToolBar () ? toolToolBar ()->tool () : nullptr; } // public kpViewManager *kpView::viewManager () const { return d->viewManager; } // public kpView *kpView::buddyView () const { return d->buddyView; } // public kpViewScrollableContainer *kpView::buddyViewScrollableContainer () const { return (buddyView () ? buddyView ()->scrollableContainer () : nullptr); } // public kpViewScrollableContainer *kpView::scrollableContainer () const { return d->scrollableContainer; } // public int kpView::zoomLevelX (void) const { return d->hzoom; } // public int kpView::zoomLevelY (void) const { return d->vzoom; } // public virtual void kpView::setZoomLevel (int hzoom, int vzoom) { hzoom = qBound (MinZoomLevel, hzoom, MaxZoomLevel); vzoom = qBound (MinZoomLevel, vzoom, MaxZoomLevel); if (hzoom == d->hzoom && vzoom == d->vzoom) return; d->hzoom = hzoom; d->vzoom = vzoom; if (viewManager ()) viewManager ()->updateView (this); emit zoomLevelChanged (hzoom, vzoom); } // public QPoint kpView::origin () const { return d->origin; } // public virtual void kpView::setOrigin (const QPoint &origin) { #if DEBUG_KP_VIEW qCDebug(kpLogViews) << "kpView(" << objectName () << ")::setOrigin" << origin; #endif if (origin == d->origin) { #if DEBUG_KP_VIEW qCDebug(kpLogViews) << "\tNOP"; #endif return; } d->origin = origin; if (viewManager ()) viewManager ()->updateView (this); emit originChanged (origin); } // public bool kpView::canShowGrid () const { // (minimum zoom level < 400% would probably be reported as a bug by // users who thought that the grid was a part of the image!) return ((zoomLevelX () >= 400 && zoomLevelX () % 100 == 0) && (zoomLevelY () >= 400 && zoomLevelY () % 100 == 0)); } // public bool kpView::isGridShown () const { return d->showGrid; } // public void kpView::showGrid (bool yes) { if (d->showGrid == yes) return; if (yes && !canShowGrid ()) return; d->showGrid = yes; if (viewManager ()) viewManager ()->updateView (this); } // public bool kpView::isBuddyViewScrollableContainerRectangleShown () const { return d->isBuddyViewScrollableContainerRectangleShown; } // public void kpView::showBuddyViewScrollableContainerRectangle (bool yes) { if (yes == d->isBuddyViewScrollableContainerRectangleShown) return; d->isBuddyViewScrollableContainerRectangleShown = yes; if (d->isBuddyViewScrollableContainerRectangleShown) { // Got these connect statements by analysing deps of // updateBuddyViewScrollableContainerRectangle() rect update code. connect (this, SIGNAL (zoomLevelChanged(int,int)), this, SLOT (updateBuddyViewScrollableContainerRectangle())); connect (this, SIGNAL (originChanged(QPoint)), this, SLOT (updateBuddyViewScrollableContainerRectangle())); if (buddyViewScrollableContainer ()) { connect (buddyViewScrollableContainer (), SIGNAL (contentsMoved()), this, SLOT (updateBuddyViewScrollableContainerRectangle())); connect (buddyViewScrollableContainer (), SIGNAL (resized()), this, SLOT (updateBuddyViewScrollableContainerRectangle())); } if (buddyView ()) { connect (buddyView (), SIGNAL (zoomLevelChanged(int,int)), this, SLOT (updateBuddyViewScrollableContainerRectangle())); connect (buddyView (), SIGNAL (originChanged(QPoint)), this, SLOT (updateBuddyViewScrollableContainerRectangle())); connect (buddyView (), SIGNAL (sizeChanged(int,int)), this, SLOT (updateBuddyViewScrollableContainerRectangle())); } } else { disconnect (this, SIGNAL (zoomLevelChanged(int,int)), this, SLOT (updateBuddyViewScrollableContainerRectangle())); disconnect (this, SIGNAL (originChanged(QPoint)), this, SLOT (updateBuddyViewScrollableContainerRectangle())); if (buddyViewScrollableContainer ()) { disconnect (buddyViewScrollableContainer (), SIGNAL (contentsMoved()), this, SLOT (updateBuddyViewScrollableContainerRectangle())); disconnect (buddyViewScrollableContainer (), SIGNAL (resized()), this, SLOT (updateBuddyViewScrollableContainerRectangle())); } if (buddyView ()) { disconnect (buddyView (), SIGNAL (zoomLevelChanged(int,int)), this, SLOT (updateBuddyViewScrollableContainerRectangle())); disconnect (buddyView (), SIGNAL (originChanged(QPoint)), this, SLOT (updateBuddyViewScrollableContainerRectangle())); disconnect (buddyView (), SIGNAL (sizeChanged(int,int)), this, SLOT (updateBuddyViewScrollableContainerRectangle())); } } updateBuddyViewScrollableContainerRectangle (); } // protected QRect kpView::buddyViewScrollableContainerRectangle () const { return d->buddyViewScrollableContainerRectangle; } // protected slot void kpView::updateBuddyViewScrollableContainerRectangle () { if (viewManager ()) viewManager ()->setQueueUpdates (); { if (d->buddyViewScrollableContainerRectangle.isValid ()) { if (viewManager ()) { // Erase last viewManager ()->updateViewRectangleEdges (this, d->buddyViewScrollableContainerRectangle); } } QRect newRect; if (isBuddyViewScrollableContainerRectangleShown () && buddyViewScrollableContainer () && buddyView ()) { QRect docRect = buddyView ()->transformViewToDoc ( QRect (buddyViewScrollableContainer ()->horizontalScrollBar()->value(), buddyViewScrollableContainer ()->verticalScrollBar()->value(), qMin (buddyView ()->width (), buddyViewScrollableContainer ()->viewport()->width ()), qMin (buddyView ()->height (), buddyViewScrollableContainer ()->viewport()->height ()))); QRect viewRect = this->transformDocToView (docRect); // (Surround the area of interest by moving outwards by 1 pixel in each // direction - don't overlap area) newRect = QRect (viewRect.x () - 1, viewRect.y () - 1, viewRect.width () + 2, viewRect.height () + 2); } else { newRect = QRect (); } if (newRect != d->buddyViewScrollableContainerRectangle) { // (must set before updateView() for paintEvent() to see new // rect) d->buddyViewScrollableContainerRectangle = newRect; if (newRect.isValid ()) { if (viewManager ()) { viewManager ()->updateViewRectangleEdges (this, d->buddyViewScrollableContainerRectangle); } } } } if (viewManager ()) viewManager ()->restoreQueueUpdates (); } //--------------------------------------------------------------------- // public double kpView::transformViewToDocX (double viewX) const { return (viewX - origin ().x ()) * 100.0 / zoomLevelX (); } //--------------------------------------------------------------------- // public double kpView::transformViewToDocY (double viewY) const { return (viewY - origin ().y ()) * 100.0 / zoomLevelY (); } //--------------------------------------------------------------------- // public QPoint kpView::transformViewToDoc (const QPoint &viewPoint) const { return QPoint ((int) transformViewToDocX (viewPoint.x ()), (int) transformViewToDocY (viewPoint.y ())); } //--------------------------------------------------------------------- // public QRect kpView::transformViewToDoc (const QRect &viewRect) const { if (zoomLevelX () == 100 && zoomLevelY () == 100) { return QRect (viewRect.x () - origin ().x (), viewRect.y () - origin ().y (), viewRect.width (), viewRect.height ()); } else { const QPoint docTopLeft = transformViewToDoc (viewRect.topLeft ()); // (don't call transformViewToDoc[XY]() - need to round up dimensions) const int docWidth = qRound (double (viewRect.width ()) * 100.0 / double (zoomLevelX ())); const int docHeight = qRound (double (viewRect.height ()) * 100.0 / double (zoomLevelY ())); // (like QWMatrix::Areas) return QRect (docTopLeft.x (), docTopLeft.y (), docWidth, docHeight); } } //--------------------------------------------------------------------- // public double kpView::transformDocToViewX (double docX) const { return (docX * zoomLevelX () / 100.0) + origin ().x (); } // public double kpView::transformDocToViewY (double docY) const { return (docY * zoomLevelY () / 100.0) + origin ().y (); } // public QPoint kpView::transformDocToView (const QPoint &docPoint) const { return QPoint ((int) transformDocToViewX (docPoint.x ()), (int) transformDocToViewY (docPoint.y ())); } // public QRect kpView::transformDocToView (const QRect &docRect) const { if (zoomLevelX () == 100 && zoomLevelY () == 100) { return QRect (docRect.x () + origin ().x (), docRect.y () + origin ().y (), docRect.width (), docRect.height ()); } else { const QPoint viewTopLeft = transformDocToView (docRect.topLeft ()); // (don't call transformDocToView[XY]() - need to round up dimensions) const int viewWidth = qRound (double (docRect.width ()) * double (zoomLevelX ()) / 100.0); const int viewHeight = qRound (double (docRect.height ()) * double (zoomLevelY ()) / 100.0); // (like QWMatrix::Areas) return QRect (viewTopLeft.x (), viewTopLeft.y (), viewWidth, viewHeight); } } // public QPoint kpView::transformViewToOtherView (const QPoint &viewPoint, const kpView *otherView) { if (this == otherView) return viewPoint; const double docX = transformViewToDocX (viewPoint.x ()); const double docY = transformViewToDocY (viewPoint.y ()); const double otherViewX = otherView->transformDocToViewX (docX); const double otherViewY = otherView->transformDocToViewY (docY); return QPoint ((int) otherViewX, (int) otherViewY); } // public int kpView::zoomedDocWidth () const { return document () ? document ()->width () * zoomLevelX () / 100 : 0; } // public int kpView::zoomedDocHeight () const { return document () ? document ()->height () * zoomLevelY () / 100 : 0; } // public void kpView::setHasMouse (bool yes) { kpViewManager *vm = viewManager (); if (!vm) return; #if DEBUG_KP_VIEW && 0 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::setHasMouse(" << yes << ") existing viewUnderCursor=" << (vm->viewUnderCursor () ? vm->viewUnderCursor ()->objectName () : "(none)") << endl; #endif if (yes && vm->viewUnderCursor () != this) vm->setViewUnderCursor (this); else if (!yes && vm->viewUnderCursor () == this) vm->setViewUnderCursor (nullptr); } //--------------------------------------------------------------------- // public void kpView::addToQueuedArea (const QRegion ®ion) { #if DEBUG_KP_VIEW && 0 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::addToQueuedArea() already=" << d->queuedUpdateArea << " - plus - " << region << endl; #endif d->queuedUpdateArea += region; } //--------------------------------------------------------------------- // public void kpView::addToQueuedArea (const QRect &rect) { #if DEBUG_KP_VIEW && 0 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::addToQueuedArea() already=" << d->queuedUpdateArea << " - plus - " << rect << endl; #endif d->queuedUpdateArea += rect; } //--------------------------------------------------------------------- // public void kpView::invalidateQueuedArea () { #if DEBUG_KP_VIEW && 0 qCDebug(kpLogViews) << "kpView::invalidateQueuedArea()"; #endif d->queuedUpdateArea = QRegion (); } //--------------------------------------------------------------------- // public void kpView::updateQueuedArea () { kpViewManager *vm = viewManager (); #if DEBUG_KP_VIEW && 0 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::updateQueuedArea() vm=" << (bool) vm << " queueUpdates=" << (vm && vm->queueUpdates ()) << " fastUpdates=" << (vm && vm->fastUpdates ()) << " area=" << d->queuedUpdateArea << endl; #endif if (!vm) return; if (vm->queueUpdates ()) return; if (!d->queuedUpdateArea.isEmpty ()) vm->updateView (this, d->queuedUpdateArea); invalidateQueuedArea (); } //--------------------------------------------------------------------- // public QPoint kpView::mouseViewPoint (const QPoint &returnViewPoint) const { if (returnViewPoint != KP_INVALID_POINT) return returnViewPoint; else { // TODO: I don't think this is right for the main view since that's // inside the scrollview (which can scroll). return mapFromGlobal (QCursor::pos ()); } } //--------------------------------------------------------------------- // public virtual QVariant kpView::inputMethodQuery (Qt::InputMethodQuery query) const { #if DEBUG_KP_VIEW && 1 qCDebug(kpLogViews) << "kpView(" << objectName () << ")::inputMethodQuery()"; #endif QVariant ret; switch (query) { case Qt::ImMicroFocus: { QRect r = d->viewManager->textCursorRect (); r.setTopLeft (r.topLeft () + origin ()); r.setHeight (r.height() + 2); r = transformDocToView (r); ret = r; break; } case Qt::ImFont: { if (textSelection ()) { ret = textSelection ()->textStyle ().font (); } break; } default: break; } return ret; } //--------------------------------------------------------------------- diff --git a/views/kpZoomedThumbnailView.cpp b/views/kpZoomedThumbnailView.cpp index bc3475eb..8bb2f6ab 100644 --- a/views/kpZoomedThumbnailView.cpp +++ b/views/kpZoomedThumbnailView.cpp @@ -1,140 +1,141 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define DEBUG_KP_ZOOMED_THUMBNAIL_VIEW 0 #include "views/kpZoomedThumbnailView.h" #include "kpLogCategories.h" #include "document/kpDocument.h" #include "views/manager/kpViewManager.h" #include //-------------------------------------------------------------------------------- kpZoomedThumbnailView::kpZoomedThumbnailView (kpDocument *document, kpToolToolBar *toolToolBar, kpViewManager *viewManager, kpView *buddyView, kpViewScrollableContainer *scrollableContainer, QWidget *parent ) : kpThumbnailView (document, toolToolBar, viewManager, buddyView, scrollableContainer, parent) { // Call to virtual function - this is why the class is sealed adjustToEnvironment (); } kpZoomedThumbnailView::~kpZoomedThumbnailView () { } // public virtual [base kpThumbnailView] QString kpZoomedThumbnailView::caption () const { return i18n ("%1% - Thumbnail", zoomLevelX ()); } // public slot virtual [base kpView] void kpZoomedThumbnailView::adjustToEnvironment () { #if DEBUG_KP_ZOOMED_THUMBNAIL_VIEW qCDebug(kpLogViews) << "kpZoomedThumbnailView(" << name () << ")::adjustToEnvironment()" << " width=" << width () << " height=" << height () << endl; #endif if (!document ()) return; #if DEBUG_KP_ZOOMED_THUMBNAIL_VIEW qCDebug(kpLogViews) << "\tdoc: width=" << document ()->width () << " height=" << document ()->height () << endl; #endif if (document ()->width () <= 0 || document ()->height () <= 0) { qCCritical(kpLogViews) << "kpZoomedThumbnailView::adjustToEnvironment() doc:" << " width=" << document ()->width () << " height=" << document ()->height () << endl; return; } int hzoom = qMax (1, width () * 100 / document ()->width ()); int vzoom = qMax (1, height () * 100 / document ()->height ()); // keep aspect ratio if (hzoom < vzoom) vzoom = hzoom; else hzoom = vzoom; #if DEBUG_KP_ZOOMED_THUMBNAIL_VIEW && 1 qCDebug(kpLogViews) << "\tproposed zoom=" << hzoom; #endif if (hzoom > 100 || vzoom > 100) { #if DEBUG_KP_ZOOMED_THUMBNAIL_VIEW && 1 qCDebug(kpLogViews) << "\twon't magnify - setting zoom to 100%"; #endif - hzoom = 100, vzoom = 100; + hzoom = 100; + vzoom = 100; } if (viewManager ()) viewManager ()->setQueueUpdates (); { setZoomLevel (hzoom, vzoom); setOrigin (QPoint ((width () - zoomedDocWidth ()) / 2, (height () - zoomedDocHeight ()) / 2)); setMaskToCoverDocument (); if (viewManager ()) viewManager ()->updateView (this); } if (viewManager ()) viewManager ()->restoreQueueUpdates (); } diff --git a/widgets/toolbars/options/kpToolWidgetBase.cpp b/widgets/toolbars/options/kpToolWidgetBase.cpp index eb9b9093..d1ba0c86 100644 --- a/widgets/toolbars/options/kpToolWidgetBase.cpp +++ b/widgets/toolbars/options/kpToolWidgetBase.cpp @@ -1,721 +1,722 @@ /* Copyright (c) 2003-2007 Clarence Dang All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define DEBUG_KP_TOOL_WIDGET_BASE 0 #include "kpToolWidgetBase.h" #include "kpDefs.h" #include #include #include #include "kpLogCategories.h" #include #include #include #include #include #include #include #include //--------------------------------------------------------------------- kpToolWidgetBase::kpToolWidgetBase (QWidget *parent, const QString &name) : QFrame(parent), m_baseWidget(nullptr), m_selectedRow(-1), m_selectedCol(-1) { setObjectName (name); setFrameStyle (QFrame::Panel | QFrame::Sunken); setFixedSize (44, 66); setSizePolicy (QSizePolicy::Minimum, QSizePolicy::Minimum); } //--------------------------------------------------------------------- kpToolWidgetBase::~kpToolWidgetBase () { } //--------------------------------------------------------------------- // public void kpToolWidgetBase::addOption (const QPixmap &pixmap, const QString &toolTip) { if (m_pixmaps.isEmpty ()) startNewOptionRow (); m_pixmaps.last ().append (pixmap); m_pixmapRects.last ().append (QRect ()); m_toolTips.last ().append (toolTip); } //--------------------------------------------------------------------- // public void kpToolWidgetBase::startNewOptionRow () { m_pixmaps.append (QList ()); m_pixmapRects.append (QList ()); m_toolTips.append (QList ()); } //--------------------------------------------------------------------- // public void kpToolWidgetBase::finishConstruction (int fallBackRow, int fallBackCol) { #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "kpToolWidgetBase(" << objectName () << ")::kpToolWidgetBase(fallBack:row=" << fallBackRow << ",col=" << fallBackCol << ")" << endl; #endif relayoutOptions (); // HACK: Undo the maximum half of setFixedSize() in the ctor to avoid // bizarre redraw errors when tool widgets are hidden and others // are shown. // // The reason why we didn't just use setMinimumSize() in the ctor is // because all tool widgets construct pixmaps whose sizes are dependent // on the size() in the ctor, so we needed to get the correct size // in there. This is bad design because it means that tool widgets // can't really be resized. setMaximumSize (QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); const QPair rowColPair = defaultSelectedRowAndCol (); if (!setSelected (rowColPair.first, rowColPair.second, false/*don't save*/)) { if (!setSelected (fallBackRow, fallBackCol)) { if (!setSelected (0, 0)) { qCCritical(kpLogWidgets) << "kpToolWidgetBase::finishConstruction() " "can't even fall back to setSelected(row=0,col=0)" << endl; } } } } //--------------------------------------------------------------------- // private QList kpToolWidgetBase::spreadOutElements (const QList &sizes, int max) { if (sizes.count () == 0) return QList (); else if (sizes.count () == 1) { QList ret; ret.append (sizes.first () > max ? 0 : 1/*margin*/); return ret; } QList retOffsets; for (int i = 0; i < sizes.count (); i++) retOffsets.append (0); int totalSize = 0; for (int i = 0; i < (int) sizes.count (); i++) totalSize += sizes [i]; int margin = 1; // if don't fit with margin, then just return elements // packed right next to each other if (totalSize + margin * 2 > max) { retOffsets [0] = 0; for (int i = 1; i < (int) sizes.count (); i++) retOffsets [i] = retOffsets [i - 1] + sizes [i - 1]; return retOffsets; } int maxLeftOver = max - (totalSize + margin * 2 * sizes.count()); int startCompensating = -1; int numCompensate = 0; int spacing = 0; spacing = maxLeftOver / (sizes.count () - 1); if (spacing * int (sizes.count () - 1) < maxLeftOver) { numCompensate = maxLeftOver - spacing * (sizes.count () - 1); startCompensating = ((sizes.count () - 1) - numCompensate) / 2; } retOffsets [0] = margin; for (int i = 1; i < (int) sizes.count (); i++) { retOffsets [i] += retOffsets [i - 1] + sizes [i - 1] + spacing + ((numCompensate && i >= startCompensating && i < startCompensating + numCompensate) ? 1 : 0); } return retOffsets; } //--------------------------------------------------------------------- // public QPair kpToolWidgetBase::defaultSelectedRowAndCol () const { int row = -1, col = -1; if (!objectName ().isEmpty ()) { KConfigGroup cfg (KSharedConfig::openConfig (), kpSettingsGroupTools); row = cfg.readEntry (objectName () + QLatin1String (" Row"), -1); col = cfg.readEntry (objectName () + QLatin1String (" Col"), -1); } #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "kpToolWidgetBase(" << objectName () << ")::defaultSelectedRowAndCol() returning row=" << row << " col=" << col << endl; #endif return qMakePair (row, col); } //--------------------------------------------------------------------- // public int kpToolWidgetBase::defaultSelectedRow () const { return defaultSelectedRowAndCol ().first; } //--------------------------------------------------------------------- // public int kpToolWidgetBase::defaultSelectedCol () const { return defaultSelectedRowAndCol ().second; } //--------------------------------------------------------------------- // public void kpToolWidgetBase::saveSelectedAsDefault () const { #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "kpToolWidgetBase(" << objectName () << ")::saveSelectedAsDefault() row=" << m_selectedRow << " col=" << m_selectedCol << endl; #endif if (objectName ().isEmpty ()) return; KConfigGroup cfg (KSharedConfig::openConfig (), kpSettingsGroupTools); cfg.writeEntry (objectName () + QLatin1String (" Row"), m_selectedRow); cfg.writeEntry (objectName () + QLatin1String (" Col"), m_selectedCol); cfg.sync (); } //--------------------------------------------------------------------- // public void kpToolWidgetBase::relayoutOptions () { #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "kpToolWidgetBase::relayoutOptions() size=" << size (); #endif while (!m_pixmaps.isEmpty () && m_pixmaps.last ().count () == 0) { #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\tkilling #" << m_pixmaps.count () - 1; #endif m_pixmaps.removeLast (); m_pixmapRects.removeLast (); m_toolTips.removeLast (); } if (m_pixmaps.isEmpty ()) return; #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\tsurvived killing of empty rows"; qCDebug(kpLogWidgets) << "\tfinding heights of rows:"; #endif QList maxHeightOfRow; for (int r = 0; r < m_pixmaps.count (); r++) maxHeightOfRow.append (0); for (int r = 0; r < (int) m_pixmaps.count (); r++) { for (int c = 0; c < (int) m_pixmaps [r].count (); c++) { if (c == 0 || m_pixmaps [r][c].height () > maxHeightOfRow [r]) maxHeightOfRow [r] = m_pixmaps [r][c].height (); } #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\t\t" << r << ": " << maxHeightOfRow [r]; #endif } QList rowYOffset = spreadOutElements (maxHeightOfRow, height ()); #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\tspread out offsets of rows:"; for (int r = 0; r < (int) rowYOffset.count (); r++) qCDebug(kpLogWidgets) << "\t\t" << r << ": " << rowYOffset [r]; #endif for (int r = 0; r < (int) m_pixmaps.count (); r++) { #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\tlaying out row " << r << ":"; #endif QList widths; for (int c = 0; c < (int) m_pixmaps [r].count (); c++) widths.append (m_pixmaps [r][c].width ()); #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\t\twidths of cols:"; for (int c = 0; c < (int) m_pixmaps [r].count (); c++) qCDebug(kpLogWidgets) << "\t\t\t" << c << ": " << widths [c]; #endif QList colXOffset = spreadOutElements (widths, width ()); #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\t\tspread out offsets of cols:"; for (int c = 0; c < (int) colXOffset.count (); c++) qCDebug(kpLogWidgets) << "\t\t\t" << c << ": " << colXOffset [c]; #endif for (int c = 0; c < (int) colXOffset.count (); c++) { int x = colXOffset [c]; int y = rowYOffset [r]; int w, h; if (c == (int) colXOffset.count () - 1) { if (x + m_pixmaps [r][c].width () >= width ()) w = m_pixmaps [r][c].width (); else w = width () - 1 - x; } else w = colXOffset [c + 1] - x; if (r == (int) m_pixmaps.count () - 1) { if (y + m_pixmaps [r][c].height () >= height ()) h = m_pixmaps [r][c].height (); else h = height () - 1 - y; } else h = rowYOffset [r + 1] - y; m_pixmapRects [r][c] = QRect (x, y, w, h); } } update (); } //--------------------------------------------------------------------- // public int kpToolWidgetBase::selectedRow () const { return m_selectedRow; } //--------------------------------------------------------------------- // public int kpToolWidgetBase::selectedCol () const { return m_selectedCol; } //--------------------------------------------------------------------- // public int kpToolWidgetBase::selected () const { if (m_selectedRow < 0 || m_selectedRow >= (int) m_pixmaps.count () || m_selectedCol < 0) { return -1; } int upto = 0; for (int y = 0; y < m_selectedRow; y++) upto += m_pixmaps [y].count (); if (m_selectedCol >= (int) m_pixmaps [m_selectedRow].count ()) return -1; upto += m_selectedCol; return upto; } //--------------------------------------------------------------------- // public bool kpToolWidgetBase::hasPreviousOption (int *row, int *col) const { #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "kpToolWidgetBase(" << objectName () << ")::hasPreviousOption() current row=" << m_selectedRow << " col=" << m_selectedCol << endl; #endif if (row) *row = -1; if (col) *col = -1; if (m_selectedRow < 0 || m_selectedCol < 0) return false; int newRow = m_selectedRow, newCol = m_selectedCol; newCol--; if (newCol < 0) { newRow--; if (newRow < 0) return false; newCol = m_pixmaps [newRow].count () - 1; if (newCol < 0) return false; } if (row) *row = newRow; if (col) *col = newCol; return true; } //--------------------------------------------------------------------- // public bool kpToolWidgetBase::hasNextOption (int *row, int *col) const { #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "kpToolWidgetBase(" << objectName () << ")::hasNextOption() current row=" << m_selectedRow << " col=" << m_selectedCol << endl; #endif if (row) *row = -1; if (col) *col = -1; if (m_selectedRow < 0 || m_selectedCol < 0) return false; int newRow = m_selectedRow, newCol = m_selectedCol; newCol++; if (newCol >= (int) m_pixmaps [newRow].count ()) { newRow++; if (newRow >= (int) m_pixmaps.count ()) return false; newCol = 0; if (newCol >= (int) m_pixmaps [newRow].count ()) return false; } if (row) *row = newRow; if (col) *col = newCol; return true; } //--------------------------------------------------------------------- // public slot virtual bool kpToolWidgetBase::setSelected (int row, int col, bool saveAsDefault) { #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "kpToolWidgetBase::setSelected(row=" << row << ",col=" << col << ",saveAsDefault=" << saveAsDefault << ")" << endl; #endif if (row < 0 || col < 0 || row >= (int) m_pixmapRects.count () || col >= (int) m_pixmapRects [row].count ()) { #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\tout of range"; #endif return false; } if (row == m_selectedRow && col == m_selectedCol) { #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\tNOP"; #endif if (saveAsDefault) saveSelectedAsDefault (); return true; } const int wasSelectedRow = m_selectedRow; const int wasSelectedCol = m_selectedCol; - m_selectedRow = row, m_selectedCol = col; + m_selectedRow = row; + m_selectedCol = col; if (wasSelectedRow >= 0 && wasSelectedCol >= 0) { // unhighlight old option update (m_pixmapRects [wasSelectedRow][wasSelectedCol]); } // highlight new option update (m_pixmapRects [row][col]); #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\tOK"; #endif if (saveAsDefault) saveSelectedAsDefault (); emit optionSelected (row, col); return true; } //--------------------------------------------------------------------- // public slot bool kpToolWidgetBase::setSelected (int row, int col) { return setSelected (row, col, true/*set as default*/); } //--------------------------------------------------------------------- // public slot bool kpToolWidgetBase::selectPreviousOption () { int newRow, newCol; if (!hasPreviousOption (&newRow, &newCol)) return false; return setSelected (newRow, newCol); } //--------------------------------------------------------------------- // public slot bool kpToolWidgetBase::selectNextOption () { int newRow, newCol; if (!hasNextOption (&newRow, &newCol)) return false; return setSelected (newRow, newCol); } //--------------------------------------------------------------------- // protected virtual [base QWidget] bool kpToolWidgetBase::event (QEvent *e) { // TODO: It's unclear when we should call the base, call accept() and // return true or false. Look at other event() handlers. The // kpToolText one is wrong since after calling accept(), it calls // its base which calls ignore() :) if (e->type () == QEvent::ToolTip) { QHelpEvent *he = (QHelpEvent *) e; #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "kpToolWidgetBase::event() QHelpEvent pos=" << he->pos (); #endif bool showedText = false; for (int r = 0; r < (int) m_pixmapRects.count (); r++) { for (int c = 0; c < (int) m_pixmapRects [r].count (); c++) { if (m_pixmapRects [r][c].contains (he->pos ())) { const QString tip = m_toolTips [r][c]; #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\tin option: r=" << r << "c=" << c << "tip='" << tip << "'" << endl; #endif if (!tip.isEmpty ()) { QToolTip::showText (he->globalPos (), tip, this); showedText = true; } e->accept (); goto exit_loops; } } } exit_loops: if (!showedText) { #if DEBUG_KP_TOOL_WIDGET_BASE qCDebug(kpLogWidgets) << "\thiding text"; #endif QToolTip::hideText (); } return true; } else return QWidget::event (e); } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpToolWidgetBase::mousePressEvent (QMouseEvent *e) { e->ignore (); if (e->button () != Qt::LeftButton) return; for (int i = 0; i < (int) m_pixmapRects.count (); i++) { for (int j = 0; j < (int) m_pixmapRects [i].count (); j++) { if (m_pixmapRects [i][j].contains (e->pos ())) { setSelected (i, j); e->accept (); return; } } } } //--------------------------------------------------------------------- // protected virtual [base QWidget] void kpToolWidgetBase::paintEvent (QPaintEvent *e) { #if DEBUG_KP_TOOL_WIDGET_BASE && 1 qCDebug(kpLogWidgets) << "kpToolWidgetBase::paintEvent(): rect=" << contentsRect (); #endif // Draw frame first. QFrame::paintEvent (e); QPainter painter (this); for (int i = 0; i < (int) m_pixmaps.count (); i++) { #if DEBUG_KP_TOOL_WIDGET_BASE && 1 qCDebug(kpLogWidgets) << "\tRow: " << i; #endif for (int j = 0; j < (int) m_pixmaps [i].count (); j++) { QRect rect = m_pixmapRects [i][j]; QPixmap pixmap = m_pixmaps [i][j]; #if DEBUG_KP_TOOL_WIDGET_BASE && 1 qCDebug(kpLogWidgets) << "\t\tCol: " << j << " rect=" << rect; #endif if (i == m_selectedRow && j == m_selectedCol) { painter.fillRect(rect, palette().color(QPalette::Highlight).rgb()); } #if DEBUG_KP_TOOL_WIDGET_BASE && 1 qCDebug(kpLogWidgets) << "\t\t\tdraw pixmap @ x=" << rect.x () + (rect.width () - pixmap.width ()) / 2 << " y=" << rect.y () + (rect.height () - pixmap.height ()) / 2 << endl; #endif painter.drawPixmap(QPoint(rect.x () + (rect.width () - pixmap.width ()) / 2, rect.y () + (rect.height () - pixmap.height ()) / 2), pixmap); } } } //---------------------------------------------------------------------