diff options
Diffstat (limited to 'utils/regtools/qeditor/aux.cpp')
-rw-r--r-- | utils/regtools/qeditor/aux.cpp | 746 |
1 files changed, 746 insertions, 0 deletions
diff --git a/utils/regtools/qeditor/aux.cpp b/utils/regtools/qeditor/aux.cpp new file mode 100644 index 0000000000..0614bb57f6 --- /dev/null +++ b/utils/regtools/qeditor/aux.cpp | |||
@@ -0,0 +1,746 @@ | |||
1 | #include "aux.h" | ||
2 | #include <QFontMetrics> | ||
3 | #include <QPainter> | ||
4 | #include <QTextDocument> | ||
5 | #include <QAbstractTextDocumentLayout> | ||
6 | #include <QHeaderView> | ||
7 | #include <QDebug> | ||
8 | #include <QElapsedTimer> | ||
9 | #include <QXmlStreamReader> | ||
10 | #include <QXmlStreamWriter> | ||
11 | #include <QTextBlock> | ||
12 | |||
13 | /** | ||
14 | * SocBitRangeValidator | ||
15 | */ | ||
16 | SocBitRangeValidator::SocBitRangeValidator(QObject *parent) | ||
17 | :QValidator(parent) | ||
18 | { | ||
19 | } | ||
20 | |||
21 | void SocBitRangeValidator::fixup(QString& input) const | ||
22 | { | ||
23 | input = input.trimmed(); | ||
24 | } | ||
25 | |||
26 | QValidator::State SocBitRangeValidator::validate(QString& input, int& pos) const | ||
27 | { | ||
28 | Q_UNUSED(pos); | ||
29 | int first, last; | ||
30 | State state = parse(input, last, first); | ||
31 | return state; | ||
32 | } | ||
33 | |||
34 | QValidator::State SocBitRangeValidator::parse(const QString& input, int& last, int& first) const | ||
35 | { | ||
36 | // the empty string is always intermediate | ||
37 | if(input.size() == 0) | ||
38 | return Intermediate; | ||
39 | // check if there is ':' | ||
40 | int pos = input.indexOf(':'); | ||
41 | if(pos == -1) | ||
42 | pos = input.size(); | ||
43 | // if field start with ':', the last bit is implicit and is 31 | ||
44 | if(pos > 0) | ||
45 | { | ||
46 | // parse last bit and check it's between 0 and 31 | ||
47 | bool ok = false; | ||
48 | last = input.left(pos).toInt(&ok); | ||
49 | if(!ok || last < 0 || last >= 32) | ||
50 | return Invalid; | ||
51 | } | ||
52 | else | ||
53 | last = 31; | ||
54 | // parse first bit | ||
55 | if(pos < input.size() - 1) | ||
56 | { | ||
57 | bool ok = false; | ||
58 | first = input.mid(pos + 1).toInt(&ok); | ||
59 | if(!ok || first < 0 || first > last) | ||
60 | return Invalid; | ||
61 | } | ||
62 | // if input ends with ':', first bit is implicit and is 0 | ||
63 | else if(pos == input.size() - 1) | ||
64 | first = 0; | ||
65 | // if there no ':', first=last | ||
66 | else | ||
67 | first = last; | ||
68 | return Acceptable; | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * SocFieldValidator | ||
73 | */ | ||
74 | |||
75 | SocFieldValidator::SocFieldValidator(QObject *parent) | ||
76 | :QValidator(parent) | ||
77 | { | ||
78 | m_field.first_bit = 0; | ||
79 | m_field.last_bit = 31; | ||
80 | } | ||
81 | |||
82 | SocFieldValidator::SocFieldValidator(const soc_reg_field_t& field, QObject *parent) | ||
83 | :QValidator(parent), m_field(field) | ||
84 | { | ||
85 | } | ||
86 | |||
87 | void SocFieldValidator::fixup(QString& input) const | ||
88 | { | ||
89 | input = input.trimmed(); | ||
90 | } | ||
91 | |||
92 | QValidator::State SocFieldValidator::validate(QString& input, int& pos) const | ||
93 | { | ||
94 | Q_UNUSED(pos); | ||
95 | soc_word_t val; | ||
96 | State state = parse(input, val); | ||
97 | return state; | ||
98 | } | ||
99 | |||
100 | QValidator::State SocFieldValidator::parse(const QString& input, soc_word_t& val) const | ||
101 | { | ||
102 | // the empty string is always intermediate | ||
103 | if(input.size() == 0) | ||
104 | return Intermediate; | ||
105 | // first check named values | ||
106 | State state = Invalid; | ||
107 | foreach(const soc_reg_field_value_t& value, m_field.value) | ||
108 | { | ||
109 | QString name = QString::fromLocal8Bit(value.name.c_str()); | ||
110 | // cannot be a substring if too long or empty | ||
111 | if(input.size() > name.size()) | ||
112 | continue; | ||
113 | // check equal string | ||
114 | if(input == name) | ||
115 | { | ||
116 | state = Acceptable; | ||
117 | val = value.value; | ||
118 | break; | ||
119 | } | ||
120 | // check substring | ||
121 | if(name.startsWith(input)) | ||
122 | state = Intermediate; | ||
123 | } | ||
124 | // early return for exact match | ||
125 | if(state == Acceptable) | ||
126 | return state; | ||
127 | // do a few special cases for convenience | ||
128 | if(input.compare("0x", Qt::CaseInsensitive) == 0 || | ||
129 | input.compare("0b", Qt::CaseInsensitive) == 0) | ||
130 | return Intermediate; | ||
131 | // try by parsing | ||
132 | unsigned basis, pos; | ||
133 | if(input.size() >= 2 && input.startsWith("0x", Qt::CaseInsensitive)) | ||
134 | { | ||
135 | basis = 16; | ||
136 | pos = 2; | ||
137 | } | ||
138 | else if(input.size() >= 2 && input.startsWith("0b", Qt::CaseInsensitive)) | ||
139 | { | ||
140 | basis = 2; | ||
141 | pos = 2; | ||
142 | } | ||
143 | else if(input.size() >= 2 && input.startsWith("0")) | ||
144 | { | ||
145 | basis = 8; | ||
146 | pos = 1; | ||
147 | } | ||
148 | else | ||
149 | { | ||
150 | basis = 10; | ||
151 | pos = 0; | ||
152 | } | ||
153 | bool ok = false; | ||
154 | unsigned long v = input.mid(pos).toULong(&ok, basis); | ||
155 | // if not ok, return result of name parsing | ||
156 | if(!ok) | ||
157 | return state; | ||
158 | // if ok, check if it fits in the number of bits | ||
159 | unsigned nr_bits = m_field.last_bit - m_field.first_bit + 1; | ||
160 | unsigned long max = nr_bits == 32 ? 0xffffffff : (1 << nr_bits) - 1; | ||
161 | if(v <= max) | ||
162 | { | ||
163 | val = v; | ||
164 | return Acceptable; | ||
165 | } | ||
166 | |||
167 | return state; | ||
168 | } | ||
169 | |||
170 | /** | ||
171 | * RegLineEdit | ||
172 | */ | ||
173 | RegLineEdit::RegLineEdit(QWidget *parent) | ||
174 | :QWidget(parent) | ||
175 | { | ||
176 | m_layout = new QHBoxLayout(this); | ||
177 | m_button = new QToolButton(this); | ||
178 | m_button->setCursor(Qt::ArrowCursor); | ||
179 | m_button->setStyleSheet("QToolButton { font-weight: bold; color: white; background: black; }"); | ||
180 | m_button->setPopupMode(QToolButton::InstantPopup); | ||
181 | m_edit = new QLineEdit(this); | ||
182 | m_layout->addWidget(m_button); | ||
183 | m_layout->addWidget(m_edit); | ||
184 | m_menu = new QMenu(this); | ||
185 | connect(m_menu->addAction("Write"), SIGNAL(triggered()), this, SLOT(OnWriteAct())); | ||
186 | connect(m_menu->addAction("Set"), SIGNAL(triggered()), this, SLOT(OnSetAct())); | ||
187 | connect(m_menu->addAction("Clear"), SIGNAL(triggered()), this, SLOT(OnClearAct())); | ||
188 | connect(m_menu->addAction("Toggle"), SIGNAL(triggered()), this, SLOT(OnToggleAct())); | ||
189 | EnableSCT(false); | ||
190 | SetReadOnly(false); | ||
191 | ShowMode(true); | ||
192 | SetMode(Write); | ||
193 | } | ||
194 | |||
195 | void RegLineEdit::SetReadOnly(bool ro) | ||
196 | { | ||
197 | m_edit->setReadOnly(ro); | ||
198 | m_readonly = ro; | ||
199 | ShowMode(!ro); | ||
200 | } | ||
201 | |||
202 | void RegLineEdit::EnableSCT(bool en) | ||
203 | { | ||
204 | m_has_sct = en; | ||
205 | if(!m_has_sct) | ||
206 | { | ||
207 | m_button->setMenu(0); | ||
208 | SetMode(Write); | ||
209 | } | ||
210 | else | ||
211 | m_button->setMenu(m_menu); | ||
212 | } | ||
213 | |||
214 | RegLineEdit::~RegLineEdit() | ||
215 | { | ||
216 | } | ||
217 | |||
218 | QLineEdit *RegLineEdit::GetLineEdit() | ||
219 | { | ||
220 | return m_edit; | ||
221 | } | ||
222 | |||
223 | void RegLineEdit::ShowMode(bool show) | ||
224 | { | ||
225 | if(show) | ||
226 | m_button->show(); | ||
227 | else | ||
228 | m_button->hide(); | ||
229 | } | ||
230 | |||
231 | void RegLineEdit::OnWriteAct() | ||
232 | { | ||
233 | SetMode(Write); | ||
234 | } | ||
235 | |||
236 | void RegLineEdit::OnSetAct() | ||
237 | { | ||
238 | SetMode(Set); | ||
239 | } | ||
240 | |||
241 | void RegLineEdit::OnClearAct() | ||
242 | { | ||
243 | SetMode(Clear); | ||
244 | } | ||
245 | |||
246 | void RegLineEdit::OnToggleAct() | ||
247 | { | ||
248 | SetMode(Toggle); | ||
249 | } | ||
250 | |||
251 | void RegLineEdit::SetMode(EditMode mode) | ||
252 | { | ||
253 | m_mode = mode; | ||
254 | switch(m_mode) | ||
255 | { | ||
256 | case Write: m_button->setText("WR"); break; | ||
257 | case Set: m_button->setText("SET"); break; | ||
258 | case Clear: m_button->setText("CLR"); break; | ||
259 | case Toggle: m_button->setText("TOG"); break; | ||
260 | default: break; | ||
261 | } | ||
262 | } | ||
263 | |||
264 | RegLineEdit::EditMode RegLineEdit::GetMode() | ||
265 | { | ||
266 | return m_mode; | ||
267 | } | ||
268 | |||
269 | void RegLineEdit::setText(const QString& text) | ||
270 | { | ||
271 | m_edit->setText(text); | ||
272 | } | ||
273 | |||
274 | QString RegLineEdit::text() const | ||
275 | { | ||
276 | return m_edit->text(); | ||
277 | } | ||
278 | |||
279 | /** | ||
280 | * SocFieldItemDelegate | ||
281 | */ | ||
282 | |||
283 | QString SocFieldItemDelegate::displayText(const QVariant& value, const QLocale& locale) const | ||
284 | { | ||
285 | if(value.type() == QVariant::UInt) | ||
286 | return QString("0x%1").arg(value.toUInt(), (m_bitcount + 3) / 4, 16, QChar('0')); | ||
287 | else | ||
288 | return QStyledItemDelegate::displayText(value, locale); | ||
289 | } | ||
290 | |||
291 | /** | ||
292 | * SocFieldEditor | ||
293 | */ | ||
294 | SocFieldEditor::SocFieldEditor(const soc_reg_field_t& field, QWidget *parent) | ||
295 | :QLineEdit(parent), m_reg_field(field) | ||
296 | { | ||
297 | m_validator = new SocFieldValidator(field); | ||
298 | setValidator(m_validator); | ||
299 | } | ||
300 | |||
301 | SocFieldEditor::~SocFieldEditor() | ||
302 | { | ||
303 | delete m_validator; | ||
304 | } | ||
305 | |||
306 | uint SocFieldEditor::field() const | ||
307 | { | ||
308 | soc_word_t v; | ||
309 | /* in case validator fails to parse, return old value */ | ||
310 | if(m_validator->parse(text(), v) == QValidator::Acceptable) | ||
311 | return v; | ||
312 | else | ||
313 | return m_field; | ||
314 | } | ||
315 | |||
316 | void SocFieldEditor::setField(uint field) | ||
317 | { | ||
318 | m_field = field; | ||
319 | int digits = (m_reg_field.last_bit - m_reg_field.first_bit + 4) / 4; | ||
320 | setText(QString("0x%1").arg(field, digits, 16, QChar('0'))); | ||
321 | } | ||
322 | |||
323 | /** | ||
324 | * SocFieldEditorCreator | ||
325 | */ | ||
326 | QWidget *SocFieldEditorCreator::createWidget(QWidget *parent) const | ||
327 | { | ||
328 | return new SocFieldEditor(m_field, parent); | ||
329 | } | ||
330 | |||
331 | QByteArray SocFieldEditorCreator::valuePropertyName() const | ||
332 | { | ||
333 | return QByteArray("field"); | ||
334 | } | ||
335 | |||
336 | /** | ||
337 | * RegSexyDisplay | ||
338 | */ | ||
339 | RegSexyDisplay::RegSexyDisplay(const SocRegRef& reg, QWidget *parent) | ||
340 | :QWidget(parent), m_reg(reg) | ||
341 | { | ||
342 | m_size = QSize(); | ||
343 | } | ||
344 | |||
345 | int RegSexyDisplay::separatorSize() const | ||
346 | { | ||
347 | return 1; | ||
348 | } | ||
349 | |||
350 | int RegSexyDisplay::marginSize() const | ||
351 | { | ||
352 | return fontMetrics().height() / 3; | ||
353 | } | ||
354 | |||
355 | int RegSexyDisplay::textSep() const | ||
356 | { | ||
357 | return marginSize() / 2; | ||
358 | } | ||
359 | |||
360 | int RegSexyDisplay::headerHeight() const | ||
361 | { | ||
362 | return 2 * marginSize() + textSep() + 2 * fontMetrics().height(); | ||
363 | } | ||
364 | |||
365 | int RegSexyDisplay::columnWidth() const | ||
366 | { | ||
367 | return 2 * marginSize() + fontMetrics().height(); | ||
368 | } | ||
369 | |||
370 | int RegSexyDisplay::maxContentHeight() const | ||
371 | { | ||
372 | int max = 0; | ||
373 | QFontMetrics metrics = fontMetrics(); | ||
374 | for(size_t i = 0; i < m_reg.GetReg().field.size(); i++) | ||
375 | { | ||
376 | QString s = QString::fromStdString(m_reg.GetReg().field[i].name); | ||
377 | // add extra spaces arounds | ||
378 | s = " " + s + " "; | ||
379 | max = qMax(max, metrics.boundingRect(s).width()); | ||
380 | } | ||
381 | return 2 * marginSize() + max; | ||
382 | } | ||
383 | |||
384 | int RegSexyDisplay::gapHeight() const | ||
385 | { | ||
386 | return marginSize() / 2; | ||
387 | } | ||
388 | |||
389 | QSize RegSexyDisplay::minimumSizeHint() const | ||
390 | { | ||
391 | /* cache computation because it's expensive */ | ||
392 | if(m_size.isValid()) | ||
393 | return m_size; | ||
394 | /* width: display 32 columns + 33 vertical separators */ | ||
395 | m_size.setWidth(32 * columnWidth() + 33 * separatorSize()); | ||
396 | /* height: one separator + two digits + one separator + margin + separator | ||
397 | * + names + separator */ | ||
398 | m_size.setHeight(4 * separatorSize() + headerHeight() + gapHeight() + maxContentHeight()); | ||
399 | return m_size; | ||
400 | } | ||
401 | |||
402 | QSize RegSexyDisplay::sizeHint() const | ||
403 | { | ||
404 | return minimumSizeHint(); | ||
405 | } | ||
406 | |||
407 | void RegSexyDisplay::paintEvent(QPaintEvent *event) | ||
408 | { | ||
409 | // FIXME could be optimised with QStaticText | ||
410 | Q_UNUSED(event); | ||
411 | int txt_h = fontMetrics().height(); | ||
412 | int sep_sz = separatorSize(); | ||
413 | int w = width(); | ||
414 | int h = height() - 1; | ||
415 | int col_w = (w - 33 * sep_sz) / 32; | ||
416 | int hdr_h = headerHeight(); | ||
417 | int gap_h = gapHeight(); | ||
418 | int tot_w = 33 * sep_sz + 32 * col_w; | ||
419 | int margin = marginSize(); | ||
420 | int txt_sep = textSep(); | ||
421 | int tot_hdr_sz = 2 * sep_sz + hdr_h; | ||
422 | // computer xshift | ||
423 | int x_shift = (w - tot_w) / 2; | ||
424 | #define ith_col_x(i) (x_shift + (i) * (sep_sz + col_w)) | ||
425 | |||
426 | QPainter painter(this); | ||
427 | QBrush back_brush = palette().base(); | ||
428 | QBrush line_brush = palette().dark(); | ||
429 | |||
430 | // fill interesting zone with base | ||
431 | painter.fillRect(x_shift, 0, tot_w, h, back_brush); | ||
432 | |||
433 | // draw top and bottom lines | ||
434 | painter.setPen(QPen(palette().dark(), sep_sz)); | ||
435 | painter.fillRect(x_shift, 0, tot_w, sep_sz, line_brush); | ||
436 | painter.fillRect(x_shift, h - sep_sz, tot_w, sep_sz, line_brush); | ||
437 | // draw intemediate lines | ||
438 | for(int i = 0; i <= 32; i++) | ||
439 | painter.fillRect(ith_col_x(i), 0, sep_sz, 2 * sep_sz + hdr_h, line_brush); | ||
440 | // draw bottom header lines | ||
441 | painter.fillRect(ith_col_x(0), sep_sz + hdr_h, tot_w, sep_sz, line_brush); | ||
442 | painter.fillRect(ith_col_x(0), tot_hdr_sz + gap_h, tot_w, sep_sz, line_brush); | ||
443 | // redraw some lines but wider | ||
444 | for(int i = 4; i < 32; i += 4) | ||
445 | painter.fillRect(ith_col_x(i) - sep_sz, 0, 3 * sep_sz, tot_hdr_sz, line_brush); | ||
446 | // draw numbers in the header | ||
447 | painter.setPen(palette().brush(QPalette::ButtonText).color()); | ||
448 | for(int i = 0; i < 32; i++) | ||
449 | { | ||
450 | QRect r(ith_col_x(i), sep_sz + margin, col_w, txt_h); | ||
451 | painter.drawText(r, Qt::AlignCenter, QString("%1").arg((31 - i) / 10)); | ||
452 | r.translate(0, txt_h + txt_sep); | ||
453 | painter.drawText(r, Qt::AlignCenter, QString("%1").arg((31 - i) % 10)); | ||
454 | } | ||
455 | // display content | ||
456 | for(size_t i = 0; i < m_reg.GetReg().field.size(); i++) | ||
457 | { | ||
458 | const soc_reg_field_t& field = m_reg.GetReg().field[i]; | ||
459 | QRect r(QPoint(ith_col_x(31 - field.last_bit) + sep_sz, tot_hdr_sz), | ||
460 | QPoint(ith_col_x(32 - field.first_bit), h - sep_sz)); | ||
461 | painter.fillRect(r.x() - sep_sz, r.y(), sep_sz, r.height(), line_brush); | ||
462 | painter.fillRect(r.right(), r.y(), sep_sz, r.height(), line_brush); | ||
463 | r.setY(r.y() + gap_h + sep_sz); | ||
464 | // draw rotated text | ||
465 | painter.save(); | ||
466 | painter.translate(r.bottomLeft()); | ||
467 | painter.rotate(-90); | ||
468 | //painter.fillRect(QRect(0, 0, r.height(), r.width()), QBrush(Qt::red)); | ||
469 | QRect r2(0, 0, r.height(), r.width()); | ||
470 | painter.drawText(r2, Qt::AlignCenter, QString::fromStdString(field.name)); | ||
471 | painter.restore(); | ||
472 | } | ||
473 | #undef ith_col_x | ||
474 | } | ||
475 | |||
476 | /** | ||
477 | * GrowingTextEdit | ||
478 | */ | ||
479 | GrowingTextEdit::GrowingTextEdit(QWidget *parent) | ||
480 | :QTextEdit(parent) | ||
481 | { | ||
482 | connect(this, SIGNAL(textChanged()), this, SLOT(TextChanged())); | ||
483 | setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); | ||
484 | setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); | ||
485 | } | ||
486 | |||
487 | void GrowingTextEdit::TextChanged() | ||
488 | { | ||
489 | int content_size = document()->documentLayout()->documentSize().height(); | ||
490 | content_size = qMax(content_size, fontMetrics().height()); | ||
491 | setFixedHeight(content_size + contentsMargins().top() + contentsMargins().bottom()); | ||
492 | } | ||
493 | |||
494 | /** | ||
495 | * GrowingTableWidget | ||
496 | */ | ||
497 | GrowingTableWidget::GrowingTableWidget(QWidget *parent) | ||
498 | :QTableWidget(parent) | ||
499 | { | ||
500 | connect(model(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), | ||
501 | this, SLOT(DataChanged(const QModelIndex&, const QModelIndex&))); | ||
502 | } | ||
503 | |||
504 | void GrowingTableWidget::DataChanged(const QModelIndex& tl, const QModelIndex& br) | ||
505 | { | ||
506 | Q_UNUSED(tl); | ||
507 | Q_UNUSED(br); | ||
508 | resizeRowsToContents(); | ||
509 | resizeColumnsToContents(); | ||
510 | int h = contentsMargins().top() + contentsMargins().bottom(); | ||
511 | h += horizontalHeader()->height(); | ||
512 | for(int i = 0; i < rowCount(); i++) | ||
513 | h += rowHeight(i); | ||
514 | setMinimumHeight(h); | ||
515 | } | ||
516 | |||
517 | /** | ||
518 | * MyTextEditor | ||
519 | */ | ||
520 | MyTextEditor::MyTextEditor(QWidget *parent) | ||
521 | :QWidget(parent) | ||
522 | { | ||
523 | QVBoxLayout *layout = new QVBoxLayout; | ||
524 | m_toolbar = new QToolBar(this); | ||
525 | m_edit = new QTextEdit(this); | ||
526 | layout->addWidget(m_toolbar, 0); | ||
527 | layout->addWidget(m_edit, 1); | ||
528 | setLayout(layout); | ||
529 | setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); | ||
530 | |||
531 | m_edit->setAcceptRichText(false); | ||
532 | m_edit->setAutoFormatting(QTextEdit::AutoAll); | ||
533 | |||
534 | m_bold_button = new QToolButton(this); | ||
535 | m_bold_button->setIcon(QIcon::fromTheme("format-text-bold")); | ||
536 | m_bold_button->setText("bold"); | ||
537 | m_bold_button->setCheckable(true); | ||
538 | |||
539 | m_italic_button = new QToolButton(this); | ||
540 | m_italic_button->setIcon(QIcon::fromTheme("format-text-italic")); | ||
541 | m_italic_button->setText("italic"); | ||
542 | m_italic_button->setCheckable(true); | ||
543 | |||
544 | m_underline_button = new QToolButton(this); | ||
545 | m_underline_button->setIcon(QIcon::fromTheme("format-text-underline")); | ||
546 | m_underline_button->setText("underline"); | ||
547 | m_underline_button->setCheckable(true); | ||
548 | |||
549 | m_toolbar->addWidget(m_bold_button); | ||
550 | m_toolbar->addWidget(m_italic_button); | ||
551 | m_toolbar->addWidget(m_underline_button); | ||
552 | |||
553 | connect(m_bold_button, SIGNAL(toggled(bool)), this, SLOT(OnTextBold(bool))); | ||
554 | connect(m_italic_button, SIGNAL(toggled(bool)), this, SLOT(OnTextItalic(bool))); | ||
555 | connect(m_underline_button, SIGNAL(toggled(bool)), this, SLOT(OnTextUnderline(bool))); | ||
556 | connect(m_edit, SIGNAL(textChanged()), this, SLOT(OnInternalTextChanged())); | ||
557 | connect(m_edit, SIGNAL(currentCharFormatChanged(const QTextCharFormat&)), | ||
558 | this, SLOT(OnCharFormatChanged(const QTextCharFormat&))); | ||
559 | |||
560 | SetGrowingMode(false); | ||
561 | SetReadOnly(false); | ||
562 | } | ||
563 | |||
564 | void MyTextEditor::SetReadOnly(bool en) | ||
565 | { | ||
566 | m_read_only = en; | ||
567 | if(en) | ||
568 | m_toolbar->hide(); | ||
569 | else | ||
570 | m_toolbar->hide(); | ||
571 | m_edit->setReadOnly(en); | ||
572 | } | ||
573 | |||
574 | void MyTextEditor::SetGrowingMode(bool en) | ||
575 | { | ||
576 | m_growing_mode = en; | ||
577 | if(en) | ||
578 | { | ||
579 | m_edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); | ||
580 | m_edit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); | ||
581 | OnTextChanged(); | ||
582 | } | ||
583 | else | ||
584 | { | ||
585 | m_edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); | ||
586 | m_edit->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); | ||
587 | } | ||
588 | } | ||
589 | |||
590 | void MyTextEditor::OnInternalTextChanged() | ||
591 | { | ||
592 | if(m_growing_mode) | ||
593 | { | ||
594 | int content_size = m_edit->document()->documentLayout()->documentSize().height(); | ||
595 | content_size = qMax(content_size, m_edit->fontMetrics().height()); | ||
596 | m_edit->setMinimumHeight(content_size + m_edit->contentsMargins().top() + | ||
597 | m_edit->contentsMargins().bottom()); | ||
598 | } | ||
599 | emit OnTextChanged(); | ||
600 | } | ||
601 | |||
602 | void MyTextEditor::OnTextBold(bool checked) | ||
603 | { | ||
604 | QTextCursor cursor = m_edit->textCursor(); | ||
605 | QTextCharFormat fmt = cursor.charFormat(); | ||
606 | fmt.setFontWeight(checked ? QFont::Bold : QFont::Normal); | ||
607 | cursor.setCharFormat(fmt); | ||
608 | m_edit->setTextCursor(cursor); | ||
609 | } | ||
610 | |||
611 | void MyTextEditor::OnTextItalic(bool checked) | ||
612 | { | ||
613 | QTextCursor cursor = m_edit->textCursor(); | ||
614 | QTextCharFormat fmt = cursor.charFormat(); | ||
615 | fmt.setFontItalic(checked); | ||
616 | cursor.setCharFormat(fmt); | ||
617 | m_edit->setTextCursor(cursor); | ||
618 | } | ||
619 | |||
620 | void MyTextEditor::OnTextUnderline(bool checked) | ||
621 | { | ||
622 | QTextCursor cursor = m_edit->textCursor(); | ||
623 | QTextCharFormat fmt = cursor.charFormat(); | ||
624 | fmt.setFontUnderline(checked); | ||
625 | cursor.setCharFormat(fmt); | ||
626 | m_edit->setTextCursor(cursor); | ||
627 | } | ||
628 | |||
629 | void MyTextEditor::OnCharFormatChanged(const QTextCharFormat& fmt) | ||
630 | { | ||
631 | /* NOTE: changing the button states programmaticaly doesn't trigger | ||
632 | * the toggled() signals, otherwise it would result in a loop | ||
633 | * between this function and OnText{Bold,Italic,Underline,...} */ | ||
634 | m_bold_button->setChecked(fmt.fontWeight() > QFont::Normal); | ||
635 | m_italic_button->setChecked(fmt.fontItalic()); | ||
636 | m_underline_button->setChecked(fmt.fontUnderline()); | ||
637 | } | ||
638 | |||
639 | void MyTextEditor::SetTextHtml(const QString& text) | ||
640 | { | ||
641 | m_edit->setHtml(text); | ||
642 | } | ||
643 | |||
644 | QString MyTextEditor::GetTextHtml() | ||
645 | { | ||
646 | return m_edit->toPlainText(); | ||
647 | } | ||
648 | |||
649 | bool MyTextEditor::IsModified() | ||
650 | { | ||
651 | return m_edit->document()->isModified(); | ||
652 | } | ||
653 | |||
654 | /** | ||
655 | * MySwitchableTextEditor | ||
656 | */ | ||
657 | MySwitchableTextEditor::MySwitchableTextEditor(QWidget *parent) | ||
658 | :QWidget(parent) | ||
659 | { | ||
660 | QVBoxLayout *layout = new QVBoxLayout(this); | ||
661 | m_edit = new MyTextEditor(this); | ||
662 | m_label = new QLabel(this); | ||
663 | m_label->setTextFormat(Qt::RichText); | ||
664 | m_label->setAlignment(Qt::AlignTop); | ||
665 | m_line = new QLineEdit(this); | ||
666 | |||
667 | layout->addWidget(m_label); | ||
668 | layout->addWidget(m_edit); | ||
669 | layout->addWidget(m_line); | ||
670 | |||
671 | setLayout(layout); | ||
672 | |||
673 | m_editor_mode = false; | ||
674 | m_line_mode = false; | ||
675 | UpdateVisibility(); | ||
676 | } | ||
677 | |||
678 | void MySwitchableTextEditor::SetEditorMode(bool edit) | ||
679 | { | ||
680 | if(edit == m_editor_mode) | ||
681 | return; | ||
682 | QString text = GetTextHtml(); | ||
683 | m_editor_mode = edit; | ||
684 | UpdateVisibility(); | ||
685 | SetTextHtml(text); | ||
686 | } | ||
687 | |||
688 | QString MySwitchableTextEditor::GetTextHtml() | ||
689 | { | ||
690 | if(m_editor_mode) | ||
691 | return m_line_mode ? m_line->text() : m_edit->GetTextHtml(); | ||
692 | else | ||
693 | return m_label->text(); | ||
694 | } | ||
695 | |||
696 | void MySwitchableTextEditor::SetTextHtml(const QString& text) | ||
697 | { | ||
698 | if(m_editor_mode) | ||
699 | { | ||
700 | if(m_line_mode) | ||
701 | m_line->setText(text); | ||
702 | else | ||
703 | m_edit->SetTextHtml(text); | ||
704 | } | ||
705 | else | ||
706 | m_label->setText(text); | ||
707 | } | ||
708 | |||
709 | MyTextEditor *MySwitchableTextEditor::GetEditor() | ||
710 | { | ||
711 | return m_edit; | ||
712 | } | ||
713 | |||
714 | void MySwitchableTextEditor::SetLineMode(bool en) | ||
715 | { | ||
716 | if(m_line_mode == en) | ||
717 | return; | ||
718 | QString text = GetTextHtml(); | ||
719 | m_line_mode = en; | ||
720 | SetTextHtml(text); | ||
721 | UpdateVisibility(); | ||
722 | } | ||
723 | |||
724 | QLineEdit *MySwitchableTextEditor::GetLineEdit() | ||
725 | { | ||
726 | return m_line; | ||
727 | } | ||
728 | |||
729 | void MySwitchableTextEditor::UpdateVisibility() | ||
730 | { | ||
731 | m_label->setVisible(!m_editor_mode); | ||
732 | m_edit->setVisible(m_editor_mode && !m_line_mode); | ||
733 | m_line->setVisible(m_editor_mode && m_line_mode); | ||
734 | } | ||
735 | |||
736 | QLabel *MySwitchableTextEditor::GetLabel() | ||
737 | { | ||
738 | return m_label; | ||
739 | } | ||
740 | |||
741 | bool MySwitchableTextEditor::IsModified() | ||
742 | { | ||
743 | if(!m_editor_mode) | ||
744 | return false; | ||
745 | return m_line_mode ? m_line->isModified() : m_edit->IsModified(); | ||
746 | } | ||