summaryrefslogtreecommitdiffstats
path: root/kio/kio/kuserprofile.cpp
blob: fe07c3ad1ee9a5f1f94e10d7114db5c60d226add (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
/*  This file is part of the KDE libraries
 *  Copyright (C) 1999 Torben Weis <weis@kde.org>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License version 2 as published by the Free Software Foundation;
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 **/

#include "kuserprofile.h"
#include "kservice.h"
#include "kservicetype.h"
#include "kservicetypefactory.h"

#include <kconfig.h>
#include <kapplication.h>
#include <kglobal.h>
#include <kdebug.h>
#include <kstaticdeleter.h>

#include <tqtl.h>

template class TQPtrList<KServiceTypeProfile>;
typedef TQPtrList<KServiceTypeProfile> KServiceTypeProfileList;

/*********************************************
 *
 * KServiceTypeProfile
 *
 *********************************************/

KServiceTypeProfileList* KServiceTypeProfile::s_lstProfiles = 0L;
static KStaticDeleter< KServiceTypeProfileList > profileDeleter;
bool KServiceTypeProfile::s_configurationMode = false;

void KServiceTypeProfile::initStatic()
{
  if ( s_lstProfiles )
    return;

  // Make sure that a KServiceTypeFactory gets created.
  (void) KServiceTypeFactory::self();

  profileDeleter.setObject(s_lstProfiles, new KServiceTypeProfileList);
  s_lstProfiles->setAutoDelete( true );

  KConfig config( "profilerc", true, false);

  static const TQString & defaultGroup = TDEGlobal::staticQString("<default>");

  TQStringList tmpList = config.groupList();
  for (TQStringList::Iterator aIt = tmpList.begin();
       aIt != tmpList.end(); ++aIt) {
    if ( *aIt == defaultGroup )
      continue;

    config.setGroup( *aIt );

    TQString appId = config.readEntry( "Application" );

    KService::Ptr pService = KService::serviceByStorageId(appId);

    if ( pService ) {
      TQString application = pService->storageId();
      TQString type = config.readEntry( "ServiceType" );
      TQString type2 = config.readEntry( "GenericServiceType" );
      if (type2.isEmpty()) // compat code
          type2 = (pService->type() == "Application") ? "Application" : "KParts/ReadOnlyPart";
      int pref = config.readNumEntry( "Preference" );

      if ( !type.isEmpty() /* && pref >= 0*/ ) // Don't test for pref here. We want those in the list, to mark them as forbidden
      {
        KServiceTypeProfile* p =
          KServiceTypeProfile::serviceTypeProfile( type, type2 );

        if ( !p ) {
          p = new KServiceTypeProfile( type, type2 );
          s_lstProfiles->append( p );
        }

        bool allow = config.readBoolEntry( "AllowAsDefault" );
        //kdDebug(7014) << "KServiceTypeProfile::initStatic adding service " << application << " to profile for " << type << "," << type2 << " with preference " << pref << endl;
        p->addService( application, pref, allow );
      }
    }
  }
}

//static
void KServiceTypeProfile::clear()
{
    // HACK ksycoca may open the dummy db, in such case the first call to ksycoca
    // in initStatic() leads to closing the dummy db and clear() being called
    // in the middle of it, making s_lstProfiles be NULL
    if( s_lstProfiles == NULL || s_lstProfiles->count() == 0 )
        return;
    profileDeleter.destructObject();
}

//static
KServiceTypeProfile::OfferList KServiceTypeProfile::offers( const TQString& _servicetype, const TQString& _genericServiceType )
{
    OfferList offers;
    TQStringList serviceList;
    //kdDebug(7014) << "KServiceTypeProfile::offers( " << _servicetype << "," << _genericServiceType << " )" << endl;

    // Note that KServiceTypeProfile::offers() calls KServiceType::offers(),
    // so we _do_ get the new services, that are available but not in the profile.
    if ( _genericServiceType.isEmpty() )
    {
        initStatic();
        // We want all profiles for servicetype, if we have profiles.
        // ## Slow loop, if profilerc is big. We should use a map instead?
        TQPtrListIterator<KServiceTypeProfile> it( *s_lstProfiles );
        for( ; it.current(); ++it )
            if ( it.current()->m_strServiceType == _servicetype )
            {
                offers += it.current()->offers();
            }
        //kdDebug(7014) << "Found profile: " << offers.count() << " offers" << endl;
    }
    else
    {
        KServiceTypeProfile* profile = serviceTypeProfile( _servicetype, _genericServiceType );
        if ( profile )
        {
            //kdDebug(7014) << "Found profile: " << profile->offers().count() << " offers" << endl;
            offers += profile->offers();
        }
        else
        {
            // Try the other way round, order is not like size, it doesn't matter.
            profile = serviceTypeProfile( _genericServiceType, _servicetype );
            if ( profile )
            {
                //kdDebug(7014) << "Found profile after switching: " << profile->offers().count() << " offers" << endl;
                offers += profile->offers();
            }
        }
    }

    // Collect services, to make the next loop faster
    OfferList::Iterator itOffers = offers.begin();
    for( ; itOffers != offers.end(); ++itOffers )
        serviceList += (*itOffers).service()->desktopEntryPath(); // this should identify each service uniquely
    //kdDebug(7014) << "serviceList: " << serviceList.join(",") << endl;

    // Now complete with any other offers that aren't in the profile
    // This can be because the services have been installed after the profile was written,
    // but it's also the case for any service that's neither App nor ReadOnlyPart, e.g. RenameDlg/Plugin
    KService::List list = KServiceType::offers( _servicetype );
    //kdDebug(7014) << "Using KServiceType::offers, result: " << list.count() << " offers" << endl;
    TQValueListIterator<KService::Ptr> it = list.begin();
    for( ; it != list.end(); ++it )
    {
        if (_genericServiceType.isEmpty() /*no constraint*/ || (*it)->hasServiceType( _genericServiceType ))
        {
            // Check that we don't already have it ;)
            if ( serviceList.find( (*it)->desktopEntryPath() ) == serviceList.end() )
            {
                bool allow = (*it)->allowAsDefault();
                KServiceOffer o( (*it), (*it)->initialPreferenceForMimeType(_servicetype), allow );
                offers.append( o );
                //kdDebug(7014) << "Appending offer " << (*it)->name() << " initial preference=" << (*it)->initialPreference() << " allow-as-default=" << allow << endl;
            }
            //else
            //    kdDebug(7014) << "Already having offer " << (*it)->name() << endl;
        }
    }

    qBubbleSort( offers );

#if 0
    // debug code, comment if you wish but don't remove.
    kdDebug(7014) << "Sorted list:" << endl;
    OfferList::Iterator itOff = offers.begin();
    for( ; itOff != offers.end(); ++itOff )
        kdDebug(7014) << (*itOff).service()->name() << " allow-as-default=" << (*itOff).allowAsDefault() << endl;
#endif

    //kdDebug(7014) << "Returning " << offers.count() << " offers" << endl;
    return offers;
}

KServiceTypeProfile::KServiceTypeProfile( const TQString& _servicetype, const TQString& _genericServiceType )
{
  initStatic();

  m_strServiceType = _servicetype;
  m_strGenericServiceType = _genericServiceType;
}

KServiceTypeProfile::~KServiceTypeProfile()
{
}

void KServiceTypeProfile::addService( const TQString& _service,
				      int _preference, bool _allow_as_default )
{
  m_mapServices[ _service ].m_iPreference = _preference;
  m_mapServices[ _service ].m_bAllowAsDefault = _allow_as_default;
}

int KServiceTypeProfile::preference( const TQString& _service ) const
{
  KService::Ptr service = KService::serviceByName( _service );
  if (!service)
    return 0;
  TQMap<TQString,Service>::ConstIterator it = m_mapServices.find( service->storageId() );
  if ( it == m_mapServices.end() )
    return 0;

  return it.data().m_iPreference;
}

bool KServiceTypeProfile::allowAsDefault( const TQString& _service ) const
{
  KService::Ptr service = KService::serviceByName( _service );
  if (!service)
    return false;

  // Does the service itself not allow that ?
  if ( !service->allowAsDefault() )
    return false;

  // Look what the user says ...
  TQMap<TQString,Service>::ConstIterator it = m_mapServices.find( service->storageId() );
  if ( it == m_mapServices.end() )
    return 0;

  return it.data().m_bAllowAsDefault;
}

KServiceTypeProfile* KServiceTypeProfile::serviceTypeProfile( const TQString& _servicetype, const TQString& _genericServiceType )
{
  initStatic();
  static const TQString& app_str = TDEGlobal::staticQString("Application");

  const TQString &_genservicetype  = ((!_genericServiceType.isEmpty()) ? _genericServiceType : app_str);

  TQPtrListIterator<KServiceTypeProfile> it( *s_lstProfiles );
  for( ; it.current(); ++it )
    if (( it.current()->m_strServiceType == _servicetype ) &&
        ( it.current()->m_strGenericServiceType == _genservicetype))
      return it.current();

  return 0;
}


KServiceTypeProfile::OfferList KServiceTypeProfile::offers() const
{
  OfferList offers;

  kdDebug(7014) << "KServiceTypeProfile::offers serviceType=" << m_strServiceType << " genericServiceType=" << m_strGenericServiceType << endl;
  KService::List list = KServiceType::offers( m_strServiceType );
  TQValueListIterator<KService::Ptr> it = list.begin();
  for( ; it != list.end(); ++it )
  {
    //kdDebug(7014) << "KServiceTypeProfile::offers considering " << (*it)->name() << endl;
    if ( m_strGenericServiceType.isEmpty() || (*it)->hasServiceType( m_strGenericServiceType ) )
    {
      // Now look into the profile, to find this service's preference.
      TQMap<TQString,Service>::ConstIterator it2 = m_mapServices.find( (*it)->storageId() );

      if( it2 != m_mapServices.end() )
      {
        //kdDebug(7014) << "found in mapServices pref=" << it2.data().m_iPreference << endl;
        if ( it2.data().m_iPreference > 0 ) {
          bool allow = (*it)->allowAsDefault();
          if ( allow )
            allow = it2.data().m_bAllowAsDefault;
          KServiceOffer o( (*it), it2.data().m_iPreference, allow );
          offers.append( o );
        }
      }
      else
      {
        //kdDebug(7014) << "not found in mapServices. Appending." << endl;
        // We use 0 as the preference to ensure new apps don't take over existing apps (which default to 1)
        KServiceOffer o( (*it), 0, (*it)->allowAsDefault() );
        offers.append( o );
      }
    }/* else
      kdDebug(7014) << "Doesn't have " << m_strGenericServiceType << endl;*/
  }

  qBubbleSort( offers );

  //kdDebug(7014) << "KServiceTypeProfile::offers returning " << offers.count() << " offers" << endl;
  return offers;
}

KService::Ptr KServiceTypeProfile::preferredService( const TQString & _serviceType, const TQString & _genericServiceType )
{
  OfferList lst = offers( _serviceType, _genericServiceType );

  OfferList::Iterator itOff = lst.begin();
  // Look for the first one that is allowed as default.
  // Since the allowed-as-default are first anyway, we only have
  // to look at the first one to know.
  if( itOff != lst.end() && (*itOff).allowAsDefault() )
    return (*itOff).service();

  //kdDebug(7014) << "No offers, or none allowed as default" << endl;
  return 0L;
}

/*********************************************
 *
 * KServiceOffer
 *
 *********************************************/

KServiceOffer::KServiceOffer()
{
  m_iPreference = -1;
}

KServiceOffer::KServiceOffer( const KServiceOffer& _o )
{
  m_pService = _o.m_pService;
  m_iPreference = _o.m_iPreference;
  m_bAllowAsDefault = _o.m_bAllowAsDefault;
}

KServiceOffer::KServiceOffer( KService::Ptr _service, int _pref, bool _default )
{
  m_pService = _service;
  m_iPreference = _pref;
  m_bAllowAsDefault = _default;
}


bool KServiceOffer::operator< ( const KServiceOffer& _o ) const
{
  // Put offers allowed as default FIRST.
  if ( _o.m_bAllowAsDefault && !m_bAllowAsDefault )
    return false; // _o is default and not 'this'.
  if ( !_o.m_bAllowAsDefault && m_bAllowAsDefault )
    return true; // 'this' is default but not _o.
 // Both offers are allowed or not allowed as default
 // -> use preferences to sort them
 // The bigger the better, but we want the better FIRST
  return _o.m_iPreference < m_iPreference;
}