LeechCraft 0.6.70-17609-g3dde4097dd
Modular cross-platform feature rich live environment.
Loading...
Searching...
No Matches
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
21namespace 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
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}
static Item_ptr FromDesktopFile(const QString &file)
Loads the XDG .desktop item from file.
Definition item.cpp:197
bool IsReady() const
Checks whether this items finder is ready.
ItemsFinder(const ICoreProxy_ptr &, const QList< Type > &types, QObject *parent=nullptr)
Constructs the items finder for the given types.
Item_ptr FindItem(const QString &permanentID) const
Finds an XDG item for the given permanent ID.
void itemsListChanged()
Notifies when the list of items changes in any way.
Cat2Items_t GetItems() const
Returns the categorized list of XDG items.
std::shared_ptr< ICoreProxy > ICoreProxy_ptr
Definition icoreproxy.h:181
QHash< QString, QList< Item_ptr > > Cat2Items_t
Definition itemsfinder.h:22
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
auto Stlize(Assoc &&assoc) noexcept
Converts an Qt's associative sequence assoc to an STL-like iteratable range.
Definition qtutil.h:48
auto ConcatMap(Cont &&c, F &&f)
Definition prelude.h:168
Task< R, ContextExtensions > ContextTask
Definition taskfwd.h:20
decltype(auto) Sorted(Cont &&cont)
Definition prelude.h:196
void swap(FDGuard &g1, FDGuard &g2)
Definition fdguard.cpp:51