summaryrefslogtreecommitdiffstats
path: root/kteatime/toplevel.cpp
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitae2a03c2941bf92573f89b88ef73f8aa842bea0a (patch)
tree3566563f3fb6ac3cb3496669d8f233062d3091bc /kteatime/toplevel.cpp
downloadtdetoys-ae2a03c2941bf92573f89b88ef73f8aa842bea0a.tar.gz
tdetoys-ae2a03c2941bf92573f89b88ef73f8aa842bea0a.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdetoys@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kteatime/toplevel.cpp')
-rw-r--r--kteatime/toplevel.cpp856
1 files changed, 856 insertions, 0 deletions
diff --git a/kteatime/toplevel.cpp b/kteatime/toplevel.cpp
new file mode 100644
index 0000000..1cf0c96
--- /dev/null
+++ b/kteatime/toplevel.cpp
@@ -0,0 +1,856 @@
+/*
+ * This file is part of the KTeaTime application.
+ *
+ * Copyright (C) 1998-1999 Matthias Hoelzer-Kluepfel (hoelzer@kde.org)
+ * Copyright (C) 2002-2003 Martin Willers (willers@xm-arts.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include <qcheckbox.h>
+#include <qlayout.h>
+#include <qhbox.h>
+#include <qvbox.h>
+#include <qlineedit.h>
+#include <qpainter.h>
+#include <qtooltip.h>
+#include <qfile.h>
+#include <qcursor.h>
+#include <qpushbutton.h>
+#include <qgroupbox.h>
+#include <qheader.h>
+#include <qpixmap.h>
+#include <qbitmap.h>
+
+#include <kconfig.h>
+#include <khelpmenu.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kpassivepopup.h>
+#include <knotifyclient.h>
+#include <knuminput.h>
+#include <kseparator.h>
+#include <kpopupmenu.h>
+#include <kdialogbase.h>
+#include <kaction.h>
+#include <knotifydialog.h>
+
+#include "tealist.h"
+#include "timeedit.h"
+#include "toplevel.h"
+#include "toplevel.moc"
+
+
+const int TopLevel::DEFAULT_TEA_TIME = 3*60;
+
+
+TopLevel::TopLevel() : KSystemTray()
+{
+ setBackgroundMode(X11ParentRelative); // what for?
+ QString n, key;
+ unsigned int num;
+
+ teas.clear();
+
+ KConfig *config = kapp->config();
+ config->setGroup("Teas");
+
+ if (config->hasKey("Number")) {
+ // assuming this is a new-style config
+ num = config->readNumEntry("Number", 0);
+ teas.resize(num);
+ QString tempstr;
+ for (unsigned int index=1; index<=num; ++index) {
+ key.sprintf("Tea%d Time", index);
+ tempstr = config->readEntry(key, NULL);
+ teas[index-1].time = tempstr.toInt();
+ key.sprintf("Tea%d Name", index);
+ teas[index-1].name = config->readEntry(key, NULL);
+ // FIXME: check for non-existence!
+ }
+ config->setGroup("General");
+ } else {
+ // either old-style config or first start, so provide some sensible defaults
+ // (which are the same as in old-style kteatime)
+ tea_struct temp;
+ temp.name = i18n("Black Tea");
+ temp.time = 180;
+ teas.append(temp);
+ temp.name = i18n("Earl Grey");
+ temp.time = 300;
+ teas.append(temp);
+ temp.name = i18n("Fruit Tea");
+ temp.time = 480;
+ teas.append(temp);
+
+ // switch back to old-style default group
+ config->setGroup(NULL);
+ // look for old-style "UserTea"-entry and add that one also
+ if (config->hasKey("UserTea")) {
+ num = config->readNumEntry("UserTea", 150);
+ temp.name = i18n("Other Tea");
+ temp.time = num;
+ teas.append(temp);
+ }
+ }
+ current_selected = config->readNumEntry("Tea", 0);
+ if (current_selected >= teas.count())
+ current_selected = 0;
+
+ listempty = (teas.count() == 0);
+
+
+ startAct = new KAction(i18n("&Start"), "1rightarrow", 0,
+ this, SLOT(start()), actionCollection(), "start");
+ stopAct = new KAction(i18n("Sto&p"), "cancel", 0,
+ this, SLOT(stop()), actionCollection(), "stop");
+ confAct = new KAction(i18n("&Configure..."), "configure", 0,
+ this, SLOT(config()), actionCollection(), "configure");
+ anonAct = new KAction(i18n("&Anonymous..."), 0, 0,
+ this, SLOT(anonymous()), actionCollection(), "anonymous");
+// KAction *quitAct = actionCollection()->action("file_quit");
+
+ // create app menu (displayed on right-click)
+ menu = new QPopupMenu();
+ menu->setCheckable(true);
+ connect(menu, SIGNAL(activated(int)), this, SLOT(teaSelected(int)));
+
+ // this menu will be displayed when no tea is steeping, and left mouse button is clicked
+ start_menu = new QPopupMenu();
+ start_menu->setCheckable(true); // menu isn't tickable, but this gives some add. spacing
+ connect(start_menu, SIGNAL(activated(int)), this, SLOT(teaStartSelected(int)));
+
+ rebuildTeaMenus(); // populate tops of menus with tea-entries from config
+
+ KHelpMenu* help = new KHelpMenu(this, KGlobal::instance()->aboutData(), false);
+ KPopupMenu* helpMnu = help->menu();
+
+ start_menu->insertSeparator();
+ anonAct->plug(start_menu);
+
+ menu->insertSeparator();
+ anonAct->plug(menu);
+ startAct->plug(menu);
+ stopAct->plug(menu);
+ menu->insertSeparator();
+ confAct->plug(menu);
+ menu->insertItem(SmallIcon("help"), i18n("&Help"), helpMnu);
+ menu->insertItem(SmallIcon("exit"), i18n("Quit"), kapp, SLOT(quit()));
+// quitAct->plug(menu); // FIXME: this doesn't seem to work with above definition of quitAct?
+ // (need special 'quit'-method?)
+
+ // this menu will be displayed when a tea is steeping, and left mouse button is clicked
+ steeping_menu = new QPopupMenu();
+// steeping_menu->insertItem(SmallIcon("cancel"), i18n("Just &Cancel Current"), this, SLOT(stop()));
+ stopAct->plug(steeping_menu); // FIXME: can provide different text for this incarnation?
+
+// start_menu->insertSeparator();
+// startAct->plug(start_menu); // FIXME: include "start" entry here for quick access to current tea?
+
+ // read remaining entries from config-file
+ useNotify = config->readBoolEntry("Beep", true); // "Beep" should really be named "Notify"
+ usePopup = config->readBoolEntry("Popup", true);
+ useAction = config->readBoolEntry("UseAction", true);
+ action = config->readEntry("Action");
+ useTrayVis = config->readBoolEntry("UseTrayVis", true);
+
+ mugPixmap = loadIcon("mug");
+ teaNotReadyPixmap = loadIcon("tea_not_ready");
+ teaAnim1Pixmap = loadIcon("tea_anim1");
+ teaAnim2Pixmap = loadIcon("tea_anim2");
+
+ confdlg = 0L;
+ anondlg = 0L;
+
+ stop(); // reset timer, disable some menu entries, etc.
+}
+
+/* slot: signal shutDown() from KApplication */
+/* (not currently needed)
+void TopLevel::queryExit()
+{
+ KConfig *config = kapp->config();
+// config->sync();
+}
+*/
+
+
+/** Destructor */
+TopLevel::~TopLevel()
+{
+ delete menu;
+ delete steeping_menu;
+ delete start_menu;
+ // FIXME: must delete more (like all the QWidgets in config-window)?
+}
+
+
+/** Handle mousePressEvent */
+void TopLevel::mousePressEvent(QMouseEvent *event)
+{
+ if (event->button() == LeftButton) {
+ if (ready) {
+ stop(); // reset tooltip and stop animation
+ } else {
+ if (running)
+ steeping_menu->popup(QCursor::pos());
+ else
+ start_menu->popup(QCursor::pos());
+ }
+ } else if (event->button() == RightButton)
+ menu->popup(QCursor::pos());
+// else if (event->button() == MidButton) // currently unused
+}
+
+/** Handle paintEvent (ie. animate icon) */
+void TopLevel::paintEvent(QPaintEvent *)
+{
+ QPixmap *pm = &mugPixmap;
+
+ if (running) {
+ if (useTrayVis)
+ pm = &teaAnim1Pixmap; // this is 'mugPixmap' plus brown content
+ else
+ pm = &teaNotReadyPixmap; // generic "steeping" icon
+ } else {
+ // use simple two-frame "animation"
+ // FIXME: how about using a QMovie instead? (eg. MNG)
+ if (ready) {
+ if (firstFrame)
+ pm = &teaAnim1Pixmap;
+ else
+ pm = &teaAnim2Pixmap;
+ }
+ }
+
+ // overlay pie chart onto tray icon
+ QPixmap base(*pm); // make copy of base pixmap
+ if (useTrayVis && running) {
+ // extend mask
+ QBitmap mask = *(base.mask());
+ QPainter pm(&mask);
+ pm.setBrush(Qt::color1); // fill with "foreground-colour"
+ pm.setPen(Qt::NoPen); // no border needed/wanted
+ pm.drawPie(0+1, 9+1, 11, 11, 90*16, -360*16); // full circle of small size
+ pm.drawPie(0, 9, 13, 13, 90*16, percentDone*16); // pie part of big size
+ pm.end();
+ base.setMask(mask);
+
+ // draw pie chart
+ QPainter px(&base);
+ px.setPen(QPen(Qt::black, 0)); // black border
+ px.setBrush(QColor(192, 0, 0)); // red fill colour for small circle
+ px.drawPie(0+1, 9+1, 11, 11, 90*16, -360*16);
+
+ px.setBrush(QColor(0, 192, 0)); // green fill colour for pie part
+ px.drawPie(0, 9, 13, 13, 90*16, percentDone*16);
+ px.end();
+ }
+ // FIXME: over-emphasize first and last few percent? (for better visibility)
+ // FIXME: some optimizations (eg. store pre-drawn QPixmap with small circle)
+ // (and use drawEllipse() instead of drawPie() for small circle!)
+
+ // set new tray icon
+ QPainter p(this);
+ int x = 1 + (12 - pm->width()/2);
+ int y = 1 + (12 - pm->height()/2);
+ p.drawPixmap(x, y, base);
+ p.end();
+}
+
+/** Check timer and initiate appropriate action if finished */
+void TopLevel::timerEvent(QTimerEvent *)
+{
+ if (running) {
+ // a tea is steeping; must count down
+ seconds--;
+
+ if (seconds <= 0) {
+ // timer has run out; notify user
+ running = false;
+ ready = true;
+ enable_menuEntries();
+ if (shooting) {
+ // re-check current tea
+ shooting = false;
+ if (!listempty)
+ menu->setItemChecked(current_selected, true);
+ }
+
+ QString teaMessage = i18n("The %1 is now ready!").arg(current_name);
+ // invoke action
+ if (useNotify) {
+ KNotifyClient::event(winId(), "tea", teaMessage);
+ }
+ if (useAction && (!action.isEmpty())) {
+ QString cmd = action;
+ cmd.replace("%t", current_name);
+ system(QFile::encodeName(cmd));
+ }
+ if (usePopup)
+ KPassivePopup::message(i18n("The Tea Cooker"),
+ teaMessage, teaAnim1Pixmap, this, "popup", 0);
+ // FIXME: does auto-deletion work without timeout?
+ setToolTip(teaMessage);
+ repaint();
+ } else {
+ // timer not yet run out; just update tray-icon (if configured)...
+ if (useTrayVis) {
+ int pDone = (360 * (startSeconds - seconds)) / startSeconds;
+ if (pDone - percentDone > 8) {
+ // update icon not every second, but only if somewhat noticable
+ percentDone = pDone;
+ repaint();
+ }
+ }
+ // ...and Tooltip
+ QString min = int2time(seconds);
+ setToolTip(i18n("%1 left for %2").arg(min).arg(current_name));
+ }
+ } else {
+ // no tea is steeping; just animate icon
+ if (ready) {
+ firstFrame = !firstFrame;
+ repaint();
+ }
+ }
+}
+
+/** update ToolTip */
+void TopLevel::setToolTip(const QString &text, bool force)
+{
+ // don't update if text hasn't changed
+ if (lastTip == text)
+ return;
+
+ // don't remove Tooltip if (probably - can't know for sure?) currently showing
+ // FIXME: this isn't too nice: currently mouse must stay outside for >1s for update to occur
+ if (force || !this->hasMouse() || (lastTip == i18n("The Tea Cooker"))) {
+ lastTip = text;
+ QToolTip::remove(this);
+ QToolTip::add(this, text);
+ }
+}
+
+
+/** add all configured teas to both menus */
+void TopLevel::rebuildTeaMenus() {
+ // first remove all current tea-entries from menus; these can be identified by their positive id
+ while (menu->idAt(0) >= 0)
+ menu->removeItemAt(0); // remove from right-click menu
+ while (start_menu->idAt(0) >= 0)
+ start_menu->removeItemAt(0); // remove from left-click menu
+
+ // now add new tea-entries to top of menus
+ int id = 0;
+ int index = 0;
+ for (QValueVector<tea_struct>::ConstIterator it=teas.begin(); it != teas.end(); ++it) {
+ // construct string with name and steeping time
+ QString str = it->name;
+ str.append(" (");
+ str.append(int2time(it->time));
+ str.append(")");
+
+ start_menu->insertItem(str, id, index); // add to left-click menu
+ menu->insertItem(str, id++, index++); // add to right-click menu
+ }
+
+ // now select 'current' tea
+ if (!listempty)
+ menu->setItemChecked(current_selected, true); // all others aren't checked,
+ // because we just added them
+}
+
+/* enable/disable menu-entries according to current running-state */
+void TopLevel::enable_menuEntries()
+{
+ for (int index=0; menu->idAt(index) >= 0; ++index) {
+ // [en|dis]able all tea-entries (all have positive menu-ids)
+ menu->setItemEnabled(menu->idAt(index), !running);
+ }
+
+ startAct->setEnabled(!running); // "start" entry
+ stopAct->setEnabled(running); // "stop" entry
+ confAct->setEnabled(!running); // "configuration" entry
+ anonAct->setEnabled(!running); // "anonymous" entry
+}
+
+/* menu-slot: tea selected in tea-menu */
+void TopLevel::teaSelected(int index)
+{
+ if (index >=0 && (unsigned int)index < teas.count()) {
+ // tick new active item in menu
+ menu->setItemChecked(current_selected, false);
+ menu->setItemChecked(index, true);
+
+ current_selected = index;
+ KConfig *config = kapp->config();
+ config->setGroup("General");
+ config->writeEntry("Tea", current_selected);
+ }
+ // all other entries of this menu have custom handlers
+}
+
+/* start_menu-slot: tea selected (and activated!) in tea-menu */
+void TopLevel::teaStartSelected(int index)
+{
+ if (index >=0 && (unsigned int)index < teas.count()) {
+ teaSelected(index);
+
+ start();
+ }
+}
+
+/* menu-slot: "start" selected in menu */
+void TopLevel::start()
+{
+ if (listempty && !shooting) {
+ KMessageBox::error(this, i18n("There is no tea to begin steeping."), i18n("No Tea"));
+ } else {
+ if (!shooting) {
+ current_name = teas[current_selected].name; // remember name of current tea
+ startSeconds = teas[current_selected].time; // initialize time for current tea
+ seconds = startSeconds;
+ percentDone = 0;
+ }
+ // else both are already defined by dialog handler
+
+ killTimers();
+ startTimer(1000); // 1000ms = 1s (sufficient resolution)
+
+ running = true;
+ ready = false;
+ enable_menuEntries(); // disable "start", enable "stop"
+
+ repaint();
+ }
+}
+
+/* menu-slot: "stop" selected in menu */
+void TopLevel::stop()
+{
+ killTimers();
+
+ running = false;
+ ready = false;
+ enable_menuEntries(); // disable "top", enable "start"
+ if (shooting) {
+ // re-check current tea
+ shooting = false;
+ if (!listempty)
+ menu->setItemChecked(current_selected, true);
+ }
+
+ setToolTip(i18n("The Tea Cooker"), true);
+ repaint();
+}
+
+/* open dialog to start an 'anonymous' tea */
+void TopLevel::anonymous()
+{
+ if (!anondlg) {
+ // FIXME: dialog appears centered on screen, but should be near systray icon!
+ anondlg = new KDialogBase(KDialogBase::Plain, i18n("Anonymous Tea"),
+ KDialogBase::Ok | KDialogBase::Cancel,
+ KDialogBase::Ok, this, "anonymous", true);
+ QWidget *page = anondlg->plainPage();
+ QBoxLayout *top_box = new QVBoxLayout(page);
+ QBoxLayout *prop_box = new QHBoxLayout(top_box);
+ QVBox *propleft = new QVBox(page);
+ prop_box->addWidget(propleft);
+ QVBox *propright = new QVBox(page);
+ prop_box->addWidget(propright);
+
+ anon_time = new TimeEdit(propright);
+ anon_time->setFixedHeight(anon_time->sizeHint().height());
+ anon_time->setValue(DEFAULT_TEA_TIME);
+ QLabel *l = new QLabel(anon_time, i18n("Tea time:"), propleft);
+ l->setFixedSize(l->sizeHint());
+
+ top_box->addStretch();
+
+ anon_time->setFocus();
+ } else {
+ // FIXME: do what here?
+ // reset time to DEFAULT_TEA_TIME?
+ // (why? - better use LRU, and save that to config)
+ }
+
+ if (anondlg->exec() == QDialog::Accepted) {
+ shooting = true;
+ if (!listempty)
+ menu->setItemChecked(current_selected, false); // no item is to be checked
+ current_name = i18n("tea"); // some generic tea name
+ startSeconds = anon_time->value();
+ seconds = startSeconds;
+ percentDone = 0;
+ start();
+ }
+}
+
+
+
+
+//
+// Configure-window handling
+//
+
+
+/* enable/disable buttons for editing listbox */
+void TopLevel::enable_controls() {
+ bool haveSelection = (listbox->currentItem() != 0);
+ bool amFirst = (listbox->currentItem() == listbox->firstChild());
+ bool amLast = true;
+ if (haveSelection)
+ amLast = (!listbox->currentItem()->itemBelow()); // itemBelow() returns returns NULL if last
+
+ btn_del->setEnabled(haveSelection);
+ btn_up->setEnabled(haveSelection && !amFirst);
+ btn_down->setEnabled(haveSelection && !amLast);
+ if (haveSelection)
+ listbox->ensureItemVisible(listbox->currentItem());
+}
+
+/* disable right side of configure-window */
+void TopLevel::disable_properties() {
+ editgroup->setEnabled(false);
+}
+
+/* enable right side of configure-window */
+void TopLevel::enable_properties() {
+ editgroup->setEnabled(true);
+}
+
+/* config-slot: item in tea-list selected */
+void TopLevel::listBoxItemSelected() {
+ if (listbox->currentItem()) {
+ // item selected, display its properties on right side
+ nameEdit->setText(static_cast<TeaListItem *>(listbox->currentItem())->name());
+ timeEdit->setValue(static_cast<TeaListItem *>(listbox->currentItem())->time());
+ enable_controls();
+ }
+}
+
+/* config-slot: name of a tea edited */
+void TopLevel::nameEditTextChanged(const QString& newText) {
+ /* this method also gets called when the last TeaListItem has been deleted
+ * (to clear the name edit widget), so check for empty listbox */
+ if (listbox->currentItem() != NULL) {
+ listbox->blockSignals(TRUE);
+ static_cast<TeaListItem *>(listbox->currentItem())->setName(newText);
+ listbox->blockSignals(FALSE);
+ }
+}
+
+/* config-slot: time for a tea changed */
+void TopLevel::spinBoxValueChanged(int v) {
+ /* this method also gets called when the last TeaListItem has been deleted
+ * (to clear the time edit widget), so check for empty listbox */
+ if (listbox->currentItem() != NULL)
+ static_cast<TeaListItem *>(listbox->currentItem())->setTime(v);
+}
+
+/* config-slot: "new" button clicked */
+void TopLevel::newButtonClicked() {
+ TeaListItem* item = new TeaListItem(listbox, listbox->currentItem());
+ listbox->setCurrentItem(item);
+
+ nameEdit->setText(i18n("New Tea"));
+ timeEdit->setValue(DEFAULT_TEA_TIME);
+
+ nameEdit->setFocus();
+
+ if (listbox->childCount() == 1) {
+ enable_properties();
+ current_item = item;
+ }
+ enable_controls();
+}
+
+/* config-slot: "delete" button clicked */
+void TopLevel::delButtonClicked() {
+ if (listbox->currentItem()) {
+ TeaListItem *curritem = static_cast<TeaListItem *>(listbox->currentItem());
+
+ if (listbox->childCount() == 1) {
+ // no childs left after we've deleted this item
+ listbox->setSelected(listbox->currentItem(), false);
+ nameEdit->setText("");
+ timeEdit->setValue(0);
+ disable_properties();
+ } else {
+ // select new current item
+ if (listbox->firstChild() != curritem)
+ listbox->setSelected(listbox->firstChild(), true);
+ else
+ listbox->setSelected(listbox->firstChild()->nextSibling(), true);
+ }
+
+ delete curritem;
+ enable_controls();
+ }
+}
+
+/* config-slot: "up" button clicked */
+void TopLevel::upButtonClicked() {
+ QListViewItem* item = listbox->currentItem();
+
+ if (item && item->itemAbove())
+ item->itemAbove()->moveItem(item);
+
+ enable_controls();
+}
+
+/* config-slot: "down" button clicked */
+void TopLevel::downButtonClicked() {
+ QListViewItem* item = listbox->currentItem();
+
+ if (item && item->itemBelow())
+ item->moveItem(item->itemBelow());
+
+ enable_controls();
+}
+
+/* config-slot: checkbox next to "action" field toggled*/
+void TopLevel::actionEnableToggled(bool on)
+{
+ actionEdit->setEnabled(on);
+}
+
+/* config-slot: "help" button clicked */
+void TopLevel::help()
+{
+ kapp->invokeHelp();
+}
+
+/* config-slot: "Configure Events..." button clicked */
+void TopLevel::confButtonClicked()
+{
+ KNotifyDialog::configure(btn_conf);
+}
+
+
+void TopLevel::config()
+{
+ if (!confdlg) {
+ confdlg = new KDialogBase(KDialogBase::Plain, i18n("Configure Tea Cooker"),
+ KDialogBase::Ok|KDialogBase::Cancel|KDialogBase::Help,
+ KDialogBase::Ok, this, "config", true);
+ QWidget *page = confdlg->plainPage();
+ // FIXME: enforce sensible initial/default size of dialog
+ // FIXME: modal is ok, but can avoid always-on-top?
+
+ QBoxLayout *top_box = new QVBoxLayout(page, 0, 8); // whole config-stuff
+ QBoxLayout *box = new QHBoxLayout(top_box); // list + properties
+
+ /* left side - tea list and list-modifying buttons */
+ QBoxLayout *leftside = new QVBoxLayout(box);
+ QGroupBox *listgroup = new QGroupBox(2, Vertical, i18n("Tea List"), page);
+ leftside->addWidget(listgroup, 0, 0);
+
+ listbox = new QListView(listgroup, "listBox");
+ listbox->addColumn(i18n("Name"));
+ listbox->header()->setClickEnabled(false, listbox->header()->count()-1);
+ listbox->addColumn(i18n("Time"));
+ listbox->header()->setClickEnabled(false, listbox->header()->count()-1);
+ listbox->setSorting(-1);
+ connect(listbox, SIGNAL(selectionChanged()), SLOT(listBoxItemSelected()));
+
+ // now buttons for editing the tea-list
+ QWidget *listgroup_widget = new QWidget(listgroup);
+ QBoxLayout *hbox = new QHBoxLayout(listgroup_widget);
+ hbox->setSpacing(4);
+ btn_new = new QPushButton(QString::null, listgroup_widget);
+ QToolTip::add(btn_new, i18n("New"));
+ btn_new->setPixmap(SmallIcon("filenew"));
+ btn_new->setMinimumSize(btn_new->sizeHint() * 1.2);
+ connect(btn_new, SIGNAL(clicked()), SLOT(newButtonClicked()));
+ hbox->addWidget(btn_new);
+
+ btn_del = new QPushButton(QString::null, listgroup_widget);
+ QToolTip::add(btn_del, i18n("Delete"));
+ btn_del->setIconSet(SmallIconSet("editdelete"));
+ btn_del->setMinimumSize(btn_new->sizeHint() * 1.2);
+ connect(btn_del, SIGNAL(clicked()), SLOT(delButtonClicked()));
+ hbox->addWidget(btn_del);
+
+ btn_up = new QPushButton(QString::null, listgroup_widget);
+ QToolTip::add(btn_up, i18n("Up"));
+ btn_up->setIconSet(SmallIconSet("up"));
+ btn_up->setMinimumSize(btn_up->sizeHint() * 1.2);
+ connect(btn_up, SIGNAL(clicked()), SLOT(upButtonClicked()));
+ hbox->addWidget(btn_up);
+
+ btn_down = new QPushButton(QString::null, listgroup_widget);
+ QToolTip::add(btn_down, i18n("Down"));
+ btn_down->setIconSet(SmallIconSet("down"));
+ btn_down->setMinimumSize(btn_down->sizeHint() * 1.2);
+ connect(btn_down, SIGNAL(clicked()), SLOT(downButtonClicked()));
+ hbox->addWidget(btn_down);
+
+ hbox->addStretch(10);
+
+ /* right side - tea properties */
+ QBoxLayout *rightside = new QVBoxLayout(box);
+ editgroup = new QGroupBox(2, Vertical, i18n("Tea Properties"), page);
+ rightside->addWidget(editgroup, 0, 0);
+ QHBox *propbox = new QHBox(editgroup);
+
+ // FIXME: - must enforce correct vertical alignment of each label-editor pair
+ // (better use one HBox for each label-editor pair?)
+ QVBox *propleft = new QVBox(propbox);
+ QVBox *propright = new QVBox(propbox);
+ nameEdit = new QLineEdit(propright);
+ nameEdit->setFixedHeight(nameEdit->sizeHint().height());
+ nameEdit->setAlignment(QLineEdit::AlignLeft);
+ QLabel *l = new QLabel(nameEdit, i18n("Name:"), propleft);
+ l->setFixedSize(l->sizeHint());
+ connect(nameEdit, SIGNAL(textChanged(const QString&)), SLOT(nameEditTextChanged(const QString&)) );
+
+ timeEdit = new TimeEdit(propright);
+ timeEdit->setFixedHeight(timeEdit->sizeHint().height());
+ l = new QLabel(timeEdit, i18n("Tea time:"), propleft);
+ l->setFixedSize(l->sizeHint());
+ connect(timeEdit, SIGNAL(valueChanged(int)), SLOT(spinBoxValueChanged(int)));
+
+ /* bottom - timeout actions */
+ QGroupBox *actiongroup = new QGroupBox(4, Vertical, i18n("Action"), page);
+ top_box->addWidget(actiongroup, 0, 0);
+
+ QWidget *actionconf_widget = new QWidget(actiongroup);
+ QBoxLayout *actionconf_hbox = new QHBoxLayout(actionconf_widget);
+ btn_conf = new QPushButton(i18n("Configure Events..."), actionconf_widget);
+ actionconf_hbox->addWidget(btn_conf);
+ connect(btn_conf, SIGNAL(clicked()), SLOT(confButtonClicked()));
+ actionconf_hbox->addStretch(10);
+
+ eventEnable = new QCheckBox(i18n("Event"), actiongroup);
+ popupEnable = new QCheckBox(i18n("Popup"), actiongroup);
+ eventEnable->setFixedHeight(eventEnable->sizeHint().height());
+ popupEnable->setFixedHeight(popupEnable->sizeHint().height());
+
+ QHBox *actionbox = new QHBox(actiongroup);
+ actionEnable = new QCheckBox(actionbox);
+// FIXME: add text to this line:
+// QLabel *actionLabel = new QLabel(i18n("Execute: "), actiongroup);
+ actionEdit = new QLineEdit(actionbox);
+ actionEdit->setFixedHeight(actionEdit->sizeHint().height());
+ QToolTip::add(actionEdit, i18n("Enter command here; '%t' will be replaced with name of steeping tea"));
+ connect(actionEnable, SIGNAL(toggled(bool)), SLOT(actionEnableToggled(bool)));
+ rightside->addStretch();
+
+ // single checkbox
+ visEnable = new QCheckBox(i18n("Visualize progress in icon tray"), page);
+ top_box->addWidget(visEnable, 0, 0);
+
+
+ // let listbox claim all remaining vertical space
+ top_box->setStretchFactor(box, 10);
+
+ connect(confdlg, SIGNAL(helpClicked()), SLOT(help()));
+ }
+
+ // now add all defined teas (and their times) to the listview
+ // this is done backwards because QListViewItems are inserted at the top
+ listbox->clear();
+ for (int i=teas.count()-1; i>=0; i--) {
+ TeaListItem *item = new TeaListItem(listbox);
+ item->setName(teas[i].name);
+ item->setTime(teas[i].time);
+ if ((unsigned int)i == current_selected)
+ current_item = item;
+ }
+
+ // select first entry in listbox; if no entries present then disable right side
+ if (listempty) {
+ enable_controls();
+ disable_properties();
+ nameEdit->setText("");
+ timeEdit->setValue(1);
+ } else {
+ listbox->setSelected(listbox->firstChild(), true);
+ }
+
+ // -------------------------
+
+ eventEnable->setChecked(useNotify);
+ popupEnable->setChecked(usePopup);
+ actionEnable->setChecked(useAction);
+ actionEdit->setText(action);
+ actionEdit->setEnabled(useAction);
+ visEnable->setChecked(useTrayVis);
+
+ if (confdlg->exec() == QDialog::Accepted)
+ {
+ // activate new settings
+ useNotify = eventEnable->isChecked();
+ usePopup = popupEnable->isChecked();
+ useAction = actionEnable->isChecked();
+ action = actionEdit->text();
+ useTrayVis = visEnable->isChecked();
+
+ teas.clear();
+
+ // Copy over teas and times from the QListView
+ int i = 0;
+ teas.clear();
+ teas.resize(listbox->childCount());
+ for (QListViewItemIterator it(listbox); it.current() != 0; ++it) {
+ teas[i].name = static_cast<TeaListItem *>(it.current())->name();
+ teas[i].time = static_cast<TeaListItem *>(it.current())->time();
+ if (it.current() == current_item)
+ current_selected = i;
+ i++;
+ }
+
+ listempty = (teas.count() == 0);
+
+ rebuildTeaMenus();
+
+ // and store to config
+ KConfig *config = kapp->config();
+ // remove old-style entries from default-group (if present)
+ if (config->hasKey("UserTea"))
+ config->deleteEntry("UserTea");
+
+ config->setGroup("General");
+ config->writeEntry("Beep", useNotify);
+ config->writeEntry("Popup", usePopup);
+ config->writeEntry("UseAction", useAction);
+ config->writeEntry("Action", action);
+ config->writeEntry("Tea", current_selected);
+ config->writeEntry("UseTrayVis", useTrayVis);
+ // first get rid of all previous tea-entries from config, then write anew
+ config->deleteGroup("Teas", true); // deep remove of whole group
+ config->setGroup("Teas");
+ config->writeEntry("Number", teas.count());
+ QString key;
+ int index = 1;
+ for (QValueVector<tea_struct>::ConstIterator it = teas.begin(); it != teas.end(); ++it) {
+ key.sprintf("Tea%d Name", index);
+ config->writeEntry(key, it->name);
+ key.sprintf("Tea%d Time", index);
+ config->writeEntry(key, it->time);
+ index++;
+ }
+
+ config->sync();
+ }
+}