/********************************************************************** ** Copyright (C) 2005-2008 Trolltech ASA. All rights reserved. ** ** This file is part of Qt Designer. ** ** This file may be used under the terms of the GNU General ** Public License versions 2.0 or 3.0 as published by the Free ** Software Foundation and appearing in the files LICENSE.GPL2 ** and LICENSE.GPL3 included in the packaging of this file. ** Alternatively you may (at your option) use any later version ** of the GNU General Public License if such license has been ** publicly approved by Trolltech ASA (or its successors, if any) ** and the KDE Free Qt Foundation. ** ** Please review the following information to ensure GNU General ** Public Licensing requirements will be met: ** http://trolltech.com/products/qt/licenses/licensing/opensource/. ** If you are unsure which license is appropriate for your use, please ** review the following information: ** http://trolltech.com/products/qt/licenses/licensing/licensingoverview ** or contact the sales department at sales@trolltech.com. ** ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial License Agreement provided with ** the Software. ** ** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, ** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted ** herein. ** **********************************************************************/ #include "formfile.h" #include "timestamp.h" #include "project.h" #include "formwindow.h" #include "command.h" #include "sourceeditor.h" #include "mainwindow.h" #include "../interfaces/languageinterface.h" #include "resource.h" #include "workspace.h" #include #include #include #include "propertyeditor.h" #include #include #include "designerappiface.h" #include static QString make_func_pretty( const QString &s ) { QString res = s; if ( res.find( ")" ) - res.find( "(" ) == 1 ) return res; res.replace( "(", "( " ); res.replace( ")", " )" ); res.replace( "&", " &" ); res.replace( "*", " *" ); res.replace( ",", ", " ); res.replace( ":", " : " ); res = res.simplifyWhiteSpace(); res.replace( " : : ", "::" ); res.replace( ">>", "> >" ); return res; } FormFile::FormFile( const QString &fn, bool temp, Project *p, const char *name ) : filename( fn ), fileNameTemp( temp ), pro( p ), fw( 0 ), ed( 0 ), timeStamp( 0, fn + codeExtension() ), codeEdited( FALSE ), pkg( FALSE ), cm( FALSE ), codeFileStat( None ), timeStampUi(0, fn) { MetaDataBase::addEntry( this ); fake = qstrcmp( name, "qt_fakewindow" ) == 0; //codeFileStat = FormFile::None; pro->addFormFile( this ); loadCode(); if ( !temp ) checkFileName( FALSE ); timeStampUi.update(); } FormFile::~FormFile() { pro->removeFormFile( this ); if ( formWindow() ) formWindow()->setFormFile( 0 ); } void FormFile::setFormWindow( FormWindow *f ) { if ( f == fw ) return; if ( fw ) fw->setFormFile( 0 ); fw = f; if ( fw ) fw->setFormFile( this ); parseCode( cod, FALSE ); QTimer::singleShot( 0, this, SLOT( notifyFormWindowChange() ) ); } void FormFile::setEditor( SourceEditor *e ) { ed = e; } void FormFile::setFileName( const QString &fn ) { if ( fn == filename ) return; if ( fn.isEmpty() ) { fileNameTemp = TRUE; if ( filename.find( "unnamed" ) != 0 ) filename = createUnnamedFileName(); return; } else { fileNameTemp = FALSE; } filename = fn; timeStamp.setFileName( filename + codeExtension() ); timeStampUi.setFileName( filename ); cod = ""; loadCode(); } void FormFile::setCode( const QString &c ) { cod = c; } FormWindow *FormFile::formWindow() const { return fw; } SourceEditor *FormFile::editor() const { return ed; } QString FormFile::fileName() const { return filename; } QString FormFile::absFileName() const { return pro->makeAbsolute( filename ); } QString FormFile::codeFile() const { QString codeExt = codeExtension(); if ( codeExt.isEmpty() ) return ""; return filename + codeExt; } QString FormFile::code() { return cod; } bool FormFile::save( bool withMsgBox, bool ignoreModified ) { if ( fileNameTemp ) return saveAs(); if ( !ignoreModified && !isModified() ) return TRUE; if ( ed ) ed->save(); if (!shouldOverwriteUi()) return TRUE; if ( formWindow() && isModified( WFormWindow ) ) { if ( withMsgBox ) { if ( !formWindow()->checkCustomWidgets() ) return FALSE; } if ( QFile::exists( pro->makeAbsolute( filename ) ) ) { QString fn( pro->makeAbsolute( filename ) ); #if defined(Q_OS_WIN32) fn += ".bak"; #else fn += "~"; #endif QFile f( pro->makeAbsolute( filename ) ); if ( f.open( IO_ReadOnly ) ) { QFile f2( fn ); if ( f2.open( IO_WriteOnly | IO_Translate ) ) { QCString data( f.size() ); f.readBlock( data.data(), f.size() ); f2.writeBlock( data ); } else { QMessageBox::warning( MainWindow::self, "Save", "The file " + codeFile() + " could not be saved" ); } } } } if ( isModified( WFormCode ) ) { if ( QFile::exists( pro->makeAbsolute( codeFile() ) ) ) { QString fn( pro->makeAbsolute( codeFile() ) ); #if defined(Q_OS_WIN32) fn += ".bak"; #else fn += "~"; #endif QFile f( pro->makeAbsolute( codeFile() ) ); if ( f.open( IO_ReadOnly ) ) { QFile f2( fn ); if ( f2.open( IO_WriteOnly | IO_Translate) ) { QCString data( f.size() ); f.readBlock( data.data(), f.size() ); f2.writeBlock( data ); } else if ( qApp->type() != QApplication::Tty ) { QMessageBox::warning( MainWindow::self, "Save", "The file " + codeFile() + " could not be saved" ); } } } } if ( formWindow() ) { Resource resource( MainWindow::self ); resource.setWidget( formWindow() ); bool formCodeOnly = isModified( WFormCode ) && !isModified( WFormWindow ); if ( !resource.save( pro->makeAbsolute( filename ), formCodeOnly ) ) { if ( MainWindow::self ) MainWindow::self->statusBar()->message( tr( "Failed to save file '%1'.").arg( formCodeOnly ? codeFile(): filename ), 5000 ); if ( formCodeOnly ) return FALSE; return saveAs(); } if ( MainWindow::self ) MainWindow::self->statusBar()->message( tr( "'%1' saved."). arg( formCodeOnly ? codeFile() : filename ), 3000 ); } else { if ( !Resource::saveFormCode(this, MetaDataBase::languageInterface(pro->language())) ) return FALSE; } timeStamp.update(); timeStampUi.update(); setModified( FALSE ); return TRUE; } bool FormFile::shouldOverwriteUi() const { if (timeStampUi.isUpToDate()) return TRUE; return (QMessageBox::information(MainWindow::self, tr( "Qt Designer" ), tr("File '%1' has been changed outside Qt Designer.\n" "Do you want to overwrite it?" ).arg(timeStampUi.fileName()), tr("&Yes"), tr("&No")) == 0); } bool FormFile::saveAs( bool ignoreModified ) { QString f = pro->makeAbsolute( fileName() ); if ( fileNameTemp && formWindow() ) { f = QString( formWindow()->name() ).lower(); f.replace( "::", "_" ); f = pro->makeAbsolute( f + ".ui" ); } bool saved = FALSE; if ( ignoreModified ) { QString dir = QStringList::split( ':', pro->iFace()->customSetting( "QTSCRIPT_PACKAGES" ) ).first(); f = QFileInfo( f ).fileName(); f.prepend( dir + "/" ); } QString fn; while ( !saved ) { fn = QFileDialog::getSaveFileName( f, tr( "Qt User-Interface Files (*.ui)" ) + ";;" + tr( "All Files (*)" ), MainWindow::self, 0, tr( "Save Form '%1' As ...").arg( formName() ), MainWindow::self ? &MainWindow::self->lastSaveFilter : 0 ); if ( fn.isEmpty() ) return FALSE; QFileInfo fi( fn ); if ( fi.extension() != "ui" ) fn += ".ui"; fileNameTemp = FALSE; filename = pro->makeRelative( fn ); QFileInfo relfi( filename ); if ( relfi.exists() ) { if ( QMessageBox::warning( MainWindow::self, tr( "File Already Exists" ), tr( "The file already exists. Do you wish to overwrite it?" ), QMessageBox::Yes, QMessageBox::No ) == QMessageBox::Yes ) { saved = TRUE; } else { filename = f; } } else { saved = TRUE; } } if ( !checkFileName( TRUE ) ) { filename = f; return FALSE; } pro->setModified( TRUE ); timeStamp.setFileName( pro->makeAbsolute( codeFile() ) ); if ( ed && formWindow() ) ed->setCaption( tr( "Edit %1" ).arg( formWindow()->name() ) ); setModified( TRUE ); if ( pro->isDummy() ) fw->mainWindow()->addRecentlyOpenedFile( fn ); return save( TRUE, ignoreModified ); } bool FormFile::close() { if ( editor() ) { editor()->save(); editor()->close(); } if ( formWindow() ) return formWindow()->close(); return TRUE; } bool FormFile::closeEvent() { if ( !isModified() && fileNameTemp ) { pro->removeFormFile( this ); return TRUE; } if ( !isModified() ) return TRUE; if ( editor() ) editor()->save(); switch ( QMessageBox::warning( MainWindow::self, tr( "Save Form" ), tr( "Save changes to form '%1'?" ).arg( filename ), tr( "&Yes" ), tr( "&No" ), tr( "&Cancel" ), 0, 2 ) ) { case 0: // save if ( !save() ) return FALSE; case 1: // don't save loadCode(); if ( ed ) ed->editorInterface()->setText( cod ); if ( fileNameTemp ) pro->removeFormFile( this ); if ( MainWindow::self ) MainWindow::self->workspace()->update(); break; case 2: // cancel return FALSE; default: break; } setModified( FALSE ); if ( MainWindow::self ) MainWindow::self->updateFunctionList(); setCodeEdited( FALSE ); return TRUE; } void FormFile::setModified( bool m, int who ) { if ( ( who & WFormWindow ) == WFormWindow ) setFormWindowModified( m ); if ( ( who & WFormCode ) == WFormCode ) setCodeModified( m ); } bool FormFile::isModified( int who ) { if ( who == WFormWindow ) return isFormWindowModified(); if ( who == WFormCode ) return isCodeModified(); return isCodeModified() || isFormWindowModified(); } bool FormFile::isFormWindowModified() const { if ( !formWindow() || !formWindow()->commandHistory() ) return FALSE; return formWindow()->commandHistory()->isModified(); } bool FormFile::isCodeModified() const { if ( !editor() ) return cm; return editor()->isModified(); } void FormFile::setFormWindowModified( bool m ) { bool b = isFormWindowModified(); if ( m == b ) return; if ( !formWindow() || !formWindow()->commandHistory() ) return; formWindow()->commandHistory()->setModified( m ); emit somethingChanged( this ); } void FormFile::setCodeModified( bool m ) { bool b = isCodeModified(); if ( m == b ) return; emit somethingChanged( this ); cm = m; if ( !editor() ) return; editor()->setModified( m ); } void FormFile::showFormWindow() { if ( !MainWindow::self ) return; if ( formWindow() ) { if ( ( formWindow()->hasFocus() || MainWindow::self->qWorkspace()->activeWindow() == formWindow() ) && MainWindow::self->propertyeditor()->formWindow() != formWindow() ) { MainWindow::self->propertyeditor()->setWidget( formWindow()->currentWidget(), formWindow() ); MainWindow::self->objectHierarchy()->setFormWindow( formWindow(), formWindow()->currentWidget() ); } formWindow()->setFocus(); return; } MainWindow::self->openFormWindow( pro->makeAbsolute( filename ), TRUE, this ); } bool FormFile::setupUihFile( bool askForUih ) { if ( !pro->isCpp() || !askForUih ) { if ( !hasFormCode() ) { createFormCode(); setModified( TRUE ); } codeFileStat = FormFile::Ok; return TRUE; } if ( codeFileStat != FormFile::Ok && !ed ) { if ( hasFormCode() ) { int i = QMessageBox::information( MainWindow::self, tr( "Using ui.h file" ), tr( "An \"ui.h\" file for this form already exists.\n" "Do you want to use it or create a new one?" ), tr( "Use existing" ), tr( "Create new" ), tr( "Cancel" ), 2, 2 ); if ( i == 2 ) return FALSE; if ( i == 1 ) createFormCode(); } else { if ( QMessageBox::Yes != QMessageBox::information( MainWindow::self, tr( "Creating ui.h file" ), tr( "Do you want to create an new \"ui.h\" file?" ), QMessageBox::Yes, QMessageBox::No ) ) return FALSE; createFormCode(); } setModified( TRUE ); } codeFileStat = FormFile::Ok; return TRUE; } SourceEditor *FormFile::showEditor( bool askForUih ) { if ( !MainWindow::self ) return 0; showFormWindow(); if ( !setupUihFile( askForUih ) ) return 0; SourceEditor *se = MainWindow::self->openSourceEditor(); if ( !se ) return 0; se->setActiveWindow(); se->setFocus(); return se; } static int ui_counter = 0; QString FormFile::createUnnamedFileName() { return QString( "unnamed" ) + QString::number( ++ui_counter ) + QString( ".ui" ); } QString FormFile::codeExtension() const { LanguageInterface *iface = MetaDataBase::languageInterface( pro->language() ); if ( iface ) return iface->formCodeExtension(); return ""; } QString FormFile::codeComment() const { return QString( "/****************************************************************************\n" "** ui.h extension file, included from the uic-generated form implementation.\n" "**\n" "** If you want to add, delete, or rename functions or slots, use\n" "** Qt Designer to update this file, preserving your code.\n" "**\n" "** You should not define a constructor or destructor in this file.\n" "** Instead, write your code in functions called init() and destroy().\n" "** These will automatically be called by the form's constructor and\n" "** destructor.\n" "*****************************************************************************/\n" ); } bool FormFile::hasFormCode() const { return !cod.isEmpty() && cod != QString( codeComment() ); } int FormFile::codeFileState() const { return hasFormCode() ? codeFileStat : None; } void FormFile::setCodeFileState( UihState s ) { codeFileStat = s; } void FormFile::createFormCode() { if ( !formWindow() ) return; LanguageInterface *iface = MetaDataBase::languageInterface( pro->language() ); if ( !iface ) return; if ( pro->isCpp() ) cod = codeComment(); QValueList functionList = MetaDataBase::functionList( formWindow() ); for ( QValueList::Iterator it = functionList.begin(); it != functionList.end(); ++it ) { cod += (!cod.isEmpty() ? "\n\n" : "") + iface->createFunctionStart( formWindow()->name(), make_func_pretty((*it).function), (*it).returnType.isEmpty() ? QString( "void" ) : (*it).returnType, (*it).access ) + "\n" + iface->createEmptyFunction(); } parseCode( cod, FALSE ); } void FormFile::load() { showFormWindow(); code(); } bool FormFile::loadCode() { QString fn = pro->makeAbsolute( codeFile() ); if ( fn.isEmpty() ) return FALSE; QFile f( fn ); if ( !f.open( IO_ReadOnly ) ) { cod = ""; setCodeFileState( FormFile::None ); return FALSE; } QTextStream ts( &f ); cod = ts.read(); parseCode( cod, FALSE ); if ( hasFormCode() && codeFileStat != FormFile::Ok ) setCodeFileState( FormFile::Deleted ); timeStamp.update(); return TRUE; } bool FormFile::isCodeEdited() const { return codeEdited; } void FormFile::setCodeEdited( bool b ) { codeEdited = b; } void FormFile::parseCode( const QString &txt, bool allowModify ) { if ( !formWindow() ) return; LanguageInterface *iface = MetaDataBase::languageInterface( pro->language() ); if ( !iface ) return; QValueList functions; QValueList newFunctions, oldFunctions; oldFunctions = MetaDataBase::functionList( formWindow() ); iface->functions( txt, &functions ); QMap funcs; for ( QValueList::Iterator it = functions.begin(); it != functions.end(); ++it ) { bool found = FALSE; for ( QValueList::Iterator fit = oldFunctions.begin(); fit != oldFunctions.end(); ++fit ) { QString f( (*fit).function ); if ( MetaDataBase::normalizeFunction( f ) == MetaDataBase::normalizeFunction( (*it).name ) ) { found = TRUE; MetaDataBase::Function function; function.function = make_func_pretty( (*it).name ); function.specifier = (*fit).specifier; function.type = (*fit).type; if ( !pro->isCpp() ) function.access = (*it).access; else function.access = (*fit).access; function.language = (*fit).language; function.returnType = (*it).returnType; newFunctions << function; funcs.insert( (*it).name, (*it).body ); oldFunctions.remove( fit ); break; } } if ( !found ) { MetaDataBase::Function function; function.function = make_func_pretty( (*it).name ); function.specifier = "virtual"; function.access = "public"; function.language = pro->language(); function.returnType = (*it).returnType; if ( function.returnType == "void" ) function.type = "slot"; else function.type = "function"; if ( function.function == "init()" || function.function == "destroy()" ) { function.type = "function"; function.access = "private"; function.specifier = "non virtual"; } newFunctions << function; funcs.insert( (*it).name, (*it).body ); if ( allowModify ) setFormWindowModified( TRUE ); } } if ( allowModify && oldFunctions.count() > 0 ) setFormWindowModified( TRUE ); MetaDataBase::setFunctionList( formWindow(), newFunctions ); } void FormFile::syncCode() { if ( !editor() ) return; parseCode( editor()->editorInterface()->text(), TRUE ); cod = editor()->editorInterface()->text(); } void FormFile::checkTimeStamp() { if ( timeStamp.isUpToDate() ) return; timeStamp.update(); if ( codeEdited ) { if ( QMessageBox::information( MainWindow::self, tr( "Qt Designer" ), tr( "File '%1' has been changed outside Qt Designer.\n" "Do you want to reload it?" ).arg( timeStamp.fileName() ), tr( "&Yes" ), tr( "&No" ) ) == 0 ) { QFile f( timeStamp.fileName() ); if ( f.open( IO_ReadOnly ) ) { QTextStream ts( &f ); editor()->editorInterface()->setText( ts.read() ); editor()->save(); if ( MainWindow::self ) MainWindow::self->functionsChanged(); } } } else { loadCode(); } } bool FormFile::isUihFileUpToDate() { if ( timeStamp.isUpToDate() ) return TRUE; if ( !editor() ) { MainWindow::self->editSource(); parseCode( editor()->editorInterface()->text(), TRUE ); } checkTimeStamp(); return FALSE; } void FormFile::addFunctionCode( MetaDataBase::Function function ) { if ( pro->isCpp() && !hasFormCode() && !codeEdited ) return; LanguageInterface *iface = MetaDataBase::languageInterface( pro->language() ); if ( !iface ) return; QValueList funcs; iface->functions( cod, &funcs ); bool hasFunc = FALSE; for ( QValueList::Iterator it = funcs.begin(); it != funcs.end(); ++it ) { if ( MetaDataBase::normalizeFunction( (*it).name ) == MetaDataBase::normalizeFunction( function.function ) ) { hasFunc = TRUE; break; } } if ( !hasFunc ) { if ( !codeEdited && !timeStamp.isUpToDate() ) loadCode(); MetaDataBase::MetaInfo mi = MetaDataBase::metaInfo( formWindow() ); QString cn; if ( mi.classNameChanged ) cn = mi.className; if ( cn.isEmpty() ) cn = formWindow()->name(); QString body = "\n\n" + iface->createFunctionStart( cn, make_func_pretty( function.function ), function.returnType.isEmpty() ? QString( "void" ) : function.returnType, function.access ) + "\n" + iface->createEmptyFunction(); cod += body; if ( codeEdited ) { setModified( TRUE ); emit somethingChanged( this ); } } } void FormFile::removeFunctionCode( MetaDataBase::Function function ) { if ( pro->isCpp() && !hasFormCode() && !codeEdited ) return; LanguageInterface *iface = MetaDataBase::languageInterface( pro->language() ); if ( !iface ) return; checkTimeStamp(); QString sourceCode = code(); if ( sourceCode.isEmpty() ) return; QValueList functions; iface->functions( sourceCode, &functions ); QString fu = MetaDataBase::normalizeFunction( function.function ); for ( QValueList::Iterator fit = functions.begin(); fit != functions.end(); ++fit ) { if ( MetaDataBase::normalizeFunction( (*fit).name ) == fu ) { int line = 0; int start = 0; while ( line < (*fit).start - 1 ) { start = sourceCode.find( '\n', start ); if ( start == -1 ) return; start++; line++; } if ( start == -1 ) return; int end = start; while ( line < (*fit).end + 1 ) { end = sourceCode.find( '\n', end ); if ( end == -1 ) { if ( line <= (*fit).end ) end = sourceCode.length() - 1; else return; } end++; line++; } if ( end < start ) return; sourceCode.remove( start, end - start ); setCode( sourceCode ); } } } void FormFile::functionNameChanged( const QString &oldName, const QString &newName ) { if ( !cod.isEmpty() ) { QString funcStart = QString( formWindow()->name() ) + QString( "::" ); int i = cod.find( funcStart + oldName ); if ( i != -1 ) { cod.remove( i + funcStart.length(), oldName.length() ); cod.insert( i + funcStart.length(), newName ); } } } void FormFile::functionRetTypeChanged( const QString &fuName, const QString &oldType, const QString &newType ) { if ( !cod.isEmpty() ) { QString oldFunct = oldType + " " + QString( formWindow()->name() ) + "::" + fuName; QString newFunct = newType + " " + QString( formWindow()->name() ) + "::" + fuName; int i = cod.find( oldFunct ); if ( i != -1 ) { cod.remove( i, oldFunct.length() ); cod.insert( i, newFunct ); } } } QString FormFile::formName() const { FormFile* that = (FormFile*) this; if ( formWindow() ) { that->cachedFormName = formWindow()->name(); return cachedFormName; } if ( !cachedFormName.isNull() ) return cachedFormName; QFile f( pro->makeAbsolute( filename ) ); if ( f.open( IO_ReadOnly ) ) { QTextStream ts( &f ); QString line; QString className; while ( !ts.eof() ) { line = ts.readLine(); if ( !className.isEmpty() ) { int end = line.find( "" ); if ( end == -1 ) { className += line; } else { className += line.left( end ); break; } continue; } int start; if ( ( start = line.find( "" ) ) != -1 ) { int end = line.find( "" ); if ( end == -1 ) { className = line.mid( start + 7 ); } else { className = line.mid( start + 7, end - ( start + 7 ) ); break; } } } that->cachedFormName = className; } if ( cachedFormName.isEmpty() ) that->cachedFormName = filename; return cachedFormName; } void FormFile::formWindowChangedSomehow() { emit somethingChanged( this ); } bool FormFile::checkFileName( bool allowBreak ) { FormFile *ff = pro->findFormFile( filename, this ); if ( ff ) QMessageBox::warning( MainWindow::self, tr( "Invalid Filename" ), tr( "The project already contains a form with a\n" "filename of '%1'. Please choose a new filename." ).arg( filename ) ); if (!formWindow()) // We are starting up return FALSE; while ( ff ) { QString fn; while ( fn.isEmpty() ) { fn = QFileDialog::getSaveFileName( pro->makeAbsolute( fileName() ), tr( "Qt User-Interface Files (*.ui)" ) + ";;" + tr( "All Files (*)" ), MainWindow::self, 0, tr( "Save Form '%1' As ..."). arg( formWindow()->name() ), MainWindow::self ? &MainWindow::self->lastSaveFilter : 0 ); if ( allowBreak && fn.isEmpty() ) return FALSE; } filename = pro->makeRelative( fn ); ff = pro->findFormFile( filename, this ); } return TRUE; } void FormFile::addConnection( const QString &sender, const QString &signal, const QString &receiver, const QString &slot ) { LanguageInterface *iface = MetaDataBase::languageInterface( pro->language() ); if ( iface ) iface->addConnection( sender, signal, receiver, slot, &cod ); if ( ed ) ed->editorInterface()->setText( cod ); } void FormFile::removeConnection( const QString &sender, const QString &signal, const QString &receiver, const QString &slot ) { LanguageInterface *iface = MetaDataBase::languageInterface( pro->language() ); if ( iface ) iface->removeConnection( sender, signal, receiver, slot, &cod ); if ( ed ) ed->editorInterface()->setText( cod ); } void FormFile::notifyFormWindowChange() { if ( fw ) pro->formOpened( fw ); }