summaryrefslogtreecommitdiffstats
path: root/knotes/knote.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'knotes/knote.cpp')
-rw-r--r--knotes/knote.cpp1339
1 files changed, 1339 insertions, 0 deletions
diff --git a/knotes/knote.cpp b/knotes/knote.cpp
new file mode 100644
index 000000000..795da8a16
--- /dev/null
+++ b/knotes/knote.cpp
@@ -0,0 +1,1339 @@
+/*******************************************************************
+ 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 <qlabel.h>
+#include <qdrawutil.h>
+#include <qsize.h>
+#include <qsizegrip.h>
+#include <qbitmap.h>
+#include <qcursor.h>
+#include <qpainter.h>
+#include <qpaintdevicemetrics.h>
+#include <qsimplerichtext.h>
+#include <qobjectlist.h>
+#include <qfile.h>
+#include <qcheckbox.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kaction.h>
+#include <kstdaction.h>
+#include <kcombobox.h>
+#include <ktoolbar.h>
+#include <kpopupmenu.h>
+#include <kxmlguibuilder.h>
+#include <kxmlguifactory.h>
+#include <kcolordrag.h>
+#include <kiconeffect.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kmessagebox.h>
+#include <kfind.h>
+#include <kprocess.h>
+#include <kinputdialog.h>
+#include <kmdcodec.h>
+#include <kglobalsettings.h>
+#include <kfiledialog.h>
+#include <kio/netaccess.h>
+
+#include <libkcal/journal.h>
+
+#include "knote.h"
+#include "knotebutton.h"
+#include "knoteedit.h"
+#include "knoteconfig.h"
+#include "knotesglobalconfig.h"
+#include "knoteconfigdlg.h"
+#include "knotealarmdlg.h"
+#include "knotehostdlg.h"
+#include "knotesnetsend.h"
+#include "knoteprinter.h"
+#include "version.h"
+
+#include "pushpin.xpm"
+
+#include <kwin.h>
+#include <netwm.h>
+
+#include <fixx11h.h>
+
+using namespace KCal;
+
+extern Time qt_x_time;
+
+int KNote::s_ppOffset = 0;
+
+KNote::KNote( QDomDocument buildDoc, Journal *j, QWidget *parent, const char *name )
+ : QFrame( parent, name, WStyle_Customize | WStyle_NoBorder | WDestructiveClose ),
+ m_label( 0 ), m_pushpin( 0 ), m_fold( 0 ), m_button( 0 ), m_tool( 0 ), m_editor( 0 ),
+ m_config( 0 ), m_journal( j ), m_find( 0 ),
+ m_kwinConf( KSharedConfig::openConfig( "kwinrc", true ) )
+{
+ setAcceptDrops( true );
+ actionCollection()->setWidget( this );
+
+ setDOMDocument( buildDoc );
+
+ // just set the name of the file to save the actions to, do NOT reparse it
+ setXMLFile( instance()->instanceName() + "ui.rc", false, false );
+
+ // if there is no title yet, use the start date if valid
+ // (KOrganizer's journals don't have titles but a valid start date)
+ if ( m_journal->summary().isNull() && m_journal->dtStart().isValid() )
+ {
+ QString s = KGlobal::locale()->formatDateTime( m_journal->dtStart() );
+ m_journal->setSummary( s );
+ }
+
+ // create the menu items for the note - not the editor...
+ // rename, mail, print, save as, insert date, alarm, close, delete, new note
+ new KAction( i18n("New"), "filenew", 0,
+ this, SIGNAL(sigRequestNewNote()), actionCollection(), "new_note" );
+ new KAction( i18n("Rename..."), "text", 0,
+ this, SLOT(slotRename()), actionCollection(), "rename_note" );
+ m_readOnly = new KToggleAction( i18n("Lock"), "lock" , 0,
+ this, SLOT(slotUpdateReadOnly()), actionCollection(), "lock_note" );
+ m_readOnly->setCheckedState( KGuiItem( i18n("Unlock"), "unlock" ) );
+ new KAction( i18n("Hide"), "fileclose" , Key_Escape,
+ this, SLOT(slotClose()), actionCollection(), "hide_note" );
+ new KAction( i18n("Delete"), "knotes_delete", 0,
+ this, SLOT(slotKill()), actionCollection(), "delete_note" );
+
+ new KAction( i18n("Insert Date"), "knotes_date", 0 ,
+ this, SLOT(slotInsDate()), actionCollection(), "insert_date" );
+ new KAction( i18n("Set Alarm..."), "knotes_alarm", 0 ,
+ this, SLOT(slotSetAlarm()), actionCollection(), "set_alarm" );
+
+ new KAction( i18n("Send..."), "network", 0,
+ this, SLOT(slotSend()), actionCollection(), "send_note" );
+ new KAction( i18n("Mail..."), "mail_send", 0,
+ this, SLOT(slotMail()), actionCollection(), "mail_note" );
+ new KAction( i18n("Save As..."), "filesaveas", 0,
+ this, SLOT(slotSaveAs()), actionCollection(), "save_note" );
+ KStdAction::print( this, SLOT(slotPrint()), actionCollection(), "print_note" );
+ new KAction( i18n("Preferences..."), "configure", 0,
+ this, SLOT(slotPreferences()), actionCollection(), "configure_note" );
+
+ m_keepAbove = new KToggleAction( i18n("Keep Above Others"), "up", 0,
+ this, SLOT(slotUpdateKeepAboveBelow()), actionCollection(), "keep_above" );
+ m_keepAbove->setExclusiveGroup( "keepAB" );
+
+ m_keepBelow = new KToggleAction( i18n("Keep Below Others"), "down", 0,
+ this, SLOT(slotUpdateKeepAboveBelow()), actionCollection(), "keep_below" );
+ m_keepBelow->setExclusiveGroup( "keepAB" );
+
+ m_toDesktop = new KListAction( i18n("To Desktop"), 0,
+ this, SLOT(slotPopupActionToDesktop(int)), actionCollection(), "to_desktop" );
+ connect( m_toDesktop->popupMenu(), SIGNAL(aboutToShow()), this, SLOT(slotUpdateDesktopActions()) );
+
+ // invisible action to walk through the notes to make this configurable
+ new KAction( i18n("Walk Through Notes"), 0, SHIFT+Key_BackTab,
+ this, SIGNAL(sigShowNextNote()), actionCollection(), "walk_notes" );
+
+ // create the note header, button and label...
+ m_label = new QLabel( this );
+ m_label->setFrameStyle( NoFrame );
+ m_label->setLineWidth( 0 );
+ m_label->installEventFilter( this ); // receive events (for dragging & action menu)
+ setName( m_journal->summary() ); // don't worry, no signals are connected at this stage yet
+
+ m_button = new KNoteButton( "knotes_close", this );
+ connect( m_button, SIGNAL(clicked()), this, SLOT(slotClose()) );
+
+ // create the note editor
+ m_editor = new KNoteEdit( actionCollection(), this );
+ m_editor->installEventFilter( this ); // receive events (for modified)
+ m_editor->viewport()->installEventFilter( this );
+ connect( m_editor, SIGNAL(contentsMoving( int, int )), this, SLOT(slotUpdateViewport( int, int )));
+
+ KXMLGUIBuilder builder( this );
+ KXMLGUIFactory factory( &builder, this );
+ factory.addClient( this );
+
+ m_menu = dynamic_cast<KPopupMenu*>(factory.container( "note_context", this ));
+ m_edit_menu = dynamic_cast<KPopupMenu*>(factory.container( "note_edit", this ));
+ m_tool = dynamic_cast<KToolBar*>(factory.container( "note_tool", this ));
+
+ if ( m_tool ) {
+ m_tool->setIconSize( 10 );
+ m_tool->setFixedHeight( 16 );
+ m_tool->setIconText( KToolBar::IconOnly );
+
+ // if there was just a way of making KComboBox adhere the toolbar height...
+ QObjectList *list = m_tool->queryList( "KComboBox" );
+ QObjectListIt it( *list );
+ while ( it.current() != 0 )
+ {
+ KComboBox *combo = (KComboBox *)it.current();
+ QFont font = combo->font();
+ font.setPointSize( 7 );
+ combo->setFont( font );
+ combo->setFixedHeight( 14 );
+ ++it;
+ }
+ delete list;
+
+ m_tool->hide();
+ }
+
+ setFocusProxy( m_editor );
+
+ // create the resize handle
+ m_editor->setCornerWidget( new QSizeGrip( this ) );
+ uint width = m_editor->cornerWidget()->width();
+ uint height = m_editor->cornerWidget()->height();
+ QBitmap mask;
+ mask.resize( width, height );
+ mask.fill( color0 );
+ QPointArray array;
+ array.setPoints( 3, 0, height, width, height, width, 0 );
+ QPainter p;
+ p.begin( &mask );
+ p.setBrush( color1 );
+ p.drawPolygon( array );
+ p.end();
+ m_editor->cornerWidget()->setMask( mask );
+ m_editor->cornerWidget()->setBackgroundMode( PaletteBase );
+
+ // the config file location
+ QString configFile = KGlobal::dirs()->saveLocation( "appdata", "notes/" );
+ configFile += m_journal->uid();
+
+ // no config file yet? -> use the default display config if available
+ // we want to write to configFile, so use "false"
+ bool newNote = !KIO::NetAccess::exists( KURL::fromPathOrURL( configFile ), false, 0 );
+
+ m_config = new KNoteConfig( KSharedConfig::openConfig( configFile, false, false ) );
+ m_config->readConfig();
+ m_config->setVersion( KNOTES_VERSION );
+
+ if ( newNote )
+ {
+ // until kdelibs provides copying of KConfigSkeletons (KDE 3.4)
+ KNotesGlobalConfig *globalConfig = KNotesGlobalConfig::self();
+ m_config->setBgColor( globalConfig->bgColor() );
+ m_config->setFgColor( globalConfig->fgColor() );
+ m_config->setWidth( globalConfig->width() );
+ m_config->setHeight( globalConfig->height() );
+
+ m_config->setFont( globalConfig->font() );
+ m_config->setTitleFont( globalConfig->titleFont() );
+ m_config->setAutoIndent( globalConfig->autoIndent() );
+ m_config->setRichText( globalConfig->richText() );
+ m_config->setTabSize( globalConfig->tabSize() );
+ m_config->setReadOnly( globalConfig->readOnly() );
+
+ m_config->setDesktop( globalConfig->desktop() );
+ m_config->setHideNote( globalConfig->hideNote() );
+ m_config->setPosition( globalConfig->position() );
+ m_config->setShowInTaskbar( globalConfig->showInTaskbar() );
+ m_config->setKeepAbove( globalConfig->keepAbove() );
+ m_config->setKeepBelow( globalConfig->keepBelow() );
+
+ m_config->writeConfig();
+ }
+
+ // set up the look&feel of the note
+ setMinimumSize( 20, 20 );
+ setLineWidth( 1 );
+ setMargin( 0 );
+
+ m_editor->setMargin( 0 );
+ m_editor->setFrameStyle( NoFrame );
+ m_editor->setBackgroundOrigin( WindowOrigin );
+
+ // can be done here since this doesn't pick up changes while KNotes is running anyway
+ bool closeLeft = false;
+ m_kwinConf->setGroup( "Style" );
+ if ( m_kwinConf->readBoolEntry( "CustomButtonPositions" ) )
+ closeLeft = m_kwinConf->readEntry( "ButtonsOnLeft" ).find( 'X' ) > -1;
+
+ QPixmap pushpin_pix;
+ if ( closeLeft )
+ pushpin_pix = QPixmap( QPixmap( pushpin_xpm ).convertToImage().mirror( true, false ) );
+ else
+ pushpin_pix = QPixmap( pushpin_xpm );
+
+ // the pushpin label at the top left or right corner
+ m_pushpin = new QLabel( this );
+ m_pushpin->setScaledContents( true );
+ m_pushpin->setBackgroundMode( NoBackground );
+ m_pushpin->setPixmap( pushpin_pix );
+ m_pushpin->resize( pushpin_pix.size() );
+
+ // fold label at bottom right corner
+ m_fold = new QLabel( this );
+ m_fold->setScaledContents( true );
+ m_fold->setBackgroundMode( NoBackground );
+
+ // load the display configuration of the note
+ width = m_config->width();
+ height = m_config->height();
+ resize( width, height );
+
+ // let KWin do the placement if the position is illegal--at least 10 pixels
+ // of a note need to be visible
+ const QPoint& position = m_config->position();
+ QRect desk = kapp->desktop()->rect();
+ desk.addCoords( 10, 10, -10, -10 );
+ if ( desk.intersects( QRect( position, QSize( width, height ) ) ) )
+ move( position ); // do before calling show() to avoid flicker
+
+ // config items in the journal have priority
+ QString property = m_journal->customProperty( "KNotes", "FgColor" );
+ if ( !property.isNull() )
+ m_config->setFgColor( QColor( property ) );
+ else
+ m_journal->setCustomProperty( "KNotes", "FgColor", m_config->fgColor().name() );
+
+ property = m_journal->customProperty( "KNotes", "BgColor" );
+ if ( !property.isNull() )
+ m_config->setBgColor( QColor( property ) );
+ else
+ m_journal->setCustomProperty( "KNotes", "BgColor", m_config->bgColor().name() );
+
+ property = m_journal->customProperty( "KNotes", "RichText" );
+ if ( !property.isNull() )
+ m_config->setRichText( property == "true" ? true : false );
+ else
+ m_journal->setCustomProperty( "KNotes", "RichText", m_config->richText() ? "true" : "false" );
+
+ // read configuration settings...
+ slotApplyConfig();
+
+ // create the mask for the fold---to be done after slotApplyConfig(),
+ // which calls createFold()
+ m_fold->setMask( QRegion( m_fold->pixmap()->createHeuristicMask() ) );
+
+ // if this is a new note put on current desktop - we can't use defaults
+ // in KConfig XT since only _changes_ will be stored in the config file
+ int desktop = m_config->desktop();
+ if ( desktop < 0 && desktop != NETWinInfo::OnAllDesktops )
+ desktop = KWin::currentDesktop();
+
+ // show the note if desired
+ if ( desktop != 0 && !m_config->hideNote() )
+ {
+ // to avoid flicker, call this before show()
+ toDesktop( desktop );
+ show();
+
+ // because KWin forgets about that for hidden windows
+ if ( desktop == NETWinInfo::OnAllDesktops )
+ toDesktop( desktop );
+ }
+
+ m_editor->setText( m_journal->description() );
+ m_editor->setModified( false );
+
+ m_readOnly->setChecked( m_config->readOnly() );
+ slotUpdateReadOnly();
+
+ if ( m_config->keepAbove() )
+ m_keepAbove->setChecked( true );
+ else if ( m_config->keepBelow() )
+ m_keepBelow->setChecked( true );
+ else
+ {
+ m_keepAbove->setChecked( false );
+ m_keepBelow->setChecked( false );
+ }
+ slotUpdateKeepAboveBelow();
+
+ // HACK: update the icon color - again after showing the note, to make kicker aware of the new colors
+ KIconEffect effect;
+ QPixmap icon = effect.apply( kapp->icon(), KIconEffect::Colorize, 1, m_config->bgColor(), false );
+ QPixmap miniIcon = effect.apply( kapp->miniIcon(), KIconEffect::Colorize, 1, m_config->bgColor(), false );
+ KWin::setIcons( winId(), icon, miniIcon );
+}
+
+KNote::~KNote()
+{
+ delete m_config;
+}
+
+
+// -------------------- public slots -------------------- //
+
+void KNote::slotKill( bool force )
+{
+ if ( !force &&
+ KMessageBox::warningContinueCancel( this,
+ i18n("<qt>Do you really want to delete note <b>%1</b>?</qt>").arg( m_label->text() ),
+ i18n("Confirm Delete"), KGuiItem( i18n("&Delete"), "editdelete" ),
+ "ConfirmDeleteNote"
+ )
+ != KMessageBox::Continue )
+ {
+ return;
+ }
+
+ // delete the configuration first, then the corresponding file
+ delete m_config;
+ m_config = 0;
+
+ QString configFile = KGlobal::dirs()->saveLocation( "appdata", "notes/" );
+ configFile += m_journal->uid();
+
+ if ( !KIO::NetAccess::del( KURL::fromPathOrURL( configFile ), this ) )
+ kdError(5500) << "Can't remove the note config: " << configFile << endl;
+
+ emit sigKillNote( m_journal );
+}
+
+
+// -------------------- public member functions -------------------- //
+
+void KNote::saveData()
+{
+ m_journal->setSummary( m_label->text() );
+ m_journal->setDescription( m_editor->text() );
+ m_journal->setCustomProperty( "KNotes", "FgColor", m_config->fgColor().name() );
+ m_journal->setCustomProperty( "KNotes", "BgColor", m_config->bgColor().name() );
+ m_journal->setCustomProperty( "KNotes", "RichText", m_config->richText() ? "true" : "false" );
+
+ emit sigDataChanged();
+ m_editor->setModified( false );
+}
+
+void KNote::saveConfig() const
+{
+ m_config->setWidth( width() );
+ if ( m_tool )
+ m_config->setHeight( height() - (m_tool->isHidden() ? 0 : m_tool->height()) );
+ else
+ m_config->setHeight( 0 );
+ m_config->setPosition( pos() );
+
+ NETWinInfo wm_client( qt_xdisplay(), winId(), qt_xrootwin(), NET::WMDesktop );
+ if ( wm_client.desktop() == NETWinInfo::OnAllDesktops || wm_client.desktop() > 0 )
+ m_config->setDesktop( wm_client.desktop() );
+
+ // actually store the config on disk
+ m_config->writeConfig();
+}
+
+QString KNote::noteId() const
+{
+ return m_journal->uid();
+}
+
+QString KNote::name() const
+{
+ return m_label->text();
+}
+
+QString KNote::text() const
+{
+ return m_editor->text();
+}
+
+QString KNote::plainText() const
+{
+ if ( m_editor->textFormat() == RichText )
+ {
+ QTextEdit conv;
+ conv.setTextFormat( RichText );
+ conv.setText( m_editor->text() );
+ conv.setTextFormat( PlainText );
+ return conv.text();
+ }
+ else
+ return m_editor->text();
+}
+
+void KNote::setName( const QString& name )
+{
+ m_label->setText( name );
+ updateLabelAlignment();
+
+ if ( m_editor ) // not called from CTOR?
+ saveData();
+
+ // set the window's name for the taskbar entry to be more helpful (#58338)
+ NETWinInfo note_win( qt_xdisplay(), winId(), qt_xrootwin(), NET::WMDesktop );
+ note_win.setName( name.utf8() );
+
+ emit sigNameChanged();
+}
+
+void KNote::setText( const QString& text )
+{
+ m_editor->setText( text );
+ saveData();
+}
+
+QColor KNote::fgColor() const
+{
+ return m_config->fgColor();
+}
+
+QColor KNote::bgColor() const
+{
+ return m_config->bgColor();
+}
+
+void KNote::setColor( const QColor& fg, const QColor& bg )
+{
+ m_journal->setCustomProperty( "KNotes", "FgColor", fg.name() );
+ m_journal->setCustomProperty( "KNotes", "BgColor", bg.name() );
+ m_config->setFgColor( fg );
+ m_config->setBgColor( bg );
+
+ m_journal->updated(); // because setCustomProperty() doesn't call it!!
+ emit sigDataChanged();
+ m_config->writeConfig();
+
+ QPalette newpalette = palette();
+ newpalette.setColor( QColorGroup::Background, bg );
+ newpalette.setColor( QColorGroup::Foreground, fg );
+ newpalette.setColor( QColorGroup::Base, bg ); // text background
+ newpalette.setColor( QColorGroup::Text, fg ); // text color
+ newpalette.setColor( QColorGroup::Button, bg );
+ newpalette.setColor( QColorGroup::ButtonText, fg );
+
+// newpalette.setColor( QColorGroup::Highlight, bg );
+// newpalette.setColor( QColorGroup::HighlightedText, fg );
+
+ // the shadow
+ newpalette.setColor( QColorGroup::Midlight, bg.light(150) );
+ newpalette.setColor( QColorGroup::Shadow, bg.dark(116) );
+ newpalette.setColor( QColorGroup::Light, bg.light(180) );
+ if ( s_ppOffset )
+ newpalette.setColor( QColorGroup::Dark, bg.dark(200) );
+ else
+ newpalette.setColor( QColorGroup::Dark, bg.dark(108) );
+ setPalette( newpalette );
+
+ // set the text color
+ m_editor->setTextColor( fg );
+
+ // set the background color or gradient
+ updateBackground();
+
+ // set darker value for the hide button...
+ QPalette darker = palette();
+ darker.setColor( QColorGroup::Button, bg.dark(116) );
+ m_button->setPalette( darker );
+
+ // update the icon color
+ KIconEffect effect;
+ QPixmap icon = effect.apply( kapp->icon(), KIconEffect::Colorize, 1, bg, false );
+ QPixmap miniIcon = effect.apply( kapp->miniIcon(), KIconEffect::Colorize, 1, bg, false );
+ KWin::setIcons( winId(), icon, miniIcon );
+
+ // set the color for the selection used to highlight the find stuff
+ QColor sel = palette().color( QPalette::Active, QColorGroup::Base ).dark();
+ if ( sel == Qt::black )
+ sel = palette().color( QPalette::Active, QColorGroup::Base ).light();
+
+ m_editor->setSelectionAttributes( 1, sel, true );
+
+ // update the color of the fold
+ createFold();
+
+ // update the color of the title
+ updateFocus();
+ emit sigColorChanged();
+}
+
+void KNote::find( const QString& pattern, long options )
+{
+ delete m_find;
+ m_find = new KFind( pattern, options, this );
+
+ connect( m_find, SIGNAL(highlight( const QString &, int, int )),
+ this, SLOT(slotHighlight( const QString &, int, int )) );
+ connect( m_find, SIGNAL(findNext()), this, SLOT(slotFindNext()) );
+
+ m_find->setData( plainText() );
+ slotFindNext();
+}
+
+void KNote::slotFindNext()
+{
+ // TODO: honor FindBackwards
+ // TODO: dialogClosed() -> delete m_find
+
+ // Let KFind inspect the text fragment, and display a dialog if a match is found
+ KFind::Result res = m_find->find();
+
+ if ( res == KFind::NoMatch ) // i.e. at end-pos
+ {
+ m_editor->removeSelection( 1 );
+ emit sigFindFinished();
+ delete m_find;
+ m_find = 0;
+ }
+ else
+ {
+ show();
+ KWin::setCurrentDesktop( KWin::windowInfo( winId() ).desktop() );
+ }
+}
+
+void KNote::slotHighlight( const QString& str, int idx, int len )
+{
+ int paraFrom = 0, idxFrom = 0, p = 0;
+ for ( ; p < idx; ++p )
+ if ( str[p] == '\n' )
+ {
+ ++paraFrom;
+ idxFrom = 0;
+ }
+ else
+ ++idxFrom;
+
+ int paraTo = paraFrom, idxTo = idxFrom;
+
+ for ( ; p < idx + len; ++p )
+ {
+ if ( str[p] == '\n' )
+ {
+ ++paraTo;
+ idxTo = 0;
+ }
+ else
+ ++idxTo;
+ }
+
+ m_editor->setSelection( paraFrom, idxFrom, paraTo, idxTo, 1 );
+}
+
+bool KNote::isModified() const
+{
+ return m_editor->isModified();
+}
+
+// FIXME KDE 4.0: remove sync(), isNew() and isModified()
+void KNote::sync( const QString& app )
+{
+ QByteArray sep( 1 );
+ sep[0] = '\0';
+
+ KMD5 hash;
+ QCString result;
+
+ hash.update( m_label->text().utf8() );
+ hash.update( sep );
+ hash.update( m_editor->text().utf8() );
+ hash.hexDigest( result );
+
+ // hacky... not possible with KConfig XT
+ KConfig *config = m_config->config();
+ config->setGroup( "Synchronisation" );
+ config->writeEntry( app, result.data() );
+}
+
+bool KNote::isNew( const QString& app ) const
+{
+ KConfig *config = m_config->config();
+ config->setGroup( "Synchronisation" );
+ QString hash = config->readEntry( app );
+ return hash.isEmpty();
+}
+
+bool KNote::isModified( const QString& app ) const
+{
+ QByteArray sep( 1 );
+ sep[0] = '\0';
+
+ KMD5 hash;
+ hash.update( m_label->text().utf8() );
+ hash.update( sep );
+ hash.update( m_editor->text().utf8() );
+ hash.hexDigest();
+
+ KConfig *config = m_config->config();
+ config->setGroup( "Synchronisation" );
+ QString orig = config->readEntry( app );
+
+ if ( hash.verify( orig.utf8() ) ) // returns false on error!
+ return false;
+ else
+ return true;
+}
+
+void KNote::setStyle( int style )
+{
+ if ( style == KNotesGlobalConfig::EnumStyle::Plain )
+ s_ppOffset = 0;
+ else
+ s_ppOffset = 12;
+}
+
+
+// ------------------ private slots (menu actions) ------------------ //
+
+void KNote::slotRename()
+{
+ // pop up dialog to get the new name
+ bool ok;
+ QString newName = KInputDialog::getText( QString::null,
+ i18n("Please enter the new name:"), m_label->text(), &ok, this );
+ if ( !ok ) // handle cancel
+ return;
+
+ setName( newName );
+}
+
+void KNote::slotUpdateReadOnly()
+{
+ const bool readOnly = m_readOnly->isChecked();
+
+ m_editor->setReadOnly( readOnly );
+ m_config->setReadOnly( readOnly );
+
+ // Enable/disable actions accordingly
+ actionCollection()->action( "configure_note" )->setEnabled( !readOnly );
+ actionCollection()->action( "insert_date" )->setEnabled( !readOnly );
+ actionCollection()->action( "delete_note" )->setEnabled( !readOnly );
+
+ actionCollection()->action( "edit_undo" )->setEnabled( !readOnly && m_editor->isUndoAvailable() );
+ actionCollection()->action( "edit_redo" )->setEnabled( !readOnly && m_editor->isRedoAvailable() );
+ actionCollection()->action( "edit_cut" )->setEnabled( !readOnly && m_editor->hasSelectedText() );
+ actionCollection()->action( "edit_paste" )->setEnabled( !readOnly );
+ actionCollection()->action( "edit_clear" )->setEnabled( !readOnly );
+
+ updateFocus();
+}
+
+void KNote::slotClose()
+{
+ NETWinInfo wm_client( qt_xdisplay(), winId(), qt_xrootwin(), NET::WMDesktop );
+ if ( wm_client.desktop() == NETWinInfo::OnAllDesktops || wm_client.desktop() > 0 )
+ m_config->setDesktop( wm_client.desktop() );
+
+ m_editor->clearFocus();
+ m_config->setHideNote( true );
+ m_config->setPosition( pos() );
+
+ // just hide the note so it's still available from the dock window
+ hide();
+}
+
+void KNote::slotInsDate()
+{
+ m_editor->insert( KGlobal::locale()->formatDateTime(QDateTime::currentDateTime()) );
+}
+
+void KNote::slotSetAlarm()
+{
+ KNoteAlarmDlg dlg( name(), this );
+ dlg.setIncidence( m_journal );
+
+ if ( dlg.exec() == QDialog::Accepted )
+ emit sigDataChanged();
+}
+
+void KNote::slotPreferences()
+{
+ // reuse if possible
+ if ( KNoteConfigDlg::showDialog( noteId().utf8() ) )
+ return;
+
+ // create a new preferences dialog...
+ KNoteConfigDlg *dialog = new KNoteConfigDlg( m_config, name(), this, noteId().utf8() );
+ connect( dialog, SIGNAL(settingsChanged()), this, SLOT(slotApplyConfig()) );
+ connect( this, SIGNAL(sigNameChanged()), dialog, SLOT(slotUpdateCaption()) );
+ dialog->show();
+}
+
+void KNote::slotSend()
+{
+ // pop up dialog to get the IP
+ KNoteHostDlg hostDlg( i18n("Send \"%1\"").arg( name() ), this );
+ bool ok = (hostDlg.exec() == QDialog::Accepted);
+ QString host = hostDlg.host();
+
+ if ( !ok ) // handle cancel
+ return;
+
+ if ( host.isEmpty() )
+ {
+ KMessageBox::sorry( this, i18n("The host cannot be empty.") );
+ return;
+ }
+
+ // Send the note
+ KNotesNetworkSender *sender = new KNotesNetworkSender( host, KNotesGlobalConfig::port() );
+ sender->setSenderId( KNotesGlobalConfig::senderID() );
+ sender->setNote( name(), text() );
+ sender->connect();
+}
+
+void KNote::slotMail()
+{
+ // get the mail action command
+ QStringList cmd_list = QStringList::split( QChar(' '), KNotesGlobalConfig::mailAction() );
+
+ KProcess mail;
+ for ( QStringList::Iterator it = cmd_list.begin();
+ it != cmd_list.end(); ++it )
+ {
+ if ( *it == "%f" )
+ mail << plainText().local8Bit(); // convert rich text to plain text
+ else if ( *it == "%t" )
+ mail << m_label->text().local8Bit();
+ else
+ mail << (*it).local8Bit();
+ }
+
+ if ( !mail.start( KProcess::DontCare ) )
+ KMessageBox::sorry( this, i18n("Unable to start the mail process.") );
+}
+
+void KNote::slotPrint()
+{
+ saveData();
+
+ QString content;
+ if ( m_editor->textFormat() == PlainText )
+ content = QStyleSheet::convertFromPlainText( m_editor->text() );
+ else
+ content = m_editor->text();
+
+ KNotePrinter printer;
+ printer.setMimeSourceFactory( m_editor->mimeSourceFactory() );
+ printer.setFont( m_config->font() );
+ printer.setContext( m_editor->context() );
+ printer.setStyleSheet( m_editor->styleSheet() );
+ printer.setColorGroup( colorGroup() );
+ printer.printNote( QString(), content );
+}
+
+void KNote::slotSaveAs()
+{
+ QCheckBox *convert = 0;
+
+ if ( m_editor->textFormat() == RichText )
+ {
+ convert = new QCheckBox( 0 );
+ convert->setText( i18n("Save note as plain text") );
+ }
+
+ KFileDialog dlg( QString::null, QString::null, this, "filedialog", true, convert );
+ dlg.setOperationMode( KFileDialog::Saving );
+ dlg.setCaption( i18n("Save As") );
+ dlg.exec();
+
+ QString fileName = dlg.selectedFile();
+ if ( fileName.isEmpty() )
+ return;
+
+ QFile file( fileName );
+
+ if ( file.exists() &&
+ KMessageBox::warningContinueCancel( this, i18n("<qt>A file named <b>%1</b> already exists.<br>"
+ "Are you sure you want to overwrite it?</qt>").arg( QFileInfo(file).fileName() ) )
+ != KMessageBox::Continue )
+ {
+ return;
+ }
+
+ if ( file.open( IO_WriteOnly ) )
+ {
+ QTextStream stream( &file );
+ // convert rich text to plain text first
+ if ( convert && convert->isChecked() )
+ stream << plainText();
+ else
+ stream << text();
+ }
+}
+
+void KNote::slotPopupActionToDesktop( int id )
+{
+ toDesktop( id - 1 ); // compensate for the menu separator, -1 == all desktops
+}
+
+
+// ------------------ private slots (configuration) ------------------ //
+
+void KNote::slotApplyConfig()
+{
+ if ( m_config->richText() )
+ m_editor->setTextFormat( RichText );
+ else
+ m_editor->setTextFormat( PlainText );
+
+ m_label->setFont( m_config->titleFont() );
+ m_editor->setTextFont( m_config->font() );
+ m_editor->setTabStop( m_config->tabSize() );
+ m_editor->setAutoIndentMode( m_config->autoIndent() );
+
+ // if called as a slot, save the text, we might have changed the
+ // text format - otherwise the journal will not be updated
+ if ( sender() )
+ saveData();
+
+ setColor( m_config->fgColor(), m_config->bgColor() );
+
+ updateLabelAlignment();
+ slotUpdateShowInTaskbar();
+}
+
+void KNote::slotUpdateKeepAboveBelow()
+{
+ KWin::WindowInfo info( KWin::windowInfo( winId() ) );
+
+ if ( m_keepAbove->isChecked() )
+ {
+ m_config->setKeepAbove( true );
+ m_config->setKeepBelow( false );
+ KWin::setState( winId(), info.state() | NET::KeepAbove );
+ }
+ else if ( m_keepBelow->isChecked() )
+ {
+ m_config->setKeepAbove( false );
+ m_config->setKeepBelow( true );
+ KWin::setState( winId(), info.state() | NET::KeepBelow );
+ }
+ else
+ {
+ m_config->setKeepAbove( false );
+ KWin::clearState( winId(), NET::KeepAbove );
+
+ m_config->setKeepBelow( false );
+ KWin::clearState( winId(), NET::KeepBelow );
+ }
+}
+
+void KNote::slotUpdateShowInTaskbar()
+{
+ if ( !m_config->showInTaskbar() )
+ KWin::setState( winId(), KWin::windowInfo(winId()).state() | NET::SkipTaskbar );
+ else
+ KWin::clearState( winId(), NET::SkipTaskbar );
+}
+
+void KNote::slotUpdateDesktopActions()
+{
+ NETRootInfo wm_root( qt_xdisplay(), NET::NumberOfDesktops | NET::DesktopNames );
+ NETWinInfo wm_client( qt_xdisplay(), winId(), qt_xrootwin(), NET::WMDesktop );
+
+ QStringList desktops;
+ desktops.append( i18n("&All Desktops") );
+ desktops.append( QString::null ); // Separator
+
+ int count = wm_root.numberOfDesktops();
+ for ( int n = 1; n <= count; n++ )
+ desktops.append( QString("&%1 %2").arg( n ).arg( QString::fromUtf8(wm_root.desktopName( n )) ) );
+
+ m_toDesktop->setItems( desktops );
+
+ if ( wm_client.desktop() == NETWinInfo::OnAllDesktops )
+ m_toDesktop->setCurrentItem( 0 );
+ else
+ m_toDesktop->setCurrentItem( wm_client.desktop() + 1 ); // compensate for separator (+1)
+}
+
+void KNote::slotUpdateViewport( int /*x*/, int y )
+{
+ if ( s_ppOffset )
+ updateBackground( y );
+}
+
+// -------------------- private methods -------------------- //
+
+void KNote::toDesktop( int desktop )
+{
+ if ( desktop == 0 )
+ return;
+
+ if ( desktop == NETWinInfo::OnAllDesktops )
+ KWin::setOnAllDesktops( winId(), true );
+ else
+ KWin::setOnDesktop( winId(), desktop );
+}
+
+void KNote::createFold()
+{
+ QPixmap fold( 15, 15 );
+ QPainter foldp( &fold );
+ foldp.setPen( Qt::NoPen );
+ foldp.setBrush( palette().active().dark() );
+ QPointArray foldpoints( 3 );
+ foldpoints.putPoints( 0, 3, 0, 0, 14, 0, 0, 14 );
+ foldp.drawPolygon( foldpoints );
+ foldp.end();
+ m_fold->setPixmap( fold );
+}
+
+void KNote::updateLabelAlignment()
+{
+ // if the name is too long to fit, left-align it, otherwise center it (#59028)
+ QString labelText = m_label->text();
+ if ( m_label->fontMetrics().boundingRect( labelText ).width() > m_label->width() )
+ m_label->setAlignment( AlignLeft );
+ else
+ m_label->setAlignment( AlignHCenter );
+}
+
+void KNote::updateFocus()
+{
+ if ( hasFocus() )
+ {
+ m_label->setBackgroundColor( palette().active().shadow() );
+ m_button->show();
+ m_editor->cornerWidget()->show();
+
+ if ( !m_editor->isReadOnly() )
+ {
+ if ( m_tool && m_tool->isHidden() && m_editor->textFormat() == QTextEdit::RichText )
+ {
+ m_tool->show();
+ setGeometry( x(), y(), width(), height() + m_tool->height() );
+ }
+ }
+ else if ( m_tool && !m_tool->isHidden() )
+ {
+ m_tool->hide();
+ setGeometry( x(), y(), width(), height() - m_tool->height() );
+ updateLayout(); // to update the minimum height
+ }
+
+ m_fold->hide();
+ }
+ else
+ {
+ m_button->hide();
+ m_editor->cornerWidget()->hide();
+
+ if ( m_tool && !m_tool->isHidden() )
+ {
+ m_tool->hide();
+ setGeometry( x(), y(), width(), height() - m_tool->height() );
+ updateLayout(); // to update the minimum height
+ }
+
+ if ( s_ppOffset )
+ {
+ m_label->setBackgroundColor( palette().active().midlight() );
+ m_fold->show();
+ }
+ else
+ m_label->setBackgroundColor( palette().active().background() );
+ }
+}
+
+void KNote::updateMask()
+{
+ if ( !s_ppOffset )
+ {
+ clearMask();
+ return;
+ }
+
+ int w = width();
+ int h = height();
+ QRegion reg( 0, s_ppOffset, w, h - s_ppOffset );
+
+ const QBitmap *pushpin_bitmap = m_pushpin->pixmap()->mask();
+ QRegion pushpin_reg( *pushpin_bitmap );
+ m_pushpin->setMask( pushpin_reg );
+ pushpin_reg.translate( m_pushpin->x(), m_pushpin->y() );
+
+ if ( !hasFocus() )
+ {
+ QPointArray foldpoints( 3 );
+ foldpoints.putPoints( 0, 3, w-15, h, w, h-15, w, h );
+ QRegion fold( foldpoints, false );
+ setMask( reg.unite( pushpin_reg ).subtract( fold ) );
+ }
+ else
+ setMask( reg.unite( pushpin_reg ) );
+}
+
+void KNote::updateBackground( int y_offset )
+{
+ if ( !s_ppOffset )
+ {
+ m_editor->setPaper( QBrush( colorGroup().background() ) );
+ return;
+ }
+
+ int w = m_editor->visibleWidth();
+ int h = m_editor->visibleHeight();
+
+ // in case y_offset is not set, calculate y_offset as the content
+ // y-coordinate of the top-left point of the viewport - which is essentially
+ // the vertical scroll amount
+ if ( y_offset == -1 )
+ y_offset = m_editor->contentsY();
+
+ y_offset = y_offset % h;
+
+ QImage grad_img( w, h, 32 );
+ QRgb rgbcol;
+ QColor bg = palette().active().background();
+
+ for ( int i = 0; i < h; ++i )
+ {
+ // if the scrollbar has moved, then adjust the gradient by the amount the
+ // scrollbar moved -- so that the background gradient looks ok when tiled
+
+ // the lightness is calculated as follows:
+ // if i >= y, then lightness = 150 - (i-y)*75/h;
+ // if i < y, then lightness = 150 - (i+h-y)*75/h
+
+ int i_1 = 150 - 75 * ((i - y_offset + h) % h) / h;
+ rgbcol = bg.light( i_1 ).rgb();
+ for ( int j = 0; j < w; ++j )
+ grad_img.setPixel( j, i, rgbcol );
+ }
+
+ // setPaletteBackgroundPixmap makes QTextEdit::color() stop working!!
+ m_editor->setPaper( QBrush( Qt::black, QPixmap( grad_img ) ) );
+}
+
+void KNote::updateLayout()
+{
+ const int headerHeight = m_label->sizeHint().height();
+ const int margin = m_editor->margin();
+ bool closeLeft = false;
+
+ m_kwinConf->setGroup( "Style" );
+ if ( m_kwinConf->readBoolEntry( "CustomButtonPositions" ) )
+ closeLeft = m_kwinConf->readEntry( "ButtonsOnLeft" ).find( 'X' ) > -1;
+
+ if ( s_ppOffset )
+ {
+ if ( !m_editor->paper().pixmap() ) // just changed the style
+ setColor( palette().active().foreground(), palette().active().background() );
+
+ m_pushpin->show();
+ setFrameStyle( Panel | Raised );
+
+ if ( closeLeft )
+ m_pushpin->move( width() - m_pushpin->width(), 0 );
+ else
+ m_pushpin->move( 0, 0 );
+ }
+ else
+ {
+ if ( m_editor->paper().pixmap() ) // just changed the style
+ setColor( palette().active().foreground(), palette().active().background() );
+
+ setFrameStyle( WinPanel | Raised );
+ m_pushpin->hide();
+ m_fold->hide();
+ }
+
+ m_button->setGeometry(
+ closeLeft ? contentsRect().x() : contentsRect().width() - headerHeight,
+ contentsRect().y() + s_ppOffset,
+ headerHeight,
+ headerHeight
+ );
+
+ m_label->setGeometry(
+ contentsRect().x(), contentsRect().y() + s_ppOffset,
+ contentsRect().width(), headerHeight
+ );
+
+ m_editor->setGeometry( QRect(
+ QPoint( contentsRect().x(),
+ contentsRect().y() + headerHeight + s_ppOffset ),
+ QPoint( contentsRect().right(),
+ contentsRect().bottom() - ( m_tool ? (m_tool->isHidden() ? 0 : m_tool->height()) : 0 ) )
+ ) );
+
+ if( m_tool ) {
+ m_tool->setGeometry(
+ contentsRect().x(),
+ contentsRect().bottom() - m_tool->height() + 1,
+ contentsRect().width(),
+ m_tool->height()
+ );
+ }
+
+ if ( s_ppOffset )
+ m_fold->move( width() - 15, height() - 15 );
+
+ setMinimumSize(
+ m_editor->cornerWidget()->width() + margin*2,
+ headerHeight + s_ppOffset + ( m_tool ? (m_tool->isHidden() ? 0 : m_tool->height() ) : 0 ) +
+ m_editor->cornerWidget()->height() + margin*2
+ );
+
+ updateLabelAlignment();
+ updateMask();
+ updateBackground();
+}
+
+// -------------------- protected methods -------------------- //
+
+void KNote::drawFrame( QPainter *p )
+{
+ QRect r = frameRect();
+ r.setTop( s_ppOffset );
+ if ( s_ppOffset )
+ qDrawShadePanel( p, r, colorGroup(), false, lineWidth() );
+ else
+ qDrawWinPanel( p, r, colorGroup(), false );
+}
+
+void KNote::showEvent( QShowEvent * )
+{
+ if ( m_config->hideNote() )
+ {
+ // KWin does not preserve these properties for hidden windows
+ slotUpdateKeepAboveBelow();
+ slotUpdateShowInTaskbar();
+ toDesktop( m_config->desktop() );
+ move( m_config->position() );
+ m_config->setHideNote( false );
+ }
+}
+
+void KNote::resizeEvent( QResizeEvent *qre )
+{
+ QFrame::resizeEvent( qre );
+ updateLayout();
+}
+
+void KNote::closeEvent( QCloseEvent * )
+{
+ slotClose();
+}
+
+void KNote::dragEnterEvent( QDragEnterEvent *e )
+{
+ if ( !m_config->readOnly() )
+ e->accept( KColorDrag::canDecode( e ) );
+}
+
+void KNote::dropEvent( QDropEvent *e )
+{
+ if ( m_config->readOnly() )
+ return;
+
+ QColor bg;
+ if ( KColorDrag::decode( e, bg ) )
+ setColor( paletteForegroundColor(), bg );
+}
+
+bool KNote::focusNextPrevChild( bool )
+{
+ return true;
+}
+
+bool KNote::event( QEvent *ev )
+{
+ if ( ev->type() == QEvent::LayoutHint )
+ {
+ updateLayout();
+ return true;
+ }
+ else
+ return QFrame::event( ev );
+}
+
+bool KNote::eventFilter( QObject *o, QEvent *ev )
+{
+ if ( ev->type() == QEvent::DragEnter &&
+ KColorDrag::canDecode( static_cast<QDragEnterEvent *>(ev) ) )
+ {
+ dragEnterEvent( static_cast<QDragEnterEvent *>(ev) );
+ return true;
+ }
+
+ if ( ev->type() == QEvent::Drop &&
+ KColorDrag::canDecode( static_cast<QDropEvent *>(ev) ) )
+ {
+ dropEvent( static_cast<QDropEvent *>(ev) );
+ return true;
+ }
+
+ if ( o == m_label )
+ {
+ QMouseEvent *e = (QMouseEvent *)ev;
+
+ if ( ev->type() == QEvent::MouseButtonDblClick )
+ slotRename();
+
+ if ( ev->type() == QEvent::MouseButtonPress &&
+ (e->button() == LeftButton || e->button() == MidButton))
+ {
+ e->button() == LeftButton ? KWin::raiseWindow( winId() )
+ : KWin::lowerWindow( winId() );
+
+ XUngrabPointer( qt_xdisplay(), qt_x_time );
+ NETRootInfo wm_root( qt_xdisplay(), NET::WMMoveResize );
+ wm_root.moveResizeRequest( winId(), e->globalX(), e->globalY(), NET::Move );
+ return true;
+ }
+
+#if KDE_IS_VERSION( 3, 5, 1 )
+ if ( ev->type() == QEvent::MouseButtonRelease )
+ {
+ NETRootInfo wm_root( qt_xdisplay(), NET::WMMoveResize );
+ wm_root.moveResizeRequest( winId(), e->globalX(), e->globalY(), NET::MoveResizeCancel );
+ return false;
+ }
+#endif
+
+ if ( m_menu && ( ev->type() == QEvent::MouseButtonPress )
+ && ( e->button() == RightButton ) )
+ {
+ m_menu->popup( QCursor::pos() );
+ return true;
+ }
+
+ return false;
+ }
+
+ if ( o == m_editor )
+ {
+ if ( ev->type() == QEvent::FocusOut )
+ {
+ QFocusEvent *fe = static_cast<QFocusEvent *>(ev);
+ if ( fe->reason() != QFocusEvent::Popup &&
+ fe->reason() != QFocusEvent::Mouse )
+ {
+ updateFocus();
+ if ( m_editor->isModified() )
+ saveData();
+ }
+ }
+ else if ( ev->type() == QEvent::FocusIn )
+ updateFocus();
+
+ return false;
+ }
+
+ if ( o == m_editor->viewport() )
+ {
+ if ( m_edit_menu &&
+ ev->type() == QEvent::MouseButtonPress &&
+ ((QMouseEvent *)ev)->button() == RightButton )
+ {
+ m_edit_menu->popup( QCursor::pos() );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+#include "knote.moc"
+#include "knotebutton.moc"