libyui-qt
2.43.5
|
00001 /* 00002 Copyright (C) 2000-2012 Novell, Inc 00003 This library is free software; you can redistribute it and/or modify 00004 it under the terms of the GNU Lesser General Public License as 00005 published by the Free Software Foundation; either version 2.1 of the 00006 License, or (at your option) version 3.0 of the License. This library 00007 is distributed in the hope that it will be useful, but WITHOUT ANY 00008 WARRANTY; without even the implied warranty of MERCHANTABILITY or 00009 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 00010 License for more details. You should have received a copy of the GNU 00011 Lesser General Public License along with this library; if not, write 00012 to the Free Software Foundation, Inc., 51 Franklin Street, Fifth 00013 Floor, Boston, MA 02110-1301 USA 00014 */ 00015 00016 00017 /*-/ 00018 00019 File: YQTimezoneSelector.cc 00020 00021 Author: Stephan Kulow <coolo@suse.de> 00022 00023 /-*/ 00024 00025 00026 #define YUILogComponent "qt-ui" 00027 #include <yui/YUILog.h> 00028 #include <math.h> 00029 00030 #include <qdatetimeedit.h> 00031 00032 #include "utf8.h" 00033 #include "YQUI.h" 00034 #include "YQTimezoneSelector.h" 00035 #include "YQWidgetCaption.h" 00036 #include <yui/YEvent.h> 00037 #include <QVBoxLayout> 00038 #include <QPainter> 00039 #include <QMouseEvent> 00040 #include <QDebug> 00041 #include <QToolTip> 00042 00043 #include "icons/zoom-in.xpm" 00044 00045 class YQTimezoneSelectorPrivate 00046 { 00047 QWidget *parent; 00048 00049 public: 00050 00051 YQTimezoneSelectorPrivate( QWidget *p ) { 00052 parent = p; 00053 blink = 0; 00054 highlight = 0; 00055 } 00056 QImage _pix; 00057 QPoint _zoom; 00058 00059 struct Location 00060 { 00061 QString country; 00062 double latitude; 00063 double longitude; 00064 QString zone; 00065 QString comment; 00066 QString tip; 00067 00068 QPoint pix_pos; 00069 00070 bool operator<(const Location& l2) const; 00071 }; 00072 00073 Location _best; 00074 00075 QList<Location> locations; 00076 00077 Location findBest( const QPoint &pos ) const; 00078 00079 QTimer *blink; 00080 00081 int highlight; 00082 00083 QPoint pixPosition( const Location &pos ) const; 00084 00085 QPoint pixToWindow( const QPoint &pos ) const; 00086 00087 QPixmap cachePix; 00088 }; 00089 00090 static float 00091 convert_pos (const QString &pos, int digits) 00092 { 00093 if (pos.length() < 4 || digits > 9) return 0.0; 00094 00095 QString whole = pos.left( digits + 1 ); 00096 QString fraction = pos.mid( digits + 1 ); 00097 00098 float t1 = whole.toFloat(); 00099 float t2 = fraction.toFloat(); 00100 00101 if (t1 >= 0.0) 00102 return t1 + t2/pow (10.0, fraction.length() ); 00103 else 00104 return t1 - t2/pow (10.0, fraction.length()); 00105 } 00106 00107 bool YQTimezoneSelectorPrivate::Location::operator<(const Location& l1 ) const 00108 { 00109 return l1.latitude < latitude; 00110 } 00111 00112 YQTimezoneSelector::YQTimezoneSelector( YWidget * parent, const std::string & pixmap, const std::map<std::string,std::string> & timezones ) 00113 : QFrame( (QWidget *) parent->widgetRep() ) 00114 , YTimezoneSelector( parent, pixmap, timezones ) 00115 { 00116 d = new YQTimezoneSelectorPrivate( this ); 00117 00118 setWidgetRep( this ); 00119 setMouseTracking(true); 00120 d->_pix.load( fromUTF8( pixmap ) ); 00121 00122 setStretchable( YD_HORIZ, true ); 00123 setStretchable( YD_VERT, true ); 00124 00125 char buf[4096]; 00126 FILE *tzfile = fopen ("/usr/share/zoneinfo/zone.tab", "r"); 00127 while (fgets (buf, sizeof(buf), tzfile)) 00128 { 00129 if (*buf == '#') continue; 00130 00131 QString sbuf = buf; 00132 QStringList arr = sbuf.trimmed().split( '\t' ); 00133 00134 int split_index = 1; 00135 while ( split_index < arr[1].length() && arr[1][split_index] != '-' && arr[1][split_index] != '+' ) 00136 split_index++; 00137 00138 YQTimezoneSelectorPrivate::Location loc; 00139 loc.country = arr[0]; 00140 loc.zone = arr[2]; 00141 std::map<std::string, std::string>::const_iterator tooltip = timezones.find( loc.zone.toStdString() ); 00142 if (tooltip == timezones.end() ) 00143 continue; 00144 00145 loc.tip = fromUTF8( tooltip->second ); 00146 if ( arr.size() > 3 ) 00147 loc.comment = arr[3]; 00148 loc.latitude = convert_pos ( arr[1].left( split_index ), 2); 00149 loc.longitude = convert_pos ( arr[1].mid( split_index ), 3); 00150 00151 loc.pix_pos = d->pixPosition( loc ); 00152 00153 d->locations.push_back( loc ); 00154 } 00155 00156 fclose (tzfile); 00157 00158 qSort( d->locations.begin(), d->locations.end() ); 00159 00160 d->blink = new QTimer( this ); 00161 d->blink->setInterval( 200 ); 00162 connect( d->blink, SIGNAL( timeout() ), SLOT( slotBlink() ) ); 00163 00164 d->highlight = 0; 00165 } 00166 00167 YQTimezoneSelector::~YQTimezoneSelector() 00168 { 00169 delete d; 00170 // NOP 00171 } 00172 00173 00174 int YQTimezoneSelector::preferredWidth() 00175 { 00176 return 600; 00177 } 00178 00179 00180 int YQTimezoneSelector::preferredHeight() 00181 { 00182 return 300; 00183 } 00184 00185 00186 void YQTimezoneSelector::setSize( int newWidth, int newHeight ) 00187 { 00188 resize( newWidth, newHeight ); 00189 } 00190 00191 QPoint YQTimezoneSelectorPrivate::pixPosition( const Location &pos ) const 00192 { 00193 return QPoint( (int) ( _pix.width() / 2 + _pix.width() / 2 * pos.longitude / 180 ), 00194 (int) ( _pix.height() / 2 - _pix.height() / 2 * pos.latitude / 90 ) ) ; 00195 } 00196 00197 void YQTimezoneSelector::mousePressEvent ( QMouseEvent * event ) 00198 { 00199 if ( event->button() == Qt::RightButton ) 00200 { 00201 d->_zoom = QPoint(); 00202 d->cachePix = QPixmap(); 00203 } 00204 else if ( event->button() == Qt::LeftButton ) 00205 { 00206 d->_best = d->findBest( event->pos() ); 00207 00208 if ( d->_zoom.isNull() ) 00209 { 00210 QPoint click = event->pos(); 00211 /* keep the zoom point in unscaled math */ 00212 d->_zoom.rx() = (int) ( double( click.x() ) * d->_pix.width() / width() ); 00213 d->_zoom.ry() = (int) ( double( click.y() ) * d->_pix.height() / height() ); 00214 } 00215 00216 d->cachePix = QPixmap(); 00217 00218 if ( notify() ) 00219 YQUI::ui()->sendEvent( new YWidgetEvent( this, YEvent::ValueChanged ) ); 00220 00221 d->blink->start(); 00222 } else 00223 return; 00224 00225 update(); 00226 } 00227 00228 void YQTimezoneSelector::paintEvent( QPaintEvent *event ) 00229 { 00230 QFrame::paintEvent( event ); 00231 QPainter p( this ); 00232 00233 if ( d->cachePix.width() != width() || d->cachePix.height() != height() ) 00234 d->cachePix = QPixmap(); 00235 00236 if ( d->_zoom.isNull() ) 00237 { 00238 if ( d->cachePix.isNull() ) 00239 { 00240 QImage t = d->_pix.scaled( width(), height(), Qt::KeepAspectRatio ); 00241 d->cachePix = QPixmap::fromImage( t ); 00242 } 00243 p.drawPixmap( ( width() - d->cachePix.width() ) / 2, ( height() - d->cachePix.height() ) / 2, d->cachePix ); 00244 00245 setCursor( QCursor( QPixmap( zoom_in ) ) ); 00246 } else { 00247 int left = qMin( qMax( d->_zoom.x() - width() / 2, 0 ), d->_pix.width() - width() ); 00248 int top = qMin( qMax( d->_zoom.y() - height() / 2, 0 ), d->_pix.height() - height() ); 00249 00250 if ( d->cachePix.isNull() ) 00251 d->cachePix = QPixmap::fromImage( d->_pix.copy( QRect( QPoint( left, top ), size() ) ) ); 00252 00253 p.drawPixmap( 0, 0, d->cachePix ); 00254 00255 setCursor( Qt::CrossCursor ); 00256 } 00257 00258 p.setBrush( QColor( "#D8DF57" ) ); 00259 p.setPen( QColor( "#B9DFD6" ) ); 00260 for ( QList<YQTimezoneSelectorPrivate::Location>::const_iterator it = d->locations.begin(); it != d->locations.end(); ++it ) 00261 { 00262 if ( !d->highlight || ( *it ).zone != d->_best.zone ) 00263 { 00264 if ( d->_zoom.isNull() ) 00265 p.drawEllipse( QRect( d->pixToWindow( ( *it ).pix_pos ) - QPoint( 1,1 ), QSize( 3, 3 ) ) ); 00266 else 00267 p.drawEllipse( QRect( d->pixToWindow( ( *it ).pix_pos ) - QPoint( 2,2 ), QSize( 5, 5 ) ) ); 00268 } 00269 } 00270 if ( d->highlight > 0 ) 00271 { 00272 // QPoint pos = d->pixPosition( d->_best ); 00273 static const QColor blinks[] = { QColor( "#00ff00" ), QColor( "#22dd00" ), QColor( "#44bb00" ), 00274 QColor( "#669900" ), QColor( "#887700" ), QColor( "#aa5500" ), 00275 QColor( "#887700" ), QColor( "#669900" ), QColor( "#44bb00" ), 00276 QColor( "#22dd00" ) }; 00277 int index = d->highlight - 1; 00278 p.setPen( blinks[ index ] ); 00279 p.setBrush( blinks[ index ] ); 00280 00281 p.drawEllipse( QRect( d->pixToWindow( d->_best.pix_pos ) - QPoint( 2,2 ), QSize( 5, 5 ) ) ); 00282 00283 QFont f( font() ); 00284 f.setBold( true ); 00285 p.setFont( f ); 00286 QFontMetrics fm( f ); 00287 00288 QPoint off = d->pixToWindow( d->_best.pix_pos ) + QPoint( 11, 4 ); 00289 int tw = fm.width( d->_best.tip ); 00290 if ( tw + off.x() > width() ) 00291 off.rx() = d->pixToWindow( d->_best.pix_pos ).x() - tw - 10; 00292 00293 p.setPen( Qt::black ); 00294 p.drawText( off, d->_best.tip ); 00295 00296 p.setPen( Qt::white ); 00297 p.drawText( off - QPoint( 1, 1 ), d->_best.tip ); 00298 00299 } 00300 } 00301 00302 YQTimezoneSelectorPrivate::Location YQTimezoneSelectorPrivate::findBest( const QPoint &pos ) const 00303 { 00304 double min_dist = 2000; 00305 Location best; 00306 for ( QList<Location>::const_iterator it = locations.begin(); it != locations.end(); ++it ) 00307 { 00308 double dist = QPoint( pixToWindow( ( *it ).pix_pos ) - pos ).manhattanLength (); 00309 if ( dist < min_dist ) 00310 { 00311 min_dist = dist; 00312 best = *it; 00313 } 00314 } 00315 return best; 00316 } 00317 00318 bool YQTimezoneSelector::event(QEvent *event) 00319 { 00320 if (event->type() == QEvent::ToolTip) 00321 { 00322 QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event); 00323 00324 YQTimezoneSelectorPrivate::Location best = d->findBest( helpEvent->pos() ); 00325 QToolTip::showText(helpEvent->globalPos(), best.tip ); 00326 } 00327 return QWidget::event(event); 00328 } 00329 00330 00331 std::string YQTimezoneSelector::currentZone() const 00332 { 00333 return d->_best.zone.toStdString(); 00334 } 00335 00336 QPoint YQTimezoneSelectorPrivate::pixToWindow( const QPoint &pos ) const 00337 { 00338 if ( _zoom.isNull() ) 00339 { 00340 return QPoint( (int) ( double( pos.x() ) * cachePix.width() / _pix.width() ) + ( parent->width() - cachePix.width() ) / 2, 00341 (int) ( double( pos.y() ) * cachePix.height() / _pix.height() ) + ( parent->height() - cachePix.height() ) /2 ); 00342 } 00343 int left = qMin( qMax( _zoom.x() - parent->width() / 2, 0 ), _pix.width() - parent->width() ); 00344 int top = qMin( qMax( _zoom.y() - parent->height() / 2, 0 ), _pix.height() - parent->height() ); 00345 00346 return QPoint( pos.x() - left, pos.y() - top ); 00347 } 00348 00349 void YQTimezoneSelector::setCurrentZone( const std::string &_zone, bool zoom ) 00350 { 00351 QString zone = fromUTF8( _zone ); 00352 00353 if ( d->_best.zone == zone ) 00354 return; 00355 00356 d->_best = YQTimezoneSelectorPrivate::Location(); 00357 00358 for ( QList<YQTimezoneSelectorPrivate::Location>::const_iterator it = d->locations.begin(); it != d->locations.end(); ++it ) 00359 { 00360 if ( ( *it ).zone == zone ) 00361 d->_best = *it; 00362 } 00363 00364 if ( zoom ) 00365 d->_zoom = d->_best.pix_pos; 00366 else 00367 d->_zoom = QPoint(); 00368 00369 d->cachePix = QPixmap(); 00370 d->highlight = 1; 00371 00372 d->blink->start(); 00373 update(); 00374 } 00375 00376 void YQTimezoneSelector::slotBlink() 00377 { 00378 if ( d->_best.zone.isNull() ) 00379 { 00380 d->blink->stop(); 00381 return; 00382 } 00383 00384 if ( d->highlight++ > 9 ) 00385 d->highlight = 1; 00386 00387 QPoint current = d->pixToWindow( d->_best.pix_pos ); 00388 update( QRect( current - QPoint( 3, 3 ), QSize( 7, 7 ) ) ); 00389 } 00390 00391 #include "YQTimezoneSelector.moc"