File indexing completed on 2024-04-28 08:51:04
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 <KLocalizedString> 0012 #include <KFormat> 0013 #ifdef BUILD_ZEROCONF 0014 #include <kdnssd_version.h> 0015 #include <KDNSSD/ServiceBrowser> 0016 #include <KDNSSD/RemoteService> 0017 #endif 0018 #include <QLoggingCategory> 0019 #include <QStandardPaths> 0020 #include <QObject> 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()) return QVariant(); 0103 return QLocale().toString(item.created.toLocalTime(), QLocale::FormatType::ShortFormat); 0104 case RemoteDesktopsModel::Source: 0105 switch (item.source) { 0106 case RemoteDesktop::Bookmarks: 0107 return i18nc("Where each displayed link comes from", "Bookmarks"); 0108 case RemoteDesktop::History: 0109 return i18nc("Where each displayed link comes from", "History"); 0110 case RemoteDesktop::Zeroconf: 0111 return i18nc("Where each displayed link comes from", "Zeroconf"); 0112 case RemoteDesktop::None: 0113 return i18nc("Where each displayed link comes from", "None"); 0114 } 0115 // fall-through 0116 default: 0117 return QVariant(); 0118 } 0119 0120 case Qt::CheckStateRole: 0121 if (index.column() == RemoteDesktopsModel::Favorite) 0122 return item.favorite ? Qt::Checked : Qt::Unchecked; 0123 return QVariant(); 0124 0125 case Qt::ToolTipRole: 0126 switch(index.column()) { 0127 case RemoteDesktopsModel::Favorite: 0128 if (item.favorite) { 0129 return i18nc("Remove the selected url from the bookarks menu", "Remove the bookmark for %1.", item.title); 0130 } else { 0131 return i18nc("Add the selected url to the bookmarks menu", "Bookmark %1.", item.title); 0132 } 0133 case RemoteDesktopsModel::LastConnected: 0134 if (!item.lastConnected.isNull()) { 0135 return QLocale().toString(item.lastConnected.toLocalTime(), QLocale::FormatType::LongFormat); 0136 } 0137 break; // else show default tooltip 0138 case RemoteDesktopsModel::Created: 0139 if (!item.created.isNull()) { 0140 return QLocale().toString(item.lastConnected.toLocalTime(), QLocale::FormatType::LongFormat); 0141 } 0142 break; // else show default tooltip 0143 default: 0144 break; 0145 } 0146 return item.url; //use the url for the tooltip 0147 0148 case 10001: //url for dockwidget 0149 return item.url; 0150 0151 case 10002: //filter 0152 return QUrl::fromPercentEncoding(QString(item.url + item.title).toUtf8()); // return both user visible title and url data, percent encoded 0153 0154 case 10003: //title for dockwidget 0155 return item.title; 0156 0157 default: 0158 return QVariant(); 0159 } 0160 } 0161 0162 QVariant RemoteDesktopsModel::headerData(int section, Qt::Orientation orientation, 0163 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) (newItem ? item : remoteDesktops[index]).created = created; 0233 // then the last visited datetime 0234 ok = false; 0235 connected = QDateTime::fromSecsSinceEpoch(bm.metaDataItem(QLatin1String("time_visited")).toLongLong(&ok)); 0236 if (ok) (newItem ? item : remoteDesktops[index]).lastConnected = connected; 0237 // finally the visited count 0238 ok = false; 0239 int visits = bm.metaDataItem(QLatin1String("visit_count")).toInt(&ok); 0240 if (ok) (newItem ? item : remoteDesktops[index]).visits = visits; 0241 } else { 0242 if (newItem) { 0243 // if this is a new item, just add the rest of the required data 0244 item.lastConnected = QDateTime(); 0245 item.created = QDateTime(); 0246 item.visits = 0; 0247 item.favorite = true; 0248 item.source = RemoteDesktop::Bookmarks; 0249 } else { 0250 // otherwise override these fields with the info from the bookmark 0251 remoteDesktops[index].title = bm.fullText(); 0252 remoteDesktops[index].favorite = true; 0253 remoteDesktops[index].source = RemoteDesktop::Bookmarks; 0254 } 0255 } 0256 // if we have a new item, add it 0257 if (newItem) 0258 remoteDesktops.append(item); 0259 } 0260 bm = group.next(bm); // next item in the group 0261 } 0262 } 0263 0264 #ifdef BUILD_ZEROCONF 0265 void RemoteDesktopsModel::servicesChanged() 0266 { 0267 //redo list because it is easier than finding and removing one that disappeared 0268 const QList<KDNSSD::RemoteService::Ptr> services = zeroconfBrowser->services(); 0269 QUrl url; 0270 removeAllItemsFromSources(RemoteDesktop::Zeroconf); 0271 for (const KDNSSD::RemoteService::Ptr& service : services) { 0272 url.setScheme(m_protocols[service->type()].toLower()); 0273 url.setHost(service->hostName()); 0274 url.setPort(service->port()); 0275 0276 RemoteDesktop item; 0277 item.url = url.url(); 0278 0279 if (!remoteDesktops.contains(item)) { 0280 item.title = service->serviceName(); 0281 item.source = RemoteDesktop::Zeroconf; 0282 item.created = QDateTime::currentDateTime(); 0283 item.favorite = false; 0284 item.visits = 0; 0285 remoteDesktops.append(item); 0286 } 0287 } 0288 beginResetModel(); 0289 endResetModel(); 0290 } 0291 #endif 0292