Places Panel fixes

Implement adding, editing and removing of entries. Note that the
result currently is still not stored in bookmarks.xml (this needs
to wait until the hiding is implemented in the model).
This commit is contained in:
Peter Penz 2012-04-28 23:58:56 +02:00
parent 0a00f13bf2
commit b419cfc597
8 changed files with 301 additions and 112 deletions

View file

@ -18,6 +18,8 @@
***************************************************************************/
#include "kstandarditemmodel.h"
#include <KDebug>
#include "kstandarditem.h"
KStandardItemModel::KStandardItemModel(QObject* parent) :
@ -29,6 +31,9 @@ KStandardItemModel::KStandardItemModel(QObject* parent) :
KStandardItemModel::~KStandardItemModel()
{
qDeleteAll(m_items);
m_items.clear();
m_indexesForItems.clear();
}
void KStandardItemModel::insertItem(int index, KStandardItem* item)
@ -43,18 +48,56 @@ void KStandardItemModel::insertItem(int index, KStandardItem* item)
}
}
void KStandardItemModel::replaceItem(int index, KStandardItem* item)
{
if (index >= 0 && index < count()) {
QSet<QByteArray> changedRoles;
KStandardItem* oldItem= m_items[index];
const QHash<QByteArray, QVariant> oldData = oldItem->data();
const QHash<QByteArray, QVariant> newData = item->data();
// Determine which roles have been changed
QHashIterator<QByteArray, QVariant> it(oldData);
while (it.hasNext()) {
it.next();
const QByteArray role = it.key();
const QVariant oldValue = it.value();
if (newData.contains(role) && newData.value(role) != oldValue) {
changedRoles.insert(role);
}
}
m_indexesForItems.remove(oldItem);
delete oldItem;
oldItem = 0;
m_items[index] = item;
m_indexesForItems.insert(item, index);
emit itemsChanged(KItemRangeList() << KItemRange(index, 1), changedRoles);
} else {
kWarning() << "No item available to replace on the given index" << index;
delete item;
item = 0;
}
}
void KStandardItemModel::appendItem(KStandardItem *item)
{
insertItem(m_items.count(), item);
}
void KStandardItemModel::removeItem(KStandardItem* item)
void KStandardItemModel::removeItem(int index)
{
const int index = m_indexesForItems.value(item, -1);
if (index >= 0) {
m_items.removeAt(index);
if (index >= 0 && index < count()) {
KStandardItem* item = m_items[index];
m_indexesForItems.remove(item);
m_items.removeAt(index);
delete item;
item = 0;
emit itemsRemoved(KItemRangeList() << KItemRange(index, 1));
// TODO: no hierarchical items are handled yet
}
}
@ -79,9 +122,11 @@ int KStandardItemModel::count() const
QHash<QByteArray, QVariant> KStandardItemModel::data(int index) const
{
const KStandardItem* item = m_items[index];
if (item) {
return item->data();
if (index >= 0 && index < count()) {
const KStandardItem* item = m_items[index];
if (item) {
return item->data();
}
}
return QHash<QByteArray, QVariant>();
}

View file

@ -44,8 +44,9 @@ public:
virtual ~KStandardItemModel();
void insertItem(int index, KStandardItem* item);
void replaceItem(int index, KStandardItem* item);
void appendItem(KStandardItem* item);
void removeItem(KStandardItem* item);
void removeItem(int index);
KStandardItem* item(int index) const;
int index(const KStandardItem* item) const;

View file

@ -59,7 +59,7 @@ void PlacesItemEditDialog::setIcon(const QString& icon)
QString PlacesItemEditDialog::icon() const
{
return m_icon;
return m_iconButton->icon();
}
void PlacesItemEditDialog::setText(const QString& text)
@ -69,7 +69,7 @@ void PlacesItemEditDialog::setText(const QString& text)
QString PlacesItemEditDialog::text() const
{
return m_text;
return m_textEdit->text().isEmpty() ? m_urlEdit->url().fileName() : m_textEdit->text();
}
void PlacesItemEditDialog::setUrl(const KUrl& url)
@ -79,7 +79,7 @@ void PlacesItemEditDialog::setUrl(const KUrl& url)
KUrl PlacesItemEditDialog::url() const
{
return m_url;
return m_urlEdit->url();
}
void PlacesItemEditDialog::setAllowGlobal(bool allow)
@ -100,6 +100,11 @@ bool PlacesItemEditDialog::event(QEvent* event)
return QWidget::event(event);
}
void PlacesItemEditDialog::slotUrlChanged(const QString& text)
{
enableButtonOk(!text.isEmpty());
}
PlacesItemEditDialog::~PlacesItemEditDialog()
{
}
@ -122,6 +127,7 @@ void PlacesItemEditDialog::initialize()
formLayout->addRow(i18nc("@label", "Location:"), m_urlEdit);
// Provide room for at least 40 chars (average char width is half of height)
m_urlEdit->setMinimumWidth(m_urlEdit->fontMetrics().height() * (40 / 2));
connect(m_urlEdit->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(slotUrlChanged(QString)));
m_iconButton = new KIconButton(mainWidget);
formLayout->addRow(i18nc("@label", "Choose an icon:"), m_iconButton);
@ -152,7 +158,7 @@ void PlacesItemEditDialog::initialize()
m_textEdit->setFocus();
}
setMainWidget( mainWidget );
setMainWidget(mainWidget);
}
#include "placesitemeditdialog.moc"

