Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

tableview.cpp

Go to the documentation of this file.
00001 /*
00002                              This file is part of the Opie Project
00003 
00004                              Copyright (C) Opie Team <opie-devel@handhelds.org>
00005               =.
00006             .=l.
00007            .>+-=
00008  _;:,     .>    :=|.         This program is free software; you can
00009 .> <`_,   >  .   <=          redistribute it and/or  modify it under
00010 :`=1 )Y*s>-.--   :           the terms of the GNU Library General Public
00011 .="- .-=="i,     .._         License as published by the Free Software
00012  - .   .-<_>     .<>         Foundation; either version 2 of the License,
00013      ._= =}       :          or (at your option) any later version.
00014     .%`+i>       _;_.
00015     .i_,=:_.      -<s.       This program is distributed in the hope that
00016      +  .  -:.       =       it will be useful,  but WITHOUT ANY WARRANTY;
00017     : ..    .:,     . . .    without even the implied warranty of
00018     =_        +     =;=|`    MERCHANTABILITY or FITNESS FOR A
00019   _.=:.       :    :=>`:     PARTICULAR PURPOSE. See the GNU
00020 ..}^=.=       =       ;      Library General Public License for more
00021 ++=   -.     .`     .:       details.
00022 :     =  ...= . :.=-
00023  -.   .:....=;==+<;          You should have received a copy of the GNU
00024   -_. . .   )=.  =           Library General Public License along with
00025     --        :-=`           this library; see the file COPYING.LIB.
00026                              If not, write to the Free Software Foundation,
00027                              Inc., 59 Temple Place - Suite 330,
00028                              Boston, MA 02111-1307, USA.
00029 */
00030 
00031 #include <stdlib.h>
00032 #include <cmath>
00033 #include <cctype>
00034 
00035 #include <opie2/odebug.h>
00036 #include <opie2/opimrecurrence.h>
00037 #include <opie2/oresource.h>
00038 
00039 #include <qpe/config.h>
00040 #include <qpe/qpeapplication.h>
00041 
00042 #include <qcombobox.h>
00043 #include <qlineedit.h>
00044 #include <qpopupmenu.h>
00045 
00046 #include "mainwindow.h"
00047 //#include "tableitems.h"
00048 #include "tableview.h"
00049 
00050 using namespace Todo;
00051 
00052 namespace {
00053     static const int BoxSize = 14;
00054     static const int RowHeight = 20;
00055 }
00056 
00057 TableView::EditorWidget::EditorWidget() : m_wid(0l), m_row(-1), m_col(-1) {
00058 }
00059 void TableView::EditorWidget::setCellWidget(QWidget* wid, int row, int col ) {
00060     m_wid = wid;
00061     m_row = row;
00062     m_col = col;
00063 }
00064 void TableView::EditorWidget::releaseCellWidget() {
00065     m_wid = 0;
00066     m_row = m_col = -1;
00067 }
00068 QWidget* TableView::EditorWidget::cellWidget()const {
00069     return m_wid;
00070 }
00071 int TableView::EditorWidget::cellRow()const {
00072     return m_row;
00073 }
00074 int TableView::EditorWidget::cellCol()const {
00075     return m_col;
00076 }
00077 
00078 
00079 void TableView::initConfig() {
00080     Config config( "todo" );
00081     config.setGroup( "Options" );
00082     m_completeStrokeWidth = config.readNumEntry( "CompleteStrokeWidth", 8 );
00083     for (int i = 0; i < numCols(); i++ ) {
00084         int width = config.readNumEntry("Width"+QString::number(i), -1 );
00085         setColumnWidth(i, width == -1 ? columnWidth(i) : width );
00086     }
00087 }
00088 
00089 TableView::TableView( MainWindow* window, QWidget* wid )
00090     : QTable(  wid ), TodoView( window ) {
00091 
00092     setName("TableView");
00093     // Load icons
00094     // TODO - probably should be done globally somewhere else,
00095     //        see also quickeditimpl.cpp/h, taskeditoroverview.cpp/h
00096     m_pic_completed = Opie::Core::OResource::loadPixmap( "todo/completed" );
00097     QString namestr;
00098     for ( unsigned int i = 1; i < 6; i++ ) {
00099         namestr = "todo/priority";
00100         namestr.append( QString::number( i ) );
00101         m_pic_priority[ i - 1 ] = Opie::Core::OResource::loadPixmap( namestr );
00102     }
00103 
00104     setUpdatesEnabled( false );
00105     viewport()->setUpdatesEnabled( false );
00106     m_enablePaint = false;
00107     setNumRows(0);
00108     setNumCols(4);
00109 
00110     horizontalHeader()->setLabel( 0, QWidget::tr("C.") );
00111     horizontalHeader()->setLabel( 1, QWidget::tr("Priority") );
00112     horizontalHeader()->setLabel( 2, QWidget::tr("Summary" ) );
00113     horizontalHeader()->setLabel( 3, QWidget::tr("Deadline") );
00114 
00115     setShowDeadline( todoWindow()->showDeadline() );
00116 
00117     setSorting( TRUE );
00118     setSelectionMode( NoSelection );
00119 
00120     setLeftMargin( 0 );
00121     verticalHeader()->hide();
00122 
00123     QPEApplication::setStylusOperation( viewport(), QPEApplication::RightOnHold );
00124     connect((QTable*)this, SIGNAL( clicked(int,int,int,const QPoint&) ),
00125             this, SLOT( slotClicked(int,int,int,const QPoint&) ) );
00126     connect((QTable*)this, SIGNAL(valueChanged(int,int) ),
00127             this, SLOT( slotValueChanged(int,int) ) );
00128     connect((QTable*)this, SIGNAL(currentChanged(int,int) ),
00129             this, SLOT( slotCurrentChanged(int,int) ) );
00130 
00131     /* now let's init the config */
00132     initConfig();
00133 
00134 
00135     m_enablePaint = true;
00136     setUpdatesEnabled( true );
00137     viewport()->setUpdatesEnabled( true );
00138     viewport()->update();
00139     setSortOrder( Opie::OPimTodoAccess::Completed );
00140     setAscending( TRUE );
00141     m_first = true;
00142 
00143 
00144 }
00145 /* a new day has started
00146  * update the day
00147  */
00148 void TableView::newDay() {
00149     clear();
00150     updateView();
00151 }
00152 TableView::~TableView() {
00153     Config config( "todo" );
00154     config.setGroup( "Options" );
00155     for (int i = 0; i < numCols(); i++ )
00156         config.writeEntry("Width"+QString::number(i), columnWidth(i) );
00157 }
00158 QString TableView::type() const {
00159     return QString::fromLatin1( tr("Table View") );
00160 }
00161 int TableView::current() {
00162     if (numRows() == 0 ) return 0;
00163     int uid = sorted().uidAt(currentRow() );
00164 
00165     return uid;
00166 }
00167 int TableView::next() {
00168     if ( numRows() == 0 ) return 0;
00169     if ( currentRow() + 1 >= numRows() ) return 0;
00170     setCurrentCell( currentRow() +1, currentColumn()  );
00171     return sorted().uidAt( currentRow() );
00172 }
00173 int TableView::prev() {
00174     if ( numRows() == 0 ) return 0;
00175     if ( currentRow() - 1 < 0 ) return 0;
00176     setCurrentCell( currentRow() -1, currentColumn()  );
00177     return sorted().uidAt( currentRow() );
00178 
00179 }
00180 QString TableView::currentRepresentation() {
00181     OPimTodo to = sorted()[currentRow()];
00182     return to.summary().isEmpty() ? to.description().left(20) : to.summary() ;
00183 }
00184 /* show overdue */
00185 void TableView::showOverDue( bool ) {
00186     clear();
00187     updateView();
00188 }
00189 
00190 void TableView::updateView( ) {
00191     m_row = false;
00192     static int id;
00193     id = startTimer(4000 );
00194     /* FIXME we want one page to be read!
00195      *
00196      * Calculate that screensize
00197      */
00198     todoWindow()->setReadAhead( 4 );
00199     sort();
00200     OPimTodoAccess::List::Iterator it, end;
00201     it = sorted().begin();
00202     end = sorted().end();
00203 
00204     QTime time;
00205     time.start();
00206     m_enablePaint = false;
00207     setUpdatesEnabled( false );
00208     viewport()->setUpdatesEnabled( false );
00209 
00210     setNumRows( it.count() );
00211     if ( it.count() == 0 )
00212         killTimer(id);
00213 
00214 //    int elc = time.elapsed();
00215     setUpdatesEnabled( true );
00216     viewport()->setUpdatesEnabled( true );
00217     viewport()->update();
00218 
00219     m_enablePaint = true;
00220 //    int el = time.elapsed();
00221 }
00222 void TableView::setTodo( int, const OPimTodo&) {
00223     sort();
00224 
00225     /* repaint */
00226     repaint();
00227 }
00228 void TableView::addEvent( const OPimTodo&) {
00229 
00230     /* fix problems of not showing the 'Haken' */
00231     updateView();
00232 }
00233 /*
00234  * find the event
00235  * and then replace the complete row
00236  */
00237 void TableView::replaceEvent( const OPimTodo& ev) {
00238     addEvent( ev );
00239 }
00240 /*
00241  * re aligning table can be slow too
00242  * FIXME: look what performs better
00243  * either this or the old align table
00244  */
00245 void TableView::removeEvent( int ) {
00246     updateView();
00247 }
00248 void TableView::setShowCompleted( bool ) {
00249     updateView();
00250 }
00251 void TableView::setShowDeadline( bool b ) {
00252     if ( b )
00253         showColumn( 3 );
00254     else
00255         hideColumn( 3 );
00256 
00257     // Try to intelligently size columns
00258     // TODO - would use width() below, but doesn't have valid value at time of c'tor
00259     int col2width = 238;
00260     int width = m_pic_completed.width();
00261     setColumnWidth( 0, width );
00262     col2width -= width;
00263     width = fontMetrics().boundingRect( horizontalHeader()->label( 1 ) ).width() + 8;
00264     setColumnWidth( 1, width );
00265     col2width -= width;
00266     if ( b ) {
00267         width = fontMetrics().boundingRect( horizontalHeader()->label( 3 ) ).width() + 8;
00268         setColumnWidth( 3, width );
00269         col2width -= width;
00270     }
00271     setColumnWidth( 2, col2width );
00272 }
00273 void TableView::setShowCategory( const QString& str) {
00274     if ( str != m_oleCat || m_first )
00275         updateView();
00276 
00277     m_oleCat = str;
00278     m_first = false;
00279 }
00280 void TableView::clear() {
00281     setNumRows(0);
00282 }
00283 void TableView::slotClicked(int row, int col, int,
00284                             const QPoint& point) {
00285     if ( m_editorWidget.cellWidget() ) {
00286         //setCellContentFromEditor(m_editorWidget.cellRow(), m_editorWidget.cellCol() );
00287         endEdit(m_editorWidget.cellRow(), m_editorWidget.cellCol(),
00288                 true, true );
00289         m_editorWidget.releaseCellWidget();
00290     }
00291 
00292     if ( !cellGeometry(row, col ).contains(point ) )
00293         return;
00294 
00295     int ui= sorted().uidAt( row );
00296 
00297 
00298     switch( col ) {
00299     case 0:{
00300          int x = point.x() -columnPos( col );
00301          int y = point.y() -rowPos( row );
00302          int w = columnWidth( col );
00303          int h = rowHeight( row );
00304          if ( x >= ( w - BoxSize ) / 2 &&
00305               x <= ( w - BoxSize ) / 2 + BoxSize &&
00306               y >= ( h - BoxSize ) / 2 &&
00307               y <= ( h - BoxSize ) / 2 + BoxSize ) {
00308              TodoView::complete(sorted()[row] );
00309          }
00310     }
00311         break;
00312 
00313         // Priority emit a double click...
00314     case 1:{
00315         QWidget* wid = beginEdit( row, col, FALSE );
00316         m_editorWidget.setCellWidget( wid, row, col );
00317     }
00318         break;
00319 
00320     case 2: {
00321         showTodo( ui );
00322         break;
00323     }
00324     case 3: {
00325         TodoView::edit( ui );
00326         break;
00327     }
00328     }
00329 
00330 
00331 }
00332 QWidget* TableView::widget() {
00333     return this;
00334 }
00335 /*
00336  * We need to overwrite sortColumn
00337  * because we want to sort whole row
00338  * based
00339  * We event want to set the setOrder
00340  * to a sort() and update()
00341  */
00342 void TableView::sortColumn( int col, bool asc, bool ) {
00343     switch(col) {
00344     case 1:
00345        col = Opie::OPimTodoAccess::Priority;
00346        break;
00347     case 2:
00348        col = Opie::OPimTodoAccess::SortSummary;
00349        break;
00350     case 3:
00351        col = Opie::OPimTodoAccess::Deadline;
00352        break;
00353     case 0:
00354     default:
00355        col = Opie::OPimTodoAccess::Completed;
00356        break;
00357     }
00358 
00359     setSortOrder( col );
00360     setAscending( asc );
00361     updateView();
00362 }
00363 void TableView::viewportPaintEvent( QPaintEvent* e) {
00364     if (m_enablePaint )
00365         QTable::viewportPaintEvent( e );
00366 }
00367 /*
00368  * This segment is copyrighted by TT
00369  * it was taken from their todolist
00370  * application this code is GPL
00371  */
00372 void TableView::paintCell(QPainter* p,  int row, int col, const QRect& cr, bool ) {
00373     const QColorGroup &cg = colorGroup();
00374 
00375     p->save();
00376 
00377     OPimTodo task = sorted()[row];
00378 
00379     // TODO - give user option for grid or bars?
00380 
00381     // Paint alternating background bars
00382     if (  (row % 2 ) == 0 ) {
00383         p->fillRect( 0, 0, cr.width(), cr.height(), cg.brush( QColorGroup::Base ) );
00384         p->setPen( QPen( cg.text() ) );
00385     }
00386     else {
00387         p->fillRect( 0, 0, cr.width(), cr.height(), cg.brush( QColorGroup::Background ) );
00388         p->setPen( QPen( cg.buttonText() ) );
00389     }
00390 
00391     // Paint grid
00392     //p->fillRect( 0, 0, cr.width(), cr.height(), cg.brush( QColorGroup::Base ) );
00393     //QPen op = p->pen();
00394     //p->setPen(cg.mid());
00395     //p->drawLine( 0, cr.height() - 1, cr.width() - 1, cr.height() - 1 );
00396     //p->drawLine( cr.width() - 1, 0, cr.width() - 1, cr.height() - 1 );
00397     //p->setPen(op);
00398 
00399     QFont f = p->font();
00400     QFontMetrics fm(f);
00401 
00402     int marg = ( cr.width() - BoxSize ) / 2;
00403     int x = 0;
00404     int y = ( cr.height() - BoxSize ) / 2;
00405 
00406     switch(col) {
00407     case 0:  // completed field
00408     {
00409         //p->setPen( QPen( cg.text() ) );
00410         //p->drawRect( x + marg, y, BoxSize, BoxSize );
00411         //p->drawRect( x + marg+1, y+1, BoxSize-2, BoxSize-2 );
00412         if ( task.isCompleted() ) {
00413             p->drawPixmap( x + marg, y, m_pic_completed );
00414         }
00415     }
00416     break;
00417     case 1:  // priority field
00418     {
00419        p->drawPixmap( x + marg, y, m_pic_priority[ task.priority() - 1 ] );
00420     }
00421     break;
00422     case 2:  // description field
00423     {
00424         QString text = task.summary().isEmpty() ?
00425                        task.description().left(20) :
00426                        task.summary();
00427         p->drawText(2,2 + fm.ascent(), text);
00428     }
00429     break;
00430     case 3:
00431     {
00432         QString text;
00433         if (task.hasDueDate()) {
00434             int off = QDate::currentDate().daysTo( task.dueDate() );
00435             text = tr( "%1 day(s)").arg(QString::number(off));
00436             /*
00437              * set color if not completed
00438              */
00439             if (!task.isCompleted() ) {
00440                 QColor color = Qt::black;
00441                 if ( off < 0 )
00442                     color = Qt::red;
00443                 else if ( off == 0 )
00444                     color = Qt::yellow;
00445                 else if ( off > 0 )
00446                     color = Qt::green;
00447                 p->setPen(color  );
00448             }
00449         } else {
00450             text = tr("None");
00451         }
00452         p->drawText(2,2 + fm.ascent(), text);
00453     }
00454     break;
00455     }
00456     p->restore();
00457 }
00458 QWidget* TableView::createEditor(int row, int col, bool )const {
00459     switch( col ) {
00460     case 1: {
00461         /* the priority stuff */
00462         QComboBox* combo = new QComboBox( viewport() );
00463         for ( int i = 0; i < 5; i++ ) {
00464             combo->insertItem( m_pic_priority[ i ] );
00465         }
00466         combo->setCurrentItem( sorted()[row].priority()-1 );
00467         return combo;
00468     }
00469         /* summary */
00470     case 2:{
00471         QLineEdit* edit = new QLineEdit( viewport() );
00472         edit->setText( sorted()[row].summary() );
00473         return edit;
00474     }
00475     case 0:
00476     default:
00477         return 0l;
00478     }
00479 }
00480 void TableView::setCellContentFromEditor(int row, int col ) {
00481     if ( col == 1 ) {
00482         QWidget* wid = cellWidget(row, 1 );
00483         if ( wid->inherits("QComboBox") ) {
00484             int pri = ((QComboBox*)wid)->currentItem() + 1;
00485             OPimTodo todo = sorted()[row];
00486             if ( todo.priority() != pri ) {
00487                 todo.setPriority( pri  );
00488                 TodoView::update( todo.uid(), todo );
00489                 updateView();
00490             }
00491         }
00492     }else if ( col == 2) {
00493         QWidget* wid = cellWidget(row, 2);
00494         if ( wid->inherits("QLineEdit") ) {
00495             QString text = ((QLineEdit*)wid)->text();
00496             OPimTodo todo = sorted()[row];
00497             if ( todo.summary() != text ) {
00498                 todo.setSummary( text );
00499                 TodoView::update( todo.uid(), todo );
00500                 updateView();
00501             }
00502         }
00503     }
00504 }
00505 void TableView::slotPriority() {
00506     setCellContentFromEditor( currentRow(), currentColumn() );
00507 }
00508 /*
00509  * We'll use the TimerEvent to read ahead or to keep the cahce always
00510  * filled enough.
00511  * We will try to read ahead 4 items in both ways
00512  * up and down. On odd or even we will currentRow()+-4 or +-9
00513  *
00514  */
00515 void TableView::timerEvent( QTimerEvent*  ) {
00516     if (sorted().count() == 0 )
00517         return;
00518 
00519     int row = currentRow();
00520     if ( m_row ) {
00521         int ro = row-4;
00522         if (ro < 0 ) ro = 0;
00523         sorted()[ro];
00524 
00525         ro = row+4;
00526         sorted()[ro];
00527     } else {
00528         int ro = row + 8;
00529         sorted()[ro];
00530 
00531         ro = row-8;
00532         if (ro < 0 ) ro = 0;
00533         sorted()[ro];
00534     }
00535 
00536     m_row = !m_row;
00537 }
00538 
00539 // We want  a strike through completed ;)
00540 // durchstreichen to complete
00541 /*
00542  * MouseTracking is off this mean we only receive
00543  * these events if the mouse button is pressed
00544  * We've the previous point saved
00545  * We check if the previous and current Point are
00546  * in the same row.
00547  * Then we check if they're some pixel horizontal away
00548  * if the distance between the two points is greater than
00549  * 8 we mark the underlying todo as completed and do a repaint
00550  *
00551  * BUG: When clicking on the Due column and it's scrollable
00552  * the todo is marked as completed...
00553  * REASON: QTable is doing auto scrolling which leads to a move
00554  * in the x coordinate and this way it's able to pass the
00555  * m_completeStrokeWidth criteria
00556  * WORKAROUND: strike through needs to strike through the same
00557  * row and two columns!
00558  */
00559 void TableView::contentsMouseReleaseEvent( QMouseEvent *e ) {
00560     int row = rowAt(m_prevP.y());
00561     int colOld = columnAt(m_prevP.x() );
00562     int colNew = columnAt(e->x() );
00563     if ( row == rowAt( e->y() ) && row != -1 &&
00564          colOld != colNew ) {
00565         TodoView::complete( sorted()[row] );
00566         return;
00567     }
00568     QTable::contentsMouseReleaseEvent( e );
00569 }
00570 void TableView::contentsMousePressEvent( QMouseEvent *e ) {
00571     if ( e->button() == RightButton ) {
00572         QPopupMenu *menu = todoWindow()->contextMenu( current(), sorted()[currentRow()].recurrence().doesRecur() );
00573         menu->exec( QCursor::pos() );
00574     }
00575     else {
00576         m_prevP = e->pos();
00577         QTable::contentsMousePressEvent( e );
00578     }
00579 }
00580 void TableView::keyPressEvent( QKeyEvent* event) {
00581     if ( m_editorWidget.cellWidget() ) {
00582 //        setCellContentFromEditor(m_editorWidget.cellRow(), m_editorWidget.cellCol() );
00583         endEdit(m_editorWidget.cellRow(), m_editorWidget.cellCol(),
00584                 true, true );
00585         m_editorWidget.releaseCellWidget();
00586         setFocus();
00587     }
00588 
00589     if ( sorted().count() < 1 ) {
00590         QTable::keyPressEvent( event );
00591         return;
00592     }
00593 
00594     int row = currentRow();
00595     int col = currentColumn();
00596 
00597     char key = ::toupper( event->ascii() );
00598     /* let QTable also handle the d letter */
00599     if ( key == 'D'  ) {
00600         event->accept();
00601         removeQuery( sorted().uidAt( row ) );
00602         return;
00603     }
00604 
00605 
00606     switch( event->key() ) {
00607     case Qt::Key_F33:
00608     case Qt::Key_Enter:
00609     case Qt::Key_Return:
00610     case Qt::Key_Space:
00611         if ( col == 0 ) {
00612             TodoView::complete(sorted()[row]);
00613         }else if ( col == 1 ) {
00614             QWidget* wid = beginEdit(row, col, FALSE );
00615             m_editorWidget.setCellWidget( wid, row, col );
00616         }else if ( col == 2 ) {
00617             showTodo( sorted().uidAt( currentRow() ) );
00618         }else if ( col == 3 ) {
00619             TodoView::edit( sorted().uidAt(row) );
00620         }
00621         event->accept();
00622         break;
00623     default:
00624         QTable::keyPressEvent( event );
00625     }
00626 }
00627 

Generated on Sat Nov 5 16:15:55 2005 for OPIE by  doxygen 1.4.2