lib_text.cpp 14 KB
Newer Older
1 2 3
/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
4
 * Copyright (C) 2004-2012 KiCad Developers, see change_log.txt for contributors.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * 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, you may find one here:
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * or you may search the http://www.gnu.org website for the version 2 license,
 * or you may write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */
23 24

/**
25 26
 * @file lib_text.cpp
 */
27

28 29 30 31 32 33 34 35 36
#include <fctsys.h>
#include <gr_basic.h>
#include <macros.h>
#include <class_drawpanel.h>
#include <plot_common.h>
#include <drawtxt.h>
#include <trigo.h>
#include <wxstruct.h>
#include <richio.h>
37
#include <base_units.h>
38
#include <msgpanel.h>
39

40 41 42 43
#include <lib_draw_item.h>
#include <general.h>
#include <transform.h>
#include <lib_text.h>
44 45


46
LIB_TEXT::LIB_TEXT( LIB_PART * aParent ) :
47
    LIB_ITEM( LIB_TEXT_T, aParent ),
48
    EDA_TEXT()
49
{
50 51 52 53
    m_Size       = wxSize( 50, 50 );
    m_typeName   = _( "Text" );
    m_rotate     = false;
    m_updateText = false;
54 55 56
}


57
bool LIB_TEXT::Save( OUTPUTFORMATTER& aFormatter )
58 59 60
{
    wxString text = m_Text;

61 62 63 64 65 66 67 68 69 70 71 72
    if( text.Contains( wxT( "~" ) ) || text.Contains( wxT( "\"" ) ) )
    {
        // convert double quote to similar-looking two apostrophes
        text.Replace( wxT( "\"" ), wxT( "''" ) );
        text = wxT( "\"" ) + text + wxT( "\"" );
    }
    else
    {
        // Spaces are not allowed in text because it is not double quoted:
        // changed to '~'
        text.Replace( wxT( " " ), wxT( "~" ) );
    }
73

Dick Hollenbeck's avatar
Dick Hollenbeck committed
74
    aFormatter.Print( 0, "T %g %d %d %d %d %d %d %s ", GetOrientation(), m_Pos.x, m_Pos.y,
75
                      m_Size.x, m_Attributs, m_Unit, m_Convert, TO_UTF8( text ) );
76

77
    aFormatter.Print( 0, " %s %d", m_Italic ? "Italic" : "Normal", ( m_Bold > 0 ) ? 1 : 0 );
78 79

    char hjustify = 'C';
80

81 82 83 84 85 86
    if( m_HJustify == GR_TEXT_HJUSTIFY_LEFT )
        hjustify = 'L';
    else if( m_HJustify == GR_TEXT_HJUSTIFY_RIGHT )
        hjustify = 'R';

    char vjustify = 'C';
87

88 89 90 91 92
    if( m_VJustify == GR_TEXT_VJUSTIFY_BOTTOM )
        vjustify = 'B';
    else if( m_VJustify == GR_TEXT_VJUSTIFY_TOP )
        vjustify = 'T';

93
    aFormatter.Print( 0, " %c %c\n", hjustify, vjustify );
94 95 96 97 98

    return true;
}


99
bool LIB_TEXT::Load( LINE_READER& aLineReader, wxString& errorMsg )
100
{
101
    int     cnt, thickness = 0;
Dick Hollenbeck's avatar
Dick Hollenbeck committed
102 103 104 105 106
    char    hjustify = 'C', vjustify = 'C';
    char    buf[256];
    char    tmp[256];
    char*   line = (char*) aLineReader;
    double  angle;
107 108 109 110

    buf[0] = 0;
    tmp[0] = 0;         // For italic option, Not in old versions

111
    cnt = sscanf( line + 2, "%lf %d %d %d %d %d %d \"%[^\"]\" %255s %d %c %c",
Dick Hollenbeck's avatar
Dick Hollenbeck committed
112
                  &angle, &m_Pos.x, &m_Pos.y, &m_Size.x, &m_Attributs,
113 114
                  &m_Unit, &m_Convert, buf, tmp, &thickness, &hjustify,
                  &vjustify );
115

116
    if( cnt >= 8 ) // if quoted loading failed, load as not quoted
117
    {
118
        m_Text = FROM_UTF8( buf );
119

120 121
        // convert two apostrophes back to double quote
        m_Text.Replace( wxT( "''" ), wxT( "\"" ) );
122
    }
123 124
    else
    {
125
        cnt = sscanf( line + 2, "%lf %d %d %d %d %d %d %255s %255s %d %c %c",
Dick Hollenbeck's avatar
Dick Hollenbeck committed
126
                      &angle, &m_Pos.x, &m_Pos.y, &m_Size.x, &m_Attributs,
127 128
                      &m_Unit, &m_Convert, buf, tmp, &thickness, &hjustify,
                      &vjustify );
129

130 131
        if( cnt < 8 )
        {
132
            errorMsg.Printf( _( "Text only had %d parameters of the required 8" ), cnt );
133 134
            return false;
        }
135

136 137 138 139
        /* Convert '~' to spaces (only if text is not quoted). */
        m_Text = FROM_UTF8( buf );
        m_Text.Replace( wxT( "~" ), wxT( " " ) );
    }
140

Dick Hollenbeck's avatar
Dick Hollenbeck committed
141 142
    SetOrientation( angle );

143 144 145 146
    m_Size.y = m_Size.x;

    if( strnicmp( tmp, "Italic", 6 ) == 0 )
        m_Italic = true;
147

148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
    if( thickness > 0 )
    {
        m_Bold = true;
    }

    switch( hjustify )
    {
    case 'L':
        m_HJustify = GR_TEXT_HJUSTIFY_LEFT;
        break;

    case 'C':
        m_HJustify = GR_TEXT_HJUSTIFY_CENTER;
        break;

    case 'R':
        m_HJustify = GR_TEXT_HJUSTIFY_RIGHT;
        break;
    }

    switch( vjustify )
    {
    case 'T':
        m_VJustify = GR_TEXT_VJUSTIFY_TOP;
        break;

    case 'C':
        m_VJustify = GR_TEXT_VJUSTIFY_CENTER;
        break;

    case 'B':
        m_VJustify = GR_TEXT_VJUSTIFY_BOTTOM;
        break;
    }


    return true;
}

