Patch to implement resource categorization via tags

s_suelzer at lavabit.com s_suelzer at lavabit.com
Tue May 14 15:13:07 UTC 2013


Hi. :)

I've attached a patch that improves the way the user can organize
resources into named sets that can be added, deleted and chosen from a
drop down menu that I've added to KoResourceItemChooser. This made the
changes available to the paintoppreset choosers, the gradient choosers
and the pattern choosers.

Currenly only adding and deleting categories is supported, renaming them
is in the code but lacks an UI feature, which I was unable to implement.
(I wanted the unfiltered view to be immutable and tried dis and enabling
the editable state of the drop down box based on which index was active,
but that introduced a segfault.)

I have removed the `show all' checkbox in the paintop preset docker and
all references to that functionality since it made filtering a bit more
complicated as well as just not making too much sense with the
improvements to the tagging system.

I have also removed the proxy adapter in KisPresetChooser since it
didn't do anything useful after these changes, yet required attention
since it overloaded some now improved functions.

Current shortcomings of the system:
Since I used the already existing KoResourceTagging class to handle all
the tagging bits, and ultimaltely save tags to disk, in alphabetical
order, restarting Krita, and thus repopulating the combo boxes also
fills them in alphabetical order and not in the order they might have
been defined by the user.

I also had a crash at one point in time where some brush presets got
changed around in master and clicking some resources in the brush preset
box crashed the program, but I was not able to reproduce that after a
clean rebuild of my local workspace and changing presets around
externally, so it might not be an issue, but for completeness sake ;).

And that's it.
Cheers~!
-------------- next part --------------
>From 0e4f294be60fc2c2283b938b104b7bde23c7d09d Mon Sep 17 00:00:00 2001
From: Sascha Suelzer <s.suelzer at lavabit.com>
Date: Mon, 13 May 2013 21:26:01 +0200
Subject: [PATCH] Implemented an easier way to mass tag resources.

- Resources can now be be categorized by tag.
  A tag set can be modified by entering in or exclusions in the edit
  box at the bottom of any resource chooser for a preview of the
  changes in the set based on them.
  Pressing return applies these changes and clears the edit box.
- Removed 'show all' checkbox and related code, including previous
  filtering on paintoppreset type, new tagging/filtering replaces it.
- Removed KisPresetProxyAdapter from KisPresetChooser
- Tooltips of resources now show which tags they belong to.
---
 .../dockers/presetdocker/presetdocker_dock.cpp     |   7 +-
 krita/ui/forms/wdgpaintoppresets.ui                |  33 +--
 krita/ui/kis_config.cc                             |  11 -
 krita/ui/kis_config.h                              |   3 -
 krita/ui/kis_paintop_box.cc                        |   1 -
 krita/ui/kis_palette_manager.cpp                   |  12 +-
 .../widgets/kis_paintop_presets_chooser_popup.cpp  |  39 +--
 .../ui/widgets/kis_paintop_presets_chooser_popup.h |   8 -
 krita/ui/widgets/kis_preset_chooser.cpp            | 145 +---------
 krita/ui/widgets/kis_preset_chooser.h              |  11 +-
 krita/ui/widgets/kis_preset_selector_strip.cpp     |   7 -
 libs/widgets/CMakeLists.txt                        |   1 +
 libs/widgets/KoPresetFiltering.cpp                 | 184 ++++++++++++
 libs/widgets/KoPresetFiltering.h                   |  61 ++++
 libs/widgets/KoResourceItemChooser.cpp             | 313 ++++++++++++++-------
 libs/widgets/KoResourceItemChooser.h               |  23 +-
 libs/widgets/KoResourceModel.cpp                   |  37 ++-
 libs/widgets/KoResourceModel.h                     |   4 +-
 libs/widgets/KoResourceServer.h                    |   5 +
 libs/widgets/KoResourceServerAdapter.h             |  78 +++--
 20 files changed, 591 insertions(+), 392 deletions(-)
 create mode 100644 libs/widgets/KoPresetFiltering.cpp
 create mode 100644 libs/widgets/KoPresetFiltering.h

diff --git a/krita/plugins/extensions/dockers/presetdocker/presetdocker_dock.cpp b/krita/plugins/extensions/dockers/presetdocker/presetdocker_dock.cpp
index 097ebd3..72885e7 100644
--- a/krita/plugins/extensions/dockers/presetdocker/presetdocker_dock.cpp
+++ b/krita/plugins/extensions/dockers/presetdocker/presetdocker_dock.cpp
@@ -63,12 +63,7 @@ void PresetDockerDock::setCanvas(KoCanvasBase * canvas)
 
 void PresetDockerDock::resourceChanged(int /*key*/, const QVariant& /*v*/)
 {
-    if (m_canvas) {
-        KisPaintOpPresetSP preset = m_canvas->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value<KisPaintOpPresetSP>();
-        if (preset) {
-            m_presetChooser->setPresetFilter(preset->paintOp());
-        }
-    }
+   
 }
 
 #include "presetdocker_dock.moc"
diff --git a/krita/ui/forms/wdgpaintoppresets.ui b/krita/ui/forms/wdgpaintoppresets.ui
index c2560a5..bb95281 100644
--- a/krita/ui/forms/wdgpaintoppresets.ui
+++ b/krita/ui/forms/wdgpaintoppresets.ui
@@ -18,28 +18,15 @@
   </property>
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
-    <layout class="QGridLayout" name="gridLayout_2" columnstretch="0,1,0" columnminimumwidth="0,0,0">
-     <item row="0" column="0">
-      <widget class="KLineEdit" name="searchBar">
-       <property name="clickMessage">
-        <string>Search Preset</string>
-       </property>
-       <property name="showClearButton" stdset="0">
-        <bool>true</bool>
-       </property>
-      </widget>
-     </item>
+    <layout class="QGridLayout" name="gridLayout_2" columnstretch="0,0">
      <item row="0" column="1">
-      <widget class="QCheckBox" name="showAllCheckBox">
+      <widget class="QToolButton" name="viewModeButton">
        <property name="text">
-        <string>Show all</string>
-       </property>
-       <property name="checked">
-        <bool>true</bool>
+        <string>...</string>
        </property>
       </widget>
      </item>
-     <item row="1" column="0" colspan="3">
+     <item row="1" column="0" colspan="2">
       <widget class="KisPresetChooser" name="wdgPresetChooser" native="true">
        <property name="sizePolicy">
         <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@@ -55,24 +42,12 @@
        </property>
       </widget>
      </item>
-     <item row="0" column="2">
-      <widget class="QToolButton" name="viewModeButton">
-       <property name="text">
-        <string>...</string>
-       </property>
-      </widget>
-     </item>
     </layout>
    </item>
   </layout>
  </widget>
  <customwidgets>
   <customwidget>
-   <class>KLineEdit</class>
-   <extends>QLineEdit</extends>
-   <header>klineedit.h</header>
-  </customwidget>
-  <customwidget>
    <class>KisPresetChooser</class>
    <extends>QWidget</extends>
    <header>widgets/kis_preset_chooser.h</header>
diff --git a/krita/ui/kis_config.cc b/krita/ui/kis_config.cc
index ae51c5c..c6e50da 100644
--- a/krita/ui/kis_config.cc
+++ b/krita/ui/kis_config.cc
@@ -736,17 +736,6 @@ void KisConfig::setPresetChooserViewMode(const int mode)
     m_cfg.writeEntry("presetChooserViewMode", mode);
 }
 
-
-bool KisConfig::presetShowAllMode() const
-{
-    return m_cfg.readEntry("presetChooserShowAllPresets", true);
-}
-
-void KisConfig::setPresetShowAllMode(bool showAll)
-{
-    m_cfg.writeEntry("presetChooserShowAllPresets", showAll);
-}
-
 bool KisConfig::firstRun() const
 {
     return m_cfg.readEntry("firstRun", true);
diff --git a/krita/ui/kis_config.h b/krita/ui/kis_config.h
index aaa1b3d..dace8b7 100644
--- a/krita/ui/kis_config.h
+++ b/krita/ui/kis_config.h
@@ -230,9 +230,6 @@ public:
     int presetChooserViewMode() const;
     void setPresetChooserViewMode(const int mode);
 
-    bool presetShowAllMode() const;
-    void setPresetShowAllMode(bool showAll);
-
     bool firstRun() const;
     void setFirstRun(const bool firstRun) const;
 
diff --git a/krita/ui/kis_paintop_box.cc b/krita/ui/kis_paintop_box.cc
index 17cf40b..61c0d12 100644
--- a/krita/ui/kis_paintop_box.cc
+++ b/krita/ui/kis_paintop_box.cc
@@ -372,7 +372,6 @@ void KisPaintopBox::setCurrentPaintop(const KoID& paintop, KisPaintOpPresetSP pr
     m_optionWidget->setConfiguration(preset->settings());
 
     m_presetsPopup->setPaintOpSettingsWidget(m_optionWidget);
-    m_presetsChooserPopup->setPresetFilter(paintop);
 
     Q_ASSERT(m_optionWidget && m_presetWidget);
     connect(m_optionWidget, SIGNAL(sigConfigurationUpdated()), this, SLOT(slotUpdatePreset()));
diff --git a/krita/ui/kis_palette_manager.cpp b/krita/ui/kis_palette_manager.cpp
index 326590d..ab692c5 100644
--- a/krita/ui/kis_palette_manager.cpp
+++ b/krita/ui/kis_palette_manager.cpp
@@ -67,14 +67,10 @@ KisPaletteManager::KisPaletteManager(KoFavoriteResourceManager *manager, KisPain
     m_allPresetsView = new KisPresetChooser(this);
     m_allPresetsView->showButtons(false);
     m_allPresetsView->showTaggingBar(false,false);
-    m_allPresetsView->setPresetFilter(KoID("dummy",""));
-    m_allPresetsView->setShowAll(true);
 
     m_palettePresetsView = new KisPresetChooser(this);
     m_palettePresetsView->showButtons(false);
     m_palettePresetsView->showTaggingBar(false,false);
-    m_palettePresetsView->setPresetFilter(KoID("dummy",""));
-    m_palettePresetsView->setShowAll(true);
 
     /*LEFT COMPONENTS*/
     QFrame *HSeparator = new QFrame();
@@ -211,7 +207,13 @@ void KisPaletteManager::showEvent(QShowEvent* e)
 
 void KisPaletteManager::updatePaletteView()
 {
-    m_palettePresetsView->setFilteredNames(m_resourceManager->favoritePresetList());
+    QStringList faves = m_resourceManager->favoritePresetList();
+    // this filters all the resources, making the view empty
+    // the palette view is a special case that basically uses all presets
+    // by virtue of using kis_preset_chooser even though it's only interested
+    // in up to ten of them. not sure if that warrants an 'empty' mode somewhere.
+    if (faves.isEmpty())faves.push_back("!!!!!!");
+    m_palettePresetsView->setFilteredNames(faves);
 }
 
 void KisPaletteManager::slotThumbnailMode()
diff --git a/krita/ui/widgets/kis_paintop_presets_chooser_popup.cpp b/krita/ui/widgets/kis_paintop_presets_chooser_popup.cpp
index 9f5a7f8..078c572 100644
--- a/krita/ui/widgets/kis_paintop_presets_chooser_popup.cpp
+++ b/krita/ui/widgets/kis_paintop_presets_chooser_popup.cpp
@@ -45,7 +45,6 @@ KisPaintOpPresetsChooserPopup::KisPaintOpPresetsChooserPopup(QWidget * parent)
     QActionGroup *actionGroup = new QActionGroup(this);
 
     KisPresetChooser::ViewMode mode = (KisPresetChooser::ViewMode)KisConfig().presetChooserViewMode();
-    bool showAll = KisConfig().presetShowAllMode();
 
     QAction* action = menu->addAction(koIcon("view-preview"), i18n("Thumbnails"), this, SLOT(slotThumbnailMode()));
     action->setCheckable(true);
@@ -61,38 +60,20 @@ KisPaintOpPresetsChooserPopup::KisPaintOpPresetsChooserPopup(QWidget * parent)
     m_d->uiWdgPaintOpPresets.viewModeButton->setMenu(menu);
     m_d->uiWdgPaintOpPresets.viewModeButton->setPopupMode(QToolButton::InstantPopup);
     m_d->uiWdgPaintOpPresets.wdgPresetChooser->setViewMode(mode);
-    m_d->uiWdgPaintOpPresets.wdgPresetChooser->showTaggingBar(false,true);
+    m_d->uiWdgPaintOpPresets.wdgPresetChooser->showTaggingBar(true,true);
 
     connect(m_d->uiWdgPaintOpPresets.wdgPresetChooser, SIGNAL(resourceSelected(KoResource*)),
             this, SIGNAL(resourceSelected(KoResource*)));
-
-    connect(m_d->uiWdgPaintOpPresets.searchBar, SIGNAL(textChanged(QString)),
-            m_d->uiWdgPaintOpPresets.wdgPresetChooser, SLOT(searchTextChanged(QString)));
-
-    connect(m_d->uiWdgPaintOpPresets.searchBar, SIGNAL(textChanged(QString)),
-                this, SLOT(setLineEditCompleter(QString)));
-
-    connect(m_d->uiWdgPaintOpPresets.searchBar, SIGNAL(returnPressed(QString)),
-                this, SLOT(returnKeyPressed(QString)));
-
-    connect(m_d->uiWdgPaintOpPresets.showAllCheckBox, SIGNAL(toggled(bool)),
-            m_d->uiWdgPaintOpPresets.wdgPresetChooser, SLOT(setShowAll(bool)));
+    
     m_d->firstShown = true;
 
-    m_d->uiWdgPaintOpPresets.showAllCheckBox->setChecked(showAll);
 }
 
 KisPaintOpPresetsChooserPopup::~KisPaintOpPresetsChooserPopup()
 {
-    KisConfig().setPresetShowAllMode(m_d->uiWdgPaintOpPresets.showAllCheckBox->isChecked());
     delete m_d;
 }
 
-void KisPaintOpPresetsChooserPopup::setPresetFilter(const KoID& paintopID)
-{
-    m_d->uiWdgPaintOpPresets.wdgPresetChooser->setPresetFilter(paintopID);
-}
-
 void KisPaintOpPresetsChooserPopup::slotThumbnailMode()
 {
     KisConfig().setPresetChooserViewMode(KisPresetChooser::THUMBNAIL);
@@ -115,22 +96,6 @@ void KisPaintOpPresetsChooserPopup::paintEvent(QPaintEvent* event)
     }
 }
 
-void KisPaintOpPresetsChooserPopup::setLineEditCompleter(const QString& searchString)
-{
-    QCompleter* tagCompleter = new QCompleter(m_d->uiWdgPaintOpPresets.wdgPresetChooser->getTagNamesList(searchString),this);
-    m_d->uiWdgPaintOpPresets.searchBar->setCompleter(tagCompleter);
-}
-
-void KisPaintOpPresetsChooserPopup::returnKeyPressed(QString lineEditText)
-{
-    m_d->uiWdgPaintOpPresets.wdgPresetChooser->returnKeyPressed(lineEditText);
-    if(!lineEditText.endsWith(", ")) {
-        lineEditText.append(", ");
-    }
-    m_d->uiWdgPaintOpPresets.searchBar->setText(lineEditText);
-    setLineEditCompleter(lineEditText);
-}
-
 void KisPaintOpPresetsChooserPopup::showButtons(bool show)
 {
     m_d->uiWdgPaintOpPresets.wdgPresetChooser->showButtons(show);
diff --git a/krita/ui/widgets/kis_paintop_presets_chooser_popup.h b/krita/ui/widgets/kis_paintop_presets_chooser_popup.h
index 69880ee..b0c9af7 100644
--- a/krita/ui/widgets/kis_paintop_presets_chooser_popup.h
+++ b/krita/ui/widgets/kis_paintop_presets_chooser_popup.h
@@ -34,10 +34,6 @@ public:
     KisPaintOpPresetsChooserPopup(QWidget * parent = 0);
     virtual ~KisPaintOpPresetsChooserPopup();
     
-    ///Set id for paintop to be accept by the proxy model
-    ///@param paintopID id of the paintop for which the presets will be shown
-    void setPresetFilter(const KoID & paintopID);
-
     void showButtons(bool show);
 signals:
     void resourceSelected( KoResource * resource );
@@ -45,10 +41,6 @@ signals:
 private slots:
     void slotThumbnailMode();
     void slotDetailMode();
-    /// Passes the lineEdit text to the preset Chooser when a returnPressed signal is generated
-    void returnKeyPressed(QString lineEditText);
-    /// Sets the lineEdit so that it can search for multiple tags like "round, circle, "
-    void setLineEditCompleter(const QString& searchString);
     virtual void paintEvent(QPaintEvent* );
    
 private:
diff --git a/krita/ui/widgets/kis_preset_chooser.cpp b/krita/ui/widgets/kis_preset_chooser.cpp
index 01f1b7b..0c42696 100644
--- a/krita/ui/widgets/kis_preset_chooser.cpp
+++ b/krita/ui/widgets/kis_preset_chooser.cpp
@@ -38,6 +38,8 @@
 #include <KoResourceModel.h>
 #include <KoResourceServerAdapter.h>
 
+#include <KoPresetFiltering.h>
+
 #include "kis_paintop_settings.h"
 #include "kis_paintop_preset.h"
 #include "kis_resource_server_provider.h"
@@ -109,100 +111,6 @@ void KisPresetDelegate::paint(QPainter * painter, const QStyleOptionViewItem & o
     painter->restore();
 }
 
-class KisPresetProxyAdapter : public KoResourceServerAdapter<KisPaintOpPreset>
-{
-    static bool compareKoResources(const KoResource* a, const KoResource* b)
-    {
-        return a->name() < b->name();
-    }
-
-public:
-    KisPresetProxyAdapter(KoResourceServer< KisPaintOpPreset >* resourceServer)
-        : KoResourceServerAdapter<KisPaintOpPreset>(resourceServer), m_filterNames(false)
-    {
-        m_showAll = KisConfig().presetShowAllMode();
-    }
-    virtual ~KisPresetProxyAdapter() {}
-
-    virtual QList< KoResource* > resources() {
-        if( ! resourceServer() )
-            return QList<KoResource*>();
-
-        QList<KisPaintOpPreset*> serverResources = resourceServer()->resources();
-
-        QList<KoResource*> resources;
-        foreach( KisPaintOpPreset* resource, serverResources ) {
-            if(filterAcceptsPreset(resource) || (m_filterNames && m_filteredNames.contains(resource->filename())) ) {
-                resources.append( resource );
-            }
-        }
-
-        qSort(resources.begin(), resources.end(), KisPresetProxyAdapter::compareKoResources);
-        return resources;
-    }
-
-    bool filterAcceptsPreset(KisPaintOpPreset* preset) const
-    {
-        if(m_paintopID.id().isEmpty())
-            return true;
-
-        return ((preset->paintOp() == m_paintopID || m_showAll) &&
-                preset->name().contains(m_nameFilter, Qt::CaseInsensitive) &&
-                (!m_filterNames || m_filteredNames.contains(preset->name())));
-    }
-
-    ///Set id for paintop to be accept by the proxy model, if not filter is set all
-    ///presets will be shown.
-    void setPresetFilter(const KoID &paintopID)
-    {
-        m_paintopID = paintopID;
-    }
-
-    KoID presetFilter()
-    {
-        return m_paintopID;
-    }
-
-    /// Set a filter for preset name, only presets with name containing the string will be shown
-    void setPresetNameFilter(const QString &nameFilter)
-    {
-        m_nameFilter = nameFilter;
-    }
-
-    void setFilteredNames(const QStringList filteredNames)
-    {
-        m_filteredNames = filteredNames;
-    }
-
-    void setFilterNames(bool filterNames)
-    {
-        m_filterNames = filterNames;
-    }
-
-    void setShowAll(bool show)
-    {
-        m_showAll = show;
-    }
-
-    bool showAll()
-    {
-        return m_showAll;
-    }
-
-
-    ///Resets the model connected to the adapter
-    void invalidate() {
-        emitRemovingResource(0);
-    }
-
-private:
-    KoID m_paintopID;
-    QString m_nameFilter;
-    bool m_showAll;
-    bool m_filterNames;
-    QStringList m_filteredNames;
-};
-
 KisPresetChooser::KisPresetChooser(QWidget *parent, const char *name)
     : QWidget(parent)
 {
@@ -210,8 +118,10 @@ KisPresetChooser::KisPresetChooser(QWidget *parent, const char *name)
     QVBoxLayout * layout = new QVBoxLayout(this);
     layout->setMargin(0);
     KoResourceServer<KisPaintOpPreset> * rserver = KisResourceServerProvider::instance()->paintOpPresetServer();
-    m_presetProxy = new KisPresetProxyAdapter(rserver);
-    m_chooser = new KoResourceItemChooser(m_presetProxy, this);
+
+    m_adapter = new KoResourceServerAdapter<KisPaintOpPreset>(rserver);
+
+    m_chooser = new KoResourceItemChooser(m_adapter, this);
     QString knsrcFile = "kritapresets.knsrc";
     m_chooser->setKnsrcFile(knsrcFile);
     m_chooser->showGetHotNewStuff(true, true);
@@ -232,39 +142,12 @@ KisPresetChooser::~KisPresetChooser()
 {
 }
 
-void KisPresetChooser::setPresetFilter(const KoID& paintopID)
-{
-    KoID oldFilter = m_presetProxy->presetFilter();
-    m_presetProxy->setPresetFilter(paintopID);
-    if(oldFilter.id() != paintopID.id() && !m_presetProxy->showAll()) {
-        m_presetProxy->invalidate();
-        updateViewSettings();
-    }
-}
-
 void KisPresetChooser::setFilteredNames(const QStringList filteredNames)
 {
-    m_presetProxy->setFilterNames(true);
-    m_presetProxy->setFilteredNames(filteredNames);
-    m_presetProxy->invalidate();
-    updateViewSettings();
-}
-
-void KisPresetChooser::searchTextChanged(const QString& searchString)
-{
-    if(searchString.isEmpty()) {
-        m_presetProxy->setFilterNames(false);
-    }
-    m_presetProxy->setPresetNameFilter(searchString);
-    m_presetProxy->invalidate();
-    updateViewSettings();
-}
+    m_adapter->setTaggedResourceFileNames(filteredNames);
+    m_adapter->enableResourceFiltering(true);
+    m_adapter->updateServer();
 
-void KisPresetChooser::returnKeyPressed(QString lineEditText)
-{
-    m_presetProxy->setFilterNames(true);
-    m_presetProxy->setFilteredNames(m_chooser->getTaggedResourceFileNames(lineEditText));
-    m_presetProxy->invalidate();
     updateViewSettings();
 }
 
@@ -273,13 +156,6 @@ QStringList KisPresetChooser::getTagNamesList(const QString& searchString)
     return m_chooser->getTagNamesList(searchString);
 }
 
-void KisPresetChooser::setShowAll(bool show)
-{
-    m_presetProxy->setShowAll(show);
-    m_presetProxy->invalidate();
-    updateViewSettings();
-}
-
 void KisPresetChooser::showButtons(bool show)
 {
     m_chooser->showButtons(show);
@@ -300,7 +176,7 @@ void KisPresetChooser::resizeEvent(QResizeEvent* event)
 void KisPresetChooser::updateViewSettings()
 {
     if (m_mode == THUMBNAIL) {
-        int resourceCount = m_presetProxy->resources().count();
+        int resourceCount = m_adapter->resources().count();
         int width = m_chooser->viewSize().width();
         int maxColums = width/50;
         int cols = width/100 + 1;
@@ -324,7 +200,6 @@ void KisPresetChooser::updateViewSettings()
         m_chooser->setColumnWidth(m_chooser->viewSize().height() - 7);
         m_delegate->setShowText(false);
     }
-
 }
 
 KoResource* KisPresetChooser::currentResource()
diff --git a/krita/ui/widgets/kis_preset_chooser.h b/krita/ui/widgets/kis_preset_chooser.h
index 0433173..6b4db49 100644
--- a/krita/ui/widgets/kis_preset_chooser.h
+++ b/krita/ui/widgets/kis_preset_chooser.h
@@ -24,8 +24,8 @@
 #include <krita_export.h>
 #include <KoID.h>
 
+class KoAbstractResourceServerAdapter;
 class KisPresetDelegate;
-class KisPresetProxyAdapter;
 class KoResourceItemChooser;
 class KoResource;
 
@@ -48,10 +48,6 @@ public:
         DETAIL,  /// Shows thumbsnails with text next to it
         STRIP  /// Shows thumbnails arranged in a single row
     };
-    
-    ///Set id for paintop to be accept by the proxy model
-    ///@param paintopID id of the paintop for which the presets will be shown
-    void setPresetFilter(const KoID & paintopID);
 
     /// Set a list of preset names for resources that should be show, others will be hidden
     /// Turns on name filter mode
@@ -60,7 +56,6 @@ public:
     /// get tag names and used to set Completer object in paintop_presets_popup class
     QStringList getTagNamesList(const QString& searchString);
     /// Sets a list of resources in the paintop list, when ever user press enter in the linedit of paintop_presets_popup Class
-    void returnKeyPressed(QString lineEditText);
     void setViewMode(ViewMode mode);
     void showButtons(bool show);
     
@@ -75,8 +70,6 @@ signals:
     void resourceSelected(KoResource * resource);
     
 public slots:
-    void searchTextChanged(const QString& searchString);
-    void setShowAll(bool show);
     void updateViewSettings();
     
 protected:
@@ -84,9 +77,9 @@ protected:
     
 private:
     KoResourceItemChooser *m_chooser;
-    KisPresetProxyAdapter *m_presetProxy;
     KisPresetDelegate* m_delegate;
     ViewMode m_mode;
+    KoAbstractResourceServerAdapter * m_adapter;
 };
 
 #endif // KIS_ITEM_CHOOSER_H_
diff --git a/krita/ui/widgets/kis_preset_selector_strip.cpp b/krita/ui/widgets/kis_preset_selector_strip.cpp
index 068c745..4eddfc4 100644
--- a/krita/ui/widgets/kis_preset_selector_strip.cpp
+++ b/krita/ui/widgets/kis_preset_selector_strip.cpp
@@ -45,7 +45,6 @@ KisPresetSelectorStrip::KisPresetSelectorStrip(QWidget* parent)
     /* This is an heuristic to fill smallPresetChooser with only the presets
      * for the paintop that comes selected by default: Pixel Brush. */
     const QString PIXEL_BRUSH_ID = "paintbrush";
-    smallPresetChooser->setPresetFilter(KoID(PIXEL_BRUSH_ID));
 
     deletePresetBtn->setIcon(koIcon("trash-empty"));
     deletePresetBtn->setVisible(true);
@@ -70,12 +69,6 @@ void KisPresetSelectorStrip::showEvent(QShowEvent* event)
 
 void KisPresetSelectorStrip::currentPaintopChanged(QString paintOpID)
 {
-    foreach (const KoID &paintOp, KisPaintOpRegistry::instance()->listKeys()) {
-        if (paintOp.id() == paintOpID) {
-            smallPresetChooser->setPresetFilter(paintOp);
-            break;
-        }
-    }
     deletePresetBtn->hide();
 }
 
diff --git a/libs/widgets/CMakeLists.txt b/libs/widgets/CMakeLists.txt
index 4e6e362..3cdfbe6 100644
--- a/libs/widgets/CMakeLists.txt
+++ b/libs/widgets/CMakeLists.txt
@@ -66,6 +66,7 @@ set(kowidgets_LIB_SRCS
     KoDockWidgetTitleBarButton.cpp
     KoViewItemContextBar.cpp
     KoContextBarButton.cpp
+    KoPresetFiltering.cpp
 
 )
 
diff --git a/libs/widgets/KoPresetFiltering.cpp b/libs/widgets/KoPresetFiltering.cpp
new file mode 100644
index 0000000..4682201
--- /dev/null
+++ b/libs/widgets/KoPresetFiltering.cpp
@@ -0,0 +1,184 @@
+/*  This file is part of the KDE project
+
+    Copyright (C) 2013 Sascha Suelzer <s.suelzer at lavabit.com>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "KoPresetFiltering.h"
+#include "KoResourceTagging.h"
+
+class KoPresetFiltering::Private
+{
+public:
+    Private()
+    : m_isTag("\\[(.+)\\]")
+    , m_searchTokenizer("\\s*,+\\s*")
+    , m_hasNewFilters(false)
+    {}
+    bool m_hasNewFilters;
+    QRegExp m_isTag;
+    QRegExp m_searchTokenizer;
+    QStringList m_rootTagList;
+    QStringList m_includedNames;
+    QStringList m_excludedNames;
+};
+
+KoPresetFiltering::KoPresetFiltering() : d(new Private())
+{}
+
+KoPresetFiltering::~KoPresetFiltering()
+{
+    delete d;
+}
+void KoPresetFiltering::setChanged()
+{
+    d->m_hasNewFilters = true;
+}
+
+void KoPresetFiltering::setRootResourceFilenames(QStringList tag)
+{
+    d->m_rootTagList = tag;
+    d->m_excludedNames.clear();
+    d->m_includedNames.clear();
+    setChanged();
+}
+
+bool KoPresetFiltering::matchesResource(QString &resourceName, QString &resourceFileName,const QStringList &filterList) const
+{
+    foreach (QString filter, filterList) {
+        if (resourceName.contains(filter) || resourceFileName.contains(filter))
+            return true;
+    }
+    return false;
+}
+
+void KoPresetFiltering::sanitizeExclusionList()
+{
+   if(!d->m_includedNames.isEmpty()) {
+
+        foreach (QString exclusion, d->m_excludedNames) {
+            if (!excludeFilterIsValid(exclusion))
+                d->m_excludedNames.removeAll(exclusion);
+        }
+    }
+}
+
+QStringList KoPresetFiltering::tokenizeSearchString(const QString searchString)
+{
+    return searchString.split(d->m_searchTokenizer, QString::SkipEmptyParts);
+}
+
+void KoPresetFiltering::populateIncludeExcludeFilters(const QStringList& filteredNames, KoResourceTagging* tagObject)
+{
+    foreach (QString name, filteredNames) {
+        QStringList* target;
+
+        if(name.startsWith("!")) {
+            name.remove("!");
+            target = &d->m_excludedNames;
+        } else {
+            target = &d->m_includedNames;
+        }
+
+        if(!name.isEmpty()) {
+            if (!name.startsWith("[")) {
+                target->push_back(name);
+            }
+            else if (d->m_isTag.exactMatch(name) && tagObject) {
+                name = d->m_isTag.cap(1);
+                (*target) += tagObject->searchTag(name);
+            }
+        }
+    }
+    sanitizeExclusionList();
+}
+
+bool KoPresetFiltering::hasFilters()
+{
+    return (!d->m_rootTagList.isEmpty() || !d->m_includedNames.isEmpty() || !d->m_excludedNames.isEmpty());
+}
+
+bool KoPresetFiltering::filtersHaveChanged()
+{
+    return d->m_hasNewFilters;
+}
+
+void KoPresetFiltering::setFilters(const QString &searchString, KoResourceTagging * tagObject)
+{
+    d->m_excludedNames.clear();
+    d->m_includedNames.clear();
+    QStringList filteredNames = tokenizeSearchString(searchString);
+    populateIncludeExcludeFilters(filteredNames,tagObject);
+    setChanged();
+}
+
+bool KoPresetFiltering::presetMatchesSearch(KoResource * resource) const
+{
+
+    QString resourceName = resource->name();
+    QString resourceFileName = resource->filename();
+    if (matchesResource(resourceName,resourceFileName,d->m_excludedNames))
+        return false;
+    if (matchesResource(resourceName,resourceFileName,d->m_includedNames))
+        return true;
+    foreach (QString filter, d->m_rootTagList) {
+        if (!resourceFileName.compare(filter) || !resourceName.compare(filter))
+            return true;
+    }
+    return false;
+}
+void KoPresetFiltering::setInclusions(QStringList inclusions)
+{
+    d->m_includedNames = inclusions;
+    setChanged();
+}
+void KoPresetFiltering::setExclusions(QStringList exclusions)
+{
+    d->m_excludedNames = exclusions;
+    setChanged();
+}
+
+/// Checks an exclusion filter against the inclusion list.
+/// Exclusion filters that match an inclusion filter must
+/// exceed the length of the inclusion filter to be valid.
+bool KoPresetFiltering::excludeFilterIsValid(const QString &exclusion)
+{
+    foreach(QString inclusion, d->m_includedNames) {
+        if ((inclusion.startsWith(exclusion)  && exclusion.size() <= inclusion.size())) {
+            return false;
+        }
+    }
+    return true;
+}
+QList< KoResource* > KoPresetFiltering::filterResources(QList< KoResource* > resources)
+{
+
+    foreach(KoResource* resource, resources) {
+        if(!presetMatchesSearch(resource)) {
+            resources.removeAll(resource);
+        }
+    }
+    setDoneFiltering();
+    return resources;
+
+}
+
+// this would be better with an observer or delegate.
+void KoPresetFiltering::setDoneFiltering()
+{
+    d->m_hasNewFilters = false;
+}
+
diff --git a/libs/widgets/KoPresetFiltering.h b/libs/widgets/KoPresetFiltering.h
new file mode 100644
index 0000000..dd9e947
--- /dev/null
+++ b/libs/widgets/KoPresetFiltering.h
@@ -0,0 +1,61 @@
+
+/*  This file is part of the KDE project
+
+    Copyright (C) 2013 Sascha Suelzer <s.suelzer at lavabit.com>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef KISPRESETFILTER_H
+#define KISPRESETFILTER_H
+
+#include <QStringList>
+#include <QString>
+
+#include "KoResource.h"
+
+#include "kowidgets_export.h"
+
+class KoResourceTagging;
+
+class KOWIDGETS_EXPORT KoPresetFiltering
+{
+
+public:
+    KoPresetFiltering();
+    virtual ~KoPresetFiltering();
+    bool hasFilters();
+    bool filtersHaveChanged();
+    void setRootResourceFilenames(QStringList tag);
+    void setFilters(const QString& searchString, KoResourceTagging* tagObject);
+    QList<KoResource*> filterResources(QList<KoResource*> resources);
+
+private:
+    void setInclusions(QStringList inclusions);
+    void setExclusions(QStringList exclusions);
+    void setDoneFiltering();
+    bool presetMatchesSearch(KoResource * resource) const;
+    void setChanged();
+    bool excludeFilterIsValid(const QString &exclusion);
+    bool matchesResource(QString &resourceName, QString &resourceFileName,const QStringList &filterList) const;
+    void populateIncludeExcludeFilters(const QStringList& filteredNames, KoResourceTagging* tagObject);
+    void sanitizeExclusionList();
+    QStringList tokenizeSearchString(const QString searchString);
+    class Private;
+    Private * const d;
+
+};
+
+#endif // KISPRESETFILTER_H
diff --git a/libs/widgets/KoResourceItemChooser.cpp b/libs/widgets/KoResourceItemChooser.cpp
index 61273fe..79f5314 100644
--- a/libs/widgets/KoResourceItemChooser.cpp
+++ b/libs/widgets/KoResourceItemChooser.cpp
@@ -34,11 +34,13 @@
 #include <QPainter>
 #include <QToolButton>
 #include <QSplitter>
+#include <QInputDialog>
 
 #include <kfiledialog.h>
 #include <klocale.h>
 #include <kdebug.h>
 #include <klineedit.h>
+#include <kcombobox.h>
 
 
 #ifdef GHNS
@@ -55,9 +57,17 @@
 #include "KoResourceModel.h"
 #include "KoResource.h"
 
+// Don't know enough Qt/KDE GUI stuff, yet. I'm putting my interface code here
+// to hopefully make swapping it out for something better easier.
+class TagCategoryInterface
+{
+    
+};
+
 class KoResourceItemChooser::Private
 {
 public:
+    enum TagButtons { Button_AddTag, Button_RemoveTag };
     Private()
         : model(0)
         , view(0)
@@ -68,7 +78,9 @@ public:
     KoResourceModel* model;
     KoResourceItemView* view;
     QButtonGroup* buttonGroup;
-    KLineEdit *tagSearchLineEdit, *tagOpLineEdit;
+    QButtonGroup* tagButtonGroup;
+    KLineEdit *tagSearchLineEdit;
+    KComboBox *tagOpComboBox;
     QString knsrcFile;
     QCompleter *tagCompleter;
     QScrollArea *previewScroller;
@@ -76,6 +88,9 @@ public:
     QSplitter *splitter;
     bool tiledPreview;
     bool grayscalePreview;
+    QString currentTag;
+    QList<KoResource*> originalResources;
+
 };
 
 KoResourceItemChooser::KoResourceItemChooser(KoAbstractResourceServerAdapter * resourceAdapter, QWidget *parent )
@@ -108,18 +123,54 @@ KoResourceItemChooser::KoResourceItemChooser(KoAbstractResourceServerAdapter * r
     d->buttonGroup = new QButtonGroup( this );
     d->buttonGroup->setExclusive( false );
 
-    d->tagSearchLineEdit = new KLineEdit(this);
-    d->tagSearchLineEdit->setClearButtonShown(true);
-    d->tagSearchLineEdit->setClickMessage("Enter search tag here");
-    d->tagSearchLineEdit->setEnabled( true );
-    d->tagSearchLineEdit->hide();
+    d->tagButtonGroup = new QButtonGroup( this );
+    d->tagButtonGroup->setExclusive( false );
 
-    connect( d->tagSearchLineEdit, SIGNAL(returnPressed(QString)), this, SLOT(tagSearchLineEditActivated(QString)));
-    connect( d->tagSearchLineEdit, SIGNAL(textChanged(QString)), this, SLOT(tagSearchLineEditTextChanged(QString)));
+    d->tagOpComboBox = new KComboBox( this );
+    d->tagOpComboBox->setInsertPolicy(KComboBox::NoInsert);
+    d->tagOpComboBox->setEnabled( true );
+    d->tagOpComboBox->hide();
+
+    addTagToComboBox("Unfiltered View",false);
+
+    QStringList tagNames = d->model->getTagNamesList();
+    foreach (QString tagName, tagNames) {
+        addTagToComboBox(tagName,false);
+    }
+
+    connect(d->tagOpComboBox, SIGNAL(returnPressed(QString)), this, SLOT(tagChooserReturnPressed(QString)));
+    connect(d->tagOpComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(tagChooserIndexChanged(QString)));
 
     QVBoxLayout* layout = new QVBoxLayout( this );
+    QGridLayout* comboLayout = new QGridLayout;
+
     layout->setMargin(0);
-    layout->addWidget( d->tagSearchLineEdit );
+    comboLayout->addWidget( d->tagOpComboBox, 0, 0 );
+
+    QPushButton *tagButton = new QPushButton( this );
+
+    tagButton->setText("+");
+    tagButton->setToolTip( "Add Tag" );
+    tagButton->setEnabled( true );
+    d->tagButtonGroup->addButton( tagButton, Private::Button_AddTag );
+    tagButton->hide();
+    comboLayout->addWidget( tagButton, 0, 1 );
+
+    tagButton = new QPushButton( this );
+    tagButton->setText("-");
+    tagButton->setToolTip( "Remove Tag" );
+    tagButton->setEnabled( true );
+    tagButton->hide();
+    d->tagButtonGroup->addButton( tagButton, Private::Button_RemoveTag );
+    comboLayout->addWidget( tagButton, 0, 2 );
+
+    connect( d->tagButtonGroup, SIGNAL( buttonClicked( int ) ), this, SLOT( slotTagButtonClicked( int ) ));
+
+    comboLayout->setSpacing( 0 );
+    comboLayout->setMargin( 0 );
+    comboLayout->setColumnStretch(0,3);
+
+    layout->addLayout(comboLayout);
     layout->addWidget( d->splitter );
 
     QGridLayout* buttonLayout = new QGridLayout;
@@ -162,15 +213,17 @@ KoResourceItemChooser::KoResourceItemChooser(KoAbstractResourceServerAdapter * r
     buttonLayout->setSpacing( 0 );
     buttonLayout->setMargin( 0 );
 
-    d->tagOpLineEdit = new KLineEdit( this );
-    d->tagOpLineEdit->setClickMessage("Add / Remove Tag");
-    d->tagOpLineEdit->setEnabled( false );
-    d->tagOpLineEdit->hide();
 
-    connect(d->tagOpLineEdit, SIGNAL(returnPressed(QString)), this, SLOT(tagOpLineEditActivated(QString)));
-    connect(d->tagOpLineEdit, SIGNAL(textChanged(QString)), this, SLOT(tagOpLineEditTextChanged(QString)));
+    d->tagSearchLineEdit = new KLineEdit(this);
+    d->tagSearchLineEdit->setClearButtonShown(true);
+    d->tagSearchLineEdit->setClickMessage("Switch to/create a Tag to filter");
+    d->tagSearchLineEdit->setEnabled( false );
+    d->tagSearchLineEdit->hide();
 
-    layout->addWidget( d->tagOpLineEdit );
+    connect( d->tagSearchLineEdit, SIGNAL(returnPressed(QString)), this, SLOT(tagSearchLineEditActivated(QString)));
+    connect( d->tagSearchLineEdit, SIGNAL(textChanged(QString)), this, SLOT(tagSearchLineEditTextChanged(QString)));
+
+    layout->addWidget( d->tagSearchLineEdit );
     layout->addLayout( buttonLayout );
 
     d->tagCompleter = new QCompleter(getTagNamesList(""),this);
@@ -277,7 +330,10 @@ void KoResourceItemChooser::showGetHotNewStuff( bool showDownload, bool showUplo
 void KoResourceItemChooser::showTaggingBar(bool showSearchBar, bool showOpBar)
 {
     showSearchBar ? d->tagSearchLineEdit->show() : d->tagSearchLineEdit->hide();
-    showOpBar ? d->tagOpLineEdit->show() : d->tagOpLineEdit->hide();
+    showOpBar ? d->tagOpComboBox->show() : d->tagOpComboBox->hide();
+
+    foreach( QAbstractButton * button, d->tagButtonGroup->buttons() )
+        showOpBar ? button->show() : button->hide();
 }
 
 void KoResourceItemChooser::setRowCount( int rowCount )
@@ -331,7 +387,6 @@ void KoResourceItemChooser::setCurrentResource(KoResource* resource)
         return;
 
     d->view->setCurrentIndex(index);
-    setTagOpLineEdit(d->model->getAssignedTagsList(resource));
     updatePreview(resource);
 }
 
@@ -365,8 +420,6 @@ void KoResourceItemChooser::setCurrentItem(int row, int column)
     if (index.isValid()) {
         updatePreview(resourceFromModelIndex(index));
     }
-
-
 }
 
 void KoResourceItemChooser::setProxyModel( QAbstractProxyModel* proxyModel )
@@ -380,7 +433,6 @@ void KoResourceItemChooser::activated(const QModelIndex &/*index*/)
     KoResource* resource = currentResource();
     if (resource) {
         emit resourceSelected( resource );
-        setTagOpLineEdit(d->model->getAssignedTagsList(resource));
         updatePreview(resource);
         updateButtonState();
     }
@@ -400,13 +452,13 @@ void KoResourceItemChooser::updateButtonState()
     if( resource ) {
         removeButton->setEnabled( true );
         uploadButton->setEnabled(resource->removable());
-        d->tagOpLineEdit->setEnabled(true);
+        d->tagOpComboBox->setEnabled(true);
         return;
     }
 
     removeButton->setEnabled( false );
     uploadButton->setEnabled(false);
-    d->tagOpLineEdit->setEnabled( false );
+    d->tagOpComboBox->setEnabled( true );
 }
 
 void KoResourceItemChooser::updatePreview(KoResource *resource)
@@ -459,6 +511,11 @@ void KoResourceItemChooser::updatePreview(KoResource *resource)
 
 }
 
+QString KoResourceItemChooser::makeValidName(QString string)
+{
+    return string;
+}
+
 KoResource* KoResourceItemChooser::resourceFromModelIndex(const QModelIndex& index)
 {
     if(!index.isValid())
@@ -484,69 +541,6 @@ QSize KoResourceItemChooser::viewSize()
     return d->view->size();
 }
 
-void KoResourceItemChooser::setTagOpLineEdit(QStringList tagsList)
-{
-    QString tags;
-    if(!tagsList.isEmpty()) {
-        tagsList.sort();
-        tags = tagsList.join(", ");
-        tags.append(", ");
-        d->tagOpLineEdit->setText(tags);
-    }
-    else
-    {
-        d->tagOpLineEdit->clear();
-    }
-
-    d->tagCompleter = new QCompleter(getTagNamesList(tags),this);
-    d->tagOpLineEdit->setCompleter(d->tagCompleter);
-}
-
-
-void KoResourceItemChooser::tagOpLineEditActivated(QString lineEditText)
-{
-    QStringList tagsListNew = lineEditText.split(", ");
-    if(tagsListNew.contains("")) {
-        tagsListNew.removeAll("");
-    }
-
-    KoResource* resource = currentResource();
-    if(!resource) {
-        return;
-    }
-    QStringList tagsList = d->model->getAssignedTagsList(resource);
-
-    foreach(const QString& tag, tagsListNew) {
-        if(!tagsList.contains(tag)) {
-            d->model->addTag(resource, tag);
-        }
-    }
-
-    foreach(const QString& tag, tagsList) {
-        if(!tagsListNew.contains(tag)) {
-            d->model->deleteTag(resource, tag);
-        }
-    }
-
-    setTagOpLineEdit( d->model->getAssignedTagsList(resource));
-}
-
-void KoResourceItemChooser::tagOpLineEditTextChanged(QString lineEditText)
-{
-    KoResource* resource = currentResource();
-    if(!resource) {
-        return;
-    }
-    if(lineEditText.isEmpty()) {
-        QStringList assignedTagsList = d->model->getAssignedTagsList(resource);
-        foreach(const QString& tag, assignedTagsList) {
-            d->model->deleteTag(resource, tag);
-        }
-    }
-    d->tagCompleter = new QCompleter(getTagNamesList(lineEditText),this);
-    d->tagOpLineEdit->setCompleter(d->tagCompleter);
-}
-
 QStringList KoResourceItemChooser::getTagNamesList(QString lineEditText)
 {
     QStringList tagNamesList = d->model->getTagNamesList();
@@ -586,32 +580,155 @@ KoResourceItemView *KoResourceItemChooser::itemView()
 
 QStringList KoResourceItemChooser::getTaggedResourceFileNames(QString lineEditText)
 {
-    return d->model->searchTag(lineEditText);
+    return QStringList();
 }
 
 void KoResourceItemChooser::tagSearchLineEditActivated(QString lineEditText)
 {
-    d->model->setTagSearch(true);
-    d->model->setTaggedResourceFileNames(getTaggedResourceFileNames(lineEditText));
+    QList<KoResource*> newResources = d->model->currentlyVisibleResources();
+    foreach(KoResource * oldRes, d->originalResources) {
+        if (!newResources.contains(oldRes))
+            removeResourceTag(oldRes,d->currentTag);
+    }
+    foreach(KoResource * newRes, newResources) {
+        if (!d->originalResources.contains(newRes))
+            addResourceTag(newRes,d->currentTag);
+    }
+    d->model->setTaggedResourceFileNames(d->model->searchTag(d->currentTag));
+    d->model->updateServer();
+    d->originalResources = d->model->currentlyVisibleResources();
+    d->tagSearchLineEdit->clear();
+}
+
+void KoResourceItemChooser::tagSearchLineEditTextChanged(QString lineEditText)
+{
+    d->model->searchTextChanged(lineEditText);
     d->model->updateServer();
 
-    if(!lineEditText.endsWith(", ")) {
-        lineEditText.append(", ");
-    }
     d->tagCompleter = new QCompleter(getTagNamesList(lineEditText),this);
     d->tagSearchLineEdit->setCompleter(d->tagCompleter);
-    d->tagSearchLineEdit->setText( lineEditText );
+}
+
+void KoResourceItemChooser::tagChooserIndexChanged(QString lineEditText)
+{
+    int index = d->tagOpComboBox->currentIndex();
 
+    if ( index > 0) {
+        d->currentTag = lineEditText;
+        d->model->enableResourceFiltering(true);
+        d->tagSearchLineEdit->setClickMessage("Type presets or [Tag]s to in or !exclude here.");
+        d->tagSearchLineEdit->setEnabled(true);
+    }
+    else {
+        d->model->enableResourceFiltering(false);
+        d->tagSearchLineEdit->setClickMessage("Create a Tag to enable filtering.");
+        d->tagSearchLineEdit->setEnabled(false);
+        d->currentTag.clear();
+    }
+    // sets the current tag as the base for filtering, non destructive until user presses return
+    // in search bar, then tags get updated.
+    d->model->setTaggedResourceFileNames(d->model->searchTag(d->currentTag));
+    d->tagSearchLineEdit->clear();
+    d->model->updateServer();
+    d->originalResources = d->model->currentlyVisibleResources();
 }
 
-void KoResourceItemChooser::tagSearchLineEditTextChanged(QString lineEditText)
+// TODO: put the newName.compare check in the add index function
+QString KoResourceItemChooser::renameTag(QString oldName, QString newName)
 {
-    if(lineEditText.isEmpty()) {
-        d->model->setTagSearch(false);
-        d->model->updateServer();
+
+    if (!newName.compare(oldName)) {
+        d->tagOpComboBox->setEditText(oldName);
+        return oldName;
     }
+
+    QList<KoResource*> resources = d->model->currentlyVisibleResources();
+    foreach(KoResource * resource, resources) {
+        QStringList tagsList = d->model->getAssignedTagsList(resource);
+
+        foreach(QString tagname, tagsList) {
+            if(!tagname.compare(oldName)) {
+                d->model->deleteTag(resource, oldName);
+            }
+            if(tagname.compare(newName)) {
+                d->model->addTag(resource, newName);
+            }
+        }
+    }
+    return oldName;
+}
+void KoResourceItemChooser::tagChooserReturnPressed(QString lineEditText)
+{
+    int index = d->tagOpComboBox->currentIndex();
+    QString oldTagname = d->tagOpComboBox->itemText(index);
+    QString newTagName = renameTag(oldTagname, lineEditText);
+    d->tagOpComboBox->setItemText(index,newTagName);
+    d->currentTag = newTagName;
+
     d->tagCompleter = new QCompleter(getTagNamesList(lineEditText),this);
-    d->tagSearchLineEdit->setCompleter(d->tagCompleter);
+    d->tagOpComboBox->setCompleter(d->tagCompleter);
+
+}
+
+void KoResourceItemChooser::addTagToComboBox(QString tagName, bool followEntry)
+{
+    d->tagOpComboBox->addItem(tagName);
+    int pos = d->tagOpComboBox->findText(tagName);
+    if (pos != -1 && followEntry) {
+        d->tagOpComboBox->setCurrentIndex(pos);
+    }
+}
+
+void KoResourceItemChooser::removeTagFromComboBox()
+{
+    int index = d->tagOpComboBox->currentIndex();
+    if (index > 0) {
+        QList<KoResource*> resources = d->model->currentlyVisibleResources();
+        foreach(KoResource * resource, resources) {
+            removeResourceTag(resource,d->currentTag);
+        }
+        d->tagOpComboBox->removeItem(index);
+    }
+}
+void KoResourceItemChooser::slotTagButtonClicked( int button )
+{
+    if( button == Private::Button_AddTag ) {
+        bool ok;
+        QString tagName = QInputDialog::getText(this,"Enter name for new Tag","Tag name:",QLineEdit::Normal,"",&ok);
+        if (ok) {
+            addTagToComboBox(tagName,true);
+        }
+    }
+    else if( button == Private::Button_RemoveTag ) {
+        removeTagFromComboBox();
+    }
+}
+
+void KoResourceItemChooser::addResourceTag(KoResource * resource, QString tagName)
+{
+    QStringList tagsList = d->model->getAssignedTagsList(resource);
+    if (tagsList.isEmpty()) {
+        d->model->addTag(resource, tagName);
+    }
+    else {
+        foreach(QString tag, tagsList) {
+            if(tag.compare(tagName)) {
+                d->model->addTag(resource, tagName);
+            }
+        }
+    }
+}
+
+void KoResourceItemChooser::removeResourceTag(KoResource * resource, QString tagName)
+{
+    QStringList tagsList = d->model->getAssignedTagsList(resource);
+
+    foreach(QString oldName, tagsList) {
+        if(!oldName.compare(tagName)) {
+            d->model->deleteTag(resource, oldName);
+        }
+    }
+
 }
 
 #include <KoResourceItemChooser.moc>
diff --git a/libs/widgets/KoResourceItemChooser.h b/libs/widgets/KoResourceItemChooser.h
index 3bd790a..3e1a504 100644
--- a/libs/widgets/KoResourceItemChooser.h
+++ b/libs/widgets/KoResourceItemChooser.h
@@ -48,7 +48,7 @@ class KOWIDGETS_EXPORT KoResourceItemChooser : public QWidget
   Q_OBJECT
 public:
     enum Buttons { Button_Import, Button_Remove, Button_GhnsDownload, Button_GhnsUpload };
-    
+
     explicit KoResourceItemChooser( KoAbstractResourceServerAdapter * resourceAdapter, QWidget *parent = 0 );
     ~KoResourceItemChooser();
 
@@ -57,13 +57,13 @@ public:
 
     /// Sets number of rows in the view and causes the number of columns to be calculated accordingly
     void setRowCount( int rowCount );
-    
+
     /// Sets the height of the view rows
     void setRowHeight( int rowHeight );
 
     /// Sets the width of the view columns
     void setColumnWidth( int columnWidth );
-    
+
     /// Sets a custom delegate for the view
     void setItemDelegate( QAbstractItemDelegate * delegate );
 
@@ -115,21 +115,26 @@ signals:
     void splitterMoved();
 public slots:
     void slotButtonClicked( int button );
-    
+    void slotTagButtonClicked( int button );
+
 private slots:
     void activated ( const QModelIndex & index );
 
-    void setTagOpLineEdit(QStringList assignedTagsList);
-
-    void tagOpLineEditActivated(QString lineEditText);
-    void tagOpLineEditTextChanged(QString lineEditText);
-
     void tagSearchLineEditActivated(QString lineEditText);
     void tagSearchLineEditTextChanged(QString lineEditText);
 
+    void tagChooserIndexChanged(QString lineEditText);
+    void tagChooserReturnPressed(QString lineEditText);
+
 private:
+    QString makeValidName(QString string);
     void updateButtonState();
     void updatePreview(KoResource *resource);
+    QString renameTag(QString oldName, QString newName);
+    void addTagToComboBox(QString tagName, bool followEntry);
+    void removeTagFromComboBox();
+    void addResourceTag(KoResource * resource, QString tagName);
+    void removeResourceTag(KoResource * resource, QString tagName);
 
     /// Resource for a given model index
     /// @returns the resource pointer, 0 is index not valid
diff --git a/libs/widgets/KoResourceModel.cpp b/libs/widgets/KoResourceModel.cpp
index c25be97..ba8df50 100644
--- a/libs/widgets/KoResourceModel.cpp
+++ b/libs/widgets/KoResourceModel.cpp
@@ -29,11 +29,11 @@ KoResourceModel::KoResourceModel( KoAbstractResourceServerAdapter * resourceAdap
 {
     Q_ASSERT( m_resourceAdapter );
     m_resourceAdapter->connectToResourceServer();
-    connect(m_resourceAdapter, SIGNAL(resourceAdded(KoResource*)), 
+    connect(m_resourceAdapter, SIGNAL(resourceAdded(KoResource*)),
             this, SLOT(resourceAdded(KoResource*)));
-    connect(m_resourceAdapter, SIGNAL(removingResource(KoResource*)), 
+    connect(m_resourceAdapter, SIGNAL(removingResource(KoResource*)),
             this, SLOT(resourceRemoved(KoResource*)));
-    connect(m_resourceAdapter, SIGNAL(resourceChanged(KoResource*)), 
+    connect(m_resourceAdapter, SIGNAL(resourceChanged(KoResource*)),
             this, SLOT(resourceChanged(KoResource*)));
 }
 
@@ -63,8 +63,17 @@ QVariant KoResourceModel::data( const QModelIndex &index, int role ) const
             KoResource * resource = static_cast<KoResource*>(index.internalPointer());
             if( ! resource )
                 return QVariant();
-
-            return QVariant( i18n( resource->name().toUtf8().data() ) );
+            QString resName = i18n( resource->name().toUtf8().data());
+            
+           
+            if (m_resourceAdapter->getAssignedTagsList(resource).count()) {
+                QString tagList;
+                tagList += " - Tags: [";
+                tagList += m_resourceAdapter->getAssignedTagsList(resource).join("] , [");
+                tagList += "]";
+                return QVariant( resName + tagList );
+            }
+            return QVariant( resName );
         }
         case Qt::DecorationRole:
         {
@@ -144,7 +153,7 @@ void KoResourceModel::resourceChanged(KoResource* resource)
     if (!modelIndex.isValid()) {
         return;
     }
-    
+
     emit dataChanged(modelIndex, modelIndex);
 }
 
@@ -153,7 +162,7 @@ QModelIndex KoResourceModel::indexFromResource(KoResource* resource)
     int resourceIndex = m_resourceAdapter->resources().indexOf(resource);
     int row = resourceIndex / columnCount();
     int column = resourceIndex % columnCount();
-    return index(row, column);    
+    return index(row, column);
 }
 
 QString KoResourceModel::extensions()
@@ -206,9 +215,14 @@ QStringList KoResourceModel::searchTag(const QString& lineEditText)
     return m_resourceAdapter->searchTag(lineEditText);
 }
 
-void KoResourceModel::setTagSearch(bool tagSearch)
+void KoResourceModel::searchTextChanged(const QString& searchString)
+{
+    m_resourceAdapter->searchTextChanged(searchString);
+}
+
+void KoResourceModel::enableResourceFiltering(bool enable)
 {
-    m_resourceAdapter->setTagSearch(tagSearch);
+    m_resourceAdapter->enableResourceFiltering(enable);
 }
 
 void KoResourceModel::setTaggedResourceFileNames(const QStringList& resourceFileNames)
@@ -227,4 +241,9 @@ int KoResourceModel::resourcesCount() const
     return m_resourceAdapter->resources().count();
 }
 
+// needs to be optimized
+QList<KoResource *> KoResourceModel::currentlyVisibleResources()
+{
+  return m_resourceAdapter->resources();
+}
 #include <KoResourceModel.moc>
diff --git a/libs/widgets/KoResourceModel.h b/libs/widgets/KoResourceModel.h
index d63d32a..3ccb28b 100644
--- a/libs/widgets/KoResourceModel.h
+++ b/libs/widgets/KoResourceModel.h
@@ -64,10 +64,12 @@ public:
     void deleteTag( KoResource* resource, const QString& tag);
     QStringList getTagNamesList();
     QStringList searchTag(const QString& lineEditText);
-    void setTagSearch(bool tagSearch);
+    void enableResourceFiltering(bool enable);
     void setTaggedResourceFileNames(const QStringList& resourceFileNames);
+    void searchTextChanged(const QString& searchString);
     void updateServer();
     int resourcesCount() const;
+    QList<KoResource *> currentlyVisibleResources();
 
 private slots:
     void resourceAdded(KoResource *resource);
diff --git a/libs/widgets/KoResourceServer.h b/libs/widgets/KoResourceServer.h
index b363684..1ab4ad6 100644
--- a/libs/widgets/KoResourceServer.h
+++ b/libs/widgets/KoResourceServer.h
@@ -434,6 +434,11 @@ public:
     {
         return m_tagObject->searchTag(lineEditText);
     }
+    
+    KoResourceTagging * tagObject() 
+    {
+        return m_tagObject;
+    }
 
 
 #ifdef NEPOMUK
diff --git a/libs/widgets/KoResourceServerAdapter.h b/libs/widgets/KoResourceServerAdapter.h
index bb5c414..e7379d6 100644
--- a/libs/widgets/KoResourceServerAdapter.h
+++ b/libs/widgets/KoResourceServerAdapter.h
@@ -24,6 +24,7 @@
 
 #include "KoResourceServer.h"
 #include <KoResource.h>
+#include <KoPresetFiltering.h>
 
 #include "kowidgets_export.h"
 
@@ -43,12 +44,13 @@ public:
     virtual void importResourceFile(const QString & filename, bool fileCreation=true) = 0;
     virtual QString extensions() = 0;
     virtual void setTaggedResourceFileNames(const QStringList& resourceFileNames)=0;
-    virtual void setTagSearch(bool tagSearch)=0;
+    virtual void enableResourceFiltering(bool tagSearch)=0;
     virtual void updateServer()=0;
     virtual QStringList getAssignedTagsList( KoResource* resource )=0;
     virtual QStringList getTagNamesList()=0;
     virtual void addTag( KoResource* resource,const QString& tag)=0;
     virtual void deleteTag( KoResource* resource,const QString& tag)=0;
+    virtual void searchTextChanged(const QString& searchString)=0;
     virtual QStringList searchTag(const QString& lineEditText)=0;
 
 signals:
@@ -73,7 +75,9 @@ public:
         : KoAbstractResourceServerAdapter(parent)
         , m_resourceServer(resourceServer)
     {
-        m_tagSearch=false;
+        m_changeCounter = 0;
+        m_oldChangeCounter = 0;
+        m_enableFiltering=false;
     }
 
     virtual ~KoResourceServerAdapter()
@@ -88,28 +92,22 @@ public:
             m_resourceServer->addObserver(this);
     }
 
-    virtual QList<KoResource*> resources() 
+    virtual QList<KoResource*> resources()
     {
         if( ! m_resourceServer )
             return QList<KoResource*>();
 
-        QList<T*> serverResources = m_resourceServer->resources();
-
-        QList<KoResource*> resources;
-
-        foreach( T* resource, serverResources ) {
-            resources.append( resource );
+        bool serverResourcesChanged = serverResourcesHaveChanged();
+        if(serverResourcesChanged) {
+            updateServerResources(m_resourceServer->resources());
         }
-
-        if(m_tagSearch) {
-            foreach(KoResource* resource, resources) {
-                if(!m_resourceFileNames.contains(resource->filename())) {
-                    resources.removeAll(resource);
-                }
+        if ( m_enableFiltering) {
+            if (m_presetFilter.filtersHaveChanged() || serverResourcesChanged) {
+                m_filteredResources = m_presetFilter.filterResources( m_serverResources );
             }
+            return m_filteredResources;
         }
-
-        return resources;
+        return m_serverResources;
     }
 
     bool addResource(KoResource* resource)
@@ -150,21 +148,23 @@ public:
         }
 
         m_resourceServer->removeResourceFile(filename);
-
     }
 
     void resourceAdded(T* resource)
     {
+        flagServerResourceListDirty(true);
         emitResourceAdded(resource);
     }
 
     void removingResource(T* resource)
     {
+        flagServerResourceListDirty(true);
         emitRemovingResource(resource);
     }
-    
+
     void resourceChanged(T* resource)
     {
+        flagServerResourceListDirty(true);
         emitResourceChanged(resource);
     }
 
@@ -175,15 +175,17 @@ public:
 
         return m_resourceServer->extensions();
     }
-    
+
     void setTaggedResourceFileNames(const QStringList& resourceFileNames)
     {
         m_resourceFileNames = resourceFileNames;
+        flagServerResourceListDirty(true);
+        m_presetFilter.setRootResourceFilenames(resourceFileNames);
     }
 
-    void setTagSearch(bool tagSearch )
+    void enableResourceFiltering(bool enable )
     {
-        m_tagSearch = tagSearch;
+        m_enableFiltering = enable;
     }
 
     void updateServer()
@@ -211,6 +213,12 @@ public:
         m_resourceServer->delTag(resource,tag);
     }
 
+    void searchTextChanged(const QString& searchString)
+    {
+        m_presetFilter.setFilters(searchString, resourceServer()->tagObject());
+        flagServerResourceListDirty(true);
+    }
+
     QStringList searchTag(const QString& lineEditText)
     {
         return m_resourceServer->searchTag(lineEditText);
@@ -221,11 +229,33 @@ protected:
     {
         return m_resourceServer;
     }
-
+protected:
+    KoPresetFiltering m_presetFilter;
 private:
+    bool serverResourcesHaveChanged()
+    {
+        return m_changeCounter != m_oldChangeCounter;
+    }
+    void flagServerResourceListDirty(bool dirty)
+    {
+        dirty? ++m_changeCounter : m_oldChangeCounter = m_changeCounter;
+    }
+    void updateServerResources(QList< T* > serverResources)
+    {
+         m_serverResources.clear();
+
+            foreach( T* resource, serverResources ) {
+                m_serverResources.append( resource );
+            }
+            flagServerResourceListDirty (false);
+    }
     KoResourceServer<T>* m_resourceServer;
     QStringList m_resourceFileNames;
-    bool m_tagSearch;
+    unsigned int m_changeCounter;
+    unsigned int m_oldChangeCounter;
+    QList<KoResource*> m_serverResources;
+    QList<KoResource*> m_filteredResources;
+    bool m_enableFiltering;
 };
 
 #endif // KO_RESOURCESERVER_ADAPTER_H_
-- 
1.8.2



More information about the kimageshop mailing list