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

865 lines
28 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 <tqcheckbox.h>
#include <tqlayout.h>
#include <tqhbox.h>
#include <tqvbox.h>
#include <tqlineedit.h>
#include <tqpainter.h>
#include <tqtooltip.h>
#include <tqfile.h>
#include <tqcursor.h>
#include <tqpushbutton.h>
#include <tqgroupbox.h>
#include <tqheader.h>
#include <tqpixmap.h>
#include <tqbitmap.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?
TQString 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);
TQString 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,
TQT_TQOBJECT(this), TQT_SLOT(start()), actionCollection(), "start");
stopAct = new KAction(i18n("Sto&p"), "cancel", 0,
TQT_TQOBJECT(this), TQT_SLOT(stop()), actionCollection(), "stop");
confAct = new KAction(i18n("&Configure..."), "configure", 0,
TQT_TQOBJECT(this), TQT_SLOT(config()), actionCollection(), "configure");
anonAct = new KAction(i18n("&Anonymous..."), 0, 0,
TQT_TQOBJECT(this), TQT_SLOT(anonymous()), actionCollection(), "anonymous");
// KAction *quitAct = actionCollection()->action("file_quit");
// create app menu (displayed on right-click)
menu = new TQPopupMenu();
menu->setCheckable(true);
connect(menu, TQT_SIGNAL(activated(int)), this, TQT_SLOT(teaSelected(int)));
// this menu will be displayed when no tea is steeping, and left mouse button is clicked
start_menu = new TQPopupMenu();
start_menu->setCheckable(true); // menu isn't tickable, but this gives some add. spacing
connect(start_menu, TQT_SIGNAL(activated(int)), this, TQT_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, TQT_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 TQPopupMenu();
// steeping_menu->insertItem(SmallIcon("cancel"), i18n("Just &Cancel Current"), this, TQT_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 = loadSizedIcon("mug", width());
teaNotReadyPixmap = loadSizedIcon("tea_not_ready", width());
teaAnim1Pixmap = loadSizedIcon("tea_anim1", width());
teaAnim2Pixmap = loadSizedIcon("tea_anim2", width());
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 TQWidgets in config-window)?
}
void TopLevel::resizeEvent ( TQResizeEvent * )
{
mugPixmap = loadSizedIcon("mug", width());
teaNotReadyPixmap = loadSizedIcon("tea_not_ready", width());
teaAnim1Pixmap = loadSizedIcon("tea_anim1", width());
teaAnim2Pixmap = loadSizedIcon("tea_anim2", width());
repaint();
}
/** Handle mousePressEvent */
void TopLevel::mousePressEvent(TQMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
if (ready) {
stop(); // reset tooltip and stop animation
} else {
if (running)
steeping_menu->popup(TQCursor::pos());
else
start_menu->popup(TQCursor::pos());
}
} else if (event->button() == Qt::RightButton)
menu->popup(TQCursor::pos());
// else if (event->button() == MidButton) // currently unused
}
/** Handle paintEvent (ie. animate icon) */
void TopLevel::paintEvent(TQPaintEvent *)
{
TQPixmap *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 TQMovie instead? (eg. MNG)
if (ready) {
if (firstFrame)
pm = &teaAnim1Pixmap;
else
pm = &teaAnim2Pixmap;
}
}
// overlay pie chart onto tray icon
TQPixmap base(*pm); // make copy of base pixmap
if (useTrayVis && running) {
// extend mask
TQBitmap mask = *(base.mask());
TQPainter pm(&mask);
pm.setBrush(TQt::color1); // fill with "foreground-colour"
pm.setPen(TQt::NoPen); // no border needed/wanted
pm.drawPie(0+1, ((float) width()/(float) 2.44444444444)+1, (width()/2), (width()/2), 90*16, -360*16); // full circle of small size
pm.drawPie(0, ((float) width()/(float) 2.44444444444), ((float) width()/(float) 1.69230769231), ((float) width()/(float) 1.69230769231), 90*16, percentDone*16); // pie part of big size
pm.end();
base.setMask(mask);
// draw pie chart
TQPainter px(&base);
px.setPen(TQPen(TQt::black, 0)); // black border
px.setBrush(TQColor(192, 0, 0)); // red fill colour for small circle
px.drawPie(0+1, ((float) width()/(float) 2.44444444444)+1, (width()/2), (width()/2), 90*16, -360*16);
px.setBrush(TQColor(0, 192, 0)); // green fill colour for pie part
px.drawPie(0, ((float) width()/(float) 2.44444444444), ((float) width()/(float) 1.69230769231), ((float) width()/(float) 1.69230769231), 90*16, percentDone*16);
px.end();
}
// FIXME: over-emphasize first and last few percent? (for better visibility)
// FIXME: some optimizations (eg. store pre-drawn TQPixmap with small circle)
// (and use drawEllipse() instead of drawPie() for small circle!)
// set new tray icon
TQPainter p(this);
int x = 1 + (((float) width()/(float) 1.83333333333) - pm->width()/2);
int y = 1 + (((float) width()/(float) 1.83333333333) - pm->height()/2);
p.drawPixmap(x, y, base);
p.end();
}
/** Check timer and initiate appropriate action if finished */
void TopLevel::timerEvent(TQTimerEvent *)
{
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);
}
TQString teaMessage = i18n("The %1 is now ready!").arg(current_name);
// invoke action
if (useNotify) {
KNotifyClient::event(winId(), "tea", teaMessage);
}
if (useAction && (!action.isEmpty())) {
TQString cmd = action;
cmd.replace("%t", current_name);
system(TQFile::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
TQString 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 TQString &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;
TQToolTip::remove(this);
TQToolTip::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 (TQValueVector<tea_struct>::ConstIterator it=teas.begin(); it != teas.end(); ++it) {
// construct string with name and steeping time
TQString 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
TQT_TQOBJECT(this)->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()
{
TQT_TQOBJECT(this)->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);
TQWidget *page = anondlg->plainPage();
TQBoxLayout *top_box = new TQVBoxLayout(page);
TQBoxLayout *prop_box = new TQHBoxLayout(top_box);
TQVBox *propleft = new TQVBox(page);
prop_box->addWidget(propleft);
TQVBox *propright = new TQVBox(page);
prop_box->addWidget(propright);
anon_time = new TimeEdit(propright);
anon_time->setFixedHeight(anon_time->sizeHint().height());
anon_time->setValue(DEFAULT_TEA_TIME);
TQLabel *l = new TQLabel(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() == TQDialog::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 TQString& 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() {
TQListViewItem* item = listbox->currentItem();
if (item && item->itemAbove())
item->itemAbove()->moveItem(item);
enable_controls();
}
/* config-slot: "down" button clicked */
void TopLevel::downButtonClicked() {
TQListViewItem* 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);
TQWidget *page = confdlg->plainPage();
// FIXME: enforce sensible initial/default size of dialog
// FIXME: modal is ok, but can avoid always-on-top?
TQBoxLayout *top_box = new TQVBoxLayout(page, 0, 8); // whole config-stuff
TQBoxLayout *box = new TQHBoxLayout(top_box); // list + properties
/* left side - tea list and list-modifying buttons */
TQBoxLayout *leftside = new TQVBoxLayout(box);
TQGroupBox *listgroup = new TQGroupBox(2,Qt::Vertical, i18n("Tea List"), page);
leftside->addWidget(listgroup, 0, 0);
listbox = new TQListView(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, TQT_SIGNAL(selectionChanged()), TQT_SLOT(listBoxItemSelected()));
// now buttons for editing the tea-list
TQWidget *listgroup_widget = new TQWidget(listgroup);
TQBoxLayout *hbox = new TQHBoxLayout(listgroup_widget);
hbox->setSpacing(4);
btn_new = new TQPushButton(TQString(), listgroup_widget);
TQToolTip::add(btn_new, i18n("New"));
btn_new->setPixmap(SmallIcon("filenew"));
btn_new->setMinimumSize(btn_new->sizeHint() * 1.2);
connect(btn_new, TQT_SIGNAL(clicked()), TQT_SLOT(newButtonClicked()));
hbox->addWidget(btn_new);
btn_del = new TQPushButton(TQString(), listgroup_widget);
TQToolTip::add(btn_del, i18n("Delete"));
btn_del->setIconSet(SmallIconSet("editdelete"));
btn_del->setMinimumSize(btn_new->sizeHint() * 1.2);
connect(btn_del, TQT_SIGNAL(clicked()), TQT_SLOT(delButtonClicked()));
hbox->addWidget(btn_del);
btn_up = new TQPushButton(TQString(), listgroup_widget);
TQToolTip::add(btn_up, i18n("Up"));
btn_up->setIconSet(SmallIconSet("up"));
btn_up->setMinimumSize(btn_up->sizeHint() * 1.2);
connect(btn_up, TQT_SIGNAL(clicked()), TQT_SLOT(upButtonClicked()));
hbox->addWidget(btn_up);
btn_down = new TQPushButton(TQString(), listgroup_widget);
TQToolTip::add(btn_down, i18n("Down"));
btn_down->setIconSet(SmallIconSet("down"));
btn_down->setMinimumSize(btn_down->sizeHint() * 1.2);
connect(btn_down, TQT_SIGNAL(clicked()), TQT_SLOT(downButtonClicked()));
hbox->addWidget(btn_down);
hbox->addStretch(10);
/* right side - tea properties */
TQBoxLayout *rightside = new TQVBoxLayout(box);
editgroup = new TQGroupBox(2,Qt::Vertical, i18n("Tea Properties"), page);
rightside->addWidget(editgroup, 0, 0);
TQHBox *propbox = new TQHBox(editgroup);
// FIXME: - must enforce correct vertical alignment of each label-editor pair
// (better use one HBox for each label-editor pair?)
TQVBox *propleft = new TQVBox(propbox);
TQVBox *propright = new TQVBox(propbox);
nameEdit = new TQLineEdit(propright);
nameEdit->setFixedHeight(nameEdit->sizeHint().height());
nameEdit->setAlignment(TQLineEdit::AlignLeft);
TQLabel *l = new TQLabel(nameEdit, i18n("Name:"), propleft);
l->setFixedSize(l->sizeHint());
connect(nameEdit, TQT_SIGNAL(textChanged(const TQString&)), TQT_SLOT(nameEditTextChanged(const TQString&)) );
timeEdit = new TimeEdit(propright);
timeEdit->setFixedHeight(timeEdit->sizeHint().height());
l = new TQLabel(timeEdit, i18n("Tea time:"), propleft);
l->setFixedSize(l->sizeHint());
connect(timeEdit, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(spinBoxValueChanged(int)));
/* bottom - timeout actions */
TQGroupBox *actiongroup = new TQGroupBox(4,Qt::Vertical, i18n("Action"), page);
top_box->addWidget(actiongroup, 0, 0);
TQWidget *actionconf_widget = new TQWidget(actiongroup);
TQBoxLayout *actionconf_hbox = new TQHBoxLayout(actionconf_widget);
btn_conf = new TQPushButton(i18n("Configure Events..."), actionconf_widget);
actionconf_hbox->addWidget(btn_conf);
connect(btn_conf, TQT_SIGNAL(clicked()), TQT_SLOT(confButtonClicked()));
actionconf_hbox->addStretch(10);
eventEnable = new TQCheckBox(i18n("Event"), actiongroup);
popupEnable = new TQCheckBox(i18n("Popup"), actiongroup);
eventEnable->setFixedHeight(eventEnable->sizeHint().height());
popupEnable->setFixedHeight(popupEnable->sizeHint().height());
TQHBox *actionbox = new TQHBox(actiongroup);
actionEnable = new TQCheckBox(actionbox);
// FIXME: add text to this line:
// TQLabel *actionLabel = new TQLabel(i18n("Execute: "), actiongroup);
actionEdit = new TQLineEdit(actionbox);
actionEdit->setFixedHeight(actionEdit->sizeHint().height());
TQToolTip::add(actionEdit, i18n("Enter command here; '%t' will be replaced with name of steeping tea"));
connect(actionEnable, TQT_SIGNAL(toggled(bool)), TQT_SLOT(actionEnableToggled(bool)));
rightside->addStretch();
// single checkbox
visEnable = new TQCheckBox(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, TQT_SIGNAL(helpClicked()), TQT_SLOT(help()));
}
// now add all defined teas (and their times) to the listview
// this is done backwards because TQListViewItems 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() == TQDialog::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 TQListView
int i = 0;
teas.clear();
teas.resize(listbox->childCount());
for (TQListViewItemIterator 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());
TQString key;
int index = 1;
for (TQValueVector<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();
}
}