summaryrefslogtreecommitdiffstats
path: root/kteatime
diff options
context:
space:
mode:
Diffstat (limited to 'kteatime')
-rw-r--r--kteatime/ChangeLog105
-rw-r--r--kteatime/Makefile.am27
-rw-r--r--kteatime/TODO58
-rw-r--r--kteatime/cr22-action-mug.pngbin0 -> 310 bytes
-rw-r--r--kteatime/cr22-action-tea_anim1.pngbin0 -> 310 bytes
-rw-r--r--kteatime/cr22-action-tea_anim2.pngbin0 -> 362 bytes
-rw-r--r--kteatime/cr22-action-tea_not_ready.pngbin0 -> 441 bytes
-rw-r--r--kteatime/eventsrc183
-rw-r--r--kteatime/hi16-app-kteatime.pngbin0 -> 815 bytes
-rw-r--r--kteatime/hi32-app-kteatime.pngbin0 -> 2287 bytes
-rw-r--r--kteatime/hi48-app-kteatime.pngbin0 -> 4001 bytes
-rw-r--r--kteatime/kteatime.desktop72
-rw-r--r--kteatime/main.cpp60
-rw-r--r--kteatime/tealist.cpp64
-rw-r--r--kteatime/tealist.h37
-rw-r--r--kteatime/timeedit.cpp146
-rw-r--r--kteatime/timeedit.h72
-rw-r--r--kteatime/toplevel.cpp856
-rw-r--r--kteatime/toplevel.h124
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
new file mode 100644
index 0000000..8c2cc54
--- /dev/null
+++ b/kteatime/cr22-action-mug.png
Binary files differ
diff --git a/kteatime/cr22-action-tea_anim1.png b/kteatime/cr22-action-tea_anim1.png
new file mode 100644
index 0000000..e6e0256
--- /dev/null
+++ b/kteatime/cr22-action-tea_anim1.png
Binary files differ
diff --git a/kteatime/cr22-action-tea_anim2.png b/kteatime/cr22-action-tea_anim2.png
new file mode 100644
index 0000000..db6cf91
--- /dev/null
+++ b/kteatime/cr22-action-tea_anim2.png
Binary files differ
diff --git a/kteatime/cr22-action-tea_not_ready.png b/kteatime/cr22-action-tea_not_ready.png
new file mode 100644
index 0000000..0ff40f3
--- /dev/null
+++ b/kteatime/cr22-action-tea_not_ready.png
Binary files differ
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
new file mode 100644
index 0000000..4952035
--- /dev/null
+++ b/kteatime/hi16-app-kteatime.png
Binary files differ
diff --git a/kteatime/hi32-app-kteatime.png b/kteatime/hi32-app-kteatime.png
new file mode 100644
index 0000000..78097e0
--- /dev/null
+++ b/kteatime/hi32-app-kteatime.png
Binary files differ
diff --git a/kteatime/hi48-app-kteatime.png b/kteatime/hi48-app-kteatime.png
new file mode 100644
index 0000000..f84a3e0
--- /dev/null
+++ b/kteatime/hi48-app-kteatime.png
Binary files differ
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