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

kstars

  • sources
  • kde-4.12
  • kdeedu
  • kstars
  • kstars
  • indi
indielement.cpp
Go to the documentation of this file.
1 /* INDI Element
2  Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com)
3 
4  This application is free software; you can redistribute it and/or
5  modify it under the terms of the GNU General Public
6  License as published by the Free Software Foundation; either
7  version 2 of the License, or (at your option) any later version.
8 
9  2004-01-15 INDI element is the most basic unit of the INDI KStars client.
10  */
11 
12 #include <base64.h>
13 
14 #include "indielement.h"
15 #include "indiproperty.h"
16 #include "indigroup.h"
17 #include "indidevice.h"
18 
19 #include <indicom.h>
20 
21 #include <QCheckBox>
22 #include <QLabel>
23 #include <QLayout>
24 #include <QSlider>
25 #include <QDir>
26 #include <QHBoxLayout>
27 #include <QButtonGroup>
28 #include <QFont>
29 #include <QDoubleSpinBox>
30 #include <QDebug>
31 
32 #include <KPushButton>
33 #include <KSqueezedTextLabel>
34 #include <KLineEdit>
35 #include <KLocale>
36 #include <KLed>
37 #include <KFileDialog>
38 #include <KMessageBox>
39 
40 extern const char *libindi_strings_context;
41 
42 /*******************************************************************
43 ** INDI Element
44 *******************************************************************/
45 INDI_E::INDI_E(INDI_P *gProp, INDI::Property *dProp)
46 {
47  guiProp = gProp;
48  dataProp = dProp;
49 
50  EHBox = new QHBoxLayout;
51  EHBox->setMargin(0);
52  EHBox->setSpacing(KDialog::spacingHint());
53 
54  tp = NULL;
55  sp = NULL;
56  np = NULL;
57  label_w = NULL;
58  read_w = NULL;
59  write_w = NULL;
60  spin_w = NULL;
61  slider_w = NULL;
62  push_w = NULL;
63  browse_w = NULL;
64  check_w = NULL;
65  led_w = NULL;
66  hSpacer = NULL;
67 
68 }
69 
70 INDI_E::~INDI_E()
71 {
72  delete (EHBox);
73  delete (label_w);
74  delete (read_w);
75  delete (write_w);
76  delete (spin_w);
77  delete (slider_w);
78  delete (push_w);
79  delete (browse_w);
80  delete (check_w);
81  delete (led_w);
82  delete (hSpacer);
83 }
84 
85 void INDI_E::buildSwitch(QButtonGroup* groupB, ISwitch *sw)
86 {
87  name = sw->name;
88  label = i18nc(libindi_strings_context, sw->label);
89 
90  if (label == "(I18N_EMPTY_MESSAGE)")
91  label = sw->label;
92 
93  sp = sw;
94 
95  if (label.isEmpty())
96  label = i18nc(libindi_strings_context, sw->name);
97 
98  if (label == "(I18N_EMPTY_MESSAGE)")
99  label = sw->name;
100 
101  if (groupB == NULL)
102  return;
103 
104  switch (guiProp->getGUIType())
105  {
106  case PG_BUTTONS:
107  push_w = new KPushButton(label, guiProp->getGroup()->getContainer());
108  groupB->addButton(push_w);
109 
110  syncSwitch();
111 
112  guiProp->addWidget(push_w);
113 
114  push_w->show();
115 
116  break;
117 
118  case PG_RADIO:
119  check_w = new QCheckBox(label, guiProp->getGroup()->getContainer());
120  groupB->addButton(check_w);
121 
122  syncSwitch();
123 
124  guiProp->addWidget(check_w);
125 
126  check_w->show();
127 
128  break;
129 
130  default:
131  break;
132 
133  }
134 
135 }
136 
137 void INDI_E::buildMenuItem(ISwitch *sw)
138 {
139  buildSwitch(NULL, sw);
140 }
141 
142 void INDI_E::buildText(IText *itp)
143 {
144  name = itp->name;
145  if (itp->label[0])
146  label = i18nc(libindi_strings_context, itp->label);
147 
148  if (label == "(I18N_EMPTY_MESSAGE)")
149  label = itp->label;
150 
151  tp = itp;
152 
153  if (label.isEmpty())
154  label = i18nc(libindi_strings_context, itp->name);
155 
156  if (label == "(I18N_EMPTY_MESSAGE)")
157  label = itp->name;
158 
159  setupElementLabel();
160 
161  if (tp->text[0])
162  text = i18nc(libindi_strings_context, tp->text);
163 
164  switch (dataProp->getPermission())
165  {
166  case IP_RW:
167  setupElementRead(ELEMENT_READ_WIDTH);
168  setupElementWrite(ELEMENT_WRITE_WIDTH);
169 
170  break;
171 
172  case IP_RO:
173  setupElementRead(ELEMENT_FULL_WIDTH);
174  break;
175 
176  case IP_WO:
177  setupElementWrite(ELEMENT_FULL_WIDTH);
178  break;
179  }
180 
181 
182  guiProp->addLayout(EHBox);
183  //pp->PVBox->addLayout(EHBox);
184 }
185 
186 void INDI_E::setupElementLabel()
187 {
188  QPalette palette;
189 
190  label_w = new KSqueezedTextLabel(guiProp->getGroup()->getContainer());
191  label_w->setMinimumWidth(ELEMENT_LABEL_WIDTH);
192  label_w->setMaximumWidth(ELEMENT_LABEL_WIDTH);
193  label_w->setFrameShape( KSqueezedTextLabel::Box );
194 
195  palette.setColor(label_w->backgroundRole(), QColor( 224, 232, 238 ));
196  label_w->setPalette(palette);
197  label_w->setTextFormat( Qt::RichText );
198  label_w->setAlignment( Qt::AlignCenter );
199  label_w->setWordWrap(true);
200 
201  if (label.length() > MAX_LABEL_LENGTH)
202  {
203  QFont tempFont( label_w->font() );
204  tempFont.setPointSize( tempFont.pointSize() - MED_INDI_FONT );
205  label_w->setFont( tempFont );
206  }
207 
208  label_w->setText(label);
209 
210  EHBox->addWidget(label_w);
211 }
212 
213 void INDI_E::syncSwitch()
214 {
215  QFont buttonFont;
216 
217  switch (guiProp->getGUIType())
218  {
219  case PG_BUTTONS:
220  if (sp->s == ISS_ON)
221  {
222  push_w->setDown(true);
223  buttonFont = push_w->font();
224  buttonFont.setBold(true);
225  push_w->setFont(buttonFont);
226  }
227  else
228  {
229  push_w->setDown(false);
230  buttonFont = push_w->font();
231  buttonFont.setBold(false);
232  push_w->setFont(buttonFont);
233  }
234  break;
235 
236  case PG_RADIO:
237  if (sp->s == ISS_ON)
238  check_w->setChecked(true);
239  else
240  check_w->setChecked(false);
241  break;
242 
243  default:
244  break;
245 
246  }
247 }
248 
249 void INDI_E::syncText()
250 {
251 
252  if (tp == NULL)
253  return;
254 
255  read_w->setText(i18nc(libindi_strings_context, tp->text));
256 
257 }
258 
259 void INDI_E::syncNumber()
260 {
261 
262  char iNumber[32];
263  if (np == NULL || read_w == NULL)
264  return;
265 
266  numberFormat(iNumber, np->format, np->value);
267 
268  text = iNumber;
269 
270  read_w->setText(text);
271 
272  if (spin_w)
273  {
274  if (np->min != spin_w->minimum())
275  setMin();
276  if (np->max != spin_w->maximum())
277  setMax();
278  }
279 
280 }
281 
282 void INDI_E::updateTP()
283 {
284 
285  if (tp == NULL)
286  return;
287 
288  IUSaveText(tp, write_w->text().toLatin1().constData());
289 }
290 
291 void INDI_E::updateNP()
292 {
293  if (np == NULL)
294  return;
295 
296  if (write_w != NULL)
297  {
298  if (write_w->text().isEmpty())
299  return;
300 
301  f_scansexa(write_w->text().toLatin1().constData(), &(np->value));
302  return;
303  }
304 
305  if (spin_w != NULL)
306  np->value = spin_w->value();
307 
308 }
309 
310 void INDI_E::setText(const QString &newText)
311 {
312  if (tp == NULL)
313  return;
314 
315  switch(dataProp->getPermission())
316  {
317  case IP_RO:
318  read_w->setText(newText);
319  break;
320 
321  case IP_WO:
322  case IP_RW:
323  text = newText;
324  IUSaveText(tp, newText.toLatin1().constData());
325  read_w->setText(newText);
326  write_w->setText(newText);
327  break;
328  }
329 
330 }
331 
332 void INDI_E::buildBLOB(IBLOB *ibp)
333 {
334  name = ibp->name;
335  label = i18nc(libindi_strings_context, ibp->label);
336 
337  if (label == "(I18N_EMPTY_MESSAGE)")
338  label = ibp->label;
339 
340  bp = ibp;
341 
342  if (label.isEmpty())
343  label = i18nc(libindi_strings_context, ibp->name);
344 
345  if (label == "(I18N_EMPTY_MESSAGE)")
346  label = ibp->name;
347 
348  setupElementLabel();
349 
350  text = i18n("INDI DATA STREAM");
351 
352  switch (dataProp->getPermission())
353  {
354  case IP_RW:
355  setupElementRead(ELEMENT_READ_WIDTH);
356  setupElementWrite(ELEMENT_WRITE_WIDTH);
357  setupBrowseButton();
358  break;
359 
360  case IP_RO:
361  setupElementRead(ELEMENT_FULL_WIDTH);
362  break;
363 
364  case IP_WO:
365  setupElementWrite(ELEMENT_FULL_WIDTH);
366  setupBrowseButton();
367  break;
368  }
369 
370  guiProp->addLayout(EHBox);
371 
372 }
373 
374 void INDI_E::buildNumber (INumber *inp)
375 {
376  bool scale = false;
377  char iNumber[32];
378 
379  name = inp->name;
380  label = i18nc(libindi_strings_context, inp->label);
381 
382  if (label == "(I18N_EMPTY_MESSAGE)")
383  label = inp->label;
384 
385  np = inp;
386 
387  if (label.isEmpty())
388  label = i18nc(libindi_strings_context, inp->name);
389 
390  if (label == "(I18N_EMPTY_MESSAGE)")
391  label = inp->name;
392 
393  numberFormat(iNumber, np->format, np->value);
394  text = iNumber;
395 
396  setupElementLabel();
397 
398  if (np->step != 0 && (np->max - np->min)/np->step <= 100)
399  scale = true;
400 
401  switch (dataProp->getPermission())
402  {
403  case IP_RW:
404  setupElementRead(ELEMENT_READ_WIDTH);
405  if (scale)
406  setupElementScale(ELEMENT_WRITE_WIDTH);
407  else
408  setupElementWrite(ELEMENT_WRITE_WIDTH);
409 
410  guiProp->addLayout(EHBox);
411  break;
412 
413  case IP_RO:
414  setupElementRead(ELEMENT_READ_WIDTH);
415  guiProp->addLayout(EHBox);
416  break;
417 
418  case IP_WO:
419  if (scale)
420  setupElementScale(ELEMENT_FULL_WIDTH);
421  else
422  setupElementWrite(ELEMENT_FULL_WIDTH);
423 
424  guiProp->addLayout(EHBox);
425 
426  break;
427  }
428 
429 }
430 
431 void INDI_E::buildLight(ILight *ilp)
432 {
433  name = ilp->name;
434  label = i18nc(libindi_strings_context, ilp->label);
435 
436  if (label == "(I18N_EMPTY_MESSAGE)")
437  label = ilp->label;
438 
439  lp = ilp;
440 
441  if (label.isEmpty())
442  label = i18nc(libindi_strings_context, ilp->name);
443 
444  if (label == "(I18N_EMPTY_MESSAGE)")
445  label = ilp->name;
446 
447  led_w = new KLed (guiProp->getGroup()->getContainer());
448  led_w->setMaximumSize(16,16);
449  led_w->setLook( KLed::Sunken );
450 
451  syncLight();
452 
453  EHBox->addWidget(led_w);
454 
455  setupElementLabel();
456 
457  guiProp->addLayout(EHBox);
458 
459 }
460 
461 void INDI_E::syncLight()
462 {
463  if (lp == NULL)
464  return;
465 
466  switch (lp->s)
467  {
468  case IPS_IDLE:
469  led_w->setColor(Qt::gray);
470  break;
471 
472  case IPS_OK:
473  led_w->setColor(Qt::green);
474  break;
475 
476  case IPS_BUSY:
477  led_w->setColor(Qt::yellow);
478  break;
479 
480  case IPS_ALERT:
481  led_w->setColor(Qt::red);
482  break;
483 
484  default:
485  break;
486 
487  }
488 }
489 
490 void INDI_E::setupElementScale(int length)
491 {
492 
493  if (np == NULL)
494  return;
495 
496  int steps = (int) ((np->max - np->min) / np->step);
497  spin_w = new QDoubleSpinBox(guiProp->getGroup()->getContainer());
498  spin_w->setRange(np->min, np->max);
499  spin_w->setSingleStep(np->step);
500  spin_w->setValue(np->value);
501  spin_w->setDecimals(2);
502 
503  slider_w = new QSlider( Qt::Horizontal, guiProp->getGroup()->getContainer() );
504  slider_w->setRange(0, steps);
505  slider_w->setPageStep(1);
506  slider_w->setValue((int) ((np->value - np->min) / np->step));
507 
508  connect(spin_w, SIGNAL(valueChanged(double)), this, SLOT(spinChanged(double )));
509  connect(slider_w, SIGNAL(sliderMoved(int)), this, SLOT(sliderChanged(int )));
510 
511 
512  if (length == ELEMENT_FULL_WIDTH)
513  spin_w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
514  else
515  spin_w->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
516 
517  spin_w->setMinimumWidth( (int) (length * 0.45) );
518  slider_w->setMinimumWidth( (int) (length * 0.55) );
519 
520  EHBox->addWidget(slider_w);
521  EHBox->addWidget(spin_w);
522 
523 }
524 
525 void INDI_E::spinChanged(double value)
526 {
527 
528  int slider_value = (int) ((value - np->min) / np->step);
529  slider_w->setValue(slider_value);
530 
531 }
532 
533 void INDI_E::sliderChanged(int value)
534 {
535 
536  double spin_value = (value * np->step) + np->min;
537  spin_w->setValue(spin_value);
538 
539 
540 }
541 
542 void INDI_E::setMin ()
543 {
544 
545  if (spin_w)
546  {
547  spin_w->setMinimum(np->min);
548  spin_w->setValue(np->value);
549  }
550  if (slider_w)
551  {
552  slider_w->setMaximum((int) ((np->max - np->min) / np->step));
553  slider_w->setMinimum(0);
554  slider_w->setPageStep(1);
555  slider_w->setValue( (int) ((np->value - np->min) / np->step ));
556  }
557 
558 }
559 
560 void INDI_E::setMax ()
561 {
562  if (spin_w)
563  {
564  spin_w->setMaximum(np->max);
565  spin_w->setValue(np->value);
566  }
567  if (slider_w)
568  {
569  slider_w->setMaximum((int) ((np->max - np->min) / np->step));
570  slider_w->setMinimum(0);
571  slider_w->setPageStep(1);
572  slider_w->setValue( (int) ((np->value - np->min) / np->step ));
573  }
574 }
575 
576 void INDI_E::setupElementWrite(int length)
577 {
578 
579  write_w = new KLineEdit( guiProp->getGroup()->getContainer());
580  write_w->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred);
581  write_w->setMinimumWidth( length );
582  write_w->setMaximumWidth( length);
583 
584  write_w->setText(text);
585 
586  //QObject::connect(write_w, SIGNAL(returnPressed(QString)), this, SLOT(updateTP()));
587  QObject::connect(write_w, SIGNAL(returnPressed()), guiProp, SLOT(sendText()));
588  EHBox->addWidget(write_w);
589 
590 }
591 
592 
593 void INDI_E::setupElementRead(int length)
594 {
595 
596 
597  read_w = new KLineEdit( guiProp->getGroup()->getContainer() );
598  read_w->setMinimumWidth( length );
599  read_w->setFocusPolicy( Qt::NoFocus );
600  read_w->setCursorPosition( 0 );
601  read_w->setAlignment( Qt::AlignCenter );
602  read_w->setReadOnly( true );
603  read_w->setText(text);
604 
605  EHBox->addWidget(read_w);
606 
607 
608 }
609 
610 void INDI_E::setupBrowseButton()
611 {
612  browse_w = new KPushButton("...", guiProp->getGroup()->getContainer());
613  browse_w->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
614  browse_w->setMinimumWidth( MIN_SET_WIDTH );
615  browse_w->setMaximumWidth( MAX_SET_WIDTH );
616 
617  EHBox->addWidget(browse_w);
618  QObject::connect(browse_w, SIGNAL(clicked()), this, SLOT(browseBlob()));
619 }
620 
621 
622 void INDI_E::browseBlob()
623 {
624 
625  QFile fp;
626  QString filename;
627  QString format;
628  QDataStream binaryStream;
629  int data64_size=0, pos=0;
630  unsigned char *data_file;
631  KUrl currentURL;
632 
633  currentURL = KFileDialog::getOpenUrl( QDir::homePath(), "*");
634 
635  // if user presses cancel
636  if (currentURL.isEmpty())
637  return;
638 
639  if ( currentURL.isValid() )
640  write_w->setText(currentURL.path());
641 
642  fp.setFileName(currentURL.path());
643 
644  if ( (pos = filename.lastIndexOf(".")) != -1)
645  format = filename.mid (pos, filename.length());
646 
647  //qDebug() << "Filename is " << fp.fileName() << endl;
648 
649  if (!fp.open(QIODevice::ReadOnly))
650  {
651  KMessageBox::error(0, i18n("Cannot open file %1 for reading", filename));
652  return;
653  }
654 
655  binaryStream.setDevice(&fp);
656 
657  data_file = new unsigned char[fp.size()];
658 
659  bp->bloblen = fp.size();
660 
661  if (data_file == NULL)
662  {
663  KMessageBox::error(0, i18n("Not enough memory to load %1", filename));
664  fp.close();
665  return;
666  }
667 
668  binaryStream.readRawData((char*)data_file, fp.size());
669 
670  bp->blob = new unsigned char[4*fp.size()/3+4];
671  if (bp->blob == NULL)
672  {
673  KMessageBox::error(0, i18n("Not enough memory to convert file %1 to base64", filename));
674  fp.close();
675  }
676 
677  data64_size = to64frombits ( ((unsigned char *) bp->blob), data_file, fp.size());
678 
679  delete [] data_file;
680 
681  bp->size = data64_size;
682 
683  //qDebug() << "BLOB " << bp->name << " has size of " << bp->size << " and bloblen of " << bp->bloblen << endl;
684 
685  blobDirty = true;
686 
687 }
688 
689 QString INDI_E::getWriteField()
690 {
691  if (write_w)
692  return write_w->text();
693  else
694  return NULL;
695 }
696 
697 QString INDI_E::getReadField()
698 {
699  if (read_w)
700  return read_w->text();
701  else
702  return NULL;
703 }
704 
705 #include "indielement.moc"
ELEMENT_FULL_WIDTH
#define ELEMENT_FULL_WIDTH
Definition: indicommon.h:37
INDI_P
Definition: indiproperty.h:38
INDI_E::setupElementWrite
void setupElementWrite(int length)
Definition: indielement.cpp:576
INDI_E::syncText
void syncText()
Definition: indielement.cpp:249
ELEMENT_READ_WIDTH
#define ELEMENT_READ_WIDTH
Definition: indicommon.h:35
INDI_E::~INDI_E
~INDI_E()
Definition: indielement.cpp:70
MAX_LABEL_LENGTH
#define MAX_LABEL_LENGTH
Definition: indicommon.h:41
INDI_E::setupElementScale
void setupElementScale(int length)
Definition: indielement.cpp:490
INDI_E::syncLight
void syncLight()
Definition: indielement.cpp:461
INDI_P::addLayout
void addLayout(QHBoxLayout *layout)
Definition: indiproperty.cpp:555
MAX_SET_WIDTH
#define MAX_SET_WIDTH
Definition: indicommon.h:39
INDI_E::INDI_E
INDI_E(INDI_P *gProp, INDI::Property *dProp)
Definition: indielement.cpp:45
indidevice.h
MIN_SET_WIDTH
#define MIN_SET_WIDTH
Definition: indicommon.h:38
INDI_E::updateNP
void updateNP()
Definition: indielement.cpp:291
INDI_E::syncSwitch
void syncSwitch()
Definition: indielement.cpp:213
INDI_E::syncNumber
void syncNumber()
Definition: indielement.cpp:259
MED_INDI_FONT
#define MED_INDI_FONT
Definition: indicommon.h:40
PG_BUTTONS
Definition: indicommon.h:46
INDI_E::sliderChanged
void sliderChanged(int value)
Definition: indielement.cpp:533
INDI_E::browseBlob
void browseBlob()
Definition: indielement.cpp:622
INDI_E::buildText
void buildText(IText *itp)
Definition: indielement.cpp:142
INDI_E::getWriteField
QString getWriteField()
Definition: indielement.cpp:689
INDI_E::buildLight
void buildLight(ILight *ilp)
Definition: indielement.cpp:431
INDI_E::buildNumber
void buildNumber(INumber *inp)
Definition: indielement.cpp:374
INDI_E::setupBrowseButton
void setupBrowseButton()
Definition: indielement.cpp:610
i18nc
i18nc("string from libindi, used in the config dialog","100x")
INDI_E::getReadField
QString getReadField()
Definition: indielement.cpp:697
INDI_E::setupElementRead
void setupElementRead(int length)
Definition: indielement.cpp:593
INDI_E::buildMenuItem
void buildMenuItem(ISwitch *sw)
Definition: indielement.cpp:137
INDI_E::updateTP
void updateTP()
Definition: indielement.cpp:282
ELEMENT_WRITE_WIDTH
#define ELEMENT_WRITE_WIDTH
Definition: indicommon.h:36
INDI_G::getContainer
QFrame * getContainer()
Definition: indigroup.h:43
INDI_E::setText
void setText(const QString &newText)
Definition: indielement.cpp:310
KLineEdit
INDI_E::setMax
void setMax()
Definition: indielement.cpp:560
INDI_E::setupElementLabel
void setupElementLabel()
Definition: indielement.cpp:186
ELEMENT_LABEL_WIDTH
#define ELEMENT_LABEL_WIDTH
Definition: indicommon.h:34
INDI_P::addWidget
void addWidget(QWidget *w)
Definition: indiproperty.cpp:550
PG_RADIO
Definition: indicommon.h:47
INDI_E::buildBLOB
void buildBLOB(IBLOB *ibp)
Definition: indielement.cpp:332
INDI_E::setMin
void setMin()
Definition: indielement.cpp:542
indiproperty.h
INDI_E::spinChanged
void spinChanged(double value)
Definition: indielement.cpp:525
indigroup.h
indielement.h
libindi_strings_context
const char * libindi_strings_context
Definition: indidevice.cpp:54
INDI_P::getGUIType
PGui getGUIType()
Definition: indiproperty.h:71
INDI_E::buildSwitch
void buildSwitch(QButtonGroup *groupB, ISwitch *sw)
Definition: indielement.cpp:85
INDI_P::getGroup
INDI_G * getGroup()
Definition: indiproperty.h:73
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:36:19 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kstars

Skip menu "kstars"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdeedu API Reference

Skip menu "kdeedu API Reference"
  • Analitza
  •     lib
  • kalgebra
  • kalzium
  •   libscience
  • kanagram
  • kig
  •   lib
  • klettres
  • kstars
  • libkdeedu
  •   keduvocdocument
  • marble
  • parley
  • rocs
  •   App
  •   RocsCore
  •   VisualEditor
  •   stepcore

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