00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <qclipboard.h>
00022 #include <qptrlist.h>
00023 #include <qdir.h>
00024
00025 #include <kdebug.h>
00026 #include <kaction.h>
00027 #include <kxmlguifactory.h>
00028 #include <ksystemtray.h>
00029 #include <klocale.h>
00030 #include <kiconloader.h>
00031 #include <kstandarddirs.h>
00032 #include <kpopupmenu.h>
00033 #include <khelpmenu.h>
00034 #include <kkeydialog.h>
00035 #include <kglobalaccel.h>
00036 #include <kmessagebox.h>
00037 #include <ksimpleconfig.h>
00038 #include <kio/netaccess.h>
00039 #include <kwin.h>
00040
00041 #include "libkcal/journal.h"
00042 #include "libkcal/icalformat.h"
00043
00044 #include "knotesapp.h"
00045 #include "knote.h"
00046 #include "knoteconfigdlg.h"
00047 #include "knoteslegacy.h"
00048 #include "version.h"
00049
00050 using namespace KCal;
00051
00052
00053 int KNotesApp::KNoteActionList::compareItems( QPtrCollection::Item s1, QPtrCollection::Item s2 )
00054 {
00055 if ( ((KAction*)s1)->text() == ((KAction*)s2)->text() )
00056 return 0;
00057 return ( ((KAction*)s1)->text() < ((KAction*)s2)->text() ? -1 : 1 );
00058 }
00059
00060
00061 KNotesApp::KNotesApp()
00062 : DCOPObject("KNotesIface"), QLabel( 0, 0, WType_TopLevel ),
00063 KXMLGUIBuilder( this )
00064 {
00065 m_noteActions.setAutoDelete( true );
00066 m_noteList.setAutoDelete( true );
00067
00068
00069 KWin::setSystemTrayWindowFor( winId(), qt_xrootwin() );
00070 setBackgroundMode( X11ParentRelative );
00071 setPixmap( KSystemTray::loadIcon( "knotes" ) );
00072
00073
00074 new KAction( i18n("New Note"), "filenew", 0,
00075 this, SLOT(slotNewNote()), actionCollection(), "new_note" );
00076 new KAction( i18n("New Note From Clipboard"), "editpaste", 0,
00077 this, SLOT(slotNewNoteFromClipboard()), actionCollection(), "new_note_clipboard" );
00078 new KHelpMenu( this, kapp->aboutData(), false, actionCollection() );
00079
00080 KStdAction::preferences( this, SLOT(slotPreferences()), actionCollection() );
00081 KStdAction::keyBindings( this, SLOT(slotConfigureAccels()), actionCollection() );
00082 KStdAction::quit( this, SLOT(slotQuit()), actionCollection() )->setShortcut( 0 );
00083
00084 setXMLFile( QString( instance()->instanceName() + "ui.rc" ) );
00085 factory = new KXMLGUIFactory( this, this, "guifactory" );
00086 factory->addClient( this );
00087
00088 m_context_menu = static_cast<KPopupMenu*>(factory->container( "knotes_context", this ));
00089 m_note_menu = static_cast<KPopupMenu*>(factory->container( "notes_menu", this ));
00090
00091
00092 globalAccel = new KGlobalAccel( this, "global accel" );
00093 globalAccel->insert( "global_new_note", i18n("New Note"), "",
00094 ALT+SHIFT+Key_N, ALT+SHIFT+Key_N ,
00095 this, SLOT(slotNewNote()), true, true );
00096 globalAccel->insert( "global_new_note_clipboard", i18n("New Note From Clipboard"), "",
00097 ALT+SHIFT+Key_C, ALT+SHIFT+Key_C,
00098 this, SLOT(slotNewNoteFromClipboard()), true, true );
00099
00100 globalAccel->readSettings();
00101
00102 KConfig *config = KGlobal::config();
00103 config->setGroup( "Global Keybindings" );
00104 globalAccel->setEnabled( config->readBoolEntry( "Enabled", true ) );
00105
00106 updateGlobalAccels();
00107
00108
00109 KNotesLegacy::cleanUp();
00110
00111
00112 m_calendar.load( KGlobal::dirs()->saveLocation( "appdata" ) + "notes.ics" );
00113
00114
00115
00116
00117
00118
00119
00120 if ( KNotesLegacy::convert( &m_calendar ) )
00121 saveNotes();
00122
00123 QDir noteDir( KGlobal::dirs()->saveLocation( "appdata", "notes/" ) );
00124 Journal::List notes = m_calendar.journals();
00125 Journal::List::ConstIterator it;
00126 for ( it = notes.begin(); it != notes.end(); ++it )
00127 {
00128 Journal *note = *it;
00129
00130 if ( note->attachments(CONFIG_MIME).isEmpty() )
00131 {
00132
00133 QString file = noteDir.absFilePath( note->uid() );
00134
00135
00136 KIO::NetAccess::copy(
00137 KURL( KGlobal::dirs()->findResource( "config", "knotesrc" ) ),
00138 KURL( file )
00139 );
00140
00141 note->addAttachment( new Attachment( file, CONFIG_MIME ) );
00142 }
00143
00144 if ( note->summary().isNull() && note->dtStart().isValid() )
00145 note->setSummary( KGlobal::locale()->formatDateTime( note->dtStart() ) );
00146
00147 KNote* newNote = new KNote( this, domDocument(), note );
00148 m_noteList.insert( note->uid(), newNote );
00149
00150 connect( newNote, SIGNAL(sigNewNote()), this, SLOT(slotNewNote()) );
00151 connect( newNote, SIGNAL(sigKillNote( KCal::Journal* )),
00152 this, SLOT(slotNoteKilled( KCal::Journal* )) );
00153 connect( newNote, SIGNAL(sigNameChanged()), this, SLOT(updateNoteActions()) );
00154 connect( newNote, SIGNAL(sigSaveData()), this, SLOT(saveNotes()) );
00155 }
00156 updateNoteActions();
00157
00158 connect( kapp, SIGNAL(lastWindowClosed()), kapp, SLOT(quit()) );
00159
00160 kapp->installEventFilter( this );
00161
00162 if ( m_noteList.count() == 0 && !kapp->isRestored() )
00163 newNote();
00164 }
00165
00166 KNotesApp::~KNotesApp()
00167 {
00168 saveNotes();
00169
00170 blockSignals( true );
00171 m_noteList.clear();
00172 blockSignals( false );
00173
00174 delete factory;
00175 }
00176
00177 bool KNotesApp::commitData( QSessionManager& )
00178 {
00179 saveConfig();
00180 return true;
00181 }
00182
00183
00184
00185 QString KNotesApp::newNote( const QString& name, const QString& text )
00186 {
00187
00188 Journal *note = new Journal();
00189
00190
00191 QDir noteDir( KGlobal::dirs()->saveLocation( "appdata", "notes/" ) );
00192 QString file = noteDir.absFilePath( note->uid() );
00193
00194
00195 KIO::NetAccess::copy( KURL( KGlobal::dirs()->findResource( "config", "knotesrc" ) ),
00196 KURL( file ) );
00197
00198
00199 if ( !name.isNull() )
00200 note->setSummary( name );
00201 else
00202 note->setSummary( KGlobal::locale()->formatDateTime( QDateTime::currentDateTime() ) );
00203
00204 note->setDescription( text );
00205 note->addAttachment( new Attachment( file, CONFIG_MIME ) );
00206 m_calendar.addJournal( note );
00207
00208 KNote* newNote = new KNote( this, domDocument(), note );
00209 m_noteList.insert( newNote->noteId(), newNote );
00210
00211 connect( newNote, SIGNAL(sigNewNote()), this, SLOT(slotNewNote()) );
00212 connect( newNote, SIGNAL(sigKillNote( KCal::Journal* )),
00213 this, SLOT(slotNoteKilled( KCal::Journal* )) );
00214 connect( newNote, SIGNAL(sigNameChanged()), this, SLOT(updateNoteActions()) );
00215 connect( newNote, SIGNAL(sigSaveData()), this, SLOT(saveNotes()) );
00216
00217 updateNoteActions();
00218 showNote( newNote );
00219
00220 return newNote->noteId();
00221 }
00222
00223 QString KNotesApp::newNoteFromClipboard( const QString& name )
00224 {
00225 const QString& text = KApplication::clipboard()->text();
00226 return newNote( name, text );
00227 }
00228
00229 void KNotesApp::showNote( const QString& id ) const
00230 {
00231 KNote* note = m_noteList[id];
00232 if ( note )
00233 showNote( note );
00234 else
00235 kdWarning(5500) << "No note with id: " << id << endl;
00236 }
00237
00238 void KNotesApp::hideNote( const QString& id ) const
00239 {
00240 KNote* note = m_noteList[id];
00241 if ( note )
00242 note->hide();
00243 else
00244 kdWarning(5500) << "No note with id: " << id << endl;
00245 }
00246
00247 void KNotesApp::killNote( const QString& id, bool force )
00248 {
00249 KNote* note = m_noteList[id];
00250 if ( note )
00251 note->slotKill( force );
00252 else
00253 kdWarning(5500) << "No note with id: " << id << endl;
00254 }
00255
00256 void KNotesApp::killNote( const QString& id )
00257 {
00258 killNote( id, false );
00259 }
00260
00261 QMap<QString,QString> KNotesApp::notes() const
00262 {
00263 QMap<QString,QString> notes;
00264 QDictIterator<KNote> it( m_noteList );
00265
00266 for ( ; it.current(); ++it )
00267 notes.insert( it.current()->noteId(), it.current()->name() );
00268
00269 return notes;
00270 }
00271
00272 QString KNotesApp::name( const QString& id ) const
00273 {
00274 KNote* note = m_noteList[id];
00275 if ( note )
00276 return note->name();
00277 else
00278 return QString::null;
00279 }
00280
00281 QString KNotesApp::text( const QString& id ) const
00282 {
00283 KNote* note = m_noteList[id];
00284 if ( note )
00285 return note->text();
00286 else
00287 return QString::null;
00288 }
00289
00290 void KNotesApp::setName( const QString& id, const QString& newName )
00291 {
00292 KNote* note = m_noteList[id];
00293 if ( note )
00294 note->setName( newName );
00295 else
00296 kdWarning(5500) << "No note with id: " << id << endl;
00297 }
00298
00299 void KNotesApp::setText( const QString& id, const QString& newText )
00300 {
00301 KNote* note = m_noteList[id];
00302 if ( note )
00303 note->setText( newText );
00304 else
00305 kdWarning(5500) << "No note with id: " << id << endl;
00306 }
00307
00308 void KNotesApp::sync( const QString& app )
00309 {
00310 QDictIterator<KNote> it( m_noteList );
00311
00312 for ( ; it.current(); ++it )
00313 it.current()->sync( app );
00314 }
00315
00316 bool KNotesApp::isNew( const QString& app, const QString& id ) const
00317 {
00318 KNote* note = m_noteList[id];
00319 if ( note )
00320 return note->isNew( app );
00321 else
00322 return false;
00323 }
00324
00325 bool KNotesApp::isModified( const QString& app, const QString& id ) const
00326 {
00327 KNote* note = m_noteList[id];
00328 if ( note )
00329 return note->isModified( app );
00330 else
00331 return false;
00332 }
00333
00334
00335
00336
00337 void KNotesApp::mousePressEvent( QMouseEvent* e )
00338 {
00339 if ( !rect().contains( e->pos() ) )
00340 return;
00341
00342 switch ( e->button() )
00343 {
00344 case LeftButton:
00345 if ( m_noteList.count() == 1 )
00346 {
00347 QDictIterator<KNote> it( m_noteList );
00348 showNote( it.toFirst() );
00349 }
00350 else if ( m_note_menu->count() > 0 )
00351 m_note_menu->popup( e->globalPos() );
00352 break;
00353 case MidButton:
00354 newNote();
00355 break;
00356 case RightButton:
00357 m_context_menu->popup( e->globalPos() );
00358 default: break;
00359 }
00360 }
00361
00362 bool KNotesApp::eventFilter( QObject* o, QEvent* ev )
00363 {
00364 if ( ev->type() == QEvent::KeyPress )
00365 {
00366 QKeyEvent* ke = (QKeyEvent*)ev;
00367
00368 if ( ke->key() == Key_BackTab )
00369 {
00370
00371 QDictIterator<KNote> it( m_noteList );
00372 KNote* first = it.toFirst();
00373 for ( ; it.current(); ++it )
00374 if ( it.current()->hasFocus() )
00375 {
00376 if ( ++it )
00377 showNote( it.current() );
00378 else
00379 showNote( first );
00380 break;
00381 }
00382
00383 ke->accept();
00384 return true;
00385 }
00386 else
00387 ke->ignore();
00388 }
00389
00390 return QLabel::eventFilter( o, ev );
00391 }
00392
00393
00394
00395
00396 void KNotesApp::slotNewNote()
00397 {
00398 newNote();
00399 }
00400
00401 void KNotesApp::slotNewNoteFromClipboard()
00402 {
00403 newNoteFromClipboard();
00404 }
00405
00406 void KNotesApp::slotShowNote()
00407 {
00408
00409 QString name = QString::fromUtf8( sender()->name() );
00410 showNote( name );
00411 }
00412
00413 void KNotesApp::slotNoteKilled( Journal *journal )
00414 {
00415
00416 m_noteList.remove( journal->uid() );
00417
00418 QString configFile = journal->attachments( CONFIG_MIME ).first()->uri();
00419 if ( !QDir::home().remove( configFile ) )
00420 kdError(5500) << "Can't remove the note config: " << configFile << endl;
00421
00422 m_calendar.deleteJournal( journal );
00423
00424 updateNoteActions();
00425 }
00426
00427 void KNotesApp::slotPreferences() const
00428 {
00429
00430 KNoteConfigDlg config( "knotesrc", i18n("KNotes Defaults"), true );
00431 config.exec();
00432 }
00433
00434 void KNotesApp::slotConfigureAccels()
00435 {
00436 KKeyDialog::configure( globalAccel, this, false );
00437 globalAccel->writeSettings();
00438 updateGlobalAccels();
00439 }
00440
00441 void KNotesApp::slotQuit()
00442 {
00443 saveConfig();
00444 kapp->quit();
00445 }
00446
00447
00448
00449
00450 void KNotesApp::showNote( KNote* note ) const
00451 {
00452 if ( !note->isHidden() )
00453 {
00454
00455
00456 KWin::setCurrentDesktop( KWin::info( note->winId() ).desktop );
00457 KWin::setActiveWindow( note->winId() );
00458 note->setFocus();
00459 }
00460 else
00461 {
00462 WId id = note->winId();
00463
00464
00465 note->show();
00466 note->toDesktop( KWin::currentDesktop() );
00467 if ( note->m_alwaysOnTop->isChecked() )
00468 KWin::setState( id, KWin::info( id ).state | NET::StaysOnTop );
00469 KWin::setActiveWindow( id );
00470 note->setFocus();
00471 }
00472 }
00473
00474 void KNotesApp::saveConfig()
00475 {
00476 QDictIterator<KNote> it( m_noteList );
00477 for ( ; it.current(); ++it )
00478 it.current()->saveConfig();
00479 }
00480
00481 void KNotesApp::saveNotes()
00482 {
00483 QString file = KGlobal::dirs()->saveLocation( "appdata" ) + "notes.ics";
00484 QString backup = file + "~";
00485
00486
00487
00488
00489 if ( KIO::NetAccess::exists( KURL( file ) ) && !KIO::NetAccess::file_copy( KURL( file ), KURL( backup ), -1, true) )
00490 KMessageBox::error(0, i18n("<qt>Unable to save the notes backup to <b>%1</b>! Check that there is sufficient disk space.</qt>")
00491 .arg( backup ) );
00492 else if ( !m_calendar.save( file, new ICalFormat() ) )
00493 KMessageBox::error(0, i18n("<qt>Unable to save the notes to <b>%1</b>! Check that there is sufficient disk space.<br>"
00494 "There should be a backup in <b>%2</b> though.</qt>")
00495 .arg( file ).arg( backup ) );
00496 }
00497
00498 void KNotesApp::updateNoteActions()
00499 {
00500 unplugActionList( "notes" );
00501 m_noteActions.clear();
00502
00503 for ( QDictIterator<KNote> it( m_noteList ); it.current(); ++it )
00504 {
00505 KAction *action = new KAction( it.current()->name().replace( "&", "&&"), KShortcut(),
00506 this, SLOT(slotShowNote()),
00507 (QObject*)0, it.current()->noteId().utf8() );
00508 QPixmap pix( 16, 16 );
00509 pix.fill( it.current()->paletteBackgroundColor() );
00510 action->setIconSet( pix );
00511 m_noteActions.append( action );
00512 }
00513
00514 m_noteActions.sort();
00515
00516 if ( m_noteActions.isEmpty() )
00517 {
00518 KAction *action = new KAction( i18n("No Notes") );
00519 m_noteActions.append( action );
00520 }
00521
00522 plugActionList( "notes", m_noteActions );
00523 }
00524
00525 void KNotesApp::updateGlobalAccels()
00526 {
00527 if ( globalAccel->isEnabled() )
00528 {
00529 KAction *action = actionCollection()->action( "new_note" );
00530 if ( action )
00531 action->setShortcut( globalAccel->shortcut( "global_new_note" ) );
00532 action = actionCollection()->action( "new_note_clipboard" );
00533 if ( action )
00534 action->setShortcut( globalAccel->shortcut( "global_new_note_clipboard" ) );
00535
00536 globalAccel->updateConnections();
00537 }
00538 else
00539 {
00540 KAction *action = actionCollection()->action( "new_note" );
00541 if ( action )
00542 action->setShortcut( 0 );
00543 action = actionCollection()->action( "new_note_clipboard" );
00544 if ( action )
00545 action->setShortcut( 0 );
00546 }
00547 }
00548
00549 #include "knotesapp.moc"