View file

@ -55,6 +55,9 @@ public:
protected:
virtual bool event(QEvent* event);
private slots:
void slotUrlChanged(const QString& text);
private:
void initialize();

View file

@ -49,8 +49,8 @@ PlacesItemModel::PlacesItemModel(QObject* parent) :
m_nepomukRunning(false),
m_availableDevices(),
m_bookmarkManager(0),
m_defaultBookmarks(),
m_defaultBookmarksIndexes()
m_systemBookmarks(),
m_systemBookmarksIndexes()
{
#ifdef HAVE_NEPOMUK
m_nepomukRunning = (Nepomuk::ResourceManager::instance()->initialized());
@ -58,7 +58,7 @@ PlacesItemModel::PlacesItemModel(QObject* parent) :
const QString file = KStandardDirs::locateLocal("data", "kfileplaces/bookmarks.xml");
m_bookmarkManager = KBookmarkManager::managerForFile(file, "kfilePlaces");
createDefaultBookmarks();
createSystemBookmarks();
loadBookmarks();
}
@ -71,6 +71,49 @@ int PlacesItemModel::hiddenCount() const
return 0;
}
bool PlacesItemModel::isSystemItem(int index) const
{
if (index >= 0 && index < count()) {
const KUrl url = data(index).value("url").value<KUrl>();
return m_systemBookmarksIndexes.contains(url);
}
return false;
}
int PlacesItemModel::closestItem(const KUrl& url) const
{
int foundIndex = -1;
int maxLength = 0;
for (int i = 0; i < count(); ++i) {
const KUrl itemUrl = data(i).value("url").value<KUrl>();
if (itemUrl.isParentOf(url)) {
const int length = itemUrl.prettyUrl().length();
if (length > maxLength) {
foundIndex = i;
maxLength = length;
}
}
}
return foundIndex;
}
QString PlacesItemModel::placesGroupName() const
{
return i18nc("@item", "Places");
}
QString PlacesItemModel::recentlyAccessedGroupName() const
{
return i18nc("@item", "Recently Accessed");
}
QString PlacesItemModel::searchForGroupName() const
{
return i18nc("@item", "Search For");
}
QAction* PlacesItemModel::ejectAction(int index) const
{
Q_UNUSED(index);
@ -89,9 +132,9 @@ void PlacesItemModel::loadBookmarks()
KBookmark bookmark = root.first();
QSet<QString> devices = m_availableDevices;
QSet<KUrl> missingDefaultBookmarks;
foreach (const DefaultBookmarkData& data, m_defaultBookmarks) {
missingDefaultBookmarks.insert(data.url);
QSet<KUrl> missingSystemBookmarks;
foreach (const SystemBookmarkData& data, m_systemBookmarks) {
missingSystemBookmarks.insert(data.url);
}
while (!bookmark.isNull()) {
@ -109,16 +152,16 @@ void PlacesItemModel::loadBookmarks()
item->setDataValue("address", bookmark.address());
item->setDataValue("url", url);
if (missingDefaultBookmarks.contains(url)) {
missingDefaultBookmarks.remove(url);
// Apply the translated text to the default bookmarks, otherwise an outdated
if (missingSystemBookmarks.contains(url)) {
missingSystemBookmarks.remove(url);
// Apply the translated text to the system bookmarks, otherwise an outdated
// translation might be shown.
const int index = m_defaultBookmarksIndexes.value(url);
item->setText(m_defaultBookmarks[index].text);
const int index = m_systemBookmarksIndexes.value(url);
item->setText(m_systemBookmarks[index].text);
// The default bookmarks don't contain "real" queries stored as URLs, so
// The system bookmarks don't contain "real" queries stored as URLs, so
// they must be translated first.
item->setDataValue("url", translatedDefaultBookmarkUrl(url));
item->setDataValue("url", translatedSystemBookmarkUrl(url));
} else {
item->setText(bookmark.text());
}
@ -136,13 +179,13 @@ void PlacesItemModel::loadBookmarks()
bookmark = root.next(bookmark);
}
if (!missingDefaultBookmarks.isEmpty()) {
foreach (const DefaultBookmarkData& data, m_defaultBookmarks) {
if (missingDefaultBookmarks.contains(data.url)) {
if (!missingSystemBookmarks.isEmpty()) {
foreach (const SystemBookmarkData& data, m_systemBookmarks) {
if (missingSystemBookmarks.contains(data.url)) {
KStandardItem* item = new KStandardItem();
item->setIcon(KIcon(data.icon));
item->setText(data.text);
item->setDataValue("url", translatedDefaultBookmarkUrl(data.url));
item->setDataValue("url", translatedSystemBookmarkUrl(data.url));
item->setGroup(data.group);
appendItem(item);
}
@ -150,76 +193,77 @@ void PlacesItemModel::loadBookmarks()
}
}
void PlacesItemModel::createDefaultBookmarks()
void PlacesItemModel::createSystemBookmarks()
{
Q_ASSERT(m_defaultBookmarks.isEmpty());
Q_ASSERT(m_defaultBookmarksIndexes.isEmpty());
Q_ASSERT(m_systemBookmarks.isEmpty());
Q_ASSERT(m_systemBookmarksIndexes.isEmpty());
const QString placesGroup = i18nc("@item", "Places");
const QString recentlyAccessedGroup = i18nc("@item", "Recently Accessed");
const QString searchForGroup = i18nc("@item", "Search For");
const QString placesGroup = placesGroupName();
const QString recentlyAccessedGroup = recentlyAccessedGroupName();
const QString searchForGroup = searchForGroupName();
const QString timeLineIcon = "package_utility_time"; // TODO: Ask the Oxygen team to create
// a custom icon for the timeline-protocol
m_defaultBookmarks.append(DefaultBookmarkData(KUrl(KUser().homeDir()),
"user-home",
i18nc("@item", "Home"),
placesGroup));
m_defaultBookmarks.append(DefaultBookmarkData(KUrl("remote:/"),
"network-workgroup",
i18nc("@item", "Network"),
placesGroup));
m_defaultBookmarks.append(DefaultBookmarkData(KUrl("/"),
"folder-red",
i18nc("@item", "Root"),
placesGroup));
m_defaultBookmarks.append(DefaultBookmarkData(KUrl("trash:/"),
"user-trash",
i18nc("@item", "Trash"),
placesGroup));
m_systemBookmarks.append(SystemBookmarkData(KUrl(KUser().homeDir()),
"user-home",
i18nc("@item", "Home"),
placesGroup));
m_systemBookmarks.append(SystemBookmarkData(KUrl("remote:/"),
"network-workgroup",
i18nc("@item", "Network"),
placesGroup));
m_systemBookmarks.append(SystemBookmarkData(KUrl("/"),
"folder-red",
i18nc("@item", "Root"),
placesGroup));
m_systemBookmarks.append(SystemBookmarkData(KUrl("trash:/"),
"user-trash",
i18nc("@item", "Trash"),
placesGroup));
if (m_nepomukRunning) {
m_defaultBookmarks.append(DefaultBookmarkData(KUrl("timeline:/today"),
timeLineIcon,
i18nc("@item Recently Accessed", "Today"),
recentlyAccessedGroup));
m_defaultBookmarks.append(DefaultBookmarkData(KUrl("timeline:/yesterday"),
timeLineIcon,
i18nc("@item Recently Accessed", "Yesterday"),
recentlyAccessedGroup));
m_defaultBookmarks.append(DefaultBookmarkData(KUrl("timeline:/thismonth"),
timeLineIcon,
i18nc("@item Recently Accessed", "This Month"),
recentlyAccessedGroup));
m_defaultBookmarks.append(DefaultBookmarkData(KUrl("timeline:/lastmonth"),
timeLineIcon,
i18nc("@item Recently Accessed", "Last Month"),
recentlyAccessedGroup));
m_defaultBookmarks.append(DefaultBookmarkData(KUrl("search:/documents"),
"folder-txt",
i18nc("@item Commonly Accessed", "Documents"),
searchForGroup));
m_defaultBookmarks.append(DefaultBookmarkData(KUrl("search:/images"),
"folder-image",
i18nc("@item Commonly Accessed", "Images"),
searchForGroup));
m_defaultBookmarks.append(DefaultBookmarkData(KUrl("search:/audio"),
"folder-sound",
i18nc("@item Commonly Accessed", "Audio"),
searchForGroup));
m_defaultBookmarks.append(DefaultBookmarkData(KUrl("search:/videos"),
"folder-video",
i18nc("@item Commonly Accessed", "Videos"),
searchForGroup));
m_systemBookmarks.append(SystemBookmarkData(KUrl("timeline:/today"),
timeLineIcon,
i18nc("@item Recently Accessed", "Today"),
recentlyAccessedGroup));
m_systemBookmarks.append(SystemBookmarkData(KUrl("timeline:/yesterday"),
timeLineIcon,
i18nc("@item Recently Accessed", "Yesterday"),
recentlyAccessedGroup));
m_systemBookmarks.append(SystemBookmarkData(KUrl("timeline:/thismonth"),
timeLineIcon,
i18nc("@item Recently Accessed", "This Month"),
recentlyAccessedGroup));
m_systemBookmarks.append(SystemBookmarkData(KUrl("timeline:/lastmonth"),
timeLineIcon,
i18nc("@item Recently Accessed", "Last Month"),
recentlyAccessedGroup));
m_systemBookmarks.append(SystemBookmarkData(KUrl("search:/documents"),
"folder-txt",
i18nc("@item Commonly Accessed", "Documents"),
searchForGroup));
m_systemBookmarks.append(SystemBookmarkData(KUrl("search:/images"),
"folder-image",
i18nc("@item Commonly Accessed", "Images"),
searchForGroup));
m_systemBookmarks.append(SystemBookmarkData(KUrl("search:/audio"),
"folder-sound",
i18nc("@item Commonly Accessed", "Audio"),
searchForGroup));
m_systemBookmarks.append(SystemBookmarkData(KUrl("search:/videos"),
"folder-video",
i18nc("@item Commonly Accessed", "Videos"),
searchForGroup));
}
for (int i = 0; i < m_defaultBookmarks.count(); ++i) {
m_defaultBookmarksIndexes.insert(m_defaultBookmarks[i].url, i);
for (int i = 0; i < m_systemBookmarks.count(); ++i) {
const KUrl url = translatedSystemBookmarkUrl(m_systemBookmarks[i].url);
m_systemBookmarksIndexes.insert(url, i);
}
}
KUrl PlacesItemModel::translatedDefaultBookmarkUrl(const KUrl& url) const
KUrl PlacesItemModel::translatedSystemBookmarkUrl(const KUrl& url) const
{
KUrl translatedUrl = url;
if (url.protocol() == QLatin1String("timeline")) {

View file

@ -52,15 +52,34 @@ public:
int hiddenCount() const;
/**
* @return True if the item is a default item created by
* the system (e.g. the places for home, root, trash etc.)
*/
bool isSystemItem(int index) const;
/**
* Search the item which is equal to the URL or at least
* is a parent URL. If there are more than one possible
* candidates, return the item which covers the biggest
* range of the URL. -1 is returned if no closest item
* could be found.
*/
int closestItem(const KUrl& url) const;
QString placesGroupName() const;
QString recentlyAccessedGroupName() const;
QString searchForGroupName() const;
QAction* ejectAction(int index) const;
QAction* tearDownAction(int index) const;
private:
void loadBookmarks();
void createDefaultBookmarks();
void createSystemBookmarks();
KUrl translatedDefaultBookmarkUrl(const KUrl& url) const;
KUrl translatedSystemBookmarkUrl(const KUrl& url) const;
/**
* @return URL using the timeline-protocol for searching.
@ -96,9 +115,9 @@ private:
QSet<QString> m_availableDevices;
KBookmarkManager* m_bookmarkManager;
struct DefaultBookmarkData
struct SystemBookmarkData
{
DefaultBookmarkData(const KUrl& url,
SystemBookmarkData(const KUrl& url,
const QString& icon,
const QString& text,
const QString& group) :
@ -109,8 +128,8 @@ private:
QString group;
};
QList<DefaultBookmarkData> m_defaultBookmarks;
QHash<KUrl, int> m_defaultBookmarksIndexes;
QList<SystemBookmarkData> m_systemBookmarks;
QHash<KUrl, int> m_systemBookmarksIndexes;
};
#endif

View file

@ -24,6 +24,7 @@
#include "placespanel.h"
#include <KConfigGroup>
#include <KDebug>
#include <KDirNotify>
#include <KIcon>
#include <KIO/Job>
@ -31,6 +32,8 @@
#include <KLocale>
#include <kitemviews/kitemlistcontainer.h>
#include <kitemviews/kitemlistcontroller.h>
#include <kitemviews/kitemlistselectionmanager.h>
#include <kitemviews/kstandarditem.h>
#include <kitemviews/kstandarditemlistview.h>
#include <KMenu>
#include <KMessageBox>
@ -89,6 +92,8 @@ void PlacesPanel::showEvent(QShowEvent* event)
QVBoxLayout* layout = new QVBoxLayout(this);
layout->setMargin(0);
layout->addWidget(container);
selectClosestItem();
}
Panel::showEvent(event);
@ -124,6 +129,7 @@ void PlacesPanel::slotItemContextMenuRequested(int index, const QPointF& pos)
QAction* tearDownAction = 0;
QAction* ejectAction = 0;
const bool isSystemItem = m_model->isSystemItem(index);
const bool isDevice = !data.value("udi").toString().isEmpty();
if (isDevice) {
ejectAction = m_model->ejectAction(index);
@ -149,8 +155,10 @@ void PlacesPanel::slotItemContextMenuRequested(int index, const QPointF& pos)
menu.addSeparator();
}
addAction = menu.addAction(KIcon("document-new"), i18nc("@item:inmenu", "Add Entry..."));
mainSeparator = menu.addSeparator();
editAction = menu.addAction(KIcon("document-properties"), i18nc("@item:inmenu", "Edit Entry '%1'...", label));
if (!isSystemItem) {
mainSeparator = menu.addSeparator();
editAction = menu.addAction(KIcon("document-properties"), i18nc("@item:inmenu", "Edit Entry '%1'...", label));
}
}
if (!addAction) {
@ -172,7 +180,7 @@ void PlacesPanel::slotItemContextMenuRequested(int index, const QPointF& pos)
}
QAction* removeAction = 0;
if (!isDevice) {
if (!isDevice && !isSystemItem) {
removeAction = menu.addAction(KIcon("edit-delete"), i18nc("@item:inmenu", "Remove Entry '%1'", label));
}
@ -182,22 +190,23 @@ void PlacesPanel::slotItemContextMenuRequested(int index, const QPointF& pos)
}
QAction* action = menu.exec(pos.toPoint());
if (!action) {
return;
if (action) {
if (action == emptyTrashAction) {
emptyTrash();
} else if (action == addAction) {
addEntry();
} else if (action == editAction) {
editEntry(index);
} else if (action == removeAction) {
m_model->removeItem(index);
} else if (action == hideAction) {
} else if (action == showAllAction) {
} else if (action == tearDownAction) {
} else if (action == ejectAction) {
}
}
if (action == emptyTrashAction) {
emptyTrash();
} else if (action == addAction) {
addEntry();
} else if (action == editAction) {
editEntry(index);
} else if (action == removeAction) {
} else if (action == hideAction) {
} else if (action == showAllAction) {
} else if (action == tearDownAction) {
} else if (action == ejectAction) {
}
selectClosestItem();
}
void PlacesPanel::slotViewContextMenuRequested(const QPointF& pos)
@ -214,6 +223,8 @@ void PlacesPanel::slotViewContextMenuRequested(const QPointF& pos)
if (action == addAction) {
addEntry();
}
selectClosestItem();
}
void PlacesPanel::slotUrlsDropped(const KUrl& dest, QDropEvent* event, QWidget* parent)
@ -252,11 +263,30 @@ void PlacesPanel::emptyTrash()
void PlacesPanel::addEntry()
{
const int index = m_controller->selectionManager()->currentItem();
const KUrl url = m_model->data(index).value("url").value<KUrl>();
QPointer<PlacesItemEditDialog> dialog = new PlacesItemEditDialog(this);
dialog->setCaption(i18nc("@title:window", "Add Places Entry"));
dialog->setAllowGlobal(true);
dialog->setUrl(url);
if (dialog->exec() == QDialog::Accepted) {
// TODO
KStandardItem* item = createStandardItemFromDialog(dialog);
// Insert the item as last item of the "Places" group
bool inserted = false;
int i = 0;
while (!inserted && i < m_model->count()) {
if (m_model->item(i)->group() != m_model->placesGroupName()) {
m_model->insertItem(i, item);
inserted = true;
}
++i;
}
if (!inserted) {
m_model->appendItem(item);
}
}
delete dialog;
@ -264,7 +294,7 @@ void PlacesPanel::addEntry()
void PlacesPanel::editEntry(int index)
{
const QHash<QByteArray, QVariant> data = m_model->data(index);
QHash<QByteArray, QVariant> data = m_model->data(index);
QPointer<PlacesItemEditDialog> dialog = new PlacesItemEditDialog(this);
dialog->setCaption(i18nc("@title:window", "Edit Places Entry"));
@ -273,10 +303,37 @@ void PlacesPanel::editEntry(int index)
dialog->setUrl(data.value("url").value<KUrl>());
dialog->setAllowGlobal(true);
if (dialog->exec() == QDialog::Accepted) {
// TODO
KStandardItem* oldItem = m_model->item(index);
if (oldItem) {
KStandardItem* item = createStandardItemFromDialog(dialog);
item->setGroup(oldItem->group());
m_model->replaceItem(index, item);
}
}
delete dialog;
}
void PlacesPanel::selectClosestItem()
{
const int index = m_model->closestItem(url());
KItemListSelectionManager* selectionManager = m_controller->selectionManager();
selectionManager->setCurrentItem(index);
selectionManager->clearSelection();
selectionManager->setSelected(index);
}
KStandardItem* PlacesPanel::createStandardItemFromDialog(PlacesItemEditDialog* dialog) const
{
Q_ASSERT(dialog);
KStandardItem* item = new KStandardItem();
item->setIcon(KIcon(dialog->icon()));
item->setText(dialog->text());
item->setDataValue("url", dialog->url());
item->setGroup(m_model->placesGroupName());
return item;
}
#include "placespanel.moc"

View file

@ -26,7 +26,9 @@
#include <KUrl>
#include <panels/panel.h>
class KStandardItem;
class KItemListController;
class PlacesItemEditDialog;
class PlacesItemModel;
/**
@ -61,6 +63,18 @@ private:
void addEntry();
void editEntry(int index);
/**
* Selects the item that has the closest URL for the URL set
* for the panel (see Panel::setUrl()).
*/
void selectClosestItem();
/**
* @return New instance of a KStandardItem containing the properties that have
* been set in the places-dialog.
*/
KStandardItem* createStandardItemFromDialog(PlacesItemEditDialog* dialog) const;
private:
KItemListController* m_controller;
PlacesItemModel* m_model;