diff options
author | Dmitry Gamza <gamzad@yahoo.com> | 2014-05-12 09:59:24 +0400 |
---|---|---|
committer | Amaury Pouly <amaury.pouly@gmail.com> | 2014-05-12 19:05:19 +0200 |
commit | 440ff9500bc74cc278e0b66f528cfdf624547240 (patch) | |
tree | eed9c67fa3d282e33821267ca0d7d94bb97dc7e7 /utils/regtools/qeditor/utils.cpp | |
parent | 7b590a953639714cf8ea4b550dd7f6558698e23f (diff) | |
download | rockbox-440ff9500bc74cc278e0b66f528cfdf624547240.tar.gz rockbox-440ff9500bc74cc278e0b66f528cfdf624547240.zip |
qeditor: on Windows it’s not impossible to create a file with AUX
Change-Id: Ic7ef01328eccbed4afddb0a09d2afbb6c1f6a28f
Reviewed-on: http://gerrit.rockbox.org/811
Reviewed-by: Amaury Pouly <amaury.pouly@gmail.com>
Diffstat (limited to 'utils/regtools/qeditor/utils.cpp')
-rw-r--r-- | utils/regtools/qeditor/utils.cpp | 800 |
1 files changed, 800 insertions, 0 deletions
diff --git a/utils/regtools/qeditor/utils.cpp b/utils/regtools/qeditor/utils.cpp new file mode 100644 index 0000000000..1662169bdb --- /dev/null +++ b/utils/regtools/qeditor/utils.cpp | |||
@@ -0,0 +1,800 @@ | |||
1 | #include "utils.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 | * SocFieldCachedItemDelegate | ||
325 | */ | ||
326 | |||
327 | QString SocFieldCachedItemDelegate::displayText(const QVariant& value, const QLocale& locale) const | ||
328 | { | ||
329 | // FIXME see QTBUG-30392 | ||
330 | if(value.type() == QVariant::UserType && value.userType() == qMetaTypeId< SocFieldCachedValue >()) | ||
331 | { | ||
332 | const SocFieldCachedValue& v = value.value< SocFieldCachedValue >(); | ||
333 | int bitcount = v.field().last_bit - v.field().first_bit; | ||
334 | return QString("0x%1").arg(v.value(), (bitcount + 3) / 4, 16, QChar('0')); | ||
335 | } | ||
336 | else | ||
337 | return QStyledItemDelegate::displayText(value, locale); | ||
338 | } | ||
339 | |||
340 | /** | ||
341 | * SocFieldCachedEditor | ||
342 | */ | ||
343 | SocFieldCachedEditor::SocFieldCachedEditor(QWidget *parent) | ||
344 | :SocFieldEditor(soc_reg_field_t(), parent) | ||
345 | { | ||
346 | } | ||
347 | |||
348 | SocFieldCachedEditor::~SocFieldCachedEditor() | ||
349 | { | ||
350 | } | ||
351 | |||
352 | SocFieldCachedValue SocFieldCachedEditor::value() const | ||
353 | { | ||
354 | return SocFieldCachedValue(m_value.field(), field()); | ||
355 | } | ||
356 | |||
357 | void SocFieldCachedEditor::setValue(SocFieldCachedValue val) | ||
358 | { | ||
359 | m_value = val; | ||
360 | SetRegField(m_value.field()); | ||
361 | setField(m_value.value()); | ||
362 | } | ||
363 | |||
364 | /** | ||
365 | * SocFieldEditorCreator | ||
366 | */ | ||
367 | QWidget *SocFieldEditorCreator::createWidget(QWidget *parent) const | ||
368 | { | ||
369 | return new SocFieldEditor(m_field, parent); | ||
370 | } | ||
371 | |||
372 | QByteArray SocFieldEditorCreator::valuePropertyName() const | ||
373 | { | ||
374 | return QByteArray("field"); | ||
375 | } | ||
376 | |||
377 | /** | ||
378 | * SocFieldCachedEditorCreator | ||
379 | */ | ||
380 | QWidget *SocFieldCachedEditorCreator::createWidget(QWidget *parent) const | ||
381 | { | ||
382 | return new SocFieldCachedEditor(parent); | ||
383 | } | ||
384 | |||
385 | QByteArray SocFieldCachedEditorCreator::valuePropertyName() const | ||
386 | { | ||
387 | return QByteArray("value"); | ||
388 | } | ||
389 | |||
390 | /** | ||
391 | * RegSexyDisplay | ||
392 | */ | ||
393 | RegSexyDisplay::RegSexyDisplay(const SocRegRef& reg, QWidget *parent) | ||
394 | :QWidget(parent), m_reg(reg) | ||
395 | { | ||
396 | m_size = QSize(); | ||
397 | } | ||
398 | |||
399 | int RegSexyDisplay::separatorSize() const | ||
400 | { | ||
401 | return 1; | ||
402 | } | ||
403 | |||
404 | int RegSexyDisplay::marginSize() const | ||
405 | { | ||
406 | return fontMetrics().height() / 3; | ||
407 | } | ||
408 | |||
409 | int RegSexyDisplay::textSep() const | ||
410 | { | ||
411 | return marginSize() / 2; | ||
412 | } | ||
413 | |||
414 | int RegSexyDisplay::headerHeight() const | ||
415 | { | ||
416 | return 2 * marginSize() + textSep() + 2 * fontMetrics().height(); | ||
417 | } | ||
418 | |||
419 | int RegSexyDisplay::columnWidth() const | ||
420 | { | ||
421 | return 2 * marginSize() + fontMetrics().height(); | ||
422 | } | ||
423 | |||
424 | int RegSexyDisplay::maxContentHeight() const | ||
425 | { | ||
426 | int max = 0; | ||
427 | QFontMetrics metrics = fontMetrics(); | ||
428 | for(size_t i = 0; i < m_reg.GetReg().field.size(); i++) | ||
429 | { | ||
430 | QString s = QString::fromStdString(m_reg.GetReg().field[i].name); | ||
431 | // add extra spaces arounds | ||
432 | s = " " + s + " "; | ||
433 | max = qMax(max, metrics.boundingRect(s).width()); | ||
434 | } | ||
435 | return 2 * marginSize() + max; | ||
436 | } | ||
437 | |||
438 | int RegSexyDisplay::gapHeight() const | ||
439 | { | ||
440 | return marginSize() / 2; | ||
441 | } | ||
442 | |||
443 | QSize RegSexyDisplay::minimumSizeHint() const | ||
444 | { | ||
445 | /* cache computation because it's expensive */ | ||
446 | if(m_size.isValid()) | ||
447 | return m_size; | ||
448 | /* width: display 32 columns + 33 vertical separators */ | ||
449 | m_size.setWidth(32 * columnWidth() + 33 * separatorSize()); | ||
450 | /* height: one separator + two digits + one separator + margin + separator | ||
451 | * + names + separator */ | ||
452 | m_size.setHeight(4 * separatorSize() + headerHeight() + gapHeight() + maxContentHeight()); | ||
453 | return m_size; | ||
454 | } | ||
455 | |||
456 | QSize RegSexyDisplay::sizeHint() const | ||
457 | { | ||
458 | return minimumSizeHint(); | ||
459 | } | ||
460 | |||
461 | void RegSexyDisplay::paintEvent(QPaintEvent *event) | ||
462 | { | ||
463 | // FIXME could be optimised with QStaticText | ||
464 | Q_UNUSED(event); | ||
465 | int txt_h = fontMetrics().height(); | ||
466 | int sep_sz = separatorSize(); | ||
467 | int w = width(); | ||
468 | int h = height() - 1; | ||
469 | int col_w = (w - 33 * sep_sz) / 32; | ||
470 | int hdr_h = headerHeight(); | ||
471 | int gap_h = gapHeight(); | ||
472 | int tot_w = 33 * sep_sz + 32 * col_w; | ||
473 | int margin = marginSize(); | ||
474 | int txt_sep = textSep(); | ||
475 | int tot_hdr_sz = 2 * sep_sz + hdr_h; | ||
476 | // computer xshift | ||
477 | int x_shift = (w - tot_w) / 2; | ||
478 | #define ith_col_x(i) (x_shift + (i) * (sep_sz + col_w)) | ||
479 | |||
480 | QPainter painter(this); | ||
481 | QBrush back_brush = palette().base(); | ||
482 | QBrush line_brush = palette().dark(); | ||
483 | |||
484 | // fill interesting zone with base | ||
485 | painter.fillRect(x_shift, 0, tot_w, h, back_brush); | ||
486 | |||
487 | // draw top and bottom lines | ||
488 | painter.setPen(QPen(palette().dark(), sep_sz)); | ||
489 | painter.fillRect(x_shift, 0, tot_w, sep_sz, line_brush); | ||
490 | painter.fillRect(x_shift, h - sep_sz, tot_w, sep_sz, line_brush); | ||
491 | // draw intemediate lines | ||
492 | for(int i = 0; i <= 32; i++) | ||
493 | painter.fillRect(ith_col_x(i), 0, sep_sz, 2 * sep_sz + hdr_h, line_brush); | ||
494 | // draw bottom header lines | ||
495 | painter.fillRect(ith_col_x(0), sep_sz + hdr_h, tot_w, sep_sz, line_brush); | ||
496 | painter.fillRect(ith_col_x(0), tot_hdr_sz + gap_h, tot_w, sep_sz, line_brush); | ||
497 | // redraw some lines but wider | ||
498 | for(int i = 4; i < 32; i += 4) | ||
499 | painter.fillRect(ith_col_x(i) - sep_sz, 0, 3 * sep_sz, tot_hdr_sz, line_brush); | ||
500 | // draw numbers in the header | ||
501 | painter.setPen(palette().brush(QPalette::ButtonText).color()); | ||
502 | for(int i = 0; i < 32; i++) | ||
503 | { | ||
504 | QRect r(ith_col_x(i), sep_sz + margin, col_w, txt_h); | ||
505 | painter.drawText(r, Qt::AlignCenter, QString("%1").arg((31 - i) / 10)); | ||
506 | r.translate(0, txt_h + txt_sep); | ||
507 | painter.drawText(r, Qt::AlignCenter, QString("%1").arg((31 - i) % 10)); | ||
508 | } | ||
509 | // display content | ||
510 | for(size_t i = 0; i < m_reg.GetReg().field.size(); i++) | ||
511 | { | ||
512 | const soc_reg_field_t& field = m_reg.GetReg().field[i]; | ||
513 | QRect r(QPoint(ith_col_x(31 - field.last_bit) + sep_sz, tot_hdr_sz), | ||
514 | QPoint(ith_col_x(32 - field.first_bit), h - sep_sz)); | ||
515 | painter.fillRect(r.x() - sep_sz, r.y(), sep_sz, r.height(), line_brush); | ||
516 | painter.fillRect(r.right(), r.y(), sep_sz, r.height(), line_brush); | ||
517 | r.setY(r.y() + gap_h + sep_sz); | ||
518 | // draw rotated text | ||
519 | painter.save(); | ||
520 | painter.translate(r.bottomLeft()); | ||
521 | painter.rotate(-90); | ||
522 | //painter.fillRect(QRect(0, 0, r.height(), r.width()), QBrush(Qt::red)); | ||
523 | QRect r2(0, 0, r.height(), r.width()); | ||
524 | painter.drawText(r2, Qt::AlignCenter, QString::fromStdString(field.name)); | ||
525 | painter.restore(); | ||
526 | } | ||
527 | #undef ith_col_x | ||
528 | } | ||
529 | |||
530 | /** | ||
531 | * GrowingTextEdit | ||
532 | */ | ||
533 | GrowingTextEdit::GrowingTextEdit(QWidget *parent) | ||
534 | :QTextEdit(parent) | ||
535 | { | ||
536 | connect(this, SIGNAL(textChanged()), this, SLOT(TextChanged())); | ||
537 | setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); | ||
538 | setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); | ||
539 | } | ||
540 | |||
541 | void GrowingTextEdit::TextChanged() | ||
542 | { | ||
543 | int content_size = document()->documentLayout()->documentSize().height(); | ||
544 | content_size = qMax(content_size, fontMetrics().height()); | ||
545 | setFixedHeight(content_size + contentsMargins().top() + contentsMargins().bottom()); | ||
546 | } | ||
547 | |||
548 | /** | ||
549 | * GrowingTableWidget | ||
550 | */ | ||
551 | GrowingTableWidget::GrowingTableWidget(QWidget *parent) | ||
552 | :QTableWidget(parent) | ||
553 | { | ||
554 | connect(model(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), | ||
555 | this, SLOT(DataChanged(const QModelIndex&, const QModelIndex&))); | ||
556 | } | ||
557 | |||
558 | void GrowingTableWidget::DataChanged(const QModelIndex& tl, const QModelIndex& br) | ||
559 | { | ||
560 | Q_UNUSED(tl); | ||
561 | Q_UNUSED(br); | ||
562 | resizeRowsToContents(); | ||
563 | resizeColumnsToContents(); | ||
564 | int h = contentsMargins().top() + contentsMargins().bottom(); | ||
565 | h += horizontalHeader()->height(); | ||
566 | for(int i = 0; i < rowCount(); i++) | ||
567 | h += rowHeight(i); | ||
568 | setMinimumHeight(h); | ||
569 | } | ||
570 | |||
571 | /** | ||
572 | * MyTextEditor | ||
573 | */ | ||
574 | MyTextEditor::MyTextEditor(QWidget *parent) | ||
575 | :QWidget(parent) | ||
576 | { | ||
577 | QVBoxLayout *layout = new QVBoxLayout; | ||
578 | m_toolbar = new QToolBar(this); | ||
579 | m_edit = new QTextEdit(this); | ||
580 | layout->addWidget(m_toolbar, 0); | ||
581 | layout->addWidget(m_edit, 1); | ||
582 | setLayout(layout); | ||
583 | setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); | ||
584 | |||
585 | m_edit->setAcceptRichText(false); | ||
586 | m_edit->setAutoFormatting(QTextEdit::AutoAll); | ||
587 | |||
588 | m_bold_button = new QToolButton(this); | ||
589 | m_bold_button->setIcon(QIcon::fromTheme("format-text-bold")); | ||
590 | m_bold_button->setText("bold"); | ||
591 | m_bold_button->setCheckable(true); | ||
592 | |||
593 | m_italic_button = new QToolButton(this); | ||
594 | m_italic_button->setIcon(QIcon::fromTheme("format-text-italic")); | ||
595 | m_italic_button->setText("italic"); | ||
596 | m_italic_button->setCheckable(true); | ||
597 | |||
598 | m_underline_button = new QToolButton(this); | ||
599 | m_underline_button->setIcon(QIcon::fromTheme("format-text-underline")); | ||
600 | m_underline_button->setText("underline"); | ||
601 | m_underline_button->setCheckable(true); | ||
602 | |||
603 | m_toolbar->addWidget(m_bold_button); | ||
604 | m_toolbar->addWidget(m_italic_button); | ||
605 | m_toolbar->addWidget(m_underline_button); | ||
606 | |||
607 | connect(m_bold_button, SIGNAL(toggled(bool)), this, SLOT(OnTextBold(bool))); | ||
608 | connect(m_italic_button, SIGNAL(toggled(bool)), this, SLOT(OnTextItalic(bool))); | ||
609 | connect(m_underline_button, SIGNAL(toggled(bool)), this, SLOT(OnTextUnderline(bool))); | ||
610 | connect(m_edit, SIGNAL(textChanged()), this, SLOT(OnInternalTextChanged())); | ||
611 | connect(m_edit, SIGNAL(currentCharFormatChanged(const QTextCharFormat&)), | ||
612 | this, SLOT(OnCharFormatChanged(const QTextCharFormat&))); | ||
613 | |||
614 | SetGrowingMode(false); | ||
615 | SetReadOnly(false); | ||
616 | } | ||
617 | |||
618 | void MyTextEditor::SetReadOnly(bool en) | ||
619 | { | ||
620 | m_read_only = en; | ||
621 | if(en) | ||
622 | m_toolbar->hide(); | ||
623 | else | ||
624 | m_toolbar->hide(); | ||
625 | m_edit->setReadOnly(en); | ||
626 | } | ||
627 | |||
628 | void MyTextEditor::SetGrowingMode(bool en) | ||
629 | { | ||
630 | m_growing_mode = en; | ||
631 | if(en) | ||
632 | { | ||
633 | m_edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); | ||
634 | m_edit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); | ||
635 | OnTextChanged(); | ||
636 | } | ||
637 | else | ||
638 | { | ||
639 | m_edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); | ||
640 | m_edit->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); | ||
641 | } | ||
642 | } | ||
643 | |||
644 | void MyTextEditor::OnInternalTextChanged() | ||
645 | { | ||
646 | if(m_growing_mode) | ||
647 | { | ||
648 | int content_size = m_edit->document()->documentLayout()->documentSize().height(); | ||
649 | content_size = qMax(content_size, m_edit->fontMetrics().height()); | ||
650 | m_edit->setMinimumHeight(content_size + m_edit->contentsMargins().top() + | ||
651 | m_edit->contentsMargins().bottom()); | ||
652 | } | ||
653 | emit OnTextChanged(); | ||
654 | } | ||
655 | |||
656 | void MyTextEditor::OnTextBold(bool checked) | ||
657 | { | ||
658 | QTextCursor cursor = m_edit->textCursor(); | ||
659 | QTextCharFormat fmt = cursor.charFormat(); | ||
660 | fmt.setFontWeight(checked ? QFont::Bold : QFont::Normal); | ||
661 | cursor.setCharFormat(fmt); | ||
662 | m_edit->setTextCursor(cursor); | ||
663 | } | ||
664 | |||
665 | void MyTextEditor::OnTextItalic(bool checked) | ||
666 | { | ||
667 | QTextCursor cursor = m_edit->textCursor(); | ||
668 | QTextCharFormat fmt = cursor.charFormat(); | ||
669 | fmt.setFontItalic(checked); | ||
670 | cursor.setCharFormat(fmt); | ||
671 | m_edit->setTextCursor(cursor); | ||
672 | } | ||
673 | |||
674 | void MyTextEditor::OnTextUnderline(bool checked) | ||
675 | { | ||
676 | QTextCursor cursor = m_edit->textCursor(); | ||
677 | QTextCharFormat fmt = cursor.charFormat(); | ||
678 | fmt.setFontUnderline(checked); | ||
679 | cursor.setCharFormat(fmt); | ||
680 | m_edit->setTextCursor(cursor); | ||
681 | } | ||
682 | |||
683 | void MyTextEditor::OnCharFormatChanged(const QTextCharFormat& fmt) | ||
684 | { | ||
685 | /* NOTE: changing the button states programmaticaly doesn't trigger | ||
686 | * the toggled() signals, otherwise it would result in a loop | ||
687 | * between this function and OnText{Bold,Italic,Underline,...} */ | ||
688 | m_bold_button->setChecked(fmt.fontWeight() > QFont::Normal); | ||
689 | m_italic_button->setChecked(fmt.fontItalic()); | ||
690 | m_underline_button->setChecked(fmt.fontUnderline()); | ||
691 | } | ||
692 | |||
693 | void MyTextEditor::SetTextHtml(const QString& text) | ||
694 | { | ||
695 | m_edit->setHtml(text); | ||
696 | } | ||
697 | |||
698 | QString MyTextEditor::GetTextHtml() | ||
699 | { | ||
700 | return m_edit->toPlainText(); | ||
701 | } | ||
702 | |||
703 | bool MyTextEditor::IsModified() | ||
704 | { | ||
705 | return m_edit->document()->isModified(); | ||
706 | } | ||
707 | |||
708 | /** | ||
709 | * MySwitchableTextEditor | ||
710 | */ | ||
711 | MySwitchableTextEditor::MySwitchableTextEditor(QWidget *parent) | ||
712 | :QWidget(parent) | ||
713 | { | ||
714 | QVBoxLayout *layout = new QVBoxLayout(this); | ||
715 | m_edit = new MyTextEditor(this); | ||
716 | m_label = new QLabel(this); | ||
717 | m_label->setTextFormat(Qt::RichText); | ||
718 | m_label->setAlignment(Qt::AlignTop); | ||
719 | m_line = new QLineEdit(this); | ||
720 | |||
721 | layout->addWidget(m_label); | ||
722 | layout->addWidget(m_edit); | ||
723 | layout->addWidget(m_line); | ||
724 | |||
725 | setLayout(layout); | ||
726 | |||
727 | m_editor_mode = false; | ||
728 | m_line_mode = false; | ||
729 | UpdateVisibility(); | ||
730 | } | ||
731 | |||
732 | void MySwitchableTextEditor::SetEditorMode(bool edit) | ||
733 | { | ||
734 | if(edit == m_editor_mode) | ||
735 | return; | ||
736 | QString text = GetTextHtml(); | ||
737 | m_editor_mode = edit; | ||
738 | UpdateVisibility(); | ||
739 | SetTextHtml(text); | ||
740 | } | ||
741 | |||
742 | QString MySwitchableTextEditor::GetTextHtml() | ||
743 | { | ||
744 | if(m_editor_mode) | ||
745 | return m_line_mode ? m_line->text() : m_edit->GetTextHtml(); | ||
746 | else | ||
747 | return m_label->text(); | ||
748 | } | ||
749 | |||
750 | void MySwitchableTextEditor::SetTextHtml(const QString& text) | ||
751 | { | ||
752 | if(m_editor_mode) | ||
753 | { | ||
754 | if(m_line_mode) | ||
755 | m_line->setText(text); | ||
756 | else | ||
757 | m_edit->SetTextHtml(text); | ||
758 | } | ||
759 | else | ||
760 | m_label->setText(text); | ||
761 | } | ||
762 | |||
763 | MyTextEditor *MySwitchableTextEditor::GetEditor() | ||
764 | { | ||
765 | return m_edit; | ||
766 | } | ||
767 | |||
768 | void MySwitchableTextEditor::SetLineMode(bool en) | ||
769 | { | ||
770 | if(m_line_mode == en) | ||
771 | return; | ||
772 | QString text = GetTextHtml(); | ||
773 | m_line_mode = en; | ||
774 | SetTextHtml(text); | ||
775 | UpdateVisibility(); | ||
776 | } | ||
777 | |||
778 | QLineEdit *MySwitchableTextEditor::GetLineEdit() | ||
779 | { | ||
780 | return m_line; | ||
781 | } | ||
782 | |||
783 | void MySwitchableTextEditor::UpdateVisibility() | ||
784 | { | ||
785 | m_label->setVisible(!m_editor_mode); | ||
786 | m_edit->setVisible(m_editor_mode && !m_line_mode); | ||
787 | m_line->setVisible(m_editor_mode && m_line_mode); | ||
788 | } | ||
789 | |||
790 | QLabel *MySwitchableTextEditor::GetLabel() | ||
791 | { | ||
792 | return m_label; | ||
793 | } | ||
794 | |||
795 | bool MySwitchableTextEditor::IsModified() | ||
796 | { | ||
797 | if(!m_editor_mode) | ||
798 | return false; | ||
799 | return m_line_mode ? m_line->isModified() : m_edit->IsModified(); | ||
800 | } | ||