/*
Rosegarden
A MIDI and audio sequencer and musical notation editor .
This program is Copyright 2000 - 2008
Guillaume Laurent < glaurent @ telegraph - road . org > ,
Chris Cannam < cannam @ all - day - breakfast . com > ,
Richard Bown < richard . bown @ ferventsoftware . com >
The moral rights of Guillaume Laurent , Chris Cannam , and Richard
Bown to claim authorship of this work have been asserted .
Other copyrights also apply to some parts of this work . Please
see the AUTHORS file and individual file headers for details .
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 . See the file
COPYING included with this distribution for more information .
*/
# include "ChordNameRuler.h"
# include "misc/Debug.h"
# include <tdelocale.h>
# include "misc/Strings.h"
# include "base/AnalysisTypes.h"
# include "base/Composition.h"
# include "base/CompositionTimeSliceAdapter.h"
# include "base/Instrument.h"
# include "base/NotationTypes.h"
# include "base/Profiler.h"
# include "base/PropertyName.h"
# include "base/NotationQuantizer.h"
# include "base/RefreshStatus.h"
# include "base/RulerScale.h"
# include "base/Segment.h"
# include "base/Selection.h"
# include "base/Studio.h"
# include "base/Track.h"
# include "document/RosegardenGUIDoc.h"
# include "document/MultiViewCommandHistory.h"
# include "gui/general/GUIPalette.h"
# include <tqfont.h>
# include <tqfontmetrics.h>
# include <tqobject.h>
# include <tqpainter.h>
# include <tqrect.h>
# include <tqsize.h>
# include <tqtooltip.h>
# include <tqwidget.h>
namespace Rosegarden
{
ChordNameRuler : : ChordNameRuler ( RulerScale * rulerScale ,
RosegardenGUIDoc * doc ,
double xorigin ,
int height ,
TQWidget * parent ,
const char * name ) :
TQWidget ( parent , name ) ,
m_xorigin ( xorigin ) ,
m_height ( height ) ,
m_currentXOffset ( 0 ) ,
m_width ( - 1 ) ,
m_ready ( false ) ,
m_rulerScale ( rulerScale ) ,
m_composition ( & doc - > getComposition ( ) ) ,
m_regetSegmentsOnChange ( true ) ,
m_currentSegment ( 0 ) ,
m_studio ( 0 ) ,
m_chordSegment ( 0 ) ,
m_fontMetrics ( m_boldFont ) ,
TEXT_FORMAL_X ( " TextFormalX " ) ,
TEXT_ACTUAL_X ( " TextActualX " )
{
m_font . setPointSize ( 11 ) ;
m_font . setPixelSize ( 12 ) ;
m_boldFont . setPointSize ( 11 ) ;
m_boldFont . setPixelSize ( 12 ) ;
m_boldFont . setBold ( true ) ;
m_fontMetrics = TQFontMetrics ( m_boldFont ) ;
setBackgroundColor ( GUIPalette : : getColour ( GUIPalette : : ChordNameRulerBackground ) ) ;
m_compositionRefreshStatusId = m_composition - > getNewRefreshStatusId ( ) ;
TQObject : : connect ( doc - > getCommandHistory ( ) , TQ_SIGNAL ( commandExecuted ( ) ) ,
this , TQ_SLOT ( update ( ) ) ) ;
TQToolTip : : add
( this , i18n ( " Chord name ruler. \n Turn it on and off from the Settings->Rulers menu. " ) ) ;
}
ChordNameRuler : : ChordNameRuler ( RulerScale * rulerScale ,
RosegardenGUIDoc * doc ,
std : : vector < Segment * > & segments ,
double xorigin ,
int height ,
TQWidget * parent ,
const char * name ) :
TQWidget ( parent , name ) ,
m_xorigin ( xorigin ) ,
m_height ( height ) ,
m_currentXOffset ( 0 ) ,
m_width ( - 1 ) ,
m_ready ( false ) ,
m_rulerScale ( rulerScale ) ,
m_composition ( & doc - > getComposition ( ) ) ,
m_regetSegmentsOnChange ( false ) ,
m_currentSegment ( 0 ) ,
m_studio ( 0 ) ,
m_chordSegment ( 0 ) ,
m_fontMetrics ( m_boldFont ) ,
TEXT_FORMAL_X ( " TextFormalX " ) ,
TEXT_ACTUAL_X ( " TextActualX " )
{
m_font . setPointSize ( 11 ) ;
m_font . setPixelSize ( 12 ) ;
m_boldFont . setPointSize ( 11 ) ;
m_boldFont . setPixelSize ( 12 ) ;
m_boldFont . setBold ( true ) ;
m_fontMetrics = TQFontMetrics ( m_boldFont ) ;
setBackgroundColor ( GUIPalette : : getColour ( GUIPalette : : ChordNameRulerBackground ) ) ;
m_compositionRefreshStatusId = m_composition - > getNewRefreshStatusId ( ) ;
TQObject : : connect ( doc - > getCommandHistory ( ) , TQ_SIGNAL ( commandExecuted ( ) ) ,
this , TQ_SLOT ( update ( ) ) ) ;
for ( std : : vector < Segment * > : : iterator i = segments . begin ( ) ;
i ! = segments . end ( ) ; + + i ) {
m_segments . insert ( SegmentRefreshMap : : value_type
( * i , ( * i ) - > getNewRefreshStatusId ( ) ) ) ;
}
}
ChordNameRuler : : ~ ChordNameRuler ( )
{
delete m_chordSegment ;
}
void
ChordNameRuler : : setReady ( )
{
m_ready = true ;
update ( ) ;
}
void
ChordNameRuler : : setCurrentSegment ( Segment * segment )
{
m_currentSegment = segment ;
}
void
ChordNameRuler : : setStudio ( Studio * studio )
{
m_studio = studio ;
}
void
ChordNameRuler : : slotScrollHoriz ( int x )
{
int w = width ( ) , h = height ( ) ;
int dx = x - ( - m_currentXOffset ) ;
m_currentXOffset = - x ;
if ( dx = = 0 )
return ;
if ( dx > w * 7 / 8 | | dx < - w * 7 / 8 ) {
update ( ) ;
return ;
}
if ( dx > 0 ) { // moving right, so the existing stuff moves left
bitBlt ( this , 0 , 0 , this , dx , 0 , w - dx , h ) ;
repaint ( w - dx , 0 , dx , h ) ;
} else { // moving left, so the existing stuff moves right
bitBlt ( this , - dx , 0 , this , 0 , 0 , w + dx , h ) ;
repaint ( 0 , 0 , - dx , h ) ;
}
}
TQSize
ChordNameRuler : : sizeHint ( ) const
{
double width =
m_rulerScale - > getBarPosition ( m_rulerScale - > getLastVisibleBar ( ) ) +
m_rulerScale - > getBarWidth ( m_rulerScale - > getLastVisibleBar ( ) ) ;
NOTATION_DEBUG < < " Returning chord-label ruler width as " < < width < < endl ;
TQSize res ( std : : max ( int ( width ) , m_width ) , m_height ) ;
return res ;
}
TQSize
ChordNameRuler : : minimumSizeHint ( ) const
{
double firstBarWidth = m_rulerScale - > getBarWidth ( 0 ) ;
TQSize res = TQSize ( int ( firstBarWidth ) , m_height ) ;
return res ;
}
void
ChordNameRuler : : recalculate ( timeT from , timeT to )
{
if ( ! m_ready )
return ;
Profiler profiler ( " ChordNameRuler::recalculate " ) ;
NOTATION_DEBUG < < " ChordNameRuler[ " < < this < < " ]::recalculate " < < endl ;
bool regetSegments = false ;
enum RecalcLevel { RecalcNone , RecalcVisible , RecalcWhole } ;
RecalcLevel level = RecalcNone ;
if ( m_segments . empty ( ) ) {
regetSegments = true ;
} else if ( m_regetSegmentsOnChange ) {
RefreshStatus & rs =
m_composition - > getRefreshStatus ( m_compositionRefreshStatusId ) ;
if ( rs . needsRefresh ( ) ) {
rs . setNeedsRefresh ( false ) ;
regetSegments = true ;
}
}
if ( regetSegments ) {
SegmentSelection ss ;
for ( Composition : : iterator ci = m_composition - > begin ( ) ;
ci ! = m_composition - > end ( ) ; + + ci ) {
if ( m_studio ) {
TrackId ti = ( * ci ) - > getTrack ( ) ;
Instrument * instr = m_studio - > getInstrumentById
( m_composition - > getTrackById ( ti ) - > getInstrument ( ) ) ;
if ( instr & &
instr - > getInstrumentType ( ) = = Instrument : : Midi & &
instr - > isPercussion ( ) ) {
continue ;
}
}
ss . insert ( * ci ) ;
}
std : : vector < SegmentRefreshMap : : iterator > eraseThese ;
for ( SegmentRefreshMap : : iterator si = m_segments . begin ( ) ;
si ! = m_segments . end ( ) ; + + si ) {
if ( ss . find ( si - > first ) = = ss . end ( ) ) {
eraseThese . push_back ( si ) ;
level = RecalcWhole ;
NOTATION_DEBUG < < " Segment deleted, updating (now have " < < m_segments . size ( ) < < " segments) " < < endl ;
}
}
for ( std : : vector < SegmentRefreshMap : : iterator > : : iterator ei = eraseThese . begin ( ) ;
ei ! = eraseThese . end ( ) ; + + ei ) {
m_segments . erase ( * ei ) ;
}
for ( SegmentSelection : : iterator si = ss . begin ( ) ;
si ! = ss . end ( ) ; + + si ) {
if ( m_segments . find ( * si ) = = m_segments . end ( ) ) {
m_segments . insert ( SegmentRefreshMap : : value_type
( * si , ( * si ) - > getNewRefreshStatusId ( ) ) ) ;
level = RecalcWhole ;
NOTATION_DEBUG < < " Segment created, adding (now have " < < m_segments . size ( ) < < " segments) " < < endl ;
}
}
if ( m_currentSegment & &
ss . find ( m_currentSegment ) = = ss . end ( ) ) {
m_currentSegment = 0 ;
level = RecalcWhole ;
}
}
if ( ! m_chordSegment )
m_chordSegment = new Segment ( ) ;
if ( m_segments . empty ( ) )
return ;
SegmentRefreshStatus overallStatus ;
overallStatus . setNeedsRefresh ( false ) ;
for ( SegmentRefreshMap : : iterator i = m_segments . begin ( ) ;
i ! = m_segments . end ( ) ; + + i ) {
SegmentRefreshStatus & status =
i - > first - > getRefreshStatus ( i - > second ) ;
if ( status . needsRefresh ( ) ) {
overallStatus . push ( status . from ( ) , status . to ( ) ) ;
}
}
// We now have the overall area affected by these changes, across
// all segments. If it's entirely within our displayed area, just
// recalculate the displayed area; if it overlaps, calculate the
// union of the two areas; if it's entirely without, calculate
// nothing.
if ( level = = RecalcNone ) {
if ( from = = to ) {
NOTATION_DEBUG < < " ChordNameRuler::recalculate: from==to, recalculating all " < < endl ;
level = RecalcWhole ;
} else if ( overallStatus . from ( ) = = overallStatus . to ( ) ) {
NOTATION_DEBUG < < " ChordNameRuler::recalculate: overallStatus.from==overallStatus.to, ignoring " < < endl ;
level = RecalcNone ;
} else if ( overallStatus . from ( ) > = from & & overallStatus . to ( ) < = to ) {
NOTATION_DEBUG < < " ChordNameRuler::recalculate: change is " < < overallStatus . from ( ) < < " -> " < < overallStatus . to ( ) < < " , I show " < < from < < " -> " < < to < < " , recalculating visible area " < < endl ;
level = RecalcVisible ;
} else if ( overallStatus . from ( ) > = to | | overallStatus . to ( ) < = from ) {
NOTATION_DEBUG < < " ChordNameRuler::recalculate: change is " < < overallStatus . from ( ) < < " -> " < < overallStatus . to ( ) < < " , I show " < < from < < " -> " < < to < < " , ignoring " < < endl ;
level = RecalcNone ;
} else {
NOTATION_DEBUG < < " ChordNameRuler::recalculate: change is " < < overallStatus . from ( ) < < " -> " < < overallStatus . to ( ) < < " , I show " < < from < < " -> " < < to < < " , recalculating whole " < < endl ;
level = RecalcWhole ;
}
}
if ( level = = RecalcNone )
return ;
for ( SegmentRefreshMap : : iterator i = m_segments . begin ( ) ;
i ! = m_segments . end ( ) ; + + i ) {
i - > first - > getRefreshStatus ( i - > second ) . setNeedsRefresh ( false ) ;
}
if ( ! m_currentSegment ) { //!!! arbitrary, must do better
//!!! need a segment starting at zero or so with a clef and key in it!
m_currentSegment = m_segments . begin ( ) - > first ;
}
/*!!!
for ( Composition : : iterator ci = m_composition - > begin ( ) ;
ci ! = m_composition - > end ( ) ; + + ci ) {
if ( ( * ci ) - > getEndMarkerTime ( ) > = from & &
( ( * ci ) - > getStartTime ( ) < = from | |
( clefKeySegment & &
( * ci ) - > getStartTime ( ) < clefKeySegment - > getStartTime ( ) ) ) ) {
clefKeySegment = * ci ;
}
}
if ( ! clefKeySegment ) return ;
}
*/
if ( level = = RecalcWhole ) {
m_chordSegment - > clear ( ) ;
timeT clefKeyTime = m_currentSegment - > getStartTime ( ) ;
//(from < m_currentSegment->getStartTime() ?
// m_currentSegment->getStartTime() : from);
Clef clef = m_currentSegment - > getClefAtTime ( clefKeyTime ) ;
m_chordSegment - > insert ( clef . getAsEvent ( - 1 ) ) ;
: : Rosegarden : : Key key = m_currentSegment - > getKeyAtTime ( clefKeyTime ) ;
m_chordSegment - > insert ( key . getAsEvent ( - 1 ) ) ;
from = 0 ;
to = 0 ;
} else {
Segment : : iterator i = m_chordSegment - > findTime ( from ) ;
Segment : : iterator j = m_chordSegment - > findTime ( to ) ;
m_chordSegment - > erase ( i , j ) ;
}
SegmentSelection selection ;
for ( SegmentRefreshMap : : iterator si = m_segments . begin ( ) ; si ! = m_segments . end ( ) ;
+ + si ) {
selection . insert ( si - > first ) ;
}
CompositionTimeSliceAdapter adapter ( m_composition , & selection , from , to ) ;
AnalysisHelper helper ;
helper . labelChords ( adapter , * m_chordSegment , m_composition - > getNotationQuantizer ( ) ) ;
}
void
ChordNameRuler : : paintEvent ( TQPaintEvent * e )
{
if ( ! m_composition | | ! m_ready )
return ;
NOTATION_DEBUG < < " *** Chord Name Ruler: paintEvent " < < endl ;
Profiler profiler1 ( " ChordNameRuler::paintEvent (whole) " ) ;
TQPainter paint ( this ) ;
paint . setPen ( GUIPalette : : getColour ( GUIPalette : : ChordNameRulerForeground ) ) ;
paint . setClipRegion ( e - > region ( ) ) ;
paint . setClipRect ( e - > rect ( ) . normalize ( ) ) ;
TQRect clipRect = paint . clipRegion ( ) . boundingRect ( ) ;
timeT from = m_rulerScale - > getTimeForX
( clipRect . x ( ) - m_currentXOffset - m_xorigin - 50 ) ;
timeT to = m_rulerScale - > getTimeForX
( clipRect . x ( ) + clipRect . width ( ) - m_currentXOffset - m_xorigin + 50 ) ;
recalculate ( from , to ) ;
if ( ! m_chordSegment )
return ;
Profiler profiler2 ( " ChordNameRuler::paintEvent (paint) " ) ;
TQRect boundsForHeight = m_fontMetrics . boundingRect ( " ^j|lM " ) ;
int fontHeight = boundsForHeight . height ( ) ;
int textY = ( height ( ) - 6 ) / 2 + fontHeight / 2 ;
double prevX = 0 ;
timeT keyAt = from - 1 ;
std : : string keyText ;
NOTATION_DEBUG < < " *** Chord Name Ruler: paint " < < from < < " -> " < < to < < endl ;
for ( Segment : : iterator i = m_chordSegment - > findTime ( from ) ;
i ! = m_chordSegment - > findTime ( to ) ; + + i ) {
NOTATION_DEBUG < < " type " < < ( * i ) - > getType ( ) < < " at " < < ( * i ) - > getAbsoluteTime ( )
< < endl ;
if ( ! ( * i ) - > isa ( Text : : EventType ) | |
! ( * i ) - > has ( Text : : TextPropertyName ) | |
! ( * i ) - > has ( Text : : TextTypePropertyName ) )
continue ;
std : : string text ( ( * i ) - > get
< String > ( Text : : TextPropertyName ) ) ;
if ( ( * i ) - > get
< String > ( Text : : TextTypePropertyName ) = = Text : : KeyName ) {
timeT myTime = ( * i ) - > getAbsoluteTime ( ) ;
if ( myTime = = keyAt & & text = = keyText )
continue ;
else {
keyAt = myTime ;
keyText = text ;
}
}
double x = m_rulerScale - > getXForTime ( ( * i ) - > getAbsoluteTime ( ) ) ;
( * i ) - > set
< Int > ( TEXT_FORMAL_X , ( long ) x ) ;
TQRect textBounds = m_fontMetrics . boundingRect ( strtoqstr ( text ) ) ;
int width = textBounds . width ( ) ;
x - = width / 2 ;
if ( prevX > = x - 3 )
x = prevX + 3 ;
( * i ) - > set
< Int > ( TEXT_ACTUAL_X , long ( x ) ) ;
prevX = x + width ;
}
for ( Segment : : iterator i = m_chordSegment - > findTime ( from ) ;
i ! = m_chordSegment - > findTime ( to ) ; + + i ) {
if ( ! ( * i ) - > isa ( Text : : EventType ) )
continue ;
std : : string text ( ( * i ) - > get
< String > ( Text : : TextPropertyName ) ) ;
std : : string type ( ( * i ) - > get
< String > ( Text : : TextTypePropertyName ) ) ;
if ( ! ( * i ) - > has ( TEXT_FORMAL_X ) )
continue ;
long formalX = ( * i ) - > get
< Int > ( TEXT_FORMAL_X ) ;
long actualX = ( * i ) - > get
< Int > ( TEXT_ACTUAL_X ) ;
formalX + = m_currentXOffset + long ( m_xorigin ) ;
actualX + = m_currentXOffset + long ( m_xorigin ) ;
paint . drawLine ( formalX , height ( ) - 4 , formalX , height ( ) ) ;
if ( type = = Text : : KeyName ) {
paint . setFont ( m_boldFont ) ;
} else {
paint . setFont ( m_font ) ;
}
paint . drawText ( actualX , textY , strtoqstr ( text ) ) ;
}
}
}
# include "ChordNameRuler.moc"