diff options
Diffstat (limited to 'kicker/applets/launcher/quicklauncher.cpp')
| -rw-r--r-- | kicker/applets/launcher/quicklauncher.cpp | 1130 | 
1 files changed, 1130 insertions, 0 deletions
diff --git a/kicker/applets/launcher/quicklauncher.cpp b/kicker/applets/launcher/quicklauncher.cpp new file mode 100644 index 000000000..1ee251b36 --- /dev/null +++ b/kicker/applets/launcher/quicklauncher.cpp @@ -0,0 +1,1130 @@ +/***************************************************************** + +Copyright (c) 2000 Bill Nagel +Copyright (c) 2004 Dan Bullok <dan.devel@bullok.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#include <tqpainter.h> +#include <tqpopupmenu.h> +#include <tqslider.h> +#include <tqtimer.h> +#include <tqtooltip.h> + +#include <dcopclient.h> +#include <tdeaction.h> +#include <tdeapplication.h> +#include <tdeaboutapplication.h> +#include <tdeaboutdata.h> +#include <kdialogbase.h> +#include <tdeglobal.h> +#include <tdelocale.h> +#include <tdemessagebox.h> +#include <knuminput.h> +#include <tdeconfig.h> +#include <kstandarddirs.h> +#include <kurldrag.h> +#include <kdebug.h> + + +#include <algorithm> +#include <list> +#include <math.h> +#include <set> +#include <assert.h> + +#include "configdlg.h" +#include "popularity.h" +#include "quicklauncher.h" +#include "quickbutton.h" +#include "quickaddappsmenu.h" +#include "quickbuttongroup.h" + +typedef ButtonGroup::iterator ButtonIter; +const ButtonGroup::Index NotFound=ButtonGroup::NotFound; +const ButtonGroup::Index Append=ButtonGroup::Append; + +#ifdef DEBUG +   #define DEBUGSTR kdDebug() +#else +   #define DEBUGSTR kndDebug() +#endif + +extern "C" +{ +    KDE_EXPORT KPanelApplet* init(TQWidget *parent, const TQString& configFile) +    { +        TDEGlobal::locale()->insertCatalogue("quicklauncher"); +        return new QuickLauncher(configFile, KPanelApplet::Normal, +                                 KPanelApplet::Preferences, +                                 parent, "quicklauncher"); +    } +} + +QuickLauncher::QuickLauncher(const TQString& configFile, Type type, int actions, +                             TQWidget *parent, const char *name) :  +    KPanelApplet(configFile, type, actions, parent, name) +{ +    DCOPObject::setObjId("QuickLauncherApplet"); +    DEBUGSTR << endl << endl << endl << "------------" << flush; +    DEBUGSTR << "QuickLauncher::QuickLauncher(" << configFile << ",...)" << +                endl << flush; + +    m_settings = new Prefs(sharedConfig()); +    m_settings->readConfig(); + +    m_needsSave = false; +    m_needsRefresh = false; +    m_refreshEnabled = false; + +    m_configDialog = 0; +    m_popup = 0; +    m_appletPopup = 0; +    m_removeAppsMenu = 0; + +    m_dragAccepted = false; + +    m_buttons = new ButtonGroup; +    m_manager = new FlowGridManager; +    m_newButtons = 0; +    m_oldButtons = 0; +    m_dragButtons = 0; + +    m_configAction = new TDEAction(i18n("Configure Quicklauncher..."), "configure", TDEShortcut(), +        TQT_TQOBJECT(this), TQT_SLOT(slotConfigure()), TQT_TQOBJECT(this)); + +    m_saveTimer = new TQTimer(this, "m_saveTimer"); +    connect(m_saveTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(saveConfig())); + +    m_popularity = new PopularityStatistics(); + +    setBackgroundOrigin(AncestorOrigin); + +    loadConfig(); + +    buildPopupMenu(); +    m_minPanelDim = std::max(16, m_settings->iconDimChoices()[1]); +    refreshContents(); +    setRefreshEnabled(true); + +    setAcceptDrops(true); +    //TQToolTip::add(this, i18n("Drop applications here")); +    DEBUGSTR << "    QuickLauncher::QuickLauncher(" << configFile <<  +                ",...) END" << endl << flush; + +    DCOPClient *dcopClient = TDEApplication::dcopClient(); +    dcopClient->connectDCOPSignal(0, "appLauncher",  +        "serviceStartedByStorageId(TQString,TQString)", +        "QuickLauncherApplet", +        "serviceStartedByStorageId(TQString,TQString)", +        false); +    kdDebug() << "Quicklauncher registered DCOP signal" << endl; +} + + +//TODO:?  Drag/drop more than one item at a time + +QuickLauncher::~QuickLauncher() +{ +    TDEGlobal::locale()->removeCatalogue("quicklauncher"); +    setCustomMenu(0); +    delete m_popup; +    delete m_appletPopup; +    delete m_removeAppsMenu; +    delete m_popularity; +    clearTempButtons(); +    if (m_buttons) +    { +        m_buttons->deleteContents(); +        delete m_buttons; +    } +} + +// Builds, connects _popup menu +void QuickLauncher::buildPopupMenu() +{ +    QuickAddAppsMenu *addAppsMenu = new QuickAddAppsMenu(this, this); +    m_popup = new TQPopupMenu(this); +    m_popup->insertItem(i18n("Add Application"), addAppsMenu); +    m_configAction->plug(m_popup); + +    m_appletPopup = new TQPopupMenu(this); +    m_appletPopup->insertItem(i18n("Add Application"), addAppsMenu); +    m_removeAppsMenu = new TQPopupMenu(this); +    connect(m_removeAppsMenu, TQT_SIGNAL(aboutToShow()), +            TQT_SLOT(fillRemoveAppsMenu())); +    connect(m_removeAppsMenu, TQT_SIGNAL(activated(int)),  +            TQT_SLOT(removeAppManually(int))); +    m_appletPopup->insertItem(i18n("Remove Application"), m_removeAppsMenu); + +    m_appletPopup->insertSeparator(); +    m_appletPopup->setCheckable( true ); +    m_appletPopup->insertItem(i18n("About"), this, TQT_SLOT(about())); +    setCustomMenu(m_appletPopup); +} + + +// Fill the remove apps menu +void QuickLauncher::fillRemoveAppsMenu() +{ +    m_removeAppsMenu->clear(); +    ButtonIter iter(m_buttons->begin()); +    int i = 0; +    while (iter != m_buttons->end()) +    { +        TQString text = TQToolTip::textFor(*iter); +        if (text.isEmpty()) +        { +            text = (*iter)->url(); +            if (text.isEmpty()) +            { +                text = i18n("Unknown"); +            } +        } +        m_removeAppsMenu->insertItem((*iter)->icon(), text, i); +        ++iter; +        ++i; +    } +} + +void QuickLauncher::slotSettingsDialogChanged() +{ +    // Update conserve space setting +    setConserveSpace(m_settings->conserveSpace()); +    m_popularity->setHistoryHorizon(m_settings->historyHorizon()/100.0); +    slotAdjustToCurrentPopularity(); +    kdDebug() << "Icon size: " << m_settings->iconDim() << endl; +    refreshContents(); + +    saveConfig(); +} + +void QuickLauncher::action(Action a) +{ +    if (a == KPanelApplet::Preferences) +    { +        slotConfigure(); +    } +    else +    { +        KPanelApplet::action(a); +    } +} + +void QuickLauncher::slotConfigure() +{ +    if (!m_configDialog) +    { +        m_configDialog = new ConfigDlg(this, "configdialog",  +            m_settings, SIZE_AUTO, KDialogBase::Plain, KDialogBase::Ok |  +            KDialogBase::Cancel | KDialogBase::Apply | KDialogBase::Default); +        connect(m_configDialog, TQT_SIGNAL(settingsChanged()), +            this, TQT_SLOT(slotSettingsDialogChanged())); +    } + +    m_configDialog->show(); +} + + +int QuickLauncher::findApp(QuickButton *button) +{ +    if (m_buttons->empty()) +    { +        return NotFound; +    } +    int pos = m_buttons->findValue(button); +    return pos; +} + + +int QuickLauncher::findApp(TQString url) +{ +    if (m_buttons->empty()) +    { +        return NotFound; +    } +    int pos=m_buttons->findDescriptor(url); +    return pos; +} + +void QuickLauncher::removeAppManually(int index) +{ +    removeApp(index, true); +} + +void QuickLauncher::removeApp(int index, bool manuallyRemoved) +{ +    if (m_buttons->empty()) +    { +        return; +    } +    if (!m_buttons->isValidIndex(index)) +    { +        kdWarning() << "    removeApp (" << index <<  +            ") *******WARNING****** index=" << index << "is out of bounds." << +            endl << flush; +        return; +    } +    DEBUGSTR << "Removing button.  index=" << index << " url='" << +        (*m_buttons)[index]->url() << "'" << endl << flush; + +    TQString removeAppUrl = (*m_buttons)[index]->url(); +    TQString removeAppMenuId = (*m_buttons)[index]->menuId(); + +    if (removeAppUrl == "SPECIAL_BUTTON__SHOW_DESKTOP") { +        m_settings->setShowDesktopEnabled(false); +    } +    +    delete (*m_buttons)[index]; +    m_buttons->eraseAt(index); +    refreshContents(); +    +    if (int(m_buttons->size()) < m_settings->autoAdjustMinItems() && manuallyRemoved) +    { +        m_settings->setAutoAdjustMinItems(m_buttons->size()); +    } + +    if (manuallyRemoved) +    { +        m_popularity->moveToBottom(removeAppMenuId); +        slotAdjustToCurrentPopularity(); +    } + +    saveConfig(); +} + + +void QuickLauncher::removeApp(TQString url, bool manuallyRemoved) +{ +    int index = findApp(url); +    if (index == NotFound) +    { +        kdDebug() << "removeApp: Not found: " << url << endl; +        return; +    } +    removeApp(index, manuallyRemoved); +} + + +void QuickLauncher::removeAppManually(QuickButton *button) +{ +    int index = findApp(button); +    if (index == NotFound) +    { +        return; +    } +    removeApp(index, true); +} + + +int QuickLauncher::widthForHeight(int h) const +{ +    FlowGridManager temp_manager = *m_manager; +    temp_manager.setFrameSize(TQSize(h,h)); +    temp_manager.setOrientation(Qt::Horizontal); // ??? probably not necessary +    if (temp_manager.isValid()) +    { +        return temp_manager.frameSize().width(); +    } +    return m_minPanelDim; +} + + +int QuickLauncher::heightForWidth(int w) const +{ +    FlowGridManager temp_manager=*m_manager; +    temp_manager.setFrameSize(TQSize(w,w)); +    temp_manager.setOrientation(Qt::Vertical); // ??? probably not necessary +    if (temp_manager.isValid()) +    { +        return temp_manager.frameSize().height(); +    } +    return m_minPanelDim; +} + + +int QuickLauncher::dimension() const +{ +    if (orientation()==Qt::Vertical) +    { +        return size().width(); +    } +    return size().height(); +} + +void QuickLauncher::addApp(TQString url, bool manuallyAdded) +{ +    assert(m_buttons); +    TQString newButtonId = QuickURL(url).menuId(); +    if (m_appOrdering.find(newButtonId) == m_appOrdering.end()) +    { +        m_appOrdering[newButtonId] = m_appOrdering.size(); +    } +    uint appPos; +    for (appPos = 0; appPos < m_buttons->size(); ++appPos) +    { +        TQString buttonId = (*m_buttons)[appPos]->menuId(); +        if (m_appOrdering[buttonId] >= m_appOrdering[newButtonId]) +        { +            break; +        } +    } +    addApp(url, appPos, manuallyAdded); +} + +QuickButton* QuickLauncher::createButton(TQString url) +{ +    QuickButton* newButton=new QuickButton(url, m_configAction, this); +    connect(newButton, TQT_SIGNAL(executed(TQString)),  +            this, TQT_SLOT(slotOwnServiceExecuted(TQString))); +    connect(newButton, TQT_SIGNAL(stickyToggled(bool)),  +            this, TQT_SLOT(slotStickyToggled())); +    newButton->setPopupDirection(popupDirection()); +    return newButton; +} + +void QuickLauncher::addApp(TQString url, int index, bool manuallyAdded) +{ +    DEBUGSTR << endl <<"About to add: url='" << url <<  +                "' index=" << index << endl << flush; +    QuickButton *newButton; +    if (!m_buttons->isValidInsertIndex(index)) +    { +        kdWarning() << "    *******WARNING****** index=" << index <<  +            "is out of bounds." << endl << flush; +        index = m_buttons->lastIndex(); +    } +    int old = findApp(QuickURL(url).url()); +    if (old != NotFound) +    { +        if (index == old) +        { +            return; +        } +        if (index > old) +        { +            index--; +        } +        newButton = (*m_buttons)[old]; +        m_buttons->eraseAt(old); +    } +    else +    { +        newButton = createButton(url); +    } +    m_buttons->insertAt(index, newButton); +    DEBUGSTR << "Added: url='"<<url<<"' index="<<index<<endl<<endl<<flush; +    refreshContents(); + +    if (manuallyAdded) +    { +        newButton->setSticky(true); +        if (int(m_buttons->size()) > m_settings->autoAdjustMaxItems()) +        { +            m_settings->setAutoAdjustMaxItems(m_buttons->size()); +        } +    } + +    updateInsertionPosToStatusQuo(); +    saveConfig(); +} + +void QuickLauncher::updateInsertionPosToStatusQuo() +{ +    // Update the app ordering map, so that next time,  +    // addApp(url,manAdded) (without index) will insert the  +    // item at the same position again. +    std::list<TQString> appList; +    std::set<int> posList; +    //kdDebug() << "Rearranging application order. Before:" << endl; +    for (uint n = 0; n < m_buttons->size(); ++n) +    { +        TQString buttonId = (*m_buttons)[n]->menuId(); +        appList.push_back(buttonId); +        if (m_appOrdering.find(buttonId) == m_appOrdering.end()) +        { +            m_appOrdering[buttonId] = m_appOrdering.size(); +        } +        posList.insert(m_appOrdering[buttonId]); +        //kdDebug() << m_appOrdering[buttonId] << " = " << buttonId << endl; +    } +    //kdDebug() << "After:" << endl; +    while (posList.size() > 0) +    { +        assert(appList.size() > 0); +        m_appOrdering[*appList.begin()] = *posList.begin(); +        kdDebug() << *posList.begin() << " = " << *appList.begin() << endl; +        posList.erase(posList.begin()); +        appList.pop_front(); +    } +    //kdDebug() << "Done." << endl; +} + +void QuickLauncher::addAppBeforeManually(TQString url, TQString sender) +{ +    if (sender.isNull()) +    { +        addApp(url, Append, true); +    } +    int pos = findApp(sender); +    if (pos < 0)  +    { +        pos = Append; +    } +    DEBUGSTR << "QuickLauncher::addAppBefore(" << url <<  +                "," << sender << "):  pos=" << pos << endl << flush; +    addApp(url, pos, true); +} + + +void QuickLauncher::about() +{ +    TDEAboutData about("quicklauncher", I18N_NOOP("Quick Launcher"), "2.0", +                     I18N_NOOP("A simple application launcher"), +                     TDEAboutData::License_GPL_V2,  +                     "(C) 2000 Bill Nagel\n(C) 2004 Dan Bullok\n(C) 2005 Fred Schaettgen"); +    TDEAboutApplication a(&about, this); +    a.exec(); +} + + +void QuickLauncher::mousePressEvent(TQMouseEvent *e) +{ +    if (e->button() == Qt::RightButton) +    { +        m_popup->popup(e->globalPos()); +    } +} + +void QuickLauncher::resizeEvent(TQResizeEvent*) +{ +    refreshContents(); +} + +void QuickLauncher::dragEnterEvent(TQDragEnterEvent *e) +{ +    DEBUGSTR << "QuickLauncher::dragEnterEvent(pos=" << e->pos() <<  +        " type=" << e->type() << ")" << endl << flush; +    m_dragAccepted=false; +    KURL::List kurlList; +    if (!isDragEnabled() || !KURLDrag::decode(e, kurlList))  +    { +        e->accept(false); +        return; +    } + +    if (kurlList.size()<=0) +    { +        e->accept(false); +        return; +    } +    m_dragButtons=new ButtonGroup; +    m_oldButtons=new ButtonGroup(*m_buttons); + +    TQString url; +    KURL::List::ConstIterator it = kurlList.begin(); +    for ( ; it != kurlList.end(); ++it ) +    { +        url = QuickURL((*it).url()).url(); +        kdDebug() << "    Drag Object='"<<url<<"' " << (*it).url() << endl; +        int pos = m_buttons->findDescriptor(url); +        if (pos != NotFound) +        { +            // if it's already in m_buttons, take it out +            m_dragButtons->push_back(m_buttons->takeFrom(pos)); +        } +        else +        { +            // otherwise, create a new one +            QuickButton* button = createButton(url); +            button->setSticky(true); +            m_dragButtons->push_back(button); +        } +    } +    if (m_dragButtons->size() > 0) +    { +        //make sure we can drag at least one button. +        m_dragAccepted=true; +        m_newButtons=new ButtonGroup(*m_buttons); +        m_dropPos=NotFound; +        e->accept(true); +        return; +    } +    e->accept(false); +    clearTempButtons(); +} + + +void QuickLauncher::dragMoveEvent(TQDragMoveEvent *e) +{ +    if (!m_dragAccepted) +    { +        kdWarning() << "QuickLauncher::dragMoveEvent: Drag is not accepted." << +            m_dragAccepted << endl << flush; +        e->accept(false); +        return; +    } + +    e->accept(true); +    int pos=m_manager->indexNearest(e->pos()); +    if (pos == m_dropPos) +    { +        return;// Already been inserted here, no need to update +    } + +    if (m_newButtons->isValidInsertIndex(pos)) +    { +        mergeButtons(pos); +        m_dropPos=pos; +    } +    refreshContents(); +} + + +void QuickLauncher::dragLeaveEvent(TQDragLeaveEvent *e) +{ +    DEBUGSTR << "QuickLauncher::dragLeaveEvent(type=" <<  +        e->type() << ")" << endl << flush; +    if (!m_dragAccepted) +    { +        return; +    } + +    // No drop.  Return to starting state. +    std::swap(m_buttons,m_oldButtons); +    clearTempButtons(); + +    refreshContents(); +    saveConfig(); +} + + +void QuickLauncher::dropEvent(TQDropEvent *e) +{ +    DEBUGSTR << "QuickLauncher::dropEvent(pos=" << e->pos() <<  +        " type=" << e->type() << ")" << endl << flush; +    if (!m_dragAccepted) +    { +        e->accept(false); +        return; +    } + +    if (e->source() == 0) +    { +        for (uint n=0; n<m_dragButtons->size(); ++n) +        { +            (*m_dragButtons)[n]->setSticky(true); +        } +    } + +    clearTempButtons(); +    refreshContents(); +    saveConfig(); +    updateInsertionPosToStatusQuo(); +} + +// insert dragbuttons at index in m_newButtons.  Put result in m_buttons +void QuickLauncher::mergeButtons(int index) +{ +    if (!m_newButtons->isValidInsertIndex(index)) +    { +        index=m_newButtons->size(); +    } + +    m_buttons->clear(); +    (*m_buttons) = (*m_newButtons); +    m_buttons->insertAt(index, *m_dragButtons); +    refreshContents(); +} + +void QuickLauncher::clearTempButtons() +{ +    std::set<QuickButton*> allButtons; +    //put all the m_buttons in a set (removes duplicates automatically +    if (m_newButtons)  +    { +        allButtons.insert(m_newButtons->begin(),m_newButtons->end()); +    } +    if (m_oldButtons) +    { +        allButtons.insert(m_oldButtons->begin(),m_oldButtons->end()); +    } +    if (m_dragButtons) +    { +        allButtons.insert(m_dragButtons->begin(),m_dragButtons->end()); +    } +     +    //delete temp ButtonGroups +    delete m_newButtons; m_newButtons=0; +    delete m_oldButtons; m_oldButtons=0; +    delete m_dragButtons; m_dragButtons=0; +     +    //if an element allButtons is NOT in m_buttons (the ones we keep), delete it +    std::set<QuickButton *>::iterator iter = allButtons.begin(); +    while (iter != allButtons.end()) +    { +        if (findApp(*iter) == NotFound) +        { +            delete *iter; +        } +        ++iter; +    } +    m_dragAccepted = false; +    m_dropPos = NotFound; +} + +void QuickLauncher::refreshContents() +{ +    int idim, d(dimension()); + +    // make sure show desktop setting is honored +    TQStringList urls, volatileUrls; +    ButtonIter iter = m_buttons->begin(); +    while (iter != m_buttons->end()) { +        if ((*iter)->sticky() == false) +        { +            volatileUrls.append((*iter)->menuId()); +        } +        urls.append((*iter)->menuId()); +        ++iter; +    } +    if (m_settings->showDesktopEnabled()) { +        if (!urls.contains("SPECIAL_BUTTON__SHOW_DESKTOP")) { +            urls.prepend("SPECIAL_BUTTON__SHOW_DESKTOP"); +            addApp("SPECIAL_BUTTON__SHOW_DESKTOP", 0, true); +        } +    } +    else { +        if (urls.contains("SPECIAL_BUTTON__SHOW_DESKTOP")) { +            urls.remove("SPECIAL_BUTTON__SHOW_DESKTOP"); +            removeApp("SPECIAL_BUTTON__SHOW_DESKTOP", true); +        } +    } + +    // determine button size +    if (m_settings->iconDim() == SIZE_AUTO) +    { +        if (d < 18) +        { +             idim = std::min(16,d); +        } +        else if (d < 64) +        { +            idim = 16; +        } +        else if (d < 80) +        { +            idim = 20; +        } +        else if (d < 122) +        { +            idim = 24; +        } +        else +        { +            idim = 28; +        } +    } +    else +    { +        idim = std::min(m_settings->iconDim(), d - std::max((d/8)-1, 0) * 2); +    } +    m_space = std::max((idim/8)-1, 0); +    m_border = m_space; +    m_buttonSize = TQSize(idim, idim); +    m_manager->setOrientation(orientation()); +    m_manager->setNumItems(m_buttons->size()); +    m_manager->setFrameSize(size()); +    m_manager->setItemSize(m_buttonSize); +    m_manager->setSpaceSize(TQSize(m_space, m_space)); +    m_manager->setBorderSize(TQSize(m_border, m_border)); +    if (!m_refreshEnabled) +    { +        m_needsRefresh=true; +        return; +    } +    if (!m_manager->isValid()) +    { +        kdDebug()<<endl<<"******WARNING******    Layout is invalid."<< +            endl << flush; +        m_manager->dump(); +        return; +    } + +    unsigned index; +    TQPoint pos; +    setUpdatesEnabled(false); +    m_buttons->setUpdatesEnabled(false); +    for (index = 0; index < m_buttons->size(); index++) +    { +        pos = m_manager->pos(index); +        QuickButton *button = (*m_buttons)[index]; +        button->resize(m_manager->itemSize()); +        button->move(pos.x(), pos.y()); +        button->setDragging(false); +        button->setEnableDrag(isDragEnabled()); +        button->setDynamicModeEnabled(m_settings->autoAdjustEnabled()); +    } +    if (m_newButtons) +    { +        m_newButtons->setDragging(false); +    } +    if (m_dragButtons) +    { +        m_dragButtons->setDragging(true); +    } +    m_buttons->show(); +    setUpdatesEnabled(true); +    update(); +    m_buttons->setUpdatesEnabled(true); +    updateGeometry(); +    emit updateLayout(); +    updateStickyHighlightLayer(); +} + + +void QuickLauncher::setDragEnabled(bool enable) +{ +    m_settings->setDragEnabled(enable); +} + +void QuickLauncher::setConserveSpace(bool conserve_space) +{ +    m_manager->setConserveSpace(conserve_space); +    if (conserve_space) +    { +        m_manager->setSlack(FlowGridManager::SpaceSlack, +                            FlowGridManager::SpaceSlack); +    } +    else +    { +        m_manager->setSlack(FlowGridManager::ItemSlack, +                            FlowGridManager::ItemSlack); +    } +    refreshContents(); +} + +class SortByPopularity { +public: +    bool operator()(const QuickLauncher::PopularityInfo& a, +                   const QuickLauncher::PopularityInfo& b) +    { +        return a.popularity < b.popularity; +    } +}; + +void QuickLauncher::loadConfig() +{ +    DEBUGSTR << "QuickLauncher::loadConfig()" << endl << flush; +    //TDEConfig *c = config(); +    //c->setGroup("General"); +    setConserveSpace(m_settings->conserveSpace()); +    setDragEnabled(m_settings->dragEnabled()); +    /*DEBUGSTR << "    IconDim="<<m_iconDim << endl << flush; +    DEBUGSTR << "    ConserveSpace=" << (m_manager->conserveSpace()) <<  +        endl << flush; +    DEBUGSTR << "    DragEnabled=" << isDragEnabled() << endl << flush;*/ +    TQStringList volatileButtons = m_settings->volatileButtons(); +    TQStringList urls = m_settings->buttons(); +    if (m_settings->showDesktopEnabled()) { +        if (!urls.contains("SPECIAL_BUTTON__SHOW_DESKTOP")) +            urls.prepend("SPECIAL_BUTTON__SHOW_DESKTOP"); +    } +    else { +        if (urls.contains("SPECIAL_BUTTON__SHOW_DESKTOP")) +            urls.remove("SPECIAL_BUTTON__SHOW_DESKTOP"); +    } +    kdDebug() << "GetButtons " << urls.join("/") << endl; +    TQStringList::Iterator iter(urls.begin()); +    int n = 0; +    while (iter != urls.end()) { +        TQString url = *iter; +        addApp(url, n, false); +        ++iter; +        ++n; +    } + +    // Restore sticky state +    for (n=0; n<int(m_buttons->size()); ++n) +    { +        QuickButton* button = (*m_buttons)[n]; +        if (volatileButtons.contains(button->menuId()) == false) +        { +            button->setSticky(true); +        } +        button->setDynamicModeEnabled(m_settings->autoAdjustEnabled()); +    } + +    m_popularity->readConfig(m_settings); +    m_popularity->setHistoryHorizon(m_settings->historyHorizon()/100.0); + +    TQStringList serviceNames = m_settings->serviceNames(); +    TQValueList<int> insPos = m_settings->serviceInspos(); +    for (int n=std::min(serviceNames.size(),insPos.size())-1; n>=0; --n) +    { +        m_appOrdering[serviceNames[n]] = insPos[n]; +    } +} + +void QuickLauncher::saveConfig() +{ +    if (!m_refreshEnabled) +    { +        m_needsSave=true; +        return; +    } +    TQStringList urls, volatileUrls; +    ButtonIter iter = m_buttons->begin(); +    while (iter != m_buttons->end()) { +        if ((*iter)->sticky() == false) +        { +            volatileUrls.append((*iter)->menuId()); +        } +        urls.append((*iter)->menuId()); +        ++iter; +    } +    m_settings->setButtons(urls); +    kdDebug() << "SetButtons " << urls.join("/") << endl; +    m_settings->setVolatileButtons(volatileUrls); +    m_settings->setConserveSpace(m_manager->conserveSpace()); +    m_settings->setDragEnabled(isDragEnabled()); +     +    m_popularity->writeConfig(m_settings); +     +    // m_popularity must have written the current service list by now +    TQStringList serviceNames = m_settings->serviceNames(); +    TQValueList<int> insertionPositions; +    for (int n=0; n<int(serviceNames.size()); ++n) +    { +        if (m_appOrdering.find(serviceNames[n]) != m_appOrdering.end()) +        { +            insertionPositions.push_back(m_appOrdering[serviceNames[n]]); +        } +    } +    m_settings->setServiceInspos(insertionPositions); + +    m_settings->writeConfig(); +} + + +void QuickLauncher::setRefreshEnabled(bool enable) +{ +    m_refreshEnabled=enable; +    if (m_refreshEnabled) +    { +        if (m_needsSave) { +           saveConfig(); +        } +        if (m_needsRefresh) { +            refreshContents(); +        } +    } +} + +void QuickLauncher::serviceStartedByStorageId(TQString /*starter*/, TQString storageId) +{ +    KService::Ptr service = KService::serviceByStorageId(storageId); +    if (service->icon() == TQString::null)  +    { +        kdDebug() << storageId << " has no icon. Makes no sense to add it."; +        return; +    } +    QuickURL url = QuickURL(locate("apps", service->desktopEntryPath())); +    TQString desktopMenuId(url.menuId()); +    kdDebug() << "storageId=" << storageId << " desktopURL=" << desktopMenuId << endl; +    // A service was started somwhere else. If the quicklauncher contains +    // this service too, we flash the icon +    QuickButton *startedButton = 0; +    std::set<TQString> buttonIdSet; +    for (uint n = 0; n < m_buttons->size(); ++n) +    { +        QuickButton *button = (*m_buttons)[n]; +        TQString buttonMenuId = button->menuId(); +        buttonIdSet.insert(buttonMenuId); +        if (desktopMenuId == buttonMenuId) +        { +           kdDebug() << "QuickLauncher: I know that one: " << storageId << endl; +           button->flash(); +           startedButton = button; +        } +    } + +    // Update popularity info. +    // We do this even if autoadjust is disabled +    // so there are sane values to start with if it's turned on. +    m_popularity->useService(desktopMenuId); + +    if (m_settings->autoAdjustEnabled()) +    { +        TQTimer::singleShot(0, this, TQT_SLOT(slotAdjustToCurrentPopularity())); +    } +} + +void QuickLauncher::slotAdjustToCurrentPopularity() +{ +    // TODO: Shrink immediately if buttons->size() > maxItems +    kdDebug() << "Starting popularity update" << endl; +    PopularityStatistics* stats = m_popularity; +    int minItems = m_settings->autoAdjustMinItems(); +    int maxItems = m_settings->autoAdjustMaxItems(); + +    static const double hysteresisFactor = 0.90; +    double minAddPopularity = 0; +    for (int n = 0; n < maxItems; ++n) +    { +        // All items with a popularity not less than 0.75 of the average +        // of the first maxItems apps are included in the list +        double belowAvgAllowed = 0.75; +        minAddPopularity += (belowAvgAllowed * stats->popularityByRank(n)) / maxItems; +    } +    double minDelPopularity = minAddPopularity * hysteresisFactor; +    std::map<TQString, QuickButton*> removeableApps; +    std::set<TQString> existingApps; +    int numApps = m_buttons->size(); +    for (int n = 0; n < int(m_buttons->size()); ++n) +    { +        QuickButton *button = (*m_buttons)[n]; +        if (((stats->popularityByRank(stats->rankByService(button->menuId())) < +             minDelPopularity) || m_settings->autoAdjustEnabled()==false) &&  +            (button->sticky() == false))  +        { +            removeableApps[button->menuId()] = button; +            --numApps; +        } +        existingApps.insert(button->menuId()); +    } +    for (int n = 0;  +         (numApps < minItems && stats->popularityByRank(n) > 0) || +         (numApps < maxItems && stats->popularityByRank(n) > minAddPopularity); +         ++n) +    { +        TQString app = m_popularity->serviceByRank(n); +        if (existingApps.find(app) == existingApps.end()) +        { +            addApp(QuickURL(m_popularity->serviceByRank(n)).url(), false); +            kdDebug() << "Adding app " << app << endl; +            ++numApps; +        } +        else if (removeableApps.find(app) != removeableApps.end()) +        { +            removeableApps.erase(app); +            ++numApps; +        } +    } +    while (removeableApps.size() > 0) +    { +        removeApp(findApp(removeableApps.begin()->second), false); +        kdDebug() << "Removing app " << removeableApps.begin()->first << endl; +        removeableApps.erase(removeableApps.begin()->first); +    } +    kdDebug() << "done popularity update" << endl; +    m_settings->setAutoAdjustMinItems(minItems); +    m_settings->setAutoAdjustMaxItems(maxItems); + +    // TODO: Think of something better than that: +    m_saveTimer->start(10000,true); +} + +void QuickLauncher::slotOwnServiceExecuted(TQString serviceMenuId) +{ +    m_popularity->useService(serviceMenuId); +    if (m_settings->autoAdjustEnabled()) +    { +        TQTimer::singleShot(0, this, TQT_SLOT(slotAdjustToCurrentPopularity())); +    } +} + +void QuickLauncher::updateStickyHighlightLayer() +{ +    // Creates a transparent image which is used +    // to highlight those buttons which will never +    // be removed automatically from the launcher +    TQPixmap areaPix(width(), height()); +    TQPainter areaPixPainter(&areaPix); +    areaPixPainter.fillRect(0, 0, width(), height(), TQColor(255, 255, 255)); +    TQSize itemSize = m_manager->itemSize(); +    TQSize spaceSize = m_manager->spaceSize(); +    for (uint n=0; n<m_buttons->size(); ++n) +    { +        TQPoint pos = m_manager->pos(n); +        if ((*m_buttons)[n]->sticky() == false) +        { +            areaPixPainter.fillRect(pos.x()-(spaceSize.width()+1)/2,  +                                    pos.y()-(spaceSize.height()+1)/2,  +                                    itemSize.width()+spaceSize.width()+1,  +                                    itemSize.height()+spaceSize.height()+1,  +                                    TQColor(0, 0, 0)); +        } +    } +    TQImage areaLayer = areaPix.convertToImage(); +    m_stickyHighlightLayer = TQImage(width(), height(), 32); +    m_stickyHighlightLayer.setAlphaBuffer(true); +    int pix, tlPix, brPix, w(width()), h(height()); +    QRgb transparent(tqRgba(0, 0, 0, 0)); +    for (int y = h-1; y >= 0; --y) +    { +        for (int x = w-1; x >= 0; --x) +        { +            pix = tqRed(areaLayer.pixel(x, y)); +            if (pix == 0) +            { +                tlPix = (y>0 && x>0) ? tqRed(areaLayer.pixel(x-1,y-1)) : 255; +                brPix = (y<h-1 && x<w-1) ? tqRed(areaLayer.pixel(x+1,y+1)) : 255; +                int c = tlPix-brPix < 0 ? 255 : 0; +                int alpha = abs(tlPix-brPix)/2; +                m_stickyHighlightLayer.setPixel(x, y, tqRgba(c, c, c, alpha)); +            } +            else  +            { +                m_stickyHighlightLayer.setPixel(x, y, transparent); +            } +        } +    } +    repaint(); +} + +void QuickLauncher::paintEvent(TQPaintEvent* e) +{ +    KPanelApplet::paintEvent(e); + +    if (m_settings->autoAdjustEnabled() &&  +        m_settings->showVolatileButtonIndicator()) +    { +        TQPainter p(this); +        p.drawImage(0, 0, m_stickyHighlightLayer); +    } +} + +void QuickLauncher::slotStickyToggled() +{ +    updateStickyHighlightLayer(); +    saveConfig(); +} + +void QuickLauncher::positionChange(Position) +{ +    for (int n=0; n<int(m_buttons->size()); ++n) +    { +        (*m_buttons)[n]->setPopupDirection(popupDirection()); +    } +} + + +#include "quicklauncher.moc"  | 
