27#include <QDialogButtonBox>
34#include "moc_qgsdatetimewithtimezonewidget.cpp"
36using namespace Qt::StringLiterals;
42std::vector< QgsTimeZoneItemModel::TimeZoneItem > QgsTimeZoneItemModel::sItems;
45 : QAbstractItemModel(
parent )
47 static std::once_flag initialized;
48 std::call_once( initialized, [] {
49 const QDateTime now = QDateTime::currentDateTime();
50 const QList<QByteArray> zones = QTimeZone::availableTimeZoneIds();
51 sItems.reserve( zones.size() );
52 for (
const QByteArray &zoneId : std::as_const( zones ) )
54 const QTimeZone timeZone( zoneId );
55 if ( !timeZone.isValid() )
58 const int offset = timeZone.offsetFromUtc( now );
61 TimeZoneItem item { zoneId, displayString, timeZone, offset };
62 sItems.emplace_back( item );
72 return static_cast< int>( sItems.size() );
82 if ( !
index.isValid() )
85 if (
index.row() < 0 ||
index.row() >=
static_cast< int >( sItems.size() ) )
88 const TimeZoneItem &item = sItems[
index.row()];
93 return item.displayString;
99 return QVariant::fromValue( item.timeZone );
102 return item.utcOffset;
115 return QModelIndex();
120 return createIndex( row, column,
nullptr );
123 return QModelIndex();
129 return QModelIndex();
134 if ( !timeZone.isValid() )
135 return QModelIndex();
136 const QString
id = timeZone.id();
137 const QModelIndex startIndex =
index( 0, 0 );
139 return matches.isEmpty() ? QModelIndex() : matches.at( 0 );
144 const QDateTime now = QDateTime::currentDateTime();
145 const int offset = timeZone.offsetFromUtc( now );
147 return u
"%1: %2 (%3)"_s.arg( offsetToString( offset ), QString( timeZone.id() ), timeZone.displayName( now, nameType ) );
150QString QgsTimeZoneItemModel::offsetToString(
int seconds )
152 const int offsetInMinutes = std::abs( seconds ) / 60;
153 const int hours = offsetInMinutes / 60;
154 const int minutes = offsetInMinutes % 60;
155 return tr(
"UTC%1%2:%3" ).arg( seconds >= 0 ?
'+' :
'-' ).arg( hours, 2, 10,
'0' ).arg( minutes, 2, 10,
'0' );
163 : QSortFilterProxyModel( parent )
164 , mSourceModel( source )
166 setSourceModel( mSourceModel );
167 setDynamicSortFilter(
true );
173 return mapFromSource( mSourceModel->indexFromTimeZone( timeZone ) );
178 mFilterString = filter;
184 const QString itemText = sourceModel()->data( sourceModel()->index( sourceRow, 0, sourceParent ) ).toString();
186 if ( !mFilterString.trimmed().isEmpty() )
188 if ( !itemText.contains( mFilterString, Qt::CaseInsensitive ) )
199 if ( leftOffset != rightOffset )
201 return leftOffset < rightOffset;
204 const QString leftText = sourceModel()->data( left, Qt::DisplayRole ).toString();
205 const QString rightText = sourceModel()->data( right, Qt::DisplayRole ).toString();
206 if ( leftText.isEmpty() )
208 if ( rightText.isEmpty() )
211 return QString::localeAwareCompare( leftText, rightText ) < 0;
225 mFilterEdit->setShowClearButton(
true );
226 mFilterEdit->setShowSearchIcon(
true );
227 mFilterEdit->setPlaceholderText(
"Search…" );
229 mListView =
new QListView();
230 mListView->setModel( mProxyModel );
231 mListView->setUniformItemSizes(
true );
233 auto layout =
new QVBoxLayout();
234 layout->setContentsMargins( 0, 0, 0, 0 );
235 layout->addWidget( mFilterEdit );
236 layout->addWidget( mListView, 1 );
241 connect( mListView->selectionModel(), &QItemSelectionModel::selectionChanged,
this, [
this] { emit timeZoneChanged( timeZone() ); } );
243 connect( mListView, &QListView::doubleClicked,
this, &QgsTimeZoneWidget::itemDoubleClicked );
248 const QModelIndexList list = mListView->selectionModel()->selectedIndexes().toList();
249 if ( list.isEmpty() )
252 const QModelIndex currentIndex = list.at( 0 );
253 if ( !currentIndex.isValid() )
261 const QModelIndex index = mProxyModel->indexFromTimeZone( zone );
262 if ( !index.isValid() )
264 mListView->clearSelection();
268 mListView->selectionModel()->select( index, QItemSelectionModel::SelectionFlag::ClearAndSelect );
269 mListView->scrollTo( index );
273void QgsTimeZoneWidget::itemDoubleClicked(
const QModelIndex &index )
284StandardTimeZoneModel::StandardTimeZoneModel( QObject *parent )
285 : QAbstractItemModel( parent )
288QVariant StandardTimeZoneModel::data(
const QModelIndex &index,
int role )
const
290 if ( !index.isValid() )
293 if ( index.row() < 0 || index.row() >= rowCount() )
299 case Qt::DisplayRole:
300 case Qt::ToolTipRole:
304 return tr(
"Not set" );
314 case static_cast< int >( StandardTimeZoneModel::CustomRole::TimeZone ):
318 return QVariant::fromValue( QTimeZone() );
321 return QVariant::fromValue( mCurrentTimeZone );
324 return QVariant::fromValue( QTimeZone::systemTimeZone() );
335int StandardTimeZoneModel::rowCount(
const QModelIndex &parent )
const
337 if ( parent.isValid() )
343int StandardTimeZoneModel::columnCount(
const QModelIndex & )
const
348QModelIndex StandardTimeZoneModel::index(
int row,
int column,
const QModelIndex &parent )
const
350 if ( row < 0 || row >= rowCount() || column != 0 || parent.isValid() )
351 return QModelIndex();
353 return createIndex( row, column );
356QModelIndex StandardTimeZoneModel::parent(
const QModelIndex & )
const
358 return QModelIndex();
363 switch ( index.row() )
384 return index( 0, 0, QModelIndex() );
386 return index( 1, 0, QModelIndex() );
388 return index( 2, 0, QModelIndex() );
393void StandardTimeZoneModel::setCurrentTimeZone(
const QTimeZone &timeZone )
395 mCurrentTimeZone = timeZone;
396 emit dataChanged( index( 1, 0, QModelIndex() ), index( 1, 0, QModelIndex() ) );
402StandardTimeZoneProxyModel::StandardTimeZoneProxyModel( QObject *parent )
403 : QSortFilterProxyModel( parent )
405 mModel =
new StandardTimeZoneModel(
this );
408 setSourceModel( mModel );
409 setDynamicSortFilter(
true );
418void StandardTimeZoneProxyModel::setCurrentTimeZone(
const QTimeZone &timeZone )
420 mModel->setCurrentTimeZone( timeZone );
427 return mapFromSource( mModel->indexForItem( item ) );
430bool StandardTimeZoneProxyModel::filterAcceptsRow(
int sourceRow,
const QModelIndex &sourceParent )
const
433 return mItems.testFlag( item );
445 , mDialogTitle( tr(
"Time Zone Selector" ) )
447 mComboBox =
new QComboBox();
448 mTimeZoneModel =
new StandardTimeZoneProxyModel(
this );
449 mComboBox->setModel( mTimeZoneModel );
451 QHBoxLayout *layout =
new QHBoxLayout();
452 layout->setContentsMargins( 0, 0, 0, 0 );
454 layout->addWidget( mComboBox, 1 );
456 mButton =
new QToolButton(
this );
458 mButton->setToolTip( tr(
"Select Time Zone" ) );
459 layout->addWidget( mButton );
461 setFocusPolicy( Qt::StrongFocus );
462 setFocusProxy( mButton );
466 connect( mComboBox, qOverload< int >( &QComboBox::currentIndexChanged ),
this, [
this] {
467 if ( !mBlockSignals )
473 connect( mButton, &QToolButton::clicked,
this, &QgsTimeZoneComboBox::selectTimeZone );
478 return mTimeZoneModel->data( mTimeZoneModel->index( mComboBox->currentIndex(), 0 ),
static_cast< int >( StandardTimeZoneModel::CustomRole::TimeZone ) ).value< QTimeZone >();
483 mDialogTitle = title;
493 const QTimeZone oldTimeZone =
timeZone();
495 mTimeZoneModel->setCurrentTimeZone( zone );
496 if ( zone.isValid() )
511void QgsTimeZoneComboBox::selectTimeZone()
518 mActivePanel->setTimeZone(
timeZone() );
519 mActivePanel->setPanelTitle( mDialogTitle );
522 if ( mIgnorePanelSignals )
525 const QTimeZone
timeZone = mActivePanel->timeZone();
527 mIgnorePanelSignals++;
529 mIgnorePanelSignals--;
537 QgsTimeZoneDialog dlg(
this, QgsGuiUtils::ModalDialogFlags );
538 dlg.setWindowTitle( mDialogTitle );
552 : QDialog( parent, fl )
554 QVBoxLayout *vlayout =
new QVBoxLayout();
557 vlayout->addWidget( mWidget, 1 );
559 mButtonBox =
new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Ok );
560 connect( mButtonBox, &QDialogButtonBox::accepted,
this, &QgsTimeZoneDialog::accept );
561 connect( mButtonBox, &QDialogButtonBox::rejected,
this, &QgsTimeZoneDialog::reject );
563 vlayout->addWidget( mButtonBox );
565 setLayout( vlayout );
575 return mWidget->timeZone();
580 mWidget->setTimeZone( zone );
590 auto layout =
new QHBoxLayout();
591 layout->setContentsMargins( 0, 0, 0, 0 );
593 layout->addWidget( mDateTimeEdit, 1 );
595 layout->addWidget( mTimeZoneCombo, 2 );
605 connect( mDateTimeEdit, &QgsDateTimeEdit::dateTimeChanged,
this, [
this] {
615 return mDateTimeEdit;
620 return mTimeZoneCombo;
630 return mTimeZoneCombo->dialogTitle();
635 QDateTime res = mDateTimeEdit->dateTime();
636 res.setTimeZone( mTimeZoneCombo->timeZone() );
644 mDateTimeEdit->setDateTime(
dateTime );
645 mTimeZoneCombo->setTimeZone(
dateTime.timeZone() );
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
A QDateTimeEdit with the capability of setting/reading null date/times.
QLineEdit subclass with built in support for clearing the widget's value and handling custom null val...
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
A combo box widget for selection of available time zones.
void setTimeZone(const QTimeZone &zone)
Sets the current time zone to select in the widget.
QString dialogTitle() const
Returns the title for the time zone selector dialog window.
void timeZoneChanged(const QTimeZone &zone)
Emitted when the selected time zone is changed.
QFlags< StandardItem > StandardItems
Standard time zone options.
QgsTimeZoneComboBox(QWidget *parent=nullptr)
Constructor for QgsTimeZoneComboBox, with the specified parent widget.
QTimeZone timeZone() const
Returns the selected time zone.
void setDialogTitle(const QString &title)
Sets the title for the time zone selector dialog window.
StandardItem
Standard time zone options.
@ Current
Current time zone value.
@ Invalid
Invalid/not set option.
@ System
System time zone.
QTimeZone timeZone() const
Returns the selected time zone.
QgsTimeZoneDialog(QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
Constructor for QgsTimeZoneDialog.
void setTimeZone(const QTimeZone &zone)
Sets the current time zone to select in the dialog.
A custom model for display of available time zones.
QVariant data(const QModelIndex &index, int role) const override
static QString displayStringForTimeZone(const QTimeZone &timeZone, QTimeZone::NameType nameType=QTimeZone::NameType::DefaultName)
Returns a friendly display string for a time zone.
QModelIndex indexFromTimeZone(const QTimeZone &timeZone) const
Returns the model index corresponding to a time zone.
int columnCount(const QModelIndex &parent=QModelIndex()) const override
int rowCount(const QModelIndex &parent=QModelIndex()) const override
QgsTimeZoneItemModel(QObject *parent=nullptr)
Constructor for a QgsTimeZoneItemModel with the specified parent object.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
@ TimeZoneId
Time zone ID.
@ UtcOffset
Offset from UTC.
@ TimeZone
QTimeZone object.
QModelIndex parent(const QModelIndex &index) const override
A sort filter model for display of available time zones.
QgsTimeZoneSortFilterModel(QgsTimeZoneItemModel *source, QObject *parent=nullptr)
Constructor for a QgsTimeZoneSortFilterModel with the specified parent object and source model.
void setFilterString(const QString &filter)
Sets a filter string, such that only time zones with names containing the specified string will be sh...
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
QModelIndex indexFromTimeZone(const QTimeZone &timeZone) const
Returns the proxy model index corresponding to a time zone.
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
#define BUILTIN_UNREACHABLE