karbon

vgradienttool.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2001, 2002, 2003 The Karbon Developers
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20#include <tqcursor.h>
21#include <tqlabel.h>
22
23#include <tdelocale.h>
24
25#include <karbon_part.h>
26#include <karbon_view.h>
27#include <karbon_factory.h>
28#include <render/vpainter.h>
29#include <render/vpainterfactory.h>
30#include "vgradienttool.h"
31#include <widgets/vgradienttabwidget.h>
32#include <commands/vfillcmd.h>
33#include <commands/vstrokecmd.h>
34#include <core/vstroke.h>
35#include <core/vselection.h>
36#include <widgets/vstrokefillpreview.h>
37
38#include <kdebug.h>
39
40VGradientTool::VGradientOptionsWidget::VGradientOptionsWidget( VGradient& gradient )
41 : KDialogBase( 0L, "", true, i18n( "Edit Gradient" ), Ok | Cancel )
42{
43 m_gradientWidget = new VGradientTabWidget( gradient, KarbonFactory::rServer(), this );
44 setMainWidget( m_gradientWidget );
45 setFixedSize( baseSize() );
46}
47
48VGradientTool::VGradientTool( KarbonView *view )
49 : VTool( view, "gradienttool" ), m_state( normal ), m_handleSize( 3 ), m_active( false )
50{
51 setName( "tool_gradient" );
52 m_optionsWidget = new VGradientOptionsWidget( m_gradient );
53 registerTool( this );
54}
55
56VGradientTool::~VGradientTool()
57{
58 delete m_optionsWidget;
59}
60
61void
63{
64 m_active = true;
65 m_state = normal;
66 view()->statusMessage()->setText( i18n( "Gradient" ) );
67 view()->setCursor( TQCursor( TQt::crossCursor ) );
69
70 if( view() )
71 {
72 // disable selection handles
73 view()->part()->document().selection()->showHandle( false );
74 // connect to the stroke-fill-preview to get notice when the stroke or fill gets selected
75 VStrokeFillPreview* preview = view()->strokeFillPreview();
76 if( preview )
77 {
78 connect( preview, TQ_SIGNAL( fillSelected() ), this, TQ_SLOT( targetChanged() ) );
79 connect( preview, TQ_SIGNAL( strokeSelected() ), this, TQ_SLOT( targetChanged() ) );
80 }
81 view()->repaintAll( view()->part()->document().selection()->boundingBox() );
82 }
83}
84
85void
87{
88 m_active = false;
89
90 if( view() )
91 {
92 // enable selection handles
93 view()->part()->document().selection()->showHandle( true );
94 VStrokeFillPreview* preview = view()->strokeFillPreview();
95 if( preview )
96 {
97 disconnect( preview, TQ_SIGNAL( fillSelected() ), this, TQ_SLOT( targetChanged() ) );
98 disconnect( preview, TQ_SIGNAL( strokeSelected() ), this, TQ_SLOT( targetChanged() ) );
99 }
100 view()->repaintAll( view()->part()->document().selection()->boundingBox() );
101 }
102}
103
104TQString
106{
107 return i18n( "Gradient Tool" );
108}
109
110TQString
112{
113 TQString s = i18n( "<qt><b>Gradient tool:</b><br>" );
114 s += i18n( "<i>Click and drag</i> to choose the gradient vector.<br>" );
115 s += i18n( "<i>Click and drag</i> a gradient vector handle to change the gradient vector.<br>" );
116 s += i18n( "<i>Shift click and drag</i> to move the radial gradient focal point.<br>" );
117 s += i18n( "<i>Press i or Shift+i</i> to decrease or increase the handle size.<br>" );
118 s += i18n( "<br><b>Gradient editing:</b><br>" );
119 s += i18n( "<i>Click and drag</i> to move points.<br>" );
120 s += i18n( "<i>Double click</i> on a color point to edit it.<br>" );
121 s += i18n( "<i>Right click</i> on a color point to remove it.</qt>" );
122 return s;
123}
124
125bool
126VGradientTool::getGradient( VGradient &gradient )
127{
128 if( ! view() )
129 return false;
130
131 // determine if stroke of fill is selected for editing
132 VStrokeFillPreview *preview = view()->strokeFillPreview();
133 bool strokeSelected = ( preview && preview->strokeIsSelected() );
134
135 VSelection* selection = view()->part()->document().selection();
136 if( selection->objects().count() != 1 )
137 return false;
138
139 VObject *obj = selection->objects().getFirst();
140 // get the gradient of the first selected object, if any
141 if( strokeSelected && obj->stroke()->type() == VStroke::grad )
142 gradient = obj->stroke()->gradient();
143 else if( ! strokeSelected && obj->fill()->type() == VFill::grad )
144 gradient = obj->fill()->gradient();
145 else
146 return false;
147
148 return true;
149}
150
151bool
152VGradientTool::getOpacity( double &opacity )
153{
154 if( ! view() )
155 return false;
156
157 // determine if stroke of fill is selected for editing
158 VStrokeFillPreview *preview = view()->strokeFillPreview();
159 bool strokeSelected = ( preview && preview->strokeIsSelected() );
160
161 VSelection* selection = view()->part()->document().selection();
162 if( selection->objects().count() != 1 )
163 return false;
164
165 VObject *obj = selection->objects().getFirst();
166 // get the opacity of the first selected object, if any
167 if( strokeSelected && obj->stroke()->type() == VStroke::grad )
168 opacity = obj->stroke()->color().opacity();
169 else if( ! strokeSelected && obj->fill()->type() == VFill::grad )
170 opacity = obj->fill()->color().opacity();
171 else return false;
172
173 return true;
174}
175
176void
177VGradientTool::draw( VPainter* painter )
178{
179 if( ! m_active )
180 return;
181
182 if( m_state != normal )
183 return;
184
185 if( ! getGradient( m_gradient ) )
186 return;
187
188 KoPoint s = m_gradient.origin();
189 KoPoint e = m_gradient.vector();
190 KoPoint f = m_gradient.focalPoint();
191
192 // save the handle rects for later inside testing
193 m_origin = KoRect( s.x()-m_handleSize, s.y()-m_handleSize, 2*m_handleSize, 2*m_handleSize );
194 m_vector = KoRect( e.x()-m_handleSize, e.y()-m_handleSize, 2*m_handleSize, 2*m_handleSize );
195 m_center = KoRect( f.x()-m_handleSize, f.y()-m_handleSize, 2*m_handleSize, 2*m_handleSize );
196
197 painter->setPen( TQt::blue.light() );
198 painter->setBrush( TQt::blue.light() );
199 painter->setRasterOp( TQt::XorROP );
200
201 // draw the gradient vector
202 painter->newPath();
203 painter->moveTo( s );
204 painter->lineTo( e );
205 painter->strokePath();
206
207 if( m_gradient.type() == VGradient::radial )
208 {
209 // draw the focal point cross
210 double size = (double)m_handleSize / view()->zoom();
211 KoPoint focal = m_center.center();
212 KoRect cross = KoRect( focal.x()-3*size, focal.y()-3*size, 6*size, 6*size );
213 painter->newPath();
214 painter->moveTo( cross.topLeft() );
215 painter->lineTo( cross.bottomRight() );
216 painter->strokePath();
217 painter->newPath();
218 painter->moveTo( cross.bottomLeft() );
219 painter->lineTo( cross.topRight() );
220 painter->strokePath();
221 }
222 // draw the handle rects
223 painter->drawNode( m_origin.center(), m_handleSize );
224 painter->drawNode( m_vector.center(), m_handleSize );
225}
226
227void
228VGradientTool::draw()
229{
230 if( ! view() || view()->part()->document().selection()->objects().count() == 0 )
231 return;
232
233 VPainter *painter = view()->painterFactory()->editpainter();
234 painter->setRasterOp( TQt::NotROP );
235
236 painter->setPen( TQt::DotLine );
237 painter->newPath();
238
239 // differentiate between moving a handle and creating a complete new vector
240 if( m_state == moveOrigin || m_state == moveVector )
241 {
242 painter->moveTo( m_fixed );
243 painter->lineTo( m_current );
244 // draw the handle rects
245 painter->drawNode( m_fixed, m_handleSize );
246 painter->drawNode( m_current, m_handleSize );
247 }
248 else if( m_state == createNew )
249 {
250 painter->moveTo( first() );
251 painter->lineTo( m_current );
252 // draw the handle rects
253 painter->drawNode( first(), m_handleSize );
254 painter->drawNode( m_current, m_handleSize );
255 }
256 else if( m_state == moveCenter )
257 {
258 // draw the focal point cross
259 double size = (double)m_handleSize / view()->zoom();
260 KoRect cross = KoRect( m_current.x()-3*size, m_current.y()-3*size, 6*size, 6*size );
261 painter->moveTo( cross.topLeft() );
262 painter->lineTo( cross.bottomRight() );
263 painter->strokePath();
264 painter->newPath();
265 painter->moveTo( cross.bottomLeft() );
266 painter->lineTo( cross.topRight() );
267 }
268
269 painter->strokePath();
270}
271
272void
274{
275 if( m_state == normal )
276 return;
277
278 // undo old line
279 draw();
280
281 m_current = last();
282
283 draw();
284}
285
286void
288{
289 m_current = first();
290
291 // set the apropriate editing state
292 if( m_center.contains( m_current ) && shiftPressed())
293 {
294 m_state = moveCenter;
295 }
296 else if( m_origin.contains( m_current ) )
297 {
298 m_state = moveOrigin;
299 m_fixed = m_vector.center();
300 }
301 else if( m_vector.contains( m_current ) )
302 {
303 m_state = moveVector;
304 m_fixed = m_origin.center();
305 }
306 else
307 m_state = createNew;
308}
309
310void
312{
313 m_state = normal;
314
315 if( ! view() || view()->part()->document().selection()->objects().count() == 0 )
316 return;
317
318 // save old gradient position
319 VGradient oldGradient = m_gradient;
320
321 bool strokeSelected = false;
322
323 // determine the target from the stroke-fill-preview-widget
324 VStrokeFillPreview* preview = view()->strokeFillPreview();
325 if( preview && preview->strokeIsSelected() )
326 strokeSelected = true;
327
328 if( first() == last() )
329 {
330 m_optionsWidget->gradientWidget()->setGradient( m_gradient );
331 if( strokeSelected )
332 {
333 m_optionsWidget->gradientWidget()->setTarget( VGradientTabWidget::STROKE );
334 m_optionsWidget->gradientWidget()->setOpacity( 1.0 );
335 }
336 else
337 {
338 m_optionsWidget->gradientWidget()->setTarget( VGradientTabWidget::FILL );
339 double opacity;
340 if( getOpacity( opacity ) )
341 m_optionsWidget->gradientWidget()->setOpacity( opacity );
342 }
343
344 if( ! showDialog() )
345 return;
346
347 m_gradient = m_optionsWidget->gradientWidget()->gradient();
348
349 // if the gradient dialog was shown and accepted, determine the target from the dialog
350 strokeSelected = ( m_optionsWidget->gradientWidget()->target() == VGradientTabWidget::STROKE );
351 }
352
353 // calculate a sane intial position for the new gradient
354 if( view()->part()->document().selection()->objects().count() == 1 )
355 {
356 VObject *obj = view()->part()->document().selection()->objects().getFirst();
357
358 if( ( ! strokeSelected && obj->fill()->type() != VFill::grad )
359 || ( strokeSelected && obj->stroke()->type() != VStroke::grad ) )
360 {
361 KoRect bbox = obj->boundingBox();
362 switch( m_gradient.type() )
363 {
364 case VGradient::linear:
365 oldGradient.setOrigin( bbox.bottomLeft() + 0.5*(bbox.bottomRight()-bbox.bottomLeft()) );
366 oldGradient.setVector( bbox.topLeft() + 0.5*(bbox.topRight()-bbox.topLeft()) );
367 oldGradient.setFocalPoint( bbox.center() );
368 break;
369 case VGradient::radial:
370 oldGradient.setOrigin( bbox.center() );
371 oldGradient.setVector( bbox.topLeft() + 0.5*(bbox.topRight()-bbox.topLeft()) );
372 oldGradient.setFocalPoint( bbox.center() );
373 break;
374 case VGradient::conic:
375 oldGradient.setOrigin( bbox.center() );
376 oldGradient.setVector( bbox.topLeft() + 0.5*(bbox.topRight()-bbox.topLeft()) );
377 oldGradient.setFocalPoint( bbox.center() );
378 break;
379 }
380 }
381 }
382
383 // workaround for a libart 2.3.10 bug
384 if( oldGradient.origin() == oldGradient.vector() )
385 oldGradient.vector().rx()+=1;
386
387 // use the old gradient position
388 m_gradient.setVector( oldGradient.vector() );
389 m_gradient.setOrigin( oldGradient.origin() );
390 m_gradient.setFocalPoint( oldGradient.focalPoint() );
391
392 if( ! strokeSelected )
393 {
394 VFill fill;
395 fill.gradient() = m_gradient;
396 fill.setType( VFill::grad );
397 VColor c = fill.color();
398 c.setOpacity( m_optionsWidget->gradientWidget()->opacity() );
399 fill.setColor( c, false );
400 view()->part()->addCommand(
401 new VFillCmd( &view()->part()->document(), fill, "14_gradient" ), true );
402 }
403 else
404 {
405 view()->part()->addCommand(
406 new VStrokeCmd( &view()->part()->document(), &m_gradient ), true );
407 }
408}
409
410void
412{
413 if( ! view() || m_state == normal )
414 return;
415
416 if( view()->part()->document().selection()->objects().count() == 0 )
417 {
418 draw();
419 return;
420 }
421
422 if( m_state == moveOrigin )
423 m_gradient.setOrigin( last() );
424 else if( m_state == moveVector )
425 m_gradient.setVector( last() );
426 else if( m_state == moveCenter )
427 m_gradient.setFocalPoint( last() );
428 else if( m_state == createNew )
429 {
430 m_gradient.setOrigin( first() );
431 m_gradient.setFocalPoint( first() );
432 m_gradient.setVector( last() );
433 }
434
435 m_state = normal;
436
437 VStrokeFillPreview* preview = view()->strokeFillPreview();
438 if( ! preview )
439 return;
440
441 if( ! preview->strokeIsSelected() )
442 {
443 VFill fill;
444 fill.gradient() = m_gradient;
445 fill.setType( VFill::grad );
446 VColor c = fill.color();
447 c.setOpacity( m_optionsWidget->gradientWidget()->opacity() );
448 fill.setColor( c, false );
449 view()->part()->addCommand(
450 new VFillCmd( &view()->part()->document(), fill, "14_gradient" ), true );
451 }
452 else
453 view()->part()->addCommand(
454 new VStrokeCmd( &view()->part()->document(), &m_gradient ), true );
455}
456
457void
459{
460 // Erase old object:
461 if( isDragging() )
462 draw();
463 m_state = normal;
464}
465
466bool
468{
469 return m_optionsWidget->exec() == TQDialog::Accepted;
470}
471
472void
473VGradientTool::setup( TDEActionCollection *collection )
474{
475 m_action = static_cast<TDERadioAction *>(collection -> action( name() ) );
476
477 if( m_action == 0 )
478 {
479 m_action = new TDERadioAction( i18n( "Gradient Tool" ), "14_gradient", TQt::Key_G, this, TQ_SLOT( activate() ), collection, name() );
480 m_action->setToolTip( i18n( "Gradient" ) );
481 m_action->setExclusiveGroup( "misc" );
482 //m_ownAction = true;
483 }
484}
485
486void
488{
489 if( !view() ) return;
490
491 // set a different cursor if mouse is inside the handle rects
492 if( m_origin.contains( last() ) || m_vector.contains( last() ) || m_center.contains( last() ) )
493 view()->setCursor( TQCursor( TQt::SizeAllCursor ) );
494 else
495 view()->setCursor( TQCursor( TQt::arrowCursor ) );
496}
497
498bool
499VGradientTool::keyReleased( TQt::Key key )
500{
501 // increase/decrease the handle size
502 switch( key )
503 {
504 case TQt::Key_I:
505 if( shiftPressed() )
506 m_handleSize++;
507 else if( m_handleSize > 3 )
508 m_handleSize--;
509 break;
510 default: return false;
511 }
512
513 if( view() )
514 view()->repaintAll( view()->part()->document().selection()->boundingBox() );
515
516 return true;
517}
518
519void
520VGradientTool::targetChanged()
521{
522 if( view() )
523 view()->repaintAll( view()->part()->document().selection()->boundingBox() );
524}
525
526#include "vgradienttool.moc"
void addCommand(VCommand *cmd, bool repaint=false)
insert a command into the undo/redo-history:
void setOpacity(float opacity)
Sets the color opacity.
Definition vcolor.h:138
float opacity() const
Returns the color opacity.
Definition vcolor.h:131
VSelection * selection() const
Returns a pointer to the selection.
Definition vdocument.h:238
@ grad
solid fill
Definition vfill.h:51
virtual const KoRect & boundingBox() const
Calculates the tightest bounding box around the object.
Definition vobject.h:111
virtual VFill * fill() const
Gets the object's actual fill.
Definition vobject.h:177
virtual VStroke * stroke() const
Gets the object's actual stroke.
Definition vobject.h:170
const VObjectList & objects() const
Read only access to the selected objects.
Definition vselection.h:136
void showHandle(bool handle=true)
Toggle selection handles on/off.
Definition vselection.h:166
@ grad
solid stroke
Definition vstroke.h:55
The base class for all karbon tools.
Definition vtool.h:52
virtual void mouseDragRelease()
Mouse button release.
Definition vtool.h:220
virtual void mouseButtonPress()
Left mouse button press.
Definition vtool.h:185
const KoPoint & last() const
The last mouse coordinate.
Definition vtool.h:285
const KoPoint & first() const
Most tools need to know the first mouse coordinate.
Definition vtool.h:280
virtual void mouseButtonRelease()
Left mouse button release.
Definition vtool.h:195
TDERadioAction * action() const
Returns the tool's associated action object.
Definition vtool.h:151
virtual void cancel()
Cancels all tool operations.
Definition vtool.h:255
virtual TQString statusText()
The tool status text.
Definition vtool.h:108
virtual void deactivate()
Deactivates the tool.
Definition vtool.h:83
virtual void activate()
Called during the tool activation.
Definition vtool.cpp:252
bool shiftPressed() const
The status of the shift key.
Definition vtool.h:292
TDERadioAction * m_action
The tool's action object.
Definition vtool.h:309
bool isDragging() const
Returns the current dagging state.
Definition vtool.h:173
virtual TQString contextHelp()
The context help of the tool.
Definition vtool.h:98
virtual void setCursor() const
This function is called on each mouse event the tool receives.
Definition vtool.h:180
virtual void mouseDrag()
Mouse drag.
Definition vtool.h:215
KarbonView * view() const
Returns the connected karbon part.
Definition vtool.cpp:66
virtual bool showDialog() const
Shows the tools option dialog.
Definition vtool.h:88