示例#1
0
    void addRecentApplication(KService::Ptr service, bool append) {
        // remove existing item if any
        if (removeExistingItem(service->entryPath())) {
            --recentApplicationCount;
        }

        QStandardItem *appItem = StandardItemFactory::createItemForService(service, displayOrder);
        appItem->setData(applicationTitle, Kickoff::GroupNameRole);
        itemsByPath.insert(service->entryPath(), appItem);

        if (append) {
            q->insertRow(recentApplicationCount, appItem);
        } else {
            q->insertRow(0, appItem);
        }
        ++recentApplicationCount;

        while (recentApplicationCount > maxRecentApps) {
            --recentApplicationCount;
            QList<QStandardItem*> row = q->takeRow(recentApplicationCount);

            //don't leave pending stuff in itemsByPath
            if (!row.isEmpty()) {
                itemsByPath.remove(row.first()->data(UrlRole).toString());
                qDeleteAll(row.begin(), row.end());
            }
        }
    }
示例#2
0
void KMimeTypeTest::testPreferredService()
{
    // The "NotShowIn=KDE" service should not be the preferred one!
    KService::Ptr serv = KMimeTypeTrader::self()->preferredService("text/plain");
    QVERIFY(serv);
    qDebug() << serv->entryPath();
    QVERIFY(serv->entryPath() != m_nonKdeApp);
    QCOMPARE(serv->entryPath(), m_textPlainApp);
}
示例#3
0
    void addRecentApplication(KService::Ptr service,bool append)
    {
        // remove existing item if any
        removeExistingItem(service->entryPath());

        QStandardItem *appItem = StandardItemFactory::createItemForService(service);
        itemsByPath.insert(service->entryPath(),appItem);

        if (append) {
            recentAppItem->appendRow(appItem);
        } else {
            recentAppItem->insertRow(0,appItem);
        }
    }
