00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "quicklaunchApplet.h"
00021
00022 #include <KConfigDialog>
00023 #include <KDesktopFile>
00024 #include <QGraphicsSceneDragDropEvent>
00025 #include <QGraphicsWidget>
00026 #include <QDrag>
00027 #include <QMouseEvent>
00028 #include <QMimeData>
00029 #include <QToolButton>
00030
00031 #include <KDialog>
00032 #include <KMimeType>
00033 #include <KStandardDirs>
00034 #include <KWindowSystem>
00035
00036 #include <plasma/containment.h>
00037 #include <plasma/dialog.h>
00038 #include <plasma/corona.h>
00039
00040 #include "math.h"
00041
00042 static const int s_defaultIconSize = 16;
00043 static const int s_defaultSpacing = 2;
00044
00045 QuicklaunchApplet::QuicklaunchApplet(QObject *parent, const QVariantList &args)
00046 : Plasma::Applet(parent, args),
00047 m_visibleIcons(6),
00048 m_rowCount(2),
00049 m_dialogRowCount(2),
00050 m_dialog(0),
00051 m_dialogWidget(0),
00052 m_dialogLayout(0),
00053 m_addDialog(0),
00054 m_rightClickedIcon(0),
00055 m_addAction(0),
00056 m_removeAction(0)
00057 {
00058 setHasConfigurationInterface(true);
00059 setAcceptDrops(true);
00060 setAspectRatioMode(Plasma::IgnoreAspectRatio);
00061
00062
00063 resize((m_visibleIcons / m_rowCount) * s_defaultIconSize +
00064 (s_defaultSpacing * (m_visibleIcons + 1)),
00065 m_rowCount * 22 + s_defaultSpacing * 3);
00066 }
00067
00068 QuicklaunchApplet::~QuicklaunchApplet()
00069 {
00070 if (m_dialog) {
00071 m_dialog->close();
00072 delete m_dialog;
00073 }
00074
00075 delete m_dialogWidget;
00076 }
00077
00078 void QuicklaunchApplet::saveState(KConfigGroup &config) const
00079 {
00080 QStringList iconUrls;
00081 foreach (QuicklaunchIcon * container, m_icons) {
00082 iconUrls.append(container->url().prettyUrl());
00083 }
00084
00085 config.writeEntry("iconUrls", iconUrls);
00086 }
00087
00088 void QuicklaunchApplet::init()
00089 {
00090 KConfigGroup cg = config();
00091 m_rowCount = qMax(1, cg.readEntry("rowCount", m_rowCount));
00092 m_visibleIcons = qMax(1, cg.readEntry("visibleIcons", m_visibleIcons));
00093 m_dialogRowCount = qMax(1, cg.readEntry("dialogRowCount", m_dialogRowCount));
00094
00095
00096 m_layout = new QGraphicsLinearLayout(this);
00097 m_layout->setContentsMargins(0, 0, 0, 0);
00098 m_layout->setSpacing(0);
00099 setLayout(m_layout);
00100
00101
00102 m_innerLayout = new QuicklaunchLayout(0, m_rowCount);
00103 m_innerLayout->setContentsMargins(0, 0, 0, 0);
00104 m_innerLayout->setSpacing(0);
00105 m_layout->addItem(m_innerLayout);
00106
00107
00108 m_arrow = new Plasma::IconWidget(this);
00109 m_arrow->setIcon(KIcon("arrow-right"));
00110 connect(m_arrow, SIGNAL(clicked()), SLOT(showDialog()));
00111
00112 QStringList desktopFiles = cg.readEntry("iconUrls", QStringList());
00113
00114 if (desktopFiles.isEmpty()) {
00115 QStringList defaultApps;
00116 defaultApps << "konqbrowser" << "dolphin" << "kopete";
00117
00118 foreach (const QString &defaultApp, defaultApps) {
00119 KService::Ptr service = KService::serviceByStorageId(defaultApp);
00120 if (service && service->isValid()) {
00121 QString path = service->entryPath();
00122 kDebug() << path;
00123 if (!path.isEmpty() && QDir::isAbsolutePath(path)) {
00124 desktopFiles << path;
00125 }
00126 }
00127 }
00128 kDebug() << desktopFiles;
00129 }
00130
00131 loadPrograms(desktopFiles);
00132 refactorUi();
00133 }
00134
00135 QSizeF QuicklaunchApplet::sizeHint(Qt::SizeHint which, const QSizeF & constraint) const
00136 {
00137 if (which == Qt::PreferredSize) {
00138 QSizeF sizeHint = size();
00139 if (m_icons.size() > m_visibleIcons) {
00140 sizeHint.setWidth(sizeHint.height() / m_innerLayout->rowCount() * m_innerLayout->columnCount() + size().height());
00141 } else {
00142 sizeHint.setWidth(sizeHint.height() / m_innerLayout->rowCount() * m_innerLayout->columnCount());
00143 }
00144 return sizeHint;
00145 }
00146 return QGraphicsWidget::sizeHint(which, constraint);
00147 }
00148
00149 void QuicklaunchApplet::constraintsEvent(Plasma::Constraints constraints)
00150 {
00151 if (constraints & Plasma::SizeConstraint) {
00152
00153 refactorUi();
00154 }
00155 }
00156
00157 void QuicklaunchApplet::refactorUi()
00158 {
00159 clearLayout(m_innerLayout);
00160
00161 if (m_dialogLayout) {
00162 clearLayout(m_dialogLayout);
00163 m_dialogLayout->setRowCount(m_dialogRowCount);
00164 }
00165 int rowCount;
00166 int iconWidth;
00167 if (formFactor() == Plasma::Vertical) {
00168 rowCount = qMin(m_rowCount, int(size().width()) / (s_defaultIconSize + s_defaultSpacing));
00169
00170 rowCount = qMax(1, rowCount );
00171 iconWidth = size().width() / rowCount;
00172 } else {
00173 rowCount = qMin(m_rowCount, int(size().height()) / (s_defaultIconSize + s_defaultSpacing));
00174
00175 rowCount = qMax(1, rowCount );
00176 iconWidth = qMax(s_defaultIconSize, int(size().height()) / rowCount);
00177 }
00178
00179 m_innerLayout->setRowCount(rowCount);
00180 const QSizeF minSize = QSizeF(iconWidth, iconWidth);
00181 const QSizeF maxSize = QSizeF(iconWidth, iconWidth);
00182 int count = 0;
00183 kDebug() << m_icons.count() << iconWidth << "pixel icons in" << rowCount
00184 << "rows, with a max of" << m_visibleIcons << "visible";
00185 foreach (QuicklaunchIcon *icon, m_icons) {
00186
00187
00188 icon->resize(minSize);
00189
00190 if (count < m_visibleIcons || m_visibleIcons == -1) {
00191 icon->show();
00192 m_innerLayout->addItem(icon);
00193 } else if (m_dialogLayout) {
00194 icon->setMinimumSize(minSize);
00195 icon->setMaximumSize(maxSize);
00196 icon->show();
00197 m_dialogLayout->addItem(icon);
00198 } else {
00199 icon->hide();
00200 }
00201
00202 ++count;
00203 }
00204
00205 m_layout->removeItem(m_arrow);
00206 if (count > m_visibleIcons && m_visibleIcons != -1) {
00207
00208
00209 m_arrow->resize(minSize);
00210 m_layout->addItem(m_arrow);
00211 m_arrow->show();
00212 } else {
00213 m_arrow->hide();
00214 }
00215
00216 if (m_dialog) {
00217 m_dialog->close();
00218 m_dialogLayout->updateGeometry();
00219 m_dialog->adjustSize();
00220 }
00221 m_innerLayout->updateGeometry();
00222 m_layout->updateGeometry();
00223 }
00224
00225 void QuicklaunchApplet::showDialog()
00226 {
00227 if (!m_dialog) {
00228 m_dialogWidget = new QGraphicsWidget(this);
00229 m_dialogWidget->setAcceptDrops(true);
00230 m_dialogWidget->installEventFilter(this);
00231 qobject_cast<Plasma::Corona*>(m_dialogWidget->scene())->addOffscreenWidget(m_dialogWidget);
00232
00233
00234 m_dialog = new Plasma::Dialog(0, Qt::X11BypassWindowManagerHint);
00235 m_dialog->setAcceptDrops(true);
00236
00237 m_dialog->setContextMenuPolicy(Qt::ActionsContextMenu);
00238 m_dialogLayout = new QuicklaunchLayout(m_dialogWidget, m_dialogRowCount);
00239 m_dialogWidget->setLayout(m_dialogLayout);
00240 refactorUi();
00241 m_dialog->setGraphicsWidget(m_dialogWidget);
00242 }
00243
00244 if (m_dialog->isVisible()) {
00245 m_dialog->hide();
00246 } else {
00247 m_dialog->resize(m_dialogLayout->preferredSize().toSize());
00248
00249 if(containment() && containment()->corona()) {
00250 kDebug() << "position:" << containment()->corona()->popupPosition(m_arrow, m_dialog->size()) << "dialog size:" << m_dialog->size() << "layout preferred-size:" << m_dialogLayout->preferredSize().toSize();
00251 m_dialog->move(containment()->corona()->popupPosition(m_arrow, m_dialog->size()));
00252 }
00253 KWindowSystem::setState(m_dialog->winId(), NET::SkipTaskbar);
00254
00255
00256 m_dialog->show();
00257 }
00258 }
00259
00260 void QuicklaunchApplet::createConfigurationInterface(KConfigDialog *parent)
00261 {
00262 QWidget *widget = new QWidget(parent);
00263 uiConfig.setupUi(widget);
00264 connect(parent, SIGNAL(accepted()), SLOT(configAccepted()));
00265 uiConfig.rowCount->setValue(m_rowCount);
00266 uiConfig.dialogRowCount->setValue(m_dialogRowCount);
00267 uiConfig.dialogRowCount->hide();
00268 uiConfig.dialogrowLabel->hide();
00269 uiConfig.icons->setValue(m_visibleIcons);
00270 parent->addPage(widget, i18n("General"), icon());
00271 }
00272
00273 void QuicklaunchApplet::configAccepted()
00274 {
00275 bool changed = false;
00276 int temp = uiConfig.rowCount->value();
00277
00278 KConfigGroup cg = config();
00279 if (temp != m_rowCount) {
00280 m_rowCount = temp;
00281 cg.writeEntry("rowCount", m_rowCount);
00282 changed = true;
00283 }
00284
00285 temp = uiConfig.icons->value();
00286 if (temp != m_visibleIcons) {
00287 m_visibleIcons = temp;
00288 cg.writeEntry("visibleIcons", m_visibleIcons);
00289 changed = true;
00290 }
00291
00292 temp = uiConfig.dialogRowCount->value();
00293 if (temp != m_dialogRowCount) {
00294 m_dialogRowCount = temp;
00295 cg.writeEntry("dialogRowCount", m_dialogRowCount);
00296 changed = true;
00297 }
00298
00299 if (changed) {
00300 emit configNeedsSaving();
00301 refactorUi();
00302 }
00303 }
00304
00305 QList<QAction*> QuicklaunchApplet::contextActions(QuicklaunchIcon *icon)
00306 {
00307 QList<QAction*> tempActions;
00308 if (!m_addAction) {
00309 m_addAction = new QAction(KIcon("list-add"), i18n("Add Icon..."), this);
00310 connect(m_addAction, SIGNAL(triggered(bool)), this, SLOT(showAddInterface()));
00311 }
00312
00313 tempActions << m_addAction;
00314
00315 if (icon) {
00316 m_rightClickedIcon = icon;
00317 if (!m_removeAction) {
00318 m_removeAction = new QAction(KIcon("list-remove"), i18n("Remove Icon"), this);
00319 connect(m_removeAction, SIGNAL(triggered(bool)), this, SLOT(removeCurrentIcon()));
00320 }
00321 tempActions << m_removeAction;
00322 }
00323
00324 return tempActions;
00325 }
00326
00327 bool QuicklaunchApplet::eventFilter(QObject * object, QEvent * event)
00328 {
00329 Q_UNUSED(object)
00330 if (event->type() == QEvent::GraphicsSceneDrop) {
00331 dropEvent(static_cast<QGraphicsSceneDragDropEvent*>(event));
00332 return true;
00333 }
00334 return QObject::eventFilter(object, event);
00335 }
00336
00337 void QuicklaunchApplet::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
00338 {
00339 event->setDropAction(Qt::MoveAction);
00340 event->setAccepted(event->mimeData()->hasUrls());
00341 }
00342
00343 void QuicklaunchApplet::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
00344 {
00345 event->setDropAction(Qt::MoveAction);
00346 event->setAccepted(event->mimeData()->hasUrls());
00347 }
00348
00349 void QuicklaunchApplet::dropEvent(QGraphicsSceneDragDropEvent *event)
00350 {
00351
00352 QPointF point = mapFromScene(event->scenePos());
00353 int rowCount = m_innerLayout->rowCount();
00354 int cols = static_cast<int>(ceil(1.0 * qMin(m_icons.size(), m_visibleIcons) / rowCount));
00355 int col = static_cast<int>((round(point.x()) * cols / m_innerLayout->geometry().width()));
00356 col = (col >= cols) ? col - 1 : col;
00357 int row = static_cast<int>(floor(point.y() * rowCount / m_innerLayout->geometry().height()));
00358 row = (row >= m_rowCount) ? row - 1 : row;
00359 int pos = row * cols + col;
00360
00361 if (pos >= m_icons.size()) {
00362 pos = m_icons.size() - 1;
00363 }
00364
00365 if (dropHandler(pos, event->mimeData())) {
00366 event->setDropAction(Qt::MoveAction);
00367 event->accept();
00368 saveConfig();
00369 refactorUi();
00370 }
00371 }
00372
00373 void QuicklaunchApplet::addProgram(int index, const QString &url)
00374 {
00375 if (index < 0 || index > m_icons.size()) {
00376 index = m_icons.size();
00377 }
00378 if( url.isEmpty() )
00379 return;
00380 KUrl appUrl = KUrl(url);
00381 KIcon icon;
00382
00383 if (appUrl.isLocalFile() && KDesktopFile::isDesktopFile(appUrl.toLocalFile())) {
00384 KDesktopFile *f = new KDesktopFile(appUrl.toLocalFile());
00385 icon = KIcon(f->readIcon());
00386 delete f;
00387 } else {
00388 icon = KIcon(KMimeType::iconNameForUrl(appUrl));
00389 }
00390
00391 if (icon.isNull()) {
00392 icon = KIcon("unknown");
00393 }
00394
00395 QuicklaunchIcon *container = new QuicklaunchIcon(appUrl, icon, this);
00396 container->installEventFilter(this);
00397 m_icons.insert(index, container);
00398 }
00399
00400 void QuicklaunchApplet::loadPrograms(const QStringList &desktopFiles)
00401 {
00402 foreach (const QString &desktopFile, desktopFiles) {
00403 addProgram(-1, desktopFile);
00404 }
00405 }
00406
00407 void QuicklaunchApplet::clearLayout(QGraphicsLayout *layout)
00408 {
00409 while (layout->count() > 0) {
00410 layout->removeAt(0);
00411 }
00412 }
00413
00414 void QuicklaunchApplet::removeCurrentIcon()
00415 {
00416 m_icons.removeAll(m_rightClickedIcon);
00417 m_rightClickedIcon->hide();
00418 m_rightClickedIcon->deleteLater();
00419 refactorUi();
00420 }
00421
00422 bool QuicklaunchApplet::dropHandler(const int pos, const QMimeData *mimedata)
00423 {
00424 if (!KUrl::List::canDecode(mimedata)) {
00425 return false;
00426 }
00427
00428 KUrl::List urls = KUrl::List::fromMimeData(mimedata);
00429
00430 if (!urls.count()) {
00431 return false;
00432 }
00433
00434
00435 if (urls.count() > 1) {
00436 urls.removeLast();
00437 }
00438
00439 foreach (const KUrl &url, urls) {
00440 if(KDesktopFile::isDesktopFile(url.toLocalFile())) {
00441 addProgram(pos, url.toLocalFile());
00442 }
00443 }
00444 return true;
00445 }
00446
00447
00448 void QuicklaunchApplet::showAddInterface()
00449 {
00450 if (!m_addDialog) {
00451 m_addDialog = new KDialog;
00452 m_addDialog->setCaption(i18n("Add Shortcut"));
00453
00454 QWidget *widget = new QWidget;
00455 addUi.setupUi(widget);
00456 m_addDialog->setMainWidget(widget);
00457 connect(m_addDialog, SIGNAL(okClicked()), this, SLOT(addAccepted()));
00458 }
00459 m_addDialog->show();
00460 }
00461
00462 void QuicklaunchApplet::addAccepted()
00463 {
00464 int insertplace = m_rightClickedIcon ? m_icons.indexOf(m_rightClickedIcon) : m_icons.size();
00465 addProgram(insertplace, addUi.urlIcon->url().url());
00466 refactorUi();
00467 }
00468
00469 K_EXPORT_PLASMA_APPLET(quicklaunch, QuicklaunchApplet)
00470
00471 #include "quicklaunchApplet.moc"