knotes Library API Documentation

knote.cpp

00001 /*******************************************************************
00002  KNotes -- Notes for the KDE project
00003 
00004  Copyright (c) 1997-2004, 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00019 *******************************************************************/
00020 
00021 #include <qlabel.h>
00022 #include <qsize.h>
00023 #include <qsizegrip.h>
00024 #include <qbitmap.h>
00025 #include <qcursor.h>
00026 #include <qpainter.h>
00027 #include <qpaintdevicemetrics.h>
00028 #include <qsimplerichtext.h>
00029 #include <qobjectlist.h>
00030 #include <qfile.h>
00031 #include <qcheckbox.h>
00032 
00033 #include <kapplication.h>
00034 #include <kdebug.h>
00035 #include <kaction.h>
00036 #include <kstdaction.h>
00037 #include <kcombobox.h>
00038 #include <ktoolbar.h>
00039 #include <kpopupmenu.h>
00040 #include <kxmlguibuilder.h>
00041 #include <kxmlguifactory.h>
00042 #include <kcolordrag.h>
00043 #include <kiconeffect.h>
00044 #include <kprinter.h>
00045 #include <klocale.h>
00046 #include <kstandarddirs.h>
00047 #include <kmessagebox.h>
00048 #include <kprocess.h>
00049 #include <kinputdialog.h>
00050 #include <kmdcodec.h>
00051 #include <kglobalsettings.h>
00052 #include <kfiledialog.h>
00053 #include <kio/netaccess.h>
00054 
00055 #include <libkcal/journal.h>
00056 
00057 #include "knote.h"
00058 #include "knotebutton.h"
00059 #include "knoteedit.h"
00060 #include "knoteconfig.h"
00061 #include "knotesglobalconfig.h"
00062 #include "knoteconfigdlg.h"
00063 #include "knotehostdlg.h"
00064 #include "knotesnetsend.h"
00065 #include "version.h"
00066 
00067 #include <kwin.h>
00068 #include <netwm.h>
00069 
00070 #include <fixx11h.h>
00071 
00072 using namespace KCal;
00073 
00074 extern Time qt_x_time;
00075 
00076 
00077 KNote::KNote( QDomDocument buildDoc, Journal *j, QWidget *parent, const char *name )
00078   : QFrame( parent, name, WStyle_Customize | WStyle_NoBorder | WDestructiveClose ),
00079     m_label( 0 ), m_button( 0 ), m_tool( 0 ), m_editor( 0 ), m_config( 0 ),
00080     m_journal( j ), m_kwinConf( KSharedConfig::openConfig( "kwinrc", true ) )
00081 {
00082     // be explicit
00083     KWin::setIcons( winId(), kapp->icon(), kapp->miniIcon() );
00084 
00085     setAcceptDrops( true );
00086     actionCollection()->setWidget( this );
00087 
00088     setDOMDocument( buildDoc );
00089 
00090     // just set the name of the file to save the actions to, do NOT reparse it
00091     setXMLFile( instance()->instanceName() + "ui.rc", false, false );
00092 
00093     // if there is no title yet, use the start date if valid
00094     // (KOrganizer's journals don't have titles but a valid start date)
00095     if ( m_journal->summary().isNull() && m_journal->dtStart().isValid() )
00096     {
00097         QString s = KGlobal::locale()->formatDateTime( m_journal->dtStart() );
00098         m_journal->setSummary( s );
00099     }
00100 
00101     // create the menu items for the note - not the editor...
00102     // rename, mail, print, save as, insert date, close, delete, new note
00103     new KAction( i18n("New"), "filenew", 0,
00104         this, SIGNAL(sigRequestNewNote()), actionCollection(), "new_note" );
00105     new KAction( i18n("Rename..."), "text", 0,
00106         this, SLOT(slotRename()), actionCollection(), "rename_note" );
00107     new KAction( i18n("Hide"), "fileclose" , Key_Escape,
00108         this, SLOT(slotClose()), actionCollection(), "hide_note" );
00109     new KAction( i18n("Delete"), "knotes_delete", 0,
00110         this, SLOT(slotKill()), actionCollection(), "delete_note" );
00111 
00112     new KAction( i18n("Insert Date"), "knotes_date", 0 ,
00113         this, SLOT(slotInsDate()), actionCollection(), "insert_date" );
00114     new KAction( i18n("Send..."), "network", 0,
00115         this, SLOT(slotSend()), actionCollection(), "send_note" );
00116     new KAction( i18n("Mail..."), "mail_send", 0,
00117         this, SLOT(slotMail()), actionCollection(), "mail_note" );
00118 //    new KAction( i18n("Save As..."), "filesaveas", 0,
00119 //        this, SLOT(slotSaveAs()), actionCollection(), "save_note" );
00120     KStdAction::print( this, SLOT(slotPrint()), actionCollection(), "print_note" );
00121     new KAction( i18n("Preferences..."), "configure", 0,
00122         this, SLOT(slotPreferences()), actionCollection(), "configure_note" );
00123 
00124     m_keepAbove = new KToggleAction( i18n("Keep Above Others"), "up", 0,
00125         this, SLOT(slotUpdateKeepAboveBelow()), actionCollection(), "keep_above" );
00126     m_keepAbove->setExclusiveGroup( "keepAB" );
00127 
00128     m_keepBelow = new KToggleAction( i18n("Keep Below Others"), "down", 0,
00129         this, SLOT(slotUpdateKeepAboveBelow()), actionCollection(), "keep_below" );
00130     m_keepBelow->setExclusiveGroup( "keepAB" );
00131 
00132     m_toDesktop = new KListAction( i18n("To Desktop"), 0,
00133         this, SLOT(slotPopupActionToDesktop(int)), actionCollection(), "to_desktop" );
00134     connect( m_toDesktop->popupMenu(), SIGNAL(aboutToShow()), this, SLOT(slotUpdateDesktopActions()) );
00135 
00136     // invisible action to walk through the notes to make this configurable
00137     new KAction( QString::null, 0, SHIFT+Key_BackTab,
00138                  this, SIGNAL(sigShowNextNote()), actionCollection(), "walk_notes" );
00139 //    new KAction( i18n("Walk Through Notes"), 0, SHIFT+Key_BackTab,
00140 //                 this, SIGNAL(sigShowNextNote()), actionCollection(), "walk_notes" );
00141 
00142     // create the note header, button and label...
00143     m_label = new QLabel( this );
00144     m_label->installEventFilter( this );  // receive events (for dragging & action menu)
00145     setName( m_journal->summary() );      // don't worry, no signals are connected at this stage yet
00146 
00147     m_button = new KNoteButton( "knotes_close", this );
00148     connect( m_button, SIGNAL(clicked()), this, SLOT(slotClose()) );
00149 
00150     // create the note editor
00151     m_editor = new KNoteEdit( actionCollection(), this );
00152     m_editor->installEventFilter( this ); // receive events (for modified)
00153     m_editor->viewport()->installEventFilter( this );
00154 
00155     KXMLGUIBuilder builder( this );
00156     KXMLGUIFactory factory( &builder, this );
00157     factory.addClient( this );
00158 
00159     m_menu = static_cast<KPopupMenu*>(factory.container( "note_context", this ));
00160     m_edit_menu = static_cast<KPopupMenu*>(factory.container( "note_edit", this ));
00161     m_tool = static_cast<KToolBar*>(factory.container( "note_tool", this ));
00162     m_tool->setIconSize( 10 );
00163     m_tool->setFixedHeight( 16 );
00164 
00165     // if there was just a way of making KComboBox adhere the toolbar height...
00166     QObjectList *list = m_tool->queryList( "KComboBox" );
00167     QObjectListIt it( *list );
00168     while ( it.current() != 0 )
00169     {
00170         KComboBox *combo = (KComboBox *)it.current();
00171         QFont font = combo->font();
00172         font.setPointSize( 7 );
00173         combo->setFont( font );
00174         combo->setFixedHeight( 14 );
00175         ++it;
00176     }
00177     delete list;
00178 
00179     m_tool->hide();
00180 
00181     setFocusProxy( m_editor );
00182 
00183     // create the resize handle
00184     m_editor->setCornerWidget( new QSizeGrip( this ) );
00185     uint width = m_editor->cornerWidget()->width();
00186     uint height = m_editor->cornerWidget()->height();
00187     QBitmap mask;
00188     mask.resize( width, height );
00189     mask.fill( color0 );
00190     QPointArray array;
00191     array.setPoints( 3, 0, height, width, height, width, 0 );
00192     QPainter p;
00193     p.begin( &mask );
00194     p.setBrush( color1 );
00195     p.drawPolygon( array );
00196     p.end();
00197     m_editor->cornerWidget()->setMask( mask );
00198 
00199     // set up the look&feel of the note
00200     setMinimumSize( 20, 20 );
00201     setFrameStyle( WinPanel | Raised );
00202     setLineWidth( 1 );
00203 
00204     m_editor->setMargin( 5 );
00205     m_editor->setFrameStyle( NoFrame );
00206     m_editor->setBackgroundMode( PaletteBase );
00207 
00208     // the config file location
00209     QString configFile = KGlobal::dirs()->saveLocation( "appdata", "notes/" );
00210     configFile += m_journal->uid();
00211 
00212     // no config file yet? -> use the default display config if available
00213     // we want to write to configFile, so use "false"
00214     bool newNote = !KIO::NetAccess::exists( KURL::fromPathOrURL( configFile ), false, 0 );
00215 
00216     m_config = new KNoteConfig( KSharedConfig::openConfig( configFile, false, false ) );
00217     m_config->readConfig();
00218     m_config->setVersion( KNOTES_VERSION );
00219 
00220     if ( newNote )
00221     {
00222         // until kdelibs provides copying of KConfigSkeletons (KDE 3.4)
00223         KNotesGlobalConfig *globalConfig = KNotesGlobalConfig::self();
00224         m_config->setBgColor( globalConfig->bgColor() );
00225         m_config->setFgColor( globalConfig->fgColor() );
00226         m_config->setWidth( globalConfig->width() );
00227         m_config->setHeight( globalConfig->height() );
00228 
00229         m_config->setFont( globalConfig->font() );
00230         m_config->setTitleFont( globalConfig->titleFont() );
00231         m_config->setAutoIndent( globalConfig->autoIndent() );
00232         m_config->setRichText( globalConfig->richText() );
00233         m_config->setTabSize( globalConfig->tabSize() );
00234 
00235         m_config->setDesktop( globalConfig->desktop() );
00236         m_config->setHideNote( globalConfig->hideNote() );
00237         m_config->setPosition( globalConfig->position() );
00238         m_config->setShowInTaskbar( globalConfig->showInTaskbar() );
00239         m_config->setKeepAbove( globalConfig->keepAbove() );
00240         m_config->setKeepBelow( globalConfig->keepBelow() );
00241 
00242         m_config->writeConfig();
00243     }
00244 
00245     // load the display configuration of the note
00246     width = m_config->width();
00247     height = m_config->height();
00248     resize( width, height );
00249 
00250     if ( m_config->keepAbove() )
00251         m_keepAbove->setChecked( true );
00252     else if ( m_config->keepBelow() )
00253         m_keepBelow->setChecked( true );
00254     else
00255     {
00256         m_keepAbove->setChecked( false );
00257         m_keepBelow->setChecked( false );
00258     }
00259 
00260     // let KWin do the placement if the position is illegal--at least 10 pixels
00261     // of a note need to be visible
00262     const QPoint& position = m_config->position();
00263     QRect desk = kapp->desktop()->rect();
00264     desk.addCoords( 10, 10, -10, -10 );
00265     if ( desk.intersects( QRect( position, QSize( width, height ) ) ) )
00266         move( position );           // do before calling show() to avoid flicker
00267 
00268     // config items in the journal have priority
00269     QString property = m_journal->customProperty( "KNotes", "FgColor" );
00270     if ( property != QString::null )
00271         m_config->setFgColor( QColor( property ) );
00272     else
00273         m_journal->setCustomProperty( "KNotes", "FgColor", m_config->fgColor().name() );
00274 
00275     property = m_journal->customProperty( "KNotes", "BgColor" );
00276     if ( property != QString::null )
00277         m_config->setBgColor( QColor( property ) );
00278     else
00279         m_journal->setCustomProperty( "KNotes", "BgColor", m_config->bgColor().name() );
00280 
00281     property = m_journal->customProperty( "KNotes", "RichText" );
00282     if ( property != QString::null )
00283         m_config->setRichText( property == "true" ? true : false );
00284     else
00285         m_journal->setCustomProperty( "KNotes", "RichText", m_config->richText() ? "true" : "false" );
00286 
00287     // read configuration settings...
00288     slotApplyConfig();
00289 
00290     // if this is a new note put on current desktop - we can't use defaults
00291     // in KConfig XT since only _changes_ will be stored in the config file
00292     int desktop = m_config->desktop();
00293     if ( desktop < 0 && desktop != NETWinInfo::OnAllDesktops )
00294         desktop = KWin::currentDesktop();
00295 
00296     // show the note if desired
00297     if ( desktop != 0 && !m_config->hideNote() )
00298     {
00299         // to avoid flicker, call this before show()
00300         toDesktop( desktop );
00301         show();
00302 
00303         // because KWin forgets about that for hidden windows
00304         if ( desktop == NETWinInfo::OnAllDesktops )
00305             toDesktop( desktop );
00306     }
00307 
00308     m_editor->setText( m_journal->description() );
00309     m_editor->setModified( false );
00310 }
00311 
00312 KNote::~KNote()
00313 {
00314     delete m_config;
00315 }
00316 
00317 
00318 // -------------------- public slots -------------------- //
00319 
00320 void KNote::slotKill( bool force )
00321 {
00322     if ( !force &&
00323          KMessageBox::warningContinueCancel( this,
00324             i18n("<qt>Do you really want to delete note <b>%1</b>?</qt>")
00325                 .arg( m_label->text() ),
00326             i18n("Confirm Delete"), KGuiItem( i18n("&Delete"), "editdelete") )
00327          != KMessageBox::Continue )
00328     {
00329         return;
00330     }
00331 
00332     // delete the configuration first, then the corresponding file
00333     delete m_config;
00334     m_config = 0;
00335 
00336     QString configFile = KGlobal::dirs()->saveLocation( "appdata", "notes/" );
00337     configFile += m_journal->uid();
00338 
00339     if ( !KIO::NetAccess::del( KURL::fromPathOrURL( configFile ), this ) )
00340         kdError(5500) << "Can't remove the note config: " << configFile << endl;
00341 
00342     emit sigKillNote( m_journal );
00343 }
00344 
00345 
00346 // -------------------- public member functions -------------------- //
00347 
00348 void KNote::saveData()
00349 {
00350     m_journal->setSummary( m_label->text() );
00351     m_journal->setDescription( m_editor->text() );
00352     m_journal->setCustomProperty( "KNotes", "FgColor", paletteForegroundColor().name() );
00353     m_journal->setCustomProperty( "KNotes", "BgColor", paletteBackgroundColor().name() );
00354     m_journal->setCustomProperty( "KNotes", "RichText", m_config->richText() ? "true" : "false" );
00355 
00356     emit sigDataChanged();
00357     m_editor->setModified( false );
00358 }
00359 
00360 void KNote::saveConfig() const
00361 {
00362     m_config->setWidth( width() );
00363     m_config->setHeight( height() - (m_tool->isHidden() ? 0 : m_tool->height()) );
00364     m_config->setPosition( pos() );
00365 
00366     NETWinInfo wm_client( qt_xdisplay(), winId(), qt_xrootwin(), NET::WMDesktop );
00367     if ( wm_client.desktop() == NETWinInfo::OnAllDesktops || wm_client.desktop() > 0 )
00368         m_config->setDesktop( wm_client.desktop() );
00369 
00370     // actually store the config on disk
00371     m_config->writeConfig();
00372 }
00373 
00374 QString KNote::noteId() const
00375 {
00376     return m_journal->uid();
00377 }
00378 
00379 QString KNote::name() const
00380 {
00381     return m_label->text();
00382 }
00383 
00384 QString KNote::text() const
00385 {
00386     return m_editor->text();
00387 }
00388 
00389 void KNote::setName( const QString& name )
00390 {
00391     m_label->setText( name );
00392     updateLabelAlignment();
00393 
00394     if ( m_editor )    // not called from CTOR?
00395         saveData();
00396 
00397     // set the window's name for the taskbar entry to be more helpful (#58338)
00398     NETWinInfo note_win( qt_xdisplay(), winId(), qt_xrootwin(), NET::WMDesktop );
00399     note_win.setName( name.utf8() );
00400 
00401     emit sigNameChanged();
00402 }
00403 
00404 void KNote::setText( const QString& text )
00405 {
00406     m_editor->setText( text );
00407     saveData();
00408 }
00409 
00410 bool KNote::isModified() const
00411 {
00412     return m_editor->isModified();
00413 }
00414 
00415 // FIXME KDE 4.0: remove sync(), isNew() and isModified()
00416 void KNote::sync( const QString& app )
00417 {
00418     QByteArray sep( 1 );
00419     sep[0] = '\0';
00420 
00421     KMD5 hash;
00422     QCString result;
00423 
00424     hash.update( m_label->text().utf8() );
00425     hash.update( sep );
00426     hash.update( m_editor->text().utf8() );
00427     hash.hexDigest( result );
00428 
00429     // hacky... not possible with KConfig XT
00430     KConfig *config = m_config->config();
00431     config->setGroup( "Synchronisation" );
00432     config->writeEntry( app, result.data() );
00433 }
00434 
00435 bool KNote::isNew( const QString& app ) const
00436 {
00437     KConfig *config = m_config->config();
00438     config->setGroup( "Synchronisation" );
00439     QString hash = config->readEntry( app );
00440     return hash.isEmpty();
00441 }
00442 
00443 bool KNote::isModified( const QString& app ) const
00444 {
00445     QByteArray sep( 1 );
00446     sep[0] = '\0';
00447 
00448     KMD5 hash;
00449     hash.update( m_label->text().utf8() );
00450     hash.update( sep );
00451     hash.update( m_editor->text().utf8() );
00452     hash.hexDigest();
00453 
00454     KConfig *config = m_config->config();
00455     config->setGroup( "Synchronisation" );
00456     QString orig = config->readEntry( app );
00457 
00458     if ( hash.verify( orig.utf8() ) )   // returns false on error!
00459         return false;
00460     else
00461         return true;
00462 }
00463 
00464 
00465 // ------------------ private slots (menu actions) ------------------ //
00466 
00467 void KNote::slotRename()
00468 {
00469     // pop up dialog to get the new name
00470     bool ok;
00471     QString newName = KInputDialog::getText( QString::null,
00472         i18n("Please enter the new name:"), m_label->text(), &ok, this );
00473     if ( !ok ) // handle cancel
00474         return;
00475 
00476     setName( newName );
00477 }
00478 
00479 void KNote::slotClose()
00480 {
00481     NETWinInfo wm_client( qt_xdisplay(), winId(), qt_xrootwin(), NET::WMDesktop );
00482     if ( wm_client.desktop() == NETWinInfo::OnAllDesktops || wm_client.desktop() > 0 )
00483         m_config->setDesktop( wm_client.desktop() );
00484 
00485     m_editor->clearFocus();
00486     m_config->setHideNote( true );
00487 
00488     // just hide the note so it's still available from the dock window
00489     hide();
00490 }
00491 
00492 void KNote::slotInsDate()
00493 {
00494     m_editor->insert( KGlobal::locale()->formatDateTime(QDateTime::currentDateTime()) );
00495 }
00496 
00497 void KNote::slotPreferences()
00498 {
00499     // reuse if possible
00500     if ( KNoteConfigDlg::showDialog( noteId().utf8() ) )
00501         return;
00502 
00503     // create a new preferences dialog...
00504     KNoteConfigDlg *dialog = new KNoteConfigDlg( m_config, name(), this, noteId().utf8() );
00505     connect( dialog, SIGNAL(settingsChanged()), this, SLOT(slotApplyConfig()) );
00506     connect( this, SIGNAL(sigNameChanged()), dialog, SLOT(slotUpdateCaption()) );
00507     dialog->show();
00508 }
00509 
00510 void KNote::slotSend()
00511 {
00512     // pop up dialog to get the IP
00513     KNoteHostDlg hostDlg( i18n("Send \"%1\"").arg( name() ), this );
00514     bool ok = (hostDlg.exec() == QDialog::Accepted);
00515     QString host = hostDlg.host();
00516 
00517     if ( !ok ) // handle cancel
00518         return;
00519 
00520     if ( host.isEmpty() )
00521     {
00522         KMessageBox::sorry( this, i18n("The host cannot be empty.") );
00523         return;
00524     }
00525 
00526     // Send the note
00527     KNotesNetworkSender *sender = new KNotesNetworkSender( host, KNotesGlobalConfig::port() );
00528     sender->setSenderId( KNotesGlobalConfig::senderID() );
00529     sender->setNote( name(), text() );
00530     sender->connect();
00531 }
00532 
00533 void KNote::slotMail()
00534 {
00535     QString msg_body = m_editor->text();
00536 
00537     // convert rich text to plain text first
00538     if ( m_editor->textFormat() == RichText )
00539         msg_body = toPlainText( msg_body );
00540 
00541     // get the mail action command
00542     QStringList cmd_list = QStringList::split( QChar(' '), KNotesGlobalConfig::mailAction() );
00543 
00544     KProcess mail;
00545     for ( QStringList::Iterator it = cmd_list.begin();
00546         it != cmd_list.end(); ++it )
00547     {
00548         if ( *it == "%f" )
00549             mail << msg_body.local8Bit();
00550         else if ( *it == "%t" )
00551             mail << m_label->text().local8Bit();
00552         else
00553             mail << (*it).local8Bit();
00554     }
00555 
00556     if ( !mail.start( KProcess::DontCare ) )
00557         KMessageBox::sorry( this, i18n("Unable to start the mail process.") );
00558 }
00559 
00560 void KNote::slotPrint()
00561 {
00562     saveData();
00563 
00564     KPrinter printer;
00565     printer.setFullPage( true );
00566 
00567     if ( printer.setup(0L, i18n("Print %1").arg(name())) )
00568     {
00569         QPainter painter;
00570         painter.begin( &printer );
00571 
00572         const int margin = 40;  // pt
00573 
00574         QPaintDeviceMetrics metrics( painter.device() );
00575         int marginX = margin * metrics.logicalDpiX() / 72;
00576         int marginY = margin * metrics.logicalDpiY() / 72;
00577 
00578         QRect body( marginX, marginY,
00579                     metrics.width() - marginX * 2,
00580                     metrics.height() - marginY * 2 );
00581 
00582         QString content;
00583         if ( m_editor->textFormat() == PlainText )
00584             content = QStyleSheet::convertFromPlainText( m_editor->text() );
00585         else
00586             content = m_editor->text();
00587 
00588         QSimpleRichText text( content, m_config->font(), m_editor->context(),
00589                               m_editor->styleSheet(), m_editor->mimeSourceFactory(),
00590                               body.height() /*, linkColor, linkUnderline? */ );
00591 
00592         text.setWidth( &painter, body.width() );
00593         QRect view( body );
00594 
00595         int page = 1;
00596 
00597         for (;;)
00598         {
00599             text.draw( &painter, body.left(), body.top(), view, colorGroup() );
00600             view.moveBy( 0, body.height() );
00601             painter.translate( 0, -body.height() );
00602 
00603             // page numbers
00604             painter.setFont( m_config->font() );
00605             painter.drawText(
00606                 view.right() - painter.fontMetrics().width( QString::number( page ) ),
00607                 view.bottom() + painter.fontMetrics().ascent() + 5, QString::number( page )
00608             );
00609 
00610             if ( view.top() >= text.height() )
00611                 break;
00612 
00613             printer.newPage();
00614             page++;
00615         }
00616 
00617         painter.end();
00618     }
00619 }
00620 
00621 void KNote::slotSaveAs()
00622 {
00623 /*
00624     QString msg_body = m_editor->text();
00625     QCheckBox *convert = 0;
00626 
00627     if ( m_editor->textFormat() == RichText )
00628     {
00629         convert = new QCheckBox( 0 );
00630         convert->setText( i18n("Save note as plain text") );
00631     }
00632 
00633     KFileDialog dlg( QString::null, QString::null, this, "filedialog", true, convert );
00634     dlg.setOperationMode( KFileDialog::Saving );
00635     dlg.setCaption( i18n("Save As") );
00636     dlg.exec();
00637 
00638     QString fileName = dlg.selectedFile();
00639     if ( fileName.isEmpty() )
00640         return;
00641 
00642     // convert rich text to plain text first
00643     if ( convert && convert->isChecked() )
00644         msg_body = toPlainText( msg_body );
00645 
00646     QFile file( fileName );
00647     if ( file.open( IO_WriteOnly ) )
00648     {
00649         QTextStream stream( &file );
00650         stream << msg_body;
00651     }
00652 */
00653 }
00654 
00655 void KNote::slotPopupActionToDesktop( int id )
00656 {
00657     toDesktop( id - 1 ); // compensate for the menu separator, -1 == all desktops
00658 }
00659 
00660 
00661 // ------------------ private slots (configuration) ------------------ //
00662 
00663 void KNote::slotApplyConfig()
00664 {
00665     if ( m_config->richText() )
00666         m_editor->setTextFormat( RichText );
00667     else
00668         m_editor->setTextFormat( PlainText );
00669 
00670     m_label->setFont( m_config->titleFont() );
00671     m_editor->setTextFont( m_config->font() );
00672     m_editor->setTabStop( m_config->tabSize() );
00673     m_editor->setAutoIndentMode( m_config->autoIndent() );
00674 
00675     // if called as a slot, save the text, we might have changed the
00676     // text format - otherwise the journal will not be updated
00677     if ( sender() )
00678         saveData();
00679 
00680     setColor( m_config->fgColor(), m_config->bgColor() );
00681 
00682     updateLabelAlignment();
00683     slotUpdateShowInTaskbar();
00684 }
00685 
00686 void KNote::slotUpdateKeepAboveBelow()
00687 {
00688     KWin::WindowInfo info( KWin::windowInfo( winId() ) );
00689 
00690     if ( m_keepAbove->isChecked() )
00691     {
00692         m_config->setKeepAbove( true );
00693         m_config->setKeepBelow( false );
00694         KWin::setState( winId(), info.state() | NET::KeepAbove );
00695     }
00696     else if ( m_keepBelow->isChecked() )
00697     {
00698         m_config->setKeepAbove( false );
00699         m_config->setKeepBelow( true );
00700         KWin::setState( winId(), info.state() | NET::KeepBelow );
00701     }
00702     else
00703     {
00704         m_config->setKeepAbove( false );
00705         KWin::clearState( winId(), NET::KeepAbove );
00706 
00707         m_config->setKeepBelow( false );
00708         KWin::clearState( winId(), NET::KeepBelow );
00709     }
00710 }
00711 
00712 void KNote::slotUpdateShowInTaskbar()
00713 {
00714     if ( !m_config->showInTaskbar() )
00715         KWin::setState( winId(), KWin::windowInfo(winId()).state() | NET::SkipTaskbar );
00716     else
00717         KWin::clearState( winId(), NET::SkipTaskbar );
00718 }
00719 
00720 void KNote::slotUpdateDesktopActions()
00721 {
00722     NETRootInfo wm_root( qt_xdisplay(), NET::NumberOfDesktops | NET::DesktopNames );
00723     NETWinInfo wm_client( qt_xdisplay(), winId(), qt_xrootwin(), NET::WMDesktop );
00724 
00725     QStringList desktops;
00726     desktops.append( i18n("&All Desktops") );
00727     desktops.append( QString::null );           // Separator
00728 
00729     int count = wm_root.numberOfDesktops();
00730     for ( int n = 1; n <= count; n++ )
00731         desktops.append( QString("&%1 %2").arg( n ).arg( QString::fromUtf8(wm_root.desktopName( n )) ) );
00732 
00733     m_toDesktop->setItems( desktops );
00734 
00735     if ( wm_client.desktop() == NETWinInfo::OnAllDesktops )
00736         m_toDesktop->setCurrentItem( 0 );
00737     else
00738         m_toDesktop->setCurrentItem( wm_client.desktop() + 1 ); // compensate for separator (+1)
00739 }
00740 
00741 
00742 // -------------------- private methods -------------------- //
00743 
00744 QString KNote::toPlainText( const QString& text )
00745 {
00746     QTextEdit conv;
00747     conv.setTextFormat( RichText );
00748     conv.setText( text );
00749     conv.setTextFormat( PlainText );
00750     return conv.text();
00751 }
00752 
00753 void KNote::toDesktop( int desktop )
00754 {
00755     if ( desktop == 0 )
00756         return;
00757 
00758     if ( desktop == NETWinInfo::OnAllDesktops )
00759         KWin::setOnAllDesktops( winId(), true );
00760     else
00761         KWin::setOnDesktop( winId(), desktop );
00762 }
00763 
00764 void KNote::setColor( const QColor &fg, const QColor &bg )
00765 {
00766     QPalette newpalette = palette();
00767     newpalette.setColor( QColorGroup::Background, bg );
00768     newpalette.setColor( QColorGroup::Foreground, fg );
00769     newpalette.setColor( QColorGroup::Base,       bg ); // text background
00770     newpalette.setColor( QColorGroup::Text,       fg ); // text color
00771     newpalette.setColor( QColorGroup::Button,     bg );
00772 
00773     // the shadow
00774     newpalette.setColor( QColorGroup::Midlight, bg.light(110) );
00775     newpalette.setColor( QColorGroup::Shadow, bg.dark(116) );  // 132 ?
00776     newpalette.setColor( QColorGroup::Light, bg.light(180) );
00777     newpalette.setColor( QColorGroup::Dark, bg.dark(108) );
00778     setPalette( newpalette );
00779 
00780     // set the text color
00781     m_editor->setTextColor( fg );
00782 
00783     // set darker value for the hide button...
00784     QPalette darker = palette();
00785     darker.setColor( QColorGroup::Button, bg.dark(116) );
00786     m_button->setPalette( darker );
00787 
00788     // update the icon color
00789     KIconEffect effect;
00790     QPixmap icon = effect.apply( kapp->icon(), KIconEffect::Colorize, 1, bg, false );
00791     QPixmap miniIcon = effect.apply( kapp->miniIcon(), KIconEffect::Colorize, 1, bg, false );
00792     KWin::setIcons( winId(), icon, miniIcon );
00793 
00794     // to set the color of the title
00795     updateFocus();
00796     emit sigColorChanged();
00797 }
00798 
00799 void KNote::updateLabelAlignment()
00800 {
00801     // if the name is too long to fit, left-align it, otherwise center it (#59028)
00802     QString labelText = m_label->text();
00803     if ( m_label->fontMetrics().boundingRect( labelText ).width() > m_label->width() )
00804         m_label->setAlignment( AlignLeft );
00805     else
00806         m_label->setAlignment( AlignHCenter );
00807 }
00808 
00809 void KNote::updateFocus()
00810 {
00811     if ( hasFocus() )
00812     {
00813         m_label->setBackgroundColor( palette().active().shadow() );
00814         m_button->show();
00815         m_editor->cornerWidget()->show();
00816 
00817         if ( m_tool->isHidden() && m_editor->textFormat() == QTextEdit::RichText )
00818         {
00819             m_tool->show();
00820             setGeometry( x(), y(), width(), height() + m_tool->height() );
00821         }
00822     }
00823     else
00824     {
00825         m_label->setBackgroundColor( palette().active().background() );
00826         m_button->hide();
00827         m_editor->cornerWidget()->hide();
00828 
00829         if ( !m_tool->isHidden() )
00830         {
00831             m_tool->hide();
00832             setGeometry( x(), y(), width(), height() - m_tool->height() );
00833             updateLayout();     // to update the minimum height
00834         }
00835     }
00836 }
00837 
00838 void KNote::updateLayout()
00839 {
00840     // DAMN, Qt still has no support for widgets with a fixed aspect ratio :-(
00841     // So we have to write our own layout manager...
00842 
00843     const int headerHeight = m_label->sizeHint().height();
00844     const int margin = m_editor->margin();
00845     static const int border = 2;
00846     bool closeLeft = false;
00847 
00848     m_kwinConf->setGroup( "Style" );
00849     if ( m_kwinConf->readBoolEntry( "CustomButtonPositions" ) )
00850         closeLeft = m_kwinConf->readEntry( "ButtonsOnLeft" ).find( 'X' ) > -1;
00851 
00852     m_button->setGeometry(
00853         closeLeft ? frameRect().x() + border
00854                   : frameRect().width() - headerHeight - border,
00855         frameRect().y() + border,
00856         headerHeight,
00857         headerHeight
00858     );
00859 
00860     m_label->setGeometry(
00861         frameRect().x() + border, frameRect().y() + border,
00862         frameRect().width() - border*2, headerHeight
00863     );
00864 
00865     m_editor->setGeometry(
00866         contentsRect().x(),
00867         contentsRect().y() + headerHeight + border,
00868         contentsRect().width(),
00869         contentsRect().height() - headerHeight -
00870                 (m_tool->isHidden() ? 0 : m_tool->height()) - border*2
00871     );
00872 
00873     m_tool->setGeometry(
00874         contentsRect().x(), contentsRect().height() - m_tool->height(),
00875         contentsRect().width(), m_tool->height()
00876     );
00877 
00878     setMinimumSize(
00879         m_editor->cornerWidget()->width() + margin*2 + border*2,
00880         headerHeight + (m_tool->isHidden() ? 0 : m_tool->height()) +
00881                 m_editor->cornerWidget()->height() + margin*2 + border*2
00882     );
00883 
00884     updateLabelAlignment();
00885 }
00886 
00887 // -------------------- protected methods -------------------- //
00888 
00889 void KNote::showEvent( QShowEvent * )
00890 {
00891     if ( m_config->hideNote() )
00892     {
00893         // KWin does not preserve these properties for hidden windows
00894         slotUpdateKeepAboveBelow();
00895         slotUpdateShowInTaskbar();
00896         toDesktop( m_config->desktop() );
00897         m_config->setHideNote( false );
00898     }
00899 }
00900 
00901 void KNote::resizeEvent( QResizeEvent *qre )
00902 {
00903     QFrame::resizeEvent( qre );
00904     updateLayout();
00905 }
00906 
00907 void KNote::closeEvent( QCloseEvent * )
00908 {
00909     slotClose();
00910 }
00911 
00912 void KNote::dragEnterEvent( QDragEnterEvent *e )
00913 {
00914     e->accept( KColorDrag::canDecode( e ) );
00915 }
00916 
00917 void KNote::dropEvent( QDropEvent *e )
00918 {
00919     QColor bg;
00920     if ( KColorDrag::decode( e, bg ) )
00921     {
00922         setColor( paletteForegroundColor(), bg );
00923         m_journal->setCustomProperty( "KNotes", "BgColor", bg.name() );
00924         m_config->setBgColor( bg );
00925     }
00926 }
00927 
00928 bool KNote::focusNextPrevChild( bool )
00929 {
00930     return true;
00931 }
00932 
00933 bool KNote::event( QEvent *ev )
00934 {
00935     if ( ev->type() == QEvent::LayoutHint )
00936     {
00937         updateLayout();
00938         return true;
00939     }
00940     else
00941         return QFrame::event( ev );
00942 }
00943 
00944 bool KNote::eventFilter( QObject *o, QEvent *ev )
00945 {
00946     if ( ev->type() == QEvent::DragEnter &&
00947          KColorDrag::canDecode( static_cast<QDragEnterEvent *>(ev) ) )
00948     {
00949         dragEnterEvent( static_cast<QDragEnterEvent *>(ev) );
00950         return true;
00951     }
00952 
00953     if ( ev->type() == QEvent::Drop &&
00954          KColorDrag::canDecode( static_cast<QDropEvent *>(ev) ) )
00955     {
00956         dropEvent( static_cast<QDropEvent *>(ev) );
00957         return true;
00958     }
00959 
00960     if ( o == m_label )
00961     {
00962         QMouseEvent *e = (QMouseEvent *)ev;
00963 
00964         if ( ev->type() == QEvent::MouseButtonDblClick )
00965             slotRename();
00966 
00967         if ( ev->type() == QEvent::MouseButtonPress &&
00968              (e->button() == LeftButton || e->button() == MidButton))
00969         {
00970             e->button() == LeftButton ? KWin::raiseWindow( winId() )
00971                                       : KWin::lowerWindow( winId() );
00972 
00973             XUngrabPointer( qt_xdisplay(), qt_x_time );
00974             NETRootInfo wm_root( qt_xdisplay(), NET::WMMoveResize );
00975             wm_root.moveResizeRequest( winId(), e->globalX(), e->globalY(), NET::Move );
00976             return true;
00977         }
00978 
00979         if ( m_menu && ( ev->type() == QEvent::MouseButtonPress )
00980             && ( e->button() == RightButton ) )
00981         {
00982             m_menu->popup( QCursor::pos() );
00983             return true;
00984         }
00985 
00986         return false;
00987     }
00988 
00989     if ( o == m_editor )
00990     {
00991         if ( ev->type() == QEvent::FocusOut )
00992         {
00993             QFocusEvent *fe = static_cast<QFocusEvent *>(ev);
00994             if ( fe->reason() != QFocusEvent::Popup &&
00995                  fe->reason() != QFocusEvent::Mouse )
00996             {
00997                 updateFocus();
00998                 if ( m_editor->isModified() )
00999                     saveData();
01000             }
01001         }
01002         else if ( ev->type() == QEvent::FocusIn )
01003             updateFocus();
01004 
01005         return false;
01006     }
01007 
01008     if ( o == m_editor->viewport() )
01009     {
01010         if ( m_edit_menu &&
01011              ev->type() == QEvent::MouseButtonPress &&
01012              ((QMouseEvent *)ev)->button() == RightButton )
01013         {
01014             m_edit_menu->popup( QCursor::pos() );
01015             return true;
01016         }
01017     }
01018 
01019     return false;
01020 }
01021 
01022 
01023 #include "knote.moc"
01024 #include "knotebutton.moc"
KDE Logo
This file is part of the documentation for knotes Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Jan 31 15:53:19 2007 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003