libyui-qt  2.49.2
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, &pclass(d->blink)::timeout,
163  this, &pclass(this)::slotBlink );
164 
165  d->highlight = 0;
166 }
167 
169 {
170  delete d;
171  // NOP
172 }
173 
174 
176 {
177  return 600;
178 }
179 
180 
182 {
183  return 300;
184 }
185 
186 
187 void YQTimezoneSelector::setSize( int newWidth, int newHeight )
188 {
189  resize( newWidth, newHeight );
190 }
191 
192 QPoint YQTimezoneSelectorPrivate::pixPosition( const Location &pos ) const
193 {
194  return QPoint( (int) ( _pix.width() / 2 + _pix.width() / 2 * pos.longitude / 180 ),
195  (int) ( _pix.height() / 2 - _pix.height() / 2 * pos.latitude / 90 ) ) ;
196 }
197 
198 void YQTimezoneSelector::mousePressEvent ( QMouseEvent * event )
199 {
200  if ( event->button() == Qt::RightButton )
201  {
202  d->_zoom = QPoint();
203  d->cachePix = QPixmap();
204  }
205  else if ( event->button() == Qt::LeftButton )
206  {
207  d->_best = d->findBest( event->pos() );
208 
209  if ( d->_zoom.isNull() )
210  {
211  QPoint click = event->pos();
212  /* keep the zoom point in unscaled math */
213  d->_zoom.rx() = (int) ( double( click.x() ) * d->_pix.width() / width() );
214  d->_zoom.ry() = (int) ( double( click.y() ) * d->_pix.height() / height() );
215  }
216 
217  d->cachePix = QPixmap();
218 
219  if ( notify() )
220  YQUI::ui()->sendEvent( new YWidgetEvent( this, YEvent::ValueChanged ) );
221 
222  d->blink->start();
223  } else
224  return;
225 
226  update();
227 }
228 
229 void YQTimezoneSelector::paintEvent( QPaintEvent *event )
230 {
231  QFrame::paintEvent( event );
232  QPainter p( this );
233 
234  if ( d->cachePix.width() != width() || d->cachePix.height() != height() )
235  d->cachePix = QPixmap();
236 
237  if ( d->_zoom.isNull() )
238  {
239  if ( d->cachePix.isNull() )
240  {
241  QImage t = d->_pix.scaled( width(), height(), Qt::KeepAspectRatio );
242  d->cachePix = QPixmap::fromImage( t );
243  }
244  p.drawPixmap( ( width() - d->cachePix.width() ) / 2, ( height() - d->cachePix.height() ) / 2, d->cachePix );
245 
246  setCursor( QCursor( QPixmap( zoom_in ) ) );
247  } else {
248  int left = qMin( qMax( d->_zoom.x() - width() / 2, 0 ), d->_pix.width() - width() );
249  int top = qMin( qMax( d->_zoom.y() - height() / 2, 0 ), d->_pix.height() - height() );
250 
251  if ( d->cachePix.isNull() )
252  d->cachePix = QPixmap::fromImage( d->_pix.copy( QRect( QPoint( left, top ), size() ) ) );
253 
254  p.drawPixmap( 0, 0, d->cachePix );
255 
256  setCursor( Qt::CrossCursor );
257  }
258 
259  p.setBrush( QColor( "#D8DF57" ) );
260  p.setPen( QColor( "#B9DFD6" ) );
261  for ( QList<YQTimezoneSelectorPrivate::Location>::const_iterator it = d->locations.begin(); it != d->locations.end(); ++it )
262  {
263  if ( !d->highlight || ( *it ).zone != d->_best.zone )
264  {
265  if ( d->_zoom.isNull() )
266  p.drawEllipse( QRect( d->pixToWindow( ( *it ).pix_pos ) - QPoint( 1,1 ), QSize( 3, 3 ) ) );
267  else
268  p.drawEllipse( QRect( d->pixToWindow( ( *it ).pix_pos ) - QPoint( 2,2 ), QSize( 5, 5 ) ) );
269  }
270  }
271  if ( d->highlight > 0 )
272  {
273 // QPoint pos = d->pixPosition( d->_best );
274  static const QColor blinks[] = { QColor( "#00ff00" ), QColor( "#22dd00" ), QColor( "#44bb00" ),
275  QColor( "#669900" ), QColor( "#887700" ), QColor( "#aa5500" ),
276  QColor( "#887700" ), QColor( "#669900" ), QColor( "#44bb00" ),
277  QColor( "#22dd00" ) };
278  int index = d->highlight - 1;
279  p.setPen( blinks[ index ] );
280  p.setBrush( blinks[ index ] );
281 
282  p.drawEllipse( QRect( d->pixToWindow( d->_best.pix_pos ) - QPoint( 2,2 ), QSize( 5, 5 ) ) );
283 
284  QFont f( font() );
285  f.setBold( true );
286  p.setFont( f );
287  QFontMetrics fm( f );
288 
289  QPoint off = d->pixToWindow( d->_best.pix_pos ) + QPoint( 11, 4 );
290  int tw = fm.width( d->_best.tip );
291  if ( tw + off.x() > width() )
292  off.rx() = d->pixToWindow( d->_best.pix_pos ).x() - tw - 10;
293 
294  p.setPen( Qt::black );
295  p.drawText( off, d->_best.tip );
296 
297  p.setPen( Qt::white );
298  p.drawText( off - QPoint( 1, 1 ), d->_best.tip );
299 
300  }
301 }
302 
303 YQTimezoneSelectorPrivate::Location YQTimezoneSelectorPrivate::findBest( const QPoint &pos ) const
304 {
305  double min_dist = 2000;
306  Location best;
307  for ( QList<Location>::const_iterator it = locations.begin(); it != locations.end(); ++it )
308  {
309  double dist = QPoint( pixToWindow( ( *it ).pix_pos ) - pos ).manhattanLength ();
310  if ( dist < min_dist )
311  {
312  min_dist = dist;
313  best = *it;
314  }
315  }
316  return best;
317 }
318 
319 bool YQTimezoneSelector::event(QEvent *event)
320 {
321  if (event->type() == QEvent::ToolTip)
322  {
323  QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
324 
325  YQTimezoneSelectorPrivate::Location best = d->findBest( helpEvent->pos() );
326  QToolTip::showText(helpEvent->globalPos(), best.tip );
327  }
328  return QWidget::event(event);
329 }
330 
331 
333 {
334  return d->_best.zone.toStdString();
335 }
336 
337 QPoint YQTimezoneSelectorPrivate::pixToWindow( const QPoint &pos ) const
338 {
339  if ( _zoom.isNull() )
340  {
341  return QPoint( (int) ( double( pos.x() ) * cachePix.width() / _pix.width() ) + ( parent->width() - cachePix.width() ) / 2,
342  (int) ( double( pos.y() ) * cachePix.height() / _pix.height() ) + ( parent->height() - cachePix.height() ) /2 );
343  }
344  int left = qMin( qMax( _zoom.x() - parent->width() / 2, 0 ), _pix.width() - parent->width() );
345  int top = qMin( qMax( _zoom.y() - parent->height() / 2, 0 ), _pix.height() - parent->height() );
346 
347  return QPoint( pos.x() - left, pos.y() - top );
348 }
349 
350 void YQTimezoneSelector::setCurrentZone( const std::string &_zone, bool zoom )
351 {
352  QString zone = fromUTF8( _zone );
353 
354  if ( d->_best.zone == zone )
355  return;
356 
358 
359  for ( QList<YQTimezoneSelectorPrivate::Location>::const_iterator it = d->locations.begin(); it != d->locations.end(); ++it )
360  {
361  if ( ( *it ).zone == zone )
362  d->_best = *it;
363  }
364 
365  if ( zoom )
366  d->_zoom = d->_best.pix_pos;
367  else
368  d->_zoom = QPoint();
369 
370  d->cachePix = QPixmap();
371  d->highlight = 1;
372 
373  d->blink->start();
374  update();
375 }
376 
377 void YQTimezoneSelector::slotBlink()
378 {
379  if ( d->_best.zone.isNull() )
380  {
381  d->blink->stop();
382  return;
383  }
384 
385  if ( d->highlight++ > 9 )
386  d->highlight = 1;
387 
388  QPoint current = d->pixToWindow( d->_best.pix_pos );
389  update( QRect( current - QPoint( 3, 3 ), QSize( 7, 7 ) ) );
390 }
391 
392 #include "YQTimezoneSelector.moc"
virtual int preferredHeight()
Preferred height of the widget.
virtual void setSize(int newWidth, int newHeight)
Set the new size of the widget.
virtual std::string currentZone() const
subclasses have to implement this to return value
YQTimezoneSelector(YWidget *parent, const std::string &pixmap, const std::map< std::string, std::string > &timezones)
Constructor.
void sendEvent(YEvent *event)
Widget event handlers (slots) call this when an event occured that should be the answer to a UserInpu...
Definition: YQUI.cc:468
virtual ~YQTimezoneSelector()
Destructor.
virtual int preferredWidth()
Preferred width of the widget.
virtual void setCurrentZone(const std::string &zone, bool zoom)
subclasses have to implement this to set value
static YQUI * ui()
Access the global Qt-UI.
Definition: YQUI.h:80