author | eilers <eilers> | 2002-10-21 16:29:20 (UTC) |
---|---|---|
committer | eilers <eilers> | 2002-10-21 16:29:20 (UTC) |
commit | 507afe645a86191815a2f85380a452ab6797e383 (patch) (side-by-side diff) | |
tree | e69293f25af3d6d8e2125b0f92a097615ddb0a38 | |
parent | 71c7800e8ae5dc2d701242828ceb8c11bcd96fbe (diff) | |
download | opie-507afe645a86191815a2f85380a452ab6797e383.zip opie-507afe645a86191815a2f85380a452ab6797e383.tar.gz opie-507afe645a86191815a2f85380a452ab6797e383.tar.bz2 |
Some usability updates in picker and find..
-rw-r--r-- | core/pim/addressbook/TODO | 5 | ||||
-rw-r--r-- | core/pim/addressbook/ablabel.cpp | 39 | ||||
-rw-r--r-- | core/pim/addressbook/abtable.cpp | 27 | ||||
-rw-r--r-- | core/pim/addressbook/abtable.h | 7 | ||||
-rw-r--r-- | core/pim/addressbook/addressbook.cpp | 66 | ||||
-rw-r--r-- | core/pim/addressbook/addressbook.h | 7 | ||||
-rw-r--r-- | core/pim/addressbook/picker.cpp | 24 | ||||
-rw-r--r-- | core/pim/addressbook/picker.h | 3 |
8 files changed, 150 insertions, 28 deletions
diff --git a/core/pim/addressbook/TODO b/core/pim/addressbook/TODO index 4daa2a8..796dc49 100644 --- a/core/pim/addressbook/TODO +++ b/core/pim/addressbook/TODO @@ -1,33 +1,36 @@ Stuff todo until OPIE 1.0 : Urgent: - Font menu is invisible using german translation Important: -- Picker: Activated letter schould be more visible +- Cursor keys should work in detail-view (ablabel) - "What's this" should be added - Store last settings of combo-boxes - Mail-Icon is missing +- Overview window cleanup needed.. - Finishing of new View functions (List, Phonebook...) - The names of the countries are sorted by there english names, only.. Even if they are translated.. :S - Reload if contacts were changed externally Less important: - The picker (alphabetical sort widget) should be placed verticaly or horizontally (configurable) - Use advanced database functions in abtable to decrease memory footprint and to make everything more easy ! (abtable should store Iterator for selected Category) Should be Fixed (not absolute sure, need further validation): - "Nonenglish" translation bug has to be fixed. Fixed: - Syncing: abtable not reloaded after sync. - Find widget should be replaced by something like qpdf has. - Adding a configuration dialog
\ No newline at end of file +- Picker: Activated letter schould be more visible +- Advanced handling of cursor keys (search..) diff --git a/core/pim/addressbook/ablabel.cpp b/core/pim/addressbook/ablabel.cpp index cf1e39f..ea80700 100644 --- a/core/pim/addressbook/ablabel.cpp +++ b/core/pim/addressbook/ablabel.cpp @@ -1,53 +1,90 @@ /********************************************************************** ** Copyright (C) 2000 Trolltech AS. All rights reserved. ** ** This file is part of Qt Palmtop Environment. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #include "ablabel.h" #include <qpe/stringutil.h> #include <qregexp.h> #include <qstylesheet.h> AbLabel::AbLabel( QWidget *parent, const char *name ) : QTextView( parent, name ) { } AbLabel::~AbLabel() { } void AbLabel::init( const OContact &entry ) { ent = entry; } void AbLabel::sync() { QString text = ent.toRichText(); setText( text ); } void AbLabel::keyPressEvent( QKeyEvent *e ) { - if ( e->key() == Qt::Key_F33 ) { + // Commonly handled keys + switch( e->key() ) { + case Qt::Key_Left: + qWarning( "Left.."); + case Qt::Key_F33: + qWarning( "OK.."); emit okPressed(); + break; + } + + + if ( /* m_inSearch */ false ) { + // Running in seach-mode, therefore we will interprete + // some key differently + qWarning("Received key in search mode"); + switch( e->key() ) { + case Qt::Key_Up: + qWarning("a"); + // emit signalSearchBackward(); + break; + case Qt::Key_Down: + qWarning("b"); + // emit signalSearchNext(); + break; + } + + } else { + qWarning("Received key in NON search mode"); + + switch( e->key() ) { + case Qt::Key_Up: + qWarning("a"); + // emit signalSearchBackward(); + break; + case Qt::Key_Down: + qWarning("b"); + // emit signalSearchNext(); + break; + } } } diff --git a/core/pim/addressbook/abtable.cpp b/core/pim/addressbook/abtable.cpp index d4dcf7b..97b26db 100644 --- a/core/pim/addressbook/abtable.cpp +++ b/core/pim/addressbook/abtable.cpp @@ -1,1016 +1,1043 @@ /********************************************************************** ** Copyright (C) 2000 Trolltech AS. All rights reserved. ** ** This file is part of Qt Palmtop Environment. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #define QTOPIA_INTERNAL_CONTACT_MRE #include <qpe/categoryselect.h> #include <qpe/config.h> #include <qpe/stringutil.h> #include <qpe/qcopenvelope_qws.h> #include <opie/orecordlist.h> #include <qasciidict.h> #include <qdatetime.h> #include <qfile.h> #include <qregexp.h> #include <qmessagebox.h> #include "abtable.h" #include <errno.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <ctype.h> //toupper() for key hack static bool contactCompare( const OContact &cnt, const QRegExp &r, int category ); /*! \class AbTableItem abtable.h \brief QTableItem based class for showing a field of an entry */ AbTableItem::AbTableItem( QTable *t, EditType et, const QString &s, const QString &secondSortKey) : QTableItem( t, et, s ) { // sortKey = s.lower() + QChar( '\0' ) + secondSortKey.lower(); sortKey = Qtopia::buildSortKey( s, secondSortKey ); } int AbTableItem::alignment() const { return AlignLeft|AlignVCenter; } QString AbTableItem::key() const { return sortKey; } // A way to reset the item, without out doing a delete or a new... void AbTableItem::setItem( const QString &txt, const QString &secondKey ) { setText( txt ); sortKey = Qtopia::buildSortKey( txt, secondKey ); // sortKey = txt.lower() + QChar( '\0' ) + secondKey.lower(); } /*! \class AbPickItem abtable.h \brief QTableItem based class for showing slection of an entry */ AbPickItem::AbPickItem( QTable *t ) : QTableItem(t, WhenCurrent, "?") { } QWidget *AbPickItem::createEditor() const { QComboBox* combo = new QComboBox( table()->viewport() ); ( (AbPickItem*)this )->cb = combo; AbTable* t = static_cast<AbTable*>(table()); QStringList c = t->choiceNames(); int cur = 0; for (QStringList::ConstIterator it = c.begin(); it!=c.end(); ++it) { if ( *it == text() ) cur = combo->count(); combo->insertItem(*it); } combo->setCurrentItem(cur); return combo; } void AbPickItem::setContentFromEditor( QWidget *w ) { if ( w->inherits("QComboBox") ) setText( ( (QComboBox*)w )->currentText() ); else QTableItem::setContentFromEditor( w ); } /*! \class AbTable abtable.h \brief QTable based class for showing a list of entries */ AbTable::AbTable( const QValueList<int> *order, QWidget *parent, const char *name ) // #ifdef QT_QTABLE_NOHEADER_CONSTRUCTOR // : QTable( 0, 0, parent, name, TRUE ), // #else : QTable( parent, name ), // #endif lastSortCol( -1 ), asc( TRUE ), intFields( order ), currFindRow( -1 ), mCat( 0 ), + m_inSearch (false), m_contactdb ("addressbook", 0l, 0l, false) // Handle syncing myself.. ! { mCat.load( categoryFileName() ); setSelectionMode( NoSelection ); init(); setSorting( TRUE ); connect( this, SIGNAL(clicked(int,int,int,const QPoint &)), this, SLOT(itemClicked(int,int)) ); } AbTable::~AbTable() { } void AbTable::init() { showChar = '\0'; setNumRows( 0 ); setNumCols( 2 ); horizontalHeader()->setLabel( 0, tr( "Full Name" )); horizontalHeader()->setLabel( 1, tr( "Contact" )); setLeftMargin( 0 ); verticalHeader()->hide(); columnVisible = true; } void AbTable::columnClicked( int col ) { if ( !sorting() ) return; if ( lastSortCol == -1 ) lastSortCol = col; if ( col == lastSortCol ) { asc = !asc; } else { lastSortCol = col; asc = TRUE; } //QMessageBox::information( this, "resort", "columnClicked" ); resort(); } void AbTable::resort() { if ( sorting() ) { if ( lastSortCol == -1 ) lastSortCol = 0; sortColumn( lastSortCol, asc, TRUE ); //QMessageBox::information( this, "resort", "resort" ); updateVisible(); } } OContact AbTable::currentEntry() { OContact cnt; AbTableItem *abItem; abItem = static_cast<AbTableItem*>(item( currentRow(), 0 )); if ( abItem ) { cnt = contactList[abItem]; //cnt = contactList[currentRow()]; } return cnt; } void AbTable::replaceCurrentEntry( const OContact &newContact ) { int row = currentRow(); updateVisible(); journalFreeReplace( newContact, row ); } void AbTable::deleteCurrentEntry() { int row = currentRow(); // a little wasteful, but it ensure's there is only one place // where we delete. journalFreeRemove( row ); updateVisible(); if ( numRows() == 0 ) emit empty( TRUE ); } void AbTable::clear() { contactList.clear(); for ( int r = 0; r < numRows(); ++r ) { for ( int c = 0; c < numCols(); ++c ) { if ( cellWidget( r, c ) ) clearCellWidget( r, c ); clearCell( r, c ); } } setNumRows( 0 ); } void AbTable::refresh() { int rows = numRows(); QString value; AbTableItem *abi; // hide columns so no flashing ? if ( showBk == "Cards" ) { hideColumn(0); hideColumn(1); } for ( int r = 0; r < rows; ++r ) { abi = static_cast<AbTableItem*>( item(r, 0) ); value = findContactContact( contactList[abi], r ); static_cast<AbTableItem*>( item(r, 1) )->setItem( value, abi->text() ); } resort(); } void AbTable::keyPressEvent( QKeyEvent *e ) { char key = toupper( e->ascii() ); if ( key >= 'A' && key <= 'Z' ) moveTo( key ); + if ( m_inSearch ) { + // Running in seach-mode, therefore we will interprete + // some key differently + qWarning("Received key in search mode"); switch( e->key() ) { case Qt::Key_Space: case Qt::Key_Return: case Qt::Key_Enter: emit details(); break; + case Qt::Key_Up: + qWarning("a"); + emit signalSearchBackward(); + break; + case Qt::Key_Down: + qWarning("b"); + emit signalSearchNext(); + break; default: QTable::keyPressEvent( e ); } + + } else { + qWarning("Received key in NON search mode"); + + switch( e->key() ) { + case Qt::Key_Space: + case Qt::Key_Return: + case Qt::Key_Enter: + emit details(); + break; + default: + QTable::keyPressEvent( e ); + } + } } void AbTable::moveTo( char c ) { int rows = numRows(); QString value; AbTableItem *abi; int r; if ( asc ) { r = 0; while ( r < rows-1) { abi = static_cast<AbTableItem*>( item(r, 0) ); QChar first = abi->key()[0]; //### is there a bug in QChar to char comparison??? if ( first.row() || first.cell() >= c ) break; r++; } } else { //### should probably disable reverse sorting instead r = rows - 1; while ( r > 0 ) { abi = static_cast<AbTableItem*>( item(r, 0) ); QChar first = abi->key()[0]; //### is there a bug in QChar to char comparison??? if ( first.row() || first.cell() >= c ) break; r--; } } setCurrentCell( r, currentColumn() ); } QString AbTable::findContactName( const OContact &entry ) { // We use the fileAs, then company, defaultEmail QString str; str = entry.fileAs(); if ( str.isEmpty() ) { str = entry.company(); if ( str.isEmpty() ) { str = entry.defaultEmail(); } } return str; } QString AbTable::findContactContact( const OContact &entry, int /* row */ ) { QString value; value = ""; for ( QValueList<int>::ConstIterator it = intFields->begin(); it != intFields->end(); ++it ) { switch ( *it ) { default: break; case Qtopia::Title: value = entry.title(); break; case Qtopia::Suffix: value = entry.suffix(); break; case Qtopia::FileAs: value = entry.fileAs(); break; case Qtopia::DefaultEmail: value = entry.defaultEmail(); case Qtopia::Emails: value = entry.emails(); break; case Qtopia::HomeStreet: value = entry.homeStreet(); break; case Qtopia::HomeCity: value = entry.homeCity(); break; case Qtopia::HomeState: value = entry.homeState(); break; case Qtopia::HomeZip: value = entry.homeZip(); break; case Qtopia::HomeCountry: value = entry.homeCountry(); break; case Qtopia::HomePhone: value = entry.homePhone(); break; case Qtopia::HomeFax: value = entry.homeFax(); break; case Qtopia::HomeMobile: value = entry.homeMobile(); break; case Qtopia::HomeWebPage: value = entry.homeWebpage(); break; case Qtopia::Company: value = entry.company(); break; case Qtopia::BusinessCity: value = entry.businessCity(); break; case Qtopia::BusinessStreet: value = entry.businessStreet(); break; case Qtopia::BusinessZip: value = entry.businessZip(); break; case Qtopia::BusinessCountry: value = entry.businessCountry(); break; case Qtopia::BusinessWebPage: value = entry.businessWebpage(); break; case Qtopia::JobTitle: value = entry.jobTitle(); break; case Qtopia::Department: value = entry.department(); break; case Qtopia::Office: value = entry.office(); break; case Qtopia::BusinessPhone: value = entry.businessPhone(); break; case Qtopia::BusinessFax: value = entry.businessFax(); break; case Qtopia::BusinessMobile: value = entry.businessMobile(); break; case Qtopia::BusinessPager: value = entry.businessPager(); break; case Qtopia::Profession: value = entry.profession(); break; case Qtopia::Assistant: value = entry.assistant(); break; case Qtopia::Manager: value = entry.manager(); break; case Qtopia::Spouse: value = entry.spouse(); break; case Qtopia::Gender: value = entry.gender(); break; case Qtopia::Birthday: value = entry.birthday(); break; case Qtopia::Anniversary: value = entry.anniversary(); break; case Qtopia::Nickname: value = entry.nickname(); break; case Qtopia::Children: value = entry.children(); break; case Qtopia::Notes: value = entry.notes(); break; } if ( !value.isEmpty() ) break; } return value; } void AbTable::addEntry( const OContact &newCnt ) { int row = numRows(); setNumRows( row + 1 ); insertIntoTable( newCnt, row ); qWarning("abtable:AddContact"); m_contactdb.add ( newCnt ); setCurrentCell( row, 0 ); // updateVisible(); } void AbTable::resizeRows() { /* if (numRows()) { for (int i = 0; i < numRows(); i++) { setRowHeight( i, size ); } } updateVisible(); */ } bool AbTable::save() { // QTime t; // t.start(); qWarning("abtable:Save data"); return m_contactdb.save(); } void AbTable::load() { setSorting( false ); setUpdatesEnabled( FALSE ); qWarning("abtable:Load data"); OContactAccess::List list = m_contactdb.allRecords(); OContactAccess::List::Iterator it; setNumRows( list.count() ); int row = 0; for ( it = list.begin(); it != list.end(); ++it ) insertIntoTable( *it, row++ ); setUpdatesEnabled( TRUE ); setSorting( true ); resort(); } void AbTable::reload() { m_contactdb.reload(); load(); } void AbTable::realignTable( int row ) { QTableItem *ti1, *ti2; int totalRows = numRows(); for ( int curr = row; curr < totalRows - 1; curr++ ) { // the same info from the todo list still applies, but I // don't think it is _too_ bad. ti1 = item( curr + 1, 0 ); ti2 = item( curr + 1, 1 ); takeItem( ti1 ); takeItem( ti2 ); setItem( curr, 0, ti1 ); setItem( curr, 1, ti2 ); } setNumRows( totalRows - 1 ); resort(); } // Add contact into table. void AbTable::insertIntoTable( const OContact &cnt, int row ) { QString strName, strContact; strName = findContactName( cnt ); strContact = findContactContact( cnt, row ); AbTableItem *ati; ati = new AbTableItem( this, QTableItem::Never, strName, strContact); contactList.insert( ati, cnt ); setItem( row, 0, ati ); ati = new AbTableItem( this, QTableItem::Never, strContact, strName); setItem( row, 1, ati ); //### cannot do this; table only has two columns at this point // setItem( row, 2, new AbPickItem( this ) ); // resort at some point? } // Replace or add an entry void AbTable::journalFreeReplace( const OContact &cnt, int row ) { QString strName, strContact; AbTableItem *ati = 0l; strName = findContactName( cnt ); strContact = findContactContact( cnt, row ); ati = static_cast<AbTableItem*>(item(row, 0)); // Replace element if found in row "row" // or add this element if not. if ( ati != 0 ) { // replace // :SX db access -> replace qWarning ("Replace Contact in DB ! UID: %d", contactList[ati].uid() ); m_contactdb.replace ( cnt ); contactList.remove( ati ); ati->setItem( strName, strContact ); contactList.insert( ati, cnt ); ati = static_cast<AbTableItem*>(item(row, 1)); ati->setItem( strContact, strName ); }else{ // add int myrows = numRows(); setNumRows( myrows + 1 ); insertIntoTable( cnt, myrows ); // gets deleted when returning -- Why ? (se) // :SX db access -> add qWarning ("Are you sure to add to database ? -> Currently disabled !!"); // m_contactdb.add( cnt ); } } // Remove entry void AbTable::journalFreeRemove( int row ) { AbTableItem *ati; ati = static_cast<AbTableItem*>(item(row, 0)); if ( !ati ) return; // :SX db access -> remove qWarning ("Remove Contact from DB ! UID: %d",contactList[ati].uid() ); m_contactdb.remove( contactList[ati].uid() ); contactList.remove( ati ); realignTable( row ); } #if QT_VERSION <= 230 #ifndef SINGLE_APP void QTable::paintEmptyArea( QPainter *p, int cx, int cy, int cw, int ch ) { // Region of the rect we should draw QRegion reg( QRect( cx, cy, cw, ch ) ); // Subtract the table from it reg = reg.subtract( QRect( QPoint( 0, 0 ), tableSize() ) ); // And draw the rectangles (transformed as needed) QArray<QRect> r = reg.rects(); for (unsigned int i=0; i<r.count(); i++) p->fillRect( r[i], colorGroup().brush( QColorGroup::Base ) ); } #endif #endif // int AbTable::rowHeight( int ) const // { // return 18; // } // int AbTable::rowPos( int row ) const // { // return 18*row; // } // int AbTable::rowAt( int pos ) const // { // return QMIN( pos/18, numRows()-1 ); // } void AbTable::slotDoFind( const QString &findString, bool caseSensitive, bool useRegExp, bool backwards, QString cat /* int category */ ) { int category = 0; // Use the current Category if nothing else selected if ( cat.isEmpty() ) category = mCat.id( "Contacts", showCat ); else{ category = mCat.id("Contacts", cat ); } qWarning ("Found in Category %d", category); if ( currFindRow < -1 ) currFindRow = - 1; clearSelection( TRUE ); int rows, row; AbTableItem *ati; QRegExp r( findString ); r.setCaseSensitive( caseSensitive ); r.setWildcard( !useRegExp ); rows = numRows(); static bool wrapAround = true; if ( !backwards ) { for ( row = currFindRow + 1; row < rows; row++ ) { ati = static_cast<AbTableItem*>( item(row, 0) ); if ( contactCompare( contactList[ati], r, category ) ) break; } } else { for ( row = currFindRow - 1; row > -1; row-- ) { ati = static_cast<AbTableItem*>( item(row, 0) ); if ( contactCompare( contactList[ati], r, category ) ) break; } } if ( row >= rows || row < 0 ) { if ( row < 0 ) currFindRow = rows; else currFindRow = -1; if ( wrapAround ) emit signalWrapAround(); else emit signalNotFound(); wrapAround = !wrapAround; } else { currFindRow = row; QTableSelection foundSelection; foundSelection.init( currFindRow, 0 ); foundSelection.expandTo( currFindRow, numCols() - 1 ); addSelection( foundSelection ); setCurrentCell( currFindRow, 0 /* numCols() - 1 */ ); wrapAround = true; } } static bool contactCompare( const OContact &cnt, const QRegExp &r, int category ) { bool returnMe; QArray<int> cats; cats = cnt.categories(); returnMe = false; if ( (cats.count() == 0) || (category == 0) ) returnMe = cnt.match( r ); else { int i; for ( i = 0; i < int(cats.count()); i++ ) { if ( cats[i] == category ) { returnMe = cnt.match( r ); break; } } } return returnMe; } void AbTable::fitColumns() { int contentsWidth = visibleWidth() / 2; if ( showBk == "Cards" ) { showColumn(1); //adjustColumn(1); setColumnWidth( 1, visibleWidth() ); columnVisible = false; } else { if ( columnVisible == false ){ showColumn(0); columnVisible = true; } setColumnWidth( 0, contentsWidth ); adjustColumn(1); if ( columnWidth(1) < contentsWidth ) setColumnWidth( 1, contentsWidth ); } } void AbTable::show() { fitColumns(); QTable::show(); } void AbTable::setChoiceNames( const QStringList& list) { choicenames = list; if ( choicenames.isEmpty() ) { // hide pick column setNumCols( 2 ); } else { // show pick column setNumCols( 3 ); setColumnWidth( 2, fontMetrics().width(tr( "Pick" ))+8 ); horizontalHeader()->setLabel( 2, tr( "Pick" )); } fitColumns(); } void AbTable::itemClicked(int,int col) { if ( col == 2 ) { return; } else { emit details(); } } QStringList AbTable::choiceNames() const { return choicenames; } void AbTable::setChoiceSelection(int /*index*/, const QStringList& /*list*/) { /* ###### QString selname = choicenames.at(index); for (each row) { OContact *c = contactForRow(row); if ( list.contains(c->email) ) { list.remove(c->email); setText(row, 2, selname); } } for (remaining list items) { OContact *c = new contact(item); setText(newrow, 2, selname); } */ } QStringList AbTable::choiceSelection(int /*index*/) const { QStringList r; /* ###### QString selname = choicenames.at(index); for (each row) { OContact *c = contactForRow(row); if ( text(row,2) == selname ) { r.append(c->email); } } */ return r; } void AbTable::setShowCategory( const QString &b, const QString &c ) { showBk = b; showCat = c; //QMessageBox::information( this, "setShowCategory", "setShowCategory" ); //updateVisible(); refresh(); ensureCellVisible( currentRow(), 0 ); updateVisible(); // :SX } void AbTable::setShowByLetter( char c ) { showChar = tolower(c); updateVisible(); } QString AbTable::showCategory() const { return showCat; } QString AbTable::showBook() const { return showBk; } QStringList AbTable::categories() { mCat.load( categoryFileName() ); QStringList categoryList = mCat.labels( "Contacts" ); return categoryList; } void AbTable::updateVisible() { int visible, totalRows, id, totalCats, it, row; bool hide; AbTableItem *ati; OContact *cnt; QString fileAsName; QString tmpStr; visible = 0; setPaintingEnabled( FALSE ); totalRows = numRows(); id = mCat.id( "Contacts", showCat ); QArray<int> cats; for ( row = 0; row < totalRows; row++ ) { ati = static_cast<AbTableItem*>( item(row, 0) ); cnt = &contactList[ati]; cats = cnt->categories(); fileAsName = cnt->fileAs(); hide = false; if ( !showCat.isEmpty() ) { if ( showCat == tr( "Unfiled" ) ) { if ( cats.count() > 0 ) hide = true; } else { // do some comparing if ( !hide ) { hide = true; totalCats = int(cats.count()); for ( it = 0; it < totalCats; it++ ) { if ( cats[it] == id ) { hide = false; break; } } } } } if ( showChar != '\0' ) { tmpStr = fileAsName.left(1); tmpStr = tmpStr.lower(); if ( tmpStr != QString(QChar(showChar)) && showChar != '#' ) { hide = true; } if ( showChar == '#' ) { if (tmpStr == "a") hide = true; if (tmpStr == "b") hide = true; if (tmpStr == "c") hide = true; if (tmpStr == "d") hide = true; if (tmpStr == "e") hide = true; if (tmpStr == "f") hide = true; if (tmpStr == "g") hide = true; if (tmpStr == "h") hide = true; if (tmpStr == "i") hide = true; if (tmpStr == "j") hide = true; if (tmpStr == "k") hide = true; if (tmpStr == "l") hide = true; if (tmpStr == "m") hide = true; if (tmpStr == "n") hide = true; if (tmpStr == "o") hide = true; if (tmpStr == "p") hide = true; if (tmpStr == "q") hide = true; if (tmpStr == "r") hide = true; if (tmpStr == "s") hide = true; if (tmpStr == "t") hide = true; if (tmpStr == "u") hide = true; if (tmpStr == "v") hide = true; if (tmpStr == "w") hide = true; if (tmpStr == "x") hide = true; if (tmpStr == "y") hide = true; if (tmpStr == "z") hide = true; } } if ( hide ) { if ( currentRow() == row ) setCurrentCell( -1, 0 ); if ( rowHeight(row) > 0 ) hideRow( row ); } else { if ( rowHeight(row) == 0 ) { showRow( row ); adjustRow( row ); } visible++; } } if ( !visible ) setCurrentCell( -1, 0 ); setPaintingEnabled( TRUE ); } void AbTable::setPaintingEnabled( bool e ) { if ( e != enablePainting ) { if ( !enablePainting ) { enablePainting = true; rowHeightChanged( 0 ); viewport()->update(); } else { enablePainting = false; } } } void AbTable::rowHeightChanged( int row ) { if ( enablePainting ) QTable::rowHeightChanged( row ); } diff --git a/core/pim/addressbook/abtable.h b/core/pim/addressbook/abtable.h index b445874..35a1e9e 100644 --- a/core/pim/addressbook/abtable.h +++ b/core/pim/addressbook/abtable.h @@ -1,154 +1,161 @@ /********************************************************************** ** Copyright (C) 2000 Trolltech AS. All rights reserved. ** ** This file is part of Qt Palmtop Environment. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #ifndef ABTABLE_H #define ABTABLE_H #include <qpe/categories.h> #include <opie/ocontact.h> #include <qmap.h> #include <qtable.h> #include <qstringlist.h> #include <qcombobox.h> #include <opie/ocontactaccess.h> class AbTableItem : public QTableItem { public: AbTableItem( QTable *t, EditType et, const QString &s, const QString &secondSortKey); QString entryKey() const; void setEntryKey( const QString & k ); virtual int alignment() const; virtual QString key() const; void setItem( const QString &txt, const QString &secondKey ); private: QString sortKey; }; class AbPickItem : public QTableItem { public: AbPickItem( QTable *t ); QWidget *createEditor() const; void setContentFromEditor( QWidget *w ); private: QGuardedPtr<QComboBox> cb; }; class AbTable : public QTable { Q_OBJECT public: AbTable( const QValueList<int> *ordered, QWidget *parent, const char *name=0 ); ~AbTable(); // NEW void addEntry( const OContact &newContact ); OContact currentEntry(); void replaceCurrentEntry( const OContact &newContact ); void init(); void deleteCurrentEntry(); void clear(); void clearFindRow() { currFindRow = -1; } void loadFields(); void refresh(); bool save(); void load(); void reload(); // addresspicker mode void setChoiceNames( const QStringList& list); QStringList choiceNames() const; void setChoiceSelection(int index, const QStringList& list); QStringList choiceSelection(int index) const; void setShowCategory( const QString &b, const QString &c ); void setShowByLetter( char c ); QString showCategory() const; QStringList categories(); void resizeRows(); void show(); void setPaintingEnabled( bool e ); QString showBook() const; + void inSearch() { m_inSearch = true; } + void offSearch() { m_inSearch = false; } + public slots: void slotDoFind( const QString &str, bool caseSensitive, bool useRegExp, bool backwards, QString category = QString::null ); signals: void empty( bool ); void details(); void signalNotFound(); void signalWrapAround(); + void signalSearchBackward(); // Signalled if backward search is requested + void signalSearchNext(); // Singalled if forward search is requested protected: virtual void keyPressEvent( QKeyEvent *e ); // int rowHeight( int ) const; // int rowPos( int row ) const; // virtual int rowAt( int pos ) const; protected slots: void moveTo( char ); virtual void columnClicked( int col ); void itemClicked(int,int col); void rowHeightChanged( int row ); private: void loadFile( const QString &strFile, bool journalFile ); void fitColumns(); void resort(); void updateJournal( const OContact &contact, OContact::journal_action action, int row = -1 ); void insertIntoTable( const OContact &contact, int row ); QString findContactName( const OContact &entry ); QString findContactContact( const OContact &entry, int row ); void journalFreeReplace( const OContact &cnt, int row ); void journalFreeRemove( int row ); void realignTable( int ); void updateVisible(); int lastSortCol; bool asc; char showChar; QMap<AbTableItem*, OContact> contactList; const QValueList<int> *intFields; int currFindRow; QString showCat; QStringList choicenames; bool enablePainting; Categories mCat; QString showBk; bool columnVisible; + bool m_inSearch; + OContactAccess m_contactdb; }; #endif // ABTABLE_H diff --git a/core/pim/addressbook/addressbook.cpp b/core/pim/addressbook/addressbook.cpp index f7e4c95..3466801 100644 --- a/core/pim/addressbook/addressbook.cpp +++ b/core/pim/addressbook/addressbook.cpp @@ -1,1056 +1,1088 @@ /********************************************************************** ** Copyright (C) 2000 Trolltech AS. All rights reserved. ** ** This file is part of Qt Palmtop Environment. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** OContact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #define QTOPIA_INTERNAL_FD #include "contacteditor.h" #include "ablabel.h" #include "abtable.h" #include "addresssettings.h" #include "addressbook.h" #include <opie/ofileselector.h> #include <opie/ofiledialog.h> #include <qpe/qpeapplication.h> #include <qpe/config.h> #include <opie/ocontact.h> #include <qpe/global.h> #include <qpe/resource.h> #include <qpe/ir.h> #include <qpe/qpemessagebox.h> #include <qpe/qcopenvelope_qws.h> #include <qaction.h> #include <qdialog.h> #include <qdir.h> #include <qfile.h> #include <qimage.h> #include <qlayout.h> #include <qpe/qpemenubar.h> #include <qmessagebox.h> #include <qpixmap.h> #include <qpopupmenu.h> #include <qpe/qpetoolbar.h> #include <qstringlist.h> #include <qtoolbutton.h> #include <qwhatsthis.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> #include <qdatetime.h> #include "picker.h" #include "configdlg.h" static QString addressbookPersonalVCardName() { QString filename = Global::applicationFileName("addressbook", "businesscard.vcf"); return filename; } AddressbookWindow::AddressbookWindow( QWidget *parent, const char *name, WFlags f ) : QMainWindow( parent, name, f ), abEditor(0), useRegExp(false), - DoSignalWrapAround(false), + doNotifyWrapAround(true), caseSensitive(false), bAbEditFirstTime(TRUE), syncing(FALSE) { isLoading = true; + // Read Config settings + Config cfg("AddressBook"); + cfg.setGroup("Search"); + useRegExp = cfg.readBoolEntry( "useRegExp" ); + caseSensitive = cfg.readBoolEntry( "caseSensitive" ); + doNotifyWrapAround = cfg.readBoolEntry( "doNotifyWrapAround" ); + initFields(); setCaption( tr("Contacts") ); setIcon( Resource::loadPixmap( "AddressBook" ) ); setToolBarsMovable( FALSE ); // Create Toolbars QPEToolBar *bar = new QPEToolBar( this ); bar->setHorizontalStretchable( TRUE ); QPEMenuBar *mbList = new QPEMenuBar( bar ); mbList->setMargin( 0 ); QPopupMenu *edit = new QPopupMenu( this ); mbList->insertItem( tr( "Contact" ), edit ); listTools = new QPEToolBar( this, "list operations" ); QAction *a = new QAction( tr( "New" ), Resource::loadPixmap( "new" ), QString::null, 0, this, 0 ); actionNew = a; connect( a, SIGNAL( activated() ), this, SLOT( slotListNew() ) ); a->addTo( edit ); a->addTo( listTools ); a = new QAction( tr( "Edit" ), Resource::loadPixmap( "edit" ), QString::null, 0, this, 0 ); actionEdit = a; connect( a, SIGNAL( activated() ), this, SLOT( slotViewEdit() ) ); a->addTo( edit ); a->addTo( listTools ); a = new QAction( tr( "Delete" ), Resource::loadPixmap( "trash" ), QString::null, 0, this, 0 ); actionTrash = a; connect( a, SIGNAL( activated() ), this, SLOT( slotListDelete() ) ); a->addTo( edit ); a->addTo( listTools ); // make it possible to go directly to businesscard via qcop call #if defined(Q_WS_QWS) #if !defined(QT_NO_COP) QCopChannel *addressChannel = new QCopChannel("QPE/Addressbook" , this ); connect (addressChannel, SIGNAL( received(const QCString &, const QByteArray &)), this, SLOT ( appMessage(const QCString &, const QByteArray &) ) ); #endif #endif a = new QAction( tr( "Find" ), Resource::loadPixmap( "mag" ), QString::null, 0, this, 0 ); actionFind = a; connect( a, SIGNAL(activated()), this, SLOT( slotFindOpen()) ); a->addTo( edit ); a->addTo( listTools ); // Much better search widget, taken from QTReader.. (se) searchBar = new OFloatBar( "Search", this, QMainWindow::Top, TRUE ); searchBar->setHorizontalStretchable( TRUE ); searchBar->hide(); searchEdit = new QLineEdit( searchBar, "searchEdit" ); // QFont f("unifont", 16 /*, QFont::Bold*/); // searchEdit->setFont( f ); searchBar->setStretchableWidget( searchEdit ); connect( searchEdit, SIGNAL( returnPressed( ) ), this, SLOT( slotFind( ) ) ); a = new QAction( tr( "Find Next" ), Resource::loadPixmap( "next" ), QString::null, 0, this, 0 ); connect( a, SIGNAL( activated() ), this, SLOT( slotFindNext() ) ); a->addTo( searchBar ); a = new QAction( tr( "Close Find" ), Resource::loadPixmap( "close" ), QString::null, 0, this, 0 ); connect( a, SIGNAL( activated() ), this, SLOT( slotFindClose() ) ); a->addTo( searchBar ); a = new QAction( tr( "Write Mail To" ), Resource::loadPixmap( "qtmail/reply" ), QString::null, 0, this, 0 ); //a->setEnabled( FALSE ); we got support for it now :) zecke actionMail = a; connect( a, SIGNAL( activated() ), this, SLOT( writeMail() ) ); a->addTo( edit ); a->addTo( listTools ); if ( Ir::supported() ) { a = new QAction( tr ("Beam Entry" ), Resource::loadPixmap( "beam" ), QString::null, 0, this, 0 ); actionBeam = a; connect( a, SIGNAL( activated() ), this, SLOT( slotBeam() ) ); a->addTo( edit ); a->addTo( listTools ); } edit->insertSeparator(); a = new QAction( tr("Import vCard"), QString::null, 0, 0, 0, TRUE ); actionPersonal = a; connect( a, SIGNAL( activated() ), this, SLOT( importvCard() ) ); a->addTo( edit ); edit->insertSeparator(); a = new QAction( tr("My Personal Details"), QString::null, 0, 0, 0, TRUE ); actionPersonal = a; connect( a, SIGNAL( activated() ), this, SLOT( slotPersonalView() ) ); a->addTo( edit ); // Do we need this function ? (se) // a = new QAction( tr( "Arrange Edit Fields"), QString::null, 0, 0 ); // connect( a, SIGNAL( activated() ), this, SLOT( slotSettings() ) ); // a->addTo( edit ); #ifdef __DEBUG_RELEASE // Remove this function for public Release ! This is only // for debug purposes .. a = new QAction( tr( "Save all Data"), QString::null, 0, 0 ); connect( a, SIGNAL( activated() ), this , SLOT( slotSave() ) ); a->addTo( edit ); #endif a = new QAction( tr( "Config" ), Resource::loadPixmap( "today/config" ), QString::null, 0, this, 0 ); connect( a, SIGNAL( activated() ), this, SLOT( slotConfig() ) ); a->addTo( edit ); // Create Views listContainer = new QWidget( this ); QVBoxLayout *vb = new QVBoxLayout( listContainer ); abList = new AbTable( &orderedFields, listContainer, "table" ); vb->addWidget(abList); // abList->setHScrollBarMode( QScrollView::AlwaysOff ); connect( abList, SIGNAL( empty( bool ) ), this, SLOT( listIsEmpty( bool ) ) ); connect( abList, SIGNAL( details() ), this, SLOT( slotListView() ) ); connect( abList, SIGNAL(currentChanged(int,int)), this, SLOT(slotUpdateToolbar()) ); + connect( abList, SIGNAL( signalSearchNext() ), this, SLOT( slotFindNext() ) ); + connect( abList, SIGNAL( signalSearchBackward() ), this, SLOT( slotFindPrevious() ) ); + + // Maybe we should react on Wraparound and notfound ? + QObject::connect( abList, SIGNAL(signalNotFound()), this, SLOT(slotNotFound()) ); + QObject::connect( abList, SIGNAL(signalWrapAround()), this, SLOT(slotWrapAround()) ); mView = 0; abList->load(); pLabel = new LetterPicker( listContainer ); connect(pLabel, SIGNAL(letterClicked(char)), this, SLOT(slotSetLetter(char))); vb->addWidget(pLabel); catMenu = new QPopupMenu( this ); catMenu->setCheckable( TRUE ); connect( catMenu, SIGNAL(activated(int)), this, SLOT(slotSetCategory(int)) ); populateCategories(); mbList->insertItem( tr("View"), catMenu ); // setCentralWidget( listContainer ); fontMenu = new QPopupMenu(this); fontMenu->setCheckable( true ); connect( fontMenu, SIGNAL(activated(int)), this, SLOT(slotSetFont(int))); fontMenu->insertItem(tr( "Small" ), 0); fontMenu->insertItem(tr( "Normal" ), 1); fontMenu->insertItem(tr( "Large" ), 2); defaultFont = new QFont( abList->font() ); slotSetFont(startFontSize); mbList->insertItem( tr("Font"), fontMenu); setCentralWidget(listContainer); // qDebug("adressbook contrsuction: t=%d", t.elapsed() ); abList->setCurrentCell( 0, 0 ); - // Read Config settings - Config cfg("AddressBook"); - cfg.setGroup("Search"); - useRegExp = cfg.readBoolEntry( "useRegExp" ); - caseSensitive = cfg.readBoolEntry( "caseSensitive" ); - DoSignalWrapAround = cfg.readBoolEntry( "signalWrapAround" ); - isLoading = false; } void AddressbookWindow::slotConfig() { ConfigDlg* dlg = new ConfigDlg( this, "Config" ); dlg -> setUseRegExp ( useRegExp ); dlg -> setBeCaseSensitive( caseSensitive ); - dlg -> setSignalWrapAround( DoSignalWrapAround ); + dlg -> setSignalWrapAround( doNotifyWrapAround ); dlg -> showMaximized(); if ( dlg -> exec() ) { qWarning ("Config Dialog accepted !"); useRegExp = dlg -> useRegExp(); caseSensitive = dlg -> beCaseSensitive(); - DoSignalWrapAround = dlg -> signalWrapAround(); + doNotifyWrapAround = dlg -> signalWrapAround(); } delete dlg; } void AddressbookWindow::slotSetFont( int size ) { if (size > 2 || size < 0) size = 1; startFontSize = size; QFont *currentFont; switch (size) { case 0: fontMenu->setItemChecked(0, true); fontMenu->setItemChecked(1, false); fontMenu->setItemChecked(2, false); abList->setFont( QFont( defaultFont->family(), defaultFont->pointSize() - 2 ) ); currentFont = new QFont (abList->font()); // abList->resizeRows(currentFont->pixelSize() + 7); abList->resizeRows(); break; case 1: fontMenu->setItemChecked(0, false); fontMenu->setItemChecked(1, true); fontMenu->setItemChecked(2, false); abList->setFont( *defaultFont ); currentFont = new QFont (abList->font()); // abList->resizeRows(currentFont->pixelSize() + 7); abList->resizeRows(); break; case 2: fontMenu->setItemChecked(0, false); fontMenu->setItemChecked(1, false); fontMenu->setItemChecked(2, true); abList->setFont( QFont( defaultFont->family(), defaultFont->pointSize() + 2 ) ); currentFont = new QFont (abList->font()); //abList->resizeRows(currentFont->pixelSize() + 7); abList->resizeRows(); break; } } void AddressbookWindow::importvCard() { QString str = OFileDialog::getOpenFileName( 1,"/");//,"", "*", this ); if(!str.isEmpty() ) setDocument((const QString&) str ); } void AddressbookWindow::setDocument( const QString &filename ) { if ( filename.find(".vcf") != int(filename.length()) - 4 ) return; QValueList<OContact> cl = OContact::readVCard( filename ); for( QValueList<OContact>::Iterator it = cl.begin(); it != cl.end(); ++it ) { // QString msg = tr("You received a vCard for\n%1.\nDo You want to add it to your\naddressbook?") // .arg( (*it).fullName() ); // if ( QMessageBox::information( this, tr("received contact"), msg, QMessageBox::Ok, QMessageBox::Cancel ) == // QMessageBox::Ok ) { abList->addEntry( *it ); // } } } void AddressbookWindow::resizeEvent( QResizeEvent *e ) { QMainWindow::resizeEvent( e ); if ( centralWidget() == listContainer ) showList(); else if ( centralWidget() == mView ) showView(); } AddressbookWindow::~AddressbookWindow() { Config cfg("AddressBook"); cfg.setGroup("Font"); cfg.writeEntry("fontSize", startFontSize); cfg.setGroup("Search"); cfg.writeEntry("useRegExp", useRegExp); cfg.writeEntry("caseSensitive", caseSensitive); - cfg.writeEntry("signalWrapAround", DoSignalWrapAround); + cfg.writeEntry("doNotifyWrapAround", doNotifyWrapAround); } void AddressbookWindow::slotUpdateToolbar() { OContact ce = abList->currentEntry(); actionMail->setEnabled( !ce.defaultEmail().isEmpty() ); } void AddressbookWindow::showList() { bool visiblemView; visiblemView = false; if ( mView ) { mView->hide(); visiblemView = true; } setCentralWidget( listContainer ); listContainer->show(); // update our focues... (or use a stack widget!); abList->setFocus(); // This makes sure we are scrolled all the way to the left abList->setContentsPos( 0, abList->contentsY() ); //if ( visiblemView && abList->showBook() == "Cards" ) // abList->setShowCategory( abList->showBook(), abList->showCategory() ); } void AddressbookWindow::showView() { if ( abList->numRows() > 0 ) { listContainer->hide(); setCentralWidget( abView() ); mView->show(); mView->setFocus(); } } void AddressbookWindow::slotListNew() { OContact cnt; if( !syncing ) { if ( abEditor ) abEditor->setEntry( cnt ); abView()->init( cnt ); editEntry( NewEntry ); } else { QMessageBox::warning(this, tr("OContacts"), tr("Can not edit data, currently syncing")); } } void AddressbookWindow::slotListView() { abView()->init( abList->currentEntry() ); mView->sync(); showView(); } void AddressbookWindow::slotListDelete() { if(!syncing) { OContact tmpEntry = abList->currentEntry(); // get a name, do the best we can... QString strName = tmpEntry.fullName(); if ( strName.isEmpty() ) { strName = tmpEntry.company(); if ( strName.isEmpty() ) strName = "No Name"; } if ( QPEMessageBox::confirmDelete( this, tr( "Contacts" ), strName ) ) { abList->deleteCurrentEntry(); showList(); } } else { QMessageBox::warning( this, tr("Contacts"), tr("Can not edit data, currently syncing") ); } } void AddressbookWindow::slotViewBack() { showList(); } void AddressbookWindow::slotViewEdit() { if(!syncing) { if (actionPersonal->isOn()) { editPersonal(); } else { if ( !bAbEditFirstTime ) abEditor->setEntry( abList->currentEntry() ); editEntry( EditEntry ); } } else { QMessageBox::warning( this, tr("Contacts"), tr("Can not edit data, currently syncing") ); } } void AddressbookWindow::writeMail() { OContact c = abList->currentEntry(); QString name = c.fileAs(); QString email = c.defaultEmail(); QCopEnvelope e("QPE/Application/qtmail", "writeMail(QString,QString)"); e << name << email; } static const char * beamfile = "/tmp/obex/contact.vcf"; void AddressbookWindow::slotBeam() { QString filename; OContact c; if ( actionPersonal->isOn() ) { filename = addressbookPersonalVCardName(); if (!QFile::exists(filename)) return; // can't beam a non-existent file c = OContact::readVCard( filename )[0]; } else { unlink( beamfile ); // delete if exists c = abList->currentEntry(); mkdir("/tmp/obex/", 0755); OContact::writeVCard( beamfile, c ); filename = beamfile; } Ir *ir = new Ir( this ); connect( ir, SIGNAL( done( Ir * ) ), this, SLOT( beamDone( Ir * ) ) ); QString description = c.fullName(); ir->send( filename, description, "text/x-vCard" ); } void AddressbookWindow::beamDone( Ir *ir ) { delete ir; unlink( beamfile ); } static void parseName( const QString& name, QString *first, QString *middle, QString * last ) { int comma = name.find ( "," ); QString rest; if ( comma > 0 ) { *last = name.left( comma ); comma++; while ( comma < int(name.length()) && name[comma] == ' ' ) comma++; rest = name.mid( comma ); } else { int space = name.findRev( ' ' ); *last = name.mid( space+1 ); rest = name.left( space ); } int space = rest.find( ' ' ); if ( space <= 0 ) { *first = rest; } else { *first = rest.left( space ); *middle = rest.mid( space+1 ); } } void AddressbookWindow::appMessage(const QCString &msg, const QByteArray &data) { if (msg == "editPersonal()") { editPersonal(); } else if (msg == "editPersonalAndClose()") { editPersonal(); close(); } else if ( msg == "addContact(QString,QString)" ) { QDataStream stream(data,IO_ReadOnly); QString name, email; stream >> name >> email; OContact cnt; QString fn, mn, ln; parseName( name, &fn, &mn, &ln ); // qDebug( " %s - %s - %s", fn.latin1(), mn.latin1(), ln.latin1() ); cnt.setFirstName( fn ); cnt.setMiddleName( mn ); cnt.setLastName( ln ); cnt.insertEmails( email ); cnt.setDefaultEmail( email ); cnt.setFileAs(); if ( bAbEditFirstTime ) { abEditor = new ContactEditor( cnt, &orderedFields, &slOrderedFields, this, "editor" ); bAbEditFirstTime = FALSE; } else { abEditor->setEntry( cnt ); } abView()->init( cnt ); editEntry( NewEntry ); } #if 0 else if (msg == "pickAddresses(QCString,QCString,QStringList,...)" ) { QDataStream stream(data,IO_ReadOnly); QCString ch,m; QStringList types; stream >> ch >> m >> types; AddressPicker picker(abList,this,0,TRUE); picker.showMaximized(); picker.setChoiceNames(types); int i=0; for (QStringList::ConstIterator it = types.begin(); it!=types.end(); ++it) { QStringList sel; stream >> sel; picker.setSelection(i++,sel); } picker.showMaximized(); picker.exec(); // ###### note: contacts may have been added - save here! setCentralWidget(abList); QCopEnvelope e(ch,m); i=0; for (QStringList::ConstIterator it = types.begin(); it!=types.end(); ++it) { QStringList sel = picker.selection(i++); e << sel; } } #endif } void AddressbookWindow::editPersonal() { QString filename = addressbookPersonalVCardName(); OContact me; if (QFile::exists(filename)) me = OContact::readVCard( filename )[0]; if (bAbEditFirstTime) { abEditor = new ContactEditor( me, &orderedFields, &slOrderedFields, this, "editor" ); // don't create a new editor every time bAbEditFirstTime = FALSE; } else abEditor->setEntry( me ); abEditor->setCaption(tr("Edit My Personal Details")); abEditor->showMaximized(); // fix the foxus... abEditor->setNameFocus(); if ( abEditor->exec() ) { setFocus(); OContact new_personal = abEditor->entry(); QString fname = addressbookPersonalVCardName(); OContact::writeVCard( fname, new_personal ); abView()->init(new_personal); abView()->sync(); } abEditor->setCaption( tr("Edit Address") ); } void AddressbookWindow::slotPersonalView() { if (!actionPersonal->isOn()) { // we just turned it off setCaption( tr("Contacts") ); actionNew->setEnabled(TRUE); actionTrash->setEnabled(TRUE); -#ifndef MAKE_FOR_SHARP_ROM actionFind->setEnabled(TRUE); -#endif slotUpdateToolbar(); // maybe some of the above could be moved there showList(); return; } // XXX need to disable some QActions. actionNew->setEnabled(FALSE); actionTrash->setEnabled(FALSE); #ifndef MAKE_FOR_SHARP_ROM actionFind->setEnabled(FALSE); #endif actionMail->setEnabled(FALSE); setCaption( tr("Contacts - My Personal Details") ); QString filename = addressbookPersonalVCardName(); OContact me; if (QFile::exists(filename)) me = OContact::readVCard( filename )[0]; abView()->init( me ); abView()->sync(); listContainer->hide(); setCentralWidget( abView() ); mView->show(); mView->setFocus(); } void AddressbookWindow::editEntry( EntryMode entryMode ) { OContact entry; if ( bAbEditFirstTime ) { abEditor = new ContactEditor( entry, &orderedFields, &slOrderedFields, this, "editor" ); bAbEditFirstTime = FALSE; if ( entryMode == EditEntry ) abEditor->setEntry( abList->currentEntry() ); } // other things may chane the caption. abEditor->setCaption( tr("Edit Address") ); #if defined(Q_WS_QWS) || defined(_WS_QWS_) abEditor->showMaximized(); #endif // fix the foxus... abEditor->setNameFocus(); if ( abEditor->exec() ) { setFocus(); if ( entryMode == NewEntry ) { OContact insertEntry = abEditor->entry(); insertEntry.assignUid(); abList->addEntry( insertEntry ); } else { OContact replaceEntry = abEditor->entry(); if ( !replaceEntry.isValidUid() ) replaceEntry.assignUid(); abList->replaceCurrentEntry( replaceEntry ); } } populateCategories(); showList(); } void AddressbookWindow::listIsEmpty( bool empty ) { if ( !empty ) { deleteButton->setEnabled( TRUE ); } } void AddressbookWindow::reload() { syncing = FALSE; abList->clear(); abList->reload(); } void AddressbookWindow::flush() { syncing = TRUE; abList->save(); } void AddressbookWindow::closeEvent( QCloseEvent *e ) { if ( centralWidget() == mView ) { if (actionPersonal->isOn()) { // pretend we clicked it off actionPersonal->setOn(FALSE); slotPersonalView(); } else { showList(); } e->ignore(); return; } if(syncing) { /* shouldn't we save, I hear you say? well its already been set so that an edit can not occur during a sync, and we flushed at the start of the sync, so there is no need to save Saving however itself would cause problems. */ e->accept(); return; } //################## shouldn't always save // True, but the database handles this automatically ! (se) if ( save() ) e->accept(); else e->ignore(); } /* Returns TRUE if it is OK to exit */ bool AddressbookWindow::save() { if ( !abList->save() ) { if ( QMessageBox::critical( 0, tr( "Out of space" ), tr("Unable to save information.\n" "Free up some space\n" "and try again.\n" "\nQuit anyway?"), QMessageBox::Yes|QMessageBox::Escape, QMessageBox::No|QMessageBox::Default ) != QMessageBox::No ) return TRUE; else return FALSE; } return TRUE; } #ifdef __DEBUG_RELEASE void AddressbookWindow::slotSave() { save(); } #endif void AddressbookWindow::slotSettings() { AddressSettings frmSettings( this ); #if defined(Q_WS_QWS) || defined(_WS_QWS_) frmSettings.showMaximized(); #endif if ( frmSettings.exec() ) { allFields.clear(); orderedFields.clear(); slOrderedFields.clear(); initFields(); if ( abEditor ) abEditor->loadFields(); abList->refresh(); } } void AddressbookWindow::initFields() { // we really don't need the things from the configuration, anymore // only thing that is important are the important categories. So, // Call the contact functions that correspond to these old functions... QStringList xmlFields = OContact::fields(); QStringList visibleFields = OContact::untrfields(); // QStringList trFields = OContact::trfields(); xmlFields.remove( "Title" ); visibleFields.remove( "Name Title" ); visibleFields.remove( "Notes" ); int i, version; Config cfg( "AddressBook" ); QString zn; // ### Write a function to keep this from happening again... QStringList::ConstIterator it; for ( i = 0, it = xmlFields.begin(); it != xmlFields.end(); ++it, i++ ) { allFields.append( i + 3 ); } cfg.setGroup( "Version" ); version = cfg.readNumEntry( "version" ); i = 0; startFontSize = 1; if ( version >= ADDRESSVERSION ) { cfg.setGroup( "ImportantCategory" ); zn = cfg.readEntry( "Category" + QString::number(i), QString::null ); while ( !zn.isNull() ) { if ( zn.contains( "Work" ) || zn.contains( "Mb" ) ) { slOrderedFields.clear(); break; } slOrderedFields.append( zn ); zn = cfg.readEntry( "Category" + QString::number(++i), QString::null ); } cfg.setGroup( "Font" ); startFontSize = cfg.readNumEntry( "fontSize", 1 ); } else { QString str; str = getenv("HOME"); str += "/Settings/AddressBook.conf"; QFile::remove( str ); } if ( slOrderedFields.count() > 0 ) { for( QStringList::ConstIterator it = slOrderedFields.begin(); it != slOrderedFields.end(); ++it ) { QValueList<int>::ConstIterator itVl; QStringList::ConstIterator itVis; itVl = allFields.begin(); for ( itVis = visibleFields.begin(); itVis != visibleFields.end() && itVl != allFields.end(); ++itVis, ++itVl ) { if ( *it == *itVis && itVl != allFields.end() ) { orderedFields.append( *itVl ); } } } } else { QValueList<int>::ConstIterator it; for ( it = allFields.begin(); it != allFields.end(); ++it ) orderedFields.append( *it ); slOrderedFields = visibleFields; orderedFields.remove( Qtopia::AddressUid ); orderedFields.remove( Qtopia::Title ); orderedFields.remove( Qtopia::Groups ); orderedFields.remove( Qtopia::AddressCategory ); orderedFields.remove( Qtopia::FirstName ); orderedFields.remove( Qtopia::LastName ); orderedFields.remove( Qtopia::DefaultEmail ); orderedFields.remove( Qtopia::FileAs ); orderedFields.remove( Qtopia::Notes ); orderedFields.remove( Qtopia::Gender ); slOrderedFields.remove( "Name Title" ); slOrderedFields.remove( "First Name" ); slOrderedFields.remove( "Last Name" ); slOrderedFields.remove( "File As" ); slOrderedFields.remove( "Default Email" ); slOrderedFields.remove( "Notes" ); slOrderedFields.remove( "Gender" ); } } AbLabel *AddressbookWindow::abView() { if ( !mView ) { mView = new AbLabel( this, "viewer" ); mView->init( OContact() ); connect( mView, SIGNAL( okPressed() ), this, SLOT( slotListView() ) ); } return mView; } void AddressbookWindow::slotFindOpen() { searchBar->show(); + abList -> inSearch(); searchEdit->setFocus(); } void AddressbookWindow::slotFindClose() { searchBar->hide(); + abList -> offSearch(); abList->setFocus(); } void AddressbookWindow::slotFindNext() { if ( centralWidget() == abView() ) showList(); - // Maybe we should react on Wraparound and notfound ? -// QObject::connect( abList, SIGNAL(signalNotFound()), &frmFind, SLOT(slotNotFound()) ); -// QObject::connect( abList, SIGNAL(signalWrapAround()), &frmFind, SLOT(slotWrapAround()) ); - abList->slotDoFind( searchEdit->text(), caseSensitive, useRegExp, false); + searchEdit->clearFocus(); + abList->setFocus(); + if ( abList->numSelections() ) + abList->clearSelection(); + +} +void AddressbookWindow::slotFindPrevious() +{ + if ( centralWidget() == abView() ) + showList(); + + abList->slotDoFind( searchEdit->text(), caseSensitive, useRegExp, true); if ( abList->numSelections() ) abList->clearSelection(); } void AddressbookWindow::slotFind() { abList->clearFindRow(); slotFindNext(); } +void AddressbookWindow::slotNotFound() +{ + qWarning("Got notfound signal !"); + QMessageBox::information( this, tr( "Not Found" ), + tr( "Unable to find a contact for this" ) + "\n" + + tr( "search pattern !" ) ); + + +} +void AddressbookWindow::slotWrapAround() +{ + qWarning("Got wrap signal !"); + if ( doNotifyWrapAround ) + QMessageBox::information( this, tr( "End of list" ), + tr( "End of list. Wrap around now.. !" ) + "\n" ); + +} + void AddressbookWindow::slotSetCategory( int c ) { QString cat, book; if ( c <= 0 ) return; // Set checkItem for selected one for ( unsigned int i = 1; i < catMenu->count(); i++ ) catMenu->setItemChecked( i, c == (int)i ); for ( unsigned int i = 1; i < catMenu->count(); i++ ) { if (catMenu->isItemChecked( i )) { if ( i == 1 ) // default List view book = QString::null; else if ( i == 2 ) book = "Phone"; else if ( i == 3 ) book = "Company"; else if ( i == 4 ) book = "Email"; else if ( i == 5 ) book = "Cards"; else if ( i == 6 ) // default All Categories cat = QString::null; else if ( i == (unsigned int)catMenu->count() ) // last menu option will be Unfiled cat = "Unfiled"; else cat = abList->categories()[i - 7]; } } abList->setShowCategory( book, cat ); if ( book.isEmpty() ) book = "List"; if ( cat.isEmpty() ) cat = "All"; setCaption( tr( "Contacts" ) + " - " + tr( book ) + " - " + tr( cat ) ); } void AddressbookWindow::slotSetLetter( char c ) { abList->setShowByLetter( c ); } void AddressbookWindow::populateCategories() { catMenu->clear(); int id, rememberId; id = 1; rememberId = 0; catMenu->insertItem( tr( "List" ), id++ ); catMenu->insertItem( tr( "Phone Book" ), id++ ); catMenu->insertItem( tr( "Company Book" ), id++ ); catMenu->insertItem( tr( "Email Book" ), id++ ); catMenu->insertItem( tr( "Cards" ), id++ ); catMenu->insertSeparator(); catMenu->insertItem( tr( "All" ), id++ ); QStringList categories = abList->categories(); categories.append( tr( "Unfiled" ) ); for ( QStringList::Iterator it = categories.begin(); it != categories.end(); ++it ) { catMenu->insertItem( *it, id ); if ( *it == abList->showCategory() ) rememberId = id; ++id; } if ( abList->showBook().isEmpty() ) { catMenu->setItemChecked( 1, true ); } else if ( abList->showBook() == "Phone" ) { catMenu->setItemChecked( 2, true ); } else if ( abList->showBook() == "Company" ) { catMenu->setItemChecked( 3, true ); } else if ( abList->showBook() == "Email" ) { catMenu->setItemChecked( 4, true ); } else if ( abList->showBook() == "Cards" ) { catMenu->setItemChecked( 5, true ); } if ( abList->showCategory().isEmpty() ) { slotSetCategory( 6 ); } else { slotSetCategory( rememberId ); } } diff --git a/core/pim/addressbook/addressbook.h b/core/pim/addressbook/addressbook.h index 18b083f..299ed70 100644 --- a/core/pim/addressbook/addressbook.h +++ b/core/pim/addressbook/addressbook.h @@ -1,130 +1,133 @@ /********************************************************************** ** Copyright (C) 2000 Trolltech AS. All rights reserved. ** ** This file is part of Qt Palmtop Environment. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #ifndef Addressbook_H #define Addressbook_H // Remove this for OPIE releae 1.0 ! #define __DEBUG_RELEASE #include <qmainwindow.h> #include <qvaluelist.h> #include <qstringlist.h> #include <qlineedit.h> #include "ofloatbar.h" class ContactEditor; class AbLabel; class AbTable; class QPEToolBar; class QPopupMenu; class QToolButton; class QDialog; class Ir; class QAction; class LetterPicker; class AddressbookWindow: public QMainWindow { Q_OBJECT public: AddressbookWindow( QWidget *parent = 0, const char *name = 0, WFlags f = 0 ); ~AddressbookWindow(); protected: void resizeEvent( QResizeEvent * e ); void showList(); void showView(); enum EntryMode { NewEntry=0, EditEntry }; void editPersonal(); void editEntry( EntryMode ); void closeEvent( QCloseEvent *e ); bool save(); public slots: void flush(); void reload(); void appMessage(const QCString &, const QByteArray &); void setDocument( const QString & ); + void slotFindNext(); + void slotFindPrevious(); #ifdef __DEBUG_RELEASE void slotSave(); #endif private slots: void importvCard(); void slotListNew(); void slotListView(); void slotListDelete(); void slotViewBack(); void slotViewEdit(); void slotPersonalView(); void listIsEmpty( bool ); void slotSettings(); void writeMail(); void slotBeam(); void beamDone( Ir * ); void slotSetCategory( int ); void slotSetLetter( char ); void slotUpdateToolbar(); void slotSetFont(int); void slotFindOpen(); void slotFindClose(); void slotFind(); - void slotFindNext(); + void slotNotFound(); + void slotWrapAround(); void slotConfig(); private: void initFields(); // inititialize our fields... AbLabel *abView(); void populateCategories(); QPopupMenu *catMenu, *fontMenu; QPEToolBar *listTools; QToolButton *deleteButton; QValueList<int> allFields, orderedFields; QStringList slOrderedFields; enum Panes { paneList=0, paneView, paneEdit }; ContactEditor *abEditor; AbLabel *mView; LetterPicker *pLabel; AbTable *abList; QWidget *listContainer; // Searching stuff OFloatBar* searchBar; QLineEdit* searchEdit; bool useRegExp; - bool DoSignalWrapAround; + bool doNotifyWrapAround; bool caseSensitive; QAction *actionNew, *actionEdit, *actionTrash, *actionFind, *actionBeam, *actionPersonal, *actionMail; bool bAbEditFirstTime; int viewMargin; bool syncing; QFont *defaultFont; int startFontSize; bool isLoading; }; #endif diff --git a/core/pim/addressbook/picker.cpp b/core/pim/addressbook/picker.cpp index a165451..7f4acb0 100644 --- a/core/pim/addressbook/picker.cpp +++ b/core/pim/addressbook/picker.cpp @@ -1,229 +1,239 @@ #include "picker.h" #include <qfont.h> #include <qstring.h> #include <qtimer.h> #include <qlayout.h> char PickerLabel::lastLetter = '\0'; PickerLabel::PickerLabel( QWidget *parent, const char *name ) : QLabel ( parent, name ) { currentLetter = 0; //lastLetter = 0; letter1 = '\0'; letter2 = '\0'; letter3 = '\0'; setFont( QFont( "smallsmooth", 9 ) ); setTextFormat( Qt::RichText ); } PickerLabel::~PickerLabel() { } void PickerLabel::setLetters( char ch1, char ch2, char ch3 ) { QString tmpStr; if (ch1 != '\0') letter1 = ch1; else letter1 = ' '; if (ch2 != '\0') letter2 = ch2; else letter2 = ' '; if (ch3 != '\0') letter3 = ch3; else letter3 = ' '; tmpStr = "<qt>"; tmpStr += letter1; tmpStr += letter2; tmpStr += letter3; tmpStr += "</qt>"; setText(tmpStr); currentLetter = 0; } void PickerLabel::clearLetter() { QString tmpStr; tmpStr = "<qt>"; tmpStr += letter1; tmpStr += letter2; tmpStr += letter3; tmpStr += "</qt>"; setText(tmpStr); currentLetter = 0; } -void PickerLabel::mouseReleaseEvent( QMouseEvent *e ) +void PickerLabel::mousePressEvent( QMouseEvent* e ) +{ + // If one pickerlabel is was, and an other is now selected, we + // have to simulate the releaseevent.. Otherwise the new label + // will not get a highlighted letter.. + // Maybe there is a more intelligent solution, but this works and I am tired.. (se) + if ( ( currentLetter == 0 ) && ( lastLetter != '\0' ) ) mouseReleaseEvent( e ); +} + +void PickerLabel::mouseReleaseEvent( QMouseEvent* /* e */ ) { QString tmpStr; if (lastLetter != letter1 && lastLetter != letter2 && lastLetter != letter3 && lastLetter != '\0') QTimer::singleShot( 0, this, SLOT(emitClearSignal()) ); switch (currentLetter) { case 0: - tmpStr = "<qt><font color=\"#7F0000\">"; + tmpStr = "<qt><u><font color=\"#7F0000\">"; tmpStr += letter1; - tmpStr += "</font>"; + tmpStr += "</font></u>"; tmpStr += letter2; tmpStr += letter3; tmpStr += "</qt>"; setText(tmpStr); currentLetter++; lastLetter = letter1; emit selectedLetter( letter1 ); break; case 1: tmpStr = "<qt>"; tmpStr += letter1; - tmpStr += "<font color=\"#7F0000\">"; + tmpStr += "<u><font color=\"#7F0000\">"; tmpStr += letter2; - tmpStr += "</font>"; + tmpStr += "</font></u>"; tmpStr += letter3; tmpStr += "</qt>"; setText(tmpStr); currentLetter++; lastLetter = letter2; emit selectedLetter( letter2 ); break; case 2: tmpStr = "<qt>"; tmpStr += letter1; tmpStr += letter2; - tmpStr += "<font color=\"#7F0000\">"; + tmpStr += "<u><font color=\"#7F0000\">"; tmpStr += letter3; - tmpStr += "</font></qt>"; + tmpStr += "</font></u></qt>"; setText(tmpStr); currentLetter++; lastLetter = letter3; emit selectedLetter( letter3 ); break; default: clearLetter(); lastLetter = '\0'; emit selectedLetter( '\0' ); } } void PickerLabel::emitClearSignal() { emit clearAll(); } LetterPicker::LetterPicker( QWidget *parent, const char *name ) : QFrame( parent, name ) { QHBoxLayout *l = new QHBoxLayout(this); lblABC = new PickerLabel( this ); l->addWidget( lblABC ); lblDEF = new PickerLabel( this ); l->addWidget( lblDEF ); lblGHI = new PickerLabel( this ); l->addWidget( lblGHI ); lblJKL = new PickerLabel( this ); l->addWidget( lblJKL ); lblMNO = new PickerLabel( this ); l->addWidget( lblMNO ); lblPQR = new PickerLabel( this ); l->addWidget( lblPQR ); lblSTU = new PickerLabel( this ); l->addWidget( lblSTU ); lblVWX = new PickerLabel( this ); l->addWidget( lblVWX ); lblYZ = new PickerLabel( this ); l->addWidget( lblYZ ); lblABC->setLetters( 'A', 'B', 'C' ); lblDEF->setLetters( 'D', 'E', 'F' ); lblGHI->setLetters( 'G', 'H', 'I' ); lblJKL->setLetters( 'J', 'K', 'L' ); lblMNO->setLetters( 'M', 'N', 'O' ); lblPQR->setLetters( 'P', 'Q', 'R' ); lblSTU->setLetters( 'S', 'T', 'U' ); lblVWX->setLetters( 'V', 'W', 'X' ); lblYZ->setLetters( 'Y', 'Z', '#' ); connect(lblABC, SIGNAL(selectedLetter(char)), this, SLOT(newLetter(char))); connect(lblDEF, SIGNAL(selectedLetter(char)), this, SLOT(newLetter(char))); connect(lblGHI, SIGNAL(selectedLetter(char)), this, SLOT(newLetter(char))); connect(lblJKL, SIGNAL(selectedLetter(char)), this, SLOT(newLetter(char))); connect(lblMNO, SIGNAL(selectedLetter(char)), this, SLOT(newLetter(char))); connect(lblPQR, SIGNAL(selectedLetter(char)), this, SLOT(newLetter(char))); connect(lblSTU, SIGNAL(selectedLetter(char)), this, SLOT(newLetter(char))); connect(lblVWX, SIGNAL(selectedLetter(char)), this, SLOT(newLetter(char))); connect(lblYZ, SIGNAL(selectedLetter(char)), this, SLOT(newLetter(char))); connect(lblABC, SIGNAL(clearAll()), this, SLOT(clear())); connect(lblDEF, SIGNAL(clearAll()), this, SLOT(clear())); connect(lblGHI, SIGNAL(clearAll()), this, SLOT(clear())); connect(lblJKL, SIGNAL(clearAll()), this, SLOT(clear())); connect(lblMNO, SIGNAL(clearAll()), this, SLOT(clear())); connect(lblPQR, SIGNAL(clearAll()), this, SLOT(clear())); connect(lblSTU, SIGNAL(clearAll()), this, SLOT(clear())); connect(lblVWX, SIGNAL(clearAll()), this, SLOT(clear())); connect(lblYZ, SIGNAL(clearAll()), this, SLOT(clear())); } LetterPicker::~LetterPicker() { } void LetterPicker::clear() { lblABC->clearLetter(); lblDEF->clearLetter(); lblGHI->clearLetter(); lblJKL->clearLetter(); lblMNO->clearLetter(); lblPQR->clearLetter(); lblSTU->clearLetter(); lblVWX->clearLetter(); lblYZ->clearLetter(); } void LetterPicker::newLetter( char letter ) { + qWarning("LetterClicked"); emit letterClicked( letter ); } diff --git a/core/pim/addressbook/picker.h b/core/pim/addressbook/picker.h index de5bd9d..d76d582 100644 --- a/core/pim/addressbook/picker.h +++ b/core/pim/addressbook/picker.h @@ -1,65 +1,68 @@ /* * Letter Chooser Widget. * * (c) 2002 Mike Crawford * * This file is FREE SOFTWARE covered under the GUN General Public License. * * */ #ifndef PICKER_H #define PICKER_H #include <qlabel.h> #include <qframe.h> #include <qevent.h> class PickerLabel: public QLabel { Q_OBJECT public: PickerLabel( QWidget *parent = 0, const char *name = 0 ); ~PickerLabel(); void setLetters( char ch1, char ch2, char ch3 ); void clearLetter(); signals: void selectedLetter( char ); void clearAll(); protected: void mouseReleaseEvent( QMouseEvent *e ); + void mousePressEvent( QMouseEvent *e ); private: int currentLetter; static char lastLetter; char letter1, letter2, letter3; private slots: void emitClearSignal(); }; class LetterPicker: public QFrame { Q_OBJECT public: LetterPicker( QWidget *parent = 0, const char *name = 0 ); ~LetterPicker(); + public slots: void clear(); signals: void letterClicked( char ); private: PickerLabel *lblABC, *lblDEF, *lblGHI, *lblJKL, *lblMNO, *lblPQR, *lblSTU, *lblVWX, *lblYZ; + PickerLabel *lastLabel; private slots: void newLetter( char letter ); }; #endif |