add more spacing
[personal-kdebase.git] / workspace / plasma / applets / quicklaunch / quicklaunchApplet.cpp
blob1cff96dcb2cc0cda6d64e0e90515612294ee50af
1 /***************************************************************************
2 * Copyright (C) 2008 by Lukas Appelhans *
3 * l.appelhans@gmx.de *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
20 #include "quicklaunchApplet.h"
22 #include <KConfigDialog>
23 #include <KDesktopFile>
24 #include <QGraphicsSceneDragDropEvent>
25 #include <QGraphicsWidget>
26 #include <QDrag>
27 #include <QMouseEvent>
28 #include <QMimeData>
29 #include <QToolButton>
31 #include <KDialog>
32 #include <KMimeType>
33 #include <KStandardDirs>
34 #include <KWindowSystem>
36 #include <plasma/containment.h>
37 #include <plasma/dialog.h>
38 #include <plasma/corona.h>
40 #include "math.h"
42 static const int s_defaultIconSize = 16;
43 static const int s_defaultSpacing = 2;
45 QuicklaunchApplet::QuicklaunchApplet(QObject *parent, const QVariantList &args)
46 : Plasma::Applet(parent, args),
47 m_visibleIcons(6),
48 m_rowCount(2),
49 m_dialogRowCount(2),
50 m_dialog(0),
51 m_dialogWidget(0),
52 m_dialogLayout(0),
53 m_addDialog(0),
54 m_rightClickedIcon(0),
55 m_addAction(0),
56 m_removeAction(0)
58 setHasConfigurationInterface(true);
59 setAcceptDrops(true);
60 setAspectRatioMode(Plasma::IgnoreAspectRatio);
62 // set our default size here
63 resize((m_visibleIcons / m_rowCount) * s_defaultIconSize +
64 (s_defaultSpacing * (m_visibleIcons + 1)),
65 m_rowCount * 22 + s_defaultSpacing * 3);
68 QuicklaunchApplet::~QuicklaunchApplet()
70 if (m_dialog) {
71 m_dialog->close();
72 delete m_dialog;
75 delete m_dialogWidget;
78 void QuicklaunchApplet::saveState(KConfigGroup &config) const
80 QStringList iconUrls;
81 foreach (QuicklaunchIcon * container, m_icons) {
82 iconUrls.append(container->url().prettyUrl());
85 config.writeEntry("iconUrls", iconUrls);
88 void QuicklaunchApplet::init()
90 KConfigGroup cg = config();
91 m_rowCount = qMax(1, cg.readEntry("rowCount", m_rowCount));
92 m_visibleIcons = qMax(1, cg.readEntry("visibleIcons", m_visibleIcons));
93 m_dialogRowCount = qMax(1, cg.readEntry("dialogRowCount", m_dialogRowCount));
95 // Initialize outer layout
96 m_layout = new QGraphicsLinearLayout(this);
97 m_layout->setContentsMargins(0, 0, 0, 0);
98 m_layout->setSpacing(0);
99 setLayout(m_layout);
101 // Initialize inner layout
102 m_innerLayout = new QuicklaunchLayout(0, m_rowCount);
103 m_innerLayout->setContentsMargins(0, 0, 0, 0);
104 m_innerLayout->setSpacing(0);
105 m_layout->addItem(m_innerLayout);
107 // Initial "more icons" arrow
108 m_arrow = new Plasma::IconWidget(this);
109 m_arrow->setIcon(KIcon("arrow-right"));
110 connect(m_arrow, SIGNAL(clicked()), SLOT(showDialog()));
112 QStringList desktopFiles = cg.readEntry("iconUrls", QStringList());
114 if (desktopFiles.isEmpty()) {
115 QStringList defaultApps;
116 defaultApps << "konqbrowser" << "dolphin" << "kopete";
118 foreach (const QString &defaultApp, defaultApps) {
119 KService::Ptr service = KService::serviceByStorageId(defaultApp);
120 if (service && service->isValid()) {
121 QString path = service->entryPath();
122 kDebug() << path;
123 if (!path.isEmpty() && QDir::isAbsolutePath(path)) {
124 desktopFiles << path;
128 kDebug() << desktopFiles;
131 loadPrograms(desktopFiles);
132 refactorUi();
135 QSizeF QuicklaunchApplet::sizeHint(Qt::SizeHint which, const QSizeF & constraint) const
137 if (which == Qt::PreferredSize) {
138 QSizeF sizeHint = size();
139 if (m_icons.size() > m_visibleIcons) {
140 sizeHint.setWidth(sizeHint.height() / m_innerLayout->rowCount() * m_innerLayout->columnCount() + size().height());
141 } else {
142 sizeHint.setWidth(sizeHint.height() / m_innerLayout->rowCount() * m_innerLayout->columnCount());
144 return sizeHint;
146 return QGraphicsWidget::sizeHint(which, constraint);
149 void QuicklaunchApplet::constraintsEvent(Plasma::Constraints constraints)
151 if (constraints & Plasma::SizeConstraint) {
152 //TODO: don't call so often
153 refactorUi();
157 void QuicklaunchApplet::refactorUi()
159 clearLayout(m_innerLayout);
161 if (m_dialogLayout) {
162 clearLayout(m_dialogLayout);
163 m_dialogLayout->setRowCount(m_dialogRowCount);
165 int rowCount;
166 int iconWidth;
167 if (formFactor() == Plasma::Vertical) {
168 rowCount = qMin(m_rowCount, int(size().width()) / (s_defaultIconSize + s_defaultSpacing));
169 // prevent possible division by zero if size().width() is 0
170 rowCount = qMax(1, rowCount );
171 iconWidth = size().width() / rowCount;
172 } else {
173 rowCount = qMin(m_rowCount, int(size().height()) / (s_defaultIconSize + s_defaultSpacing));
174 // prevent possible division by zero if size().height() is 0
175 rowCount = qMax(1, rowCount );
176 iconWidth = qMax(s_defaultIconSize, int(size().height()) / rowCount);
179 m_innerLayout->setRowCount(rowCount);
180 const QSizeF minSize = QSizeF(iconWidth, iconWidth);
181 const QSizeF maxSize = QSizeF(iconWidth, iconWidth);
182 int count = 0;
183 kDebug() << m_icons.count() << iconWidth << "pixel icons in" << rowCount
184 << "rows, with a max of" << m_visibleIcons << "visible";
185 foreach (QuicklaunchIcon *icon, m_icons) {
186 //icon->setMinimumSize(minSize);
187 //icon->setMaximumSize(maxSize);
188 icon->resize(minSize);
190 if (count < m_visibleIcons || m_visibleIcons == -1) {
191 icon->show();
192 m_innerLayout->addItem(icon);
193 } else if (m_dialogLayout) {
194 icon->setMinimumSize(minSize);
195 icon->setMaximumSize(maxSize);
196 icon->show();
197 m_dialogLayout->addItem(icon);
198 } else {
199 icon->hide();
202 ++count;
205 m_layout->removeItem(m_arrow);
206 if (count > m_visibleIcons && m_visibleIcons != -1) {
207 //m_arrow->setMinimumSize(minSize);
208 //m_arrow->setMaximumSize(maxSize);
209 m_arrow->resize(minSize);
210 m_layout->addItem(m_arrow);
211 m_arrow->show();
212 } else {
213 m_arrow->hide();
216 if (m_dialog) {
217 m_dialog->close();
218 m_dialogLayout->updateGeometry();
219 m_dialog->adjustSize();
221 m_innerLayout->updateGeometry();
222 m_layout->updateGeometry();
225 void QuicklaunchApplet::showDialog()
227 if (!m_dialog) {
228 m_dialogWidget = new QGraphicsWidget(this);
229 m_dialogWidget->setAcceptDrops(true);
230 m_dialogWidget->installEventFilter(this);
231 qobject_cast<Plasma::Corona*>(m_dialogWidget->scene())->addOffscreenWidget(m_dialogWidget);
233 // Initialize "more icons" dialog
234 m_dialog = new Plasma::Dialog(0, Qt::X11BypassWindowManagerHint);
235 m_dialog->setAcceptDrops(true);
236 //m_dialog->installEventFilter(this);
237 m_dialog->setContextMenuPolicy(Qt::ActionsContextMenu);
238 m_dialogLayout = new QuicklaunchLayout(m_dialogWidget, m_dialogRowCount);
239 m_dialogWidget->setLayout(m_dialogLayout);
240 refactorUi();
241 m_dialog->setGraphicsWidget(m_dialogWidget);
244 if (m_dialog->isVisible()) {
245 m_dialog->hide();
246 } else {
247 m_dialog->resize(m_dialogLayout->preferredSize().toSize());
248 //m_dialog->updateGeometry();
249 if(containment() && containment()->corona()) {
250 kDebug() << "position:" << containment()->corona()->popupPosition(m_arrow, m_dialog->size()) << "dialog size:" << m_dialog->size() << "layout preferred-size:" << m_dialogLayout->preferredSize().toSize();
251 m_dialog->move(containment()->corona()->popupPosition(m_arrow, m_dialog->size()));
253 KWindowSystem::setState(m_dialog->winId(), NET::SkipTaskbar);
254 //QPoint(popupPosition(m_dialog->sizeHint()).x() + (m_visibleIcons) * size().height() / m_rowCount,
255 // popupPosition(m_dialog->sizeHint()).y()));
256 m_dialog->show();
260 void QuicklaunchApplet::createConfigurationInterface(KConfigDialog *parent)
262 QWidget *widget = new QWidget(parent);
263 uiConfig.setupUi(widget);
264 connect(parent, SIGNAL(accepted()), SLOT(configAccepted()));
265 uiConfig.rowCount->setValue(m_rowCount);
266 uiConfig.dialogRowCount->setValue(m_dialogRowCount);
267 uiConfig.dialogRowCount->hide();
268 uiConfig.dialogrowLabel->hide();
269 uiConfig.icons->setValue(m_visibleIcons);
270 parent->addPage(widget, i18n("General"), icon());
273 void QuicklaunchApplet::configAccepted()
275 bool changed = false;
276 int temp = uiConfig.rowCount->value();
278 KConfigGroup cg = config();
279 if (temp != m_rowCount) {
280 m_rowCount = temp;
281 cg.writeEntry("rowCount", m_rowCount);
282 changed = true;
285 temp = uiConfig.icons->value();
286 if (temp != m_visibleIcons) {
287 m_visibleIcons = temp;
288 cg.writeEntry("visibleIcons", m_visibleIcons);
289 changed = true;
292 temp = uiConfig.dialogRowCount->value();
293 if (temp != m_dialogRowCount) {
294 m_dialogRowCount = temp;
295 cg.writeEntry("dialogRowCount", m_dialogRowCount);
296 changed = true;
299 if (changed) {
300 emit configNeedsSaving();
301 refactorUi();
305 QList<QAction*> QuicklaunchApplet::contextActions(QuicklaunchIcon *icon)
307 QList<QAction*> tempActions;
308 if (!m_addAction) {
309 m_addAction = new QAction(KIcon("list-add"), i18n("Add Icon..."), this);
310 connect(m_addAction, SIGNAL(triggered(bool)), this, SLOT(showAddInterface()));
313 tempActions << m_addAction;
315 if (icon) {
316 m_rightClickedIcon = icon;
317 if (!m_removeAction) {
318 m_removeAction = new QAction(KIcon("list-remove"), i18n("Remove Icon"), this);
319 connect(m_removeAction, SIGNAL(triggered(bool)), this, SLOT(removeCurrentIcon()));
321 tempActions << m_removeAction;
324 return tempActions;
327 bool QuicklaunchApplet::eventFilter(QObject * object, QEvent * event)
329 Q_UNUSED(object)
330 if (event->type() == QEvent::GraphicsSceneDrop) {
331 dropEvent(static_cast<QGraphicsSceneDragDropEvent*>(event));
332 return true;
334 return QObject::eventFilter(object, event);
337 void QuicklaunchApplet::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
339 event->setDropAction(Qt::MoveAction);
340 event->setAccepted(event->mimeData()->hasUrls());
343 void QuicklaunchApplet::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
345 event->setDropAction(Qt::MoveAction);
346 event->setAccepted(event->mimeData()->hasUrls());
349 void QuicklaunchApplet::dropEvent(QGraphicsSceneDragDropEvent *event)
351 // Calculate position
352 QPointF point = mapFromScene(event->scenePos());
353 int rowCount = m_innerLayout->rowCount();
354 int cols = static_cast<int>(ceil(1.0 * qMin(m_icons.size(), m_visibleIcons) / rowCount));
355 int col = static_cast<int>((round(point.x()) * cols / m_innerLayout->geometry().width()));
356 col = (col >= cols) ? col - 1 : col;
357 int row = static_cast<int>(floor(point.y() * rowCount / m_innerLayout->geometry().height()));
358 row = (row >= m_rowCount) ? row - 1 : row;
359 int pos = row * cols + col;
361 if (pos >= m_icons.size()) {
362 pos = m_icons.size() - 1;
365 if (dropHandler(pos, event->mimeData())) {
366 event->setDropAction(Qt::MoveAction);
367 event->accept();
368 saveConfig();
369 refactorUi();
373 void QuicklaunchApplet::addProgram(int index, const QString &url)
375 if (index < 0 || index > m_icons.size()) {
376 index = m_icons.size();
378 if( url.isEmpty() )
379 return;
380 KUrl appUrl = KUrl(url);
381 KIcon icon;
383 if (appUrl.isLocalFile() && KDesktopFile::isDesktopFile(appUrl.toLocalFile())) {
384 KDesktopFile *f = new KDesktopFile(appUrl.toLocalFile());
385 icon = KIcon(f->readIcon());
386 delete f;
387 } else {
388 icon = KIcon(KMimeType::iconNameForUrl(appUrl));
391 if (icon.isNull()) {
392 icon = KIcon("unknown");
395 QuicklaunchIcon *container = new QuicklaunchIcon(appUrl, icon, this);
396 container->installEventFilter(this);
397 m_icons.insert(index, container);
400 void QuicklaunchApplet::loadPrograms(const QStringList &desktopFiles)
402 foreach (const QString &desktopFile, desktopFiles) {
403 addProgram(-1, desktopFile);
407 void QuicklaunchApplet::clearLayout(QGraphicsLayout *layout)
409 while (layout->count() > 0) {
410 layout->removeAt(0);
414 void QuicklaunchApplet::removeCurrentIcon()
416 m_icons.removeAll(m_rightClickedIcon);
417 m_rightClickedIcon->hide();
418 m_rightClickedIcon->deleteLater();
419 refactorUi();
422 bool QuicklaunchApplet::dropHandler(const int pos, const QMimeData *mimedata)
424 if (!KUrl::List::canDecode(mimedata)) {
425 return false;
428 KUrl::List urls = KUrl::List::fromMimeData(mimedata);
430 if (!urls.count()) {
431 return false;
434 //if there are more than one the last is junk
435 if (urls.count() > 1) {
436 urls.removeLast();
439 foreach (const KUrl &url, urls) {
440 if(KDesktopFile::isDesktopFile(url.toLocalFile())) {
441 addProgram(pos, url.toLocalFile());
444 return true;
448 void QuicklaunchApplet::showAddInterface()
450 if (!m_addDialog) {
451 m_addDialog = new KDialog;
452 m_addDialog->setCaption(i18n("Add Shortcut"));
454 QWidget *widget = new QWidget;
455 addUi.setupUi(widget);
456 m_addDialog->setMainWidget(widget);
457 connect(m_addDialog, SIGNAL(okClicked()), this, SLOT(addAccepted()));
459 m_addDialog->show();
462 void QuicklaunchApplet::addAccepted()
464 int insertplace = m_rightClickedIcon ? m_icons.indexOf(m_rightClickedIcon) : m_icons.size();
465 addProgram(insertplace, addUi.urlIcon->url().url());
466 refactorUi();
469 K_EXPORT_PLASMA_APPLET(quicklaunch, QuicklaunchApplet)
471 #include "quicklaunchApplet.moc"