source: projects/punch-card/punch-card-editor/src/app/decktexteditor.cc @ 48

Last change on this file since 48 was 48, checked in by sven, 10 years ago
  • Fixed more bugs
  • Cardeditor is now capable of writing text live using Codecs
  • Toolbars for Cardeditor, Codec changing menu
  • etc...
File size: 12.2 KB
Line 
1#include "decktexteditor.h"
2
3#include <QPainter>
4#include <QTextBlock>
5#include <QBoxLayout>
6#include <QLabel>
7#include <QComboBox>
8#include <QPushButton>
9
10using namespace QPunchCard;
11
12TextEditorDock::TextEditorDock(EditorWindow* parent) : QDockWidget(parent), main(parent) {
13        // Title grundsaetzlich angeben (wird ueberall angezeigt)
14        setWindowTitle(tr("Text Editor"));
15
16        editor = new DeckTextEditor(main);
17
18        // Signal weiterleiten
19        connect(editor, SIGNAL(cursorRowChanged(DeckIndex)), this, SIGNAL(cardSelected(DeckIndex)));
20        connect(main, SIGNAL(contentsChanged(DeckIndex,DeckIndex)), editor, SLOT(contentsChanged(DeckIndex,DeckIndex)));
21
22        // urhm... Codebar erstellen
23        create_code_bar();
24        create_color_bar();
25
26        // Container-Widget erstellen
27        QWidget* container = new QWidget(this);
28        QVBoxLayout* layout = new QVBoxLayout(container);
29
30        //layout->addWidget(code_bar);
31        layout->addWidget(editor);
32        layout->addWidget(color_bar);
33
34        setWidget(container);
35        setTitleBarWidget(code_bar);
36        setFeatures(QDockWidget::AllDockWidgetFeatures);
37}
38
39void TextEditorDock::create_code_bar() {
40        code_bar = new QWidget(this);
41        QBoxLayout* layout = new QBoxLayout(QBoxLayout::LeftToRight, code_bar);
42
43        // Label fuer Fenster
44        layout->addWidget(new QLabel(tr("Card Text"), code_bar));
45
46        // Codec-Auswahlbox
47        QComboBox* codec_selection = new QComboBox(code_bar);
48        codec_selection->addItems( CodecFactory::availableCodecs() );
49        codec_selection->setStatusTip(tr("Use another codec to display the decks contents as ASCII text. This won't change the cards contents"));
50        connect(codec_selection, SIGNAL(currentIndexChanged(QString)), this, SLOT(setCodec(QString)));
51        layout->addWidget(codec_selection);
52
53        // Button zur Codec-Umwandlung
54        QPushButton* text_converter = new QPushButton(tr("Convert text..."), code_bar);
55        text_converter->setStatusTip(tr("Convert the current deck (or selection) to another punch card code"));
56        connect(text_converter, SIGNAL(clicked()), this, SLOT(showTextConverterDialog()));
57        layout->addWidget(text_converter);
58}
59
60void TextEditorDock::create_color_bar() {
61        color_bar = new QWidget(this);
62        // usw.
63}
64
65void TextEditorDock::setCard(DeckIndex i) {
66        editor->highlightRow(i);
67}
68
69void TextEditorDock::setCodec(QString by_name) {
70        // QSharedPointer: Durch Assignment wird das alte Objekt geloescht :-)
71        editor->codec = QSharedPointer<const Codec>( CodecFactory::createCodec(by_name) );
72        // jetzt: Text komplett neu auswerten. Todo...
73}
74
75void TextEditorDock::showTextConverterDialog() {
76        qDebug("Text Converter Dialog... PENDING");
77}
78
79void DeckTextEditor::contentsChanged(DeckIndex lower_card, DeckIndex upper_card) {
80        // translate indices to text rows and load new text
81        qDebug("Loading new text between [%d, %d]", (int)lower_card, (int)upper_card);
82        for(int i = lower_card; i < upper_card; i++) {
83                DeckIndex index(main->deck, i);
84                if(!index.isValid()) {
85                        qDebug("text update: skipping invalid %d", i);
86                        continue;
87                }
88
89                // Ganzen Block markieren und ueberschreiben
90                QTextCursor cursor( this->document()->findBlockByNumber(i) );
91                cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
92                // an dieser Stelle: Ungueltige Zeichen maskieren (per anderem QTextCharFormat)!
93                cursor.insertText(QString("Zeile %1 von Lochkarte").arg(i));
94        }
95}
96
97DeckTextEditor::DeckTextEditor(EditorWindow* main) : QPlainTextEdit(main), main(main) {
98        row_area = new NumberArea(this);
99        col_area = new NumberArea(this);
100
101        connect(this->document(), SIGNAL(contentsChange(int,int,int)),
102                this, SLOT(translateChanges(int, int, int)));
103
104        // Codec erstellen
105        codec = QSharedPointer<const Codec>( CodecFactory::createCodec("o29_code") );
106        if(!codec) {
107                qDebug("Got NULL Codec :-(");
108        }
109
110        // Font einstellen
111        QFont font;
112        font.setFamily("Courier");
113        font.setFixedPitch(true);
114        font.setPointSize(13);
115        setFont(font);
116
117        QFont col_font = font;
118        col_font.setPointSize(8);
119        col_area->setFont(col_font);
120
121        connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int)));
122        connect(this, SIGNAL(updateRequest(const QRect &, int)), this, SLOT(updateLineNumberArea(const QRect &, int)));
123        connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentCursorPos()));
124
125        updateLineNumberAreaWidth(0);
126        highlightCurrentCursorPos();
127}
128
129void DeckTextEditor::translateChanges(int position, int charsRemoved, int charsAdded) {
130        // Dieser slot wird durch das QTextDocument aufgerufen, wenn der Benutzer
131        // den Inhalt geaendert hat. => Uebersetzen ins Kartenmodell und als Signal
132        // weitergeben.
133        Q_ASSERT(main != 0);
134        Q_ASSERT(main->deck != 0);
135        // NATÜRLICH muss ZUSAETZLICH auch noch das MODEL DIREKT VERAENDERT werden.
136        // also die betreffenden Karten AUSTAUSCHEN durch den neuen Inhalt, der dann
137        // per Codec uebersetzt wird!
138        qDebug("Pos: %d, removed: %d, added: %d", position, charsRemoved, charsAdded);
139        // veraenderte Zeilen ausrechnen
140        int start_pos = position;
141        int end_pos = position + charsAdded - charsRemoved;
142        QTextBlock start_block = document()->findBlock(start_pos);
143        QTextBlock end_block = document()->findBlock(end_pos);
144        DeckIndex start_block_number = main->deck->createIndex( start_block.blockNumber() );
145        DeckIndex end_block_number = main->deck->createIndex( end_block.blockNumber() );
146
147        if(! start_block.isValid()) {
148                // keine gute Grundlage.
149                qDebug("Pretty bad: Start block pos is invalid.");
150                return;
151        }
152        if(! start_block_number.isValid()) {
153                // auch keine gute Grundlage
154                qDebug("Pretty bad: Start deck index pos is invalid");
155                qDebug() << start_block_number;
156                return;
157        }
158
159        if(! end_block.isValid()) {
160                // das Dokument ist *kuerzer* geworden
161                DeckIndex new_max_blocks = main->deck->createIndex( document()->lineCount() );
162                // sicherheitshalber
163                if(! new_max_blocks.isValid()) {
164                        qDebug() << "new max blocks is invalid " << new_max_blocks;
165                        return;
166                }
167
168                // Alle Blocks von new_max_blocks bis end_block_number loeschen
169                main->deck->erase(new_max_blocks, end_block_number);
170        }
171
172        // start_block und end_block sind innerhalb des Dokumentes.
173        // pruefe, ob Dokument laenger geworden ist
174
175        if(end_block_number > main->deck->count()) {
176                // ja, es sind
177                int new_lines_count = main->deck->count() - end_block_number;
178                // Reihen dazugekommen.
179                qDebug("Inserting %d cards at end", new_lines_count);
180                main->deck->insertTimes( main->deck->createIndex( main->deck->count()-1), new_lines_count);
181        }
182
183        // jetzt: ueber bloecke iterieren.
184        for(QTextBlock current_block = start_block;
185            current_block < end_block || current_block == end_block;
186            current_block = current_block.next()) {
187                if(!translateBlock(current_block)) {
188                        qDebug("Translation stopped at block %d, failing. breaking translation", current_block.blockNumber());
189                        break;
190                }
191        }
192}
193
194bool DeckTextEditor::translateBlock(const QTextBlock& block) {
195        Q_ASSERT(!codec.isNull());
196        Q_ASSERT(!main->deck.isNull());
197        if(! block.isValid()) {
198                // urhh
199                qDebug("translateBlock called on invalid block");
200                return false;
201        }
202
203        DeckIndex card_index = main->deck->createIndex( block.blockNumber() );
204
205        // und nun: Block uebersetzen.
206        qDebug() << "Going to translate to " << card_index;
207        QString text = block.text();
208        qDebug() << "Contents:" <<  text;
209
210        if(!card_index.isValid()) {
211                qDebug("Invalid card index, won't translate");
212                return false;
213        }
214
215        if(!codec->canEncode(text)) {
216                // gut, der text ist halt schlecht. sollte an anderer Stelle
217                // schon bemerkt worden sein
218                qDebug("Codec will make errors while translating");
219        }
220
221        // start translation
222        qDebug() << "starting translation";
223        codec->fromAscii(text, main->deck->at( card_index ) );
224        qDebug("Translated successfully");
225        // urhm... that was all the magic.
226        // send "you were changed" thingy! :-)
227        main->deck->emitChanged(card_index, card_index);
228        return true;
229}
230
231QSize DeckTextEditor::numberAreaSize(const NumberArea* area) const {
232        if(area == row_area) {
233                int digits = 1;
234                int max = qMax(1, blockCount());
235                while (max >= 10) {
236                        max /= 10;
237                        ++digits;
238                }
239
240                int space = 10 + fontMetrics().width(QLatin1Char('9')) * digits;
241
242                return QSize(space, 0);
243        } else if(area == col_area) {
244                return QSize(0, 4 + fontMetrics().height());
245        } else {
246                qDebug("Illegal area at numberAreaSize()");
247                return QSize();
248        }
249}
250
251void DeckTextEditor::updateLineNumberAreaWidth(int /* newBlockCount */) {
252        // this is called when the block count changed (line count changed)
253        setViewportMargins(numberAreaSize(row_area).width(), numberAreaSize(col_area).height(), 0, 0);
254}
255
256void DeckTextEditor::updateLineNumberArea(const QRect &rect, int dy) {
257        if (dy)
258                row_area->scroll(0, dy);
259        else
260                row_area->update(0, rect.y(), row_area->width(), rect.height());
261
262        if (rect.contains(viewport()->rect()))
263                updateLineNumberAreaWidth(0);
264 }
265
266void DeckTextEditor::resizeEvent(QResizeEvent *e) {
267        QPlainTextEdit::resizeEvent(e);
268
269        QRect cr = contentsRect();
270        row_area->setGeometry(QRect(cr.left(), cr.top(), numberAreaSize(row_area).width(), cr.height()));
271        col_area->setGeometry(QRect(cr.left(), cr.top(), cr.width(), numberAreaSize(col_area).height()));
272}
273
274void DeckTextEditor::highlightCurrentCursorPos() {
275        // emit signal: Cursor position changed!
276        emit cursorRowChanged(textCursor().blockNumber());
277        paintHighlights();
278}
279
280void DeckTextEditor::highlightRow(DeckIndex i) {
281        // externe eingabe: Highlighted_row machen und so...
282        highlighted_row = i;
283        paintHighlights();
284}
285
286void DeckTextEditor::paintHighlights() {
287        // first: Update Row Mark
288        QList<QTextEdit::ExtraSelection> extra_selections;
289
290        {
291                // Row Mark
292                QTextEdit::ExtraSelection selection;
293
294                QColor row_color = QColor(Qt::yellow).lighter(160);
295
296                selection.format.setBackground(row_color);
297                selection.format.setProperty(QTextFormat::FullWidthSelection, true);
298                selection.cursor = textCursor();
299                selection.cursor.clearSelection();
300                extra_selections.append(selection);
301        }
302        if(highlighted_row >= 0 && highlighted_row < textCursor().blockNumber()) {
303                // highlighted row mark
304                QTextEdit::ExtraSelection selection;
305                QColor row_color = QColor(Qt::blue).lighter(160);
306
307                selection.format.setBackground(row_color);
308                selection.format.setProperty(QTextFormat::FullWidthSelection, true);
309                selection.cursor = textCursor();
310                selection.cursor.clearSelection();
311                extra_selections.append(selection);
312        }
313        {
314                // Col Mark
315                QTextEdit::ExtraSelection selection_template;
316
317                QColor col_color = QColor(Qt::green).lighter(160);
318                selection_template.format.setBackground(col_color);
319
320                // ueber alle sichtbaren reihen iterieren
321                QTextBlock block = firstVisibleBlock();
322
323                while(block.isValid() && block.isVisible()) {
324                        // Eine Selection *kopieren*
325                        QTextEdit::ExtraSelection selection; // nicht mehr von selection_template
326                        selection.format.setBackground(col_color);
327                        // Cursor am Anfang des Blocks
328                        selection.cursor = QTextCursor(block);
329                        selection.cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, textCursor().columnNumber()-1);
330                        selection.cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, 1);
331                        extra_selections.append(selection);
332
333                        block = block.next();
334                }
335        }
336
337        setExtraSelections(extra_selections);
338}
339
340void DeckTextEditor::numberAreaPaintEvent(NumberArea* area, QPaintEvent *event) {
341        if(area == row_area) {
342                QPainter painter(row_area);
343                painter.fillRect(event->rect(), Qt::lightGray);
344
345
346                QTextBlock block = firstVisibleBlock();
347                int blockNumber = block.blockNumber();
348                int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
349                int bottom = top + (int) blockBoundingRect(block).height();
350
351                while (block.isValid() && top <= event->rect().bottom()) {
352                        if (block.isVisible() && bottom >= event->rect().top()) {
353                                QString number = QString::number(blockNumber + 1);
354                                painter.setPen(Qt::black);
355                                painter.drawText(0, top + col_area->height(), row_area->width(), fontMetrics().height(),
356                                        Qt::AlignRight, number);
357                        }
358
359                        block = block.next();
360                        top = bottom;
361                        bottom = top + (int) blockBoundingRect(block).height();
362                        ++blockNumber;
363                }
364        } else if(area == col_area) {
365                QPainter painter(col_area);
366                painter.fillRect(event->rect(), Qt::darkGray);
367                painter.setPen(Qt::black);
368                /*QFont font = painter.font();
369                font.setPixelSize(8);
370                painter.setFont(font);*/
371
372                int font_width = fontMetrics().width('4');
373
374                // ueber alle *zeichen* iterieren.
375                for(int i = 0; i < 80; i++) {
376                        painter.drawText(row_area->width() + i * font_width, 4, font_width, fontMetrics().height(), Qt::AlignCenter,
377                                QString::number(i));
378                }
379        } else {
380                qDebug("Illegal number area at numberAreaPaintEvent");
381        }
382}
Note: See TracBrowser for help on using the repository browser.
© 2008 - 2013 technikum29 • Sven Köppel • Some rights reserved
Powered by Trac
Expect where otherwise noted, content on this site is licensed under a Creative Commons 3.0 License