libyui-qt  2.43.5
 All Classes Functions Variables
YQTimezoneSelector.cc
1 /*
2  Copyright (C) 2000-2012 Novell, Inc
3  This library is free software; you can redistribute it and/or modify
4  it under the terms of the GNU Lesser General Public License as
5  published by the Free Software Foundation; either version 2.1 of the
6  License, or (at your option) version 3.0 of the License. This library
7  is distributed in the hope that it will be useful, but WITHOUT ANY
8  WARRANTY; without even the implied warranty of MERCHANTABILITY or
9  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
10  License for more details. You should have received a copy of the GNU
11  Lesser General Public License along with this library; if not, write
12  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
13  Floor, Boston, MA 02110-1301 USA
14 */
15 
16 
17 /*-/
18 
19  File: YQTimezoneSelector.cc
20 
21  Author: Stephan Kulow <coolo@suse.de>
22 
23 /-*/
24 
25 
26 #define YUILogComponent "qt-ui"
27 #include <yui/YUILog.h>
28 #include <math.h>
29 
30 #include <qdatetimeedit.h>
31 
32 #include "utf8.h"
33 #include "YQUI.h"
34 #include "YQTimezoneSelector.h"
35 #include "YQWidgetCaption.h"
36 #include <yui/YEvent.h>
37 #include <QVBoxLayout>
38 #include <QPainter>
39 #include <QMouseEvent>
40 #include <QDebug>
41 #include <QToolTip>
42 
43 #include "icons/zoom-in.xpm"
44 
46 {
47  QWidget *parent;
48 
49 public:
50 
51  YQTimezoneSelectorPrivate( QWidget *p ) {
52  parent = p;
53  blink = 0;
54  highlight = 0;
55  }
56  QImage _pix;
57  QPoint _zoom;
58 
59  struct Location
60  {
61  QString country;
62  double latitude;
63  double longitude;
64  QString zone;
65  QString comment;
66  QString tip;
67 
68  QPoint pix_pos;
69 
70  bool operator<(const Location& l2) const;
71  };
72 
73  Location _best;
74 
75  QList<Location> locations;
76 
77  Location findBest( const QPoint &pos ) const;
78 
79  QTimer *blink;
80 
81  int highlight;
82 
83  QPoint pixPosition( const Location &pos ) const;
84 
85  QPoint pixToWindow( const QPoint &pos ) const;
86 
87  QPixmap cachePix;
88 };
89 
90 static float
91 convert_pos (const QString &pos, int digits)
92 {
93  if (pos.length() < 4 || digits > 9) return 0.0;
94 
95  QString whole = pos.left( digits + 1 );
96  QString fraction = pos.mid( digits + 1 );
97 
98  float t1 = whole.toFloat();
99  float t2 = fraction.toFloat();
100 
101  if (t1 >= 0.0)
102  return t1 + t2/pow (10.0, fraction.length() );
103  else
104  return t1 - t2/pow (10.0, fraction.length());
105 }
106 
107 bool YQTimezoneSelectorPrivate::Location::operator<(const Location& l1 ) const
108 {
109  return l1.latitude < latitude;
110 }
111 
112 YQTimezoneSelector::YQTimezoneSelector( YWidget * parent, const std::string & pixmap, const std::map<std::string,std::string> & timezones )
113  : QFrame( (QWidget *) parent->widgetRep() )
114  , YTimezoneSelector( parent, pixmap, timezones )
115 {
116  d = new YQTimezoneSelectorPrivate( this );
117 
118  setWidgetRep( this );
119  setMouseTracking(true);
120  d->_pix.load( fromUTF8( pixmap ) );
121 
122  setStretchable( YD_HORIZ, true );
123  setStretchable( YD_VERT, true );
124 
125  char buf[4096];
126  FILE *tzfile = fopen ("/usr/share/zoneinfo/zone.tab", "r");
127  while (fgets (buf, sizeof(buf), tzfile))
128  {
129  if (*buf == '#') continue;
130 
131  QString sbuf = buf;
132  QStringList arr = sbuf.trimmed().split( '\t' );
133 
134  int split_index = 1;
135  while ( split_index < arr[1].length() && arr[1][split_index] != '-' && arr[1][split_index] != '+' )
136  split_index++;
137 
139  loc.country = arr[0];
140  loc.zone = arr[2];
141  std::map<std::string, std::string>::const_iterator tooltip = timezones.find( loc.zone.toStdString() );
142  if (tooltip == timezones.end() )
143  continue;
144 
145  loc.tip = fromUTF8( tooltip->second );
146  if ( arr.size() > 3 )
147  loc.comment = arr[3];
148  loc.latitude = convert_pos ( arr[1].left( split_index ), 2);
149  loc.longitude = convert_pos ( arr[1].mid( split_index ), 3);
150 
151  loc.pix_pos = d->pixPosition( loc );
152 
153  d->locations.push_back( loc );
154  }
155 
156  fclose (tzfile);
157 
158  qSort( d->locations.begin(), d->locations.end() );
159 
160  d->blink = new QTimer( this );
161  d->blink->setInterval( 200 );
162  connect( d->blink, SIGNAL( timeout() ), SLOT( slotBlink() ) );
163 
164  d->highlight = 0;
165 }
166 
168 {
169  delete d;
170  // NOP
171 }
172 
173 
175 {
176  return 600;
177 }
178 
179 
181 {
182  return 300;
183 }
184 
185 
186 void YQTimezoneSelector::setSize( int newWidth, int newHeight )
187 {
188  resize( newWidth, newHeight );
189 }
190 
191 QPoint YQTimezoneSelectorPrivate::pixPosition( const Location &pos ) const
192 {
193  return QPoint( (int) ( _pix.width() / 2 + _pix.width() / 2 * pos.longitude / 180 ),
194  (int) ( _pix.height() / 2 - _pix.height() / 2 * pos.latitude / 90 ) ) ;
195 }
196 
197 void YQTimezoneSelector::mousePressEvent ( QMouseEvent * event )
198 {
199  if ( event->button() == Qt::RightButton )
200  {
201  d->_zoom = QPoint();
202  d->cachePix = QPixmap();
203  }
204  else if ( event->button() == Qt::LeftButton )
205  {
206  d->_best = d->findBest( event->pos() );
207 
208  if ( d->_zoom.isNull() )
209  {
210  QPoint click = event->pos();
211  /* keep the zoom point in unscaled math */
212  d->_zoom.rx() = (int) ( double( click.x() ) * d->_pix.width() / width() );
213  d->_zoom.ry() = (int) ( double( click.y() ) * d->_pix.height() / height() );
214  }
215 
216  d->cachePix = QPixmap();
217 
218  if ( notify() )
219  YQUI::ui()->sendEvent( new YWidgetEvent( this, YEvent::ValueChanged ) );
220 
221  d->blink->start();
222  } else
223  return;
224 
225  update();
226 }
227 
228 void YQTimezoneSelector::paintEvent( QPaintEvent *event )
229 {
230  QFrame::paintEvent( event );
231  QPainter p( this );
232 
233  if ( d->cachePix.width() != width() || d->cachePix.height() != height() )
234  d->cachePix = QPixmap();
235 
236  if ( d->_zoom.isNull() )
237  {
238  if ( d->cachePix.isNull() )
239  {
240  QImage t = d->_pix.scaled( width(), height(), Qt::KeepAspectRatio );
241  d->cachePix = QPixmap::fromImage( t );
242  }
243  p.drawPixmap( ( width() - d->cachePix.width() ) / 2, ( height() - d->cachePix.height() ) / 2, d->cachePix );
244 
245  setCursor( QCursor( QPixmap( zoom_in ) ) );
246  } else {
247  int left = qMin( qMax( d->_zoom.x() - width() / 2, 0 ), d->_pix.width() - width() );
248  int top = qMin( qMax( d->_zoom.y() - height() / 2, 0 ), d->_pix.height() - height() );
249 
250  if ( d->cachePix.isNull() )
251  d->cachePix = QPixmap::fromImage( d->_pix.copy( QRect( QPoint( left, top ), size() ) ) );
252 
253  p.drawPixmap( 0, 0, d->cachePix );
254 
255  setCursor( Qt::CrossCursor );
256  }
257 
258  p.setBrush( QColor( "#D8DF57" ) );
259  p.setPen( QColor( "#B9DFD6" ) );
260  for ( QList<YQTimezoneSelectorPrivate::Location>::const_iterator it = d->locations.begin(); it != d->locations.end(); ++it )
261  {
262  if ( !d->highlight || ( *it ).zone != d->_best.zone )
263  {
264  if ( d->_zoom.isNull() )
265  p.drawEllipse( QRect( d->pixToWindow( ( *it ).pix_pos ) - QPoint( 1,1 ), QSize( 3, 3 ) ) );
266  else
267  p.drawEllipse( QRect( d->pixToWindow( ( *it ).pix_pos ) - QPoint( 2,2 ), QSize( 5, 5 ) ) );
268  }
269  }
270  if ( d->highlight > 0 )
271  {
272 // QPoint pos = d->pixPosition( d->_best );
273  static const QColor blinks[] = { QColor( "#00ff00" ), QColor( "#22dd00" ), QColor( "#44bb00" ),
274  QColor( "#669900" ), QColor( "#887700" ), QColor( "#aa5500" ),
275  QColor( "#887700" ), QColor( "#669900" ), QColor( "#44bb00" ),
276  QColor( "#22dd00" ) };
277  int index = d->highlight - 1;
278  p.setPen( blinks[ index ] );
279  p.setBrush( blinks[ index ] );
280 
281  p.drawEllipse( QRect( d->pixToWindow( d->_best.pix_pos ) - QPoint( 2,2 ), QSize( 5, 5 ) ) );
282 
283  QFont f( font() );
284  f.setBold( true );
285  p.setFont( f );
286  QFontMetrics fm( f );
287 
288  QPoint off = d->pixToWindow( d->_best.pix_pos ) + QPoint( 11, 4 );
289  int tw = fm.width( d->_best.tip );
290  if ( tw + off.x() > width() )
291  off.rx() = d->pixToWindow( d->_best.pix_pos ).x() - tw - 10;
292 
293  p.setPen( Qt::black );
294  p.drawText( off, d->_best.tip );
295 
296  p.setPen( Qt::white );
297  p.drawText( off - QPoint( 1, 1 ), d->_best.tip );
298 
299  }
300 }
301 
302 YQTimezoneSelectorPrivate::Location YQTimezoneSelectorPrivate::findBest( const QPoint &pos ) const
303 {
304  double min_dist = 2000;
305  Location best;
306  for ( QList<Location>::const_iterator it = locations.begin(); it != locations.end(); ++it )
307  {
308  double dist = QPoint( pixToWindow( ( *it ).pix_pos ) - pos ).manhattanLength ();
309  if ( dist < min_dist )
310  {
311  min_dist = dist;
312  best = *it;
313  }
314  }
315  return best;
316 }
317 
318 bool YQTimezoneSelector::event(QEvent *event)
319 {
320  if (event->type() == QEvent::ToolTip)
321  {
322  QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
323 
324  YQTimezoneSelectorPrivate::Location best = d->findBest( helpEvent->pos() );
325  QToolTip::showText(helpEvent->globalPos(), best.tip );
326  }
327  return QWidget::event(event);
328 }
329 
330 
332 {
333  return d->_best.zone.toStdString();
334 }
335 
336 QPoint YQTimezoneSelectorPrivate::pixToWindow( const QPoint &pos ) const
337 {
338  if ( _zoom.isNull() )
339  {
340  return QPoint( (int) ( double( pos.x() ) * cachePix.width() / _pix.width() ) + ( parent->width() - cachePix.width() ) / 2,
341  (int) ( double( pos.y() ) * cachePix.height() / _pix.height() ) + ( parent->height() - cachePix.height() ) /2 );
342  }
343  int left = qMin( qMax( _zoom.x() - parent->width() / 2, 0 ), _pix.width() - parent->width() );
344  int top = qMin( qMax( _zoom.y() - parent->height() / 2, 0 ), _pix.height() - parent->height() );
345 
346  return QPoint( pos.x() - left, pos.y() - top );
347 }
348 
349 void YQTimezoneSelector::setCurrentZone( const std::string &_zone, bool zoom )
350 {
351  QString zone = fromUTF8( _zone );
352 
353  if ( d->_best.zone == zone )
354  return;
355 
357 
358  for ( QList<YQTimezoneSelectorPrivate::Location>::const_iterator it = d->locations.begin(); it != d->locations.end(); ++it )
359  {
360  if ( ( *it ).zone == zone )
361  d->_best = *it;
362  }
363 
364  if ( zoom )
365  d->_zoom = d->_best.pix_pos;
366  else
367  d->_zoom = QPoint();
368 
369  d->cachePix = QPixmap();
370  d->highlight = 1;
371 
372  d->blink->start();
373  update();
374 }
375 
376 void YQTimezoneSelector::slotBlink()
377 {
378  if ( d->_best.zone.isNull() )
379  {
380  d->blink->stop();
381  return;
382  }
383 
384  if ( d->highlight++ > 9 )
385  d->highlight = 1;
386 
387  QPoint current = d->pixToWindow( d->_best.pix_pos );
388  update( QRect( current - QPoint( 3, 3 ), QSize( 7, 7 ) ) );
389 }
390 
391 #include "YQTimezoneSelector.moc"