You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
582 lines
16 KiB
582 lines
16 KiB
15 years ago
|
/*
|
||
|
This file is part of libkdepim.
|
||
|
|
||
|
Copyright (c) 2004 Tobias Koenig <tokoe@kde.org>
|
||
|
|
||
|
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 <qheader.h>
|
||
|
#include <qlabel.h>
|
||
|
#include <qlayout.h>
|
||
|
#include <qsignalmapper.h>
|
||
|
#include <qtoolbutton.h>
|
||
|
|
||
|
#include <kabc/stdaddressbook.h>
|
||
|
#include <kcombobox.h>
|
||
|
#include <kdialog.h>
|
||
|
#include <kglobal.h>
|
||
|
#include <kiconloader.h>
|
||
|
#include <klineedit.h>
|
||
|
#include <klistview.h>
|
||
|
#include <klocale.h>
|
||
|
|
||
|
#include "addresseeselector.h"
|
||
|
|
||
|
using namespace KPIM;
|
||
|
|
||
|
class AddresseeSelector::AddressBookManager
|
||
|
{
|
||
|
public:
|
||
|
QStringList titles() const;
|
||
|
|
||
|
void addResource( KABC::Resource* );
|
||
|
void addAddressBook( const QString &title, SelectionItem::List &list );
|
||
|
|
||
|
void clear();
|
||
|
bool contains( uint index, const SelectionItem& );
|
||
|
|
||
|
private:
|
||
|
struct AddressBookEntry {
|
||
|
QString title;
|
||
|
SelectionItem::List list;
|
||
|
};
|
||
|
|
||
|
QValueList<KABC::Resource*> mResources;
|
||
|
QValueList<AddressBookEntry> mAddressBooks;
|
||
|
};
|
||
|
|
||
|
QStringList AddresseeSelector::AddressBookManager::titles() const
|
||
|
{
|
||
|
QStringList titles;
|
||
|
|
||
|
// we've always an 'all' entry
|
||
|
titles.append( i18n( "All" ) );
|
||
|
|
||
|
QValueList<KABC::Resource*>::ConstIterator resIt;
|
||
|
for ( resIt = mResources.begin(); resIt != mResources.end(); ++resIt )
|
||
|
titles.append( (*resIt)->resourceName() );
|
||
|
|
||
|
QValueList<AddressBookEntry>::ConstIterator abIt;
|
||
|
for ( abIt = mAddressBooks.begin(); abIt != mAddressBooks.end(); ++abIt )
|
||
|
titles.append( (*abIt).title );
|
||
|
|
||
|
return titles;
|
||
|
}
|
||
|
|
||
|
void AddresseeSelector::AddressBookManager::addResource( KABC::Resource *resource )
|
||
|
{
|
||
|
if ( mResources.find( resource ) == mResources.end() )
|
||
|
mResources.append( resource );
|
||
|
}
|
||
|
|
||
|
void AddresseeSelector::AddressBookManager::addAddressBook( const QString &title,
|
||
|
SelectionItem::List &list )
|
||
|
{
|
||
|
AddressBookEntry entry;
|
||
|
entry.title = title;
|
||
|
entry.list = list;
|
||
|
|
||
|
|
||
|
// TODO: check for duplicates
|
||
|
mAddressBooks.append( entry );
|
||
|
}
|
||
|
|
||
|
void AddresseeSelector::AddressBookManager::clear()
|
||
|
{
|
||
|
mResources.clear();
|
||
|
mAddressBooks.clear();
|
||
|
}
|
||
|
|
||
|
bool AddresseeSelector::AddressBookManager::contains( uint index, const SelectionItem &item )
|
||
|
{
|
||
|
if ( index == 0 ) // the 'all' entry
|
||
|
return true;
|
||
|
|
||
|
if ( mResources.count() > 0 ) {
|
||
|
if ( index <= mResources.count() ) {
|
||
|
index--;
|
||
|
if ( item.addressee().resource() == mResources[ index ] )
|
||
|
return true;
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
index = index - mResources.count();
|
||
|
|
||
|
if ( mAddressBooks.count() > 0 ) {
|
||
|
if ( index <= mAddressBooks.count() ) {
|
||
|
index--;
|
||
|
AddressBookEntry entry = mAddressBooks[ index ];
|
||
|
SelectionItem::List::ConstIterator it;
|
||
|
for ( it = entry.list.begin(); it != entry.list.end(); ++it )
|
||
|
if ( (*it).addressee() == item.addressee() )
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
SelectionItem::SelectionItem( const KABC::Addressee &addressee, uint index )
|
||
|
: mAddressee( addressee ), mDistributionList( 0 ), mIndex( index )
|
||
|
{
|
||
|
mField.fill( false, 10 );
|
||
|
}
|
||
|
|
||
|
SelectionItem::SelectionItem( KABC::DistributionList *list, uint index )
|
||
|
: mDistributionList( list ), mIndex( index )
|
||
|
{
|
||
|
mField.fill( false, 10 );
|
||
|
}
|
||
|
|
||
|
SelectionItem::SelectionItem()
|
||
|
: mDistributionList( 0 ), mIndex( 0 )
|
||
|
{
|
||
|
mField.fill( false, 10 );
|
||
|
}
|
||
|
|
||
|
void SelectionItem::addToField( int index )
|
||
|
{
|
||
|
mField.setBit( index );
|
||
|
}
|
||
|
|
||
|
void SelectionItem::removeFromField( int index )
|
||
|
{
|
||
|
mField.clearBit( index );
|
||
|
}
|
||
|
|
||
|
bool SelectionItem::isInField( int index )
|
||
|
{
|
||
|
return mField.testBit( index );
|
||
|
}
|
||
|
|
||
|
KABC::Addressee SelectionItem::addressee() const
|
||
|
{
|
||
|
return mAddressee;
|
||
|
}
|
||
|
|
||
|
KABC::DistributionList* SelectionItem::distributionList() const
|
||
|
{
|
||
|
return mDistributionList;
|
||
|
}
|
||
|
|
||
|
uint SelectionItem::index() const
|
||
|
{
|
||
|
return mIndex;
|
||
|
}
|
||
|
|
||
|
|
||
|
class SelectionViewItem : public QListViewItem
|
||
|
{
|
||
|
public:
|
||
|
SelectionViewItem( QListView *parent, Selection *selection,
|
||
|
SelectionItem *item )
|
||
|
: QListViewItem( parent, "" ), mSelection( selection ), mItem( item )
|
||
|
{
|
||
|
if ( mItem->distributionList() == 0 )
|
||
|
mIcon = mSelection->itemIcon( mItem->addressee(), mItem->index() );
|
||
|
else
|
||
|
mIcon = mSelection->distributionListIcon( mItem->distributionList() );
|
||
|
}
|
||
|
|
||
|
QString text( int column ) const
|
||
|
{
|
||
|
if ( column == 0 ) {
|
||
|
if ( mItem->distributionList() == 0 )
|
||
|
return mSelection->itemText( mItem->addressee(), mItem->index() );
|
||
|
else
|
||
|
return mSelection->distributionListText( mItem->distributionList() );
|
||
|
} else
|
||
|
return QString::null;
|
||
|
}
|
||
|
|
||
|
const QPixmap* pixmap( int column ) const
|
||
|
{
|
||
|
if ( column == 0 ) {
|
||
|
return &mIcon;
|
||
|
} else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
SelectionItem* item() const { return mItem; }
|
||
|
|
||
|
private:
|
||
|
Selection *mSelection;
|
||
|
SelectionItem *mItem;
|
||
|
QPixmap mIcon;
|
||
|
};
|
||
|
|
||
|
AddresseeSelector::AddresseeSelector( Selection *selection, QWidget *parent, const char *name )
|
||
|
: QWidget( parent, name ), mSelection( selection ), mManager( 0 )
|
||
|
{
|
||
|
mMoveMapper = new QSignalMapper( this );
|
||
|
mRemoveMapper = new QSignalMapper( this );
|
||
|
|
||
|
mAddressBookManager = new AddressBookManager();
|
||
|
|
||
|
initGUI();
|
||
|
|
||
|
init();
|
||
|
|
||
|
mSelection->setSelector( this );
|
||
|
}
|
||
|
|
||
|
AddresseeSelector::~AddresseeSelector()
|
||
|
{
|
||
|
delete mManager;
|
||
|
mManager = 0;
|
||
|
|
||
|
delete mAddressBookManager;
|
||
|
mAddressBookManager = 0;
|
||
|
}
|
||
|
|
||
|
void AddresseeSelector::init()
|
||
|
{
|
||
|
connect( KABC::StdAddressBook::self( true ), SIGNAL( addressBookChanged( AddressBook* ) ),
|
||
|
this, SLOT( reloadAddressBook() ) );
|
||
|
connect( mAddresseeFilter, SIGNAL( textChanged( const QString& ) ),
|
||
|
this, SLOT( updateAddresseeView() ) );
|
||
|
connect( mAddressBookCombo, SIGNAL( activated( int ) ),
|
||
|
this, SLOT( updateAddresseeView() ) );
|
||
|
|
||
|
connect( mMoveMapper, SIGNAL( mapped( int ) ),
|
||
|
this, SLOT( move( int ) ) );
|
||
|
connect( mRemoveMapper, SIGNAL( mapped( int ) ),
|
||
|
this, SLOT( remove( int ) ) );
|
||
|
|
||
|
reloadAddressBook();
|
||
|
}
|
||
|
|
||
|
void AddresseeSelector::initGUI()
|
||
|
{
|
||
|
QGridLayout *layout = new QGridLayout( this, 2, 3, KDialog::marginHint(), KDialog::spacingHint() );
|
||
|
QGridLayout *topLayout = new QGridLayout( this, 2, 2, KDialog::marginHint() );
|
||
|
|
||
|
QLabel *label = new QLabel( i18n( "Address book:" ), this );
|
||
|
mAddressBookCombo = new KComboBox( false, this );
|
||
|
|
||
|
topLayout->addWidget( label, 0, 0 );
|
||
|
topLayout->addWidget( mAddressBookCombo, 0, 1 );
|
||
|
|
||
|
label = new QLabel( i18n( "Search:" ), this );
|
||
|
mAddresseeFilter = new KLineEdit( this );
|
||
|
|
||
|
topLayout->addWidget( label, 1, 0 );
|
||
|
topLayout->addWidget( mAddresseeFilter, 1, 1 );
|
||
|
|
||
|
topLayout->setColStretch( 1, 1 );
|
||
|
|
||
|
layout->addMultiCellLayout( topLayout, 0, 0, 0, 2 );
|
||
|
|
||
|
int row = 1;
|
||
|
|
||
|
QIconSet moveSet = KGlobal::iconLoader()->loadIconSet( "next", KIcon::Small );
|
||
|
QIconSet removeSet = KGlobal::iconLoader()->loadIconSet( "previous", KIcon::Small );
|
||
|
|
||
|
uint count = mSelection->fieldCount();
|
||
|
for ( uint i = 0; i < count; ++i, ++row ) {
|
||
|
KListView *listView = new KListView( this );
|
||
|
listView->addColumn( mSelection->fieldTitle( i ) );
|
||
|
listView->setFullWidth( true );
|
||
|
mSelectionViews.append( listView );
|
||
|
|
||
|
connect( listView, SIGNAL( doubleClicked( QListViewItem*, const QPoint&, int ) ),
|
||
|
mRemoveMapper, SLOT( map() ) );
|
||
|
mRemoveMapper->setMapping( listView, i );
|
||
|
|
||
|
QVBoxLayout *buttonLayout = new QVBoxLayout( this );
|
||
|
buttonLayout->setAlignment( Qt::AlignBottom );
|
||
|
layout->addLayout( buttonLayout, row, 1 );
|
||
|
|
||
|
// move button
|
||
|
QToolButton *moveButton = new QToolButton( this );
|
||
|
moveButton->setIconSet( moveSet );
|
||
|
moveButton->setFixedSize( 30, 30 );
|
||
|
|
||
|
connect( moveButton, SIGNAL( clicked() ),
|
||
|
mMoveMapper, SLOT( map() ) );
|
||
|
mMoveMapper->setMapping( moveButton, i );
|
||
|
|
||
|
// remove button
|
||
|
QToolButton *removeButton = new QToolButton( this );
|
||
|
removeButton->setIconSet( removeSet );
|
||
|
removeButton->setFixedSize( 30, 30 );
|
||
|
|
||
|
connect( removeButton, SIGNAL( clicked() ),
|
||
|
mRemoveMapper, SLOT( map() ) );
|
||
|
mRemoveMapper->setMapping( removeButton, i );
|
||
|
|
||
|
buttonLayout->addWidget( moveButton );
|
||
|
buttonLayout->addWidget( removeButton );
|
||
|
|
||
|
layout->addWidget( listView, row, 2 );
|
||
|
}
|
||
|
|
||
|
mAddresseeView = new KListView( this );
|
||
|
mAddresseeView->addColumn( "" );
|
||
|
mAddresseeView->header()->hide();
|
||
|
mAddresseeView->setFullWidth( true );
|
||
|
|
||
|
layout->addMultiCellWidget( mAddresseeView, 1, row, 0, 0 );
|
||
|
}
|
||
|
|
||
|
void AddresseeSelector::finish()
|
||
|
{
|
||
|
SelectionItem::List::Iterator it;
|
||
|
|
||
|
for ( uint field = 0; field < mSelection->fieldCount(); ++field ) {
|
||
|
for ( it = mSelectionItems.begin(); it != mSelectionItems.end(); ++it ) {
|
||
|
if ( (*it).isInField( field ) ) {
|
||
|
if ( (*it).distributionList() == 0 )
|
||
|
mSelection->addSelectedAddressees( field, (*it).addressee(), (*it).index() );
|
||
|
else
|
||
|
mSelection->addSelectedDistributionList( field, (*it).distributionList() );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void AddresseeSelector::updateAddresseeView()
|
||
|
{
|
||
|
mAddresseeView->clear();
|
||
|
|
||
|
int addressBookIndex = mAddressBookCombo->currentItem();
|
||
|
|
||
|
SelectionItem::List::Iterator it;
|
||
|
for ( it = mSelectionItems.begin(); it != mSelectionItems.end(); ++it ) {
|
||
|
if ( mAddressBookManager->contains( addressBookIndex, *it ) ) {
|
||
|
if ( (*it).distributionList() == 0 ) {
|
||
|
if ( mAddresseeFilter->text().isEmpty() ||
|
||
|
mSelection->itemMatches( (*it).addressee(), (*it).index(),
|
||
|
mAddresseeFilter->text() ) )
|
||
|
new SelectionViewItem( mAddresseeView, mSelection, &(*it) );
|
||
|
} else {
|
||
|
if ( mAddresseeFilter->text().isEmpty() ||
|
||
|
mSelection->distributionListMatches( (*it).distributionList(),
|
||
|
mAddresseeFilter->text() ) )
|
||
|
new SelectionViewItem( mAddresseeView, mSelection, &(*it) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
updateSelectionViews();
|
||
|
}
|
||
|
|
||
|
void AddresseeSelector::move( int index )
|
||
|
{
|
||
|
SelectionViewItem *item = dynamic_cast<SelectionViewItem*>( mAddresseeView->selectedItem() );
|
||
|
if ( item ) {
|
||
|
item->item()->addToField( index );
|
||
|
updateSelectionView( index );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void AddresseeSelector::remove( int index )
|
||
|
{
|
||
|
KListView *view = mSelectionViews[ index ];
|
||
|
|
||
|
SelectionViewItem *item = dynamic_cast<SelectionViewItem*>( view->selectedItem() );
|
||
|
if ( item ) {
|
||
|
item->item()->removeFromField( index );
|
||
|
updateSelectionView( index );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void AddresseeSelector::setItemSelected( uint fieldIndex, const KABC::Addressee &addr, uint itemIndex )
|
||
|
{
|
||
|
bool found = false;
|
||
|
|
||
|
SelectionItem::List::Iterator it;
|
||
|
for ( it = mSelectionItems.begin(); it != mSelectionItems.end(); ++it ) {
|
||
|
if ( (*it).addressee() == addr && (*it).index() == itemIndex ) {
|
||
|
(*it).addToField( fieldIndex );
|
||
|
found = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !found ) {
|
||
|
SelectionItem item( addr, itemIndex );
|
||
|
item.addToField( fieldIndex );
|
||
|
|
||
|
mSelectionItems.append( item );
|
||
|
}
|
||
|
|
||
|
updateSelectionView( fieldIndex );
|
||
|
}
|
||
|
|
||
|
void AddresseeSelector::setItemSelected( uint fieldIndex, const KABC::Addressee &addr,
|
||
|
uint itemIndex, const QString &text )
|
||
|
{
|
||
|
bool found = false;
|
||
|
|
||
|
SelectionItem::List::Iterator it;
|
||
|
for ( it = mSelectionItems.begin(); it != mSelectionItems.end(); ++it ) {
|
||
|
if ( mSelection->itemEquals( (*it).addressee(), (*it).index(), text ) ) {
|
||
|
(*it).addToField( fieldIndex );
|
||
|
found = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !found ) {
|
||
|
SelectionItem item( addr, itemIndex );
|
||
|
item.addToField( fieldIndex );
|
||
|
|
||
|
mSelectionItems.append( item );
|
||
|
}
|
||
|
|
||
|
updateSelectionView( fieldIndex );
|
||
|
}
|
||
|
|
||
|
void AddresseeSelector::updateSelectionView( int index )
|
||
|
{
|
||
|
KListView *view = mSelectionViews[ index ];
|
||
|
view->clear();
|
||
|
|
||
|
SelectionItem::List::Iterator it;
|
||
|
for ( it = mSelectionItems.begin(); it != mSelectionItems.end(); ++it ) {
|
||
|
if ( (*it).isInField( index ) )
|
||
|
new SelectionViewItem( view, mSelection, &(*it) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void AddresseeSelector::updateSelectionViews()
|
||
|
{
|
||
|
for ( uint i = 0; i < mSelection->fieldCount(); ++i )
|
||
|
updateSelectionView( i );
|
||
|
}
|
||
|
|
||
|
void AddresseeSelector::reloadAddressBook()
|
||
|
{
|
||
|
// load contacts
|
||
|
KABC::Addressee::List list = KABC::StdAddressBook::self( true )->allAddressees();
|
||
|
KABC::Addressee::List::Iterator it;
|
||
|
|
||
|
SelectionItem::List selectedItems;
|
||
|
|
||
|
SelectionItem::List::Iterator itemIt;
|
||
|
for ( itemIt = mSelectionItems.begin(); itemIt != mSelectionItems.end(); ++itemIt ) {
|
||
|
bool isSelected = false;
|
||
|
for ( uint i = 0; i < mSelection->fieldCount(); ++i ) {
|
||
|
if ( (*itemIt).isInField( i ) ) {
|
||
|
isSelected = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// we don't save distribution lists, since this leads to crashes
|
||
|
if ( isSelected && (*itemIt).distributionList() == 0 ) {
|
||
|
selectedItems.append( *itemIt );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mSelectionItems.clear();
|
||
|
mSelectionItems = selectedItems;
|
||
|
|
||
|
for ( it = list.begin(); it != list.end(); ++it ) {
|
||
|
uint itemCount = mSelection->itemCount( *it );
|
||
|
for ( uint index = 0; index < itemCount; ++index ) {
|
||
|
bool available = false;
|
||
|
for ( itemIt = mSelectionItems.begin(); itemIt != mSelectionItems.end(); ++itemIt ) {
|
||
|
if ( (*itemIt).addressee() == (*it) && (*itemIt).index() == index ) {
|
||
|
available = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !available ) {
|
||
|
SelectionItem item( *it, index );
|
||
|
mSelectionItems.append( item );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// load distribution lists
|
||
|
delete mManager;
|
||
|
mManager = new KABC::DistributionListManager( KABC::StdAddressBook::self( true ) );
|
||
|
|
||
|
mManager->load();
|
||
|
|
||
|
QStringList lists = mManager->listNames();
|
||
|
|
||
|
QStringList::Iterator listIt;
|
||
|
for ( listIt = lists.begin(); listIt != lists.end(); ++listIt ) {
|
||
|
KABC::DistributionList *list = mManager->list( *listIt );
|
||
|
SelectionItem item( list, 0 );
|
||
|
mSelectionItems.append( item );
|
||
|
}
|
||
|
|
||
|
mAddressBookManager->clear();
|
||
|
|
||
|
// update address book combo
|
||
|
mAddressBookCombo->clear();
|
||
|
|
||
|
QPtrList<KABC::Resource> resources = KABC::StdAddressBook::self( true )->resources();
|
||
|
QPtrListIterator<KABC::Resource> resIt( resources );
|
||
|
while ( resIt.current() ) {
|
||
|
if ( resIt.current()->isActive() )
|
||
|
mAddressBookManager->addResource( resIt );
|
||
|
|
||
|
++resIt;
|
||
|
}
|
||
|
|
||
|
for ( uint i = 0; i < mSelection->addressBookCount(); ++i ) {
|
||
|
SelectionItem::List itemList;
|
||
|
|
||
|
KABC::Addressee::List addrList = mSelection->addressBookContent( i );
|
||
|
for ( it = addrList.begin(); it != addrList.end(); ++it ) {
|
||
|
uint itemCount = mSelection->itemCount( *it );
|
||
|
for ( uint index = 0; index < itemCount; ++index ) {
|
||
|
SelectionItem item( *it, index );
|
||
|
mSelectionItems.append( item );
|
||
|
itemList.append( item );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mAddressBookManager->addAddressBook( mSelection->addressBookTitle( i ),
|
||
|
itemList );
|
||
|
}
|
||
|
|
||
|
mAddressBookCombo->insertStringList( mAddressBookManager->titles() );
|
||
|
|
||
|
updateAddresseeView();
|
||
|
}
|
||
|
|
||
|
|
||
|
AddresseeSelectorDialog::AddresseeSelectorDialog( Selection *selection,
|
||
|
QWidget *parent, const char *name )
|
||
|
: KDialogBase( Plain, "", Ok | Cancel, Ok, parent, name, true )
|
||
|
{
|
||
|
QFrame *frame = plainPage();
|
||
|
QVBoxLayout *layout = new QVBoxLayout( frame );
|
||
|
mSelector = new KPIM::AddresseeSelector( selection, frame );
|
||
|
layout->addWidget( mSelector );
|
||
|
|
||
|
resize( 500, 490 );
|
||
|
}
|
||
|
|
||
|
void AddresseeSelectorDialog::accept()
|
||
|
{
|
||
|
mSelector->finish();
|
||
|
QDialog::accept();
|
||
|
}
|
||
|
|
||
|
#include "addresseeselector.moc"
|