diff --git a/Browser/BrowserWidget.cpp b/Browser/BrowserWidget.cpp index 37c9aba5..6f2962a4 100644 --- a/Browser/BrowserWidget.cpp +++ b/Browser/BrowserWidget.cpp @@ -1,481 +1,481 @@ /* Copyright (C) 2003-2018 Jesper K. Pedersen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "BrowserWidget.h" #include #include #include #include #include #include "ImageViewPage.h" #include "CategoryPage.h" #include "TreeFilter.h" #include #include #include "OverviewPage.h" #include "enums.h" #include #include "Settings/SettingsData.h" #include #include #include "Utilities/Util.h" #include "Utilities/ShowBusyCursor.h" #include #include "DB/CategoryCollection.h" #include "TreeCategoryModel.h" Browser::BrowserWidget* Browser::BrowserWidget::s_instance = nullptr; bool Browser::BrowserWidget::s_isResizing = false; Browser::BrowserWidget::BrowserWidget( QWidget* parent ) :QWidget( parent ), m_current(-1) { Q_ASSERT( !s_instance ); s_instance = this; createWidgets(); connect( DB::ImageDB::instance()->categoryCollection(), &DB::CategoryCollection::categoryCollectionChanged, this, &BrowserWidget::reload); connect( this, &BrowserWidget::viewChanged, this, &BrowserWidget::resetIconViewSearch); m_filterProxy = new TreeFilter(this); m_filterProxy->setFilterKeyColumn(0); m_filterProxy->setFilterCaseSensitivity( Qt::CaseInsensitive ); m_filterProxy->setSortRole( ValueRole ); m_filterProxy->setSortCaseSensitivity( Qt::CaseInsensitive ); addAction( new OverviewPage( Breadcrumb::home(), DB::ImageSearchInfo(), this ) ); QTimer::singleShot( 0, this, SLOT(emitSignals()) ); } void Browser::BrowserWidget::forward() { int targetIndex = m_current; while ( targetIndex < m_list.count()-1 ) { targetIndex++; if ( m_list[targetIndex]->showDuringMovement() ) { break; } } activatePage(targetIndex); } void Browser::BrowserWidget::back() { int targetIndex = m_current; while ( targetIndex > 0 ) { targetIndex--; if ( m_list[targetIndex]->showDuringMovement() ) break; } activatePage(targetIndex); } void Browser::BrowserWidget::activatePage(int pageIndex) { if (pageIndex != m_current) { if (currentAction() != 0) { currentAction()->deactivate(); } m_current = pageIndex; go(); } } void Browser::BrowserWidget::go() { switchToViewType( currentAction()->viewType() ); currentAction()->activate(); setBranchOpen(QModelIndex(), true); adjustTreeViewColumnSize(); emitSignals(); } void Browser::BrowserWidget::addSearch( DB::ImageSearchInfo& info ) { addAction( new OverviewPage( Breadcrumb::empty(), info, this ) ); } void Browser::BrowserWidget::addImageView( const DB::FileName& context ) { addAction( new ImageViewPage( context, this ) ); } void Browser::BrowserWidget::addAction( Browser::BrowserPage* action ) { // remove actions which would go forward in the breadcrumbs while ( (int) m_list.count() > m_current+1 ) { BrowserPage* m = m_list.back(); m_list.pop_back(); delete m; } m_list.append(action); activatePage(m_list.count() - 1); } void Browser::BrowserWidget::emitSignals() { emit canGoBack( m_current > 0 ); emit canGoForward( m_current < (int)m_list.count()-1 ); if ( currentAction()->viewer() == ShowBrowser ) emit showingOverview(); emit isSearchable( currentAction()->isSearchable() ); emit isViewChangeable( currentAction()->isViewChangeable() ); bool isCategoryAction = (dynamic_cast( currentAction() ) != 0); if ( isCategoryAction ) { DB::CategoryPtr category = DB::ImageDB::instance()->categoryCollection()->categoryForName( currentCategory() ); Q_ASSERT( category.data() ); emit currentViewTypeChanged( category->viewType()); } emit pathChanged( createPath() ); emit viewChanged(); emit imageCount( DB::ImageDB::instance()->count(currentAction()->searchInfo()).total() ); } void Browser::BrowserWidget::home() { addAction( new OverviewPage( Breadcrumb::home(), DB::ImageSearchInfo(), this ) ); } void Browser::BrowserWidget::reload() { currentAction()->activate(); } Browser::BrowserWidget* Browser::BrowserWidget::instance() { Q_ASSERT( s_instance ); return s_instance; } void Browser::BrowserWidget::load( const QString& category, const QString& value ) { DB::ImageSearchInfo info; info.addAnd( category, value ); DB::MediaCount counts = DB::ImageDB::instance()->count( info ); bool loadImages = (counts.total() < Settings::SettingsData::instance()->autoShowThumbnailView()); if ( QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) ) loadImages = !loadImages; if ( loadImages ) addAction( new ImageViewPage( info, this ) ); else addAction( new OverviewPage( Breadcrumb(value, true) , info, this ) ); go(); topLevelWidget()->raise(); activateWindow(); } DB::ImageSearchInfo Browser::BrowserWidget::currentContext() { return currentAction()->searchInfo(); } void Browser::BrowserWidget::slotSmallListView() { changeViewTypeForCurrentView( DB::Category::TreeView ); } void Browser::BrowserWidget::slotLargeListView() { changeViewTypeForCurrentView( DB::Category::ThumbedTreeView ); } void Browser::BrowserWidget::slotSmallIconView() { changeViewTypeForCurrentView( DB::Category::IconView ); } void Browser::BrowserWidget::slotLargeIconView() { changeViewTypeForCurrentView( DB::Category::ThumbedIconView ); } void Browser::BrowserWidget::changeViewTypeForCurrentView( DB::Category::ViewType type ) { Q_ASSERT( m_list.size() > 0 ); DB::CategoryPtr category = DB::ImageDB::instance()->categoryCollection()->categoryForName( currentCategory() ); Q_ASSERT( category.data() ); category->setViewType( type ); switchToViewType( type ); reload(); } void Browser::BrowserWidget::setFocus() { m_curView->setFocus(); } QString Browser::BrowserWidget::currentCategory() const { if ( CategoryPage* action = dynamic_cast( currentAction() ) ) return action->category()->name(); else return QString(); } void Browser::BrowserWidget::slotLimitToMatch( const QString& str ) { m_filterProxy->resetCache(); m_filterProxy->setFilterFixedString( str ); setBranchOpen(QModelIndex(), true); adjustTreeViewColumnSize(); } void Browser::BrowserWidget::resetIconViewSearch() { m_filterProxy->resetCache(); m_filterProxy->setFilterRegExp( QString() ); adjustTreeViewColumnSize(); } void Browser::BrowserWidget::slotInvokeSeleted() { if ( !m_curView->currentIndex().isValid() ) { if ( m_filterProxy->rowCount( QModelIndex() ) == 0 ) { // Absolutely nothing to see here :-) return; } else { // Use the first item itemClicked( m_filterProxy->index( 0,0,QModelIndex() ) ); } } else itemClicked( m_curView->currentIndex() ); } void Browser::BrowserWidget::itemClicked( const QModelIndex& index ) { Utilities::ShowBusyCursor busy; BrowserPage* action = currentAction()->activateChild( m_filterProxy->mapToSource( index ) ); if ( action ) addAction( action ); } Browser::BrowserPage* Browser::BrowserWidget::currentAction() const { return m_current >= 0? m_list[m_current] : 0; } void Browser::BrowserWidget::setModel( QAbstractItemModel* model) { m_filterProxy->setSourceModel( model ); // make sure the view knows about the source model change: m_curView->setModel( m_filterProxy ); if (qobject_cast(model)) { // FIXME: The new-style connect here does not work, reload() is not triggered //connect(model, &QAbstractItemModel::dataChanged, this, &BrowserWidget::reload); // The old-style one triggers reload() correctly connect(model, SIGNAL(dataChanged()), this, SLOT(reload())); } } void Browser::BrowserWidget::switchToViewType( DB::Category::ViewType type ) { if ( m_curView ) { m_curView->setModel(0); - disconnect( m_curView, &QAbstractItemView::activated, this, &BrowserWidget::itemClicked); + disconnect( m_curView, &QAbstractItemView::clicked, this, &BrowserWidget::itemClicked); } if ( type == DB::Category::TreeView || type == DB::Category::ThumbedTreeView ) { m_curView = m_treeView; } else { m_curView =m_listView; m_filterProxy->invalidate(); m_filterProxy->sort( 0, Qt::AscendingOrder ); m_listView->setViewMode(dynamic_cast(currentAction()) == 0 ? CenteringIconView::NormalIconView : CenteringIconView::CenterView ); } if ( CategoryPage* action = dynamic_cast( currentAction() ) ) { const int size = action->category()->thumbnailSize(); m_curView->setIconSize( QSize(size,size) ); // m_curView->setGridSize( QSize( size+10, size+10 ) ); } // Hook up the new view m_curView->setModel( m_filterProxy ); - connect( m_curView, &QAbstractItemView::activated, this, &BrowserWidget::itemClicked); + connect( m_curView, &QAbstractItemView::clicked, this, &BrowserWidget::itemClicked); m_stack->setCurrentWidget( m_curView ); adjustTreeViewColumnSize(); } void Browser::BrowserWidget::setBranchOpen( const QModelIndex& parent, bool open ) { if ( m_curView != m_treeView ) return; const int count = m_filterProxy->rowCount(parent); if ( count > 5 ) open = false; m_treeView->setExpanded( parent, open ); for ( int row = 0; row < count; ++row ) setBranchOpen( m_filterProxy->index( row, 0 ,parent ), open ); } Browser::BreadcrumbList Browser::BrowserWidget::createPath() const { BreadcrumbList result; for ( int i = 0; i <= m_current; ++i ) result.append(m_list[i]->breadcrumb() ); return result; } void Browser::BrowserWidget::widenToBreadcrumb( const Browser::Breadcrumb& breadcrumb ) { while ( currentAction()->breadcrumb() != breadcrumb ) m_current--; go(); } void Browser::BrowserWidget::adjustTreeViewColumnSize() { m_treeView->header()->resizeSections(QHeaderView::ResizeToContents); } void Browser::BrowserWidget::createWidgets() { m_stack = new QStackedWidget; QHBoxLayout* layout = new QHBoxLayout( this ); layout->setContentsMargins(0,0,0,0); layout->addWidget( m_stack ); m_listView = new CenteringIconView ( m_stack ); m_listView->setIconSize( QSize(100,75) ); m_listView->setSelectionMode( QListView::SingleSelection ); m_listView->setSpacing(10); m_listView->setUniformItemSizes(true); m_listView->setResizeMode( QListView::Adjust ); m_stack->addWidget( m_listView ); m_treeView = new QTreeView( m_stack ); m_treeView->setDragEnabled(true); m_treeView->setAcceptDrops(true); m_treeView->setDropIndicatorShown(true); m_treeView->setDefaultDropAction( Qt::MoveAction ); QPalette pal = m_treeView->palette(); pal.setBrush( QPalette::Base, QApplication::palette().color( QPalette::Background ) ); m_treeView->setPalette( pal ); m_treeView->header()->setStretchLastSection(false); m_treeView->header()->setSortIndicatorShown(true); m_treeView->setSortingEnabled(true); m_treeView->sortByColumn( 0, Qt::AscendingOrder ); m_stack->addWidget( m_treeView ); // Do not give focus to the widgets when they are scrolled with the wheel. m_listView->setFocusPolicy( Qt::StrongFocus ); m_treeView->setFocusPolicy( Qt::StrongFocus ); m_treeView->installEventFilter( this ); m_treeView->viewport()->installEventFilter( this ); m_listView->installEventFilter( this ); m_listView->viewport()->installEventFilter( this ); connect( m_treeView, &QTreeView::expanded, this, &BrowserWidget::adjustTreeViewColumnSize); m_curView = nullptr; } bool Browser::BrowserWidget::eventFilter( QObject* /* obj */, QEvent* event) { if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseMove || event->type() == QEvent::MouseButtonRelease ) { QMouseEvent* me = static_cast( event ); if ( me->buttons() & Qt::MidButton || me->button() & Qt::MidButton) { handleResizeEvent( me ); return true; } } return false; } void Browser::BrowserWidget::scrollKeyPressed( QKeyEvent* event ) { QApplication::sendEvent(m_curView, event ); } void Browser::BrowserWidget::handleResizeEvent( QMouseEvent* event ) { static int offset; CategoryPage* action = dynamic_cast( currentAction() ); if ( !action ) return; DB::CategoryPtr category = action->category(); if ( !action ) return; if ( event->type() == QEvent::MouseButtonPress ) { m_resizePressPos = event->pos(); offset = category->thumbnailSize(); s_isResizing = true; } else if ( event->type() == QEvent::MouseMove ) { int distance = (event->pos() - m_resizePressPos).x() + (event->pos() - m_resizePressPos).y() / 3; int size = distance + offset; size = qMax( qMin( 512, size ), 32 ); action->category()->setThumbnailSize( size ); m_curView->setIconSize( QSize(size,size) ); m_filterProxy->invalidate(); adjustTreeViewColumnSize(); } else if ( event->type() == QEvent::MouseButtonRelease ) { s_isResizing = false; update(); } } // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/Settings/BirthdayPage.cpp b/Settings/BirthdayPage.cpp index ae24ec61..50565bc2 100644 --- a/Settings/BirthdayPage.cpp +++ b/Settings/BirthdayPage.cpp @@ -1,337 +1,337 @@ /* Copyright (C) 2014-2015 Jesper K. Pedersen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // Qt includes #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include #include // Local includes #include "BirthdayPage.h" #include "DB/ImageDB.h" #include "DB/CategoryCollection.h" #include "DB/Category.h" #include "MainWindow/DirtyIndicator.h" #include "DateTableWidgetItem.h" Settings::BirthdayPage::BirthdayPage(QWidget* parent) : QWidget(parent) { QVBoxLayout* mainLayout = new QVBoxLayout(this); QHBoxLayout* dataLayout = new QHBoxLayout; mainLayout->addLayout(dataLayout); QVBoxLayout* itemsLayout = new QVBoxLayout; dataLayout->addLayout(itemsLayout); QHBoxLayout* itemsHeaderLayout = new QHBoxLayout; itemsLayout->addLayout(itemsHeaderLayout); QLabel* categoryText = new QLabel(i18n("Category:")); itemsHeaderLayout->addWidget(categoryText); m_categoryBox = new QComboBox; itemsHeaderLayout->addWidget(m_categoryBox); connect(m_categoryBox, static_cast(&QComboBox::currentIndexChanged), this, &BirthdayPage::changeCategory); m_filter = new QLineEdit; m_filter->setPlaceholderText(i18n("Filter (Alt+f)")); itemsHeaderLayout->addWidget(m_filter); connect(m_filter, &QLineEdit::textChanged, this, &BirthdayPage::resetCategory); new QShortcut(Qt::AltModifier + Qt::Key_F, m_filter, SLOT(setFocus())); if (QLocale().dateFormat(QLocale::ShortFormat).contains(QString::fromUtf8("yyyy"))) { m_dateFormats << QLocale().dateFormat(QLocale::ShortFormat); } else { m_dateFormats << QLocale().dateFormat(QLocale::ShortFormat).replace( QString::fromUtf8("yy"), QString::fromUtf8("yyyy")); } m_dateFormats << QLocale().dateFormat(QLocale::ShortFormat) << QLocale().dateFormat(QLocale::LongFormat); m_dataView = new QTableWidget; m_dataView->setColumnCount(2); m_dataView->verticalHeader()->hide(); m_dataView->setShowGrid(false); itemsLayout->addWidget(m_dataView); - connect(m_dataView, &QTableWidget::cellActivated, this, &BirthdayPage::editDate); + connect(m_dataView, &QTableWidget::cellClicked, this, &BirthdayPage::editDate); QVBoxLayout* calendarLayout = new QVBoxLayout; dataLayout->addLayout(calendarLayout); calendarLayout->addStretch(); m_birthdayOfLabel = new QLabel; calendarLayout->addWidget(m_birthdayOfLabel); m_dateInput = new QLineEdit; calendarLayout->addWidget(m_dateInput); connect(m_dateInput, &QLineEdit::textEdited, this, &BirthdayPage::checkDateInput); connect(m_dateInput, &QLineEdit::editingFinished, this, &BirthdayPage::checkDate); m_calendar = new QCalendarWidget; calendarLayout->addWidget(m_calendar); connect(m_calendar, &QCalendarWidget::clicked, this, &BirthdayPage::setDate); m_unsetButton = new QPushButton(i18n("Remove birthday")); calendarLayout->addWidget(m_unsetButton); connect(m_unsetButton, &QPushButton::clicked, this, &BirthdayPage::removeDate); calendarLayout->addStretch(); QLabel* info = new QLabel(i18n("Set the date of birth for items (say people) here, " "and then see their age when viewing the images.")); mainLayout->addWidget(info); m_noDateString = QString::fromUtf8("---"); m_boldFont.setBold(true); disableCalendar(); } void Settings::BirthdayPage::pageChange(KPageWidgetItem* page) { if (page->widget() == this) { m_lastItem = nullptr; reload(); } } void Settings::BirthdayPage::reload() { m_dateInput->setText(QString()); m_calendar->setSelectedDate(QDate::currentDate()); disableCalendar(); m_categoryBox->blockSignals(true); m_categoryBox->clear(); int defaultIndex = 0; int index = 0; for (const DB::CategoryPtr& category: DB::ImageDB::instance()->categoryCollection()->categories()) { if (category->isSpecialCategory()) { continue; } m_categoryBox->addItem(category->name()); if (category->name() == i18n("People")) { defaultIndex = index; } ++index; } m_categoryBox->setCurrentIndex(defaultIndex); changeCategory(defaultIndex); m_categoryBox->blockSignals(false); } void Settings::BirthdayPage::resetCategory() { changeCategory(m_categoryBox->currentIndex()); } void Settings::BirthdayPage::changeCategory(int index) { m_lastItem = nullptr; m_dataView->clear(); m_dataView->setSortingEnabled(false); m_dataView->setHorizontalHeaderLabels(QStringList() << i18n("Name") << i18n("Birthday")); const QString categoryName = m_categoryBox->itemText(index); const DB::CategoryPtr category = DB::ImageDB::instance()->categoryCollection()->categoryForName(categoryName); QStringList items = category->items(); m_dataView->setRowCount(items.count()); int row = 0; for (const QString& text : items) { if (! m_filter->text().isEmpty() && text.indexOf(m_filter->text(), 0, Qt::CaseInsensitive) == -1) { m_dataView->setRowCount(m_dataView->rowCount() - 1); continue; } QTableWidgetItem* nameItem = new QTableWidgetItem(text); nameItem->setFlags(nameItem->flags() & ~Qt::ItemIsEditable & ~Qt::ItemIsSelectable); m_dataView->setItem(row, 0, nameItem); QDate dateForItem; if (m_changedData.contains(categoryName)) { if (m_changedData[categoryName].contains(text)) { dateForItem = m_changedData[categoryName][text]; } else { dateForItem = category->birthDate(text); } } else { dateForItem = category->birthDate(text); } DateTableWidgetItem* dateItem = new DateTableWidgetItem(textForDate(dateForItem)); dateItem->setData(Qt::UserRole, dateForItem); dateItem->setFlags(dateItem->flags() & ~Qt::ItemIsEditable & ~Qt::ItemIsSelectable); m_dataView->setItem(row, 1, dateItem); row++; } m_dataView->setSortingEnabled(true); m_dataView->sortItems(0); disableCalendar(); } QString Settings::BirthdayPage::textForDate(const QDate& date) const { if (date.isNull()) { return m_noDateString; } else { return QLocale().toString(date, m_dateFormats.at(0)); } } void Settings::BirthdayPage::editDate(int row, int) { m_dateInput->setEnabled(true); m_calendar->setEnabled(true); m_unsetButton->setEnabled(m_dataView->item(row, 1)->text() != m_noDateString); if (m_lastItem != nullptr) { m_lastItem->setFont(m_font); m_dataView->item(m_lastItem->row(), 1)->setFont(m_font); } m_dataView->item(row, 0)->setFont(m_boldFont); m_dataView->item(row, 1)->setFont(m_boldFont); m_birthdayOfLabel->setText(i18n("Birthday of %1:", m_dataView->item(row, 0)->text())); QString dateString = m_dataView->item(row, 1)->text(); if (dateString != m_noDateString) { m_dateInput->setText(dateString); m_calendar->setSelectedDate(m_dataView->item(row, 1)->data(Qt::UserRole).toDate()); } else { m_dateInput->setText(QString()); m_dateInput->setPlaceholderText( i18n("Enter a date...")); m_calendar->setSelectedDate(QDate::currentDate()); } m_lastItem = m_dataView->item(row, 0); } QDate Settings::BirthdayPage::parseDate(QString date) { QDate parsedDate = QDate(); for (const QString &format : m_dateFormats) { parsedDate = QDate::fromString(date, format); if (parsedDate.isValid()) { return parsedDate; } } return parsedDate; } void Settings::BirthdayPage::checkDateInput(QString date) { QDate parsedDate = parseDate(date); if (parsedDate.isValid()) { m_calendar->setSelectedDate(parsedDate); m_dateInput->setStyleSheet(QString()); } else { m_dateInput->setStyleSheet(QString::fromUtf8("color:red;")); } } void Settings::BirthdayPage::checkDate() { QDate parsedDate = parseDate(m_dateInput->text()); if (parsedDate.isValid()) { setDate(parsedDate); } } void Settings::BirthdayPage::setDate(const QDate& date) { const QString currentCategory = m_categoryBox->currentText(); if (! m_changedData.contains(currentCategory)) { m_changedData[currentCategory] = QMap(); } const QString currentItem = m_dataView->item(m_dataView->currentRow(), 0)->text(); m_changedData[currentCategory][currentItem] = date; m_dataView->item(m_dataView->currentRow(), 1)->setText(textForDate(date)); m_dataView->item(m_dataView->currentRow(), 1)->setData(Qt::UserRole, date); m_unsetButton->setEnabled(true); } void Settings::BirthdayPage::disableCalendar() { m_dateInput->setEnabled(false); m_calendar->setEnabled(false); m_unsetButton->setEnabled(false); m_birthdayOfLabel->setText(i18n("Select an item on the left to edit the birthday")); } void Settings::BirthdayPage::discardChanges() { m_changedData.clear(); } void Settings::BirthdayPage::saveSettings() { QMapIterator> changedCategory(m_changedData); while (changedCategory.hasNext()) { changedCategory.next(); DB::CategoryPtr category = DB::ImageDB::instance()->categoryCollection() ->categoryForName(changedCategory.key()); QMapIterator changedItem(changedCategory.value()); while (changedItem.hasNext()) { changedItem.next(); category->setBirthDate(changedItem.key(), changedItem.value()); } } if (m_changedData.size() > 0) { MainWindow::DirtyIndicator::markDirty(); m_changedData.clear(); } } void Settings::BirthdayPage::removeDate() { m_dateInput->setText(QString()); m_calendar->setSelectedDate(QDate::currentDate()); setDate(QDate()); } diff --git a/Settings/TagGroupsPage.cpp b/Settings/TagGroupsPage.cpp index 8f4d0875..b6155099 100644 --- a/Settings/TagGroupsPage.cpp +++ b/Settings/TagGroupsPage.cpp @@ -1,819 +1,819 @@ /* Copyright (C) 2003-2014 Jesper K. Pedersen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "TagGroupsPage.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include #include // Local includes #include "MainWindow/DirtyIndicator.h" #include "DB/CategoryCollection.h" #include "CategoriesGroupsWidget.h" #include "Settings/SettingsData.h" Settings::TagGroupsPage::TagGroupsPage(QWidget* parent) : QWidget(parent) { QGridLayout* layout = new QGridLayout(this); // The category and group tree layout->addWidget(new QLabel(i18nc("@label","Categories and groups:")), 0, 0); m_categoryTreeWidget = new CategoriesGroupsWidget(this); m_categoryTreeWidget->header()->hide(); m_categoryTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu); layout->addWidget(m_categoryTreeWidget, 1, 0); connect(m_categoryTreeWidget, &CategoriesGroupsWidget::customContextMenuRequested, this, &TagGroupsPage::showTreeContextMenu); - connect(m_categoryTreeWidget, &CategoriesGroupsWidget::itemActivated, this, &TagGroupsPage::slotGroupSelected); + connect(m_categoryTreeWidget, &CategoriesGroupsWidget::itemClicked, this, &TagGroupsPage::slotGroupSelected); // The member list m_selectGroupToAddTags = i18nc("@label/rich","Select a group on the left side to add tags to it"); m_tagsInGroupLabel = new QLabel(m_selectGroupToAddTags); layout->addWidget(m_tagsInGroupLabel, 0, 1); m_membersListWidget = new QListWidget; m_membersListWidget->setEnabled(false); m_membersListWidget->setContextMenuPolicy(Qt::CustomContextMenu); layout->addWidget(m_membersListWidget, 1, 1); connect(m_membersListWidget, &QListWidget::itemChanged, this, &TagGroupsPage::checkItemSelection); connect(m_membersListWidget, &QListWidget::customContextMenuRequested, this, &TagGroupsPage::showMembersContextMenu); // The "pending rename actions" label m_pendingChangesLabel = new QLabel(i18nc("@label/rich","There are pending changes on the categories page. " "Please save the changes before working on tag groups.")); m_pendingChangesLabel->hide(); layout->addWidget(m_pendingChangesLabel, 2, 0, 1, 2); QDialog *parentDialog = qobject_cast(parent); connect( parentDialog, &QDialog::rejected, this, &TagGroupsPage::discardChanges); // Context menu actions m_newGroupAction = new QAction(i18nc("@action:inmenu","Add group ..."), this); connect(m_newGroupAction, &QAction::triggered, this, &TagGroupsPage::slotAddGroup); m_renameAction = new QAction(this); connect(m_renameAction, &QAction::triggered, this, &TagGroupsPage::slotRenameGroup); m_deleteAction = new QAction(this); connect(m_deleteAction, &QAction::triggered, this, &TagGroupsPage::slotDeleteGroup); m_deleteMemberAction = new QAction(this); connect(m_deleteMemberAction, &QAction::triggered, this, &TagGroupsPage::slotDeleteMember); m_renameMemberAction = new QAction(this); connect(m_renameMemberAction, &QAction::triggered, this, &TagGroupsPage::slotRenameMember); m_memberMap = DB::ImageDB::instance()->memberMap(); connect(DB::ImageDB::instance()->categoryCollection(), SIGNAL(itemRemoved(DB::Category*,QString)), &m_memberMap, SLOT(deleteItem(DB::Category*,QString))); connect(DB::ImageDB::instance()->categoryCollection(), SIGNAL(itemRenamed(DB::Category*,QString,QString)), &m_memberMap, SLOT(renameItem(DB::Category*,QString,QString))); connect(DB::ImageDB::instance()->categoryCollection(), SIGNAL(categoryRemoved(QString)), &m_memberMap, SLOT(deleteCategory(QString))); m_dataChanged = false; } void Settings::TagGroupsPage::updateCategoryTree() { // Store all expanded items so that they can be expanded after reload QList> expandedItems = QList>(); QTreeWidgetItemIterator it(m_categoryTreeWidget); while (*it) { if ((*it)->isExpanded()) { if ((*it)->parent() == nullptr) { expandedItems.append(QPair((*it)->text(0), QString())); } else { expandedItems.append( QPair((*it)->text(0), (*it)->parent()->text(0)) ); } } ++it; } m_categoryTreeWidget->clear(); // Create a tree view of all groups and their sub-groups QList categories = DB::ImageDB::instance()->categoryCollection()->categories(); Q_FOREACH(const DB::CategoryPtr category, categories ) { if (category->isSpecialCategory()) { continue; } // Add the real categories as top-level items QTreeWidgetItem* topLevelItem = new QTreeWidgetItem; topLevelItem->setText(0, category->name()); topLevelItem->setFlags(topLevelItem->flags() & Qt::ItemIsEnabled); QFont font = topLevelItem->font(0); font.setWeight(QFont::Bold); topLevelItem->setFont(0, font); m_categoryTreeWidget->addTopLevelItem(topLevelItem); // Build a map with all members for each group QMap membersForGroup; QStringList allGroups = m_memberMap.groups(category->name()); foreach (const QString &group, allGroups) { // FIXME: Why does the member map return an empty category?! if (group.isEmpty()) { continue; } QStringList allMembers = m_memberMap.members(category->name(), group, false); foreach (const QString &member, allMembers) { membersForGroup[group] << member; } // We add an empty member placeholder if the group currently has no members. // Otherwise, it won't be added. if (! membersForGroup.contains(group)) { membersForGroup[group] = QStringList(); } } // Add all groups (their sub-groups will be added recursively) addSubCategories(topLevelItem, membersForGroup, allGroups); } // Order the items alphabetically m_categoryTreeWidget->sortItems(0, Qt::AscendingOrder); // Re-expand all previously expanded items QTreeWidgetItemIterator it2(m_categoryTreeWidget); while (*it2) { if ((*it2)->parent() == nullptr) { if (expandedItems.contains(QPair((*it2)->text(0), QString()))) { (*it2)->setExpanded(true); } } else { if (expandedItems.contains(QPair((*it2)->text(0), (*it2)->parent()->text(0)))) { (*it2)->setExpanded(true); } } ++it2; } } void Settings::TagGroupsPage::addSubCategories(QTreeWidgetItem* superCategory, QMap& membersForGroup, QStringList& allGroups) { // Process all group members QMap::iterator memIt1; for (memIt1 = membersForGroup.begin(); memIt1 != membersForGroup.end(); ++memIt1) { bool isSubGroup = false; // Search for a membership in another group for the current group QMap::iterator memIt2; for (memIt2 = membersForGroup.begin(); memIt2 != membersForGroup.end(); ++memIt2) { if (memIt2.value().contains(memIt1.key())) { isSubGroup = true; break; } } // Add the group if it's not member of another group if (! isSubGroup) { QTreeWidgetItem* group = new QTreeWidgetItem; group->setText(0, memIt1.key()); superCategory->addChild(group); // Search the member list for other groups QMap subGroups; foreach (const QString &groupName, allGroups) { if (membersForGroup[memIt1.key()].contains(groupName)) { subGroups[groupName] = membersForGroup[groupName]; } } // If the list contains other groups, add them recursively if (subGroups.count() > 0) { addSubCategories(group, subGroups, allGroups); } } } } QString Settings::TagGroupsPage::getCategory(QTreeWidgetItem* currentItem) { while (currentItem->parent() != nullptr) { currentItem = currentItem->parent(); } return currentItem->text(0); } void Settings::TagGroupsPage::showTreeContextMenu(QPoint point) { QTreeWidgetItem* currentItem = m_categoryTreeWidget->currentItem(); if (currentItem == nullptr) { return; } m_currentSubCategory = currentItem->text(0); if (currentItem->parent() == nullptr) { // It's a top-level, "real" category m_currentSuperCategory.clear(); } else { // It's a normal sub-category that belongs to another one m_currentSuperCategory = currentItem->parent()->text(0); } m_currentCategory = getCategory(currentItem); QMenu *menu = new QMenu; menu->addAction(m_newGroupAction); // "Real" top-level categories have to processed on the category page. if (!m_currentSuperCategory.isEmpty()) { menu->addSeparator(); m_renameAction->setText(i18nc("@action:inmenu","Rename group \"%1\"", m_currentSubCategory)); menu->addAction(m_renameAction); m_deleteAction->setText(i18nc("@action:inmenu","Delete group \"%1\"", m_currentSubCategory)); menu->addAction(m_deleteAction); } menu->exec(m_categoryTreeWidget->mapToGlobal(point)); delete menu; } void Settings::TagGroupsPage::categoryChanged(const QString& name) { if (name.isEmpty()) { return; } m_membersListWidget->blockSignals(true); m_membersListWidget->clear(); QStringList list = getCategoryObject(name)->items(); list += m_memberMap.groups(name); QStringList alreadyAdded; Q_FOREACH( const QString &member, list ) { if (member.isEmpty()) { // This can happen if we add group that currently has no members. continue; } if (! alreadyAdded.contains(member)) { alreadyAdded << member; if (Settings::SettingsData::instance()->hasUntaggedCategoryFeatureConfigured() && ! Settings::SettingsData::instance()->untaggedImagesTagVisible()) { if (name == Settings::SettingsData::instance()->untaggedCategory()) { if (member == Settings::SettingsData::instance()->untaggedTag()) { continue; } } } QListWidgetItem* newItem = new QListWidgetItem(member, m_membersListWidget); newItem->setFlags(newItem->flags() | Qt::ItemIsUserCheckable); newItem->setCheckState(Qt::Unchecked); } } m_currentGroup.clear(); m_membersListWidget->clearSelection(); m_membersListWidget->sortItems(); m_membersListWidget->setEnabled(false); m_membersListWidget->blockSignals(false); } void Settings::TagGroupsPage::slotGroupSelected(QTreeWidgetItem* item) { // When something else than a "real" category has been selected before, // we have to save it's members. if (!m_currentGroup.isEmpty()) { saveOldGroup(); } if (item->parent() == nullptr) { // A "real" category has been selected, not a group m_currentCategory.clear(); m_currentGroup.clear(); m_membersListWidget->setEnabled(false); categoryChanged(item->text(0)); m_tagsInGroupLabel->setText(m_selectGroupToAddTags); return; } // Let's see if the category changed QString itemCategory = getCategory(item); if (m_currentCategory != itemCategory) { m_currentCategory = itemCategory; categoryChanged(m_currentCategory); } m_currentGroup = item->text(0); selectMembers(m_currentGroup); m_tagsInGroupLabel->setText(i18nc("@label","Tags in group \"%1\" of category \"%2\"", m_currentGroup, m_currentCategory)); } void Settings::TagGroupsPage::slotAddGroup() { bool ok; DB::CategoryPtr category = getCategoryObject(m_currentCategory); QStringList groups = m_memberMap.groups(m_currentCategory); QStringList tags; for (QString tag : category->items()) { if (! groups.contains(tag)) { tags << tag; } } //// reject existing group names: //KStringListValidator validator(groups); //QString newSubCategory = KInputDialog::getText(i18nc("@title:window","New Group"), // i18nc("@label:textbox","Group name:"), // QString() /*value*/, // &ok, // this /*parent*/, // &validator, // QString() /*mask*/, // QString() /*WhatsThis*/, // tags /*completion*/ // ); // FIXME: KF5-port: QInputDialog does not accept a validator, // and KInputDialog was removed in KF5. -> Reimplement input validation using other stuff QString newSubCategory = QInputDialog::getText(this, i18nc("@title:window","New Group"), i18nc("@label:textbox","Group name:"), QLineEdit::Normal, QString(), &ok ); if (groups.contains(newSubCategory)) return; // only a workaround until GUI-support for validation is restored if (! ok) { return; } // Let's see if we already have this group if (groups.contains(newSubCategory)) { // (with the validator working correctly, we should not get to this point) KMessageBox::sorry(this, i18nc("@info","

