integrate new "Tags" node into the video library
authormontellese <montellese@xbmc.org>
Sat, 30 Jun 2012 11:20:45 +0000 (13:20 +0200)
committermontellese <montellese@xbmc.org>
Sun, 8 Jul 2012 21:36:03 +0000 (23:36 +0200)
18 files changed:
language/English/strings.po
system/library/video/movies/tags.xml [new file with mode: 0644]
xbmc/dialogs/GUIDialogContextMenu.h
xbmc/filesystem/VideoDatabaseDirectory.cpp
xbmc/filesystem/VideoDatabaseDirectory/DirectoryNode.cpp
xbmc/filesystem/VideoDatabaseDirectory/DirectoryNode.h
xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeMoviesOverview.cpp
xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeTags.cpp [new file with mode: 0644]
xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeTags.h [new file with mode: 0644]
xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeTitleMovies.cpp
xbmc/filesystem/VideoDatabaseDirectory/Makefile
xbmc/filesystem/VideoDatabaseDirectory/QueryParams.cpp
xbmc/filesystem/VideoDatabaseDirectory/QueryParams.h
xbmc/video/GUIViewStateVideo.cpp
xbmc/video/windows/GUIWindowVideoBase.cpp
xbmc/video/windows/GUIWindowVideoNav.cpp
xbmc/video/windows/GUIWindowVideoNav.h
xbmc/windows/GUIMediaWindow.cpp

index 8219f01..68ef88a 100644 (file)
@@ -7427,8 +7427,32 @@ msgctxt "#20458"
 msgid "Group movies in sets"
 msgstr ""
 
+msgctxt "#20459"
+msgid "Tags"
+msgstr ""
+
+msgctxt "#20460"
+msgid "Add %s"
+msgstr ""
+
+msgctxt "#20461"
+msgid "Remove %s"
+msgstr ""
+
+msgctxt "#20462"
+msgid "New tag..."
+msgstr ""
+
+msgctxt "#20463"
+msgid "A tag with the name '%s' already exists."
+msgstr ""
+
+msgctxt "#20464"
+msgid "Select %s"
+msgstr ""
+
 #up to 21329 is reserved for the video db !! !
-#empty strings from id 20459 to 21329
+#empty strings from id 20465 to 21329
 
 msgctxt "#21330"
 msgid "Show hidden files and directories"
diff --git a/system/library/video/movies/tags.xml b/system/library/video/movies/tags.xml
new file mode 100644 (file)
index 0000000..c897a6a
--- /dev/null
@@ -0,0 +1,5 @@
+<node order="7" type="folder" visible="Library.HasContent(MovieSets)">
+  <label>20459</label>
+  <path>videodb://1/9</path>
+  <icon>DefaultTags.png</icon>
+</node>
index 9a9600f..0771c7d 100644 (file)
@@ -104,6 +104,8 @@ enum CONTEXT_BUTTON { CONTEXT_BUTTON_CANCELLED = 0,
                       CONTEXT_BUTTON_SET_MOVIESET_FANART,
                       CONTEXT_BUTTON_DELETE_PLUGIN,
                       CONTEXT_BUTTON_PLAY_AND_QUEUE,
+                      CONTEXT_BUTTON_TAGS_ADD_ITEMS,
+                      CONTEXT_BUTTON_TAGS_REMOVE_ITEMS,
                       CONTEXT_BUTTON_USER1,
                       CONTEXT_BUTTON_USER2,
                       CONTEXT_BUTTON_USER3,
index ce8ba39..1b7bbcc 100644 (file)
@@ -160,6 +160,10 @@ bool CVideoDatabaseDirectory::GetLabel(const CStdString& strDirectory, CStdStrin
   if (params.GetSetId() != -1)
     strLabel += videodatabase.GetSetById(params.GetSetId());
 
+  // get tag
+  if (params.GetTagId() != -1)
+    strLabel += videodatabase.GetTagById(params.GetTagId());
+
   // get year
   if (params.GetYear() != -1)
   {
@@ -190,6 +194,8 @@ bool CVideoDatabaseDirectory::GetLabel(const CStdString& strDirectory, CStdStrin
       strLabel = g_localizeStrings.Get(20348); break;
     case NODE_TYPE_SETS: // Sets
       strLabel = g_localizeStrings.Get(20434); break;
+    case NODE_TYPE_TAGS: // Tags
+      strLabel = g_localizeStrings.Get(20459); break;
     case NODE_TYPE_MOVIES_OVERVIEW: // Movies
       strLabel = g_localizeStrings.Get(342); break;
     case NODE_TYPE_TVSHOWS_OVERVIEW: // TV Shows
@@ -253,6 +259,8 @@ CStdString CVideoDatabaseDirectory::GetIcon(const CStdString &strDirectory)
     return "DefaultCountry.png";
   case NODE_TYPE_SETS: // Sets
     return "DefaultSets.png";
+  case NODE_TYPE_TAGS: // Tags
+    return "DefaultTags.png";
   case NODE_TYPE_YEAR: // Year
     return "DefaultYear.png";
   case NODE_TYPE_DIRECTOR: // Director
index 6fcfeaa..1eb27b3 100644 (file)
@@ -43,6 +43,7 @@
 #include "DirectoryNodeRecentlyAddedMusicVideos.h"
 #include "DirectoryNodeTitleMusicVideos.h"
 #include "DirectoryNodeMusicVideoAlbum.h"
+#include "DirectoryNodeTags.h"
 #include "video/VideoInfoTag.h"
 #include "URL.h"
 #include "settings/AdvancedSettings.h"
@@ -122,6 +123,8 @@ CDirectoryNode* CDirectoryNode::CreateNode(NODE_TYPE Type, const CStdString& str
     return new CDirectoryNodeCountry(strName, pParent);
   case NODE_TYPE_SETS:
     return new CDirectoryNodeSets(strName, pParent);
+  case NODE_TYPE_TAGS:
+    return new CDirectoryNodeTags(strName, pParent);
   case NODE_TYPE_YEAR:
     return new CDirectoryNodeYear(strName, pParent);
   case NODE_TYPE_ACTOR:
index 8a51a50..0ba9896 100644 (file)
@@ -54,7 +54,8 @@ namespace XFILE
       NODE_TYPE_TITLE_MUSICVIDEOS,
       NODE_TYPE_MUSICVIDEOS_ALBUM,
       NODE_TYPE_SETS,
-      NODE_TYPE_COUNTRY
+      NODE_TYPE_COUNTRY,
+      NODE_TYPE_TAGS
     } NODE_TYPE;
 
     typedef struct {
index 1d9b7f6..87c5370 100644 (file)
@@ -36,6 +36,7 @@ Node MovieChildren[] = {
                         { NODE_TYPE_STUDIO,       6, 20388 },
                         { NODE_TYPE_SETS,         7, 20434 },
                         { NODE_TYPE_COUNTRY,      8, 20451 },
+                        { NODE_TYPE_TAGS,         9, 20459 }
                        };
 
 CDirectoryNodeMoviesOverview::CDirectoryNodeMoviesOverview(const CStdString& strName, CDirectoryNode* pParent)
diff --git a/xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeTags.cpp b/xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeTags.cpp
new file mode 100644 (file)
index 0000000..d95da74
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ *      Copyright (C) 2012 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  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, 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 XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "DirectoryNodeTags.h"
+#include "QueryParams.h"
+#include "video/VideoDatabase.h"
+
+using namespace XFILE::VIDEODATABASEDIRECTORY;
+
+CDirectoryNodeTags::CDirectoryNodeTags(const CStdString& strName, CDirectoryNode* pParent)
+  : CDirectoryNode(NODE_TYPE_TAGS, strName, pParent)
+{
+
+}
+
+NODE_TYPE CDirectoryNodeTags::GetChildType() const
+{
+  return NODE_TYPE_TITLE_MOVIES;
+}
+
+CStdString CDirectoryNodeTags::GetLocalizedName() const
+{
+  CVideoDatabase db;
+  if (db.Open())
+    return db.GetTagById(GetID());
+  return "";
+}
+
+bool CDirectoryNodeTags::GetContent(CFileItemList& items) const
+{
+  CVideoDatabase videodatabase;
+  if (!videodatabase.Open())
+    return false;
+
+  CQueryParams params;
+  CollectQueryParams(params);
+
+  bool bSuccess = videodatabase.GetTagsNav(BuildPath(), items, params.GetContentType());
+  videodatabase.Close();
+
+  return bSuccess;
+}
diff --git a/xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeTags.h b/xbmc/filesystem/VideoDatabaseDirectory/DirectoryNodeTags.h
new file mode 100644 (file)
index 0000000..7ab10ec
--- /dev/null
@@ -0,0 +1,41 @@
+#pragma once
+/*
+ *      Copyright (C) 2012 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  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, 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 XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "DirectoryNode.h"
+
+namespace XFILE
+{
+  namespace VIDEODATABASEDIRECTORY
+  {
+    class CDirectoryNodeTags : public CDirectoryNode
+    {
+    public:
+      CDirectoryNodeTags(const CStdString& strName, CDirectoryNode* pParent);
+    protected:
+      virtual NODE_TYPE GetChildType() const;
+      virtual bool GetContent(CFileItemList& items) const;
+      virtual CStdString GetLocalizedName() const;
+    };
+  }
+}
+
+
index 07557c7..7989c81 100644 (file)
@@ -41,7 +41,7 @@ bool CDirectoryNodeTitleMovies::GetContent(CFileItemList& items) const
   CollectQueryParams(params);
 
   CStdString strBaseDir=BuildPath();
-  bool bSuccess=videodatabase.GetMoviesNav(strBaseDir, items, params.GetGenreId(), params.GetYear(), params.GetActorId(), params.GetDirectorId(), params.GetStudioId(), params.GetCountryId(), params.GetSetId());
+  bool bSuccess=videodatabase.GetMoviesNav(strBaseDir, items, params.GetGenreId(), params.GetYear(), params.GetActorId(), params.GetDirectorId(), params.GetStudioId(), params.GetCountryId(), params.GetSetId(), params.GetTagId());
 
   videodatabase.Close();
 
index 2bd4107..bda9117 100644 (file)
@@ -15,6 +15,7 @@ SRCS=DirectoryNode.cpp \
      DirectoryNodeSeasons.cpp \
      DirectoryNodeSets.cpp \
      DirectoryNodeStudio.cpp \
+     DirectoryNodeTags.cpp \
      DirectoryNodeTitleMovies.cpp \
      DirectoryNodeTitleMusicVideos.cpp \
      DirectoryNodeTitleTvShows.cpp \
index ea84cbe..5c2089e 100644 (file)
@@ -39,6 +39,7 @@ CQueryParams::CQueryParams()
   m_idMVideo = -1;
   m_idAlbum = -1;
   m_idSet = -1;
+  m_idTag = -1;
 }
 
 void CQueryParams::SetQueryParam(NODE_TYPE NodeType, const CStdString& strNodeName)
@@ -92,6 +93,9 @@ void CQueryParams::SetQueryParam(NODE_TYPE NodeType, const CStdString& strNodeNa
   case NODE_TYPE_SETS:
     m_idSet = idDb;
     break;
+  case NODE_TYPE_TAGS:
+    m_idTag = idDb;
+    break;
   default:
     break;
   }
index 60adaea..9fb7efc 100644 (file)
@@ -44,6 +44,7 @@ namespace XFILE
       long GetStudioId() const { return m_idStudio; }
       long GetMVideoId() const { return m_idMVideo; }
       long GetSetId() const { return m_idSet; }
+      long GetTagId() const { return m_idTag; }
 
     protected:
       void SetQueryParam(NODE_TYPE NodeType, const CStdString& strNodeName);
@@ -64,6 +65,7 @@ namespace XFILE
       long m_idMVideo;
       long m_idAlbum;
       long m_idSet;
+      long m_idTag;
     };
   }
 }
index c40db5f..0aa92fa 100644 (file)
@@ -211,6 +211,18 @@ CGUIViewStateWindowVideoNav::CGUIViewStateWindowVideoNav(const CFileItemList& it
         SetSortOrder(g_settings.m_viewStateVideoNavGenres.m_sortOrder);
       }
       break;
+    case NODE_TYPE_TAGS:
+      {
+        SORT_METHOD method = SORT_METHOD_LABEL_IGNORE_THE;
+        if (!g_guiSettings.GetBool("filelists.ignorethewhensorting"))
+          method = SORT_METHOD_LABEL;
+
+        AddSortMethod(method, 551, LABEL_MASKS("%T","", "%T",""));  // Title, empty | Title, empty
+        SetSortMethod(method);
+        SetViewAsControl(g_settings.m_viewStateVideoNavGenres.m_viewMode);
+        SetSortOrder(g_settings.m_viewStateVideoNavGenres.m_sortOrder);
+      }
+      break;
     case NODE_TYPE_EPISODES:
       {
         if (params.GetSeason() > -1)
index bf59784..7406d35 100644 (file)
@@ -875,7 +875,8 @@ bool CGUIWindowVideoBase::OnSelect(int iItem)
   CFileItemPtr item = m_vecItems->Get(iItem);
 
   CStdString path = item->GetPath();
-  if (!item->m_bIsFolder && path != "add" && path != "addons://more/video" && path.Left(19) != "newsmartplaylist://" && path.Left(14) != "newplaylist://")
+  if (!item->m_bIsFolder && path != "add" && path != "addons://more/video" &&
+      path.Left(19) != "newsmartplaylist://" && path.Left(14) != "newplaylist://" && path.Left(9) != "newtag://")
     return OnFileAction(iItem, g_guiSettings.GetInt("myvideos.selectaction"));
 
   return CGUIMediaWindow::OnSelect(iItem);
@@ -1187,7 +1188,7 @@ void CGUIWindowVideoBase::GetContextButtons(int itemNumber, CContextButtons &but
             buttons.Add(CONTEXT_BUTTON_PLAY_PART, 20324);
         }
 
-        if (!m_vecItems->GetPath().IsEmpty() && !item->GetPath().Left(19).Equals("newsmartplaylist://")
+        if (!m_vecItems->GetPath().IsEmpty() && !item->GetPath().Left(19).Equals("newsmartplaylist://") && !item->GetPath().Left(9).Equals("newtag://")
             && !m_vecItems->IsSourcesPath())
         {
           buttons.Add(CONTEXT_BUTTON_QUEUE_ITEM, 13347);      // Add to Playlist
index b88a1db..4d58b8c 100644 (file)
@@ -51,6 +51,7 @@
 #include "utils/URIUtils.h"
 #include "utils/StringUtils.h"
 #include "TextureCache.h"
+#include "dialogs/GUIDialogKeyboard.h"
 
 using namespace XFILE;
 using namespace VIDEODATABASEDIRECTORY;
@@ -207,6 +208,14 @@ CStdString CGUIWindowVideoNav::GetQuickpathName(const CStdString& strPath) const
     return "MovieActors";
   else if (strPath.Equals("videodb://1/5/"))
     return "MovieDirectors";
+  else if (strPath.Equals("videodb://1/6/"))
+    return "MovieStudios";
+  else if (strPath.Equals("videodb://1/7/"))
+    return "MovieSets";
+  else if (strPath.Equals("videodb://1/8/"))
+    return "MovieCountries";
+  else if (strPath.Equals("videodb://1/9/"))
+    return "MovieTags";
   else if (strPath.Equals("videodb://1/"))
     return "Movies";
   else if (strPath.Equals("videodb://2/1/"))
@@ -315,6 +324,7 @@ bool CGUIWindowVideoNav::GetDirectory(const CStdString &strDirectory, CFileItemL
       }
       else if (node == NODE_TYPE_TITLE_MOVIES ||
                node == NODE_TYPE_SETS ||
+               node == NODE_TYPE_TAGS ||
                node == NODE_TYPE_RECENTLY_ADDED_MOVIES)
         items.SetContent("movies");
       else if (node == NODE_TYPE_TITLE_TVSHOWS)
@@ -352,6 +362,15 @@ bool CGUIWindowVideoNav::GetDirectory(const CStdString &strDirectory, CFileItemL
       if (!items.IsSourcesPath())
         LoadVideoInfo(items);
     }
+
+    if (items.GetPath() == "videodb://1/9/" && !items.Contains("newtag://movie"))
+    {
+      CFileItemPtr newTag(new CFileItem("newtag://movie", false));
+      newTag->SetLabel(g_localizeStrings.Get(20462));
+      newTag->SetLabelPreformated(true);
+      newTag->SetSpecialSort(SortSpecialOnTop);
+      items.Add(newTag);
+    }
   }
   return bResult;
 }
@@ -624,7 +643,8 @@ void CGUIWindowVideoNav::OnDeleteItem(CFileItemPtr pItem)
   {
     if (!pItem->GetPath().Equals("newsmartplaylist://video") &&
         !pItem->GetPath().Equals("special://videoplaylists/") &&
-        !pItem->GetPath().Equals("sources://video/"))
+        !pItem->GetPath().Equals("sources://video/") &&
+        !pItem->GetPath().Left(9).Equals("newtag://"))
       CGUIWindowVideoBase::OnDeleteItem(pItem);
   }
   else if (pItem->GetPath().Left(14).Equals("videodb://1/7/") &&
@@ -644,10 +664,28 @@ void CGUIWindowVideoNav::OnDeleteItem(CFileItemPtr pItem)
       for (int i=0;i<items.Size();++i)
         OnDeleteItem(items[i]);
 
-       CVideoDatabaseDirectory dir;
-       CQueryParams params;
-       dir.GetQueryParams(pItem->GetPath(),params);
-       m_database.DeleteSet(params.GetSetId());
+      CVideoDatabaseDirectory dir;
+      CQueryParams params;
+      dir.GetQueryParams(pItem->GetPath(),params);
+      m_database.DeleteSet(params.GetSetId());
+    }
+  }
+  else if (pItem->GetPath().Left(14).Equals("videodb://1/9/") &&
+           pItem->GetPath().size() > 14 && pItem->m_bIsFolder)
+  {
+    CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
+    pDialog->SetHeading(432);
+    CStdString strLabel;
+    strLabel.Format(g_localizeStrings.Get(433),pItem->GetLabel());
+    pDialog->SetLine(1, strLabel);
+    pDialog->SetLine(2, "");;
+    pDialog->DoModal();
+    if (pDialog->IsConfirmed())
+    {
+      CVideoDatabaseDirectory dir;
+      CQueryParams params;
+      dir.GetQueryParams(pItem->GetPath(), params);
+      m_database.DeleteTag(params.GetTagId(), "movie");
     }
   }
   else if (m_vecItems->GetPath().Equals(CUtil::VideoPlaylistsLocation()) ||
@@ -883,7 +921,9 @@ void CGUIWindowVideoNav::GetContextButtons(int itemNumber, CContextButtons &butt
             buttons.Add(CONTEXT_BUTTON_UPDATE_TVSHOW, 13349);
         }
         if (!item->IsPlugin() && !item->IsScript() && !item->IsLiveTV() && !item->IsAddonsPath() &&
-             item->GetPath() != "sources://video/" && item->GetPath() != "special://videoplaylists/")
+             item->GetPath() != "sources://video/" && item->GetPath() != "special://videoplaylists/" &&
+             item->GetPath().Left(19) != "newsmartplaylist://" && item->GetPath().Left(14) != "newplaylist://" &&
+             item->GetPath().Left(9) != "newtag://")
         {
           if (item->m_bIsFolder)
           {
@@ -924,6 +964,15 @@ void CGUIWindowVideoNav::GetContextButtons(int itemNumber, CContextButtons &butt
           buttons.Add(CONTEXT_BUTTON_DELETE, 646);
         }
 
+        if (item->GetPath().Left(14).Equals("videodb://1/9/") && item->GetPath().size() > 14 && item->m_bIsFolder) // tags
+        {
+          CStdString strLabelAdd; strLabelAdd.Format(g_localizeStrings.Get(20460), g_localizeStrings.Get(20342).c_str());
+          CStdString strLabelRemove; strLabelRemove.Format(g_localizeStrings.Get(20461), g_localizeStrings.Get(20342).c_str());
+          buttons.Add(CONTEXT_BUTTON_TAGS_ADD_ITEMS, strLabelAdd);
+          buttons.Add(CONTEXT_BUTTON_TAGS_REMOVE_ITEMS, strLabelRemove);
+          buttons.Add(CONTEXT_BUTTON_DELETE, 646);
+        }
+
         if (node == NODE_TYPE_ACTOR && !dir.IsAllItem(item->GetPath()) && item->m_bIsFolder)
         {
           if (m_vecItems->GetPath().Left(11).Equals("videodb://3")) // mvids
@@ -1195,6 +1244,74 @@ bool CGUIWindowVideoNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
       Update(m_vecItems->GetPath());
       return true;
     }
+  case CONTEXT_BUTTON_TAGS_ADD_ITEMS:
+    {
+      if (!item->GetPath().Left(10).Equals("videodb://"))
+        return false;
+      
+      std::string mediaType;
+      if (item->GetPath().Mid(9, 3).Equals("/1/"))
+        mediaType = "movie";
+      else
+        return false;
+
+      CFileItemList items;
+      CStdString localizedType = GetLocalizedType(mediaType);
+      CStdString strLabel; strLabel.Format(g_localizeStrings.Get(20464), localizedType.c_str());
+      if (!GetItemsForTag(strLabel, mediaType, items, item->GetVideoInfoTag()->m_iDbId))
+        return true;
+
+      CVideoDatabase videodb;
+      if (!videodb.Open())
+        return true;
+
+      for (int index = 0; index < items.Size(); index++)
+      {
+        if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
+          continue;
+
+        videodb.AddTagToItem(items[index]->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_iDbId, mediaType);
+      }
+
+      // we need to clear any cached version of this tag's listing
+      items.SetPath(item->GetPath());
+      items.RemoveDiscCache(GetID());
+      return true;
+    }
+  case CONTEXT_BUTTON_TAGS_REMOVE_ITEMS:
+    {
+      if (!item->GetPath().Left(10).Equals("videodb://"))
+        return false;
+      
+      std::string mediaType;
+      if (item->GetPath().Mid(9, 3).Equals("/1/"))
+        mediaType = "movie";
+      else
+        return false;
+
+      CFileItemList items;
+      CStdString localizedType = GetLocalizedType(mediaType);
+      CStdString strLabel; strLabel.Format(g_localizeStrings.Get(20464), localizedType.c_str());
+      if (!GetItemsForTag(strLabel, mediaType, items, item->GetVideoInfoTag()->m_iDbId, false))
+        return true;
+
+      CVideoDatabase videodb;
+      if (!videodb.Open())
+        return true;
+
+      for (int index = 0; index < items.Size(); index++)
+      {
+        if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
+          continue;
+
+        videodb.RemoveTagFromItem(items[index]->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_iDbId, mediaType);
+      }
+
+      // we need to clear any cached version of this tag's listing
+      items.SetPath(item->GetPath());
+      items.RemoveDiscCache(GetID());
+      return true;
+    }
   case CONTEXT_BUTTON_UPDATE_LIBRARY:
     {
       OnScan("");
@@ -1332,6 +1449,54 @@ bool CGUIWindowVideoNav::OnClick(int iItem)
     m_viewControl.SetSelectedItem(iItem);
     return true;
   }
+  else if (item->GetPath().Left(9).Equals("newtag://"))
+  {
+    // dont allow update while scanning
+    if (g_application.IsVideoScanning())
+    {
+      CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
+      return true;
+    }
+
+    //Get the new title
+    CStdString strTag;
+    if (!CGUIDialogKeyboard::ShowAndGetInput(strTag, g_localizeStrings.Get(20462), false))
+      return true;
+
+    CVideoDatabase videodb;
+    if (!videodb.Open())
+      return true;
+
+    CStdString mediaType = item->GetPath().Mid(9);
+    CStdString localizedType = GetLocalizedType(mediaType);
+    if (localizedType.empty())
+      return true;
+
+    if (!videodb.GetSingleValue("tag", "tag.idTag", videodb.PrepareSQL("tag.strTag = '%s' AND tag.idTag IN (SELECT taglinks.idTag FROM taglinks WHERE taglinks.media_type = '%s')", strTag.c_str(), mediaType.c_str())).empty())
+    {
+      CStdString strError; strError.Format(g_localizeStrings.Get(20463), strTag.c_str());
+      CGUIDialogOK::ShowAndGetInput(20462, strError, "", "");
+      return true;
+    }
+
+    int idTag = videodb.AddTag(strTag);
+    CFileItemList items;
+    CStdString strLabel; strLabel.Format(g_localizeStrings.Get(20464), localizedType.c_str());
+    if (GetItemsForTag(strLabel, mediaType, items, idTag))
+    {
+      for (int index = 0; index < items.Size(); index++)
+      {
+        if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
+          continue;
+
+        videodb.AddTagToItem(items[index]->GetVideoInfoTag()->m_iDbId, idTag, mediaType);
+      }
+    }
+
+    m_vecItems->RemoveDiscCache(GetID());
+    Update(m_vecItems->GetPath());
+    return true;
+  }
 
   return CGUIWindowVideoBase::OnClick(iItem);
 }
@@ -1354,6 +1519,8 @@ CStdString CGUIWindowVideoNav::GetStartFolder(const CStdString &dir)
     return "videodb://1/7/";
   else if (dir.Equals("MovieCountries"))
     return "videodb://1/8/";
+  else if (dir.Equals("MovieTags"))
+    return "videodb://1/9/";
   else if (dir.Equals("Movies"))
     return "videodb://1/";
   else if (dir.Equals("TvShowGenres"))
@@ -1405,6 +1572,7 @@ void CGUIWindowVideoNav::ApplyWatchedFilter(CFileItemList &items)
   if (node == NODE_TYPE_EPISODES
   ||  node == NODE_TYPE_SEASONS
   ||  node == NODE_TYPE_SETS
+  ||  node == NODE_TYPE_TAGS
   ||  node == NODE_TYPE_TITLE_MOVIES
   ||  node == NODE_TYPE_TITLE_TVSHOWS
   ||  node == NODE_TYPE_TITLE_MUSICVIDEOS
@@ -1443,3 +1611,59 @@ void CGUIWindowVideoNav::ApplyWatchedFilter(CFileItemList &items)
     }
   }
 }
+
+bool CGUIWindowVideoNav::GetItemsForTag(const CStdString &strHeading, const std::string &type, CFileItemList &items, int idTag /* = -1 */, bool showAll /* = true */)
+{
+  CVideoDatabase videodb;
+  if (!videodb.Open())
+    return false;
+
+  CFileItemList listItems;
+  bool result = false;
+  if (idTag <= 0)
+    result = videodb.GetMoviesNav("videodb://1/2/", listItems);
+  else
+  {
+    if (showAll)
+    {
+      CVideoDatabase::Filter filter;
+      filter.where = videodb.PrepareSQL("movieview.idMovie NOT IN (SELECT taglinks.idMedia FROM taglinks WHERE taglinks.idTag = %d AND taglinks.media_type = '%s')", idTag, type.c_str());
+      result = videodb.GetMoviesByWhere("videodb://1/2/", filter, listItems);
+    }
+    else
+      result = videodb.GetMoviesNav("videodb://1/9/", listItems, -1, -1, -1, -1, -1, -1, -1, idTag);
+  }
+
+  if (!result || listItems.Size() <= 0)
+    return false;
+
+  CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
+  if (dialog == NULL)
+    return false;
+
+  listItems.Sort(SORT_METHOD_LABEL_IGNORE_THE, SortOrderAscending);
+
+  dialog->Reset();
+  dialog->SetMultiSelection(true);
+  dialog->SetHeading(strHeading);
+  dialog->SetItems(&listItems);
+  dialog->EnableButton(true, 186);
+  dialog->DoModal();
+
+  items.Copy(dialog->GetSelectedItems());
+  return items.Size() > 0;
+}
+
+CStdString CGUIWindowVideoNav::GetLocalizedType(const std::string &strType)
+{
+  if (strType == "movie")
+    return g_localizeStrings.Get(20342);
+  else if (strType == "tvshow")
+    return g_localizeStrings.Get(20343);
+  else if (strType == "episode")
+    return g_localizeStrings.Get(20359);
+  else if (strType == "musicvideo")
+    return g_localizeStrings.Get(20391);
+  else
+    return "";
+}
index 6ee0708..631851b 100644 (file)
@@ -67,5 +67,8 @@ protected:
 
   virtual CStdString GetQuickpathName(const CStdString& strPath) const;
 
+  bool GetItemsForTag(const CStdString &strHeading, const std::string &type, CFileItemList &items, int idTag = -1, bool showAll = true);
+  static CStdString GetLocalizedType(const std::string &strType);
+
   VECSOURCES m_shares;
 };
index 8d398ed..02a2c36 100644 (file)
@@ -1425,7 +1425,7 @@ void CGUIMediaWindow::GetContextButtons(int itemNumber, CContextButtons &buttons
 
   // TODO: FAVOURITES Conditions on masterlock and localisation
   if (!item->IsParentFolder() && !item->GetPath().Equals("add") && !item->GetPath().Equals("newplaylist://") &&
-      !item->GetPath().Left(19).Equals("newsmartplaylist://"))
+      !item->GetPath().Left(19).Equals("newsmartplaylist://") && !item->GetPath().Left(9).Equals("newtag://"))
   {
     if (CFavourites::IsFavourite(item.get(), GetID()))
       buttons.Add(CONTEXT_BUTTON_ADD_FAVOURITE, 14077);     // Remove Favourite