1 /***************************************************************************
2 * Copyright (C) 2003 by S�astien Laot *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 #include <q3dragobject.h>
27 #include <QWheelEvent>
28 #include <QContextMenuEvent>
29 #include <QFocusEvent>
30 #include <QPaintEvent>
31 #include <QDragMoveEvent>
32 #include <Q3VBoxLayout>
33 #include <QDragLeaveEvent>
36 #include <QResizeEvent>
39 #include <Q3PopupMenu>
40 #include <QDragEnterEvent>
41 #include <Q3HBoxLayout>
42 #include <Q3ValueList>
43 #include <QMouseEvent>
44 #include <QCloseEvent>
45 #include <Q3GridLayout>
48 #include <q3listview.h>
50 #include <q3simplerichtext.h>
51 #include <qpushbutton.h>
52 #include <ktextedit.h>
54 #include <qstringlist.h>
55 #include <kapplication.h>
56 #include <kglobalsettings.h>
57 #include <kopenwith.h>
60 #include <kglobalaccel.h>
63 #include <qfileinfo.h>
64 #include <kfiledialog.h>
65 #include <kaboutdata.h>
66 #include <klineedit.h>
67 #include <ksavefile.h>
71 #include <unistd.h> // For sleep()
74 #include <kiconloader.h>
77 #include <q3toolbar.h>
78 #include <qclipboard.h>
80 #include <kmessagebox.h>
81 #include <qinputdialog.h>
85 #include <stdlib.h> // rand() function
86 #include <qdatetime.h> // seed for rand()
91 #include "notefactory.h"
96 #include "backgroundmanager.h"
99 #include "debugwindow.h"
100 #include "exporterdialog.h"
102 #include "popupmenu.h"
109 /** Class NoteSelection: */
111 NoteSelection
* NoteSelection::nextStacked()
113 // First, search in the childs:
115 if (firstChild
->note
&& firstChild
->note
->content())
118 return firstChild
->nextStacked();
120 // Then, in the next:
122 if (next
->note
&& next
->note
->content())
125 return next
->nextStacked();
127 // And finally, in the parent:
128 NoteSelection
*node
= parent
;
131 if (node
->next
->note
&& node
->next
->note
->content())
134 return node
->next
->nextStacked();
142 NoteSelection
* NoteSelection::firstStacked()
147 if (note
&& note
->content())
150 return nextStacked();
153 void NoteSelection::append(NoteSelection
*node
)
159 NoteSelection
*last
= firstChild
;
172 int NoteSelection::count()
179 for (NoteSelection
*node
= this; node
; node
= node
->next
)
180 if (node
->note
&& node
->note
->content())
183 count
+= node
->firstChild
->count();
188 Q3ValueList
<Note
*> NoteSelection::parentGroups()
190 Q3ValueList
<Note
*> groups
;
193 for (NoteSelection
*node
= firstStacked(); node
; node
= node
->nextStacked())
194 // For each parent groups of the note:
195 for (Note
*note
= node
->note
->parentNote(); note
; note
= note
->parentNote())
196 // Add it (if it was not already in the list):
197 if (!note
->isColumn() && !groups
.contains(note
))
203 /** Class DecoratedBasket: */
205 DecoratedBasket::DecoratedBasket(QWidget
*parent
, const QString
&folderName
, const char *name
, Qt::WFlags fl
)
206 : QWidget(parent
, name
, fl
)
208 m_layout
= new Q3VBoxLayout(this);
209 m_filter
= new FilterBar(this);
210 m_basket
= new Basket(this, folderName
);
211 m_layout
->addWidget(m_basket
);
212 setFilterBarPosition(Settings::filterOnTop());
214 m_filter
->setShown(true);
215 m_basket
->setFocus(); // To avoid the filter bar have focus on load
217 connect( m_filter
, SIGNAL(newFilter(const FilterData
&)), m_basket
, SLOT(newFilter(const FilterData
&)) );
218 connect( m_filter
, SIGNAL(escapePressed()), m_basket
, SLOT(cancelFilter()) );
219 connect( m_filter
, SIGNAL(returnPressed()), m_basket
, SLOT(validateFilter()) );
221 connect( m_basket
, SIGNAL(postMessage(const QString
&)), Global::bnpView
, SLOT(postStatusbarMessage(const QString
&)) );
222 connect( m_basket
, SIGNAL(setStatusBarText(const QString
&)), Global::bnpView
, SLOT(setStatusBarHint(const QString
&)) );
223 connect( m_basket
, SIGNAL(resetStatusBarText()), Global::bnpView
, SLOT(updateStatusBarHint()) );
226 DecoratedBasket::~DecoratedBasket()
230 void DecoratedBasket::setFilterBarPosition(bool onTop
)
232 m_layout
->remove(m_filter
);
234 m_layout
->insertWidget(0, m_filter
);
235 setTabOrder(this/*(QWidget*)parent()*/, m_filter
);
236 setTabOrder(m_filter
, m_basket
);
237 setTabOrder(m_basket
, (QWidget
*)parent());
239 m_layout
->addWidget(m_filter
);
240 setTabOrder(this/*(QWidget*)parent()*/, m_basket
);
241 setTabOrder(m_basket
, m_filter
);
242 setTabOrder(m_filter
, (QWidget
*)parent());
246 void DecoratedBasket::setFilterBarShown(bool show
, bool switchFocus
)
248 // m_basket->setShowFilterBar(true);//show);
250 // In this order (m_basket and then m_filter) because setShown(false)
251 // will call resetFilter() that will update actions, and then check the
252 // Ctrl+F action whereas it should be unchecked
253 // FIXME: It's very uggly all those things
254 m_filter
->setShown(true);//show);
257 m_filter
->setEditFocus();
258 } else if (m_filter
->hasEditFocus())
259 m_basket
->setFocus();
262 void DecoratedBasket::resetFilter()
267 /** Class TransparentWidget */
269 TransparentWidget::TransparentWidget(Basket
*basket
)
270 : QWidget(basket
->viewport(), "", Qt::WNoAutoErase
), m_basket(basket
)
272 setFocusPolicy(Qt::NoFocus
);
273 setWFlags(Qt::WNoAutoErase
);
274 setMouseTracking(true); // To receive mouseMoveEvents
276 basket
->viewport()->installEventFilter(this);
279 /*void TransparentWidget::reparent(QWidget *parent, Qt::WFlags f, const QPoint &p, bool showIt)
281 QWidget::reparent(parent, Qt::WNoAutoErase, p, showIt);
284 void TransparentWidget::setPosition(int x
, int y
)
290 void TransparentWidget::paintEvent(QPaintEvent
*event
)
292 QWidget::paintEvent(event
);
293 QPainter
painter(this);
297 painter
.translate(-m_x
, -m_y
);
298 m_basket
->drawContents(&painter
, m_x
, m_y
, width(), height());
300 // painter.restore();
301 // painter.setPen(Qt::blue);
302 // painter.drawRect(0, 0, width(), height());
305 void TransparentWidget::mouseMoveEvent(QMouseEvent
*event
)
307 QMouseEvent
*translated
= new QMouseEvent(QEvent::MouseMove
, event
->pos() + QPoint(m_x
, m_y
), event
->button(), event
->state());
308 m_basket
->contentsMouseMoveEvent(translated
);
312 bool TransparentWidget::eventFilter(QObject */
*object*/
, QEvent
*event
)
314 // If the parent basket viewport has changed, we should change too:
315 if (event
->type() == QEvent::Paint
)
318 return false; // Event not consumed, in every cases (because it's only a notification)!
323 const int Basket::FRAME_DELAY
= 50/*1500*/; // Delay between two animation "frames" in milliseconds
326 * Convenient function (defined in note.cpp !):
328 void drawGradient( QPainter
*p
, const QColor
&colorTop
, const QColor
& colorBottom
,
329 int x
, int y
, int w
, int h
,
330 bool sunken
, bool horz
, bool flat
);
333 * Defined in note.cpp:
335 extern void substractRectOnAreas(const QRect
&rectToSubstract
, Q3ValueList
<QRect
> &areas
, bool andRemove
= true);
337 void debugZone(int zone
)
341 case Note::Handle
: s
= "Handle"; break;
342 case Note::Group
: s
= "Group"; break;
343 case Note::TagsArrow
: s
= "TagsArrow"; break;
344 case Note::Custom0
: s
= "Custom0"; break;
345 case Note::GroupExpander
: s
= "GroupExpander"; break;
346 case Note::Content
: s
= "Content"; break;
347 case Note::Link
: s
= "Link"; break;
348 case Note::TopInsert
: s
= "TopInsert"; break;
349 case Note::TopGroup
: s
= "TopGroup"; break;
350 case Note::BottomInsert
: s
= "BottomInsert"; break;
351 case Note::BottomGroup
: s
= "BottomGroup"; break;
352 case Note::BottomColumn
: s
= "BottomColumn"; break;
353 case Note::None
: s
= "None"; break;
355 if (zone
== Note::Emblem0
)
358 s
= "Emblem0+" + QString::number(zone
- Note::Emblem0
);
361 std::cout
<< s
<< std::endl
;
364 #define FOR_EACH_NOTE(noteVar) \
365 for (Note *noteVar = firstNote(); noteVar; noteVar = noteVar->next())
367 void Basket::prependNoteIn(Note
*note
, Note
*in
)
370 // No note to prepend:
377 Note
*last
= note
->lastSibling();
379 for (Note
*n
= note
; n
; n
= n
->next())
380 n
->setParentNote(in
);
381 // note->setPrev(0L);
382 last
->setNext(in
->firstChild());
384 if (in
->firstChild())
385 in
->firstChild()->setPrev(last
);
387 in
->setFirstChild(note
);
390 signalCountsChanged();
392 // Prepend it directly in the basket:
393 appendNoteBefore(note
, firstNote());
396 void Basket::appendNoteIn(Note
*note
, Note
*in
)
399 // No note to append:
406 // Note *last = note->lastSibling();
407 Note
*lastChild
= in
->lastChild();
409 for (Note
*n
= note
; n
; n
= n
->next())
410 n
->setParentNote(in
);
411 note
->setPrev(lastChild
);
412 // last->setNext(0L);
414 if (!in
->firstChild())
415 in
->setFirstChild(note
);
418 lastChild
->setNext(note
);
421 signalCountsChanged();
423 // Prepend it directly in the basket:
424 appendNoteAfter(note
, lastNote());
427 void Basket::appendNoteAfter(Note
*note
, Note
*after
)
430 // No note to append:
434 // By default, insert after the last note:
437 if (m_loaded
&& after
&& !after
->isFree() && !after
->isColumn())
438 for (Note
*n
= note
; n
; n
= n
->next())
439 n
->inheritTagsOf(after
);
441 // if (!alreadyInBasket)
444 Note
*last
= note
->lastSibling();
447 for (Note
*n
= note
; n
; n
= n
->next())
448 n
->setParentNote(after
->parentNote());
449 note
->setPrev(after
);
450 last
->setNext(after
->next());
451 after
->setNext(note
);
453 last
->next()->setPrev(last
);
455 // There is no note in the basket:
456 for (Note
*n
= note
; n
; n
= n
->next())
463 // if (!alreadyInBasket)
465 signalCountsChanged();
468 void Basket::appendNoteBefore(Note
*note
, Note
*before
)
471 // No note to append:
475 // By default, insert before the first note:
476 before
= firstNote();
478 if (m_loaded
&& before
&& !before
->isFree() && !before
->isColumn())
479 for (Note
*n
= note
; n
; n
= n
->next())
480 n
->inheritTagsOf(before
);
484 Note
*last
= note
->lastSibling();
487 for (Note
*n
= note
; n
; n
= n
->next())
488 n
->setParentNote(before
->parentNote());
489 note
->setPrev(before
->prev());
490 last
->setNext(before
);
491 before
->setPrev(last
);
493 note
->prev()->setNext(note
);
495 if (note
->parentNote())
496 note
->parentNote()->setFirstChild(note
);
501 // There is no note in the basket:
502 for (Note
*n
= note
; n
; n
= n
->next())
510 signalCountsChanged();
513 DecoratedBasket
* Basket::decoration()
515 return (DecoratedBasket
*)parent();
518 void Basket::preparePlug(Note
*note
)
520 // Select only the new notes, compute the new notes count and the new number of found notes:
526 for (Note
*n
= note
; n
; n
= n
->next()) {
528 n
->setSelectedRecursivly(true); // Notes should have a parent basket (and they have, so that's OK).
530 founds
+= n
->newFilter(decoration()->filterData());
534 m_countFounds
+= founds
;
536 // Focus the last inserted note:
537 if (m_loaded
&& last
) {
538 setFocusedNote(last
);
539 m_startOfShiftSelectionNote
= (last
->isGroup() ? last
->lastRealChild() : last
);
542 // If some notes don't match (are hidden), tell it to the user:
543 if (m_loaded
&& founds
< count
) {
544 if (count
== 1) postMessage( i18n("The new note does not match the filter and is hidden.") );
545 else if (founds
== count
- 1) postMessage( i18n("A new note does not match the filter and is hidden.") );
546 else if (founds
> 0) postMessage( i18n("Some new notes do not match the filter and are hidden.") );
547 else postMessage( i18n("The new notes do not match the filter and are hidden.") );
551 void Basket::unplugNote(Note
*note
)
553 // If there is nothing to do...
557 // if (!willBeReplugged) {
558 note
->setSelectedRecursivly(false); // To removeSelectedNote() and decrease the selectedsCount.
559 m_count
-= note
->count();
560 m_countFounds
-= note
->newFilter(decoration()->filterData());
561 signalCountsChanged();
564 // If it was the first note, change the first note:
565 if (m_firstNote
== note
)
566 m_firstNote
= note
->next();
568 // Change previous and next notes:
570 note
->prev()->setNext(note
->next());
572 note
->next()->setPrev(note
->prev());
574 if (note
->parentNote()) {
575 // If it was the first note of a group, change the first note of the group:
576 if (note
->parentNote()->firstChild() == note
)
577 note
->parentNote()->setFirstChild( note
->next() );
579 if (!note
->parentNote()->isColumn()) {
580 // Ungroup if still 0 note inside parent group:
581 if ( ! note
->parentNote()->firstChild() )
582 unplugNote(note
->parentNote()); // TODO delete
584 // Ungroup if still 1 note inside parent group:
585 else if ( ! note
->parentNote()->firstChild()->next() )
586 ungroupNote(note
->parentNote());
590 note
->setParentNote(0);
594 // recomputeBlankRects(); // FIXME: called too much time. It's here because when dragging and moving a note to another basket and then go back to the original basket, the note is deleted but the note rect is not painter anymore.
597 void Basket::ungroupNote(Note
*group
)
599 Note
*note
= group
->firstChild();
600 Note
*lastGroupedNote
= group
;
603 // Move all notes after the group (not before, to avoid to change m_firstNote or group->m_firstChild):
605 nextNote
= note
->next();
607 if (lastGroupedNote
->next())
608 lastGroupedNote
->next()->setPrev(note
);
609 note
->setNext(lastGroupedNote
->next());
610 lastGroupedNote
->setNext(note
);
611 note
->setParentNote(group
->parentNote());
612 note
->setPrev(lastGroupedNote
);
614 note
->setGroupWidth(group
->groupWidth() - Note::GROUP_WIDTH
);
615 lastGroupedNote
= note
;
620 group
->setFirstChild(0);
621 unplugNote(group
); // TODO: delete
626 void Basket::groupNoteBefore(Note
*note
, Note
*with
)
629 // No note to group or nowhere to group it:
632 // if (m_loaded && before && !with->isFree() && !with->isColumn())
633 for (Note
*n
= note
; n
; n
= n
->next())
634 n
->inheritTagsOf(with
);
638 Note
*last
= note
->lastSibling();
640 Note
*group
= new Note(this);
641 group
->setPrev(with
->prev());
642 group
->setNext(with
->next());
643 group
->setX(with
->x());
644 group
->setY(with
->y());
645 if (with
->parentNote() && with
->parentNote()->firstChild() == with
)
646 with
->parentNote()->setFirstChild(group
);
647 else if (m_firstNote
== with
)
649 group
->setParentNote(with
->parentNote());
650 group
->setFirstChild(note
);
651 group
->setGroupWidth(with
->groupWidth() + Note::GROUP_WIDTH
);
654 with
->prev()->setNext(group
);
656 with
->next()->setPrev(group
);
657 with
->setParentNote(group
);
661 for (Note
*n
= note
; n
; n
= n
->next())
662 n
->setParentNote(group
);
663 // note->setPrev(0L);
667 signalCountsChanged();
670 void Basket::groupNoteAfter(Note
*note
, Note
*with
)
673 // No note to group or nowhere to group it:
676 // if (m_loaded && before && !with->isFree() && !with->isColumn())
677 for (Note
*n
= note
; n
; n
= n
->next())
678 n
->inheritTagsOf(with
);
682 // Note *last = note->lastSibling();
684 Note
*group
= new Note(this);
685 group
->setPrev(with
->prev());
686 group
->setNext(with
->next());
687 group
->setX(with
->x());
688 group
->setY(with
->y());
689 if (with
->parentNote() && with
->parentNote()->firstChild() == with
)
690 with
->parentNote()->setFirstChild(group
);
691 else if (m_firstNote
== with
)
693 group
->setParentNote(with
->parentNote());
694 group
->setFirstChild(with
);
695 group
->setGroupWidth(with
->groupWidth() + Note::GROUP_WIDTH
);
698 with
->prev()->setNext(group
);
700 with
->next()->setPrev(group
);
701 with
->setParentNote(group
);
705 for (Note
*n
= note
; n
; n
= n
->next())
706 n
->setParentNote(group
);
708 // last->setNext(0L);
711 signalCountsChanged();
714 void Basket::loadNotes(const QDomElement
¬es
, Note
*parent
)
717 for (QDomNode n
= notes
.firstChild(); !n
.isNull(); n
= n
.nextSibling()) {
718 QDomElement e
= n
.toElement();
719 if (e
.isNull()) // Cannot handle that!
723 if (e
.tagName() == "group") {
724 note
= new Note(this); // 1. Create the group...
725 loadNotes(e
, note
); // 3. ... And populate it with child notes.
726 int noteCount
= note
->count();
727 if (noteCount
> 0 || (parent
== 0 && !isFreeLayout())) { // But don't remove columns!
728 appendNoteIn(note
, parent
); // 2. ... Insert it... FIXME: Initially, the if() the insrtion was the step 2. Was it on purpose?
729 // The notes in the group are counted two times (it's why appendNoteIn() was called before loadNotes):
730 m_count
-= noteCount
;// TODO: Recompute note count every time noteCount() is emitted!
731 m_countFounds
-= noteCount
;
734 // Load a Content-Based Note:
735 if (e
.tagName() == "note" || e
.tagName() == "item") { // Keep compatible with 0.6.0 Alpha 1
736 note
= new Note(this); // Create the note...
737 NoteFactory__loadNode(XMLWork::getElement(e
, "content"), e
.attribute("type"), note
, /*lazyLoad=*/m_finishLoadOnFirstShow
); // ... Populate it with content...
738 if (e
.attribute("type") == "text")
739 m_shouldConvertPlainTextNotes
= true; // Convert Pre-0.6.0 baskets: plain text notes should be converted to rich text ones once all is loaded!
740 appendNoteIn(note
, parent
); // ... And insert it.
742 if (e
.hasAttribute("added"))
743 note
->setAddedDate( QDateTime::fromString(e
.attribute("added"), Qt::ISODate
));
744 if (e
.hasAttribute("lastModification"))
745 note
->setLastModificationDate(QDateTime::fromString(e
.attribute("lastModification"), Qt::ISODate
));
747 // If we successfully loaded a note:
749 // Free Note Properties:
750 if (note
->isFree()) {
751 int x
= e
.attribute("x").toInt();
752 int y
= e
.attribute("y").toInt();
753 note
->setX(x
< 0 ? 0 : x
);
754 note
->setY(y
< 0 ? 0 : y
);
756 // Resizeable Note Properties:
757 if (note
->hasResizer() || note
->isColumn())
758 note
->setGroupWidth(e
.attribute("width", "200").toInt());
760 if (note
->isGroup() && !note
->isColumn() && XMLWork::trueOrFalse(e
.attribute("folded", "false")))
761 note
->toggleFolded(false);
763 if (note
->content()) {
764 QString tagsString
= XMLWork::getElementText(e
, "tags", "");
765 QStringList tagsId
= QStringList::split(";", tagsString
);
766 for (QStringList::iterator it
= tagsId
.begin(); it
!= tagsId
.end(); ++it
) {
767 State
*state
= Tag::stateForId(*it
);
769 note
->addState(state
, /*orReplace=*/true);
773 // kapp->processEvents();
777 void Basket::saveNotes(QDomDocument
&document
, QDomElement
&element
, Note
*parent
)
779 Note
*note
= (parent
? parent
->firstChild() : firstNote());
782 QDomElement noteElement
= document
.createElement(note
->isGroup() ? "group" : "note");
783 element
.appendChild(noteElement
);
784 // Free Note Properties:
785 if (note
->isFree()) {
786 noteElement
.setAttribute("x", note
->finalX());
787 noteElement
.setAttribute("y", note
->finalY());
789 // Resizeable Note Properties:
790 if (note
->hasResizer())
791 noteElement
.setAttribute("width", note
->groupWidth());
793 if (note
->isGroup() && !note
->isColumn())
794 noteElement
.setAttribute("folded", XMLWork::trueOrFalse(note
->isFolded()));
796 if (note
->content()) {
798 noteElement
.setAttribute("added", note
->addedDate().toString(Qt::ISODate
) );
799 noteElement
.setAttribute("lastModification", note
->lastModificationDate().toString(Qt::ISODate
));
801 noteElement
.setAttribute("type", note
->content()->lowerTypeName());
802 QDomElement content
= document
.createElement("content");
803 noteElement
.appendChild(content
);
804 note
->content()->saveToNode(document
, content
);
806 if (note
->states().count() > 0) {
808 for (State::List::iterator it
= note
->states().begin(); it
!= note
->states().end(); ++it
)
809 tags
+= (tags
.isEmpty() ? "" : ";") + (*it
)->id();
810 XMLWork::addElement(document
, noteElement
, "tags", tags
);
814 saveNotes(document
, noteElement
, note
);
815 // Go to the Next One:
820 void Basket::loadProperties(const QDomElement
&properties
)
822 // Compute Default Values for When Loading the Properties:
823 QString defaultBackgroundColor
= (backgroundColorSetting().isValid() ? backgroundColorSetting().name() : "");
824 QString defaultTextColor
= (textColorSetting().isValid() ? textColorSetting().name() : "");
826 // Load the Properties:
827 QString icon
= XMLWork::getElementText(properties
, "icon", this->icon() );
828 QString name
= XMLWork::getElementText(properties
, "name", basketName() );
830 QDomElement appearance
= XMLWork::getElement(properties
, "appearance");
831 // In 0.6.0-Alpha versions, there was a typo error: "backround" instead of "background"
832 QString backgroundImage
= appearance
.attribute( "backgroundImage", appearance
.attribute( "backroundImage", backgroundImageName() ) );
833 QString backgroundColorString
= appearance
.attribute( "backgroundColor", appearance
.attribute( "backroundColor", defaultBackgroundColor
) );
834 QString textColorString
= appearance
.attribute( "textColor", defaultTextColor
);
835 QColor backgroundColor
= (backgroundColorString
.isEmpty() ? QColor() : QColor(backgroundColorString
));
836 QColor textColor
= (textColorString
.isEmpty() ? QColor() : QColor(textColorString
) );
838 QDomElement disposition
= XMLWork::getElement(properties
, "disposition");
839 bool free
= XMLWork::trueOrFalse( disposition
.attribute( "free", XMLWork::trueOrFalse(isFreeLayout()) ) );
840 int columnCount
= disposition
.attribute( "columnCount", QString::number(this->columnsCount()) ).toInt();
841 bool mindMap
= XMLWork::trueOrFalse( disposition
.attribute( "mindMap", XMLWork::trueOrFalse(isMindMap()) ) );
843 QDomElement shortcut
= XMLWork::getElement(properties
, "shortcut");
844 QString actionStrings
[] = { "show", "globalShow", "globalSwitch" };
845 KShortcut combination
= KShortcut( shortcut
.attribute( "combination", m_action
->shortcut().toStringInternal() ) );
846 QString actionString
= shortcut
.attribute( "action" );
847 int action
= shortcutAction();
848 if (actionString
== actionStrings
[0]) action
= 0;
849 if (actionString
== actionStrings
[1]) action
= 1;
850 if (actionString
== actionStrings
[2]) action
= 2;
852 QDomElement protection
= XMLWork::getElement(properties
, "protection");
853 m_encryptionType
= protection
.attribute( "type" ).toInt();
854 m_encryptionKey
= protection
.attribute( "key" );
856 // Apply the Properties:
857 setDisposition((free
? (mindMap
? 2 : 1) : 0), columnCount
);
858 setShortcut(combination
, action
);
859 setAppearance(icon
, name
, backgroundImage
, backgroundColor
, textColor
); // Will emit propertiesChanged(this)
862 void Basket::saveProperties(QDomDocument
&document
, QDomElement
&properties
)
864 XMLWork::addElement( document
, properties
, "name", basketName() );
865 XMLWork::addElement( document
, properties
, "icon", icon() );
867 QDomElement appearance
= document
.createElement("appearance");
868 properties
.appendChild(appearance
);
869 appearance
.setAttribute( "backgroundImage", backgroundImageName() );
870 appearance
.setAttribute( "backgroundColor", backgroundColorSetting().isValid() ? backgroundColorSetting().name() : "" );
871 appearance
.setAttribute( "textColor", textColorSetting().isValid() ? textColorSetting().name() : "" );
873 QDomElement disposition
= document
.createElement("disposition");
874 properties
.appendChild(disposition
);
875 disposition
.setAttribute( "free", XMLWork::trueOrFalse(isFreeLayout()) );
876 disposition
.setAttribute( "columnCount", QString::number(columnsCount()) );
877 disposition
.setAttribute( "mindMap", XMLWork::trueOrFalse(isMindMap()) );
879 QDomElement shortcut
= document
.createElement("shortcut");
880 properties
.appendChild(shortcut
);
881 QString actionStrings
[] = { "show", "globalShow", "globalSwitch" };
882 shortcut
.setAttribute( "combination", m_action
->shortcut().toStringInternal() );
883 shortcut
.setAttribute( "action", actionStrings
[shortcutAction()] );
885 QDomElement protection
= document
.createElement("protection");
886 properties
.appendChild(protection
);
887 protection
.setAttribute( "type", m_encryptionType
);
888 protection
.setAttribute( "key", m_encryptionKey
);
891 void Basket::subscribeBackgroundImages()
893 if (!m_backgroundImageName
.isEmpty()) {
894 Global::backgroundManager
->subscribe(m_backgroundImageName
);
895 Global::backgroundManager
->subscribe(m_backgroundImageName
, this->backgroundColor());
896 Global::backgroundManager
->subscribe(m_backgroundImageName
, selectionRectInsideColor());
897 m_backgroundPixmap
= Global::backgroundManager
->pixmap(m_backgroundImageName
);
898 m_opaqueBackgroundPixmap
= Global::backgroundManager
->opaquePixmap(m_backgroundImageName
, this->backgroundColor());
899 m_selectedBackgroundPixmap
= Global::backgroundManager
->opaquePixmap(m_backgroundImageName
, selectionRectInsideColor());
900 m_backgroundTiled
= Global::backgroundManager
->tiled(m_backgroundImageName
);
904 void Basket::unsubscribeBackgroundImages()
906 if (hasBackgroundImage()) {
907 Global::backgroundManager
->unsubscribe(m_backgroundImageName
);
908 Global::backgroundManager
->unsubscribe(m_backgroundImageName
, this->backgroundColor());
909 Global::backgroundManager
->unsubscribe(m_backgroundImageName
, selectionRectInsideColor());
910 m_backgroundPixmap
= 0;
911 m_opaqueBackgroundPixmap
= 0;
912 m_selectedBackgroundPixmap
= 0;
916 void Basket::setAppearance(const QString
&icon
, const QString
&name
, const QString
&backgroundImage
, const QColor
&backgroundColor
, const QColor
&textColor
)
918 unsubscribeBackgroundImages();
922 m_backgroundImageName
= backgroundImage
;
923 m_backgroundColorSetting
= backgroundColor
;
924 m_textColorSetting
= textColor
;
926 m_action
->setText("BASKET SHORTCUT: " + name
);
928 // Basket should ALWAYS have an icon (the "basket" icon by default):
929 QPixmap iconTest
= kapp
->iconLoader()->loadIcon(m_icon
, KIconLoader::NoGroup
, 16, KIconLoader::DefaultState
, 0L, /*canReturnNull=*/true);
930 if (iconTest
.isNull())
933 // We don't request the background images if it's not loaded yet (to make the application startup fast).
934 // When the basket is loading (because requested by the user: he/she want to access it)
935 // it load the properties, subscribe to (and then load) the images, update the "Loading..." message with the image,
936 // load all the notes and it's done!
937 if (m_loadingLaunched
)
938 subscribeBackgroundImages();
940 recomputeAllStyles(); // If a note have a tag with the same background color as the basket one, then display a "..."
941 recomputeBlankRects(); // See the drawing of blank areas in Basket::drawContents()
945 if (isDuringEdit() && m_editor
->widget()) {
946 m_editor
->widget()->setPaletteBackgroundColor( m_editor
->note()->backgroundColor() );
947 m_editor
->widget()->setPaletteForegroundColor( m_editor
->note()->textColor() );
950 emit
propertiesChanged(this);
953 void Basket::setDisposition(int disposition
, int columnCount
)
955 static const int COLUMNS_LAYOUT
= 0;
956 static const int FREE_LAYOUT
= 1;
957 static const int MINDMAPS_LAYOUT
= 2;
959 int currentDisposition
= (isFreeLayout() ? (isMindMap() ? MINDMAPS_LAYOUT
: FREE_LAYOUT
) : COLUMNS_LAYOUT
);
961 if (currentDisposition
== COLUMNS_LAYOUT
&& disposition
== COLUMNS_LAYOUT
) {
962 if (firstNote() && columnCount
> m_columnsCount
) {
963 // Insert each new columns:
964 for (int i
= m_columnsCount
; i
< columnCount
; ++i
) {
965 Note
*newColumn
= new Note(this);
966 insertNote(newColumn
, /*clicked=*/lastNote(), /*zone=*/Note::BottomInsert
, QPoint(), /*animateNewPosition=*/false);
968 } else if (firstNote() && columnCount
< m_columnsCount
) {
969 Note
*column
= firstNote();
970 Note
*cuttedNotes
= 0;
971 for (int i
= 1; i
<= m_columnsCount
; ++i
) {
972 Note
*columnToRemove
= column
;
973 column
= column
->next();
974 if (i
> columnCount
) {
975 // Remove the columns that are too much:
976 unplugNote(columnToRemove
);
977 // "Cut" the content in the columns to be deleted:
978 if (columnToRemove
->firstChild()) {
979 for (Note
*it
= columnToRemove
->firstChild(); it
; it
= it
->next())
980 it
->setParentNote(0);
982 cuttedNotes
= columnToRemove
->firstChild();
984 Note
*lastCuttedNote
= cuttedNotes
;
985 while (lastCuttedNote
->next())
986 lastCuttedNote
= lastCuttedNote
->next();
987 lastCuttedNote
->setNext(columnToRemove
->firstChild());
988 columnToRemove
->firstChild()->setPrev(lastCuttedNote
);
990 columnToRemove
->setFirstChild(0);
994 // Paste the content in the last column:
996 insertNote(cuttedNotes
, /*clicked=*/lastNote(), /*zone=*/Note::BottomColumn
, QPoint(), /*animateNewPosition=*/true);
999 if (columnCount
!= m_columnsCount
) {
1000 m_columnsCount
= (columnCount
<= 0 ? 1 : columnCount
);
1001 equalizeColumnSizes(); // Will relayoutNotes()
1003 } else if (currentDisposition
== COLUMNS_LAYOUT
&& (disposition
== FREE_LAYOUT
|| disposition
== MINDMAPS_LAYOUT
)) {
1004 Note
*column
= firstNote();
1005 m_columnsCount
= 0; // Now, so relayoutNotes() will not relayout the free notes as if they were columns!
1007 // Move all childs on the first level:
1008 Note
*nextColumn
= column
->next();
1009 ungroupNote(column
);
1010 column
= nextColumn
;
1013 m_mindMap
= (disposition
== MINDMAPS_LAYOUT
);
1014 relayoutNotes(true);
1015 } else if ((currentDisposition
== FREE_LAYOUT
|| currentDisposition
== MINDMAPS_LAYOUT
) && disposition
== COLUMNS_LAYOUT
) {
1017 // TODO: Reorder notes!
1018 // Remove all notes (but keep a reference to them, we're not crazy ;-) ):
1019 Note
*notes
= m_firstNote
;
1023 // Insert the number of columns that is needed:
1024 Note
*lastInsertedColumn
= 0;
1025 for (int i
= 0; i
< columnCount
; ++i
) {
1026 Note
*column
= new Note(this);
1027 if (lastInsertedColumn
)
1028 insertNote(column
, /*clicked=*/lastInsertedColumn
, /*zone=*/Note::BottomInsert
, QPoint(), /*animateNewPosition=*/false);
1030 m_firstNote
= column
;
1031 lastInsertedColumn
= column
;
1033 // Reinsert the old notes in the first column:
1034 insertNote(notes
, /*clicked=*/firstNote(), /*zone=*/Note::BottomColumn
, QPoint(), /*animateNewPosition=*/true);
1037 // Insert the number of columns that is needed:
1038 Note
*lastInsertedColumn
= 0;
1039 for (int i
= 0; i
< columnCount
; ++i
) {
1040 Note
*column
= new Note(this);
1041 if (lastInsertedColumn
)
1042 insertNote(column
, /*clicked=*/lastInsertedColumn
, /*zone=*/Note::BottomInsert
, QPoint(), /*animateNewPosition=*/false);
1044 m_firstNote
= column
;
1045 lastInsertedColumn
= column
;
1048 m_columnsCount
= (columnCount
<= 0 ? 1 : columnCount
);
1049 equalizeColumnSizes(); // Will relayoutNotes()
1053 void Basket::equalizeColumnSizes()
1058 // Necessary to know the available space;
1059 relayoutNotes(true);
1061 int availableSpace
= visibleWidth();
1062 int columnWidth
= (visibleWidth() - (columnsCount()-1)*Note::GROUP_WIDTH
) / columnsCount();
1063 int columnCount
= columnsCount();
1064 Note
*column
= firstNote();
1066 int minGroupWidth
= column
->minRight() - column
->x();
1067 if (minGroupWidth
> columnWidth
) {
1068 availableSpace
-= minGroupWidth
;
1071 column
= column
->next();
1073 columnWidth
= (availableSpace
- (columnsCount()-1)*Note::GROUP_WIDTH
) / columnCount
;
1075 column
= firstNote();
1077 int minGroupWidth
= column
->minRight() - column
->x();
1078 if (minGroupWidth
> columnWidth
)
1079 column
->setGroupWidth(minGroupWidth
);
1081 column
->setGroupWidth(columnWidth
);
1082 column
= column
->next();
1085 relayoutNotes(true);
1088 void Basket::enableActions()
1090 Global::bnpView
->enableActions();
1091 setFocusPolicy(isLocked() ? Qt::NoFocus
: Qt::StrongFocus
);
1093 viewport()->setCursor(Qt::ArrowCursor
); // When locking, the cursor stays the last form it was
1101 DEBUG_WIN
<< "Basket[" + folderName() + "]: Saving...";
1104 QDomDocument
document(/*doctype=*/"basket");
1105 QDomElement root
= document
.createElement("basket");
1106 document
.appendChild(root
);
1108 // Create Properties Element and Populate It:
1109 QDomElement properties
= document
.createElement("properties");
1110 saveProperties(document
, properties
);
1111 root
.appendChild(properties
);
1113 // Create Notes Element and Populate It:
1114 QDomElement notes
= document
.createElement("notes");
1115 saveNotes(document
, notes
, 0);
1116 root
.appendChild(notes
);
1119 if(!saveToFile(fullPath() + ".basket", "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" + document
.toString()))
1121 DEBUG_WIN
<< "Basket[" + folderName() + "]: <font color=red>FAILED to save</font>!";
1125 Global::bnpView
->setUnsavedStatus(false);
1129 void Basket::aboutToBeActivated()
1131 if (m_finishLoadOnFirstShow
) {
1132 FOR_EACH_NOTE (note
)
1133 note
->finishLazyLoad();
1135 //relayoutNotes(/*animate=*/false);
1136 setFocusedNote(0); // So that during the focusInEvent that will come shortly, the FIRST note is focused.
1138 if (Settings::playAnimations() && !decoration()->filterBar()->filterData().isFiltering
&& Global::bnpView
->currentBasket() == this) // No animation when filtering all!
1139 animateLoad();//QTimer::singleShot( 0, this, SLOT(animateLoad()) );
1141 m_finishLoadOnFirstShow
= false;
1145 void Basket::reload()
1148 unbufferizeAll(); // Keep the memory footprint low
1153 m_loadingLaunched
= false;
1161 if (m_loadingLaunched
)
1163 m_loadingLaunched
= true;
1165 // StopWatch::start(10);
1167 DEBUG_WIN
<< "Basket[" + folderName() + "]: Loading...";
1168 QDomDocument
*doc
= 0;
1171 // StopWatch::start(0);
1173 if (loadFromFile(fullPath() + ".basket", &content
)) {
1174 doc
= new QDomDocument("basket");
1175 if ( ! doc
->setContent(content
) ) {
1176 DEBUG_WIN
<< "Basket[" + folderName() + "]: <font color=red>FAILED to parse XML</font>!";
1182 DEBUG_WIN
<< "Basket is encrypted.";
1184 DEBUG_WIN
<< "Basket[" + folderName() + "]: <font color=red>FAILED to load</font>!";
1185 m_loadingLaunched
= false;
1188 Global::bnpView
->notesStateChanged(); // Show "Locked" instead of "Loading..." in the statusbar
1193 QDomElement docElem
= doc
->documentElement();
1194 QDomElement properties
= XMLWork::getElement(docElem
, "properties");
1196 loadProperties(properties
); // Since we are loading, this time the background image will also be loaded!
1197 // Now that the background image is loaded and subscribed, we display it during the load process:
1200 // kapp->processEvents();
1202 //BEGIN Compatibility with 0.6.0 Pre-Alpha versions:
1203 QDomElement notes
= XMLWork::getElement(docElem
, "notes");
1205 notes
= XMLWork::getElement(docElem
, "items");
1206 m_watcher
->stopScan();
1207 m_shouldConvertPlainTextNotes
= false; // Convert Pre-0.6.0 baskets: plain text notes should be converted to rich text ones once all is loaded!
1208 // StopWatch::check(0);
1210 // StopWatch::start(1);
1211 m_finishLoadOnFirstShow
= (Global::bnpView
->currentBasket() != this);
1212 loadNotes(notes
, 0L);
1213 // StopWatch::check(1);
1214 // StopWatch::start(2);
1216 if (m_shouldConvertPlainTextNotes
)
1218 m_watcher
->startScan();
1219 //loadNotes(XMLWork::getElement(docElem, "notes"), 0L);
1222 // StopWatch::check(0);
1224 signalCountsChanged();
1225 if (isColumnsLayout()) {
1226 // Count the number of columns:
1227 int columnsCount
= 0;
1228 Note
*column
= firstNote();
1231 column
= column
->next();
1233 m_columnsCount
= columnsCount
;
1236 relayoutNotes(false);
1238 // On application start, the current basket is not focused yet, so the focus rectangle is not shown when calling focusANote():
1239 if (Global::bnpView
->currentBasket() == this)
1243 if (Settings::playAnimations() && !decoration()->filterBar()->filterData().isFiltering
&& Global::bnpView
->currentBasket() == this) // No animation when filtering all!
1244 animateLoad();//QTimer::singleShot( 0, this, SLOT(animateLoad()) );
1248 // StopWatch::check(2);
1250 // StopWatch::check(10);
1253 void Basket::filterAgain(bool andEnsureVisible
/* = true*/)
1255 newFilter(decoration()->filterData(), andEnsureVisible
);
1258 void Basket::filterAgainDelayed()
1260 QTimer::singleShot( 0, this, SLOT(filterAgain()) );
1263 void Basket::newFilter(const FilterData
&data
, bool andEnsureVisible
/* = true*/)
1268 //StopWatch::start(20);
1271 for (Note
*note
= firstNote(); note
; note
= note
->next())
1272 m_countFounds
+= note
->newFilter(data
);
1274 relayoutNotes(true);
1275 signalCountsChanged();
1277 if (hasFocus()) // if (!hasFocus()), focusANote() will be called at focusInEvent()
1278 focusANote(); // so, we avoid de-focus a note if it will be re-shown soon
1279 if (andEnsureVisible
&& m_focusedNote
!= 0L)
1280 ensureNoteVisible(m_focusedNote
);
1282 Global::bnpView
->setFiltering(data
.isFiltering
);
1284 //StopWatch::check(20);
1287 void Basket::cancelFilter()
1289 decoration()->filterBar()->reset();
1293 void Basket::validateFilter()
1296 m_editor
->widget()->setFocus();
1301 bool Basket::isFiltering()
1303 return decoration()->filterBar()->filterData().isFiltering
;
1308 QString
Basket::fullPath()
1310 return Global::basketsFolder() + folderName();
1313 QString
Basket::fullPathForFileName(const QString
&fileName
)
1315 return fullPath() + fileName
;
1318 /*static*/ QString
Basket::fullPathForFolderName(const QString
&folderName
)
1320 return Global::basketsFolder() + folderName
;
1324 void Basket::setShortcut(KShortcut shortcut
, int action
)
1326 if(!Global::globalAccel
)
1328 QString sAction
= "global_basket_activate_" + folderName();
1329 Global::globalAccel
->remove(sAction
);
1330 Global::globalAccel
->updateConnections();
1332 m_action
->setShortcut(shortcut
);
1333 m_shortcutAction
= action
;
1336 Global::globalAccel
->insert(sAction
, m_action
->text(), /*whatsThis=*/"", m_action
->shortcut(), KShortcut(), this, SLOT(activatedShortcut()), /*configurable=*/false);
1337 Global::globalAccel
->updateConnections();
1340 void Basket::activatedShortcut()
1342 Global::bnpView
->setCurrentBasket(this);
1344 if (m_shortcutAction
== 1)
1345 Global::bnpView
->setActive(true);
1348 void Basket::signalCountsChanged()
1350 if (!m_timerCountsChanged
.isActive())
1351 m_timerCountsChanged
.start(0/*ms*/, /*singleShot=*/true);
1354 void Basket::countsChangedTimeOut()
1356 emit
countsChanged(this);
1360 Basket::Basket(QWidget
*parent
, const QString
&folderName
)
1361 : Q3ScrollView(parent
),
1362 QToolTip(viewport()),
1363 m_noActionOnMouseRelease(false), m_ignoreCloseEditorOnNextMouseRelease(false), m_pressPos(-100, -100), m_canDrag(false),
1364 m_firstNote(0), m_columnsCount(1), m_mindMap(false), m_resizingNote(0L), m_pickedResizer(0), m_movingNote(0L), m_pickedHandle(0, 0),
1365 m_clickedToInsert(0), m_zoneToInsert(0), m_posToInsert(-1, -1),
1366 m_isInsertPopupMenu(false),
1367 m_loaded(false), m_loadingLaunched(false), m_locked(false), m_decryptBox(0), m_button(0), m_encryptionType(NoEncryption
),
1368 #ifdef HAVE_LIBGPGME
1371 m_backgroundPixmap(0), m_opaqueBackgroundPixmap(0), m_selectedBackgroundPixmap(0),
1372 m_action(0), m_shortcutAction(0),
1373 m_hoveredNote(0), m_hoveredZone(Note::None
), m_lockedHovering(false), m_underMouse(false),
1374 m_inserterRect(), m_inserterShown(false), m_inserterSplit(true), m_inserterTop(false), m_inserterGroup(false),
1375 m_isSelecting(false), m_selectionStarted(false),
1376 m_count(0), m_countFounds(0), m_countSelecteds(0),
1377 m_folderName(folderName
),
1378 m_editor(0), m_leftEditorBorder(0), m_rightEditorBorder(0), m_redirectEditActions(false), m_editorWidth(-1), m_editorHeight(-1),
1379 m_doNotCloseEditor(false), m_editParagraph(0), m_editIndex(0),
1380 m_isDuringDrag(false), m_draggedNotes(),
1381 m_focusedNote(0), m_startOfShiftSelectionNote(0),
1382 m_finishLoadOnFirstShow(false), m_relayoutOnNextShow(false)
1384 QString sAction
= "local_basket_activate_" + folderName
;
1385 m_action
= new KAction("FAKE TEXT", "FAKE ICON", KShortcut(), this, SLOT(activatedShortcut()), Global::bnpView
->actionCollection(), sAction
);
1386 m_action
->setShortcutConfigurable(false); // We do it in the basket properties dialog (and keep it in sync with the global one)
1388 if (!m_folderName
.endsWith("/"))
1389 m_folderName
+= "/";
1391 setFocusPolicy(Qt::StrongFocus
);
1392 setWFlags(Qt::WNoAutoErase
);
1393 setDragAutoScroll(true);
1395 // By default, there is no corner widget: we set one for the corner area to be painted!
1396 // If we don't set one and there are two scrollbars present, slowly resizing up the window show graphical glitches in that area!
1397 m_cornerWidget
= new QWidget(this);
1398 setCornerWidget(m_cornerWidget
);
1400 viewport()->setAcceptDrops(true);
1401 viewport()->setMouseTracking(true);
1402 viewport()->setBackgroundMode(NoBackground
); // Do not clear the widget before paintEvent() because we always draw every pixels (faster and flicker-free)
1405 m_watcher
= new KDirWatch(this);
1406 connect( m_watcher
, SIGNAL(dirty(const QString
&)), this, SLOT(watchedFileModified(const QString
&)) );
1407 connect( m_watcher
, SIGNAL(deleted(const QString
&)), this, SLOT(watchedFileDeleted(const QString
&)) );
1408 connect( &m_watcherTimer
, SIGNAL(timeout()), this, SLOT(updateModifiedNotes()) );
1410 // Various Connections:
1411 connect( &m_animationTimer
, SIGNAL(timeout()), this, SLOT(animateObjects()) );
1412 connect( &m_autoScrollSelectionTimer
, SIGNAL(timeout()), this, SLOT(doAutoScrollSelection()) );
1413 connect( &m_timerCountsChanged
, SIGNAL(timeout()), this, SLOT(countsChangedTimeOut()) );
1414 connect( &m_inactivityAutoSaveTimer
, SIGNAL(timeout()), this, SLOT(inactivityAutoSaveTimeout()) );
1415 connect( &m_inactivityAutoLockTimer
, SIGNAL(timeout()), this, SLOT(inactivityAutoLockTimeout()) );
1416 connect( this, SIGNAL(contentsMoving(int, int)), this, SLOT(contentsMoved()) );
1417 #ifdef HAVE_LIBGPGME
1418 m_gpg
= new KGpgMe();
1420 m_locked
= isFileEncrypted();
1423 void Basket::contentsMoved()
1425 // This slot is called BEFORE the content move, so we delay the hover effects:
1426 QTimer::singleShot(0, this, SLOT(doHoverEffects()));
1429 void Basket::enterEvent(QEvent
*)
1431 m_underMouse
= true;
1435 void Basket::leaveEvent(QEvent
*)
1437 m_underMouse
= false;
1440 if (m_lockedHovering
)
1444 if (m_hoveredNote
) {
1445 m_hoveredNote
->setHovered(false);
1446 m_hoveredNote
->setHoveredZone(Note::None
);
1447 updateNote(m_hoveredNote
);
1452 void Basket::setFocusIfNotInPopupMenu()
1454 if (!kapp
->activePopupWidget())
1456 m_editor
->widget()->setFocus();
1461 void Basket::contentsMousePressEvent(QMouseEvent
*event
)
1463 // If user click the basket, focus it!
1464 // The focus is delayed because if the click results in showing a popup menu,
1465 // the interface flicker by showing the focused rectangle (as the basket gets focus)
1466 // and immediatly removing it (because the popup menu now have focus).
1467 if (!isDuringEdit())
1468 QTimer::singleShot(0, this, SLOT(setFocusIfNotInPopupMenu()));
1470 // Convenient variables:
1471 bool controlPressed
= event
->stateAfter() & Qt::ControlModifier
;
1472 bool shiftPressed
= event
->stateAfter() & Qt::ShiftModifier
;
1474 // Do nothing if we disabled the click some milliseconds sooner.
1475 // For instance when a popup menu has been closed with click, we should not do action:
1476 if (event
->button() == Qt::LeftButton
&& (kapp
->activePopupWidget() || m_lastDisableClick
.msecsTo(QTime::currentTime()) <= 80)) {
1478 m_noActionOnMouseRelease
= true;
1479 // But we allow to select:
1480 // The code is the same as at the bottom of this method:
1481 if (event
->button() == Qt::LeftButton
) {
1482 m_selectionStarted
= true;
1483 m_selectionBeginPoint
= event
->pos();
1484 m_selectionInvert
= controlPressed
|| shiftPressed
;
1489 // Figure out what is the clicked note and zone:
1490 Note
*clicked
= noteAt(event
->pos().x(), event
->pos().y());
1491 Note::Zone zone
= (clicked
? clicked
->zoneAt( event
->pos() - QPoint(clicked
->x(), clicked
->y()) ) : Note::None
);
1494 if (zone
== Note::TagsArrow
&& !controlPressed
&& !shiftPressed
&& event
->button() != Qt::MidButton
) {
1495 if (!clicked
->isSelected())
1496 unselectAllBut(clicked
);
1497 setFocusedNote(clicked
); /// /// ///
1498 m_startOfShiftSelectionNote
= clicked
;
1499 m_noActionOnMouseRelease
= true;
1500 popupTagsMenu(clicked
);
1504 if (event
->button() == Qt::LeftButton
) {
1505 // Prepare to allow drag and drop when moving mouse further:
1506 if ( (zone
== Note::Handle
|| zone
== Note::Group
) ||
1507 (clicked
&& clicked
->isSelected() &&
1508 (zone
== Note::TagsArrow
|| zone
== Note::Custom0
|| zone
== Note::Content
|| zone
== Note::Link
/**/ || zone
>= Note::Emblem0
/**/)) ) {
1509 if (!shiftPressed
&& !controlPressed
) {
1510 m_pressPos
= event
->pos(); // TODO: Allow to drag emblems to assign them to other notes. Then don't allow drag at Emblem0!!
1512 // Saving where we were editing, because during a drag, the mouse can fly over the text edit and move the cursor position:
1513 if (m_editor
&& m_editor
->textEdit()) {
1514 Q3TextEdit
*editor
= m_editor
->textEdit();
1515 editor
->getCursorPosition(&m_editParagraph
, &m_editIndex
);
1520 // Initializing Resizer move:
1521 if (zone
== Note::Resizer
) {
1522 m_resizingNote
= clicked
;
1523 m_pickedResizer
= event
->pos().x() - clicked
->rightLimit();
1524 m_noActionOnMouseRelease
= true;
1525 m_lockedHovering
= true;
1530 if (zone
== Note::Handle
|| zone
== Note::Group
|| (zone
== Note::GroupExpander
&& (controlPressed
|| shiftPressed
))) {
1531 Note
*end
= clicked
;
1532 if (clicked
->isGroup() && shiftPressed
) {
1533 if (clicked
->contains(m_startOfShiftSelectionNote
)) {
1534 m_startOfShiftSelectionNote
= clicked
->firstRealChild();
1535 end
= clicked
->lastRealChild();
1536 } else if (clicked
->firstRealChild()->isAfter(m_startOfShiftSelectionNote
))
1537 end
= clicked
->lastRealChild();
1539 end
= clicked
->firstRealChild();
1541 if (controlPressed
&& shiftPressed
)
1542 selectRange(m_startOfShiftSelectionNote
, end
, /*unselectOthers=*/false);
1543 else if (shiftPressed
)
1544 selectRange(m_startOfShiftSelectionNote
, end
);
1545 else if (controlPressed
)
1546 clicked
->setSelectedRecursivly(!clicked
->allSelected());
1547 else if (!clicked
->allSelected())
1548 unselectAllBut(clicked
);
1549 setFocusedNote(end
); /// /// ///
1550 m_startOfShiftSelectionNote
= (end
->isGroup() ? end
->firstRealChild() : end
);
1551 //m_noActionOnMouseRelease = false;
1552 m_noActionOnMouseRelease
= true;
1555 // MOVED TO RELEASE EVENT:
1556 /* else if (clicked && zone != Note::None && zone != Note::BottomColumn && zone != Note::Resizer && (controlPressed || shiftPressed)) {
1557 if (controlPressed && shiftPressed)
1558 selectRange(m_startOfShiftSelectionNote, clicked, / *unselectOthers=* /false);
1559 else if (shiftPressed)
1560 selectRange(m_startOfShiftSelectionNote, clicked);
1561 else if (controlPressed)
1562 clicked->setSelectedRecursivly(!clicked->allSelected());
1563 setFocusedNote(clicked); /// /// ///
1564 m_startOfShiftSelectionNote = (clicked->isGroup() ? clicked->firstRealChild() : clicked);
1565 m_noActionOnMouseRelease = true;
1569 // Initializing Note move:
1570 /* if ((zone == Note::Group || zone == Note::Handle) && clicked->isFree()) {
1571 m_movingNote = clicked;
1572 m_pickedHandle = QPoint(event->pos().x() - clicked->x(), event->pos().y() - clicked->y());
1573 m_noActionOnMouseRelease = true;
1574 m_lockedHovering = true;
1579 // Folding/Unfolding group:
1580 if (zone
== Note::GroupExpander
) {
1581 clicked
->toggleFolded(Settings::playAnimations());
1582 relayoutNotes(true);
1583 m_noActionOnMouseRelease
= true;
1588 // Popup menu for tag emblems:
1589 if (event
->button() == Qt::RightButton
&& zone
>= Note::Emblem0
) {
1590 if (!clicked
->isSelected())
1591 unselectAllBut(clicked
);
1592 setFocusedNote(clicked
); /// /// ///
1593 m_startOfShiftSelectionNote
= clicked
;
1594 popupEmblemMenu(clicked
, zone
- Note::Emblem0
);
1595 m_noActionOnMouseRelease
= true;
1599 // Insertion Popup Menu:
1600 if ( (event
->button() == Qt::RightButton
) &&
1601 ((!clicked
&& isFreeLayout()) ||
1602 (clicked
&& (zone
== Note::TopInsert
|| zone
== Note::TopGroup
|| zone
== Note::BottomInsert
|| zone
== Note::BottomGroup
|| zone
== Note::BottomColumn
))) ) {
1604 m_clickedToInsert
= clicked
;
1605 m_zoneToInsert
= zone
;
1606 m_posToInsert
= event
->pos();
1607 KMenu
* menu
= (KMenu
*)(Global::bnpView
->popupMenu("insert_popup"));
1608 if (!menu
->title(/*id=*/120).isEmpty()) // If we already added a title, remove it because it would be kept and then added several times:
1609 menu
->removeItem(/*id=*/120);
1610 menu
->insertTitle((zone
== Note::TopGroup
|| zone
== Note::BottomGroup
? i18n("The verb (Group New Note)", "Group") : i18n("The verb (Insert New Note)", "Insert")), /*id=*/120, /*index=*/0);
1611 setInsertPopupMenu();
1612 connect( menu
, SIGNAL(aboutToHide()), this, SLOT(delayedCancelInsertPopupMenu()) );
1613 connect( menu
, SIGNAL(aboutToHide()), this, SLOT(unlockHovering()) );
1614 connect( menu
, SIGNAL(aboutToHide()), this, SLOT(disableNextClick()) );
1615 connect( menu
, SIGNAL(aboutToHide()), this, SLOT(hideInsertPopupMenu()) );
1616 doHoverEffects(clicked
, zone
); // In the case where another popup menu was open, we should do that manually!
1617 m_lockedHovering
= true;
1618 menu
->exec(QCursor::pos());
1619 m_noActionOnMouseRelease
= true;
1623 // Note Context Menu:
1624 if (event
->button() == Qt::RightButton
&& clicked
&& !clicked
->isColumn() && zone
!= Note::Resizer
) {
1625 if (!clicked
->isSelected())
1626 unselectAllBut(clicked
);
1627 setFocusedNote(clicked
); /// /// ///
1628 if (editedNote() == clicked
) {
1630 clicked
->setSelected(true);
1632 m_startOfShiftSelectionNote
= (clicked
->isGroup() ? clicked
->firstRealChild() : clicked
);
1633 Q3PopupMenu
* menu
= Global::bnpView
->popupMenu("note_popup");
1634 connect( menu
, SIGNAL(aboutToHide()), this, SLOT(unlockHovering()) );
1635 connect( menu
, SIGNAL(aboutToHide()), this, SLOT(disableNextClick()) );
1636 doHoverEffects(clicked
, zone
); // In the case where another popup menu was open, we should do that manually!
1637 m_lockedHovering
= true;
1638 menu
->exec(QCursor::pos());
1639 m_noActionOnMouseRelease
= true;
1643 // Paste selection under cursor (but not "create new primary note under cursor" because this is on moveRelease):
1644 if (event
->button() == Qt::MidButton
&& zone
!= Note::Resizer
&& (!isDuringEdit() || clicked
!= editedNote())) {
1645 if ((Settings::middleAction() != 0) && (event
->state() == Qt::ShiftModifier
)) {
1646 m_clickedToInsert
= clicked
;
1647 m_zoneToInsert
= zone
;
1648 m_posToInsert
= event
->pos();
1650 removeInserter(); // If clicked at an insertion line and the new note shows a dialog for editing,
1651 NoteType::Id type
= (NoteType::Id
)0; // hide that inserter before the note edition instead of after the dialog is closed
1652 switch (Settings::middleAction()) {
1654 m_isInsertPopupMenu
= true;
1657 case 2: type
= NoteType::Image
; break;
1658 case 3: type
= NoteType::Link
; break;
1659 case 4: type
= NoteType::Launcher
; break;
1661 m_noActionOnMouseRelease
= false;
1662 return; // Other options should be done on mouse release (to avoid mouse release to cancel them!)
1663 /* case 5: type = NoteType::Color; break;
1665 Global::bnpView->grabScreenshot();
1668 Global::bnpView->slotColorFromScreen();
1671 Global::bnpView->insertWizard(3); // loadFromFile
1674 Global::bnpView->insertWizard(1); // importKMenuLauncher
1677 Global::bnpView->insertWizard(2); // importIcon
1681 m_ignoreCloseEditorOnNextMouseRelease
= true;
1682 Global::bnpView
->insertEmpty(type
);
1686 zone
= clicked
->zoneAt( event
->pos() - QPoint(clicked
->x(), clicked
->y()), true );
1688 clickedToInsert(event
, clicked
, zone
);
1691 m_noActionOnMouseRelease
= true;
1695 // Finally, no action has been done durint pressEvent, so an action can be done on releaseEvent:
1696 m_noActionOnMouseRelease
= false;
1698 /* Selection scenario:
1699 * On contentsMousePressEvent, put m_selectionStarted to true and init Begin and End selection point.
1700 * On contentsMouseMoveEvent, if m_selectionStarted, update End selection point, update selection rect,
1701 * and if it's larger, switching to m_isSelecting mode: we can draw the selection rectangle.
1703 // Prepare selection:
1704 if (event
->button() == Qt::LeftButton
) {
1705 m_selectionStarted
= true;
1706 m_selectionBeginPoint
= event
->pos();
1707 // We usualy invert the selection with the Ctrl key, but some environements (like GNOME or The Gimp) do it with the Shift key.
1708 // Since the Shift key has no specific usage, we allow to invert selection ALSO with Shift for Gimp people
1709 m_selectionInvert
= controlPressed
|| shiftPressed
;
1713 void Basket::delayedCancelInsertPopupMenu()
1715 QTimer::singleShot( 0, this, SLOT(cancelInsertPopupMenu()) );
1718 void Basket::contentsContextMenuEvent(QContextMenuEvent
*event
)
1720 if (event
->reason() == QContextMenuEvent::Keyboard
) {
1721 if (countFounds
/*countShown*/() == 0) { // TODO: Count shown!!
1722 QRect
basketRect( mapToGlobal(QPoint(0,0)), size() );
1723 Q3PopupMenu
*menu
= Global::bnpView
->popupMenu("insert_popup");
1724 setInsertPopupMenu();
1725 connect( menu
, SIGNAL(aboutToHide()), this, SLOT(delayedCancelInsertPopupMenu()) );
1726 connect( menu
, SIGNAL(aboutToHide()), this, SLOT(unlockHovering()) );
1727 connect( menu
, SIGNAL(aboutToHide()), this, SLOT(disableNextClick()) );
1729 m_lockedHovering
= true;
1730 PopupMenu::execAtRectCenter(*menu
, basketRect
); // Popup at center or the basket
1732 if ( ! m_focusedNote
->isSelected() )
1733 unselectAllBut(m_focusedNote
);
1734 setFocusedNote(m_focusedNote
); /// /// ///
1735 m_startOfShiftSelectionNote
= (m_focusedNote
->isGroup() ? m_focusedNote
->firstRealChild() : m_focusedNote
);
1736 // Popup at bottom (or top) of the focused note, if visible :
1737 Q3PopupMenu
*menu
= Global::bnpView
->popupMenu("note_popup");
1738 connect( menu
, SIGNAL(aboutToHide()), this, SLOT(unlockHovering()) );
1739 connect( menu
, SIGNAL(aboutToHide()), this, SLOT(disableNextClick()) );
1740 doHoverEffects(m_focusedNote
, Note::Content
); // In the case where another popup menu was open, we should do that manually!
1741 m_lockedHovering
= true;
1742 PopupMenu::execAtRectBottom(*menu
, noteVisibleRect(m_focusedNote
), true);
1747 QRect
Basket::noteVisibleRect(Note
*note
)
1749 QRect
rect( contentsToViewport(QPoint(note
->x(), note
->y())), QSize(note
->width(),note
->height()) );
1750 QPoint basketPoint
= mapToGlobal(QPoint(0,0));
1751 rect
.moveTopLeft( rect
.topLeft() + basketPoint
+ QPoint(frameWidth(), frameWidth()) );
1753 // Now, rect contain the global note rectangle on the screen.
1754 // We have to clip it by the basket widget :
1755 if (rect
.bottom() > basketPoint
.y() + visibleHeight() + 1) { // Bottom too... bottom
1756 rect
.setBottom( basketPoint
.y() + visibleHeight() + 1);
1757 if (rect
.height() <= 0) // Have at least one visible pixel of height
1758 rect
.setTop(rect
.bottom());
1760 if (rect
.top() < basketPoint
.y() + frameWidth()) { // Top too... top
1761 rect
.setTop( basketPoint
.y() + frameWidth());
1762 if (rect
.height() <= 0)
1763 rect
.setBottom(rect
.top());
1765 if (rect
.right() > basketPoint
.x() + visibleWidth() + 1) { // Right too... right
1766 rect
.setRight( basketPoint
.x() + visibleWidth() + 1);
1767 if (rect
.width() <= 0) // Have at least one visible pixel of width
1768 rect
.setLeft(rect
.right());
1770 if (rect
.left() < basketPoint
.x() + frameWidth()) { // Left too... left
1771 rect
.setLeft( basketPoint
.x() + frameWidth());
1772 if (rect
.width() <= 0)
1773 rect
.setRight(rect
.left());
1779 void Basket::disableNextClick()
1781 m_lastDisableClick
= QTime::currentTime();
1784 void Basket::recomputeAllStyles()
1786 FOR_EACH_NOTE (note
)
1787 note
->recomputeAllStyles();
1790 void Basket::removedStates(const Q3ValueList
<State
*> &deletedStates
)
1792 bool modifiedBasket
= false;
1794 FOR_EACH_NOTE (note
)
1795 if (note
->removedStates(deletedStates
))
1796 modifiedBasket
= true;
1802 void Basket::insertNote(Note
*note
, Note
*clicked
, int zone
, const QPoint
&pos
, bool animateNewPosition
)
1805 std::cout
<< "Wanted to insert NO note" << std::endl
;
1809 if (clicked
&& zone
== Note::BottomColumn
) {
1810 // When inserting at the bottom of a column, it's obvious the new note SHOULD inherit tags.
1811 // We ensure that by changing the insertion point after the last note of the column:
1812 Note
*last
= clicked
->lastChild();
1815 zone
= Note::BottomInsert
;
1819 /// Insertion at the bottom of a column:
1820 if (clicked
&& zone
== Note::BottomColumn
) {
1821 note
->setWidth(clicked
->rightLimit() - clicked
->x());
1822 Note
*lastChild
= clicked
->lastChild();
1823 if (!animateNewPosition
|| !Settings::playAnimations())
1824 for (Note
*n
= note
; n
; n
= n
->next()) {
1825 n
->setXRecursivly(clicked
->x());
1826 n
->setYRecursivly((lastChild
? lastChild
: clicked
)->bottom() + 1);
1828 appendNoteIn(note
, clicked
);
1830 /// Insertion relative to a note (top/bottom, insert/group):
1831 } else if (clicked
) {
1832 note
->setWidth(clicked
->width());
1833 if (!animateNewPosition
|| !Settings::playAnimations())
1834 for (Note
*n
= note
; n
; n
= n
->next()) {
1835 if (zone
== Note::TopGroup
|| zone
== Note::BottomGroup
)
1836 n
->setXRecursivly(clicked
->x() + Note::GROUP_WIDTH
);
1838 n
->setXRecursivly(clicked
->x());
1839 if (zone
== Note::TopInsert
|| zone
== Note::TopGroup
)
1840 n
->setYRecursivly(clicked
->y());
1842 n
->setYRecursivly(clicked
->bottom() + 1);
1845 if (zone
== Note::TopInsert
) { appendNoteBefore(note
, clicked
); }
1846 else if (zone
== Note::BottomInsert
) { appendNoteAfter(note
, clicked
); }
1847 else if (zone
== Note::TopGroup
) { groupNoteBefore(note
, clicked
); }
1848 else if (zone
== Note::BottomGroup
) { groupNoteAfter(note
, clicked
); }
1851 } else if (isFreeLayout()) {
1852 // Group if note have siblings:
1854 Note
*group
= new Note(this);
1855 for (Note
*n
= note
; n
; n
= n
->next())
1856 n
->setParentNote(group
);
1857 group
->setFirstChild(note
);
1860 // Insert at cursor position:
1861 const int initialWidth
= 250;
1862 note
->setWidth(note
->isGroup() ? Note::GROUP_WIDTH
: initialWidth
);
1863 if (note
->isGroup() && note
->firstChild())
1864 note
->setInitialHeight(note
->firstChild()->height());
1865 //note->setGroupWidth(initialWidth);
1866 if (animateNewPosition
&& Settings::playAnimations())
1867 note
->setFinalPosition(pos
.x(), pos
.y());
1869 note
->setXRecursivly(pos
.x());
1870 note
->setYRecursivly(pos
.y());
1872 appendNoteAfter(note
, lastNote());
1875 relayoutNotes(true);
1878 void Basket::clickedToInsert(QMouseEvent
*event
, Note
*clicked
, /*Note::Zone*/int zone
)
1881 if (event
->button() == Qt::MidButton
)
1882 note
= NoteFactory::dropNote(KApplication::clipboard()->data(QClipboard::Selection
), this);
1884 note
= NoteFactory::createNoteText("", this);
1889 insertNote(note
, clicked
, zone
, event
->pos(), /*animateNewPosition=*/false);
1891 // ensureNoteVisible(lastInsertedNote()); // TODO: in insertNote()
1893 if (event
->button() != Qt::MidButton
) {
1894 removeInserter(); // Case: user clicked below a column to insert, the note is inserted and doHoverEffects() put a new inserter below. We don't want it.
1896 noteEdit(note
, /*justAdded=*/true);
1900 void Basket::contentsDragEnterEvent(QDragEnterEvent
*event
)
1902 m_isDuringDrag
= true;
1903 Global::bnpView
->updateStatusBarHint();
1904 if (NoteDrag::basketOf(event
) == this)
1905 m_draggedNotes
= NoteDrag::notesOf(event
);
1908 void Basket::contentsDragMoveEvent(QDragMoveEvent
*event
)
1910 // m_isDuringDrag = true;
1915 // FIXME: viewportToContents does NOT work !!!
1916 // QPoint pos = viewportToContents(event->pos());
1917 // QPoint pos( event->pos().x() + contentsX(), event->pos().y() + contentsY() );
1919 // if (insertAtCursorPos())
1920 // computeInsertPlace(pos);
1921 doHoverEffects(event
->pos());
1923 // showFrameInsertTo();
1924 if (isFreeLayout() || noteAt(event
->pos().x(), event
->pos().y())) // Cursor before rightLimit() or hovering the dragged source notes
1925 acceptDropEvent(event
);
1927 event
->acceptAction(false);
1928 event
->accept(false);
1931 /* Note *hoveredNote = noteAt(event->pos().x(), event->pos().y());
1932 if ( (isColumnsLayout() && !hoveredNote) || (draggedNotes().contains(hoveredNote)) ) {
1933 event->acceptAction(false);
1934 event->accept(false);
1936 acceptDropEvent(event);*/
1938 // A workarround since QScrollView::dragAutoScroll seem to have no effect :
1939 // ensureVisible(event->pos().x() + contentsX(), event->pos().y() + contentsY(), 30, 30);
1940 // QScrollView::dragMoveEvent(event);
1943 void Basket::contentsDragLeaveEvent(QDragLeaveEvent
*)
1946 m_isDuringDrag
= false;
1947 m_draggedNotes
.clear();
1948 m_noActionOnMouseRelease
= true;
1949 emit
resetStatusBarText();
1953 void Basket::contentsDropEvent(QDropEvent
*event
)
1955 QPoint pos
= event
->pos();
1956 std::cout
<< "Contents Drop Event at position " << pos
.x() << ":" << pos
.y() << std::endl
;
1958 m_isDuringDrag
= false;
1959 emit
resetStatusBarText();
1964 // Do NOT check the bottom&right borders.
1965 // Because imagine someone drag&drop a big note from the top to the bottom of a big basket (with big vertical scrollbars),
1966 // the note is first removed, and relayoutNotes() compute the new height that is smaller
1967 // Then noteAt() is called for the mouse pointer position, because the basket is now smaller, the cursor is out of boundaries!!!
1968 // Should, of course, not return 0:
1969 Note
*clicked
= noteAt(event
->pos().x(), event
->pos().y());
1971 if (NoteFactory::movingNotesInTheSameBasket(event
, this, event
->action()) && event
->action() == QDropEvent::Move
) {
1972 m_doNotCloseEditor
= true;
1975 Note
*note
= NoteFactory::dropNote( event
, this, true, event
->action(), dynamic_cast<Note
*>(event
->source()) );
1978 Note::Zone zone
= (clicked
? clicked
->zoneAt( event
->pos() - QPoint(clicked
->x(), clicked
->y()), /*toAdd=*/true ) : Note::None
);
1979 bool animateNewPosition
= NoteFactory::movingNotesInTheSameBasket(event
, this, event
->action());
1980 if (animateNewPosition
) {
1983 // FOR_EACH_NOTE_IN_CHUNK(note)
1984 for (Note
*n
= note
; n
; n
= n
->next())
1988 insertNote(note
, clicked
, zone
, event
->pos(), animateNewPosition
);
1990 // If moved a note on bottom, contentsHeight has been disminished, then view scrolled up, and we should re-scroll the view down:
1991 ensureNoteVisible(note
);
1993 // if (event->button() != Qt::MidButton) {
1994 // removeInserter(); // Case: user clicked below a column to insert, the note is inserted and doHoverEffects() put a new inserter below. We don't want it.
1998 // doHoverEffects(); called by insertNote()
2002 m_draggedNotes
.clear();
2004 m_doNotCloseEditor
= false;
2005 // When starting the drag, we saved where we were editing.
2006 // This is because during a drag, the mouse can fly over the text edit and move the cursor position, and even HIDE the cursor.
2007 // So we re-show the cursor, and re-position it at the right place:
2008 if (m_editor
&& m_editor
->textEdit()) {
2009 Q3TextEdit
*editor
= m_editor
->textEdit();
2010 editor
->setCursorPosition(m_editParagraph
, m_editIndex
);
2014 // handles dropping of a note to basket that is not shown
2015 // (usually through its entry in the basket list)
2016 void Basket::blindDrop(QDropEvent
* event
)
2018 if (!m_isInsertPopupMenu
&& redirectEditActions()) {
2019 if (m_editor
->textEdit())
2020 m_editor
->textEdit()->paste();
2021 else if (m_editor
->lineEdit())
2022 m_editor
->lineEdit()->paste();
2025 Global::bnpView
->showPassiveLoading(this);
2030 Note
*note
= NoteFactory::dropNote( event
, this, true, event
->action(),
2031 dynamic_cast<Note
*>(event
->source()) );
2033 insertCreatedNote(note
);
2034 //unselectAllBut(note);
2035 if (Settings::usePassivePopup())
2036 Global::bnpView
->showPassiveDropped(i18n("Dropped to basket <i>%1</i>"));
2042 void Basket::insertEmptyNote(int type
)
2048 Note
*note
= NoteFactory::createEmptyNote((NoteType::Id
)type
, this);
2049 insertCreatedNote(note
/*, / *edit=* /true*/);
2050 noteEdit(note
, /*justAdded=*/true);
2053 void Basket::insertWizard(int type
)
2055 saveInsertionData();
2059 case 1: note
= NoteFactory::importKMenuLauncher(this); break;
2060 case 2: note
= NoteFactory::importIcon(this); break;
2061 case 3: note
= NoteFactory::importFileContent(this); break;
2065 restoreInsertionData();
2066 insertCreatedNote(note
);
2067 unselectAllBut(note
);
2068 resetInsertionData();
2071 void Basket::insertColor(const QColor
&color
)
2073 Note
*note
= NoteFactory::createNoteColor(color
, this);
2074 restoreInsertionData();
2075 insertCreatedNote(note
);
2076 unselectAllBut(note
);
2077 resetInsertionData();
2080 void Basket::insertImage(const QPixmap
&image
)
2082 Note
*note
= NoteFactory::createNoteImage(image
, this);
2083 restoreInsertionData();
2084 insertCreatedNote(note
);
2085 unselectAllBut(note
);
2086 resetInsertionData();
2089 void Basket::pasteNote(QClipboard::Mode mode
)
2091 if (!m_isInsertPopupMenu
&& redirectEditActions()) {
2092 if (m_editor
->textEdit())
2093 m_editor
->textEdit()->paste();
2094 else if (m_editor
->lineEdit())
2095 m_editor
->lineEdit()->paste();
2098 Global::bnpView
->showPassiveLoading(this);
2103 Note
*note
= NoteFactory::dropNote(KApplication::clipboard()->data(mode
), this);
2105 insertCreatedNote(note
);
2106 //unselectAllBut(note);
2111 void Basket::insertCreatedNote(Note
*note
)
2113 // Get the insertion data if the user clicked inside the basket:
2114 Note
*clicked
= m_clickedToInsert
;
2115 int zone
= m_zoneToInsert
;
2116 QPoint pos
= m_posToInsert
;
2118 // If it isn't the case, use the default position:
2119 if (!clicked
&& (pos
.x() < 0 || pos
.y() < 0)) {
2120 // Insert right after the focused note:
2122 if (m_focusedNote
) {
2123 clicked
= m_focusedNote
;
2124 zone
= (m_focusedNote
->isFree() ? Note::BottomGroup
: Note::BottomInsert
);
2125 pos
= QPoint(m_focusedNote
->x(), m_focusedNote
->finalBottom());
2126 // Insert at the end of the last column:
2127 } else if (isColumnsLayout()) {
2128 Note
*column
= /*(Settings::newNotesPlace == 0 ?*/ firstNote() /*: lastNote())*/;
2129 /*if (Settings::newNotesPlace == 0 && column->firstChild()) { // On Top, if at least one child in the column
2130 clicked = column->firstChild();
2131 zone = Note::TopInsert;
2132 } else { // On Bottom*/
2134 zone
= Note::BottomColumn
;
2136 // Insert at free position:
2142 insertNote(note
, clicked
, zone
, pos
);
2143 // ensureNoteVisible(lastInsertedNote());
2144 removeInserter(); // Case: user clicked below a column to insert, the note is inserted and doHoverEffects() put a new inserter below. We don't want it.
2149 void Basket::saveInsertionData()
2151 m_savedClickedToInsert
= m_clickedToInsert
;
2152 m_savedZoneToInsert
= m_zoneToInsert
;
2153 m_savedPosToInsert
= m_posToInsert
;
2156 void Basket::restoreInsertionData()
2158 m_clickedToInsert
= m_savedClickedToInsert
;
2159 m_zoneToInsert
= m_savedZoneToInsert
;
2160 m_posToInsert
= m_savedPosToInsert
;
2163 void Basket::resetInsertionData()
2165 m_clickedToInsert
= 0;
2167 m_posToInsert
= QPoint(-1, -1);
2170 void Basket::hideInsertPopupMenu()
2172 QTimer::singleShot( 50/*ms*/, this, SLOT(timeoutHideInsertPopupMenu()) );
2175 void Basket::timeoutHideInsertPopupMenu()
2177 resetInsertionData();
2180 void Basket::acceptDropEvent(QDropEvent
*event
, bool preCond
)
2182 // FIXME: Should not accept all actions! Or not all actions (link not supported?!)
2183 event
->acceptAction(preCond
&& 1);
2184 event
->accept(preCond
);
2187 void Basket::contentsMouseReleaseEvent(QMouseEvent
*event
)
2189 // Now disallow drag:
2192 // Cancel Resizer move:
2193 if (m_resizingNote
) {
2195 m_pickedResizer
= 0;
2196 m_lockedHovering
= false;
2201 // Cancel Note move:
2202 /* if (m_movingNote) {
2204 m_pickedHandle = QPoint(0, 0);
2205 m_lockedHovering = false;
2211 // Cancel Selection rectangle:
2212 if (m_isSelecting
) {
2213 m_isSelecting
= false;
2214 stopAutoScrollSelection();
2215 resetWasInLastSelectionRect();
2217 updateContents(m_selectionRect
);
2219 m_selectionStarted
= false;
2221 Note
*clicked
= noteAt(event
->pos().x(), event
->pos().y());
2222 Note::Zone zone
= (clicked
? clicked
->zoneAt( event
->pos() - QPoint(clicked
->x(), clicked
->y()) ) : Note::None
);
2223 if ((zone
== Note::Handle
|| zone
== Note::Group
) && editedNote() && editedNote() == clicked
) {
2224 if (m_ignoreCloseEditorOnNextMouseRelease
)
2225 m_ignoreCloseEditorOnNextMouseRelease
= false;
2227 bool editedNoteStillThere
= closeEditor();
2228 if (editedNoteStillThere
)
2229 //clicked->setSelected(true);
2230 unselectAllBut(clicked
);
2235 if (event
->stateAfter() == 0 && (zone
== Note::Group
|| zone
== Note::Handle
)) {
2237 unselectAllBut(clicked
);
2241 // Do nothing if an action has already been made during mousePressEvent,
2242 // or if user made a selection and canceled it by regressing to a very small rectangle.
2243 if (m_noActionOnMouseRelease
)
2245 // We immediatly set it to true, to avoid actions set on mouseRelease if NO mousePress event has been triggered.
2246 // This is the case when a popup menu is shown, and user click to the basket area to close it:
2247 // the menu then receive the mousePress event and the basket area ONLY receive the mouseRelease event.
2248 // Obviously, nothing should be done in this case:
2249 m_noActionOnMouseRelease
= true;
2253 if (event
->button() == Qt::MidButton
&& zone
!= Note::Resizer
&& (!isDuringEdit() || clicked
!= editedNote())) {
2254 if ((Settings::middleAction() != 0) && (event
->stateAfter() == Qt::ShiftModifier
)) {
2255 m_clickedToInsert
= clicked
;
2256 m_zoneToInsert
= zone
;
2257 m_posToInsert
= event
->pos();
2259 removeInserter(); // If clicked at an insertion line and the new note shows a dialog for editing,
2260 NoteType::Id type
= (NoteType::Id
)0; // hide that inserter before the note edition instead of after the dialog is closed
2261 switch (Settings::middleAction()) {
2262 case 5: type
= NoteType::Color
; break;
2264 Global::bnpView
->grabScreenshot();
2267 Global::bnpView
->slotColorFromScreen();
2270 Global::bnpView
->insertWizard(3); // loadFromFile
2273 Global::bnpView
->insertWizard(1); // importKMenuLauncher
2276 Global::bnpView
->insertWizard(2); // importIcon
2280 m_ignoreCloseEditorOnNextMouseRelease
= true;
2281 Global::bnpView
->insertEmpty(type
);
2287 // Note *clicked = noteAt(event->pos().x(), event->pos().y());
2289 if (isFreeLayout() && event
->button() == Qt::LeftButton
) {
2290 clickedToInsert(event
);
2295 // Note::Zone zone = clicked->zoneAt( event->pos() - QPoint(clicked->x(), clicked->y()) );
2297 // Convenient variables:
2298 bool controlPressed
= event
->stateAfter() & Qt::ControlModifier
;
2299 bool shiftPressed
= event
->stateAfter() & Qt::ShiftModifier
;
2301 if (clicked
&& zone
!= Note::None
&& zone
!= Note::BottomColumn
&& zone
!= Note::Resizer
&& (controlPressed
|| shiftPressed
)) {
2302 if (controlPressed
&& shiftPressed
)
2303 selectRange(m_startOfShiftSelectionNote
, clicked
, /*unselectOthers=*/false);
2304 else if (shiftPressed
)
2305 selectRange(m_startOfShiftSelectionNote
, clicked
);
2306 else if (controlPressed
)
2307 clicked
->setSelectedRecursivly(!clicked
->allSelected());
2308 setFocusedNote(clicked
); /// /// ///
2309 m_startOfShiftSelectionNote
= (clicked
->isGroup() ? clicked
->firstRealChild() : clicked
);
2310 m_noActionOnMouseRelease
= true;
2314 // Switch tag states:
2315 if (zone
>= Note::Emblem0
) {
2316 if (event
->button() == Qt::LeftButton
) {
2318 for (State::List::iterator it
= clicked
->states().begin(); it
!= clicked
->states().end(); ++it
) {
2319 if ( ! (*it
)->emblem().isEmpty() )
2321 if (icons
== zone
- Note::Emblem0
) {
2322 State
*state
= (*it
)->nextState();
2325 it
= clicked
->states().insert(it
, state
);
2327 clicked
->states().remove(it
);
2328 clicked
->recomputeStyle();
2329 clicked
->unbufferize();
2330 updateNote(clicked
);
2331 updateEditorAppearance();
2338 }/* else if (event->button() == Qt::RightButton) {
2339 popupEmblemMenu(clicked, zone - Note::Emblem0);
2344 // Insert note or past clipboard:
2349 if (event
->button() == Qt::MidButton
&& zone
== Note::Resizer
)
2350 return; //zone = clicked->zoneAt( event->pos() - QPoint(clicked->x(), clicked->y()), true );
2351 if (event
->button() == Qt::RightButton
&& (clicked
->isColumn() || zone
== Note::Resizer
))
2353 if (clicked
->isGroup() && zone
== Note::None
)
2358 // We select note on mousePress if it was unselected or Ctrl is pressed.
2359 // But the user can want to drag select_s_ notes, so it the note is selected, we only select it alone on mouseRelease:
2360 if (event
->stateAfter() == 0) {
2361 std::cout
<< "EXEC" << std::endl
;
2362 if ( !(event
->stateAfter() & Qt::ControlModifier
) && clicked
->allSelected())
2363 unselectAllBut(clicked
);
2364 if (zone
== Note::Handle
&& isDuringEdit() && editedNote() == clicked
) {
2366 clicked
->setSelected(true);
2372 //unselectAllBut(clicked);
2373 setFocusedNote(clicked
);
2377 case Note::GroupExpander
:
2378 case Note::TagsArrow
:
2382 link
= clicked
->linkAt(event
->pos() - QPoint(clicked
->x(), clicked
->y()));
2383 if ( ! link
.isEmpty() ) {
2384 if (link
== "basket-internal-remove-basket") {
2385 // TODO: ask confirmation: "Do you really want to delete the welcome baskets?\n You can re-add them at any time in the Help menu."
2386 Global::bnpView
->doBasketDeletion(this);
2387 } else if (link
== "basket-internal-import") {
2388 Q3PopupMenu
*menu
= Global::bnpView
->popupMenu("fileimport");
2389 menu
->exec(event
->globalPos());
2391 KRun
*run
= new KRun(link
); // open the URL.
2392 run
->setAutoDelete(true);
2395 } // If there is no link, edit note content
2398 unselectAllBut(clicked
);
2399 noteEdit(clicked
, /*justAdded=*/false, event
->pos());
2402 case Note::TopInsert
:
2403 case Note::TopGroup
:
2404 case Note::BottomInsert
:
2405 case Note::BottomGroup
:
2406 case Note::BottomColumn
:
2407 clickedToInsert(event
, clicked
, zone
);
2413 KMessageBox::information(viewport(),
2414 i18n("This message should never appear. If it does, this program is buggy! "
2415 "Please report the bug to the developer."));
2420 void Basket::contentsMouseDoubleClickEvent(QMouseEvent
*event
)
2422 Note
*clicked
= noteAt(event
->pos().x(), event
->pos().y());
2423 Note::Zone zone
= (clicked
? clicked
->zoneAt( event
->pos() - QPoint(clicked
->x(), clicked
->y()) ) : Note::None
);
2425 if (event
->button() == Qt::LeftButton
&& (zone
== Note::Group
|| zone
== Note::Handle
)) {
2426 doCopy(CopyToSelection
);
2427 m_noActionOnMouseRelease
= true;
2429 contentsMousePressEvent(event
);
2432 void Basket::contentsMouseMoveEvent(QMouseEvent
*event
)
2435 if (m_canDrag
&& (m_pressPos
- event
->pos()).manhattanLength() > KApplication::startDragDistance()) {
2437 m_isSelecting
= false; // Don't draw selection rectangle ater drag!
2438 m_selectionStarted
= false;
2439 NoteSelection
*selection
= selectedNotes();
2440 if (selection
->firstStacked()) {
2441 Q3DragObject
*d
= NoteDrag::dragObject(selection
, /*cutting=*/false, /*source=*/this); // d will be deleted by QT
2442 /*bool shouldRemove = */d
->drag();
2443 // delete selection;
2445 // Never delete because URL is dragged and the file must be available for the extern appliation
2446 // if (shouldRemove && d->target() == 0) // If target is another application that request to remove the note
2447 // emit wantDelete(this);
2452 // Moving a Resizer:
2453 if (m_resizingNote
) {
2454 int groupWidth
= event
->pos().x() - m_resizingNote
->x() - m_pickedResizer
;
2455 int minRight
= m_resizingNote
->minRight();
2456 int maxRight
= 100 * contentsWidth(); // A big enough value (+infinity) for free layouts.
2457 Note
*nextColumn
= m_resizingNote
->next();
2458 if (m_resizingNote
->isColumn()) {
2460 maxRight
= nextColumn
->x() + nextColumn
->rightLimit() - nextColumn
->minRight() - Note::RESIZER_WIDTH
;
2462 maxRight
= contentsWidth();
2464 if (groupWidth
> maxRight
- m_resizingNote
->x())
2465 groupWidth
= maxRight
- m_resizingNote
->x();
2466 if (groupWidth
< minRight
- m_resizingNote
->x())
2467 groupWidth
= minRight
- m_resizingNote
->x();
2468 int delta
= groupWidth
- m_resizingNote
->groupWidth();
2469 m_resizingNote
->setGroupWidth(groupWidth
);
2470 // If resizing columns:
2471 if (m_resizingNote
->isColumn()) {
2472 Note
*column
= m_resizingNote
;
2473 if ( (column
= column
->next()) ) {
2474 // Next columns should not have them X coordinate animated, because it would flicker:
2475 column
->setXRecursivly(column
->x() + delta
);
2476 // And the resizer should resize the TWO sibling columns, and not push the other columns on th right:
2477 column
->setGroupWidth(column
->groupWidth() - delta
);
2480 relayoutNotes(true);
2484 /* if (m_movingNote) {
2485 int x = event->pos().x() - m_pickedHandle.x();
2486 int y = event->pos().y() - m_pickedHandle.y();
2489 m_movingNote->setX(x);
2490 m_movingNote->setY(y);
2491 m_movingNote->relayoutAt(x, y, / *animate=* /false);
2492 relayoutNotes(true);
2496 // Dragging the selection rectangle:
2497 if (m_selectionStarted
)
2498 doAutoScrollSelection();
2500 doHoverEffects(event
->pos());
2503 void Basket::doAutoScrollSelection()
2505 static const int AUTO_SCROLL_MARGIN
= 50; // pixels
2506 static const int AUTO_SCROLL_DELAY
= 100; // milliseconds
2508 QPoint pos
= viewport()->mapFromGlobal(QCursor::pos());
2510 // Do the selection:
2513 updateContents(m_selectionRect
);
2515 m_selectionEndPoint
= viewportToContents(pos
);
2516 m_selectionRect
= QRect(m_selectionBeginPoint
, m_selectionEndPoint
).normalize();
2517 if (m_selectionRect
.left() < 0) m_selectionRect
.setLeft(0);
2518 if (m_selectionRect
.top() < 0) m_selectionRect
.setTop(0);
2519 if (m_selectionRect
.right() >= contentsWidth()) m_selectionRect
.setRight(contentsWidth() - 1);
2520 if (m_selectionRect
.bottom() >= contentsHeight()) m_selectionRect
.setBottom(contentsHeight() - 1);
2522 if ( (m_selectionBeginPoint
- m_selectionEndPoint
).manhattanLength() > QApplication::startDragDistance() ) {
2523 m_isSelecting
= true;
2524 selectNotesIn(m_selectionRect
, m_selectionInvert
);
2525 updateContents(m_selectionRect
);
2526 m_noActionOnMouseRelease
= true;
2528 // If the user was selecting but cancel by making the rectangle too small, cancel it really!!!
2529 if (m_isSelecting
) {
2530 if (m_selectionInvert
)
2531 selectNotesIn(QRect(), m_selectionInvert
);
2533 unselectAllBut(0); // TODO: unselectAll();
2536 resetWasInLastSelectionRect();
2537 m_isSelecting
= false;
2538 stopAutoScrollSelection();
2542 // Do the auto-scrolling:
2543 // FIXME: It's still flickering
2545 QRect
insideRect(AUTO_SCROLL_MARGIN
, AUTO_SCROLL_MARGIN
, visibleWidth() - 2*AUTO_SCROLL_MARGIN
, visibleHeight() - 2*AUTO_SCROLL_MARGIN
);
2550 if (pos
.y() < AUTO_SCROLL_MARGIN
)
2551 dy
= pos
.y() - AUTO_SCROLL_MARGIN
;
2552 else if (pos
.y() > visibleHeight() - AUTO_SCROLL_MARGIN
)
2553 dy
= pos
.y() - visibleHeight() + AUTO_SCROLL_MARGIN
;
2555 if (pos
.x() < AUTO_SCROLL_MARGIN
)
2556 dx
= pos
.x() - AUTO_SCROLL_MARGIN
;
2557 else if (pos
.x() > visibleWidth() - AUTO_SCROLL_MARGIN
)
2558 dx
= pos
.x() - visibleWidth() + AUTO_SCROLL_MARGIN
;
2561 kapp
->sendPostedEvents(); // Do the repaints, because the scrolling will make the area to repaint to be wrong
2563 if (!m_autoScrollSelectionTimer
.isActive())
2564 m_autoScrollSelectionTimer
.start(AUTO_SCROLL_DELAY
);
2566 stopAutoScrollSelection();
2569 void Basket::stopAutoScrollSelection()
2571 m_autoScrollSelectionTimer
.stop();
2574 void Basket::resetWasInLastSelectionRect()
2576 Note
*note
= m_firstNote
;
2578 note
->resetWasInLastSelectionRect();
2579 note
= note
->next();
2583 void Basket::selectAll()
2585 if (redirectEditActions()) {
2586 if (m_editor
->textEdit())
2587 m_editor
->textEdit()->selectAll();
2588 else if (m_editor
->lineEdit())
2589 m_editor
->lineEdit()->selectAll();
2591 // First select all in the group, then in the parent group...
2592 Note
*child
= m_focusedNote
;
2593 Note
*parent
= (m_focusedNote
? m_focusedNote
->parentNote() : 0);
2595 if (!parent
->allSelected()) {
2596 parent
->setSelectedRecursivly(true);
2600 parent
= parent
->parentNote();
2602 // Then, select all:
2603 FOR_EACH_NOTE (note
)
2604 note
->setSelectedRecursivly(true);
2608 void Basket::unselectAll()
2610 if (redirectEditActions()) {
2611 if (m_editor
->textEdit()) {
2612 m_editor
->textEdit()->removeSelection();
2613 selectionChangedInEditor(); // THIS IS NOT EMITED BY Qt!!!
2614 } else if (m_editor
->lineEdit())
2615 m_editor
->lineEdit()->deselect();
2617 if (countSelecteds() > 0) // Optimisation
2618 FOR_EACH_NOTE (note
)
2619 note
->setSelectedRecursivly(false);
2623 void Basket::invertSelection()
2625 FOR_EACH_NOTE (note
)
2626 note
->invertSelectionRecursivly();
2629 void Basket::unselectAllBut(Note
*toSelect
)
2631 FOR_EACH_NOTE (note
)
2632 note
->unselectAllBut(toSelect
);
2635 void Basket::invertSelectionOf(Note
*toSelect
)
2637 FOR_EACH_NOTE (note
)
2638 note
->invertSelectionOf(toSelect
);
2641 void Basket::selectNotesIn(const QRect
&rect
, bool invertSelection
, bool unselectOthers
/*= true*/)
2643 FOR_EACH_NOTE (note
)
2644 note
->selectIn(rect
, invertSelection
, unselectOthers
);
2647 void Basket::doHoverEffects()
2649 doHoverEffects( viewportToContents( viewport()->mapFromGlobal(QCursor::pos()) ) );
2652 void Basket::doHoverEffects(Note
*note
, Note::Zone zone
, const QPoint
&pos
)
2654 // Inform the old and new hovered note (if any):
2655 Note
*oldHoveredNote
= m_hoveredNote
;
2656 if (note
!= m_hoveredNote
) {
2657 if (m_hoveredNote
) {
2658 m_hoveredNote
->setHovered(false);
2659 m_hoveredNote
->setHoveredZone(Note::None
);
2660 updateNote(m_hoveredNote
);
2662 m_hoveredNote
= note
;
2664 note
->setHovered(true);
2667 // If we are hovering a note, compute which zone is hovered and inform the note:
2668 if (m_hoveredNote
) {
2669 if (zone
!= m_hoveredZone
|| oldHoveredNote
!= m_hoveredNote
) {
2670 m_hoveredZone
= zone
;
2671 m_hoveredNote
->setCursor(zone
);
2672 updateNote(m_hoveredNote
);
2674 m_hoveredNote
->setHoveredZone(zone
);
2675 // If we are hovering an insert line zone, update this thing:
2676 if (zone
== Note::TopInsert
|| zone
== Note::TopGroup
|| zone
== Note::BottomInsert
|| zone
== Note::BottomGroup
|| zone
== Note::BottomColumn
)
2677 placeInserter(m_hoveredNote
, zone
);
2680 // If we are hovering an embedded link in a rich text element, show the destination in the statusbar:
2681 if (zone
== Note::Link
)
2682 emit
setStatusBarText(m_hoveredNote
->linkAt( pos
- QPoint(m_hoveredNote
->x(), m_hoveredNote
->y()) ));
2683 else if (m_hoveredNote
->content())
2684 emit
setStatusBarText(m_hoveredNote
->content()->statusBarMessage(m_hoveredZone
));//resetStatusBarText();
2685 // If we aren't hovering a note, reset all:
2687 if (isFreeLayout() && !isSelecting())
2688 viewport()->setCursor(Qt::CrossCursor
);
2690 viewport()->unsetCursor();
2691 m_hoveredZone
= Note::None
;
2693 emit
resetStatusBarText();
2697 void Basket::doHoverEffects(const QPoint
&pos
)
2699 // if (isDuringEdit())
2700 // viewport()->unsetCursor();
2702 // Do we have the right to do hover effects?
2703 if ( ! m_loaded
|| m_lockedHovering
)
2706 // enterEvent() (mouse enter in the widget) set m_underMouse to true, and leaveEvent() make it false.
2707 // But some times the enterEvent() is not trigerred: eg. when dragging the scrollbar:
2708 // Ending the drag INSIDE the basket area will make NO hoverEffects() because m_underMouse is false.
2709 // User need to leave the area and re-enter it to get effects.
2710 // This hack solve that by dismissing the m_underMouse variable:
2711 bool underMouse
= Global::bnpView
->currentBasket() == this && QRect(contentsX(), contentsY(), visibleWidth(), visibleHeight()).contains(pos
);
2713 // Don't do hover effects when a popup menu is opened.
2714 // Primarily because the basket area will only receive mouseEnterEvent and mouveLeaveEvent.
2715 // It willn't be noticed of mouseMoveEvent, which would result in a apparently broken application state:
2716 if (kapp
->activePopupWidget())
2719 // Compute which note is hovered:
2720 Note
*note
= (m_isSelecting
|| !underMouse
? 0 : noteAt(pos
.x(), pos
.y()));
2721 Note::Zone zone
= (note
? note
->zoneAt( pos
- QPoint(note
->x(), note
->y()), isDuringDrag() ) : Note::None
);
2723 // Inform the old and new hovered note (if any) and update the areas:
2724 doHoverEffects(note
, zone
, pos
);
2727 void Basket::mouseEnteredEditorWidget()
2729 if (!m_lockedHovering
&& !kapp
->activePopupWidget())
2730 doHoverEffects(editedNote(), Note::Content
, QPoint());
2733 void Basket::removeInserter()
2735 if (m_inserterShown
) { // Do not hide (and then update/repaint the view) if it is already hidden!
2736 m_inserterShown
= false;
2737 updateContents(m_inserterRect
);
2741 void Basket::placeInserter(Note
*note
, int zone
)
2743 // Remove the inserter:
2749 // Update the old position:
2750 if (inserterShown())
2751 updateContents(m_inserterRect
);
2753 m_inserterShown
= true;
2754 m_inserterTop
= (zone
== Note::TopGroup
|| zone
== Note::TopInsert
);
2755 m_inserterGroup
= (zone
== Note::TopGroup
|| zone
== Note::BottomGroup
);
2757 int groupIndent
= (note
->isGroup() ? note
->width() : Note::HANDLE_WIDTH
);
2759 int width
= (note
->isGroup() ? note
->rightLimit() - note
->x() : note
->width());
2760 if (m_inserterGroup
) {
2762 width
-= groupIndent
;
2764 m_inserterSplit
= (Settings::groupOnInsertionLine() && note
&& !note
->isGroup() && !note
->isFree() && !note
->isColumn());
2765 // if (note->isGroup())
2766 // width = note->rightLimit() - note->x() - (m_inserterGroup ? groupIndent : 0);
2768 int y
= note
->y() - (m_inserterGroup
&& m_inserterTop
? 1 : 3);
2770 y
+= (note
->isColumn() ? note
->finalHeight() : note
->height());
2771 // Assigning result:
2772 m_inserterRect
= QRect(x
, y
, width
, 6 - (m_inserterGroup
? 2 : 0));
2773 // Update the new position:
2774 updateContents(m_inserterRect
);
2777 inline void drawLineByRect(QPainter
&painter
, int x
, int y
, int width
, int height
)
2779 painter
.drawLine(x
, y
, x
+ width
- 1, y
+ height
- 1);
2782 void Basket::drawInserter(QPainter
&painter
, int xPainter
, int yPainter
)
2784 if (!m_inserterShown
)
2787 QRect rect
= m_inserterRect
; // For shorter code-lines when drawing!
2788 rect
.moveBy(-xPainter
, -yPainter
);
2789 int lineY
= (m_inserterGroup
&& m_inserterTop
? 0 : 2);
2790 int roundY
= (m_inserterGroup
&& m_inserterTop
? 0 : 1);
2792 QColor dark
= KApplication::palette().active().dark();
2793 QColor light
= dark
.light().light();
2794 if (m_inserterGroup
&& Settings::groupOnInsertionLine())
2795 light
= Tools::mixColor(light
, KGlobalSettings::highlightColor());
2796 painter
.setPen(dark
);
2797 // The horizontal line:
2798 //painter.drawRect( rect.x(), rect.y() + lineY, rect.width(), 2);
2799 int width
= rect
.width() - 4;
2800 drawGradient(&painter
, dark
, light
, rect
.x() + 2, rect
.y() + lineY
, width
/2, 2, /*sunken=*/false, /*horz=*/false, /*flat=*/false);
2801 drawGradient(&painter
, light
, dark
, rect
.x() + 2 + width
/2, rect
.y() + lineY
, width
- width
/2, 2, /*sunken=*/false, /*horz=*/false, /*flat=*/false);
2802 // The left-most and right-most edges (biggest vertical lines):
2803 drawLineByRect(painter
, rect
.x(), rect
.y(), 1, (m_inserterGroup
? 4 : 6));
2804 drawLineByRect(painter
, rect
.x() + rect
.width() - 1, rect
.y(), 1, (m_inserterGroup
? 4 : 6));
2805 // The left and right mid vertical lines:
2806 drawLineByRect(painter
, rect
.x() + 1, rect
.y() + roundY
, 1, (m_inserterGroup
? 3 : 4));
2807 drawLineByRect(painter
, rect
.x() + rect
.width() - 2, rect
.y() + roundY
, 1, (m_inserterGroup
? 3 : 4));
2808 // Draw the split as a feedback to know where is the limit between insert and group:
2809 if (m_inserterSplit
) {
2810 int noteWidth
= rect
.width() + (m_inserterGroup
? Note::HANDLE_WIDTH
: 0);
2811 int xSplit
= rect
.x() - (m_inserterGroup
? Note::HANDLE_WIDTH
: 0) + noteWidth
/ 2;
2812 painter
.setPen(Tools::mixColor(dark
, light
));
2813 painter
.drawRect(xSplit
- 2, rect
.y() + lineY
, 4, 2);
2814 painter
.setPen(dark
);
2815 painter
.drawRect(xSplit
- 1, rect
.y() + lineY
, 2, 2);
2819 void Basket::maybeTip(const QPoint
&pos
)
2821 if ( !m_loaded
|| !Settings::showNotesToolTip() )
2827 QPoint contentPos
= viewportToContents(pos
);
2828 Note
*note
= noteAt(contentPos
.x(), contentPos
.y());
2830 if (!note
&& isFreeLayout()) {
2831 message
= i18n("Insert note here\nRight click for more options");
2833 for (Q3ValueList
<QRect
>::iterator it
= m_blankAreas
.begin(); it
!= m_blankAreas
.end(); ++it
) {
2834 itRect
= QRect(0, 0, visibleWidth(), visibleHeight()).intersect(*it
);
2835 if (itRect
.contains(contentPos
)) {
2837 rect
.moveLeft(rect
.left() - contentsX());
2838 rect
.moveTop( rect
.top() - contentsY());
2846 Note::Zone zone
= note
->zoneAt( contentPos
- QPoint(note
->x(), note
->y()) );
2848 case Note::Resizer
: message
= (note
->isColumn() ?
2849 i18n("Resize those columns") :
2851 i18n("Resize this group") :
2852 i18n("Resize this note"))); break;
2853 case Note::Handle
: message
= i18n("Select or move this note"); break;
2854 case Note::Group
: message
= i18n("Select or move this group"); break;
2855 case Note::TagsArrow
: message
= i18n("Assign or remove tags from this note");
2856 if (note
->states().count() > 0) {
2857 message
= "<qt><nobr>" + message
+ "</nobr><br>" + i18n("<b>Assigned Tags</b>: %1");
2858 QString tagsString
= "";
2859 for (State::List::iterator it
= note
->states().begin(); it
!= note
->states().end(); ++it
) {
2860 QString tagName
= "<nobr>" + Tools::textToHTMLWithoutP((*it
)->fullName()) + "</nobr>";
2861 if (tagsString
.isEmpty())
2862 tagsString
= tagName
;
2864 tagsString
= i18n("%1, %2").arg(tagsString
, tagName
);
2866 message
= message
.arg(tagsString
);
2869 case Note::Custom0
: message
= note
->content()->zoneTip(zone
); break; //"Open this link/Open this file/Open this sound file/Launch this application"
2870 case Note::GroupExpander
: message
= (note
->isFolded() ?
2871 i18n("Expand this group") :
2872 i18n("Collapse this group")); break;
2874 case Note::Content
: message
= note
->content()->editToolTipText(); break;
2875 case Note::TopInsert
:
2876 case Note::BottomInsert
: message
= i18n("Insert note here\nRight click for more options"); break;
2877 case Note::TopGroup
: message
= i18n("Group note with the one below\nRight click for more options"); break;
2878 case Note::BottomGroup
: message
= i18n("Group note with the one above\nRight click for more options"); break;
2879 case Note::BottomColumn
: message
= i18n("Insert note here\nRight click for more options"); break;
2880 case Note::None
: message
= "** Zone NONE: internal error **"; break;
2882 if (zone
>= Note::Emblem0
)
2883 message
= note
->stateForEmblemNumber(zone
- Note::Emblem0
)->fullName();
2889 if (zone
== Note::Content
|| zone
== Note::Link
|| zone
== Note::Custom0
) {
2893 note
->content()->toolTipInfos(&keys
, &values
);
2894 keys
.append(i18n("Added"));
2895 keys
.append(i18n("Last Modification"));
2896 values
.append(note
->addedStringDate());
2897 values
.append(note
->lastModificationStringDate());
2899 message
= "<qt><nobr>" + message
;
2900 QStringList::iterator key
;
2901 QStringList::iterator value
;
2902 for (key
= keys
.begin(), value
= values
.begin(); key
!= keys
.end() && value
!= values
.end(); ++key
, ++value
)
2903 message
+= "<br>" + i18n("of the form 'key: value'", "<b>%1</b>: %2").arg(*key
, *value
);
2904 message
+= "</nobr></qt>";
2905 } else if (m_inserterSplit
&& (zone
== Note::TopInsert
|| zone
== Note::BottomInsert
))
2906 message
+= "\n" + i18n("Click on the right to group instead of insert");
2907 else if (m_inserterSplit
&& (zone
== Note::TopGroup
|| zone
== Note::BottomGroup
))
2908 message
+= "\n" + i18n("Click on the left to insert instead of group");
2910 rect
= note
->zoneRect( zone
, contentPos
- QPoint(note
->x(), note
->y()) );
2912 rect
.moveLeft(rect
.left() - contentsX());
2913 rect
.moveTop( rect
.top() - contentsY());
2915 rect
.moveLeft(rect
.left() + note
->x());
2916 rect
.moveTop( rect
.top() + note
->y());
2922 Note
* Basket::lastNote()
2924 Note
*note
= firstNote();
2925 while (note
&& note
->next())
2926 note
= note
->next();
2930 void Basket::deleteNotes()
2932 Note
*note
= m_firstNote
;
2935 Note
*tmp
= note
->next();
2943 m_startOfShiftSelectionNote
= 0;
2945 m_clickedToInsert
= 0;
2946 m_savedClickedToInsert
= 0;
2950 m_countSelecteds
= 0;
2952 emit
resetStatusBarText();
2953 emit
countsChanged(this);
2956 Note
* Basket::noteAt(int x
, int y
)
2959 // // Do NOT check the bottom&right borders.
2960 // // Because imagine someone drag&drop a big note from the top to the bottom of a big basket (with big vertical scrollbars),
2961 // // the note is first removed, and relayoutNotes() compute the new height that is smaller
2962 // // Then noteAt() is called for the mouse pointer position, because the basket is now smaller, the cursor is out of boundaries!!!
2963 // // Should, of course, not return 0:
2964 if (x
< 0 || x
> contentsWidth() || y
< 0 || y
> contentsHeight())
2967 // When resizing a note/group, keep it highlighted:
2969 return m_resizingNote
;
2971 // Search and return the hovered note:
2972 Note
*note
= m_firstNote
;
2975 possibleNote
= note
->noteAt(x
, y
);
2977 if (draggedNotes().contains(possibleNote
))
2980 return possibleNote
;
2982 note
= note
->next();
2985 // If the basket is layouted in columns, return one of the columns to be able to add notes in them:
2986 if (isColumnsLayout()) {
2987 Note
*column
= m_firstNote
;
2989 if (x
>= column
->x() && x
< column
->rightLimit())
2991 column
= column
->next();
2995 // Nothing found, no note is hovered:
3003 delete m_decryptBox
;
3004 #ifdef HAVE_LIBGPGME
3010 void Basket::viewportResizeEvent(QResizeEvent
*event
)
3012 relayoutNotes(true);
3013 //cornerWidget()->setShown(horizontalScrollBar()->isShown() && verticalScrollBar()->isShown());
3014 if (horizontalScrollBar()->isShown() && verticalScrollBar()->isShown()) {
3015 if (!cornerWidget())
3016 setCornerWidget(m_cornerWidget
);
3021 // if (isDuringEdit())
3022 // ensureNoteVisible(editedNote());
3023 Q3ScrollView::viewportResizeEvent(event
);
3026 void Basket::animateLoad()
3028 const int viewHeight
= contentsY() + visibleHeight();
3030 QTime t
= QTime::currentTime(); // Set random seed
3031 srand(t
.hour()*12 + t
.minute()*60 + t
.second()*60);
3033 Note
*note
= firstNote();
3035 if ((note
->finalY() < viewHeight
) && note
->matching())
3036 note
->initAnimationLoad();
3037 note
= note
->next();
3043 QColor
Basket::selectionRectInsideColor()
3045 return Tools::mixColor(Tools::mixColor(backgroundColor(), KGlobalSettings::highlightColor()), backgroundColor());
3048 QColor
alphaBlendColors(const QColor
&bgColor
, const QColor
&fgColor
, const int a
)
3051 QRgb rgb
= bgColor
.rgb();
3052 QRgb rgb_b
= fgColor
.rgb();
3054 if (alpha
>255) alpha
= 255;
3055 if (alpha
<0) alpha
= 0;
3056 int inv_alpha
= 255 - alpha
;
3057 QColor result
= QColor( qRgb(qRed(rgb_b
)*inv_alpha
/255 + qRed(rgb
)*alpha
/255,
3058 qGreen(rgb_b
)*inv_alpha
/255 + qGreen(rgb
)*alpha
/255,
3059 qBlue(rgb_b
)*inv_alpha
/255 + qBlue(rgb
)*alpha
/255) );
3064 void Basket::unlock()
3066 QTimer::singleShot( 0, this, SLOT(load()) );
3069 void Basket::inactivityAutoLockTimeout()
3074 void Basket::drawContents(QPainter
*painter
, int clipX
, int clipY
, int clipWidth
, int clipHeight
)
3076 // Start the load the first time the basket is shown:
3077 if (!m_loadingLaunched
)
3080 QTimer::singleShot( 0, this, SLOT(load()) );
3082 Global::bnpView
->notesStateChanged(); // Show "Locked" instead of "Loading..." in the statusbar
3086 QBrush
brush(backgroundColor()); // FIXME: share it for all the basket?
3087 QRect
clipRect(clipX
, clipY
, clipWidth
, clipHeight
);
3093 m_decryptBox
= new Q3Frame( this, "m_decryptBox" );
3094 m_decryptBox
->setFrameShape( Q3Frame::StyledPanel
);
3095 m_decryptBox
->setFrameShadow( Q3Frame::Plain
);
3096 m_decryptBox
->setLineWidth( 1 );
3098 Q3GridLayout
* layout
= new Q3GridLayout( m_decryptBox
, 1, 1, 11, 6, "decryptBoxLayout");
3100 #ifdef HAVE_LIBGPGME
3101 m_button
= new QPushButton( m_decryptBox
, "button" );
3102 m_button
->setText( i18n( "&Unlock" ) );
3103 layout
->addWidget( m_button
, 1, 2 );
3104 connect( m_button
, SIGNAL( clicked() ), this, SLOT( unlock() ) );
3106 QLabel
* label
= new QLabel( m_decryptBox
, "label" );
3107 QString text
= "<b>" + i18n("Password protected basket.") + "</b><br/>";
3108 #ifdef HAVE_LIBGPGME
3109 label
->setText( text
+ i18n("Press Unlock to access it.") );
3111 label
->setText( text
+ i18n("Encryption is not supported by<br/>this version of %1.").arg(kapp
->aboutData()->programName()) );
3113 label
->setAlignment( int( Qt::AlignTop
) );
3114 layout
->addMultiCellWidget( label
, 0, 0, 1, 2 );
3115 QLabel
* pixmap
= new QLabel( m_decryptBox
, "pixmap" );
3116 pixmap
->setPixmap( KIconLoader::global()->loadIcon("encrypted", KIconLoader::NoGroup
, KIconLoader::SizeHuge
) );
3117 layout
->addMultiCellWidget( pixmap
, 0, 1, 0, 0 );
3119 QSpacerItem
* spacer
= new QSpacerItem( 40, 20, QSizePolicy::Expanding
, QSizePolicy::Minimum
);
3120 layout
->addItem( spacer
, 1, 1 );
3122 label
= new QLabel("<small>" +
3123 i18n("To make baskets stay unlocked, change the automatic<br>"
3124 "locking duration in the application settings.") + "</small>",
3126 //label->setFixedWidth(label->sizeHint().width() / 2);
3127 label
->setAlignment( int( Qt::AlignTop
) );
3128 layout
->addMultiCellWidget( label
, 2,2,0,2 );
3130 m_decryptBox
->resize(layout
->sizeHint());
3132 if(m_decryptBox
->isHidden())
3134 m_decryptBox
->show();
3136 #ifdef HAVE_LIBGPGME
3137 m_button
->setFocus();
3139 m_decryptBox
->move((visibleWidth() - m_decryptBox
->width()) / 2,
3140 (visibleHeight() - m_decryptBox
->height()) / 2);
3144 if(m_decryptBox
&& m_decryptBox
->isShown())
3145 m_decryptBox
->hide();
3148 // Draw notes (but not when it's not loaded or locked yet):
3149 Note
*note
= ((m_loaded
|| m_locked
) ? m_firstNote
: 0);
3151 note
->draw(painter
, clipRect
);
3152 note
= note
->next();
3156 // Draw loading message:
3158 QPixmap
pixmap(visibleWidth(), visibleHeight()); // TODO: Clip it to asked size only!
3159 QPainter
painter2(&pixmap
);
3160 Q3SimpleRichText
rt(QString("<center>%1</center>").arg(i18n("Loading...")), Q3ScrollView::font());
3161 rt
.setWidth(visibleWidth());
3162 int hrt
= rt
.height();
3163 painter2
.fillRect(0, 0, visibleWidth(), visibleHeight(), brush
);
3164 blendBackground(painter2
, QRect(0, 0, visibleWidth(), visibleHeight()), -1, -1, /*opaque=*/true);
3165 QColorGroup cg
= colorGroup();
3166 cg
.setColor(QColorGroup::Text
, textColor());
3167 rt
.draw(&painter2
, 0, (visibleHeight() - hrt
) / 2, QRect(), cg
);
3169 painter
->drawPixmap(0, 0, pixmap
);
3170 return; // TODO: Clip to the wanted rectangle
3173 // We will draw blank areas below.
3174 // For each rectangle to be draw there is three ways to do so:
3175 // - The rectangle is full of the background COLOR => we fill a rect directly on screen
3176 // - The rectangle is full of the background PIXMAP => we draw it directly on screen (we draw m_opaqueBackgroundPixmap that is not transparent)
3177 // - The rectangle contains the resizer => We draw it on an offscreen buffer and then paint the buffer on screen
3178 // If the background image is not tiled, we know that recomputeBlankRects() broken rects so that they are full of either background pixmap or color, but not a mix.
3180 // Draw blank areas (see the last preparation above):
3184 for (Q3ValueList
<QRect
>::iterator it
= m_blankAreas
.begin(); it
!= m_blankAreas
.end(); ++it
) {
3185 rect
= clipRect
.intersect(*it
);
3186 if (rect
.width() > 0 && rect
.height() > 0) {
3187 // If there is an inserter to draw, draw the image off screen,
3188 // apply the inserter and then draw the image on screen:
3189 if ( (inserterShown() && rect
.intersects(inserterRect())) || (m_isSelecting
&& rect
.intersects(m_selectionRect
)) ) {
3190 pixmap
.resize(rect
.width(), rect
.height());
3191 painter2
.begin(&pixmap
);
3192 painter2
.fillRect(0, 0, rect
.width(), rect
.height(), backgroundColor());
3193 blendBackground(painter2
, rect
, -1, -1, /*opaque=*/true);
3195 if (inserterShown() && rect
.intersects(inserterRect()))
3196 drawInserter(painter2
, rect
.x(), rect
.y());
3197 // Draw selection rect:
3198 if (m_isSelecting
&& rect
.intersects(m_selectionRect
)) {
3199 QRect selectionRect
= m_selectionRect
;
3200 selectionRect
.moveBy(-rect
.x(), -rect
.y());
3201 QRect
selectionRectInside(selectionRect
.x() + 1, selectionRect
.y() + 1, selectionRect
.width() - 2, selectionRect
.height() - 2);
3202 if (selectionRectInside
.width() > 0 && selectionRectInside
.height() > 0) {
3203 QColor insideColor
= selectionRectInsideColor();
3204 painter2
.fillRect(selectionRectInside
, insideColor
);
3205 selectionRectInside
.moveBy(rect
.x(), rect
.y());
3206 blendBackground(painter2
, selectionRectInside
, rect
.x(), rect
.y(), true, /*&*/m_selectedBackgroundPixmap
);
3208 painter2
.setPen(KGlobalSettings::highlightColor().dark());
3209 painter2
.drawRect(selectionRect
);
3210 painter2
.setPen(Tools::mixColor(KGlobalSettings::highlightColor().dark(), backgroundColor()));
3211 painter2
.drawPoint(selectionRect
.topLeft());
3212 painter2
.drawPoint(selectionRect
.topRight());
3213 painter2
.drawPoint(selectionRect
.bottomLeft());
3214 painter2
.drawPoint(selectionRect
.bottomRight());
3217 painter
->drawPixmap(rect
.x(), rect
.y(), pixmap
);
3218 // If it's only a blank rectangle to draw, draw it directly on screen (faster!!!):
3219 } else if ( ! hasBackgroundImage() ) {
3220 painter
->fillRect(rect
, backgroundColor());
3221 // It's either a background pixmap to draw or a background color to fill:
3223 if (isTiledBackground() || (rect
.x() < backgroundPixmap()->width() && rect
.y() < backgroundPixmap()->height()))
3224 blendBackground(*painter
, rect
, 0, 0, /*opaque=*/true);
3226 painter
->fillRect(rect
, backgroundColor());
3232 /* rect(x,y,width,height)==(xBackgroundToDraw,yBackgroundToDraw,widthToDraw,heightToDraw)
3234 void Basket::blendBackground(QPainter
&painter
, const QRect
&rect
, int xPainter
, int yPainter
, bool opaque
, QPixmap
*bg
)
3236 if (xPainter
== -1 && yPainter
== -1) {
3237 xPainter
= rect
.x();
3238 yPainter
= rect
.y();
3241 if (hasBackgroundImage()) {
3242 const QPixmap
*bgPixmap
= (bg
? /* * */bg
: (opaque
? m_opaqueBackgroundPixmap
: m_backgroundPixmap
));
3243 if (isTiledBackground())
3244 painter
.drawTiledPixmap(rect
.x() - xPainter
, rect
.y() - yPainter
, rect
.width(), rect
.height(), *bgPixmap
, rect
.x(), rect
.y());
3246 painter
.drawPixmap(rect
.x() - xPainter
, rect
.y() - yPainter
, *bgPixmap
, rect
.x(), rect
.y(), rect
.width(), rect
.height());
3250 void Basket::recomputeBlankRects()
3252 m_blankAreas
.clear();
3253 m_blankAreas
.append( QRect(0, 0, contentsWidth(), contentsHeight()) );
3255 FOR_EACH_NOTE (note
)
3256 note
->recomputeBlankRects(m_blankAreas
);
3258 // See the drawing of blank areas in Basket::drawContents()
3259 if (hasBackgroundImage() && ! isTiledBackground())
3260 substractRectOnAreas( QRect(0, 0, backgroundPixmap()->width(), backgroundPixmap()->height()), m_blankAreas
, false );
3263 void Basket::addAnimatedNote(Note
*note
)
3265 if (m_animatedNotes
.isEmpty()) {
3266 m_animationTimer
.start(FRAME_DELAY
);
3267 m_lastFrameTime
= QTime::currentTime();
3270 m_animatedNotes
.append(note
);
3273 void Basket::unsetNotesWidth()
3275 Note
*note
= m_firstNote
;
3278 note
= note
->next();
3282 void Basket::relayoutNotes(bool animate
)
3284 if (Global::bnpView
->currentBasket() != this)
3285 return; // Optimize load time, and basket will be relaid out when activated, anyway
3287 if (!Settings::playAnimations())
3291 m_animatedNotes
.clear();
3292 m_animationTimer
.stop();
3298 Note
*note
= m_firstNote
;
3300 if (note
->matching()) {
3301 note
->relayoutAt(0, h
, animate
);
3302 if (note
->hasResizer()) {
3303 int minGroupWidth
= note
->minRight() - note
->finalX();
3304 if (note
->groupWidth() < minGroupWidth
) {
3305 note
->setGroupWidth(minGroupWidth
);
3306 relayoutNotes(animate
); // Redo the thing, but this time it should not recurse
3310 h
+= note
->finalHeight();
3312 note
= note
->next();
3320 resizeContents( qMax(tmpWidth
, visibleWidth()), qMax(tmpHeight
, visibleHeight()) );
3321 recomputeBlankRects();
3327 void Basket::updateNote(Note
*note
)
3329 updateContents(note
->rect());
3330 if (note
->hasResizer())
3331 updateContents(note
->resizerRect());
3334 void Basket::animateObjects()
3336 Q3ValueList
<Note
*>::iterator it
;
3337 for (it
= m_animatedNotes
.begin(); it
!= m_animatedNotes
.end(); )
3338 // if ((*it)->y() >= contentsY() && (*it)->bottom() <= contentsY() + contentsWidth())
3340 if ((*it
)->advance())
3341 it
= m_animatedNotes
.remove(it
);
3343 // if ((*it)->y() >= contentsY() && (*it)->bottom() <= contentsY() + contentsWidth())
3348 if (m_animatedNotes
.isEmpty()) {
3349 // Stop animation timer:
3350 m_animationTimer
.stop();
3351 // Reset all onTop notes:
3352 Note
* note
= m_firstNote
;
3354 note
->setOnTop(false);
3355 note
= note
->next();
3360 ensureNoteVisible(m_focusedNote
);
3362 // We refresh content if it was the last frame,
3363 // or if the drawing of the last frame was not too long.
3364 if (!m_animationTimer
.isActive() || (m_lastFrameTime
.msecsTo(QTime::currentTime()) < FRAME_DELAY
*11/10)) { // *11/10 == *1.1 : We keep a 0.1 security margin
3365 m_lastFrameTime
= m_lastFrameTime
.addMSecs(FRAME_DELAY
); // because timers are not accurate and can trigger late
3366 //m_lastFrameTime = QTime::currentTime();
3367 //std::cout << ">>" << m_lastFrameTime.toString("hh:mm:ss.zzz") << std::endl;
3370 recomputeBlankRects();
3371 //relayoutNotes(true); // In case an animated note was to the contents view boundaries, resize the view!
3373 // If the drawing of the last frame was too long, we skip the drawing of the current and do the next one:
3375 m_lastFrameTime
= m_lastFrameTime
.addMSecs(FRAME_DELAY
);
3376 //std::cout << "+=" << m_lastFrameTime.toString("hh:mm:ss.zzz") << std::endl;
3383 /* int delta = m_deltaY / 3;
3384 if (delta == 0 && m_deltaY != 0)
3385 delta = (m_deltaY > 0 ? 1 : -1);
3387 resizeContents(contentsWidth(), contentsHeight() + delta); //m_lastNote->y() + m_lastNote->height()
3391 void Basket::popupEmblemMenu(Note
*note
, int emblemNumber
)
3393 m_tagPopupNote
= note
;
3394 State
*state
= note
->stateForEmblemNumber(emblemNumber
);
3395 State
*nextState
= state
->nextState(/*cycle=*/false);
3396 Tag
*tag
= state
->parentTag();
3399 QKeySequence sequence
= tag
->shortcut().operator QKeySequence();
3400 bool sequenceOnDelete
= (nextState
== 0 && !tag
->shortcut().isNull());
3403 if (tag
->countStates() == 1) {
3404 menu
.insertTitle(/*SmallIcon(state->icon()), */tag
->name());
3405 menu
.insertItem( SmallIconSet("editdelete"), i18n("&Remove"), 1 );
3406 menu
.insertItem( SmallIconSet("configure"), i18n("&Customize..."), 2 );
3407 menu
.insertSeparator();
3408 menu
.insertItem( SmallIconSet("filter"), i18n("&Filter by this Tag"), 3 );
3410 menu
.insertTitle(tag
->name());
3411 Q3ValueList
<State
*>::iterator it
;
3412 State
*currentState
;
3415 for (it
= tag
->states().begin(); it
!= tag
->states().end(); ++it
) {
3417 QKeySequence sequence
;
3418 if (currentState
== nextState
&& !tag
->shortcut().isNull())
3419 sequence
= tag
->shortcut().operator QKeySequence();
3420 menu
.insertItem(StateMenuItem::radioButtonIconSet(state
== currentState
, menu
.colorGroup()), new StateMenuItem(currentState
, sequence
, false), i
);
3421 if (currentState
== nextState
&& !tag
->shortcut().isNull())
3422 menu
.setAccel(sequence
, i
);
3425 menu
.insertSeparator();
3426 menu
.insertItem( new IndentedMenuItem(i18n("&Remove"), "editdelete", (sequenceOnDelete
? sequence
: QKeySequence())), 1 );
3427 menu
.insertItem( new IndentedMenuItem(i18n("&Customize..."), "configure"), 2 );
3428 menu
.insertSeparator();
3429 menu
.insertItem( new IndentedMenuItem(i18n("&Filter by this Tag"), "filter"), 3 );
3430 menu
.insertItem( new IndentedMenuItem(i18n("Filter by this &State"), "filter"), 4 );
3432 if (sequenceOnDelete
)
3433 menu
.setAccel(sequence
, 1);
3435 connect( &menu
, SIGNAL(activated(int)), this, SLOT(toggledStateInMenu(int)) );
3436 connect( &menu
, SIGNAL(aboutToHide()), this, SLOT(unlockHovering()) );
3437 connect( &menu
, SIGNAL(aboutToHide()), this, SLOT(disableNextClick()) );
3439 m_lockedHovering
= true;
3440 menu
.exec(QCursor::pos());
3443 void Basket::toggledStateInMenu(int id
)
3446 removeTagFromSelectedNotes(m_tagPopup
);
3447 //m_tagPopupNote->removeTag(m_tagPopup);
3448 //m_tagPopupNote->setWidth(0); // To force a new layout computation
3449 updateEditorAppearance();
3454 if (id
== 2) { // Customize this State:
3455 TagsEditDialog
dialog(this, m_tagPopupNote
->stateOfTag(m_tagPopup
));
3459 if (id
== 3) { // Filter by this Tag
3460 decoration()->filterBar()->filterTag(m_tagPopup
);
3463 if (id
== 4) { // Filter by this State
3464 decoration()->filterBar()->filterState(m_tagPopupNote
->stateOfTag(m_tagPopup
));
3468 /*addStateToSelectedNotes*/changeStateOfSelectedNotes(m_tagPopup
->states()[id
- 10] /*, orReplace=true*/);
3469 //m_tagPopupNote->addState(m_tagPopup->states()[id - 10], true);
3474 State
* Basket::stateForTagFromSelectedNotes(Tag
*tag
)
3478 FOR_EACH_NOTE (note
)
3479 if (note
->stateForTagFromSelectedNotes(tag
, &state
) && state
== 0)
3484 void Basket::activatedTagShortcut(Tag
*tag
)
3486 // Compute the next state to set:
3487 State
*state
= stateForTagFromSelectedNotes(tag
);
3489 state
= state
->nextState(/*cycle=*/false);
3491 state
= tag
->states().first();
3495 FOR_EACH_NOTE (note
)
3496 note
->addStateToSelectedNotes(state
, /*orReplace=*/true);
3497 updateEditorAppearance();
3499 removeTagFromSelectedNotes(tag
);
3505 void Basket::popupTagsMenu(Note
*note
)
3507 m_tagPopupNote
= note
;
3510 menu
.insertTitle(i18n("Tags"));
3511 // QValueList<Tag*>::iterator it;
3513 // State *currentState;
3515 // for (it = Tag::all.begin(); it != Tag::all.end(); ++it) {
3516 // // Current tag and first state of it:
3517 // currentTag = *it;
3518 // currentState = currentTag->states().first();
3519 // QKeySequence sequence;
3520 // if (!currentTag->shortcut().isNull())
3521 // sequence = currentTag->shortcut().operator QKeySequence();
3522 // menu.insertItem(StateMenuItem::checkBoxIconSet(note->hasTag(currentTag), menu.colorGroup()), new StateMenuItem(currentState, sequence, true), i );
3523 // if (!currentTag->shortcut().isNull())
3524 // menu.setAccel(sequence, i);
3528 // menu.insertSeparator();
3529 // // menu.insertItem( /*SmallIconSet("editdelete"),*/ "&Assign New Tag...", 1 );
3530 // //id = menu.insertItem( SmallIconSet("editdelete"), "&Remove All", -2 );
3531 // //if (note->states().isEmpty())
3532 // // menu.setItemEnabled(id, false);
3533 // // menu.insertItem( SmallIconSet("configure"), "&Customize...", 3 );
3534 // menu.insertItem( new IndentedMenuItem(i18n("&Assign New Tag...")), 1 );
3535 // menu.insertItem( new IndentedMenuItem(i18n("&Remove All"), "editdelete"), 2 );
3536 // menu.insertItem( new IndentedMenuItem(i18n("&Customize..."), "configure"), 3 );
3538 // if (!selectedNotesHaveTags())//note->states().isEmpty())
3539 // menu.setItemEnabled(2, false);
3541 // connect( &menu, SIGNAL(activated(int)), this, SLOT(toggledTagInMenu(int)) );
3542 // connect( &menu, SIGNAL(aboutToHide()), this, SLOT(unlockHovering()) );
3543 // connect( &menu, SIGNAL(aboutToHide()), this, SLOT(disableNextClick()) );
3545 Global::bnpView
->populateTagsMenu(menu
, note
);
3547 m_lockedHovering
= true;
3548 menu
.exec(QCursor::pos());
3551 void Basket::unlockHovering()
3553 m_lockedHovering
= false;
3557 void Basket::toggledTagInMenu(int id
)
3559 if (id
== 1) { // Assign new Tag...
3560 TagsEditDialog
dialog(this, /*stateToEdit=*/0, /*addNewTag=*/true);
3562 if (!dialog
.addedStates().isEmpty()) {
3563 State::List states
= dialog
.addedStates();
3564 for (State::List::iterator itState
= states
.begin(); itState
!= states
.end(); ++itState
)
3565 FOR_EACH_NOTE (note
)
3566 note
->addStateToSelectedNotes(*itState
);
3567 updateEditorAppearance();
3573 if (id
== 2) { // Remove All
3574 removeAllTagsFromSelectedNotes();
3579 if (id
== 3) { // Customize...
3580 TagsEditDialog
dialog(this);
3585 Tag
*tag
= Tag::all
[id
- 10];
3589 if (m_tagPopupNote
->hasTag(tag
))
3590 removeTagFromSelectedNotes(tag
);
3592 addTagToSelectedNotes(tag
);
3593 m_tagPopupNote
->setWidth(0); // To force a new layout computation
3598 void Basket::addTagToSelectedNotes(Tag
*tag
)
3600 FOR_EACH_NOTE (note
)
3601 note
->addTagToSelectedNotes(tag
);
3602 updateEditorAppearance();
3605 void Basket::removeTagFromSelectedNotes(Tag
*tag
)
3607 FOR_EACH_NOTE (note
)
3608 note
->removeTagFromSelectedNotes(tag
);
3609 updateEditorAppearance();
3612 void Basket::addStateToSelectedNotes(State
*state
)
3614 FOR_EACH_NOTE (note
)
3615 note
->addStateToSelectedNotes(state
);
3616 updateEditorAppearance();
3619 void Basket::updateEditorAppearance()
3621 if (isDuringEdit() && m_editor
->widget()) {
3622 m_editor
->widget()->setFont(m_editor
->note()->font());
3623 m_editor
->widget()->setPaletteBackgroundColor(m_editor
->note()->backgroundColor());
3624 m_editor
->widget()->setPaletteForegroundColor(m_editor
->note()->textColor());
3626 // Uggly Hack arround Qt bugs: placeCursor() don't call any signal:
3627 HtmlEditor
*htmlEditor
= dynamic_cast<HtmlEditor
*>(m_editor
);
3630 m_editor
->textEdit()->getCursorPosition(¶
, &index
);
3631 if (para
== 0 && index
== 0) {
3632 m_editor
->textEdit()->moveCursor(Q3TextEdit::MoveForward
, /*select=*/false);
3633 m_editor
->textEdit()->moveCursor(Q3TextEdit::MoveBackward
, /*select=*/false);
3635 m_editor
->textEdit()->moveCursor(Q3TextEdit::MoveBackward
, /*select=*/false);
3636 m_editor
->textEdit()->moveCursor(Q3TextEdit::MoveForward
, /*select=*/false);
3638 htmlEditor
->cursorPositionChanged(); // Does not work anyway :-( (when clicking on a red bold text, the toolbar still show black normal text)
3643 void Basket::editorPropertiesChanged()
3645 if (isDuringEdit() && m_editor
->note()->content()->type() == NoteType::Html
) {
3646 m_editor
->textEdit()->setAutoFormatting(Settings::autoBullet() ? Q3TextEdit::AutoAll
: Q3TextEdit::AutoNone
);
3650 void Basket::changeStateOfSelectedNotes(State
*state
)
3652 FOR_EACH_NOTE (note
)
3653 note
->changeStateOfSelectedNotes(state
);
3654 updateEditorAppearance();
3657 void Basket::removeAllTagsFromSelectedNotes()
3659 FOR_EACH_NOTE (note
)
3660 note
->removeAllTagsFromSelectedNotes();
3661 updateEditorAppearance();
3664 bool Basket::selectedNotesHaveTags()
3666 FOR_EACH_NOTE (note
)
3667 if (note
->selectedNotesHaveTags())
3672 QColor
Basket::backgroundColor()
3674 if (m_backgroundColorSetting
.isValid())
3675 return m_backgroundColorSetting
;
3677 return KGlobalSettings::baseColor();
3680 QColor
Basket::textColor()
3682 if (m_textColorSetting
.isValid())
3683 return m_textColorSetting
;
3685 return KGlobalSettings::textColor();
3688 void Basket::unbufferizeAll()
3690 FOR_EACH_NOTE (note
)
3691 note
->unbufferizeAll();
3694 Note
* Basket::editedNote()
3697 return m_editor
->note();
3702 bool Basket::hasTextInEditor()
3704 if (!isDuringEdit() || !redirectEditActions())
3707 if (m_editor
->textEdit())
3708 return ! m_editor
->textEdit()->text().isEmpty();
3709 else if (m_editor
->lineEdit())
3710 return ! m_editor
->lineEdit()->text().isEmpty();
3715 bool Basket::hasSelectedTextInEditor()
3717 if (!isDuringEdit() || !redirectEditActions())
3720 if (m_editor
->textEdit()) {
3721 // The following line does NOT work if one letter is selected and the user press Shift+Left or Shift+Right to unselect than letter:
3722 // Qt misteriously tell us there is an invisible selection!!
3723 //return m_editor->textEdit()->hasSelectedText();
3724 return !m_editor
->textEdit()->selectedText().isEmpty();
3725 } else if (m_editor
->lineEdit())
3726 return m_editor
->lineEdit()->hasSelectedText();
3731 bool Basket::selectedAllTextInEditor()
3733 if (!isDuringEdit() || !redirectEditActions())
3736 if (m_editor
->textEdit())
3737 return m_editor
->textEdit()->text().isEmpty() || m_editor
->textEdit()->text() == m_editor
->textEdit()->selectedText();
3738 else if (m_editor
->lineEdit())
3739 return m_editor
->lineEdit()->text().isEmpty() || m_editor
->lineEdit()->text() == m_editor
->lineEdit()->selectedText();
3744 void Basket::selectionChangedInEditor()
3746 Global::bnpView
->notesStateChanged();
3749 void Basket::contentChangedInEditor()
3751 // Do not wait 3 seconds, because we need the note to expand as needed (if a line is too wider... the note should grow wider):
3752 if (m_editor
->textEdit())
3753 m_editor
->autoSave(/*toFileToo=*/false);
3755 if (m_inactivityAutoSaveTimer
.isActive())
3756 m_inactivityAutoSaveTimer
.stop();
3757 m_inactivityAutoSaveTimer
.start(3 * 1000, /*singleShot=*/true);
3758 Global::bnpView
->setUnsavedStatus(true);
3762 void Basket::inactivityAutoSaveTimeout()
3765 m_editor
->autoSave(/*toFileToo=*/true);
3768 void Basket::placeEditorAndEnsureVisible()
3770 placeEditor(/*andEnsureVisible=*/true);
3773 void Basket::placeEditor(bool /*andEnsureVisible*/ /*= false*/)
3775 if (!isDuringEdit())
3778 Q3Frame
*editorQFrame
= dynamic_cast<Q3Frame
*>(m_editor
->widget());
3779 KTextEdit
*textEdit
= m_editor
->textEdit();
3780 // QLineEdit *lineEdit = m_editor->lineEdit();
3781 Note
*note
= m_editor
->note();
3783 int frameWidth
= (editorQFrame
? editorQFrame
->frameWidth() : 0);
3784 int x
= note
->x() + note
->contentX() + note
->content()->xEditorIndent() - frameWidth
;
3786 int maxHeight
= qMax(visibleHeight(), contentsHeight());
3791 // Need to do it 2 times, because it's wrong overwise
3792 // (sometimes, width depends on height, and sometimes, height depends on with):
3793 for (int i
= 0; i
< 2; i
++) {
3794 // FIXME: CRASH: Select all text, press Del or [<--] and editor->removeSelectedText() is called:
3795 // editor->sync() CRASH!!
3797 y
= note
->y() + Note::NOTE_MARGIN
- frameWidth
;
3798 height
= textEdit
->contentsHeight() + 2*frameWidth
;
3799 // height = /*qMax(*/height/*, note->height())*/;
3800 // height = qMin(height, visibleHeight());
3801 width
= note
->x() + note
->width() - x
+ 1;// /*note->x() + note->width()*/note->rightLimit() - x + 2*frameWidth + 1;
3802 //width=qMax(width,textEdit->contentsWidth()+2*frameWidth);
3803 if (y
+ height
> maxHeight
)
3804 y
= maxHeight
- height
;
3805 textEdit
->setFixedSize(width
, height
);
3808 height
= note
->height() - 2*Note::NOTE_MARGIN
+ 2*frameWidth
;
3809 width
= note
->x() + note
->width() - x
;//note->rightLimit() - x + 2*frameWidth;
3810 m_editor
->widget()->setFixedSize(width
, height
);
3812 y
= note
->y() + Note::NOTE_MARGIN
- frameWidth
;
3814 if ((m_editorWidth
> 0 && m_editorWidth
!= width
) || (m_editorHeight
> 0 && m_editorHeight
!= height
)) {
3815 m_editorWidth
= width
; // Avoid infinite recursion!!!
3816 m_editorHeight
= height
;
3817 m_editor
->autoSave(/*toFileToo=*/true);
3819 m_editorWidth
= width
;
3820 m_editorHeight
= height
;
3821 addChild(m_editor
->widget(), x
, y
);
3825 m_leftEditorBorder
->setFixedSize( (m_editor
->textEdit() ? 3 : 0), height
);
3826 // m_leftEditorBorder->raise();
3827 addChild(m_leftEditorBorder
, x
, y
);
3828 m_leftEditorBorder
->setPosition( x
, y
);
3830 m_rightEditorBorder
->setFixedSize(3, height
);
3831 // m_rightEditorBorder->raise();
3832 // addChild(m_rightEditorBorder, note->rightLimit() - Note::NOTE_MARGIN, note->y() + Note::NOTE_MARGIN );
3833 // m_rightEditorBorder->setPosition( note->rightLimit() - Note::NOTE_MARGIN, note->y() + Note::NOTE_MARGIN );
3834 addChild(m_rightEditorBorder
, note
->x() + note
->width() - Note::NOTE_MARGIN
, note
->y() + Note::NOTE_MARGIN
);
3835 m_rightEditorBorder
->setPosition( note
->x() + note
->width() - Note::NOTE_MARGIN
, note
->y() + Note::NOTE_MARGIN
);
3837 // if (andEnsureVisible)
3838 // ensureNoteVisible(note);
3842 #include <private/qrichtext_p.h>
3843 void Basket::editorCursorPositionChanged()
3845 if (!isDuringEdit())
3848 FocusedTextEdit
*textEdit
= (FocusedTextEdit
*) m_editor
->textEdit();
3849 const QTextCursor
*cursor
= textEdit
->textCursor();
3850 // std::cout << cursor->x() << ";" << cursor->y() << " "
3851 // << cursor->globalX() << ";" << cursor->globalY() << " "
3852 // << cursor->offsetX() << ";" << cursor->offsetY() << ";" << std::endl;
3854 ensureVisible(m_editorX
+ cursor
->globalX(), m_editorY
+ cursor
->globalY(), 50, 50);
3857 void Basket::closeEditorDelayed()
3860 QTimer::singleShot( 0, this, SLOT(closeEditor()) );
3863 bool Basket::closeEditor()
3865 if (!isDuringEdit())
3868 if (m_doNotCloseEditor
)
3871 if (m_redirectEditActions
) {
3872 disconnect( m_editor
->widget(), SIGNAL(selectionChanged()), this, SLOT(selectionChangedInEditor()) );
3873 if (m_editor
->textEdit()) {
3874 disconnect( m_editor
->textEdit(), SIGNAL(textChanged()), this, SLOT(selectionChangedInEditor()) );
3875 disconnect( m_editor
->textEdit(), SIGNAL(textChanged()), this, SLOT(contentChangedInEditor()) );
3876 } else if (m_editor
->lineEdit()) {
3877 disconnect( m_editor
->lineEdit(), SIGNAL(textChanged(const QString
&)), this, SLOT(selectionChangedInEditor()) );
3878 disconnect( m_editor
->lineEdit(), SIGNAL(textChanged(const QString
&)), this, SLOT(contentChangedInEditor()) );
3881 m_editor
->widget()->disconnect();
3882 m_editor
->widget()->hide();
3883 m_editor
->validate();
3885 delete m_leftEditorBorder
;
3886 delete m_rightEditorBorder
;
3887 m_leftEditorBorder
= 0;
3888 m_rightEditorBorder
= 0;
3890 Note
*note
= m_editor
->note();
3891 note
->setWidth(0); // For relayoutNotes() to succeed to take care of the change
3893 // Delete the editor BEFORE unselecting the note because unselecting the note would trigger closeEditor() recursivly:
3894 bool isEmpty
= m_editor
->isEmpty();
3897 m_redirectEditActions
= false;
3899 m_editorHeight
= -1;
3900 m_inactivityAutoSaveTimer
.stop();
3902 // Delete the note if it is now empty:
3904 focusANonSelectedNoteAboveOrThenBelow();
3905 note
->setSelected(true);
3906 note
->deleteSelectedNotes();
3912 filterAgain(/*andEnsureVisible=*/false);
3915 // if (Settings::playAnimations())
3916 // note->setOnTop(true); // So if it grew, do not obscure it temporarily while the notes below it are moving
3919 note
->setSelected(false);//unselectAll();
3923 Global::bnpView
->m_actEditNote
->setEnabled( !isLocked() && countSelecteds() == 1 /*&& !isDuringEdit()*/ );
3925 emit
resetStatusBarText(); // Remove the "Editing. ... to validate." text.
3927 //if (kapp->activeWindow() == Global::mainContainer)
3929 // Set focus to the basket, unless the user pressed a letter key in the filter bar and the currently edited note came hidden, then editing closed:
3930 if (!decoration()->filterBar()->lineEdit()->hasFocus())
3933 // Return true if the note is still there:
3937 void Basket::closeBasket()
3940 unbufferizeAll(); // Keep the memory footprint low
3941 if (isEncrypted()) {
3942 if (Settings::enableReLockTimeout()) {
3943 int seconds
= Settings::reLockTimeoutMinutes() * 60;
3944 m_inactivityAutoLockTimer
.start(seconds
* 1000, /*singleShot=*/true);
3949 void Basket::openBasket()
3951 if (m_inactivityAutoLockTimer
.isActive())
3952 m_inactivityAutoLockTimer
.stop();
3955 Note
* Basket::theSelectedNote()
3957 if (countSelecteds() != 1) {
3958 std::cout
<< "NO SELECTED NOTE !!!!" << std::endl
;
3963 FOR_EACH_NOTE (note
) {
3964 selectedOne
= note
->theSelectedNote();
3969 std::cout
<< "One selected note, BUT NOT FOUND !!!!" << std::endl
;
3974 void debugSel(NoteSelection
* sel
, int n
= 0)
3976 for (NoteSelection
*node
= sel
; node
; node
= node
->next
) {
3977 for (int i
= 0; i
< n
; i
++)
3979 std::cout
<< (node
->firstChild
? "Group" : node
->note
->content()->toText("")) << std::endl
;
3980 if (node
->firstChild
)
3981 debugSel(node
->firstChild
, n
+1);
3985 NoteSelection
* Basket::selectedNotes()
3987 NoteSelection selection
;
3989 FOR_EACH_NOTE (note
)
3990 selection
.append(note
->selectedNotes());
3992 if (!selection
.firstChild
)
3995 for (NoteSelection
*node
= selection
.firstChild
; node
; node
= node
->next
)
3998 // If the top-most groups are columns, export only childs of those groups
3999 // (because user is not consciencious that columns are groups, and don't care: it's not what she want):
4000 if (selection
.firstChild
->note
->isColumn()) {
4001 NoteSelection tmpSelection
;
4002 NoteSelection
*nextNode
;
4003 NoteSelection
*nextSubNode
;
4004 for (NoteSelection
*node
= selection
.firstChild
; node
; node
= nextNode
) {
4005 nextNode
= node
->next
;
4006 if (node
->note
->isColumn()) {
4007 for (NoteSelection
*subNode
= node
->firstChild
; subNode
; subNode
= nextSubNode
) {
4008 nextSubNode
= subNode
->next
;
4009 tmpSelection
.append(subNode
);
4010 subNode
->parent
= 0;
4014 tmpSelection
.append(node
);
4019 // debugSel(tmpSelection.firstChild);
4020 return tmpSelection
.firstChild
;
4022 // debugSel(selection.firstChild);
4023 return selection
.firstChild
;
4027 void Basket::showEditedNoteWhileFiltering()
4030 Note
*note
= m_editor
->note();
4032 note
->setSelected(true);
4033 relayoutNotes(false);
4034 note
->setX(note
->finalX());
4035 note
->setY(note
->finalY());
4036 filterAgainDelayed();
4040 void Basket::noteEdit(Note
*note
, bool justAdded
, const QPoint
&clickedPoint
) // TODO: Remove the first parameter!!!
4043 note
= theSelectedNote(); // TODO: Or pick the focused note!
4047 if (isDuringEdit()) {
4048 closeEditor(); // Validate the noteeditors in KLineEdit that does not intercept Enter key press (and edit is triggered with Enter too... Can conflict)
4052 if (note
!= m_focusedNote
) {
4053 setFocusedNote(note
);
4054 m_startOfShiftSelectionNote
= note
;
4057 if (justAdded
&& isFiltering()) {
4058 QTimer::singleShot( 0, this, SLOT(showEditedNoteWhileFiltering()) );
4061 doHoverEffects(note
, Note::Content
); // Be sure (in the case Edit was triggered by menu or Enter key...): better feedback!
4062 //m_lockedHovering = true;
4064 //m_editorWidget = note->content()->launchEdit(this);
4065 NoteEditor
*editor
= NoteEditor::editNoteContent(note
->content(), this);
4067 if (editor
->widget()) {
4069 m_leftEditorBorder
= new TransparentWidget(this);
4070 m_rightEditorBorder
= new TransparentWidget(this);
4071 m_editor
->widget()->reparent(viewport(), QPoint(0,0), true);
4072 m_leftEditorBorder
->reparent(viewport(), QPoint(0,0), true);
4073 m_rightEditorBorder
->reparent(viewport(), QPoint(0,0), true);
4074 addChild(m_editor
->widget(), 0, 0);
4075 placeEditorAndEnsureVisible(); // placeEditor(); // FIXME: After?
4076 m_redirectEditActions
= m_editor
->lineEdit() || m_editor
->textEdit();
4077 if (m_redirectEditActions
) {
4078 connect( m_editor
->widget(), SIGNAL(selectionChanged()), this, SLOT(selectionChangedInEditor()) );
4079 // In case there is NO text, "Select All" is disabled. But if the user press a key the there is now a text:
4080 // selection has not changed but "Select All" should be re-enabled:
4081 if (m_editor
->textEdit()) {
4082 connect( m_editor
->textEdit(), SIGNAL(textChanged()), this, SLOT(selectionChangedInEditor()) );
4083 connect( m_editor
->textEdit(), SIGNAL(textChanged()), this, SLOT(contentChangedInEditor()) );
4084 } else if (m_editor
->lineEdit()) {
4085 connect( m_editor
->lineEdit(), SIGNAL(textChanged(const QString
&)), this, SLOT(selectionChangedInEditor()) );
4086 connect( m_editor
->lineEdit(), SIGNAL(textChanged(const QString
&)), this, SLOT(contentChangedInEditor()) );
4089 m_editor
->widget()->show();
4090 //m_editor->widget()->raise();
4091 m_editor
->widget()->setFocus();
4092 connect( m_editor
, SIGNAL(askValidation()), this, SLOT(closeEditorDelayed()) );
4093 connect( m_editor
, SIGNAL(mouseEnteredEditorWidget()), this, SLOT(mouseEnteredEditorWidget()) );
4094 if (m_editor
->textEdit()) {
4095 connect( m_editor
->textEdit(), SIGNAL(textChanged()), this, SLOT(placeEditorAndEnsureVisible()) );
4096 if (clickedPoint
!= QPoint()) {
4097 QPoint
pos(clickedPoint
.x() - note
->x() - note
->contentX() + m_editor
->textEdit()->frameWidth() + 4 - m_editor
->textEdit()->frameWidth(),
4098 clickedPoint
.y() - note
->y() - m_editor
->textEdit()->frameWidth());
4099 // Do it right before the kapp->processEvents() to not have the cursor to quickly flicker at end (and sometimes stay at end AND where clicked):
4100 m_editor
->textEdit()->moveCursor(KTextEdit::MoveHome
, false);
4101 m_editor
->textEdit()->ensureCursorVisible();
4102 m_editor
->textEdit()->placeCursor(pos
);
4103 updateEditorAppearance();
4106 // kapp->processEvents(); // Show the editor toolbar before ensuring the note is visible
4107 ensureNoteVisible(note
); // because toolbar can create a new line and then partially hide the note
4108 m_editor
->widget()->setFocus(); // When clicking in the basket, a QTimer::singleShot(0, ...) focus the basket! So we focus the the widget after kapp->processEvents()
4109 emit
resetStatusBarText(); // Display "Editing. ... to validate."
4111 // Delete the note user have canceled the addition:
4112 if ((justAdded
&& editor
->canceled()) || editor
->isEmpty() /*) && editor->note()->states().count() <= 0*/) {
4113 focusANonSelectedNoteAboveOrThenBelow();
4114 editor
->note()->setSelected(true);
4115 editor
->note()->deleteSelectedNotes();
4123 Global::bnpView
->m_actEditNote
->setEnabled(false);
4126 void Basket::noteDelete()
4128 if (redirectEditActions()) {
4129 if (m_editor
->textEdit())
4130 m_editor
->textEdit()->del();
4131 else if (m_editor
->lineEdit())
4132 m_editor
->lineEdit()->del();
4136 if (countSelecteds() <= 0)
4138 int really
= KMessageBox::Yes
;
4139 if (Settings::confirmNoteDeletion())
4140 really
= KMessageBox::questionYesNo( this,
4141 i18n("<qt>Do you really want to delete this note?</qt>",
4142 "<qt>Do you really want to delete those <b>%n</b> notes?</qt>",
4144 i18n("Delete Note", "Delete Notes", countSelecteds())
4145 #if KDE_IS_VERSION( 3, 2, 90 ) // KDE 3.3.x
4146 , KStandardGuiItem::del(), KStandardGuiItem::cancel());
4150 if (really
== KMessageBox::No
)
4153 noteDeleteWithoutConfirmation();
4156 void Basket::focusANonSelectedNoteBelow(bool inSameColumn
)
4158 // First focus another unselected one below it...:
4159 if (m_focusedNote
&& m_focusedNote
->isSelected()) {
4160 Note
*next
= m_focusedNote
->nextShownInStack();
4161 while (next
&& next
->isSelected())
4162 next
= next
->nextShownInStack();
4164 if (inSameColumn
&& isColumnsLayout() && m_focusedNote
->parentPrimaryNote() == next
->parentPrimaryNote()) {
4165 setFocusedNote(next
);
4166 m_startOfShiftSelectionNote
= next
;
4172 void Basket::focusANonSelectedNoteAbove(bool inSameColumn
)
4175 if (m_focusedNote
&& m_focusedNote
->isSelected()) {
4176 Note
*prev
= m_focusedNote
->prevShownInStack();
4177 while (prev
&& prev
->isSelected())
4178 prev
= prev
->prevShownInStack();
4180 if (inSameColumn
&& isColumnsLayout() && m_focusedNote
->parentPrimaryNote() == prev
->parentPrimaryNote()) {
4181 setFocusedNote(prev
);
4182 m_startOfShiftSelectionNote
= prev
;
4188 void Basket::focusANonSelectedNoteBelowOrThenAbove()
4190 focusANonSelectedNoteBelow(/*inSameColumn=*/true);
4191 focusANonSelectedNoteAbove(/*inSameColumn=*/true);
4192 focusANonSelectedNoteBelow(/*inSameColumn=*/false);
4193 focusANonSelectedNoteAbove(/*inSameColumn=*/false);
4196 void Basket::focusANonSelectedNoteAboveOrThenBelow()
4198 focusANonSelectedNoteAbove(/*inSameColumn=*/true);
4199 focusANonSelectedNoteBelow(/*inSameColumn=*/true);
4200 focusANonSelectedNoteAbove(/*inSameColumn=*/false);
4201 focusANonSelectedNoteBelow(/*inSameColumn=*/false);
4204 void Basket::noteDeleteWithoutConfirmation(bool deleteFilesToo
)
4206 // If the currently focused note is selected, it will be deleted.
4207 focusANonSelectedNoteBelowOrThenAbove();
4210 Note
*note
= firstNote();
4213 next
= note
->next(); // If we delete 'note' on the next line, note->next() will be 0!
4214 note
->deleteSelectedNotes(deleteFilesToo
);
4218 relayoutNotes(true); // FIXME: filterAgain()?
4222 void Basket::doCopy(CopyMode copyMode
)
4224 QClipboard
*cb
= KApplication::clipboard();
4225 QClipboard::Mode mode
= (copyMode
== CopyToSelection
? QClipboard::Selection
: QClipboard::Clipboard
);
4227 NoteSelection
*selection
= selectedNotes();
4228 int countCopied
= countSelecteds();
4229 if (selection
->firstStacked()) {
4230 Q3DragObject
*d
= NoteDrag::dragObject(selection
, copyMode
== CutToClipboard
, /*source=*/0); // d will be deleted by QT
4231 // /*bool shouldRemove = */d->drag();
4232 // delete selection;
4233 cb
->setData(d
, mode
); // NoteMultipleDrag will be deleted by QT
4234 // if (copyMode == CutToClipboard && !note->useFile()) // If useFile(), NoteDrag::dragObject() will delete it TODO
4235 // note->slotDelete();
4237 if (copyMode
== CutToClipboard
)
4238 noteDeleteWithoutConfirmation(/*deleteFilesToo=*/false);
4242 case CopyToClipboard
: emit
postMessage(i18n("Copied note to clipboard.", "Copied notes to clipboard.", countCopied
)); break;
4243 case CutToClipboard
: emit
postMessage(i18n("Cut note to clipboard.", "Cut notes to clipboard.", countCopied
)); break;
4244 case CopyToSelection
: emit
postMessage(i18n("Copied note to selection.", "Copied notes to selection.", countCopied
)); break;
4249 void Basket::noteCopy()
4251 if (redirectEditActions()) {
4252 if (m_editor
->textEdit())
4253 m_editor
->textEdit()->copy();
4254 else if (m_editor
->lineEdit())
4255 m_editor
->lineEdit()->copy();
4257 doCopy(CopyToClipboard
);
4260 void Basket::noteCut()
4262 if (redirectEditActions()) {
4263 if (m_editor
->textEdit())
4264 m_editor
->textEdit()->cut();
4265 else if (m_editor
->lineEdit())
4266 m_editor
->lineEdit()->cut();
4268 doCopy(CutToClipboard
);
4271 void Basket::noteOpen(Note
*note
)
4275 NoSelectedNote || Count == 0 ? return
4277 Get { url, message(count) }
4280 // TODO: Open ALL selected notes!
4282 note
= theSelectedNote();
4286 KUrl url
= note
->content()->urlToOpen(/*with=*/false);
4287 QString message
= note
->content()->messageWhenOpenning(NoteContent::OpenOne
/*NoteContent::OpenSeveral*/);
4288 if (url
.isEmpty()) {
4289 if (message
.isEmpty())
4290 emit
postMessage(i18n("Unable to open this note.") /*"Unable to open those notes."*/);
4292 int result
= KMessageBox::warningContinueCancel(this, message
, /*caption=*/QString::null
, KGuiItem(i18n("&Edit"), "edit"));
4293 if (result
== KMessageBox::Continue
)
4297 emit
postMessage(message
); // "Openning link target..." / "Launching application..." / "Openning note file..."
4298 // Finally do the opening job:
4299 QString customCommand
= note
->content()->customOpenCommand();
4300 if (customCommand
.isEmpty()) {
4301 KRun
*run
= new KRun(url
);
4302 run
->setAutoDelete(true);
4304 KRun::run(customCommand
, url
);
4308 /** Code from bool KRun::displayOpenWithDialog(const KUrl::List& lst, bool tempFiles)
4309 * It does not allow to set a text, so I ripped it to do that:
4311 bool KRun__displayOpenWithDialog(const KUrl::List
& lst
, bool tempFiles
, const QString
&text
)
4313 if (kapp
&& !KAuthorized::authorizeKAction("openwith")) {
4314 KMessageBox::sorry(0L, i18n("You are not authorized to open this file.")); // TODO: Better message, i18n freeze :-(
4317 KOpenWithDlg
l(lst
, text
, QString::null
, 0L);
4319 KService::Ptr service
= l
.service();
4321 return KRun::run(*service
, lst
, tempFiles
);
4322 //kDebug(250) << "No service set, running " << l.text() << endl;
4323 return KRun::run(l
.text(), lst
); // TODO handle tempFiles
4328 void Basket::noteOpenWith(Note
*note
)
4331 note
= theSelectedNote();
4335 KUrl url
= note
->content()->urlToOpen(/*with=*/true);
4336 QString message
= note
->content()->messageWhenOpenning(NoteContent::OpenOneWith
/*NoteContent::OpenSeveralWith*/);
4337 QString text
= note
->content()->messageWhenOpenning(NoteContent::OpenOneWithDialog
/*NoteContent::OpenSeveralWithDialog*/);
4339 emit
postMessage(i18n("Unable to open this note.") /*"Unable to open those notes."*/);
4340 else if (KRun__displayOpenWithDialog(url
, false, text
))
4341 emit
postMessage(message
); // "Openning link target with..." / "Openning note file with..."
4344 void Basket::noteSaveAs()
4347 // note = theSelectedNote();
4348 Note
*note
= theSelectedNote();
4352 KUrl url
= note
->content()->urlToOpen(/*with=*/false);
4356 QString fileName
= KFileDialog::getSaveFileName(url
.fileName(), note
->content()->saveAsFilters(), this, i18n("Save to File"));
4357 // TODO: Ask to overwrite !
4358 if (fileName
.isEmpty())
4361 // TODO: Convert format, etc. (use NoteContent::saveAs(fileName))
4362 KIO::copy(url
, KUrl(fileName
));
4365 Note
* Basket::selectedGroup()
4367 FOR_EACH_NOTE (note
) {
4368 Note
*selectedGroup
= note
->selectedGroup();
4369 if (selectedGroup
) {
4370 // If the selected group is one group in a column, then return that group, and not the column,
4371 // because the column is not ungrouppage, and the Ungroup action would be disabled.
4372 if (selectedGroup
->isColumn() && selectedGroup
->firstChild() && !selectedGroup
->firstChild()->next()) {
4373 return selectedGroup
->firstChild();
4375 return selectedGroup
;
4381 bool Basket::selectionIsOneGroup()
4383 return (selectedGroup() != 0);
4386 Note
* Basket::firstSelected()
4389 FOR_EACH_NOTE (note
) {
4390 first
= note
->firstSelected();
4397 Note
* Basket::lastSelected()
4399 Note
*last
= 0, *tmp
= 0;
4400 FOR_EACH_NOTE (note
) {
4401 tmp
= note
->lastSelected();
4408 bool Basket::convertTexts()
4410 m_watcher
->stopScan();
4411 bool convertedNotes
= false;
4416 FOR_EACH_NOTE (note
)
4417 if (note
->convertTexts())
4418 convertedNotes
= true;
4422 m_watcher
->startScan();
4423 return convertedNotes
;
4426 void Basket::noteGroup()
4428 /* // Nothing to do?
4429 if (isLocked() || countSelecteds() <= 1)
4432 // If every selected notes are ALREADY in one group, then don't touch anything:
4433 Note *selectedGroup = this->selectedGroup();
4434 if (selectedGroup && !selectedGroup->isColumn())
4438 // Copied from BNPView::updateNotesActions()
4439 bool severalSelected
= countSelecteds() >= 2;
4440 Note
*selectedGroup
= (severalSelected
? this->selectedGroup() : 0);
4441 bool enabled
= !isLocked() && severalSelected
&& (!selectedGroup
|| selectedGroup
->isColumn());
4445 // Get the first selected note: we will group selected items just before:
4446 Note
*first
= firstSelected();
4447 // if (selectedGroup != 0 || first == 0)
4450 m_loaded
= false; // Hack to avoid notes to be unselected and new notes to be selected:
4452 // Create and insert the receiving group:
4453 Note
*group
= new Note(this);
4454 if (first
->isFree()) {
4455 insertNote(group
, 0L, Note::BottomColumn
, QPoint(first
->finalX(), first
->finalY()), /*animateNewPosition=*/false);
4457 insertNote(group
, first
, Note::TopInsert
, QPoint(), /*animateNewPosition=*/false);
4460 // Put a FAKE UNSELECTED note in the new group, so if the new group is inside an allSelected() group, the parent group is not moved inside the new group!
4461 Note
*fakeNote
= NoteFactory::createNoteColor(Qt::red
, this);
4462 insertNote(fakeNote
, group
, Note::BottomColumn
, QPoint(), /*animateNewPosition=*/false);
4466 Note
*note
= firstNote();
4468 nextNote
= note
->next();
4469 note
->groupIn(group
);
4473 m_loaded
= true; // Part 2 / 2 of the workarround!
4476 unplugNote(fakeNote
);
4478 group
->setSelectedRecursivly(true); // Notes were unselected by unplugging
4480 relayoutNotes(true);
4484 void Basket::noteUngroup()
4486 Note
*group
= selectedGroup();
4487 if (group
&& !group
->isColumn())
4492 void Basket::unplugSelection(NoteSelection
*selection
)
4494 for (NoteSelection
*toUnplug
= selection
->firstStacked(); toUnplug
; toUnplug
= toUnplug
->nextStacked())
4495 unplugNote(toUnplug
->note
);
4498 void Basket::insertSelection(NoteSelection
*selection
, Note
*after
)
4500 for (NoteSelection
*toUnplug
= selection
->firstStacked(); toUnplug
; toUnplug
= toUnplug
->nextStacked()) {
4501 if (toUnplug
->note
->isGroup()) {
4502 Note
*group
= new Note(this);
4503 insertNote(group
, after
, Note::BottomInsert
, QPoint(), /*animateNewPosition=*/false);
4504 Note
*fakeNote
= NoteFactory::createNoteColor(Qt::red
, this);
4505 insertNote(fakeNote
, group
, Note::BottomColumn
, QPoint(), /*animateNewPosition=*/false);
4506 insertSelection(toUnplug
->firstChild
, fakeNote
);
4507 unplugNote(fakeNote
);
4510 Note
*note
= toUnplug
->note
;
4513 insertNote(note
, after
, Note::BottomInsert
, QPoint(), /*animateNewPosition=*/true);
4519 void Basket::selectSelection(NoteSelection
*selection
)
4521 for (NoteSelection
*toUnplug
= selection
->firstStacked(); toUnplug
; toUnplug
= toUnplug
->nextStacked()) {
4522 if (toUnplug
->note
->isGroup())
4523 selectSelection(toUnplug
);
4525 toUnplug
->note
->setSelected(true);
4529 void Basket::noteMoveOnTop()
4531 // TODO: Get the group containing the selected notes and first move inside the group, then inside parent group, then in the basket
4532 // TODO: Move on top/bottom... of the column or basjet
4534 NoteSelection
*selection
= selectedNotes();
4535 unplugSelection(selection
);
4536 // Replug the notes:
4537 Note
*fakeNote
= NoteFactory::createNoteColor(Qt::red
, this);
4538 if (isColumnsLayout()) {
4539 if (firstNote()->firstChild())
4540 insertNote(fakeNote
, firstNote()->firstChild(), Note::TopInsert
, QPoint(), /*animateNewPosition=*/false);
4542 insertNote(fakeNote
, firstNote(), Note::BottomColumn
, QPoint(), /*animateNewPosition=*/false);
4544 // TODO: Also allow to move notes on top of a group!!!!!!!
4545 insertNote(fakeNote
, 0, Note::BottomInsert
, QPoint(0, 0), /*animateNewPosition=*/false);
4547 insertSelection(selection
, fakeNote
);
4548 unplugNote(fakeNote
);
4549 selectSelection(selection
);
4550 relayoutNotes(true);
4554 void Basket::noteMoveOnBottom()
4557 // TODO: Duplicate code: void noteMoveOn();
4559 // TODO: Get the group containing the selected notes and first move inside the group, then inside parent group, then in the basket
4560 // TODO: Move on top/bottom... of the column or basjet
4562 NoteSelection
*selection
= selectedNotes();
4563 unplugSelection(selection
);
4564 // Replug the notes:
4565 Note
*fakeNote
= NoteFactory::createNoteColor(Qt::red
, this);
4566 if (isColumnsLayout())
4567 insertNote(fakeNote
, firstNote(), Note::BottomColumn
, QPoint(), /*animateNewPosition=*/false);
4569 // TODO: Also allow to move notes on top of a group!!!!!!!
4570 insertNote(fakeNote
, 0, Note::BottomInsert
, QPoint(0, 0), /*animateNewPosition=*/false);
4572 insertSelection(selection
, fakeNote
);
4573 unplugNote(fakeNote
);
4574 selectSelection(selection
);
4575 relayoutNotes(true);
4579 void Basket::moveSelectionTo(Note
*here
, bool below
/* = true*/)
4581 NoteSelection
*selection
= selectedNotes();
4582 unplugSelection(selection
);
4583 // Replug the notes:
4584 Note
*fakeNote
= NoteFactory::createNoteColor(Qt::red
, this);
4585 // if (isColumnsLayout())
4586 insertNote(fakeNote
, here
, (below
? Note::BottomInsert
: Note::TopInsert
), QPoint(), /*animateNewPosition=*/false);
4588 // // TODO: Also allow to move notes on top of a group!!!!!!!
4589 // insertNote(fakeNote, 0, Note::BottomInsert, QPoint(0, 0), /*animateNewPosition=*/false);
4591 insertSelection(selection
, fakeNote
);
4592 unplugNote(fakeNote
);
4593 selectSelection(selection
);
4594 relayoutNotes(true);
4598 void Basket::noteMoveNoteUp()
4601 // TODO: Move between columns, even if they are empty !!!!!!!
4603 // TODO: if first note of a group, move just above the group! And let that even if there is no note before that group!!!
4605 Note
*first
= firstSelected();
4606 Note
*previous
= first
->prevShownInStack();
4608 moveSelectionTo(previous
, /*below=*/false);
4611 void Basket::noteMoveNoteDown()
4613 Note
*first
= lastSelected();
4614 Note
*next
= first
->nextShownInStack();
4616 moveSelectionTo(next
, /*below=*/true);
4619 void Basket::wheelEvent(QWheelEvent
*event
)
4621 Q3ScrollView::wheelEvent(event
);
4624 void Basket::linkLookChanged()
4626 Note
*note
= m_firstNote
;
4628 note
->linkLookChanged();
4629 note
= note
->next();
4631 relayoutNotes(true);
4634 void Basket::slotCopyingDone2(KIO::Job
*job
)
4637 DEBUG_WIN
<< "Copy finished, ERROR";
4640 KIO::FileCopyJob
*fileCopyJob
= (KIO::FileCopyJob
*)job
;
4641 Note
*note
= noteForFullPath(fileCopyJob
->destURL().path());
4642 DEBUG_WIN
<< "Copy finished, load note: " + fileCopyJob
->destURL().path() + (note
? "" : " --- NO CORRESPONDING NOTE");
4644 note
->content()->loadFromFile(/*lazyLoad=*/false);
4646 note
->content()->saveToFile();
4647 if (m_focusedNote
== note
) // When inserting a new note we ensure it visble
4648 ensureNoteVisible(note
); // But after loading it has certainly grown and if it was
4649 } // on bottom of the basket it's not visible entirly anymore
4652 Note
* Basket::noteForFullPath(const QString
&path
)
4654 Note
*note
= firstNote();
4657 found
= note
->noteForFullPath(path
);
4660 note
= note
->next();
4665 void Basket::deleteFiles()
4667 m_watcher
->stopScan();
4668 Tools::deleteRecursively(fullPath());
4671 Q3ValueList
<State
*> Basket::usedStates()
4673 Q3ValueList
<State
*> states
;
4674 FOR_EACH_NOTE (note
)
4675 note
->usedStates(states
);
4679 QString
Basket::saveGradientBackground(const QColor
&color
, const QFont
&font
, const QString
&folder
)
4681 // Construct file name and return if the file already exists:
4682 QString fileName
= "note_background_" + color
.name().toLower().mid(1) + ".png";
4683 QString fullPath
= folder
+ fileName
;
4684 if (QFile::exists(fullPath
))
4687 // Get the gradient top and bottom colors:
4689 QColor bottomBgColor
;
4690 Note::getGradientColors(color
, &topBgColor
, &bottomBgColor
);
4692 // Draw and save the gradient image:
4693 int sampleTextHeight
= QFontMetrics(font
)
4694 .boundingRect(0, 0, /*width=*/10000, /*height=*/0, Qt::AlignLeft
| Qt::AlignTop
| Qt::TextWordWrap
, "Test text")
4696 QPixmap
noteGradient(100, sampleTextHeight
+ Note::NOTE_MARGIN
);
4697 QPainter
painter(¬eGradient
);
4698 drawGradient(&painter
, topBgColor
, bottomBgColor
, 0, 0, noteGradient
.width(), noteGradient
.height(), /*sunken=*/false, /*horz=*/true, /*flat=*/false);
4700 noteGradient
.save(fullPath
, "PNG");
4702 // Return the name of the created file:
4706 void Basket::listUsedTags(Q3ValueList
<Tag
*> &list
)
4712 FOR_EACH_NOTE (child
)
4713 child
->listUsedTags(list
);
4717 /** Unfocus the previously focused note (unless it was null)
4718 * and focus the new @param note (unless it is null) if hasFocus()
4719 * Update m_focusedNote to the new one
4721 void Basket::setFocusedNote(Note
*note
) // void Basket::changeFocusTo(Note *note)
4723 // Don't focus an hidden note:
4724 if (note
!= 0L && !note
->isShown())
4726 // When clicking a group, this group gets focused. But only content-based notes should be focused:
4727 if (note
&& note
->isGroup())
4728 note
= note
->firstRealChild();
4729 // The first time a note is focused, it becomes the start of the Shift selection:
4730 if (m_startOfShiftSelectionNote
== 0)
4731 m_startOfShiftSelectionNote
= note
;
4732 // Unfocus the old focused note:
4733 if (m_focusedNote
!= 0L)
4734 m_focusedNote
->setFocused(false);
4735 // Notify the new one to draw a focus rectangle... only if the basket is focused:
4736 if (hasFocus() && note
!= 0L)
4737 note
->setFocused(true);
4738 // Save the new focused note:
4739 m_focusedNote
= note
;
4742 /** If no shown note is currently focused, try to find a shown note and focus it
4743 * Also update m_focusedNote to the new one (or null if there isn't)
4745 void Basket::focusANote()
4747 if (countFounds() == 0) { // No note to focus
4749 // m_startOfShiftSelectionNote = 0;
4753 if (m_focusedNote
== 0L) { // No focused note yet : focus the first shown
4754 Note
*toFocus
= (isFreeLayout() ? noteOnHome() : firstNoteShownInStack());
4755 setFocusedNote(toFocus
);
4756 // m_startOfShiftSelectionNote = m_focusedNote;
4760 // Search a visible note to focus if the focused one isn't shown :
4761 Note
*toFocus
= m_focusedNote
;
4762 if (toFocus
&& !toFocus
->isShown())
4763 toFocus
= toFocus
->nextShownInStack();
4764 if (!toFocus
&& m_focusedNote
)
4765 toFocus
= m_focusedNote
->prevShownInStack();
4766 setFocusedNote(toFocus
);
4767 // m_startOfShiftSelectionNote = toFocus;
4770 Note
* Basket::firstNoteInStack()
4775 if (firstNote()->content())
4778 return firstNote()->nextInStack();
4781 Note
* Basket::lastNoteInStack()
4783 Note
*note
= lastNote();
4785 if (note
->content())
4787 Note
*possibleNote
= note
->lastRealChild();
4788 if (possibleNote
&& possibleNote
->content())
4789 return possibleNote
;
4790 note
= note
->prev();
4795 Note
* Basket::firstNoteShownInStack()
4797 Note
*first
= firstNoteInStack();
4798 while (first
&& !first
->isShown())
4799 first
= first
->nextInStack();
4803 Note
* Basket::lastNoteShownInStack()
4805 Note
*last
= lastNoteInStack();
4806 while (last
&& !last
->isShown())
4807 last
= last
->prevInStack();
4811 inline int abs(int n
)
4813 return (n
< 0 ? -n
: n
);
4816 Note
* Basket::noteOn(NoteOn side
)
4820 int bestDistance
= contentsWidth() * contentsHeight() * 10;
4822 Note
*note
= firstNoteShownInStack();
4823 Note
*primary
= m_focusedNote
->parentPrimaryNote();
4826 case LEFT_SIDE
: distance
= m_focusedNote
->distanceOnLeftRight(note
, LEFT_SIDE
); break;
4827 case RIGHT_SIDE
: distance
= m_focusedNote
->distanceOnLeftRight(note
, RIGHT_SIDE
); break;
4828 case TOP_SIDE
: distance
= m_focusedNote
->distanceOnTopBottom(note
, TOP_SIDE
); break;
4829 case BOTTOM_SIDE
: distance
= m_focusedNote
->distanceOnTopBottom(note
, BOTTOM_SIDE
); break;
4831 if ((side
== TOP_SIDE
|| side
== BOTTOM_SIDE
|| primary
!= note
->parentPrimaryNote()) && note
!= m_focusedNote
&& distance
> 0 && distance
< bestDistance
) {
4833 bestDistance
= distance
;
4835 note
= note
->nextShownInStack();
4841 Note
* Basket::firstNoteInGroup()
4843 Note
*child
= m_focusedNote
;
4844 Note
*parent
= (m_focusedNote
? m_focusedNote
->parentNote() : 0);
4846 if (parent
->firstChild() != child
&& !parent
->isColumn())
4847 return parent
->firstRealChild();
4849 parent
= parent
->parentNote();
4854 Note
* Basket::noteOnHome()
4856 // First try to find the first note of the group containing the focused note:
4857 Note
*child
= m_focusedNote
;
4858 Note
*parent
= (m_focusedNote
? m_focusedNote
->parentNote() : 0);
4860 if (parent
->nextShownInStack() != m_focusedNote
)
4861 return parent
->nextShownInStack();
4863 parent
= parent
->parentNote();
4866 // If it was not found, then focus the very first note in the basket:
4867 if (isFreeLayout()) {
4868 Note
*first
= firstNoteShownInStack(); // The effective first note found
4869 Note
*note
= first
; // The current note, to conpare with the previous first note, if this new note is more on top
4871 note
= note
->nextShownInStack();
4873 if (note
->finalY() < first
->finalY() || (note
->finalY() == first
->finalY() && note
->finalX() < first
->finalX()))
4875 note
= note
->nextShownInStack();
4879 return firstNoteShownInStack();
4882 Note
* Basket::noteOnEnd()
4884 Note
*child
= m_focusedNote
;
4885 Note
*parent
= (m_focusedNote
? m_focusedNote
->parentNote() : 0);
4888 lastChild
= parent
->lastRealChild();
4889 if (lastChild
&& lastChild
!= m_focusedNote
) {
4890 if (lastChild
->isShown())
4892 lastChild
= lastChild
->prevShownInStack();
4893 if (lastChild
&& lastChild
->isShown() && lastChild
!= m_focusedNote
)
4897 parent
= parent
->parentNote();
4899 if (isFreeLayout()) {
4902 last
= note
= firstNoteShownInStack();
4903 note
= note
->nextShownInStack();
4905 if (note
->finalBottom() > last
->finalBottom() || (note
->finalBottom() == last
->finalBottom() && note
->finalX() > last
->finalX()))
4907 note
= note
->nextShownInStack();
4911 return lastNoteShownInStack();
4915 void Basket::keyPressEvent(QKeyEvent
*event
)
4917 if (isDuringEdit() && event
->key() == Qt::Key_Return
) {
4918 //if (m_editor->lineEdit())
4921 m_editor
->widget()->setFocus();
4922 } else if (event
->key() == Qt::Key_Escape
) {
4925 else if (decoration()->filterData().isFiltering
)
4931 if (countFounds() == 0)
4939 switch (event
->key()) {
4941 toFocus
= (isFreeLayout() ? noteOn(BOTTOM_SIDE
) : m_focusedNote
->nextShownInStack());
4944 scrollBy(0, 30); // This cases do not move focus to another note...
4947 toFocus
= (isFreeLayout() ? noteOn(TOP_SIDE
) : m_focusedNote
->prevShownInStack());
4950 scrollBy(0, -30); // This cases do not move focus to another note...
4952 case Qt::Key_PageDown
:
4953 if (isFreeLayout()) {
4954 Note
*lastFocused
= m_focusedNote
;
4955 for (int i
= 0; i
< 10 && m_focusedNote
; ++i
)
4956 m_focusedNote
= noteOn(BOTTOM_SIDE
);
4957 toFocus
= m_focusedNote
;
4958 m_focusedNote
= lastFocused
;
4960 toFocus
= m_focusedNote
;
4961 for (int i
= 0; i
< 10 && toFocus
; ++i
)
4962 toFocus
= toFocus
->nextShownInStack();
4965 toFocus
= (isFreeLayout() ? noteOnEnd() : lastNoteShownInStack());
4966 if (toFocus
&& toFocus
!= m_focusedNote
)
4968 scrollBy(0, visibleHeight() / 2); // This cases do not move focus to another note...
4970 case Qt::Key_PageUp
:
4971 if (isFreeLayout()) {
4972 Note
*lastFocused
= m_focusedNote
;
4973 for (int i
= 0; i
< 10 && m_focusedNote
; ++i
)
4974 m_focusedNote
= noteOn(TOP_SIDE
);
4975 toFocus
= m_focusedNote
;
4976 m_focusedNote
= lastFocused
;
4978 toFocus
= m_focusedNote
;
4979 for (int i
= 0; i
< 10 && toFocus
; ++i
)
4980 toFocus
= toFocus
->prevShownInStack();
4983 toFocus
= (isFreeLayout() ? noteOnHome() : firstNoteShownInStack());
4984 if (toFocus
&& toFocus
!= m_focusedNote
)
4986 scrollBy(0, - visibleHeight() / 2); // This cases do not move focus to another note...
4989 toFocus
= noteOnHome();
4992 toFocus
= noteOnEnd();
4995 if (m_focusedNote
->tryFoldParent())
4997 if ( (toFocus
= noteOn(LEFT_SIDE
)) )
4999 if ( (toFocus
= firstNoteInGroup()) )
5001 scrollBy(-30, 0); // This cases do not move focus to another note...
5004 if (m_focusedNote
->tryExpandParent())
5006 if ( (toFocus
= noteOn(RIGHT_SIDE
)) )
5008 scrollBy(30, 0); // This cases do not move focus to another note...
5010 case Qt::Key_Space
: // This case do not move focus to another note...
5011 if (m_focusedNote
) {
5012 m_focusedNote
->setSelected( ! m_focusedNote
->isSelected() );
5016 return; // ... so we return after the process
5021 if (toFocus
== 0L) { // If no direction keys have been pressed OR reached out the begin or end
5022 event
->ignore(); // Important !!
5026 if (event
->state() & Qt::ShiftModifier
) { // Shift+arrowKeys selection
5027 if (m_startOfShiftSelectionNote
== 0L)
5028 m_startOfShiftSelectionNote
= toFocus
;
5029 ensureNoteVisible(toFocus
); // Important: this line should be before the other ones because else repaint would be done on the wrong part!
5030 selectRange(m_startOfShiftSelectionNote
, toFocus
);
5031 setFocusedNote(toFocus
);
5034 } else /*if (toFocus != m_focusedNote)*/ { // Move focus to ANOTHER note...
5035 ensureNoteVisible(toFocus
); // Important: this line should be before the other ones because else repaint would be done on the wrong part!
5036 setFocusedNote(toFocus
);
5037 m_startOfShiftSelectionNote
= toFocus
;
5038 if ( ! (event
->state() & Qt::ControlModifier
) ) // ... select only current note if Control
5039 unselectAllBut(m_focusedNote
);
5044 event
->ignore(); // Important !!
5047 /** Select a range of notes and deselect the others.
5048 * The order between start and end has no importance (end could be before start)
5050 void Basket::selectRange(Note
*start
, Note
*end
, bool unselectOthers
/*= true*/)
5055 // Avoid crash when start (or end) is null
5060 // And if *both* are null
5066 // In case there is only one note to select
5069 unselectAllBut(start
);
5071 start
->setSelected(true);
5075 // Free layout baskets should select range as if we were drawing a rectangle between start and end:
5076 if (isFreeLayout()) {
5077 QRect
startRect( start
->finalX(), start
->finalY(), start
->width(), start
->finalHeight() );
5078 QRect
endRect( end
->finalX(), end
->finalY(), end
->width(), end
->finalHeight() );
5079 QRect toSelect
= startRect
.unite(endRect
);
5080 selectNotesIn(toSelect
, /*invertSelection=*/false, unselectOthers
);
5084 // Search the REAL first (and deselect the others before it) :
5085 for (cur
= firstNoteInStack(); cur
!= 0L; cur
= cur
->nextInStack()) {
5086 if (cur
== start
|| cur
== end
)
5089 cur
->setSelected(false);
5092 // Select the notes after REAL start, until REAL end :
5095 else if (cur
== end
)
5098 for (/*cur = cur*/; cur
!= 0L; cur
= cur
->nextInStack()) {
5099 cur
->setSelected(cur
->isShown()); // Select all notes in the range, but only if they are shown
5104 if (!unselectOthers
)
5107 // Deselect the remaining notes :
5109 cur
= cur
->nextInStack();
5110 for (/*cur = cur*/; cur
!= 0L; cur
= cur
->nextInStack())
5111 cur
->setSelected(false);
5114 void Basket::focusInEvent(QFocusEvent
*)
5116 // Focus cannot be get with Tab when locked, but a click can focus the basket!
5119 QTimer::singleShot( 0, m_button
, SLOT(setFocus()) );
5121 focusANote(); // hasFocus() is true at this stage, note will be focused
5124 void Basket::focusOutEvent(QFocusEvent
*)
5126 if (m_focusedNote
!= 0L)
5127 m_focusedNote
->setFocused(false);
5130 void Basket::ensureNoteVisible(Note
*note
)
5132 if (!note
->isShown()) // Logical!
5135 if (note
== editedNote()) // HACK: When filtering while editing big notes, etc... cause unwanted scrolls
5138 int finalBottom
= note
->finalY() + qMin(note
->finalHeight(), visibleHeight());
5139 int finalRight
= note
->finalX() + qMin(note
->width() + (note
->hasResizer() ? Note::RESIZER_WIDTH
: 0), visibleWidth());
5140 ensureVisible( finalRight
, finalBottom
, 0,0 );
5141 ensureVisible( note
->finalX(), note
->finalY(), 0,0 );
5144 void Basket::addWatchedFile(const QString
&fullPath
)
5146 // DEBUG_WIN << "Watcher>Add Monitoring Of : <font color=blue>" + fullPath + "</font>";
5147 m_watcher
->addFile(fullPath
);
5150 void Basket::removeWatchedFile(const QString
&fullPath
)
5152 // DEBUG_WIN << "Watcher>Remove Monitoring Of : <font color=blue>" + fullPath + "</font>";
5153 m_watcher
->removeFile(fullPath
);
5156 void Basket::watchedFileModified(const QString
&fullPath
)
5158 if (!m_modifiedFiles
.contains(fullPath
))
5159 m_modifiedFiles
.append(fullPath
);
5160 // If a big file is saved by an application, notifications are send several times.
5161 // We wait they are not sent anymore to considere the file complete!
5162 m_watcherTimer
.start(200/*ms*/, true);
5163 DEBUG_WIN
<< "Watcher>Modified : <font color=blue>" + fullPath
+ "</font>";
5166 void Basket::watchedFileDeleted(const QString
&fullPath
)
5168 Note
*note
= noteForFullPath(fullPath
);
5169 removeWatchedFile(fullPath
);
5171 NoteSelection
*selection
= selectedNotes();
5172 unselectAllBut(note
);
5173 noteDeleteWithoutConfirmation();
5175 selection
->note
->setSelected(true);
5176 selection
= selection
->nextStacked();
5179 DEBUG_WIN
<< "Watcher>Removed : <font color=blue>" + fullPath
+ "</font>";
5182 void Basket::updateModifiedNotes()
5184 for (Q3ValueList
<QString
>::iterator it
= m_modifiedFiles
.begin(); it
!= m_modifiedFiles
.end(); ++it
) {
5185 Note
*note
= noteForFullPath(*it
);
5187 note
->content()->loadFromFile(/*lazyLoad=*/false);
5189 m_modifiedFiles
.clear();
5192 bool Basket::setProtection(int type
, QString key
)
5194 #ifdef HAVE_LIBGPGME
5195 if(type
== PasswordEncryption
|| // Ask a new password
5196 m_encryptionType
!= type
|| m_encryptionKey
!= key
)
5198 int savedType
= m_encryptionType
;
5199 QString savedKey
= m_encryptionKey
;
5201 m_encryptionType
= type
;
5202 m_encryptionKey
= key
;
5203 m_gpg
->clearCache();
5207 emit
propertiesChanged(this);
5211 m_encryptionType
= savedType
;
5212 m_encryptionKey
= savedKey
;
5213 m_gpg
->clearCache();
5219 m_encryptionType
= type
;
5220 m_encryptionKey
= key
;
5225 bool Basket::saveAgain()
5227 bool result
= false;
5229 m_watcher
->stopScan();
5230 // Re-encrypt basket file:
5232 // Re-encrypt every note files recursively:
5235 FOR_EACH_NOTE (note
)
5237 result
= note
->saveAgain();
5242 m_watcher
->startScan();
5246 bool Basket::loadFromFile(const QString
&fullPath
, QString
*string
, bool isLocalEncoding
)
5250 if(loadFromFile(fullPath
, &array
)){
5251 if (isLocalEncoding
)
5252 *string
= QString::fromLocal8Bit(array
.data(), array
.size());
5254 *string
= QString::fromUtf8(array
.data(), array
.size());
5261 bool Basket::isEncrypted()
5263 return (m_encryptionType
!= NoEncryption
);
5266 bool Basket::isFileEncrypted()
5268 QFile
file(fullPath() + ".basket");
5270 if (file
.open(QIODevice::ReadOnly
)){
5273 file
.readLine(line
, 32);
5274 if(line
.startsWith("-----BEGIN PGP MESSAGE-----"))
5280 bool Basket::loadFromFile(const QString
&fullPath
, QByteArray
*array
)
5282 QFile
file(fullPath
);
5283 bool encrypted
= false;
5285 if (file
.open(QIODevice::ReadOnly
)){
5286 *array
= file
.readAll();
5287 const char* magic
= "-----BEGIN PGP MESSAGE-----";
5290 if(array
->size() > strlen(magic
))
5291 for (i
= 0; array
->at(i
) == magic
[i
]; ++i
)
5293 if (i
== strlen(magic
))
5298 #ifdef HAVE_LIBGPGME
5301 QByteArray
tmp(*array
);
5304 // Only use gpg-agent for private key encryption since it doesn't
5305 // cache password used in symmetric encryption.
5306 m_gpg
->setUseGnuPGAgent(Settings::useGnuPGAgent() && m_encryptionType
== PrivateKeyEncryption
);
5307 if(m_encryptionType
== PrivateKeyEncryption
)
5308 m_gpg
->setText(i18n("Please enter the password for the following private key:"), false);
5310 m_gpg
->setText(i18n("Please enter the password for the basket <b>%1</b>:").arg(basketName()), false); // Used when decrypting
5311 return m_gpg
->decrypt(tmp
, array
);
5324 bool Basket::saveToFile(const QString
& fullPath
, const QString
& string
, bool isLocalEncoding
)
5326 Q3CString bytes
= (isLocalEncoding
? string
.local8Bit() : string
.utf8());
5327 return saveToFile(fullPath
, bytes
, bytes
.length());
5330 bool Basket::saveToFile(const QString
& fullPath
, const QByteArray
& array
)
5332 return saveToFile(fullPath
, array
, array
.size());
5335 bool Basket::saveToFile(const QString
& fullPath
, const QByteArray
& array
, Q_ULONG length
)
5337 bool success
= true;
5340 #ifdef HAVE_LIBGPGME
5343 QString key
= QString::null
;
5345 // We only use gpg-agent for private key encryption and saving without
5346 // public key doesn't need one.
5347 m_gpg
->setUseGnuPGAgent(false);
5348 if(m_encryptionType
== PrivateKeyEncryption
)
5350 key
= m_encryptionKey
;
5351 // public key doesn't need password
5352 m_gpg
->setText("", false);
5355 m_gpg
->setText(i18n("Please assign a password to the basket <b>%1</b>:").arg(basketName()), true); // Used when defining a new password
5357 success
= m_gpg
->encrypt(array
, length
, &tmp
, key
);
5358 length
= tmp
.size();
5364 success
= !isEncrypted();
5368 /*if (success && (success = file.open(QIODevice::WriteOnly))){
5369 success = (file.write(tmp) == (Q_LONG)tmp.size());
5374 return safelySaveToFile(fullPath
, tmp
, length
);
5379 /** Same as saveToFile(), but it is static, and does not crypt the data if needed.
5380 * Basically, to save a file owned by a basket (a basket or a note file), use saveToFile().
5381 * But to save another file (eg. the basket hierarchy), use this safelySaveToFile() static method.
5383 /*static*/ bool Basket::safelySaveToFile(const QString
& fullPath
, const QByteArray
& array
, Q_ULONG length
)
5385 // Here, we take a double protection:
5386 // - We use KSaveFile to write atomically to the file (either it's a success or the file is untouched)
5387 // - We show a modal dialog to the user when no disk space is left or access is denied and retry every couple of seconds
5389 // Static, because safelySaveToFile() can be called a second time while blocked.
5391 // User type something and press Enter: safelySaveToFile() is called and block.
5392 // Three seconds later, a timer ask to save changes, and this second safelySaveToFile() block too.
5393 // Do not show the dialog twice in this case!
5394 static DiskErrorDialog
*dialog
= 0;
5396 //std::cout << "---------- Saving " << fullPath << ":" << std::endl;
5399 bool errorWhileWritting
;
5401 KSaveFile
saveFile(fullPath
);
5402 //std::cout << "==>>" << std::endl << "SAVE FILE CREATED: " << strerror(saveFile.status()) << std::endl;
5403 openSuccess
= (saveFile
.status() == 0 && saveFile
.file() != 0);
5405 saveFile
.file()->write(array
, length
);
5406 //std::cout << "FILE WRITTEN: " << strerror(saveFile.status()) << std::endl;
5407 closeSuccess
= saveFile
.close();
5408 //std::cout << "FILE CLOSED: " << (closeSuccess ? "well" : "erroneous") << std::endl;
5410 errorWhileWritting
= (!openSuccess
|| !closeSuccess
|| saveFile
.status() != 0);
5411 if (errorWhileWritting
) {
5412 //std::cout << "ERROR DETECTED" << std::endl;
5414 //std::cout << "Opening dialog for " << fullPath << std::endl;
5415 dialog
= new DiskErrorDialog(
5417 ? i18n("Insufficient Disk Space to Save Basket Data")
5418 : i18n("Wrong Basket File Permissions")
5421 ? i18n("Please remove files on the disk <b>%1</b> to let the application safely save your changes.")
5422 .arg(KIO::findPathMountPoint(fullPath
))
5423 : i18n("File permissions are bad for <b>%1</b>. Please check that you have write access to it and the parent folders.")
5426 kapp
->activeWindow()
5429 if (!dialog
->isShown())
5431 const int retryDelay
= 1000/*ms*/;
5432 const int sleepDelay
= 50/*ms*/;
5433 for (int i
= 0; i
< retryDelay
/ sleepDelay
; ++i
) {
5434 kapp
->processEvents();
5438 } while (errorWhileWritting
);
5444 return true; // Hum...?!
5447 /*static*/ bool Basket::safelySaveToFile(const QString
& fullPath
, const QString
& string
, bool isLocalEncoding
)
5449 Q3CString bytes
= (isLocalEncoding
? string
.local8Bit() : string
.utf8());
5450 return safelySaveToFile(fullPath
, bytes
, bytes
.length() - 1);
5453 /*static*/ bool Basket::safelySaveToFile(const QString
& fullPath
, const QByteArray
& array
)
5455 return safelySaveToFile(fullPath
, array
, array
.size());
5458 DiskErrorDialog::DiskErrorDialog(const QString
&titleMessage
, const QString
&message
, QWidget
*parent
)
5461 setObjectName("DiskError");
5462 setCaption(i18n("Save Error"));
5463 setMainWidget(new QWidget(this));
5464 //enableButtonCancel(false);
5465 //enableButtonClose(false);
5466 //enableButton(Close, false);
5467 //enableButtonOk(false);
5469 Q3HBoxLayout
*layout
= new Q3HBoxLayout(mainWidget(), /*margin=*/0, spacingHint());
5470 QPixmap icon
= kapp
->iconLoader()->loadIcon("hdd_unmount", KIconLoader::NoGroup
, 64, KIconLoader::DefaultState
, /*path_store=*/0L, /*canReturnNull=*/true);
5471 QLabel
*iconLabel
= new QLabel(mainWidget());
5472 iconLabel
->setPixmap(icon
);
5473 iconLabel
->setFixedSize(iconLabel
->sizeHint());
5474 QLabel
*label
= new QLabel("<p><nobr><b><font size='+1'>" + titleMessage
+ "</font></b></nobr></p><p>" + message
+ "</p>", mainWidget());
5476 layout
->addWidget(iconLabel
);
5477 layout
->addWidget(label
);
5480 DiskErrorDialog::~DiskErrorDialog()
5484 void DiskErrorDialog::closeEvent(QCloseEvent
*event
)
5489 void DiskErrorDialog::keyPressEvent(QKeyEvent
*)
5491 // Escape should not close the window...
5496 #ifdef HAVE_LIBGPGME
5498 m_gpg
->clearCache();
5503 m_loadingLaunched
= false;
5510 #include <qlayout.h>
5512 #include <qstring.h>
5513 #include <qpixmap.h>
5516 #include <kurllabel.h>
5517 #include <qcheckbox.h>
5518 #include <qpalette.h>
5519 #include <qcursor.h>
5520 #include <qaction.h>
5521 #include <kstdaccel.h>
5522 #include <kglobalsettings.h>
5525 #include <kapplication.h>
5526 #include <kaboutdata.h>
5527 #include <qinputdialog.h>
5528 #include <q3dragobject.h>
5529 #include <k3urldrag.h>
5530 #include <kiconloader.h>
5531 #include <klocale.h>
5532 #include <kmimetype.h>
5533 #include <kfiledialog.h>
5535 #include <kiconloader.h>
5536 #include <qregexp.h>
5537 #include <qfileinfo.h>
5539 #include <qstringlist.h>
5543 #include <kmessagebox.h>
5544 #include <kdeversion.h>
5546 #include "kdirwatch.h"
5547 #include <qstringlist.h>
5548 #include <klineedit.h>
5551 #include <qtextcodec.h>
5552 #include <kauthorized.h>
5556 #include "notefactory.h"
5557 #include "variouswidgets.h"
5558 #include "linklabel.h"
5560 #include "container.h"
5561 #include "xmlwork.h"
5562 #include "settings.h"
5563 #include "popupmenu.h"
5564 #include "debugwindow.h"
5565 #include "exporterdialog.h"
5570 const int Basket::c_updateTime
= 200;
5574 // Remove the note from the basket and delete the associated file
5575 // If the note mirror a file, it will ask before deleting or not the file
5576 // But if askForMirroredFile is false, it willn't ask NOR delete the MIRRORED file
5577 // (it will anyway delete the file if it is not a mirror)
5578 void Basket::delNote(Note
*note
, bool askForMirroredFile
)
5582 focusANote(); // We need note->next() and note->previous() here [BUT deleted note should be hidden]
5583 if (note
->isSelected())
5584 note
->setSelected(false); //removeSelectedNote();
5588 resetInsertTo(); // If we delete the first or the last, pointer to it is invalid
5591 if (note
== m_startOfShiftSelectionNote
)
5592 m_startOfShiftSelectionNote
= 0L;
5594 if (isDuringEdit() && m_editor
->editedNote() == note
)
5600 // Calculate where to paste or drop
5601 void Basket::computeInsertPlace(const QPoint
&cursorPosition
)
5603 int y
= cursorPosition
.y();
5605 if (countShown() == 0)
5608 // TODO: Memorize the last hovered note to avoid a new computation on dragMoveEvent !!
5609 // If the mouse is not over the last note, compute which new is :
5610 // TODO: Optimization : start from m_insertAtNote and compare y position to search before or after (or the same)
5611 for (Note
*it
= firstNote(); it
!= 0L; it
= it
->next())
5612 if ( (it
->isShown()) && (it
->y() + it
->height() >= y
) && (it
->y() < y
) ) {
5613 int center
= it
->y() + (it
->height() / 2);
5614 m_insertAtNote
= it
;
5615 m_insertAfter
= y
> center
;
5618 // Else, there is at least one shown note but cursor hover NO note, so we are after the last shown note
5619 m_insertAtNote
= lastShownNote();
5620 m_insertAfter
= true;
5622 // Code for rectangular notes :
5623 /*QRect globalRect = it->rect();
5624 globalRect.moveTopLeft(it->pos() + contentsY());
5625 if ( globalRect.contains(curPos) ) {
5626 it->doInterestingThing();
5630 void Basket::dragMoveEvent(QDragMoveEvent
* event
)
5632 // m_isDuringDrag = true;
5637 // FIXME: viewportToContents does NOT work !!!
5638 // QPoint pos = viewportToContents(event->pos());
5639 QPoint
pos( event
->pos().x() + contentsX(), event
->pos().y() + contentsY() );
5641 // if (insertAtCursorPos())
5642 computeInsertPlace(pos
);
5644 showFrameInsertTo();
5645 acceptDropEvent(event
);
5647 // A workarround since QScrollView::dragAutoScroll seem to have no effect :
5648 ensureVisible(event
->pos().x() + contentsX(), event
->pos().y() + contentsY(), 30, 30);
5649 // QScrollView::dragMoveEvent(event);
5652 void Basket::dropEvent(QDropEvent
*event
)
5654 QPoint pos
= event
->pos();
5655 std::cout
<< "Drop Event at position " << pos
.x() << ":" << pos
.y() << std::endl
;
5656 m_isDuringDrag
= false;
5657 emit
resetStatusBarText();
5662 NoteFactory::dropNote( event
, this, true, event
->action(), dynamic_cast<Note
*>(event
->source()) );
5663 // TODO: need to know if we really inserted an (or several!!!!) note !!!
5664 ensureNoteVisible(lastInsertedNote());
5665 unselectAllBut(lastInsertedNote());
5666 setFocusedNote(lastInsertedNote());
5671 void Basket::moveOnTop()
5673 if (m_countSelecteds
== 0)
5676 Note
*endOfBrowse
= firstShownNote();
5677 Note
*topNote
= firstNote();
5679 for (Note
*it
= lastShownNote(); it
!= 0L; ) {
5680 prev
= it
->previous();
5681 if (it
->isSelected()) {
5682 m_insertAtNote
= topNote
;
5683 m_insertAfter
= false;
5684 changeNotePlace(it
);
5687 if (it
== endOfBrowse
)
5691 ensureNoteVisible(firstShownNote());
5692 ensureNoteVisible(m_focusedNote
);
5695 void Basket::moveOnBottom()
5697 if (m_countSelecteds
== 0)
5700 Note
*endOfBrowse
= lastShownNote();
5701 Note
*bottomNote
= lastNote();
5703 for (Note
*it
= firstShownNote(); it
!= 0L; ) {
5705 if (it
->isSelected()) {
5706 m_insertAtNote
= bottomNote
;
5707 m_insertAfter
= true;
5708 changeNotePlace(it
);
5711 if (it
== endOfBrowse
)
5715 ensureNoteVisible(lastShownNote());
5716 ensureNoteVisible(m_focusedNote
);
5719 void Basket::moveNoteUp()
5721 if (m_countSelecteds
== 0)
5724 // Begin from the top (important move all selected notes one note up
5725 // AND to quit early if a selected note is the first shown one
5726 for (Note
*it
= firstShownNote(); it
!= 0L; it
= it
->next()) {
5727 if (it
->isSelected() && it
->isShown()) { // it->isShown() not necessary, but in case...
5728 if (it
== firstShownNote())
5729 return; // No way...
5730 m_insertAtNote
= nextShownNoteFrom(it
, -1); // Previous shown note
5731 if (m_insertAtNote
== 0L) { // Should not appends, since it's not the first shown note,
5732 resetInsertTo(); // there SHOULD be one before
5735 m_insertAfter
= false;
5736 changeNotePlace(it
);
5738 if (it
== lastShownNote())
5741 ensureNoteVisible(m_focusedNote
);
5744 void Basket::moveNoteDown()
5746 if (m_countSelecteds
== 0)
5749 // Begin from the bottom (important move all selected notes one note down
5750 // AND to quit early if a selected note is the last shown one
5751 for (Note
*it
= lastShownNote(); it
!= 0L; it
= it
->previous()) {
5752 if (it
->isSelected() && it
->isShown()) { // it->isShown() not necessary, but in case...
5753 if (it
== lastShownNote())
5754 return; // No way...
5755 m_insertAtNote
= nextShownNoteFrom(it
, 1); // Next shown note
5756 if (m_insertAtNote
== 0L) { // Should not appends, since it's not the last shown note,
5757 resetInsertTo(); // there SHOULD be one before
5760 m_insertAfter
= true;
5761 changeNotePlace(it
);
5763 if (it
== firstShownNote())
5766 ensureNoteVisible(m_focusedNote
);
5771 #include "basket.moc"