diff options
Diffstat (limited to 'kteatime')
-rw-r--r-- | kteatime/ChangeLog | 105 | ||||
-rw-r--r-- | kteatime/Makefile.am | 27 | ||||
-rw-r--r-- | kteatime/TODO | 58 | ||||
-rw-r--r-- | kteatime/cr22-action-mug.png | bin | 0 -> 310 bytes | |||
-rw-r--r-- | kteatime/cr22-action-tea_anim1.png | bin | 0 -> 310 bytes | |||
-rw-r--r-- | kteatime/cr22-action-tea_anim2.png | bin | 0 -> 362 bytes | |||
-rw-r--r-- | kteatime/cr22-action-tea_not_ready.png | bin | 0 -> 441 bytes | |||
-rw-r--r-- | kteatime/eventsrc | 183 | ||||
-rw-r--r-- | kteatime/hi16-app-kteatime.png | bin | 0 -> 815 bytes | |||
-rw-r--r-- | kteatime/hi32-app-kteatime.png | bin | 0 -> 2287 bytes | |||
-rw-r--r-- | kteatime/hi48-app-kteatime.png | bin | 0 -> 4001 bytes | |||
-rw-r--r-- | kteatime/kteatime.desktop | 72 | ||||
-rw-r--r-- | kteatime/main.cpp | 60 | ||||
-rw-r--r-- | kteatime/tealist.cpp | 64 | ||||
-rw-r--r-- | kteatime/tealist.h | 37 | ||||
-rw-r--r-- | kteatime/timeedit.cpp | 146 | ||||
-rw-r--r-- | kteatime/timeedit.h | 72 | ||||
-rw-r--r-- | kteatime/toplevel.cpp | 856 | ||||
-rw-r--r-- | kteatime/toplevel.h | 124 |
19 files changed, 1804 insertions, 0 deletions
diff --git a/kteatime/ChangeLog b/kteatime/ChangeLog new file mode 100644 index 0000000..6baec9f --- /dev/null +++ b/kteatime/ChangeLog @@ -0,0 +1,105 @@ +2003-11-02 Martin Willers <willers@xm-arts.de> + - bugfix: opening the configuration dialog was leaking memory + - workaround for Qt bug(?): deleting teas didn't update QListView properly (fixes #63840) + (the above two fixes based on patches by Daniel Teske) + - bugfix: when no tea was defined, one couldn't start an anonymous tea either + - bugfix: on some occasions, the tooltip wasn't updated properly + +2003-08-23 Martin Willers <willers@xm-arts.de> + - added option to visualize steeping progress in tray icon + - some work towards unified source headers + - bumped version number + +2003-08-22 Martin Willers <willers@xm-arts.de> + - fix ToolTip disappearing prematurely + (patch by "Ivan") + +2003-04-17 Martin Willers <willers@xm-arts.de> + - fixed bug with propagation of valueChanged() signals from TimeEdit + - fixed bug: removing all teas from list was leading to crash! + - changed default tea time of "anonymous" dialog to 3 minutes + +2003-04-10 Martin Willers <willers@xm-arts.de> + - have now proper wrapping time-edit widget + - added facility to start steeping an 'anonymous' tea + - using new KDE 3.1.1 method for positioning KNotifyClient + (based on patches provided by Daniel Teske) + +2003-03-14 Martin Willers <willers@xm-arts.de> + - added "Configure Events..." button for quick access to kcontrol + - some internal tidying and cleanups + - using convenience class TeaListItem instead of QListViewItem + - small optimization: don't use lastitem() of listbox (which traverses whole list), + work with itemBelow() instead + - using custom TimeEdit-widget for entering times with minutes and seconds + (patches provided by Daniel Teske) + +2003-03-07 (external) + - Implement place holder for tea sort like requested in #54551 + +2003-02-23 Martin Willers <willers@xm-arts.de> + - "beep" option now uses KNotify::event() + (need own "steeping" sound now) + (patch provided by Daniel Teske <teske@bigfoot.com>) + +2003-02-22 Martin Willers <willers@xm-arts.de> + - don't display ticks in left-click menu + +2003-02-15 Martin Willers <willers@xm-arts.de> + - using KActions for start, stop, and configure entries + - display list of configured teas on left-click, + making one-click starting of a new tea possible + - some cleanups + +2003-02-07 Martin Willers <willers@xm-arts.de> + - fixed inserting of new items into listview + - simplified layout in config-window + - fixed sizing of labels in config-window + - using QValueVector instead of a QStringList for teas + (patches provided by Daniel Teske <teske@bigfoot.com>) + +2003-01-07 Martin Willers <willers@xm-arts.de> + - fixed destructor of class toplevel + - finally avoiding stretching of buttons in config-dialog + (patches provided by Daniel Teske <teske@bigfoot.com>) + +2003-01-02 Martin Willers <willers@xm-arts.de> + - configuration dialog now selects first tea available + - small optimization in TopLevel::teaSelected() + - when a tea is brewing, more menu entries are now disabled + (patches provided by Daniel Teske <teske@bigfoot.com>) + +2002-07-14 Martin Willers <willers@xm-arts.de> + - KPassivePopup now stays on-screen until acknowlegded by mouse-click + - now saving state of actionEnable to config + +2002-06-23 Matthias Hoelzer-Kluepfel <mhk@kde.org> + - added left-click menu while steeping + - gave focus to name edit when a new tea is added + - now unable to start steeping if no teas exist + - added a QCheckBox to enable/disable the action field + - made text in the name edit field right-justified + (patch provided by Jackson Dunstan <jdunstan@digipen.edu>) + +2002-06-20 Matthias Hoelzer-Kluepfel <mhk@kde.org> + - improved the display of times in the menu + (patch provided by -Jackson Dunstan <jdunstan@digipen.edu>) + +2002-04-20 Martin Willers <willers@xm-arts.de> + - also displaying name of current tea when finished + +2002-04-20 Martin Willers <willers@xm-arts.de> + - now displaying name of current tea in tooltip + - better removing of unused tea-entries from config + - some cleanups + +2002-04-18 Martin Willers <willers@xm-arts.de> + - when saving config, non-used old tea-entries are now deleted from config + +2002-04-10 Martin Willers <willers@xm-arts.de> + - Bugfix: when tea was finished, click on icon -> icon ok, but + tooltip was still "tea finished" + - added "Cancel"-button to menu, and can also cancel timer by clicking + on icon while running + - showing remaining time not as "x.y", but as "xx:yy" + - can configure arbitrary amount of teas, with custom name and time diff --git a/kteatime/Makefile.am b/kteatime/Makefile.am new file mode 100644 index 0000000..82ebf40 --- /dev/null +++ b/kteatime/Makefile.am @@ -0,0 +1,27 @@ + +INCLUDES= $(all_includes) + +bin_PROGRAMS = kteatime + +kteatime_SOURCES = main.cpp toplevel.cpp tealist.cpp timeedit.cpp + +kteatime_METASOURCES = AUTO +kteatime_LDFLAGS = $(all_libraries) $(KDE_RPATH) + +# LIB_KIO needed for KNotifyDialog +kteatime_LDADD = $(LIB_KDEUI) $(LIB_KIO) + +KDE_ICON = kteatime + +KDE_OPTIONS = nofinal + +appsdata_DATA = eventsrc +appsdatadir = $(kde_datadir)/kteatime + +kteatimeicondir = $(kde_datadir)/kteatime/icons +kteatimeicon_ICON = AUTO + +messages: + $(XGETTEXT) $(kteatime_SOURCES) -o $(podir)/kteatime.pot + +xdg_apps_DATA = kteatime.desktop diff --git a/kteatime/TODO b/kteatime/TODO new file mode 100644 index 0000000..3400b2b --- /dev/null +++ b/kteatime/TODO @@ -0,0 +1,58 @@ +- create SVG icon(s) (or at least a 32x32 version for "About KTeaTime") +- Rename "shooting" +- Rename "Event" to "Notification" +- nicer placement of "Configure events" button: + "Configure Events..." --> move to same line as "Event" and rename to "Configure..."? + (and/or have a look at KStdAction::configureNotifications()!) + (common is "Configure &Notifications..." btw.) + [which gets translated in kdelibs.ps to "&Benachrichtigungen festlegen..."] + [[more often, "einrichten" is used for "configure"]] +- use "readPathEntry()" instead of "readEntry()" (expands/replaces $HOME) + +- have a look at memory consumption; seems too high for me.. +- adapt kteatime.docbook to new century... +- save default time for "anonymous" to config +- in configure-window: add "Execute: "-label in front of "action"-text edit +- KNotifyClient: "Execute" not capable of %t substitution? + (--> hence no replacement for my own "Execute: " action) +- KNotifyClient: passive popup cannot(?) have no timeout + (--> hence no replacement for my own "Popup" action) +- when steeping, display "stop <teaname>" on left-click, not just "stop" + (not easy when using KAction in a simple way?) +- use QMovie for tea-is-finished-icon instead of two alternating pixmaps? + + +internals: +- 'int2time()' somewhere into class-namespace? (ie. not as global function) +- do kapp->closeAllWindows() on slotFileQuit() ? +- when in "ready"-state, left-click -> auto-go to initial state + before opening left-click menu(?) +- remember geometry of config-dialog (and its QListView!) + (or at least: x-maximize QListView's titles) +- somehow make double-click with left button start 'current' tea + without displaying start_menu? +- figure out how to use KAction for "quit"-entry of menu + (somehow doesn't work as expected..) +- some more tidying of configure-dialog + (eg. initial position!, initial size) +- DCOP interface? +- have a closer look at i18n-dos and -donts +- use XML-UI +- check for memory leaks +- make config-reading bullet-proof + + +---- new approach: + +- make it a generalized timer-application (with better UI than KTimer) + (think: cooking spaghetti, watch TV-show, etc.) + + - This needs another approach to entering timeouts, since they'll often be + needed just once -> eg. popup "Enter timeout:"-window on leftclick? + - Must be able to run arbitrary amounts of parallel timers! + (difficult to use 'useTrayVis' then..) + - Should be able to enter timeouts not just as seconds, but in any unit. + - option for "popup reminder every X minutes" + - Allow periodical timouts (eg. "Take this medication every 2 hours") + (if not clicked away, add count to window) +--> better start new application and let people migrate to it from KTeaTime diff --git a/kteatime/cr22-action-mug.png b/kteatime/cr22-action-mug.png Binary files differnew file mode 100644 index 0000000..8c2cc54 --- /dev/null +++ b/kteatime/cr22-action-mug.png diff --git a/kteatime/cr22-action-tea_anim1.png b/kteatime/cr22-action-tea_anim1.png Binary files differnew file mode 100644 index 0000000..e6e0256 --- /dev/null +++ b/kteatime/cr22-action-tea_anim1.png diff --git a/kteatime/cr22-action-tea_anim2.png b/kteatime/cr22-action-tea_anim2.png Binary files differnew file mode 100644 index 0000000..db6cf91 --- /dev/null +++ b/kteatime/cr22-action-tea_anim2.png diff --git a/kteatime/cr22-action-tea_not_ready.png b/kteatime/cr22-action-tea_not_ready.png Binary files differnew file mode 100644 index 0000000..0ff40f3 --- /dev/null +++ b/kteatime/cr22-action-tea_not_ready.png diff --git a/kteatime/eventsrc b/kteatime/eventsrc new file mode 100644 index 0000000..aae92b2 --- /dev/null +++ b/kteatime/eventsrc @@ -0,0 +1,183 @@ +[!Global!] +IconName=kteatime +Comment=The KDE Tea Cooker +Comment[ar]=طابخ شاي KDE +Comment[be]=Прыгатаўленне гарбаты пры дапамозе KDE +Comment[bg]=Програма за приготвяне на чай +Comment[bn]=কে.ডি.ই.'র চায়ের কেটলি +Comment[br]=Pod te KDE +Comment[bs]=KDE kuhač čaja +Comment[ca]=El cuiner de te de KDE +Comment[cs]=Vařič čaje pro KDE +Comment[cy]=Tegell Te KDE +Comment[da]=KDE's tekoger +Comment[de]=Der KDE-Teekocher +Comment[el]=Εργαλείο φτιαξίματος τσαγιού του KDE +Comment[eo]=La KDE-Teinfuzilo +Comment[es]=Tetera de Kde +Comment[et]=KDE teekann +Comment[eu]=KDE Te-Sukaldea +Comment[fa]=چایساز KDE +Comment[fi]=Teenkeittäjä +Comment[fr]=Le préparateur de thé de KDE +Comment[ga]=Cócaire Tae KDE +Comment[gl]=Quece chás +Comment[he]=מבשל התה של KDE +Comment[hi]=केडीई चाय केतली +Comment[hr]=KDE čajnik +Comment[hu]=KDE teafőző +Comment[is]=KDE te hitarinn +Comment[it]=La teiera di KDE +Comment[ja]=KDE ティーメーカー +Comment[km]=ចង្ក្រានតែ KDE +Comment[ko]=KDE 차 끓이기 도구 +Comment[lv]=KDE tējas vārītājs +Comment[mk]=Чајникот на KDE +Comment[ms]=Penjerang Teh KDE +Comment[mt]=Il-Kitla tat-Tè tal-KDE +Comment[nb]=KDEs tekoker +Comment[nds]=De KDE-Teekaker +Comment[ne]=केडीई चिया बनाउने +Comment[nl]=De KDE Theezetter +Comment[nn]=KDE-tekokaren +Comment[pl]=Przyrządzanie herbaty dla KDE +Comment[pt]=Cozinheiro de chá do KDE +Comment[pt_BR]=O cozinheiro do KDE +Comment[ro]=Ceainicul KDE +Comment[ru]=Приготовление чая +Comment[sk]=Varič čaju pre KDE +Comment[sl]=Kuhalnik čaja za KDE +Comment[sr]=KDE Чајник +Comment[sr@Latn]=KDE Čajnik +Comment[sv]=KDE:s tekokare +Comment[ta]=The KDE தேநீர் குக்கர் +Comment[tg]=Пухтани Чойи KDE +Comment[tr]=Çay Pişirici +Comment[uk]=Приготування чаю KDE +Comment[uz]=KDE choy damlovchi +Comment[uz@cyrillic]=KDE чой дамловчи +Comment[wa]=Li fjheu di té di KDE +Comment[zh_CN]=KDE 煮茶器 +Comment[zh_TW]=KDE 煮茶器 + +[tea] +Name=Tea is ready +Name[ar]=الشاي جاهز +Name[be]=Гарбата гатова +Name[bg]=Чаят е готов +Name[bn]=চা প্রস্তুত +Name[br]=Prest eo an te +Name[bs]=Čaj je gotov +Name[ca]=El te està fet +Name[cs]=Čaj je připraven +Name[cy]=Mae'r te yn barod +Name[da]=Teen er klar +Name[de]=Tee ist fertig +Name[el]=Το τσάι είναι έτοιμο +Name[eo]=Teo estas preta +Name[es]=El té está listo +Name[et]=Tee on valmis +Name[eu]=Tea prest dago +Name[fa]=چای آماده است +Name[fi]=Tee on valmista +Name[fr]=Le thé est prêt +Name[ga]=Tá an tae réidh +Name[gl]=O chá está feito +Name[he]= התה מוכן +Name[hi]=चाय तैयार है +Name[hr]=Čaj je spreman +Name[hu]=Kész a tea! +Name[is]=Te hitarinn +Name[it]=Il tè è pronto +Name[ja]=お茶が入りました +Name[km]=តែឆ្អិនហើយ +Name[ko]=차가 준비되었습니다 +Name[lv]=Tēja ir gatava +Name[mk]=Чајот е готов +Name[mt]=It-Tè lest +Name[nb]=Tekokeren +Name[nds]=Tee is fardig +Name[ne]=चिया तयार छ +Name[nl]=De thee is klaar +Name[nn]=Teen er klar +Name[pl]=Herbata jest gotowa +Name[pt]=o chá está pronto +Name[pt_BR]=O chá está pronto +Name[ro]=Gata ceaiul +Name[ru]=Чай готов +Name[sk]=Čaj je pripravený +Name[sl]=Čaj je pripravljen +Name[sr]=Чај је готов +Name[sr@Latn]=Čaj je gotov +Name[sv]=Teet är klart +Name[ta]= தேநீர் தயார் +Name[tg]=Чой тайёр шуд +Name[tr]=Çay hazır +Name[uk]=Чай готовий +Name[uz]=Choy tayyor +Name[uz@cyrillic]=Чой тайёр +Name[wa]=Li té est presse +Name[zh_CN]=茶沏好了 +Name[zh_TW]=茶煮好了 +Comment=Tea is ready +Comment[ar]=الشاي جاهز +Comment[be]=Гарбата гатова +Comment[bg]=Чаят е готов +Comment[bn]=অই চা গরমমম!!! +Comment[br]=Prest eo an te +Comment[bs]=Čaj je gotov +Comment[ca]=El te està fet +Comment[cs]=Čaj je připraven +Comment[cy]=Mae'r te yn barod +Comment[da]=Teen er klar +Comment[de]=Tee ist fertig +Comment[el]=Το τσάι είναι έτοιμο +Comment[eo]=Teo estas preta +Comment[es]=El té está listo +Comment[et]=Tee on valmis +Comment[eu]=Tea prest dago +Comment[fa]=چای آماده است +Comment[fi]=Tee on valmista +Comment[fr]=Le thé est prêt +Comment[ga]=Tá an tae réidh +Comment[gl]=O té está feito +Comment[he]=התה מוכן +Comment[hi]=चाय तैयार है +Comment[hr]=Čaj je spreman +Comment[hu]=Kész a tea! +Comment[is]=Teið er tilbúið +Comment[it]=Il tè è pronto +Comment[ja]=お茶が入りました +Comment[km]=តែឆ្អិនហើយ +Comment[ko]=차가 준비되었습니다 +Comment[lv]=Tēja ir gatava +Comment[mk]=Чајот е готов +Comment[ms]=Teh sudah siap +Comment[mt]=It-Tè lest +Comment[nb]=Teen er klar +Comment[nds]=Tee is fardig +Comment[ne]=चिया तयार छ +Comment[nl]=De thee is klaar +Comment[nn]=Teen er klar +Comment[pl]=Herbata jest gotowa +Comment[pt]=O chá está pronto +Comment[pt_BR]=O chá está pronto +Comment[ro]=Ceaiul este gata +Comment[ru]=Чай готов +Comment[sk]=Čaj je pripravený +Comment[sl]=Čaj je pripravljen +Comment[sr]=Чај је готов +Comment[sr@Latn]=Čaj je gotov +Comment[sv]=Teet är klart +Comment[ta]= தேநீர் தயார் +Comment[tg]=Чой тайёр шуд +Comment[tr]=Çay hazır +Comment[uk]=Чай готовий +Comment[uz]=Choy tayyor +Comment[uz@cyrillic]=Чой тайёр +Comment[wa]=Li té est presse +Comment[zh_CN]=茶沏好了 +Comment[zh_TW]=茶煮好了 +default_sound=KDE_Notify.wav +default_presentation=1 + diff --git a/kteatime/hi16-app-kteatime.png b/kteatime/hi16-app-kteatime.png Binary files differnew file mode 100644 index 0000000..4952035 --- /dev/null +++ b/kteatime/hi16-app-kteatime.png diff --git a/kteatime/hi32-app-kteatime.png b/kteatime/hi32-app-kteatime.png Binary files differnew file mode 100644 index 0000000..78097e0 --- /dev/null +++ b/kteatime/hi32-app-kteatime.png diff --git a/kteatime/hi48-app-kteatime.png b/kteatime/hi48-app-kteatime.png Binary files differnew file mode 100644 index 0000000..f84a3e0 --- /dev/null +++ b/kteatime/hi48-app-kteatime.png diff --git a/kteatime/kteatime.desktop b/kteatime/kteatime.desktop new file mode 100644 index 0000000..3182834 --- /dev/null +++ b/kteatime/kteatime.desktop @@ -0,0 +1,72 @@ +[Desktop Entry] +Name=KTeaTime +Name[ar]=برنامج تحضير الشاي KTeaTime +Name[bn]=কে-টী-টাইম +Name[hr]=KVrijeme za čaj +Name[mk]=КЧајник +Name[mt]=It-Tè lest +Name[ne]=केडीई चिया समय +Name[sv]=Teet är klart +Name[ta]= கேதேநீர் நேரம் +Name[tg]=КВақтиЧой +GenericName=Tea Cooker +GenericName[be]=Прыгатаўленне гарбаты +GenericName[bg]=Приготвяне на чай +GenericName[bn]=চায়ের কেটলি +GenericName[br]=Ar pod te +GenericName[ca]=El cuiner de te +GenericName[cs]=Vařič čaje +GenericName[cy]=Tegell Te +GenericName[da]=Tekoger +GenericName[de]=Teekocher +GenericName[el]=Εργαλείο φτιαξίματος τσαγιού +GenericName[eo]=Teinfuzilo +GenericName[es]=Tetera +GenericName[et]=Teekann +GenericName[eu]=Te-Sukaldea +GenericName[fa]=چایساز +GenericName[fi]=Teenkeittäjä +GenericName[fr]=Préparateur de thé +GenericName[ga]=Cócaire Tae +GenericName[gl]=Quece Chá +GenericName[he]=מבשל התה +GenericName[hr]=Čajnik +GenericName[hu]=Teafőző +GenericName[is]=Te hitarinn +GenericName[it]=Teiera +GenericName[ja]=ティーメーカー +GenericName[km]=ចង្ក្រានតែ +GenericName[ko]=차 끓이기 +GenericName[lv]=Tējas vārītājs +GenericName[mk]=Чајник +GenericName[mt]=Il-Kitla tat-Tè +GenericName[nb]=Tekoker +GenericName[nds]=Teekaker +GenericName[ne]=चिया बनाउने +GenericName[nl]=Theezetter +GenericName[nn]=Tekokaren +GenericName[pl]=Przyrządzanie herbaty +GenericName[pt]=Chá +GenericName[pt_BR]=Hora do Chá +GenericName[ro]=Ceainic +GenericName[ru]=Приготовление чая +GenericName[sk]=Varič čaju +GenericName[sl]=Kuhalnik čaja +GenericName[sr]=Чајник +GenericName[sr@Latn]=Čajnik +GenericName[sv]=Tekokaren +GenericName[ta]= தேநீர் தயாரிப்பான் +GenericName[tg]=ЧойДамКардан +GenericName[tr]=Çaycı +GenericName[uk]=Приготування чаю +GenericName[wa]=Fijheu d' té +GenericName[zh_CN]=煮茶器 +GenericName[zh_TW]=煮茶器 +Exec=kteatime +Icon=kteatime +Type=Application +Terminal=false +DocPath=kteatime/index.html +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Utility;Amusement; diff --git a/kteatime/main.cpp b/kteatime/main.cpp new file mode 100644 index 0000000..fb147a6 --- /dev/null +++ b/kteatime/main.cpp @@ -0,0 +1,60 @@ +/* + * KTeaTime - A tea timer. + * + * Copyright (C) 1998-1999 Matthias Hoelzer-Kluepfel (hoelzer@kde.org) + * Copyright (C) 2002-2003 Martin Willers (willers@xm-arts.de) + * + * With contributions from Daniel Teske <teske@bigfoot.com>, and + * Jackson Dunstan <jdunstan@digipen.edu> + * (and possibly others, as well) + * + * 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 <kwin.h> +#include <kstartupinfo.h> +#include <kcmdlineargs.h> +#include <kaboutdata.h> + +#include "toplevel.h" + + +static const char description[] = + I18N_NOOP("KDE utility for making a fine cup of tea"); + +static const char version[] = "v1.1.0"; + +int main(int argc, char *argv[]) +{ + KAboutData aboutData( "kteatime", I18N_NOOP("KTeaTime"), + version, description, KAboutData::License_GPL, + "(c) 1998-1999, Matthias Hoelzer-Kluepfel\n(c) 2002-2003, Martin Willers"); + aboutData.addAuthor("Matthias Hoelzer-Kluepfel",0, "hoelzer@kde.org"); + aboutData.addAuthor("Martin Willers", 0, "willers@xm-arts.de"); + aboutData.addCredit("Daniel Teske", I18N_NOOP("Many patches"), "teske@bigfoot.com"); + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication app; + + TopLevel toplevel; + KWin::setSystemTrayWindowFor(toplevel.winId(), 0); + toplevel.show(); + + app.setTopWidget(&toplevel); + KStartupInfo::appStarted(); + + return app.exec(); +} diff --git a/kteatime/tealist.cpp b/kteatime/tealist.cpp new file mode 100644 index 0000000..0f7aa26 --- /dev/null +++ b/kteatime/tealist.cpp @@ -0,0 +1,64 @@ +/* ------------------------------------------------------------- + + tealist.cpp + + (C) 2003 by Daniel Teske (teske@bigfoot.com) + + ------------------------------------------------------------- */ + +#include <klocale.h> +#include <qlistview.h> +#include "tealist.h" + + +QString int2time(int time) +{ + QString str; + if (time / 60) + str.append(i18n("%1 min").arg(time / 60)); + if (time % 60) + if (str.isEmpty()) + str.append(i18n("%1 s").arg(time % 60)); + else + str.append(i18n(" %1 s").arg(time % 60)); + return str; +} + + +TeaListItem::TeaListItem(QListView * parent) + :QListViewItem(parent) +{ + +} + +TeaListItem::TeaListItem(QListView * parent, QListViewItem *after) + :QListViewItem(parent, after) +{ + +} + +TeaListItem::~TeaListItem() +{ +} + +void TeaListItem::setTime(int t) +{ + QListViewItem::setText(1, int2time(t)); + tim = t; +} + +void TeaListItem::setName(const QString &n) +{ + nam = n; + QListViewItem::setText(0, n); +} + +QString TeaListItem::name() +{ + return nam; +} + +int TeaListItem::time() +{ + return tim; +} diff --git a/kteatime/tealist.h b/kteatime/tealist.h new file mode 100644 index 0000000..8d025c8 --- /dev/null +++ b/kteatime/tealist.h @@ -0,0 +1,37 @@ +/* ------------------------------------------------------------- + + tealist.h + + (C) 2003 by Daniel Teske (teske@bigfoot.com) + + ------------------------------------------------------------- */ +#ifndef TEALIST_H +#define TEALIST_H + +#include <qstring.h> + +class QListView; +class QListViewItem; + + +class TeaListItem : public QListViewItem +{ + +public: + TeaListItem(QListView *parent); + TeaListItem(QListView *parent, QListViewItem *after); + ~TeaListItem(); + + int time(); + QString name(); + void setTime(int v); + void setName(const QString &n); + +private: + int tim; + QString nam; +}; + + +QString int2time(int t); +#endif diff --git a/kteatime/timeedit.cpp b/kteatime/timeedit.cpp new file mode 100644 index 0000000..c101de0 --- /dev/null +++ b/kteatime/timeedit.cpp @@ -0,0 +1,146 @@ +/* ------------------------------------------------------------- + + timeedit.cpp + + (C) 2003 by Daniel Teske (teske@bigfoot.com) + + ------------------------------------------------------------- */ + +#include <klocale.h> +#include <qlabel.h> +#include <qlayout.h> + +#include "timeedit.h" +#include "timeedit.moc" + +WrappingSpinBox::WrappingSpinBox(int minValue, int maxValue, int step, QWidget *parent, const char *name) + : QSpinBox(minValue, maxValue, step, parent, name) +{ +} + +WrappingSpinBox::~WrappingSpinBox() +{ +} + + +/** Overloaded QSpinBox method */ +void WrappingSpinBox::stepUp() +{ + bool wrap = false; + if (value() == 59) + wrap = true; + if (wrap) + emit wrapUp(); // must wrap first (to avoid double-step-up) + QSpinBox::stepUp(); +} + +/** Overloaded QSpinBox method */ +void WrappingSpinBox::stepDown() +{ + bool wrap = false; + if (value() == 0) + wrap = true; + QSpinBox::stepDown(); + if (wrap) + emit wrapDown(); +} + + +// ------------------------------------------------------------------------- + + +TimeEdit::TimeEdit(QWidget* parent, const char* name) + : QWidget(parent, name) +{ + layout = new QHBoxLayout(this); + minuteBox = new QSpinBox(0, 300, 1, this); +// minuteBox->setFixedSize(minuteBox->sizeHint()); + + QLabel* min = new QLabel(i18n(" min"), this); + min->setFixedSize(min->sizeHint()); + secondBox = new WrappingSpinBox(0, 59, 1, this); + secondBox->setWrapping(true); +// secondBox->setFixedSize(secondBox->sizeHint()); + + QLabel* sec = new QLabel(i18n(" sec"),this); + sec->setFixedSize(sec->sizeHint()); + + layout->addWidget(minuteBox); + layout->addWidget(min); + + layout->addWidget(secondBox); + layout->addWidget(sec); + + connect(minuteBox, SIGNAL(valueChanged(int)), SLOT(spinBoxValueChanged(int)) ); + connect(secondBox, SIGNAL(valueChanged(int)), SLOT(spinBoxValueChanged(int)) ); + connect(secondBox, SIGNAL(wrapUp()), SLOT(wrappedUp())); + connect(secondBox, SIGNAL(wrapDown()), SLOT(wrappedDown())); +} + +TimeEdit::~TimeEdit() +{ +} + +/** Set to specified number of seconds. */ +void TimeEdit::setValue(int val) +{ + if (val < 0) + return; + + // block signals to avoid receiption of valueChanged() + // between changing of minutes and seconds + secondBox->blockSignals(true); + minuteBox->blockSignals(true); + + secondBox->setValue(val % 60); + minuteBox->setValue(val / 60); + + secondBox->blockSignals(false); + minuteBox->blockSignals(false); + + emit valueChanged(value()); +} + +/** Return current value in seconds. */ +int TimeEdit::value() +{ + return minuteBox->value()*60 + secondBox->value(); +} + +/** SLOT: Handle wrap-up of seconds-box */ +void TimeEdit::wrappedUp() +{ + if (minuteBox->value() != minuteBox->maxValue()) { + minuteBox->stepUp(); + } else { + secondBox->setValue(58); // ugly: must cater for wrapping-first + } +} + +/** SLOT: Handle wrap-down of seconds-box */ +void TimeEdit::wrappedDown() +{ + // well, the "if" should always be true + if (minuteBox->value() != minuteBox->minValue()) { + minuteBox->stepDown(); + } else { + secondBox->setValue(0); + } +} + +/** SLOT: Handle any change in minutes of seconds */ +void TimeEdit::spinBoxValueChanged(int) +{ + if (value() == 0) { + secondBox->stepUp(); // this will give another spinBoxValueChanged() invocation + return; + } + + emit valueChanged(value()); +} + +/** SLOT (overloading QSpinBox): set focus */ +void TimeEdit::setFocus() +{ + minuteBox->setFocus(); +} diff --git a/kteatime/timeedit.h b/kteatime/timeedit.h new file mode 100644 index 0000000..cc03f58 --- /dev/null +++ b/kteatime/timeedit.h @@ -0,0 +1,72 @@ +/* ------------------------------------------------------------- + + timeedit.h + + (C) 2003 by Daniel Teske (teske@bigfoot.com) + + ------------------------------------------------------------- */ +#ifndef TIMEEDIT_H +#define TIMEEDIT_H + +#include <qspinbox.h> +#include <qwidget.h> + +class QBoxLayout; + + +/** + * @short A spinbox that wraps around after reaching minValue resp. maxValue. + * @author Daniel Teske + */ +class WrappingSpinBox : public QSpinBox +{ + Q_OBJECT + +public: + WrappingSpinBox(int minValue, int maxValue, int step = 1, QWidget *parent=0, const char *name=0); + ~WrappingSpinBox(); + + void stepUp(); + void stepDown(); + +signals: + void wrapUp(); + void wrapDown(); +}; + + +/** + * @short A widget for entering a timeout in minutes and seconds. + * @author Daniel Teske + */ +class TimeEdit : public QWidget +{ + Q_OBJECT + +public: + TimeEdit(QWidget* parent = 0, const char* name = 0); + ~TimeEdit(); + + void setValue(int value); + int value(); + +public slots: + void setFocus(); + +private slots: + void spinBoxValueChanged(int); + void wrappedUp(); + void wrappedDown(); + +signals: + void valueChanged(int); + + +protected: + QSpinBox *minuteBox; + WrappingSpinBox *secondBox; + QBoxLayout* layout; +}; + +#endif + 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(); + } +} diff --git a/kteatime/toplevel.h b/kteatime/toplevel.h new file mode 100644 index 0000000..a172189 --- /dev/null +++ b/kteatime/toplevel.h @@ -0,0 +1,124 @@ +/* + * 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. + * + */ + +#ifndef TOPLEVEL_H +#define TOPLEVEL_H + +#include <kapplication.h> +#include <qpopupmenu.h> +#include <qtimer.h> +#include <qlineedit.h> +#include <qvaluevector.h> +#include <qlistview.h> +#include <qpushbutton.h> +#include <qgroupbox.h> +#include <knuminput.h> +#include <ksystemtray.h> +#include <qpixmap.h> + +class KAction; +class KDialogBase; +class QCheckBox; +class TimeEdit; + +class TopLevel : public KSystemTray +{ + Q_OBJECT + +public: + + TopLevel(); + ~TopLevel(); + +protected: + + void paintEvent(QPaintEvent *); + void mousePressEvent(QMouseEvent *); + void timerEvent(QTimerEvent *); + +private slots: + + void teaSelected(int index); + void teaStartSelected(int index); + void start(); + void stop(); + void config(); + void help(); + void anonymous(); + void setToolTip(const QString &text, bool force=false); + void rebuildTeaMenus(); + + void listBoxItemSelected(); + void nameEditTextChanged(const QString& newText); + void spinBoxValueChanged(int v); + void newButtonClicked(); + void delButtonClicked(); + void upButtonClicked(); + void downButtonClicked(); + void confButtonClicked(); + void enable_menuEntries(); + void disable_properties(); + void enable_properties(); + void enable_controls(); + void actionEnableToggled(bool on); + +private: + + static const int DEFAULT_TEA_TIME; + + struct tea_struct { + QString name; + int time; + }; + QValueVector<tea_struct> teas; // list of tea-names and times + + bool running, ready, firstFrame, listempty; + int seconds; // variable for counting down seconds + int startSeconds; // steeping time for current tea + int percentDone; // ok, this isn't really "per 100", but "per 360" + + unsigned current_selected; // index of currently +selected+ tea in menu + QListViewItem *current_item; // ptr to currently +selected+ tea in ListView + QString current_name; // name of currently +running+ tea (if any) + bool shooting; // anonymous tea currently steeping? + + bool useNotify, usePopup, useAction; + QString action; + bool useTrayVis; // visualize progress in tray icon + + QPixmap mugPixmap, teaNotReadyPixmap, teaAnim1Pixmap, teaAnim2Pixmap; + + KAction *startAct, *stopAct, *confAct, *anonAct; + QPopupMenu *menu, *steeping_menu, *start_menu; + QListView *listbox; + QLineEdit *nameEdit, *actionEdit; + TimeEdit *timeEdit; + QGroupBox *editgroup; + QPushButton *btn_new, *btn_del, *btn_up, *btn_down, *btn_conf; + + QString lastTip; + KDialogBase *anondlg, *confdlg; + TimeEdit *anon_time; + QCheckBox *eventEnable, *popupEnable, *actionEnable, *visEnable; +}; + +#endif |