You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdetoys/kteatime/toplevel.cpp

857 lines
27 KiB

/*
* 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();
}
}