1/***************************************************************************
2 klineal.cpp - description
3 -------------------
4 Begin : Fri Oct 13 2000
5 Copyright : 2000 by Till Krech <till@snafu.de>
6 2008 by Mathias Soeken <msoeken@informatik.uni-bremen.de>
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "klineal.h"
19
20#include <QBitmap>
21#include <QBrush>
22#include <QClipboard>
23#include <QPainter>
24#include <QMouseEvent>
25#include <QSlider>
26#include <QToolButton>
27#include <QWidgetAction>
28
29#include <KAction>
30#include <KActionCollection>
31#include <KColorDialog>
32#include <KConfig>
33#include <KConfigDialog>
34#include <KGlobalSettings>
35#include <KHelpMenu>
36#include <KInputDialog>
37#include <KLocale>
38#include <KMenu>
39#include <KNotification>
40#include <KShortcutsDialog>
41#include <KStandardAction>
42#include <KSystemTrayIcon>
43#include <KToolInvocation>
44#include <KWindowSystem>
45#include <KApplication>
46
47#include <netwm.h>
48
49#include "kruler.h"
50#include "krulersystemtray.h"
51#include "qautosizelabel.h"
52
53#include "ui_cfg_appearance.h"
54#include "ui_cfg_advanced.h"
55
56/**
57 * this is our cursor bitmap:
58 * a line 48 pixels long with an arrow pointing down
59 * and a sqare with a one pixel hole at the top (end)
60 */
61static const uchar cursorBits[] = {
62 0x38, 0x28, 0x38, 0x10, 0x10, 0x10, 0x10, 0x10,
63 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
64 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
65 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
66 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
67 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x10,
68};
69
70/**
71 * create the thingy with no borders and set up
72 * its members
73 */
74KLineal::KLineal( QWidget *parent )
75 : QWidget( parent ),
76 mDragging( false ),
77 mShortEdgeLen( 70 ),
78 mCloseAction( 0 ),
79 mLenMenu( 0 ), // INFO This member could be eventually deleted
80 // since if mFullScreenAction is initialized
81 // mLenMenu should have been, too.
82 mFullScreenAction( 0 ),
83 mScaleDirectionAction( 0 ),
84 mCenterOriginAction( 0 ),
85 mOffsetAction( 0 ),
86 mClicked( false ),
87 mActionCollection( 0 ),
88 mCloseButton( 0 ),
89 mTrayIcon( 0 )
90{
91 KWindowSystem::setType( winId(), NET::Override ); // or NET::Normal
92 KWindowSystem::setState( winId(), NET::KeepAbove );
93
94 setAttribute( Qt::WA_TranslucentBackground );
95 setWindowFlags( Qt::FramelessWindowHint );
96 setWindowTitle( i18nc( "@title:window", "KRuler" ) );
97
98 setMinimumSize( 60, 60 );
99 setMaximumSize( 8000, 8000 );
100 setWhatsThis( i18n( "This is a tool to measure pixel distances and colors on the screen. "
101 "It is useful for working on layouts of dialogs, web pages etc." ) );
102 setMouseTracking( true );
103
104 QBitmap bim = QBitmap::fromData( QSize( 8, 48 ), cursorBits, QImage::Format_Mono );
105 QMatrix m;
106 m.rotate( 90.0 );
107 mNorthCursor = QCursor( bim, bim, 3, 47 );
108 bim = bim.transformed( m );
109 mEastCursor = QCursor( bim, bim, 0, 3 );
110 bim = bim.transformed( m );
111 mSouthCursor = QCursor( bim, bim, 4, 0 );
112 bim = bim.transformed( m );
113 mWestCursor = QCursor( bim, bim, 47, 4 );
114
115 mCurrentCursor = mNorthCursor;
116 mColor = RulerSettings::self()->bgColor();
117 mScaleFont = RulerSettings::self()->scaleFont();
118 mLongEdgeLen = RulerSettings::self()->length();
119 mOrientation = RulerSettings::self()->orientation();
120 mLeftToRight = RulerSettings::self()->leftToRight();
121 mOffset = RulerSettings::self()->offset();
122 mRelativeScale = RulerSettings::self()->relativeScale();
123
124 mLabel = new QAutoSizeLabel( this );
125 mLabel->setGeometry( 0, height() - 12, 32, 12 );
126 QFont labelFont( KGlobalSettings::generalFont().family(), 10 );
127 labelFont.setPixelSize( 10 );
128 mLabel->setFont( labelFont );
129 mLabel->setWhatsThis( i18n( "This is the current distance measured in pixels." ) );
130 mColorLabel = new QAutoSizeLabel( this );
131 mColorLabel->setAutoFillBackground( true );
132 QFont colorFont( KGlobalSettings::fixedFont().family(), 10 );
133 colorFont.setPixelSize( 10 );
134 mColorLabel->setFont( colorFont );
135 mColorLabel->move( mLabel->pos() + QPoint(0, 20) );
136 mColorLabel->setWhatsThis(i18n("This is the current color in hexadecimal rgb representation"
137 " as you may use it in HTML or as a QColor name. "
138 "The rectangles background shows the color of the pixel inside the "
139 "little square at the end of the line cursor." ) );
140
141 mBtnRotateLeft = new QToolButton( this );
142 mBtnRotateLeft->setIcon( KIcon( QLatin1String( "object-rotate-left" ) ) );
143 mBtnRotateLeft->setToolTip( i18n( "Turn Left" ) );
144 connect( mBtnRotateLeft, SIGNAL(clicked()), this, SLOT(turnLeft()) );
145
146 mBtnRotateRight = new QToolButton( this );
147 mBtnRotateRight->setIcon( KIcon( QLatin1String( "object-rotate-right" ) ) );
148 mBtnRotateRight->setToolTip( i18n( "Turn Right" ) );
149 connect( mBtnRotateRight, SIGNAL(clicked()), this, SLOT(turnRight()) );
150
151 resize( QSize( mLongEdgeLen, mShortEdgeLen ) );
152
153 //BEGIN setup menu and actions
154 mActionCollection = new KActionCollection( this );
155 mActionCollection->setConfigGroup( QLatin1String( "Actions" ) );
156
157 mMenu = new KMenu( this );
158 mMenu->addTitle( i18n( "KRuler" ) );
159 KMenu *oriMenu = new KMenu( i18n( "&Orientation"), this );
160 addAction( oriMenu, KIcon( QLatin1String( "kruler-north" ) ), i18nc( "Turn Kruler North", "&North" ),
161 this, SLOT(setNorth()), Qt::Key_N, QLatin1String( "turn_north" ) );
162 addAction( oriMenu, KIcon( QLatin1String( "kruler-east" ) ), i18nc( "Turn Kruler East", "&East" ),
163 this, SLOT(setEast()), Qt::Key_E, QLatin1String( "turn_east" ) );
164 addAction( oriMenu, KIcon( QLatin1String( "kruler-south" ) ), i18nc( "Turn Kruler South", "&South" ),
165 this, SLOT(setSouth()), Qt::Key_S, QLatin1String( "turn_south" ) );
166 addAction( oriMenu, KIcon( QLatin1String( "kruler-west" ) ), i18nc( "Turn Kruler West", "&West" ),
167 this, SLOT(setWest()), Qt::Key_W, QLatin1String( "turn_west" ) );
168 addAction( oriMenu, KIcon( QLatin1String( "object-rotate-right" ) ), i18n( "&Turn Right" ),
169 this, SLOT(turnRight()), Qt::Key_R, QLatin1String( "turn_right" ) );
170 addAction( oriMenu, KIcon( QLatin1String( "object-rotate-left" ) ), i18n( "Turn &Left" ),
171 this, SLOT(turnLeft()), Qt::Key_L, QLatin1String( "turn_left" ) );
172 mMenu->addMenu( oriMenu );
173
174 mLenMenu = new KMenu( i18n( "&Length" ), this );
175 addAction( mLenMenu, KIcon(), i18nc( "Make Kruler Height Short", "&Short" ),
176 this, SLOT(setShortLength()), Qt::CTRL + Qt::Key_S, QLatin1String( "length_short" ) );
177 addAction( mLenMenu, KIcon(), i18nc( "Make Kruler Height Medium", "&Medium" ),
178 this, SLOT(setMediumLength()), Qt::CTRL + Qt::Key_M, QLatin1String( "length_medium" ) );
179 addAction( mLenMenu, KIcon(), i18nc( "Make Kruler Height Tall", "&Tall" ),
180 this, SLOT(setTallLength()), Qt::CTRL + Qt::Key_T, QLatin1String( "length_tall" ) );
181 addAction( mLenMenu, KIcon(), i18n("&Full Screen Width"),
182 this, SLOT(setFullLength()), Qt::CTRL + Qt::Key_F, QLatin1String( "length_full_length" ) );
183 mLenMenu->addSeparator();
184 addAction( mLenMenu, KIcon(), i18n( "Length..." ),
185 this, SLOT(slotLength()), QKeySequence(), QLatin1String( "set_length" ) );
186 mMenu->addMenu( mLenMenu );
187
188 KMenu* scaleMenu = new KMenu( i18n( "&Scale" ), this );
189 mScaleDirectionAction = addAction( scaleMenu, KIcon(), i18n( "Right to Left" ),
190 this, SLOT(switchDirection()), Qt::Key_D, QLatin1String( "right_to_left" ) );
191 mCenterOriginAction = addAction( scaleMenu, KIcon(), i18n( "Center Origin" ),
192 this, SLOT(centerOrigin()), Qt::Key_C, QLatin1String( "center_origin" ) );
193 mCenterOriginAction->setEnabled( !mRelativeScale );
194 mOffsetAction = addAction( scaleMenu, KIcon(), i18n( "Offset..." ),
195 this, SLOT(slotOffset()), Qt::Key_O, QLatin1String( "set_offset" ) );
196 mOffsetAction->setEnabled( !mRelativeScale );
197 scaleMenu->addSeparator();
198 KAction *relativeScaleAction = addAction( scaleMenu, KIcon(), i18n( "Percentage" ),
199 0, 0, QKeySequence(), QLatin1String( "toggle_percentage" ) );
200 relativeScaleAction->setCheckable( true );
201 relativeScaleAction->setChecked( mRelativeScale );
202 connect( relativeScaleAction, SIGNAL(toggled(bool)), this, SLOT(switchRelativeScale(bool)) );
203 mMenu->addMenu( scaleMenu );
204
205 mOpacity = RulerSettings::self()->opacity();
206 KMenu* opacityMenu = new KMenu( i18n( "O&pacity" ), this );
207 QWidgetAction *opacityAction = new QWidgetAction( this );
208 QSlider *slider = new QSlider( this );
209 slider->setMinimum( 0 );
210 slider->setMaximum( 255 );
211 slider->setSingleStep( 1 );
212 slider->setOrientation( Qt::Horizontal );
213 slider->setValue( RulerSettings::self()->opacity() );
214 connect( slider, SIGNAL(valueChanged(int)), this, SLOT(slotOpacity(int)) );
215 opacityAction->setDefaultWidget( slider );
216 opacityMenu->addAction( opacityAction );
217 mMenu->addMenu( opacityMenu );
218
219 KAction *keyBindings = KStandardAction::keyBindings( this, SLOT(slotKeyBindings()), this );
220 mActionCollection->addAction( QLatin1String( "key_bindings" ), keyBindings );
221 mMenu->addAction( keyBindings );
222 KAction *preferences = KStandardAction::preferences( this, SLOT(slotPreferences()), this );
223 mActionCollection->addAction( QLatin1String( "preferences" ), preferences );
224 mMenu->addAction( preferences );
225 mMenu->addSeparator();
226 KAction *copyColorAction = KStandardAction::copy( this, SLOT(copyColor()), this );
227 copyColorAction->setText( i18n( "Copy Color" ) );
228 mActionCollection->addAction( QLatin1String( "copy_color" ), copyColorAction );
229 mMenu->addAction( copyColorAction );
230 mMenu->addSeparator();
231 mMenu->addMenu( ( new KHelpMenu( this, KGlobal::mainComponent().aboutData(), true ) )->menu() );
232 mMenu->addSeparator();
233 if ( RulerSettings::self()->trayIcon() ) {
234 createSystemTray();
235 }
236
237 KAction *quit = KStandardAction::quit( kapp, SLOT(quit()), this );
238 mActionCollection->addAction( QLatin1String( "quit" ), quit );
239 mMenu->addAction( quit );
240
241 mActionCollection->associateWidget( this );
242 mActionCollection->readSettings();
243
244 mLastClickPos = geometry().topLeft() + QPoint( width() / 2, height() / 2 );
245
246 hideLabel();
247 setOrientation( mOrientation );
248
249}
250
251KLineal::~KLineal()
252{
253 delete mTrayIcon;
254}
255
256void KLineal::createSystemTray()
257{
258 if ( !mCloseAction ) {
259 mCloseAction = KStandardAction::close( this, SLOT(slotClose()), this );
260 mActionCollection->addAction( QLatin1String( "close" ), mCloseAction );
261 mMenu->addAction( mCloseAction );
262
263 mCloseButton = new QToolButton( this );
264 mCloseButton->setIcon( mCloseAction->icon() );
265 mCloseButton->setToolTip( mCloseAction->text().remove( QLatin1Char( '&' ) ) );
266 connect( mCloseButton, SIGNAL(clicked()), this, SLOT(slotClose()) );
267 } else {
268 mCloseAction->setVisible( true );
269 }
270
271 if ( !mTrayIcon ) {
272 mTrayIcon = new KRulerSystemTray( QLatin1String( "kruler" ), this, mActionCollection );
273 mTrayIcon->setCategory( KStatusNotifierItem::ApplicationStatus );
274 }
275}
276
277
278KAction* KLineal::addAction( KMenu *menu, KIcon icon, const QString& text,
279 const QObject* receiver, const char* member,
280 const QKeySequence &shortcut, const QString& name )
281{
282 KAction *action = new KAction( icon, text, mActionCollection );
283 action->setShortcut( shortcut );
284 if ( receiver ) {
285 connect( action, SIGNAL(triggered()), receiver, member );
286 }
287 menu->addAction( action );
288 mActionCollection->addAction( name, action );
289 return action;
290}
291
292void KLineal::slotClose()
293{
294 hide();
295}
296
297void KLineal::slotQuit()
298{
299 kapp->quit();
300}
301
302void KLineal::move( int x, int y )
303{
304 move( QPoint( x, y ) );
305}
306
307void KLineal::move(const QPoint &p)
308{
309 setGeometry( QRect( p, size() ) );
310}
311
312QPoint KLineal::pos() const
313{
314 return frameGeometry().topLeft();
315}
316
317int KLineal::x() const
318{
319 return pos().x();
320}
321
322int KLineal::y() const
323{
324 return pos().y();
325}
326
327void KLineal::drawBackground( QPainter& painter )
328{
329 QColor a, b, bg = mColor;
330 QLinearGradient gradient;
331 switch ( mOrientation ) {
332 case North:
333 a = bg.light( 120 );
334 b = bg.dark( 130 );
335 gradient = QLinearGradient( 1, 0, 1, height() );
336 break;
337
338 case South:
339 b = bg.light( 120 );
340 a = bg.dark( 130 );
341 gradient = QLinearGradient( 1, 0, 1, height() );
342 break;
343
344 case West:
345 a = bg.light( 120 );
346 b = bg.dark( 130 );
347 gradient = QLinearGradient( 0, 1, width(), 1 );
348 break;
349
350 case East:
351 b = bg.light( 120 );
352 a = bg.dark( 130 );
353 gradient = QLinearGradient( 0, 1, width(), 1 );
354 break;
355 }
356 a.setAlpha( mOpacity );
357 b.setAlpha( mOpacity );
358 gradient.setColorAt( 0, a );
359 gradient.setColorAt( 1, b );
360 painter.fillRect( rect(), QBrush( gradient ) );
361}
362
363void KLineal::setOrientation( int inOrientation )
364{
365 QRect r = frameGeometry();
366 int nineties = (int)inOrientation - (int)mOrientation;
367 mOrientation = ( inOrientation + 4 ) % 4;
368 QPoint center = mLastClickPos, newTopLeft;
369
370 if ( mClicked ) {
371 center = mLastClickPos;
372 mClicked = false;
373 } else {
374 center = r.topLeft() + QPoint( width() / 2, height() / 2 );
375 }
376
377 if ( nineties % 2 ) {
378 newTopLeft = QPoint( center.x() - height() / 2, center.y() - width() / 2 );
379 } else {
380 newTopLeft = r.topLeft();
381 }
382
383 if ( mOrientation == North || mOrientation == South ) {
384 r.setSize( QSize( mLongEdgeLen, mShortEdgeLen ) );
385 } else {
386 r.setSize( QSize( mShortEdgeLen, mLongEdgeLen ) );
387 }
388
389 r.moveTo(newTopLeft);
390
391 QRect desktop = KGlobalSettings::desktopGeometry( this );
392
393 if ( r.top() < desktop.top() ) {
394 r.moveTop( desktop.top() );
395 }
396
397 if ( r.bottom() > desktop.bottom() ) {
398 r.moveBottom( desktop.bottom() );
399 }
400
401 if ( r.left() < desktop.left() ) {
402 r.moveLeft( desktop.left() );
403 }
404
405 if ( r.right() > desktop.right() ) {
406 r.moveRight( desktop.right() );
407 }
408
409 setGeometry( r );
410 switch( mOrientation ) {
411 case North:
412 mLabel->move( 4, height()-mLabel->height() - 4 );
413 mColorLabel->move( mLabel->pos() + QPoint( 0, -20 ) );
414 mCurrentCursor = mNorthCursor;
415 break;
416
417 case South:
418 mLabel->move( 4, 4 );
419 mColorLabel->move( mLabel->pos() + QPoint( 0, 20 ) );
420 mCurrentCursor = mSouthCursor;
421 break;
422
423 case East:
424 mLabel->move( 4, 4 );
425 mColorLabel->move( mLabel->pos() + QPoint( 0, 20 ) );
426 mCurrentCursor = mEastCursor;
427 break;
428
429 case West:
430 mLabel->move( width()-mLabel->width() - 4, 4 );
431 mColorLabel->move( mLabel->pos() + QPoint( -5, 20 ) );
432 mCurrentCursor = mWestCursor;
433 break;
434 }
435
436 adjustButtons();
437
438 if ( mLenMenu && mFullScreenAction ) {
439 mFullScreenAction->setText( mOrientation % 2 ? i18n( "&Full Screen Height" ) : i18n( "&Full Screen Width" ) );
440 }
441
442 updateScaleDirectionMenuItem();
443
444 setCursor( mCurrentCursor );
445 repaint();
446 saveSettings();
447}
448
449void KLineal::setNorth()
450{
451 setOrientation( North );
452}
453
454void KLineal::setEast()
455{
456 setOrientation( East );
457}
458
459void KLineal::setSouth()
460{
461 setOrientation( South );
462}
463
464void KLineal::setWest()
465{
466 setOrientation( West );
467}
468
469void KLineal::turnRight()
470{
471 setOrientation( mOrientation - 1 );
472}
473
474void KLineal::turnLeft()
475{
476 setOrientation( mOrientation + 1 );
477}
478
479void KLineal::reLength( int percentOfScreen )
480{
481 if ( percentOfScreen < 10 ) {
482 return;
483 }
484
485 QRect r = KGlobalSettings::desktopGeometry( this );
486
487 if ( mOrientation == North || mOrientation == South ) {
488 mLongEdgeLen = r.width() * percentOfScreen / 100;
489 resize( mLongEdgeLen, height() );
490 } else {
491 mLongEdgeLen = r.height() * percentOfScreen / 100;
492 resize( width(), mLongEdgeLen );
493 }
494
495 if ( x() + width() < 10 ) {
496 move( 10, y() );
497 }
498
499 if ( y() + height() < 10 ) {
500 move( x(), 10 );
501 }
502
503 adjustButtons();
504 saveSettings();
505}
506
507void KLineal::reLengthAbsolute( int length )
508{
509 if ( length < 100 ) {
510 return;
511 }
512
513 mLongEdgeLen = length;
514 if ( mOrientation == North || mOrientation == South ) {
515 resize( mLongEdgeLen, height() );
516 } else {
517 resize( width(), mLongEdgeLen );
518 }
519
520 if ( x() + width() < 10 ) {
521 move( 10, y() );
522 }
523
524 if ( y() + height() < 10 ) {
525 move( x(), 10 );
526 }
527
528 adjustButtons();
529 saveSettings();
530}
531
532void KLineal::updateScaleDirectionMenuItem()
533{
534 if ( !mScaleDirectionAction ) return;
535
536 QString label;
537
538 if ( mOrientation == North || mOrientation == South ) {
539 label = mLeftToRight ? i18n( "Right to Left" ) : i18n( "Left to Right" );
540 } else {
541 label = mLeftToRight ? i18n( "Bottom to Top" ) : i18n( "Top to Bottom" );
542 }
543
544 mScaleDirectionAction->setText( label );
545}
546
547void KLineal::setShortLength()
548{
549 reLength( 30 );
550}
551
552void KLineal::setMediumLength()
553{
554 reLength( 50 );
555}
556
557void KLineal::setTallLength()
558{
559 reLength( 75 );
560}
561
562void KLineal::setFullLength()
563{
564 reLength( 100 );
565}
566
567void KLineal::switchDirection()
568{
569 mLeftToRight = !mLeftToRight;
570 updateScaleDirectionMenuItem();
571 repaint();
572 adjustLabel();
573 saveSettings();
574}
575
576void KLineal::centerOrigin()
577{
578 mOffset = -( mLongEdgeLen / 2 );
579 repaint();
580 adjustLabel();
581 saveSettings();
582}
583
584void KLineal::slotOffset()
585{
586 bool ok;
587 int newOffset = KInputDialog::getInteger( i18nc( "@title:window", "Scale Offset" ),
588 i18n( "Offset:" ), mOffset,
589 -2147483647, 2147483647, 1, &ok, this );
590
591 if ( ok ) {
592 mOffset = newOffset;
593 repaint();
594 adjustLabel();
595 saveSettings();
596 }
597}
598
599void KLineal::slotLength()
600{
601 bool ok;
602 QRect r = KGlobalSettings::desktopGeometry( this );
603 int width = ( ( mOrientation == North ) || ( mOrientation == South ) ) ? r.width() : r.height();
604 int newLength = KInputDialog::getInteger( i18nc( "@title:window", "Ruler Length" ),
605 i18n( "Length:" ), mLongEdgeLen,
606 0, width, 1, &ok, this );
607
608 if ( ok ) {
609 reLengthAbsolute( newLength );
610 }
611}
612
613void KLineal::slotOpacity( int value )
614{
615 mOpacity = value;
616 repaint();
617 RulerSettings::self()->setOpacity( value );
618 RulerSettings::self()->writeConfig();
619}
620
621void KLineal::slotKeyBindings()
622{
623 KShortcutsDialog::configure( mActionCollection );
624}
625
626void KLineal::slotPreferences()
627{
628 KConfigDialog *dialog = new KConfigDialog( this, QLatin1String( "settings" ), RulerSettings::self() );
629
630 Ui::ConfigAppearance appearanceConfig;
631 QWidget *appearanceConfigWidget = new QWidget( dialog );
632 appearanceConfig.setupUi( appearanceConfigWidget );
633 appearanceConfig.kcfg_CloseButtonVisible->setEnabled( appearanceConfig.kcfg_TrayIcon->isChecked() );
634 dialog->addPage( appearanceConfigWidget, i18n( "Appearance" ), QLatin1String( "preferences-desktop-default-applications" ) );
635
636 Ui::ConfigAdvanced advancedConfig;
637 QWidget *advancedConfigWidget = new QWidget( dialog );
638 advancedConfig.setupUi( advancedConfigWidget );
639 dialog->addPage( advancedConfigWidget, i18n( "Advanced" ), QLatin1String( "preferences-other" ) );
640
641 connect( dialog, SIGNAL(settingsChanged(QString)), SLOT(loadConfig()) );
642 dialog->exec();
643 delete dialog;
644}
645
646void KLineal::loadConfig()
647{
648 mColor = RulerSettings::self()->bgColor();
649 mScaleFont = RulerSettings::self()->scaleFont();
650 saveSettings();
651
652 if ( RulerSettings::self()->trayIcon() ) {
653 if ( !mTrayIcon ) {
654 createSystemTray();
655 //need to adjust button
656 adjustButtons();
657 }
658 } else {
659 delete mTrayIcon;
660 mTrayIcon = 0;
661
662 if ( mCloseAction ) {
663 mCloseAction->setVisible( false );
664 }
665 }
666 repaint();
667}
668
669
670void KLineal::switchRelativeScale( bool checked )
671{
672 mRelativeScale = checked;
673
674 mCenterOriginAction->setEnabled( !mRelativeScale );
675 mOffsetAction->setEnabled( !mRelativeScale );
676
677 repaint();
678 adjustLabel();
679 saveSettings();
680}
681
682/**
683 * save the ruler color to the config file
684 */
685void KLineal::saveSettings()
686{
687 RulerSettings::self()->setBgColor( mColor );
688 RulerSettings::self()->setScaleFont( mScaleFont );
689 RulerSettings::self()->setLength( mLongEdgeLen );
690 RulerSettings::self()->setOrientation( mOrientation );
691 RulerSettings::self()->setLeftToRight( mLeftToRight );
692 RulerSettings::self()->setOffset( mOffset );
693 RulerSettings::self()->setRelativeScale( mRelativeScale );
694 RulerSettings::self()->writeConfig();
695}
696
697void KLineal::copyColor()
698{
699 QApplication::clipboard()->setText( mColorLabel->text() );
700}
701
702/**
703 * lets the context menu appear at current cursor position
704 */
705void KLineal::showMenu()
706{
707 QPoint pos = QCursor::pos();
708 mMenu->popup( pos );
709}
710
711/**
712 * overwritten to switch the value label and line cursor on
713 */
714void KLineal::enterEvent( QEvent *inEvent )
715{
716 Q_UNUSED( inEvent );
717
718 if ( !mDragging ) {
719 showLabel();
720 }
721}
722
723/**
724 * overwritten to switch the value label and line cursor off
725 */
726void KLineal::leaveEvent( QEvent *inEvent )
727{
728 Q_UNUSED( inEvent );
729
730 if ( !geometry().contains( QCursor::pos() ) ) {
731 hideLabel();
732 }
733}
734
735/**
736 * shows the value lable
737 */
738void KLineal::showLabel()
739{
740 adjustLabel();
741 mLabel->show();
742 mColorLabel->show();
743 if ( RulerSettings::self()->rotateButtonsVisible() ) {
744 mBtnRotateLeft->show();
745 mBtnRotateRight->show();
746 }
747 if ( mCloseButton &&
748 RulerSettings::self()->closeButtonVisible() &&
749 RulerSettings::self()->trayIcon()) {
750 mCloseButton->show();
751 }
752}
753
754/**
755 * hides the value label
756 */
757void KLineal::hideLabel()
758{
759 mLabel->hide();
760 mColorLabel->hide();
761 mBtnRotateLeft->hide();
762 mBtnRotateRight->hide();
763 if ( mCloseButton ) {
764 mCloseButton->hide();
765 }
766}
767
768/**
769 * updates the current value label
770 */
771void KLineal::adjustLabel()
772{
773 QString s;
774 QPoint cpos = QCursor::pos();
775
776 int digit = ( mOrientation == North || mOrientation == South ) ? cpos.x() - x() : cpos.y() - y();
777
778 if ( !mRelativeScale ) {
779 if ( mLeftToRight ) {
780 digit += mOffset;
781 } else {
782 digit = mLongEdgeLen - digit + mOffset;
783 }
784 } else {
785 // INFO: Perhaps use float also for displaying relative value
786 digit = (int)( ( digit * 100.f ) / mLongEdgeLen );
787
788 if ( !mLeftToRight ) {
789 digit = 100 - digit;
790 }
791 }
792
793 s.sprintf( "%d%s", digit, ( mRelativeScale ? "%" : " px" ) );
794 mLabel->setText( s );
795}
796
797/**
798 * Updates the position of the tool buttons
799 */
800void KLineal::adjustButtons()
801{
802 switch( mOrientation ) {
803 case North:
804 mBtnRotateLeft->move( mLongEdgeLen / 2 - 28, height() - 31 );
805 mBtnRotateRight->move( mLongEdgeLen / 2 + 2, height() - 31 );
806 if ( mCloseButton ) {
807 mCloseButton->move( width() - 31, height() - 31 );
808 }
809 break;
810
811 case South:
812 mBtnRotateLeft->move( mLongEdgeLen / 2 - 28, 5 );
813 mBtnRotateRight->move( mLongEdgeLen / 2 + 2, 5 );
814 if ( mCloseButton ) {
815 mCloseButton->move( width() - 31, 5 );
816 }
817 break;
818
819 case East:
820 mBtnRotateLeft->move( 5, mLongEdgeLen / 2 - 28 );
821 mBtnRotateRight->move( 5, mLongEdgeLen / 2 + 2 );
822 if ( mCloseButton ) {
823 mCloseButton->move( 5, height() - 31 );
824 }
825 break;
826
827 case West:
828 mBtnRotateLeft->move( width() - 31, mLongEdgeLen / 2 - 28 );
829 mBtnRotateRight->move( width() - 31, mLongEdgeLen / 2 + 2 );
830 if ( mCloseButton ) {
831 mCloseButton->move( width() - 31, height() - 31 );
832 }
833 break;
834 }
835}
836
837void KLineal::keyPressEvent( QKeyEvent *e )
838{
839 QPoint dist;
840
841 switch ( e->key() ) {
842 case Qt::Key_F1:
843 KToolInvocation::invokeHelp();
844 return;
845
846 case Qt::Key_Left:
847 dist.setX( -1 );
848 break;
849
850 case Qt::Key_Right:
851 dist.setX( 1 );
852 break;
853
854 case Qt::Key_Up:
855 dist.setY( -1 );
856 break;
857
858 case Qt::Key_Down:
859 dist.setY( 1 );
860 break;
861
862 default:
863 QWidget::keyPressEvent(e);
864 return;
865 }
866
867 if ( e->modifiers() & Qt::ShiftModifier ) {
868 dist *= 10;
869 }
870
871 move( pos() + dist );
872 KNotification::event( QString(), QLatin1String( "cursormove" ), QString() );
873}
874
875/**
876 * overwritten to handle the line cursor which is a separate widget outside the main
877 * window. Also used for dragging.
878 */
879void KLineal::mouseMoveEvent( QMouseEvent *inEvent )
880{
881 Q_UNUSED( inEvent );
882
883 if ( mDragging && this == mouseGrabber() && !RulerSettings::self()->nativeMoving() ) {
884 move( QCursor::pos() - mDragOffset );
885 } else {
886 QPoint p = QCursor::pos();
887
888 switch ( mOrientation ) {
889 case North:
890 p.setY( p.y() - 46 );
891 break;
892
893 case East:
894 p.setX( p.x() + 46 );
895 break;
896
897 case West:
898 p.setX( p.x() - 46 );
899 break;
900
901 case South:
902 p.setY( p.y() + 46 );
903 break;
904 }
905
906 QColor color = KColorDialog::grabColor( p );
907 int h, s, v;
908 color.getHsv( &h, &s, &v );
909 mColorLabel->setText( color.name().toUpper() );
910 QPalette palette = mColorLabel->palette();
911 palette.setColor( mColorLabel->backgroundRole(), color );
912 if ( v < 255 / 2 ) {
913 v = 255;
914 } else {
915 v = 0;
916 }
917 color.setHsv( h, s, v );
918 palette.setColor( mColorLabel->foregroundRole(), color );
919 mColorLabel->setPalette( palette );
920 adjustLabel();
921 }
922}
923
924/**
925 * overwritten for dragging and context menu
926 */
927void KLineal::mousePressEvent( QMouseEvent *inEvent )
928{
929 mLastClickPos = QCursor::pos();
930 hideLabel();
931
932 QRect gr = geometry();
933 mDragOffset = mLastClickPos - QPoint( gr.left(), gr.top() );
934 if ( inEvent->button() == Qt::LeftButton ) {
935#ifdef Q_WS_X11
936 if ( RulerSettings::self()->nativeMoving() ) {
937 XUngrabPointer( QX11Info::display(), QX11Info::appTime() );
938 NETRootInfo wm_root( QX11Info::display(), NET::WMMoveResize );
939 wm_root.moveResizeRequest( winId(), inEvent->globalX(), inEvent->globalY(), NET::Move );
940 } else {
941#endif
942 if ( !mDragging ) {
943 grabMouse( Qt::SizeAllCursor );
944 mDragging = true;
945 }
946#ifdef Q_WS_X11
947 }
948#endif
949 } else if ( inEvent->button() == Qt::MidButton ) {
950 mClicked = true;
951 turnLeft();
952 } else if ( inEvent->button() == Qt::RightButton ) {
953 showMenu();
954 }
955}
956
957/**
958 * overwritten for dragging
959 */
960void KLineal::mouseReleaseEvent( QMouseEvent *inEvent )
961{
962 Q_UNUSED( inEvent );
963
964#ifdef Q_WS_X11
965 if ( RulerSettings::self()->nativeMoving() ) {
966 NETRootInfo wm_root( QX11Info::display(), NET::WMMoveResize );
967 wm_root.moveResizeRequest( winId(), inEvent->globalX(), inEvent->globalY(), NET::MoveResizeCancel );
968 } else {
969#endif
970 if ( mDragging ) {
971 mDragging = false;
972 releaseMouse();
973 }
974#ifdef Q_WS_X11
975 }
976#endif
977
978 showLabel();
979}
980
981void KLineal::wheelEvent( QWheelEvent *e )
982{
983 int numDegrees = e->delta() / 8;
984 int numSteps = numDegrees / 15;
985
986 // changing offset
987 if ( e->buttons() == Qt::LeftButton ) {
988 if ( !mRelativeScale ) {
989 mLabel->show();
990 mOffset += numSteps;
991
992 repaint();
993 mLabel->setText( i18n( "Offset: %1", mOffset ) );
994 saveSettings();
995 }
996 } else { // changing length
997 int oldLen = mLongEdgeLen;
998 int newLength = mLongEdgeLen + numSteps;
999 reLengthAbsolute( newLength );
1000 mLabel->setText( i18n( "Length: %1 px", mLongEdgeLen ) );
1001
1002 // when holding shift relength at the other side
1003 if ( e->modifiers() & Qt::ShiftModifier ) {
1004 int change = mLongEdgeLen - oldLen;
1005
1006 QPoint dist;
1007
1008 if ( mOrientation == North || mOrientation == South ) {
1009 dist.setX( -change );
1010 } else {
1011 dist.setY( -change );
1012 }
1013
1014 move( pos() + dist );
1015 }
1016 }
1017
1018 QWidget::wheelEvent( e );
1019}
1020
1021/**
1022 * draws the scale according to the orientation
1023 */
1024void KLineal::drawScale( QPainter &painter )
1025{
1026 painter.setPen( Qt::black );
1027 QFont font = mScaleFont;
1028 painter.setFont( font );
1029 QFontMetrics metrics = painter.fontMetrics();
1030 int longLen;
1031 int shortStart;
1032 int w = width();
1033 int h = height();
1034
1035 // draw a frame around the whole thing
1036 // (for some unknown reason, this doesn't show up anymore)
1037 switch ( mOrientation ) {
1038 case North:
1039 default:
1040 shortStart = 0;
1041 longLen = w;
1042 painter.drawLine( 0, 0, 0, h - 1 );
1043 painter.drawLine( 0, h - 1, w - 1, h - 1 );
1044 painter.drawLine( w - 1, h - 1, w - 1, 0 );
1045 break;
1046
1047 case East:
1048 shortStart = w;
1049 longLen = h;
1050 painter.drawLine( 0, 0, 0, h - 1 );
1051 painter.drawLine( 0, h - 1, w - 1, h - 1 );
1052 painter.drawLine( w - 1, 0, 0, 0 );
1053 break;
1054
1055 case South:
1056 shortStart = h;
1057 longLen = w;
1058 painter.drawLine( 0, 0, 0, h - 1 );
1059 painter.drawLine( w - 1, h - 1, w - 1, 0 );
1060 painter.drawLine( w - 1, 0, 0, 0 );
1061 break;
1062
1063 case West:
1064 shortStart = 0;
1065 longLen = h;
1066 painter.drawLine( 0, h - 1, w - 1, h - 1 );
1067 painter.drawLine( w - 1, h - 1, w - 1, 0 );
1068 painter.drawLine( w - 1, 0, 0, 0 );
1069 break;
1070 }
1071
1072 if ( !mRelativeScale ) {
1073 int digit;
1074 int len;
1075 for ( int x = 0; x < longLen; ++x ) {
1076 if ( mLeftToRight ) {
1077 digit = x + mOffset;
1078 } else {
1079 digit = longLen - x + mOffset;
1080 }
1081
1082 if ( digit % 2 ) continue;
1083
1084 len = 6;
1085
1086 if ( digit % 10 == 0 ) len = 10;
1087 if ( digit % 20 == 0 ) len = 15;
1088 if ( digit % 100 == 0 ) len = 18;
1089
1090 if ( digit % 20 == 0 ) {
1091 font.setBold( digit % 100 == 0 );
1092 painter.setFont( font );
1093 QString units;
1094 units.sprintf( "%d", digit );
1095 QSize textSize = metrics.size( Qt::TextSingleLine, units );
1096 int tw = textSize.width();
1097 int th = textSize.height();
1098
1099 switch ( mOrientation ) {
1100 case North:
1101 painter.drawText( x - tw / 2, shortStart + len + th, units );
1102 break;
1103
1104 case South:
1105 painter.drawText( x - tw / 2, shortStart - len - 2, units );
1106 break;
1107
1108 case East:
1109 painter.drawText( shortStart - len - tw - 2, x + th / 2 - 2, units );
1110 break;
1111
1112 case West:
1113 painter.drawText( shortStart + len + 2, x + th / 2 - 2, units );
1114 break;
1115 }
1116 }
1117
1118 switch( mOrientation ) {
1119 case North:
1120 painter.drawLine( x, shortStart, x, shortStart + len );
1121 break;
1122 case South:
1123 painter.drawLine( x, shortStart, x, shortStart - len );
1124 break;
1125 case East:
1126 painter.drawLine( shortStart, x, shortStart - len, x );
1127 break;
1128 case West:
1129 painter.drawLine( shortStart, x, shortStart + len, x );
1130 break;
1131 }
1132 }
1133 } else {
1134 float step = longLen / 100.f;
1135 int len;
1136
1137 font.setBold( true );
1138 painter.setFont( font );
1139
1140 for ( int i = 0; i <= 100; ++i ) {
1141 int x = (int)( i * step );
1142 len = ( i % 10 ) ? 6 : 15;
1143
1144 if ( i % 10 == 0 ) {
1145 QString units;
1146 int value = mLeftToRight ? i : ( 100 - i );
1147 units.sprintf( "%d%%", value );
1148 QSize textSize = metrics.size( Qt::TextSingleLine, units );
1149 int tw = textSize.width();
1150 int th = textSize.height();
1151
1152 switch ( mOrientation ) {
1153 case North:
1154 painter.drawText( x - tw / 2, shortStart + len + th, units );
1155 break;
1156
1157 case South:
1158 painter.drawText( x - tw / 2, shortStart - len - 2, units );
1159 break;
1160
1161 case East:
1162 painter.drawText( shortStart - len - tw - 2, x + th / 2 - 2, units );
1163 break;
1164
1165 case West:
1166 painter.drawText( shortStart + len + 2, x + th / 2 - 2, units );
1167 break;
1168 }
1169 }
1170
1171 switch( mOrientation ) {
1172 case North:
1173 painter.drawLine( x, shortStart, x, shortStart + len );
1174 break;
1175 case South:
1176 painter.drawLine( x, shortStart, x, shortStart - len );
1177 break;
1178 case East:
1179 painter.drawLine( shortStart, x, shortStart - len, x );
1180 break;
1181 case West:
1182 painter.drawLine( shortStart, x, shortStart + len, x );
1183 break;
1184 }
1185 }
1186 }
1187}
1188
1189/**
1190 * actually draws the ruler
1191 */
1192void KLineal::paintEvent(QPaintEvent *inEvent )
1193{
1194 Q_UNUSED( inEvent );
1195
1196 QPainter painter( this );
1197 drawBackground( painter );
1198 drawScale( painter );
1199}
1200
1201#include "klineal.moc"
1202