/*
* 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 = 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 QWidgets in config-window)?
}
void TopLevel : : resizeEvent ( QResizeEvent * )
{
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 ( 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 , ( ( 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
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 , ( ( float ) width ( ) / ( float ) 2.44444444444 ) + 1 , ( width ( ) / 2 ) , ( width ( ) / 2 ) , 90 * 16 , - 360 * 16 ) ;
px . setBrush ( QColor ( 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 QPixmap with small circle)
// (and use drawEllipse() instead of drawPie() for small circle!)
// set new tray icon
QPainter 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 ( 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 ( ) ;
}
}