3 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #define DEBUG_KP_TOOL_SELECTION 0
32 #include <kpAbstractSelectionTool.h>
33 #include <kpAbstractSelectionToolPrivate.h>
42 #include <kpAbstractSelection.h>
43 #include <kpAbstractSelectionContentCommand.h>
45 #include <kpCommandHistory.h>
47 #include <kpDocument.h>
48 #include <kpMacroCommand.h>
49 #include <kpToolSelectionCreateCommand.h>
50 #include <kpToolSelectionEnvironment.h>
51 #include <kpToolToolBar.h>
52 #include <kpToolWidgetOpaqueOrTransparent.h>
54 #include <kpViewManager.h>
57 // For either of these timers, they are only active during the "drawing" phase
59 static void AssertAllTimersInactive (struct kpAbstractSelectionToolPrivate
*d
)
61 Q_ASSERT (!d
->createNOPTimer
->isActive ());
62 Q_ASSERT (!d
->RMBMoveUpdateGUITimer
->isActive ());
66 kpAbstractSelectionTool::kpAbstractSelectionTool (
68 const QString
&description
,
70 kpToolSelectionEnvironment
*environ
, QObject
*parent
,
73 : kpTool (text
, description
, key
, environ
, parent
, name
),
74 d (new kpAbstractSelectionToolPrivate ())
77 d
->currentSelContentCommand
= 0;
80 // d->hadSelectionBeforeDrag
82 // d->cancelledShapeButStillHoldingButtons
84 d
->toolWidgetOpaqueOrTransparent
= 0;
91 // It would be bad practice to have timers ticking even when this tool
93 ::AssertAllTimersInactive (d
);
96 kpAbstractSelectionTool::~kpAbstractSelectionTool ()
100 uninitResizeScale ();
103 // (state must be after construction, or after some time after end())
104 Q_ASSERT (d
->drawType
== None
);
105 Q_ASSERT (!d
->currentSelContentCommand
);
108 // d->hadSelectionBeforeDraw
110 // d->cancelledShapeButStillHoldingButtons
112 // d->toolWidgetOpaqueOrTransparent
120 kpAbstractSelectionTool::DrawType
kpAbstractSelectionTool::drawType () const
126 bool kpAbstractSelectionTool::hadSelectionBeforeDraw () const
128 return d
->hadSelectionBeforeDraw
;
132 // protected overrides [base kpTool]
133 kpToolSelectionEnvironment
*kpAbstractSelectionTool::environ () const
135 kpToolEnvironment
*e
= kpTool::environ ();
136 Q_ASSERT (dynamic_cast <kpToolSelectionEnvironment
*> (e
));
137 return static_cast <kpToolSelectionEnvironment
*> (e
);
142 bool kpAbstractSelectionTool::controlOrShiftPressed () const
144 return (controlPressed () || shiftPressed ());
149 void kpAbstractSelectionTool::pushOntoDocument ()
151 #if DEBUG_KP_TOOL_SELECTION && 1
152 kDebug () << "kpAbstractSelectionTool::pushOntoDocument() selection="
153 << document ()->selection () << endl
;
155 Q_ASSERT (document ()->selection ());
156 environ ()->deselectSelection ();
161 void kpAbstractSelectionTool::giveContentIfNeeded ()
163 kpAbstractSelection
*sel
= document ()->selection ();
166 if (sel
->hasContent ())
169 if (d
->currentSelContentCommand
)
172 d
->currentSelContentCommand
= /*virtual*/newGiveContentCommand ();
173 d
->currentSelContentCommand
->execute ();
177 // REFACTOR: sync: Code dup with kpMainWindow::addImageOrSelectionCommand ().
178 void kpAbstractSelectionTool::addNeedingContentCommand (kpCommand
*cmd
)
182 // Did we fill the selection with content?
183 if (d
->currentSelContentCommand
)
185 // Make the border creation a command.
186 #if DEBUG_KP_TOOL_SELECTION
187 kDebug () << "\thave currentSelContentCommand";
189 commandHistory ()->addCreateSelectionCommand (
190 new kpToolSelectionCreateCommand (
191 /*virtual*/nameOfCreateCommand (),
192 *d
->currentSelContentCommand
->originalSelection (),
193 environ ()->commandEnvironment ()),
194 false/*no exec - user already dragged out sel*/);
197 // Do we have a content setting command we need to commit?
198 // (yes, this is the same check as the previous "if")
199 if (d
->currentSelContentCommand
)
201 // Put the content command + given command (e.g. movement) together
202 // as a macro command, in the command history.
203 kpMacroCommand
*macroCmd
= new kpMacroCommand (
204 cmd
->name (), environ ()->commandEnvironment ());
206 macroCmd
->addCommand (d
->currentSelContentCommand
);
207 d
->currentSelContentCommand
= 0;
209 macroCmd
->addCommand (cmd
);
211 commandHistory ()->addCommand (macroCmd
, false/*no exec*/);
215 // Put the given command into the command history.
216 commandHistory ()->addCommand (cmd
, false/*no exec*/);
222 void kpAbstractSelectionTool::setSelectionBorderForHaventBegunDraw ()
224 viewManager ()->setQueueUpdates ();
226 viewManager ()->setSelectionBorderVisible (true);
227 viewManager ()->setSelectionBorderFinished (true);
229 viewManager ()->restoreQueueUpdates ();
233 QString
kpAbstractSelectionTool::haventBegunDrawUserMessage ()
235 #if DEBUG_KP_TOOL_SELECTION && 0
236 kDebug () << "kpAbstractSelectionTool::haventBegunDrawUserMessage()"
237 " cancelledShapeButStillHoldingButtons="
238 << d
->cancelledShapeButStillHoldingButtons
242 if (d
->cancelledShapeButStillHoldingButtons
)
243 return i18n ("Let go of all the mouse buttons.");
245 return operation (calculateDrawType (), HaventBegunDrawUserMessage
).toString ();
249 // public virtual [base kpTool]
250 void kpAbstractSelectionTool::begin ()
252 #if DEBUG_KP_TOOL_SELECTION
253 kDebug () << "kpAbstractSelectionTool<" << objectName () << ">::begin()";
256 ::AssertAllTimersInactive (d
);
259 // (state must be after construction, or after some time after end())
260 Q_ASSERT (d
->drawType
== None
);
261 Q_ASSERT (!d
->currentSelContentCommand
);
263 d
->dragAccepted
= false;
264 // d->hadSelectionBeforeDraw
266 d
->cancelledShapeButStillHoldingButtons
= false;
269 kpToolToolBar
*tb
= toolToolBar ();
272 d
->toolWidgetOpaqueOrTransparent
= tb
->toolWidgetOpaqueOrTransparent ();
273 Q_ASSERT (d
->toolWidgetOpaqueOrTransparent
);
274 connect (d
->toolWidgetOpaqueOrTransparent
, SIGNAL (isOpaqueChanged (bool)),
275 this, SLOT (slotIsOpaqueChanged (bool)));
276 d
->toolWidgetOpaqueOrTransparent
->show ();
278 /*virtual*/setSelectionBorderForHaventBegunDraw ();
286 setUserMessage (haventBegunDrawUserMessage ());
289 // public virtual [base kpTool]
290 void kpAbstractSelectionTool::end ()
292 #if DEBUG_KP_TOOL_SELECTION
293 kDebug () << "kpAbstractSelectionTool<" << objectName () << ">::end()";
296 if (document ()->selection ())
305 // (should have been killed by cancelShape() or endDraw())
306 Q_ASSERT (d
->drawType
== None
);
307 Q_ASSERT (!d
->currentSelContentCommand
);
310 // d->hadSelectionBeforeDraw
312 // d->cancelledShapeButStillHoldingButtons
315 Q_ASSERT (d
->toolWidgetOpaqueOrTransparent
);
316 disconnect (d
->toolWidgetOpaqueOrTransparent
, SIGNAL (isOpaqueChanged (bool)),
317 this, SLOT (slotIsOpaqueChanged (bool)));
318 d
->toolWidgetOpaqueOrTransparent
= 0;
321 viewManager ()->unsetCursor ();
323 ::AssertAllTimersInactive (d
);
327 // public virtual [base kpTool]
328 void kpAbstractSelectionTool::reselect ()
330 #if DEBUG_KP_TOOL_SELECTION
331 kDebug () << "kpAbstractSelectionTool::reselect()";
334 if (document ()->selection ())
340 kpAbstractSelectionTool::DrawType
kpAbstractSelectionTool::calculateDrawTypeInsideSelection () const
342 #if DEBUG_KP_TOOL_SELECTION
343 kDebug () << "\t\tis move";
345 return kpAbstractSelectionTool::Move
;
349 kpAbstractSelectionTool::DrawType
kpAbstractSelectionTool::calculateDrawType () const
351 kpAbstractSelection
*sel
= document ()->selection ();
354 #if DEBUG_KP_TOOL_SELECTION
355 kDebug () << "\thas sel region rect=" << sel
->boundingRect ();
358 if (onSelectionResizeHandle () && !controlOrShiftPressed ())
360 else if (sel
->contains (currentPoint ()))
361 return /*virtual*/calculateDrawTypeInsideSelection ();
366 // public virtual [base kpTool]
367 void kpAbstractSelectionTool::beginDraw ()
369 #if DEBUG_KP_TOOL_SELECTION
370 kDebug () << "kpAbstractSelectionTool::beginDraw() startPoint ()="
372 << " QCursor::pos() view startPoint="
373 << viewUnderStartPoint ()->mapFromGlobal (QCursor::pos ())
377 // endDraw() and cancelShape() should have taken care of these.
378 ::AssertAllTimersInactive (d
);
381 // In case the cursor was wrong to start with
382 // (forgot to call kpTool::somethingBelowTheCursorChanged()),
383 // make sure it is correct during this operation.
384 hover (currentPoint ());
386 // Currently used only to end the current text
387 if (hasBegunShape ())
389 endShape (currentPoint (),
390 kpBug::QRect_Normalized (
391 QRect (startPoint ()/* TODO: wrong */, currentPoint ())));
394 d
->drawType
= calculateDrawType ();
395 d
->dragAccepted
= false;
397 kpAbstractSelection
*sel
= document ()->selection ();
398 d
->hadSelectionBeforeDraw
= bool (sel
);
400 operation (d
->drawType
, BeginDraw
);
404 // public virtual [base kpTool]
405 void kpAbstractSelectionTool::hover (const QPoint
&point
)
407 #if DEBUG_KP_TOOL_SELECTION && 1
408 kDebug () << "kpAbstractSelectionTool::hover" << point
;
411 operation (calculateDrawType (), SetCursor
);
413 setUserShapePoints (point
, KP_INVALID_POINT
, false/*don't set size*/);
414 if (document () && document ()->selection ())
416 setUserShapeSize (document ()->selection ()->width (),
417 document ()->selection ()->height ());
421 setUserShapeSize (KP_INVALID_SIZE
);
424 QString mess
= haventBegunDrawUserMessage ();
425 if (mess
!= userMessage ())
426 setUserMessage (mess
);
430 // public virtual [base kpTool]
431 void kpAbstractSelectionTool::draw (const QPoint
&thisPoint
, const QPoint
& /*lastPoint*/,
432 const QRect
&normalizedRect
)
434 #if DEBUG_KP_TOOL_SELECTION && 1
435 kDebug () << "kpAbstractSelectionTool::draw (" << thisPoint
436 << ",startPoint=" << startPoint ()
437 << ",normalizedRect=" << normalizedRect
<< ")" << endl
;
439 Q_UNUSED (thisPoint
);
440 Q_UNUSED (normalizedRect
);
444 // OPT: return when thisPoint == lastPoint () so that e.g. when creating
445 // Points sel, press modifiers doesn't add multiple points in same
449 operation (d
->drawType
, Draw
);
453 // public virtual [base kpTool]
454 void kpAbstractSelectionTool::cancelShape ()
456 #if DEBUG_KP_TOOL_SELECTION
457 kDebug () << "kpAbstractSelectionTool::cancelShape() mouseButton="
458 << mouseButton () << endl
;
461 const DrawType oldDrawType
= d
->drawType
;
462 // kpTool::hasBegunDraw() returns false in this method so be consistent
463 // and clear "drawType" before dispatching the operation() below.
467 viewManager ()->setQueueUpdates ();
469 operation (oldDrawType
, Cancel
);
472 if (d
->currentSelContentCommand
)
474 #if DEBUG_KP_TOOL_SELECTION
475 kDebug () << "\t\tundo sel content";
477 d
->currentSelContentCommand
->unexecute ();
478 delete d
->currentSelContentCommand
;
479 d
->currentSelContentCommand
= 0;
483 /*virtual*/setSelectionBorderForHaventBegunDraw ();
485 viewManager ()->restoreQueueUpdates ();
488 d
->cancelledShapeButStillHoldingButtons
= true;
489 setUserMessage (i18n ("Let go of all the mouse buttons."));
492 ::AssertAllTimersInactive (d
);
495 // public virtual [base kpTool]
496 void kpAbstractSelectionTool::releasedAllButtons ()
498 d
->cancelledShapeButStillHoldingButtons
= false;
499 setUserMessage (haventBegunDrawUserMessage ());
504 void kpAbstractSelectionTool::popupRMBMenu ()
506 #if DEBUG_KP_TOOL_SELECTION
507 kDebug () << "CALL - exec'ing menu";
510 QMenu
*pop
= environ ()->selectionToolRMBMenu ();
513 // Blocks until the menu closes.
514 // WARNING: Enters event loop - may re-enter view/tool event handlers.
515 pop
->exec (QCursor::pos ());
516 #if DEBUG_KP_TOOL_SELECTION
517 kDebug () << "calling somethingBelowTheCursorChanged()";
520 // Cursor may have moved while the menu was up, triggering QMouseMoveEvents
521 // for the menu -- but not the view -- so we may have missed cursor moves.
522 // Update cursor position now.
523 somethingBelowTheCursorChanged ();
524 #if DEBUG_KP_TOOL_SELECTION
529 // public virtual [base kpTool]
530 void kpAbstractSelectionTool::endDraw (const QPoint
& /*thisPoint*/,
531 const QRect
& /*normalizedRect*/)
533 #if DEBUG_KP_TOOL_SELECTION
534 kDebug () << "kpAbstractSelectionTool::endDraw()";
537 const DrawType oldDrawType
= d
->drawType
;
538 // kpTool::hasBegunDraw() returns false in this method so be consistent
539 // and clear "drawType" before dispatching the operation() below.
543 viewManager ()->setQueueUpdates ();
545 operation (oldDrawType
, EndDraw
);
547 /*virtual*/setSelectionBorderForHaventBegunDraw ();
549 viewManager ()->restoreQueueUpdates ();
552 setUserMessage (haventBegunDrawUserMessage ());
555 ::AssertAllTimersInactive (d
);
558 if (mouseButton () == 1/*right*/)
562 // WARNING: Do not place any code after the popupRMBMenu() call
563 // (see the popupRMBMenu() API).
568 QVariant
kpAbstractSelectionTool::operation (DrawType drawType
, Operation op
,
569 const QVariant
&data1
, const QVariant
&data2
)
578 return operationCreate (op
, data1
, data2
);
581 return operationMove (op
, data1
, data2
);
584 return operationResizeScale (op
, data1
, data2
);
587 Q_ASSERT (!"Unhandled drag type");
593 #include <kpAbstractSelectionTool.moc>