libyui-qt  2.49.11
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 static float
90 convert_pos (const QString &pos, int digits)
91 {
92  if (pos.length() < 4 || digits > 9) return 0.0;
93 
94  QString whole = pos.left( digits + 1 );
95  QString fraction = pos.mid( digits + 1 );
96 
97  float t1 = whole.toFloat();
98  float t2 = fraction.toFloat();
99 
100  if (t1 >= 0.0)
101  return t1 + t2/pow (10.0, fraction.length() );
102  else
103  return t1 - t2/pow (10.0, fraction.length());
104 }
105 
106 bool YQTimezoneSelectorPrivate::Location::operator<(const Location& l1 ) const
107 {
108  return l1.latitude < latitude;
109 }
110 
111 YQTimezoneSelector::YQTimezoneSelector( YWidget * parent, const std::string & pixmap, const std::map<std::string,std::string> & timezones )
112  : QFrame( (QWidget *) parent->widgetRep() )
113  , YTimezoneSelector( parent, pixmap, timezones )
114 {
115  d = new YQTimezoneSelectorPrivate( this );
116 
117  setWidgetRep( this );
118  setMouseTracking(true);
119  d->_pix.load( fromUTF8( pixmap ) );
120 
121  setStretchable( YD_HORIZ, true );
122  setStretchable( YD_VERT, true );
123 
124  char buf[4096];
125  FILE *tzfile = fopen ("/usr/share/zoneinfo/zone.tab", "r");
126  while (fgets (buf, sizeof(buf), tzfile))
127  {
128  if (*buf == '#') continue;
129 
130  QString sbuf = buf;
131  QStringList arr = sbuf.trimmed().split( '\t' );
132 
133  int split_index = 1;
134  while ( split_index < arr[1].length() && arr[1][split_index] != '-' && arr[1][split_index] != '+' )
135  split_index++;
136 
138  loc.country = arr[0];
139  loc.zone = arr[2];
140  std::map<std::string, std::string>::const_iterator tooltip = timezones.find( loc.zone.toStdString() );
141  if (tooltip == timezones.end() )
142  continue;
143 
144  loc.tip = fromUTF8( tooltip->second );
145  if ( arr.size() > 3 )
146  loc.comment = arr[3];
147  loc.latitude = convert_pos ( arr[1].left( split_index ), 2);
148  loc.longitude = convert_pos ( arr[1].mid( split_index ), 3);
149 
150  loc.pix_pos = d->pixPosition( loc );
151 
152  d->locations.push_back( loc );
153  }
154 
155  fclose (tzfile);
156 
157  qSort( d->locations.begin(), d->locations.end() );
158 
159  d->blink = new QTimer( this );
160  d->blink->setInterval( 200 );
161  connect( d->blink, &pclass(d->blink)::timeout,
162  this, &pclass(this)::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  Q_INIT_RESOURCE(qt_icons);
246  QIcon icon = QIcon::fromTheme( "zoom-in", QIcon( ":/zoom-in" ) );
247  if ( !icon.isNull() )
248  setCursor( QCursor( icon.pixmap( QSize( 16, 16 ) ) ) );
249  } else {
250  int left = qMin( qMax( d->_zoom.x() - width() / 2, 0 ), d->_pix.width() - width() );
251  int top = qMin( qMax( d->_zoom.y() - height() / 2, 0 ), d->_pix.height() - height() );
252 
253  if ( d->cachePix.isNull() )
254  d->cachePix = QPixmap::fromImage( d->_pix.copy( QRect( QPoint( left, top ), size() ) ) );
255 
256  p.drawPixmap( 0, 0, d->cachePix );
257 
258  setCursor( Qt::CrossCursor );
259  }
260 
261  p.setBrush( QColor( "#D8DF57" ) );
262  p.setPen( QColor( "#B9DFD6" ) );
263  for ( QList<YQTimezoneSelectorPrivate::Location>::const_iterator it = d->locations.begin(); it != d->locations.end(); ++it )
264  {
265  if ( !d->highlight || ( *it ).zone != d->_best.zone )
266  {
267  if ( d->_zoom.isNull() )
268  p.drawEllipse( QRect( d->pixToWindow( ( *it ).pix_pos ) - QPoint( 1,1 ), QSize( 3, 3 ) ) );
269  else
270  p.drawEllipse( QRect( d->pixToWindow( ( *it ).pix_pos ) - QPoint( 2,2 ), QSize( 5, 5 ) ) );
271  }
272  }
273  if ( d->highlight > 0 )
274  {
275 // QPoint pos = d->pixPosition( d->_best );
276  static const QColor blinks[] = { QColor( "#00ff00" ), QColor( "#22dd00" ), QColor( "#44bb00" ),
277  QColor( "#669900" ), QColor( "#887700" ), QColor( "#aa5500" ),
278  QColor( "#887700" ), QColor( "#669900" ), QColor( "#44bb00" ),
279  QColor( "#22dd00" ) };
280  int index = d->highlight - 1;
281  p.setPen( blinks[ index ] );
282  p.setBrush( blinks[ index ] );
283 
284  p.drawEllipse( QRect( d->pixToWindow( d->_best.pix_pos ) - QPoint( 2,2 ), QSize( 5, 5 ) ) );
285 
286  QFont f( font() );
287  f.setBold( true );
288  p.setFont( f );
289  QFontMetrics fm( f );
290 
291  QPoint off = d->pixToWindow( d->_best.pix_pos ) + QPoint( 11, 4 );
292  int tw = fm.width( d->_best.tip );
293  if ( tw + off.x() > width() )
294  off.rx() = d->pixToWindow( d->_best.pix_pos ).x() - tw - 10;
295 
296  p.setPen( Qt::black );
297  p.drawText( off, d->_best.tip );
298 
299  p.setPen( Qt::white );
300  p.drawText( off - QPoint( 1, 1 ), d->_best.tip );
301 
302  }
303 }
304 
305 YQTimezoneSelectorPrivate::Location YQTimezoneSelectorPrivate::findBest( const QPoint &pos ) const
306 {
307  double min_dist = 2000;
308  Location best;
309  for ( QList<Location>::const_iterator it = locations.begin(); it != locations.end(); ++it )
310  {
311  double dist = QPoint( pixToWindow( ( *it ).pix_pos ) - pos ).manhattanLength ();
312  if ( dist < min_dist )
313  {
314  min_dist = dist;
315  best = *it;
316  }
317  }
318  return best;
319 }
320 
321 bool YQTimezoneSelector::event(QEvent *event)
322 {
323  if (event->type() == QEvent::ToolTip)
324  {
325  QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
326 
327  YQTimezoneSelectorPrivate::Location best = d->findBest( helpEvent->pos() );
328  QToolTip::showText(helpEvent->globalPos(), best.tip );
329  }
330  return QWidget::event(event);
331 }
332 
333 
335 {
336  return d->_best.zone.toStdString();
337 }
338 
339 QPoint YQTimezoneSelectorPrivate::pixToWindow( const QPoint &pos ) const
340 {
341  if ( _zoom.isNull() )
342  {
343  return QPoint( (int) ( double( pos.x() ) * cachePix.width() / _pix.width() ) + ( parent->width() - cachePix.width() ) / 2,
344  (int) ( double( pos.y() ) * cachePix.height() / _pix.height() ) + ( parent->height() - cachePix.height() ) /2 );
345  }
346  int left = qMin( qMax( _zoom.x() - parent->width() / 2, 0 ), _pix.width() - parent->width() );
347  int top = qMin( qMax( _zoom.y() - parent->height() / 2, 0 ), _pix.height() - parent->height() );
348 
349  return QPoint( pos.x() - left, pos.y() - top );
350 }
351 
352 void YQTimezoneSelector::setCurrentZone( const std::string &_zone, bool zoom )
353 {
354  QString zone = fromUTF8( _zone );
355 
356  if ( d->_best.zone == zone )
357  return;
358 
360 
361  for ( QList<YQTimezoneSelectorPrivate::Location>::const_iterator it = d->locations.begin(); it != d->locations.end(); ++it )
362  {
363  if ( ( *it ).zone == zone )
364  d->_best = *it;
365  }
366 
367  if ( zoom )
368  d->_zoom = d->_best.pix_pos;
369  else
370  d->_zoom = QPoint();
371 
372  d->cachePix = QPixmap();
373  d->highlight = 1;
374 
375  d->blink->start();
376  update();
377 }
378 
379 void YQTimezoneSelector::slotBlink()
380 {
381  if ( d->_best.zone.isNull() )
382  {
383  d->blink->stop();
384  return;
385  }
386 
387  if ( d->highlight++ > 9 )
388  d->highlight = 1;
389 
390  QPoint current = d->pixToWindow( d->_best.pix_pos );
391  update( QRect( current - QPoint( 3, 3 ), QSize( 7, 7 ) ) );
392 }
393 
394 
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