diff options
Diffstat (limited to 'libkdeedu/extdate')
-rw-r--r-- | libkdeedu/extdate/Makefile.am | 21 | ||||
-rw-r--r-- | libkdeedu/extdate/README | 43 | ||||
-rw-r--r-- | libkdeedu/extdate/extcalendarsystem.cpp | 151 | ||||
-rw-r--r-- | libkdeedu/extdate/extcalendarsystem.h | 357 | ||||
-rw-r--r-- | libkdeedu/extdate/extcalendarsystemgregorian.cpp | 328 | ||||
-rw-r--r-- | libkdeedu/extdate/extcalendarsystemgregorian.h | 96 | ||||
-rw-r--r-- | libkdeedu/extdate/extdatepicker.cpp | 532 | ||||
-rw-r--r-- | libkdeedu/extdate/extdatepicker.h | 253 | ||||
-rw-r--r-- | libkdeedu/extdate/extdatetbl.cpp | 968 | ||||
-rw-r--r-- | libkdeedu/extdate/extdatetbl.h | 427 | ||||
-rw-r--r-- | libkdeedu/extdate/extdatetime.cpp | 1148 | ||||
-rw-r--r-- | libkdeedu/extdate/extdatetime.h | 190 | ||||
-rw-r--r-- | libkdeedu/extdate/extdatetimeedit.cpp | 2751 | ||||
-rw-r--r-- | libkdeedu/extdate/extdatetimeedit.h | 341 | ||||
-rw-r--r-- | libkdeedu/extdate/extdatewidget.cpp | 177 | ||||
-rw-r--r-- | libkdeedu/extdate/extdatewidget.h | 89 | ||||
-rw-r--r-- | libkdeedu/extdate/main.cpp | 30 | ||||
-rw-r--r-- | libkdeedu/extdate/test_extdate.cc | 334 | ||||
-rw-r--r-- | libkdeedu/extdate/testwidget.cpp | 68 | ||||
-rw-r--r-- | libkdeedu/extdate/testwidget.h | 51 |
20 files changed, 8355 insertions, 0 deletions
diff --git a/libkdeedu/extdate/Makefile.am b/libkdeedu/extdate/Makefile.am new file mode 100644 index 00000000..d7b78860 --- /dev/null +++ b/libkdeedu/extdate/Makefile.am @@ -0,0 +1,21 @@ +check_PROGRAMS = test_extdate test_extdatepicker + +INCLUDES= $(all_includes) + +lib_LTLIBRARIES = libextdate.la + +libextdate_la_SOURCES = extdatetime.cpp extcalendarsystem.cpp extcalendarsystemgregorian.cpp extdatetbl.cpp extdatepicker.cpp extdatetimeedit.cpp extdatewidget.cpp + +libextdate_la_LDFLAGS = $(all_libraries) -no-undefined -version-info 3:0:2 +libextdate_la_LIBADD = $(LIB_KDEUI) + +test_extdate_SOURCES = test_extdate.cc +test_extdate_LDADD = libextdate.la +test_extdate_LDFLAGS = $(all_libraries) $(KDE_RPATH) + +test_extdatepicker_SOURCES = testwidget.cpp main.cpp +test_extdatepicker_LDADD = libextdate.la +test_extdatepicker_LDFLAGS = $(all_libraries) $(KDE_RPATH) + +METASOURCES = AUTO + diff --git a/libkdeedu/extdate/README b/libkdeedu/extdate/README new file mode 100644 index 00000000..fc9b21b1 --- /dev/null +++ b/libkdeedu/extdate/README @@ -0,0 +1,43 @@ +This libray consists of a group of classes which allow KDE +applications to access calendar dates outside of the limited range +of years imposed by QDate. + +The QDate class has a limited range of valid dates. It does not +recognize dates prior to 14 Oct 1752 (when the Gregorian calendar +was adopted by England), nor dates after 31 Dec 8000. Both of these +limits are arbitrary. + + +The following classes are included: + +ExtDate: Replaces QDate. There is no restriction on what dates +may be entered. For dates in the valid QDate range, it is +completely equivalent to QDate. + +ExtDateTime: Replaces QDateTime. Consists of a QTime object +and an ExtDate object. + +ExtCalendarSystem: Replaces KCalendarSystem. Uses ExtDate instead +of QDate. ExtCalendarSystem is a baseclass foundation for several +different calendar systems. A "calendar system" is just a method for +hierarchically subdividing the long count of days known as the Julian +Day Calendar into groups (weeks, months, years). + +ExtCalendarSystemGregorian: Replaces KCalendarSystemGregorian. +The most common calendar system in modern western societies is the +Gregorian calendar. This class implements the Gregorian calendar +as a ExtCalendarSystem. + +ExtDateTable: Replaces KDateTable. +ExtDatePicker: Replaces KDatePicker. +ExtDateTimeEdit: Replaces QDateTimeEdit. +ExtDateWidget: Replaces KDateWidget. + +There are two test programs with the library, to verify the +functionality of the ExtDate classes: + +test_extdate tests the non-GUI functionality, comparing results of +several operations with the results from QDate. + +test_extdatepicker presents a KDatePicker widget and an ExtDatePicker +widget side-by-side. diff --git a/libkdeedu/extdate/extcalendarsystem.cpp b/libkdeedu/extdate/extcalendarsystem.cpp new file mode 100644 index 00000000..f5cd0a36 --- /dev/null +++ b/libkdeedu/extdate/extcalendarsystem.cpp @@ -0,0 +1,151 @@ +/* + Copyright (c) 2002 Carlos Moro <cfmoro@correo.uniovi.es> + Copyright (c) 2002 Hans Petter Bieker <bieker@kde.org> + Copyright (c) 2004 Jason Harris <jharris@30doradus.org> + + This class has been derived from ExtCalendarSystem; + the changesd made just replace QDate objects with ExtDate objects. + These changes by Jason Harris <jharris@30doradus.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Gregorian calendar system implementation factory for creation of kde calendar +// systems. +// Also default gregorian and factory classes + +#include <kglobal.h> + +#include "extcalendarsystem.h" +#include "klocale.h" + +class ExtCalendarSystemPrivate +{ +public: + const KLocale * locale; +}; + +ExtCalendarSystem::ExtCalendarSystem(const KLocale * locale) + : d(new ExtCalendarSystemPrivate) +{ + d->locale = locale; +} + +ExtCalendarSystem::~ExtCalendarSystem() +{ + delete d; +} + +const KLocale * ExtCalendarSystem::locale() const +{ + if ( d->locale ) + return d->locale; + + return KGlobal::locale(); +} + +QString ExtCalendarSystem::dayString(const ExtDate & pDate, bool bShort) const +{ + QString sResult; + + sResult.setNum(day(pDate)); + if (!bShort && sResult.length() == 1 ) + sResult.prepend('0'); + + return sResult; +} + +QString ExtCalendarSystem::monthString(const ExtDate & pDate, bool bShort) const +{ + QString sResult; + + sResult.setNum(month(pDate)); + if (!bShort && sResult.length() == 1 ) + sResult.prepend('0'); + + return sResult; +} + +QString ExtCalendarSystem::yearString(const ExtDate & pDate, bool bShort) const +{ + QString sResult; + + sResult.setNum(year(pDate)); + if (bShort && sResult.length() == 4 ) + sResult = sResult.right(2); + + return sResult; +} + +static int stringToInteger(const QString & sNum, int & iLength) +{ + unsigned int iPos = 0; + + int result = 0; + for (; sNum.length() > iPos && sNum.at(iPos).isDigit(); iPos++) + { + result *= 10; + result += sNum.at(iPos).digitValue(); + } + + iLength = iPos; + return result; +} + + +int ExtCalendarSystem::dayStringToInteger(const QString & sNum, int & iLength) const +{ + return stringToInteger(sNum, iLength); +} + +int ExtCalendarSystem::monthStringToInteger(const QString & sNum, int & iLength) const +{ + return stringToInteger(sNum, iLength); +} + +int ExtCalendarSystem::yearStringToInteger(const QString & sNum, int & iLength) const +{ + return stringToInteger(sNum, iLength); +} + +QString ExtCalendarSystem::weekDayName (int weekDay, bool shortName) const +{ + if ( shortName ) + switch ( weekDay ) + { + case 1: return locale()->translate("Monday", "Mon"); + case 2: return locale()->translate("Tuesday", "Tue"); + case 3: return locale()->translate("Wednesday", "Wed"); + case 4: return locale()->translate("Thursday", "Thu"); + case 5: return locale()->translate("Friday", "Fri"); + case 6: return locale()->translate("Saturday", "Sat"); + case 7: return locale()->translate("Sunday", "Sun"); + } + else + switch ( weekDay ) + { + case 1: return locale()->translate("Monday"); + case 2: return locale()->translate("Tuesday"); + case 3: return locale()->translate("Wednesday"); + case 4: return locale()->translate("Thursday"); + case 5: return locale()->translate("Friday"); + case 6: return locale()->translate("Saturday"); + case 7: return locale()->translate("Sunday"); + } + + return QString::null; +} + diff --git a/libkdeedu/extdate/extcalendarsystem.h b/libkdeedu/extdate/extcalendarsystem.h new file mode 100644 index 00000000..5007ba90 --- /dev/null +++ b/libkdeedu/extdate/extcalendarsystem.h @@ -0,0 +1,357 @@ +/* + Copyright (c) 2002 Carlos Moro <cfmoro@correo.uniovi.es> + Copyright (c) 2002-2003 Hans Petter Bieker <bieker@kde.org> + Copyright (c) 2004 Jason Harris <jharris@30doradus.org> + + This class has been derived from KCalendarSystem; + the changesd made just replace QDate objects with ExtDate objects. + These changes by Jason Harris <jharris@30doradus.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef EXTCALENDARSYSTEM_H +#define EXTCALENDARSYSTEM_H + +#include "extdatetime.h" + +class KLocale; + +class ExtCalendarSystemPrivate; + +/** + * CalendarSystem abstract class, default derived kde gregorian class and + * factory class. Provides support for different calendar types for kde + * calendar widget and related stuff. + * + * Derived classes must be created through ExtCalendarFactory class + * + * @author Carlos Moro <cfmoro@correo.uniovi.es> + * @version $Id$ + * @since 3.2 + */ +class ExtCalendarSystem +{ +public: + /** + * Constructor of abstract calendar class. This will be called by the derived classes. + * + * @param locale It will use this locale for translations, 0 means global. + */ + ExtCalendarSystem(const KLocale * locale = 0); + + /** + * Descructor. + */ + virtual ~ExtCalendarSystem(); + + /** + * Gets specific calendar type year for a given gregorian date + * + * @param date gregorian date + * @return year + */ + virtual int year (const ExtDate & date) const = 0; + + /** + * Converts a date into a year literal + * + * @param pDate The date to convert + * @param bShort If the short version of should be used + * @return The year literal of the date + */ + virtual QString yearString(const ExtDate & pDate, bool bShort) const; + + /** + * Converts a year literal of a part of a string into a integer starting at the beginning of the string + * + * @param sNum The string to parse + * @param iLength The number of QChars used, and 0 if no valid symbols was found in the string + * @return An integer corresponding to the year + */ + virtual int yearStringToInteger(const QString & sNum, int & iLength) const; + + /** + * Gets specific calendar type month for a given gregorian date + * + * @param date gregorian date + * @return month number + */ + virtual int month (const ExtDate & date) const = 0; + + /** + * Converts a date into a month literal + * + * @param pDate The date to convert + * @param bShort If the short version of should be used + * @return The month literal of the date + */ + virtual QString monthString(const ExtDate & pDate, bool bShort) const; + + /** + * Converts a month literal of a part of a string into a integer starting at the beginning of the string + * + * @param sNum The string to parse + * @param iLength The number of QChars used, and 0 if no valid symbols was found in the string + * @return An integer corresponding to the month + */ + virtual int monthStringToInteger(const QString & sNum, int & iLength) const; + + /** + * Gets specific calendar type day number of month for a given date + * + * @param date gregorian date equivalent to the specific one + * @return day of the month + */ + virtual int day (const ExtDate & date) const = 0; + + /** + * Converts a date into a day literal + * + * @param pDate The date to convert + * @param bShort If the short version of should be used + * @return The day literal of the date + */ + virtual QString dayString(const ExtDate & pDate, bool bShort) const; + + /** + * Converts a day literal of a part of a string into a integer starting at the beginning of the string + * + * @param sNum The string to parse + * @param iLength The number of QChars used, and 0 if no valid symbols was found in the string + * @return An integer corresponding to the day + */ + virtual int dayStringToInteger(const QString & sNum, int & iLength) const; + + /** + * Gets specific calendar type number of day of week number for a given + * date + * + * @param date gregorian date + * @return day of week + */ + virtual int dayOfWeek (const ExtDate & date) const = 0; + + /** + * Gets specific calendar type day number of year for a given date + * + * @param date gregorian date equivalent to the specific one + * @return day number + */ + virtual int dayOfYear (const ExtDate & date) const = 0; + + /** + * Changes the date's year, month and day. The range of the year, month + * and day depends on which calendar is being used. + * + * @param date Date to change + * @param y Year + * @param m Month number + * @param d Day of month + * @return true if the date is valid; otherwise returns false. + */ + virtual bool setYMD(ExtDate & date, int y, int m, int d) const = 0; + + /** + * Returns a QDate object containing a date nyears later. + * + * @param date The old date + * @param nyears The number of years to add + * @return The new date + */ + virtual ExtDate addYears(const ExtDate & date, int nyears) const = 0; + + /** + * Returns a QDate object containing a date nmonths later. + * + * @param date The old date + * @param nmonths The number of months to add + * @return The new date + */ + virtual ExtDate addMonths(const ExtDate & date, int nmonths) const = 0; + + /** + * Returns a QDate object containing a date ndays later. + * + * @param date The old date + * @param ndays The number of days to add + * @return The new date + */ + virtual ExtDate addDays(const ExtDate & date, int ndays) const = 0; + + /** + * Gets specific calendar type number of month for a given year + * + * @param date The date whose year to use + * @return The number of months in that year + */ + virtual int monthsInYear (const ExtDate & date) const = 0; + + /** + * Gets the number of days in date whose years specified. + * + * @param date Gregorian date equivalent to the specific one + * @return The number of days in year + */ + virtual int daysInYear (const ExtDate & date) const = 0; + + /** + * Gets specific calendar type number of days in month for a given date + * + * @param date gregorian date + * @return number of days for month in date + */ + virtual int daysInMonth (const ExtDate & date) const = 0; + + /** + * Gets the number of weeks in a specified year + * + * @param year the year + * @return number of weeks in year + */ + virtual int weeksInYear(int year) const = 0; + + /** + * Gets specific calendar type week number for a given date + * + * @param date gregorian date + * @param yearNum The year the date belongs to + * @return week number + */ + virtual int weekNumber(const ExtDate& date, int * yearNum = 0) const = 0; + + /** + * Gets specific calendar type month name for a given month number + * If an invalid month is specified, QString::null is returned. + * + * @param month The month number + * @param year The year the month belongs to + * @param shortName Specifies if the short month name should be used + * @return The name of the month + */ + virtual QString monthName (int month, int year, bool shortName = false) const = 0; + + /** + * Gets specific calendar type month name for a given gregorian date + * + * @param date Gregorian date + * @param shortName Specifies if the short month name should be used + * @return The name of the month + */ + virtual QString monthName (const ExtDate & date, bool shortName = false ) const = 0; + + /** + * Returns a string containing the possessive form of the month name. + * ("of January", "of February", etc.) + * It's needed in long format dates in some languages. + * If an invalid month is specified, QString::null is returned. + * + * @param month The month number + * @param year The year the month belongs to + * @param shortName Specifies if the short month name should be used + * + * @return The possessive form of the name of the month + */ + virtual QString monthNamePossessive(int month, int year, bool shortName = false) const = 0; + + /** + * Returns a string containing the possessive form of the month name. + * ("of January", "of February", etc.) + * It's needed in long format dates in some languages. + * + * @param date Gregorian date + * @param shortName Specifies if the short month name should be used + * + * @return The possessive form of the name of the month + */ + virtual QString monthNamePossessive(const ExtDate & date, bool shortName = false) const = 0; + + /** + * Gets specific calendar type week day name + * If an invalid week day is specified, QString::null is returned. + * + * @param weekDay number of day in week (1 -> Monday) + * @param shortName short or complete day name + * @return day name + */ + virtual QString weekDayName (int weekDay, bool shortName = false) const = 0; + + /** + * Gets specific calendar type week day name + * + * @param date the date + * @param shortName short or complete day name + * @return day name + */ + virtual QString weekDayName (const ExtDate & date, bool shortName = false) const = 0; + + /** + * Gets the first year value supported by specific calendar type + * algorithms. + * + * @return first year supported + */ + virtual int minValidYear () const = 0; + + /** + * Gets the maximum year value supported by specific calendar type + * algorithms (QDate, 8000) + * + * @return maximum year supported + */ + virtual int maxValidYear () const = 0; + + /** + * Gets the day of the week traditionaly associated with pray + * + * @return day number + */ + virtual int weekDayOfPray () const = 0; + + /** + * Gets the string representing the calendar + */ + virtual QString calendarName() const = 0; + + /** + * Gets if the calendar is lunar based + * + * @return if the calendar is lunar based + */ + virtual bool isLunar() const = 0; + + /** + * Gets if the calendar is lunisolar based + * + * @return if the calendar is lunisolar based + */ + virtual bool isLunisolar() const = 0; + + /** + * Gets if the calendar is solar based + * + * @return if the calendar is solar based + */ + virtual bool isSolar() const = 0; + +protected: + const KLocale * locale() const; + +private: + ExtCalendarSystemPrivate * d; +}; + +#endif diff --git a/libkdeedu/extdate/extcalendarsystemgregorian.cpp b/libkdeedu/extdate/extcalendarsystemgregorian.cpp new file mode 100644 index 00000000..d358f595 --- /dev/null +++ b/libkdeedu/extdate/extcalendarsystemgregorian.cpp @@ -0,0 +1,328 @@ +/* + Copyright (c) 2002 Carlos Moro <cfmoro@correo.uniovi.es> + Copyright (c) 2002-2003 Hans Petter Bieker <bieker@kde.org> + Copyright (c) 2004 Jason Harris <jharris@30doradus.org> + + This class has been derived from KCalendarSystemGregorian; + the changesd made just replace QDate objects with ExtDate objects. + These changes by Jason Harris <jharris@30doradus.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Derived gregorian kde calendar class +// Just a schema. + + +#include <klocale.h> +#include <kdebug.h> + +#include "extcalendarsystemgregorian.h" + +ExtCalendarSystemGregorian::ExtCalendarSystemGregorian(const KLocale * locale) + : ExtCalendarSystem(locale) +{ +} + +ExtCalendarSystemGregorian::~ExtCalendarSystemGregorian() +{ +} + +int ExtCalendarSystemGregorian::year(const ExtDate& date) const +{ + return date.year(); +} + +int ExtCalendarSystemGregorian::monthsInYear( const ExtDate & date ) const +{ + Q_UNUSED( date ) + + return 12; +} + +int ExtCalendarSystemGregorian::weeksInYear(int year) const +{ + ExtDate temp; + temp.setYMD(year, 12, 31); + + // If the last day of the year is in the first week, we have to check the + // week before + if ( temp.weekNumber() == 1 ) + temp = temp.addDays(-7); + + return temp.weekNumber(); +} + +int ExtCalendarSystemGregorian::weekNumber(const ExtDate& date, + int * yearNum) const +{ + return date.weekNumber(yearNum); +} + +QString ExtCalendarSystemGregorian::monthName(const ExtDate& date, + bool shortName) const +{ + return monthName(month(date), year(date), shortName); +} + +QString ExtCalendarSystemGregorian::monthNamePossessive(const ExtDate& date, bool shortName) const +{ + return monthNamePossessive(month(date), year(date), shortName); +} + +QString ExtCalendarSystemGregorian::monthName(int month, int year, bool shortName) const +{ + Q_UNUSED(year); + + if ( shortName ) + switch ( month ) + { + case 1: + return locale()->translate("January", "Jan"); + case 2: + return locale()->translate("February", "Feb"); + case 3: + return locale()->translate("March", "Mar"); + case 4: + return locale()->translate("April", "Apr"); + case 5: + return locale()->translate("May short", "May"); + case 6: + return locale()->translate("June", "Jun"); + case 7: + return locale()->translate("July", "Jul"); + case 8: + return locale()->translate("August", "Aug"); + case 9: + return locale()->translate("September", "Sep"); + case 10: + return locale()->translate("October", "Oct"); + case 11: + return locale()->translate("November", "Nov"); + case 12: + return locale()->translate("December", "Dec"); + } + else + switch ( month ) + { + case 1: + return locale()->translate("January"); + case 2: + return locale()->translate("February"); + case 3: + return locale()->translate("March"); + case 4: + return locale()->translate("April"); + case 5: + return locale()->translate("May long", "May"); + case 6: + return locale()->translate("June"); + case 7: + return locale()->translate("July"); + case 8: + return locale()->translate("August"); + case 9: + return locale()->translate("September"); + case 10: + return locale()->translate("October"); + case 11: + return locale()->translate("November"); + case 12: + return locale()->translate("December"); + } + + return QString::null; +} + +QString ExtCalendarSystemGregorian::monthNamePossessive(int month, int year, + bool shortName) const +{ + Q_UNUSED(year); + + if ( shortName ) + switch ( month ) + { + case 1: + return locale()->translate("of January", "of Jan"); + case 2: + return locale()->translate("of February", "of Feb"); + case 3: + return locale()->translate("of March", "of Mar"); + case 4: + return locale()->translate("of April", "of Apr"); + case 5: + return locale()->translate("of May short", "of May"); + case 6: + return locale()->translate("of June", "of Jun"); + case 7: + return locale()->translate("of July", "of Jul"); + case 8: + return locale()->translate("of August", "of Aug"); + case 9: + return locale()->translate("of September", "of Sep"); + case 10: + return locale()->translate("of October", "of Oct"); + case 11: + return locale()->translate("of November", "of Nov"); + case 12: + return locale()->translate("of December", "of Dec"); + } + else + switch ( month ) + { + case 1: + return locale()->translate("of January"); + case 2: + return locale()->translate("of February"); + case 3: + return locale()->translate("of March"); + case 4: + return locale()->translate("of April"); + case 5: + return locale()->translate("of May long", "of May"); + case 6: + return locale()->translate("of June"); + case 7: + return locale()->translate("of July"); + case 8: + return locale()->translate("of August"); + case 9: + return locale()->translate("of September"); + case 10: + return locale()->translate("of October"); + case 11: + return locale()->translate("of November"); + case 12: + return locale()->translate("of December"); + } + + return QString::null; +} + +bool ExtCalendarSystemGregorian::setYMD(ExtDate & date, int y, int m, int d) const +{ + // ExtDate supports gregorian internally + return date.setYMD(y, m, d); +} + +ExtDate ExtCalendarSystemGregorian::addYears(const ExtDate & date, int nyears) const +{ + return date.addYears(nyears); +} + +ExtDate ExtCalendarSystemGregorian::addMonths(const ExtDate & date, int nmonths) const +{ + return date.addMonths(nmonths); +} + +ExtDate ExtCalendarSystemGregorian::addDays(const ExtDate & date, int ndays) const +{ + return date.addDays(ndays); +} + +QString ExtCalendarSystemGregorian::weekDayName(int col, bool shortName) const +{ + // ### Should this really be different to each calendar system? Or are we + // only going to support weeks with 7 days? + + return ExtCalendarSystem::weekDayName(col, shortName); +} + +QString ExtCalendarSystemGregorian::weekDayName(const ExtDate& date, bool shortName) const +{ + return weekDayName(dayOfWeek(date), shortName); +} + + +int ExtCalendarSystemGregorian::dayOfWeek(const ExtDate& date) const +{ + return date.dayOfWeek(); +} + +int ExtCalendarSystemGregorian::dayOfYear(const ExtDate & date) const +{ + return date.dayOfYear(); +} + +int ExtCalendarSystemGregorian::daysInMonth(const ExtDate& date) const +{ + return date.daysInMonth(); +} + +int ExtCalendarSystemGregorian::minValidYear() const +{ + return -50000; +} + +int ExtCalendarSystemGregorian::maxValidYear() const +{ + return 50000; +} + +int ExtCalendarSystemGregorian::day(const ExtDate& date) const +{ + return date.day(); +} + +int ExtCalendarSystemGregorian::month(const ExtDate& date) const +{ + return date.month(); +} + +int ExtCalendarSystemGregorian::daysInYear(const ExtDate& date) const +{ + return date.daysInYear(); +} + +int ExtCalendarSystemGregorian::weekDayOfPray() const +{ + return 7; // sunday +} + +QString ExtCalendarSystemGregorian::calendarName() const +{ + return QString::fromLatin1("gregorian"); +} + +bool ExtCalendarSystemGregorian::isLunar() const +{ + return false; +} + +bool ExtCalendarSystemGregorian::isLunisolar() const +{ + return false; +} + +bool ExtCalendarSystemGregorian::isSolar() const +{ + return true; +} + +int ExtCalendarSystemGregorian::yearStringToInteger(const QString & sNum, int & iLength) const +{ + int iYear; + iYear = ExtCalendarSystem::yearStringToInteger(sNum, iLength); + + // Qt treats a year in the range 0-100 as 1900-1999. + // It is nicer for the user if we treat 0-68 as 2000-2068 + if (iYear < 69) + iYear += 2000; + else if (iYear < 100) + iYear += 1900; + + return iYear; +} diff --git a/libkdeedu/extdate/extcalendarsystemgregorian.h b/libkdeedu/extdate/extcalendarsystemgregorian.h new file mode 100644 index 00000000..c426f997 --- /dev/null +++ b/libkdeedu/extdate/extcalendarsystemgregorian.h @@ -0,0 +1,96 @@ +/* + Copyright (c) 2002 Carlos Moro <cfmoro@correo.uniovi.es> + Copyright (c) 2002 Hans Petter Bieker <bieker@kde.org> + Copyright (c) 2004 Jason Harris <jharris@30doradus.org> + + This class has been derived from KCalendarSystemGregorian; + the changesd made just replace QDate objects with ExtDate objects. + These changes by Jason Harris <jharris@30doradus.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef EXTCALENDARSYSTEMGREGORIAN_H +#define EXTCALENDARSYSTEMGREGORIAN_H + + +#include "extcalendarsystem.h" + +class ExtCalendarSystemGregorianPrivate; + +/** + * @internal + * This is the Gregorian calendar implementation. + * + * The Gregorian calender is the most used calendar today. The first year in + * the calendar is set to the birth of Christ. + * + * @see KLocale,ExtCalendarSystem,ExtCalendarSystemFactory + * + * @author Carlos Moro <cfmoro@correo.uniovi.es> + * @license GNU-GPL v.2 + * @version $Id$ + * @since 3.2 + */ +class ExtCalendarSystemGregorian: public ExtCalendarSystem +{ +public: + ExtCalendarSystemGregorian (const KLocale * locale = 0); + virtual ~ExtCalendarSystemGregorian (); + + virtual int year (const ExtDate & date) const; + virtual int month (const ExtDate & date) const; + virtual int day (const ExtDate & date) const; + virtual int dayOfWeek (const ExtDate & date) const; + virtual int dayOfYear (const ExtDate & date) const; + + virtual bool setYMD(ExtDate & date, int y, int m, int d) const; + + virtual ExtDate addYears(const ExtDate & date, int nyears) const; + virtual ExtDate addMonths(const ExtDate & date, int nmonths) const; + virtual ExtDate addDays(const ExtDate & date, int ndays) const; + + virtual int monthsInYear (const ExtDate & date) const; + + virtual int daysInYear (const ExtDate & date) const; + virtual int daysInMonth (const ExtDate & date) const; + virtual int weeksInYear(int year) const; + virtual int weekNumber(const ExtDate& date, int * yearNum = 0) const; + + virtual int yearStringToInteger(const QString & sNum, int & iLength) const; + + virtual QString monthName (int month, int year, bool shortName = false) const; + virtual QString monthName (const ExtDate & date, bool shortName = false ) const; + virtual QString monthNamePossessive(int month, int year, bool shortName = false) const; + virtual QString monthNamePossessive(const ExtDate & date, bool shortName = false ) const; + virtual QString weekDayName (int weekDay, bool shortName = false) const; + virtual QString weekDayName (const ExtDate & date, bool shortName = false) const; + + virtual int minValidYear () const; + virtual int maxValidYear () const; + virtual int weekDayOfPray () const; + + virtual QString calendarName() const; + + virtual bool isLunar() const; + virtual bool isLunisolar() const; + virtual bool isSolar() const; + +private: + ExtCalendarSystemGregorianPrivate * d; +}; + +#endif diff --git a/libkdeedu/extdate/extdatepicker.cpp b/libkdeedu/extdate/extdatepicker.cpp new file mode 100644 index 00000000..22b4ba80 --- /dev/null +++ b/libkdeedu/extdate/extdatepicker.cpp @@ -0,0 +1,532 @@ +/* -*- C++ -*- + This file is part of the KDE libraries + Copyright (C) 1997 Tim D. Gilman (tdgilman@best.org) + (C) 1998-2001 Mirko Boehm (mirko@kde.org) + (C) 2004 Jason Harris (jharris@30doradus.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qlayout.h> +#include <qstyle.h> +#include <qtoolbutton.h> +#include <qtooltip.h> +#include <qpopupmenu.h> + +#include <kdialog.h> +#include <klocale.h> +#include <kiconloader.h> +#include <ktoolbar.h> +#include <kdebug.h> +#include <knotifyclient.h> + +#include "extdatepicker.h" +#include "extdatetbl.h" +#include "extdatepicker.moc" + +// Week numbers are defined by ISO 8601 +// See http://www.merlyn.demon.co.uk/weekinfo.htm for details + +class ExtDatePicker::ExtDatePickerPrivate +{ +public: + ExtDatePickerPrivate() : closeButton(0L), selectWeek(0L), todayButton(0), + navigationLayout(0), calendar(0) {} + + void fillWeeksCombo(const ExtDate &date); + + QToolButton *closeButton; + QComboBox *selectWeek; + QToolButton *todayButton; + QBoxLayout *navigationLayout; + ExtCalendarSystem *calendar; +}; + +void ExtDatePicker::fillWeeksCombo(const ExtDate &date) +{ + // every year can have a different number of weeks + +//must remain commented unless ExtDate stuff gets added to kdelibs +// const ExtCalendarSystem * calendar = KGlobal::locale()->calendar(); + + // it could be that we had 53,1..52 and now 1..53 which is the same number but different + // so always fill with new values + + d->selectWeek->clear(); + + // We show all week numbers for all weeks between first day of year to last day of year + // This of course can be a list like 53,1,2..52 + + ExtDate day(date.year(), 1, 1); + int lastMonth = d->calendar->monthsInYear(day); + ExtDate lastDay(date.year(), lastMonth, d->calendar->daysInMonth(ExtDate(date.year(), lastMonth, 1))); + + for (; day <= lastDay; day = d->calendar->addDays(day, 7 /*calendar->daysOfWeek()*/) ) + { + int year = 0; + QString week = i18n("Week %1").arg(d->calendar->weekNumber(day, &year)); + if ( year != date.year() ) week += "*"; // show that this is a week from a different year + d->selectWeek->insertItem(week); + } +} + +ExtDatePicker::ExtDatePicker(QWidget *parent, ExtDate dt, const char *name) + : QFrame(parent,name) +{ + init( dt ); +} + +ExtDatePicker::ExtDatePicker(QWidget *parent, ExtDate dt, const char *name, WFlags f) + : QFrame(parent,name, f) +{ + init( dt ); +} + +ExtDatePicker::ExtDatePicker( QWidget *parent, const char *name ) + : QFrame(parent,name) +{ + init( ExtDate::currentDate() ); +} + +void ExtDatePicker::init( const ExtDate &dt ) +{ + d = new ExtDatePickerPrivate(); + + d->calendar = new ExtCalendarSystemGregorian(); + + QBoxLayout * topLayout = new QVBoxLayout(this); + + d->navigationLayout = new QHBoxLayout(topLayout); + d->navigationLayout->addStretch(); + yearBackward = new QToolButton(this); + yearBackward->setAutoRaise(true); + d->navigationLayout->addWidget(yearBackward); + monthBackward = new QToolButton(this); + monthBackward ->setAutoRaise(true); + d->navigationLayout->addWidget(monthBackward); + d->navigationLayout->addSpacing(KDialog::spacingHint()); + + selectMonth = new QToolButton(this); + selectMonth ->setAutoRaise(true); + d->navigationLayout->addWidget(selectMonth); + selectYear = new QToolButton(this); + selectYear->setToggleButton(true); + selectYear->setAutoRaise(true); + d->navigationLayout->addWidget(selectYear); + d->navigationLayout->addSpacing(KDialog::spacingHint()); + + monthForward = new QToolButton(this); + monthForward ->setAutoRaise(true); + d->navigationLayout->addWidget(monthForward); + yearForward = new QToolButton(this); + yearForward ->setAutoRaise(true); + d->navigationLayout->addWidget(yearForward); + d->navigationLayout->addStretch(); + + line = new KLineEdit(this); + val = new ExtDateValidator(this); + table = new ExtDateTable(this); + fontsize = KGlobalSettings::generalFont().pointSize(); + if (fontsize == -1) + fontsize = QFontInfo(KGlobalSettings::generalFont()).pointSize(); + + fontsize++; // Make a little bigger + + d->selectWeek = new QComboBox(false, this); // read only week selection + d->todayButton = new QToolButton(this); + d->todayButton->setIconSet(SmallIconSet("today")); + + QToolTip::add(yearForward, i18n("Next year")); + QToolTip::add(yearBackward, i18n("Previous year")); + QToolTip::add(monthForward, i18n("Next month")); + QToolTip::add(monthBackward, i18n("Previous month")); + QToolTip::add(d->selectWeek, i18n("Select a week")); + QToolTip::add(selectMonth, i18n("Select a month")); + QToolTip::add(selectYear, i18n("Select a year")); + QToolTip::add(d->todayButton, i18n("Select the current day")); + + // ----- + setFontSize(fontsize); + line->setValidator(val); + line->installEventFilter( this ); + line->setReadOnly( true ); + + if ( QApplication::reverseLayout() ) + { + yearForward->setIconSet(BarIconSet(QString::fromLatin1("2leftarrow"))); + yearBackward->setIconSet(BarIconSet(QString::fromLatin1("2rightarrow"))); + monthForward->setIconSet(BarIconSet(QString::fromLatin1("1leftarrow"))); + monthBackward->setIconSet(BarIconSet(QString::fromLatin1("1rightarrow"))); + } + else + { + yearForward->setIconSet(BarIconSet(QString::fromLatin1("2rightarrow"))); + yearBackward->setIconSet(BarIconSet(QString::fromLatin1("2leftarrow"))); + monthForward->setIconSet(BarIconSet(QString::fromLatin1("1rightarrow"))); + monthBackward->setIconSet(BarIconSet(QString::fromLatin1("1leftarrow"))); + } + connect(table, SIGNAL(dateChanged(const ExtDate&)), SLOT(dateChangedSlot(const ExtDate&))); + connect(table, SIGNAL(tableClicked()), SLOT(tableClickedSlot())); + connect(monthForward, SIGNAL(clicked()), SLOT(monthForwardClicked())); + connect(monthBackward, SIGNAL(clicked()), SLOT(monthBackwardClicked())); + connect(yearForward, SIGNAL(clicked()), SLOT(yearForwardClicked())); + connect(yearBackward, SIGNAL(clicked()), SLOT(yearBackwardClicked())); + connect(d->selectWeek, SIGNAL(activated(int)), SLOT(weekSelected(int))); + connect(d->todayButton, SIGNAL(clicked()), SLOT(todayButtonClicked())); + connect(selectMonth, SIGNAL(clicked()), SLOT(selectMonthClicked())); + connect(selectYear, SIGNAL(toggled(bool)), SLOT(selectYearClicked())); + connect(line, SIGNAL(returnPressed()), SLOT(lineEnterPressed())); + table->setFocus(); + + + topLayout->addWidget(table); + + QBoxLayout * bottomLayout = new QHBoxLayout(topLayout); + bottomLayout->addWidget(d->todayButton); + bottomLayout->addWidget(line); + bottomLayout->addWidget(d->selectWeek); + + table->setDate(dt); + dateChangedSlot(dt); // needed because table emits changed only when newDate != oldDate +} + +ExtDatePicker::~ExtDatePicker() +{ + delete d; +} + +bool +ExtDatePicker::eventFilter(QObject *o, QEvent *e ) +{ + if ( e->type() == QEvent::KeyPress ) { + QKeyEvent *k = (QKeyEvent *)e; + + if ( (k->key() == Qt::Key_Prior) || + (k->key() == Qt::Key_Next) || + (k->key() == Qt::Key_Up) || + (k->key() == Qt::Key_Down) ) + { + QApplication::sendEvent( table, e ); + table->setFocus(); + return true; // eat event + } + } + return QFrame::eventFilter( o, e ); +} + +void +ExtDatePicker::resizeEvent(QResizeEvent* e) +{ + QWidget::resizeEvent(e); +} + +void +ExtDatePicker::dateChangedSlot(const ExtDate &date) +{ + kdDebug(298) << "ExtDatePicker::dateChangedSlot: date changed (" << date.year() << "/" << date.month() << "/" << date.day() << ")." << endl; + + +//must remain commented unless ExtDate gets added to kdelibs +// const ExtCalendarSystem * calendar = KGlobal::locale()->calendar(); + +// line->setText(KGlobal::locale()->formatDate(date, true)); + line->setText( date.toString( KGlobal::locale()->dateFormatShort() ) ); + selectMonth->setText(d->calendar->monthName(date, false)); + fillWeeksCombo(date); + + // calculate the item num in the week combo box; normalize selected day so as if 1.1. is the first day of the week + ExtDate firstDay(date.year(), 1, 1); + d->selectWeek->setCurrentItem((d->calendar->dayOfYear(date) + d->calendar->dayOfWeek(firstDay) - 2) / 7/*calendar->daysInWeek()*/); + + selectYear->setText(d->calendar->yearString(date, false)); + + emit(dateChanged(date)); +} + +void +ExtDatePicker::tableClickedSlot() +{ + kdDebug(298) << "ExtDatePicker::tableClickedSlot: table clicked." << endl; + emit(dateSelected(table->getDate())); + emit(tableClicked()); +} + +const ExtDate& +ExtDatePicker::getDate() const +{ + return table->getDate(); +} + +const ExtDate & +ExtDatePicker::date() const +{ + return table->getDate(); +} + +bool +ExtDatePicker::setDate(const ExtDate& date) +{ + if(date.isValid()) + { + table->setDate(date); // this also emits dateChanged() which then calls our dateChangedSlot() + return true; + } + else + { + kdDebug(298) << "ExtDatePicker::setDate: refusing to set invalid date." << endl; + return false; + } +} + +void +ExtDatePicker::monthForwardClicked() +{ + ExtDate temp; +// temp = KGlobal::locale()->calendar()->addMonths( table->getDate(), 1 ); + temp = d->calendar->addMonths( table->getDate(), 1 ); + setDate( temp ); +} + +void +ExtDatePicker::monthBackwardClicked() +{ + ExtDate temp; +// temp = KGlobal::locale()->calendar()->addMonths( table->getDate(), -1 ); + temp = d->calendar->addMonths( table->getDate(), -1 ); + setDate( temp ); +} + +void +ExtDatePicker::yearForwardClicked() +{ + ExtDate temp; +// temp = KGlobal::locale()->calendar()->addYears( table->getDate(), 1 ); + temp = d->calendar->addYears( table->getDate(), 1 ); + setDate( temp ); +} + +void +ExtDatePicker::yearBackwardClicked() +{ + ExtDate temp; +// temp = KGlobal::locale()->calendar()->addYears( table->getDate(), -1 ); + temp = d->calendar->addYears( table->getDate(), -1 ); + setDate( temp ); +} + +void ExtDatePicker::selectWeekClicked() {} // ### in 3.2 obsolete; kept for binary compatibility + +void +ExtDatePicker::weekSelected(int week) +{ +// const ExtCalendarSystem * calendar = KGlobal::locale()->calendar(); + + ExtDate date = table->getDate(); + int year = d->calendar->year(date); + + d->calendar->setYMD(date, year, 1, 1); // first day of selected year + + // calculate the first day in the selected week (day 1 is first day of week) + date = d->calendar->addDays(date, week * 7/*calendar->daysOfWeek()*/ -d->calendar->dayOfWeek(date) + 1); + + setDate(date); +} + +void +ExtDatePicker::selectMonthClicked() +{ + // every year can have different month names (in some calendar systems) +// const ExtCalendarSystem * calendar = KGlobal::locale()->calendar(); + ExtDate date = table->getDate(); + int i, month, months = d->calendar->monthsInYear(date); + + QPopupMenu popup(selectMonth); + + for (i = 1; i <= months; i++) + popup.insertItem(d->calendar->monthName(i, d->calendar->year(date)), i); + + popup.setActiveItem(d->calendar->month(date) - 1); + + if ( (month = popup.exec(selectMonth->mapToGlobal(QPoint(0, 0)), d->calendar->month(date) - 1)) == -1 ) return; // canceled + + int day = d->calendar->day(date); + // ----- construct a valid date in this month: + //date.setYMD(date.year(), month, 1); + //date.setYMD(date.year(), month, QMIN(day, date.daysInMonth())); + d->calendar->setYMD(date, d->calendar->year(date), month, + QMIN(day, d->calendar->daysInMonth(date))); + // ----- set this month + setDate(date); +} + +void +ExtDatePicker::selectYearClicked() +{ +// const ExtCalendarSystem * calendar = KGlobal::locale()->calendar(); + + if (selectYear->state() == QButton::Off) + { + return; + } + + int year; + KPopupFrame* popup = new KPopupFrame(this); + ExtDateInternalYearSelector* picker = new ExtDateInternalYearSelector(popup); + // ----- + picker->resize(picker->sizeHint()); + popup->setMainWidget(picker); + connect(picker, SIGNAL(closeMe(int)), popup, SLOT(close(int))); + picker->setFocus(); + if(popup->exec(selectYear->mapToGlobal(QPoint(0, selectMonth->height())))) + { + ExtDate date; + int day; + // ----- + year=picker->getYear(); + date=table->getDate(); + day=d->calendar->day(date); + // ----- construct a valid date in this month: + //date.setYMD(year, date.month(), 1); + //date.setYMD(year, date.month(), QMIN(day, date.daysInMonth())); + d->calendar->setYMD(date, year, d->calendar->month(date), + QMIN(day, d->calendar->daysInMonth(date))); + // ----- set this month + setDate(date); + } else { + KNotifyClient::beep(); + } + delete popup; +} + +void +ExtDatePicker::setEnabled(bool enable) +{ + QWidget *widgets[]= { + yearForward, yearBackward, monthForward, monthBackward, + selectMonth, selectYear, + line, table, d->selectWeek, d->todayButton }; + const int Size=sizeof(widgets)/sizeof(widgets[0]); + int count; + // ----- + for(count=0; count<Size; ++count) + { + widgets[count]->setEnabled(enable); + } +} + +void +ExtDatePicker::lineEnterPressed() +{ + ExtDate temp; + // ----- + if(val->date(line->text(), temp)==QValidator::Acceptable) + { + kdDebug(298) << "ExtDatePicker::lineEnterPressed: valid date entered." << endl; + emit(dateEntered(temp)); + setDate(temp); + } else { + KNotifyClient::beep(); + kdDebug(298) << "ExtDatePicker::lineEnterPressed: invalid date entered." << endl; + } +} + +void +ExtDatePicker::todayButtonClicked() +{ + setDate(ExtDate::currentDate()); +} + +QSize +ExtDatePicker::sizeHint() const +{ + return QWidget::sizeHint(); +} + +void +ExtDatePicker::setFontSize(int s) +{ + QWidget *buttons[]= { + // yearBackward, + // monthBackward, + selectMonth, + selectYear, + // monthForward, + // yearForward + }; + const int NoOfButtons=sizeof(buttons)/sizeof(buttons[0]); + int count; + QFont font; + QRect r; + // ----- + fontsize=s; + for(count=0; count<NoOfButtons; ++count) + { + font=buttons[count]->font(); + font.setPointSize(s); + buttons[count]->setFont(font); + } + QFontMetrics metrics(selectMonth->fontMetrics()); + + for (int i = 1; ; ++i) + { + QString str = d->calendar->monthName(i, + d->calendar->year(table->getDate()), false); + if (str.isNull()) break; + r=metrics.boundingRect(str); + maxMonthRect.setWidth(QMAX(r.width(), maxMonthRect.width())); + maxMonthRect.setHeight(QMAX(r.height(), maxMonthRect.height())); + } + + QSize metricBound = style().sizeFromContents(QStyle::CT_ToolButton, + selectMonth, + maxMonthRect); + selectMonth->setMinimumSize(metricBound); + + table->setFontSize(s); +} + +void +ExtDatePicker::setCloseButton( bool enable ) +{ + if ( enable == (d->closeButton != 0L) ) + return; + + if ( enable ) { + d->closeButton = new QToolButton( this ); + d->closeButton->setAutoRaise(true); + d->navigationLayout->addSpacing(KDialog::spacingHint()); + d->navigationLayout->addWidget(d->closeButton); + QToolTip::add(d->closeButton, i18n("Close")); + d->closeButton->setPixmap( SmallIcon("remove") ); + connect( d->closeButton, SIGNAL( clicked() ), + topLevelWidget(), SLOT( close() ) ); + } + else { + delete d->closeButton; + d->closeButton = 0L; + } + + updateGeometry(); +} + +bool ExtDatePicker::hasCloseButton() const +{ + return (d->closeButton != 0L); +} + +void ExtDatePicker::virtual_hook( int /*id*/, void* /*data*/ ) +{ /*BASE::virtual_hook( id, data );*/ } + diff --git a/libkdeedu/extdate/extdatepicker.h b/libkdeedu/extdate/extdatepicker.h new file mode 100644 index 00000000..58fae58a --- /dev/null +++ b/libkdeedu/extdate/extdatepicker.h @@ -0,0 +1,253 @@ +/* -*- C++ -*- + This file is part of the KDE libraries + Copyright (C) 1997 Tim D. Gilman (tdgilman@best.org) + (C) 1998-2001 Mirko Boehm (mirko@kde.org) + (C) 2004 Jason Harris (jharris@30doradus.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef EXTDATEPICKER_H +#define EXTDATEPICKER_H + + +#include "extdatetime.h" + +class KLineEdit; +class QToolButton; +class ExtDateValidator; +class ExtDateTable; + +/** + * Provides a widget for calendar date input. + * + * Different from the + * previous versions, it now emits two types of signals, either + * dateSelected() or dateEntered() (see documentation for both + * signals). + * + * A line edit has been added in the newer versions to allow the user + * to select a date directly by entering numbers like 19990101 + * or 990101. + * + * \image html kdatepicker.png "KDE Date Widget" + * + * @version $Id$ + * @author Tim Gilman, Mirko Boehm + * + * @short A date selection widget. + **/ +class KDE_EXPORT ExtDatePicker: public QFrame +{ + Q_OBJECT +// Q_PROPERTY( ExtDate date READ date WRITE setDate) + Q_PROPERTY( bool closeButton READ hasCloseButton WRITE setCloseButton ) + Q_PROPERTY( int fontSize READ fontSize WRITE setFontSize ) + +public: + /** The usual constructor. The given date will be displayed + * initially. + **/ + ExtDatePicker(QWidget *parent=0, + ExtDate=ExtDate::currentDate(), + const char *name=0); + + /** The usual constructor. The given date will be displayed + * initially. + * @since 3.1 + **/ + ExtDatePicker(QWidget *parent, + ExtDate, + const char *name, + WFlags f); // ### KDE 4.0: Merge + + /** + * Standard qt widget constructor. The initial date will be the + * current date. + * @since 3.1 + */ + ExtDatePicker( QWidget *parent, const char *name ); + + /** + * The destructor. + **/ + virtual ~ExtDatePicker(); + + /** The size hint for date pickers. The size hint recommends the + * minimum size of the widget so that all elements may be placed + * without clipping. This sometimes looks ugly, so when using the + * size hint, try adding 28 to each of the reported numbers of + * pixels. + **/ + QSize sizeHint() const; + + /** + * Sets the date. + * + * @returns @p false and does not change anything + * if the date given is invalid. + **/ + bool setDate(const ExtDate&); + + /** + * Returns the selected date. + * @deprecated + **/ + const ExtDate& getDate() const KDE_DEPRECATED; + + /** + * @returns the selected date. + */ + const ExtDate &date() const; + + /** + * Enables or disables the widget. + **/ + void setEnabled(bool); + + /** + * @returns the ExtDateTable widget child of this ExtDatePicker + * widget. + * @since 3.2 + */ + ExtDateTable *dateTable() const { return table; } + + /** + * Sets the font size of the widgets elements. + **/ + void setFontSize(int); + /** + * Returns the font size of the widget elements. + */ + int fontSize() const + { return fontsize; } + + /** + * By calling this method with @p enable = true, ExtDatePicker will show + * a little close-button in the upper button-row. Clicking the + * close-button will cause the ExtDatePicker's topLevelWidget()'s close() + * method being called. This is mostly useful for toplevel datepickers + * without a window manager decoration. + * @see hasCloseButton + * @since 3.1 + */ + void setCloseButton( bool enable ); + + /** + * @returns true if a ExtDatePicker shows a close-button. + * @see setCloseButton + * @since 3.1 + */ + bool hasCloseButton() const; + +protected: + /// to catch move keyEvents when QLineEdit has keyFocus + virtual bool eventFilter(QObject *o, QEvent *e ); + /// the resize event + virtual void resizeEvent(QResizeEvent*); + /// the year forward button + QToolButton *yearForward; + /// the year backward button + QToolButton *yearBackward; + /// the month forward button + QToolButton *monthForward; + /// the month backward button + QToolButton *monthBackward; + /// the button for selecting the month directly + QToolButton *selectMonth; + /// the button for selecting the year directly + QToolButton *selectYear; + /// the line edit to enter the date directly + KLineEdit *line; + /// the validator for the line edit: + ExtDateValidator *val; + /// the date table + ExtDateTable *table; + /// the size calculated during resize events + // QSize sizehint; + /// the widest month string in pixels: + QSize maxMonthRect; +protected slots: + void dateChangedSlot(const ExtDate&); + void tableClickedSlot(); + void monthForwardClicked(); + void monthBackwardClicked(); + void yearForwardClicked(); + void yearBackwardClicked(); + /** + * @since 3.1 + * @deprecated in 3.2 + */ + void selectWeekClicked(); + /** + * @since 3.1 + */ + void selectMonthClicked(); + /** + * @since 3.1 + */ + void selectYearClicked(); + /** + * @since 3.1 + */ + void lineEnterPressed(); + /** + * @since 3.2 + */ + void todayButtonClicked(); + /** + * @since 3.2 + */ + void weekSelected(int); + +signals: + /** This signal is emitted each time the selected date is changed. + * Usually, this does not mean that the date has been entered, + * since the date also changes, for example, when another month is + * selected. + * @see dateSelected + */ + void dateChanged(const ExtDate&); + /** This signal is emitted each time a day has been selected by + * clicking on the table (hitting a day in the current month). It + * has the same meaning as dateSelected() in older versions of + * ExtDatePicker. + */ + void dateSelected(const ExtDate&); + /** This signal is emitted when enter is pressed and a VALID date + * has been entered before into the line edit. Connect to both + * dateEntered() and dateSelected() to receive all events where the + * user really enters a date. + */ + void dateEntered(const ExtDate&); + /** This signal is emitted when the day has been selected by + * clicking on it in the table. + */ + void tableClicked(); + +private: + /// the font size for the widget + int fontsize; + +protected: + virtual void virtual_hook( int id, void* data ); +private: + void init( const ExtDate &dt ); + void fillWeeksCombo(const ExtDate &date); + class ExtDatePickerPrivate; + ExtDatePickerPrivate *d; +}; + +#endif // EXTDATEPICKER_H diff --git a/libkdeedu/extdate/extdatetbl.cpp b/libkdeedu/extdate/extdatetbl.cpp new file mode 100644 index 00000000..43a33a76 --- /dev/null +++ b/libkdeedu/extdate/extdatetbl.cpp @@ -0,0 +1,968 @@ +/* -*- C++ -*- + This file is part of the KDE libraries + Copyright (C) 1997 Tim D. Gilman (tdgilman@best.org) + (C) 1998-2001 Mirko Boehm (mirko@kde.org) + (C) 2004 Jason Harris (jharris@30doradus.org) + + These classes has been derived from those in kdatetbl.[h|cpp]. + The only differences are adaptations to use ExtDate instead of QDate, + to allow for more remote dates. These changes by Jason Harris. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/////////////////// ExtDateTable widget class ////////////////////// +// +// Copyright (C) 1997 Tim D. Gilman +// (C) 1998-2001 Mirko Boehm +// Written using Qt (http://www.troll.no) for the +// KDE project (http://www.kde.org) +// +// Modified to use ExtDate by Jason Harris, 2004 +// +// This is a support class for the ExtDatePicker class. It just +// draws the calender table without titles, but could theoretically +// be used as a standalone. +// +// When a date is selected by the user, it emits a signal: +// dateSelected(ExtDate) + +//#include "extdatepicker.h" +#include "extdatetbl.h" + +#include <kapplication.h> +#include <kdebug.h> +#include <knotifyclient.h> +#include "kpopupmenu.h" +#include <qpainter.h> +#include <qdict.h> +#include <assert.h> + + +class ExtDateTable::ExtDateTablePrivate +{ +public: + ExtDateTablePrivate() + { + popupMenuEnabled=false; + useCustomColors=false; + calendar = new ExtCalendarSystemGregorian(); + + } + + ~ExtDateTablePrivate() + { + delete calendar; + } + + bool popupMenuEnabled; + bool useCustomColors; + + struct DatePaintingMode + { + QColor fgColor; + QColor bgColor; + BackgroundMode bgMode; + }; + QDict <DatePaintingMode> customPaintingModes; + ExtCalendarSystem *calendar; +}; + + +ExtDateValidator::ExtDateValidator(QWidget* parent, const char* name) + : QValidator(parent, name) +{ +} + +QValidator::State +ExtDateValidator::validate(QString& text, int&) const +{ + ExtDate temp; + // ----- everything is tested in date(): + return date(text, temp); +} + +QValidator::State +ExtDateValidator::date(const QString& text, ExtDate& ed) const +{ + //FIXME: Can't uncomment unless ExtDate is adopted by KDE + //ExtDate tmp = KGlobal::locale()->readDate(text); + ExtDate tmp = ExtDate::fromString( text ); + + if (!tmp.isNull()) + { + ed = tmp; + return Acceptable; + } else + return Valid; +} + +void +ExtDateValidator::fixup( QString& ) const +{ + +} + +ExtDateTable::ExtDateTable(QWidget *parent, ExtDate date_, const char* name, WFlags f) + : QGridView(parent, name, f) +{ + d = new ExtDateTablePrivate; + setFontSize(10); + if(!date_.isValid()) + { + kdDebug() << "ExtDateTable ctor: WARNING: Given date is invalid, using current date." << endl; + date_=ExtDate::currentDate(); + } + setFocusPolicy( QWidget::StrongFocus ); + setNumRows(7); // 6 weeks max + headline + setNumCols(7); // 7 days a week + setHScrollBarMode(AlwaysOff); + setVScrollBarMode(AlwaysOff); + viewport()->setEraseColor(KGlobalSettings::baseColor()); + setDate(date_); // this initializes firstday, numdays, numDaysPrevMonth +} + +ExtDateTable::~ExtDateTable() +{ + delete d; +} + +int ExtDateTable::posFromDate( const ExtDate &dt ) +{ +//FIXME: Can't uncomment unless ExtDate is added to kdelibs +// const ExtCalendarSystem * calendar = KGlobal::locale()->calendar(); +// const int firstWeekDay = KGlobal::locale()->weekStartDay(); + + const int firstWeekDay = 7; + + int pos = d->calendar->day( dt ); + int offset = (firstday - firstWeekDay + 7) % 7; + // make sure at least one day of the previous month is visible. + // adjust this <1 if more days should be forced visible: + if ( offset < 1 ) offset += 7; + return pos + offset; +} + +ExtDate ExtDateTable::dateFromPos( int pos ) +{ + ExtDate pCellDate; + +//FIXME: Can't uncomment unless ExtDate is added to kdelibs +// const ExtCalendarSystem * calendar = KGlobal::locale()->calendar(); +// int firstWeekDay = KGlobal::locale()->weekStartDay(); + const int firstWeekDay = 7; + + d->calendar->setYMD(pCellDate, d->calendar->year(date), d->calendar->month(date), 1); + + int offset = (firstday - firstWeekDay + 7) % 7; + // make sure at least one day of the previous month is visible. + // adjust this <1 if more days should be forced visible: + if ( offset < 1 ) offset += 7; + pCellDate = d->calendar->addDays( pCellDate, pos - offset ); + + return pCellDate; +} + +void +ExtDateTable::paintCell(QPainter *painter, int row, int col) +{ +//FIXME: Can't uncomment unless ExtDate is added to kdelibs +// const ExtCalendarSystem * calendar = KGlobal::locale()->calendar(); +// int firstWeekDay = KGlobal::locale()->weekStartDay(); + const int firstWeekDay = 7; + + QRect rect; + QString text; + QPen pen; + int w=cellWidth(); + int h=cellHeight(); + QFont font=KGlobalSettings::generalFont(); + // ----- + + if(row==0) + { // we are drawing the headline + font.setBold(true); + painter->setFont(font); + bool normalday = true; + int daynum = ( col+firstWeekDay < 8 ) ? col+firstWeekDay : + col+firstWeekDay-7; + if ( daynum == d->calendar->weekDayOfPray() || + ( daynum == 6 && d->calendar->calendarName() == "gregorian" ) ) + normalday=false; + + QBrush brushTitle(); + QBrush brushInvertTitle(colorGroup().base()); + QColor titleColor(isEnabled()?( KGlobalSettings::activeTitleColor() ):( KGlobalSettings::inactiveTitleColor() ) ); + QColor textColor(isEnabled()?( KGlobalSettings::activeTextColor() ):( KGlobalSettings::inactiveTextColor() ) ); + if (!normalday) + { + painter->setPen(textColor); + painter->setBrush(textColor); + painter->drawRect(0, 0, w, h); + painter->setPen(titleColor); + } else { + painter->setPen(titleColor); + painter->setBrush(titleColor); + painter->drawRect(0, 0, w, h); + painter->setPen(textColor); + } + painter->drawText(0, 0, w, h-1, AlignCenter, + d->calendar->weekDayName(daynum, true), -1, &rect); + painter->setPen(colorGroup().text()); + painter->moveTo(0, h-1); + painter->lineTo(w-1, h-1); + // ----- draw the weekday: + } else { + bool paintRect=true; + painter->setFont(font); + int pos=7*(row-1)+col; + + ExtDate pCellDate = dateFromPos( pos ); + // First day of month + text = d->calendar->dayString(pCellDate, true); + if( d->calendar->month(pCellDate) != d->calendar->month(date) ) + { // we are either + // ° painting a day of the previous month or + // ° painting a day of the following month + // TODO: don't hardcode gray here! Use a color with less contrast to the background than normal text. + painter->setPen( colorGroup().mid() ); +// painter->setPen(gray); + } else { // paint a day of the current month + if ( d->useCustomColors ) + { + ExtDateTablePrivate::DatePaintingMode *mode=d->customPaintingModes[pCellDate.toString()]; + if (mode) + { + if (mode->bgMode != NoBgMode) + { + QBrush oldbrush=painter->brush(); + painter->setBrush( mode->bgColor ); + switch(mode->bgMode) + { + case(CircleMode) : painter->drawEllipse(0,0,w,h);break; + case(RectangleMode) : painter->drawRect(0,0,w,h);break; + case(NoBgMode) : // Should never be here, but just to get one + // less warning when compiling + default: break; + } + painter->setBrush( oldbrush ); + paintRect=false; + } + painter->setPen( mode->fgColor ); + } else + painter->setPen(colorGroup().text()); + } else //if ( firstWeekDay < 4 ) // <- this doesn' make sense at all! + painter->setPen(colorGroup().text()); + } + + pen=painter->pen(); + int offset=firstday-firstWeekDay; + if(offset<1) + offset+=7; + int dy = d->calendar->day(date); + if( ((offset+dy) == (pos+1)) && hasFocus()) + { + // draw the currently selected date + painter->setPen(colorGroup().highlight()); + painter->setBrush(colorGroup().highlight()); + pen=colorGroup().highlightedText(); + } else { + painter->setBrush(paletteBackgroundColor()); + painter->setPen(paletteBackgroundColor()); +// painter->setBrush(colorGroup().base()); +// painter->setPen(colorGroup().base()); + } + + if ( pCellDate == ExtDate::currentDate() ) + { + painter->setPen(colorGroup().text()); + } + + if ( paintRect ) painter->drawRect(0, 0, w, h); + painter->setPen(pen); + painter->drawText(0, 0, w, h, AlignCenter, text, -1, &rect); + } + if(rect.width()>maxCell.width()) maxCell.setWidth(rect.width()); + if(rect.height()>maxCell.height()) maxCell.setHeight(rect.height()); +} + +void +ExtDateTable::keyPressEvent( QKeyEvent *e ) +{ +//FIXME: Can't uncomment unless ExtDate is added to kdelibs +// const ExtCalendarSystem * calendar = KGlobal::locale()->calendar(); + + ExtDate temp = date; + + switch( e->key() ) { + case Key_Prior: + temp = d->calendar->addMonths( date, -1 ); + setDate(temp); + return; + case Key_Next: + temp = d->calendar->addMonths( date, 1 ); + setDate(temp); + return; + case Key_Up: + if ( d->calendar->day(date) > 7 ) { + setDate(date.addDays(-7)); + return; + } + break; + case Key_Down: + if ( d->calendar->day(date) <= d->calendar->daysInMonth(date)-7 ) { + setDate(date.addDays(7)); + return; + } + break; + case Key_Left: + if ( d->calendar->day(date) > 1 ) { + setDate(date.addDays(-1)); + return; + } + break; + case Key_Right: + if ( d->calendar->day(date) < d->calendar->daysInMonth(date) ) { + setDate(date.addDays(1)); + return; + } + break; + case Key_Minus: + setDate(date.addDays(-1)); + return; + case Key_Plus: + setDate(date.addDays(1)); + return; + case Key_N: + setDate(ExtDate::currentDate()); + return; + case Key_Return: + case Key_Enter: + emit tableClicked(); + return; + default: + break; + } + + KNotifyClient::beep(); +} + +void +ExtDateTable::viewportResizeEvent(QResizeEvent * e) +{ + QGridView::viewportResizeEvent(e); + + setCellWidth(viewport()->width()/7); + setCellHeight(viewport()->height()/7); +} + +void +ExtDateTable::setFontSize(int size) +{ +//FIXME: Can't uncomment unless ExtDate is added to kdelibs +// const ExtCalendarSystem * calendar = KGlobal::locale()->calendar(); + + int count; + QFontMetrics metrics(fontMetrics()); + QRect rect; + // ----- store rectangles: + fontsize=size; + // ----- find largest day name: + maxCell.setWidth(0); + maxCell.setHeight(0); + for(count=0; count<7; ++count) + { + rect=metrics.boundingRect(d->calendar->weekDayName(count+1, true)); + maxCell.setWidth(QMAX(maxCell.width(), rect.width())); + maxCell.setHeight(QMAX(maxCell.height(), rect.height())); + } + // ----- compare with a real wide number and add some space: + rect=metrics.boundingRect(QString::fromLatin1("88")); + maxCell.setWidth(QMAX(maxCell.width()+2, rect.width())); + maxCell.setHeight(QMAX(maxCell.height()+4, rect.height())); +} + +void +ExtDateTable::wheelEvent ( QWheelEvent * e ) +{ + setDate(date.addMonths( -(int)(e->delta()/120)) ); + e->accept(); +} + +void +ExtDateTable::contentsMousePressEvent(QMouseEvent *e) +{ + + if(e->type()!=QEvent::MouseButtonPress) + { // the ExtDatePicker only reacts on mouse press events: + return; + } + if(!isEnabled()) + { + KNotifyClient::beep(); + return; + } + + // ----- + int row, col, pos, temp; + QPoint mouseCoord; + // ----- + mouseCoord = e->pos(); + row=rowAt(mouseCoord.y()); + col=columnAt(mouseCoord.x()); + if(row<1 || col<0) + { // the user clicked on the frame of the table + return; + } + + // Rows and columns are zero indexed. The (row - 1) below is to avoid counting + // the row with the days of the week in the calculation. + + // old selected date: + temp = posFromDate( date ); + // new position and date + pos = (7 * (row - 1)) + col; + ExtDate clickedDate = dateFromPos( pos ); + + // set the new date. If it is in the previous or next month, the month will + // automatically be changed, no need to do that manually... + setDate( clickedDate ); + + // call updateCell on the old and new selection. If setDate switched to a different + // month, these cells will be painted twice, but that's no problem. + updateCell( temp/7+1, temp%7 ); + updateCell( row, col ); + + emit tableClicked(); + + if ( e->button() == Qt::RightButton && d->popupMenuEnabled ) + { + KPopupMenu *menu = new KPopupMenu(); + +//FIXME: Uncomment the following line (and remove the one after it) +// if ExtDate is added to kdelibs +// menu->insertTitle( KGlobal::locale()->formatDate(clickedDate) ); + menu->insertTitle( clickedDate.toString() ); + + emit aboutToShowContextMenu( menu, clickedDate ); + menu->popup(e->globalPos()); + } +} + +bool +ExtDateTable::setDate(const ExtDate& date_) +{ + bool changed=false; + ExtDate temp; + // ----- + if(!date_.isValid()) + { + kdDebug() << "ExtDateTable::setDate: refusing to set invalid date." << endl; + return false; + } + if(date!=date_) + { + emit(dateChanged(date, date_)); + date=date_; + emit(dateChanged(date)); + changed=true; + } + +//FIXME: Can't uncomment the following unless ExtDate is moved to kdelibs +// const ExtCalendarSystem * calendar = KGlobal::locale()->calendar(); + + d->calendar->setYMD(temp, d->calendar->year(date), d->calendar->month(date), 1); + //temp.setYMD(date.year(), date.month(), 1); + //kdDebug() << "firstDayInWeek: " << temp.toString() << endl; + firstday=temp.dayOfWeek(); + numdays=d->calendar->daysInMonth(date); + + temp = d->calendar->addMonths(temp, -1); + numDaysPrevMonth=d->calendar->daysInMonth(temp); + if(changed) + { + repaintContents(false); + } + return true; +} + +const ExtDate& +ExtDateTable::getDate() const +{ + return date; +} + +// what are those repaintContents() good for? (pfeiffer) +void ExtDateTable::focusInEvent( QFocusEvent *e ) +{ +// repaintContents(false); + QGridView::focusInEvent( e ); +} + +void ExtDateTable::focusOutEvent( QFocusEvent *e ) +{ +// repaintContents(false); + QGridView::focusOutEvent( e ); +} + +QSize +ExtDateTable::sizeHint() const +{ + if(maxCell.height()>0 && maxCell.width()>0) + { + return QSize(maxCell.width()*numCols()+2*frameWidth(), + (maxCell.height()+2)*numRows()+2*frameWidth()); + } else { + kdDebug() << "ExtDateTable::sizeHint: obscure failure - " << endl; + return QSize(-1, -1); + } +} + +void ExtDateTable::setPopupMenuEnabled( bool enable ) +{ + d->popupMenuEnabled=enable; +} + +bool ExtDateTable::popupMenuEnabled() const +{ + return d->popupMenuEnabled; +} + +void ExtDateTable::setCustomDatePainting(const ExtDate &date, const QColor &fgColor, BackgroundMode bgMode, const QColor &bgColor) +{ + if (!fgColor.isValid()) + { + unsetCustomDatePainting( date ); + return; + } + + ExtDateTablePrivate::DatePaintingMode *mode=new ExtDateTablePrivate::DatePaintingMode; + mode->bgMode=bgMode; + mode->fgColor=fgColor; + mode->bgColor=bgColor; + + d->customPaintingModes.replace( date.toString(), mode ); + d->useCustomColors=true; + update(); +} + +void ExtDateTable::unsetCustomDatePainting( const ExtDate &date ) +{ + d->customPaintingModes.remove( date.toString() ); +} + +ExtDateInternalWeekSelector::ExtDateInternalWeekSelector +(QWidget* parent, const char* name) + : KLineEdit(parent, name), + val(new QIntValidator(this)), + result(0) +{ + QFont font; + // ----- + font=KGlobalSettings::generalFont(); + setFont(font); + setFrameStyle(QFrame::NoFrame); + setValidator(val); + connect(this, SIGNAL(returnPressed()), SLOT(weekEnteredSlot())); +} + +void +ExtDateInternalWeekSelector::weekEnteredSlot() +{ + bool ok; + int week; + // ----- check if this is a valid week: + week=text().toInt(&ok); + if(!ok) + { + KNotifyClient::beep(); + return; + } + result=week; + emit(closeMe(1)); +} + +int +ExtDateInternalWeekSelector::getWeek() +{ + return result; +} + +void +ExtDateInternalWeekSelector::setWeek(int week) +{ + QString temp; + // ----- + temp.setNum(week); + setText(temp); +} + +void +ExtDateInternalWeekSelector::setMaxWeek(int max) +{ + val->setRange(1, max); +} + +// ### CFM To avoid binary incompatibility. +// In future releases, remove this and replace by a ExtDate +// private member, needed in ExtDateInternalMonthPicker::paintCell +class ExtDateInternalMonthPicker::ExtDateInternalMonthPrivate { +public: + ExtDateInternalMonthPrivate (int y, int m, int d) + : year(y), month(m), day(d) + { calendar = new ExtCalendarSystemGregorian(); } + ~ExtDateInternalMonthPrivate() + { delete calendar; } + + ExtCalendarSystem *calendar; + int year; + int month; + int day; +}; + +ExtDateInternalMonthPicker::~ExtDateInternalMonthPicker() { + delete d; +} + +ExtDateInternalMonthPicker::ExtDateInternalMonthPicker +(const ExtDate & date, QWidget* parent, const char* name) + : QGridView(parent, name), + result(0) // invalid +{ +//FIXME: Can't uncomment the following unless ExtDate is moved to kdelibs +// ExtCalendarSystem *calendar = KGlobal::locale()->calendar(); + + QRect rect; + QFont font; + // ----- + activeCol = -1; + activeRow = -1; + font=KGlobalSettings::generalFont(); + setFont(font); + setHScrollBarMode(AlwaysOff); + setVScrollBarMode(AlwaysOff); + setFrameStyle(QFrame::NoFrame); + setNumCols(3); + d = new ExtDateInternalMonthPrivate(date.year(), date.month(), date.day()); + // For monthsInYear != 12 + setNumRows( (d->calendar->monthsInYear(date) + 2) / 3); + // enable to find drawing failures: + // setTableFlags(Tbl_clipCellPainting); + viewport()->setEraseColor(KGlobalSettings::baseColor()); // for consistency with the datepicker + // ----- find the preferred size + // (this is slow, possibly, but unfortunately it is needed here): + QFontMetrics metrics(font); + for(int i = 1; ; ++i) + { + QString str = d->calendar->monthName(i, + d->calendar->year(date), false); + if (str.isNull()) break; + rect=metrics.boundingRect(str); + if(max.width()<rect.width()) max.setWidth(rect.width()); + if(max.height()<rect.height()) max.setHeight(rect.height()); + } +} + +QSize +ExtDateInternalMonthPicker::sizeHint() const +{ + return QSize((max.width()+6)*numCols()+2*frameWidth(), + (max.height()+6)*numRows()+2*frameWidth()); +} + +int +ExtDateInternalMonthPicker::getResult() const +{ + return result; +} + +void +ExtDateInternalMonthPicker::setupPainter(QPainter *p) +{ + p->setPen(KGlobalSettings::textColor()); +} + +void +ExtDateInternalMonthPicker::viewportResizeEvent(QResizeEvent*) +{ + setCellWidth(width() / numCols()); + setCellHeight(height() / numRows()); +} + +void +ExtDateInternalMonthPicker::paintCell(QPainter* painter, int row, int col) +{ + int index; + QString text; + // ----- find the number of the cell: + index=3*row+col+1; + text=d->calendar->monthName(index, + d->calendar->year(ExtDate(d->year, d->month, + d->day)), false); + painter->drawText(0, 0, cellWidth(), cellHeight(), AlignCenter, text); + if ( activeCol == col && activeRow == row ) + painter->drawRect( 0, 0, cellWidth(), cellHeight() ); +} + +void +ExtDateInternalMonthPicker::contentsMousePressEvent(QMouseEvent *e) +{ + if(!isEnabled() || e->button() != LeftButton) + { + KNotifyClient::beep(); + return; + } + // ----- + int row, col; + QPoint mouseCoord; + // ----- + mouseCoord = e->pos(); + row=rowAt(mouseCoord.y()); + col=columnAt(mouseCoord.x()); + + if(row<0 || col<0) + { // the user clicked on the frame of the table + activeCol = -1; + activeRow = -1; + } else { + activeCol = col; + activeRow = row; + updateCell( row, col /*, false */ ); + } +} + +void +ExtDateInternalMonthPicker::contentsMouseMoveEvent(QMouseEvent *e) +{ + if (e->state() & LeftButton) + { + int row, col; + QPoint mouseCoord; + // ----- + mouseCoord = e->pos(); + row=rowAt(mouseCoord.y()); + col=columnAt(mouseCoord.x()); + int tmpRow = -1, tmpCol = -1; + if(row<0 || col<0) + { // the user clicked on the frame of the table + if ( activeCol > -1 ) + { + tmpRow = activeRow; + tmpCol = activeCol; + } + activeCol = -1; + activeRow = -1; + } else { + bool differentCell = (activeRow != row || activeCol != col); + if ( activeCol > -1 && differentCell) + { + tmpRow = activeRow; + tmpCol = activeCol; + } + if ( differentCell) + { + activeRow = row; + activeCol = col; + updateCell( row, col /*, false */ ); // mark the new active cell + } + } + if ( tmpRow > -1 ) // repaint the former active cell + updateCell( tmpRow, tmpCol /*, true */ ); + } +} + +void +ExtDateInternalMonthPicker::contentsMouseReleaseEvent(QMouseEvent *e) +{ + if(!isEnabled()) + { + return; + } + // ----- + int row, col, pos; + QPoint mouseCoord; + // ----- + mouseCoord = e->pos(); + row=rowAt(mouseCoord.y()); + col=columnAt(mouseCoord.x()); + if(row<0 || col<0) + { // the user clicked on the frame of the table + emit(closeMe(0)); + } + + pos=3*row+col+1; + result=pos; + emit(closeMe(1)); +} + + + +ExtDateInternalYearSelector::ExtDateInternalYearSelector +(QWidget* parent, const char* name) + : QLineEdit(parent, name), + val(new QIntValidator(this)), + result(0), + d(new ExtDateInternalYearPrivate()) +{ + QFont font; + // ----- + font=KGlobalSettings::generalFont(); + setFont(font); + setFrameStyle(QFrame::NoFrame); + // set year limits (perhaps we should get rid of limits altogether) + //there si also a year limit in ExtCalendarSystemGregorian... + val->setRange(-50000, 50000); + setValidator(val); + connect(this, SIGNAL(returnPressed()), SLOT(yearEnteredSlot())); +} + +ExtDateInternalYearSelector::~ExtDateInternalYearSelector() { + delete val; + delete d; +} + +void +ExtDateInternalYearSelector::yearEnteredSlot() +{ + bool ok; + int year; + ExtDate date; + // ----- check if this is a valid year: + year=text().toInt(&ok); + if(!ok) + { + KNotifyClient::beep(); + return; + } + //date.setYMD(year, 1, 1); + d->calendar->setYMD(date, year, 1, 1); + if(!date.isValid()) + { + KNotifyClient::beep(); + return; + } + result=year; + emit(closeMe(1)); +} + +int +ExtDateInternalYearSelector::getYear() +{ + return result; +} + +void +ExtDateInternalYearSelector::setYear(int year) +{ + QString temp; + // ----- + temp.setNum(year); + setText(temp); +} + +KPopupFrame::KPopupFrame(QWidget* parent, const char* name) + : QFrame(parent, name, WType_Popup), + result(0), // rejected + main(0) +{ + setFrameStyle(QFrame::Box|QFrame::Raised); + setMidLineWidth(2); +} + +void +KPopupFrame::keyPressEvent(QKeyEvent* e) +{ + if(e->key()==Key_Escape) + { + result=0; // rejected + qApp->exit_loop(); + } +} + +void +KPopupFrame::close(int r) +{ + result=r; + qApp->exit_loop(); +} + +void +KPopupFrame::setMainWidget(QWidget* m) +{ + main=m; + if(main!=0) + { + resize(main->width()+2*frameWidth(), main->height()+2*frameWidth()); + } +} + +void +KPopupFrame::resizeEvent(QResizeEvent*) +{ + if(main!=0) + { + main->setGeometry(frameWidth(), frameWidth(), + width()-2*frameWidth(), height()-2*frameWidth()); + } +} + +void +KPopupFrame::popup(const QPoint &pos) +{ + // Make sure the whole popup is visible. + QRect d = KGlobalSettings::desktopGeometry(pos); + + int x = pos.x(); + int y = pos.y(); + int w = width(); + int h = height(); + if (x+w > d.x()+d.width()) + x = d.width() - w; + if (y+h > d.y()+d.height()) + y = d.height() - h; + if (x < d.x()) + x = 0; + if (y < d.y()) + y = 0; + + // Pop the thingy up. + move(x, y); + show(); +} + +int +KPopupFrame::exec(QPoint pos) +{ + popup(pos); + repaint(); + qApp->enter_loop(); + hide(); + return result; +} + +int +KPopupFrame::exec(int x, int y) +{ + return exec(QPoint(x, y)); +} + +void KPopupFrame::virtual_hook( int, void* ) +{ /*BASE::virtual_hook( id, data );*/ } + +void ExtDateTable::virtual_hook( int, void* ) +{ /*BASE::virtual_hook( id, data );*/ } + +#include "extdatetbl.moc" diff --git a/libkdeedu/extdate/extdatetbl.h b/libkdeedu/extdate/extdatetbl.h new file mode 100644 index 00000000..ac9b7156 --- /dev/null +++ b/libkdeedu/extdate/extdatetbl.h @@ -0,0 +1,427 @@ +/* -*- C++ -*- + This file is part of the KDE libraries + Copyright (C) 1997 Tim D. Gilman (tdgilman@best.org) + (C) 1998-2001 Mirko Boehm (mirko@kde.org) + (C) 2004 Jason Harris (jharris@30doradus.org) + + These classes has been derived from those in kdatetbl.[h|cpp]. + The only differences are adaptations to use ExtDate instead of QDate, + to allow for more remote dates. These changes by Jason Harris. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef EXTDATETBL_H +#define EXTDATETBL_H + +#include <qvalidator.h> +#include <qgridview.h> +#include <klineedit.h> +#include "extcalendarsystemgregorian.h" + +class KPopupMenu; + +/** Week selection widget. +* @internal +* @version $Id$ +* @author Stephan Binner +*/ +class ExtDateInternalWeekSelector : public KLineEdit +{ + Q_OBJECT +protected: + QIntValidator *val; + int result; +public slots: + void weekEnteredSlot(); + void setMaxWeek(int max); +signals: + void closeMe(int); +public: + ExtDateInternalWeekSelector( QWidget* parent=0, const char* name=0); + int getWeek(); + void setWeek(int week); + +private: + class ExtDateInternalWeekPrivate; + ExtDateInternalWeekPrivate *d; +}; + +/** +* A table containing month names. It is used to pick a month directly. +* @internal +* @version $Id$ +* @author Tim Gilman, Mirko Boehm +*/ +class ExtDateInternalMonthPicker : public QGridView +{ + Q_OBJECT +protected: + /** + * Store the month that has been clicked [1..12]. + */ + int result; + /** + * the cell under mouse cursor when LBM is pressed + */ + short int activeCol; + short int activeRow; + /** + * Contains the largest rectangle needed by the month names. + */ + QRect max; +signals: + /** + * This is send from the mouse click event handler. + */ + void closeMe(int); +public: + /** + * The constructor. + */ + ExtDateInternalMonthPicker(const ExtDate& date, QWidget* parent, const char* name=0); + /** + * The destructor. + */ + ~ExtDateInternalMonthPicker(); + /** + * The size hint. + */ + QSize sizeHint() const; + /** + * Return the result. 0 means no selection (reject()), 1..12 are the + * months. + */ + int getResult() const; +protected: + /** + * Set up the painter. + */ + void setupPainter(QPainter *p); + /** + * The resize event. + */ + virtual void viewportResizeEvent(QResizeEvent*); + /** + * Paint a cell. This simply draws the month names in it. + */ + virtual void paintCell(QPainter* painter, int row, int col); + /** + * Catch mouse click and move events to paint a rectangle around the item. + */ + virtual void contentsMousePressEvent(QMouseEvent *e); + virtual void contentsMouseMoveEvent(QMouseEvent *e); + /** + * Emit monthSelected(int) when a cell has been released. + */ + virtual void contentsMouseReleaseEvent(QMouseEvent *e); + +private: + class ExtDateInternalMonthPrivate; + ExtDateInternalMonthPrivate *d; +}; + +/** Year selection widget. +* @internal +* @version $Id$ +* @author Tim Gilman, Mirko Boehm +*/ +class ExtDateInternalYearSelector : public QLineEdit +{ + Q_OBJECT +protected: + QIntValidator *val; + int result; +public slots: + void yearEnteredSlot(); +signals: + void closeMe(int); +public: + ExtDateInternalYearSelector( QWidget* parent=0, const char* name=0); + ~ExtDateInternalYearSelector(); + int getYear(); + void setYear(int year); + +private: + class ExtDateInternalYearPrivate { + public: + ExtDateInternalYearPrivate() { + calendar = new ExtCalendarSystemGregorian(); + } + ~ExtDateInternalYearPrivate() { + delete calendar; + } + ExtCalendarSystem *calendar; + }; + ExtDateInternalYearPrivate *d; + +}; + +/** + * Frame with popup menu behavior. + * @author Tim Gilman, Mirko Boehm + * @version $Id$ + */ +class KPopupFrame : public QFrame +{ + Q_OBJECT +protected: + /** + * The result. It is returned from exec() when the popup window closes. + */ + int result; + /** + * Catch key press events. + */ + virtual void keyPressEvent(QKeyEvent* e); + /** + * The only subwidget that uses the whole dialog window. + */ + QWidget *main; +public slots: + /** + * Close the popup window. This is called from the main widget, usually. + * @p r is the result returned from exec(). + */ + void close(int r); +public: + /** + * The contructor. Creates a dialog without buttons. + */ + KPopupFrame(QWidget* parent=0, const char* name=0); + /** + * Set the main widget. You cannot set the main widget from the constructor, + * since it must be a child of the frame itselfes. + * Be careful: the size is set to the main widgets size. It is up to you to + * set the main widgets correct size before setting it as the main + * widget. + */ + void setMainWidget(QWidget* m); + /** + * The resize event. Simply resizes the main widget to the whole + * widgets client size. + */ + virtual void resizeEvent(QResizeEvent*); + /** + * Open the popup window at position pos. + */ + void popup(const QPoint &pos); + /** + * Execute the popup window. + */ + int exec(QPoint p); + /** + * Dito. + */ + int exec(int x, int y); + +private: + + virtual bool close(bool alsoDelete) { return QFrame::close(alsoDelete); } +protected: + virtual void virtual_hook( int id, void* data ); +private: + class KPopupFramePrivate; + KPopupFramePrivate *d; +}; + +/** +* Validates user-entered dates. +*/ +class ExtDateValidator : public QValidator +{ +public: + ExtDateValidator(QWidget* parent=0, const char* name=0); + virtual State validate(QString&, int&) const; + virtual void fixup ( QString & input ) const; + State date(const QString&, ExtDate&) const; +}; + +/** + * Date selection table. + * This is a support class for the ExtDatePicker class. It just + * draws the calender table without titles, but could theoretically + * be used as a standalone. + * + * When a date is selected by the user, it emits a signal: + * dateSelected(ExtDate) + * + * @internal + * @version $Id$ + * @author Tim Gilman, Mirko Boehm + */ +class ExtDateTable : public QGridView +{ + Q_OBJECT + //Q_PROPERTY( ExtDate date READ getDate WRITE setDate ) + Q_PROPERTY( bool popupMenu READ popupMenuEnabled WRITE setPopupMenuEnabled ) + +public: + /** + * The constructor. + */ + ExtDateTable(QWidget *parent=0, + ExtDate date=ExtDate::currentDate(), + const char* name=0, WFlags f=0); + + /** + * The destructor. + */ + ~ExtDateTable(); + + /** + * Returns a recommended size for the widget. + * To save some time, the size of the largest used cell content is + * calculated in each paintCell() call, since all calculations have + * to be done there anyway. The size is stored in maxCell. The + * sizeHint() simply returns a multiple of maxCell. + */ + virtual QSize sizeHint() const; + /** + * Set the font size of the date table. + */ + void setFontSize(int size); + /** + * Select and display this date. + */ + bool setDate(const ExtDate&); + // ### 4.0 rename to date() + const ExtDate& getDate() const; + + /** + * Enables a popup menu when right clicking on a date. + * + * When it's enabled, this object emits a aboutToShowContextMenu signal + * where you can fill in the menu items. + * + * @since 3.2 + */ + void setPopupMenuEnabled( bool enable ); + + /** + * Returns if the popup menu is enabled or not + */ + bool popupMenuEnabled() const; + + enum BackgroundMode { NoBgMode=0, RectangleMode, CircleMode }; + + /** + * Makes a given date be painted with a given foregroundColor, and background + * (a rectangle, or a circle/ellipse) in a given color. + * + * @since 3.2 + */ + void setCustomDatePainting( const ExtDate &date, const QColor &fgColor, BackgroundMode bgMode=NoBgMode, const QColor &bgColor=QColor()); + + /** + * Unsets the custom painting of a date so that the date is painted as usual. + * + * @since 3.2 + */ + void unsetCustomDatePainting( const ExtDate &date ); + +protected: + /** + * calculate the position of the cell in the matrix for the given date. The result is the 0-based index. + */ + int posFromDate( const ExtDate &date ); // KDE4: make this virtual, so subclasses can reimplement this and use a different default for the start of the matrix + /** + * calculate the date that is displayed at a given cell in the matrix. pos is the + * 0-based index in the matrix. Inverse function to posForDate(). + */ + ExtDate dateFromPos( int pos ); // KDE4: make this virtual + + /** + * Paint a cell. + */ + virtual void paintCell(QPainter*, int, int); + /** + * Handle the resize events. + */ + virtual void viewportResizeEvent(QResizeEvent *); + /** + * React on mouse clicks that select a date. + */ + virtual void contentsMousePressEvent(QMouseEvent *); + virtual void wheelEvent( QWheelEvent * e ); + virtual void keyPressEvent( QKeyEvent *e ); + virtual void focusInEvent( QFocusEvent *e ); + virtual void focusOutEvent( QFocusEvent *e ); + + // ### KDE 4.0 make the following private and mark as members + + /** + * The font size of the displayed text. + */ + int fontsize; + /** + * The currently selected date. + */ + ExtDate date; + /** + * The day of the first day in the month [1..7]. + */ + int firstday; + /** + * The number of days in the current month. + */ + int numdays; + /** + * The number of days in the previous month. + */ + int numDaysPrevMonth; + /** + * unused + * ### remove in KDE 4.0 + */ + bool unused_hasSelection; + /** + * Save the size of the largest used cell content. + */ + QRect maxCell; +signals: + /** + * The selected date changed. + */ + void dateChanged(const ExtDate&); + /** + * This function behaves essentially like the one above. + * The selected date changed. + * @param cur The current date + * @param old The date before the date was changed + */ + void dateChanged(const ExtDate& cur, const ExtDate& old); + /** + * A date has been selected by clicking on the table. + */ + void tableClicked(); + + /** + * A popup menu for a given date is about to be shown (as when the user + * right clicks on that date and the popup menu is enabled). Connect + * the slot where you fill the menu to this signal. + * + * @since 3.2 + */ + void aboutToShowContextMenu( KPopupMenu * menu, const ExtDate &date); + +protected: + virtual void virtual_hook( int id, void* data ); +private: + class ExtDateTablePrivate; + ExtDateTablePrivate *d; +}; + +#endif // EXTDATETBL_H diff --git a/libkdeedu/extdate/extdatetime.cpp b/libkdeedu/extdate/extdatetime.cpp new file mode 100644 index 00000000..36e3860d --- /dev/null +++ b/libkdeedu/extdate/extdatetime.cpp @@ -0,0 +1,1148 @@ +/************************************************************************* +** Definition of extended range date classe +** (c) 2004 by Michel Guitel <michel.guitel@sap.ap-hop-paris.fr> +** modifications by Jason Harris <kstars@30doradus.org> +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +**********************************************************************/ + +#include "extdatetime.h" +#include <qregexp.h> + +#include <kglobal.h> +#include <klocale.h> +#include <kdebug.h> +#include <assert.h> +#include <time.h> + +static const uint SECS_PER_DAY = 86400; +static const uint MSECS_PER_DAY = 86400000; +static const uint SECS_PER_HOUR = 3600; +static const uint MSECS_PER_HOUR= 3600000; +static const uint SECS_PER_MIN = 60; +static const uint MSECS_PER_MIN = 60000; + +/***************************************************************************** + ExtDate class + *****************************************************************************/ + +/***************************************************************************** + * + * Concepts : + * a date is represented internally by its Julian Day number, a simple count + * of the number of days since a remote, arbitrary date (01 January, 4713 BC). + * This date has Julian Day number zero. + * + * ***************************************************************************/ + +uint ExtDate::m_monthLength[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; +uint ExtDate::m_monthOrigin[] = { 0, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + +QString ExtDate::m_shortMonthNames[12] = { + i18n("Short month name", "Jan"), i18n("Short month name", "Feb"), + i18n("Short month name", "Mar"), i18n("Short month name", "Apr"), + i18n("Short month name", "May"), i18n("Short month name", "Jun"), + i18n("Short month name", "Jul"), i18n("Short month name", "Aug"), + i18n("Short month name", "Sep"), i18n("Short month name", "Oct"), + i18n("Short month name", "Nov"), i18n("Short month name", "Dec") +}; +QString ExtDate::m_shortDayNames[7] = { + i18n("Short day name", "Mon"), i18n("Short day name", "Tue"), + i18n("Short day name", "Wed"), i18n("Short day name", "Thu"), + i18n("Short day name", "Fri"), i18n("Short day name", "Sat"), + i18n("Short day name", "Sun") +}; +QString ExtDate::m_longMonthNames[12] = { + i18n("Long month name", "January"), i18n("Long month name", "February"), + i18n("Long month name", "March"), i18n("Long month name", "April"), + i18n("Long month name", "May"), i18n("Long month name", "June"), + i18n("Long month name", "July"), i18n("Long month name", "August"), + i18n("Long month name", "September"), i18n("Long month name", "October"), + i18n("Long month name", "November"), i18n("Long month name", "December") +}; +QString ExtDate::m_longDayNames[7] = { + i18n("Long day name", "Monday"), i18n("Long day name", "Tuesday"), + i18n("Long day name", "Wednesday"), i18n("Long day name", "Thursday"), + i18n("Long day name", "Friday"), i18n("Long day name", "Saturday"), + i18n("Long day name", "Sunday") +}; + +ExtDate::ExtDate( int y, int m, int d) +{ + if ( !isValid(y,m,d) ) { +#if defined(QT_CHECK_RANGE) + qWarning( "ExtDate: Invalid date %04d-%02d-%02d", y, m, d ); +#endif + m_year = 0; + m_month = 0; + m_day = 0; + m_jd = INVALID_DAY; + } else { + m_year = y; + m_month = m; + m_day = d; + m_jd = GregorianToJD(y, m, d); + } +} + +ExtDate::ExtDate( long int jd ) { + m_jd = jd; + JDToGregorian( jd, m_year, m_month, m_day ); +} + +long int ExtDate::GregorianToJD( int year, int month, int day ) +{ + int m, y, A, B, C, D; + + if (month > 2) { + m = month; + y = year; + } else { + y = year - 1; + m = month + 12; + } + +/* If the date is after 10/15/1582, then take Pope Gregory's modification + to the Julian calendar into account */ + + if ( ( year >1582 ) || + ( year ==1582 && month >9 ) || + ( year ==1582 && month ==9 && day >15 )) + { + A = int(y/100); + B = 2 - A + int(A/4); + } else { + B = 0; + } + + if (y < 0) { + C = int((365.25*y) - 0.75); + } else { + C = int(365.25*y); + } + + D = int(30.6001*(m+1)); + + long int jd = B + C + D + day + 1720995; + + return jd; +} + +void ExtDate::JDToGregorian( long int jd, int &year, int &month, int &day ) +{ + int a, b, c, d, e, alpha; + + if (jd<2299161) { + a = jd; + } else { + alpha = int ((jd-1867216.25)/ 36524.25); + a = jd + 1 + alpha - int(alpha / 4.0); + } + b = a + 1524; + c = int ((b-122.1)/ 365.25); + d = int (365.25*c); + e = int ((b-d)/ 30.6001); + + day = b-d-int(30.6001*e); + month = (e<14) ? e-1 : e-13; + year = (month>2) ? c-4716 : c-4715; +} + +bool ExtDate::isValid() const +{ + return ( jd() != INVALID_DAY && isValid( year(), month(), day() ) ); +} + +int ExtDate::dayOfWeek() const +{ + //JD 2451545 (01 Jan 2000) was a Saturday, which is dayOfWeek=6. + int a_day = (( jd() - 2451545 + 6 ) % 7); + if ( a_day < 0 ) a_day += 7; + return (a_day == 0) ? 7 : a_day; +} + +int ExtDate::dayOfYear() const +{ + return jd() - GregorianToJD( year(), 1, 1) + 1; +} + +int ExtDate::daysInMonth() const +{ + if ( isValid() ) { + int m = month(); + int d = m_monthLength[m-1]; + if (m==2 && leapYear(year())) d++; + return d; + } else { + return 31; + } +} + +int ExtDate::daysInYear() const +{ + if ( ! isValid() ) return 365; + return (leapYear(year()) ? 366 : 365); +} + +int ExtDate::weekNumber( int *yearNum ) const +{ + //ISO 8601: + //Week 1 is the week containing the first Thursday of the year. + ExtDate day1( year(), 1, 1 ); //First day of the year + + if ( day1.dayOfWeek() > 4 ) { + //Jan 1 is after Thursday, so it's in the previous year's last week. + //Set day1 to be the following Monday, which is the start of week 1 + day1 = day1.addDays( 7 - day1.dayOfWeek() + 1 ); + } else { + //Jan 1 is before Friday, so it is in Week 1. + //Set day1 to be the preceding Monday + day1 = day1.addDays( 1 - day1.dayOfWeek() ); + } + + //Is the target date prior to day1? If so, the target is in the + //last week of the previous year. + if ( day1.daysTo( *this ) < 0 ) { + if ( yearNum ) *yearNum = year() - 1; + + //The last week of the year always contains Dec 28th (ISO 8601) + ExtDate lastDec28( year()-1, 12, 28 ); + return lastDec28.weekNumber(); + } + + //If the target date is after Dec 28th, it's possible that it is in + //Week 1 of the following year. + ExtDate dec28( year(), 12, 28 ); + if ( dayOfYear() > dec28.dayOfYear() && dayOfWeek() < 4 ) { + if ( yearNum ) *yearNum = year() + 1; + return 1; + } + + //If we reach here, the week number will be in this year. + int week = 1 + int( day1.daysTo( *this )/7 ); + + if ( yearNum ) *yearNum = year(); + return week; +} + +#ifndef QT_NO_TEXTDATE +QString ExtDate::shortMonthName( int month ) {return m_shortMonthNames[month-1];} +QString ExtDate::shortDayName( int weekday ) {return m_shortDayNames[weekday-1];} +QString ExtDate::longMonthName( int month ) {return m_longMonthNames[month-1];} +QString ExtDate::longDayName( int weekday ) {return m_longDayNames[weekday-1];} +#endif //QT_NO_TEXTDATE + +#ifndef QT_NO_TEXTSTRING +#if !defined(QT_NO_SPRINTF) +QString ExtDate::toString( Qt::DateFormat f) const +{ + QString a_format; + + if ( ! isValid() ) return QString::null; + + switch (f) + { + case Qt::TextDate : // Sat May 20 1995 + a_format = "%a %b %e %Y"; + break; + + case Qt::ISODate : // YYYY-MM-DD + a_format = "%Y-%m-%d"; + break; + + case Qt::LocalDate : // local settings + a_format = KGlobal::locale()->dateFormat(); + break; + + default : + a_format = "toString : unknown format"; + break; + + } + return toString(a_format); +} +#endif + +QString ExtDate::toString( const QString& format ) const +{ + if ( ! isValid() ) return QString::null; + + //We use the KDE Date format specs. + //Replace occurences of the following tokens with their + //corresponding values: + // + // %Y The year, including centuries prefix (e.g., "1984") + // %y The year, excluding centuries prefix (e.g., "84") + // %n Numerical month value (e.g., "3" for March) + // %m Numerical month value, two digits (e.g., "03" for March) + // %e Numerical day value (e.g., "3" on March 3rd) + // %d Numerical day value, two digits (e.g., "03" on March 3rd) + // %b Month name, short form (e.g., "Mar" for March) + // %B Month name, long form (e.g., "March") + // %a Weekday name, short form (e.g., "Wed" for Wednesday) + // %A Weekday name, long form (e.g., "Wednesday") + + //All other characters are left as-is. + + QString result( format ); + + result.replace( "%Y", QString().sprintf( "%d", year() ) ); + result.replace( "%y", QString().sprintf( "%02d", (year() % 100) ) ); + result.replace( "%n", QString().sprintf( "%d", month() ) ); + result.replace( "%m", QString().sprintf( "%02d", month() ) ); + result.replace( "%e", QString().sprintf( "%d", day() ) ); + result.replace( "%d", QString().sprintf( "%02d", day() ) ); + result.replace( "%b", shortMonthName( month() ) ); + result.replace( "%B", longMonthName( month() ) ); + result.replace( "%a", shortDayName( dayOfWeek() ) ); + result.replace( "%A", longDayName( dayOfWeek() ) ); + + return result; +} +#endif + +bool ExtDate::setYMD( int y, int m, int d ) +{ + if ( ! isValid(y,m,d) ) { +#if defined(QT_CHECK_RANGE) + qWarning( "ExtDate: Invalid date %04d-%02d-%02d", y, m, d ); +#endif + m_year = 0; + m_month = 0; + m_day = 0; + m_jd = INVALID_DAY; + return false; + } else { + m_year = y; + m_month = m; + m_day = d; + m_jd = GregorianToJD( y, m, d ); + return true; + } +} + +bool ExtDate::setJD( long int _jd ) { + if ( _jd == INVALID_DAY ) { + m_jd = _jd; + m_year = 0; + m_month = 0; + m_day = 0; + return false; + } else { + m_jd = _jd; + JDToGregorian( _jd, m_year, m_month, m_day ); + return true; + } +} + +ExtDate ExtDate::addDays( int days ) const +{ + ExtDate a_date; + a_date.setJD( jd() + days ); + return a_date; +} + +ExtDate ExtDate::addMonths( int months ) const +{ + int a_month = month() + months%12; + int a_year = year() + int(months/12); + + while ( a_month < 1 ) { + a_month += 12; + a_year--; + } + + while ( a_month > 12 ) { + a_month -= 12; + a_year++; + } + + return ExtDate(a_year, a_month, day()); +} + +ExtDate ExtDate::addYears( int years ) const +{ + return ExtDate(year() + years, month(), day()); +} + +int ExtDate::daysTo( const ExtDate & a_date) const +{ + return a_date.jd() - jd(); +} + +ExtDate ExtDate::currentDate(Qt::TimeSpec ts) +{ + time_t a_current_time; + struct tm a_current_time_tm; + + time(&a_current_time); + switch (ts) + { + case Qt::LocalTime : + localtime_r(&a_current_time, &a_current_time_tm); + break; + + case Qt::UTC : + gmtime_r(&a_current_time, &a_current_time_tm); + break; + + default : + assert(0); + break; + } + return ExtDate(a_current_time_tm.tm_year + 1900, a_current_time_tm.tm_mon + 1, a_current_time_tm.tm_mday); +} + +#ifndef QT_NO_DATESTRING +//Try both DateFormat values +ExtDate ExtDate::fromString( const QString& s ) +{ + ExtDate dResult = ExtDate::fromString( s, Qt::TextDate ); + if ( dResult.isValid() ) return dResult; + + dResult = ExtDate::fromString( s, Qt::ISODate ); + if ( dResult.isValid() ) return dResult; + else return ExtDate(); //invalid +} + +ExtDate ExtDate::fromString( const QString& s, Qt::DateFormat f ) +{ + ExtDate dt = ExtDate(); //initialize invalid date + if ( s.isEmpty() ) { return dt; } + if ( f == Qt::LocalDate ) { //can't use LocalFormat +#if defined(QT_CHECK_RANGE) + qWarning( "QDate::fromString: Parameter out of range" ); +#endif + return dt; + } + + switch( f ) { + case Qt::ISODate : + { + int year( s.mid( 0, 4 ).toInt() ); + int month( s.mid( 5, 2 ).toInt() ); + int day( s.mid( 8, 2 ).toInt() ); + + if ( year && month && day ) + return ExtDate( year, month, day ); + } + break; + + default : +#ifndef QT_NO_TEXTDATE + case Qt::TextDate : + { + //Three possible date formats: + //dd mth yyyy; mth dd yyyy; wkd mth dd yyyy + //"mth" is the word for the month (long or short form) + QStringList ss = QStringList::split( " ", s ); + bool ok = false; + int month = -1; + uint imonth = 0; + uint iyear = 0; + + //If neither of the first two words is a number, then we'll assume + //the first word is a superfluous "weekday" string + int day = ss[0].toInt( &ok ); + if ( ! ok ) { + day = ss[1].toInt( &ok ); + if ( ! ok ) { + day = ss[2].toInt( &ok ); + if ( !ok ) return dt; //could not find a valid day number in first three words + imonth = 1; //the month must be the second word + iyear = 3; //the year must be the fourth word + } else { + //the month is either the first word, or the third. + imonth = 0; + iyear = 2; + } + } else { + //month is the second word + imonth = 1; + iyear = 2; + } + + for ( uint i = 0; i < 12; i++ ) { + if ( ss[imonth] == shortMonthName( i+1 ) || ss[imonth] == longMonthName( i+1 ) ) { + month = i + 1; + break; + } + } + + if ( month == -1 && imonth == 0 ) { //try the third word + imonth = 2; + iyear = 3; + for ( uint i = 0; i < 12; i++ ) { + if ( ss[imonth] == shortMonthName( i+1 ) || ss[imonth] == longMonthName( i+1 ) ) { + month = i + 1; + break; + } + } + } + + if ( month > -1 ) ok = true; + if ( ! ok ) return dt; //could not parse month; return invalid + + int year = ss[iyear].toInt( &ok ); + if ( ! ok ) return dt; //could not parse year; return invalid + + return ExtDate( year, month, day ); + + break; + } +#else + break; +#endif //ifndef QT_NO_TEXTDATE + } + + return dt; +} +#endif //ifndef QT_NO_DATESTRING + +bool ExtDate::isValid( int y, int m, int d ) +{ + if (m < 1 || m > 12) return false; + if (d < 1) return false; + if (m != 2 && d > (int) m_monthLength[m-1]) return false; + if (m == 2 && d > ( (int) m_monthLength[1] + (leapYear(y) ? 1 : 0))) return false; + return true; +} + +QDate ExtDate::qdate() const { + QDate q( year(), month(), day() ); + + if ( q.isValid() ) + return q; + else + return QDate(); +} + +bool ExtDate::leapYear( int year ) +{ + // year is the year-number where JC birth is 0 + if ((year % 4) != 0) return false; + // multiple of 4 : can be a leap year + // centennial years are NOT leap, but quadri-centennial ARE. + if ((year % 400) == 0) return true; + if ((year % 100) == 0) return false; + // year is multiple of 4 but not centennial so leap year ! + return true; +} + +int ExtDate::dayOfYear(int y, int m, int d) +{ + return m_monthOrigin[m-1] + d + ((m > 1) ? (leapYear(y) ? 1 : 0) : 0); +} + +/***************************************************************************** + ExtDateTime member functions + *****************************************************************************/ + +/*! + \class ExtDateTime extdatetime.h + \brief The ExtDateTime class provides date and time functions. + + \ingroup time + + A ExtDateTime object contains a calendar date and a clock time (a + "datetime"). It is a combination of the ExtDate and QTime classes. + It can read the current datetime from the system clock. It + provides functions for comparing datetimes and for manipulating a + datetime by adding a number of seconds, days, months or years. + + A ExtDateTime object is typically created either by giving a date + and time explicitly in the constructor, or by using the static + function currentDateTime(), which returns a ExtDateTime object set + to the system clock's time. The date and time can be changed with + setDate() and setTime(). A datetime can also be set using the + setTime_t() function, which takes a POSIX-standard "number of + seconds since 00:00:00 on January 1, 1970" value. The fromString() + function returns a ExtDateTime given a string and a date format + which is used to interpret the date within the string. + + The date() and time() functions provide access to the date and + time parts of the datetime. The same information is provided in + textual format by the toString() function. + + ExtDateTime provides a full set of operators to compare two + ExtDateTime objects where smaller means earlier and larger means + later. + + You can increment (or decrement) a datetime by a given number of + seconds using addSecs() or days using addDays(). Similarly you can + use addMonths() and addYears(). The daysTo() function returns the + number of days between two datetimes, and secsTo() returns the + number of seconds between two datetimes. + + The range of a datetime object is constrained to the ranges of the + ExtDate and QTime objects which it embodies. + + Methods in this class are reentrant. + + \sa ExtDate QTime ExtDateTimeEdit +*/ + + +/*! + \fn ExtDateTime::ExtDateTime() + + Constructs a null datetime (i.e. null date and null time). A null + datetime is invalid, since the date is invalid. + + \sa isValid() +*/ + + +/*! + Constructs a datetime with date \a date and null (but valid) time + (00:00:00.000). +*/ + +ExtDateTime::ExtDateTime( const ExtDate &date ) + : d(date) +{ +} + +/*! + Constructs a datetime with date \a date and time \a time. +*/ + +ExtDateTime::ExtDateTime( const ExtDate &date, const QTime &time ) + : d(date), t(time) +{ +} + + +/*! + \fn bool ExtDateTime::isNull() const + + Returns TRUE if both the date and the time are null; otherwise + returns FALSE. A null datetime is invalid. + + \sa ExtDate::isNull(), QTime::isNull() +*/ + +/*! + \fn bool ExtDateTime::isValid() const + + Returns TRUE if both the date and the time are valid; otherwise + returns FALSE. + + \sa ExtDate::isValid(), QTime::isValid() +*/ + +/*! + \fn ExtDate ExtDateTime::date() const + + Returns the date part of the datetime. + + \sa setDate(), time() +*/ + +/*! + \fn QTime ExtDateTime::time() const + + Returns the time part of the datetime. + + \sa setTime(), date() +*/ + +/*! + \fn void ExtDateTime::setDate( const ExtDate &date ) + + Sets the date part of this datetime to \a date. + + \sa date(), setTime() +*/ + +/*! + \fn void ExtDateTime::setTime( const QTime &time ) + + Sets the time part of this datetime to \a time. + + \sa time(), setDate() +*/ + + +/*! + Returns the datetime as the number of seconds that have passed + since 1970-01-01T00:00:00, Coordinated Universal Time (UTC). + + On systems that do not support timezones, this function will + behave as if local time were UTC. + + \sa setTime_t() +*/ + +uint ExtDateTime::toTime_t() const +{ + tm brokenDown; + brokenDown.tm_sec = t.second(); + brokenDown.tm_min = t.minute(); + brokenDown.tm_hour = t.hour(); + brokenDown.tm_mday = d.day(); + brokenDown.tm_mon = d.month() - 1; + brokenDown.tm_year = d.year() - 1900; + brokenDown.tm_isdst = -1; + int secsSince1Jan1970UTC = (int) mktime( &brokenDown ); + if ( secsSince1Jan1970UTC < -1 ) + secsSince1Jan1970UTC = -1; + return (uint) secsSince1Jan1970UTC; +} + +/*! + \overload + + Convenience function that sets the date and time to local time + based on the given UTC time. +*/ + +void ExtDateTime::setTime_t( uint secsSince1Jan1970UTC ) +{ + setTime_t( secsSince1Jan1970UTC, Qt::LocalTime ); +} + +/*! + Sets the date and time to \a ts time (\c Qt::LocalTime or \c + Qt::UTC) given the number of seconds that have passed since + 1970-01-01T00:00:00, Coordinated Universal Time (UTC). On systems + that do not support timezones this function will behave as if + local time were UTC. + + On Windows, only a subset of \a secsSince1Jan1970UTC values are + supported, as Windows starts counting from 1980. + + \sa toTime_t() +*/ +void ExtDateTime::setTime_t( uint secsSince1Jan1970UTC, Qt::TimeSpec ts ) +{ + time_t tmp = (time_t) secsSince1Jan1970UTC; + tm *brokenDown = 0; + +#if defined(Q_OS_UNIX) && defined(QT_THREAD_SUPPORT) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) + // posix compliant system + // use the reentrant versions of localtime() and gmtime() where available + tm res; + if ( ts == Qt::LocalTime ) + brokenDown = localtime_r( &tmp, &res ); + if ( !brokenDown ) { + brokenDown = gmtime_r( &tmp, &res ); + if ( !brokenDown ) { + d.setJD( ExtDate::GregorianToJD( 1970, 1, 1 ) ); + t.setHMS(0,0,0); + // t.ds = 0; + return; + } + } +#else + if ( ts == Qt::LocalTime ) + brokenDown = localtime( &tmp ); + if ( !brokenDown ) { + brokenDown = gmtime( &tmp ); + if ( !brokenDown ) { + d.setJD( ExtDate::GregorianToJD( 1970, 1, 1 ) ); +// t.ds = 0; + t.setHMS(0,0,0); + return; + } + } +#endif + + d.setJD( ExtDate::GregorianToJD( brokenDown->tm_year + 1900, + brokenDown->tm_mon + 1, + brokenDown->tm_mday ) ); + t.setHMS( brokenDown->tm_hour, brokenDown->tm_min, brokenDown->tm_sec ); +// t.ds = MSECS_PER_HOUR * brokenDown->tm_hour + +// MSECS_PER_MIN * brokenDown->tm_min + +// 1000 * brokenDown->tm_sec; +} +#ifndef QT_NO_DATESTRING +#ifndef QT_NO_SPRINTF +/*! + \overload + + Returns the datetime as a string. The \a f parameter determines + the format of the string. + + If \a f is \c Qt::TextDate, the string format is "Wed May 20 + 03:40:13 1998" (using ExtDate::shortDayName(), ExtDate::shortMonthName(), + and QTime::toString() to generate the string, so the day and month + names will have localized names). + + If \a f is \c Qt::ISODate, the string format corresponds to the + ISO 8601 extended specification for representations of dates and + times, which is YYYY-MM-DDTHH:MM:SS. + + If \a f is \c Qt::LocalDate, the string format depends on the + locale settings of the system. + + If the format \a f is invalid or the datetime is invalid, toString() + returns a null string. + + \sa ExtDate::toString() QTime::toString() +*/ + +QString ExtDateTime::toString( Qt::DateFormat f ) const +{ + if ( !isValid() ) + return QString::null; + + if ( f == Qt::ISODate ) { + return d.toString( Qt::ISODate ) + "T" + t.toString( Qt::ISODate ); + } +#ifndef QT_NO_TEXTDATE + else if ( f == Qt::TextDate ) { + return toString( "%a %b %e %Y %H:%M:%S" ); + } +#endif + else if ( f == Qt::LocalDate ) { + return toString( KGlobal::locale()->dateFormat() + + " " + KGlobal::locale()->timeFormat() ); + } + + return QString::null; +} +#endif + +QString ExtDateTime::toString( const QString& format ) const +{ + if ( !isValid() ) + return QString::null; + + //Parse the date portion of the format string + QString result = date().toString( format ); + + //For the time format, use the following KDE format specs: + //Replace occurences of the following tokens with their + //corresponding values: + // + // %H Hour in 24h format, 2 digits + // %k Hour in 24h format, 1-2 digits + // %I Hour in 12h format, 2 digits + // %l Hour in 12h format, 1-2 digits + // %M Minute, 2 digits + // %S Seconds, 2 digits + // %p pm/am + + int h = time().hour(); + + result.replace( "%H", QString().sprintf( "%02d", h ) ); + result.replace( "%k", QString().sprintf( "%d", h ) ); + result.replace( "%I", QString().sprintf( "%02d", ( h > 12 ) ? h-12 : h ) ); + result.replace( "%l", QString().sprintf( "%d", ( h > 12 ) ? h-12 : h ) ); + result.replace( "%M", QString().sprintf( "%02d", time().minute() ) ); + result.replace( "%S", QString().sprintf( "%02d", time().second() ) ); + result.replace( "%p", QString().sprintf( "%s", ( h > 12 ) ? "pm" : "am" ) ); + + return result; +} +#endif //QT_NO_DATESTRING + +/*! + Returns a ExtDateTime object containing a datetime \a ndays days + later than the datetime of this object (or earlier if \a ndays is + negative). + + \sa daysTo(), addMonths(), addYears(), addSecs() +*/ + +ExtDateTime ExtDateTime::addDays( int ndays ) const +{ + return ExtDateTime( d.addDays(ndays), t ); +} + +/*! + Returns a ExtDateTime object containing a datetime \a nmonths months + later than the datetime of this object (or earlier if \a nmonths + is negative). + + \sa daysTo(), addDays(), addYears(), addSecs() +*/ + +ExtDateTime ExtDateTime::addMonths( int nmonths ) const +{ + return ExtDateTime( d.addMonths(nmonths), t ); +} + +/*! + Returns a ExtDateTime object containing a datetime \a nyears years + later than the datetime of this object (or earlier if \a nyears is + negative). + + \sa daysTo(), addDays(), addMonths(), addSecs() +*/ + +ExtDateTime ExtDateTime::addYears( int nyears ) const +{ + return ExtDateTime( d.addYears(nyears), t ); +} + +/*! + Returns a ExtDateTime object containing a datetime \a nsecs seconds + later than the datetime of this object (or earlier if \a nsecs is + negative). + + \sa secsTo(), addDays(), addMonths(), addYears() +*/ + +ExtDateTime ExtDateTime::addSecs( int nsecs ) const +{ + long int dd = d.jd(); + int tt = MSECS_PER_HOUR*t.hour() + MSECS_PER_MIN*t.minute() + 1000*t.second() + t.msec(); + tt += nsecs*1000; + + while ( tt < 0 ) { + tt += MSECS_PER_DAY; + --dd; + } + + while ( tt > int(MSECS_PER_DAY) ) { + tt -= MSECS_PER_DAY; + ++dd; + } + + ExtDateTime ret; + ret.setTime( QTime().addMSecs( tt ) ); + ret.setDate( ExtDate( dd ) ); + + return ret; +} + +/*! + Returns the number of days from this datetime to \a dt (which is + negative if \a dt is earlier than this datetime). + + \sa addDays(), secsTo() +*/ + +int ExtDateTime::daysTo( const ExtDateTime &dt ) const +{ + return d.daysTo( dt.d ); +} + +/*! + Returns the number of seconds from this datetime to \a dt (which + is negative if \a dt is earlier than this datetime). + + Example: + \code + ExtDateTime dt = ExtDateTime::currentDateTime(); + ExtDateTime xmas( ExtDate(dt.date().year(),12,24), QTime(17,00) ); + kdDebug( ) << "There are " << dt.secsTo(xmas) << " seconds to Christmas" << endl; + \endcode + + \sa addSecs(), daysTo(), QTime::secsTo() +*/ + +int ExtDateTime::secsTo( const ExtDateTime &dt ) const +{ + return t.secsTo(dt.t) + d.daysTo(dt.d)*SECS_PER_DAY; +} + + +/*! + Returns TRUE if this datetime is equal to \a dt; otherwise returns FALSE. + + \sa operator!=() +*/ + +bool ExtDateTime::operator==( const ExtDateTime &dt ) const +{ + return t == dt.t && d == dt.d; +} + +/*! + Returns TRUE if this datetime is different from \a dt; otherwise + returns FALSE. + + \sa operator==() +*/ + +bool ExtDateTime::operator!=( const ExtDateTime &dt ) const +{ + return t != dt.t || d != dt.d; +} + +/*! + Returns TRUE if this datetime is earlier than \a dt; otherwise + returns FALSE. +*/ + +bool ExtDateTime::operator<( const ExtDateTime &dt ) const +{ + if ( d < dt.d ) + return true; + return d == dt.d ? t < dt.t : false; +} + +/*! + Returns TRUE if this datetime is earlier than or equal to \a dt; + otherwise returns FALSE. +*/ + +bool ExtDateTime::operator<=( const ExtDateTime &dt ) const +{ + if ( d < dt.d ) + return true; + return d == dt.d ? t <= dt.t : false; +} + +/*! + Returns TRUE if this datetime is later than \a dt; otherwise + returns FALSE. +*/ + +bool ExtDateTime::operator>( const ExtDateTime &dt ) const +{ + if ( d > dt.d ) + return true; + return d == dt.d ? t > dt.t : false; +} + +/*! + Returns TRUE if this datetime is later than or equal to \a dt; + otherwise returns FALSE. +*/ + +bool ExtDateTime::operator>=( const ExtDateTime &dt ) const +{ + if ( d > dt.d ) + return true; + return d == dt.d ? t >= dt.t : false; +} + +/*! + \overload + + Returns the current datetime, as reported by the system clock. + + \sa ExtDate::currentDate(), QTime::currentTime() +*/ + +ExtDateTime ExtDateTime::currentDateTime() +{ + return currentDateTime( Qt::LocalTime ); +} + +/*! + Returns the current datetime, as reported by the system clock, for the + TimeSpec \a ts. The default TimeSpec is LocalTime. + + \sa ExtDate::currentDate(), QTime::currentTime(), Qt::TimeSpec +*/ + +ExtDateTime ExtDateTime::currentDateTime( Qt::TimeSpec ts ) +{ + ExtDateTime dt; + dt.setDate( ExtDate::currentDate(ts) ); + QTime t = t.currentTime(ts); + if ( t.hour()==0 && t.minute()==0 ) // midnight or right after? + dt.setDate( ExtDate::currentDate(ts) ); // fetch date again + dt.setTime( t ); + return dt; +} + +#ifndef QT_NO_DATESTRING +/*! + Returns the ExtDateTime represented by the string \a s, using the + format \a f, or an invalid datetime if this is not possible. + + Note for \c Qt::TextDate: It is recommended that you use the + English short month names (e.g. "Jan"). Although localized month + names can also be used, they depend on the user's locale settings. + + \warning Note that \c Qt::LocalDate cannot be used here. +*/ +ExtDateTime ExtDateTime::fromString( const QString& s ) +{ + ExtDateTime dtResult = ExtDateTime::fromString( s, Qt::TextDate ); + if ( dtResult.isValid() ) return dtResult; + + dtResult = ExtDateTime::fromString( s, Qt::ISODate ); + + if ( dtResult.isValid() ) return dtResult; + else return ExtDateTime(); //invalid +} + +ExtDateTime ExtDateTime::fromString( const QString& s, Qt::DateFormat f ) +{ + ExtDateTime dt; + + if ( ( s.isEmpty() ) || ( f == Qt::LocalDate ) ) { +#if defined(QT_CHECK_RANGE) + qWarning( "ExtDateTime::fromString: Parameter out of range" ); +#endif + dt.d.setJD( INVALID_DAY ); + return dt; + } + + if ( f == Qt::ISODate ) { + if ( s.length() <= 10 || ! s.contains( ':' ) ) { //no time specified + QTime t = QTime(0,0,0); + return ExtDateTime( ExtDate::fromString( s.mid(0,10), Qt::ISODate ) ); + } else { + return ExtDateTime( ExtDate::fromString( s.mid(0,10), Qt::ISODate ), + QTime::fromString( s.mid(11), Qt::ISODate ) ); + } + } +#if !defined(QT_NO_REGEXP) && !defined(QT_NO_TEXTDATE) + else if ( f == Qt::TextDate ) { + + //parse the time, if it exists. + QTime time; + QString sd = s; + int hour, minute, second; + int pivot = s.find( QRegExp(QString::fromLatin1("[0-9][0-9]:[0-9][0-9]:[0-9][0-9]")) ); + if ( pivot != -1 ) { + hour = s.mid( pivot, 2 ).toInt(); + minute = s.mid( pivot+3, 2 ).toInt(); + second = s.mid( pivot+6, 2 ).toInt(); + time.setHMS( hour, minute, second ); + + sd = s.left( pivot - 1 ); + } + + //sd is now just the date string. + ExtDate date = ExtDate::fromString( s, Qt::TextDate ); + return ExtDateTime( date, time ); + } + +#endif //QT_NO_REGEXP + return ExtDateTime(); +} +#endif //QT_NO_DATESTRING + + +#ifndef QT_NO_DATASTREAM +KDE_EXPORT QDataStream &operator<<( QDataStream & ostream, const ExtDate & date) +{ + return ostream << (Q_UINT32)(date.jd()); +} + +KDE_EXPORT QDataStream &operator>>( QDataStream & ostream, ExtDate & date) +{ + Q_UINT32 julday; + ostream >> julday; + date.setJD( julday ); + return ostream; +} + +KDE_EXPORT QDataStream &operator<<( QDataStream & ostream, const ExtDateTime & dt) +{ + return ostream << dt.d << dt.t; +} + +KDE_EXPORT QDataStream &operator>>( QDataStream & ostream, ExtDateTime & dt) +{ + ostream >> dt.d >> dt.t; + return ostream; +} + +#endif // QT_NO_DATASTREAM diff --git a/libkdeedu/extdate/extdatetime.h b/libkdeedu/extdate/extdatetime.h new file mode 100644 index 00000000..e1bc2a79 --- /dev/null +++ b/libkdeedu/extdate/extdatetime.h @@ -0,0 +1,190 @@ +/************************************************************************* +** Definition of extended range date class +** (c) 2004 by Michel Guitel <michel.guitel@sap.ap-hop-paris.fr> +** modifications by Jason Harris <kstars@30doradus.org> +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +**********************************************************************/ + +#ifndef EXTDATETIME_H +#define EXTDATETIME_H + +#include <limits.h> +#include "qstring.h" +#include "qnamespace.h" +#include "qdatetime.h" +#include <kdemacros.h> + +#define INVALID_DAY LONG_MIN + +class ExtDateTime; + +/***************************************************************************** + ExtDate class + *****************************************************************************/ + +extern void test2_unit(int y, int m, int d); + +class ExtDateTime; + +class KDE_EXPORT ExtDate +{ +public: + ExtDate() : m_jd(INVALID_DAY), m_year(0), m_month(0), m_day(0) {} + ExtDate( int y, int m, int d ); + ExtDate( const QDate &q ) { ExtDate( q.year(), q.month(), q.day() ); } + ExtDate( long int jd ); + + bool isNull() const { return m_jd == INVALID_DAY; } + bool isValid() const; + + QDate qdate() const; + + int year() const { return m_year; } + int month() const { return m_month; } + int day() const { return m_day; } + int dayOfWeek() const; + int dayOfYear() const; + int daysInMonth() const; + int daysInYear() const; + int weekNumber( int *yearNum = 0 ) const; + long int jd() const { return m_jd; } + +#ifndef QT_NO_TEXTDATE +#ifndef QT_NO_COMPAT + static QString monthName( int month ) { return shortMonthName( month ); } + static QString dayName( int weekday ) { return shortDayName( weekday ); } +#endif + static QString shortMonthName( int month ); + static QString shortDayName( int weekday ); + static QString longMonthName( int month ); + static QString longDayName( int weekday ); +#endif //QT_NO_TEXTDATE +#ifndef QT_NO_TEXTSTRING +#if !defined(QT_NO_SPRINTF) + QString toString( Qt::DateFormat f = Qt::TextDate ) const; +#endif + QString toString( const QString& format ) const; +#endif + bool setYMD( int y, int m, int d ); + bool setJD( long int _jd ); + + ExtDate addDays( int days ) const; + ExtDate addMonths( int months ) const; + ExtDate addYears( int years ) const; + int daysTo( const ExtDate & ) const; + + bool operator==( const ExtDate &d ) const { return m_jd == d.jd(); } + bool operator!=( const ExtDate &d ) const { return m_jd != d.jd(); } + bool operator<( const ExtDate &d ) const { return m_jd < d.jd(); } + bool operator<=( const ExtDate &d ) const { return m_jd <= d.jd(); } + bool operator>( const ExtDate &d ) const { return m_jd > d.jd(); } + bool operator>=( const ExtDate &d ) const { return m_jd >= d.jd(); } + + static ExtDate currentDate( Qt::TimeSpec ts = Qt::LocalTime ); +#ifndef QT_NO_DATESTRING + static ExtDate fromString( const QString &s ); + static ExtDate fromString( const QString &s, Qt::DateFormat f ); +#endif + static bool isValid( int y, int m, int d ); + static bool leapYear( int year ); + + static long int GregorianToJD( int y, int m, int d ); + static void JDToGregorian( long int jd, int &y, int &m, int &d ); + +private: + static int dayOfYear(int y, int m, int d); + + long int m_jd; + int m_year, m_month, m_day; + static uint m_monthLength[12]; + static uint m_monthOrigin[12]; + static QString m_shortMonthNames[12]; + static QString m_shortDayNames[7]; + static QString m_longMonthNames[12]; + static QString m_longDayNames[7]; + + friend class ExtDateTime; + +#ifndef QT_NO_DATASTREAM + friend Q_EXPORT QDataStream &operator<<( QDataStream &, const ExtDate & ); + friend Q_EXPORT QDataStream &operator>>( QDataStream &, ExtDate & ); + friend Q_EXPORT QDataStream &operator<<( QDataStream &, const ExtDateTime & ); + friend Q_EXPORT QDataStream &operator>>( QDataStream &, ExtDateTime & ); +#endif +}; + +/***************************************************************************** + ExtDateTime class + *****************************************************************************/ + +class KDE_EXPORT ExtDateTime +{ +public: + ExtDateTime() {} // set null date and null time + ExtDateTime( const ExtDate & ); + ExtDateTime( const ExtDate &, const QTime & ); + + bool isNull() const { return d.isNull() && t.isNull(); } + bool isValid() const { return d.isValid() && t.isValid(); } + + ExtDate date() const { return d; } + QTime time() const { return t; } + uint toTime_t() const; + void setDate( const ExtDate &date ) { d = date; } + void setTime( const QTime &time ) { t = time; } + void setTime_t( uint secsSince1Jan1970UTC ); + void setTime_t( uint secsSince1Jan1970UTC, Qt::TimeSpec ); +#ifndef QT_NO_DATESTRING +#ifndef QT_NO_SPRINTF + QString toString( Qt::DateFormat f = Qt::TextDate ) const; +#endif + QString toString( const QString& format ) const; +#endif + ExtDateTime addDays( int days ) const; + ExtDateTime addMonths( int months ) const; + ExtDateTime addYears( int years ) const; + ExtDateTime addSecs( int secs ) const; + int daysTo( const ExtDateTime & ) const; + int secsTo( const ExtDateTime & ) const; + + bool operator==( const ExtDateTime &dt ) const; + bool operator!=( const ExtDateTime &dt ) const; + bool operator<( const ExtDateTime &dt ) const; + bool operator<=( const ExtDateTime &dt ) const; + bool operator>( const ExtDateTime &dt ) const; + bool operator>=( const ExtDateTime &dt ) const; + + static ExtDateTime currentDateTime(); + static ExtDateTime currentDateTime( Qt::TimeSpec ); +#ifndef QT_NO_DATESTRING + static ExtDateTime fromString( const QString &s ); + static ExtDateTime fromString( const QString &s, Qt::DateFormat f ); +#endif +private: + ExtDate d; + QTime t; +#ifndef QT_NO_DATASTREAM + friend Q_EXPORT QDataStream &operator<<( QDataStream &, const ExtDateTime &); + friend Q_EXPORT QDataStream &operator>>( QDataStream &, ExtDateTime & ); +#endif +}; + +/***************************************************************************** + Date and time stream functions + *****************************************************************************/ + +#ifndef QT_NO_DATASTREAM +Q_EXPORT QDataStream &operator<<( QDataStream &, const ExtDate & ); +Q_EXPORT QDataStream &operator>>( QDataStream &, ExtDate & ); +#endif // QT_NO_DATASTREAM + +#endif // EXTDATE_H + diff --git a/libkdeedu/extdate/extdatetimeedit.cpp b/libkdeedu/extdate/extdatetimeedit.cpp new file mode 100644 index 00000000..2a9a467b --- /dev/null +++ b/libkdeedu/extdate/extdatetimeedit.cpp @@ -0,0 +1,2751 @@ +/**************************************************************************** +** +** +** Implementation of date and time edit classes +** +** Created : 001103 +** +** Original qatetimeedit.cpp Copyright (C) 2000-2002 Trolltech AS. All rights reserved. +**>> ExtDate modifications (C) 2004 Jason Harris <jharris@30doradus.org> +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +**********************************************************************/ + +//DEBUG +#include <kdebug.h> + +#include "extdatetimeedit.h" + +#ifndef QT_NO_DATETIMEEDIT + +//#include "../kernel/qinternal_p.h" +//#include "../kernel/qrichtext_p.h" +#include <private/qinternal_p.h> +#include <private/qrichtext_p.h> +#include <qrangecontrol.h> +#include <qapplication.h> +#include <qpixmap.h> +#include <qapplication.h> +#include <qvaluelist.h> +#include <qstring.h> +#include <qstyle.h> +#include <qdatetimeedit.h> //need for QTimeEdit + +#define EXTDATETIMEEDIT_HIDDEN_CHAR '0' + +static QString *lDateSep = 0; +static QString *lTimeSep = 0; +static bool lAMPM = FALSE; +static QString *lAM = 0; +static QString *lPM = 0; +static ExtDateEdit::Order lOrder = ExtDateEdit::YMD; +static int refcount = 0; + +static void cleanup() +{ + delete lDateSep; + lDateSep = 0; + delete lTimeSep; + lTimeSep = 0; + delete lAM; + lAM = 0; + delete lPM; + lPM = 0; +} + +/*! +\internal +try to get the order of DMY and the date/time separator from the locale settings +*/ +static void readLocaleSettings() +{ + int dpos, mpos, ypos; + cleanup(); + + lDateSep = new QString(); + lTimeSep = new QString(); + +#if defined(Q_WS_WIN) + QT_WA( { + TCHAR data[10]; + GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_SDATE, data, 10 ); + *lDateSep = QString::fromUcs2( (ushort*)data ); + GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_STIME, data, 10 ); + *lTimeSep = QString::fromUcs2( (ushort*)data ); + GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_ITIME, data, 10 ); + lAMPM = QString::fromUcs2( (ushort*)data ).toInt()==0; + GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_S1159, data, 10 ); + QString am = QString::fromUcs2( (ushort*)data ); + if ( !am.isEmpty() ) + lAM = new QString( am ); + GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_S2359, data, 10 ); + QString pm = QString::fromUcs2( (ushort*)data ); + if ( !pm.isEmpty() ) + lPM = new QString( pm ); + } , { + char data[10]; + GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_SDATE, (char*)&data, 10 ); + *lDateSep = QString::fromLocal8Bit( data ); + GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_STIME, (char*)&data, 10 ); + *lTimeSep = QString::fromLocal8Bit( data ); + GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_ITIME, (char*)&data, 10 ); + lAMPM = QString::fromLocal8Bit( data ).toInt()==0; + GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_S1159, (char*)&data, 10 ); + QString am = QString::fromLocal8Bit( data ); + if ( !am.isEmpty() ) + lAM = new QString( am ); + GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_S2359, (char*)&data, 10 ); + QString pm = QString::fromLocal8Bit( data ); + if ( !pm.isEmpty() ) + lPM = new QString( pm ); + } ); +#else + *lDateSep = "-"; + *lTimeSep = ":"; +#endif + QString d = ExtDate( 1999, 11, 22 ).toString( Qt::LocalDate ); + dpos = d.find( "22" ); + mpos = d.find( "11" ); + ypos = d.find( "99" ); + if ( dpos > -1 && mpos > -1 && ypos > -1 ) { + // test for DMY, MDY, YMD, YDM + if ( dpos < mpos && mpos < ypos ) { + lOrder = ExtDateEdit::DMY; + } else if ( mpos < dpos && dpos < ypos ) { + lOrder = ExtDateEdit::MDY; + } else if ( ypos < mpos && mpos < dpos ) { + lOrder = ExtDateEdit::YMD; + } else if ( ypos < dpos && dpos < mpos ) { + lOrder = ExtDateEdit::YDM; + } else { + // cannot determine the dateformat - use the default + return; + } + + // this code needs to change if new formats are added + +#ifndef Q_WS_WIN + QString sep = d.mid( QMIN( dpos, mpos ) + 2, QABS( dpos - mpos ) - 2 ); + if ( d.contains( sep ) == 2 ) { + *lDateSep = sep; + } +#endif + } + +#ifndef Q_WS_WIN + QString t = QTime( 11, 22, 33 ).toString( Qt::LocalDate ); + dpos = t.find( "11" ); + mpos = t.find( "22" ); + ypos = t.find( "33" ); + // We only allow hhmmss + if ( dpos > -1 && dpos < mpos && mpos < ypos ) { + QString sep = t.mid( dpos + 2, mpos - dpos - 2 ); + if ( sep == t.mid( mpos + 2, ypos - mpos - 2 ) ) { + *lTimeSep = sep; + } + } +#endif +} + +static ExtDateEdit::Order localOrder() { + if ( !lDateSep ) { + readLocaleSettings(); + } + return lOrder; +} + +static QString localDateSep() { + if ( !lDateSep ) { + readLocaleSettings(); + } + return *lDateSep; +} + +static QString localTimeSep() { + if ( !lTimeSep ) { + readLocaleSettings(); + } + return *lTimeSep; +} + +class ExtDateTimeEditorPrivate +{ +public: + ExtDateTimeEditorPrivate() + : frm( TRUE ), + parag( new QTextParagraph( 0, 0, 0, FALSE ) ), + focusSec(0) + { + parag->formatter()->setWrapEnabled( FALSE ); + cursor = new QTextCursor( 0 ); + cursor->setParagraph( parag ); + offset = 0; + sep = localDateSep(); + refcount++; + } + ~ExtDateTimeEditorPrivate() + { + delete parag; + delete cursor; + if ( !--refcount ) + cleanup(); + } + + void appendSection( const QNumberSection& sec ) + { + sections.append( sec ); + + } + void clearSections() + { + sections.clear(); + } + void setSectionSelection( int sec, int selstart, int selend ) + { + if ( sec < 0 || sec > (int)sections.count() ) + return; + sections[sec].setSelectionStart( selstart ); + sections[sec].setSelectionEnd( selend ); + } + uint sectionCount() const { return (uint)sections.count(); } + void setSeparator( const QString& s ) { sep = s; } + QString separator() const { return sep; } + + void setFrame( bool f ) { frm = f; } + bool frame() const { return frm; } + + int focusSection() const { return focusSec; } + int section( const QPoint& p ) + { + cursor->place( p + QPoint( offset, 0 ), parag ); + int idx = cursor->index(); + for ( uint i = 0; i < sections.count(); ++i ) { + if ( idx >= sections[i].selectionStart() && + idx <= sections[i].selectionEnd() ) + return i; + } + return -1; + } + QNumberSection section( int idx ) const + { + return sections[idx]; + } + bool setFocusSection( int idx ) + { + if ( idx > (int)sections.count()-1 || idx < 0 ) + return FALSE; + if ( idx != focusSec ) { + focusSec = idx; + applyFocusSelection(); + return TRUE; + } + return FALSE; + } + + bool inSectionSelection( int idx ) + { + for ( uint i = 0; i < sections.count(); ++i ) { + if ( idx >= sections[i].selectionStart() && + idx <= sections[i].selectionEnd() ) + return TRUE; + } + return FALSE; + } + + void paint( const QString& txt, bool focus, QPainter& p, + const QColorGroup& cg, const QRect& rect, QStyle& style ) + { + int fw = 0; + if ( frm ) + fw = style.pixelMetric(QStyle::PM_DefaultFrameWidth); + + parag->truncate( 0 ); + parag->append( txt ); + if ( !focus ) + parag->removeSelection( QTextDocument::Standard ); + else { + applyFocusSelection(); + } + + /* color all EXTDATETIMEEDIT_HIDDEN_CHAR chars to background color */ + QTextFormat *fb = parag->formatCollection()->format( p.font(), + cg.base() ); + QTextFormat *nf = parag->formatCollection()->format( p.font(), + cg.text() ); + for ( uint i = 0; i < txt.length(); ++i ) { + parag->setFormat( i, 1, nf ); + if ( inSectionSelection( i ) ) + continue; + if ( txt.at(i) == EXTDATETIMEEDIT_HIDDEN_CHAR ) + parag->setFormat( i, 1, fb ); + else + parag->setFormat( i, 1, nf ); + } + fb->removeRef(); + nf->removeRef(); + + QRect r( rect.x(), rect.y(), rect.width() - 2 * ( 2 + fw ), rect.height() ); + parag->pseudoDocument()->docRect = r; + parag->invalidate(0); + parag->format(); + + int xoff = 2 + fw - offset; + int yoff = ( rect.height() - parag->rect().height() + 1 ) / 2; + if ( yoff < 0 ) + yoff = 0; + + p.translate( xoff, yoff ); + parag->paint( p, cg, 0, TRUE ); + if ( frm ) + p.translate( -xoff, -yoff ); + } + + void resize( const QSize& size ) { sz = size; } + + int mapSection( int sec ) + { + return sections[sec].index(); + } + +protected: + void applyFocusSelection() + { + if ( focusSec > -1 ) { + int selstart = sections[ focusSec ].selectionStart(); + int selend = sections[ focusSec ].selectionEnd(); + parag->setSelection( QTextDocument::Standard, selstart, selend ); + parag->format(); + if ( parag->at( selstart )->x < offset || + parag->at( selend )->x + parag->string()->width( selend ) > offset + sz.width() ) { + offset = parag->at( selstart )->x; + } + } + } +private: + bool frm; + QTextParagraph *parag; + QTextCursor *cursor; + QSize sz; + int focusSec; + QValueList< QNumberSection > sections; + QString sep; + int offset; +}; + +class ExtDateTimeSpinWidget : public QSpinWidget +{ +public: + ExtDateTimeSpinWidget( QWidget *parent, const char *name ) + : QSpinWidget( parent, name ) + { + } + +protected: +#ifndef QT_NO_WHEELEVENT + void wheelEvent( QWheelEvent *e ) + { + ExtDateTimeEditor *editor = (ExtDateTimeEditor*)editWidget()->qt_cast( "ExtDateTimeEditor" ); + Q_ASSERT( editor ); + if ( !editor ) + return; + + int section = editor->sectionAt( e->pos() ); + editor->setFocusSection( section ); + + if ( section == -1 ) + return; + QSpinWidget::wheelEvent( e ); + } +#endif +}; + +/*! + Constructs an empty datetime editor with parent \a parent and + called \a name. +*/ +ExtDateTimeEditor::ExtDateTimeEditor( ExtDateTimeEditBase * parent, + const char * name ) + : QWidget( parent, name, WNoAutoErase ) +{ + d = new ExtDateTimeEditorPrivate(); + cw = parent; + init(); +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +ExtDateTimeEditor::~ExtDateTimeEditor() +{ + delete d; +} + +/*! \internal + +*/ + +void ExtDateTimeEditor::init() +{ + setBackgroundMode( PaletteBase ); + setFocusSection( -1 ); + installEventFilter( this ); + setFocusPolicy( WheelFocus ); +} + + +/*! \reimp + +*/ + +bool ExtDateTimeEditor::event( QEvent *e ) +{ + if ( e->type() == QEvent::FocusIn || e->type() == QEvent::FocusOut ) { + if ( e->type() == QEvent::FocusOut ) + qApp->sendEvent( cw, e ); + update( rect() ); + } else if ( e->type() == QEvent::AccelOverride ) { + QKeyEvent* ke = (QKeyEvent*) e; + switch ( ke->key() ) { + case Key_Delete: + case Key_Backspace: + case Key_Up: + case Key_Down: + case Key_Left: + case Key_Right: + ke->accept(); + default: + break; + } + } + return QWidget::event( e ); +} + +/*! \reimp + +*/ + +void ExtDateTimeEditor::resizeEvent( QResizeEvent *e ) +{ + d->resize( e->size() ); + QWidget::resizeEvent( e ); +} + + +/*! \reimp + +*/ + +void ExtDateTimeEditor::paintEvent( QPaintEvent * ) +{ + QString txt; + for ( uint i = 0; i < d->sectionCount(); ++i ) { + txt += cw->sectionFormattedText( i ); + if ( i < d->sectionCount()-1 ) { + if ( d->section( i+1 ).separator() ) + txt += d->separator(); + else + txt += " "; + } + } + + QSharedDoubleBuffer buffer( this ); + const QBrush &bg = + colorGroup().brush( isEnabled() ? QColorGroup::Base : QColorGroup::Background ); + buffer.painter()->fillRect( 0, 0, width(), height(), bg ); + d->paint( txt, hasFocus(), *buffer.painter(), colorGroup(), rect(), + style() ); + buffer.end(); +} + + +/*! + Returns the section index at point \a p. +*/ +int ExtDateTimeEditor::sectionAt( const QPoint &p ) +{ + return d->section( p ); +} + +int ExtDateTimeEditor::mapSection( int sec ) +{ + return d->mapSection( sec ); +} + + +/*! \reimp + +*/ + +void ExtDateTimeEditor::mousePressEvent( QMouseEvent *e ) +{ + QPoint p( e->pos().x(), 0 ); + int sec = sectionAt( p ); + if ( sec != -1 ) { + cw->setFocusSection( sec ); + repaint( rect(), FALSE ); + } +} + +/*! \reimp + +*/ +bool ExtDateTimeEditor::eventFilter( QObject *o, QEvent *e ) +{ + if ( o == this ) { + if ( e->type() == QEvent::KeyPress ) { + QKeyEvent *ke = (QKeyEvent*)e; + switch ( ke->key() ) { + case Key_Right: + if ( d->focusSection() < (int)d->sectionCount()-1 ) { + if ( cw->setFocusSection( focusSection()+1 ) ) + repaint( rect(), FALSE ); + } + return TRUE; + case Key_Left: + if ( d->focusSection() > 0 ) { + if ( cw->setFocusSection( focusSection()-1 ) ) + repaint( rect(), FALSE ); + } + return TRUE; + case Key_Up: + cw->stepUp(); + return TRUE; + case Key_Down: + cw->stepDown(); + return TRUE; + case Key_Backspace: + if ( ::qt_cast<ExtDateEdit*>(cw) ) + ((ExtDateEdit*)cw)->removeFirstNumber( d->focusSection() ); + else if ( ::qt_cast<QTimeEdit*>(cw) ) + ((QTimeEdit*)cw)->removeFirstNumber( d->focusSection() ); + return TRUE; + case Key_Delete: + cw->removeLastNumber( d->focusSection() ); + return TRUE; + case Key_Tab: + case Key_BackTab: { + if ( ke->state() == Qt::ControlButton ) + return FALSE; + + QWidget *w = this; + bool hadDateEdit = FALSE; + while ( w ) { + if ( ::qt_cast<ExtDateTimeSpinWidget*>(w) && qstrcmp( w->name(), "qt_spin_widget" ) != 0 || + ::qt_cast<ExtDateTimeEdit*>(w) ) + break; + hadDateEdit = hadDateEdit || ::qt_cast<ExtDateEdit*>(w); + w = w->parentWidget(); + } + + if ( w ) { + if ( !::qt_cast<ExtDateTimeEdit*>(w) ) { + w = w->parentWidget(); + } else { + ExtDateTimeEdit *ed = (ExtDateTimeEdit*)w; + if ( hadDateEdit && ke->key() == Key_Tab ) { + ed->timeEdit()->setFocus(); + return TRUE; + } else if ( !hadDateEdit && ke->key() == Key_BackTab ) { + ed->dateEdit()->setFocus(); + return TRUE; + } else { + while ( w && !::qt_cast<ExtDateTimeEdit*>(w) ) + w = w->parentWidget(); + } + } + + qApp->sendEvent( w, e ); + return TRUE; + } + } break; + default: + QString txt = ke->text().lower(); + if ( !txt.isEmpty() && !separator().isEmpty() && txt[0] == separator()[0] ) { + // do the same thing as KEY_RIGHT when the user presses the separator key + if ( d->focusSection() < 2 ) { + if ( cw->setFocusSection( focusSection()+1 ) ) + repaint( rect(), FALSE ); + } + return TRUE; + } else if ( !txt.isEmpty() && ::qt_cast<QTimeEdit*>(cw) && focusSection() == (int) d->sectionCount()-1 ) { + // the first character of the AM/PM indicator toggles if the section has focus + QTimeEdit *te = (QTimeEdit*)cw; + QTime time = te->time(); + if ( lAMPM && lAM && lPM && (te->display()&QTimeEdit::AMPM) ) { + if ( txt[0] == (*lAM).lower()[0] && time.hour() >= 12 ) { + time.setHMS( time.hour()-12, time.minute(), time.second(), time.msec() ); + te->setTime( time ); + } else if ( txt[0] == (*lPM).lower()[0] && time.hour() < 12 ) { + time.setHMS( time.hour()+12, time.minute(), time.second(), time.msec() ); + te->setTime( time ); + } + } + } + + int num = txt[0].digitValue(); + if ( num != -1 ) { + cw->addNumber( d->focusSection(), num ); + return TRUE; + } + } + } + } + return FALSE; +} + + +/*! + Appends the number section \a sec to the editor. +*/ + +void ExtDateTimeEditor::appendSection( const QNumberSection& sec ) +{ + d->appendSection( sec ); +} + +/*! + Removes all sections from the editor. +*/ + +void ExtDateTimeEditor::clearSections() +{ + d->clearSections(); +} + +/*! + Sets the selection of \a sec to start at \a selstart and end at \a + selend. +*/ + +void ExtDateTimeEditor::setSectionSelection( int sec, int selstart, int selend ) +{ + d->setSectionSelection( sec, selstart, selend ); +} + +/*! + Sets the separator for all numbered sections to \a s. Note that + currently, only the first character of \a s is used. +*/ + +void ExtDateTimeEditor::setSeparator( const QString& s ) +{ + d->setSeparator( s ); + update(); +} + + +/*! + Returns the editor's separator. +*/ + +QString ExtDateTimeEditor::separator() const +{ + return d->separator(); +} + +/*! + Returns the number of the section that has focus. +*/ + +int ExtDateTimeEditor::focusSection() const +{ + return d->focusSection(); +} + + +/*! + Sets the focus to section \a sec. If \a sec does not exist, + nothing happens. +*/ + +bool ExtDateTimeEditor::setFocusSection( int sec ) +{ + return d->setFocusSection( sec ); +} + +/*! \class ExtDateTimeEditBase ExtDatetimeedit.h + \brief The ExtDateTimeEditBase class provides an abstraction for date and edit editors. + \internal + + Small abstract class that provides some functions that are common + for both ExtDateEdit and QTimeEdit. Its used internally by + ExtDateTimeEditor. +*/ + +/*! \fn QString ExtDateTimeEditBase::sectionFormattedText( int sec ) + \internal + + Pure virtual function which returns the formatted text of section \a + sec. + +*/ + +/*! \fn void ExtDateTimeEditBase::stepUp() + \internal + + Pure virtual slot which is called whenever the user increases the + number in a section by pressing the widget's arrow buttons or the + keyboard's arrow keys. +*/ + +/*! \fn void ExtDateTimeEditBase::stepDown() + \internal + + Pure virtual slot which is called whenever the user decreases the + number in a section by pressing the widget's arrow buttons or the + keyboard's arrow keys. + +*/ + +/*! \fn void ExtDateTimeEditBase::addNumber( int sec, int num ) + \internal + + Pure virtual function which is called whenever the user types a number. + \a sec indicates the section where the number should be added. \a + num is the number that was pressed. +*/ + +/*! \fn void ExtDateTimeEditBase::removeLastNumber( int sec ) + \internal + + Pure virtual function which is called whenever the user tries to + remove the last number from \a sec by pressing the delete key. +*/ + +//////////////// + +class ExtDateEditPrivate +{ +public: + int y; + int m; + int d; + // remebers the last entry for the day. + // if the day is 31 and you cycle through the months, + // the day will be 31 again if you reach a month with 31 days + // otherwise it will be the highest day in the month + int dayCache; + int yearSection; + int monthSection; + int daySection; + ExtDateEdit::Order ord; + bool overwrite; + bool adv; + int timerId; + bool typing; + ExtDate min; + ExtDate max; + bool changed; + ExtDateTimeEditor *ed; + QSpinWidget *controls; +}; + + +/*! + \class ExtDateEdit ExtDatetimeedit.h + \brief The ExtDateEdit class provides a date editor. + + \ingroup advanced + \ingroup time + + ExtDateEdit allows the user to edit dates by using the keyboard or + the arrow keys to increase/decrease date values. The arrow keys + can be used to move from section to section within the ExtDateEdit + box. Dates appear in accordance with the local date/time settings + or in year, month, day order if the system doesn't provide this + information. It is recommended that the ExtDateEdit be initialised + with a date, e.g. + + \code + ExtDateEdit *dateEdit = new ExtDateEdit( ExtDate::currentDate(), this ); + dateEdit->setRange( ExtDate::currentDate().addDays( -365 ), + ExtDate::currentDate().addDays( 365 ) ); + dateEdit->setOrder( ExtDateEdit::MDY ); + dateEdit->setAutoAdvance( TRUE ); + \endcode + + Here we've created a new ExtDateEdit object initialised with today's + date and restricted the valid date range to today plus or minus + 365 days. We've set the order to month, day, year. If the auto + advance property is TRUE (as we've set it here) when the user + completes a section of the date, e.g. enters two digits for the + month, they are automatically taken to the next section. + + The maximum and minimum values for a date value in the date editor + default to the maximum and minimum values for a ExtDate. You can + change this by calling setMinValue(), setMaxValue() or setRange(). + + Terminology: A ExtDateEdit widget comprises three 'sections', one + each for the year, month and day. You can change the separator + character using ExtDateTimeEditor::setSeparator(), by default the + separator will be taken from the systems settings. If that is + not possible, it defaults to "-". + + \image html datetimewidgets.png "Date Time Widgets" + + \sa ExtDate QTimeEdit ExtDateTimeEdit +*/ + +/*! + \enum ExtDateEdit::Order + + This enum defines the order in which the sections that comprise a + date appear. + +*/ + + +/*! + \enum QTimeEdit::Display + + This enum defines the sections that comprise a time + + \value Hours The hours section + \value Minutes The minutes section + \value Seconds The seconds section + \value AMPM The AM/PM section + + The values can be or'ed together to show any combination. +*/ + +/*! + Constructs an empty date editor which is a child of \a parent and + called name \a name. +*/ + +ExtDateEdit::ExtDateEdit( QWidget * parent, const char * name ) + : ExtDateTimeEditBase( parent, name ) +{ + init(); + updateButtons(); +} + +/*! + \overload + + Constructs a date editor with the initial value \a date, parent \a + parent and called \a name. + + The date editor is initialized with \a date. +*/ + +ExtDateEdit::ExtDateEdit( const ExtDate& date, QWidget * parent, const char * name ) + : ExtDateTimeEditBase( parent, name ) +{ + init(); + setDate( date ); +} + +/*! \internal +*/ +void ExtDateEdit::init() +{ + d = new ExtDateEditPrivate(); + d->controls = new ExtDateTimeSpinWidget( this, + qstrcmp( name(), "qt_datetime_dateedit" ) == 0 ? + "qt_spin_widget" : "date edit controls" ); + d->ed = new ExtDateTimeEditor( this, "date editor" ); + d->controls->setEditWidget( d->ed ); + setFocusProxy( d->ed ); + connect( d->controls, SIGNAL( stepUpPressed() ), SLOT( stepUp() ) ); + connect( d->controls, SIGNAL( stepDownPressed() ), SLOT( stepDown() ) ); + connect( this, SIGNAL( valueChanged(const ExtDate&) ), + SLOT( updateButtons() ) ); + d->ed->appendSection( QNumberSection( 0,4 ) ); + d->ed->appendSection( QNumberSection( 5,7 ) ); + d->ed->appendSection( QNumberSection( 8,10 ) ); + + d->yearSection = -1; + d->monthSection = -1; + d->daySection = -1; + + d->y = 0; + d->m = 0; + d->d = 0; + d->dayCache = 0; + setOrder( localOrder() ); + setFocusSection( 0 ); + d->overwrite = TRUE; + d->adv = FALSE; + d->timerId = 0; + d->typing = FALSE; + d->min = ExtDate( -50000, 1, 1 ); + d->max = ExtDate( 50000, 12, 31 ); + d->changed = FALSE; + + setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ); + + refcount++; +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +ExtDateEdit::~ExtDateEdit() +{ + delete d; + if ( !--refcount ) + cleanup(); +} + +/*! + \property ExtDateEdit::minValue + + \brief the editor's minimum value + + Setting the minimum date value is equivalent to calling + ExtDateEdit::setRange( \e d, maxValue() ), where \e d is the minimum + date. The default minimum date is 1752-09-14. + + \sa maxValue setRange() +*/ + +ExtDate ExtDateEdit::minValue() const +{ + return d->min; +} + +/*! + \property ExtDateEdit::maxValue + + \brief the editor's maximum value + + Setting the maximum date value for the editor is equivalent to + calling ExtDateEdit::setRange( minValue(), \e d ), where \e d is the + maximum date. The default maximum date is 8000-12-31. + + \sa minValue setRange() +*/ + +ExtDate ExtDateEdit::maxValue() const +{ + return d->max; +} + + +/*! + Sets the valid input range for the editor to be from \a min to \a + max inclusive. If \a min is invalid no minimum date will be set. + Similarly, if \a max is invalid no maximum date will be set. +*/ + +void ExtDateEdit::setRange( const ExtDate& min, const ExtDate& max ) +{ + if ( min.isValid() ) + d->min = min; + if ( max.isValid() ) + d->max = max; +} + +/*! + Sets the separator to \a s. Note that currently only the first + character of \a s is used. +*/ + +void ExtDateEdit::setSeparator( const QString& s ) +{ + d->ed->setSeparator( s ); +} + +/*! + Returns the editor's separator. +*/ + +QString ExtDateEdit::separator() const +{ + return d->ed->separator(); +} + + +/*! + Enables/disables the push buttons according to the min/max date + for this widget. +*/ + +void ExtDateEdit::updateButtons() +{ + if ( !isEnabled() ) + return; + + fix(); + + bool upEnabled = date() < maxValue(); + bool downEnabled = date() > minValue(); + + d->controls->setUpEnabled( upEnabled ); + d->controls->setDownEnabled( downEnabled ); +} + +/*! \reimp + */ +void ExtDateEdit::resizeEvent( QResizeEvent * ) +{ + d->controls->resize( width(), height() ); +} + +/*! \reimp + +*/ +QSize ExtDateEdit::sizeHint() const +{ + constPolish(); + QFontMetrics fm( font() ); + int fw = style().pixelMetric( QStyle::PM_DefaultFrameWidth, this ); + int h = QMAX( fm.lineSpacing(), 14 ) + 2; + int w = 2 + fm.width( '9' ) * 8 + fm.width( d->ed->separator() ) * 2 + + d->controls->upRect().width() + fw * 4; + + return QSize( w, QMAX(h + fw * 2,20) ).expandedTo( QApplication::globalStrut() ); +} + +/*! \reimp + +*/ +QSize ExtDateEdit::minimumSizeHint() const +{ + return sizeHint(); +} + + +/*! + Returns the formatted number for section \a sec. This will + correspond to either the year, month or day section, depending on + the current display order. + + \sa setOrder() +*/ + +QString ExtDateEdit::sectionFormattedText( int sec ) +{ + QString txt; + txt = sectionText( sec ); + if ( d->typing && sec == d->ed->focusSection() ) + d->ed->setSectionSelection( sec, sectionOffsetEnd( sec ) - txt.length(), + sectionOffsetEnd( sec ) ); + else + d->ed->setSectionSelection( sec, sectionOffsetEnd( sec ) - sectionLength( sec ), + sectionOffsetEnd( sec ) ); + + txt = txt.rightJustify( sectionLength( sec ), EXTDATETIMEEDIT_HIDDEN_CHAR ); + return txt; +} + + +/*! + Returns the desired length (number of digits) of section \a sec. + This will correspond to either the year, month or day section, + depending on the current display order. + + \sa setOrder() +*/ + +int ExtDateEdit::sectionLength( int sec ) const +{ + int val = 0; + if ( sec == d->yearSection ) + val = 4; + else if ( sec == d->monthSection ) + val = 2; + else if ( sec == d->daySection ) + val = 2; + + return val; +} + +/*! + Returns the text of section \a sec. This will correspond to either + the year, month or day section, depending on the current display + order. + + \sa setOrder() +*/ + +QString ExtDateEdit::sectionText( int sec ) const +{ + int val = 0; + if ( sec == d->yearSection ) + val = d->y; + else if ( sec == d->monthSection ) + val = d->m; + else if ( sec == d->daySection ) + val = d->d; + + return QString::number( val ); +} + +/*! \internal + + Returns the end of the section offset \a sec. + +*/ + +int ExtDateEdit::sectionOffsetEnd( int sec ) const +{ + if ( sec == d->yearSection ) { + switch( d->ord ) { + case DMY: + case MDY: + return sectionOffsetEnd( sec-1) + separator().length() + sectionLength( sec ); + case YMD: + case YDM: + return sectionLength( sec ); + } + } else if ( sec == d->monthSection ) { + switch( d->ord ) { + case DMY: + case YDM: + case YMD: + return sectionOffsetEnd( sec-1) + separator().length() + sectionLength( sec ); + case MDY: + return sectionLength( sec ); + } + } else if ( sec == d->daySection ) { + switch( d->ord ) { + case DMY: + return sectionLength( sec ); + case YMD: + case MDY: + case YDM: + return sectionOffsetEnd( sec-1 ) + separator().length() + sectionLength( sec ); + } + } + return 0; +} + + +/*! + \property ExtDateEdit::order + \brief the order in which the year, month and day appear + + The default order is locale dependent. + + \sa Order +*/ + +void ExtDateEdit::setOrder( ExtDateEdit::Order order ) +{ + d->ord = order; + switch( d->ord ) { + case DMY: + d->yearSection = 2; + d->monthSection = 1; + d->daySection = 0; + break; + case MDY: + d->yearSection = 2; + d->monthSection = 0; + d->daySection = 1; + break; + case YMD: + d->yearSection = 0; + d->monthSection = 1; + d->daySection = 2; + break; + case YDM: + d->yearSection = 0; + d->monthSection = 2; + d->daySection = 1; + break; + } + + if ( isVisible() ) + d->ed->repaint( d->ed->rect(), FALSE ); +} + + +ExtDateEdit::Order ExtDateEdit::order() const +{ + return d->ord; +} + + +/*! \reimp + +*/ +void ExtDateEdit::stepUp() +{ + int sec = d->ed->focusSection(); + bool accepted = FALSE; + if ( sec == d->yearSection ) { + if ( !outOfRange( d->y+1, d->m, d->d ) ) { + accepted = TRUE; + setYear( d->y+1 ); + } + } else if ( sec == d->monthSection ) { + if ( !outOfRange( d->y, d->m+1, d->d ) ) { + accepted = TRUE; + setMonth( d->m+1 ); + } + } else if ( sec == d->daySection ) { + if ( !outOfRange( d->y, d->m, d->d+1 ) ) { + accepted = TRUE; + setDay( d->d+1 ); + } + } + if ( accepted ) { + d->changed = TRUE; + emit valueChanged( date() ); + } + + d->ed->repaint( d->ed->rect(), FALSE ); +} + + + +/*! \reimp + +*/ + +void ExtDateEdit::stepDown() +{ + int sec = d->ed->focusSection(); + bool accepted = FALSE; + if ( sec == d->yearSection ) { + if ( !outOfRange( d->y-1, d->m, d->d ) ) { + accepted = TRUE; + setYear( d->y-1 ); + } + } else if ( sec == d->monthSection ) { + if ( !outOfRange( d->y, d->m-1, d->d ) ) { + accepted = TRUE; + setMonth( d->m-1 ); + } + } else if ( sec == d->daySection ) { + if ( !outOfRange( d->y, d->m, d->d-1 ) ) { + accepted = TRUE; + setDay( d->d-1 ); + } + } + if ( accepted ) { + d->changed = TRUE; + emit valueChanged( date() ); + } + + d->ed->repaint( d->ed->rect(), FALSE ); +} + +/*! + Sets the year to \a year, which must be a valid year. The range + currently supported is from 1752 to 8000. + + \sa ExtDate +*/ + +void ExtDateEdit::setYear( int year ) +{ + if ( !outOfRange( year, d->m, d->d ) ) { + d->y = year; + setMonth( d->m ); + } +} + + +/*! + Sets the month to \a month, which must be a valid month, i.e. + between 1 and 12. +*/ + +void ExtDateEdit::setMonth( int month ) +{ + if ( month < 1 ) + month = 1; + if ( month > 12 ) + month = 12; + if ( !outOfRange( d->y, month, d->d ) ) { + d->m = month; + setDay( d->d ); + } +} + + +/*! + Sets the day to \a day, which must be a valid day. The function + will ensure that the \a day set is valid for the month and year. +*/ + +void ExtDateEdit::setDay( int day ) +{ + ExtDate test = ExtDate( d->y, d->m, 1 ); + + if ( day < 1 ) + day = 1; + if ( day > test.daysInMonth() ) + day = test.daysInMonth(); + + d->dayCache = d->d; + d->d = day; +} + + +/*! + \property ExtDateEdit::date + \brief the editor's date value. + + If the date property is not valid, the editor displays all zeroes + and ExtDateEdit::date() will return an invalid date. It is strongly + recommended that the editor is given a default date value (e.g. + currentDate()). That way, attempts to set the date property to an + invalid date will fail. + + When changing the date property, if the date is less than + minValue(), or is greater than maxValue(), nothing happens. +*/ + +void ExtDateEdit::setDate( const ExtDate& date ) +{ + if ( !date.isValid() ) { + d->y = 0; + d->m = 0; + d->d = 0; + d->dayCache = 0; + } else { + if ( date > maxValue() || date < minValue() ) + return; + d->y = date.year(); + d->m = date.month(); + d->d = date.day(); + d->dayCache = d->d; + emit valueChanged( date ); + } + d->changed = FALSE; + d->ed->repaint( d->ed->rect(), FALSE ); +} + +ExtDate ExtDateEdit::date() const +{ + if ( ExtDate::isValid( d->y, d->m, d->d ) ) + return ExtDate( d->y, d->m, d->d ); + return ExtDate(); +} + +/*! \internal + + Returns TRUE if \a y, \a m, \a d is out of range, otherwise returns + FALSE. + + \sa setRange() + +*/ + +bool ExtDateEdit::outOfRange( int y, int m, int d ) const +{ + if ( ExtDate::isValid( y, m, d ) ) { + ExtDate currentDate( y, m, d ); + if ( currentDate > maxValue() || currentDate < minValue() ) { + //## outOfRange should set overwrite? + return TRUE; + } + return FALSE; + } + return FALSE; /* assume ok */ +} + +/*! \reimp + +*/ + +void ExtDateEdit::addNumber( int sec, int num ) +{ + if ( sec == -1 ) + return; + killTimer( d->timerId ); + bool overwrite = FALSE; + bool accepted = FALSE; + d->typing = TRUE; + QString txt; + if ( sec == d->yearSection ) { + if ( d->overwrite ) { + d->y = num; + d->overwrite = FALSE; + accepted = TRUE; + } else { + txt = QString::number( 10*d->y + num ); + if ( txt.length() > 5 ) txt = txt.mid(1); + d->y = txt.toInt(); + accepted = TRUE; + } +/* + txt = QString::number( d->y ); + if ( d->overwrite || txt.length() == 4 ) { + accepted = TRUE; + d->y = num; + } else { + txt += QString::number( num ); + if ( txt.length() == 4 ) { + int val = txt.toInt(); + if ( val < 1792 ) + d->y = 1792; + else if ( val > 8000 ) + d->y = 8000; + else if ( outOfRange( val, d->m, d->d ) ) + txt = QString::number( d->y ); + else { + accepted = TRUE; + d->y = val; + } + } else { + accepted = TRUE; + d->y = txt.toInt(); + } + if ( d->adv && txt.length() == 4 ) { + d->ed->setFocusSection( d->ed->focusSection()+1 ); + overwrite = TRUE; + } + } +*/ + } else if ( sec == d->monthSection ) { + txt = QString::number( d->m ); + if ( d->overwrite || txt.length() == 2 ) { + accepted = TRUE; + d->m = num; + } else { + txt += QString::number( num ); + int temp = txt.toInt(); + if ( temp > 12 ) + temp = num; + if ( outOfRange( d->y, temp, d->d ) ) + txt = QString::number( d->m ); + else { + accepted = TRUE; + d->m = temp; + } + if ( d->adv && txt.length() == 2 ) { + d->ed->setFocusSection( d->ed->focusSection()+1 ); + overwrite = TRUE; + } + } + } else if ( sec == d->daySection ) { + txt = QString::number( d->d ); + if ( d->overwrite || txt.length() == 2 ) { + accepted = TRUE; + d->d = num; + d->dayCache = d->d; + } else { + txt += QString::number( num ); + int temp = txt.toInt(); + if ( temp > 31 ) + temp = num; + if ( outOfRange( d->y, d->m, temp ) ) + txt = QString::number( d->d ); + else { + accepted = TRUE; + d->d = temp; + d->dayCache = d->d; + } + if ( d->adv && txt.length() == 2 ) { + d->ed->setFocusSection( d->ed->focusSection()+1 ); + overwrite = TRUE; + } + } + } + if ( accepted ) { + d->changed = TRUE; + emit valueChanged( date() ); + } + d->overwrite = overwrite; + d->timerId = startTimer( qApp->doubleClickInterval()*4 ); + d->ed->repaint( d->ed->rect(), FALSE ); +} + + +/*! \reimp + +*/ + +bool ExtDateEdit::setFocusSection( int s ) +{ + if ( s != d->ed->focusSection() ) { + killTimer( d->timerId ); + d->overwrite = TRUE; + d->typing = FALSE; + fix(); // will emit valueChanged if necessary + } + return d->ed->setFocusSection( s ); +} + + +/*! + Attempts to fix any invalid date entries. + + The rules applied are as follows: + + - if the day is larger than the number of days in the month, + - it is reset to that number + - If the year has four digits it is left unchanged. + - If the year has two digits, the year will be changed to four + digits in the range current year - 70 to current year + 29. + - If the year has three digits in the range 100..999, the + current millennium, i.e. 2000, will be added giving a year + in the range 2100..2999. + +*/ + +void ExtDateEdit::fix() +{ + bool changed = FALSE; + + ExtDate test = ExtDate( d->y, d->m, 1 ); + if ( d->d > test.daysInMonth() ) { + + d->d = test.daysInMonth(); + changed = TRUE; + } + + int currentYear = ExtDate::currentDate().year(); + int year = d->y; +/* No longer valid for extended dates + if ( year < 100 ) { + int currentCentury = currentYear / 100; + year += currentCentury * 100; + if ( currentYear > year ) { + if ( currentYear > year + 70 ) + year += 100; + } else { + if ( year >= currentYear + 30 ) + year -= 100; + } + changed = TRUE; + } else if ( year < 1000 ) { + int currentMillennium = currentYear / 10; + year += currentMillennium * 10; + changed = TRUE; + } +*/ + if ( changed && outOfRange( year, d->m, d->d ) ) { + if ( minValue().isValid() && date() < minValue() ) { + d->d = minValue().day(); + d->dayCache = d->d; + d->m = minValue().month(); + d->y = minValue().year(); + } + if ( date() > maxValue() ) { + d->d = maxValue().day(); + d->dayCache = d->d; + d->m = maxValue().month(); + d->y = maxValue().year(); + } + } else if ( changed ) + setYear( year ); + + if ( changed ) { +// emit valueChanged( date() ); +// d->changed = FALSE; + } +} + + +/*! \reimp + +*/ + +bool ExtDateEdit::event( QEvent *e ) +{ + if( e->type() == QEvent::FocusOut ) { + d->typing = FALSE; + d->overwrite = TRUE; + // the following can't be done in fix() because fix() called + // from all over the place and it will break the old behaviour + if ( !ExtDate::isValid( d->y, d->m, d->d ) ) { + d->dayCache = d->d; + int i = d->d; + for ( ; i > 0; i-- ) { + d->d = i; + if ( ExtDate::isValid( d->y, d->m, d->d ) ) + break; + } + d->changed = TRUE; + } + if ( d->changed ) { + fix(); + emit valueChanged( date() ); + d->changed = FALSE; + } + } else if ( e->type() == QEvent::LocaleChange ) { + readLocaleSettings(); + d->ed->setSeparator( localDateSep() ); + setOrder( localOrder() ); + } + + bool result = ExtDateTimeEditBase::event( e ); + + return result; +} + +/*! + \internal + + Function which is called whenever the user tries to + remove the first number from \a sec by pressing the backspace key. +*/ + +void ExtDateEdit::removeFirstNumber( int sec ) +{ + if ( sec == -1 ) + return; + QString txt; + if ( sec == d->yearSection ) { + txt = QString::number( d->y ); + txt = txt.mid( 1, txt.length() ) + "0"; + d->y = txt.toInt(); + } else if ( sec == d->monthSection ) { + txt = QString::number( d->m ); + txt = txt.mid( 1, txt.length() ) + "0"; + d->m = txt.toInt(); + } else if ( sec == d->daySection ) { + txt = QString::number( d->d ); + txt = txt.mid( 1, txt.length() ) + "0"; + d->d = txt.toInt(); + d->dayCache = d->d; + } + d->ed->repaint( d->ed->rect(), FALSE ); +} + +/*! \reimp + +*/ + +void ExtDateEdit::removeLastNumber( int sec ) +{ + if ( sec == -1 ) + return; + QString txt; + if ( sec == d->yearSection ) { + txt = QString::number( d->y ); + txt = txt.mid( 0, txt.length()-1 ); + d->y = txt.toInt(); + } else if ( sec == d->monthSection ) { + txt = QString::number( d->m ); + txt = txt.mid( 0, txt.length()-1 ); + d->m = txt.toInt(); + } else if ( sec == d->daySection ) { + txt = QString::number( d->d ); + txt = txt.mid( 0, txt.length()-1 ); + d->d = txt.toInt(); + d->dayCache = d->d; + } + d->ed->repaint( d->ed->rect(), FALSE ); +} + +/*! + \property ExtDateEdit::autoAdvance + \brief whether the editor automatically advances to the next + section + + If autoAdvance is TRUE, the editor will automatically advance + focus to the next date section if a user has completed a section. + The default is FALSE. +*/ + +void ExtDateEdit::setAutoAdvance( bool advance ) +{ + d->adv = advance; +} + + +bool ExtDateEdit::autoAdvance() const +{ + return d->adv; +} + +/*! \reimp +*/ + +void ExtDateEdit::timerEvent( QTimerEvent * ) +{ + d->overwrite = TRUE; +} + +/*! + \fn void ExtDateEdit::valueChanged( const ExtDate& date ) + + This signal is emitted whenever the editor's value changes. The \a + date parameter is the new value. +*/ + +/////////// + +class QTimeEditPrivate +{ +public: + int h; + int m; + int s; + uint display; + bool adv; + bool overwrite; + int timerId; + bool typing; + QTime min; + QTime max; + bool changed; + ExtDateTimeEditor *ed; + QSpinWidget *controls; +}; + +/*! + \class QTimeEdit ExtDatetimeedit.h + \brief The QTimeEdit class provides a time editor. + + \ingroup advanced + \ingroup time + \mainclass + + QTimeEdit allows the user to edit times by using the keyboard or + the arrow keys to increase/decrease time values. The arrow keys + can be used to move from section to section within the QTimeEdit + box. The user can automatically be moved to the next section once + they complete a section using setAutoAdvance(). Times appear in + hour, minute, second order. It is recommended that the QTimeEdit + is initialised with a time, e.g. + \code + QTime timeNow = QTime::currentTime(); + QTimeEdit *timeEdit = new QTimeEdit( timeNow, this ); + timeEdit->setRange( timeNow, timeNow.addSecs( 60 * 60 ) ); + \endcode + Here we've created a QTimeEdit widget set to the current time. + We've also set the minimum value to the current time and the + maximum time to one hour from now. + + The maximum and minimum values for a time value in the time editor + default to the maximum and minimum values for a QTime. You can + change this by calling setMinValue(), setMaxValue() or setRange(). + + Terminology: A QTimeWidget consists of three sections, one each + for the hour, minute and second. You can change the separator + character using setSeparator(), by default the separator is read + from the system's settings. + + \img datetimewidgets.png Date Time Widgets + + \sa QTime ExtDateEdit ExtDateTimeEdit +*/ + + +// /*! +// Constructs an empty time edit with parent \a parent and called \a +// name. +// */ +// +// QTimeEdit::QTimeEdit( QWidget * parent, const char * name ) +// : ExtDateTimeEditBase( parent, name ) +// { +// init(); +// } +// +// /*! +// \overload +// +// Constructs a time edit with the initial time value, \a time, +// parent \a parent and called \a name. +// */ +// +// QTimeEdit::QTimeEdit( const QTime& time, QWidget * parent, const char * name ) +// : ExtDateTimeEditBase( parent, name ) +// { +// init(); +// setTime( time ); +// } +// +// /*! \internal +// */ +// +// void QTimeEdit::init() +// { +// d = new QTimeEditPrivate(); +// d->ed = new ExtDateTimeEditor( this, "time edit base" ); +// d->controls = new ExtDateTimeSpinWidget( this, qstrcmp( name(), "qt_datetime_timeedit" ) == 0 ? "qt_spin_widget" : "time edit controls" ); +// d->controls->setEditWidget( d->ed ); +// setFocusProxy( d->ed ); +// connect( d->controls, SIGNAL( stepUpPressed() ), SLOT( stepUp() ) ); +// connect( d->controls, SIGNAL( stepDownPressed() ), SLOT( stepDown() ) ); +// +// d->ed->appendSection( QNumberSection( 0,0, TRUE, 0 ) ); +// d->ed->appendSection( QNumberSection( 0,0, TRUE, 1 ) ); +// d->ed->appendSection( QNumberSection( 0,0, TRUE, 2 ) ); +// d->ed->setSeparator( localTimeSep() ); +// +// d->h = 0; +// d->m = 0; +// d->s = 0; +// d->display = Hours | Minutes | Seconds; +// if ( lAMPM ) { +// d->display |= AMPM; +// d->ed->appendSection( QNumberSection( 0,0, FALSE, 3 ) ); +// } +// d->adv = FALSE; +// d->overwrite = TRUE; +// d->timerId = 0; +// d->typing = FALSE; +// d->min = QTime( 0, 0, 0 ); +// d->max = QTime( 23, 59, 59 ); +// d->changed = FALSE; +// +// setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ); +// +// refcount++; +// } +// +// /*! +// Destroys the object and frees any allocated resources. +// */ +// +// QTimeEdit::~QTimeEdit() +// { +// delete d; +// if ( !--refcount ) +// cleanup(); +// } +// +// /*! +// \property QTimeEdit::minValue +// \brief the minimum time value +// +// Setting the minimum time value is equivalent to calling +// QTimeEdit::setRange( \e t, maxValue() ), where \e t is the minimum +// time. The default minimum time is 00:00:00. +// +// \sa maxValue setRange() +// */ +// +// QTime QTimeEdit::minValue() const +// { +// return d->min; +// } +// +// /*! +// \property QTimeEdit::maxValue +// \brief the maximum time value +// +// Setting the maximum time value is equivalent to calling +// QTimeEdit::setRange( minValue(), \e t ), where \e t is the maximum +// time. The default maximum time is 23:59:59. +// +// \sa minValue setRange() +// */ +// +// QTime QTimeEdit::maxValue() const +// { +// return d->max; +// } +// +// +// /*! +// Sets the valid input range for the editor to be from \a min to \a +// max inclusive. If \a min is invalid no minimum time is set. +// Similarly, if \a max is invalid no maximum time is set. +// */ +// +// void QTimeEdit::setRange( const QTime& min, const QTime& max ) +// { +// if ( min.isValid() ) +// d->min = min; +// if ( max.isValid() ) +// d->max = max; +// } +// +// /*! +// \property QTimeEdit::display +// \brief the sections that are displayed in the time edit +// +// The value can be any combination of the values in the Display enum. +// By default, the widget displays hours, minutes and seconds. +// */ +// void QTimeEdit::setDisplay( uint display ) +// { +// if ( d->display == display ) +// return; +// +// d->ed->clearSections(); +// d->display = display; +// if ( d->display & Hours ) +// d->ed->appendSection( QNumberSection( 0,0, TRUE, 0 ) ); +// if ( d->display & Minutes ) +// d->ed->appendSection( QNumberSection( 0,0, TRUE, 1 ) ); +// if ( d->display & Seconds ) +// d->ed->appendSection( QNumberSection( 0,0, TRUE, 2 ) ); +// if ( d->display & AMPM ) +// d->ed->appendSection( QNumberSection( 0,0, FALSE, 3 ) ); +// +// d->ed->setFocusSection( 0 ); +// d->ed->update(); +// } +// +// uint QTimeEdit::display() const +// { +// return d->display; +// } +// +// /*! +// \property QTimeEdit::time +// \brief the editor's time value. +// +// When changing the time property, if the time is less than +// minValue(), or is greater than maxValue(), nothing happens. +// */ +// +// void QTimeEdit::setTime( const QTime& time ) +// { +// if ( !time.isValid() ) { +// d->h = 0; +// d->m = 0; +// d->s = 0; +// } else { +// if ( time > maxValue() || time < minValue() ) +// return; +// d->h = time.hour(); +// d->m = time.minute(); +// d->s = time.second(); +// emit valueChanged( time ); +// } +// d->changed = FALSE; +// d->ed->repaint( d->ed->rect(), FALSE ); +// } +// +// QTime QTimeEdit::time() const +// { +// if ( QTime::isValid( d->h, d->m, d->s ) ) +// return QTime( d->h, d->m, d->s ); +// return QTime(); +// } +// +// /*! +// \property QTimeEdit::autoAdvance +// \brief whether the editor automatically advances to the next +// section +// +// If autoAdvance is TRUE, the editor will automatically advance +// focus to the next time section if a user has completed a section. +// The default is FALSE. +// */ +// +// void QTimeEdit::setAutoAdvance( bool advance ) +// { +// d->adv = advance; +// } +// +// bool QTimeEdit::autoAdvance() const +// { +// return d->adv; +// } +// +// /*! +// Sets the separator to \a s. Note that currently only the first +// character of \a s is used. +// */ +// +// void QTimeEdit::setSeparator( const QString& s ) +// { +// d->ed->setSeparator( s ); +// } +// +// /*! +// Returns the editor's separator. +// */ +// +// QString QTimeEdit::separator() const +// { +// return d->ed->separator(); +// } +// +// +// /*! +// \fn void QTimeEdit::valueChanged( const QTime& time ) +// +// This signal is emitted whenever the editor's value changes. The \a +// time parameter is the new value. +// */ +// +// /*! \reimp +// +// */ +// +// bool QTimeEdit::event( QEvent *e ) +// { +// if ( e->type() == QEvent::FocusOut ) { +// d->typing = FALSE; +// if ( d->changed ) { +// emit valueChanged( time() ); +// d->changed = FALSE; +// } +// } else if ( e->type() == QEvent::LocaleChange ) { +// readLocaleSettings(); +// d->ed->setSeparator( localTimeSep() ); +// } +// return ExtDateTimeEditBase::event( e ); +// } +// +// /*! \reimp +// +// */ +// +// void QTimeEdit::timerEvent( QTimerEvent * ) +// { +// d->overwrite = TRUE; +// } +// +// +// /*! \reimp +// +// */ +// +// void QTimeEdit::stepUp() +// { +// int sec = d->ed->mapSection( d->ed->focusSection() ); +// bool accepted = TRUE; +// switch( sec ) { +// case 0: +// if ( !outOfRange( d->h+1, d->m, d->s ) ) +// setHour( d->h+1 ); +// else +// setHour( d->min.hour() ); +// break; +// case 1: +// if ( !outOfRange( d->h, d->m+1, d->s ) ) +// setMinute( d->m+1 ); +// else +// setMinute( d->min.minute() ); +// break; +// case 2: +// if ( !outOfRange( d->h, d->m, d->s+1 ) ) +// setSecond( d->s+1 ); +// else +// setSecond( d->min.second() ); +// break; +// case 3: +// if ( d->h < 12 ) +// setHour( d->h+12 ); +// else +// setHour( d->h-12 ); +// break; +// default: +// accepted = FALSE; +// #ifdef QT_CHECK_RANGE +// qWarning( "QTimeEdit::stepUp: Focus section out of range!" ); +// #endif +// break; +// } +// if ( accepted ) { +// d->changed = TRUE; +// emit valueChanged( time() ); +// } +// d->ed->repaint( d->ed->rect(), FALSE ); +// } +// +// +// /*! \reimp +// +// */ +// +// void QTimeEdit::stepDown() +// { +// int sec = d->ed->mapSection( d->ed->focusSection() ); +// +// bool accepted = TRUE; +// switch( sec ) { +// case 0: +// if ( !outOfRange( d->h-1, d->m, d->s ) ) +// setHour( d->h-1 ); +// else +// setHour( d->max.hour() ); +// break; +// case 1: +// if ( !outOfRange( d->h, d->m-1, d->s ) ) +// setMinute( d->m-1 ); +// else +// setMinute( d->max.minute() ); +// break; +// case 2: +// if ( !outOfRange( d->h, d->m, d->s-1 ) ) +// setSecond( d->s-1 ); +// else +// setSecond( d->max.second() ); +// break; +// case 3: +// if ( d->h > 11 ) +// setHour( d->h-12 ); +// else +// setHour( d->h+12 ); +// break; +// default: +// accepted = FALSE; +// #ifdef QT_CHECK_RANGE +// qWarning( "QTimeEdit::stepDown: Focus section out of range!" ); +// #endif +// break; +// } +// if ( accepted ) { +// d->changed = TRUE; +// emit valueChanged( time() ); +// } +// d->ed->repaint( d->ed->rect(), FALSE ); +// } +// +// +// /*! +// Returns the formatted number for section \a sec. This will +// correspond to either the hour, minute or second section, depending +// on \a sec. +// */ +// +// QString QTimeEdit::sectionFormattedText( int sec ) +// { +// QString txt; +// txt = sectionText( sec ); +// txt = txt.rightJustify( 2, EXTDATETIMEEDIT_HIDDEN_CHAR ); +// int offset = sec*2+sec*separator().length() + txt.length(); +// if ( d->typing && sec == d->ed->focusSection() ) +// d->ed->setSectionSelection( sec, offset - txt.length(), offset ); +// else +// d->ed->setSectionSelection( sec, offset - txt.length(), offset ); +// +// return txt; +// } +// +// +// /*! \reimp +// +// */ +// +// bool QTimeEdit::setFocusSection( int sec ) +// { +// if ( sec != d->ed->focusSection() ) { +// killTimer( d->timerId ); +// d->overwrite = TRUE; +// d->typing = FALSE; +// QString txt = sectionText( sec ); +// txt = txt.rightJustify( 2, EXTDATETIMEEDIT_HIDDEN_CHAR ); +// int offset = sec*2+sec*separator().length() + txt.length(); +// d->ed->setSectionSelection( sec, offset - txt.length(), offset ); +// if ( d->changed ) { +// emit valueChanged( time() ); +// d->changed = FALSE; +// } +// } +// return d->ed->setFocusSection( sec ); +// } +// +// +// /*! +// Sets the hour to \a h, which must be a valid hour, i.e. in the +// range 0..24. +// */ +// +// void QTimeEdit::setHour( int h ) +// { +// if ( h < 0 ) +// h = 0; +// if ( h > 23 ) +// h = 23; +// d->h = h; +// } +// +// +// /*! +// Sets the minute to \a m, which must be a valid minute, i.e. in the +// range 0..59. +// */ +// +// void QTimeEdit::setMinute( int m ) +// { +// if ( m < 0 ) +// m = 0; +// if ( m > 59 ) +// m = 59; +// d->m = m; +// } +// +// +// /*! +// Sets the second to \a s, which must be a valid second, i.e. in the +// range 0..59. +// */ +// +// void QTimeEdit::setSecond( int s ) +// { +// if ( s < 0 ) +// s = 0; +// if ( s > 59 ) +// s = 59; +// d->s = s; +// } +// +// +// /*! \internal +// +// Returns the text of section \a sec. +// +// */ +// +// QString QTimeEdit::sectionText( int sec ) +// { +// sec = d->ed->mapSection( sec ); +// +// QString txt; +// switch( sec ) { +// case 0: +// if ( !(d->display & AMPM) || ( d->h < 13 && d->h ) ) { // I wished the day stared at 0:00 for everybody +// txt = QString::number( d->h ); +// } else { +// if ( d->h ) +// txt = QString::number( d->h - 12 ); +// else +// txt = "12"; +// } +// break; +// case 1: +// txt = QString::number( d->m ); +// break; +// case 2: +// txt = QString::number( d->s ); +// break; +// case 3: +// if ( d->h < 12 ) { +// if ( lAM ) +// txt = *lAM; +// else +// txt = QString::fromLatin1( "AM" ); +// } else { +// if ( lPM ) +// txt = *lPM; +// else +// txt = QString::fromLatin1( "PM" ); +// } +// break; +// default: +// break; +// } +// return txt; +// } +// +// +// /*! \internal +// Returns TRUE if \a h, \a m, and \a s are out of range. +// */ +// +// bool QTimeEdit::outOfRange( int h, int m, int s ) const +// { +// if ( QTime::isValid( h, m, s ) ) { +// QTime currentTime( h, m, s ); +// if ( currentTime > maxValue() || +// currentTime < minValue() ) +// return TRUE; +// else +// return FALSE; +// } +// return TRUE; +// } +// +// /*! \reimp +// +// */ +// +// void QTimeEdit::addNumber( int sec, int num ) +// { +// if ( sec == -1 ) +// return; +// sec = d->ed->mapSection( sec ); +// killTimer( d->timerId ); +// bool overwrite = FALSE; +// bool accepted = FALSE; +// d->typing = TRUE; +// QString txt; +// +// switch( sec ) { +// case 0: +// txt = ( d->display & AMPM && d->h > 12 ) ? +// QString::number( d->h - 12 ) : QString::number( d->h ); +// +// if ( d->overwrite || txt.length() == 2 ) { +// if ( d->display & AMPM && num == 0 ) +// break; // Don't process 0 in 12 hour clock mode +// if ( d->display & AMPM && d->h > 11 ) +// num += 12; +// if ( !outOfRange( num, d->m, d->s ) ) { +// accepted = TRUE; +// d->h = num; +// } +// } else { +// txt += QString::number( num ); +// int temp = txt.toInt(); +// +// if ( d->display & AMPM ) { +// if ( temp == 12 ) { +// if ( d->h < 12 ) { +// temp = 0; +// } +// accepted = TRUE; +// } else if ( outOfRange( temp + 12, d->m, d->s ) ) { +// txt = QString::number( d->h ); +// } else { +// if ( d->h > 11 ) { +// temp += 12; +// } +// accepted = TRUE; +// } +// } else if ( !(d->display & AMPM) && outOfRange( temp, d->m, d->s ) ) { +// txt = QString::number( d->h ); +// } else { +// accepted = TRUE; +// } +// +// if ( accepted ) +// d->h = temp; +// +// if ( d->adv && txt.length() == 2 ) { +// setFocusSection( d->ed->focusSection()+1 ); +// overwrite = TRUE; +// } +// } +// break; +// +// case 1: +// txt = QString::number( d->m ); +// if ( d->overwrite || txt.length() == 2 ) { +// if ( !outOfRange( d->h, num, d->s ) ) { +// accepted = TRUE; +// d->m = num; +// } +// } else { +// txt += QString::number( num ); +// int temp = txt.toInt(); +// if ( temp > 59 ) +// temp = num; +// if ( outOfRange( d->h, temp, d->s ) ) +// txt = QString::number( d->m ); +// else { +// accepted = TRUE; +// d->m = temp; +// } +// if ( d->adv && txt.length() == 2 ) { +// setFocusSection( d->ed->focusSection()+1 ); +// overwrite = TRUE; +// } +// } +// break; +// +// case 2: +// txt = QString::number( d->s ); +// if ( d->overwrite || txt.length() == 2 ) { +// if ( !outOfRange( d->h, d->m, num ) ) { +// accepted = TRUE; +// d->s = num; +// } +// } else { +// txt += QString::number( num ); +// int temp = txt.toInt(); +// if ( temp > 59 ) +// temp = num; +// if ( outOfRange( d->h, d->m, temp ) ) +// txt = QString::number( d->s ); +// else { +// accepted = TRUE; +// d->s = temp; +// } +// if ( d->adv && txt.length() == 2 ) { +// setFocusSection( d->ed->focusSection()+1 ); +// overwrite = TRUE; +// } +// } +// break; +// +// case 3: +// break; +// +// default: +// break; +// } +// d->changed = accepted; +// if ( accepted ) +// emit valueChanged( time() ); +// d->overwrite = overwrite; +// d->timerId = startTimer( qApp->doubleClickInterval()*4 ); +// d->ed->repaint( d->ed->rect(), FALSE ); +// } +// +// +// /*! +// \internal +// +// Function which is called whenever the user tries to +// remove the first number from \a sec by pressing the backspace key. +// */ +// +// void QTimeEdit::removeFirstNumber( int sec ) +// { +// if ( sec == -1 ) +// return; +// sec = d->ed->mapSection( sec ); +// QString txt; +// switch( sec ) { +// case 0: +// txt = QString::number( d->h ); +// break; +// case 1: +// txt = QString::number( d->m ); +// break; +// case 2: +// txt = QString::number( d->s ); +// break; +// } +// txt = txt.mid( 1, txt.length() ) + "0"; +// switch( sec ) { +// case 0: +// d->h = txt.toInt(); +// break; +// case 1: +// d->m = txt.toInt(); +// break; +// case 2: +// d->s = txt.toInt(); +// break; +// } +// d->ed->repaint( d->ed->rect(), FALSE ); +// } +// +// /*! \reimp +// +// */ +// void QTimeEdit::removeLastNumber( int sec ) +// { +// if ( sec == -1 ) +// return; +// sec = d->ed->mapSection( sec ); +// QString txt; +// switch( sec ) { +// case 0: +// txt = QString::number( d->h ); +// break; +// case 1: +// txt = QString::number( d->m ); +// break; +// case 2: +// txt = QString::number( d->s ); +// break; +// } +// txt = txt.mid( 0, txt.length()-1 ); +// switch( sec ) { +// case 0: +// d->h = txt.toInt(); +// break; +// case 1: +// d->m = txt.toInt(); +// break; +// case 2: +// d->s = txt.toInt(); +// break; +// } +// d->ed->repaint( d->ed->rect(), FALSE ); +// } +// +// /*! \reimp +// */ +// void QTimeEdit::resizeEvent( QResizeEvent * ) +// { +// d->controls->resize( width(), height() ); +// } +// +// /*! \reimp +// */ +// QSize QTimeEdit::sizeHint() const +// { +// constPolish(); +// QFontMetrics fm( font() ); +// int fw = style().pixelMetric( QStyle::PM_DefaultFrameWidth, this ); +// int h = fm.lineSpacing() + 2; +// int w = 2 + fm.width( '9' ) * 6 + fm.width( d->ed->separator() ) * 2 + +// d->controls->upRect().width() + fw * 4; +// if ( d->display & AMPM ) { +// if ( lAM ) +// w += fm.width( *lAM ) + 4; +// else +// w += fm.width( QString::fromLatin1( "AM" ) ) + 4; +// } +// +// return QSize( w, QMAX(h + fw * 2,20) ).expandedTo( QApplication::globalStrut() ); +// } +// +// /*! \reimp +// */ +// QSize QTimeEdit::minimumSizeHint() const +// { +// return sizeHint(); +// } +// +// /*! +// \internal +// Enables/disables the push buttons according to the min/max time +// for this widget. +// */ +// +// // ### Remove in 4.0? +// +// void QTimeEdit::updateButtons() +// { +// if ( !isEnabled() ) +// return; +// +// bool upEnabled = time() < maxValue(); +// bool downEnabled = time() > minValue(); +// +// d->controls->setUpEnabled( upEnabled ); +// d->controls->setDownEnabled( downEnabled ); +// } + + +class ExtDateTimeEditPrivate +{ +public: + bool adv; +}; + +/*! + \class ExtDateTimeEdit ExtDatetimeedit.h + \brief The ExtDateTimeEdit class combines a ExtDateEdit and QTimeEdit + widget into a single widget for editing datetimes. + + \ingroup advanced + \ingroup time + + ExtDateTimeEdit consists of a ExtDateEdit and QTimeEdit widget placed + side by side and offers the functionality of both. The user can + edit the date and time by using the keyboard or the arrow keys to + increase/decrease date or time values. The Tab key can be used to + move from section to section within the ExtDateTimeEdit widget, and + the user can be moved automatically when they complete a section + using setAutoAdvance(). The datetime can be set with + setDateTime(). + + The date format is read from the system's locale settings. It is + set to year, month, day order if that is not possible. See + ExtDateEdit::setOrder() to change this. Times appear in the order + hours, minutes, seconds using the 24 hour clock. + + It is recommended that the ExtDateTimeEdit is initialised with a + datetime, e.g. + \code + ExtDateTimeEdit *dateTimeEdit = new ExtDateTimeEdit( ExtDateTime::currentDateTime(), this ); + dateTimeEdit->dateEdit()->setRange( ExtDateTime::currentDate(), + ExtDateTime::currentDate().addDays( 7 ) ); + \endcode + Here we've created a new ExtDateTimeEdit set to the current date and + time, and set the date to have a minimum date of now and a maximum + date of a week from now. + + Terminology: A ExtDateEdit widget consists of three 'sections', one + each for the year, month and day. Similarly a QTimeEdit consists + of three sections, one each for the hour, minute and second. The + character that separates each date section is specified with + setDateSeparator(); similarly setTimeSeparator() is used for the + time sections. + + \image html datetimewidgets.png "Date Time Widgets" + + \sa ExtDateEdit QTimeEdit +*/ + +/*! + Constructs an empty datetime edit with parent \a parent and called + \a name. +*/ +ExtDateTimeEdit::ExtDateTimeEdit( QWidget * parent, const char * name ) + : QWidget( parent, name ) +{ + init(); +} + + +/*! + \overload + + Constructs a datetime edit with the initial value \a datetime, + parent \a parent and called \a name. +*/ +ExtDateTimeEdit::ExtDateTimeEdit( const ExtDateTime& datetime, + QWidget * parent, const char * name ) + : QWidget( parent, name ) +{ + init(); + setDateTime( datetime ); +} + + + +/*! + Destroys the object and frees any allocated resources. +*/ + +ExtDateTimeEdit::~ExtDateTimeEdit() +{ + delete d; +} + + +/*! + \reimp + + Intercepts and handles resize events which have special meaning + for the ExtDateTimeEdit. +*/ + +void ExtDateTimeEdit::resizeEvent( QResizeEvent * ) +{ + int dw = de->sizeHint().width(); + int tw = te->sizeHint().width(); + int w = width(); + int h = height(); + int extra = w - ( dw + tw ); + + if ( tw + extra < 0 ) { + dw = w; + } else { + dw += 9 * extra / 16; + } + tw = w - dw; + + de->setGeometry( 0, 0, dw, h ); + te->setGeometry( dw, 0, tw, h ); +} + +/*! \reimp +*/ + +QSize ExtDateTimeEdit::minimumSizeHint() const +{ + QSize dsh = de->minimumSizeHint(); + QSize tsh = te->minimumSizeHint(); + return QSize( dsh.width() + tsh.width(), + QMAX( dsh.height(), tsh.height() ) ); +} + +/*! \internal + */ + +void ExtDateTimeEdit::init() +{ + d = new ExtDateTimeEditPrivate(); + de = new ExtDateEdit( this, "qt_datetime_dateedit" ); + te = new QTimeEdit( this, "qt_datetime_timeedit" ); + d->adv = FALSE; + connect( de, SIGNAL( valueChanged( const ExtDate& ) ), + this, SLOT( newValue( const ExtDate& ) ) ); + connect( te, SIGNAL( valueChanged( const QTime& ) ), + this, SLOT( newValue( const QTime& ) ) ); + setFocusProxy( de ); + setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ); +} + +/*! \reimp + */ + +QSize ExtDateTimeEdit::sizeHint() const +{ + constPolish(); + QSize dsh = de->sizeHint(); + QSize tsh = te->sizeHint(); + return QSize( dsh.width() + tsh.width(), + QMAX( dsh.height(), tsh.height() ) ); +} + +/*! + \property ExtDateTimeEdit::dateTime + \brief the editor's datetime value + + The datetime edit's datetime which may be an invalid datetime. +*/ + +void ExtDateTimeEdit::setDateTime( const ExtDateTime & dt ) +{ + if ( dt.isValid() ) { + de->setDate( dt.date() ); + te->setTime( dt.time() ); + emit valueChanged( dt ); + } +} + +ExtDateTime ExtDateTimeEdit::dateTime() const +{ + return ExtDateTime( de->date(), te->time() ); +} + +/*! + \fn void ExtDateTimeEdit::valueChanged( const ExtDateTime& datetime ) + + This signal is emitted every time the date or time changes. The \a + datetime argument is the new datetime. +*/ + + +/*! \internal + + Re-emits the value \a d. + */ + +void ExtDateTimeEdit::newValue( const ExtDate& ) +{ + ExtDateTime dt = dateTime(); + emit valueChanged( dt ); +} + +/*! \internal + \overload + Re-emits the value \a t. + */ + +void ExtDateTimeEdit::newValue( const QTime& ) +{ + ExtDateTime dt = dateTime(); + emit valueChanged( dt ); +} + + +/*! + Sets the auto advance property of the editor to \a advance. If set + to TRUE, the editor will automatically advance focus to the next + date or time section if the user has completed a section. +*/ + +void ExtDateTimeEdit::setAutoAdvance( bool advance ) +{ + de->setAutoAdvance( advance ); + te->setAutoAdvance( advance ); +} + +/*! + Returns TRUE if auto-advance is enabled, otherwise returns FALSE. + + \sa setAutoAdvance() +*/ + +bool ExtDateTimeEdit::autoAdvance() const +{ + return de->autoAdvance(); +} + +/*! + \fn ExtDateEdit* ExtDateTimeEdit::dateEdit() + + Returns the internal widget used for editing the date part of the + datetime. +*/ + +/*! + \fn QTimeEdit* ExtDateTimeEdit::timeEdit() + + Returns the internal widget used for editing the time part of the + datetime. +*/ + +#include "extdatetimeedit.moc" + +#endif diff --git a/libkdeedu/extdate/extdatetimeedit.h b/libkdeedu/extdate/extdatetimeedit.h new file mode 100644 index 00000000..048f7aa3 --- /dev/null +++ b/libkdeedu/extdate/extdatetimeedit.h @@ -0,0 +1,341 @@ +/**************************************************************************** +** +** +** Definition of date and time edit classes +** +** Created : 001103 +** +** Original QDateTimeEdit Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** >> modifications to introduce ExtDate (C) 2004 Jason Harris <jharris@30doradus.org> +** >> ExtDate modifications are licensed under the GPL: http://www.gnu.org/licenses/gpl.html +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +**********************************************************************/ + +#ifndef EXTDATETIMEEDIT_H +#define EXTDATETIMEEDIT_H + +#ifndef QT_H +#include <qwidget.h> +#include <qstring.h> +#endif // QT_H + +#include "extdatetime.h" + +#ifndef QT_NO_DATETIMEEDIT + +class QTimeEdit; + +class ExtDateTimeEditBase : public QWidget +{ + Q_OBJECT +public: + ExtDateTimeEditBase( QWidget* parent=0, const char* name=0 ) + : QWidget( parent, name ) {} + + virtual bool setFocusSection( int sec ) = 0; + virtual QString sectionFormattedText( int sec ) = 0; + virtual void addNumber( int sec, int num ) = 0; + virtual void removeLastNumber( int sec ) = 0; + +public slots: + virtual void stepUp() = 0; + virtual void stepDown() = 0; + +private: +#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator= + ExtDateTimeEditBase( const ExtDateTimeEditBase & ); + ExtDateTimeEditBase &operator=( const ExtDateTimeEditBase & ); +#endif +}; + +class ExtDateEditPrivate; + +class KDE_EXPORT ExtDateEdit : public ExtDateTimeEditBase +{ + Q_OBJECT + Q_ENUMS( Order ) + Q_PROPERTY( Order order READ order WRITE setOrder ) +// Q_PROPERTY( ExtDate date READ date WRITE setDate ) + Q_PROPERTY( bool autoAdvance READ autoAdvance WRITE setAutoAdvance ) +// Q_PROPERTY( ExtDate maxValue READ maxValue WRITE setMaxValue ) +// Q_PROPERTY( ExtDate minValue READ minValue WRITE setMinValue ) + +public: + ExtDateEdit( QWidget* parent=0, const char* name=0 ); + ExtDateEdit( const ExtDate& date, QWidget* parent=0, const char* name=0 ); + ~ExtDateEdit(); + + enum Order { DMY /**< Day-Month-Year */, + MDY /**< Month-Day-Year */, + YMD /**< Year-Month-Day, also the default */, + YDM /**< Year-Day-Month @deprecated Included for completeness. */ }; + + QSize sizeHint() const; + QSize minimumSizeHint() const; + +public slots: + virtual void setDate( const ExtDate& date ); + +public: + ExtDate date() const; + virtual void setOrder( Order order ); + Order order() const; + virtual void setAutoAdvance( bool advance ); + bool autoAdvance() const; + + virtual void setMinValue( const ExtDate& d ) { setRange( d, maxValue() ); } + ExtDate minValue() const; + virtual void setMaxValue( const ExtDate& d ) { setRange( minValue(), d ); } + ExtDate maxValue() const; + virtual void setRange( const ExtDate& min, const ExtDate& max ); + QString separator() const; + virtual void setSeparator( const QString& s ); + + // Make removeFirstNumber() virtual in ExtDateTimeEditBase in 4.0 + void removeFirstNumber( int sec ); + +signals: + void valueChanged( const ExtDate& date ); + +protected: + bool event( QEvent *e ); + void timerEvent( QTimerEvent * ); + void resizeEvent( QResizeEvent * ); + void stepUp(); + void stepDown(); + QString sectionFormattedText( int sec ); + void addNumber( int sec, int num ); + + void removeLastNumber( int sec ); + bool setFocusSection( int s ); + + virtual void setYear( int year ); + virtual void setMonth( int month ); + virtual void setDay( int day ); + virtual void fix(); + virtual bool outOfRange( int y, int m, int d ) const; + +protected slots: + void updateButtons(); + +private: + void init(); + int sectionOffsetEnd( int sec ) const; + int sectionLength( int sec ) const; + QString sectionText( int sec ) const; + ExtDateEditPrivate* d; + +#if defined(Q_DISABLE_COPY) + ExtDateEdit( const ExtDateEdit & ); + ExtDateEdit &operator=( const ExtDateEdit & ); +#endif +}; + +// class QTimeEditPrivate; +// +// class Q_EXPORT QTimeEdit : public ExtDateTimeEditBase +// { +// Q_OBJECT +// Q_SETS( Display ) +// Q_PROPERTY( QTime time READ time WRITE setTime ) +// Q_PROPERTY( bool autoAdvance READ autoAdvance WRITE setAutoAdvance ) +// Q_PROPERTY( QTime maxValue READ maxValue WRITE setMaxValue ) +// Q_PROPERTY( QTime minValue READ minValue WRITE setMinValue ) +// Q_PROPERTY( Display display READ display WRITE setDisplay ) +// +// public: +// enum Display { +// Hours = 0x01, +// Minutes = 0x02, +// Seconds = 0x04, +// /*Reserved = 0x08,*/ +// AMPM = 0x10 +// }; +// +// QTimeEdit( QWidget* parent=0, const char* name=0 ); +// QTimeEdit( const QTime& time, QWidget* parent=0, const char* name=0 ); +// ~QTimeEdit(); +// +// QSize sizeHint() const; +// QSize minimumSizeHint() const; +// +// public slots: +// virtual void setTime( const QTime& time ); +// +// public: +// QTime time() const; +// virtual void setAutoAdvance( bool advance ); +// bool autoAdvance() const; +// +// virtual void setMinValue( const QTime& d ) { setRange( d, maxValue() ); } +// QTime minValue() const; +// virtual void setMaxValue( const QTime& d ) { setRange( minValue(), d ); } +// QTime maxValue() const; +// virtual void setRange( const QTime& min, const QTime& max ); +// QString separator() const; +// virtual void setSeparator( const QString& s ); +// +// uint display() const; +// void setDisplay( uint disp ); +// +// // Make removeFirstNumber() virtual in ExtDateTimeEditBase in 4.0 +// void removeFirstNumber( int sec ); +// +// signals: +// void valueChanged( const QTime& time ); +// +// protected: +// bool event( QEvent *e ); +// void timerEvent( QTimerEvent *e ); +// void resizeEvent( QResizeEvent * ); +// void stepUp(); +// void stepDown(); +// QString sectionFormattedText( int sec ); +// void addNumber( int sec, int num ); +// void removeLastNumber( int sec ); +// bool setFocusSection( int s ); +// +// virtual bool outOfRange( int h, int m, int s ) const; +// virtual void setHour( int h ); +// virtual void setMinute( int m ); +// virtual void setSecond( int s ); +// +// protected slots: +// void updateButtons(); +// +// private: +// void init(); +// QString sectionText( int sec ); +// QTimeEditPrivate* d; +// +// #if defined(Q_DISABLE_COPY) +// QTimeEdit( const QTimeEdit & ); +// QTimeEdit &operator=( const QTimeEdit & ); +// #endif +// }; +// + +class ExtDateTimeEditPrivate; + +class KDE_EXPORT ExtDateTimeEdit : public QWidget +{ + Q_OBJECT +// Q_PROPERTY( ExtDateTime dateTime READ dateTime WRITE setDateTime ) + +public: + ExtDateTimeEdit( QWidget* parent=0, const char* name=0 ); + ExtDateTimeEdit( const ExtDateTime& datetime, QWidget* parent=0, + const char* name=0 ); + ~ExtDateTimeEdit(); + + QSize sizeHint() const; + QSize minimumSizeHint() const; + +public slots: + virtual void setDateTime( const ExtDateTime & dt ); + +public: + ExtDateTime dateTime() const; + + ExtDateEdit* dateEdit() { return de; } + QTimeEdit* timeEdit() { return te; } + + virtual void setAutoAdvance( bool advance ); + bool autoAdvance() const; + +signals: + void valueChanged( const ExtDateTime& datetime ); + +protected: + // ### make init() private in Qt 4.0 + void init(); + void resizeEvent( QResizeEvent * ); + +protected slots: + // ### make these two functions private in Qt 4.0, + // and merge them into one with no parameter + void newValue( const ExtDate& d ); + void newValue( const QTime& t ); + +private: + ExtDateEdit* de; + QTimeEdit* te; + ExtDateTimeEditPrivate* d; + +#if defined(Q_DISABLE_COPY) + ExtDateTimeEdit( const ExtDateTimeEdit & ); + ExtDateTimeEdit &operator=( const ExtDateTimeEdit & ); +#endif +}; + +class QNumberSection +{ +public: + QNumberSection( int selStart = 0, int selEnd = 0, bool separat = TRUE, int actual = -1 ) + : selstart( selStart ), selend( selEnd ), act( actual ), sep( separat ) + {} + int selectionStart() const { return selstart; } + void setSelectionStart( int s ) { selstart = s; } + int selectionEnd() const { return selend; } + void setSelectionEnd( int s ) { selend = s; } + int width() const { return selend - selstart; } + int index() const { return act; } + bool separator() const { return sep; } + Q_DUMMY_COMPARISON_OPERATOR( QNumberSection ) +private: + int selstart :12; + int selend :12; + int act :7; + bool sep :1; +}; + +class ExtDateTimeEditorPrivate; + +class ExtDateTimeEditor : public QWidget +{ + Q_OBJECT +public: + ExtDateTimeEditor( ExtDateTimeEditBase * parent=0, + const char * name=0 ); + ~ExtDateTimeEditor(); + +// void setControlWidget( ExtDateTimeEditBase * widget ); +// ExtDateTimeEditBase * controlWidget() const; + + void setSeparator( const QString& s ); + QString separator() const; + + int focusSection() const; + bool setFocusSection( int s ); + void appendSection( const QNumberSection& sec ); + void clearSections(); + void setSectionSelection( int sec, int selstart, int selend ); + bool eventFilter( QObject *o, QEvent *e ); + int sectionAt( const QPoint &p ); + int mapSection( int sec ); + +protected: + void init(); + bool event( QEvent *e ); + void resizeEvent( QResizeEvent * ); + void paintEvent( QPaintEvent * ); + void mousePressEvent( QMouseEvent *e ); + +private: + ExtDateTimeEditBase* cw; + ExtDateTimeEditorPrivate* d; +}; + +#endif +#endif diff --git a/libkdeedu/extdate/extdatewidget.cpp b/libkdeedu/extdate/extdatewidget.cpp new file mode 100644 index 00000000..6a3fcbf5 --- /dev/null +++ b/libkdeedu/extdate/extdatewidget.cpp @@ -0,0 +1,177 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001 Waldo Bastian (bastian@kde.org) + + Modified to use ExtDate instead of QDate. Modifications + Copyright (C) 2004 Jason Harris (jharris@30doradus.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +#include <qcombobox.h> +#include <qlayout.h> +#include <qlineedit.h> + +#include <knuminput.h> +#include <kdialog.h> + +#include "extdatewidget.h" +#include "extcalendarsystemgregorian.h" + +class ExtDateWidgetSpinBox : public QSpinBox +{ +public: + ExtDateWidgetSpinBox(int min, int max, QWidget *parent) + : QSpinBox(min, max, 1, parent) + { + editor()->setAlignment(AlignRight); + } +}; + +class ExtDateWidget::ExtDateWidgetPrivate +{ +public: + ExtDateWidgetPrivate() { calendar = new ExtCalendarSystemGregorian(); } + ~ExtDateWidgetPrivate() { delete calendar; } + ExtDateWidgetSpinBox *m_day; + QComboBox *m_month; + ExtDateWidgetSpinBox *m_year; + ExtDate m_dat; + ExtCalendarSystemGregorian *calendar; +}; + + +ExtDateWidget::ExtDateWidget( QWidget *parent, const char *name ) + : QWidget( parent, name ) +{ + init(ExtDate::currentDate()); + setDate(ExtDate()); +} + +ExtDateWidget::ExtDateWidget( const ExtDate &date, QWidget *parent, + const char *name ) + : QWidget( parent, name ) +{ + init(date); + setDate(date); +} + +// // ### CFM Repaced by init(const ExtDate&). Can be safely removed +// // when no risk of BIC +// void ExtDateWidget::init() +// { +// d = new ExtDateWidgetPrivate; +// KLocale *locale = KGlobal::locale(); +// QHBoxLayout *layout = new QHBoxLayout(this, 0, KDialog::spacingHint()); +// layout->setAutoAdd(true); +// d->m_day = new ExtDateWidgetSpinBox(1, 1, this); +// d->m_month = new QComboBox(false, this); +// for (int i = 1; ; ++i) +// { +// QString str = d->calendar->monthName(i, +// d->calendar->year(ExtDate())); +// if (str.isNull()) break; +// d->m_month->insertItem(str); +// } +// +// d->m_year = new ExtDateWidgetSpinBox(d->calendar->minValidYear(), +// d->calendar->maxValidYear(), this); +// +// connect(d->m_day, SIGNAL(valueChanged(int)), this, SLOT(slotDateChanged())); +// connect(d->m_month, SIGNAL(activated(int)), this, SLOT(slotDateChanged())); +// connect(d->m_year, SIGNAL(valueChanged(int)), this, SLOT(slotDateChanged())); +// } + +void ExtDateWidget::init(const ExtDate& date) +{ + d = new ExtDateWidgetPrivate; + //KLocale *locale = KGlobal::locale(); + QHBoxLayout *layout = new QHBoxLayout(this, 0, KDialog::spacingHint()); + layout->setAutoAdd(true); + d->m_day = new ExtDateWidgetSpinBox(1, 1, this); + d->m_month = new QComboBox(false, this); + for (int i = 1; ; ++i) + { + QString str = d->calendar->monthName(i, + d->calendar->year(date)); + if (str.isNull()) break; + d->m_month->insertItem(str); + } + + d->m_year = new ExtDateWidgetSpinBox(d->calendar->minValidYear(), + d->calendar->maxValidYear(), this); + + connect(d->m_day, SIGNAL(valueChanged(int)), this, SLOT(slotDateChanged())); + connect(d->m_month, SIGNAL(activated(int)), this, SLOT(slotDateChanged())); + connect(d->m_year, SIGNAL(valueChanged(int)), this, SLOT(slotDateChanged())); +} + +ExtDateWidget::~ExtDateWidget() +{ + delete d; +} + +void ExtDateWidget::setDate( const ExtDate &date ) +{ +// const KCalendarSystem * calendar = KGlobal::locale()->calendar(); + + d->m_day->blockSignals(true); + d->m_month->blockSignals(true); + d->m_year->blockSignals(true); + + d->m_day->setMaxValue(d->calendar->daysInMonth(date)); + d->m_day->setValue(d->calendar->day(date)); + d->m_month->setCurrentItem(d->calendar->month(date)-1); + d->m_year->setValue(d->calendar->year(date)); + + d->m_day->blockSignals(false); + d->m_month->blockSignals(false); + d->m_year->blockSignals(false); + + d->m_dat = date; + emit changed(d->m_dat); +} + +ExtDate ExtDateWidget::date() const +{ + return d->m_dat; +} + +void ExtDateWidget::slotDateChanged( ) +{ +// const KCalendarSystem * calendar = KGlobal::locale()->calendar(); + + ExtDate date; + int y,m,day; + + y = d->m_year->value(); + y = QMIN(QMAX(y, d->calendar->minValidYear()), d->calendar->maxValidYear()); + + d->calendar->setYMD(date, y, 1, 1); + m = d->m_month->currentItem()+1; + m = QMIN(QMAX(m,1), d->calendar->monthsInYear(date)); + + d->calendar->setYMD(date, y, m, 1); + day = d->m_day->value(); + day = QMIN(QMAX(day,1), d->calendar->daysInMonth(date)); + + d->calendar->setYMD(date, y, m, day); + setDate(date); +} + +void ExtDateWidget::virtual_hook( int, void* ) +{ /*BASE::virtual_hook( id, data );*/ } + +#include "extdatewidget.moc" diff --git a/libkdeedu/extdate/extdatewidget.h b/libkdeedu/extdate/extdatewidget.h new file mode 100644 index 00000000..66bec871 --- /dev/null +++ b/libkdeedu/extdate/extdatewidget.h @@ -0,0 +1,89 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001 Waldo Bastian (bastian@kde.org) + + Modified to use ExtDate instead of QDate. Modifications + Copyright (C) 2004 Jason Harris (jharris@30doradus.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __EXTDATEWIDGET_H__ +#define __EXTDATEWIDGET_H__ + +#include "extdatetime.h" + +/** +* This widget can be used to display or allow user selection of a date. +* +* @see ExtDatePicker +* +* @short A pushbutton to display or allow user selection of a date. +* @version $Id$ +*/ +class KDE_EXPORT ExtDateWidget : public QWidget +{ + Q_OBJECT +// Q_PROPERTY( ExtDate date READ date WRITE setDate ) + +public: + /** + * Constructs a date selection widget, initialized to the current CPU date. + */ + ExtDateWidget( QWidget *parent=0, const char *name=0 ); + + /** + * Constructs a date selection widget with the initial date set to @p date. + */ + ExtDateWidget( const ExtDate &date, QWidget *parent=0, const char *name=0 ); + + /** + * Destructs the date selection widget. + */ + virtual ~ExtDateWidget(); + + /** + * Returns the currently selected date. + */ + ExtDate date() const; + + /** + * Changes the selected date to @p date. + */ + void setDate(const ExtDate &date); + + +signals: + /** + * Emitted whenever the date of the widget + * is changed, either with setDate() or via user selection. + */ + void changed(ExtDate); + +protected: + void init(); + void init(const ExtDate&); + +protected slots: + void slotDateChanged(); + +protected: + virtual void virtual_hook( int id, void* data ); +private: + class ExtDateWidgetPrivate; + ExtDateWidgetPrivate *d; +}; + +#endif + diff --git a/libkdeedu/extdate/main.cpp b/libkdeedu/extdate/main.cpp new file mode 100644 index 00000000..f71a6acd --- /dev/null +++ b/libkdeedu/extdate/main.cpp @@ -0,0 +1,30 @@ +#include "testwidget.h" +#include <kapplication.h> +#include <kcmdlineargs.h> +#include <kaboutdata.h> + +static const char description[] = I18N_NOOP("ExtDatePicker test program"); +static const char notice[] = I18N_NOOP("Compares KDatePicker and ExtDatePicker"); + +static KCmdLineOptions options[] = +{ + KCmdLineLastOption +}; + +int main( int argc, char *argv[] ) +{ + KAboutData aboutData( "test_extdatepicker", I18N_NOOP("Test ExtDatePicker"), + "0.1", description, KAboutData::License_GPL, + I18N_NOOP("(c) 2004, Jason Harris"), notice, + "http://30doradus.org"); + aboutData.addAuthor("Jason Harris", 0, + "jharris@30doradus.org", "http://www.30doradus.org"); + + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication a; + TestWidget *t = new TestWidget(0,0); + t->show(); + QObject::connect(kapp, SIGNAL(lastWindowClosed()), kapp, SLOT(quit())); + return a.exec(); +} diff --git a/libkdeedu/extdate/test_extdate.cc b/libkdeedu/extdate/test_extdate.cc new file mode 100644 index 00000000..b21e228b --- /dev/null +++ b/libkdeedu/extdate/test_extdate.cc @@ -0,0 +1,334 @@ +#include <stdlib.h> +#include <iostream> +#include "extdatetime.h" + + +void test1_unit(int a_year) +{ + std::cout << a_year << " (QDate|ExtDate): " << ((QDate::leapYear(a_year)) ? "yes" : "no") + <<"|"<< ((ExtDate::leapYear(a_year)) ? "yes" : "no") << std::endl; +} + +void test1() +{ + int a_set_of_years[] = + { + 1996, + 1997, + 1998, + 2000, + 1999, + 2001, + 1900, + 1800, + 1700, + 1600, + 2100, + 2200, + 2300, + 0, + -1, + -4, + -100, + -200, + -300, + -400, + -500 + }; + uint i; + std::cout << "Checking Leap Years:\n" << std::endl; + for (i = 0 ; i < sizeof(a_set_of_years)/sizeof(a_set_of_years[0]) ; i++) + { + test1_unit(a_set_of_years[i]); + } + std::cout << "--------------------" << std::endl; +} + +void test2_unit(int y, int m, int d) +{ + QDate q(y, m, d); + ExtDate e(y, m, d); + int q_week_number = q.dayOfWeek(); + int e_week_number = e.dayOfWeek(); + int q_day_of_year = q.dayOfYear(); + int e_day_of_year = e.dayOfYear(); + std::cout << "(" << y << ", " << m << ", " << d << ") :: " + << q.toString("dd.MMM.yyyy").local8Bit() << " : " + << q.dayOfWeek() << " : " << q_week_number << " : " << q_day_of_year << " :: " + << e.toString("%d.%b.%Y").local8Bit() << " : " + << e.dayOfWeek() << " : " << e_week_number << " : " << e_day_of_year << std::endl; +} + +void test2() +{ + int a_set_of_dates[][3] = + { + {0, 1, 1}, + {1, 1, 1}, + {2, 1, 1}, + {3, 1, 1}, + {4, 1, 1}, + {5, 1, 1}, + {99, 1, 1}, + {100, 1, 1}, + {100, 12, 31}, + {101, 1, 1}, + {102, 1, 1}, + {103, 1, 1}, + {104, 1, 1}, + {399, 1, 1}, + {400, 1, 1}, + {401, 1, 1}, + {402, 1, 1}, + {403, 1, 1}, + {404, 1, 1}, + {2003, 1, 1}, + {2003, 1, 2}, + {2003, 1, 3}, + {2003, 1, 4}, + {2003, 1, 5}, + {2003, 1, 6}, + {2003, 1, 7}, + {2003, 1, 8}, + {2003, 1, 9}, + {2003, 1, 10}, + {2003, 1, 11}, + {2003, 1, 12}, + {2003, 1, 13}, + {2003, 1, 14}, + {2003, 1, 15}, + {2003, 1, 16}, + {2003, 1, 17}, + {2003, 1, 18}, + {2003, 12, 19}, + {2003, 12, 20}, + {2003, 12, 21}, + {2003, 12, 22}, + {2003, 12, 23}, + {2003, 12, 24}, + {2003, 12, 25}, + {2003, 12, 26}, + {2003, 12, 27}, + {2003, 12, 28}, + {2003, 12, 29}, + {2003, 12, 30}, + {2003, 12, 31}, + {2004, 1, 1}, + {2003, 11, 2} + }; + uint i; + std::cout << "(y, m, d) :: QDate : Q.dayOfWeek() : Q.weekNumber() : Q.dayOfYear() :: ExtDate : E.dayOfWeek() : E.weekNumber() : E.dayOfYear()\n" << std::endl; + + for (i = 0 ; i < sizeof(a_set_of_dates)/sizeof(a_set_of_dates[0]) ; i++) + { + test2_unit(a_set_of_dates[i][0], a_set_of_dates[i][1], a_set_of_dates[i][2]); + } + std::cout << "--------------------" << std::endl; +} + +void test3_unit(int y, int m, int d, int dm) +{ + QDate q(y, m, d); + ExtDate e(y, m, d); + QDate q2 = q.addMonths(dm); + ExtDate e2 = e.addMonths(dm); + std::cout << e.toString("%d.%b.%Y").local8Bit() << " + " << dm + << " months :: ExtDate : " << e2.toString("%d.%b.%Y").local8Bit() + << " QDate : " << q2.toString("dd.MMM.yyyy").local8Bit() << std::endl; +} + +void test3() +{ + int a_set_of_dates_delta[][4] = + { + {2003, 11, 5, 0}, + {2003, 11, 5, 1}, + {2003, 11, 5, -1}, + {2003, 11, 5, 2}, + {2003, 11, 5, 3} + }; + uint i; + std::cout << "Adding Months:\n" << std::endl; + for (i = 0 ; i < sizeof(a_set_of_dates_delta)/sizeof(a_set_of_dates_delta[0]) ; i++) + { + test3_unit(a_set_of_dates_delta[i][0], a_set_of_dates_delta[i][1], a_set_of_dates_delta[i][2], a_set_of_dates_delta[i][3]); + } + std::cout << "--------------------" << std::endl; +} +void test4_unit(int y, int m, int d, int dy) +{ + QDate q(y, m, d); + ExtDate e(y, m, d); + QDate q2 = q.addYears(dy); + ExtDate e2 = e.addYears(dy); + std::cout << e.toString("%d.%m.%Y").local8Bit() << " + " << dy << " years :: ExtDate : " + << e2.toString().local8Bit() << " QDate : " + << q2.toString().local8Bit() << std::endl; +} + +void test4() +{ + int a_set_of_dates_delta[][4] = + { + {-1, 11, 5, 0}, + {-1, 11, 5, 1}, + {-1, 11, 5, 2}, + {2003, 11, 5, 1}, + {2003, 11, 5, -1}, + {2003, 11, 5, 2}, + {2003, 11, 5, 3} + }; + uint i; + std::cout << "Adding years:\n" << std::endl; + for (i = 0 ; i < sizeof(a_set_of_dates_delta)/sizeof(a_set_of_dates_delta[0]) ; i++) + { + test4_unit(a_set_of_dates_delta[i][0], a_set_of_dates_delta[i][1], a_set_of_dates_delta[i][2], a_set_of_dates_delta[i][3]); + } + std::cout << "--------------------" << std::endl; +} + +void test5_unit(int y, int m, int d, const char *qformat, const char *eformat) +{ + QDate q(y, m, d); + ExtDate e(y, m, d); + + if ( QString(qformat) == "<default>" ) + std::cout << eformat << " : " << e.toString().local8Bit() << " :: " + << qformat << " : " << q.toString().local8Bit() << std::endl; + else + std::cout << eformat << " : " << e.toString(eformat).local8Bit() << " :: " + << qformat << " : " << q.toString(qformat).local8Bit() << std::endl; +} + +void test5() +{ + const char *q_set_of_formats[7] = + { + "d.M.yy", + "dd.MM.yy", + "ddd.MMM.yy", + "ddd dd.MMM.yy", + "dddd.MMMM.yy", + ">dd.M.yyyy<", + "<default>" + }; + const char *e_set_of_formats[7] = + { + "%e.%n.%y", + "%d.%m.%y", + "%d.%b.%y", + "%a %d.%b.%y", + "%A.%B.%y", + ">%d.%n.%Y<", + "<default>" + }; + + uint i; + std::cout << "Date.toString(\"...\")" << std::endl; + std::cout << "Ext Format : ExtDate :: Q Format : QDate\n" << std::endl; + + for (i = 0 ; i < sizeof(q_set_of_formats)/sizeof(q_set_of_formats[0]) ; i++) + { + test5_unit(2003, 11, 5, q_set_of_formats[i], e_set_of_formats[i]); + } + std::cout << "--------------------" << std::endl; +} + +void test6_unit(int y, int m, int d) +{ + std::cout << d << "/" << m << "/" << y << " :: " + << ( ExtDate::isValid(y, m, d) ? "TRUE" : "FALSE" ) << " : " + << ( QDate::isValid(y, m, d) ? "TRUE" : "FALSE" ) << std::endl; +} + +void test6() +{ + int a_set_of_dates[][3] = + { + {-1, 11, 5}, + {-1, 11, 5}, + {-1, 11, 5}, + {2003, 11, 5}, + {2003, -1, 5}, + {2003, 0, 5}, + {2003, 12, 5}, + {2003, 13, 5}, + {2003, 11, -2}, + {2003, 11, 0}, + {2003, 11, 40}, + {2004, 2, 28}, + {2004, 2, 29}, + {2004, 2, 30}, + {2003, 2, 28}, + {2003, 2, 29}, + {2003, 2, 30} + }; + uint i; + std::cout << "Date.isValid()" << std::endl; + std::cout << "d/m/y :: ExtDate.isValid() : QDate.isValid()\n" << std::endl; + + for (i = 0 ; i < sizeof(a_set_of_dates)/sizeof(a_set_of_dates[0]) ; i++) + { + test6_unit(a_set_of_dates[i][0], a_set_of_dates[i][1], a_set_of_dates[i][2]); + } + std::cout << "--------------------" << std::endl; +} + +void test7() +{ + std::cout << "Express the current date:\n" << std::endl; + QDate q = QDate::currentDate(Qt::LocalTime); + ExtDate e = ExtDate::currentDate(Qt::TimeSpec(Qt::LocalTime)); + std::cout << "Qt::LocalTime :: ExtDate : " << e.toString().local8Bit() << " QDate : " + << q.toString().local8Bit() << std::endl; + q = QDate::currentDate(Qt::UTC); + e = ExtDate::currentDate(Qt::UTC); + std::cout << "Qt::UTC :: ExtDate : " << e.toString().local8Bit() << " QDate : " + << q.toString().local8Bit() << std::endl; + q = QDate::currentDate(); + e = ExtDate::currentDate(); + std::cout << "<default> :: ExtDate : " << e.toString().local8Bit() << " QDate : " + << q.toString().local8Bit() << std::endl; + std::cout << "--------------------" << std::endl; +} + +void test8() { + std::cout << "Set dates using days 1-32 for Jan and Feb (some will be invalid): \n" << std::endl; + std::cout << " QDate : ExtDate" << std::endl; + + for ( uint m=1; m<=2; ++m ) { + for ( uint d=1; d<=32; ++d ) { + + QDate test1( 2004, m, d ); + ExtDate test2( 2004, m, d ); + std::cout << test1.toString( "ddd dd.MMM.yy" ).local8Bit() << " : " + << test2.toString( "%a %d.%b.%y" ).local8Bit() << std::endl; + } + } + + std::cout << "--------------------" << std::endl; +} + +void test9() { + std::cout << "QDateTime : ExtDateTime: \n" << std::endl; + QDateTime q = QDateTime::currentDateTime(); + ExtDateTime e = ExtDateTime::currentDateTime(); + + std::cout << q.toString().local8Bit() << " : " << e.toString().local8Bit() << std::endl; + std::cout << "--------------------" << std::endl; +} + +int main(int argc, char *argv[]) +{ + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + test7(); + test8(); + test9(); + exit(0); +} + diff --git a/libkdeedu/extdate/testwidget.cpp b/libkdeedu/extdate/testwidget.cpp new file mode 100644 index 00000000..4dcd91c8 --- /dev/null +++ b/libkdeedu/extdate/testwidget.cpp @@ -0,0 +1,68 @@ +/*************************************************************************** + testwidget.h - description + ------------------- + begin : Sun Apr 11 2004 + copyright : (C) 2004 by Jason Harris + email : kstars@30doradus.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. * + * * + ***************************************************************************/ + +#include <kdatepicker.h> +#include <kdatewidget.h> +#include <klineedit.h> +#include <qlayout.h> +#include <qlabel.h> + +#include "extdatepicker.h" +#include "extdatewidget.h" +#include "testwidget.h" + +TestWidget::TestWidget( QWidget *p=0, const char *name=0 ) : KMainWindow( p, name ) { + QWidget *w = new QWidget(this); + + glay = new QGridLayout(w, 3, 2); + + QLabel *kdpLabel = new QLabel( QString("KDatePicker"), w ); + QLabel *edpLabel = new QLabel( QString("ExtDatePicker"), w ); + kdp = new KDatePicker(w); + edp = new ExtDatePicker(w); + kdpEdit = new KLineEdit(w); + kdpEdit->setReadOnly( TRUE ); + edpEdit = new KLineEdit(w); + edpEdit->setReadOnly( TRUE ); + + kdw = new KDateWidget( QDate::currentDate(), w ); + edw = new ExtDateWidget( ExtDate::currentDate(), w ); + + glay->addWidget( kdpLabel, 0, 0 ); + glay->addWidget( edpLabel, 0, 1 ); + glay->addWidget( kdp, 1, 0 ); + glay->addWidget( edp, 1, 1 ); + glay->addWidget( kdpEdit, 2, 0 ); + glay->addWidget( edpEdit, 2, 1 ); + glay->addWidget( kdw, 3, 0 ); + glay->addWidget( edw, 3, 1 ); + + setCentralWidget(w); + + connect( kdp, SIGNAL( dateChanged(QDate) ), this, SLOT( slotKDateChanged(QDate) ) ); + connect( edp, SIGNAL( dateChanged(const ExtDate&) ), this, SLOT( slotExtDateChanged(const ExtDate&) ) ); +} + +void TestWidget::slotKDateChanged(QDate d) { + kdpEdit->setText( d.toString() ); +} + +void TestWidget::slotExtDateChanged(const ExtDate &d) { + edpEdit->setText( d.toString() ); +} + +#include "testwidget.moc" diff --git a/libkdeedu/extdate/testwidget.h b/libkdeedu/extdate/testwidget.h new file mode 100644 index 00000000..ef69f8b6 --- /dev/null +++ b/libkdeedu/extdate/testwidget.h @@ -0,0 +1,51 @@ +/*************************************************************************** + testwidget.cpp - description + ------------------- + begin : Sun Apr 11 2004 + copyright : (C) 2004 by Jason Harris + email : kstars@30doradus.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. * + * * + ***************************************************************************/ + +#ifndef TESTWIDGET_H +#define TESTWIDGET_H + +#include <kmainwindow.h> + +class KDatePicker; +class KLineEdit; +class KDateWidget; +class ExtDatePicker; +class ExtDateWidget; +class QGridLayout; +class QDate; +class ExtDate; + +class TestWidget : public KMainWindow { + Q_OBJECT + public: + TestWidget( QWidget *parent, const char *name ); + ~TestWidget() {} + + public slots: + void slotKDateChanged(QDate); + void slotExtDateChanged(const ExtDate&); + + private: + QGridLayout *glay; + KDatePicker *kdp; + ExtDatePicker *edp; + ExtDateWidget *edw; + KDateWidget *kdw; + KLineEdit *kdpEdit, *edpEdit; +}; + +#endif //ifndef TESTWIDGET_H |