187

188
bool LIB_TEXT::HitTest( const wxPoint& aPosition ) const
189
{
190
    return HitTest( aPosition, 0, DefaultTransform );
191 192 193
}


194
bool LIB_TEXT::HitTest( const wxPoint &aPosition, int aThreshold, const TRANSFORM& aTransform ) const
195
{
196 197 198
    if( aThreshold < 0 )
        aThreshold = 0;

199 200
    EDA_TEXT tmp_text( *this );
    tmp_text.SetTextPosition( aTransform.TransformCoordinate( m_Pos ) );
201

202 203 204
    /* The text orientation may need to be flipped if the
     *  transformation matrix causes xy axes to be flipped.
     * this simple algo works only for schematic matrix (rot 90 or/and mirror)
205 206
     */
    int t1 = ( aTransform.x1 != 0 ) ^ ( m_Orient != 0 );
207 208
    tmp_text.SetOrientation( t1 ? TEXT_ORIENT_HORIZ : TEXT_ORIENT_VERT );
    return tmp_text.TextHitTest( aPosition );
209 210 211
}


212
EDA_ITEM* LIB_TEXT::Clone() const
213
{
214
    LIB_TEXT* newitem = new LIB_TEXT(NULL);
215

216
    newitem->m_Pos       = m_Pos;
217 218 219 220 221 222 223
    newitem->m_Orient    = m_Orient;
    newitem->m_Size      = m_Size;
    newitem->m_Attributs = m_Attributs;
    newitem->m_Unit      = m_Unit;
    newitem->m_Convert   = m_Convert;
    newitem->m_Flags     = m_Flags;
    newitem->m_Text      = m_Text;
224
    newitem->m_Thickness = m_Thickness;
225 226 227 228
    newitem->m_Italic    = m_Italic;
    newitem->m_Bold      = m_Bold;
    newitem->m_HJustify  = m_HJustify;
    newitem->m_VJustify  = m_VJustify;
229
    return newitem;
230 231 232
}


233
int LIB_TEXT::compare( const LIB_ITEM& other ) const
234
{
235
    wxASSERT( other.Type() == LIB_TEXT_T );
236

237
    const LIB_TEXT* tmp = ( LIB_TEXT* ) &other;
238

239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
    int result = m_Text.CmpNoCase( tmp->m_Text );

    if( result != 0 )
        return result;

    if( m_Pos.x != tmp->m_Pos.x )
        return m_Pos.x - tmp->m_Pos.x;

    if( m_Pos.y != tmp->m_Pos.y )
        return m_Pos.y - tmp->m_Pos.y;

    if( m_Size.x != tmp->m_Size.x )
        return m_Size.x - tmp->m_Size.x;

    if( m_Size.y != tmp->m_Size.y )
        return m_Size.y - tmp->m_Size.y;

    return 0;
257 258 259
}


260
void LIB_TEXT::SetOffset( const wxPoint& aOffset )
261
{
262
    m_Pos += aOffset;
263 264 265
}


266
bool LIB_TEXT::Inside( EDA_RECT& rect ) const
267 268 269
{
    /*
     * FIXME: This should calculate the text size and justification and
270
     *        use rectangle intersect.
271
     */
272
    return rect.Contains( m_Pos.x, -m_Pos.y );
273 274 275
}


