1 /***************************************************************************
2 * Copyright (C) 2008 by Lukas Appelhans *
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. *
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. *
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>
27 #include <QMouseEvent>
29 #include <QToolButton>
33 #include <KStandardDirs>
34 #include <KWindowSystem>
36 #include <plasma/containment.h>
37 #include <plasma/dialog.h>
38 #include <plasma/corona.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
),
54 m_rightClickedIcon(0),
58 setHasConfigurationInterface(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()
75 delete m_dialogWidget
;
78 void QuicklaunchApplet::saveState(KConfigGroup
&config
) const
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);
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();
123 if (!path
.isEmpty() && QDir::isAbsolutePath(path
)) {
124 desktopFiles
<< path
;
128 kDebug() << desktopFiles
;
131 loadPrograms(desktopFiles
);
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());
142 sizeHint
.setWidth(sizeHint
.height() / m_innerLayout
->rowCount() * m_innerLayout
->columnCount());
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
157 void QuicklaunchApplet::refactorUi()
159 clearLayout(m_innerLayout
);
161 if (m_dialogLayout
) {
162 clearLayout(m_dialogLayout
);
163 m_dialogLayout
->setRowCount(m_dialogRowCount
);
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
;
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
);
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) {
192 m_innerLayout
->addItem(icon
);
193 } else if (m_dialogLayout
) {
194 icon
->setMinimumSize(minSize
);
195 icon
->setMaximumSize(maxSize
);
197 m_dialogLayout
->addItem(icon
);
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
);
218 m_dialogLayout
->updateGeometry();
219 m_dialog
->adjustSize();
221 m_innerLayout
->updateGeometry();
222 m_layout
->updateGeometry();
225 void QuicklaunchApplet::showDialog()
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
);
241 m_dialog
->setGraphicsWidget(m_dialogWidget
);
244 if (m_dialog
->isVisible()) {
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()));
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
) {
281 cg
.writeEntry("rowCount", m_rowCount
);
285 temp
= uiConfig
.icons
->value();
286 if (temp
!= m_visibleIcons
) {
287 m_visibleIcons
= temp
;
288 cg
.writeEntry("visibleIcons", m_visibleIcons
);
292 temp
= uiConfig
.dialogRowCount
->value();
293 if (temp
!= m_dialogRowCount
) {
294 m_dialogRowCount
= temp
;
295 cg
.writeEntry("dialogRowCount", m_dialogRowCount
);
300 emit
configNeedsSaving();
305 QList
<QAction
*> QuicklaunchApplet::contextActions(QuicklaunchIcon
*icon
)
307 QList
<QAction
*> tempActions
;
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
;
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
;
327 bool QuicklaunchApplet::eventFilter(QObject
* object
, QEvent
* event
)
330 if (event
->type() == QEvent::GraphicsSceneDrop
) {
331 dropEvent(static_cast<QGraphicsSceneDragDropEvent
*>(event
));
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
);
373 void QuicklaunchApplet::addProgram(int index
, const QString
&url
)
375 if (index
< 0 || index
> m_icons
.size()) {
376 index
= m_icons
.size();
380 KUrl appUrl
= KUrl(url
);
383 if (appUrl
.isLocalFile() && KDesktopFile::isDesktopFile(appUrl
.toLocalFile())) {
384 KDesktopFile
*f
= new KDesktopFile(appUrl
.toLocalFile());
385 icon
= KIcon(f
->readIcon());
388 icon
= KIcon(KMimeType::iconNameForUrl(appUrl
));
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) {
414 void QuicklaunchApplet::removeCurrentIcon()
416 m_icons
.removeAll(m_rightClickedIcon
);
417 m_rightClickedIcon
->hide();
418 m_rightClickedIcon
->deleteLater();
422 bool QuicklaunchApplet::dropHandler(const int pos
, const QMimeData
*mimedata
)
424 if (!KUrl::List::canDecode(mimedata
)) {
428 KUrl::List urls
= KUrl::List::fromMimeData(mimedata
);
434 //if there are more than one the last is junk
435 if (urls
.count() > 1) {
439 foreach (const KUrl
&url
, urls
) {
440 if(KDesktopFile::isDesktopFile(url
.toLocalFile())) {
441 addProgram(pos
, url
.toLocalFile());
448 void QuicklaunchApplet::showAddInterface()
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()));
462 void QuicklaunchApplet::addAccepted()
464 int insertplace
= m_rightClickedIcon
? m_icons
.indexOf(m_rightClickedIcon
) : m_icons
.size();
465 addProgram(insertplace
, addUi
.urlIcon
->url().url());
469 K_EXPORT_PLASMA_APPLET(quicklaunch
, QuicklaunchApplet
)
471 #include "quicklaunchApplet.moc"