moved kdeaccessibility kdeaddons kdeadmin kdeartwork kdebindings kdeedu kdegames...
[kdeedu.git] / ktouch / src / ktouchlectureeditor.cpp
blob3aba879a23be9d6e2f16ce38a67531c1a56d9160
1 /***************************************************************************
2 * ktouchlectureeditor.cpp *
3 * ----------------------- *
4 * Copyright (C) 2004 by Andreas Nicolai *
5 * ghorwin@users.sourceforge.net *
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 ***************************************************************************/
14 #include <qlabel.h>
15 #include <qstring.h>
16 #include <qstringlist.h>
18 #include <kpushbutton.h>
19 #include <klistview.h>
20 #include <klineedit.h>
21 #include <ktextedit.h>
22 #include <klocale.h>
23 #include <kdebug.h>
24 #include <ksqueezedtextlabel.h>
25 #include <kmessagebox.h>
26 #include <kcombobox.h>
27 #include <kfontdialog.h>
28 #include <kfiledialog.h>
30 #include <algorithm> // for std::swap
32 #include "ktouchlectureeditor.h"
33 #include "ktouchlectureeditor.moc"
35 #include "ktouchlecture.h"
36 #include "ktouchopenrequest.h"
37 #include "ktouch.h"
40 // **************************
41 // ***** Public functions ***
42 // **************************
44 KTouchLectureEditor::KTouchLectureEditor(QWidget *parent, const char* name, bool modal, WFlags fl)
45 : KTouchEditorDlg(parent, name, modal, fl)
47 levelListView->setSorting(-1); // don't sort my level list view!
49 connect(levelListView, SIGNAL(selectionChanged(QListViewItem*)),this, SLOT(newSelection(QListViewItem*)) );
50 connect(newCharsEdit, SIGNAL(textChanged(const QString&)), this, SLOT(newCharsChanged(const QString&)) );
51 connect(newBtn, SIGNAL(clicked()), this, SLOT(newLevel()) );
52 connect(deleteBtn, SIGNAL(clicked()), this, SLOT(deleteLevel()) );
53 connect(upBtn, SIGNAL(clicked()), this, SLOT(moveUp()) );
54 connect(downBtn, SIGNAL(clicked()), this, SLOT(moveDown()) );
56 // make the connections for making the lecture modified
57 connect(titleEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setModified()) );
58 connect(lectureCommentEdit, SIGNAL(textChanged()), this, SLOT(setModified()) );
59 connect(levelCommentEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setModified()) );
60 connect(linesTextEdit, SIGNAL(textChanged()), this, SLOT(setModified()) );
62 // The font, open, save, saveas and close buttons are already connected
64 // -----------------------------------------------------------------------------
66 bool KTouchLectureEditor::startEditor(const KURL& url) {
67 // call open request dialog and load a lecture, and start the dialogs event loop if
68 // the user did not cancel the open request dialog
69 if (openLectureFile(url)==QDialog::Accepted) {
70 exec();
71 return true;
73 else return false;
75 // -----------------------------------------------------------------------------
78 // *************************
79 // ***** Private slots *****
80 // *************************
82 void KTouchLectureEditor::fontBtnClicked() {
83 QFont f;
84 if (KFontDialog::getFont(f, false, this, true)==QDialog::Accepted) {
85 linesTextEdit->setFont(f);
86 lectureCommentEdit->setFont(f);
87 levelCommentEdit->setFont(f);
88 newCharsEdit->setFont(f);
89 titleEdit->setFont(f);
90 levelListView->setFont(f);
91 m_lecture.m_fontSuggestions = f.toString();
92 setModified();
95 // -----------------------------------------------------------------------------
97 void KTouchLectureEditor::openBtnClicked() {
98 saveModified(); // save if modified
99 openLectureFile("");
101 // -----------------------------------------------------------------------------
103 void KTouchLectureEditor::saveBtnClicked() {
104 if (m_currentURL.isEmpty()) saveAsBtnClicked();
105 else {
106 transfer_from_dialog();
107 m_lecture.saveXML(this, m_currentURL);
108 setModified(false);
111 // -----------------------------------------------------------------------------
113 void KTouchLectureEditor::saveAsBtnClicked() {
114 QString tmp = KFileDialog::getSaveFileName(QString::null,
115 "*.ktouch.xml|KTouch Lecture Files\n*.*|All Files", this, i18n("Save Training Lecture") );
116 if (!tmp.isEmpty()) {
117 transfer_from_dialog();
118 m_currentURL = tmp;
119 m_lecture.saveXML(this, m_currentURL);
120 setCaption(m_currentURL.url());
121 setModified(false);
124 // -----------------------------------------------------------------------------
126 void KTouchLectureEditor::newSelection(QListViewItem* item) {
127 if (m_selecting) return;
128 bool current_modified_flag = m_modified; // remember our current status
129 // first store the current level data
130 storeCurrentLevel();
131 // now we need to find the level which has been selected
132 QListViewItem *i = levelListView->firstChild();
133 unsigned int level=0;
134 while (i!=0 && i!=item) {
135 i = i->nextSibling();
136 ++level;
138 if (i!=0) {
139 m_currentItem = i;
140 m_level = level;
141 showCurrentLevel();
143 m_selecting = true; // prevent the selectionChanged() signal from interfering
144 levelListView->setSelected(m_currentItem, true);
145 m_selecting = false;
147 if (m_lecture.m_lectureData.size()==1) {
148 downBtn->setEnabled(false);
149 upBtn->setEnabled(false);
151 else {
152 if (m_level==m_lecture.m_lectureData.size()-1) downBtn->setEnabled(false);
153 else downBtn->setEnabled(true);
154 if (m_level==0) upBtn->setEnabled(false);
155 else upBtn->setEnabled(true);
157 setModified(current_modified_flag);
159 // -----------------------------------------------------------------------------
161 void KTouchLectureEditor::newCharsChanged(const QString& text) {
162 if (m_currentItem==0) return; // shouldn't happen, but a little bit of paranoia...
163 m_currentItem->setText(0, text);
164 setModified();
166 // -----------------------------------------------------------------------------
168 void KTouchLectureEditor::newLevel() {
169 createNewLevel();
170 QListViewItem *newItem = new QListViewItem( levelListView,
171 levelListView->lastItem(), m_lecture.m_lectureData.back().m_newChars );
172 newSelection(newItem);
173 upBtn->setEnabled(true);
174 downBtn->setEnabled(false);
175 deleteBtn->setEnabled(true);
176 setModified();
178 // -----------------------------------------------------------------------------
180 void KTouchLectureEditor::deleteLevel() {
181 // Open for discussion: Should there be a message box to confirm the delete?
182 if (m_level >= m_lecture.m_lectureData.size()) return; // paranoia check
183 m_selecting = true; // prevent the selectionChanged() signal from interfering
184 // first remove the item from the list view
185 delete m_currentItem;
186 // then remove the level data
187 QValueVector<KTouchLevelData>::iterator it=m_lecture.m_lectureData.begin();
188 std::advance(it, m_level);
189 m_lecture.m_lectureData.erase(it);
190 m_currentItem = levelListView->firstChild();
191 for (unsigned int i=0; i<m_level; ++i)
192 m_currentItem = m_currentItem->nextSibling();
193 levelListView->setSelected(m_currentItem, true);
194 showCurrentLevel();
195 m_selecting = false;
196 if (m_lecture.m_lectureData.size()==1)
197 deleteBtn->setEnabled(false);
198 if (m_level==m_lecture.m_lectureData.size()-1)
199 downBtn->setEnabled(false);
200 if (m_level==0)
201 upBtn->setEnabled(false);
202 setModified();
204 // -----------------------------------------------------------------------------
206 void KTouchLectureEditor::moveUp() {
207 if (m_level==0) return;
208 m_selecting=true; // again, I don't want to process changeSelection() signals now
209 storeCurrentLevel();
210 QListViewItem *upperItem = m_currentItem->itemAbove();
211 std::swap(m_lecture.m_lectureData[m_level], m_lecture.m_lectureData[m_level-1]);
212 upperItem->setText(0, m_lecture.m_lectureData[m_level-1].m_newChars);
213 m_currentItem->setText(0, m_lecture.m_lectureData[m_level].m_newChars);
214 m_currentItem=upperItem;
215 --m_level;
216 levelListView->setSelected(m_currentItem, true);
217 showCurrentLevel();
218 m_selecting = false;
219 if (m_level==0)
220 upBtn->setEnabled(false);
221 downBtn->setEnabled(true);
222 setModified();
224 // -----------------------------------------------------------------------------
226 void KTouchLectureEditor::moveDown() {
227 if (m_level>=m_lecture.m_lectureData.size()-1) return;
228 m_selecting=true; // again, I don't want to process changeSelection() signals now
229 storeCurrentLevel();
230 QListViewItem *lowerItem = m_currentItem->itemBelow();
231 std::swap(m_lecture.m_lectureData[m_level], m_lecture.m_lectureData[m_level+1]);
232 m_currentItem->setText(0, m_lecture.m_lectureData[m_level].m_newChars);
233 lowerItem->setText(0, m_lecture.m_lectureData[m_level+1].m_newChars);
234 m_currentItem=lowerItem;
235 ++m_level;
236 levelListView->setSelected(m_currentItem, true);
237 showCurrentLevel();
238 m_selecting = false;
239 if (m_level==m_lecture.m_lectureData.size()-1)
240 downBtn->setEnabled(false);
241 upBtn->setEnabled(true);
242 setModified();
244 // -----------------------------------------------------------------------------
248 // ****************************
249 // ***** Private functions ****
250 // ****************************
252 void KTouchLectureEditor::transfer_to_dialog() {
253 bool current_modified_flag = m_modified;
254 // set the title and the filename of the lecture
255 titleEdit->setText(m_lecture.title());
256 lectureCommentEdit->setText(m_lecture.m_comment);
257 if (m_currentURL.isEmpty()) setCaption(i18n("KTouch Lecture Editor - ") + i18n("<new unnamed lecture file>"));
258 else setCaption(i18n("KTouch Lecture Editor - ") + m_currentURL.fileName());
259 // copy the 'new char' strings of the lectures into the list view
260 levelListView->clear();
261 QValueVector<KTouchLevelData>::const_iterator it=m_lecture.m_lectureData.begin();
262 // add first item
263 QListViewItem *item=new QListViewItem( levelListView, (it++)->m_newChars );
264 // add all the others
265 for (;it!=m_lecture.m_lectureData.end(); ++it)
266 item = new QListViewItem( levelListView, item, it->m_newChars );
267 m_currentItem=levelListView->firstChild();
268 m_selecting = true; // prevent the selectionChanged() signal from interfering
269 levelListView->setSelected(m_currentItem, true);
270 m_selecting = false;
271 // open first lesson
272 m_level=0;
273 showCurrentLevel();
274 // set up the buttons
275 upBtn->setEnabled(false);
276 if (m_lecture.m_lectureData.size()>1) {
277 downBtn->setEnabled(true);
278 deleteBtn->setEnabled(true);
280 else {
281 downBtn->setEnabled(false);
282 deleteBtn->setEnabled(false);
284 // finally the font
285 if (!m_lecture.m_fontSuggestions.isEmpty()) {
286 QFont f;
287 // TODO : multiple font suggestions
288 f.fromString(m_lecture.m_fontSuggestions);
289 linesTextEdit->setFont(f);
290 lectureCommentEdit->setFont(f);
291 levelCommentEdit->setFont(f);
292 newCharsEdit->setFont(f);
293 titleEdit->setFont(f);
294 levelListView->setFont(f);
296 setModified(current_modified_flag);
298 // -----------------------------------------------------------------------------
300 void KTouchLectureEditor::transfer_from_dialog() {
301 storeCurrentLevel();
302 m_lecture.m_title = titleEdit->text();
303 m_lecture.m_comment = lectureCommentEdit->text();
304 setModified();
306 // -----------------------------------------------------------------------------
308 void KTouchLectureEditor::showCurrentLevel() {
309 if (m_level >= m_lecture.m_lectureData.size()) return; // should not happen, but why running a risk here...
310 levelLabel->setText(i18n("Data of Level %1").arg(m_level+1) );
311 levelCommentEdit->setText(m_lecture.m_lectureData[m_level].m_comment);
312 newCharsEdit->setText(m_lecture.m_lectureData[m_level].m_newChars);
313 QString text;
314 for (QValueVector<QString>::const_iterator it=m_lecture.m_lectureData[m_level].m_lines.begin();
315 it!=m_lecture.m_lectureData[m_level].m_lines.end(); ++it)
317 text += *it + '\n';
319 linesTextEdit->setText(text);
321 // -----------------------------------------------------------------------------
323 void KTouchLectureEditor::storeCurrentLevel() {
324 if (m_level>=m_lecture.m_lectureData.size()) return; // should not happen, but ... better check one time too much...
325 m_lecture.m_lectureData[m_level].m_comment = levelCommentEdit->text();
326 m_lecture.m_lectureData[m_level].m_newChars = newCharsEdit->text();
327 m_lecture.m_lectureData[m_level].m_lines.clear();
328 QString text = linesTextEdit->text();
329 QStringList lines;
330 QString currentLine;
331 for (unsigned int i=0; i<text.length(); ++i) {
332 QChar c = text[i];
333 if (c=='\t') c=' '; // replace tabs with spaces
334 if (c=='\n') {
335 lines.append(currentLine);
336 currentLine = "";
338 else
339 currentLine += c;
341 lines.append(currentLine);
342 for (QStringList::const_iterator it=lines.begin(); it!=lines.end(); ++it) {
343 if ((*it).isEmpty()) continue;
344 m_lecture.m_lectureData[m_level].m_lines.push_back(*it);
347 // -----------------------------------------------------------------------------
349 void KTouchLectureEditor::createNewLevel() {
350 KTouchLevelData newLevel;
351 newLevel.m_newChars = i18n("abcdefghijklmnopqrstuvwxyz");
352 newLevel.m_comment = QString();
353 newLevel.m_lines.clear(); // remove the lines of the default mini level
354 newLevel.m_lines.push_back(i18n("Enter your lines here..."));
355 m_lecture.m_lectureData.push_back(newLevel);
357 // -----------------------------------------------------------------------------
359 int KTouchLectureEditor::openLectureFile(const KURL& url) {
360 // First setup the open request dialog
361 KTouchOpenRequest dlg(this);
362 // Call the dialog
363 KURL new_url;
364 int result = dlg.requestFileToOpen(new_url,
365 i18n("Open Lecture File"),
366 i18n("Which Lecture File Would You Like to Edit?"),
367 i18n("Edit current lecture:"),
368 i18n("Open a default lecture:"),
369 i18n("Open a lecture file:"),
370 i18n("Create new lecture"),
371 url, KTouchPtr->lectureFiles(), i18n("<no lecture files available>"));
373 if (result == QDialog::Accepted) {
374 // Ok, user confirmed the dialog, now lets get the url
375 m_currentURL = new_url;
376 // Try to load the lecture, if that failes, we create a new lecture instead
377 m_lecture = KTouchLecture(); // empty the lecture
378 if (!m_currentURL.isEmpty()) {
379 // try to read old format first then XML format
380 if (!m_lecture.load(this, m_currentURL) && !m_lecture.loadXML(this, m_currentURL)) {
381 KMessageBox::sorry(this, i18n("Could not open the lecture file, creating a new one instead."));
382 m_currentURL = QString::null; // new lectures haven't got a URL
385 // If we have no URL, we create a new lecture - can happen if either the user
386 // chose "new lecture" or the chosen lecture could not be opened
387 if (m_currentURL.isEmpty()) {
388 m_lecture.createDefault();
389 m_modified = true; // new lectures are modified by default
391 else
392 m_modified = false; // newly read lectures are not modified in the begin
393 // Update our editor with the lecture data
394 transfer_to_dialog();
395 return QDialog::Accepted;
397 else return QDialog::Rejected;
399 // -----------------------------------------------------------------------------
401 void KTouchLectureEditor::setModified(bool flag) {
402 m_modified = flag;
403 if (!m_currentURL.isEmpty()) {
404 if (flag) setCaption(i18n("KTouch Lecture Editor - ") + m_currentURL.fileName() + i18n(" (modified)"));
405 else setCaption(i18n("KTouch Lecture Editor - ") + m_currentURL.fileName());
408 // -----------------------------------------------------------------------------
410 bool KTouchLectureEditor::saveModified() {
411 if (!m_modified) return true;
412 // ok, ask the user to save the changes
413 int result = KMessageBox::questionYesNoCancel(this,
414 i18n("The lecture has been changed. Do you want to save the changes?"));
415 if (result == KMessageBox::Cancel) return false; // User aborted
416 if (result == KMessageBox::Yes) saveBtnClicked();
417 // if successfully saved the modified flag will be resetted in the saveBtnClicked() function
418 return true; // User acknowledged
420 // -----------------------------------------------------------------------------