monthpage.cpp Example File

calendardemo/src/monthpage.cpp

  /****************************************************************************
  **
  ** Copyright (C) 2017 The Qt Company Ltd.
  ** Contact: https://www.qt.io/licensing/
  **
  ** This file is part of the examples for the Qt PIM module of the Qt Toolkit.
  **
  ** $QT_BEGIN_LICENSE:BSD$
  ** Commercial License Usage
  ** Licensees holding valid commercial Qt licenses may use this file in
  ** accordance with the commercial license agreement provided with the
  ** Software or, alternatively, in accordance with the terms contained in
  ** a written agreement between you and The Qt Company. For licensing terms
  ** and conditions see https://www.qt.io/terms-conditions. For further
  ** information use the contact form at https://www.qt.io/contact-us.
  **
  ** BSD License Usage
  ** Alternatively, you may use this file under the terms of the BSD license
  ** as follows:
  **
  ** "Redistribution and use in source and binary forms, with or without
  ** modification, are permitted provided that the following conditions are
  ** met:
  **   * Redistributions of source code must retain the above copyright
  **     notice, this list of conditions and the following disclaimer.
  **   * Redistributions in binary form must reproduce the above copyright
  **     notice, this list of conditions and the following disclaimer in
  **     the documentation and/or other materials provided with the
  **     distribution.
  **   * Neither the name of The Qt Company Ltd nor the names of its
  **     contributors may be used to endorse or promote products derived
  **     from this software without specific prior written permission.
  **
  **
  ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  **
  ** $QT_END_LICENSE$
  **
  ****************************************************************************/

  #include <QLocale>
  #include <QtWidgets>
  #include <QtOrganizer/qorganizer.h>
  #include <QtOrganizer/qorganizerabstractrequest.h>

  #include "monthpage.h"
  #include "calendardemo.h"

  QTORGANIZER_USE_NAMESPACE

  Q_DECLARE_METATYPE(QOrganizerItem)

  MonthPage::MonthPage(QWidget *parent)
      :QWidget(parent),
      m_manager(0),
      m_calendarWidget(0),
      m_dateLabel(0),
      m_itemList(0),
      m_ignoreShowDayPageOnceFlag(false)
  {
      // Create widgets
      QFormLayout *mainlayout = new QFormLayout(this);

      m_managerComboBox = new QComboBox(this);
      foreach (const QString& manager, QOrganizerManager::availableManagers()) {
          if (manager != "invalid" && manager != "skeleton")
              m_managerComboBox->addItem(manager);
      }
      mainlayout->addRow("Backend:", m_managerComboBox);
      connect(m_managerComboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(backendChanged(const QString &)));

      m_dateLabel = new QLabel(this);
      m_dateLabel->setAlignment(Qt::AlignCenter);
      mainlayout->addRow(m_dateLabel);

      m_calendarWidget = new QCalendarWidget(this);
      m_calendarWidget->setGridVisible(true);
      m_calendarWidget->setHorizontalHeaderFormat(QCalendarWidget::SingleLetterDayNames);
      m_calendarWidget->setNavigationBarVisible(false);
      mainlayout->addRow(m_calendarWidget);
      connect(m_calendarWidget, SIGNAL(selectionChanged()), this, SLOT(refreshDayItems()));
      connect(m_calendarWidget, SIGNAL(currentPageChanged(int, int)), this, SLOT(currentMonthChanged()));
      connect(m_calendarWidget, SIGNAL(activated(QDate)), this, SLOT(dayDoubleClicked(QDate)));

      m_itemList = new QListWidget(this);
      m_itemList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
      m_itemList->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
      m_itemList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
      m_itemList->setFocusPolicy(Qt::NoFocus);
      mainlayout->addRow(m_itemList);
      connect(m_itemList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(itemDoubleClicked(QListWidgetItem*)));

      setLayout(mainlayout);
  }

  // This is separate from the constructor so we can do it after connecting the signals
  void MonthPage::init()
  {
      backendChanged(m_managerComboBox->currentText());
      currentMonthChanged();
  }

  MonthPage::~MonthPage()
  {

  }

  void MonthPage::backendChanged(const QString &managerName)
  {
      // Nothing to change
      if (m_manager && m_manager->managerName() == managerName)
          return;

      // Try creating a new manager
      QMap<QString, QString> parameters;
      QOrganizerManager* newManager = new QOrganizerManager(managerName, parameters, this);
      if (!newManager || newManager->error()) {
          QMessageBox::information(this, tr("Failed!"), QString("Failed to create manager"));
          delete newManager;
          m_managerComboBox->setCurrentIndex(m_managerComboBox->findText(m_manager->managerName()));
      } else {
          // Success: Replace the old one
          delete m_manager;
          m_manager = newManager;
          emit managerChanged(m_manager);
          refresh();
      }
  }

  void MonthPage::editItem()
  {
      QListWidgetItem *listItem = m_itemList->currentItem();
      if (!listItem)
          return;

      QOrganizerItem organizerItem = listItem->data(ORGANIZER_ITEM_ROLE).value<QOrganizerItem>();
      if (!organizerItem.isEmpty())
          emit showEditPage(organizerItem);
  }

  void MonthPage::removeItem()
  {
      QListWidgetItem *listItem = m_itemList->currentItem();
      if (!listItem)
          return;

      QOrganizerItem organizerItem = listItem->data(ORGANIZER_ITEM_ROLE).value<QOrganizerItem>();
      if (organizerItem.isEmpty())
          return;

      m_manager->removeItem(organizerItem.id());
      refresh();
  }

  void MonthPage::refresh()
  {
      // fetch all current months items
      QDate start(m_calendarWidget->yearShown(), m_calendarWidget->monthShown(), 1);
      QDate end = start.addDays(start.daysInMonth());
      QDateTime startDateTime(start, QTime(0, 0, 0, 0));
      QDateTime endDateTime(end, QTime(23, 59, 59, 0));
      // Remove all previous formatting
      for (int i=0; i<start.daysInMonth(); i++) {
          QTextCharFormat cf = m_calendarWidget->dateTextFormat(start.addDays(i));
          cf.setFontItalic(false);
          cf.setFontUnderline(false);
          cf.clearBackground();
          m_calendarWidget->setDateTextFormat(start.addDays(i), cf);
      }

      // Underline current date
      QTextCharFormat cf = m_calendarWidget->dateTextFormat(QDate::currentDate());
      cf.setFontUnderline(true);
      m_calendarWidget->setDateTextFormat(QDate::currentDate(), cf);

      // TODO: switch to item instances when theres a backed
      QList<QOrganizerItem> items = m_manager->items(startDateTime, endDateTime);

      // Get dates for all items
      QList<QDate> dates;
      foreach (const QOrganizerItem &item, items)
      {
          QDate startDate;
          QDate endDate;

          QOrganizerEventTime eventTime = item.detail(QOrganizerItemDetail::TypeEventTime);
          if (!eventTime.isEmpty()) {
              startDate = eventTime.startDateTime().date();
              endDate = eventTime.endDateTime().date();
          } else {
              QOrganizerTodoTime todoTime = item.detail(QOrganizerItemDetail::TypeTodoTime);
              if (!todoTime.isEmpty()) {
                  startDate = todoTime.startDateTime().date();
                  endDate = todoTime.dueDateTime().date();
              } else {
                  QOrganizerJournalTime journalTime = item.detail(QOrganizerItemDetail::TypeJournalTime);
                  if (!journalTime.isEmpty())
                      startDate = endDate = journalTime.entryDateTime().date();
              }
          }

          if (!startDate.isNull()) {
              while (startDate <= endDate) {
                  dates << startDate;
                  startDate = startDate.addDays(1);
              }
          }
      }

      // Mark all dates which has events.
      QBrush brush;
      brush.setColor(Qt::green);
      foreach (QDate date, dates) {
          if (date == QDate())
              continue;
          QTextCharFormat cf = m_calendarWidget->dateTextFormat(date);
          cf.setFontItalic(true); // TODO: find a better way to mark dates
          cf.setBackground(brush);
          m_calendarWidget->setDateTextFormat(date, cf);
      }

      // As the day item list is not showed do not refresh
      // the day items to improve performance
      refreshDayItems();
  }

  void MonthPage::refreshDayItems()
  {
      QDate selectedDate = m_calendarWidget->selectedDate();
      emit currentDayChanged(selectedDate);

      QDateTime startOfDay(selectedDate, QTime(0, 0, 0));
      QDateTime endOfDay(selectedDate, QTime(23, 59, 59));
      // Clear items shown
      m_itemList->clear();

      // Find all items for today
      QList<QOrganizerItem> items = m_manager->items(startOfDay, endOfDay);

      foreach (const QOrganizerItem &item, items)
      {
          QOrganizerEventTime eventTime = item.detail(QOrganizerItemDetail::TypeEventTime);
          if (!eventTime.isEmpty()) {
              QString time = eventTime.startDateTime().time().toString("hh:mm");
              QListWidgetItem* listItem = new QListWidgetItem();
              listItem->setText(QString("Event:%1-%2").arg(time).arg(item.displayLabel()));
              QVariant data = QVariant::fromValue<QOrganizerItem>(item);
              listItem->setData(ORGANIZER_ITEM_ROLE, data);
              m_itemList->addItem(listItem);
          }

          QOrganizerTodoTime todoTime = item.detail(QOrganizerItemDetail::TypeTodoTime);
          if (!todoTime.isEmpty()) {
              QString time = todoTime.startDateTime().time().toString("hh:mm");
              QListWidgetItem* listItem = new QListWidgetItem();
              listItem->setText(QString("Todo:%1-%2").arg(time).arg(item.displayLabel()));
              QVariant data = QVariant::fromValue<QOrganizerItem>(item);
              listItem->setData(ORGANIZER_ITEM_ROLE, data);
              m_itemList->addItem(listItem);
          }

          QOrganizerJournalTime journalTime = item.detail(QOrganizerItemDetail::TypeJournalTime);
          if (!journalTime.isEmpty()) {
              QString time = journalTime.entryDateTime().time().toString("hh:mm");
              QListWidgetItem* listItem = new QListWidgetItem();
              listItem->setText(QString("Journal:%1-%2").arg(time).arg(item.displayLabel()));
              QVariant data = QVariant::fromValue<QOrganizerItem>(item);
              listItem->setData(ORGANIZER_ITEM_ROLE, data);
              m_itemList->addItem(listItem);
          }

          // TODO: other item types
          // TODO: icons for event/todo/journal would be nice
      }
      if (m_itemList->count() == 0)
          m_itemList->addItem("(no entries)");
  }

  void MonthPage::currentMonthChanged()
  {
      int month = m_calendarWidget->monthShown();
      int year = m_calendarWidget->yearShown();
      m_dateLabel->setText(QString("%1 %2").arg(QLocale().standaloneMonthName(month)).arg(year));
      refresh();
      m_ignoreShowDayPageOnceFlag = true;
  }

  void MonthPage::dayDoubleClicked(QDate date)
  {
      if (!m_ignoreShowDayPageOnceFlag)
          emit showDayPage(date);
      else
          m_ignoreShowDayPageOnceFlag = false;
  }

  void MonthPage::openDay()
  {
      emit showDayPage(m_calendarWidget->selectedDate());
  }

  void MonthPage::itemDoubleClicked(QListWidgetItem *listItem)
  {
      if (!listItem)
          return;

      QOrganizerItem organizerItem = listItem->data(ORGANIZER_ITEM_ROLE).value<QOrganizerItem>();
      if (!organizerItem.isEmpty())
          emit showEditPage(organizerItem);
  }

  void MonthPage::showEvent(QShowEvent *event)
  {
      window()->setWindowTitle("Calendar Demo");
      QWidget::showEvent(event);
  }