276
void LIB_TEXT::Move( const wxPoint& newPosition )
277 278 279 280 281
{
    m_Pos = newPosition;
}


282
void LIB_TEXT::MirrorHorizontal( const wxPoint& center )
283 284 285 286 287 288
{
    m_Pos.x -= center.x;
    m_Pos.x *= -1;
    m_Pos.x += center.x;
}

289
void LIB_TEXT::MirrorVertical( const wxPoint& center )
290 291 292 293 294 295
{
    m_Pos.y -= center.y;
    m_Pos.y *= -1;
    m_Pos.y += center.y;
}

296
void LIB_TEXT::Rotate( const wxPoint& center, bool aRotateCCW )
297
{
298 299 300
    int rot_angle = aRotateCCW ? -900 : 900;

    RotatePoint( &m_Pos, center, rot_angle );
301 302 303
    m_Orient = m_Orient ? 0 : 900;
}

304

305 306
void LIB_TEXT::Plot( PLOTTER* plotter, const wxPoint& offset, bool fill,
                     const TRANSFORM& aTransform )
307 308 309 310 311
{
    wxASSERT( plotter != NULL );

    /* The text orientation may need to be flipped if the
     * transformation matrix causes xy axes to be flipped. */
312 313
    int t1  = ( aTransform.x1 != 0 ) ^ ( m_Orient != 0 );
    wxPoint pos = aTransform.TransformCoordinate( m_Pos ) + offset;
314

315 316 317 318 319 320 321 322
    // Get color
    EDA_COLOR_T     color;

    if( plotter->GetColorMode() )       // Used normal color or selected color
        color = IsSelected() ? GetItemSelectedColor() : GetDefaultColor();
    else
        color = BLACK;

323
    plotter->Text( pos, color, GetShownText(),
324 325 326 327 328 329
                   t1 ? TEXT_ORIENT_HORIZ : TEXT_ORIENT_VERT,
                   m_Size, GR_TEXT_HJUSTIFY_CENTER, GR_TEXT_VJUSTIFY_CENTER,
                   GetPenSize(), m_Italic, m_Bold );
}


330
int LIB_TEXT::GetPenSize() const
331
{
332
    int     pensize = m_Thickness;
333

334
    if( pensize == 0 )   // Use default values for pen size
335 336
    {
        if( m_Bold  )
337
            pensize = GetPenSizeForBold( m_Size.x );
338
        else
339
            pensize = GetDefaultLineThickness();
340
    }
341

342
    // Clip pen size for small texts:
343 344 345 346
    pensize = Clamp_Text_PenSize( pensize, m_Size, m_Bold );
    return pensize;
}

347

348
void LIB_TEXT::drawGraphic( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint& aOffset,
349 350
                            EDA_COLOR_T aColor, GR_DRAWMODE aDrawMode, void* aData,
                            const TRANSFORM& aTransform )
351
{
352
    EDA_COLOR_T color = GetDefaultColor();
353

354 355
    if( aColor < 0 )       // Used normal color or selected color
    {
356
        if( IsSelected() )
357
            color = GetItemSelectedColor();
358 359
    }
    else
360
    {
361
        color = aColor;
362
    }
363

364 365
    GRSetDrawMode( aDC, aDrawMode );

366 367 368 369
    /* Calculate the text orientation, according to the component
     * orientation/mirror (needed when draw text in schematic)
     */
    int orient = m_Orient;
370

371
    if( aTransform.y1 )  // Rotate component 90 degrees.
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
    {
        if( orient == TEXT_ORIENT_HORIZ )
            orient = TEXT_ORIENT_VERT;
        else
            orient = TEXT_ORIENT_HORIZ;
    }

    /* Calculate the text justification, according to the component
     * orientation/mirror this is a bit complicated due to cumulative
     * calculations:
     * - numerous cases (mirrored or not, rotation)
     * - the DrawGraphicText function recalculate also H and H justifications
     *      according to the text orientation.
     * - When a component is mirrored, the text is not mirrored and
     *   justifications are complicated to calculate
     * so the more easily way is to use no justifications ( Centered text )
     * and use GetBoundaryBox to know the text coordinate considered as centered
    */
390
    EDA_RECT bBox = GetBoundingBox();
391
    wxPoint txtpos = bBox.Centre();
392

393 394 395
    // Calculate pos accordint to mirror/rotation.
    txtpos = aTransform.TransformCoordinate( txtpos ) + aOffset;

396
    EDA_RECT* clipbox = aPanel? aPanel->GetClipBox() : NULL;
397
    DrawGraphicText( clipbox, aDC, txtpos, color, GetShownText(), orient, m_Size,
398 399 400
                     GR_TEXT_HJUSTIFY_CENTER, GR_TEXT_VJUSTIFY_CENTER, GetPenSize(),
                     m_Italic, m_Bold );

401

402 403
    /* Enable this to draw the bounding box around the text field to validate
     * the bounding box calculations.
404
     */
405
#if 0
406
    EDA_RECT grBox;
407 408
    grBox.SetOrigin( aTransform.TransformCoordinate( bBox.GetOrigin() ) );
    grBox.SetEnd( aTransform.TransformCoordinate( bBox.GetEnd() ) );
409
    grBox.Move( aOffset );
410
    GRRect( clipbox, aDC, grBox, 0, LIGHTMAGENTA );
411
#endif
412 413 414
}


