1/*
2 This program is free software; you can redistribute it and/or
3 modify it under the terms of the GNU General Public License
4 as published by the Free Software Foundation; either version 2
5 of the License, or (at your option) any later version.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software
14 Foundation, Inc., 51 Franklin Street, Fifth Floor,
15 Boston, MA 02110-1301, USA.
16
17 ---
18 Copyright (C) 2009 Alexander Rieder <alexanderrieder@gmail.com>
19 Copyright (C) 2012 Martin Kuettler <martin.kuettler@gmail.com>
20 */
21
22#include <QGraphicsWidget>
23#include <QTextLayout>
24#include <QTextDocument>
25#include <QTimer>
26#include <QPrinter>
27#include <QXmlQuery>
28
29#include <KMessageBox>
30#include <KStandardDirs>
31#include <KActionCollection>
32#include <KAction>
33#include <KFontAction>
34#include <KFontSizeAction>
35#include <KSelectAction>
36#include <KToggleAction>
37#include <KColorDialog>
38#include <KColorScheme>
39
40#include "config-cantor.h"
41#include "worksheet.h"
42#include "settings.h"
43#include "commandentry.h"
44#include "textentry.h"
45#include "latexentry.h"
46#include "imageentry.h"
47#include "pagebreakentry.h"
48#include "placeholderentry.h"
49#include "lib/backend.h"
50#include "lib/extension.h"
51#include "lib/result.h"
52#include "lib/helpresult.h"
53#include "lib/session.h"
54#include "lib/defaulthighlighter.h"
55
56const double Worksheet::LeftMargin = 4;
57const double Worksheet::RightMargin = 4;
58const double Worksheet::TopMargin = 4;
59
60Worksheet::Worksheet(Cantor::Backend* backend, QWidget* parent)
61 : QGraphicsScene(parent)
62{
63 m_session = backend->createSession();
64 m_highlighter = 0;
65
66 m_firstEntry = 0;
67 m_lastEntry = 0;
68 m_lastFocusedTextItem = 0;
69 m_dragEntry = 0;
70 m_placeholderEntry = 0;
71 m_viewWidth = 0;
72 m_protrusion = 0;
73 m_dragScrollTimer = 0;
74
75 m_isPrinting = false;
76 m_loginFlag = true;
77 QTimer::singleShot(0, this, SLOT(loginToSession()));
78}
79
80Worksheet::~Worksheet()
81{
82 // This is necessary, because a SeachBar might access firstEntry()
83 // while the scene is deleted. Maybe there is a better solution to
84 // this problem, but I can't seem to find it.
85 m_firstEntry = 0;
86 m_session->logout();
87}
88
89void Worksheet::loginToSession()
90{
91 if(m_loginFlag==true)
92 {
93 m_session->login();
94
95 enableHighlighting(Settings::self()->highlightDefault());
96 enableCompletion(Settings::self()->completionDefault());
97 enableExpressionNumbering(Settings::self()->expressionNumberingDefault());
98 enableAnimations(Settings::self()->animationDefault());
99#ifdef WITH_EPS
100 session()->setTypesettingEnabled(Settings::self()->typesetDefault());
101#else
102 session()->setTypesettingEnabled(false);
103#endif
104
105 m_loginFlag=false;
106 }
107}
108
109void Worksheet::print(QPrinter* printer)
110{
111 m_epsRenderer.useHighResolution(true);
112 m_isPrinting = true;
113 QRect pageRect = printer->pageRect();
114 qreal scale = 1; // todo: find good scale for page size
115 // todo: use epsRenderer()->scale() for printing ?
116 const qreal width = pageRect.width()/scale;
117 const qreal height = pageRect.height()/scale;
118 setViewSize(width, height, scale, true);
119
120 QPainter painter(printer);
121 painter.scale(scale, scale);
122 painter.setRenderHint(QPainter::Antialiasing);
123 WorksheetEntry* entry = firstEntry();
124 qreal y = 0;
125
126 while (entry) {
127 qreal h = 0;
128 do {
129 if (entry->type() == PageBreakEntry::Type) {
130 entry = entry->next();
131 break;
132 }
133 h += entry->size().height();
134 entry = entry->next();
135 } while (entry && h + entry->size().height() <= height);
136
137 render(&painter, QRectF(0, 0, width, height),
138 QRectF(0, y, width, h));
139 y += h;
140 if (entry)
141 printer->newPage();
142 }
143
144 //render(&painter);
145
146 painter.end();
147 m_isPrinting = false;
148 m_epsRenderer.useHighResolution(false);
149 m_epsRenderer.setScale(-1); // force update in next call to setViewSize,
150 worksheetView()->updateSceneSize(); // ... which happens in here
151}
152
153bool Worksheet::isPrinting()
154{
155 return m_isPrinting;
156}
157
158void Worksheet::setViewSize(qreal w, qreal h, qreal s, bool forceUpdate)
159{
160 Q_UNUSED(h);
161
162 m_viewWidth = w;
163 if (s != m_epsRenderer.scale() || forceUpdate) {
164 m_epsRenderer.setScale(s);
165 for (WorksheetEntry *entry = firstEntry(); entry; entry = entry->next())
166 entry->updateEntry();
167 }
168 updateLayout();
169}
170
171void Worksheet::updateLayout()
172{
173 bool cursorRectVisible = false;
174 bool atEnd = worksheetView()->isAtEnd();
175 if (currentTextItem()) {
176 QRectF cursorRect = currentTextItem()->sceneCursorRect();
177 cursorRectVisible = worksheetView()->isVisible(cursorRect);
178 }
179
180 const qreal w = m_viewWidth - LeftMargin - RightMargin;
181 qreal y = TopMargin;
182 const qreal x = LeftMargin;
183 for (WorksheetEntry *entry = firstEntry(); entry; entry = entry->next())
184 y += entry->setGeometry(x, y, w);
185 setSceneRect(QRectF(0, 0, m_viewWidth + m_protrusion, y));
186 if (cursorRectVisible)
187 makeVisible(worksheetCursor());
188 else if (atEnd)
189 worksheetView()->scrollToEnd();
190}
191
192void Worksheet::updateEntrySize(WorksheetEntry* entry)
193{
194 bool cursorRectVisible = false;
195 bool atEnd = worksheetView()->isAtEnd();
196 if (currentTextItem()) {
197 QRectF cursorRect = currentTextItem()->sceneCursorRect();
198 cursorRectVisible = worksheetView()->isVisible(cursorRect);
199 }
200
201 qreal y = entry->y() + entry->size().height();
202 for (entry = entry->next(); entry; entry = entry->next()) {
203 entry->setY(y);
204 y += entry->size().height();
205 }
206 setSceneRect(QRectF(0, 0, m_viewWidth + m_protrusion, y));
207 if (cursorRectVisible)
208 makeVisible(worksheetCursor());
209 else if (atEnd)
210 worksheetView()->scrollToEnd();
211}
212
213void Worksheet::addProtrusion(qreal width)
214{
215 if (m_itemProtrusions.contains(width))
216 ++m_itemProtrusions[width];
217 else
218 m_itemProtrusions.insert(width, 1);
219 if (width > m_protrusion) {
220 m_protrusion = width;
221 qreal y = lastEntry()->size().height() + lastEntry()->y();
222 setSceneRect(QRectF(0, 0, m_viewWidth + m_protrusion, y));
223 }
224}
225
226void Worksheet::updateProtrusion(qreal oldWidth, qreal newWidth)
227{
228 removeProtrusion(oldWidth);
229 addProtrusion(newWidth);
230}
231
232void Worksheet::removeProtrusion(qreal width)
233{
234 if (--m_itemProtrusions[width] == 0) {
235 m_itemProtrusions.remove(width);
236 if (width == m_protrusion) {
237 qreal max = -1;
238 foreach (qreal p, m_itemProtrusions.keys()) {
239 if (p > max)
240 max = p;
241 }
242 m_protrusion = max;
243 qreal y = lastEntry()->size().height() + lastEntry()->y();
244 setSceneRect(QRectF(0, 0, m_viewWidth + m_protrusion, y));
245 }
246 }
247}
248
249bool Worksheet::isEmpty()
250{
251 return !m_firstEntry;
252}
253
254void Worksheet::makeVisible(WorksheetEntry* entry)
255{
256 QRectF r = entry->boundingRect();
257 r = entry->mapRectToScene(r);
258 r.adjust(0, -10, 0, 10);
259 worksheetView()->makeVisible(r);
260}
261
262void Worksheet::makeVisible(const WorksheetCursor& cursor)
263{
264 if (cursor.textCursor().isNull()) {
265 if (cursor.entry())
266 makeVisible(cursor.entry());
267 return;
268 }
269 QRectF r = cursor.textItem()->sceneCursorRect(cursor.textCursor());
270 QRectF er = cursor.entry()->boundingRect();
271 er = cursor.entry()->mapRectToScene(er);
272 er.adjust(0, -10, 0, 10);
273 r.adjust(0, qMax(qreal(-100.0), er.top() - r.top()),
274 0, qMin(qreal(100.0), er.bottom() - r.bottom()));
275 worksheetView()->makeVisible(r);
276}
277
278WorksheetView* Worksheet::worksheetView()
279{
280 return qobject_cast<WorksheetView*>(views()[0]);
281}
282
283void Worksheet::setModified()
284{
285 emit modified();
286}
287
288WorksheetCursor Worksheet::worksheetCursor()
289{
290 WorksheetEntry* entry = currentEntry();
291 WorksheetTextItem* item = currentTextItem();
292
293 if (!entry || !item)
294 return WorksheetCursor();
295 return WorksheetCursor(entry, item, item->textCursor());
296}
297
298void Worksheet::setWorksheetCursor(const WorksheetCursor& cursor)
299{
300 if (!cursor.isValid())
301 return;
302
303 if (m_lastFocusedTextItem)
304 m_lastFocusedTextItem->clearSelection();
305
306 m_lastFocusedTextItem = cursor.textItem();
307
308 cursor.textItem()->setTextCursor(cursor.textCursor());
309}
310
311WorksheetEntry* Worksheet::currentEntry()
312{
313 QGraphicsItem* item = focusItem();
314 if (!item /*&& !hasFocus()*/)
315 item = m_lastFocusedTextItem;
316 /*else
317 m_focusItem = item;*/
318 while (item && (item->type() < QGraphicsItem::UserType ||
319 item->type() >= QGraphicsItem::UserType + 100))
320 item = item->parentItem();
321 if (item) {
322 WorksheetEntry* entry = qobject_cast<WorksheetEntry*>(item->toGraphicsObject());
323 if (entry && entry->aboutToBeRemoved()) {
324 if (entry->isAncestorOf(m_lastFocusedTextItem))
325 m_lastFocusedTextItem = 0;
326 return 0;
327 }
328 return entry;
329 }
330 return 0;
331}
332
333WorksheetEntry* Worksheet::firstEntry()
334{
335 return m_firstEntry;
336}
337
338WorksheetEntry* Worksheet::lastEntry()
339{
340 return m_lastEntry;
341}
342
343void Worksheet::setFirstEntry(WorksheetEntry* entry)
344{
345 if (m_firstEntry)
346 disconnect(m_firstEntry, SIGNAL(aboutToBeDeleted()),
347 this, SLOT(invalidateFirstEntry()));
348 m_firstEntry = entry;
349 if (m_firstEntry)
350 connect(m_firstEntry, SIGNAL(aboutToBeDeleted()),
351 this, SLOT(invalidateFirstEntry()), Qt::DirectConnection);
352}
353
354void Worksheet::setLastEntry(WorksheetEntry* entry)
355{
356 if (m_lastEntry)
357 disconnect(m_lastEntry, SIGNAL(aboutToBeDeleted()),
358 this, SLOT(invalidateLastEntry()));
359 m_lastEntry = entry;
360 if (m_lastEntry)
361 connect(m_lastEntry, SIGNAL(aboutToBeDeleted()),
362 this, SLOT(invalidateLastEntry()), Qt::DirectConnection);
363}
364
365void Worksheet::invalidateFirstEntry()
366{
367 if (m_firstEntry)
368 setFirstEntry(m_firstEntry->next());
369}
370
371void Worksheet::invalidateLastEntry()
372{
373 if (m_lastEntry)
374 setLastEntry(m_lastEntry->previous());
375}
376
377WorksheetEntry* Worksheet::entryAt(qreal x, qreal y)
378{
379 QGraphicsItem* item = itemAt(x, y);
380 while (item && (item->type() <= QGraphicsItem::UserType ||
381 item->type() >= QGraphicsItem::UserType + 100))
382 item = item->parentItem();
383 if (item)
384 return qobject_cast<WorksheetEntry*>(item->toGraphicsObject());
385 return 0;
386}
387
388WorksheetEntry* Worksheet::entryAt(QPointF p)
389{
390 return entryAt(p.x(), p.y());
391}
392
393void Worksheet::focusEntry(WorksheetEntry *entry)
394{
395 if (!entry)
396 return;
397 entry->focusEntry();
398 //bool rt = entry->acceptRichText();
399 //setActionsEnabled(rt);
400 //setAcceptRichText(rt);
401 //ensureCursorVisible();
402}
403
404void Worksheet::startDrag(WorksheetEntry* entry, QDrag* drag)
405{
406 m_dragEntry = entry;
407 WorksheetEntry* prev = entry->previous();
408 WorksheetEntry* next = entry->next();
409 m_placeholderEntry = new PlaceHolderEntry(this, entry->size());
410 m_placeholderEntry->setPrevious(prev);
411 m_placeholderEntry->setNext(next);
412 if (prev)
413 prev->setNext(m_placeholderEntry);
414 else
415 setFirstEntry(m_placeholderEntry);
416 if (next)
417 next->setPrevious(m_placeholderEntry);
418 else
419 setLastEntry(m_placeholderEntry);
420 m_dragEntry->hide();
421 Qt::DropAction action = drag->exec();
422
423 kDebug() << action;
424 if (action == Qt::MoveAction && m_placeholderEntry) {
425 kDebug() << "insert in new position";
426 prev = m_placeholderEntry->previous();
427 next = m_placeholderEntry->next();
428 }
429 m_dragEntry->setPrevious(prev);
430 m_dragEntry->setNext(next);
431 if (prev)
432 prev->setNext(m_dragEntry);
433 else
434 setFirstEntry(m_dragEntry);
435 if (next)
436 next->setPrevious(m_dragEntry);
437 else
438 setLastEntry(m_dragEntry);
439 m_dragEntry->show();
440 m_dragEntry->focusEntry();
441 const QPointF scenePos = worksheetView()->sceneCursorPos();
442 if (entryAt(scenePos) != m_dragEntry)
443 m_dragEntry->hideActionBar();
444 updateLayout();
445 if (m_placeholderEntry) {
446 m_placeholderEntry->setPrevious(0);
447 m_placeholderEntry->setNext(0);
448 m_placeholderEntry->hide();
449 m_placeholderEntry->deleteLater();
450 m_placeholderEntry = 0;
451 }
452 m_dragEntry = 0;
453}
454
455void Worksheet::evaluate()
456{
457 kDebug()<<"evaluate worksheet";
458 firstEntry()->evaluate(WorksheetEntry::EvaluateNext);
459
460 emit modified();
461}
462
463void Worksheet::evaluateCurrentEntry()
464{
465 kDebug() << "evaluation requested...";
466 WorksheetEntry* entry = currentEntry();
467 if(!entry)
468 return;
469 entry->evaluateCurrentItem();
470}
471
472bool Worksheet::completionEnabled()
473{
474 return m_completionEnabled;
475}
476
477void Worksheet::showCompletion()
478{
479 WorksheetEntry* current = currentEntry();
480 current->showCompletion();
481}
482
483WorksheetEntry* Worksheet::appendEntry(const int type)
484{
485 WorksheetEntry* entry = WorksheetEntry::create(type, this);
486
487 if (entry)
488 {
489 kDebug() << "Entry Appended";
490 entry->setPrevious(lastEntry());
491 if (lastEntry())
492 lastEntry()->setNext(entry);
493 if (!firstEntry())
494 setFirstEntry(entry);
495 setLastEntry(entry);
496 updateLayout();
497 makeVisible(entry);
498 focusEntry(entry);
499 }
500 return entry;
501}
502
503WorksheetEntry* Worksheet::appendCommandEntry()
504{
505 return appendEntry(CommandEntry::Type);
506}
507
508WorksheetEntry* Worksheet::appendTextEntry()
509{
510 return appendEntry(TextEntry::Type);
511}
512
513
514WorksheetEntry* Worksheet::appendPageBreakEntry()
515{
516 return appendEntry(PageBreakEntry::Type);
517}
518
519WorksheetEntry* Worksheet::appendImageEntry()
520{
521 return appendEntry(ImageEntry::Type);
522}
523
524WorksheetEntry* Worksheet::appendLatexEntry()
525{
526 return appendEntry(LatexEntry::Type);
527}
528
529void Worksheet::appendCommandEntry(const QString& text)
530{
531 WorksheetEntry* entry = lastEntry();
532 if(!entry->isEmpty())
533 {
534 entry = appendCommandEntry();
535 }
536
537 if (entry)
538 {
539 focusEntry(entry);
540 entry->setContent(text);
541 evaluateCurrentEntry();
542 }
543}
544
545WorksheetEntry* Worksheet::insertEntry(const int type, WorksheetEntry* current)
546{
547 if (!current)
548 current = currentEntry();
549
550 if (!current)
551 return appendEntry(type);
552
553 WorksheetEntry *next = current->next();
554 WorksheetEntry *entry = 0;
555
556 if (!next || next->type() != type || !next->isEmpty())
557 {
558 entry = WorksheetEntry::create(type, this);
559 entry->setPrevious(current);
560 entry->setNext(next);
561 current->setNext(entry);
562 if (next)
563 next->setPrevious(entry);
564 else
565 setLastEntry(entry);
566 updateLayout();
567 } else {
568 entry = next;
569 }
570
571 focusEntry(entry);
572 makeVisible(entry);
573 return entry;
574}
575
576WorksheetEntry* Worksheet::insertTextEntry(WorksheetEntry* current)
577{
578 return insertEntry(TextEntry::Type, current);
579}
580
581WorksheetEntry* Worksheet::insertCommandEntry(WorksheetEntry* current)
582{
583 return insertEntry(CommandEntry::Type, current);
584}
585
586WorksheetEntry* Worksheet::insertImageEntry(WorksheetEntry* current)
587{
588 return insertEntry(ImageEntry::Type, current);
589}
590
591WorksheetEntry* Worksheet::insertPageBreakEntry(WorksheetEntry* current)
592{
593 return insertEntry(PageBreakEntry::Type, current);
594}
595
596WorksheetEntry* Worksheet::insertLatexEntry(WorksheetEntry* current)
597{
598 return insertEntry(LatexEntry::Type, current);
599}
600
601void Worksheet::insertCommandEntry(const QString& text)
602{
603 WorksheetEntry* entry = insertCommandEntry();
604 if(entry&&!text.isNull())
605 {
606 entry->setContent(text);
607 evaluateCurrentEntry();
608 }
609}
610
611
612WorksheetEntry* Worksheet::insertEntryBefore(int type, WorksheetEntry* current)
613{
614 if (!current)
615 current = currentEntry();
616
617 if (!current)
618 return 0;
619
620 WorksheetEntry *prev = current->previous();
621 WorksheetEntry *entry = 0;
622
623 if(!prev || prev->type() != type || !prev->isEmpty())
624 {
625 entry = WorksheetEntry::create(type, this);
626 entry->setNext(current);
627 entry->setPrevious(prev);
628 current->setPrevious(entry);
629 if (prev)
630 prev->setNext(entry);
631 else
632 setFirstEntry(entry);
633 updateLayout();
634 }
635
636 focusEntry(entry);
637 return entry;
638}
639
640WorksheetEntry* Worksheet::insertTextEntryBefore(WorksheetEntry* current)
641{
642 return insertEntryBefore(TextEntry::Type, current);
643}
644
645WorksheetEntry* Worksheet::insertCommandEntryBefore(WorksheetEntry* current)
646{
647 return insertEntryBefore(CommandEntry::Type, current);
648}
649
650WorksheetEntry* Worksheet::insertPageBreakEntryBefore(WorksheetEntry* current)
651{
652 return insertEntryBefore(PageBreakEntry::Type, current);
653}
654
655WorksheetEntry* Worksheet::insertImageEntryBefore(WorksheetEntry* current)
656{
657 return insertEntryBefore(ImageEntry::Type, current);
658}
659
660WorksheetEntry* Worksheet::insertLatexEntryBefore(WorksheetEntry* current)
661{
662 return insertEntryBefore(LatexEntry::Type, current);
663}
664
665void Worksheet::interrupt()
666{
667 m_session->interrupt();
668 emit updatePrompt();
669}
670
671void Worksheet::interruptCurrentEntryEvaluation()
672{
673 currentEntry()->interruptEvaluation();
674}
675
676void Worksheet::highlightItem(WorksheetTextItem* item)
677{
678 if (!m_highlighter)
679 return;
680
681 QTextDocument *oldDocument = m_highlighter->document();
682 QList<QList<QTextLayout::FormatRange> > formats;
683
684 if (oldDocument)
685 for (QTextBlock b = oldDocument->firstBlock();
686 b.isValid(); b = b.next())
687 {
688 formats.append(b.layout()->additionalFormats());
689 }
690
691 // Not every highlighter is a Cantor::DefaultHighligther (e.g. the
692 // highlighter for KAlgebra)
693 Cantor::DefaultHighlighter* hl = qobject_cast<Cantor::DefaultHighlighter*>(m_highlighter);
694 if (hl) {
695 hl->setTextItem(item);
696 } else {
697 m_highlighter->setDocument(item->document());
698 }
699
700 if (oldDocument)
701 for (QTextBlock b = oldDocument->firstBlock();
702 b.isValid(); b = b.next())
703 {
704 b.layout()->setAdditionalFormats(formats.first());
705 formats.pop_front();
706 }
707
708}
709
710void Worksheet::rehighlight()
711{
712 if(m_highlighter)
713 {
714 // highlight every entry
715 WorksheetEntry* entry;
716 for (entry = firstEntry(); entry; entry = entry->next()) {
717 WorksheetTextItem* item = entry->highlightItem();
718 if (!item)
719 continue;
720 highlightItem(item);
721 m_highlighter->rehighlight();
722 }
723 entry = currentEntry();
724 WorksheetTextItem* textitem = entry ? entry->highlightItem() : 0;
725 if (textitem && textitem->hasFocus())
726 highlightItem(textitem);
727 } else
728 {
729 // remove highlighting from entries
730 WorksheetEntry* entry;
731 for (entry = firstEntry(); entry; entry = entry->next()) {
732 WorksheetTextItem* item = entry->highlightItem();
733 if (!item)
734 continue;
735 for (QTextBlock b = item->document()->firstBlock();
736 b.isValid(); b = b.next())
737 {
738 b.layout()->clearAdditionalFormats();
739 }
740 }
741 update();
742 }
743}
744
745void Worksheet::enableHighlighting(bool highlight)
746{
747 if(highlight)
748 {
749 if(m_highlighter)
750 m_highlighter->deleteLater();
751 m_highlighter=session()->syntaxHighlighter(this);
752 if(!m_highlighter)
753 m_highlighter=new Cantor::DefaultHighlighter(this);
754
755 connect(m_highlighter, SIGNAL(rulesChanged()), this, SLOT(rehighlight()));
756
757 }else
758 {
759 if(m_highlighter)
760 m_highlighter->deleteLater();
761 m_highlighter=0;
762 }
763
764 rehighlight();
765}
766
767void Worksheet::enableCompletion(bool enable)
768{
769 m_completionEnabled=enable;
770}
771
772Cantor::Session* Worksheet::session()
773{
774 return m_session;
775}
776
777bool Worksheet::isRunning()
778{
779 return m_session->status()==Cantor::Session::Running;
780}
781
782bool Worksheet::showExpressionIds()
783{
784 return m_showExpressionIds;
785}
786
787bool Worksheet::animationsEnabled()
788{
789 return m_animationsEnabled;
790}
791
792void Worksheet::enableAnimations(bool enable)
793{
794 m_animationsEnabled = enable;
795}
796
797void Worksheet::enableExpressionNumbering(bool enable)
798{
799 m_showExpressionIds=enable;
800 emit updatePrompt();
801}
802
803QDomDocument Worksheet::toXML(KZip* archive)
804{
805 QDomDocument doc( "CantorWorksheet" );
806 QDomElement root=doc.createElement( "Worksheet" );
807 root.setAttribute("backend", m_session->backend()->name());
808 doc.appendChild(root);
809
810 for( WorksheetEntry* entry = firstEntry(); entry; entry = entry->next())
811 {
812 QDomElement el = entry->toXml(doc, archive);
813 root.appendChild( el );
814 }
815 return doc;
816}
817
818void Worksheet::save( const QString& filename )
819{
820 kDebug()<<"saving to filename";
821 KZip zipFile( filename );
822
823
824 if ( !zipFile.open(QIODevice::WriteOnly) )
825 {
826 KMessageBox::error( worksheetView(),
827 i18n( "Cannot write file %1." , filename ),
828 i18n( "Error - Cantor" ));
829 return;
830 }
831
832 QByteArray content = toXML(&zipFile).toByteArray();
833 kDebug()<<"content: "<<content;
834 zipFile.writeFile( "content.xml", QString(), QString(), content.data(), content.size() );
835
836 /*zipFile.close();*/
837}
838
839
840void Worksheet::savePlain(const QString& filename)
841{
842 QFile file(filename);
843 if(!file.open(QIODevice::WriteOnly))
844 {
845 KMessageBox::error(worksheetView(), i18n("Error saving file %1", filename), i18n("Error - Cantor"));
846 return;
847 }
848
849 QString cmdSep=";\n";
850 QString commentStartingSeq = "";
851 QString commentEndingSeq = "";
852
853 Cantor::Backend * const backend=session()->backend();
854 if (backend->extensions().contains("ScriptExtension"))
855 {
856 Cantor::ScriptExtension* e=dynamic_cast<Cantor::ScriptExtension*>(backend->extension("ScriptExtension"));
857 cmdSep=e->commandSeparator();
858 commentStartingSeq = e->commentStartingSequence();
859 commentEndingSeq = e->commentEndingSequence();
860 }
861
862 QTextStream stream(&file);
863
864 for(WorksheetEntry * entry = firstEntry(); entry; entry = entry->next())
865 {
866 const QString& str=entry->toPlain(cmdSep, commentStartingSeq, commentEndingSeq);
867 if(!str.isEmpty())
868 stream << str + '\n';
869 }
870
871 file.close();
872}
873
874void Worksheet::saveLatex(const QString& filename)
875{
876 kDebug()<<"exporting to Latex: " <<filename;
877
878 QFile file(filename);
879 if(!file.open(QIODevice::WriteOnly))
880 {
881 KMessageBox::error(worksheetView(), i18n("Error saving file %1", filename), i18n("Error - Cantor"));
882 return;
883 }
884
885 QString xml = toXML().toString();
886 QTextStream stream(&file);
887 QXmlQuery query(QXmlQuery::XSLT20);
888 query.setFocus(xml);
889
890 QString stylesheet = KStandardDirs::locate("appdata", "xslt/latex.xsl");
891 if (stylesheet.isEmpty())
892 {
893 KMessageBox::error(worksheetView(), i18n("Error loading latex.xsl stylesheet"), i18n("Error - Cantor"));
894 return;
895 }
896
897 query.setQuery(QUrl(stylesheet));
898 QString out;
899 if (query.evaluateTo(&out))
900 stream << out;
901 file.close();
902}
903
904void Worksheet::load(const QString& filename )
905{
906 // m_file is always local so we can use QFile on it
907 KZip file(filename);
908 if (!file.open(QIODevice::ReadOnly))
909 return ;
910
911 const KArchiveEntry* contentEntry=file.directory()->entry("content.xml");
912 if (!contentEntry->isFile())
913 {
914 kDebug()<<"error";
915 }
916 const KArchiveFile* content=static_cast<const KArchiveFile*>(contentEntry);
917 QByteArray data=content->data();
918
919 kDebug()<<"read: "<<data;
920
921 QDomDocument doc;
922 doc.setContent(data);
923 QDomElement root=doc.documentElement();
924 kDebug()<<root.tagName();
925
926 const QString backendName=root.attribute("backend");
927 Cantor::Backend* b=Cantor::Backend::createBackend(backendName);
928 if (!b)
929 {
930 KMessageBox::error(worksheetView(), i18n("The backend with which this file was generated is not installed. It needs %1", backendName), i18n("Cantor"));
931 return;
932 }
933
934 if(!b->isEnabled())
935 {
936 KMessageBox::information(worksheetView(), i18n("There are some problems with the %1 backend,\n"\
937 "please check your configuration or install the needed packages.\n"
938 "You will only be able to view this worksheet.", backendName), i18n("Cantor"));
939
940 }
941
942
943 //cleanup the worksheet and all it contains
944 delete m_session;
945 m_session=0;
946 for(WorksheetEntry* entry = firstEntry(); entry; entry = entry->next())
947 delete entry;
948 clear();
949 setFirstEntry(0);
950 setLastEntry(0);
951
952 m_session=b->createSession();
953 m_loginFlag=true;
954
955 kDebug()<<"loading entries";
956 QDomElement expressionChild = root.firstChildElement();
957 WorksheetEntry* entry;
958 while (!expressionChild.isNull()) {
959 QString tag = expressionChild.tagName();
960 if (tag == "Expression")
961 {
962 entry = appendCommandEntry();
963 entry->setContent(expressionChild, file);
964 } else if (tag == "Text")
965 {
966 entry = appendTextEntry();
967 entry->setContent(expressionChild, file);
968 } else if (tag == "Latex")
969 {
970 entry = appendLatexEntry();
971 entry->setContent(expressionChild, file);
972 } else if (tag == "PageBreak")
973 {
974 entry = appendPageBreakEntry();
975 entry->setContent(expressionChild, file);
976 }
977 else if (tag == "Image")
978 {
979 entry = appendImageEntry();
980 entry->setContent(expressionChild, file);
981 }
982
983 expressionChild = expressionChild.nextSiblingElement();
984 }
985
986 //login to the session, but let Qt process all the events in its pipeline
987 //first.
988 QTimer::singleShot(0, this, SLOT(loginToSession()));
989
990 //Set the Highlighting, depending on the current state
991 //If the session isn't logged in, use the default
992 enableHighlighting( m_highlighter!=0 || (m_loginFlag && Settings::highlightDefault()) );
993
994
995
996 emit sessionChanged();
997}
998
999void Worksheet::gotResult(Cantor::Expression* expr)
1000{
1001 if(expr==0)
1002 expr=qobject_cast<Cantor::Expression*>(sender());
1003
1004 if(expr==0)
1005 return;
1006 //We're only interested in help results, others are handled by the WorksheetEntry
1007 if(expr->result()&&expr->result()->type()==Cantor::HelpResult::Type)
1008 {
1009 QString help=expr->result()->toHtml();
1010 //Do some basic LaTeX replacing
1011 help.replace(QRegExp("\\\\code\\{([^\\}]*)\\}"), "<b>\\1</b>");
1012 help.replace(QRegExp("\\$([^\\$])\\$"), "<i>\\1</i>");
1013
1014 emit showHelp(help);
1015 }
1016}
1017
1018void Worksheet::removeCurrentEntry()
1019{
1020 kDebug()<<"removing current entry";
1021 WorksheetEntry* entry=currentEntry();
1022 if(!entry)
1023 return;
1024
1025 // In case we just removed this
1026 if (entry->isAncestorOf(m_lastFocusedTextItem))
1027 m_lastFocusedTextItem = 0;
1028 entry->startRemoving();
1029}
1030
1031EpsRenderer* Worksheet::epsRenderer()
1032{
1033 return &m_epsRenderer;
1034}
1035
1036KMenu* Worksheet::createContextMenu()
1037{
1038 KMenu *menu = new KMenu(worksheetView());
1039 connect(menu, SIGNAL(aboutToHide()), menu, SLOT(deleteLater()));
1040
1041 return menu;
1042}
1043
1044void Worksheet::populateMenu(KMenu *menu, const QPointF& pos)
1045{
1046 WorksheetEntry* entry = entryAt(pos);
1047 if (entry && !entry->isAncestorOf(m_lastFocusedTextItem)) {
1048 WorksheetTextItem* item =
1049 qgraphicsitem_cast<WorksheetTextItem*>(itemAt(pos));
1050 if (item && item->isEditable())
1051 m_lastFocusedTextItem = item;
1052 }
1053
1054 if (!isRunning())
1055 menu->addAction(KIcon("system-run"), i18n("Evaluate Worksheet"),
1056 this, SLOT(evaluate()), 0);
1057 else
1058 menu->addAction(KIcon("process-stop"), i18n("Interrupt"), this,
1059 SLOT(interrupt()), 0);
1060 menu->addSeparator();
1061
1062 if (entry) {
1063 KMenu* insert = new KMenu(menu);
1064 KMenu* insertBefore = new KMenu(menu);
1065
1066 insert->addAction(i18n("Command Entry"), entry, SLOT(insertCommandEntry()));
1067 insert->addAction(i18n("Text Entry"), entry, SLOT(insertTextEntry()));
1068 insert->addAction(i18n("LaTeX Entry"), entry, SLOT(insertLatexEntry()));
1069 insert->addAction(i18n("Image"), entry, SLOT(insertImageEntry()));
1070 insert->addAction(i18n("Page Break"), entry, SLOT(insertPageBreakEntry()));
1071
1072 insertBefore->addAction(i18n("Command Entry"), entry, SLOT(insertCommandEntryBefore()));
1073 insertBefore->addAction(i18n("Text Entry"), entry, SLOT(insertTextEntryBefore()));
1074 insertBefore->addAction(i18n("LaTeX Entry"), entry, SLOT(insertLatexEntryBefore()));
1075 insertBefore->addAction(i18n("Image"), entry, SLOT(insertImageEntryBefore()));
1076 insertBefore->addAction(i18n("Page Break"), entry, SLOT(insertPageBreakEntryBefore()));
1077
1078 insert->setTitle(i18n("Insert"));
1079 insertBefore->setTitle(i18n("Insert Before"));
1080 menu->addMenu(insert);
1081 menu->addMenu(insertBefore);
1082 } else {
1083 menu->addAction(i18n("Insert Command Entry"), this, SLOT(appendCommandEntry()));
1084 menu->addAction(i18n("Insert Text Entry"), this, SLOT(appendTextEntry()));
1085 menu->addAction(i18n("Insert LaTeX Entry"), this, SLOT(appendLatexEntry()));
1086 menu->addAction(i18n("Insert Image"), this, SLOT(appendImageEntry()));
1087 menu->addAction(i18n("Insert Page Break"), this, SLOT(appendPageBreakEntry()));
1088 }
1089}
1090
1091void Worksheet::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
1092{
1093 // forward the event to the items
1094 QGraphicsScene::contextMenuEvent(event);
1095
1096 if (!event->isAccepted()) {
1097 event->accept();
1098 KMenu *menu = createContextMenu();
1099 populateMenu(menu, event->scenePos());
1100
1101 menu->popup(event->screenPos());
1102 }
1103}
1104
1105void Worksheet::mousePressEvent(QGraphicsSceneMouseEvent* event)
1106{
1107 QGraphicsScene::mousePressEvent(event);
1108 if (event->button() == Qt::LeftButton && !focusItem() && lastEntry() &&
1109 event->scenePos().y() > lastEntry()->y() + lastEntry()->size().height())
1110 lastEntry()->focusEntry(WorksheetTextItem::BottomRight);
1111}
1112
1113void Worksheet::createActions(KActionCollection* collection)
1114{
1115 // Mostly copied from KRichTextWidget::createActions(KActionCollection*)
1116 // It would be great if this wasn't necessary.
1117
1118 // Text color
1119 KAction* action;
1120 /* This is "format-stroke-color" in KRichTextWidget */
1121 action = new KAction(KIcon("format-text-color"),
1122 i18nc("@action", "Text &Color..."), collection);
1123 action->setIconText(i18nc("@label text color", "Color"));
1124 action->setPriority(QAction::LowPriority);
1125 m_richTextActionList.append(action);
1126 collection->addAction("format_text_foreground_color", action);
1127 connect(action, SIGNAL(triggered()), this, SLOT(setTextForegroundColor()));
1128
1129 // Text color
1130 action = new KAction(KIcon("format-fill-color"),
1131 i18nc("@action", "Text &Highlight..."), collection);
1132 action->setPriority(QAction::LowPriority);
1133 m_richTextActionList.append(action);
1134 collection->addAction("format_text_background_color", action);
1135 connect(action, SIGNAL(triggered()), this, SLOT(setTextBackgroundColor()));
1136
1137 // Font Family
1138 m_fontAction = new KFontAction(i18nc("@action", "&Font"), collection);
1139 m_richTextActionList.append(m_fontAction);
1140 collection->addAction("format_font_family", m_fontAction);
1141 connect(m_fontAction, SIGNAL(triggered(QString)), this,
1142 SLOT(setFontFamily(QString)));
1143
1144 // Font Size
1145 m_fontSizeAction = new KFontSizeAction(i18nc("@action", "Font &Size"),
1146 collection);
1147 m_richTextActionList.append(m_fontSizeAction);
1148 collection->addAction("format_font_size", m_fontSizeAction);
1149 connect(m_fontSizeAction, SIGNAL(fontSizeChanged(int)), this,
1150 SLOT(setFontSize(int)));
1151
1152 // Bold
1153 m_boldAction = new KToggleAction(KIcon("format-text-bold"),
1154 i18nc("@action boldify selected text", "&Bold"),
1155 collection);
1156 m_boldAction->setPriority(QAction::LowPriority);
1157 QFont bold;
1158 bold.setBold(true);
1159 m_boldAction->setFont(bold);
1160 m_richTextActionList.append(m_boldAction);
1161 collection->addAction("format_text_bold", m_boldAction);
1162 m_boldAction->setShortcut(KShortcut(Qt::CTRL + Qt::Key_B));
1163 connect(m_boldAction, SIGNAL(triggered(bool)), this,
1164 SLOT(setTextBold(bool)));
1165
1166 // Italic
1167 m_italicAction = new KToggleAction(KIcon("format-text-italic"),
1168 i18nc("@action italicize selected text",
1169 "&Italic"),
1170 collection);
1171 m_italicAction->setPriority(QAction::LowPriority);
1172 QFont italic;
1173 italic.setItalic(true);
1174 m_italicAction->setFont(italic);
1175 m_richTextActionList.append(m_italicAction);
1176 collection->addAction("format_text_italic", m_italicAction);
1177 m_italicAction->setShortcut(KShortcut(Qt::CTRL + Qt::Key_I));
1178 connect(m_italicAction, SIGNAL(triggered(bool)), this,
1179 SLOT(setTextItalic(bool)));
1180
1181 // Underline
1182 m_underlineAction = new KToggleAction(KIcon("format-text-underline"),
1183 i18nc("@action underline selected text",
1184 "&Underline"),
1185 collection);
1186 m_underlineAction->setPriority(QAction::LowPriority);
1187 QFont underline;
1188 underline.setUnderline(true);
1189 m_underlineAction->setFont(underline);
1190 m_richTextActionList.append(m_underlineAction);
1191 collection->addAction("format_text_underline", m_underlineAction);
1192 m_underlineAction->setShortcut(KShortcut(Qt::CTRL + Qt::Key_U));
1193 connect(m_underlineAction, SIGNAL(triggered(bool)), this,
1194 SLOT(setTextUnderline(bool)));
1195
1196 // Strike
1197 m_strikeOutAction = new KToggleAction(KIcon("format-text-strikethrough"),
1198 i18nc("@action", "&Strike Out"),
1199 collection);
1200 m_strikeOutAction->setPriority(QAction::LowPriority);
1201 m_richTextActionList.append(m_strikeOutAction);
1202 collection->addAction("format_text_strikeout", m_strikeOutAction);
1203 m_strikeOutAction->setShortcut(KShortcut(Qt::CTRL + Qt::Key_L));
1204 connect(m_strikeOutAction, SIGNAL(triggered(bool)), this,
1205 SLOT(setTextStrikeOut(bool)));
1206
1207 // Alignment
1208 QActionGroup *alignmentGroup = new QActionGroup(this);
1209
1210 // Align left
1211 m_alignLeftAction = new KToggleAction(KIcon("format-justify-left"),
1212 i18nc("@action", "Align &Left"),
1213 collection);
1214 m_alignLeftAction->setPriority(QAction::LowPriority);
1215 m_alignLeftAction->setIconText(i18nc("@label left justify", "Left"));
1216 m_richTextActionList.append(m_alignLeftAction);
1217 collection->addAction("format_align_left", m_alignLeftAction);
1218 connect(m_alignLeftAction, SIGNAL(triggered()), this,
1219 SLOT(setAlignLeft()));
1220 alignmentGroup->addAction(m_alignLeftAction);
1221
1222 // Align center
1223 m_alignCenterAction = new KToggleAction(KIcon("format-justify-center"),
1224 i18nc("@action", "Align &Center"),
1225 collection);
1226 m_alignCenterAction->setPriority(QAction::LowPriority);
1227 m_alignCenterAction->setIconText(i18nc("@label center justify", "Center"));
1228 m_richTextActionList.append(m_alignCenterAction);
1229 collection->addAction("format_align_center", m_alignCenterAction);
1230 connect(m_alignCenterAction, SIGNAL(triggered()), this,
1231 SLOT(setAlignCenter()));
1232 alignmentGroup->addAction(m_alignCenterAction);
1233
1234 // Align right
1235 m_alignRightAction = new KToggleAction(KIcon("format-justify-right"),
1236 i18nc("@action", "Align &Right"),
1237 collection);
1238 m_alignRightAction->setPriority(QAction::LowPriority);
1239 m_alignRightAction->setIconText(i18nc("@label right justify", "Right"));
1240 m_richTextActionList.append(m_alignRightAction);
1241 collection->addAction("format_align_right", m_alignRightAction);
1242 connect(m_alignRightAction, SIGNAL(triggered()), this,
1243 SLOT(setAlignRight()));
1244 alignmentGroup->addAction(m_alignRightAction);
1245
1246 // Align justify
1247 m_alignJustifyAction = new KToggleAction(KIcon("format-justify-fill"),
1248 i18nc("@action", "&Justify"),
1249 collection);
1250 m_alignJustifyAction->setPriority(QAction::LowPriority);
1251 m_alignJustifyAction->setIconText(i18nc("@label justify fill", "Justify"));
1252 m_richTextActionList.append(m_alignJustifyAction);
1253 collection->addAction("format_align_justify", m_alignJustifyAction);
1254 connect(m_alignJustifyAction, SIGNAL(triggered()), this,
1255 SLOT(setAlignJustify()));
1256 alignmentGroup->addAction(m_alignJustifyAction);
1257
1258 /*
1259 // List style
1260 KSelectAction* selAction;
1261 selAction = new KSelectAction(KIcon("format-list-unordered"),
1262 i18nc("@title:menu", "List Style"),
1263 collection);
1264 QStringList listStyles;
1265 listStyles << i18nc("@item:inmenu no list style", "None")
1266 << i18nc("@item:inmenu disc list style", "Disc")
1267 << i18nc("@item:inmenu circle list style", "Circle")
1268 << i18nc("@item:inmenu square list style", "Square")
1269 << i18nc("@item:inmenu numbered lists", "123")
1270 << i18nc("@item:inmenu lowercase abc lists", "abc")
1271 << i18nc("@item:inmenu uppercase abc lists", "ABC");
1272 selAction->setItems(listStyles);
1273 selAction->setCurrentItem(0);
1274 action = selAction;
1275 m_richTextActionList.append(action);
1276 collection->addAction("format_list_style", action);
1277 connect(action, SIGNAL(triggered(int)),
1278 this, SLOT(_k_setListStyle(int)));
1279 connect(action, SIGNAL(triggered()),
1280 this, SLOT(_k_updateMiscActions()));
1281
1282 // Indent
1283 action = new KAction(KIcon("format-indent-more"),
1284 i18nc("@action", "Increase Indent"), collection);
1285 action->setPriority(QAction::LowPriority);
1286 m_richTextActionList.append(action);
1287 collection->addAction("format_list_indent_more", action);
1288 connect(action, SIGNAL(triggered()),
1289 this, SLOT(indentListMore()));
1290 connect(action, SIGNAL(triggered()),
1291 this, SLOT(_k_updateMiscActions()));
1292
1293 // Dedent
1294 action = new KAction(KIcon("format-indent-less"),
1295 i18nc("@action", "Decrease Indent"), collection);
1296 action->setPriority(QAction::LowPriority);
1297 m_richTextActionList.append(action);
1298 collection->addAction("format_list_indent_less", action);
1299 connect(action, SIGNAL(triggered()), this, SLOT(indentListLess()));
1300 connect(action, SIGNAL(triggered()), this, SLOT(_k_updateMiscActions()));
1301 */
1302}
1303
1304WorksheetTextItem* Worksheet::lastFocusedTextItem()
1305{
1306 return m_lastFocusedTextItem;
1307}
1308
1309void Worksheet::updateFocusedTextItem(WorksheetTextItem* newItem)
1310{
1311 if (m_lastFocusedTextItem && m_lastFocusedTextItem != newItem) {
1312 disconnect(m_lastFocusedTextItem, SIGNAL(undoAvailable(bool)),
1313 this, SIGNAL(undoAvailable(bool)));
1314 disconnect(m_lastFocusedTextItem, SIGNAL(redoAvailable(bool)),
1315 this, SIGNAL(redoAvailable(bool)));
1316 disconnect(this, SIGNAL(undo()), m_lastFocusedTextItem, SLOT(undo()));
1317 disconnect(this, SIGNAL(redo()), m_lastFocusedTextItem, SLOT(redo()));
1318 disconnect(m_lastFocusedTextItem, SIGNAL(cutAvailable(bool)),
1319 this, SIGNAL(cutAvailable(bool)));
1320 disconnect(m_lastFocusedTextItem, SIGNAL(copyAvailable(bool)),
1321 this, SIGNAL(copyAvailable(bool)));
1322 disconnect(m_lastFocusedTextItem, SIGNAL(pasteAvailable(bool)),
1323 this, SIGNAL(pasteAvailable(bool)));
1324 disconnect(this, SIGNAL(cut()), m_lastFocusedTextItem, SLOT(cut()));
1325 disconnect(this, SIGNAL(copy()), m_lastFocusedTextItem, SLOT(copy()));
1326 disconnect(this, SIGNAL(paste()), m_lastFocusedTextItem, SLOT(paste()));
1327
1328 m_lastFocusedTextItem->clearSelection();
1329 }
1330
1331 if (newItem && m_lastFocusedTextItem != newItem) {
1332 setAcceptRichText(newItem->richTextEnabled());
1333 emit undoAvailable(newItem->isUndoAvailable());
1334 emit redoAvailable(newItem->isRedoAvailable());
1335 connect(newItem, SIGNAL(undoAvailable(bool)),
1336 this, SIGNAL(undoAvailable(bool)));
1337 connect(newItem, SIGNAL(redoAvailable(bool)),
1338 this, SIGNAL(redoAvailable(bool)));
1339 connect(this, SIGNAL(undo()), newItem, SLOT(undo()));
1340 connect(this, SIGNAL(redo()), newItem, SLOT(redo()));
1341 emit cutAvailable(newItem->isCutAvailable());
1342 emit copyAvailable(newItem->isCopyAvailable());
1343 emit pasteAvailable(newItem->isPasteAvailable());
1344 connect(newItem, SIGNAL(cutAvailable(bool)),
1345 this, SIGNAL(cutAvailable(bool)));
1346 connect(newItem, SIGNAL(copyAvailable(bool)),
1347 this, SIGNAL(copyAvailable(bool)));
1348 connect(newItem, SIGNAL(pasteAvailable(bool)),
1349 this, SIGNAL(pasteAvailable(bool)));
1350 connect(this, SIGNAL(cut()), newItem, SLOT(cut()));
1351 connect(this, SIGNAL(copy()), newItem, SLOT(copy()));
1352 connect(this, SIGNAL(paste()), newItem, SLOT(paste()));
1353 } else if (!newItem) {
1354 emit undoAvailable(false);
1355 emit redoAvailable(false);
1356 emit cutAvailable(false);
1357 emit copyAvailable(false);
1358 emit pasteAvailable(false);
1359 }
1360 m_lastFocusedTextItem = newItem;
1361}
1362
1363void Worksheet::setRichTextInformation(const RichTextInfo& info)
1364{
1365 m_boldAction->setChecked(info.bold);
1366 m_italicAction->setChecked(info.italic);
1367 m_underlineAction->setChecked(info.underline);
1368 m_strikeOutAction->setChecked(info.strikeOut);
1369 m_fontAction->setFont(info.font);
1370 if (info.fontSize > 0)
1371 m_fontSizeAction->setFontSize(info.fontSize);
1372
1373 if (info.align & Qt::AlignLeft)
1374 m_alignLeftAction->setChecked(true);
1375 else if (info.align & Qt::AlignCenter)
1376 m_alignCenterAction->setChecked(true);
1377 else if (info.align & Qt::AlignRight)
1378 m_alignRightAction->setChecked(true);
1379 else if (info.align & Qt::AlignJustify)
1380 m_alignJustifyAction->setChecked(true);
1381}
1382
1383void Worksheet::setAcceptRichText(bool b)
1384{
1385 foreach(KAction* action, m_richTextActionList) {
1386 action->setEnabled(b);
1387 }
1388
1389 /*
1390 foreach(QWidget* widget, m_fontAction->createdWidgets()) {
1391 widget->setEnabled(b);
1392 }
1393
1394 foreach(QWidget* widget, m_fontSizeAction->createdWidgets()) {
1395 widget->setEnabled(b);
1396 }
1397 */
1398}
1399
1400WorksheetTextItem* Worksheet::currentTextItem()
1401{
1402 QGraphicsItem* item = focusItem();
1403 if (!item)
1404 item = m_lastFocusedTextItem;
1405 while (item && item->type() != WorksheetTextItem::Type)
1406 item = item->parentItem();
1407
1408 return qgraphicsitem_cast<WorksheetTextItem*>(item);
1409}
1410
1411void Worksheet::setTextForegroundColor()
1412{
1413 WorksheetTextItem* item = currentTextItem();
1414 if (item)
1415 item->setTextForegroundColor();
1416}
1417
1418void Worksheet::setTextBackgroundColor()
1419{
1420 WorksheetTextItem* item = currentTextItem();
1421 if (item)
1422 item->setTextBackgroundColor();
1423}
1424
1425void Worksheet::setTextBold(bool b)
1426{
1427 WorksheetTextItem* item = currentTextItem();
1428 if (item)
1429 item->setTextBold(b);
1430}
1431
1432void Worksheet::setTextItalic(bool b)
1433{
1434 WorksheetTextItem* item = currentTextItem();
1435 if (item)
1436 item->setTextItalic(b);
1437}
1438
1439void Worksheet::setTextUnderline(bool b)
1440{
1441 WorksheetTextItem* item = currentTextItem();
1442 if (item)
1443 item->setTextUnderline(b);
1444}
1445
1446void Worksheet::setTextStrikeOut(bool b)
1447{
1448 WorksheetTextItem* item = currentTextItem();
1449 if (item)
1450 item->setTextStrikeOut(b);
1451}
1452
1453void Worksheet::setAlignLeft()
1454{
1455 WorksheetTextItem* item = currentTextItem();
1456 if (item)
1457 item->setAlignment(Qt::AlignLeft);
1458}
1459
1460void Worksheet::setAlignRight()
1461{
1462 WorksheetTextItem* item = currentTextItem();
1463 if (item)
1464 item->setAlignment(Qt::AlignRight);
1465}
1466
1467void Worksheet::setAlignCenter()
1468{
1469 WorksheetTextItem* item = currentTextItem();
1470 if (item)
1471 item->setAlignment(Qt::AlignCenter);
1472}
1473
1474void Worksheet::setAlignJustify()
1475{
1476 WorksheetTextItem* item = currentTextItem();
1477 if (item)
1478 item->setAlignment(Qt::AlignJustify);
1479}
1480
1481void Worksheet::setFontFamily(QString font)
1482{
1483 WorksheetTextItem* item = currentTextItem();
1484 if (item)
1485 item->setFontFamily(font);
1486}
1487
1488void Worksheet::setFontSize(int size)
1489{
1490 WorksheetTextItem* item = currentTextItem();
1491 if (item)
1492 item->setFontSize(size);
1493}
1494
1495bool Worksheet::isShortcut(QKeySequence sequence)
1496{
1497 return m_shortcuts.contains(sequence);
1498}
1499
1500void Worksheet::registerShortcut(QAction* action)
1501{
1502 kDebug() << action->shortcuts();
1503 foreach(QKeySequence shortcut, action->shortcuts()) {
1504 m_shortcuts.insert(shortcut, action);
1505 }
1506 connect(action, SIGNAL(changed()), this, SLOT(updateShortcut()));
1507}
1508
1509void Worksheet::updateShortcut()
1510{
1511 QAction* action = qobject_cast<QAction*>(sender());
1512 if (!action)
1513 return;
1514
1515 // delete the old shortcuts of this action
1516 QList<QKeySequence> shortcuts = m_shortcuts.keys(action);
1517 foreach(QKeySequence shortcut, shortcuts) {
1518 m_shortcuts.remove(shortcut);
1519 }
1520 // add the new shortcuts
1521 foreach(QKeySequence shortcut, action->shortcuts()) {
1522 m_shortcuts.insert(shortcut, action);
1523 }
1524}
1525
1526void Worksheet::dragEnterEvent(QGraphicsSceneDragDropEvent* event)
1527{
1528 kDebug() << "enter";
1529 if (m_dragEntry)
1530 event->accept();
1531 else
1532 QGraphicsScene::dragEnterEvent(event);
1533}
1534
1535void Worksheet::dragLeaveEvent(QGraphicsSceneDragDropEvent* event)
1536{
1537 if (!m_dragEntry) {
1538 QGraphicsScene::dragLeaveEvent(event);
1539 return;
1540 }
1541
1542 kDebug() << "leave";
1543 event->accept();
1544 if (m_placeholderEntry) {
1545 m_placeholderEntry->startRemoving();
1546 m_placeholderEntry = 0;
1547 }
1548}
1549
1550void Worksheet::dragMoveEvent(QGraphicsSceneDragDropEvent* event)
1551{
1552 if (!m_dragEntry) {
1553 QGraphicsScene::dragMoveEvent(event);
1554 return;
1555 }
1556
1557 QPointF pos = event->scenePos();
1558 WorksheetEntry* entry = entryAt(pos);
1559 WorksheetEntry* prev = 0;
1560 WorksheetEntry* next = 0;
1561 if (entry) {
1562 if (pos.y() < entry->y() + entry->size().height()/2) {
1563 prev = entry->previous();
1564 next = entry;
1565 } else if (pos.y() >= entry->y() + entry->size().height()/2) {
1566 prev = entry;
1567 next = entry->next();
1568 }
1569 } else {
1570 WorksheetEntry* last = lastEntry();
1571 if (last && pos.y() > last->y() + last->size().height()) {
1572 prev = last;
1573 next = 0;
1574 }
1575 }
1576
1577 if (prev || next) {
1578 PlaceHolderEntry* oldPlaceHolder = m_placeholderEntry;
1579 if (prev && prev->type() == PlaceHolderEntry::Type &&
1580 (!prev->aboutToBeRemoved() || prev->stopRemoving())) {
1581 m_placeholderEntry = qgraphicsitem_cast<PlaceHolderEntry*>(prev);
1582 m_placeholderEntry->changeSize(m_dragEntry->size());
1583 } else if (next && next->type() == PlaceHolderEntry::Type &&
1584 (!next->aboutToBeRemoved() || next->stopRemoving())) {
1585 m_placeholderEntry = qgraphicsitem_cast<PlaceHolderEntry*>(next);
1586 m_placeholderEntry->changeSize(m_dragEntry->size());
1587 } else {
1588 m_placeholderEntry = new PlaceHolderEntry(this, QSizeF(0,0));
1589 m_placeholderEntry->setPrevious(prev);
1590 m_placeholderEntry->setNext(next);
1591 if (prev)
1592 prev->setNext(m_placeholderEntry);
1593 else
1594 setFirstEntry(m_placeholderEntry);
1595 if (next)
1596 next->setPrevious(m_placeholderEntry);
1597 else
1598 setLastEntry(m_placeholderEntry);
1599 m_placeholderEntry->changeSize(m_dragEntry->size());
1600 }
1601 if (oldPlaceHolder && oldPlaceHolder != m_placeholderEntry)
1602 oldPlaceHolder->startRemoving();
1603 updateLayout();
1604 }
1605
1606 const QPoint viewPos = worksheetView()->mapFromScene(pos);
1607 const int viewHeight = worksheetView()->viewport()->height();
1608 if ((viewPos.y() < 10 || viewPos.y() > viewHeight - 10) &&
1609 !m_dragScrollTimer) {
1610 m_dragScrollTimer = new QTimer(this);
1611 m_dragScrollTimer->setSingleShot(true);
1612 m_dragScrollTimer->setInterval(100);
1613 connect(m_dragScrollTimer, SIGNAL(timeout()), this,
1614 SLOT(updateDragScrollTimer()));
1615 m_dragScrollTimer->start();
1616 }
1617
1618 event->accept();
1619}
1620
1621void Worksheet::dropEvent(QGraphicsSceneDragDropEvent* event)
1622{
1623 if (!m_dragEntry)
1624 QGraphicsScene::dropEvent(event);
1625 event->accept();
1626}
1627
1628void Worksheet::updateDragScrollTimer()
1629{
1630 if (!m_dragScrollTimer)
1631 return;
1632
1633 const QPoint viewPos = worksheetView()->viewCursorPos();
1634 const QWidget* viewport = worksheetView()->viewport();
1635 const int viewHeight = viewport->height();
1636 if (!m_dragEntry || !(viewport->rect().contains(viewPos)) ||
1637 (viewPos.y() >= 10 && viewPos.y() <= viewHeight - 10)) {
1638 delete m_dragScrollTimer;
1639 m_dragScrollTimer = 0;
1640 return;
1641 }
1642
1643 if (viewPos.y() < 10)
1644 worksheetView()->scrollBy(-10*(10 - viewPos.y()));
1645 else
1646 worksheetView()->scrollBy(10*(viewHeight - viewPos.y()));
1647
1648 m_dragScrollTimer->start();
1649}
1650
1651#include "worksheet.moc"
1652