summaryrefslogtreecommitdiff
path: root/libopie2/opieui/big-screen/omodalhelper.h
Unidiff
Diffstat (limited to 'libopie2/opieui/big-screen/omodalhelper.h') (more/less context) (show whitespace changes)
-rw-r--r--libopie2/opieui/big-screen/omodalhelper.h774
1 files changed, 774 insertions, 0 deletions
diff --git a/libopie2/opieui/big-screen/omodalhelper.h b/libopie2/opieui/big-screen/omodalhelper.h
new file mode 100644
index 0000000..096cec4
--- a/dev/null
+++ b/libopie2/opieui/big-screen/omodalhelper.h
@@ -0,0 +1,774 @@
1/*
2               =. This file is part of the OPIE Project
3             .=l. Copyright (c) 2003 hOlgAr <zecke@handhelds.org>
4           .>+-=
5 _;:,     .>    :=|. This library is free software; you can
6.> <`_,   >  .   <= redistribute it and/or modify it under
7:`=1 )Y*s>-.--   : the terms of the GNU Library General Public
8.="- .-=="i,     .._ License as published by the Free Software
9 - .   .-<_>     .<> Foundation; either version 2 of the License,
10     ._= =}       : or (at your option) any later version.
11    .%`+i>       _;_.
12    .i_,=:_.      -<s. This library is distributed in the hope that
13     +  .  -:.       = it will be useful, but WITHOUT ANY WARRANTY;
14    : ..    .:,     . . . without even the implied warranty of
15    =_        +     =;=|` MERCHANTABILITY or FITNESS FOR A
16  _.=:.       :    :=>`: PARTICULAR PURPOSE. See the GNU
17..}^=.=       =       ; Library General Public License for more
18++=   -.     .`     .: details.
19 :     =  ...= . :.=-
20 -.   .:....=;==+<; You should have received a copy of the GNU
21  -_. . .   )=.  = Library General Public License along with
22    --        :-=` this library; see the file COPYING.LIB.
23 If not, write to the Free Software Foundation,
24 Inc., 59 Temple Place - Suite 330,
25 Boston, MA 02111-1307, USA.
26
27*/
28
29#ifndef OMODALHELPER_H
30#define OMODALHELPER_H
31
32/* QT*/
33#include <qdialog.h>
34#include <qwidget.h>
35#include <qvaluelist.h>
36#include <qmap.h>
37#include <qvariant.h>
38
39typedef int TransactionID;
40
41class QDialog;
42
43namespace Opie
44{
45
46class OModalHelperControler;
47class OModalHelperSignal;
48
49struct OModalHelperBase
50{
51 virtual void done( int status, TransactionID ) = 0;
52 virtual void next( TransactionID ) = 0;
53 virtual void prev( TransactionID ) = 0;
54};
55
56/**
57 * Modality sucks! ;) But it is easy to work with
58 * do exec() on a dialog and you know everything is funky.
59 * You only need to have one Dialog loaded and so on.
60 * This class helps you to work like with modality and help
61 * you to keep things in sync
62 * It's a template class but it sends signals once one Item is ready
63 * the signals contains the status and id of the item and then you
64 * need fetch it.
65 * Handled Records will stay available until the first call to retrieve
66 * either the record via the TransactionID or via the QValueList<Record>. Note
67 * that most functions do not take handled records into account.
68 * Also if you edit an record external you can tell this class and it'll
69 * call the merge() function of your widget to maybe merge in these changes.
70 * It also supports multiple modes. Either it can create new dialogs
71 * for each item or it can queue them depending on your usage. But it is
72 * so smart that if only one item is shown that the queue bar is not shown
73 * See the example for simple usage.
74 *
75 * @short helps to life without modaility
76 * @author hOlgAr
77 * @version 0.01
78 */
79template<class Dialog, class Record, typename Id = int>
80class OModalHelper : private OModalHelperBase
81{
82 friend class OModalHelperSignal;
83 friend class OModalHelperControler;
84public:
85 typedef QValueList<Record> RecordList;
86 typedef QMap<Id, Record> IdMap;
87 typedef QMap<TransactionID, Id> TransactionMap;
88 typedef QMap<QDialog*, TransactionID> DialogMap
89 enum Mode { Queue, New };
90 OModalHelper(enum Mode mode, QObject* parnet );
91
92 bool handles( Id id)const;
93 TransactionID transactionID( Id id)const;
94
95 void suspend( bool = true );
96
97 void cancel();
98 void cancel( TransactionID );
99
100 void connectDone( QObject* rec, const char* slot );
101 void connectAccepted( QObject* rec, const char* slot );
102 void connectRejected( QObject* rec, const char* slot );
103
104 TransactionID handle( Id id, const Record& rec = Record() );
105
106 void edited( Id, int what, const QVariant& data );
107
108 Record record( TransactionID )const;
109 RecordList recordsDone()const;
110private:
111 virtual void done( int, TransactionID );
112 virtual void next( TransactionID );
113 virtual void prev( TransactionID );
114
115 Record nextRecord( TransactionID &, int & )const;
116 Record prevRecord( TransactionID &, int & )const;
117 int pos( TransactionID )const;
118 Dialog* newDialogRecord( const Record& );
119
120private:
121 OModalHelperDialog *queuedDialog()const; // generate or recycle
122 OModalHelperDialog *m_dialog;
123 OModalHelperSignal *m_signal; // our signal
124 OModalHelperControler *m_controler;
125 IdMap m_ids; // maps ids (uids) to a record
126 IdMap m_doneIds;
127 TransactionMap m_transactions; // activate transactions
128 TransactionMap m_done; // done and waiting for getting picked
129 DialogMap m_editing; // only used for New Mode
130 enum Mode m_mode; // the mode we're in
131bool m_disabled :1;
132};
133
134
135
136/* ### FIXME use namespace with Qt3 */
137
138/*
139 * A note on flow. The Signal is used for QT Signals when
140 * a record is done.
141 * There is either one controler and this controler slot will
142 * be connected to a dialog signal.
143 * In Queue we get the next and prev signals and call the Helper.
144 * this then changes the Record of the dialog and sets the transactionId
145 * of the controler.
146 * For the new mode
147 *
148 */
149
150class OModalHelperSignal : public QObject
151{
152 Q_OBJECT
153public:
154 OModalHelperSignal(OModalHelperBase* base, QObject* parent);
155 ~OModalHelperSignal();
156
157signals:
158 done( int status, TransactionID transaction );
159 accepted( TransactionID transaction );
160 rejected( TransactionID transaction );
161
162private:
163 OModalHelperBase* m_base;
164};
165
166
167class OModalHelperControler : public QObject
168{
169 Q_OBJECT
170public:
171 OModalHelperControler( OModalHelperBase* , QObject* parent);
172 virtual TransactionID transactionID()const;
173 void setTransactionID( TransactionID id );
174 QDialog* dialog()const;
175
176public slots:
177 virtual void done(int result );
178 virtual void next();
179 virtual void prev();
180private:
181 QDialog *m_dia;
182 TransactionID m_id;
183 OModalHelperBase *m_base;
184}
185
186struct OModalQueueBar;
187class OModalQueuedDialog : public QDialog
188{
189 Q_OBJECT
190public:
191 OModalQueuedDialog(QDialog *mainWidget);
192 ~OModalQueuedDialog();
193
194 QDialog* centerDialog()const;
195
196 void setQueueBarEnabled( bool = true );
197 void setRecord( int record, int count );
198
199signals:
200 void next();
201 void prev();
202
203private:
204 OModalQueueBar *m_bar;
205 QDialog *m_center;
206};
207
208
209/*
210 * Tcpp Template Implementation
211 */
212
213/**
214 * This is the simple Template c'tor. It takes the mode
215 * this helper should operate in and the parent object.
216 * This helper will be deleted when the parent gets deleted
217 * or you delete it yourself.
218 *
219 * @param mode The mode this dialog should be in
220 * @param parent The parent QObject of this helper.
221 */
222template<class Dialog, class Record, typename Id>
223OModalHelper<Dialog, Record, Id>::OModalHelper( enum Mode mode, QObject* parent )
224{
225 m_disabled = false;
226 m_mode = mode;
227 m_signal = new OModalHelperSignal( this, parent );
228 m_controler = new OModalHelperControler( this, m_signal );
229}
230
231
232/**
233 * This functions looks for your record and sees if it is
234 * handled with this helper. Note that done records
235 * will not be returned.
236 *
237 * @return true if the record is currenlty edited otherwise false
238 *
239 * @param Id The id which might be handled
240 */
241template<class Dialog, class Record, typename Id>
242bool OModalHelper<Dialog, Record, Id>::handles( Id id )const
243{
244 if ( m_transactions.isEmpty() )
245 return false;
246
247 TransactionMap::ConstIterator it = m_transactions.begin();
248 for ( ; it != m_transactions.end(); ++it )
249 if ( it.data() == id )
250 return true;
251
252 return false;
253}
254
255
256/**
257 * just like handles( Id ) but returns the TransactionId
258 */
259template<class Dialog, class Record, typename Id>
260TransactionID OModalHelper<Dialog, Record, Id>::transactionID( Id id)const
261{
262 if ( m_transactions.isEmpty() || !m_ids.contains( id ) )
263 return 0;
264
265 TransactionMap::ConstIterator it = m_transactions.begin();
266 for ( ; it != m_transactions.end(); ++it )
267 if ( it.data() == id )
268 return it.key();
269
270 return 0;
271}
272
273/**
274 * If you're requested to flush your data and you do not want
275 * to call cancel with this method you can disable and enabled
276 * all dialogs.
277 * The state gets saved so if you want to handle a new record the dialog
278 * will be disabled as well.
279 *
280 * @param sus If true setDisabled(TRUE) will be called otherwise FALSE
281 */
282template<class Dialog, class Record, typename Id>
283void OModalHelper<Dialog, Record, Id>::suspend(bool sus)
284{
285 m_disabled = sus;
286 if (m_mode == New )
287 for (DialogMap::Iterator it = m_editing.begin(); it != m_editing.end(); ++it )
288 it.key()->setDisabled( sus );
289 else if (m_dialog )
290 queuedDialog()->setDisabled( sus );
291}
292
293/**
294 * Cancel will cancel all current operations and clear the list
295 * of done operations as well.
296 * This also clears all done operations you did not popped
297 */
298template<class Dialog, class Record, typename Id>
299void OModalHelper<Dialog, Record, Id>::cancel()
300{
301 m_ids.clear();
302 m_doneIds.clear();
303 m_done.clear();
304 m_transactions.clear();
305
306 /* we also need to remove the QDialogs */
307 /* and hide the queue dialog if present */
308 if (m_mode == New && !m_editing.isEmpty() )
309 {
310 for (DialogMap::Iterator it = m_editing.begin(); it != m_editing.end(); ++it )
311 delete it.key();
312
313 m_editing.clear();
314 }
315 else if (m_dialog )
316 queuedDialog()->setRecord( 0, 0 );
317
318 m_controler->setTransactionID( 0 );
319}
320
321
322/**
323 * This cancels editing of the record behind the Transaction Number
324 * Note that if editing is already done it will also be removed from this list
325 */
326template<class Dialog, class Record, typename Id>
327void OModalHelper::cancel( TransactionID tid )
328{
329 /* wrong tid */
330 if (!m_transactions.contains( tid ) && !m_done.contains( tid) )
331 return;
332
333 if (m_mode == New )
334 /* reverse map eek */
335 for (DialogMap::Iterator it = m_editing.begin(); it != m_editing.end(); ++it )
336 if ( it.data() == tid )
337 {
338 it.key()->hide();
339 delete it.key();
340 it = m_editing.remove( it );
341 break;
342 }
343
344 /* now remove from the various maps done and currently editing map*/
345 if (m_transactions.contains( tid ) )
346 m_ids.remove( m_transactions[tid] );
347 if (m_done.contains( tid ) )
348 m_doneIds.remove( m_done[tid ] );
349 m_done.remove( tid );
350 m_transactions.remove( tid );
351
352 next( 0 );
353}
354
355/**
356 * Connect to the done Signal. SIGNAL( done(int, TransactionID ) )
357 * This signal gets emitted whenever a Record was accepted or rejected
358 *
359 * @param rec The object where the slot belongs to
360 * @param slot The slot which should be called. See the needed parameter above
361 */
362template<class Dialog, class Record, typename Id>
363void OModalHelper<Dialog, Record, Id>::connectDone( QObject* rec, const char* slot )
364{
365 QObject::connect(m_signal, SIGNAL(done(int, TransactionID) ),
366 rec, slot );
367}
368
369/**
370 * Connect to the accepted Signal. SIGNAL( accepted(TransactionID ) )
371 * This signal gets emitted whenever a Record was accepted
372 *
373 * @param rec The object where the slot belongs to
374 * @param slot The slot which should be called. See the needed parameter above
375 */
376template<class Dialog, class Record, typename Id>
377void OModalHelper<Dialog, Record, Id>::connectAccepted( QObject* rec, const char* slot )
378{
379 QObject::connect(m_signal, SIGNAL(accepted(TransactionID) ),
380 rec, slot );
381}
382
383/**
384 * Same as the accepted method but this one gets emitted if the dialog
385 * got rejected.
386 * SIGNAL( rejected(TransactionID) )
387 *
388 * @param rec The QObject of the slot
389 * @param slot The slot make sure the signature is correct
390 */
391template<class Dialog, class Record, typename Id>
392void OModalHelper<Dialog, Record, Id>::connectRejected( QObject* rec, const char* slot )
393{
394 QObject::connect(m_signal, SIGNAL(rejected(TransactionID) ),
395 rec, slot );
396}
397
398/**
399 * Tell the helper to handle a record. If the record is currently handled
400 * it will be made active.
401 * Already handled record which are waiting getting popped are not taken into account
402 * Otherwise this helpers make the record editable.
403 * The record supplied needs to have a valid copy operator and constructor.
404 * In the case where the record is already present the parameter gets discarded.
405 * If you want the new record to be taken you need to cancel the Transaction first
406 *
407 * @param id The Identification of the Record. For PIM it would uid()
408 * @param rec The record we want to be edited
409 *
410 * @returns This functions returns the TransactionId assigned to the record
411 *
412 */
413template<class Dialog, class Record, typename Id>
414TransactionID OModalHelper<Dialog, Record, Id>::handle( Id id, const Record& rec )
415{
416 static TransactionID t_id = 0;
417 /*
418 *this method consists out of two parts divided each into New and Queued Mode.
419 * Either we have the dialog already, in this case we need to highlight the widget
420 * Or we need to add it.
421 */
422 TransactionID tid = 0;
423 /* we already have the record lets see if it was done or not */
424 if ( !(tid = transactionID( id ) ) )
425 {
426 if (m_mode == New )
427 {
428 /* lets find the dialog and show it need to reverse map*/
429 for (DialogMap::Iterator it = m_editing.begin(); it != m_editing.end(); ++it )
430 if ( it.data() == tid )
431 it.key()->show();
432 }
433 else if (m_controler->transactionID() != tid )
434 {
435 int po = pos( tid );
436 m_controler->setTransactionID( tid );
437 static_cast<Dialog*>( queuedDialog()->centerDialog() )->setRecord( m_ids[ m_transactions[tid] ] );
438 queuedDialog()->setRecord( po, m_transactions.count() );
439 }
440 }
441 else
442 {
443 tid = ++t_id;
444 m_transactions.insert( tid, id );
445 m_ids.insert( id, rec );
446
447 if (m_mode == New )
448 m_editing.insert( newDialogRecord( rec ), tid );
449 else
450 {
451 m_controler->setTransactionID( tid );
452 static_cast<Dialog*>( queuedDialog()->centerDialog() )->setRecord( rec );
453 queuedDialog()->setRecord( m_transactions.count(), m_transactions.count() );
454 }
455 }
456 return tid;
457}
458
459/**
460 * The goal of this helper is to help you to create non blocking
461 * GUIs. In the example of the todolist you can have the edit dialog
462 * but still change the priority or completion inline even if you currently
463 * edit the record.
464 * Your Dialog needs to have a Method setData(int,const QVariant& ) which will be called
465 * in these cases.
466 * If you edit anything while a record is edited call this function to merge the
467 * change in. Note if the record is not handled here we will ignore the request
468 *
469 */
470template<class Dialog, class Record, typename Id>
471void OModalHelper<Dialog, Record, Id>::edited( Id id, int what, const QVariant& data )
472{
473 int tid;
474 if (!( tid = transactionID( id ) ) )
475 return;
476
477 if (m_mode == New )
478 {
479 for (DialogMap::Iterator it= m_editing.begin(); it != m_editing.end(); ++it )
480 if ( it.data() == tid )
481 it.key()->setData( what, data );
482 }
483 else
484 {
485 int po = pos( tid );
486 Dialog* dia = static_cast<Dialog*>( queuedDialog()->centerDialog() );
487 dia->setRecord( m_ids[id] );
488 dia->setData( what, data );
489 queuedDialog()->setRecord( pos, m_transactions.count() );
490 }
491}
492
493/**
494 * This functions either returns the unedited record the done record
495 * or a new empty Record using Record().
496 * If a done record is retrieved all traces are removed inside this class. This
497 * is what was called popping a record. This means when you call this function
498 * with the same TransactionID an Empty record is retrieved.
499 *
500 */
501template<class Dialog, class Record, typename Id>
502Record OModalHelper<Dialog, Record, Id>::record( TransactionID tid)const
503{
504 if (m_transactions.contains( tid ) )
505 return m_ids[ m_transactions[tid] ];
506 else if (m_done.contains( tid ) )
507 {
508 Record rec = m_doneIds[ m_done[ tid] ];
509 m_doneIds.remove( m_done[ tid ] );
510 m_done.remove( tid );
511 return rec;
512 }
513 else
514 return Record();
515}
516
517/**
518 * Returns all done Records and removes all references to them internally. A 2nd call to this will
519 * only contain done record that where edited past the point
520 */
521template<class Dialog, class Record, typename Id>
522OModalHelper<Dialog,Record,Id>::RecordList OModalHelper<Dialog, Record, Id>::recordsDone()const
523{
524 RecordList list;
525
526 for (IdMap::ConstIterator it = m_doneIds.begin(); it != m_doneIds.end(); ++it )
527 list.append( it.data() );
528
529 /* clean up */
530 m_done.clear();
531 m_doneIds.clear();
532
533 return list;
534}
535
536
537/**
538 * @internal
539 */
540template<class Dialog, class Record, typename Id>
541void OModalHelper<Dialog, Record, Id>::done( int status, TransactionID tid)
542{
543 /* If we're in New mode the transaction Id does not count */
544 Record rec;
545
546 if (m_mode == New )
547 {
548 Dialog *dia = static_cast<Dialog*>( m_controler->dialog() );
549 m_controler->setTransactionID( 0 ); // set the internal dialog to 0l again
550 tid = m_editing[ dia ];
551 m_editing.remove( dia );
552 rec = dia->record();
553 delete dia;
554 }
555 else
556 rec = queuedDialog()->record();
557
558 Id id = m_transactions[ tid ];
559 if (result == QDialog::Accept )
560 {
561 m_doneIds.insert( is, rec );
562 m_done.insert( tid, id );
563 }
564
565 m_transactions.remove( tid );
566 m_ids.remove( id );
567
568
569 if (status == QDialog::Accept )
570 emit m_signal->accepted( tid );
571 else
572 emit m_signal->rejected( tid );
573
574 emit m_signal->done( result, tid );
575
576 next( 0 );
577}
578
579/**
580 * @internal
581 */
582template<class Dialog, class Record, typename Id>
583void OModalHelper<Dialog, Record, Id>::next( TransactionID tid)
584{
585 if (m_mode == New )
586 return;
587
588 if (! (m_transactions.count() ) )
589 {
590 m_controler->setTransactionID( 0 );
591 queuedDialog()->setRecord( 0, 0 );
592 return;
593 }
594
595 int next;
596 Record rec;
597
598 /* save the maybe edited record before switching */
599 Dialog *dia = static_cast<Dialog*>( queuedDialog()->centerDialog() );
600 rec = dia->record();
601 m_ids.replace( m_transactions[tid], rec );
602
603 rec = nextRecord( tid, next );
604 queuedDialog()->setRecord( next, m_transactions.count() );
605 dia->setRecord( rec );
606
607 m_controler->setTransactionID( tid ); // was changed during the next call
608}
609
610/**
611 * @internal
612 */
613/*
614 * code duplication should create a template fcuntion
615 * which takes a pointer to a function ( next, prev ) function
616 */
617template<class Dialog, class Record, typename Id>
618void OModalHelper<Dialog, Record, Id>::prev( TransactionID tid )
619{
620 if (m_mode == New )
621 return;
622
623 if (! (m_transactions.count()) )
624 {
625 m_controler->setTransactionID( 0 );
626 queuedDialog()->setRecord( 0, 0 );
627 return;
628 }
629
630 int prev;
631 Record rec;
632
633 /* save the maybe edited record before switching */
634 Dialog *dia = static_cast<Dialog*>( queuedDialog()->centerDialog() );
635 rec = dia->record();
636 m_ids.replace( m_transactions[tid], rec );
637
638 rec = prevRecord( tid, prev );
639 queuedDialog()->setRecord( prev, m_transactions.count() );
640 dia->setRecord( rec );
641
642 m_controler->setTransactionID( tid ); // was changed during the next call
643}
644
645/**
646 * @internal
647 */
648template<class Dialog, class Record, typename Id>
649Record OModalHelper<Dialog, Record, Id>::nextRecord( TransactionID &tid, int &po )
650{
651 /* if tid is == 0 we will take the first one */
652 /* pos starts at 1 here */
653 /* we know we're only called if there are records */
654 Record rec;
655 TransactionMap::Iterator it;
656 if (!tid )
657 {
658 po = 1;
659 TransactionMap::Iterator it = m_transactions.begin();
660 }
661 else
662 {
663 po = pos( tid );
664 /* if it is the last take the first as next */
665 if ( po == m_transactions.count() )
666 {
667 po = 1;
668 it = m_transactions.begin();
669 }
670 else
671 {
672 /* we know we're not the last and there is one after us */
673 it = m_transactions.find( tid );
674 ++it; ++po;
675 }
676 }
677
678 tid = it.key();
679 rec = m_ids[ tid ];
680 return rec;
681}
682
683/**
684 * @internal
685 */
686template<class Dialog, class Record, typename Id>
687Record OModalHelper<Dialog, Record, Id>::prevRecord( TransactionID& tid, int& pos )
688{
689 /* if tid is == 0 we will take the first one */
690 /* pos starts at 1 here */
691 /* we know we're only called if there are records */
692 Record rec;
693 TransactionMap::Iterator it;
694 if (!tid )
695 {
696 po = 1;
697 TransactionMap::Iterator it = m_transactions.begin();
698 }
699 else
700 {
701 po = pos( tid );
702 /* if it is the last take the first as next */
703 if ( po == 1 )
704 {
705 po = m_transactions.count();
706 it = m_transactions.end();
707 --it;
708 }
709 else
710 {
711 /* we know we're not the first and there is one before us */
712 it = m_transactions.find( tid );
713 --it; --po;
714 }
715 }
716
717 tid = it.key();
718 rec = m_ids[ tid ];
719 return rec;
720}
721
722/**
723 * @internal
724 */
725template<class Dialog, class Record, typename Id>
726int OModalHelper<Dialog, Record, Id>::pos( TransactionID id)const
727{
728 int i = 1;
729 for ( TransactionMap::ConstIterator it = m_transactions.begin(); it != m_transactions.end(); ++it, i++ )
730 if ( it.key() == id )
731 return i;
732
733
734 return 0;
735}
736
737/**
738 * @internal
739 */
740template<class Dialog, class Record, typename Id>
741Dialog* OModalHelper<Dialog, Record, Id>::newDialogRecord( const Record& rec )
742{
743 Dialog* dia = new Dialog;
744 dia->setRecord( rec );
745 dia->setDisabled( m_disabled );
746
747 QObject::connect(dia, SIGNAL(done(int) ),
748 m_controler, SLOT(done(int) ) );
749
750 /* FIXME big screen QPEApplication needs fixed*/
751 dia->show();
752}
753
754template<class Record, class Dialog, typename Id>
755OModalHelperDialog* OModalHelper<Record, Dialog, Id>::queuedDialog()const
756{
757 if (!m_dialog )
758 {
759 m_dialog = new OModalHelperDialog;
760 m_dialog->setEnabled( m_disabled );
761
762 QObject::connect(m_dialog, SIGNAL(done(int) ),
763 m_controler, SLOT(done(int) ) );
764 QObject::connect(m_dialog, SIGNAL(next() ),
765 m_controler, SLOT(next() ) );
766 QObject::connect(m_dialog, SIGNAL(prev() ),
767 m_controler, SLOT(prev() ) );
768 }
769 return m_dialog;
770}
771
772};
773
774#endif