示例#4
0
void KateFileTree::slotFixOpenWithMenu()
{
  QMenu *menu = (QMenu*)sender();
  menu->clear();
  
   KTextEditor::Document *doc = model()->data(m_indexContextMenu, KateFileTreeModel::DocumentRole).value<KTextEditor::Document *>();
  if (!doc) return;

  // get a list of appropriate services.
  KMimeType::Ptr mime = KMimeType::mimeType(doc->mimeType());
  //kDebug(13001) << "mime type: " << mime->name();

  QAction *a = 0;
  KService::List offers = KMimeTypeTrader::self()->query(mime->name(), "Application");
  // for each one, insert a menu item...
  for(KService::List::Iterator it = offers.begin(); it != offers.end(); ++it)
  {
    KService::Ptr service = *it;
    if (service->name() == "Kate") continue;
    a = menu->addAction(KIcon(service->icon()), service->name());
    a->setData(service->entryPath());
  }
  // append "Other..." to call the KDE "open with" dialog.
  a = menu->addAction(i18n("&Other..."));
  a->setData(QString());
}
示例#5
0
KPluginInfo::KPluginInfo( const KService::Ptr service )
: d( new KPluginInfoPrivate )
{
    if (!service) {
        d = 0; // isValid() == false
        return;
    }
    d->service = service;
    d->entryPath = service->entryPath();

    if ( service->isDeleted() )
    {
        d->hidden = true;
        return;
    }

    d->name = service->name();
    d->comment = service->comment();
    d->icon = service->icon();
    d->author = service->property( QLatin1String("X-KDE-PluginInfo-Author") ).toString();
    d->email = service->property( QLatin1String("X-KDE-PluginInfo-Email") ).toString();
    d->pluginName = service->property( QLatin1String("X-KDE-PluginInfo-Name") ).toString();
    d->version = service->property( QLatin1String("X-KDE-PluginInfo-Version") ).toString();
    d->website = service->property( QLatin1String("X-KDE-PluginInfo-Website") ).toString();
    d->category = service->property( QLatin1String("X-KDE-PluginInfo-Category") ).toString();
    d->license = service->property( QLatin1String("X-KDE-PluginInfo-License") ).toString();
    d->dependencies =
        service->property( QLatin1String("X-KDE-PluginInfo-Depends") ).toStringList();
    QVariant tmp = service->property( QLatin1String("X-KDE-PluginInfo-EnabledByDefault") );
    d->enabledbydefault = tmp.isValid() ? tmp.toBool() : false;
}
示例#6
0
bool KonqView::changePart(const QString &mimeType,
                          const QString &serviceName,
                          bool forceAutoEmbed)
{
    // Caller should call stop first.
    assert( !m_bLoading );

    //kDebug() << "mimeType=" << mimeType
    //             << "requested serviceName=" << serviceName
    //             << "current service name=" << m_service->desktopEntryName();

    if (serviceName == m_service->desktopEntryName()) {
        m_serviceType = mimeType;
        return true;
    }

    if (isLockedViewMode()) {
        //kDebug() << "This view's mode is locked - can't change";
        return false; // we can't do that if our view mode is locked
    }

    KService::List partServiceOffers, appServiceOffers;
    KService::Ptr service;
    KonqFactory konqFactory;
    KonqViewFactory viewFactory = konqFactory.createView( mimeType, serviceName, &service, &partServiceOffers, &appServiceOffers, forceAutoEmbed );

  if ( viewFactory.isNull() )
  {
    // Revert location bar's URL to the working one
    if(currentHistoryEntry())
      setLocationBarURL( currentHistoryEntry()->locationBarURL );
    return false;
  }

  m_serviceType = mimeType;
  m_partServiceOffers = partServiceOffers;
  m_appServiceOffers = appServiceOffers;

  // Check if that's already the kind of part we have -> no need to recreate it
  // Note: we should have an operator== for KService...
  if ( m_service && m_service->entryPath() == service->entryPath() )
  {
    kDebug() << "Reusing service. Service type set to" << m_serviceType;
    if (  m_pMainWindow->currentView() == this )
      m_pMainWindow->updateViewModeActions();
  }
  else
  {
    m_service = service;

    switchView( viewFactory );
  }

  return true;
}
示例#7
0
void ApplicationsProtocol::get( const KUrl & url )
{
    KService::Ptr service = KService::serviceByDesktopName(url.fileName());
    if (service && service->isValid()) {
        KUrl redirUrl(KStandardDirs::locate("apps", service->entryPath()));
        redirection(redirUrl);
        finished();
    } else {
        error( KIO::ERR_IS_DIRECTORY, url.prettyUrl() );
    }
}
示例#8
0
QMimeData * KRunnerModel::mimeData(const QModelIndexList &indexes) const
{
    KUrl::List urls;

    foreach (const QModelIndex & index, indexes) {
        KUrl url = data(index, CommonModel::Url).toString();

        KService::Ptr service = serviceForUrl(url);

        if (service) {
            urls << KUrl(service->entryPath());
        }
    }
示例#9
0
static void createFileEntry(KIO::UDSEntry& entry, const KService::Ptr& service, const KUrl& parentUrl)
{
    entry.clear();
    entry.insert(KIO::UDSEntry::UDS_NAME, KIO::encodeFileName(service->name()));
    entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG);
    const QString fileUrl = parentUrl.url(KUrl::AddTrailingSlash) + service->desktopEntryName();
    entry.insert(KIO::UDSEntry::UDS_URL, fileUrl);
    entry.insert(KIO::UDSEntry::UDS_ACCESS, 0500);
    entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, "application/x-desktop");
    entry.insert(KIO::UDSEntry::UDS_SIZE, 0);
    entry.insert(KIO::UDSEntry::UDS_LOCAL_PATH, KStandardDirs::locate("apps", service->entryPath()));
    entry.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME, time(0));
    entry.insert(KIO::UDSEntry::UDS_ICON_NAME, service->icon());
}
void RecordItNowPluginManager::loadInfos(const QString &type)
{

    KConfig cfg("recorditnowrc");
    KConfigGroup pCfg(&cfg, "Plugins");

    KService::List offers = KServiceTypeTrader::self()->query(type);
    KService::List::const_iterator iter;

    for (iter = offers.begin(); iter < offers.end(); ++iter) {
        KService::Ptr service = *iter;
        KPluginInfo info(service);

        if (!info.isValid()) {
            kWarning() << "invalid plugin info:" << service->entryPath();
            continue;
        } else {
            info.setConfig(pCfg);
            info.load(pCfg);
            m_plugins[info] = 0;
        }
    }

}
示例#11
0
/*
* 接受isoftappdaemon finished 信号
* 1.更新所有包列表中对应包的状态
* 2.给每个页面发送taskfinished信号
* 3.更新taskqueue队列:
*   a、删除当前任务
*   b、开始新任务
*/
void JadedBus::getFinished(const QString &pkgName,qlonglong status)
{
    if (status != STATUS_REMOVED &&
        status != STATUS_UPDATED &&
        status != STATUS_INSTALLED &&
        status != STATUS_INSTALL &&
        status != STATUS_UPGRADED &&
        status != STATUS_INSTALL_ERROR) {
        return;
    }

    if(pkgName.isEmpty()) {
        return;
    }

    int i =0;
    for (i = 0; i < AllPkgList.size(); ++i) {
        if (AllPkgList.at(i).pkgName  == pkgName) {
            if (status == STATUS_INSTALLED) {
                if (AllPkgList[i].status != 1) {
                    AllPkgList[i].status = 1;
                    QDateTime local(QDateTime::currentDateTime());
                    AllPkgList[i].datetime = local.toString("yyyy-MM-dd hh:mm:ss");

                    QString desktopName = m_isoftapp->GetDesktopName(pkgName).value();
                    if (!desktopName.isEmpty())
                        desktopName = desktopName.left(desktopName.size() - 8);
                    KService::Ptr service = KService::serviceByDesktopName(desktopName);
                    if (!service) {
                        service = KService::serviceByDesktopName(pkgName);
                    }
                    if (service) {
                        if (!service->exec().isEmpty() && !service->noDisplay()) {
                            QFile::link(service->entryPath(),
                                QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) +
                                "/" + service->name() + ".desktop");
                        }
                    }
                }

            } else if (status == STATUS_REMOVED) {
                if (AllPkgList[i].status != 2) {
                    AllPkgList[i].status = 2;
                    QDateTime local(QDateTime::currentDateTime());
                    AllPkgList[i].datetime = local.toString("yyyy-MM-dd hh:mm:ss");
                }
            } else if ( status == STATUS_INSTALL_ERROR) {
                if (AllPkgList[i].status != 2) {
                    AllPkgList[i].status = 2;
                }
                QString details ="insatallfailed";
                m_errored(pkgName,details);
            }
            getMyPkgNumber();

            break;
        }
    }

    taskFinished(pkgName);

    for (i = 0; i < m_taskQueue.size(); i++) {
        if (m_taskQueue[i].status == "doing" &&
            pkgName == m_taskQueue[i].name) {
            if (m_taskQueue[i].action == "update") {
                int t = (int)time(NULL);
                m_updateInfo.replace(pkgName,QString::number(t, 10));
                //getUpdate() ;
            }
            printf("trace:%s,%d,name[%s],index[%d][%s] task is finished.\n",__FUNCTION__,__LINE__,
                   qPrintable(m_taskQueue[i].name),i,qPrintable(m_taskQueue[i].action));
            m_taskQueue[i].status = "done";
            g_doingPkgName = "";
            m_taskQueue.removeFirst();
            m_runTask();
            return;
        }
    }

    return;
}
示例#12
0
QVariant AppsModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid() || index.row() >= m_entryList.count()) {
        return QVariant();
    }

    const AbstractEntry *entry = m_entryList.at(index.row());

    if (role == Qt::DisplayRole) {
        return entry->name();
    } else if (role == Qt::DecorationRole) {
        return entry->icon();
    } else if (role == Kicker::IsParentRole) {
        return (entry->type() == AbstractEntry::GroupType);
    } else if (role == Kicker::HasChildrenRole) {
        if (entry->type() == AbstractEntry::GroupType) {
            const AbstractGroupEntry *groupEntry = static_cast<const AbstractGroupEntry *>(entry);

            if (groupEntry->model() && groupEntry->model()->count()) {
                return true;
            }
        }
    } else if (role == Kicker::FavoriteIdRole) {
        if (entry->type() == AbstractEntry::RunnableType) {
            return QVariant("app:" + static_cast<AppEntry *>(m_entryList.at(index.row()))->service()->storageId());
        }
    } else if (role == Kicker::HasActionListRole) {
        if (entry->type() == AbstractEntry::RunnableType || !m_hiddenEntries.isEmpty()) {
            return true;
        } else if (entry->type() == AbstractEntry::GroupType) {
            const AbstractGroupEntry *groupEntry = static_cast<const AbstractGroupEntry *>(entry);

            if (groupEntry->model()) {
                const AppsModel *appsModel = qobject_cast<const AppsModel *>(groupEntry->model());

                if (appsModel && !appsModel->hiddenEntries().isEmpty()) {
                    return true;
                }
            }
        }
    } else if (role == Kicker::ActionListRole) {
        QVariantList actionList;

        if (entry->type() == AbstractEntry::RunnableType) {
            const KService::Ptr service = static_cast<const AppEntry *>(entry)->service();

            if (ContainmentInterface::mayAddLauncher(m_appletInterface, ContainmentInterface::Desktop)) {
                actionList << Kicker::createActionItem(i18n("Add to Desktop"), "addToDesktop");
            }

            if (ContainmentInterface::mayAddLauncher(m_appletInterface, ContainmentInterface::Panel)) {
                actionList << Kicker::createActionItem(i18n("Add to Panel"), "addToPanel");
            }

            if (ContainmentInterface::mayAddLauncher(m_appletInterface, ContainmentInterface::TaskManager, service->entryPath())) {
                actionList << Kicker::createActionItem(i18n("Add as Launcher"), "addToTaskManager");
            }

            if (m_menuEntryEditor->canEdit(service->entryPath())) {
                actionList << Kicker::createSeparatorActionItem();

                QVariantMap editAction = Kicker::createActionItem(i18n("Edit Application..."), "editApplication");
                editAction["icon"] = "kmenuedit"; // TODO: Using the KMenuEdit icon might be misleading.
                actionList << editAction;
            }

#ifdef PackageKitQt5_FOUND
            QStringList files(service->entryPath());

            if (service->isApplication()) {
                files += QStandardPaths::findExecutable(KShell::splitArgs(service->exec()).first());
            }

            FindPackageJob* job = new FindPackageJob(files); // TODO: Would be great to make this async.

            if (job->exec() && !job->packageNames().isEmpty()) {
                QString packageName = job->packageNames().first();

                QVariantMap removeAction = Kicker::createActionItem(i18n("Remove '%1'...", packageName), "removeApplication", packageName);
                removeAction["icon"] = "applications-other";
                actionList << removeAction;
            }
#endif

            if (appletConfig() && appletConfig()->contains("hiddenApplications")) {
                const QStringList &hiddenApps = appletConfig()->value("hiddenApplications").toStringList();

                if (!hiddenApps.contains(service->menuId())) {
                    actionList << Kicker::createActionItem(i18n("Hide Application"), "hideApplication");
                }
            }
        }

        if (!m_hiddenEntries.isEmpty()) {
            actionList << Kicker::createSeparatorActionItem();
            actionList << Kicker::createActionItem(i18n("Unhide Applications in this Submenu"), "unhideSiblingApplications");
        }

        if (entry->type() == AbstractEntry::GroupType) {
            const AbstractGroupEntry *groupEntry = static_cast<const AbstractGroupEntry *>(entry);

            if (groupEntry->model()) {
                const AppsModel *appsModel = qobject_cast<const AppsModel *>(groupEntry->model());

                if (appsModel && !appsModel->hiddenEntries().isEmpty()) {
                    actionList << Kicker::createActionItem(i18n("Unhide Applications in '%1'", entry->name()), "unhideChildApplications");
                }
            }
        }

        return actionList;
    } else if (role == Kicker::UrlRole) {
        if (entry->type() == AbstractEntry::RunnableType) {
            return QUrl::fromLocalFile(static_cast<AppEntry *>(m_entryList.at(index.row()))->service()->entryPath());
        }
    }

    return QVariant();
}
示例#13
0
void KateProjectTreeViewContextMenu::exec(const QString &filename, const QPoint &pos, QWidget *parent)
{
    /**
     * create context menu
     */
    QMenu menu;

    QAction *copyAction = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("Copy Filename"));

    /**
     * handle "open with"
     * find correct mimetype to query for possible applications
     */
    QMenu *openWithMenu = menu.addMenu(i18n("Open With"));
    QMimeType mimeType = QMimeDatabase().mimeTypeForFile(filename);
    KService::List offers = KMimeTypeTrader::self()->query(mimeType.name(), QStringLiteral("Application"));

    /**
     * for each one, insert a menu item...
     */
    for (KService::List::Iterator it = offers.begin(); it != offers.end(); ++it) {
        KService::Ptr service = *it;
        if (service->name() == QStringLiteral("Kate")) {
            continue;    // omit Kate
        }
        QAction *action = openWithMenu->addAction(QIcon::fromTheme(service->icon()), service->name());
        action->setData(service->entryPath());
    }

    /**
     * perhaps disable menu, if no entries!
     */
    openWithMenu->setEnabled(!openWithMenu->isEmpty());

    KMoreToolsMenuFactory menuFactory(QLatin1String("kate/addons/project/git-tools"));

    QMenu gitMenu; // must live as long as the maybe filled menu items should live

    if (isGit(filename)) {

        menuFactory.fillMenuFromGroupingNames(&gitMenu, { QLatin1String("git-clients-and-actions") },
                                                               QUrl::fromLocalFile(filename));

        menu.addSection(i18n("Git:"));
        Q_FOREACH(auto action, gitMenu.actions()) {
            menu.addAction(action);
        }
    }

    /**
     * run menu and handle the triggered action
     */
    if (QAction *action = menu.exec(pos)) {

        // handle apps
        if (copyAction == action) {
            QApplication::clipboard()->setText(filename);
        } else {
            // handle "open with"
            const QString openWith = action->data().toString();
            if (KService::Ptr app = KService::serviceByDesktopPath(openWith)) {
                QList<QUrl> list;
                list << QUrl::fromLocalFile(filename);
                KRun::runService(*app, list, parent);
            }
        }
    }
}
示例#14
0
void
KSycocaDict::save(QDataStream &str)
{
   if (count() == 0)
   {
      d->hashTableSize = 0;
      d->hashList.clear();
      str << d->hashTableSize;
      str << d->hashList;
      return;
   }

   d->offset = str.device()->pos();

   //qDebug() << "KSycocaDict:" << count() << "entries.";

   //qDebug() << "Calculating hash keys..";

   int maxLength = 0;
   //qDebug() << "Finding maximum string length";
   for(KSycocaDictStringList::const_iterator it = d->stringlist->constBegin(); it != d->stringlist->constEnd(); ++it)
   {
      string_entry* entry = *it;
      entry->hash = 0;
      if (entry->length > maxLength)
         maxLength = entry->length;
   }

   //qDebug() << "Max string length=" << maxLength << "existing hashList=" << d->hashList;

   // use "almost prime" number for sz (to calculate diversity) and later
   // for the table size of big tables
   // int sz = d->stringlist->count()*5-1;
   register unsigned int sz = count()*4 + 1;
   while(!(((sz % 3) && (sz % 5) && (sz % 7) && (sz % 11) && (sz % 13))))
      sz+=2;

   d->hashList.clear();

   // Times (with warm caches, i.e. after multiple runs)
   // kbuildsycoca5 --noincremental  2.83s user 0.20s system 95% cpu 3.187 total
   // kbuildsycoca5 --noincremental  2.74s user 0.25s system 93% cpu 3.205 total
   // unittest: 0.50-60 msec per iteration / 0.40-50 msec per iteration

   // Now that MimeTypes are not parsed anymore:
   // kbuildsycoca5 --noincremental  2.18s user 0.30s system 91% cpu 2.719 total
   // kbuildsycoca5 --noincremental  2.07s user 0.34s system 89% cpu 2.681 total

   // If I enabled s_maxItems = 50, it goes down to
   // but I don't know if that's a good idea.
   // kbuildsycoca5 --noincremental  1.73s user 0.31s system 85% cpu 2.397 total
   // kbuildsycoca5 --noincremental  1.84s user 0.29s system 95% cpu 2.230 total

   // try to limit diversity scan by "predicting" positions
   // with high diversity
   QVector<int> oldvec(maxLength*2+1);
   oldvec.fill(0);
   int mindiv=0;
   int lastDiv = 0;

   while(true)
   {
      int divsum=0,divnum=0;

      int maxDiv = 0;
      int maxPos = 0;
      for (int pos = -maxLength; pos <= maxLength; ++pos) {
         // cut off
         if (oldvec[pos+maxLength] < mindiv) { oldvec[pos+maxLength]=0; continue; }

         const int diversity = calcDiversity(d->stringlist, pos, sz);
         if (diversity > maxDiv) {
            maxDiv = diversity;
            maxPos = pos;
         }
         oldvec[pos + maxLength] = diversity;
         divsum += diversity;
         ++divnum;
      }
      // arbitrary cut-off value 3/4 of average seems to work
      if (divnum)
         mindiv=(3*divsum)/(4*divnum);

      if (maxDiv <= lastDiv)
         break;
      //qDebug() << "Max Div=" << maxDiv << "at pos" << maxPos;
      lastDiv = maxDiv;
      addDiversity(d->stringlist, maxPos);
      d->hashList.append(maxPos);
   }


   for(KSycocaDictStringList::Iterator it = d->stringlist->begin(); it != d->stringlist->end(); ++it) {
      (*it)->hash = d->hashKey((*it)->keyStr);
   }
// fprintf(stderr, "Calculating minimum table size..\n");

   d->hashTableSize = sz;

   //qDebug() << "hashTableSize=" << sz << "hashList=" << d->hashList << "oldvec=" << oldvec;

   struct hashtable_entry {
      string_entry *entry;
      QList<string_entry*>* duplicates;
      qint64 duplicate_offset;
   };

   hashtable_entry *hashTable = new hashtable_entry[ sz ];

   //qDebug() << "Clearing hashtable...";
   for (unsigned int i=0; i < sz; i++)
   {
      hashTable[i].entry = 0;
      hashTable[i].duplicates = 0;
   }

   //qDebug() << "Filling hashtable...";
   for(KSycocaDictStringList::const_iterator it = d->stringlist->constBegin(); it != d->stringlist->constEnd(); ++it)
   {
      string_entry* entry = *it;
      //qDebug() << "entry keyStr=" << entry->keyStr << entry->payload.data() << entry->payload->entryPath();
      int hash = entry->hash % sz;
      if (!hashTable[hash].entry)
      { // First entry
         hashTable[hash].entry = entry;
      }
      else
      {
         if (!hashTable[hash].duplicates)
         { // Second entry, build duplicate list.
            hashTable[hash].duplicates = new QList<string_entry*>;
            hashTable[hash].duplicates->append(hashTable[hash].entry);
            hashTable[hash].duplicate_offset = 0;
         }
         hashTable[hash].duplicates->append(entry);
      }
   }

   str << d->hashTableSize;
   str << d->hashList;

   d->offset = str.device()->pos(); // d->offset points to start of hashTable
   //qDebug() << QString("Start of Hash Table, offset = %1").arg(d->offset,8,16);

   // Write the hashtable + the duplicates twice.
   // The duplicates are after the normal hashtable, but the offset of each
   // duplicate entry is written into the normal hashtable.
   for(int pass = 1; pass <= 2; pass++)
   {
      str.device()->seek(d->offset);
      //qDebug() << QString("Writing hash table (pass #%1)").arg(pass);
      for(uint i=0; i < d->hashTableSize; i++)
      {
         qint32 tmpid;
         if (!hashTable[i].entry)
            tmpid = (qint32) 0;
         else if (!hashTable[i].duplicates)
            tmpid = (qint32) hashTable[i].entry->payload->offset(); // Positive ID
         else
            tmpid = (qint32) -hashTable[i].duplicate_offset; // Negative ID
         str << tmpid;
         //qDebug() << QString("Hash table : %1").arg(tmpid,8,16);
      }
      //qDebug() << QString("End of Hash Table, offset = %1").arg(str.device()->at(),8,16);

      //qDebug() << QString("Writing duplicate lists (pass #%1)").arg(pass);
      for(uint i=0; i < d->hashTableSize; i++)
      {
         const QList<string_entry*> *dups = hashTable[i].duplicates;
         if (dups)
         {
            hashTable[i].duplicate_offset = str.device()->pos();

            /*qDebug() << QString("Duplicate lists: Offset = %1 list_size = %2")                           .arg(hashTable[i].duplicate_offset,8,16).arg(dups->count());
*/
        for(QList<string_entry*>::ConstIterator dup = dups->begin(); dup != dups->end(); ++dup)
            {
               const qint32 offset = (*dup)->payload->offset();
               if (!offset) {
                   const QString storageId = (*dup)->payload->storageId();
                   qDebug() << "about to assert! dict=" << this << "storageId=" << storageId << (*dup)->payload.data();
                   if ((*dup)->payload->isType(KST_KService)) {
                       KService::Ptr service = KService::Ptr::staticCast((*dup)->payload);
                       qDebug() << service->storageId() << service->entryPath();
                   }
                   // save() must have been called on the entry
                   Q_ASSERT_X( offset, "KSycocaDict::save",
                               QByteArray("entry offset is 0, save() was not called on "
                               + (*dup)->payload->storageId().toLatin1()
                               + " entryPath="
                               + (*dup)->payload->entryPath().toLatin1())
                       );
               }
               str << offset ;                       // Positive ID
               str << (*dup)->keyStr;                // Key (QString)
            }
            str << (qint32) 0;               // End of list marker (0)
         }
      }
      //qDebug() << QString("End of Dict, offset = %1").arg(str.device()->at(),8,16);
   }

   //qDebug() << "Cleaning up hash table.";
   for(uint i=0; i < d->hashTableSize; i++)
   {
      delete hashTable[i].duplicates;
   }
   delete [] hashTable;
}