00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <QPixmap>
00023
00024 #include <QClipboard>
00025 #include <QTcpServer>
00026
00027 #include <kaction.h>
00028 #include <kactioncollection.h>
00029 #include <kconfig.h>
00030 #include <kdebug.h>
00031 #include <kfind.h>
00032 #include <kfinddialog.h>
00033 #include <khelpmenu.h>
00034 #include <kicon.h>
00035 #include <kiconeffect.h>
00036 #include <klocale.h>
00037 #include <kmenu.h>
00038 #include <kshortcutsdialog.h>
00039 #include <ksocketfactory.h>
00040 #include <kstandardaction.h>
00041 #include <kstandarddirs.h>
00042 #include <ksystemtrayicon.h>
00043 #include <kwindowsystem.h>
00044 #include <kxmlguibuilder.h>
00045 #include <kxmlguifactory.h>
00046
00047 #include <kcal/calendarlocal.h>
00048 #include <kcal/journal.h>
00049 #include <kiconloader.h>
00050
00051 #include "knote.h"
00052 #include "knoteconfigdlg.h"
00053 #include "knotes/resourcemanager.h"
00054 #include "knotesadaptor.h"
00055 #include "knotesalarm.h"
00056 #include "knotesapp.h"
00057 #include "knotesglobalconfig.h"
00058 #include "knoteslegacy.h"
00059 #include "knotesnetrecv.h"
00060
00061 class KNotesKeyDialog
00062 : public KDialog
00063 {
00064 public:
00065 KNotesKeyDialog( KActionCollection *globals, QWidget *parent )
00066 : KDialog( parent )
00067 {
00068 setCaption( i18n( "Configure Shortcuts" ) );
00069 setButtons( Default | Ok | Cancel );
00070
00071 m_keyChooser = new KShortcutsEditor( globals, this );
00072 setMainWidget( m_keyChooser );
00073 connect( this, SIGNAL( defaultClicked() ),
00074 m_keyChooser, SLOT( allDefault() ) );
00075 }
00076
00077 void insert( KActionCollection *actions )
00078 {
00079 m_keyChooser->addCollection( actions, i18n( "Note Actions" ) );
00080 }
00081
00082 void configure()
00083 {
00084 if ( exec() == Accepted ) {
00085 m_keyChooser->save();
00086 }
00087 }
00088
00089 private:
00090 KShortcutsEditor *m_keyChooser;
00091 };
00092
00093
00094 static bool qActionLessThan( const QAction *a1, const QAction *a2 )
00095 {
00096 return ( a1->text() < a2->text() );
00097 }
00098
00099
00100 KNotesApp::KNotesApp()
00101 : QWidget(), m_alarm( 0 ), m_listener( 0 ), m_find( 0 ), m_findPos( 0 )
00102 {
00103 new KNotesAdaptor( this );
00104 QDBusConnection::sessionBus().registerObject( "/KNotes" , this );
00105 connect( kapp, SIGNAL( lastWindowClosed() ), kapp, SLOT( quit() ) );
00106
00107
00108 m_tray = new KSystemTrayIcon();
00109
00110 m_tray->setToolTip( i18n( "KNotes: Sticky notes for KDE" ) );
00111 m_tray->setIcon( KSystemTrayIcon::loadIcon( "knotes" ) );
00112 connect( m_tray, SIGNAL( activated( QSystemTrayIcon::ActivationReason ) ),
00113 SLOT( slotActivated( QSystemTrayIcon::ActivationReason ) ) );
00114
00115
00116 #ifdef __GNUC__
00117 #warning FIXME
00118 #endif
00119
00120
00121
00122 KAction *action = new KAction( KIcon( "document-new" ),
00123 i18n( "New Note" ), this );
00124 action->setGlobalShortcut( KShortcut( Qt::ALT + Qt::SHIFT + Qt::Key_N ),
00125 KAction::DefaultShortcut );
00126 actionCollection()->addAction( "new_note", action );
00127 connect( action, SIGNAL( triggered() ), SLOT( newNote() ) );
00128
00129 action = new KAction( KIcon( "edit-paste" ),
00130 i18n( "New Note From Clipboard" ), this );
00131 action->setGlobalShortcut( KShortcut( Qt::ALT + Qt::SHIFT + Qt::Key_C ),
00132 KAction::DefaultShortcut );
00133 actionCollection()->addAction( "new_note_clipboard", action );
00134 connect( action, SIGNAL( triggered() ), SLOT( newNoteFromClipboard() ) );
00135
00136 action = new KAction( KIcon( "knotes" ), i18n( "Show All Notes" ), this );
00137 action->setGlobalShortcut( KShortcut( Qt::ALT + Qt::SHIFT + Qt::Key_S ),
00138 KAction::DefaultShortcut );
00139 actionCollection()->addAction( "show_all_notes", action );
00140 connect( action, SIGNAL( triggered() ), SLOT( showAllNotes() ) );
00141
00142 action = new KAction( KIcon( "window-close" ),
00143 i18n( "Hide All Notes" ), this );
00144 action->setGlobalShortcut( KShortcut( Qt::ALT + Qt::SHIFT + Qt::Key_H ),
00145 KAction::DefaultShortcut );
00146 actionCollection()->addAction( "hide_all_notes", action );
00147 connect( action, SIGNAL( triggered() ), SLOT( hideAllNotes() ) );
00148
00149 new KHelpMenu( this, KGlobal::mainComponent().aboutData(), false,
00150 actionCollection() );
00151
00152 KStandardAction::find( this, SLOT( slotOpenFindDialog() ),
00153 actionCollection() );
00154 KStandardAction::preferences( this, SLOT( slotPreferences() ),
00155 actionCollection() );
00156 KStandardAction::keyBindings( this, SLOT( slotConfigureAccels() ),
00157 actionCollection() );
00158
00159 KStandardAction::quit( this, SLOT( slotQuit() ),
00160 actionCollection() )->setShortcut( 0 );
00161
00162 setXMLFile( componentData().componentName() + "appui.rc" );
00163
00164 m_guiBuilder = new KXMLGUIBuilder( this );
00165 m_guiFactory = new KXMLGUIFactory( m_guiBuilder, this );
00166 m_guiFactory->addClient( this );
00167
00168 m_contextMenu = static_cast<KMenu *>( m_guiFactory->container(
00169 "knotes_context",
00170 this ) );
00171 m_noteMenu = static_cast<KMenu *>( m_guiFactory->container(
00172 "notes_menu", this ) );
00173 m_tray->setContextMenu( m_contextMenu );
00174
00175 QString xmlFileName = componentData().componentName() + "ui.rc";
00176 QString filter = componentData().componentName() + '/' + xmlFileName;
00177 QStringList fileList =
00178 componentData().dirs()->findAllResources( "data", filter ) +
00179 componentData().dirs()->findAllResources( "data", xmlFileName );
00180
00181 QString doc;
00182 KXMLGUIClient::findMostRecentXMLFile( fileList, doc );
00183 m_noteGUI.setContent( doc );
00184
00185 KConfigGroup config( KGlobal::config(), "Global Keybindings" );
00186
00187
00188 KNotesLegacy::cleanUp();
00189
00190
00191 m_manager = new KNotesResourceManager();
00192 connect( m_manager, SIGNAL( sigRegisteredNote( KCal::Journal * ) ),
00193 this, SLOT( createNote( KCal::Journal * ) ) );
00194 connect( m_manager, SIGNAL( sigDeregisteredNote( KCal::Journal * ) ),
00195 this, SLOT( killNote( KCal::Journal * ) ) );
00196
00197
00198 m_manager->load();
00199
00200
00201 KCal::CalendarLocal calendar( QString::fromLatin1( "UTC" ) );
00202 if ( KNotesLegacy::convert( &calendar ) ) {
00203 KCal::Journal::List notes = calendar.journals();
00204 KCal::Journal::List::ConstIterator it;
00205 for ( it = notes.begin(); it != notes.end(); ++it ) {
00206 m_manager->addNewNote( *it );
00207 }
00208
00209 m_manager->save();
00210 }
00211
00212
00213
00214 m_alarm = new KNotesAlarm( m_manager, this );
00215
00216 updateNetworkListener();
00217
00218 if ( m_notes.size() == 0 && !kapp->isSessionRestored() ) {
00219 newNote();
00220 }
00221
00222 updateNoteActions();
00223 m_tray->show();
00224 }
00225
00226 KNotesApp::~KNotesApp()
00227 {
00228 saveNotes();
00229
00230 blockSignals( true );
00231 qDeleteAll( m_notes );
00232 m_notes.clear();
00233 qDeleteAll( m_noteActions );
00234 m_noteActions.clear();
00235 blockSignals( false );
00236
00237
00238 delete m_manager;
00239 delete m_guiBuilder;
00240 delete m_tray;
00241 }
00242
00243 bool KNotesApp::commitData( QSessionManager & )
00244 {
00245 saveConfigs();
00246 return true;
00247 }
00248
00249
00250
00251 QString KNotesApp::newNote( const QString &name, const QString &text )
00252 {
00253
00254 KCal::Journal *journal = new KCal::Journal();
00255
00256
00257 if ( !name.isEmpty() ) {
00258 journal->setSummary( name );
00259 } else {
00260 journal->setSummary( KGlobal::locale()->formatDateTime(
00261 QDateTime::currentDateTime() ) );
00262 }
00263
00264
00265 journal->setDescription( text );
00266
00267 m_manager->addNewNote( journal );
00268
00269 showNote( journal->uid() );
00270
00271 return journal->uid();
00272 }
00273
00274 QString KNotesApp::newNoteFromClipboard( const QString &name )
00275 {
00276 const QString &text = KApplication::clipboard()->text();
00277 return newNote( name, text );
00278 }
00279
00280 void KNotesApp::hideAllNotes() const
00281 {
00282 foreach ( KNote *note, m_notes ) {
00283 note->hide();
00284 }
00285 }
00286
00287 void KNotesApp::showAllNotes() const
00288 {
00289 foreach ( KNote *note, m_notes ) {
00290
00291 note->hide();
00292
00293 note->show();
00294 note->setFocus();
00295 }
00296 }
00297
00298 void KNotesApp::showNote( const QString &id ) const
00299 {
00300 KNote *note = m_notes.value( id );
00301 if ( note ) {
00302 showNote( note );
00303 } else {
00304 kWarning( 5500 ) << "showNote: no note with id:" << id;
00305 }
00306 }
00307
00308 void KNotesApp::hideNote( const QString &id ) const
00309 {
00310 KNote *note = m_notes.value( id );
00311 if ( note ) {
00312 note->hide();
00313 } else {
00314 kWarning( 5500 ) << "hideNote: no note with id:" << id;
00315 }
00316 }
00317
00318 void KNotesApp::killNote( const QString &id, bool force )
00319 {
00320 KNote *note = m_notes.value( id );
00321 if ( note ) {
00322 note->slotKill( force );
00323 } else {
00324 kWarning( 5500 ) << "killNote: no note with id:" << id;
00325 }
00326 }
00327
00328
00329 void KNotesApp::killNote( const QString &id )
00330 {
00331 killNote( id, false );
00332 }
00333
00334 QMap<QString, QString> KNotesApp::notes() const
00335 {
00336 QMap<QString, QString> notes;
00337
00338 foreach ( KNote *note, m_notes ) {
00339 notes.insert( note->noteId(), note->name() );
00340 }
00341
00342 return notes;
00343 }
00344
00345 QString KNotesApp::name( const QString &id ) const
00346 {
00347 KNote *note = m_notes.value( id );
00348 if ( note ) {
00349 return note->name();
00350 } else {
00351 return QString();
00352 }
00353 }
00354
00355 QString KNotesApp::text( const QString &id ) const
00356 {
00357 KNote *note = m_notes.value( id );
00358 if ( note ) {
00359 return note->text();
00360 } else {
00361 return QString();
00362 }
00363 }
00364
00365 void KNotesApp::setName( const QString &id, const QString &newName )
00366 {
00367 KNote *note = m_notes.value( id );
00368 if ( note ) {
00369 note->setName( newName );
00370 } else {
00371 kWarning( 5500 ) << "setName: no note with id:" << id;
00372 }
00373 }
00374
00375 void KNotesApp::setText( const QString &id, const QString &newText )
00376 {
00377 KNote *note = m_notes.value( id );
00378 if ( note ) {
00379 note->setText( newText );
00380 } else {
00381 kWarning( 5500 ) << "setText: no note with id:" << id;
00382 }
00383 }
00384
00385
00386
00387 void KNotesApp::slotActivated( QSystemTrayIcon::ActivationReason r )
00388 {
00389 switch ( r ) {
00390
00391 case QSystemTrayIcon::Trigger:
00392 if ( m_notes.size() == 1 ) {
00393 showNote( *m_notes.begin() );
00394 } else if ( m_notes.size() > 1 ) {
00395 m_noteMenu->popup( QCursor::pos () );
00396 }
00397 break;
00398
00399 case QSystemTrayIcon::MiddleClick:
00400 newNote();
00401 break;
00402
00403 default:
00404 break;
00405
00406 }
00407 }
00408
00409 void KNotesApp::slotShowNote()
00410 {
00411
00412 showNote( sender()->objectName() );
00413 }
00414
00415 void KNotesApp::slotWalkThroughNotes()
00416 {
00417
00418 QMap<QString, KNote *>::const_iterator it = m_notes.begin();
00419 for ( ; it != m_notes.end(); ++it ) {
00420 if ( ( *it )->hasFocus() ) {
00421 if ( ++it != m_notes.end() ) {
00422 showNote( *it );
00423 } else {
00424 showNote( *m_notes.begin() );
00425 }
00426 break;
00427 }
00428 }
00429 }
00430
00431 void KNotesApp::slotOpenFindDialog()
00432 {
00433 KFindDialog findDia( this );
00434 findDia.setObjectName( "find_dialog" );
00435 findDia.setHasSelection( false );
00436 findDia.setHasCursor( false );
00437 findDia.setSupportsBackwardsFind( false );
00438
00439 if ( findDia.exec() != QDialog::Accepted ) {
00440 return;
00441 }
00442
00443 delete m_findPos;
00444 m_findPos = new QMap<QString, KNote *>::iterator();
00445 *m_findPos = m_notes.begin();
00446
00447
00448
00449 delete m_find;
00450 m_find = new KFind( findDia.pattern(), findDia.options(), this );
00451
00452 slotFindNext();
00453 }
00454
00455 void KNotesApp::slotFindNext()
00456 {
00457 if ( *m_findPos != m_notes.end() ) {
00458 KNote *note = * ( *m_findPos++ );
00459 note->find( m_find->pattern(), m_find->options() );
00460 } else {
00461 m_find->displayFinalDialog();
00462 delete m_find;
00463 m_find = 0;
00464 delete m_findPos;
00465 m_findPos = 0;
00466 }
00467 }
00468
00469 void KNotesApp::slotPreferences()
00470 {
00471
00472 if ( KNoteConfigDlg::showDialog( "KNotes Default Settings" ) ) {
00473 return;
00474 }
00475
00476
00477 KNoteConfigDlg *dialog = new KNoteConfigDlg( 0, i18n( "Settings" ), this,
00478 "KNotes Settings" );
00479 connect( dialog, SIGNAL( settingsChanged( const QString & ) ),
00480 this, SLOT( updateNetworkListener() ) );
00481 connect( dialog, SIGNAL( settingsChanged( const QString & ) ),
00482 this, SLOT( updateStyle() ) );
00483 dialog->show();
00484 }
00485
00486 void KNotesApp::slotConfigureAccels()
00487 {
00488 KNotesKeyDialog keys( actionCollection(), this );
00489
00490 QMap<QString, KNote *>::const_iterator it = m_notes.begin();
00491
00492 if ( !m_notes.isEmpty() ) {
00493 keys.insert( ( *it )->actionCollection() );
00494 }
00495
00496 keys.configure();
00497
00498
00499 m_noteGUI.setContent(
00500 KXMLGUIFactory::readConfigFile( componentData().componentName() + "ui.rc",
00501 componentData() )
00502 );
00503
00504 if ( m_notes.isEmpty() ) {
00505 return;
00506 }
00507
00508 foreach ( QAction *action, ( *it )->actionCollection()->actions() ) {
00509 it = m_notes.begin();
00510 for ( ++it; it != m_notes.end(); ++it ) {
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520 QAction *toChange =
00521 ( *it )->actionCollection()->action( action->objectName() );
00522
00523 if ( toChange ) {
00524 toChange->setShortcuts( action->shortcuts() );
00525 }
00526 }
00527 }
00528 }
00529
00530 void KNotesApp::slotNoteKilled( KCal::Journal *journal )
00531 {
00532 m_manager->deleteNote( journal );
00533 saveNotes();
00534 }
00535
00536 void KNotesApp::slotQuit()
00537 {
00538 foreach ( KNote *note, m_notes ) {
00539 if ( note->isModified() ) {
00540 note->saveData();
00541 }
00542 }
00543
00544 saveConfigs();
00545 kapp->quit();
00546 }
00547
00548
00549
00550 void KNotesApp::showNote( KNote *note ) const
00551 {
00552 note->show();
00553 #ifdef Q_WS_X11
00554 KWindowSystem::setCurrentDesktop( KWindowSystem::windowInfo( note->winId(),
00555 NET::WMDesktop ).desktop() );
00556 KWindowSystem::forceActiveWindow( note->winId() );
00557 #endif
00558 note->setFocus();
00559 }
00560
00561 void KNotesApp::createNote( KCal::Journal *journal )
00562 {
00563 KNote *newNote = new KNote( m_noteGUI, journal, 0 );
00564 m_notes.insert( newNote->noteId(), newNote );
00565
00566 connect( newNote, SIGNAL( sigRequestNewNote() ),
00567 SLOT( newNote() ) );
00568 connect( newNote, SIGNAL( sigShowNextNote() ),
00569 SLOT( slotWalkThroughNotes() ) ) ;
00570 connect( newNote, SIGNAL( sigKillNote( KCal::Journal * ) ),
00571 SLOT( slotNoteKilled( KCal::Journal * ) ) );
00572 connect( newNote, SIGNAL( sigNameChanged() ),
00573 SLOT( updateNoteActions() ) );
00574 connect( newNote, SIGNAL( sigDataChanged() ),
00575 SLOT( saveNotes() ) );
00576 connect( newNote, SIGNAL( sigColorChanged() ),
00577 SLOT( updateNoteActions() ) );
00578 connect( newNote, SIGNAL( sigFindFinished() ),
00579 SLOT( slotFindNext() ) );
00580
00581
00582 if ( m_alarm ) {
00583 updateNoteActions();
00584 }
00585 }
00586
00587 void KNotesApp::killNote( KCal::Journal *journal )
00588 {
00589
00590 delete m_notes.take( journal->uid() );
00591 updateNoteActions();
00592 }
00593
00594 void KNotesApp::acceptConnection()
00595 {
00596
00597 QTcpSocket *s = m_listener->nextPendingConnection();
00598
00599 if ( s ) {
00600 KNotesNetworkReceiver *recv = new KNotesNetworkReceiver( s );
00601 connect( recv,
00602 SIGNAL( sigNoteReceived( const QString &, const QString & ) ),
00603 SLOT( newNote( const QString &, const QString & ) ) );
00604 }
00605 }
00606
00607 void KNotesApp::saveNotes()
00608 {
00609 KNotesGlobalConfig::self()->writeConfig();
00610 m_manager->save();
00611 }
00612
00613 void KNotesApp::saveConfigs()
00614 {
00615 foreach ( KNote *note, m_notes ) {
00616 note->saveConfig();
00617 }
00618 }
00619
00620 void KNotesApp::updateNoteActions()
00621 {
00622 unplugActionList( "notes" );
00623 m_noteActions.clear();
00624
00625 foreach ( KNote *note, m_notes ) {
00626
00627 #ifdef __GNUC__
00628 #warning utf8: use QString
00629 #endif
00630 KAction *action = new KAction( note->name().replace( "&", "&&" ), this );
00631 action->setObjectName( note->noteId() );
00632 connect( action, SIGNAL( triggered( bool ) ), SLOT( slotShowNote() ) );
00633 KIconEffect effect;
00634 QPixmap icon =
00635 effect.apply( qApp->windowIcon().pixmap( IconSize( KIconLoader::Small ),
00636 IconSize( KIconLoader::Small ) ),
00637 KIconEffect::Colorize,
00638 1,
00639 note->palette().color( note->backgroundRole() ),
00640 false );
00641
00642 action->setIcon( icon );
00643 m_noteActions.append( action );
00644 }
00645
00646 qSort( m_noteActions.begin(), m_noteActions.end(), qActionLessThan );
00647
00648 if ( m_noteActions.isEmpty() ) {
00649 KAction *action = new KAction( i18n( "No Notes" ), this );
00650 m_noteActions.append( action );
00651 }
00652
00653 plugActionList( "notes", m_noteActions );
00654 }
00655
00656 void KNotesApp::updateNetworkListener()
00657 {
00658 delete m_listener;
00659 m_listener=0;
00660
00661 if ( KNotesGlobalConfig::receiveNotes() ) {
00662
00663 m_listener=KSocketFactory::listen( "knotes" , QHostAddress::Any,
00664 KNotesGlobalConfig::port() );
00665 connect( m_listener, SIGNAL( newConnection() ),
00666 SLOT( acceptConnection() ) );
00667 }
00668 }
00669
00670 void KNotesApp::updateStyle()
00671 {
00672 #ifdef __GNUC__
00673 #warning FIXME!
00674 #endif
00675
00676
00677 foreach ( KNote *note, m_notes ) {
00678 QApplication::postEvent( note, new QEvent( QEvent::LayoutRequest ) );
00679 }
00680 }
00681
00682 #include "knotesapp.moc"