415
void LIB_TEXT::GetMsgPanelInfo( MSG_PANEL_ITEMS& aList )
416 417 418
{
    wxString msg;

419
    LIB_ITEM::GetMsgPanelInfo( aList );
420

421
    msg = StringFromValue( g_UserUnit, m_Thickness, true );
422

423
    aList.push_back( MSG_PANEL_ITEM( _( "Line Width" ), msg, BLUE ) );
424
}
425 426


427
const EDA_RECT LIB_TEXT::GetBoundingBox() const
428
{
429 430
    /* Y coordinates for LIB_ITEMS are bottom to top, so we must invert the Y position when
     * calling GetTextBox() that works using top to bottom Y axis orientation.
431
     */
432
    EDA_RECT rect = GetTextBox( -1, -1, true );
433 434 435

    wxPoint orig = rect.GetOrigin();
    wxPoint end = rect.GetEnd();
436 437
    NEGATE( orig.y);
    NEGATE( end.y);
438

439 440
    RotatePoint( &orig, m_Pos, -m_Orient );
    RotatePoint( &end, m_Pos, -m_Orient );
441 442
    rect.SetOrigin( orig );
    rect.SetEnd( end );
443
    rect.Normalize();
444

445 446
    return rect;
}
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461


void LIB_TEXT::Rotate()
{
    if( InEditMode() )
    {
        m_rotate = true;
    }
    else
    {
        m_Orient = ( m_Orient == TEXT_ORIENT_VERT ) ? TEXT_ORIENT_HORIZ : TEXT_ORIENT_VERT;
    }
}


462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478
void LIB_TEXT::SetText( const wxString& aText )
{
    if( aText == m_Text )
        return;

    if( InEditMode() )
    {
        m_savedText = aText;
        m_updateText = true;
    }
    else
    {
        m_Text = aText;
    }
}


479 480 481
wxString LIB_TEXT::GetSelectMenuText() const
{
    wxString msg;
482
    msg.Printf( _( "Graphic Text %s" ), GetChars( ShortenedShownText() ) );
483 484 485 486
    return msg;
}


487
void LIB_TEXT::BeginEdit( STATUS_FLAGS aEditMode, const wxPoint aPosition )
488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
{
    wxCHECK_RET( ( aEditMode & ( IS_NEW | IS_MOVED ) ) != 0,
                 wxT( "Invalid edit mode for LIB_TEXT object." ) );

    if( aEditMode == IS_MOVED )
    {
        m_initialPos = m_Pos;
        m_initialCursorPos = aPosition;
        SetEraseLastDrawItem();
    }
    else
    {
        m_Pos = aPosition;
    }

    m_Flags = aEditMode;
}


bool LIB_TEXT::ContinueEdit( const wxPoint aPosition )
{
    wxCHECK_MSG( ( m_Flags & ( IS_NEW | IS_MOVED ) ) != 0, false,
                   wxT( "Bad call to ContinueEdit().  Text is not being edited." ) );

    return false;
}


void LIB_TEXT::EndEdit( const wxPoint& aPosition, bool aAbort )
{
    wxCHECK_RET( ( m_Flags & ( IS_NEW | IS_MOVED ) ) != 0,
                   wxT( "Bad call to EndEdit().  Text is not being edited." ) );

    m_Flags = 0;
522 523
    m_rotate = false;
    m_updateText = false;
524 525 526 527 528 529 530 531 532 533 534 535
    SetEraseLastDrawItem( false );
}


void LIB_TEXT::calcEdit( const wxPoint& aPosition )
{
    if( m_rotate )
    {
        m_Orient = ( m_Orient == TEXT_ORIENT_VERT ) ? TEXT_ORIENT_HORIZ : TEXT_ORIENT_VERT;
        m_rotate = false;
    }

536 537 538 539 540 541
    if( m_updateText )
    {
        EXCHG( m_Text, m_savedText );
        m_updateText = false;
    }

542 543 544 545 546 547 548 549 550 551
    if( m_Flags == IS_NEW )
    {
        SetEraseLastDrawItem();
        m_Pos = aPosition;
    }
    else if( m_Flags == IS_MOVED )
    {
        Move( m_initialPos + aPosition - m_initialCursorPos );
    }
}