File indexing completed on 2024-05-12 04:21:14

0001 /*
0002    Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
0003    All rights reserved.
0004 
0005    Redistribution and use in source and binary forms, with or without
0006    modification, are permitted provided that the following conditions
0007    are met:
0008 
0009    1. Redistributions of source code must retain the above copyright
0010       notice, this list of conditions and the following disclaimer.
0011    2. Redistributions in binary form must reproduce the above copyright
0012       notice, this list of conditions and the following disclaimer in the
0013       documentation and/or other materials provided with the distribution.
0014 
0015    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0016    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0017    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
0018    IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0019    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
0020    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0021    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0022    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0023    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0024    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0025 */
0026 
0027 
0028 #define DEBUG_KP_DOCUMENT_META_INFO_DIALOG 0
0029 
0030 
0031 #include "kpDocumentMetaInfoDialog.h"
0032 
0033 #include "kpDefs.h"
0034 #include "imagelib/kpDocumentMetaInfo.h"
0035 
0036 #include <KLocalizedString>
0037 #include <KMessageBox>
0038 #include "kpLogCategories.h"
0039 
0040 #include <QAbstractItemView>
0041 #include <QDialogButtonBox>
0042 #include <QLabel>
0043 #include <QGroupBox>
0044 #include <QGridLayout>
0045 #include <QHBoxLayout>
0046 #include <QPushButton>
0047 #include <QTableWidget>
0048 #include <QSpinBox>
0049 #include <QDoubleSpinBox>
0050 
0051 
0052 struct kpDocumentMetaInfoDialogPrivate
0053 {
0054     const kpDocumentMetaInfo *originalMetaInfoPtr;
0055 
0056     QDoubleSpinBox *horizDpiInput, *vertDpiInput;
0057     QSpinBox *horizOffsetInput, *vertOffsetInput;
0058 
0059     QTableWidget *fieldsTableWidget;
0060     QPushButton *fieldsAddRowButton, *fieldsDeleteRowButton, *fieldsResetButton;
0061 };
0062 
0063 
0064 // (shared by all dialogs, across all main windows, in a KolourPaint instance)
0065 static int LastWidth = -1, LastHeight = -1;
0066 
0067 
0068 // sync: You must keep DpiMinStep = 10 ^ (-DpiPrecision).
0069 //
0070 // You can increase the precision to reduce the chance of inadvertently changing
0071 // the resolution when converting from kpDocumentMetaInfo's "dots per meter"
0072 // to our "dots per inch" and back.  It would be bad if simply going into this
0073 // dialog and pressing OK changed the resolution (it's unlikely but I still think
0074 // it might happen with the current precision).
0075 // TODO: On a related note, for many particular resolutions, if the user enters
0076 //       one of them into the UI, presses OK and then comes back to the dialog,
0077 //       s/he is presented with a different resolution to the one typed in.
0078 //       Maybe make DotsPerMeter[XY] be of type "double" instead of "int" to
0079 //       solve this problem?
0080 //
0081 // Of course, if you increase the precision too much, the minimum step will
0082 // become so small that it will start experiencing floating point inaccuracies
0083 // esp. since we use it for the "DpiUnspecified" hack.
0084 static const int DpiPrecision = 3;
0085 static const double DpiMinStep = 0.001;
0086 
0087 static const double DpiLegalMin =
0088     kpDocumentMetaInfo::MinDotsPerMeter / KP_INCHES_PER_METER;
0089 
0090 // Out of range represents unspecified DPI.
0091 static const double DpiUnspecified = ::DpiLegalMin - ::DpiMinStep;
0092 
0093 static const double DpiInputMin = ::DpiUnspecified;
0094 static const double DpiInputMax =
0095     kpDocumentMetaInfo::MaxDotsPerMeter / KP_INCHES_PER_METER;
0096 
0097 // The increment the DPI spinboxes jump by when they're clicked.
0098 //
0099 // We make this relatively big since people don't usually just increase their
0100 // DPIs by 1 or so -- they are usually changing from say, 72, to 96.
0101 //
0102 // Obviously, making it equal to DpiMinStep is too slow a UI.  Therefore, with
0103 // our big setting, the user will still have to manually change the value in
0104 // the spinbox, using the keyboard, after all their clicking to ensure it is
0105 // exactly the value they want.
0106 static const double DpiInputStep = 10;
0107 
0108 
0109 // TODO: Halve groupbox layout margins in every other file since it doesn't
0110 //       seem to be need in Qt4.
0111 kpDocumentMetaInfoDialog::kpDocumentMetaInfoDialog (
0112         const kpDocumentMetaInfo *docMetaInfo,
0113         QWidget *parent)
0114 
0115     : QDialog (parent),
0116       d (new kpDocumentMetaInfoDialogPrivate ())
0117 {
0118     d->originalMetaInfoPtr = docMetaInfo;
0119 
0120 
0121     setWindowTitle (i18nc ("@title:window", "Document Properties"));
0122     auto * buttons = new QDialogButtonBox (QDialogButtonBox::Ok |
0123                                                        QDialogButtonBox::Cancel, this);
0124 
0125     connect (buttons, &QDialogButtonBox::accepted, this, &kpDocumentMetaInfoDialog::accept);
0126     connect (buttons, &QDialogButtonBox::rejected, this, &kpDocumentMetaInfoDialog::reject);
0127 
0128     auto *baseWidget = new QWidget (this);
0129 
0130     auto *dialogLayout = new QVBoxLayout (this);
0131     dialogLayout->addWidget (baseWidget);
0132     dialogLayout->addWidget (buttons);
0133 
0134 
0135     //
0136     // DPI Group Box
0137     //
0138 
0139     Q_ASSERT (::DpiInputMin < ::DpiInputMax);
0140 
0141     auto *dpiGroupBox = new QGroupBox(i18n("Dots &Per Inch (DPI)"), baseWidget);
0142 
0143     d->horizDpiInput = new QDoubleSpinBox(dpiGroupBox);
0144     d->horizDpiInput->setRange(::DpiInputMin, ::DpiInputMax);
0145     d->horizDpiInput->setValue(0.0);
0146     d->horizDpiInput->setSingleStep(::DpiInputStep);
0147     d->horizDpiInput->setDecimals(::DpiPrecision);
0148     d->horizDpiInput->setSpecialValueText(i18n("Unspecified"));
0149 
0150     auto *dpiXLabel = new QLabel (
0151         i18nc ("Horizontal DPI 'x' Vertical DPI", " x "), dpiGroupBox);
0152     dpiXLabel->setAlignment (Qt::AlignCenter);
0153 
0154     d->vertDpiInput = new QDoubleSpinBox(dpiGroupBox);
0155     d->vertDpiInput->setRange(::DpiInputMin, ::DpiInputMax);
0156     d->vertDpiInput->setValue(0.0);
0157     d->vertDpiInput->setSingleStep(::DpiInputStep);
0158     d->vertDpiInput->setDecimals(::DpiPrecision);
0159     d->vertDpiInput->setSpecialValueText(i18n("Unspecified"));
0160 
0161 
0162     auto *dpiLay = new QGridLayout(dpiGroupBox);
0163 
0164     dpiLay->addWidget(new QLabel(i18n("Horizontal:")), 0, 0, Qt::AlignHCenter);
0165     dpiLay->addWidget(d->horizDpiInput, 1, 0);
0166     dpiLay->addWidget(dpiXLabel, 0, 1);
0167     dpiLay->addWidget(new QLabel(i18n("Vertical:")), 0, 2, Qt::AlignHCenter);
0168     dpiLay->addWidget(d->vertDpiInput, 1, 2);
0169 
0170     dpiLay->setRowStretch(2, 1);
0171 
0172 
0173     dpiGroupBox->setWhatsThis (
0174         i18n (
0175             "<qt>"
0176             "<p><b>Dots Per Inch</b> (DPI) specifies the number of pixels"
0177             " of the image that should be printed inside one inch (2.54cm).</p>"
0178 
0179             "<p>The higher the image's DPI, the smaller the printed image."
0180             " Note that your printer is unlikely to produce high"
0181             " quality prints if you increase this to more than 300 or 600 DPI,"
0182             " depending on the printer.</p>"
0183 
0184             "<p>If you would like to print the image so that it is the same"
0185             " size as it is displayed on the screen, set the image's DPI"
0186             " values to be the same as the screen's.</p>"
0187 
0188             // TODO: This is currently not true!
0189             //       See "96dpi" TODO in kpMainWindow::sendPixmapToPrinter().
0190             //       This also why we don't try to report the current screen DPI
0191             //       for the above paragraph.
0192             "<p>If either DPI value is <b>Unspecified</b>, the image will also"
0193             " be printed to be the same size as on the screen.</p>"
0194 
0195             "<p>Not all image formats support DPI values. If the format you"
0196             " save in does not support them, they will not be saved.</p>"
0197             "</qt>"
0198         ));
0199 
0200 
0201     //
0202     // Offset Group Box
0203     //
0204 
0205     auto *offsetGroupBox = new QGroupBox(i18n ("O&ffset"), baseWidget);
0206 
0207     d->horizOffsetInput = new QSpinBox;
0208     d->horizOffsetInput->setRange(kpDocumentMetaInfo::MinOffset, kpDocumentMetaInfo::MaxOffset);
0209 
0210     d->vertOffsetInput = new QSpinBox;
0211     d->vertOffsetInput->setRange(kpDocumentMetaInfo::MinOffset, kpDocumentMetaInfo::MaxOffset);
0212 
0213     auto *offsetLay = new QGridLayout(offsetGroupBox);
0214 
0215     offsetLay->addWidget(new QLabel(i18n("Horizontal:")), 0, 0, Qt::AlignHCenter);
0216     offsetLay->addWidget(d->horizOffsetInput, 1, 0);
0217     offsetLay->addWidget(new QLabel(i18n("Vertical:")), 0, 1, Qt::AlignHCenter);
0218     offsetLay->addWidget(d->vertOffsetInput, 1, 1);
0219 
0220     offsetLay->setRowStretch (2, 1);
0221 
0222 
0223     offsetGroupBox->setWhatsThis (
0224         i18n (
0225             "<qt>"
0226             "<p>The <b>Offset</b> is the relative position where this image"
0227             " should be placed, compared to other images.</p>"
0228 
0229             "<p>Not all image formats support the <b>Offset</b> feature."
0230             " If the format you save in does not support it, the values"
0231             " specified here will not be saved.</p>"
0232             "</qt>"
0233         ));
0234 
0235 
0236     //
0237     // Fields Group Box
0238     //
0239 
0240 
0241     auto *fieldsGroupBox = new QGroupBox (i18n ("&Text Fields"),
0242         baseWidget);
0243 
0244     d->fieldsTableWidget = new QTableWidget (fieldsGroupBox);
0245     d->fieldsTableWidget->setEditTriggers(QAbstractItemView::AllEditTriggers);
0246 
0247     connect (d->fieldsTableWidget, &QTableWidget::currentCellChanged,
0248              this, &kpDocumentMetaInfoDialog::slotFieldsCurrentCellChanged);
0249 
0250     connect (d->fieldsTableWidget, &QTableWidget::itemChanged,
0251              this, &kpDocumentMetaInfoDialog::slotFieldsItemChanged);
0252 
0253     d->fieldsAddRowButton = new QPushButton (i18n ("&Add Row"),
0254         fieldsGroupBox);
0255     connect (d->fieldsAddRowButton, &QPushButton::clicked,
0256              this, &kpDocumentMetaInfoDialog::slotFieldsAddRowButtonClicked);
0257 
0258     d->fieldsDeleteRowButton = new QPushButton (i18n ("&Delete Row"),
0259         fieldsGroupBox);
0260     connect (d->fieldsDeleteRowButton, &QPushButton::clicked,
0261              this, &kpDocumentMetaInfoDialog::slotFieldsDeleteRowButtonClicked);
0262 
0263     d->fieldsResetButton = new QPushButton (i18n ("&Reset"),
0264         fieldsGroupBox);
0265     connect (d->fieldsResetButton, &QPushButton::clicked,
0266              this, &kpDocumentMetaInfoDialog::setUIToOriginalMetaInfo);
0267 
0268     auto *fieldsButtonsLayout = new QHBoxLayout ();
0269     fieldsButtonsLayout->addWidget (d->fieldsAddRowButton);
0270     fieldsButtonsLayout->addWidget (d->fieldsDeleteRowButton);
0271     fieldsButtonsLayout->addStretch ();
0272     fieldsButtonsLayout->addWidget (d->fieldsResetButton);
0273 
0274     auto *fieldsLayout = new QVBoxLayout (fieldsGroupBox);
0275 
0276     fieldsLayout->addWidget (d->fieldsTableWidget);
0277     fieldsLayout->addLayout (fieldsButtonsLayout);
0278 
0279 
0280     fieldsGroupBox->setWhatsThis (
0281         i18n (
0282             "<qt>"
0283             "<p><b>Text Fields</b> provide extra information about the image."
0284             " This is probably a comment area that you can freely write any text in.</p>"
0285 
0286             "<p>However, this is format-specific so the fields could theoretically be"
0287             " computer-interpreted data - that you should not modify -"
0288             " but this is unlikely.</p>"
0289 
0290             "<p>Not all image formats support <b>Text Fields</b>. If the format"
0291             " you save in does not support them, they will not be saved.</p>"
0292             "</qt>"
0293         ));
0294 
0295 
0296     //
0297     // Global Layout
0298     //
0299     auto *baseLayout = new QGridLayout (baseWidget);
0300     baseLayout->setContentsMargins(0, 0, 0, 0);
0301 
0302     // Col 0
0303     baseLayout->addWidget (dpiGroupBox, 0, 0);
0304     baseLayout->addWidget (offsetGroupBox, 1, 0);
0305 
0306     // Col 1
0307     baseLayout->addWidget (fieldsGroupBox, 0, 1, 2/*row span*/, 1/*col span*/);
0308     baseLayout->setColumnStretch (1, 1/*stretch*/);
0309 
0310 
0311     //
0312     // Remaining UI Setup
0313     //
0314 
0315 
0316     setUIToOriginalMetaInfo ();
0317 
0318 
0319     if (::LastWidth > 0 && ::LastHeight > 0) {
0320         resize (::LastWidth, ::LastHeight);
0321     }
0322 }
0323 
0324 //---------------------------------------------------------------------
0325 
0326 kpDocumentMetaInfoDialog::~kpDocumentMetaInfoDialog ()
0327 {
0328     ::LastWidth = width ();
0329     ::LastHeight = height ();
0330 
0331     delete d;
0332 }
0333 
0334 //---------------------------------------------------------------------
0335 // private
0336 
0337 void kpDocumentMetaInfoDialog::editCell (int r, int c)
0338 {
0339     d->fieldsTableWidget->setCurrentCell (r, c);
0340     d->fieldsTableWidget->editItem (d->fieldsTableWidget->item (r, c));
0341 }
0342 
0343 //---------------------------------------------------------------------
0344 // private slot
0345 
0346 void kpDocumentMetaInfoDialog::setUIToOriginalMetaInfo ()
0347 {
0348     // Set DPI spinboxes.
0349     d->horizDpiInput->setValue (d->originalMetaInfoPtr->dotsPerMeterX () /
0350         KP_INCHES_PER_METER);
0351     d->vertDpiInput->setValue (d->originalMetaInfoPtr->dotsPerMeterY () /
0352         KP_INCHES_PER_METER);
0353 
0354 
0355     // Set Offset spinboxes.
0356     d->horizOffsetInput->setValue (d->originalMetaInfoPtr->offset ().x ());
0357     d->vertOffsetInput->setValue (d->originalMetaInfoPtr->offset ().y ());
0358 
0359 
0360     // Set Text Fields.
0361     //
0362     // Block itemChanged() signal as slotFieldsItemChanged() should not get called
0363     // when rows are half-created.
0364     const bool b = d->fieldsTableWidget->blockSignals (true);
0365     {
0366         d->fieldsTableWidget->clear ();
0367 
0368         d->fieldsTableWidget->setRowCount (d->originalMetaInfoPtr->textKeys ().size ());
0369         d->fieldsTableWidget->setColumnCount (2);
0370 
0371         QStringList fieldsHeader;
0372         fieldsHeader << i18n ("Key") << i18n ("Value");
0373         d->fieldsTableWidget->setHorizontalHeaderLabels (fieldsHeader);
0374 
0375         int row = 0;
0376         for (const auto &key : d->originalMetaInfoPtr->textKeys ())
0377         {
0378             d->fieldsTableWidget->setItem (row, 0/*1st col*/,
0379                 new QTableWidgetItem (key));
0380             d->fieldsTableWidget->setItem (row, 1/*2nd col*/,
0381                 new QTableWidgetItem (d->originalMetaInfoPtr->text (key)));
0382 
0383             row++;
0384         }
0385 
0386         fieldsAppendEmptyRow ();
0387     }
0388     d->fieldsTableWidget->blockSignals (b);
0389 
0390 
0391     editCell (0/*row*/, 0/*col*/);
0392 
0393 
0394     enableFieldsDeleteRowButtonIfShould ();
0395 }
0396 
0397 //---------------------------------------------------------------------
0398 // public
0399 
0400 bool kpDocumentMetaInfoDialog::isNoOp () const
0401 {
0402     return (metaInfo () == *d->originalMetaInfoPtr);
0403 }
0404 
0405 //---------------------------------------------------------------------
0406 
0407 // public
0408 kpDocumentMetaInfo kpDocumentMetaInfoDialog::originalMetaInfo () const
0409 {
0410     return *d->originalMetaInfoPtr;
0411 }
0412 
0413 // public
0414 kpDocumentMetaInfo kpDocumentMetaInfoDialog::metaInfo (
0415         QString *errorMessage) const
0416 {
0417     if (errorMessage)
0418     {
0419         // No errors to start with.
0420         *errorMessage = QString ();
0421     }
0422 
0423 
0424     kpDocumentMetaInfo ret;
0425 
0426 
0427     if (d->horizDpiInput->value () < ::DpiLegalMin) {
0428         ret.setDotsPerMeterX (0/*unspecified*/);
0429     }
0430     else {
0431         ret.setDotsPerMeterX (qRound (d->horizDpiInput->value () * KP_INCHES_PER_METER));
0432     }
0433 
0434     if (d->vertDpiInput->value () < ::DpiLegalMin) {
0435         ret.setDotsPerMeterY (0/*unspecified*/);
0436     }
0437     else {
0438         ret.setDotsPerMeterY (qRound (d->vertDpiInput->value () * KP_INCHES_PER_METER));
0439     }
0440 
0441 
0442     ret.setOffset (QPoint (d->horizOffsetInput->value (),
0443                            d->vertOffsetInput->value ()));
0444 
0445 
0446     for (int r = 0; r < d->fieldsTableWidget->rowCount (); r++)
0447     {
0448         const QString key = d->fieldsTableWidget->item (r, 0)->text ();
0449         const QString value = d->fieldsTableWidget->item (r, 1)->text ();
0450 
0451         // Empty key?
0452         if (key.isEmpty ())
0453         {
0454             // Empty value too?
0455             if (value.isEmpty ())
0456             {
0457                 // Ignore empty row.
0458                 continue;
0459             }
0460             // Value without a key?
0461 
0462 
0463             if (errorMessage)
0464             {
0465                 *errorMessage =
0466                         ki18n ("The text value \"%1\" on line %2 requires a key.")
0467                         .subs (value).subs (r + 1/*count from 1*/).toString ();
0468 
0469                 // Print only 1 error message per method invocation.
0470                 errorMessage = nullptr;
0471             }
0472 
0473             // Ignore.
0474             continue;
0475 
0476         }
0477 
0478         // Duplicate key?
0479         if (ret.textKeys ().contains (key))
0480         {
0481             if (errorMessage)
0482             {
0483                 int q;
0484                 for (q = 0; q < r; q++)
0485                 {
0486                     if (d->fieldsTableWidget->item (q, 0)->text () == key) {
0487                         break;
0488                     }
0489                 }
0490                 Q_ASSERT (q != r);
0491 
0492                 *errorMessage =
0493                     ki18n ("All text keys must be unique. The text key \"%1\""
0494                            " on lines %2 and %3 are identical.")
0495                     .subs (key)
0496                     .subs (q + 1/*count from 1*/)
0497                     .subs (r + 1/*count from 1*/)
0498                     .toString ();
0499 
0500                 // Print only 1 error message per method invocation.
0501                 errorMessage = nullptr;
0502             }
0503 
0504             // Ignore this duplicate - keep the first value of the key.
0505             continue;
0506         }
0507 
0508         ret.setText (key, value);
0509 
0510     }  // for (r = 0; r < table widget rows; r++) {
0511 
0512 
0513     return ret;
0514 }
0515 
0516 
0517 // private
0518 void kpDocumentMetaInfoDialog::fieldsUpdateVerticalHeader ()
0519 {
0520     QStringList vertLabels;
0521     for (int r = 1; r <= d->fieldsTableWidget->rowCount (); r++) {
0522         vertLabels << QString::number (r);
0523     }
0524 
0525     d->fieldsTableWidget->setVerticalHeaderLabels (vertLabels);
0526 }
0527 
0528 
0529 // private
0530 void kpDocumentMetaInfoDialog::fieldsAddEmptyRow (int atRow)
0531 {
0532     // Block itemChanged() signal as slotFieldsItemChanged() should not get called
0533     // when rows are half-created.
0534     const bool b = d->fieldsTableWidget->blockSignals (true);
0535     {
0536         d->fieldsTableWidget->insertRow (atRow);
0537 
0538         d->fieldsTableWidget->setItem (atRow, 0, new QTableWidgetItem (QString ()));
0539         d->fieldsTableWidget->setItem (atRow, 1, new QTableWidgetItem (QString ()));
0540     }
0541     d->fieldsTableWidget->blockSignals (b);
0542 
0543     // Hack around Qt's failure to redraw these sometimes.
0544     fieldsUpdateVerticalHeader ();
0545 
0546     enableFieldsDeleteRowButtonIfShould ();
0547 }
0548 
0549 // private
0550 void kpDocumentMetaInfoDialog::fieldsAppendEmptyRow ()
0551 {
0552     fieldsAddEmptyRow (d->fieldsTableWidget->rowCount ());
0553 }
0554 
0555 
0556 // private
0557 bool kpDocumentMetaInfoDialog::isFieldsRowDeleteable (int row) const
0558 {
0559     // Can't delete no row and can't delete last (always blank) row, which
0560     // is used to make it easy for the user to add rows without pressing
0561     // the "Add" button explicitly.
0562     return (row >= 0 && row < d->fieldsTableWidget->rowCount () - 1);
0563 }
0564 
0565 // private
0566 void kpDocumentMetaInfoDialog::fieldsDeleteRow (int r)
0567 {
0568 #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG
0569     qCDebug(kpLogDialogs) << "kpDocumentMetaInfoDialog::fieldsDeleteRow("
0570               << "row=" << r << ")"
0571               << " currentRow=" << d->fieldsTableWidget->currentRow ();
0572 #endif
0573 
0574     Q_ASSERT (isFieldsRowDeleteable (r));
0575 
0576     if (r == d->fieldsTableWidget->currentRow ())
0577     {
0578         // Assertion follows from previous assertion.
0579         const int newRow = r + 1;
0580     #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG
0581         qCDebug(kpLogDialogs) << "\tnewRow=" << newRow;
0582     #endif
0583         Q_ASSERT (newRow < d->fieldsTableWidget->rowCount ());
0584 
0585         int newCol = d->fieldsTableWidget->currentColumn ();
0586     #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG
0587         qCDebug(kpLogDialogs) << "\tnewCol=" << newCol;
0588     #endif
0589         if (newCol != 0 && newCol != 1)
0590         {
0591             newCol = 0;
0592         #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG
0593             qCDebug(kpLogDialogs) << "\t\tcorrecting to " << newCol;
0594         #endif
0595         }
0596 
0597         // WARNING: You must call this _before_ deleting the row.  Else, you'll
0598         //          trigger a Qt bug where if the editor is active on the row to
0599         //          be deleted, it might crash.  To reproduce, move this line to
0600         //          after removeRow() (and subtract 1 from newRow) and
0601         //          press the "Delete Row" button several times in succession
0602         //          very quickly.
0603         //
0604         // TODO: This usually results in a redraw error if the scrollbar scrolls
0605         //       after deleting the 2nd last row.  Qt bug.
0606         editCell (newRow, newCol);
0607     }
0608 
0609 
0610     d->fieldsTableWidget->removeRow (r);
0611 
0612 
0613     fieldsUpdateVerticalHeader ();
0614 
0615     enableFieldsDeleteRowButtonIfShould ();
0616 }
0617 
0618 
0619 // private
0620 void kpDocumentMetaInfoDialog::enableFieldsDeleteRowButtonIfShould ()
0621 {
0622 #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG
0623     qCDebug(kpLogDialogs) << "kpDocumentMetaInfoDialog::enableFieldsDeleteRowButtonIfShould()";
0624 #endif
0625 
0626     const int r = d->fieldsTableWidget->currentRow ();
0627 #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG
0628     qCDebug(kpLogDialogs) << "\tr=" << r;
0629 #endif
0630 
0631     d->fieldsDeleteRowButton->setEnabled (isFieldsRowDeleteable (r));
0632 }
0633 
0634 
0635 // private slot
0636 void kpDocumentMetaInfoDialog::slotFieldsCurrentCellChanged (int row, int col,
0637         int oldRow, int oldCol)
0638 {
0639 #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG
0640     qCDebug(kpLogDialogs) << "kpDocumentMetaInfoDialog::slotFieldsCurrentCellChanged("
0641               << "row=" << row << ",col=" << col
0642               << ",oldRow=" << oldRow << ",oldCol=" << oldCol
0643               << ")" << endl;
0644 #endif
0645 
0646     (void) row;
0647     (void) col;
0648     (void) oldRow;
0649     (void) oldCol;
0650 
0651     enableFieldsDeleteRowButtonIfShould ();
0652 }
0653 
0654 //---------------------------------------------------------------------
0655 // private slot
0656 
0657 void kpDocumentMetaInfoDialog::slotFieldsItemChanged (QTableWidgetItem *it)
0658 {
0659 #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG
0660     qCDebug(kpLogDialogs) << "kpDocumentMetaInfoDialog::slotFieldsItemChanged("
0661               << "item=" << it << ") rows=" << d->fieldsTableWidget->rowCount ();
0662 #endif
0663 
0664     const int r = d->fieldsTableWidget->row (it);
0665 #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG
0666     qCDebug(kpLogDialogs) << "\tr=" << r;
0667 #endif
0668     Q_ASSERT (r >= 0 && r < d->fieldsTableWidget->rowCount ());
0669 
0670     const QString key = d->fieldsTableWidget->item (r, 0)->text ();
0671 #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG
0672     qCDebug(kpLogDialogs) << " key='" << key << "'";
0673 #endif
0674 
0675     const QString value = d->fieldsTableWidget->item (r, 1)->text ();
0676 #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG
0677     qCDebug(kpLogDialogs) << " value='" << value << "'";
0678 #endif
0679 
0680     // At the last row?
0681     if (r == d->fieldsTableWidget->rowCount () - 1)
0682     {
0683         // Typed some text?
0684         if (!key.isEmpty () || !value.isEmpty ())
0685         {
0686             // LOTODO: If we're called due to the cell's text being finalized
0687             //         as a result of the user pressing the "Add Row" button,
0688             //         should this really append a row since
0689             //         slotFieldsAddRowButtonClicked() button is going to add
0690             //         another one?  That's two rows when the user only clicked
0691             //         that button once!
0692             fieldsAppendEmptyRow ();
0693         }
0694     }
0695 }
0696 
0697 //---------------------------------------------------------------------
0698 // private slot
0699 
0700 void kpDocumentMetaInfoDialog::slotFieldsAddRowButtonClicked ()
0701 {
0702 #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG
0703     qCDebug(kpLogDialogs) << "kpDocumentMetaInfoDialog::slotFieldsAddRowButtonClicked()";
0704 #endif
0705 
0706     const int r = d->fieldsTableWidget->currentRow ();
0707 #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG
0708     qCDebug(kpLogDialogs) << "\tr=" << r;
0709 #endif
0710 
0711     // (if no row is selected, r = -1)
0712     fieldsAddEmptyRow (r + 1);
0713 
0714     // Edit the key of this new row (column 0).
0715     // No one edits the value first (column 1).
0716     editCell ((r + 1)/*row*/, 0/*col*/);
0717 }
0718 
0719 //---------------------------------------------------------------------
0720 
0721 // private slot
0722 void kpDocumentMetaInfoDialog::slotFieldsDeleteRowButtonClicked ()
0723 {
0724 #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG
0725     qCDebug(kpLogDialogs) << "kpDocumentMetaInfoDialog::slotFieldsDeleteRowButtonClicked()";
0726 #endif
0727 
0728     const int r = d->fieldsTableWidget->currentRow ();
0729 #if DEBUG_KP_DOCUMENT_META_INFO_DIALOG
0730     qCDebug(kpLogDialogs) << "\tr=" << r;
0731 #endif
0732 
0733     Q_ASSERT (isFieldsRowDeleteable (r));
0734     fieldsDeleteRow (r);
0735 }
0736 
0737 
0738 // private slot virtual [base QDialog]
0739 void kpDocumentMetaInfoDialog::accept ()
0740 {
0741     // Validate text fields.
0742     QString errorMessage;
0743     (void) metaInfo (&errorMessage);
0744     if (!errorMessage.isEmpty ())
0745     {
0746         KMessageBox::error (this, errorMessage, i18nc ("@title:window", "Invalid Text Fields"));
0747         return;
0748     }
0749 
0750     QDialog::accept ();
0751 }
0752 
0753 #include "moc_kpDocumentMetaInfoDialog.cpp"