2 Copyright (c) 2006 Paolo Capriotti <p.capriotti@sns.it>
3 (c) 2006 Maurizio Monge <maurizio.monge@kdemail.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
12 #include <QApplication>
14 #include <QPaintEvent>
15 #include <QMouseEvent>
18 #include <QHBoxLayout>
19 #include <QVBoxLayout>
20 #include <QScrollArea>
21 #include <QToolButton>
26 #include <kstandarddirs.h>
28 #include "pref_theme.h"
29 #include "movelist_widget.h"
30 #include "movelist_table.h"
31 #include "movelist_notifier.h"
32 #include "movelist_p.h"
37 #define MARGIN_RIGHT 4
39 #define MARGIN_BOTTOM 0
41 #define COMMENT_INDENTATION 4
42 #define VAR_INDENTATION 16
44 #define BORDER_RIGHT 3
46 #define BORDER_BOTTOM 3
47 #define MIN_COL_WIDTH 5.0
49 #define DEFAULT_ANIMATION_TIME 350.0
51 //BEGIN FancyItem--------------------------------------------------------------
53 bool FancyItem::showing() {
54 return !(time_opacity
!=-1 && target_opacity
== 0) &&
55 ((time_opacity
!=-1 && target_opacity
== 255) || (visible() && opacity() == 255));
58 void FancyItem::appear() {
59 if((time_opacity
!=-1 && target_opacity
== 255)
60 || (visible() && opacity() == 255))
63 Widget
*m
= dynamic_cast<Widget
*>(topLevelCanvas());
64 if(!m
->m_settings
->anim_enabled
|| !m
->m_settings
->anim_hideshow
)
69 time_opacity
= m
->layout_time
;
74 void FancyItem::disappear() {
75 if((time_opacity
!=-1 && target_opacity
== 0)
76 || !visible() || opacity() == 0)
79 Widget
*m
= dynamic_cast<Widget
*>(topLevelCanvas());
81 if(!m
->m_settings
->anim_enabled
|| !m
->m_settings
->anim_hideshow
)
86 time_opacity
= m
->layout_time
;
91 void FancyItem::goTo(QPoint p
) {
92 if((time_pos
!=-1 && target_pos
== p
) || (time_pos
==-1 && pos() == p
))
95 Widget
*m
= dynamic_cast<Widget
*>(topLevelCanvas());
96 if(!m
->m_settings
->anim_enabled
|| !m
->m_settings
->anim_moving
)
101 time_pos
= m
->layout_time
;
102 /*std::cout << m->layout_time << " start " << this << " "
103 << prettyTypeName(typeid(*this).name()) << std::endl;*/
108 void FancyItem::setHighlight(bool h
) {
112 Widget
*m
= dynamic_cast<Widget
*>(topLevelCanvas());
113 if(!m
->m_settings
->anim_enabled
|| !m
->m_settings
->anim_highlight
) {
114 curr_highlight
= h
? 255 : 0;
119 old_highlight
= highlighted
? 255 : 0;
120 target_highlight
= h
? 255 : 0;
122 time_highlight
= m
->mSecs();
127 void FancyItem::advance(int time
) {
128 Widget
*m
= dynamic_cast<Widget
*>(topLevelCanvas());
129 /*std::cout << time << " anim " << this << " "
130 << prettyTypeName(typeid(*this).name()) << std::endl;*/
131 if(time_highlight
!= -1) {
132 float fact
= (time
- time_highlight
) / m
->m_settings
->anim_time
;
134 curr_highlight
= target_highlight
;
138 curr_highlight
= int(target_highlight
*fact
+ old_highlight
*(1-fact
));
141 if(time_opacity
!= -1) {
142 float fact
= (time
- time_opacity
) / m
->m_settings
->anim_time
;
144 setOpacity(target_opacity
);
145 setVisible(target_opacity
!= 0);
149 setOpacity( int(target_opacity
*fact
+ old_opacity
*(1-fact
)) );
154 float fact
= (time
- time_pos
) / m
->m_settings
->anim_time
;
156 /*std::cout << time << " done " << this << " "
157 << prettyTypeName(typeid(*this).name()) << std::endl;*/
162 /*std::cout << time << " move " << this << " "
163 << prettyTypeName(typeid(*this).name()) << std::endl;*/
164 moveTo( int(target_pos
.x()*fact
+ old_pos
.x()*(1-fact
)+0.5),
165 int(target_pos
.y()*fact
+ old_pos
.y()*(1-fact
)+0.5));
169 /*std::cout << time << " stop " << this << " "
170 << prettyTypeName(typeid(*this).name()) << std::endl;*/
175 bool FancyItem::layered() const {
179 //END FancyItem----------------------------------------------------------------
182 //BEGIN Brace------------------------------------------------------------------
184 void Brace::setHeight(int h
) {
186 if((animated() && target_height
== h
) || height
== h
)
189 Widget
*m
= dynamic_cast<Widget
*>(topLevelCanvas());
190 if(!m
->m_settings
->anim_enabled
|| !m
->m_settings
->anim_moving
) {
197 time_height
= m
->layout_time
;
203 void Brace::advance(int time
) {
204 if(time_height
!= -1) {
205 Widget
*m
= dynamic_cast<Widget
*>(topLevelCanvas());
206 float fact
= (time
- time_height
) / m
->m_settings
->anim_time
;
208 height
= target_height
;
212 height
= int(target_height
*fact
+ old_height
*(1-fact
) + 0.5);
215 FancyItem::advance(time
);
218 void Brace::paint (QPainter
*p
) {
219 Widget
*m
= dynamic_cast<Widget
*>(topLevelCanvas());
220 if(height
< m
->entry_size
)
222 QPointF
p1((pos().x()*2+width
)/2.0, (pos().y()*2+m
->entry_size
)/2.0);
223 QPointF
p2((pos().x()*2+width
)/2.0, (pos().y()*2+height
*2-m
->entry_size
)/2.0);
227 p
->setBrush(QColor(255,192,224));
228 q
= QColor(128,0,64);
231 p
->setBrush(QColor(192,255,224));
232 q
= QColor(0,128,64);
236 p
->setRenderHint(QPainter::Antialiasing
);
237 int s
= std::min(m
->entry_size
, width
);
238 float cs1
= (0.4 + 0.2*(curr_highlight
/255.0)) * s
;
242 p
->drawEllipse(QRectF(-cs1
/2,-cs1
/2,cs1
,cs1
).translated(p1
));
243 p
->setBrush(p
->pen().color());
244 p
->drawEllipse(QRectF(-cs2
/2,-cs2
/2,cs2
,cs2
).translated(p2
));
248 QRect
Brace::rect () const {
249 return QRect(pos(), QSize(width
, height
));
252 //END Brace--------------------------------------------------------------------
255 //BEGIN Text-------------------------------------------------------------------
257 void Text::paint (QPainter
*p
) {
258 Widget
*m
= dynamic_cast<Widget
*>(topLevelCanvas());
259 if(curr_highlight
!= 0) {
260 p
->setBrush(QColor(192,224,208, curr_highlight
));
261 p
->setPen(QColor(64,128,96, curr_highlight
));
262 p
->drawRect(rect().adjusted(0,0,-1,-1));
264 p
->setFont(selected
? m
->m_settings
->sel_mv_font
: m
->m_settings
->mv_font
);
265 p
->setPen(selected
? m
->m_settings
->select_color
: Qt::black
);
266 p
->drawText(pos()+QPoint(MARGIN_LEFT
, MARGIN_TOP
+m
->m_settings
->mv_fmetrics
.ascent()), text
);
269 QRect
Text::rect () const {
270 return QRect(pos(), QSize(width
, height
));
273 void Text::doUpdate () {
277 Widget
*m
= dynamic_cast<Widget
*>(topLevelCanvas());
278 width
= (selected
? m
->m_settings
->sel_mv_fmetrics
279 : m
->m_settings
->mv_fmetrics
).boundingRect(text
).right()
280 + MARGIN_LEFT
+ MARGIN_RIGHT
;
281 height
= m
->entry_size
;
283 needs_update
= false;
287 //END Text---------------------------------------------------------------------
290 //BEGIN Comment----------------------------------------------------------------
292 void Comment::paint (QPainter
*p
) {
293 Widget
*m
= dynamic_cast<Widget
*>(topLevelCanvas());
295 if(curr_highlight
!= 0) {
296 p
->setBrush(QColor(255,255,255, curr_highlight
));
297 p
->setPen(QColor(192,192,192, curr_highlight
));
298 p
->drawRect(rect().adjusted(0,0,-1,-1));
300 p
->setFont(m
->m_settings
->comm_font
);
301 p
->setPen(m
->m_settings
->comment_color
);
302 p
->drawText(pos().x() + MARGIN_RIGHT
,
304 width
- MARGIN_LEFT
- MARGIN_RIGHT
, 9999,
305 Qt::AlignLeft
|Qt::AlignTop
|Qt::TextWordWrap
, text
);
308 QRect
Comment::rect () const {
309 return QRect(pos(), QSize(width
, height
));
312 void Comment::doUpdate () {
316 Widget
*m
= dynamic_cast<Widget
*>(topLevelCanvas());
317 QPoint dest
= ((time_pos
!= -1) ? target_pos
: pos());
318 width
= std::max(m
->width() - dest
.x(), m
->entry_size
);
319 height
= m
->m_settings
->comm_fmetrics
.boundingRect(0,0,width
- MARGIN_LEFT
- MARGIN_RIGHT
, 99999,
320 Qt::AlignLeft
|Qt::AlignTop
|Qt::TextWordWrap
, text
).height()
321 + MARGIN_TOP
+ MARGIN_BOTTOM
;
323 needs_update
= false;
327 //END Comment------------------------------------------------------------------
330 //BEGIN Entry------------------------------------------------------------------
332 void Entry::paint (QPainter
*p
) {
333 Widget
*m
= dynamic_cast<Widget
*>(topLevelCanvas());
334 if(curr_highlight
!= 0) {
335 p
->setBrush(QColor(192,224,255, curr_highlight
));
336 p
->setPen(QColor(64,96,128, curr_highlight
));
337 p
->drawRect(rect().adjusted(0,0,-1,-1));
339 p
->setPen(selected
? m
->m_settings
->select_color
: Qt::black
);
340 int x
= pos().x()+MARGIN_LEFT
;
341 int y
= pos().y()+MARGIN_TOP
+m_ascent
;
343 p
->setRenderHint(QPainter::TextAntialiasing
);
344 QFont tf
= selected
? m
->m_settings
->sel_mv_font
: m
->m_settings
->mv_font
;
345 QFontMetrics
& fm
= selected
? m
->m_settings
->sel_mv_fmetrics
: m
->m_settings
->mv_fmetrics
;
348 for(int i
=0;i
<(int)move
.size();i
++) {
349 if(move
[i
].m_type
== MovePart::Text
) {
351 p
->drawText(QPoint(x
+r
.width(), y
), move
[i
].m_string
);
352 QRect b
= fm
.boundingRect(move
[i
].m_string
);
353 r
|= b
.translated(r
.width()-b
.x(), 0);
355 else if(move
[i
].m_type
== MovePart::Figurine
) {
356 ::Loader::Glyph g
= m
->m_loader
.getGlyph(move
[i
].m_string
);
357 p
->setFont(g
.m_font_valid
? g
.m_font
: tf
);
358 p
->drawText(QPoint(x
+r
.width(), y
), g
.m_char
);
359 QFontMetrics
fi(g
.m_font_valid
? g
.m_font
: tf
);
360 QRect b
= fi
.boundingRect(g
.m_char
);
361 r
|= b
.translated(r
.width()-b
.x(), 0);
366 QRect
Entry::rect () const {
367 return m_rect
.translated(pos().x()+MARGIN_LEFT
, pos().y()+MARGIN_TOP
+m_ascent
);
370 void Entry::doUpdate () {
374 Widget
*m
= dynamic_cast<Widget
*>(topLevelCanvas());
375 QFont tf
= selected
? m
->m_settings
->sel_mv_font
: m
->m_settings
->mv_font
;
376 QFontMetrics
& fm
= selected
? m
->m_settings
->sel_mv_fmetrics
: m
->m_settings
->mv_fmetrics
;
377 m_ascent
= m
->m_settings
->mv_fmetrics
.ascent();
378 m_rect
= QRect(0,0,0,0);
380 for(int i
=0;i
<(int)move
.size();i
++) {
381 if(move
[i
].m_type
== MovePart::Text
) {
382 QRect b
= fm
.boundingRect(move
[i
].m_string
);
383 m_rect
|= b
.translated(m_rect
.width()-b
.x(), 0);
385 else if(move
[i
].m_type
== MovePart::Figurine
) {
386 ::Loader::Glyph g
= m
->m_loader
.getGlyph(move
[i
].m_string
);
387 QFontMetrics
fi(g
.m_font_valid
? g
.m_font
: tf
);
388 QRect b
= fi
.boundingRect(g
.m_char
);
389 m_rect
|= b
.translated(m_rect
.width()-b
.x(), 0);
392 m_rect
= QRect(m_rect
.x(),m_rect
.y(),m_rect
.width()+MARGIN_RIGHT
,m_rect
.height());
394 needs_update
= false;
398 //END Entry--------------------------------------------------------------------
401 //BEGIN Settings---------------------------------------------------------------
403 void Settings::load() {
404 ::Settings s
= settings
.group("move-list");
405 ::Settings s_anim
= s
.group("animations");
407 anim_enabled
= s
.group("animations").flag("enabled", true);
408 anim_moving
= s_anim
.group("moving").flag("enabled", true);
409 anim_hideshow
= s_anim
.group("hideshow").flag("enabled", true);
410 anim_highlight
= s_anim
.group("highlight").flag("enabled", true);
411 anim_speed
= s_anim
["speed"] | 16;
412 anim_time
= DEFAULT_ANIMATION_TIME
*pow(5.0, 1.0 - anim_speed
/16.0);
413 anim_smoothness
= s_anim
["smoothness"] | 16;
414 select_color
= s
["select-color"] | QColor(Qt::red
);
415 comment_color
= s
["comment-color"] | QColor(64,64,64);
416 mv_font
= QApplication::font();
417 if ((use_mv_font
= s
.group("moves-font").flag("enabled", true)))
418 mv_font
= s
["moves-font"] | mv_font
;
419 sel_mv_font
= mv_font
;
420 sel_mv_font
.setBold(true);
421 comm_font
= QApplication::font();
422 comm_font
.setItalic(true);
423 if ((use_comm_font
= s
.group("comment-font").flag("enabled", true)))
424 comm_font
= s
["CommentFont"] | comm_font
;
425 mv_fmetrics
= QFontMetrics(mv_font
);
426 sel_mv_fmetrics
= QFontMetrics(sel_mv_font
);
427 comm_fmetrics
= QFontMetrics(comm_font
);
429 // settings.qSettings()->endGroup();
432 void Settings::save() {
433 ::Settings s
= settings
.group("move-list");
434 ::Settings s_anim
= s
.group("animations");
436 s
.group("animations").setFlag("enabled", anim_enabled
);
437 s_anim
.group("moving").setFlag("enabled", anim_moving
);
438 s_anim
.group("hideshow").setFlag("enabled", anim_hideshow
);
439 s_anim
.group("highlight").setFlag("enabled", anim_highlight
);
440 s_anim
["speed"] = anim_speed
;
441 s_anim
["smoothness"] = anim_smoothness
;
442 s
["select-color"] = select_color
;
443 s
["comment-color"] = comment_color
;
444 s
.group("moves-font").flag("enabled", use_mv_font
);
445 s
["moves-font"] = mv_font
;
446 s
.group("comment-font").flag("enabled", use_comm_font
);
447 s
["comment-font"] = comm_font
;
450 //END Settings-----------------------------------------------------------------
453 //BEGIN Widget-----------------------------------------------------------------
455 Widget::Widget(QWidget
*parent
, Table
*o
)
456 : KGameCanvasWidget(parent
)
459 , comment_editor(NULL
)
460 , layout_pending(false)
462 , layout_goto_selected(false)
463 , layout_width_changed(true)
464 , layout_must_relayout(true)
467 , m_settings(new Settings
) {
470 setSizePolicy ( QSizePolicy::MinimumExpanding
, QSizePolicy::Minimum
);
472 setMouseTracking(true);
481 void Widget::reset() {
482 curr_highlight
= Index(-1);
483 curr_selected
= Index(-1);
485 delete comment_editor
;
486 comment_editor
= NULL
;
490 mv
.push_back(MovePart(QString("Mainline:")));
492 history
.push_back( EntryPtr(new Entry(-1, mv
, Index(0), this)) );
497 EntryPtr
Widget::fetch(const Index
& ix
) {
499 History
*vec
= fetchRef(ix
, &at
);
500 return vec
? (*vec
)[at
] : EntryPtr();
503 History
* Widget::fetchRef(const Index
& ix
, int* idx
) {
504 if(ix
.num_moves
>= (int)history
.size() || ix
.num_moves
< 0 )
507 History
* aretv
= &history
;
508 EntryPtr retv
= history
[ix
.num_moves
];
509 if(idx
) *idx
= ix
.num_moves
;
511 for(int i
=0; i
<(int)ix
.nested
.size();i
++) {
512 Variations::iterator it
= retv
->variations
.find(ix
.nested
[i
].variation
);
513 if(it
== retv
->variations
.end() || ix
.nested
[i
].num_moves
>= (int)it
->second
.size()
514 || ix
.nested
[i
].num_moves
< 0 )
518 retv
= it
->second
[ix
.nested
[i
].num_moves
];
519 if(idx
) *idx
= ix
.nested
[i
].num_moves
;
524 Notifier
* Widget::getNotifier() {
528 void Widget::setNotifier(Notifier
* n
, bool detach_prev
){
529 if(detach_prev
&& notifier
&& notifier
!= n
)
530 notifier
->onDetachNotifier();
534 void Widget::settingsChanged() {
537 setAnimationDelay( int(70.0*pow(10.0, -m_settings
->anim_smoothness
/32.0)) );
539 entry_size
= m_settings
->mv_fmetrics
.height()+MARGIN_TOP
+MARGIN_BOTTOM
;
540 owner_table
->m_scroll_area
->setMinimumSize(entry_size
*6, entry_size
*9);
542 m_loader
.setSize(m_settings
->mv_font
.pointSize());
544 layout_must_relayout
= true;
548 void Widget::mouseMoveEvent ( QMouseEvent
* event
) {
549 KGameCanvasItem
*i
= itemAt(event
->pos());
550 Entry
* e
= i
? dynamic_cast<Entry
*>(i
) : NULL
;
551 Text
* f
= i
? dynamic_cast<Text
*>(i
) : NULL
;
552 Brace
* b
= i
? dynamic_cast<Brace
*>(i
) : NULL
;
553 Comment
* c
= i
? dynamic_cast<Comment
*>(i
) : NULL
;
554 EntryPtr olde
= fetch(curr_highlight
);
555 f
= f
&& f
->type
== 1 ? f
: NULL
;
557 int oldtype
= curr_highlight_type
;
560 if(curr_highlight
== e
->index
&& curr_highlight_type
==-1)
562 e
->setHighlight(true);
563 curr_highlight
= e
->index
;
564 curr_highlight_type
= -1;
567 if(curr_highlight
== f
->entry
->index
&& curr_highlight_type
==-2)
569 f
->setHighlight(true);
570 curr_highlight
= f
->entry
->index
;
571 curr_highlight_type
= -2;
574 if( (curr_highlight
==c
->entry
->index
) &&
575 ((c
->variation
==-1) ? (curr_highlight_type
== -3) :
576 (curr_highlight_type
== -1000-c
->variation
) ) )
578 c
->setHighlight(true);
579 curr_highlight
= c
->entry
->index
;
580 curr_highlight_type
= (c
->variation
==-1) ? -3 : (-1000-c
->variation
);
583 if(curr_highlight
== b
->entry
->index
&& curr_highlight_type
==b
->variation
)
585 b
->setHighlight(true);
586 curr_highlight
= b
->entry
->index
;
587 curr_highlight_type
= b
->variation
;
590 curr_highlight
= Index(-1);
594 olde
->setHighlight(false);
595 else if(oldtype
==-2 && olde
->fregna
)
596 olde
->fregna
->setHighlight(false);
597 else if(oldtype
==-3 && olde
->comment
)
598 olde
->comment
->setHighlight(false);
599 else if(oldtype
<=-1000 && olde
->vcomments
.count(-1000-oldtype
)==1)
600 olde
->vcomments
[-1000-oldtype
]->setHighlight(false);
601 else if(oldtype
>=0 && olde
->braces
.count(oldtype
)==1)
602 olde
->braces
[oldtype
]->setHighlight(false);
606 void Widget::startEditing(const Index
& i
, int v
) {
609 EntryPtr e
= fetch(i
);
611 std::cout
<< "--> Error in Widget::startEditing! Invalid index!" << std::endl
;
615 CommentPtr c
= v
== -1 ? e
->comment
: (e
->vcomments
.count(v
) ?
616 e
->vcomments
[v
] : CommentPtr());
621 TextPtr n
= e
->number
;
622 if(!n
&& i
> Index(0))
623 n
= fetch(i
.prev())->number
;
624 int x
= (n
? n
->pos().x() : e
->pos().x()) + ((v
== -1) ? 0 : VAR_INDENTATION
);
626 rect
= QRect(x
, e
->pos().y()+entry_size
, width()-x
, 0);
629 edited_comment_variation
= v
;
630 edited_comment
= boost::weak_ptr
<Entry
>(e
);
631 comment_editor
= new QTextEdit(c
? c
->text
: QString(), this);
632 comment_editor
->setGeometry(rect
.adjusted(0,0,0,entry_size
*3));
633 comment_editor
->show();
634 comment_editor
->setFocus(Qt::MouseFocusReason
);
635 comment_editor
->installEventFilter(this);
638 bool Widget::eventFilter(QObject
*obj
, QEvent
*event
) {
639 if(obj
== comment_editor
&& event
->type() == QEvent::FocusOut
) {
646 void Widget::stopEditing() {
647 EntryPtr e
= edited_comment
.lock();
649 if(comment_editor
&& notifier
) {
650 QString c
= comment_editor
->toPlainText();
651 c
.replace(QRegExp("(?:[ \t]\r?\n\r?|\r?\n\r?[ \t]|\r?\n\r?)"), " ");
652 if(edited_comment_variation
== -1)
653 notifier
->onUserSetComment(e
->index
, c
);
655 notifier
->onUserSetVComment(e
->index
, edited_comment_variation
, c
);
657 edited_comment
.reset();
660 comment_editor
->deleteLater();
661 comment_editor
= NULL
;
665 void Widget::mousePressEvent ( QMouseEvent
* event
) {
668 KGameCanvasItem
*i
= itemAt(event
->pos());
672 Text
*t
= dynamic_cast<Text
*>(i
);
673 if(t
&& t
->type
== 1) {
676 e
->hide_next
= false;
680 e
->expanded
= !e
->expanded
;
685 Brace
*b
= dynamic_cast<Brace
*>(i
);
687 if(event
->button() == Qt::LeftButton
) {
689 EntryPtr first
= e
->variations
[b
->variation
][0];
690 first
->hide_next
= !first
->hide_next
;
693 else if(event
->button() == Qt::RightButton
) {
696 a
= m
.addAction(QIcon(m_settings
->icons
+"comment.png"), "&Set comment");
697 a
->setData("comment");
699 a
= m
.addAction(QIcon(m_settings
->icons
+"promote.png"), "&Promote variation");
700 a
->setData("promote");
701 a
= m
.addAction(QIcon(m_settings
->icons
+"remove.png"), "&Remove variation");
702 a
->setData("remove");
703 boost::weak_ptr
<Entry
> ewptr
= boost::weak_ptr
<Entry
>(fetch(b
->entry
->index
));
704 int v
= b
->variation
;
706 a
= m
.exec(event
->globalPos());
708 /* beware, here, after exec, e could be a dangling pointer */
709 EntryPtr eptr
= ewptr
.lock();
710 if(a
&& notifier
&& eptr
&& eptr
->variations
.count(v
)) {
711 if(a
->data() == "comment")
712 startEditing(eptr
->index
, v
);
713 else if(a
->data() == "promote")
714 notifier
->onUserPromoteVariation(eptr
->index
.next(v
));
715 else if(a
->data() == "remove")
716 notifier
->onUserRemoveVariation(eptr
->index
.next(v
));
722 Comment
*c
= dynamic_cast<Comment
*>(i
);
724 startEditing(c
->entry
->index
, c
->variation
);
728 Entry
*e
= dynamic_cast<Entry
*>(i
);
730 if(event
->button() == Qt::LeftButton
) {
732 notifier
->onUserSelectMove(e
->index
);
734 else if(event
->button() == Qt::RightButton
) {
737 a
= m
.addAction(QIcon(m_settings
->icons
+"comment.png"), "&Set comment");
738 a
->setData("comment");
739 a
= m
.addAction(QIcon(m_settings
->icons
+"clear.png"), "&Clear variations");
740 a
->setEnabled(!e
->variations
.empty());
742 a
= m
.addAction(QIcon(m_settings
->icons
+"truncate.png"), "&Truncate");
743 a
->setEnabled(fetch(e
->index
.next()));
744 a
->setData("truncate");
746 a
= m
.addAction(QIcon(m_settings
->icons
+"promote.png"), "&Promote variation");
747 a
->setEnabled(e
->index
.nested
.size());
748 a
->setData("promote");
749 a
= m
.addAction(QIcon(m_settings
->icons
+"remove.png"), "&Remove variation");
750 a
->setEnabled(e
->index
.nested
.size());
751 a
->setData("remove");
752 boost::weak_ptr
<Entry
> ewptr
= boost::weak_ptr
<Entry
>(fetch(e
->index
));
754 a
= m
.exec(event
->globalPos());
756 /* beware, here, after exec, e could be a dangling pointer */
757 EntryPtr eptr
= ewptr
.lock();
758 if(a
&& notifier
&& eptr
) {
759 if(a
->data() == "comment")
760 startEditing(eptr
->index
, -1);
761 else if(a
->data() == "clear")
762 notifier
->onUserClearVariations(eptr
->index
);
763 else if(a
->data() == "truncate")
764 notifier
->onUserTruncate(eptr
->index
);
765 else if(a
->data() == "promote")
766 notifier
->onUserPromoteVariation(eptr
->index
);
767 else if(a
->data() == "remove")
768 notifier
->onUserRemoveVariation(eptr
->index
);
775 void Widget::mouseReleaseEvent ( QMouseEvent
* /*event*/ ) {
779 void Widget::resizeEvent ( QResizeEvent
* event
) {
781 if(event
->size().width() != event
->oldSize().width()) {
782 layout_width_changed
= true;
787 void Widget::layout() {
791 layout_pending
= true;
792 QTimer::singleShot( 0, this, SLOT(doLayout()) );
795 void Widget::doLayout() {
796 layout_time
= mSecs();
797 layout_pending
= false;
798 layout_max_width
= 0;
799 //std::cout << "layout_must_relayout = " << layout_must_relayout << std::endl;
800 int h
= layoutHistory(history
, BORDER_LEFT
, BORDER_TOP
, -1, 0, 0, true);
802 QSize
s(std::max(entry_size
*7, layout_max_width
+BORDER_RIGHT
),
803 std::max(entry_size
*10, h
+BORDER_BOTTOM
) );
806 layout_width_changed
= false;
807 layout_must_relayout
= false;
808 if(layout_goto_selected
) {
809 EntryPtr e
= fetch(curr_selected
);
811 owner_table
->m_scroll_area
->ensureVisible( int(e
->pos().x() + e
->m_rect
.width()*0.5),
812 int(e
->pos().y() + e
->m_rect
.height()*0.5) );
813 layout_goto_selected
= false;
817 int Widget::layoutHistory(History
& array
, int at_x
, int at_y
,
818 int a_prev_turn
, int mv_num
, int sub_mv_num
, bool visible
) {
823 int prev_turn
= a_prev_turn
;
825 for(int i
=0;i
<(int)array
.size();i
++) {
826 EntryPtr e
= array
[i
];
828 /* if this is not visible, hide the item and hide all the number/fregna tags */
832 e
->number
->disappear();
834 e
->fregna
->disappear();
836 e
->comment
->disappear();
838 /* hide the subvariations */
839 for(Variations::iterator it
= e
->variations
.begin(); it
!= e
->variations
.end(); ++it
)
840 layoutHistory(it
->second
, 0, 0, e
->move_turn
, mv_num
, sub_mv_num
, false);
841 for(Braces::iterator it
= e
->braces
.begin(); it
!= e
->braces
.end(); ++it
)
842 it
->second
->disappear();
843 for(VComments::iterator it
= e
->vcomments
.begin(); it
!= e
->vcomments
.end(); ++it
)
844 it
->second
->disappear();
849 /* adjust the position if this is paired on the right */
850 bool draw_num
= false;
853 if(e
->move_turn
!= prev_turn
) {
860 if(layout_style
==0) {
861 if(e
->move_turn
== 0 || i
==0 || array
[i
-1]->childs_height
!= 0) {
862 if(mv_num
>=1 && (e
->move_turn
!= prev_turn
|| i
==0 || array
[i
-1]->childs_height
!= 0)) {
867 flow_x
= (mv_num
>=1) ? nflow_x
: at_x
;
870 if(e
->move_turn
!= prev_turn
) {
871 flow_x
= std::max(flow_x
+ MIDDLE_PAD
, int(MIN_COL_WIDTH
*entry_size
));
872 flow_y
-= entry_size
;
875 flow_x
= int(MIN_COL_WIDTH
*entry_size
);
879 if(e
->move_turn
!= prev_turn
|| i
==0 || array
[i
-1]->childs_height
!= 0) {
885 else if(col_num
== layout_style
) {
890 flow_y
-= entry_size
;
891 flow_x
= std::max(flow_x
+ MIDDLE_PAD
,
892 at_x
+ col_num
*int(MIN_COL_WIDTH
*entry_size
));
899 /* update the number */
901 TextPtr
& n
= e
->number
;
903 n
= TextPtr(new Text(e
.get(), 0, this));
905 n
->text
= QString::number((mv_num
+1)/2)+(mv_num
&1 ? "." : ". ...");
907 n
->text
= QString::number(mv_num
)+
908 (sub_mv_num
? "+"+QString::number(sub_mv_num
) : QString())+".";
909 n
->needs_update
= true;
911 else if( !n
->showing() || layout_must_relayout
)
912 n
->needs_update
= true;
914 /* Mh, the number should never change, only appear disappear.
915 should this change, add here the code to enable number changes. */
916 QPoint
dest(flow_x
, flow_y
);
930 e
->number
->disappear();
933 /* update the entry */
934 QPoint
dest(flow_x
, flow_y
);
940 if( !e
->showing() || layout_must_relayout
)
941 e
->needs_update
= true;
944 e
->childs_height
= 0;
945 flow_x
+= e
->m_rect
.width();
948 /* Update the fregna. The fregna is visible if there are subvariations in this
949 entry, or if this entry is the first one of a variation where the remaining
950 entries are hidden and that contains the current position */
951 bool expandable
= !e
->variations
.empty() || e
->comment
;
952 bool sel
= (e
->hide_next
&& e
->index
<curr_selected
)
953 || (!e
->expanded
&& expandable
&&
954 e
->index
<curr_selected
&& !(e
->index
.next()<=curr_selected
));
955 if(expandable
|| sel
) {
957 e
->fregna
= TextPtr(new Text(e
.get(), 1, this));
959 /* update the data, if needed */
960 TextPtr f
= e
->fregna
;
961 const char *text
= (sel
||!e
->expanded
||e
->hide_next
) ? "[+]" : "[-]";
962 if(f
->text
!= text
|| f
->selected
!= sel
) {
965 f
->needs_update
= true;
967 else if( !f
->showing() || layout_must_relayout
)
968 f
->needs_update
= true;
970 QPoint
dest(flow_x
, flow_y
);
974 f
->goTo(QPoint(flow_x
, flow_y
));
976 f
->moveTo(QPoint(flow_x
, flow_y
));
983 e
->fregna
->disappear();
985 /* update the flow information */
986 flow_y
+= entry_size
;
987 layout_max_width
= std::max(flow_x
, layout_max_width
);
988 int prev_pos
= flow_y
;
990 /* update the comment */
992 CommentPtr c
= e
->comment
;
994 if(e
->expanded
&& !e
->hide_next
) {
995 QPoint
dest(at_x
+ COMMENT_INDENTATION
, flow_y
);
997 if(c
->pos() != dest
) {
1002 c
->needs_update
= true;
1004 else if( !c
->showing() || layout_width_changed
|| layout_must_relayout
)
1005 c
->needs_update
= true;
1008 flow_y
+= c
->height
;
1014 /* update the variations */
1015 for(Variations::iterator it
= e
->variations
.begin(); it
!= e
->variations
.end(); ++it
) {
1016 int old_pos
= flow_y
;
1018 /* update the variation's comment */
1019 if(e
->vcomments
.count(it
->first
)) {
1020 CommentPtr c
= e
->vcomments
[it
->first
];
1022 if(e
->expanded
&& !e
->hide_next
) {
1023 QPoint
dest(at_x
+ VAR_INDENTATION
+ COMMENT_INDENTATION
, flow_y
);
1025 if( !c
->showing() || layout_must_relayout
)
1026 c
->needs_update
= true;
1027 if(c
->pos() != dest
)
1034 flow_y
+= c
->height
;
1040 /* layout the variation */
1041 flow_y
= layoutHistory(it
->second
, at_x
+ VAR_INDENTATION
, flow_y
,
1042 e
->move_turn
, mv_num
, sub_mv_num
, e
->expanded
&& !e
->hide_next
);
1044 /* update the brace of the variation */
1045 BracePtr b
= e
->braces
[it
->first
];
1046 if(e
->expanded
&& !e
->hide_next
) {
1047 b
->depth
= e
->index
.nested
.size();
1048 b
->setHeight((it
->second
.size() && it
->second
[0]->hide_next
) ? entry_size
: flow_y
- old_pos
);
1049 b
->width
= VAR_INDENTATION
;
1051 b
->goTo(QPoint(at_x
, old_pos
));
1053 b
->moveTo(QPoint(at_x
, old_pos
));
1060 e
->childs_height
= flow_y
- prev_pos
;
1064 prev_turn
= e
->move_turn
;
1069 QPixmap
Widget::getPixmap(const QString
& s
, bool selected
) {
1070 QString k
= selected
? s
+"_sel":s
;
1071 if(loaded_pixmaps
.contains(k
))
1072 return loaded_pixmaps
[k
];
1074 QString iconFile
= KStandardDirs::locate("appdata", "piece_icons/" + s
+ ".png");
1075 QImage
img(iconFile
);
1079 p
.setCompositionMode(QPainter::CompositionMode_SourceAtop
);
1080 p
.fillRect(0,0,img
.width(), img
.height(), m_settings
->select_color
);
1082 return loaded_pixmaps
[k
] = QPixmap::fromImage(img
.scaled(m_settings
->mv_fmetrics
.ascent(),
1083 m_settings
->mv_fmetrics
.ascent(),
1084 Qt::IgnoreAspectRatio
, Qt::SmoothTransformation
));
1087 void Widget::setComment(EntryPtr e
, int v
, const QString
& comment
) {
1088 if(comment
.isEmpty()) {
1091 e
->comment
= CommentPtr();
1094 if(e
->vcomments
.count(v
))
1095 e
->vcomments
.erase(v
);
1103 e
->comment
= CommentPtr(new Comment(e
.get(), this));
1107 if(!e
->variations
.count(v
))
1110 if(!e
->vcomments
.count(v
))
1111 p
= e
->vcomments
[v
] = CommentPtr(new Comment(e
.get(), this, v
));
1113 p
= e
->vcomments
[v
];
1116 if(p
->text
== comment
)
1119 p
->needs_update
= true;
1125 void Widget::setComment(const Index
& index
, const QString
& comment
) {
1126 EntryPtr e
= fetch(index
);
1128 std::cout
<< "--> Error in Widget::setComment! Invalid index " << index
<< std::endl
;
1131 setComment(e
, -1, comment
);
1134 void Widget::setVComment(const Index
& index
, int v
, const QString
& comment
) {
1135 EntryPtr e
= fetch(index
);
1136 if(!e
|| !e
->variations
.count(v
)) {
1137 std::cout
<< "--> Error in Widget::setVComment! Invalid index " << index
<< std::endl
;
1140 setComment(e
, v
, comment
);
1143 void Widget::setMove(const Index
& index
,
1144 int turn
, const QString
& move
, const QString
& comment
) {
1147 mv
.push_back(MovePart(move
));
1149 //TODO: move this code in some other place, it really should not stay here
1150 QRegExp
reg("[KQRBNP]");
1152 while(reg
.indexIn(move
, x
) != -1) {
1154 mv
.push_back(MovePart(MoveText
, move
.mid(x
, reg
.pos()-x
)));
1155 mv
.push_back(MovePart(MovePixmap
, reg
.cap().toLower()));
1156 x
= reg
.pos() + reg
.matchedLength();
1159 mv
.push_back(MovePart(MoveText
, move
.mid(x
)));
1161 setMove(index
, turn
, mv
, comment
);
1164 void Widget::setMove(const Index
& index
,
1165 int turn
, const DecoratedMove
& move
, const QString
& comment
) {
1166 EntryPtr e
= fetch(index
);
1168 e
->move_turn
= turn
;
1170 e
->needs_update
= true;
1171 setComment(e
, -1, comment
);
1177 History
*vec
= fetchRef(index
.prev(), &at
);
1179 std::cout
<< "--> Error in Widget::setMove! Invalid index " << index
<< std::endl
;
1183 if(index
.nested
.size() && index
.nested
[index
.nested
.size()-1].num_moves
== 0) {
1185 int v
= index
.nested
[index
.nested
.size()-1].variation
;
1186 var
.push_back(e
= EntryPtr( new Entry(turn
, move
, index
, this)) );
1187 (*vec
)[at
]->variations
[v
] = var
;
1188 (*vec
)[at
]->braces
[v
] = BracePtr( new Brace( (*vec
)[at
].get(), v
, this) );
1191 vec
->push_back(e
= EntryPtr( new Entry(turn
, move
, index
, this)) );
1193 setComment(e
, -1, comment
);
1198 void Widget::remove(const Index
& index
) {
1200 if(index
.atVariationStart() ) {
1201 EntryPtr e
= fetch(index
.prev());
1205 int v
= index
.nested
[index
.nested
.size()-1].variation
;
1206 if(!e
->variations
.count(v
))
1209 e
->variations
.erase(v
);
1211 e
->vcomments
.erase(v
);
1215 History
*vec
= fetchRef(index
, &at
);
1219 while((int)vec
->size() > at
)
1225 void Widget::fixIndices(const Index
& ix
) {
1227 History
*vec
= fetchRef(ix
, &at
);
1229 std::cout
<< "--> Error in Widget::fixIndices, invalid index "<<ix
<< std::endl
;
1233 for(int i
=at
;i
<(int)vec
->size();i
++) {
1234 EntryPtr e
= (*vec
)[i
];
1237 for(Variations::const_iterator it
= e
->variations
.begin();
1238 it
!= e
->variations
.end(); ++it
)
1239 fixIndices(index
.next(it
->first
));
1240 index
= index
.next();
1244 void Widget::promoteVariation(const Index
& ix
, int v
) {
1246 History
*vec
= fetchRef(ix
, &at
);
1248 std::cout
<< "--> Error in Widget::promoteVariation, invalid index "<<ix
<< std::endl
;
1252 History vold
= (*vec
)[at
]->variations
[v
];
1254 for(int i
=at
+1; i
<(int)vec
->size(); i
++)
1255 vnew
.push_back((*vec
)[i
]);
1256 while((int)vec
->size()>at
+1)
1258 for(int i
=0; i
<(int)vold
.size(); i
++)
1259 vec
->push_back(vold
[i
]);
1260 (*vec
)[at
]->variations
[v
] = vnew
;
1262 Q_ASSERT((int)vec
->size()>at
+1);
1263 (*vec
)[at
+1]->hide_next
= false;
1265 fixIndices(ix
.next());
1266 fixIndices(ix
.next(v
));
1268 curr_selected
= curr_selected
.flipVariation(ix
, v
);
1269 curr_highlight
= curr_highlight
.flipVariation(ix
, v
);
1273 void Widget::select(const Index
& index
) {
1274 if(curr_selected
== index
)
1276 EntryPtr e
= fetch(index
);
1277 EntryPtr olde
= fetch(curr_selected
);
1279 olde
->selected
= false;
1280 olde
->needs_update
= true;
1284 e
->needs_update
= true;
1285 layout_goto_selected
= true;
1287 curr_selected
= index
;
1291 //END Widget-------------------------------------------------------------------
1293 } //end namespace MoveList