/******************************************************************* KNotes -- Notes for the KDE project Copyright (c) 1997-2006, The KNotes Developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *******************************************************************/ #include <tqclipboard.h> #include <tqptrlist.h> #include <tqtooltip.h> #include <kdebug.h> #include <tdeaction.h> #include <kxmlguifactory.h> #include <kxmlguibuilder.h> #include <ksystemtray.h> #include <tdelocale.h> #include <kiconeffect.h> #include <kstandarddirs.h> #include <tdepopupmenu.h> #include <khelpmenu.h> #include <kfind.h> #include <kfinddialog.h> #include <kkeydialog.h> #include <kglobalaccel.h> #include <ksimpleconfig.h> #include <twin.h> #include <kbufferedsocket.h> #include <kserversocket.h> #include <libkcal/journal.h> #include <libkcal/calendarlocal.h> #include "knotesapp.h" #include "knote.h" #include "knotesalarm.h" #include "knoteconfigdlg.h" #include "knotesglobalconfig.h" #include "knoteslegacy.h" #include "knotesnetrecv.h" #include "knotes/resourcemanager.h" using namespace KNetwork; class KNotesKeyDialog : public KDialogBase { public: KNotesKeyDialog( TDEGlobalAccel *globals, TQWidget *parent, const char* name = 0 ) : KDialogBase( parent, name, true, i18n("Configure Shortcuts"), Default|Ok|Cancel, Ok ) { m_keyChooser = new KKeyChooser( globals, this ); setMainWidget( m_keyChooser ); connect( this, TQT_SIGNAL(defaultClicked()), m_keyChooser, TQT_SLOT(allDefault()) ); } void insert( TDEActionCollection *actions ) { m_keyChooser->insert( actions, i18n("Note Actions") ); } void configure() { if ( exec() == Accepted ) m_keyChooser->save(); } private: KKeyChooser *m_keyChooser; }; int KNotesApp::KNoteActionList::compareItems( TQPtrCollection::Item s1, TQPtrCollection::Item s2 ) { if ( ((TDEAction*)s1)->text() == ((TDEAction*)s2)->text() ) return 0; return ( ((TDEAction*)s1)->text() < ((TDEAction*)s2)->text() ? -1 : 1 ); } KNotesApp::KNotesApp() : DCOPObject("KNotesIface"), TQLabel( 0, 0, WType_TopLevel ), m_alarm( 0 ), m_listener( 0 ), m_find( 0 ), m_findPos( 0 ) { connect( kapp, TQT_SIGNAL(lastWindowClosed()), kapp, TQT_SLOT(quit()) ); m_noteList.setAutoDelete( true ); m_noteActions.setAutoDelete( true ); // create the dock widget... KWin::setSystemTrayWindowFor( winId(), tqt_xrootwin() ); TQToolTip::add( this, i18n( "KNotes: Sticky notes for TDE" ) ); setBackgroundMode( X11ParentRelative ); setPixmap( KSystemTray::loadIcon( "knotes" ) ); // set the initial style KNote::setStyle( KNotesGlobalConfig::style() ); // create the GUI... new TDEAction( i18n("New Note"), "document-new", 0, TQT_TQOBJECT(this), TQT_SLOT(newNote()), actionCollection(), "new_note" ); new TDEAction( i18n("New Note From Clipboard"), "edit-paste", 0, TQT_TQOBJECT(this), TQT_SLOT(newNoteFromClipboard()), actionCollection(), "new_note_clipboard" ); new TDEAction( i18n("Show All Notes"), "knotes", 0, TQT_TQOBJECT(this), TQT_SLOT(showAllNotes()), actionCollection(), "show_all_notes" ); new TDEAction( i18n("Hide All Notes"), "window-close", 0, TQT_TQOBJECT(this), TQT_SLOT(hideAllNotes()), actionCollection(), "hide_all_notes" ); new KHelpMenu( this, kapp->aboutData(), false, actionCollection() ); m_findAction = KStdAction::find( TQT_TQOBJECT(this), TQT_SLOT(slotOpenFindDialog()), actionCollection() ); KStdAction::preferences( TQT_TQOBJECT(this), TQT_SLOT(slotPreferences()), actionCollection() ); KStdAction::keyBindings( TQT_TQOBJECT(this), TQT_SLOT(slotConfigureAccels()), actionCollection() ); //FIXME: no shortcut removing!? KStdAction::quit( TQT_TQOBJECT(this), TQT_SLOT(slotQuit()), actionCollection() )->setShortcut( 0 ); setXMLFile( instance()->instanceName() + "appui.rc" ); m_guiBuilder = new KXMLGUIBuilder( this ); m_guiFactory = new KXMLGUIFactory( m_guiBuilder, TQT_TQOBJECT(this) ); m_guiFactory->addClient( this ); m_context_menu = static_cast<TDEPopupMenu*>(m_guiFactory->container( "knotes_context", this )); m_note_menu = static_cast<TDEPopupMenu*>(m_guiFactory->container( "notes_menu", this )); // get the most recent XML UI file TQString xmlFileName = instance()->instanceName() + "ui.rc"; TQString filter = TQString::fromLatin1( instance()->instanceName() + '/' ) + xmlFileName; TQStringList fileList = instance()->dirs()->findAllResources( "data", filter ) + instance()->dirs()->findAllResources( "data", xmlFileName ); TQString doc; KXMLGUIClient::findMostRecentXMLFile( fileList, doc ); m_noteGUI.setContent( doc ); // create accels for global shortcuts m_globalAccel = new TDEGlobalAccel( TQT_TQOBJECT(this), "global accel" ); m_globalAccel->insert( "global_new_note", i18n("New Note"), "", ALT+SHIFT+Key_N, ALT+SHIFT+Key_N , TQT_TQOBJECT(this), TQT_SLOT(newNote()), true, true ); m_globalAccel->insert( "global_new_note_clipboard", i18n("New Note From Clipboard"), "", ALT+SHIFT+Key_C, ALT+SHIFT+Key_C, TQT_TQOBJECT(this), TQT_SLOT(newNoteFromClipboard()), true, true ); m_globalAccel->insert( "global_hide_all_notes", i18n("Hide All Notes"), "", ALT+SHIFT+Key_H, ALT+SHIFT+Key_H , TQT_TQOBJECT(this), TQT_SLOT(hideAllNotes()), true, true ); m_globalAccel->insert( "global_show_all_notes", i18n("Show All Notes"), "", ALT+SHIFT+Key_S, ALT+SHIFT+Key_S, TQT_TQOBJECT(this), TQT_SLOT(showAllNotes()), true, true ); m_globalAccel->readSettings(); TDEConfig *config = TDEGlobal::config(); config->setGroup( "Global Keybindings" ); m_globalAccel->setEnabled( config->readBoolEntry( "Enabled", true ) ); updateGlobalAccels(); // clean up old config files KNotesLegacy::cleanUp(); // create the resource manager m_manager = new KNotesResourceManager(); connect( m_manager, TQT_SIGNAL(sigRegisteredNote( KCal::Journal * )), this, TQT_SLOT(createNote( KCal::Journal * )) ); connect( m_manager, TQT_SIGNAL(sigDeregisteredNote( KCal::Journal * )), this, TQT_SLOT(killNote( KCal::Journal * )) ); // read the notes m_manager->load(); // read the old config files, convert and add them KCal::CalendarLocal calendar( TQString::fromLatin1( "UTC" ) ); if ( KNotesLegacy::convert( &calendar ) ) { KCal::Journal::List notes = calendar.journals(); KCal::Journal::List::ConstIterator it; for ( it = notes.constBegin(); it != notes.constEnd(); ++it ) m_manager->addNewNote( *it ); m_manager->save(); } // set up the alarm reminder - do it after loading the notes because this // is used as a check if updateNoteActions has to be called for a new note m_alarm = new KNotesAlarm( m_manager, TQT_TQOBJECT(this) ); // create the socket and possibly start listening for connections m_listener = new TDEServerSocket(); m_listener->setResolutionEnabled( true ); connect( m_listener, TQT_SIGNAL(readyAccept()), TQT_SLOT(acceptConnection()) ); updateNetworkListener(); if ( m_noteList.count() == 0 && !kapp->isRestored() ) newNote(); updateNoteActions(); } void KNotesApp::resizeTrayIcon () { // Honor Free Desktop specifications that allow for arbitrary system tray icon sizes TQPixmap origpixmap; TQPixmap scaledpixmap; TQImage newIcon; origpixmap = KSystemTray::loadSizedIcon( "knotes", TQWidget::width() ); newIcon = origpixmap; newIcon = newIcon.smoothScale(TQWidget::width(), TQWidget::height()); scaledpixmap = newIcon; setPixmap(scaledpixmap); } void KNotesApp::resizeEvent ( TQResizeEvent * ) { // Honor Free Desktop specifications that allow for arbitrary system tray icon sizes resizeTrayIcon(); } void KNotesApp::showEvent ( TQShowEvent * ) { // Honor Free Desktop specifications that allow for arbitrary system tray icon sizes resizeTrayIcon(); } KNotesApp::~KNotesApp() { saveNotes(); blockSignals( true ); m_noteList.clear(); blockSignals( false ); delete m_listener; delete m_manager; delete m_guiBuilder; } bool KNotesApp::commitData( TQSessionManager& ) { saveConfigs(); return true; } // -------------------- public DCOP interface -------------------- // TQString KNotesApp::newNote( const TQString& name, const TQString& text ) { // create the new note KCal::Journal *journal = new KCal::Journal(); // new notes have the current date/time as title if none was given if ( !name.isEmpty() ) journal->setSummary( name ); else journal->setSummary( TDEGlobal::locale()->formatDateTime( TQDateTime::currentDateTime() ) ); // the body of the note journal->setDescription( text ); if ( m_manager->addNewNote( journal ) ) { showNote( journal->uid() ); } return journal->uid(); } TQString KNotesApp::newNoteFromClipboard( const TQString& name ) { const TQString& text = TDEApplication::clipboard()->text(); return newNote( name, text ); } void KNotesApp::hideAllNotes() const { TQDictIterator<KNote> it( m_noteList ); for ( ; *it; ++it ) (*it)->close(); } void KNotesApp::showAllNotes() const { TQDictIterator<KNote> it( m_noteList ); for ( ; *it; ++it ) { (*it)->show(); } } void KNotesApp::showNote( const TQString& id ) const { KNote* note = m_noteList[id]; if ( note ) showNote( note ); else kdWarning(5500) << "showNote: no note with id: " << id << endl; } void KNotesApp::hideNote( const TQString& id ) const { KNote* note = m_noteList[id]; if ( note ) note->hide(); else kdWarning(5500) << "hideNote: no note with id: " << id << endl; } void KNotesApp::killNote( const TQString& id, bool force ) { KNote* note = m_noteList[id]; if ( note ) note->slotKill( force ); else kdWarning(5500) << "killNote: no note with id: " << id << endl; } // "bool force = false" doesn't work with dcop void KNotesApp::killNote( const TQString& id ) { killNote( id, false ); } TQMap<TQString,TQString> KNotesApp::notes() const { TQMap<TQString,TQString> notes; TQDictIterator<KNote> it( m_noteList ); for ( ; it.current(); ++it ) notes.insert( it.current()->noteId(), it.current()->name() ); return notes; } TQString KNotesApp::name( const TQString& id ) const { KNote* note = m_noteList[id]; if ( note ) return note->name(); else return TQString(); } TQString KNotesApp::text( const TQString& id ) const { KNote* note = m_noteList[id]; if ( note ) return note->text(); else return TQString(); } void KNotesApp::setName( const TQString& id, const TQString& newName ) { KNote* note = m_noteList[id]; if ( note ) note->setName( newName ); else kdWarning(5500) << "setName: no note with id: " << id << endl; } void KNotesApp::setText( const TQString& id, const TQString& newText ) { KNote* note = m_noteList[id]; if ( note ) note->setText( newText ); else kdWarning(5500) << "setText: no note with id: " << id << endl; } TQString KNotesApp::fgColor( const TQString& id ) const { KNote* note = m_noteList[id]; if ( note ) return note->fgColor().name(); else return TQString(); } TQString KNotesApp::bgColor( const TQString& id ) const { KNote* note = m_noteList[id]; if ( note ) return note->bgColor().name(); else return TQString(); } void KNotesApp::setColor( const TQString& id, const TQString& fgColor, const TQString& bgColor ) { KNote* note = m_noteList[id]; if ( note ) note->setColor( TQColor( fgColor ), TQColor( bgColor ) ); else kdWarning(5500) << "setColor: no note with id: " << id << endl; } int KNotesApp::width( const TQString& id ) const { KNote* note = m_noteList[id]; if ( note ) return note->width(); else return 0; } int KNotesApp::height( const TQString& id ) const { KNote* note = m_noteList[id]; if ( note ) return note->height(); else return 0; } void KNotesApp::move( const TQString& id, int x, int y ) const { KNote* note = m_noteList[id]; if ( note ) return note->move( x, y ); else kdWarning(5500) << "move: no note with id: " << id << endl; } void KNotesApp::resize( const TQString& id, int width, int height ) const { KNote* note = m_noteList[id]; if ( note ) return note->resize( width, height ); else kdWarning(5500) << "resize: no note with id: " << id << endl; } void KNotesApp::sync( const TQString& app ) { TQDictIterator<KNote> it( m_noteList ); for ( ; it.current(); ++it ) it.current()->sync( app ); } bool KNotesApp::isNew( const TQString& app, const TQString& id ) const { KNote* note = m_noteList[id]; if ( note ) return note->isNew( app ); else return false; } bool KNotesApp::isModified( const TQString& app, const TQString& id ) const { KNote* note = m_noteList[id]; if ( note ) return note->isModified( app ); else return false; } // ------------------- protected methods ------------------- // void KNotesApp::mousePressEvent( TQMouseEvent* e ) { if ( !rect().contains( e->pos() ) ) return; switch ( e->button() ) { case Qt::LeftButton: if ( m_noteList.count() == 1 ) { TQDictIterator<KNote> it( m_noteList ); showNote( it.toFirst() ); } else if ( m_note_menu->count() > 0 ) m_note_menu->popup( e->globalPos() ); break; case Qt::MidButton: newNote(); break; case Qt::RightButton: m_context_menu->popup( e->globalPos() ); default: break; } } // -------------------- protected slots -------------------- // void KNotesApp::slotShowNote() { // tell the WM to give this note focus showNote( TQString::fromUtf8( TQT_TQOBJECT(const_cast<TQT_BASE_OBJECT_NAME*>(sender()))->name() ) ); } void KNotesApp::slotWalkThroughNotes() { // show next note TQDictIterator<KNote> it( m_noteList ); KNote *first = it.toFirst(); for ( ; *it; ++it ) if ( (*it)->hasFocus() ) { if ( ++it ) showNote( *it ); else showNote( first ); break; } } void KNotesApp::slotOpenFindDialog() { KFindDialog findDia( this, "find_dialog" ); findDia.setHasSelection( false ); findDia.setHasCursor( false ); findDia.setSupportsBackwardsFind( false ); if ( (findDia.exec() != TQDialog::Accepted) || findDia.pattern().isEmpty() ) return; delete m_findPos; m_findPos = new TQDictIterator<KNote>( m_noteList ); // this could be in an own method if searching without a dialog should be possible delete m_find; m_find = new KFind( findDia.pattern(), findDia.options(), this ); slotFindNext(); } void KNotesApp::slotFindNext() { if ( **m_findPos ) { KNote *note = **m_findPos; ++*m_findPos; note->find( m_find->pattern(), m_find->options() ); } else { m_find->displayFinalDialog(); delete m_find; m_find = 0; delete m_findPos; m_findPos = 0; } } void KNotesApp::slotPreferences() { // reuse the dialog if possible if ( KNoteConfigDlg::showDialog( "KNotes Default Settings" ) ) return; // create a new preferences dialog... KNoteConfigDlg *dialog = new KNoteConfigDlg( 0, i18n("Settings"), this, "KNotes Settings" ); connect( dialog, TQT_SIGNAL(settingsChanged()), TQT_TQOBJECT(this), TQT_SLOT(updateNetworkListener()) ); connect( dialog, TQT_SIGNAL(settingsChanged()), TQT_TQOBJECT(this), TQT_SLOT(updateStyle()) ); dialog->show(); } void KNotesApp::slotConfigureAccels() { KNotesKeyDialog keys( m_globalAccel, this ); TQDictIterator<KNote> notes( m_noteList ); if ( !m_noteList.isEmpty() ) keys.insert( (*notes)->actionCollection() ); keys.configure(); m_globalAccel->writeSettings(); updateGlobalAccels(); // update GUI doc for new notes m_noteGUI.setContent( KXMLGUIFactory::readConfigFile( instance()->instanceName() + "ui.rc", instance() ) ); if ( m_noteList.isEmpty() ) return; notes.toFirst(); TQValueList<TDEAction *> list = (*notes)->actionCollection()->actions(); for ( TQValueList<TDEAction *>::iterator it = list.begin(); it != list.end(); ++it ) { notes.toFirst(); for ( ++notes; *notes; ++notes ) { TDEAction *toChange = (*notes)->actionCollection()->action( (*it)->name() ); if ( toChange->shortcut() != (*it)->shortcut() ) toChange->setShortcut( (*it)->shortcut() ); } } } void KNotesApp::slotNoteKilled( KCal::Journal *journal ) { m_noteUidModify=""; m_manager->deleteNote( journal ); saveNotes(); } void KNotesApp::slotQuit() { TQDictIterator<KNote> it( m_noteList ); for ( ; *it; ++it ) if ( (*it)->isModified() ) (*it)->saveData(false); saveConfigs(); kapp->quit(); } // -------------------- private methods -------------------- // void KNotesApp::showNote( KNote* note ) const { note->show(); KWin::setCurrentDesktop( KWin::windowInfo( note->winId() ).desktop() ); KWin::forceActiveWindow( note->winId() ); note->setFocus(); } void KNotesApp::createNote( KCal::Journal *journal ) { if( journal->uid() == m_noteUidModify) { KNote *note = m_noteList[m_noteUidModify]; if ( note ) note->changeJournal(journal); return; } m_noteUidModify = journal->uid(); KNote *newNote = new KNote( m_noteGUI, journal, 0, journal->uid().utf8() ); m_noteList.insert( newNote->noteId(), newNote ); connect( newNote, TQT_SIGNAL(sigRequestNewNote()), TQT_SLOT(newNote()) ); connect( newNote, TQT_SIGNAL(sigShowNextNote()), TQT_SLOT(slotWalkThroughNotes()) ); connect( newNote, TQT_SIGNAL(sigKillNote( KCal::Journal* )), TQT_SLOT(slotNoteKilled( KCal::Journal* )) ); connect( newNote, TQT_SIGNAL(sigNameChanged()), TQT_SLOT(updateNoteActions()) ); connect( newNote, TQT_SIGNAL(sigDataChanged(const TQString &)), TQT_SLOT(saveNotes(const TQString &)) ); connect( newNote, TQT_SIGNAL(sigColorChanged()), TQT_SLOT(updateNoteActions()) ); connect( newNote, TQT_SIGNAL(sigFindFinished()), TQT_SLOT(slotFindNext()) ); // don't call this during startup for each and every loaded note if ( m_alarm ) updateNoteActions(); } void KNotesApp::killNote( KCal::Journal *journal ) { if(m_noteUidModify == journal->uid()) { return; } // this kills the KNote object KNote *note = m_noteList.take( journal->uid() ); if ( note ) { note->deleteWhenIdle(); updateNoteActions(); } } void KNotesApp::acceptConnection() { // Accept the connection and make KNotesNetworkReceiver do the job TDEBufferedSocket *s = static_cast<TDEBufferedSocket *>(m_listener->accept()); if ( s ) { KNotesNetworkReceiver *recv = new KNotesNetworkReceiver( s ); connect( recv, TQT_SIGNAL(sigNoteReceived( const TQString &, const TQString & )), TQT_TQOBJECT(this), TQT_SLOT(newNote( const TQString &, const TQString & )) ); } } void KNotesApp::saveNotes( const TQString & uid ) { m_noteUidModify = uid; saveNotes(); } void KNotesApp::saveNotes() { KNotesGlobalConfig::writeConfig(); m_manager->save(); } void KNotesApp::saveConfigs() { TQDictIterator<KNote> it( m_noteList ); for ( ; it.current(); ++it ) it.current()->saveConfig(); } void KNotesApp::updateNoteActions() { unplugActionList( "notes" ); m_noteActions.clear(); for ( TQDictIterator<KNote> it( m_noteList ); it.current(); ++it ) { TDEAction *action = new TDEAction( it.current()->name().replace("&", "&&"), TDEShortcut(), TQT_TQOBJECT(this), TQT_SLOT(slotShowNote()), (TQObject *)0, it.current()->noteId().utf8() ); TDEIconEffect effect; TQPixmap icon = effect.apply( kapp->miniIcon(), TDEIconEffect::Colorize, 1, it.current()->paletteBackgroundColor(), false ); action->setIconSet( icon ); m_noteActions.append( action ); } if ( m_noteActions.isEmpty() ) { actionCollection()->action( "hide_all_notes" )->setEnabled( false ); actionCollection()->action( "show_all_notes" )->setEnabled( false ); m_findAction->setEnabled( false ); TDEAction *action = new TDEAction( i18n("No Notes") ); m_noteActions.append( action ); } else { actionCollection()->action( "hide_all_notes" )->setEnabled( true ); actionCollection()->action( "show_all_notes" )->setEnabled( true ); m_findAction->setEnabled( true ); m_noteActions.sort(); } plugActionList( "notes", m_noteActions ); } void KNotesApp::updateGlobalAccels() { if ( m_globalAccel->isEnabled() ) { TDEAction *action = actionCollection()->action( "new_note" ); if ( action ) action->setShortcut( m_globalAccel->shortcut( "global_new_note" ) ); action = actionCollection()->action( "new_note_clipboard" ); if ( action ) action->setShortcut( m_globalAccel->shortcut( "global_new_note_clipboard" ) ); action = actionCollection()->action( "hide_all_notes" ); if ( action ) action->setShortcut( m_globalAccel->shortcut( "global_hide_all_notes" ) ); action = actionCollection()->action( "show_all_notes" ); if ( action ) action->setShortcut( m_globalAccel->shortcut( "global_show_all_notes" ) ); m_globalAccel->updateConnections(); } else { TDEAction *action = actionCollection()->action( "new_note" ); if ( action ) action->setShortcut( 0 ); action = actionCollection()->action( "new_note_clipboard" ); if ( action ) action->setShortcut( 0 ); action = actionCollection()->action( "hide_all_notes" ); if ( action ) action->setShortcut( 0 ); action = actionCollection()->action( "show_all_notes" ); if ( action ) action->setShortcut( 0 ); } } void KNotesApp::updateNetworkListener() { m_listener->close(); if ( KNotesGlobalConfig::receiveNotes() ) { m_listener->setAddress( TQString::number( KNotesGlobalConfig::port() ) ); m_listener->bind(); m_listener->listen(); } } void KNotesApp::updateStyle() { KNote::setStyle( KNotesGlobalConfig::style() ); TQDictIterator<KNote> it( m_noteList ); for ( ; it.current(); ++it ) TQApplication::postEvent( *it, new TQEvent( TQEvent::LayoutHint ) ); } #include "knotesapp.moc"