mirror of
https://bitbucket.org/smil3y/kde-playground.git
synced 2025-02-23 18:32:51 +00:00
481 lines
17 KiB
C++
481 lines
17 KiB
C++
/*
|
|
Copyright (C) 2010 Casey Link <unnamedrambler@gmail.com>
|
|
Copyright (c) 2009-2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
|
|
|
|
This library is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU Library General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or (at your
|
|
option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
|
|
License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to the
|
|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
02110-1301, USA.
|
|
*/
|
|
|
|
#include "attendeeline.h"
|
|
#include "attendeedata.h"
|
|
|
|
#include <KCalUtils/Stringify>
|
|
|
|
#include <KPIMUtils/Email>
|
|
|
|
#include <KCompletionBox>
|
|
#include <KDebug>
|
|
#include <KDialog>
|
|
#include <KLocalizedString>
|
|
|
|
#include <QBoxLayout>
|
|
#include <QKeyEvent>
|
|
#include <QMenu>
|
|
|
|
using namespace IncidenceEditorNG;
|
|
|
|
typedef QPair<QString, QIcon> TextIconPair;
|
|
|
|
AttendeeComboBox::AttendeeComboBox( QWidget *parent )
|
|
: QToolButton( parent ), mMenu( new QMenu( this ) ), mCurrentIndex( -1 )
|
|
{
|
|
setPopupMode( QToolButton::InstantPopup );
|
|
setToolButtonStyle( Qt::ToolButtonIconOnly );
|
|
setMenu( mMenu );
|
|
}
|
|
|
|
void AttendeeComboBox::addItem( const QIcon &icon, const QString &text )
|
|
{
|
|
mList.append( TextIconPair( text, icon ) );
|
|
if ( mCurrentIndex == -1 ) {
|
|
setCurrentIndex( 0 );
|
|
}
|
|
int index = mList.size() - 1;
|
|
QAction *act = menu()->addAction( icon, text, this, SLOT(slotActionTriggered()) );
|
|
act->setData( index );
|
|
}
|
|
|
|
void AttendeeComboBox::addItems( const QStringList &texts )
|
|
{
|
|
foreach ( const QString &str, texts ) {
|
|
addItem( QIcon(), str );
|
|
}
|
|
if ( mCurrentIndex == -1 ) {
|
|
setCurrentIndex( 0 );
|
|
}
|
|
}
|
|
|
|
int AttendeeComboBox::currentIndex() const
|
|
{
|
|
return mCurrentIndex;
|
|
}
|
|
|
|
void AttendeeComboBox::clear()
|
|
{
|
|
mCurrentIndex = -1;
|
|
mMenu->clear();
|
|
mList.clear();
|
|
}
|
|
|
|
void AttendeeComboBox::setCurrentIndex( int index )
|
|
{
|
|
Q_ASSERT( index < mList.size() );
|
|
const int old = mCurrentIndex;
|
|
mCurrentIndex = index;
|
|
setIcon( mList.at( index ).second );
|
|
setToolTip( mList.at( index ).first );
|
|
if ( old != index ) {
|
|
emit itemChanged();
|
|
}
|
|
}
|
|
|
|
void AttendeeComboBox::slotActionTriggered()
|
|
{
|
|
int index = qobject_cast<QAction*> ( sender() )->data().toInt();
|
|
setCurrentIndex( index );
|
|
}
|
|
|
|
void AttendeeComboBox::keyPressEvent( QKeyEvent *ev )
|
|
{
|
|
if ( ev->key() == Qt::Key_Left ) {
|
|
emit leftPressed();
|
|
} else if ( ev->key() == Qt::Key_Right ) {
|
|
emit rightPressed();
|
|
} else {
|
|
QToolButton::keyPressEvent( ev );
|
|
}
|
|
}
|
|
|
|
AttendeeLineEdit::AttendeeLineEdit( QWidget *parent )
|
|
: AddresseeLineEdit( parent, true )
|
|
{
|
|
}
|
|
|
|
void AttendeeLineEdit::keyPressEvent( QKeyEvent *ev )
|
|
{
|
|
if ( ( ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return ) &&
|
|
!completionBox()->isVisible() ) {
|
|
emit downPressed();
|
|
KPIM::AddresseeLineEdit::keyPressEvent( ev );
|
|
} else if ( ev->key() == Qt::Key_Backspace && text().isEmpty() ) {
|
|
ev->accept();
|
|
emit deleteMe();
|
|
} else if ( ev->key() == Qt::Key_Left && cursorPosition() == 0 &&
|
|
!ev->modifiers().testFlag( Qt::ShiftModifier ) ) {
|
|
// Shift would be pressed during selection
|
|
emit leftPressed();
|
|
} else if ( ev->key() == Qt::Key_Right && cursorPosition() == (int)text().length() &&
|
|
!ev->modifiers().testFlag( Qt::ShiftModifier ) ) {
|
|
// Shift would be pressed during selection
|
|
emit rightPressed();
|
|
} else if ( ev->key() == Qt::Key_Down ) {
|
|
emit downPressed();
|
|
} else if ( ev->key() == Qt::Key_Up ) {
|
|
emit upPressed();
|
|
} else {
|
|
KPIM::AddresseeLineEdit::keyPressEvent( ev );
|
|
}
|
|
}
|
|
|
|
AttendeeLine::AttendeeLine( QWidget *parent )
|
|
: MultiplyingLine( parent ),
|
|
mRoleCombo( new AttendeeComboBox( this ) ),
|
|
mStateCombo( new AttendeeComboBox( this ) ),
|
|
mResponseCombo( new AttendeeComboBox( this ) ),
|
|
mEdit( new AttendeeLineEdit( this ) ),
|
|
mData( new AttendeeData( QString(), QString() ) ),
|
|
mModified( false )
|
|
{
|
|
setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
|
|
|
|
QBoxLayout *topLayout = new QHBoxLayout( this );
|
|
topLayout->setSpacing( KDialog::spacingHint() );
|
|
topLayout->setMargin( 0 );
|
|
#ifdef KDEPIM_MOBILE_UI
|
|
mRoleCombo->addItem( DesktopIcon( "meeting-participant", 48 ),
|
|
KCalUtils::Stringify::attendeeRole( KCalCore::Attendee::ReqParticipant ) );
|
|
mRoleCombo->addItem( DesktopIcon( "meeting-participant-optional", 48 ),
|
|
KCalUtils::Stringify::attendeeRole( KCalCore::Attendee::OptParticipant ) );
|
|
mRoleCombo->addItem( DesktopIcon( "meeting-observer", 48 ),
|
|
KCalUtils::Stringify::attendeeRole( KCalCore::Attendee::NonParticipant ) );
|
|
mRoleCombo->addItem( DesktopIcon( "meeting-chair", 48 ),
|
|
KCalUtils::Stringify::attendeeRole( KCalCore::Attendee::Chair ) );
|
|
|
|
mResponseCombo->addItem( DesktopIcon( "meeting-participant-request-response", 48 ),
|
|
i18nc( "@item:inlistbox", "Request Response" ) );
|
|
mResponseCombo->addItem( DesktopIcon( "meeting-participant-no-response", 48 ),
|
|
i18nc( "@item:inlistbox", "Request No Response" ) );
|
|
#else
|
|
mRoleCombo->addItem( SmallIcon( "meeting-participant" ),
|
|
KCalUtils::Stringify::attendeeRole( KCalCore::Attendee::ReqParticipant ) );
|
|
mRoleCombo->addItem( SmallIcon( "meeting-participant-optional" ),
|
|
KCalUtils::Stringify::attendeeRole( KCalCore::Attendee::OptParticipant ) );
|
|
mRoleCombo->addItem( SmallIcon( "meeting-observer" ),
|
|
KCalUtils::Stringify::attendeeRole( KCalCore::Attendee::NonParticipant ) );
|
|
mRoleCombo->addItem( SmallIcon( "meeting-chair" ),
|
|
KCalUtils::Stringify::attendeeRole( KCalCore::Attendee::Chair ) );
|
|
|
|
mResponseCombo->addItem( SmallIcon( "meeting-participant-request-response" ),
|
|
i18nc( "@item:inlistbox", "Request Response" ) );
|
|
mResponseCombo->addItem( SmallIcon( "meeting-participant-no-response" ),
|
|
i18nc( "@item:inlistbox", "Request No Response" ) );
|
|
#endif
|
|
|
|
mEdit->setToolTip( i18nc( "@info:tooltip",
|
|
"Enter the name or email address of the attendee." ) );
|
|
mEdit->setClearButtonShown( true );
|
|
|
|
mStateCombo->setWhatsThis( i18nc( "@info:whatsthis",
|
|
"Edits the current attendance status of the attendee." ) );
|
|
|
|
mRoleCombo->setWhatsThis( i18nc( "@info:whatsthis",
|
|
"Edits the role of the attendee." ) );
|
|
|
|
mEdit->setWhatsThis( i18nc( "@info:whatsthis",
|
|
"The email address or name of the attendee. An invitation "
|
|
"can be sent to the user if an email address is provided." ) );
|
|
|
|
setActions( EventActions );
|
|
|
|
mResponseCombo->setToolTip( i18nc( "@info:tooltip", "Request a response from the attendee" ) );
|
|
mResponseCombo->setWhatsThis( i18nc( "@info:whatsthis",
|
|
"Edits whether to send an email to the "
|
|
"attendee to request a response concerning "
|
|
"attendance." ) );
|
|
|
|
// add them to the layout in the correct order
|
|
topLayout->addWidget( mRoleCombo );
|
|
topLayout->addWidget( mEdit );
|
|
topLayout->addWidget( mStateCombo );
|
|
topLayout->addWidget( mResponseCombo );
|
|
|
|
connect( mEdit, SIGNAL(returnPressed()), SLOT(slotReturnPressed()) );
|
|
connect( mEdit, SIGNAL(deleteMe()),
|
|
SLOT(slotPropagateDeletion()), Qt::QueuedConnection );
|
|
connect( mEdit, SIGNAL(textChanged(QString)),
|
|
SLOT(slotTextChanged(QString)), Qt::QueuedConnection );
|
|
connect( mEdit, SIGNAL(upPressed()), SLOT(slotFocusUp()) );
|
|
connect( mEdit, SIGNAL(downPressed()), SLOT(slotFocusDown()) );
|
|
|
|
connect( mRoleCombo, SIGNAL(rightPressed()), mEdit, SLOT(setFocus()) );
|
|
connect( mEdit, SIGNAL(leftPressed()), mRoleCombo, SLOT(setFocus()) );
|
|
|
|
connect( mEdit, SIGNAL(rightPressed()), mStateCombo, SLOT(setFocus()) );
|
|
connect( mStateCombo, SIGNAL(leftPressed()), mEdit, SLOT(setFocus()) );
|
|
|
|
connect( mStateCombo, SIGNAL(rightPressed()), mResponseCombo, SLOT(setFocus()) );
|
|
|
|
connect( mResponseCombo, SIGNAL(leftPressed()), mStateCombo, SLOT(setFocus()) );
|
|
connect( mResponseCombo, SIGNAL(rightPressed()), SIGNAL(rightPressed()) );
|
|
|
|
connect( mEdit, SIGNAL(editingFinished()),
|
|
SLOT(slotHandleChange()), Qt::QueuedConnection );
|
|
connect( mEdit, SIGNAL(textCompleted()),
|
|
SLOT(slotHandleChange()), Qt::QueuedConnection );
|
|
connect( mEdit, SIGNAL(clearButtonClicked()),
|
|
SLOT(slotPropagateDeletion()), Qt::QueuedConnection );
|
|
|
|
connect( mRoleCombo, SIGNAL(itemChanged()), this, SLOT(slotComboChanged()) );
|
|
connect( mStateCombo, SIGNAL(itemChanged()), this, SLOT(slotComboChanged()) );
|
|
connect( mResponseCombo, SIGNAL(itemChanged()), this, SLOT(slotComboChanged()) );
|
|
|
|
}
|
|
|
|
void AttendeeLine::activate()
|
|
{
|
|
mEdit->setFocus();
|
|
}
|
|
|
|
void AttendeeLine::clear()
|
|
{
|
|
mEdit->clear();
|
|
mRoleCombo->setCurrentIndex( 0 );
|
|
mStateCombo->setCurrentIndex( 0 );
|
|
mResponseCombo->setCurrentIndex( 0 );
|
|
mUid.clear();
|
|
}
|
|
|
|
void AttendeeLine::clearModified()
|
|
{
|
|
mModified = false;
|
|
mEdit->setModified( false );
|
|
}
|
|
|
|
KPIM::MultiplyingLineData::Ptr AttendeeLine::data() const
|
|
{
|
|
if ( isModified() ) {
|
|
const_cast<AttendeeLine*>( this )->dataFromFields();
|
|
}
|
|
return mData;
|
|
}
|
|
|
|
void AttendeeLine::dataFromFields()
|
|
{
|
|
if ( !mData ) {
|
|
return;
|
|
}
|
|
|
|
KCalCore::Attendee::Ptr oldAttendee( mData->attendee() );
|
|
|
|
QString email, name;
|
|
KPIMUtils::extractEmailAddressAndName( mEdit->text(), email, name );
|
|
|
|
mData->setName( name );
|
|
mData->setEmail( email );
|
|
|
|
mData->setRole( AttendeeData::Role( mRoleCombo->currentIndex() ) );
|
|
mData->setStatus( AttendeeData::PartStat( mStateCombo->currentIndex() ) );
|
|
mData->setRSVP( mResponseCombo->currentIndex() == 0 );
|
|
mData->setUid( mUid );
|
|
|
|
clearModified();
|
|
if ( !( oldAttendee == mData->attendee() ) && !email.isEmpty() ) {
|
|
// if email is empty, we don't want to update anything
|
|
kDebug() << oldAttendee->email() << mData->email();
|
|
emit changed( oldAttendee, mData->attendee() );
|
|
}
|
|
}
|
|
|
|
void AttendeeLine::fieldsFromData()
|
|
{
|
|
if( !mData ) {
|
|
return;
|
|
}
|
|
|
|
mEdit->setText( mData->fullName() );
|
|
mRoleCombo->setCurrentIndex( mData->role() );
|
|
AttendeeData::PartStat partStat = mData->status();
|
|
if ( partStat != AttendeeData::None ) {
|
|
mStateCombo->setCurrentIndex( partStat );
|
|
} else {
|
|
mStateCombo->setCurrentIndex( AttendeeData::NeedsAction );
|
|
}
|
|
mResponseCombo->setCurrentIndex( mData->RSVP() ? 0 : 1 );
|
|
mUid = mData->uid();
|
|
}
|
|
|
|
void AttendeeLine::fixTabOrder( QWidget *previous )
|
|
{
|
|
setTabOrder( previous, mRoleCombo );
|
|
setTabOrder( mRoleCombo, mEdit );
|
|
setTabOrder( mEdit, mStateCombo );
|
|
setTabOrder( mStateCombo, mResponseCombo );
|
|
}
|
|
|
|
QWidget *AttendeeLine::tabOut() const
|
|
{
|
|
return mResponseCombo;
|
|
}
|
|
|
|
bool AttendeeLine::isActive() const
|
|
{
|
|
return mEdit->hasFocus();
|
|
}
|
|
|
|
bool AttendeeLine::isEmpty() const
|
|
{
|
|
return mEdit->text().isEmpty();
|
|
}
|
|
|
|
bool AttendeeLine::isModified() const
|
|
{
|
|
return mModified || mEdit->isModified();
|
|
}
|
|
|
|
void AttendeeLine::moveCompletionPopup()
|
|
{
|
|
if ( mEdit->completionBox( false ) ) {
|
|
if ( mEdit->completionBox()->isVisible() ) {
|
|
// ### trigger moving, is there a nicer way to do that?
|
|
mEdit->completionBox()->hide();
|
|
mEdit->completionBox()->show();
|
|
}
|
|
}
|
|
}
|
|
|
|
int AttendeeLine::setColumnWidth( int w )
|
|
{
|
|
w = qMax( w, mRoleCombo->sizeHint().width() );
|
|
mRoleCombo->setFixedWidth( w );
|
|
mRoleCombo->updateGeometry();
|
|
parentWidget()->updateGeometry();
|
|
return w;
|
|
}
|
|
|
|
void AttendeeLine::setActions( AttendeeActions actions )
|
|
{
|
|
mStateCombo->clear();
|
|
if ( actions == EventActions ) {
|
|
#ifdef KDEPIM_MOBILE_UI
|
|
mStateCombo->addItem( DesktopIcon( "task-attention", 48 ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::NeedsAction ) );
|
|
mStateCombo->addItem( DesktopIcon( "task-accepted", 48 ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::Accepted ) );
|
|
mStateCombo->addItem( DesktopIcon( "task-reject", 48 ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::Declined ) );
|
|
mStateCombo->addItem( DesktopIcon( "task-attempt", 48 ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::Tentative ) );
|
|
mStateCombo->addItem( DesktopIcon( "task-delegate", 48 ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::Delegated ) );
|
|
#else
|
|
mStateCombo->addItem( SmallIcon( "task-attention" ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::NeedsAction ) );
|
|
mStateCombo->addItem( SmallIcon( "task-accepted" ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::Accepted ) );
|
|
mStateCombo->addItem( SmallIcon( "task-reject" ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::Declined ) );
|
|
mStateCombo->addItem( SmallIcon( "task-attempt" ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::Tentative ) );
|
|
mStateCombo->addItem( SmallIcon( "task-delegate" ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::Delegated ) );
|
|
#endif
|
|
} else {
|
|
#ifdef KDEPIM_MOBILE_UI
|
|
mStateCombo->addItem( DesktopIcon( "task-attention", 48 ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::NeedsAction ) );
|
|
mStateCombo->addItem( DesktopIcon( "task-accepted", 48 ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::Accepted ) );
|
|
mStateCombo->addItem( DesktopIcon( "task-reject", 48 ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::Declined ) );
|
|
mStateCombo->addItem( DesktopIcon( "task-attempt", 48 ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::Tentative ) );
|
|
mStateCombo->addItem( DesktopIcon( "task-delegate", 48 ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::Delegated ) );
|
|
mStateCombo->addItem( DesktopIcon( "task-complete", 48 ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::Completed ) );
|
|
mStateCombo->addItem( DesktopIcon( "task-ongoing", 48 ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::InProcess ) );
|
|
#else
|
|
mStateCombo->addItem( SmallIcon( "task-attention" ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::NeedsAction ) );
|
|
mStateCombo->addItem( SmallIcon( "task-accepted" ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::Accepted ) );
|
|
mStateCombo->addItem( SmallIcon( "task-reject" ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::Declined ) );
|
|
mStateCombo->addItem( SmallIcon( "task-attempt" ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::Tentative ) );
|
|
mStateCombo->addItem( SmallIcon( "task-delegate" ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::Delegated ) );
|
|
mStateCombo->addItem( SmallIcon( "task-complete" ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::Completed ) );
|
|
mStateCombo->addItem( SmallIcon( "task-ongoing" ),
|
|
KCalUtils::Stringify::attendeeStatus( AttendeeData::InProcess ) );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void AttendeeLine::setCompletionMode( KGlobalSettings::Completion mode )
|
|
{
|
|
mEdit->setCompletionMode( mode );
|
|
}
|
|
|
|
void AttendeeLine::setData( const KPIM::MultiplyingLineData::Ptr &data )
|
|
{
|
|
AttendeeData::Ptr attendee = qSharedPointerDynamicCast<AttendeeData>( data );
|
|
if ( !attendee ) {
|
|
return;
|
|
}
|
|
mData = attendee;
|
|
fieldsFromData();
|
|
}
|
|
|
|
void AttendeeLine::slotHandleChange()
|
|
{
|
|
if ( mEdit->text().isEmpty() ) {
|
|
emit deleteLine( this );
|
|
} else {
|
|
// has bad side-effects, and I have no idea what this was supposed to be doing
|
|
// mEdit->setCursorPosition( 0 );
|
|
emit editingFinished( this );
|
|
dataFromFields();
|
|
}
|
|
}
|
|
|
|
void AttendeeLine::slotTextChanged( const QString &str )
|
|
{
|
|
Q_UNUSED( str );
|
|
mModified = true;
|
|
emit changed(); // TODO: This doesn't seem connected to anywhere in incidenceattendee.cpp.
|
|
// but the important code is run in slotHandleChange() anyway so we don't see any bug
|
|
}
|
|
|
|
void AttendeeLine::slotComboChanged()
|
|
{
|
|
mModified = true;
|
|
// If mUid is empty, we're still populating the widget, don't write fields to data yet
|
|
if ( !mUid.isEmpty() )
|
|
dataFromFields();
|
|
}
|
|
|
|
void AttendeeLine::aboutToBeDeleted()
|
|
{
|
|
if ( !mData ) {
|
|
return;
|
|
}
|
|
|
|
emit changed( mData->attendee(), KCalCore::Attendee::Ptr( new KCalCore::Attendee( "", "" ) ) );
|
|
}
|
|
|
|
|