KDEUI
kdatetable.cpp
Go to the documentation of this file.
00001 /* -*- C++ -*- 00002 This file is part of the KDE libraries 00003 Copyright (C) 1997 Tim D. Gilman (tdgilman@best.org) 00004 (C) 1998-2001 Mirko Boehm (mirko@kde.org) 00005 (C) 2007 John Layt <john@layt.net> 00006 This library is free software; you can redistribute it and/or 00007 modify it under the terms of the GNU Library General Public 00008 License as published by the Free Software Foundation; either 00009 version 2 of the License, or (at your option) any later version. 00010 00011 This library is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 Library General Public License for more details. 00015 00016 You should have received a copy of the GNU Library General Public License 00017 along with this library; see the file COPYING.LIB. If not, write to 00018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 Boston, MA 02110-1301, USA. 00020 */ 00021 00022 #include "kdatetable.h" 00023 00024 #include <kconfig.h> 00025 #include <kcolorscheme.h> 00026 #include <kglobal.h> 00027 #include <kglobalsettings.h> 00028 #include <kdebug.h> 00029 #include <knotification.h> 00030 #include <kcalendarsystem.h> 00031 #include <klocalizeddate.h> 00032 #include <kshortcut.h> 00033 #include <kstandardshortcut.h> 00034 #include "kdatepicker.h" 00035 #include "kmenu.h" 00036 #include "kactioncollection.h" 00037 #include "kaction.h" 00038 #include <kdeversion.h> 00039 00040 #include <QtCore/QDate> 00041 #include <QtCore/QCharRef> 00042 #include <QtGui/QPen> 00043 #include <QtGui/QPainter> 00044 #include <QtGui/QStyle> 00045 #include <QtGui/QStyleOptionViewItem> 00046 #include <QtGui/QDialog> 00047 #include <QtGui/QActionEvent> 00048 #include <QtCore/QHash> 00049 #include <QtGui/QApplication> 00050 #include <assert.h> 00051 00052 #include <cmath> 00053 00054 class KDateTable::KDateTablePrivate 00055 { 00056 public: 00057 KDateTablePrivate( KDateTable *q ): q( q ) 00058 { 00059 m_popupMenuEnabled = false; 00060 m_useCustomColors = false; 00061 m_hoveredPos = -1; 00062 setDate( QDate::currentDate() ); 00063 } 00064 00065 ~KDateTablePrivate() 00066 { 00067 } 00068 00069 void setDate( const QDate& date ); 00070 void nextMonth(); 00071 void previousMonth(); 00072 void beginningOfMonth(); 00073 void endOfMonth(); 00074 void beginningOfWeek(); 00075 void endOfWeek(); 00076 00077 KDateTable *q; 00078 00082 int fontsize; 00083 00087 KLocalizedDate m_date; 00088 // Need to keep a QDate copy as the "const QDate &date() const;" method returns a reference 00089 // and returning m_date.date() creates a temporary leading to crashes. Doh! 00090 QDate m_refDate; 00091 00095 int m_weekDayFirstOfMonth; 00096 00100 int m_numDaysThisMonth; 00101 00105 QRectF m_maxCell; 00106 00110 int m_numWeekRows; 00111 00115 int m_numDayColumns; 00116 00117 bool m_popupMenuEnabled; 00118 bool m_useCustomColors; 00119 00120 struct DatePaintingMode 00121 { 00122 QColor fgColor; 00123 QColor bgColor; 00124 BackgroundMode bgMode; 00125 }; 00126 QHash <int, DatePaintingMode*> m_customPaintingModes; 00127 00128 int m_hoveredPos; 00129 }; 00130 00131 00132 class KPopupFrame::KPopupFramePrivate 00133 { 00134 public: 00135 KPopupFramePrivate( KPopupFrame *q ); 00136 ~KPopupFramePrivate(); 00137 00138 KPopupFrame *q; 00139 00143 int result; 00144 00148 QWidget *main; 00149 00150 // ### KDE 5: Remove this, add a hideEvent() reimplementation instead. 00151 class OutsideClickCatcher; 00152 OutsideClickCatcher *outsideClickCatcher; 00153 }; 00154 00155 00156 class KPopupFrame::KPopupFramePrivate::OutsideClickCatcher 00157 : public QObject 00158 { 00159 public: 00160 OutsideClickCatcher(QObject *parent = 0) 00161 : QObject(parent), m_popup(0) { } 00162 ~OutsideClickCatcher() { } 00163 00164 void setPopupFrame(KPopupFrame *popup) 00165 { 00166 m_popup = popup; 00167 popup->installEventFilter(this); 00168 } 00169 00170 KPopupFrame *m_popup; 00171 00172 bool eventFilter(QObject *object, QEvent *event) 00173 { 00174 Q_UNUSED(object); 00175 00176 // To catch outside clicks, it is sufficient to check for 00177 // hide events on Qt::Popup type widgets 00178 if (event->type() == QEvent::Hide && m_popup) { 00179 // do not set d->result here, because the popup 00180 // hides itself after leaving the event loop. 00181 emit m_popup->leaveModality(); 00182 } 00183 return false; 00184 } 00185 }; 00186 00187 00188 KPopupFrame::KPopupFramePrivate::KPopupFramePrivate( KPopupFrame *q ): 00189 q( q ), 00190 result( 0 ), // rejected 00191 main( 0 ), 00192 outsideClickCatcher(new OutsideClickCatcher) 00193 { 00194 outsideClickCatcher->setPopupFrame(q); 00195 } 00196 00197 KPopupFrame::KPopupFramePrivate::~KPopupFramePrivate() 00198 { 00199 delete outsideClickCatcher; 00200 } 00201 00202 00203 class KDateValidator::KDateValidatorPrivate 00204 { 00205 public: 00206 KDateValidatorPrivate( KDateValidator *q ): q( q ) 00207 { 00208 } 00209 00210 ~KDateValidatorPrivate() 00211 { 00212 } 00213 00214 KDateValidator *q; 00215 }; 00216 00217 KDateValidator::KDateValidator( QWidget *parent ) : QValidator( parent ), d( 0 ) 00218 { 00219 } 00220 00221 QValidator::State KDateValidator::validate( QString &text, int &unused ) const 00222 { 00223 Q_UNUSED( unused ); 00224 00225 QDate temp; 00226 // ----- everything is tested in date(): 00227 return date( text, temp ); 00228 } 00229 00230 QValidator::State KDateValidator::date( const QString &text, QDate &d ) const 00231 { 00232 //FIXME This is wrong if the widget is not using the global! 00233 QDate tmp = KGlobal::locale()->readDate( text ); 00234 if ( KGlobal::locale()->calendar()->isValid( tmp ) ) { 00235 d = tmp; 00236 return Acceptable; 00237 } else { 00238 return QValidator::Intermediate; 00239 } 00240 } 00241 00242 void KDateValidator::fixup( QString& ) const 00243 { 00244 } 00245 00246 KDateTable::KDateTable( const QDate& date, QWidget* parent ) 00247 : QWidget( parent ), 00248 d( new KDateTablePrivate( this ) ) 00249 { 00250 init( date ); 00251 } 00252 00253 KDateTable::KDateTable( QWidget *parent ) 00254 : QWidget( parent ), 00255 d( new KDateTablePrivate( this ) ) 00256 { 00257 init( QDate::currentDate() ); 00258 } 00259 00260 KDateTable::~KDateTable() 00261 { 00262 delete d; 00263 } 00264 00265 void KDateTable::init( const QDate &date ) 00266 { 00267 d->m_numWeekRows = 7; 00268 00269 setFontSize( 10 ); 00270 setFocusPolicy( Qt::StrongFocus ); 00271 setBackgroundRole(QPalette::Base); 00272 setAutoFillBackground(true); 00273 initAccels(); 00274 setAttribute(Qt::WA_Hover, true); 00275 00276 setDate( date ); 00277 } 00278 00279 void KDateTable::initAccels() 00280 { 00281 KActionCollection * localCollection = new KActionCollection( this ); 00282 00283 KAction* next = localCollection->addAction( QLatin1String( "next" ) ); 00284 next->setShortcuts( KStandardShortcut::next() ); 00285 connect( next, SIGNAL( triggered( bool ) ), SLOT( nextMonth() ) ); 00286 00287 KAction* prior = localCollection->addAction( QLatin1String( "prior" ) ); 00288 prior->setShortcuts( KStandardShortcut::prior() ); 00289 connect( prior, SIGNAL( triggered( bool ) ), SLOT( previousMonth() ) ); 00290 00291 KAction* beginMonth = localCollection->addAction( QLatin1String( "beginMonth" ) ); 00292 beginMonth->setShortcuts( KStandardShortcut::begin() ); 00293 connect( beginMonth, SIGNAL( triggered( bool ) ), SLOT( beginningOfMonth() ) ); 00294 00295 KAction* endMonth = localCollection->addAction( QLatin1String( "endMonth" ) ); 00296 endMonth->setShortcuts( KStandardShortcut::end() ); 00297 connect( endMonth, SIGNAL( triggered( bool ) ), SLOT( endOfMonth() ) ); 00298 00299 KAction* beginWeek = localCollection->addAction( QLatin1String( "beginWeek" ) ); 00300 beginWeek->setShortcuts( KStandardShortcut::beginningOfLine() ); 00301 connect( beginWeek, SIGNAL( triggered( bool ) ), SLOT( beginningOfWeek() ) ); 00302 00303 KAction* endWeek = localCollection->addAction( "endWeek" ); 00304 endWeek->setShortcuts( KStandardShortcut::endOfLine() ); 00305 connect( endWeek, SIGNAL( triggered( bool ) ), SLOT( endOfWeek() ) ); 00306 00307 localCollection->readSettings(); 00308 localCollection->addAssociatedWidget( this ); 00309 foreach (QAction* action, localCollection->actions()) { 00310 action->setShortcutContext(Qt::WidgetWithChildrenShortcut); 00311 } 00312 } 00313 00314 int KDateTable::posFromDate( const QDate &date ) 00315 { 00316 int initialPosition = calendar()->day( date ); 00317 int offset = ( d->m_weekDayFirstOfMonth - calendar()->weekStartDay() + d->m_numDayColumns ) % d->m_numDayColumns; 00318 00319 // make sure at least one day of the previous month is visible. 00320 // adjust this < 1 if more days should be forced visible: 00321 if ( offset < 1 ) { 00322 offset += d->m_numDayColumns; 00323 } 00324 00325 return initialPosition + offset; 00326 } 00327 00328 QDate KDateTable::dateFromPos( int position ) 00329 { 00330 int offset = ( d->m_weekDayFirstOfMonth - calendar()->weekStartDay() + d->m_numDayColumns ) % d->m_numDayColumns; 00331 00332 // make sure at least one day of the previous month is visible. 00333 // adjust this < 1 if more days should be forced visible: 00334 if ( offset < 1 ) { 00335 offset += d->m_numDayColumns; 00336 } 00337 00338 return d->m_date.firstDayOfMonth().addDays( position - offset ).date(); 00339 } 00340 00341 void KDateTable::paintEvent( QPaintEvent *e ) 00342 { 00343 QPainter p( this ); 00344 KColorScheme colorScheme(palette().currentColorGroup(), KColorScheme::View); 00345 const QRect &rectToUpdate = e->rect(); 00346 double cellWidth = width() / ( double ) d->m_numDayColumns; 00347 double cellHeight = height() / ( double ) d->m_numWeekRows; 00348 int leftCol = ( int )std::floor( rectToUpdate.left() / cellWidth ); 00349 int topRow = ( int )std::floor( rectToUpdate.top() / cellHeight ); 00350 int rightCol = ( int )std::ceil( rectToUpdate.right() / cellWidth ); 00351 int bottomRow = ( int )std::ceil( rectToUpdate.bottom() / cellHeight ); 00352 bottomRow = qMin( bottomRow, d->m_numWeekRows - 1 ); 00353 rightCol = qMin( rightCol, d->m_numDayColumns - 1 ); 00354 if ( layoutDirection() == Qt::RightToLeft ) { 00355 p.translate( ( d->m_numDayColumns - leftCol - 1 ) * cellWidth, topRow * cellHeight ); 00356 } else { 00357 p.translate( leftCol * cellWidth, topRow * cellHeight ); 00358 } 00359 for ( int i = leftCol; i <= rightCol; ++i ) { 00360 for ( int j = topRow; j <= bottomRow; ++j ) { 00361 paintCell( &p, j, i, colorScheme ); 00362 p.translate( 0, cellHeight ); 00363 } 00364 if ( layoutDirection() == Qt::RightToLeft ) { 00365 p.translate( -cellWidth, 0 ); 00366 } else { 00367 p.translate( cellWidth, 0 ); 00368 } 00369 p.translate( 0, -cellHeight * ( bottomRow - topRow + 1 ) ); 00370 } 00371 } 00372 00373 void KDateTable::paintCell( QPainter *painter, int row, int col, const KColorScheme &colorScheme ) 00374 { 00375 double w = ( width() / ( double ) d->m_numDayColumns ) - 1; 00376 double h = ( height() / ( double ) d->m_numWeekRows ) - 1; 00377 QRectF cell = QRectF( 0, 0, w, h ); 00378 QString cellText; 00379 QPen pen; 00380 QColor cellBackgroundColor, cellTextColor; 00381 QFont cellFont = KGlobalSettings::generalFont(); 00382 bool workingDay = false; 00383 int cellWeekDay, pos; 00384 BackgroundMode cellBackgroundMode = RectangleMode; 00385 00386 //Calculate the position of the cell in the grid 00387 pos = d->m_numDayColumns * ( row - 1 ) + col; 00388 00389 //Calculate what day of the week the cell is 00390 if ( col + calendar()->weekStartDay() <= d->m_numDayColumns ) { 00391 cellWeekDay = col + calendar()->weekStartDay(); 00392 } else { 00393 cellWeekDay = col + calendar()->weekStartDay() - d->m_numDayColumns; 00394 } 00395 00396 //FIXME This is wrong if the widget is not using the global! 00397 //See if cell day is normally a working day 00398 if ( KGlobal::locale()->workingWeekStartDay() <= KGlobal::locale()->workingWeekEndDay() ) { 00399 if ( cellWeekDay >= KGlobal::locale()->workingWeekStartDay() && 00400 cellWeekDay <= KGlobal::locale()->workingWeekEndDay() ) { 00401 workingDay = true; 00402 } 00403 } else { 00404 if ( cellWeekDay >= KGlobal::locale()->workingWeekStartDay() || 00405 cellWeekDay <= KGlobal::locale()->workingWeekEndDay() ) { 00406 workingDay = true; 00407 } 00408 } 00409 00410 if( row == 0 ) { 00411 00412 //We are drawing a header cell 00413 00414 //If not a normal working day, then use "do not work today" color 00415 if ( workingDay ) { 00416 cellTextColor = palette().color(QPalette::WindowText); 00417 } else { 00418 KColorScheme colorScheme(palette().currentColorGroup(), KColorScheme::Window); 00419 cellTextColor = colorScheme.foreground(KColorScheme::NegativeText).color(); 00420 } 00421 cellBackgroundColor = palette().color(QPalette::Window); 00422 00423 //Set the text to the short day name and bold it 00424 cellFont.setBold( true ); 00425 cellText = calendar()->weekDayName( cellWeekDay, KCalendarSystem::ShortDayName ); 00426 00427 } else { 00428 00429 //We are drawing a day cell 00430 00431 //Calculate the date the cell represents 00432 //Copy current date to get same calendar system & locale 00433 KLocalizedDate cellDate = d->m_date; 00434 cellDate = dateFromPos( pos ); 00435 00436 bool validDay = cellDate.isValid(); 00437 00438 // Draw the day number in the cell, if the date is not valid then we don't want to show it 00439 if ( validDay ) { 00440 cellText = cellDate.formatDate( KLocale::Day, KLocale::ShortNumber ); 00441 } else { 00442 cellText = ""; 00443 } 00444 00445 if( ! validDay || cellDate.month() != d->m_date.month() ) { 00446 // we are either 00447 // ° painting an invalid day 00448 // ° painting a day of the previous month or 00449 // ° painting a day of the following month or 00450 cellBackgroundColor = palette().color(backgroundRole()); 00451 cellTextColor = colorScheme.foreground(KColorScheme::InactiveText).color(); 00452 } else { 00453 //Paint a day of the current month 00454 00455 // Background Colour priorities will be (high-to-low): 00456 // * Selected Day Background Colour 00457 // * Customized Day Background Colour 00458 // * Normal Day Background Colour 00459 00460 // Background Shape priorities will be (high-to-low): 00461 // * Customized Day Shape 00462 // * Normal Day Shape 00463 00464 // Text Colour priorities will be (high-to-low): 00465 // * Customized Day Colour 00466 // * Day of Pray Colour (Red letter) 00467 // * Selected Day Colour 00468 // * Normal Day Colour 00469 00470 //Determine various characteristics of the cell date 00471 bool selectedDay = ( cellDate == date() ); 00472 bool currentDay = ( cellDate == QDate::currentDate() ); 00473 bool dayOfPray = ( cellDate.dayOfWeek() == calendar()->weekDayOfPray() ); 00474 bool customDay = ( d->m_useCustomColors && d->m_customPaintingModes.contains(cellDate.toJulianDay()) ); 00475 00476 //Default values for a normal cell 00477 cellBackgroundColor = palette().color( backgroundRole() ); 00478 cellTextColor = palette().color( foregroundRole() ); 00479 00480 // If we are drawing the current date, then draw it bold and active 00481 if ( currentDay ) { 00482 cellFont.setBold( true ); 00483 cellTextColor = colorScheme.foreground(KColorScheme::ActiveText).color(); 00484 } 00485 00486 // if we are drawing the day cell currently selected in the table 00487 if ( selectedDay ) { 00488 // set the background to highlighted 00489 cellBackgroundColor = palette().color( QPalette::Highlight ); 00490 cellTextColor = palette().color( QPalette::HighlightedText ); 00491 } 00492 00493 //If custom colors or shape are required for this date 00494 if ( customDay ) { 00495 KDateTablePrivate::DatePaintingMode * mode = d->m_customPaintingModes[cellDate.toJulianDay()]; 00496 if ( mode->bgMode != NoBgMode ) { 00497 cellBackgroundMode = mode->bgMode; 00498 if (!selectedDay) cellBackgroundColor = mode->bgColor; 00499 } 00500 cellTextColor = mode->fgColor; 00501 } 00502 00503 //If the cell day is the day of religious observance, then always color text red unless Custom overrides 00504 if ( ! customDay && dayOfPray ) { 00505 KColorScheme colorScheme(palette().currentColorGroup(), 00506 selectedDay ? KColorScheme::Selection : KColorScheme::View); 00507 cellTextColor = colorScheme.foreground(KColorScheme::NegativeText).color(); 00508 } 00509 00510 } 00511 } 00512 00513 //Draw the background 00514 if (row == 0) { 00515 painter->setPen( cellBackgroundColor ); 00516 painter->setBrush( cellBackgroundColor ); 00517 painter->drawRect( cell ); 00518 } else if (cellBackgroundColor != palette().color(backgroundRole()) || pos == d->m_hoveredPos) { 00519 QStyleOptionViewItemV4 opt; 00520 opt.initFrom(this); 00521 opt.rect = cell.toRect(); 00522 if (cellBackgroundColor != palette().color(backgroundRole())) { 00523 opt.palette.setBrush(QPalette::Highlight, cellBackgroundColor); 00524 opt.state |= QStyle::State_Selected; 00525 } 00526 if (pos == d->m_hoveredPos && opt.state & QStyle::State_Enabled) { 00527 opt.state |= QStyle::State_MouseOver; 00528 } else { 00529 opt.state &= ~QStyle::State_MouseOver; 00530 } 00531 opt.showDecorationSelected = true; 00532 opt.viewItemPosition = QStyleOptionViewItemV4::OnlyOne; 00533 style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, this); 00534 } 00535 00536 //Draw the text 00537 painter->setPen( cellTextColor ); 00538 painter->setFont( cellFont ); 00539 painter->drawText( cell, Qt::AlignCenter, cellText, &cell ); 00540 00541 //Draw the base line 00542 if (row == 0) { 00543 painter->setPen( palette().color(foregroundRole()) ); 00544 painter->drawLine( QPointF( 0, h ), QPointF( w, h ) ); 00545 } 00546 00547 // If the day cell we just drew is bigger than the current max cell sizes, 00548 // then adjust the max to the current cell 00549 if ( cell.width() > d->m_maxCell.width() ) d->m_maxCell.setWidth( cell.width() ); 00550 if ( cell.height() > d->m_maxCell.height() ) d->m_maxCell.setHeight( cell.height() ); 00551 } 00552 00553 void KDateTable::KDateTablePrivate::nextMonth() 00554 { 00555 // setDate does validity checking for us 00556 q->setDate( m_date.addMonths( 1 ).date() ); 00557 } 00558 00559 void KDateTable::KDateTablePrivate::previousMonth() 00560 { 00561 // setDate does validity checking for us 00562 q->setDate( m_date.addMonths( -1 ).date() ); 00563 } 00564 00565 void KDateTable::KDateTablePrivate::beginningOfMonth() 00566 { 00567 // setDate does validity checking for us 00568 q->setDate( m_date.firstDayOfMonth().date() ); 00569 } 00570 00571 void KDateTable::KDateTablePrivate::endOfMonth() 00572 { 00573 // setDate does validity checking for us 00574 q->setDate( m_date.lastDayOfMonth().date() ); 00575 } 00576 00577 // JPL Do these make the assumption that first day of week is weekday 1? As it may not be. 00578 void KDateTable::KDateTablePrivate::beginningOfWeek() 00579 { 00580 // setDate does validity checking for us 00581 q->setDate( m_date.addDays( 1 - m_date.dayOfWeek() ).date() ); 00582 } 00583 00584 // JPL Do these make the assumption that first day of week is weekday 1? As it may not be. 00585 void KDateTable::KDateTablePrivate::endOfWeek() 00586 { 00587 // setDate does validity checking for us 00588 q->setDate( m_date.addDays( m_date.daysInWeek() - m_date.dayOfWeek() ).date() ); 00589 } 00590 00591 void KDateTable::keyPressEvent( QKeyEvent *e ) 00592 { 00593 switch( e->key() ) { 00594 case Qt::Key_Up: 00595 // setDate does validity checking for us 00596 setDate( d->m_date.addDays( - d->m_numDayColumns ).date() ); 00597 break; 00598 case Qt::Key_Down: 00599 // setDate does validity checking for us 00600 setDate( d->m_date.addDays( d->m_numDayColumns ).date() ); 00601 break; 00602 case Qt::Key_Left: 00603 // setDate does validity checking for us 00604 setDate( d->m_date.addDays( -1 ).date() ); 00605 break; 00606 case Qt::Key_Right: 00607 // setDate does validity checking for us 00608 setDate( d->m_date.addDays( 1 ).date() ); 00609 break; 00610 case Qt::Key_Minus: 00611 // setDate does validity checking for us 00612 setDate( d->m_date.addDays( -1 ).date() ); 00613 break; 00614 case Qt::Key_Plus: 00615 // setDate does validity checking for us 00616 setDate( d->m_date.addDays( 1 ).date() ); 00617 break; 00618 case Qt::Key_N: 00619 // setDate does validity checking for us 00620 setDate( QDate::currentDate() ); 00621 break; 00622 case Qt::Key_Return: 00623 case Qt::Key_Enter: 00624 emit tableClicked(); 00625 break; 00626 case Qt::Key_Control: 00627 case Qt::Key_Alt: 00628 case Qt::Key_Meta: 00629 case Qt::Key_Shift: 00630 // Don't beep for modifiers 00631 break; 00632 default: 00633 if ( !e->modifiers() ) { // hm 00634 KNotification::beep(); 00635 } 00636 } 00637 } 00638 00639 void KDateTable::setFontSize( int size ) 00640 { 00641 QFontMetricsF metrics( fontMetrics() ); 00642 QRectF rect; 00643 // ----- store rectangles: 00644 d->fontsize = size; 00645 // ----- find largest day name: 00646 d->m_maxCell.setWidth( 0 ); 00647 d->m_maxCell.setHeight( 0 ); 00648 for( int weekday = 1; weekday <= d->m_date.daysInWeek(); ++weekday ) { 00649 rect = metrics.boundingRect( calendar()->weekDayName( weekday, KCalendarSystem::ShortDayName ) ); 00650 d->m_maxCell.setWidth( qMax( d->m_maxCell.width(), rect.width() ) ); 00651 d->m_maxCell.setHeight( qMax( d->m_maxCell.height(), rect.height() ) ); 00652 } 00653 // ----- compare with a real wide number and add some space: 00654 rect = metrics.boundingRect( QLatin1String( "88" ) ); 00655 d->m_maxCell.setWidth( qMax( d->m_maxCell.width() + 2, rect.width() ) ); 00656 d->m_maxCell.setHeight( qMax( d->m_maxCell.height() + 4, rect.height() ) ); 00657 } 00658 00659 void KDateTable::wheelEvent ( QWheelEvent * e ) 00660 { 00661 setDate( d->m_date.addMonths( -( int )( e->delta() / 120 ) ).date() ); 00662 e->accept(); 00663 } 00664 00665 bool KDateTable::event(QEvent *ev) 00666 { 00667 switch (ev->type()) { 00668 case QEvent::HoverMove: 00669 { 00670 QHoverEvent *e = static_cast<QHoverEvent *>(ev); 00671 const int row = e->pos().y() * d->m_numWeekRows / height(); 00672 int col; 00673 if ( layoutDirection() == Qt::RightToLeft ) { 00674 col = d->m_numDayColumns - ( e->pos().x() * d->m_numDayColumns / width() ) - 1; 00675 } else { 00676 col = e->pos().x() * d->m_numDayColumns / width(); 00677 } 00678 00679 const int pos = row < 1 ? -1 : (d->m_numDayColumns * (row - 1)) + col; 00680 00681 if (pos != d->m_hoveredPos) { 00682 d->m_hoveredPos = pos; 00683 update(); 00684 } 00685 break; 00686 } 00687 case QEvent::HoverLeave: 00688 if (d->m_hoveredPos != -1) { 00689 d->m_hoveredPos = -1; 00690 update(); 00691 } 00692 break; 00693 default: 00694 break; 00695 } 00696 return QWidget::event(ev); 00697 } 00698 00699 void KDateTable::mousePressEvent( QMouseEvent *e ) 00700 { 00701 if( e->type() != QEvent::MouseButtonPress ) { // the KDatePicker only reacts on mouse press events: 00702 return; 00703 } 00704 00705 if( !isEnabled() ) { 00706 KNotification::beep(); 00707 return; 00708 } 00709 00710 int row, col, pos, temp; 00711 00712 QPoint mouseCoord = e->pos(); 00713 row = mouseCoord.y() * d->m_numWeekRows / height(); 00714 if ( layoutDirection() == Qt::RightToLeft ) { 00715 col = d->m_numDayColumns - ( mouseCoord.x() * d->m_numDayColumns / width() ) - 1; 00716 } else { 00717 col = mouseCoord.x() * d->m_numDayColumns / width(); 00718 } 00719 00720 if( row < 1 || col < 0 ) { // the user clicked on the frame of the table 00721 return; 00722 } 00723 00724 // Rows and columns are zero indexed. The (row - 1) below is to avoid counting 00725 // the row with the days of the week in the calculation. 00726 00727 // old selected date: 00728 temp = posFromDate( date() ); 00729 00730 // new position and date 00731 pos = ( d->m_numDayColumns * ( row - 1 ) ) + col; 00732 QDate clickedDate = dateFromPos( pos ); 00733 00734 // set the new date. If it is in the previous or next month, the month will 00735 // automatically be changed, no need to do that manually... 00736 // validity checking done inside setDate 00737 setDate( clickedDate ); 00738 00739 // This could be optimized to only call update over the regions 00740 // of old and new cell, but 99% of times there is also a call to 00741 // setDate that already calls update() so no need to optimize that 00742 // much here 00743 update(); 00744 00745 emit tableClicked(); 00746 00747 if ( e->button() == Qt::RightButton && d->m_popupMenuEnabled ) { 00748 KMenu * menu = new KMenu(); 00749 menu->addTitle( d->m_date.formatDate() ); 00750 emit aboutToShowContextMenu( menu, clickedDate ); 00751 menu->popup( e->globalPos() ); 00752 } 00753 } 00754 00755 void KDateTable::KDateTablePrivate::setDate( const QDate& date ) 00756 { 00757 m_date.setDate( date ); 00758 m_refDate = date; 00759 m_weekDayFirstOfMonth = m_date.firstDayOfMonth().dayOfWeek(); 00760 m_numDaysThisMonth = m_date.daysInMonth(); 00761 m_numDayColumns = m_date.daysInWeek(); 00762 } 00763 00764 bool KDateTable::setDate( const QDate& toDate ) 00765 { 00766 if ( !calendar()->isValid( toDate ) ) { 00767 return false; 00768 } 00769 00770 if ( toDate == date() ) { 00771 return true; 00772 } 00773 00774 QDate oldDate = date(); 00775 d->setDate( toDate ); 00776 emit( dateChanged( date(), oldDate ) ); 00777 emit( dateChanged( date() ) ); 00778 update(); 00779 00780 return true; 00781 } 00782 00783 const QDate &KDateTable::date() const 00784 { 00785 return d->m_refDate; 00786 } 00787 00788 const KCalendarSystem *KDateTable::calendar() const 00789 { 00790 return d->m_date.calendar(); 00791 } 00792 00793 bool KDateTable::setCalendar( KCalendarSystem *newCalendar ) 00794 { 00795 QDate oldDate = date(); 00796 d->m_date = KLocalizedDate( oldDate, newCalendar ); 00797 return setDate( oldDate ); 00798 } 00799 00800 bool KDateTable::setCalendar( const QString &newCalendarType ) 00801 { 00802 return setCalendarSystem( KCalendarSystem::calendarSystemForCalendarType( newCalendarType ) ); 00803 } 00804 00805 bool KDateTable::setCalendarSystem( KLocale::CalendarSystem newCalendarSystem ) 00806 { 00807 d->m_date.setCalendarSystem( newCalendarSystem ); 00808 return true; 00809 } 00810 00811 void KDateTable::focusInEvent( QFocusEvent *e ) 00812 { 00813 QWidget::focusInEvent( e ); 00814 } 00815 00816 void KDateTable::focusOutEvent( QFocusEvent *e ) 00817 { 00818 QWidget::focusOutEvent( e ); 00819 } 00820 00821 QSize KDateTable::sizeHint() const 00822 { 00823 if( d->m_maxCell.height() > 0 && d->m_maxCell.width() > 0 ) { 00824 return QSize( qRound( d->m_maxCell.width() * d->m_numDayColumns ), 00825 ( qRound( d->m_maxCell.height() + 2 ) * d->m_numWeekRows ) ); 00826 } else { 00827 kDebug() << "KDateTable::sizeHint: obscure failure - " << endl; 00828 return QSize( -1, -1 ); 00829 } 00830 } 00831 00832 void KDateTable::setPopupMenuEnabled( bool enable ) 00833 { 00834 d->m_popupMenuEnabled = enable; 00835 } 00836 00837 bool KDateTable::popupMenuEnabled() const 00838 { 00839 return d->m_popupMenuEnabled; 00840 } 00841 00842 void KDateTable::setCustomDatePainting( const QDate &date, const QColor &fgColor, BackgroundMode bgMode, const QColor &bgColor ) 00843 { 00844 if ( !fgColor.isValid() ) { 00845 unsetCustomDatePainting( date ); 00846 return; 00847 } 00848 00849 KDateTablePrivate::DatePaintingMode *mode = new KDateTablePrivate::DatePaintingMode; 00850 mode->bgMode = bgMode; 00851 mode->fgColor = fgColor; 00852 mode->bgColor = bgColor; 00853 00854 d->m_customPaintingModes.insert( date.toJulianDay(), mode ); 00855 d->m_useCustomColors = true; 00856 update(); 00857 } 00858 00859 void KDateTable::unsetCustomDatePainting( const QDate &date ) 00860 { 00861 d->m_customPaintingModes.remove( date.toJulianDay() ); 00862 if ( d->m_customPaintingModes.isEmpty() ) d->m_useCustomColors = false; 00863 update(); 00864 } 00865 00866 00867 // JPL Shouldn't this be in own file as is used in a couple of places? Or moved to private in KDE5? 00868 00869 KPopupFrame::KPopupFrame( QWidget* parent ) 00870 : QFrame( parent, Qt::Popup ), d( new KPopupFramePrivate( this ) ) 00871 { 00872 setFrameStyle( QFrame::Box | QFrame::Raised ); 00873 setMidLineWidth( 2 ); 00874 } 00875 00876 KPopupFrame::~KPopupFrame() 00877 { 00878 delete d; 00879 } 00880 00881 void KPopupFrame::keyPressEvent( QKeyEvent* e ) 00882 { 00883 if( e->key() == Qt::Key_Escape ) { 00884 d->result = 0; // rejected 00885 emit leaveModality(); 00886 //qApp->exit_loop(); 00887 } 00888 } 00889 00890 void KPopupFrame::close( int r ) 00891 { 00892 d->result = r; 00893 emit leaveModality(); 00894 //qApp->exit_loop(); 00895 } 00896 00897 void KPopupFrame::setMainWidget( QWidget *m ) 00898 { 00899 d->main = m; 00900 if( d->main ) { 00901 resize( d->main->width() + 2 * frameWidth(), d->main->height() + 2 * frameWidth() ); 00902 } 00903 } 00904 00905 void KPopupFrame::resizeEvent( QResizeEvent *e ) 00906 { 00907 Q_UNUSED( e ); 00908 00909 if( d->main ) { 00910 d->main->setGeometry( frameWidth(), frameWidth(), 00911 width() - 2 * frameWidth(), height() - 2 * frameWidth() ); 00912 } 00913 } 00914 00915 void KPopupFrame::popup( const QPoint &pos ) 00916 { 00917 // Make sure the whole popup is visible. 00918 QRect desktopGeometry = KGlobalSettings::desktopGeometry( pos ); 00919 00920 int x = pos.x(); 00921 int y = pos.y(); 00922 int w = width(); 00923 int h = height(); 00924 if ( x + w > desktopGeometry.x() + desktopGeometry.width() ) { 00925 x = desktopGeometry.width() - w; 00926 } 00927 if ( y + h > desktopGeometry.y() + desktopGeometry.height() ) { 00928 y = desktopGeometry.height() - h; 00929 } 00930 if ( x < desktopGeometry.x() ) { 00931 x = 0; 00932 } 00933 if ( y < desktopGeometry.y() ) { 00934 y = 0; 00935 } 00936 00937 // Pop the thingy up. 00938 move( x, y ); 00939 show(); 00940 d->main->setFocus(); 00941 } 00942 00943 int KPopupFrame::exec( const QPoint &pos ) 00944 { 00945 popup( pos ); 00946 repaint(); 00947 d->result = 0; // rejected 00948 QEventLoop eventLoop; 00949 connect( this, SIGNAL( leaveModality() ), 00950 &eventLoop, SLOT( quit() ) ); 00951 eventLoop.exec(); 00952 00953 hide(); 00954 return d->result; 00955 } 00956 00957 int KPopupFrame::exec( int x, int y ) 00958 { 00959 return exec( QPoint( x, y ) ); 00960 } 00961 00962 #include "kdatetable.moc"
KDE 4.6 API Reference