Don't offer to restart a Running (=busy) agent, it won't work.
[kdepim.git] / knotes / knote.cpp
blob9b13406b1fbe1c18e0cae6e8bb9b0e3d0cd84407
1 /*******************************************************************
2 KNotes -- Notes for the KDE project
4 Copyright (c) 1997-2007, The KNotes Developers
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 *******************************************************************/
21 #include "knote.h"
22 #include "knotealarmdlg.h"
23 #include "knotebutton.h"
24 #include "knoteconfig.h"
25 #include "knoteconfigdlg.h"
26 #include "knoteedit.h"
27 #include "knotehostdlg.h"
28 #include "knoteprinter.h"
29 #include "knotesglobalconfig.h"
30 #include "knotesnetsend.h"
31 #include "kdepim-version.h"
33 #include <kaction.h>
34 #include <kactioncollection.h>
35 #include <kapplication.h>
36 #include <kcal/journal.h>
37 #include <kcodecs.h>
38 #include <kcombobox.h>
39 #include <kdebug.h>
40 #include <kfiledialog.h>
41 #include <kfind.h>
42 #include <kglobalsettings.h>
43 #include <kicon.h>
44 #include <kiconeffect.h>
45 #include <kiconloader.h>
46 #include <kinputdialog.h>
47 #include <kio/netaccess.h>
48 #include <klocale.h>
49 #include <kmenu.h>
50 #include <kmessagebox.h>
51 #include <kprocess.h>
52 #include <kselectaction.h>
53 #include <ksocketfactory.h>
54 #include <kstandardaction.h>
55 #include <kstandarddirs.h>
56 #include <ktoggleaction.h>
57 #include <ktoolbar.h>
58 #include <kwindowsystem.h>
59 #include <kxmlguibuilder.h>
60 #include <kxmlguifactory.h>
61 #include <netwm.h>
63 #include <QBoxLayout>
64 #include <QCheckBox>
65 #include <QFile>
66 #include <QHBoxLayout>
67 #include <QLabel>
68 #include <QObject>
69 #include <QPixmap>
70 #include <QSize>
71 #include <QSizeGrip>
72 #include <QTextStream>
73 #include <QVBoxLayout>
74 #include <QDesktopWidget>
76 #ifdef Q_WS_X11
77 #include <fixx11h.h>
78 #include <QX11Info>
79 #endif
81 using namespace KCal;
84 KNote::KNote( const QDomDocument& buildDoc, Journal *j, QWidget *parent )
85 : QFrame( parent, Qt::FramelessWindowHint ), m_label( 0 ), m_grip( 0 ),
86 m_button( 0 ), m_tool( 0 ), m_editor( 0 ), m_config( 0 ), m_journal( j ),
87 m_find( 0 ), m_kwinConf( KSharedConfig::openConfig( "kwinrc" ) ), m_blockEmitDataChanged( false ),mBlockWriteConfigDuringCommitData( false )
89 setAcceptDrops( true );
90 setAttribute( Qt::WA_DeleteOnClose );
91 setDOMDocument( buildDoc );
92 setObjectName( m_journal->uid() );
93 setXMLFile( componentData().componentName() + "ui.rc", false, false );
95 // create the main layout
96 m_noteLayout = new QVBoxLayout( this );
97 m_noteLayout->setMargin( 0 );
99 // if there is no title yet, use the start date if valid
100 // (KOrganizer's journals don't have titles but a valid start date)
101 if ( m_journal->summary().isNull() && m_journal->dtStart().isValid() ) {
102 const QString s = KGlobal::locale()->formatDateTime( m_journal->dtStart() );
103 m_journal->setSummary( s );
106 createActions();
108 buildGui();
110 prepare();
113 KNote::~KNote()
115 delete m_config;
118 void KNote::changeJournal(KCal::Journal *journal)
120 m_journal = journal;
121 m_editor->setText( m_journal->description() );
122 m_editor->document()->setModified( false );
123 m_label->setText( m_journal->summary() );
124 updateLabelAlignment();
128 // -------------------- public slots -------------------- //
130 void KNote::slotKill( bool force )
132 m_blockEmitDataChanged = true;
133 if ( !force &&
134 ( KMessageBox::warningContinueCancel( this,
135 i18n( "<qt>Do you really want to delete note <b>%1</b>?</qt>",
136 m_label->text() ),
137 i18n( "Confirm Delete" ),
138 KGuiItem( i18n( "&Delete" ), "edit-delete" ),
139 KStandardGuiItem::cancel(),
140 "ConfirmDeleteNote" ) != KMessageBox::Continue ) ) {
141 m_blockEmitDataChanged = false;
142 return;
144 // delete the configuration first, then the corresponding file
145 delete m_config;
146 m_config = 0;
147 QString configFile = KGlobal::dirs()->saveLocation( "appdata", "notes/" );
148 configFile += m_journal->uid();
149 if ( !KIO::NetAccess::del( KUrl( configFile ), this ) ) {
150 kError( 5500 ) <<"Can't remove the note config:" << configFile;
153 emit sigKillNote( m_journal );
157 // -------------------- public member functions -------------------- //
159 void KNote::saveData(bool update )
161 m_journal->setSummary( m_label->text() );
162 m_journal->setDescription( m_editor->text() );
163 m_journal->setCustomProperty( "KNotes", "FgColor",
164 m_config->fgColor().name() );
165 m_journal->setCustomProperty( "KNotes", "BgColor",
166 m_config->bgColor().name() );
167 m_journal->setCustomProperty( "KNotes", "RichText",
168 m_config->richText() ? "true" : "false" );
170 if(update)
172 emit sigDataChanged(noteId());
173 m_editor->document()->setModified( false );
177 void KNote::saveConfig() const
179 m_config->setWidth( width() );
180 if ( m_tool ) {
181 m_config->setHeight(
182 height() - ( m_tool->isHidden() ? 0 : m_tool->height() ) );
183 } else {
184 m_config->setHeight( 0 );
186 m_config->setPosition( pos() );
188 #ifdef Q_WS_X11
189 NETWinInfo wm_client( QX11Info::display(), winId(),
190 QX11Info::appRootWindow(), NET::WMDesktop );
191 if ( ( wm_client.desktop() == NETWinInfo::OnAllDesktops ) ||
192 ( wm_client.desktop() > 0 ) ) {
193 m_config->setDesktop( wm_client.desktop() );
195 #endif
197 // actually store the config on disk
198 m_config->writeConfig();
201 QString KNote::noteId() const
203 return m_journal->uid();
206 QString KNote::name() const
208 return m_label->text();
211 QString KNote::text() const
213 return m_editor->text();
216 void KNote::setName( const QString& name )
218 m_label->setText( name );
219 updateLabelAlignment();
221 if ( m_editor ) { // not called from CTOR?
222 saveData();
224 #ifdef Q_WS_X11
225 // set the window's name for the taskbar entry to be more helpful (#58338)
226 NETWinInfo note_win( QX11Info::display(), winId(), QX11Info::appRootWindow(),
227 NET::WMDesktop );
228 note_win.setName( name.toUtf8() );
229 #endif
231 emit sigNameChanged(name);
234 void KNote::setText( const QString& text )
236 m_editor->setText( text );
238 saveData();
241 void KNote::find( KFind* kfind )
243 m_find = kfind;
244 disconnect( m_find );
245 connect( m_find, SIGNAL(highlight(QString,int,int)),
246 this, SLOT(slotHighlight(QString,int,int)) );
247 connect( m_find, SIGNAL(findNext()), this, SLOT(slotFindNext()) );
249 m_find->setData( m_editor->toPlainText() );
250 slotFindNext();
253 void KNote::slotFindNext()
255 // TODO: honor FindBackwards
257 // Let KFind inspect the text fragment, and display a dialog if a match is
258 // found
259 KFind::Result res = m_find->find();
261 if ( res == KFind::NoMatch ) { // i.e. at end-pos
263 QTextCursor c = m_editor->textCursor(); //doesn't return by reference, so we use setTextCursor
264 c.clearSelection();
265 m_editor->setTextCursor( c );
267 disconnect( m_find, 0, this, 0 );
268 emit sigFindFinished();
269 } else {
271 show();
272 #ifdef Q_WS_X11
273 KWindowSystem::setCurrentDesktop( KWindowSystem::windowInfo( winId(),
274 NET::WMDesktop ).desktop() );
275 #endif
279 void KNote::slotHighlight( const QString& /*str*/, int idx, int len )
281 m_editor->textCursor().clearSelection();
282 m_editor->highlightWord( len, idx );
284 // TODO: modify the selection color, use a different QTextCursor?
287 bool KNote::isModified() const
289 return m_editor->document()->isModified();
292 // ------------------ private slots (menu actions) ------------------ //
294 void KNote::slotRename()
296 m_blockEmitDataChanged = true;
297 // pop up dialog to get the new name
298 bool ok;
299 const QString oldName = m_label->text();
300 const QString newName = KInputDialog::getText( QString::null, //krazy:exclude=nullstrassign for old broken gcc
301 i18n( "Please enter the new name:" ), m_label->text(), &ok, this );
302 m_blockEmitDataChanged = false;
303 if ( !ok || (oldName == newName) ) { // handle cancel
304 return;
307 setName( newName );
310 void KNote::slotUpdateReadOnly()
312 const bool readOnly = m_readOnly->isChecked();
314 m_editor->setReadOnly( readOnly );
315 m_config->setReadOnly( readOnly );
317 // enable/disable actions accordingly
318 actionCollection()->action( "configure_note" )->setEnabled( !readOnly );
319 actionCollection()->action( "insert_date" )->setEnabled( !readOnly );
320 actionCollection()->action( "delete_note" )->setEnabled( !readOnly );
321 actionCollection()->action( "format_bold" )->setEnabled( !readOnly );
322 actionCollection()->action( "format_italic" )->setEnabled( !readOnly );
323 actionCollection()->action( "format_underline" )->setEnabled( !readOnly );
324 actionCollection()->action( "format_strikeout" )->setEnabled( !readOnly );
325 actionCollection()->action( "format_alignleft" )->setEnabled( !readOnly );
326 actionCollection()->action( "format_aligncenter" )->setEnabled( !readOnly );
327 actionCollection()->action( "format_alignright" )->setEnabled( !readOnly );
328 actionCollection()->action( "format_alignblock" )->setEnabled( !readOnly );
329 actionCollection()->action( "format_list" )->setEnabled( !readOnly );
330 actionCollection()->action( "format_super" )->setEnabled( !readOnly );
331 actionCollection()->action( "format_sub" )->setEnabled( !readOnly );
332 actionCollection()->action( "format_increaseindent" )->setEnabled( !readOnly );
333 actionCollection()->action( "format_decreaseindent" )->setEnabled( !readOnly );
334 actionCollection()->action( "text_background_color" )->setEnabled( !readOnly );
335 actionCollection()->action( "format_size" )->setEnabled( !readOnly );
336 actionCollection()->action( "format_color" )->setEnabled( !readOnly );
337 actionCollection()->action( "rename_note" )->setEnabled( !readOnly);
339 updateFocus();
343 void KNote::commitData()
345 mBlockWriteConfigDuringCommitData = true;
348 void KNote::slotClose()
350 #ifdef Q_WS_X11
351 NETWinInfo wm_client( QX11Info::display(), winId(),
352 QX11Info::appRootWindow(), NET::WMDesktop );
353 if ( ( wm_client.desktop() == NETWinInfo::OnAllDesktops ) ||
354 ( wm_client.desktop() > 0 ) ) {
355 m_config->setDesktop( wm_client.desktop() );
357 #endif
359 m_editor->clearFocus();
360 if( !mBlockWriteConfigDuringCommitData )
362 m_config->setHideNote( true );
363 m_config->setPosition( pos() );
364 m_config->writeConfig();
366 // just hide the note so it's still available from the dock window
367 hide();
370 void KNote::slotInsDate()
372 m_editor->insertPlainText(
373 KGlobal::locale()->formatDateTime( QDateTime::currentDateTime() ) );
376 void KNote::slotSetAlarm()
378 m_blockEmitDataChanged = true;
379 KNoteAlarmDlg dlg( name(), this );
380 dlg.setIncidence( m_journal );
382 if ( dlg.exec() == QDialog::Accepted ) {
383 emit sigDataChanged(noteId());
385 m_blockEmitDataChanged = false;
388 void KNote::slotPreferences()
390 m_blockEmitDataChanged = true;
392 // create a new preferences dialog...
393 KNoteSimpleConfigDlg *dialog = new KNoteSimpleConfigDlg( m_config, name(), this, noteId() );
394 connect( dialog, SIGNAL(settingsChanged(QString)) , this,
395 SLOT(slotApplyConfig()) );
396 connect( this, SIGNAL(sigNameChanged(QString)), dialog,
397 SLOT(slotUpdateCaption(QString)) );
398 dialog->exec();
399 delete dialog;
400 m_blockEmitDataChanged = false;
401 saveData();
404 void KNote::slotSend()
406 // pop up dialog to get the IP
407 KNoteHostDlg hostDlg( i18n( "Send \"%1\"", name() ), this );
408 const bool ok = ( hostDlg.exec() == QDialog::Accepted );
410 if ( !ok ) { // handle cancel
411 return;
413 const QString host = hostDlg.host();
414 quint16 port = hostDlg.port();
416 if ( !port ) { // not specified, use default
417 port = KNotesGlobalConfig::port();
420 if ( host.isEmpty() ) {
421 KMessageBox::sorry( this, i18n( "The host cannot be empty." ) );
422 return;
425 // Send the note
427 KNotesNetworkSender *sender = new KNotesNetworkSender(
428 KSocketFactory::connectToHost( "knotes", host, port ) );
429 sender->setSenderId( KNotesGlobalConfig::senderID() );
430 sender->setNote( name(), text() ); // FIXME: plainText ??
433 void KNote::slotMail()
435 // get the mail action command
436 const QStringList cmd_list = KNotesGlobalConfig::mailAction().split( QChar(' '),
437 QString::SkipEmptyParts );
439 KProcess mail;
440 foreach ( const QString &cmd, cmd_list ) {
441 if ( cmd == "%f" ) {
442 mail << m_editor->toPlainText();
443 } else if ( cmd == "%t" ) {
444 mail << m_label->text();
445 } else {
446 mail << cmd;
450 if ( !mail.startDetached() ) {
451 KMessageBox::sorry( this, i18n( "Unable to start the mail process." ) );
455 void KNote::slotPrint()
457 QString content;
458 if ( !Qt::mightBeRichText( m_editor->text() ) ) {
459 content = Qt::convertFromPlainText( m_editor->text() );
460 } else {
461 content = m_editor->text();
463 KNotePrinter printer;
464 printer.setDefaultFont( m_config->font() );
465 printer.printNote( name(), content );
468 void KNote::slotSaveAs()
470 // TODO: where to put pdf file support? In the printer??!??!
471 m_blockEmitDataChanged = true;
472 QCheckBox *convert = 0;
474 if ( m_editor->acceptRichText() ) {
475 convert = new QCheckBox( 0 );
476 convert->setText( i18n( "Save note as plain text" ) );
478 m_blockEmitDataChanged = true;
479 KUrl url;
480 KFileDialog dlg( url, QString(), this, convert );
481 dlg.setOperationMode( KFileDialog::Saving );
482 dlg.setCaption( i18n( "Save As" ) );
483 dlg.exec();
485 QString fileName = dlg.selectedFile();
486 if ( fileName.isEmpty() ) {
487 m_blockEmitDataChanged = false;
488 return;
491 QFile file( fileName );
493 if ( file.exists() &&
494 KMessageBox::warningContinueCancel( this,
495 i18n( "<qt>A file named <b>%1</b> already exists.<br />"
496 "Are you sure you want to overwrite it?</qt>",
497 QFileInfo( file ).fileName() ) ) != KMessageBox::Continue ) {
498 m_blockEmitDataChanged = false;
499 return;
502 if ( file.open( QIODevice::WriteOnly ) ) {
503 QTextStream stream( &file );
504 if ( convert && !convert->isChecked() ) {
505 stream << m_editor->toHtml();
506 } else {
507 stream << m_editor->toPlainText();
510 m_blockEmitDataChanged = false;
513 void KNote::slotPopupActionToDesktop( int id )
515 toDesktop( id - 1 ); // compensate for the menu separator, -1 == all desktops
519 // ------------------ private slots (configuration) ------------------ //
521 void KNote::slotApplyConfig()
523 m_label->setFont( m_config->titleFont() );
524 m_editor->setRichText( m_config->richText() );
525 m_editor->setTextFont( m_config->font() );
526 m_editor->setTabStop( m_config->tabSize() );
527 m_editor->setAutoIndentMode( m_config->autoIndent() );
529 setColor( m_config->fgColor(), m_config->bgColor() );
531 updateLayout();
532 slotUpdateShowInTaskbar();
535 void KNote::slotKeepAbove()
537 if ( m_keepBelow->isChecked() )
539 m_keepBelow->setChecked( false );
541 slotUpdateKeepAboveBelow();
544 void KNote::slotKeepBelow()
546 if ( m_keepAbove->isChecked() )
548 m_keepAbove->setChecked( false );
550 slotUpdateKeepAboveBelow();
553 void KNote::slotUpdateKeepAboveBelow()
555 #ifdef Q_WS_X11
556 unsigned long state = KWindowInfo( KWindowSystem::windowInfo( winId(), NET::WMState ) ).state();
557 #else
558 unsigned long state = 0; // neutral state, TODO
559 #endif
560 if ( m_keepAbove->isChecked() ) {
561 m_config->setKeepAbove( true );
562 m_config->setKeepBelow( false );
563 KWindowSystem::setState( winId(), state | NET::KeepAbove );
564 } else if ( m_keepBelow->isChecked() ) {
565 m_config->setKeepAbove( false );
566 m_config->setKeepBelow( true );
567 KWindowSystem::setState( winId(), state | NET::KeepBelow );
568 } else {
569 m_config->setKeepAbove( false );
570 KWindowSystem::clearState( winId(), NET::KeepAbove );
571 m_config->setKeepBelow( false );
572 KWindowSystem::clearState( winId(), NET::KeepBelow );
576 void KNote::slotUpdateShowInTaskbar()
578 #ifdef Q_WS_X11
579 if ( !m_config->showInTaskbar() ) {
580 KWindowSystem::setState( winId(), KWindowSystem::windowInfo( winId(),
581 NET::WMState ).state() | NET::SkipTaskbar );
582 } else {
583 KWindowSystem::clearState( winId(), NET::SkipTaskbar );
585 #endif
588 void KNote::slotUpdateDesktopActions()
590 #ifdef Q_WS_X11
591 NETRootInfo wm_root( QX11Info::display(), NET::NumberOfDesktops |
592 NET::DesktopNames );
593 NETWinInfo wm_client( QX11Info::display(), winId(),
594 QX11Info::appRootWindow(), NET::WMDesktop );
596 QStringList desktops;
597 desktops.append( i18n( "&All Desktops" ) );
598 desktops.append( QString::null ); // Separator
599 // krazy:exclude=nullstrassign
600 // for old broken gcc
602 const int count = wm_root.numberOfDesktops();
603 for ( int n = 1; n <= count; n++ ) {
604 desktops.append( QString( "&%1 %2" ).arg( n ).arg(
605 QString::fromUtf8( wm_root.desktopName( n ) ) ) );
607 m_toDesktop->setItems( desktops );
609 if ( wm_client.desktop() == NETWinInfo::OnAllDesktops ) {
610 m_toDesktop->setCurrentItem( 0 );
611 } else {
612 m_toDesktop->setCurrentItem( wm_client.desktop() + 1 ); // compensate for
613 // separator (+1)
615 #endif
619 // -------------------- private methods -------------------- //
621 void KNote::buildGui()
623 createNoteHeader();
624 createNoteEditor();
626 KXMLGUIBuilder builder( this );
627 KXMLGUIFactory factory( &builder, this );
628 factory.addClient( this );
630 m_menu = dynamic_cast<KMenu*>( factory.container( "note_context", this ) );
631 m_tool = dynamic_cast<KToolBar*>( factory.container( "note_tool", this ) );
633 createNoteFooter();
636 void KNote::createActions()
638 // create the menu items for the note - not the editor...
639 // rename, mail, print, save as, insert date, alarm, close, delete, new note
640 KAction *action;
642 action = new KAction( KIcon( "document-new" ), i18n( "New" ), this );
643 actionCollection()->addAction( "new_note", action );
644 connect( action, SIGNAL(triggered(bool)), SLOT(slotRequestNewNote()) );
646 action = new KAction( KIcon( "edit-rename" ), i18n( "Rename..." ), this );
647 actionCollection()->addAction( "rename_note", action );
648 connect( action, SIGNAL(triggered(bool)), SLOT(slotRename()) );
650 m_readOnly = new KToggleAction( KIcon( "object-locked" ),
651 i18n( "Lock" ), this );
652 actionCollection()->addAction( "lock_note", m_readOnly );
653 connect( m_readOnly, SIGNAL(triggered(bool)),
654 SLOT(slotUpdateReadOnly()) );
655 m_readOnly->setCheckedState( KGuiItem( i18n( "Unlock" ), "object-unlocked" ) );
657 action = new KAction( KIcon( "window-close" ), i18n( "Hide" ), this );
658 actionCollection()->addAction( "hide_note", action );
659 connect( action, SIGNAL(triggered(bool)), SLOT(slotClose()) );
660 action->setShortcut( QKeySequence( Qt::Key_Escape ) );
662 action = new KAction( KIcon( "edit-delete" ), i18n( "Delete" ), this );
663 actionCollection()->addAction( "delete_note", action );
664 connect( action, SIGNAL(triggered(bool)), SLOT(slotKill()),Qt::QueuedConnection );
666 action = new KAction( KIcon( "knotes_date" ), i18n( "Insert Date" ), this );
667 actionCollection()->addAction( "insert_date", action );
668 connect( action, SIGNAL(triggered(bool)), SLOT(slotInsDate()) );
670 action = new KAction( KIcon( "knotes_alarm" ), i18n( "Set Alarm..." ),
671 this );
672 actionCollection()->addAction( "set_alarm", action );
673 connect( action, SIGNAL(triggered(bool)), SLOT(slotSetAlarm()) );
675 action = new KAction( KIcon( "network-wired" ), i18n( "Send..." ), this );
676 actionCollection()->addAction( "send_note", action );
677 connect( action, SIGNAL(triggered(bool)), SLOT(slotSend()) );
679 action = new KAction( KIcon( "mail-send" ), i18n( "Mail..." ), this );
680 actionCollection()->addAction( "mail_note", action );
681 connect( action, SIGNAL(triggered(bool)), SLOT(slotMail()) );
683 action = new KAction( KIcon( "document-save-as" ), i18n( "Save As..." ),
684 this );
685 actionCollection()->addAction( "save_note", action );
686 connect( action, SIGNAL(triggered(bool)), SLOT(slotSaveAs()) );
687 actionCollection()->addAction( KStandardAction::Print, "print_note", this,
688 SLOT(slotPrint()) );
690 action = new KAction( KIcon( "configure" ), i18n( "Preferences..." ), this );
691 actionCollection()->addAction( "configure_note", action );
692 connect( action, SIGNAL(triggered(bool)), SLOT(slotPreferences()) );
695 m_keepAbove = new KToggleAction( KIcon( "go-up" ),
696 i18n( "Keep Above Others" ), this );
697 actionCollection()->addAction( "keep_above", m_keepAbove );
698 connect( m_keepAbove, SIGNAL(triggered(bool)),
699 SLOT(slotKeepAbove()) );
701 m_keepBelow = new KToggleAction( KIcon( "go-down" ),
702 i18n( "Keep Below Others" ), this );
703 actionCollection()->addAction( "keep_below", m_keepBelow );
704 connect( m_keepBelow, SIGNAL(triggered(bool)),
705 SLOT(slotKeepBelow()) );
707 #ifdef Q_WS_X11
708 m_toDesktop = new KSelectAction( i18n( "To Desktop" ), this );
709 actionCollection()->addAction( "to_desktop", m_toDesktop );
710 connect( m_toDesktop, SIGNAL(triggered(int)),
711 SLOT(slotPopupActionToDesktop(int)) );
712 connect( m_toDesktop->menu(), SIGNAL(aboutToShow()),
713 SLOT(slotUpdateDesktopActions()) );
714 #endif
716 // invisible action to walk through the notes to make this configurable
717 action = new KAction( i18n( "Walk Through Notes" ), this );
718 actionCollection()->addAction( "walk_notes", action );
719 connect( action, SIGNAL(triggered(bool)), SIGNAL(sigShowNextNote()) );
720 action->setShortcut( QKeySequence( Qt::SHIFT + Qt::Key_Backtab ) );
722 actionCollection()->addAssociatedWidget( this );
723 foreach (QAction* action, actionCollection()->actions())
724 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
727 void KNote::createNoteHeader()
729 // load style configuration
730 KConfigGroup styleGroup( m_kwinConf, "Style" );
732 QBoxLayout::Direction headerLayoutDirection = QBoxLayout::LeftToRight;
734 if ( styleGroup.readEntry( "CustomButtonPositions", false ) ) {
735 if ( styleGroup.readEntry( "ButtonsOnLeft" ).contains( 'X' ) ) {
736 headerLayoutDirection = QBoxLayout::RightToLeft;
740 QBoxLayout *headerLayout = new QBoxLayout( headerLayoutDirection);
743 // create header label
744 m_label = new QLabel( this );
745 headerLayout->addWidget( m_label );
746 m_label->setFrameStyle( NoFrame );
747 m_label->setBackgroundRole( QPalette::Base );
748 m_label->setLineWidth( 0 );
749 m_label->setAutoFillBackground( true );
750 m_label->installEventFilter( this ); // receive events ( for dragging &
751 // action menu )
752 setName( m_journal->summary() ); // don't worry, no signals are
753 // connected at this stage yet
754 m_button = new KNoteButton( "knotes_close", this );
755 headerLayout->addWidget( m_button );
757 connect( m_button, SIGNAL(clicked()), this, SLOT(slotClose()) );
759 m_noteLayout->addItem( headerLayout );
762 void KNote::createNoteEditor()
764 m_editor = new KNoteEdit( actionCollection(), this );
765 m_noteLayout->addWidget( m_editor );
766 m_editor->setNote( this );
767 m_editor->installEventFilter( this ); // receive focus events for modified
768 setFocusProxy( m_editor );
771 void KNote::slotRequestNewNote()
773 //Be sure to save before to request a new note
774 saveConfig();
775 saveData();
776 emit sigRequestNewNote();
779 void KNote::createNoteFooter()
781 if ( m_tool ) {
782 m_tool->setIconSize( QSize( 10, 10 ) );
783 m_tool->setFixedHeight( 24 );
784 m_tool->setToolButtonStyle( Qt::ToolButtonIconOnly );
787 // create size grip
788 QHBoxLayout *gripLayout = new QHBoxLayout;
789 m_grip = new QSizeGrip( this );
790 m_grip->setFixedSize( m_grip->sizeHint() );
792 if ( m_tool ) {
793 gripLayout->addWidget( m_tool );
794 gripLayout->setAlignment( m_tool, Qt::AlignBottom | Qt::AlignLeft );
795 m_tool->hide();
798 gripLayout->addWidget( m_grip );
799 gripLayout->setAlignment( m_grip, Qt::AlignBottom | Qt::AlignRight );
800 m_noteLayout->addItem( gripLayout );
802 // if there was just a way of making KComboBox adhere the toolbar height...
803 if ( m_tool ) {
804 foreach ( KComboBox *combo, m_tool->findChildren<KComboBox *>() ) {
805 QFont font = combo->font();
806 font.setPointSize( 7 );
807 combo->setFont( font );
808 combo->setFixedHeight( 14 );
813 void KNote::prepare()
815 // the config file location
816 const QString configFile = KGlobal::dirs()->saveLocation( "appdata", "notes/" ) + m_journal->uid();
818 // no config file yet? -> use the default display config if available
819 // we want to write to configFile, so use "false"
820 const bool newNote = !KIO::NetAccess::exists( KUrl( configFile ),
821 KIO::NetAccess::DestinationSide, 0 );
823 m_config = new KNoteConfig( KSharedConfig::openConfig( configFile,
824 KConfig::NoGlobals ) );
825 m_config->readConfig();
826 m_config->setVersion( KDEPIM_VERSION );
828 if ( newNote ) {
829 // until kdelibs provides copying of KConfigSkeletons (KDE 3.4)
830 KNotesGlobalConfig *globalConfig = KNotesGlobalConfig::self();
831 m_config->setBgColor( globalConfig->bgColor() );
832 m_config->setFgColor( globalConfig->fgColor() );
833 m_config->setWidth( globalConfig->width() );
834 m_config->setHeight( globalConfig->height() );
836 m_config->setFont( globalConfig->font() );
837 m_config->setTitleFont( globalConfig->titleFont() );
838 m_config->setAutoIndent( globalConfig->autoIndent() );
839 m_config->setRichText( globalConfig->richText() );
840 m_config->setTabSize( globalConfig->tabSize() );
841 m_config->setReadOnly( globalConfig->readOnly() );
843 m_config->setDesktop( globalConfig->desktop() );
844 m_config->setHideNote( globalConfig->hideNote() );
845 m_config->setPosition( globalConfig->position() );
846 m_config->setShowInTaskbar( globalConfig->showInTaskbar() );
847 m_config->setRememberDesktop( globalConfig->rememberDesktop() );
848 m_config->setKeepAbove( globalConfig->keepAbove() );
849 m_config->setKeepBelow( globalConfig->keepBelow() );
851 m_config->writeConfig();
854 // set up the look&feel of the note
855 setFrameStyle( Panel | Raised );
856 setMinimumSize( 20, 20 );
857 setBackgroundRole( QPalette::Base );
859 m_editor->setContentsMargins( 0, 0, 0, 0 );
860 m_editor->setBackgroundRole( QPalette::Base );
861 m_editor->setFrameStyle( NoFrame );
862 m_editor->setText( m_journal->description() );
864 // load the display configuration of the note
865 uint width = m_config->width();
866 uint height = m_config->height();
867 resize( width, height );
869 // let KWin do the placement if the position is illegal--at least 10 pixels
870 // of a note need to be visible
871 const QPoint& position = m_config->position();
872 QRect desk = kapp->desktop()->rect();
873 desk.adjust( 10, 10, -10, -10 );
874 if ( desk.intersects( QRect( position, QSize( width, height ) ) ) ) {
875 move( position ); // do before calling show() to avoid flicker
878 // config items in the journal have priority
879 QString property = m_journal->customProperty( "KNotes", "FgColor" );
880 if ( !property.isNull() ) {
881 m_config->setFgColor( QColor( property ) );
882 } else {
883 m_journal->setCustomProperty( "KNotes", "FgColor",
884 m_config->fgColor().name() );
887 property = m_journal->customProperty( "KNotes", "BgColor" );
888 if ( !property.isNull() ) {
889 m_config->setBgColor( QColor( property ) );
890 } else {
891 m_journal->setCustomProperty( "KNotes", "BgColor",
892 m_config->bgColor().name() );
894 property = m_journal->customProperty( "KNotes", "RichText" );
895 if ( !property.isNull() ) {
896 m_config->setRichText( property == "true" ? true : false );
897 } else {
898 m_journal->setCustomProperty( "KNotes", "RichText",
899 m_config->richText() ? "true" : "false" );
902 // read configuration settings...
903 slotApplyConfig();
905 // if this is a new note put on current desktop - we can't use defaults
906 // in KConfig XT since only _changes_ will be stored in the config file
907 int desktop = m_config->desktop();
909 #ifdef Q_WS_X11
910 if ( ( desktop < 0 && desktop != NETWinInfo::OnAllDesktops ) ||
911 !m_config->rememberDesktop() )
912 desktop = KWindowSystem::currentDesktop();
913 #endif
915 // show the note if desired
916 if ( desktop != 0 && !m_config->hideNote() ) {
917 // to avoid flicker, call this before show()
918 toDesktop( desktop );
919 show();
921 // because KWin forgets about that for hidden windows
922 #ifdef Q_WS_X11
923 if ( desktop == NETWinInfo::OnAllDesktops ) {
924 toDesktop( desktop );
926 #endif
929 m_readOnly->setChecked( m_config->readOnly() );
930 slotUpdateReadOnly();
932 if ( m_config->keepAbove() ) {
933 m_keepAbove->setChecked( true );
934 } else if ( m_config->keepBelow() ) {
935 m_keepBelow->setChecked( true );
936 } else {
937 m_keepAbove->setChecked( false );
938 m_keepBelow->setChecked( false );
940 slotUpdateKeepAboveBelow();
942 // HACK: update the icon color - again after showing the note, to make kicker
943 // aware of the new colors
944 KIconEffect effect;
945 QPixmap icon = effect.apply( qApp->windowIcon().pixmap(
946 IconSize( KIconLoader::Desktop ),
947 IconSize( KIconLoader::Desktop ) ),
948 KIconEffect::Colorize,
949 1, m_config->bgColor(), false );
950 QPixmap miniIcon = effect.apply( qApp->windowIcon().pixmap(
951 IconSize( KIconLoader::Small ),
952 IconSize( KIconLoader::Small ) ),
953 KIconEffect::Colorize,
954 1, m_config->bgColor(), false );
955 #ifdef Q_WS_X11
956 KWindowSystem::setIcons( winId(), icon, miniIcon );
957 #endif
958 m_editor->document()->setModified( false );
961 void KNote::toDesktop( int desktop )
963 if ( desktop == 0 ) {
964 return;
967 #ifdef Q_WS_X11
968 if ( desktop == NETWinInfo::OnAllDesktops ) {
969 KWindowSystem::setOnAllDesktops( winId(), true );
970 } else {
971 KWindowSystem::setOnDesktop( winId(), desktop );
973 #endif
976 void KNote::setColor( const QColor &fg, const QColor &bg )
978 QPalette p = palette();
980 // better: from light(150) to light(100) to light(75)
981 // QLinearGradient g( width()/2, 0, width()/2, height() );
982 // g.setColorAt( 0, bg );
983 // g.setColorAt( 1, bg.dark(150) );
985 p.setColor( QPalette::Window, bg );
986 // p.setBrush( QPalette::Window, g );
987 p.setColor( QPalette::Base, bg );
988 // p.setBrush( QPalette::Base, g );
990 p.setColor( QPalette::WindowText, fg );
991 p.setColor( QPalette::Text, fg );
993 p.setColor( QPalette::Button, bg.dark( 116 ) );
994 p.setColor( QPalette::ButtonText, fg );
996 //p.setColor( QPalette::Highlight, bg );
997 //p.setColor( QPalette::HighlightedText, fg );
999 // order: Light, Midlight, Button, Mid, Dark, Shadow
1001 // the shadow
1002 p.setColor( QPalette::Light, bg.light( 180 ) );
1003 p.setColor( QPalette::Midlight, bg.light( 150 ) );
1004 p.setColor( QPalette::Mid, bg.light( 150 ) );
1005 p.setColor( QPalette::Dark, bg.dark( 108 ) );
1006 p.setColor( QPalette::Shadow, bg.dark( 116 ) );
1008 setPalette( p );
1010 // darker values for the active label
1011 p.setColor( QPalette::Active, QPalette::Base, bg.dark( 116 ) );
1013 m_label->setPalette( p );
1015 // set the text color
1016 m_editor->setTextColor( fg );
1018 // update the icon color
1019 KIconEffect effect;
1020 QPixmap icon = effect.apply( qApp->windowIcon().pixmap(
1021 IconSize( KIconLoader::Desktop ),
1022 IconSize( KIconLoader::Desktop ) ),
1023 KIconEffect::Colorize, 1, bg, false );
1024 QPixmap miniIcon = effect.apply( qApp->windowIcon().pixmap(
1025 IconSize( KIconLoader::Small ),
1026 IconSize( KIconLoader::Small ) ),
1027 KIconEffect::Colorize, 1, bg, false );
1028 #ifdef Q_WS_X11
1029 KWindowSystem::setIcons( winId(), icon, miniIcon );
1030 #endif
1031 // update the color of the title
1032 updateFocus();
1033 emit sigColorChanged();
1036 void KNote::updateLabelAlignment()
1038 // if the name is too long to fit, left-align it, otherwise center it (#59028)
1039 const QString labelText = m_label->text();
1040 if ( m_label->fontMetrics().boundingRect( labelText ).width() >
1041 m_label->width() ) {
1042 m_label->setAlignment( Qt::AlignLeft );
1043 } else {
1044 m_label->setAlignment( Qt::AlignHCenter );
1048 void KNote::updateFocus()
1050 if ( hasFocus() )
1053 if ( !m_editor->isReadOnly() )
1055 if ( m_tool && m_tool->isHidden() && m_editor->acceptRichText() )
1057 m_tool->show();
1058 setGeometry( x(), y(), width(), height() + m_tool->height() );
1060 m_grip->show();
1062 else
1064 if ( m_tool && !m_tool->isHidden() ) {
1065 m_tool->hide();
1066 setGeometry( x(), y(), width(), height() - m_tool->height() );
1067 updateLayout(); // to update the minimum height
1069 m_grip->hide();
1072 else
1074 m_grip->hide();
1076 if ( m_tool && !m_tool->isHidden() )
1078 m_tool->hide();
1079 setGeometry( x(), y(), width(), height() - m_tool->height() );
1080 updateLayout(); // to update the minimum height
1085 void KNote::updateLayout()
1087 // TODO: remove later if no longer needed.
1088 updateLabelAlignment();
1091 // -------------------- protected methods -------------------- //
1093 void KNote::contextMenuEvent( QContextMenuEvent *e )
1095 if ( m_menu ) {
1096 m_menu->popup( e->globalPos() );
1100 void KNote::showEvent( QShowEvent * )
1102 if ( m_config->hideNote() ) {
1103 // KWin does not preserve these properties for hidden windows
1104 slotUpdateKeepAboveBelow();
1105 slotUpdateShowInTaskbar();
1106 toDesktop( m_config->desktop() );
1107 move( m_config->position() );
1108 m_config->setHideNote( false );
1112 void KNote::resizeEvent( QResizeEvent *qre )
1114 QFrame::resizeEvent( qre );
1115 updateLayout();
1118 void KNote::closeEvent( QCloseEvent * event )
1120 event->ignore(); //We don't want to close (and delete the widget). Just hide it
1121 slotClose();
1124 void KNote::dragEnterEvent( QDragEnterEvent *e )
1126 if ( !m_config->readOnly() ) {
1127 e->setAccepted( e->mimeData()->hasColor() );
1131 void KNote::dropEvent( QDropEvent *e )
1133 if ( m_config->readOnly() ) {
1134 return;
1137 const QMimeData *md = e->mimeData();
1138 if ( md->hasColor() ) {
1139 QColor bg = qvariant_cast<QColor>( md->colorData() );
1140 setColor( palette().color( foregroundRole() ), bg );
1141 m_journal->setCustomProperty( "KNotes", "BgColor", bg.name() );
1142 m_config->setBgColor( bg );
1146 bool KNote::event( QEvent *ev )
1148 if ( ev->type() == QEvent::LayoutRequest ) {
1149 updateLayout();
1150 return true;
1151 } else {
1152 return QFrame::event( ev );
1156 bool KNote::eventFilter( QObject *o, QEvent *ev )
1158 if ( ev->type() == QEvent::DragEnter &&
1159 static_cast<QDragEnterEvent*>( ev )->mimeData()->hasColor() ) {
1160 dragEnterEvent( static_cast<QDragEnterEvent *>( ev ) );
1161 return true;
1164 if ( ev->type() == QEvent::Drop &&
1165 static_cast<QDropEvent *>( ev )->mimeData()->hasColor() ) {
1166 dropEvent( static_cast<QDropEvent *>( ev ) );
1167 return true;
1170 if ( o == m_label ) {
1171 QMouseEvent *e = ( QMouseEvent * )ev;
1173 if ( ev->type() == QEvent::MouseButtonDblClick ) {
1174 if(!m_editor->isReadOnly())
1175 slotRename();
1178 if ( ev->type() == QEvent::MouseButtonPress &&
1179 ( e->button() == Qt::LeftButton || e->button() == Qt::MidButton ) ) {
1180 #ifdef Q_WS_X11
1181 e->button() == Qt::LeftButton ? KWindowSystem::raiseWindow( winId() )
1182 : KWindowSystem::lowerWindow( winId() );
1184 XUngrabPointer( QX11Info::display(), QX11Info::appTime() );
1185 NETRootInfo wm_root( QX11Info::display(), NET::WMMoveResize );
1186 wm_root.moveResizeRequest( winId(), e->globalX(), e->globalY(),
1187 NET::Move );
1188 #endif
1189 return true;
1192 if ( ev->type() == QEvent::MouseButtonRelease ) {
1193 #ifdef Q_WS_X11
1194 NETRootInfo wm_root( QX11Info::display(), NET::WMMoveResize );
1195 wm_root.moveResizeRequest( winId(), e->globalX(), e->globalY(),
1196 NET::MoveResizeCancel );
1197 #endif
1198 return false;
1201 return false;
1204 if ( o == m_editor ) {
1205 if ( ev->type() == QEvent::FocusOut ) {
1206 QFocusEvent *fe = static_cast<QFocusEvent *>( ev );
1207 if ( fe->reason() != Qt::PopupFocusReason &&
1208 fe->reason() != Qt::MouseFocusReason ) {
1209 updateFocus();
1210 if ( isModified() ) {
1211 saveConfig();
1212 if ( !m_blockEmitDataChanged )
1213 saveData();
1216 } else if ( ev->type() == QEvent::FocusIn ) {
1217 updateFocus();
1220 return false;
1223 return false;
1227 #include "knote.moc"