• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdepim
  • Sitemap
  • Contact Us
 

knotes

knote.cpp

Go to the documentation of this file.
00001 /*******************************************************************
00002  KNotes -- Notes for the KDE project
00003 
00004  Copyright (c) 1997-2007, The KNotes Developers
00005 
00006  This program is free software; you can redistribute it and/or
00007  modify it under the terms of the GNU General Public License
00008  as published by the Free Software Foundation; either version 2
00009  of the License, or (at your option) any later version.
00010 
00011  This program is distributed in the hope that it will be useful,
00012  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  GNU General Public License for more details.
00015 
00016  You should have received a copy of the GNU General Public License
00017  along with this program; if not, write to the Free Software
00018  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019 *******************************************************************/
00020 
00021 #include <QBoxLayout>
00022 #include <QCheckBox>
00023 #include <QFile>
00024 #include <QHBoxLayout>
00025 #include <QLabel>
00026 #include <QObject>
00027 #include <QPixmap>
00028 #include <QSize>
00029 #include <QSizeGrip>
00030 #include <QTextStream>
00031 #include <QVBoxLayout>
00032 #include <QDesktopWidget>
00033 
00034 #include <k3colordrag.h>
00035 #include <kaction.h>
00036 #include <kactioncollection.h>
00037 #include <kapplication.h>
00038 #include <kcal/journal.h>
00039 #include <kcodecs.h>
00040 #include <kcombobox.h>
00041 #include <kdebug.h>
00042 #include <kfiledialog.h>
00043 #include <kfind.h>
00044 #include <kglobalsettings.h>
00045 #include <kicon.h>
00046 #include <kiconeffect.h>
00047 #include <kiconloader.h>
00048 #include <kinputdialog.h>
00049 #include <kio/netaccess.h>
00050 #include <klocale.h>
00051 #include <kmenu.h>
00052 #include <kmessagebox.h>
00053 #include <kprocess.h>
00054 #include <kselectaction.h>
00055 #include <ksocketfactory.h>
00056 #include <kstandardaction.h>
00057 #include <kstandarddirs.h>
00058 #include <ktoggleaction.h>
00059 #include <ktoolbar.h>
00060 #include <kwindowsystem.h>
00061 #include <kxmlguibuilder.h>
00062 #include <kxmlguifactory.h>
00063 #include <netwm.h>
00064 #include <kdeversion.h>
00065 
00066 #ifdef Q_WS_X11
00067 #include <fixx11h.h>
00068 #include <QX11Info>
00069 #endif
00070 
00071 #include "knote.h"
00072 #include "knotealarmdlg.h"
00073 #include "knotebutton.h"
00074 #include "knoteconfig.h"
00075 #include "knoteconfigdlg.h"
00076 #include "knoteedit.h"
00077 #include "knotehostdlg.h"
00078 #include "knoteprinter.h"
00079 #include "knotesglobalconfig.h"
00080 #include "knotesnetsend.h"
00081 #include "version.h"
00082 
00083 using namespace KCal;
00084 
00085 
00086 KNote::KNote( QDomDocument buildDoc, Journal *j, QWidget *parent )
00087   : QFrame( parent, Qt::FramelessWindowHint ), m_label( 0 ), m_grip( 0 ),
00088     m_button( 0 ), m_tool( 0 ), m_editor( 0 ), m_config( 0 ), m_journal( j ),
00089     m_find( 0 ), m_kwinConf( KSharedConfig::openConfig( "kwinrc" ) )
00090 {
00091   setAcceptDrops( true );
00092   setAttribute( Qt::WA_DeleteOnClose );
00093   setDOMDocument( buildDoc );
00094   setObjectName( m_journal->uid() );  
00095   setXMLFile( componentData().componentName() + "ui.rc", false, false );
00096   
00097   // create the main layout
00098   m_noteLayout = new QVBoxLayout( this );
00099   setLayout( m_noteLayout );
00100   m_noteLayout->setMargin( 0 );
00101    
00102   // if there is no title yet, use the start date if valid
00103   // (KOrganizer's journals don't have titles but a valid start date)
00104   if ( m_journal->summary().isNull() && m_journal->dtStart().isValid() ) {
00105     QString s = KGlobal::locale()->formatDateTime( m_journal->dtStart() );
00106     m_journal->setSummary( s );
00107   }
00108   
00109   createActions();
00110 
00111   buildGui();
00112 
00113   prepare();
00114 }
00115 
00116 KNote::~KNote()
00117 {
00118   delete m_config;
00119 }
00120 
00121 
00122 // -------------------- public slots -------------------- //
00123 
00124 void KNote::slotKill( bool force )
00125 {
00126   if ( !force &&
00127        ( KMessageBox::warningContinueCancel( this,
00128          i18n( "<qt>Do you really want to delete note <b>%1</b>?</qt>",
00129                m_label->text() ),
00130          i18n( "Confirm Delete" ),
00131          KGuiItem( i18n( "&Delete" ), "edit-delete" ),
00132          KStandardGuiItem::cancel(),
00133          "ConfirmDeleteNote" ) != KMessageBox::Continue ) ) {
00134     return;
00135   }
00136   
00137   // delete the configuration first, then the corresponding file
00138   delete m_config;
00139   m_config = 0;
00140   
00141   QString configFile = KGlobal::dirs()->saveLocation( "appdata", "notes/" );
00142   configFile += m_journal->uid();
00143   
00144   if ( !KIO::NetAccess::del( KUrl( configFile ), this ) ) {
00145     kError( 5500 ) <<"Can't remove the note config:" << configFile;
00146   }
00147   
00148   emit sigKillNote( m_journal );
00149 }
00150 
00151 
00152 // -------------------- public member functions -------------------- //
00153 
00154 void KNote::saveData()
00155 {
00156   m_journal->setSummary( m_label->text() );
00157   m_journal->setDescription( m_editor->text() );
00158   m_journal->setCustomProperty( "KNotes", "FgColor",
00159                                 m_config->fgColor().name() );
00160   m_journal->setCustomProperty( "KNotes", "BgColor",
00161                                 m_config->bgColor().name() );
00162   m_journal->setCustomProperty( "KNotes", "RichText",
00163                                 m_config->richText() ? "true" : "false" );
00164   
00165   emit sigDataChanged();
00166   m_editor->document()->setModified( false );
00167 }
00168 
00169 void KNote::saveConfig() const
00170 {
00171   m_config->setWidth( width() );
00172   if ( m_tool ) {
00173     m_config->setHeight(
00174         height() - ( m_tool->isHidden() ? 0 : m_tool->height() ) );
00175   } else {
00176     m_config->setHeight( 0 );
00177   }
00178   m_config->setPosition( pos() );
00179   
00180 #ifdef Q_WS_X11
00181   NETWinInfo wm_client( QX11Info::display(), winId(),
00182                         QX11Info::appRootWindow(), NET::WMDesktop );
00183   if ( ( wm_client.desktop() == NETWinInfo::OnAllDesktops ) ||
00184      ( wm_client.desktop() > 0 ) ) {
00185     m_config->setDesktop( wm_client.desktop() );
00186   }
00187 #endif
00188   
00189   // actually store the config on disk
00190   m_config->writeConfig();
00191 }
00192 
00193 QString KNote::noteId() const
00194 {
00195   return m_journal->uid();
00196 }
00197 
00198 QString KNote::name() const
00199 {
00200   return m_label->text();
00201 }
00202 
00203 QString KNote::text() const
00204 {
00205   return m_editor->text();
00206 }
00207 
00208 void KNote::setName( const QString& name )
00209 {
00210   m_label->setText( name );
00211   updateLabelAlignment();
00212   
00213   if ( m_editor ) {    // not called from CTOR?
00214     saveData();
00215   }
00216 #ifdef Q_WS_X11
00217   // set the window's name for the taskbar entry to be more helpful (#58338)
00218   NETWinInfo note_win( QX11Info::display(), winId(), QX11Info::appRootWindow(),
00219                        NET::WMDesktop );
00220   note_win.setName( name.toUtf8() );
00221 #endif
00222   
00223   emit sigNameChanged();
00224 }
00225 
00226 void KNote::setText( const QString& text )
00227 {
00228   m_editor->setText( text );
00229   
00230   saveData();
00231 }
00232 
00233 void KNote::find( const QString& pattern, long options )
00234 {
00235   delete m_find;
00236   m_find = new KFind( pattern, options, this );
00237   
00238   connect( m_find, SIGNAL( highlight( const QString &, int, int ) ),
00239            this, SLOT( slotHighlight( const QString &, int, int ) ) );
00240   connect( m_find, SIGNAL( findNext() ), this, SLOT( slotFindNext() ) );
00241   
00242   m_find->setData( m_editor->toPlainText() );
00243   slotFindNext();
00244 }
00245 
00246 void KNote::slotFindNext()
00247 {
00248   // TODO: honor FindBackwards
00249   // TODO: dialogClosed() -> delete m_find
00250 
00251   // Let KFind inspect the text fragment, and display a dialog if a match is
00252   // found
00253   KFind::Result res = m_find->find();
00254   
00255   if ( res == KFind::NoMatch ) { // i.e. at end-pos
00256     // use a different text cursor!
00257     m_editor->textCursor().clearSelection();
00258     emit sigFindFinished();
00259     delete m_find;
00260     m_find = 0;
00261   } else {
00262     show();
00263 #ifdef Q_WS_X11
00264     KWindowSystem::setCurrentDesktop( KWindowSystem::windowInfo( winId(),
00265                                       NET::WMDesktop ).desktop() );
00266 #endif
00267   }
00268 }
00269 
00270 void KNote::slotHighlight( const QString& /*str*/, int idx, int len )
00271 {
00272   QTextCursor c = m_editor->textCursor();
00273   c.setPosition( idx );
00274   c.setPosition( idx + len, QTextCursor::KeepAnchor );
00275   // TODO: modify the selection color, use a different QTextCursor?
00276 }
00277 
00278 bool KNote::isModified() const
00279 {
00280   return m_editor->document()->isModified();
00281 }
00282 
00283 // ------------------ private slots (menu actions) ------------------ //
00284 
00285 void KNote::slotRename()
00286 {
00287   // pop up dialog to get the new name
00288   bool ok;
00289   QString newName = KInputDialog::getText( QString::null,
00290     //krazy:exclude=nullstrassign for old broken gcc
00291     i18n( "Please enter the new name:" ), m_label->text(), &ok, this );
00292   
00293   if ( !ok ) { // handle cancel
00294     return;
00295   }
00296   
00297   setName( newName );
00298 }
00299 
00300 void KNote::slotUpdateReadOnly()
00301 {
00302   const bool readOnly = m_readOnly->isChecked();
00303   
00304   m_editor->setReadOnly( readOnly );
00305   m_config->setReadOnly( readOnly );
00306   
00307   // enable/disable actions accordingly
00308   actionCollection()->action( "configure_note" )->setEnabled( !readOnly );
00309   actionCollection()->action( "insert_date" )->setEnabled( !readOnly );
00310   actionCollection()->action( "delete_note" )->setEnabled( !readOnly );
00311   
00312   actionCollection()->action( "edit_undo" )->setEnabled( !readOnly &&
00313                               m_editor->document()->isUndoAvailable() );
00314   actionCollection()->action( "edit_redo" )->setEnabled( !readOnly &&
00315                               m_editor->document()->isRedoAvailable() );
00316   actionCollection()->action( "edit_cut" )->setEnabled( !readOnly &&
00317                               m_editor->textCursor().hasSelection() );
00318   actionCollection()->action( "edit_paste" )->setEnabled( !readOnly );
00319   actionCollection()->action( "edit_clear" )->setEnabled( !readOnly );
00320   
00321   updateFocus();
00322 }
00323 
00324 void KNote::slotClose()
00325 {
00326 #ifdef Q_WS_X11
00327   NETWinInfo wm_client( QX11Info::display(), winId(),
00328                         QX11Info::appRootWindow(), NET::WMDesktop );
00329   if ( ( wm_client.desktop() == NETWinInfo::OnAllDesktops ) ||
00330        ( wm_client.desktop() > 0 ) ) {
00331     m_config->setDesktop( wm_client.desktop() );
00332   }
00333 #endif
00334   
00335   m_editor->clearFocus();
00336   m_config->setHideNote( true );
00337   m_config->setPosition( pos() );
00338   
00339   // just hide the note so it's still available from the dock window
00340   hide();
00341 }
00342 
00343 void KNote::slotInsDate()
00344 {
00345   m_editor->insertPlainText(
00346     KGlobal::locale()->formatDateTime( QDateTime::currentDateTime() ) );
00347 }
00348 
00349 void KNote::slotSetAlarm()
00350 {
00351   KNoteAlarmDlg dlg( name(), this );
00352   dlg.setIncidence( m_journal );
00353   
00354   if ( dlg.exec() == QDialog::Accepted ) {
00355     emit sigDataChanged();
00356   }
00357 }
00358 
00359 void KNote::slotPreferences()
00360 {
00361   // reuse if possible
00362   if ( KNoteConfigDlg::showDialog( noteId() ) ) {
00363     return;
00364   }
00365   
00366   // create a new preferences dialog...
00367   KNoteConfigDlg *dialog = new KNoteConfigDlg( m_config, name(), this, noteId() );
00368   connect( dialog, SIGNAL( settingsChanged( const QString & ) ) , this,
00369            SLOT( slotApplyConfig() ) );
00370   connect( this, SIGNAL( sigNameChanged() ), dialog,
00371            SLOT( slotUpdateCaption() ) );
00372            dialog->show();
00373 }
00374 
00375 void KNote::slotSend()
00376 {
00377   // pop up dialog to get the IP
00378   KNoteHostDlg hostDlg( i18n( "Send \"%1\"", name() ), this );
00379   bool ok = ( hostDlg.exec() == QDialog::Accepted );
00380   QString host = hostDlg.host();
00381   
00382   if ( !ok ) { // handle cancel
00383     return;
00384   }
00385   
00386   if ( host.isEmpty() ) {
00387     KMessageBox::sorry( this, i18n( "The host cannot be empty." ) );
00388     return;
00389   }
00390   
00391   // Send the note
00392   
00393   KNotesNetworkSender *sender = new KNotesNetworkSender(
00394     KSocketFactory::connectToHost( "knotes", host,
00395                                    KNotesGlobalConfig::port() ) );
00396   sender->setSenderId( KNotesGlobalConfig::senderID() );
00397   sender->setNote( name(), text() ); // FIXME: plainText ??
00398 }
00399 
00400 void KNote::slotMail()
00401 {
00402   // get the mail action command
00403   QStringList cmd_list = KNotesGlobalConfig::mailAction().split( QChar(' '),
00404       QString::SkipEmptyParts );
00405   
00406   KProcess mail;
00407   foreach ( const QString& cmd, cmd_list ) {
00408     if ( cmd == "%f" ) {
00409       mail << m_editor->toPlainText();
00410     } else if ( cmd == "%t" ) {
00411       mail << m_label->text();
00412     } else {
00413       mail << cmd;
00414     }
00415   }
00416   
00417   if ( !mail.startDetached() ) {
00418     KMessageBox::sorry( this, i18n( "Unable to start the mail process." ) );
00419   }
00420 }
00421 
00422 void KNote::slotPrint()
00423 {
00424   saveData();
00425   QString content;
00426   if ( !Qt::mightBeRichText( m_editor->text() ) ) {
00427     content = Qt::convertFromPlainText( m_editor->text() );
00428   } else {
00429     content = m_editor->text();
00430   }
00431   KNotePrinter printer;
00432   printer.setDefaultFont( m_config->font() );
00433   printer.printNote( name(), content );
00434 }
00435 
00436 void KNote::slotSaveAs()
00437 {
00438   // TODO: where to put pdf file support? In the printer??!??!
00439   
00440   QCheckBox *convert = 0;
00441   
00442   if ( m_editor->acceptRichText() ) {
00443     convert = new QCheckBox( 0 );
00444     convert->setText( i18n( "Save note as plain text" ) );
00445   }
00446   
00447   KUrl url;
00448   KFileDialog dlg( url, QString(), this, convert );
00449   dlg.setOperationMode( KFileDialog::Saving );
00450   dlg.setCaption( i18n( "Save As" ) );
00451   dlg.exec();
00452   
00453   QString fileName = dlg.selectedFile();
00454   if ( fileName.isEmpty() ) {
00455     return;
00456   }
00457   
00458   QFile file( fileName );
00459   
00460   if ( file.exists() &&
00461        KMessageBox::warningContinueCancel( this,
00462           i18n( "<qt>A file named <b>%1</b> already exists.<br />"
00463                 "Are you sure you want to overwrite it?</qt>",
00464                 QFileInfo( file ).fileName() ) ) != KMessageBox::Continue ) {
00465     return;
00466   }
00467   
00468   if ( file.open( QIODevice::WriteOnly ) ) {
00469     QTextStream stream( &file );
00470     if ( convert && !convert->isChecked() ) {
00471       stream << m_editor->toHtml();
00472     } else {
00473       stream << m_editor->toPlainText();
00474     }
00475   }
00476 }
00477 
00478 void KNote::slotPopupActionToDesktop( int id )
00479 {
00480   toDesktop( id - 1 ); // compensate for the menu separator, -1 == all desktops
00481 }
00482 
00483 
00484 // ------------------ private slots (configuration) ------------------ //
00485 
00486 void KNote::slotApplyConfig()
00487 {
00488   m_label->setFont( m_config->titleFont() );
00489   m_editor->setRichText( m_config->richText() );
00490   m_editor->setTextFont( m_config->font() );
00491   m_editor->setTabStop( m_config->tabSize() );
00492   m_editor->setAutoIndentMode( m_config->autoIndent() );
00493   
00494   // if called as a slot, save the text, we might have changed the
00495   // text format - otherwise the journal will not be updated
00496   if ( sender() ) {
00497     saveData();
00498   }
00499   
00500   setColor( m_config->fgColor(), m_config->bgColor() );
00501   
00502   updateLayout();
00503   slotUpdateShowInTaskbar();
00504 }
00505 
00506 void KNote::slotUpdateKeepAboveBelow()
00507 {
00508 #ifdef Q_WS_X11
00509   unsigned long state = KWindowInfo( KWindowSystem::windowInfo( winId(), NET::WMState ) ).state();
00510 #else
00511   unsigned long state = 0; // neutral state, TODO
00512 #endif
00513   if ( m_keepAbove->isChecked() ) {
00514     m_config->setKeepAbove( true );
00515     m_config->setKeepBelow( false );
00516     KWindowSystem::setState( winId(), state | NET::KeepAbove );
00517   } else if ( m_keepBelow->isChecked() ) {
00518     m_config->setKeepAbove( false );
00519     m_config->setKeepBelow( true );
00520     KWindowSystem::setState( winId(), state | NET::KeepBelow );
00521   } else {
00522     m_config->setKeepAbove( false );
00523     KWindowSystem::clearState( winId(), NET::KeepAbove );
00524     m_config->setKeepBelow( false );
00525     KWindowSystem::clearState( winId(), NET::KeepBelow );
00526   }
00527 }
00528 
00529 void KNote::slotUpdateShowInTaskbar()
00530 {
00531 #ifdef Q_WS_X11
00532   if ( !m_config->showInTaskbar() ) {
00533     KWindowSystem::setState( winId(), KWindowSystem::windowInfo( winId(),
00534                              NET::WMState ).state() | NET::SkipTaskbar );
00535   } else {
00536     KWindowSystem::clearState( winId(), NET::SkipTaskbar );
00537   }
00538 #endif
00539 }
00540 
00541 void KNote::slotUpdateDesktopActions()
00542 {
00543 #ifdef Q_WS_X11
00544   NETRootInfo wm_root( QX11Info::display(), NET::NumberOfDesktops |
00545                        NET::DesktopNames );
00546   NETWinInfo wm_client( QX11Info::display(), winId(),
00547                         QX11Info::appRootWindow(), NET::WMDesktop );
00548   
00549   QStringList desktops;
00550   desktops.append( i18n( "&All Desktops" ) );
00551   desktops.append( QString::null );           // Separator
00552                                               // krazy:exclude=nullstrassign
00553                                               // for old broken gcc
00554   
00555   int count = wm_root.numberOfDesktops();
00556   for ( int n = 1; n <= count; n++ ) {
00557     desktops.append( QString( "&%1 %2" ).arg( n ).arg(
00558       QString::fromUtf8( wm_root.desktopName( n ) ) ) );
00559   }
00560   m_toDesktop->setItems( desktops );
00561   
00562   if ( wm_client.desktop() == NETWinInfo::OnAllDesktops ) {
00563     m_toDesktop->setCurrentItem( 0 );
00564   } else {
00565     m_toDesktop->setCurrentItem( wm_client.desktop() + 1 ); // compensate for
00566                                                             // separator (+1)
00567   }
00568 #endif
00569 }
00570 
00571 
00572 // -------------------- private methods -------------------- //
00573 
00574 void KNote::buildGui()
00575 {
00576   createNoteHeader();  
00577   createNoteEditor();
00578   
00579   KXMLGUIBuilder builder( this );
00580   KXMLGUIFactory factory( &builder, this );
00581   factory.addClient( this );
00582   
00583   m_menu = dynamic_cast<KMenu*>( factory.container( "note_context", this ) );
00584   m_editor->setContextMenu( dynamic_cast<KMenu *>(
00585                               factory.container( "note_edit", this ) ) );
00586   m_tool = dynamic_cast<KToolBar*>( factory.container( "note_tool", this ) );
00587 
00588   createNoteFooter();
00589 }
00590 
00591 void KNote::createActions()
00592 {
00593   // create the menu items for the note - not the editor...
00594   // rename, mail, print, save as, insert date, alarm, close, delete, new note
00595   KAction *action;
00596 
00597   action  = new KAction( KIcon( "document-new" ), i18n( "New" ),  this );
00598   actionCollection()->addAction( "new_note", action );
00599   connect( action, SIGNAL( triggered( bool ) ), SIGNAL( sigRequestNewNote() ) );
00600 
00601   action  = new KAction( KIcon( "edit-rename" ), i18n( "Rename..." ), this );
00602   actionCollection()->addAction( "rename_note", action );
00603   connect( action, SIGNAL( triggered( bool ) ), SLOT( slotRename() ) );
00604 
00605   m_readOnly  = new KToggleAction( KIcon( "system-lock-screen" ),
00606                                    i18n( "Lock" ), this );
00607   actionCollection()->addAction( "lock_note", m_readOnly );
00608   connect( m_readOnly, SIGNAL( triggered( bool ) ),
00609           SLOT( slotUpdateReadOnly() ) );
00610   m_readOnly->setCheckedState( KGuiItem( i18n( "Unlock" ), "unlock" ) );
00611 
00612   action  = new KAction( KIcon( "window-close" ), i18n( "Hide" ), this );
00613   actionCollection()->addAction( "hide_note", action );
00614   connect( action, SIGNAL( triggered( bool ) ), SLOT( slotClose() ) );
00615   action->setShortcut( QKeySequence( Qt::Key_Escape ) );
00616 
00617   action  = new KAction( KIcon( "edit-delete" ), i18n( "Delete" ), this );
00618   actionCollection()->addAction( "delete_note", action );
00619   connect( action, SIGNAL( triggered( bool ) ), SLOT( slotKill() ) );
00620   
00621   action  = new KAction( KIcon( "knotes_date" ), i18n( "Insert Date" ), this );
00622   actionCollection()->addAction( "insert_date", action );
00623   connect( action, SIGNAL( triggered( bool ) ), SLOT( slotInsDate() ) );
00624 
00625   action  = new KAction( KIcon( "knotes_alarm" ), i18n( "Set Alarm..." ),
00626                          this );
00627   actionCollection()->addAction( "set_alarm", action );
00628   connect( action, SIGNAL( triggered( bool ) ), SLOT( slotSetAlarm() ) );
00629   
00630   action  = new KAction( KIcon( "network-wired" ), i18n( "Send..." ), this );
00631   actionCollection()->addAction( "send_note", action );
00632   connect( action, SIGNAL( triggered( bool ) ), SLOT( slotSend() ) );
00633 
00634   action  = new KAction( KIcon( "mail-send" ), i18n( "Mail..." ), this );
00635   actionCollection()->addAction( "mail_note", action );
00636   connect( action, SIGNAL( triggered( bool ) ), SLOT( slotMail() ) );
00637 
00638   action  = new KAction( KIcon( "document-save-as" ), i18n( "Save As..." ),
00639                                 this );
00640   actionCollection()->addAction( "save_note", action );
00641   connect( action, SIGNAL( triggered( bool ) ), SLOT( slotSaveAs() ) );
00642   actionCollection()->addAction( KStandardAction::Print,  "print_note", this,
00643                                  SLOT( slotPrint() ) );
00644 
00645   action  = new KAction( KIcon( "configure" ), i18n( "Preferences..." ), this );
00646   actionCollection()->addAction( "configure_note", action );
00647   connect( action, SIGNAL( triggered( bool ) ), SLOT( slotPreferences() ) );
00648   
00649   QActionGroup *kab = new QActionGroup( this );
00650   kab->setExclusive( true );
00651   
00652   m_keepAbove  = new KToggleAction( KIcon( "go-up" ),
00653                                     i18n( "Keep Above Others" ), this );
00654   actionCollection()->addAction( "keep_above", m_keepAbove );
00655   connect( m_keepAbove, SIGNAL( triggered( bool ) ),
00656            SLOT( slotUpdateKeepAboveBelow() ) );
00657   kab->addAction( m_keepAbove );
00658   
00659   m_keepBelow  = new KToggleAction( KIcon( "go-down" ),
00660                                     i18n( "Keep Below Others" ), this );
00661   actionCollection()->addAction( "keep_below", m_keepBelow );
00662   connect( m_keepBelow, SIGNAL( triggered( bool ) ),
00663            SLOT( slotUpdateKeepAboveBelow() ) );
00664   kab->addAction( m_keepBelow );
00665   
00666 #ifdef Q_WS_X11
00667   m_toDesktop  = new KSelectAction( i18n( "To Desktop" ), this );
00668   actionCollection()->addAction( "to_desktop", m_toDesktop );
00669   connect( m_toDesktop, SIGNAL( triggered( int ) ),
00670            SLOT( slotPopupActionToDesktop( int ) ) );
00671   connect( m_toDesktop->menu(), SIGNAL( aboutToShow() ),
00672            SLOT( slotUpdateDesktopActions() ) );
00673 #endif
00674   
00675   // invisible action to walk through the notes to make this configurable
00676   action  = new KAction( i18n( "Walk Through Notes" ), this );
00677   actionCollection()->addAction( "walk_notes", action );
00678   connect( action, SIGNAL( triggered( bool ) ), SIGNAL( sigShowNextNote() ) );
00679   action->setShortcut( QKeySequence( Qt::SHIFT + Qt::Key_Backtab ) );
00680 
00681   actionCollection()->addAssociatedWidget( this );
00682   foreach (QAction* action, actionCollection()->actions())
00683     action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
00684 }
00685 
00686 void KNote::createNoteHeader()
00687 {
00688   // load style configuration
00689   KConfigGroup styleGroup( m_kwinConf, "Style" );
00690   
00691   QBoxLayout::Direction headerLayoutDirection = QBoxLayout::LeftToRight;
00692   
00693   if ( styleGroup.readEntry( "CustomButtonPositions", false ) ) {
00694     if ( styleGroup.readEntry( "ButtonsOnLeft" ).contains( 'X' ) ) {
00695       headerLayoutDirection = QBoxLayout::RightToLeft;
00696     }
00697   }
00698   
00699   QBoxLayout *headerLayout = new QBoxLayout( headerLayoutDirection, this );
00700   
00701   
00702   // create header label
00703   m_label = new QLabel( this );
00704   headerLayout->addWidget( m_label );
00705   m_label->setFrameStyle( NoFrame );
00706   m_label->setBackgroundRole( QPalette::Base );
00707   m_label->setLineWidth( 0 );
00708   m_label->setAutoFillBackground( true );
00709   m_label->installEventFilter( this );  // receive events ( for dragging &
00710                                         // action menu )
00711   setName( m_journal->summary() );      // don't worry, no signals are
00712                                         // connected at this stage yet
00713   m_button = new KNoteButton( "knotes_close", this );
00714   headerLayout->addWidget( m_button );
00715   
00716   connect( m_button, SIGNAL( clicked() ), this, SLOT( slotClose() ) );
00717   
00718   m_noteLayout->addItem( headerLayout );
00719 }
00720 
00721 void KNote::createNoteEditor()
00722 {
00723   m_editor = new KNoteEdit( actionCollection(), this );
00724   m_noteLayout->addWidget( m_editor );
00725   m_editor->installEventFilter( this ); // receive focus events for modified
00726   setFocusProxy( m_editor );  
00727 }
00728 
00729 void KNote::createNoteFooter()
00730 {
00731   if ( m_tool ) {
00732     m_tool->setIconSize( QSize( 10, 10 ) );
00733     m_tool->setFixedHeight( 24 );
00734     m_tool->setToolButtonStyle( Qt::ToolButtonIconOnly );
00735   }
00736   
00737   // create size grip
00738   QHBoxLayout *gripLayout = new QHBoxLayout( this );
00739   m_grip = new QSizeGrip( this );
00740   m_grip->setFixedSize( m_grip->sizeHint() );
00741   
00742   if ( m_tool ) {
00743     gripLayout->addWidget( m_tool );
00744     gripLayout->setAlignment( m_tool, Qt::AlignBottom | Qt::AlignLeft );
00745     m_tool->hide();
00746   }
00747   
00748   gripLayout->addWidget( m_grip );
00749   gripLayout->setAlignment( m_grip, Qt::AlignBottom | Qt::AlignRight );
00750   m_noteLayout->addItem( gripLayout );
00751   
00752   // if there was just a way of making KComboBox adhere the toolbar height...
00753   foreach ( KComboBox *combo, m_tool->findChildren<KComboBox *>() ) {
00754     QFont font = combo->font();
00755     font.setPointSize( 7 );
00756     combo->setFont( font );
00757     combo->setFixedHeight( 14 );
00758   }
00759 }
00760 
00761 void KNote::prepare()
00762 {
00763   // the config file location
00764   QString configFile = KGlobal::dirs()->saveLocation( "appdata", "notes/" );
00765   configFile += m_journal->uid();
00766   
00767   // no config file yet? -> use the default display config if available
00768   // we want to write to configFile, so use "false"
00769   bool newNote = !KIO::NetAccess::exists( KUrl( configFile ),
00770                                           KIO::NetAccess::DestinationSide, 0 );
00771   
00772   m_config = new KNoteConfig( KSharedConfig::openConfig( configFile,
00773                                                          KConfig::NoGlobals ) );
00774   m_config->readConfig();
00775   m_config->setVersion( KNOTES_VERSION );
00776   
00777   if ( newNote ) {
00778     // until kdelibs provides copying of KConfigSkeletons (KDE 3.4)
00779     KNotesGlobalConfig *globalConfig = KNotesGlobalConfig::self();
00780     m_config->setBgColor( globalConfig->bgColor() );
00781     m_config->setFgColor( globalConfig->fgColor() );
00782     m_config->setWidth( globalConfig->width() );
00783     m_config->setHeight( globalConfig->height() );
00784     
00785     m_config->setFont( globalConfig->font() );
00786     m_config->setTitleFont( globalConfig->titleFont() );
00787     m_config->setAutoIndent( globalConfig->autoIndent() );
00788     m_config->setRichText( globalConfig->richText() );
00789     m_config->setTabSize( globalConfig->tabSize() );
00790     m_config->setReadOnly( globalConfig->readOnly() );
00791     
00792     m_config->setDesktop( globalConfig->desktop() );
00793     m_config->setHideNote( globalConfig->hideNote() );
00794     m_config->setPosition( globalConfig->position() );
00795     m_config->setShowInTaskbar( globalConfig->showInTaskbar() );
00796     m_config->setRememberDesktop( globalConfig->rememberDesktop() );
00797     m_config->setKeepAbove( globalConfig->keepAbove() );
00798     m_config->setKeepBelow( globalConfig->keepBelow() );
00799     
00800     m_config->writeConfig();
00801   }
00802   
00803   // set up the look&feel of the note
00804   setFrameStyle( Panel | Raised );
00805   setMinimumSize( 20, 20 );
00806   setBackgroundRole( QPalette::Base );
00807   
00808   m_editor->setContentsMargins( 0, 0, 0, 0 );
00809   m_editor->setBackgroundRole( QPalette::Base );
00810   m_editor->setFrameStyle( NoFrame );
00811   m_editor->setText( m_journal->description() );
00812   m_editor->document()->setModified( false );
00813   
00814   // load the display configuration of the note
00815   uint width = m_config->width();
00816   uint height = m_config->height();
00817   resize( width, height );
00818   
00819   // let KWin do the placement if the position is illegal--at least 10 pixels
00820   // of a note need to be visible
00821   const QPoint& position = m_config->position();
00822   QRect desk = kapp->desktop()->rect();
00823   desk.adjust( 10, 10, -10, -10 );
00824   if ( desk.intersects( QRect( position, QSize( width, height ) ) ) ) {
00825     move( position );           // do before calling show() to avoid flicker
00826   }
00827   
00828   // config items in the journal have priority
00829   QString property = m_journal->customProperty( "KNotes", "FgColor" );
00830   if ( !property.isNull() ) {
00831     m_config->setFgColor( QColor( property ) );
00832   } else {
00833     m_journal->setCustomProperty( "KNotes", "FgColor",
00834                                   m_config->fgColor().name() );
00835   }
00836   
00837   property = m_journal->customProperty( "KNotes", "BgColor" );
00838   if ( !property.isNull() ) {
00839     m_config->setBgColor( QColor( property ) );
00840   } else {
00841     m_journal->setCustomProperty( "KNotes", "BgColor",
00842                                   m_config->bgColor().name() );
00843   }
00844   property = m_journal->customProperty( "KNotes", "RichText" );
00845   if ( !property.isNull() ) {
00846     m_config->setRichText( property == "true" ? true : false );
00847   } else {
00848     m_journal->setCustomProperty( "KNotes", "RichText",
00849                                   m_config->richText() ? "true" : "false" );
00850   }
00851   
00852   // read configuration settings...
00853   slotApplyConfig();
00854   
00855   // if this is a new note put on current desktop - we can't use defaults
00856   // in KConfig XT since only _changes_ will be stored in the config file
00857   int desktop = m_config->desktop();
00858   
00859 #ifdef Q_WS_X11
00860   if ( ( desktop < 0 && desktop != NETWinInfo::OnAllDesktops ) ||
00861        !m_config->rememberDesktop() )
00862     desktop = KWindowSystem::currentDesktop();
00863 #endif
00864   
00865   // show the note if desired
00866   if ( desktop != 0 && !m_config->hideNote() ) {
00867     // to avoid flicker, call this before show()
00868     toDesktop( desktop );
00869     show();
00870     
00871     // because KWin forgets about that for hidden windows
00872 #ifdef Q_WS_X11
00873     if ( desktop == NETWinInfo::OnAllDesktops ) {
00874       toDesktop( desktop );
00875     }
00876 #endif
00877   }
00878   
00879   m_readOnly->setChecked( m_config->readOnly() );
00880   slotUpdateReadOnly();
00881   
00882   if ( m_config->keepAbove() ) {
00883     m_keepAbove->setChecked( true );
00884   } else if ( m_config->keepBelow() ) {
00885     m_keepBelow->setChecked( true );
00886   } else {
00887     m_keepAbove->setChecked( false );
00888     m_keepBelow->setChecked( false );
00889   }
00890   slotUpdateKeepAboveBelow();
00891   
00892   // HACK: update the icon color - again after showing the note, to make kicker
00893   // aware of the new colors
00894   KIconEffect effect;
00895   QPixmap icon = effect.apply( qApp->windowIcon().pixmap(
00896                                  IconSize( KIconLoader::Desktop ),
00897                                  IconSize( KIconLoader::Desktop ) ),
00898                                KIconEffect::Colorize,
00899                                1, m_config->bgColor(), false );
00900   QPixmap miniIcon = effect.apply( qApp->windowIcon().pixmap(
00901                                      IconSize( KIconLoader::Small ),
00902                                      IconSize( KIconLoader::Small ) ),
00903                                    KIconEffect::Colorize,
00904                                    1, m_config->bgColor(), false );
00905 #ifdef Q_WS_X11
00906   KWindowSystem::setIcons( winId(), icon, miniIcon );
00907 #endif
00908 }
00909 
00910 void KNote::toDesktop( int desktop )
00911 {
00912   if ( desktop == 0 ) {
00913     return;
00914   }
00915   
00916 #ifdef Q_WS_X11
00917   if ( desktop == NETWinInfo::OnAllDesktops ) {
00918     KWindowSystem::setOnAllDesktops( winId(), true );
00919   } else {
00920     KWindowSystem::setOnDesktop( winId(), desktop );
00921   }
00922 #endif
00923 }
00924 
00925 void KNote::setColor( const QColor &fg, const QColor &bg )
00926 {
00927   QPalette p = palette();
00928   
00929   // better: from light(150) to light(100) to light(75)
00930   // QLinearGradient g( width()/2, 0, width()/2, height() );
00931   // g.setColorAt( 0, bg );
00932   // g.setColorAt( 1, bg.dark(150) );
00933   
00934   p.setColor( QPalette::Window,     bg );
00935   // p.setBrush( QPalette::Window,     g );
00936   p.setColor( QPalette::Base,       bg );
00937   // p.setBrush( QPalette::Base,       g );
00938   
00939   p.setColor( QPalette::WindowText, fg );
00940   p.setColor( QPalette::Text,       fg );
00941   
00942   p.setColor( QPalette::Button,     bg.dark( 116 ) );
00943   p.setColor( QPalette::ButtonText, fg );
00944   
00945   //p.setColor( QPalette::Highlight,  bg );
00946   //p.setColor( QPalette::HighlightedText, fg );
00947   
00948   // order: Light, Midlight, Button, Mid, Dark, Shadow
00949   
00950   // the shadow
00951   p.setColor( QPalette::Light, bg.light( 180 ) );
00952   p.setColor( QPalette::Midlight, bg.light( 150 ) );
00953   p.setColor( QPalette::Mid, bg.light( 150 ) );
00954   p.setColor( QPalette::Dark, bg.dark( 108 ) );
00955   p.setColor( QPalette::Shadow, bg.dark( 116 ) );
00956   
00957   setPalette( p );
00958   
00959   // darker values for the active label
00960   p.setColor( QPalette::Active, QPalette::Base, bg.dark( 116 ) );
00961   
00962   m_label->setPalette( p );
00963   
00964   // set the text color
00965   m_editor->setTextColor( fg );
00966   
00967   // update the icon color
00968   KIconEffect effect;
00969   QPixmap icon = effect.apply( qApp->windowIcon().pixmap(
00970                                  IconSize( KIconLoader::Desktop ),
00971                                  IconSize( KIconLoader::Desktop ) ),
00972                                KIconEffect::Colorize, 1, bg, false );
00973   QPixmap miniIcon = effect.apply( qApp->windowIcon().pixmap(
00974                                      IconSize( KIconLoader::Small ),
00975                                      IconSize( KIconLoader::Small ) ),
00976                                    KIconEffect::Colorize, 1, bg, false );
00977 #ifdef Q_WS_X11
00978   KWindowSystem::setIcons( winId(), icon, miniIcon );
00979 #endif
00980   // update the color of the title
00981   updateFocus();
00982   emit sigColorChanged();
00983 }
00984 
00985 void KNote::updateLabelAlignment()
00986 {
00987   // if the name is too long to fit, left-align it, otherwise center it (#59028)
00988   QString labelText = m_label->text();
00989   if ( m_label->fontMetrics().boundingRect( labelText ).width() >
00990        m_label->width() ) {
00991     m_label->setAlignment( Qt::AlignLeft );
00992   } else {
00993     m_label->setAlignment( Qt::AlignHCenter );
00994   }
00995 }
00996 
00997 void KNote::updateFocus()
00998 {
00999   if ( hasFocus() ) {
01000     m_button->show();
01001     m_grip->show();
01002     
01003     if ( !m_editor->isReadOnly() ) {
01004       if ( m_tool && m_tool->isHidden() && m_editor->acceptRichText() ) {
01005         m_tool->show();
01006         setGeometry( x(), y(), width(), height() + m_tool->height() );
01007       }
01008     } else if ( m_tool && !m_tool->isHidden() ) {
01009       m_tool->hide();
01010       setGeometry( x(), y(), width(), height() - m_tool->height() );
01011       updateLayout();     // to update the minimum height
01012     }
01013   } else {
01014     m_button->hide();
01015     m_grip->hide();
01016     
01017     if ( m_tool && !m_tool->isHidden() ) {
01018       m_tool->hide();
01019       setGeometry( x(), y(), width(), height() - m_tool->height() );
01020       updateLayout();     // to update the minimum height
01021     }
01022   }
01023 }
01024 
01025 void KNote::updateLayout()
01026 {
01027   // TODO: remove later if no longer needed.
01028   updateLabelAlignment();
01029 }
01030 
01031 // -------------------- protected methods -------------------- //
01032 
01033 void KNote::contextMenuEvent( QContextMenuEvent *e )
01034 {
01035   if ( m_menu ) {
01036     m_menu->popup( e->globalPos() );
01037   }
01038 }
01039 
01040 void KNote::showEvent( QShowEvent * )
01041 {
01042   if ( m_config->hideNote() ) {
01043     // KWin does not preserve these properties for hidden windows
01044     slotUpdateKeepAboveBelow();
01045     slotUpdateShowInTaskbar();
01046     toDesktop( m_config->desktop() );
01047     move( m_config->position() );
01048     m_config->setHideNote( false );
01049   }
01050 }
01051 
01052 void KNote::resizeEvent( QResizeEvent *qre )
01053 {
01054   QFrame::resizeEvent( qre );
01055   updateLayout();
01056 }
01057 
01058 void KNote::closeEvent( QCloseEvent * )
01059 {
01060   slotClose();
01061 }
01062 
01063 void KNote::dragEnterEvent( QDragEnterEvent *e )
01064 {
01065   if ( !m_config->readOnly() ) {
01066     e->setAccepted( e->mimeData()->hasColor() );
01067   }
01068 }
01069 
01070 void KNote::dropEvent( QDropEvent *e )
01071 {
01072   if ( m_config->readOnly() ) {
01073     return;
01074   }
01075   
01076   const QMimeData *md = e->mimeData();
01077   if ( md->hasColor() ) {
01078        QColor bg =  qvariant_cast<QColor>( md->colorData() );
01079        setColor( palette().color( foregroundRole() ), bg );
01080        m_journal->setCustomProperty( "KNotes", "BgColor", bg.name() );
01081        m_config->setBgColor( bg );
01082   }
01083 }
01084 
01085 bool KNote::event( QEvent *ev )
01086 {
01087   if ( ev->type() == QEvent::LayoutRequest ) {
01088     updateLayout();
01089     return true;
01090   } else {
01091     return QFrame::event( ev );
01092   }
01093 }
01094 
01095 bool KNote::eventFilter( QObject *o, QEvent *ev )
01096 {
01097   if ( ev->type() == QEvent::DragEnter &&
01098     static_cast<QDragEnterEvent*>( ev )->mimeData()->hasColor() ) {
01099     dragEnterEvent( static_cast<QDragEnterEvent *>( ev ) );
01100     return true;
01101   }
01102   
01103   if ( ev->type() == QEvent::Drop &&
01104        static_cast<QDropEvent *>( ev )->mimeData()->hasColor() ) {
01105     dropEvent( static_cast<QDropEvent *>( ev ) );
01106     return true;
01107   }
01108   
01109   if ( o == m_label ) {
01110     QMouseEvent *e = ( QMouseEvent * )ev;
01111     
01112     if ( ev->type() == QEvent::MouseButtonDblClick ) {
01113       slotRename();
01114     }
01115     
01116     if ( ev->type() == QEvent::MouseButtonPress &&
01117         ( e->button() == Qt::LeftButton || e->button() == Qt::MidButton ) ) {
01118 #ifdef Q_WS_X11
01119       e->button() == Qt::LeftButton ? KWindowSystem::raiseWindow( winId() )
01120                                     : KWindowSystem::lowerWindow( winId() );
01121       
01122       XUngrabPointer( QX11Info::display(), QX11Info::appTime() );
01123       NETRootInfo wm_root( QX11Info::display(), NET::WMMoveResize );
01124       wm_root.moveResizeRequest( winId(), e->globalX(), e->globalY(),
01125                                  NET::Move );
01126 #endif
01127       return true;
01128     }
01129     
01130     if ( ev->type() == QEvent::MouseButtonRelease ) {
01131 #ifdef Q_WS_X11
01132         NETRootInfo wm_root( QX11Info::display(), NET::WMMoveResize );
01133         wm_root.moveResizeRequest( winId(), e->globalX(), e->globalY(),
01134                                    NET::MoveResizeCancel );
01135 #endif
01136         return false;
01137     }
01138     
01139     return false;
01140   }
01141   
01142   if ( o == m_editor ) {
01143     if ( ev->type() == QEvent::FocusOut ) {
01144           QFocusEvent *fe = static_cast<QFocusEvent *>( ev );
01145           if ( fe->reason() != Qt::PopupFocusReason &&
01146                fe->reason() != Qt::MouseFocusReason ) {
01147             updateFocus();
01148             if ( isModified() ) {
01149               saveData();
01150             }
01151           }
01152     } else if ( ev->type() == QEvent::FocusIn ) {
01153       updateFocus();
01154     }
01155     
01156     return false;
01157   }
01158   
01159   return false;
01160 }
01161 
01162 
01163 #include "knote.moc"

knotes

Skip menu "knotes"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members