1 /***************************************************************************
2                  kraftview.cpp  - Interactive document view
3                              -------------------
4     begin                : Mit Dez 31 19:24:05 CET 2003
5     copyright            : (C) 2003 by Klaas Freitag
6     email                : freitag@kde.org
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 // include files for Qt
19 #include <QLayout>
20 #include <QLabel>
21 #include <QScrollArea>
22 #include <QStackedWidget>
23 #include <QSizePolicy>
24 #include <QTextEdit>
25 #include <QSignalMapper>
26 #include <QTabWidget>
27 #include <QColor>
28 #include <QSplitter>
29 #include <QToolTip>
30 #include <QFont>
31 #include <QResizeEvent>
32 #include <QPalette>
33 #include <QTimer>
34 #include <QScrollBar>
35 #include <QComboBox>
36 #include <QDateTimeEdit>
37 #include <QMessageBox>
38 
39 #include <QDebug>
40 #include <QDialog>
41 #include <QDialogButtonBox>
42 #include <QPushButton>
43 #include <QVBoxLayout>
44 
45 // application specific includes
46 #include "kraftdb.h"
47 #include "kraftsettings.h"
48 #include "kraftview.h"
49 #include "kraftdoc.h"
50 #include "ui_docheader.h"
51 #include "documentman.h"
52 #include "docassistant.h"
53 #include "positionviewwidget.h"
54 #include "ui_docfooter.h"
55 #include "docposition.h"
56 #include "unitmanager.h"
57 #include "docpostcard.h"
58 #include "kataloglistview.h"
59 #include "katalogman.h"
60 #include "templkatalog.h"
61 #include "templkataloglistview.h"
62 #include "catalogselection.h"
63 #include "kraftdocheaderedit.h"
64 #include "kraftdocpositionsedit.h"
65 #include "kraftdocfooteredit.h"
66 #include "inserttempldialog.h"
67 #include "defaultprovider.h"
68 #include "stockmaterial.h"
69 #include "templtopositiondialogbase.h"
70 #include "doctype.h"
71 #include "catalogtemplate.h"
72 #include "importitemdialog.h"
73 #include "addressprovider.h"
74 #include "addressselectordialog.h"
75 #include "format.h"
76 
77 #define NO_TAX   0
78 #define RED_TAX  1
79 #define FULL_TAX 2
80 #define INDI_TAX 3
81 
KraftView(QWidget * parent)82 KraftView::KraftView(QWidget *parent) :
83   KraftViewBase( parent ),
84   mHelpLabel(nullptr), mRememberAmount( -1 ), mModified( false ),
85   mTaxBefore( -1 ), mDocPosEditorIndx( -1 )
86 {
87   setWindowTitle( i18n("Document" ) );
88   setModal( false );
89   QVBoxLayout *mainLayout = new QVBoxLayout;
90   setLayout(mainLayout);
91 
92 
93   mDetailHeader = new QLabel;
94   mDetailHeader->setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ) );
95   mDetailHeader->setFrameStyle( QFrame::Box + QFrame::Plain );
96   mDetailHeader->setLineWidth( 1 );
97   mDetailHeader->setAutoFillBackground(true);
98 
99   mAddressProvider = new AddressProvider( this );
100   connect( mAddressProvider, SIGNAL(lookupResult(QString,KContacts::Addressee)),
101            this, SLOT( slotAddresseeFound(QString,KContacts::Addressee)));
102 
103   QPalette palette;
104   palette.setColor(mDetailHeader->backgroundRole(), QColor( "darkBlue" ));
105   palette.setColor(mDetailHeader->foregroundRole(), QColor( "white "));
106   mDetailHeader->setPalette( palette );
107   mDetailHeader->setTextFormat( Qt::PlainText );
108   mDetailHeader->setFixedHeight( 40 ); // FIXME
109   QFont f = mDetailHeader->font();
110   f.setPointSize( qRound( 1.4 * f.pointSize() ) );
111   f.setBold( true );
112   mDetailHeader->setFont( f );
113   mainLayout->addWidget( mDetailHeader );
114 
115   mCSplit    = new QSplitter(this);
116   mainLayout->addWidget( mCSplit );
117 
118   mViewStack = new QStackedWidget;
119   mCSplit->addWidget( mViewStack );
120 
121   // qDebug () << "mViewSTack height is " << mViewStack->height() << endl;
122 
123   mAssistant = new DocAssistant( mCSplit );
124   mCSplit->addWidget( mAssistant );
125 
126   /* catalog template selection signal */
127   connect(mAssistant, &DocAssistant::templatesToDocument,
128           this, &KraftView::slotAddItems);
129 
130   /* signal to toggle the visibility of the template section in the assistant */
131   connect(  mAssistant, SIGNAL( toggleShowTemplates( bool ) ),
132             this,  SLOT( slotShowTemplates( bool ) ) );
133 
134   /* signal that brings a new address to the document */
135   connect( mAssistant, SIGNAL( headerTextTemplate( const QString& ) ),
136            this, SLOT( slotNewHeaderText( const QString& ) ) );
137 
138   connect( mAssistant, SIGNAL( footerTextTemplate( const QString& ) ),
139            this, SLOT( slotNewFooterText( const QString& ) ) );
140 
141   connect( mAssistant, SIGNAL( selectPage( int ) ),
142            this,  SLOT( slotSwitchToPage( int ) ) );
143 
144   mAssistant->slotSelectDocPart( KraftDoc::Header );
145 
146   setupMappers();
147 
148   QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
149   QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
150   okButton->setDefault(true);
151   okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
152   connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
153   connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
154   mainLayout->addWidget(buttonBox);
155 
156 
157 }
158 
~KraftView()159 KraftView::~KraftView()
160 {
161     // qDebug () << "KRAFTVIEW going away." << endl;
162 }
163 
setupMappers()164 void KraftView::setupMappers()
165 {
166 
167   mDeleteMapper = new QSignalMapper( this );
168   connect( mDeleteMapper, SIGNAL( mapped(int)),
169            this, SLOT( slotDeletePosition( int ) ) );
170 
171   mMoveUpMapper = new QSignalMapper( this );
172   connect( mMoveUpMapper, SIGNAL( mapped(int)),
173            this, SLOT( slotMovePositionUp( int  ) ) );
174 
175   mMoveDownMapper = new QSignalMapper( this );
176   connect( mMoveDownMapper, SIGNAL( mapped(int)),
177            this, SLOT( slotMovePositionDown( int ) ) );
178 
179   mLockPositionMapper = new QSignalMapper( this );
180   connect( mLockPositionMapper, SIGNAL( mapped( int )),
181            this, SLOT( slotLockPosition( int ) ) );
182 
183   mUnlockPositionMapper = new QSignalMapper( this );
184   connect( mUnlockPositionMapper, SIGNAL( mapped( int )),
185            this, SLOT( slotUnlockPosition( int ) ) );
186 
187   mModifiedMapper = new QSignalMapper( this );
188   connect( mModifiedMapper,  SIGNAL( mapped( int ) ),
189            this,  SLOT( slotPositionModified( int ) ) );
190   // block signals as long as the widget is built up.
191   // unblocking happens in redrawDocument()
192   mModifiedMapper->blockSignals(true);
193 }
194 
setup(DocGuardedPtr doc)195 void KraftView::setup( DocGuardedPtr doc )
196 {
197   KraftViewBase::setup(doc);
198 
199   if ( KraftSettings::self()->docViewSplitter().count() == 2 ) {
200     mCSplit->setSizes( KraftSettings::self()->docViewSplitter() );
201   }
202 
203   setupDocHeaderView();
204   setupItems();
205   setupFooter();
206   setWindowTitle( m_doc->docIdentifier() );
207   slotSwitchToPage( KraftDoc::Header );
208 
209   if( doc->isNew() ) {
210       mModified = true;
211   }
212 }
213 
214 
slotSwitchToPage(int id)215 void KraftView::slotSwitchToPage( int id )
216 {
217   // check if the wanted part is already visible
218   if ( mViewStack->currentWidget() == mViewStack->widget( id ) ) return;
219 
220 
221   mViewStack->setCurrentIndex( id );
222 
223   KraftDocEdit *edit =
224     static_cast<KraftDocEdit *>( mViewStack->currentWidget() );
225 
226   mDetailHeader->setText( edit->title() );
227 
228   QPalette palette;
229   palette.setColor(mDetailHeader->backgroundRole(), edit->color());
230   // FIXME: color
231   palette.setColor(mDetailHeader->foregroundRole(), QColor( "#00008b" ));
232   mDetailHeader->setPalette( palette );
233 
234   mAssistant->slotSelectDocPart( mViewStack->currentIndex() );
235 }
236 
slotShowTemplates(bool)237 void KraftView::slotShowTemplates( bool )
238 {
239 }
240 
setupDocHeaderView()241 void KraftView::setupDocHeaderView()
242 {
243     KraftDocHeaderEdit *edit = new KraftDocHeaderEdit(this);
244 
245     mHeaderId = mViewStack->addWidget( edit ); // , KraftDoc::Header );
246 
247     m_headerEdit = edit->docHeaderEdit();
248 
249     m_headerEdit->m_cbType->clear();
250     // m_headerEdit->m_cbType->insertStringList( DefaultProvider::self()->docTypes() );
251     m_headerEdit->m_cbType->insertItems(-1, DocType::allLocalised() );
252     m_headerEdit->mButtLang->hide();
253 
254     const QString predecessorDbId = m_doc->predecessorDbId();
255     bool predecIsVisible = false;
256     if( !predecessorDbId.isEmpty() ) {
257         DocGuardedPtr predecDoc = DocumentMan::self()->openDocument(predecessorDbId);
258         if( predecDoc ) {
259             const QString id = predecDoc->docIdentifier();
260             const QString link = QString("<a href=\"doc://show?id=%1\">%2</a>").arg(predecessorDbId).arg(id);
261             m_headerEdit->_labFollowup->setText( i18n("Successor of %1", link));
262             predecIsVisible = true;
263             connect( m_headerEdit->_labFollowup, SIGNAL(linkActivated(QString)),
264                      this, SLOT(slotLinkClicked(QString)));
265             delete predecDoc;
266         }
267     }
268     m_headerEdit->_labFollowup->setVisible(predecIsVisible);
269 
270     connect( m_headerEdit->m_cbType,  SIGNAL( activated( const QString& ) ),
271              this, SLOT( slotDocTypeChanged( const QString& ) ) );
272 
273     connect( m_headerEdit->mButtLang, SIGNAL( clicked() ),
274              this, SLOT( slotLanguageSettings() ) );
275     connect( edit, SIGNAL( modified() ),
276               this, SLOT( slotModifiedHeader() ) );
277     connect( edit, SIGNAL(pickAddressee()), this, SLOT(slotPickAddressee()) );
278 }
279 
slotLinkClicked(const QString & link)280 void KraftView::slotLinkClicked(const QString& link)
281 {
282     QUrl u(link);
283     if( u.scheme() == "doc" && u.host() == "show" ) {
284         const QString ident = u.queryItemValue("id");
285         qDebug() << "Link clicked to open document " << ident;
286         emit openROView( ident );
287     }
288 }
289 
setupItems()290 void KraftView::setupItems()
291 {
292     KraftDocPositionsEdit *edit = new KraftDocPositionsEdit(this);
293     mDocPosEditorIndx = mViewStack->addWidget( edit ); // , KraftDoc::Positions );
294 
295     m_positionScroll = edit->positionScroll();
296 
297     connect( edit, SIGNAL( addPositionClicked() ), SLOT( slotAddNewItem() ) );
298     connect( edit, SIGNAL( addExtraClicked() ), SLOT( slotAddExtraPosition() ) );
299     connect( edit, SIGNAL( importItemsClicked() ), SLOT( slotImportItems() ) );
300 
301 }
302 
redrawDocument()303 void KraftView::redrawDocument( )
304 {
305     KraftDoc *doc = getDocument();
306     if( !doc ) {
307       // qDebug () << "ERR: No document available in view, return!" << endl;
308     } else {
309       // qDebug () << "** Starting redraw of document " << doc << endl;
310     }
311 
312     /* header: date and document type */
313     QDate date = doc->date();
314     m_headerEdit->m_dateEdit->setDate( date );
315     m_headerEdit->m_cbType->setCurrentIndex(m_headerEdit->m_cbType->findText( doc->docType() ));
316 
317     /* header: address */
318     mContactUid  = doc->addressUid();
319     QString address = doc->address();
320 
321     // qDebug () << "Loaded address uid from database " << mContactUid << endl;
322     if( ! mContactUid.isEmpty() ) {
323       mAddressProvider->lookupAddressee( mContactUid );
324     }
325 
326     if( !address.isEmpty() ) {
327       // custom address stored in the document.
328       // qDebug() << "custom address came in: " << address << endl;
329       m_headerEdit->m_postAddressEdit->setText( address );
330     }
331 
332     if( !doc->salut().isEmpty() ) {
333       m_headerEdit->m_letterHead->insertItem(-1, doc->salut() );
334       m_headerEdit->m_letterHead->setCurrentIndex(m_headerEdit->m_letterHead->findText( doc->salut() ));
335     }
336     /* pre- and post text */
337     m_headerEdit->m_teEntry->setText( doc->preText() );
338     m_headerEdit->m_whiteboardEdit->setText( doc->whiteboard() );
339     m_headerEdit->mProjectLabelEdit->setText( doc->projectLabel() );
340     m_footerEdit->ui()->m_teSummary->setText( doc->postText() );
341     const QString goodbye = doc->goodbye();
342     m_footerEdit->slotSetGreeting(goodbye);
343 
344     mAssistant->slotSetDocType( doc->docType() );
345 
346     redrawDocPositions( );
347     slotDocTypeChanged( doc->docType() );
348     refreshPostCard();
349     mModifiedMapper->blockSignals(false);
350 
351    // mModified = false;
352 }
353 
slotPickAddressee()354 void KraftView::slotPickAddressee()
355 {
356     AddressSelectorDialog dialog(this);
357 
358     if( dialog.exec() ) {
359         const KContacts::Addressee contact = dialog.addressee();
360         slotNewAddress( contact );
361     }
362 }
363 
slotAddresseeFound(const QString & uid,const KContacts::Addressee & contact)364 void KraftView::slotAddresseeFound( const QString& uid, const KContacts::Addressee& contact )
365 {
366     if( contact.isEmpty() ) {
367         const QString err = mAddressProvider->errorMsg(uid);
368         if( !err.isEmpty() ) {
369             qDebug () << "Error while looking up address for uid" << uid << ":" << err;
370         }
371     } else {
372         slotNewAddress( contact, false );
373         qDebug () << "No contact found for uid " << uid;
374     }
375 }
376 
slotNewAddress(const KContacts::Addressee & contact,bool interactive)377 void KraftView::slotNewAddress( const KContacts::Addressee& contact, bool interactive )
378 {
379 
380     Addressee adr( contact );
381 
382     if( contact.isEmpty() ) {
383         return;
384     }
385     QString newAddress = mAddressProvider->formattedAddress( contact );
386     const QString currAddress = m_headerEdit->m_postAddressEdit->toPlainText();
387 
388     bool replace = true;
389     m_headerEdit->m_labName->setText( contact.realName() );
390 
391     if( currAddress.isEmpty() ) {
392         replace = true;
393     } else if( currAddress != newAddress ) {
394         // non empty and current different from new address
395         // need to ask first if we overwrite
396         if( interactive ) {
397             QMessageBox msgBox;
398             msgBox.setText(i18n( "The address label is not empty and different from the selected one.<br/>"
399                                  "Do you really want to replace it with the text shown below?<pre>%1</pre>", newAddress));
400 
401             msgBox.setStandardButtons(QMessageBox::Yes| QMessageBox::No);
402             msgBox.setDefaultButton(QMessageBox::Yes);
403             int ret = msgBox.exec();
404 
405             if ( ret != QMessageBox::Yes) {
406                 replace = false;
407             }
408         } else {
409             // this happens when the document is loaded and the address arrives from addressbook
410             replace = false;
411         }
412     } else if( currAddress == newAddress ) {
413         // both are equal, no action needed
414         replace = false;
415     }
416 
417     if( replace ) {
418         mContactUid = contact.uid();
419 
420         m_headerEdit->m_postAddressEdit->setText( newAddress );
421 
422         // Generate the welcome
423         m_headerEdit->m_letterHead->clear();
424         QStringList li = generateLetterHead(adr.familyName(), adr.givenName());
425 
426         m_headerEdit->m_letterHead->insertItems(-1, li );
427         m_headerEdit->m_letterHead->setCurrentIndex( KraftSettings::self()->salut() );
428     }
429 }
430 
redrawDocPositions()431 void KraftView::redrawDocPositions( )
432 {
433   // If there is no position yet, come up with a help message.
434   KraftDoc *doc = getDocument();
435   if ( ! doc ) return;
436 
437   DocPositionList list = doc->positions();
438   if ( list.count() == 0 ) {
439     // the doc has no positions yet. Let's show a help page
440     if ( ! mHelpLabel ) {
441       mHelpLabel = new QLabel(this);
442       mHelpLabel->setTextFormat(Qt::RichText);
443       // mHelpLabel->setMinimumHeight(400);
444 //TODO PORT QT5       mHelpLabel->setMargin( QDialog::marginHint() );
445       mHelpLabel->setText( i18n( "<qt><h2>The Document Items List is still empty, but Items "
446                                  "can be added now.</h2>"
447                                  "To add items to the document either "
448                                  "<ul>"
449                                  "<li>Press the 'Add item' button above.</li>"
450                                  "<li>Open the template catalog by clicking on the 'show Template' "
451                                   "button on the right and pick one of the available templates.</li>"
452                                    "</ul></qt>" ) );
453       mHelpLabel->setWordWrap(true);
454       mHelpLabel->setMinimumHeight(200);
455       m_positionScroll->addChild( mHelpLabel, 0);
456     }
457     return;
458 
459   } else {
460     if ( mHelpLabel ) {
461       delete mHelpLabel;
462       mHelpLabel = nullptr;
463     }
464   }
465 
466   // qDebug () << "starting to redraw " << list.count() << " positions!";
467   int cnt = 0;
468   DocPositionListIterator it( list );
469   while( it.hasNext() ) {
470     DocPositionBase *dp = it.next();
471     PositionViewWidget *w = mPositionWidgetList.widgetFromPosition( dp );
472     if( !w ) {
473       w = createPositionViewWidget( dp, cnt);
474       w->slotAllowIndividualTax( currentTaxSetting() == DocPositionBase::TaxIndividual );
475     }
476     cnt++;
477     // qDebug () << "now position " << dp->positionNumber() << endl;
478   }
479 
480   // now go through the positionWidgetMap and check if it contains elements
481   // that are not any longer in the list of positions
482 
483   QMutableListIterator<PositionViewWidget*> it2( mPositionWidgetList );
484   while( it2.hasNext() ) {
485     PositionViewWidget *w = it2.next();
486 
487     if( w && (list.contains( w->position() ) == 0) ) {
488       // qDebug () << "Removing this one: " << w << endl;
489       m_positionScroll->removeChild( w );
490       it2.remove();
491       // mPositionWidgetList.erase( it2 );
492       break;
493     }
494   }
495 }
496 
setMappingId(QWidget * widget,int pos)497 void KraftView::setMappingId( QWidget *widget, int pos )
498 {
499   mDeleteMapper->setMapping( widget, pos );
500   mMoveUpMapper->setMapping( widget, pos );
501   mMoveDownMapper->setMapping( widget, pos );
502   mLockPositionMapper->setMapping( widget, pos );
503   mUnlockPositionMapper->setMapping( widget, pos );
504   mModifiedMapper->setMapping( widget, pos );
505 }
506 
507 //
508 // create a new position widget.
509 // The position parameter comes in as list counter, starting at 0
510 
createPositionViewWidget(DocPositionBase * dp,int pos)511 PositionViewWidget *KraftView::createPositionViewWidget( DocPositionBase *dp, int pos )
512 {
513   PositionViewWidget *w = new PositionViewWidget( );
514 
515   int cw = m_positionScroll->width();
516   if ( cw < 400 ) cw = 400;
517   w->resize( cw, w->height() );
518 
519   connect( w, SIGNAL( deletePosition() ),  mDeleteMapper, SLOT( map() ) );
520   connect( w, SIGNAL( moveUp() ),          mMoveUpMapper, SLOT( map() ) );
521   connect( w, SIGNAL( moveDown() ),        mMoveDownMapper, SLOT( map() ) );
522   connect( w, SIGNAL( lockPosition() ),    mLockPositionMapper, SLOT( map() ) );
523   connect( w, SIGNAL( unlockPosition() ),  mUnlockPositionMapper, SLOT( map() ) );
524   connect( w, SIGNAL( positionModified()), mModifiedMapper,  SLOT( map() ) );
525 
526   setMappingId( w, pos );
527 
528   QStringList units = UnitManager::self()->allUnits();
529   units.sort();
530   w->m_cbUnit->addItems(units);
531 
532   if( dp->dbId().toInt() < 0 ) {
533     w->slotSetState( PositionViewWidget::New );
534   }
535 
536   /* do resizing and add the widget to the scrollview and move it to the final place */
537 
538   if ( mHelpLabel ) {
539     mHelpLabel->hide();
540   }
541 
542   mPositionWidgetList.insert( pos,  w );
543   m_positionScroll->addChild( w, pos );
544 
545   //Set the correct indexes when the widget is not appended
546   if(pos < mPositionWidgetList.count())
547   {
548     for(int i = pos+1; i < mPositionWidgetList.count(); ++i)
549     {
550       mPositionWidgetList.at(i)->setOrdNumber(i+1);
551       setMappingId(mPositionWidgetList.at(i), i);
552     }
553   }
554 
555   w->setDocPosition(dp);
556   w->setOrdNumber( pos+1 );
557   w->slotSetTax( dp->taxType() );
558   return w;
559 }
560 
currentTaxSetting()561 DocPositionBase::TaxType KraftView::currentTaxSetting()
562 {
563   // add 1 to the currentItem since that starts with zero.
564   int taxKind = 1+( m_footerEdit->ui()->mTaxCombo->currentIndex() );
565   DocPositionBase::TaxType tt = DocPositionBase::TaxInvalid;
566 
567   if ( taxKind == 1 ) { // No Tax at all
568     tt = DocPositionBase::TaxNone;
569   } else if ( taxKind == 2 ) { // Reduced tax for all items
570     tt = DocPositionBase::TaxReduced;
571   } else if ( taxKind == 3 ) { // Full tax for all items
572     tt = DocPositionBase::TaxFull;
573   } else { // individual level
574     tt = DocPositionBase::TaxIndividual;
575   }
576   return tt;
577 }
578 
refreshPostCard()579 void KraftView::refreshPostCard()
580 {
581   DocPositionList positions = currentPositionList();
582 
583   if( !getDocument() ) return;
584 
585   if ( mAssistant->postCard() ) {
586     QDate d = m_headerEdit->m_dateEdit->date();
587     const QString dStr = Format::toDateString( d, KraftSettings::self()->dateFormat() );
588 
589     mAssistant->postCard()->setHeaderData( m_headerEdit->m_cbType->currentText(),
590                                            dStr, m_headerEdit->m_postAddressEdit->toPlainText(),
591                                            getDocument()->ident(),
592                                            m_headerEdit->m_teEntry->toPlainText() );
593 
594 
595     mAssistant->postCard()->setPositions( positions,  currentTaxSetting(),
596                                           DocumentMan::self()->tax( d ),
597                                           DocumentMan::self()->reducedTax( d ) );
598 
599     mAssistant->postCard()->setFooterData( m_footerEdit->ui()->m_teSummary->toPlainText(),
600                                            m_footerEdit->greeting() );
601 
602     mAssistant->postCard()->renderDoc( mViewStack->currentIndex() ); // id( mViewStack->visibleWidget() ) );
603   }
604 
605 
606   DocPositionListIterator it( positions );
607   DocPositionBase *dp;
608   while( it.hasNext()) {
609     dp = it.next();
610 
611     if (  dp->type() == DocPositionBase::ExtraDiscount ) {
612       PositionViewWidget *w = ( static_cast<DocPosition*>( dp ) )->associatedWidget();
613       if( w ) {
614         w->slotSetOverallPrice( ( static_cast<DocPosition*>( dp ) )->overallPrice() );
615       } else {
616         // qDebug () << "Warning: Position object has no associated widget!" << endl;
617       }
618     }
619   }
620 }
621 
setupFooter()622 void KraftView::setupFooter()
623 {
624   m_footerEdit = new KraftDocFooterEdit(this);
625 
626   mViewStack->addWidget( m_footerEdit ); //  KraftDoc::Footer );
627 
628   // ATTENTION: If you change the following inserts, make sure to check the code
629   //            in method currentPositionList!
630   m_footerEdit->ui()->mTaxCombo->insertItem( NO_TAX,   QIcon::fromTheme("kraft_notax"), i18n( "Display no tax at all" ));
631   m_footerEdit->ui()->mTaxCombo->insertItem( RED_TAX,  QIcon::fromTheme("kraft_redtax"), i18n( "Calculate reduced tax for all items" ));
632   m_footerEdit->ui()->mTaxCombo->insertItem( FULL_TAX, QIcon::fromTheme("kraft_fulltax"), i18n( "Calculate full tax for all items" ));
633   m_footerEdit->ui()->mTaxCombo->insertItem( INDI_TAX, i18n( "Calculate individual tax for each item"));
634 
635   // set the tax type combo correctly: If all items have the same tax type, take it.
636   // If items have different, its the individual thing.
637   DocPositionList list = m_doc->positions();
638 
639   int tt = -1;
640   DocPositionBase *dp = nullptr;
641 
642   DocPositionListIterator it( list );
643   int taxIndex = 0;
644 
645   while( it.hasNext()) {
646     dp = it.next();
647     if ( tt == -1 )
648       tt = dp->taxTypeNumeric(); // store the first entry.
649     else {
650       if ( tt != dp->taxTypeNumeric() ) {
651         taxIndex = INDI_TAX;
652       } else {
653         // old and new taxtype are the same.
654       }
655     }
656   }
657   if ( tt == -1 ) {
658     // means that there is no item yet, the default tax type needs to be used.
659     int deflt = KraftSettings::self()->defaultTaxType();
660     if ( deflt > 0 ) {
661       deflt -= 1;
662     }
663     taxIndex = deflt;
664   } else {
665     if ( taxIndex == 0 ) {
666       taxIndex = tt-1;
667     }
668   }
669 
670   connect( m_footerEdit->ui()->mTaxCombo,SIGNAL(currentIndexChanged(int)),this,SLOT(slotTaxComboChanged(int)));
671 
672   m_footerEdit->ui()->mTaxCombo->setCurrentIndex( taxIndex );
673   slotTaxComboChanged( taxIndex );
674 
675   mTaxBefore = taxIndex;
676 
677   connect(m_footerEdit,SIGNAL(modified()),this,SLOT(slotModifiedFooter()));
678 }
679 
slotTaxComboChanged(int newId)680 void KraftView::slotTaxComboChanged(int newId)
681 {
682   bool allowTaxSetting = false;
683 
684   if( mTaxBefore == newId ) return;
685 
686   if( mTaxBefore == INDI_TAX ) {
687     // we're changing away from individual settings. WARNING needed.
688     // qDebug () << "You switch away from item individual tax settings.";
689     if( QMessageBox::question( this,
690                                i18n("Tax Settings Overwrite"),
691                                i18n("Really overwrite all individual tax settings of the items?")
692                                ) == QMessageBox::No ) {
693       m_footerEdit->ui()->mTaxCombo->setCurrentIndex( INDI_TAX );
694       return;
695     }
696   }
697   DocPositionBase::TaxType tax = DocPositionBase::TaxFull;
698 
699   if( newId == INDI_TAX ) {
700     allowTaxSetting = true;
701   } else if( newId == RED_TAX ) {
702     tax = DocPositionBase::TaxReduced;
703   } else if( newId == NO_TAX ) {
704     tax = DocPositionBase::TaxNone;
705   }
706 
707   PositionViewWidgetListIterator it( mPositionWidgetList );
708   while( it.hasNext() ) {
709     PositionViewWidget *w = it.next();
710     w->slotAllowIndividualTax( allowTaxSetting );
711     w->slotSetTax( tax );
712   }
713 
714   mTaxBefore = newId;
715   slotModifiedFooter();
716 }
717 
718 /* This is the flow in the move up method:
719          0    Bla1          0 Bla1                         0 Bla1
720          1    Bla2 <- w2    1 Bla2   -> insert at pos-1 => 1 Bla3
721  pos ->  2    Bla3 <- w1    2 Bla4                         2 Bla2
722          3    Bla4                                         3 Bla4
723  */
slotMovePositionUp(int pos)724 void KraftView::slotMovePositionUp( int pos )
725 {
726   PositionViewWidget *w1 = nullptr;
727   PositionViewWidget *w2 = nullptr;
728 
729   // qDebug () << "Moving position up: " << pos << endl;
730   if( pos < 1 || pos > mPositionWidgetList.count() ) {
731     // qDebug () << "ERR: position out of range: " << pos << endl;
732     return;
733   }
734 
735   mPositionWidgetList.swap( pos, pos-1 );
736   w1 = mPositionWidgetList.at( pos-1 );
737   w2 = mPositionWidgetList.at( pos ); // Porting ATTENTION: check assignment of w1, w1
738 
739   // qDebug () << "Found at pos " << pos << " the widgets " << w1 << " and " << w2 << endl;
740 
741   if( w1 && w2 ) {
742     // qDebug () << "Setting ord number: " << pos << endl;
743     w1->setOrdNumber( pos );  // note: ordnumbers start with 1, thus add one
744     w2->setOrdNumber( pos+1 );
745     setMappingId( w1, pos-1 );
746     setMappingId( w2, pos );
747 
748     m_positionScroll->moveChild( w2, m_positionScroll->indexOf(w1) );
749     mModified = true;
750     QTimer::singleShot( 0, this, SLOT(refreshPostCard()  ) );
751   } else {
752     // qDebug () << "ERR: Did not find the two corresponding widgets!" << endl;
753   }
754 }
755 /*
756           0    Bla1          0 Bla1                         0 Bla1
757   pos ->  1    Bla2 <- w1    1 Bla3                         1 Bla3
758           2    Bla3 <- w2    2 Bla4   -> insert at pos+1 => 2 Bla2
759           3    Bla4                                         3 Bla4
760 */
slotMovePositionDown(int pos)761 void KraftView::slotMovePositionDown( int pos )
762 {
763   PositionViewWidget *w1 = nullptr;
764   PositionViewWidget *w2 = nullptr;
765   // qDebug () << "Moving position down: " << pos << endl;
766 
767   if( pos < 0 || pos >= mPositionWidgetList.count() -1 ) {
768     // qDebug () << "ERR: position out of range: " << pos << endl;
769     return;
770   }
771 
772   mPositionWidgetList.swap( pos, pos+1);
773   w1 = mPositionWidgetList.at( pos+1 );
774   w2 = mPositionWidgetList.at( pos );  // Porting ATTENTION: check assignment of w1, w1
775 
776   if( w1 && w2 ) {
777     w1->setOrdNumber( pos+2  );
778     w2->setOrdNumber( pos+1 );
779 
780     setMappingId( w1, pos+1 );
781     setMappingId( w2, pos );
782 
783     m_positionScroll->moveChild( w1, m_positionScroll->indexOf( w2 ) );
784 
785     mModified = true;
786     QTimer::singleShot( 0, this, SLOT( refreshPostCard() ) );
787   } else {
788     // qDebug () << "ERR: Did not find the two corresponding widgets!" << endl;
789   }
790 }
791 
slotDeletePosition(int pos)792 void KraftView::slotDeletePosition( int pos )
793 {
794   PositionViewWidget *w1 = mPositionWidgetList.at( pos );
795   if( w1 ) {
796     w1->slotSetState( PositionViewWidget::Deleted );
797     w1->slotModified();
798   }
799 }
800 
slotLockPosition(int pos)801 void KraftView::slotLockPosition( int pos )
802 {
803   // qDebug () << "Locking Position " << pos << endl;
804 
805   PositionViewWidget *w1 = mPositionWidgetList.at( pos );
806   if( w1 ) {
807     w1->slotSetState( PositionViewWidget::Locked );
808     refreshPostCard();
809   }
810 }
811 
slotUnlockPosition(int pos)812 void KraftView::slotUnlockPosition( int pos )
813 {
814   // qDebug () << "Unlocking Position " << pos << endl;
815 
816   PositionViewWidget *w1 = mPositionWidgetList.at( pos );
817   if( w1 ) {
818     w1->slotSetState( PositionViewWidget::Active );
819     refreshPostCard();
820   }
821 }
822 
slotPositionModified(int)823 void KraftView::slotPositionModified( int )
824 {
825     // qDebug () << "Modified Position " << pos << endl;
826     mModified = true;
827 
828     QTimer::singleShot( 0, this, SLOT( refreshPostCard() ) );
829 }
830 
slotAddressFound(const QString & uid,const KContacts::Addressee & contact)831 void KraftView::slotAddressFound(const QString& uid, const KContacts::Addressee& contact)
832 {
833     const QString currAddress = m_headerEdit->m_postAddressEdit->toPlainText();
834     const QString newAddress = mAddressProvider->formattedAddress(contact);
835     bool replace = true;
836 
837     if( currAddress.isEmpty() ) {
838         replace = true;
839     } else if(currAddress != newAddress) {
840         // non empty and current different from new address
841         // need to ask first if we overwrite
842         if( QMessageBox::question( this, i18n("Address Overwrite"),
843                                    i18n("The address label is not empty and different from the selected one.<br/>"
844                                         "Do you really want to replace it with the text shown below?<pre>%1</pre>",
845 					newAddress)
846                                    ) == QMessageBox::No ) {
847             replace = false;
848         }
849     } else if( currAddress == newAddress ) {
850         // both are equal, no action needed
851         return;
852     }
853 
854     if( replace ) {
855         mContactUid = uid;
856         m_headerEdit->m_labName->setText( contact.realName() );
857 
858         m_headerEdit->m_postAddressEdit->setText( newAddress );
859 
860         // Generate the welcome
861         m_headerEdit->m_letterHead->clear();
862         QStringList li = generateLetterHead( contact.familyName(), contact.givenName() );
863 
864         m_headerEdit->m_letterHead->insertItems(-1, li );
865         KraftDoc *doc = getDocument();
866         if( doc->isNew() ) {
867             m_headerEdit->m_letterHead->setCurrentIndex( KraftSettings::self()->salut() );
868         } else {
869             if(!doc->salut().isEmpty()) {
870                 m_headerEdit->m_letterHead->setCurrentText( doc->salut() );
871             }
872         }
873     }
874 }
875 
slotDocTypeChanged(const QString & newType)876 void KraftView::slotDocTypeChanged( const QString& newType )
877 {
878   // qDebug () << "Doc Type changed to " << newType << endl;
879   mAssistant->slotSetDocType( newType );
880 
881   DocType docType( newType );
882 
883   PositionViewWidgetListIterator it( mPositionWidgetList );
884   while( it.hasNext() ) {
885     PositionViewWidget *w = it.next();
886     w->slotEnableKindMenu( docType.allowAlternative() );
887     w->slotShowPrice(docType.pricesVisible());
888   }
889 
890   mAssistant->postCard()->slotShowPrices( docType.pricesVisible() );
891   m_footerEdit->ui()->_taxGroup->setVisible( docType.pricesVisible() );
892   if( mDocPosEditorIndx > -1 ) {
893       KraftDocPositionsEdit *w = dynamic_cast<KraftDocPositionsEdit*>(mViewStack->widget(mDocPosEditorIndx));
894       if(w) {
895           w->setDiscountButtonVisible(docType.pricesVisible());
896       }
897   }
898 }
899 
slotLanguageSettings()900 void KraftView::slotLanguageSettings()
901 {
902   // qDebug () << "Language Settings" << endl;
903   // FIXME
904 }
905 
slotNewHeaderText(const QString & str)906 void KraftView::slotNewHeaderText( const QString& str )
907 {
908   m_headerEdit->m_teEntry->setText( str );
909   slotModifiedHeader();
910 }
911 
slotNewFooterText(const QString & str)912 void KraftView::slotNewFooterText( const QString& str )
913 {
914   m_footerEdit->ui()->m_teSummary->setText( str );
915   slotModifiedFooter();
916 }
917 
918 // Add a new item. A katalog is required if user wants to store it in a
919 // catalog immediately. FIXME - now the current active catalog in the
920 // catalog selection is used.
slotAddNewItem()921 void KraftView::slotAddNewItem()
922 {
923   Katalog* kat = mAssistant->catalogSelection()->currentSelectedKat();
924   slotAddItem( kat, nullptr, QString() );
925 }
926 
slotAddItems(Katalog * kat,CatalogTemplateList templates,const QString & selectedChapter)927 void KraftView::slotAddItems( Katalog *kat, CatalogTemplateList templates, const QString& selectedChapter)
928 {
929 
930     if(templates.count() == 0) {
931         slotAddItem(kat, nullptr, selectedChapter);
932     } else {
933         for(CatalogTemplate *templ : templates ) {
934             slotAddItem( kat, templ, selectedChapter );
935         }
936     }
937 }
938 
slotAddItem(Katalog * kat,CatalogTemplate * tmpl,const QString & selectedChapter)939 void KraftView::slotAddItem( Katalog *kat, CatalogTemplate *tmpl, const QString& selectedChapter )
940 {
941     // newpos is a list position, starts counting at zero!
942     int newpos = mPositionWidgetList.count();
943     // qDebug () << "Adding Position at list position " << newpos << endl;
944 
945     QScopedPointer<TemplToPositionDialogBase> dia;
946 
947     DocPosition *dp = new DocPosition();
948     dp->setPositionNumber( newpos +1 );
949 
950     bool newTemplate = false;
951     if ( !tmpl ) {
952         newTemplate = true;
953     }
954 
955     dia.reset(new InsertTemplDialog( this ));
956     dia->setCatalogChapters( kat->getKatalogChapters(), selectedChapter);
957 
958     int tmplId = 0;
959 
960     if ( !newTemplate ) {
961         //  it's not a new template
962         dp->setText(tmpl->getText());
963         dp->setUnit(tmpl->unit());
964         dp->setUnitPrice(tmpl->unitPrice());
965     }
966 
967     if ( mRememberAmount > 0 ) {
968         dp->setAmount( mRememberAmount );
969     }
970 
971     KraftDoc *doc = getDocument();
972     if(doc) {
973         DocType docType = doc->docType();
974         dia->setDocPosition( dp, newTemplate, docType.pricesVisible() );
975     }
976     DocPositionList list = currentPositionList();
977     dia->setPositionList( list, newpos );
978 
979     QSize s = KraftSettings::self()->templateToPosDialogSize();
980     dia->resize( s );
981 
982     int execResult = dia->exec();
983 
984     if ( execResult == QDialog::Accepted ) {
985         DocPosition diaPos = dia->docPosition();
986         *dp = diaPos;
987 
988         // set the tax settings
989         if( currentTaxSetting() == DocPositionBase::TaxIndividual ) {
990             // FIXME: In case a new item is added, add the default tax type.
991             // otherwise add the tax of the template
992             dp->setTaxType( DocPositionBase::TaxFull );
993         } else {
994             dp->setTaxType( currentTaxSetting() );
995         }
996 
997         // store the initial size of the template-to-doc-pos dialogs
998         s = dia->size();
999         KraftSettings::self()->setTemplateToPosDialogSize( s );
1000 
1001         if ( kat->type() == TemplateCatalog ) {
1002 
1003             // save the template if it is has a valid chapter.
1004             // the method chapter() considers the checkbox. If it is not checked to keep the template,
1005             // an empty chapter is returned.
1006 
1007             const QString chapter = dia->chapter();
1008             if (!chapter.isEmpty()) {
1009                 int chapterId = KatalogMan::self()->defaultTemplateCatalog()->chapterID(chapter).toInt();
1010 
1011                 FloskelTemplate *flos = new FloskelTemplate( -1, dp->text(),
1012                                                              dp->unit().id(),
1013                                                              chapterId,
1014                                                              1 /* CalcKind = Manual */ );
1015                 flos->setManualPrice( dp->unitPrice() );
1016                 flos->save();
1017                 tmplId = flos->getTemplID();
1018 
1019                 // reload the entire katalog
1020                 Katalog *defaultKat = KatalogMan::self()->defaultTemplateCatalog();
1021                 if( defaultKat ) {
1022                     defaultKat->load();
1023                     KatalogMan::self()->notifyKatalogChange( defaultKat , dbID() );
1024                 }
1025             }
1026         }
1027         if (!newTemplate){
1028             tmplId = static_cast<FloskelTemplate*>(tmpl)->getTemplID();
1029         }
1030     } else if ( kat->type() == MaterialCatalog ) {
1031         if ( newTemplate ) {
1032             // FIXME
1033         }
1034     }
1035 
1036     if (execResult == QDialog::Accepted) {
1037         newpos = dia->insertAfterPosition();
1038 
1039         mRememberAmount = dp->amount();
1040 
1041         if (tmplId > 0) {
1042             kat->recordUsage(tmplId);
1043         }
1044 
1045         PositionViewWidget *widget = createPositionViewWidget( dp, newpos );
1046         widget->slotModified();
1047         widget->slotAllowIndividualTax( currentTaxSetting() == DocPositionBase::TaxIndividual );
1048 
1049         // Check if the new widget is supposed to display prices, based on the doc type
1050         // FIXME: Shouldn't this be done by the positionViewWidget rather than here?
1051         const QString dt = getDocument()->docType();
1052         if( !dt.isEmpty() ) {
1053             DocType docType(dt);
1054             widget->slotShowPrice(docType.pricesVisible());
1055         }
1056         slotFocusItem( widget, newpos );
1057         refreshPostCard();
1058     }
1059 }
1060 
slotImportItems()1061 void KraftView::slotImportItems()
1062 {
1063   ImportItemDialog dia( this );
1064   DocPositionList list = currentPositionList();
1065   int newpos = list.count();
1066   dia.setPositionList( list, newpos );
1067 
1068   if ( dia.exec() ) {
1069     DocPositionList list = dia.positionList();
1070     if ( list.count() > 0 ) {
1071       // qDebug () << "Importlist amount of entries: " << list.count();
1072       int cnt = 0;
1073       int newpos = dia.getPositionCombo()->currentIndex();
1074       // qDebug () << "Newpos is " << newpos;
1075 
1076       DocPositionListIterator posIt( list );
1077       while( posIt.hasNext() ) {
1078         DocPosition *dp_old = static_cast<DocPosition*>(posIt.next());
1079 
1080         DocPosition *dp = new DocPosition( *(dp_old) );
1081         dp->setTaxType( currentTaxSetting() );
1082         PositionViewWidget *widget = createPositionViewWidget( dp, newpos + cnt++ );
1083         widget->slotSetTax( DocPositionBase::TaxFull ); // FIXME: Value from Import?
1084         widget->slotModified();
1085       }
1086       refreshPostCard();
1087     }
1088   }
1089 }
1090 
slotAddExtraPosition()1091 void KraftView::slotAddExtraPosition()
1092 {
1093   // newpos is a list position, starts counting at 0
1094   int newpos = mPositionWidgetList.count();
1095   // qDebug () << "Adding EXTRA Position at position " << newpos << endl;
1096 
1097   DocPosition *dp = new DocPosition( DocPosition::ExtraDiscount );
1098   dp->setPositionNumber( newpos+1 );
1099   dp->setText( i18n( "Discount" ) );
1100   Einheit e = UnitManager::self()->getPauschUnit();
1101   dp->setUnit(e);
1102   if( currentTaxSetting() == DocPositionBase::TaxIndividual ) {
1103     dp->setTaxType( DocPositionBase::TaxFull );
1104   } else {
1105     dp->setTaxType( currentTaxSetting() );
1106   }
1107 
1108   // qDebug () << "New Extra position is " << dp << endl;
1109 
1110   PositionViewWidget *widget = createPositionViewWidget( dp, newpos );
1111   // qDebug () << "PositionViewWiget doc position is: " << widget->position() << endl;
1112   widget->slotModified();
1113   slotFocusItem( widget, newpos );
1114   refreshPostCard();
1115 
1116 }
1117 
currentPositionList()1118 DocPositionList KraftView::currentPositionList()
1119 {
1120     DocPositionList list;
1121     PositionViewWidget *widget = nullptr;
1122     int cnt = 1;
1123 
1124     PositionViewWidgetListIterator outerIt( mPositionWidgetList );
1125 
1126     bool progress = true;
1127 
1128     while ( progress && ( list.count() != mPositionWidgetList.count() ) ) {
1129       // the loop runs until all positions have a valid price.
1130 
1131       int preListCnt = list.count();
1132       // qDebug() << "# Pre List Count: " << preListCnt << endl;
1133 
1134       while ( outerIt.hasNext() ) {
1135         widget = outerIt.next();
1136         DocPositionBase *dpb = widget->position();
1137 
1138         KraftDB::StringMap replaceMap;
1139 
1140         if ( dpb ) {
1141           DocPosition *newDp = new DocPosition( dpb->type() );
1142           newDp->setPositionNumber( cnt++ );
1143           newDp->setAttributeMap( dpb->attributes() );
1144           newDp->setDbId( dpb->dbId().toInt() );
1145           newDp->setAssociatedWidget( widget );
1146 
1147           bool calculatable = true;
1148 
1149           if ( dpb->type() == DocPosition::ExtraDiscount ) {
1150             double discount = widget->mDiscountPercent->value();
1151 
1152             /* set Attributes with the discount percentage */
1153             Attribute a( DocPosition::Discount );
1154             a.setPersistant( true );
1155             a.setValue( discount );
1156             newDp->setAttribute( a );
1157 
1158             QString tagRequired = widget->extraDiscountTagRestriction();
1159 
1160             if ( !tagRequired.isEmpty() ) {
1161               Attribute tr(  DocPosition::ExtraDiscountTagRequired );
1162               tr.setValueRelation( "tagTemplates", "tagTmplID", "name" );
1163               tr.setPersistant( true );
1164               tr.setValue( QVariant( tagRequired ) );
1165               newDp->setAttribute( tr );
1166             }
1167 
1168             /* Calculate the current sum over all widgets */
1169             PositionViewWidgetListIterator it( mPositionWidgetList );
1170             PositionViewWidget *w1;
1171             Geld sum;
1172             // qDebug () << "Starting to loop over the items " << endl;
1173             while (  calculatable && it.hasNext() ) {
1174               w1 = it.next();
1175 
1176               if ( widget != w1 ) { // ATTENTION Porting: do not take the own value into account
1177                 if ( tagRequired.isEmpty()  // means that all positions are to calculate
1178                      || w1->tagList().contains( tagRequired ) ) {
1179                   if ( w1->priceValid() ) {
1180                     sum += w1->currentPrice();
1181                     // qDebug () << "Summing up pos with text " << w1->ordNumber() << " and price "
1182                              // << w1->currentPrice().toLong() << endl;
1183                   } else {
1184                     calculatable = false; // give up, we continue in outerIt
1185                     // qDebug () << "We skip pos " << w1->ordNumber() << endl;
1186                   }
1187                 }
1188               } else {
1189                 // we can not calculate ourselves.
1190                 // qDebug () << "Skipping pos " << w1->ordNumber() << " in summing up, thats me!" << endl;
1191               }
1192             }
1193             // qDebug () << "Finished loop over items with calculatable=" << calculatable << endl;
1194 
1195             if ( calculatable ) {
1196               sum = sum.percent( discount );
1197               newDp->setUnitPrice( sum );
1198               newDp->setAmount( 1.0 );
1199               widget->setCurrentPrice( sum );
1200             }
1201 
1202             // replace some tags in the text
1203 
1204             replaceMap["%DISCOUNT"]     = DefaultProvider().self()->locale()->toString( discount );
1205             replaceMap["%ABS_DISCOUNT"] = DefaultProvider().self()->locale()->toString( qAbs( discount ) );
1206 
1207           } else {
1208             /* Type is ordinary position */
1209             newDp->setUnitPrice( widget->unitPrice() );
1210 
1211             double v = widget->m_sbAmount->value();
1212             newDp->setAmount( v );
1213             widget->setCurrentPrice( newDp->overallPrice() );
1214           }
1215 
1216           if ( calculatable ) {
1217             // copy information from the widget
1218             newDp->setToDelete( widget->deleted() );
1219 
1220             QString t = widget->m_teFloskel->toPlainText();
1221             if ( !replaceMap.empty() ) {
1222               t = KraftDB::self()->replaceTagsInWord( t, replaceMap );
1223             }
1224             newDp->setText( t );
1225 
1226             QString h = widget->m_cbUnit->currentText();
1227             int eId   = UnitManager::self()->getUnitIDSingular( h );
1228             Einheit e = UnitManager::self()->getUnit( eId );
1229             newDp->setUnit( e );
1230 
1231             PositionViewWidget::Kind k = widget->kind();
1232 
1233             if ( k != PositionViewWidget::Normal ) {
1234               Attribute a( DocPosition::Kind );
1235               a.setPersistant( true );
1236               a.setValue( widget->kindString() );
1237               newDp->setAttribute( a );
1238             } else {
1239               newDp->removeAttribute( DocPosition::Kind );
1240             }
1241 
1242             /* set Attribute with the tags */
1243             QStringList tagStrings = widget->tagList();
1244             if ( !tagStrings.isEmpty() ) {
1245               Attribute tags( DocPosition::Tags );
1246               tags.setValueRelation( "tagTemplates", "tagTmplID", "name" );
1247               tags.setPersistant( true );
1248               tags.setListValue( true );
1249               tags.setValue( QVariant( tagStrings ) );
1250               newDp->setAttribute( tags );
1251               // qDebug() << "============ " << tags.toString() << endl;
1252             } else {
1253               newDp->removeAttribute( DocPosition::Tags );
1254             }
1255 
1256             // tax settings
1257             if( currentTaxSetting() == DocPositionBase::TaxIndividual ) {
1258               newDp->setTaxType( widget->taxType() );
1259             } else {
1260               newDp->setTaxType( currentTaxSetting() );
1261             }
1262             list.append( newDp );
1263           }
1264         } else {
1265           qCritical() << "Fatal: Widget without position found!" << endl;
1266         }
1267       }
1268       // qDebug() << " Post List Count: " << list.count() << endl;
1269 
1270       if ( preListCnt == list.count() ) {
1271         qCritical() << "No progress in widget list processing - abort!" << endl;
1272         progress = false;
1273       }
1274     }
1275     return list;
1276 }
1277 
slotShowCatalog(bool on)1278 void KraftView::slotShowCatalog( bool on )
1279 {
1280   if ( on ) {
1281     mAssistant->slotShowCatalog();
1282   } else {
1283     mAssistant->setFullPreview( true, KraftDoc::Positions );
1284   }
1285 }
1286 
slotModifiedPositions()1287 void KraftView::slotModifiedPositions()
1288 {
1289     // As long as the modified mapper is blocked, ignore this.
1290     if( mModifiedMapper->signalsBlocked() ) {
1291         return;
1292     }
1293 
1294     qDebug () << "Position Modified" << endl;
1295     mModified = true;
1296 }
1297 
slotModifiedHeader()1298 void KraftView::slotModifiedHeader()
1299 {
1300     // qDebug () << "Modified the header!" << endl;
1301     // As long as the modified mapper is blocked, ignore this.
1302     if( mModifiedMapper->signalsBlocked() ) {
1303         return;
1304     }
1305 
1306     mModified = true;
1307 
1308     QTimer::singleShot( 0, this, SLOT( refreshPostCard() ) );
1309 }
1310 
slotModifiedFooter()1311 void KraftView::slotModifiedFooter()
1312 {
1313     // qDebug () << "Modified the footer!" << endl;
1314     // As long as the modified mapper is blocked, ignore this.
1315     if( mModifiedMapper->signalsBlocked() ) {
1316         return;
1317     }
1318 
1319     mModified = true;
1320 
1321     QTimer::singleShot( 0, this, SLOT( refreshPostCard() ) );
1322 }
1323 
generateLetterHead(const QString & familyName,const QString & givenName)1324 QStringList KraftView::generateLetterHead( const QString& familyName, const QString& givenName )
1325 {
1326     QStringList s;
1327 
1328     KraftDB::StringMap m;
1329     m[ "%NAME"]       = familyName;
1330     m[ "%GIVEN_NAME"] = givenName;
1331 
1332     return KraftDB::self()->wordList( "salut", m );
1333 }
1334 
done(int r)1335 void KraftView::done( int r )
1336 {
1337     bool okToContinue = true;
1338 
1339     //Closed using the cancel button .. Check if we can close
1340     if(r == 0) {
1341         if( mModified ) {
1342             okToContinue = documentModifiedMessageBox();
1343             if(!okToContinue) {
1344                 return;
1345             }
1346         }
1347     }
1348     //Closed using the OK button .. it can be closed, but data needs saved
1349     if( mModified && r > 0 ) {
1350         saveChanges();
1351         emit viewClosed( r == 1, m_doc );
1352     }
1353     // remember the sizes of the docassistant splitter if visible.
1354     mAssistant->saveSplitterSizes();
1355     KraftSettings::self()->setDocViewSplitter(mCSplit->sizes());
1356     const QByteArray geo = saveGeometry().toBase64();
1357     KraftSettings::self()->setDocEditGeometry(geo);
1358 
1359     QDialog::done( r );
1360 }
1361 
saveChanges()1362 void KraftView::saveChanges()
1363 {
1364     // qDebug () << "Saving changes!" << endl;
1365 
1366     KraftDoc *doc = getDocument();
1367 
1368     if( !doc ) {
1369       // qDebug () << "ERR: No document available in view, return!" << endl;
1370       return;
1371     }
1372     // transfer all values to the document
1373     doc->setDate( m_headerEdit->m_dateEdit->date() );
1374     doc->setAddressUid( mContactUid );
1375     doc->setAddress(  m_headerEdit->m_postAddressEdit->toPlainText() );
1376     doc->setDocType(  m_headerEdit->m_cbType->currentText() );
1377     doc->setPreText(  m_headerEdit->m_teEntry->toPlainText() );
1378     doc->setWhiteboard( m_headerEdit->m_whiteboardEdit->toPlainText() );
1379     doc->setProjectLabel( m_headerEdit->mProjectLabelEdit->text() );
1380     doc->setSalut(    m_headerEdit->m_letterHead->currentText() );
1381     doc->setPostText( m_footerEdit->ui()->m_teSummary->toPlainText() );
1382     doc->setGoodbye(  m_footerEdit->greeting() );
1383 
1384     DocPositionList list = currentPositionList();
1385     doc->setPositionList( list );
1386 
1387     doc->saveDocument( );
1388 
1389     if ( doc->isNew() ) {
1390       // For new documents the user had to select a greeting and we make this
1391       // default for the future
1392       KraftSettings::self()->setGreeting( m_footerEdit->greeting() );
1393       KraftSettings::self()->setSalut( m_headerEdit->m_letterHead->currentIndex() );
1394     }
1395 }
1396 
slotFocusItem(PositionViewWidget * posWidget,int pos)1397 void KraftView::slotFocusItem( PositionViewWidget *posWidget, int pos )
1398 {
1399   if( posWidget && pos > 0) {
1400     int y = (1+pos)*posWidget->height();
1401     m_positionScroll->ensureVisible(0, y);
1402   } else {
1403     m_positionScroll->ensureVisible( 0, 0 );
1404   }
1405   // setting Focus within the item.
1406   if( posWidget ) {
1407     if( posWidget->m_teFloskel->toPlainText().isEmpty() ) {
1408       posWidget->m_teFloskel->setFocus();
1409     } else {
1410       posWidget->m_sbAmount->setFocus();
1411     }
1412   }
1413 }
1414 
documentModifiedMessageBox()1415 bool KraftView::documentModifiedMessageBox()
1416 {
1417   if ( mModified ) {
1418       QMessageBox msgBox;
1419       msgBox.setText(i18n("The document has been modified."));
1420       msgBox.setInformativeText(i18n("Do you want to save your changes?"));
1421       msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
1422       msgBox.setDefaultButton(QMessageBox::Save);
1423       int ret = msgBox.exec();
1424 
1425       if( ret == QMessageBox::Cancel  ) {
1426           return false;
1427       }
1428   }
1429   return true;
1430 
1431 }
1432 
discardChanges()1433 void KraftView::discardChanges()
1434 {
1435   // We need to reread the document
1436   KraftDoc *doc = getDocument();
1437   if( doc && doc->isModified() ) {
1438     // qDebug () << "Document refetch from database" << endl;
1439     doc->reloadDocument();
1440   }
1441 }
1442 
1443