File indexing completed on 2025-04-27 04:37:43
0001 /* 0002 SPDX-FileCopyrightText: 2008 Urs Wolfer <uwolfer@kde.org> 0003 SPDX-FileCopyrightText: 2009 Tony Murray <murraytony@gmail.com> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "remotedesktopsmodel.h" 0009 #include "krdc_debug.h" 0010 0011 #include <KFormat> 0012 #include <KLocalizedString> 0013 #ifdef BUILD_ZEROCONF 0014 #include <KDNSSD/RemoteService> 0015 #include <KDNSSD/ServiceBrowser> 0016 #include <kdnssd_version.h> 0017 #endif 0018 #include <QLoggingCategory> 0019 #include <QObject> 0020 #include <QStandardPaths> 0021 0022 RemoteDesktopsModel::RemoteDesktopsModel(QObject *parent, KBookmarkManager *manager) 0023 : QAbstractTableModel(parent) 0024 { 0025 m_manager = manager; 0026 connect(m_manager, SIGNAL(changed(QString, QString)), SLOT(bookmarksChanged())); 0027 buildModelFromBookmarkGroup(m_manager->root()); 0028 0029 #ifdef BUILD_ZEROCONF 0030 // Add RDP and NX if they start announcing via Zeroconf: 0031 m_protocols[QLatin1String("_rfb._tcp")] = QLatin1String("vnc"); 0032 0033 zeroconfBrowser = new KDNSSD::ServiceBrowser(QLatin1String("_rfb._tcp"), true); 0034 connect(zeroconfBrowser, SIGNAL(finished()), this, SLOT(servicesChanged())); 0035 zeroconfBrowser->startBrowse(); 0036 qCDebug(KRDC) << "Browsing for zeroconf hosts."; 0037 #endif 0038 } 0039 0040 RemoteDesktopsModel::~RemoteDesktopsModel() 0041 { 0042 } 0043 0044 int RemoteDesktopsModel::columnCount(const QModelIndex &) const 0045 { 0046 return 6; // same as count of RemoteDesktopsModel::DisplayItems enum 0047 } 0048 0049 int RemoteDesktopsModel::rowCount(const QModelIndex &) const 0050 { 0051 return remoteDesktops.size(); 0052 } 0053 0054 Qt::ItemFlags RemoteDesktopsModel::flags(const QModelIndex &index) const 0055 { 0056 if (!index.isValid()) 0057 return Qt::NoItemFlags; 0058 if (index.column() == RemoteDesktopsModel::Favorite) { 0059 return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; 0060 } 0061 return Qt::ItemIsEnabled; 0062 } 0063 0064 bool RemoteDesktopsModel::setData(const QModelIndex &index, const QVariant &value, int role) 0065 { 0066 if (index.isValid() && role == Qt::CheckStateRole && index.column() == RemoteDesktopsModel::Favorite) { 0067 bool checked = (Qt::CheckState)value.toUInt() == Qt::Checked; 0068 remoteDesktops[index.row()].favorite = checked; 0069 0070 RemoteDesktop rd = remoteDesktops.at(index.row()); 0071 if (checked) { 0072 KBookmarkGroup root = m_manager->root(); 0073 root.addBookmark(rd.title, QUrl(rd.url), QString()); 0074 m_manager->emitChanged(root); 0075 } else { 0076 BookmarkManager::removeByUrl(m_manager, rd.url, true, rd.title); 0077 } 0078 return true; 0079 } 0080 return false; 0081 } 0082 0083 QVariant RemoteDesktopsModel::data(const QModelIndex &index, int role) const 0084 { 0085 if (!index.isValid()) 0086 return QVariant(); 0087 0088 RemoteDesktop item = remoteDesktops.at(index.row()); 0089 0090 switch (role) { 0091 case Qt::DisplayRole: 0092 switch (index.column()) { 0093 case RemoteDesktopsModel::Favorite: 0094 return item.favorite; 0095 case RemoteDesktopsModel::Title: 0096 return item.title; 0097 case RemoteDesktopsModel::LastConnected: 0098 return QVariant(item.lastConnected); 0099 case RemoteDesktopsModel::VisitCount: 0100 return item.visits; 0101 case RemoteDesktopsModel::Created: 0102 if (item.created.isNull()) 0103 return QVariant(); 0104 return QLocale().toString(item.created.toLocalTime(), QLocale::FormatType::ShortFormat); 0105 case RemoteDesktopsModel::Source: 0106 switch (item.source) { 0107 case RemoteDesktop::Bookmarks: 0108 return i18nc("Where each displayed link comes from", "Bookmarks"); 0109 case RemoteDesktop::History: 0110 return i18nc("Where each displayed link comes from", "History"); 0111 case RemoteDesktop::Zeroconf: 0112 return i18nc("Where each displayed link comes from", "Zeroconf"); 0113 case RemoteDesktop::None: 0114 return i18nc("Where each displayed link comes from", "None"); 0115 } 0116 // fall-through 0117 default: 0118 return QVariant(); 0119 } 0120 0121 case Qt::CheckStateRole: 0122 if (index.column() == RemoteDesktopsModel::Favorite) 0123 return item.favorite ? Qt::Checked : Qt::Unchecked; 0124 return QVariant(); 0125 0126 case Qt::ToolTipRole: 0127 switch (index.column()) { 0128 case RemoteDesktopsModel::Favorite: 0129 if (item.favorite) { 0130 return i18nc("Remove the selected url from the bookarks menu", "Remove the bookmark for %1.", item.title); 0131 } else { 0132 return i18nc("Add the selected url to the bookmarks menu", "Bookmark %1.", item.title); 0133 } 0134 case RemoteDesktopsModel::LastConnected: 0135 if (!item.lastConnected.isNull()) { 0136 return QLocale().toString(item.lastConnected.toLocalTime(), QLocale::FormatType::LongFormat); 0137 } 0138 break; // else show default tooltip 0139 case RemoteDesktopsModel::Created: 0140 if (!item.created.isNull()) { 0141 return QLocale().toString(item.lastConnected.toLocalTime(), QLocale::FormatType::LongFormat); 0142 } 0143 break; // else show default tooltip 0144 default: 0145 break; 0146 } 0147 return item.url; // use the url for the tooltip 0148 0149 case 10001: // url for dockwidget 0150 return item.url; 0151 0152 case 10002: // filter 0153 return QUrl::fromPercentEncoding(QString(item.url + item.title).toUtf8()); // return both user visible title and url data, percent encoded 0154 0155 case 10003: // title for dockwidget 0156 return item.title; 0157 0158 default: 0159 return QVariant(); 0160 } 0161 } 0162 0163 QVariant RemoteDesktopsModel::headerData(int section, Qt::Orientation orientation, int role) const 0164 { 0165 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { 0166 switch (section) { 0167 case RemoteDesktopsModel::Favorite: 0168 return QVariant(); // the favorite column is to small for a header 0169 case RemoteDesktopsModel::Title: 0170 return i18nc("Header of the connections list, title/url for remote connection", "Remote Desktop"); 0171 case RemoteDesktopsModel::LastConnected: 0172 return i18nc("Header of the connections list, the last time this connection was initiated", "Last Connected"); 0173 case RemoteDesktopsModel::VisitCount: 0174 return i18nc("Header of the connections list, the number of times this connection has been visited", "Visits"); 0175 case RemoteDesktopsModel::Created: 0176 return i18nc("Header of the connections list, the time when this entry was created", "Created"); 0177 case RemoteDesktopsModel::Source: 0178 return i18nc("Header of the connections list, where this entry comes from", "Source"); 0179 } 0180 } 0181 return QVariant(); 0182 } 0183 0184 // does not trigger view update, you must do this by hand after using this function 0185 void RemoteDesktopsModel::removeAllItemsFromSources(RemoteDesktop::Sources sources) 0186 { 0187 QMutableListIterator<RemoteDesktop> iter(remoteDesktops); 0188 while (iter.hasNext()) { 0189 iter.next(); 0190 // if it matches any of the specified sources, remove it 0191 if ((iter.value().source & sources) > 0) 0192 iter.remove(); 0193 } 0194 } 0195 0196 void RemoteDesktopsModel::bookmarksChanged() 0197 { 0198 removeAllItemsFromSources(RemoteDesktop::Bookmarks | RemoteDesktop::History); 0199 buildModelFromBookmarkGroup(m_manager->root()); 0200 beginResetModel(); 0201 endResetModel(); 0202 } 0203 0204 // Danger Will Roobinson, confusing code ahead! 0205 void RemoteDesktopsModel::buildModelFromBookmarkGroup(const KBookmarkGroup &group) 0206 { 0207 KBookmark bm = group.first(); 0208 while (!bm.isNull()) { 0209 if (bm.isGroup()) { 0210 // recurse subfolders and treat it special if it is the history folder 0211 buildModelFromBookmarkGroup(bm.toGroup()); 0212 } else { // not a group 0213 0214 RemoteDesktop item; 0215 item.title = bm.fullText(); 0216 item.url = bm.url().url(); 0217 int index = remoteDesktops.indexOf(item); // search for this url to see if we need to update it 0218 bool newItem = index < 0; // do we need to create a new item? 0219 0220 // we want to merge all copies of a url into one link, so if the item exists, update it 0221 if (group.metaDataItem(QLatin1String("krdc-history")) == QLatin1String("historyfolder")) { 0222 // set source and favorite (will override later if needed) 0223 item.source = RemoteDesktop::History; 0224 item.favorite = false; 0225 0226 // since we are in the history folder collect statistics and add them 0227 QDateTime connected = QDateTime(); 0228 QDateTime created = QDateTime(); 0229 bool ok = false; 0230 // first the created datetime 0231 created = QDateTime::fromSecsSinceEpoch(bm.metaDataItem(QLatin1String("time_added")).toLongLong(&ok)); 0232 if (ok) 0233 (newItem ? item : remoteDesktops[index]).created = created; 0234 // then the last visited datetime 0235 ok = false; 0236 connected = QDateTime::fromSecsSinceEpoch(bm.metaDataItem(QLatin1String("time_visited")).toLongLong(&ok)); 0237 if (ok) 0238 (newItem ? item : remoteDesktops[index]).lastConnected = connected; 0239 // finally the visited count 0240 ok = false; 0241 int visits = bm.metaDataItem(QLatin1String("visit_count")).toInt(&ok); 0242 if (ok) 0243 (newItem ? item : remoteDesktops[index]).visits = visits; 0244 } else { 0245 if (newItem) { 0246 // if this is a new item, just add the rest of the required data 0247 item.lastConnected = QDateTime(); 0248 item.created = QDateTime(); 0249 item.visits = 0; 0250 item.favorite = true; 0251 item.source = RemoteDesktop::Bookmarks; 0252 } else { 0253 // otherwise override these fields with the info from the bookmark 0254 remoteDesktops[index].title = bm.fullText(); 0255 remoteDesktops[index].favorite = true; 0256 remoteDesktops[index].source = RemoteDesktop::Bookmarks; 0257 } 0258 } 0259 // if we have a new item, add it 0260 if (newItem) 0261 remoteDesktops.append(item); 0262 } 0263 bm = group.next(bm); // next item in the group 0264 } 0265 } 0266 0267 #ifdef BUILD_ZEROCONF 0268 void RemoteDesktopsModel::servicesChanged() 0269 { 0270 // redo list because it is easier than finding and removing one that disappeared 0271 const QList<KDNSSD::RemoteService::Ptr> services = zeroconfBrowser->services(); 0272 QUrl url; 0273 removeAllItemsFromSources(RemoteDesktop::Zeroconf); 0274 for (const KDNSSD::RemoteService::Ptr &service : services) { 0275 url.setScheme(m_protocols[service->type()].toLower()); 0276 url.setHost(service->hostName()); 0277 url.setPort(service->port()); 0278 0279 RemoteDesktop item; 0280 item.url = url.url(); 0281 0282 if (!remoteDesktops.contains(item)) { 0283 item.title = service->serviceName(); 0284 item.source = RemoteDesktop::Zeroconf; 0285 item.created = QDateTime::currentDateTime(); 0286 item.favorite = false; 0287 item.visits = 0; 0288 remoteDesktops.append(item); 0289 } 0290 } 0291 beginResetModel(); 0292 endResetModel(); 0293 } 0294 #endif