/*
Copyright (c) 2010, Stijn Leenknegt
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the KHL nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY Stijn Leenknegt ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL Stijn Leenknegt BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "entrywidget.h"
#include "khluurrooster.h"
KHLUurrooster::KHLUurrooster(QObject *parent, const QVariantList &args)
: Plasma::Applet(parent, args)
{
// this will get us the standard applet background, for free!
setHasConfigurationInterface(true);
setBackgroundHints(DefaultBackground);
resize(300, 200);
//setup the rooster object
rooster = new Uurrooster();
connect(rooster, SIGNAL(errorOccurred(QString)), this, SLOT(roosterError(QString)));
connect(rooster, SIGNAL(entriesChanged()), this, SLOT(sendNextIcalLink()));
//set the timer who get the latest uurrooster information online
timerUpdate = new QTimer(this);
connect(timerUpdate, SIGNAL(timeout()), this, SLOT(updateRoosters()));
//set the refresh timer
timerRefresh = new QTimer(this);
timerRefresh->start(60 * 1000); //om de minuut de entries hertekenen
connect(timerRefresh, SIGNAL(timeout()), this, SLOT(displayEntries()));
//set the error timer
timerError = new QTimer(this);
connect(timerError, SIGNAL(timeout()), this, SLOT(updateRoosters()));
//make layout for the widget
layout = new QGraphicsGridLayout(this);
setLayout(layout);
//set date
date = QDate::currentDate();
//set UI components
headerLabel = new Plasma::Label();
vorigeButton = new Plasma::PushButton();
volgendeButton = new Plasma::PushButton();
m_scrollWidget = new Plasma::ScrollWidget();
m_scroller = new QGraphicsWidget();
m_scrollerLayout = new QGraphicsLinearLayout(Qt::Vertical, m_scroller);
//configre the UI components
headerLabel->setAlignment(Qt::AlignCenter);
m_scrollWidget->setMinimumSize(300, 200);
m_scrollWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
m_scrollWidget->setWidget(m_scroller);
//connect the buttons
connect(vorigeButton, SIGNAL(clicked()), this, SLOT(prevDate()));
connect(volgendeButton, SIGNAL(clicked()), this, SLOT(nextDate()));
//add to layout
layout->addItem(headerLabel, 0, 0, 1, 2);
layout->addItem(m_scrollWidget, 1, 0, 1, 2);
layout->addItem(vorigeButton, 2, 0);
layout->addItem(volgendeButton, 2, 1);
}
KHLUurrooster::~KHLUurrooster()
{
}
void KHLUurrooster::init()
{
readConfig();
//make ~/.khl_personal.xml
file = new QFile(QDir::homePath() + "/.khl_personal.xml");
if(! file->exists()) {
QDomDocument doc("personal");
QDomElement root = doc.createElement("entries");
doc.appendChild(root);
file->open(QIODevice::WriteOnly);
file->write(doc.toByteArray());
file->close();
}
if(ical_1.isEmpty() && ical_2.isEmpty() && ical_3.isEmpty()) { //no configuration setted yet
this->setConfigurationRequired(true, "Je moet op zijn minst 1 iCal link opgeven");
} else {
configChanged();
}
}
void KHLUurrooster::showHelp()
{
KDialog *dialog = new KDialog();
dialog->setCaption("Help pagina");
dialog->setButtons(KDialog::Ok);
KRichTextWidget *widget = new KRichTextWidget(dialog);
widget->setHtml("KHL Uurrooster KDE widget
Over de widget
De KHL Uurrooster KDE widget is een KDE widget. Een KDE widget is een programma dat draait op je bureaublad. De bedoeling van deze widget is om het uurrooster van de student te tonen. Omdat er op iedere uurrooster pagia van de KHL een iCal link is voorzien, werkt deze widget met die iCal kalender.
De widget laat de verschillende uurroosters zien in verschillende kleuren en kan ook zelfde vakken verbergen. Stel dat je les volgt in verschillende reeksen, dan kan het zijn dat die reeksen gemeenschappelijke vakken heeft, die kan je laten verbergen.
Je kan ook zelf afspraken toevoegen. Dit kan handig zijn om je uurrooster een persoonlijke touch te geven. Zo kan je afspraken met lectoren erbij plaatsen.
Deze widget is gemaakt door Stijn Leenknegt, student aan de KHL departement G&T, voor het vak Small ICT Projects. De widget kan je ook vinden op de volgende website: http://aiids.wordpress.com/khl-uurrooster-applet/.
Configuratie
Bij de configuratie heb je drie tabs: iCal instellingen, Opties en Afspraak toevoegen. De eerste tab is belangrijk. Wanneer je op de portaal site bent en je gaat naar je uurrooster zie je op de pagina de tekst ICal-adres voor deze uurrooster staan. De link moet je kopieren en plakken in een veldje. Je begint bij iCal link 1 en gaat zo verder tot iCal link 3. Je kan dus drie uurroosters toevoegen indien je les volgt in meerdere reeksen.
Bij het tablblad Opties kan je de kleuren instellen en ook kiezen of je dubbele vakken wilt verbergen.
Zelf afspraken toevoegen
Je kan ook zelf afspraken toevoegen. Bij de configuratie klik je op het derde tabblad en vul je de gegevens in. Wanneer je op toevoegen klikt, wordt alles toevoegd aan een XML bestand (~/.khl_personal.xml) en getoond in je uurrooster widget.
");
dialog->setMainWidget(widget);
dialog->setGeometry(0, 0, 600, 450);
dialog->show();
}
void KHLUurrooster::createConfigurationInterface(KConfigDialog *parent)
{
QWidget *widget = new QWidget(0);
ui.setupUi(widget);
//als er op ok of apply gedrukt is, moet de config opgeslaan worden
connect(parent, SIGNAL(okClicked()), this, SLOT(configAccepted()));
connect(parent, SIGNAL(applyClicked()), this, SLOT(configAccepted()));
connect(parent, SIGNAL(user1Clicked()), this, SLOT(showHelp()));
//stel de value van de textbox in
ui.txtIcal_1->setText(ical_1);
ui.txtIcal_2->setText(ical_2);
ui.txtIcal_3->setText(ical_3);
//stel de opties in
ui.checkDuplicates->setChecked(hide_duplicates);
if(! ical_1_color.isEmpty()) {
ui.comboKleur1->setCurrentIndex(ui.comboKleur1->findText(ical_1_color));
}
if(! ical_2_color.isEmpty()) {
ui.comboKleur2->setCurrentIndex(ui.comboKleur1->findText(ical_2_color));
}
if(! ical_3_color.isEmpty()) {
ui.comboKleur3->setCurrentIndex(ui.comboKleur1->findText(ical_3_color));
}
if(! personal_color.isEmpty()) {
ui.comboKleurA->setCurrentIndex(ui.comboKleur1->findText(personal_color));
}
//personal entries toevoegen
ui.dtStart->setDateTime(QDateTime::currentDateTime());
ui.dtEind->setDateTime(QDateTime::currentDateTime().addSecs(3600));
connect(ui.btnToevoegen, SIGNAL(clicked(bool)), this, SLOT(savePersonalEntry()));
//alle persoonlijke entries toevoegen aan de delete lijst
QList *p_entries = rooster->getPersonalEntries();
for(int i = 0 ; i < p_entries->size() ; i++) {
ui.comboDelete->addItem(p_entries->at(i).beschrijving);
}
connect(ui.btnDelete, SIGNAL(clicked(bool)), this, SLOT(deletePersonalEntry()));
parent->setButtons(KDialog::Ok | KDialog::Apply | KDialog::Cancel | KDialog::User1);
parent->setButtonIcon(KDialog::User1, KIcon("help-contents"));
parent->setButtonText(KDialog::User1, "Help");
parent->setGeometry(QRect(0, 0, 475, 350));
parent->addPage(widget, "KHL Uurrooster", "view-calendar-list");
}
void KHLUurrooster::readConfig()
{
KConfigGroup cg = config();
ical_1 = cg.readEntry("ical_1");
ical_2 = cg.readEntry("ical_2");
ical_3 = cg.readEntry("ical_3");
ical_1_color = cg.readEntry("ical_1_color");
ical_2_color = cg.readEntry("ical_2_color");
ical_3_color = cg.readEntry("ical_3_color");
personal_color = cg.readEntry("personal_color");
hide_duplicates = (cg.readEntry("hide_duplicates") == "true") ? true : false;
}
void KHLUurrooster::configChanged()
{
//stop the timers
if(timerUpdate->isActive()) timerUpdate->stop();
if(timerRefresh->isActive()) timerRefresh->stop();
if(timerError->isActive()) timerError->stop();
//get the config
readConfig();
//check if filled in
if(ical_1.isEmpty() && ical_2.isEmpty() && ical_3.isEmpty()) { //no configuration setted yet
this->setConfigurationRequired(true, "Je moet op zijn minst 1 iCal link opgeven");
} else {
this->setConfigurationRequired(false, "Je moet op zijn minst 1 iCal link opgeven");
//update het uurrooster, hier wordt het ical terug opgehaald.
ical_busy = 1;
rooster->removeEntries();
rooster->update(ical_1, ical_1_color, hide_duplicates);
//start the timer with a given timeout, default it's once a hour. It checks if the uurrooster is updated online or not.
timerUpdate->start(60 * 60 * 1000);
}
}
void KHLUurrooster::configAccepted()
{
ical_1 = ui.txtIcal_1->text();
ical_2 = ui.txtIcal_2->text();
ical_3 = ui.txtIcal_3->text();
ical_1_color = ui.comboKleur1->currentText();
ical_2_color = ui.comboKleur2->currentText();
ical_3_color = ui.comboKleur3->currentText();
personal_color = ui.comboKleurA->currentText();
hide_duplicates = ui.checkDuplicates->isChecked();
KConfigGroup cg = config();
cg.writeEntry("ical_1", ical_1);
cg.writeEntry("ical_2", ical_2);
cg.writeEntry("ical_3", ical_3);
cg.writeEntry("ical_1_color", ical_1_color);
cg.writeEntry("ical_2_color", ical_2_color);
cg.writeEntry("ical_3_color", ical_3_color);
cg.writeEntry("personal_color", personal_color);
cg.writeEntry("hide_duplicates", (hide_duplicates) ? "true" : "false");
emit configNeedsSaving();
}
void KHLUurrooster::paintInterface(QPainter *p, const QStyleOptionGraphicsItem *option, const QRect &contentsRect)
{
Q_UNUSED(option);
p->setRenderHint(QPainter::SmoothPixmapTransform);
p->setRenderHint(QPainter::Antialiasing);
layout->setGeometry(contentsRect);
}
void KHLUurrooster::displayEntries()
{
if(ical_1.isEmpty() && ical_2.isEmpty() && ical_3.isEmpty()) return;
//ff de refreshtimer stoppen
if(timerRefresh->isActive()) {
timerRefresh->stop();
}
//de uurrooster entries, mooi gesorteerd.
QList *entries= rooster->getEntries(date);
//delete body frame entries
QGraphicsLayoutItem *item;
while(m_scrollerLayout->count() > 0) {
item = m_scrollerLayout->itemAt(m_scrollerLayout->count() - 1);
m_scrollerLayout->removeItem(item);
delete item;
}
//set the label text
headerLabel->setText("" + date.toString(Qt::DefaultLocaleLongDate) + "");
//set the button labels
vorigeButton->setText(date.addDays(-1).toString());
volgendeButton->setText(date.addDays(1).toString());
//add entries to frame
if(entries->count() > 0) {
EntryWidget *widget;
Plasma::Separator *sep;
QTime currTime = QTime::currentTime();
QDate currDate = QDate::currentDate();
//loop the entries
for(int i = 0 ; i < entries->size() ; i++) {
//markeer de items die nu bezig zijn in het bold
if(entries->at(i).starttijd.date() == currDate && entries->at(i).starttijd.time() <= currTime && currTime <= entries->at(i).eindtijd.time()) {
widget = new EntryWidget(entries->at(i), true);
} else {
widget = new EntryWidget(entries->at(i), false);
}
m_scrollerLayout->addItem(widget);
//scheiding toevoegen tussen de verschillende entries
if(i != entries->size()-1) {
sep = new Plasma::Separator();
sep->setOrientation(Qt::Horizontal);
m_scrollerLayout->addItem(sep);
}
}
} else {
Plasma::Label *nothingLabel;
nothingLabel = new Plasma::Label();
nothingLabel->setText("Geen les vandaag! JOEPIE!");
nothingLabel->setAlignment(Qt::AlignCenter);
m_scroller->resize(300, 200);
m_scrollerLayout->addItem(nothingLabel);
}
//resume the refresh timer
timerRefresh->start(60 * 1000);
}
void KHLUurrooster::displayError(const QString& error)
{
//stop de timers
if(timerUpdate->isActive()) timerUpdate->stop();
if(timerRefresh->isActive()) timerRefresh->stop();
if(timerError->isActive()) timerError->stop();
//delete body frame entries
QGraphicsLayoutItem *item;
while(m_scrollerLayout->count() > 0) {
item = m_scrollerLayout->itemAt(m_scrollerLayout->count() - 1);
m_scrollerLayout->removeItem(item);
delete item;
}
//display the error on applet
Plasma::Label *errorLabel = new Plasma::Label();
errorLabel->setText("Fout:
" + error);
m_scroller->resize(300, 200);
m_scrollerLayout->addItem(errorLabel);
//start error timer met een interval van 10 seconden
timerError->start(10 * 1000);
}
void KHLUurrooster::updateRoosters()
{
if(timerError->isActive()) {
timerError->stop();
}
date = QDate::currentDate();
ical_busy = 1;
rooster->removeEntries();
rooster->update(ical_1, ical_1_color, hide_duplicates);
}
void KHLUurrooster::roosterError(const QString& error)
{
displayError(error);
}
void KHLUurrooster::sendNextIcalLink()
{
ical_busy += 1;
if(ical_busy == 2) {
rooster->update(ical_2, ical_2_color, hide_duplicates);
} else if(ical_busy == 3) {
rooster->update(ical_3, ical_3_color, hide_duplicates);
} else {
rooster->updatePersonal(personal_color);
displayEntries();
}
}
void KHLUurrooster::nextDate()
{
if(! timerError->isActive()) {
date = date.addDays(1);
displayEntries();
}
}
void KHLUurrooster::prevDate()
{
if(! timerError->isActive()) {
date = date.addDays(-1);
displayEntries();
}
}
void KHLUurrooster::savePersonalEntry()
{
QDomDocument *doc = new QDomDocument("personal");
if(file != NULL) delete file;
file = new QFile(QDir::homePath() + "/.khl_personal.xml");
file->open(QIODevice::ReadWrite);
doc->setContent(file);
//find root
QDomElement root = doc->documentElement();
//maak een element
QDomElement entry = doc->createElement("entry");
QDomElement beschrijving = doc->createElement("beschrijving");
QDomElement locatie = doc->createElement("locatie");
QDomElement starttijd = doc->createElement("starttijd");
QDomElement eindtijd = doc->createElement("eindtijd");
//maak de content elementen
QDomText txtBeschrijving = doc->createTextNode(ui.txtOmschrijving->text());
QDomText txtLocatie = doc->createTextNode(ui.txtLocatie->text());
QDomText txtStarttijd = doc->createTextNode(ui.dtStart->text());
QDomText txtEindtijd = doc->createTextNode(ui.dtEind->text());
//link elements met inhoud
beschrijving.appendChild(txtBeschrijving);
locatie.appendChild(txtLocatie);
starttijd.appendChild(txtStarttijd);
eindtijd.appendChild(txtEindtijd);
//link elementen met de entry elementen
entry.appendChild(beschrijving);
entry.appendChild(locatie);
entry.appendChild(starttijd);
entry.appendChild(eindtijd);
//append it to the xml
root.appendChild(entry);
//write it to the file
file->resize(0);
file->write(doc->toByteArray());
file->close();
//show message
ui.comboDelete->addItem(ui.txtOmschrijving->text());
ui.txtLocatie->setText("");
ui.txtOmschrijving->setText("");
ui.dtStart->setDateTime(QDateTime::currentDateTime());
ui.dtEind->setDateTime(QDateTime::currentDateTime().addSecs(3600));
KMessageBox::information(NULL, "Afspraak toegevoegd!");
}
void KHLUurrooster::deletePersonalEntry()
{
QString beschrijving = ui.comboDelete->currentText();
QDomDocument *doc = new QDomDocument("personal");
QFile *file = new QFile(QDir::homePath() + "/.khl_personal.xml");
file->open(QIODevice::ReadWrite);
doc->setContent(file);
//find root
QDomElement root = doc->documentElement();
//iterate the personal elements
QDomNodeList nodeList = root.elementsByTagName("entry");
QDomNode el;
for(int i = 0 ; i < nodeList.size() ; i++) {
el = nodeList.at(i);
if(el.childNodes().at(0).childNodes().at(0).nodeValue() == beschrijving) {
root.removeChild(el);
}
}
file->resize(0);
file->write(doc->toByteArray());
file->close();
ui.comboDelete->removeItem(ui.comboDelete->currentIndex());
KMessageBox::information(NULL, "Afspraak verwijderd!");
updateRoosters();
}
#include "khluurrooster.moc"