summaryrefslogtreecommitdiffstats
path: root/kalarm/lib
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commit460c52653ab0dcca6f19a4f492ed2c5e4e963ab0 (patch)
tree67208f7c145782a7e90b123b982ca78d88cc2c87 /kalarm/lib
downloadtdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.tar.gz
tdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdepim@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kalarm/lib')
-rw-r--r--kalarm/lib/Makefile.am22
-rw-r--r--kalarm/lib/buttongroup.cpp70
-rw-r--r--kalarm/lib/buttongroup.h90
-rw-r--r--kalarm/lib/checkbox.cpp133
-rw-r--r--kalarm/lib/checkbox.h88
-rw-r--r--kalarm/lib/colourcombo.cpp239
-rw-r--r--kalarm/lib/colourcombo.h102
-rw-r--r--kalarm/lib/colourlist.cpp43
-rw-r--r--kalarm/lib/colourlist.h110
-rw-r--r--kalarm/lib/combobox.cpp78
-rw-r--r--kalarm/lib/combobox.h69
-rw-r--r--kalarm/lib/dateedit.cpp122
-rw-r--r--kalarm/lib/dateedit.h90
-rw-r--r--kalarm/lib/datetime.cpp80
-rw-r--r--kalarm/lib/datetime.h241
-rw-r--r--kalarm/lib/label.cpp118
-rw-r--r--kalarm/lib/label.h96
-rw-r--r--kalarm/lib/lineedit.cpp200
-rw-r--r--kalarm/lib/lineedit.h94
-rw-r--r--kalarm/lib/messagebox.cpp178
-rw-r--r--kalarm/lib/messagebox.h125
-rw-r--r--kalarm/lib/pushbutton.cpp102
-rw-r--r--kalarm/lib/pushbutton.h77
-rw-r--r--kalarm/lib/radiobutton.cpp134
-rw-r--r--kalarm/lib/radiobutton.h88
-rw-r--r--kalarm/lib/shellprocess.cpp208
-rw-r--r--kalarm/lib/shellprocess.h138
-rw-r--r--kalarm/lib/slider.cpp85
-rw-r--r--kalarm/lib/slider.h80
-rw-r--r--kalarm/lib/spinbox.cpp476
-rw-r--r--kalarm/lib/spinbox.h156
-rw-r--r--kalarm/lib/spinbox2.cpp511
-rw-r--r--kalarm/lib/spinbox2.h317
-rw-r--r--kalarm/lib/spinbox2private.h92
-rw-r--r--kalarm/lib/synchtimer.cpp233
-rw-r--r--kalarm/lib/synchtimer.h198
-rw-r--r--kalarm/lib/timeedit.cpp207
-rw-r--r--kalarm/lib/timeedit.h122
-rw-r--r--kalarm/lib/timeperiod.cpp384
-rw-r--r--kalarm/lib/timeperiod.h146
-rw-r--r--kalarm/lib/timespinbox.cpp364
-rw-r--r--kalarm/lib/timespinbox.h127
42 files changed, 6633 insertions, 0 deletions
diff --git a/kalarm/lib/Makefile.am b/kalarm/lib/Makefile.am
new file mode 100644
index 00000000..7d68a252
--- /dev/null
+++ b/kalarm/lib/Makefile.am
@@ -0,0 +1,22 @@
+INCLUDES = -I$(top_srcdir)/kalarm -I$(top_srcdir) $(all_includes)
+
+noinst_LTLIBRARIES = libkalarm.la
+
+libkalarm_la_METASOURCES = AUTO
+
+libkalarm_la_SOURCES = \
+ buttongroup.cpp checkbox.cpp colourcombo.cpp colourlist.cpp \
+ combobox.cpp dateedit.cpp datetime.cpp label.cpp messagebox.cpp \
+ pushbutton.cpp radiobutton.cpp timeedit.cpp timespinbox.cpp \
+ timeperiod.cpp shellprocess.cpp slider.cpp spinbox.cpp spinbox2.cpp \
+ lineedit.cpp synchtimer.cpp
+
+noinst_HEADERS = \
+ buttongroup.h checkbox.h colourcombo.h colourlist.h \
+ combobox.h dateedit.h datetime.h label.h lineedit.h messagebox.h \
+ pushbutton.h radiobutton.h timeedit.h timespinbox.h \
+ timeperiod.h shellprocess.h slider.h spinbox.h spinbox2.h \
+ synchtimer.h spinbox2private.h
+
+DOXYGEN_REFERENCES=kdecore kdeui libkdepim
+include $(top_srcdir)/admin/Doxyfile.am
diff --git a/kalarm/lib/buttongroup.cpp b/kalarm/lib/buttongroup.cpp
new file mode 100644
index 00000000..b448df48
--- /dev/null
+++ b/kalarm/lib/buttongroup.cpp
@@ -0,0 +1,70 @@
+/*
+ * buttongroup.cpp - QButtonGroup with an extra signal and KDE 2 compatibility
+ * Program: kalarm
+ * Copyright (c) 2002, 2004 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 "kalarm.h"
+
+#include <qlayout.h>
+#include <qbutton.h>
+#include <kdialog.h>
+
+#include "buttongroup.moc"
+
+
+ButtonGroup::ButtonGroup(QWidget* parent, const char* name)
+ : QButtonGroup(parent, name)
+{
+ connect(this, SIGNAL(clicked(int)), SIGNAL(buttonSet(int)));
+}
+
+ButtonGroup::ButtonGroup(const QString& title, QWidget* parent, const char* name)
+ : QButtonGroup(title, parent, name)
+{
+ connect(this, SIGNAL(clicked(int)), SIGNAL(buttonSet(int)));
+}
+
+ButtonGroup::ButtonGroup(int strips, Qt::Orientation orient, QWidget* parent, const char* name)
+ : QButtonGroup(strips, orient, parent, name)
+{
+ connect(this, SIGNAL(clicked(int)), SIGNAL(buttonSet(int)));
+}
+
+ButtonGroup::ButtonGroup(int strips, Qt::Orientation orient, const QString& title, QWidget* parent, const char* name)
+ : QButtonGroup(strips, orient, title, parent, name)
+{
+ connect(this, SIGNAL(clicked(int)), SIGNAL(buttonSet(int)));
+}
+
+/******************************************************************************
+ * Inserts a button in the group.
+ * This should really be a virtual method...
+ */
+int ButtonGroup::insert(QButton* button, int id)
+{
+ id = QButtonGroup::insert(button, id);
+ connect(button, SIGNAL(toggled(bool)), SLOT(slotButtonToggled(bool)));
+ return id;
+}
+
+/******************************************************************************
+ * Called when one of the member buttons is toggled.
+ */
+void ButtonGroup::slotButtonToggled(bool)
+{
+ emit buttonSet(selectedId());
+}
diff --git a/kalarm/lib/buttongroup.h b/kalarm/lib/buttongroup.h
new file mode 100644
index 00000000..1d647b42
--- /dev/null
+++ b/kalarm/lib/buttongroup.h
@@ -0,0 +1,90 @@
+/*
+ * buttongroup.h - QButtonGroup with an extra signal and Qt 2 compatibility
+ * Program: kalarm
+ * Copyright © 2002,2004,2006 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 BUTTONGROUP_H
+#define BUTTONGROUP_H
+
+#include <qbuttongroup.h>
+
+
+/**
+ * @short A QButtonGroup with signal on new selection, plus Qt 2 compatibility.
+ *
+ * The ButtonGroup class provides an enhanced version of the QButtonGroup class.
+ *
+ * It emits an additional signal, buttonSet(int), whenever any of its buttons
+ * changes state, for whatever reason, including programmatic control. (The
+ * QButtonGroup class only emits signals when buttons are clicked on by the user.)
+ * The class also provides Qt 2 compatibility.
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+ */
+class ButtonGroup : public QButtonGroup
+{
+ Q_OBJECT
+ public:
+ /** Constructor.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ explicit ButtonGroup(QWidget* parent, const char* name = 0);
+ /** Constructor.
+ * @param title The title displayed for this button group.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ ButtonGroup(const QString& title, QWidget* parent, const char* name = 0);
+ /** Constructor.
+ * @param strips The number of rows or columns of buttons.
+ * @param orient The orientation (Qt::Horizontal or Qt::Vertical) of the button group.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ ButtonGroup(int strips, Qt::Orientation orient, QWidget* parent, const char* name = 0);
+ /** Constructor.
+ * @param strips The number of rows or columns of buttons.
+ * @param orient The orientation (Qt::Horizontal or Qt::Vertical) of the button group.
+ * @param title The title displayed for this button group.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ ButtonGroup(int strips, Qt::Orientation orient, const QString& title, QWidget* parent, const char* name = 0);
+ /** Inserts a button in the group.
+ * This overrides the insert() method of QButtonGroup, which should really be a virtual method...
+ * @param button The button to insert.
+ * @param id The identifier for the button.
+ * @return The identifier of the inserted button.
+ */
+ int insert(QButton* button, int id = -1);
+ /** Sets the button with the specified identifier to be on. If this is an exclusive group,
+ * all other buttons in the group will be set off. The buttonSet() signal is emitted.
+ * @param id The identifier of the button to set on.
+ */
+ virtual void setButton(int id) { QButtonGroup::setButton(id); emit buttonSet(id); }
+ private slots:
+ void slotButtonToggled(bool);
+ signals:
+ /** Signal emitted whenever whenever any button in the group changes state,
+ * for whatever reason.
+ * @param id The identifier of the button which is now selected.
+ */
+ void buttonSet(int id);
+};
+
+#endif // BUTTONGROUP_H
diff --git a/kalarm/lib/checkbox.cpp b/kalarm/lib/checkbox.cpp
new file mode 100644
index 00000000..c600a495
--- /dev/null
+++ b/kalarm/lib/checkbox.cpp
@@ -0,0 +1,133 @@
+/*
+ * checkbox.cpp - check box with read-only option
+ * Program: kalarm
+ * Copyright (c) 2002, 2003 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 "checkbox.moc"
+
+
+CheckBox::CheckBox(QWidget* parent, const char* name)
+ : QCheckBox(parent, name),
+ mFocusPolicy(focusPolicy()),
+ mFocusWidget(0),
+ mReadOnly(false)
+{ }
+
+CheckBox::CheckBox(const QString& text, QWidget* parent, const char* name)
+ : QCheckBox(text, parent, name),
+ mFocusPolicy(focusPolicy()),
+ mFocusWidget(0),
+ mReadOnly(false)
+{ }
+
+/******************************************************************************
+* Set the read-only status. If read-only, the checkbox can be toggled by the
+* application, but not by the user.
+*/
+void CheckBox::setReadOnly(bool ro)
+{
+ if ((int)ro != (int)mReadOnly)
+ {
+ mReadOnly = ro;
+ setFocusPolicy(ro ? QWidget::NoFocus : mFocusPolicy);
+ if (ro)
+ clearFocus();
+ }
+}
+
+/******************************************************************************
+* Specify a widget to receive focus when the checkbox is clicked on.
+*/
+void CheckBox::setFocusWidget(QWidget* w, bool enable)
+{
+ mFocusWidget = w;
+ mFocusWidgetEnable = enable;
+ if (w)
+ connect(this, SIGNAL(clicked()), SLOT(slotClicked()));
+ else
+ disconnect(this, SIGNAL(clicked()), this, SLOT(slotClicked()));
+}
+
+/******************************************************************************
+* Called when the checkbox is clicked.
+* If it is now checked, focus is transferred to any specified focus widget.
+*/
+void CheckBox::slotClicked()
+{
+ if (mFocusWidget && isChecked())
+ {
+ if (mFocusWidgetEnable)
+ mFocusWidget->setEnabled(true);
+ mFocusWidget->setFocus();
+ }
+}
+
+/******************************************************************************
+* Event handlers to intercept events if in read-only mode.
+* Any events which could change the checkbox state are discarded.
+*/
+void CheckBox::mousePressEvent(QMouseEvent* e)
+{
+ if (mReadOnly)
+ {
+ // Swallow up the event if it's the left button
+ if (e->button() == Qt::LeftButton)
+ return;
+ }
+ QCheckBox::mousePressEvent(e);
+}
+
+void CheckBox::mouseReleaseEvent(QMouseEvent* e)
+{
+ if (mReadOnly)
+ {
+ // Swallow up the event if it's the left button
+ if (e->button() == Qt::LeftButton)
+ return;
+ }
+ QCheckBox::mouseReleaseEvent(e);
+}
+
+void CheckBox::mouseMoveEvent(QMouseEvent* e)
+{
+ if (!mReadOnly)
+ QCheckBox::mouseMoveEvent(e);
+}
+
+void CheckBox::keyPressEvent(QKeyEvent* e)
+{
+ if (mReadOnly)
+ switch (e->key())
+ {
+ case Qt::Key_Up:
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ case Qt::Key_Down:
+ // Process keys which shift the focus
+ break;
+ default:
+ return;
+ }
+ QCheckBox::keyPressEvent(e);
+}
+
+void CheckBox::keyReleaseEvent(QKeyEvent* e)
+{
+ if (!mReadOnly)
+ QCheckBox::keyReleaseEvent(e);
+}
diff --git a/kalarm/lib/checkbox.h b/kalarm/lib/checkbox.h
new file mode 100644
index 00000000..72ad4aee
--- /dev/null
+++ b/kalarm/lib/checkbox.h
@@ -0,0 +1,88 @@
+/*
+ * checkbox.h - check box with focus widget and read-only options
+ * Program: kalarm
+ * Copyright © 2002,2003,2005,2006 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 CHECKBOX_H
+#define CHECKBOX_H
+
+#include <qcheckbox.h>
+
+
+/**
+ * @short A QCheckBox with focus widget and read-only options.
+ *
+ * The CheckBox class is a QCheckBox with the ability to transfer focus to another
+ * widget when checked, and with a read-only option.
+ *
+ * Another widget may be specified as the focus widget for the check box. Whenever
+ * the user clicks on the check box so as to set its state to checked, focus is
+ * automatically transferred to the focus widget.
+ *
+ * The widget may be set as read-only. This has the same effect as disabling it, except
+ * that its appearance is unchanged.
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+ */
+class CheckBox : public QCheckBox
+{
+ Q_OBJECT
+ public:
+ /** Constructor.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ explicit CheckBox(QWidget* parent, const char* name = 0);
+ /** Constructor.
+ * @param text Text to display.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ CheckBox(const QString& text, QWidget* parent, const char* name = 0);
+ /** Returns true if the widget is read only. */
+ bool isReadOnly() const { return mReadOnly; }
+ /** Sets whether the check box is read-only for the user. If read-only,
+ * its state cannot be changed by the user.
+ * @param readOnly True to set the widget read-only, false to set it read-write.
+ */
+ virtual void setReadOnly(bool readOnly);
+ /** Returns the widget which receives focus when the user selects the check box by clicking on it. */
+ QWidget* focusWidget() const { return mFocusWidget; }
+ /** Specifies a widget to receive focus when the user selects the check box by clicking on it.
+ * @param widget Widget to receive focus.
+ * @param enable If true, @p widget will be enabled before receiving focus. If
+ * false, the enabled state of @p widget will be left unchanged when
+ * the check box is clicked.
+ */
+ void setFocusWidget(QWidget* widget, bool enable = true);
+ protected:
+ virtual void mousePressEvent(QMouseEvent*);
+ virtual void mouseReleaseEvent(QMouseEvent*);
+ virtual void mouseMoveEvent(QMouseEvent*);
+ virtual void keyPressEvent(QKeyEvent*);
+ virtual void keyReleaseEvent(QKeyEvent*);
+ protected slots:
+ void slotClicked();
+ private:
+ QWidget::FocusPolicy mFocusPolicy; // default focus policy for the QCheckBox
+ QWidget* mFocusWidget; // widget to receive focus when button is clicked on
+ bool mFocusWidgetEnable; // enable focus widget before setting focus
+ bool mReadOnly; // value cannot be changed
+};
+
+#endif // CHECKBOX_H
diff --git a/kalarm/lib/colourcombo.cpp b/kalarm/lib/colourcombo.cpp
new file mode 100644
index 00000000..d5fa052a
--- /dev/null
+++ b/kalarm/lib/colourcombo.cpp
@@ -0,0 +1,239 @@
+/*
+ * colourcombo.cpp - colour selection combo box
+ * Program: kalarm
+ * Copyright (c) 2001 - 2003, 2005 by David Jarvie <software@astrojar.org.uk>
+ *
+ * Some code taken from kdelibs/kdeui/kcolorcombo.cpp in the KDE libraries:
+ * Copyright (C) 1997 Martin Jones (mjones@kde.org)
+ *
+ * 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 <qpainter.h>
+
+#include <klocale.h>
+#include <kcolordialog.h>
+
+#include "preferences.h"
+#include "colourcombo.moc"
+
+
+ColourCombo::ColourCombo(QWidget* parent, const char* name, const QColor& defaultColour)
+ : QComboBox(parent, name),
+ mColourList(Preferences::messageColours()),
+ mSelectedColour(defaultColour),
+ mCustomColour(255, 255, 255),
+ mReadOnly(false),
+ mDisabled(false)
+{
+ addColours();
+ connect(this, SIGNAL(activated(int)), SLOT(slotActivated(int)));
+ connect(this, SIGNAL(highlighted(int)), SLOT(slotHighlighted(int)));
+ Preferences::connect(SIGNAL(preferencesChanged()), this, SLOT(slotPreferencesChanged()));
+}
+
+void ColourCombo::setColour(const QColor& colour)
+{
+ mSelectedColour = colour;
+ addColours();
+}
+
+/******************************************************************************
+* Set a new colour selection.
+*/
+void ColourCombo::setColours(const ColourList& colours)
+{
+ mColourList = colours;
+ if (mSelectedColour != mCustomColour
+ && !mColourList.contains(mSelectedColour))
+ {
+ // The current colour has been deleted
+ mSelectedColour = mColourList.count() ? mColourList.first() : mCustomColour;
+ }
+ addColours();
+}
+
+/******************************************************************************
+* Called when the user changes the preference settings.
+* If the colour list has changed, update the colours displayed.
+*/
+void ColourCombo::slotPreferencesChanged()
+{
+ const ColourList& prefColours = Preferences::messageColours();
+ if (prefColours != mColourList)
+ setColours(prefColours); // update the display with the new colours
+}
+
+/******************************************************************************
+* Enable or disable the control.
+* If it is disabled, its colour is set to the dialog background colour.
+*/
+void ColourCombo::setEnabled(bool enable)
+{
+ if (enable && mDisabled)
+ {
+ mDisabled = false;
+ setColour(mSelectedColour);
+ }
+ else if (!enable && !mDisabled)
+ {
+ mSelectedColour = color();
+ int end = count();
+ if (end > 1)
+ {
+ // Add a dialog background colour item
+ QPixmap pm = *pixmap(1);
+ pm.fill(paletteBackgroundColor());
+ insertItem(pm);
+ setCurrentItem(end);
+ }
+ mDisabled = true;
+ }
+ QComboBox::setEnabled(enable);
+}
+
+void ColourCombo::slotActivated(int index)
+{
+ if (index)
+ mSelectedColour = mColourList[index - 1];
+ else
+ {
+ if (KColorDialog::getColor(mCustomColour, this) == QDialog::Accepted)
+ {
+ QRect rect;
+ drawCustomItem(rect, false);
+ }
+ mSelectedColour = mCustomColour;
+ }
+ emit activated(mSelectedColour);
+}
+
+void ColourCombo::slotHighlighted(int index)
+{
+ mSelectedColour = index ? mColourList[index - 1] : mCustomColour;
+ emit highlighted(mSelectedColour);
+}
+
+/******************************************************************************
+* Initialise the items in the combo box to one for each colour in the list.
+*/
+void ColourCombo::addColours()
+{
+ clear();
+
+ for (ColourList::const_iterator it = mColourList.begin(); ; ++it)
+ {
+ if (it == mColourList.end())
+ {
+ mCustomColour = mSelectedColour;
+ break;
+ }
+ if (mSelectedColour == *it)
+ break;
+ }
+
+ QRect rect;
+ drawCustomItem(rect, true);
+
+ QPainter painter;
+ QPixmap pixmap(rect.width(), rect.height());
+ int i = 1;
+ for (ColourList::const_iterator it = mColourList.begin(); it != mColourList.end(); ++i, ++it)
+ {
+ painter.begin(&pixmap);
+ QBrush brush(*it);
+ painter.fillRect(rect, brush);
+ painter.end();
+
+ insertItem(pixmap);
+ pixmap.detach();
+
+ if (*it == mSelectedColour.rgb())
+ setCurrentItem(i);
+ }
+}
+
+void ColourCombo::drawCustomItem(QRect& rect, bool insert)
+{
+ QPen pen;
+ if (qGray(mCustomColour.rgb()) < 128)
+ pen.setColor(Qt::white);
+ else
+ pen.setColor(Qt::black);
+
+ QPainter painter;
+ QFontMetrics fm = QFontMetrics(painter.font());
+ rect.setRect(0, 0, width(), fm.height() + 4);
+ QPixmap pixmap(rect.width(), rect.height());
+
+ painter.begin(&pixmap);
+ QBrush brush(mCustomColour);
+ painter.fillRect(rect, brush);
+ painter.setPen(pen);
+ painter.drawText(2, fm.ascent() + 2, i18n("Custom..."));
+ painter.end();
+
+ if (insert)
+ insertItem(pixmap);
+ else
+ changeItem(pixmap, 0);
+ pixmap.detach();
+}
+
+void ColourCombo::setReadOnly(bool ro)
+{
+ mReadOnly = ro;
+}
+
+void ColourCombo::resizeEvent(QResizeEvent* re)
+{
+ QComboBox::resizeEvent(re);
+ addColours();
+}
+
+void ColourCombo::mousePressEvent(QMouseEvent* e)
+{
+ if (mReadOnly)
+ {
+ // Swallow up the event if it's the left button
+ if (e->button() == Qt::LeftButton)
+ return;
+ }
+ QComboBox::mousePressEvent(e);
+}
+
+void ColourCombo::mouseReleaseEvent(QMouseEvent* e)
+{
+ if (!mReadOnly)
+ QComboBox::mouseReleaseEvent(e);
+}
+
+void ColourCombo::mouseMoveEvent(QMouseEvent* e)
+{
+ if (!mReadOnly)
+ QComboBox::mouseMoveEvent(e);
+}
+
+void ColourCombo::keyPressEvent(QKeyEvent* e)
+{
+ if (!mReadOnly || e->key() == Qt::Key_Escape)
+ QComboBox::keyPressEvent(e);
+}
+
+void ColourCombo::keyReleaseEvent(QKeyEvent* e)
+{
+ if (!mReadOnly)
+ QComboBox::keyReleaseEvent(e);
+}
diff --git a/kalarm/lib/colourcombo.h b/kalarm/lib/colourcombo.h
new file mode 100644
index 00000000..f3e4ebca
--- /dev/null
+++ b/kalarm/lib/colourcombo.h
@@ -0,0 +1,102 @@
+/*
+ * colourcombo.h - colour selection combo box
+ * Program: kalarm
+ * Copyright © 2001-2003,2005,2006 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 COLOURCOMBO_H
+#define COLOURCOMBO_H
+
+#include <qcombobox.h>
+#include "colourlist.h"
+
+
+/**
+ * @short A colour selection combo box whose colour list can be specified.
+ *
+ * The ColourCombo class is a combo box allowing the user to select a colour.
+ *
+ * It is similar to KColorCombo but allows the list of colours to be restricted to those
+ * which are specified. The first item in the list is a custom colour entry, which allows
+ * the user to define an arbitrary colour. The remaining entries in the list are preset
+ * by the program.
+ *
+ * The widget may be set as read-only. This has the same effect as disabling it, except
+ * that its appearance is unchanged.
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+ */
+class ColourCombo : public QComboBox
+{
+ Q_OBJECT
+ Q_PROPERTY(QColor color READ color WRITE setColor)
+ public:
+ /** Constructor.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ * @param defaultColour The colour which is selected by default.
+ */
+ explicit ColourCombo(QWidget* parent = 0, const char* name = 0, const QColor& defaultColour = 0xFFFFFF);
+ /** Returns the selected colour. */
+ QColor color() const { return mSelectedColour; }
+ /** Returns the selected colour. */
+ QColor colour() const { return mSelectedColour; }
+ /** Sets the selected colour to @p c. */
+ void setColor(const QColor& c) { setColour(c); }
+ /** Sets the selected colour to @p c. */
+ void setColour(const QColor& c);
+ /** Initialises the list of colours to @p list. */
+ void setColours(const ColourList& list);
+ /** Returns true if the first entry in the list, i.e. the custom colour, is selected. */
+ bool isCustomColour() const { return !currentItem(); }
+ /** Returns true if the widget is read only. */
+ bool isReadOnly() const { return mReadOnly; }
+ /** Sets whether the combo box can be changed by the user.
+ * @param readOnly True to set the widget read-only, false to set it read-write.
+ */
+ virtual void setReadOnly(bool readOnly);
+ signals:
+ /** Signal emitted when a new colour has been selected. */
+ void activated(const QColor&); // a new colour box has been selected
+ /** Signal emitted when a new colour has been highlighted. */
+ void highlighted(const QColor&); // a new item has been highlighted
+ public slots:
+ /** Enables or disables the widget. */
+ virtual void setEnabled(bool enabled);
+ protected:
+ virtual void resizeEvent(QResizeEvent*);
+ virtual void mousePressEvent(QMouseEvent*);
+ virtual void mouseReleaseEvent(QMouseEvent*);
+ virtual void mouseMoveEvent(QMouseEvent*);
+ virtual void keyPressEvent(QKeyEvent*);
+ virtual void keyReleaseEvent(QKeyEvent*);
+ private slots:
+ void slotActivated(int index);
+ void slotHighlighted(int index);
+ void slotPreferencesChanged();
+ private:
+ void addColours();
+ void drawCustomItem(QRect&, bool insert);
+
+ ColourList mColourList; // the sorted colours to display
+ QColor mSelectedColour; // currently selected colour
+ QColor mCustomColour; // current colour of the Custom item
+ bool mReadOnly; // value cannot be changed
+ bool mDisabled;
+};
+
+#endif // COLOURCOMBO_H
diff --git a/kalarm/lib/colourlist.cpp b/kalarm/lib/colourlist.cpp
new file mode 100644
index 00000000..e02a6446
--- /dev/null
+++ b/kalarm/lib/colourlist.cpp
@@ -0,0 +1,43 @@
+/*
+ * colourlist.cpp - an ordered list of colours
+ * Program: kalarm
+ * Copyright (C) 2003 by David Jarvie software@astrojar.org.uk
+ *
+ * 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 "colourlist.h"
+
+
+ColourList::ColourList(const QColor* colours)
+{
+ while (colours->isValid())
+ mList.append((*colours++).rgb());
+}
+
+void ColourList::insert(const QColor& colour)
+{
+ QRgb rgb = colour.rgb();
+ for (QValueListIterator<QRgb> it = mList.begin(); it != mList.end(); ++it)
+ {
+ if (rgb <= *it)
+ {
+ if (rgb != *it) // don't insert duplicates
+ mList.insert(it, rgb);
+ return;
+ }
+ }
+ mList.append(rgb);
+}
diff --git a/kalarm/lib/colourlist.h b/kalarm/lib/colourlist.h
new file mode 100644
index 00000000..ef641c04
--- /dev/null
+++ b/kalarm/lib/colourlist.h
@@ -0,0 +1,110 @@
+/*
+ * colourlist.h - an ordered list of colours
+ * Program: kalarm
+ * Copyright (C) 2003, 2005 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 COLOURLIST_H
+#define COLOURLIST_H
+
+#include <qtl.h>
+#include <qcolor.h>
+#include <qvaluelist.h>
+
+
+/**
+ * @short Represents a sorted list of colours.
+ *
+ * The ColourList class holds a list of colours, sorted in RGB value order.
+ *
+ * It provides a sorted QValueList of colours in RGB value order, with iterators
+ * and other access methods which return either QRgb or QColor objects.
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+ */
+class ColourList
+{
+ public:
+ typedef size_t size_type;
+ typedef QValueListConstIterator<QRgb> const_iterator;
+
+ /** Constructs an empty list. */
+ ColourList() { }
+ /** Copy constructor. */
+ ColourList(const ColourList& l) : mList(l.mList) { }
+ /** Constructs a list whose values are preset to the colours in @p list. */
+ ColourList(const QValueList<QRgb>& list) : mList(list) { qHeapSort(mList); }
+ /** Constructs a list whose values are preset to the colours in the @p list.
+ * Terminate @p list by an invalid colour.
+ */
+ ColourList(const QColor* list);
+ /** Assignment operator. */
+ ColourList& operator=(const ColourList& l) { mList = l.mList; return *this; }
+ /** Sets the list to comprise the colours in @p list. */
+ ColourList& operator=(const QValueList<QRgb>& list) { mList = list; qHeapSort(mList); return *this; }
+ /** Removes all values from the list. */
+ void clear() { mList.clear(); }
+ /** Adds the specified colour @p c to the list. */
+ void insert(const QColor& c);
+ /** Removes the colour @p c from the list. */
+ void remove(const QColor& c) { mList.remove(c.rgb()); }
+ /** Adds the specified colour @p c to the list. */
+ ColourList& operator+=(const QColor& c) { insert(c); return *this; }
+ /** Adds the colours in @p list to this list. */
+ ColourList& operator+=(const ColourList& list) { mList += list.mList; qHeapSort(mList); return *this; }
+ /** Returns true if the colours in the two lists are the same. */
+ bool operator==(const ColourList& l) const { return mList == l.mList; }
+ /** Returns true if the colours in the two lists differ. */
+ bool operator!=(const ColourList& l) const { return mList != l.mList; }
+ /** Returns the number of colours in the list. */
+ size_type count() const { return mList.count(); }
+ /** Returns true if the list is empty. */
+ bool isEmpty() const { return mList.isEmpty(); }
+ /** Returns an iterator pointing to the first colour in the list. */
+ const_iterator begin() const { return mList.begin(); }
+ /** Returns an iterator pointing past the last colour in the list. */
+ const_iterator end() const { return mList.end(); }
+ /** Returns an iterator pointing to the last colour in the list, or end() if the list is empty. */
+ const_iterator fromLast() const { return mList.fromLast(); }
+ /** Returns an iterator pointing to the colour at position @p i in the list. */
+ const_iterator at(size_type i) const { return mList.at(i); }
+ /** Returns true if the list contains the colour @p c. */
+ size_type contains(const QColor& c) const { return mList.contains(c.rgb()); }
+ /** Returns an iterator pointing to the first occurrence of colour @p c in the list.
+ * Returns end() if colour @p c is not in the list.
+ */
+ const_iterator find(const QColor& c) const { return mList.find(c.rgb()); }
+ /** Returns an iterator pointing to the first occurrence of colour @p c in the list, starting.
+ * from position @p it. Returns end() if colour @p c is not in the list.
+ */
+ const_iterator find(const_iterator it, const QColor& c) const { return mList.find(it, c.rgb()); }
+ /** Returns the index to the first occurrence of colour @p c in the list.
+ * Returns -1 if colour @p c is not in the list.
+ */
+ int findIndex(const QColor& c) const { return mList.findIndex(c.rgb()); }
+ /** Returns the first colour in the list. If the list is empty, the result is undefined. */
+ QColor first() const { return QColor(mList.first()); }
+ /** Returns the last colour in the list. If the list is empty, the result is undefined. */
+ QColor last() const { return QColor(mList.last()); }
+ /** Returns the colour at position @p i in the list. If the item does not exist, the result is undefined. */
+ QColor operator[](size_type i) const { return QColor(mList[i]); }
+ private:
+ void sort();
+ QValueList<QRgb> mList;
+};
+
+#endif // COLOURLIST_H
diff --git a/kalarm/lib/combobox.cpp b/kalarm/lib/combobox.cpp
new file mode 100644
index 00000000..7e0bea4b
--- /dev/null
+++ b/kalarm/lib/combobox.cpp
@@ -0,0 +1,78 @@
+/*
+ * combobox.cpp - combo box with read-only option
+ * Program: kalarm
+ * Copyright (c) 2002 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 <qlineedit.h>
+#include "combobox.moc"
+
+
+ComboBox::ComboBox(QWidget* parent, const char* name)
+ : QComboBox(parent, name),
+ mReadOnly(false)
+{ }
+
+ComboBox::ComboBox(bool rw, QWidget* parent, const char* name)
+ : QComboBox(rw, parent, name),
+ mReadOnly(false)
+{ }
+
+void ComboBox::setReadOnly(bool ro)
+{
+ if ((int)ro != (int)mReadOnly)
+ {
+ mReadOnly = ro;
+ if (lineEdit())
+ lineEdit()->setReadOnly(ro);
+ }
+}
+
+void ComboBox::mousePressEvent(QMouseEvent* e)
+{
+ if (mReadOnly)
+ {
+ // Swallow up the event if it's the left button
+ if (e->button() == Qt::LeftButton)
+ return;
+ }
+ QComboBox::mousePressEvent(e);
+}
+
+void ComboBox::mouseReleaseEvent(QMouseEvent* e)
+{
+ if (!mReadOnly)
+ QComboBox::mouseReleaseEvent(e);
+}
+
+void ComboBox::mouseMoveEvent(QMouseEvent* e)
+{
+ if (!mReadOnly)
+ QComboBox::mouseMoveEvent(e);
+}
+
+void ComboBox::keyPressEvent(QKeyEvent* e)
+{
+ if (!mReadOnly || e->key() == Qt::Key_Escape)
+ QComboBox::keyPressEvent(e);
+}
+
+void ComboBox::keyReleaseEvent(QKeyEvent* e)
+{
+ if (!mReadOnly)
+ QComboBox::keyReleaseEvent(e);
+}
diff --git a/kalarm/lib/combobox.h b/kalarm/lib/combobox.h
new file mode 100644
index 00000000..d33ac147
--- /dev/null
+++ b/kalarm/lib/combobox.h
@@ -0,0 +1,69 @@
+/*
+ * combobox.h - combo box with read-only option
+ * Program: kalarm
+ * Copyright © 2002,2005,2006 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 COMBOBOX_H
+#define COMBOBOX_H
+
+#include <qcombobox.h>
+
+
+/**
+ * @short A QComboBox with read-only option.
+ *
+ * The ComboBox class is a QComboBox with a read-only option.
+ *
+ * The widget may be set as read-only. This has the same effect as disabling it, except
+ * that its appearance is unchanged.
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+ */
+class ComboBox : public QComboBox
+{
+ Q_OBJECT
+ public:
+ /** Constructor.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ explicit ComboBox(QWidget* parent = 0, const char* name = 0);
+ /** Constructor.
+ * @param rw True for a read-write combo box, false for a read-only combo box.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ explicit ComboBox(bool rw, QWidget* parent = 0, const char* name = 0);
+ /** Returns true if the widget is read only. */
+ bool isReadOnly() const { return mReadOnly; }
+ /** Sets whether the combo box is read-only for the user. If read-only,
+ * its state cannot be changed by the user.
+ * @param readOnly True to set the widget read-only, false to set it read-write.
+ */
+ virtual void setReadOnly(bool readOnly);
+ protected:
+ virtual void mousePressEvent(QMouseEvent*);
+ virtual void mouseReleaseEvent(QMouseEvent*);
+ virtual void mouseMoveEvent(QMouseEvent*);
+ virtual void keyPressEvent(QKeyEvent*);
+ virtual void keyReleaseEvent(QKeyEvent*);
+ private:
+ bool mReadOnly; // value cannot be changed
+};
+
+#endif // COMBOBOX_H
diff --git a/kalarm/lib/dateedit.cpp b/kalarm/lib/dateedit.cpp
new file mode 100644
index 00000000..71553d70
--- /dev/null
+++ b/kalarm/lib/dateedit.cpp
@@ -0,0 +1,122 @@
+/*
+ * dateedit.cpp - date entry widget
+ * Program: kalarm
+ * Copyright © 2002-2007 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 <kglobal.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include "dateedit.moc"
+
+
+DateEdit::DateEdit(QWidget* parent, const char* name)
+ : KDateEdit(parent, name)
+{
+ connect(this, SIGNAL(dateEntered(const QDate&)), SLOT(newDateEntered(const QDate&)));
+}
+
+void DateEdit::setMinDate(const QDate& d, const QString& errorDate)
+{
+ mMinDate = d;
+ if (mMinDate.isValid() && date().isValid() && date() < mMinDate)
+ setDate(mMinDate);
+ mMinDateErrString = errorDate;
+}
+
+void DateEdit::setMaxDate(const QDate& d, const QString& errorDate)
+{
+ mMaxDate = d;
+ if (mMaxDate.isValid() && date().isValid() && date() > mMaxDate)
+ setDate(mMaxDate);
+ mMaxDateErrString = errorDate;
+}
+
+void DateEdit::setInvalid()
+{
+ setDate(QDate());
+}
+
+// Check a new date against any minimum or maximum date.
+void DateEdit::newDateEntered(const QDate& newDate)
+{
+ if (newDate.isValid())
+ {
+ if (mMinDate.isValid() && newDate < mMinDate)
+ {
+ pastLimitMessage(mMinDate, mMinDateErrString,
+ i18n("Date cannot be earlier than %1"));
+ setDate(mMinDate);
+ }
+ else if (mMaxDate.isValid() && newDate > mMaxDate)
+ {
+ pastLimitMessage(mMaxDate, mMaxDateErrString,
+ i18n("Date cannot be later than %1"));
+ setDate(mMaxDate);
+ }
+ }
+}
+
+void DateEdit::pastLimitMessage(const QDate& limit, const QString& error, const QString& defaultError)
+{
+ QString errString = error;
+ if (errString.isNull())
+ {
+ if (limit == QDate::currentDate())
+ errString = i18n("today");
+ else
+ errString = KGlobal::locale()->formatDate(limit, true);
+ errString = defaultError.arg(errString);
+ }
+ KMessageBox::sorry(this, errString);
+}
+
+void DateEdit::mousePressEvent(QMouseEvent *e)
+{
+ if (isReadOnly())
+ {
+ // Swallow up the event if it's the left button
+ if (e->button() == Qt::LeftButton)
+ return;
+ }
+ KDateEdit::mousePressEvent(e);
+}
+
+void DateEdit::mouseReleaseEvent(QMouseEvent* e)
+{
+ if (!isReadOnly())
+ KDateEdit::mouseReleaseEvent(e);
+}
+
+void DateEdit::mouseMoveEvent(QMouseEvent* e)
+{
+ if (!isReadOnly())
+ KDateEdit::mouseMoveEvent(e);
+}
+
+void DateEdit::keyPressEvent(QKeyEvent* e)
+{
+ if (!isReadOnly())
+ KDateEdit::keyPressEvent(e);
+}
+
+void DateEdit::keyReleaseEvent(QKeyEvent* e)
+{
+ if (!isReadOnly())
+ KDateEdit::keyReleaseEvent(e);
+}
diff --git a/kalarm/lib/dateedit.h b/kalarm/lib/dateedit.h
new file mode 100644
index 00000000..031a9674
--- /dev/null
+++ b/kalarm/lib/dateedit.h
@@ -0,0 +1,90 @@
+/*
+ * dateedit.h - date entry widget
+ * Program: kalarm
+ * Copyright © 2002-2007 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 DATEEDIT_H
+#define DATEEDIT_H
+
+#include <libkdepim/kdateedit.h>
+
+/**
+ * @short Date edit widget with range limits.
+ *
+ * The DateEdit class provides a date editor with the ability to set limits on the
+ * dates which can be entered.
+ *
+ * Minimum and/or maximum permissible dates may be set, together with corresponding
+ * error messages. If the user tries to enter a date outside the allowed range, the
+ * appropriate error message (if any) is output using KMessageBox::sorry().
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+ */
+class DateEdit : public KDateEdit
+{
+ Q_OBJECT
+ public:
+ /** Constructor.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ explicit DateEdit(QWidget* parent = 0, const char* name = 0);
+ /** Returns true if the widget contains a valid date. */
+ bool isValid() const { return date().isValid(); }
+ /** Returns the earliest date which can be entered.
+ * If there is no minimum date, returns an invalid date.
+ */
+ const QDate& minDate() const { return mMinDate; }
+ /** Returns the latest date which can be entered.
+ * If there is no maximum date, returns an invalid date.
+ */
+ const QDate& maxDate() const { return mMaxDate; }
+ /** Sets the earliest date which can be entered.
+ * @param date Earliest date allowed. If invalid, any minimum limit is removed.
+ * @param errorDate Error message to be displayed when a date earlier than
+ * @p date is entered. Set to QString::null to use the default error message.
+ */
+ void setMinDate(const QDate& date, const QString& errorDate = QString::null);
+ /** Sets the latest date which can be entered.
+ * @param date Latest date allowed. If invalid, any maximum limit is removed.
+ * @param errorDate Error message to be displayed when a date later than
+ * @p date is entered. Set to QString::null to use the default error message.
+ */
+ void setMaxDate(const QDate& date, const QString& errorDate = QString::null);
+ /** Sets the date held in the widget to an invalid date. */
+ void setInvalid();
+
+ protected:
+ virtual void mousePressEvent(QMouseEvent*);
+ virtual void mouseReleaseEvent(QMouseEvent*);
+ virtual void mouseMoveEvent(QMouseEvent*);
+ virtual void keyPressEvent(QKeyEvent*);
+ virtual void keyReleaseEvent(QKeyEvent*);
+
+ private slots:
+ void newDateEntered(const QDate&);
+
+ private:
+ void pastLimitMessage(const QDate& limit, const QString& error, const QString& defaultError);
+
+ QDate mMinDate; // minimum allowed date, or invalid for no minimum
+ QDate mMaxDate; // maximum allowed date, or invalid for no maximum
+ QString mMinDateErrString; // error message when entered date < mMinDate
+ QString mMaxDateErrString; // error message when entered date > mMaxDate
+};
+
+#endif // DATEEDIT_H
diff --git a/kalarm/lib/datetime.cpp b/kalarm/lib/datetime.cpp
new file mode 100644
index 00000000..d7f5ee86
--- /dev/null
+++ b/kalarm/lib/datetime.cpp
@@ -0,0 +1,80 @@
+/*
+ * datetime.cpp - date/time representation with optional date-only value
+ * Program: kalarm
+ * Copyright (C) 2003, 2005 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 "kalarm.h"
+
+#include <kglobal.h>
+#include <klocale.h>
+
+#include "datetime.h"
+
+QTime DateTime::mStartOfDay;
+
+QTime DateTime::time() const
+{
+ return mDateOnly ? mStartOfDay : mDateTime.time();
+}
+
+QDateTime DateTime::dateTime() const
+{
+ return mDateOnly ? QDateTime(mDateTime.date(), mStartOfDay) : mDateTime;
+}
+
+QString DateTime::formatLocale(bool shortFormat) const
+{
+ if (mDateOnly)
+ return KGlobal::locale()->formatDate(mDateTime.date(), shortFormat);
+ else if (mTimeValid)
+ return KGlobal::locale()->formatDateTime(mDateTime, shortFormat);
+ else
+ return QString::null;
+}
+
+bool operator==(const DateTime& dt1, const DateTime& dt2)
+{
+ if (dt1.mDateTime.date() != dt2.mDateTime.date())
+ return false;
+ if (dt1.mDateOnly && dt2.mDateOnly)
+ return true;
+ if (!dt1.mDateOnly && !dt2.mDateOnly)
+ {
+ bool valid1 = dt1.mTimeValid && dt1.mDateTime.time().isValid();
+ bool valid2 = dt2.mTimeValid && dt2.mDateTime.time().isValid();
+ if (!valid1 && !valid2)
+ return true;
+ if (!valid1 || !valid2)
+ return false;
+ return dt1.mDateTime.time() == dt2.mDateTime.time();
+ }
+ return (dt1.mDateOnly ? dt2.mDateTime.time() : dt1.mDateTime.time()) == DateTime::startOfDay();
+}
+
+bool operator<(const DateTime& dt1, const DateTime& dt2)
+{
+ if (dt1.mDateTime.date() != dt2.mDateTime.date())
+ return dt1.mDateTime.date() < dt2.mDateTime.date();
+ if (dt1.mDateOnly && dt2.mDateOnly)
+ return false;
+ if (!dt1.mDateOnly && !dt2.mDateOnly)
+ return dt1.mDateTime.time() < dt2.mDateTime.time();
+ QTime t = DateTime::startOfDay();
+ if (dt1.mDateOnly)
+ return t < dt2.mDateTime.time();
+ return dt1.mDateTime.time() < t;
+}
diff --git a/kalarm/lib/datetime.h b/kalarm/lib/datetime.h
new file mode 100644
index 00000000..3b083191
--- /dev/null
+++ b/kalarm/lib/datetime.h
@@ -0,0 +1,241 @@
+/*
+ * datetime.h - date/time representation with optional date-only value
+ * Program: kalarm
+ * Copyright © 2003,2005,2007 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 DATETIME_H
+#define DATETIME_H
+
+#include <qdatetime.h>
+
+
+/**
+ * @short A QDateTime with date-only option.
+ *
+ * The DateTime class holds a date, with or without a time.
+ *
+ * DateTime is very similar to the QDateTime class, with the additional option to
+ * hold a date-only value. This allows a single date-time representation to be used
+ * for both an event having a specific date and time, and an all-day event.
+ *
+ * The time assumed for date-only values is the start-of-day time set by setStartOfDay().
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+*/
+class DateTime
+{
+ public:
+ /** Default constructor.
+ * Constructs an invalid date-time.
+ */
+ DateTime() : mDateOnly(false), mTimeValid(false) { }
+ /** Constructor for a date-only value. */
+ DateTime(const QDate& d) : mDateTime(d), mDateOnly(true) { }
+ /** Constructor for a date-time value. */
+ DateTime(const QDate& d, const QTime& t)
+ : mDateTime(d, t), mDateOnly(false), mTimeValid(true) { }
+ /** Constructor for a date-time or date-only value.
+ * @param dt the date and time to use.
+ * @param dateOnly True to construct a date-only value; false to construct a date-time value.
+ */
+ DateTime(const QDateTime& dt, bool dateOnly = false)
+ : mDateTime(dt), mDateOnly(dateOnly), mTimeValid(true)
+ { if (dateOnly) mDateTime.setTime(QTime()); }
+ /** Assignment operator.
+ * Sets the value to a specified date-time or date-only value.
+ */
+ DateTime& operator=(const DateTime& dt)
+ { mDateTime = dt.mDateTime; mDateOnly = dt.mDateOnly; mTimeValid = dt.mTimeValid; return *this; }
+ /** Assignment operator.
+ * Sets the value to a specified date-time.
+ */
+ DateTime& operator=(const QDateTime& dt)
+ { mDateTime = dt; mDateOnly = false; mTimeValid = true; return *this; }
+ /** Assignment operator.
+ * Sets the value to a specified date-only value.
+ */
+ DateTime& operator=(const QDate& d)
+ { mDateTime.setDate(d); mDateTime.setTime(QTime()); mDateOnly = true; return *this; }
+ /** Returns true if the date is null and, if it is a date-time value, the time is also null. */
+ bool isNull() const { return mDateTime.date().isNull() && (mDateOnly || mDateTime.time().isNull()); }
+ /** Returns true if the date is valid and, if it is a date-time value, the time is also valid. */
+ bool isValid() const { return mDateTime.date().isValid() && (mDateOnly || mTimeValid && mDateTime.time().isValid()); }
+ /** Returns true if it is date-only value. */
+ bool isDateOnly() const { return mDateOnly; }
+ /** Sets the value to be either date-only or date-time.
+ * @param d True to set the value to be date-only; false to set it to a date-time value.
+ */
+ void setDateOnly(bool d) { if (d) mDateTime.setTime(QTime());
+ else if (mDateOnly) mTimeValid = false;
+ mDateOnly = d;
+ }
+ /** Returns the date part of the value. */
+ QDate date() const { return mDateTime.date(); }
+ /** Returns the time part of the value.
+ * If the value is date-only, the time returned is the start-of-day time set by setStartOfDay().
+ */
+ QTime time() const;
+ /** Returns the date and time of the value.
+ * If the value is date-only, the time part returned is equal to the start-of-day time set
+ * by setStartOfDay().
+ */
+ QDateTime dateTime() const;
+ /** Returns the date and time of the value.
+ * if the value is date-only, the time part returned is 00:00:00.
+ */
+ QDateTime rawDateTime() const { return mDateTime; }
+ /** Sets a date-time or date-only value.
+ * @param dt the date-time to use.
+ * @param dateOnly True to set a date-only value; false to set a date-time value.
+ */
+ void set(const QDateTime& dt, bool dateOnly = false)
+ {
+ mDateTime = dt;
+ mDateOnly = dateOnly;
+ if (dateOnly)
+ mDateTime.setTime(QTime());
+ mTimeValid = true;
+ }
+ /** Sets a date-time value. */
+ void set(const QDate& d, const QTime& t)
+ { mDateTime.setDate(d); mDateTime.setTime(t); mDateOnly = false; mTimeValid = true; }
+ /** Sets the time component of the value.
+ * The value is converted if necessary to be a date-time value.
+ */
+ void setTime(const QTime& t) { mDateTime.setTime(t); mDateOnly = false; mTimeValid = true; }
+ /** Sets the value to a specified date-time value.
+ * @param secs The time_t date-time value, expressed as the number of seconds elapsed
+ * since 1970-01-01 00:00:00 UTC.
+ */
+ void setTime_t(uint secs) { mDateTime.setTime_t(secs); mDateOnly = false; mTimeValid = true; }
+ /** Returns a DateTime value @p secs seconds later than the value of this object.
+ * If this object is date-only, @p secs is first rounded down to a whole number of days
+ * before adding the value.
+ */
+ DateTime addSecs(int n) const
+ {
+ if (mDateOnly)
+ return DateTime(mDateTime.date().addDays(n / (3600*24)), true);
+ else
+ return DateTime(mDateTime.addSecs(n), false);
+ }
+ /** Returns a DateTime value @p mins minutes later than the value of this object.
+ * If this object is date-only, @p mins is first rounded down to a whole number of days
+ * before adding the value.
+ */
+ DateTime addMins(int n) const
+ {
+ if (mDateOnly)
+ return DateTime(mDateTime.addDays(n / (60*24)), true);
+ else
+ return DateTime(mDateTime.addSecs(n * 60), false);
+ }
+ /** Returns a DateTime value @p n days later than the value of this object. */
+ DateTime addDays(int n) const { return DateTime(mDateTime.addDays(n), mDateOnly); }
+ /** Returns a DateTime value @p n months later than the value of this object. */
+ DateTime addMonths(int n) const { return DateTime(mDateTime.addMonths(n), mDateOnly); }
+ /** Returns a DateTime value @p n years later than the value of this object. */
+ DateTime addYears(int n) const { return DateTime(mDateTime.addYears(n), mDateOnly); }
+ /** Returns the number of days from this date or date-time to @p dt. */
+ int daysTo(const DateTime& dt) const
+ { return (mDateOnly || dt.mDateOnly) ? mDateTime.date().daysTo(dt.date()) : mDateTime.daysTo(dt.mDateTime); }
+ /** Returns the number of minutes from this date or date-time to @p dt.
+ * If either of the values is date-only, the result is calculated by simply
+ * taking the difference in dates and ignoring the times.
+ */
+ int minsTo(const DateTime& dt) const
+ { return (mDateOnly || dt.mDateOnly) ? mDateTime.date().daysTo(dt.date()) * 24*60 : mDateTime.secsTo(dt.mDateTime) / 60; }
+ /** Returns the number of seconds from this date or date-time to @p dt.
+ * If either of the values is date-only, the result is calculated by simply
+ * taking the difference in dates and ignoring the times.
+ */
+ int secsTo(const DateTime& dt) const
+ { return (mDateOnly || dt.mDateOnly) ? mDateTime.date().daysTo(dt.date()) * 24*3600 : mDateTime.secsTo(dt.mDateTime); }
+ /** Returns the value as a string.
+ * If it is a date-time, both time and date are included in the output.
+ * If it is date-only, only the date is included in the output.
+ */
+ QString toString(Qt::DateFormat f = Qt::TextDate) const
+ {
+ if (mDateOnly)
+ return mDateTime.date().toString(f);
+ else if (mTimeValid)
+ return mDateTime.toString(f);
+ else
+ return QString::null;
+ }
+ /** Returns the value as a string.
+ * If it is a date-time, both time and date are included in the output.
+ * If it is date-only, only the date is included in the output.
+ */
+ QString toString(const QString& format) const
+ {
+ if (mDateOnly)
+ return mDateTime.date().toString(format);
+ else if (mTimeValid)
+ return mDateTime.toString(format);
+ else
+ return QString::null;
+ }
+ /** Returns the value as a string, formatted according to the user's locale.
+ * If it is a date-time, both time and date are included in the output.
+ * If it is date-only, only the date is included in the output.
+ */
+ QString formatLocale(bool shortFormat = true) const;
+ /** Sets the start-of-day time.
+ * The default value is midnight (0000 hrs).
+ */
+ static void setStartOfDay(const QTime& sod) { mStartOfDay = sod; }
+ /** Returns the start-of-day time. */
+ static QTime startOfDay() { return mStartOfDay; }
+
+ friend bool operator==(const DateTime& dt1, const DateTime& dt2);
+ friend bool operator<(const DateTime& dt1, const DateTime& dt2);
+
+ private:
+ static QTime mStartOfDay;
+ QDateTime mDateTime;
+ bool mDateOnly;
+ bool mTimeValid; // whether the time is potentially valid - applicable only if mDateOnly false
+};
+
+/** Returns true if the two values are equal. */
+bool operator==(const DateTime& dt1, const DateTime& dt2);
+/** Returns true if the @p dt1 is earlier than @p dt2.
+ * If the two values have the same date, and one value is date-only while the other is a date-time, the
+ * time used for the date-only value is the start-of-day time set in the KAlarm Preferences dialogue.
+ */
+bool operator<(const DateTime& dt1, const DateTime& dt2);
+/** Returns true if the two values are not equal. */
+inline bool operator!=(const DateTime& dt1, const DateTime& dt2) { return !operator==(dt1, dt2); }
+/** Returns true if the @p dt1 is later than @p dt2.
+ * If the two values have the same date, and one value is date-only while the other is a date-time, the
+ * time used for the date-only value is the start-of-day time set in the KAlarm Preferences dialogue.
+ */
+inline bool operator>(const DateTime& dt1, const DateTime& dt2) { return operator<(dt2, dt1); }
+/** Returns true if the @p dt1 is later than or equal to @p dt2.
+ * If the two values have the same date, and one value is date-only while the other is a date-time, the
+ * time used for the date-only value is the start-of-day time set in the KAlarm Preferences dialogue.
+ */
+inline bool operator>=(const DateTime& dt1, const DateTime& dt2) { return !operator<(dt1, dt2); }
+/** Returns true if the @p dt1 is earlier than or equal to @p dt2.
+ * If the two values have the same date, and one value is date-only while the other is a date-time, the
+ * time used for the date-only value is the start-of-day time set in the KAlarm Preferences dialogue.
+ */
+inline bool operator<=(const DateTime& dt1, const DateTime& dt2) { return !operator<(dt2, dt1); }
+
+#endif // DATETIME_H
diff --git a/kalarm/lib/label.cpp b/kalarm/lib/label.cpp
new file mode 100644
index 00000000..c61ce76a
--- /dev/null
+++ b/kalarm/lib/label.cpp
@@ -0,0 +1,118 @@
+/*
+ * label.cpp - label with radiobutton buddy option
+ * Program: kalarm
+ * Copyright (C) 2004 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 "kalarm.h"
+#include <qradiobutton.h>
+#include "label.moc"
+
+
+Label::Label(QWidget* parent, const char* name, WFlags f)
+ : QLabel(parent, name, f),
+ mRadioButton(0),
+ mFocusWidget(0)
+{ }
+
+Label::Label(const QString& text, QWidget* parent, const char* name, WFlags f)
+ : QLabel(text, parent, name, f),
+ mRadioButton(0),
+ mFocusWidget(0)
+{ }
+
+Label::Label(QWidget* buddy, const QString& text, QWidget* parent, const char* name, WFlags f)
+ : QLabel(buddy, text, parent, name, f),
+ mRadioButton(0),
+ mFocusWidget(0)
+{ }
+
+/******************************************************************************
+* Set a buddy widget.
+* If it (or its focus proxy) is a radio button, create a focus widget.
+* When the accelerator key is pressed, the focus widget then receives focus.
+* That event triggers the selection of the radio button.
+*/
+void Label::setBuddy(QWidget* bud)
+{
+ if (mRadioButton)
+ disconnect(mRadioButton, SIGNAL(destroyed()), this, SLOT(buddyDead()));
+ QWidget* w = bud;
+ if (w)
+ {
+ while (w->focusProxy())
+ w = w->focusProxy();
+ if (!w->inherits("QRadioButton"))
+ w = 0;
+ }
+ if (!w)
+ {
+ // The buddy widget isn't a radio button
+ QLabel::setBuddy(bud);
+ delete mFocusWidget;
+ mFocusWidget = 0;
+ mRadioButton = 0;
+ }
+ else
+ {
+ // The buddy widget is a radio button, so set a different buddy
+ if (!mFocusWidget)
+ mFocusWidget = new LabelFocusWidget(this);
+ QLabel::setBuddy(mFocusWidget);
+ mRadioButton = (QRadioButton*)bud;
+ connect(mRadioButton, SIGNAL(destroyed()), this, SLOT(buddyDead()));
+ }
+}
+
+void Label::buddyDead()
+{
+ delete mFocusWidget;
+ mFocusWidget = 0;
+ mRadioButton = 0;
+}
+
+/******************************************************************************
+* Called when focus is transferred to the label's special focus widget.
+* Transfer focus to the radio button and select it.
+*/
+void Label::activated()
+{
+ if (mFocusWidget && mRadioButton)
+ {
+ mRadioButton->setFocus();
+ mRadioButton->setChecked(true);
+ }
+}
+
+
+/*=============================================================================
+* Class: LabelFocusWidget
+=============================================================================*/
+
+LabelFocusWidget::LabelFocusWidget(QWidget* parent, const char* name)
+ : QWidget(parent, name)
+{
+ setFocusPolicy(ClickFocus);
+ setFixedSize(QSize(1,1));
+}
+
+void LabelFocusWidget::focusInEvent(QFocusEvent*)
+{
+ Label* parent = (Label*)parentWidget();
+ parent->activated();
+
+}
diff --git a/kalarm/lib/label.h b/kalarm/lib/label.h
new file mode 100644
index 00000000..c65a7fcd
--- /dev/null
+++ b/kalarm/lib/label.h
@@ -0,0 +1,96 @@
+/*
+ * label.h - label with radiobutton buddy option
+ * Program: kalarm
+ * Copyright © 2004-2006 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 LABEL_H
+#define LABEL_H
+
+#include <qlabel.h>
+class QRadioButton;
+class LabelFocusWidget;
+
+/**
+ * @short A QLabel with option for a buddy radio button.
+ *
+ * The Label class provides a text display, with special behaviour when a radio
+ * button is set as a buddy.
+ *
+ * The Label object in effect acts as if it were part of the buddy radio button,
+ * in that when the label's accelerator key is pressed, the radio button receives
+ * focus and is switched on. When a non-radio button is specified as a buddy, the
+ * behaviour is the same as for QLabel.
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+ */
+class Label : public QLabel
+{
+ Q_OBJECT
+ friend class LabelFocusWidget;
+ public:
+ /** Constructs an empty label.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ * @param f Flags. See QWidget constructor for details.
+ */
+ explicit Label(QWidget* parent, const char* name = 0, WFlags f = 0);
+ /** Constructs a label that displays @p text.
+ * @param text Text string to display.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ * @param f Flags. See QWidget constructor for details.
+ */
+ Label(const QString& text, QWidget* parent, const char* name = 0, WFlags f = 0);
+ /** Constructs a label, with a buddy widget, that displays @p text.
+ * @param buddy Buddy widget which receives the keyboard focus when the
+ * label's accelerator key is pressed. If @p buddy is a radio
+ * button, @p buddy is in addition selected when the
+ * accelerator key is pressed.
+ * @param text Text string to display.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ * @param f Flags. See QWidget constructor for details.
+ */
+ Label(QWidget* buddy, const QString& text, QWidget* parent, const char* name = 0, WFlags f = 0);
+ /** Sets the label's buddy widget which receives the keyboard focus when the
+ * label's accelerator key is pressed. If @p buddy is a radio button,
+ * @p buddy is in addition selected when the accelerator key is pressed.
+ */
+ virtual void setBuddy(QWidget* buddy);
+ protected:
+ virtual void drawContents(QPainter* p) { QLabel::drawContents(p); }
+ private slots:
+ void buddyDead();
+ private:
+ void activated();
+ QRadioButton* mRadioButton; // buddy widget if it's a radio button, else 0
+ LabelFocusWidget* mFocusWidget;
+};
+
+
+// Private class for use by Label
+class LabelFocusWidget : public QWidget
+{
+ Q_OBJECT
+ public:
+ LabelFocusWidget(QWidget* parent, const char* name = 0);
+ protected:
+ virtual void focusInEvent(QFocusEvent*);
+};
+
+#endif // LABEL_H
diff --git a/kalarm/lib/lineedit.cpp b/kalarm/lib/lineedit.cpp
new file mode 100644
index 00000000..943d7b2d
--- /dev/null
+++ b/kalarm/lib/lineedit.cpp
@@ -0,0 +1,200 @@
+/*
+ * lineedit.cpp - Line edit widget with extra drag and drop options
+ * Program: kalarm
+ * Copyright (C) 2003 - 2005 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 "kalarm.h"
+
+#include <qregexp.h>
+#include <qdragobject.h>
+
+#include <kurldrag.h>
+#include <kurlcompletion.h>
+
+#include <libkdepim/maillistdrag.h>
+#include <libkdepim/kvcarddrag.h>
+#include <libkcal/icaldrag.h>
+
+#include "lineedit.moc"
+
+
+/*=============================================================================
+= Class LineEdit
+= Line edit which accepts drag and drop of text, URLs and/or email addresses.
+* It has an option to prevent its contents being selected when it receives
+= focus.
+=============================================================================*/
+LineEdit::LineEdit(Type type, QWidget* parent, const char* name)
+ : KLineEdit(parent, name),
+ mType(type),
+ mNoSelect(false),
+ mSetCursorAtEnd(false)
+{
+ init();
+}
+
+LineEdit::LineEdit(QWidget* parent, const char* name)
+ : KLineEdit(parent, name),
+ mType(Text),
+ mNoSelect(false),
+ mSetCursorAtEnd(false)
+{
+ init();
+}
+
+void LineEdit::init()
+{
+ if (mType == Url)
+ {
+ setCompletionMode(KGlobalSettings::CompletionShell);
+ KURLCompletion* comp = new KURLCompletion(KURLCompletion::FileCompletion);
+ comp->setReplaceHome(true);
+ setCompletionObject(comp);
+ setAutoDeleteCompletionObject(true);
+ }
+ else
+ setCompletionMode(KGlobalSettings::CompletionNone);
+}
+
+/******************************************************************************
+* Called when the line edit receives focus.
+* If 'noSelect' is true, prevent the contents being selected.
+*/
+void LineEdit::focusInEvent(QFocusEvent* e)
+{
+ if (mNoSelect)
+ QFocusEvent::setReason(QFocusEvent::Other);
+ KLineEdit::focusInEvent(e);
+ if (mNoSelect)
+ {
+ QFocusEvent::resetReason();
+ mNoSelect = false;
+ }
+}
+
+void LineEdit::setText(const QString& text)
+{
+ KLineEdit::setText(text);
+ setCursorPosition(mSetCursorAtEnd ? text.length() : 0);
+}
+
+void LineEdit::dragEnterEvent(QDragEnterEvent* e)
+{
+ if (KCal::ICalDrag::canDecode(e))
+ e->accept(false); // don't accept "text/calendar" objects
+ e->accept(QTextDrag::canDecode(e)
+ || KURLDrag::canDecode(e)
+ || mType != Url && KPIM::MailListDrag::canDecode(e)
+ || mType == Emails && KVCardDrag::canDecode(e));
+}
+
+void LineEdit::dropEvent(QDropEvent* e)
+{
+ QString newText;
+ QStringList newEmails;
+ QString txt;
+ KPIM::MailList mailList;
+ KURL::List files;
+ KABC::Addressee::List addrList;
+
+ if (mType != Url
+ && e->provides(KPIM::MailListDrag::format())
+ && KPIM::MailListDrag::decode(e, mailList))
+ {
+ // KMail message(s) - ignore all but the first
+ if (mailList.count())
+ {
+ if (mType == Emails)
+ newText = mailList.first().from();
+ else
+ setText(mailList.first().subject()); // replace any existing text
+ }
+ }
+ // This must come before KURLDrag
+ else if (mType == Emails
+ && KVCardDrag::canDecode(e) && KVCardDrag::decode(e, addrList))
+ {
+ // KAddressBook entries
+ for (KABC::Addressee::List::Iterator it = addrList.begin(); it != addrList.end(); ++it)
+ {
+ QString em((*it).fullEmail());
+ if (!em.isEmpty())
+ newEmails.append(em);
+ }
+ }
+ else if (KURLDrag::decode(e, files) && files.count())
+ {
+ // URL(s)
+ switch (mType)
+ {
+ case Url:
+ // URL entry field - ignore all but the first dropped URL
+ setText(files.first().prettyURL()); // replace any existing text
+ break;
+ case Emails:
+ {
+ // Email entry field - ignore all but mailto: URLs
+ QString mailto = QString::fromLatin1("mailto");
+ for (KURL::List::Iterator it = files.begin(); it != files.end(); ++it)
+ {
+ if ((*it).protocol() == mailto)
+ newEmails.append((*it).path());
+ }
+ break;
+ }
+ case Text:
+ newText = files.first().prettyURL();
+ break;
+ }
+ }
+ else if (QTextDrag::decode(e, txt))
+ {
+ // Plain text
+ if (mType == Emails)
+ {
+ // Remove newlines from a list of email addresses, and allow an eventual mailto: protocol
+ QString mailto = QString::fromLatin1("mailto:");
+ newEmails = QStringList::split(QRegExp("[\r\n]+"), txt);
+ for (QStringList::Iterator it = newEmails.begin(); it != newEmails.end(); ++it)
+ {
+ if ((*it).startsWith(mailto))
+ {
+ KURL url(*it);
+ *it = url.path();
+ }
+ }
+ }
+ else
+ {
+ int newline = txt.find('\n');
+ newText = (newline >= 0) ? txt.left(newline) : txt;
+ }
+ }
+
+ if (newEmails.count())
+ {
+ newText = newEmails.join(",");
+ int c = cursorPosition();
+ if (c > 0)
+ newText.prepend(",");
+ if (c < static_cast<int>(text().length()))
+ newText.append(",");
+ }
+ if (!newText.isEmpty())
+ insert(newText);
+}
diff --git a/kalarm/lib/lineedit.h b/kalarm/lib/lineedit.h
new file mode 100644
index 00000000..57f8378e
--- /dev/null
+++ b/kalarm/lib/lineedit.h
@@ -0,0 +1,94 @@
+/*
+ * lineedit.h - line edit widget with extra drag and drop options
+ * Program: kalarm
+ * Copyright (C) 2003 - 2005 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 LINEEDIT_H
+#define LINEEDIT_H
+
+#include <klineedit.h>
+
+
+/**
+ * @short Line edit widget with extra drag and drop options.
+ *
+ * The LineEdit class is a line edit widget which accepts specified types of drag and
+ * drop content.
+ *
+ * The widget will always accept drag and drop of text, except text/calendar mime type,
+ * and of URLs. It will accept additional mime types depending on its configuration:
+ * Text type accepts email address lists.
+ * Email type accepts email address lists and VCard data (e.g. from KAddressBook).
+ *
+ * The class also provides an option to prevent its contents being selected when the
+ * widget receives focus.
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+ */
+class LineEdit : public KLineEdit
+{
+ Q_OBJECT
+ public:
+ /** Types of drag and drop content which will be accepted.
+ * @li Text - the line edit contains general text. It accepts text, a URL
+ * or an email from KMail (the subject line is used). If multiple
+ * URLs or emails are dropped, only the first is used; the
+ * rest are ignored.
+ * @li Url - the line edit contains a URL. It accepts text or a URL. If
+ * multiple URLs are dropped, only the first URL is used; the
+ * rest are ignored.
+ * @li Emails - the line edit contains email addresses. It accepts text,
+ * mailto: URLs, emails from KMail (the From address is used)
+ * or vcard data (e.g. from KAddressBook). If multiple emails
+ * are dropped, only the first is used; the rest are ignored.
+ *
+ */
+ enum Type { Text, Url, Emails };
+ /** Constructor.
+ * @param type The content type for the line edit.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ explicit LineEdit(Type type, QWidget* parent = 0, const char* name = 0);
+ /** Constructs a line edit whose content type is Text.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ explicit LineEdit(QWidget* parent = 0, const char* name = 0);
+ /** Prevents the line edit's contents being selected when the widget receives focus. */
+ void setNoSelect() { mNoSelect = true; }
+ /** Sets whether the cursor should be set at the beginning or end of the text when
+ * setText() is called.
+ */
+ void setCursorAtEnd(bool end = true) { mSetCursorAtEnd = end; }
+ public slots:
+ /** Sets the contents of the line edit to be @p str. */
+ virtual void setText(const QString& str);
+ protected:
+ virtual void focusInEvent(QFocusEvent*);
+ virtual void dragEnterEvent(QDragEnterEvent*);
+ virtual void dropEvent(QDropEvent*);
+ private:
+ void init();
+
+ Type mType;
+ bool mNoSelect;
+ bool mSetCursorAtEnd; // setText() should position cursor at end
+};
+
+#endif // LINEEDIT_H
diff --git a/kalarm/lib/messagebox.cpp b/kalarm/lib/messagebox.cpp
new file mode 100644
index 00000000..514b45bc
--- /dev/null
+++ b/kalarm/lib/messagebox.cpp
@@ -0,0 +1,178 @@
+/*
+ * messagebox.cpp - enhanced KMessageBox class
+ * Program: kalarm
+ * Copyright (C) 2004 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 "kalarm.h"
+#include <kconfig.h>
+#include "messagebox.h"
+
+
+KConfig* MessageBox::mConfig = 0;
+QMap<QString, KMessageBox::ButtonCode> MessageBox::mContinueDefaults;
+
+
+/******************************************************************************
+* Set the default button for continue/cancel message boxes with the specified
+* 'dontAskAgainName'.
+*/
+void MessageBox::setContinueDefault(const QString& dontAskAgainName, ButtonCode defaultButton)
+{
+ mContinueDefaults[dontAskAgainName] = (defaultButton == Cancel ? Cancel : Continue);
+}
+
+/******************************************************************************
+* Get the default button for continue/cancel message boxes with the specified
+* 'dontAskAgainName'.
+*/
+KMessageBox::ButtonCode MessageBox::getContinueDefault(const QString& dontAskAgainName)
+{
+ ButtonCode defaultButton = Continue;
+ if (!dontAskAgainName.isEmpty())
+ {
+ QMap<QString, ButtonCode>::ConstIterator it = mContinueDefaults.find(dontAskAgainName);
+ if (it != mContinueDefaults.end())
+ defaultButton = it.data();
+ }
+ return defaultButton;
+}
+
+/******************************************************************************
+* Continue/cancel message box.
+* If 'dontAskAgainName' is specified:
+* 1) The message box will only be suppressed if the user chose Continue last time.
+* 2) The default button is that last set with either setContinueDefault() or
+* warningContinueCancel() for that 'dontAskAgainName' value. If neither method
+* has set a default button, Continue is the default.
+*/
+int MessageBox::warningContinueCancel(QWidget* parent, const QString& text, const QString& caption,
+ const KGuiItem& buttonContinue, const QString& dontAskAgainName)
+{
+ ButtonCode defaultButton = getContinueDefault(dontAskAgainName);
+ return warningContinueCancel(parent, defaultButton, text, caption, buttonContinue, dontAskAgainName);
+}
+
+/******************************************************************************
+* Continue/cancel message box with the option as to which button is the default.
+* If 'dontAskAgainName' is specified, the message box will only be suppressed
+* if the user chose Continue last time.
+*/
+int MessageBox::warningContinueCancel(QWidget* parent, ButtonCode defaultButton, const QString& text,
+ const QString& caption, const KGuiItem& buttonContinue,
+ const QString& dontAskAgainName)
+{
+ setContinueDefault(dontAskAgainName, defaultButton);
+ if (defaultButton != Cancel)
+ return KMessageBox::warningContinueCancel(parent, text, caption, buttonContinue, dontAskAgainName);
+
+ // Cancel is the default button, so we have to use KMessageBox::warningYesNo()
+ if (!dontAskAgainName.isEmpty())
+ {
+ ButtonCode b;
+ if (!shouldBeShownYesNo(dontAskAgainName, b)
+ && b != KMessageBox::Yes)
+ {
+ // Notification has been suppressed, but No (alias Cancel) is the default,
+ // so unsuppress notification.
+ saveDontShowAgain(dontAskAgainName, true, false);
+ }
+ }
+ return warningYesNo(parent, text, caption, buttonContinue, KStdGuiItem::cancel(), dontAskAgainName);
+}
+
+/******************************************************************************
+* If there is no current setting for whether a non-yes/no message box should be
+* shown, set it to 'defaultShow'.
+* If a continue/cancel message box has Cancel as the default button, either
+* setContinueDefault() or warningContinueCancel() must have been called
+* previously to set this for this 'dontShowAgainName' value.
+* Reply = true if 'defaultShow' was written.
+*/
+bool MessageBox::setDefaultShouldBeShownContinue(const QString& dontShowAgainName, bool defaultShow)
+{
+ if (dontShowAgainName.isEmpty())
+ return false;
+ // First check whether there is an existing setting
+ KConfig* config = mConfig ? mConfig : KGlobal::config();
+ config->setGroup(QString::fromLatin1("Notification Messages"));
+ if (config->hasKey(dontShowAgainName))
+ return false;
+
+ // There is no current setting, so write one
+ saveDontShowAgainContinue(dontShowAgainName, !defaultShow);
+ return true;
+}
+
+/******************************************************************************
+* Return whether a non-yes/no message box should be shown.
+* If the message box has Cancel as the default button, either setContinueDefault()
+* or warningContinueCancel() must have been called previously to set this for this
+* 'dontShowAgainName' value.
+*/
+bool MessageBox::shouldBeShownContinue(const QString& dontShowAgainName)
+{
+ if (getContinueDefault(dontShowAgainName) != Cancel)
+ return KMessageBox::shouldBeShownContinue(dontShowAgainName);
+ // Cancel is the default button, so we have to use a yes/no message box
+ ButtonCode b;
+ return shouldBeShownYesNo(dontShowAgainName, b);
+}
+
+
+/******************************************************************************
+* Save whether the yes/no message box should not be shown again.
+* If 'dontShow' is true, the message box will be suppressed and it will return
+* 'result'.
+*/
+void MessageBox::saveDontShowAgainYesNo(const QString& dontShowAgainName, bool dontShow, ButtonCode result)
+{
+ saveDontShowAgain(dontShowAgainName, true, dontShow, (result == Yes ? "yes" : "no"));
+}
+
+/******************************************************************************
+* Save whether a non-yes/no message box should not be shown again.
+* If 'dontShow' is true, the message box will be suppressed and it will return
+* Continue.
+* If the message box has Cancel as the default button, either setContinueDefault()
+* or warningContinueCancel() must have been called previously to set this for this
+* 'dontShowAgainName' value.
+*/
+void MessageBox::saveDontShowAgainContinue(const QString& dontShowAgainName, bool dontShow)
+{
+ if (getContinueDefault(dontShowAgainName) == Cancel)
+ saveDontShowAgainYesNo(dontShowAgainName, dontShow, Yes);
+ else
+ saveDontShowAgain(dontShowAgainName, false, dontShow);
+}
+
+/******************************************************************************
+* Save whether the message box should not be shown again.
+*/
+void MessageBox::saveDontShowAgain(const QString& dontShowAgainName, bool yesno, bool dontShow, const char* yesnoResult)
+{
+ if (dontShowAgainName.isEmpty())
+ return;
+ KConfig* config = mConfig ? mConfig : KGlobal::config();
+ config->setGroup(QString::fromLatin1("Notification Messages"));
+ bool global = (dontShowAgainName[0] == ':');
+ if (yesno)
+ config->writeEntry(dontShowAgainName, QString::fromLatin1(dontShow ? yesnoResult : ""), true, global);
+ else
+ config->writeEntry(dontShowAgainName, !dontShow, true, global);
+ config->sync();
+}
diff --git a/kalarm/lib/messagebox.h b/kalarm/lib/messagebox.h
new file mode 100644
index 00000000..32e0d732
--- /dev/null
+++ b/kalarm/lib/messagebox.h
@@ -0,0 +1,125 @@
+/*
+ * messagebox.h - enhanced KMessageBox class
+ * Program: kalarm
+ * Copyright (C) 2004 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 MESSAGEBOX_H
+#define MESSAGEBOX_H
+
+#include <kstdguiitem.h>
+#include <kmessagebox.h>
+
+
+/**
+ * @short Enhanced KMessageBox.
+ *
+ * The MessageBox class provides an extension to KMessageBox, including the option for
+ * Continue/Cancel message boxes to have a default button of Cancel.
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+ */
+class MessageBox : public KMessageBox
+{
+ public:
+ /** MessageBox types.
+ * @li CONT_CANCEL_DEF_CONT - Continue/Cancel, with Continue as the default button.
+ * @li CONT_CANCEL_DEF_CANCEL - Continue/Cancel, with Cancel as the default button.
+ * @li YES_NO_DEF_NO - Yes/No, with No as the default button.
+ */
+ enum AskType { // MessageBox types
+ CONT_CANCEL_DEF_CONT, // Continue/Cancel, with default = Continue
+ CONT_CANCEL_DEF_CANCEL, // Continue/Cancel, with default = Cancel
+ YES_NO_DEF_NO // Yes/No, with default = No
+ };
+ /** Gets the default button for the Continue/Cancel message box with the specified
+ * "don't ask again" name.
+ * @param dontAskAgainName The identifier controlling whether the message box is suppressed.
+ */
+ static ButtonCode getContinueDefault(const QString& dontAskAgainName);
+ /** Sets the default button for the Continue/Cancel message box with the specified
+ * "don't ask again" name.
+ * @param dontAskAgainName The identifier controlling whether the message box is suppressed.
+ * @param defaultButton The default button for the message box. Valid values are Continue or Cancel.
+ */
+ static void setContinueDefault(const QString& dontAskAgainName, ButtonCode defaultButton);
+ /** Displays a Continue/Cancel message box with the option as to which button is the default.
+ * @param parent Parent widget
+ * @param defaultButton The default button for the message box. Valid values are Continue or Cancel.
+ * @param text Message string
+ * @param caption Caption (window title) of the message box
+ * @param buttonContinue The text for the first button (default = i18n("Continue"))
+ * @param dontAskAgainName If specified, the message box will only be suppressed
+ * if the user chose Continue last time.
+ */
+ static int warningContinueCancel(QWidget* parent, ButtonCode defaultButton, const QString& text,
+ const QString& caption = QString::null,
+ const KGuiItem& buttonContinue = KStdGuiItem::cont(),
+ const QString& dontAskAgainName = QString::null);
+ /** Displays a Continue/Cancel message box.
+ * @param parent Parent widget
+ * @param text Message string
+ * @param caption Caption (window title) of the message box
+ * @param buttonContinue The text for the first button (default = i18n("Continue"))
+ * @param dontAskAgainName If specified, (1) The message box will only be suppressed
+ * if the user chose Continue last time, and (2) The default button is that last set
+ * with either setContinueDefault() or warningContinueCancel() for the same
+ * @p dontAskAgainName value. If neither method has been used to set a default button,
+ * Continue is the default.
+ */
+ static int warningContinueCancel(QWidget* parent, const QString& text, const QString& caption = QString::null,
+ const KGuiItem& buttonContinue = KStdGuiItem::cont(),
+ const QString& dontAskAgainName = QString::null);
+ /** If there is no current setting for whether a non-Yes/No message box should be
+ * shown, sets it to @p defaultShow.
+ * If a Continue/Cancel message box has Cancel as the default button, either
+ * setContinueDefault() or warningContinueCancel() must have been called
+ * previously to set this for the specified @p dontShowAgainName value.
+ * @return true if @p defaultShow was written.
+ */
+ static bool setDefaultShouldBeShownContinue(const QString& dontShowAgainName, bool defaultShow);
+ /** Returns whether a non-Yes/No message box should be shown.
+ * If the message box has Cancel as the default button, either setContinueDefault()
+ * or warningContinueCancel() must have been called previously to set this for the
+ * specified @p dontShowAgainName value.
+ * @param dontShowAgainName The identifier controlling whether the message box is suppressed.
+ */
+ static bool shouldBeShownContinue(const QString& dontShowAgainName);
+ /** Stores whether the Yes/No message box should or should not be shown again.
+ * @param dontShowAgainName The identifier controlling whether the message box is suppressed.
+ * @param dontShow If true, the message box will be suppressed and will return @p result.
+ * @param result The button code to return if the message box is suppressed.
+ */
+ static void saveDontShowAgainYesNo(const QString& dontShowAgainName, bool dontShow = true, ButtonCode result = No);
+ /** Stores whether a non-Yes/No message box should or should not be shown again.
+ * If the message box has Cancel as the default button, either setContinueDefault()
+ * or warningContinueCancel() must have been called previously to set this for the
+ * specified @p dontShowAgainName value.
+ * @param dontShowAgainName The identifier controlling whether the message box is suppressed.
+ * @param dontShow If true, the message box will be suppressed and will return Continue.
+ */
+ static void saveDontShowAgainContinue(const QString& dontShowAgainName, bool dontShow = true);
+ /** Sets the KConfig object to be used by the MessageBox class. */
+ static void setDontShowAskAgainConfig(KConfig* cfg) { mConfig = cfg; }
+
+ private:
+ static void saveDontShowAgain(const QString& dontShowAgainName, bool yesno, bool dontShow, const char* yesnoResult = 0);
+ static KConfig* mConfig;
+ static QMap<QString, ButtonCode> mContinueDefaults;
+};
+
+#endif
diff --git a/kalarm/lib/pushbutton.cpp b/kalarm/lib/pushbutton.cpp
new file mode 100644
index 00000000..8b279082
--- /dev/null
+++ b/kalarm/lib/pushbutton.cpp
@@ -0,0 +1,102 @@
+/*
+ * pushbutton.cpp - push button with read-only option
+ * Program: kalarm
+ * Copyright (c) 2002 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 "pushbutton.moc"
+
+
+PushButton::PushButton(QWidget* parent, const char* name)
+ : QPushButton(parent, name),
+ mFocusPolicy(focusPolicy()),
+ mReadOnly(false)
+{ }
+
+PushButton::PushButton(const QString& text, QWidget* parent, const char* name)
+ : QPushButton(text, parent, name),
+ mFocusPolicy(focusPolicy()),
+ mReadOnly(false)
+{ }
+
+PushButton::PushButton(const QIconSet& icon, const QString& text, QWidget* parent, const char* name)
+ : QPushButton(icon, text, parent, name),
+ mFocusPolicy(focusPolicy()),
+ mReadOnly(false)
+{ }
+
+void PushButton::setReadOnly(bool ro)
+{
+ if ((int)ro != (int)mReadOnly)
+ {
+ mReadOnly = ro;
+ setFocusPolicy(ro ? QWidget::NoFocus : mFocusPolicy);
+ if (ro)
+ clearFocus();
+ }
+}
+
+void PushButton::mousePressEvent(QMouseEvent* e)
+{
+ if (mReadOnly)
+ {
+ // Swallow up the event if it's the left button
+ if (e->button() == Qt::LeftButton)
+ return;
+ }
+ QPushButton::mousePressEvent(e);
+}
+
+void PushButton::mouseReleaseEvent(QMouseEvent* e)
+{
+ if (mReadOnly)
+ {
+ // Swallow up the event if it's the left button
+ if (e->button() == Qt::LeftButton)
+ return;
+ }
+ QPushButton::mouseReleaseEvent(e);
+}
+
+void PushButton::mouseMoveEvent(QMouseEvent* e)
+{
+ if (!mReadOnly)
+ QPushButton::mouseMoveEvent(e);
+}
+
+void PushButton::keyPressEvent(QKeyEvent* e)
+{
+ if (mReadOnly)
+ switch (e->key())
+ {
+ case Qt::Key_Up:
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ case Qt::Key_Down:
+ // Process keys which shift the focus
+ break;
+ default:
+ return;
+ }
+ QPushButton::keyPressEvent(e);
+}
+
+void PushButton::keyReleaseEvent(QKeyEvent* e)
+{
+ if (!mReadOnly)
+ QPushButton::keyReleaseEvent(e);
+}
diff --git a/kalarm/lib/pushbutton.h b/kalarm/lib/pushbutton.h
new file mode 100644
index 00000000..6ef0f3a2
--- /dev/null
+++ b/kalarm/lib/pushbutton.h
@@ -0,0 +1,77 @@
+/*
+ * pushbutton.h - push button with read-only option
+ * Program: kalarm
+ * Copyright © 2002,2003,2005,2006 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 PUSHBUTTON_H
+#define PUSHBUTTON_H
+
+#include <qpushbutton.h>
+
+
+/**
+ * @short A QPushButton with read-only option.
+ *
+ * The PushButton class is a QPushButton with a read-only option.
+ *
+ * The widget may be set as read-only. This has the same effect as disabling it, except
+ * that its appearance is unchanged.
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+ */
+class PushButton : public QPushButton
+{
+ Q_OBJECT
+ Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly)
+ public:
+ /** Constructor.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ explicit PushButton(QWidget* parent, const char* name = 0);
+ /** Constructor for a push button which displays a text.
+ * @param text The text to show on the button.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ PushButton(const QString& text, QWidget* parent, const char* name = 0);
+ /** Constructor for a push button which displays an icon and a text.
+ * @param icon The icon to show on the button.
+ * @param text The text to show on the button.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ PushButton(const QIconSet& icon, const QString& text, QWidget* parent, const char* name = 0);
+ /** Sets whether the push button is read-only for the user.
+ * @param readOnly True to set the widget read-only, false to enable its action.
+ */
+ virtual void setReadOnly(bool readOnly);
+ /** Returns true if the widget is read only. */
+ virtual bool isReadOnly() const { return mReadOnly; }
+ protected:
+ virtual void mousePressEvent(QMouseEvent*);
+ virtual void mouseReleaseEvent(QMouseEvent*);
+ virtual void mouseMoveEvent(QMouseEvent*);
+ virtual void keyPressEvent(QKeyEvent*);
+ virtual void keyReleaseEvent(QKeyEvent*);
+ private:
+ QWidget::FocusPolicy mFocusPolicy; // default focus policy for the QPushButton
+ bool mReadOnly; // value cannot be changed
+};
+
+#endif // PUSHBUTTON_H
diff --git a/kalarm/lib/radiobutton.cpp b/kalarm/lib/radiobutton.cpp
new file mode 100644
index 00000000..d66911ee
--- /dev/null
+++ b/kalarm/lib/radiobutton.cpp
@@ -0,0 +1,134 @@
+/*
+ * radiobutton.cpp - radio button with read-only option
+ * Program: kalarm
+ * Copyright (c) 2002, 2003 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 "radiobutton.moc"
+
+
+RadioButton::RadioButton(QWidget* parent, const char* name)
+ : QRadioButton(parent, name),
+ mFocusPolicy(focusPolicy()),
+ mFocusWidget(0),
+ mReadOnly(false)
+{ }
+
+RadioButton::RadioButton(const QString& text, QWidget* parent, const char* name)
+ : QRadioButton(text, parent, name),
+ mFocusPolicy(focusPolicy()),
+ mFocusWidget(0),
+ mReadOnly(false)
+{ }
+
+/******************************************************************************
+* Set the read-only status. If read-only, the button can be toggled by the
+* application, but not by the user.
+*/
+void RadioButton::setReadOnly(bool ro)
+{
+ if ((int)ro != (int)mReadOnly)
+ {
+ mReadOnly = ro;
+ setFocusPolicy(ro ? QWidget::NoFocus : mFocusPolicy);
+ if (ro)
+ clearFocus();
+ }
+}
+
+/******************************************************************************
+* Specify a widget to receive focus when the button is clicked on.
+*/
+void RadioButton::setFocusWidget(QWidget* w, bool enable)
+{
+ mFocusWidget = w;
+ mFocusWidgetEnable = enable;
+ if (w)
+ connect(this, SIGNAL(clicked()), SLOT(slotClicked()));
+ else
+ disconnect(this, SIGNAL(clicked()), this, SLOT(slotClicked()));
+}
+
+/******************************************************************************
+* Called when the button is clicked.
+* If it is now checked, focus is transferred to any specified focus widget.
+*/
+void RadioButton::slotClicked()
+{
+ if (mFocusWidget && isChecked())
+ {
+ if (mFocusWidgetEnable)
+ mFocusWidget->setEnabled(true);
+ mFocusWidget->setFocus();
+ }
+}
+
+/******************************************************************************
+* Event handlers to intercept events if in read-only mode.
+* Any events which could change the button state are discarded.
+*/
+void RadioButton::mousePressEvent(QMouseEvent* e)
+{
+ if (mReadOnly)
+ {
+ // Swallow up the event if it's the left button
+ if (e->button() == Qt::LeftButton)
+ return;
+ }
+ QRadioButton::mousePressEvent(e);
+}
+
+void RadioButton::mouseReleaseEvent(QMouseEvent* e)
+{
+ if (mReadOnly)
+ {
+ // Swallow up the event if it's the left button
+ if (e->button() == Qt::LeftButton)
+ return;
+ }
+ QRadioButton::mouseReleaseEvent(e);
+}
+
+void RadioButton::mouseMoveEvent(QMouseEvent* e)
+{
+ if (!mReadOnly)
+ QRadioButton::mouseMoveEvent(e);
+}
+
+void RadioButton::keyPressEvent(QKeyEvent* e)
+{
+ if (mReadOnly)
+ switch (e->key())
+ {
+ case Qt::Key_Up:
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ case Qt::Key_Down:
+ // Process keys which shift the focus
+ case Qt::Key_Escape:
+ break;
+ default:
+ return;
+ }
+ QRadioButton::keyPressEvent(e);
+}
+
+void RadioButton::keyReleaseEvent(QKeyEvent* e)
+{
+ if (!mReadOnly)
+ QRadioButton::keyReleaseEvent(e);
+}
diff --git a/kalarm/lib/radiobutton.h b/kalarm/lib/radiobutton.h
new file mode 100644
index 00000000..96bca04d
--- /dev/null
+++ b/kalarm/lib/radiobutton.h
@@ -0,0 +1,88 @@
+/*
+ * radiobutton.h - radio button with focus widget and read-only options
+ * Program: kalarm
+ * Copyright © 2002,2003,2005,2006 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 RADIOBUTTON_H
+#define RADIOBUTTON_H
+
+#include <qradiobutton.h>
+
+
+/**
+ * @short A QRadioButton with focus widget and read-only options.
+ *
+ * The RadioButton class is a QRadioButton with the ability to transfer focus to
+ * another widget when checked, and with a read-only option.
+ *
+ * Another widget may be specified as the focus widget for the radio button. Whenever
+ * the user clicks on the radio button so as to set its state to checked, focus is
+ * automatically transferred to the focus widget.
+ *
+ * The widget may be set as read-only. This has the same effect as disabling it, except
+ * that its appearance is unchanged.
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+ */
+class RadioButton : public QRadioButton
+{
+ Q_OBJECT
+ public:
+ /** Constructor.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ explicit RadioButton(QWidget* parent, const char* name = 0);
+ /** Constructor.
+ * @param text Text to display.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ RadioButton(const QString& text, QWidget* parent, const char* name = 0);
+ /** Returns true if the widget is read only. */
+ bool isReadOnly() const { return mReadOnly; }
+ /** Sets whether the radio button is read-only for the user. If read-only,
+ * its state cannot be changed by the user.
+ * @param readOnly True to set the widget read-only, false to set it read-write.
+ */
+ virtual void setReadOnly(bool readOnly);
+ /** Returns the widget which receives focus when the button is clicked. */
+ QWidget* focusWidget() const { return mFocusWidget; }
+ /** Specifies a widget to receive focus when the button is clicked.
+ * @param widget Widget to receive focus.
+ * @param enable If true, @p widget will be enabled before receiving focus. If
+ * false, the enabled state of @p widget will be left unchanged when
+ * the radio button is clicked.
+ */
+ void setFocusWidget(QWidget* widget, bool enable = true);
+ protected:
+ virtual void mousePressEvent(QMouseEvent*);
+ virtual void mouseReleaseEvent(QMouseEvent*);
+ virtual void mouseMoveEvent(QMouseEvent*);
+ virtual void keyPressEvent(QKeyEvent*);
+ virtual void keyReleaseEvent(QKeyEvent*);
+ protected slots:
+ void slotClicked();
+ private:
+ QWidget::FocusPolicy mFocusPolicy; // default focus policy for the QRadioButton
+ QWidget* mFocusWidget; // widget to receive focus when button is clicked on
+ bool mFocusWidgetEnable; // enable focus widget before setting focus
+ bool mReadOnly; // value cannot be changed
+};
+
+#endif // RADIOBUTTON_H
diff --git a/kalarm/lib/shellprocess.cpp b/kalarm/lib/shellprocess.cpp
new file mode 100644
index 00000000..1e37d2e7
--- /dev/null
+++ b/kalarm/lib/shellprocess.cpp
@@ -0,0 +1,208 @@
+/*
+ * shellprocess.cpp - execute a shell process
+ * Program: kalarm
+ * Copyright (c) 2004, 2005 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <kapplication.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+#include "shellprocess.moc"
+
+
+QCString ShellProcess::mShellName;
+QCString ShellProcess::mShellPath;
+bool ShellProcess::mInitialised = false;
+bool ShellProcess::mAuthorised = false;
+
+
+ShellProcess::ShellProcess(const QString& command)
+ : KShellProcess(shellName()),
+ mCommand(command),
+ mStatus(INACTIVE),
+ mStdinExit(false)
+{
+}
+
+/******************************************************************************
+* Execute a command.
+*/
+bool ShellProcess::start(Communication comm)
+{
+ if (!authorised())
+ {
+ mStatus = UNAUTHORISED;
+ return false;
+ }
+ KShellProcess::operator<<(mCommand);
+ connect(this, SIGNAL(wroteStdin(KProcess*)), SLOT(writtenStdin(KProcess*)));
+ connect(this, SIGNAL(processExited(KProcess*)), SLOT(slotExited(KProcess*)));
+ if (!KShellProcess::start(KProcess::NotifyOnExit, comm))
+ {
+ mStatus = START_FAIL;
+ return false;
+ }
+ mStatus = RUNNING;
+ return true;
+}
+
+/******************************************************************************
+* Called when a shell process execution completes.
+* Interprets the exit status according to which shell was called, and emits
+* a shellExited() signal.
+*/
+void ShellProcess::slotExited(KProcess* proc)
+{
+ kdDebug(5950) << "ShellProcess::slotExited()\n";
+ mStdinQueue.clear();
+ mStatus = SUCCESS;
+ if (!proc->normalExit())
+ {
+ kdWarning(5950) << "ShellProcess::slotExited(" << mCommand << ") " << mShellName << ": died/killed\n";
+ mStatus = DIED;
+ }
+ else
+ {
+ // Some shells report if the command couldn't be found, or is not executable
+ int status = proc->exitStatus();
+ if (mShellName == "bash" && (status == 126 || status == 127)
+ || mShellName == "ksh" && status == 127)
+ {
+ kdWarning(5950) << "ShellProcess::slotExited(" << mCommand << ") " << mShellName << ": not found or not executable\n";
+ mStatus = NOT_FOUND;
+ }
+ }
+ emit shellExited(this);
+}
+
+/******************************************************************************
+* Write a string to STDIN.
+*/
+void ShellProcess::writeStdin(const char* buffer, int bufflen)
+{
+ QCString scopy(buffer, bufflen+1); // construct a deep copy
+ bool write = mStdinQueue.isEmpty();
+ mStdinQueue.append(scopy);
+ if (write)
+ KProcess::writeStdin(mStdinQueue.first(), mStdinQueue.first().length());
+}
+
+/******************************************************************************
+* Called when output to STDIN completes.
+* Send the next queued output, if any.
+* Note that buffers written to STDIN must not be freed until the writtenStdin()
+* signal has been processed.
+*/
+void ShellProcess::writtenStdin(KProcess* proc)
+{
+ mStdinQueue.pop_front(); // free the buffer which has now been written
+ if (!mStdinQueue.isEmpty())
+ proc->writeStdin(mStdinQueue.first(), mStdinQueue.first().length());
+ else if (mStdinExit)
+ kill();
+}
+
+/******************************************************************************
+* Tell the process to exit once all STDIN strings have been written.
+*/
+void ShellProcess::stdinExit()
+{
+ if (mStdinQueue.isEmpty())
+ kill();
+ else
+ mStdinExit = true;
+}
+
+/******************************************************************************
+* Return the error message corresponding to the command exit status.
+* Reply = null string if not yet exited, or if command successful.
+*/
+QString ShellProcess::errorMessage() const
+{
+ switch (mStatus)
+ {
+ case UNAUTHORISED:
+ return i18n("Failed to execute command (shell access not authorized):");
+ case START_FAIL:
+ case NOT_FOUND:
+ return i18n("Failed to execute command:");
+ case DIED:
+ return i18n("Command execution error:");
+ case INACTIVE:
+ case RUNNING:
+ case SUCCESS:
+ default:
+ return QString::null;
+ }
+}
+
+/******************************************************************************
+* Determine which shell to use.
+* This is a duplication of what KShellProcess does, but we need to know
+* which shell is used in order to decide what its exit code means.
+*/
+const QCString& ShellProcess::shellPath()
+{
+ if (mShellPath.isEmpty())
+ {
+ // Get the path to the shell
+ mShellPath = "/bin/sh";
+ QCString envshell = QCString(getenv("SHELL")).stripWhiteSpace();
+ if (!envshell.isEmpty())
+ {
+ struct stat fileinfo;
+ if (stat(envshell.data(), &fileinfo) != -1 // ensure file exists
+ && !S_ISDIR(fileinfo.st_mode) // and it's not a directory
+ && !S_ISCHR(fileinfo.st_mode) // and it's not a character device
+ && !S_ISBLK(fileinfo.st_mode) // and it's not a block device
+#ifdef S_ISSOCK
+ && !S_ISSOCK(fileinfo.st_mode) // and it's not a socket
+#endif
+ && !S_ISFIFO(fileinfo.st_mode) // and it's not a fifo
+ && !access(envshell.data(), X_OK)) // and it's executable
+ mShellPath = envshell;
+ }
+
+ // Get the shell filename with the path stripped off
+ int i = mShellPath.findRev('/');
+ if (i >= 0)
+ mShellName = mShellPath.mid(i + 1);
+ else
+ mShellName = mShellPath;
+ }
+ return mShellPath;
+}
+
+/******************************************************************************
+* Check whether shell commands are allowed at all.
+*/
+bool ShellProcess::authorised()
+{
+ if (!mInitialised)
+ {
+ mAuthorised = kapp->authorize("shell_access");
+ mInitialised = true;
+ }
+ return mAuthorised;
+}
diff --git a/kalarm/lib/shellprocess.h b/kalarm/lib/shellprocess.h
new file mode 100644
index 00000000..06a262a8
--- /dev/null
+++ b/kalarm/lib/shellprocess.h
@@ -0,0 +1,138 @@
+/*
+ * shellprocess.h - execute a process through the shell
+ * Program: kalarm
+ * Copyright © 2004-2006 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 SHELLPROCESS_H
+#define SHELLPROCESS_H
+
+/** @file shellprocess.h - execute a process through the shell */
+
+#include <kprocess.h>
+
+
+/**
+ * @short Enhanced KShellProcess.
+ *
+ * The ShellProcess class runs a shell command and interprets the shell exit status
+ * as far as possible. It blocks execution if shell access is prohibited. It buffers
+ * data written to the process's stdin.
+ *
+ * Before executing any command, ShellProcess checks whether shell commands are
+ * allowed at all. If not (e.g. if the user is running in kiosk mode), it blocks
+ * execution.
+ *
+ * Derived from KShellProcess, this class additionally tries to interpret the shell
+ * exit status. Different shells use different exit codes. Currently, if bash or ksh
+ * report that the command could not be found or could not be executed, the NOT_FOUND
+ * status is returned.
+ *
+ * Writes to the process's stdin are buffered, so that unlike with KShellProcess, there
+ * is no need to wait for the write to complete before writing again.
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+ */
+class ShellProcess : public KShellProcess
+{
+ Q_OBJECT
+ public:
+ /** Current status of the shell process.
+ * @li INACTIVE - start() has not yet been called to run the command.
+ * @li RUNNING - the command is currently running.
+ * @li SUCCESS - the command appears to have exited successfully.
+ * @li UNAUTHORISED - shell commands are not authorised for this user.
+ * @li DIED - the command didn't exit cleanly, i.e. was killed or died.
+ * @li NOT_FOUND - the command was either not found or not executable.
+ * @li START_FAIL - the command couldn't be started for other reasons.
+ */
+ enum Status {
+ INACTIVE, // start() has not yet been called to run the command
+ RUNNING, // command is currently running
+ SUCCESS, // command appears to have exited successfully
+ UNAUTHORISED, // shell commands are not authorised for this user
+ DIED, // command didn't exit cleanly, i.e. was killed or died
+ NOT_FOUND, // command either not found or not executable
+ START_FAIL // command couldn't be started for other reasons
+ };
+ /** Constructor.
+ * @param command The command line to be run when start() is called.
+ */
+ explicit ShellProcess(const QString& command);
+ /** Executes the configured command.
+ * @param comm Which communication links should be established to the child process
+ * (stdin/stdout/stderr).
+ */
+ bool start(Communication comm = NoCommunication);
+ /** Returns the current status of the shell process. */
+ Status status() const { return mStatus; }
+ /** Returns whether the command was run successfully.
+ * @return True if the command has been run and appears to have exited successfully.
+ */
+ bool normalExit() const { return mStatus == SUCCESS; }
+ /** Returns the command configured to be run. */
+ const QString& command() const { return mCommand; }
+ /** Returns the error message corresponding to the command exit status.
+ * @return Error message if an error occurred. Null string if the command has not yet
+ * exited, or if the command ran successfully.
+ */
+ QString errorMessage() const;
+ /** Writes a string to the process's STDIN. */
+ void writeStdin(const char* buffer, int bufflen);
+ /** Tell the process to exit once any outstanding STDIN strings have been written. */
+ void stdinExit();
+ /** Returns whether the user is authorised to run shell commands. Shell commands may
+ * be prohibited in kiosk mode, for example.
+ */
+ static bool authorised();
+ /** Determines which shell to use.
+ * @return file name of shell, excluding path.
+ */
+ static const QCString& shellName() { shellPath(); return mShellName; }
+ /** Determines which shell to use.
+ * @return path name of shell.
+ */
+ static const QCString& shellPath();
+
+ signals:
+ /** Signal emitted when the shell process execution completes. It is not emitted
+ * if start() did not attempt to start the command execution, e.g. in kiosk mode.
+ */
+ void shellExited(ShellProcess*);
+
+ private slots:
+ void writtenStdin(KProcess*);
+ void slotExited(KProcess*);
+
+ private:
+ // Prohibit the following inherited methods
+ ShellProcess& operator<<(const QString&);
+ ShellProcess& operator<<(const QCString&);
+ ShellProcess& operator<<(const QStringList&);
+ ShellProcess& operator<<(const char*);
+
+ static QCString mShellName; // name of shell to be used
+ static QCString mShellPath; // path of shell to be used
+ static bool mInitialised; // true once static data has been initialised
+ static bool mAuthorised; // true if shell commands are authorised
+ QString mCommand; // copy of command to be executed
+ QValueList<QCString> mStdinQueue; // queued strings to send to STDIN
+ Status mStatus; // current execution status
+ bool mStdinExit; // exit once STDIN queue has been written
+};
+
+#endif // SHELLPROCESS_H
diff --git a/kalarm/lib/slider.cpp b/kalarm/lib/slider.cpp
new file mode 100644
index 00000000..1a099964
--- /dev/null
+++ b/kalarm/lib/slider.cpp
@@ -0,0 +1,85 @@
+/*
+ * slider.cpp - slider control with read-only option
+ * Program: kalarm
+ * Copyright (c) 2004 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 "slider.moc"
+
+
+Slider::Slider(QWidget* parent, const char* name)
+ : QSlider(parent, name),
+ mReadOnly(false)
+{ }
+
+Slider::Slider(Orientation o, QWidget* parent, const char* name)
+ : QSlider(o, parent, name),
+ mReadOnly(false)
+{ }
+
+Slider::Slider(int minval, int maxval, int pageStep, int value, Orientation o, QWidget* parent, const char* name)
+ : QSlider(minval, maxval, pageStep, value, o, parent, name),
+ mReadOnly(false)
+{ }
+
+/******************************************************************************
+* Set the read-only status. If read-only, the slider can be moved by the
+* application, but not by the user.
+*/
+void Slider::setReadOnly(bool ro)
+{
+ mReadOnly = ro;
+}
+
+/******************************************************************************
+* Event handlers to intercept events if in read-only mode.
+* Any events which could change the slider value are discarded.
+*/
+void Slider::mousePressEvent(QMouseEvent* e)
+{
+ if (mReadOnly)
+ {
+ // Swallow up the event if it's the left button
+ if (e->button() == Qt::LeftButton)
+ return;
+ }
+ QSlider::mousePressEvent(e);
+}
+
+void Slider::mouseReleaseEvent(QMouseEvent* e)
+{
+ if (!mReadOnly)
+ QSlider::mouseReleaseEvent(e);
+}
+
+void Slider::mouseMoveEvent(QMouseEvent* e)
+{
+ if (!mReadOnly)
+ QSlider::mouseMoveEvent(e);
+}
+
+void Slider::keyPressEvent(QKeyEvent* e)
+{
+ if (!mReadOnly || e->key() == Qt::Key_Escape)
+ QSlider::keyPressEvent(e);
+}
+
+void Slider::keyReleaseEvent(QKeyEvent* e)
+{
+ if (!mReadOnly)
+ QSlider::keyReleaseEvent(e);
+}
diff --git a/kalarm/lib/slider.h b/kalarm/lib/slider.h
new file mode 100644
index 00000000..17635e68
--- /dev/null
+++ b/kalarm/lib/slider.h
@@ -0,0 +1,80 @@
+/*
+ * slider.h - slider control with read-only option
+ * Program: kalarm
+ * Copyright © 2004,2006 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 SLIDER_H
+#define SLIDER_H
+
+#include <qslider.h>
+
+
+/**
+ * @short A QSlider with read-only option.
+ *
+ * The Slider class is a QSlider with a read-only option.
+ *
+ * The widget may be set as read-only. This has the same effect as disabling it, except
+ * that its appearance is unchanged.
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+ */
+class Slider : public QSlider
+{
+ Q_OBJECT
+ Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly)
+ public:
+ /** Constructor.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ explicit Slider(QWidget* parent = 0, const char* name = 0);
+ /** Constructor.
+ * @param orient The orientation of the slider, either Qt::Horizonal or Qt::Vertical.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ explicit Slider(Orientation orient, QWidget* parent = 0, const char* name = 0);
+ /** Constructor.
+ * @param minValue The minimum value which the slider can have.
+ * @param maxValue The maximum value which the slider can have.
+ * @param pageStep The page step increment.
+ * @param value The initial value for the slider.
+ * @param orient The orientation of the slider, either Qt::Horizonal or Qt::Vertical.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ Slider(int minValue, int maxValue, int pageStep, int value, Orientation orient,
+ QWidget* parent = 0, const char* name = 0);
+ /** Returns true if the slider is read only. */
+ bool isReadOnly() const { return mReadOnly; }
+ /** Sets whether the slider is read-only for the user.
+ * @param readOnly True to set the widget read-only, false to set it read-write.
+ */
+ virtual void setReadOnly(bool readOnly);
+ protected:
+ virtual void mousePressEvent(QMouseEvent*);
+ virtual void mouseReleaseEvent(QMouseEvent*);
+ virtual void mouseMoveEvent(QMouseEvent*);
+ virtual void keyPressEvent(QKeyEvent*);
+ virtual void keyReleaseEvent(QKeyEvent*);
+ private:
+ bool mReadOnly; // value cannot be changed by the user
+};
+
+#endif // SLIDER_H
diff --git a/kalarm/lib/spinbox.cpp b/kalarm/lib/spinbox.cpp
new file mode 100644
index 00000000..bb9ef244
--- /dev/null
+++ b/kalarm/lib/spinbox.cpp
@@ -0,0 +1,476 @@
+/*
+ * spinbox.cpp - spin box with read-only option and shift-click step value
+ * Program: kalarm
+ * Copyright © 2002,2004,2008 by David Jarvie <djarvie@kde.org>
+ *
+ * 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 <kdeversion.h>
+#include <qlineedit.h>
+#include <qobjectlist.h>
+#include "spinbox.moc"
+
+
+SpinBox::SpinBox(QWidget* parent, const char* name)
+ : QSpinBox(0, 99999, 1, parent, name),
+ mMinValue(QSpinBox::minValue()),
+ mMaxValue(QSpinBox::maxValue())
+{
+ init();
+}
+
+SpinBox::SpinBox(int minValue, int maxValue, int step, QWidget* parent, const char* name)
+ : QSpinBox(minValue, maxValue, step, parent, name),
+ mMinValue(minValue),
+ mMaxValue(maxValue)
+{
+ init();
+}
+
+void SpinBox::init()
+{
+ int step = QSpinBox::lineStep();
+ mLineStep = step;
+ mLineShiftStep = step;
+ mCurrentButton = NO_BUTTON;
+ mShiftMouse = false;
+ mShiftMinBound = false;
+ mShiftMaxBound = false;
+ mSelectOnStep = true;
+ mReadOnly = false;
+ mSuppressSignals = false;
+ mEdited = false;
+
+ // Find the spin widgets which are part of the spin boxes, in order to
+ // handle their shift-button presses.
+ QObjectList* spinwidgets = queryList("QSpinWidget", 0, false, true);
+ QSpinWidget* spin = (QSpinWidget*)spinwidgets->getFirst();
+ if (spin)
+ spin->installEventFilter(this); // handle shift-button presses
+ delete spinwidgets;
+ editor()->installEventFilter(this); // handle shift-up/down arrow presses
+
+#if KDE_IS_VERSION(3,1,90)
+ // Detect when the text field is edited
+ connect(editor(), SIGNAL(textChanged(const QString&)), SLOT(textEdited()));
+#endif
+}
+
+void SpinBox::setReadOnly(bool ro)
+{
+ if ((int)ro != (int)mReadOnly)
+ {
+ mReadOnly = ro;
+ editor()->setReadOnly(ro);
+ if (ro)
+ setShiftStepping(false, mCurrentButton);
+ }
+}
+
+int SpinBox::bound(int val) const
+{
+ return (val < mMinValue) ? mMinValue : (val > mMaxValue) ? mMaxValue : val;
+}
+
+void SpinBox::setMinValue(int val)
+{
+ mMinValue = val;
+ QSpinBox::setMinValue(val);
+ mShiftMinBound = false;
+}
+
+void SpinBox::setMaxValue(int val)
+{
+ mMaxValue = val;
+ QSpinBox::setMaxValue(val);
+ mShiftMaxBound = false;
+}
+
+void SpinBox::setLineStep(int step)
+{
+ mLineStep = step;
+ if (!mShiftMouse)
+ QSpinBox::setLineStep(step);
+}
+
+void SpinBox::setLineShiftStep(int step)
+{
+ mLineShiftStep = step;
+ if (mShiftMouse)
+ QSpinBox::setLineStep(step);
+}
+
+void SpinBox::stepUp()
+{
+ int step = QSpinBox::lineStep();
+ addValue(step);
+ emit stepped(step);
+}
+
+void SpinBox::stepDown()
+{
+ int step = -QSpinBox::lineStep();
+ addValue(step);
+ emit stepped(step);
+}
+
+/******************************************************************************
+* Adds a positive or negative increment to the current value, wrapping as appropriate.
+* If 'current' is true, any temporary 'shift' values for the range are used instead
+* of the real maximum and minimum values.
+*/
+void SpinBox::addValue(int change, bool current)
+{
+ int newval = value() + change;
+ int maxval = current ? QSpinBox::maxValue() : mMaxValue;
+ int minval = current ? QSpinBox::minValue() : mMinValue;
+ if (wrapping())
+ {
+ int range = maxval - minval + 1;
+ if (newval > maxval)
+ newval = minval + (newval - maxval - 1) % range;
+ else if (newval < minval)
+ newval = maxval - (minval - 1 - newval) % range;
+ }
+ else
+ {
+ if (newval > maxval)
+ newval = maxval;
+ else if (newval < minval)
+ newval = minval;
+ }
+ setValue(newval);
+}
+
+void SpinBox::valueChange()
+{
+ if (!mSuppressSignals)
+ {
+ int val = value();
+ if (mShiftMinBound && val >= mMinValue)
+ {
+ // Reinstate the minimum bound now that the value has returned to the normal range.
+ QSpinBox::setMinValue(mMinValue);
+ mShiftMinBound = false;
+ }
+ if (mShiftMaxBound && val <= mMaxValue)
+ {
+ // Reinstate the maximum bound now that the value has returned to the normal range.
+ QSpinBox::setMaxValue(mMaxValue);
+ mShiftMaxBound = false;
+ }
+
+ bool focus = !mSelectOnStep && hasFocus();
+ if (focus)
+ clearFocus(); // prevent selection of the spin box text
+ QSpinBox::valueChange();
+ if (focus)
+ setFocus();
+ }
+}
+
+/******************************************************************************
+* Called whenever the line edit text is changed.
+*/
+void SpinBox::textEdited()
+{
+ mEdited = true;
+}
+
+void SpinBox::updateDisplay()
+{
+ mEdited = false;
+ QSpinBox::updateDisplay();
+}
+
+/******************************************************************************
+* Receives events destined for the spin widget or for the edit field.
+*/
+bool SpinBox::eventFilter(QObject* obj, QEvent* e)
+{
+ if (obj == editor())
+ {
+ int step = 0;
+ bool shift = false;
+ switch (e->type())
+ {
+ case QEvent::KeyPress:
+ {
+ // Up and down arrow keys step the value
+ QKeyEvent* ke = (QKeyEvent*)e;
+ int key = ke->key();
+ if (key == Qt::Key_Up)
+ step = 1;
+ else if (key == Qt::Key_Down)
+ step = -1;
+ shift = ((ke->state() & (Qt::ShiftButton | Qt::AltButton)) == Qt::ShiftButton);
+ break;
+ }
+ case QEvent::Wheel:
+ {
+ QWheelEvent* we = (QWheelEvent*)e;
+ step = (we->delta() > 0) ? 1 : -1;
+ shift = ((we->state() & (Qt::ShiftButton | Qt::AltButton)) == Qt::ShiftButton);
+ break;
+ }
+#if KDE_IS_VERSION(3,1,90)
+ case QEvent::Leave:
+ if (mEdited)
+ interpretText();
+ break;
+#endif
+ default:
+ break;
+ }
+ if (step)
+ {
+ if (mReadOnly)
+ return true; // discard up/down arrow keys or wheel
+ if (shift)
+ {
+ // Shift stepping
+ int val = value();
+ if (step > 0)
+ step = mLineShiftStep - val % mLineShiftStep;
+ else
+ step = - ((val + mLineShiftStep - 1) % mLineShiftStep + 1);
+ }
+ else
+ step = (step > 0) ? mLineStep : -mLineStep;
+ addValue(step, false);
+ return true;
+ }
+ }
+ else
+ {
+ int etype = e->type(); // avoid switch compile warnings
+ switch (etype)
+ {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonDblClick:
+ {
+ QMouseEvent* me = (QMouseEvent*)e;
+ if (me->button() == Qt::LeftButton)
+ {
+ // It's a left button press. Set normal or shift stepping as appropriate.
+ if (mReadOnly)
+ return true; // discard the event
+ mCurrentButton = whichButton(me->pos());
+ if (mCurrentButton == NO_BUTTON)
+ return true;
+ bool shift = (me->state() & (Qt::ShiftButton | Qt::AltButton)) == Qt::ShiftButton;
+ if (setShiftStepping(shift, mCurrentButton))
+ return true; // hide the event from the spin widget
+ return false; // forward event to the destination widget
+ }
+ break;
+ }
+ case QEvent::MouseButtonRelease:
+ {
+ QMouseEvent* me = (QMouseEvent*)e;
+ if (me->button() == Qt::LeftButton && mShiftMouse)
+ {
+ setShiftStepping(false, mCurrentButton); // cancel shift stepping
+ return false; // forward event to the destination widget
+ }
+ break;
+ }
+ case QEvent::MouseMove:
+ {
+ QMouseEvent* me = (QMouseEvent*)e;
+ if (me->state() & Qt::LeftButton)
+ {
+ // The left button is down. Track which spin button it's in.
+ if (mReadOnly)
+ return true; // discard the event
+ int newButton = whichButton(me->pos());
+ if (newButton != mCurrentButton)
+ {
+ // The mouse has moved to a new spin button.
+ // Set normal or shift stepping as appropriate.
+ mCurrentButton = newButton;
+ bool shift = (me->state() & (Qt::ShiftButton | Qt::AltButton)) == Qt::ShiftButton;
+ if (setShiftStepping(shift, mCurrentButton))
+ return true; // hide the event from the spin widget
+ }
+ return false; // forward event to the destination widget
+ }
+ break;
+ }
+ case QEvent::Wheel:
+ {
+ QWheelEvent* we = (QWheelEvent*)e;
+ bool shift = (we->state() & (Qt::ShiftButton | Qt::AltButton)) == Qt::ShiftButton;
+ if (setShiftStepping(shift, (we->delta() > 0 ? UP : DOWN)))
+ return true; // hide the event from the spin widget
+ return false; // forward event to the destination widget
+ }
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+ case QEvent::AccelOverride: // this is needed to receive Shift presses!
+ {
+ QKeyEvent* ke = (QKeyEvent*)e;
+ int key = ke->key();
+ int state = ke->state();
+ if ((state & Qt::LeftButton)
+ && (key == Qt::Key_Shift || key == Qt::Key_Alt))
+ {
+ // The left mouse button is down, and the Shift or Alt key has changed
+ if (mReadOnly)
+ return true; // discard the event
+ state ^= (key == Qt::Key_Shift) ? Qt::ShiftButton : Qt::AltButton; // new state
+ bool shift = (state & (Qt::ShiftButton | Qt::AltButton)) == Qt::ShiftButton;
+ if ((!shift && mShiftMouse) || (shift && !mShiftMouse))
+ {
+ // The effective shift state has changed.
+ // Set normal or shift stepping as appropriate.
+ if (setShiftStepping(shift, mCurrentButton))
+ return true; // hide the event from the spin widget
+ }
+ }
+ break;
+ }
+ }
+ }
+ return QSpinBox::eventFilter(obj, e);
+}
+
+/******************************************************************************
+* Set spin widget stepping to the normal or shift increment.
+*/
+bool SpinBox::setShiftStepping(bool shift, int currentButton)
+{
+ if (currentButton == NO_BUTTON)
+ shift = false;
+ if (shift && !mShiftMouse)
+ {
+ /* The value is to be stepped to a multiple of the shift increment.
+ * Adjust the value so that after the spin widget steps it, it will be correct.
+ * Then, if the mouse button is held down, the spin widget will continue to
+ * step by the shift amount.
+ */
+ int val = value();
+ int step = (currentButton == UP) ? mLineShiftStep : (currentButton == DOWN) ? -mLineShiftStep : 0;
+ int adjust = shiftStepAdjustment(val, step);
+ mShiftMouse = true;
+ if (adjust)
+ {
+ /* The value is to be stepped by other than the shift increment,
+ * presumably because it is being set to a multiple of the shift
+ * increment. Achieve this by making the adjustment here, and then
+ * allowing the normal step processing to complete the job by
+ * adding/subtracting the normal shift increment.
+ */
+ if (!wrapping())
+ {
+ // Prevent the step from going past the spinbox's range, or
+ // to the minimum value if that has a special text unless it is
+ // already at the minimum value + 1.
+ int newval = val + adjust + step;
+ int svt = specialValueText().isEmpty() ? 0 : 1;
+ int minval = mMinValue + svt;
+ if (newval <= minval || newval >= mMaxValue)
+ {
+ // Stepping to the minimum or maximum value
+ if (svt && newval <= mMinValue && val == mMinValue)
+ newval = mMinValue;
+ else
+ newval = (newval <= minval) ? minval : mMaxValue;
+ QSpinBox::setValue(newval);
+ emit stepped(step);
+ return true;
+ }
+
+ // If the interim value will lie outside the spinbox's range,
+ // temporarily adjust the range to allow the value to be set.
+ int tempval = val + adjust;
+ if (tempval < mMinValue)
+ {
+ QSpinBox::setMinValue(tempval);
+ mShiftMinBound = true;
+ }
+ else if (tempval > mMaxValue)
+ {
+ QSpinBox::setMaxValue(tempval);
+ mShiftMaxBound = true;
+ }
+ }
+
+ // Don't process changes since this new value will be stepped immediately
+ mSuppressSignals = true;
+ bool blocked = signalsBlocked();
+ blockSignals(true);
+ addValue(adjust, true);
+ blockSignals(blocked);
+ mSuppressSignals = false;
+ }
+ QSpinBox::setLineStep(mLineShiftStep);
+ }
+ else if (!shift && mShiftMouse)
+ {
+ // Reinstate to normal (non-shift) stepping
+ QSpinBox::setLineStep(mLineStep);
+ QSpinBox::setMinValue(mMinValue);
+ QSpinBox::setMaxValue(mMaxValue);
+ mShiftMinBound = mShiftMaxBound = false;
+ mShiftMouse = false;
+ }
+ return false;
+}
+
+/******************************************************************************
+* Return the initial adjustment to the value for a shift step up or down.
+* The default is to step up or down to the nearest multiple of the shift
+* increment, so the adjustment returned is for stepping up the decrement
+* required to round down to a multiple of the shift increment <= current value,
+* or for stepping down the increment required to round up to a multiple of the
+* shift increment >= current value.
+* This method's caller then adjusts the resultant value if necessary to cater
+* for the widget's minimum/maximum value, and wrapping.
+* This should really be a static method, but it needs to be virtual...
+*/
+int SpinBox::shiftStepAdjustment(int oldValue, int shiftStep)
+{
+ if (oldValue == 0 || shiftStep == 0)
+ return 0;
+ if (shiftStep > 0)
+ {
+ if (oldValue >= 0)
+ return -(oldValue % shiftStep);
+ else
+ return (-oldValue - 1) % shiftStep + 1 - shiftStep;
+ }
+ else
+ {
+ shiftStep = -shiftStep;
+ if (oldValue >= 0)
+ return shiftStep - ((oldValue - 1) % shiftStep + 1);
+ else
+ return (-oldValue) % shiftStep;
+ }
+}
+
+/******************************************************************************
+* Find which spin widget button a mouse event is in.
+*/
+int SpinBox::whichButton(const QPoint& pos)
+{
+ if (upRect().contains(pos))
+ return UP;
+ if (downRect().contains(pos))
+ return DOWN;
+ return NO_BUTTON;
+}
diff --git a/kalarm/lib/spinbox.h b/kalarm/lib/spinbox.h
new file mode 100644
index 00000000..0df91023
--- /dev/null
+++ b/kalarm/lib/spinbox.h
@@ -0,0 +1,156 @@
+/*
+ * spinbox.h - spin box with shift-click step value and read-only option
+ * Program: kalarm
+ * Copyright © 2002-2006,2008 by David Jarvie <djarvie@kde.org>
+ *
+ * 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 SPINBOX_H
+#define SPINBOX_H
+
+#include <qspinbox.h>
+
+
+/**
+ * @short Spin box with accelerated shift key stepping and read-only option.
+ *
+ * The SpinBox class provides a QSpinBox with accelerated stepping using the shift key.
+ *
+ * A separate step increment may optionally be specified for use when the shift key is
+ * held down. Typically this would be larger than the normal step. Then, when the user
+ * clicks the spin buttons, he/she can increment or decrement the value faster by holding
+ * the shift key down.
+ *
+ * The widget may be set as read-only. This has the same effect as disabling it, except
+ * that its appearance is unchanged.
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+ */
+class SpinBox : public QSpinBox
+{
+ Q_OBJECT
+ public:
+ /** Constructor.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ explicit SpinBox(QWidget* parent = 0, const char* name = 0);
+ /** Constructor.
+ * @param minValue The minimum value which the spin box can have.
+ * @param maxValue The maximum value which the spin box can have.
+ * @param step The (unshifted) step interval.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ SpinBox(int minValue, int maxValue, int step = 1, QWidget* parent = 0, const char* name = 0);
+ /** Returns true if the widget is read only. */
+ bool isReadOnly() const { return mReadOnly; }
+ /** Sets whether the spin box can be changed by the user.
+ * @param readOnly True to set the widget read-only, false to set it read-write.
+ */
+ virtual void setReadOnly(bool readOnly);
+ /** Returns whether the spin box value text is selected when its value is stepped. */
+ bool selectOnStep() const { return mSelectOnStep; }
+ /** Sets whether the spin box value text should be selected when its value is stepped. */
+ void setSelectOnStep(bool sel) { mSelectOnStep = sel; }
+ /** Adds a value to the current value of the spin box. */
+ void addValue(int change) { addValue(change, false); }
+ /** Returns the minimum value of the spin box. */
+ int minValue() const { return mMinValue; }
+ /** Returns the maximum value of the spin box. */
+ int maxValue() const { return mMaxValue; }
+ /** Sets the minimum value of the spin box. */
+ void setMinValue(int val);
+ /** Sets the maximum value of the spin box. */
+ void setMaxValue(int val);
+ /** Sets the minimum and maximum values of the spin box. */
+ void setRange(int minValue, int maxValue) { setMinValue(minValue); setMaxValue(maxValue); }
+ /** Returns the specified value clamped to the range of the spin box. */
+ int bound(int val) const;
+ /** Returns the unshifted step increment, i.e. the amount by which the spin box value
+ * changes when a spin button is clicked without the shift key being pressed.
+ */
+ int lineStep() const { return mLineStep; }
+ /** Sets the unshifted step increment, i.e. the amount by which the spin box value
+ * changes when a spin button is clicked without the shift key being pressed.
+ */
+ void setLineStep(int step);
+ /** Returns the shifted step increment, i.e. the amount by which the spin box value
+ * changes when a spin button is clicked while the shift key is pressed.
+ */
+ int lineShiftStep() const { return mLineShiftStep; }
+ /** Sets the shifted step increment, i.e. the amount by which the spin box value
+ * changes when a spin button is clicked while the shift key is pressed.
+ */
+ void setLineShiftStep(int step);
+ public slots:
+ /** Increments the value of the spin box by the unshifted step increment. */
+ virtual void stepUp();
+ /** Decrements the value of the spin box by the unshifted step increment. */
+ virtual void stepDown();
+ signals:
+ /** Signal emitted when the spin box's value is stepped (by the shifted or unshifted increment).
+ * @param step The requested step in the spin box's value. Note that the actual change in value
+ * may have been less than this.
+ */
+ void stepped(int step);
+
+ protected:
+ /** A virtual method called whenever the value of the spin box has changed. */
+ virtual void valueChange();
+ /** Returns the initial adjustment to the value for a shift step up or down.
+ * The default is to step up or down to the nearest multiple of the shift
+ * increment, so the adjustment returned is for stepping up the decrement
+ * required to round down to a multiple of the shift increment <= current value,
+ * or for stepping down the increment required to round up to a multiple of the
+ * shift increment >= current value.
+ * This method's caller then adjusts the resultant value if necessary to cater
+ * for the widget's minimum/maximum value, and wrapping.
+ * This should really be a static method, but it needs to be virtual...
+ */
+ virtual int shiftStepAdjustment(int oldValue, int shiftStep);
+ /** Receives events destined for the spin widget or for the edit field. */
+ virtual bool eventFilter(QObject*, QEvent*);
+ /** Updates the contents of the embedded QLineEdit to reflect the current value
+ * using mapValueToText(). Also enables/disables the up/down push buttons accordingly.
+ */
+ virtual void updateDisplay();
+
+ private slots:
+ void textEdited();
+ private:
+ void init();
+ void addValue(int change, bool current);
+ int whichButton(const QPoint&);
+ bool setShiftStepping(bool, int currentButton);
+
+ enum { NO_BUTTON, UP, DOWN };
+
+ int mMinValue;
+ int mMaxValue;
+ int mLineStep; // step when spin arrows are pressed
+ int mLineShiftStep; // step when spin arrows are pressed with shift key
+ int mCurrentButton; // current spin widget button
+ bool mShiftMouse; // true while left button is being held down with shift key
+ bool mShiftMinBound; // true if a temporary minimum bound has been set during shift stepping
+ bool mShiftMaxBound; // true if a temporary maximum bound has been set during shift stepping
+ bool mSelectOnStep; // select the editor text whenever spin buttons are clicked (default)
+ bool mReadOnly; // value cannot be changed
+ bool mSuppressSignals;
+ bool mEdited; // text field has been edited
+};
+
+#endif // SPINBOX_H
diff --git a/kalarm/lib/spinbox2.cpp b/kalarm/lib/spinbox2.cpp
new file mode 100644
index 00000000..313d3eb5
--- /dev/null
+++ b/kalarm/lib/spinbox2.cpp
@@ -0,0 +1,511 @@
+/*
+ * spinbox2.cpp - spin box with extra pair of spin buttons (for Qt 3)
+ * Program: kalarm
+ * Copyright © 2001-2005,2008 by David Jarvie <djarvie@kde.org>
+ *
+ * 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 <qglobal.h>
+
+#include <stdlib.h>
+
+#include <qstyle.h>
+#include <qobjectlist.h>
+#include <qapplication.h>
+#include <qpixmap.h>
+#include <qcursor.h>
+#include <qtimer.h>
+#include <qwmatrix.h>
+
+#include "spinbox2.moc"
+#include "spinbox2private.moc"
+
+
+/* List of styles which need to display the extra pair of spin buttons as a
+ * left-to-right mirror image. This is only necessary when, for example, the
+ * corners of widgets are rounded. For most styles, it is better not to mirror
+ * the spin widgets so as to keep the normal lighting/shading on either side.
+ */
+static const char* mirrorStyles[] = {
+ "PlastikStyle",
+ 0 // list terminator
+};
+static bool mirrorStyle(const QStyle&);
+
+
+int SpinBox2::mReverseLayout = -1;
+
+SpinBox2::SpinBox2(QWidget* parent, const char* name)
+ : QFrame(parent, name),
+ mReverseWithLayout(true)
+{
+ mUpdown2Frame = new QFrame(this);
+ mSpinboxFrame = new QFrame(this);
+ mUpdown2 = new ExtraSpinBox(mUpdown2Frame, "updown2");
+// mSpinbox = new MainSpinBox(0, 1, 1, this, mSpinboxFrame);
+ mSpinbox = new MainSpinBox(this, mSpinboxFrame);
+ init();
+}
+
+SpinBox2::SpinBox2(int minValue, int maxValue, int step, int step2, QWidget* parent, const char* name)
+ : QFrame(parent, name),
+ mReverseWithLayout(true)
+{
+ mUpdown2Frame = new QFrame(this);
+ mSpinboxFrame = new QFrame(this);
+ mUpdown2 = new ExtraSpinBox(minValue, maxValue, step2, mUpdown2Frame, "updown2");
+ mSpinbox = new MainSpinBox(minValue, maxValue, step, this, mSpinboxFrame);
+ setSteps(step, step2);
+ init();
+}
+
+void SpinBox2::init()
+{
+ if (mReverseLayout < 0)
+ mReverseLayout = QApplication::reverseLayout() ? 1 : 0;
+ mMinValue = mSpinbox->minValue();
+ mMaxValue = mSpinbox->maxValue();
+ mLineStep = mSpinbox->lineStep();
+ mLineShiftStep = mSpinbox->lineShiftStep();
+ mPageStep = mUpdown2->lineStep();
+ mPageShiftStep = mUpdown2->lineShiftStep();
+ mSpinbox->setSelectOnStep(false); // default
+ mUpdown2->setSelectOnStep(false); // always false
+ setFocusProxy(mSpinbox);
+ mUpdown2->setFocusPolicy(QWidget::NoFocus);
+ mSpinMirror = new SpinMirror(mUpdown2, mUpdown2Frame, this);
+ if (!mirrorStyle(style()))
+ mSpinMirror->hide(); // hide mirrored spin buttons when they are inappropriate
+ connect(mSpinbox, SIGNAL(valueChanged(int)), SLOT(valueChange()));
+ connect(mSpinbox, SIGNAL(valueChanged(int)), SIGNAL(valueChanged(int)));
+ connect(mSpinbox, SIGNAL(valueChanged(const QString&)), SIGNAL(valueChanged(const QString&)));
+ connect(mUpdown2, SIGNAL(stepped(int)), SLOT(stepPage(int)));
+ connect(mUpdown2, SIGNAL(styleUpdated()), SLOT(updateMirror()));
+}
+
+void SpinBox2::setReadOnly(bool ro)
+{
+ if (static_cast<int>(ro) != static_cast<int>(mSpinbox->isReadOnly()))
+ {
+ mSpinbox->setReadOnly(ro);
+ mUpdown2->setReadOnly(ro);
+ mSpinMirror->setReadOnly(ro);
+ }
+}
+
+void SpinBox2::setReverseWithLayout(bool reverse)
+{
+ if (reverse != mReverseWithLayout)
+ {
+ mReverseWithLayout = reverse;
+ setSteps(mLineStep, mPageStep);
+ setShiftSteps(mLineShiftStep, mPageShiftStep);
+ }
+}
+
+void SpinBox2::setEnabled(bool enabled)
+{
+ QFrame::setEnabled(enabled);
+ updateMirror();
+}
+
+void SpinBox2::setWrapping(bool on)
+{
+ mSpinbox->setWrapping(on);
+ mUpdown2->setWrapping(on);
+}
+
+QRect SpinBox2::up2Rect() const
+{
+ return mUpdown2->upRect();
+}
+
+QRect SpinBox2::down2Rect() const
+{
+ return mUpdown2->downRect();
+}
+
+void SpinBox2::setLineStep(int step)
+{
+ mLineStep = step;
+ if (reverseButtons())
+ mUpdown2->setLineStep(step); // reverse layout, but still set the right buttons
+ else
+ mSpinbox->setLineStep(step);
+}
+
+void SpinBox2::setSteps(int line, int page)
+{
+ mLineStep = line;
+ mPageStep = page;
+ if (reverseButtons())
+ {
+ mUpdown2->setLineStep(line); // reverse layout, but still set the right buttons
+ mSpinbox->setLineStep(page);
+ }
+ else
+ {
+ mSpinbox->setLineStep(line);
+ mUpdown2->setLineStep(page);
+ }
+}
+
+void SpinBox2::setShiftSteps(int line, int page)
+{
+ mLineShiftStep = line;
+ mPageShiftStep = page;
+ if (reverseButtons())
+ {
+ mUpdown2->setLineShiftStep(line); // reverse layout, but still set the right buttons
+ mSpinbox->setLineShiftStep(page);
+ }
+ else
+ {
+ mSpinbox->setLineShiftStep(line);
+ mUpdown2->setLineShiftStep(page);
+ }
+}
+
+void SpinBox2::setButtonSymbols(QSpinBox::ButtonSymbols newSymbols)
+{
+ if (mSpinbox->buttonSymbols() == newSymbols)
+ return;
+ mSpinbox->setButtonSymbols(newSymbols);
+ mUpdown2->setButtonSymbols(newSymbols);
+}
+
+int SpinBox2::bound(int val) const
+{
+ return (val < mMinValue) ? mMinValue : (val > mMaxValue) ? mMaxValue : val;
+}
+
+void SpinBox2::setMinValue(int val)
+{
+ mMinValue = val;
+ mSpinbox->setMinValue(val);
+ mUpdown2->setMinValue(val);
+}
+
+void SpinBox2::setMaxValue(int val)
+{
+ mMaxValue = val;
+ mSpinbox->setMaxValue(val);
+ mUpdown2->setMaxValue(val);
+}
+
+void SpinBox2::valueChange()
+{
+ int val = mSpinbox->value();
+ bool blocked = mUpdown2->signalsBlocked();
+ mUpdown2->blockSignals(true);
+ mUpdown2->setValue(val);
+ mUpdown2->blockSignals(blocked);
+}
+
+/******************************************************************************
+* Called when the widget is about to be displayed.
+* (At construction time, the spin button widths cannot be determined correctly,
+* so we need to wait until now to definitively rearrange the widget.)
+*/
+void SpinBox2::showEvent(QShowEvent*)
+{
+ arrange();
+}
+
+QSize SpinBox2::sizeHint() const
+{
+ getMetrics();
+ QSize size = mSpinbox->sizeHint();
+ size.setWidth(size.width() - xSpinbox + wUpdown2 + wGap);
+ return size;
+}
+
+QSize SpinBox2::minimumSizeHint() const
+{
+ getMetrics();
+ QSize size = mSpinbox->minimumSizeHint();
+ size.setWidth(size.width() - xSpinbox + wUpdown2 + wGap);
+ return size;
+}
+
+void SpinBox2::styleChange(QStyle&)
+{
+ if (mirrorStyle(style()))
+ mSpinMirror->show(); // show rounded corners with Plastik etc.
+ else
+ mSpinMirror->hide(); // keep normal shading with other styles
+ arrange();
+}
+
+/******************************************************************************
+* Called when the extra pair of spin buttons has repainted after a style change.
+* Updates the mirror image of the spin buttons.
+*/
+void SpinBox2::updateMirror()
+{
+ mSpinMirror->setNormalButtons(QPixmap::grabWidget(mUpdown2Frame, 0, 0));
+}
+
+/******************************************************************************
+* Set the positions and sizes of all the child widgets.
+*/
+void SpinBox2::arrange()
+{
+ getMetrics();
+ QRect arrowRect = QStyle::visualRect(QRect(0, 0, wUpdown2, height()), this);
+ mUpdown2Frame->setGeometry(arrowRect);
+ mUpdown2->setGeometry(-xUpdown2, 0, mUpdown2->width(), height());
+ mSpinboxFrame->setGeometry(QStyle::visualRect(QRect(wUpdown2 + wGap, 0, width() - wUpdown2 - wGap, height()), this));
+ mSpinbox->setGeometry(-xSpinbox, 0, mSpinboxFrame->width() + xSpinbox, height());
+ mSpinMirror->resize(wUpdown2, mUpdown2->height());
+ mSpinMirror->setGeometry(arrowRect);
+//mSpinMirror->setGeometry(QStyle::visualRect(QRect(0, 11, wUpdown2, height()), this));
+ mSpinMirror->setNormalButtons(QPixmap::grabWidget(mUpdown2Frame, 0, 0));
+}
+
+/******************************************************************************
+* Calculate the width and position of the extra pair of spin buttons.
+* Style-specific adjustments are made for a better appearance.
+*/
+void SpinBox2::getMetrics() const
+{
+ QRect rect = mUpdown2->style().querySubControlMetrics(QStyle::CC_SpinWidget, mUpdown2, QStyle::SC_SpinWidgetButtonField);
+ if (style().inherits("PlastikStyle"))
+ rect.setLeft(rect.left() - 1); // Plastik excludes left border from spin widget rectangle
+ xUpdown2 = mReverseLayout ? 0 : rect.left();
+ wUpdown2 = mUpdown2->width() - rect.left();
+ xSpinbox = mSpinbox->style().querySubControlMetrics(QStyle::CC_SpinWidget, mSpinbox, QStyle::SC_SpinWidgetEditField).left();
+ wGap = 0;
+
+ // Make style-specific adjustments for a better appearance
+ if (style().inherits("QMotifPlusStyle"))
+ {
+ xSpinbox = 0; // show the edit control left border
+ wGap = 2; // leave a space to the right of the left-hand pair of spin buttons
+ }
+}
+
+/******************************************************************************
+* Called when the extra pair of spin buttons is clicked to step the value.
+* Normally this is a page step, but with a right-to-left language where the
+* button functions are reversed, this is a line step.
+*/
+void SpinBox2::stepPage(int step)
+{
+ if (abs(step) == mUpdown2->lineStep())
+ mSpinbox->setValue(mUpdown2->value());
+ else
+ {
+ // It's a shift step
+ int oldValue = mSpinbox->value();
+ if (!reverseButtons())
+ {
+ // The button pairs have the normal function.
+ // Page shift stepping - step up or down to a multiple of the
+ // shift page increment, leaving unchanged the part of the value
+ // which is the remainder from the page increment.
+ if (oldValue >= 0)
+ oldValue -= oldValue % mUpdown2->lineStep();
+ else
+ oldValue += (-oldValue) % mUpdown2->lineStep();
+ }
+ int adjust = mSpinbox->shiftStepAdjustment(oldValue, step);
+ if (adjust == -step
+ && ((step > 0 && oldValue + step >= mSpinbox->maxValue())
+ || (step < 0 && oldValue + step <= mSpinbox->minValue())))
+ adjust = 0; // allow stepping to the minimum or maximum value
+ mSpinbox->addValue(adjust + step);
+ }
+ bool focus = mSpinbox->selectOnStep() && mUpdown2->hasFocus();
+ if (focus)
+ mSpinbox->selectAll();
+
+ // Make the covering arrows image show the pressed arrow
+ mSpinMirror->redraw(QPixmap::grabWidget(mUpdown2Frame, 0, 0));
+}
+
+
+/*=============================================================================
+= Class SpinBox2::MainSpinBox
+=============================================================================*/
+
+/******************************************************************************
+* Return the initial adjustment to the value for a shift step up or down, for
+* the main (visible) spin box.
+* Normally this is a line step, but with a right-to-left language where the
+* button functions are reversed, this is a page step.
+*/
+int SpinBox2::MainSpinBox::shiftStepAdjustment(int oldValue, int shiftStep)
+{
+ if (owner->reverseButtons())
+ {
+ // The button pairs have the opposite function from normal.
+ // Page shift stepping - step up or down to a multiple of the
+ // shift page increment, leaving unchanged the part of the value
+ // which is the remainder from the page increment.
+ if (oldValue >= 0)
+ oldValue -= oldValue % lineStep();
+ else
+ oldValue += (-oldValue) % lineStep();
+ }
+ return SpinBox::shiftStepAdjustment(oldValue, shiftStep);
+}
+
+
+/*=============================================================================
+= Class ExtraSpinBox
+=============================================================================*/
+
+/******************************************************************************
+* Repaint the widget.
+* If it's the first time since a style change, tell the parent SpinBox2 to
+* update the SpinMirror with the new unpressed button image. We make the
+* presumably reasonable assumption that when a style change occurs, the spin
+* buttons are unpressed.
+*/
+void ExtraSpinBox::paintEvent(QPaintEvent* e)
+{
+ SpinBox::paintEvent(e);
+ if (mNewStylePending)
+ {
+ mNewStylePending = false;
+ emit styleUpdated();
+ }
+}
+
+
+/*=============================================================================
+= Class SpinMirror
+=============================================================================*/
+
+SpinMirror::SpinMirror(SpinBox* spinbox, QFrame* spinFrame, QWidget* parent, const char* name)
+ : QCanvasView(new QCanvas, parent, name),
+ mSpinbox(spinbox),
+ mSpinFrame(spinFrame),
+ mReadOnly(false)
+{
+ setVScrollBarMode(QScrollView::AlwaysOff);
+ setHScrollBarMode(QScrollView::AlwaysOff);
+ setFrameStyle(QFrame::NoFrame);
+
+ // Find the spin widget which is part of the spin box, in order to
+ // pass on its shift-button presses.
+ QObjectList* spinwidgets = spinbox->queryList("QSpinWidget", 0, false, true);
+ mSpinWidget = (SpinBox*)spinwidgets->getFirst();
+ delete spinwidgets;
+}
+
+void SpinMirror::setNormalButtons(const QPixmap& px)
+{
+ mNormalButtons = px;
+ redraw(mNormalButtons);
+}
+
+void SpinMirror::redraw()
+{
+ redraw(QPixmap::grabWidget(mSpinFrame, 0, 0));
+}
+
+void SpinMirror::redraw(const QPixmap& px)
+{
+ QCanvas* c = canvas();
+ c->setBackgroundPixmap(px);
+ c->setAllChanged();
+ c->update();
+}
+
+void SpinMirror::resize(int w, int h)
+{
+ canvas()->resize(w, h);
+ QCanvasView::resize(w, h);
+ resizeContents(w, h);
+ setWorldMatrix(QWMatrix(-1, 0, 0, 1, w - 1, 0)); // mirror left to right
+}
+
+/******************************************************************************
+* Pass on all mouse events to the spinbox which we're covering up.
+*/
+void SpinMirror::contentsMouseEvent(QMouseEvent* e)
+{
+ if (mReadOnly)
+ return;
+ QPoint pt = contentsToViewport(e->pos());
+ pt.setX(pt.x() + mSpinbox->upRect().left());
+ QApplication::postEvent(mSpinWidget, new QMouseEvent(e->type(), pt, e->button(), e->state()));
+
+ // If the mouse button has been pressed or released, refresh the spin buttons
+ switch (e->type())
+ {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ QTimer::singleShot(0, this, SLOT(redraw()));
+ break;
+ default:
+ break;
+ }
+}
+
+/******************************************************************************
+* Pass on all mouse events to the spinbox which we're covering up.
+*/
+void SpinMirror::contentsWheelEvent(QWheelEvent* e)
+{
+ if (mReadOnly)
+ return;
+ QPoint pt = contentsToViewport(e->pos());
+ pt.setX(pt.x() + mSpinbox->upRect().left());
+ QApplication::postEvent(mSpinWidget, new QWheelEvent(pt, e->delta(), e->state(), e->orientation()));
+}
+
+/******************************************************************************
+* Pass on to the main spinbox events which are needed to activate mouseover and
+* other graphic effects when the mouse cursor enters and leaves the widget.
+*/
+bool SpinMirror::event(QEvent* e)
+{
+ switch (e->type())
+ {
+ case QEvent::Leave:
+ case QEvent::Enter:
+ QApplication::postEvent(mSpinWidget, new QEvent(e->type()));
+ QTimer::singleShot(0, this, SLOT(redraw()));
+ break;
+ case QEvent::FocusIn:
+ mSpinbox->setFocus();
+ QTimer::singleShot(0, this, SLOT(redraw()));
+ break;
+ default:
+ break;
+ }
+ return QCanvasView::event(e);
+}
+
+
+/*=============================================================================
+= Local functions
+=============================================================================*/
+
+/******************************************************************************
+* Determine whether the extra pair of spin buttons needs to be mirrored
+* left-to-right in the specified style.
+*/
+static bool mirrorStyle(const QStyle& style)
+{
+ for (const char** s = mirrorStyles; *s; ++s)
+ if (style.inherits(*s))
+ return true;
+ return false;
+}
diff --git a/kalarm/lib/spinbox2.h b/kalarm/lib/spinbox2.h
new file mode 100644
index 00000000..772eaee7
--- /dev/null
+++ b/kalarm/lib/spinbox2.h
@@ -0,0 +1,317 @@
+/*
+ * spinbox2.h - spin box with extra pair of spin buttons (for Qt 3)
+ * Program: kalarm
+ * Copyright © 2001-2007 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 SPINBOX2_H
+#define SPINBOX2_H
+
+#include <qglobal.h>
+#include <qlineedit.h>
+
+class SpinMirror;
+class ExtraSpinBox;
+#include "spinbox.h"
+
+
+/**
+ * @short Spin box with a pair of spin buttons on either side.
+ *
+ * The SpinBox2 class provides a spin box with two pairs of spin buttons, one on either side.
+ *
+ * It is designed as a base class for implementing such facilities as time spin boxes, where
+ * the hours and minutes values are separately displayed in the edit field. When the
+ * appropriate step increments are configured, the left spin arrows can then be used to
+ * change the hours value, while the right spin arrows can be used to change the minutes
+ * value.
+ *
+ * Rather than using SpinBox2 directly for time entry, use in preference TimeSpinBox or
+ * TimeEdit classes which are tailored from SpinBox2 for this purpose.
+ *
+ * Separate step increments may optionally be specified for use when the shift key is
+ * held down. Typically these would be larger than the normal steps. Then, when the user
+ * clicks the spin buttons, he/she can increment or decrement the value faster by holding
+ * the shift key down.
+ *
+ * The widget may be set as read-only. This has the same effect as disabling it, except
+ * that its appearance is unchanged.
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+ */
+class SpinBox2 : public QFrame
+{
+ Q_OBJECT
+ public:
+ /** Constructor.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ explicit SpinBox2(QWidget* parent = 0, const char* name = 0);
+ /** Constructor.
+ * @param minValue The minimum value which the spin box can have.
+ * @param maxValue The maximum value which the spin box can have.
+ * @param step The (unshifted) step interval for the right-hand spin buttons.
+ * @param step2 The (unshifted) step interval for the left-hand spin buttons.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ SpinBox2(int minValue, int maxValue, int step = 1, int step2 = 1,
+ QWidget* parent = 0, const char* name = 0);
+ /** Sets whether the spin box can be changed by the user.
+ * @param readOnly True to set the widget read-only, false to set it read-write.
+ */
+ virtual void setReadOnly(bool readOnly);
+ /** Returns true if the widget is read only. */
+ bool isReadOnly() const { return mSpinbox->isReadOnly(); }
+ /** Sets whether the spin box value text should be selected when its value is stepped. */
+ void setSelectOnStep(bool sel) { mSpinbox->setSelectOnStep(sel); }
+ /** Sets whether the spin button pairs should be reversed for a right-to-left language.
+ * The default is for them to be reversed.
+ */
+ void setReverseWithLayout(bool reverse);
+ /** Returns whether the spin button pairs will be reversed for a right-to-left language. */
+ bool reverseButtons() const { return mReverseLayout && !mReverseWithLayout; }
+
+ /** Returns the spin box's text, including any prefix() and suffix(). */
+ QString text() const { return mSpinbox->text(); }
+ /** Returns the prefix for the spin box's text. */
+ virtual QString prefix() const { return mSpinbox->prefix(); }
+ /** Returns the suffix for the spin box's text. */
+ virtual QString suffix() const { return mSpinbox->suffix(); }
+ /** Returns the spin box's text with no prefix(), suffix() or leading or trailing whitespace. */
+ virtual QString cleanText() const { return mSpinbox->cleanText(); }
+
+ /** Sets the special-value text which, if non-null, is displayed instead of a numeric
+ * value when the current value is equal to minValue().
+ */
+ virtual void setSpecialValueText(const QString& text) { mSpinbox->setSpecialValueText(text); }
+ /** Returns the special-value text which, if non-null, is displayed instead of a numeric
+ * value when the current value is equal to minValue().
+ */
+ QString specialValueText() const { return mSpinbox->specialValueText(); }
+
+ /** Sets whether it is possible to step the value from the highest value to the
+ * lowest value and vice versa.
+ */
+ virtual void setWrapping(bool on);
+ /** Returns whether it is possible to step the value from the highest value to the
+ * lowest value and vice versa.
+ */
+ bool wrapping() const { return mSpinbox->wrapping(); }
+
+ /** Set the text alignment of the widget */
+ void setAlignment(int a) { mSpinbox->setAlignment(a); }
+ /** Sets the button symbols to use (arrows or plus/minus). */
+ virtual void setButtonSymbols(QSpinBox::ButtonSymbols);
+ /** Returns the button symbols currently in use (arrows or plus/minus). */
+ QSpinBox::ButtonSymbols buttonSymbols() const { return mSpinbox->buttonSymbols(); }
+
+ /** Sets the validator to @p v. The validator controls what keyboard input is accepted
+ * when the user is editing the value field.
+ */
+ virtual void setValidator(const QValidator* v) { mSpinbox->setValidator(v); }
+ /** Returns the current validator. The validator controls what keyboard input is accepted
+ * when the user is editing the value field.
+ */
+ const QValidator* validator() const { return mSpinbox->validator(); }
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ /** Returns the minimum value of the spin box. */
+ int minValue() const { return mMinValue; }
+ /** Returns the maximum value of the spin box. */
+ int maxValue() const { return mMaxValue; }
+ /** Sets the minimum value of the spin box. */
+ virtual void setMinValue(int val);
+ /** Sets the maximum value of the spin box. */
+ virtual void setMaxValue(int val);
+ /** Sets the minimum and maximum values of the spin box. */
+ void setRange(int minValue, int maxValue) { setMinValue(minValue); setMaxValue(maxValue); }
+ /** Returns the current value of the spin box. */
+ int value() const { return mSpinbox->value(); }
+ /** Returns the specified value clamped to the range of the spin box. */
+ int bound(int val) const;
+
+ /** Returns the geometry of the right-hand "up" button. */
+ QRect upRect() const { return mSpinbox->upRect(); }
+ /** Returns the geometry of the right-hand "down" button. */
+ QRect downRect() const { return mSpinbox->downRect(); }
+ /** Returns the geometry of the left-hand "up" button. */
+ QRect up2Rect() const;
+ /** Returns the geometry of the left-hand "down" button. */
+ QRect down2Rect() const;
+
+ /** Returns the unshifted step increment for the right-hand spin buttons,
+ * i.e. the amount by which the spin box value changes when a right-hand
+ * spin button is clicked without the shift key being pressed.
+ */
+ int lineStep() const { return mLineStep; }
+ /** Returns the shifted step increment for the right-hand spin buttons,
+ * i.e. the amount by which the spin box value changes when a right-hand
+ * spin button is clicked while the shift key is pressed.
+ */
+ int lineShiftStep() const { return mLineShiftStep; }
+ /** Returns the unshifted step increment for the left-hand spin buttons,
+ * i.e. the amount by which the spin box value changes when a left-hand
+ * spin button is clicked without the shift key being pressed.
+ */
+ int pageStep() const { return mPageStep; }
+ /** Returns the shifted step increment for the left-hand spin buttons,
+ * i.e. the amount by which the spin box value changes when a left-hand
+ * spin button is clicked while the shift key is pressed.
+ */
+ int pageShiftStep() const { return mPageShiftStep; }
+ /** Sets the unshifted step increment for the right-hand spin buttons,
+ * i.e. the amount by which the spin box value changes when a right-hand
+ * spin button is clicked without the shift key being pressed.
+ */
+ void setLineStep(int step);
+ /** Sets the unshifted step increments for the two pairs of spin buttons,
+ * i.e. the amount by which the spin box value changes when a spin button
+ * is clicked without the shift key being pressed.
+ * @param line The step increment for the right-hand spin buttons.
+ * @param page The step increment for the left-hand spin buttons.
+ */
+ void setSteps(int line, int page);
+ /** Sets the shifted step increments for the two pairs of spin buttons,
+ * i.e. the amount by which the spin box value changes when a spin button
+ * is clicked while the shift key is pressed.
+ * @param line The shift step increment for the right-hand spin buttons.
+ * @param page The shift step increment for the left-hand spin buttons.
+ */
+ void setShiftSteps(int line, int page);
+
+ /** Increments the current value by adding the unshifted step increment for
+ * the left-hand spin buttons.
+ */
+ void addPage() { addValue(mPageStep); }
+ /** Decrements the current value by subtracting the unshifted step increment for
+ * the left-hand spin buttons.
+ */
+ void subtractPage() { addValue(-mPageStep); }
+ /** Increments the current value by adding the unshifted step increment for
+ * the right-hand spin buttons.
+ */
+ void addLine() { addValue(mLineStep); }
+ /** Decrements the current value by subtracting the unshifted step increment for
+ * the right-hand spin buttons.
+ */
+ void subtractLine() { addValue(-mLineStep); }
+ /** Adjusts the current value by adding @p change. */
+ void addValue(int change) { mSpinbox->addValue(change); }
+
+ public slots:
+ /** Sets the current value to @p val. */
+ virtual void setValue(int val) { mSpinbox->setValue(val); }
+ /** Sets the prefix which is prepended to the start of the displayed text. */
+ virtual void setPrefix(const QString& text) { mSpinbox->setPrefix(text); }
+ /** Sets the suffix which is prepended to the start of the displayed text. */
+ virtual void setSuffix(const QString& text) { mSpinbox->setSuffix(text); }
+ /** Increments the current value by adding the unshifted step increment for
+ * the right-hand spin buttons.
+ */
+ virtual void stepUp() { addValue(mLineStep); }
+ /** Decrements the current value by subtracting the unshifted step increment for
+ * the right-hand spin buttons.
+ */
+ virtual void stepDown() { addValue(-mLineStep); }
+ /** Increments the current value by adding the unshifted step increment for
+ * the left-hand spin buttons.
+ */
+ virtual void pageUp() { addValue(mPageStep); }
+ /** Decrements the current value by subtracting the unshifted step increment for
+ * the left-hand spin buttons.
+ */
+ virtual void pageDown() { addValue(-mPageStep); }
+ /** Selects all the text in the spin box's editor. */
+ virtual void selectAll() { mSpinbox->selectAll(); }
+ /** Sets whether the widget is enabled. */
+ virtual void setEnabled(bool enabled);
+
+ signals:
+ /** Signal which is emitted whenever the value of the spin box changes. */
+ void valueChanged(int value);
+ /** Signal which is emitted whenever the value of the spin box changes. */
+ void valueChanged(const QString& valueText);
+
+ protected:
+ virtual QString mapValueToText(int v) { return mSpinbox->mapValToText(v); }
+ virtual int mapTextToValue(bool* ok) { return mSpinbox->mapTextToVal(ok); }
+ virtual void resizeEvent(QResizeEvent*) { arrange(); }
+ virtual void showEvent(QShowEvent*);
+ virtual void styleChange(QStyle&);
+ virtual void getMetrics() const;
+
+ mutable int wUpdown2; // width of second spin widget
+ mutable int xUpdown2; // x offset of visible area in 'mUpdown2'
+ mutable int xSpinbox; // x offset of visible area in 'mSpinbox'
+ mutable int wGap; // gap between mUpdown2Frame and mSpinboxFrame
+
+ protected slots:
+ virtual void valueChange();
+ virtual void stepPage(int);
+
+ private slots:
+ void updateMirror();
+
+ private:
+ void init();
+ void arrange();
+ int whichButton(QObject* spinWidget, const QPoint&);
+ void setShiftStepping(bool on);
+
+ // Visible spin box class.
+ // Declared here to allow use of mSpinBox in inline methods.
+ class MainSpinBox : public SpinBox
+ {
+ public:
+ MainSpinBox(SpinBox2* sb2, QWidget* parent, const char* name = 0)
+ : SpinBox(parent, name), owner(sb2) { }
+ MainSpinBox(int minValue, int maxValue, int step, SpinBox2* sb2, QWidget* parent, const char* name = 0)
+ : SpinBox(minValue, maxValue, step, parent, name), owner(sb2) { }
+ void setAlignment(int a) { editor()->setAlignment(a); }
+ virtual QString mapValueToText(int v) { return owner->mapValueToText(v); }
+ virtual int mapTextToValue(bool* ok) { return owner->mapTextToValue(ok); }
+ QString mapValToText(int v) { return SpinBox::mapValueToText(v); }
+ int mapTextToVal(bool* ok) { return SpinBox::mapTextToValue(ok); }
+ virtual int shiftStepAdjustment(int oldValue, int shiftStep);
+ private:
+ SpinBox2* owner; // owner SpinBox2
+ };
+
+ enum { NO_BUTTON = -1, UP, DOWN, UP2, DOWN2 };
+
+ static int mReverseLayout; // widgets are mirrored right to left
+ QFrame* mUpdown2Frame; // contains visible part of the extra pair of spin buttons
+ QFrame* mSpinboxFrame; // contains the main spin box
+ ExtraSpinBox* mUpdown2; // the extra pair of spin buttons
+ MainSpinBox* mSpinbox; // the visible spin box
+ SpinMirror* mSpinMirror; // image of the extra pair of spin buttons
+ int mMinValue;
+ int mMaxValue;
+ int mLineStep; // right button increment
+ int mLineShiftStep; // right button increment with shift pressed
+ int mPageStep; // left button increment
+ int mPageShiftStep; // left button increment with shift pressed
+ bool mReverseWithLayout; // reverse button positions if reverse layout (default = true)
+
+ friend class MainSpinBox;
+};
+
+#endif // SPINBOX2_H
diff --git a/kalarm/lib/spinbox2private.h b/kalarm/lib/spinbox2private.h
new file mode 100644
index 00000000..74fbeb66
--- /dev/null
+++ b/kalarm/lib/spinbox2private.h
@@ -0,0 +1,92 @@
+/*
+ * spinbox2private.h - private classes for SpinBox2 (for Qt 3)
+ * Program: kalarm
+ * Copyright © 2005,2006,2008 by David Jarvie <djarvie@kde.org>
+ *
+ * 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 SPINBOX2PRIVATE_H
+#define SPINBOX2PRIVATE_H
+
+#include <qcanvas.h>
+#include "spinbox.h"
+
+
+/*=============================================================================
+= Class ExtraSpinBox
+* Extra pair of spin buttons for SpinBox2.
+* The widget is actually a whole spin box, but only the buttons are displayed.
+=============================================================================*/
+
+class ExtraSpinBox : public SpinBox
+{
+ Q_OBJECT
+ public:
+ explicit ExtraSpinBox(QWidget* parent, const char* name = 0)
+ : SpinBox(parent, name), mNewStylePending(false) { }
+ ExtraSpinBox(int minValue, int maxValue, int step, QWidget* parent, const char* name = 0)
+ : SpinBox(minValue, maxValue, step, parent, name), mNewStylePending(false) { }
+ signals:
+ void styleUpdated();
+ protected:
+ virtual void paintEvent(QPaintEvent*);
+ virtual void styleChange(QStyle&) { mNewStylePending = true; }
+ private:
+ bool mNewStylePending; // style has changed, but not yet repainted
+};
+
+
+/*=============================================================================
+= Class SpinMirror
+* Displays the left-to-right mirror image of a pair of spin buttons, for use
+* as the extra spin buttons in a SpinBox2. All mouse clicks are passed on to
+* the real extra pair of spin buttons for processing.
+* Mirroring in this way allows styles with rounded corners to display correctly.
+=============================================================================*/
+
+class SpinMirror : public QCanvasView
+{
+ Q_OBJECT
+ public:
+ explicit SpinMirror(SpinBox*, QFrame* spinFrame, QWidget* parent = 0, const char* name = 0);
+ void setReadOnly(bool ro) { mReadOnly = ro; }
+ bool isReadOnly() const { return mReadOnly; }
+ void setNormalButtons(const QPixmap&);
+ void redraw(const QPixmap&);
+
+ public slots:
+ virtual void resize(int w, int h);
+ void redraw();
+
+ protected:
+ virtual void contentsMousePressEvent(QMouseEvent* e) { contentsMouseEvent(e); }
+ virtual void contentsMouseReleaseEvent(QMouseEvent* e) { contentsMouseEvent(e); }
+ virtual void contentsMouseMoveEvent(QMouseEvent* e) { contentsMouseEvent(e); }
+ virtual void contentsMouseDoubleClickEvent(QMouseEvent* e) { contentsMouseEvent(e); }
+ virtual void contentsWheelEvent(QWheelEvent*);
+ virtual bool event(QEvent*);
+
+ private:
+ void contentsMouseEvent(QMouseEvent*);
+
+ SpinBox* mSpinbox; // spinbox whose spin buttons are being mirrored
+ QFrame* mSpinFrame; // widget containing mSpinbox's spin buttons
+ QWidget* mSpinWidget; // spin buttons in mSpinbox
+ QPixmap mNormalButtons; // image of spin buttons in unpressed state
+ bool mReadOnly; // value cannot be changed
+};
+
+#endif // SPINBOX2PRIVATE_H
diff --git a/kalarm/lib/synchtimer.cpp b/kalarm/lib/synchtimer.cpp
new file mode 100644
index 00000000..1b681d6e
--- /dev/null
+++ b/kalarm/lib/synchtimer.cpp
@@ -0,0 +1,233 @@
+/*
+ * synchtimer.cpp - timers which synchronise to time boundaries
+ * Program: kalarm
+ * Copyright (C) 2004, 2005 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 "kalarm.h"
+#include <qtimer.h>
+#include <kdebug.h>
+#include "synchtimer.moc"
+
+
+/*=============================================================================
+= Class: SynchTimer
+= Virtual base class for application-wide timer synchronised to a time boundary.
+=============================================================================*/
+
+SynchTimer::SynchTimer()
+{
+ mTimer = new QTimer(this, "mTimer");
+}
+
+SynchTimer::~SynchTimer()
+{
+ delete mTimer;
+ mTimer = 0;
+}
+
+/******************************************************************************
+* Connect to the timer. The timer is started if necessary.
+*/
+void SynchTimer::connecT(QObject* receiver, const char* member)
+{
+ Connection connection(receiver, member);
+ if (mConnections.find(connection) != mConnections.end())
+ return; // the slot is already connected, so ignore request
+ connect(mTimer, SIGNAL(timeout()), receiver, member);
+ mConnections.append(connection);
+ if (!mTimer->isActive())
+ {
+ connect(mTimer, SIGNAL(timeout()), this, SLOT(slotTimer()));
+ start();
+ }
+}
+
+/******************************************************************************
+* Disconnect from the timer. The timer is stopped if no longer needed.
+*/
+void SynchTimer::disconnecT(QObject* receiver, const char* member)
+{
+ if (mTimer)
+ {
+ mTimer->disconnect(receiver, member);
+ if (member)
+ mConnections.remove(Connection(receiver, member));
+ else
+ {
+ for (QValueList<Connection>::Iterator it = mConnections.begin(); it != mConnections.end(); )
+ {
+ if ((*it).receiver == receiver)
+ it = mConnections.remove(it);
+ else
+ ++it;
+ }
+ }
+ if (mConnections.isEmpty())
+ {
+ mTimer->disconnect();
+ mTimer->stop();
+ }
+ }
+}
+
+
+/*=============================================================================
+= Class: MinuteTimer
+= Application-wide timer synchronised to the minute boundary.
+=============================================================================*/
+
+MinuteTimer* MinuteTimer::mInstance = 0;
+
+MinuteTimer* MinuteTimer::instance()
+{
+ if (!mInstance)
+ mInstance = new MinuteTimer;
+ return mInstance;
+}
+
+/******************************************************************************
+* Called when the timer triggers, or to start the timer.
+* Timers can under some circumstances wander off from the correct trigger time,
+* so rather than setting a 1 minute interval, calculate the correct next
+* interval each time it triggers.
+*/
+void MinuteTimer::slotTimer()
+{
+ kdDebug(5950) << "MinuteTimer::slotTimer()" << endl;
+ int interval = 62 - QTime::currentTime().second();
+ mTimer->start(interval * 1000, true); // execute a single shot
+}
+
+
+/*=============================================================================
+= Class: DailyTimer
+= Application-wide timer synchronised to midnight.
+=============================================================================*/
+
+QValueList<DailyTimer*> DailyTimer::mFixedTimers;
+
+DailyTimer::DailyTimer(const QTime& timeOfDay, bool fixed)
+ : mTime(timeOfDay),
+ mFixed(fixed)
+{
+ if (fixed)
+ mFixedTimers.append(this);
+}
+
+DailyTimer::~DailyTimer()
+{
+ if (mFixed)
+ mFixedTimers.remove(this);
+}
+
+DailyTimer* DailyTimer::fixedInstance(const QTime& timeOfDay, bool create)
+{
+ for (QValueList<DailyTimer*>::Iterator it = mFixedTimers.begin(); it != mFixedTimers.end(); ++it)
+ if ((*it)->mTime == timeOfDay)
+ return *it;
+ return create ? new DailyTimer(timeOfDay, true) : 0;
+}
+
+/******************************************************************************
+* Disconnect from the timer signal which triggers at the given fixed time of day.
+* If there are no remaining connections to that timer, it is destroyed.
+*/
+void DailyTimer::disconnect(const QTime& timeOfDay, QObject* receiver, const char* member)
+{
+ DailyTimer* timer = fixedInstance(timeOfDay, false);
+ if (!timer)
+ return;
+ timer->disconnecT(receiver, member);
+ if (!timer->hasConnections())
+ delete timer;
+}
+
+/******************************************************************************
+* Change the time at which the variable timer triggers.
+*/
+void DailyTimer::changeTime(const QTime& newTimeOfDay, bool triggerMissed)
+{
+ if (mFixed)
+ return;
+ if (mTimer->isActive())
+ {
+ mTimer->stop();
+ bool triggerNow = false;
+ if (triggerMissed)
+ {
+ QTime now = QTime::currentTime();
+ if (now >= newTimeOfDay && now < mTime)
+ {
+ // The trigger time is now earlier and it has already arrived today.
+ // Trigger a timer event immediately.
+ triggerNow = true;
+ }
+ }
+ mTime = newTimeOfDay;
+ if (triggerNow)
+ mTimer->start(0, true); // trigger immediately
+ else
+ start();
+ }
+ else
+ mTime = newTimeOfDay;
+}
+
+/******************************************************************************
+* Initialise the timer to trigger at the specified time.
+* This will either be today or tomorrow, depending on whether the trigger time
+* has already passed.
+*/
+void DailyTimer::start()
+{
+ // TIMEZONE = local time
+ QDateTime now = QDateTime::currentDateTime();
+ // Find out whether to trigger today or tomorrow.
+ // In preference, use the last trigger date to determine this, since
+ // that will avoid possible errors due to daylight savings time changes.
+ bool today;
+ if (mLastDate.isValid())
+ today = (mLastDate < now.date());
+ else
+ today = (now.time() < mTime);
+ QDateTime next;
+ if (today)
+ next = QDateTime(now.date(), mTime);
+ else
+ next = QDateTime(now.date().addDays(1), mTime);
+ uint interval = next.toTime_t() - now.toTime_t();
+ mTimer->start(interval * 1000, true); // execute a single shot
+ kdDebug(5950) << "DailyTimer::start(at " << mTime.hour() << ":" << mTime.minute() << "): interval = " << interval/3600 << ":" << (interval/60)%60 << ":" << interval%60 << endl;
+}
+
+/******************************************************************************
+* Called when the timer triggers.
+* Set the timer to trigger again tomorrow at the specified time.
+* Note that if daylight savings time changes occur, this will not be 24 hours
+* from now.
+*/
+void DailyTimer::slotTimer()
+{
+ // TIMEZONE = local time
+ QDateTime now = QDateTime::currentDateTime();
+ mLastDate = now.date();
+ QDateTime next = QDateTime(mLastDate.addDays(1), mTime);
+ uint interval = next.toTime_t() - now.toTime_t();
+ mTimer->start(interval * 1000, true); // execute a single shot
+ kdDebug(5950) << "DailyTimer::slotTimer(at " << mTime.hour() << ":" << mTime.minute() << "): interval = " << interval/3600 << ":" << (interval/60)%60 << ":" << interval%60 << endl;
+}
diff --git a/kalarm/lib/synchtimer.h b/kalarm/lib/synchtimer.h
new file mode 100644
index 00000000..551fd168
--- /dev/null
+++ b/kalarm/lib/synchtimer.h
@@ -0,0 +1,198 @@
+/*
+ * synchtimer.h - timers which synchronise to time boundaries
+ * Program: kalarm
+ * Copyright (C) 2004, 2005 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 SYNCHTIMER_H
+#define SYNCHTIMER_H
+
+/* @file synchtimer.h - timers which synchronise to time boundaries */
+
+#include <qobject.h>
+#include <qvaluelist.h>
+#include <qcstring.h>
+#include <qdatetime.h>
+class QTimer;
+
+/** SynchTimer is a virtual base class for application-wide timers synchronised
+ * to a time boundary.
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+ */
+class SynchTimer : public QObject
+{
+ Q_OBJECT
+ public:
+ virtual ~SynchTimer();
+
+ struct Connection
+ {
+ Connection() { }
+ Connection(QObject* r, const char* s) : receiver(r), slot(s) { }
+ bool operator==(const Connection& c) const { return receiver == c.receiver && slot == c.slot; }
+ QObject* receiver;
+ const QCString slot;
+ };
+ protected:
+ SynchTimer();
+ virtual void start() = 0;
+ void connecT(QObject* receiver, const char* member);
+ void disconnecT(QObject* receiver, const char* member = 0);
+ bool hasConnections() const { return !mConnections.isEmpty(); }
+
+ QTimer* mTimer;
+
+ protected slots:
+ virtual void slotTimer() = 0;
+
+ private slots:
+ void slotReceiverGone(QObject* r) { disconnecT(r); }
+
+ private:
+ SynchTimer(const SynchTimer&); // prohibit copying
+ QValueList<Connection> mConnections; // list of current clients
+};
+
+
+/** MinuteTimer is an application-wide timer synchronised to the minute boundary.
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+ */
+class MinuteTimer : public SynchTimer
+{
+ Q_OBJECT
+ public:
+ virtual ~MinuteTimer() { mInstance = 0; }
+ /** Connect to the timer signal.
+ * @param receiver Receiving object.
+ * @param member Slot to activate.
+ */
+ static void connect(QObject* receiver, const char* member)
+ { instance()->connecT(receiver, member); }
+ /** Disconnect from the timer signal.
+ * @param receiver Receiving object.
+ * @param member Slot to disconnect. If null, all slots belonging to
+ * @p receiver will be disconnected.
+ */
+ static void disconnect(QObject* receiver, const char* member = 0)
+ { if (mInstance) mInstance->disconnecT(receiver, member); }
+
+ protected:
+ MinuteTimer() : SynchTimer() { }
+ static MinuteTimer* instance();
+ virtual void start() { slotTimer(); }
+
+ protected slots:
+ virtual void slotTimer();
+
+ private:
+ static MinuteTimer* mInstance; // the one and only instance
+};
+
+
+/** DailyTimer is an application-wide timer synchronised to a specified time of day, local time.
+ *
+ * Daily timers come in two flavours: fixed, which can only be accessed through static methods,
+ * and variable, whose time can be adjusted and which are accessed through non-static methods.
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+ */
+class DailyTimer : public SynchTimer
+{
+ Q_OBJECT
+ public:
+ virtual ~DailyTimer();
+ /** Connect to the timer signal which triggers at the given fixed time of day.
+ * A new timer is created if necessary.
+ * @param timeOfDay Time at which the timer is to trigger.
+ * @param receiver Receiving object.
+ * @param member Slot to activate.
+ */
+ static void connect(const QTime& timeOfDay, QObject* receiver, const char* member)
+ { fixedInstance(timeOfDay)->connecT(receiver, member); }
+ /** Disconnect from the timer signal which triggers at the given fixed time of day.
+ * If there are no remaining connections to that timer, it is destroyed.
+ * @param timeOfDay Time at which the timer triggers.
+ * @param receiver Receiving object.
+ * @param member Slot to disconnect. If null, all slots belonging to
+ * @p receiver will be disconnected.
+ */
+ static void disconnect(const QTime& timeOfDay, QObject* receiver, const char* member = 0);
+ /** Change the time at which this variable timer triggers.
+ * @param newTimeOfDay New time at which the timer should trigger.
+ * @param triggerMissed If true, and if @p newTimeOfDay < @p oldTimeOfDay, and if the current
+ * time is between @p newTimeOfDay and @p oldTimeOfDay, the timer will be
+ * triggered immediately so as to avoid missing today's trigger.
+ */
+ void changeTime(const QTime& newTimeOfDay, bool triggerMissed = true);
+ /** Return the current time of day at which this variable timer triggers. */
+ QTime timeOfDay() const { return mTime; }
+
+ protected:
+ /** Construct an instance.
+ * The constructor is protected to ensure that for variable timers, only derived classes
+ * can construct instances. This ensures that multiple timers are not created for the same
+ * use.
+ */
+ DailyTimer(const QTime&, bool fixed);
+ /** Return the instance which triggers at the specified fixed time of day,
+ * optionally creating a new instance if necessary.
+ * @param timeOfDay Time at which the timer triggers.
+ * @param create If true, create a new instance if none already exists
+ * for @p timeOfDay.
+ * @return The instance for @p timeOfDay, or 0 if it does not exist.
+ */
+ static DailyTimer* fixedInstance(const QTime& timeOfDay, bool create = true);
+ virtual void start();
+
+ protected slots:
+ virtual void slotTimer();
+
+ private:
+ static QValueList<DailyTimer*> mFixedTimers; // list of timers whose trigger time is fixed
+ QTime mTime;
+ QDate mLastDate; // the date on which the timer was last triggered
+ bool mFixed; // the time at which the timer triggers cannot be changed
+};
+
+
+/** MidnightTimer is an application-wide timer synchronised to midnight, local time.
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+ */
+class MidnightTimer
+{
+ public:
+ /** Connect to the timer signal.
+ * @param receiver Receiving object.
+ * @param member Slot to activate.
+ */
+ static void connect(QObject* receiver, const char* member)
+ { DailyTimer::connect(QTime(0,0), receiver, member); }
+ /** Disconnect from the timer signal.
+ * @param receiver Receiving object.
+ * @param member Slot to disconnect. If null, all slots belonging to
+ * @p receiver will be disconnected.
+ */
+ static void disconnect(QObject* receiver, const char* member = 0)
+ { DailyTimer::disconnect(QTime(0,0), receiver, member); }
+
+};
+
+#endif // SYNCHTIMER_H
+
diff --git a/kalarm/lib/timeedit.cpp b/kalarm/lib/timeedit.cpp
new file mode 100644
index 00000000..8aabb90b
--- /dev/null
+++ b/kalarm/lib/timeedit.cpp
@@ -0,0 +1,207 @@
+/*
+ * timeedit.cpp - time-of-day edit widget, with AM/PM shown depending on locale
+ * Program: kalarm
+ * Copyright (C) 2004 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 "kalarm.h"
+
+#include <kglobal.h>
+#include <klocale.h>
+
+#include "combobox.h"
+#include "timespinbox.h"
+#include "timeedit.moc"
+
+
+TimeEdit::TimeEdit(QWidget* parent, const char* name)
+ : QHBox(parent, name),
+ mAmPm(0),
+ mAmIndex(-1),
+ mPmIndex(-1),
+ mReadOnly(false)
+{
+ bool use12hour = KGlobal::locale()->use12Clock();
+ mSpinBox = new TimeSpinBox(!use12hour, this);
+ mSpinBox->setFixedSize(mSpinBox->sizeHint());
+ connect(mSpinBox, SIGNAL(valueChanged(int)), SLOT(slotValueChanged(int)));
+ if (use12hour)
+ {
+ mAmPm = new ComboBox(this);
+ setAmPmCombo(1, 1); // add "am" and "pm" options to the combo box
+ mAmPm->setFixedSize(mAmPm->sizeHint());
+ connect(mAmPm, SIGNAL(highlighted(int)), SLOT(slotAmPmChanged(int)));
+ }
+}
+
+void TimeEdit::setReadOnly(bool ro)
+{
+ if (ro != mReadOnly)
+ {
+ mReadOnly = ro;
+ mSpinBox->setReadOnly(ro);
+ if (mAmPm)
+ mAmPm->setReadOnly(ro);
+ }
+}
+
+int TimeEdit::value() const
+{
+ return mSpinBox->value();
+}
+
+bool TimeEdit::isValid() const
+{
+ return mSpinBox->isValid();
+}
+
+/******************************************************************************
+ * Set the edit value as valid or invalid.
+ * If newly invalid, the value is displayed as asterisks.
+ * If newly valid, the value is set to the minimum value.
+ */
+void TimeEdit::setValid(bool valid)
+{
+ bool oldValid = mSpinBox->isValid();
+ if (valid && !oldValid
+ || !valid && oldValid)
+ {
+ mSpinBox->setValid(valid);
+ if (mAmPm)
+ mAmPm->setCurrentItem(0);
+ }
+}
+
+/******************************************************************************
+ * Set the widget's value.
+ */
+void TimeEdit::setValue(int minutes)
+{
+ if (mAmPm)
+ {
+ int i = (minutes >= 720) ? mPmIndex : mAmIndex;
+ mAmPm->setCurrentItem(i >= 0 ? i : 0);
+ }
+ mSpinBox->setValue(minutes);
+}
+
+bool TimeEdit::wrapping() const
+{
+ return mSpinBox->wrapping();
+}
+
+void TimeEdit::setWrapping(bool on)
+{
+ mSpinBox->setWrapping(on);
+}
+
+int TimeEdit::minValue() const
+{
+ return mSpinBox->minValue();
+}
+
+int TimeEdit::maxValue() const
+{
+ return mSpinBox->maxValue();
+}
+
+void TimeEdit::setMinValue(int minutes)
+{
+ if (mAmPm)
+ setAmPmCombo((minutes < 720 ? 1 : 0), -1); // insert/remove "am" in combo box
+ mSpinBox->setMinValue(minutes);
+}
+
+void TimeEdit::setMaxValue(int minutes)
+{
+ if (mAmPm)
+ setAmPmCombo(-1, (minutes < 720 ? 0 : 1)); // insert/remove "pm" in combo box
+ mSpinBox->setMaxValue(minutes);
+}
+
+/******************************************************************************
+ * Called when the spin box value has changed.
+ */
+void TimeEdit::slotValueChanged(int value)
+{
+ if (mAmPm)
+ {
+ bool pm = (mAmPm->currentItem() == mPmIndex);
+ if (pm && value < 720)
+ mAmPm->setCurrentItem(mAmIndex);
+ else if (!pm && value >= 720)
+ mAmPm->setCurrentItem(mPmIndex);
+ }
+ emit valueChanged(value);
+}
+
+/******************************************************************************
+ * Called when a new selection has been made by the user in the AM/PM combo box.
+ * Adjust the current time value by 12 hours.
+ */
+void TimeEdit::slotAmPmChanged(int item)
+{
+ if (mAmPm)
+ {
+ int value = mSpinBox->value();
+ if (item == mPmIndex && value < 720)
+ mSpinBox->setValue(value + 720);
+ else if (item != mPmIndex && value >= 720)
+ mSpinBox->setValue(value - 720);
+ }
+}
+
+/******************************************************************************
+ * Set up the AM/PM combo box to contain the specified items.
+ */
+void TimeEdit::setAmPmCombo(int am, int pm)
+{
+ if (am > 0 && mAmIndex < 0)
+ {
+ // Insert "am"
+ mAmIndex = 0;
+ mAmPm->insertItem(KGlobal::locale()->translate("am"), mAmIndex);
+ if (mPmIndex >= 0)
+ mPmIndex = 1;
+ mAmPm->setCurrentItem(mPmIndex >= 0 ? mPmIndex : mAmIndex);
+ }
+ else if (am == 0 && mAmIndex >= 0)
+ {
+ // Remove "am"
+ mAmPm->removeItem(mAmIndex);
+ mAmIndex = -1;
+ if (mPmIndex >= 0)
+ mPmIndex = 0;
+ mAmPm->setCurrentItem(mPmIndex);
+ }
+
+ if (pm > 0 && mPmIndex < 0)
+ {
+ // Insert "pm"
+ mPmIndex = mAmIndex + 1;
+ mAmPm->insertItem(KGlobal::locale()->translate("pm"), mPmIndex);
+ if (mAmIndex < 0)
+ mAmPm->setCurrentItem(mPmIndex);
+ }
+ else if (pm == 0 && mPmIndex >= 0)
+ {
+ // Remove "pm"
+ mAmPm->removeItem(mPmIndex);
+ mPmIndex = -1;
+ mAmPm->setCurrentItem(mAmIndex);
+ }
+}
diff --git a/kalarm/lib/timeedit.h b/kalarm/lib/timeedit.h
new file mode 100644
index 00000000..5888c766
--- /dev/null
+++ b/kalarm/lib/timeedit.h
@@ -0,0 +1,122 @@
+/*
+ * timeedit.h - time-of-day edit widget, with AM/PM shown depending on locale
+ * Program: kalarm
+ * Copyright (C) 2004 by David Jarvie <software@astrojar.org.uk>
+ *
+ * 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 TIMEEDIT_H
+#define TIMEEDIT_H
+
+#include <qdatetime.h>
+#include <qhbox.h>
+
+class ComboBox;
+class TimeSpinBox;
+
+
+/**
+ * @short Widget to enter a time of day.
+ *
+ * The TimeEdit class provides a widget to enter a time of day in hours and minutes,
+ * using a 12- or 24-hour clock according to the user's locale settings.
+ *
+ * It displays a TimeSpinBox widget to enter hours and minutes. If a 12-hour clock is
+ * being used, it also displays a combo box to select am or pm.
+ *
+ * TimeSpinBox displays a spin box with two pairs of spin buttons, one for hours and
+ * one for minutes. It provides accelerated stepping using the spin buttons, when the
+ * shift key is held down (inherited from SpinBox2). The default shift steps are 5
+ * minutes and 6 hours.
+ *
+ * The widget may be set as read-only. This has the same effect as disabling it, except
+ * that its appearance is unchanged.
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+ */
+class TimeEdit : public QHBox
+{
+ Q_OBJECT
+ public:
+ /** Constructor.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ explicit TimeEdit(QWidget* parent = 0, const char* name = 0);
+ /** Returns true if the widget is read only. */
+ bool isReadOnly() const { return mReadOnly; }
+ /** Sets whether the widget is read-only for the user. If read-only,
+ * the time cannot be edited and the spin buttons and am/pm combo box
+ * are inactive.
+ * @param readOnly True to set the widget read-only, false to set it read-write.
+ */
+ virtual void setReadOnly(bool readOnly);
+ /** Returns true if the widget contains a valid value. */
+ bool isValid() const;
+ /** Sets whether the edit value is valid.
+ * If newly invalid, the value is displayed as asterisks.
+ * If newly valid, the value is set to the minimum value.
+ * @param valid True to set the value valid, false to set it invalid.
+ */
+ void setValid(bool valid);
+ /** Returns the entered time as a value in minutes. */
+ int value() const;
+ /** Returns the entered time as a QTime value. */
+ QTime time() const { int m = value(); return QTime(m/60, m%60); }
+ /** Returns true if it is possible to step the value from the highest value to the lowest value and vice versa. */
+ bool wrapping() const;
+ /** Sets whether it is possible to step the value from the highest value to the lowest value and vice versa.
+ * @param on True to enable wrapping, else false.
+ */
+ void setWrapping(bool on);
+ /** Returns the minimum value of the widget in minutes. */
+ int minValue() const;
+ /** Returns the maximum value of the widget in minutes. */
+ int maxValue() const;
+ /** Returns the maximum value of the widget as a QTime value. */
+ QTime maxTime() const { int mv = maxValue(); return QTime(mv/60, mv%60); }
+ /** Sets the minimum value of the widget. */
+ void setMinValue(int minutes);
+ /** Sets the maximum value of the widget. */
+ void setMaxValue(int minutes);
+ /** Sets the maximum value of the widget. */
+ void setMaxValue(const QTime& time) { setMaxValue(time.hour()*60 + time.minute()); }
+ public slots:
+ /** Sets the value of the widget. */
+ virtual void setValue(int minutes);
+ /** Sets the value of the widget. */
+ void setValue(const QTime& t) { setValue(t.hour()*60 + t.minute()); }
+ signals:
+ /** This signal is emitted every time the value of the widget changes
+ * (for whatever reason).
+ * @param minutes The new value.
+ */
+ void valueChanged(int minutes);
+
+ private slots:
+ void slotValueChanged(int);
+ void slotAmPmChanged(int item);
+ private:
+ void setAmPmCombo(int am, int pm);
+
+ TimeSpinBox* mSpinBox; // always holds the 24-hour time
+ ComboBox* mAmPm;
+ int mAmIndex; // mAmPm index to "am", or -1 if none
+ int mPmIndex; // mAmPm index to "pm", or -1 if none
+ bool mReadOnly; // the widget is read only
+};
+
+#endif // TIMEEDIT_H
diff --git a/kalarm/lib/timeperiod.cpp b/kalarm/lib/timeperiod.cpp
new file mode 100644
index 00000000..22a7c317
--- /dev/null
+++ b/kalarm/lib/timeperiod.cpp
@@ -0,0 +1,384 @@
+/*
+ * timeperiod.h - time period data entry widget
+ * Program: kalarm
+ * Copyright © 2003,2004,2007,2008 by David Jarvie <djarvie@kde.org>
+ *
+ * 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 "kalarm.h"
+
+#include <qwidgetstack.h>
+#include <qwhatsthis.h>
+
+#include <klocale.h>
+#include <kdialog.h>
+
+#include "combobox.h"
+#include "spinbox.h"
+#include "timespinbox.h"
+#include "timeperiod.moc"
+
+
+// Collect these widget labels together to ensure consistent wording and
+// translations across different modules.
+QString TimePeriod::i18n_minutes() { return i18n("minutes"); }
+QString TimePeriod::i18n_Minutes() { return i18n("Minutes"); }
+QString TimePeriod::i18n_hours_mins() { return i18n("hours/minutes"); }
+QString TimePeriod::i18n_Hours_Mins() { return i18n("Hours/Minutes"); }
+QString TimePeriod::i18n_days() { return i18n("days"); }
+QString TimePeriod::i18n_Days() { return i18n("Days"); }
+QString TimePeriod::i18n_weeks() { return i18n("weeks"); }
+QString TimePeriod::i18n_Weeks() { return i18n("Weeks"); }
+
+static const int maxMinutes = 1000*60-1; // absolute maximum value for hours:minutes = 999H59M
+
+/*=============================================================================
+= Class TimePeriod
+= Contains a time unit combo box, plus a time spinbox, to select a time period.
+=============================================================================*/
+
+TimePeriod::TimePeriod(bool allowHourMinute, QWidget* parent, const char* name)
+ : QHBox(parent, name),
+ mMaxDays(9999),
+ mNoHourMinute(!allowHourMinute),
+ mReadOnly(false)
+{
+ setSpacing(KDialog::spacingHint());
+
+ mSpinStack = new QWidgetStack(this);
+ mSpinBox = new SpinBox(mSpinStack);
+ mSpinBox->setLineStep(1);
+ mSpinBox->setLineShiftStep(10);
+ mSpinBox->setRange(1, mMaxDays);
+ connect(mSpinBox, SIGNAL(valueChanged(int)), SLOT(slotDaysChanged(int)));
+ mSpinStack->addWidget(mSpinBox, 0);
+
+ mTimeSpinBox = new TimeSpinBox(0, 99999, mSpinStack);
+ mTimeSpinBox->setRange(1, maxMinutes); // max 999H59M
+ connect(mTimeSpinBox, SIGNAL(valueChanged(int)), SLOT(slotTimeChanged(int)));
+ mSpinStack->addWidget(mTimeSpinBox, 1);
+
+ mSpinStack->setFixedSize(mSpinBox->sizeHint().expandedTo(mTimeSpinBox->sizeHint()));
+ mHourMinuteRaised = mNoHourMinute;
+ showHourMin(!mNoHourMinute);
+
+ mUnitsCombo = new ComboBox(false, this);
+ if (mNoHourMinute)
+ mDateOnlyOffset = 2;
+ else
+ {
+ mDateOnlyOffset = 0;
+ mUnitsCombo->insertItem(i18n_minutes());
+ mUnitsCombo->insertItem(i18n_hours_mins());
+ }
+ mUnitsCombo->insertItem(i18n_days());
+ mUnitsCombo->insertItem(i18n_weeks());
+ mMaxUnitShown = WEEKS;
+ mUnitsCombo->setFixedSize(mUnitsCombo->sizeHint());
+ connect(mUnitsCombo, SIGNAL(activated(int)), SLOT(slotUnitsSelected(int)));
+
+ setFocusProxy(mUnitsCombo);
+ setTabOrder(mUnitsCombo, mSpinStack);
+}
+
+void TimePeriod::setReadOnly(bool ro)
+{
+ if (ro != mReadOnly)
+ {
+ mReadOnly = ro;
+ mSpinBox->setReadOnly(ro);
+ mTimeSpinBox->setReadOnly(ro);
+ mUnitsCombo->setReadOnly(ro);
+ }
+}
+
+/******************************************************************************
+* Set whether the editor text is to be selected whenever spin buttons are
+* clicked. Default is to select them.
+*/
+void TimePeriod::setSelectOnStep(bool sel)
+{
+ mSpinBox->setSelectOnStep(sel);
+ mTimeSpinBox->setSelectOnStep(sel);
+}
+
+/******************************************************************************
+* Set the input focus on the count field.
+*/
+void TimePeriod::setFocusOnCount()
+{
+ mSpinStack->setFocus();
+}
+
+/******************************************************************************
+* Set the maximum values for the hours:minutes and days/weeks spinboxes.
+* If 'hourmin' = 0, the hours:minutes maximum is left unchanged.
+*/
+void TimePeriod::setMaximum(int hourmin, int days)
+{
+ int oldmins = minutes();
+ if (hourmin > 0)
+ {
+ if (hourmin > maxMinutes)
+ hourmin = maxMinutes;
+ mTimeSpinBox->setRange(1, hourmin);
+ }
+ mMaxDays = (days >= 0) ? days : 0;
+ adjustDayWeekShown();
+ setUnitRange();
+ int mins = minutes();
+ if (mins != oldmins)
+ emit valueChanged(mins);
+}
+
+/******************************************************************************
+ * Get the specified number of minutes.
+ * Reply = 0 if error.
+ */
+int TimePeriod::minutes() const
+{
+ int factor = 0;
+ switch (mUnitsCombo->currentItem() + mDateOnlyOffset)
+ {
+ case HOURS_MINUTES:
+ return mTimeSpinBox->value();
+ case MINUTES: factor = 1; break;
+ case DAYS: factor = 24*60; break;
+ case WEEKS: factor = 7*24*60; break;
+ }
+ return mSpinBox->value() * factor;
+}
+
+/******************************************************************************
+* Initialise the controls with a specified time period.
+* The time unit combo-box is initialised to 'defaultUnits', but if 'dateOnly'
+* is true, it will never be initialised to minutes or hours/minutes.
+*/
+void TimePeriod::setMinutes(int mins, bool dateOnly, TimePeriod::Units defaultUnits)
+{
+ int oldmins = minutes();
+ if (!dateOnly && mNoHourMinute)
+ dateOnly = true;
+ int item;
+ if (mins)
+ {
+ int count = mins;
+ if (mins % (24*60))
+ item = (defaultUnits == MINUTES && count <= mSpinBox->maxValue()) ? MINUTES : HOURS_MINUTES;
+ else if (mins % (7*24*60))
+ {
+ item = DAYS;
+ count = mins / (24*60);
+ }
+ else
+ {
+ item = WEEKS;
+ count = mins / (7*24*60);
+ }
+ if (item < mDateOnlyOffset)
+ item = mDateOnlyOffset;
+ else if (item > mMaxUnitShown)
+ item = mMaxUnitShown;
+ mUnitsCombo->setCurrentItem(item - mDateOnlyOffset);
+ if (item == HOURS_MINUTES)
+ mTimeSpinBox->setValue(count);
+ else
+ mSpinBox->setValue(count);
+ item = setDateOnly(mins, dateOnly, false);
+ }
+ else
+ {
+ item = defaultUnits;
+ if (item < mDateOnlyOffset)
+ item = mDateOnlyOffset;
+ else if (item > mMaxUnitShown)
+ item = mMaxUnitShown;
+ mUnitsCombo->setCurrentItem(item - mDateOnlyOffset);
+ if (dateOnly && !mDateOnlyOffset || !dateOnly && mDateOnlyOffset)
+ item = setDateOnly(mins, dateOnly, false);
+ }
+ showHourMin(item == HOURS_MINUTES && !mNoHourMinute);
+
+ int newmins = minutes();
+ if (newmins != oldmins)
+ emit valueChanged(newmins);
+}
+
+/******************************************************************************
+* Enable/disable hours/minutes units (if hours/minutes were permitted in the
+* constructor).
+*/
+TimePeriod::Units TimePeriod::setDateOnly(int mins, bool dateOnly, bool signal)
+{
+ int oldmins = 0;
+ if (signal)
+ oldmins = minutes();
+ int index = mUnitsCombo->currentItem();
+ Units units = static_cast<Units>(index + mDateOnlyOffset);
+ if (!mNoHourMinute)
+ {
+ if (!dateOnly && mDateOnlyOffset)
+ {
+ // Change from date-only to allow hours/minutes
+ mUnitsCombo->insertItem(i18n_minutes(), 0);
+ mUnitsCombo->insertItem(i18n_hours_mins(), 1);
+ mDateOnlyOffset = 0;
+ adjustDayWeekShown();
+ mUnitsCombo->setCurrentItem(index += 2);
+ }
+ else if (dateOnly && !mDateOnlyOffset)
+ {
+ // Change from allowing hours/minutes to date-only
+ mUnitsCombo->removeItem(0);
+ mUnitsCombo->removeItem(0);
+ mDateOnlyOffset = 2;
+ if (index > 2)
+ index -= 2;
+ else
+ index = 0;
+ adjustDayWeekShown();
+ mUnitsCombo->setCurrentItem(index);
+ if (units == HOURS_MINUTES || units == MINUTES)
+ {
+ // Set units to days and round up the warning period
+ units = DAYS;
+ mUnitsCombo->setCurrentItem(DAYS - mDateOnlyOffset);
+ mSpinBox->setValue((mins + 1439) / 1440);
+ }
+ showHourMin(false);
+ }
+ }
+
+ if (signal)
+ {
+ int newmins = minutes();
+ if (newmins != oldmins)
+ emit valueChanged(newmins);
+ }
+ return units;
+}
+
+/******************************************************************************
+* Adjust the days/weeks units shown to suit the maximum days limit.
+*/
+void TimePeriod::adjustDayWeekShown()
+{
+ Units newMaxUnitShown = (mMaxDays >= 7) ? WEEKS : (mMaxDays || mDateOnlyOffset) ? DAYS : HOURS_MINUTES;
+ if (newMaxUnitShown > mMaxUnitShown)
+ {
+ if (mMaxUnitShown < DAYS)
+ mUnitsCombo->insertItem(i18n_days());
+ if (newMaxUnitShown == WEEKS)
+ mUnitsCombo->insertItem(i18n_weeks());
+ }
+ else if (newMaxUnitShown < mMaxUnitShown)
+ {
+ if (mMaxUnitShown == WEEKS)
+ mUnitsCombo->removeItem(WEEKS - mDateOnlyOffset);
+ if (newMaxUnitShown < DAYS)
+ mUnitsCombo->removeItem(DAYS - mDateOnlyOffset);
+ }
+ mMaxUnitShown = newMaxUnitShown;
+}
+
+/******************************************************************************
+* Set the maximum value which may be entered into the day/week count field,
+* depending on the current unit selection.
+*/
+void TimePeriod::setUnitRange()
+{
+ int maxval;
+ switch (static_cast<Units>(mUnitsCombo->currentItem() + mDateOnlyOffset))
+ {
+ case WEEKS:
+ maxval = mMaxDays / 7;
+ if (maxval)
+ break;
+ mUnitsCombo->setCurrentItem(DAYS - mDateOnlyOffset);
+ // fall through to DAYS
+ case DAYS:
+ maxval = mMaxDays ? mMaxDays : 1;
+ break;
+ case MINUTES:
+ maxval = mTimeSpinBox->maxValue();
+ break;
+ case HOURS_MINUTES:
+ default:
+ return;
+ }
+ mSpinBox->setRange(1, maxval);
+}
+
+/******************************************************************************
+* Called when a new item is made current in the time units combo box.
+*/
+void TimePeriod::slotUnitsSelected(int index)
+{
+ setUnitRange();
+ showHourMin(index + mDateOnlyOffset == HOURS_MINUTES);
+ emit valueChanged(minutes());
+}
+
+/******************************************************************************
+* Called when the value of the days/weeks spin box changes.
+*/
+void TimePeriod::slotDaysChanged(int)
+{
+ if (!mHourMinuteRaised)
+ emit valueChanged(minutes());
+}
+
+/******************************************************************************
+* Called when the value of the time spin box changes.
+*/
+void TimePeriod::slotTimeChanged(int value)
+{
+ if (mHourMinuteRaised)
+ emit valueChanged(value);
+}
+
+/******************************************************************************
+ * Set the currently displayed count widget.
+ */
+void TimePeriod::showHourMin(bool hourMinute)
+{
+ if (hourMinute != mHourMinuteRaised)
+ {
+ mHourMinuteRaised = hourMinute;
+ if (hourMinute)
+ {
+ mSpinStack->raiseWidget(mTimeSpinBox);
+ mSpinStack->setFocusProxy(mTimeSpinBox);
+ }
+ else
+ {
+ mSpinStack->raiseWidget(mSpinBox);
+ mSpinStack->setFocusProxy(mSpinBox);
+ }
+ }
+}
+
+/******************************************************************************
+ * Set separate WhatsThis texts for the count spinboxes and the units combobox.
+ * If the hours:minutes text is omitted, both spinboxes are set to the same
+ * WhatsThis text.
+ */
+void TimePeriod::setWhatsThis(const QString& units, const QString& dayWeek, const QString& hourMin)
+{
+ QWhatsThis::add(mUnitsCombo, units);
+ QWhatsThis::add(mSpinBox, dayWeek);
+ QWhatsThis::add(mTimeSpinBox, (hourMin.isNull() ? dayWeek : hourMin));
+}
diff --git a/kalarm/lib/timeperiod.h b/kalarm/lib/timeperiod.h
new file mode 100644
index 00000000..ef1a3b5c
--- /dev/null
+++ b/kalarm/lib/timeperiod.h
@@ -0,0 +1,146 @@
+/*
+ * timeperiod.cpp - time period data entry widget
+ * Program: kalarm
+ * Copyright © 2003,2004,2007,2008 by David Jarvie <djarvie@kde.org>
+ *
+ * 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 TIMEPERIOD_H
+#define TIMEPERIOD_H
+
+#include <qhbox.h>
+#include <qstring.h>
+
+class QWidgetStack;
+class ComboBox;
+class SpinBox;
+class TimeSpinBox;
+
+
+/**
+ * @short Time period entry widget.
+ *
+ * The TimePeriod class provides a widget for entering a time period as a number of
+ * weeks, days, hours and minutes, or minutes.
+ *
+ * It displays a combo box to select the time units (weeks, days, hours and minutes, or
+ * minutes) alongside a spin box to enter the number of units. The type of spin box
+ * displayed alters according to the units selection: day, week and minute values are
+ * entered in a normal spin box, while hours and minutes are entered in a time spin box
+ * (with two pairs of spin buttons, one for hours and one for minutes).
+ *
+ * The widget may be set as read-only. This has the same effect as disabling it, except
+ * that its appearance is unchanged.
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+ */
+class TimePeriod : public QHBox
+{
+ Q_OBJECT
+ public:
+ /** Units for the time period.
+ * @li MINUTES - the time period is entered as a number of minutes.
+ * @li HOURS_MINUTES - the time period is entered as an hours/minutes value.
+ * @li DAYS - the time period is entered as a number of days.
+ * @li WEEKS - the time period is entered as a number of weeks.
+ */
+ enum Units { MINUTES, HOURS_MINUTES, DAYS, WEEKS };
+
+ /** Constructor.
+ * @param allowMinute Set false to prevent hours/minutes or minutes from
+ * being allowed as units; only days and weeks can ever be used,
+ * regardless of other method calls. Set true to allow minutes,
+ * hours/minutes, days or weeks as units.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ TimePeriod(bool allowMinute, QWidget* parent, const char* name = 0);
+ /** Returns true if the widget is read only. */
+ bool isReadOnly() const { return mReadOnly; }
+ /** Sets whether the widget is read-only for the user. If read-only,
+ * the time period cannot be edited and the units combo box is inactive.
+ * @param readOnly True to set the widget read-only, false to set it read-write.
+ */
+ virtual void setReadOnly(bool readOnly);
+ /** Gets the entered time period expressed in minutes. */
+ int minutes() const;
+ /** Initialises the time period value.
+ * @param minutes The value of the time period to set, expressed as a number of minutes.
+ * @param dateOnly True to restrict the units available in the combo box to days or weeks.
+ * @param defaultUnits The units to display initially in the combo box.
+ */
+ void setMinutes(int minutes, bool dateOnly, Units defaultUnits);
+ /** Enables or disables minutes and hours/minutes units in the combo box. To
+ * disable minutes and hours/minutes, set @p dateOnly true; to enable minutes
+ * and hours/minutes, set @p dateOnly false. But note that minutes and
+ * hours/minutes cannot be enabled if it was disallowed in the constructor.
+ */
+ void setDateOnly(bool dateOnly) { setDateOnly(minutes(), dateOnly, true); }
+ /** Sets the maximum values for the minutes and hours/minutes, and days/weeks
+ * spin boxes.
+ * Set @p hourmin = 0 to leave the minutes and hours/minutes maximum unchanged.
+ */
+ void setMaximum(int hourmin, int days);
+ /** Sets whether the editor text is to be selected whenever spin buttons are
+ * clicked. The default is to select it.
+ */
+ void setSelectOnStep(bool select);
+ /** Sets the input focus to the count field. */
+ void setFocusOnCount();
+ /** Sets separate WhatsThis texts for the count spin boxes and the units combo box.
+ * If @p hourMin is omitted, both spin boxes are set to the same WhatsThis text.
+ */
+ void setWhatsThis(const QString& units, const QString& dayWeek, const QString& hourMin = QString::null);
+
+ static QString i18n_minutes(); // text of 'minutes' units, lower case
+ static QString i18n_Minutes(); // text of 'Minutes' units, initial capitals
+ static QString i18n_hours_mins(); // text of 'hours/minutes' units, lower case
+ static QString i18n_Hours_Mins(); // text of 'Hours/Minutes' units, initial capitals
+ static QString i18n_days(); // text of 'days' units, lower case
+ static QString i18n_Days(); // text of 'Days' units, initial capital
+ static QString i18n_weeks(); // text of 'weeks' units, lower case
+ static QString i18n_Weeks(); // text of 'Weeks' units, initial capital
+
+ signals:
+ /** This signal is emitted whenever the value held in the widget changes.
+ * @param minutes The current value of the time period, expressed in minutes.
+ */
+ void valueChanged(int minutes); // value has changed
+
+ private slots:
+ void slotUnitsSelected(int index);
+ void slotDaysChanged(int);
+ void slotTimeChanged(int minutes);
+
+ private:
+ Units setDateOnly(int minutes, bool dateOnly, bool signal);
+ void setUnitRange();
+ void showHourMin(bool hourMin);
+ void adjustDayWeekShown();
+
+ QWidgetStack* mSpinStack; // displays either the days/weeks or hours:minutes spinbox
+ SpinBox* mSpinBox; // the minutes/days/weeks value spinbox
+ TimeSpinBox* mTimeSpinBox; // the hours:minutes value spinbox
+ ComboBox* mUnitsCombo;
+ int mMaxDays; // maximum day count
+ int mDateOnlyOffset; // for mUnitsCombo: 2 if minutes & hours/minutes are disabled, else 0
+ Units mMaxUnitShown; // for mUnitsCombo: maximum units shown
+ bool mNoHourMinute; // hours/minutes cannot be displayed, ever
+ bool mReadOnly; // the widget is read only
+ bool mHourMinuteRaised; // hours:minutes spinbox is currently displayed
+};
+
+#endif // TIMEPERIOD_H
diff --git a/kalarm/lib/timespinbox.cpp b/kalarm/lib/timespinbox.cpp
new file mode 100644
index 00000000..7a22cd23
--- /dev/null
+++ b/kalarm/lib/timespinbox.cpp
@@ -0,0 +1,364 @@
+/*
+ * timespinbox.cpp - time spinbox widget
+ * Program: kalarm
+ * Copyright © 2001-2004,2007,2008 by David Jarvie <djarvie@kde.org>
+ *
+ * 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 "kalarm.h"
+
+#include <qvalidator.h>
+#include <qlineedit.h>
+#include <klocale.h>
+
+#include "timespinbox.moc"
+
+
+class TimeSpinBox::TimeValidator : public QValidator
+{
+ public:
+ TimeValidator(int minMin, int maxMin, QWidget* parent, const char* name = 0)
+ : QValidator(parent, name),
+ minMinute(minMin), maxMinute(maxMin), m12Hour(false), mPm(false) { }
+ virtual State validate(QString&, int&) const;
+ int minMinute, maxMinute;
+ bool m12Hour;
+ bool mPm;
+};
+
+
+/*=============================================================================
+= Class TimeSpinBox
+= This is a spin box displaying a time in the format hh:mm, with a pair of
+= spin buttons for each of the hour and minute values.
+= It can operate in 3 modes:
+= 1) a time of day using the 24-hour clock.
+= 2) a time of day using the 12-hour clock. The value is held as 0:00 - 23:59,
+= but is displayed as 12:00 - 11:59. This is for use in a TimeEdit widget.
+= 3) a length of time, not restricted to the length of a day.
+=============================================================================*/
+
+/******************************************************************************
+ * Construct a wrapping 00:00 - 23:59, or 12:00 - 11:59 time spin box.
+ */
+TimeSpinBox::TimeSpinBox(bool use24hour, QWidget* parent, const char* name)
+ : SpinBox2(0, 1439, 1, 60, parent, name),
+ mMinimumValue(0),
+ m12Hour(!use24hour),
+ mPm(false),
+ mInvalid(false),
+ mEnteredSetValue(false)
+{
+ mValidator = new TimeValidator(0, 1439, this, "TimeSpinBox validator");
+ mValidator->m12Hour = m12Hour;
+ setValidator(mValidator);
+ setWrapping(true);
+ setReverseWithLayout(false); // keep buttons the same way round even if right-to-left language
+ setShiftSteps(5, 360); // shift-left button increments 5 min / 6 hours
+ setSelectOnStep(false);
+ setAlignment(Qt::AlignHCenter);
+ connect(this, SIGNAL(valueChanged(int)), SLOT(slotValueChanged(int)));
+}
+
+/******************************************************************************
+ * Construct a non-wrapping time spin box.
+ */
+TimeSpinBox::TimeSpinBox(int minMinute, int maxMinute, QWidget* parent, const char* name)
+ : SpinBox2(minMinute, maxMinute, 1, 60, parent, name),
+ mMinimumValue(minMinute),
+ m12Hour(false),
+ mInvalid(false),
+ mEnteredSetValue(false)
+{
+ mValidator = new TimeValidator(minMinute, maxMinute, this, "TimeSpinBox validator");
+ setValidator(mValidator);
+ setReverseWithLayout(false); // keep buttons the same way round even if right-to-left language
+ setShiftSteps(5, 300); // shift-left button increments 5 min / 5 hours
+ setSelectOnStep(false);
+ setAlignment(QApplication::reverseLayout() ? Qt::AlignLeft : Qt::AlignRight);
+}
+
+QString TimeSpinBox::shiftWhatsThis()
+{
+ return i18n("Press the Shift key while clicking the spin buttons to adjust the time by a larger step (6 hours / 5 minutes).");
+}
+
+QTime TimeSpinBox::time() const
+{
+ return QTime(value() / 60, value() % 60);
+}
+
+QString TimeSpinBox::mapValueToText(int v)
+{
+ if (m12Hour)
+ {
+ if (v < 60)
+ v += 720; // convert 0:nn to 12:nn
+ else if (v >= 780)
+ v -= 720; // convert 13 - 23 hours to 1 - 11
+ }
+ QString s;
+ s.sprintf((wrapping() ? "%02d:%02d" : "%d:%02d"), v/60, v%60);
+ return s;
+}
+
+/******************************************************************************
+ * Convert the user-entered text to a value in minutes.
+ * The allowed formats are:
+ * [hour]:[minute], where minute must be non-blank, or
+ * hhmm, 4 digits, where hour < 24.
+ */
+int TimeSpinBox::mapTextToValue(bool* ok)
+{
+ QString text = cleanText();
+ int colon = text.find(':');
+ if (colon >= 0)
+ {
+ // [h]:m format for any time value
+ QString hour = text.left(colon).stripWhiteSpace();
+ QString minute = text.mid(colon + 1).stripWhiteSpace();
+ if (!minute.isEmpty())
+ {
+ bool okmin;
+ bool okhour = true;
+ int m = minute.toUInt(&okmin);
+ int h = 0;
+ if (!hour.isEmpty())
+ h = hour.toUInt(&okhour);
+ if (okhour && okmin && m < 60)
+ {
+ if (m12Hour)
+ {
+ if (h == 0 || h > 12)
+ h = 100; // error
+ else if (h == 12)
+ h = 0; // convert 12:nn to 0:nn
+ if (mPm)
+ h += 12; // convert to PM
+ }
+ int t = h * 60 + m;
+ if (t >= mMinimumValue && t <= maxValue())
+ {
+ if (ok)
+ *ok = true;
+ return t;
+ }
+ }
+ }
+ }
+ else if (text.length() == 4)
+ {
+ // hhmm format for time of day
+ bool okn;
+ int mins = text.toUInt(&okn);
+ if (okn)
+ {
+ int m = mins % 100;
+ int h = mins / 100;
+ if (m12Hour)
+ {
+ if (h == 0 || h > 12)
+ h = 100; // error
+ else if (h == 12)
+ h = 0; // convert 12:nn to 0:nn
+ if (mPm)
+ h += 12; // convert to PM
+ }
+ int t = h * 60 + m;
+ if (h < 24 && m < 60 && t >= mMinimumValue && t <= maxValue())
+ {
+ if (ok)
+ *ok = true;
+ return t;
+ }
+ }
+
+ }
+ if (ok)
+ *ok = false;
+ return 0;
+}
+
+/******************************************************************************
+ * Set the spin box as valid or invalid.
+ * If newly invalid, the value is displayed as asterisks.
+ * If newly valid, the value is set to the minimum value.
+ */
+void TimeSpinBox::setValid(bool valid)
+{
+ if (valid && mInvalid)
+ {
+ mInvalid = false;
+ if (value() < mMinimumValue)
+ SpinBox2::setValue(mMinimumValue);
+ setSpecialValueText(QString());
+ SpinBox2::setMinValue(mMinimumValue);
+ }
+ else if (!valid && !mInvalid)
+ {
+ mInvalid = true;
+ SpinBox2::setMinValue(mMinimumValue - 1);
+ setSpecialValueText(QString::fromLatin1("**:**"));
+ SpinBox2::setValue(mMinimumValue - 1);
+ }
+}
+
+/******************************************************************************
+* Set the spin box's minimum value.
+*/
+void TimeSpinBox::setMinValue(int minutes)
+{
+ mMinimumValue = minutes;
+ SpinBox2::setMinValue(mMinimumValue - (mInvalid ? 1 : 0));
+}
+
+/******************************************************************************
+ * Set the spin box's value.
+ */
+void TimeSpinBox::setValue(int minutes)
+{
+ if (!mEnteredSetValue)
+ {
+ mEnteredSetValue = true;
+ mPm = (minutes >= 720);
+ if (minutes > maxValue())
+ setValid(false);
+ else
+ {
+ if (mInvalid)
+ {
+ mInvalid = false;
+ setSpecialValueText(QString());
+ SpinBox2::setMinValue(mMinimumValue);
+ }
+ SpinBox2::setValue(minutes);
+ mEnteredSetValue = false;
+ }
+ }
+}
+
+/******************************************************************************
+ * Step the spin box value.
+ * If it was invalid, set it valid and set the value to the minimum.
+ */
+void TimeSpinBox::stepUp()
+{
+ if (mInvalid)
+ setValid(true);
+ else
+ SpinBox2::stepUp();
+}
+
+void TimeSpinBox::stepDown()
+{
+ if (mInvalid)
+ setValid(true);
+ else
+ SpinBox2::stepDown();
+}
+
+bool TimeSpinBox::isValid() const
+{
+ return value() >= mMinimumValue;
+}
+
+void TimeSpinBox::slotValueChanged(int value)
+{
+ mPm = mValidator->mPm = (value >= 720);
+}
+
+QSize TimeSpinBox::sizeHint() const
+{
+ QSize sz = SpinBox2::sizeHint();
+ QFontMetrics fm(font());
+ return QSize(sz.width() + fm.width(":"), sz.height());
+}
+
+QSize TimeSpinBox::minimumSizeHint() const
+{
+ QSize sz = SpinBox2::minimumSizeHint();
+ QFontMetrics fm(font());
+ return QSize(sz.width() + fm.width(":"), sz.height());
+}
+
+/******************************************************************************
+ * Validate the time spin box input.
+ * The entered time must either be 4 digits, or it must contain a colon, but
+ * hours may be blank.
+ */
+QValidator::State TimeSpinBox::TimeValidator::validate(QString& text, int& /*cursorPos*/) const
+{
+ QString cleanText = text.stripWhiteSpace();
+ if (cleanText.isEmpty())
+ return QValidator::Intermediate;
+ QValidator::State state = QValidator::Acceptable;
+ QString hour;
+ bool ok;
+ int hr = 0;
+ int mn = 0;
+ int colon = cleanText.find(':');
+ if (colon >= 0)
+ {
+ QString minute = cleanText.mid(colon + 1);
+ if (minute.isEmpty())
+ state = QValidator::Intermediate;
+ else if ((mn = minute.toUInt(&ok)) >= 60 || !ok)
+ return QValidator::Invalid;
+
+ hour = cleanText.left(colon);
+ }
+ else if (maxMinute >= 1440)
+ {
+ // The hhmm form of entry is only allowed for time-of-day, i.e. <= 2359
+ hour = cleanText;
+ state = QValidator::Intermediate;
+ }
+ else
+ {
+ if (cleanText.length() > 4)
+ return QValidator::Invalid;
+ if (cleanText.length() < 4)
+ state = QValidator::Intermediate;
+ hour = cleanText.left(2);
+ QString minute = cleanText.mid(2);
+ if (!minute.isEmpty()
+ && ((mn = minute.toUInt(&ok)) >= 60 || !ok))
+ return QValidator::Invalid;
+ }
+
+ if (!hour.isEmpty())
+ {
+ hr = hour.toUInt(&ok);
+ if (m12Hour)
+ {
+ if (hr == 0 || hr > 12)
+ hr = 100; // error;
+ else if (hr == 12)
+ hr = 0; // convert 12:nn to 0:nn
+ if (mPm)
+ hr += 12; // convert to PM
+ }
+ if (!ok || hr > maxMinute/60)
+ return QValidator::Invalid;
+ }
+ if (state == QValidator::Acceptable)
+ {
+ int t = hr * 60 + mn;
+ if (t < minMinute || t > maxMinute)
+ return QValidator::Invalid;
+ }
+ return state;
+}
diff --git a/kalarm/lib/timespinbox.h b/kalarm/lib/timespinbox.h
new file mode 100644
index 00000000..275fdf30
--- /dev/null
+++ b/kalarm/lib/timespinbox.h
@@ -0,0 +1,127 @@
+/*
+ * timespinbox.h - time spinbox widget
+ * Program: kalarm
+ * Copyright © 2001-2008 by David Jarvie <djarvie@kde.org>
+ *
+ * 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 TIMESPINBOX_H
+#define TIMESPINBOX_H
+
+#include <qdatetime.h>
+#include "spinbox2.h"
+
+
+/**
+ * @short Hours/minutes time entry widget.
+ *
+ * The TimeSpinBox class provides a widget to enter a time consisting of an hours/minutes
+ * value. It can hold a time in any of 3 modes: a time of day using the 24-hour clock; a
+ * time of day using the 12-hour clock; or a length of time not restricted to 24 hours.
+ *
+ * Derived from SpinBox2, it displays a spin box with two pairs of spin buttons, one
+ * for hours and one for minutes. It provides accelerated stepping using the spin
+ * buttons, when the shift key is held down (inherited from SpinBox2). The default
+ * shift steps are 5 minutes and 6 hours.
+ *
+ * The widget may be set as read-only. This has the same effect as disabling it, except
+ * that its appearance is unchanged.
+ *
+ * @author David Jarvie <software@astrojar.org.uk>
+ */
+class TimeSpinBox : public SpinBox2
+{
+ Q_OBJECT
+ public:
+ /** Constructor for a wrapping time spin box which can be used to enter a time of day.
+ * @param use24hour True for entry of 24-hour clock times (range 00:00 to 23:59).
+ * False for entry of 12-hour clock times (range 12:00 to 11:59).
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ explicit TimeSpinBox(bool use24hour, QWidget* parent = 0, const char* name = 0);
+ /** Constructor for a non-wrapping time spin box which can be used to enter a length of time.
+ * @param minMinute The minimum value which the spin box can hold, in minutes.
+ * @param maxMinute The maximum value which the spin box can hold, in minutes.
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ */
+ TimeSpinBox(int minMinute, int maxMinute, QWidget* parent = 0, const char* name = 0);
+ /** Returns true if the spin box holds a valid value.
+ * An invalid value is displayed as asterisks.
+ */
+ bool isValid() const;
+ /** Sets the spin box as holding a valid or invalid value.
+ * If newly invalid, the value is displayed as asterisks.
+ * If newly valid, the value is set to the minimum value.
+ */
+ void setValid(bool);
+ /** Returns the current value held in the spin box.
+ * If an invalid value is displayed, returns a value lower than the minimum value.
+ */
+ QTime time() const;
+ /** Sets the maximum value which can be held in the spin box.
+ * @param minutes The maximum value expressed in minutes.
+ */
+ virtual void setMinValue(int minutes);
+ /** Sets the maximum value which can be held in the spin box.
+ * @param minutes The maximum value expressed in minutes.
+ */
+ virtual void setMaxValue(int minutes) { SpinBox2::setMaxValue(minutes); }
+ /** Sets the maximum value which can be held in the spin box. */
+ void setMaxValue(const QTime& t) { SpinBox2::setMaxValue(t.hour()*60 + t.minute()); }
+ /** Returns the maximum value which can be held in the spin box. */
+ QTime maxTime() const { int mv = maxValue(); return QTime(mv/60, mv%60); }
+ /** Returns a text describing use of the shift key as an accelerator for
+ * the spin buttons, designed for incorporation into WhatsThis texts.
+ */
+ static QString shiftWhatsThis();
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ public slots:
+ /** Sets the value of the spin box.
+ * @param minutes The new value of the spin box, expressed in minutes.
+ */
+ virtual void setValue(int minutes);
+ /** Sets the value of the spin box. */
+ void setValue(const QTime& t) { setValue(t.hour()*60 + t.minute()); }
+ /** Increments the spin box value.
+ * If the value was previously invalid, the spin box is set to the minimum value.
+ */
+ virtual void stepUp();
+ /** Decrements the spin box value.
+ * If the value was previously invalid, the spin box is set to the minimum value.
+ */
+ virtual void stepDown();
+
+ protected:
+ virtual QString mapValueToText(int v);
+ virtual int mapTextToValue(bool* ok);
+ private slots:
+ void slotValueChanged(int value);
+ private:
+ class TimeValidator;
+ TimeValidator* mValidator;
+ int mMinimumValue; // real minimum value, excluding special value for "**:**"
+ bool m12Hour; // use 12-hour clock
+ bool mPm; // use PM for manually entered values (with 12-hour clock)
+ bool mInvalid; // value is currently invalid (asterisks)
+ bool mEnteredSetValue; // to prevent infinite recursion in setValue()
+};
+
+#endif // TIMESPINBOX_H