Make the working-set tooltip prettier: Use only the working-set icon at top-left...
[kdevelop:kdevplatform.git] / shell / workingsetcontroller.cpp
1 /*
2     Copyright David Nolden  <david.nolden.kdevelop@art-master.de>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License along
15     with this program; if not, write to the Free Software Foundation, Inc.,
16     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #include <KDE/KTextEditor/Document>
20 #include "workingsetcontroller.h"
21 #include <kconfiggroup.h>
22 #include <kconfig.h>
23 #include <kglobal.h>
24 #include <ksharedconfig.h>
25 #include <kcolorutils.h>
26 #include <sublime/view.h>
27 #include <sublime/areaindex.h>
28 #include <sublime/document.h>
29 #include <interfaces/idocument.h>
30 #include "core.h"
31 #include "documentcontroller.h"
32 #include <sublime/area.h>
33 #include "mainwindow.h"
34 #include <qboxlayout.h>
35 #include <klocalizedstring.h>
36 #include <util/pushvalue.h>
37 #include <kiconeffect.h>
38 #include <qapplication.h>
39 #include <util/activetooltip.h>
40 #include <qevent.h>
41 #include <qmenu.h>
42 #include <sublime/urldocument.h>
43 #include "partdocument.h"
44 #include "textdocument.h"
45 #include <qpushbutton.h>
46 #include <interfaces/iprojectcontroller.h>
47 #include <interfaces/iproject.h>
48 #include <interfaces/isession.h>
49 #include <kactioncollection.h>
50 #include <qtimer.h>
51
52 #define SYNC_OFTEN
53
54 using namespace KDevelop;
55
56 bool WorkingSet::m_loading = false;
57 const int toolTipTimeout = 2000;
58
59 static MainWindow* mainWindow() {
60     MainWindow* ret = dynamic_cast<MainWindow*>(Core::self()->uiController()->activeMainWindow());
61     Q_ASSERT(ret);
62     return ret;
63 }
64
65 //Filters the views in the main-window so they only contain the given files to keep
66 void filterViews(QSet< QString > keepFiles)
67 {
68     foreach(Sublime::View* view, mainWindow()->area()->views()) {
69
70         PartDocument* partDoc = dynamic_cast<PartDocument*>(view->document());
71         if(partDoc && !keepFiles.contains(partDoc->documentSpecifier())) {
72             if(view->document()->views().count() == 1) {
73                 partDoc->close();
74                 continue;
75             }
76
77             mainWindow()->area()->closeView(view);
78         }
79     }
80 }
81
82 //Random set of icons that are well distinguishable from each other. If the user doesn't have them, they won't be used.
83 QStringList setIcons = QStringList() << "chronometer" << "games-config-tiles" << "im-user" << "irc-voice" << "irc-operator" << "office-chart-pie" << "office-chart-ring" << "speaker" << "view-pim-notes" << "esd" << "akonadi" << "kleopatra" << "nepomuk" << "package_edutainment_art" << "package_games_amusement" << "package_games_sports" << "package_network" << "package_office_database" << "package_system_applet" << "package_system_emulator" << "preferences-desktop-notification-bell" << "wine" << "utilities-desktop-extra" << "step" << "preferences-web-browser-cookies" << "preferences-plugin" << "preferences-kcalc-constants" << "preferences-desktop-icons" << "tagua" << "inkscape" << "java" << "kblogger" << "preferences-desktop-personal" << "emblem-favorite" << "face-smile-big" << "face-embarrassed" << "user-identity" << "mail-tagged" << "media-playlist-suffle" << "weather-clouds";
84
85 WorkingSetController::WorkingSetController(Core* core) : m_core(core)
86 {
87     m_hideToolTipTimer = new QTimer(this);
88     m_hideToolTipTimer->setInterval(toolTipTimeout);
89     m_hideToolTipTimer->setSingleShot(true);
90 }
91
92 void WorkingSetController::initialize()
93 {
94     //Load all working-sets
95     KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets");
96     foreach(const QString& set, setConfig.groupList())
97     {
98         if(setConfig.group(set).hasKey("iconName"))
99             getWorkingSet(set, setConfig.group(set).readEntry<QString>("iconName", QString()));
100         else
101             kDebug() << "have garbage working set with id " << set;
102     }
103     
104     if(!(Core::self()->setupFlags() & Core::NoUi)) setupActions();
105 }
106
107 void WorkingSetController::cleanup()
108 {
109     foreach(Sublime::MainWindow* window, Core::self()->uiControllerInternal()->mainWindows()) {
110         foreach (Sublime::Area *area, window->areas()) {
111             if (!area->workingSet().isEmpty()) {
112                 Q_ASSERT(m_workingSets.contains(area->workingSet()));
113                 m_workingSets[area->workingSet()]->saveFromArea(area, area->rootIndex());
114             }
115         }
116     }
117
118     foreach(WorkingSet* set, m_workingSets) {
119         kDebug() << "set" << set->id() << "persistent" << set->isPersistent() << "has areas:" << set->hasConnectedAreas() << "files" << set->fileList();
120         if(!set->isPersistent() && !set->hasConnectedAreas()) {
121             kDebug() << "deleting";
122             set->deleteSet(true, true);
123         }
124         delete set;
125     }
126
127     m_workingSets.clear();
128 }
129
130
131 bool WorkingSetController::usingIcon(QString icon)
132 {
133     foreach(WorkingSet* set, m_workingSets)
134         if(set->iconName() == icon)
135             return true;
136     return false;
137 }
138
139 bool WorkingSetController::iconValid(QString icon)
140 {
141     return !KIconLoader::global()->iconPath(icon, KIconLoader::Small, true).isNull();
142 }
143
144
145 WorkingSet* WorkingSetController::newWorkingSet(QString prefix)
146 {
147     QString newId = QString("%1_%2").arg(prefix).arg(qrand() % 10000000);
148     return getWorkingSet(newId);
149 }
150
151 WorkingSet* WorkingSetController::getWorkingSet(QString id, QString icon)
152 {
153     if(!m_workingSets.contains(id)) {
154
155         if(icon.isEmpty())
156         {
157             for(int a = 0; a < 100; ++a) {
158                 int pick = (qHash(id) + a) % setIcons.size(); ///@todo Pick icons semantically, by content, and store them in the config
159                 if(!usingIcon(setIcons[pick])) {
160                     if(iconValid(setIcons[pick])) {
161                         icon = setIcons[pick];
162                     break;
163                     }
164                 }
165             }
166         }
167         if(icon.isEmpty()) {
168             kDebug() << "found no icon for working-set" << id;
169             icon = "invalid";
170         }
171         m_workingSets[id] = new WorkingSet(id, icon);
172         emit workingSetAdded(id);
173     }
174
175     return m_workingSets[id];
176 }
177
178 void deleteGroupRecursive(KConfigGroup group) {
179 //     kDebug() << "deleting" << group.name();
180     foreach(const QString& entry, group.entryMap().keys()) {
181         group.deleteEntry(entry);
182     }
183     Q_ASSERT(group.entryMap().isEmpty());
184
185     foreach(const QString& subGroup, group.groupList()) {
186         deleteGroupRecursive(group.group(subGroup));
187         group.deleteGroup(subGroup);
188     }
189     //Why doesn't this work?
190 //     Q_ASSERT(group.groupList().isEmpty());
191     group.deleteGroup();
192
193 #ifdef SYNC_OFTEN
194     group.sync();
195 #endif
196 }
197
198 void WorkingSet::saveFromArea(Sublime::Area* area, Sublime::AreaIndex* areaIndex)
199 {
200     if(m_id.isEmpty()) {
201         Q_ASSERT(areaIndex->viewCount() == 0 && !areaIndex->isSplitted());
202         return;
203     }
204     
205     kDebug() << "saving" << m_id << "from area";
206
207     bool wasPersistent = isPersistent();
208     
209     KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets");
210     KConfigGroup group = setConfig.group(m_id);
211     deleteGroupRecursive(group);
212     group.writeEntry("iconName", m_iconName);
213     if (area->activeView()) {
214         group.writeEntry("Active View", area->activeView()->document()->documentSpecifier());
215     } else {
216         group.writeEntry("Active View", QString());
217     }
218     saveFromArea(area, areaIndex, group);
219
220     if(isEmpty())
221         deleteGroupRecursive(group);
222
223     setPersistent(wasPersistent);
224     
225 #ifdef SYNC_OFTEN
226     setConfig.sync();
227 #endif
228     
229     emit setChangedSignificantly();
230 }
231
232 void WorkingSet::saveFromArea(Sublime::Area* a, Sublime::AreaIndex * area, KConfigGroup & group)
233 {
234     if (area->isSplitted()) {
235         group.writeEntry("Orientation", area->orientation() == Qt::Horizontal ? "Horizontal" : "Vertical");
236
237         if (area->first()) {
238             KConfigGroup subgroup(&group, "0");
239             subgroup.deleteGroup();
240             saveFromArea(a, area->first(), subgroup);
241         }
242
243         if (area->second()) {
244             KConfigGroup subgroup(&group, "1");
245             subgroup.deleteGroup();
246             saveFromArea(a, area->second(), subgroup);
247         }
248     } else {
249         group.writeEntry("View Count", area->viewCount());
250
251         int index = 0;
252         foreach (Sublime::View* view, area->views()) {
253             kDebug() << view->document()->title();
254             group.writeEntry(QString("View %1 Type").arg(index), view->document()->documentType());
255             group.writeEntry(QString("View %1").arg(index), view->document()->documentSpecifier());
256
257             TextDocument *textDoc = qobject_cast<KDevelop::TextDocument*>(view->document());
258             if (textDoc && textDoc->textDocument()) {
259                 QString encoding = textDoc->textDocument()->encoding();
260                 if (!encoding.isEmpty())
261                     group.writeEntry(QString("View %1 Encoding").arg(index), encoding);
262             }
263             QString state = view->viewState();
264             if (!state.isEmpty())
265                 group.writeEntry(QString("View %1 State").arg(index), state);
266
267             ++index;
268         }
269     }
270 }
271
272
273 bool WorkingSet::isEmpty() const
274 {
275     if(m_id.isEmpty())
276         return true;
277     KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets");
278     KConfigGroup group = setConfig.group(m_id);
279     return !group.hasKey("Orientation") && group.readEntry("View Count", 0) == 0;
280 }
281
282 struct DisableMainWindowUpdatesFromArea
283 {
284     DisableMainWindowUpdatesFromArea(Sublime::Area* area) : m_area(area) {
285         if(area) {
286
287             foreach(Sublime::MainWindow* window, Core::self()->uiControllerInternal()->mainWindows()) {
288                 if(window->area() == area) {
289                     if(window->updatesEnabled()) {
290                         wasUpdatesEnabled.insert(window);
291                         window->setUpdatesEnabled(false);
292                     }
293                 }
294             }
295         }
296     }
297
298     ~DisableMainWindowUpdatesFromArea() {
299         if(m_area) {
300             foreach(Sublime::MainWindow* window, wasUpdatesEnabled) {
301                 window->setUpdatesEnabled(true);
302             }
303         }
304     }
305
306     Sublime::Area* m_area;
307     QSet<Sublime::MainWindow*> wasUpdatesEnabled;
308 };
309
310 void loadFileList(QStringList& ret, KConfigGroup group)
311 {
312     if (group.hasKey("Orientation")) {
313         QStringList subgroups = group.groupList();
314
315         if (subgroups.contains("0")) {
316
317             {
318                 KConfigGroup subgroup(&group, "0");
319                 loadFileList(ret, subgroup);
320             }
321
322             if (subgroups.contains("1")) {
323                 KConfigGroup subgroup(&group, "1");
324                 loadFileList(ret, subgroup);
325             }
326         }
327
328     } else {
329
330         int viewCount = group.readEntry("View Count", 0);
331         for (int i = 0; i < viewCount; ++i) {
332             QString type = group.readEntry(QString("View %1 Type").arg(i), "");
333             QString specifier = group.readEntry(QString("View %1").arg(i), "");
334
335             ret << specifier;
336         }
337     }
338 }
339 QStringList WorkingSet::fileList() const
340 {
341     if(m_id.isEmpty())
342         return QStringList();
343
344     QStringList ret;
345     KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets");
346     KConfigGroup group = setConfig.group(m_id);
347
348     loadFileList(ret, group);
349     return ret;
350 }
351
352 void WorkingSet::loadToArea(Sublime::Area* area, Sublime::AreaIndex* areaIndex, bool clear) {
353     PushValue<bool> enableLoading(m_loading, true);
354
355     DisableMainWindowUpdatesFromArea updatesDisabler(area);
356
357     kDebug() << "loading working-set" << m_id << "into area" << area;
358
359     if(clear) {
360         kDebug() << "clearing area with working-set" << area->workingSet();
361         QSet< QString > files = fileList().toSet();
362         foreach(Sublime::View* view, area->views()) {
363             Sublime::UrlDocument* doc = dynamic_cast<Sublime::UrlDocument*>(view->document());
364             if(!doc || !files.contains(doc->documentSpecifier()))
365                 area->closeView(view);
366         }
367     }
368
369     if(m_id.isEmpty())
370         return;
371
372     KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets");
373     KConfigGroup group = setConfig.group(m_id);
374
375     loadToArea(area, areaIndex, group);
376
377     //activate view in the working set
378     if (!area->views().isEmpty()) {
379         foreach(Sublime::MainWindow* window, Core::self()->uiControllerInternal()->mainWindows()) {
380             if(window->area() == area) {
381                 window->setArea(area);
382                 QString activeView = group.readEntry("Active View", QString());
383                 kDebug() << activeView;
384                 bool found = false;
385                 foreach (Sublime::View *v, area->views()) {
386                     if (v->document()->documentSpecifier() == activeView) {
387                         window->activateView(v);
388                         found = true;
389                         break;
390                     }
391                 }
392                 if (!found) window->activateView(area->views().first()); //fallback
393                 break;
394             }
395         }
396     }
397 }
398
399 void WorkingSet::loadToArea(Sublime::Area* area, Sublime::AreaIndex* areaIndex, KConfigGroup group)
400 {
401     if (group.hasKey("Orientation")) {
402         QStringList subgroups = group.groupList();
403
404         if (subgroups.contains("0") && subgroups.contains("1")) {
405 //             kDebug() << "has zero, split:" << split;
406
407             Qt::Orientation orientation = group.readEntry("Orientation", "Horizontal") == "Vertical" ? Qt::Vertical : Qt::Horizontal;
408             if(!areaIndex->isSplitted()){
409                 areaIndex->split(orientation);
410             }else{
411                 areaIndex->setOrientation(orientation);
412             }
413
414             loadToArea(area, areaIndex->first(), KConfigGroup(&group, "0"));
415
416             loadToArea(area, areaIndex->second(), KConfigGroup(&group, "1"));
417         }
418     } else {
419         while (areaIndex->isSplitted()) {
420             areaIndex = areaIndex->first();
421             Q_ASSERT(areaIndex);// Split area index did not contain a first child area index if this fails
422             kDebug() << "is already splitted, using first index" << areaIndex;
423         }
424
425         int viewCount = group.readEntry("View Count", 0);
426         for (int i = 0; i < viewCount; ++i) {
427             QString type = group.readEntry(QString("View %1 Type").arg(i), "");
428             QString specifier = group.readEntry(QString("View %1").arg(i), "");
429
430             bool viewExists = false;
431             foreach (Sublime::View* view, areaIndex->views()) {
432                 if (view->document()->documentSpecifier() == specifier) {
433                     viewExists = true;
434                     break;
435                 }
436             }
437
438             if (viewExists) {
439                 kDebug() << "View already exists!";
440                 continue;
441             }
442
443             IDocument* doc = Core::self()->documentControllerInternal()->openDocument(specifier,
444                              KTextEditor::Cursor::invalid(), IDocumentController::DoNotActivate | IDocumentController::DoNotCreateView);
445             Sublime::Document *document = dynamic_cast<Sublime::Document*>(doc);
446             if (document) {
447                 kDebug() << document->title();
448                 Sublime::View* view = document->createView();
449
450                 QString state = group.readEntry(QString("View %1 State").arg(i), "");
451                 if (!state.isEmpty())
452                     view->setState(state);
453
454                 area->addViewSilently(view, areaIndex);
455             } else {
456                 kWarning() << "Unable to create view of type " << type;
457             }
458         }
459     }
460 }
461
462 void WorkingSet::deleteSet(bool force, bool silent)
463 {
464     if((m_areas.isEmpty() || force) && !m_id.isEmpty()) {
465         KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets");
466         KConfigGroup group = setConfig.group(m_id);
467         deleteGroupRecursive(group);
468 #ifdef SYNC_OFTEN
469         setConfig.sync();
470 #endif
471
472         if(!silent)
473             emit setChangedSignificantly();
474     }
475 }
476
477 QWidget* WorkingSetController::createSetManagerWidget(MainWindow* parent, bool local, Sublime::Area* fixedArea) {
478     return new WorkingSetWidget(parent, this, local, fixedArea);
479 }
480
481 WorkingSetWidget::WorkingSetWidget(MainWindow* parent, WorkingSetController* controller, bool mini, Sublime::Area* fixedArea)
482         : QWidget(0), m_fixedArea(fixedArea), m_mini(mini), m_mainWindow(parent)
483 {
484     m_layout = new QHBoxLayout(this);
485     m_layout->setMargin(0);
486     if(!m_fixedArea)
487         connect(parent, SIGNAL(areaChanged(Sublime::Area*)), this, SLOT(areaChanged(Sublime::Area*)));
488
489     connect(controller, SIGNAL(workingSetAdded(QString)), this, SLOT(workingSetsChanged()));
490     connect(controller, SIGNAL(workingSetRemoved(QString)), this, SLOT(workingSetsChanged()));
491
492     Sublime::Area* area = parent->area();
493     if(m_fixedArea)
494         area = m_fixedArea;
495     if(area)
496         areaChanged(area);
497
498     workingSetsChanged();
499 }
500
501 void WorkingSetWidget::areaChanged(Sublime::Area* area)
502 {
503     if(m_connectedArea) {
504         disconnect(m_connectedArea, SIGNAL(changingWorkingSet(Sublime::Area*,QString,QString)), this, SLOT(changingWorkingSet(Sublime::Area*,QString,QString)));
505     }
506
507     //Queued connect so the change is already applied to the area when we start processing
508     connect(area, SIGNAL(changingWorkingSet(Sublime::Area*,QString,QString)), this, SLOT(changingWorkingSet(Sublime::Area*,QString,QString)), Qt::QueuedConnection);
509
510     m_connectedArea = area;
511
512     changingWorkingSet(area, QString(), area->workingSet());
513 }
514
515 void WorkingSetWidget::changingWorkingSet(Sublime::Area*, QString, QString)
516 {
517     workingSetsChanged();
518 }
519
520 QString htmlColorElement(int element) {
521     QString ret = QString("%1").arg(element, 2, 16, QChar('0'));
522     return ret;
523 }
524
525 QString htmlColor(QColor color) {
526     return "#" + htmlColorElement(color.red()) + htmlColorElement(color.green()) + htmlColorElement(color.blue());
527 }
528
529 void WorkingSetToolButton::contextMenuEvent(QContextMenuEvent* ev)
530 {
531     QToolButton::contextMenuEvent(ev);
532
533     QMenu menu;
534     Sublime::MainWindow* mainWindow = dynamic_cast<Sublime::MainWindow*>(Core::self()->uiController()->activeMainWindow());
535     Q_ASSERT(mainWindow);
536     if(m_set->id() == mainWindow->area()->workingSet()) {
537         menu.addAction(i18n("Close Working Set (Left Click)"), this, SLOT(closeSet()));
538         menu.addAction(i18n("Duplicate Working Set"), this, SLOT(duplicateSet()));
539     }else{
540         menu.addAction(i18n("Load Working Set (Left Click)"), this, SLOT(loadSet()));
541 //         menu.addAction(i18n("Merge Working Set"), this, SLOT(mergeSet()));
542 //         menu.addSeparator();
543 //         menu.addAction(i18n("Intersect Working Set"), this, SLOT(intersectSet()));
544 //         menu.addAction(i18n("Subtract Working Set"), this, SLOT(subtractSet()));
545     }
546     menu.actions()[0]->setIcon(KIcon(m_set->iconName()));
547
548     if(!m_set->hasConnectedAreas()) {
549         menu.addSeparator();
550         menu.addAction(i18n("Delete Working Set"), m_set, SLOT(deleteSet()));
551     }
552     menu.exec(ev->globalPos());
553
554     ev->accept();
555 }
556
557 void WorkingSetToolButton::intersectSet()
558 {
559     m_set->setPersistent(true);
560
561     filterViews(Core::self()->workingSetControllerInternal()->getWorkingSet(mainWindow()->area()->workingSet())->fileList().toSet() & m_set->fileList().toSet());
562 }
563
564 void WorkingSetToolButton::subtractSet()
565 {
566     m_set->setPersistent(true);
567
568     filterViews(Core::self()->workingSetControllerInternal()->getWorkingSet(mainWindow()->area()->workingSet())->fileList().toSet() - m_set->fileList().toSet());
569 }
570
571 void WorkingSetToolButton::mergeSet()
572 {
573     QSet< QString > loadFiles = m_set->fileList().toSet() - Core::self()->workingSetControllerInternal()->getWorkingSet(mainWindow()->area()->workingSet())->fileList().toSet();
574     foreach(const QString& file, loadFiles)
575         Core::self()->documentController()->openDocument(file);
576 }
577
578 void WorkingSetToolButton::duplicateSet()
579 {
580     if(!Core::self()->documentControllerInternal()->saveAllDocumentsForWindow(mainWindow(), KDevelop::IDocument::Default))
581         return;
582     WorkingSet* set = Core::self()->workingSetControllerInternal()->newWorkingSet("clone");
583     set->setPersistent(true);
584     set->saveFromArea(mainWindow()->area(), mainWindow()->area()->rootIndex());
585     mainWindow()->area()->setWorkingSet(set->id());
586 }
587
588 void WorkingSetToolButton::loadSet()
589 {
590     m_set->setPersistent(true);
591
592     if(!Core::self()->documentControllerInternal()->saveAllDocumentsForWindow(mainWindow(), KDevelop::IDocument::Default))
593         return;
594     mainWindow()->area()->setWorkingSet(QString(m_set->id()));
595 }
596
597 void WorkingSetToolButton::closeSet()
598 {
599     m_set->setPersistent(true);
600     m_set->saveFromArea(mainWindow()->area(), mainWindow()->area()->rootIndex());
601
602     if(!Core::self()->documentControllerInternal()->saveAllDocumentsForWindow(mainWindow(), KDevelop::IDocument::Default))
603         return;
604     mainWindow()->area()->setWorkingSet(QString());
605 }
606
607 static QPointer<KDevelop::ActiveToolTip> tooltip;
608
609 bool WorkingSetToolButton::event(QEvent* e)
610 {
611     if(m_toolTipEnabled && e->type() == QEvent::ToolTip) {
612         e->accept();
613         static WorkingSetToolButton* oldTooltipButton;
614         if(tooltip && oldTooltipButton == this)
615             return true;
616
617         delete tooltip;
618         oldTooltipButton = this;
619
620         tooltip = new KDevelop::ActiveToolTip(Core::self()->uiControllerInternal()->activeMainWindow(), QCursor::pos() + QPoint(10, 20));
621         tooltip->addExtendRect(QRect(parentWidget()->mapToGlobal(geometry().topLeft()), parentWidget()->mapToGlobal(geometry().bottomRight())));
622         QVBoxLayout* layout = new QVBoxLayout(tooltip);
623         layout->setMargin(0);
624         WorkingSetToolTipWidget* widget = new WorkingSetToolTipWidget(tooltip, m_set, mainWindow());
625         layout->addWidget(widget);
626         tooltip->resize( tooltip->sizeHint() );
627         connect(widget, SIGNAL(shouldClose()), tooltip, SLOT(close()));
628         ActiveToolTip::showToolTip(tooltip);
629         return true;
630     }
631     return QToolButton::event(e);
632 }
633
634 void WorkingSetWidget::workingSetsChanged()
635 {
636     kDebug() << "re-creating widget" << m_connectedArea << m_fixedArea << m_mini;
637     foreach(QToolButton* button, m_buttons.keys())
638         delete button;
639     m_buttons.clear();
640
641     foreach(WorkingSet* set, Core::self()->workingSetControllerInternal()->allWorkingSets()) {
642
643         disconnect(set, SIGNAL(setChangedSignificantly()), this, SLOT(workingSetsChanged()));
644         connect(set, SIGNAL(setChangedSignificantly()), this, SLOT(workingSetsChanged()));
645
646         if(m_mini && set->id() != m_connectedArea->workingSet()) {
647 //             kDebug() << "skipping" << set->id() << ", searching" << m_connectedArea->workingSet();
648             continue; //In "mini" mode, show only the current working set
649         }
650         if(set->isEmpty()) {
651 //             kDebug() << "skipping" << set->id() << "because empty";
652             continue;
653         }
654
655         // Don't show working-sets that are active in an area belong to this main-window, as those
656         // can be activated directly through the icons in the tabs
657         if(!m_mini && set->hasConnectedAreas(m_mainWindow->areas()))
658              continue;
659         
660 //         kDebug() << "adding button for" << set->id();
661         QToolButton* butt = new WorkingSetToolButton(this, set, m_mainWindow);
662         butt->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored));
663
664         m_layout->addWidget(butt);
665         m_buttons[butt] = set;
666     }
667     update();
668 }
669
670 void WorkingSetToolButton::buttonTriggered()
671 {
672     //Only close the working-set if the file was saved before
673     if(!Core::self()->documentControllerInternal()->saveAllDocumentsForWindow(mainWindow(), KDevelop::IDocument::Default))
674         return;
675
676     if(mainWindow()->area()->workingSet() == m_set->id()) {
677         closeSet();
678     }else{
679         m_set->setPersistent(true);
680         mainWindow()->area()->setWorkingSet(m_set->id());
681     }
682 }
683
684 void WorkingSet::changingWorkingSet(Sublime::Area* area, QString from, QString to) {
685     kDebug() << "changing working-set from" << from << "to" << to << ", local: " << m_id << "area" << area;
686     Q_ASSERT(from == m_id);
687     if (from == to)
688         return;
689     Q_ASSERT(m_areas.contains(area));
690     if (!m_id.isEmpty()) saveFromArea(area, area->rootIndex());
691     disconnectArea(area);
692     WorkingSet* newSet = Core::self()->workingSetControllerInternal()->getWorkingSet(to);
693     newSet->connectArea(area);
694     kDebug() << "update ready";
695 }
696
697 ///@todo Move this function into WorkingSetController
698 void WorkingSet::changedWorkingSet(Sublime::Area* area, QString from, QString to) {
699     kDebug() << "changed working-set from" << from << "to" << to << ", local: " << m_id << "area" << area;
700     Q_ASSERT(to == m_id);
701     loadToArea(area, area->rootIndex(), !from.isEmpty());
702     kDebug() << "update ready";
703     Core::self()->workingSetControllerInternal()->notifyWorkingSetSwitched();
704 }
705
706 void WorkingSet::areaViewAdded(Sublime::AreaIndex*, Sublime::View*) {
707     Sublime::Area* area = qobject_cast<Sublime::Area*>(sender());
708     Q_ASSERT(area);
709     Q_ASSERT(area->workingSet() == m_id);
710
711     kDebug() << "added view in" << area << ", id" << m_id;
712     if (m_loading) {
713         kDebug() << "doing nothing because loading";
714         return;
715     }
716     if (m_id.isEmpty()) {
717         //Spawn a new working-set
718
719         WorkingSet* set = Core::self()->workingSetControllerInternal()->newWorkingSet(area->objectName());
720         set->saveFromArea(area, area->rootIndex());
721         area->setWorkingSet(set->id());
722         return;
723     }
724     changed(area);
725 }
726
727 void WorkingSet::areaViewRemoved(Sublime::AreaIndex*, Sublime::View*) {
728     Sublime::Area* area = qobject_cast<Sublime::Area*>(sender());
729     Q_ASSERT(area);
730     Q_ASSERT(area->workingSet() == m_id);
731
732     kDebug() << "removed view in" << area << ", id" << m_id;
733     if (m_loading) {
734         kDebug() << "doing nothing because loading";
735         return;
736     }
737     changed(area);
738 }
739
740 WorkingSet::WorkingSet(QString id, QString icon) : m_id(id), m_iconName(icon) {
741     //Give the working-set icons one color, so they are less disruptive
742     QImage imgActive(KIconLoader::global()->loadIcon(icon, KIconLoader::NoGroup, 16).toImage());
743     QImage imgInactive = imgActive;
744
745     QColor activeIconColor = QApplication::palette().color(QPalette::Active, QPalette::Highlight);
746     QColor inActiveIconColor = QApplication::palette().color(QPalette::Active, QPalette::Base);
747
748     KIconEffect::colorize(imgActive, KColorUtils::mix(inActiveIconColor, activeIconColor, 0.7), 0.5);
749     KIconEffect::colorize(imgInactive, KColorUtils::mix(inActiveIconColor, activeIconColor, 0.3), 0.5);
750
751     m_activeIcon = QIcon(QPixmap::fromImage(imgActive));
752     m_inactiveIcon = QIcon(QPixmap::fromImage(imgActive));
753
754     QImage imgNonPersistent = imgInactive;
755
756     KIconEffect::deSaturate(imgNonPersistent, 1.0);
757
758     m_inactiveNonPersistentIcon = QIcon(QPixmap::fromImage(imgNonPersistent));
759     //effect.apply(KIconLoader::global()->loadIcon(icon, KIconLoader::NoGroup, 16), KIconLoader::NoGroup, );
760 }
761
762 void WorkingSetFileLabel::setIsActiveFile(bool active)
763 {
764     if(active)
765     {
766         ///@todo Use a nicer-looking "blended" highlighting for the active item, like in the area-tabs
767         setAutoFillBackground(true);
768         setBackgroundRole(QPalette::Highlight);
769         setForegroundRole(QPalette::HighlightedText);
770     }else{
771         setAutoFillBackground(false);
772         setBackgroundRole(QPalette::Window);
773         setForegroundRole(QPalette::WindowText);
774     }
775     m_isActive = active;
776 }
777
778
779 void WorkingSetFileLabel::mouseReleaseEvent(QMouseEvent* ev)
780 {
781     if(ev->button() == Qt::LeftButton)
782     {
783         
784         ev->accept();
785         emit clicked();
786         return;
787     }
788     QLabel::mouseReleaseEvent(ev);
789 }
790
791
792 WorkingSetToolTipWidget::WorkingSetToolTipWidget(QWidget* parent, WorkingSet* set, MainWindow* mainwindow) : QWidget(parent), m_set(set) {
793     QVBoxLayout* layout = new QVBoxLayout(this);
794     layout->setSpacing(0);
795     
796     layout->setMargin(0);
797
798     connect(static_cast<Sublime::MainWindow*>(mainwindow)->area(), SIGNAL(viewAdded(Sublime::AreaIndex*,Sublime::View*)), SLOT(updateFileButtons()), Qt::QueuedConnection);
799     connect(static_cast<Sublime::MainWindow*>(mainwindow)->area(), SIGNAL(viewRemoved(Sublime::AreaIndex*,Sublime::View*)), SLOT(updateFileButtons()), Qt::QueuedConnection);
800
801     connect(Core::self()->workingSetControllerInternal(), SIGNAL(workingSetSwitched()), SLOT(updateFileButtons()));
802     
803     // title bar
804     {
805         QHBoxLayout* topLayout = new QHBoxLayout;
806         m_setButton = new WorkingSetToolButton(this, set, mainwindow);
807         m_setButton->hide();
808        
809         topLayout->addSpacing(5);
810         QLabel* icon = new QLabel;
811         topLayout->addWidget(icon);
812         topLayout->addSpacing(5);
813
814         QLabel* name = new QLabel(i18n("<b>Working Set</b>"));
815         name->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
816         topLayout->addWidget(name);
817          topLayout->addSpacing(10);
818
819         icon->setPixmap(m_setButton->icon().pixmap(name->sizeHint().height()+8, name->sizeHint().height()+8));
820         
821         topLayout->addStretch();
822
823         m_openButton = new QPushButton;
824         m_openButton->setFlat(true);
825         topLayout->addWidget(m_openButton);
826
827         m_deleteButton = new QPushButton;
828         m_deleteButton->setIcon(KIcon("edit-delete"));
829         m_deleteButton->setText(i18n("Delete"));
830         m_deleteButton->setToolTip(i18n("Remove this working set. The contained documents are not affected."));
831         m_deleteButton->setFlat(true);
832         connect(m_deleteButton, SIGNAL(clicked(bool)), m_set, SLOT(deleteSet()));
833         connect(m_deleteButton, SIGNAL(clicked(bool)), this, SIGNAL(shouldClose()));
834         topLayout->addWidget(m_deleteButton);
835         layout->addLayout(topLayout);
836         // horizontal line
837         QFrame* line = new QFrame();
838         line->setFrameShape(QFrame::HLine);
839         line->setFrameShadow(QFrame::Raised);
840         layout->addWidget(line);
841     }
842
843     // everything else is added to the following widget which just has a different background color
844     QVBoxLayout* bodyLayout = new QVBoxLayout;
845     {
846         QWidget* body = new QWidget();
847         body->setLayout(bodyLayout);
848         layout->addWidget(body);
849         body->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
850     }
851
852     // document list actions
853     {
854         QHBoxLayout* actionsLayout = new QHBoxLayout;
855
856         QLabel* label = new QLabel(i18n("Documents:"));
857         label->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
858         actionsLayout->addWidget(label);
859
860         actionsLayout->addStretch();
861
862         m_mergeButton = new QPushButton;
863         m_mergeButton->setIcon(KIcon("list-add"));
864         m_mergeButton->setText(i18n("Add All"));
865         m_mergeButton->setToolTip(i18n("Add all documents that are part of this working set to the currently active working set."));
866         m_mergeButton->setFlat(true);
867         connect(m_mergeButton, SIGNAL(clicked(bool)), m_setButton, SLOT(mergeSet()));
868         actionsLayout->addWidget(m_mergeButton);
869
870         m_subtractButton = new QPushButton;
871         m_subtractButton->setIcon(KIcon("list-remove"));
872         m_subtractButton->setText(i18n("Subtract All"));
873         m_subtractButton->setToolTip(i18n("Remove all documents that are part of this working set from the currently active working set."));
874         m_subtractButton->setFlat(true);
875         connect(m_subtractButton, SIGNAL(clicked(bool)), m_setButton, SLOT(subtractSet()));
876         actionsLayout->addWidget(m_subtractButton);
877         bodyLayout->addLayout(actionsLayout);
878     }
879
880     QSet<QString> hadFiles;
881
882     QVBoxLayout* filesLayout = new QVBoxLayout;
883     filesLayout->setMargin(0);
884     
885     foreach(const QString& file, m_set->fileList()) {
886         
887         if(hadFiles.contains(file))
888             continue;
889         
890         hadFiles.insert(file);
891         
892         FileWidget* widget = new FileWidget;
893         widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
894         
895         QHBoxLayout* fileLayout = new QHBoxLayout(widget);
896
897         QToolButton* plusButton = new QToolButton;
898         plusButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Maximum);
899         fileLayout->addWidget(plusButton);
900
901         WorkingSetFileLabel* fileLabel = new WorkingSetFileLabel;
902         fileLabel->setTextFormat(Qt::RichText);
903         // We add spaces behind and after, to make it look nicer
904         fileLabel->setText("&nbsp;" + Core::self()->projectController()->prettyFileName(KUrl(file)) + "&nbsp;");
905         fileLabel->setToolTip(i18n("Click to open and activate this document."));
906         fileLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
907         fileLayout->addWidget(fileLabel);
908         fileLayout->setMargin(0);
909
910         plusButton->setMaximumHeight(fileLabel->sizeHint().height() + 4);
911         plusButton->setMaximumWidth(plusButton->maximumHeight());
912         
913         plusButton->setObjectName(file);
914         fileLabel->setObjectName(file);
915         fileLabel->setCursor(QCursor(Qt::PointingHandCursor));
916
917         widget->m_button = plusButton;
918         widget->m_label = fileLabel;
919         
920         filesLayout->addWidget(widget);
921         m_fileWidgets.insert(file, widget);
922         m_orderedFileWidgets.push_back(widget);
923
924         connect(plusButton, SIGNAL(clicked(bool)), this, SLOT(buttonClicked(bool)));
925         connect(fileLabel, SIGNAL(clicked()), this, SLOT(labelClicked()));
926     }
927     
928     bodyLayout->addLayout(filesLayout);
929
930     updateFileButtons();
931     connect(set, SIGNAL(setChangedSignificantly()), SLOT(updateFileButtons()));
932     connect(Core::self()->workingSetControllerInternal()->getWorkingSet(mainwindow->area()->workingSet()), SIGNAL(setChangedSignificantly()), SLOT(updateFileButtons()));
933     connect(mainwindow->area(), SIGNAL(changedWorkingSet(Sublime::Area*,QString,QString)), SLOT(updateFileButtons()), Qt::QueuedConnection);
934
935     QMetaObject::invokeMethod(this, "updateFileButtons");
936 }
937
938
939 void WorkingSetToolTipWidget::updateFileButtons()
940 {
941     MainWindow* mainWindow = dynamic_cast<MainWindow*>(Core::self()->uiController()->activeMainWindow());
942     Q_ASSERT(mainWindow);
943     
944     QString activeFile;
945     
946     if(mainWindow->area()->activeView())
947         activeFile = mainWindow->area()->activeView()->document()->documentSpecifier();
948
949     WorkingSet* currentWorkingSet = Core::self()->workingSetControllerInternal()->getWorkingSet(mainWindow->area()->workingSet());
950     QSet<QString> openFiles = currentWorkingSet->fileList().toSet();
951
952     bool allOpen = true;
953     bool noneOpen = true;
954
955     bool needResize = false;
956     
957     bool allHidden = true;
958     
959     for(QMap< QString, FileWidget* >::iterator it = m_fileWidgets.begin(); it != m_fileWidgets.end(); ++it) {
960         if(openFiles.contains(it.key())) {
961             noneOpen = false;
962             (*it)->m_button->setToolTip(i18n("Remove this file from the current working set"));
963             (*it)->m_button->setIcon(KIcon("list-remove"));
964             (*it)->show();
965         }else{
966             allOpen = false;
967             (*it)->m_button->setToolTip(i18n("Add this file to the current working set"));
968             (*it)->m_button->setIcon(KIcon("list-add"));
969             if(currentWorkingSet == m_set)
970             {
971                 (*it)->hide();
972                 needResize = true;
973             }
974         }
975         
976         
977         if(!(*it)->isHidden())
978             allHidden = false;
979         
980         (*it)->m_label->setIsActiveFile(it.key() == activeFile);
981     }
982
983     // NOTE: allways hide merge&subtract all on current working set
984     // if we want to enable mergeButton, we have to fix it's behavior since it operates directly on the
985     // set contents and not on the m_fileWidgets
986     m_mergeButton->setHidden(allOpen || currentWorkingSet->id() == m_set->id());
987     m_subtractButton->setHidden(noneOpen || mainWindow->area()->workingSet() == m_set->id());
988     m_deleteButton->setHidden(m_set->hasConnectedAreas());
989
990     if(m_set->id() == mainWindow->area()->workingSet()) {
991         disconnect(m_openButton, SIGNAL(clicked(bool)), m_setButton, SLOT(loadSet()));
992         connect(m_openButton, SIGNAL(clicked(bool)), m_setButton, SLOT(closeSet()));
993         m_openButton->setIcon(KIcon("project-development-close"));
994         m_openButton->setText(i18n("Close"));
995     }else{
996         disconnect(m_openButton, SIGNAL(clicked(bool)), m_setButton, SLOT(closeSet()));
997         connect(m_openButton, SIGNAL(clicked(bool)), m_setButton, SLOT(loadSet()));
998         m_openButton->setIcon(KIcon("project-open"));
999         m_openButton->setText(i18n("Load"));
1000     }
1001     
1002     if(allHidden && tooltip)
1003         tooltip->hide();
1004     
1005     if(needResize && tooltip)
1006         tooltip->resize(tooltip->sizeHint());
1007 }
1008
1009 void WorkingSetToolTipWidget::buttonClicked(bool)
1010 {
1011     QPointer<WorkingSetToolTipWidget> stillExists(this);
1012
1013     QToolButton* s = qobject_cast<QToolButton*>(sender());
1014     Q_ASSERT(s);
1015
1016     MainWindow* mainWindow = dynamic_cast<MainWindow*>(Core::self()->uiController()->activeMainWindow());
1017     Q_ASSERT(mainWindow);
1018     QSet<QString> openFiles = Core::self()->workingSetControllerInternal()->getWorkingSet(mainWindow->area()->workingSet())->fileList().toSet();
1019
1020     if(!openFiles.contains(s->objectName())) {
1021         Core::self()->documentControllerInternal()->openDocument(s->objectName());
1022     }else{
1023         openFiles.remove(s->objectName());
1024         filterViews(openFiles);
1025     }
1026
1027     if(stillExists)
1028         updateFileButtons();
1029 }
1030
1031 void WorkingSetToolTipWidget::labelClicked()
1032 {
1033     QPointer<WorkingSetToolTipWidget> stillExists(this);
1034
1035     WorkingSetFileLabel* s = qobject_cast<WorkingSetFileLabel*>(sender());
1036     Q_ASSERT(s);
1037     
1038     bool found = false;
1039     
1040     Sublime::MainWindow* window = static_cast<Sublime::MainWindow*>(ICore::self()->uiController()->activeMainWindow());
1041     
1042     foreach(Sublime::View* view, window->area()->views())
1043     {
1044         if(view->document()->documentSpecifier() == s->objectName())
1045         {
1046             window->activateView(view);
1047             found = true;
1048             break;
1049         }
1050     }
1051     
1052     if(!found)
1053         Core::self()->documentControllerInternal()->openDocument(s->objectName());
1054     
1055     if(stillExists)
1056         updateFileButtons();
1057 }
1058
1059 WorkingSetToolButton::WorkingSetToolButton(QWidget* parent, WorkingSet* set, MainWindow* mainWindow) : QToolButton(parent), m_set(set), m_toolTipEnabled(true) {
1060     setFocusPolicy(Qt::NoFocus);
1061     QColor activeBgColor = palette().color(QPalette::Active, QPalette::Highlight);
1062     QColor normalBgColor = palette().color(QPalette::Active, QPalette::Base);
1063     QColor useColor;
1064     if(mainWindow && mainWindow->area() && mainWindow->area()->workingSet() == set->id()) {
1065         useColor = KColorUtils::mix(normalBgColor, activeBgColor, 0.6);
1066         setIcon(set->activeIcon());
1067     }else{
1068         useColor = KColorUtils::mix(normalBgColor, activeBgColor, 0.2);
1069         setIcon(set->inactiveIcon());
1070     }
1071
1072     QString sheet = QString("QToolButton { background : %1}").arg(htmlColor(useColor));
1073     setStyleSheet(sheet);
1074
1075     connect(this, SIGNAL(clicked(bool)), SLOT(buttonTriggered()));
1076 }
1077
1078 void WorkingSet::setPersistent(bool persistent) {
1079     if(m_id.isEmpty())
1080         return;
1081     KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets");
1082     KConfigGroup group = setConfig.group(m_id);
1083     group.writeEntry("persistent", persistent);
1084 #ifdef SYNC_OFTEN
1085     group.sync();
1086 #endif
1087     kDebug() << "setting" << m_id << "persistent:" << persistent;
1088 }
1089
1090 bool WorkingSet::isPersistent() const {
1091     if(m_id.isEmpty())
1092         return false;
1093     KConfigGroup setConfig(Core::self()->activeSession()->config(), "Working File Sets");
1094     KConfigGroup group = setConfig.group(m_id);
1095     return group.readEntry("persistent", false);
1096 }
1097
1098 QIcon WorkingSet::inactiveIcon() const {
1099     if(isPersistent())
1100         return m_inactiveIcon;
1101     else
1102         return m_inactiveNonPersistentIcon;
1103 }
1104
1105 void WorkingSetController::setupActions()
1106 {
1107     KActionCollection * ac =
1108         Core::self()->uiControllerInternal()->defaultMainWindow()->actionCollection();
1109
1110     KAction *action;
1111
1112     action = ac->addAction ( "view_next_window" );
1113     action->setText( i18n( "Next Document" ) );
1114     action->setIcon( KIcon("go-next") );
1115     action->setShortcut( Qt::ALT + Qt::SHIFT + Qt::Key_Right );
1116     action->setWhatsThis( i18n( "Switch the focus to the next open document." ) );
1117     action->setStatusTip( i18n( "Switch the focus to the next open document." ) );
1118     connect( action, SIGNAL(triggered()), this, SLOT(nextDocument()) );
1119
1120     action = ac->addAction ( "view_previous_window" );
1121     action->setText( i18n( "Previous Document" ) );
1122     action->setIcon( KIcon("go-previous") );
1123     action->setShortcut( Qt::ALT + Qt::SHIFT + Qt::Key_Left );
1124     action->setWhatsThis( i18n( "Switch the focus to the previous open document." ) );
1125     action->setStatusTip( i18n( "Switch the focus to the previous open document." ) );
1126     connect( action, SIGNAL(triggered()), this, SLOT(previousDocument()) );
1127 }
1128
1129 void WorkingSetController::showGlobalToolTip()
1130 {
1131     delete tooltip;
1132     
1133     KDevelop::MainWindow* window = static_cast<KDevelop::MainWindow*>(Core::self()->uiControllerInternal()->activeMainWindow());
1134
1135     tooltip = new KDevelop::ActiveToolTip(window, window->mapToGlobal(window->geometry().topRight()));
1136     QVBoxLayout* layout = new QVBoxLayout(tooltip);
1137     layout->setMargin(0);
1138     WorkingSetToolTipWidget* widget = new WorkingSetToolTipWidget(tooltip, getWorkingSet(window->area()->workingSet()), window);
1139     layout->addWidget(widget);
1140     tooltip->resize( tooltip->sizeHint() );
1141     ActiveToolTip::showToolTip(tooltip);
1142     connect(m_hideToolTipTimer, SIGNAL(timeout()),  tooltip, SLOT(deleteLater()));
1143     m_hideToolTipTimer->start();
1144     connect(tooltip, SIGNAL(mouseIn()), m_hideToolTipTimer, SLOT(stop()));
1145     connect(tooltip, SIGNAL(mouseOut()), m_hideToolTipTimer, SLOT(start()));
1146 }
1147
1148 void WorkingSetController::nextDocument()
1149 {
1150     if(!tooltip)
1151         showGlobalToolTip();
1152
1153     m_hideToolTipTimer->stop();
1154     m_hideToolTipTimer->start(toolTipTimeout);
1155
1156     if(tooltip)
1157     {
1158         WorkingSetToolTipWidget* widget = tooltip->findChild<WorkingSetToolTipWidget*>();
1159         Q_ASSERT(widget);
1160         widget->nextDocument();
1161     }
1162 }
1163
1164 void WorkingSetController::previousDocument()
1165 {
1166     if(!tooltip)
1167         showGlobalToolTip();
1168
1169     m_hideToolTipTimer->stop();
1170     m_hideToolTipTimer->start(toolTipTimeout);
1171     
1172     if(tooltip)
1173     {
1174         WorkingSetToolTipWidget* widget = tooltip->findChild<WorkingSetToolTipWidget*>();
1175         Q_ASSERT(widget);
1176         widget->previousDocument();
1177     }
1178 }
1179
1180 void WorkingSetToolTipWidget::nextDocument()
1181 {
1182     int active = -1;
1183     for(int a = 0; a < m_orderedFileWidgets.size(); ++a)
1184         if(m_orderedFileWidgets[a]->m_label->isActive())
1185             active = a;
1186     
1187     if(active == -1)
1188     {
1189         kWarning() << "Found no active document";
1190         return;
1191     }
1192     
1193     int next = (active + 1) % m_orderedFileWidgets.size();
1194     while(m_orderedFileWidgets[next]->isHidden() && next != active)
1195         next = (next + 1) % m_orderedFileWidgets.size();
1196     
1197     m_orderedFileWidgets[next]->m_label->emitClicked();
1198 }
1199
1200 void WorkingSetToolTipWidget::previousDocument()
1201 {
1202     int active = -1;
1203     for(int a = 0; a < m_orderedFileWidgets.size(); ++a)
1204         if(m_orderedFileWidgets[a]->m_label->isActive())
1205             active = a;
1206     
1207     if(active == -1)
1208     {
1209         kWarning() << "Found no active document";
1210         return;
1211     }
1212     
1213     int next = active - 1;
1214     if(next < 0)
1215         next += m_orderedFileWidgets.size();
1216     
1217     while(m_orderedFileWidgets[next]->isHidden() && next != active)
1218     {
1219         next -= 1;
1220         if(next < 0)
1221             next += m_orderedFileWidgets.size();
1222     }
1223     
1224     m_orderedFileWidgets[next]->m_label->emitClicked();
1225 }
1226
1227 #include "workingsetcontroller.moc"
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238