summaryrefslogtreecommitdiffstats
path: root/kig/modes/label.cc
diff options
context:
space:
mode:
Diffstat (limited to 'kig/modes/label.cc')
-rw-r--r--kig/modes/label.cc589
1 files changed, 589 insertions, 0 deletions
diff --git a/kig/modes/label.cc b/kig/modes/label.cc
new file mode 100644
index 00000000..cd726918
--- /dev/null
+++ b/kig/modes/label.cc
@@ -0,0 +1,589 @@
+// Copyright (C) 2002 Dominique Devriese <devriese@kde.org>
+
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301, USA.
+
+#include "label.h"
+#include "normal.h"
+
+#include "textlabelwizard.h"
+#include "linkslabel.h"
+
+#include "../kig/kig_commands.h"
+#include "../kig/kig_document.h"
+#include "../kig/kig_part.h"
+#include "../kig/kig_view.h"
+#include "../misc/common.h"
+#include "../misc/kigpainter.h"
+#include "../objects/bogus_imp.h"
+#include "../objects/curve_imp.h"
+#include "../objects/object_factory.h"
+#include "../objects/point_imp.h"
+#include "../objects/text_imp.h"
+#include "../objects/text_type.h"
+
+#include <kcursor.h>
+#include <kmessagebox.h>
+#include <kaction.h>
+#include <klocale.h>
+#include <qtextedit.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <qregexp.h>
+#include <qpopupmenu.h>
+#include <qcheckbox.h>
+
+#include <algorithm>
+#include <functional>
+
+class TextLabelModeBase::Private
+{
+public:
+ // point last clicked..
+ QPoint plc;
+ // the currently selected coordinate
+ Coordinate mcoord;
+ // the possible parent object that defines the location of the label..
+ ObjectCalcer* locationparent;
+
+ // the text is only kept in the text input widget, not here
+ // QString mtext;
+
+ // the property objects we'll be using as args, we keep a reference
+ // to them in the args object, and keep a pointer to them ( or 0 )
+ // in the correct order in args ( separately, because we can't use
+ // the order of the parents of a ReferenceObject, and certainly
+ // can't give 0 as a parent..
+ argvect args;
+
+ // if we're ReallySelectingArgs, then this var points to the arg
+ // we're currently selecting...
+ int mwaaws;
+
+ // last percent count...
+ uint lpc;
+
+ TextLabelWizard* wiz;
+
+ // What Are We Doing
+ wawdtype mwawd;
+};
+
+TextLabelModeBase::~TextLabelModeBase()
+{
+ delete d->wiz;
+ delete d;
+}
+
+TextLabelModeBase::TextLabelModeBase( KigPart& doc )
+ : KigMode( doc ), d( new Private )
+{
+ d->locationparent = 0;
+ d->lpc = 0;
+ d->mwawd = SelectingLocation;
+ d->wiz = new TextLabelWizard( doc.widget(), this );
+}
+
+void TextLabelModeBase::leftClicked( QMouseEvent* e, KigWidget* )
+{
+ d->plc = e->pos();
+ switch( d->mwawd )
+ {
+ case RequestingText:
+ case SelectingArgs:
+ d->wiz->raise();
+ d->wiz->setActiveWindow();
+ break;
+ default:
+ break;
+ };
+}
+
+void TextLabelModeBase::leftReleased( QMouseEvent* e, KigWidget* v )
+{
+ switch( d->mwawd )
+ {
+ case SelectingLocation:
+ {
+ if ( ( d->plc - e->pos() ).manhattanLength() > 4 ) return;
+ setCoordinate( v->fromScreen( d->plc ) );
+ break;
+ }
+ case RequestingText:
+ case SelectingArgs:
+ d->wiz->raise();
+ d->wiz->setActiveWindow();
+ break;
+ case ReallySelectingArgs:
+ {
+ if ( ( d->plc - e->pos() ).manhattanLength() > 4 ) break;
+ std::vector<ObjectHolder*> os = mdoc.document().whatAmIOn( v->fromScreen( d->plc ), *v );
+ if ( os.empty() ) break;
+ ObjectHolder* o = os[0];
+ QPopupMenu* p = new QPopupMenu( v, "text_label_select_arg_popup" );
+ p->insertItem( i18n( "Name" ), 0 );
+ QCStringList l = o->imp()->properties();
+ assert( l.size() == o->imp()->numberOfProperties() );
+ for ( int i = 0; static_cast<uint>( i ) < l.size(); ++i )
+ {
+ QString s = i18n( l[i] );
+ const char* iconfile = o->imp()->iconForProperty( i );
+ int t;
+ if ( iconfile && *iconfile )
+ {
+ QPixmap pix = mdoc.instance()->iconLoader()->loadIcon( iconfile, KIcon::User );
+ t = p->insertItem( QIconSet( pix ), s, i + 1 );
+ }
+ else
+ {
+ t = p->insertItem( s, i + 1 );
+ };
+ assert( t == i + 1 );
+ };
+ int result = p->exec( v->mapToGlobal( d->plc ) );
+ ObjectCalcer::shared_ptr argcalcer;
+ if ( result == -1 ) break;
+ else if ( result == 0 )
+ {
+ argcalcer = o->nameCalcer();
+ if ( !argcalcer )
+ {
+ ObjectConstCalcer* c = new ObjectConstCalcer( new StringImp( i18n( "<unnamed object>" ) ) );
+ o->setNameCalcer( c );
+ argcalcer = c;
+ }
+ }
+ else
+ {
+ assert( static_cast<uint>( result ) < l.size() + 1 );
+ argcalcer = new ObjectPropertyCalcer( o->calcer(), result - 1 );
+ }
+ d->args[d->mwaaws] = argcalcer.get();
+ argcalcer->calc( mdoc.document() );
+
+ updateLinksLabel();
+ updateWiz();
+ break;
+ }
+ default:
+ assert( false );
+ break;
+ };
+}
+
+void TextLabelModeBase::killMode()
+{
+ mdoc.doneMode( this );
+}
+
+void TextLabelModeBase::cancelConstruction()
+{
+ killMode();
+}
+
+void TextLabelModeBase::enableActions()
+{
+ KigMode::enableActions();
+
+ mdoc.aCancelConstruction->setEnabled( true );
+}
+
+void TextLabelModeBase::mouseMoved( QMouseEvent* e, KigWidget* w )
+{
+ if ( d->mwawd == ReallySelectingArgs )
+ {
+ std::vector<ObjectHolder*> os = mdoc.document().whatAmIOn( w->fromScreen( e->pos() ), *w );
+ if ( !os.empty() ) w->setCursor( KCursor::handCursor() );
+ else w->setCursor( KCursor::arrowCursor() );
+ }
+ else if ( d->mwawd == SelectingLocation )
+ {
+ std::vector<ObjectHolder*> os = mdoc.document().whatAmIOn( w->fromScreen( e->pos() ), *w );
+ bool attachable = false;
+ d->locationparent = 0;
+ for ( std::vector<ObjectHolder*>::iterator i = os.begin(); i != os.end(); ++i )
+ {
+ if( (*i)->imp()->attachPoint().valid() ||
+ (*i)->imp()->inherits( PointImp::stype() ) ||
+ (*i)->imp()->inherits( CurveImp::stype() ) )
+ {
+ attachable = true;
+ d->locationparent = (*i)->calcer();
+ break;
+ };
+ };
+ w->updateCurPix();
+ if ( attachable )
+ {
+ w->setCursor( KCursor::handCursor() );
+ QString s = d->locationparent->imp()->type()->attachToThisStatement();
+ mdoc.emitStatusBarText( s );
+
+ KigPainter p( w->screenInfo(), &w->curPix, mdoc.document() );
+
+ // set the text next to the arrow cursor
+ QPoint point = e->pos();
+ point.setX(point.x()+15);
+
+ p.drawTextStd( point, s );
+ w->updateWidget( p.overlay() );
+ }
+ else
+ {
+ w->setCursor( KCursor::crossCursor() );
+ mdoc.emitStatusBarText( 0 );
+ w->updateWidget();
+ };
+ }
+}
+
+void TextLabelModeBase::enterTextPageEntered()
+{
+}
+
+void TextLabelModeBase::selectArgumentsPageEntered()
+{
+ updateLinksLabel();
+}
+
+void TextLabelModeBase::cancelPressed()
+{
+ cancelConstruction();
+}
+
+static uint percentCount( const QString& s )
+{
+// QRegExp re( QString::fromUtf8( "%[0-9]" ) );
+ QRegExp re( QString::fromUtf8( "%[\\d]+" ) );
+ int offset = 0;
+ uint percentcount = 0;
+ while ( ( offset = re.search( s, offset ) ) != -1 )
+ {
+ ++percentcount;
+ offset += re.matchedLength();
+ };
+ return percentcount;
+}
+
+void TextLabelModeBase::finishPressed()
+{
+ bool needframe = d->wiz->needFrameCheckBox->isChecked();
+ QString s = d->wiz->labelTextInput->text();
+
+ assert( percentCount( s ) == d->args.size() );
+ if ( d->wiz->currentPage() == d->wiz->enter_text_page )
+ assert( d->args.size() == 0 );
+
+ bool finished = true;
+ for ( argvect::iterator i = d->args.begin(); i != d->args.end(); ++i )
+ finished &= ( *i != 0 );
+
+ if ( ! finished )
+ KMessageBox::sorry( mdoc.widget(),
+ i18n( "There are '%n' parts in the text that you have not selected a "
+ "value for. Please remove them or select enough arguments." ) );
+ else
+ {
+ finish( d->mcoord, s, d->args, needframe, d->locationparent );
+ killMode();
+ };
+}
+
+void TextLabelModeBase::updateWiz()
+{
+ QString s = d->wiz->labelTextInput->text();
+ uint percentcount = percentCount( s );
+ if ( d->lpc > percentcount )
+ {
+ d->args = argvect( d->args.begin(), d->args.begin() + percentcount );
+ }
+ else if ( d->lpc < percentcount )
+ {
+ d->args.resize( percentcount, 0 );
+ };
+
+ if ( percentcount == 0 && ! s.isEmpty() )
+ {
+ d->wiz->setNextEnabled( d->wiz->enter_text_page, false );
+ d->wiz->setFinishEnabled( d->wiz->enter_text_page, true );
+ d->wiz->setAppropriate( d->wiz->select_arguments_page, false );
+ }
+ else
+ {
+ d->wiz->setAppropriate( d->wiz->select_arguments_page, !s.isEmpty() );
+ d->wiz->setNextEnabled( d->wiz->enter_text_page, ! s.isEmpty() );
+ d->wiz->setFinishEnabled( d->wiz->enter_text_page, false );
+ bool finished = true;
+ for ( argvect::iterator i = d->args.begin(); i != d->args.end(); ++i )
+ finished &= ( *i != 0 );
+ assert( percentCount( s ) == d->args.size() );
+
+ d->wiz->setFinishEnabled( d->wiz->select_arguments_page, finished );
+ };
+
+ d->lpc = percentcount;
+}
+
+void TextLabelModeBase::labelTextChanged()
+{
+ updateWiz();
+}
+
+void TextLabelModeBase::updateLinksLabel()
+{
+ LinksLabel::LinksLabelEditBuf buf = d->wiz->myCustomWidget1->startEdit();
+ QString s = d->wiz->labelTextInput->text();
+// QRegExp re( "%[0-9]" );
+ QRegExp re( "%[\\d]+" );
+ int prevpos = 0;
+ int pos = 0;
+ uint count = 0;
+ // we split up the string into text and "links"
+ while ( ( pos = re.search( s, pos ) ) != -1 )
+ {
+ // prevpos is the first character after the last match, pos is the
+ // first char of the current match..
+ if ( prevpos != pos )
+ {
+ // there is a text part between the previous and the current
+ // "link"...
+ assert( prevpos < pos );
+ // fetch the text part...
+ QString subs = s.mid( prevpos, pos - prevpos );
+ // and add it...
+ d->wiz->myCustomWidget1->addText( subs, buf );
+ };
+ // we always need a link part...
+ QString linktext( "%1" );
+ assert( count < d->args.size() );
+ if ( d->args[count] )
+ {
+ // if the user has already selected a property, then we show its
+ // value...
+ d->args[count]->imp()->fillInNextEscape( linktext, mdoc.document() );
+ }
+ else
+ // otherwise, we show a stub...
+ linktext = i18n( "argument %1" ).arg( count + 1 );
+
+ d->wiz->myCustomWidget1->addLink( linktext, buf );
+ // set pos and prevpos to the next char after the last match, so
+ // we don't enter infinite loops...
+// pos += 2;
+ pos += re.matchedLength();
+ prevpos = pos;
+ ++count;
+ };
+
+ if ( static_cast<uint>( prevpos ) != s.length() )
+ d->wiz->myCustomWidget1->addText( s.mid( prevpos ), buf );
+
+ d->wiz->myCustomWidget1->applyEdit( buf );
+ d->wiz->relayoutArgsPage();
+
+ d->wiz->resize( d->wiz->size() );
+}
+
+void TextLabelModeBase::linkClicked( int i )
+{
+ mdoc.widget()->setActiveWindow();
+ mdoc.widget()->raise();
+
+ assert( d->args.size() >= static_cast<uint>( i + 1 ) );
+
+ d->mwawd = ReallySelectingArgs;
+ d->mwaaws = i;
+
+ mdoc.emitStatusBarText( i18n( "Selecting argument %1" ).arg( i + 1 ) );
+}
+
+void TextLabelModeBase::redrawScreen( KigWidget* w )
+{
+ w->redrawScreen( std::vector<ObjectHolder*>() );
+ w->updateScrollBars();
+}
+
+void TextLabelModeBase::setCoordinate( const Coordinate& coord )
+{
+ d->mcoord = coord;
+ if ( d->mwawd == SelectingLocation )
+ {
+ d->mwawd = RequestingText;
+ updateWiz();
+ d->wiz->show();
+ // shouldn't be necessary, but seems to be anyway.. :(
+ updateWiz();
+ };
+}
+
+void TextLabelModeBase::setText( const QString& s )
+{
+ d->wiz->labelTextInput->setText( s );
+}
+
+void TextLabelModeBase::setPropertyObjects( const argvect& props )
+{
+ d->args = props;
+ for ( argvect::iterator i = d->args.begin(); i != d->args.end(); ++i )
+ (*i)->calc( mdoc.document() );
+}
+
+TextLabelConstructionMode::TextLabelConstructionMode( KigPart& d )
+ : TextLabelModeBase( d )
+{
+}
+
+TextLabelConstructionMode::~TextLabelConstructionMode()
+{
+}
+
+void TextLabelConstructionMode::finish(
+ const Coordinate& coord, const QString& s,
+ const argvect& props, bool needframe,
+ ObjectCalcer* locationparent )
+{
+ std::vector<ObjectCalcer*> args;
+ for ( argvect::const_iterator i = props.begin();
+ i != props.end(); ++i )
+ args.push_back( i->get() );
+
+ ObjectHolder* label = 0;
+ if ( locationparent )
+ label = ObjectFactory::instance()->attachedLabel( s, locationparent, coord, needframe, args, mdoc.document() );
+ else
+ label = ObjectFactory::instance()->label( s, coord, needframe, args, mdoc.document() );
+ mdoc.addObject( label );
+}
+
+TextLabelRedefineMode::TextLabelRedefineMode( KigPart& d, ObjectTypeCalcer* label )
+ : TextLabelModeBase( d ), mlabel( label )
+{
+ assert( label->imp()->inherits( TextImp::stype() ) );
+ std::vector<ObjectCalcer*> parents = label->parents();
+ assert( parents.size() >= 3 );
+ std::vector<ObjectCalcer*> firstthree( parents.begin(), parents.begin() + 3 );
+ std::vector<ObjectCalcer*> rest( parents.begin() + 3, parents.end() );
+ firstthree = TextType::instance()->argParser().parse( firstthree );
+
+ assert( firstthree[0]->imp()->inherits( IntImp::stype() ) );
+ assert( firstthree[1]->imp()->inherits( PointImp::stype() ) );
+ assert( firstthree[2]->imp()->inherits( StringImp::stype() ) );
+
+ bool frame = static_cast<const IntImp*>( firstthree[0]->imp() )->data() != 0;
+ Coordinate coord = static_cast<const PointImp*>( firstthree[1]->imp() )->coordinate();
+ QString text = static_cast<const StringImp*>( firstthree[2]->imp() )->data();
+
+ // don't set it, let the user redefine it..
+// setCoordinate( coord );
+ setText( text );
+ setFrame( frame );
+
+ argvect v;
+ for ( uint i = 0; i < rest.size(); ++i )
+ {
+ v.push_back( rest[i] );
+ };
+ assert( v.size() == rest.size() );
+
+ setPropertyObjects( v );
+}
+
+TextLabelRedefineMode::~TextLabelRedefineMode()
+{
+}
+
+void TextLabelRedefineMode::finish(
+ const Coordinate& coord, const QString& s,
+ const argvect& props, bool needframe,
+ ObjectCalcer* locationparent )
+{
+ std::vector<ObjectCalcer*> parents = mlabel->parents();
+ assert( parents.size() >= 3 );
+ std::vector<ObjectCalcer*> firstthree( parents.begin(), parents.begin() + 3 );
+ std::vector<ObjectCalcer*> rest( parents.begin() + 3, parents.end() );
+ firstthree = TextType::instance()->argParser().parse( firstthree );
+
+ KigCommand* kc = new KigCommand( mdoc, i18n( "Change Label" ) );
+ MonitorDataObjects mon( firstthree );
+
+ assert( firstthree[0]->imp()->inherits( IntImp::stype() ) );
+ assert( firstthree[1]->imp()->inherits( PointImp::stype() ) );
+ assert( firstthree[2]->imp()->inherits( StringImp::stype() ) );
+
+ assert( dynamic_cast<ObjectConstCalcer*>( firstthree[0] ) );
+ assert( dynamic_cast<ObjectConstCalcer*>( firstthree[2] ) );
+ static_cast<ObjectConstCalcer*>( firstthree[0] )->setImp( new IntImp( needframe ? 1 : 0 ) );
+
+ // we don't do this, because
+ // 1 this isn't necessarily a DataObject, we also support it to be a
+ // user-known point, or an internal constrained point..
+ // 2 we don't know that we don't want it to become a user-known
+ // point or an internal constrained point, instead of a
+ // DataObject..
+ // static_cast<DataObject*>( firstthree[1] )->setImp( new PointImp(
+ // coord ) );
+
+ static_cast<ObjectConstCalcer*>( firstthree[2] )->setImp( new StringImp( s ) );
+ mon.finish( kc );
+
+ std::vector<ObjectCalcer*> oldparents = mlabel->parents();
+ std::vector<ObjectCalcer*> p;
+ for ( argvect::const_iterator i = props.begin();
+ i != props.end(); ++i )
+ p.push_back( i->get() );
+ for ( std::vector<ObjectCalcer*>::iterator i = p.begin();
+ i != p.end(); ++i )
+ ( *i )->calc( mdoc.document() );
+
+ std::vector<ObjectCalcer*> np = firstthree;
+ /*
+ * take advantage of the method "getAttachPoint" that should
+ * do all the work; it is also used when creating a new label
+ */
+ np[1] = ObjectFactory::instance()->getAttachPoint( locationparent, coord, mdoc.document() );
+
+/* this is the old code, just in case... */
+// if ( locationparent && locationparent->imp()->inherits( CurveImp::stype() ) )
+// {
+// double param = static_cast<const CurveImp*>( locationparent->imp() )->getParam( coord, mdoc.document() );
+// np[1] = ObjectFactory::instance()->constrainedPointCalcer( locationparent, param );
+// np[1]->calc( mdoc.document() );
+// }
+// else if ( locationparent )
+// {
+// assert( locationparent->imp()->inherits( PointImp::stype() ) );
+// np[1] = locationparent;
+// }
+// else
+// np[1] = new ObjectConstCalcer( new PointImp( coord ) );
+
+ copy( p.begin(), p.end(), back_inserter( np ) );
+
+ kc->addTask(
+ new ChangeParentsAndTypeTask(
+ mlabel, np, TextType::instance() ) );
+
+ mdoc.history()->addCommand( kc );
+}
+
+void TextLabelModeBase::setFrame( bool f )
+{
+ d->wiz->needFrameCheckBox->setChecked( f );
+}
+
+void TextLabelModeBase::setLocationParent( ObjectCalcer* o )
+{
+ d->locationparent = o;
+}