1+ #include " AppSelector.hpp"
2+
3+ #include " Model/AppListModel.hpp"
4+ #include " Model/AppListDelegate.hpp"
5+
6+ #include < cstdlib>
7+ #include < filesystem>
8+ #include < fstream>
9+ #include < sstream>
10+ #include < iostream>
11+ #include < algorithm>
12+
13+ #include < RunAsGPU/Shared/Shared.hpp>
14+
15+ #include < QFileInfo>
16+ #include < QDir>
17+ #include < QLineEdit>
18+ #include < QSortFilterProxyModel>
19+ #include < QStandardItemModel>
20+
21+ namespace fs = std::filesystem;
22+
23+ std::vector<Application> ListInstalledApps () {
24+ std::vector<Application> appList;
25+ std::vector<std::string> searchPaths;
26+
27+ const char *xdgDirs = getenv (" XDG_DATA_DIRS" );
28+ if (xdgDirs) {
29+ std::istringstream ss (xdgDirs);
30+ std::string path;
31+
32+ while (std::getline (ss, path, ' :' ))
33+ searchPaths.push_back (path + " /applications" );
34+ } else
35+ searchPaths = {" /usr/share/applications" , " /usr/local/share/applications" };
36+
37+ const char *homeDir = getenv (" HOME" );
38+ if (homeDir)
39+ searchPaths.push_back (std::string (homeDir) + " /.local/share/applications" );
40+
41+ for (const auto &path: searchPaths) {
42+ if (!fs::exists (path))
43+ continue ;
44+
45+ if (!fs::is_directory (path))
46+ continue ;
47+
48+ for (const auto &entry: fs::directory_iterator (path)) {
49+ if (entry.path ().extension () != " .desktop" )
50+ continue ;
51+
52+ QString name, description, execPath, iconStr;
53+ std::ifstream file (entry.path ());
54+ if (!file.is_open ())
55+ continue ;
56+
57+ std::map<std::string, std::string> desktopEntries;
58+ std::string line;
59+
60+ bool inDesktopEntry = false ;
61+ while (std::getline (file, line)) {
62+ line = TrimString (line);
63+
64+ if (line.empty ())
65+ continue ;
66+
67+ if (line.front () == ' [' && line.back () == ' ]' ) {
68+ inDesktopEntry = true ;
69+ continue ;
70+ }
71+
72+ if (inDesktopEntry) {
73+ size_t pos = line.find (' =' );
74+ if (pos == std::string::npos)
75+ continue ;
76+
77+ std::string key = line.substr (0 , pos);
78+ std::string value = line.substr (pos + 1 );
79+ desktopEntries[key] = value;
80+ }
81+ }
82+
83+ if (desktopEntries.find (" Name" ) != desktopEntries.end ())
84+ name = QString::fromStdString (desktopEntries[" Name" ]);
85+ if (desktopEntries.find (" Comment" ) != desktopEntries.end ())
86+ description = QString::fromStdString (desktopEntries[" Comment" ]);
87+ if (desktopEntries.find (" Exec" ) != desktopEntries.end ())
88+ execPath = QString::fromStdString (desktopEntries[" Exec" ]);
89+ if (desktopEntries.find (" Icon" ) != desktopEntries.end ())
90+ iconStr = QString::fromStdString (desktopEntries[" Icon" ]);
91+
92+ Application app{name, description, execPath, iconStr};
93+ appList.push_back (app);
94+ }
95+ }
96+
97+ return appList;
98+ }
99+
100+ std::vector<Application> appList,
101+ filteredApps;
102+
103+ std::vector<QString> qSelectedApps;
104+
105+ std::vector<Application> Ui::selectedItems;
106+
107+ AppListModel *modelAppList;
108+ AppListDelegate *delegateAppList;
109+
110+ void Ui_AppSelector::performLogic () const {
111+ modelAppList = new AppListModel (listInstalledApps);
112+ delegateAppList = new AppListDelegate (listInstalledApps);
113+
114+ self->setFixedSize (self->size ());
115+
116+ listInstalledApps->setModel (modelAppList);
117+ listInstalledApps->setItemDelegate (delegateAppList);
118+ listInstalledApps->setViewMode (QListView::ListMode);
119+ listInstalledApps->setUniformItemSizes (true );
120+ listInstalledApps->setSpacing (5 );
121+ listInstalledApps->setSelectionMode (QAbstractItemView::MultiSelection);
122+ listInstalledApps->setEditTriggers (QAbstractItemView::NoEditTriggers);
123+ listInstalledApps->setFocusPolicy (Qt::StrongFocus);
124+
125+ appList = ListInstalledApps ();
126+ std::sort (appList.begin (), appList.end (), [](const Application &a, const Application &b) {
127+ return a.name .compare (b.name , Qt::CaseInsensitive) < 0 ;
128+ });
129+
130+ for (const auto &app: appList) {
131+ if (app.execPath .isEmpty ())
132+ continue ;
133+
134+ modelAppList->addItem (app);
135+ }
136+
137+ QObject::connect (listInstalledApps->selectionModel (), &QItemSelectionModel::selectionChanged, self,
138+ [&](const QItemSelection &selected, const QItemSelection &deselected) {
139+ for (const QModelIndex &index: selected.indexes ()) {
140+ QString selectedName = modelAppList->data (index, Qt::DisplayRole).toString ();
141+
142+ auto it = std::find_if (qSelectedApps.begin (), qSelectedApps.end (),
143+ [&](const QString &appName) {
144+ return appName == selectedName;
145+ });
146+
147+ if (it == qSelectedApps.end ()) {
148+ auto appIt = std::find_if (appList.begin (), appList.end (), [&](const Application &app) {
149+ return app.name == selectedName;
150+ });
151+
152+ if (appIt != appList.end ())
153+ qSelectedApps.push_back (selectedName);
154+ }
155+ }
156+
157+ for (const QModelIndex &index: deselected.indexes ()) {
158+ QString deselectedName = modelAppList->data (index, Qt::DisplayRole).toString ();
159+
160+ qSelectedApps.erase (std::remove_if (qSelectedApps.begin (), qSelectedApps.end (),
161+ [&](const QString &appName) {
162+ return appName == deselectedName;
163+ }), qSelectedApps.end ());
164+ }
165+ });
166+
167+ QObject::connect (filterBox, &QLineEdit::textChanged, self, [&](const QString &text) {
168+ QItemSelectionModel *selectionModel = listInstalledApps->selectionModel ();
169+
170+ filteredApps.clear ();
171+ for (const auto &app: appList) {
172+ if (app.name .contains (text, Qt::CaseInsensitive))
173+ filteredApps.push_back (app);
174+ }
175+
176+ modelAppList->clear ();
177+ for (const auto &app: filteredApps)
178+ modelAppList->addItem (app);
179+
180+ // restore selections
181+ for (int i = 0 ; i < modelAppList->rowCount (); i++) {
182+ QModelIndex index = modelAppList->index (i, 0 );
183+ QString appName = modelAppList->data (index, Qt::DisplayRole).toString ();
184+ if (std::find (qSelectedApps.begin (), qSelectedApps.end (), appName) != qSelectedApps.end ())
185+ selectionModel->select (index, QItemSelectionModel::Select);
186+ }
187+ });
188+
189+ QObject::connect (buttonBox, &QDialogButtonBox::accepted, self, [&]() {
190+ Ui::selectedItems.clear ();
191+
192+ for (const QString &index: qSelectedApps) {
193+ auto it = std::find_if (appList.begin (), appList.end (), [&](const Application &app) {
194+ return app.name == index;
195+ });
196+
197+ if (it != appList.end ()) {
198+ Ui::selectedItems.push_back (*it);
199+ }
200+ }
201+
202+ listInstalledApps->selectionModel ()->clearSelection ();
203+
204+ self->accept ();
205+ });
206+
207+ QObject::connect (buttonBox, &QDialogButtonBox::rejected, self, [&]() {
208+ self->reject ();
209+ });
210+ }
0 commit comments