• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepimlibs API Reference
  • KDE Home
  • Contact Us
 

akonadi/contact

  • sources
  • kde-4.14
  • kdepimlibs
  • akonadi
  • contact
  • editor
geoeditwidget.cpp
1 /*
2  This file is part of Akonadi Contact.
3 
4  Copyright (c) 2009 Tobias Koenig <tokoe@kde.org>
5 
6  This library is free software; you can redistribute it and/or modify it
7  under the terms of the GNU Library General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or (at your
9  option) any later version.
10 
11  This library is distributed in the hope that it will be useful, but WITHOUT
12  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
14  License for more details.
15 
16  You should have received a copy of the GNU Library General Public License
17  along with this library; see the file COPYING.LIB. If not, write to the
18  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  02110-1301, USA.
20 */
21 
22 #include "geoeditwidget.h"
23 
24 #include "autoqpointer_p.h"
25 
26 #include <kabc/addressee.h>
27 #include <kabc/geo.h>
28 #include <kcombobox.h>
29 #include <klocale.h>
30 #include <klocalizedstring.h>
31 #include <kstandarddirs.h>
32 
33 #include <QtCore/QFile>
34 #include <QtCore/QTextStream>
35 #include <QDoubleSpinBox>
36 #include <QGridLayout>
37 #include <QGroupBox>
38 #include <QLabel>
39 #include <QPainter>
40 #include <QPushButton>
41 #include <QSpinBox>
42 
43 class GeoMapWidget : public QWidget
44 {
45 public:
46  GeoMapWidget(QWidget *parent = 0)
47  : QWidget(parent)
48  {
49  mWorld = QPixmap(KStandardDirs::locate("data", QLatin1String("akonadi/contact/pics/world.jpg")));
50 
51  setAttribute(Qt::WA_NoSystemBackground, true);
52  setFixedSize(400, 200);
53 
54  update();
55  }
56 
57  void setCoordinates(const KABC::Geo &coordinates)
58  {
59  mCoordinates = coordinates;
60 
61  update();
62  }
63 
64 protected:
65  virtual void paintEvent(QPaintEvent *)
66  {
67  QPainter p;
68  p.begin(this);
69  p.setPen(QColor(255, 0, 0));
70  p.setBrush(QColor(255, 0, 0));
71 
72  p.drawPixmap(0, 0, mWorld);
73 
74  if (mCoordinates.isValid()) {
75  const double latMid = height() / 2;
76  const double longMid = width() / 2;
77  const double latOffset = (mCoordinates.latitude() * latMid) / 90;
78  const double longOffset = (mCoordinates.longitude() * longMid) / 180;
79 
80  const int x = (int)(longMid + longOffset);
81  const int y = (int)(latMid - latOffset);
82  p.drawEllipse(x, y, 4, 4);
83  }
84 
85  p.end();
86  }
87 
88 private:
89  QPixmap mWorld;
90  KABC::Geo mCoordinates;
91 };
92 
93 GeoEditWidget::GeoEditWidget(QWidget *parent)
94  : QWidget(parent)
95 {
96  QGridLayout *layout = new QGridLayout(this);
97  layout->setMargin(0);
98 
99  mMap = new GeoMapWidget;
100  layout->addWidget(mMap, 0, 0, 1, 4, Qt::AlignCenter | Qt::AlignVCenter);
101 
102  QLabel *label = new QLabel(i18nc("@label", "Latitude:"));
103  label->setAlignment(Qt::AlignRight);
104  layout->addWidget(label, 1, 0);
105 
106  mLatitudeLabel = new QLabel;
107  layout->addWidget(mLatitudeLabel, 1, 1);
108 
109  label = new QLabel(i18nc("@label", "Longitude:"));
110  label->setAlignment(Qt::AlignRight);
111  layout->addWidget(label, 1, 2);
112 
113  mLongitudeLabel = new QLabel;
114  layout->addWidget(mLongitudeLabel, 1, 3);
115 
116  mChangeButton = new QPushButton(i18nc("@label Change the coordinates", "Change..."));
117  layout->addWidget(mChangeButton, 2, 0, 1, 4, Qt::AlignRight);
118 
119  layout->setRowStretch(3, 1);
120 
121  connect(mChangeButton, SIGNAL(clicked()), SLOT(changeClicked()));
122 
123  updateView();
124 }
125 
126 GeoEditWidget::~GeoEditWidget()
127 {
128 }
129 
130 void GeoEditWidget::loadContact(const KABC::Addressee &contact)
131 {
132  mCoordinates = contact.geo();
133  updateView();
134 }
135 
136 void GeoEditWidget::storeContact(KABC::Addressee &contact) const
137 {
138  contact.setGeo(mCoordinates);
139 }
140 
141 void GeoEditWidget::setReadOnly(bool readOnly)
142 {
143  mChangeButton->setEnabled(!readOnly);
144 }
145 
146 void GeoEditWidget::updateView()
147 {
148  if (!mCoordinates.isValid()) {
149  mLatitudeLabel->setText(i18nc("@label Coordinates are not available", "n/a"));
150  mLongitudeLabel->setText(i18nc("@label Coordinates are not available", "n/a"));
151  } else {
152  mLatitudeLabel->setText(i18nc("@label The formatted coordinates", "%1 %2", mCoordinates.latitude(), QChar(176)));
153  mLongitudeLabel->setText(i18nc("@label The formatted coordinates", "%1 %2", mCoordinates.longitude(), QChar(176)));
154  }
155  mMap->setCoordinates(mCoordinates);
156 }
157 
158 void GeoEditWidget::changeClicked()
159 {
160  AutoQPointer<GeoDialog> dlg = new GeoDialog(mCoordinates, this);
161  if (dlg->exec()) {
162  mCoordinates = dlg->coordinates();
163  updateView();
164  }
165 }
166 
167 static double calculateCoordinate(const QString &coordinate)
168 {
169  int neg;
170  int d = 0, m = 0, s = 0;
171  QString str = coordinate;
172 
173  neg = str.left(1) == QLatin1String("-");
174  str.remove(0, 1);
175 
176  switch (str.length()) {
177  case 4:
178  d = str.left(2).toInt();
179  m = str.mid(2).toInt();
180  break;
181  case 5:
182  d = str.left(3).toInt();
183  m = str.mid(3).toInt();
184  break;
185  case 6:
186  d = str.left(2).toInt();
187  m = str.mid(2, 2).toInt();
188  s = str.right(2).toInt();
189  break;
190  case 7:
191  d = str.left(3).toInt();
192  m = str.mid(3, 2).toInt();
193  s = str.right(2).toInt();
194  break;
195  default:
196  break;
197  }
198 
199  if (neg) {
200  return - (d + m / 60.0 + s / 3600.0);
201  } else {
202  return d + m / 60.0 + s / 3600.0;
203  }
204 }
205 
206 GeoDialog::GeoDialog(const KABC::Geo &coordinates, QWidget *parent)
207  : KDialog(parent)
208  , mCoordinates(coordinates)
209 {
210  KGlobal::locale()->insertCatalog(QLatin1String("timezones4"));
211  setCaption(i18nc("@title:window", "Coordinate Selection"));
212  setButtons(Ok | Cancel);
213  setDefaultButton(Ok);
214  showButtonSeparator(true);
215  setModal(true);
216 
217  QFrame *page = new QFrame(this);
218  setMainWidget(page);
219 
220  QVBoxLayout *layout = new QVBoxLayout(page);
221 
222  mCityCombo = new KComboBox(page);
223  layout->addWidget(mCityCombo);
224 
225  QGroupBox *decimalGroup = new QGroupBox(i18nc("@title:group Decimal representation of coordinates", "Decimal"), page);
226  QGridLayout *decimalLayout = new QGridLayout();
227  decimalGroup->setLayout(decimalLayout);
228  decimalLayout->setSpacing(spacingHint());
229 
230  QLabel *label = new QLabel(i18nc("@label:spinbox", "Latitude:"), decimalGroup);
231  decimalLayout->addWidget(label, 0, 0);
232 
233  mLatitude = new QDoubleSpinBox(decimalGroup);
234  mLatitude->setMinimum(-90);
235  mLatitude->setMaximum(90);
236  mLatitude->setSingleStep(1);
237  mLatitude->setValue(0);
238  mLatitude->setDecimals(6);
239  mLatitude->setSuffix(QChar(176));
240  decimalLayout->addWidget(mLatitude, 0, 1);
241 
242  label = new QLabel(i18nc("@label:spinbox", "Longitude:"), decimalGroup);
243  decimalLayout->addWidget(label, 1, 0);
244 
245  mLongitude = new QDoubleSpinBox(decimalGroup);
246  mLongitude->setMinimum(-180);
247  mLongitude->setMaximum(180);
248  mLongitude->setSingleStep(1);
249  mLongitude->setValue(0);
250  mLongitude->setDecimals(6);
251  mLongitude->setSuffix(QChar(176));
252  decimalLayout->addWidget(mLongitude, 1, 1);
253 
254  QGroupBox *sexagesimalGroup = new QGroupBox(i18nc("@title:group", "Sexagesimal"), page);
255  QGridLayout *sexagesimalLayout = new QGridLayout();
256  sexagesimalGroup->setLayout(sexagesimalLayout);
257  sexagesimalLayout->setSpacing(spacingHint());
258 
259  label = new QLabel(i18nc("@label:spinbox", "Latitude:"), sexagesimalGroup);
260  sexagesimalLayout->addWidget(label, 0, 0);
261 
262  mLatDegrees = new QSpinBox(sexagesimalGroup);
263  mLatDegrees->setMinimum(0);
264  mLatDegrees->setMaximum(90);
265  mLatDegrees->setValue(1);
266  mLatDegrees->setSuffix(QChar(176));
267  mLatDegrees->setWrapping(false);
268  label->setBuddy(mLatDegrees);
269  sexagesimalLayout->addWidget(mLatDegrees, 0, 1);
270 
271  mLatMinutes = new QSpinBox(sexagesimalGroup);
272  mLatMinutes->setMinimum(0);
273  mLatMinutes->setMaximum(59);
274  mLatMinutes->setValue(1);
275 
276  mLatMinutes->setSuffix(QLatin1String("'"));
277  sexagesimalLayout->addWidget(mLatMinutes, 0, 2);
278 
279  mLatSeconds = new QSpinBox(sexagesimalGroup);
280  mLatSeconds->setMinimum(0);
281  mLatSeconds->setMaximum(59);
282  mLatSeconds->setValue(1);
283  mLatSeconds->setSuffix(QLatin1String("\""));
284  sexagesimalLayout->addWidget(mLatSeconds, 0, 3);
285 
286  mLatDirection = new KComboBox(sexagesimalGroup);
287  mLatDirection->addItem(i18nc("@item:inlistbox Latitude direction", "North"));
288  mLatDirection->addItem(i18nc("@item:inlistbox Latitude direction", "South"));
289  sexagesimalLayout->addWidget(mLatDirection, 0, 4);
290 
291  label = new QLabel(i18nc("@label:spinbox", "Longitude:"), sexagesimalGroup);
292  sexagesimalLayout->addWidget(label, 1, 0);
293 
294  mLongDegrees = new QSpinBox(sexagesimalGroup);
295  mLongDegrees->setMinimum(0);
296  mLongDegrees->setMaximum(180);
297  mLongDegrees->setValue(1);
298  mLongDegrees->setSuffix(QChar(176));
299  label->setBuddy(mLongDegrees);
300  sexagesimalLayout->addWidget(mLongDegrees, 1, 1);
301 
302  mLongMinutes = new QSpinBox(sexagesimalGroup);
303  mLongMinutes->setMinimum(0);
304  mLongMinutes->setMaximum(59);
305  mLongMinutes->setValue(1);
306  mLongMinutes->setSuffix(QLatin1String("'"));
307  sexagesimalLayout->addWidget(mLongMinutes, 1, 2);
308 
309  mLongSeconds = new QSpinBox(sexagesimalGroup);
310  mLongSeconds->setMinimum(0);
311  mLongSeconds->setMaximum(59);
312  mLongSeconds->setValue(1);
313  mLongSeconds->setSuffix(QLatin1String("\""));
314  sexagesimalLayout->addWidget(mLongSeconds, 1, 3);
315 
316  mLongDirection = new KComboBox(sexagesimalGroup);
317  mLongDirection->addItem(i18nc("@item:inlistbox Longtitude direction", "East"));
318  mLongDirection->addItem(i18nc("@item:inlistbox Longtitude direction", "West"));
319  sexagesimalLayout->addWidget(mLongDirection, 1, 4);
320 
321  layout->addWidget(decimalGroup);
322  layout->addWidget(sexagesimalGroup);
323 
324  loadCityList();
325 
326  connect(mCityCombo, SIGNAL(activated(int)),
327  SLOT(cityInputChanged()));
328  connect(mLatitude, SIGNAL(valueChanged(double)),
329  SLOT(decimalInputChanged()));
330  connect(mLongitude, SIGNAL(valueChanged(double)),
331  SLOT(decimalInputChanged()));
332  connect(mLatDegrees, SIGNAL(valueChanged(int)),
333  SLOT(sexagesimalInputChanged()));
334  connect(mLatMinutes, SIGNAL(valueChanged(int)),
335  SLOT(sexagesimalInputChanged()));
336  connect(mLatSeconds, SIGNAL(valueChanged(int)),
337  SLOT(sexagesimalInputChanged()));
338  connect(mLatDirection, SIGNAL(activated(int)),
339  SLOT(sexagesimalInputChanged()));
340  connect(mLongDegrees, SIGNAL(valueChanged(int)),
341  SLOT(sexagesimalInputChanged()));
342  connect(mLongMinutes, SIGNAL(valueChanged(int)),
343  SLOT(sexagesimalInputChanged()));
344  connect(mLongSeconds, SIGNAL(valueChanged(int)),
345  SLOT(sexagesimalInputChanged()));
346  connect(mLongDirection, SIGNAL(activated(int)),
347  SLOT(sexagesimalInputChanged()));
348 
349  updateInputs();
350 }
351 
352 KABC::Geo GeoDialog::coordinates() const
353 {
354  return mCoordinates;
355 }
356 
357 void GeoDialog::cityInputChanged()
358 {
359  if (mCityCombo->currentIndex() != 0) {
360  GeoData geoData = mGeoDataMap[mCityCombo->currentText()];
361  mCoordinates.setLatitude(geoData.latitude);
362  mCoordinates.setLongitude(geoData.longitude);
363  } else {
364  mCoordinates.setLatitude(0);
365  mCoordinates.setLongitude(0);
366  }
367 
368  updateInputs(ExceptCity);
369 }
370 
371 void GeoDialog::decimalInputChanged()
372 {
373  mCoordinates.setLatitude(mLatitude->value());
374  mCoordinates.setLongitude(mLongitude->value());
375 
376  updateInputs(ExceptDecimal);
377 }
378 
379 void GeoDialog::sexagesimalInputChanged()
380 {
381  double latitude = (double)(mLatDegrees->value() + (double)mLatMinutes->value() /
382  60 + (double)mLatSeconds->value() / 3600);
383  latitude *= (mLatDirection->currentIndex() == 1 ? -1 : 1);
384 
385  double longitude = (double)(mLongDegrees->value() + (double)mLongMinutes->value() /
386  60 + (double)mLongSeconds->value() / 3600);
387  longitude *= (mLongDirection->currentIndex() == 1 ? -1 : 1);
388 
389  mCoordinates.setLatitude(latitude);
390  mCoordinates.setLongitude(longitude);
391 
392  updateInputs(ExceptSexagesimal);
393 }
394 
395 void GeoDialog::updateInputs(ExceptType type)
396 {
397  mCityCombo->blockSignals(true);
398  mLatitude->blockSignals(true);
399  mLongitude->blockSignals(true);
400  mLatDegrees->blockSignals(true);
401  mLatMinutes->blockSignals(true);
402  mLatSeconds->blockSignals(true);
403  mLatDirection->blockSignals(true);
404  mLongDegrees->blockSignals(true);
405  mLongMinutes->blockSignals(true);
406  mLongSeconds->blockSignals(true);
407  mLongDirection->blockSignals(true);
408 
409  if (!(type &ExceptDecimal)) {
410  mLatitude->setValue(mCoordinates.latitude());
411  mLongitude->setValue(mCoordinates.longitude());
412  }
413 
414  if (!(type &ExceptSexagesimal)) {
415  int degrees, minutes, seconds;
416  double latitude = mCoordinates.latitude();
417  double longitude = mCoordinates.longitude();
418 
419  latitude *= (latitude < 0 ? -1 : 1);
420  longitude *= (longitude < 0 ? -1 : 1);
421 
422  degrees = (int)(latitude * 1);
423  minutes = (int)((latitude - degrees) * 60);
424  seconds = (int)((double)((double)latitude - (double)degrees - ((double)minutes / (double)60)) * (double)3600);
425 
426  mLatDegrees->setValue(degrees);
427  mLatMinutes->setValue(minutes);
428  mLatSeconds->setValue(seconds);
429 
430  mLatDirection->setCurrentIndex(mLatitude->value() < 0 ? 1 : 0);
431 
432  degrees = (int)(longitude * 1);
433  minutes = (int)((longitude - degrees) * 60);
434  seconds = (int)((double)(longitude - (double)degrees - ((double)minutes / 60)) * 3600);
435 
436  mLongDegrees->setValue(degrees);
437  mLongMinutes->setValue(minutes);
438  mLongSeconds->setValue(seconds);
439  mLongDirection->setCurrentIndex(mLongitude->value() < 0 ? 1 : 0);
440  }
441 
442  if (!(type &ExceptCity)) {
443  const int index = nearestCity(mCoordinates.longitude(), mCoordinates.latitude());
444  if (index != -1) {
445  mCityCombo->setCurrentIndex(index + 1);
446  } else {
447  mCityCombo->setCurrentIndex(0);
448  }
449  }
450 
451  mCityCombo->blockSignals(false);
452  mLatitude->blockSignals(false);
453  mLongitude->blockSignals(false);
454  mLatDegrees->blockSignals(false);
455  mLatMinutes->blockSignals(false);
456  mLatSeconds->blockSignals(false);
457  mLatDirection->blockSignals(false);
458  mLongDegrees->blockSignals(false);
459  mLongMinutes->blockSignals(false);
460  mLongSeconds->blockSignals(false);
461  mLongDirection->blockSignals(false);
462 }
463 
464 void GeoDialog::loadCityList()
465 {
466  mCityCombo->clear();
467  mGeoDataMap.clear();
468 
469  QFile file(KStandardDirs::locate("data", QLatin1String("akonadi/contact/data/zone.tab")));
470 
471  if (file.open(QIODevice::ReadOnly)) {
472  QTextStream s(&file);
473 
474  QString line, country;
475  QRegExp coord(QLatin1String("[+-]\\d+[+-]\\d+"));
476  QRegExp name(QLatin1String("[^\\s]+/[^\\s]+"));
477  int pos;
478 
479  while (!s.atEnd()) {
480  line = s.readLine().trimmed();
481  if (line.isEmpty() || line[0] == QLatin1Char('#')) {
482  continue;
483  }
484 
485  country = line.left(2);
486  QString c, n;
487  pos = coord.indexIn(line, 0);
488  if (pos >= 0) {
489  c = line.mid(pos, coord.matchedLength());
490  }
491 
492  pos = name.indexIn(line, pos);
493  if (pos > 0) {
494  n = line.mid(pos, name.matchedLength()).trimmed();
495  }
496 
497  if (!c.isEmpty() && !n.isEmpty()) {
498  pos = c.indexOf(QLatin1Char('+'), 1);
499  if (pos < 0) {
500  pos = c.indexOf(QLatin1Char('-'), 1);
501  }
502  if (pos > 0) {
503  GeoData geoData;
504  geoData.latitude = calculateCoordinate(c.left(pos));
505  geoData.longitude = calculateCoordinate(c.mid(pos));
506  geoData.country = country;
507 
508  mGeoDataMap.insert(i18n(qPrintable(n)).replace(QLatin1Char('_'), QLatin1Char(' ')), geoData);
509  }
510  }
511  }
512 
513  QStringList items(mGeoDataMap.keys());
514  items.prepend(i18nc("@item:inlistbox Undefined location", "Undefined"));
515  mCityCombo->addItems(items);
516 
517  file.close();
518  }
519 }
520 
521 int GeoDialog::nearestCity(double x, double y) const
522 {
523  QMap<QString, GeoData>::ConstIterator it;
524  int pos = 0;
525  for (it = mGeoDataMap.begin(); it != mGeoDataMap.end(); ++it, ++pos) {
526  double dist = ((*it).longitude - x) * ((*it).longitude - x) +
527  ((*it).latitude - y) * ((*it).latitude - y);
528  if (dist < 0.0005) {
529  return pos;
530  }
531  }
532 
533  return -1;
534 }
QWidget::layout
QLayout * layout() const
QString::indexOf
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
QWidget
QPainter::end
bool end()
QGridLayout::addWidget
void addWidget(QWidget *widget, int row, int column, QFlags< Qt::AlignmentFlag > alignment)
QChar
QMap
QWidget::y
y
QWidget::setAttribute
void setAttribute(Qt::WidgetAttribute attribute, bool on)
QLabel::setAlignment
void setAlignment(QFlags< Qt::AlignmentFlag >)
QGridLayout
QString::remove
QString & remove(int position, int n)
QFile
QWidget::update
void update()
QTextStream
QGridLayout::setSpacing
void setSpacing(int spacing)
QWidget::width
int width() const
QLabel::setBuddy
void setBuddy(QWidget *buddy)
QRegExp
QDoubleSpinBox
QBoxLayout::addWidget
void addWidget(QWidget *widget, int stretch, QFlags< Qt::AlignmentFlag > alignment)
QWidget::paintEvent
virtual void paintEvent(QPaintEvent *event)
QString::insert
QString & insert(int position, QChar ch)
QGridLayout::setRowStretch
void setRowStretch(int row, int stretch)
QWidget::setLayout
void setLayout(QLayout *layout)
QGroupBox
QWidget::x
x
QPainter::setPen
void setPen(const QColor &color)
QPainter::drawEllipse
void drawEllipse(const QRectF &rectangle)
QPainter::drawPixmap
void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
QString::toInt
int toInt(bool *ok, int base) const
QPainter
QString::isEmpty
bool isEmpty() const
QString::trimmed
QString trimmed() const
QPainter::setBrush
void setBrush(const QBrush &brush)
QVBoxLayout
QString
QMap::end
iterator end()
QColor
QLayout::setMargin
void setMargin(int margin)
QMap::begin
iterator begin()
QStringList
QString::right
QString right(int n) const
QPixmap
QSpinBox
QWidget::setFixedSize
void setFixedSize(const QSize &s)
QLatin1Char
AutoQPointer
A QPointer which when destructed, deletes the object it points to.
Definition: autoqpointer_p.h:34
QFrame
QString::mid
QString mid(int position, int n) const
QLatin1String
QWidget::setCaption
void setCaption(const QString &c)
QString::length
int length() const
QString::left
QString left(int n) const
QPushButton
QList::prepend
void prepend(const T &value)
QPaintEvent
QObject::connect
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QLabel
QObject::parent
QObject * parent() const
QPainter::begin
bool begin(QPaintDevice *device)
QWidget::height
int height() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:38:19 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

akonadi/contact

Skip menu "akonadi/contact"
  • Main Page
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Related Pages

kdepimlibs API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal