1/****************************************************************************
2**
3** Copyright (C) 2008 Urs Wolfer <uwolfer @ kde.org>
4** Copyright (C) 2009 Tony Murray <murraytony@gmail.com>
5**
6** This file is part of KDE.
7**
8** This program is free software; you can redistribute it and/or modify
9** it under the terms of the GNU General Public License as published by
10** the Free Software Foundation; either version 2 of the License, or
11** (at your option) any later version.
12**
13** This program is distributed in the hope that it will be useful,
14** but WITHOUT ANY WARRANTY; without even the implied warranty of
15** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16** GNU General Public License for more details.
17**
18** You should have received a copy of the GNU General Public License
19** along with this program; see the file COPYING. If not, write to
20** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21** Boston, MA 02110-1301, USA.
22**
23****************************************************************************/
24
25#include "remotedesktopsmodel.h"
26#include "bookmarkmanager.h"
27
28#include <KStandardDirs>
29#include <KDebug>
30#include <KLocale>
31
32RemoteDesktopsModel::RemoteDesktopsModel(QObject *parent)
33 : QAbstractTableModel(parent)
34{
35 const QString file = KStandardDirs::locateLocal("data", "krdc/bookmarks.xml");
36 m_manager = KBookmarkManager::managerForFile(file, "krdc");
37 m_manager->setUpdate(true);
38 connect(m_manager, SIGNAL(changed(QString,QString)), SLOT(bookmarksChanged()));
39 buildModelFromBookmarkGroup(m_manager->root());
40
41#ifdef BUILD_ZEROCONF
42 // Add RDP and NX if they start announcing via Zeroconf:
43 m_protocols["_rfb._tcp"] = "vnc";
44
45 zeroconfBrowser = new DNSSD::ServiceBrowser("_rfb._tcp", true);
46 connect(zeroconfBrowser, SIGNAL(finished()), this, SLOT(servicesChanged()));
47 zeroconfBrowser->startBrowse();
48 kDebug(5010) << "Browsing for zeroconf hosts.";
49#endif
50}
51
52RemoteDesktopsModel::~RemoteDesktopsModel()
53{
54}
55
56int RemoteDesktopsModel::columnCount(const QModelIndex &) const
57{
58 return 6; // same as count of RemoteDesktopsModel::DisplayItems enum
59}
60
61int RemoteDesktopsModel::rowCount(const QModelIndex &) const
62{
63 return remoteDesktops.size();
64}
65
66Qt::ItemFlags RemoteDesktopsModel::flags(const QModelIndex &index) const
67{
68 if (!index.isValid())
69 return 0;
70 if (index.column() == RemoteDesktopsModel::Favorite) {
71 return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
72 }
73 return Qt::ItemIsEnabled;
74}
75
76bool RemoteDesktopsModel::setData(const QModelIndex &index, const QVariant &value, int role)
77{
78 if (index.isValid() && role == Qt::CheckStateRole && index.column() == RemoteDesktopsModel::Favorite) {
79 bool checked = (Qt::CheckState)value.toUInt() == Qt::Checked;
80 remoteDesktops[index.row()].favorite = checked;
81
82 RemoteDesktop rd = remoteDesktops.at(index.row());
83 if (checked) {
84 KBookmarkGroup root = m_manager->root();
85 root.addBookmark(rd.title, rd.url);
86 m_manager->emitChanged(root);
87 } else {
88 BookmarkManager::removeByUrl(m_manager, rd.url, true, rd.title);
89 }
90 return true;
91 }
92 return false;
93}
94
95QVariant RemoteDesktopsModel::data(const QModelIndex &index, int role) const
96{
97 if (!index.isValid())
98 return QVariant();
99
100 RemoteDesktop item = remoteDesktops.at(index.row());
101
102 switch (role) {
103 case Qt::DisplayRole:
104 switch (index.column()) {
105 case RemoteDesktopsModel::Favorite:
106 return item.favorite;
107 case RemoteDesktopsModel::Title:
108 return item.title;
109 case RemoteDesktopsModel::LastConnected:
110 return QVariant(item.lastConnected.dateTime());
111 case RemoteDesktopsModel::VisitCount:
112 return item.visits;
113 case RemoteDesktopsModel::Created:
114 if (item.created.isNull()) return QVariant();
115 return KGlobal::locale()->formatDateTime(item.created.toLocalZone(), KLocale::ShortDate);
116 case RemoteDesktopsModel::Source:
117 switch (item.source) {
118 case RemoteDesktop::Bookmarks:
119 return i18nc("Where each displayed link comes from", "Bookmarks");
120 case RemoteDesktop::History:
121 return i18nc("Where each displayed link comes from", "History");
122 case RemoteDesktop::Zeroconf:
123 return i18nc("Where each displayed link comes from", "Zeroconf");
124 case RemoteDesktop::None:
125 return i18nc("Where each displayed link comes from", "None");
126 }
127 default:
128 return QVariant();
129 }
130
131 case Qt::CheckStateRole:
132 if (index.column() == RemoteDesktopsModel::Favorite)
133 return item.favorite ? Qt::Checked : Qt::Unchecked;
134 return QVariant();
135
136 case Qt::ToolTipRole:
137 switch(index.column()) {
138 case RemoteDesktopsModel::Favorite:
139 if (item.favorite) {
140 return i18nc("Remove the selected url from the bookarks menu", "Remove the bookmark for %1.", item.title);
141 } else {
142 return i18nc("Add the selected url to the bookmarks menu", "Bookmark %1.", item.title);
143 }
144 case RemoteDesktopsModel::LastConnected:
145 if (!item.lastConnected.isNull()) {
146 return KGlobal::locale()->formatDateTime(item.lastConnected.toLocalZone(), KLocale::FancyLongDate);
147 }
148 break; // else show default tooltip
149 case RemoteDesktopsModel::Created:
150 if (!item.created.isNull()) {
151 return KGlobal::locale()->formatDateTime(item.created.toLocalZone(), KLocale::FancyLongDate);
152 }
153 break; // else show default tooltip
154 default:
155 break;
156 }
157 return item.url; //use the url for the tooltip
158
159 case 10001: //url for dockwidget
160 return item.url;
161
162 case 10002: //filter
163 return QUrl::fromPercentEncoding(QString(item.url + item.title).toUtf8()); // return both user visible title and url data, percent encoded
164
165 case 10003: //title for dockwidget
166 return item.title;
167
168 default:
169 return QVariant();
170 }
171}
172
173QVariant RemoteDesktopsModel::headerData(int section, Qt::Orientation orientation,
174 int role) const
175{
176 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
177 switch (section) {
178 case RemoteDesktopsModel::Favorite:
179 return QVariant(); // the favorite column is to small for a header
180 case RemoteDesktopsModel::Title:
181 return i18nc("Header of the connections list, title/url for remote connection", "Remote Desktop");
182 case RemoteDesktopsModel::LastConnected:
183 return i18nc("Header of the connections list, the last time this connection was initiated", "Last Connected");
184 case RemoteDesktopsModel::VisitCount:
185 return i18nc("Header of the connections list, the number of times this connection has been visited", "Visits");
186 case RemoteDesktopsModel::Created:
187 return i18nc("Header of the connections list, the time when this entry was created", "Created");
188 case RemoteDesktopsModel::Source:
189 return i18nc("Header of the connections list, where this entry comes from", "Source");
190 }
191 }
192 return QVariant();
193}
194
195// does not trigger view update, you must do this by hand after using this function
196void RemoteDesktopsModel::removeAllItemsFromSources(RemoteDesktop::Sources sources)
197{
198 QMutableListIterator<RemoteDesktop> iter(remoteDesktops);
199 while (iter.hasNext()) {
200 iter.next();
201 // if it matches any of the specified sources, remove it
202 if ((iter.value().source & sources) > 0)
203 iter.remove();
204 }
205}
206
207void RemoteDesktopsModel::bookmarksChanged()
208{
209 kDebug(5010);
210 removeAllItemsFromSources(RemoteDesktop::Bookmarks | RemoteDesktop::History);
211 buildModelFromBookmarkGroup(m_manager->root());
212 reset();
213}
214
215// Danger Will Roobinson, confusing code ahead!
216void RemoteDesktopsModel::buildModelFromBookmarkGroup(const KBookmarkGroup &group)
217{
218 KBookmark bm = group.first();
219 while (!bm.isNull()) {
220 if (bm.isGroup()) {
221 // recurse subfolders and treat it special if it is the history folder
222 buildModelFromBookmarkGroup(bm.toGroup());
223 } else { // not a group
224
225 RemoteDesktop item;
226 item.title = bm.fullText();
227 item.url = bm.url().url();
228 int index = remoteDesktops.indexOf(item); //search for this url to see if we need to update it
229 bool newItem = index < 0; // do we need to create a new item?
230
231 // we want to merge all copies of a url into one link, so if the item exists, update it
232 if (group.metaDataItem("krdc-history") == "historyfolder") {
233 // set source and favorite (will override later if needed)
234 item.source = RemoteDesktop::History;
235 item.favorite = false;
236
237 // since we are in the history folder collect statitics and add them
238 KDateTime connected = KDateTime();
239 KDateTime created = KDateTime();
240 bool ok = false;
241 // first the created datetime
242 created.setTime_t(bm.metaDataItem("time_added").toLongLong(&ok));
243 if (ok) (newItem ? item : remoteDesktops[index]).created = created;
244 // then the last visited datetime
245 ok = false;
246 connected.setTime_t(bm.metaDataItem("time_visited").toLongLong(&ok));
247 if (ok) (newItem ? item : remoteDesktops[index]).lastConnected = connected;
248 // finally the visited count
249 ok = false;
250 int visits = bm.metaDataItem("visit_count").toInt(&ok);
251 if (ok) (newItem ? item : remoteDesktops[index]).visits = visits;
252 } else {
253 if (newItem) {
254 // if this is a new item, just add the rest of the required data
255 item.lastConnected = KDateTime();
256 item.created = KDateTime();
257 item.visits = 0;
258 item.favorite = true;
259 item.source = RemoteDesktop::Bookmarks;
260 } else {
261 // otherwise override these fields with the info from the bookmark
262 remoteDesktops[index].title = bm.fullText();
263 remoteDesktops[index].favorite = true;
264 remoteDesktops[index].source = RemoteDesktop::Bookmarks;
265 }
266 }
267 // if we have a new item, add it
268 if (newItem)
269 remoteDesktops.append(item);
270 }
271 bm = group.next(bm); // next item in the group
272 }
273}
274
275#ifdef BUILD_ZEROCONF
276void RemoteDesktopsModel::servicesChanged()
277{
278 //redo list because it is easier than finding and removing one that disappeared
279 QList<DNSSD::RemoteService::Ptr> services = zeroconfBrowser->services();
280 KUrl url;
281 removeAllItemsFromSources(RemoteDesktop::Zeroconf);
282 foreach(DNSSD::RemoteService::Ptr service, services) {
283 url.setProtocol(m_protocols[service->type()].toLower());
284 url.setHost(service->hostName());
285 url.setPort(service->port());
286
287 RemoteDesktop item;
288 item.url = url.url();
289
290 if (!remoteDesktops.contains(item)) {
291 item.title = service->serviceName();
292 item.source = RemoteDesktop::Zeroconf;
293 item.created = KDateTime::currentLocalDateTime();
294 item.favorite = false;
295 item.visits = 0;
296 remoteDesktops.append(item);
297 }
298 }
299 reset();
300}
301#endif
302
303#include "remotedesktopsmodel.moc"
304