libyui-qt  2.49.16
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 #include <QIcon>
43 
45 {
46  QWidget *parent;
47 
48 public:
49 
50  YQTimezoneSelectorPrivate( QWidget *p ) {
51  parent = p;
52  blink = 0;
53  highlight = 0;
54  }
55  QImage _pix;
56  QPoint _zoom;
57 
58  struct Location
59  {
60  QString country;
61  double latitude;
62  double longitude;
63  QString zone;
64  QString comment;
65  QString tip;
66 
67  QPoint pix_pos;
68 
69  bool operator<(const Location& l2) const;
70  };
71 
72  Location _best;
73 
74  QList<Location> locations;
75 
76  Location findBest( const QPoint &pos ) const;
77 
78  QTimer *blink;
79 
80  int highlight;
81 
82  QPoint pixPosition( const Location &pos ) const;
83 
84  QPoint pixToWindow( const QPoint &pos ) const;
85 
86  QPixmap cachePix;
87 };
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 
108 bool YQTimezoneSelectorPrivate::Location::operator<(const Location& l1 ) const
109 {
110  return l1.latitude < latitude;
111 }
112 
113 
114 YQTimezoneSelector::YQTimezoneSelector( YWidget * parent, const std::string & pixmap, const std::map<std::string,std::string> & timezones )
115  : QFrame( (QWidget *) parent->widgetRep() )
116  , YTimezoneSelector( parent, pixmap, timezones )
117 {
118  d = new YQTimezoneSelectorPrivate( this );
119 
120  setWidgetRep( this );
121  setMouseTracking(true);
122  d->_pix.load( fromUTF8( pixmap ) );
123 
124  setStretchable( YD_HORIZ, true );
125  setStretchable( YD_VERT, true );
126 
127  char buf[4096];
128  FILE *tzfile = fopen ("/usr/share/zoneinfo/zone.tab", "r");
129  while (fgets (buf, sizeof(buf), tzfile))
130  {
131  if (*buf == '#') continue;
132 
133  QString sbuf = buf;
134  QStringList arr = sbuf.trimmed().split( '\t' );
135 
136  int split_index = 1;
137  while ( split_index < arr[1].length() && arr[1][split_index] != '-' && arr[1][split_index] != '+' )
138  split_index++;
139 
141  loc.country = arr[0];
142  loc.zone = arr[2];
143  std::map<std::string, std::string>::const_iterator tooltip = timezones.find( loc.zone.toStdString() );
144  if (tooltip == timezones.end() )
145  continue;
146 
147  loc.tip = fromUTF8( tooltip->second );
148  if ( arr.size() > 3 )
149  loc.comment = arr[3];
150  loc.latitude = convert_pos ( arr[1].left( split_index ), 2);
151  loc.longitude = convert_pos ( arr[1].mid( split_index ), 3);
152 
153  loc.pix_pos = d->pixPosition( loc );
154 
155  d->locations.push_back( loc );
156  }
157 
158  fclose (tzfile);
159 
160  qSort( d->locations.begin(), d->locations.end() );
161 
162  d->blink = new QTimer( this );
163  d->blink->setInterval( 200 );
164  connect( d->blink, &pclass(d->blink)::timeout,
165  this, &pclass(this)::slotBlink );
166 
167  d->highlight = 0;
168 }
169 
171 {
172  delete d;
173  // NOP
174 }
175 
176 
178 {
179  return 600;
180 }
181 
182 
184 {
185  return 300;
186 }
187 
188 
189 void YQTimezoneSelector::setSize( int newWidth, int newHeight )
190 {
191  resize( newWidth, newHeight );
192 }
193 
194 
195 QPoint YQTimezoneSelectorPrivate::pixPosition( const Location &pos ) const
196 {
197  return QPoint( (int) ( _pix.width() / 2 + _pix.width() / 2 * pos.longitude / 180 ),
198  (int) ( _pix.height() / 2 - _pix.height() / 2 * pos.latitude / 90 ) ) ;
199 }
200 
201 
202 void YQTimezoneSelector::mousePressEvent ( QMouseEvent * event )
203 {
204  if ( event->button() == Qt::RightButton )
205  {
206  d->_zoom = QPoint();
207  d->cachePix = QPixmap();
208  }
209  else if ( event->button() == Qt::LeftButton )
210  {
211  d->_best = d->findBest( event->pos() );
212 
213  if ( d->_zoom.isNull() )
214  {
215  QPoint click = event->pos();
216  /* keep the zoom point in unscaled math */
217  d->_zoom.rx() = (int) ( double( click.x() ) * d->_pix.width() / width() );
218  d->_zoom.ry() = (int) ( double( click.y() ) * d->_pix.height() / height() );
219  }
220 
221  d->cachePix = QPixmap();
222 
223  if ( notify() )
224  YQUI::ui()->sendEvent( new YWidgetEvent( this, YEvent::ValueChanged ) );
225 
226  d->blink->start();
227  } else
228  return;
229 
230  update();
231 }
232 
233 
234 void YQTimezoneSelector::paintEvent( QPaintEvent *event )
235 {
236  QFrame::paintEvent( event );
237  QPainter p( this );
238 
239  if ( d->cachePix.width() != width() || d->cachePix.height() != height() )
240  d->cachePix = QPixmap();
241 
242  if ( d->_zoom.isNull() )
243  {
244  if ( d->cachePix.isNull() )
245  {
246  QImage t = d->_pix.scaled( width(), height(), Qt::KeepAspectRatio );
247  d->cachePix = QPixmap::fromImage( t );
248  }
249  p.drawPixmap( ( width() - d->cachePix.width() ) / 2, ( height() - d->cachePix.height() ) / 2, d->cachePix );
250 
251  Q_INIT_RESOURCE( qt_icons );
252  QIcon icon = QIcon::fromTheme( "zoom-in", QIcon( ":/zoom-in" ) );
253 
254  if ( !icon.isNull() )
255  setCursor( QCursor( icon.pixmap( QSize( 16, 16 ) ) ) );
256  }
257  else
258  {
259  int left = qMin( qMax( d->_zoom.x() - width() / 2, 0 ), d->_pix.width() - width() );
260  int top = qMin( qMax( d->_zoom.y() - height() / 2, 0 ), d->_pix.height() - height() );
261 
262  if ( d->cachePix.isNull() )
263  d->cachePix = QPixmap::fromImage( d->_pix.copy( QRect( QPoint( left, top ), size() ) ) );
264 
265  p.drawPixmap( 0, 0, d->cachePix );
266 
267  setCursor( Qt::CrossCursor );
268  }
269 
270  p.setBrush( QColor( "#D8DF57" ) );
271  p.setPen( QColor( "#B9DFD6" ) );
272 
273  for ( QList<YQTimezoneSelectorPrivate::Location>::const_iterator it = d->locations.begin(); it != d->locations.end(); ++it )
274  {
275  if ( !d->highlight || ( *it ).zone != d->_best.zone )
276  {
277  if ( d->_zoom.isNull() )
278  p.drawEllipse( QRect( d->pixToWindow( ( *it ).pix_pos ) - QPoint( 1,1 ), QSize( 3, 3 ) ) );
279  else
280  p.drawEllipse( QRect( d->pixToWindow( ( *it ).pix_pos ) - QPoint( 2,2 ), QSize( 5, 5 ) ) );
281  }
282  }
283  if ( d->highlight > 0 )
284  {
285  static const QColor blinks[] = { QColor( "#00ff00" ), QColor( "#22dd00" ), QColor( "#44bb00" ),
286  QColor( "#669900" ), QColor( "#887700" ), QColor( "#aa5500" ),
287  QColor( "#887700" ), QColor( "#669900" ), QColor( "#44bb00" ),
288  QColor( "#22dd00" ) };
289  int index = d->highlight - 1;
290  p.setPen( blinks[ index ] );
291  p.setBrush( blinks[ index ] );
292 
293  p.drawEllipse( QRect( d->pixToWindow( d->_best.pix_pos ) - QPoint( 2,2 ), QSize( 5, 5 ) ) );
294 
295  QFont f( font() );
296  f.setBold( true );
297  p.setFont( f );
298  QFontMetrics fm( f );
299 
300  QPoint off = d->pixToWindow( d->_best.pix_pos ) + QPoint( 11, 4 );
301  int tw = fm.width( d->_best.tip );
302  if ( tw + off.x() > width() )
303  off.rx() = d->pixToWindow( d->_best.pix_pos ).x() - tw - 10;
304 
305  p.setPen( Qt::black );
306  p.drawText( off, d->_best.tip );
307 
308  p.setPen( Qt::white );
309  p.drawText( off - QPoint( 1, 1 ), d->_best.tip );
310 
311  }
312 }
313 
314 YQTimezoneSelectorPrivate::Location YQTimezoneSelectorPrivate::findBest( const QPoint &pos ) const
315 {
316  double min_dist = 2000;
317  Location best;
318  for ( QList<Location>::const_iterator it = locations.begin(); it != locations.end(); ++it )
319  {
320  double dist = QPoint( pixToWindow( ( *it ).pix_pos ) - pos ).manhattanLength ();
321  if ( dist < min_dist )
322  {
323  min_dist = dist;
324  best = *it;
325  }
326  }
327  return best;
328 }
329 
330 bool YQTimezoneSelector::event(QEvent *event)
331 {
332  if (event->type() == QEvent::ToolTip)
333  {
334  QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
335 
336  YQTimezoneSelectorPrivate::Location best = d->findBest( helpEvent->pos() );
337  QToolTip::showText(helpEvent->globalPos(), best.tip );
338  }
339  return QWidget::event(event);
340 }
341 
342 
344 {
345  return d->_best.zone.toStdString();
346 }
347 
348 QPoint YQTimezoneSelectorPrivate::pixToWindow( const QPoint &pos ) const
349 {
350  if ( _zoom.isNull() )
351  {
352  return QPoint( (int) ( double( pos.x() ) * cachePix.width() / _pix.width() ) + ( parent->width() - cachePix.width() ) / 2,
353  (int) ( double( pos.y() ) * cachePix.height() / _pix.height() ) + ( parent->height() - cachePix.height() ) /2 );
354  }
355  int left = qMin( qMax( _zoom.x() - parent->width() / 2, 0 ), _pix.width() - parent->width() );
356  int top = qMin( qMax( _zoom.y() - parent->height() / 2, 0 ), _pix.height() - parent->height() );
357 
358  return QPoint( pos.x() - left, pos.y() - top );
359 }
360 
361 void YQTimezoneSelector::setCurrentZone( const std::string &_zone, bool zoom )
362 {
363  QString zone = fromUTF8( _zone );
364 
365  if ( d->_best.zone == zone )
366  return;
367 
369 
370  for ( QList<YQTimezoneSelectorPrivate::Location>::const_iterator it = d->locations.begin(); it != d->locations.end(); ++it )
371  {
372  if ( ( *it ).zone == zone )
373  d->_best = *it;
374  }
375 
376  if ( zoom )
377  d->_zoom = d->_best.pix_pos;
378  else
379  d->_zoom = QPoint();
380 
381  d->cachePix = QPixmap();
382  d->highlight = 1;
383 
384  d->blink->start();
385  update();
386 }
387 
388 void YQTimezoneSelector::slotBlink()
389 {
390  if ( d->_best.zone.isNull() )
391  {
392  d->blink->stop();
393  return;
394  }
395 
396  if ( d->highlight++ > 9 )
397  d->highlight = 1;
398 
399  QPoint current = d->pixToWindow( d->_best.pix_pos );
400  update( QRect( current - QPoint( 3, 3 ), QSize( 7, 7 ) ) );
401 }
402 
403 
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:469
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:81