The group \"%1\" already exists.

", newSubCategory), i18nc("@title:window","Cannot add group")); return; } // Add the group as a new tag to the respective category MainWindow::DirtyIndicator::suppressMarkDirty(true); category->addItem(newSubCategory); MainWindow::DirtyIndicator::suppressMarkDirty(false); QMap categoryChange; categoryChange[CategoryEdit::Category] = m_currentCategory; categoryChange[CategoryEdit::Add] = newSubCategory; m_categoryChanges.append(categoryChange); m_dataChanged = true; // Add the group m_memberMap.addGroup(m_currentCategory, newSubCategory); // Display the new group categoryChanged(m_currentCategory); // Display the new item QTreeWidgetItem* parentItem = m_categoryTreeWidget->currentItem(); addNewSubItem(newSubCategory, parentItem); // Check if we also have to update some other group (in case this is not a top-level group) if (!m_currentSuperCategory.isEmpty()) { m_memberMap.addMemberToGroup(m_currentCategory, parentItem->text(0), newSubCategory); slotGroupSelected(parentItem); } m_dataChanged = true; } void Settings::TagGroupsPage::addNewSubItem(QString& name, QTreeWidgetItem* parentItem) { QTreeWidgetItem* newItem = new QTreeWidgetItem; newItem->setText(0, name); parentItem->addChild(newItem); if (! parentItem->isExpanded()) { parentItem->setExpanded(true); } } QTreeWidgetItem* Settings::TagGroupsPage::findCategoryItem(QString category) { QTreeWidgetItem* categoryItem = nullptr; for (int i = 0; i < m_categoryTreeWidget->topLevelItemCount(); ++i) { categoryItem = m_categoryTreeWidget->topLevelItem(i); if (categoryItem->text(0) == category) { break; } } return categoryItem; } void Settings::TagGroupsPage::checkItemSelection(QListWidgetItem*) { m_dataChanged = true; saveOldGroup(); updateCategoryTree(); } void Settings::TagGroupsPage::slotRenameGroup() { bool ok; DB::CategoryPtr category = getCategoryObject(m_currentCategory); QStringList groups = m_memberMap.groups(m_currentCategory); QStringList tags; for (QString tag : category->items()) { if (! groups.contains(tag)) { tags << tag; } } // FIXME: reject existing group names QString newSubCategoryName = QInputDialog::getText(this, i18nc("@title:window","Rename Group"), i18nc("@label:textbox","New group name:"), QLineEdit::Normal, m_currentSubCategory, &ok ); // workaround until validation with GUI support is reimplemented: if (groups.contains(newSubCategoryName)) return; if (! ok || m_currentSubCategory == newSubCategoryName) { return; } if (m_memberMap.groups(m_currentCategory).contains(newSubCategoryName)) { // (with the validator working correctly, we should not get to this point) KMessageBox::sorry(this, i18nc("@info","Cannot rename group \"%1\" to \"%2\": " "\"%2\" already exists in category \"%3\"", m_currentSubCategory, newSubCategoryName, m_currentCategory), i18nc("@title:window","Rename Group")); return; } QTreeWidgetItem* selectedGroup = m_categoryTreeWidget->currentItem(); saveOldGroup(); // Update the group m_memberMap.renameGroup(m_currentCategory, m_currentSubCategory, newSubCategoryName); // Update the tag in the respective category MainWindow::DirtyIndicator::suppressMarkDirty(true); category->renameItem(m_currentSubCategory, newSubCategoryName); MainWindow::DirtyIndicator::suppressMarkDirty(false); QMap categoryChange; categoryChange[CategoryEdit::Category] = m_currentCategory; categoryChange[CategoryEdit::Rename] = m_currentSubCategory; categoryChange[CategoryEdit::NewName] = newSubCategoryName; m_categoryChanges.append(categoryChange); m_dataChanged = true; // Search for all possible sub-category items in this category that have to be renamed QTreeWidgetItem* categoryItem = findCategoryItem(m_currentCategory); for (int i = 0; i < categoryItem->childCount(); ++i) { renameAllSubCategories(categoryItem->child(i), m_currentSubCategory, newSubCategoryName); } // Update the displayed items categoryChanged(m_currentCategory); slotGroupSelected(selectedGroup); m_dataChanged = true; } void Settings::TagGroupsPage::renameAllSubCategories(QTreeWidgetItem* categoryItem, QString oldName, QString newName) { // Probably, it item itself has to be renamed if (categoryItem->text(0) == oldName) { categoryItem->setText(0, newName); } // Also check all sub-categories recursively for (int i = 0; i < categoryItem->childCount(); ++i) { renameAllSubCategories(categoryItem->child(i), oldName, newName); } } void Settings::TagGroupsPage::slotDeleteGroup() { QTreeWidgetItem* currentItem = m_categoryTreeWidget->currentItem(); QString message; QString title; if (currentItem->childCount() > 0) { message = i18nc("@info","Really delete group \"%1\"?" "Sub-categories of this group will be moved to the super category of \"%1\" (\"%2\"). " "All other memberships of the sub-categories will stay intact.", m_currentSubCategory, m_currentSuperCategory); } else { message = i18nc("@info","Really delete group \"%1\"?", m_currentSubCategory); } int res = KMessageBox::warningContinueCancel(this, message, i18nc("@title:window","Delete Group"), KGuiItem(i18n("&Delete"), QString::fromUtf8("editdelete"))); if (res == KMessageBox::Cancel) { return; } // Delete the group m_memberMap.deleteGroup(m_currentCategory, m_currentSubCategory); // Delete the tag MainWindow::DirtyIndicator::suppressMarkDirty(true); getCategoryObject(m_currentCategory)->removeItem(m_currentSubCategory); MainWindow::DirtyIndicator::suppressMarkDirty(false); QMap categoryChange; categoryChange[CategoryEdit::Category] = m_currentCategory; categoryChange[CategoryEdit::Remove] = m_currentSubCategory; m_categoryChanges.append(categoryChange); m_dataChanged = true; slotPageChange(); m_dataChanged = true; } void Settings::TagGroupsPage::saveOldGroup() { QStringList list; for (int i = 0; i < m_membersListWidget->count(); ++i) { QListWidgetItem *item = m_membersListWidget->item(i); if (item->checkState() == Qt::Checked) { list << item->text(); } } m_memberMap.setMembers(m_currentCategory, m_currentGroup, list); } void Settings::TagGroupsPage::selectMembers(const QString& group) { m_membersListWidget->blockSignals(true); m_membersListWidget->setEnabled(false); m_currentGroup = group; QStringList memberList = m_memberMap.members(m_currentCategory, group, false); for (int i = 0; i < m_membersListWidget->count(); ++i) { QListWidgetItem* item = m_membersListWidget->item(i); item->setCheckState(Qt::Unchecked); if (! m_memberMap.canAddMemberToGroup(m_currentCategory, group, item->text())) { item->setFlags(item->flags() & ~Qt::ItemIsSelectable & ~Qt::ItemIsEnabled); } else { item->setFlags(item->flags() | Qt::ItemIsSelectable | Qt::ItemIsEnabled); if (memberList.contains(item->text())) { item->setCheckState(Qt::Checked); } } } m_membersListWidget->setEnabled(true); m_membersListWidget->blockSignals(false); } void Settings::TagGroupsPage::slotPageChange() { m_tagsInGroupLabel->setText(m_selectGroupToAddTags); m_membersListWidget->setEnabled(false); m_membersListWidget->clear(); m_currentCategory.clear(); updateCategoryTree(); } void Settings::TagGroupsPage::saveSettings() { saveOldGroup(); slotPageChange(); DB::ImageDB::instance()->memberMap() = m_memberMap; m_categoryChanges.clear(); if (m_dataChanged) { m_dataChanged = false; MainWindow::DirtyIndicator::markDirty(); } m_categoryTreeWidget->setEnabled(true); m_membersListWidget->setEnabled(true); m_pendingChangesLabel->hide(); } void Settings::TagGroupsPage::discardChanges() { m_memberMap = DB::ImageDB::instance()->memberMap(); slotPageChange(); m_dataChanged = false; // Revert all changes to the "real" category objects MainWindow::DirtyIndicator::suppressMarkDirty(true); for (int i = m_categoryChanges.size() - 1; i >= 0; i--) { DB::CategoryPtr category = getCategoryObject(m_categoryChanges.at(i)[CategoryEdit::Category]); if (m_categoryChanges.at(i).contains(CategoryEdit::Add)) { // Remove added tags category->removeItem(m_categoryChanges.at(i)[CategoryEdit::Add]); } else if (m_categoryChanges.at(i).contains(CategoryEdit::Remove)) { // Add removed tags category->addItem(m_categoryChanges.at(i)[CategoryEdit::Add]); } else if (m_categoryChanges.at(i).contains(CategoryEdit::Rename)) { // Re-rename tags to their old name category->renameItem(m_categoryChanges.at(i)[CategoryEdit::NewName], m_categoryChanges.at(i)[Rename]); } } MainWindow::DirtyIndicator::suppressMarkDirty(false); m_categoryChanges.clear(); m_categoryTreeWidget->setEnabled(true); m_membersListWidget->setEnabled(true); m_pendingChangesLabel->hide(); } void Settings::TagGroupsPage::loadSettings() { categoryChanged(m_currentCategory); updateCategoryTree(); } void Settings::TagGroupsPage::categoryChangesPending() { m_categoryTreeWidget->setEnabled(false); m_membersListWidget->setEnabled(false); m_pendingChangesLabel->show(); } DB::MemberMap* Settings::TagGroupsPage::memberMap() { return &m_memberMap; } void Settings::TagGroupsPage::processDrop(QTreeWidgetItem* draggedItem, QTreeWidgetItem* targetItem) { if (targetItem->parent() != nullptr) { // Dropped on a group // Select the group m_categoryTreeWidget->setCurrentItem(targetItem); slotGroupSelected(targetItem); // Check the dragged group on the member side to make it a sub-group of the target group m_membersListWidget->findItems(draggedItem->text(0), Qt::MatchExactly)[0]->setCheckState(Qt::Checked); } else { // Dropped on a top-level category // Check if it's already a direct child of the category. // If so, we don't need to do anything. QTreeWidgetItem* parent = draggedItem->parent(); if (parent->parent() == nullptr) { return; } // Select the former super group m_categoryTreeWidget->setCurrentItem(parent); slotGroupSelected(parent); // Deselect the dragged group (this will bring it to the top level) m_membersListWidget->findItems(draggedItem->text(0), Qt::MatchExactly)[0]->setCheckState(Qt::Unchecked); } } void Settings::TagGroupsPage::showMembersContextMenu(QPoint point) { if (m_membersListWidget->currentItem() == nullptr) { return; } QMenu* menu = new QMenu; m_renameMemberAction->setText(i18nc("@action:inmenu","Rename \"%1\"", m_membersListWidget->currentItem()->text())); menu->addAction(m_renameMemberAction); m_deleteMemberAction->setText(i18nc("@action:inmenu","Delete \"%1\"", m_membersListWidget->currentItem()->text())); menu->addAction(m_deleteMemberAction); menu->exec(m_membersListWidget->mapToGlobal(point)); delete menu; } void Settings::TagGroupsPage::slotRenameMember() { bool ok; QString newTagName = QInputDialog::getText(this, i18nc("@title:window","New Tag Name"), i18nc("@label:textbox","Tag name:"), QLineEdit::Normal, m_membersListWidget->currentItem()->text(), &ok); if (! ok || newTagName == m_membersListWidget->currentItem()->text()) { return; } // Update the tag name in the database MainWindow::DirtyIndicator::suppressMarkDirty(true); getCategoryObject(m_currentCategory)->renameItem(m_membersListWidget->currentItem()->text(), newTagName); MainWindow::DirtyIndicator::suppressMarkDirty(false); QMap categoryChange; categoryChange[CategoryEdit::Category] = m_currentCategory; categoryChange[CategoryEdit::Rename] = m_membersListWidget->currentItem()->text(); categoryChange[CategoryEdit::NewName] = newTagName; m_categoryChanges.append(categoryChange); // Update the displayed tag name m_membersListWidget->currentItem()->setText(newTagName); // Re-order the tags, as their alphabetial order may have changed m_membersListWidget->sortItems(); } void Settings::TagGroupsPage::slotDeleteMember() { QString memberToDelete = m_membersListWidget->currentItem()->text(); if (m_memberMap.groups(m_currentCategory).contains(memberToDelete)) { // The item to delete is a group // Find the tag in the tree view and select it ... QTreeWidgetItemIterator it(m_categoryTreeWidget); while (*it) { if ((*it)->text(0) == memberToDelete && getCategory((*it)) == m_currentCategory) { m_categoryTreeWidget->setCurrentItem((*it)); m_currentSubCategory = (*it)->text(0); m_currentSuperCategory = (*it)->parent()->text(0); break; } ++it; } // ... then delete it like it had been requested by the TreeWidget's context menu slotDeleteGroup(); } else { // The item to delete is a normal tag int res = KMessageBox::warningContinueCancel(this, i18nc("@info","Do you really want to delete \"%1\"?" "Deleting the item will remove any information " "about it from any image containing the item.", memberToDelete), i18nc("@title:window","Really delete %1?", memberToDelete), KGuiItem(i18n("&Delete"), QString::fromUtf8("editdelete")) ); if (res != KMessageBox::Continue) { return; } // Delete the tag as if it had been deleted from the annotation dialog. getCategoryObject(m_currentCategory)->removeItem(memberToDelete); slotPageChange(); } } DB::CategoryPtr Settings::TagGroupsPage::getCategoryObject(QString category) const { return DB::ImageDB::instance()->categoryCollection()->categoryForName(category); } // vi:expandtab:tabstop=4 shiftwidth=4: