LeechCraft  0.6.70-18450-gabe19ee3b0
Modular cross-platform feature rich live environment.
itemsfinder.cpp
Go to the documentation of this file.
1 /**********************************************************************
2  * LeechCraft - modular cross-platform feature rich internet client.
3  * Copyright (C) 2006-2014 Georg Rudoy
4  *
5  * Distributed under the Boost Software License, Version 1.0.
6  * (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)
7  **********************************************************************/
8 
9 #include "itemsfinder.h"
10 #include <QDir>
11 #include <QTimer>
12 #include <QtDebug>
13 #include <QtConcurrentRun>
14 #include <util/sll/prelude.h>
15 #include <util/sll/qtutil.h>
17 #include <util/threads/coro.h>
18 #include "xdg.h"
19 #include "item.h"
20 
21 namespace LC::Util::XDG
22 {
24  const QList<Type>& types, QObject *parent)
25  : QObject { parent }
26  , Proxy_ { proxy }
27  , Types_ { types }
28  {
29  QTimer::singleShot (1000, this, &ItemsFinder::Update);
30  }
31 
32  bool ItemsFinder::IsReady () const
33  {
34  return IsReady_;
35  }
36 
38  {
39  return Items_;
40  }
41 
42  Item_ptr ItemsFinder::FindItem (const QString& id) const
43  {
44  for (const auto& list : Items_)
45  {
46  const auto pos = std::find_if (list.begin (), list.end (),
47  [&id] (const Item_ptr& item) { return item->GetPermanentID () == id; });
48  if (pos != list.end ())
49  return *pos;
50  }
51 
52  return {};
53  }
54 
55  namespace
56  {
57  using Cat2ID2Item_t = QHash<QString, QHash<QString, Item_ptr>>;
58 
59  Cat2ID2Item_t ItemsList2Map (const Cat2Items_t& items)
60  {
61  Cat2ID2Item_t result;
62 
63  for (const auto& pair : Util::Stlize (items))
64  {
65  auto& map = result [pair.first];
66  for (const auto& item : pair.second)
67  map [item->GetPermanentID ()] = item;
68  }
69 
70  return result;
71  }
72 
73  Cat2Items_t ItemsMap2List (const Cat2ID2Item_t& items)
74  {
75  Cat2Items_t result;
76 
77  for (const auto& pair : Util::Stlize (items))
78  std::copy (pair.second.begin (), pair.second.end (),
79  std::back_inserter (result [pair.first]));
80 
81  return result;
82  }
83 
84  QStringList ScanDir (const QString& path)
85  {
86  const auto& infos = QDir (path).entryInfoList ({ QStringLiteral ("*.desktop") },
87  QDir::Files | QDir::AllDirs | QDir::NoDotAndDotDot);
88 
89  return Util::ConcatMap (infos,
90  [] (const QFileInfo& info)
91  {
92  return info.isDir () ?
93  ScanDir (info.absoluteFilePath ()) :
94  QStringList { info.absoluteFilePath () };
95  });
96  }
97 
98  Cat2ID2Item_t FindAndParse (const QList<Type>& types)
99  {
100  Cat2ID2Item_t result;
101 
102  QStringList paths;
103  for (const auto& dir : ToPaths (types))
104  paths << ScanDir (dir);
105 
106  for (const auto& path : paths)
107  {
108  Item_ptr item;
109  try
110  {
111  item = Item::FromDesktopFile (path);
112  }
113  catch (const std::exception& e)
114  {
115  qWarning () << Q_FUNC_INFO
116  << "error parsing"
117  << path
118  << e.what ();
119  continue;
120  }
121 
122  if (!item->IsValid ())
123  {
124  qWarning () << Q_FUNC_INFO
125  << "invalid item"
126  << path;
127  continue;
128  }
129 
130  for (const auto& cat : item->GetCategories ())
131  if (!cat.startsWith ("X-"_ql))
132  result [cat] [item->GetPermanentID ()] = item;
133  }
134 
135  return result;
136  }
137 
138  template<typename T>
139  struct DiffResult
140  {
141  std::decay_t<T> Added_;
142  std::decay_t<T> Removed_;
143  std::decay_t<T> Intersection_;
144 
145  DiffResult (T&& oldCont, T&& newCont)
146  {
147  std::set_difference (oldCont.begin (), oldCont.end (),
148  newCont.begin (), newCont.end (),
149  std::back_inserter (Removed_));
150  std::set_difference (newCont.begin (), newCont.end (),
151  oldCont.begin (), oldCont.end (),
152  std::back_inserter (Added_));
153 
154  std::set_intersection (oldCont.begin (), oldCont.end (),
155  newCont.begin (), newCont.end (),
156  std::back_inserter (Intersection_));
157  }
158 
159  bool HasChanges () const
160  {
161  return !Removed_.isEmpty () || !Added_.isEmpty ();
162  }
163  };
164 
165  template<typename Container>
166  DiffResult<Container> CalcDiff (Container&& oldCont, Container&& newCont)
167  {
168  return { std::forward<Container> (oldCont), std::forward<Container> (newCont) };
169  }
170 
171  std::optional<Cat2Items_t> Merge (const Cat2Items_t& existing, Cat2ID2Item_t result)
172  {
173  auto ourItems = ItemsList2Map (existing);
174 
175  using std::swap;
176 
177  const auto& diffCats = CalcDiff (Util::Sorted (existing.keys ()),
178  Util::Sorted (result.keys ()));
179 
180  for (const auto& removed : diffCats.Removed_)
181  ourItems.remove (removed);
182  for (const auto& added : diffCats.Added_)
183  swap (ourItems [added], result [added]);
184 
185  bool changed = diffCats.HasChanges ();
186 
187  for (const auto& cat : diffCats.Intersection_)
188  {
189  auto& ourList = ourItems [cat];
190  auto& newList = result [cat];
191  const auto& diffItems = CalcDiff (Util::Sorted (ourList.keys ()),
192  Util::Sorted (newList.keys ()));
193 
194  changed = changed || diffItems.HasChanges ();
195 
196  for (const auto& removed : diffItems.Removed_)
197  ourList.remove (removed);
198  for (const auto& added : diffItems.Added_)
199  swap (ourList [added], newList [added]);
200 
201  for (const auto& existing : diffItems.Intersection_)
202  if (*ourList [existing] != *newList [existing])
203  {
204  swap (ourList [existing], newList [existing]);
205  changed = true;
206  }
207  }
208 
209  if (!changed)
210  return {};
211 
212  return ItemsMap2List (ourItems);
213  }
214  }
215 
217  {
218  if (IsScanning_)
219  return;
220 
221  IsScanning_ = true;
222 
223  [this] () -> ContextTask<void>
224  {
225  co_await AddContextObject { *this };
226  const auto& cat2id2item = co_await QtConcurrent::run (FindAndParse, Types_);
227  const auto& result = co_await QtConcurrent::run (Merge, Items_, cat2id2item);
228 
229  IsScanning_ = false;
230  IsReady_ = true;
231 
232  if (result)
233  {
234  Items_ = *result;
235  emit itemsListChanged ();
236  }
237  } ();
238  }
239 }
void itemsListChanged()
Notifies when the list of items changes in any way.
bool IsReady() const
Checks whether this items finder is ready.
Definition: itemsfinder.cpp:32
Item_ptr FindItem(const QString &permanentID) const
Finds an XDG item for the given permanent ID.
Definition: itemsfinder.cpp:42
void swap(FDGuard &g1, FDGuard &g2)
Definition: fdguard.cpp:51
auto Stlize(Assoc &&assoc) noexcept
Converts an Qt&#39;s associative sequence assoc to an STL-like iteratable range.
Definition: qtutil.h:50
std::decay_t< T > Removed_
Cat2Items_t GetItems() const
Returns the categorized list of XDG items.
Definition: itemsfinder.cpp:37
decltype(auto) Sorted(Cont &&cont)
Definition: prelude.h:196
std::shared_ptr< ICoreProxy > ICoreProxy_ptr
Definition: icoreproxy.h:177
std::decay_t< T > Intersection_
std::decay_t< T > Added_
ItemsFinder(const ICoreProxy_ptr &, const QList< Type > &types, QObject *parent=nullptr)
Constructs the items finder for the given types.
Definition: itemsfinder.cpp:23
std::shared_ptr< Item > Item_ptr
Definition: item.h:24
QStringList ToPaths(const QList< Type > &types)
Returns a set of typical directories with desktop files of the given types.
Definition: itemtypes.cpp:60
QHash< QString, QList< Item_ptr > > Cat2Items_t
Definition: itemsfinder.h:22
auto ConcatMap(Cont &&c, F &&f)
Definition: prelude.h:168
static Item_ptr FromDesktopFile(const QString &file)
Loads the XDG .desktop item from file.
Definition: item.cpp:197