summaryrefslogtreecommitdiff
path: root/library/backend/categories.cpp
Unidiff
Diffstat (limited to 'library/backend/categories.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--library/backend/categories.cpp701
1 files changed, 701 insertions, 0 deletions
diff --git a/library/backend/categories.cpp b/library/backend/categories.cpp
new file mode 100644
index 0000000..91331db
--- a/dev/null
+++ b/library/backend/categories.cpp
@@ -0,0 +1,701 @@
1/**********************************************************************
2** Copyright (C) 2000 Trolltech AS. All rights reserved.
3**
4** This file is part of Qtopia Environment.
5**
6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free
8** Software Foundation and appearing in the file LICENSE.GPL included
9** in the packaging of this file.
10**
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
12** THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
13** PARTICULAR PURPOSE.
14**
15** See http://www.trolltech.com/gpl/ for GPL licensing information.
16**
17** Contact info@trolltech.com if any conditions of this licensing are
18** not clear to you.
19**
20**********************************************************************/
21#include "categories.h"
22#include <qfile.h>
23#include <qcstring.h>
24#include <qtextstream.h>
25#include "stringutil.h"
26
27using namespace Qtopia;
28
29/***********************************************************
30 *
31 * CategoryGroup
32 *
33 **********************************************************/
34
35#ifdef PALMTOPCENTER
36UidGen CategoryGroup::sUidGen( UidGen::PalmtopCenter );
37#else
38UidGen CategoryGroup::sUidGen( UidGen::Qtopia );
39#endif
40
41int CategoryGroup::add( const QString &label )
42{
43 if ( label == QObject::tr("All") || label == QObject::tr("Unfiled") )
44 return 0;
45
46 QMap<QString,int>::Iterator findIt = mLabelIdMap.find( label );
47 if ( findIt != mLabelIdMap.end() )
48 return 0;
49 int newUid = uidGen().generate();
50 insert( newUid, label );
51 return newUid;
52}
53
54void CategoryGroup::insert( int uid, const QString &label )
55{
56 uidGen().store( uid );
57 mIdLabelMap[uid] = label;
58 mLabelIdMap[label] = uid;
59}
60
61bool CategoryGroup::add( int uid, const QString &label )
62{
63 if ( label == QObject::tr("All") || label == QObject::tr("Unfiled") )
64 return FALSE;
65
66 QMap<QString,int>::ConstIterator labelIt = mLabelIdMap.find( label );
67 if ( labelIt != mLabelIdMap.end() )
68 return FALSE;
69 QMap<int,QString>::ConstIterator idIt = mIdLabelMap.find( uid );
70 if ( idIt != mIdLabelMap.end() )
71 return FALSE;
72 insert( uid, label );
73 return TRUE;
74}
75
76bool CategoryGroup::remove( const QString &label )
77{
78 QMap<QString,int>::Iterator findIt = mLabelIdMap.find( label );
79 if ( findIt == mLabelIdMap.end() )
80 return FALSE;
81
82 mIdLabelMap.remove( *findIt );
83 mLabelIdMap.remove( findIt );
84
85 return TRUE;
86}
87
88bool CategoryGroup::remove( int uid )
89{
90 QMap<int,QString>::Iterator idIt = mIdLabelMap.find( uid );
91 if ( idIt == mIdLabelMap.end() )
92 return FALSE;
93
94 mLabelIdMap.remove( *idIt );
95 mIdLabelMap.remove( idIt );
96
97 return TRUE;
98}
99
100bool CategoryGroup::rename( int uid, const QString &newLabel )
101{
102 if ( newLabel == QObject::tr("All") || newLabel == QObject::tr("Unfiled") )
103 return FALSE;
104
105 QMap<int, QString>::Iterator idIt = mIdLabelMap.find( uid );
106 if ( idIt == mIdLabelMap.end() )
107 return FALSE;
108
109 mLabelIdMap.remove( *idIt );
110 mLabelIdMap[newLabel] = uid;
111 *idIt = newLabel;
112
113 return TRUE;
114}
115
116bool CategoryGroup::rename( const QString &oldLabel, const QString &newLabel )
117{
118 return rename( id(oldLabel), newLabel );
119}
120
121bool CategoryGroup::contains(int uid) const
122{
123 return ( mIdLabelMap.find( uid ) != mIdLabelMap.end() );
124}
125
126bool CategoryGroup::contains(const QString &label) const
127{
128 return ( mLabelIdMap.find( label ) != mLabelIdMap.end() );
129}
130
131/** Returns label associated with the uid or QString::null if
132 * not found
133 */
134const QString &CategoryGroup::label(int uid) const
135{
136 QMap<int,QString>::ConstIterator idIt = mIdLabelMap.find( uid );
137 if ( idIt == mIdLabelMap.end() )
138 return QString::null;
139 return *idIt;
140}
141
142/** Returns the uid associated with label or 0 if not found */
143int CategoryGroup::id(const QString &label) const
144{
145 QMap<QString,int>::ConstIterator labelIt = mLabelIdMap.find( label );
146 if ( labelIt == mLabelIdMap.end() )
147 return 0;
148 return *labelIt;
149}
150
151QStringList CategoryGroup::labels() const
152{
153 QStringList labels;
154 for ( QMap<int, QString>::ConstIterator it = mIdLabelMap.begin();
155 it != mIdLabelMap.end(); ++it )
156 labels += *it;
157 // ### I don't think this is the place for this...
158// labels.sort();
159 return labels;
160}
161
162QStringList CategoryGroup::labels(const QArray<int> &catids ) const
163{
164 QStringList labels;
165 if ( catids.count() == 0 )
166 return labels;
167 for ( QMap<int, QString>::ConstIterator it = mIdLabelMap.begin();
168 it != mIdLabelMap.end(); ++it )
169 if ( catids.find( it.key() ) != -1 )
170 labels += *it;
171 return labels;
172}
173
174QArray<int> CategoryGroup::ids( const QStringList &cats ) const
175{
176 QArray<int> results;
177
178 for ( QStringList::ConstIterator catIt = cats.begin();
179 catIt != cats.end(); ++catIt ) {
180 if ( *catIt == QObject::tr("All") || *catIt == QObject::tr("Unfiled") )
181 continue;
182 int value = id( *catIt );
183 if ( value != 0 ) {
184 int tmp = results.size();
185 results.resize( tmp + 1 );
186 results[ tmp ] = value;
187 }
188 }
189
190 return results;
191}
192
193QArray<int> CategoryGroup::ids() const
194{
195 QArray<int> results( mIdLabelMap.count() );
196 int i = 0;
197 for ( QMap<int, QString>::ConstIterator it = mIdLabelMap.begin();
198 it != mIdLabelMap.end(); ++it )
199 results[i++] = it.key();
200
201 return results;
202}
203
204/***********************************************************
205 *
206 * Categories
207 *
208 **********************************************************/
209
210/** Add the category name as long as it doesn't already exist locally
211 * or globally. Return TRUE if added, FALSE if conflicts.
212 */
213int Categories::addCategory( const QString &appname,
214 const QString &catname,
215 int uid )
216{
217 if ( mGlobalCats.contains(catname) )
218 return 0;
219
220 QMap< QString, CategoryGroup >::Iterator
221 appIt = mAppCats.find( appname );
222
223 if ( appIt == mAppCats.end() ) {
224 CategoryGroup newgroup;
225 newgroup.add( uid, catname );
226 mAppCats.insert( appname, newgroup );
227 emit categoryAdded( *this, appname, uid );
228 return uid;
229 }
230
231 CategoryGroup &cats = *appIt;
232 cats.add( uid, catname );
233 emit categoryAdded( *this, appname, uid );
234 return uid;
235}
236
237int Categories::addCategory( const QString &appname,
238 const QString &catname )
239{
240 if ( mGlobalCats.contains(catname) )
241 return 0;
242
243 QMap< QString, CategoryGroup >::Iterator
244 appIt = mAppCats.find( appname );
245
246 if ( appIt == mAppCats.end() ) {
247 CategoryGroup newgroup;
248 int uid = newgroup.add( catname );
249 mAppCats.insert( appname, newgroup );
250 emit categoryAdded( *this, appname, uid );
251 return uid;
252 }
253
254 CategoryGroup &cats = *appIt;
255 int uid = cats.add( catname );
256 if ( !uid )
257 return 0;
258 emit categoryAdded( *this, appname, uid );
259 return uid;
260}
261
262int Categories::addGlobalCategory( const QString &catname, int uid )
263{
264 mGlobalCats.add( uid, catname );
265 emit categoryAdded( *this, QString::null, uid );
266 return uid;
267}
268
269int Categories::addGlobalCategory( const QString &catname )
270{
271 int uid = mGlobalCats.add( catname );
272 if ( !uid )
273 return 0;
274 emit categoryAdded( *this, QString::null, uid );
275 return uid;
276}
277
278/** Removes the category from the application; if it is not found
279 * in the application, then it attempts to remove it from
280 * the global list
281 */
282bool Categories::removeCategory( const QString &appname,
283 const QString &catname,
284 bool checkGlobal )
285{
286 QMap< QString, CategoryGroup >::Iterator
287 appIt = mAppCats.find( appname );
288 if ( appIt != mAppCats.end() ) {
289 CategoryGroup &cats = *appIt;
290 int uid = cats.id( catname );
291 if ( cats.remove( uid ) ) {
292 emit categoryRemoved( *this, appname, uid );
293 return TRUE;
294 }
295 }
296 if ( !checkGlobal )
297 return FALSE;
298 return removeGlobalCategory( catname );
299}
300
301bool Categories::removeCategory( const QString &appname, int uid )
302{
303 QMap< QString, CategoryGroup >::Iterator
304 appIt = mAppCats.find( appname );
305 if ( appIt != mAppCats.end() ) {
306 CategoryGroup &cats = *appIt;
307 if ( cats.remove( uid ) ) {
308 emit categoryRemoved( *this, appname, uid );
309 return TRUE;
310 }
311 }
312 return FALSE;
313}
314
315bool Categories::removeGlobalCategory( const QString &catname )
316{
317 int uid = mGlobalCats.id( catname );
318 if ( mGlobalCats.remove( uid ) ) {
319 emit categoryRemoved( *this, QString::null, uid );
320 return TRUE;
321 }
322 return FALSE;
323}
324
325
326bool Categories::removeGlobalCategory( int uid )
327{
328 if ( mGlobalCats.remove( uid ) ) {
329 emit categoryRemoved( *this, QString::null, uid );
330 return TRUE;
331 }
332 return FALSE;
333}
334
335/** Returns the sorted list of all categories that are associated with
336 * the app. If includeGlobal parameter is TRUE then the returned
337 * categories will include the global category items.
338 */
339QStringList Categories::labels( const QString &app,
340 bool includeGlobal,
341 ExtraLabels extra ) const
342{
343 QMap< QString, CategoryGroup >::ConstIterator
344 appIt = mAppCats.find( app );
345 QStringList cats;
346 switch ( extra ) {
347 case NoExtra: break;
348 case AllUnfiled:
349 cats.append( tr("All") );
350 cats.append( tr("Unfiled") );
351 break;
352 case AllLabel:
353 cats.append( tr("All") );
354 break;
355 case UnfiledLabel:
356 cats.append( tr("Unfiled") );
357 break;
358 }
359 if ( appIt != mAppCats.end() )
360 cats += (*appIt).labels();
361 else qDebug("Categories::labels didn't find app %s", app.latin1() );
362 if ( includeGlobal )
363 cats += mGlobalCats.labels();
364 // I don't think a sorted list is useful, the user might find prefer
365 // it in the original order.
366// cats.sort();
367 return cats;
368}
369
370QString Categories::label( const QString &app, int id ) const
371{
372 if ( mGlobalCats.contains( id ) )
373 return mGlobalCats.label( id );
374 QMap< QString, CategoryGroup >::ConstIterator
375 appIt = mAppCats.find( app );
376 if ( appIt == mAppCats.end() )
377 return QString::null;
378 return (*appIt).label( id );
379}
380
381QStringList Categories::labels( const QString & app,
382 const QArray<int> &catids ) const
383{
384 QStringList strs = mGlobalCats.labels( catids );
385 strs += mAppCats[app].labels( catids );
386 return strs;
387}
388
389/** Returns a single string associated with the cat ids for display in
390 * a combobox or any area that requires one string. If catids are empty
391 * then "Unfiled" will be returned. If multiple categories are assigned
392 * the first cat id is shown with " (multi)" appended to the string.
393 */
394QString Categories::displaySingle( const QString &app,
395 const QArray<int> &catids,
396 DisplaySingle display ) const
397{
398 QStringList strs = labels( app, catids );
399 if ( !strs.count() )
400 return tr("Unfiled");
401 strs.sort();
402 QString r;
403 if ( strs.count() > 1 ) {
404 switch ( display ) {
405 case ShowFirst:
406 r = strs.first();
407 break;
408 case ShowMulti:
409 r = strs.first() + tr(" (multi.)");
410 break;
411 case ShowAll:
412 r = strs.join(" ");
413 break;
414 }
415 }
416 else r = strs.first();
417 return r;
418}
419
420QArray<int> Categories::ids( const QString &app ) const
421{
422 QArray<int> allIds = mGlobalCats.ids();
423 QArray<int> appIds = mAppCats[app].ids();
424
425 // we should make the guarentee that the ids are in the
426 // same order as the labels, (i.e. app cats then global)
427 // otherwise there is no point in having these two separate functions.
428 uint appSize = appIds.size();
429 appIds.resize( appSize + allIds.size() );
430 for ( uint i = appSize; i < appIds.size(); ++i )
431 appIds[int(i)] = allIds[int(i - appSize)];
432
433 return appIds;
434}
435
436QArray<int> Categories::ids( const QString &app, const QStringList &cats ) const
437{
438 QArray<int> allIds = mGlobalCats.ids( cats );
439 QArray<int> appIds = mAppCats[app].ids( cats );
440
441 uint appSize = appIds.size();
442 appIds.resize( appSize + allIds.size() );
443 for ( uint i = appSize; i < appIds.size(); ++i )
444 appIds[int(i)] = allIds[int(i - appSize)];
445
446 return appIds;
447}
448
449int Categories::id( const QString &app, const QString &cat ) const
450{
451 if ( cat == tr("Unfiled") || cat.contains( tr(" (multi.)") ) )
452 return 0;
453 int uid = mGlobalCats.id( cat );
454 if ( uid != 0 )
455 return uid;
456 return mAppCats[app].id( cat );
457}
458
459
460/** Return TRUE if renaming succeeded; FALSE if app name not found,
461 * or if there was a name conflict
462 */
463bool Categories::renameCategory( const QString &appname,
464 const QString &oldName,
465 const QString &newName )
466{
467 QMap< QString, CategoryGroup >::Iterator
468 appIt = mAppCats.find( appname );
469
470 if ( appIt != mAppCats.end() ) {
471 CategoryGroup &cats = *appIt;
472 int id = cats.id( oldName );
473 if ( id != 0 && cats.rename( id, newName ) ) {
474 emit categoryRenamed( *this, appname, id );
475 return TRUE;
476 }
477 }
478 return renameGlobalCategory( oldName, newName );
479}
480
481bool Categories::renameGlobalCategory( const QString &oldName,
482 const QString &newName )
483{
484 int uid = mGlobalCats.id( oldName );
485 if ( uid != 0 && mGlobalCats.rename( uid, newName ) ) {
486 emit categoryRenamed( *this, QString::null, uid );
487 return TRUE;
488 }
489 return FALSE;
490}
491
492void Categories::setGlobal( const QString &appname,
493 const QString &catname,
494 bool global )
495{
496 // if in global and should be in app; then move it
497 if ( mGlobalCats.contains( catname ) && !global ) {
498 mGlobalCats.remove( catname );
499 addCategory( appname, catname );
500 return ;
501 }
502
503 // if in app and should be in global, then move it
504 if ( !global )
505 return;
506 if ( removeCategory( appname, catname, FALSE ) )
507 addGlobalCategory( catname );
508}
509
510bool Categories::isGlobal( const QString &catname ) const
511{
512 return mGlobalCats.contains( catname );
513}
514
515
516/** Returns true if the catname is associated with any application
517 */
518bool Categories::exists( const QString &catname ) const
519{
520 if ( isGlobal(catname) )
521 return TRUE;
522
523 for ( QMap<QString, CategoryGroup>::ConstIterator appsIt = mAppCats.begin(); appsIt != mAppCats.end(); ++appsIt )
524 if ( exists( appsIt.key(), catname ) )
525 return TRUE;
526
527 return FALSE;
528}
529
530bool Categories::exists( const QString &appname,
531 const QString &catname) const
532{
533 QMap< QString, CategoryGroup >::ConstIterator
534 appIt = mAppCats.find( appname );
535
536 if ( appIt == mAppCats.end() )
537 return FALSE;
538
539 return (*appIt).contains( catname );
540}
541
542bool Categories::save( const QString &fname ) const
543{
544 QFile file( fname );
545 if ( !file.open( IO_WriteOnly ) ) {
546 qWarning("Unable to write to %s", fname.latin1());
547 return FALSE;
548 }
549
550 QTextStream ts( &file );
551 ts << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
552 ts << "<!DOCTYPE CategoryList>" << endl;
553
554 ts << "<Categories>" << endl;
555 for ( QMap<int, QString>::ConstIterator git = mGlobalCats.idMap().begin();
556 git != mGlobalCats.idMap().end(); ++git )
557 ts << "<Category id=\"" << git.key() << "\""
558 << " name=\"" << escapeString(*git) << "\" />" << endl;
559
560 for ( QMap<QString, CategoryGroup>::ConstIterator appsIt=mAppCats.begin();
561 appsIt != mAppCats.end(); ++appsIt ) {
562 const QString &app = appsIt.key();
563 const QMap<int, QString> &appcats = (*appsIt).idMap();
564 for ( QMap<int, QString>::ConstIterator appcatit = appcats.begin();
565 appcatit != appcats.end(); ++appcatit )
566 ts << "<Category id=\"" << appcatit.key() << "\""
567 << " app=\"" << escapeString(app) << "\""
568 << " name=\"" << escapeString(*appcatit) << "\" />" << endl;
569 }
570 ts << "</Categories>" << endl;
571
572 file.close();
573 return TRUE;
574}
575
576bool Categories::load( const QString &fname )
577{
578 QFile file( fname );
579 if ( !file.open( IO_ReadOnly ) ) {
580 qWarning("Unable to open %s", fname.latin1());
581 return FALSE;
582 }
583
584 clear();
585 QByteArray ba = file.readAll();
586 QString data = QString::fromUtf8( ba.data(), ba.size() );
587 QChar *uc = (QChar *)data.unicode();
588 int len = data.length();
589
590 // QTime t;
591 // t.start();
592 QString name;
593 QString id;
594 QString app;
595 int i = 0;
596 while ( (i = data.find( "<Category ", i)) != -1 ) {
597
598 i += 10;
599 name = QString::null;
600 app = QString::null;
601 while ( 1 ) {
602 // skip white space
603 while ( i < len &&
604 (uc[i] == ' ' || uc[i] == '\n' || uc[i] == '\r') )
605 i++;
606 // if at the end, then done
607 if ( i >= len-2 || (uc[i] == '/' && uc[i+1] == '>') )
608 break;
609 // we have another attribute read it.
610 int j = i;
611 while ( j < len && uc[j] != '=' )
612 j++;
613 QString attr = QConstString( uc+i, j-i ).string();
614 i = ++j; // skip =
615 while ( i < len && uc[i] != '"' )
616 i++;
617 j = ++i;
618 while ( j < len && uc[j] != '"' )
619 j++;
620 QString value = Qtopia::plainString( QConstString( uc+i, j-i ).string() );
621 i = j + 1;
622
623 // qDebug("attr='%s' value='%s'", attr.latin1(), value.latin1() );
624 if ( attr == "id" )
625 id = value;
626 else if ( attr == "app" )
627 app = value;
628
629 else if ( attr == "name" )
630 name = value;
631 }
632
633 if ( name.isNull() || id.isNull() ) {
634 qWarning("No name or id in the category");
635 continue;
636 }
637 if ( app.isNull() )
638 mGlobalCats.add( id.toInt(), name );
639 else
640 mAppCats[ app ].add( id.toInt(), name );
641 }
642
643 return TRUE;
644}
645
646void Categories::clear()
647{
648 mGlobalCats.clear();
649 mAppCats.clear();
650}
651
652void Categories::dump() const
653{
654 qDebug("\tglobal categories = %s", mGlobalCats.labels().join(", ").latin1() );
655 for ( QMap<QString, CategoryGroup>::ConstIterator appsIt = mAppCats.begin(); appsIt != mAppCats.end(); ++appsIt ) {
656 const QString &app = appsIt.key();
657 QStringList appcats = (*appsIt).labels();
658 qDebug("\tapp = %s\tcategories = %s", app.latin1(),
659 appcats.join(", ").latin1() );
660
661 }
662}
663
664QStringList CheckedListView::checked() const
665{
666 QStringList strs;
667 for ( QCheckListItem *i = (QCheckListItem *) firstChild();
668 i; i = (QCheckListItem *)i->nextSibling() )
669 if ( i->isOn() )
670 strs += i->text( 0 );
671 return strs;
672}
673
674void CheckedListView::addCheckableList( const QStringList &options )
675{
676 for ( QStringList::ConstIterator it = options.begin();
677 it != options.end(); ++it ) {
678 (void) new QCheckListItem( this, *it,
679 QCheckListItem::CheckBox );
680 }
681}
682
683void CheckedListView::setChecked( const QStringList &checked )
684{
685 // iterate over all items
686 bool showingChecked = FALSE;
687 for ( QCheckListItem *i = (QCheckListItem *) firstChild();
688 i; i = (QCheckListItem *)i->nextSibling() )
689 // see if the item should be checked by searching the
690 // checked list
691 if ( checked.find( i->text( 0 ) ) != checked.end() ) {
692 i->setOn( TRUE );
693 // make sure it is showing at least one checked item
694 if ( !showingChecked ) {
695 ensureItemVisible( i );
696 showingChecked = TRUE;
697 }
698 }
699 else
700 i->setOn( FALSE );
701}